diff --git a/bazaar/plugin/gdal/alg/GNUmakefile b/bazaar/plugin/gdal/alg/GNUmakefile new file mode 100644 index 000000000..832c6a0ef --- /dev/null +++ b/bazaar/plugin/gdal/alg/GNUmakefile @@ -0,0 +1,51 @@ + +include ../GDALmake.opt + +OBJ = gdalmediancut.o gdaldither.o gdal_crs.o gdaltransformer.o \ + gdalsimplewarp.o gdalwarper.o gdalwarpkernel.o \ + gdalwarpoperation.o gdalchecksum.o gdal_rpc.o gdal_tps.o \ + thinplatespline.o llrasterize.o gdalrasterize.o gdalgeoloc.o \ + gdalgrid.o gdalcutline.o gdalproximity.o rasterfill.o \ + gdalrasterpolygonenumerator.o \ + gdalsievefilter.o gdalwarpkernel_opencl.o polygonize.o \ + gdalrasterfpolygonenumerator.o fpolygonize.o \ + contour.o gdaltransformgeolocs.o \ + gdal_octave.o gdal_simplesurf.o gdalmatching.o + +ifeq ($(HAVE_AVX_AT_COMPILE_TIME),yes) +CPPFLAGS := -DHAVE_AVX_AT_COMPILE_TIME $(CPPFLAGS) +endif + +ifeq ($(HAVE_SSE_AT_COMPILE_TIME),yes) +CPPFLAGS := -DHAVE_SSE_AT_COMPILE_TIME $(CPPFLAGS) +endif + +ifeq ($(HAVE_GEOS),yes) +CPPFLAGS := -DHAVE_GEOS=1 $(GEOS_CFLAGS) $(CPPFLAGS) +endif + +ifeq ($(HAVE_ARMADILLO),yes) +CPPFLAGS := -DHAVE_ARMADILLO $(CPPFLAGS) +endif + +CPPFLAGS := $(CPPFLAGS) $(OPENCL_FLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) gdalgridavx.$(OBJ_EXT) gdalgridsse.$(OBJ_EXT) + +gdalgridavx.$(OBJ_EXT): gdalgridavx.cpp + $(CXX) $(GDAL_INCLUDE) $(CXXFLAGS) $(AVXFLAGS) $(CPPFLAGS) -c -o $@ $< + +gdalgridsse.$(OBJ_EXT): gdalgridsse.cpp + $(CXX) $(GDAL_INCLUDE) $(CXXFLAGS) $(SSEFLAGS) $(CPPFLAGS) -c -o $@ $< + +clean: + $(RM) *.o $(O_OBJ) + +docs: + (cd ..; $(MAKE) docs) + +install: + for f in *.h ; do $(INSTALL_DATA) $$f $(DESTDIR)$(INST_INCLUDE) ; done + +lib: $(OBJ:.o=.$(OBJ_EXT)) + (cd .. ; $(MAKE) force-lib) diff --git a/bazaar/plugin/gdal/alg/contour.cpp b/bazaar/plugin/gdal/alg/contour.cpp new file mode 100644 index 000000000..40a0587dd --- /dev/null +++ b/bazaar/plugin/gdal/alg/contour.cpp @@ -0,0 +1,1627 @@ +/****************************************************************************** + * $Id: contour.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: Contour Generation + * Purpose: Core algorithm implementation for contour line generation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2003, Applied Coherent Technology Corporation, www.actgate.com + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_alg.h" +#include "ogr_api.h" + +CPL_CVSID("$Id: contour.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +#ifdef OGR_ENABLED + +// The amount of a contour interval that pixels should be fudged by if they +// match a contour level exactly. + +#define FUDGE_EXACT 0.001 + +// The amount of a pixel that line ends need to be within to be considered to +// match for joining purposes. + +#define JOIN_DIST 0.0001 + +/************************************************************************/ +/* GDALContourItem */ +/************************************************************************/ +class GDALContourItem +{ +public: + int bRecentlyAccessed; + double dfLevel; + + int nPoints; + int nMaxPoints; + double *padfX; + double *padfY; + + int bLeftIsHigh; + + double dfTailX; + + GDALContourItem( double dfLevel ); + ~GDALContourItem(); + + int AddSegment( double dfXStart, double dfYStart, + double dfXEnd, double dfYEnd, int bLeftHigh ); + void MakeRoomFor( int ); + int Merge( GDALContourItem * ); + void PrepareEjection(); +}; + +/************************************************************************/ +/* GDALContourLevel */ +/************************************************************************/ +class GDALContourLevel +{ + double dfLevel; + + int nEntryMax; + int nEntryCount; + GDALContourItem **papoEntries; + +public: + GDALContourLevel( double ); + ~GDALContourLevel(); + + double GetLevel() { return dfLevel; } + int GetContourCount() { return nEntryCount; } + GDALContourItem *GetContour( int i) { return papoEntries[i]; } + void AdjustContour( int ); + void RemoveContour( int ); + int FindContour( double dfX, double dfY ); + int InsertContour( GDALContourItem * ); +}; + +/************************************************************************/ +/* GDALContourGenerator */ +/************************************************************************/ +class GDALContourGenerator +{ + int nWidth; + int nHeight; + int iLine; + + double *padfLastLine; + double *padfThisLine; + + int nLevelMax; + int nLevelCount; + GDALContourLevel **papoLevels; + + int bNoDataActive; + double dfNoDataValue; + + int bFixedLevels; + double dfContourInterval; + double dfContourOffset; + + CPLErr AddSegment( double dfLevel, + double dfXStart, double dfYStart, + double dfXEnd, double dfYEnd, int bLeftHigh ); + + CPLErr ProcessPixel( int iPixel ); + CPLErr ProcessRect( double, double, double, + double, double, double, + double, double, double, + double, double, double ); + + void Intersect( double, double, double, + double, double, double, + double, double, int *, double *, double * ); + + GDALContourLevel *FindLevel( double dfLevel ); + +public: + GDALContourWriter pfnWriter; + void *pWriterCBData; + + GDALContourGenerator( int nWidth, int nHeight, + GDALContourWriter pfnWriter, void *pWriterCBData ); + ~GDALContourGenerator(); + + void SetNoData( double dfNoDataValue ); + void SetContourLevels( double dfContourInterval, + double dfContourOffset = 0.0 ) + { this->dfContourInterval = dfContourInterval; + this->dfContourOffset = dfContourOffset; } + + void SetFixedLevels( int, double * ); + CPLErr FeedLine( double *padfScanline ); + CPLErr EjectContours( int bOnlyUnused = FALSE ); + +}; + +/************************************************************************/ +/* GDAL_CG_Create() */ +/************************************************************************/ + +GDALContourGeneratorH +GDAL_CG_Create( int nWidth, int nHeight, int bNoDataSet, double dfNoDataValue, + double dfContourInterval, double dfContourBase, + GDALContourWriter pfnWriter, void *pCBData ) + +{ + GDALContourGenerator *poCG = new GDALContourGenerator( nWidth, nHeight, + pfnWriter, pCBData ); + + if( bNoDataSet ) + poCG->SetNoData( dfNoDataValue ); + + poCG->SetContourLevels( dfContourInterval, dfContourBase ); + return (GDALContourGeneratorH) poCG; +} + +/************************************************************************/ +/* GDAL_CG_FeedLine() */ +/************************************************************************/ + +CPLErr GDAL_CG_FeedLine( GDALContourGeneratorH hCG, double *padfScanline ) + +{ + VALIDATE_POINTER1( hCG, "GDAL_CG_FeedLine", CE_Failure ); + + return ((GDALContourGenerator *) hCG)->FeedLine( padfScanline ); +} + +/************************************************************************/ +/* GDAL_CG_Destroy() */ +/************************************************************************/ + +void GDAL_CG_Destroy( GDALContourGeneratorH hCG ) + +{ + delete ((GDALContourGenerator *) hCG); +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALContourGenerator */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALContourGenerator() */ +/************************************************************************/ + +GDALContourGenerator::GDALContourGenerator( int nWidthIn, int nHeightIn, + GDALContourWriter pfnWriterIn, + void *pWriterCBDataIn ) +{ + nWidth = nWidthIn; + nHeight = nHeightIn; + + padfLastLine = (double *) CPLCalloc(sizeof(double),nWidth); + padfThisLine = (double *) CPLCalloc(sizeof(double),nWidth); + + pfnWriter = pfnWriterIn; + pWriterCBData = pWriterCBDataIn; + + iLine = -1; + + bNoDataActive = FALSE; + dfNoDataValue = -1000000.0; + dfContourInterval = 10.0; + dfContourOffset = 0.0; + + nLevelMax = 0; + nLevelCount = 0; + papoLevels = NULL; + bFixedLevels = FALSE; +} + +/************************************************************************/ +/* ~GDALContourGenerator() */ +/************************************************************************/ + +GDALContourGenerator::~GDALContourGenerator() + +{ + int i; + + for( i = 0; i < nLevelCount; i++ ) + delete papoLevels[i]; + CPLFree( papoLevels ); + + CPLFree( padfLastLine ); + CPLFree( padfThisLine ); +} + +/************************************************************************/ +/* SetFixedLevels() */ +/************************************************************************/ + +void GDALContourGenerator::SetFixedLevels( int nFixedLevelCount, + double *padfFixedLevels ) + +{ + bFixedLevels = TRUE; + for( int i = 0; i < nFixedLevelCount; i++ ) + FindLevel( padfFixedLevels[i] ); +} + +/************************************************************************/ +/* SetNoData() */ +/************************************************************************/ + +void GDALContourGenerator::SetNoData( double dfNewValue ) + +{ + bNoDataActive = TRUE; + dfNoDataValue = dfNewValue; +} + +/************************************************************************/ +/* ProcessPixel() */ +/************************************************************************/ + +CPLErr GDALContourGenerator::ProcessPixel( int iPixel ) + +{ + double dfUpLeft, dfUpRight, dfLoLeft, dfLoRight; + int bSubdivide = FALSE; + +/* -------------------------------------------------------------------- */ +/* Collect the four corner pixel values. Value left or right */ +/* of the scanline are taken from the nearest pixel on the */ +/* scanline itself. */ +/* -------------------------------------------------------------------- */ + dfUpLeft = padfLastLine[MAX(0,iPixel-1)]; + dfUpRight = padfLastLine[MIN(nWidth-1,iPixel)]; + + dfLoLeft = padfThisLine[MAX(0,iPixel-1)]; + dfLoRight = padfThisLine[MIN(nWidth-1,iPixel)]; + +/* -------------------------------------------------------------------- */ +/* Check if we have any nodata values. */ +/* -------------------------------------------------------------------- */ + if( bNoDataActive + && ( dfUpLeft == dfNoDataValue + || dfLoLeft == dfNoDataValue + || dfLoRight == dfNoDataValue + || dfUpRight == dfNoDataValue ) ) + bSubdivide = TRUE; + +/* -------------------------------------------------------------------- */ +/* Check if we have any nodata, if so, go to a special case of */ +/* code. */ +/* -------------------------------------------------------------------- */ + if( iPixel > 0 && iPixel < nWidth + && iLine > 0 && iLine < nHeight && !bSubdivide ) + { + return ProcessRect( dfUpLeft, iPixel - 0.5, iLine - 0.5, + dfLoLeft, iPixel - 0.5, iLine + 0.5, + dfLoRight, iPixel + 0.5, iLine + 0.5, + dfUpRight, iPixel + 0.5, iLine - 0.5 ); + } + +/* -------------------------------------------------------------------- */ +/* Prepare subdivisions. */ +/* -------------------------------------------------------------------- */ + int nGoodCount = 0; + double dfASum = 0.0; + double dfCenter, dfTop=0.0, dfRight=0.0, dfLeft=0.0, dfBottom=0.0; + + if( dfUpLeft != dfNoDataValue ) + { + dfASum += dfUpLeft; + nGoodCount++; + } + + if( dfLoLeft != dfNoDataValue ) + { + dfASum += dfLoLeft; + nGoodCount++; + } + + if( dfLoRight != dfNoDataValue ) + { + dfASum += dfLoRight; + nGoodCount++; + } + + if( dfUpRight != dfNoDataValue ) + { + dfASum += dfUpRight; + nGoodCount++; + } + + if( nGoodCount == 0.0 ) + return CE_None; + + dfCenter = dfASum / nGoodCount; + + if( dfUpLeft != dfNoDataValue ) + { + if( dfUpRight != dfNoDataValue ) + dfTop = (dfUpLeft + dfUpRight) / 2.0; + else + dfTop = dfUpLeft; + + if( dfLoLeft != dfNoDataValue ) + dfLeft = (dfUpLeft + dfLoLeft) / 2.0; + else + dfLeft = dfUpLeft; + } + else + { + dfTop = dfUpRight; + dfLeft = dfLoLeft; + } + + if( dfLoRight != dfNoDataValue ) + { + if( dfUpRight != dfNoDataValue ) + dfRight = (dfLoRight + dfUpRight) / 2.0; + else + dfRight = dfLoRight; + + if( dfLoLeft != dfNoDataValue ) + dfBottom = (dfLoRight + dfLoLeft) / 2.0; + else + dfBottom = dfLoRight; + } + else + { + dfBottom = dfLoLeft;; + dfRight = dfUpRight; + } + +/* -------------------------------------------------------------------- */ +/* Process any quadrants that aren't "nodata" anchored. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + + if( dfUpLeft != dfNoDataValue && iPixel > 0 && iLine > 0 ) + { + eErr = ProcessRect( dfUpLeft, iPixel - 0.5, iLine - 0.5, + dfLeft, iPixel - 0.5, iLine, + dfCenter, iPixel, iLine, + dfTop, iPixel, iLine - 0.5 ); + } + + if( dfLoLeft != dfNoDataValue && eErr == CE_None + && iPixel > 0 && iLine < nHeight ) + { + eErr = ProcessRect( dfLeft, iPixel - 0.5, iLine, + dfLoLeft, iPixel - 0.5, iLine + 0.5, + dfBottom, iPixel, iLine + 0.5, + dfCenter, iPixel, iLine ); + } + + if( dfLoRight != dfNoDataValue && iPixel < nWidth && iLine < nHeight ) + { + eErr = ProcessRect( dfCenter, iPixel, iLine, + dfBottom, iPixel, iLine + 0.5, + dfLoRight, iPixel + 0.5, iLine + 0.5, + dfRight, iPixel + 0.5, iLine ); + } + + if( dfUpRight != dfNoDataValue && iPixel < nWidth && iLine > 0 ) + { + eErr = ProcessRect( dfTop, iPixel, iLine - 0.5, + dfCenter, iPixel, iLine, + dfRight, iPixel + 0.5, iLine, + dfUpRight, iPixel + 0.5, iLine - 0.5 ); + } + + return eErr; +} + +/************************************************************************/ +/* ProcessRect() */ +/************************************************************************/ + +CPLErr GDALContourGenerator::ProcessRect( + double dfUpLeft, double dfUpLeftX, double dfUpLeftY, + double dfLoLeft, double dfLoLeftX, double dfLoLeftY, + double dfLoRight, double dfLoRightX, double dfLoRightY, + double dfUpRight, double dfUpRightX, double dfUpRightY ) + +{ +/* -------------------------------------------------------------------- */ +/* Identify the range of elevations over this rect. */ +/* -------------------------------------------------------------------- */ + int iStartLevel, iEndLevel; + + double dfMin = MIN(MIN(dfUpLeft,dfUpRight),MIN(dfLoLeft,dfLoRight)); + double dfMax = MAX(MAX(dfUpLeft,dfUpRight),MAX(dfLoLeft,dfLoRight)); + + +/* -------------------------------------------------------------------- */ +/* Compute the set of levels to compute contours for. */ +/* -------------------------------------------------------------------- */ + + /* + ** If we are using fixed levels, then find the min/max in the levels + ** table. + */ + if( bFixedLevels ) + { + int nStart=0, nEnd=nLevelCount-1, nMiddle; + + iStartLevel = -1; + while( nStart <= nEnd ) + { + nMiddle = (nEnd + nStart) / 2; + + double dfMiddleLevel = papoLevels[nMiddle]->GetLevel(); + + if( dfMiddleLevel < dfMin ) + nStart = nMiddle + 1; + else if( dfMiddleLevel > dfMin ) + nEnd = nMiddle - 1; + else + { + iStartLevel = nMiddle; + break; + } + } + + if( iStartLevel == -1 ) + iStartLevel = nEnd + 1; + + iEndLevel = iStartLevel; + while( iEndLevel < nLevelCount-1 + && papoLevels[iEndLevel+1]->GetLevel() < dfMax ) + iEndLevel++; + + if( iStartLevel >= nLevelCount ) + return CE_None; + + CPLAssert( iStartLevel >= 0 && iStartLevel < nLevelCount ); + CPLAssert( iEndLevel >= 0 && iEndLevel < nLevelCount ); + } + + /* + ** Otherwise figure out the start and end using the base and offset. + */ + else + { + iStartLevel = (int) + ceil((dfMin - dfContourOffset) / dfContourInterval); + iEndLevel = (int) + floor((dfMax - dfContourOffset) / dfContourInterval); + } + + if( iStartLevel > iEndLevel ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Loop over them. */ +/* -------------------------------------------------------------------- */ + int iLevel; + + for( iLevel = iStartLevel; iLevel <= iEndLevel; iLevel++ ) + { + double dfLevel; + + if( bFixedLevels ) + dfLevel = papoLevels[iLevel]->GetLevel(); + else + dfLevel = iLevel * dfContourInterval + dfContourOffset; + + int nPoints = 0; + double adfX[4], adfY[4]; + CPLErr eErr = CE_None; + + /* Logs how many points we have af left + bottom, + ** and left + bottom + right. + */ + int nPoints1 = 0, nPoints2 = 0, nPoints3 = 0; + + + Intersect( dfUpLeft, dfUpLeftX, dfUpLeftY, + dfLoLeft, dfLoLeftX, dfLoLeftY, + dfLoRight, dfLevel, &nPoints, adfX, adfY ); + nPoints1 = nPoints; + Intersect( dfLoLeft, dfLoLeftX, dfLoLeftY, + dfLoRight, dfLoRightX, dfLoRightY, + dfUpRight, dfLevel, &nPoints, adfX, adfY ); + nPoints2 = nPoints; + Intersect( dfLoRight, dfLoRightX, dfLoRightY, + dfUpRight, dfUpRightX, dfUpRightY, + dfUpLeft, dfLevel, &nPoints, adfX, adfY ); + nPoints3 = nPoints; + Intersect( dfUpRight, dfUpRightX, dfUpRightY, + dfUpLeft, dfUpLeftX, dfUpLeftY, + dfLoLeft, dfLevel, &nPoints, adfX, adfY ); + + if( nPoints == 1 || nPoints == 3 ) + CPLDebug( "CONTOUR", "Got nPoints = %d", nPoints ); + + if( nPoints >= 2 ) + { + if ( nPoints1 == 1 && nPoints2 == 2) // left + bottom + { + eErr = AddSegment( dfLevel, + adfX[0], adfY[0], adfX[1], adfY[1], + dfUpRight > dfLoLeft ); + } + else if ( nPoints1 == 1 && nPoints3 == 2 ) // left + right + { + eErr = AddSegment( dfLevel, + adfX[0], adfY[0], adfX[1], adfY[1], + dfUpLeft > dfLoRight ); + } + else if ( nPoints1 == 1 && nPoints == 2 ) // left + top + { // Do not do vertical contours on the left, due to symmetry + if ( !(dfUpLeft == dfLevel && dfLoLeft == dfLevel) ) + eErr = AddSegment( dfLevel, + adfX[0], adfY[0], adfX[1], adfY[1], + dfUpLeft > dfLoRight ); + } + else if( nPoints2 == 1 && nPoints3 == 2) // bottom + right + { + eErr = AddSegment( dfLevel, + adfX[0], adfY[0], adfX[1], adfY[1], + dfUpLeft > dfLoRight ); + } + else if ( nPoints2 == 1 && nPoints == 2 ) // bottom + top + { + eErr = AddSegment( dfLevel, + adfX[0], adfY[0], adfX[1], adfY[1], + dfLoLeft > dfUpRight ); + } + else if ( nPoints3 == 1 && nPoints == 2 ) // right + top + { // Do not do horizontal contours on upside, due to symmetry + if ( !(dfUpRight == dfLevel && dfUpLeft == dfLevel) ) + eErr = AddSegment( dfLevel, + adfX[0], adfY[0], adfX[1], adfY[1], + dfLoLeft > dfUpRight ); + } + else + { + // If we get here it is a serious error! + CPLDebug( "CONTOUR", "Contour state not implemented!"); + } + + if( eErr != CE_None ) + return eErr; + } + + if( nPoints == 4 ) + { + // Do not do horizontal contours on upside, due to symmetry + if ( !(dfUpRight == dfLevel && dfUpLeft == dfLevel) ) + { +/* -------------------------------------------------------------------- */ +/* If we get here, we know the first was left+bottom, */ +/* so we are at right+top, therefore "left is high" */ +/* if low-left is larger than up-right. */ +/* We do not do a diagonal check here as we are dealing with */ +/* a saddle point. */ +/* -------------------------------------------------------------------- */ + eErr = AddSegment( dfLevel, + adfX[2], adfY[2], adfX[3], adfY[3], + ( dfLoRight > dfUpRight) ); + if( eErr != CE_None ) + return eErr; + } + } + } + + return CE_None; +} + +/************************************************************************/ +/* Intersect() */ +/************************************************************************/ + +void GDALContourGenerator::Intersect( double dfVal1, double dfX1, double dfY1, + double dfVal2, double dfX2, double dfY2, + double dfNext, + double dfLevel, int *pnPoints, + double *padfX, double *padfY ) + +{ + if( dfVal1 < dfLevel && dfVal2 >= dfLevel ) + { + double dfRatio = (dfLevel - dfVal1) / (dfVal2 - dfVal1); + + padfX[*pnPoints] = dfX1 * (1.0 - dfRatio) + dfX2 * dfRatio; + padfY[*pnPoints] = dfY1 * (1.0 - dfRatio) + dfY2 * dfRatio; + (*pnPoints)++; + } + else if( dfVal1 > dfLevel && dfVal2 <= dfLevel ) + { + double dfRatio = (dfLevel - dfVal2) / (dfVal1 - dfVal2); + + padfX[*pnPoints] = dfX2 * (1.0 - dfRatio) + dfX1 * dfRatio; + padfY[*pnPoints] = dfY2 * (1.0 - dfRatio) + dfY1 * dfRatio; + (*pnPoints)++; + } + else if( dfVal1 == dfLevel && dfVal2 == dfLevel && dfNext != dfLevel ) + { + padfX[*pnPoints] = dfX2; + padfY[*pnPoints] = dfY2; + (*pnPoints)++; + } +} + +/************************************************************************/ +/* AddSegment() */ +/************************************************************************/ + +CPLErr GDALContourGenerator::AddSegment( double dfLevel, + double dfX1, double dfY1, + double dfX2, double dfY2, + int bLeftHigh) + +{ + GDALContourLevel *poLevel = FindLevel( dfLevel ); + GDALContourItem *poTarget; + int iTarget; + +/* -------------------------------------------------------------------- */ +/* Check all active contours for any that this might attach */ +/* to. Eventually this should be recoded to find the contours */ +/* of the correct level more efficiently. */ +/* -------------------------------------------------------------------- */ + + if( dfY1 < dfY2 ) + iTarget = poLevel->FindContour( dfX1, dfY1 ); + else + iTarget = poLevel->FindContour( dfX2, dfY2 ); + + if( iTarget != -1 ) + { + poTarget = poLevel->GetContour( iTarget ); + + poTarget->AddSegment( dfX1, dfY1, dfX2, dfY2, bLeftHigh ); + + poLevel->AdjustContour( iTarget ); + + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* No existing contour found, lets create a new one. */ +/* -------------------------------------------------------------------- */ + poTarget = new GDALContourItem( dfLevel ); + + poTarget->AddSegment( dfX1, dfY1, dfX2, dfY2, bLeftHigh ); + + poLevel->InsertContour( poTarget ); + + return CE_None; +} + +/************************************************************************/ +/* FeedLine() */ +/************************************************************************/ + +CPLErr GDALContourGenerator::FeedLine( double *padfScanline ) + +{ +/* -------------------------------------------------------------------- */ +/* Switch current line to "lastline" slot, and copy new data */ +/* into new "this line". */ +/* -------------------------------------------------------------------- */ + double *padfTempLine = padfLastLine; + padfLastLine = padfThisLine; + padfThisLine = padfTempLine; + +/* -------------------------------------------------------------------- */ +/* If this is the end of the lines (NULL passed in), copy the */ +/* last line. */ +/* -------------------------------------------------------------------- */ + if( padfScanline == NULL ) + { + memcpy( padfThisLine, padfLastLine, sizeof(double) * nWidth ); + } + else + { + memcpy( padfThisLine, padfScanline, sizeof(double) * nWidth ); + } + +/* -------------------------------------------------------------------- */ +/* Perturb any values that occur exactly on level boundaries. */ +/* -------------------------------------------------------------------- */ + int iPixel; + + for( iPixel = 0; iPixel < nWidth; iPixel++ ) + { + if( bNoDataActive && padfThisLine[iPixel] == dfNoDataValue ) + continue; + + double dfLevel = (padfThisLine[iPixel] - dfContourOffset) + / dfContourInterval; + + if( dfLevel - (int) dfLevel == 0.0 ) + { + padfThisLine[iPixel] += dfContourInterval * FUDGE_EXACT; + } + } + +/* -------------------------------------------------------------------- */ +/* If this is the first line we need to initialize the previous */ +/* line from the first line of data. */ +/* -------------------------------------------------------------------- */ + if( iLine == -1 ) + { + memcpy( padfLastLine, padfThisLine, sizeof(double) * nWidth ); + iLine = 0; + } + +/* -------------------------------------------------------------------- */ +/* Clear the recently used flags on the contours so we can */ +/* check later which ones were touched for this scanline. */ +/* -------------------------------------------------------------------- */ + int iLevel, iContour; + + for( iLevel = 0; iLevel < nLevelCount; iLevel++ ) + { + GDALContourLevel *poLevel = papoLevels[iLevel]; + + for( iContour = 0; iContour < poLevel->GetContourCount(); iContour++ ) + poLevel->GetContour( iContour )->bRecentlyAccessed = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Process each pixel. */ +/* -------------------------------------------------------------------- */ + for( iPixel = 0; iPixel < nWidth+1; iPixel++ ) + { + CPLErr eErr = ProcessPixel( iPixel ); + if( eErr != CE_None ) + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* eject any pending contours. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = EjectContours( padfScanline != NULL ); + + iLine++; + + if( iLine == nHeight && eErr == CE_None ) + return FeedLine( NULL ); + else + return eErr; +} + +/************************************************************************/ +/* EjectContours() */ +/************************************************************************/ + +CPLErr GDALContourGenerator::EjectContours( int bOnlyUnused ) + +{ + int iLevel; + CPLErr eErr = CE_None; + +/* -------------------------------------------------------------------- */ +/* Process all contours of all levels that match our criteria */ +/* -------------------------------------------------------------------- */ + for( iLevel = 0; iLevel < nLevelCount && eErr == CE_None; iLevel++ ) + { + GDALContourLevel *poLevel = papoLevels[iLevel]; + int iContour; + + for( iContour = 0; + iContour < poLevel->GetContourCount() && eErr == CE_None; + /* increment in loop if we don't consume it. */ ) + { + int iC2; + GDALContourItem *poTarget = poLevel->GetContour( iContour ); + + if( bOnlyUnused && poTarget->bRecentlyAccessed ) + { + iContour++; + continue; + } + + poLevel->RemoveContour( iContour ); + + // Try to find another contour we can merge with in this level. + + for( iC2 = 0; iC2 < poLevel->GetContourCount(); iC2++ ) + { + GDALContourItem *poOther = poLevel->GetContour( iC2 ); + + if( poOther->Merge( poTarget ) ) + break; + } + + // If we didn't merge it, then eject (write) it out. + if( iC2 == poLevel->GetContourCount() ) + { + if( pfnWriter != NULL ) + { + // If direction is wrong, then reverse before ejecting. + poTarget->PrepareEjection(); + + eErr = pfnWriter( poTarget->dfLevel, poTarget->nPoints, + poTarget->padfX, poTarget->padfY, + pWriterCBData ); + } + } + + delete poTarget; + } + } + + return eErr; +} + +/************************************************************************/ +/* FindLevel() */ +/************************************************************************/ + +GDALContourLevel *GDALContourGenerator::FindLevel( double dfLevel ) + +{ + int nStart=0, nEnd=nLevelCount-1, nMiddle; + +/* -------------------------------------------------------------------- */ +/* Binary search to find the requested level. */ +/* -------------------------------------------------------------------- */ + while( nStart <= nEnd ) + { + nMiddle = (nEnd + nStart) / 2; + + double dfMiddleLevel = papoLevels[nMiddle]->GetLevel(); + + if( dfMiddleLevel < dfLevel ) + nStart = nMiddle + 1; + else if( dfMiddleLevel > dfLevel ) + nEnd = nMiddle - 1; + else + return papoLevels[nMiddle]; + } + +/* -------------------------------------------------------------------- */ +/* Didn't find the level, create a new one and insert it in */ +/* order. */ +/* -------------------------------------------------------------------- */ + GDALContourLevel *poLevel = new GDALContourLevel( dfLevel ); + + if( nLevelMax == nLevelCount ) + { + nLevelMax = nLevelMax * 2 + 10; + papoLevels = (GDALContourLevel **) + CPLRealloc( papoLevels, sizeof(void*) * nLevelMax ); + } + + if( nLevelCount - nEnd - 1 > 0 ) + memmove( papoLevels + nEnd + 2, papoLevels + nEnd + 1, + (nLevelCount - nEnd - 1) * sizeof(void*) ); + papoLevels[nEnd+1] = poLevel; + nLevelCount++; + + return poLevel; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALContourLevel */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALContourLevel() */ +/************************************************************************/ + +GDALContourLevel::GDALContourLevel( double dfLevelIn ) + +{ + dfLevel = dfLevelIn; + nEntryMax = 0; + nEntryCount = 0; + papoEntries = NULL; +} + +/************************************************************************/ +/* ~GDALContourLevel() */ +/************************************************************************/ + +GDALContourLevel::~GDALContourLevel() + +{ + CPLAssert( nEntryCount == 0 ); + CPLFree( papoEntries ); +} + +/************************************************************************/ +/* AdjustContour() */ +/* */ +/* Assume the indicated contour's tail may have changed, and */ +/* adjust it up or down in the list of contours to re-establish */ +/* proper ordering. */ +/************************************************************************/ + +void GDALContourLevel::AdjustContour( int iChanged ) + +{ + while( iChanged > 0 + && papoEntries[iChanged]->dfTailX < papoEntries[iChanged-1]->dfTailX ) + { + GDALContourItem *poTemp = papoEntries[iChanged]; + papoEntries[iChanged] = papoEntries[iChanged-1]; + papoEntries[iChanged-1] = poTemp; + iChanged--; + } + + while( iChanged < nEntryCount-1 + && papoEntries[iChanged]->dfTailX > papoEntries[iChanged+1]->dfTailX ) + { + GDALContourItem *poTemp = papoEntries[iChanged]; + papoEntries[iChanged] = papoEntries[iChanged+1]; + papoEntries[iChanged+1] = poTemp; + iChanged++; + } +} + +/************************************************************************/ +/* RemoveContour() */ +/************************************************************************/ + +void GDALContourLevel::RemoveContour( int iTarget ) + +{ + if( iTarget < nEntryCount ) + memmove( papoEntries + iTarget, papoEntries + iTarget + 1, + (nEntryCount - iTarget - 1) * sizeof(void*) ); + nEntryCount--; +} + +/************************************************************************/ +/* FindContour() */ +/* */ +/* Perform a binary search to find the requested "tail" */ +/* location. If not available return -1. In theory there can */ +/* be more than one contour with the same tail X and different */ +/* Y tails ... ensure we check against them all. */ +/************************************************************************/ + +int GDALContourLevel::FindContour( double dfX, double dfY ) + +{ + int nStart = 0, nEnd = nEntryCount-1, nMiddle; + + while( nEnd >= nStart ) + { + nMiddle = (nEnd + nStart) / 2; + + double dfMiddleX = papoEntries[nMiddle]->dfTailX; + + if( dfMiddleX < dfX ) + nStart = nMiddle + 1; + else if( dfMiddleX > dfX ) + nEnd = nMiddle - 1; + else + { + while( nMiddle > 0 + && fabs(papoEntries[nMiddle]->dfTailX-dfX) < JOIN_DIST ) + nMiddle--; + + while( nMiddle < nEntryCount + && fabs(papoEntries[nMiddle]->dfTailX-dfX) < JOIN_DIST ) + { + if( fabs(papoEntries[nMiddle]->padfY[papoEntries[nMiddle]->nPoints-1] - dfY) < JOIN_DIST ) + return nMiddle; + nMiddle++; + } + + return -1; + } + } + + return -1; +} + +/************************************************************************/ +/* InsertContour() */ +/* */ +/* Ensure the newly added contour is placed in order according */ +/* to the X value relative to the other contours. */ +/************************************************************************/ + +int GDALContourLevel::InsertContour( GDALContourItem *poNewContour ) + +{ +/* -------------------------------------------------------------------- */ +/* Find where to insert by binary search. */ +/* -------------------------------------------------------------------- */ + int nStart = 0, nEnd = nEntryCount-1, nMiddle; + + while( nEnd >= nStart ) + { + nMiddle = (nEnd + nStart) / 2; + + double dfMiddleX = papoEntries[nMiddle]->dfTailX; + + if( dfMiddleX < poNewContour->dfLevel ) + nStart = nMiddle + 1; + else if( dfMiddleX > poNewContour->dfLevel ) + nEnd = nMiddle - 1; + else + { + nEnd = nMiddle - 1; + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Do we need to grow the array? */ +/* -------------------------------------------------------------------- */ + if( nEntryMax == nEntryCount ) + { + nEntryMax = nEntryMax * 2 + 10; + papoEntries = (GDALContourItem **) + CPLRealloc( papoEntries, sizeof(void*) * nEntryMax ); + } + +/* -------------------------------------------------------------------- */ +/* Insert the new contour at the appropriate location. */ +/* -------------------------------------------------------------------- */ + if( nEntryCount - nEnd - 1 > 0 ) + memmove( papoEntries + nEnd + 2, papoEntries + nEnd + 1, + (nEntryCount - nEnd - 1) * sizeof(void*) ); + papoEntries[nEnd+1] = poNewContour; + nEntryCount++; + + return nEnd+1; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* GDALContourItem */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALContourItem() */ +/************************************************************************/ + +GDALContourItem::GDALContourItem( double dfLevelIn ) + +{ + dfLevel = dfLevelIn; + bRecentlyAccessed = FALSE; + nPoints = 0; + nMaxPoints = 0; + padfX = NULL; + padfY = NULL; + + bLeftIsHigh = FALSE; + + dfTailX = 0.0; +} + +/************************************************************************/ +/* ~GDALContourItem() */ +/************************************************************************/ + +GDALContourItem::~GDALContourItem() + +{ + CPLFree( padfX ); + CPLFree( padfY ); +} + +/************************************************************************/ +/* AddSegment() */ +/************************************************************************/ + +int GDALContourItem::AddSegment( double dfXStart, double dfYStart, + double dfXEnd, double dfYEnd, + int bLeftHigh) + +{ + MakeRoomFor( nPoints + 1 ); + +/* -------------------------------------------------------------------- */ +/* If there are no segments, just add now. */ +/* -------------------------------------------------------------------- */ + if( nPoints == 0 ) + { + nPoints = 2; + + padfX[0] = dfXStart; + padfY[0] = dfYStart; + padfX[1] = dfXEnd; + padfY[1] = dfYEnd; + bRecentlyAccessed = TRUE; + + dfTailX = padfX[1]; + + // Here we know that the left of this vector is the high side + bLeftIsHigh = bLeftHigh; + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Try to matching up with one of the ends, and insert. */ +/* -------------------------------------------------------------------- */ + if( fabs(padfX[nPoints-1]-dfXStart) < JOIN_DIST + && fabs(padfY[nPoints-1]-dfYStart) < JOIN_DIST ) + { + padfX[nPoints] = dfXEnd; + padfY[nPoints] = dfYEnd; + nPoints++; + + bRecentlyAccessed = TRUE; + + dfTailX = dfXEnd; + + return TRUE; + } + else if( fabs(padfX[nPoints-1]-dfXEnd) < JOIN_DIST + && fabs(padfY[nPoints-1]-dfYEnd) < JOIN_DIST ) + { + padfX[nPoints] = dfXStart; + padfY[nPoints] = dfYStart; + nPoints++; + + bRecentlyAccessed = TRUE; + + dfTailX = dfXStart; + + return TRUE; + } + else + return FALSE; +} + +/************************************************************************/ +/* Merge() */ +/************************************************************************/ + +int GDALContourItem::Merge( GDALContourItem *poOther ) + +{ + if( poOther->dfLevel != dfLevel ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Try to matching up with one of the ends, and insert. */ +/* -------------------------------------------------------------------- */ + if( fabs(padfX[nPoints-1]-poOther->padfX[0]) < JOIN_DIST + && fabs(padfY[nPoints-1]-poOther->padfY[0]) < JOIN_DIST ) + { + MakeRoomFor( nPoints + poOther->nPoints - 1 ); + + memcpy( padfX + nPoints, poOther->padfX + 1, + sizeof(double) * (poOther->nPoints-1) ); + memcpy( padfY + nPoints, poOther->padfY + 1, + sizeof(double) * (poOther->nPoints-1) ); + nPoints += poOther->nPoints - 1; + + bRecentlyAccessed = TRUE; + + dfTailX = padfX[nPoints-1]; + + return TRUE; + } + else if( fabs(padfX[0]-poOther->padfX[poOther->nPoints-1]) < JOIN_DIST + && fabs(padfY[0]-poOther->padfY[poOther->nPoints-1]) < JOIN_DIST ) + { + MakeRoomFor( nPoints + poOther->nPoints - 1 ); + + memmove( padfX + poOther->nPoints - 1, padfX, + sizeof(double) * nPoints ); + memmove( padfY + poOther->nPoints - 1, padfY, + sizeof(double) * nPoints ); + memcpy( padfX, poOther->padfX, + sizeof(double) * (poOther->nPoints-1) ); + memcpy( padfY, poOther->padfY, + sizeof(double) * (poOther->nPoints-1) ); + nPoints += poOther->nPoints - 1; + + bRecentlyAccessed = TRUE; + + dfTailX = padfX[nPoints-1]; + + return TRUE; + } + else if( fabs(padfX[nPoints-1]-poOther->padfX[poOther->nPoints-1]) < JOIN_DIST + && fabs(padfY[nPoints-1]-poOther->padfY[poOther->nPoints-1]) < JOIN_DIST ) + { + int i; + + MakeRoomFor( nPoints + poOther->nPoints - 1 ); + + for( i = 0; i < poOther->nPoints-1; i++ ) + { + padfX[i+nPoints] = poOther->padfX[poOther->nPoints-i-2]; + padfY[i+nPoints] = poOther->padfY[poOther->nPoints-i-2]; + } + + nPoints += poOther->nPoints - 1; + + bRecentlyAccessed = TRUE; + + dfTailX = padfX[nPoints-1]; + + return TRUE; + } + else if( fabs(padfX[0]-poOther->padfX[0]) < JOIN_DIST + && fabs(padfY[0]-poOther->padfY[0]) < JOIN_DIST ) + { + int i; + + MakeRoomFor( nPoints + poOther->nPoints - 1 ); + + memmove( padfX + poOther->nPoints - 1, padfX, + sizeof(double) * nPoints ); + memmove( padfY + poOther->nPoints - 1, padfY, + sizeof(double) * nPoints ); + + for( i = 0; i < poOther->nPoints-1; i++ ) + { + padfX[i] = poOther->padfX[poOther->nPoints - i - 1]; + padfY[i] = poOther->padfY[poOther->nPoints - i - 1]; + } + + nPoints += poOther->nPoints - 1; + + bRecentlyAccessed = TRUE; + + dfTailX = padfX[nPoints-1]; + + return TRUE; + } + else + return FALSE; +} + +/************************************************************************/ +/* MakeRoomFor() */ +/************************************************************************/ + +void GDALContourItem::MakeRoomFor( int nNewPoints ) + +{ + if( nNewPoints > nMaxPoints ) + { + nMaxPoints = nNewPoints * 2 + 50; + padfX = (double *) CPLRealloc(padfX,sizeof(double) * nMaxPoints); + padfY = (double *) CPLRealloc(padfY,sizeof(double) * nMaxPoints); + } +} + +/************************************************************************/ +/* PrepareEjection() */ +/************************************************************************/ + +void GDALContourItem::PrepareEjection() + +{ + /* If left side is the high side, then reverse to get curve normal + ** pointing downwards + */ + if( bLeftIsHigh ) + { + int i; + + // Reverse the arrays + for( i = 0; i < nPoints / 2; i++ ) + { + double dfTemp; + dfTemp = padfX[i]; + padfX[i] = padfX[ nPoints - i - 1]; + padfX[ nPoints - i - 1] = dfTemp; + + dfTemp = padfY[i]; + padfY[i] = padfY[ nPoints - i - 1]; + padfY[ nPoints - i - 1] = dfTemp; + } + } +} + + +/************************************************************************/ +/* ==================================================================== */ +/* Additional C Callable Functions */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* OGRContourWriter() */ +/************************************************************************/ + +CPLErr OGRContourWriter( double dfLevel, + int nPoints, double *padfX, double *padfY, + void *pInfo ) + +{ + OGRContourWriterInfo *poInfo = (OGRContourWriterInfo *) pInfo; + OGRFeatureH hFeat; + OGRGeometryH hGeom; + int iPoint; + + hFeat = OGR_F_Create( OGR_L_GetLayerDefn( (OGRLayerH) poInfo->hLayer ) ); + + if( poInfo->nIDField != -1 ) + OGR_F_SetFieldInteger( hFeat, poInfo->nIDField, poInfo->nNextID++ ); + + if( poInfo->nElevField != -1 ) + OGR_F_SetFieldDouble( hFeat, poInfo->nElevField, dfLevel ); + + hGeom = OGR_G_CreateGeometry( wkbLineString ); + + for( iPoint = nPoints-1; iPoint >= 0; iPoint-- ) + { + OGR_G_SetPoint( hGeom, iPoint, + poInfo->adfGeoTransform[0] + + poInfo->adfGeoTransform[1] * padfX[iPoint] + + poInfo->adfGeoTransform[2] * padfY[iPoint], + poInfo->adfGeoTransform[3] + + poInfo->adfGeoTransform[4] * padfX[iPoint] + + poInfo->adfGeoTransform[5] * padfY[iPoint], + dfLevel ); + } + + OGR_F_SetGeometryDirectly( hFeat, hGeom ); + + OGR_L_CreateFeature( (OGRLayerH) poInfo->hLayer, hFeat ); + OGR_F_Destroy( hFeat ); + + return CE_None; +} +#endif // OGR_ENABLED + +/************************************************************************/ +/* GDALContourGenerate() */ +/************************************************************************/ + +/** + * Create vector contours from raster DEM. + * + * This algorithm will generate contour vectors for the input raster band + * on the requested set of contour levels. The vector contours are written + * to the passed in OGR vector layer. Also, a NODATA value may be specified + * to identify pixels that should not be considered in contour line generation. + * + * The gdal/apps/gdal_contour.cpp mainline can be used as an example of + * how to use this function. + * + * ALGORITHM RULES + +For contouring purposes raster pixel values are assumed to represent a point +value at the center of the corresponding pixel region. For the purpose of +contour generation we virtually connect each pixel center to the values to +the left, right, top and bottom. We assume that the pixel value is linearly +interpolated between the pixel centers along each line, and determine where +(if any) contour lines will appear along these line segements. Then the +contour crossings are connected. + +This means that contour lines' nodes won't actually be on pixel edges, but +rather along vertical and horizontal lines connecting the pixel centers. + +\verbatim +General Case: + + 5 | | 3 + -- + ---------------- + -- + | | + | | + | | + | | + 10 + | + |\ | + | \ | + -- + -+-------------- + -- + 12 | 10 | 1 + + +Saddle Point: + + 5 | | 12 + -- + -------------+-- + -- + | \ | + | \| + | + + | | + + | + |\ | + | \ | + -- + -+-------------- + -- + 12 | | 1 + +or: + + 5 | | 12 + -- + -------------+-- + -- + | __/ | + | ___/ | + | ___/ __+ + | / __/ | + +' __/ | + | __/ | + | ,__/ | + -- + -+-------------- + -- + 12 | | 1 +\endverbatim + +Nodata: + +In the "nodata" case we treat the whole nodata pixel as a no-mans land. +We extend the corner pixels near the nodata out to half way and then +construct extra lines from those points to the center which is assigned +an averaged value from the two nearby points (in this case (12+3+5)/3). + +\verbatim + 5 | | 3 + -- + ---------------- + -- + | | + | | + | 6.7 | + | +---------+ 3 + 10 +___ | + | \____+ 10 + | | + -- + -------+ + + 12 | 12 (nodata) + +\endverbatim + + * + * @param hBand The band to read raster data from. The whole band will be + * processed. + * + * @param dfContourInterval The elevation interval between contours generated. + * + * @param dfContourBase The "base" relative to which contour intervals are + * applied. This is normally zero, but could be different. To generate 10m + * contours at 5, 15, 25, ... the ContourBase would be 5. + * + * @param nFixedLevelCount The number of fixed levels. If this is greater than + * zero, then fixed levels will be used, and ContourInterval and ContourBase + * are ignored. + * + * @param padfFixedLevels The list of fixed contour levels at which contours + * should be generated. It will contain FixedLevelCount entries, and may be + * NULL if fixed levels are disabled (FixedLevelCount = 0). + * + * @param bUseNoData If TRUE the dfNoDataValue will be used. + * + * @param dfNoDataValue The value to use as a "nodata" value. That is, a + * pixel value which should be ignored in generating contours as if the value + * of the pixel were not known. + * + * @param hLayer The layer to which new contour vectors will be written. + * Each contour will have a LINESTRING geometry attached to it. This + * is really of type OGRLayerH, but void * is used to avoid pulling the + * ogr_api.h file in here. + * + * @param iIDField If not -1 this will be used as a field index to indicate + * where a unique id should be written for each feature (contour) written. + * + * @param iElevField If not -1 this will be used as a field index to indicate + * where the elevation value of the contour should be written. + * + * @param pfnProgress A GDALProgressFunc that may be used to report progress + * to the user, or to interrupt the algorithm. May be NULL if not required. + * + * @param pProgressArg The callback data for the pfnProgress function. + * + * @return CE_None on success or CE_Failure if an error occurs. + */ + +CPLErr GDALContourGenerate( GDALRasterBandH hBand, + double dfContourInterval, double dfContourBase, + int nFixedLevelCount, double *padfFixedLevels, + int bUseNoData, double dfNoDataValue, + void *hLayer, int iIDField, int iElevField, + GDALProgressFunc pfnProgress, void *pProgressArg ) + +{ +#ifndef OGR_ENABLED + CPLError(CE_Failure, CPLE_NotSupported, "GDALContourGenerate() unimplemented in a non OGR build"); + return CE_Failure; +#else + VALIDATE_POINTER1( hBand, "GDALContourGenerate", CE_Failure ); + + OGRContourWriterInfo oCWI; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + if( !pfnProgress( 0.0, "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Setup contour writer information. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hSrcDS; + + oCWI.hLayer = (OGRLayerH) hLayer; + + oCWI.nElevField = iElevField; + oCWI.nIDField = iIDField; + + oCWI.adfGeoTransform[0] = 0.0; + oCWI.adfGeoTransform[1] = 1.0; + oCWI.adfGeoTransform[2] = 0.0; + oCWI.adfGeoTransform[3] = 0.0; + oCWI.adfGeoTransform[4] = 0.0; + oCWI.adfGeoTransform[5] = 1.0; + hSrcDS = GDALGetBandDataset( hBand ); + if( hSrcDS != NULL ) + GDALGetGeoTransform( hSrcDS, oCWI.adfGeoTransform ); + oCWI.nNextID = 0; + +/* -------------------------------------------------------------------- */ +/* Setup contour generator. */ +/* -------------------------------------------------------------------- */ + int nXSize = GDALGetRasterBandXSize( hBand ); + int nYSize = GDALGetRasterBandYSize( hBand ); + + GDALContourGenerator oCG( nXSize, nYSize, OGRContourWriter, &oCWI ); + + if( nFixedLevelCount > 0 ) + oCG.SetFixedLevels( nFixedLevelCount, padfFixedLevels ); + else + oCG.SetContourLevels( dfContourInterval, dfContourBase ); + + if( bUseNoData ) + oCG.SetNoData( dfNoDataValue ); + +/* -------------------------------------------------------------------- */ +/* Feed the data into the contour generator. */ +/* -------------------------------------------------------------------- */ + int iLine; + double *padfScanline; + CPLErr eErr = CE_None; + + padfScanline = (double *) VSIMalloc(sizeof(double) * nXSize); + if (padfScanline == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "VSIMalloc(): Out of memory in GDALContourGenerate" ); + return CE_Failure; + } + + for( iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ ) + { + GDALRasterIO( hBand, GF_Read, 0, iLine, nXSize, 1, + padfScanline, nXSize, 1, GDT_Float64, 0, 0 ); + eErr = oCG.FeedLine( padfScanline ); + + if( eErr == CE_None + && !pfnProgress( (iLine+1) / (double) nYSize, "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + + CPLFree( padfScanline ); + + return eErr; +#endif // OGR_ENABLED +} diff --git a/bazaar/plugin/gdal/alg/fpolygonize.cpp b/bazaar/plugin/gdal/alg/fpolygonize.cpp new file mode 100644 index 000000000..5883fa839 --- /dev/null +++ b/bazaar/plugin/gdal/alg/fpolygonize.cpp @@ -0,0 +1,820 @@ +/****************************************************************************** + * $Id: fpolygonize.cpp 22501 2011-06-04 21:28:47Z rouault $ + * + * Project: GDAL + * Purpose: Version of Raster to Polygon Converter using float buffers. + * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com. Most of the code + * taken from GDALPolygonize.cpp, by Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Jorge Arevalo + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_alg_priv.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: fpolygonize.cpp 22501 2011-06-04 21:28:47Z rouault $"); + +#define GP_NODATA_MARKER -51502112 + +#ifdef OGR_ENABLED + +/******************************************************************************/ +/* GDALFloatEquals() */ +/* Code from: */ +/* http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */ +/******************************************************************************/ +GBool GDALFloatEquals(float A, float B) +{ + /** + * This function will allow maxUlps-1 floats between A and B. + */ + int maxUlps = MAX_ULPS; + int aInt, bInt; + + /** + * Make sure maxUlps is non-negative and small enough that the default NAN + * won't compare as equal to anything. + */ + CPLAssert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); + + /** + * This assignation could violate strict aliasing. It causes a warning with + * gcc -O2. Use of memcpy preferred. Credits for Even Rouault. Further info + * at http://trac.osgeo.org/gdal/ticket/4005#comment:6 + */ + //int aInt = *(int*)&A; + memcpy(&aInt, &A, 4); + + /** + * Make aInt lexicographically ordered as a twos-complement int + */ + if (aInt < 0) + aInt = 0x80000000 - aInt; + /** + * Make bInt lexicographically ordered as a twos-complement int + */ + //int bInt = *(int*)&B; + memcpy(&bInt, &B, 4); + + if (bInt < 0) + bInt = 0x80000000 - bInt; + int intDiff = abs(aInt - bInt); + if (intDiff <= maxUlps) + return true; + return false; +} + +/************************************************************************/ +/* ==================================================================== */ +/* RPolygonF */ +/* */ +/* This is a helper class to hold polygons while they are being */ +/* formed in memory, and to provide services to coalesce a much */ +/* of edge sections into complete rings. */ +/* ==================================================================== */ +/************************************************************************/ + +class RPolygonF { +public: + RPolygonF( float fValue ) { fPolyValue = fValue; nLastLineUpdated = -1; } + + float fPolyValue; + int nLastLineUpdated; + + std::vector< std::vector > aanXY; + + void AddSegment( int x1, int y1, int x2, int y2 ); + void Dump(); + void Coalesce(); + void Merge( int iBaseString, int iSrcString, int iDirection ); +}; + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ +void RPolygonF::Dump() +{ + size_t iString; + + printf( "RPolygonF: Value=%f, LastLineUpdated=%d\n", + fPolyValue, nLastLineUpdated ); + + for( iString = 0; iString < aanXY.size(); iString++ ) + { + std::vector &anString = aanXY[iString]; + size_t iVert; + + printf( " String %d:\n", (int) iString ); + for( iVert = 0; iVert < anString.size(); iVert += 2 ) + { + printf( " (%d,%d)\n", anString[iVert], anString[iVert+1] ); + } + } +} + +/************************************************************************/ +/* Coalesce() */ +/************************************************************************/ + +void RPolygonF::Coalesce() + +{ + size_t iBaseString; + +/* -------------------------------------------------------------------- */ +/* Iterate over loops starting from the first, trying to merge */ +/* other segments into them. */ +/* -------------------------------------------------------------------- */ + for( iBaseString = 0; iBaseString < aanXY.size(); iBaseString++ ) + { + std::vector &anBase = aanXY[iBaseString]; + int bMergeHappened = TRUE; + +/* -------------------------------------------------------------------- */ +/* Keep trying to merge the following strings into our target */ +/* "base" string till we have tried them all once without any */ +/* mergers. */ +/* -------------------------------------------------------------------- */ + while( bMergeHappened ) + { + size_t iString; + + bMergeHappened = FALSE; + +/* -------------------------------------------------------------------- */ +/* Loop over the following strings, trying to find one we can */ +/* merge onto the end of our base string. */ +/* -------------------------------------------------------------------- */ + for( iString = iBaseString+1; + iString < aanXY.size(); + iString++ ) + { + std::vector &anString = aanXY[iString]; + + if( anBase[anBase.size()-2] == anString[0] + && anBase[anBase.size()-1] == anString[1] ) + { + Merge( iBaseString, iString, 1 ); + bMergeHappened = TRUE; + } + else if( anBase[anBase.size()-2] == anString[anString.size()-2] + && anBase[anBase.size()-1] == anString[anString.size()-1] ) + { + Merge( iBaseString, iString, -1 ); + bMergeHappened = TRUE; + } + } + } + + /* At this point our loop *should* be closed! */ + + CPLAssert( anBase[0] == anBase[anBase.size()-2] + && anBase[1] == anBase[anBase.size()-1] ); + } + +} + +/************************************************************************/ +/* Merge() */ +/************************************************************************/ + +void RPolygonF::Merge( int iBaseString, int iSrcString, int iDirection ) + +{ + std::vector &anBase = aanXY[iBaseString]; + std::vector &anString = aanXY[iSrcString]; + int iStart, iEnd, i; + + if( iDirection == 1 ) + { + iStart = 1; + iEnd = anString.size() / 2; + } + else + { + iStart = anString.size() / 2 - 2; + iEnd = -1; + } + + for( i = iStart; i != iEnd; i += iDirection ) + { + anBase.push_back( anString[i*2+0] ); + anBase.push_back( anString[i*2+1] ); + } + + if( iSrcString < ((int) aanXY.size())-1 ) + aanXY[iSrcString] = aanXY[aanXY.size()-1]; + + size_t nSize = aanXY.size(); + aanXY.resize(nSize-1); +} + +/************************************************************************/ +/* AddSegment() */ +/************************************************************************/ + +void RPolygonF::AddSegment( int x1, int y1, int x2, int y2 ) + +{ + nLastLineUpdated = MAX(y1, y2); + +/* -------------------------------------------------------------------- */ +/* Is there an existing string ending with this? */ +/* -------------------------------------------------------------------- */ + size_t iString; + + for( iString = 0; iString < aanXY.size(); iString++ ) + { + std::vector &anString = aanXY[iString]; + size_t nSSize = anString.size(); + + if( anString[nSSize-2] == x1 + && anString[nSSize-1] == y1 ) + { + int nTemp; + + nTemp = x2; + x2 = x1; + x1 = nTemp; + + nTemp = y2; + y2 = y1; + y1 = nTemp; + } + + if( anString[nSSize-2] == x2 + && anString[nSSize-1] == y2 ) + { + // We are going to add a segment, but should we just extend + // an existing segment already going in the right direction? + + int nLastLen = MAX(ABS(anString[nSSize-4]-anString[nSSize-2]), + ABS(anString[nSSize-3]-anString[nSSize-1])); + + if( nSSize >= 4 + && (anString[nSSize-4] - anString[nSSize-2] + == (anString[nSSize-2] - x1)*nLastLen) + && (anString[nSSize-3] - anString[nSSize-1] + == (anString[nSSize-1] - y1)*nLastLen) ) + { + anString.pop_back(); + anString.pop_back(); + } + + anString.push_back( x1 ); + anString.push_back( y1 ); + return; + } + } + +/* -------------------------------------------------------------------- */ +/* Create a new string. */ +/* -------------------------------------------------------------------- */ + size_t nSize = aanXY.size(); + aanXY.resize(nSize + 1); + std::vector &anString = aanXY[nSize]; + + anString.push_back( x1 ); + anString.push_back( y1 ); + anString.push_back( x2 ); + anString.push_back( y2 ); + + return; +} + +/************************************************************************/ +/* ==================================================================== */ +/* End of RPolygonF */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* AddEdges() */ +/* */ +/* Examine one pixel and compare to its neighbour above */ +/* (previous) and right. If they are different polygon ids */ +/* then add the pixel edge to this polygon and the one on the */ +/* other side of the edge. */ +/************************************************************************/ + +static void AddEdges( GInt32 *panThisLineId, GInt32 *panLastLineId, + GInt32 *panPolyIdMap, float *pafPolyValue, + RPolygonF **papoPoly, int iX, int iY ) + +{ + int nThisId = panThisLineId[iX]; + int nRightId = panThisLineId[iX+1]; + int nPreviousId = panLastLineId[iX]; + int iXReal = iX - 1; + + if( nThisId != -1 ) + nThisId = panPolyIdMap[nThisId]; + if( nRightId != -1 ) + nRightId = panPolyIdMap[nRightId]; + if( nPreviousId != -1 ) + nPreviousId = panPolyIdMap[nPreviousId]; + + if( nThisId != nPreviousId ) + { + if( nThisId != -1 ) + { + if( papoPoly[nThisId] == NULL ) + papoPoly[nThisId] = new RPolygonF( pafPolyValue[nThisId] ); + + papoPoly[nThisId]->AddSegment( iXReal, iY, iXReal+1, iY ); + } + if( nPreviousId != -1 ) + { + if( papoPoly[nPreviousId] == NULL ) + papoPoly[nPreviousId] = new RPolygonF(pafPolyValue[nPreviousId]); + + papoPoly[nPreviousId]->AddSegment( iXReal, iY, iXReal+1, iY ); + } + } + + if( nThisId != nRightId ) + { + if( nThisId != -1 ) + { + if( papoPoly[nThisId] == NULL ) + papoPoly[nThisId] = new RPolygonF(pafPolyValue[nThisId]); + + papoPoly[nThisId]->AddSegment( iXReal+1, iY, iXReal+1, iY+1 ); + } + + if( nRightId != -1 ) + { + if( papoPoly[nRightId] == NULL ) + papoPoly[nRightId] = new RPolygonF(pafPolyValue[nRightId]); + + papoPoly[nRightId]->AddSegment( iXReal+1, iY, iXReal+1, iY+1 ); + } + } +} + +/************************************************************************/ +/* EmitPolygonToLayer() */ +/************************************************************************/ + +static CPLErr +EmitPolygonToLayer( OGRLayerH hOutLayer, int iPixValField, + RPolygonF *poRPoly, double *padfGeoTransform ) + +{ + OGRFeatureH hFeat; + OGRGeometryH hPolygon; + +/* -------------------------------------------------------------------- */ +/* Turn bits of lines into coherent rings. */ +/* -------------------------------------------------------------------- */ + poRPoly->Coalesce(); + +/* -------------------------------------------------------------------- */ +/* Create the polygon geometry. */ +/* -------------------------------------------------------------------- */ + size_t iString; + + hPolygon = OGR_G_CreateGeometry( wkbPolygon ); + + for( iString = 0; iString < poRPoly->aanXY.size(); iString++ ) + { + std::vector &anString = poRPoly->aanXY[iString]; + OGRGeometryH hRing = OGR_G_CreateGeometry( wkbLinearRing ); + + int iVert; + + // we go last to first to ensure the linestring is allocated to + // the proper size on the first try. + for( iVert = anString.size()/2 - 1; iVert >= 0; iVert-- ) + { + double dfX, dfY; + int nPixelX, nPixelY; + + nPixelX = anString[iVert*2]; + nPixelY = anString[iVert*2+1]; + + dfX = padfGeoTransform[0] + + nPixelX * padfGeoTransform[1] + + nPixelY * padfGeoTransform[2]; + dfY = padfGeoTransform[3] + + nPixelX * padfGeoTransform[4] + + nPixelY * padfGeoTransform[5]; + + OGR_G_SetPoint_2D( hRing, iVert, dfX, dfY ); + } + + OGR_G_AddGeometryDirectly( hPolygon, hRing ); + } + +/* -------------------------------------------------------------------- */ +/* Create the feature object. */ +/* -------------------------------------------------------------------- */ + hFeat = OGR_F_Create( OGR_L_GetLayerDefn( hOutLayer ) ); + + OGR_F_SetGeometryDirectly( hFeat, hPolygon ); + + if( iPixValField >= 0 ) + OGR_F_SetFieldDouble( hFeat, iPixValField, (double)poRPoly->fPolyValue ); + +/* -------------------------------------------------------------------- */ +/* Write the to the layer. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + + if( OGR_L_CreateFeature( hOutLayer, hFeat ) != OGRERR_NONE ) + eErr = CE_Failure; + + OGR_F_Destroy( hFeat ); + + return eErr; +} + +/************************************************************************/ +/* GPMaskImageData() */ +/* */ +/* Mask out image pixels to a special nodata value if the mask */ +/* band is zero. */ +/************************************************************************/ + +static CPLErr +GPMaskImageData( GDALRasterBandH hMaskBand, GByte* pabyMaskLine, int iY, int nXSize, + float *pafImageLine ) + +{ + CPLErr eErr; + + eErr = GDALRasterIO( hMaskBand, GF_Read, 0, iY, nXSize, 1, + pabyMaskLine, nXSize, 1, GDT_Byte, 0, 0 ); + if( eErr == CE_None ) + { + int i; + for( i = 0; i < nXSize; i++ ) + { + if( pabyMaskLine[i] == 0 ) + pafImageLine[i] = GP_NODATA_MARKER; + } + } + + return eErr; +} +#endif // OGR_ENABLED + +/************************************************************************/ +/* GDALFPolygonize() */ +/************************************************************************/ + +/** + * Create polygon coverage from raster data. + * + * This function creates vector polygons for all connected regions of pixels in + * the raster sharing a common pixel value. Optionally each polygon may be + * labelled with the pixel value in an attribute. Optionally a mask band + * can be provided to determine which pixels are eligible for processing. + * + * The source pixel band values are read into a 32bit float buffer. If you want + * to use a (probably faster) version using signed 32bit integer buffer, see + * GDALPolygonize() at polygonize.cpp. + * + * Polygon features will be created on the output layer, with polygon + * geometries representing the polygons. The polygon geometries will be + * in the georeferenced coordinate system of the image (based on the + * geotransform of the source dataset). It is acceptable for the output + * layer to already have features. Note that GDALFPolygonize() does not + * set the coordinate system on the output layer. Application code should + * do this when the layer is created, presumably matching the raster + * coordinate system. + * + * The algorithm used attempts to minimize memory use so that very large + * rasters can be processed. However, if the raster has many polygons + * or very large/complex polygons, the memory use for holding polygon + * enumerations and active polygon geometries may grow to be quite large. + * + * The algorithm will generally produce very dense polygon geometries, with + * edges that follow exactly on pixel boundaries for all non-interior pixels. + * For non-thematic raster data (such as satellite images) the result will + * essentially be one small polygon per pixel, and memory and output layer + * sizes will be substantial. The algorithm is primarily intended for + * relatively simple thematic imagery, masks, and classification results. + * + * @param hSrcBand the source raster band to be processed. + * @param hMaskBand an optional mask band. All pixels in the mask band with a + * value other than zero will be considered suitable for collection as + * polygons. + * @param hOutLayer the vector feature layer to which the polygons should + * be written. + * @param iPixValField the attribute field index indicating the feature + * attribute into which the pixel value of the polygon should be written. + * @param papszOptions a name/value list of additional options + *
+ *
"8CONNECTED":
May be set to "8" to use 8 connectedness. + * Otherwise 4 connectedness will be applied to the algorithm + *
+ * @param pfnProgress callback for reporting algorithm progress matching the + * GDALProgressFunc() semantics. May be NULL. + * @param pProgressArg callback argument passed to pfnProgress. + * + * @return CE_None on success or CE_Failure on a failure. + * + * @since GDAL 1.9.0 + */ + +CPLErr CPL_STDCALL +GDALFPolygonize( GDALRasterBandH hSrcBand, + GDALRasterBandH hMaskBand, + OGRLayerH hOutLayer, int iPixValField, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ +#ifndef OGR_ENABLED + CPLError(CE_Failure, CPLE_NotSupported, "GDALFPolygonize() unimplemented in a non OGR build"); + return CE_Failure; +#else + VALIDATE_POINTER1( hSrcBand, "GDALFPolygonize", CE_Failure ); + VALIDATE_POINTER1( hOutLayer, "GDALFPolygonize", CE_Failure ); + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + int nConnectedness = CSLFetchNameValue( papszOptions, "8CONNECTED" ) ? 8 : 4; + +/* -------------------------------------------------------------------- */ +/* Confirm our output layer will support feature creation. */ +/* -------------------------------------------------------------------- */ + if( !OGR_L_TestCapability( hOutLayer, OLCSequentialWrite ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Output feature layer does not appear to support creation\n" + "of features in GDALFPolygonize()." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Allocate working buffers. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + int nXSize = GDALGetRasterBandXSize( hSrcBand ); + int nYSize = GDALGetRasterBandYSize( hSrcBand ); + float *pafLastLineVal = (float *) VSIMalloc2(sizeof(float),nXSize + 2); + float *pafThisLineVal = (float *) VSIMalloc2(sizeof(float),nXSize + 2); + GInt32 *panLastLineId = (GInt32 *) VSIMalloc2(sizeof(GInt32),nXSize + 2); + GInt32 *panThisLineId = (GInt32 *) VSIMalloc2(sizeof(GInt32),nXSize + 2); + GByte *pabyMaskLine = (hMaskBand != NULL) ? (GByte *) VSIMalloc(nXSize) : NULL; + if (pafLastLineVal == NULL || pafThisLineVal == NULL || + panLastLineId == NULL || panThisLineId == NULL || + (hMaskBand != NULL && pabyMaskLine == NULL)) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Could not allocate enough memory for temporary buffers"); + CPLFree( panThisLineId ); + CPLFree( panLastLineId ); + CPLFree( pafThisLineVal ); + CPLFree( pafLastLineVal ); + CPLFree( pabyMaskLine ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Get the geotransform, if there is one, so we can convert the */ +/* vectors into georeferenced coordinates. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hSrcDS = GDALGetBandDataset( hSrcBand ); + double adfGeoTransform[6] = { 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; + + if( hSrcDS ) + GDALGetGeoTransform( hSrcDS, adfGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* The first pass over the raster is only used to build up the */ +/* polygon id map so we will know in advance what polygons are */ +/* what on the second pass. */ +/* -------------------------------------------------------------------- */ + int iY; + GDALRasterFPolygonEnumerator oFirstEnum(nConnectedness); + + for( iY = 0; eErr == CE_None && iY < nYSize; iY++ ) + { + eErr = GDALRasterIO( + hSrcBand, + GF_Read, 0, iY, nXSize, 1, + pafThisLineVal, nXSize, 1, GDT_Float32, 0, 0 ); + + if( eErr == CE_None && hMaskBand != NULL ) + eErr = GPMaskImageData( hMaskBand, pabyMaskLine, iY, nXSize, + pafThisLineVal ); + + if( iY == 0 ) + oFirstEnum.ProcessLine( + NULL, pafThisLineVal, NULL, panThisLineId, nXSize ); + else + oFirstEnum.ProcessLine( + pafLastLineVal, pafThisLineVal, + panLastLineId, panThisLineId, + nXSize ); + + // swap lines + float * pafTmp = pafLastLineVal; + pafLastLineVal = pafThisLineVal; + pafThisLineVal = pafTmp; + + GInt32 * panTmp = panThisLineId; + panThisLineId = panLastLineId; + panLastLineId = panTmp; + +/* -------------------------------------------------------------------- */ +/* Report progress, and support interrupts. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None + && !pfnProgress( 0.10 * ((iY+1) / (double) nYSize), + "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Make a pass through the maps, ensuring every polygon id */ +/* points to the final id it should use, not an intermediate */ +/* value. */ +/* -------------------------------------------------------------------- */ + oFirstEnum.CompleteMerges(); + +/* -------------------------------------------------------------------- */ +/* Initialize ids to -1 to serve as a nodata value for the */ +/* previous line, and past the beginning and end of the */ +/* scanlines. */ +/* -------------------------------------------------------------------- */ + int iX; + + panThisLineId[0] = -1; + panThisLineId[nXSize+1] = -1; + + for( iX = 0; iX < nXSize+2; iX++ ) + panLastLineId[iX] = -1; + +/* -------------------------------------------------------------------- */ +/* We will use a new enumerator for the second pass primariliy */ +/* so we can preserve the first pass map. */ +/* -------------------------------------------------------------------- */ + GDALRasterFPolygonEnumerator oSecondEnum(nConnectedness); + RPolygonF **papoPoly = (RPolygonF **) + CPLCalloc(sizeof(RPolygonF*),oFirstEnum.nNextPolygonId); + +/* ==================================================================== */ +/* Second pass during which we will actually collect polygon */ +/* edges as geometries. */ +/* ==================================================================== */ + for( iY = 0; eErr == CE_None && iY < nYSize+1; iY++ ) + { +/* -------------------------------------------------------------------- */ +/* Read the image data. */ +/* -------------------------------------------------------------------- */ + if( iY < nYSize ) + { + eErr = GDALRasterIO( hSrcBand, GF_Read, 0, iY, nXSize, 1, + pafThisLineVal, nXSize, 1, GDT_Float32, 0, 0 ); + + if( eErr == CE_None && hMaskBand != NULL ) + eErr = GPMaskImageData( hMaskBand, pabyMaskLine, iY, nXSize, + pafThisLineVal ); + } + + if( eErr != CE_None ) + continue; + +/* -------------------------------------------------------------------- */ +/* Determine what polygon the various pixels belong to (redoing */ +/* the same thing done in the first pass above). */ +/* -------------------------------------------------------------------- */ + if( iY == nYSize ) + { + for( iX = 0; iX < nXSize+2; iX++ ) + panThisLineId[iX] = -1; + } + else if( iY == 0 ) + oSecondEnum.ProcessLine( + NULL, pafThisLineVal, NULL, panThisLineId+1, nXSize ); + else + oSecondEnum.ProcessLine( + pafLastLineVal, pafThisLineVal, + panLastLineId+1, panThisLineId+1, + nXSize ); + +/* -------------------------------------------------------------------- */ +/* Add polygon edges to our polygon list for the pixel */ +/* boundaries within and above this line. */ +/* -------------------------------------------------------------------- */ + for( iX = 0; iX < nXSize+1; iX++ ) + { + AddEdges( panThisLineId, panLastLineId, + oFirstEnum.panPolyIdMap, oFirstEnum.pafPolyValue, + papoPoly, iX, iY ); + } + +/* -------------------------------------------------------------------- */ +/* Periodically we scan out polygons and write out those that */ +/* haven't been added to on the last line as we can be sure */ +/* they are complete. */ +/* -------------------------------------------------------------------- */ + if( iY % 8 == 7 ) + { + for( iX = 0; + eErr == CE_None && iX < oSecondEnum.nNextPolygonId; + iX++ ) + { + if( papoPoly[iX] && papoPoly[iX]->nLastLineUpdated < iY-1 ) + { + if( hMaskBand == NULL + || !GDALFloatEquals(papoPoly[iX]->fPolyValue, GP_NODATA_MARKER) ) + { + eErr = + EmitPolygonToLayer( hOutLayer, iPixValField, + papoPoly[iX], adfGeoTransform ); + } + delete papoPoly[iX]; + papoPoly[iX] = NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Swap pixel value, and polygon id lines to be ready for the */ +/* next line. */ +/* -------------------------------------------------------------------- */ + float *pafTmp = pafLastLineVal; + pafLastLineVal = pafThisLineVal; + pafThisLineVal = pafTmp; + + GInt32 *panTmp = panThisLineId; + panThisLineId = panLastLineId; + panLastLineId = panTmp; + +/* -------------------------------------------------------------------- */ +/* Report progress, and support interrupts. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None + && !pfnProgress( 0.10 + 0.90 * ((iY+1) / (double) nYSize), + "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Make a cleanup pass for all unflushed polygons. */ +/* -------------------------------------------------------------------- */ + for( iX = 0; eErr == CE_None && iX < oSecondEnum.nNextPolygonId; iX++ ) + { + if( papoPoly[iX] ) + { + if( hMaskBand == NULL + || !GDALFloatEquals(papoPoly[iX]->fPolyValue, GP_NODATA_MARKER) ) + { + eErr = + EmitPolygonToLayer( hOutLayer, iPixValField, + papoPoly[iX], adfGeoTransform ); + } + delete papoPoly[iX]; + papoPoly[iX] = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( panThisLineId ); + CPLFree( panLastLineId ); + CPLFree( pafThisLineVal ); + CPLFree( pafLastLineVal ); + CPLFree( pabyMaskLine ); + CPLFree( papoPoly ); + + return eErr; +#endif // OGR_ENABLED +} + diff --git a/bazaar/plugin/gdal/alg/gdal_alg.h b/bazaar/plugin/gdal/alg/gdal_alg.h new file mode 100644 index 000000000..b4734fbdb --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdal_alg.h @@ -0,0 +1,482 @@ +/****************************************************************************** + * $Id: gdal_alg.h 27850 2014-10-12 16:58:09Z rouault $ + * + * Project: GDAL Image Processing Algorithms + * Purpose: Prototypes, and definitions for various GDAL based algorithms. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_ALG_H_INCLUDED +#define GDAL_ALG_H_INCLUDED + +/** + * \file gdal_alg.h + * + * Public (C callable) GDAL algorithm entry points, and definitions. + */ + +#ifndef DOXYGEN_SKIP +#include "gdal.h" +#include "cpl_minixml.h" +#include "ogr_api.h" +#endif + +CPL_C_START + +int CPL_DLL CPL_STDCALL GDALComputeMedianCutPCT( GDALRasterBandH hRed, + GDALRasterBandH hGreen, + GDALRasterBandH hBlue, + int (*pfnIncludePixel)(int,int,void*), + int nColors, + GDALColorTableH hColorTable, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +int CPL_DLL CPL_STDCALL GDALDitherRGB2PCT( GDALRasterBandH hRed, + GDALRasterBandH hGreen, + GDALRasterBandH hBlue, + GDALRasterBandH hTarget, + GDALColorTableH hColorTable, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +int CPL_DLL CPL_STDCALL GDALChecksumImage( GDALRasterBandH hBand, + int nXOff, int nYOff, int nXSize, int nYSize ); + +CPLErr CPL_DLL CPL_STDCALL +GDALComputeProximity( GDALRasterBandH hSrcBand, + GDALRasterBandH hProximityBand, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +CPLErr CPL_DLL CPL_STDCALL +GDALFillNodata( GDALRasterBandH hTargetBand, + GDALRasterBandH hMaskBand, + double dfMaxSearchDist, + int bDeprecatedOption, + int nSmoothingIterations, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +CPLErr CPL_DLL CPL_STDCALL +GDALPolygonize( GDALRasterBandH hSrcBand, + GDALRasterBandH hMaskBand, + OGRLayerH hOutLayer, int iPixValField, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +CPLErr CPL_DLL CPL_STDCALL +GDALFPolygonize( GDALRasterBandH hSrcBand, + GDALRasterBandH hMaskBand, + OGRLayerH hOutLayer, int iPixValField, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +CPLErr CPL_DLL CPL_STDCALL +GDALSieveFilter( GDALRasterBandH hSrcBand, GDALRasterBandH hMaskBand, + GDALRasterBandH hDstBand, + int nSizeThreshold, int nConnectedness, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +/* + * Warp Related. + */ + +typedef int +(*GDALTransformerFunc)( void *pTransformerArg, + int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +#define GDAL_GTI2_SIGNATURE "GTI2" + +typedef struct { + GByte abySignature[4]; + const char *pszClassName; + GDALTransformerFunc pfnTransform; + void (*pfnCleanup)( void * pTransformerArg ); + CPLXMLNode *(*pfnSerialize)( void * pTransformerArg ); + void* (*pfnCreateSimilar)( void* pTransformerArg, double dfSrcRatioX, double dfSrcRatioY ); +} GDALTransformerInfo; + +void CPL_DLL GDALDestroyTransformer( void *pTransformerArg ); +int CPL_DLL GDALUseTransformer( void *pTranformerArg, + int bDstToSrc, int nPointCount, + double *x, double *y, double *z, + int *panSuccess ); +void* GDALCreateSimilarTransformer( void* psTransformerArg, double dfSrcRatioX, double dfSrcRatioY ); + + +/* High level transformer for going from image coordinates on one file + to image coordiantes on another, potentially doing reprojection, + utilizing GCPs or using the geotransform. */ + +void CPL_DLL * +GDALCreateGenImgProjTransformer( GDALDatasetH hSrcDS, const char *pszSrcWKT, + GDALDatasetH hDstDS, const char *pszDstWKT, + int bGCPUseOK, double dfGCPErrorThreshold, + int nOrder ); +void CPL_DLL * +GDALCreateGenImgProjTransformer2( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, + char **papszOptions ); +void CPL_DLL * +GDALCreateGenImgProjTransformer3( const char *pszSrcWKT, + const double *padfSrcGeoTransform, + const char *pszDstWKT, + const double *padfDstGeoTransform ); +void CPL_DLL GDALSetGenImgProjTransformerDstGeoTransform( void *, + const double * ); +void CPL_DLL GDALDestroyGenImgProjTransformer( void * ); +int CPL_DLL GDALGenImgProjTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +void GDALSetTransformerDstGeoTransform( void *, const double * ); + +/* Geo to geo reprojection transformer. */ +void CPL_DLL * +GDALCreateReprojectionTransformer( const char *pszSrcWKT, + const char *pszDstWKT ); +void CPL_DLL GDALDestroyReprojectionTransformer( void * ); +int CPL_DLL GDALReprojectionTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +/* GCP based transformer ... forward is to georef coordinates */ +void CPL_DLL * +GDALCreateGCPTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int nReqOrder, int bReversed ); + +/* GCP based transformer with refinement of the GCPs ... forward is to georef coordinates */ +void CPL_DLL * +GDALCreateGCPRefineTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int nReqOrder, int bReversed, double tolerance, int minimumGcps); + +void CPL_DLL GDALDestroyGCPTransformer( void *pTransformArg ); +int CPL_DLL GDALGCPTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +/* Thin Plate Spine transformer ... forward is to georef coordinates */ + +void CPL_DLL * +GDALCreateTPSTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int bReversed ); +void CPL_DLL GDALDestroyTPSTransformer( void *pTransformArg ); +int CPL_DLL GDALTPSTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +char CPL_DLL ** RPCInfoToMD( GDALRPCInfo *psRPCInfo ); + +/* RPC based transformer ... src is pixel/line/elev, dst is long/lat/elev */ + +void CPL_DLL * +GDALCreateRPCTransformer( GDALRPCInfo *psRPC, int bReversed, + double dfPixErrThreshold, + char **papszOptions ); +void CPL_DLL GDALDestroyRPCTransformer( void *pTransformArg ); +int CPL_DLL GDALRPCTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +/* Geolocation transformer */ + +void CPL_DLL * +GDALCreateGeoLocTransformer( GDALDatasetH hBaseDS, + char **papszGeolocationInfo, + int bReversed ); +void CPL_DLL GDALDestroyGeoLocTransformer( void *pTransformArg ); +int CPL_DLL GDALGeoLocTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + +/* Approximate transformer */ +void CPL_DLL * +GDALCreateApproxTransformer( GDALTransformerFunc pfnRawTransformer, + void *pRawTransformerArg, double dfMaxError ); +void CPL_DLL GDALApproxTransformerOwnsSubtransformer( void *pCBData, + int bOwnFlag ); +void CPL_DLL GDALDestroyApproxTransformer( void *pApproxArg ); +int CPL_DLL GDALApproxTransform( + void *pTransformArg, int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); + + + + +int CPL_DLL CPL_STDCALL +GDALSimpleImageWarp( GDALDatasetH hSrcDS, + GDALDatasetH hDstDS, + int nBandCount, int *panBandList, + GDALTransformerFunc pfnTransform, + void *pTransformArg, + GDALProgressFunc pfnProgress, + void *pProgressArg, + char **papszWarpOptions ); + +CPLErr CPL_DLL CPL_STDCALL +GDALSuggestedWarpOutput( GDALDatasetH hSrcDS, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfGeoTransformOut, + int *pnPixels, int *pnLines ); +CPLErr CPL_DLL CPL_STDCALL +GDALSuggestedWarpOutput2( GDALDatasetH hSrcDS, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfGeoTransformOut, + int *pnPixels, int *pnLines, + double *padfExtents, + int nOptions ); + +CPLXMLNode CPL_DLL * +GDALSerializeTransformer( GDALTransformerFunc pfnFunc, void *pTransformArg ); +CPLErr CPL_DLL GDALDeserializeTransformer( CPLXMLNode *psTree, + GDALTransformerFunc *ppfnFunc, + void **ppTransformArg ); + +CPLErr CPL_DLL +GDALTransformGeolocations( GDALRasterBandH hXBand, + GDALRasterBandH hYBand, + GDALRasterBandH hZBand, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + GDALProgressFunc pfnProgress, + void *pProgressArg, + char **papszOptions ); + +/* -------------------------------------------------------------------- */ +/* Contour Line Generation */ +/* -------------------------------------------------------------------- */ + +typedef CPLErr (*GDALContourWriter)( double dfLevel, int nPoints, + double *padfX, double *padfY, void * ); + +typedef void *GDALContourGeneratorH; + +GDALContourGeneratorH CPL_DLL +GDAL_CG_Create( int nWidth, int nHeight, + int bNoDataSet, double dfNoDataValue, + double dfContourInterval, double dfContourBase, + GDALContourWriter pfnWriter, void *pCBData ); +CPLErr CPL_DLL GDAL_CG_FeedLine( GDALContourGeneratorH hCG, + double *padfScanline ); +void CPL_DLL GDAL_CG_Destroy( GDALContourGeneratorH hCG ); + +typedef struct +{ + void *hLayer; + + double adfGeoTransform[6]; + + int nElevField; + int nIDField; + int nNextID; +} OGRContourWriterInfo; + +CPLErr CPL_DLL +OGRContourWriter( double, int, double *, double *, void *pInfo ); + +CPLErr CPL_DLL +GDALContourGenerate( GDALRasterBandH hBand, + double dfContourInterval, double dfContourBase, + int nFixedLevelCount, double *padfFixedLevels, + int bUseNoData, double dfNoDataValue, + void *hLayer, int iIDField, int iElevField, + GDALProgressFunc pfnProgress, void *pProgressArg ); + +/************************************************************************/ +/* Rasterizer API - geometries burned into GDAL raster. */ +/************************************************************************/ + +CPLErr CPL_DLL +GDALRasterizeGeometries( GDALDatasetH hDS, + int nBandCount, int *panBandList, + int nGeomCount, OGRGeometryH *pahGeometries, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfGeomBurnValue, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); +CPLErr CPL_DLL +GDALRasterizeLayers( GDALDatasetH hDS, + int nBandCount, int *panBandList, + int nLayerCount, OGRLayerH *pahLayers, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfLayerBurnValues, + char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressArg ); + +CPLErr CPL_DLL +GDALRasterizeLayersBuf( void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, int nPixelSpace, int nLineSpace, + int nLayerCount, OGRLayerH *pahLayers, + const char *pszDstProjection, + double *padfDstGeoTransform, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, double dfBurnValue, + char **papszOptions, GDALProgressFunc pfnProgress, + void *pProgressArg ); + + +/************************************************************************/ +/* Gridding interface. */ +/************************************************************************/ + +/** Gridding Algorithms */ +typedef enum { + /*! Inverse distance to a power */ GGA_InverseDistanceToAPower = 1, + /*! Moving Average */ GGA_MovingAverage = 2, + /*! Nearest Neighbor */ GGA_NearestNeighbor = 3, + /*! Minimum Value (Data Metric) */ GGA_MetricMinimum = 4, + /*! Maximum Value (Data Metric) */ GGA_MetricMaximum = 5, + /*! Data Range (Data Metric) */ GGA_MetricRange = 6, + /*! Number of Points (Data Metric) */ GGA_MetricCount = 7, + /*! Average Distance (Data Metric) */ GGA_MetricAverageDistance = 8, + /*! Average Distance Between Data Points (Data Metric) */ + GGA_MetricAverageDistancePts = 9 +} GDALGridAlgorithm; + +/** Inverse distance to a power method control options */ +typedef struct +{ + /*! Weighting power. */ + double dfPower; + /*! Smoothing parameter. */ + double dfSmoothing; + /*! Reserved for future use. */ + double dfAnisotropyRatio; + /*! Reserved for future use. */ + double dfAnisotropyAngle; + /*! The first radius (X axis if rotation angle is 0) of search ellipse. */ + double dfRadius1; + /*! The second radius (Y axis if rotation angle is 0) of search ellipse. */ + double dfRadius2; + /*! Angle of ellipse rotation in degrees. + * + * Ellipse rotated counter clockwise. + */ + double dfAngle; + /*! Maximum number of data points to use. + * + * Do not search for more points than this number. + * If less amount of points found the grid node considered empty and will + * be filled with NODATA marker. + */ + GUInt32 nMaxPoints; + /*! Minimum number of data points to use. + * + * If less amount of points found the grid node considered empty and will + * be filled with NODATA marker. + */ + GUInt32 nMinPoints; + /*! No data marker to fill empty points. */ + double dfNoDataValue; +} GDALGridInverseDistanceToAPowerOptions; + +/** Moving average method control options */ +typedef struct +{ + /*! The first radius (X axis if rotation angle is 0) of search ellipse. */ + double dfRadius1; + /*! The second radius (Y axis if rotation angle is 0) of search ellipse. */ + double dfRadius2; + /*! Angle of ellipse rotation in degrees. + * + * Ellipse rotated counter clockwise. + */ + double dfAngle; + /*! Minimum number of data points to average. + * + * If less amount of points found the grid node considered empty and will + * be filled with NODATA marker. + */ + GUInt32 nMinPoints; + /*! No data marker to fill empty points. */ + double dfNoDataValue; +} GDALGridMovingAverageOptions; + +/** Nearest neighbor method control options */ +typedef struct +{ + /*! The first radius (X axis if rotation angle is 0) of search ellipse. */ + double dfRadius1; + /*! The second radius (Y axis if rotation angle is 0) of search ellipse. */ + double dfRadius2; + /*! Angle of ellipse rotation in degrees. + * + * Ellipse rotated counter clockwise. + */ + double dfAngle; + /*! No data marker to fill empty points. */ + double dfNoDataValue; +} GDALGridNearestNeighborOptions; + +/** Data metrics method control options */ +typedef struct +{ + /*! The first radius (X axis if rotation angle is 0) of search ellipse. */ + double dfRadius1; + /*! The second radius (Y axis if rotation angle is 0) of search ellipse. */ + double dfRadius2; + /*! Angle of ellipse rotation in degrees. + * + * Ellipse rotated counter clockwise. + */ + double dfAngle; + /*! Minimum number of data points to average. + * + * If less amount of points found the grid node considered empty and will + * be filled with NODATA marker. + */ + GUInt32 nMinPoints; + /*! No data marker to fill empty points. */ + double dfNoDataValue; +} GDALGridDataMetricsOptions; + +CPLErr CPL_DLL +GDALGridCreate( GDALGridAlgorithm, const void *, GUInt32, + const double *, const double *, const double *, + double, double, double, double, + GUInt32, GUInt32, GDALDataType, void *, + GDALProgressFunc, void *); + +GDAL_GCP CPL_DLL * +GDALComputeMatchingPoints( GDALDatasetH hFirstImage, + GDALDatasetH hSecondImage, + char **papszOptions, + int *pnGCPCount ); +CPL_C_END + +#endif /* ndef GDAL_ALG_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/alg/gdal_alg_priv.h b/bazaar/plugin/gdal/alg/gdal_alg_priv.h new file mode 100644 index 000000000..3015ec026 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdal_alg_priv.h @@ -0,0 +1,229 @@ +/****************************************************************************** + * $Id: gdal_alg_priv.h 28826 2015-03-30 17:51:14Z rouault $ + * + * Project: GDAL Image Processing Algorithms + * Purpose: Prototypes and definitions for various GDAL based algorithms: + * private declarations. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2008, Andrey Kiselev + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_ALG_PRIV_H_INCLUDED +#define GDAL_ALG_PRIV_H_INCLUDED + +#include "gdal_alg.h" + +CPL_C_START + +/** Source of the burn value */ +typedef enum { + /*! Use value from padfBurnValue */ GBV_UserBurnValue = 0, + /*! Use value from the Z coordinate */ GBV_Z = 1, + /*! Use value form the M value */ GBV_M = 2 +} GDALBurnValueSrc; + +typedef enum { + GRMA_Replace = 0, + GRMA_Add = 1, +} GDALRasterMergeAlg; + +typedef struct { + unsigned char * pabyChunkBuf; + int nXSize; + int nYSize; + int nBands; + GDALDataType eType; + double *padfBurnValue; + GDALBurnValueSrc eBurnValueSource; + GDALRasterMergeAlg eMergeAlg; +} GDALRasterizeInfo; + +/************************************************************************/ +/* Low level rasterizer API. */ +/************************************************************************/ + +typedef void (*llScanlineFunc)( void *, int, int, int, double ); +typedef void (*llPointFunc)( void *, int, int, double ); + +void GDALdllImagePoint( int nRasterXSize, int nRasterYSize, + int nPartCount, int *panPartSize, + double *padfX, double *padfY, double *padfVariant, + llPointFunc pfnPointFunc, void *pCBData ); + +void GDALdllImageLine( int nRasterXSize, int nRasterYSize, + int nPartCount, int *panPartSize, + double *padfX, double *padfY, double *padfVariant, + llPointFunc pfnPointFunc, void *pCBData ); + +void GDALdllImageLineAllTouched(int nRasterXSize, int nRasterYSize, + int nPartCount, int *panPartSize, + double *padfX, double *padfY, + double *padfVariant, + llPointFunc pfnPointFunc, void *pCBData ); + +void GDALdllImageFilledPolygon(int nRasterXSize, int nRasterYSize, + int nPartCount, int *panPartSize, + double *padfX, double *padfY, + double *padfVariant, + llScanlineFunc pfnScanlineFunc, void *pCBData ); + +CPL_C_END + +/************************************************************************/ +/* Polygon Enumerator */ +/************************************************************************/ + +#define GP_NODATA_MARKER -51502112 + +class GDALRasterPolygonEnumerator + +{ +private: + void MergePolygon( int nSrcId, int nDstId ); + int NewPolygon( GInt32 nValue ); + +public: // these are intended to be readonly. + + GInt32 *panPolyIdMap; + GInt32 *panPolyValue; + + int nNextPolygonId; + int nPolyAlloc; + + int nConnectedness; + +public: + GDALRasterPolygonEnumerator( int nConnectedness=4 ); + ~GDALRasterPolygonEnumerator(); + + void ProcessLine( GInt32 *panLastLineVal, GInt32 *panThisLineVal, + GInt32 *panLastLineId, GInt32 *panThisLineId, + int nXSize ); + + void CompleteMerges(); + + void Clear(); +}; + +#ifdef OGR_ENABLED +/************************************************************************/ +/* Polygon Enumerator */ +/* */ +/* Buffers has float values instead og GInt32 */ +/************************************************************************/ +class GDALRasterFPolygonEnumerator + +{ +private: + void MergePolygon( int nSrcId, int nDstId ); + int NewPolygon( float fValue ); + +public: // these are intended to be readonly. + + GInt32 *panPolyIdMap; + float *pafPolyValue; + + int nNextPolygonId; + int nPolyAlloc; + + int nConnectedness; + +public: + GDALRasterFPolygonEnumerator( int nConnectedness=4 ); + ~GDALRasterFPolygonEnumerator(); + + void ProcessLine( float *pafLastLineVal, float *pafThisLineVal, + GInt32 *panLastLineId, GInt32 *panThisLineId, + int nXSize ); + + void CompleteMerges(); + + void Clear(); +}; +#endif + +typedef void* (*GDALTransformDeserializeFunc)( CPLXMLNode *psTree ); + +void* GDALRegisterTransformDeserializer(const char* pszTransformName, + GDALTransformerFunc pfnTransformerFunc, + GDALTransformDeserializeFunc pfnDeserializeFunc); +void GDALUnregisterTransformDeserializer(void* pData); + +void GDALCleanupTransformDeserializerMutex(); + +/* Transformer cloning */ + +void* GDALCreateTPSTransformerInt( int nGCPCount, const GDAL_GCP *pasGCPList, + int bReversed, char** papszOptions ); + +void CPL_DLL * GDALCloneTransformer( void *pTranformerArg ); + +/************************************************************************/ +/* Color table related */ +/************************************************************************/ + +int +GDALComputeMedianCutPCTInternal( GDALRasterBandH hRed, + GDALRasterBandH hGreen, + GDALRasterBandH hBlue, + GByte* pabyRedBand, + GByte* pabyGreenBand, + GByte* pabyBlueBand, + int (*pfnIncludePixel)(int,int,void*), + int nColors, + int nBits, + int* panHistogram, + GDALColorTableH hColorTable, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +int GDALDitherRGB2PCTInternal( GDALRasterBandH hRed, + GDALRasterBandH hGreen, + GDALRasterBandH hBlue, + GDALRasterBandH hTarget, + GDALColorTableH hColorTable, + int nBits, + GInt16* pasDynamicColorMap, + int bDither, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + +#define PRIME_FOR_65536 98317 +#define MEDIAN_CUT_AND_DITHER_BUFFER_SIZE_65536 (6 * sizeof(int) * PRIME_FOR_65536) + +/************************************************************************/ +/* Float comparison function. */ +/************************************************************************/ + +/** + * Units in the Last Place. This specifies how big an error we are willing to + * accept in terms of the value of the least significant digit of the floating + * point number’s representation. MAX_ULPS can also be interpreted in terms of + * how many representable floats we are willing to accept between A and B. + */ +#define MAX_ULPS 10 + +GBool GDALFloatEquals(float A, float B); + +#endif /* ndef GDAL_ALG_PRIV_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/alg/gdal_crs.c b/bazaar/plugin/gdal/alg/gdal_crs.c new file mode 100644 index 000000000..8f0e74c32 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdal_crs.c @@ -0,0 +1,1166 @@ +/****************************************************************************** + * $Id: gdal_crs.c 29207 2015-05-18 17:23:45Z mloskot $ + * + * Project: Mapinfo Image Warper + * Purpose: Implemention of the GDALTransformer wrapper around CRS.C functions + * to build a polynomial transformation based on ground control + * points. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + *************************************************************************** + + CRS.C - Center for Remote Sensing rectification routines + + Written By: Brian J. Buckley + + At: The Center for Remote Sensing + Michigan State University + 302 Berkey Hall + East Lansing, MI 48824 + (517)353-7195 + + Written: 12/19/91 + + Last Update: 12/26/91 Brian J. Buckley + Last Update: 1/24/92 Brian J. Buckley + Added printout of trnfile. Triggered by BDEBUG. + Last Update: 1/27/92 Brian J. Buckley + Fixed bug so that only the active control points were used. + Last Update: 6/29/2011 C. F. Stallmann & R. van den Dool (South African National Space Agency) + GCP refinement added + + + Copyright (c) 1992, Michigan State University + * Copyright (c) 2008-2013, Even Rouault + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + ****************************************************************************/ + +#include "gdal_alg.h" +#include "cpl_conv.h" +#include "cpl_minixml.h" +#include "cpl_string.h" +#include "cpl_atomic_ops.h" + +CPL_CVSID("$Id: gdal_crs.c 29207 2015-05-18 17:23:45Z mloskot $"); + +/* Hum, we cannot include gdal_priv.h from a .c file... */ +CPL_C_START + +void GDALSerializeGCPListToXML( CPLXMLNode* psParentNode, + GDAL_GCP* pasGCPList, + int nGCPCount, + const char* pszGCPProjection ); +void GDALDeserializeGCPListFromXML( CPLXMLNode* psGCPList, + GDAL_GCP** ppasGCPList, + int* pnGCPCount, + char** ppszGCPProjection ); + +CPL_C_END + +#define MAXORDER 3 + +struct Control_Points +{ + int count; + double *e1; + double *n1; + double *e2; + double *n2; + int *status; +}; + +typedef struct +{ + GDALTransformerInfo sTI; + + double adfToGeoX[20]; + double adfToGeoY[20]; + + double adfFromGeoX[20]; + double adfFromGeoY[20]; + + int nOrder; + int bReversed; + + int nGCPCount; + GDAL_GCP *pasGCPList; + int bRefine; + int nMinimumGcps; + double dfTolerance; + + volatile int nRefCount; + +} GCPTransformInfo; + +CPL_C_START +CPLXMLNode *GDALSerializeGCPTransformer( void *pTransformArg ); +void *GDALDeserializeGCPTransformer( CPLXMLNode *psTree ); +CPL_C_END + +/* crs.c */ +static int CRS_georef(double, double, double *, double *, + double [], double [], int); +static int CRS_compute_georef_equations(struct Control_Points *, + double [], double [], double [], double [], int); +static int remove_outliers(GCPTransformInfo *); + +static char *CRS_error_message[] = { + "Failed to compute GCP transform: Not enough points available", + "Failed to compute GCP transform: Transform is not solvable", + "Failed to compute GCP transform: Not enough memory", + "Failed to compute GCP transform: Parameter error", + "Failed to compute GCP transform: Internal error" +}; + +/************************************************************************/ +/* GDALCreateSimilarGCPTransformer() */ +/************************************************************************/ + +static +void* GDALCreateSimilarGCPTransformer( void *hTransformArg, double dfRatioX, double dfRatioY ) +{ + int i; + GDAL_GCP *pasGCPList; + GCPTransformInfo *psInfo = (GCPTransformInfo *) hTransformArg; + + VALIDATE_POINTER1( hTransformArg, "GDALCreateSimilarGCPTransformer", NULL ); + + if( dfRatioX == 1.0 && dfRatioY == 1.0 ) + { + /* We can just use a ref count, since using the source transformation */ + /* is thread-safe */ + CPLAtomicInc(&(psInfo->nRefCount)); + } + else + { + pasGCPList = GDALDuplicateGCPs( psInfo->nGCPCount, psInfo->pasGCPList ); + for(i=0;inGCPCount;i++) + { + pasGCPList[i].dfGCPPixel /= dfRatioX; + pasGCPList[i].dfGCPLine /= dfRatioY; + } + /* As remove_outliers modifies the provided GCPs we don't need to reapply it */ + psInfo = (GCPTransformInfo *) GDALCreateGCPTransformer( + psInfo->nGCPCount, pasGCPList, psInfo->nOrder, psInfo->bReversed ); + GDALDeinitGCPs( psInfo->nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + return psInfo; +} + +/************************************************************************/ +/* GDALCreateGCPTransformer() */ +/************************************************************************/ + +/** + * Create GCP based polynomial transformer. + * + * Computes least squares fit polynomials from a provided set of GCPs, + * and stores the coefficients for later transformation of points between + * pixel/line and georeferenced coordinates. + * + * The return value should be used as a TransformArg in combination with + * the transformation function GDALGCPTransform which fits the + * GDALTransformerFunc signature. The returned transform argument should + * be deallocated with GDALDestroyGCPTransformer when no longer needed. + * + * This function may fail (returning NULL) if the provided set of GCPs + * are inadequate for the requested order, the determinate is zero or they + * are otherwise "ill conditioned". + * + * Note that 2nd order requires at least 6 GCPs, and 3rd order requires at + * least 10 gcps. If nReqOrder is 0 the highest order possible with the + * provided gcp count will be used. + * + * @param nGCPCount the number of GCPs in pasGCPList. + * @param pasGCPList an array of GCPs to be used as input. + * @param nReqOrder the requested polynomial order. It should be 1, 2 or 3. + * + * @return the transform argument or NULL if creation fails. + */ + +void *GDALCreateGCPTransformerEx( int nGCPCount, const GDAL_GCP *pasGCPList, + int nReqOrder, int bReversed, int bRefine, double dfTolerance, int nMinimumGcps) + +{ + GCPTransformInfo *psInfo; + double *padfGeoX, *padfGeoY, *padfRasterX, *padfRasterY; + int *panStatus, iGCP; + int nCRSresult; + struct Control_Points sPoints; + + if( nReqOrder == 0 ) + { + if( nGCPCount >= 10 ) + nReqOrder = 2; /*for now we avoid 3rd order since it is unstable*/ + else if( nGCPCount >= 6 ) + nReqOrder = 2; + else + nReqOrder = 1; + } + + psInfo = (GCPTransformInfo *) CPLCalloc(sizeof(GCPTransformInfo),1); + psInfo->bReversed = bReversed; + psInfo->nOrder = nReqOrder; + psInfo->bRefine = bRefine; + psInfo->dfTolerance = dfTolerance; + psInfo->nMinimumGcps = nMinimumGcps; + + psInfo->nRefCount = 1; + + psInfo->pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPList ); + psInfo->nGCPCount = nGCPCount; + + memcpy( psInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE) ); + psInfo->sTI.pszClassName = "GDALGCPTransformer"; + psInfo->sTI.pfnTransform = GDALGCPTransform; + psInfo->sTI.pfnCleanup = GDALDestroyGCPTransformer; + psInfo->sTI.pfnSerialize = GDALSerializeGCPTransformer; + psInfo->sTI.pfnCreateSimilar = GDALCreateSimilarGCPTransformer; + +/* -------------------------------------------------------------------- */ +/* Compute the forward and reverse polynomials. */ +/* -------------------------------------------------------------------- */ + + if(bRefine) + { + nCRSresult = remove_outliers(psInfo); + } + else + { + /* -------------------------------------------------------------------- */ + /* Allocate and initialize the working points list. */ + /* -------------------------------------------------------------------- */ + padfGeoX = (double *) CPLCalloc(sizeof(double),nGCPCount); + padfGeoY = (double *) CPLCalloc(sizeof(double),nGCPCount); + padfRasterX = (double *) CPLCalloc(sizeof(double),nGCPCount); + padfRasterY = (double *) CPLCalloc(sizeof(double),nGCPCount); + panStatus = (int *) CPLCalloc(sizeof(int),nGCPCount); + + for( iGCP = 0; iGCP < nGCPCount; iGCP++ ) + { + panStatus[iGCP] = 1; + padfGeoX[iGCP] = pasGCPList[iGCP].dfGCPX; + padfGeoY[iGCP] = pasGCPList[iGCP].dfGCPY; + padfRasterX[iGCP] = pasGCPList[iGCP].dfGCPPixel; + padfRasterY[iGCP] = pasGCPList[iGCP].dfGCPLine; + } + + sPoints.count = nGCPCount; + sPoints.e1 = padfRasterX; + sPoints.n1 = padfRasterY; + sPoints.e2 = padfGeoX; + sPoints.n2 = padfGeoY; + sPoints.status = panStatus; + nCRSresult = CRS_compute_georef_equations( &sPoints, + psInfo->adfToGeoX, psInfo->adfToGeoY, + psInfo->adfFromGeoX, psInfo->adfFromGeoY, + nReqOrder ); + CPLFree( padfGeoX ); + CPLFree( padfGeoY ); + CPLFree( padfRasterX ); + CPLFree( padfRasterY ); + CPLFree( panStatus ); + } + + if (nCRSresult != 1) + { + CPLError( CE_Failure, CPLE_AppDefined, "%s", CRS_error_message[-nCRSresult]); + GDALDestroyGCPTransformer( psInfo ); + return NULL; + } + else + { + return psInfo; + } +} + +void *GDALCreateGCPTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int nReqOrder, int bReversed ) + +{ + return GDALCreateGCPTransformerEx(nGCPCount, pasGCPList, nReqOrder, bReversed, FALSE, -1, -1); +} + +void *GDALCreateGCPRefineTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int nReqOrder, int bReversed, double dfTolerance, int nMinimumGcps) + +{ + //If no minimumGcp parameter was passed, we use the default value according to the model + if(nMinimumGcps == -1) + { + nMinimumGcps = ((nReqOrder+1) * (nReqOrder+2)) / 2 + 1; + } + return GDALCreateGCPTransformerEx(nGCPCount, pasGCPList, nReqOrder, bReversed, TRUE, dfTolerance, nMinimumGcps); +} + + +/************************************************************************/ +/* GDALDestroyGCPTransformer() */ +/************************************************************************/ + +/** + * Destroy GCP transformer. + * + * This function is used to destroy information about a GCP based + * polynomial transformation created with GDALCreateGCPTransformer(). + * + * @param pTransformArg the transform arg previously returned by + * GDALCreateGCPTransformer(). + */ + +void GDALDestroyGCPTransformer( void *pTransformArg ) + +{ + GCPTransformInfo *psInfo = NULL; + + if( pTransformArg == NULL ) + return; + + psInfo = (GCPTransformInfo *) pTransformArg; + + if( CPLAtomicDec(&(psInfo->nRefCount)) == 0 ) + { + GDALDeinitGCPs( psInfo->nGCPCount, psInfo->pasGCPList ); + CPLFree( psInfo->pasGCPList ); + + CPLFree( pTransformArg ); + } +} + +/************************************************************************/ +/* GDALGCPTransform() */ +/************************************************************************/ + +/** + * Transforms point based on GCP derived polynomial model. + * + * This function matches the GDALTransformerFunc signature, and can be + * used to transform one or more points from pixel/line coordinates to + * georeferenced coordinates (SrcToDst) or vice versa (DstToSrc). + * + * @param pTransformArg return value from GDALCreateGCPTransformer(). + * @param bDstToSrc TRUE if transformation is from the destination + * (georeferenced) coordinates to pixel/line or FALSE when transforming + * from pixel/line to georeferenced coordinates. + * @param nPointCount the number of values in the x, y and z arrays. + * @param x array containing the X values to be transformed. + * @param y array containing the Y values to be transformed. + * @param z array containing the Z values to be transformed. + * @param panSuccess array in which a flag indicating success (TRUE) or + * failure (FALSE) of the transformation are placed. + * + * @return TRUE. + */ + +int GDALGCPTransform( void *pTransformArg, int bDstToSrc, + int nPointCount, + double *x, double *y, CPL_UNUSED double *z, + int *panSuccess ) + +{ + int i; + GCPTransformInfo *psInfo = (GCPTransformInfo *) pTransformArg; + + if( psInfo->bReversed ) + bDstToSrc = !bDstToSrc; + + for( i = 0; i < nPointCount; i++ ) + { + if( x[i] == HUGE_VAL || y[i] == HUGE_VAL ) + { + panSuccess[i] = FALSE; + continue; + } + + if( bDstToSrc ) + { + CRS_georef( x[i], y[i], x + i, y + i, + psInfo->adfFromGeoX, psInfo->adfFromGeoY, + psInfo->nOrder ); + } + else + { + CRS_georef( x[i], y[i], x + i, y + i, + psInfo->adfToGeoX, psInfo->adfToGeoY, + psInfo->nOrder ); + } + panSuccess[i] = TRUE; + } + + return TRUE; +} + +/************************************************************************/ +/* GDALSerializeGCPTransformer() */ +/************************************************************************/ + +CPLXMLNode *GDALSerializeGCPTransformer( void *pTransformArg ) + +{ + CPLXMLNode *psTree; + GCPTransformInfo *psInfo = (GCPTransformInfo *) pTransformArg; + + VALIDATE_POINTER1( pTransformArg, "GDALSerializeGCPTransformer", NULL ); + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "GCPTransformer" ); + +/* -------------------------------------------------------------------- */ +/* Serialize Order and bReversed. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( + psTree, "Order", + CPLSPrintf( "%d", psInfo->nOrder ) ); + + CPLCreateXMLElementAndValue( + psTree, "Reversed", + CPLSPrintf( "%d", psInfo->bReversed ) ); + + if( psInfo->bRefine ) + { + CPLCreateXMLElementAndValue( + psTree, "Refine", + CPLSPrintf( "%d", psInfo->bRefine ) ); + + CPLCreateXMLElementAndValue( + psTree, "MinimumGcps", + CPLSPrintf( "%d", psInfo->nMinimumGcps ) ); + + CPLCreateXMLElementAndValue( + psTree, "Tolerance", + CPLSPrintf( "%f", psInfo->dfTolerance ) ); + } + +/* -------------------------------------------------------------------- */ +/* Attach GCP List. */ +/* -------------------------------------------------------------------- */ + if( psInfo->nGCPCount > 0 ) + { + if(psInfo->bRefine) + { + remove_outliers(psInfo); + } + + GDALSerializeGCPListToXML( psTree, + psInfo->pasGCPList, + psInfo->nGCPCount, + NULL ); + } + + return psTree; +} + +/************************************************************************/ +/* GDALDeserializeReprojectionTransformer() */ +/************************************************************************/ + +void *GDALDeserializeGCPTransformer( CPLXMLNode *psTree ) + +{ + GDAL_GCP *pasGCPList = 0; + int nGCPCount = 0; + void *pResult; + int nReqOrder; + int bReversed; + int bRefine; + int nMinimumGcps; + double dfTolerance; + + /* -------------------------------------------------------------------- */ + /* Check for GCPs. */ + /* -------------------------------------------------------------------- */ + CPLXMLNode *psGCPList = CPLGetXMLNode( psTree, "GCPList" ); + + if( psGCPList != NULL ) + { + GDALDeserializeGCPListFromXML( psGCPList, + &pasGCPList, + &nGCPCount, + NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Get other flags. */ +/* -------------------------------------------------------------------- */ + nReqOrder = atoi(CPLGetXMLValue(psTree,"Order","3")); + bReversed = atoi(CPLGetXMLValue(psTree,"Reversed","0")); + bRefine = atoi(CPLGetXMLValue(psTree,"Refine","0")); + nMinimumGcps = atoi(CPLGetXMLValue(psTree,"MinimumGcps","6")); + dfTolerance = CPLAtof(CPLGetXMLValue(psTree,"Tolerance","1.0")); + +/* -------------------------------------------------------------------- */ +/* Generate transformation. */ +/* -------------------------------------------------------------------- */ + if(bRefine) + { + pResult = GDALCreateGCPRefineTransformer( nGCPCount, pasGCPList, nReqOrder, + bReversed, dfTolerance, nMinimumGcps ); + } + else + { + pResult = GDALCreateGCPTransformer( nGCPCount, pasGCPList, nReqOrder, + bReversed ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup GCP copy. */ +/* -------------------------------------------------------------------- */ + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + + return pResult; +} + +/************************************************************************/ +/* ==================================================================== */ +/* Everything below this point derived from the CRS.C from GRASS. */ +/* ==================================================================== */ +/************************************************************************/ + + +/* STRUCTURE FOR USE INTERNALLY WITH THESE FUNCTIONS. THESE FUNCTIONS EXPECT + SQUARE MATRICES SO ONLY ONE VARIABLE IS GIVEN (N) FOR THE MATRIX SIZE */ + +struct MATRIX +{ + int n; /* SIZE OF THIS MATRIX (N x N) */ + double *v; +}; + +/* CALCULATE OFFSET INTO ARRAY BASED ON R/C */ + +#define M(row,col) m->v[(((row)-1)*(m->n))+(col)-1] + + +#define MSUCCESS 1 /* SUCCESS */ +#define MNPTERR 0 /* NOT ENOUGH POINTS */ +#define MUNSOLVABLE -1 /* NOT SOLVABLE */ +#define MMEMERR -2 /* NOT ENOUGH MEMORY */ +#define MPARMERR -3 /* PARAMETER ERROR */ +#define MINTERR -4 /* INTERNAL ERROR */ + +/***************************************************************************/ +/* + FUNCTION PROTOTYPES FOR STATIC (INTERNAL) FUNCTIONS +*/ +/***************************************************************************/ + +static int calccoef(struct Control_Points *,double *,double *,int); +static int calcls(struct Control_Points *,struct MATRIX *, + double *,double *,double *,double *); +static int exactdet(struct Control_Points *,struct MATRIX *, + double *,double *,double *,double *); +static int solvemat(struct MATRIX *,double *,double *,double *,double *); +static double term(int,double,double); + +/***************************************************************************/ +/* + TRANSFORM A SINGLE COORDINATE PAIR. +*/ +/***************************************************************************/ + +static int +CRS_georef ( + double e1, /* EASTINGS TO BE TRANSFORMED */ + double n1, /* NORTHINGS TO BE TRANSFORMED */ + double *e, /* EASTINGS TO BE TRANSFORMED */ + double *n, /* NORTHINGS TO BE TRANSFORMED */ + double E[], /* EASTING COEFFICIENTS */ + double N[], /* NORTHING COEFFICIENTS */ + int order /* ORDER OF TRANSFORMATION TO BE PERFORMED, MUST MATCH THE + ORDER USED TO CALCULATE THE COEFFICIENTS */ +) + { + double e3, e2n, en2, n3, e2, en, n2; + + switch(order) + { + case 1: + + *e = E[0] + E[1] * e1 + E[2] * n1; + *n = N[0] + N[1] * e1 + N[2] * n1; + break; + + case 2: + + e2 = e1 * e1; + n2 = n1 * n1; + en = e1 * n1; + + *e = E[0] + E[1] * e1 + E[2] * n1 + + E[3] * e2 + E[4] * en + E[5] * n2; + *n = N[0] + N[1] * e1 + N[2] * n1 + + N[3] * e2 + N[4] * en + N[5] * n2; + break; + + case 3: + + e2 = e1 * e1; + en = e1 * n1; + n2 = n1 * n1; + e3 = e1 * e2; + e2n = e2 * n1; + en2 = e1 * n2; + n3 = n1 * n2; + + *e = E[0] + + E[1] * e1 + E[2] * n1 + + E[3] * e2 + E[4] * en + E[5] * n2 + + E[6] * e3 + E[7] * e2n + E[8] * en2 + E[9] * n3; + *n = N[0] + + N[1] * e1 + N[2] * n1 + + N[3] * e2 + N[4] * en + N[5] * n2 + + N[6] * e3 + N[7] * e2n + N[8] * en2 + N[9] * n3; + break; + + default: + + return(MPARMERR); + } + + return(MSUCCESS); + } + +/***************************************************************************/ +/* + COMPUTE THE GEOREFFERENCING COEFFICIENTS BASED ON A SET OF CONTROL POINTS +*/ +/***************************************************************************/ + +static int +CRS_compute_georef_equations (struct Control_Points *cp, + double E12[], double N12[], + double E21[], double N21[], + int order) +{ + double *tempptr; + int status; + + if(order < 1 || order > MAXORDER) + return(MPARMERR); + + /* CALCULATE THE FORWARD TRANSFORMATION COEFFICIENTS */ + + status = calccoef(cp,E12,N12,order); + if(status != MSUCCESS) + return(status); + + /* SWITCH THE 1 AND 2 EASTING AND NORTHING ARRAYS */ + + tempptr = cp->e1; + cp->e1 = cp->e2; + cp->e2 = tempptr; + tempptr = cp->n1; + cp->n1 = cp->n2; + cp->n2 = tempptr; + + /* CALCULATE THE BACKWARD TRANSFORMATION COEFFICIENTS */ + + status = calccoef(cp,E21,N21,order); + + /* SWITCH THE 1 AND 2 EASTING AND NORTHING ARRAYS BACK */ + + tempptr = cp->e1; + cp->e1 = cp->e2; + cp->e2 = tempptr; + tempptr = cp->n1; + cp->n1 = cp->n2; + cp->n2 = tempptr; + + return(status); +} + +/***************************************************************************/ +/* + COMPUTE THE GEOREFFERENCING COEFFICIENTS BASED ON A SET OF CONTROL POINTS +*/ +/***************************************************************************/ + +static int +calccoef (struct Control_Points *cp, double E[], double N[], int order) +{ + struct MATRIX m; + double *a; + double *b; + int numactive; /* NUMBER OF ACTIVE CONTROL POINTS */ + int status, i; + + /* CALCULATE THE NUMBER OF VALID CONTROL POINTS */ + + for(i = numactive = 0 ; i < cp->count ; i++) + { + if(cp->status[i] > 0) + numactive++; + } + + /* CALCULATE THE MINIMUM NUMBER OF CONTROL POINTS NEEDED TO DETERMINE + A TRANSFORMATION OF THIS ORDER */ + + m.n = ((order + 1) * (order + 2)) / 2; + + if(numactive < m.n) + return(MNPTERR); + + /* INITIALIZE MATRIX */ + + m.v = (double *)CPLCalloc(m.n*m.n,sizeof(double)); + if(m.v == NULL) + { + return(MMEMERR); + } + a = (double *)CPLCalloc(m.n,sizeof(double)); + if(a == NULL) + { + CPLFree((char *)m.v); + return(MMEMERR); + } + b = (double *)CPLCalloc(m.n,sizeof(double)); + if(b == NULL) + { + CPLFree((char *)m.v); + CPLFree((char *)a); + return(MMEMERR); + } + + if(numactive == m.n) + status = exactdet(cp,&m,a,b,E,N); + else + status = calcls(cp,&m,a,b,E,N); + + CPLFree((char *)m.v); + CPLFree((char *)a); + CPLFree((char *)b); + + return(status); +} + +/***************************************************************************/ +/* + CALCULATE THE TRANSFORMATION COEFFICIENTS WITH EXACTLY THE MINIMUM + NUMBER OF CONTROL POINTS REQUIRED FOR THIS TRANSFORMATION. +*/ +/***************************************************************************/ + +static int exactdet ( + struct Control_Points *cp, + struct MATRIX *m, + double a[], + double b[], + double E[], /* EASTING COEFFICIENTS */ + double N[] /* NORTHING COEFFICIENTS */ +) + { + int pntnow, currow, j; + + currow = 1; + for(pntnow = 0 ; pntnow < cp->count ; pntnow++) + { + if(cp->status[pntnow] > 0) + { + /* POPULATE MATRIX M */ + + for(j = 1 ; j <= m->n ; j++) + { + M(currow,j) = term(j,cp->e1[pntnow],cp->n1[pntnow]); + } + + /* POPULATE MATRIX A AND B */ + + a[currow-1] = cp->e2[pntnow]; + b[currow-1] = cp->n2[pntnow]; + + currow++; + } + } + + if(currow - 1 != m->n) + return(MINTERR); + + return(solvemat(m,a,b,E,N)); + } + +/***************************************************************************/ +/* + CALCULATE THE TRANSFORMATION COEFFICIENTS WITH MORE THAN THE MINIMUM + NUMBER OF CONTROL POINTS REQUIRED FOR THIS TRANSFORMATION. THIS + ROUTINE USES THE LEAST SQUARES METHOD TO COMPUTE THE COEFFICIENTS. +*/ +/***************************************************************************/ + +static int calcls ( + struct Control_Points *cp, + struct MATRIX *m, + double a[], + double b[], + double E[], /* EASTING COEFFICIENTS */ + double N[] /* NORTHING COEFFICIENTS */ +) +{ + int i, j, n, numactive = 0; + + /* INITIALIZE THE UPPER HALF OF THE MATRIX AND THE TWO COLUMN VECTORS */ + + for(i = 1 ; i <= m->n ; i++) + { + for(j = i ; j <= m->n ; j++) + M(i,j) = 0.0; + a[i-1] = b[i-1] = 0.0; + } + + /* SUM THE UPPER HALF OF THE MATRIX AND THE COLUMN VECTORS ACCORDING TO + THE LEAST SQUARES METHOD OF SOLVING OVER DETERMINED SYSTEMS */ + + for(n = 0 ; n < cp->count ; n++) + { + if(cp->status[n] > 0) + { + numactive++; + for(i = 1 ; i <= m->n ; i++) + { + for(j = i ; j <= m->n ; j++) + M(i,j) += term(i,cp->e1[n],cp->n1[n]) * term(j,cp->e1[n],cp->n1[n]); + + a[i-1] += cp->e2[n] * term(i,cp->e1[n],cp->n1[n]); + b[i-1] += cp->n2[n] * term(i,cp->e1[n],cp->n1[n]); + } + } + } + + if(numactive <= m->n) + return(MINTERR); + + /* TRANSPOSE VALUES IN UPPER HALF OF M TO OTHER HALF */ + + for(i = 2 ; i <= m->n ; i++) + { + for(j = 1 ; j < i ; j++) + M(i,j) = M(j,i); + } + + return(solvemat(m,a,b,E,N)); +} + +/***************************************************************************/ +/* + CALCULATE THE X/Y TERM BASED ON THE TERM NUMBER + +ORDER\TERM 1 2 3 4 5 6 7 8 9 10 + 1 e0n0 e1n0 e0n1 + 2 e0n0 e1n0 e0n1 e2n0 e1n1 e0n2 + 3 e0n0 e1n0 e0n1 e2n0 e1n1 e0n2 e3n0 e2n1 e1n2 e0n3 +*/ +/***************************************************************************/ + +static double term (int term, double e, double n) +{ + switch(term) + { + case 1: return((double)1.0); + case 2: return((double)e); + case 3: return((double)n); + case 4: return((double)(e*e)); + case 5: return((double)(e*n)); + case 6: return((double)(n*n)); + case 7: return((double)(e*e*e)); + case 8: return((double)(e*e*n)); + case 9: return((double)(e*n*n)); + case 10: return((double)(n*n*n)); + } + return((double)0.0); +} + +/***************************************************************************/ +/* + SOLVE FOR THE 'E' AND 'N' COEFFICIENTS BY USING A SOMEWHAT MODIFIED + GAUSSIAN ELIMINATION METHOD. + + | M11 M12 ... M1n | | E0 | | a0 | + | M21 M22 ... M2n | | E1 | = | a1 | + | . . . . | | . | | . | + | Mn1 Mn2 ... Mnn | | En-1 | | an-1 | + + and + + | M11 M12 ... M1n | | N0 | | b0 | + | M21 M22 ... M2n | | N1 | = | b1 | + | . . . . | | . | | . | + | Mn1 Mn2 ... Mnn | | Nn-1 | | bn-1 | +*/ +/***************************************************************************/ + +static int solvemat (struct MATRIX *m, + double a[], double b[], double E[], double N[]) +{ + int i, j, i2, j2, imark; + double factor, temp; + double pivot; /* ACTUAL VALUE OF THE LARGEST PIVOT CANDIDATE */ + + for(i = 1 ; i <= m->n ; i++) + { + j = i; + + /* find row with largest magnitude value for pivot value */ + + pivot = M(i,j); + imark = i; + for(i2 = i + 1 ; i2 <= m->n ; i2++) + { + temp = fabs(M(i2,j)); + if(temp > fabs(pivot)) + { + pivot = M(i2,j); + imark = i2; + } + } + + /* if the pivot is very small then the points are nearly co-linear */ + /* co-linear points result in an undefined matrix, and nearly */ + /* co-linear points results in a solution with rounding error */ + + if(pivot == 0.0) + return(MUNSOLVABLE); + + /* if row with highest pivot is not the current row, switch them */ + + if(imark != i) + { + for(j2 = 1 ; j2 <= m->n ; j2++) + { + temp = M(imark,j2); + M(imark,j2) = M(i,j2); + M(i,j2) = temp; + } + + temp = a[imark-1]; + a[imark-1] = a[i-1]; + a[i-1] = temp; + + temp = b[imark-1]; + b[imark-1] = b[i-1]; + b[i-1] = temp; + } + + /* compute zeros above and below the pivot, and compute + values for the rest of the row as well */ + + for(i2 = 1 ; i2 <= m->n ; i2++) + { + if(i2 != i) + { + factor = M(i2,j) / pivot; + for(j2 = j ; j2 <= m->n ; j2++) + M(i2,j2) -= factor * M(i,j2); + a[i2-1] -= factor * a[i-1]; + b[i2-1] -= factor * b[i-1]; + } + } + } + + /* SINCE ALL OTHER VALUES IN THE MATRIX ARE ZERO NOW, CALCULATE THE + COEFFICIENTS BY DIVIDING THE COLUMN VECTORS BY THE DIAGONAL VALUES. */ + + for(i = 1 ; i <= m->n ; i++) + { + E[i-1] = a[i-1] / M(i,i); + N[i-1] = b[i-1] / M(i,i); + } + + return(MSUCCESS); +} + +/***************************************************************************/ +/* + DETECTS THE WORST OUTLIER IN THE GCP LIST AND RETURNS THE INDEX OF THE + OUTLIER. + + THE WORST OUTLIER IS CALCULATED BASED ON THE CONTROL POINTS, COEFFICIENTS + AND THE ALLOWED TOLERANCE: + + sampleAdj = a0 + a1*sample + a2*line + a3*line*sample + lineAdj = b0 + b1*sample + b2*line + b3*line*sample + + WHERE sampleAdj AND lineAdj ARE CORRELATED GCPS + + [residualSample] = [A1][sampleCoefficients] - [b1] + [residualLine] = [A2][lineCoefficients] - [b2] + + sampleResidual^2 = sum( [residualSample]^2 ) + lineResidual^2 = sum( [lineSample]^2 ) + + residuals(i) = squareRoot( residualSample(i)^2 + residualLine(i)^2 ) + + THE GCP WITH THE GREATEST DISTANCE residual(i) GREATER THAN THE TOLERANCE WILL + CONSIDERED THE WORST OUTLIER. + + IF NO OUTLIER CAN BE FOUND, -1 WILL BE RETURNED. +*/ +/***************************************************************************/ +static int worst_outlier(struct Control_Points *cp, double E[], double N[], double dfTolerance) +{ + double *padfResiduals; + int nI, nIndex; + double dfDifference, dfSampleResidual, dfLineResidual, dfSampleRes, dfLineRes, dfCurrentDifference; + double dfE1, dfN1, dfE2, dfN2, dfEn; + + padfResiduals = (double *) CPLCalloc(sizeof(double),cp->count); + dfSampleResidual = 0.0; + dfLineResidual = 0.0; + + for(nI = 0; nI < cp->count; nI++) + { + dfE1 = cp->e1[nI]; + dfN1 = cp->n1[nI]; + dfE2 = dfE1 * dfE1; + dfN2 = dfN1 * dfN1; + dfEn = dfE1 * dfN1; + + dfSampleRes = E[0] + E[1] * dfE1 + E[2] * dfN1 + E[3] * dfE2 + E[4] * dfEn + E[5] * dfN2 - cp->e2[nI]; + dfLineRes = N[0] + N[1] * dfE1 + N[2] * dfN1 + N[3] * dfE2 + N[4] * dfEn + N[5] * dfN2 - cp->n2[nI]; + + dfSampleResidual += dfSampleRes*dfSampleRes; + dfLineResidual += dfLineRes*dfLineRes; + + padfResiduals[nI] = sqrt(dfSampleRes*dfSampleRes + dfLineRes*dfLineRes); + } + + nIndex = -1; + dfDifference = -1.0; + for(nI = 0; nI < cp->count; nI++) + { + dfCurrentDifference = padfResiduals[nI]; + if(fabs(dfCurrentDifference) < 1.19209290E-07F /*FLT_EPSILON*/) + { + dfCurrentDifference = 0.0; + } + if(dfCurrentDifference > dfDifference && dfCurrentDifference >= dfTolerance) + { + dfDifference = dfCurrentDifference; + nIndex = nI; + } + } + CPLFree( padfResiduals ); + return nIndex; +} + +/***************************************************************************/ +/* + REMOVES THE WORST OUTLIERS ITERATIVELY UNTIL THE MINIMUM NUMBER OF GCPS + ARE REACHED OR NO OUTLIERS CAN BE DETECTED. + + 1. WE CALCULATE THE COEFFICIENTS FOR ALL THE GCPS. + 2. THE GCP LIST WILL BE SCANED TO DETERMINE THE WORST OUTLIER USING + THE CALCULATED COEFFICIENTS. + 3. THE WORST OUTLIER WILL BE REMOVED FROM THE GCP LIST. + 4. THE COEFFICIENTS WILL BE RECALCULATED WITHOUT THE WORST OUTLIER. + 5. STEP 1 TO 4 ARE EXECUTED UNTIL THE MINIMUM NUMBER OF GCPS WERE REACHED + OR IF NO GCP IS CONSIDERED AN OUTLIER WITH THE PASSED TOLERANCE. +*/ +/***************************************************************************/ +static int remove_outliers( GCPTransformInfo *psInfo ) +{ + double *padfGeoX, *padfGeoY, *padfRasterX, *padfRasterY; + int *panStatus; + int nI, nCRSresult, nGCPCount, nMinimumGcps, nReqOrder; + double dfTolerance; + struct Control_Points sPoints; + + nGCPCount = psInfo->nGCPCount; + nMinimumGcps = psInfo->nMinimumGcps; + nReqOrder = psInfo->nOrder; + dfTolerance = psInfo->dfTolerance; + + padfGeoX = (double *) CPLCalloc(sizeof(double),nGCPCount); + padfGeoY = (double *) CPLCalloc(sizeof(double),nGCPCount); + padfRasterX = (double *) CPLCalloc(sizeof(double),nGCPCount); + padfRasterY = (double *) CPLCalloc(sizeof(double),nGCPCount); + panStatus = (int *) CPLCalloc(sizeof(int),nGCPCount); + + for( nI = 0; nI < nGCPCount; nI++ ) + { + panStatus[nI] = 1; + padfGeoX[nI] = psInfo->pasGCPList[nI].dfGCPX; + padfGeoY[nI] = psInfo->pasGCPList[nI].dfGCPY; + padfRasterX[nI] = psInfo->pasGCPList[nI].dfGCPPixel; + padfRasterY[nI] = psInfo->pasGCPList[nI].dfGCPLine; + } + + sPoints.count = nGCPCount; + sPoints.e1 = padfRasterX; + sPoints.n1 = padfRasterY; + sPoints.e2 = padfGeoX; + sPoints.n2 = padfGeoY; + sPoints.status = panStatus; + + nCRSresult = CRS_compute_georef_equations( &sPoints, + psInfo->adfToGeoX, psInfo->adfToGeoY, + psInfo->adfFromGeoX, psInfo->adfFromGeoY, + nReqOrder ); + + while(sPoints.count > nMinimumGcps) + { + int nIndex; + + nIndex = worst_outlier(&sPoints, psInfo->adfFromGeoX, psInfo->adfFromGeoY, dfTolerance); + + //If no outliers were detected, stop the GCP elimination + if(nIndex == -1) + { + break; + } + + CPLFree(psInfo->pasGCPList[nIndex].pszId); + CPLFree(psInfo->pasGCPList[nIndex].pszInfo); + + for( nI = nIndex; nI < sPoints.count - 1; nI++ ) + { + sPoints.e1[nI] = sPoints.e1[nI + 1]; + sPoints.n1[nI] = sPoints.n1[nI + 1]; + sPoints.e2[nI] = sPoints.e2[nI + 1]; + sPoints.n2[nI] = sPoints.n2[nI + 1]; + psInfo->pasGCPList[nI].pszId = psInfo->pasGCPList[nI + 1].pszId; + psInfo->pasGCPList[nI].pszInfo = psInfo->pasGCPList[nI + 1].pszInfo; + } + + sPoints.count = sPoints.count - 1; + + nCRSresult = CRS_compute_georef_equations( &sPoints, + psInfo->adfToGeoX, psInfo->adfToGeoY, + psInfo->adfFromGeoX, psInfo->adfFromGeoY, + nReqOrder ); + } + + for( nI = 0; nI < sPoints.count; nI++ ) + { + psInfo->pasGCPList[nI].dfGCPX = sPoints.e2[nI]; + psInfo->pasGCPList[nI].dfGCPY = sPoints.n2[nI]; + psInfo->pasGCPList[nI].dfGCPPixel = sPoints.e1[nI]; + psInfo->pasGCPList[nI].dfGCPLine = sPoints.n1[nI]; + } + psInfo->nGCPCount = sPoints.count; + + CPLFree( sPoints.e1 ); + CPLFree( sPoints.n1 ); + CPLFree( sPoints.e2 ); + CPLFree( sPoints.n2 ); + CPLFree( sPoints.status ); + return nCRSresult; +} diff --git a/bazaar/plugin/gdal/alg/gdal_nrgcrs.c b/bazaar/plugin/gdal/alg/gdal_nrgcrs.c new file mode 100644 index 000000000..c5c759edb --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdal_nrgcrs.c @@ -0,0 +1,287 @@ +/****************************************************************************** + * $Id: gdal_nrgcrs.c 23156 2011-10-01 15:34:16Z rouault $ + * + * Project: Mapinfo Image Warper + * Purpose: Implemention of the GDALTransformer wrapper around CRS.C functions + * to build a polynomial transformation based on ground control + * points. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + *************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_alg.h" +#include "cpl_conv.h" +#include "cpl_minixml.h" + +extern int TwoDPolyFit( double *, double *, int, int, double *, double *, double * ); +extern double TwoDPolyEval( double *, int, double, double ); + +typedef struct +{ + double adfToGeoX[20]; + double adfToGeoY[20]; + + double adfFromGeoX[20]; + double adfFromGeoY[20]; + + int nOrder; + int bReversed; + +} GCPTransformInfo; + + +/************************************************************************/ +/* GDALCreateGCPTransformer() */ +/************************************************************************/ + +/** + * Create GCP based polynomial transformer. + * + * Computes least squares fit polynomials from a provided set of GCPs, + * and stores the coefficients for later transformation of points between + * pixel/line and georeferenced coordinates. + * + * The return value should be used as a TransformArg in combination with + * the transformation function GDALGCPTransform which fits the + * GDALTransformerFunc signature. The returned transform argument should + * be deallocated with GDALDestroyGCPTransformer when no longer needed. + * + * This function may fail (returning NULL) if the provided set of GCPs + * are inadequate for the requested order, the determinate is zero or they + * are otherwise "ill conditioned". + * + * Note that 2nd order requires at least 6 GCPs, and 3rd order requires at + * least 10 gcps. If nReqOrder is 0 the highest order possible with the + * provided gcp count will be used. + * + * @param nGCPCount the number of GCPs in pasGCPList. + * @param pasGCPList an array of GCPs to be used as input. + * @param nReqOrder the requested polynomial order. It should be 1, 2 or 3. + * @param bReversed set it to TRUE to compute the reversed transformation. + * + * @return the transform argument or NULL if creation fails. + */ + +void *GDALCreateGCPTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int nReqOrder, int bReversed ) + +{ + GCPTransformInfo *psInfo; + double *padfGeoX, *padfGeoY, *padfRasterX, *padfRasterY; + int *panStatus, iGCP; + double rms_err; + + if( nReqOrder == 0 ) + { + if( nGCPCount >= 10 ) + nReqOrder = 3; + else if( nGCPCount >= 6 ) + nReqOrder = 2; + else + nReqOrder = 1; + } + + psInfo = (GCPTransformInfo *) CPLCalloc(sizeof(GCPTransformInfo),1); + psInfo->bReversed = bReversed; + psInfo->nOrder = nReqOrder; + +/* -------------------------------------------------------------------- */ +/* Allocate and initialize the working points list. */ +/* -------------------------------------------------------------------- */ + padfGeoX = (double *) CPLCalloc(sizeof(double),nGCPCount); + padfGeoY = (double *) CPLCalloc(sizeof(double),nGCPCount); + padfRasterX = (double *) CPLCalloc(sizeof(double),nGCPCount); + padfRasterY = (double *) CPLCalloc(sizeof(double),nGCPCount); + panStatus = (int *) CPLCalloc(sizeof(int),nGCPCount); + + for( iGCP = 0; iGCP < nGCPCount; iGCP++ ) + { + panStatus[iGCP] = 1; + padfGeoX[iGCP] = pasGCPList[iGCP].dfGCPX; + padfGeoY[iGCP] = pasGCPList[iGCP].dfGCPY; + padfRasterX[iGCP] = pasGCPList[iGCP].dfGCPPixel; + padfRasterY[iGCP] = pasGCPList[iGCP].dfGCPLine; + } + +/* -------------------------------------------------------------------- */ +/* Compute the forward and reverse polynomials. */ +/* -------------------------------------------------------------------- */ + if ( TwoDPolyFit( &rms_err, psInfo->adfFromGeoX, nReqOrder, nGCPCount, + padfRasterX, padfGeoX, padfGeoY ) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to compute polynomial equations of desired order\n" + "for provided control points." ); + goto CleanupAfterError; + } + if ( TwoDPolyFit( &rms_err, psInfo->adfFromGeoY, nReqOrder, nGCPCount, + padfRasterY, padfGeoX, padfGeoY ) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to compute polynomial equations of desired order\n" + "for provided control points." ); + goto CleanupAfterError; + } + if ( TwoDPolyFit( &rms_err, psInfo->adfToGeoX, nReqOrder, nGCPCount, + padfGeoX, padfRasterX, padfRasterY ) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to compute polynomial equations of desired order\n" + "for provided control points." ); + goto CleanupAfterError; + } + if ( TwoDPolyFit( &rms_err, psInfo->adfToGeoY, nReqOrder, nGCPCount, + padfGeoY, padfRasterX, padfRasterY ) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to compute polynomial equations of desired order\n" + "for provided control points." ); + goto CleanupAfterError; + } + +/* -------------------------------------------------------------------- */ +/* Dump residuals. */ +/* -------------------------------------------------------------------- */ + CPLDebug( "GDALCreateGCPTransformer", + "Number of GCPs %d, transformation order %d", + nGCPCount, psInfo->nOrder ); + + for( iGCP = 0; iGCP < nGCPCount; iGCP++ ) + { + double x = pasGCPList[iGCP].dfGCPX; + double y = pasGCPList[iGCP].dfGCPY; + double z = pasGCPList[iGCP].dfGCPZ; + int bSuccess; + GDALGCPTransform( psInfo, TRUE, 1, &x, &y, &z, &bSuccess ); + CPLDebug( "GDALCreateGCPTransformer", + "GCP %d. Residuals: X: %f, Y: %f", iGCP, + pasGCPList[iGCP].dfGCPPixel - x, pasGCPList[iGCP].dfGCPLine - y ); + } + + return psInfo; + + CleanupAfterError: + CPLFree( padfGeoX ); + CPLFree( padfGeoY ); + CPLFree( padfRasterX ); + CPLFree( padfRasterX ); + CPLFree( panStatus ); + + CPLFree( psInfo ); + return NULL; +} + +/************************************************************************/ +/* GDALDestroyGCPTransformer() */ +/************************************************************************/ + +/** + * Destroy GCP transformer. + * + * This function is used to destroy information about a GCP based + * polynomial transformation created with GDALCreateGCPTransformer(). + * + * @param pTransformArg the transform arg previously returned by + * GDALCreateGCPTransformer(). + */ + +void GDALDestroyGCPTransformer( void *pTransformArg ) + +{ + CPLFree( pTransformArg ); +} + +/************************************************************************/ +/* GDALGCPTransform() */ +/************************************************************************/ + +/** + * Transforms point based on GCP derived polynomial model. + * + * This function matches the GDALTransformerFunc signature, and can be + * used to transform one or more points from pixel/line coordinates to + * georeferenced coordinates (SrcToDst) or vice versa (DstToSrc). + * + * @param pTransformArg return value from GDALCreateGCPTransformer(). + * @param bDstToSrc TRUE if transformation is from the destination + * (georeferenced) coordinates to pixel/line or FALSE when transforming + * from pixel/line to georeferenced coordinates. + * @param nPointCount the number of values in the x, y and z arrays. + * @param x array containing the X values to be transformed. + * @param y array containing the Y values to be transformed. + * @param z array containing the Z values to be transformed. + * @param panSuccess array in which a flag indicating success (TRUE) or + * failure (FALSE) of the transformation are placed. + * + * @return TRUE. + */ + +int GDALGCPTransform( void *pTransformArg, int bDstToSrc, + int nPointCount, + double *x, double *y, double *z, + int *panSuccess ) + +{ + int i; + double X, Y; + GCPTransformInfo *psInfo = (GCPTransformInfo *) pTransformArg; + + if( psInfo->bReversed ) + bDstToSrc = !bDstToSrc; + + for( i = 0; i < nPointCount; i++ ) + { + X = x[i]; + Y = y[i]; + if( bDstToSrc ) + { + x[i] = TwoDPolyEval( psInfo->adfFromGeoX, psInfo->nOrder, X, Y ); + y[i] = TwoDPolyEval( psInfo->adfFromGeoY, psInfo->nOrder, X, Y ); + } + else + { + x[i] = TwoDPolyEval( psInfo->adfToGeoX, psInfo->nOrder, X, Y ); + y[i] = TwoDPolyEval( psInfo->adfToGeoY, psInfo->nOrder, X, Y ); + } + z[i] = 0; + panSuccess[i] = TRUE; + } + + return TRUE; +} + +CPLXMLNode *GDALSerializeGCPTransformer( void *pTransformArg ) + +{ + CPLError( CE_Failure, CPLE_AppDefined, + "serialization not supported for this type of gcp transformer."); + return NULL; +} + +void *GDALDeserializeGCPTransformer( CPLXMLNode *psTree ) + +{ + CPLError( CE_Failure, CPLE_AppDefined, + "deserialization not supported for this type of gcp transformer."); + return NULL; +} diff --git a/bazaar/plugin/gdal/alg/gdal_octave.cpp b/bazaar/plugin/gdal/alg/gdal_octave.cpp new file mode 100644 index 000000000..2bbeeb10f --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdal_octave.cpp @@ -0,0 +1,301 @@ +/****************************************************************************** + * Project: GDAL + * Purpose: Correlator + * Author: Andrew Migal, migal.drew@gmail.com + * + ****************************************************************************** + * Copyright (c) 2012, Andrew Migal + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_simplesurf.h" + +CPL_CVSID("$Id"); + +/************************************************************************/ +/* ==================================================================== */ +/* GDALIntegralImage */ +/* ==================================================================== */ +/************************************************************************/ + +GDALIntegralImage::GDALIntegralImage() +{ + pMatrix = 0; + nHeight = 0; + nWidth = 0; +} + +int GDALIntegralImage::GetHeight() { return nHeight; } + +int GDALIntegralImage::GetWidth() { return nWidth; } + +void GDALIntegralImage::Initialize(const double **padfImg, int nHeight, int nWidth) +{ + //Memory allocation + pMatrix = new double*[nHeight]; + for (int i = 0; i < nHeight; i++) + pMatrix[i] = new double[nWidth]; + + this->nHeight = nHeight; + this->nWidth = nWidth; + + //Integral image calculation + for (int i = 0; i < nHeight; i++) + for (int j = 0; j < nWidth; j++) + { + double val = padfImg[i][j]; + double a = 0, b = 0, c = 0; + + if (i - 1 >= 0 && j - 1 >= 0) + a = pMatrix[i - 1][j - 1]; + if (j - 1 >= 0) + b = pMatrix[i][j - 1]; + if (i - 1 >= 0) + c = pMatrix[i - 1][j]; + + //New value based on previous calculations + pMatrix[i][j] = val - a + b + c; + } +} + +/* + * Returns value of specified cell + */ +double GDALIntegralImage::GetValue(int nRow, int nCol) +{ + if ((nRow >= 0 && nRow < nHeight) && (nCol >= 0 && nCol < nWidth)) + return pMatrix[nRow][nCol]; + else + return 0; +} + +double GDALIntegralImage::GetRectangleSum(int nRow, int nCol, int nWidth, int nHeight) +{ + double a = 0, b = 0, c = 0, d = 0; + + //Left top point of rectangle is first + int w = nWidth - 1; + int h = nHeight - 1; + + int row = nRow; + int col = nCol; + + //Left top point + int lt_row = (row <= this->nHeight) ? (row - 1) : -1; + int lt_col = (col <= this->nWidth) ? (col - 1) : -1; + //Right bottom point of the rectangle + int rb_row = (row + h < this->nHeight) ? (row + h) : (this->nHeight - 1); + int rb_col = (col + w < this->nWidth) ? (col + w) : (this->nWidth - 1); + + if (lt_row >= 0 && lt_col >= 0) + a = this->GetValue(lt_row, lt_col); + + if (lt_row >= 0 && rb_col >= 0) + b = this->GetValue(lt_row, rb_col); + + if (rb_row >= 0 && rb_col >= 0) + c = this->GetValue(rb_row, rb_col); + + if (rb_row >= 0 && lt_col >= 0) + d = this->GetValue(rb_row, lt_col); + + double res = a + c - b - d; + + return (res > 0) ? res : 0; +} + +double GDALIntegralImage::HaarWavelet_X(int nRow, int nCol, int nSize) +{ + return GetRectangleSum(nRow, nCol + nSize / 2, nSize / 2, nSize) + - GetRectangleSum(nRow, nCol, nSize / 2, nSize); +} + +double GDALIntegralImage::HaarWavelet_Y(int nRow, int nCol, int nSize) +{ + return GetRectangleSum(nRow + nSize / 2, nCol, nSize, nSize / 2) + - GetRectangleSum(nRow, nCol, nSize, nSize / 2); +} + +GDALIntegralImage::~GDALIntegralImage() +{ + //Clean up memory + for (int i = 0; i < nHeight; i++) + delete[] pMatrix[i]; + + delete[] pMatrix; +} + + + +/************************************************************************/ +/* ==================================================================== */ +/* GDALOctaveLayer */ +/* ==================================================================== */ +/************************************************************************/ + +GDALOctaveLayer::GDALOctaveLayer(int nOctave, int nInterval) +{ + this->octaveNum = nOctave; + this->filterSize = 3 * ((int)pow(2.0, nOctave) * nInterval + 1); + this->radius = (this->filterSize - 1) / 2; + this->scale = (int)pow(2.0, nOctave); + this->width = 0; + this->height = 0; + + this->detHessians = 0; + this->signs = 0; +} + +void GDALOctaveLayer::ComputeLayer(GDALIntegralImage *poImg) +{ + this->width = poImg->GetWidth(); + this->height = poImg->GetHeight(); + + //Allocate memory for arrays + this->detHessians = new double*[this->height]; + this->signs = new int*[this->height]; + + for (int i = 0; i < this->height; i++) + { + this->detHessians[i] = new double[this->width]; + this->signs[i] = new int[this->width]; + } + //End Allocate memory for arrays + + //Values of Fast Hessian filters + double dxx, dyy, dxy; + // 1/3 of filter side + int lobe = filterSize / 3; + + //Length of the longer side of the lobe in dxx and dyy filters + int longPart = 2 * lobe - 1; + + int normalization = filterSize * filterSize; + + //Loop over image pixels + //Filter should remain into image borders + for (int r = radius; r <= height - radius; r++) + for (int c = radius; c <= width - radius; c++) + { + dxx = poImg->GetRectangleSum(r - lobe + 1, c - radius, filterSize, longPart) + - 3 * poImg->GetRectangleSum(r - lobe + 1, c - (lobe - 1) / 2, lobe, longPart); + dyy = poImg->GetRectangleSum(r - radius, c - lobe - 1, longPart, filterSize) + - 3 * poImg->GetRectangleSum(r - lobe + 1, c - lobe + 1, longPart, lobe); + dxy = poImg->GetRectangleSum(r - lobe, c - lobe, lobe, lobe) + + poImg->GetRectangleSum(r + 1, c + 1, lobe, lobe) + - poImg->GetRectangleSum(r - lobe, c + 1, lobe, lobe) + - poImg->GetRectangleSum(r + 1, c - lobe, lobe, lobe); + + dxx /= normalization; + dyy /= normalization; + dxy /= normalization; + + //Memorize Hessian values and their signs + detHessians[r][c] = dxx * dyy - 0.9 * 0.9 * dxy * dxy; + signs[r][c] = (dxx + dyy >= 0) ? 1 : -1; + } +} + +GDALOctaveLayer::~GDALOctaveLayer() +{ + for (int i = 0; i < height; i++) + { + delete[] detHessians[i]; + delete[] signs[i]; + } + + delete[] detHessians; + delete[] signs; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALOctaveMap */ +/* ==================================================================== */ +/************************************************************************/ + +GDALOctaveMap::GDALOctaveMap(int nOctaveStart, int nOctaveEnd) +{ + this->octaveStart = nOctaveStart; + this->octaveEnd = nOctaveEnd; + + pMap = new GDALOctaveLayer**[octaveEnd]; + + for (int i = 0; i < nOctaveEnd; i++) + pMap[i] = new GDALOctaveLayer*[INTERVALS]; + + for (int oct = octaveStart; oct <= octaveEnd; oct++) + for (int i = 1; i <= INTERVALS; i++) + pMap[oct - 1][i - 1] = new GDALOctaveLayer(oct, i); +} + +void GDALOctaveMap::ComputeMap(GDALIntegralImage *poImg) +{ + for (int oct = octaveStart; oct <= octaveEnd; oct++) + for (int i = 1; i <= INTERVALS; i++) + pMap[oct - 1][i - 1]->ComputeLayer(poImg); +} + +bool GDALOctaveMap::PointIsExtremum(int row, int col, GDALOctaveLayer *bot, + GDALOctaveLayer *mid, GDALOctaveLayer *top, double threshold) +{ + //Check that point in middle layer has all neighbours + if (row <= top->radius || col <= top->radius || + row + top->radius >= top->height || col + top->radius >= top->width) + return false; + + double curPoint = mid->detHessians[row][col]; + + //Hessian should be higher than threshold + if (curPoint < threshold) + return false; + + //Hessian should be higher than hessians of all neighbours + for (int i = -1; i <= 1; i++) + for (int j = -1; j <= 1; j++) + { + double topPoint = top->detHessians[row + i][col + j]; + double midPoint = mid->detHessians[row + i][col + j]; + double botPoint = bot->detHessians[row + i][col + j]; + + if (topPoint >= curPoint || botPoint >= curPoint) + return false; + + if (i != 0 || j != 0) + if (midPoint >= curPoint) + return false; + } + + return true; +} + +GDALOctaveMap::~GDALOctaveMap() +{ + // Clean up Octave layers + for (int oct = octaveStart; oct <= octaveEnd; oct++) + for(int i = 0; i < INTERVALS; i++) + delete pMap[oct - 1][i]; + + //Clean up allocated memory + for (int oct = 0; oct < octaveEnd; oct++) + delete[] pMap[oct]; + + delete[] pMap; +} diff --git a/bazaar/plugin/gdal/alg/gdal_rpc.cpp b/bazaar/plugin/gdal/alg/gdal_rpc.cpp new file mode 100644 index 000000000..812e5e25f --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdal_rpc.cpp @@ -0,0 +1,1430 @@ +/****************************************************************************** + * $Id: gdal_rpc.cpp 29207 2015-05-18 17:23:45Z mloskot $ + * + * Project: Image Warper + * Purpose: Implements a rational polynomail (RPC) based transformer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_alg.h" +#include "ogr_spatialref.h" +#include "cpl_minixml.h" +#include "gdal_mdreader.h" + +CPL_CVSID("$Id: gdal_rpc.cpp 29207 2015-05-18 17:23:45Z mloskot $"); + +CPL_C_START +CPLXMLNode *GDALSerializeRPCTransformer( void *pTransformArg ); +void *GDALDeserializeRPCTransformer( CPLXMLNode *psTree ); +CPL_C_END + +/************************************************************************/ +/* RPCInfoToMD() */ +/* */ +/* Turn an RPCInfo structure back into it's metadata format. */ +/************************************************************************/ + +char ** RPCInfoToMD( GDALRPCInfo *psRPCInfo ) + +{ + char **papszMD = NULL; + CPLString osField, osMultiField; + int i; + + osField.Printf( "%.15g", psRPCInfo->dfLINE_OFF ); + papszMD = CSLSetNameValue( papszMD, RPC_LINE_OFF, osField ); + + osField.Printf( "%.15g", psRPCInfo->dfSAMP_OFF ); + papszMD = CSLSetNameValue( papszMD, RPC_SAMP_OFF, osField ); + + osField.Printf( "%.15g", psRPCInfo->dfLAT_OFF ); + papszMD = CSLSetNameValue( papszMD, RPC_LAT_OFF, osField ); + + osField.Printf( "%.15g", psRPCInfo->dfLONG_OFF ); + papszMD = CSLSetNameValue( papszMD, RPC_LONG_OFF, osField ); + + osField.Printf( "%.15g", psRPCInfo->dfHEIGHT_OFF ); + papszMD = CSLSetNameValue( papszMD, RPC_HEIGHT_OFF, osField ); + + osField.Printf( "%.15g", psRPCInfo->dfLINE_SCALE ); + papszMD = CSLSetNameValue( papszMD, RPC_LINE_SCALE, osField ); + + osField.Printf( "%.15g", psRPCInfo->dfSAMP_SCALE ); + papszMD = CSLSetNameValue( papszMD, RPC_SAMP_SCALE, osField ); + + osField.Printf( "%.15g", psRPCInfo->dfLAT_SCALE ); + papszMD = CSLSetNameValue( papszMD, RPC_LAT_SCALE, osField ); + + osField.Printf( "%.15g", psRPCInfo->dfLONG_SCALE ); + papszMD = CSLSetNameValue( papszMD, RPC_LONG_SCALE, osField ); + + osField.Printf( "%.15g", psRPCInfo->dfHEIGHT_SCALE ); + papszMD = CSLSetNameValue( papszMD, RPC_HEIGHT_SCALE, osField ); + + osField.Printf( "%.15g", psRPCInfo->dfMIN_LONG ); + papszMD = CSLSetNameValue( papszMD, "MIN_LONG", osField ); + + osField.Printf( "%.15g", psRPCInfo->dfMIN_LAT ); + papszMD = CSLSetNameValue( papszMD, "MIN_LAT", osField ); + + osField.Printf( "%.15g", psRPCInfo->dfMAX_LONG ); + papszMD = CSLSetNameValue( papszMD, "MAX_LONG", osField ); + + osField.Printf( "%.15g", psRPCInfo->dfMAX_LAT ); + papszMD = CSLSetNameValue( papszMD, "MAX_LAT", osField ); + + for( i = 0; i < 20; i++ ) + { + osField.Printf( "%.15g", psRPCInfo->adfLINE_NUM_COEFF[i] ); + if( i > 0 ) + osMultiField += " "; + else + osMultiField = ""; + osMultiField += osField; + } + papszMD = CSLSetNameValue( papszMD, "LINE_NUM_COEFF", osMultiField ); + + for( i = 0; i < 20; i++ ) + { + osField.Printf( "%.15g", psRPCInfo->adfLINE_DEN_COEFF[i] ); + if( i > 0 ) + osMultiField += " "; + else + osMultiField = ""; + osMultiField += osField; + } + papszMD = CSLSetNameValue( papszMD, "LINE_DEN_COEFF", osMultiField ); + + for( i = 0; i < 20; i++ ) + { + osField.Printf( "%.15g", psRPCInfo->adfSAMP_NUM_COEFF[i] ); + if( i > 0 ) + osMultiField += " "; + else + osMultiField = ""; + osMultiField += osField; + } + papszMD = CSLSetNameValue( papszMD, "SAMP_NUM_COEFF", osMultiField ); + + for( i = 0; i < 20; i++ ) + { + osField.Printf( "%.15g", psRPCInfo->adfSAMP_DEN_COEFF[i] ); + if( i > 0 ) + osMultiField += " "; + else + osMultiField = ""; + osMultiField += osField; + } + papszMD = CSLSetNameValue( papszMD, "SAMP_DEN_COEFF", osMultiField ); + + return papszMD; +} + +/************************************************************************/ +/* RPCComputeTerms() */ +/************************************************************************/ + +static void RPCComputeTerms( double dfLong, double dfLat, double dfHeight, + double *padfTerms ) + +{ + padfTerms[0] = 1.0; + padfTerms[1] = dfLong; + padfTerms[2] = dfLat; + padfTerms[3] = dfHeight; + padfTerms[4] = dfLong * dfLat; + padfTerms[5] = dfLong * dfHeight; + padfTerms[6] = dfLat * dfHeight; + padfTerms[7] = dfLong * dfLong; + padfTerms[8] = dfLat * dfLat; + padfTerms[9] = dfHeight * dfHeight; + + padfTerms[10] = dfLong * dfLat * dfHeight; + padfTerms[11] = dfLong * dfLong * dfLong; + padfTerms[12] = dfLong * dfLat * dfLat; + padfTerms[13] = dfLong * dfHeight * dfHeight; + padfTerms[14] = dfLong * dfLong * dfLat; + padfTerms[15] = dfLat * dfLat * dfLat; + padfTerms[16] = dfLat * dfHeight * dfHeight; + padfTerms[17] = dfLong * dfLong * dfHeight; + padfTerms[18] = dfLat * dfLat * dfHeight; + padfTerms[19] = dfHeight * dfHeight * dfHeight; +} + +/************************************************************************/ +/* RPCEvaluate() */ +/************************************************************************/ + +static double RPCEvaluate( double *padfTerms, double *padfCoefs ) + +{ + double dfSum = 0.0; + int i; + + for( i = 0; i < 20; i++ ) + dfSum += padfTerms[i] * padfCoefs[i]; + + return dfSum; +} + +/************************************************************************/ +/* RPCTransformPoint() */ +/************************************************************************/ + +static void RPCTransformPoint( GDALRPCInfo *psRPC, + double dfLong, double dfLat, double dfHeight, + double *pdfPixel, double *pdfLine ) + +{ + double dfResultX, dfResultY; + double adfTerms[20]; + + RPCComputeTerms( + (dfLong - psRPC->dfLONG_OFF) / psRPC->dfLONG_SCALE, + (dfLat - psRPC->dfLAT_OFF) / psRPC->dfLAT_SCALE, + (dfHeight - psRPC->dfHEIGHT_OFF) / psRPC->dfHEIGHT_SCALE, + adfTerms ); + + dfResultX = RPCEvaluate( adfTerms, psRPC->adfSAMP_NUM_COEFF ) + / RPCEvaluate( adfTerms, psRPC->adfSAMP_DEN_COEFF ); + + dfResultY = RPCEvaluate( adfTerms, psRPC->adfLINE_NUM_COEFF ) + / RPCEvaluate( adfTerms, psRPC->adfLINE_DEN_COEFF ); + + *pdfPixel = dfResultX * psRPC->dfSAMP_SCALE + psRPC->dfSAMP_OFF; + *pdfLine = dfResultY * psRPC->dfLINE_SCALE + psRPC->dfLINE_OFF; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALRPCTransformer */ +/* ==================================================================== */ +/************************************************************************/ + +/*! DEM Resampling Algorithm */ +typedef enum { + /*! Nearest neighbour (select on one input pixel) */ DRA_NearestNeighbour=0, + /*! Bilinear (2x2 kernel) */ DRA_Bilinear=1, + /*! Cubic Convolution Approximation (4x4 kernel) */ DRA_Cubic=2 +} DEMResampleAlg; + +typedef struct { + + GDALTransformerInfo sTI; + + GDALRPCInfo sRPC; + + double adfPLToLatLongGeoTransform[6]; + + int bReversed; + + double dfPixErrThreshold; + + double dfHeightOffset; + + double dfHeightScale; + + char *pszDEMPath; + + DEMResampleAlg eResampleAlg; + + int bHasDEMMissingValue; + double dfDEMMissingValue; + + int bHasTriedOpeningDS; + GDALDataset *poDS; + + OGRCoordinateTransformation *poCT; + + double adfDEMGeoTransform[6]; + double adfDEMReverseGeoTransform[6]; +} GDALRPCTransformInfo; + +/************************************************************************/ +/* GDALSerializeRPCDEMResample() */ +/************************************************************************/ + +static const char* GDALSerializeRPCDEMResample(DEMResampleAlg eResampleAlg) +{ + switch(eResampleAlg) + { + case DRA_NearestNeighbour: + return "near"; + case DRA_Cubic: + return "cubic"; + default: + case DRA_Bilinear: + return "bilinear"; + } +} + +/************************************************************************/ +/* GDALCreateSimilarRPCTransformer() */ +/************************************************************************/ + +static +void* GDALCreateSimilarRPCTransformer( void *hTransformArg, double dfRatioX, double dfRatioY ) +{ + VALIDATE_POINTER1( hTransformArg, "GDALCreateSimilarRPCTransformer", NULL ); + + GDALRPCTransformInfo *psInfo = (GDALRPCTransformInfo *) hTransformArg; + + GDALRPCInfo sRPC; + memcpy(&sRPC, &(psInfo->sRPC), sizeof(GDALRPCInfo)); + + if( dfRatioX != 1.0 || dfRatioY != 1.0 ) + { + sRPC.dfLINE_OFF /= dfRatioY; + sRPC.dfLINE_SCALE /= dfRatioY; + sRPC.dfSAMP_OFF /= dfRatioX; + sRPC.dfSAMP_SCALE /= dfRatioX; + } + + char** papszOptions = NULL; + papszOptions = CSLSetNameValue(papszOptions, "RPC_HEIGHT", + CPLSPrintf("%.18g", psInfo->dfHeightOffset)); + papszOptions = CSLSetNameValue(papszOptions, "RPC_HEIGHT_SCALE", + CPLSPrintf("%.18g", psInfo->dfHeightScale)); + if( psInfo->pszDEMPath != NULL ) + { + papszOptions = CSLSetNameValue(papszOptions, "RPC_DEM", psInfo->pszDEMPath); + papszOptions = CSLSetNameValue(papszOptions, "RPC_DEMINTERPOLATION", + GDALSerializeRPCDEMResample(psInfo->eResampleAlg)); + if( psInfo->bHasDEMMissingValue ) + papszOptions = CSLSetNameValue(papszOptions, "RPC_DEM_MISSING_VALUE", + CPLSPrintf("%.18g", psInfo->dfDEMMissingValue)) ; + } + psInfo = (GDALRPCTransformInfo*) GDALCreateRPCTransformer( &sRPC, + psInfo->bReversed, psInfo->dfPixErrThreshold, papszOptions ); + CSLDestroy(papszOptions); + + return psInfo; +} + +/************************************************************************/ +/* GDALCreateRPCTransformer() */ +/************************************************************************/ + +/** + * Create an RPC based transformer. + * + * The geometric sensor model describing the physical relationship between + * image coordinates and ground coordinate is known as a Rigorous Projection + * Model. A Rigorous Projection Model expresses the mapping of the image space + * coordinates of rows and columns (r,c) onto the object space reference + * surface geodetic coordinates (long, lat, height). + * + * RPC supports a generic description of the Rigorous Projection Models. The + * approximation used by GDAL (RPC00) is a set of rational polynomials exp + * ressing the normalized row and column values, (rn , cn), as a function of + * normalized geodetic latitude, longitude, and height, (P, L, H), given a + * set of normalized polynomial coefficients (LINE_NUM_COEF_n, LINE_DEN_COEF_n, + * SAMP_NUM_COEF_n, SAMP_DEN_COEF_n). Normalized values, rather than actual + * values are used in order to minimize introduction of errors during the + * calculations. The transformation between row and column values (r,c), and + * normalized row and column values (rn, cn), and between the geodetic + * latitude, longitude, and height and normalized geodetic latitude, + * longitude, and height (P, L, H), is defined by a set of normalizing + * translations (offsets) and scales that ensure all values are contained i + * the range -1 to +1. + * + * This function creates a GDALTransformFunc compatible transformer + * for going between image pixel/line and long/lat/height coordinates + * using RPCs. The RPCs are provided in a GDALRPCInfo structure which is + * normally read from metadata using GDALExtractRPCInfo(). + * + * GDAL RPC Metadata has the following entries (also described in GDAL RFC 22 + * and the GeoTIFF RPC document http://geotiff.maptools.org/rpc_prop.html . + * + *
    + *
  • ERR_BIAS: Error - Bias. The RMS bias error in meters per horizontal axis of all points in the image (-1.0 if unknown) + *
  • ERR_RAND: Error - Random. RMS random error in meters per horizontal axis of each point in the image (-1.0 if unknown) + *
  • LINE_OFF: Line Offset + *
  • SAMP_OFF: Sample Offset + *
  • LAT_OFF: Geodetic Latitude Offset + *
  • LONG_OFF: Geodetic Longitude Offset + *
  • HEIGHT_OFF: Geodetic Height Offset + *
  • LINE_SCALE: Line Scale + *
  • SAMP_SCALE: Sample Scale + *
  • LAT_SCALE: Geodetic Latitude Scale + *
  • LONG_SCALE: Geodetic Longitude Scale + *
  • HEIGHT_SCALE: Geodetic Height Scale + *
  • LINE_NUM_COEFF (1-20): Line Numerator Coefficients. Twenty coefficients for the polynomial in the Numerator of the rn equation. (space separated) + *
  • LINE_DEN_COEFF (1-20): Line Denominator Coefficients. Twenty coefficients for the polynomial in the Denominator of the rn equation. (space separated) + *
  • SAMP_NUM_COEFF (1-20): Sample Numerator Coefficients. Twenty coefficients for the polynomial in the Numerator of the cn equation. (space separated) + *
  • SAMP_DEN_COEFF (1-20): Sample Denominator Coefficients. Twenty coefficients for the polynomial in the Denominator of the cn equation. (space separated) + *
+ * + * The transformer normally maps from pixel/line/height to long/lat/height space + * as a forward transformation though in RPC terms that would be considered + * an inverse transformation (and is solved by iterative approximation using + * long/lat/height to pixel/line transformations). The default direction can + * be reversed by passing bReversed=TRUE. + * + * The iterative solution of pixel/line + * to lat/long/height is currently run for up to 10 iterations or until + * the apparent error is less than dfPixErrThreshold pixels. Passing zero + * will not avoid all error, but will cause the operation to run for the maximum + * number of iterations. + * + * Additional options to the transformer can be supplied in papszOptions. + * + * Options: + * + *
    + *
  • RPC_HEIGHT: a fixed height offset to be applied to all points passed + * in. In this situation the Z passed into the transformation function is + * assumed to be height above ground, and the RPC_HEIGHT is assumed to be + * an average height above sea level for ground in the target scene. + * + *
  • RPC_HEIGHT_SCALE: a factor used to multiply heights above ground. + * Useful when elevation offsets of the DEM are not expressed in meters. (GDAL >= 1.8.0) + * + *
  • RPC_DEM: the name of a GDAL dataset (a DEM file typically) used to + * extract elevation offsets from. In this situation the Z passed into the + * transformation function is assumed to be height above ground. This option + * should be used in replacement of RPC_HEIGHT to provide a way of defining + * a non uniform ground for the target scene (GDAL >= 1.8.0) + * + *
  • RPC_DEMINTERPOLATION: the DEM interpolation (near, bilinear or cubic) + * + *
  • RPC_DEM_MISSING_VALUE: value of DEM height that must be used in case + * the DEM has nodata value at the sampling point, or if its extent does not + * cover the requested coordinate. When not specified, missing values will cause + * a failed transform. (GDAL >= 1.11.2) + * + *
+ * + * @param psRPCInfo Definition of the RPC parameters. + * + * @param bReversed If true "forward" transformation will be lat/long to pixel/line instead of the normal pixel/line to lat/long. + * + * @param dfPixErrThreshold the error (measured in pixels) allowed in the + * iterative solution of pixel/line to lat/long computations (the other way + * is always exact given the equations). + * + * @param papszOptions Other transformer options (ie. RPC_HEIGHT=). + * + * @return transformer callback data (deallocate with GDALDestroyTransformer()). + */ + +void *GDALCreateRPCTransformer( GDALRPCInfo *psRPCInfo, int bReversed, + double dfPixErrThreshold, + char **papszOptions ) + +{ + GDALRPCTransformInfo *psTransform; + +/* -------------------------------------------------------------------- */ +/* Initialize core info. */ +/* -------------------------------------------------------------------- */ + psTransform = (GDALRPCTransformInfo *) + CPLCalloc(sizeof(GDALRPCTransformInfo),1); + + memcpy( &(psTransform->sRPC), psRPCInfo, sizeof(GDALRPCInfo) ); + psTransform->bReversed = bReversed; + psTransform->dfPixErrThreshold = dfPixErrThreshold; + psTransform->dfHeightOffset = 0.0; + psTransform->dfHeightScale = 1.0; + + memcpy( psTransform->sTI.abySignature, GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE) ); + psTransform->sTI.pszClassName = "GDALRPCTransformer"; + psTransform->sTI.pfnTransform = GDALRPCTransform; + psTransform->sTI.pfnCleanup = GDALDestroyRPCTransformer; + psTransform->sTI.pfnSerialize = GDALSerializeRPCTransformer; + psTransform->sTI.pfnCreateSimilar = GDALCreateSimilarRPCTransformer; + +/* -------------------------------------------------------------------- */ +/* Do we have a "average height" that we want to consider all */ +/* elevations to be relative to? */ +/* -------------------------------------------------------------------- */ + const char *pszHeight = CSLFetchNameValue( papszOptions, "RPC_HEIGHT" ); + if( pszHeight != NULL ) + psTransform->dfHeightOffset = CPLAtof(pszHeight); + +/* -------------------------------------------------------------------- */ +/* The "height scale" */ +/* -------------------------------------------------------------------- */ + const char *pszHeightScale = CSLFetchNameValue( papszOptions, "RPC_HEIGHT_SCALE" ); + if( pszHeightScale != NULL ) + psTransform->dfHeightScale = CPLAtof(pszHeightScale); + +/* -------------------------------------------------------------------- */ +/* The DEM file name */ +/* -------------------------------------------------------------------- */ + const char *pszDEMPath = CSLFetchNameValue( papszOptions, "RPC_DEM" ); + if( pszDEMPath != NULL ) + psTransform->pszDEMPath = CPLStrdup(pszDEMPath); + +/* -------------------------------------------------------------------- */ +/* The DEM interpolation */ +/* -------------------------------------------------------------------- */ + const char *pszDEMInterpolation = CSLFetchNameValueDef( papszOptions, "RPC_DEMINTERPOLATION", "bilinear" ); + if(EQUAL(pszDEMInterpolation, "near" )) + psTransform->eResampleAlg = DRA_NearestNeighbour; + else if(EQUAL(pszDEMInterpolation, "bilinear" )) + psTransform->eResampleAlg = DRA_Bilinear; + else if(EQUAL(pszDEMInterpolation, "cubic" )) + psTransform->eResampleAlg = DRA_Cubic; + else + { + CPLDebug("RPC", "Unknown interpolation %s. Defaulting to bilinear", pszDEMInterpolation); + psTransform->eResampleAlg = DRA_Bilinear; + } + +/* -------------------------------------------------------------------- */ +/* The DEM missing value */ +/* -------------------------------------------------------------------- */ + const char *pszDEMMissingValue = CSLFetchNameValue( papszOptions, "RPC_DEM_MISSING_VALUE" ); + if( pszDEMMissingValue != NULL ) + { + psTransform->bHasDEMMissingValue = TRUE; + psTransform->dfDEMMissingValue = CPLAtof(pszDEMMissingValue); + } + +/* -------------------------------------------------------------------- */ +/* Establish a reference point for calcualating an affine */ +/* geotransform approximate transformation. */ +/* -------------------------------------------------------------------- */ + double adfGTFromLL[6], dfRefPixel = -1.0, dfRefLine = -1.0; + double dfRefLong = 0.0, dfRefLat = 0.0; + + if( psRPCInfo->dfMIN_LONG != -180 || psRPCInfo->dfMAX_LONG != 180 ) + { + dfRefLong = (psRPCInfo->dfMIN_LONG + psRPCInfo->dfMAX_LONG) * 0.5; + dfRefLat = (psRPCInfo->dfMIN_LAT + psRPCInfo->dfMAX_LAT ) * 0.5; + + RPCTransformPoint( psRPCInfo, dfRefLong, dfRefLat, 0.0, + &dfRefPixel, &dfRefLine ); + } + + // Try with scale and offset if we don't can't use bounds or + // the results seem daft. + if( dfRefPixel < 0.0 || dfRefLine < 0.0 + || dfRefPixel > 100000 || dfRefLine > 100000 ) + { + dfRefLong = psRPCInfo->dfLONG_OFF; + dfRefLat = psRPCInfo->dfLAT_OFF; + + RPCTransformPoint( psRPCInfo, dfRefLong, dfRefLat, 0.0, + &dfRefPixel, &dfRefLine ); + } + +/* -------------------------------------------------------------------- */ +/* Transform nearby locations to establish affine direction */ +/* vectors. */ +/* -------------------------------------------------------------------- */ + double dfRefPixelDelta, dfRefLineDelta, dfLLDelta = 0.0001; + + RPCTransformPoint( psRPCInfo, dfRefLong+dfLLDelta, dfRefLat, 0.0, + &dfRefPixelDelta, &dfRefLineDelta ); + adfGTFromLL[1] = (dfRefPixelDelta - dfRefPixel) / dfLLDelta; + adfGTFromLL[4] = (dfRefLineDelta - dfRefLine) / dfLLDelta; + + RPCTransformPoint( psRPCInfo, dfRefLong, dfRefLat+dfLLDelta, 0.0, + &dfRefPixelDelta, &dfRefLineDelta ); + adfGTFromLL[2] = (dfRefPixelDelta - dfRefPixel) / dfLLDelta; + adfGTFromLL[5] = (dfRefLineDelta - dfRefLine) / dfLLDelta; + + adfGTFromLL[0] = dfRefPixel + - adfGTFromLL[1] * dfRefLong - adfGTFromLL[2] * dfRefLat; + adfGTFromLL[3] = dfRefLine + - adfGTFromLL[4] * dfRefLong - adfGTFromLL[5] * dfRefLat; + + if( !GDALInvGeoTransform( adfGTFromLL, psTransform->adfPLToLatLongGeoTransform) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + GDALDestroyRPCTransformer(psTransform); + return NULL; + } + return psTransform; +} + +/************************************************************************/ +/* GDALDestroyReprojectionTransformer() */ +/************************************************************************/ + +void GDALDestroyRPCTransformer( void *pTransformAlg ) + +{ + if( pTransformAlg == NULL ) + return; + + GDALRPCTransformInfo *psTransform = (GDALRPCTransformInfo *) pTransformAlg; + + CPLFree( psTransform->pszDEMPath ); + + if(psTransform->poDS) + GDALClose(psTransform->poDS); + if(psTransform->poCT) + OCTDestroyCoordinateTransformation((OGRCoordinateTransformationH)psTransform->poCT); + + CPLFree( pTransformAlg ); +} + +/************************************************************************/ +/* RPCInverseTransformPoint() */ +/************************************************************************/ + +static void +RPCInverseTransformPoint( GDALRPCTransformInfo *psTransform, + double dfPixel, double dfLine, double dfHeight, + double *pdfLong, double *pdfLat ) + +{ + double dfResultX, dfResultY; + int iIter; + GDALRPCInfo *psRPC = &(psTransform->sRPC); + +/* -------------------------------------------------------------------- */ +/* Compute an initial approximation based on linear */ +/* interpolation from our reference point. */ +/* -------------------------------------------------------------------- */ + dfResultX = psTransform->adfPLToLatLongGeoTransform[0] + + psTransform->adfPLToLatLongGeoTransform[1] * dfPixel + + psTransform->adfPLToLatLongGeoTransform[2] * dfLine; + + dfResultY = psTransform->adfPLToLatLongGeoTransform[3] + + psTransform->adfPLToLatLongGeoTransform[4] * dfPixel + + psTransform->adfPLToLatLongGeoTransform[5] * dfLine; + +/* -------------------------------------------------------------------- */ +/* Now iterate, trying to find a closer LL location that will */ +/* back transform to the indicated pixel and line. */ +/* -------------------------------------------------------------------- */ + double dfPixelDeltaX=0.0, dfPixelDeltaY=0.0; + + for( iIter = 0; iIter < 10; iIter++ ) + { + double dfBackPixel, dfBackLine; + + RPCTransformPoint( psRPC, dfResultX, dfResultY, dfHeight, + &dfBackPixel, &dfBackLine ); + + dfPixelDeltaX = dfBackPixel - dfPixel; + dfPixelDeltaY = dfBackLine - dfLine; + + dfResultX = dfResultX + - dfPixelDeltaX * psTransform->adfPLToLatLongGeoTransform[1] + - dfPixelDeltaY * psTransform->adfPLToLatLongGeoTransform[2]; + dfResultY = dfResultY + - dfPixelDeltaX * psTransform->adfPLToLatLongGeoTransform[4] + - dfPixelDeltaY * psTransform->adfPLToLatLongGeoTransform[5]; + + if( ABS(dfPixelDeltaX) < psTransform->dfPixErrThreshold + && ABS(dfPixelDeltaY) < psTransform->dfPixErrThreshold ) + { + iIter = -1; + //CPLDebug( "RPC", "Converged!" ); + break; + } + } + + if( iIter != -1 ) + { +#ifdef notdef + CPLDebug( "RPC", "Failed Iterations %d: Got: %g,%g Offset=%g,%g", + iIter, + dfResultX, dfResultY, + dfPixelDeltaX, dfPixelDeltaY ); +#endif + } + + *pdfLong = dfResultX; + *pdfLat = dfResultY; +} + + +static +double BiCubicKernel(double dfVal) +{ + if ( dfVal > 2.0 ) + return 0.0; + + double a, b, c, d; + double xm1 = dfVal - 1.0; + double xp1 = dfVal + 1.0; + double xp2 = dfVal + 2.0; + + a = ( xp2 <= 0.0 ) ? 0.0 : xp2 * xp2 * xp2; + b = ( xp1 <= 0.0 ) ? 0.0 : xp1 * xp1 * xp1; + c = ( dfVal <= 0.0 ) ? 0.0 : dfVal * dfVal * dfVal; + d = ( xm1 <= 0.0 ) ? 0.0 : xm1 * xm1 * xm1; + + return ( 0.16666666666666666667 * ( a - ( 4.0 * b ) + ( 6.0 * c ) - ( 4.0 * d ) ) ); +} + +/************************************************************************/ +/* GDALRPCGetDEMHeight() */ +/************************************************************************/ + +static +int GDALRPCGetDEMHeight( GDALRPCTransformInfo *psTransform, + double dfX, double dfY, double* pdfDEMH ) +{ + + int bGotNoDataValue = FALSE; + double dfNoDataValue = 0; + int nRasterXSize = psTransform->poDS->GetRasterXSize(); + int nRasterYSize = psTransform->poDS->GetRasterYSize(); + dfNoDataValue = psTransform->poDS->GetRasterBand(1)->GetNoDataValue( &bGotNoDataValue ); + + int bands[1] = {1}; + + int dX = int(dfX); + int dY = int(dfY); + double dfDEMH(0); + double dfDeltaX = dfX - dX; + double dfDeltaY = dfY - dY; + + if(psTransform->eResampleAlg == DRA_Cubic) + { + int dXNew = dX - 1; + int dYNew = dY - 1; + if (!(dXNew >= 0 && dYNew >= 0 && dXNew + 4 <= nRasterXSize && dYNew + 4 <= nRasterYSize)) + { + return FALSE; + } + //cubic interpolation + double adfElevData[16] = {0}; + CPLErr eErr = psTransform->poDS->RasterIO(GF_Read, dXNew, dYNew, 4, 4, + &adfElevData, 4, 4, + GDT_Float64, 1, bands, 0, 0, 0, + NULL); + if(eErr != CE_None) + { + return FALSE; + } + + double dfSumH(0), dfSumWeight(0); + for ( int k_i = 0; k_i < 4; k_i++ ) + { + // Loop across the X axis + for ( int k_j = 0; k_j < 4; k_j++ ) + { + // Calculate the weight for the specified pixel according + // to the bicubic b-spline kernel we're using for + // interpolation + int dKernIndX = k_j - 1; + int dKernIndY = k_i - 1; + double dfPixelWeight = BiCubicKernel(dKernIndX - dfDeltaX) * BiCubicKernel(dKernIndY - dfDeltaY); + + // Create a sum of all values + // adjusted for the pixel's calculated weight + double dfElev = adfElevData[k_j + k_i * 4]; + if( bGotNoDataValue && ARE_REAL_EQUAL(dfNoDataValue, dfElev) ) + continue; + + dfSumH += dfElev * dfPixelWeight; + dfSumWeight += dfPixelWeight; + } + } + if( dfSumWeight == 0.0 ) + { + return FALSE; + } + dfDEMH = dfSumH / dfSumWeight; + } + else if(psTransform->eResampleAlg == DRA_Bilinear) + { + if (!(dX >= 0 && dY >= 0 && dX + 2 <= nRasterXSize && dY + 2 <= nRasterYSize)) + { + return FALSE; + } + //bilinear interpolation + double adfElevData[4] = {0,0,0,0}; + CPLErr eErr = psTransform->poDS->RasterIO(GF_Read, dX, dY, 2, 2, + &adfElevData, 2, 2, + GDT_Float64, 1, bands, 0, 0, 0, + NULL); + if(eErr != CE_None) + { + return FALSE; + } + if( bGotNoDataValue ) + { + // TODO: we could perhaps use a valid sample if there's one + int bFoundNoDataElev = FALSE; + for(int k_i=0;k_i<4;k_i++) + { + if( ARE_REAL_EQUAL(dfNoDataValue, adfElevData[k_i]) ) + bFoundNoDataElev = TRUE; + } + if( bFoundNoDataElev ) + { + return FALSE; + } + } + double dfDeltaX1 = 1.0 - dfDeltaX; + double dfDeltaY1 = 1.0 - dfDeltaY; + + double dfXZ1 = adfElevData[0] * dfDeltaX1 + adfElevData[1] * dfDeltaX; + double dfXZ2 = adfElevData[2] * dfDeltaX1 + adfElevData[3] * dfDeltaX; + double dfYZ = dfXZ1 * dfDeltaY1 + dfXZ2 * dfDeltaY; + dfDEMH = dfYZ; + } + else + { + if (!(dX >= 0 && dY >= 0 && dX < nRasterXSize && dY < nRasterYSize)) + { + return FALSE; + } + CPLErr eErr = psTransform->poDS->RasterIO(GF_Read, dX, dY, 1, 1, + &dfDEMH, 1, 1, + GDT_Float64, 1, bands, 0, 0, 0, + NULL); + if(eErr != CE_None || + (bGotNoDataValue && ARE_REAL_EQUAL(dfNoDataValue, dfDEMH)) ) + { + return FALSE; + } + } + + *pdfDEMH = dfDEMH; + + return TRUE; +} + +/************************************************************************/ +/* GDALRPCTransformWholeLineWithDEM() */ +/************************************************************************/ + +static int GDALRPCTransformWholeLineWithDEM( GDALRPCTransformInfo *psTransform, + int nPointCount, + double *padfX, double *padfY, double *padfZ, + int *panSuccess, + int nXLeft, int nXWidth, + int nYTop, int nYHeight ) +{ + int i; + GDALRPCInfo *psRPC = &(psTransform->sRPC); + + double* padfDEMBuffer = (double*) VSIMalloc2(sizeof(double), nXWidth * nYHeight); + if( padfDEMBuffer == NULL ) + { + for( i = 0; i < nPointCount; i++ ) + panSuccess[i] = FALSE; + return FALSE; + } + CPLErr eErr = psTransform->poDS->GetRasterBand(1)-> + RasterIO(GF_Read, nXLeft, nYTop, nXWidth, nYHeight, + padfDEMBuffer, nXWidth, nYHeight, + GDT_Float64, 0, 0, NULL); + if( eErr != CE_None ) + { + for( i = 0; i < nPointCount; i++ ) + panSuccess[i] = FALSE; + VSIFree(padfDEMBuffer); + return FALSE; + } + + + int bGotNoDataValue = FALSE; + double dfNoDataValue = 0; + dfNoDataValue = psTransform->poDS->GetRasterBand(1)->GetNoDataValue( &bGotNoDataValue ); + + double dfY = psTransform->adfDEMReverseGeoTransform[3] + + padfY[0] * psTransform->adfDEMReverseGeoTransform[5]; + int nY = int(dfY); + double dfDeltaY = dfY - nY; + + for( i = 0; i < nPointCount; i++ ) + { + double dfX = psTransform->adfDEMReverseGeoTransform[0] + + padfX[i] * psTransform->adfDEMReverseGeoTransform[1]; + + double dfDEMH(0); + + int nX = int(dfX); + double dfDeltaX = dfX - nX; + + if(psTransform->eResampleAlg == DRA_Cubic) + { + int nXNew = nX - 1; + + double dfSumH(0), dfSumWeight(0); + for ( int k_i = 0; k_i < 4; k_i++ ) + { + // Loop across the X axis + for ( int k_j = 0; k_j < 4; k_j++ ) + { + // Calculate the weight for the specified pixel according + // to the bicubic b-spline kernel we're using for + // interpolation + int dKernIndX = k_j - 1; + int dKernIndY = k_i - 1; + double dfPixelWeight = BiCubicKernel(dKernIndX - dfDeltaX) * BiCubicKernel(dKernIndY - dfDeltaY); + + // Create a sum of all values + // adjusted for the pixel's calculated weight + double dfElev = padfDEMBuffer[k_i * nXWidth + nXNew - nXLeft + k_j]; + if( bGotNoDataValue && ARE_REAL_EQUAL(dfNoDataValue, dfElev) ) + continue; + + dfSumH += dfElev * dfPixelWeight; + dfSumWeight += dfPixelWeight; + } + } + if( dfSumWeight == 0.0 ) + { + if( psTransform->bHasDEMMissingValue ) + dfDEMH = psTransform->dfDEMMissingValue; + else + { + panSuccess[i] = FALSE; + continue; + } + } + else + dfDEMH = dfSumH / dfSumWeight; + } + else if(psTransform->eResampleAlg == DRA_Bilinear) + { + + //bilinear interpolation + double adfElevData[4]; + memcpy(adfElevData, padfDEMBuffer + nX - nXLeft, 2 * sizeof(double)); + memcpy(adfElevData + 2, padfDEMBuffer + nXWidth + nX - nXLeft, 2 * sizeof(double)); + + int bFoundNoDataElev = FALSE; + if( bGotNoDataValue ) + { + int k_valid_sample = -1; + for(int k_i=0;k_i<4;k_i++) + { + if( ARE_REAL_EQUAL(dfNoDataValue, adfElevData[k_i]) ) + { + bFoundNoDataElev = TRUE; + } + else if( k_valid_sample < 0 ) + k_valid_sample = k_i; + } + if( bFoundNoDataElev ) + { + if( k_valid_sample >= 0 ) + { + dfDEMH = adfElevData[k_valid_sample]; + RPCTransformPoint( psRPC, padfX[i], padfY[i], + padfZ[i] + (psTransform->dfHeightOffset + dfDEMH) * + psTransform->dfHeightScale, + padfX + i, padfY + i ); + + panSuccess[i] = TRUE; + continue; + } + else if( psTransform->bHasDEMMissingValue ) + { + dfDEMH = psTransform->dfDEMMissingValue; + RPCTransformPoint( psRPC, padfX[i], padfY[i], + padfZ[i] + (psTransform->dfHeightOffset + dfDEMH) * + psTransform->dfHeightScale, + padfX + i, padfY + i ); + + panSuccess[i] = TRUE; + continue; + } + else + { + panSuccess[i] = FALSE; + continue; + } + } + } + double dfDeltaX1 = 1.0 - dfDeltaX; + double dfDeltaY1 = 1.0 - dfDeltaY; + + double dfXZ1 = adfElevData[0] * dfDeltaX1 + adfElevData[1] * dfDeltaX; + double dfXZ2 = adfElevData[2] * dfDeltaX1 + adfElevData[3] * dfDeltaX; + double dfYZ = dfXZ1 * dfDeltaY1 + dfXZ2 * dfDeltaY; + dfDEMH = dfYZ; + } + else + { + dfDEMH = padfDEMBuffer[nX - nXLeft]; + if( bGotNoDataValue && ARE_REAL_EQUAL(dfNoDataValue, dfDEMH) ) + { + if( psTransform->bHasDEMMissingValue ) + dfDEMH = psTransform->dfDEMMissingValue; + else + { + panSuccess[i] = FALSE; + continue; + } + } + } + + RPCTransformPoint( psRPC, padfX[i], padfY[i], + padfZ[i] + (psTransform->dfHeightOffset + dfDEMH) * + psTransform->dfHeightScale, + padfX + i, padfY + i ); + + panSuccess[i] = TRUE; + } + + VSIFree(padfDEMBuffer); + + return TRUE; +} + +/************************************************************************/ +/* GDALRPCTransform() */ +/************************************************************************/ + +int GDALRPCTransform( void *pTransformArg, int bDstToSrc, + int nPointCount, + double *padfX, double *padfY, double *padfZ, + int *panSuccess ) + +{ + VALIDATE_POINTER1( pTransformArg, "GDALRPCTransform", 0 ); + + GDALRPCTransformInfo *psTransform = (GDALRPCTransformInfo *) pTransformArg; + GDALRPCInfo *psRPC = &(psTransform->sRPC); + int i; + + if( psTransform->bReversed ) + bDstToSrc = !bDstToSrc; + +/* -------------------------------------------------------------------- */ +/* Lazy opening of the optionnal DEM file. */ +/* -------------------------------------------------------------------- */ + if(psTransform->pszDEMPath != NULL && + psTransform->bHasTriedOpeningDS == FALSE) + { + int bIsValid = FALSE; + psTransform->bHasTriedOpeningDS = TRUE; + psTransform->poDS = (GDALDataset *) + GDALOpen( psTransform->pszDEMPath, GA_ReadOnly ); + if(psTransform->poDS != NULL && psTransform->poDS->GetRasterCount() >= 1) + { + const char* pszSpatialRef = psTransform->poDS->GetProjectionRef(); + if (pszSpatialRef != NULL && pszSpatialRef[0] != '\0') + { + OGRSpatialReference* poWGSSpaRef = + new OGRSpatialReference(SRS_WKT_WGS84); + OGRSpatialReference* poDSSpaRef = + new OGRSpatialReference(pszSpatialRef); + if(!poWGSSpaRef->IsSame(poDSSpaRef)) + psTransform->poCT =OGRCreateCoordinateTransformation( + poWGSSpaRef, poDSSpaRef ); + delete poWGSSpaRef; + delete poDSSpaRef; + } + + if (psTransform->poDS->GetGeoTransform( + psTransform->adfDEMGeoTransform) == CE_None && + GDALInvGeoTransform( psTransform->adfDEMGeoTransform, + psTransform->adfDEMReverseGeoTransform )) + { + bIsValid = TRUE; + } + } + + if (!bIsValid && psTransform->poDS != NULL) + { + GDALClose(psTransform->poDS); + psTransform->poDS = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* The simple case is transforming from lat/long to pixel/line. */ +/* Just apply the equations directly. */ +/* -------------------------------------------------------------------- */ + if( bDstToSrc ) + { + /* Optimization to avoid doing too many picking in DEM in the particular */ + /* case where each point to transform is on a single line of the DEM */ + /* To make it simple and fast we check that all input latitudes are */ + /* identical, that the DEM is in WGS84 geodetic and that it has no rotation. */ + /* Such case is for example triggered when doing gdalwarp with a target SRS */ + /* of EPSG:4326 or EPSG:3857 */ + if( nPointCount >= 10 && psTransform->poDS != NULL && + psTransform->poCT == NULL && padfY[0] == padfY[nPointCount-1] && + padfY[0] == padfY[nPointCount/ 2] && + psTransform->adfDEMReverseGeoTransform[1] > 0.0 && + psTransform->adfDEMReverseGeoTransform[2] == 0.0 && + psTransform->adfDEMReverseGeoTransform[4] == 0.0 && + CSLTestBoolean(CPLGetConfigOption("GDAL_RPC_DEM_OPTIM", "YES")) ) + { + int bUseOptimized = TRUE; + double dfMinX = padfX[0], dfMaxX = padfX[0]; + for(i = 1; i < nPointCount; i++) + { + if( padfY[i] != padfY[0] ) + { + bUseOptimized = FALSE; + break; + } + if( padfX[i] < dfMinX ) dfMinX = padfX[i]; + if( padfX[i] > dfMaxX ) dfMaxX = padfX[i]; + } + if( bUseOptimized ) + { + double dfX1, dfY1, dfX2, dfY2; + GDALApplyGeoTransform( psTransform->adfDEMReverseGeoTransform, + dfMinX, padfY[0], &dfX1, &dfY1 ); + GDALApplyGeoTransform( psTransform->adfDEMReverseGeoTransform, + dfMaxX, padfY[0], &dfX2, &dfY2 ); + + int nXLeft = int(floor(dfX1)); + int nXRight = int(floor(dfX2)); + int nXWidth = nXRight - nXLeft + 1; + int nYTop = int(floor(dfY1)); + int nYHeight; + if( psTransform->eResampleAlg == DRA_Cubic ) + { + nXLeft --; + nXWidth += 3; + nYTop --; + nYHeight = 4; + } + else if( psTransform->eResampleAlg == DRA_Bilinear ) + { + nXWidth ++; + nYHeight = 2; + } + else + nYHeight = 1; + if( nXLeft >= 0 && nXLeft + nXWidth <= psTransform->poDS->GetRasterXSize() && + nYTop >= 0 && nYTop + nYHeight <= psTransform->poDS->GetRasterYSize() ) + { + static int bOnce = FALSE; + if( !bOnce ) + { + bOnce = TRUE; + CPLDebug("RPC", "Using GDALRPCTransformWholeLineWithDEM"); + } + return GDALRPCTransformWholeLineWithDEM( psTransform, nPointCount, + padfX, padfY, padfZ, + panSuccess, + nXLeft, nXWidth, + nYTop, nYHeight ); + } + } + } + + for( i = 0; i < nPointCount; i++ ) + { + if(psTransform->poDS) + { + double dfX, dfY; + //check if dem is not in WGS84 and transform points padfX[i], padfY[i] + if(psTransform->poCT) + { + double dfXOrig = padfX[i]; + double dfYOrig = padfY[i]; + double dfZOrig = padfZ[i]; + if (!psTransform->poCT->Transform( + 1, &dfXOrig, &dfYOrig, &dfZOrig)) + { + panSuccess[i] = FALSE; + continue; + } + GDALApplyGeoTransform( psTransform->adfDEMReverseGeoTransform, + dfXOrig, dfYOrig, &dfX, &dfY ); + } + else + GDALApplyGeoTransform( psTransform->adfDEMReverseGeoTransform, + padfX[i], padfY[i], &dfX, &dfY ); + + double dfDEMH(0); + if( !GDALRPCGetDEMHeight( psTransform, dfX, dfY, &dfDEMH) ) + { + if( psTransform->bHasDEMMissingValue ) + dfDEMH = psTransform->dfDEMMissingValue; + else + { + panSuccess[i] = FALSE; + continue; + } + } + + RPCTransformPoint( psRPC, padfX[i], padfY[i], + padfZ[i] + (psTransform->dfHeightOffset + dfDEMH) * + psTransform->dfHeightScale, + padfX + i, padfY + i ); + } + else + RPCTransformPoint( psRPC, padfX[i], padfY[i], + padfZ[i] + psTransform->dfHeightOffset * + psTransform->dfHeightScale, + padfX + i, padfY + i ); + panSuccess[i] = TRUE; + } + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Compute the inverse (pixel/line/height to lat/long). This */ +/* function uses an iterative method from an initial linear */ +/* approximation. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nPointCount; i++ ) + { + double dfResultX, dfResultY; + + if(psTransform->poDS) + { + RPCInverseTransformPoint( psTransform, padfX[i], padfY[i], + padfZ[i] + psTransform->dfHeightOffset * + psTransform->dfHeightScale, + &dfResultX, &dfResultY ); + + double dfX, dfY; + //check if dem is not in WGS84 and transform points padfX[i], padfY[i] + if(psTransform->poCT) + { + double dfZ = 0; + if (!psTransform->poCT->Transform(1, &dfResultX, &dfResultY, &dfZ)) + { + panSuccess[i] = FALSE; + continue; + } + } + + GDALApplyGeoTransform( psTransform->adfDEMReverseGeoTransform, + dfResultX, dfResultY, &dfX, &dfY ); + + double dfDEMH(0); + if( !GDALRPCGetDEMHeight( psTransform, dfX, dfY, &dfDEMH) ) + { + if( psTransform->bHasDEMMissingValue ) + dfDEMH = psTransform->dfDEMMissingValue; + else + { + panSuccess[i] = FALSE; + continue; + } + } + + RPCInverseTransformPoint( psTransform, padfX[i], padfY[i], + padfZ[i] + (psTransform->dfHeightOffset + dfDEMH) * + psTransform->dfHeightScale, + &dfResultX, &dfResultY ); + } + else + { + RPCInverseTransformPoint( psTransform, padfX[i], padfY[i], + padfZ[i] + psTransform->dfHeightOffset * + psTransform->dfHeightScale, + &dfResultX, &dfResultY ); + + } + padfX[i] = dfResultX; + padfY[i] = dfResultY; + + panSuccess[i] = TRUE; + } + + return TRUE; +} + +/************************************************************************/ +/* GDALSerializeRPCTransformer() */ +/************************************************************************/ + +CPLXMLNode *GDALSerializeRPCTransformer( void *pTransformArg ) + +{ + VALIDATE_POINTER1( pTransformArg, "GDALSerializeRPCTransformer", NULL ); + + CPLXMLNode *psTree; + GDALRPCTransformInfo *psInfo = + (GDALRPCTransformInfo *)(pTransformArg); + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "RPCTransformer" ); + +/* -------------------------------------------------------------------- */ +/* Serialize bReversed. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( + psTree, "Reversed", + CPLString().Printf( "%d", psInfo->bReversed ) ); + +/* -------------------------------------------------------------------- */ +/* Serialize Height Offset. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( + psTree, "HeightOffset", + CPLString().Printf( "%.15g", psInfo->dfHeightOffset ) ); + +/* -------------------------------------------------------------------- */ +/* Serialize Height Scale. */ +/* -------------------------------------------------------------------- */ + if (psInfo->dfHeightScale != 1.0) + CPLCreateXMLElementAndValue( + psTree, "HeightScale", + CPLString().Printf( "%.15g", psInfo->dfHeightScale ) ); + +/* -------------------------------------------------------------------- */ +/* Serialize DEM path. */ +/* -------------------------------------------------------------------- */ + if (psInfo->pszDEMPath != NULL) + { + CPLCreateXMLElementAndValue( + psTree, "DEMPath", + CPLString().Printf( "%s", psInfo->pszDEMPath ) ); + +/* -------------------------------------------------------------------- */ +/* Serialize DEM interpolation */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( + psTree, "DEMInterpolation", GDALSerializeRPCDEMResample(psInfo->eResampleAlg) ); + + if( psInfo->bHasDEMMissingValue ) + { + CPLCreateXMLElementAndValue( + psTree, "DEMMissingValue", CPLSPrintf("%.18g", psInfo->dfDEMMissingValue) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Serialize pixel error threshold. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( + psTree, "PixErrThreshold", + CPLString().Printf( "%.15g", psInfo->dfPixErrThreshold ) ); + +/* -------------------------------------------------------------------- */ +/* RPC metadata. */ +/* -------------------------------------------------------------------- */ + char **papszMD = RPCInfoToMD( &(psInfo->sRPC) ); + CPLXMLNode *psMD= CPLCreateXMLNode( psTree, CXT_Element, + "Metadata" ); + + for( int i = 0; papszMD != NULL && papszMD[i] != NULL; i++ ) + { + const char *pszRawValue; + char *pszKey; + CPLXMLNode *psMDI; + + pszRawValue = CPLParseNameValue( papszMD[i], &pszKey ); + + psMDI = CPLCreateXMLNode( psMD, CXT_Element, "MDI" ); + CPLSetXMLValue( psMDI, "#key", pszKey ); + CPLCreateXMLNode( psMDI, CXT_Text, pszRawValue ); + + CPLFree( pszKey ); + } + + CSLDestroy( papszMD ); + + return psTree; +} + +/************************************************************************/ +/* GDALDeserializeRPCTransformer() */ +/************************************************************************/ + +void *GDALDeserializeRPCTransformer( CPLXMLNode *psTree ) + +{ + void *pResult; + char **papszOptions = NULL; + +/* -------------------------------------------------------------------- */ +/* Collect metadata. */ +/* -------------------------------------------------------------------- */ + char **papszMD = NULL; + CPLXMLNode *psMDI, *psMetadata; + GDALRPCInfo sRPC; + + psMetadata = CPLGetXMLNode( psTree, "Metadata" ); + + if( psMetadata == NULL + || psMetadata->eType != CXT_Element + || !EQUAL(psMetadata->pszValue,"Metadata") ) + return NULL; + + for( psMDI = psMetadata->psChild; psMDI != NULL; + psMDI = psMDI->psNext ) + { + if( !EQUAL(psMDI->pszValue,"MDI") + || psMDI->eType != CXT_Element + || psMDI->psChild == NULL + || psMDI->psChild->psNext == NULL + || psMDI->psChild->eType != CXT_Attribute + || psMDI->psChild->psChild == NULL ) + continue; + + papszMD = + CSLSetNameValue( papszMD, + psMDI->psChild->psChild->pszValue, + psMDI->psChild->psNext->pszValue ); + } + + if( !GDALExtractRPCInfo( papszMD, &sRPC ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to reconstitute RPC transformer." ); + CSLDestroy( papszMD ); + return NULL; + } + + CSLDestroy( papszMD ); + +/* -------------------------------------------------------------------- */ +/* Get other flags. */ +/* -------------------------------------------------------------------- */ + double dfPixErrThreshold; + int bReversed; + + bReversed = atoi(CPLGetXMLValue(psTree,"Reversed","0")); + + dfPixErrThreshold = + CPLAtof(CPLGetXMLValue(psTree,"PixErrThreshold","0.25")); + + papszOptions = CSLSetNameValue( papszOptions, "RPC_HEIGHT", + CPLGetXMLValue(psTree,"HeightOffset","0")); + papszOptions = CSLSetNameValue( papszOptions, "RPC_HEIGHT_SCALE", + CPLGetXMLValue(psTree,"HeightScale","1")); + const char* pszDEMPath = CPLGetXMLValue(psTree,"DEMPath",NULL); + if (pszDEMPath != NULL) + papszOptions = CSLSetNameValue( papszOptions, "RPC_DEM", + pszDEMPath); + + const char* pszDEMInterpolation = CPLGetXMLValue(psTree,"DEMInterpolation", "bilinear"); + if (pszDEMInterpolation != NULL) + papszOptions = CSLSetNameValue( papszOptions, "RPC_DEMINTERPOLATION", + pszDEMInterpolation); + + const char* pszDEMMissingValue = CPLGetXMLValue(psTree,"DEMMissingValue", NULL); + if (pszDEMMissingValue != NULL) + papszOptions = CSLSetNameValue( papszOptions, "RPC_DEM_MISSING_VALUE", + pszDEMMissingValue); + +/* -------------------------------------------------------------------- */ +/* Generate transformation. */ +/* -------------------------------------------------------------------- */ + pResult = GDALCreateRPCTransformer( &sRPC, bReversed, dfPixErrThreshold, + papszOptions ); + + CSLDestroy( papszOptions ); + + return pResult; +} diff --git a/bazaar/plugin/gdal/alg/gdal_simplesurf.cpp b/bazaar/plugin/gdal/alg/gdal_simplesurf.cpp new file mode 100644 index 000000000..754c136ef --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdal_simplesurf.cpp @@ -0,0 +1,522 @@ +/****************************************************************************** + * Project: GDAL + * Purpose: Correlator - GDALSimpleSURF and GDALFeaturePoint classes. + * Author: Andrew Migal, migal.drew@gmail.com + * + ****************************************************************************** + * Copyright (c) 2012, Andrew Migal + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_simplesurf.h" + +CPL_CVSID("$Id"); + +/************************************************************************/ +/* ==================================================================== */ +/* GDALFeaturePoint */ +/* ==================================================================== */ +/************************************************************************/ + +GDALFeaturePoint::GDALFeaturePoint() +{ + nX = -1; + nY = -1; + nScale = -1; + nRadius = -1; + nSign = -1; + + padfDescriptor = new double[DESC_SIZE]; +} + +GDALFeaturePoint::GDALFeaturePoint(const GDALFeaturePoint& fp) +{ + nX = fp.nX; + nY = fp.nY; + nScale = fp.nScale; + nRadius = fp.nRadius; + nSign = fp.nSign; + + padfDescriptor = new double[DESC_SIZE]; + for (int i = 0; i < DESC_SIZE; i++) + padfDescriptor[i] = fp.padfDescriptor[i]; +} + +GDALFeaturePoint::GDALFeaturePoint(int nX, int nY, + int nScale, int nRadius, int nSign) +{ + this->nX = nX; + this->nY = nY; + this->nScale = nScale; + this->nRadius = nRadius; + this->nSign = nSign; + + this->padfDescriptor = new double[DESC_SIZE]; +} + +GDALFeaturePoint& GDALFeaturePoint::operator=(const GDALFeaturePoint& point) +{ + if (this != &point) + { + nX = point.nX; + nY = point.nY; + nScale = point.nScale; + nRadius = point.nRadius; + nSign = point.nSign; + + //Free memory + delete[] padfDescriptor; + + //Copy descriptor values + padfDescriptor = new double[DESC_SIZE]; + for (int i = 0; i < DESC_SIZE; i++) + padfDescriptor[i] = point.padfDescriptor[i]; + } + + return *this; +} + +int GDALFeaturePoint::GetX() { return nX; } +void GDALFeaturePoint::SetX(int nX) { this->nX = nX; } + +int GDALFeaturePoint::GetY() { return nY; } +void GDALFeaturePoint::SetY(int nY) { this->nY = nY; } + +int GDALFeaturePoint::GetScale() { return nScale; } +void GDALFeaturePoint::SetScale(int nScale) { this->nScale = nScale; } + +int GDALFeaturePoint::GetRadius() { return nRadius; } +void GDALFeaturePoint::SetRadius(int nRadius) { this->nRadius = nRadius; } + +int GDALFeaturePoint::GetSign() { return nSign; } +void GDALFeaturePoint::SetSign(int nSign) { this->nSign = nSign; } + +double& GDALFeaturePoint::operator [] (int nIndex) +{ + if (nIndex < 0 || nIndex >= DESC_SIZE) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Descriptor index is out of range"); + } + + return padfDescriptor[nIndex]; +} + +GDALFeaturePoint::~GDALFeaturePoint() { + delete[] padfDescriptor; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALSimpleSurf */ +/* ==================================================================== */ +/************************************************************************/ + +GDALSimpleSURF::GDALSimpleSURF(int nOctaveStart, int nOctaveEnd) +{ + this->octaveStart = nOctaveStart; + this->octaveEnd = nOctaveEnd; + + // Initialize Octave map with custom range + poOctMap = new GDALOctaveMap(octaveStart, octaveEnd); +} + +CPLErr GDALSimpleSURF::ConvertRGBToLuminosity( + GDALRasterBand *red, GDALRasterBand *green, GDALRasterBand *blue, + int nXSize, int nYSize, double **padfImg, int nHeight, int nWidth) +{ + const double forRed = 0.21; + const double forGreen = 0.72; + const double forBlue = 0.07; + + if (red == NULL || green == NULL || blue == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Raster bands are not specified"); + return CE_Failure; + } + + if (nXSize > red->GetXSize() || nYSize > red->GetYSize()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Red band has less size than has been requested"); + return CE_Failure; + } + + if (padfImg == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer isn't specified"); + return CE_Failure; + } + + GDALDataType eRedType = red->GetRasterDataType(); + GDALDataType eGreenType = green->GetRasterDataType(); + GDALDataType eBlueType = blue->GetRasterDataType(); + + int dataRedSize = GDALGetDataTypeSize(eRedType) / 8; + int dataGreenSize = GDALGetDataTypeSize(eGreenType) / 8; + int dataBlueSize = GDALGetDataTypeSize(eBlueType) / 8; + + void *paRedLayer = CPLMalloc(dataRedSize * nWidth * nHeight); + void *paGreenLayer = CPLMalloc(dataGreenSize * nWidth * nHeight); + void *paBlueLayer = CPLMalloc(dataBlueSize * nWidth * nHeight); + + red->RasterIO(GF_Read, 0, 0, nXSize, nYSize, paRedLayer, nWidth, nHeight, eRedType, 0, 0, NULL); + green->RasterIO(GF_Read, 0, 0, nXSize, nYSize, paGreenLayer, nWidth, nHeight, eGreenType, 0, 0, NULL); + blue->RasterIO(GF_Read, 0, 0, nXSize, nYSize, paBlueLayer, nWidth, nHeight, eBlueType, 0, 0, NULL); + + double maxValue = 255.0; + for (int row = 0; row < nHeight; row++) + for (int col = 0; col < nWidth; col++) + { + // Get RGB values + double dfRedVal = SRCVAL(paRedLayer, eRedType, + nWidth * row + col * dataRedSize); + double dfGreenVal = SRCVAL(paGreenLayer, eGreenType, + nWidth * row + col * dataGreenSize); + double dfBlueVal = SRCVAL(paBlueLayer, eBlueType, + nWidth * row + col * dataBlueSize); + // Compute luminosity value + padfImg[row][col] = ( + dfRedVal * forRed + + dfGreenVal * forGreen + + dfBlueVal * forBlue) / maxValue; + } + + CPLFree(paRedLayer); + CPLFree(paGreenLayer); + CPLFree(paBlueLayer); + + return CE_None; +} + +std::vector* +GDALSimpleSURF::ExtractFeaturePoints(GDALIntegralImage *poImg, + double dfThreshold) +{ + std::vector* poCollection = + new std::vector(); + + //Calc Hessian values for layers + poOctMap->ComputeMap(poImg); + + //Search for exremum points + for (int oct = octaveStart; oct <= octaveEnd; oct++) + { + for (int k = 0; k < GDALOctaveMap::INTERVALS - 2; k++) + { + GDALOctaveLayer *bot = poOctMap->pMap[oct - 1][k]; + GDALOctaveLayer *mid = poOctMap->pMap[oct - 1][k + 1]; + GDALOctaveLayer *top = poOctMap->pMap[oct - 1][k + 2]; + + for (int i = 0; i < mid->height; i++) + { + for (int j = 0; j < mid->width; j++) + { + if (poOctMap->PointIsExtremum(i, j, bot, mid, top, dfThreshold)) + { + GDALFeaturePoint oFP(j, i, mid->scale, + mid->radius, mid->signs[i][j]); + SetDescriptor(&oFP, poImg); + poCollection->push_back(oFP); + } + } + } + } + } + + return poCollection; +} + + +double GDALSimpleSURF::GetEuclideanDistance( + GDALFeaturePoint &firstPoint, GDALFeaturePoint &secondPoint) +{ + double sum = 0; + + for (int i = 0; i < GDALFeaturePoint::DESC_SIZE; i++) + sum += (firstPoint[i] - secondPoint[i]) * (firstPoint[i] - secondPoint[i]); + + return sqrt(sum); +} + +void GDALSimpleSURF::NormalizeDistances(std::list *poList) +{ + double max = 0; + + std::list::iterator i; + for (i = poList->begin(); i != poList->end(); i++) + if ((*i).euclideanDist > max) + max = (*i).euclideanDist; + + if (max != 0) + { + for (i = poList->begin(); i != poList->end(); i++) + (*i).euclideanDist /= max; + } +} + +void GDALSimpleSURF::SetDescriptor( + GDALFeaturePoint *poPoint, GDALIntegralImage *poImg) +{ + // Affects to the descriptor area + const int haarScale = 20; + + // Side of the Haar wavelet + int haarFilterSize = 2 * poPoint->GetScale(); + + // Length of the side of the descriptor area + int descSide = haarScale * poPoint->GetScale(); + + // Side of the quadrant in 4x4 grid + int quadStep = descSide / 4; + + // Side of the sub-quadrant in 5x5 regular grid of quadrant + int subQuadStep = quadStep / 5; + + int leftTop_row = poPoint->GetY() - (descSide / 2); + int leftTop_col = poPoint->GetX() - (descSide / 2); + + int count = 0; + + for (int r = leftTop_row; r < leftTop_row + descSide; r += quadStep) + for (int c = leftTop_col; c < leftTop_col + descSide; c += quadStep) + { + double dx = 0; + double dy = 0; + double abs_dx = 0; + double abs_dy = 0; + + for (int sub_r = r; sub_r < r + quadStep; sub_r += subQuadStep) + for (int sub_c = c; sub_c < c + quadStep; sub_c += subQuadStep) + { + // Approximate center of sub quadrant + int cntr_r = sub_r + subQuadStep / 2; + int cntr_c = sub_c + subQuadStep / 2; + + // Left top point for Haar wavelet computation + int cur_r = cntr_r - haarFilterSize / 2; + int cur_c = cntr_c - haarFilterSize / 2; + + // Gradients + double cur_dx = poImg->HaarWavelet_X(cur_r, cur_c, haarFilterSize); + double cur_dy = poImg->HaarWavelet_Y(cur_r, cur_c, haarFilterSize); + + dx += cur_dx; + dy += cur_dy; + abs_dx += fabs(cur_dx); + abs_dy += fabs(cur_dy); + } + + // Fills point's descriptor + (*poPoint)[count++] = dx; + (*poPoint)[count++] = dy; + (*poPoint)[count++] = abs_dx; + (*poPoint)[count++] = abs_dy; + } +} + +/** + * Find corresponding points (equal points in two collections). + * + * @param poMatched Resulting collection for matched points + * @param poFirstCollection Points on the first image + * @param poSecondCollection Points on the second image + * @param dfThreshold Value from 0 to 1. Threshold affects to number of + * matched points. If threshold is higher, amount of corresponding + * points is larger, and vice versa + * + * @note Typical threshold's value is 0,1. BUT it's a very approximate guide. + * It can be 0,001 or even 1. This threshold provides direct adjustment + * of point matching. + * NOTICE that if threshold is lower, matches are more robust and correct, but number of + * matched points is smaller. Therefore if algorithm performs many false + * detections and produces bad results, reduce threshold. + * Otherwise, if algorithm finds nothing, increase threshold. + * + * @return CE_None or CE_Failure if error occurs. + */ + +CPLErr GDALSimpleSURF::MatchFeaturePoints( + std::vector *poMatchPairs, + std::vector *poFirstCollect, + std::vector *poSecondCollect, + double dfThreshold) +{ +/* -------------------------------------------------------------------- */ +/* Validate parameters. */ +/* -------------------------------------------------------------------- */ + if (poMatchPairs == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Matched points colection isn't specified"); + return CE_Failure; + } + + if (poFirstCollect == NULL || poSecondCollect == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Feature point collections are not specified"); + return CE_Failure; + } + +/* ==================================================================== */ +/* Matching algorithm. */ +/* ==================================================================== */ + // Affects to false matching pruning + const double ratioThreshold = 0.8; + + int len_1 = poFirstCollect->size(); + int len_2 = poSecondCollect->size(); + + int minLength = (len_1 < len_2) ? len_1 : len_2; + + // Temporary pointers. Used to swap collections + std::vector *p_1; + std::vector *p_2; + + bool isSwap = false; + + // Assign p_1 - collection with minimal number of points + if (minLength == len_2) + { + p_1 = poSecondCollect; + p_2 = poFirstCollect; + + int tmp = 0; + tmp = len_1; + len_1 = len_2; + len_2 = tmp; + isSwap = true; + } + else + { + // Assignment 'as is' + p_1 = poFirstCollect; + p_2 = poSecondCollect; + isSwap = false; + } + + // Stores matched point indexes and + // their euclidean distances + std::list *poPairInfoList = + new std::list(); + + // Flags that points in the 2nd collection are matched or not + bool *alreadyMatched = new bool[len_2]; + for (int i = 0; i < len_2; i++) + alreadyMatched[i] = false; + + for (int i = 0; i < len_1; i++) + { + // Distance to the nearest point + double bestDist = -1; + // Index of the nearest point in p_2 collection + int bestIndex = -1; + + // Distance to the 2nd nearest point + double bestDist_2 = -1; + + // Find the nearest and 2nd nearest points + for (int j = 0; j < len_2; j++) + if (!alreadyMatched[j]) + if (p_1->at(i).GetSign() == + p_2->at(j).GetSign()) + { + // Get distance between two feature points + double curDist = GetEuclideanDistance( + p_1->at(i), p_2->at(j)); + + if (bestDist == -1) + { + bestDist = curDist; + bestIndex = j; + } + else + { + if (curDist < bestDist) + { + bestDist = curDist; + bestIndex = j; + } + } + + // Findes the 2nd nearest point + if (bestDist_2 < 0) + bestDist_2 = curDist; + else + if (curDist > bestDist && curDist < bestDist_2) + bestDist_2 = curDist; + } +/* -------------------------------------------------------------------- */ +/* False matching pruning. */ +/* If ratio bestDist to bestDist_2 greater than 0.8 => */ +/* consider as false detection. */ +/* Otherwise, add points as matched pair. */ +/*----------------------------------------------------------------------*/ + if (bestDist_2 > 0 && bestDist >= 0) + if (bestDist / bestDist_2 < ratioThreshold) + { + MatchedPointPairInfo info(i, bestIndex, bestDist); + poPairInfoList->push_back(info); + alreadyMatched[bestIndex] = true; + } + } + + +/* -------------------------------------------------------------------- */ +/* Pruning based on the provided threshold */ +/* -------------------------------------------------------------------- */ + + NormalizeDistances(poPairInfoList); + + std::list::const_iterator iter; + for (iter = poPairInfoList->begin(); iter != poPairInfoList->end(); iter++) + { + if ((*iter).euclideanDist <= dfThreshold) + { + int i_1 = (*iter).ind_1; + int i_2 = (*iter).ind_2; + + // Add copies into MatchedCollection + if(!isSwap) + { + poMatchPairs->push_back( &(p_1->at(i_1)) ); + poMatchPairs->push_back( &(p_2->at(i_2)) ); + } + else + { + poMatchPairs->push_back( &(p_2->at(i_2)) ); + poMatchPairs->push_back( &(p_1->at(i_1)) ); + } + } + } + + // Clean up + delete[] alreadyMatched; + + return CE_None; +} + +GDALSimpleSURF::~GDALSimpleSURF() +{ + delete poOctMap; +} + diff --git a/bazaar/plugin/gdal/alg/gdal_simplesurf.h b/bazaar/plugin/gdal/alg/gdal_simplesurf.h new file mode 100644 index 000000000..1428e171b --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdal_simplesurf.h @@ -0,0 +1,554 @@ +/****************************************************************************** + * Project: GDAL + * Purpose: Correlator + * Author: Andrew Migal, migal.drew@gmail.com + * + ****************************************************************************** + * Copyright (c) 2012, Andrew Migal + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/** + * @file + * @author Andrew Migal migal.drew@gmail.com + * @brief Class for searching corresponding points on images. + */ + +#ifndef GDALSIMPLESURF_H_ +#define GDALSIMPLESURF_H_ + +#include "gdal_priv.h" +#include "cpl_conv.h" +#include + +/** + * @brief Class of "feature point" in raster. Used by SURF-based algorithm. + * + * @details This point presents coordinates of distinctive pixel in image. + * In computer vision, feature points - the most "strong" and "unique" + * pixels (or areas) in picture, which can be distinguished from others. + * For more details, see FAST corner detector, SIFT, SURF and similar algorithms. + */ +class GDALFeaturePoint +{ +public: + /** + * Standard constructor. Initializes all parameters with negative numbers + * and allocates memory for descriptor. + */ + GDALFeaturePoint(); + + /** + * Copy constructor + * @param fp Copied instance of GDALFeaturePoint class + */ + GDALFeaturePoint(const GDALFeaturePoint& fp); + + /** + * Create instance of GDALFeaturePoint class + * + * @param nX X-coordinate (pixel) + * @param nY Y-coordinate (line) + * @param nScale Scale which contains this point (2, 4, 8, 16 and so on) + * @param nRadius Half of the side of descriptor area + * @param nSign Sign of Hessian determinant for this point + * + * @note This constructor normally is invoked by SURF-based algorithm, + * which provides all necessary parameters. + */ + GDALFeaturePoint(int nX, int nY, int nScale, int nRadius, int nSign); + virtual ~GDALFeaturePoint(); + + GDALFeaturePoint& operator=(const GDALFeaturePoint& point); + + /** + * Provide access to point's descriptor. + * + * @param nIndex Position of descriptor's value. + * nIndex should be within range from 0 to DESC_SIZE (in current version - 64) + * + * @return Reference to value of descriptor in 'nIndex' position. + * If index is out of range then behaviour is undefined. + */ + double& operator[](int nIndex); + + // Descriptor length + static const int DESC_SIZE = 64; + + /** + * Fetch X-coordinate (pixel) of point + * + * @return X-coordinate in pixels + */ + int GetX(); + + /** + * Set X coordinate of point + * + * @param nX X coordinate in pixels + */ + void SetX(int nX); + + /** + * Fetch Y-coordinate (line) of point. + * + * @return Y-coordinate in pixels. + */ + int GetY(); + + /** + * Set Y coordinate of point. + * + * @param nY Y coordinate in pixels. + */ + void SetY(int nY); + + /** + * Fetch scale of point. + * + * @return Scale for this point. + */ + int GetScale(); + + /** + * Set scale of point. + * + * @param nScale Scale for this point. + */ + void SetScale(int nScale); + + /** + * Fetch radius of point. + * + * @return Radius for this point. + */ + int GetRadius(); + + /** + * Set radius of point. + * + * @param nRadius Radius for this point. + */ + void SetRadius(int nRadius); + + /** + * Fetch sign of Hessian determinant of point. + * + * @return Sign for this point. + */ + int GetSign(); + + /** + * Set sign of point. + * + * @param nSign Sign of Hessian determinant for this point. + */ + void SetSign(int nSign); + +private: + // Coordinates of point in image + int nX; + int nY; + // -------------------- + int nScale; + int nRadius; + int nSign; + // Descriptor array + double *padfDescriptor; +}; + +/** + * @author Andrew Migal migal.drew@gmail.com + * @brief Integral image class (summed area table). + * @details Integral image is a table for fast computing the sum of + * values in rectangular subarea. In more detail, for 2-dimensional array + * of numbers this class provides capabilty to get sum of values in + * rectangular arbitrary area with any size in constant time. + * Integral image is constructed from grayscale picture. + */ +class GDALIntegralImage +{ +public: + GDALIntegralImage(); + virtual ~GDALIntegralImage(); + + /** + * Compute integral image for specified array. Result is stored internally. + * + * @param padfImg Pointer to 2-dimensional array of values + * @param nHeight Number of rows in array + * @param nWidth Number of columns in array + */ + void Initialize(const double **padfImg, int nHeight, int nWidth); + + /** + * Fetch value of specified position in integral image. + * + * @param nRow Row of this position + * @param nCol Column of this position + * + * @return Value in specified position or zero if parameters are out of range. + */ + double GetValue(int nRow, int nCol); + + /** + * Get sum of values in specified rectangular grid. Rectangle is constructed + * from left top point. + * + * @param nRow Row of left top point of rectangle + * @param nCol Column of left top point of rectangle + * @param nWidth Width of rectangular area (number of columns) + * @param nHeight Heigth of rectangular area (number of rows) + * + * @return Sum of values in specified grid. + */ + double GetRectangleSum(int nRow, int nCol, int nWidth, int nHeight); + + /** + * Get value of horizontal Haar wavelet in specified square grid. + * + * @param nRow Row of left top point of square + * @param nCol Column of left top point of square + * @param nSize Side of the square + * + * @return Value of horizontal Haar wavelet in specified square grid. + */ + double HaarWavelet_X(int nRow, int nCol, int nSize); + + /** + * Get value of vertical Haar wavelet in specified square grid. + * + * @param nRow Row of left top point of square + * @param nCol Column of left top point of square + * @param nSize Side of the square + * + * @return Value of vertical Haar wavelet in specified square grid. + */ + double HaarWavelet_Y(int nRow, int nCol, int nSize); + + /** + * Fetch height of integral image. + * + * @return Height of integral image (number of rows). + */ + int GetHeight(); + + /** + * Fetch width of integral image. + * + * @return Width of integral image (number of columns). + */ + int GetWidth(); + +private: + double **pMatrix; + int nWidth; + int nHeight; +}; + +/** + * @author Andrew Migal migal.drew@gmail.com + * @brief Class for computation and storage of Hessian values in SURF-based algorithm. + * + * @details SURF-based algorithm normally uses this class for searching + * feature points on raster images. Class also contains traces of Hessian matrices + * to provide fast computations. + */ +class GDALOctaveLayer +{ +public: + GDALOctaveLayer(); + + /** + * Create instance with provided parameters. + * + * @param nOctave Number of octave which contains this layer + * @param nInterval Number of position in octave + * + * @note Normally constructor is invoked only by SURF-based algorithm. + */ + GDALOctaveLayer(int nOctave, int nInterval); + virtual ~GDALOctaveLayer(); + + /** + * Perform calculation of Hessian determinats and their signs + * for specified integral image. Result is stored internally. + * + * @param poImg Integral image object, which provides all necessary + * data for computation + * + * @note Normally method is invoked only by SURF-based algorithm. + */ + void ComputeLayer(GDALIntegralImage *poImg); + + /** + * Octave which contains this layer (1,2,3...) + */ + int octaveNum; + /** + * Length of the side of filter + */ + int filterSize; + /** + * Length of the border + */ + int radius; + /** + * Scale for this layer + */ + int scale; + /** + * Image width in pixels + */ + int width; + /** + * Image height in pixels + */ + int height; + /** + * Hessian values for image pixels + */ + double **detHessians; + /** + * Hessian signs for speeded matching + */ + int **signs; +}; + +/** + * @author Andrew Migal migal.drew@gmail.com + * @brief Class for handling octave layers in SURF-based algorithm. + * @details Class contains OctaveLayers and provides capability to construct octave space and distinguish + * feature points. Normally this class is used only by SURF-based algorithm. + */ +class GDALOctaveMap +{ +public: + /** + * Create octave space. Octave numbers are start with one. (1, 2, 3, 4, ... ) + * + * @param nOctaveStart Number of bottom octave + * @param nOctaveEnd Number of top octave. Should be equal or greater than OctaveStart + */ + GDALOctaveMap(int nOctaveStart, int nOctaveEnd); + virtual ~GDALOctaveMap(); + + /** + * Calculate Hessian values for octave space + * (for all stored octave layers) using specified integral image + * @param poImg Integral image instance which provides necessary data + * @see GDALOctaveLayer + */ + void ComputeMap(GDALIntegralImage *poImg); + + /** + * Method makes decision that specified point + * in middle octave layer is maximum among all points + * from 3x3x3 neighbourhood (surrounding points in + * bottom, middle and top layers). Provided layers should be from the same octave's interval. + * Detects feature points. + * + * @param row Row of point, which is candidate to be feature point + * @param col Column of point, which is candidate to be feature point + * @param bot Bottom octave layer + * @param mid Middle octave layer + * @param top Top octave layer + * @param threshold Threshold for feature point recognition. Detected feature point + * will have Hessian value greater than this provided threshold. + * + * @return TRUE if candidate was evaluated as feature point or FALSE otherwise. + */ + bool PointIsExtremum(int row, int col, GDALOctaveLayer *bot, + GDALOctaveLayer *mid, GDALOctaveLayer *top, double threshold); + + /** + * 2-dimensional array of octave layers + */ + GDALOctaveLayer ***pMap; + + /** + * Value for constructing internal octave space + */ + static const int INTERVALS = 4; + + /** + * Number of bottom octave + */ + int octaveStart; + + /** + * Number of top octave. Should be equal or greater than OctaveStart + */ + int octaveEnd; +}; + +/** + * @author Andrew Migal migal.drew@gmail.com + * @brief Class for searching corresponding points on images. + * @details Provides capability for detection feature points + * and finding equal points on different images. + * Class implements simplified version of SURF algorithm (Speeded Up Robust Features). + * As original, this realization is scale invariant, but sensitive to rotation. + * Images should have similar rotation angles (maximum difference is up to 10-15 degrees), + * otherwise algorithm produces incorrect and very unstable results. + */ + +class GDALSimpleSURF +{ +private: + /** + * Class stores indexes of pair of point + * and distance between them. + */ + class MatchedPointPairInfo + { + public: + MatchedPointPairInfo(int nInd_1, int nInd_2, double dfDist) + { + ind_1 = nInd_1; + ind_2 = nInd_2; + euclideanDist = dfDist; + } + + int ind_1; + int ind_2; + double euclideanDist; + }; + +public: + /** + * Prepare class according to specified parameters. Octave numbers affects + * to amount of detected points and their robustness. + * Range between bottom and top octaves also affects to required time of detection points + * (if range is large, algorithm should perform more operations). + * @param nOctaveStart Number of bottom octave. Octave numbers starts with one + * @param nOctaveEnd Number of top octave. Should be equal or greater than OctaveStart + * + * @note + * Every octave finds points with specific size. For small images + * use small octave numbers, for high resolution - large. + * For 1024x1024 images it's normal to use any octave numbers from range 1-6. + * (for example, octave start - 1, octave end - 3, or octave start - 2, octave end - 2.) + * For larger images, try 1-10 range or even higher. + * Pay attention that number of detected point decreases quickly per octave + * for particular image. Algorithm finds more points in case of small octave numbers. + * If method detects nothing, reduce bottom bound of octave range. + * + * NOTICE that every octave requires time to compute. Use a little range + * or only one octave if execution time is significant. + */ + GDALSimpleSURF(int nOctaveStart, int nOctaveEnd); + virtual ~GDALSimpleSURF(); + + /** + * Convert image with RGB channels to grayscale using "luminosity" method. + * Result is used in SURF-based algorithm, but may be used anywhere where + * grayscale images with nice contrast are required. + * + * @param red Image's red channel + * @param green Image's green channel + * @param blue Image's blue channel + * @param nXSize Width of initial image + * @param nYSize Height of initial image + * @param padfImg Array for resulting grayscale image + * @param nHeight Height of resulting image + * @param nWidth Width of resulting image + * + * @return CE_None or CE_Failure if error occurs. + */ + static CPLErr ConvertRGBToLuminosity( + GDALRasterBand *red, + GDALRasterBand *green, + GDALRasterBand *blue, + int nXSize, int nYSize, + double **padfImg, int nHeight, int nWidth); + + /** + * Find feature points using specified integral image. + * + * @param poImg Integral image to be used + * @param dfThreshold Threshold for feature point recognition. Detected feature point + * will have Hessian value greater than this provided threshold. + * + * @note Typical threshold's value is 0,001. But this value + * can be various in each case and depends on image's nature. + * For example, value can be 0.002 or 0.005. + * Fill free to experiment with it. + * If threshold is high, than number of detected feature points is small, + * and vice versa. + */ + std::vector* + ExtractFeaturePoints(GDALIntegralImage *poImg, double dfThreshold); + + /** + * Find corresponding points (equal points in two collections). + * + * @param poMatchPairs Resulting collection for matched points + * @param poSecondCollect Points on the first image + * @param poSecondCollect Points on the second image + * @param dfThreshold Value from 0 to 1. Threshold affects to number of + * matched points. If threshold is lower, amount of corresponding + * points is larger, and vice versa + * + * @return CE_None or CE_Failure if error occurs. + */ + static CPLErr MatchFeaturePoints( + std::vector *poMatchPairs, + std::vector *poFirstCollect, + std::vector *poSecondCollect, + double dfThreshold); + +private: + /** + * Compute euclidean distance between descriptors of two feature points. + * It's used in comparison and matching of points. + * + * @param firstPoint First feature point to be compared + * @param secondPoint Second feature point to be compared + * + * @return Euclidean distance between descriptors. + */ + static double GetEuclideanDistance( + GDALFeaturePoint &firstPoint, GDALFeaturePoint &secondPoint); + + /** + * Set provided distance values to range from 0 to 1. + * + * @param poList List of distances to be normalized + */ + static void NormalizeDistances(std::list *poList); + + /** + * Compute descriptor for specified feature point. + * + * @param poPoint Feature point instance + * @param poImg image where feature point was found + */ + void SetDescriptor(GDALFeaturePoint *poPoint, GDALIntegralImage *poImg); + + +private: + int octaveStart; + int octaveEnd; + GDALOctaveMap *poOctMap; +}; + + +#endif /* GDALSIMPLESURF_H_ */ diff --git a/bazaar/plugin/gdal/alg/gdal_tps.cpp b/bazaar/plugin/gdal/alg/gdal_tps.cpp new file mode 100644 index 000000000..a7274aa18 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdal_tps.cpp @@ -0,0 +1,403 @@ +/****************************************************************************** + * $Id: gdal_tps.cpp 29207 2015-05-18 17:23:45Z mloskot $ + * + * Project: High Performance Image Reprojector + * Purpose: Thin Plate Spline transformer (GDAL wrapper portion) + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "thinplatespline.h" +#include "gdal_alg.h" +#include "gdal_alg_priv.h" +#include "gdal_priv.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_atomic_ops.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: gdal_tps.cpp 29207 2015-05-18 17:23:45Z mloskot $"); + +CPL_C_START +CPLXMLNode *GDALSerializeTPSTransformer( void *pTransformArg ); +void *GDALDeserializeTPSTransformer( CPLXMLNode *psTree ); +CPL_C_END + +typedef struct +{ + GDALTransformerInfo sTI; + + VizGeorefSpline2D *poForward; + VizGeorefSpline2D *poReverse; + int bForwardSolved; + int bReverseSolved; + + int bReversed; + + int nGCPCount; + GDAL_GCP *pasGCPList; + + volatile int nRefCount; + +} TPSTransformInfo; + +/************************************************************************/ +/* GDALCreateSimilarTPSTransformer() */ +/************************************************************************/ + +static +void* GDALCreateSimilarTPSTransformer( void *hTransformArg, double dfRatioX, double dfRatioY ) +{ + VALIDATE_POINTER1( hTransformArg, "GDALCreateSimilarTPSTransformer", NULL ); + + TPSTransformInfo *psInfo = (TPSTransformInfo *) hTransformArg; + + if( dfRatioX == 1.0 && dfRatioY == 1.0 ) + { + /* We can just use a ref count, since using the source transformation */ + /* is thread-safe */ + CPLAtomicInc(&(psInfo->nRefCount)); + } + else + { + GDAL_GCP *pasGCPList = GDALDuplicateGCPs( psInfo->nGCPCount, + psInfo->pasGCPList ); + for(int i=0;inGCPCount;i++) + { + pasGCPList[i].dfGCPPixel /= dfRatioX; + pasGCPList[i].dfGCPLine /= dfRatioY; + } + psInfo = (TPSTransformInfo *) GDALCreateTPSTransformer( psInfo->nGCPCount, pasGCPList, + psInfo->bReversed ); + GDALDeinitGCPs( psInfo->nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + return psInfo; +} + +/************************************************************************/ +/* GDALCreateTPSTransformer() */ +/************************************************************************/ + +/** + * Create Thin Plate Spline transformer from GCPs. + * + * The thin plate spline transformer produces exact transformation + * at all control points and smoothly varying transformations between + * control points with greatest influence from local control points. + * It is suitable for for many applications not well modelled by polynomial + * transformations. + * + * Creating the TPS transformer involves solving systems of linear equations + * related to the number of control points involved. This solution is + * computed within this function call. It can be quite an expensive operation + * for large numbers of GCPs. For instance, for reference, it takes on the + * order of 10s for 400 GCPs on a 2GHz Athlon processor. + * + * TPS Transformers are serializable. + * + * The GDAL Thin Plate Spline transformer is based on code provided by + * Gilad Ronnen on behalf of VIZRT Inc (http://www.visrt.com). Incorporation + * of the algorithm into GDAL was supported by the Centro di Ecologia Alpina + * (http://www.cealp.it). + * + * @param nGCPCount the number of GCPs in pasGCPList. + * @param pasGCPList an array of GCPs to be used as input. + * @param bReversed set it to TRUE to compute the reversed transformation. + * + * @return the transform argument or NULL if creation fails. + */ + +void *GDALCreateTPSTransformer( int nGCPCount, const GDAL_GCP *pasGCPList, + int bReversed ) +{ + return GDALCreateTPSTransformerInt(nGCPCount, pasGCPList, bReversed, NULL); +} + +static void GDALTPSComputeForwardInThread(void* pData) +{ + TPSTransformInfo *psInfo = (TPSTransformInfo *)pData; + psInfo->bForwardSolved = psInfo->poForward->solve() != 0; +} + +void *GDALCreateTPSTransformerInt( int nGCPCount, const GDAL_GCP *pasGCPList, + int bReversed, char** papszOptions ) + +{ + TPSTransformInfo *psInfo; + int iGCP; + +/* -------------------------------------------------------------------- */ +/* Allocate transform info. */ +/* -------------------------------------------------------------------- */ + psInfo = (TPSTransformInfo *) CPLCalloc(sizeof(TPSTransformInfo),1); + + psInfo->pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPList ); + psInfo->nGCPCount = nGCPCount; + + psInfo->bReversed = bReversed; + psInfo->poForward = new VizGeorefSpline2D( 2 ); + psInfo->poReverse = new VizGeorefSpline2D( 2 ); + + memcpy( psInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE) ); + psInfo->sTI.pszClassName = "GDALTPSTransformer"; + psInfo->sTI.pfnTransform = GDALTPSTransform; + psInfo->sTI.pfnCleanup = GDALDestroyTPSTransformer; + psInfo->sTI.pfnSerialize = GDALSerializeTPSTransformer; + psInfo->sTI.pfnCreateSimilar = GDALCreateSimilarTPSTransformer; + +/* -------------------------------------------------------------------- */ +/* Attach all the points to the transformation. */ +/* -------------------------------------------------------------------- */ + for( iGCP = 0; iGCP < nGCPCount; iGCP++ ) + { + double afPL[2], afXY[2]; + + afPL[0] = pasGCPList[iGCP].dfGCPPixel; + afPL[1] = pasGCPList[iGCP].dfGCPLine; + afXY[0] = pasGCPList[iGCP].dfGCPX; + afXY[1] = pasGCPList[iGCP].dfGCPY; + + if( bReversed ) + { + psInfo->poReverse->add_point( afPL[0], afPL[1], afXY ); + psInfo->poForward->add_point( afXY[0], afXY[1], afPL ); + } + else + { + psInfo->poForward->add_point( afPL[0], afPL[1], afXY ); + psInfo->poReverse->add_point( afXY[0], afXY[1], afPL ); + } + } + + psInfo->nRefCount = 1; + + int nThreads = 1; + if( nGCPCount > 100 ) + { + const char* pszWarpThreads = CSLFetchNameValue(papszOptions, "NUM_THREADS"); + if (pszWarpThreads == NULL) + pszWarpThreads = CPLGetConfigOption("GDAL_NUM_THREADS", "1"); + if (EQUAL(pszWarpThreads, "ALL_CPUS")) + nThreads = CPLGetNumCPUs(); + else + nThreads = atoi(pszWarpThreads); + } + + if( nThreads > 1 ) + { + /* Compute direct and reverse transforms in parallel */ + CPLJoinableThread* hThread = CPLCreateJoinableThread(GDALTPSComputeForwardInThread, psInfo); + psInfo->bReverseSolved = psInfo->poReverse->solve() != 0; + if( hThread != NULL ) + CPLJoinThread(hThread); + else + psInfo->bForwardSolved = psInfo->poForward->solve() != 0; + } + else + { + psInfo->bForwardSolved = psInfo->poForward->solve() != 0; + psInfo->bReverseSolved = psInfo->poReverse->solve() != 0; + } + + if( !psInfo->bForwardSolved || !psInfo->bReverseSolved ) + { + GDALDestroyTPSTransformer(psInfo); + return NULL; + } + + return psInfo; +} + +/************************************************************************/ +/* GDALDestroyTPSTransformer() */ +/************************************************************************/ + +/** + * Destroy TPS transformer. + * + * This function is used to destroy information about a GCP based + * polynomial transformation created with GDALCreateTPSTransformer(). + * + * @param pTransformArg the transform arg previously returned by + * GDALCreateTPSTransformer(). + */ + +void GDALDestroyTPSTransformer( void *pTransformArg ) + +{ + if( pTransformArg == NULL ) + return; + + TPSTransformInfo *psInfo = (TPSTransformInfo *) pTransformArg; + + if( CPLAtomicDec(&(psInfo->nRefCount)) == 0 ) + { + delete psInfo->poForward; + delete psInfo->poReverse; + + GDALDeinitGCPs( psInfo->nGCPCount, psInfo->pasGCPList ); + CPLFree( psInfo->pasGCPList ); + + CPLFree( pTransformArg ); + } +} + +/************************************************************************/ +/* GDALTPSTransform() */ +/************************************************************************/ + +/** + * Transforms point based on GCP derived polynomial model. + * + * This function matches the GDALTransformerFunc signature, and can be + * used to transform one or more points from pixel/line coordinates to + * georeferenced coordinates (SrcToDst) or vice versa (DstToSrc). + * + * @param pTransformArg return value from GDALCreateTPSTransformer(). + * @param bDstToSrc TRUE if transformation is from the destination + * (georeferenced) coordinates to pixel/line or FALSE when transforming + * from pixel/line to georeferenced coordinates. + * @param nPointCount the number of values in the x, y and z arrays. + * @param x array containing the X values to be transformed. + * @param y array containing the Y values to be transformed. + * @param z array containing the Z values to be transformed. + * @param panSuccess array in which a flag indicating success (TRUE) or + * failure (FALSE) of the transformation are placed. + * + * @return TRUE. + */ + +int GDALTPSTransform( void *pTransformArg, int bDstToSrc, + int nPointCount, + double *x, double *y, + CPL_UNUSED double *z, + int *panSuccess ) +{ + VALIDATE_POINTER1( pTransformArg, "GDALTPSTransform", 0 ); + + int i; + TPSTransformInfo *psInfo = (TPSTransformInfo *) pTransformArg; + + for( i = 0; i < nPointCount; i++ ) + { + double xy_out[2]; + + if( bDstToSrc ) + { + psInfo->poReverse->get_point( x[i], y[i], xy_out ); + x[i] = xy_out[0]; + y[i] = xy_out[1]; + } + else + { + psInfo->poForward->get_point( x[i], y[i], xy_out ); + x[i] = xy_out[0]; + y[i] = xy_out[1]; + } + panSuccess[i] = TRUE; + } + + return TRUE; +} + +/************************************************************************/ +/* GDALSerializeTPSTransformer() */ +/************************************************************************/ + +CPLXMLNode *GDALSerializeTPSTransformer( void *pTransformArg ) + +{ + VALIDATE_POINTER1( pTransformArg, "GDALSerializeTPSTransformer", NULL ); + + CPLXMLNode *psTree; + TPSTransformInfo *psInfo = static_cast(pTransformArg); + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "TPSTransformer" ); + +/* -------------------------------------------------------------------- */ +/* Serialize bReversed. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( + psTree, "Reversed", + CPLString().Printf( "%d", psInfo->bReversed ) ); + +/* -------------------------------------------------------------------- */ +/* Attach GCP List. */ +/* -------------------------------------------------------------------- */ + if( psInfo->nGCPCount > 0 ) + { + GDALSerializeGCPListToXML( psTree, + psInfo->pasGCPList, + psInfo->nGCPCount, + NULL ); + } + + return psTree; +} + +/************************************************************************/ +/* GDALDeserializeTPSTransformer() */ +/************************************************************************/ + +void *GDALDeserializeTPSTransformer( CPLXMLNode *psTree ) + +{ + GDAL_GCP *pasGCPList = 0; + int nGCPCount = 0; + void *pResult; + int bReversed; + + /* -------------------------------------------------------------------- */ + /* Check for GCPs. */ + /* -------------------------------------------------------------------- */ + CPLXMLNode *psGCPList = CPLGetXMLNode( psTree, "GCPList" ); + + if( psGCPList != NULL ) + { + GDALDeserializeGCPListFromXML( psGCPList, + &pasGCPList, + &nGCPCount, + NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Get other flags. */ +/* -------------------------------------------------------------------- */ + bReversed = atoi(CPLGetXMLValue(psTree,"Reversed","0")); + +/* -------------------------------------------------------------------- */ +/* Generate transformation. */ +/* -------------------------------------------------------------------- */ + pResult = GDALCreateTPSTransformer( nGCPCount, pasGCPList, bReversed ); + +/* -------------------------------------------------------------------- */ +/* Cleanup GCP copy. */ +/* -------------------------------------------------------------------- */ + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + + return pResult; +} diff --git a/bazaar/plugin/gdal/alg/gdalchecksum.cpp b/bazaar/plugin/gdal/alg/gdalchecksum.cpp new file mode 100644 index 000000000..b3fec608e --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalchecksum.cpp @@ -0,0 +1,172 @@ +/****************************************************************************** + * $Id: gdalchecksum.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL + * Purpose: Compute simple checksum for a region of image data. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2007-2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_alg.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: gdalchecksum.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* GDALChecksumImage() */ +/************************************************************************/ + +/** + * Compute checksum for image region. + * + * Computes a 16bit (0-65535) checksum from a region of raster data on a GDAL + * supported band. Floating point data is converted to 32bit integer + * so decimal portions of such raster data will not affect the checksum. + * Real and Imaginary components of complex bands influence the result. + * + * @param hBand the raster band to read from. + * @param nXOff pixel offset of window to read. + * @param nYOff line offset of window to read. + * @param nXSize pixel size of window to read. + * @param nYSize line size of window to read. + * + * @return Checksum value. + */ + +int CPL_STDCALL +GDALChecksumImage( GDALRasterBandH hBand, + int nXOff, int nYOff, int nXSize, int nYSize ) + +{ + VALIDATE_POINTER1( hBand, "GDALChecksumImage", 0 ); + + const static int anPrimes[11] = + { 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43 }; + + int iLine, i, nChecksum = 0, iPrime = 0, nCount; + GDALDataType eDataType = GDALGetRasterDataType( hBand ); + int bComplex = GDALDataTypeIsComplex( eDataType ); + + if (eDataType == GDT_Float32 || eDataType == GDT_Float64 || + eDataType == GDT_CFloat32 || eDataType == GDT_CFloat64) + { + double* padfLineData; + GDALDataType eDstDataType = (bComplex) ? GDT_CFloat64 : GDT_Float64; + + padfLineData = (double *) VSIMalloc2(nXSize, sizeof(double) * 2); + if (padfLineData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "VSIMalloc2(): Out of memory in GDALChecksumImage. " + "Checksum value couldn't be computed\n"); + return 0; + } + + for( iLine = nYOff; iLine < nYOff + nYSize; iLine++ ) + { + if (GDALRasterIO( hBand, GF_Read, nXOff, iLine, nXSize, 1, + padfLineData, nXSize, 1, eDstDataType, 0, 0 ) != CE_None) + { + CPLError( CE_Failure, CPLE_FileIO, + "Checksum value couldn't be computed due to I/O read error.\n"); + break; + } + nCount = (bComplex) ? nXSize * 2 : nXSize; + + for( i = 0; i < nCount; i++ ) + { + double dfVal = padfLineData[i]; + int nVal; + if (CPLIsNan(dfVal) || CPLIsInf(dfVal)) + { + /* Most compilers seem to cast NaN or Inf to 0x80000000. */ + /* but VC7 is an exception. So we force the result */ + /* of such a cast */ + nVal = 0x80000000; + } + else + { + /* Standard behaviour of GDALCopyWords when converting */ + /* from floating point to Int32 */ + dfVal += 0.5; + + if( dfVal < -2147483647.0 ) + nVal = -2147483647; + else if( dfVal > 2147483647 ) + nVal = 2147483647; + else + nVal = (GInt32) floor(dfVal); + } + + nChecksum += (nVal % anPrimes[iPrime++]); + if( iPrime > 10 ) + iPrime = 0; + + nChecksum &= 0xffff; + } + } + + CPLFree(padfLineData); + } + else + { + int *panLineData; + GDALDataType eDstDataType = (bComplex) ? GDT_CInt32 : GDT_Int32; + + panLineData = (GInt32 *) VSIMalloc2(nXSize, sizeof(GInt32) * 2); + if (panLineData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "VSIMalloc2(): Out of memory in GDALChecksumImage. " + "Checksum value couldn't be computed\n"); + return 0; + } + + for( iLine = nYOff; iLine < nYOff + nYSize; iLine++ ) + { + if (GDALRasterIO( hBand, GF_Read, nXOff, iLine, nXSize, 1, + panLineData, nXSize, 1, eDstDataType, 0, 0 ) != CE_None) + { + CPLError( CE_Failure, CPLE_FileIO, + "Checksum value couldn't be computed due to I/O read error.\n"); + break; + } + nCount = (bComplex) ? nXSize * 2 : nXSize; + + for( i = 0; i < nCount; i++ ) + { + nChecksum += (panLineData[i] % anPrimes[iPrime++]); + if( iPrime > 10 ) + iPrime = 0; + + nChecksum &= 0xffff; + } + } + + CPLFree( panLineData ); + } + + return nChecksum; +} + diff --git a/bazaar/plugin/gdal/alg/gdalcutline.cpp b/bazaar/plugin/gdal/alg/gdalcutline.cpp new file mode 100644 index 000000000..36ff99e9a --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalcutline.cpp @@ -0,0 +1,418 @@ +/****************************************************************************** + * $Id: gdalcutline.cpp 28223 2014-12-26 11:28:03Z goatbar $ + * + * Project: High Performance Image Reprojector + * Purpose: Implement cutline/blend mask generator. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdalwarper.h" +#include "gdal_alg.h" +#include "ogr_api.h" +#include "ogr_geos.h" +#include "ogr_geometry.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: gdalcutline.cpp 28223 2014-12-26 11:28:03Z goatbar $"); + +/************************************************************************/ +/* BlendMaskGenerator() */ +/************************************************************************/ + +static CPLErr +BlendMaskGenerator( +#ifndef HAVE_GEOS + CPL_UNUSED int nXOff, CPL_UNUSED int nYOff, + CPL_UNUSED int nXSize, CPL_UNUSED int nYSize, + CPL_UNUSED GByte *pabyPolyMask, + CPL_UNUSED float *pafValidityMask, + CPL_UNUSED OGRGeometryH hPolygon, + CPL_UNUSED double dfBlendDist +#else + int nXOff, int nYOff, int nXSize, int nYSize, + GByte *pabyPolyMask, float *pafValidityMask, + OGRGeometryH hPolygon, double dfBlendDist +#endif +) +{ +#ifndef HAVE_GEOS + CPLError( CE_Failure, CPLE_AppDefined, + "Blend distance support not available without the GEOS library."); + return CE_Failure; + +#else /* HAVE_GEOS */ + +/* -------------------------------------------------------------------- */ +/* Convert the polygon into a collection of lines so that we */ +/* measure distance from the edge even on the inside. */ +/* -------------------------------------------------------------------- */ + OGRGeometry *poLines + = OGRGeometryFactory::forceToMultiLineString( + ((OGRGeometry *) hPolygon)->clone() ); + +/* -------------------------------------------------------------------- */ +/* Prepare a clipping polygon a bit bigger than the area of */ +/* interest in the hopes of simplifying the cutline down to */ +/* stuff that will be relavent for this area of interest. */ +/* -------------------------------------------------------------------- */ + CPLString osClipRectWKT; + + osClipRectWKT.Printf( "POLYGON((%g %g,%g %g,%g %g,%g %g,%g %g))", + nXOff - (dfBlendDist+1), + nYOff - (dfBlendDist+1), + nXOff + nXSize + (dfBlendDist+1), + nYOff - (dfBlendDist+1), + nXOff + nXSize + (dfBlendDist+1), + nYOff + nYSize + (dfBlendDist+1), + nXOff - (dfBlendDist+1), + nYOff + nYSize + (dfBlendDist+1), + nXOff - (dfBlendDist+1), + nYOff - (dfBlendDist+1) ); + + OGRPolygon *poClipRect = NULL; + char *pszWKT = (char *) osClipRectWKT.c_str(); + + OGRGeometryFactory::createFromWkt( &pszWKT, NULL, + (OGRGeometry**) (&poClipRect) ); + + if( poClipRect ) + { + + /***** if it doesnt intersect the polym zero the mask and return *****/ + + if ( ! ((OGRGeometry *) hPolygon)->Intersects( poClipRect ) ) + { + + memset( pafValidityMask, 0, sizeof(float) * nXSize * nYSize ); + + delete poLines; + delete poClipRect; + + return CE_None; + } + + /***** if it doesnt intersect the line at all just return *****/ + + else if ( ! ((OGRGeometry *) poLines)->Intersects( poClipRect ) ) + { + delete poLines; + delete poClipRect; + + return CE_None; + } + + OGRGeometry *poClippedLines = + poLines->Intersection( poClipRect ); + delete poLines; + poLines = poClippedLines; + delete poClipRect; + } + +/* -------------------------------------------------------------------- */ +/* Convert our polygon into GEOS format, and compute an */ +/* envelope to accelerate later distance operations. */ +/* -------------------------------------------------------------------- */ + OGREnvelope sEnvelope; + int iXMin, iYMin, iXMax, iYMax; + GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext(); + GEOSGeom poGEOSPoly; + + poGEOSPoly = poLines->exportToGEOS(hGEOSCtxt); + OGR_G_GetEnvelope( hPolygon, &sEnvelope ); + + delete poLines; + + /***** this check was already done in the calling *****/ + /***** function and should never be true *****/ + + /*if( sEnvelope.MinY - dfBlendDist > nYOff+nYSize + || sEnvelope.MaxY + dfBlendDist < nYOff + || sEnvelope.MinX - dfBlendDist > nXOff+nXSize + || sEnvelope.MaxX + dfBlendDist < nXOff ) + return CE_None; + */ + + + iXMin = MAX(0,(int) floor(sEnvelope.MinX - dfBlendDist - nXOff)); + iXMax = MIN(nXSize, (int) ceil(sEnvelope.MaxX + dfBlendDist - nXOff)); + iYMin = MAX(0,(int) floor(sEnvelope.MinY - dfBlendDist - nYOff)); + iYMax = MIN(nYSize, (int) ceil(sEnvelope.MaxY + dfBlendDist - nYOff)); + +/* -------------------------------------------------------------------- */ +/* Loop over potential area within blend line distance, */ +/* processing each pixel. */ +/* -------------------------------------------------------------------- */ + int iY, iX; + double dfLastDist; + + for( iY = 0; iY < nYSize; iY++ ) + { + dfLastDist = 0.0; + + for( iX = 0; iX < nXSize; iX++ ) + { + if( iX < iXMin || iX >= iXMax + || iY < iYMin || iY > iYMax + || dfLastDist > dfBlendDist + 1.5 ) + { + if( pabyPolyMask[iX + iY * nXSize] == 0 ) + pafValidityMask[iX + iY * nXSize] = 0.0; + + dfLastDist -= 1.0; + continue; + } + + double dfDist, dfRatio; + CPLString osPointWKT; + GEOSGeom poGEOSPoint; + + osPointWKT.Printf( "POINT(%d.5 %d.5)", iX + nXOff, iY + nYOff ); + poGEOSPoint = GEOSGeomFromWKT_r( hGEOSCtxt, osPointWKT ); + + GEOSDistance_r( hGEOSCtxt, poGEOSPoly, poGEOSPoint, &dfDist ); + GEOSGeom_destroy_r( hGEOSCtxt, poGEOSPoint ); + + dfLastDist = dfDist; + + if( dfDist > dfBlendDist ) + { + if( pabyPolyMask[iX + iY * nXSize] == 0 ) + pafValidityMask[iX + iY * nXSize] = 0.0; + + continue; + } + + if( pabyPolyMask[iX + iY * nXSize] == 0 ) + { + /* outside */ + dfRatio = 0.5 - (dfDist / dfBlendDist) * 0.5; + } + else + { + /* inside */ + dfRatio = 0.5 + (dfDist / dfBlendDist) * 0.5; + } + + pafValidityMask[iX + iY * nXSize] *= (float)dfRatio; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + GEOSGeom_destroy_r( hGEOSCtxt, poGEOSPoly ); + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + + return CE_None; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* CutlineTransformer() */ +/* */ +/* A simple transformer for the cutline that just offsets */ +/* relative to the current chunk. */ +/************************************************************************/ + +static int CutlineTransformer( void *pTransformArg, + int bDstToSrc, + int nPointCount, + double *x, + double *y, + CPL_UNUSED double *z, + CPL_UNUSED int *panSuccess ) +{ + int nXOff = ((int *) pTransformArg)[0]; + int nYOff = ((int *) pTransformArg)[1]; + int i; + + if( bDstToSrc ) + { + nXOff *= -1; + nYOff *= -1; + } + + for( i = 0; i < nPointCount; i++ ) + { + x[i] -= nXOff; + y[i] -= nYOff; + } + + return TRUE; +} + + +/************************************************************************/ +/* GDALWarpCutlineMasker() */ +/* */ +/* This function will generate a source mask based on a */ +/* provided cutline, and optional blend distance. */ +/************************************************************************/ + +CPLErr +GDALWarpCutlineMasker( void *pMaskFuncArg, + CPL_UNUSED int nBandCount, + CPL_UNUSED GDALDataType eType, + int nXOff, int nYOff, int nXSize, int nYSize, + GByte ** /*ppImageData */, + int bMaskIsFloat, void *pValidityMask ) + +{ + GDALWarpOptions *psWO = (GDALWarpOptions *) pMaskFuncArg; + float *pafMask = (float *) pValidityMask; + CPLErr eErr; + GDALDriverH hMemDriver; + + if( nXSize < 1 || nYSize < 1 ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Do some minimal checking. */ +/* -------------------------------------------------------------------- */ + if( !bMaskIsFloat ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + if( psWO == NULL || psWO->hCutline == NULL ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + hMemDriver = GDALGetDriverByName("MEM"); + if (hMemDriver == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWarpCutlineMasker needs MEM driver"); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Check the polygon. */ +/* -------------------------------------------------------------------- */ + OGRGeometryH hPolygon = (OGRGeometryH) psWO->hCutline; + OGREnvelope sEnvelope; + + if( wkbFlatten(OGR_G_GetGeometryType(hPolygon)) != wkbPolygon + && wkbFlatten(OGR_G_GetGeometryType(hPolygon)) != wkbMultiPolygon ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + OGR_G_GetEnvelope( hPolygon, &sEnvelope ); + + if( sEnvelope.MaxX + psWO->dfCutlineBlendDist < nXOff + || sEnvelope.MinX - psWO->dfCutlineBlendDist > nXOff + nXSize + || sEnvelope.MaxY + psWO->dfCutlineBlendDist < nYOff + || sEnvelope.MinY - psWO->dfCutlineBlendDist > nYOff + nYSize ) + { + // We are far from the blend line - everything is masked to zero. + // It would be nice to realize no work is required for this whole + // chunk! + memset( pafMask, 0, sizeof(float) * nXSize * nYSize ); + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Create a byte buffer into which we can burn the */ +/* mask polygon and wrap it up as a memory dataset. */ +/* -------------------------------------------------------------------- */ + GByte *pabyPolyMask = (GByte *) CPLCalloc( nXSize, nYSize ); + GDALDatasetH hMemDS; + double adfGeoTransform[6] = { 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; + + char szDataPointer[100]; + char *apszOptions[] = { szDataPointer, NULL }; + + memset( szDataPointer, 0, sizeof(szDataPointer) ); + sprintf( szDataPointer, "DATAPOINTER=" ); + CPLPrintPointer( szDataPointer+strlen(szDataPointer), + pabyPolyMask, + sizeof(szDataPointer) - strlen(szDataPointer) ); + + hMemDS = GDALCreate( hMemDriver, "warp_temp", + nXSize, nYSize, 0, GDT_Byte, NULL ); + GDALAddBand( hMemDS, GDT_Byte, apszOptions ); + GDALSetGeoTransform( hMemDS, adfGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Burn the polygon into the mask with 1.0 values. */ +/* -------------------------------------------------------------------- */ + int nTargetBand = 1; + double dfBurnValue = 255.0; + int anXYOff[2]; + char **papszRasterizeOptions = NULL; + + + if( CSLFetchBoolean( psWO->papszWarpOptions, "CUTLINE_ALL_TOUCHED", FALSE )) + papszRasterizeOptions = + CSLSetNameValue( papszRasterizeOptions, "ALL_TOUCHED", "TRUE" ); + + anXYOff[0] = nXOff; + anXYOff[1] = nYOff; + + eErr = + GDALRasterizeGeometries( hMemDS, 1, &nTargetBand, + 1, &hPolygon, + CutlineTransformer, anXYOff, + &dfBurnValue, papszRasterizeOptions, + NULL, NULL ); + + CSLDestroy( papszRasterizeOptions ); + + // Close and ensure data flushed to underlying array. + GDALClose( hMemDS ); + +/* -------------------------------------------------------------------- */ +/* In the case with no blend distance, we just apply this as a */ +/* mask, zeroing out everything outside the polygon. */ +/* -------------------------------------------------------------------- */ + if( psWO->dfCutlineBlendDist == 0.0 ) + { + int i; + + for( i = nXSize * nYSize - 1; i >= 0; i-- ) + { + if( pabyPolyMask[i] == 0 ) + ((float *) pValidityMask)[i] = 0.0; + } + } + else + { + eErr = BlendMaskGenerator( nXOff, nYOff, nXSize, nYSize, + pabyPolyMask, (float *) pValidityMask, + hPolygon, psWO->dfCutlineBlendDist ); + } + +/* -------------------------------------------------------------------- */ +/* Clean up. */ +/* -------------------------------------------------------------------- */ + CPLFree( pabyPolyMask ); + + return eErr; +} diff --git a/bazaar/plugin/gdal/alg/gdaldither.cpp b/bazaar/plugin/gdal/alg/gdaldither.cpp new file mode 100644 index 000000000..e7ff8d844 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdaldither.cpp @@ -0,0 +1,656 @@ +/****************************************************************************** + * $Id: gdaldither.cpp 28085 2014-12-07 14:36:00Z rouault $ + * + * Project: CIETMap Phase 2 + * Purpose: Convert RGB (24bit) to a pseudo-colored approximation using + * Floyd-Steinberg dithering (error diffusion). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2007, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * Notes: + * + * [1] Floyd-Steinberg dither: + * I should point out that the actual fractions we used were, assuming + * you are at X, moving left to right: + * + * X 7/16 + * 3/16 5/16 1/16 + * + * Note that the error goes to four neighbors, not three. I think this + * will probably do better (at least for black and white) than the + * 3/8-3/8-1/4 distribution, at the cost of greater processing. I have + * seen the 3/8-3/8-1/4 distribution described as "our" algorithm before, + * but I have no idea who the credit really belongs to. + * -- + * Lou Steinberg + * + */ + +#include "gdal_priv.h" +#include "gdal_alg.h" +#include "gdal_alg_priv.h" + +#if defined(__x86_64) || defined(_M_X64) +#define USE_SSE2 +#endif + +#ifdef USE_SSE2 + +#include +#define CAST_PCT(x) ((GByte*)x) +#define ALIGN_INT_ARRAY_ON_16_BYTE(x) ( (((GPtrDiff_t)(x) % 16) != 0 ) ? (int*)((GByte*)(x) + 16 - ((GPtrDiff_t)(x) % 16)) : (x) ) + +#else + +#define CAST_PCT(x) x + +#endif + +#define MAKE_COLOR_CODE(r,g,b) ((r)|((g)<<8)|((b)<<16)) + +CPL_CVSID("$Id: gdaldither.cpp 28085 2014-12-07 14:36:00Z rouault $"); + +static void FindNearestColor( int nColors, int *panPCT, GByte *pabyColorMap, + int nCLevels ); +static int FindNearestColor( int nColors, int *panPCT, + int nRedValue, int nGreenValue, int nBlueValue ); + +/* Structure for a hashmap from a color code to a color index of the color table */ +typedef struct /* NOTE: if changing the size of this structure, edit MEDIAN_CUT_AND_DITHER_BUFFER_SIZE_65536 */ +{ + GUInt32 nColorCode; + GUInt32 nColorCode2; + GUInt32 nColorCode3; + GByte nIndex; + GByte nIndex2; + GByte nIndex3; + GByte nPadding; +} ColorIndex; + +/************************************************************************/ +/* GDALDitherRGB2PCT() */ +/************************************************************************/ + +/** + * 24bit to 8bit conversion with dithering. + * + * This functions utilizes Floyd-Steinberg dithering in the process of + * converting a 24bit RGB image into a pseudocolored 8bit image using a + * provided color table. + * + * The red, green and blue input bands do not necessarily need to come + * from the same file, but they must be the same width and height. They will + * be clipped to 8bit during reading, so non-eight bit bands are generally + * inappropriate. Likewise the hTarget band will be written with 8bit values + * and must match the width and height of the source bands. + * + * The color table cannot have more than 256 entries. + * + * @param hRed Red input band. + * @param hGreen Green input band. + * @param hBlue Blue input band. + * @param hTarget Output band. + * @param hColorTable the color table to use with the output band. + * @param pfnProgress callback for reporting algorithm progress matching the + * GDALProgressFunc() semantics. May be NULL. + * @param pProgressArg callback argument passed to pfnProgress. + * + * @return CE_None on success or CE_Failure if an error occurs. + */ + +int CPL_STDCALL +GDALDitherRGB2PCT( GDALRasterBandH hRed, + GDALRasterBandH hGreen, + GDALRasterBandH hBlue, + GDALRasterBandH hTarget, + GDALColorTableH hColorTable, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + return GDALDitherRGB2PCTInternal( hRed, hGreen, hBlue, hTarget, + hColorTable, 5, NULL, TRUE, + pfnProgress, pProgressArg ); +} + +int GDALDitherRGB2PCTInternal( GDALRasterBandH hRed, + GDALRasterBandH hGreen, + GDALRasterBandH hBlue, + GDALRasterBandH hTarget, + GDALColorTableH hColorTable, + int nBits, + GInt16* pasDynamicColorMap, /* NULL or at least 256 * 256 * 256 * sizeof(GInt16) bytes */ + int bDither, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + VALIDATE_POINTER1( hRed, "GDALDitherRGB2PCT", CE_Failure ); + VALIDATE_POINTER1( hGreen, "GDALDitherRGB2PCT", CE_Failure ); + VALIDATE_POINTER1( hBlue, "GDALDitherRGB2PCT", CE_Failure ); + VALIDATE_POINTER1( hTarget, "GDALDitherRGB2PCT", CE_Failure ); + VALIDATE_POINTER1( hColorTable, "GDALDitherRGB2PCT", CE_Failure ); + + int nXSize, nYSize; + CPLErr err = CE_None; + +/* -------------------------------------------------------------------- */ +/* Validate parameters. */ +/* -------------------------------------------------------------------- */ + nXSize = GDALGetRasterBandXSize( hRed ); + nYSize = GDALGetRasterBandYSize( hRed ); + + if( GDALGetRasterBandXSize( hGreen ) != nXSize + || GDALGetRasterBandYSize( hGreen ) != nYSize + || GDALGetRasterBandXSize( hBlue ) != nXSize + || GDALGetRasterBandYSize( hBlue ) != nYSize ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Green or blue band doesn't match size of red band.\n" ); + + return CE_Failure; + } + + if( GDALGetRasterBandXSize( hTarget ) != nXSize + || GDALGetRasterBandYSize( hTarget ) != nYSize ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALDitherRGB2PCT(): " + "Target band doesn't match size of source bands.\n" ); + + return CE_Failure; + } + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* Setup more direct colormap. */ +/* -------------------------------------------------------------------- */ + int nColors, iColor; +#ifdef USE_SSE2 + int anPCTUnaligned[256+4]; /* 4 for alignment on 16-byte boundary */ + int* anPCT = ALIGN_INT_ARRAY_ON_16_BYTE(anPCTUnaligned); +#else + int anPCT[256*4]; +#endif + nColors = GDALGetColorEntryCount( hColorTable ); + + if (nColors == 0 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALDitherRGB2PCT(): " + "Color table must not be empty.\n" ); + + return CE_Failure; + } + else if (nColors > 256) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALDitherRGB2PCT(): " + "Color table cannot have more than 256 entries.\n" ); + + return CE_Failure; + } + + for( iColor = 0; iColor < nColors; iColor++ ) + { + GDALColorEntry sEntry; + + GDALGetColorEntryAsRGB( hColorTable, iColor, &sEntry ); + CAST_PCT(anPCT)[4*iColor+0] = sEntry.c1; + CAST_PCT(anPCT)[4*iColor+1] = sEntry.c2; + CAST_PCT(anPCT)[4*iColor+2] = sEntry.c3; + CAST_PCT(anPCT)[4*iColor+3] = 0; + } +#ifdef USE_SSE2 + /* Pad to multiple of 8 colors */ + int nColorsMod8 = nColors % 8; + if( nColorsMod8 ) + { + for( iColor = 0; iColor < 8 - nColorsMod8; iColor ++) + { + anPCT[nColors+iColor] = anPCT[nColors-1]; + } + } +#endif + +/* -------------------------------------------------------------------- */ +/* Setup various variables. */ +/* -------------------------------------------------------------------- */ + GByte *pabyRed, *pabyGreen, *pabyBlue, *pabyIndex; + GByte *pabyColorMap = NULL; + int *panError; + int nCLevels = 1 << nBits; + ColorIndex* psColorIndexMap = NULL; + + pabyRed = (GByte *) VSIMalloc(nXSize); + pabyGreen = (GByte *) VSIMalloc(nXSize); + pabyBlue = (GByte *) VSIMalloc(nXSize); + + pabyIndex = (GByte *) VSIMalloc(nXSize); + + panError = (int *) VSICalloc(sizeof(int),(nXSize+2) * 3); + + if (pabyRed == NULL || + pabyGreen == NULL || + pabyBlue == NULL || + pabyIndex == NULL || + panError == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "VSIMalloc(): Out of memory in GDALDitherRGB2PCT" ); + err = CE_Failure; + goto end_and_cleanup; + } + + if( pasDynamicColorMap == NULL ) + { +/* -------------------------------------------------------------------- */ +/* Build a 24bit to 8 bit color mapping. */ +/* -------------------------------------------------------------------- */ + + pabyColorMap = (GByte *) VSIMalloc(nCLevels * nCLevels * nCLevels + * sizeof(GByte)); + if( pabyColorMap == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "VSIMalloc(): Out of memory in GDALDitherRGB2PCT" ); + err = CE_Failure; + goto end_and_cleanup; + } + + FindNearestColor( nColors, anPCT, pabyColorMap, nCLevels); + } + else + { + pabyColorMap = NULL; + if( nBits == 8 && (GIntBig)nXSize * nYSize <= 65536 ) + { + /* If the image is small enough, then the number of colors */ + /* will be limited and using a hashmap, rather than a full table */ + /* will be more efficient */ + psColorIndexMap = (ColorIndex*)pasDynamicColorMap; + memset(psColorIndexMap, 0xFF, sizeof(ColorIndex) * PRIME_FOR_65536); + } + else + { + memset(pasDynamicColorMap, 0xFF, 256 * 256 * 256 * sizeof(GInt16)); + } + } + +/* ==================================================================== */ +/* Loop over all scanlines of data to process. */ +/* ==================================================================== */ + int iScanline; + + for( iScanline = 0; iScanline < nYSize; iScanline++ ) + { + int nLastRedError, nLastGreenError, nLastBlueError, i; + +/* -------------------------------------------------------------------- */ +/* Report progress */ +/* -------------------------------------------------------------------- */ + if( !pfnProgress( iScanline / (double) nYSize, NULL, pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User Terminated" ); + err = CE_Failure; + goto end_and_cleanup; + } + +/* -------------------------------------------------------------------- */ +/* Read source data. */ +/* -------------------------------------------------------------------- */ + GDALRasterIO( hRed, GF_Read, 0, iScanline, nXSize, 1, + pabyRed, nXSize, 1, GDT_Byte, 0, 0 ); + GDALRasterIO( hGreen, GF_Read, 0, iScanline, nXSize, 1, + pabyGreen, nXSize, 1, GDT_Byte, 0, 0 ); + GDALRasterIO( hBlue, GF_Read, 0, iScanline, nXSize, 1, + pabyBlue, nXSize, 1, GDT_Byte, 0, 0 ); + +/* -------------------------------------------------------------------- */ +/* Apply the error from the previous line to this one. */ +/* -------------------------------------------------------------------- */ + if( bDither ) + { + for( i = 0; i < nXSize; i++ ) + { + pabyRed[i] = (GByte) + MAX(0,MIN(255,(pabyRed[i] + panError[i*3+0+3]))); + pabyGreen[i] = (GByte) + MAX(0,MIN(255,(pabyGreen[i] + panError[i*3+1+3]))); + pabyBlue[i] = (GByte) + MAX(0,MIN(255,(pabyBlue[i] + panError[i*3+2+3]))); + } + + memset( panError, 0, sizeof(int) * (nXSize+2) * 3 ); + } + +/* -------------------------------------------------------------------- */ +/* Figure out the nearest color to the RGB value. */ +/* -------------------------------------------------------------------- */ + nLastRedError = 0; + nLastGreenError = 0; + nLastBlueError = 0; + + for( i = 0; i < nXSize; i++ ) + { + int iIndex, nError, nSixth; + int nRedValue, nGreenValue, nBlueValue; + + nRedValue = MAX(0,MIN(255, pabyRed[i] + nLastRedError)); + nGreenValue = MAX(0,MIN(255, pabyGreen[i] + nLastGreenError)); + nBlueValue = MAX(0,MIN(255, pabyBlue[i] + nLastBlueError)); + + if( psColorIndexMap ) + { + GUInt32 nColorCode = MAKE_COLOR_CODE(nRedValue, nGreenValue, nBlueValue); + GUInt32 nIdx = nColorCode % PRIME_FOR_65536; + //int nCollisions = 0; + //static int nMaxCollisions = 0; + while( TRUE ) + { + if( psColorIndexMap[nIdx].nColorCode == nColorCode ) + { + iIndex = psColorIndexMap[nIdx].nIndex; + break; + } + if( (int)psColorIndexMap[nIdx].nColorCode < 0 ) + { + psColorIndexMap[nIdx].nColorCode = nColorCode; + iIndex = FindNearestColor( nColors, anPCT, + nRedValue, nGreenValue, nBlueValue ); + psColorIndexMap[nIdx].nIndex = (GByte) iIndex; + break; + } + if( psColorIndexMap[nIdx].nColorCode2 == nColorCode ) + { + iIndex = psColorIndexMap[nIdx].nIndex2; + break; + } + if( (int)psColorIndexMap[nIdx].nColorCode2 < 0 ) + { + psColorIndexMap[nIdx].nColorCode2 = nColorCode; + iIndex = FindNearestColor( nColors, anPCT, + nRedValue, nGreenValue, nBlueValue ); + psColorIndexMap[nIdx].nIndex2 = (GByte) iIndex; + break; + } + if( psColorIndexMap[nIdx].nColorCode3 == nColorCode ) + { + iIndex = psColorIndexMap[nIdx].nIndex3; + break; + } + if( (int)psColorIndexMap[nIdx].nColorCode3 < 0 ) + { + psColorIndexMap[nIdx].nColorCode3 = nColorCode; + iIndex = FindNearestColor( nColors, anPCT, + nRedValue, nGreenValue, nBlueValue ); + psColorIndexMap[nIdx].nIndex3 = (GByte) iIndex; + break; + } + + do + { + //nCollisions ++; + nIdx+=257; + if( nIdx >= PRIME_FOR_65536 ) + nIdx -= PRIME_FOR_65536; + } + while( (int)psColorIndexMap[nIdx].nColorCode >= 0 && + psColorIndexMap[nIdx].nColorCode != nColorCode && + (int)psColorIndexMap[nIdx].nColorCode2 >= 0 && + psColorIndexMap[nIdx].nColorCode2 != nColorCode&& + (int)psColorIndexMap[nIdx].nColorCode3 >= 0 && + psColorIndexMap[nIdx].nColorCode3 != nColorCode ); + /*if( nCollisions > nMaxCollisions ) + { + nMaxCollisions = nCollisions; + printf("nCollisions = %d for R=%d,G=%d,B=%d\n", + nCollisions, nRedValue, nGreenValue, nBlueValue); + }*/ + } + } + else if( pasDynamicColorMap == NULL ) + { + int iRed = nRedValue * nCLevels / 256; + int iGreen = nGreenValue * nCLevels / 256; + int iBlue = nBlueValue * nCLevels / 256; + + iIndex = pabyColorMap[iRed + iGreen * nCLevels + + iBlue * nCLevels * nCLevels]; + } + else + { + GUInt32 nColorCode = MAKE_COLOR_CODE(nRedValue, nGreenValue, nBlueValue); + GInt16* psIndex = &pasDynamicColorMap[nColorCode]; + if( *psIndex < 0 ) + iIndex = *psIndex = FindNearestColor( nColors, anPCT, + nRedValue, + nGreenValue, + nBlueValue ); + else + iIndex = *psIndex; + } + + pabyIndex[i] = (GByte) iIndex; + if( !bDither ) + continue; + +/* -------------------------------------------------------------------- */ +/* Compute Red error, and carry it on to the next error line. */ +/* -------------------------------------------------------------------- */ + nError = nRedValue - CAST_PCT(anPCT)[4*iIndex+0]; + nSixth = nError / 6; + + panError[i*3 ] += nSixth; + panError[i*3+6 ] = nSixth; + panError[i*3+3 ] += nError - 5 * nSixth; + + nLastRedError = 2 * nSixth; + +/* -------------------------------------------------------------------- */ +/* Compute Green error, and carry it on to the next error line. */ +/* -------------------------------------------------------------------- */ + nError = nGreenValue - CAST_PCT(anPCT)[4*iIndex+1]; + nSixth = nError / 6; + + panError[i*3 +1] += nSixth; + panError[i*3+6+1] = nSixth; + panError[i*3+3+1] += nError - 5 * nSixth; + + nLastGreenError = 2 * nSixth; + +/* -------------------------------------------------------------------- */ +/* Compute Blue error, and carry it on to the next error line. */ +/* -------------------------------------------------------------------- */ + nError = nBlueValue - CAST_PCT(anPCT)[4*iIndex+2]; + nSixth = nError / 6; + + panError[i*3 +2] += nSixth; + panError[i*3+6+2] = nSixth; + panError[i*3+3+2] += nError - 5 * nSixth; + + nLastBlueError = 2 * nSixth; + } + +/* -------------------------------------------------------------------- */ +/* Write results. */ +/* -------------------------------------------------------------------- */ + GDALRasterIO( hTarget, GF_Write, 0, iScanline, nXSize, 1, + pabyIndex, nXSize, 1, GDT_Byte, 0, 0 ); + } + + pfnProgress( 1.0, NULL, pProgressArg ); + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ +end_and_cleanup: + CPLFree( pabyRed ); + CPLFree( pabyGreen ); + CPLFree( pabyBlue ); + CPLFree( pabyIndex ); + CPLFree( panError ); + CPLFree( pabyColorMap ); + + return err; +} + +static int FindNearestColor( int nColors, int *panPCT, + int nRedValue, int nGreenValue, int nBlueValue ) + +{ +#ifdef USE_SSE2 + int iColor; + + int nBestDist = 768, nBestIndex = 0; + + int anDistanceUnaligned[16+4]; /* 4 for alignment on 16-byte boundary */ + int* anDistance = ALIGN_INT_ARRAY_ON_16_BYTE(anDistanceUnaligned); + + const __m128i ff = _mm_set1_epi32(0xFFFFFFFF); + const __m128i mask_low = _mm_srli_epi64(ff, 32); + const __m128i mask_high = _mm_slli_epi64(ff, 32); + + unsigned int nColorVal = MAKE_COLOR_CODE(nRedValue, nGreenValue, nBlueValue); + const __m128i thisColor = _mm_set1_epi32(nColorVal); + const __m128i thisColor_low = _mm_srli_epi64(thisColor, 32); + const __m128i thisColor_high = _mm_slli_epi64(thisColor, 32); + + for( iColor = 0; iColor < nColors; iColor+=8 ) + { + __m128i pctColor = _mm_load_si128((__m128i*)&panPCT[iColor]); + __m128i pctColor2 = _mm_load_si128((__m128i*)&panPCT[iColor+4]); + + _mm_store_si128((__m128i*)anDistance, + _mm_sad_epu8(_mm_and_si128(pctColor,mask_low),thisColor_low)); + _mm_store_si128((__m128i*)(anDistance+4), + _mm_sad_epu8(_mm_and_si128(pctColor,mask_high),thisColor_high)); + _mm_store_si128((__m128i*)(anDistance+8), + _mm_sad_epu8(_mm_and_si128(pctColor2,mask_low),thisColor_low)); + _mm_store_si128((__m128i*)(anDistance+12), + _mm_sad_epu8(_mm_and_si128(pctColor2,mask_high),thisColor_high)); + + if( anDistance[0] < nBestDist ) + { + nBestIndex = iColor; + nBestDist = anDistance[0]; + } + if( anDistance[4] < nBestDist ) + { + nBestIndex = iColor+1; + nBestDist = anDistance[4]; + } + if( anDistance[2] < nBestDist ) + { + nBestIndex = iColor+2; + nBestDist = anDistance[2]; + } + if( anDistance[6] < nBestDist ) + { + nBestIndex = iColor+3; + nBestDist = anDistance[6]; + } + if( anDistance[8+0] < nBestDist ) + { + nBestIndex = iColor+4; + nBestDist = anDistance[8+0]; + } + if( anDistance[8+4] < nBestDist ) + { + nBestIndex = iColor+4+1; + nBestDist = anDistance[8+4]; + } + if( anDistance[8+2] < nBestDist ) + { + nBestIndex = iColor+4+2; + nBestDist = anDistance[8+2]; + } + if( anDistance[8+6] < nBestDist ) + { + nBestIndex = iColor+4+3; + nBestDist = anDistance[8+6]; + } + } + return nBestIndex; +#else + int iColor; + + int nBestDist = 768, nBestIndex = 0; + + for( iColor = 0; iColor < nColors; iColor++ ) + { + int nThisDist; + + nThisDist = ABS(nRedValue - panPCT[4*iColor+0]) + + ABS(nGreenValue - panPCT[4*iColor+1]) + + ABS(nBlueValue - panPCT[4*iColor+2]); + + if( nThisDist < nBestDist ) + { + nBestIndex = iColor; + nBestDist = nThisDist; + } + } + return nBestIndex; +#endif +} + + +/************************************************************************/ +/* FindNearestColor() */ +/* */ +/* Finear near PCT color for any RGB color. */ +/************************************************************************/ + +static void FindNearestColor( int nColors, int *panPCT, GByte *pabyColorMap, + int nCLevels ) + +{ + int iBlue, iGreen, iRed; + +/* -------------------------------------------------------------------- */ +/* Loop over all the cells in the high density cube. */ +/* -------------------------------------------------------------------- */ + for( iBlue = 0; iBlue < nCLevels; iBlue++ ) + { + for( iGreen = 0; iGreen < nCLevels; iGreen++ ) + { + for( iRed = 0; iRed < nCLevels; iRed++ ) + { + int nRedValue, nGreenValue, nBlueValue; + + nRedValue = (iRed * 255) / (nCLevels-1); + nGreenValue = (iGreen * 255) / (nCLevels-1); + nBlueValue = (iBlue * 255) / (nCLevels-1); + + int nBestIndex = FindNearestColor( nColors, panPCT, + nRedValue, nGreenValue, nBlueValue ); + pabyColorMap[iRed + iGreen*nCLevels + + iBlue*nCLevels*nCLevels] = (GByte)nBestIndex; + } + } + } +} diff --git a/bazaar/plugin/gdal/alg/gdalgeoloc.cpp b/bazaar/plugin/gdal/alg/gdalgeoloc.cpp new file mode 100644 index 000000000..b17f52693 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalgeoloc.cpp @@ -0,0 +1,1280 @@ +/****************************************************************************** + * $Id: gdalgeoloc.cpp 29207 2015-05-18 17:23:45Z mloskot $ + * + * Project: GDAL + * Purpose: Implements Geolocation array based transformer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2006, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_alg.h" + +#ifdef SHAPE_DEBUG +#include "/u/pkg/shapelib/shapefil.h" + +SHPHandle hSHP = NULL; +DBFHandle hDBF = NULL; +#endif + +CPL_CVSID("$Id: gdalgeoloc.cpp 29207 2015-05-18 17:23:45Z mloskot $"); + +CPL_C_START +CPLXMLNode *GDALSerializeGeoLocTransformer( void *pTransformArg ); +void *GDALDeserializeGeoLocTransformer( CPLXMLNode *psTree ); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* GDALGeoLocTransformer */ +/* ==================================================================== */ +/************************************************************************/ + +typedef struct { + + GDALTransformerInfo sTI; + + int bReversed; + + // Map from target georef coordinates back to geolocation array + // pixel line coordinates. Built only if needed. + + int nBackMapWidth; + int nBackMapHeight; + double adfBackMapGeoTransform[6]; // maps georef to pixel/line. + float *pafBackMapX; + float *pafBackMapY; + + // geolocation bands. + + GDALDatasetH hDS_X; + GDALRasterBandH hBand_X; + GDALDatasetH hDS_Y; + GDALRasterBandH hBand_Y; + + // Located geolocation data. + int nGeoLocXSize; + int nGeoLocYSize; + double *padfGeoLocX; + double *padfGeoLocY; + + int bHasNoData; + double dfNoDataX; + + // geolocation <-> base image mapping. + double dfPIXEL_OFFSET; + double dfPIXEL_STEP; + double dfLINE_OFFSET; + double dfLINE_STEP; + + char ** papszGeolocationInfo; + +} GDALGeoLocTransformInfo; + +/************************************************************************/ +/* GeoLocLoadFullData() */ +/************************************************************************/ + +static int GeoLocLoadFullData( GDALGeoLocTransformInfo *psTransform ) + +{ + int nXSize, nYSize; + + int nXSize_XBand = GDALGetRasterXSize( psTransform->hDS_X ); + int nYSize_XBand = GDALGetRasterYSize( psTransform->hDS_X ); + int nXSize_YBand = GDALGetRasterXSize( psTransform->hDS_Y ); + int nYSize_YBand = GDALGetRasterYSize( psTransform->hDS_Y ); + if (nYSize_XBand == 1 && nYSize_YBand == 1) + { + nXSize = nXSize_XBand; + nYSize = nXSize_YBand; + } + else + { + nXSize = nXSize_XBand; + nYSize = nYSize_XBand; + } + + psTransform->nGeoLocXSize = nXSize; + psTransform->nGeoLocYSize = nYSize; + + psTransform->padfGeoLocY = (double *) + VSIMalloc3(sizeof(double), nXSize, nYSize); + psTransform->padfGeoLocX = (double *) + VSIMalloc3(sizeof(double), nXSize, nYSize); + + if( psTransform->padfGeoLocX == NULL || + psTransform->padfGeoLocY == NULL ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "GeoLocLoadFullData : Out of memory"); + return FALSE; + } + + if (nYSize_XBand == 1 && nYSize_YBand == 1) + { + /* Case of regular grid */ + /* The XBAND contains the x coordinates for all lines */ + /* The YBAND contains the y coordinates for all columns */ + + double* padfTempX = (double*)VSIMalloc2(nXSize, sizeof(double)); + double* padfTempY = (double*)VSIMalloc2(nYSize, sizeof(double)); + if (padfTempX == NULL || padfTempY == NULL) + { + CPLFree(padfTempX); + CPLFree(padfTempY); + CPLError(CE_Failure, CPLE_OutOfMemory, + "GeoLocLoadFullData : Out of memory"); + return FALSE; + } + + CPLErr eErr = CE_None; + + eErr = GDALRasterIO( psTransform->hBand_X, GF_Read, + 0, 0, nXSize, 1, + padfTempX, nXSize, 1, + GDT_Float64, 0, 0 ); + + int i,j; + for(j=0;jpadfGeoLocX + j * nXSize, + padfTempX, + nXSize * sizeof(double) ); + } + + if (eErr == CE_None) + { + eErr = GDALRasterIO( psTransform->hBand_Y, GF_Read, + 0, 0, nYSize, 1, + padfTempY, nYSize, 1, + GDT_Float64, 0, 0 ); + + for(j=0;jpadfGeoLocY[j * nXSize + i] = padfTempY[j]; + } + } + } + + CPLFree(padfTempX); + CPLFree(padfTempY); + + if (eErr != CE_None) + return FALSE; + } + else + { + if( GDALRasterIO( psTransform->hBand_X, GF_Read, + 0, 0, nXSize, nYSize, + psTransform->padfGeoLocX, nXSize, nYSize, + GDT_Float64, 0, 0 ) != CE_None + || GDALRasterIO( psTransform->hBand_Y, GF_Read, + 0, 0, nXSize, nYSize, + psTransform->padfGeoLocY, nXSize, nYSize, + GDT_Float64, 0, 0 ) != CE_None ) + return FALSE; + } + + psTransform->dfNoDataX = GDALGetRasterNoDataValue( psTransform->hBand_X, + &(psTransform->bHasNoData) ); + + return TRUE; +} + +/************************************************************************/ +/* GeoLocGenerateBackMap() */ +/************************************************************************/ + +static int GeoLocGenerateBackMap( GDALGeoLocTransformInfo *psTransform ) + +{ + int nXSize = psTransform->nGeoLocXSize; + int nYSize = psTransform->nGeoLocYSize; + int nMaxIter = 3; + +/* -------------------------------------------------------------------- */ +/* Scan forward map for lat/long extents. */ +/* -------------------------------------------------------------------- */ + double dfMinX=0, dfMaxX=0, dfMinY=0, dfMaxY=0; + int i, bInit = FALSE; + + for( i = nXSize * nYSize - 1; i >= 0; i-- ) + { + if( !psTransform->bHasNoData || + psTransform->padfGeoLocX[i] != psTransform->dfNoDataX ) + { + if( bInit ) + { + dfMinX = MIN(dfMinX,psTransform->padfGeoLocX[i]); + dfMaxX = MAX(dfMaxX,psTransform->padfGeoLocX[i]); + dfMinY = MIN(dfMinY,psTransform->padfGeoLocY[i]); + dfMaxY = MAX(dfMaxY,psTransform->padfGeoLocY[i]); + } + else + { + bInit = TRUE; + dfMinX = dfMaxX = psTransform->padfGeoLocX[i]; + dfMinY = dfMaxY = psTransform->padfGeoLocY[i]; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Decide on resolution for backmap. We aim for slightly */ +/* higher resolution than the source but we can't easily */ +/* establish how much dead space there is in the backmap, so it */ +/* is approximate. */ +/* -------------------------------------------------------------------- */ + double dfTargetPixels = (nXSize * nYSize * 1.3); + double dfPixelSize = sqrt((dfMaxX - dfMinX) * (dfMaxY - dfMinY) + / dfTargetPixels); + int nBMXSize, nBMYSize; + + nBMYSize = psTransform->nBackMapHeight = + (int) ((dfMaxY - dfMinY) / dfPixelSize + 1); + nBMXSize= psTransform->nBackMapWidth = + (int) ((dfMaxX - dfMinX) / dfPixelSize + 1); + + if (nBMXSize > INT_MAX / nBMYSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Int overflow : %d x %d", + nBMXSize, nBMYSize); + return FALSE; + } + + dfMinX -= dfPixelSize/2.0; + dfMaxY += dfPixelSize/2.0; + + psTransform->adfBackMapGeoTransform[0] = dfMinX; + psTransform->adfBackMapGeoTransform[1] = dfPixelSize; + psTransform->adfBackMapGeoTransform[2] = 0.0; + psTransform->adfBackMapGeoTransform[3] = dfMaxY; + psTransform->adfBackMapGeoTransform[4] = 0.0; + psTransform->adfBackMapGeoTransform[5] = -dfPixelSize; + +/* -------------------------------------------------------------------- */ +/* Allocate backmap, and initialize to nodata value (-1.0). */ +/* -------------------------------------------------------------------- */ + GByte *pabyValidFlag; + + pabyValidFlag = (GByte *) + VSICalloc(nBMXSize, nBMYSize); + + psTransform->pafBackMapX = (float *) + VSIMalloc3(nBMXSize, nBMYSize, sizeof(float)); + psTransform->pafBackMapY = (float *) + VSIMalloc3(nBMXSize, nBMYSize, sizeof(float)); + + if( pabyValidFlag == NULL || + psTransform->pafBackMapX == NULL || + psTransform->pafBackMapY == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate %dx%d back-map for geolocation array transformer.", + nBMXSize, nBMYSize ); + CPLFree( pabyValidFlag ); + return FALSE; + } + + for( i = nBMXSize * nBMYSize - 1; i >= 0; i-- ) + { + psTransform->pafBackMapX[i] = -1.0; + psTransform->pafBackMapY[i] = -1.0; + } + +/* -------------------------------------------------------------------- */ +/* Run through the whole geoloc array forward projecting and */ +/* pushing into the backmap. */ +/* Initialise to the nMaxIter+1 value so we can spot genuinely */ +/* valid pixels in the hole-filling loop. */ +/* -------------------------------------------------------------------- */ + int iBMX, iBMY; + int iX, iY; + + for( iY = 0; iY < nYSize; iY++ ) + { + for( iX = 0; iX < nXSize; iX++ ) + { + if( psTransform->bHasNoData && + psTransform->padfGeoLocX[iX + iY * nXSize] + == psTransform->dfNoDataX ) + continue; + + i = iX + iY * nXSize; + + iBMX = (int) ((psTransform->padfGeoLocX[i] - dfMinX) / dfPixelSize); + iBMY = (int) ((dfMaxY - psTransform->padfGeoLocY[i]) / dfPixelSize); + + if( iBMX < 0 || iBMY < 0 || iBMX >= nBMXSize || iBMY >= nBMYSize ) + continue; + + psTransform->pafBackMapX[iBMX + iBMY * nBMXSize] = + (float)(iX * psTransform->dfPIXEL_STEP + psTransform->dfPIXEL_OFFSET); + psTransform->pafBackMapY[iBMX + iBMY * nBMXSize] = + (float)(iY * psTransform->dfLINE_STEP + psTransform->dfLINE_OFFSET); + + pabyValidFlag[iBMX + iBMY * nBMXSize] = (GByte) (nMaxIter+1); + + } + } + +/* -------------------------------------------------------------------- */ +/* Now, loop over the backmap trying to fill in holes with */ +/* nearby values. */ +/* -------------------------------------------------------------------- */ + int iIter; + int nNumValid; + + for( iIter = 0; iIter < nMaxIter; iIter++ ) + { + nNumValid = 0; + for( iBMY = 0; iBMY < nBMYSize; iBMY++ ) + { + for( iBMX = 0; iBMX < nBMXSize; iBMX++ ) + { + // if this point is already set, ignore it. + if( pabyValidFlag[iBMX + iBMY*nBMXSize] ) + { + nNumValid++; + continue; + } + + int nCount = 0; + double dfXSum = 0.0, dfYSum = 0.0; + int nMarkedAsGood = nMaxIter - iIter; + + // left? + if( iBMX > 0 && + pabyValidFlag[iBMX-1+iBMY*nBMXSize] > nMarkedAsGood ) + { + dfXSum += psTransform->pafBackMapX[iBMX-1+iBMY*nBMXSize]; + dfYSum += psTransform->pafBackMapY[iBMX-1+iBMY*nBMXSize]; + nCount++; + } + // right? + if( iBMX + 1 < nBMXSize && + pabyValidFlag[iBMX+1+iBMY*nBMXSize] > nMarkedAsGood ) + { + dfXSum += psTransform->pafBackMapX[iBMX+1+iBMY*nBMXSize]; + dfYSum += psTransform->pafBackMapY[iBMX+1+iBMY*nBMXSize]; + nCount++; + } + // top? + if( iBMY > 0 && + pabyValidFlag[iBMX+(iBMY-1)*nBMXSize] > nMarkedAsGood ) + { + dfXSum += psTransform->pafBackMapX[iBMX+(iBMY-1)*nBMXSize]; + dfYSum += psTransform->pafBackMapY[iBMX+(iBMY-1)*nBMXSize]; + nCount++; + } + // bottom? + if( iBMY + 1 < nBMYSize && + pabyValidFlag[iBMX+(iBMY+1)*nBMXSize] > nMarkedAsGood ) + { + dfXSum += psTransform->pafBackMapX[iBMX+(iBMY+1)*nBMXSize]; + dfYSum += psTransform->pafBackMapY[iBMX+(iBMY+1)*nBMXSize]; + nCount++; + } + // top-left? + if( iBMX > 0 && iBMY > 0 && + pabyValidFlag[iBMX-1+(iBMY-1)*nBMXSize] > nMarkedAsGood ) + { + dfXSum += psTransform->pafBackMapX[iBMX-1+(iBMY-1)*nBMXSize]; + dfYSum += psTransform->pafBackMapY[iBMX-1+(iBMY-1)*nBMXSize]; + nCount++; + } + // top-right? + if( iBMX + 1 < nBMXSize && iBMY > 0 && + pabyValidFlag[iBMX+1+(iBMY-1)*nBMXSize] > nMarkedAsGood ) + { + dfXSum += psTransform->pafBackMapX[iBMX+1+(iBMY-1)*nBMXSize]; + dfYSum += psTransform->pafBackMapY[iBMX+1+(iBMY-1)*nBMXSize]; + nCount++; + } + // bottom-left? + if( iBMX > 0 && iBMY + 1 < nBMYSize && + pabyValidFlag[iBMX-1+(iBMY+1)*nBMXSize] > nMarkedAsGood ) + { + dfXSum += psTransform->pafBackMapX[iBMX-1+(iBMY+1)*nBMXSize]; + dfYSum += psTransform->pafBackMapY[iBMX-1+(iBMY+1)*nBMXSize]; + nCount++; + } + // bottom-right? + if( iBMX + 1 < nBMXSize && iBMY + 1 < nBMYSize && + pabyValidFlag[iBMX+1+(iBMY+1)*nBMXSize] > nMarkedAsGood ) + { + dfXSum += psTransform->pafBackMapX[iBMX+1+(iBMY+1)*nBMXSize]; + dfYSum += psTransform->pafBackMapY[iBMX+1+(iBMY+1)*nBMXSize]; + nCount++; + } + + if( nCount > 0 ) + { + psTransform->pafBackMapX[iBMX + iBMY * nBMXSize] = (float)(dfXSum/nCount); + psTransform->pafBackMapY[iBMX + iBMY * nBMXSize] = (float)(dfYSum/nCount); + // genuinely valid points will have value iMaxIter+1 + // On each iteration mark newly valid points with a + // descending value so that it will not be used on the + // current iteration only on subsequent ones. + pabyValidFlag[iBMX+iBMY*nBMXSize] = (GByte) (nMaxIter - iIter); + } + } + } + if (nNumValid == nBMXSize * nBMYSize) + break; + } + +#ifdef notdef + GDALDatasetH hBMDS = GDALCreate( GDALGetDriverByName( "GTiff" ), + "backmap.tif", nBMXSize, nBMYSize, 2, + GDT_Float32, NULL ); + GDALSetGeoTransform( hBMDS, psTransform->adfBackMapGeoTransform ); + GDALRasterIO( GDALGetRasterBand(hBMDS,1), GF_Write, + 0, 0, nBMXSize, nBMYSize, + psTransform->pafBackMapX, nBMXSize, nBMYSize, + GDT_Float32, 0, 0 ); + GDALRasterIO( GDALGetRasterBand(hBMDS,2), GF_Write, + 0, 0, nBMXSize, nBMYSize, + psTransform->pafBackMapY, nBMXSize, nBMYSize, + GDT_Float32, 0, 0 ); + GDALClose( hBMDS ); +#endif + + CPLFree( pabyValidFlag ); + + return TRUE; +} + +/************************************************************************/ +/* FindGeoLocPosition() */ +/************************************************************************/ + +#ifdef notdef + +/* +This searching approach has been abandoned because it is too sensitive +to discontinuities in the data. Left in case it might be revived in +the future. + */ + +static int FindGeoLocPosition( GDALGeoLocTransformInfo *psTransform, + double dfGeoX, double dfGeoY, + int nStartX, int nStartY, + double *pdfFoundX, double *pdfFoundY ) + +{ + double adfPathX[5000], adfPathY[5000]; + + if( psTransform->padfGeoLocX == NULL ) + return FALSE; + + int nXSize = psTransform->nGeoLocXSize; + int nYSize = psTransform->nGeoLocYSize; + int nStepCount = 0; + + // Start in center if we don't have any provided info. + if( nStartX < 0 || nStartY < 0 + || nStartX >= nXSize || nStartY >= nYSize ) + { + nStartX = nXSize / 2; + nStartY = nYSize / 2; + } + + nStartX = MIN(nStartX,nXSize-2); + nStartY = MIN(nStartY,nYSize-2); + + int iX = nStartX, iY = nStartY; + int iLastX = -1, iLastY = -1; + int iSecondLastX = -1, iSecondLastY = -1; + + while( nStepCount < MAX(nXSize,nYSize) ) + { + int iXNext = -1, iYNext = -1; + double dfDeltaXRight, dfDeltaYRight, dfDeltaXDown, dfDeltaYDown; + + double *padfThisX = psTransform->padfGeoLocX + iX + iY * nXSize; + double *padfThisY = psTransform->padfGeoLocY + iX + iY * nXSize; + + double dfDeltaX = dfGeoX - *padfThisX; + double dfDeltaY = dfGeoY - *padfThisY; + + if( iX == nXSize-1 ) + { + dfDeltaXRight = *(padfThisX) - *(padfThisX-1); + dfDeltaYRight = *(padfThisY) - *(padfThisY-1); + } + else + { + dfDeltaXRight = *(padfThisX+1) - *padfThisX; + dfDeltaYRight = *(padfThisY+1) - *padfThisY; + } + + if( iY == nYSize - 1 ) + { + dfDeltaXDown = *(padfThisX) - *(padfThisX-nXSize); + dfDeltaYDown = *(padfThisY) - *(padfThisY-nXSize); + } + else + { + dfDeltaXDown = *(padfThisX+nXSize) - *padfThisX; + dfDeltaYDown = *(padfThisY+nXSize) - *padfThisY; + } + + double dfRightProjection = + (dfDeltaXRight * dfDeltaX + dfDeltaYRight * dfDeltaY) + / (dfDeltaXRight*dfDeltaXRight + dfDeltaYRight*dfDeltaYRight); + + double dfDownProjection = + (dfDeltaXDown * dfDeltaX + dfDeltaYDown * dfDeltaY) + / (dfDeltaXDown*dfDeltaXDown + dfDeltaYDown*dfDeltaYDown); + + // Are we in our target cell? + if( dfRightProjection >= 0.0 && dfRightProjection < 1.0 + && dfDownProjection >= 0.0 && dfDownProjection < 1.0 ) + { + *pdfFoundX = iX + dfRightProjection; + *pdfFoundY = iY + dfDownProjection; + + return TRUE; + } + + if( ABS(dfRightProjection) > ABS(dfDownProjection) ) + { + // Do we want to move right? + if( dfRightProjection > 1.0 && iX < nXSize-1 ) + { + iXNext = iX + MAX(1,(int)(dfRightProjection - nStepCount)/2); + iYNext = iY; + } + + // Do we want to move left? + else if( dfRightProjection < 0.0 && iX > 0 ) + { + iXNext = iX - MAX(1,(int)(ABS(dfRightProjection) - nStepCount)/2); + iYNext = iY; + } + + // Do we want to move down. + else if( dfDownProjection > 1.0 && iY < nYSize-1 ) + { + iXNext = iX; + iYNext = iY + MAX(1,(int)(dfDownProjection - nStepCount)/2); + } + + // Do we want to move up? + else if( dfDownProjection < 0.0 && iY > 0 ) + { + iXNext = iX; + iYNext = iY - MAX(1,(int)(ABS(dfDownProjection) - nStepCount)/2); + } + + // We aren't there, and we have no where to go + else + { + return FALSE; + } + } + else + { + // Do we want to move down. + if( dfDownProjection > 1.0 && iY < nYSize-1 ) + { + iXNext = iX; + iYNext = iY + MAX(1,(int)(dfDownProjection - nStepCount)/2); + } + + // Do we want to move up? + else if( dfDownProjection < 0.0 && iY > 0 ) + { + iXNext = iX; + iYNext = iY - MAX(1,(int)(ABS(dfDownProjection) - nStepCount)/2); + } + + // Do we want to move right? + else if( dfRightProjection > 1.0 && iX < nXSize-1 ) + { + iXNext = iX + MAX(1,(int)(dfRightProjection - nStepCount)/2); + iYNext = iY; + } + + // Do we want to move left? + else if( dfRightProjection < 0.0 && iX > 0 ) + { + iXNext = iX - MAX(1,(int)(ABS(dfRightProjection) - nStepCount)/2); + iYNext = iY; + } + + // We aren't there, and we have no where to go + else + { + return FALSE; + } + } + adfPathX[nStepCount] = iX; + adfPathY[nStepCount] = iY; + + nStepCount++; + iX = MAX(0,MIN(iXNext,nXSize-1)); + iY = MAX(0,MIN(iYNext,nYSize-1)); + + if( iX == iSecondLastX && iY == iSecondLastY ) + { + // Are we *near* our target cell? + if( dfRightProjection >= -1.0 && dfRightProjection < 2.0 + && dfDownProjection >= -1.0 && dfDownProjection < 2.0 ) + { + *pdfFoundX = iX + dfRightProjection; + *pdfFoundY = iY + dfDownProjection; + + return TRUE; + } + +#ifdef SHAPE_DEBUG + if( hSHP != NULL ) + { + SHPObject *hObj; + + hObj = SHPCreateSimpleObject( SHPT_ARC, nStepCount, + adfPathX, adfPathY, NULL ); + SHPWriteObject( hSHP, -1, hObj ); + SHPDestroyObject( hObj ); + + int iShape = DBFGetRecordCount( hDBF ); + DBFWriteDoubleAttribute( hDBF, iShape, 0, dfGeoX ); + DBFWriteDoubleAttribute( hDBF, iShape, 1, dfGeoY ); + } +#endif + //CPLDebug( "GeoL", "Looping at step (%d) on search for %g,%g.", + // nStepCount, dfGeoX, dfGeoY ); + return FALSE; + } + + iSecondLastX = iLastX; + iSecondLastY = iLastY; + + iLastX = iX; + iLastY = iY; + + } + + //CPLDebug( "GeoL", "Exceeded step count max (%d) on search for %g,%g.", + // MAX(nXSize,nYSize), + // dfGeoX, dfGeoY ); + +#ifdef SHAPE_DEBUG + if( hSHP != NULL ) + { + SHPObject *hObj; + + hObj = SHPCreateSimpleObject( SHPT_ARC, nStepCount, + adfPathX, adfPathY, NULL ); + SHPWriteObject( hSHP, -1, hObj ); + SHPDestroyObject( hObj ); + + int iShape = DBFGetRecordCount( hDBF ); + DBFWriteDoubleAttribute( hDBF, iShape, 0, dfGeoX ); + DBFWriteDoubleAttribute( hDBF, iShape, 1, dfGeoY ); + } +#endif + + return FALSE; +} +#endif /* def notdef */ + + +/************************************************************************/ +/* GDALGeoLocRescale() */ +/************************************************************************/ + +static void GDALGeoLocRescale(char**& papszMD, const char* pszItem, + double dfRatio, double dfDefaultVal) +{ + double dfVal = CPLAtofM(CSLFetchNameValueDef(papszMD, pszItem, + CPLSPrintf("%.18g", dfDefaultVal))); + dfVal *= dfRatio; + papszMD = CSLSetNameValue(papszMD, pszItem, CPLSPrintf("%.18g", dfVal)); +} + +/************************************************************************/ +/* GDALCreateSimilarGeoLocTransformer() */ +/************************************************************************/ + +static +void* GDALCreateSimilarGeoLocTransformer( void *hTransformArg, double dfRatioX, double dfRatioY ) +{ + VALIDATE_POINTER1( hTransformArg, "GDALCreateSimilarGeoLocTransformer", NULL ); + + GDALGeoLocTransformInfo *psInfo = (GDALGeoLocTransformInfo *) hTransformArg; + + char** papszGeolocationInfo = CSLDuplicate(psInfo->papszGeolocationInfo); + + if( dfRatioX != 1.0 || dfRatioY != 1.0 ) + { + GDALGeoLocRescale(papszGeolocationInfo, "PIXEL_OFFSET", dfRatioX, 0.0); + GDALGeoLocRescale(papszGeolocationInfo, "LINE_OFFSET", dfRatioY, 0.0); + GDALGeoLocRescale(papszGeolocationInfo, "PIXEL_STEP", 1.0 / dfRatioX, 1.0); + GDALGeoLocRescale(papszGeolocationInfo, "LINE_STEP", 1.0 / dfRatioY, 1.0); + } + + psInfo = (GDALGeoLocTransformInfo*) GDALCreateGeoLocTransformer( + NULL, papszGeolocationInfo, psInfo->bReversed ); + + CSLDestroy(papszGeolocationInfo); + + return psInfo; +} + +/************************************************************************/ +/* GDALCreateGeoLocTransformer() */ +/************************************************************************/ + +void *GDALCreateGeoLocTransformer( GDALDatasetH hBaseDS, + char **papszGeolocationInfo, + int bReversed ) + +{ + GDALGeoLocTransformInfo *psTransform; + + if( CSLFetchNameValue(papszGeolocationInfo,"PIXEL_OFFSET") == NULL + || CSLFetchNameValue(papszGeolocationInfo,"LINE_OFFSET") == NULL + || CSLFetchNameValue(papszGeolocationInfo,"PIXEL_STEP") == NULL + || CSLFetchNameValue(papszGeolocationInfo,"LINE_STEP") == NULL + || CSLFetchNameValue(papszGeolocationInfo,"X_BAND") == NULL + || CSLFetchNameValue(papszGeolocationInfo,"Y_BAND") == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing some geolocation fields in GDALCreateGeoLocTransformer()" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Initialize core info. */ +/* -------------------------------------------------------------------- */ + psTransform = (GDALGeoLocTransformInfo *) + CPLCalloc(sizeof(GDALGeoLocTransformInfo),1); + + psTransform->bReversed = bReversed; + + memcpy( psTransform->sTI.abySignature, GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE) ); + psTransform->sTI.pszClassName = "GDALGeoLocTransformer"; + psTransform->sTI.pfnTransform = GDALGeoLocTransform; + psTransform->sTI.pfnCleanup = GDALDestroyGeoLocTransformer; + psTransform->sTI.pfnSerialize = GDALSerializeGeoLocTransformer; + psTransform->sTI.pfnCreateSimilar = GDALCreateSimilarGeoLocTransformer; + + psTransform->papszGeolocationInfo = CSLDuplicate( papszGeolocationInfo ); + +/* -------------------------------------------------------------------- */ +/* Pull geolocation info from the options/metadata. */ +/* -------------------------------------------------------------------- */ + psTransform->dfPIXEL_OFFSET = CPLAtof(CSLFetchNameValue( papszGeolocationInfo, + "PIXEL_OFFSET" )); + psTransform->dfLINE_OFFSET = CPLAtof(CSLFetchNameValue( papszGeolocationInfo, + "LINE_OFFSET" )); + psTransform->dfPIXEL_STEP = CPLAtof(CSLFetchNameValue( papszGeolocationInfo, + "PIXEL_STEP" )); + psTransform->dfLINE_STEP = CPLAtof(CSLFetchNameValue( papszGeolocationInfo, + "LINE_STEP" )); + +/* -------------------------------------------------------------------- */ +/* Establish access to geolocation dataset(s). */ +/* -------------------------------------------------------------------- */ + const char *pszDSName = CSLFetchNameValue( papszGeolocationInfo, + "X_DATASET" ); + if( pszDSName != NULL ) + { + psTransform->hDS_X = GDALOpenShared( pszDSName, GA_ReadOnly ); + } + else + { + psTransform->hDS_X = hBaseDS; + GDALReferenceDataset( psTransform->hDS_X ); + psTransform->papszGeolocationInfo = + CSLSetNameValue( psTransform->papszGeolocationInfo, + "X_DATASET", + GDALGetDescription( hBaseDS ) ); + } + + pszDSName = CSLFetchNameValue( papszGeolocationInfo, "Y_DATASET" ); + if( pszDSName != NULL ) + { + psTransform->hDS_Y = GDALOpenShared( pszDSName, GA_ReadOnly ); + } + else + { + psTransform->hDS_Y = hBaseDS; + GDALReferenceDataset( psTransform->hDS_Y ); + psTransform->papszGeolocationInfo = + CSLSetNameValue( psTransform->papszGeolocationInfo, + "Y_DATASET", + GDALGetDescription( hBaseDS ) ); + } + + if (psTransform->hDS_X == NULL || + psTransform->hDS_Y == NULL) + { + GDALDestroyGeoLocTransformer( psTransform ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Get the band handles. */ +/* -------------------------------------------------------------------- */ + int nBand; + + nBand = MAX(1,atoi(CSLFetchNameValue( papszGeolocationInfo, "X_BAND" ))); + psTransform->hBand_X = GDALGetRasterBand( psTransform->hDS_X, nBand ); + + nBand = MAX(1,atoi(CSLFetchNameValue( papszGeolocationInfo, "Y_BAND" ))); + psTransform->hBand_Y = GDALGetRasterBand( psTransform->hDS_Y, nBand ); + + if (psTransform->hBand_X == NULL || + psTransform->hBand_Y == NULL) + { + GDALDestroyGeoLocTransformer( psTransform ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Check that X and Y bands have the same dimensions */ +/* -------------------------------------------------------------------- */ + int nXSize_XBand = GDALGetRasterXSize( psTransform->hDS_X ); + int nYSize_XBand = GDALGetRasterYSize( psTransform->hDS_X ); + int nXSize_YBand = GDALGetRasterXSize( psTransform->hDS_Y ); + int nYSize_YBand = GDALGetRasterYSize( psTransform->hDS_Y ); + if (nYSize_XBand == 1 || nYSize_YBand == 1) + { + if (nYSize_XBand != 1 || nYSize_YBand != 1) + { + CPLError(CE_Failure, CPLE_AppDefined, + "X_BAND and Y_BAND should have both nYSize == 1"); + GDALDestroyGeoLocTransformer( psTransform ); + return NULL; + } + } + else if (nXSize_XBand != nXSize_YBand || + nYSize_XBand != nYSize_YBand ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "X_BAND and Y_BAND do not have the same dimensions"); + GDALDestroyGeoLocTransformer( psTransform ); + return NULL; + } + + if (nXSize_XBand > INT_MAX / nYSize_XBand) + { + CPLError(CE_Failure, CPLE_AppDefined, "Int overflow : %d x %d", + nXSize_XBand, nYSize_XBand); + GDALDestroyGeoLocTransformer( psTransform ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Load the geolocation array. */ +/* -------------------------------------------------------------------- */ + if( !GeoLocLoadFullData( psTransform ) + || !GeoLocGenerateBackMap( psTransform ) ) + { + GDALDestroyGeoLocTransformer( psTransform ); + return NULL; + } + + return psTransform; +} + +/************************************************************************/ +/* GDALDestroyGeoLocTransformer() */ +/************************************************************************/ + +void GDALDestroyGeoLocTransformer( void *pTransformAlg ) + +{ + if( pTransformAlg == NULL ) + return; + + GDALGeoLocTransformInfo *psTransform = + (GDALGeoLocTransformInfo *) pTransformAlg; + + CPLFree( psTransform->pafBackMapX ); + CPLFree( psTransform->pafBackMapY ); + CSLDestroy( psTransform->papszGeolocationInfo ); + CPLFree( psTransform->padfGeoLocX ); + CPLFree( psTransform->padfGeoLocY ); + + if( psTransform->hDS_X != NULL + && GDALDereferenceDataset( psTransform->hDS_X ) == 0 ) + GDALClose( psTransform->hDS_X ); + + if( psTransform->hDS_Y != NULL + && GDALDereferenceDataset( psTransform->hDS_Y ) == 0 ) + GDALClose( psTransform->hDS_Y ); + + CPLFree( pTransformAlg ); +} + +/************************************************************************/ +/* GDALGeoLocTransform() */ +/************************************************************************/ + +int GDALGeoLocTransform( void *pTransformArg, + int bDstToSrc, + int nPointCount, + double *padfX, double *padfY, + CPL_UNUSED double *padfZ, + int *panSuccess ) +{ + GDALGeoLocTransformInfo *psTransform = + (GDALGeoLocTransformInfo *) pTransformArg; + + if( psTransform->bReversed ) + bDstToSrc = !bDstToSrc; + +/* -------------------------------------------------------------------- */ +/* Do original pixel line to target geox/geoy. */ +/* -------------------------------------------------------------------- */ + if( !bDstToSrc ) + { + int i, nXSize = psTransform->nGeoLocXSize; + + for( i = 0; i < nPointCount; i++ ) + { + if( padfX[i] == HUGE_VAL || padfY[i] == HUGE_VAL ) + { + panSuccess[i] = FALSE; + continue; + } + + double dfGeoLocPixel = (padfX[i] - psTransform->dfPIXEL_OFFSET) + / psTransform->dfPIXEL_STEP; + double dfGeoLocLine = (padfY[i] - psTransform->dfLINE_OFFSET) + / psTransform->dfLINE_STEP; + + int iX, iY; + + iX = MAX(0,(int) dfGeoLocPixel); + iX = MIN(iX,psTransform->nGeoLocXSize-1); + iY = MAX(0,(int) dfGeoLocLine); + iY = MIN(iY,psTransform->nGeoLocYSize-1); + + double *padfGLX = psTransform->padfGeoLocX + iX + iY * nXSize; + double *padfGLY = psTransform->padfGeoLocY + iX + iY * nXSize; + + if( psTransform->bHasNoData && + padfGLX[0] == psTransform->dfNoDataX ) + { + panSuccess[i] = FALSE; + padfX[i] = HUGE_VAL; + padfY[i] = HUGE_VAL; + continue; + } + + // This assumes infinite extension beyond borders of available + // data based on closest grid square. + + if( iX + 1 < psTransform->nGeoLocXSize && + iY + 1 < psTransform->nGeoLocYSize && + (!psTransform->bHasNoData || + (padfGLX[1] != psTransform->dfNoDataX && + padfGLX[nXSize] != psTransform->dfNoDataX && + padfGLX[nXSize + 1] != psTransform->dfNoDataX) )) + { + padfX[i] = (1 - (dfGeoLocLine -iY)) * (padfGLX[0] + (dfGeoLocPixel-iX) * (padfGLX[1] - padfGLX[0])) + + (dfGeoLocLine -iY) * (padfGLX[nXSize] + (dfGeoLocPixel-iX) * (padfGLX[nXSize+1] - padfGLX[nXSize])); + padfY[i] = (1 - (dfGeoLocLine -iY)) * (padfGLY[0] + (dfGeoLocPixel-iX) * (padfGLY[1] - padfGLY[0])) + + (dfGeoLocLine -iY) * (padfGLY[nXSize] + (dfGeoLocPixel-iX) * (padfGLY[nXSize+1] - padfGLY[nXSize])); + } + else if( iX + 1 < psTransform->nGeoLocXSize && + (!psTransform->bHasNoData || + padfGLX[1] != psTransform->dfNoDataX) ) + { + padfX[i] = padfGLX[0] + + (dfGeoLocPixel-iX) * (padfGLX[1] - padfGLX[0]); + padfY[i] = padfGLY[0] + + (dfGeoLocPixel-iX) * (padfGLY[1] - padfGLY[0]); + } + else if( iY + 1 < psTransform->nGeoLocYSize && + (!psTransform->bHasNoData || + padfGLX[nXSize] != psTransform->dfNoDataX) ) + { + padfX[i] = padfGLX[0] + + (dfGeoLocLine -iY) * (padfGLX[nXSize] - padfGLX[0]); + padfY[i] = padfGLY[0] + + (dfGeoLocLine -iY) * (padfGLY[nXSize] - padfGLY[0]); + } + else + { + padfX[i] = padfGLX[0]; + padfY[i] = padfGLY[0]; + } + + panSuccess[i] = TRUE; + } + } + +/* -------------------------------------------------------------------- */ +/* geox/geoy to pixel/line using backmap. */ +/* -------------------------------------------------------------------- */ + else + { + int i; + + for( i = 0; i < nPointCount; i++ ) + { + if( padfX[i] == HUGE_VAL || padfY[i] == HUGE_VAL ) + { + panSuccess[i] = FALSE; + continue; + } + + double dfBMX, dfBMY; + int iBMX, iBMY; + + dfBMX = ((padfX[i] - psTransform->adfBackMapGeoTransform[0]) + / psTransform->adfBackMapGeoTransform[1]); + dfBMY = ((padfY[i] - psTransform->adfBackMapGeoTransform[3]) + / psTransform->adfBackMapGeoTransform[5]); + + iBMX = (int) dfBMX; + iBMY = (int) dfBMY; + + int iBM = iBMX + iBMY * psTransform->nBackMapWidth; + + if( iBMX < 0 || iBMY < 0 + || iBMX >= psTransform->nBackMapWidth + || iBMY >= psTransform->nBackMapHeight + || psTransform->pafBackMapX[iBM] < 0 ) + { + panSuccess[i] = FALSE; + padfX[i] = HUGE_VAL; + padfY[i] = HUGE_VAL; + continue; + } + + float* pafBMX = psTransform->pafBackMapX + iBM; + float* pafBMY = psTransform->pafBackMapY + iBM; + + if( iBMX + 1 < psTransform->nBackMapWidth && + iBMY + 1 < psTransform->nBackMapHeight && + pafBMX[1] >=0 && pafBMX[psTransform->nBackMapWidth] >= 0 && + pafBMX[psTransform->nBackMapWidth+1] >= 0) + { + padfX[i] = (1-(dfBMY - iBMY)) * (pafBMX[0] + (dfBMX - iBMX) * (pafBMX[1] - pafBMX[0])) + + (dfBMY - iBMY) * (pafBMX[psTransform->nBackMapWidth] + + (dfBMX - iBMX) * (pafBMX[psTransform->nBackMapWidth+1] - pafBMX[psTransform->nBackMapWidth])); + padfY[i] = (1-(dfBMY - iBMY)) * (pafBMY[0] + (dfBMX - iBMX) * (pafBMY[1] - pafBMY[0])) + + (dfBMY - iBMY) * (pafBMY[psTransform->nBackMapWidth] + + (dfBMX - iBMX) * (pafBMY[psTransform->nBackMapWidth+1] - pafBMY[psTransform->nBackMapWidth])); + } + else if( iBMX + 1 < psTransform->nBackMapWidth && + pafBMX[1] >=0) + { + padfX[i] = pafBMX[0] + + (dfBMX - iBMX) * (pafBMX[1] - pafBMX[0]); + padfY[i] = pafBMY[0] + + (dfBMX - iBMX) * (pafBMY[1] - pafBMY[0]); + } + else if( iBMY + 1 < psTransform->nBackMapHeight && + pafBMX[psTransform->nBackMapWidth] >= 0 ) + { + padfX[i] = pafBMX[0] + + (dfBMY - iBMY) * (pafBMX[psTransform->nBackMapWidth] - pafBMX[0]); + padfY[i] = pafBMY[0] + + (dfBMY - iBMY) * (pafBMY[psTransform->nBackMapWidth] - pafBMY[0]); + } + else + { + padfX[i] = pafBMX[0]; + padfY[i] = pafBMY[0]; + } + panSuccess[i] = TRUE; + } + } + +/* -------------------------------------------------------------------- */ +/* geox/geoy to pixel/line using search algorithm. */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + else + { + int i; + int nStartX = -1, nStartY = -1; + +#ifdef SHAPE_DEBUG + hSHP = SHPCreate( "tracks.shp", SHPT_ARC ); + hDBF = DBFCreate( "tracks.dbf" ); + DBFAddField( hDBF, "GEOX", FTDouble, 10, 4 ); + DBFAddField( hDBF, "GEOY", FTDouble, 10, 4 ); +#endif + for( i = 0; i < nPointCount; i++ ) + { + double dfGeoLocX, dfGeoLocY; + + if( padfX[i] == HUGE_VAL || padfY[i] == HUGE_VAL ) + { + panSuccess[i] = FALSE; + continue; + } + + if( !FindGeoLocPosition( psTransform, padfX[i], padfY[i], + -1, -1, &dfGeoLocX, &dfGeoLocY ) ) + { + padfX[i] = HUGE_VAL; + padfY[i] = HUGE_VAL; + + panSuccess[i] = FALSE; + continue; + } + nStartX = (int) dfGeoLocX; + nStartY = (int) dfGeoLocY; + + padfX[i] = dfGeoLocX * psTransform->dfPIXEL_STEP + + psTransform->dfPIXEL_OFFSET; + padfY[i] = dfGeoLocY * psTransform->dfLINE_STEP + + psTransform->dfLINE_OFFSET; + + panSuccess[i] = TRUE; + } + +#ifdef SHAPE_DEBUG + if( hSHP != NULL ) + { + DBFClose( hDBF ); + hDBF = NULL; + + SHPClose( hSHP ); + hSHP = NULL; + } +#endif + } +#endif + + return TRUE; +} + +/************************************************************************/ +/* GDALSerializeGeoLocTransformer() */ +/************************************************************************/ + +CPLXMLNode *GDALSerializeGeoLocTransformer( void *pTransformArg ) + +{ + VALIDATE_POINTER1( pTransformArg, "GDALSerializeGeoLocTransformer", NULL ); + + CPLXMLNode *psTree; + GDALGeoLocTransformInfo *psInfo = + (GDALGeoLocTransformInfo *)(pTransformArg); + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "GeoLocTransformer" ); + +/* -------------------------------------------------------------------- */ +/* Serialize bReversed. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( + psTree, "Reversed", + CPLString().Printf( "%d", psInfo->bReversed ) ); + +/* -------------------------------------------------------------------- */ +/* geoloc metadata. */ +/* -------------------------------------------------------------------- */ + char **papszMD = psInfo->papszGeolocationInfo; + CPLXMLNode *psMD= CPLCreateXMLNode( psTree, CXT_Element, + "Metadata" ); + + for( int i = 0; papszMD != NULL && papszMD[i] != NULL; i++ ) + { + const char *pszRawValue; + char *pszKey; + CPLXMLNode *psMDI; + + pszRawValue = CPLParseNameValue( papszMD[i], &pszKey ); + + psMDI = CPLCreateXMLNode( psMD, CXT_Element, "MDI" ); + CPLSetXMLValue( psMDI, "#key", pszKey ); + CPLCreateXMLNode( psMDI, CXT_Text, pszRawValue ); + + CPLFree( pszKey ); + } + + return psTree; +} + +/************************************************************************/ +/* GDALDeserializeGeoLocTransformer() */ +/************************************************************************/ + +void *GDALDeserializeGeoLocTransformer( CPLXMLNode *psTree ) + +{ + void *pResult; + int bReversed; + char **papszMD = NULL; + CPLXMLNode *psMDI, *psMetadata; + +/* -------------------------------------------------------------------- */ +/* Collect metadata. */ +/* -------------------------------------------------------------------- */ + psMetadata = CPLGetXMLNode( psTree, "Metadata" ); + + if( psMetadata == NULL || + psMetadata->eType != CXT_Element + || !EQUAL(psMetadata->pszValue,"Metadata") ) + return NULL; + + for( psMDI = psMetadata->psChild; psMDI != NULL; + psMDI = psMDI->psNext ) + { + if( !EQUAL(psMDI->pszValue,"MDI") + || psMDI->eType != CXT_Element + || psMDI->psChild == NULL + || psMDI->psChild->psNext == NULL + || psMDI->psChild->eType != CXT_Attribute + || psMDI->psChild->psChild == NULL ) + continue; + + papszMD = + CSLSetNameValue( papszMD, + psMDI->psChild->psChild->pszValue, + psMDI->psChild->psNext->pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Get other flags. */ +/* -------------------------------------------------------------------- */ + bReversed = atoi(CPLGetXMLValue(psTree,"Reversed","0")); + +/* -------------------------------------------------------------------- */ +/* Generate transformation. */ +/* -------------------------------------------------------------------- */ + pResult = GDALCreateGeoLocTransformer( NULL, papszMD, bReversed ); + +/* -------------------------------------------------------------------- */ +/* Cleanup GCP copy. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( papszMD ); + + return pResult; +} diff --git a/bazaar/plugin/gdal/alg/gdalgrid.cpp b/bazaar/plugin/gdal/alg/gdalgrid.cpp new file mode 100644 index 000000000..c0a8e8ba1 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalgrid.cpp @@ -0,0 +1,2000 @@ +/****************************************************************************** + * $Id: gdalgrid.cpp 28459 2015-02-12 13:48:21Z rouault $ + * + * Project: GDAL Gridding API. + * Purpose: Implementation of GDAL scattered data gridder. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2007, Andrey Kiselev + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi.h" +#include "cpl_string.h" +#include "gdalgrid.h" +#include +#include +#include "cpl_multiproc.h" +#include "gdalgrid_priv.h" + +CPL_CVSID("$Id: gdalgrid.cpp 28459 2015-02-12 13:48:21Z rouault $"); + +#define TO_RADIANS (3.14159265358979323846 / 180.0) + +#ifndef DBL_MAX +# ifdef __DBL_MAX__ +# define DBL_MAX __DBL_MAX__ +# else +# define DBL_MAX 1.7976931348623157E+308 +# endif /* __DBL_MAX__ */ +#endif /* DBL_MAX */ + +/************************************************************************/ +/* GDALGridGetPointBounds() */ +/************************************************************************/ + +static void GDALGridGetPointBounds(const void* hFeature, CPLRectObj* pBounds) +{ + GDALGridPoint* psPoint = (GDALGridPoint*) hFeature; + GDALGridXYArrays* psXYArrays = psPoint->psXYArrays; + int i = psPoint->i; + double dfX = psXYArrays->padfX[i]; + double dfY = psXYArrays->padfY[i]; + pBounds->minx = dfX; + pBounds->miny = dfY; + pBounds->maxx = dfX; + pBounds->maxy = dfY; +}; + +/************************************************************************/ +/* GDALGridInverseDistanceToAPower() */ +/************************************************************************/ + +/** + * Inverse distance to a power. + * + * The Inverse Distance to a Power gridding method is a weighted average + * interpolator. You should supply the input arrays with the scattered data + * values including coordinates of every data point and output grid geometry. + * The function will compute interpolated value for the given position in + * output grid. + * + * For every grid node the resulting value \f$Z\f$ will be calculated using + * formula: + * + * \f[ + * Z=\frac{\sum_{i=1}^n{\frac{Z_i}{r_i^p}}}{\sum_{i=1}^n{\frac{1}{r_i^p}}} + * \f] + * + * where + *
    + *
  • \f$Z_i\f$ is a known value at point \f$i\f$, + *
  • \f$r_i\f$ is an Euclidean distance from the grid node + * to point \f$i\f$, + *
  • \f$p\f$ is a weighting power, + *
  • \f$n\f$ is a total number of points in search ellipse. + *
+ * + * In this method the weighting factor \f$w\f$ is + * + * \f[ + * w=\frac{1}{r^p} + * \f] + * + * @param poOptions Algorithm parameters. This should point to + * GDALGridInverseDistanceToAPowerOptions object. + * @param nPoints Number of elements in input arrays. + * @param padfX Input array of X coordinates. + * @param padfY Input array of Y coordinates. + * @param padfZ Input array of Z values. + * @param dfXPoint X coordinate of the point to compute. + * @param dfYPoint Y coordinate of the point to compute. + * @param pdfValue Pointer to variable where the computed grid node value + * will be returned. + * @param hExtraParamsIn extra parameters. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr +GDALGridInverseDistanceToAPower( const void *poOptions, GUInt32 nPoints, + const double *padfX, const double *padfY, + const double *padfZ, + double dfXPoint, double dfYPoint, + double *pdfValue, + CPL_UNUSED void* hExtraParamsIn ) +{ + // TODO: For optimization purposes pre-computed parameters should be moved + // out of this routine to the calling function. + + // Pre-compute search ellipse parameters + double dfRadius1 = + ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfRadius1; + double dfRadius2 = + ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfRadius2; + double dfR12; + + dfRadius1 *= dfRadius1; + dfRadius2 *= dfRadius2; + dfR12 = dfRadius1 * dfRadius2; + + // Compute coefficients for coordinate system rotation. + double dfCoeff1 = 0.0, dfCoeff2 = 0.0; + const double dfAngle = TO_RADIANS + * ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfAngle; + const bool bRotated = ( dfAngle == 0.0 ) ? false : true; + if ( bRotated ) + { + dfCoeff1 = cos(dfAngle); + dfCoeff2 = sin(dfAngle); + } + + const double dfPowerDiv2 = + ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfPower / 2; + const double dfSmoothing = + ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfSmoothing; + const GUInt32 nMaxPoints = + ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->nMaxPoints; + double dfNominator = 0.0, dfDenominator = 0.0; + GUInt32 i, n = 0; + + for ( i = 0; i < nPoints; i++ ) + { + double dfRX = padfX[i] - dfXPoint; + double dfRY = padfY[i] - dfYPoint; + const double dfR2 = + dfRX * dfRX + dfRY * dfRY + dfSmoothing * dfSmoothing; + + if ( bRotated ) + { + double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2; + double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2; + + dfRX = dfRXRotated; + dfRY = dfRYRotated; + } + + // Is this point located inside the search ellipse? + if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 ) + { + // If the test point is close to the grid node, use the point + // value directly as a node value to avoid singularity. + if ( dfR2 < 0.0000000000001 ) + { + (*pdfValue) = padfZ[i]; + return CE_None; + } + else + { + const double dfW = pow( dfR2, dfPowerDiv2 ); + double dfInvW = 1.0 / dfW; + dfNominator += dfInvW * padfZ[i]; + dfDenominator += dfInvW; + n++; + if ( nMaxPoints > 0 && n > nMaxPoints ) + break; + } + } + } + + if ( n < ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->nMinPoints + || dfDenominator == 0.0 ) + { + (*pdfValue) = + ((GDALGridInverseDistanceToAPowerOptions*)poOptions)->dfNoDataValue; + } + else + (*pdfValue) = dfNominator / dfDenominator; + + return CE_None; +} + +/************************************************************************/ +/* GDALGridInverseDistanceToAPowerNoSearch() */ +/************************************************************************/ + +/** + * Inverse distance to a power for whole data set. + * + * This is somewhat optimized version of the Inverse Distance to a Power + * method. It is used when the search ellips is not set. The algorithm and + * parameters are the same as in GDALGridInverseDistanceToAPower(), but this + * implementation works faster, because of no search. + * + * @see GDALGridInverseDistanceToAPower() + */ + +CPLErr +GDALGridInverseDistanceToAPowerNoSearch( const void *poOptions, GUInt32 nPoints, + const double *padfX, const double *padfY, + const double *padfZ, + double dfXPoint, double dfYPoint, + double *pdfValue, + CPL_UNUSED void* hExtraParamsIn ) +{ + const double dfPowerDiv2 = + ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfPower / 2; + const double dfSmoothing = + ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfSmoothing; + const double dfSmoothing2 = dfSmoothing * dfSmoothing; + double dfNominator = 0.0, dfDenominator = 0.0; + GUInt32 i = 0; + int bPower2 = (dfPowerDiv2 == 1.0); + + if( bPower2 ) + { + if( dfSmoothing2 > 0 ) + { + for ( i = 0; i < nPoints; i++ ) + { + const double dfRX = padfX[i] - dfXPoint; + const double dfRY = padfY[i] - dfYPoint; + const double dfR2 = + dfRX * dfRX + dfRY * dfRY + dfSmoothing2; + + double dfInvR2 = 1.0 / dfR2; + dfNominator += dfInvR2 * padfZ[i]; + dfDenominator += dfInvR2; + } + } + else + { + for ( i = 0; i < nPoints; i++ ) + { + const double dfRX = padfX[i] - dfXPoint; + const double dfRY = padfY[i] - dfYPoint; + const double dfR2 = + dfRX * dfRX + dfRY * dfRY; + + // If the test point is close to the grid node, use the point + // value directly as a node value to avoid singularity. + if ( dfR2 < 0.0000000000001 ) + { + break; + } + else + { + double dfInvR2 = 1.0 / dfR2; + dfNominator += dfInvR2 * padfZ[i]; + dfDenominator += dfInvR2; + } + } + } + } + else + { + for ( i = 0; i < nPoints; i++ ) + { + const double dfRX = padfX[i] - dfXPoint; + const double dfRY = padfY[i] - dfYPoint; + const double dfR2 = + dfRX * dfRX + dfRY * dfRY + dfSmoothing2; + + // If the test point is close to the grid node, use the point + // value directly as a node value to avoid singularity. + if ( dfR2 < 0.0000000000001 ) + { + break; + } + else + { + const double dfW = pow( dfR2, dfPowerDiv2 ); + double dfInvW = 1.0 / dfW; + dfNominator += dfInvW * padfZ[i]; + dfDenominator += dfInvW; + } + } + } + + if( i != nPoints ) + { + (*pdfValue) = padfZ[i]; + } + else + if ( dfDenominator == 0.0 ) + { + (*pdfValue) = + ((GDALGridInverseDistanceToAPowerOptions*)poOptions)->dfNoDataValue; + } + else + (*pdfValue) = dfNominator / dfDenominator; + + return CE_None; +} + +/************************************************************************/ +/* GDALGridMovingAverage() */ +/************************************************************************/ + +/** + * Moving average. + * + * The Moving Average is a simple data averaging algorithm. It uses a moving + * window of elliptic form to search values and averages all data points + * within the window. Search ellipse can be rotated by specified angle, the + * center of ellipse located at the grid node. Also the minimum number of data + * points to average can be set, if there are not enough points in window, the + * grid node considered empty and will be filled with specified NODATA value. + * + * Mathematically it can be expressed with the formula: + * + * \f[ + * Z=\frac{\sum_{i=1}^n{Z_i}}{n} + * \f] + * + * where + *
    + *
  • \f$Z\f$ is a resulting value at the grid node, + *
  • \f$Z_i\f$ is a known value at point \f$i\f$, + *
  • \f$n\f$ is a total number of points in search ellipse. + *
+ * + * @param poOptions Algorithm parameters. This should point to + * GDALGridMovingAverageOptions object. + * @param nPoints Number of elements in input arrays. + * @param padfX Input array of X coordinates. + * @param padfY Input array of Y coordinates. + * @param padfZ Input array of Z values. + * @param dfXPoint X coordinate of the point to compute. + * @param dfYPoint Y coordinate of the point to compute. + * @param pdfValue Pointer to variable where the computed grid node value + * will be returned. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr +GDALGridMovingAverage( const void *poOptions, GUInt32 nPoints, + const double *padfX, const double *padfY, + const double *padfZ, + double dfXPoint, double dfYPoint, double *pdfValue, + CPL_UNUSED void* hExtraParamsIn ) +{ + // TODO: For optimization purposes pre-computed parameters should be moved + // out of this routine to the calling function. + + // Pre-compute search ellipse parameters + double dfRadius1 = ((GDALGridMovingAverageOptions *)poOptions)->dfRadius1; + double dfRadius2 = ((GDALGridMovingAverageOptions *)poOptions)->dfRadius2; + double dfR12; + + dfRadius1 *= dfRadius1; + dfRadius2 *= dfRadius2; + dfR12 = dfRadius1 * dfRadius2; + + // Compute coefficients for coordinate system rotation. + double dfCoeff1 = 0.0, dfCoeff2 = 0.0; + const double dfAngle = + TO_RADIANS * ((GDALGridMovingAverageOptions *)poOptions)->dfAngle; + const bool bRotated = ( dfAngle == 0.0 ) ? false : true; + if ( bRotated ) + { + dfCoeff1 = cos(dfAngle); + dfCoeff2 = sin(dfAngle); + } + + double dfAccumulator = 0.0; + GUInt32 i = 0, n = 0; + + while ( i < nPoints ) + { + double dfRX = padfX[i] - dfXPoint; + double dfRY = padfY[i] - dfYPoint; + + if ( bRotated ) + { + double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2; + double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2; + + dfRX = dfRXRotated; + dfRY = dfRYRotated; + } + + // Is this point located inside the search ellipse? + if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 ) + { + dfAccumulator += padfZ[i]; + n++; + } + + i++; + } + + if ( n < ((GDALGridMovingAverageOptions *)poOptions)->nMinPoints + || n == 0 ) + { + (*pdfValue) = + ((GDALGridMovingAverageOptions *)poOptions)->dfNoDataValue; + } + else + (*pdfValue) = dfAccumulator / n; + + return CE_None; +} + +/************************************************************************/ +/* GDALGridNearestNeighbor() */ +/************************************************************************/ + +/** + * Nearest neighbor. + * + * The Nearest Neighbor method doesn't perform any interpolation or smoothing, + * it just takes the value of nearest point found in grid node search ellipse + * and returns it as a result. If there are no points found, the specified + * NODATA value will be returned. + * + * @param poOptions Algorithm parameters. This should point to + * GDALGridNearestNeighborOptions object. + * @param nPoints Number of elements in input arrays. + * @param padfX Input array of X coordinates. + * @param padfY Input array of Y coordinates. + * @param padfZ Input array of Z values. + * @param dfXPoint X coordinate of the point to compute. + * @param dfYPoint Y coordinate of the point to compute. + * @param pdfValue Pointer to variable where the computed grid node value + * will be returned. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr +GDALGridNearestNeighbor( const void *poOptions, GUInt32 nPoints, + const double *padfX, const double *padfY, + const double *padfZ, + double dfXPoint, double dfYPoint, double *pdfValue, + void* hExtraParamsIn) +{ + // TODO: For optimization purposes pre-computed parameters should be moved + // out of this routine to the calling function. + + // Pre-compute search ellipse parameters + double dfRadius1 = + ((GDALGridNearestNeighborOptions *)poOptions)->dfRadius1; + double dfRadius2 = + ((GDALGridNearestNeighborOptions *)poOptions)->dfRadius2; + double dfR12; + GDALGridExtraParameters* psExtraParams = (GDALGridExtraParameters*) hExtraParamsIn; + CPLQuadTree* hQuadTree = psExtraParams->hQuadTree; + + dfRadius1 *= dfRadius1; + dfRadius2 *= dfRadius2; + dfR12 = dfRadius1 * dfRadius2; + + // Compute coefficients for coordinate system rotation. + double dfCoeff1 = 0.0, dfCoeff2 = 0.0; + const double dfAngle = + TO_RADIANS * ((GDALGridNearestNeighborOptions *)poOptions)->dfAngle; + const bool bRotated = ( dfAngle == 0.0 ) ? false : true; + if ( bRotated ) + { + dfCoeff1 = cos(dfAngle); + dfCoeff2 = sin(dfAngle); + } + + // If the nearest point will not be found, its value remains as NODATA. + double dfNearestValue = + ((GDALGridNearestNeighborOptions *)poOptions)->dfNoDataValue; + // Nearest distance will be initialized with the distance to the first + // point in array. + double dfNearestR = DBL_MAX; + GUInt32 i = 0; + + double dfSearchRadius = psExtraParams->dfInitialSearchRadius; + if( hQuadTree != NULL && dfRadius1 == dfRadius2 && dfSearchRadius > 0 ) + { + CPLRectObj sAoi; + if( dfRadius1 > 0 ) + dfSearchRadius = ((GDALGridNearestNeighborOptions *)poOptions)->dfRadius1; + while(dfSearchRadius > 0) + { + sAoi.minx = dfXPoint - dfSearchRadius; + sAoi.miny = dfYPoint - dfSearchRadius; + sAoi.maxx = dfXPoint + dfSearchRadius; + sAoi.maxy = dfYPoint + dfSearchRadius; + int nFeatureCount = 0; + GDALGridPoint** papsPoints = (GDALGridPoint**) + CPLQuadTreeSearch(hQuadTree, &sAoi, &nFeatureCount); + if( nFeatureCount != 0 ) + { + if( dfRadius1 > 0 ) + dfNearestR = dfRadius1; + for(int k=0; ki; + double dfRX = padfX[i] - dfXPoint; + double dfRY = padfY[i] - dfYPoint; + + const double dfR2 = dfRX * dfRX + dfRY * dfRY; + if( dfR2 <= dfNearestR ) + { + dfNearestR = dfR2; + dfNearestValue = padfZ[i]; + } + } + + CPLFree(papsPoints); + break; + } + else + { + CPLFree(papsPoints); + if( dfRadius1 > 0 ) + break; + dfSearchRadius *= 2; + //CPLDebug("GDAL_GRID", "Increasing search radius to %.16g", dfSearchRadius); + } + } + } + else + { + while ( i < nPoints ) + { + double dfRX = padfX[i] - dfXPoint; + double dfRY = padfY[i] - dfYPoint; + + if ( bRotated ) + { + double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2; + double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2; + + dfRX = dfRXRotated; + dfRY = dfRYRotated; + } + + // Is this point located inside the search ellipse? + if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 ) + { + const double dfR2 = dfRX * dfRX + dfRY * dfRY; + if ( dfR2 <= dfNearestR ) + { + dfNearestR = dfR2; + dfNearestValue = padfZ[i]; + } + } + + i++; + } + } + + (*pdfValue) = dfNearestValue; + + return CE_None; +} + +/************************************************************************/ +/* GDALGridDataMetricMinimum() */ +/************************************************************************/ + +/** + * Minimum data value (data metric). + * + * Minimum value found in grid node search ellipse. If there are no points + * found, the specified NODATA value will be returned. + * + * \f[ + * Z=\min{(Z_1,Z_2,\ldots,Z_n)} + * \f] + * + * where + *
    + *
  • \f$Z\f$ is a resulting value at the grid node, + *
  • \f$Z_i\f$ is a known value at point \f$i\f$, + *
  • \f$n\f$ is a total number of points in search ellipse. + *
+ * + * @param poOptions Algorithm parameters. This should point to + * GDALGridDataMetricsOptions object. + * @param nPoints Number of elements in input arrays. + * @param padfX Input array of X coordinates. + * @param padfY Input array of Y coordinates. + * @param padfZ Input array of Z values. + * @param dfXPoint X coordinate of the point to compute. + * @param dfYPoint Y coordinate of the point to compute. + * @param pdfValue Pointer to variable where the computed grid node value + * will be returned. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr +GDALGridDataMetricMinimum( const void *poOptions, GUInt32 nPoints, + const double *padfX, const double *padfY, + const double *padfZ, + double dfXPoint, double dfYPoint, double *pdfValue, + CPL_UNUSED void* hExtraParamsIn ) +{ + // TODO: For optimization purposes pre-computed parameters should be moved + // out of this routine to the calling function. + + // Pre-compute search ellipse parameters + double dfRadius1 = + ((GDALGridDataMetricsOptions *)poOptions)->dfRadius1; + double dfRadius2 = + ((GDALGridDataMetricsOptions *)poOptions)->dfRadius2; + double dfR12; + + dfRadius1 *= dfRadius1; + dfRadius2 *= dfRadius2; + dfR12 = dfRadius1 * dfRadius2; + + // Compute coefficients for coordinate system rotation. + double dfCoeff1 = 0.0, dfCoeff2 = 0.0; + const double dfAngle = + TO_RADIANS * ((GDALGridDataMetricsOptions *)poOptions)->dfAngle; + const bool bRotated = ( dfAngle == 0.0 ) ? false : true; + if ( bRotated ) + { + dfCoeff1 = cos(dfAngle); + dfCoeff2 = sin(dfAngle); + } + + double dfMinimumValue=0.0; + GUInt32 i = 0, n = 0; + + while ( i < nPoints ) + { + double dfRX = padfX[i] - dfXPoint; + double dfRY = padfY[i] - dfYPoint; + + if ( bRotated ) + { + double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2; + double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2; + + dfRX = dfRXRotated; + dfRY = dfRYRotated; + } + + // Is this point located inside the search ellipse? + if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 ) + { + if ( n ) + { + if ( dfMinimumValue > padfZ[i] ) + dfMinimumValue = padfZ[i]; + } + else + dfMinimumValue = padfZ[i]; + n++; + } + + i++; + } + + if ( n < ((GDALGridDataMetricsOptions *)poOptions)->nMinPoints + || n == 0 ) + { + (*pdfValue) = + ((GDALGridDataMetricsOptions *)poOptions)->dfNoDataValue; + } + else + (*pdfValue) = dfMinimumValue; + + return CE_None; +} + +/************************************************************************/ +/* GDALGridDataMetricMaximum() */ +/************************************************************************/ + +/** + * Maximum data value (data metric). + * + * Maximum value found in grid node search ellipse. If there are no points + * found, the specified NODATA value will be returned. + * + * \f[ + * Z=\max{(Z_1,Z_2,\ldots,Z_n)} + * \f] + * + * where + *
    + *
  • \f$Z\f$ is a resulting value at the grid node, + *
  • \f$Z_i\f$ is a known value at point \f$i\f$, + *
  • \f$n\f$ is a total number of points in search ellipse. + *
+ * + * @param poOptions Algorithm parameters. This should point to + * GDALGridDataMetricsOptions object. + * @param nPoints Number of elements in input arrays. + * @param padfX Input array of X coordinates. + * @param padfY Input array of Y coordinates. + * @param padfZ Input array of Z values. + * @param dfXPoint X coordinate of the point to compute. + * @param dfYPoint Y coordinate of the point to compute. + * @param pdfValue Pointer to variable where the computed grid node value + * will be returned. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr +GDALGridDataMetricMaximum( const void *poOptions, GUInt32 nPoints, + const double *padfX, const double *padfY, + const double *padfZ, + double dfXPoint, double dfYPoint, double *pdfValue, + CPL_UNUSED void* hExtraParamsIn ) +{ + // TODO: For optimization purposes pre-computed parameters should be moved + // out of this routine to the calling function. + + // Pre-compute search ellipse parameters + double dfRadius1 = + ((GDALGridDataMetricsOptions *)poOptions)->dfRadius1; + double dfRadius2 = + ((GDALGridDataMetricsOptions *)poOptions)->dfRadius2; + double dfR12; + + dfRadius1 *= dfRadius1; + dfRadius2 *= dfRadius2; + dfR12 = dfRadius1 * dfRadius2; + + // Compute coefficients for coordinate system rotation. + double dfCoeff1 = 0.0, dfCoeff2 = 0.0; + const double dfAngle = + TO_RADIANS * ((GDALGridDataMetricsOptions *)poOptions)->dfAngle; + const bool bRotated = ( dfAngle == 0.0 ) ? false : true; + if ( bRotated ) + { + dfCoeff1 = cos(dfAngle); + dfCoeff2 = sin(dfAngle); + } + + double dfMaximumValue=0.0; + GUInt32 i = 0, n = 0; + + while ( i < nPoints ) + { + double dfRX = padfX[i] - dfXPoint; + double dfRY = padfY[i] - dfYPoint; + + if ( bRotated ) + { + double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2; + double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2; + + dfRX = dfRXRotated; + dfRY = dfRYRotated; + } + + // Is this point located inside the search ellipse? + if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 ) + { + if ( n ) + { + if ( dfMaximumValue < padfZ[i] ) + dfMaximumValue = padfZ[i]; + } + else + dfMaximumValue = padfZ[i]; + n++; + } + + i++; + } + + if ( n < ((GDALGridDataMetricsOptions *)poOptions)->nMinPoints + || n == 0 ) + { + (*pdfValue) = + ((GDALGridDataMetricsOptions *)poOptions)->dfNoDataValue; + } + else + (*pdfValue) = dfMaximumValue; + + return CE_None; +} + +/************************************************************************/ +/* GDALGridDataMetricRange() */ +/************************************************************************/ + +/** + * Data range (data metric). + * + * A difference between the minimum and maximum values found in grid node + * search ellipse. If there are no points found, the specified NODATA + * value will be returned. + * + * \f[ + * Z=\max{(Z_1,Z_2,\ldots,Z_n)}-\min{(Z_1,Z_2,\ldots,Z_n)} + * \f] + * + * where + *
    + *
  • \f$Z\f$ is a resulting value at the grid node, + *
  • \f$Z_i\f$ is a known value at point \f$i\f$, + *
  • \f$n\f$ is a total number of points in search ellipse. + *
+ * + * @param poOptions Algorithm parameters. This should point to + * GDALGridDataMetricsOptions object. + * @param nPoints Number of elements in input arrays. + * @param padfX Input array of X coordinates. + * @param padfY Input array of Y coordinates. + * @param padfZ Input array of Z values. + * @param dfXPoint X coordinate of the point to compute. + * @param dfYPoint Y coordinate of the point to compute. + * @param pdfValue Pointer to variable where the computed grid node value + * will be returned. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr +GDALGridDataMetricRange( const void *poOptions, GUInt32 nPoints, + const double *padfX, const double *padfY, + const double *padfZ, + double dfXPoint, double dfYPoint, double *pdfValue, + CPL_UNUSED void* hExtraParamsIn ) +{ + // TODO: For optimization purposes pre-computed parameters should be moved + // out of this routine to the calling function. + + // Pre-compute search ellipse parameters + double dfRadius1 = + ((GDALGridDataMetricsOptions *)poOptions)->dfRadius1; + double dfRadius2 = + ((GDALGridDataMetricsOptions *)poOptions)->dfRadius2; + double dfR12; + + dfRadius1 *= dfRadius1; + dfRadius2 *= dfRadius2; + dfR12 = dfRadius1 * dfRadius2; + + // Compute coefficients for coordinate system rotation. + double dfCoeff1 = 0.0, dfCoeff2 = 0.0; + const double dfAngle = + TO_RADIANS * ((GDALGridDataMetricsOptions *)poOptions)->dfAngle; + const bool bRotated = ( dfAngle == 0.0 ) ? false : true; + if ( bRotated ) + { + dfCoeff1 = cos(dfAngle); + dfCoeff2 = sin(dfAngle); + } + + double dfMaximumValue=0.0, dfMinimumValue=0.0; + GUInt32 i = 0, n = 0; + + while ( i < nPoints ) + { + double dfRX = padfX[i] - dfXPoint; + double dfRY = padfY[i] - dfYPoint; + + if ( bRotated ) + { + double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2; + double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2; + + dfRX = dfRXRotated; + dfRY = dfRYRotated; + } + + // Is this point located inside the search ellipse? + if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 ) + { + if ( n ) + { + if ( dfMinimumValue > padfZ[i] ) + dfMinimumValue = padfZ[i]; + if ( dfMaximumValue < padfZ[i] ) + dfMaximumValue = padfZ[i]; + } + else + dfMinimumValue = dfMaximumValue = padfZ[i]; + n++; + } + + i++; + } + + if ( n < ((GDALGridDataMetricsOptions *)poOptions)->nMinPoints + || n == 0 ) + { + (*pdfValue) = + ((GDALGridDataMetricsOptions *)poOptions)->dfNoDataValue; + } + else + (*pdfValue) = dfMaximumValue - dfMinimumValue; + + return CE_None; +} + +/************************************************************************/ +/* GDALGridDataMetricCount() */ +/************************************************************************/ + +/** + * Number of data points (data metric). + * + * A number of data points found in grid node search ellipse. + * + * \f[ + * Z=n + * \f] + * + * where + *
    + *
  • \f$Z\f$ is a resulting value at the grid node, + *
  • \f$n\f$ is a total number of points in search ellipse. + *
+ * + * @param poOptions Algorithm parameters. This should point to + * GDALGridDataMetricsOptions object. + * @param nPoints Number of elements in input arrays. + * @param padfX Input array of X coordinates. + * @param padfY Input array of Y coordinates. + * @param padfZ Input array of Z values. + * @param dfXPoint X coordinate of the point to compute. + * @param dfYPoint Y coordinate of the point to compute. + * @param pdfValue Pointer to variable where the computed grid node value + * will be returned. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr +GDALGridDataMetricCount( const void *poOptions, GUInt32 nPoints, + const double *padfX, const double *padfY, + CPL_UNUSED const double *padfZ, + double dfXPoint, double dfYPoint, double *pdfValue, + CPL_UNUSED void* hExtraParamsIn ) +{ + // TODO: For optimization purposes pre-computed parameters should be moved + // out of this routine to the calling function. + + // Pre-compute search ellipse parameters + double dfRadius1 = + ((GDALGridDataMetricsOptions *)poOptions)->dfRadius1; + double dfRadius2 = + ((GDALGridDataMetricsOptions *)poOptions)->dfRadius2; + double dfR12; + + dfRadius1 *= dfRadius1; + dfRadius2 *= dfRadius2; + dfR12 = dfRadius1 * dfRadius2; + + // Compute coefficients for coordinate system rotation. + double dfCoeff1 = 0.0, dfCoeff2 = 0.0; + const double dfAngle = + TO_RADIANS * ((GDALGridDataMetricsOptions *)poOptions)->dfAngle; + const bool bRotated = ( dfAngle == 0.0 ) ? false : true; + if ( bRotated ) + { + dfCoeff1 = cos(dfAngle); + dfCoeff2 = sin(dfAngle); + } + + GUInt32 i = 0, n = 0; + + while ( i < nPoints ) + { + double dfRX = padfX[i] - dfXPoint; + double dfRY = padfY[i] - dfYPoint; + + if ( bRotated ) + { + double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2; + double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2; + + dfRX = dfRXRotated; + dfRY = dfRYRotated; + } + + // Is this point located inside the search ellipse? + if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 ) + n++; + + i++; + } + + if ( n < ((GDALGridDataMetricsOptions *)poOptions)->nMinPoints ) + { + (*pdfValue) = + ((GDALGridDataMetricsOptions *)poOptions)->dfNoDataValue; + } + else + (*pdfValue) = (double)n; + + return CE_None; +} + +/************************************************************************/ +/* GDALGridDataMetricAverageDistance() */ +/************************************************************************/ + +/** + * Average distance (data metric). + * + * An average distance between the grid node (center of the search ellipse) + * and all of the data points found in grid node search ellipse. If there are + * no points found, the specified NODATA value will be returned. + * + * \f[ + * Z=\frac{\sum_{i = 1}^n r_i}{n} + * \f] + * + * where + *
    + *
  • \f$Z\f$ is a resulting value at the grid node, + *
  • \f$r_i\f$ is an Euclidean distance from the grid node + * to point \f$i\f$, + *
  • \f$n\f$ is a total number of points in search ellipse. + *
+ * + * @param poOptions Algorithm parameters. This should point to + * GDALGridDataMetricsOptions object. + * @param nPoints Number of elements in input arrays. + * @param padfX Input array of X coordinates. + * @param padfY Input array of Y coordinates. + * @param padfZ Input array of Z values. + * @param dfXPoint X coordinate of the point to compute. + * @param dfYPoint Y coordinate of the point to compute. + * @param pdfValue Pointer to variable where the computed grid node value + * will be returned. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr +GDALGridDataMetricAverageDistance( const void *poOptions, GUInt32 nPoints, + const double *padfX, const double *padfY, + CPL_UNUSED const double *padfZ, + double dfXPoint, double dfYPoint, + double *pdfValue, + CPL_UNUSED void* hExtraParamsIn ) +{ + // TODO: For optimization purposes pre-computed parameters should be moved + // out of this routine to the calling function. + + // Pre-compute search ellipse parameters + double dfRadius1 = + ((GDALGridDataMetricsOptions *)poOptions)->dfRadius1; + double dfRadius2 = + ((GDALGridDataMetricsOptions *)poOptions)->dfRadius2; + double dfR12; + + dfRadius1 *= dfRadius1; + dfRadius2 *= dfRadius2; + dfR12 = dfRadius1 * dfRadius2; + + // Compute coefficients for coordinate system rotation. + double dfCoeff1 = 0.0, dfCoeff2 = 0.0; + const double dfAngle = + TO_RADIANS * ((GDALGridDataMetricsOptions *)poOptions)->dfAngle; + const bool bRotated = ( dfAngle == 0.0 ) ? false : true; + if ( bRotated ) + { + dfCoeff1 = cos(dfAngle); + dfCoeff2 = sin(dfAngle); + } + + double dfAccumulator = 0.0; + GUInt32 i = 0, n = 0; + + while ( i < nPoints ) + { + double dfRX = padfX[i] - dfXPoint; + double dfRY = padfY[i] - dfYPoint; + + if ( bRotated ) + { + double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2; + double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2; + + dfRX = dfRXRotated; + dfRY = dfRYRotated; + } + + // Is this point located inside the search ellipse? + if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 ) + { + dfAccumulator += sqrt( dfRX * dfRX + dfRY * dfRY ); + n++; + } + + i++; + } + + if ( n < ((GDALGridDataMetricsOptions *)poOptions)->nMinPoints + || n == 0 ) + { + (*pdfValue) = + ((GDALGridDataMetricsOptions *)poOptions)->dfNoDataValue; + } + else + (*pdfValue) = dfAccumulator / n; + + return CE_None; +} + +/************************************************************************/ +/* GDALGridDataMetricAverageDistance() */ +/************************************************************************/ + +/** + * Average distance between points (data metric). + * + * An average distance between the data points found in grid node search + * ellipse. The distance between each pair of points within ellipse is + * calculated and average of all distances is set as a grid node value. If + * there are no points found, the specified NODATA value will be returned. + + * + * \f[ + * Z=\frac{\sum_{i = 1}^{n-1}\sum_{j=i+1}^{n} r_{ij}}{\left(n-1\right)\,n-\frac{n+{\left(n-1\right)}^{2}-1}{2}} + * \f] + * + * where + *
    + *
  • \f$Z\f$ is a resulting value at the grid node, + *
  • \f$r_{ij}\f$ is an Euclidean distance between points + * \f$i\f$ and \f$j\f$, + *
  • \f$n\f$ is a total number of points in search ellipse. + *
+ * + * @param poOptions Algorithm parameters. This should point to + * GDALGridDataMetricsOptions object. + * @param nPoints Number of elements in input arrays. + * @param padfX Input array of X coordinates. + * @param padfY Input array of Y coordinates. + * @param padfZ Input array of Z values. + * @param dfXPoint X coordinate of the point to compute. + * @param dfYPoint Y coordinate of the point to compute. + * @param pdfValue Pointer to variable where the computed grid node value + * will be returned. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr +GDALGridDataMetricAverageDistancePts( const void *poOptions, GUInt32 nPoints, + const double *padfX, const double *padfY, + CPL_UNUSED const double *padfZ, + double dfXPoint, double dfYPoint, + double *pdfValue, + CPL_UNUSED void* hExtraParamsIn ) +{ + // TODO: For optimization purposes pre-computed parameters should be moved + // out of this routine to the calling function. + + // Pre-compute search ellipse parameters + double dfRadius1 = + ((GDALGridDataMetricsOptions *)poOptions)->dfRadius1; + double dfRadius2 = + ((GDALGridDataMetricsOptions *)poOptions)->dfRadius2; + double dfR12; + + dfRadius1 *= dfRadius1; + dfRadius2 *= dfRadius2; + dfR12 = dfRadius1 * dfRadius2; + + // Compute coefficients for coordinate system rotation. + double dfCoeff1 = 0.0, dfCoeff2 = 0.0; + const double dfAngle = + TO_RADIANS * ((GDALGridDataMetricsOptions *)poOptions)->dfAngle; + const bool bRotated = ( dfAngle == 0.0 ) ? false : true; + if ( bRotated ) + { + dfCoeff1 = cos(dfAngle); + dfCoeff2 = sin(dfAngle); + } + + double dfAccumulator = 0.0; + GUInt32 i = 0, n = 0; + + // Search for the first point within the search ellipse + while ( i < nPoints - 1 ) + { + double dfRX1 = padfX[i] - dfXPoint; + double dfRY1 = padfY[i] - dfYPoint; + + if ( bRotated ) + { + double dfRXRotated = dfRX1 * dfCoeff1 + dfRY1 * dfCoeff2; + double dfRYRotated = dfRY1 * dfCoeff1 - dfRX1 * dfCoeff2; + + dfRX1 = dfRXRotated; + dfRY1 = dfRYRotated; + } + + // Is this point located inside the search ellipse? + if ( dfRadius2 * dfRX1 * dfRX1 + dfRadius1 * dfRY1 * dfRY1 <= dfR12 ) + { + GUInt32 j; + + // Search all the remaining points within the ellipse and compute + // distances between them and the first point + for ( j = i + 1; j < nPoints; j++ ) + { + double dfRX2 = padfX[j] - dfXPoint; + double dfRY2 = padfY[j] - dfYPoint; + + if ( bRotated ) + { + double dfRXRotated = dfRX2 * dfCoeff1 + dfRY2 * dfCoeff2; + double dfRYRotated = dfRY2 * dfCoeff1 - dfRX2 * dfCoeff2; + + dfRX2 = dfRXRotated; + dfRY2 = dfRYRotated; + } + + if ( dfRadius2 * dfRX2 * dfRX2 + dfRadius1 * dfRY2 * dfRY2 <= dfR12 ) + { + const double dfRX = padfX[j] - padfX[i]; + const double dfRY = padfY[j] - padfY[i]; + + dfAccumulator += sqrt( dfRX * dfRX + dfRY * dfRY ); + n++; + } + } + } + + i++; + } + + if ( n < ((GDALGridDataMetricsOptions *)poOptions)->nMinPoints + || n == 0 ) + { + (*pdfValue) = + ((GDALGridDataMetricsOptions *)poOptions)->dfNoDataValue; + } + else + (*pdfValue) = dfAccumulator / n; + + return CE_None; +} + +/************************************************************************/ +/* GDALGridJob */ +/************************************************************************/ + +typedef struct _GDALGridJob GDALGridJob; + +struct _GDALGridJob +{ + GUInt32 nYStart; + + GByte *pabyData; + GUInt32 nYStep; + GUInt32 nXSize; + GUInt32 nYSize; + double dfXMin; + double dfYMin; + double dfDeltaX; + double dfDeltaY; + GUInt32 nPoints; + const double *padfX; + const double *padfY; + const double *padfZ; + const void *poOptions; + GDALGridFunction pfnGDALGridMethod; + GDALGridExtraParameters* psExtraParameters; + int (*pfnProgress)(GDALGridJob* psJob); + GDALDataType eType; + + CPLJoinableThread *hThread; + volatile int *pnCounter; + volatile int *pbStop; + CPLCond *hCond; + CPLMutex *hCondMutex; + + GDALProgressFunc pfnRealProgress; + void *pRealProgressArg; +}; + +/************************************************************************/ +/* GDALGridProgressMultiThread() */ +/************************************************************************/ + +/* Return TRUE if the computation must be interrupted */ +static int GDALGridProgressMultiThread(GDALGridJob* psJob) +{ + CPLAcquireMutex(psJob->hCondMutex, 1.0); + (*(psJob->pnCounter)) ++; + CPLCondSignal(psJob->hCond); + int bStop = *(psJob->pbStop); + CPLReleaseMutex(psJob->hCondMutex); + + return bStop; +} + +/************************************************************************/ +/* GDALGridProgressMonoThread() */ +/************************************************************************/ + +/* Return TRUE if the computation must be interrupted */ +static int GDALGridProgressMonoThread(GDALGridJob* psJob) +{ + int nCounter = ++(*(psJob->pnCounter)); + if( !psJob->pfnRealProgress( (nCounter / (double) psJob->nYSize), + "", psJob->pRealProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + *(psJob->pbStop) = TRUE; + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* GDALGridJobProcess() */ +/************************************************************************/ + +static void GDALGridJobProcess(void* user_data) +{ + GDALGridJob* psJob = (GDALGridJob*)user_data; + GUInt32 nXPoint, nYPoint; + + const GUInt32 nYStart = psJob->nYStart; + const GUInt32 nYStep = psJob->nYStep; + GByte *pabyData = psJob->pabyData; + + const GUInt32 nXSize = psJob->nXSize; + const GUInt32 nYSize = psJob->nYSize; + const double dfXMin = psJob->dfXMin; + const double dfYMin = psJob->dfYMin; + const double dfDeltaX = psJob->dfDeltaX; + const double dfDeltaY = psJob->dfDeltaY; + GUInt32 nPoints = psJob->nPoints; + const double* padfX = psJob->padfX; + const double* padfY = psJob->padfY; + const double* padfZ = psJob->padfZ; + const void *poOptions = psJob->poOptions; + GDALGridFunction pfnGDALGridMethod = psJob->pfnGDALGridMethod; + GDALGridExtraParameters *psExtraParameters = psJob->psExtraParameters; + GDALDataType eType = psJob->eType; + int (*pfnProgress)(GDALGridJob* psJob) = psJob->pfnProgress; + + int nDataTypeSize = GDALGetDataTypeSize(eType) / 8; + int nLineSpace = nXSize * nDataTypeSize; + + /* -------------------------------------------------------------------- */ + /* Allocate a buffer of scanline size, fill it with gridded values */ + /* and use GDALCopyWords() to copy values into output data array with */ + /* appropriate data type conversion. */ + /* -------------------------------------------------------------------- */ + double *padfValues = (double *)VSIMalloc2( sizeof(double), nXSize ); + if( padfValues == NULL ) + { + *(psJob->pbStop) = TRUE; + pfnProgress(psJob); /* to notify the main thread */ + return; + } + + for ( nYPoint = nYStart; nYPoint < nYSize; nYPoint += nYStep ) + { + const double dfYPoint = dfYMin + ( nYPoint + 0.5 ) * dfDeltaY; + + for ( nXPoint = 0; nXPoint < nXSize; nXPoint++ ) + { + const double dfXPoint = dfXMin + ( nXPoint + 0.5 ) * dfDeltaX; + + if ( (*pfnGDALGridMethod)( poOptions, nPoints, padfX, padfY, padfZ, + dfXPoint, dfYPoint, + padfValues + nXPoint, psExtraParameters ) != CE_None ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Gridding failed at X position %lu, Y position %lu", + (long unsigned int)nXPoint, + (long unsigned int)nYPoint ); + *(psJob->pbStop) = TRUE; + pfnProgress(psJob); /* to notify the main thread */ + break; + } + } + + GDALCopyWords( padfValues, GDT_Float64, sizeof(double), + pabyData + nYPoint * nLineSpace, eType, nDataTypeSize, + nXSize ); + + if( *(psJob->pbStop) || pfnProgress(psJob) ) + break; + } + + CPLFree(padfValues); +} + +/************************************************************************/ +/* GDALGridCreate() */ +/************************************************************************/ + +/** + * Create regular grid from the scattered data. + * + * This function takes the arrays of X and Y coordinates and corresponding Z + * values as input and computes regular grid (or call it a raster) from these + * scattered data. You should supply geometry and extent of the output grid + * and allocate array sufficient to hold such a grid. + * + * Starting with GDAL 1.10, it is possible to set the GDAL_NUM_THREADS + * configuration option to parallelize the processing. The value to set is + * the number of worker threads, or ALL_CPUS to use all the cores/CPUs of the + * computer (default value). + * + * Starting with GDAL 1.10, on Intel/AMD i386/x86_64 architectures, some + * gridding methods will be optimized with SSE instructions (provided GDAL + * has been compiled with such support, and it is availabable at runtime). + * Currently, only 'invdist' algorithm with default parameters has an optimized + * implementation. + * This can provide substantial speed-up, but sometimes at the expense of + * reduced floating point precision. This can be disabled by setting the + * GDAL_USE_SSE configuration option to NO. + * Starting with GDAL 1.11, a further optimized version can use the AVX + * instruction set. This can be disabled by setting the GDAL_USE_AVX + * configuration option to NO. + * + * + * @param eAlgorithm Gridding method. + * @param poOptions Options to control choosen gridding method. + * @param nPoints Number of elements in input arrays. + * @param padfX Input array of X coordinates. + * @param padfY Input array of Y coordinates. + * @param padfZ Input array of Z values. + * @param dfXMin Lowest X border of output grid. + * @param dfXMax Highest X border of output grid. + * @param dfYMin Lowest Y border of output grid. + * @param dfYMax Highest Y border of output grid. + * @param nXSize Number of columns in output grid. + * @param nYSize Number of rows in output grid. + * @param eType Data type of output array. + * @param pData Pointer to array where the computed grid will be stored. + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr +GDALGridCreate( GDALGridAlgorithm eAlgorithm, const void *poOptions, + GUInt32 nPoints, + const double *padfX, const double *padfY, const double *padfZ, + double dfXMin, double dfXMax, double dfYMin, double dfYMax, + GUInt32 nXSize, GUInt32 nYSize, GDALDataType eType, void *pData, + GDALProgressFunc pfnProgress, void *pProgressArg ) +{ + CPLAssert( poOptions ); + CPLAssert( padfX ); + CPLAssert( padfY ); + CPLAssert( padfZ ); + CPLAssert( pData ); + + if ( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + if ( nXSize == 0 || nYSize == 0 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Output raster dimesions should have non-zero size." ); + return CE_Failure; + } + + GDALGridFunction pfnGDALGridMethod; + int bCreateQuadTree = FALSE; + + /* Potentially unaligned pointers */ + void* pabyX = NULL; + void* pabyY = NULL; + void* pabyZ = NULL; + + /* Starting address aligned on 16-byte boundary */ + float* pafXAligned = NULL; + float* pafYAligned = NULL; + float* pafZAligned = NULL; + + switch ( eAlgorithm ) + { + case GGA_InverseDistanceToAPower: + if ( ((GDALGridInverseDistanceToAPowerOptions *)poOptions)-> + dfRadius1 == 0.0 && + ((GDALGridInverseDistanceToAPowerOptions *)poOptions)-> + dfRadius2 == 0.0 ) + { + const double dfPower = + ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfPower; + const double dfSmoothing = + ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfSmoothing; + + pfnGDALGridMethod = GDALGridInverseDistanceToAPowerNoSearch; + if( dfPower == 2.0 && dfSmoothing == 0.0 ) + { +#ifdef HAVE_AVX_AT_COMPILE_TIME + +#define ALIGN32(x) (((char*)(x)) + ((32 - (((size_t)(x)) % 32)) % 32)) + + if( CSLTestBoolean(CPLGetConfigOption("GDAL_USE_AVX", "YES")) && + CPLHaveRuntimeAVX() ) + { + pabyX = (float*)VSIMalloc(sizeof(float) * nPoints + 31); + pabyY = (float*)VSIMalloc(sizeof(float) * nPoints + 31); + pabyZ = (float*)VSIMalloc(sizeof(float) * nPoints + 31); + if( pabyX != NULL && pabyY != NULL && pabyZ != NULL) + { + CPLDebug("GDAL_GRID", "Using AVX optimized version"); + pafXAligned = (float*) ALIGN32(pabyX); + pafYAligned = (float*) ALIGN32(pabyY); + pafZAligned = (float*) ALIGN32(pabyZ); + pfnGDALGridMethod = GDALGridInverseDistanceToAPower2NoSmoothingNoSearchAVX; + GUInt32 i; + for(i=0;i 100 && + (((GDALGridNearestNeighborOptions *)poOptions)->dfRadius1 == + ((GDALGridNearestNeighborOptions *)poOptions)->dfRadius2)); + break; + + case GGA_MetricMinimum: + pfnGDALGridMethod = GDALGridDataMetricMinimum; + break; + + case GGA_MetricMaximum: + pfnGDALGridMethod = GDALGridDataMetricMaximum; + break; + + case GGA_MetricRange: + pfnGDALGridMethod = GDALGridDataMetricRange; + break; + + case GGA_MetricCount: + pfnGDALGridMethod = GDALGridDataMetricCount; + break; + + case GGA_MetricAverageDistance: + pfnGDALGridMethod = GDALGridDataMetricAverageDistance; + break; + + case GGA_MetricAverageDistancePts: + pfnGDALGridMethod = GDALGridDataMetricAverageDistancePts; + break; + + default: + CPLError( CE_Failure, CPLE_IllegalArg, + "GDAL does not support gridding method %d", eAlgorithm ); + return CE_Failure; + } + + const double dfDeltaX = ( dfXMax - dfXMin ) / nXSize; + const double dfDeltaY = ( dfYMax - dfYMin ) / nYSize; + +/* -------------------------------------------------------------------- */ +/* Create quadtree if requested and possible. */ +/* -------------------------------------------------------------------- */ + CPLQuadTree* hQuadTree = NULL; + double dfInitialSearchRadius = 0; + GDALGridPoint* pasGridPoints = NULL; + GDALGridXYArrays sXYArrays; + sXYArrays.padfX = padfX; + sXYArrays.padfY = padfY; + + if( bCreateQuadTree ) + { + pasGridPoints = (GDALGridPoint*) VSIMalloc2(nPoints, sizeof(GDALGridPoint)); + if( pasGridPoints != NULL ) + { + CPLRectObj sRect; + GUInt32 i; + + /* Determine point extents */ + sRect.minx = padfX[0]; + sRect.miny = padfY[0]; + sRect.maxx = padfX[0]; + sRect.maxy = padfY[0]; + for(i = 1; i < nPoints; i++) + { + if( padfX[i] < sRect.minx ) sRect.minx = padfX[i]; + if( padfY[i] < sRect.miny ) sRect.miny = padfY[i]; + if( padfX[i] > sRect.maxx ) sRect.maxx = padfX[i]; + if( padfY[i] > sRect.maxy ) sRect.maxy = padfY[i]; + } + + /* Initial value for search radius is the typical dimension of a */ + /* "pixel" of the point array (assuming rather uniform distribution) */ + dfInitialSearchRadius = sqrt((sRect.maxx - sRect.minx) * + (sRect.maxy - sRect.miny) / nPoints); + + hQuadTree = CPLQuadTreeCreate(&sRect, GDALGridGetPointBounds ); + + for(i = 0; i < nPoints; i++) + { + pasGridPoints[i].psXYArrays = &sXYArrays; + pasGridPoints[i].i = i; + CPLQuadTreeInsert(hQuadTree, pasGridPoints + i); + } + } + } + + + GDALGridExtraParameters sExtraParameters; + + sExtraParameters.hQuadTree = hQuadTree; + sExtraParameters.dfInitialSearchRadius = dfInitialSearchRadius; + sExtraParameters.pafX = pafXAligned; + sExtraParameters.pafY = pafYAligned; + sExtraParameters.pafZ = pafZAligned; + + const char* pszThreads = CPLGetConfigOption("GDAL_NUM_THREADS", "ALL_CPUS"); + int nThreads; + if (EQUAL(pszThreads, "ALL_CPUS")) + nThreads = CPLGetNumCPUs(); + else + nThreads = atoi(pszThreads); + if (nThreads > 128) + nThreads = 128; + if (nThreads >= (int)nYSize / 2) + nThreads = (int)nYSize / 2; + + volatile int nCounter = 0; + volatile int bStop = FALSE; + + GDALGridJob sJob; + sJob.nYStart = 0; + sJob.pabyData = (GByte*) pData; + sJob.nYStep = 1; + sJob.nXSize = nXSize; + sJob.nYSize = nYSize; + sJob.dfXMin = dfXMin; + sJob.dfYMin = dfYMin; + sJob.dfDeltaX = dfDeltaX; + sJob.dfDeltaY = dfDeltaY; + sJob.nPoints = nPoints; + sJob.padfX = padfX; + sJob.padfY = padfY; + sJob.padfZ = padfZ; + sJob.poOptions = poOptions; + sJob.pfnGDALGridMethod = pfnGDALGridMethod; + sJob.psExtraParameters = &sExtraParameters; + sJob.pfnProgress = NULL; + sJob.eType = eType; + sJob.pfnRealProgress = pfnProgress; + sJob.pRealProgressArg = pProgressArg; + sJob.pnCounter = &nCounter; + sJob.pbStop = &bStop; + sJob.hCond = NULL; + sJob.hCondMutex = NULL; + sJob.hThread = NULL; + + if( nThreads > 1 ) + { + sJob.hCond = CPLCreateCond(); + if( sJob.hCond == NULL ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot create condition. Reverting to monothread processing"); + nThreads = 1; + } + } + + if( nThreads <= 1 ) + { + sJob.pfnProgress = GDALGridProgressMonoThread; + + GDALGridJobProcess(&sJob); + } + else + { + GDALGridJob* pasJobs = (GDALGridJob*) CPLMalloc(sizeof(GDALGridJob) * nThreads); + int i; + + CPLDebug("GDAL_GRID", "Using %d threads", nThreads); + + sJob.nYStep = nThreads; + sJob.hCondMutex = CPLCreateMutex(); /* and take implicitely the mutex */ + sJob.pfnProgress = GDALGridProgressMultiThread; + +/* -------------------------------------------------------------------- */ +/* Start threads. */ +/* -------------------------------------------------------------------- */ + for(i = 0; i < nThreads && !bStop; i++) + { + memcpy(&pasJobs[i], &sJob, sizeof(GDALGridJob)); + pasJobs[i].nYStart = i; + pasJobs[i].hThread = CPLCreateJoinableThread( GDALGridJobProcess, + (void*) &pasJobs[i] ); + } + +/* -------------------------------------------------------------------- */ +/* Report progress. */ +/* -------------------------------------------------------------------- */ + while(nCounter < (int)nYSize && !bStop) + { + CPLCondWait(sJob.hCond, sJob.hCondMutex); + + int nLocalCounter = nCounter; + CPLReleaseMutex(sJob.hCondMutex); + + if( !pfnProgress( nLocalCounter / (double) nYSize, "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + bStop = TRUE; + } + + CPLAcquireMutex(sJob.hCondMutex, 1.0); + } + + /* Release mutex before joining threads, otherwise they will dead-lock */ + /* forever in GDALGridProgressMultiThread() */ + CPLReleaseMutex(sJob.hCondMutex); + +/* -------------------------------------------------------------------- */ +/* Wait for all threads to complete and finish. */ +/* -------------------------------------------------------------------- */ + for(i=0;i + dfPower = (pszValue) ? CPLAtofM(pszValue) : 2.0; + + pszValue = CSLFetchNameValue( papszParms, "smoothing" ); + ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)-> + dfSmoothing = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "radius1" ); + ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)-> + dfRadius1 = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "radius2" ); + ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)-> + dfRadius2 = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "angle" ); + ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)-> + dfAngle = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "max_points" ); + ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)-> + nMaxPoints = (GUInt32) ((pszValue) ? CPLAtofM(pszValue) : 0); + + pszValue = CSLFetchNameValue( papszParms, "min_points" ); + ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)-> + nMinPoints = (GUInt32) ((pszValue) ? CPLAtofM(pszValue) : 0); + + pszValue = CSLFetchNameValue( papszParms, "nodata" ); + ((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)-> + dfNoDataValue = (pszValue) ? CPLAtofM(pszValue) : 0.0; + break; + + case GGA_MovingAverage: + *ppOptions = + CPLMalloc( sizeof(GDALGridMovingAverageOptions) ); + + pszValue = CSLFetchNameValue( papszParms, "radius1" ); + ((GDALGridMovingAverageOptions *)*ppOptions)-> + dfRadius1 = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "radius2" ); + ((GDALGridMovingAverageOptions *)*ppOptions)-> + dfRadius2 = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "angle" ); + ((GDALGridMovingAverageOptions *)*ppOptions)-> + dfAngle = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "min_points" ); + ((GDALGridMovingAverageOptions *)*ppOptions)-> + nMinPoints = (GUInt32) ((pszValue) ? CPLAtofM(pszValue) : 0); + + pszValue = CSLFetchNameValue( papszParms, "nodata" ); + ((GDALGridMovingAverageOptions *)*ppOptions)-> + dfNoDataValue = (pszValue) ? CPLAtofM(pszValue) : 0.0; + break; + + case GGA_NearestNeighbor: + *ppOptions = + CPLMalloc( sizeof(GDALGridNearestNeighborOptions) ); + + pszValue = CSLFetchNameValue( papszParms, "radius1" ); + ((GDALGridNearestNeighborOptions *)*ppOptions)-> + dfRadius1 = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "radius2" ); + ((GDALGridNearestNeighborOptions *)*ppOptions)-> + dfRadius2 = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "angle" ); + ((GDALGridNearestNeighborOptions *)*ppOptions)-> + dfAngle = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "nodata" ); + ((GDALGridNearestNeighborOptions *)*ppOptions)-> + dfNoDataValue = (pszValue) ? CPLAtofM(pszValue) : 0.0; + break; + + case GGA_MetricMinimum: + case GGA_MetricMaximum: + case GGA_MetricRange: + case GGA_MetricCount: + case GGA_MetricAverageDistance: + case GGA_MetricAverageDistancePts: + *ppOptions = + CPLMalloc( sizeof(GDALGridDataMetricsOptions) ); + + pszValue = CSLFetchNameValue( papszParms, "radius1" ); + ((GDALGridDataMetricsOptions *)*ppOptions)-> + dfRadius1 = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "radius2" ); + ((GDALGridDataMetricsOptions *)*ppOptions)-> + dfRadius2 = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "angle" ); + ((GDALGridDataMetricsOptions *)*ppOptions)-> + dfAngle = (pszValue) ? CPLAtofM(pszValue) : 0.0; + + pszValue = CSLFetchNameValue( papszParms, "min_points" ); + ((GDALGridDataMetricsOptions *)*ppOptions)-> + nMinPoints = (pszValue) ? atol(pszValue) : 0; + + pszValue = CSLFetchNameValue( papszParms, "nodata" ); + ((GDALGridDataMetricsOptions *)*ppOptions)-> + dfNoDataValue = (pszValue) ? CPLAtofM(pszValue) : 0.0; + break; + + } + + CSLDestroy( papszParms ); + return CE_None; +} diff --git a/bazaar/plugin/gdal/alg/gdalgrid.h b/bazaar/plugin/gdal/alg/gdalgrid.h new file mode 100644 index 000000000..dbd814fe2 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalgrid.h @@ -0,0 +1,122 @@ +/****************************************************************************** + * $Id: gdalgrid.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GDAL Gridding API. + * Purpose: Prototypes, and definitions for of GDAL scattered data gridder. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2007, Andrey Kiselev + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDALGRID_H_INCLUDED +#define GDALGRID_H_INCLUDED + +/** + * \file gdalgrid.h + * + * GDAL gridder related entry points and definitions. + */ + +#include "gdal_alg.h" + +/* + * GridCreate Algorithm names + */ + +static const char szAlgNameInvDist[] = "invdist"; +static const char szAlgNameAverage[] = "average"; +static const char szAlgNameNearest[] = "nearest"; +static const char szAlgNameMinimum[] = "minimum"; +static const char szAlgNameMaximum[] = "maximum"; +static const char szAlgNameRange[] = "range"; +static const char szAlgNameCount[] = "count"; +static const char szAlgNameAverageDistance[] = "average_distance"; +static const char szAlgNameAverageDistancePts[] = "average_distance_pts"; + +CPL_C_START + +typedef CPLErr (*GDALGridFunction)( const void *, GUInt32, + const double *, const double *, + const double *, + double, double, double *, + void* ); +CPLErr +GDALGridInverseDistanceToAPower( const void *, GUInt32, + const double *, const double *, + const double *, + double, double, double *, + void* ); +CPLErr +GDALGridInverseDistanceToAPowerNoSearch( const void *, GUInt32, + const double *, const double *, + const double *, + double, double, double *, + void* ); +CPLErr +GDALGridMovingAverage( const void *, GUInt32, + const double *, const double *, const double *, + double, double, double *, + void* ); +CPLErr +GDALGridNearestNeighbor( const void *, GUInt32, + const double *, const double *, const double *, + double, double, double *, + void* ); +CPLErr +GDALGridDataMetricMinimum( const void *, GUInt32, + const double *, const double *, const double *, + double, double, double *, + void* ); +CPLErr +GDALGridDataMetricMaximum( const void *, GUInt32, + const double *, const double *, const double *, + double, double, double *, + void* ); +CPLErr +GDALGridDataMetricRange( const void *, GUInt32, + const double *, const double *, const double *, + double, double, double *, + void* ); +CPLErr +GDALGridDataMetricCount( const void *, GUInt32, + const double *, const double *, const double *, + double, double, double *, + void* ); +CPLErr +GDALGridDataMetricAverageDistance( const void *, GUInt32, + const double *, const double *, + const double *, double, double, double *, + void* ); +CPLErr +GDALGridDataMetricAverageDistancePts( const void *, GUInt32, + const double *, const double *, + const double *, double, double, + double *, + void* ); +CPLErr CPL_DLL +ParseAlgorithmAndOptions( const char *, + GDALGridAlgorithm *, + void ** ); +CPL_C_END + +#endif /* GDALGRID_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/alg/gdalgrid_priv.h b/bazaar/plugin/gdal/alg/gdalgrid_priv.h new file mode 100644 index 000000000..fd61c34de --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalgrid_priv.h @@ -0,0 +1,99 @@ +/****************************************************************************** + * $Id: gdalgrid_priv.h 29314 2015-06-05 20:21:11Z rouault $ + * + * Project: GDAL Gridding API. + * Purpose: Prototypes, and definitions for of GDAL scattered data gridder. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_error.h" +#include "cpl_quad_tree.h" + +typedef struct +{ + const double* padfX; + const double* padfY; +} GDALGridXYArrays; + +typedef struct +{ + GDALGridXYArrays* psXYArrays; + int i; +} GDALGridPoint; + +typedef struct +{ + CPLQuadTree* hQuadTree; + double dfInitialSearchRadius; + const float *pafX; + const float *pafY; + const float *pafZ; +} GDALGridExtraParameters; + +#ifdef HAVE_SSE_AT_COMPILE_TIME +int CPLHaveRuntimeSSE(); + +CPLErr +GDALGridInverseDistanceToAPower2NoSmoothingNoSearchSSE( + const void *poOptions, + GUInt32 nPoints, + const double *unused_padfX, + const double *unused_padfY, + const double *unused_padfZ, + double dfXPoint, double dfYPoint, + double *pdfValue, + void* hExtraParamsIn ); +#endif + +#ifdef HAVE_AVX_AT_COMPILE_TIME +int CPLHaveRuntimeAVX(); + +CPLErr GDALGridInverseDistanceToAPower2NoSmoothingNoSearchAVX( + const void *poOptions, + GUInt32 nPoints, + const double *unused_padfX, + const double *unused_padfY, + const double *unused_padfZ, + double dfXPoint, double dfYPoint, + double *pdfValue, + void* hExtraParamsIn ); +#endif + +#if defined(__GNUC__) +#if defined(__x86_64) +#define GCC_CPUID(level, a, b, c, d) \ + __asm__ ("xchgq %%rbx, %q1\n" \ + "cpuid\n" \ + "xchgq %%rbx, %q1" \ + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ + : "0" (level)) +#else +#define GCC_CPUID(level, a, b, c, d) \ + __asm__ ("xchgl %%ebx, %1\n" \ + "cpuid\n" \ + "xchgl %%ebx, %1" \ + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ + : "0" (level)) +#endif +#endif diff --git a/bazaar/plugin/gdal/alg/gdalgridavx.cpp b/bazaar/plugin/gdal/alg/gdalgridavx.cpp new file mode 100644 index 000000000..528388dd5 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalgridavx.cpp @@ -0,0 +1,225 @@ +/****************************************************************************** + * $Id: gdalgridavx.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: GDAL Gridding API. + * Purpose: Implementation of GDAL scattered data gridder. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdalgrid.h" +#include "gdalgrid_priv.h" + +#ifdef HAVE_AVX_AT_COMPILE_TIME +#include + +CPL_CVSID("$Id: gdalgridavx.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* CPLHaveRuntimeAVX() */ +/************************************************************************/ + +#define CPUID_OSXSAVE_ECX_BIT 27 +#define CPUID_AVX_ECX_BIT 28 + +#define BIT_XMM_STATE (1 << 1) +#define BIT_YMM_STATE (2 << 1) + +int CPLHaveRuntimeAVX() +{ + int cpuinfo[4] = {0,0,0,0}; + GCC_CPUID(1, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]); + + /* Check OSXSAVE feature */ + if( (cpuinfo[2] & (1 << CPUID_OSXSAVE_ECX_BIT)) == 0 ) + { + return FALSE; + } + + /* Check AVX feature */ + if( (cpuinfo[2] & (1 << CPUID_AVX_ECX_BIT)) == 0 ) + { + return FALSE; + } + + /* Issue XGETBV and check the XMM and YMM state bit */ + unsigned int nXCRLow; + unsigned int nXCRHigh; + __asm__ ("xgetbv" : "=a" (nXCRLow), "=d" (nXCRHigh) : "c" (0)); + if( (nXCRLow & ( BIT_XMM_STATE | BIT_YMM_STATE )) != + ( BIT_XMM_STATE | BIT_YMM_STATE ) ) + { + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* GDALGridInverseDistanceToAPower2NoSmoothingNoSearchAVX() */ +/************************************************************************/ + +#define GDAL_mm256_load1_ps(x) _mm256_set_ps(x, x, x, x, x, x, x, x) + +CPLErr +GDALGridInverseDistanceToAPower2NoSmoothingNoSearchAVX( + const void *poOptions, + GUInt32 nPoints, + CPL_UNUSED const double *unused_padfX, + CPL_UNUSED const double *unused_padfY, + CPL_UNUSED const double *unused_padfZ, + double dfXPoint, double dfYPoint, + double *pdfValue, + void* hExtraParamsIn ) +{ + size_t i = 0; + GDALGridExtraParameters* psExtraParams = (GDALGridExtraParameters*) hExtraParamsIn; + const float* pafX = psExtraParams->pafX; + const float* pafY = psExtraParams->pafY; + const float* pafZ = psExtraParams->pafZ; + + const float fEpsilon = 0.0000000000001f; + const float fXPoint = (float)dfXPoint; + const float fYPoint = (float)dfYPoint; + const __m256 ymm_small = GDAL_mm256_load1_ps(fEpsilon); + const __m256 ymm_x = GDAL_mm256_load1_ps(fXPoint); + const __m256 ymm_y = GDAL_mm256_load1_ps(fYPoint); + __m256 ymm_nominator = _mm256_setzero_ps(); + __m256 ymm_denominator = _mm256_setzero_ps(); + int mask = 0; + +#undef LOOP_SIZE +#if defined(__x86_64) || defined(_M_X64) + /* This would also work in 32bit mode, but there are only 8 XMM registers */ + /* whereas we have 16 for 64bit */ +#define LOOP_SIZE 16 + size_t nPointsRound = (nPoints / LOOP_SIZE) * LOOP_SIZE; + for ( i = 0; i < nPointsRound; i += LOOP_SIZE ) + { + __m256 ymm_rx = _mm256_sub_ps(_mm256_load_ps(pafX + i), ymm_x); /* rx = pafX[i] - fXPoint */ + __m256 ymm_rx_8 = _mm256_sub_ps(_mm256_load_ps(pafX + i + 8), ymm_x); + __m256 ymm_ry = _mm256_sub_ps(_mm256_load_ps(pafY + i), ymm_y); /* ry = pafY[i] - fYPoint */ + __m256 ymm_ry_8 = _mm256_sub_ps(_mm256_load_ps(pafY + i + 8), ymm_y); + __m256 ymm_r2 = _mm256_add_ps(_mm256_mul_ps(ymm_rx, ymm_rx), /* r2 = rx * rx + ry * ry */ + _mm256_mul_ps(ymm_ry, ymm_ry)); + __m256 ymm_r2_8 = _mm256_add_ps(_mm256_mul_ps(ymm_rx_8, ymm_rx_8), + _mm256_mul_ps(ymm_ry_8, ymm_ry_8)); + __m256 ymm_invr2 = _mm256_rcp_ps(ymm_r2); /* invr2 = 1.0f / r2 */ + __m256 ymm_invr2_8 = _mm256_rcp_ps(ymm_r2_8); + ymm_nominator = _mm256_add_ps(ymm_nominator, /* nominator += invr2 * pafZ[i] */ + _mm256_mul_ps(ymm_invr2, _mm256_load_ps(pafZ + i))); + ymm_nominator = _mm256_add_ps(ymm_nominator, + _mm256_mul_ps(ymm_invr2_8, _mm256_load_ps(pafZ + i + 8))); + ymm_denominator = _mm256_add_ps(ymm_denominator, ymm_invr2); /* denominator += invr2 */ + ymm_denominator = _mm256_add_ps(ymm_denominator, ymm_invr2_8); + mask = _mm256_movemask_ps(_mm256_cmp_ps(ymm_r2, ymm_small, _CMP_LT_OS)) | /* if( r2 < fEpsilon) */ + (_mm256_movemask_ps(_mm256_cmp_ps(ymm_r2_8, ymm_small, _CMP_LT_OS)) << 8); + if( mask ) + break; + } +#else +#define LOOP_SIZE 8 + size_t nPointsRound = (nPoints / LOOP_SIZE) * LOOP_SIZE; + for ( i = 0; i < nPointsRound; i += LOOP_SIZE ) + { + __m256 ymm_rx = _mm256_sub_ps(_mm256_load_ps((float*)pafX + i), ymm_x); /* rx = pafX[i] - fXPoint */ + __m256 ymm_ry = _mm256_sub_ps(_mm256_load_ps((float*)pafY + i), ymm_y); /* ry = pafY[i] - fYPoint */ + __m256 ymm_r2 = _mm256_add_ps(_mm256_mul_ps(ymm_rx, ymm_rx), /* r2 = rx * rx + ry * ry */ + _mm256_mul_ps(ymm_ry, ymm_ry)); + __m256 ymm_invr2 = _mm256_rcp_ps(ymm_r2); /* invr2 = 1.0f / r2 */ + ymm_nominator = _mm256_add_ps(ymm_nominator, /* nominator += invr2 * pafZ[i] */ + _mm256_mul_ps(ymm_invr2, _mm256_load_ps((float*)pafZ + i))); + ymm_denominator = _mm256_add_ps(ymm_denominator, ymm_invr2); /* denominator += invr2 */ + mask = _mm256_movemask_ps(_mm256_cmp_ps(ymm_r2, ymm_small, _CMP_LT_OS)); /* if( r2 < fEpsilon) */ + if( mask ) + break; + } +#endif + + /* Find which i triggered r2 < fEpsilon */ + if( mask ) + { + for(int j = 0; j < LOOP_SIZE; j++ ) + { + if( mask & (1 << j) ) + { + (*pdfValue) = (pafZ)[i + j]; + return CE_None; + } + } + } +#undef LOOP_SIZE + + /* Get back nominator and denominator values for YMM registers */ + float afNominator[8], afDenominator[8]; + _mm256_storeu_ps(afNominator, ymm_nominator); + _mm256_storeu_ps(afDenominator, ymm_denominator); + + float fNominator = afNominator[0] + afNominator[1] + + afNominator[2] + afNominator[3] + + afNominator[4] + afNominator[5] + + afNominator[6] + afNominator[7]; + float fDenominator = afDenominator[0] + afDenominator[1] + + afDenominator[2] + afDenominator[3] + + afDenominator[4] + afDenominator[5] + + afDenominator[6] + afDenominator[7]; + + /* Do the few remaining loop iterations */ + for ( ; i < nPoints; i++ ) + { + const float fRX = pafX[i] - fXPoint; + const float fRY = pafY[i] - fYPoint; + const float fR2 = + fRX * fRX + fRY * fRY; + + // If the test point is close to the grid node, use the point + // value directly as a node value to avoid singularity. + if ( fR2 < 0.0000000000001 ) + { + break; + } + else + { + const float fInvR2 = 1.0f / fR2; + fNominator += fInvR2 * pafZ[i]; + fDenominator += fInvR2; + } + } + + if( i != nPoints ) + { + (*pdfValue) = pafZ[i]; + } + else + if ( fDenominator == 0.0 ) + { + (*pdfValue) = + ((GDALGridInverseDistanceToAPowerOptions*)poOptions)->dfNoDataValue; + } + else + (*pdfValue) = fNominator / fDenominator; + + return CE_None; +} + +#endif diff --git a/bazaar/plugin/gdal/alg/gdalgridsse.cpp b/bazaar/plugin/gdal/alg/gdalgridsse.cpp new file mode 100644 index 000000000..e7bb675c6 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalgridsse.cpp @@ -0,0 +1,241 @@ +/****************************************************************************** + * $Id: gdalgridsse.cpp 28033 2014-11-30 16:37:24Z rouault $ + * + * Project: GDAL Gridding API. + * Purpose: Implementation of GDAL scattered data gridder. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdalgrid.h" +#include "gdalgrid_priv.h" + +#ifdef HAVE_SSE_AT_COMPILE_TIME +#include + +CPL_CVSID("$Id: gdalgridsse.cpp 28033 2014-11-30 16:37:24Z rouault $"); + +/************************************************************************/ +/* CPLHaveRuntimeSSE() */ +/************************************************************************/ + +#define CPUID_SSE_EDX_BIT 25 + +#if (defined(_M_X64) || defined(__x86_64)) + +int CPLHaveRuntimeSSE() +{ + return TRUE; +} + +#elif defined(__GNUC__) && defined(__i386__) + +int CPLHaveRuntimeSSE() +{ + int cpuinfo[4] = {0,0,0,0}; + GCC_CPUID(1, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]); + return (cpuinfo[3] & (1 << CPUID_SSE_EDX_BIT)) != 0; +} + +#elif defined(_MSC_VER) && defined(_M_IX86) + +#if _MSC_VER <= 1310 +static void inline __cpuid(int cpuinfo[4], int level) +{ + __asm + { + push ebx + push esi + + mov esi,cpuinfo + mov eax,level + cpuid + mov dword ptr [esi], eax + mov dword ptr [esi+4],ebx + mov dword ptr [esi+8],ecx + mov dword ptr [esi+0Ch],edx + + pop esi + pop ebx + } +} +#else +#include +#endif + +int CPLHaveRuntimeSSE() +{ + int cpuinfo[4] = {0,0,0,0}; + __cpuid(cpuinfo, 1); + return (cpuinfo[3] & (1 << CPUID_SSE_EDX_BIT)) != 0; +} + +#else + +int CPLHaveRuntimeSSE() +{ + return FALSE; +} +#endif + +/************************************************************************/ +/* GDALGridInverseDistanceToAPower2NoSmoothingNoSearchSSE() */ +/************************************************************************/ + +CPLErr +GDALGridInverseDistanceToAPower2NoSmoothingNoSearchSSE( + const void *poOptions, + GUInt32 nPoints, + CPL_UNUSED const double *unused_padfX, + CPL_UNUSED const double *unused_padfY, + CPL_UNUSED const double *unused_padfZ, + double dfXPoint, double dfYPoint, + double *pdfValue, + void* hExtraParamsIn ) +{ + size_t i = 0; + GDALGridExtraParameters* psExtraParams = (GDALGridExtraParameters*) hExtraParamsIn; + const float* pafX = psExtraParams->pafX; + const float* pafY = psExtraParams->pafY; + const float* pafZ = psExtraParams->pafZ; + + const float fEpsilon = 0.0000000000001f; + const float fXPoint = (float)dfXPoint; + const float fYPoint = (float)dfYPoint; + const __m128 xmm_small = _mm_load1_ps((float*)&fEpsilon); + const __m128 xmm_x = _mm_load1_ps((float*)&fXPoint); + const __m128 xmm_y = _mm_load1_ps((float*)&fYPoint); + __m128 xmm_nominator = _mm_setzero_ps(); + __m128 xmm_denominator = _mm_setzero_ps(); + int mask = 0; + +#if defined(__x86_64) || defined(_M_X64) + /* This would also work in 32bit mode, but there are only 8 XMM registers */ + /* whereas we have 16 for 64bit */ +#define LOOP_SIZE 8 + size_t nPointsRound = (nPoints / LOOP_SIZE) * LOOP_SIZE; + for ( i = 0; i < nPointsRound; i += LOOP_SIZE ) + { + __m128 xmm_rx = _mm_sub_ps(_mm_load_ps(pafX + i), xmm_x); /* rx = pafX[i] - fXPoint */ + __m128 xmm_rx_4 = _mm_sub_ps(_mm_load_ps(pafX + i + 4), xmm_x); + __m128 xmm_ry = _mm_sub_ps(_mm_load_ps(pafY + i), xmm_y); /* ry = pafY[i] - fYPoint */ + __m128 xmm_ry_4 = _mm_sub_ps(_mm_load_ps(pafY + i + 4), xmm_y); + __m128 xmm_r2 = _mm_add_ps(_mm_mul_ps(xmm_rx, xmm_rx), /* r2 = rx * rx + ry * ry */ + _mm_mul_ps(xmm_ry, xmm_ry)); + __m128 xmm_r2_4 = _mm_add_ps(_mm_mul_ps(xmm_rx_4, xmm_rx_4), + _mm_mul_ps(xmm_ry_4, xmm_ry_4)); + __m128 xmm_invr2 = _mm_rcp_ps(xmm_r2); /* invr2 = 1.0f / r2 */ + __m128 xmm_invr2_4 = _mm_rcp_ps(xmm_r2_4); + xmm_nominator = _mm_add_ps(xmm_nominator, /* nominator += invr2 * pafZ[i] */ + _mm_mul_ps(xmm_invr2, _mm_load_ps(pafZ + i))); + xmm_nominator = _mm_add_ps(xmm_nominator, + _mm_mul_ps(xmm_invr2_4, _mm_load_ps(pafZ + i + 4))); + xmm_denominator = _mm_add_ps(xmm_denominator, xmm_invr2); /* denominator += invr2 */ + xmm_denominator = _mm_add_ps(xmm_denominator, xmm_invr2_4); + mask = _mm_movemask_ps(_mm_cmplt_ps(xmm_r2, xmm_small)) | /* if( r2 < fEpsilon) */ + (_mm_movemask_ps(_mm_cmplt_ps(xmm_r2_4, xmm_small)) << 4); + if( mask ) + break; + } +#else +#define LOOP_SIZE 4 + size_t nPointsRound = (nPoints / LOOP_SIZE) * LOOP_SIZE; + for ( i = 0; i < nPointsRound; i += LOOP_SIZE ) + { + __m128 xmm_rx = _mm_sub_ps(_mm_load_ps((float*)pafX + i), xmm_x); /* rx = pafX[i] - fXPoint */ + __m128 xmm_ry = _mm_sub_ps(_mm_load_ps((float*)pafY + i), xmm_y); /* ry = pafY[i] - fYPoint */ + __m128 xmm_r2 = _mm_add_ps(_mm_mul_ps(xmm_rx, xmm_rx), /* r2 = rx * rx + ry * ry */ + _mm_mul_ps(xmm_ry, xmm_ry)); + __m128 xmm_invr2 = _mm_rcp_ps(xmm_r2); /* invr2 = 1.0f / r2 */ + xmm_nominator = _mm_add_ps(xmm_nominator, /* nominator += invr2 * pafZ[i] */ + _mm_mul_ps(xmm_invr2, _mm_load_ps((float*)pafZ + i))); + xmm_denominator = _mm_add_ps(xmm_denominator, xmm_invr2); /* denominator += invr2 */ + mask = _mm_movemask_ps(_mm_cmplt_ps(xmm_r2, xmm_small)); /* if( r2 < fEpsilon) */ + if( mask ) + break; + } +#endif + + /* Find which i triggered r2 < fEpsilon */ + if( mask ) + { + for(int j = 0; j < LOOP_SIZE; j++ ) + { + if( mask & (1 << j) ) + { + (*pdfValue) = (pafZ)[i + j]; + return CE_None; + } + } + } + + /* Get back nominator and denominator values for XMM registers */ + float afNominator[4], afDenominator[4]; + _mm_storeu_ps(afNominator, xmm_nominator); + _mm_storeu_ps(afDenominator, xmm_denominator); + + float fNominator = afNominator[0] + afNominator[1] + + afNominator[2] + afNominator[3]; + float fDenominator = afDenominator[0] + afDenominator[1] + + afDenominator[2] + afDenominator[3]; + + /* Do the few remaining loop iterations */ + for ( ; i < nPoints; i++ ) + { + const float fRX = pafX[i] - fXPoint; + const float fRY = pafY[i] - fYPoint; + const float fR2 = + fRX * fRX + fRY * fRY; + + // If the test point is close to the grid node, use the point + // value directly as a node value to avoid singularity. + if ( fR2 < 0.0000000000001 ) + { + break; + } + else + { + const float fInvR2 = 1.0f / fR2; + fNominator += fInvR2 * pafZ[i]; + fDenominator += fInvR2; + } + } + + if( i != nPoints ) + { + (*pdfValue) = pafZ[i]; + } + else + if ( fDenominator == 0.0 ) + { + (*pdfValue) = + ((GDALGridInverseDistanceToAPowerOptions*)poOptions)->dfNoDataValue; + } + else + (*pdfValue) = fNominator / fDenominator; + + return CE_None; +} + + +#endif /* HAVE_SSE_AT_COMPILE_TIME */ diff --git a/bazaar/plugin/gdal/alg/gdalmatching.cpp b/bazaar/plugin/gdal/alg/gdalmatching.cpp new file mode 100644 index 000000000..b3840e111 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalmatching.cpp @@ -0,0 +1,299 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL + * Purpose: GDAL Wrapper for image matching via corellation algorithm. + * Author: Frank Warmerdam, warmerdam@pobox.com + * Author: Andrew Migal, migal.drew@gmail.com + * + ****************************************************************************** + * Copyright (c) 2012, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_alg.h" +#include "gdal_simplesurf.h" + +CPL_CVSID("$Id"); + +/** + * @file + * @author Andrew Migal migal.drew@gmail.com + * @brief Algorithms for searching corresponding points on images. + * @details This implementation is based on an simplified version + * of SURF algorithm (Speeded Up Robust Features). + * Provides capability for detection feature points + * and finding equal points on different images. + * As original, this realization is scale invariant, but sensitive to rotation. + * Images should have similar rotation angles (maximum difference is up to 10-15 degrees), + * otherwise algorithm produces incorrect and very unstable results. + */ + +/** + * Detect feature points on provided image. Please carefully read documentation below. + * + * @param poDataset Image on which feature points will be detected + * @param panBands Array of 3 raster bands numbers, for Red, Green, Blue bands (in that order) + * @param nOctaveStart Number of bottom octave. Octave numbers starts from one. + * This value directly and strongly affects to amount of recognized points + * @param nOctaveEnd Number of top octave. Should be equal or greater than octaveStart + * @param dfThreshold Value from 0 to 1. Threshold for feature point recognition. + * Number of detected points is larger if threshold is lower + * + * @see GDALFeaturePoint, GDALSimpleSURF class for detailes. + * + * @note Every octave finds points in specific size. For small images + * use small octave numbers, for high resolution - large. + * For 1024x1024 images it's normal to use any octave numbers from range 1-6. + * (for example, octave start - 1, octave end - 3, or octave start - 2, octave end - 2.) + * For larger images, try 1-10 range or even higher. + * Pay attention that number of detected point decreases quickly per octave + * for particular image. Algorithm finds more points in case of small octave number. + * If method detects nothing, reduce octave start value. + * In addition, if many feature points are required (the largest possible amount), + * use the lowest octave start value (1) and wide octave range. + * + * @note Typical threshold's value is 0,001. It's pretty good for all images. + * But this value depends on image's nature and may be various in each particular case. + * For example, value can be 0,002 or 0,005. + * Notice that number of detected points is larger if threshold is lower. + * But with high threshold feature points will be better - "stronger", more "unique" and distinctive. + * + * Feel free to experiment with parameters, because character, robustness and number of points + * entirely depend on provided range of octaves and threshold. + * + * NOTICE that every octave requires time to compute. Use a little range + * or only one octave, if execution time is significant. + * + * @return CE_None or CE_Failure if error occurs. + */ + +static std::vector * +GatherFeaturePoints(GDALDataset* poDataset, int* panBands, + int nOctaveStart, int nOctaveEnd, double dfThreshold) +{ + if (poDataset == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "GDALDataset isn't specified"); + return NULL; + } + + if (panBands == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Raster bands are not specified"); + return NULL; + } + + if (nOctaveStart <= 0 || nOctaveEnd < 0 || + nOctaveStart > nOctaveEnd) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Octave numbers are invalid"); + return NULL; + } + + if (dfThreshold < 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Threshold have to be greater than zero"); + return NULL; + } + + GDALRasterBand *poRstRedBand = poDataset->GetRasterBand(panBands[0]); + GDALRasterBand *poRstGreenBand = poDataset->GetRasterBand(panBands[1]); + GDALRasterBand *poRstBlueBand = poDataset->GetRasterBand(panBands[2]); + + int nWidth = poRstRedBand->GetXSize(); + int nHeight = poRstRedBand->GetYSize(); + + // Allocate memory for grayscale image + double **padfImg = NULL; + padfImg = new double*[nHeight]; + for (int i = 0; i < nHeight; i++) + padfImg[i] = new double[nWidth]; + + // Create grayscale image + GDALSimpleSURF::ConvertRGBToLuminosity( + poRstRedBand, poRstGreenBand, poRstBlueBand, nWidth, nHeight, + padfImg, nHeight, nWidth); + + // Prepare integral image + GDALIntegralImage *poImg = new GDALIntegralImage(); + poImg->Initialize((const double**)padfImg, nHeight, nWidth); + + // Get feature points + GDALSimpleSURF *poSurf = new GDALSimpleSURF(nOctaveStart, nOctaveEnd); + + std::vector *poCollection = + poSurf->ExtractFeaturePoints(poImg, dfThreshold); + + // Clean up + delete poImg; + delete poSurf; + + for (int i = 0; i < nHeight; i++) + delete[] padfImg[i]; + + delete[] padfImg; + + return poCollection; +} + +/************************************************************************/ +/* GDALComputeMatchingPoints() */ +/************************************************************************/ + +GDAL_GCP CPL_DLL * +GDALComputeMatchingPoints( GDALDatasetH hFirstImage, + GDALDatasetH hSecondImage, + char **papszOptions, + int *pnGCPCount ) +{ + *pnGCPCount = 0; + +/* -------------------------------------------------------------------- */ +/* Override default algorithm parameters. */ +/* -------------------------------------------------------------------- */ + int nOctaveStart, nOctaveEnd; + double dfSURFThreshold; + double dfMatchingThreshold = 0.015; + + nOctaveStart =atoi(CSLFetchNameValueDef(papszOptions, "OCTAVE_START", "2")); + nOctaveEnd = atoi(CSLFetchNameValueDef(papszOptions, "OCTAVE_END", "2")); + + dfSURFThreshold = CPLAtof( + CSLFetchNameValueDef(papszOptions, "SURF_THRESHOLD", "0.001")); + dfMatchingThreshold = CPLAtof( + CSLFetchNameValueDef(papszOptions, "MATCHING_THRESHOLD", "0.015")); + +/* -------------------------------------------------------------------- */ +/* Identify the bands to use. For now we are effectively */ +/* limited to using RGB input so if we have one band only treat */ +/* it as red=green=blue=band 1. Disallow non eightbit imagery. */ +/* -------------------------------------------------------------------- */ + int anBandMap1[3], anBandMap2[3]; + + if( GDALGetRasterCount(hFirstImage) >= 3 ) + { + anBandMap1[0] = 1; + anBandMap1[1] = 2; + anBandMap1[2] = 3; + } + else + { + anBandMap1[0] = anBandMap1[1] = anBandMap1[2] = 1; + } + + if( GDALGetRasterCount(hSecondImage) >= 3 ) + { + anBandMap2[0] = 1; + anBandMap2[1] = 2; + anBandMap2[2] = 3; + } + else + { + anBandMap2[0] = anBandMap2[1] = anBandMap2[2] = 1; + } + +/* -------------------------------------------------------------------- */ +/* Collect reference points on each image. */ +/* -------------------------------------------------------------------- */ + std::vector *poFPCollection1 = + GatherFeaturePoints((GDALDataset *) hFirstImage, anBandMap1, + nOctaveStart, nOctaveEnd, dfSURFThreshold); + if( poFPCollection1 == NULL ) + return NULL; + + std::vector *poFPCollection2 = + GatherFeaturePoints((GDALDataset *) hSecondImage, anBandMap2, + nOctaveStart, nOctaveEnd, + dfSURFThreshold); + + if( poFPCollection2 == NULL ) + return NULL; + + +/* -------------------------------------------------------------------- */ +/* Try to find corresponding locations. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + std::vector oMatchPairs; + + eErr = GDALSimpleSURF::MatchFeaturePoints( + &oMatchPairs, poFPCollection1, poFPCollection2, + dfMatchingThreshold ); + + if( eErr != CE_None ) + return NULL; + + + *pnGCPCount = oMatchPairs.size() / 2; + +/* -------------------------------------------------------------------- */ +/* Translate these into GCPs - but with the output coordinate */ +/* system being pixel/line on the second image. */ +/* -------------------------------------------------------------------- */ + GDAL_GCP *pasGCPList = (GDAL_GCP*) CPLCalloc(*pnGCPCount, sizeof(GDAL_GCP)); + + GDALInitGCPs(*pnGCPCount, pasGCPList); + + for (int i=0; i < *pnGCPCount; i++) + { + GDALFeaturePoint *poPoint1 = oMatchPairs[i*2 ]; + GDALFeaturePoint *poPoint2 = oMatchPairs[i*2+1]; + + pasGCPList[i].dfGCPPixel = poPoint1->GetX() + 0.5; + pasGCPList[i].dfGCPLine = poPoint1->GetY() + 0.5; + + pasGCPList[i].dfGCPX = poPoint2->GetX() + 0.5; + pasGCPList[i].dfGCPY = poPoint2->GetY() + 0.5; + pasGCPList[i].dfGCPZ = 0.0; + } + + // Cleanup the feature point lists. + delete poFPCollection1; + delete poFPCollection2; + +/* -------------------------------------------------------------------- */ +/* Optionally transform into the georef coordinates of the */ +/* output image. */ +/* -------------------------------------------------------------------- */ + int bGeorefOutput = + CSLTestBoolean(CSLFetchNameValueDef(papszOptions,"OUTPUT_GEOREF","NO")); + + if( bGeorefOutput ) + { + double adfGeoTransform[6]; + + GDALGetGeoTransform( hSecondImage, adfGeoTransform ); + + for (int i=0; i < *pnGCPCount; i++) + { + GDALApplyGeoTransform(adfGeoTransform, + pasGCPList[i].dfGCPX, + pasGCPList[i].dfGCPY, + &(pasGCPList[i].dfGCPX), + &(pasGCPList[i].dfGCPY)); + } + } + + return pasGCPList; +} diff --git a/bazaar/plugin/gdal/alg/gdalmediancut.cpp b/bazaar/plugin/gdal/alg/gdalmediancut.cpp new file mode 100644 index 000000000..3e178582d --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalmediancut.cpp @@ -0,0 +1,1126 @@ +/****************************************************************************** + * $Id: gdalmediancut.cpp 28085 2014-12-07 14:36:00Z rouault $ + * + * Project: CIETMap Phase 2 + * Purpose: Use median cut algorithm to generate an near-optimal PCT for a + * given RGB image. Implemented as function GDALComputeMedianCutPCT. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * This code was based on the tiffmedian.c code from libtiff (www.libtiff.org) + * which was based on a paper by Paul Heckbert: + * + * "Color Image Quantization for Frame Buffer Display", Paul + * Heckbert, SIGGRAPH proceedings, 1982, pp. 297-307. + * + */ + +#include "gdal_priv.h" +#include "gdal_alg.h" +#include "gdal_alg_priv.h" + +CPL_CVSID("$Id: gdalmediancut.cpp 28085 2014-12-07 14:36:00Z rouault $"); + +#define HISTOGRAM(h,n,r,g,b) h[((r)*(n)+(g))*(n)+(b)] + +#define MAKE_COLOR_CODE(r,g,b) ((r)+(g)*256+(b)*256*256) + +typedef struct /* NOTE: if changing the size of this structure, edit MEDIAN_CUT_AND_DITHER_BUFFER_SIZE_65536 */ +{ + GUInt32 nColorCode; + int nCount; + GUInt32 nColorCode2; + int nCount2; + GUInt32 nColorCode3; + int nCount3; +} HashHistogram; + +typedef struct colorbox { + struct colorbox *next, *prev; + int rmin, rmax; + int gmin, gmax; + int bmin, bmax; + int total; +} Colorbox; + +static void splitbox(Colorbox* ptr, const int* histogram, + const HashHistogram* psHashHistogram, + int nCLevels, + Colorbox **pfreeboxes, Colorbox **pusedboxes, + GByte* pabyRedBand, + GByte* pabyGreenBand, + GByte* pabyBlueBand, int nPixels); +static void shrinkbox(Colorbox* box, + const int* histogram, + int nCLevels); +static Colorbox* largest_box(Colorbox *usedboxes); + +/************************************************************************/ +/* GDALComputeMedianCutPCT() */ +/************************************************************************/ + +/** + * Compute optimal PCT for RGB image. + * + * This function implements a median cut algorithm to compute an "optimal" + * pseudocolor table for representing an input RGB image. This PCT could + * then be used with GDALDitherRGB2PCT() to convert a 24bit RGB image into + * an eightbit pseudo-colored image. + * + * This code was based on the tiffmedian.c code from libtiff (www.libtiff.org) + * which was based on a paper by Paul Heckbert: + * + * \verbatim + * "Color Image Quantization for Frame Buffer Display", Paul + * Heckbert, SIGGRAPH proceedings, 1982, pp. 297-307. + * \endverbatim + * + * The red, green and blue input bands do not necessarily need to come + * from the same file, but they must be the same width and height. They will + * be clipped to 8bit during reading, so non-eight bit bands are generally + * inappropriate. + * + * @param hRed Red input band. + * @param hGreen Green input band. + * @param hBlue Blue input band. + * @param pfnIncludePixel function used to test which pixels should be included + * in the analysis. At this time this argument is ignored and all pixels are + * utilized. This should normally be NULL. + * @param nColors the desired number of colors to be returned (2-256). + * @param hColorTable the colors will be returned in this color table object. + * @param pfnProgress callback for reporting algorithm progress matching the + * GDALProgressFunc() semantics. May be NULL. + * @param pProgressArg callback argument passed to pfnProgress. + * + * @return returns CE_None on success or CE_Failure if an error occurs. + */ + +extern "C" int CPL_STDCALL +GDALComputeMedianCutPCT( GDALRasterBandH hRed, + GDALRasterBandH hGreen, + GDALRasterBandH hBlue, + int (*pfnIncludePixel)(int,int,void*), + int nColors, + GDALColorTableH hColorTable, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + return GDALComputeMedianCutPCTInternal(hRed, hGreen, hBlue, + NULL, NULL, NULL, + pfnIncludePixel, nColors, + 5, + NULL, + hColorTable, + pfnProgress, pProgressArg); +} + +/*static int nMaxCollisions = 0;*/ + +static inline int FindColorCount(const HashHistogram* psHashHistogram, + GUInt32 nColorCode) +{ + GUInt32 nIdx = nColorCode % PRIME_FOR_65536; + /*int nCollisions = 0; */ + while( TRUE ) + { + if( (int)psHashHistogram[nIdx].nColorCode < 0 ) + { + return 0; + } + if( psHashHistogram[nIdx].nColorCode == nColorCode ) + { + return psHashHistogram[nIdx].nCount; + } + if( (int)psHashHistogram[nIdx].nColorCode2 < 0 ) + { + return 0; + } + if( psHashHistogram[nIdx].nColorCode2 == nColorCode ) + { + return psHashHistogram[nIdx].nCount2; + } + if( (int)psHashHistogram[nIdx].nColorCode3 < 0 ) + { + return 0; + } + if( psHashHistogram[nIdx].nColorCode3 == nColorCode ) + { + return psHashHistogram[nIdx].nCount3; + } + + do + { + /*nCollisions ++;*/ + nIdx+=257; + if( nIdx >= PRIME_FOR_65536 ) + nIdx -= PRIME_FOR_65536; + } + while( (int)psHashHistogram[nIdx].nColorCode >= 0 && + psHashHistogram[nIdx].nColorCode != nColorCode && + (int)psHashHistogram[nIdx].nColorCode2 >= 0 && + psHashHistogram[nIdx].nColorCode2 != nColorCode&& + (int)psHashHistogram[nIdx].nColorCode3 >= 0 && + psHashHistogram[nIdx].nColorCode3 != nColorCode ); + /*if( nCollisions > nMaxCollisions ) + { + nMaxCollisions = nCollisions; + printf("median cut: nCollisions = %d for R=%d,G=%d,B=%d\n", + nCollisions, nColorCode&0xFF, (nColorCode>>8)&0xFF, (nColorCode>>16)&0xFF); + }*/ + } +} + +static inline int* FindAndInsertColorCount(HashHistogram* psHashHistogram, + GUInt32 nColorCode) +{ + GUInt32 nIdx = nColorCode % PRIME_FOR_65536; + /*int nCollisions = 0;*/ + while( TRUE ) + { + if( psHashHistogram[nIdx].nColorCode == nColorCode ) + { + return &(psHashHistogram[nIdx].nCount); + } + if( (int)psHashHistogram[nIdx].nColorCode < 0 ) + { + psHashHistogram[nIdx].nColorCode = nColorCode; + psHashHistogram[nIdx].nCount = 0; + return &(psHashHistogram[nIdx].nCount); + } + if( psHashHistogram[nIdx].nColorCode2 == nColorCode ) + { + return &(psHashHistogram[nIdx].nCount2); + } + if( (int)psHashHistogram[nIdx].nColorCode2 < 0 ) + { + psHashHistogram[nIdx].nColorCode2 = nColorCode; + psHashHistogram[nIdx].nCount2 = 0; + return &(psHashHistogram[nIdx].nCount2); + } + if( psHashHistogram[nIdx].nColorCode3 == nColorCode ) + { + return &(psHashHistogram[nIdx].nCount3); + } + if( (int)psHashHistogram[nIdx].nColorCode3 < 0 ) + { + psHashHistogram[nIdx].nColorCode3 = nColorCode; + psHashHistogram[nIdx].nCount3 = 0; + return &(psHashHistogram[nIdx].nCount3); + } + + do + { + /*nCollisions ++;*/ + nIdx+=257; + if( nIdx >= PRIME_FOR_65536 ) + nIdx -= PRIME_FOR_65536; + } + while( (int)psHashHistogram[nIdx].nColorCode >= 0 && + psHashHistogram[nIdx].nColorCode != nColorCode && + (int)psHashHistogram[nIdx].nColorCode2 >= 0 && + psHashHistogram[nIdx].nColorCode2 != nColorCode&& + (int)psHashHistogram[nIdx].nColorCode3 >= 0 && + psHashHistogram[nIdx].nColorCode3 != nColorCode ); + /*if( nCollisions > nMaxCollisions ) + { + nMaxCollisions = nCollisions; + printf("median cut: nCollisions = %d for R=%d,G=%d,B=%d\n", + nCollisions, nColorCode&0xFF, (nColorCode>>8)&0xFF, (nColorCode>>16)&0xFF); + }*/ + } +} + +int +GDALComputeMedianCutPCTInternal( GDALRasterBandH hRed, + GDALRasterBandH hGreen, + GDALRasterBandH hBlue, + GByte* pabyRedBand, + GByte* pabyGreenBand, + GByte* pabyBlueBand, + int (*pfnIncludePixel)(int,int,void*), + int nColors, + int nBits, + int* panHistogram, /* NULL, or at least of size (1< 256 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALComputeMedianCutPCT() : nColors must be lesser than or equal to 256." ); + + return CE_Failure; + } + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* ==================================================================== */ +/* STEP 1: create empty boxes. */ +/* ==================================================================== */ + int i; + Colorbox *box_list, *ptr; + int* histogram; + Colorbox *freeboxes; + Colorbox *usedboxes; + int nCLevels = 1 << nBits; + int nColorShift = 8 - nBits; + int nColorCounter = 0; + GByte anRed[256], anGreen[256], anBlue[256]; + int nPixels = 0; + HashHistogram* psHashHistogram = NULL; + + if( nBits == 8 && pabyRedBand != NULL && pabyGreenBand != NULL && + pabyBlueBand != NULL && nXSize < INT_MAX / nYSize ) + { + nPixels = nXSize * nYSize; + } + + if( panHistogram ) + { + if( nBits == 8 && (GIntBig)nXSize * nYSize <= 65536 ) + { + /* If the image is small enough, then the number of colors */ + /* will be limited and using a hashmap, rather than a full table */ + /* will be more efficient */ + histogram = NULL; + psHashHistogram = (HashHistogram*)panHistogram; + memset(psHashHistogram, 0xFF, sizeof(HashHistogram) * PRIME_FOR_65536); + } + else + { + histogram = panHistogram; + memset(histogram, 0, nCLevels*nCLevels*nCLevels * sizeof(int)); + } + } + else + { + histogram = (int*) VSICalloc(nCLevels*nCLevels*nCLevels,sizeof(int)); + if( histogram == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "VSICalloc(): Out of memory in GDALComputeMedianCutPCT" ); + return CE_Failure; + } + } + usedboxes = NULL; + box_list = freeboxes = (Colorbox *)CPLMalloc(nColors*sizeof (Colorbox)); + freeboxes[0].next = &freeboxes[1]; + freeboxes[0].prev = NULL; + for (i = 1; i < nColors-1; ++i) { + freeboxes[i].next = &freeboxes[i+1]; + freeboxes[i].prev = &freeboxes[i-1]; + } + freeboxes[nColors-1].next = NULL; + freeboxes[nColors-1].prev = &freeboxes[nColors-2]; + +/* ==================================================================== */ +/* Build histogram. */ +/* ==================================================================== */ + GByte *pabyRedLine, *pabyGreenLine, *pabyBlueLine; + int iLine, iPixel; + +/* -------------------------------------------------------------------- */ +/* Initialize the box datastructures. */ +/* -------------------------------------------------------------------- */ + ptr = freeboxes; + freeboxes = ptr->next; + if (freeboxes) + freeboxes->prev = NULL; + ptr->next = usedboxes; + usedboxes = ptr; + if (ptr->next) + ptr->next->prev = ptr; + + ptr->rmin = ptr->gmin = ptr->bmin = 999; + ptr->rmax = ptr->gmax = ptr->bmax = -1; + ptr->total = nXSize * nYSize; + +/* -------------------------------------------------------------------- */ +/* Collect histogram. */ +/* -------------------------------------------------------------------- */ + pabyRedLine = (GByte *) VSIMalloc(nXSize); + pabyGreenLine = (GByte *) VSIMalloc(nXSize); + pabyBlueLine = (GByte *) VSIMalloc(nXSize); + + if (pabyRedLine == NULL || + pabyGreenLine == NULL || + pabyBlueLine == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "VSIMalloc(): Out of memory in GDALComputeMedianCutPCT" ); + err = CE_Failure; + goto end_and_cleanup; + } + + for( iLine = 0; iLine < nYSize; iLine++ ) + { + if( !pfnProgress( iLine / (double) nYSize, + "Generating Histogram", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User Terminated" ); + err = CE_Failure; + goto end_and_cleanup; + } + + GDALRasterIO( hRed, GF_Read, 0, iLine, nXSize, 1, + pabyRedLine, nXSize, 1, GDT_Byte, 0, 0 ); + GDALRasterIO( hGreen, GF_Read, 0, iLine, nXSize, 1, + pabyGreenLine, nXSize, 1, GDT_Byte, 0, 0 ); + GDALRasterIO( hBlue, GF_Read, 0, iLine, nXSize, 1, + pabyBlueLine, nXSize, 1, GDT_Byte, 0, 0 ); + + for( iPixel = 0; iPixel < nXSize; iPixel++ ) + { + int nRed, nGreen, nBlue; + + nRed = pabyRedLine[iPixel] >> nColorShift; + nGreen = pabyGreenLine[iPixel] >> nColorShift; + nBlue = pabyBlueLine[iPixel] >> nColorShift; + + ptr->rmin = MIN(ptr->rmin, nRed); + ptr->gmin = MIN(ptr->gmin, nGreen); + ptr->bmin = MIN(ptr->bmin, nBlue); + ptr->rmax = MAX(ptr->rmax, nRed); + ptr->gmax = MAX(ptr->gmax, nGreen); + ptr->bmax = MAX(ptr->bmax, nBlue); + + int* pnColor; + if( psHashHistogram ) + { + pnColor = FindAndInsertColorCount(psHashHistogram, + MAKE_COLOR_CODE(nRed, nGreen, nBlue)); + } + else + { + pnColor = &HISTOGRAM(histogram, nCLevels, nRed, nGreen, nBlue); + } + if( *pnColor == 0 ) + { + if( nColorShift == 0 && nColorCounter < nColors ) + { + anRed[nColorCounter] = nRed; + anGreen[nColorCounter] = nGreen; + anBlue[nColorCounter] = nBlue; + } + nColorCounter++; + } + (*pnColor) ++; + } + } + + if( !pfnProgress( 1.0, "Generating Histogram", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User Terminated" ); + err = CE_Failure; + goto end_and_cleanup; + } + + if( nColorShift == 0 && nColorCounter <= nColors ) + { + //CPLDebug("MEDIAN_CUT", "%d colors found <= %d", nColorCounter, nColors); + for(int iColor = 0;iColornext) + { + GDALColorEntry sEntry; + + sEntry.c1 = (GByte) (((ptr->rmin + ptr->rmax) << nColorShift) / 2); + sEntry.c2 = (GByte) (((ptr->gmin + ptr->gmax) << nColorShift) / 2); + sEntry.c3 = (GByte) (((ptr->bmin + ptr->bmax) << nColorShift) / 2); + sEntry.c4 = 255; + GDALSetColorEntry( hColorTable, i, &sEntry ); + } + +end_and_cleanup: + CPLFree( pabyRedLine ); + CPLFree( pabyGreenLine ); + CPLFree( pabyBlueLine ); + + /* We're done with the boxes now */ + CPLFree(box_list); + freeboxes = usedboxes = NULL; + + if( panHistogram == NULL ) + CPLFree( histogram ); + + return err; +} + +/************************************************************************/ +/* largest_box() */ +/************************************************************************/ + +static Colorbox * +largest_box(Colorbox *usedboxes) +{ + Colorbox *p, *b; + int size; + + b = NULL; + size = -1; + for (p = usedboxes; p != NULL; p = p->next) + if ((p->rmax > p->rmin || p->gmax > p->gmin || + p->bmax > p->bmin) && p->total > size) + size = (b = p)->total; + return (b); +} + +static void shrinkboxFromBand(Colorbox* ptr, + const GByte* pabyRedBand, + const GByte* pabyGreenBand, + const GByte* pabyBlueBand, int nPixels) +{ + int rmin_new = 255, rmax_new = 0, + gmin_new = 255, gmax_new = 0, + bmin_new = 255, bmax_new = 0; + for(int i=0;i= ptr->rmin && iR <= ptr->rmax && + iG >= ptr->gmin && iG <= ptr->gmax && + iB >= ptr->bmin && iB <= ptr->bmax ) + { + if( iR < rmin_new ) rmin_new = iR; + if( iR > rmax_new ) rmax_new = iR; + if( iG < gmin_new ) gmin_new = iG; + if( iG > gmax_new ) gmax_new = iG; + if( iB < bmin_new ) bmin_new = iB; + if( iB > bmax_new ) bmax_new = iB; + } + } + + CPLAssert(rmin_new >= ptr->rmin && rmin_new <= rmax_new && rmax_new <= ptr->rmax); + CPLAssert(gmin_new >= ptr->gmin && gmin_new <= gmax_new && gmax_new <= ptr->gmax); + CPLAssert(bmin_new >= ptr->bmin && bmin_new <= bmax_new && bmax_new <= ptr->bmax); + ptr->rmin = rmin_new; + ptr->rmax = rmax_new; + ptr->gmin = gmin_new; + ptr->gmax = gmax_new; + ptr->bmin = bmin_new; + ptr->bmax = bmax_new; +} + +static void shrinkboxFromHashHistogram(Colorbox* box, + const HashHistogram* psHashHistogram) +{ + int ir, ig, ib; + //int count_iter; + + if (box->rmax > box->rmin) { + //count_iter = 0; + for (ir = box->rmin; ir <= box->rmax; ++ir) { + for (ig = box->gmin; ig <= box->gmax; ++ig) { + for (ib = box->bmin; ib <= box->bmax; ++ib) { + //count_iter ++; + if (FindColorCount(psHashHistogram, + MAKE_COLOR_CODE(ir, ig, ib)) != 0) { + //if( count_iter > 65536 ) printf("iter rmin=%d\n", count_iter); + box->rmin = ir; + goto have_rmin; + } + } + } + } + } + have_rmin: + if (box->rmax > box->rmin) { + //count_iter = 0; + for (ir = box->rmax; ir >= box->rmin; --ir) { + for (ig = box->gmin; ig <= box->gmax; ++ig) { + ib = box->bmin; + for (; ib <= box->bmax; ++ib) { + //count_iter ++; + if (FindColorCount(psHashHistogram, + MAKE_COLOR_CODE(ir, ig, ib)) != 0) { + //if( count_iter > 65536 ) printf("iter rmax=%d\n", count_iter); + box->rmax = ir; + goto have_rmax; + } + } + } + } + } + + have_rmax: + if (box->gmax > box->gmin) { + //count_iter = 0; + for (ig = box->gmin; ig <= box->gmax; ++ig) { + for (ir = box->rmin; ir <= box->rmax; ++ir) { + for (ib = box->bmin; ib <= box->bmax; ++ib) { + //count_iter ++; + if (FindColorCount(psHashHistogram, + MAKE_COLOR_CODE(ir, ig, ib)) != 0) { + //if( count_iter > 65536 ) printf("iter gmin=%d\n", count_iter); + box->gmin = ig; + goto have_gmin; + } + } + } + } + } + + have_gmin: + if (box->gmax > box->gmin) { + //count_iter = 0; + for (ig = box->gmax; ig >= box->gmin; --ig) { + for (ir = box->rmin; ir <= box->rmax; ++ir) { + ib = box->bmin; + for (; ib <= box->bmax; ++ib) { + //count_iter ++; + if (FindColorCount(psHashHistogram, + MAKE_COLOR_CODE(ir, ig, ib)) != 0) { + //if( count_iter > 65536 ) printf("iter gmax=%d\n", count_iter); + box->gmax = ig; + goto have_gmax; + } + } + } + } + } + + have_gmax: + if (box->bmax > box->bmin) { + //count_iter = 0; + for (ib = box->bmin; ib <= box->bmax; ++ib) { + for (ir = box->rmin; ir <= box->rmax; ++ir) { + for (ig = box->gmin; ig <= box->gmax; ++ig) { + //count_iter ++; + if (FindColorCount(psHashHistogram, + MAKE_COLOR_CODE(ir, ig, ib)) != 0) { + //if( count_iter > 65536 ) printf("iter bmin=%d\n", count_iter); + box->bmin = ib; + goto have_bmin; + } + } + } + } + } + + have_bmin: + if (box->bmax > box->bmin) { + //count_iter = 0; + for (ib = box->bmax; ib >= box->bmin; --ib) { + for (ir = box->rmin; ir <= box->rmax; ++ir) { + ig = box->gmin; + for (; ig <= box->gmax; ++ig) { + //count_iter ++; + if (FindColorCount(psHashHistogram, + MAKE_COLOR_CODE(ir, ig, ib)) != 0) { + //if( count_iter > 65536 ) printf("iter bmax=%d\n", count_iter); + box->bmax = ib; + goto have_bmax; + } + } + } + } + } + + have_bmax: + ; +} + +/************************************************************************/ +/* splitbox() */ +/************************************************************************/ +static void +splitbox(Colorbox* ptr, const int* histogram, + const HashHistogram* psHashHistogram, + int nCLevels, + Colorbox **pfreeboxes, Colorbox **pusedboxes, + GByte* pabyRedBand, + GByte* pabyGreenBand, + GByte* pabyBlueBand, int nPixels) +{ + int hist2[256]; + int first=0, last=0; + Colorbox *new_cb; + const int *iptr; + int *histp; + int i, j; + int ir,ig,ib; + int sum, sum1, sum2; + enum { RED, GREEN, BLUE } axis; + + /* + * See which axis is the largest, do a histogram along that + * axis. Split at median point. Contract both new boxes to + * fit points and return + */ + i = ptr->rmax - ptr->rmin; + if (i >= ptr->gmax - ptr->gmin && i >= ptr->bmax - ptr->bmin) + axis = RED; + else if (ptr->gmax - ptr->gmin >= ptr->bmax - ptr->bmin) + axis = GREEN; + else + axis = BLUE; + /* get histogram along longest axis */ + int nIters = (ptr->rmax - ptr->rmin + 1) * (ptr->gmax - ptr->gmin + 1) * + (ptr->bmax - ptr->bmin + 1); + //printf("nIters = %d\n", nIters); + switch (axis) { + case RED: + { + if( nPixels != 0 && nIters > nPixels ) + { + memset(hist2, 0, sizeof(hist2)); + const int rmin = ptr->rmin, + rmax = ptr->rmax, + gmin = ptr->gmin, + gmax = ptr->gmax, + bmin = ptr->bmin, + bmax = ptr->bmax; + for(int i=0;i= rmin && iR <= rmax && + iG >= gmin && iG <= gmax && + iB >= bmin && iB <= bmax ) + { + hist2[iR] ++; + } + } + } + else if( psHashHistogram ) + { + histp = &hist2[ptr->rmin]; + for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { + *histp = 0; + for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { + for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) + { + *histp += FindColorCount(psHashHistogram, + MAKE_COLOR_CODE(ir, ig, ib)); + } + } + histp++; + } + } + else + { + histp = &hist2[ptr->rmin]; + for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { + *histp = 0; + for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { + iptr = &HISTOGRAM(histogram,nCLevels,ir,ig,ptr->bmin); + for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) + *histp += *iptr++; + } + histp++; + } + } + first = ptr->rmin; + last = ptr->rmax; + break; + } + case GREEN: + { + if( nPixels != 0 && nIters > nPixels ) + { + memset(hist2, 0, sizeof(hist2)); + const int rmin = ptr->rmin, + rmax = ptr->rmax, + gmin = ptr->gmin, + gmax = ptr->gmax, + bmin = ptr->bmin, + bmax = ptr->bmax; + for(int i=0;i= rmin && iR <= rmax && + iG >= gmin && iG <= gmax && + iB >= bmin && iB <= bmax ) + { + hist2[iG] ++; + } + } + } + else if( psHashHistogram ) + { + histp = &hist2[ptr->gmin]; + for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { + *histp = 0; + for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { + for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) + { + *histp += FindColorCount(psHashHistogram, + MAKE_COLOR_CODE(ir, ig, ib)); + } + } + histp++; + } + } + else + { + histp = &hist2[ptr->gmin]; + for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { + *histp = 0; + for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { + iptr = &HISTOGRAM(histogram,nCLevels,ir,ig,ptr->bmin); + for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) + *histp += *iptr++; + } + histp++; + } + } + first = ptr->gmin; + last = ptr->gmax; + break; + } + case BLUE: + { + if( nPixels != 0 && nIters > nPixels ) + { + memset(hist2, 0, sizeof(hist2)); + const int rmin = ptr->rmin, + rmax = ptr->rmax, + gmin = ptr->gmin, + gmax = ptr->gmax, + bmin = ptr->bmin, + bmax = ptr->bmax; + for(int i=0;i= rmin && iR <= rmax && + iG >= gmin && iG <= gmax && + iB >= bmin && iB <= bmax ) + { + hist2[iB] ++; + } + } + } + else if( psHashHistogram ) + { + histp = &hist2[ptr->bmin]; + for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) { + *histp = 0; + for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { + for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { + *histp += FindColorCount(psHashHistogram, + MAKE_COLOR_CODE(ir, ig, ib)); + } + } + histp++; + } + } + else + { + histp = &hist2[ptr->bmin]; + for (ib = ptr->bmin; ib <= ptr->bmax; ++ib) { + *histp = 0; + for (ir = ptr->rmin; ir <= ptr->rmax; ++ir) { + iptr = &HISTOGRAM(histogram,nCLevels,ir,ptr->gmin,ib); + for (ig = ptr->gmin; ig <= ptr->gmax; ++ig) { + *histp += *iptr; + iptr += nCLevels; + } + } + histp++; + } + } + first = ptr->bmin; + last = ptr->bmax; + break; + } + } + /* find median point */ + sum2 = ptr->total / 2; + histp = &hist2[first]; + sum = 0; + for (i = first; i <= last && (sum += *histp++) < sum2; ++i) + ; + if (i == first) + i++; + + /* Create new box, re-allocate points */ + new_cb = *pfreeboxes; + *pfreeboxes = new_cb->next; + if (*pfreeboxes) + (*pfreeboxes)->prev = NULL; + if (*pusedboxes) + (*pusedboxes)->prev = new_cb; + new_cb->next = *pusedboxes; + *pusedboxes = new_cb; + + histp = &hist2[first]; + for (sum1 = 0, j = first; j < i; j++) + sum1 += *histp++; + for (sum2 = 0, j = i; j <= last; j++) + sum2 += *histp++; + new_cb->total = sum1; + ptr->total = sum2; + + new_cb->rmin = ptr->rmin; + new_cb->rmax = ptr->rmax; + new_cb->gmin = ptr->gmin; + new_cb->gmax = ptr->gmax; + new_cb->bmin = ptr->bmin; + new_cb->bmax = ptr->bmax; + switch (axis) { + case RED: + new_cb->rmax = i-1; + ptr->rmin = i; + break; + case GREEN: + new_cb->gmax = i-1; + ptr->gmin = i; + break; + case BLUE: + new_cb->bmax = i-1; + ptr->bmin = i; + break; + } + if( nPixels != 0 && + (new_cb->rmax - new_cb->rmin + 1) * (new_cb->gmax - new_cb->gmin + 1) * + (new_cb->bmax - new_cb->bmin + 1) > nPixels ) + { + shrinkboxFromBand(new_cb, pabyRedBand, pabyGreenBand, pabyBlueBand, nPixels); + } + else if( psHashHistogram != NULL ) + { + shrinkboxFromHashHistogram(new_cb, psHashHistogram); + } + else + { + shrinkbox(new_cb, histogram, nCLevels); + } + if( nPixels != 0 && + (ptr->rmax - ptr->rmin + 1) * (ptr->gmax - ptr->gmin + 1) * + (ptr->bmax - ptr->bmin + 1) > nPixels ) + { + shrinkboxFromBand(ptr, pabyRedBand, pabyGreenBand, pabyBlueBand, nPixels); + } + else if( psHashHistogram != NULL ) + { + shrinkboxFromHashHistogram(ptr, psHashHistogram); + } + else + { + shrinkbox(ptr, histogram, nCLevels); + } +} + +/************************************************************************/ +/* shrinkbox() */ +/************************************************************************/ +static void +shrinkbox(Colorbox* box, const int* histogram, int nCLevels) +{ + const int *histp; + int ir, ig, ib; + //int count_iter; + + if (box->rmax > box->rmin) { + //count_iter = 0; + for (ir = box->rmin; ir <= box->rmax; ++ir) { + for (ig = box->gmin; ig <= box->gmax; ++ig) { + histp = &HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin); + for (ib = box->bmin; ib <= box->bmax; ++ib) { + //count_iter ++; + if (*histp++ != 0) { + //if( count_iter > 65536 ) printf("iter rmin=%d\n", count_iter); + box->rmin = ir; + goto have_rmin; + } + } + } + } + } + have_rmin: + if (box->rmax > box->rmin) { + //count_iter = 0; + for (ir = box->rmax; ir >= box->rmin; --ir) { + for (ig = box->gmin; ig <= box->gmax; ++ig) { + histp = &HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin); + ib = box->bmin; + for (; ib <= box->bmax; ++ib) { + //count_iter ++; + if (*histp++ != 0) { + //if( count_iter > 65536 ) printf("iter rmax=%d\n", count_iter); + box->rmax = ir; + goto have_rmax; + } + } + } + } + } + + have_rmax: + if (box->gmax > box->gmin) { + //count_iter = 0; + for (ig = box->gmin; ig <= box->gmax; ++ig) { + for (ir = box->rmin; ir <= box->rmax; ++ir) { + histp = &HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin); + for (ib = box->bmin; ib <= box->bmax; ++ib) { + //count_iter ++; + if (*histp++ != 0) { + //if( count_iter > 65536 ) printf("iter gmin=%d\n", count_iter); + box->gmin = ig; + goto have_gmin; + } + } + } + } + } + + have_gmin: + if (box->gmax > box->gmin) { + //count_iter = 0; + for (ig = box->gmax; ig >= box->gmin; --ig) { + for (ir = box->rmin; ir <= box->rmax; ++ir) { + histp = &HISTOGRAM(histogram, nCLevels, ir, ig, box->bmin); + ib = box->bmin; + for (; ib <= box->bmax; ++ib) { + //count_iter ++; + if (*histp++ != 0) { + //if( count_iter > 65536 ) printf("iter gmax=%d\n", count_iter); + box->gmax = ig; + goto have_gmax; + } + } + } + } + } + + have_gmax: + if (box->bmax > box->bmin) { + //count_iter = 0; + for (ib = box->bmin; ib <= box->bmax; ++ib) { + for (ir = box->rmin; ir <= box->rmax; ++ir) { + histp = &HISTOGRAM(histogram, nCLevels, ir, box->gmin, ib); + for (ig = box->gmin; ig <= box->gmax; ++ig) { + //count_iter ++; + if (*histp != 0) { + //if( count_iter > 65536 ) printf("iter bmin=%d\n", count_iter); + box->bmin = ib; + goto have_bmin; + } + histp += nCLevels; + } + } + } + } + + have_bmin: + if (box->bmax > box->bmin) { + //count_iter = 0; + for (ib = box->bmax; ib >= box->bmin; --ib) { + for (ir = box->rmin; ir <= box->rmax; ++ir) { + histp = &HISTOGRAM(histogram, nCLevels, ir, box->gmin, ib); + ig = box->gmin; + for (; ig <= box->gmax; ++ig) { + //count_iter ++; + if (*histp != 0) { + //if( count_iter > 65536 ) printf("iter bmax=%d\n", count_iter); + box->bmax = ib; + goto have_bmax; + } + histp += nCLevels; + } + } + } + } + + have_bmax: + ; +} diff --git a/bazaar/plugin/gdal/alg/gdalproximity.cpp b/bazaar/plugin/gdal/alg/gdalproximity.cpp new file mode 100644 index 000000000..af1381535 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalproximity.cpp @@ -0,0 +1,560 @@ +/****************************************************************************** + * $Id: gdalproximity.cpp 28882 2015-04-09 15:59:38Z rouault $ + * + * Project: GDAL + * Purpose: Compute each pixel's proximity to a set of target pixels. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_alg.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: gdalproximity.cpp 28882 2015-04-09 15:59:38Z rouault $"); + +static CPLErr +ProcessProximityLine( GInt32 *panSrcScanline, int *panNearX, int *panNearY, + int bForward, int iLine, int nXSize, double nMaxDist, + float *pafProximity, double *pdfSrcNoDataValue, + int nTargetValues, int *panTargetValues ); + +/************************************************************************/ +/* GDALComputeProximity() */ +/************************************************************************/ + +/** +Compute the proximity of all pixels in the image to a set of pixels in the source image. + +This function attempts to compute the proximity of all pixels in +the image to a set of pixels in the source image. The following +options are used to define the behavior of the function. By +default all non-zero pixels in hSrcBand will be considered the +"target", and all proximities will be computed in pixels. Note +that target pixels are set to the value corresponding to a distance +of zero. + +The progress function args may be NULL or a valid progress reporting function +such as GDALTermProgress/NULL. + +Options: + + VALUES=n[,n]* + +A list of target pixel values to measure the distance from. If this +option is not provided proximity will be computed from non-zero +pixel values. Currently pixel values are internally processed as +integers. + + DISTUNITS=[PIXEL]/GEO + +Indicates whether distances will be computed in pixel units or +in georeferenced units. The default is pixel units. This also +determines the interpretation of MAXDIST. + + MAXDIST=n + +The maximum distance to search. Proximity distances greater than +this value will not be computed. Instead output pixels will be +set to a nodata value. + + NODATA=n + +The NODATA value to use on the output band for pixels that are +beyond MAXDIST. If not provided, the hProximityBand will be +queried for a nodata value. If one is not found, 65535 will be used. + + USE_INPUT_NODATA=YES/NO + +If this option is set, the input data set no-data value will be +respected. Leaving no data pixels in the input as no data pixels in +the proximity output. + + FIXED_BUF_VAL=n + +If this option is set, all pixels within the MAXDIST threadhold are +set to this fixed value instead of to a proximity distance. +*/ + + +CPLErr CPL_STDCALL +GDALComputeProximity( GDALRasterBandH hSrcBand, + GDALRasterBandH hProximityBand, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + int nXSize, nYSize, i, bFixedBufVal = FALSE; + const char *pszOpt; + double dfMaxDist; + double dfFixedBufVal = 0.0; + + VALIDATE_POINTER1( hSrcBand, "GDALComputeProximity", CE_Failure ); + VALIDATE_POINTER1( hProximityBand, "GDALComputeProximity", CE_Failure ); + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* Are we using pixels or georeferenced coordinates for distances? */ +/* -------------------------------------------------------------------- */ + double dfDistMult = 1.0; + pszOpt = CSLFetchNameValue( papszOptions, "DISTUNITS" ); + if( pszOpt ) + { + if( EQUAL(pszOpt,"GEO") ) + { + GDALDatasetH hSrcDS = GDALGetBandDataset( hSrcBand ); + if( hSrcDS ) + { + double adfGeoTransform[6]; + + GDALGetGeoTransform( hSrcDS, adfGeoTransform ); + if( ABS(adfGeoTransform[1]) != ABS(adfGeoTransform[5]) ) + CPLError( CE_Warning, CPLE_AppDefined, + "Pixels not square, distances will be inaccurate." ); + dfDistMult = ABS(adfGeoTransform[1]); + } + } + else if( !EQUAL(pszOpt,"PIXEL") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised DISTUNITS value '%s', should be GEO or PIXEL.", + pszOpt ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* What is our maxdist value? */ +/* -------------------------------------------------------------------- */ + pszOpt = CSLFetchNameValue( papszOptions, "MAXDIST" ); + if( pszOpt ) + dfMaxDist = CPLAtof(pszOpt) / dfDistMult; + else + dfMaxDist = GDALGetRasterBandXSize(hSrcBand) + GDALGetRasterBandYSize(hSrcBand); + + CPLDebug( "GDAL", "MAXDIST=%g, DISTMULT=%g", dfMaxDist, dfDistMult ); + +/* -------------------------------------------------------------------- */ +/* Verify the source and destination are compatible. */ +/* -------------------------------------------------------------------- */ + nXSize = GDALGetRasterBandXSize( hSrcBand ); + nYSize = GDALGetRasterBandYSize( hSrcBand ); + if( nXSize != GDALGetRasterBandXSize( hProximityBand ) + || nYSize != GDALGetRasterBandYSize( hProximityBand )) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Source and proximity bands are not the same size." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Get input NODATA value. */ +/* -------------------------------------------------------------------- */ + double dfSrcNoDataValue = 0.0; + double *pdfSrcNoData = NULL; + if( CSLFetchBoolean( papszOptions, "USE_INPUT_NODATA", FALSE ) ) + { + int bSrcHasNoData = 0; + dfSrcNoDataValue = GDALGetRasterNoDataValue( hSrcBand, &bSrcHasNoData ); + if( bSrcHasNoData ) + pdfSrcNoData = &dfSrcNoDataValue; + } + +/* -------------------------------------------------------------------- */ +/* Get output NODATA value. */ +/* -------------------------------------------------------------------- */ + float fNoDataValue; + pszOpt = CSLFetchNameValue( papszOptions, "NODATA" ); + if( pszOpt != NULL ) + fNoDataValue = (float) CPLAtof(pszOpt); + else + { + int bSuccess; + + fNoDataValue = (float) GDALGetRasterNoDataValue( hProximityBand, &bSuccess ); + if( !bSuccess ) + fNoDataValue = 65535.0; + } + +/* -------------------------------------------------------------------- */ +/* Is there a fixed value we wish to force the buffer area to? */ +/* -------------------------------------------------------------------- */ + pszOpt = CSLFetchNameValue( papszOptions, "FIXED_BUF_VAL" ); + if( pszOpt ) + { + dfFixedBufVal = CPLAtof(pszOpt); + bFixedBufVal = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Get the target value(s). */ +/* -------------------------------------------------------------------- */ + int *panTargetValues = NULL; + int nTargetValues = 0; + + pszOpt = CSLFetchNameValue( papszOptions, "VALUES" ); + if( pszOpt != NULL ) + { + char **papszValuesTokens; + + papszValuesTokens = CSLTokenizeStringComplex( pszOpt, ",", FALSE,FALSE); + + nTargetValues = CSLCount(papszValuesTokens); + panTargetValues = (int *) CPLCalloc(sizeof(int),nTargetValues); + + for( i = 0; i < nTargetValues; i++ ) + panTargetValues[i] = atoi(papszValuesTokens[i]); + CSLDestroy( papszValuesTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize progress counter. */ +/* -------------------------------------------------------------------- */ + if( !pfnProgress( 0.0, "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + CPLFree(panTargetValues); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* We need a signed type for the working proximity values kept */ +/* on disk. If our proximity band is not signed, then create a */ +/* temporary file for this purpose. */ +/* -------------------------------------------------------------------- */ + GDALRasterBandH hWorkProximityBand = hProximityBand; + GDALDatasetH hWorkProximityDS = NULL; + GDALDataType eProxType = GDALGetRasterDataType( hProximityBand ); + int *panNearX = NULL, *panNearY = NULL; + float *pafProximity = NULL; + GInt32 *panSrcScanline = NULL; + int iLine; + CPLErr eErr = CE_None; + + if( eProxType == GDT_Byte + || eProxType == GDT_UInt16 + || eProxType == GDT_UInt32 ) + { + GDALDriverH hDriver = GDALGetDriverByName("GTiff"); + if (hDriver == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GDALComputeProximity needs GTiff driver"); + eErr = CE_Failure; + goto end; + } + CPLString osTmpFile = CPLGenerateTempFilename( "proximity" ); + hWorkProximityDS = + GDALCreate( hDriver, osTmpFile, + nXSize, nYSize, 1, GDT_Float32, NULL ); + if (hWorkProximityDS == NULL) + { + eErr = CE_Failure; + goto end; + } + hWorkProximityBand = GDALGetRasterBand( hWorkProximityDS, 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Allocate buffer for two scanlines of distances as floats */ +/* (the current and last line). */ +/* -------------------------------------------------------------------- */ + pafProximity = (float *) VSIMalloc2(sizeof(float), nXSize); + panNearX = (int *) VSIMalloc2(sizeof(int), nXSize); + panNearY = (int *) VSIMalloc2(sizeof(int), nXSize); + panSrcScanline = (GInt32 *) VSIMalloc2(sizeof(GInt32), nXSize); + + if( pafProximity== NULL + || panNearX == NULL + || panNearY == NULL + || panSrcScanline == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating working buffers."); + eErr = CE_Failure; + goto end; + } + +/* -------------------------------------------------------------------- */ +/* Loop from top to bottom of the image. */ +/* -------------------------------------------------------------------- */ + + for( i = 0; i < nXSize; i++ ) + panNearX[i] = panNearY[i] = -1; + + for( iLine = 0; eErr == CE_None && iLine < nYSize; iLine++ ) + { + // Read for target values. + eErr = GDALRasterIO( hSrcBand, GF_Read, 0, iLine, nXSize, 1, + panSrcScanline, nXSize, 1, GDT_Int32, 0, 0 ); + if( eErr != CE_None ) + break; + + for( i = 0; i < nXSize; i++ ) + pafProximity[i] = -1.0; + + // Left to right + ProcessProximityLine( panSrcScanline, panNearX, panNearY, + TRUE, iLine, nXSize, dfMaxDist, pafProximity, + pdfSrcNoData, nTargetValues, panTargetValues ); + + // Right to Left + ProcessProximityLine( panSrcScanline, panNearX, panNearY, + FALSE, iLine, nXSize, dfMaxDist, pafProximity, + pdfSrcNoData, nTargetValues, panTargetValues ); + + // Write out results. + eErr = + GDALRasterIO( hWorkProximityBand, GF_Write, 0, iLine, nXSize, 1, + pafProximity, nXSize, 1, GDT_Float32, 0, 0 ); + + if( eErr != CE_None ) + break; + + if( !pfnProgress( 0.5 * (iLine+1) / (double) nYSize, + "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Loop from bottom to top of the image. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nXSize; i++ ) + panNearX[i] = panNearY[i] = -1; + + for( iLine = nYSize-1; eErr == CE_None && iLine >= 0; iLine-- ) + { + // Read first pass proximity + eErr = + GDALRasterIO( hWorkProximityBand, GF_Read, 0, iLine, nXSize, 1, + pafProximity, nXSize, 1, GDT_Float32, 0, 0 ); + + if( eErr != CE_None ) + break; + + // Read pixel values. + + eErr = GDALRasterIO( hSrcBand, GF_Read, 0, iLine, nXSize, 1, + panSrcScanline, nXSize, 1, GDT_Int32, 0, 0 ); + if( eErr != CE_None ) + break; + + // Right to left + ProcessProximityLine( panSrcScanline, panNearX, panNearY, + FALSE, iLine, nXSize, dfMaxDist, pafProximity, + pdfSrcNoData, nTargetValues, panTargetValues ); + + // Left to right + ProcessProximityLine( panSrcScanline, panNearX, panNearY, + TRUE, iLine, nXSize, dfMaxDist, pafProximity, + pdfSrcNoData, nTargetValues, panTargetValues ); + + // Final post processing of distances. + for( i = 0; i < nXSize; i++ ) + { + if( pafProximity[i] < 0.0 ) + pafProximity[i] = fNoDataValue; + else if( pafProximity[i] > 0.0 ) + { + if( bFixedBufVal ) + pafProximity[i] = (float) dfFixedBufVal; + else + pafProximity[i] = (float)(pafProximity[i] * dfDistMult); + } + } + + // Write out results. + eErr = + GDALRasterIO( hProximityBand, GF_Write, 0, iLine, nXSize, 1, + pafProximity, nXSize, 1, GDT_Float32, 0, 0 ); + + if( eErr != CE_None ) + break; + + if( !pfnProgress( 0.5 + 0.5 * (nYSize-iLine) / (double) nYSize, + "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ +end: + CPLFree( panNearX ); + CPLFree( panNearY ); + CPLFree( panSrcScanline ); + CPLFree( pafProximity ); + CPLFree(panTargetValues); + + if( hWorkProximityDS != NULL ) + { + CPLString osProxFile = GDALGetDescription( hWorkProximityDS ); + GDALClose( hWorkProximityDS ); + GDALDeleteDataset( GDALGetDriverByName( "GTiff" ), osProxFile ); + } + + return eErr; +} + +/************************************************************************/ +/* ProcessProximityLine() */ +/************************************************************************/ + +static CPLErr +ProcessProximityLine( GInt32 *panSrcScanline, int *panNearX, int *panNearY, + int bForward, int iLine, int nXSize, double dfMaxDist, + float *pafProximity, double *pdfSrcNoDataValue, + int nTargetValues, int *panTargetValues ) + +{ + int iStart, iEnd, iStep, iPixel; + + if( bForward ) + { + iStart = 0; + iEnd = nXSize; + iStep = 1; + } + else + { + iStart = nXSize-1; + iEnd = -1; + iStep = -1; + } + + for( iPixel = iStart; iPixel != iEnd; iPixel += iStep ) + { + int bIsTarget = FALSE; + +/* -------------------------------------------------------------------- */ +/* Is the current pixel a target pixel? */ +/* -------------------------------------------------------------------- */ + if( nTargetValues == 0 ) + bIsTarget = (panSrcScanline[iPixel] != 0); + else + { + int i; + + for( i = 0; i < nTargetValues; i++ ) + { + if( panSrcScanline[iPixel] == panTargetValues[i] ) + bIsTarget = TRUE; + } + } + + if( bIsTarget ) + { + pafProximity[iPixel] = 0.0; + panNearX[iPixel] = iPixel; + panNearY[iPixel] = iLine; + continue; + } + +/* -------------------------------------------------------------------- */ +/* Are we near(er) to the closest target to the above (below) */ +/* pixel? */ +/* -------------------------------------------------------------------- */ + float fNearDistSq = (float) (MAX(dfMaxDist,nXSize) * MAX(dfMaxDist,nXSize) * 2); + float fDistSq; + + if( panNearX[iPixel] != -1 ) + { + fDistSq = (float) + ((panNearX[iPixel] - iPixel) * (panNearX[iPixel] - iPixel) + + (panNearY[iPixel] - iLine) * (panNearY[iPixel] - iLine)); + + if( fDistSq < fNearDistSq ) + { + fNearDistSq = fDistSq; + } + else + { + panNearX[iPixel] = -1; + panNearY[iPixel] = -1; + } + } + +/* -------------------------------------------------------------------- */ +/* Are we near(er) to the closest target to the left (right) */ +/* pixel? */ +/* -------------------------------------------------------------------- */ + int iLast = iPixel-iStep; + + if( iPixel != iStart && panNearX[iLast] != -1 ) + { + fDistSq = (float) + ((panNearX[iLast] - iPixel) * (panNearX[iLast] - iPixel) + + (panNearY[iLast] - iLine) * (panNearY[iLast] - iLine)); + + if( fDistSq < fNearDistSq ) + { + fNearDistSq = fDistSq; + panNearX[iPixel] = panNearX[iLast]; + panNearY[iPixel] = panNearY[iLast]; + } + } + +/* -------------------------------------------------------------------- */ +/* Are we near(er) to the closest target to the topright */ +/* (bottom left) pixel? */ +/* -------------------------------------------------------------------- */ + int iTR = iPixel+iStep; + + if( iTR != iEnd && panNearX[iTR] != -1 ) + { + fDistSq = (float) + ((panNearX[iTR] - iPixel) * (panNearX[iTR] - iPixel) + + (panNearY[iTR] - iLine) * (panNearY[iTR] - iLine)); + + if( fDistSq < fNearDistSq ) + { + fNearDistSq = fDistSq; + panNearX[iPixel] = panNearX[iTR]; + panNearY[iPixel] = panNearY[iTR]; + } + } + +/* -------------------------------------------------------------------- */ +/* Update our proximity value. */ +/* -------------------------------------------------------------------- */ + if( panNearX[iPixel] != -1 + && (pdfSrcNoDataValue == NULL + || panSrcScanline[iPixel] != *pdfSrcNoDataValue) + && fNearDistSq <= dfMaxDist * dfMaxDist + && (pafProximity[iPixel] < 0 + || fNearDistSq < pafProximity[iPixel] * pafProximity[iPixel]) ) + pafProximity[iPixel] = sqrt(fNearDistSq); + } + + return CE_None; +} diff --git a/bazaar/plugin/gdal/alg/gdalrasterfpolygonenumerator.cpp b/bazaar/plugin/gdal/alg/gdalrasterfpolygonenumerator.cpp new file mode 100644 index 000000000..ccbfa1f8a --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalrasterfpolygonenumerator.cpp @@ -0,0 +1,250 @@ +/****************************************************************************** + * $Id: gdalrasterfpolygonenumerator.cpp 24379 2012-05-04 01:26:19Z warmerdam $ + * + * Project: GDAL + * Purpose: Version of Raster Polygon Enumerator using float buffers. + * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com. Most of the code + * taken from GDALRasterFPolygonEnumerator, by Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Jorge Arevalo + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_alg_priv.h" +#include "cpl_conv.h" +#include + +CPL_CVSID("$Id: gdalrasterfpolygonenumerator.cpp 24379 2012-05-04 01:26:19Z warmerdam $"); + +#ifdef OGR_ENABLED +/************************************************************************/ +/* GDALRasterFPolygonEnumerator() */ +/************************************************************************/ + +GDALRasterFPolygonEnumerator::GDALRasterFPolygonEnumerator( + int nConnectedness ) + +{ + panPolyIdMap = NULL; + pafPolyValue = NULL; + nNextPolygonId = 0; + nPolyAlloc = 0; + this->nConnectedness = nConnectedness; + CPLAssert( nConnectedness == 4 || nConnectedness == 8 ); +} + +/************************************************************************/ +/* ~GDALRasterFPolygonEnumerator() */ +/************************************************************************/ + +GDALRasterFPolygonEnumerator::~GDALRasterFPolygonEnumerator() + +{ + Clear(); +} + +/************************************************************************/ +/* Clear() */ +/************************************************************************/ + +void GDALRasterFPolygonEnumerator::Clear() + +{ + CPLFree( panPolyIdMap ); + CPLFree( pafPolyValue ); + + panPolyIdMap = NULL; + pafPolyValue = NULL; + + nNextPolygonId = 0; + nPolyAlloc = 0; +} + +/************************************************************************/ +/* MergePolygon() */ +/* */ +/* Update the polygon map to indicate the merger of two polygons. */ +/************************************************************************/ + +void GDALRasterFPolygonEnumerator::MergePolygon( int nSrcId, int nDstId ) + +{ + while( panPolyIdMap[nDstId] != nDstId ) + nDstId = panPolyIdMap[nDstId]; + + while( panPolyIdMap[nSrcId] != nSrcId ) + nSrcId = panPolyIdMap[nSrcId]; + + if( nSrcId == nDstId ) + return; + + panPolyIdMap[nSrcId] = nDstId; +} + +/************************************************************************/ +/* NewPolygon() */ +/* */ +/* Allocate a new polygon id, and reallocate the polygon maps */ +/* if needed. */ +/************************************************************************/ + +int GDALRasterFPolygonEnumerator::NewPolygon( float fValue ) + +{ + int nPolyId = nNextPolygonId; + + if( nNextPolygonId >= nPolyAlloc ) + { + nPolyAlloc = nPolyAlloc * 2 + 20; + panPolyIdMap = (GInt32 *) CPLRealloc(panPolyIdMap,nPolyAlloc*4); + pafPolyValue = (float *) CPLRealloc(pafPolyValue,nPolyAlloc*4); + } + + nNextPolygonId++; + + panPolyIdMap[nPolyId] = nPolyId; + pafPolyValue[nPolyId] = fValue; + + return nPolyId; +} + +/************************************************************************/ +/* CompleteMerges() */ +/* */ +/* Make a pass through the maps, ensuring every polygon id */ +/* points to the final id it should use, not an intermediate */ +/* value. */ +/************************************************************************/ + +void GDALRasterFPolygonEnumerator::CompleteMerges() + +{ + int iPoly; + int nFinalPolyCount = 0; + + for( iPoly = 0; iPoly < nNextPolygonId; iPoly++ ) + { + while( panPolyIdMap[iPoly] + != panPolyIdMap[panPolyIdMap[iPoly]] ) + panPolyIdMap[iPoly] = panPolyIdMap[panPolyIdMap[iPoly]]; + + if( panPolyIdMap[iPoly] == iPoly ) + nFinalPolyCount++; + } + + CPLDebug( "GDALRasterFPolygonEnumerator", + "Counted %d polygon fragments forming %d final polygons.", + nNextPolygonId, nFinalPolyCount ); +} + +/************************************************************************/ +/* ProcessLine() */ +/* */ +/* Assign ids to polygons, one line at a time. */ +/************************************************************************/ + +void GDALRasterFPolygonEnumerator::ProcessLine( + float *pafLastLineVal, float *pafThisLineVal, + GInt32 *panLastLineId, GInt32 *panThisLineId, + int nXSize ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Special case for the first line. */ +/* -------------------------------------------------------------------- */ + if( pafLastLineVal == NULL ) + { + for( i=0; i < nXSize; i++ ) + { + if( i == 0 || !GDALFloatEquals(pafThisLineVal[i], pafThisLineVal[i-1]) ) + { + panThisLineId[i] = NewPolygon( pafThisLineVal[i] ); + } + else + panThisLineId[i] = panThisLineId[i-1]; + } + + return; + } + +/* -------------------------------------------------------------------- */ +/* Process each pixel comparing to the previous pixel, and to */ +/* the last line. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nXSize; i++ ) + { + if( i > 0 && GDALFloatEquals(pafThisLineVal[i], pafThisLineVal[i-1]) ) + { + panThisLineId[i] = panThisLineId[i-1]; + + if( GDALFloatEquals(pafLastLineVal[i], pafThisLineVal[i]) + && (panPolyIdMap[panLastLineId[i]] + != panPolyIdMap[panThisLineId[i]]) ) + { + MergePolygon( panLastLineId[i], panThisLineId[i] ); + } + + if( nConnectedness == 8 + && pafLastLineVal[i-1] == pafThisLineVal[i] + && (panPolyIdMap[panLastLineId[i-1]] + != panPolyIdMap[panThisLineId[i]]) ) + { + MergePolygon( panLastLineId[i-1], panThisLineId[i] ); + } + + if( nConnectedness == 8 && i < nXSize-1 + && pafLastLineVal[i+1] == pafThisLineVal[i] + && (panPolyIdMap[panLastLineId[i+1]] + != panPolyIdMap[panThisLineId[i]]) ) + { + MergePolygon( panLastLineId[i+1], panThisLineId[i] ); + } + } + else if( GDALFloatEquals(pafLastLineVal[i], pafThisLineVal[i]) ) + { + panThisLineId[i] = panLastLineId[i]; + } + else if( i > 0 && nConnectedness == 8 + && GDALFloatEquals(pafLastLineVal[i-1], pafThisLineVal[i]) ) + { + panThisLineId[i] = panLastLineId[i-1]; + + if( i < nXSize-1 && pafLastLineVal[i+1] == pafThisLineVal[i] + && (panPolyIdMap[panLastLineId[i+1]] + != panPolyIdMap[panThisLineId[i]]) ) + { + MergePolygon( panLastLineId[i+1], panThisLineId[i] ); + } + } + else if( i < nXSize-1 && nConnectedness == 8 + && GDALFloatEquals(pafLastLineVal[i+1], pafThisLineVal[i]) ) + { + panThisLineId[i] = panLastLineId[i+1]; + } + else + panThisLineId[i] = + NewPolygon( pafThisLineVal[i] ); + } +} +#endif diff --git a/bazaar/plugin/gdal/alg/gdalrasterize.cpp b/bazaar/plugin/gdal/alg/gdalrasterize.cpp new file mode 100644 index 000000000..58c98cdf9 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalrasterize.cpp @@ -0,0 +1,1323 @@ +/****************************************************************************** + * $Id: gdalrasterize.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: GDAL + * Purpose: Vector rasterization. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include + +#include "gdal_alg.h" +#include "gdal_alg_priv.h" +#include "gdal_priv.h" +#include "ogr_api.h" +#include "ogr_geometry.h" +#include "ogr_spatialref.h" + +#ifdef OGR_ENABLED +#include "ogrsf_frmts.h" +#endif + +/************************************************************************/ +/* gvBurnScanline() */ +/************************************************************************/ + +void gvBurnScanline( void *pCBData, int nY, int nXStart, int nXEnd, + double dfVariant ) + +{ + GDALRasterizeInfo *psInfo = (GDALRasterizeInfo *) pCBData; + int iBand; + + if( nXStart > nXEnd ) + return; + + CPLAssert( nY >= 0 && nY < psInfo->nYSize ); + CPLAssert( nXStart <= nXEnd ); + CPLAssert( nXStart < psInfo->nXSize ); + CPLAssert( nXEnd >= 0 ); + + if( nXStart < 0 ) + nXStart = 0; + if( nXEnd >= psInfo->nXSize ) + nXEnd = psInfo->nXSize - 1; + + if( psInfo->eType == GDT_Byte ) + { + for( iBand = 0; iBand < psInfo->nBands; iBand++ ) + { + unsigned char *pabyInsert; + unsigned char nBurnValue = (unsigned char) + ( psInfo->padfBurnValue[iBand] + + ( (psInfo->eBurnValueSource == GBV_UserBurnValue)? + 0 : dfVariant ) ); + + pabyInsert = psInfo->pabyChunkBuf + + iBand * psInfo->nXSize * psInfo->nYSize + + nY * psInfo->nXSize + nXStart; + + if( psInfo->eMergeAlg == GRMA_Add ) { + int nPixels = nXEnd - nXStart + 1; + while( nPixels-- > 0 ) + *(pabyInsert++) += nBurnValue; + } else { + memset( pabyInsert, nBurnValue, nXEnd - nXStart + 1 ); + } + } + } + else if( psInfo->eType == GDT_Float64 ) + { + for( iBand = 0; iBand < psInfo->nBands; iBand++ ) + { + int nPixels = nXEnd - nXStart + 1; + double *padfInsert; + double dfBurnValue = + ( psInfo->padfBurnValue[iBand] + + ( (psInfo->eBurnValueSource == GBV_UserBurnValue)? + 0 : dfVariant ) ); + + padfInsert = ((double *) psInfo->pabyChunkBuf) + + iBand * psInfo->nXSize * psInfo->nYSize + + nY * psInfo->nXSize + nXStart; + + if( psInfo->eMergeAlg == GRMA_Add ) { + while( nPixels-- > 0 ) + *(padfInsert++) += dfBurnValue; + } else { + while( nPixels-- > 0 ) + *(padfInsert++) = dfBurnValue; + } + } + } + else { + CPLAssert(0); + } +} + +/************************************************************************/ +/* gvBurnPoint() */ +/************************************************************************/ + +void gvBurnPoint( void *pCBData, int nY, int nX, double dfVariant ) + +{ + GDALRasterizeInfo *psInfo = (GDALRasterizeInfo *) pCBData; + int iBand; + + CPLAssert( nY >= 0 && nY < psInfo->nYSize ); + CPLAssert( nX >= 0 && nX < psInfo->nXSize ); + + if( psInfo->eType == GDT_Byte ) + { + for( iBand = 0; iBand < psInfo->nBands; iBand++ ) + { + unsigned char *pbyInsert = psInfo->pabyChunkBuf + + iBand * psInfo->nXSize * psInfo->nYSize + + nY * psInfo->nXSize + nX; + + if( psInfo->eMergeAlg == GRMA_Add ) { + *pbyInsert += (unsigned char)( psInfo->padfBurnValue[iBand] + + ( (psInfo->eBurnValueSource == GBV_UserBurnValue)? + 0 : dfVariant ) ); + } else { + *pbyInsert = (unsigned char)( psInfo->padfBurnValue[iBand] + + ( (psInfo->eBurnValueSource == GBV_UserBurnValue)? + 0 : dfVariant ) ); + } + } + } + else if( psInfo->eType == GDT_Float64 ) + { + for( iBand = 0; iBand < psInfo->nBands; iBand++ ) + { + double *pdfInsert = ((double *) psInfo->pabyChunkBuf) + + iBand * psInfo->nXSize * psInfo->nYSize + + nY * psInfo->nXSize + nX; + + if( psInfo->eMergeAlg == GRMA_Add ) { + *pdfInsert += ( psInfo->padfBurnValue[iBand] + + ( (psInfo->eBurnValueSource == GBV_UserBurnValue)? + 0 : dfVariant ) ); + } else { + *pdfInsert = ( psInfo->padfBurnValue[iBand] + + ( (psInfo->eBurnValueSource == GBV_UserBurnValue)? + 0 : dfVariant ) ); + } + } + } + else { + CPLAssert(0); + } +} + +/************************************************************************/ +/* GDALCollectRingsFromGeometry() */ +/************************************************************************/ + +static void GDALCollectRingsFromGeometry( + OGRGeometry *poShape, + std::vector &aPointX, std::vector &aPointY, + std::vector &aPointVariant, + std::vector &aPartSize, GDALBurnValueSrc eBurnValueSrc) + +{ + if( poShape == NULL ) + return; + + OGRwkbGeometryType eFlatType = wkbFlatten(poShape->getGeometryType()); + int i; + + if ( eFlatType == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poShape; + int nNewCount = aPointX.size() + 1; + + aPointX.reserve( nNewCount ); + aPointY.reserve( nNewCount ); + aPointX.push_back( poPoint->getX() ); + aPointY.push_back( poPoint->getY() ); + aPartSize.push_back( 1 ); + if( eBurnValueSrc != GBV_UserBurnValue ) + { + /*switch( eBurnValueSrc ) + { + case GBV_Z:*/ + aPointVariant.reserve( nNewCount ); + aPointVariant.push_back( poPoint->getZ() ); + /*break; + case GBV_M: + aPointVariant.reserve( nNewCount ); + aPointVariant.push_back( poPoint->getM() ); + }*/ + } + } + else if ( eFlatType == wkbLineString ) + { + OGRLineString *poLine = (OGRLineString *) poShape; + int nCount = poLine->getNumPoints(); + int nNewCount = aPointX.size() + nCount; + + aPointX.reserve( nNewCount ); + aPointY.reserve( nNewCount ); + if( eBurnValueSrc != GBV_UserBurnValue ) + aPointVariant.reserve( nNewCount ); + for ( i = nCount - 1; i >= 0; i-- ) + { + aPointX.push_back( poLine->getX(i) ); + aPointY.push_back( poLine->getY(i) ); + if( eBurnValueSrc != GBV_UserBurnValue ) + { + /*switch( eBurnValueSrc ) + { + case GBV_Z:*/ + aPointVariant.push_back( poLine->getZ(i) ); + /*break; + case GBV_M: + aPointVariant.push_back( poLine->getM(i) ); + }*/ + } + } + aPartSize.push_back( nCount ); + } + else if ( EQUAL(poShape->getGeometryName(),"LINEARRING") ) + { + OGRLinearRing *poRing = (OGRLinearRing *) poShape; + int nCount = poRing->getNumPoints(); + int nNewCount = aPointX.size() + nCount; + + aPointX.reserve( nNewCount ); + aPointY.reserve( nNewCount ); + if( eBurnValueSrc != GBV_UserBurnValue ) + aPointVariant.reserve( nNewCount ); + for ( i = nCount - 1; i >= 0; i-- ) + { + aPointX.push_back( poRing->getX(i) ); + aPointY.push_back( poRing->getY(i) ); + } + if( eBurnValueSrc != GBV_UserBurnValue ) + { + /*switch( eBurnValueSrc ) + { + case GBV_Z:*/ + aPointVariant.push_back( poRing->getZ(i) ); + /*break; + case GBV_M: + aPointVariant.push_back( poRing->getM(i) ); + }*/ + } + aPartSize.push_back( nCount ); + } + else if( eFlatType == wkbPolygon ) + { + OGRPolygon *poPolygon = (OGRPolygon *) poShape; + + GDALCollectRingsFromGeometry( poPolygon->getExteriorRing(), + aPointX, aPointY, aPointVariant, + aPartSize, eBurnValueSrc ); + + for( i = 0; i < poPolygon->getNumInteriorRings(); i++ ) + GDALCollectRingsFromGeometry( poPolygon->getInteriorRing(i), + aPointX, aPointY, aPointVariant, + aPartSize, eBurnValueSrc ); + } + + else if( eFlatType == wkbMultiPoint + || eFlatType == wkbMultiLineString + || eFlatType == wkbMultiPolygon + || eFlatType == wkbGeometryCollection ) + { + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poShape; + + for( i = 0; i < poGC->getNumGeometries(); i++ ) + GDALCollectRingsFromGeometry( poGC->getGeometryRef(i), + aPointX, aPointY, aPointVariant, + aPartSize, eBurnValueSrc ); + } + else + { + CPLDebug( "GDAL", "Rasterizer ignoring non-polygonal geometry." ); + } +} + +/************************************************************************/ +/* gv_rasterize_one_shape() */ +/************************************************************************/ +static void +gv_rasterize_one_shape( unsigned char *pabyChunkBuf, int nYOff, + int nXSize, int nYSize, + int nBands, GDALDataType eType, int bAllTouched, + OGRGeometry *poShape, double *padfBurnValue, + GDALBurnValueSrc eBurnValueSrc, + GDALRasterMergeAlg eMergeAlg, + GDALTransformerFunc pfnTransformer, + void *pTransformArg ) + +{ + GDALRasterizeInfo sInfo; + + if (poShape == NULL) + return; + + sInfo.nXSize = nXSize; + sInfo.nYSize = nYSize; + sInfo.nBands = nBands; + sInfo.pabyChunkBuf = pabyChunkBuf; + sInfo.eType = eType; + sInfo.padfBurnValue = padfBurnValue; + sInfo.eBurnValueSource = eBurnValueSrc; + sInfo.eMergeAlg = eMergeAlg; + +/* -------------------------------------------------------------------- */ +/* Transform polygon geometries into a set of rings and a part */ +/* size list. */ +/* -------------------------------------------------------------------- */ + std::vector aPointX; + std::vector aPointY; + std::vector aPointVariant; + std::vector aPartSize; + + GDALCollectRingsFromGeometry( poShape, aPointX, aPointY, aPointVariant, + aPartSize, eBurnValueSrc ); + +/* -------------------------------------------------------------------- */ +/* Transform points if needed. */ +/* -------------------------------------------------------------------- */ + if( pfnTransformer != NULL ) + { + int *panSuccess = (int *) CPLCalloc(sizeof(int),aPointX.size()); + + // TODO: we need to add all appropriate error checking at some point. + pfnTransformer( pTransformArg, FALSE, aPointX.size(), + &(aPointX[0]), &(aPointY[0]), NULL, panSuccess ); + CPLFree( panSuccess ); + } + +/* -------------------------------------------------------------------- */ +/* Shift to account for the buffer offset of this buffer. */ +/* -------------------------------------------------------------------- */ + unsigned int i; + + for( i = 0; i < aPointY.size(); i++ ) + aPointY[i] -= nYOff; + +/* -------------------------------------------------------------------- */ +/* Perform the rasterization. */ +/* According to the C++ Standard/23.2.4, elements of a vector are */ +/* stored in continuous memory block. */ +/* -------------------------------------------------------------------- */ + + // TODO - mloskot: Check if vectors are empty, otherwise it may + // lead to undefined behavior by returning non-referencable pointer. + // if (!aPointX.empty()) + // /* fill polygon */ + // else + // /* How to report this problem? */ + switch ( wkbFlatten(poShape->getGeometryType()) ) + { + case wkbPoint: + case wkbMultiPoint: + GDALdllImagePoint( sInfo.nXSize, nYSize, + aPartSize.size(), &(aPartSize[0]), + &(aPointX[0]), &(aPointY[0]), + (eBurnValueSrc == GBV_UserBurnValue)? + NULL : &(aPointVariant[0]), + gvBurnPoint, &sInfo ); + break; + case wkbLineString: + case wkbMultiLineString: + { + if( bAllTouched ) + GDALdllImageLineAllTouched( sInfo.nXSize, nYSize, + aPartSize.size(), &(aPartSize[0]), + &(aPointX[0]), &(aPointY[0]), + (eBurnValueSrc == GBV_UserBurnValue)? + NULL : &(aPointVariant[0]), + gvBurnPoint, &sInfo ); + else + GDALdllImageLine( sInfo.nXSize, nYSize, + aPartSize.size(), &(aPartSize[0]), + &(aPointX[0]), &(aPointY[0]), + (eBurnValueSrc == GBV_UserBurnValue)? + NULL : &(aPointVariant[0]), + gvBurnPoint, &sInfo ); + } + break; + + default: + { + GDALdllImageFilledPolygon( sInfo.nXSize, nYSize, + aPartSize.size(), &(aPartSize[0]), + &(aPointX[0]), &(aPointY[0]), + (eBurnValueSrc == GBV_UserBurnValue)? + NULL : &(aPointVariant[0]), + gvBurnScanline, &sInfo ); + if( bAllTouched ) + { + /* Reverting the variants to the first value because the + polygon is filled using the variant from the first point of + the first segment. Should be removed when the code to full + polygons more appropriately is added. */ + if(eBurnValueSrc == GBV_UserBurnValue) + { + GDALdllImageLineAllTouched( sInfo.nXSize, nYSize, + aPartSize.size(), &(aPartSize[0]), + &(aPointX[0]), &(aPointY[0]), + NULL, + gvBurnPoint, &sInfo ); + } + else + { + unsigned int n; + for ( i = 0, n = 0; i < aPartSize.size(); i++ ) + { + int j; + for ( j = 0; j < aPartSize[i]; j++ ) + aPointVariant[n++] = aPointVariant[0]; + } + + GDALdllImageLineAllTouched( sInfo.nXSize, nYSize, + aPartSize.size(), &(aPartSize[0]), + &(aPointX[0]), &(aPointY[0]), + &(aPointVariant[0]), + gvBurnPoint, &sInfo ); + } + } + } + break; + } +} + +/************************************************************************/ +/* GDALRasterizeOptions() */ +/* */ +/* Recognise a few rasterize options used by all three entry */ +/* points. */ +/************************************************************************/ + +static CPLErr GDALRasterizeOptions(char **papszOptions, + int *pbAllTouched, + GDALBurnValueSrc *peBurnValueSource, + GDALRasterMergeAlg *peMergeAlg) +{ + *pbAllTouched = CSLFetchBoolean( papszOptions, "ALL_TOUCHED", FALSE ); + + const char *pszOpt = CSLFetchNameValue( papszOptions, "BURN_VALUE_FROM" ); + *peBurnValueSource = GBV_UserBurnValue; + if( pszOpt ) + { + if( EQUAL(pszOpt,"Z")) + *peBurnValueSource = GBV_Z; + /*else if( EQUAL(pszOpt,"M")) + eBurnValueSource = GBV_M;*/ + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised value '%s' for BURN_VALUE_FROM.", + pszOpt ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* MERGE_ALG=[REPLACE]/ADD */ +/* -------------------------------------------------------------------- */ + *peMergeAlg = GRMA_Replace; + pszOpt = CSLFetchNameValue( papszOptions, "MERGE_ALG" ); + if( pszOpt ) + { + if( EQUAL(pszOpt,"ADD")) + *peMergeAlg = GRMA_Add; + else if( EQUAL(pszOpt,"REPLACE")) + *peMergeAlg = GRMA_Replace; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised value '%s' for MERGE_ALG.", + pszOpt ); + return CE_Failure; + } + } + + return CE_None; +} + +/************************************************************************/ +/* GDALRasterizeGeometries() */ +/************************************************************************/ + +/** + * Burn geometries into raster. + * + * Rasterize a list of geometric objects into a raster dataset. The + * geometries are passed as an array of OGRGeometryH handlers. + * + * If the geometries are in the georferenced coordinates of the raster + * dataset, then the pfnTransform may be passed in NULL and one will be + * derived internally from the geotransform of the dataset. The transform + * needs to transform the geometry locations into pixel/line coordinates + * on the raster dataset. + * + * The output raster may be of any GDAL supported datatype, though currently + * internally the burning is done either as GDT_Byte or GDT_Float32. This + * may be improved in the future. An explicit list of burn values for + * each geometry for each band must be passed in. + * + * The papszOption list of options currently only supports one option. The + * "ALL_TOUCHED" option may be enabled by setting it to "TRUE". + * + * @param hDS output data, must be opened in update mode. + * @param nBandCount the number of bands to be updated. + * @param panBandList the list of bands to be updated. + * @param nGeomCount the number of geometries being passed in pahGeometries. + * @param pahGeometries the array of geometries to burn in. + * @param pfnTransformer transformation to apply to geometries to put into + * pixel/line coordinates on raster. If NULL a geotransform based one will + * be created internally. + * @param pTransformArg callback data for transformer. + * @param padfGeomBurnValue the array of values to burn into the raster. + * There should be nBandCount values for each geometry. + * @param papszOptions special options controlling rasterization + *
+ *
"ALL_TOUCHED":
May be set to TRUE to set all pixels touched + * by the line or polygons, not just those whose center is within the polygon + * or that are selected by brezenhams line algorithm. Defaults to FALSE.
+ *
"BURN_VALUE_FROM":
May be set to "Z" to use the Z values of the + * geometries. dfBurnValue is added to this before burning. + * Defaults to GDALBurnValueSrc.GBV_UserBurnValue in which case just the + * dfBurnValue is burned. This is implemented only for points and lines for + * now. The M value may be supported in the future.
+ *
"MERGE_ALG":
May be REPLACE (the default) or ADD. REPLACE results in overwriting of value, while ADD adds the new value to the existing raster, suitable for heatmaps for instance.
+ *
+ * @param pfnProgress the progress function to report completion. + * @param pProgressArg callback data for progress function. + * + * @return CE_None on success or CE_Failure on error. + */ + +CPLErr GDALRasterizeGeometries( GDALDatasetH hDS, + int nBandCount, int *panBandList, + int nGeomCount, OGRGeometryH *pahGeometries, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfGeomBurnValue, + char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressArg ) + +{ + GDALDataType eType; + int nYChunkSize, nScanlineBytes; + unsigned char *pabyChunkBuf; + int iY; + GDALDataset *poDS = (GDALDataset *) hDS; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* Do some rudimentary arg checking. */ +/* -------------------------------------------------------------------- */ + if( nBandCount == 0 || nGeomCount == 0 ) + { + pfnProgress(1.0, "", pProgressArg ); + return CE_None; + } + + // prototype band. + GDALRasterBand *poBand = poDS->GetRasterBand( panBandList[0] ); + if (poBand == NULL) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Options */ +/* -------------------------------------------------------------------- */ + int bAllTouched; + GDALBurnValueSrc eBurnValueSource; + GDALRasterMergeAlg eMergeAlg; + if( GDALRasterizeOptions(papszOptions, &bAllTouched, + &eBurnValueSource, &eMergeAlg) == CE_Failure) { + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* If we have no transformer, assume the geometries are in file */ +/* georeferenced coordinates, and create a transformer to */ +/* convert that to pixel/line coordinates. */ +/* */ +/* We really just need to apply an affine transform, but for */ +/* simplicity we use the more general GenImgProjTransformer. */ +/* -------------------------------------------------------------------- */ + int bNeedToFreeTransformer = FALSE; + + if( pfnTransformer == NULL ) + { + bNeedToFreeTransformer = TRUE; + + pTransformArg = + GDALCreateGenImgProjTransformer( NULL, NULL, hDS, NULL, + FALSE, 0.0, 0); + pfnTransformer = GDALGenImgProjTransform; + } + +/* -------------------------------------------------------------------- */ +/* Establish a chunksize to operate on. The larger the chunk */ +/* size the less times we need to make a pass through all the */ +/* shapes. */ +/* -------------------------------------------------------------------- */ + if( poBand->GetRasterDataType() == GDT_Byte ) + eType = GDT_Byte; + else + eType = GDT_Float64; + + nScanlineBytes = nBandCount * poDS->GetRasterXSize() + * (GDALGetDataTypeSize(eType)/8); + + const char *pszYChunkSize = CSLFetchNameValue(papszOptions, "CHUNKYSIZE"); + if( pszYChunkSize == NULL || ((nYChunkSize = atoi(pszYChunkSize))) == 0) + { + nYChunkSize = 10000000 / nScanlineBytes; + } + + if( nYChunkSize > poDS->GetRasterYSize() ) + nYChunkSize = poDS->GetRasterYSize(); + + CPLDebug( "GDAL", "Rasterizer operating on %d swaths of %d scanlines.", + (poDS->GetRasterYSize()+nYChunkSize-1) / nYChunkSize, + nYChunkSize ); + + pabyChunkBuf = (unsigned char *) VSIMalloc(nYChunkSize * nScanlineBytes); + if( pabyChunkBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate rasterization buffer." ); + return CE_Failure; + } + +/* ==================================================================== */ +/* Loop over image in designated chunks. */ +/* ==================================================================== */ + CPLErr eErr = CE_None; + + pfnProgress( 0.0, NULL, pProgressArg ); + + for( iY = 0; + iY < poDS->GetRasterYSize() && eErr == CE_None; + iY += nYChunkSize ) + { + int nThisYChunkSize; + int iShape; + + nThisYChunkSize = nYChunkSize; + if( nThisYChunkSize + iY > poDS->GetRasterYSize() ) + nThisYChunkSize = poDS->GetRasterYSize() - iY; + + eErr = + poDS->RasterIO(GF_Read, + 0, iY, poDS->GetRasterXSize(), nThisYChunkSize, + pabyChunkBuf,poDS->GetRasterXSize(),nThisYChunkSize, + eType, nBandCount, panBandList, + 0, 0, 0, NULL ); + if( eErr != CE_None ) + break; + + for( iShape = 0; iShape < nGeomCount; iShape++ ) + { + gv_rasterize_one_shape( pabyChunkBuf, iY, + poDS->GetRasterXSize(), nThisYChunkSize, + nBandCount, eType, bAllTouched, + (OGRGeometry *) pahGeometries[iShape], + padfGeomBurnValue + iShape*nBandCount, + eBurnValueSource, eMergeAlg, + pfnTransformer, pTransformArg ); + } + + eErr = + poDS->RasterIO( GF_Write, 0, iY, + poDS->GetRasterXSize(), nThisYChunkSize, + pabyChunkBuf, + poDS->GetRasterXSize(), nThisYChunkSize, + eType, nBandCount, panBandList, 0, 0, 0, NULL); + + if( !pfnProgress((iY+nThisYChunkSize)/((double)poDS->GetRasterYSize()), + "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* cleanup */ +/* -------------------------------------------------------------------- */ + VSIFree( pabyChunkBuf ); + + if( bNeedToFreeTransformer ) + GDALDestroyTransformer( pTransformArg ); + + return eErr; +} + +/************************************************************************/ +/* GDALRasterizeLayers() */ +/************************************************************************/ + +/** + * Burn geometries from the specified list of layers into raster. + * + * Rasterize all the geometric objects from a list of layers into a raster + * dataset. The layers are passed as an array of OGRLayerH handlers. + * + * If the geometries are in the georferenced coordinates of the raster + * dataset, then the pfnTransform may be passed in NULL and one will be + * derived internally from the geotransform of the dataset. The transform + * needs to transform the geometry locations into pixel/line coordinates + * on the raster dataset. + * + * The output raster may be of any GDAL supported datatype, though currently + * internally the burning is done either as GDT_Byte or GDT_Float32. This + * may be improved in the future. An explicit list of burn values for + * each layer for each band must be passed in. + * + * @param hDS output data, must be opened in update mode. + * @param nBandCount the number of bands to be updated. + * @param panBandList the list of bands to be updated. + * @param nLayerCount the number of layers being passed in pahLayers array. + * @param pahLayers the array of layers to burn in. + * @param pfnTransformer transformation to apply to geometries to put into + * pixel/line coordinates on raster. If NULL a geotransform based one will + * be created internally. + * @param pTransformArg callback data for transformer. + * @param padfLayerBurnValues the array of values to burn into the raster. + * There should be nBandCount values for each layer. + * @param papszOptions special options controlling rasterization: + *
+ *
"ATTRIBUTE":
Identifies an attribute field on the features to be + * used for a burn in value. The value will be burned into all output + * bands. If specified, padfLayerBurnValues will not be used and can be a NULL + * pointer.
+ *
"CHUNKYSIZE":
The height in lines of the chunk to operate on. + * The larger the chunk size the less times we need to make a pass through all + * the shapes. If it is not set or set to zero the default chunk size will be + * used. Default size will be estimated based on the GDAL cache buffer size + * using formula: cache_size_bytes/scanline_size_bytes, so the chunk will + * not exceed the cache.
+ *
"ALL_TOUCHED":
May be set to TRUE to set all pixels touched + * by the line or polygons, not just those whose center is within the polygon + * or that are selected by brezenhams line algorithm. Defaults to FALSE.
+ *
"BURN_VALUE_FROM":
May be set to "Z" to use the Z values of the + * geometries. The value from padfLayerBurnValues or the attribute field value + * is added to this before burning. In default case dfBurnValue is burned as it + * is. This is implemented properly only for points and lines for now. Polygons + * will be burned using the Z value from the first point. The M value may be + * supported in the future.
+ *
"MERGE_ALG":
May be REPLACE (the default) or ADD. REPLACE results in overwriting of value, while ADD adds the new value to the existing raster, suitable for heatmaps for instance.
+ *
+ * @param pfnProgress the progress function to report completion. + * @param pProgressArg callback data for progress function. + * + * @return CE_None on success or CE_Failure on error. + */ + +CPLErr GDALRasterizeLayers( GDALDatasetH hDS, + int nBandCount, int *panBandList, + int nLayerCount, OGRLayerH *pahLayers, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfLayerBurnValues, + char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressArg ) + +{ +#ifndef OGR_ENABLED + CPLError(CE_Failure, CPLE_NotSupported, "GDALRasterizeLayers() unimplemented in a non OGR build"); + return CE_Failure; +#else + GDALDataType eType; + unsigned char *pabyChunkBuf; + GDALDataset *poDS = (GDALDataset *) hDS; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* Do some rudimentary arg checking. */ +/* -------------------------------------------------------------------- */ + if( nBandCount == 0 || nLayerCount == 0 ) + return CE_None; + + // prototype band. + GDALRasterBand *poBand = poDS->GetRasterBand( panBandList[0] ); + if (poBand == NULL) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Options */ +/* -------------------------------------------------------------------- */ + int bAllTouched; + GDALBurnValueSrc eBurnValueSource; + GDALRasterMergeAlg eMergeAlg; + if( GDALRasterizeOptions(papszOptions, &bAllTouched, + &eBurnValueSource, &eMergeAlg) == CE_Failure) { + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Establish a chunksize to operate on. The larger the chunk */ +/* size the less times we need to make a pass through all the */ +/* shapes. */ +/* -------------------------------------------------------------------- */ + int nYChunkSize, nScanlineBytes; + const char *pszYChunkSize = + CSLFetchNameValue( papszOptions, "CHUNKYSIZE" ); + + if( poBand->GetRasterDataType() == GDT_Byte ) + eType = GDT_Byte; + else + eType = GDT_Float64; + + nScanlineBytes = nBandCount * poDS->GetRasterXSize() + * (GDALGetDataTypeSize(eType)/8); + + if ( pszYChunkSize && ((nYChunkSize = atoi(pszYChunkSize))) != 0 ) + ; + else + { + GIntBig nYChunkSize64 = GDALGetCacheMax64() / nScanlineBytes; + if (nYChunkSize64 > INT_MAX) + nYChunkSize = INT_MAX; + else + nYChunkSize = (int)nYChunkSize64; + } + + if( nYChunkSize < 1 ) + nYChunkSize = 1; + if( nYChunkSize > poDS->GetRasterYSize() ) + nYChunkSize = poDS->GetRasterYSize(); + + CPLDebug( "GDAL", "Rasterizer operating on %d swaths of %d scanlines.", + (poDS->GetRasterYSize()+nYChunkSize-1) / nYChunkSize, + nYChunkSize ); + pabyChunkBuf = (unsigned char *) VSIMalloc(nYChunkSize * nScanlineBytes); + if( pabyChunkBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate rasterization buffer." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Read the image once for all layers if user requested to render */ +/* the whole raster in single chunk. */ +/* -------------------------------------------------------------------- */ + if ( nYChunkSize == poDS->GetRasterYSize() ) + { + if ( poDS->RasterIO( GF_Read, 0, 0, poDS->GetRasterXSize(), + nYChunkSize, pabyChunkBuf, + poDS->GetRasterXSize(), nYChunkSize, + eType, nBandCount, panBandList, 0, 0, 0, NULL ) + != CE_None ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to read buffer." ); + CPLFree( pabyChunkBuf ); + return CE_Failure; + } + } + +/* ==================================================================== */ +/* Read the specified layers transfoming and rasterizing */ +/* geometries. */ +/* ==================================================================== */ + CPLErr eErr = CE_None; + int iLayer; + const char *pszBurnAttribute = + CSLFetchNameValue( papszOptions, "ATTRIBUTE" ); + + pfnProgress( 0.0, NULL, pProgressArg ); + + for( iLayer = 0; iLayer < nLayerCount; iLayer++ ) + { + int iBurnField = -1; + double *padfBurnValues = NULL; + OGRLayer *poLayer = (OGRLayer *) pahLayers[iLayer]; + + if ( !poLayer ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Layer element number %d is NULL, skipping.\n", iLayer ); + continue; + } + +/* -------------------------------------------------------------------- */ +/* If the layer does not contain any features just skip it. */ +/* Do not force the feature count, so if driver doesn't know */ +/* exact number of features, go down the normal way. */ +/* -------------------------------------------------------------------- */ + if ( poLayer->GetFeatureCount(FALSE) == 0 ) + continue; + + if ( pszBurnAttribute ) + { + iBurnField = + poLayer->GetLayerDefn()->GetFieldIndex( pszBurnAttribute ); + if ( iBurnField == -1 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to find field %s on layer %s, skipping.\n", + pszBurnAttribute, + poLayer->GetLayerDefn()->GetName() ); + continue; + } + } + else + padfBurnValues = padfLayerBurnValues + iLayer * nBandCount; + +/* -------------------------------------------------------------------- */ +/* If we have no transformer, create the one from input file */ +/* projection. Note that each layer can be georefernced */ +/* separately. */ +/* -------------------------------------------------------------------- */ + int bNeedToFreeTransformer = FALSE; + + if( pfnTransformer == NULL ) + { + char *pszProjection = NULL; + bNeedToFreeTransformer = TRUE; + + OGRSpatialReference *poSRS = poLayer->GetSpatialRef(); + if ( !poSRS ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to fetch spatial reference on layer %s " + "to build transformer, assuming matching coordinate systems.\n", + poLayer->GetLayerDefn()->GetName() ); + } + else + poSRS->exportToWkt( &pszProjection ); + + pTransformArg = + GDALCreateGenImgProjTransformer( NULL, pszProjection, + hDS, NULL, FALSE, 0.0, 0 ); + pfnTransformer = GDALGenImgProjTransform; + + CPLFree( pszProjection ); + } + + OGRFeature *poFeat; + + poLayer->ResetReading(); + +/* -------------------------------------------------------------------- */ +/* Loop over image in designated chunks. */ +/* -------------------------------------------------------------------- */ + int iY; + for( iY = 0; + iY < poDS->GetRasterYSize() && eErr == CE_None; + iY += nYChunkSize ) + { + int nThisYChunkSize; + + nThisYChunkSize = nYChunkSize; + if( nThisYChunkSize + iY > poDS->GetRasterYSize() ) + nThisYChunkSize = poDS->GetRasterYSize() - iY; + + // Only re-read image if not a single chunk is being rendered + if ( nYChunkSize < poDS->GetRasterYSize() ) + { + eErr = + poDS->RasterIO( GF_Read, 0, iY, + poDS->GetRasterXSize(), nThisYChunkSize, + pabyChunkBuf, + poDS->GetRasterXSize(), nThisYChunkSize, + eType, nBandCount, panBandList, 0, 0, 0, NULL ); + if( eErr != CE_None ) + break; + } + + double *padfAttrValues = (double *) VSIMalloc(sizeof(double) * nBandCount); + while( (poFeat = poLayer->GetNextFeature()) != NULL ) + { + OGRGeometry *poGeom = poFeat->GetGeometryRef(); + + if ( pszBurnAttribute ) + { + int iBand; + double dfAttrValue; + + dfAttrValue = poFeat->GetFieldAsDouble( iBurnField ); + for (iBand = 0 ; iBand < nBandCount ; iBand++) + padfAttrValues[iBand] = dfAttrValue; + + padfBurnValues = padfAttrValues; + } + + gv_rasterize_one_shape( pabyChunkBuf, iY, + poDS->GetRasterXSize(), + nThisYChunkSize, + nBandCount, eType, bAllTouched, poGeom, + padfBurnValues, eBurnValueSource, + eMergeAlg, + pfnTransformer, pTransformArg ); + + delete poFeat; + } + VSIFree( padfAttrValues ); + + // Only write image if not a single chunk is being rendered + if ( nYChunkSize < poDS->GetRasterYSize() ) + { + eErr = + poDS->RasterIO( GF_Write, 0, iY, + poDS->GetRasterXSize(), nThisYChunkSize, + pabyChunkBuf, + poDS->GetRasterXSize(), nThisYChunkSize, + eType, nBandCount, panBandList, 0, 0, 0, NULL ); + } + + poLayer->ResetReading(); + + if( !pfnProgress((iY+nThisYChunkSize)/((double)poDS->GetRasterYSize()), + "", pProgressArg) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + + if ( bNeedToFreeTransformer ) + { + GDALDestroyTransformer( pTransformArg ); + pTransformArg = NULL; + pfnTransformer = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Write out the image once for all layers if user requested */ +/* to render the whole raster in single chunk. */ +/* -------------------------------------------------------------------- */ + if ( nYChunkSize == poDS->GetRasterYSize() ) + { + poDS->RasterIO( GF_Write, 0, 0, + poDS->GetRasterXSize(), nYChunkSize, + pabyChunkBuf, + poDS->GetRasterXSize(), nYChunkSize, + eType, nBandCount, panBandList, 0, 0, 0, NULL ); + } + +/* -------------------------------------------------------------------- */ +/* cleanup */ +/* -------------------------------------------------------------------- */ + VSIFree( pabyChunkBuf ); + + return eErr; +#endif /* def OGR_ENABLED */ +} + +/************************************************************************/ +/* GDALRasterizeLayersBuf() */ +/************************************************************************/ + +/** + * Burn geometries from the specified list of layer into raster. + * + * Rasterize all the geometric objects from a list of layers into supplied + * raster buffer. The layers are passed as an array of OGRLayerH handlers. + * + * If the geometries are in the georferenced coordinates of the raster + * dataset, then the pfnTransform may be passed in NULL and one will be + * derived internally from the geotransform of the dataset. The transform + * needs to transform the geometry locations into pixel/line coordinates + * of the target raster. + * + * The output raster may be of any GDAL supported datatype, though currently + * internally the burning is done either as GDT_Byte or GDT_Float32. This + * may be improved in the future. + * + * @param pData pointer to the output data array. + * + * @param nBufXSize width of the output data array in pixels. + * + * @param nBufYSize height of the output data array in pixels. + * + * @param eBufType data type of the output data array. + * + * @param nPixelSpace The byte offset from the start of one pixel value in + * pData to the start of the next pixel value within a scanline. If defaulted + * (0) the size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset from the start of one scanline in + * pData to the start of the next. If defaulted the size of the datatype + * eBufType * nBufXSize is used. + * + * @param nLayerCount the number of layers being passed in pahLayers array. + * + * @param pahLayers the array of layers to burn in. + * + * @param pszDstProjection WKT defining the coordinate system of the target + * raster. + * + * @param padfDstGeoTransform geotransformation matrix of the target raster. + * + * @param pfnTransformer transformation to apply to geometries to put into + * pixel/line coordinates on raster. If NULL a geotransform based one will + * be created internally. + * + * @param pTransformArg callback data for transformer. + * + * @param dfBurnValue the value to burn into the raster. + * + * @param papszOptions special options controlling rasterization: + *
+ *
"ATTRIBUTE":
Identifies an attribute field on the features to be + * used for a burn in value. The value will be burned into all output + * bands. If specified, padfLayerBurnValues will not be used and can be a NULL + * pointer.
+ *
"ALL_TOUCHED":
May be set to TRUE to set all pixels touched + * by the line or polygons, not just those whose center is within the polygon + * or that are selected by brezenhams line algorithm. Defaults to FALSE.
+ *
+ *
"BURN_VALUE_FROM":
May be set to "Z" to use + * the Z values of the geometries. dfBurnValue or the attribute field value is + * added to this before burning. In default case dfBurnValue is burned as it + * is. This is implemented properly only for points and lines for now. Polygons + * will be burned using the Z value from the first point. The M value may + * be supported in the future.
+ *
"MERGE_ALG":
May be REPLACE (the default) or ADD. REPLACE results in overwriting of value, while ADD adds the new value to the existing raster, suitable for heatmaps for instance.
+ * + * + * @param pfnProgress the progress function to report completion. + * + * @param pProgressArg callback data for progress function. + * + * + * @return CE_None on success or CE_Failure on error. + */ + +CPLErr GDALRasterizeLayersBuf( void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, int nLineSpace, + int nLayerCount, OGRLayerH *pahLayers, + const char *pszDstProjection, + double *padfDstGeoTransform, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, double dfBurnValue, + char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressArg ) + +{ +#ifndef OGR_ENABLED + CPLError(CE_Failure, CPLE_NotSupported, "GDALRasterizeLayersBuf() unimplemented in a non OGR build"); + return CE_Failure; +#else +/* -------------------------------------------------------------------- */ +/* If pixel and line spaceing are defaulted assign reasonable */ +/* value assuming a packed buffer. */ +/* -------------------------------------------------------------------- */ + if( nPixelSpace == 0 ) + nPixelSpace = GDALGetDataTypeSize( eBufType ) / 8; + + if( nLineSpace == 0 ) + nLineSpace = nPixelSpace * nBufXSize; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* Do some rudimentary arg checking. */ +/* -------------------------------------------------------------------- */ + if( nLayerCount == 0 ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Options */ +/* -------------------------------------------------------------------- */ + int bAllTouched; + GDALBurnValueSrc eBurnValueSource; + GDALRasterMergeAlg eMergeAlg; + if( GDALRasterizeOptions(papszOptions, &bAllTouched, + &eBurnValueSource, &eMergeAlg) == CE_Failure) { + return CE_Failure; + } + +/* ==================================================================== */ +/* Read thes pecified layers transfoming and rasterizing */ +/* geometries. */ +/* ==================================================================== */ + CPLErr eErr = CE_None; + int iLayer; + const char *pszBurnAttribute = + CSLFetchNameValue( papszOptions, "ATTRIBUTE" ); + + pfnProgress( 0.0, NULL, pProgressArg ); + + for( iLayer = 0; iLayer < nLayerCount; iLayer++ ) + { + int iBurnField = -1; + OGRLayer *poLayer = (OGRLayer *) pahLayers[iLayer]; + + if ( !poLayer ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Layer element number %d is NULL, skipping.\n", iLayer ); + continue; + } + +/* -------------------------------------------------------------------- */ +/* If the layer does not contain any features just skip it. */ +/* Do not force the feature count, so if driver doesn't know */ +/* exact number of features, go down the normal way. */ +/* -------------------------------------------------------------------- */ + if ( poLayer->GetFeatureCount(FALSE) == 0 ) + continue; + + if ( pszBurnAttribute ) + { + iBurnField = + poLayer->GetLayerDefn()->GetFieldIndex( pszBurnAttribute ); + if ( iBurnField == -1 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to find field %s on layer %s, skipping.\n", + pszBurnAttribute, + poLayer->GetLayerDefn()->GetName() ); + continue; + } + } + +/* -------------------------------------------------------------------- */ +/* If we have no transformer, create the one from input file */ +/* projection. Note that each layer can be georefernced */ +/* separately. */ +/* -------------------------------------------------------------------- */ + int bNeedToFreeTransformer = FALSE; + + if( pfnTransformer == NULL ) + { + char *pszProjection = NULL; + bNeedToFreeTransformer = TRUE; + + OGRSpatialReference *poSRS = poLayer->GetSpatialRef(); + if ( !poSRS ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to fetch spatial reference on layer %s " + "to build transformer, assuming matching coordinate systems.\n", + poLayer->GetLayerDefn()->GetName() ); + } + else + poSRS->exportToWkt( &pszProjection ); + + pTransformArg = + GDALCreateGenImgProjTransformer3( pszProjection, NULL, + pszDstProjection, + padfDstGeoTransform ); + pfnTransformer = GDALGenImgProjTransform; + + CPLFree( pszProjection ); + } + + OGRFeature *poFeat; + + poLayer->ResetReading(); + + while( (poFeat = poLayer->GetNextFeature()) != NULL ) + { + OGRGeometry *poGeom = poFeat->GetGeometryRef(); + + if ( pszBurnAttribute ) + dfBurnValue = poFeat->GetFieldAsDouble( iBurnField ); + + gv_rasterize_one_shape( (unsigned char *) pData, 0, + nBufXSize, nBufYSize, + 1, eBufType, bAllTouched, poGeom, + &dfBurnValue, eBurnValueSource, eMergeAlg, + pfnTransformer, pTransformArg ); + + delete poFeat; + } + + poLayer->ResetReading(); + + if( !pfnProgress(1, "", pProgressArg) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + + if ( bNeedToFreeTransformer ) + { + GDALDestroyTransformer( pTransformArg ); + pTransformArg = NULL; + pfnTransformer = NULL; + } + } + + return eErr; +#endif /* def OGR_ENABLED */ +} diff --git a/bazaar/plugin/gdal/alg/gdalrasterpolygonenumerator.cpp b/bazaar/plugin/gdal/alg/gdalrasterpolygonenumerator.cpp new file mode 100644 index 000000000..d09c8e746 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalrasterpolygonenumerator.cpp @@ -0,0 +1,255 @@ +/****************************************************************************** + * $Id: gdalrasterpolygonenumerator.cpp 28826 2015-03-30 17:51:14Z rouault $ + * + * Project: GDAL + * Purpose: Raster Polygon Enumerator + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_alg_priv.h" +#include "cpl_conv.h" +#include + +CPL_CVSID("$Id: gdalrasterpolygonenumerator.cpp 28826 2015-03-30 17:51:14Z rouault $"); + +/************************************************************************/ +/* GDALRasterPolygonEnumerator() */ +/************************************************************************/ + +GDALRasterPolygonEnumerator::GDALRasterPolygonEnumerator( + int nConnectedness ) + +{ + panPolyIdMap = NULL; + panPolyValue = NULL; + nNextPolygonId = 0; + nPolyAlloc = 0; + this->nConnectedness = nConnectedness; + CPLAssert( nConnectedness == 4 || nConnectedness == 8 ); +} + +/************************************************************************/ +/* ~GDALRasterPolygonEnumerator() */ +/************************************************************************/ + +GDALRasterPolygonEnumerator::~GDALRasterPolygonEnumerator() + +{ + Clear(); +} + +/************************************************************************/ +/* Clear() */ +/************************************************************************/ + +void GDALRasterPolygonEnumerator::Clear() + +{ + CPLFree( panPolyIdMap ); + CPLFree( panPolyValue ); + + panPolyIdMap = NULL; + panPolyValue = NULL; + + nNextPolygonId = 0; + nPolyAlloc = 0; +} + +/************************************************************************/ +/* MergePolygon() */ +/* */ +/* Update the polygon map to indicate the merger of two polygons. */ +/************************************************************************/ + +void GDALRasterPolygonEnumerator::MergePolygon( int nSrcId, int nDstId ) + +{ + while( panPolyIdMap[nDstId] != nDstId ) + nDstId = panPolyIdMap[nDstId]; + + while( panPolyIdMap[nSrcId] != nSrcId ) + nSrcId = panPolyIdMap[nSrcId]; + + if( nSrcId == nDstId ) + return; + + panPolyIdMap[nSrcId] = nDstId; +} + +/************************************************************************/ +/* NewPolygon() */ +/* */ +/* Allocate a new polygon id, and reallocate the polygon maps */ +/* if needed. */ +/************************************************************************/ + +int GDALRasterPolygonEnumerator::NewPolygon( GInt32 nValue ) + +{ + int nPolyId = nNextPolygonId; + + if( nNextPolygonId >= nPolyAlloc ) + { + nPolyAlloc = nPolyAlloc * 2 + 20; + panPolyIdMap = (GInt32 *) CPLRealloc(panPolyIdMap,nPolyAlloc*4); + panPolyValue = (GInt32 *) CPLRealloc(panPolyValue,nPolyAlloc*4); + } + + nNextPolygonId++; + + panPolyIdMap[nPolyId] = nPolyId; + panPolyValue[nPolyId] = nValue; + + return nPolyId; +} + +/************************************************************************/ +/* CompleteMerges() */ +/* */ +/* Make a pass through the maps, ensuring every polygon id */ +/* points to the final id it should use, not an intermediate */ +/* value. */ +/************************************************************************/ + +void GDALRasterPolygonEnumerator::CompleteMerges() + +{ + int iPoly; + int nFinalPolyCount = 0; + + for( iPoly = 0; iPoly < nNextPolygonId; iPoly++ ) + { + while( panPolyIdMap[iPoly] + != panPolyIdMap[panPolyIdMap[iPoly]] ) + panPolyIdMap[iPoly] = panPolyIdMap[panPolyIdMap[iPoly]]; + + if( panPolyIdMap[iPoly] == iPoly ) + nFinalPolyCount++; + } + + CPLDebug( "GDALRasterPolygonEnumerator", + "Counted %d polygon fragments forming %d final polygons.", + nNextPolygonId, nFinalPolyCount ); +} + +/************************************************************************/ +/* ProcessLine() */ +/* */ +/* Assign ids to polygons, one line at a time. */ +/************************************************************************/ + +void GDALRasterPolygonEnumerator::ProcessLine( + GInt32 *panLastLineVal, GInt32 *panThisLineVal, + GInt32 *panLastLineId, GInt32 *panThisLineId, + int nXSize ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Special case for the first line. */ +/* -------------------------------------------------------------------- */ + if( panLastLineVal == NULL ) + { + for( i=0; i < nXSize; i++ ) + { + if( panThisLineVal[i] == GP_NODATA_MARKER ) + { + panThisLineId[i] = -1; + } + else if( i == 0 || panThisLineVal[i] != panThisLineVal[i-1] ) + { + panThisLineId[i] = NewPolygon( panThisLineVal[i] ); + } + else + panThisLineId[i] = panThisLineId[i-1]; + } + + return; + } + +/* -------------------------------------------------------------------- */ +/* Process each pixel comparing to the previous pixel, and to */ +/* the last line. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nXSize; i++ ) + { + if( panThisLineVal[i] == GP_NODATA_MARKER ) + { + panThisLineId[i] = -1; + } + else if( i > 0 && panThisLineVal[i] == panThisLineVal[i-1] ) + { + panThisLineId[i] = panThisLineId[i-1]; + + if( panLastLineVal[i] == panThisLineVal[i] + && (panPolyIdMap[panLastLineId[i]] + != panPolyIdMap[panThisLineId[i]]) ) + { + MergePolygon( panLastLineId[i], panThisLineId[i] ); + } + + if( nConnectedness == 8 + && panLastLineVal[i-1] == panThisLineVal[i] + && (panPolyIdMap[panLastLineId[i-1]] + != panPolyIdMap[panThisLineId[i]]) ) + { + MergePolygon( panLastLineId[i-1], panThisLineId[i] ); + } + + if( nConnectedness == 8 && i < nXSize-1 + && panLastLineVal[i+1] == panThisLineVal[i] + && (panPolyIdMap[panLastLineId[i+1]] + != panPolyIdMap[panThisLineId[i]]) ) + { + MergePolygon( panLastLineId[i+1], panThisLineId[i] ); + } + } + else if( panLastLineVal[i] == panThisLineVal[i] ) + { + panThisLineId[i] = panLastLineId[i]; + } + else if( i > 0 && nConnectedness == 8 + && panLastLineVal[i-1] == panThisLineVal[i] ) + { + panThisLineId[i] = panLastLineId[i-1]; + + if( i < nXSize-1 && panLastLineVal[i+1] == panThisLineVal[i] + && (panPolyIdMap[panLastLineId[i+1]] + != panPolyIdMap[panThisLineId[i]]) ) + { + MergePolygon( panLastLineId[i+1], panThisLineId[i] ); + } + } + else if( i < nXSize-1 && nConnectedness == 8 + && panLastLineVal[i+1] == panThisLineVal[i] ) + { + panThisLineId[i] = panLastLineId[i+1]; + } + else + panThisLineId[i] = + NewPolygon( panThisLineVal[i] ); + } +} + diff --git a/bazaar/plugin/gdal/alg/gdalsievefilter.cpp b/bazaar/plugin/gdal/alg/gdalsievefilter.cpp new file mode 100644 index 000000000..abd23777b --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalsievefilter.cpp @@ -0,0 +1,578 @@ +/****************************************************************************** + * $Id: gdalsievefilter.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: GDAL + * Purpose: Raster to Polygon Converter + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_alg_priv.h" +#include "cpl_conv.h" +#include + +CPL_CVSID("$Id: gdalsievefilter.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define GP_NODATA_MARKER -51502112 +#define MY_MAX_INT 2147483647 + +/* + * General Plan + * + * 1) make a pass with the polygon enumerator to build up the + * polygon map array. Also accumulate polygon size information. + * + * 2) Identify the polygons that need to be merged. + * + * 3) Make a pass with the polygon enumerator. For each "to be merged" + * polygon keep track of it's largest neighbour. + * + * 4) Fix up remappings that would go to polygons smaller than the seive + * size. Ensure these in term map to the largest neighbour of the + * "to be seieved" polygons. + * + * 5) Make another pass with the polygon enumerator. This time we remap + * the actual pixel values of all polygons to be merged. + * + */ + +/************************************************************************/ +/* GPMaskImageData() */ +/* */ +/* Mask out image pixels to a special nodata value if the mask */ +/* band is zero. */ +/************************************************************************/ + +static CPLErr +GPMaskImageData( GDALRasterBandH hMaskBand, GByte *pabyMaskLine, int iY, int nXSize, + GInt32 *panImageLine ) + +{ + CPLErr eErr; + + eErr = GDALRasterIO( hMaskBand, GF_Read, 0, iY, nXSize, 1, + pabyMaskLine, nXSize, 1, GDT_Byte, 0, 0 ); + if( eErr == CE_None ) + { + int i; + for( i = 0; i < nXSize; i++ ) + { + if( pabyMaskLine[i] == 0 ) + panImageLine[i] = GP_NODATA_MARKER; + } + } + + return eErr; +} + +/************************************************************************/ +/* CompareNeighbour() */ +/* */ +/* Compare two neighbouring polygons, and update eaches */ +/* "biggest neighbour" if the other is larger than it's current */ +/* largest neighbour. */ +/* */ +/* Note that this should end up with each polygon knowing the */ +/* id of it's largest neighbour. No attempt is made to */ +/* restrict things to small polygons that we will be merging, */ +/* nor to exclude assigning "biggest neighbours" that are still */ +/* smaller than our sieve threshold. */ +/************************************************************************/ + +static inline void CompareNeighbour( int nPolyId1, int nPolyId2, + int *panPolyIdMap, + int *panPolyValue, + std::vector &anPolySizes, + std::vector &anBigNeighbour ) + +{ + // make sure we are working with the final merged polygon ids. + nPolyId1 = panPolyIdMap[nPolyId1]; + nPolyId2 = panPolyIdMap[nPolyId2]; + + if( nPolyId1 == nPolyId2 ) + return; + + // nodata polygon do not need neighbours, and cannot be neighbours + // to valid polygons. + if( panPolyValue[nPolyId1] == GP_NODATA_MARKER + || panPolyValue[nPolyId2] == GP_NODATA_MARKER ) + return; + + if( anBigNeighbour[nPolyId1] == -1 + || anPolySizes[anBigNeighbour[nPolyId1]] < anPolySizes[nPolyId2] ) + anBigNeighbour[nPolyId1] = nPolyId2; + + if( anBigNeighbour[nPolyId2] == -1 + || anPolySizes[anBigNeighbour[nPolyId2]] < anPolySizes[nPolyId1] ) + anBigNeighbour[nPolyId2] = nPolyId1; +} + +/************************************************************************/ +/* GDALSieveFilter() */ +/************************************************************************/ + +/** + * Removes small raster polygons. + * + * The function removes raster polygons smaller than a provided + * threshold size (in pixels) and replaces replaces them with the pixel value + * of the largest neighbour polygon. + * + * Polygon are determined (per GDALRasterPolygonEnumerator) as regions of + * the raster where the pixels all have the same value, and that are contiguous + * (connected). + * + * Pixels determined to be "nodata" per hMaskBand will not be treated as part + * of a polygon regardless of their pixel values. Nodata areas will never be + * changed nor affect polygon sizes. + * + * Polygons smaller than the threshold with no neighbours that are as large + * as the threshold will not be altered. Polygons surrounded by nodata areas + * will therefore not be altered. + * + * The algorithm makes three passes over the input file to enumerate the + * polygons and collect limited information about them. Memory use is + * proportional to the number of polygons (roughly 24 bytes per polygon), but + * is not directly related to the size of the raster. So very large raster + * files can be processed effectively if there aren't too many polygons. But + * extremely noisy rasters with many one pixel polygons will end up being + * expensive (in memory) to process. + * + * @param hSrcBand the source raster band to be processed. + * @param hMaskBand an optional mask band. All pixels in the mask band with a + * value other than zero will be considered suitable for inclusion in polygons. + * @param hDstBand the output raster band. It may be the same as hSrcBand + * to update the source in place. + * @param nSizeThreshold raster polygons with sizes smaller than this will + * be merged into their largest neighbour. + * @param nConnectedness either 4 indicating that diagonal pixels are not + * considered directly adjacent for polygon membership purposes or 8 + * indicating they are. + * @param papszOptions algorithm options in name=value list form. None currently + * supported. + * @param pfnProgress callback for reporting algorithm progress matching the + * GDALProgressFunc() semantics. May be NULL. + * @param pProgressArg callback argument passed to pfnProgress. + * + * @return CE_None on success or CE_Failure if an error occurs. + */ + +CPLErr CPL_STDCALL +GDALSieveFilter( GDALRasterBandH hSrcBand, GDALRasterBandH hMaskBand, + GDALRasterBandH hDstBand, + int nSizeThreshold, int nConnectedness, + CPL_UNUSED char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + VALIDATE_POINTER1( hSrcBand, "GDALSieveFilter", CE_Failure ); + VALIDATE_POINTER1( hDstBand, "GDALSieveFilter", CE_Failure ); + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* Allocate working buffers. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + int nXSize = GDALGetRasterBandXSize( hSrcBand ); + int nYSize = GDALGetRasterBandYSize( hSrcBand ); + GInt32 *panLastLineVal = (GInt32 *) VSIMalloc2(sizeof(GInt32), nXSize); + GInt32 *panThisLineVal = (GInt32 *) VSIMalloc2(sizeof(GInt32), nXSize); + GInt32 *panLastLineId = (GInt32 *) VSIMalloc2(sizeof(GInt32), nXSize); + GInt32 *panThisLineId = (GInt32 *) VSIMalloc2(sizeof(GInt32), nXSize); + GInt32 *panThisLineWriteVal = (GInt32 *) VSIMalloc2(sizeof(GInt32), nXSize); + GByte *pabyMaskLine = (hMaskBand != NULL) ? (GByte *) VSIMalloc(nXSize) : NULL; + if (panLastLineVal == NULL || panThisLineVal == NULL || + panLastLineId == NULL || panThisLineId == NULL || + panThisLineWriteVal == NULL || + (hMaskBand != NULL && pabyMaskLine == NULL)) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Could not allocate enough memory for temporary buffers"); + CPLFree( panThisLineId ); + CPLFree( panLastLineId ); + CPLFree( panThisLineVal ); + CPLFree( panLastLineVal ); + CPLFree( panThisLineWriteVal ); + CPLFree( pabyMaskLine ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* The first pass over the raster is only used to build up the */ +/* polygon id map so we will know in advance what polygons are */ +/* what on the second pass. */ +/* -------------------------------------------------------------------- */ + int iY, iX, iPoly; + GDALRasterPolygonEnumerator oFirstEnum( nConnectedness ); + std::vector anPolySizes; + + for( iY = 0; eErr == CE_None && iY < nYSize; iY++ ) + { + eErr = GDALRasterIO( + hSrcBand, + GF_Read, 0, iY, nXSize, 1, + panThisLineVal, nXSize, 1, GDT_Int32, 0, 0 ); + + if( eErr == CE_None && hMaskBand != NULL ) + eErr = GPMaskImageData( hMaskBand, pabyMaskLine, iY, nXSize, panThisLineVal ); + + if( iY == 0 ) + oFirstEnum.ProcessLine( + NULL, panThisLineVal, NULL, panThisLineId, nXSize ); + else + oFirstEnum.ProcessLine( + panLastLineVal, panThisLineVal, + panLastLineId, panThisLineId, + nXSize ); + +/* -------------------------------------------------------------------- */ +/* Accumulate polygon sizes. */ +/* -------------------------------------------------------------------- */ + if( oFirstEnum.nNextPolygonId > (int) anPolySizes.size() ) + anPolySizes.resize( oFirstEnum.nNextPolygonId ); + + for( iX = 0; iX < nXSize; iX++ ) + { + iPoly = panThisLineId[iX]; + + CPLAssert( iPoly >= 0 ); + if( anPolySizes[iPoly] < MY_MAX_INT ) + anPolySizes[iPoly] += 1; + } + +/* -------------------------------------------------------------------- */ +/* swap this/last lines. */ +/* -------------------------------------------------------------------- */ + GInt32 *panTmp = panLastLineVal; + panLastLineVal = panThisLineVal; + panThisLineVal = panTmp; + + panTmp = panThisLineId; + panThisLineId = panLastLineId; + panLastLineId = panTmp; + +/* -------------------------------------------------------------------- */ +/* Report progress, and support interrupts. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None + && !pfnProgress( 0.25 * ((iY+1) / (double) nYSize), + "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Make a pass through the maps, ensuring every polygon id */ +/* points to the final id it should use, not an intermediate */ +/* value. */ +/* -------------------------------------------------------------------- */ + oFirstEnum.CompleteMerges(); + +/* -------------------------------------------------------------------- */ +/* Push the sizes of merged polygon fragments into the the */ +/* merged polygon id's count. */ +/* -------------------------------------------------------------------- */ + for( iPoly = 0; iPoly < oFirstEnum.nNextPolygonId; iPoly++ ) + { + if( oFirstEnum.panPolyIdMap[iPoly] != iPoly ) + { + GIntBig nSize = anPolySizes[oFirstEnum.panPolyIdMap[iPoly]]; + + nSize += anPolySizes[iPoly]; + + if( nSize > MY_MAX_INT ) + nSize = MY_MAX_INT; + + anPolySizes[oFirstEnum.panPolyIdMap[iPoly]] = (int)nSize; + anPolySizes[iPoly] = 0; + } + } + +/* -------------------------------------------------------------------- */ +/* We will use a new enumerator for the second pass primariliy */ +/* so we can preserve the first pass map. */ +/* -------------------------------------------------------------------- */ + GDALRasterPolygonEnumerator oSecondEnum( nConnectedness ); + + std::vector anBigNeighbour; + anBigNeighbour.resize( anPolySizes.size() ); + + for( iPoly = 0; iPoly < (int) anPolySizes.size(); iPoly++ ) + anBigNeighbour[iPoly] = -1; + +/* ==================================================================== */ +/* Second pass ... identify the largest neighbour for each */ +/* polygon. */ +/* ==================================================================== */ + for( iY = 0; eErr == CE_None && iY < nYSize; iY++ ) + { +/* -------------------------------------------------------------------- */ +/* Read the image data. */ +/* -------------------------------------------------------------------- */ + eErr = GDALRasterIO( hSrcBand, GF_Read, 0, iY, nXSize, 1, + panThisLineVal, nXSize, 1, GDT_Int32, 0, 0 ); + + if( eErr == CE_None && hMaskBand != NULL ) + eErr = GPMaskImageData( hMaskBand, pabyMaskLine, iY, nXSize, panThisLineVal ); + + if( eErr != CE_None ) + continue; + +/* -------------------------------------------------------------------- */ +/* Determine what polygon the various pixels belong to (redoing */ +/* the same thing done in the first pass above). */ +/* -------------------------------------------------------------------- */ + if( iY == 0 ) + oSecondEnum.ProcessLine( + NULL, panThisLineVal, NULL, panThisLineId, nXSize ); + else + oSecondEnum.ProcessLine( + panLastLineVal, panThisLineVal, + panLastLineId, panThisLineId, + nXSize ); + +/* -------------------------------------------------------------------- */ +/* Check our neighbours, and update our biggest neighbour map */ +/* as appropriate. */ +/* -------------------------------------------------------------------- */ + for( iX = 0; iX < nXSize; iX++ ) + { + if( iY > 0 ) + { + CompareNeighbour( panThisLineId[iX], + panLastLineId[iX], + oFirstEnum.panPolyIdMap, + oFirstEnum.panPolyValue, + anPolySizes, anBigNeighbour ); + + if( iX > 0 && nConnectedness == 8 ) + CompareNeighbour( panThisLineId[iX], + panLastLineId[iX-1], + oFirstEnum.panPolyIdMap, + oFirstEnum.panPolyValue, + anPolySizes, anBigNeighbour ); + + if( iX < nXSize-1 && nConnectedness == 8 ) + CompareNeighbour( panThisLineId[iX], + panLastLineId[iX+1], + oFirstEnum.panPolyIdMap, + oFirstEnum.panPolyValue, + anPolySizes, anBigNeighbour ); + + } + + if( iX > 0 ) + CompareNeighbour( panThisLineId[iX], + panThisLineId[iX-1], + oFirstEnum.panPolyIdMap, + oFirstEnum.panPolyValue, + anPolySizes, anBigNeighbour ); + + // We don't need to compare to next pixel or next line + // since they will be compared to us. + } + +/* -------------------------------------------------------------------- */ +/* Swap pixel value, and polygon id lines to be ready for the */ +/* next line. */ +/* -------------------------------------------------------------------- */ + GInt32 *panTmp = panLastLineVal; + panLastLineVal = panThisLineVal; + panThisLineVal = panTmp; + + panTmp = panThisLineId; + panThisLineId = panLastLineId; + panLastLineId = panTmp; + +/* -------------------------------------------------------------------- */ +/* Report progress, and support interrupts. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None + && !pfnProgress( 0.25 + 0.25 * ((iY+1) / (double) nYSize), + "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* If our biggest neighbour is still smaller than the */ +/* threshold, then try tracking to that polygons biggest */ +/* neighbour, and so forth. */ +/* -------------------------------------------------------------------- */ + int nFailedMerges = 0; + int nIsolatedSmall = 0; + int nSieveTargets = 0; + + for( iPoly = 0; iPoly < (int) anPolySizes.size(); iPoly++ ) + { + if( oFirstEnum.panPolyIdMap[iPoly] != iPoly ) + continue; + + // Ignore nodata polygons. + if( oFirstEnum.panPolyValue[iPoly] == GP_NODATA_MARKER ) + continue; + + // Don't try to merge polygons larger than the threshold. + if( anPolySizes[iPoly] >= nSizeThreshold ) + { + anBigNeighbour[iPoly] = -1; + continue; + } + + nSieveTargets++; + + // if we have no neighbours but we are small, what shall we do? + if( anBigNeighbour[iPoly] == -1 ) + { + nIsolatedSmall++; + continue; + } + + // If our biggest neighbour is larger than the threshold + // then we are golden. + if( anPolySizes[anBigNeighbour[iPoly]] >= nSizeThreshold ) + continue; + +#ifdef notdef + // Will our neighbours biggest neighbour do? + // Eventually we need something sort of recursive here with + // loop detection. + if( anPolySizes[anBigNeighbour[anBigNeighbour[iPoly]]] + >= nSizeThreshold ) + { + anBigNeighbour[iPoly] = anBigNeighbour[anBigNeighbour[iPoly]]; + continue; + } +#endif + + nFailedMerges++; + anBigNeighbour[iPoly] = -1; + } + + CPLDebug( "GDALSieveFilter", + "Small Polygons: %d, Isolated: %d, Unmergable: %d", + nSieveTargets, nIsolatedSmall, nFailedMerges ); + +/* ==================================================================== */ +/* Make a third pass over the image, actually applying the */ +/* merges. We reuse the second enumerator but preserve the */ +/* "final maps" from the first. */ +/* ==================================================================== */ + oSecondEnum.Clear(); + + + for( iY = 0; eErr == CE_None && iY < nYSize; iY++ ) + { +/* -------------------------------------------------------------------- */ +/* Read the image data. */ +/* -------------------------------------------------------------------- */ + eErr = GDALRasterIO( hSrcBand, GF_Read, 0, iY, nXSize, 1, + panThisLineVal, nXSize, 1, GDT_Int32, 0, 0 ); + + memcpy( panThisLineWriteVal, panThisLineVal, 4 * nXSize ); + + if( eErr == CE_None && hMaskBand != NULL ) + eErr = GPMaskImageData( hMaskBand, pabyMaskLine, iY, nXSize, panThisLineVal ); + + if( eErr != CE_None ) + continue; + +/* -------------------------------------------------------------------- */ +/* Determine what polygon the various pixels belong to (redoing */ +/* the same thing done in the first pass above). */ +/* -------------------------------------------------------------------- */ + if( iY == 0 ) + oSecondEnum.ProcessLine( + NULL, panThisLineVal, NULL, panThisLineId, nXSize ); + else + oSecondEnum.ProcessLine( + panLastLineVal, panThisLineVal, + panLastLineId, panThisLineId, + nXSize ); + +/* -------------------------------------------------------------------- */ +/* Reprocess the actual pixel values according to the polygon */ +/* merging, and write out this line of image data. */ +/* -------------------------------------------------------------------- */ + for( iX = 0; iX < nXSize; iX++ ) + { + int iThisPoly = oFirstEnum.panPolyIdMap[panThisLineId[iX]]; + + if( anBigNeighbour[iThisPoly] != -1 ) + { + panThisLineWriteVal[iX] = + oFirstEnum.panPolyValue[ + anBigNeighbour[iThisPoly]]; + } + } + +/* -------------------------------------------------------------------- */ +/* Write the update data out. */ +/* -------------------------------------------------------------------- */ + eErr = GDALRasterIO( hDstBand, GF_Write, 0, iY, nXSize, 1, + panThisLineWriteVal, nXSize, 1, GDT_Int32, 0, 0 ); + +/* -------------------------------------------------------------------- */ +/* Swap pixel value, and polygon id lines to be ready for the */ +/* next line. */ +/* -------------------------------------------------------------------- */ + GInt32 *panTmp = panLastLineVal; + panLastLineVal = panThisLineVal; + panThisLineVal = panTmp; + + panTmp = panThisLineId; + panThisLineId = panLastLineId; + panLastLineId = panTmp; + +/* -------------------------------------------------------------------- */ +/* Report progress, and support interrupts. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None + && !pfnProgress( 0.5 + 0.5 * ((iY+1) / (double) nYSize), + "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( panThisLineId ); + CPLFree( panLastLineId ); + CPLFree( panThisLineVal ); + CPLFree( panLastLineVal ); + CPLFree( panThisLineWriteVal ); + CPLFree( pabyMaskLine ); + + return eErr; +} diff --git a/bazaar/plugin/gdal/alg/gdalsimplewarp.cpp b/bazaar/plugin/gdal/alg/gdalsimplewarp.cpp new file mode 100644 index 000000000..c5ada7041 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalsimplewarp.cpp @@ -0,0 +1,446 @@ +/****************************************************************************** + * $Id: gdalsimplewarp.cpp 15436 2008-09-24 19:26:31Z rouault $ + * + * Project: Mapinfo Image Warper + * Purpose: Simple (source in memory) warp algorithm. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, i3 - information integration and imaging, Fort Collin,CO + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_alg.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: gdalsimplewarp.cpp 15436 2008-09-24 19:26:31Z rouault $"); + +static void +GDALSimpleWarpRemapping( int nBandCount, GByte **papabySrcData, + int nSrcXSize, int nSrcYSize, + char **papszWarpOptions ); + +/************************************************************************/ +/* GDALSimpleImageWarp() */ +/************************************************************************/ + +/** + * Perform simple image warp. + * + * Copies an image from a source dataset to a destination dataset applying + * an application defined transformation. This algorithm is called simple + * because it lacks many options such as resampling kernels (other than + * nearest neighbour), support for data types other than 8bit, and the + * ability to warp images without holding the entire source and destination + * image in memory. + * + * The following option(s) may be passed in papszWarpOptions. + *
    + *
  • "INIT=v[,v...]": This option indicates that the output dataset should + * be initialized to the indicated value in any area valid data is not written. + * Distinct values may be listed for each band separated by columns. + *
+ * + * @param hSrcDS the source image dataset. + * @param hDstDS the destination image dataset. + * @param nBandCount the number of bands to be warped. If zero, all bands + * will be processed. + * @param panBandList the list of bands to translate. + * @param pfnTransform the transformation function to call. See + * GDALTransformerFunc(). + * @param pTransformArg the callback handle to pass to pfnTransform. + * @param pfnProgress the function used to report progress. See + * GDALProgressFunc(). + * @param pProgressArg the callback handle to pass to pfnProgress. + * @param papszWarpOptions additional options controlling the warp. + * + * @return TRUE if the operation completes, or FALSE if an error occurs. + */ + +int CPL_STDCALL +GDALSimpleImageWarp( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, + int nBandCount, int *panBandList, + GDALTransformerFunc pfnTransform, void *pTransformArg, + GDALProgressFunc pfnProgress, void *pProgressArg, + char **papszWarpOptions ) + +{ + VALIDATE_POINTER1( hSrcDS, "GDALSimpleImageWarp", 0 ); + VALIDATE_POINTER1( hDstDS, "GDALSimpleImageWarp", 0 ); + + int iBand, bCancelled = FALSE; + +/* -------------------------------------------------------------------- */ +/* If no bands provided assume we should process all bands. */ +/* -------------------------------------------------------------------- */ + if( nBandCount == 0 ) + { + int nResult; + + nBandCount = GDALGetRasterCount( hSrcDS ); + if (nBandCount == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "No raster band in source dataset"); + return FALSE; + } + + panBandList = (int *) CPLCalloc(sizeof(int),nBandCount); + + for( iBand = 0; iBand < nBandCount; iBand++ ) + panBandList[iBand] = iBand+1; + + nResult = GDALSimpleImageWarp( hSrcDS, hDstDS, nBandCount, panBandList, + pfnTransform, pTransformArg, + pfnProgress, pProgressArg, + papszWarpOptions ); + CPLFree( panBandList ); + return nResult; + } + +/* -------------------------------------------------------------------- */ +/* Post initial progress. */ +/* -------------------------------------------------------------------- */ + if( pfnProgress ) + { + if( !pfnProgress( 0.0, "", pProgressArg ) ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Load the source image band(s). */ +/* -------------------------------------------------------------------- */ + int nSrcXSize = GDALGetRasterXSize(hSrcDS); + int nSrcYSize = GDALGetRasterYSize(hSrcDS); + GByte **papabySrcData; + + papabySrcData = (GByte **) CPLCalloc(nBandCount,sizeof(GByte*)); + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + papabySrcData[iBand] = (GByte *) VSIMalloc(nSrcXSize*nSrcYSize); + + GDALRasterIO( GDALGetRasterBand(hSrcDS,panBandList[iBand]), GF_Read, + 0, 0, nSrcXSize, nSrcYSize, + papabySrcData[iBand], nSrcXSize, nSrcYSize, GDT_Byte, + 0, 0 ); + } + +/* -------------------------------------------------------------------- */ +/* Check for remap request(s). */ +/* -------------------------------------------------------------------- */ + GDALSimpleWarpRemapping( nBandCount, papabySrcData, nSrcXSize, nSrcYSize, + papszWarpOptions ); + +/* -------------------------------------------------------------------- */ +/* Allocate scanline buffers for output image. */ +/* -------------------------------------------------------------------- */ + int nDstXSize = GDALGetRasterXSize( hDstDS ); + int nDstYSize = GDALGetRasterYSize( hDstDS ); + GByte **papabyDstLine; + + papabyDstLine = (GByte **) CPLCalloc(nBandCount,sizeof(GByte*)); + + for( iBand = 0; iBand < nBandCount; iBand++ ) + papabyDstLine[iBand] = (GByte *) CPLMalloc( nDstXSize ); + +/* -------------------------------------------------------------------- */ +/* Allocate x,y,z coordinate arrays for transformation ... one */ +/* scanlines worth of positions. */ +/* -------------------------------------------------------------------- */ + double *padfX, *padfY, *padfZ; + int *pabSuccess; + + padfX = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfY = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfZ = (double *) CPLMalloc(sizeof(double) * nDstXSize); + pabSuccess = (int *) CPLMalloc(sizeof(int) * nDstXSize); + +/* -------------------------------------------------------------------- */ +/* Establish the value we will use to initialize the bands. We */ +/* default to -1 indicating the initial value should be read */ +/* and preserved from the source file, but allow this to be */ +/* overridden by passed */ +/* option(s). */ +/* -------------------------------------------------------------------- */ + int *panBandInit; + + panBandInit = (int *) CPLCalloc(sizeof(int),nBandCount); + if( CSLFetchNameValue( papszWarpOptions, "INIT" ) ) + { + int iBand, nTokenCount; + char **papszTokens = + CSLTokenizeStringComplex( CSLFetchNameValue( papszWarpOptions, + "INIT" ), + " ,", FALSE, FALSE ); + + nTokenCount = CSLCount(papszTokens); + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + if( nTokenCount == 0 ) + panBandInit[iBand] = 0; + else + panBandInit[iBand] = + atoi(papszTokens[MIN(iBand,nTokenCount-1)]); + } + + CSLDestroy(papszTokens); + } + +/* -------------------------------------------------------------------- */ +/* Loop over all the scanlines in the output image. */ +/* -------------------------------------------------------------------- */ + int iDstY; + + for( iDstY = 0; iDstY < nDstYSize; iDstY++ ) + { + int iDstX; + + // Clear output buffer to "transparent" value. Shouldn't we + // really be reading from the destination file to support overlay? + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + if( panBandInit[iBand] == -1 ) + GDALRasterIO( GDALGetRasterBand(hDstDS,iBand+1), GF_Read, + 0, iDstY, nDstXSize, 1, + papabyDstLine[iBand], nDstXSize, 1, GDT_Byte, + 0, 0 ); + else + memset( papabyDstLine[iBand], panBandInit[iBand], nDstXSize ); + } + + // Set point to transform. + for( iDstX = 0; iDstX < nDstXSize; iDstX++ ) + { + padfX[iDstX] = iDstX + 0.5; + padfY[iDstX] = iDstY + 0.5; + padfZ[iDstX] = 0.0; + } + + // Transform the points from destination pixel/line coordinates + // to source pixel/line coordinates. + pfnTransform( pTransformArg, TRUE, nDstXSize, + padfX, padfY, padfZ, pabSuccess ); + + // Loop over the output scanline. + for( iDstX = 0; iDstX < nDstXSize; iDstX++ ) + { + if( !pabSuccess[iDstX] ) + continue; + + // We test against the value before casting to avoid the + // problem of asymmetric truncation effects around zero. That is + // -0.5 will be 0 when cast to an int. + if( padfX[iDstX] < 0.0 || padfY[iDstX] < 0.0 ) + continue; + + int iSrcX, iSrcY, iSrcOffset; + + iSrcX = (int) padfX[iDstX]; + iSrcY = (int) padfY[iDstX]; + + if( iSrcX >= nSrcXSize || iSrcY >= nSrcYSize ) + continue; + + iSrcOffset = iSrcX + iSrcY * nSrcXSize; + + for( iBand = 0; iBand < nBandCount; iBand++ ) + papabyDstLine[iBand][iDstX] = papabySrcData[iBand][iSrcOffset]; + } + + // Write scanline to disk. + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterIO( GDALGetRasterBand(hDstDS,iBand+1), GF_Write, + 0, iDstY, nDstXSize, 1, + papabyDstLine[iBand], nDstXSize, 1, GDT_Byte, 0, 0 ); + } + + if( pfnProgress != NULL ) + { + if( !pfnProgress( (iDstY+1) / (double) nDstYSize, + "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + bCancelled = TRUE; + break; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup working buffers. */ +/* -------------------------------------------------------------------- */ + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + CPLFree( papabyDstLine[iBand] ); + CPLFree( papabySrcData[iBand] ); + } + + CPLFree( panBandInit ); + CPLFree( papabyDstLine ); + CPLFree( papabySrcData ); + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + CPLFree( pabSuccess ); + + return !bCancelled; +} + +/************************************************************************/ +/* GDALSimpleWarpRemapping() */ +/* */ +/* This function implements any raster remapping requested in */ +/* the options list. The remappings are applied to the source */ +/* data before warping. Two kinds are support ... REMAP */ +/* commands which remap selected pixel values for any band and */ +/* REMAP_MULTI which only remap pixels matching the input in */ +/* all bands at once (ie. to remap an RGB value to another). */ +/************************************************************************/ + +static void +GDALSimpleWarpRemapping( int nBandCount, GByte **papabySrcData, + int nSrcXSize, int nSrcYSize, + char **papszWarpOptions ) + +{ + +/* ==================================================================== */ +/* Process any and all single value REMAP commands. */ +/* ==================================================================== */ + int iRemap; + char **papszRemaps = CSLFetchNameValueMultiple( papszWarpOptions, + "REMAP" ); + + for( iRemap = 0; iRemap < CSLCount(papszRemaps); iRemap++ ) + { + +/* -------------------------------------------------------------------- */ +/* What are the pixel values to map from and to? */ +/* -------------------------------------------------------------------- */ + char **papszTokens = CSLTokenizeString( papszRemaps[iRemap] ); + int nFromValue, nToValue; + + if( CSLCount(papszTokens) != 2 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Ill formed REMAP `%s' ignored in GDALSimpleWarpRemapping()", + papszRemaps[iRemap] ); + continue; + } + + nFromValue = atoi(papszTokens[0]); + nToValue = atoi(papszTokens[1]); + + CSLDestroy( papszTokens ); + +/* -------------------------------------------------------------------- */ +/* Pass over each band searching for matches. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < nBandCount; iBand++ ) + { + GByte *pabyData = papabySrcData[iBand]; + int nPixelCount = nSrcXSize * nSrcYSize; + + while( nPixelCount != 0 ) + { + if( *pabyData == nFromValue ) + *pabyData = (GByte) nToValue; + + pabyData++; + nPixelCount--; + } + } + } + + CSLDestroy( papszRemaps ); + +/* ==================================================================== */ +/* Process any and all REMAP_MULTI commands. */ +/* ==================================================================== */ + papszRemaps = CSLFetchNameValueMultiple( papszWarpOptions, + "REMAP_MULTI" ); + + for( iRemap = 0; iRemap < CSLCount(papszRemaps); iRemap++ ) + { +/* -------------------------------------------------------------------- */ +/* What are the pixel values to map from and to? */ +/* -------------------------------------------------------------------- */ + char **papszTokens = CSLTokenizeString( papszRemaps[iRemap] ); + int *panFromValue, *panToValue; + int nMapBandCount, iBand; + + if( CSLCount(papszTokens) % 2 == 1 + || CSLCount(papszTokens) == 0 + || CSLCount(papszTokens) > nBandCount * 2 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Ill formed REMAP_MULTI `%s' ignored in GDALSimpleWarpRemapping()", + papszRemaps[iRemap] ); + continue; + } + + nMapBandCount = CSLCount(papszTokens) / 2; + + panFromValue = (int *) CPLMalloc(sizeof(int) * nMapBandCount ); + panToValue = (int *) CPLMalloc(sizeof(int) * nMapBandCount ); + + for( iBand = 0; iBand < nMapBandCount; iBand++ ) + { + panFromValue[iBand] = atoi(papszTokens[iBand]); + panToValue[iBand] = atoi(papszTokens[iBand+nMapBandCount]); + } + + CSLDestroy( papszTokens ); + +/* -------------------------------------------------------------------- */ +/* Search for matching values to replace. */ +/* -------------------------------------------------------------------- */ + int nPixelCount = nSrcXSize * nSrcYSize; + int iPixel; + + for( iPixel = 0; iPixel < nPixelCount; iPixel++ ) + { + if( papabySrcData[0][iPixel] != panFromValue[0] ) + continue; + + int bMatch = TRUE; + + for( iBand = 1; iBand < nMapBandCount; iBand++ ) + { + if( papabySrcData[iBand][iPixel] != panFromValue[iBand] ) + bMatch = FALSE; + } + + if( !bMatch ) + continue; + + for( iBand = 0; iBand < nMapBandCount; iBand++ ) + papabySrcData[iBand][iPixel] = (GByte) panToValue[iBand]; + } + + CPLFree( panFromValue ); + CPLFree( panToValue ); + } + + CSLDestroy( papszRemaps ); +} diff --git a/bazaar/plugin/gdal/alg/gdaltransformer.cpp b/bazaar/plugin/gdal/alg/gdaltransformer.cpp new file mode 100644 index 000000000..62a528e8e --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdaltransformer.cpp @@ -0,0 +1,3508 @@ +/****************************************************************************** + * $Id: gdaltransformer.cpp 29309 2015-06-05 18:48:48Z rouault $ + * + * Project: Mapinfo Image Warper + * Purpose: Implementation of one or more GDALTrasformerFunc types, including + * the GenImgProj (general image reprojector) transformer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, i3 - information integration and imaging + * Fort Collin, CO + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_alg.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include "gdal_alg_priv.h" +#include "cpl_list.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: gdaltransformer.cpp 29309 2015-06-05 18:48:48Z rouault $"); +CPL_C_START +void *GDALDeserializeGCPTransformer( CPLXMLNode *psTree ); +void *GDALDeserializeTPSTransformer( CPLXMLNode *psTree ); +void *GDALDeserializeGeoLocTransformer( CPLXMLNode *psTree ); +void *GDALDeserializeRPCTransformer( CPLXMLNode *psTree ); +CPL_C_END + +static CPLXMLNode *GDALSerializeReprojectionTransformer( void *pTransformArg ); +static void *GDALDeserializeReprojectionTransformer( CPLXMLNode *psTree ); + +static CPLXMLNode *GDALSerializeGenImgProjTransformer( void *pTransformArg ); +static void *GDALDeserializeGenImgProjTransformer( CPLXMLNode *psTree ); + +static void GDALRefreshGenImgProjTransformer(void* hTransformArg); + +/************************************************************************/ +/* GDALTransformFunc */ +/* */ +/* Documentation for GDALTransformFunc typedef. */ +/************************************************************************/ + +/*! + +\typedef int GDALTransformerFunc + +Generic signature for spatial point transformers. + +This function signature is used for a variety of functions that accept +passed in functions used to transform point locations between two coordinate +spaces. + +The GDALCreateGenImgProjTransformer(), GDALCreateReprojectionTransformer(), +GDALCreateGCPTransformer() and GDALCreateApproxTransformer() functions can +be used to prepare argument data for some built-in transformers. As well, +applications can implement their own transformers to the following signature. + +\code +typedef int +(*GDALTransformerFunc)( void *pTransformerArg, + int bDstToSrc, int nPointCount, + double *x, double *y, double *z, int *panSuccess ); +\endcode + +@param pTransformerArg application supplied callback data used by the +transformer. + +@param bDstToSrc if TRUE the transformation will be from the destination +coordinate space to the source coordinate system, otherwise the transformation +will be from the source coordinate system to the destination coordinate system. + +@param nPointCount number of points in the x, y and z arrays. + +@param x input X coordinates. Results returned in same array. + +@param y input Y coordinates. Results returned in same array. + +@param z input Z coordinates. Results returned in same array. + +@param panSuccess array of ints in which success (TRUE) or failure (FALSE) +flags are returned for the translation of each point. + +@return TRUE if the overall transformation succeeds (though some individual +points may have failed) or FALSE if the overall transformation fails. + +*/ + +/************************************************************************/ +/* GDALSuggestedWarpOutput() */ +/************************************************************************/ + +/** + * Suggest output file size. + * + * This function is used to suggest the size, and georeferenced extents + * appropriate given the indicated transformation and input file. It walks + * the edges of the input file (approximately 20 sample points along each + * edge) transforming into output coordinates in order to get an extents box. + * + * Then a resolution is computed with the intent that the length of the + * distance from the top left corner of the output imagery to the bottom right + * corner would represent the same number of pixels as in the source image. + * Note that if the image is somewhat rotated the diagonal taken isnt of the + * whole output bounding rectangle, but instead of the locations where the + * top/left and bottom/right corners transform. The output pixel size is + * always square. This is intended to approximately preserve the resolution + * of the input data in the output file. + * + * The values returned in padfGeoTransformOut, pnPixels and pnLines are + * the suggested number of pixels and lines for the output file, and the + * geotransform relating those pixels to the output georeferenced coordinates. + * + * The trickiest part of using the function is ensuring that the + * transformer created is from source file pixel/line coordinates to + * output file georeferenced coordinates. This can be accomplished with + * GDALCreateGenImgProjTransformer() by passing a NULL for the hDstDS. + * + * @param hSrcDS the input image (it is assumed the whole input images is + * being transformed). + * @param pfnTransformer the transformer function. + * @param pTransformArg the callback data for the transformer function. + * @param padfGeoTransformOut the array of six doubles in which the suggested + * geotransform is returned. + * @param pnPixels int in which the suggest pixel width of output is returned. + * @param pnLines int in which the suggest pixel height of output is returned. + * + * @return CE_None if successful or CE_Failure otherwise. + */ + + +CPLErr CPL_STDCALL +GDALSuggestedWarpOutput( GDALDatasetH hSrcDS, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfGeoTransformOut, + int *pnPixels, int *pnLines ) + +{ + VALIDATE_POINTER1( hSrcDS, "GDALSuggestedWarpOutput", CE_Failure ); + + double adfExtent[4] = { 0 }; + + return GDALSuggestedWarpOutput2( hSrcDS, pfnTransformer, pTransformArg, + padfGeoTransformOut, pnPixels, pnLines, + adfExtent, 0 ); +} + + +static int GDALSuggestedWarpOutput2_MustAdjustForRightBorder( + GDALTransformerFunc pfnTransformer, void *pTransformArg, + double* padfExtent, + CPL_UNUSED int nPixels, + int nLines, + double dfPixelSizeX, double dfPixelSizeY) +{ + int nSamplePoints; + double dfRatio; + int bErr; + int nBadCount; + int abSuccess[21] = { 0 }; + double adfX[21] = { 0 }; + double adfY[21] = { 0 }; + double adfZ[21] = { 0 }; + + //double dfMinXOut = padfExtent[0]; + //double dfMinYOut = padfExtent[1]; + double dfMaxXOut = padfExtent[2]; + double dfMaxYOut = padfExtent[3]; + + // Take 20 steps + nSamplePoints = 0; + for( dfRatio = 0.0; dfRatio <= 1.01; dfRatio += 0.05 ) + { + // Ensure we end exactly at the end. + if( dfRatio > 0.99 ) + dfRatio = 1.0; + + // Along right + adfX[nSamplePoints] = dfMaxXOut; + adfY[nSamplePoints] = dfMaxYOut - dfPixelSizeY * dfRatio * nLines; + adfZ[nSamplePoints++] = 0.0; + } + + bErr = FALSE; + if( !pfnTransformer( pTransformArg, TRUE, nSamplePoints, + adfX, adfY, adfZ, abSuccess ) ) + { + bErr = TRUE; + } + + if( !bErr && !pfnTransformer( pTransformArg, FALSE, nSamplePoints, + adfX, adfY, adfZ, abSuccess ) ) + { + bErr = TRUE; + } + + nSamplePoints = 0; + nBadCount = 0; + for( dfRatio = 0.0; !bErr && dfRatio <= 1.01; dfRatio += 0.05 ) + { + double expected_x = dfMaxXOut; + double expected_y = dfMaxYOut - dfPixelSizeY * dfRatio * nLines; + if (fabs(adfX[nSamplePoints] - expected_x) > dfPixelSizeX || + fabs(adfY[nSamplePoints] - expected_y) > dfPixelSizeY) + nBadCount ++; + nSamplePoints ++; + } + + return (nBadCount == nSamplePoints); +} + + +static int GDALSuggestedWarpOutput2_MustAdjustForBottomBorder( + GDALTransformerFunc pfnTransformer, void *pTransformArg, + double* padfExtent, int nPixels, + CPL_UNUSED int nLines, + double dfPixelSizeX, double dfPixelSizeY) +{ + int nSamplePoints; + double dfRatio; + int bErr; + int nBadCount; + int abSuccess[21] = { 0 }; + double adfX[21] = { 0 }; + double adfY[21] = { 0 }; + double adfZ[21] = { 0 }; + + double dfMinXOut = padfExtent[0]; + double dfMinYOut = padfExtent[1]; + //double dfMaxXOut = padfExtent[2]; + //double dfMaxYOut = padfExtent[3]; + + // Take 20 steps + nSamplePoints = 0; + for( dfRatio = 0.0; dfRatio <= 1.01; dfRatio += 0.05 ) + { + // Ensure we end exactly at the end. + if( dfRatio > 0.99 ) + dfRatio = 1.0; + + // Along right + adfX[nSamplePoints] = dfMinXOut + dfPixelSizeX * dfRatio * nPixels; + adfY[nSamplePoints] = dfMinYOut; + adfZ[nSamplePoints++] = 0.0; + } + + bErr = FALSE; + if( !pfnTransformer( pTransformArg, TRUE, nSamplePoints, + adfX, adfY, adfZ, abSuccess ) ) + { + bErr = TRUE; + } + + if( !bErr && !pfnTransformer( pTransformArg, FALSE, nSamplePoints, + adfX, adfY, adfZ, abSuccess ) ) + { + bErr = TRUE; + } + + nSamplePoints = 0; + nBadCount = 0; + for( dfRatio = 0.0; !bErr && dfRatio <= 1.01; dfRatio += 0.05 ) + { + double expected_x = dfMinXOut + dfPixelSizeX * dfRatio * nPixels; + double expected_y = dfMinYOut; + if (fabs(adfX[nSamplePoints] - expected_x) > dfPixelSizeX || + fabs(adfY[nSamplePoints] - expected_y) > dfPixelSizeY) + nBadCount ++; + nSamplePoints ++; + } + + return (nBadCount == nSamplePoints); +} + +/************************************************************************/ +/* GDALSuggestedWarpOutput2() */ +/************************************************************************/ + +/** + * Suggest output file size. + * + * This function is used to suggest the size, and georeferenced extents + * appropriate given the indicated transformation and input file. It walks + * the edges of the input file (approximately 20 sample points along each + * edge) transforming into output coordinates in order to get an extents box. + * + * Then a resolution is computed with the intent that the length of the + * distance from the top left corner of the output imagery to the bottom right + * corner would represent the same number of pixels as in the source image. + * Note that if the image is somewhat rotated the diagonal taken isnt of the + * whole output bounding rectangle, but instead of the locations where the + * top/left and bottom/right corners transform. The output pixel size is + * always square. This is intended to approximately preserve the resolution + * of the input data in the output file. + * + * The values returned in padfGeoTransformOut, pnPixels and pnLines are + * the suggested number of pixels and lines for the output file, and the + * geotransform relating those pixels to the output georeferenced coordinates. + * + * The trickiest part of using the function is ensuring that the + * transformer created is from source file pixel/line coordinates to + * output file georeferenced coordinates. This can be accomplished with + * GDALCreateGenImgProjTransformer() by passing a NULL for the hDstDS. + * + * @param hSrcDS the input image (it is assumed the whole input images is + * being transformed). + * @param pfnTransformer the transformer function. + * @param pTransformArg the callback data for the transformer function. + * @param padfGeoTransformOut the array of six doubles in which the suggested + * geotransform is returned. + * @param pnPixels int in which the suggest pixel width of output is returned. + * @param pnLines int in which the suggest pixel height of output is returned. + * @param padfExtent Four entry array to return extents as (xmin, ymin, xmax, ymax). + * @param nOptions Options, currently always zero. + * + * @return CE_None if successful or CE_Failure otherwise. + */ + +CPLErr CPL_STDCALL +GDALSuggestedWarpOutput2( GDALDatasetH hSrcDS, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + double *padfGeoTransformOut, + int *pnPixels, int *pnLines, + double *padfExtent, + CPL_UNUSED int nOptions ) +{ + VALIDATE_POINTER1( hSrcDS, "GDALSuggestedWarpOutput2", CE_Failure ); + +/* -------------------------------------------------------------------- */ +/* Setup sample points all around the edge of the input raster. */ +/* -------------------------------------------------------------------- */ + int nSamplePoints = 0; + int nInXSize = GDALGetRasterXSize( hSrcDS ); + int nInYSize = GDALGetRasterYSize( hSrcDS ); + + if (pfnTransformer == GDALGenImgProjTransform) + { + /* In case CHECK_WITH_INVERT_PROJ has been modified */ + GDALRefreshGenImgProjTransformer(pTransformArg); + } + +#define N_PIXELSTEP 50 + int nSteps = (int) (double(MIN(nInYSize, nInXSize)) / N_PIXELSTEP + .5); + if (nSteps < 20) + nSteps = 20; + nSteps = MIN(nSteps,100); + +retry: + int nSampleMax = (nSteps + 1)*(nSteps + 1); + int *pabSuccess = NULL; + double *padfX, *padfY, *padfZ; + double *padfXRevert, *padfYRevert, *padfZRevert; + + double dfRatio = 0.0; + double dfStep = 1. / nSteps; + + pabSuccess = (int *) VSIMalloc3(sizeof(int), nSteps + 1, nSteps + 1); + padfX = (double *) VSIMalloc3(sizeof(double) * 3, nSteps + 1, nSteps + 1); + padfXRevert = (double *) VSIMalloc3(sizeof(double) * 3, nSteps + 1, nSteps + 1); + if (pabSuccess == NULL || padfX == NULL || padfXRevert == NULL) + { + CPLFree( padfX ); + CPLFree( padfXRevert ); + CPLFree( pabSuccess ); + if (nSteps > 20) + { + nSteps = 20; + goto retry; + } + return CE_Failure; + } + padfY = padfX + nSampleMax; + padfZ = padfX + nSampleMax * 2; + padfYRevert = padfXRevert + nSampleMax; + padfZRevert = padfXRevert + nSampleMax * 2; + + + // Take N_STEPS steps + int iStep; + for( iStep = 0; iStep <= nSteps; iStep ++ ) + { + dfRatio = (iStep == nSteps) ? 1.0 : iStep * dfStep; + + // Along top + padfX[iStep] = dfRatio * nInXSize; + padfY[iStep] = 0.0; + padfZ[iStep] = 0.0; + + // Along bottom + padfX[nSteps + 1 + iStep] = dfRatio * nInXSize; + padfY[nSteps + 1 + iStep] = nInYSize; + padfZ[nSteps + 1 + iStep] = 0.0; + + // Along left + padfX[2 * (nSteps + 1) + iStep] = 0.0; + padfY[2 * (nSteps + 1) + iStep] = dfRatio * nInYSize; + padfZ[2 * (nSteps + 1) + iStep] = 0.0; + + // Along right + padfX[3 * (nSteps + 1) + iStep] = nInXSize; + padfY[3 * (nSteps + 1) + iStep] = dfRatio * nInYSize; + padfZ[3 * (nSteps + 1) + iStep] = 0.0; + } + + nSamplePoints = 4 * (nSteps + 1); + + memset( pabSuccess, 1, sizeof(int) * nSampleMax ); + +/* -------------------------------------------------------------------- */ +/* Transform them to the output coordinate system. */ +/* -------------------------------------------------------------------- */ + int nFailedCount = 0, i; + + if( !pfnTransformer( pTransformArg, FALSE, nSamplePoints, + padfX, padfY, padfZ, pabSuccess ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GDALSuggestedWarpOutput() failed because the passed\n" + "transformer failed." ); + CPLFree( padfX ); + CPLFree( padfXRevert ); + CPLFree( pabSuccess ); + return CE_Failure; + } + + for( i = 0; i < nSamplePoints; i++ ) + { + if( !pabSuccess[i] ) + nFailedCount++; + } + +/* -------------------------------------------------------------------- */ +/* Check if the computed target coordinates are revertable. */ +/* If not, try the detailed grid sampling. */ +/* -------------------------------------------------------------------- */ + if (nFailedCount == 0 ) + { + memcpy(padfXRevert, padfX, nSamplePoints * sizeof(double)); + memcpy(padfYRevert, padfY, nSamplePoints * sizeof(double)); + memcpy(padfZRevert, padfZ, nSamplePoints * sizeof(double)); + if( !pfnTransformer( pTransformArg, TRUE, nSamplePoints, + padfXRevert, padfYRevert, padfZRevert, pabSuccess ) ) + { + nFailedCount = 1; + } + else + { + for( i = 0; nFailedCount == 0 && i < nSamplePoints; i++ ) + { + if( !pabSuccess[i] ) + { + nFailedCount++; + break; + } + + dfRatio = (i % (nSteps + 1)) * dfStep; + if (dfRatio>0.99) + dfRatio = 1.0; + + double dfExpectedX, dfExpectedY; + if (i < nSteps + 1) + { + dfExpectedX = dfRatio * nInXSize; + dfExpectedY = 0.0; + } + else if (i < 2 * (nSteps + 1)) + { + dfExpectedX = dfRatio * nInXSize; + dfExpectedY = nInYSize; + } + else if (i < 3 * (nSteps + 1)) + { + dfExpectedX = 0.0; + dfExpectedY = dfRatio * nInYSize; + } + else + { + dfExpectedX = nInXSize; + dfExpectedY = dfRatio * nInYSize; + } + + if (fabs(padfXRevert[i] - dfExpectedX) > nInXSize / nSteps || + fabs(padfYRevert[i] - dfExpectedY) > nInYSize / nSteps) + nFailedCount ++; + } + if( nFailedCount != 0 ) + CPLDebug("WARP", "At least one point failed after revert transform"); + } + } + else + CPLDebug("WARP", "At least one point failed after direct transform"); + +/* -------------------------------------------------------------------- */ +/* If any of the edge points failed to transform, we need to */ +/* build a fairly detailed internal grid of points instead to */ +/* help identify the area that is transformable. */ +/* -------------------------------------------------------------------- */ + if( nFailedCount > 0 ) + { + int iStep2; + double dfRatio2; + nSamplePoints = 0; + + // Take N_STEPS steps + for( iStep = 0; iStep <= nSteps; iStep ++ ) + { + dfRatio = (iStep == nSteps) ? 1.0 : iStep * dfStep; + + for( iStep2 = 0; iStep2 <= nSteps; iStep2 ++ ) + { + dfRatio2 = (iStep2 == nSteps) ? 1.0 : iStep2 * dfStep; + + // From top to bottom, from left to right + padfX[nSamplePoints] = dfRatio2 * nInXSize; + padfY[nSamplePoints] = dfRatio * nInYSize; + padfZ[nSamplePoints++] = 0.0; + } + } + + CPLAssert( nSamplePoints == nSampleMax ); + + if( !pfnTransformer( pTransformArg, FALSE, nSamplePoints, + padfX, padfY, padfZ, pabSuccess ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GDALSuggestedWarpOutput() failed because the passed\n" + "transformer failed." ); + + CPLFree( padfX ); + CPLFree( padfXRevert ); + CPLFree( pabSuccess ); + + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Collect the bounds, ignoring any failed points. */ +/* -------------------------------------------------------------------- */ + double dfMinXOut=0, dfMinYOut=0, dfMaxXOut=0, dfMaxYOut=0; + int bGotInitialPoint = FALSE; + + nFailedCount = 0; + for( i = 0; i < nSamplePoints; i++ ) + { + int x_i, y_i; + + if( nSamplePoints == nSampleMax ) + { + x_i = i % (nSteps + 1); + y_i = i / (nSteps + 1); + } + else + { + if( i < 2 * (nSteps + 1 ) ) + { + x_i = i % (nSteps + 1); + y_i = (i < nSteps + 1) ? 0 : nSteps; + } + else + x_i = y_i = 0; + } + + if (x_i > 0 && (pabSuccess[i-1] || pabSuccess[i])) + { + double x_out_before = padfX[i-1]; + double x_out_after = padfX[i]; + int nIter = 0; + double x_in_before = (x_i - 1) * nInXSize * 1.0 / nSteps; + double x_in_after = x_i * nInXSize * 1.0 / nSteps; + int valid_before = pabSuccess[i-1]; + int valid_after = pabSuccess[i]; + + /* Detect discontinuity in target coordinates when the target x coordinates */ + /* change sign. This may be a false positive when the targe tx is around 0 */ + /* Dichotomic search to reduce the interval to near the discontinuity and */ + /* get a better out extent */ + while ( (!valid_before || !valid_after || + x_out_before * x_out_after < 0) && nIter < 16 ) + { + double x = (x_in_before + x_in_after) / 2; + double y = y_i * nInYSize * 1.0 / nSteps; + double z= 0; + //fprintf(stderr, "[%d] (%f, %f) -> ", nIter, x, y); + int bSuccess = TRUE; + if( !pfnTransformer( pTransformArg, FALSE, 1, + &x, &y, &z, &bSuccess ) || !bSuccess ) + { + //fprintf(stderr, "invalid\n"); + if (!valid_before) + { + x_in_before = (x_in_before + x_in_after) / 2; + } + else if (!valid_after) + { + x_in_after = (x_in_before + x_in_after) / 2; + } + else + break; + } + else + { + //fprintf(stderr, "(%f, %f)\n", x, y); + + if( !bGotInitialPoint ) + { + bGotInitialPoint = TRUE; + dfMinXOut = dfMaxXOut = x; + dfMinYOut = dfMaxYOut = y; + } + else + { + dfMinXOut = MIN(dfMinXOut,x); + dfMinYOut = MIN(dfMinYOut,y); + dfMaxXOut = MAX(dfMaxXOut,x); + dfMaxYOut = MAX(dfMaxYOut,y); + } + + if (!valid_before || x_out_before * x < 0) + { + valid_after = TRUE; + x_in_after = (x_in_before + x_in_after) / 2; + x_out_after = x; + } + else + { + valid_before = TRUE; + x_out_before = x; + x_in_before = (x_in_before + x_in_after) / 2; + } + } + nIter ++; + } + } + + if( !pabSuccess[i] ) + { + nFailedCount++; + continue; + } + + if( !bGotInitialPoint ) + { + bGotInitialPoint = TRUE; + dfMinXOut = dfMaxXOut = padfX[i]; + dfMinYOut = dfMaxYOut = padfY[i]; + } + else + { + dfMinXOut = MIN(dfMinXOut, padfX[i]); + dfMinYOut = MIN(dfMinYOut, padfY[i]); + dfMaxXOut = MAX(dfMaxXOut, padfX[i]); + dfMaxYOut = MAX(dfMaxYOut, padfY[i]); + } + } + + if( nFailedCount > nSamplePoints - 10 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many points (%d out of %d) failed to transform,\n" + "unable to compute output bounds.", + nFailedCount, nSamplePoints ); + + CPLFree( padfX ); + CPLFree( padfXRevert ); + CPLFree( pabSuccess ); + + return CE_Failure; + } + + if( nFailedCount > 0 ) + CPLDebug( "GDAL", + "GDALSuggestedWarpOutput(): %d out of %d points failed to transform.", + nFailedCount, nSamplePoints ); + +/* -------------------------------------------------------------------- */ +/* Compute the distance in "georeferenced" units from the top */ +/* corner of the transformed input image to the bottom left */ +/* corner of the transformed input. Use this distance to */ +/* compute an approximate pixel size in the output */ +/* georeferenced coordinates. */ +/* -------------------------------------------------------------------- */ + double dfDiagonalDist, dfDeltaX = 0.0, dfDeltaY = 0.0; + + if( pabSuccess[0] && pabSuccess[nSamplePoints - 1] ) + { + dfDeltaX = padfX[nSamplePoints-1] - padfX[0]; + dfDeltaY = padfY[nSamplePoints-1] - padfY[0]; + // In some cases this can result in 0 values. See #5980 + // so fallback to safer method in that case + } + if( dfDeltaX == 0.0 || dfDeltaY == 0.0 ) + { + dfDeltaX = dfMaxXOut - dfMinXOut; + dfDeltaY = dfMaxYOut - dfMinYOut; + } + + dfDiagonalDist = sqrt( dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY ); + +/* -------------------------------------------------------------------- */ +/* Compute a pixel size from this. */ +/* -------------------------------------------------------------------- */ + double dfPixelSize; + + dfPixelSize = dfDiagonalDist + / sqrt(((double)nInXSize)*nInXSize + ((double)nInYSize)*nInYSize); + + double dfPixels = (dfMaxXOut - dfMinXOut) / dfPixelSize; + double dfLines = (dfMaxYOut - dfMinYOut) / dfPixelSize; + + if( dfPixels > INT_MAX - 1 || dfLines > INT_MAX - 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Computed dimensions are too big : %.0f x %.0f", + dfPixels + 0.5, dfLines + 0.5 ); + + CPLFree( padfX ); + CPLFree( padfXRevert ); + CPLFree( pabSuccess ); + + return CE_Failure; + } + + *pnPixels = (int) (dfPixels + 0.5); + *pnLines = (int) (dfLines + 0.5); + + double dfPixelSizeX = dfPixelSize; + double dfPixelSizeY = dfPixelSize; + + double adfExtent[4]; + const double adfRatioArray[] = { 0, 0.001, 0.01, 0.1, 1 }; + size_t nRetry; + +#define N_ELEMENTS(x) (sizeof(x) / sizeof(x[0])) + +/* -------------------------------------------------------------------- */ +/* Check that the right border is not completely out of source */ +/* image. If so, adjust the x pixel size a bit in the hope it will */ +/* fit. */ +/* -------------------------------------------------------------------- */ + for( nRetry = 0; nRetry < N_ELEMENTS(adfRatioArray); nRetry ++ ) + { + double dfTryPixelSizeX = + dfPixelSizeX - dfPixelSizeX * adfRatioArray[nRetry] / *pnPixels; + adfExtent[0] = dfMinXOut; + adfExtent[1] = dfMaxYOut - (*pnLines) * dfPixelSizeY; + adfExtent[2] = dfMinXOut + (*pnPixels) * dfTryPixelSizeX; + adfExtent[3] = dfMaxYOut; + if (!GDALSuggestedWarpOutput2_MustAdjustForRightBorder( + pfnTransformer, pTransformArg, + adfExtent, *pnPixels, *pnLines, + dfTryPixelSizeX, dfPixelSizeY)) + { + dfPixelSizeX = dfTryPixelSizeX; + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Check that the bottom border is not completely out of source */ +/* image. If so, adjust the y pixel size a bit in the hope it will */ +/* fit. */ +/* -------------------------------------------------------------------- */ + for( nRetry = 0; nRetry < N_ELEMENTS(adfRatioArray); nRetry ++ ) + { + double dfTryPixelSizeY = + dfPixelSizeY - dfPixelSizeY * adfRatioArray[nRetry] / *pnLines; + adfExtent[0] = dfMinXOut; + adfExtent[1] = dfMaxYOut - (*pnLines) * dfTryPixelSizeY; + adfExtent[2] = dfMinXOut + (*pnPixels) * dfPixelSizeX; + adfExtent[3] = dfMaxYOut; + if (!GDALSuggestedWarpOutput2_MustAdjustForBottomBorder( + pfnTransformer, pTransformArg, + adfExtent, *pnPixels, *pnLines, + dfPixelSizeX, dfTryPixelSizeY)) + { + dfPixelSizeY = dfTryPixelSizeY; + break; + } + } + + +/* -------------------------------------------------------------------- */ +/* Recompute some bounds so that all return values are consistent */ +/* -------------------------------------------------------------------- */ + dfMaxXOut = dfMinXOut + (*pnPixels) * dfPixelSizeX; + dfMinYOut = dfMaxYOut - (*pnLines) * dfPixelSizeY; + + /* -------------------------------------------------------------------- */ + /* Return raw extents. */ + /* -------------------------------------------------------------------- */ + padfExtent[0] = dfMinXOut; + padfExtent[1] = dfMinYOut; + padfExtent[2] = dfMaxXOut; + padfExtent[3] = dfMaxYOut; + + /* -------------------------------------------------------------------- */ + /* Set the output geotransform. */ + /* -------------------------------------------------------------------- */ + padfGeoTransformOut[0] = dfMinXOut; + padfGeoTransformOut[1] = dfPixelSizeX; + padfGeoTransformOut[2] = 0.0; + padfGeoTransformOut[3] = dfMaxYOut; + padfGeoTransformOut[4] = 0.0; + padfGeoTransformOut[5] = - dfPixelSizeY; + + CPLFree( padfX ); + CPLFree( padfXRevert ); + CPLFree( pabSuccess ); + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALGenImgProjTransformer */ +/* ==================================================================== */ +/************************************************************************/ + +typedef struct { + + GDALTransformerInfo sTI; + + double adfSrcGeoTransform[6]; + double adfSrcInvGeoTransform[6]; + + void *pSrcGCPTransformArg; + void *pSrcRPCTransformArg; + void *pSrcTPSTransformArg; + void *pSrcGeoLocTransformArg; + + void *pReprojectArg; + + double adfDstGeoTransform[6]; + double adfDstInvGeoTransform[6]; + + void *pDstGCPTransformArg; + void *pDstRPCTransformArg; + void *pDstTPSTransformArg; + +} GDALGenImgProjTransformInfo; + +/************************************************************************/ +/* GDALCreateSimilarGenImgProjTransformer() */ +/************************************************************************/ + +static void* GDALCreateSimilarGenImgProjTransformer( void *hTransformArg, double dfRatioX, double dfRatioY ) +{ + VALIDATE_POINTER1( hTransformArg, "GDALCreateSimilarGenImgProjTransformer", NULL ); + + GDALGenImgProjTransformInfo *psInfo = + (GDALGenImgProjTransformInfo *) hTransformArg; + + GDALGenImgProjTransformInfo *psClonedInfo = (GDALGenImgProjTransformInfo *) + CPLMalloc(sizeof(GDALGenImgProjTransformInfo)); + + memcpy(psClonedInfo, psInfo, sizeof(GDALGenImgProjTransformInfo)); + + if( psClonedInfo->pSrcGCPTransformArg ) + psClonedInfo->pSrcGCPTransformArg = GDALCreateSimilarTransformer( psInfo->pSrcGCPTransformArg, dfRatioX, dfRatioY ); + else if( psClonedInfo->pSrcRPCTransformArg ) + psClonedInfo->pSrcRPCTransformArg = GDALCreateSimilarTransformer( psInfo->pSrcRPCTransformArg, dfRatioX, dfRatioY ); + else if( psClonedInfo->pSrcTPSTransformArg ) + psClonedInfo->pSrcTPSTransformArg = GDALCreateSimilarTransformer( psInfo->pSrcTPSTransformArg, dfRatioX, dfRatioY ); + else if( psClonedInfo->pSrcGeoLocTransformArg ) + psClonedInfo->pSrcGeoLocTransformArg = GDALCreateSimilarTransformer( psInfo->pSrcGeoLocTransformArg, dfRatioX, dfRatioY ); + else if( dfRatioX != 1.0 || dfRatioY != 1.0 ) + { + if( psClonedInfo->adfSrcGeoTransform[2] == 0.0 && psClonedInfo->adfSrcGeoTransform[4] == 0.0 ) + { + psClonedInfo->adfSrcGeoTransform[1] *= dfRatioX; + psClonedInfo->adfSrcGeoTransform[5] *= dfRatioY; + } + else + { + /* If the x and y ratios are not equal, then we cannot really */ + /* compute a geotransform */ + psClonedInfo->adfSrcGeoTransform[1] *= dfRatioX; + psClonedInfo->adfSrcGeoTransform[2] *= dfRatioX; + psClonedInfo->adfSrcGeoTransform[4] *= dfRatioX; + psClonedInfo->adfSrcGeoTransform[5] *= dfRatioX; + } + if( !GDALInvGeoTransform( psClonedInfo->adfSrcGeoTransform, + psClonedInfo->adfSrcInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + GDALDestroyGenImgProjTransformer( psClonedInfo ); + return NULL; + } + } + + if( psClonedInfo->pReprojectArg ) + psClonedInfo->pReprojectArg = GDALCloneTransformer( psInfo->pReprojectArg ); + + if( psClonedInfo->pDstGCPTransformArg ) + psClonedInfo->pDstGCPTransformArg = GDALCloneTransformer( psInfo->pDstGCPTransformArg ); + else if( psClonedInfo->pDstRPCTransformArg ) + psClonedInfo->pDstRPCTransformArg = GDALCloneTransformer( psInfo->pDstRPCTransformArg ); + else if( psClonedInfo->pDstTPSTransformArg ) + psClonedInfo->pDstTPSTransformArg = GDALCloneTransformer( psInfo->pDstTPSTransformArg ); + + + return psClonedInfo; +} + +/************************************************************************/ +/* GDALCreateGenImgProjTransformer() */ +/************************************************************************/ + +/** + * Create image to image transformer. + * + * This function creates a transformation object that maps from pixel/line + * coordinates on one image to pixel/line coordinates on another image. The + * images may potentially be georeferenced in different coordinate systems, + * and may used GCPs to map between their pixel/line coordinates and + * georeferenced coordinates (as opposed to the default assumption that their + * geotransform should be used). + * + * This transformer potentially performs three concatenated transformations. + * + * The first stage is from source image pixel/line coordinates to source + * image georeferenced coordinates, and may be done using the geotransform, + * or if not defined using a polynomial model derived from GCPs. If GCPs + * are used this stage is accomplished using GDALGCPTransform(). + * + * The second stage is to change projections from the source coordinate system + * to the destination coordinate system, assuming they differ. This is + * accomplished internally using GDALReprojectionTransform(). + * + * The third stage is converting from destination image georeferenced + * coordinates to destination image coordinates. This is done using the + * destination image geotransform, or if not available, using a polynomial + * model derived from GCPs. If GCPs are used this stage is accomplished using + * GDALGCPTransform(). This stage is skipped if hDstDS is NULL when the + * transformation is created. + * + * @param hSrcDS source dataset, or NULL. + * @param pszSrcWKT the coordinate system for the source dataset. If NULL, + * it will be read from the dataset itself. + * @param hDstDS destination dataset (or NULL). + * @param pszDstWKT the coordinate system for the destination dataset. If + * NULL, and hDstDS not NULL, it will be read from the destination dataset. + * @param bGCPUseOK TRUE if GCPs should be used if the geotransform is not + * available on the source dataset (not destination). + * @param dfGCPErrorThreshold ignored/deprecated. + * @param nOrder the maximum order to use for GCP derived polynomials if + * possible. Use 0 to autoselect, or -1 for thin plate splines. + * + * @return handle suitable for use GDALGenImgProjTransform(), and to be + * deallocated with GDALDestroyGenImgProjTransformer(). + */ + +void * +GDALCreateGenImgProjTransformer( GDALDatasetH hSrcDS, const char *pszSrcWKT, + GDALDatasetH hDstDS, const char *pszDstWKT, + int bGCPUseOK, + CPL_UNUSED double dfGCPErrorThreshold, + int nOrder ) +{ + char **papszOptions = NULL; + void *pRet; + + if( pszSrcWKT != NULL ) + papszOptions = CSLSetNameValue( papszOptions, "SRC_SRS", pszSrcWKT ); + if( pszDstWKT != NULL ) + papszOptions = CSLSetNameValue( papszOptions, "DST_SRS", pszDstWKT ); + if( !bGCPUseOK ) + papszOptions = CSLSetNameValue( papszOptions, "GCPS_OK", "FALSE" ); + if( nOrder != 0 ) + papszOptions = CSLSetNameValue( papszOptions, "MAX_GCP_ORDER", + CPLString().Printf("%d",nOrder) ); + + pRet = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, papszOptions ); + CSLDestroy( papszOptions ); + + return pRet; +} + + + +/************************************************************************/ +/* InsertCenterLong() */ +/* */ +/* Insert a CENTER_LONG Extension entry on a GEOGCS to indicate */ +/* the center longitude of the dataset for wrapping purposes. */ +/************************************************************************/ + +static CPLString InsertCenterLong( GDALDatasetH hDS, CPLString osWKT ) + +{ + if( !EQUALN(osWKT.c_str(), "GEOGCS[", 7) ) + return osWKT; + + if( strstr(osWKT,"EXTENSION[\"CENTER_LONG") != NULL ) + return osWKT; + +/* -------------------------------------------------------------------- */ +/* For now we only do this if we have a geotransform since */ +/* other forms require a bunch of extra work. */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + + if( GDALGetGeoTransform( hDS, adfGeoTransform ) != CE_None ) + return osWKT; + +/* -------------------------------------------------------------------- */ +/* Compute min/max longitude based on testing the four corners. */ +/* -------------------------------------------------------------------- */ + double dfMinLong, dfMaxLong; + int nXSize = GDALGetRasterXSize( hDS ); + int nYSize = GDALGetRasterYSize( hDS ); + + dfMinLong = + MIN(MIN(adfGeoTransform[0] + 0 * adfGeoTransform[1] + + 0 * adfGeoTransform[2], + adfGeoTransform[0] + nXSize * adfGeoTransform[1] + + 0 * adfGeoTransform[2]), + MIN(adfGeoTransform[0] + 0 * adfGeoTransform[1] + + nYSize * adfGeoTransform[2], + adfGeoTransform[0] + nXSize * adfGeoTransform[1] + + nYSize * adfGeoTransform[2])); + dfMaxLong = + MAX(MAX(adfGeoTransform[0] + 0 * adfGeoTransform[1] + + 0 * adfGeoTransform[2], + adfGeoTransform[0] + nXSize * adfGeoTransform[1] + + 0 * adfGeoTransform[2]), + MAX(adfGeoTransform[0] + 0 * adfGeoTransform[1] + + nYSize * adfGeoTransform[2], + adfGeoTransform[0] + nXSize * adfGeoTransform[1] + + nYSize * adfGeoTransform[2])); + + if( dfMaxLong - dfMinLong > 360.0 ) + return osWKT; + +/* -------------------------------------------------------------------- */ +/* Insert center long. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS( osWKT ); + double dfCenterLong = (dfMaxLong + dfMinLong) / 2.0; + OGR_SRSNode *poExt; + + poExt = new OGR_SRSNode( "EXTENSION" ); + poExt->AddChild( new OGR_SRSNode( "CENTER_LONG" ) ); + poExt->AddChild( new OGR_SRSNode( CPLString().Printf("%g",dfCenterLong) )); + + oSRS.GetRoot()->AddChild( poExt->Clone() ); + delete poExt; + +/* -------------------------------------------------------------------- */ +/* Convert back to wkt. */ +/* -------------------------------------------------------------------- */ + char *pszWKT = NULL; + oSRS.exportToWkt( &pszWKT ); + + osWKT = pszWKT; + CPLFree( pszWKT ); + + return osWKT; +} + +/************************************************************************/ +/* GDALCreateGenImgProjTransformerInternal() */ +/************************************************************************/ + +static GDALGenImgProjTransformInfo* GDALCreateGenImgProjTransformerInternal() +{ +/* -------------------------------------------------------------------- */ +/* Initialize the transform info. */ +/* -------------------------------------------------------------------- */ + GDALGenImgProjTransformInfo* psInfo = (GDALGenImgProjTransformInfo *) + CPLCalloc(sizeof(GDALGenImgProjTransformInfo),1); + + memcpy( psInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE) ); + psInfo->sTI.pszClassName = "GDALGenImgProjTransformer"; + psInfo->sTI.pfnTransform = GDALGenImgProjTransform; + psInfo->sTI.pfnCleanup = GDALDestroyGenImgProjTransformer; + psInfo->sTI.pfnSerialize = GDALSerializeGenImgProjTransformer; + psInfo->sTI.pfnCreateSimilar = GDALCreateSimilarGenImgProjTransformer; + + return psInfo; +} + +/************************************************************************/ +/* GDALCreateGenImgProjTransformer2() */ +/************************************************************************/ + +/** + * Create image to image transformer. + * + * This function creates a transformation object that maps from pixel/line + * coordinates on one image to pixel/line coordinates on another image. The + * images may potentially be georeferenced in different coordinate systems, + * and may used GCPs to map between their pixel/line coordinates and + * georeferenced coordinates (as opposed to the default assumption that their + * geotransform should be used). + * + * This transformer potentially performs three concatenated transformations. + * + * The first stage is from source image pixel/line coordinates to source + * image georeferenced coordinates, and may be done using the geotransform, + * or if not defined using a polynomial model derived from GCPs. If GCPs + * are used this stage is accomplished using GDALGCPTransform(). + * + * The second stage is to change projections from the source coordinate system + * to the destination coordinate system, assuming they differ. This is + * accomplished internally using GDALReprojectionTransform(). + * + * The third stage is converting from destination image georeferenced + * coordinates to destination image coordinates. This is done using the + * destination image geotransform, or if not available, using a polynomial + * model derived from GCPs. If GCPs are used this stage is accomplished using + * GDALGCPTransform(). This stage is skipped if hDstDS is NULL when the + * transformation is created. + * + * Supported Options: + *
    + *
  • SRC_SRS: WKT SRS to be used as an override for hSrcDS. + *
  • DST_SRS: WKT SRS to be used as an override for hDstDS. + *
  • GCPS_OK: If false, GCPs will not be used, default is TRUE. + *
  • REFINE_MINIMUM_GCPS: The minimum amount of GCPs that should be available after the refinement. + *
  • REFINE_TOLERANCE: The tolernace that specifies when a GCP will be eliminated. + *
  • MAX_GCP_ORDER: the maximum order to use for GCP derived polynomials if + * possible. The default is to autoselect based on the number of GCPs. + * A value of -1 triggers use of Thin Plate Spline instead of polynomials. + *
  • SRC_METHOD: may have a value which is one of GEOTRANSFORM, + * GCP_POLYNOMIAL, GCP_TPS, GEOLOC_ARRAY, RPC to force only one geolocation + * method to be considered on the source dataset. Will be used for pixel/line + * to georef transformation on the source dataset. + *
  • DST_METHOD: may have a value which is one of GEOTRANSFORM, + * GCP_POLYNOMIAL, GCP_TPS, GEOLOC_ARRAY, RPC to force only one geolocation + * method to be considered on the target dataset. Will be used for pixel/line + * to georef transformation on the destination dataset. + *
  • RPC_HEIGHT: A fixed height to be used with RPC calculations. + *
  • RPC_DEM: The name of a DEM file to be used with RPC calculations. + *
  • Other RPC related options. See GDALCreateRPCTransformer() + *
  • INSERT_CENTER_LONG: May be set to FALSE to disable setting up a + * CENTER_LONG value on the coordinate system to rewrap things around the + * center of the image. + *
+ * + * @param hSrcDS source dataset, or NULL. + * @param hDstDS destination dataset (or NULL). + * @param papszOptions NULL-terminated list of string options (or NULL). + * + * @return handle suitable for use GDALGenImgProjTransform(), and to be + * deallocated with GDALDestroyGenImgProjTransformer() or NULL on failure. + */ + +void * +GDALCreateGenImgProjTransformer2( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, + char **papszOptions ) + +{ + GDALGenImgProjTransformInfo *psInfo; + char **papszMD; + GDALRPCInfo sRPCInfo; + const char *pszMethod = CSLFetchNameValue( papszOptions, "SRC_METHOD" ); + if( pszMethod == NULL ) + pszMethod = CSLFetchNameValue( papszOptions, "METHOD" ); + const char *pszValue; + int nOrder = 0, bGCPUseOK = TRUE, nMinimumGcps = -1, bRefine = FALSE; + double dfTolerance = 0.0; + const char *pszSrcWKT = CSLFetchNameValue( papszOptions, "SRC_SRS" ); + const char *pszDstWKT = CSLFetchNameValue( papszOptions, "DST_SRS" ); + + pszValue = CSLFetchNameValue( papszOptions, "MAX_GCP_ORDER" ); + if( pszValue ) + nOrder = atoi(pszValue); + + pszValue = CSLFetchNameValue( papszOptions, "GCPS_OK" ); + if( pszValue ) + bGCPUseOK = CSLTestBoolean(pszValue); + + pszValue = CSLFetchNameValue( papszOptions, "REFINE_MINIMUM_GCPS" ); + if( pszValue ) + { + if( atoi(pszValue) != -1) + nMinimumGcps = atoi(pszValue); + } + + pszValue = CSLFetchNameValue( papszOptions, "REFINE_TOLERANCE" ); + if( pszValue ) + { + dfTolerance = CPLAtof(pszValue); + bRefine = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Initialize the transform info. */ +/* -------------------------------------------------------------------- */ + psInfo = GDALCreateGenImgProjTransformerInternal(); + +/* -------------------------------------------------------------------- */ +/* Get forward and inverse geotransform for the source image. */ +/* -------------------------------------------------------------------- */ + if( hSrcDS == NULL || (pszMethod != NULL && EQUAL(pszMethod,"NO_GEOTRANSFORM")) ) + { + psInfo->adfSrcGeoTransform[0] = 0.0; + psInfo->adfSrcGeoTransform[1] = 1.0; + psInfo->adfSrcGeoTransform[2] = 0.0; + psInfo->adfSrcGeoTransform[3] = 0.0; + psInfo->adfSrcGeoTransform[4] = 0.0; + psInfo->adfSrcGeoTransform[5] = 1.0; + memcpy( psInfo->adfSrcInvGeoTransform, psInfo->adfSrcGeoTransform, + sizeof(double) * 6 ); + } + + else if( (pszMethod == NULL || EQUAL(pszMethod,"GEOTRANSFORM")) + && GDALGetGeoTransform( hSrcDS, psInfo->adfSrcGeoTransform ) + == CE_None + && (psInfo->adfSrcGeoTransform[0] != 0.0 + || psInfo->adfSrcGeoTransform[1] != 1.0 + || psInfo->adfSrcGeoTransform[2] != 0.0 + || psInfo->adfSrcGeoTransform[3] != 0.0 + || psInfo->adfSrcGeoTransform[4] != 0.0 + || ABS(psInfo->adfSrcGeoTransform[5]) != 1.0) ) + { + if( !GDALInvGeoTransform( psInfo->adfSrcGeoTransform, + psInfo->adfSrcInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + if( pszSrcWKT == NULL ) + pszSrcWKT = GDALGetProjectionRef( hSrcDS ); + } + + else if( bGCPUseOK + && (pszMethod == NULL || EQUAL(pszMethod,"GCP_POLYNOMIAL") ) + && GDALGetGCPCount( hSrcDS ) > 0 && nOrder >= 0 ) + { + if(bRefine) + { + psInfo->pSrcGCPTransformArg = + GDALCreateGCPRefineTransformer( GDALGetGCPCount( hSrcDS ), + GDALGetGCPs( hSrcDS ), nOrder, + FALSE, dfTolerance, nMinimumGcps ); + } + else + { + psInfo->pSrcGCPTransformArg = + GDALCreateGCPTransformer( GDALGetGCPCount( hSrcDS ), + GDALGetGCPs( hSrcDS ), nOrder, + FALSE ); + } + + if( psInfo->pSrcGCPTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + + if( pszSrcWKT == NULL ) + pszSrcWKT = GDALGetGCPProjection( hSrcDS ); + } + + else if( bGCPUseOK + && GDALGetGCPCount( hSrcDS ) > 0 + && nOrder <= 0 + && (pszMethod == NULL || EQUAL(pszMethod,"GCP_TPS")) ) + { + psInfo->pSrcTPSTransformArg = + GDALCreateTPSTransformerInt( GDALGetGCPCount( hSrcDS ), + GDALGetGCPs( hSrcDS ), FALSE, + papszOptions); + if( psInfo->pSrcTPSTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + + if( pszSrcWKT == NULL ) + pszSrcWKT = GDALGetGCPProjection( hSrcDS ); + } + + else if( (pszMethod == NULL || EQUAL(pszMethod,"RPC")) + && (papszMD = GDALGetMetadata( hSrcDS, "RPC" )) != NULL + && GDALExtractRPCInfo( papszMD, &sRPCInfo ) ) + { + psInfo->pSrcRPCTransformArg = + GDALCreateRPCTransformer( &sRPCInfo, FALSE, 0.1, papszOptions ); + if( psInfo->pSrcRPCTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + if( pszSrcWKT == NULL ) + pszSrcWKT = SRS_WKT_WGS84; + } + + else if( (pszMethod == NULL || EQUAL(pszMethod,"GEOLOC_ARRAY")) + && (papszMD = GDALGetMetadata( hSrcDS, "GEOLOCATION" )) != NULL ) + { + psInfo->pSrcGeoLocTransformArg = + GDALCreateGeoLocTransformer( hSrcDS, papszMD, FALSE ); + + if( psInfo->pSrcGeoLocTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + if( pszSrcWKT == NULL ) + pszSrcWKT = CSLFetchNameValue( papszMD, "SRS" ); + } + + else if( pszMethod != NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to compute a %s based transformation between pixel/line\n" + "and georeferenced coordinates for %s.\n", + pszMethod, GDALGetDescription( hSrcDS ) ); + + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to compute a transformation between pixel/line\n" + "and georeferenced coordinates for %s.\n" + "There is no affine transformation and no GCPs.\n" + "Specify transformation option SRC_METHOD=NO_GEOTRANSFORM to bypass this check.", + GDALGetDescription( hSrcDS ) ); + + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Get forward and inverse geotransform for destination image. */ +/* If we have no destination use a unit transform. */ +/* -------------------------------------------------------------------- */ + const char *pszDstMethod = CSLFetchNameValue( papszOptions, "DST_METHOD" ); + + if( !hDstDS || (pszDstMethod != NULL && EQUAL(pszDstMethod,"NO_GEOTRANSFORM")) ) + { + psInfo->adfDstGeoTransform[0] = 0.0; + psInfo->adfDstGeoTransform[1] = 1.0; + psInfo->adfDstGeoTransform[2] = 0.0; + psInfo->adfDstGeoTransform[3] = 0.0; + psInfo->adfDstGeoTransform[4] = 0.0; + psInfo->adfDstGeoTransform[5] = 1.0; + memcpy( psInfo->adfDstInvGeoTransform, psInfo->adfDstGeoTransform, + sizeof(double) * 6 ); + } + else if( (pszDstMethod == NULL || EQUAL(pszDstMethod,"GEOTRANSFORM")) + && GDALGetGeoTransform( hDstDS, psInfo->adfDstGeoTransform ) == CE_None) + { + if( pszDstWKT == NULL ) + pszDstWKT = GDALGetProjectionRef( hDstDS ); + + if( !GDALInvGeoTransform( psInfo->adfDstGeoTransform, + psInfo->adfDstInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + } + else if( bGCPUseOK + && (pszDstMethod == NULL || EQUAL(pszDstMethod,"GCP_POLYNOMIAL") ) + && GDALGetGCPCount( hDstDS ) > 0 && nOrder >= 0 ) + { + if(bRefine) + { + psInfo->pDstGCPTransformArg = + GDALCreateGCPRefineTransformer( GDALGetGCPCount( hDstDS ), + GDALGetGCPs( hDstDS ), nOrder, + FALSE, dfTolerance, + nMinimumGcps ); + } + else + { + psInfo->pDstGCPTransformArg = + GDALCreateGCPTransformer( GDALGetGCPCount( hDstDS ), + GDALGetGCPs( hDstDS ), nOrder, + FALSE ); + } + + if( psInfo->pDstGCPTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + + if( pszDstWKT == NULL ) + pszDstWKT = GDALGetGCPProjection( hDstDS ); + } + else if( bGCPUseOK + && GDALGetGCPCount( hDstDS ) > 0 + && nOrder <= 0 + && (pszDstMethod == NULL || EQUAL(pszDstMethod,"GCP_TPS")) ) + { + psInfo->pDstTPSTransformArg = + GDALCreateTPSTransformerInt( GDALGetGCPCount( hDstDS ), + GDALGetGCPs( hDstDS ), FALSE, + papszOptions ); + if( psInfo->pDstTPSTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + + if( pszDstWKT == NULL ) + pszDstWKT = GDALGetGCPProjection( hDstDS ); + } + else if( (pszDstMethod == NULL || EQUAL(pszDstMethod,"RPC")) + && (papszMD = GDALGetMetadata( hDstDS, "RPC" )) != NULL + && GDALExtractRPCInfo( papszMD, &sRPCInfo ) ) + { + psInfo->pDstRPCTransformArg = + GDALCreateRPCTransformer( &sRPCInfo, FALSE, 0.1, papszOptions ); + if( psInfo->pDstRPCTransformArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + if( pszDstWKT == NULL ) + pszDstWKT = SRS_WKT_WGS84; + } + + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to compute a transformation between pixel/line\n" + "and georeferenced coordinates for %s.\n" + "There is no affine transformation and no GCPs.", + GDALGetDescription( hDstDS ) ); + + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Setup reprojection. */ +/* -------------------------------------------------------------------- */ + if( pszSrcWKT != NULL && strlen(pszSrcWKT) > 0 + && pszDstWKT != NULL && strlen(pszDstWKT) > 0 + && !EQUAL(pszSrcWKT,pszDstWKT) ) + { + CPLString osSrcWKT = pszSrcWKT; + if (hSrcDS + && CSLFetchBoolean( papszOptions, "INSERT_CENTER_LONG", TRUE ) ) + osSrcWKT = InsertCenterLong( hSrcDS, osSrcWKT ); + + psInfo->pReprojectArg = + GDALCreateReprojectionTransformer( osSrcWKT.c_str(), pszDstWKT ); + if( psInfo->pReprojectArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + } + + return psInfo; +} + +/************************************************************************/ +/* GDALRefreshGenImgProjTransformer() */ +/************************************************************************/ + +void GDALRefreshGenImgProjTransformer(void* hTransformArg) +{ + GDALGenImgProjTransformInfo *psInfo = + static_cast( hTransformArg ); + + if (psInfo->pReprojectArg) + { + CPLXMLNode* psXML = GDALSerializeReprojectionTransformer(psInfo->pReprojectArg); + GDALDestroyReprojectionTransformer(psInfo->pReprojectArg); + psInfo->pReprojectArg = GDALDeserializeReprojectionTransformer(psXML); + CPLDestroyXMLNode(psXML); + } +} + +/************************************************************************/ +/* GDALCreateGenImgProjTransformer3() */ +/************************************************************************/ + +/** + * Create image to image transformer. + * + * This function creates a transformation object that maps from pixel/line + * coordinates on one image to pixel/line coordinates on another image. The + * images may potentially be georeferenced in different coordinate systems, + * and may used GCPs to map between their pixel/line coordinates and + * georeferenced coordinates (as opposed to the default assumption that their + * geotransform should be used). + * + * This transformer potentially performs three concatenated transformations. + * + * The first stage is from source image pixel/line coordinates to source + * image georeferenced coordinates, and may be done using the geotransform, + * or if not defined using a polynomial model derived from GCPs. If GCPs + * are used this stage is accomplished using GDALGCPTransform(). + * + * The second stage is to change projections from the source coordinate system + * to the destination coordinate system, assuming they differ. This is + * accomplished internally using GDALReprojectionTransform(). + * + * The third stage is converting from destination image georeferenced + * coordinates to destination image coordinates. This is done using the + * destination image geotransform, or if not available, using a polynomial + * model derived from GCPs. If GCPs are used this stage is accomplished using + * GDALGCPTransform(). This stage is skipped if hDstDS is NULL when the + * transformation is created. + * + * @param pszSrcWKT source WKT (or NULL). + * @param padfSrcGeoTransform source geotransform (or NULL). + * @param pszDstWKT destination WKT (or NULL). + * @param padfDstGeoTransform destination geotransform (or NULL). + * + * @return handle suitable for use GDALGenImgProjTransform(), and to be + * deallocated with GDALDestroyGenImgProjTransformer() or NULL on failure. + */ + +void * +GDALCreateGenImgProjTransformer3( const char *pszSrcWKT, + const double *padfSrcGeoTransform, + const char *pszDstWKT, + const double *padfDstGeoTransform ) + +{ + GDALGenImgProjTransformInfo *psInfo; + +/* -------------------------------------------------------------------- */ +/* Initialize the transform info. */ +/* -------------------------------------------------------------------- */ + psInfo = GDALCreateGenImgProjTransformerInternal(); + +/* -------------------------------------------------------------------- */ +/* Get forward and inverse geotransform for the source image. */ +/* -------------------------------------------------------------------- */ + if( padfSrcGeoTransform ) + { + memcpy( psInfo->adfSrcGeoTransform, padfSrcGeoTransform, + sizeof(psInfo->adfSrcGeoTransform) ); + if( !GDALInvGeoTransform( psInfo->adfSrcGeoTransform, + psInfo->adfSrcInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + } + else + { + psInfo->adfSrcGeoTransform[0] = 0.0; + psInfo->adfSrcGeoTransform[1] = 1.0; + psInfo->adfSrcGeoTransform[2] = 0.0; + psInfo->adfSrcGeoTransform[3] = 0.0; + psInfo->adfSrcGeoTransform[4] = 0.0; + psInfo->adfSrcGeoTransform[5] = 1.0; + memcpy( psInfo->adfSrcInvGeoTransform, psInfo->adfSrcGeoTransform, + sizeof(double) * 6 ); + } + +/* -------------------------------------------------------------------- */ +/* Setup reprojection. */ +/* -------------------------------------------------------------------- */ + if( pszSrcWKT != NULL && strlen(pszSrcWKT) > 0 + && pszDstWKT != NULL && strlen(pszDstWKT) > 0 + && !EQUAL(pszSrcWKT, pszDstWKT) ) + { + psInfo->pReprojectArg = + GDALCreateReprojectionTransformer( pszSrcWKT, pszDstWKT ); + if( psInfo->pReprojectArg == NULL ) + { + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Get forward and inverse geotransform for destination image. */ +/* If we have no destination matrix use a unit transform. */ +/* -------------------------------------------------------------------- */ + if( padfDstGeoTransform ) + { + memcpy( psInfo->adfDstGeoTransform, padfDstGeoTransform, + sizeof(psInfo->adfDstGeoTransform) ); + if( !GDALInvGeoTransform( psInfo->adfDstGeoTransform, + psInfo->adfDstInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + GDALDestroyGenImgProjTransformer( psInfo ); + return NULL; + } + } + else + { + psInfo->adfDstGeoTransform[0] = 0.0; + psInfo->adfDstGeoTransform[1] = 1.0; + psInfo->adfDstGeoTransform[2] = 0.0; + psInfo->adfDstGeoTransform[3] = 0.0; + psInfo->adfDstGeoTransform[4] = 0.0; + psInfo->adfDstGeoTransform[5] = 1.0; + memcpy( psInfo->adfDstInvGeoTransform, psInfo->adfDstGeoTransform, + sizeof(double) * 6 ); + } + + return psInfo; +} + +/************************************************************************/ +/* GDALSetGenImgProjTransformerDstGeoTransform() */ +/************************************************************************/ + +/** + * Set GenImgProj output geotransform. + * + * Normally the "destination geotransform", or transformation between + * georeferenced output coordinates and pixel/line coordinates on the + * destination file is extracted from the destination file by + * GDALCreateGenImgProjTransformer() and stored in the GenImgProj private + * info. However, sometimes it is inconvenient to have an output file + * handle with appropriate geotransform information when creating the + * transformation. For these cases, this function can be used to apply + * the destination geotransform. + * + * @param hTransformArg the handle to update. + * @param padfGeoTransform the destination geotransform to apply (six doubles). + */ + +void GDALSetGenImgProjTransformerDstGeoTransform( + void *hTransformArg, const double *padfGeoTransform ) + +{ + VALIDATE_POINTER0( hTransformArg, "GDALSetGenImgProjTransformerDstGeoTransform" ); + + GDALGenImgProjTransformInfo *psInfo = + static_cast( hTransformArg ); + + memcpy( psInfo->adfDstGeoTransform, padfGeoTransform, sizeof(double) * 6 ); + if( !GDALInvGeoTransform( psInfo->adfDstGeoTransform, + psInfo->adfDstInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + } +} + +/************************************************************************/ +/* GDALDestroyGenImgProjTransformer() */ +/************************************************************************/ + +/** + * GenImgProjTransformer deallocator. + * + * This function is used to deallocate the handle created with + * GDALCreateGenImgProjTransformer(). + * + * @param hTransformArg the handle to deallocate. + */ + +void GDALDestroyGenImgProjTransformer( void *hTransformArg ) + +{ + if( hTransformArg == NULL ) + return; + + GDALGenImgProjTransformInfo *psInfo = + (GDALGenImgProjTransformInfo *) hTransformArg; + + if( psInfo->pSrcGCPTransformArg != NULL ) + GDALDestroyGCPTransformer( psInfo->pSrcGCPTransformArg ); + + if( psInfo->pSrcTPSTransformArg != NULL ) + GDALDestroyTPSTransformer( psInfo->pSrcTPSTransformArg ); + + if( psInfo->pSrcRPCTransformArg != NULL ) + GDALDestroyRPCTransformer( psInfo->pSrcRPCTransformArg ); + + if( psInfo->pSrcGeoLocTransformArg != NULL ) + GDALDestroyGeoLocTransformer( psInfo->pSrcGeoLocTransformArg ); + + if( psInfo->pDstGCPTransformArg != NULL ) + GDALDestroyGCPTransformer( psInfo->pDstGCPTransformArg ); + + if( psInfo->pDstRPCTransformArg != NULL ) + GDALDestroyRPCTransformer( psInfo->pDstRPCTransformArg ); + + if( psInfo->pDstTPSTransformArg != NULL ) + GDALDestroyTPSTransformer( psInfo->pDstTPSTransformArg ); + + if( psInfo->pReprojectArg != NULL ) + GDALDestroyReprojectionTransformer( psInfo->pReprojectArg ); + + CPLFree( psInfo ); +} + +/************************************************************************/ +/* GDALGenImgProjTransform() */ +/************************************************************************/ + +/** + * Perform general image reprojection transformation. + * + * Actually performs the transformation setup in + * GDALCreateGenImgProjTransformer(). This function matches the signature + * required by the GDALTransformerFunc(), and more details on the arguments + * can be found in that topic. + */ + +#ifdef DEBUG_APPROX_TRANSFORMER +int countGDALGenImgProjTransform = 0; +#endif + +int GDALGenImgProjTransform( void *pTransformArg, int bDstToSrc, + int nPointCount, + double *padfX, double *padfY, double *padfZ, + int *panSuccess ) +{ + GDALGenImgProjTransformInfo *psInfo = + (GDALGenImgProjTransformInfo *) pTransformArg; + int i; + double *padfGeoTransform; + void *pGCPTransformArg; + void *pRPCTransformArg; + void *pTPSTransformArg; + void *pGeoLocTransformArg; + +#ifdef DEBUG_APPROX_TRANSFORMER + CPLAssert(nPointCount > 0); + countGDALGenImgProjTransform += nPointCount; +#endif + + for( i = 0; i < nPointCount; i++ ) + { + panSuccess[i] = ( padfX[i] != HUGE_VAL && padfY[i] != HUGE_VAL ); + } + +/* -------------------------------------------------------------------- */ +/* Convert from src (dst) pixel/line to src (dst) */ +/* georeferenced coordinates. */ +/* -------------------------------------------------------------------- */ + if( bDstToSrc ) + { + padfGeoTransform = psInfo->adfDstGeoTransform; + pGCPTransformArg = psInfo->pDstGCPTransformArg; + pRPCTransformArg = psInfo->pDstRPCTransformArg; + pTPSTransformArg = psInfo->pDstTPSTransformArg; + pGeoLocTransformArg = NULL; + } + else + { + padfGeoTransform = psInfo->adfSrcGeoTransform; + pGCPTransformArg = psInfo->pSrcGCPTransformArg; + pRPCTransformArg = psInfo->pSrcRPCTransformArg; + pTPSTransformArg = psInfo->pSrcTPSTransformArg; + pGeoLocTransformArg = psInfo->pSrcGeoLocTransformArg; + } + + if( pGCPTransformArg != NULL ) + { + if( !GDALGCPTransform( pGCPTransformArg, FALSE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else if( pTPSTransformArg != NULL ) + { + if( !GDALTPSTransform( pTPSTransformArg, FALSE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else if( pRPCTransformArg != NULL ) + { + if( !GDALRPCTransform( pRPCTransformArg, FALSE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else if( pGeoLocTransformArg != NULL ) + { + if( !GDALGeoLocTransform( pGeoLocTransformArg, FALSE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else + { + for( i = 0; i < nPointCount; i++ ) + { + double dfNewX, dfNewY; + + if( padfX[i] == HUGE_VAL || padfY[i] == HUGE_VAL ) + { + panSuccess[i] = FALSE; + continue; + } + + dfNewX = padfGeoTransform[0] + + padfX[i] * padfGeoTransform[1] + + padfY[i] * padfGeoTransform[2]; + dfNewY = padfGeoTransform[3] + + padfX[i] * padfGeoTransform[4] + + padfY[i] * padfGeoTransform[5]; + + padfX[i] = dfNewX; + padfY[i] = dfNewY; + } + } + +/* -------------------------------------------------------------------- */ +/* Reproject if needed. */ +/* -------------------------------------------------------------------- */ + if( psInfo->pReprojectArg ) + { + if( !GDALReprojectionTransform( psInfo->pReprojectArg, bDstToSrc, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Convert dst (src) georef coordinates back to pixel/line. */ +/* -------------------------------------------------------------------- */ + if( bDstToSrc ) + { + padfGeoTransform = psInfo->adfSrcInvGeoTransform; + pGCPTransformArg = psInfo->pSrcGCPTransformArg; + pRPCTransformArg = psInfo->pSrcRPCTransformArg; + pTPSTransformArg = psInfo->pSrcTPSTransformArg; + pGeoLocTransformArg = psInfo->pSrcGeoLocTransformArg; + } + else + { + padfGeoTransform = psInfo->adfDstInvGeoTransform; + pGCPTransformArg = psInfo->pDstGCPTransformArg; + pRPCTransformArg = psInfo->pDstRPCTransformArg; + pTPSTransformArg = psInfo->pDstTPSTransformArg; + pGeoLocTransformArg = NULL; + } + + if( pGCPTransformArg != NULL ) + { + if( !GDALGCPTransform( pGCPTransformArg, TRUE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else if( pTPSTransformArg != NULL ) + { + if( !GDALTPSTransform( pTPSTransformArg, TRUE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else if( pRPCTransformArg != NULL ) + { + if( !GDALRPCTransform( pRPCTransformArg, TRUE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else if( pGeoLocTransformArg != NULL ) + { + if( !GDALGeoLocTransform( pGeoLocTransformArg, TRUE, + nPointCount, padfX, padfY, padfZ, + panSuccess ) ) + return FALSE; + } + else + { + for( i = 0; i < nPointCount; i++ ) + { + double dfNewX, dfNewY; + + if( !panSuccess[i] ) + continue; + + dfNewX = padfGeoTransform[0] + + padfX[i] * padfGeoTransform[1] + + padfY[i] * padfGeoTransform[2]; + dfNewY = padfGeoTransform[3] + + padfX[i] * padfGeoTransform[4] + + padfY[i] * padfGeoTransform[5]; + + padfX[i] = dfNewX; + padfY[i] = dfNewY; + } + } + + return TRUE; +} + +/************************************************************************/ +/* GDALSerializeGenImgProjTransformer() */ +/************************************************************************/ + +static CPLXMLNode * +GDALSerializeGenImgProjTransformer( void *pTransformArg ) + +{ + char szWork[200]; + CPLXMLNode *psTree; + GDALGenImgProjTransformInfo *psInfo = + (GDALGenImgProjTransformInfo *) pTransformArg; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "GenImgProjTransformer" ); + +/* -------------------------------------------------------------------- */ +/* Handle GCP transformation. */ +/* -------------------------------------------------------------------- */ + if( psInfo->pSrcGCPTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "SrcGCPTransformer" ); + + psTransformer = GDALSerializeTransformer( GDALGCPTransform, + psInfo->pSrcGCPTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle TPS transformation. */ +/* -------------------------------------------------------------------- */ + else if( psInfo->pSrcTPSTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "SrcTPSTransformer" ); + + psTransformer = + GDALSerializeTransformer( NULL, psInfo->pSrcTPSTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle GeoLoc transformation. */ +/* -------------------------------------------------------------------- */ + else if( psInfo->pSrcGeoLocTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "SrcGeoLocTransformer" ); + + psTransformer = + GDALSerializeTransformer( NULL, psInfo->pSrcGeoLocTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle RPC transformation. */ +/* -------------------------------------------------------------------- */ + else if( psInfo->pSrcRPCTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "SrcRPCTransformer" ); + + psTransformer = + GDALSerializeTransformer( NULL, psInfo->pSrcRPCTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle source geotransforms. */ +/* -------------------------------------------------------------------- */ + else + { + CPLsprintf( szWork, "%.18g,%.18g,%.18g,%.18g,%.18g,%.18g", + psInfo->adfSrcGeoTransform[0], + psInfo->adfSrcGeoTransform[1], + psInfo->adfSrcGeoTransform[2], + psInfo->adfSrcGeoTransform[3], + psInfo->adfSrcGeoTransform[4], + psInfo->adfSrcGeoTransform[5] ); + CPLCreateXMLElementAndValue( psTree, "SrcGeoTransform", szWork ); + + CPLsprintf( szWork, "%.18g,%.18g,%.18g,%.18g,%.18g,%.18g", + psInfo->adfSrcInvGeoTransform[0], + psInfo->adfSrcInvGeoTransform[1], + psInfo->adfSrcInvGeoTransform[2], + psInfo->adfSrcInvGeoTransform[3], + psInfo->adfSrcInvGeoTransform[4], + psInfo->adfSrcInvGeoTransform[5] ); + CPLCreateXMLElementAndValue( psTree, "SrcInvGeoTransform", szWork ); + } + +/* -------------------------------------------------------------------- */ +/* Handle Dest GCP transformation. */ +/* -------------------------------------------------------------------- */ + if( psInfo->pDstGCPTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "DstGCPTransformer" ); + + psTransformer = GDALSerializeTransformer( GDALGCPTransform, + psInfo->pDstGCPTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle Dest TPS transformation. */ +/* -------------------------------------------------------------------- */ + else if( psInfo->pDstTPSTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "DstTPSTransformer" ); + + psTransformer = + GDALSerializeTransformer( NULL, psInfo->pDstTPSTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle Dest RPC transformation. */ +/* -------------------------------------------------------------------- */ + else if( psInfo->pDstRPCTransformArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "DstRPCTransformer" ); + + psTransformer = + GDALSerializeTransformer( NULL, psInfo->pDstRPCTransformArg); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + +/* -------------------------------------------------------------------- */ +/* Handle destination geotransforms. */ +/* -------------------------------------------------------------------- */ + else + { + CPLsprintf( szWork, "%.18g,%.18g,%.18g,%.18g,%.18g,%.18g", + psInfo->adfDstGeoTransform[0], + psInfo->adfDstGeoTransform[1], + psInfo->adfDstGeoTransform[2], + psInfo->adfDstGeoTransform[3], + psInfo->adfDstGeoTransform[4], + psInfo->adfDstGeoTransform[5] ); + CPLCreateXMLElementAndValue( psTree, "DstGeoTransform", szWork ); + + CPLsprintf( szWork, "%.18g,%.18g,%.18g,%.18g,%.18g,%.18g", + psInfo->adfDstInvGeoTransform[0], + psInfo->adfDstInvGeoTransform[1], + psInfo->adfDstInvGeoTransform[2], + psInfo->adfDstInvGeoTransform[3], + psInfo->adfDstInvGeoTransform[4], + psInfo->adfDstInvGeoTransform[5] ); + CPLCreateXMLElementAndValue( psTree, "DstInvGeoTransform", szWork ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a reprojection transformer? */ +/* -------------------------------------------------------------------- */ + if( psInfo->pReprojectArg != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "ReprojectTransformer" ); + + psTransformer = GDALSerializeTransformer( GDALReprojectionTransform, + psInfo->pReprojectArg ); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + } + + return psTree; +} + +/************************************************************************/ +/* GDALDeserializeGeoTransform() */ +/************************************************************************/ + +static void GDALDeserializeGeoTransform(const char* pszGT, + double adfGeoTransform[6]) +{ + CPLsscanf( pszGT, "%lf,%lf,%lf,%lf,%lf,%lf", + adfGeoTransform + 0, + adfGeoTransform + 1, + adfGeoTransform + 2, + adfGeoTransform + 3, + adfGeoTransform + 4, + adfGeoTransform + 5 ); +} + +/************************************************************************/ +/* GDALDeserializeGenImgProjTransformer() */ +/************************************************************************/ + +void *GDALDeserializeGenImgProjTransformer( CPLXMLNode *psTree ) + +{ + GDALGenImgProjTransformInfo *psInfo; + CPLXMLNode *psSubtree; + +/* -------------------------------------------------------------------- */ +/* Initialize the transform info. */ +/* -------------------------------------------------------------------- */ + psInfo = GDALCreateGenImgProjTransformerInternal(); + +/* -------------------------------------------------------------------- */ +/* SrcGeotransform */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psTree, "SrcGeoTransform" ) != NULL ) + { + GDALDeserializeGeoTransform( CPLGetXMLValue( psTree, "SrcGeoTransform", "" ), + psInfo->adfSrcGeoTransform ); + + if( CPLGetXMLNode( psTree, "SrcInvGeoTransform" ) != NULL ) + { + GDALDeserializeGeoTransform( CPLGetXMLValue( psTree, "SrcInvGeoTransform", "" ), + psInfo->adfSrcInvGeoTransform ); + } + else + { + if( !GDALInvGeoTransform( psInfo->adfSrcGeoTransform, + psInfo->adfSrcInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Src GCP Transform */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "SrcGCPTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pSrcGCPTransformArg = + GDALDeserializeGCPTransformer( psSubtree->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* Src TPS Transform */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "SrcTPSTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pSrcTPSTransformArg = + GDALDeserializeTPSTransformer( psSubtree->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* Src GeoLoc Transform */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "SrcGeoLocTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pSrcGeoLocTransformArg = + GDALDeserializeGeoLocTransformer( psSubtree->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* Src RPC Transform */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "SrcRPCTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pSrcRPCTransformArg = + GDALDeserializeRPCTransformer( psSubtree->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* Dst TPS Transform */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "DstTPSTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pDstTPSTransformArg = + GDALDeserializeTPSTransformer( psSubtree->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* Dst RPC Transform */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "DstRPCTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pDstRPCTransformArg = + GDALDeserializeRPCTransformer( psSubtree->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* DstGeotransform */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psTree, "DstGeoTransform" ) != NULL ) + { + GDALDeserializeGeoTransform( CPLGetXMLValue( psTree, "DstGeoTransform", "" ), + psInfo->adfDstGeoTransform ); + + if( CPLGetXMLNode( psTree, "DstInvGeoTransform" ) != NULL ) + { + GDALDeserializeGeoTransform( CPLGetXMLValue( psTree, "DstInvGeoTransform", "" ), + psInfo->adfDstInvGeoTransform ); + } + else + { + if( !GDALInvGeoTransform( psInfo->adfDstGeoTransform, + psInfo->adfDstInvGeoTransform ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform"); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Reproject transformer */ +/* -------------------------------------------------------------------- */ + psSubtree = CPLGetXMLNode( psTree, "ReprojectTransformer" ); + if( psSubtree != NULL && psSubtree->psChild != NULL ) + { + psInfo->pReprojectArg = + GDALDeserializeReprojectionTransformer( psSubtree->psChild ); + } + + return psInfo; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALReprojectionTransformer */ +/* ==================================================================== */ +/************************************************************************/ + +typedef struct { + GDALTransformerInfo sTI; + + OGRCoordinateTransformation *poForwardTransform; + OGRCoordinateTransformation *poReverseTransform; +} GDALReprojectionTransformInfo; + +/************************************************************************/ +/* GDALCreateReprojectionTransformer() */ +/************************************************************************/ + +/** + * Create reprojection transformer. + * + * Creates a callback data structure suitable for use with + * GDALReprojectionTransformation() to represent a transformation from + * one geographic or projected coordinate system to another. On input + * the coordinate systems are described in OpenGIS WKT format. + * + * Internally the OGRCoordinateTransformation object is used to implement + * the reprojection. + * + * @param pszSrcWKT the coordinate system for the source coordinate system. + * @param pszDstWKT the coordinate system for the destination coordinate + * system. + * + * @return Handle for use with GDALReprojectionTransform(), or NULL if the + * system fails to initialize the reprojection. + **/ + +void *GDALCreateReprojectionTransformer( const char *pszSrcWKT, + const char *pszDstWKT ) + +{ + OGRSpatialReference oSrcSRS, oDstSRS; + OGRCoordinateTransformation *poForwardTransform; + +/* -------------------------------------------------------------------- */ +/* Ingest the SRS definitions. */ +/* -------------------------------------------------------------------- */ + if( oSrcSRS.importFromWkt( (char **) &pszSrcWKT ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to import coordinate system `%s'.", + pszSrcWKT ); + return NULL; + } + if( oDstSRS.importFromWkt( (char **) &pszDstWKT ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to import coordinate system `%s'.", + pszSrcWKT ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Build the forward coordinate transformation. */ +/* -------------------------------------------------------------------- */ + poForwardTransform = OGRCreateCoordinateTransformation(&oSrcSRS,&oDstSRS); + + if( poForwardTransform == NULL ) + // OGRCreateCoordinateTransformation() will report errors on its own. + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a structure to hold the transform info, and also */ +/* build reverse transform. We assume that if the forward */ +/* transform can be created, then so can the reverse one. */ +/* -------------------------------------------------------------------- */ + GDALReprojectionTransformInfo *psInfo; + + psInfo = (GDALReprojectionTransformInfo *) + CPLCalloc(sizeof(GDALReprojectionTransformInfo),1); + + psInfo->poForwardTransform = poForwardTransform; + psInfo->poReverseTransform = + OGRCreateCoordinateTransformation(&oDstSRS,&oSrcSRS); + + memcpy( psInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE) ); + psInfo->sTI.pszClassName = "GDALReprojectionTransformer"; + psInfo->sTI.pfnTransform = GDALReprojectionTransform; + psInfo->sTI.pfnCleanup = GDALDestroyReprojectionTransformer; + psInfo->sTI.pfnSerialize = GDALSerializeReprojectionTransformer; + + return psInfo; +} + +/************************************************************************/ +/* GDALDestroyReprojectionTransformer() */ +/************************************************************************/ + +/** + * Destroy reprojection transformation. + * + * @param pTransformArg the transformation handle returned by + * GDALCreateReprojectionTransformer(). + */ + +void GDALDestroyReprojectionTransformer( void *pTransformArg ) + +{ + if( pTransformArg == NULL ) + return; + + GDALReprojectionTransformInfo *psInfo = + (GDALReprojectionTransformInfo *) pTransformArg; + + if( psInfo->poForwardTransform ) + delete psInfo->poForwardTransform; + + if( psInfo->poReverseTransform ) + delete psInfo->poReverseTransform; + + CPLFree( psInfo ); +} + +/************************************************************************/ +/* GDALReprojectionTransform() */ +/************************************************************************/ + +/** + * Perform reprojection transformation. + * + * Actually performs the reprojection transformation described in + * GDALCreateReprojectionTransformer(). This function matches the + * GDALTransformerFunc() signature. Details of the arguments are described + * there. + */ + +int GDALReprojectionTransform( void *pTransformArg, int bDstToSrc, + int nPointCount, + double *padfX, double *padfY, double *padfZ, + int *panSuccess ) + +{ + GDALReprojectionTransformInfo *psInfo = + (GDALReprojectionTransformInfo *) pTransformArg; + int bSuccess; + + if( bDstToSrc ) + bSuccess = psInfo->poReverseTransform->TransformEx( + nPointCount, padfX, padfY, padfZ, panSuccess ); + else + bSuccess = psInfo->poForwardTransform->TransformEx( + nPointCount, padfX, padfY, padfZ, panSuccess ); + + return bSuccess; +} + +/************************************************************************/ +/* GDALSerializeReprojectionTransformer() */ +/************************************************************************/ + +static CPLXMLNode * +GDALSerializeReprojectionTransformer( void *pTransformArg ) + +{ + CPLXMLNode *psTree; + GDALReprojectionTransformInfo *psInfo = + (GDALReprojectionTransformInfo *) pTransformArg; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "ReprojectionTransformer" ); + +/* -------------------------------------------------------------------- */ +/* Handle SourceCS. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference *poSRS; + char *pszWKT = NULL; + + poSRS = psInfo->poForwardTransform->GetSourceCS(); + poSRS->exportToWkt( &pszWKT ); + CPLCreateXMLElementAndValue( psTree, "SourceSRS", pszWKT ); + CPLFree( pszWKT ); + +/* -------------------------------------------------------------------- */ +/* Handle DestinationCS. */ +/* -------------------------------------------------------------------- */ + poSRS = psInfo->poForwardTransform->GetTargetCS(); + poSRS->exportToWkt( &pszWKT ); + CPLCreateXMLElementAndValue( psTree, "TargetSRS", pszWKT ); + CPLFree( pszWKT ); + + return psTree; +} + +/************************************************************************/ +/* GDALDeserializeReprojectionTransformer() */ +/************************************************************************/ + +static void * +GDALDeserializeReprojectionTransformer( CPLXMLNode *psTree ) + +{ + const char *pszSourceSRS = CPLGetXMLValue( psTree, "SourceSRS", NULL ); + const char *pszTargetSRS= CPLGetXMLValue( psTree, "TargetSRS", NULL ); + char *pszSourceWKT = NULL, *pszTargetWKT = NULL; + void *pResult = NULL; + + if( pszSourceSRS != NULL ) + { + OGRSpatialReference oSRS; + + if( oSRS.SetFromUserInput( pszSourceSRS ) == OGRERR_NONE ) + oSRS.exportToWkt( &pszSourceWKT ); + } + + if( pszTargetSRS != NULL ) + { + OGRSpatialReference oSRS; + + if( oSRS.SetFromUserInput( pszTargetSRS ) == OGRERR_NONE ) + oSRS.exportToWkt( &pszTargetWKT ); + } + + if( pszSourceWKT != NULL && pszTargetWKT != NULL ) + { + pResult = GDALCreateReprojectionTransformer( pszSourceWKT, + pszTargetWKT ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "ReprojectionTransformer definition missing either\n" + "SourceSRS or TargetSRS definition." ); + } + + CPLFree( pszSourceWKT ); + CPLFree( pszTargetWKT ); + + return pResult; +} + +/************************************************************************/ +/* ==================================================================== */ +/* Approximate transformer. */ +/* ==================================================================== */ +/************************************************************************/ + +typedef struct +{ + GDALTransformerInfo sTI; + + GDALTransformerFunc pfnBaseTransformer; + void *pBaseCBData; + double dfMaxError; + + int bOwnSubtransformer; +} ApproxTransformInfo; + +/************************************************************************/ +/* GDALCreateSimilarApproxTransformer() */ +/************************************************************************/ + +static +void* GDALCreateSimilarApproxTransformer( void *hTransformArg, double dfSrcRatioX, double dfSrcRatioY ) +{ + VALIDATE_POINTER1( hTransformArg, "GDALCreateSimilarApproxTransformer", NULL ); + + ApproxTransformInfo *psInfo = + (ApproxTransformInfo *) hTransformArg; + + ApproxTransformInfo *psClonedInfo = (ApproxTransformInfo *) + CPLMalloc(sizeof(ApproxTransformInfo)); + + memcpy(psClonedInfo, psInfo, sizeof(ApproxTransformInfo)); + if( psClonedInfo->pBaseCBData ) + { + psClonedInfo->pBaseCBData = GDALCreateSimilarTransformer( psInfo->pBaseCBData, + dfSrcRatioX, + dfSrcRatioY ); + if( psClonedInfo->pBaseCBData == NULL ) + { + CPLFree(psClonedInfo); + return NULL; + } + } + psClonedInfo->bOwnSubtransformer = TRUE; + + return psClonedInfo; +} + +/************************************************************************/ +/* GDALSerializeApproxTransformer() */ +/************************************************************************/ + +static CPLXMLNode * +GDALSerializeApproxTransformer( void *pTransformArg ) + +{ + CPLXMLNode *psTree; + ApproxTransformInfo *psInfo = (ApproxTransformInfo *) pTransformArg; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "ApproxTransformer" ); + +/* -------------------------------------------------------------------- */ +/* Attach max error. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( psTree, "MaxError", + CPLString().Printf("%g",psInfo->dfMaxError) ); + +/* -------------------------------------------------------------------- */ +/* Capture underlying transformer. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "BaseTransformer" ); + + psTransformer = GDALSerializeTransformer( psInfo->pfnBaseTransformer, + psInfo->pBaseCBData ); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + + return psTree; +} + +/************************************************************************/ +/* GDALCreateApproxTransformer() */ +/************************************************************************/ + +/** + * Create an approximating transformer. + * + * This function creates a context for an approximated transformer. Basically + * a high precision transformer is supplied as input and internally linear + * approximations are computed to generate results to within a defined + * precision. + * + * The approximation is actually done at the point where GDALApproxTransform() + * calls are made, and depend on the assumption that the roughly linear. The + * first and last point passed in must be the extreme values and the + * intermediate values should describe a curve between the end points. The + * approximator transforms and center using the approximate transformer, and + * then compares the true middle transformed value to a linear approximation + * based on the end points. If the error is within the supplied threshold + * then the end points are used to linearly approximate all the values + * otherwise the inputs points are split into two smaller sets, and the + * function recursively called till a sufficiently small set of points if found + * that the linear approximation is OK, or that all the points are exactly + * computed. + * + * This function is very suitable for approximating transformation results + * from output pixel/line space to input coordinates for warpers that operate + * on one input scanline at a time. Care should be taken using it in other + * circumstances as little internal validation is done, in order to keep things + * fast. + * + * @param pfnBaseTransformer the high precision transformer which should be + * approximated. + * @param pBaseTransformArg the callback argument for the high precision + * transformer. + * @param dfMaxError the maximum cartesian error in the "output" space that + * is to be accepted in the linear approximation. + * + * @return callback pointer suitable for use with GDALApproxTransform(). It + * should be deallocated with GDALDestroyApproxTransformer(). + */ + +void *GDALCreateApproxTransformer( GDALTransformerFunc pfnBaseTransformer, + void *pBaseTransformArg, double dfMaxError) + +{ + ApproxTransformInfo *psATInfo; + + psATInfo = (ApproxTransformInfo*) CPLMalloc(sizeof(ApproxTransformInfo)); + psATInfo->pfnBaseTransformer = pfnBaseTransformer; + psATInfo->pBaseCBData = pBaseTransformArg; + psATInfo->dfMaxError = dfMaxError; + psATInfo->bOwnSubtransformer = FALSE; + + memcpy( psATInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE) ); + psATInfo->sTI.pszClassName = "GDALApproxTransformer"; + psATInfo->sTI.pfnTransform = GDALApproxTransform; + psATInfo->sTI.pfnCleanup = GDALDestroyApproxTransformer; + psATInfo->sTI.pfnSerialize = GDALSerializeApproxTransformer; + psATInfo->sTI.pfnCreateSimilar = GDALCreateSimilarApproxTransformer; + + return psATInfo; +} + +/************************************************************************/ +/* GDALApproxTransformerOwnsSubtransformer() */ +/************************************************************************/ + +void GDALApproxTransformerOwnsSubtransformer( void *pCBData, int bOwnFlag ) + +{ + ApproxTransformInfo *psATInfo = (ApproxTransformInfo *) pCBData; + + psATInfo->bOwnSubtransformer = bOwnFlag; +} + +/************************************************************************/ +/* GDALDestroyApproxTransformer() */ +/************************************************************************/ + +/** + * Cleanup approximate transformer. + * + * Deallocates the resources allocated by GDALCreateApproxTransformer(). + * + * @param pCBData callback data originally returned by + * GDALCreateApproxTransformer(). + */ + +void GDALDestroyApproxTransformer( void * pCBData ) + +{ + if( pCBData == NULL) + return; + + ApproxTransformInfo *psATInfo = (ApproxTransformInfo *) pCBData; + + if( psATInfo->bOwnSubtransformer ) + GDALDestroyTransformer( psATInfo->pBaseCBData ); + + CPLFree( pCBData ); +} + +/************************************************************************/ +/* GDALApproxTransformInternal() */ +/************************************************************************/ + +static int GDALApproxTransformInternal( void *pCBData, int bDstToSrc, int nPoints, + double *x, double *y, double *z, int *panSuccess, + const double xSMETransformed[3], /* SME = Start, Middle, End */ + const double ySMETransformed[3], + const double zSMETransformed[3]) +{ + ApproxTransformInfo *psATInfo = (ApproxTransformInfo *) pCBData; + double dfDeltaX, dfDeltaY, dfError, dfDist, dfDeltaZ; + int nMiddle, i, bSuccess; + + nMiddle = (nPoints-1)/2; + +#ifdef notdef_sanify_check + { + double x2[3], y2[3], z2[3]; + int anSuccess2[3]; + x2[0] = x[0]; + y2[0] = y[0]; + z2[0] = z[0]; + x2[1] = x[nMiddle]; + y2[1] = y[nMiddle]; + z2[1] = z[nMiddle]; + x2[2] = x[nPoints-1]; + y2[2] = y[nPoints-1]; + z2[2] = z[nPoints-1]; + + bSuccess = + psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, 3, + x2, y2, z2, anSuccess2 ); + CPLAssert(bSuccess); + CPLAssert(anSuccess2[0]); + CPLAssert(anSuccess2[1]); + CPLAssert(anSuccess2[2]); + CPLAssert(x2[0] == xSMETransformed[0]); + CPLAssert(y2[0] == ySMETransformed[0]); + CPLAssert(z2[0] == zSMETransformed[0]); + CPLAssert(x2[1] == xSMETransformed[1]); + CPLAssert(y2[1] == ySMETransformed[1]); + CPLAssert(z2[1] == zSMETransformed[1]); + CPLAssert(x2[2] == xSMETransformed[2]); + CPLAssert(y2[2] == ySMETransformed[2]); + CPLAssert(z2[2] == zSMETransformed[2]); + } +#endif + +#ifdef DEBUG_APPROX_TRANSFORMER + fprintf(stderr, "start (%.3f,%.3f) -> (%.3f,%.3f)\n", + x[0], y[0], xSMETransformed[0], ySMETransformed[0]); + fprintf(stderr, "middle (%.3f,%.3f) -> (%.3f,%.3f)\n", + x[nMiddle], y[nMiddle], xSMETransformed[1], ySMETransformed[1]); + fprintf(stderr, "end (%.3f,%.3f) -> (%.3f,%.3f)\n", + x[nPoints-1], y[nPoints-1], xSMETransformed[2], ySMETransformed[2]); +#endif + +/* -------------------------------------------------------------------- */ +/* Is the error at the middle acceptable relative to an */ +/* interpolation of the middle position? */ +/* -------------------------------------------------------------------- */ + dfDeltaX = (xSMETransformed[2] - xSMETransformed[0]) / (x[nPoints-1] - x[0]); + dfDeltaY = (ySMETransformed[2] - ySMETransformed[0]) / (x[nPoints-1] - x[0]); + dfDeltaZ = (zSMETransformed[2] - zSMETransformed[0]) / (x[nPoints-1] - x[0]); + + dfError = fabs((xSMETransformed[0] + dfDeltaX * (x[nMiddle] - x[0])) - xSMETransformed[1]) + + fabs((ySMETransformed[0] + dfDeltaY * (x[nMiddle] - x[0])) - ySMETransformed[1]); + + if( dfError > psATInfo->dfMaxError ) + { +#ifdef notdef + CPLDebug( "GDAL", "ApproxTransformer - " + "error %g over threshold %g, subdivide %d points.", + dfError, psATInfo->dfMaxError, nPoints ); +#endif + + double xMiddle[3], yMiddle[3], zMiddle[3]; + int anSuccess2[3]; + xMiddle[0] = x[ (nMiddle - 1) / 2 ]; + yMiddle[0] = y[ (nMiddle - 1) / 2 ]; + zMiddle[0] = z[ (nMiddle - 1) / 2 ]; + xMiddle[1] = x[ nMiddle - 1 ]; + yMiddle[1] = y[ nMiddle - 1 ]; + zMiddle[1] = z[ nMiddle - 1 ]; + xMiddle[2] = x[ nMiddle + (nPoints - nMiddle - 1) / 2 ]; + yMiddle[2] = y[ nMiddle + (nPoints - nMiddle - 1) / 2 ]; + zMiddle[2] = z[ nMiddle + (nPoints - nMiddle - 1) / 2 ]; + + int bUseBaseTransformForHalf1 = ( nMiddle <= 5 || + y[0] != y[nMiddle-1] || + y[0] != y[(nMiddle - 1) / 2] || + x[0] == x[nMiddle-1] || + x[0] == x[(nMiddle - 1) / 2] ); + int bUseBaseTransformForHalf2 = ( nPoints - nMiddle <= 5 || + y[nMiddle] != y[nPoints-1] || + y[nMiddle] != y[nMiddle + (nPoints - nMiddle - 1) / 2] || + x[nMiddle] == x[nPoints-1] || + x[nMiddle] == x[nMiddle + (nPoints - nMiddle - 1) / 2] ); + + if( !bUseBaseTransformForHalf1 && !bUseBaseTransformForHalf2 ) + bSuccess = + psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, 3, + xMiddle, yMiddle, zMiddle, anSuccess2 ); + else if( !bUseBaseTransformForHalf1 ) + { + bSuccess = + psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, 2, + xMiddle, yMiddle, zMiddle, anSuccess2 ); + anSuccess2[2] = TRUE; + } + else if( !bUseBaseTransformForHalf2 ) + { + bSuccess = + psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, 1, + xMiddle + 2, yMiddle + 2, zMiddle + 2, + anSuccess2 + 2 ); + anSuccess2[0] = TRUE; + anSuccess2[1] = TRUE; + } + else + bSuccess = FALSE; + if( !bSuccess || !anSuccess2[0] || !anSuccess2[1] || !anSuccess2[2] ) + { + bSuccess = psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, + nMiddle - 1, + x + 1, y + 1, z + 1, + panSuccess + 1 ); + bSuccess &= psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, + nPoints - nMiddle - 2, + x + nMiddle + 1, + y + nMiddle + 1, + z + nMiddle + 1, + panSuccess + nMiddle + 1 ); + + x[0] = xSMETransformed[0]; + y[0] = ySMETransformed[0]; + z[0] = zSMETransformed[0]; + panSuccess[0] = TRUE; + x[nMiddle] = xSMETransformed[1]; + y[nMiddle] = ySMETransformed[1]; + z[nMiddle] = zSMETransformed[1]; + panSuccess[nMiddle] = TRUE; + x[nPoints-1] = xSMETransformed[2]; + y[nPoints-1] = ySMETransformed[2]; + z[nPoints-1] = zSMETransformed[2]; + panSuccess[nPoints-1] = TRUE; + return bSuccess; + } + + double x2[3], y2[3], z2[3]; + if( !bUseBaseTransformForHalf1 ) + { + x2[0] = xSMETransformed[0]; + y2[0] = ySMETransformed[0]; + z2[0] = zSMETransformed[0]; + x2[1] = xMiddle[0]; + y2[1] = yMiddle[0]; + z2[1] = zMiddle[0]; + x2[2] = xMiddle[1]; + y2[2] = yMiddle[1]; + z2[2] = zMiddle[1]; + + bSuccess = + GDALApproxTransformInternal( psATInfo, bDstToSrc, nMiddle, + x, y, z, panSuccess, + x2, y2, z2); + + } + else + { + bSuccess = psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, + nMiddle - 1, x + 1, y + 1, z + 1, + panSuccess + 1 ); + x[0] = xSMETransformed[0]; + y[0] = ySMETransformed[0]; + z[0] = zSMETransformed[0]; + panSuccess[0] = TRUE; + } + + if( !bSuccess ) + return FALSE; + + if( !bUseBaseTransformForHalf2 ) + { + x2[0] = xSMETransformed[1]; + y2[0] = ySMETransformed[1]; + z2[0] = zSMETransformed[1]; + x2[1] = xMiddle[2]; + y2[1] = yMiddle[2]; + z2[1] = zMiddle[2]; + x2[2] = xSMETransformed[2]; + y2[2] = ySMETransformed[2]; + z2[2] = zSMETransformed[2]; + + bSuccess = + GDALApproxTransformInternal( psATInfo, bDstToSrc, nPoints - nMiddle, + x+nMiddle, y+nMiddle, z+nMiddle, + panSuccess+nMiddle, + x2, y2, z2); + } + else + { + bSuccess = psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, + nPoints - nMiddle - 2, + x+nMiddle + 1, y+nMiddle+1, z+nMiddle+1, + panSuccess+nMiddle+1 ); + + x[nMiddle] = xSMETransformed[1]; + y[nMiddle] = ySMETransformed[1]; + z[nMiddle] = zSMETransformed[1]; + panSuccess[nMiddle] = TRUE; + x[nPoints-1] = xSMETransformed[2]; + y[nPoints-1] = ySMETransformed[2]; + z[nPoints-1] = zSMETransformed[2]; + panSuccess[nPoints-1] = TRUE; + } + + if( !bSuccess ) + return FALSE; + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Error is OK since this is just used to compute output bounds */ +/* of newly created file for gdalwarper. So just use affine */ +/* approximation of the reverse transform. Eventually we */ +/* should implement iterative searching to find a result within */ +/* our error threshold. */ +/* NOTE: the above comment is not true: gdalwarp uses approximator */ +/* also to compute the source pixel of each target pixel. */ +/* -------------------------------------------------------------------- */ + for( i = nPoints-1; i >= 0; i-- ) + { +#ifdef check_error + double xtemp = x[i], ytemp = y[i], ztemp = z[i]; + double x_ori = xtemp, y_ori = ytemp; + int btemp; + psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, + 1, &xtemp, &ytemp, &ztemp, &btemp); +#endif + dfDist = (x[i] - x[0]); + x[i] = xSMETransformed[0] + dfDeltaX * dfDist; + y[i] = ySMETransformed[0] + dfDeltaY * dfDist; + z[i] = zSMETransformed[0] + dfDeltaZ * dfDist; +#ifdef check_error + dfError = fabs(x[i] - xtemp) + fabs(y[i] - ytemp); + if( dfError > 4 /*10 * psATInfo->dfMaxError*/ ) + { + printf("Error = %f on (%f, %f)\n", dfError, x_ori, y_ori); + } +#endif + panSuccess[i] = TRUE; + } + + return TRUE; +} + +/************************************************************************/ +/* GDALApproxTransform() */ +/************************************************************************/ + +/** + * Perform approximate transformation. + * + * Actually performs the approximate transformation described in + * GDALCreateApproxTransformer(). This function matches the + * GDALTransformerFunc() signature. Details of the arguments are described + * there. + */ + +int GDALApproxTransform( void *pCBData, int bDstToSrc, int nPoints, + double *x, double *y, double *z, int *panSuccess ) + +{ + ApproxTransformInfo *psATInfo = (ApproxTransformInfo *) pCBData; + double x2[3], y2[3], z2[3]; + int nMiddle, anSuccess2[3], bSuccess; + + nMiddle = (nPoints-1)/2; + +/* -------------------------------------------------------------------- */ +/* Bail if our preconditions are not met, or if error is not */ +/* acceptable. */ +/* -------------------------------------------------------------------- */ + int bRet = FALSE; + if( y[0] != y[nPoints-1] || y[0] != y[nMiddle] + || x[0] == x[nPoints-1] || x[0] == x[nMiddle] + || psATInfo->dfMaxError == 0.0 || nPoints <= 5 ) + { + bRet = psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, + nPoints, x, y, z, panSuccess ); + goto end; + } + +/* -------------------------------------------------------------------- */ +/* Transform first, last and middle point. */ +/* -------------------------------------------------------------------- */ + x2[0] = x[0]; + y2[0] = y[0]; + z2[0] = z[0]; + x2[1] = x[nMiddle]; + y2[1] = y[nMiddle]; + z2[1] = z[nMiddle]; + x2[2] = x[nPoints-1]; + y2[2] = y[nPoints-1]; + z2[2] = z[nPoints-1]; + + bSuccess = + psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, 3, + x2, y2, z2, anSuccess2 ); + if( !bSuccess || !anSuccess2[0] || !anSuccess2[1] || !anSuccess2[2] ) + { + bRet = psATInfo->pfnBaseTransformer( psATInfo->pBaseCBData, bDstToSrc, + nPoints, x, y, z, panSuccess ); + goto end; + } + + bRet = GDALApproxTransformInternal( pCBData, bDstToSrc, nPoints, + x, y, z, panSuccess, + x2, + y2, + z2 ); + +end: +#ifdef DEBUG_APPROX_TRANSFORMER + for(int i=0;ipsChild != NULL ) + { + GDALDeserializeTransformer( psContainer->psChild, + &pfnBaseTransform, + &pBaseCBData ); + + } + + if( pfnBaseTransform == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot get base transform for approx transformer." ); + return NULL; + } + else + { + void *pApproxCBData = GDALCreateApproxTransformer( pfnBaseTransform, + pBaseCBData, + dfMaxError ); + GDALApproxTransformerOwnsSubtransformer( pApproxCBData, TRUE ); + + return pApproxCBData; + } +} + +/************************************************************************/ +/* GDALApplyGeoTransform() */ +/************************************************************************/ + +/** + * Apply GeoTransform to x/y coordinate. + * + * Applies the following computation, converting a (pixel,line) coordinate + * into a georeferenced (geo_x,geo_y) location. + * + * *pdfGeoX = padfGeoTransform[0] + dfPixel * padfGeoTransform[1] + * + dfLine * padfGeoTransform[2]; + * *pdfGeoY = padfGeoTransform[3] + dfPixel * padfGeoTransform[4] + * + dfLine * padfGeoTransform[5]; + * + * @param padfGeoTransform Six coefficient GeoTransform to apply. + * @param dfPixel Input pixel position. + * @param dfLine Input line position. + * @param pdfGeoX output location where geo_x (easting/longitude) location is placed. + * @param pdfGeoY output location where geo_y (northing/latitude) location is placed. + */ + +void CPL_STDCALL GDALApplyGeoTransform( double *padfGeoTransform, + double dfPixel, double dfLine, + double *pdfGeoX, double *pdfGeoY ) +{ + *pdfGeoX = padfGeoTransform[0] + dfPixel * padfGeoTransform[1] + + dfLine * padfGeoTransform[2]; + *pdfGeoY = padfGeoTransform[3] + dfPixel * padfGeoTransform[4] + + dfLine * padfGeoTransform[5]; +} + +/************************************************************************/ +/* GDALInvGeoTransform() */ +/************************************************************************/ + +/** + * Invert Geotransform. + * + * This function will invert a standard 3x2 set of GeoTransform coefficients. + * This converts the equation from being pixel to geo to being geo to pixel. + * + * @param gt_in Input geotransform (six doubles - unaltered). + * @param gt_out Output geotransform (six doubles - updated). + * + * @return TRUE on success or FALSE if the equation is uninvertable. + */ + +int CPL_STDCALL GDALInvGeoTransform( double *gt_in, double *gt_out ) + +{ + double det, inv_det; + + /* Special case - no rotation - to avoid computing determinate */ + /* and potential precision issues. */ + if( gt_in[2] == 0.0 && gt_in[4] == 0.0 && + gt_in[1] != 0.0 && gt_in[5] != 0.0 ) + { + /*X = gt_in[0] + x * gt_in[1] + Y = gt_in[3] + y * gt_in[5] + --> + x = -gt_in[0] / gt_in[1] + (1 / gt_in[1]) * X + y = -gt_in[3] / gt_in[5] + (1 / gt_in[5]) * Y + */ + gt_out[0] = -gt_in[0] / gt_in[1]; + gt_out[1] = 1.0 / gt_in[1]; + gt_out[2] = 0.0; + gt_out[3] = -gt_in[3] / gt_in[5]; + gt_out[4] = 0.0; + gt_out[5] = 1.0 / gt_in[5]; + return 1; + } + + /* we assume a 3rd row that is [1 0 0] */ + + /* Compute determinate */ + + det = gt_in[1] * gt_in[5] - gt_in[2] * gt_in[4]; + + if( fabs(det) < 0.000000000000001 ) + return 0; + + inv_det = 1.0 / det; + + /* compute adjoint, and devide by determinate */ + + gt_out[1] = gt_in[5] * inv_det; + gt_out[4] = -gt_in[4] * inv_det; + + gt_out[2] = -gt_in[2] * inv_det; + gt_out[5] = gt_in[1] * inv_det; + + gt_out[0] = ( gt_in[2] * gt_in[3] - gt_in[0] * gt_in[5]) * inv_det; + gt_out[3] = (-gt_in[1] * gt_in[3] + gt_in[0] * gt_in[4]) * inv_det; + + return 1; +} + +/************************************************************************/ +/* GDALSerializeTransformer() */ +/************************************************************************/ + +CPLXMLNode *GDALSerializeTransformer( CPL_UNUSED GDALTransformerFunc pfnFunc, + void *pTransformArg ) +{ + VALIDATE_POINTER1( pTransformArg, "GDALSerializeTransformer", NULL ); + + GDALTransformerInfo *psInfo = static_cast(pTransformArg); + + if( psInfo == NULL || + memcmp(psInfo->abySignature, GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE)) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to serialize non-GTI2 transformer." ); + return NULL; + } + else if ( psInfo->pfnSerialize == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No serialization function available for this transformer." ); + return NULL; + } + else + { + return psInfo->pfnSerialize( pTransformArg ); + } +} + +/************************************************************************/ +/* GDALRegisterTransformDeserializer() */ +/************************************************************************/ + +static CPLList* psListDeserializer = NULL; +static CPLMutex* hDeserializerMutex = NULL; + +typedef struct +{ + char* pszTransformName; + GDALTransformerFunc pfnTransformerFunc; + GDALTransformDeserializeFunc pfnDeserializeFunc; +} TransformDeserializerInfo; + +void* GDALRegisterTransformDeserializer(const char* pszTransformName, + GDALTransformerFunc pfnTransformerFunc, + GDALTransformDeserializeFunc pfnDeserializeFunc) +{ + TransformDeserializerInfo* psInfo = + (TransformDeserializerInfo*)CPLMalloc(sizeof(TransformDeserializerInfo)); + psInfo->pszTransformName = CPLStrdup(pszTransformName); + psInfo->pfnTransformerFunc = pfnTransformerFunc; + psInfo->pfnDeserializeFunc = pfnDeserializeFunc; + + CPLMutexHolderD(&hDeserializerMutex); + psListDeserializer = CPLListInsert(psListDeserializer, psInfo, 0); + + return psInfo; +} + +/************************************************************************/ +/* GDALUnregisterTransformDeserializer() */ +/************************************************************************/ + +void GDALUnregisterTransformDeserializer(void* pData) +{ + CPLMutexHolderD(&hDeserializerMutex); + CPLList* psList = psListDeserializer; + CPLList* psLast = NULL; + while(psList) + { + if (psList->pData == pData) + { + TransformDeserializerInfo* psInfo = + (TransformDeserializerInfo*)pData; + CPLFree(psInfo->pszTransformName); + CPLFree(pData); + if (psLast) + psLast->psNext = psList->psNext; + else + psListDeserializer = NULL; + CPLFree(psList); + break; + } + psLast = psList; + psList = psList->psNext; + } +} + +/************************************************************************/ +/* GDALUnregisterTransformDeserializer() */ +/************************************************************************/ + +void GDALCleanupTransformDeserializerMutex() +{ + if( hDeserializerMutex != NULL ) + { + CPLDestroyMutex(hDeserializerMutex); + hDeserializerMutex = NULL; + } +} + +/************************************************************************/ +/* GDALDeserializeTransformer() */ +/************************************************************************/ + +CPLErr GDALDeserializeTransformer( CPLXMLNode *psTree, + GDALTransformerFunc *ppfnFunc, + void **ppTransformArg ) + +{ + *ppfnFunc = NULL; + *ppTransformArg = NULL; + + CPLErrorReset(); + + if( psTree == NULL || psTree->eType != CXT_Element ) + CPLError( CE_Failure, CPLE_AppDefined, + "Malformed element in GDALDeserializeTransformer" ); + else if( EQUAL(psTree->pszValue,"GenImgProjTransformer") ) + { + *ppfnFunc = GDALGenImgProjTransform; + *ppTransformArg = GDALDeserializeGenImgProjTransformer( psTree ); + } + else if( EQUAL(psTree->pszValue,"ReprojectionTransformer") ) + { + *ppfnFunc = GDALReprojectionTransform; + *ppTransformArg = GDALDeserializeReprojectionTransformer( psTree ); + } + else if( EQUAL(psTree->pszValue,"GCPTransformer") ) + { + *ppfnFunc = GDALGCPTransform; + *ppTransformArg = GDALDeserializeGCPTransformer( psTree ); + } + else if( EQUAL(psTree->pszValue,"TPSTransformer") ) + { + *ppfnFunc = GDALTPSTransform; + *ppTransformArg = GDALDeserializeTPSTransformer( psTree ); + } + else if( EQUAL(psTree->pszValue,"GeoLocTransformer") ) + { + *ppfnFunc = GDALGeoLocTransform; + *ppTransformArg = GDALDeserializeGeoLocTransformer( psTree ); + } + else if( EQUAL(psTree->pszValue,"RPCTransformer") ) + { + *ppfnFunc = GDALRPCTransform; + *ppTransformArg = GDALDeserializeRPCTransformer( psTree ); + } + else if( EQUAL(psTree->pszValue,"ApproxTransformer") ) + { + *ppfnFunc = GDALApproxTransform; + *ppTransformArg = GDALDeserializeApproxTransformer( psTree ); + } + else + { + GDALTransformDeserializeFunc pfnDeserializeFunc = NULL; + { + CPLMutexHolderD(&hDeserializerMutex); + CPLList* psList = psListDeserializer; + while(psList) + { + TransformDeserializerInfo* psInfo = + (TransformDeserializerInfo*)psList->pData; + if (strcmp(psInfo->pszTransformName, psTree->pszValue) == 0) + { + *ppfnFunc = psInfo->pfnTransformerFunc; + pfnDeserializeFunc = psInfo->pfnDeserializeFunc; + break; + } + psList = psList->psNext; + } + } + + if (pfnDeserializeFunc != NULL) + { + *ppTransformArg = pfnDeserializeFunc( psTree ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised element '%s' GDALDeserializeTransformer", + psTree->pszValue ); + } + } + + return CPLGetLastErrorType(); +} + +/************************************************************************/ +/* GDALDestroyTransformer() */ +/************************************************************************/ + +void GDALDestroyTransformer( void *pTransformArg ) + +{ + if( pTransformArg == NULL ) + return; + + GDALTransformerInfo *psInfo = (GDALTransformerInfo *) pTransformArg; + + if( memcmp(psInfo->abySignature,GDAL_GTI2_SIGNATURE, + strlen(GDAL_GTI2_SIGNATURE)) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to destroy non-GTI2 transformer." ); + } + else + psInfo->pfnCleanup( pTransformArg ); +} + +/************************************************************************/ +/* GDALUseTransformer() */ +/************************************************************************/ + +int GDALUseTransformer( void *pTransformArg, + int bDstToSrc, int nPointCount, + double *x, double *y, double *z, + int *panSuccess ) +{ + GDALTransformerInfo *psInfo = (GDALTransformerInfo *) pTransformArg; + + if( psInfo == NULL || + memcmp(psInfo->abySignature,GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE)) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to use non-GTI2 transformer." ); + return FALSE; + } + else + return psInfo->pfnTransform( pTransformArg, bDstToSrc, nPointCount, + x, y, z, panSuccess ); +} + +/************************************************************************/ +/* GDALCloneTransformer() */ +/************************************************************************/ + +void *GDALCloneTransformer( void *pTransformArg ) +{ + VALIDATE_POINTER1( pTransformArg, "GDALCloneTransformer", NULL ); + + GDALTransformerInfo *psInfo = static_cast(pTransformArg); + + if( psInfo == NULL || + memcmp(psInfo->abySignature,GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE)) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to clone non-GTI2 transformer." ); + return NULL; + } + + if( psInfo->pfnCreateSimilar != NULL ) + { + return psInfo->pfnCreateSimilar(psInfo, 1.0, 1.0); + } + + if ( psInfo->pfnSerialize == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No serialization function available for this transformer." ); + return NULL; + } + else + { + CPLXMLNode* pSerialized = psInfo->pfnSerialize( pTransformArg ); + if( pSerialized == NULL ) + return NULL; + GDALTransformerFunc pfnTransformer = NULL; + void *pClonedTransformArg = NULL; + if( GDALDeserializeTransformer( pSerialized, &pfnTransformer, &pClonedTransformArg ) != + CE_None ) + { + CPLDestroyXMLNode(pSerialized); + return NULL; + } + CPLDestroyXMLNode(pSerialized); + return pClonedTransformArg; + } +} + +/************************************************************************/ +/* GDALCreateSimilarTransformer() */ +/************************************************************************/ + +void* GDALCreateSimilarTransformer( void* pTransformArg, double dfRatioX, double dfRatioY ) +{ + VALIDATE_POINTER1( pTransformArg, "GDALCreateSimilarTransformer", NULL ); + + GDALTransformerInfo *psInfo = static_cast(pTransformArg); + + if( psInfo == NULL || + memcmp(psInfo->abySignature,GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE)) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to call CreateSimilar on a non-GTI2 transformer." ); + return NULL; + } + + if( psInfo->pfnCreateSimilar != NULL ) + { + return psInfo->pfnCreateSimilar(psInfo, dfRatioX, dfRatioY); + } + + CPLError( CE_Failure, CPLE_AppDefined, + "No CreateSimilar function available for this transformer." ); + return NULL; +} + +/************************************************************************/ +/* GDALSetTransformerDstGeoTransform() */ +/************************************************************************/ + +void GDALSetTransformerDstGeoTransform(void *pTransformArg, + const double *padfGeoTransform ) +{ + VALIDATE_POINTER0( pTransformArg, "GDALSetTransformerDstGeoTransform" ); + + GDALTransformerInfo *psInfo = static_cast(pTransformArg); + + if( psInfo == NULL || + memcmp(psInfo->abySignature,GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE)) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to call GDALSetTransformerDstGeoTransform on a non-GTI2 transformer." ); + return; + } + + if( EQUAL(psInfo->pszClassName, "GDALApproxTransformer") ) + { + ApproxTransformInfo *psATInfo = (ApproxTransformInfo*)pTransformArg; + psInfo = (GDALTransformerInfo *)psATInfo->pBaseCBData; + + if( psInfo == NULL || + memcmp(psInfo->abySignature,GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE)) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to call GDALSetTransformerDstGeoTransform on a non-GTI2 transformer." ); + return; + } + } + + if( EQUAL(psInfo->pszClassName, "GDALGenImgProjTransformer") ) + { + GDALSetGenImgProjTransformerDstGeoTransform(psInfo, padfGeoTransform); + } +} diff --git a/bazaar/plugin/gdal/alg/gdaltransformgeolocs.cpp b/bazaar/plugin/gdal/alg/gdaltransformgeolocs.cpp new file mode 100644 index 000000000..d12b259b5 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdaltransformgeolocs.cpp @@ -0,0 +1,151 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL + * Purpose: Algorithm to apply a transformer to geolocation style bands. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2012, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_alg_priv.h" +#include "gdal_priv.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id$"); + + + +/************************************************************************/ +/* GDALTransformGeolocations() */ +/************************************************************************/ + +/** + * Transform locations held in bands. + * + * The X/Y and possibly Z values in the identified bands are transformed + * using a spatial transformer. The changes values are written back to the + * source bands so they need to updatable. + * + * @param hXBand the band containing the X locations (usually long/easting). + * @param hYBand the band containing the Y locations (usually lat/northing). + * @param hZBand the band containing the Z locations (may be NULL). + * @param pfnTransformer the transformer function. + * @param pTransformArg the callback data for the transformer function. + * @param pfnProgress callback for reporting algorithm progress matching the + * GDALProgressFunc() semantics. May be NULL. + * @param pProgressArg callback argument passed to pfnProgress. + * @param papszOptions list of name/value options - none currently supported. + * + * @return CE_None on success or CE_Failure if an error occurs. + */ + +CPLErr +GDALTransformGeolocations( GDALRasterBandH hXBand, + GDALRasterBandH hYBand, + GDALRasterBandH hZBand, + GDALTransformerFunc pfnTransformer, + void *pTransformArg, + GDALProgressFunc pfnProgress, + void *pProgressArg, + CPL_UNUSED char **papszOptions ) + +{ + VALIDATE_POINTER1( hXBand, "GDALTransformGeolocations", CE_Failure ); + VALIDATE_POINTER1( hYBand, "GDALTransformGeolocations", CE_Failure ); + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* Ensure the bands are matching in size. */ +/* -------------------------------------------------------------------- */ + GDALRasterBand *poXBand = (GDALRasterBand *) hXBand; + GDALRasterBand *poYBand = (GDALRasterBand *) hYBand; + GDALRasterBand *poZBand = (GDALRasterBand *) hZBand; + int nXSize = poXBand->GetXSize(); + int nYSize = poXBand->GetYSize(); + + if( nXSize != poYBand->GetXSize() + || nYSize != poYBand->GetYSize() + || (poZBand != NULL && nXSize != poZBand->GetXSize()) + || (poZBand != NULL && nYSize != poZBand->GetYSize()) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Size of X, Y and/or Z bands do not match." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Allocate a buffer large enough to hold one whole row. */ +/* -------------------------------------------------------------------- */ + double *padfX = (double*) CPLMalloc(sizeof(double) * nXSize); + double *padfY = (double*) CPLMalloc(sizeof(double) * nXSize); + double *padfZ = (double*) CPLMalloc(sizeof(double) * nXSize); + int *panSuccess = (int*) CPLMalloc(sizeof(int) * nXSize); + int iLine; + CPLErr eErr = CE_None; + + pfnProgress( 0.0, "", pProgressArg ); + for( iLine = 0; eErr == CE_None && iLine < nYSize; iLine++ ) + { + eErr = poXBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + padfX, nXSize, 1, GDT_Float64, 0, 0, NULL ); + if( eErr == CE_None ) + eErr = poYBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + padfY, nXSize, 1, GDT_Float64, 0, 0, NULL ); + if( eErr == CE_None && poZBand != NULL ) + eErr = poZBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + padfZ, nXSize, 1, GDT_Float64, 0, 0, NULL ); + else + memset( padfZ, 0, sizeof(double) * nXSize); + + if( eErr == CE_None ) + { + pfnTransformer( pTransformArg, FALSE, nXSize, + padfX, padfY, padfZ, panSuccess ); + } + + if( eErr == CE_None ) + eErr = poXBand->RasterIO( GF_Write, 0, iLine, nXSize, 1, + padfX, nXSize, 1, GDT_Float64, 0, 0, NULL ); + if( eErr == CE_None ) + eErr = poYBand->RasterIO( GF_Write, 0, iLine, nXSize, 1, + padfY, nXSize, 1, GDT_Float64, 0, 0, NULL ); + if( eErr == CE_None && poZBand != NULL ) + eErr = poZBand->RasterIO( GF_Write, 0, iLine, nXSize, 1, + padfZ, nXSize, 1, GDT_Float64, 0, 0, NULL ); + + if( eErr == CE_None ) + pfnProgress( (iLine+1) / (double) nYSize, "", pProgressArg ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + CPLFree( panSuccess ); + + return eErr; +} diff --git a/bazaar/plugin/gdal/alg/gdalwarper.cpp b/bazaar/plugin/gdal/alg/gdalwarper.cpp new file mode 100644 index 000000000..09cfec626 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalwarper.cpp @@ -0,0 +1,1549 @@ +/****************************************************************************** + * $Id: gdalwarper.cpp 29207 2015-05-18 17:23:45Z mloskot $ + * + * Project: High Performance Image Reprojector + * Purpose: Implementation of high level convenience APIs for warper. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdalwarper.h" +#include "cpl_string.h" +#include "cpl_minixml.h" +#include "ogr_api.h" +#include "gdal_priv.h" + +CPL_CVSID("$Id: gdalwarper.cpp 29207 2015-05-18 17:23:45Z mloskot $"); + +/************************************************************************/ +/* GDALReprojectImage() */ +/************************************************************************/ + +/** + * Reproject image. + * + * This is a convenience function utilizing the GDALWarpOperation class to + * reproject an image from a source to a destination. In particular, this + * function takes care of establishing the transformation function to + * implement the reprojection, and will default a variety of other + * warp options. + * + * No metadata, projection info, or color tables are transferred + * to the output file. + * + * Starting with GDAL 2.0, nodata values set on destination dataset are taken + * into account. + * + * @param hSrcDS the source image file. + * @param pszSrcWKT the source projection. If NULL the source projection + * is read from from hSrcDS. + * @param hDstDS the destination image file. + * @param pszDstWKT the destination projection. If NULL the destination + * projection will be read from hDstDS. + * @param eResampleAlg the type of resampling to use. + * @param dfWarpMemoryLimit the amount of memory (in bytes) that the warp + * API is allowed to use for caching. This is in addition to the memory + * already allocated to the GDAL caching (as per GDALSetCacheMax()). May be + * 0.0 to use default memory settings. + * @param dfMaxError maximum error measured in input pixels that is allowed + * in approximating the transformation (0.0 for exact calculations). + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * @param psOptions warp options, normally NULL. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr CPL_STDCALL +GDALReprojectImage( GDALDatasetH hSrcDS, const char *pszSrcWKT, + GDALDatasetH hDstDS, const char *pszDstWKT, + GDALResampleAlg eResampleAlg, + CPL_UNUSED double dfWarpMemoryLimit, + double dfMaxError, + GDALProgressFunc pfnProgress, void *pProgressArg, + GDALWarpOptions *psOptions ) + +{ + GDALWarpOptions *psWOptions; + +/* -------------------------------------------------------------------- */ +/* Setup a reprojection based transformer. */ +/* -------------------------------------------------------------------- */ + void *hTransformArg; + + hTransformArg = + GDALCreateGenImgProjTransformer( hSrcDS, pszSrcWKT, hDstDS, pszDstWKT, + TRUE, 1000.0, 0 ); + + if( hTransformArg == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Create a copy of the user provided options, or a defaulted */ +/* options structure. */ +/* -------------------------------------------------------------------- */ + if( psOptions == NULL ) + psWOptions = GDALCreateWarpOptions(); + else + psWOptions = GDALCloneWarpOptions( psOptions ); + + psWOptions->eResampleAlg = eResampleAlg; + +/* -------------------------------------------------------------------- */ +/* Set transform. */ +/* -------------------------------------------------------------------- */ + if( dfMaxError > 0.0 ) + { + psWOptions->pTransformerArg = + GDALCreateApproxTransformer( GDALGenImgProjTransform, + hTransformArg, dfMaxError ); + + psWOptions->pfnTransformer = GDALApproxTransform; + } + else + { + psWOptions->pfnTransformer = GDALGenImgProjTransform; + psWOptions->pTransformerArg = hTransformArg; + } + +/* -------------------------------------------------------------------- */ +/* Set file and band mapping. */ +/* -------------------------------------------------------------------- */ + int iBand; + + psWOptions->hSrcDS = hSrcDS; + psWOptions->hDstDS = hDstDS; + + if( psWOptions->nBandCount == 0 ) + { + psWOptions->nBandCount = MIN(GDALGetRasterCount(hSrcDS), + GDALGetRasterCount(hDstDS)); + + psWOptions->panSrcBands = (int *) + CPLMalloc(sizeof(int) * psWOptions->nBandCount); + psWOptions->panDstBands = (int *) + CPLMalloc(sizeof(int) * psWOptions->nBandCount); + + for( iBand = 0; iBand < psWOptions->nBandCount; iBand++ ) + { + psWOptions->panSrcBands[iBand] = iBand+1; + psWOptions->panDstBands[iBand] = iBand+1; + } + } + +/* -------------------------------------------------------------------- */ +/* Set source nodata values if the source dataset seems to have */ +/* any. Same for target nodata values */ +/* -------------------------------------------------------------------- */ + for( iBand = 0; iBand < psWOptions->nBandCount; iBand++ ) + { + GDALRasterBandH hBand = GDALGetRasterBand( hSrcDS, iBand+1 ); + int bGotNoData = FALSE; + double dfNoDataValue; + + if (GDALGetRasterColorInterpretation(hBand) == GCI_AlphaBand) + { + psWOptions->nSrcAlphaBand = iBand + 1; + } + + dfNoDataValue = GDALGetRasterNoDataValue( hBand, &bGotNoData ); + if( bGotNoData ) + { + if( psWOptions->padfSrcNoDataReal == NULL ) + { + int ii; + + psWOptions->padfSrcNoDataReal = (double *) + CPLMalloc(sizeof(double) * psWOptions->nBandCount); + psWOptions->padfSrcNoDataImag = (double *) + CPLMalloc(sizeof(double) * psWOptions->nBandCount); + + for( ii = 0; ii < psWOptions->nBandCount; ii++ ) + { + psWOptions->padfSrcNoDataReal[ii] = -1.1e20; + psWOptions->padfSrcNoDataImag[ii] = 0.0; + } + } + + psWOptions->padfSrcNoDataReal[iBand] = dfNoDataValue; + } + + // Deal with target band + hBand = GDALGetRasterBand( hDstDS, iBand+1 ); + if (hBand && GDALGetRasterColorInterpretation(hBand) == GCI_AlphaBand) + { + psWOptions->nDstAlphaBand = iBand + 1; + } + + dfNoDataValue = GDALGetRasterNoDataValue( hBand, &bGotNoData ); + if( bGotNoData ) + { + if( psWOptions->padfDstNoDataReal == NULL ) + { + int ii; + + psWOptions->padfDstNoDataReal = (double *) + CPLMalloc(sizeof(double) * psWOptions->nBandCount); + psWOptions->padfDstNoDataImag = (double *) + CPLMalloc(sizeof(double) * psWOptions->nBandCount); + + for( ii = 0; ii < psWOptions->nBandCount; ii++ ) + { + psWOptions->padfDstNoDataReal[ii] = -1.1e20; + psWOptions->padfDstNoDataImag[ii] = 0.0; + } + } + + psWOptions->padfDstNoDataReal[iBand] = dfNoDataValue; + } + } + +/* -------------------------------------------------------------------- */ +/* Set the progress function. */ +/* -------------------------------------------------------------------- */ + if( pfnProgress != NULL ) + { + psWOptions->pfnProgress = pfnProgress; + psWOptions->pProgressArg = pProgressArg; + } + +/* -------------------------------------------------------------------- */ +/* Create a warp options based on the options. */ +/* -------------------------------------------------------------------- */ + GDALWarpOperation oWarper; + CPLErr eErr; + + eErr = oWarper.Initialize( psWOptions ); + + if( eErr == CE_None ) + eErr = oWarper.ChunkAndWarpImage( 0, 0, + GDALGetRasterXSize(hDstDS), + GDALGetRasterYSize(hDstDS) ); + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + GDALDestroyGenImgProjTransformer( hTransformArg ); + + if( dfMaxError > 0.0 ) + GDALDestroyApproxTransformer( psWOptions->pTransformerArg ); + + GDALDestroyWarpOptions( psWOptions ); + + return eErr; +} + +/************************************************************************/ +/* GDALCreateAndReprojectImage() */ +/* */ +/* This is a "quicky" reprojection API. */ +/************************************************************************/ + +CPLErr CPL_STDCALL GDALCreateAndReprojectImage( + GDALDatasetH hSrcDS, const char *pszSrcWKT, + const char *pszDstFilename, const char *pszDstWKT, + GDALDriverH hDstDriver, char **papszCreateOptions, + GDALResampleAlg eResampleAlg, double dfWarpMemoryLimit, double dfMaxError, + GDALProgressFunc pfnProgress, void *pProgressArg, + GDALWarpOptions *psOptions ) + +{ + VALIDATE_POINTER1( hSrcDS, "GDALCreateAndReprojectImage", CE_Failure ); + +/* -------------------------------------------------------------------- */ +/* Default a few parameters. */ +/* -------------------------------------------------------------------- */ + if( hDstDriver == NULL ) + { + hDstDriver = GDALGetDriverByName( "GTiff" ); + if (hDstDriver == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GDALCreateAndReprojectImage needs GTiff driver"); + return CE_Failure; + } + } + + if( pszSrcWKT == NULL ) + pszSrcWKT = GDALGetProjectionRef( hSrcDS ); + + if( pszDstWKT == NULL ) + pszDstWKT = pszSrcWKT; + +/* -------------------------------------------------------------------- */ +/* Create a transformation object from the source to */ +/* destination coordinate system. */ +/* -------------------------------------------------------------------- */ + void *hTransformArg; + + hTransformArg = + GDALCreateGenImgProjTransformer( hSrcDS, pszSrcWKT, NULL, pszDstWKT, + TRUE, 1000.0, 0 ); + + if( hTransformArg == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Get approximate output definition. */ +/* -------------------------------------------------------------------- */ + double adfDstGeoTransform[6]; + int nPixels, nLines; + + if( GDALSuggestedWarpOutput( hSrcDS, + GDALGenImgProjTransform, hTransformArg, + adfDstGeoTransform, &nPixels, &nLines ) + != CE_None ) + return CE_Failure; + + GDALDestroyGenImgProjTransformer( hTransformArg ); + +/* -------------------------------------------------------------------- */ +/* Create the output file. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hDstDS; + + hDstDS = GDALCreate( hDstDriver, pszDstFilename, nPixels, nLines, + GDALGetRasterCount(hSrcDS), + GDALGetRasterDataType(GDALGetRasterBand(hSrcDS,1)), + papszCreateOptions ); + + if( hDstDS == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Write out the projection definition. */ +/* -------------------------------------------------------------------- */ + GDALSetProjection( hDstDS, pszDstWKT ); + GDALSetGeoTransform( hDstDS, adfDstGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Perform the reprojection. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr ; + + eErr = + GDALReprojectImage( hSrcDS, pszSrcWKT, hDstDS, pszDstWKT, + eResampleAlg, dfWarpMemoryLimit, dfMaxError, + pfnProgress, pProgressArg, psOptions ); + + GDALClose( hDstDS ); + + return eErr; +} + +/************************************************************************/ +/* GDALWarpNoDataMasker() */ +/* */ +/* GDALMaskFunc for establishing a validity mask for a source */ +/* band based on a provided NODATA value. */ +/************************************************************************/ + +CPLErr +GDALWarpNoDataMasker( void *pMaskFuncArg, int nBandCount, GDALDataType eType, + int /* nXOff */, int /* nYOff */, int nXSize, int nYSize, + GByte **ppImageData, + int bMaskIsFloat, void *pValidityMask, int* pbOutAllValid ) + +{ + double *padfNoData = (double *) pMaskFuncArg; + GUInt32 *panValidityMask = (GUInt32 *) pValidityMask; + + *pbOutAllValid = FALSE; + + if( nBandCount != 1 || bMaskIsFloat ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid nBandCount or bMaskIsFloat argument in SourceNoDataMask" ); + return CE_Failure; + } + + switch( eType ) + { + case GDT_Byte: + { + int nNoData = (int) padfNoData[0]; + GByte *pabyData = (GByte *) *ppImageData; + int iOffset; + + // nothing to do if value is out of range. + if( padfNoData[0] < 0.0 || padfNoData[0] > 255.000001 + || padfNoData[1] != 0.0 ) + { + *pbOutAllValid = TRUE; + return CE_None; + } + + int bAllValid = TRUE; + for( iOffset = nXSize*nYSize-1; iOffset >= 0; iOffset-- ) + { + if( pabyData[iOffset] == nNoData ) + { + bAllValid = FALSE; + panValidityMask[iOffset>>5] &= ~(0x01 << (iOffset & 0x1f)); + } + } + *pbOutAllValid = bAllValid; + } + break; + + case GDT_Int16: + { + int nNoData = (int) padfNoData[0]; + GInt16 *panData = (GInt16 *) *ppImageData; + int iOffset; + + // nothing to do if value is out of range. + if( padfNoData[0] < -32768 || padfNoData[0] > 32767 + || padfNoData[1] != 0.0 ) + { + *pbOutAllValid = TRUE; + return CE_None; + } + + int bAllValid = TRUE; + for( iOffset = nXSize*nYSize-1; iOffset >= 0; iOffset-- ) + { + if( panData[iOffset] == nNoData ) + { + bAllValid = FALSE; + panValidityMask[iOffset>>5] &= ~(0x01 << (iOffset & 0x1f)); + } + } + *pbOutAllValid = bAllValid; + } + break; + + case GDT_UInt16: + { + int nNoData = (int) padfNoData[0]; + GUInt16 *panData = (GUInt16 *) *ppImageData; + int iOffset; + + // nothing to do if value is out of range. + if( padfNoData[0] < 0 || padfNoData[0] > 65535 + || padfNoData[1] != 0.0 ) + { + *pbOutAllValid = TRUE; + return CE_None; + } + + int bAllValid = TRUE; + for( iOffset = nXSize*nYSize-1; iOffset >= 0; iOffset-- ) + { + if( panData[iOffset] == nNoData ) + { + bAllValid = FALSE; + panValidityMask[iOffset>>5] &= ~(0x01 << (iOffset & 0x1f)); + } + } + *pbOutAllValid = bAllValid; + } + break; + + case GDT_Float32: + { + float fNoData = (float) padfNoData[0]; + float *pafData = (float *) *ppImageData; + int iOffset; + int bIsNoDataNan = CPLIsNan(fNoData); + + // nothing to do if value is out of range. + if( padfNoData[1] != 0.0 ) + { + *pbOutAllValid = TRUE; + return CE_None; + } + + int bAllValid = TRUE; + for( iOffset = nXSize*nYSize-1; iOffset >= 0; iOffset-- ) + { + float fVal = pafData[iOffset]; + if( (bIsNoDataNan && CPLIsNan(fVal)) || (!bIsNoDataNan && ARE_REAL_EQUAL(fVal, fNoData)) ) + { + bAllValid = FALSE; + panValidityMask[iOffset>>5] &= ~(0x01 << (iOffset & 0x1f)); + } + } + *pbOutAllValid = bAllValid; + } + break; + + case GDT_Float64: + { + double dfNoData = padfNoData[0]; + double *padfData = (double *) *ppImageData; + int iOffset; + int bIsNoDataNan = CPLIsNan(dfNoData); + + // nothing to do if value is out of range. + if( padfNoData[1] != 0.0 ) + { + *pbOutAllValid = TRUE; + return CE_None; + } + + int bAllValid = TRUE; + for( iOffset = nXSize*nYSize-1; iOffset >= 0; iOffset-- ) + { + double dfVal = padfData[iOffset]; + if( (bIsNoDataNan && CPLIsNan(dfVal)) || (!bIsNoDataNan && ARE_REAL_EQUAL(dfVal, dfNoData)) ) + { + bAllValid = FALSE; + panValidityMask[iOffset>>5] &= ~(0x01 << (iOffset & 0x1f)); + } + } + *pbOutAllValid = bAllValid; + } + break; + + default: + { + double *padfWrk; + int iLine, iPixel; + int nWordSize = GDALGetDataTypeSize(eType)/8; + + int bIsNoDataRealNan = CPLIsNan(padfNoData[0]); + int bIsNoDataImagNan = CPLIsNan(padfNoData[1]); + + padfWrk = (double *) CPLMalloc(nXSize * sizeof(double) * 2); + int bAllValid = TRUE; + for( iLine = 0; iLine < nYSize; iLine++ ) + { + GDALCopyWords( ((GByte *) *ppImageData)+nWordSize*iLine*nXSize, + eType, nWordSize, + padfWrk, GDT_CFloat64, 16, nXSize ); + + for( iPixel = 0; iPixel < nXSize; iPixel++ ) + { + if( ((bIsNoDataRealNan && CPLIsNan(padfWrk[iPixel*2])) || + (!bIsNoDataRealNan && ARE_REAL_EQUAL(padfWrk[iPixel*2], padfNoData[0]))) + && ((bIsNoDataImagNan && CPLIsNan(padfWrk[iPixel*2+1])) || + (!bIsNoDataImagNan && ARE_REAL_EQUAL(padfWrk[iPixel*2+1], padfNoData[1]))) ) + { + int iOffset = iPixel + iLine * nXSize; + + bAllValid = FALSE; + panValidityMask[iOffset>>5] &= + ~(0x01 << (iOffset & 0x1f)); + } + } + + } + *pbOutAllValid = bAllValid; + + CPLFree( padfWrk ); + } + break; + } + + return CE_None; +} + +/************************************************************************/ +/* GDALWarpSrcAlphaMasker() */ +/* */ +/* GDALMaskFunc for reading source simple 8bit alpha mask */ +/* information and building a floating point density mask from */ +/* it. */ +/************************************************************************/ + +CPLErr +GDALWarpSrcAlphaMasker( void *pMaskFuncArg, + CPL_UNUSED int nBandCount, + CPL_UNUSED GDALDataType eType, + int nXOff, int nYOff, int nXSize, int nYSize, + GByte ** /*ppImageData */, + int bMaskIsFloat, void *pValidityMask, + int* pbOutAllOpaque ) + +{ + GDALWarpOptions *psWO = (GDALWarpOptions *) pMaskFuncArg; + float *pafMask = (float *) pValidityMask; + *pbOutAllOpaque = FALSE; + +/* -------------------------------------------------------------------- */ +/* Do some minimal checking. */ +/* -------------------------------------------------------------------- */ + if( !bMaskIsFloat ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + if( psWO == NULL || psWO->nSrcAlphaBand < 1 ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Read the alpha band. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + GDALRasterBandH hAlphaBand = GDALGetRasterBand( psWO->hSrcDS, + psWO->nSrcAlphaBand ); + if (hAlphaBand == NULL) + return CE_Failure; + + eErr = GDALRasterIO( hAlphaBand, GF_Read, nXOff, nYOff, nXSize, nYSize, + pafMask, nXSize, nYSize, GDT_Float32, 0, 0 ); + + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Rescale from 0-255 to 0.0-1.0. */ +/* -------------------------------------------------------------------- */ + int bOutAllOpaque = TRUE; + for( int iPixel = nXSize * nYSize - 1; iPixel >= 0; iPixel-- ) + { // (1/255) + pafMask[iPixel] = (float)( pafMask[iPixel] * 0.00392157 ); + if( pafMask[iPixel] >= 1.0F ) + pafMask[iPixel] = 1.0F; + else + bOutAllOpaque = FALSE; + } + *pbOutAllOpaque = bOutAllOpaque; + + return CE_None; +} + +/************************************************************************/ +/* GDALWarpSrcMaskMasker() */ +/* */ +/* GDALMaskFunc for reading source simple 8bit validity mask */ +/* information and building a one bit validity mask. */ +/************************************************************************/ + +CPLErr +GDALWarpSrcMaskMasker( void *pMaskFuncArg, + CPL_UNUSED int nBandCount, + CPL_UNUSED GDALDataType eType, + int nXOff, int nYOff, int nXSize, int nYSize, + GByte ** /*ppImageData */, + int bMaskIsFloat, void *pValidityMask ) + +{ + GDALWarpOptions *psWO = (GDALWarpOptions *) pMaskFuncArg; + GUInt32 *panMask = (GUInt32 *) pValidityMask; + +/* -------------------------------------------------------------------- */ +/* Do some minimal checking. */ +/* -------------------------------------------------------------------- */ + if( bMaskIsFloat ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + if( psWO == NULL ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Allocate a temporary buffer to read mask byte data into. */ +/* -------------------------------------------------------------------- */ + GByte *pabySrcMask; + + pabySrcMask = (GByte *) VSIMalloc2(nXSize,nYSize); + if( pabySrcMask == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Failed to allocate pabySrcMask (%dx%d) in GDALWarpSrcMaskMasker()", + nXSize, nYSize ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Fetch our mask band. */ +/* -------------------------------------------------------------------- */ + GDALRasterBandH hSrcBand, hMaskBand = NULL; + + hSrcBand = GDALGetRasterBand( psWO->hSrcDS, psWO->panSrcBands[0] ); + if( hSrcBand != NULL ) + hMaskBand = GDALGetMaskBand( hSrcBand ); + + if( hMaskBand == NULL ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Read the mask band. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + eErr = GDALRasterIO( hMaskBand, GF_Read, nXOff, nYOff, nXSize, nYSize, + pabySrcMask, nXSize, nYSize, GDT_Byte, 0, 0 ); + + if( eErr != CE_None ) + { + CPLFree( pabySrcMask ); + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Pack into 1 bit per pixel for validity. */ +/* -------------------------------------------------------------------- */ + for( int iPixel = nXSize * nYSize - 1; iPixel >= 0; iPixel-- ) + { + if( pabySrcMask[iPixel] == 0 ) + panMask[iPixel>>5] &= ~(0x01 << (iPixel & 0x1f)); + } + + CPLFree( pabySrcMask ); + + return CE_None; +} + +/************************************************************************/ +/* GDALWarpDstAlphaMasker() */ +/* */ +/* GDALMaskFunc for reading or writing the destination simple */ +/* 8bit alpha mask information and building a floating point */ +/* density mask from it. Note, writing is distinguished */ +/* negative bandcount. */ +/************************************************************************/ + +CPLErr +GDALWarpDstAlphaMasker( void *pMaskFuncArg, int nBandCount, + CPL_UNUSED GDALDataType eType, + int nXOff, int nYOff, int nXSize, int nYSize, + GByte ** /*ppImageData */, + int bMaskIsFloat, void *pValidityMask ) +{ + GDALWarpOptions *psWO = (GDALWarpOptions *) pMaskFuncArg; + float *pafMask = (float *) pValidityMask; + int iPixel; + CPLErr eErr; + +/* -------------------------------------------------------------------- */ +/* Do some minimal checking. */ +/* -------------------------------------------------------------------- */ + if( !bMaskIsFloat ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + if( psWO == NULL || psWO->nDstAlphaBand < 1 ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + GDALRasterBandH hAlphaBand = + GDALGetRasterBand( psWO->hDstDS, psWO->nDstAlphaBand ); + if (hAlphaBand == NULL) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Read alpha case. */ +/* -------------------------------------------------------------------- */ + if( nBandCount >= 0 ) + { + const char *pszInitDest = + CSLFetchNameValue( psWO->papszWarpOptions, "INIT_DEST" ); + + // Special logic for destinations being initialized on the fly. + if( pszInitDest != NULL ) + { + for( iPixel = nXSize * nYSize - 1; iPixel >= 0; iPixel-- ) + pafMask[iPixel] = 0.0; + return CE_None; + } + + // Read data. + eErr = GDALRasterIO( hAlphaBand, GF_Read, nXOff, nYOff, nXSize, nYSize, + pafMask, nXSize, nYSize, GDT_Float32, 0, 0 ); + + if( eErr != CE_None ) + return eErr; + + // rescale. + for( iPixel = nXSize * nYSize - 1; iPixel >= 0; iPixel-- ) + { + pafMask[iPixel] = (float) (pafMask[iPixel] * 0.00392157); + pafMask[iPixel] = MIN( 1.0F, pafMask[iPixel] ); + } + + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Write alpha case. */ +/* -------------------------------------------------------------------- */ + else + { + for( iPixel = nXSize * nYSize - 1; iPixel >= 0; iPixel-- ) + pafMask[iPixel] = (float)(int) ( pafMask[iPixel] * 255.1 ); + + // Write data. + + /* The VRT warper will pass destination sizes that may exceed */ + /* the size of the raster for the partial blocks at the right */ + /* and bottom of the band. So let's adjust the size */ + int nDstXSize = nXSize; + if (nXOff + nXSize > GDALGetRasterBandXSize(hAlphaBand)) + nDstXSize = GDALGetRasterBandXSize(hAlphaBand) - nXOff; + int nDstYSize = nYSize; + if (nYOff + nYSize > GDALGetRasterBandYSize(hAlphaBand)) + nDstYSize = GDALGetRasterBandYSize(hAlphaBand) - nYOff; + + eErr = GDALRasterIO( hAlphaBand, GF_Write, + nXOff, nYOff, nDstXSize, nDstYSize, + pafMask, nDstXSize, nDstYSize, GDT_Float32, + 0, sizeof(float) * nXSize ); + return eErr; + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALWarpOptions */ +/* ==================================================================== */ +/************************************************************************/ + +/** + * \var char **GDALWarpOptions::papszWarpOptions; + * + * A string list of additional options controlling the warp operation in + * name=value format. A suitable string list can be prepared with + * CSLSetNameValue(). + * + * The following values are currently supported: + * + * - INIT_DEST=[value] or INIT_DEST=NO_DATA: This option forces the + * destination image to be initialized to the indicated value (for all bands) + * or indicates that it should be initialized to the NO_DATA value in + * padfDstNoDataReal/padfDstNoDataImag. If this value isn't set the + * destination image will be read and overlayed. + * + * - WRITE_FLUSH=YES/NO: This option forces a flush to disk of data after + * each chunk is processed. In some cases this helps ensure a serial + * writing of the output data otherwise a block of data may be written to disk + * each time a block of data is read for the input buffer resulting in alot + * of extra seeking around the disk, and reduced IO throughput. The default + * at this time is NO. + * + * - SKIP_NOSOURCE=YES/NO: Skip all processing for chunks for which there + * is no corresponding input data. This will disable initializing the + * destination (INIT_DEST) and all other processing, and so should be used + * careful. Mostly useful to short circuit a lot of extra work in mosaicing + * situations. + * + * - UNIFIED_SRC_NODATA=YES/[NO]: By default nodata masking values considered + * independently for each band. However, sometimes it is desired to treat all + * bands as nodata if and only if, all bands match the corresponding nodata + * values. To get this behavior set this option to YES. + * + * Normally when computing the source raster data to + * load to generate a particular output area, the warper samples transforms + * 21 points along each edge of the destination region back onto the source + * file, and uses this to compute a bounding window on the source image that + * is sufficient. Depending on the transformation in effect, the source + * window may be a bit too small, or even missing large areas. Problem + * situations are those where the transformation is very non-linear or + * "inside out". Examples are transforming from WGS84 to Polar Steregraphic + * for areas around the pole, or transformations where some of the image is + * untransformable. The following options provide some additional control + * to deal with errors in computing the source window: + * + * - SAMPLE_GRID=YES/NO: Setting this option to YES will force the sampling to + * include internal points as well as edge points which can be important if + * the transformation is esoteric inside out, or if large sections of the + * destination image are not transformable into the source coordinate system. + * + * - SAMPLE_STEPS: Modifies the density of the sampling grid. The default + * number of steps is 21. Increasing this can increase the computational + * cost, but improves the accuracy with which the source region is computed. + * + * - SOURCE_EXTRA: This is a number of extra pixels added around the source + * window for a given request, and by default it is 1 to take care of rounding + * error. Setting this larger will incease the amount of data that needs to + * be read, but can avoid missing source data. + * + * - CUTLINE: This may contain the WKT geometry for a cutline. It will + * be converted into a geometry by GDALWarpOperation::Initialize() and assigned + * to the GDALWarpOptions hCutline field. The coordinates must be expressed + * in source pixel/line coordinates. Note: this is different from the assumptions + * made for the -cutline option of the gdalwarp utility ! + * + * - CUTLINE_BLEND_DIST: This may be set with a distance in pixels which + * will be assigned to the dfCutlineBlendDist field in the GDALWarpOptions. + * + * - CUTLINE_ALL_TOUCHED: This defaults to FALSE, but may be set to TRUE + * to enable ALL_TOUCHEd mode when rasterizing cutline polygons. This is + * useful to ensure that that all pixels overlapping the cutline polygon + * will be selected, not just those whose center point falls within the + * polygon. + * + * - OPTIMIZE_SIZE: This defaults to FALSE, but may be set to TRUE when + * outputing typically to a compressed dataset (GeoTIFF with COMPRESSED creation + * option set for example) for achieving a smaller file size. This is achieved + * by writing at once data aligned on full blocks of the target dataset, which + * avoids partial writes of compressed blocks and lost space when they are rewritten + * at the end of the file. However sticking to target block size may cause major + * processing slowdown for some particular reprojections. + * + * - NUM_THREADS: (GDAL >= 1.10) Can be set to a numeric value or ALL_CPUS to + * set the number of threads to use to parallelize the computation part of the + * warping. If not set, computation will be done in a single thread. + * + * - STREAMABLE_OUTPUT: (GDAL >= 2.0) This defaults to FALSE, but may be set to TRUE when + * outputing typically to a streamed file. The gdalwarp utility automatically + * sets this option when outputing to /vsistdout/ or a named pipe (on Unix). + * This option has performance impacts for some reprojections. + * Note: band interleaved output is not currently supported by the warping algorithm in + * a streamable compabible way. + * + * - SRC_COORD_PRECISION: (GDAL >= 2.0). Advanced setting. This defaults to 0, to indicate that + * no rounding of computing source image coordinates corresponding to the target + * image must be done. If greater than 0 (and typically below 1), this value, + * expressed in pixel, will be used to round computed source image coordinates. The purpose + * of this option is to make the results of warping with the approximated transformer + * more reproducible and not sensitive to changes in warping memory size. To achieve + * that, SRC_COORD_PRECISION must be at least 10 times greater than the error + * threshold. The higher the SRC_COORD_PRECISION/error_threshold ratio, the higher + * the performance will be, since exact reprojections must statistically be + * done with a frequency of 4*error_threshold/SRC_COORD_PRECISION. + */ + +/************************************************************************/ +/* GDALCreateWarpOptions() */ +/************************************************************************/ + +GDALWarpOptions * CPL_STDCALL GDALCreateWarpOptions() + +{ + GDALWarpOptions *psOptions; + + psOptions = (GDALWarpOptions *) CPLCalloc(sizeof(GDALWarpOptions),1); + + psOptions->nBandCount = 0; + psOptions->eResampleAlg = GRA_NearestNeighbour; + psOptions->pfnProgress = GDALDummyProgress; + psOptions->eWorkingDataType = GDT_Unknown; + + return psOptions; +} + +/************************************************************************/ +/* GDALDestroyWarpOptions() */ +/************************************************************************/ + +void CPL_STDCALL GDALDestroyWarpOptions( GDALWarpOptions *psOptions ) + +{ + if( psOptions == NULL ) + return; + + CSLDestroy( psOptions->papszWarpOptions ); + CPLFree( psOptions->panSrcBands ); + CPLFree( psOptions->panDstBands ); + CPLFree( psOptions->padfSrcNoDataReal ); + CPLFree( psOptions->padfSrcNoDataImag ); + CPLFree( psOptions->padfDstNoDataReal ); + CPLFree( psOptions->padfDstNoDataImag ); + CPLFree( psOptions->papfnSrcPerBandValidityMaskFunc ); + CPLFree( psOptions->papSrcPerBandValidityMaskFuncArg ); + + if( psOptions->hCutline != NULL ) + OGR_G_DestroyGeometry( (OGRGeometryH) psOptions->hCutline ); + + CPLFree( psOptions ); +} + + +#define COPY_MEM(target,type,count) \ + do { if( (psSrcOptions->target) != NULL && (count) != 0 ) \ + { \ + (psDstOptions->target) = (type *) CPLMalloc(sizeof(type)*(count)); \ + memcpy( (psDstOptions->target), (psSrcOptions->target), \ + sizeof(type) * (count) ); \ + } \ + else \ + (psDstOptions->target) = NULL; } while(0) + +/************************************************************************/ +/* GDALCloneWarpOptions() */ +/************************************************************************/ + +GDALWarpOptions * CPL_STDCALL +GDALCloneWarpOptions( const GDALWarpOptions *psSrcOptions ) + +{ + GDALWarpOptions *psDstOptions = GDALCreateWarpOptions(); + + memcpy( psDstOptions, psSrcOptions, sizeof(GDALWarpOptions) ); + + if( psSrcOptions->papszWarpOptions != NULL ) + psDstOptions->papszWarpOptions = + CSLDuplicate( psSrcOptions->papszWarpOptions ); + + COPY_MEM( panSrcBands, int, psSrcOptions->nBandCount ); + COPY_MEM( panDstBands, int, psSrcOptions->nBandCount ); + COPY_MEM( padfSrcNoDataReal, double, psSrcOptions->nBandCount ); + COPY_MEM( padfSrcNoDataImag, double, psSrcOptions->nBandCount ); + COPY_MEM( padfDstNoDataReal, double, psSrcOptions->nBandCount ); + COPY_MEM( padfDstNoDataImag, double, psSrcOptions->nBandCount ); + COPY_MEM( papfnSrcPerBandValidityMaskFunc, GDALMaskFunc, + psSrcOptions->nBandCount ); + psDstOptions->papSrcPerBandValidityMaskFuncArg = NULL; + + if( psSrcOptions->hCutline != NULL ) + psDstOptions->hCutline = + OGR_G_Clone( (OGRGeometryH) psSrcOptions->hCutline ); + psDstOptions->dfCutlineBlendDist = psSrcOptions->dfCutlineBlendDist; + + return psDstOptions; +} + +/************************************************************************/ +/* GDALSerializeWarpOptions() */ +/************************************************************************/ + +CPLXMLNode * CPL_STDCALL +GDALSerializeWarpOptions( const GDALWarpOptions *psWO ) + +{ + CPLXMLNode *psTree; + +/* -------------------------------------------------------------------- */ +/* Create root. */ +/* -------------------------------------------------------------------- */ + psTree = CPLCreateXMLNode( NULL, CXT_Element, "GDALWarpOptions" ); + +/* -------------------------------------------------------------------- */ +/* WarpMemoryLimit */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( + psTree, "WarpMemoryLimit", + CPLString().Printf("%g", psWO->dfWarpMemoryLimit ) ); + +/* -------------------------------------------------------------------- */ +/* ResampleAlg */ +/* -------------------------------------------------------------------- */ + const char *pszAlgName; + + if( psWO->eResampleAlg == GRA_NearestNeighbour ) + pszAlgName = "NearestNeighbour"; + else if( psWO->eResampleAlg == GRA_Bilinear ) + pszAlgName = "Bilinear"; + else if( psWO->eResampleAlg == GRA_Cubic ) + pszAlgName = "Cubic"; + else if( psWO->eResampleAlg == GRA_CubicSpline ) + pszAlgName = "CubicSpline"; + else if( psWO->eResampleAlg == GRA_Lanczos ) + pszAlgName = "Lanczos"; + else if( psWO->eResampleAlg == GRA_Average ) + pszAlgName = "Average"; + else if( psWO->eResampleAlg == GRA_Mode ) + pszAlgName = "Mode"; + else if( psWO->eResampleAlg == GRA_Max ) + pszAlgName = "Maximum"; + else if( psWO->eResampleAlg == GRA_Min ) + pszAlgName = "Minimum"; + else if( psWO->eResampleAlg == GRA_Med ) + pszAlgName = "Median"; + else if( psWO->eResampleAlg == GRA_Q1 ) + pszAlgName = "Quartile1"; + else if( psWO->eResampleAlg == GRA_Q3 ) + pszAlgName = "Quartile3"; + else + pszAlgName = "Unknown"; + + CPLCreateXMLElementAndValue( + psTree, "ResampleAlg", pszAlgName ); + +/* -------------------------------------------------------------------- */ +/* Working Data Type */ +/* -------------------------------------------------------------------- */ + + CPLCreateXMLElementAndValue( + psTree, "WorkingDataType", + GDALGetDataTypeName( psWO->eWorkingDataType ) ); + +/* -------------------------------------------------------------------- */ +/* Name/value warp options. */ +/* -------------------------------------------------------------------- */ + int iWO; + + for( iWO = 0; psWO->papszWarpOptions != NULL + && psWO->papszWarpOptions[iWO] != NULL; iWO++ ) + { + char *pszName = NULL; + const char *pszValue = + CPLParseNameValue( psWO->papszWarpOptions[iWO], &pszName ); + + /* EXTRA_ELTS is an internal detail that we will recover */ + /* no need to serialize it */ + if( !EQUAL(pszName, "EXTRA_ELTS") ) + { + CPLXMLNode *psOption = + CPLCreateXMLElementAndValue( + psTree, "Option", pszValue ); + + CPLCreateXMLNode( + CPLCreateXMLNode( psOption, CXT_Attribute, "name" ), + CXT_Text, pszName ); + } + + CPLFree(pszName); + } + +/* -------------------------------------------------------------------- */ +/* Source and Destination Data Source */ +/* -------------------------------------------------------------------- */ + if( psWO->hSrcDS != NULL ) + { + CPLCreateXMLElementAndValue( + psTree, "SourceDataset", + GDALGetDescription( psWO->hSrcDS ) ); + + char** papszOpenOptions = ((GDALDataset*)psWO->hSrcDS)->GetOpenOptions(); + GDALSerializeOpenOptionsToXML(psTree, papszOpenOptions); + } + + if( psWO->hDstDS != NULL && strlen(GDALGetDescription(psWO->hDstDS)) != 0 ) + { + CPLCreateXMLElementAndValue( + psTree, "DestinationDataset", + GDALGetDescription( psWO->hDstDS ) ); + } + +/* -------------------------------------------------------------------- */ +/* Serialize transformer. */ +/* -------------------------------------------------------------------- */ + if( psWO->pfnTransformer != NULL ) + { + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformerTree; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "Transformer" ); + + psTransformerTree = + GDALSerializeTransformer( psWO->pfnTransformer, + psWO->pTransformerArg ); + + if( psTransformerTree != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformerTree ); + } + +/* -------------------------------------------------------------------- */ +/* Band count and lists. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psBandList = NULL; + int i; + + if( psWO->nBandCount != 0 ) + psBandList = CPLCreateXMLNode( psTree, CXT_Element, "BandList" ); + + for( i = 0; i < psWO->nBandCount; i++ ) + { + CPLXMLNode *psBand; + + psBand = CPLCreateXMLNode( psBandList, CXT_Element, "BandMapping" ); + if( psWO->panSrcBands != NULL ) + CPLCreateXMLNode( + CPLCreateXMLNode( psBand, CXT_Attribute, "src" ), + CXT_Text, CPLString().Printf( "%d", psWO->panSrcBands[i] ) ); + if( psWO->panDstBands != NULL ) + CPLCreateXMLNode( + CPLCreateXMLNode( psBand, CXT_Attribute, "dst" ), + CXT_Text, CPLString().Printf( "%d", psWO->panDstBands[i] ) ); + + if( psWO->padfSrcNoDataReal != NULL ) + { + if (CPLIsNan(psWO->padfSrcNoDataReal[i])) + CPLCreateXMLElementAndValue(psBand, "SrcNoDataReal", "nan"); + else + CPLCreateXMLElementAndValue( + psBand, "SrcNoDataReal", + CPLString().Printf( "%.16g", psWO->padfSrcNoDataReal[i] ) ); + } + + if( psWO->padfSrcNoDataImag != NULL ) + { + if (CPLIsNan(psWO->padfSrcNoDataImag[i])) + CPLCreateXMLElementAndValue(psBand, "SrcNoDataImag", "nan"); + else + CPLCreateXMLElementAndValue( + psBand, "SrcNoDataImag", + CPLString().Printf( "%.16g", psWO->padfSrcNoDataImag[i] ) ); + } + + if( psWO->padfDstNoDataReal != NULL ) + { + if (CPLIsNan(psWO->padfDstNoDataReal[i])) + CPLCreateXMLElementAndValue(psBand, "DstNoDataReal", "nan"); + else + CPLCreateXMLElementAndValue( + psBand, "DstNoDataReal", + CPLString().Printf( "%.16g", psWO->padfDstNoDataReal[i] ) ); + } + + if( psWO->padfDstNoDataImag != NULL ) + { + if (CPLIsNan(psWO->padfDstNoDataImag[i])) + CPLCreateXMLElementAndValue(psBand, "DstNoDataImag", "nan"); + else + CPLCreateXMLElementAndValue( + psBand, "DstNoDataImag", + CPLString().Printf( "%.16g", psWO->padfDstNoDataImag[i] ) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Alpha bands. */ +/* -------------------------------------------------------------------- */ + if( psWO->nSrcAlphaBand > 0 ) + CPLCreateXMLElementAndValue( + psTree, "SrcAlphaBand", + CPLString().Printf( "%d", psWO->nSrcAlphaBand ) ); + + if( psWO->nDstAlphaBand > 0 ) + CPLCreateXMLElementAndValue( + psTree, "DstAlphaBand", + CPLString().Printf( "%d", psWO->nDstAlphaBand ) ); + +/* -------------------------------------------------------------------- */ +/* Cutline. */ +/* -------------------------------------------------------------------- */ + if( psWO->hCutline != NULL ) + { + char *pszWKT = NULL; + if( OGR_G_ExportToWkt( (OGRGeometryH) psWO->hCutline, &pszWKT ) + == OGRERR_NONE ) + { + CPLCreateXMLElementAndValue( psTree, "Cutline", pszWKT ); + CPLFree( pszWKT ); + } + } + + if( psWO->dfCutlineBlendDist != 0.0 ) + CPLCreateXMLElementAndValue( + psTree, "CutlineBlendDist", + CPLString().Printf( "%.5g", psWO->dfCutlineBlendDist ) ); + + return psTree; +} + +/************************************************************************/ +/* GDALDeserializeWarpOptions() */ +/************************************************************************/ + +GDALWarpOptions * CPL_STDCALL GDALDeserializeWarpOptions( CPLXMLNode *psTree ) + +{ + CPLErrorReset(); + +/* -------------------------------------------------------------------- */ +/* Verify this is the right kind of object. */ +/* -------------------------------------------------------------------- */ + if( psTree == NULL || psTree->eType != CXT_Element + || !EQUAL(psTree->pszValue,"GDALWarpOptions") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Wrong node, unable to deserialize GDALWarpOptions." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create pre-initialized warp options. */ +/* -------------------------------------------------------------------- */ + GDALWarpOptions *psWO = GDALCreateWarpOptions(); + +/* -------------------------------------------------------------------- */ +/* Warp memory limit. */ +/* -------------------------------------------------------------------- */ + psWO->dfWarpMemoryLimit = + CPLAtof(CPLGetXMLValue(psTree,"WarpMemoryLimit","0.0")); + +/* -------------------------------------------------------------------- */ +/* resample algorithm */ +/* -------------------------------------------------------------------- */ + const char *pszValue = + CPLGetXMLValue(psTree,"ResampleAlg","Default"); + + if( EQUAL(pszValue,"NearestNeighbour") ) + psWO->eResampleAlg = GRA_NearestNeighbour; + else if( EQUAL(pszValue,"Bilinear") ) + psWO->eResampleAlg = GRA_Bilinear; + else if( EQUAL(pszValue,"Cubic") ) + psWO->eResampleAlg = GRA_Cubic; + else if( EQUAL(pszValue,"CubicSpline") ) + psWO->eResampleAlg = GRA_CubicSpline; + else if( EQUAL(pszValue,"Lanczos") ) + psWO->eResampleAlg = GRA_Lanczos; + else if( EQUAL(pszValue,"Average") ) + psWO->eResampleAlg = GRA_Average; + else if( EQUAL(pszValue,"Mode") ) + psWO->eResampleAlg = GRA_Mode; + else if( EQUAL(pszValue,"Maximum") ) + psWO->eResampleAlg = GRA_Max; + else if( EQUAL(pszValue,"Minimum") ) + psWO->eResampleAlg = GRA_Min; + else if( EQUAL(pszValue,"Median") ) + psWO->eResampleAlg = GRA_Med; + else if( EQUAL(pszValue,"Quartile1") ) + psWO->eResampleAlg = GRA_Q1; + else if( EQUAL(pszValue,"Quartile3") ) + psWO->eResampleAlg = GRA_Q3; + else if( EQUAL(pszValue,"Default") ) + /* leave as is */; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognise ResampleAlg value '%s'.", + pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Working data type. */ +/* -------------------------------------------------------------------- */ + psWO->eWorkingDataType = + GDALGetDataTypeByName( + CPLGetXMLValue(psTree,"WorkingDataType","Unknown")); + +/* -------------------------------------------------------------------- */ +/* Name/value warp options. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psItem; + + for( psItem = psTree->psChild; psItem != NULL; psItem = psItem->psNext ) + { + if( psItem->eType == CXT_Element + && EQUAL(psItem->pszValue,"Option") ) + { + const char *pszName = CPLGetXMLValue(psItem, "Name", NULL ); + const char *pszValue = CPLGetXMLValue(psItem, "", NULL ); + + if( pszName != NULL && pszValue != NULL ) + { + psWO->papszWarpOptions = + CSLSetNameValue( psWO->papszWarpOptions, + pszName, pszValue ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Source Dataset. */ +/* -------------------------------------------------------------------- */ + pszValue = CPLGetXMLValue(psTree,"SourceDataset",NULL); + + if( pszValue != NULL ) + { + char** papszOpenOptions = GDALDeserializeOpenOptionsFromXML(psTree); + psWO->hSrcDS = GDALOpenEx( + pszValue, GDAL_OF_SHARED | GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, NULL, + (const char* const* )papszOpenOptions, NULL ); + CSLDestroy(papszOpenOptions); + } + +/* -------------------------------------------------------------------- */ +/* Destination Dataset. */ +/* -------------------------------------------------------------------- */ + pszValue = CPLGetXMLValue(psTree,"DestinationDataset",NULL); + + if( pszValue != NULL ) + psWO->hDstDS = GDALOpenShared( pszValue, GA_Update ); + +/* -------------------------------------------------------------------- */ +/* First, count band mappings so we can establish the bandcount. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psBandTree = CPLGetXMLNode( psTree, "BandList" ); + CPLXMLNode *psBand = NULL; + + psWO->nBandCount = 0; + + if (psBandTree) + psBand = psBandTree->psChild; + else + psBand = NULL; + + for( ; psBand != NULL; psBand = psBand->psNext ) + { + if( psBand->eType != CXT_Element + || !EQUAL(psBand->pszValue,"BandMapping") ) + continue; + + psWO->nBandCount++; + } + +/* ==================================================================== */ +/* Now actually process each bandmapping. */ +/* ==================================================================== */ + int iBand = 0; + + if (psBandTree) + psBand = psBandTree->psChild; + else + psBand = NULL; + + for( ; psBand != NULL; psBand = psBand->psNext ) + { + if( psBand->eType != CXT_Element + || !EQUAL(psBand->pszValue,"BandMapping") ) + continue; + +/* -------------------------------------------------------------------- */ +/* Source band */ +/* -------------------------------------------------------------------- */ + if( psWO->panSrcBands == NULL ) + psWO->panSrcBands = (int *)CPLMalloc(sizeof(int)*psWO->nBandCount); + + pszValue = CPLGetXMLValue(psBand,"src",NULL); + if( pszValue == NULL ) + psWO->panSrcBands[iBand] = iBand+1; + else + psWO->panSrcBands[iBand] = atoi(pszValue); + +/* -------------------------------------------------------------------- */ +/* Destination band. */ +/* -------------------------------------------------------------------- */ + pszValue = CPLGetXMLValue(psBand,"dst",NULL); + if( pszValue != NULL ) + { + if( psWO->panDstBands == NULL ) + psWO->panDstBands = + (int *) CPLMalloc(sizeof(int)*psWO->nBandCount); + + psWO->panDstBands[iBand] = atoi(pszValue); + } + +/* -------------------------------------------------------------------- */ +/* Source nodata. */ +/* -------------------------------------------------------------------- */ + pszValue = CPLGetXMLValue(psBand,"SrcNoDataReal",NULL); + if( pszValue != NULL ) + { + if( psWO->padfSrcNoDataReal == NULL ) + psWO->padfSrcNoDataReal = + (double *) CPLCalloc(sizeof(double),psWO->nBandCount); + + psWO->padfSrcNoDataReal[iBand] = CPLAtof(pszValue); + } + + pszValue = CPLGetXMLValue(psBand,"SrcNoDataImag",NULL); + if( pszValue != NULL ) + { + if( psWO->padfSrcNoDataImag == NULL ) + psWO->padfSrcNoDataImag = + (double *) CPLCalloc(sizeof(double),psWO->nBandCount); + + psWO->padfSrcNoDataImag[iBand] = CPLAtof(pszValue); + } + +/* -------------------------------------------------------------------- */ +/* Destination nodata. */ +/* -------------------------------------------------------------------- */ + pszValue = CPLGetXMLValue(psBand,"DstNoDataReal",NULL); + if( pszValue != NULL ) + { + if( psWO->padfDstNoDataReal == NULL ) + psWO->padfDstNoDataReal = + (double *) CPLCalloc(sizeof(double),psWO->nBandCount); + + psWO->padfDstNoDataReal[iBand] = CPLAtof(pszValue); + } + + pszValue = CPLGetXMLValue(psBand,"DstNoDataImag",NULL); + if( pszValue != NULL ) + { + if( psWO->padfDstNoDataImag == NULL ) + psWO->padfDstNoDataImag = + (double *) CPLCalloc(sizeof(double),psWO->nBandCount); + + psWO->padfDstNoDataImag[iBand] = CPLAtof(pszValue); + } + + iBand++; + } + +/* -------------------------------------------------------------------- */ +/* Alpha bands. */ +/* -------------------------------------------------------------------- */ + psWO->nSrcAlphaBand = + atoi( CPLGetXMLValue( psTree, "SrcAlphaBand", "0" ) ); + psWO->nDstAlphaBand = + atoi( CPLGetXMLValue( psTree, "DstAlphaBand", "0" ) ); + +/* -------------------------------------------------------------------- */ +/* Cutline. */ +/* -------------------------------------------------------------------- */ + const char *pszWKT = CPLGetXMLValue( psTree, "Cutline", NULL ); + if( pszWKT ) + { + OGR_G_CreateFromWkt( (char **) &pszWKT, NULL, + (OGRGeometryH *) (&psWO->hCutline) ); + } + + psWO->dfCutlineBlendDist = + CPLAtof( CPLGetXMLValue( psTree, "CutlineBlendDist", "0" ) ); + +/* -------------------------------------------------------------------- */ +/* Transformation. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psTransformer = CPLGetXMLNode( psTree, "Transformer" ); + + if( psTransformer != NULL && psTransformer->psChild != NULL ) + { + GDALDeserializeTransformer( psTransformer->psChild, + &(psWO->pfnTransformer), + &(psWO->pTransformerArg) ); + } + +/* -------------------------------------------------------------------- */ +/* If any error has occured, cleanup else return success. */ +/* -------------------------------------------------------------------- */ + if( CPLGetLastErrorNo() != CE_None ) + { + if ( psWO->pTransformerArg ) + { + GDALDestroyTransformer( psWO->pTransformerArg ); + psWO->pTransformerArg = NULL; + } + if( psWO->hSrcDS != NULL ) + { + GDALClose( psWO->hSrcDS ); + psWO->hSrcDS = NULL; + } + if( psWO->hDstDS != NULL ) + { + GDALClose( psWO->hDstDS ); + psWO->hDstDS = NULL; + } + GDALDestroyWarpOptions( psWO ); + return NULL; + } + else + return psWO; +} diff --git a/bazaar/plugin/gdal/alg/gdalwarper.h b/bazaar/plugin/gdal/alg/gdalwarper.h new file mode 100644 index 000000000..4221bf1c2 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalwarper.h @@ -0,0 +1,450 @@ +/****************************************************************************** + * $Id: gdalwarper.h 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: GDAL High Performance Warper + * Purpose: Prototypes, and definitions for warping related work. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDALWARPER_H_INCLUDED +#define GDALWARPER_H_INCLUDED + +/** + * \file gdalwarper.h + * + * GDAL warper related entry points and definitions. Eventually it is + * expected that this file will be mostly private to the implementation, + * and the public C entry points will be available in gdal_alg.h. + */ + +#include "gdal_alg.h" +#include "cpl_minixml.h" +#include "cpl_multiproc.h" + +CPL_C_START + +/* Note: values are selected to be consistent with GDALRIOResampleAlg of gcore/gdal.h */ +/*! Warp Resampling Algorithm */ +typedef enum { + /*! Nearest neighbour (select on one input pixel) */ GRA_NearestNeighbour=0, + /*! Bilinear (2x2 kernel) */ GRA_Bilinear=1, + /*! Cubic Convolution Approximation (4x4 kernel) */ GRA_Cubic=2, + /*! Cubic B-Spline Approximation (4x4 kernel) */ GRA_CubicSpline=3, + /*! Lanczos windowed sinc interpolation (6x6 kernel) */ GRA_Lanczos=4, + /*! Average (computes the average of all non-NODATA contributing pixels) */ GRA_Average=5, + /*! Mode (selects the value which appears most often of all the sampled points) */ GRA_Mode=6, + // GRA_Gauss=7 reserved. + /*! Max (selects maximum of all non-NODATA contributing pixels) */ GRA_Max=8, + /*! Min (selects minimum of all non-NODATA contributing pixels) */ GRA_Min=9, + /*! Med (selects median of all non-NODATA contributing pixels) */ GRA_Med=10, + /*! Q1 (selects first quartile of all non-NODATA contributing pixels) */ GRA_Q1=11, + /*! Q3 (selects third quartile of all non-NODATA contributing pixels) */ GRA_Q3=12 +} GDALResampleAlg; + +/*! GWKAverageOrMode Algorithm */ +typedef enum { + /*! Average */ GWKAOM_Average=1, + /*! Mode */ GWKAOM_Fmode=2, + /*! Mode of GDT_Byte, GDT_UInt16, or GDT_Int16 */ GWKAOM_Imode=3, + /*! Maximum */ GWKAOM_Max=4, + /*! Minimum */ GWKAOM_Min=5, + /*! Quantile */ GWKAOM_Quant=6 +} GWKAverageOrModeAlg; + +typedef int +(*GDALMaskFunc)( void *pMaskFuncArg, + int nBandCount, GDALDataType eType, + int nXOff, int nYOff, + int nXSize, int nYSize, + GByte **papabyImageData, + int bMaskIsFloat, void *pMask ); + +CPLErr CPL_DLL +GDALWarpNoDataMasker( void *pMaskFuncArg, int nBandCount, GDALDataType eType, + int nXOff, int nYOff, int nXSize, int nYSize, + GByte **papabyImageData, int bMaskIsFloat, + void *pValidityMask, int* pbOutAllValid ); + +CPLErr CPL_DLL +GDALWarpDstAlphaMasker( void *pMaskFuncArg, int nBandCount, GDALDataType eType, + int nXOff, int nYOff, int nXSize, int nYSize, + GByte ** /*ppImageData */, + int bMaskIsFloat, void *pValidityMask ); +CPLErr CPL_DLL +GDALWarpSrcAlphaMasker( void *pMaskFuncArg, int nBandCount, GDALDataType eType, + int nXOff, int nYOff, int nXSize, int nYSize, + GByte ** /*ppImageData */, + int bMaskIsFloat, void *pValidityMask, int* pbOutAllOpaque ); + +CPLErr CPL_DLL +GDALWarpSrcMaskMasker( void *pMaskFuncArg, int nBandCount, GDALDataType eType, + int nXOff, int nYOff, int nXSize, int nYSize, + GByte ** /*ppImageData */, + int bMaskIsFloat, void *pValidityMask ); + +CPLErr CPL_DLL +GDALWarpCutlineMasker( void *pMaskFuncArg, int nBandCount, GDALDataType eType, + int nXOff, int nYOff, int nXSize, int nYSize, + GByte ** /* ppImageData */, + int bMaskIsFloat, void *pValidityMask ); + +/************************************************************************/ +/* GDALWarpOptions */ +/************************************************************************/ + +/** Warp control options for use with GDALWarpOperation::Initialize() */ +typedef struct { + + char **papszWarpOptions; + + /*! In bytes, 0.0 for internal default */ + double dfWarpMemoryLimit; + + /*! Resampling algorithm to use */ + GDALResampleAlg eResampleAlg; + + /*! data type to use during warp operation, GDT_Unknown lets the algorithm + select the type */ + GDALDataType eWorkingDataType; + + /*! Source image dataset. */ + GDALDatasetH hSrcDS; + + /*! Destination image dataset - may be NULL if only using GDALWarpOperation::WarpRegionToBuffer(). */ + GDALDatasetH hDstDS; + + /*! Number of bands to process, may be 0 to select all bands. */ + int nBandCount; + + /*! The band numbers for the source bands to process (1 based) */ + int *panSrcBands; + + /*! The band numbers for the destination bands to process (1 based) */ + int *panDstBands; + + /*! The source band so use as an alpha (transparency) value, 0=disabled */ + int nSrcAlphaBand; + + /*! The dest. band so use as an alpha (transparency) value, 0=disabled */ + int nDstAlphaBand; + + /*! The "nodata" value real component for each input band, if NULL there isn't one */ + double *padfSrcNoDataReal; + /*! The "nodata" value imaginary component - may be NULL even if real + component is provided. */ + double *padfSrcNoDataImag; + + /*! The "nodata" value real component for each output band, if NULL there isn't one */ + double *padfDstNoDataReal; + /*! The "nodata" value imaginary component - may be NULL even if real + component is provided. */ + double *padfDstNoDataImag; + + /*! GDALProgressFunc() compatible progress reporting function, or NULL + if there isn't one. */ + GDALProgressFunc pfnProgress; + + /*! Callback argument to be passed to pfnProgress. */ + void *pProgressArg; + + /*! Type of spatial point transformer function */ + GDALTransformerFunc pfnTransformer; + + /*! Handle to image transformer setup structure */ + void *pTransformerArg; + + GDALMaskFunc *papfnSrcPerBandValidityMaskFunc; + void **papSrcPerBandValidityMaskFuncArg; + + GDALMaskFunc pfnSrcValidityMaskFunc; + void *pSrcValidityMaskFuncArg; + + GDALMaskFunc pfnSrcDensityMaskFunc; + void *pSrcDensityMaskFuncArg; + + GDALMaskFunc pfnDstDensityMaskFunc; + void *pDstDensityMaskFuncArg; + + GDALMaskFunc pfnDstValidityMaskFunc; + void *pDstValidityMaskFuncArg; + + CPLErr (*pfnPreWarpChunkProcessor)( void *pKern, void *pArg ); + void *pPreWarpProcessorArg; + + CPLErr (*pfnPostWarpChunkProcessor)( void *pKern, void *pArg); + void *pPostWarpProcessorArg; + + /*! Optional OGRPolygonH for a masking cutline. */ + void *hCutline; + + /*! Optional blending distance to apply across cutline in pixels, default is zero. */ + double dfCutlineBlendDist; + +} GDALWarpOptions; + +GDALWarpOptions CPL_DLL * CPL_STDCALL GDALCreateWarpOptions(void); +void CPL_DLL CPL_STDCALL GDALDestroyWarpOptions( GDALWarpOptions * ); +GDALWarpOptions CPL_DLL * CPL_STDCALL +GDALCloneWarpOptions( const GDALWarpOptions * ); + +CPLXMLNode CPL_DLL * CPL_STDCALL + GDALSerializeWarpOptions( const GDALWarpOptions * ); +GDALWarpOptions CPL_DLL * CPL_STDCALL + GDALDeserializeWarpOptions( CPLXMLNode * ); + +/************************************************************************/ +/* GDALReprojectImage() */ +/************************************************************************/ + +CPLErr CPL_DLL CPL_STDCALL +GDALReprojectImage( GDALDatasetH hSrcDS, const char *pszSrcWKT, + GDALDatasetH hDstDS, const char *pszDstWKT, + GDALResampleAlg eResampleAlg, double dfWarpMemoryLimit, + double dfMaxError, + GDALProgressFunc pfnProgress, void *pProgressArg, + GDALWarpOptions *psOptions ); + +CPLErr CPL_DLL CPL_STDCALL +GDALCreateAndReprojectImage( GDALDatasetH hSrcDS, const char *pszSrcWKT, + const char *pszDstFilename, const char *pszDstWKT, + GDALDriverH hDstDriver, char **papszCreateOptions, + GDALResampleAlg eResampleAlg, double dfWarpMemoryLimit, + double dfMaxError, + GDALProgressFunc pfnProgress, void *pProgressArg, + GDALWarpOptions *psOptions ); + +/************************************************************************/ +/* VRTWarpedDataset */ +/************************************************************************/ + +GDALDatasetH CPL_DLL CPL_STDCALL +GDALAutoCreateWarpedVRT( GDALDatasetH hSrcDS, + const char *pszSrcWKT, const char *pszDstWKT, + GDALResampleAlg eResampleAlg, + double dfMaxError, const GDALWarpOptions *psOptions ); + +GDALDatasetH CPL_DLL CPL_STDCALL +GDALCreateWarpedVRT( GDALDatasetH hSrcDS, + int nPixels, int nLines, double *padfGeoTransform, + GDALWarpOptions *psOptions ); + +CPLErr CPL_DLL CPL_STDCALL +GDALInitializeWarpedVRT( GDALDatasetH hDS, + GDALWarpOptions *psWO ); + +CPL_C_END + +#ifdef __cplusplus + +/************************************************************************/ +/* GDALWarpKernel */ +/* */ +/* This class represents the lowest level of abstraction. It */ +/* is holds the imagery for one "chunk" of a warp, and the */ +/* pre-prepared masks. All IO is done before and after it's */ +/* operation. This class is not normally used by the */ +/* application. */ +/************************************************************************/ + +// This is the number of dummy pixels that must be reserved in source arrays +// in order to satisfy assumptions made in GWKResample(), and more specifically +// by GWKGetPixelRow() that always read a even number of pixels. So if we are +// in the situation to read the last pixel of the source array, we need 1 extra +// dummy pixel to avoid reading out of bounds. +#define WARP_EXTRA_ELTS 1 + +class CPL_DLL GDALWarpKernel +{ +public: + char **papszWarpOptions; + + GDALResampleAlg eResample; + GDALDataType eWorkingDataType; + int nBands; + + int nSrcXSize; + int nSrcYSize; + int nSrcXExtraSize; /* extra pixels (included in nSrcXSize) reserved for filter window. Should be ignored in scale computation */ + int nSrcYExtraSize; /* extra pixels (included in nSrcYSize) reserved for filter window. Should be ignored in scale computation */ + GByte **papabySrcImage; /* each subarray must have WARP_EXTRA_ELTS at the end */ + + GUInt32 **papanBandSrcValid; /* each subarray must have WARP_EXTRA_ELTS at the end */ + GUInt32 *panUnifiedSrcValid; /* must have WARP_EXTRA_ELTS at the end */ + float *pafUnifiedSrcDensity; /* must have WARP_EXTRA_ELTS at the end */ + + int nDstXSize; + int nDstYSize; + GByte **papabyDstImage; + GUInt32 *panDstValid; + float *pafDstDensity; + + double dfXScale; // Resampling scale, i.e. + double dfYScale; // nDstSize/nSrcSize. + double dfXFilter; // Size of filter kernel. + double dfYFilter; + int nXRadius; // Size of window to filter. + int nYRadius; + int nFiltInitX; // Filtering offset + int nFiltInitY; + + int nSrcXOff; + int nSrcYOff; + + int nDstXOff; + int nDstYOff; + + GDALTransformerFunc pfnTransformer; + void *pTransformerArg; + + GDALProgressFunc pfnProgress; + void *pProgress; + + double dfProgressBase; + double dfProgressScale; + + double *padfDstNoDataReal; + + GDALWarpKernel(); + virtual ~GDALWarpKernel(); + + CPLErr Validate(); + CPLErr PerformWarp(); +}; + +/************************************************************************/ +/* GDALWarpOperation() */ +/* */ +/* This object is application created, or created by a higher */ +/* level convenience function. It is responsible for */ +/* subdividing the operation into chunks, loading and saving */ +/* imagery, and establishing the varios validity and density */ +/* masks. Actual resampling is done by the GDALWarpKernel. */ +/************************************************************************/ + +typedef struct _GDALWarpChunk GDALWarpChunk; + +class CPL_DLL GDALWarpOperation { +private: + GDALWarpOptions *psOptions; + + void WipeOptions(); + int ValidateOptions(); + + CPLErr ComputeSourceWindow( int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + int *pnSrcXOff, int *pnSrcYOff, + int *pnSrcXSize, int *pnSrcYSize, + int *pnSrcXExtraSize, int *pnSrcYExtraSize, + double* pdfSrcFillRatio ); + + CPLErr CreateKernelMask( GDALWarpKernel *, int iBand, + const char *pszType ); + + CPLMutex *hIOMutex; + CPLMutex *hWarpMutex; + + int nChunkListCount; + int nChunkListMax; + GDALWarpChunk *pasChunkList; + + int bReportTimings; + unsigned long nLastTimeReported; + + void WipeChunkList(); + CPLErr CollectChunkList( int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize ); + void ReportTiming( const char * ); + +public: + GDALWarpOperation(); + virtual ~GDALWarpOperation(); + + CPLErr Initialize( const GDALWarpOptions *psNewOptions ); + + const GDALWarpOptions *GetOptions(); + + CPLErr ChunkAndWarpImage( int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize ); + CPLErr ChunkAndWarpMulti( int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize ); + CPLErr WarpRegion( int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + int nSrcXOff=0, int nSrcYOff=0, + int nSrcXSize=0, int nSrcYSize=0, + double dfProgressBase=0.0, double dfProgressScale=1.0); + CPLErr WarpRegion( int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + int nSrcXOff, int nSrcYOff, + int nSrcXSize, int nSrcYSize, + int nSrcXExtraSize, int nSrcYExtraSize, + double dfProgressBase, double dfProgressScale); + CPLErr WarpRegionToBuffer( int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + void *pDataBuf, + GDALDataType eBufDataType, + int nSrcXOff=0, int nSrcYOff=0, + int nSrcXSize=0, int nSrcYSize=0, + double dfProgressBase=0.0, double dfProgressScale=1.0); + CPLErr WarpRegionToBuffer( int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + void *pDataBuf, + GDALDataType eBufDataType, + int nSrcXOff, int nSrcYOff, + int nSrcXSize, int nSrcYSize, + int nSrcXExtraSize, int nSrcYExtraSize, + double dfProgressBase, double dfProgressScale); +}; + +#endif /* def __cplusplus */ + +CPL_C_START + +typedef void * GDALWarpOperationH; + +GDALWarpOperationH CPL_DLL GDALCreateWarpOperation(const GDALWarpOptions* ); +void CPL_DLL GDALDestroyWarpOperation( GDALWarpOperationH ); +CPLErr CPL_DLL GDALChunkAndWarpImage( GDALWarpOperationH, int, int, int, int ); +CPLErr CPL_DLL GDALChunkAndWarpMulti( GDALWarpOperationH, int, int, int, int ); +CPLErr CPL_DLL GDALWarpRegion( GDALWarpOperationH, + int, int, int, int, int, int, int, int ); +CPLErr CPL_DLL GDALWarpRegionToBuffer( GDALWarpOperationH, int, int, int, int, + void *, GDALDataType, + int, int, int, int ); + +/************************************************************************/ +/* Warping kernel functions */ +/************************************************************************/ + +int GWKGetFilterRadius(GDALResampleAlg eResampleAlg); + +typedef double (*FilterFuncType)(double dfX); +FilterFuncType GWKGetFilterFunc(GDALResampleAlg eResampleAlg); + +typedef double (*FilterFunc4ValuesType)(double* padfVals); +FilterFunc4ValuesType GWKGetFilterFunc4Values(GDALResampleAlg eResampleAlg); + +CPL_C_END + +#endif /* ndef GDAL_ALG_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/alg/gdalwarpkernel.cpp b/bazaar/plugin/gdal/alg/gdalwarpkernel.cpp new file mode 100644 index 000000000..27b767901 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalwarpkernel.cpp @@ -0,0 +1,5081 @@ +/****************************************************************************** + * $Id: gdalwarpkernel.cpp 28946 2015-04-18 20:12:47Z rouault $ + * + * Project: High Performance Image Reprojector + * Purpose: Implementation of the GDALWarpKernel class. Implements the actual + * image warping for a "chunk" of input and output imagery already + * loaded into memory. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include +#include +#include +#include "gdalwarper.h" +#include "gdal_alg_priv.h" +#include "cpl_string.h" +#include "gdalwarpkernel_opencl.h" +#include "cpl_atomic_ops.h" +#include "cpl_multiproc.h" +#include + +CPL_CVSID("$Id: gdalwarpkernel.cpp 28946 2015-04-18 20:12:47Z rouault $"); + +//#define INSTANCIATE_FLOAT64_SSE2_IMPL + +static const int anGWKFilterRadius[] = +{ + 0, // Nearest neighbour + 1, // Bilinear + 2, // Cubic Convolution (Catmull-Rom) + 2, // Cubic B-Spline + 3, // Lanczos windowed sinc + 0, // Average + 0, // Mode + 0, // Reserved GRA_Gauss=7 + 0, // Max + 0, // Min + 0, // Med + 0, // Q1 + 0, // Q3 +}; + +static double GWKBilinear(double dfX); +static double GWKCubic(double dfX); +static double GWKBSpline(double dfX); +static double GWKLanczosSinc(double dfX); + +static const FilterFuncType apfGWKFilter[] = +{ + NULL, // Nearest neighbour + GWKBilinear, // Bilinear + GWKCubic, // Cubic Convolution (Catmull-Rom) + GWKBSpline, // Cubic B-Spline + GWKLanczosSinc, // Lanczos windowed sinc + NULL, // Average + NULL, // Mode + NULL, // Reserved GRA_Gauss=7 + NULL, // Max + NULL, // Min + NULL, // Med + NULL, // Q1 + NULL, // Q3 +}; + +static double GWKBilinear4Values(double* padfVals); +static double GWKCubic4Values(double* padfVals); +static double GWKBSpline4Values(double* padfVals); +static double GWKLanczosSinc4Values(double* padfVals); + +static const FilterFunc4ValuesType apfGWKFilter4Values[] = +{ + NULL, // Nearest neighbour + GWKBilinear4Values, // Bilinear + GWKCubic4Values, // Cubic Convolution (Catmull-Rom) + GWKBSpline4Values, // Cubic B-Spline + GWKLanczosSinc4Values, // Lanczos windowed sinc + NULL, // Average + NULL, // Mode + NULL, // Reserved GRA_Gauss=7 + NULL, // Max + NULL, // Min + NULL, // Med + NULL, // Q1 + NULL, // Q3 +}; + +int GWKGetFilterRadius(GDALResampleAlg eResampleAlg) +{ + return anGWKFilterRadius[eResampleAlg]; +} + +FilterFuncType GWKGetFilterFunc(GDALResampleAlg eResampleAlg) +{ + return apfGWKFilter[eResampleAlg]; +} + +FilterFunc4ValuesType GWKGetFilterFunc4Values(GDALResampleAlg eResampleAlg) +{ + return apfGWKFilter4Values[eResampleAlg]; +} + +#ifdef HAVE_OPENCL +static CPLErr GWKOpenCLCase( GDALWarpKernel * ); +#endif + +static CPLErr GWKGeneralCase( GDALWarpKernel * ); +static CPLErr GWKNearestNoMasksOrDstDensityOnlyByte( GDALWarpKernel *poWK ); +static CPLErr GWKBilinearNoMasksOrDstDensityOnlyByte( GDALWarpKernel *poWK ); +static CPLErr GWKCubicNoMasksOrDstDensityOnlyByte( GDALWarpKernel *poWK ); +static CPLErr GWKCubicNoMasksOrDstDensityOnlyFloat( GDALWarpKernel *poWK ); +#ifdef INSTANCIATE_FLOAT64_SSE2_IMPL +static CPLErr GWKCubicNoMasksOrDstDensityOnlyDouble( GDALWarpKernel *poWK ); +#endif +static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyByte( GDALWarpKernel *poWK ); +static CPLErr GWKNearestByte( GDALWarpKernel *poWK ); +static CPLErr GWKNearestNoMasksOrDstDensityOnlyShort( GDALWarpKernel *poWK ); +static CPLErr GWKBilinearNoMasksOrDstDensityOnlyShort( GDALWarpKernel *poWK ); +static CPLErr GWKBilinearNoMasksOrDstDensityOnlyFloat( GDALWarpKernel *poWK ); +#ifdef INSTANCIATE_FLOAT64_SSE2_IMPL +static CPLErr GWKBilinearNoMasksOrDstDensityOnlyDouble( GDALWarpKernel *poWK ); +#endif +static CPLErr GWKCubicNoMasksOrDstDensityOnlyShort( GDALWarpKernel *poWK ); +static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyShort( GDALWarpKernel *poWK ); +static CPLErr GWKNearestShort( GDALWarpKernel *poWK ); +static CPLErr GWKNearestNoMasksOrDstDensityOnlyFloat( GDALWarpKernel *poWK ); +static CPLErr GWKNearestFloat( GDALWarpKernel *poWK ); +static CPLErr GWKAverageOrMode( GDALWarpKernel * ); +static CPLErr GWKCubicNoMasksOrDstDensityOnlyUShort( GDALWarpKernel * ); +static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyUShort( GDALWarpKernel * ); +static CPLErr GWKBilinearNoMasksOrDstDensityOnlyUShort( GDALWarpKernel * ); + +/************************************************************************/ +/* GWKJobStruct */ +/************************************************************************/ + +typedef struct _GWKJobStruct GWKJobStruct; + +struct _GWKJobStruct +{ + CPLJoinableThread *hThread; + GDALWarpKernel *poWK; + int iYMin; + int iYMax; + volatile int *pnCounter; + volatile int *pbStop; + CPLCond *hCond; + CPLMutex *hCondMutex; + int (*pfnProgress)(GWKJobStruct* psJob); + void *pTransformerArg; +} ; + +/************************************************************************/ +/* GWKProgressThread() */ +/************************************************************************/ + +/* Return TRUE if the computation must be interrupted */ +static int GWKProgressThread(GWKJobStruct* psJob) +{ + CPLAcquireMutex(psJob->hCondMutex, 1.0); + (*(psJob->pnCounter)) ++; + CPLCondSignal(psJob->hCond); + int bStop = *(psJob->pbStop); + CPLReleaseMutex(psJob->hCondMutex); + + return bStop; +} + +/************************************************************************/ +/* GWKProgressMonoThread() */ +/************************************************************************/ + +/* Return TRUE if the computation must be interrupted */ +static int GWKProgressMonoThread(GWKJobStruct* psJob) +{ + GDALWarpKernel *poWK = psJob->poWK; + int nCounter = ++(*(psJob->pnCounter)); + if( !poWK->pfnProgress( poWK->dfProgressBase + poWK->dfProgressScale * + (nCounter / (double) psJob->iYMax), + "", poWK->pProgress ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + *(psJob->pbStop) = TRUE; + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* GWKGenericMonoThread() */ +/************************************************************************/ + +static CPLErr GWKGenericMonoThread( GDALWarpKernel *poWK, + void (*pfnFunc) (void *pUserData) ) +{ + volatile int bStop = FALSE; + volatile int nCounter = 0; + + GWKJobStruct sThreadJob; + sThreadJob.poWK = poWK; + sThreadJob.pnCounter = &nCounter; + sThreadJob.iYMin = 0; + sThreadJob.iYMax = poWK->nDstYSize; + sThreadJob.pbStop = &bStop; + sThreadJob.hCond = NULL; + sThreadJob.hCondMutex = NULL; + sThreadJob.hThread = NULL; + sThreadJob.pfnProgress = GWKProgressMonoThread; + sThreadJob.pTransformerArg = poWK->pTransformerArg; + + pfnFunc(&sThreadJob); + + return !bStop ? CE_None : CE_Failure; +} + +/************************************************************************/ +/* GWKRun() */ +/************************************************************************/ + +static CPLErr GWKRun( GDALWarpKernel *poWK, + const char* pszFuncName, + void (*pfnFunc) (void *pUserData) ) + +{ + int nDstYSize = poWK->nDstYSize; + + CPLDebug( "GDAL", "GDALWarpKernel()::%s()\n" + "Src=%d,%d,%dx%d Dst=%d,%d,%dx%d", + pszFuncName, + poWK->nSrcXOff, poWK->nSrcYOff, + poWK->nSrcXSize, poWK->nSrcYSize, + poWK->nDstXOff, poWK->nDstYOff, + poWK->nDstXSize, poWK->nDstYSize ); + + if( !poWK->pfnProgress( poWK->dfProgressBase, "", poWK->pProgress ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + + const char* pszWarpThreads = CSLFetchNameValue(poWK->papszWarpOptions, "NUM_THREADS"); + int nThreads; + if (pszWarpThreads == NULL) + pszWarpThreads = CPLGetConfigOption("GDAL_NUM_THREADS", "1"); + if (EQUAL(pszWarpThreads, "ALL_CPUS")) + nThreads = CPLGetNumCPUs(); + else + nThreads = atoi(pszWarpThreads); + if (nThreads > 128) + nThreads = 128; + if (nThreads >= nDstYSize / 2) + nThreads = nDstYSize / 2; + + if (nThreads <= 1) + { + return GWKGenericMonoThread(poWK, pfnFunc); + } + else + { + GWKJobStruct* pasThreadJob = + (GWKJobStruct*)CPLCalloc(sizeof(GWKJobStruct), nThreads); + +/* -------------------------------------------------------------------- */ +/* Duplicate pTransformerArg per thread. */ +/* -------------------------------------------------------------------- */ + int i; + int bTransformerCloningSuccess = TRUE; + + for(i=0;ipTransformerArg); + if( pasThreadJob[i].pTransformerArg == NULL ) + { + CPLDebug("WARP", "Cannot deserialize transformer"); + bTransformerCloningSuccess = FALSE; + break; + } + } + + if (!bTransformerCloningSuccess) + { + for(i=0;ipfnProgress != GDALDummyProgress ) + pasThreadJob[i].pfnProgress = GWKProgressThread; + else + pasThreadJob[i].pfnProgress = NULL; + pasThreadJob[i].hThread = CPLCreateJoinableThread( pfnFunc, + (void*) &pasThreadJob[i] ); + } + +/* -------------------------------------------------------------------- */ +/* Report progress. */ +/* -------------------------------------------------------------------- */ + if( poWK->pfnProgress != GDALDummyProgress ) + { + while(nCounter < nDstYSize) + { + CPLCondWait(hCond, hCondMutex); + + if( !poWK->pfnProgress( poWK->dfProgressBase + poWK->dfProgressScale * + (nCounter / (double) nDstYSize), + "", poWK->pProgress ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + bStop = TRUE; + break; + } + } + } + + /* Release mutex before joining threads, otherwise they will dead-lock */ + /* forever in GWKProgressThread() */ + CPLReleaseMutex(hCondMutex); + +/* -------------------------------------------------------------------- */ +/* Wait for all threads to complete and finish. */ +/* -------------------------------------------------------------------- */ + for(i=0;iDesign Issues + * + * My intention is that PerformWarp() would analyse the setup in terms + * of the datatype, resampling type, and validity/density mask usage and + * pick one of many specific implementations of the warping algorithm over + * a continuim of optimization vs. generality. At one end there will be a + * reference general purpose implementation of the algorithm that supports + * any data type (working internally in double precision complex), all three + * resampling types, and any or all of the validity/density masks. At the + * other end would be highly optimized algorithms for common cases like + * nearest neighbour resampling on GDT_Byte data with no masks. + * + * The full set of optimized versions have not been decided but we should + * expect to have at least: + * - One for each resampling algorithm for 8bit data with no masks. + * - One for each resampling algorithm for float data with no masks. + * - One for each resampling algorithm for float data with any/all masks + * (essentially the generic case for just float data). + * - One for each resampling algorithm for 8bit data with support for + * input validity masks (per band or per pixel). This handles the common + * case of nodata masking. + * - One for each resampling algorithm for float data with support for + * input validity masks (per band or per pixel). This handles the common + * case of nodata masking. + * + * Some of the specializations would operate on all bands in one pass + * (especially the ones without masking would do this), while others might + * process each band individually to reduce code complexity. + * + *

Masking Semantics

+ * + * A detailed explanation of the semantics of the validity and density masks, + * and their effects on resampling kernels is needed here. + */ + +/************************************************************************/ +/* GDALWarpKernel Data Members */ +/************************************************************************/ + +/** + * \var GDALResampleAlg GDALWarpKernel::eResample; + * + * Resampling algorithm. + * + * The resampling algorithm to use. One of GRA_NearestNeighbour, GRA_Bilinear, + * GRA_Cubic, GRA_CubicSpline, GRA_Lanczos, GRA_Average, or GRA_Mode. + * + * This field is required. GDT_NearestNeighbour may be used as a default + * value. + */ + +/** + * \var GDALDataType GDALWarpKernel::eWorkingDataType; + * + * Working pixel data type. + * + * The datatype of pixels in the source image (papabySrcimage) and + * destination image (papabyDstImage) buffers. Note that operations on + * some data types (such as GDT_Byte) may be much better optimized than other + * less common cases. + * + * This field is required. It may not be GDT_Unknown. + */ + +/** + * \var int GDALWarpKernel::nBands; + * + * Number of bands. + * + * The number of bands (layers) of imagery being warped. Determines the + * number of entries in the papabySrcImage, papanBandSrcValid, + * and papabyDstImage arrays. + * + * This field is required. + */ + +/** + * \var int GDALWarpKernel::nSrcXSize; + * + * Source image width in pixels. + * + * This field is required. + */ + +/** + * \var int GDALWarpKernel::nSrcYSize; + * + * Source image height in pixels. + * + * This field is required. + */ + +/** + * \var int GDALWarpKernel::nSrcXExtraSize; + * + * Number of pixels included in nSrcXSize that are present on the edges of + * the area of interest to take into account the width of the kernel. + * + * This field is required. + */ + +/** + * \var int GDALWarpKernel::nSrcYExtraSize; + * + * Number of pixels included in nSrcYExtraSize that are present on the edges of + * the area of interest to take into account the height of the kernel. + * + * This field is required. + */ + +/** + * \var int GDALWarpKernel::papabySrcImage; + * + * Array of source image band data. + * + * This is an array of pointers (of size GDALWarpKernel::nBands) pointers + * to image data. Each individual band of image data is organized as a single + * block of image data in left to right, then bottom to top order. The actual + * type of the image data is determined by GDALWarpKernel::eWorkingDataType. + * + * To access the the pixel value for the (x=3,y=4) pixel (zero based) of + * the second band with eWorkingDataType set to GDT_Float32 use code like + * this: + * + * \code + * float dfPixelValue; + * int nBand = 1; // band indexes are zero based. + * int nPixel = 3; // zero based + * int nLine = 4; // zero based + * + * assert( nPixel >= 0 && nPixel < poKern->nSrcXSize ); + * assert( nLine >= 0 && nLine < poKern->nSrcYSize ); + * assert( nBand >= 0 && nBand < poKern->nBands ); + * dfPixelValue = ((float *) poKern->papabySrcImage[nBand-1]) + * [nPixel + nLine * poKern->nSrcXSize]; + * \endcode + * + * This field is required. + */ + +/** + * \var GUInt32 **GDALWarpKernel::papanBandSrcValid; + * + * Per band validity mask for source pixels. + * + * Array of pixel validity mask layers for each source band. Each of + * the mask layers is the same size (in pixels) as the source image with + * one bit per pixel. Note that it is legal (and common) for this to be + * NULL indicating that none of the pixels are invalidated, or for some + * band validity masks to be NULL in which case all pixels of the band are + * valid. The following code can be used to test the validity of a particular + * pixel. + * + * \code + * int bIsValid = TRUE; + * int nBand = 1; // band indexes are zero based. + * int nPixel = 3; // zero based + * int nLine = 4; // zero based + * + * assert( nPixel >= 0 && nPixel < poKern->nSrcXSize ); + * assert( nLine >= 0 && nLine < poKern->nSrcYSize ); + * assert( nBand >= 0 && nBand < poKern->nBands ); + * + * if( poKern->papanBandSrcValid != NULL + * && poKern->papanBandSrcValid[nBand] != NULL ) + * { + * GUInt32 *panBandMask = poKern->papanBandSrcValid[nBand]; + * int iPixelOffset = nPixel + nLine * poKern->nSrcXSize; + * + * bIsValid = panBandMask[iPixelOffset>>5] + * & (0x01 << (iPixelOffset & 0x1f)); + * } + * \endcode + */ + +/** + * \var GUInt32 *GDALWarpKernel::panUnifiedSrcValid; + * + * Per pixel validity mask for source pixels. + * + * A single validity mask layer that applies to the pixels of all source + * bands. It is accessed similarly to papanBandSrcValid, but without the + * extra level of band indirection. + * + * This pointer may be NULL indicating that all pixels are valid. + * + * Note that if both panUnifiedSrcValid, and papanBandSrcValid are available, + * the pixel isn't considered to be valid unless both arrays indicate it is + * valid. + */ + +/** + * \var float *GDALWarpKernel::pafUnifiedSrcDensity; + * + * Per pixel density mask for source pixels. + * + * A single density mask layer that applies to the pixels of all source + * bands. It contains values between 0.0 and 1.0 indicating the degree to + * which this pixel should be allowed to contribute to the output result. + * + * This pointer may be NULL indicating that all pixels have a density of 1.0. + * + * The density for a pixel may be accessed like this: + * + * \code + * float fDensity = 1.0; + * int nPixel = 3; // zero based + * int nLine = 4; // zero based + * + * assert( nPixel >= 0 && nPixel < poKern->nSrcXSize ); + * assert( nLine >= 0 && nLine < poKern->nSrcYSize ); + * if( poKern->pafUnifiedSrcDensity != NULL ) + * fDensity = poKern->pafUnifiedSrcDensity + * [nPixel + nLine * poKern->nSrcXSize]; + * \endcode + */ + +/** + * \var int GDALWarpKernel::nDstXSize; + * + * Width of destination image in pixels. + * + * This field is required. + */ + +/** + * \var int GDALWarpKernel::nDstYSize; + * + * Height of destination image in pixels. + * + * This field is required. + */ + +/** + * \var GByte **GDALWarpKernel::papabyDstImage; + * + * Array of destination image band data. + * + * This is an array of pointers (of size GDALWarpKernel::nBands) pointers + * to image data. Each individual band of image data is organized as a single + * block of image data in left to right, then bottom to top order. The actual + * type of the image data is determined by GDALWarpKernel::eWorkingDataType. + * + * To access the the pixel value for the (x=3,y=4) pixel (zero based) of + * the second band with eWorkingDataType set to GDT_Float32 use code like + * this: + * + * \code + * float dfPixelValue; + * int nBand = 1; // band indexes are zero based. + * int nPixel = 3; // zero based + * int nLine = 4; // zero based + * + * assert( nPixel >= 0 && nPixel < poKern->nDstXSize ); + * assert( nLine >= 0 && nLine < poKern->nDstYSize ); + * assert( nBand >= 0 && nBand < poKern->nBands ); + * dfPixelValue = ((float *) poKern->papabyDstImage[nBand-1]) + * [nPixel + nLine * poKern->nSrcYSize]; + * \endcode + * + * This field is required. + */ + +/** + * \var GUInt32 *GDALWarpKernel::panDstValid; + * + * Per pixel validity mask for destination pixels. + * + * A single validity mask layer that applies to the pixels of all destination + * bands. It is accessed similarly to papanUnitifiedSrcValid, but based + * on the size of the destination image. + * + * This pointer may be NULL indicating that all pixels are valid. + */ + +/** + * \var float *GDALWarpKernel::pafDstDensity; + * + * Per pixel density mask for destination pixels. + * + * A single density mask layer that applies to the pixels of all destination + * bands. It contains values between 0.0 and 1.0. + * + * This pointer may be NULL indicating that all pixels have a density of 1.0. + * + * The density for a pixel may be accessed like this: + * + * \code + * float fDensity = 1.0; + * int nPixel = 3; // zero based + * int nLine = 4; // zero based + * + * assert( nPixel >= 0 && nPixel < poKern->nDstXSize ); + * assert( nLine >= 0 && nLine < poKern->nDstYSize ); + * if( poKern->pafDstDensity != NULL ) + * fDensity = poKern->pafDstDensity[nPixel + nLine * poKern->nDstXSize]; + * \endcode + */ + +/** + * \var int GDALWarpKernel::nSrcXOff; + * + * X offset to source pixel coordinates for transformation. + * + * See pfnTransformer. + * + * This field is required. + */ + +/** + * \var int GDALWarpKernel::nSrcYOff; + * + * Y offset to source pixel coordinates for transformation. + * + * See pfnTransformer. + * + * This field is required. + */ + +/** + * \var int GDALWarpKernel::nDstXOff; + * + * X offset to destination pixel coordinates for transformation. + * + * See pfnTransformer. + * + * This field is required. + */ + +/** + * \var int GDALWarpKernel::nDstYOff; + * + * Y offset to destination pixel coordinates for transformation. + * + * See pfnTransformer. + * + * This field is required. + */ + +/** + * \var GDALTransformerFunc GDALWarpKernel::pfnTransformer; + * + * Source/destination location transformer. + * + * The function to call to transform coordinates between source image + * pixel/line coordinates and destination image pixel/line coordinates. + * See GDALTransformerFunc() for details of the semantics of this function. + * + * The GDALWarpKern algorithm will only ever use this transformer in + * "destination to source" mode (bDstToSrc=TRUE), and will always pass + * partial or complete scanlines of points in the destination image as + * input. This means, amoung other things, that it is safe to the the + * approximating transform GDALApproxTransform() as the transformation + * function. + * + * Source and destination images may be subsets of a larger overall image. + * The transformation algorithms will expect and return pixel/line coordinates + * in terms of this larger image, so coordinates need to be offset by + * the offsets specified in nSrcXOff, nSrcYOff, nDstXOff, and nDstYOff before + * passing to pfnTransformer, and after return from it. + * + * The GDALWarpKernel::pfnTransformerArg value will be passed as the callback + * data to this function when it is called. + * + * This field is required. + */ + +/** + * \var void *GDALWarpKernel::pTransformerArg; + * + * Callback data for pfnTransformer. + * + * This field may be NULL if not required for the pfnTransformer being used. + */ + +/** + * \var GDALProgressFunc GDALWarpKernel::pfnProgress; + * + * The function to call to report progress of the algorithm, and to check + * for a requested termination of the operation. It operates according to + * GDALProgressFunc() semantics. + * + * Generally speaking the progress function will be invoked for each + * scanline of the destination buffer that has been processed. + * + * This field may be NULL (internally set to GDALDummyProgress()). + */ + +/** + * \var void *GDALWarpKernel::pProgress; + * + * Callback data for pfnProgress. + * + * This field may be NULL if not required for the pfnProgress being used. + */ + + +/************************************************************************/ +/* GDALWarpKernel() */ +/************************************************************************/ + +GDALWarpKernel::GDALWarpKernel() + +{ + eResample = GRA_NearestNeighbour; + eWorkingDataType = GDT_Unknown; + nBands = 0; + nDstXOff = 0; + nDstYOff = 0; + nDstXSize = 0; + nDstYSize = 0; + nSrcXOff = 0; + nSrcYOff = 0; + nSrcXSize = 0; + nSrcYSize = 0; + nSrcXExtraSize = 0; + nSrcYExtraSize = 0; + dfXScale = 1.0; + dfYScale = 1.0; + dfXFilter = 0.0; + dfYFilter = 0.0; + nXRadius = 0; + nYRadius = 0; + nFiltInitX = 0; + nFiltInitY = 0; + pafDstDensity = NULL; + pafUnifiedSrcDensity = NULL; + panDstValid = NULL; + panUnifiedSrcValid = NULL; + papabyDstImage = NULL; + papabySrcImage = NULL; + papanBandSrcValid = NULL; + pfnProgress = GDALDummyProgress; + pProgress = NULL; + dfProgressBase = 0.0; + dfProgressScale = 1.0; + pfnTransformer = NULL; + pTransformerArg = NULL; + papszWarpOptions = NULL; +} + +/************************************************************************/ +/* ~GDALWarpKernel() */ +/************************************************************************/ + +GDALWarpKernel::~GDALWarpKernel() + +{ +} + +/************************************************************************/ +/* PerformWarp() */ +/************************************************************************/ + +/** + * \fn CPLErr GDALWarpKernel::PerformWarp(); + * + * This method performs the warp described in the GDALWarpKernel. + * + * @return CE_None on success or CE_Failure if an error occurs. + */ + +CPLErr GDALWarpKernel::PerformWarp() + +{ + CPLErr eErr; + + if( (eErr = Validate()) != CE_None ) + return eErr; + + // See #2445 and #3079 + if (nSrcXSize <= 0 || nSrcYSize <= 0) + { + if ( !pfnProgress( dfProgressBase + dfProgressScale, + "", pProgress ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Pre-calculate resampling scales and window sizes for filtering. */ +/* -------------------------------------------------------------------- */ + + dfXScale = (double)nDstXSize / (nSrcXSize - nSrcXExtraSize); + dfYScale = (double)nDstYSize / (nSrcYSize - nSrcYExtraSize); + if( nSrcXSize >= nDstXSize && nSrcXSize <= nDstXSize + nSrcXExtraSize ) + dfXScale = 1; + if( nSrcYSize >= nDstYSize && nSrcYSize <= nDstYSize + nSrcYExtraSize ) + dfYScale = 1; + if( dfXScale < 1 ) + { + double dfXReciprocalScale = 1. / dfXScale; + int nXReciprocalScale = (int)(dfXReciprocalScale + 0.5); + if( fabs(dfXReciprocalScale-nXReciprocalScale) < 0.05 ) + dfXScale = 1.0 / nXReciprocalScale; + } + if( dfYScale < 1 ) + { + double dfYReciprocalScale = 1. / dfYScale; + int nYReciprocalScale = (int)(dfYReciprocalScale + 0.5); + if( fabs(dfYReciprocalScale-nYReciprocalScale) < 0.05 ) + dfYScale = 1.0 / nYReciprocalScale; + } + /*CPLDebug("WARP", "dfXScale = %f, dfYScale = %f", dfXScale, dfYScale);*/ + + int bUse4SamplesFormula = (dfXScale >= 0.95 && dfYScale >= 0.95); + + // Safety check for callers that would use GDALWarpKernel without using + // GDALWarpOperation. + if( (eResample == GRA_CubicSpline || eResample == GRA_Lanczos || + ((eResample == GRA_Cubic || eResample == GRA_Bilinear) && !bUse4SamplesFormula)) && + atoi(CSLFetchNameValueDef(papszWarpOptions, "EXTRA_ELTS", "0") ) != WARP_EXTRA_ELTS ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Source arrays must have WARP_EXTRA_ELTS extra elements at their end. " + "See GDALWarpKernel class definition. If this condition is fulfilled, " + "define a EXTRA_ELTS=%d warp options", WARP_EXTRA_ELTS); + return CE_Failure; + } + + dfXFilter = anGWKFilterRadius[eResample]; + dfYFilter = anGWKFilterRadius[eResample]; + + nXRadius = ( dfXScale < 1.0 ) ? + (int)ceil( dfXFilter / dfXScale ) :(int)dfXFilter; + nYRadius = ( dfYScale < 1.0 ) ? + (int)ceil( dfYFilter / dfYScale ) : (int)dfYFilter; + + // Filter window offset depends on the parity of the kernel radius + nFiltInitX = ((anGWKFilterRadius[eResample] + 1) % 2) - nXRadius; + nFiltInitY = ((anGWKFilterRadius[eResample] + 1) % 2) - nYRadius; + +/* -------------------------------------------------------------------- */ +/* Set up resampling functions. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchBoolean( papszWarpOptions, "USE_GENERAL_CASE", FALSE ) ) + return GWKGeneralCase( this ); + +#if defined(HAVE_OPENCL) + if((eWorkingDataType == GDT_Byte + || eWorkingDataType == GDT_CInt16 + || eWorkingDataType == GDT_UInt16 + || eWorkingDataType == GDT_Int16 + || eWorkingDataType == GDT_CFloat32 + || eWorkingDataType == GDT_Float32) && + (eResample == GRA_Bilinear + || eResample == GRA_Cubic + || eResample == GRA_CubicSpline + || eResample == GRA_Lanczos) && + CSLFetchBoolean( papszWarpOptions, "USE_OPENCL", TRUE )) + { + CPLErr eResult = GWKOpenCLCase( this ); + + // CE_Warning tells us a suitable OpenCL environment was not available + // so we fall through to other CPU based methods. + if( eResult != CE_Warning ) + return eResult; + } +#endif /* defined HAVE_OPENCL */ + + int bNoMasksOrDstDensityOnly = (papanBandSrcValid == NULL + && panUnifiedSrcValid == NULL + && pafUnifiedSrcDensity == NULL + && panDstValid == NULL ); + + if( eWorkingDataType == GDT_Byte + && eResample == GRA_NearestNeighbour + && bNoMasksOrDstDensityOnly ) + return GWKNearestNoMasksOrDstDensityOnlyByte( this ); + + if( eWorkingDataType == GDT_Byte + && eResample == GRA_Bilinear + && bNoMasksOrDstDensityOnly ) + return GWKBilinearNoMasksOrDstDensityOnlyByte( this ); + + if( eWorkingDataType == GDT_Byte + && eResample == GRA_Cubic + && bNoMasksOrDstDensityOnly ) + return GWKCubicNoMasksOrDstDensityOnlyByte( this ); + + if( eWorkingDataType == GDT_Byte + && eResample == GRA_CubicSpline + && bNoMasksOrDstDensityOnly ) + return GWKCubicSplineNoMasksOrDstDensityOnlyByte( this ); + + if( eWorkingDataType == GDT_Byte + && eResample == GRA_NearestNeighbour ) + return GWKNearestByte( this ); + + if( (eWorkingDataType == GDT_Int16 || eWorkingDataType == GDT_UInt16) + && eResample == GRA_NearestNeighbour + && bNoMasksOrDstDensityOnly ) + return GWKNearestNoMasksOrDstDensityOnlyShort( this ); + + if( (eWorkingDataType == GDT_Int16 ) + && eResample == GRA_Cubic + && bNoMasksOrDstDensityOnly ) + return GWKCubicNoMasksOrDstDensityOnlyShort( this ); + + if( (eWorkingDataType == GDT_Int16 ) + && eResample == GRA_CubicSpline + && bNoMasksOrDstDensityOnly ) + return GWKCubicSplineNoMasksOrDstDensityOnlyShort( this ); + + if( (eWorkingDataType == GDT_Int16 ) + && eResample == GRA_Bilinear + && bNoMasksOrDstDensityOnly ) + return GWKBilinearNoMasksOrDstDensityOnlyShort( this ); + + if( (eWorkingDataType == GDT_UInt16 ) + && eResample == GRA_Cubic + && bNoMasksOrDstDensityOnly ) + return GWKCubicNoMasksOrDstDensityOnlyUShort( this ); + + if( (eWorkingDataType == GDT_UInt16 ) + && eResample == GRA_CubicSpline + && bNoMasksOrDstDensityOnly ) + return GWKCubicSplineNoMasksOrDstDensityOnlyUShort( this ); + + if( (eWorkingDataType == GDT_UInt16 ) + && eResample == GRA_Bilinear + && bNoMasksOrDstDensityOnly ) + return GWKBilinearNoMasksOrDstDensityOnlyUShort( this ); + + if( (eWorkingDataType == GDT_Int16 || eWorkingDataType == GDT_UInt16) + && eResample == GRA_NearestNeighbour ) + return GWKNearestShort( this ); + + if( eWorkingDataType == GDT_Float32 + && eResample == GRA_NearestNeighbour + && bNoMasksOrDstDensityOnly ) + return GWKNearestNoMasksOrDstDensityOnlyFloat( this ); + + if( eWorkingDataType == GDT_Float32 + && eResample == GRA_NearestNeighbour ) + return GWKNearestFloat( this ); + + if( eWorkingDataType == GDT_Float32 + && eResample == GRA_Bilinear + && bNoMasksOrDstDensityOnly ) + return GWKBilinearNoMasksOrDstDensityOnlyFloat( this ); + + if( eWorkingDataType == GDT_Float32 + && eResample == GRA_Cubic + && bNoMasksOrDstDensityOnly ) + return GWKCubicNoMasksOrDstDensityOnlyFloat( this ); + +#ifdef INSTANCIATE_FLOAT64_SSE2_IMPL + if( eWorkingDataType == GDT_Float64 + && eResample == GRA_Bilinear + && bNoMasksOrDstDensityOnly ) + return GWKBilinearNoMasksOrDstDensityOnlyDouble( this ); + + if( eWorkingDataType == GDT_Float64 + && eResample == GRA_Cubic + && bNoMasksOrDstDensityOnly ) + return GWKCubicNoMasksOrDstDensityOnlyDouble( this ); +#endif + + if( eResample == GRA_Average ) + return GWKAverageOrMode( this ); + + if( eResample == GRA_Mode ) + return GWKAverageOrMode( this ); + + if( eResample == GRA_Max ) + return GWKAverageOrMode( this ); + + if( eResample == GRA_Min ) + return GWKAverageOrMode( this ); + + if( eResample == GRA_Med ) + return GWKAverageOrMode( this ); + + if( eResample == GRA_Q1 ) + return GWKAverageOrMode( this ); + + if( eResample == GRA_Q3 ) + return GWKAverageOrMode( this ); + + return GWKGeneralCase( this ); +} + +/************************************************************************/ +/* Validate() */ +/************************************************************************/ + +/** + * \fn CPLErr GDALWarpKernel::Validate() + * + * Check the settings in the GDALWarpKernel, and issue a CPLError() + * (and return CE_Failure) if the configuration is considered to be + * invalid for some reason. + * + * This method will also do some standard defaulting such as setting + * pfnProgress to GDALDummyProgress() if it is NULL. + * + * @return CE_None on success or CE_Failure if an error is detected. + */ + +CPLErr GDALWarpKernel::Validate() + +{ + if ( (size_t)eResample >= + (sizeof(anGWKFilterRadius) / sizeof(anGWKFilterRadius[0])) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported resampling method %d.", (int) eResample ); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* GWKOverlayDensity() */ +/* */ +/* Compute the final density for the destination pixel. This */ +/* is a function of the overlay density (passed in) and the */ +/* original density. */ +/************************************************************************/ + +static void GWKOverlayDensity( GDALWarpKernel *poWK, int iDstOffset, + double dfDensity ) +{ + if( dfDensity < 0.0001 || poWK->pafDstDensity == NULL ) + return; + + poWK->pafDstDensity[iDstOffset] = (float) + ( 1.0 - (1.0 - dfDensity) * (1.0 - poWK->pafDstDensity[iDstOffset]) ); +} + +/************************************************************************/ +/* GWKRoundValueT() */ +/************************************************************************/ + +template +static CPL_INLINE T GWKRoundValueT(double dfValue) +{ + return (std::numeric_limits::min() < 0) ? (T)floor(dfValue + 0.5) : + (T)(dfValue + 0.5); +} + +template<> float GWKRoundValueT(double dfValue) +{ + return (float)dfValue; +} + +#ifdef notused +template<> double GWKRoundValueT(double dfValue) +{ + return dfValue; +} +#endif + +/************************************************************************/ +/* GWKClampValueT() */ +/************************************************************************/ + +template +static CPL_INLINE T GWKClampValueT(double dfValue) +{ + if (dfValue < std::numeric_limits::min()) + return std::numeric_limits::min(); + else if (dfValue > std::numeric_limits::max()) + return std::numeric_limits::max(); + else + return GWKRoundValueT(dfValue); +} + +template<> float GWKClampValueT(double dfValue) +{ + return (float)dfValue; +} + +#ifdef notused +template<> double GWKClampValueT(double dfValue) +{ + return dfValue; +} +#endif + +/************************************************************************/ +/* GWKSetPixelValueRealT() */ +/************************************************************************/ + +template +static int GWKSetPixelValueRealT( GDALWarpKernel *poWK, int iBand, + int iDstOffset, double dfDensity, + T value) +{ + T *pDst = (T*)(poWK->papabyDstImage[iBand]); + +/* -------------------------------------------------------------------- */ +/* If the source density is less than 100% we need to fetch the */ +/* existing destination value, and mix it with the source to */ +/* get the new "to apply" value. Also compute composite */ +/* density. */ +/* */ +/* We avoid mixing if density is very near one or risk mixing */ +/* in very extreme nodata values and causing odd results (#1610) */ +/* -------------------------------------------------------------------- */ + if( dfDensity < 0.9999 ) + { + double dfDstReal, dfDstDensity = 1.0; + + if( dfDensity < 0.0001 ) + return TRUE; + + if( poWK->pafDstDensity != NULL ) + dfDstDensity = poWK->pafDstDensity[iDstOffset]; + else if( poWK->panDstValid != NULL + && !((poWK->panDstValid[iDstOffset>>5] + & (0x01 << (iDstOffset & 0x1f))) ) ) + dfDstDensity = 0.0; + + // It seems like we also ought to be testing panDstValid[] here! + + dfDstReal = pDst[iDstOffset]; + + // the destination density is really only relative to the portion + // not occluded by the overlay. + double dfDstInfluence = (1.0 - dfDensity) * dfDstDensity; + + double dfReal = (value * dfDensity + dfDstReal * dfDstInfluence) + / (dfDensity + dfDstInfluence); + +/* -------------------------------------------------------------------- */ +/* Actually apply the destination value. */ +/* */ +/* Avoid using the destination nodata value for integer datatypes */ +/* if by chance it is equal to the computed pixel value. */ +/* -------------------------------------------------------------------- */ + pDst[iDstOffset] = GWKClampValueT(dfReal); + } + else + pDst[iDstOffset] = value; + + if (poWK->padfDstNoDataReal != NULL && + poWK->padfDstNoDataReal[iBand] == (double)pDst[iDstOffset]) + { + if (pDst[iDstOffset] == std::numeric_limits::min()) + pDst[iDstOffset] = std::numeric_limits::min() + 1; + else + pDst[iDstOffset] --; + } + + return TRUE; +} + +/************************************************************************/ +/* GWKSetPixelValue() */ +/************************************************************************/ + +static int GWKSetPixelValue( GDALWarpKernel *poWK, int iBand, + int iDstOffset, double dfDensity, + double dfReal, double dfImag ) + +{ + GByte *pabyDst = poWK->papabyDstImage[iBand]; + +/* -------------------------------------------------------------------- */ +/* If the source density is less than 100% we need to fetch the */ +/* existing destination value, and mix it with the source to */ +/* get the new "to apply" value. Also compute composite */ +/* density. */ +/* */ +/* We avoid mixing if density is very near one or risk mixing */ +/* in very extreme nodata values and causing odd results (#1610) */ +/* -------------------------------------------------------------------- */ + if( dfDensity < 0.9999 ) + { + double dfDstReal, dfDstImag, dfDstDensity = 1.0; + + if( dfDensity < 0.0001 ) + return TRUE; + + if( poWK->pafDstDensity != NULL ) + dfDstDensity = poWK->pafDstDensity[iDstOffset]; + else if( poWK->panDstValid != NULL + && !((poWK->panDstValid[iDstOffset>>5] + & (0x01 << (iDstOffset & 0x1f))) ) ) + dfDstDensity = 0.0; + + // It seems like we also ought to be testing panDstValid[] here! + + switch( poWK->eWorkingDataType ) + { + case GDT_Byte: + dfDstReal = pabyDst[iDstOffset]; + dfDstImag = 0.0; + break; + + case GDT_Int16: + dfDstReal = ((GInt16 *) pabyDst)[iDstOffset]; + dfDstImag = 0.0; + break; + + case GDT_UInt16: + dfDstReal = ((GUInt16 *) pabyDst)[iDstOffset]; + dfDstImag = 0.0; + break; + + case GDT_Int32: + dfDstReal = ((GInt32 *) pabyDst)[iDstOffset]; + dfDstImag = 0.0; + break; + + case GDT_UInt32: + dfDstReal = ((GUInt32 *) pabyDst)[iDstOffset]; + dfDstImag = 0.0; + break; + + case GDT_Float32: + dfDstReal = ((float *) pabyDst)[iDstOffset]; + dfDstImag = 0.0; + break; + + case GDT_Float64: + dfDstReal = ((double *) pabyDst)[iDstOffset]; + dfDstImag = 0.0; + break; + + case GDT_CInt16: + dfDstReal = ((GInt16 *) pabyDst)[iDstOffset*2]; + dfDstImag = ((GInt16 *) pabyDst)[iDstOffset*2+1]; + break; + + case GDT_CInt32: + dfDstReal = ((GInt32 *) pabyDst)[iDstOffset*2]; + dfDstImag = ((GInt32 *) pabyDst)[iDstOffset*2+1]; + break; + + case GDT_CFloat32: + dfDstReal = ((float *) pabyDst)[iDstOffset*2]; + dfDstImag = ((float *) pabyDst)[iDstOffset*2+1]; + break; + + case GDT_CFloat64: + dfDstReal = ((double *) pabyDst)[iDstOffset*2]; + dfDstImag = ((double *) pabyDst)[iDstOffset*2+1]; + break; + + default: + CPLAssert( FALSE ); + dfDstDensity = 0.0; + return FALSE; + } + + // the destination density is really only relative to the portion + // not occluded by the overlay. + double dfDstInfluence = (1.0 - dfDensity) * dfDstDensity; + + dfReal = (dfReal * dfDensity + dfDstReal * dfDstInfluence) + / (dfDensity + dfDstInfluence); + + dfImag = (dfImag * dfDensity + dfDstImag * dfDstInfluence) + / (dfDensity + dfDstInfluence); + } + +/* -------------------------------------------------------------------- */ +/* Actually apply the destination value. */ +/* */ +/* Avoid using the destination nodata value for integer datatypes */ +/* if by chance it is equal to the computed pixel value. */ +/* -------------------------------------------------------------------- */ + +#define CLAMP(type,minval,maxval) \ + do { \ + if (dfReal < minval) ((type *) pabyDst)[iDstOffset] = (type)minval; \ + else if (dfReal > maxval) ((type *) pabyDst)[iDstOffset] = (type)maxval; \ + else ((type *) pabyDst)[iDstOffset] = (minval < 0) ? (type)floor(dfReal + 0.5) : (type)(dfReal + 0.5); \ + if (poWK->padfDstNoDataReal != NULL && \ + poWK->padfDstNoDataReal[iBand] == (double)((type *) pabyDst)[iDstOffset]) \ + { \ + if (((type *) pabyDst)[iDstOffset] == minval) \ + ((type *) pabyDst)[iDstOffset] = (type)(minval + 1); \ + else \ + ((type *) pabyDst)[iDstOffset] --; \ + } } while(0) + + switch( poWK->eWorkingDataType ) + { + case GDT_Byte: + CLAMP(GByte, 0.0, 255.0); + break; + + case GDT_Int16: + CLAMP(GInt16, -32768.0, 32767.0); + break; + + case GDT_UInt16: + CLAMP(GUInt16, 0.0, 65535.0); + break; + + case GDT_UInt32: + CLAMP(GUInt32, 0.0, 4294967295.0); + break; + + case GDT_Int32: + CLAMP(GInt32, -2147483648.0, 2147483647.0); + break; + + case GDT_Float32: + ((float *) pabyDst)[iDstOffset] = (float) dfReal; + break; + + case GDT_Float64: + ((double *) pabyDst)[iDstOffset] = dfReal; + break; + + case GDT_CInt16: + if( dfReal < -32768 ) + ((GInt16 *) pabyDst)[iDstOffset*2] = -32768; + else if( dfReal > 32767 ) + ((GInt16 *) pabyDst)[iDstOffset*2] = 32767; + else + ((GInt16 *) pabyDst)[iDstOffset*2] = (GInt16) floor(dfReal+0.5); + if( dfImag < -32768 ) + ((GInt16 *) pabyDst)[iDstOffset*2+1] = -32768; + else if( dfImag > 32767 ) + ((GInt16 *) pabyDst)[iDstOffset*2+1] = 32767; + else + ((GInt16 *) pabyDst)[iDstOffset*2+1] = (GInt16) floor(dfImag+0.5); + break; + + case GDT_CInt32: + if( dfReal < -2147483648.0 ) + ((GInt32 *) pabyDst)[iDstOffset*2] = (GInt32) -2147483648.0; + else if( dfReal > 2147483647.0 ) + ((GInt32 *) pabyDst)[iDstOffset*2] = (GInt32) 2147483647.0; + else + ((GInt32 *) pabyDst)[iDstOffset*2] = (GInt32) floor(dfReal+0.5); + if( dfImag < -2147483648.0 ) + ((GInt32 *) pabyDst)[iDstOffset*2+1] = (GInt32) -2147483648.0; + else if( dfImag > 2147483647.0 ) + ((GInt32 *) pabyDst)[iDstOffset*2+1] = (GInt32) 2147483647.0; + else + ((GInt32 *) pabyDst)[iDstOffset*2+1] = (GInt32) floor(dfImag+0.5); + break; + + case GDT_CFloat32: + ((float *) pabyDst)[iDstOffset*2] = (float) dfReal; + ((float *) pabyDst)[iDstOffset*2+1] = (float) dfImag; + break; + + case GDT_CFloat64: + ((double *) pabyDst)[iDstOffset*2] = (double) dfReal; + ((double *) pabyDst)[iDstOffset*2+1] = (double) dfImag; + break; + + default: + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* GWKGetPixelValue() */ +/************************************************************************/ + +/* It is assumed that panUnifiedSrcValid has been checked before */ + +static int GWKGetPixelValue( GDALWarpKernel *poWK, int iBand, + int iSrcOffset, double *pdfDensity, + double *pdfReal, double *pdfImag ) + +{ + GByte *pabySrc = poWK->papabySrcImage[iBand]; + + if( poWK->papanBandSrcValid != NULL + && poWK->papanBandSrcValid[iBand] != NULL + && !((poWK->papanBandSrcValid[iBand][iSrcOffset>>5] + & (0x01 << (iSrcOffset & 0x1f)))) ) + { + *pdfDensity = 0.0; + return FALSE; + } + + switch( poWK->eWorkingDataType ) + { + case GDT_Byte: + *pdfReal = pabySrc[iSrcOffset]; + *pdfImag = 0.0; + break; + + case GDT_Int16: + *pdfReal = ((GInt16 *) pabySrc)[iSrcOffset]; + *pdfImag = 0.0; + break; + + case GDT_UInt16: + *pdfReal = ((GUInt16 *) pabySrc)[iSrcOffset]; + *pdfImag = 0.0; + break; + + case GDT_Int32: + *pdfReal = ((GInt32 *) pabySrc)[iSrcOffset]; + *pdfImag = 0.0; + break; + + case GDT_UInt32: + *pdfReal = ((GUInt32 *) pabySrc)[iSrcOffset]; + *pdfImag = 0.0; + break; + + case GDT_Float32: + *pdfReal = ((float *) pabySrc)[iSrcOffset]; + *pdfImag = 0.0; + break; + + case GDT_Float64: + *pdfReal = ((double *) pabySrc)[iSrcOffset]; + *pdfImag = 0.0; + break; + + case GDT_CInt16: + *pdfReal = ((GInt16 *) pabySrc)[iSrcOffset*2]; + *pdfImag = ((GInt16 *) pabySrc)[iSrcOffset*2+1]; + break; + + case GDT_CInt32: + *pdfReal = ((GInt32 *) pabySrc)[iSrcOffset*2]; + *pdfImag = ((GInt32 *) pabySrc)[iSrcOffset*2+1]; + break; + + case GDT_CFloat32: + *pdfReal = ((float *) pabySrc)[iSrcOffset*2]; + *pdfImag = ((float *) pabySrc)[iSrcOffset*2+1]; + break; + + case GDT_CFloat64: + *pdfReal = ((double *) pabySrc)[iSrcOffset*2]; + *pdfImag = ((double *) pabySrc)[iSrcOffset*2+1]; + break; + + default: + *pdfDensity = 0.0; + return FALSE; + } + + if( poWK->pafUnifiedSrcDensity != NULL ) + *pdfDensity = poWK->pafUnifiedSrcDensity[iSrcOffset]; + else + *pdfDensity = 1.0; + + return *pdfDensity != 0.0; +} + +/************************************************************************/ +/* GWKGetPixelRow() */ +/************************************************************************/ + +/* It is assumed that adfImag[] is set to 0 by caller code for non-complex */ +/* data-types. */ + +static int GWKGetPixelRow( GDALWarpKernel *poWK, int iBand, + int iSrcOffset, int nHalfSrcLen, + double* padfDensity, + double adfReal[], + double* padfImag ) +{ + // We know that nSrcLen is even, so we can *always* unroll loops 2x + int nSrcLen = nHalfSrcLen * 2; + int bHasValid = FALSE; + int i; + + if( padfDensity != NULL ) + { + // Init the density + for ( i = 0; i < nSrcLen; i += 2 ) + { + padfDensity[i] = 1.0; + padfDensity[i+1] = 1.0; + } + + if ( poWK->panUnifiedSrcValid != NULL ) + { + for ( i = 0; i < nSrcLen; i += 2 ) + { + if(poWK->panUnifiedSrcValid[(iSrcOffset+i)>>5] + & (0x01 << ((iSrcOffset+i) & 0x1f))) + bHasValid = TRUE; + else + padfDensity[i] = 0.0; + + if(poWK->panUnifiedSrcValid[(iSrcOffset+i+1)>>5] + & (0x01 << ((iSrcOffset+i+1) & 0x1f))) + bHasValid = TRUE; + else + padfDensity[i+1] = 0.0; + } + + // Reset or fail as needed + if ( bHasValid ) + bHasValid = FALSE; + else + return FALSE; + } + + if ( poWK->papanBandSrcValid != NULL + && poWK->papanBandSrcValid[iBand] != NULL) + { + for ( i = 0; i < nSrcLen; i += 2 ) + { + if(poWK->papanBandSrcValid[iBand][(iSrcOffset+i)>>5] + & (0x01 << ((iSrcOffset+i) & 0x1f))) + bHasValid = TRUE; + else + padfDensity[i] = 0.0; + + if(poWK->papanBandSrcValid[iBand][(iSrcOffset+i+1)>>5] + & (0x01 << ((iSrcOffset+i+1) & 0x1f))) + bHasValid = TRUE; + else + padfDensity[i+1] = 0.0; + } + + // Reset or fail as needed + if ( bHasValid ) + bHasValid = FALSE; + else + return FALSE; + } + } + + // Fetch data + switch( poWK->eWorkingDataType ) + { + case GDT_Byte: + { + GByte* pSrc = (GByte*) poWK->papabySrcImage[iBand]; + pSrc += iSrcOffset; + for ( i = 0; i < nSrcLen; i += 2 ) + { + adfReal[i] = pSrc[i]; + adfReal[i+1] = pSrc[i+1]; + } + break; + } + + case GDT_Int16: + { + GInt16* pSrc = (GInt16*) poWK->papabySrcImage[iBand]; + pSrc += iSrcOffset; + for ( i = 0; i < nSrcLen; i += 2 ) + { + adfReal[i] = pSrc[i]; + adfReal[i+1] = pSrc[i+1]; + } + break; + } + + case GDT_UInt16: + { + GUInt16* pSrc = (GUInt16*) poWK->papabySrcImage[iBand]; + pSrc += iSrcOffset; + for ( i = 0; i < nSrcLen; i += 2 ) + { + adfReal[i] = pSrc[i]; + adfReal[i+1] = pSrc[i+1]; + } + break; + } + + case GDT_Int32: + { + GInt32* pSrc = (GInt32*) poWK->papabySrcImage[iBand]; + pSrc += iSrcOffset; + for ( i = 0; i < nSrcLen; i += 2 ) + { + adfReal[i] = pSrc[i]; + adfReal[i+1] = pSrc[i+1]; + } + break; + } + + case GDT_UInt32: + { + GUInt32* pSrc = (GUInt32*) poWK->papabySrcImage[iBand]; + pSrc += iSrcOffset; + for ( i = 0; i < nSrcLen; i += 2 ) + { + adfReal[i] = pSrc[i]; + adfReal[i+1] = pSrc[i+1]; + } + break; + } + + case GDT_Float32: + { + float* pSrc = (float*) poWK->papabySrcImage[iBand]; + pSrc += iSrcOffset; + for ( i = 0; i < nSrcLen; i += 2 ) + { + adfReal[i] = pSrc[i]; + adfReal[i+1] = pSrc[i+1]; + } + break; + } + + case GDT_Float64: + { + double* pSrc = (double*) poWK->papabySrcImage[iBand]; + pSrc += iSrcOffset; + for ( i = 0; i < nSrcLen; i += 2 ) + { + adfReal[i] = pSrc[i]; + adfReal[i+1] = pSrc[i+1]; + } + break; + } + + case GDT_CInt16: + { + GInt16* pSrc = (GInt16*) poWK->papabySrcImage[iBand]; + pSrc += 2 * iSrcOffset; + for ( i = 0; i < nSrcLen; i += 2 ) + { + adfReal[i] = pSrc[2*i]; + padfImag[i] = pSrc[2*i+1]; + + adfReal[i+1] = pSrc[2*i+2]; + padfImag[i+1] = pSrc[2*i+3]; + } + break; + } + + case GDT_CInt32: + { + GInt32* pSrc = (GInt32*) poWK->papabySrcImage[iBand]; + pSrc += 2 * iSrcOffset; + for ( i = 0; i < nSrcLen; i += 2 ) + { + adfReal[i] = pSrc[2*i]; + padfImag[i] = pSrc[2*i+1]; + + adfReal[i+1] = pSrc[2*i+2]; + padfImag[i+1] = pSrc[2*i+3]; + } + break; + } + + case GDT_CFloat32: + { + float* pSrc = (float*) poWK->papabySrcImage[iBand]; + pSrc += 2 * iSrcOffset; + for ( i = 0; i < nSrcLen; i += 2 ) + { + adfReal[i] = pSrc[2*i]; + padfImag[i] = pSrc[2*i+1]; + + adfReal[i+1] = pSrc[2*i+2]; + padfImag[i+1] = pSrc[2*i+3]; + } + break; + } + + + case GDT_CFloat64: + { + double* pSrc = (double*) poWK->papabySrcImage[iBand]; + pSrc += 2 * iSrcOffset; + for ( i = 0; i < nSrcLen; i += 2 ) + { + adfReal[i] = pSrc[2*i]; + padfImag[i] = pSrc[2*i+1]; + + adfReal[i+1] = pSrc[2*i+2]; + padfImag[i+1] = pSrc[2*i+3]; + } + break; + } + + default: + CPLAssert(FALSE); + if( padfDensity ) + memset( padfDensity, 0, nSrcLen * sizeof(double) ); + return FALSE; + } + + if( padfDensity == NULL ) + return TRUE; + + if( poWK->pafUnifiedSrcDensity == NULL ) + { + for ( i = 0; i < nSrcLen; i += 2 ) + { + // Take into account earlier calcs + if(padfDensity[i] > 0.000000001) + { + padfDensity[i] = 1.0; + bHasValid = TRUE; + } + + if(padfDensity[i+1] > 0.000000001) + { + padfDensity[i+1] = 1.0; + bHasValid = TRUE; + } + } + } + else + { + for ( i = 0; i < nSrcLen; i += 2 ) + { + if(padfDensity[i] > 0.000000001) + padfDensity[i] = poWK->pafUnifiedSrcDensity[iSrcOffset+i]; + if(padfDensity[i] > 0.000000001) + bHasValid = TRUE; + + if(padfDensity[i+1] > 0.000000001) + padfDensity[i+1] = poWK->pafUnifiedSrcDensity[iSrcOffset+i+1]; + if(padfDensity[i+1] > 0.000000001) + bHasValid = TRUE; + } + } + + return bHasValid; +} + +/************************************************************************/ +/* GWKGetPixelT() */ +/************************************************************************/ + +template +static int GWKGetPixelT( GDALWarpKernel *poWK, int iBand, + int iSrcOffset, double *pdfDensity, + T *pValue ) + +{ + T *pSrc = (T *)poWK->papabySrcImage[iBand]; + + if ( ( poWK->panUnifiedSrcValid != NULL + && !((poWK->panUnifiedSrcValid[iSrcOffset>>5] + & (0x01 << (iSrcOffset & 0x1f))) ) ) + || ( poWK->papanBandSrcValid != NULL + && poWK->papanBandSrcValid[iBand] != NULL + && !((poWK->papanBandSrcValid[iBand][iSrcOffset>>5] + & (0x01 << (iSrcOffset & 0x1f)))) ) ) + { + *pdfDensity = 0.0; + return FALSE; + } + + *pValue = pSrc[iSrcOffset]; + + if ( poWK->pafUnifiedSrcDensity == NULL ) + *pdfDensity = 1.0; + else + *pdfDensity = poWK->pafUnifiedSrcDensity[iSrcOffset]; + + return *pdfDensity != 0.0; +} + +/************************************************************************/ +/* GWKBilinearResample() */ +/* Set of bilinear interpolators */ +/************************************************************************/ + +static int GWKBilinearResample4Sample( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + double *pdfDensity, + double *pdfReal, double *pdfImag ) + +{ + // Save as local variables to avoid following pointers + int nSrcXSize = poWK->nSrcXSize; + int nSrcYSize = poWK->nSrcYSize; + + int iSrcX = (int) floor(dfSrcX - 0.5); + int iSrcY = (int) floor(dfSrcY - 0.5); + int iSrcOffset; + double dfRatioX = 1.5 - (dfSrcX - iSrcX); + double dfRatioY = 1.5 - (dfSrcY - iSrcY); + double adfDensity[2], adfReal[2], adfImag[2] = {0, 0}; + double dfAccumulatorReal = 0.0, dfAccumulatorImag = 0.0; + double dfAccumulatorDensity = 0.0; + double dfAccumulatorDivisor = 0.0; + int bShifted = FALSE; + + if (iSrcX == -1) + { + iSrcX = 0; + dfRatioX = 1; + } + if (iSrcY == -1) + { + iSrcY = 0; + dfRatioY = 1; + } + iSrcOffset = iSrcX + iSrcY * nSrcXSize; + + // Shift so we don't overrun the array + if( nSrcXSize * nSrcYSize == iSrcOffset + 1 + || nSrcXSize * nSrcYSize == iSrcOffset + nSrcXSize + 1 ) + { + bShifted = TRUE; + --iSrcOffset; + } + + // Get pixel row + if ( iSrcY >= 0 && iSrcY < nSrcYSize + && iSrcOffset >= 0 && iSrcOffset < nSrcXSize * nSrcYSize + && GWKGetPixelRow( poWK, iBand, iSrcOffset, 1, + adfDensity, adfReal, adfImag ) ) + { + double dfMult1 = dfRatioX * dfRatioY; + double dfMult2 = (1.0-dfRatioX) * dfRatioY; + + // Shifting corrected + if ( bShifted ) + { + adfReal[0] = adfReal[1]; + adfImag[0] = adfImag[1]; + adfDensity[0] = adfDensity[1]; + } + + // Upper Left Pixel + if ( iSrcX >= 0 && iSrcX < nSrcXSize + && adfDensity[0] > 0.000000001 ) + { + dfAccumulatorDivisor += dfMult1; + + dfAccumulatorReal += adfReal[0] * dfMult1; + dfAccumulatorImag += adfImag[0] * dfMult1; + dfAccumulatorDensity += adfDensity[0] * dfMult1; + } + + // Upper Right Pixel + if ( iSrcX+1 >= 0 && iSrcX+1 < nSrcXSize + && adfDensity[1] > 0.000000001 ) + { + dfAccumulatorDivisor += dfMult2; + + dfAccumulatorReal += adfReal[1] * dfMult2; + dfAccumulatorImag += adfImag[1] * dfMult2; + dfAccumulatorDensity += adfDensity[1] * dfMult2; + } + } + + // Get pixel row + if ( iSrcY+1 >= 0 && iSrcY+1 < nSrcYSize + && iSrcOffset+nSrcXSize >= 0 + && iSrcOffset+nSrcXSize < nSrcXSize * nSrcYSize + && GWKGetPixelRow( poWK, iBand, iSrcOffset+nSrcXSize, 1, + adfDensity, adfReal, adfImag ) ) + { + double dfMult1 = dfRatioX * (1.0-dfRatioY); + double dfMult2 = (1.0-dfRatioX) * (1.0-dfRatioY); + + // Shifting corrected + if ( bShifted ) + { + adfReal[0] = adfReal[1]; + adfImag[0] = adfImag[1]; + adfDensity[0] = adfDensity[1]; + } + + // Lower Left Pixel + if ( iSrcX >= 0 && iSrcX < nSrcXSize + && adfDensity[0] > 0.000000001 ) + { + dfAccumulatorDivisor += dfMult1; + + dfAccumulatorReal += adfReal[0] * dfMult1; + dfAccumulatorImag += adfImag[0] * dfMult1; + dfAccumulatorDensity += adfDensity[0] * dfMult1; + } + + // Lower Right Pixel + if ( iSrcX+1 >= 0 && iSrcX+1 < nSrcXSize + && adfDensity[1] > 0.000000001 ) + { + dfAccumulatorDivisor += dfMult2; + + dfAccumulatorReal += adfReal[1] * dfMult2; + dfAccumulatorImag += adfImag[1] * dfMult2; + dfAccumulatorDensity += adfDensity[1] * dfMult2; + } + } + +/* -------------------------------------------------------------------- */ +/* Return result. */ +/* -------------------------------------------------------------------- */ + if ( dfAccumulatorDivisor == 1.0 ) + { + *pdfReal = dfAccumulatorReal; + *pdfImag = dfAccumulatorImag; + *pdfDensity = dfAccumulatorDensity; + return TRUE; + } + else if ( dfAccumulatorDivisor < 0.00001 ) + { + *pdfReal = 0.0; + *pdfImag = 0.0; + *pdfDensity = 0.0; + return FALSE; + } + else + { + *pdfReal = dfAccumulatorReal / dfAccumulatorDivisor; + *pdfImag = dfAccumulatorImag / dfAccumulatorDivisor; + *pdfDensity = dfAccumulatorDensity / dfAccumulatorDivisor; + return TRUE; + } +} + +template +static int GWKBilinearResampleNoMasks4SampleT( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + T *pValue ) + +{ + double dfMult; + double dfAccumulator = 0.0; + double dfAccumulatorDivisor = 0.0; + + int iSrcX = (int) floor(dfSrcX - 0.5); + int iSrcY = (int) floor(dfSrcY - 0.5); + int iSrcOffset = iSrcX + iSrcY * poWK->nSrcXSize; + double dfRatioX = 1.5 - (dfSrcX - iSrcX); + double dfRatioY = 1.5 - (dfSrcY - iSrcY); + + T* pSrc = (T *)poWK->papabySrcImage[iBand]; + + if( iSrcX >= 0 && iSrcX+1 < poWK->nSrcXSize + && iSrcY >= 0 && iSrcY+1 < poWK->nSrcYSize ) + { + dfAccumulator = ((double)pSrc[iSrcOffset] * dfRatioX + + (double)pSrc[iSrcOffset+1] * (1.0-dfRatioX)) * dfRatioY + + ((double)pSrc[iSrcOffset+poWK->nSrcXSize] * dfRatioX + + (double)pSrc[iSrcOffset+1+poWK->nSrcXSize] * (1.0-dfRatioX)) * (1.0-dfRatioY); + + *pValue = GWKRoundValueT(dfAccumulator); + + return TRUE; + } + + // Upper Left Pixel + if( iSrcX >= 0 && iSrcX < poWK->nSrcXSize + && iSrcY >= 0 && iSrcY < poWK->nSrcYSize ) + { + dfMult = dfRatioX * dfRatioY; + + dfAccumulatorDivisor += dfMult; + + dfAccumulator += (double)pSrc[iSrcOffset] * dfMult; + } + + // Upper Right Pixel + if( iSrcX+1 >= 0 && iSrcX+1 < poWK->nSrcXSize + && iSrcY >= 0 && iSrcY < poWK->nSrcYSize ) + { + dfMult = (1.0-dfRatioX) * dfRatioY; + + dfAccumulatorDivisor += dfMult; + + dfAccumulator += (double)pSrc[iSrcOffset+1] * dfMult; + } + + // Lower Right Pixel + if( iSrcX+1 >= 0 && iSrcX+1 < poWK->nSrcXSize + && iSrcY+1 >= 0 && iSrcY+1 < poWK->nSrcYSize ) + { + dfMult = (1.0-dfRatioX) * (1.0-dfRatioY); + + dfAccumulatorDivisor += dfMult; + + dfAccumulator += (double)pSrc[iSrcOffset+1+poWK->nSrcXSize] * dfMult; + } + + // Lower Left Pixel + if( iSrcX >= 0 && iSrcX < poWK->nSrcXSize + && iSrcY+1 >= 0 && iSrcY+1 < poWK->nSrcYSize ) + { + dfMult = dfRatioX * (1.0-dfRatioY); + + dfAccumulatorDivisor += dfMult; + + dfAccumulator += (double)pSrc[iSrcOffset+poWK->nSrcXSize] * dfMult; + } + +/* -------------------------------------------------------------------- */ +/* Return result. */ +/* -------------------------------------------------------------------- */ + double dfValue; + + if( dfAccumulatorDivisor < 0.00001 ) + { + *pValue = 0; + return FALSE; + } + else if( dfAccumulatorDivisor == 1.0 ) + { + dfValue = dfAccumulator; + } + else + { + dfValue = dfAccumulator / dfAccumulatorDivisor; + } + + *pValue = GWKRoundValueT(dfValue); + + return TRUE; +} + +/************************************************************************/ +/* GWKCubicResample() */ +/* Set of bicubic interpolators using cubic convolution. */ +/************************************************************************/ + +/* http://verona.fi-p.unam.mx/boris/practicas/CubConvInterp.pdf Formula 18 +or http://en.wikipedia.org/wiki/Cubic_Hermite_spline : CINTx(p_1,p0,p1,p2) +or http://en.wikipedia.org/wiki/Bicubic_interpolation: matrix notation */ + +#define CubicConvolution(distance1,distance2,distance3,f0,f1,f2,f3) \ + ( f1 \ + + 0.5 * (distance1*(f2 - f0) \ + + distance2*(2.0*f0 - 5.0*f1 + 4.0*f2 - f3) \ + + distance3*(3.0*(f1 - f2) + f3 - f0))) + +static int GWKCubicResample4Sample( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + double *pdfDensity, + double *pdfReal, double *pdfImag ) + +{ + int iSrcX = (int) (dfSrcX - 0.5); + int iSrcY = (int) (dfSrcY - 0.5); + int iSrcOffset = iSrcX + iSrcY * poWK->nSrcXSize; + double dfDeltaX = dfSrcX - 0.5 - iSrcX; + double dfDeltaY = dfSrcY - 0.5 - iSrcY; + double dfDeltaX2 = dfDeltaX * dfDeltaX; + double dfDeltaY2 = dfDeltaY * dfDeltaY; + double dfDeltaX3 = dfDeltaX2 * dfDeltaX; + double dfDeltaY3 = dfDeltaY2 * dfDeltaY; + double adfValueDens[4], adfValueReal[4], adfValueImag[4]; + double adfDensity[4], adfReal[4], adfImag[4] = {0, 0, 0, 0}; + int i; + + // Get the bilinear interpolation at the image borders + if ( iSrcX - 1 < 0 || iSrcX + 2 >= poWK->nSrcXSize + || iSrcY - 1 < 0 || iSrcY + 2 >= poWK->nSrcYSize ) + return GWKBilinearResample4Sample( poWK, iBand, dfSrcX, dfSrcY, + pdfDensity, pdfReal, pdfImag ); + + for ( i = -1; i < 3; i++ ) + { + if ( !GWKGetPixelRow(poWK, iBand, iSrcOffset + i * poWK->nSrcXSize - 1, + 2, adfDensity, adfReal, adfImag) + || adfDensity[0] < 0.000000001 + || adfDensity[1] < 0.000000001 + || adfDensity[2] < 0.000000001 + || adfDensity[3] < 0.000000001 ) + { + return GWKBilinearResample4Sample( poWK, iBand, dfSrcX, dfSrcY, + pdfDensity, pdfReal, pdfImag ); + } + + adfValueDens[i + 1] = CubicConvolution(dfDeltaX, dfDeltaX2, dfDeltaX3, + adfDensity[0], adfDensity[1], adfDensity[2], adfDensity[3]); + adfValueReal[i + 1] = CubicConvolution(dfDeltaX, dfDeltaX2, dfDeltaX3, + adfReal[0], adfReal[1], adfReal[2], adfReal[3]); + adfValueImag[i + 1] = CubicConvolution(dfDeltaX, dfDeltaX2, dfDeltaX3, + adfImag[0], adfImag[1], adfImag[2], adfImag[3]); + } + + +/* -------------------------------------------------------------------- */ +/* For now, if we have any pixels missing in the kernel area, */ +/* we fallback on using bilinear interpolation. Ideally we */ +/* should do "weight adjustment" of our results similarly to */ +/* what is done for the cubic spline and lanc. interpolators. */ +/* -------------------------------------------------------------------- */ + *pdfDensity = CubicConvolution(dfDeltaY, dfDeltaY2, dfDeltaY3, + adfValueDens[0], adfValueDens[1], + adfValueDens[2], adfValueDens[3]); + *pdfReal = CubicConvolution(dfDeltaY, dfDeltaY2, dfDeltaY3, + adfValueReal[0], adfValueReal[1], + adfValueReal[2], adfValueReal[3]); + *pdfImag = CubicConvolution(dfDeltaY, dfDeltaY2, dfDeltaY3, + adfValueImag[0], adfValueImag[1], + adfValueImag[2], adfValueImag[3]); + + return TRUE; +} + +template +static int GWKCubicResampleNoMasks4SampleT( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + T *pValue ) + +{ + int iSrcX = (int) (dfSrcX - 0.5); + int iSrcY = (int) (dfSrcY - 0.5); + int iSrcOffset = iSrcX + iSrcY * poWK->nSrcXSize; + double dfDeltaX = dfSrcX - 0.5 - iSrcX; + double dfDeltaY = dfSrcY - 0.5 - iSrcY; + double dfDeltaX2 = dfDeltaX * dfDeltaX; + double dfDeltaY2 = dfDeltaY * dfDeltaY; + double dfDeltaX3 = dfDeltaX2 * dfDeltaX; + double dfDeltaY3 = dfDeltaY2 * dfDeltaY; + double adfValue[4]; + int i; + + // Get the bilinear interpolation at the image borders + if ( iSrcX - 1 < 0 || iSrcX + 2 >= poWK->nSrcXSize + || iSrcY - 1 < 0 || iSrcY + 2 >= poWK->nSrcYSize ) + return GWKBilinearResampleNoMasks4SampleT ( poWK, iBand, dfSrcX, dfSrcY, + pValue ); + + for ( i = -1; i < 3; i++ ) + { + int iOffset = iSrcOffset + i * poWK->nSrcXSize; + + adfValue[i + 1] =CubicConvolution(dfDeltaX, dfDeltaX2, dfDeltaX3, + (double)((T *)poWK->papabySrcImage[iBand])[iOffset - 1], + (double)((T *)poWK->papabySrcImage[iBand])[iOffset], + (double)((T *)poWK->papabySrcImage[iBand])[iOffset + 1], + (double)((T *)poWK->papabySrcImage[iBand])[iOffset + 2]); + } + + double dfValue = CubicConvolution( + dfDeltaY, dfDeltaY2, dfDeltaY3, + adfValue[0], adfValue[1], adfValue[2], adfValue[3]); + + *pValue = GWKClampValueT(dfValue); + + return TRUE; +} + + +/************************************************************************/ +/* GWKLanczosSinc() */ +/************************************************************************/ + +/* + * Lanczos windowed sinc interpolation kernel with radius r. + * / + * | sinc(x) * sinc(x/r), if |x| < r + * L(x) = | 1, if x = 0 , + * | 0, otherwise + * \ + * + * where sinc(x) = sin(PI * x) / (PI * x). + */ + +#define GWK_PI 3.14159265358979323846 + +static double GWKLanczosSinc( double dfX ) +{ + /*if( fabs(dfX) > 3.0 ) + { + printf("%f\n", dfX); + return 0; + }*/ + if ( dfX == 0.0 ) + return 1.0; + + const double dfPIX = GWK_PI * dfX; + const double dfPIXoverR = dfPIX / 3; + const double dfPIX2overR = dfPIX * dfPIXoverR; + return sin(dfPIX) * sin(dfPIXoverR) / dfPIX2overR; +} + +static double GWKLanczosSinc4Values( double* padfValues ) +{ + for(int i=0;i<4;i++) + { + if ( padfValues[i] == 0.0 ) + padfValues[i] = 1.0; + else + { + const double dfPIX = GWK_PI * padfValues[i]; + const double dfPIXoverR = dfPIX / 3; + const double dfPIX2overR = dfPIX * dfPIXoverR; + padfValues[i] = sin(dfPIX) * sin(dfPIXoverR) / dfPIX2overR; + } + } + return padfValues[0] + padfValues[1] + padfValues[2] + padfValues[3]; +} + +//#undef GWK_PI + +/************************************************************************/ +/* GWKBilinear() */ +/************************************************************************/ + +static double GWKBilinear(double dfX) +{ + double dfAbsX = fabs(dfX); + if( dfAbsX <= 1.0 ) + return 1 - dfAbsX; + else + return 0.0; +} + +static double GWKBilinear4Values( double* padfValues ) +{ + double dfAbsX0 = fabs(padfValues[0]); + double dfAbsX1 = fabs(padfValues[1]); + double dfAbsX2 = fabs(padfValues[2]); + double dfAbsX3 = fabs(padfValues[3]); + if( dfAbsX0 <= 1.0 ) + padfValues[0] = 1 - dfAbsX0; + else + padfValues[0] = 0.0; + if( dfAbsX1 <= 1.0 ) + padfValues[1] = 1 - dfAbsX1; + else + padfValues[1] = 0.0; + if( dfAbsX2 <= 1.0 ) + padfValues[2] = 1 - dfAbsX2; + else + padfValues[2] = 0.0; + if( dfAbsX3 <= 1.0 ) + padfValues[3] = 1 - dfAbsX3; + else + padfValues[3] = 0.0; + return padfValues[0] + padfValues[1] + padfValues[2] + padfValues[3]; +} + +/************************************************************************/ +/* GWKCubic() */ +/************************************************************************/ + +static double GWKCubic(double dfX) +{ + /* http://en.wikipedia.org/wiki/Bicubic_interpolation#Bicubic_convolution_algorithm */ + /* W(x) formula with a = -0.5 (cubic hermite spline ) */ + /* or http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/Mitchell.pdf */ + /* k(x) (formula 8) with (B,C)=(0,0.5) the Catmull-Rom spline */ + double dfAbsX = fabs(dfX); + if( dfAbsX <= 1.0 ) + { + double dfX2 = dfX * dfX; + return dfX2 * (1.5 * dfAbsX - 2.5) + 1; + } + else if( dfAbsX <= 2.0 ) + { + double dfX2 = dfX * dfX; + return dfX2 * (-0.5 * dfAbsX + 2.5) - 4 * dfAbsX + 2; + } + else + return 0.0; +} + +static double GWKCubic4Values( double* padfValues ) +{ + double dfAbsX_0 = fabs(padfValues[0]); + double dfX2_0 = padfValues[0] * padfValues[0]; + double dfAbsX_1 = fabs(padfValues[1]); + double dfX2_1 = padfValues[1] * padfValues[1]; + double dfAbsX_2 = fabs(padfValues[2]); + double dfX2_2 = padfValues[2] * padfValues[2]; + double dfAbsX_3 = fabs(padfValues[3]); + double dfX2_3 = padfValues[3] * padfValues[3]; + double dfVal0, dfVal1, dfVal2, dfVal3; + if( dfAbsX_0 <= 1.0 ) + dfVal0 = dfX2_0 * (1.5 * dfAbsX_0 - 2.5) + 1; + else if( dfAbsX_0 <= 2.0 ) + dfVal0 = dfX2_0 * (-0.5 * dfAbsX_0 + 2.5) - 4 * dfAbsX_0 + 2; + else + dfVal0 = 0.0; + if( dfAbsX_1 <= 1.0 ) + dfVal1 = dfX2_1 * (1.5 * dfAbsX_1 - 2.5) + 1; + else if( dfAbsX_1 <= 2.0 ) + dfVal1 = dfX2_1 * (-0.5 * dfAbsX_1 + 2.5) - 4 * dfAbsX_1 + 2; + else + dfVal1 = 0.0; + if( dfAbsX_2 <= 1.0 ) + dfVal2 = dfX2_2 * (1.5 * dfAbsX_2 - 2.5) + 1; + else if( dfAbsX_2 <= 2.0 ) + dfVal2 = dfX2_2 * (-0.5 * dfAbsX_2 + 2.5) - 4 * dfAbsX_2 + 2; + else + dfVal2 = 0.0; + if( dfAbsX_3 <= 1.0 ) + dfVal3 = dfX2_3 * (1.5 * dfAbsX_3 - 2.5) + 1; + else if( dfAbsX_3 <= 2.0 ) + dfVal3 = dfX2_3 * (-0.5 * dfAbsX_3 + 2.5) - 4 * dfAbsX_3 + 2; + else + dfVal3 = 0.0; + + padfValues[0] = dfVal0; + padfValues[1] = dfVal1; + padfValues[2] = dfVal2; + padfValues[3] = dfVal3; + return dfVal0 + dfVal1 + dfVal2 + dfVal3; +} + +/************************************************************************/ +/* GWKBSpline() */ +/************************************************************************/ + +/* http://www.cs.utexas.edu/users/fussell/courses/cs384g/lectures/mitchell/Mitchell.pdf with (B,C)=(1,0) */ +/* 1/6 * ( 3 * |x|^3 - 6 * |x|^2 + 4) |x| < 1 + 1/6 * ( -|x|^3 + 6 |x|^2 - 12|x| + 8) |x| > 1 +*/ +static double GWKBSpline( double x ) +{ + double xp2 = x + 2.0; + double xp1 = x + 1.0; + double xm1 = x - 1.0; + + // This will most likely be used, so we'll compute it ahead of time to + // avoid stalling the processor + double xp2c = xp2 * xp2 * xp2; + + // Note that the test is computed only if it is needed + return (((xp2 > 0.0)?((xp1 > 0.0)?((x > 0.0)?((xm1 > 0.0)? + -4.0 * xm1*xm1*xm1:0.0) + + 6.0 * x*x*x:0.0) + + -4.0 * xp1*xp1*xp1:0.0) + + xp2c:0.0) ) /* * 0.166666666666666666666 */; +} + +static double GWKBSpline4Values( double* padfValues ) +{ + for(int i=0;i<4;i++) + { + double x = padfValues[i]; + double xp2 = x + 2.0; + double xp1 = x + 1.0; + double xm1 = x - 1.0; + + // This will most likely be used, so we'll compute it ahead of time to + // avoid stalling the processor + double xp2c = xp2 * xp2 * xp2; + + // Note that the test is computed only if it is needed + padfValues[i] = (((xp2 > 0.0)?((xp1 > 0.0)?((x > 0.0)?((xm1 > 0.0)? + -4.0 * xm1*xm1*xm1:0.0) + + 6.0 * x*x*x:0.0) + + -4.0 * xp1*xp1*xp1:0.0) + + xp2c:0.0) ) /* * 0.166666666666666666666 */; + } + return padfValues[0] + padfValues[1] + padfValues[2] + padfValues[3]; +} +/************************************************************************/ +/* GWKResampleWrkStruct */ +/************************************************************************/ + +typedef struct _GWKResampleWrkStruct GWKResampleWrkStruct; + +typedef int (*pfnGWKResampleType) ( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + double *pdfDensity, + double *pdfReal, double *pdfImag, + GWKResampleWrkStruct* psWrkStruct ); + + +struct _GWKResampleWrkStruct +{ + pfnGWKResampleType pfnGWKResample; + + // Space for saved X weights + double *padfWeightsX; + char *panCalcX; + + double *padfWeightsY; // only used by GWKResampleOptimizedLanczos + int iLastSrcX; // only used by GWKResampleOptimizedLanczos + int iLastSrcY; // only used by GWKResampleOptimizedLanczos + double dfLastDeltaX; // only used by GWKResampleOptimizedLanczos + double dfLastDeltaY; // only used by GWKResampleOptimizedLanczos + + // Space for saving a row of pixels + double *padfRowDensity; + double *padfRowReal; + double *padfRowImag; +}; + +/************************************************************************/ +/* GWKResampleCreateWrkStruct() */ +/************************************************************************/ + +static int GWKResample( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + double *pdfDensity, + double *pdfReal, double *pdfImag, + GWKResampleWrkStruct* psWrkStruct ); + +static int GWKResampleOptimizedLanczos( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + double *pdfDensity, + double *pdfReal, double *pdfImag, + GWKResampleWrkStruct* psWrkStruct ); + +static GWKResampleWrkStruct* GWKResampleCreateWrkStruct(GDALWarpKernel *poWK) +{ + int nXDist = ( poWK->nXRadius + 1 ) * 2; + int nYDist = ( poWK->nYRadius + 1 ) * 2; + + GWKResampleWrkStruct* psWrkStruct = + (GWKResampleWrkStruct*)CPLMalloc(sizeof(GWKResampleWrkStruct)); + + // Alloc space for saved X weights + psWrkStruct->padfWeightsX = (double *)CPLCalloc( nXDist, sizeof(double) ); + psWrkStruct->panCalcX = (char *)CPLMalloc( nXDist * sizeof(char) ); + + psWrkStruct->padfWeightsY = (double *)CPLCalloc( nYDist, sizeof(double) ); + psWrkStruct->iLastSrcX = -10; + psWrkStruct->iLastSrcY = -10; + psWrkStruct->dfLastDeltaX = -10; + psWrkStruct->dfLastDeltaY = -10; + + // Alloc space for saving a row of pixels + if( poWK->pafUnifiedSrcDensity == NULL && + poWK->panUnifiedSrcValid == NULL && + poWK->papanBandSrcValid == NULL ) + { + psWrkStruct->padfRowDensity = NULL; + } + else + { + psWrkStruct->padfRowDensity = (double *)CPLCalloc( nXDist, sizeof(double) ); + } + psWrkStruct->padfRowReal = (double *)CPLCalloc( nXDist, sizeof(double) ); + psWrkStruct->padfRowImag = (double *)CPLCalloc( nXDist, sizeof(double) ); + + if( poWK->eResample == GRA_Lanczos ) + { + psWrkStruct->pfnGWKResample = GWKResampleOptimizedLanczos; + + const double dfXScale = poWK->dfXScale; + if( dfXScale < 1.0 ) + { + int iMin = poWK->nFiltInitX, iMax = poWK->nXRadius; + while( iMin * dfXScale < -3.0 ) + iMin ++; + while( iMax * dfXScale > 3.0 ) + iMax --; + + for(int i = iMin; i <= iMax; ++i) + { + psWrkStruct->padfWeightsX[i-poWK->nFiltInitX] = + GWKLanczosSinc(i * dfXScale); + } + } + + const double dfYScale = poWK->dfYScale; + if( dfYScale < 1.0 ) + { + int jMin = poWK->nFiltInitY, jMax = poWK->nYRadius; + while( jMin * dfYScale < -3.0 ) + jMin ++; + while( jMax * dfYScale > 3.0 ) + jMax --; + + for(int j = jMin; j <= jMax; ++j) + { + psWrkStruct->padfWeightsY[j-poWK->nFiltInitY] = + GWKLanczosSinc(j * dfYScale); + } + } + } + else + psWrkStruct->pfnGWKResample = GWKResample; + + return psWrkStruct; +} + +/************************************************************************/ +/* GWKResampleDeleteWrkStruct() */ +/************************************************************************/ + +static void GWKResampleDeleteWrkStruct(GWKResampleWrkStruct* psWrkStruct) +{ + CPLFree( psWrkStruct->padfWeightsX ); + CPLFree( psWrkStruct->padfWeightsY ); + CPLFree( psWrkStruct->panCalcX ); + CPLFree( psWrkStruct->padfRowDensity ); + CPLFree( psWrkStruct->padfRowReal ); + CPLFree( psWrkStruct->padfRowImag ); + CPLFree( psWrkStruct ); +} + +/************************************************************************/ +/* GWKResample() */ +/************************************************************************/ + +static int GWKResample( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + double *pdfDensity, + double *pdfReal, double *pdfImag, + GWKResampleWrkStruct* psWrkStruct ) + +{ + // Save as local variables to avoid following pointers in loops + const int nSrcXSize = poWK->nSrcXSize; + const int nSrcYSize = poWK->nSrcYSize; + + double dfAccumulatorReal = 0.0, dfAccumulatorImag = 0.0; + double dfAccumulatorDensity = 0.0; + double dfAccumulatorWeight = 0.0; + const int iSrcX = (int) floor( dfSrcX - 0.5 ); + const int iSrcY = (int) floor( dfSrcY - 0.5 ); + const int iSrcOffset = iSrcX + iSrcY * nSrcXSize; + const double dfDeltaX = dfSrcX - 0.5 - iSrcX; + const double dfDeltaY = dfSrcY - 0.5 - iSrcY; + + const double dfXScale = poWK->dfXScale, dfYScale = poWK->dfYScale; + + int i, j; + const int nXDist = ( poWK->nXRadius + 1 ) * 2; + + // Space for saved X weights + double *padfWeightsX = psWrkStruct->padfWeightsX; + char *panCalcX = psWrkStruct->panCalcX; + + // Space for saving a row of pixels + double *padfRowDensity = psWrkStruct->padfRowDensity; + double *padfRowReal = psWrkStruct->padfRowReal; + double *padfRowImag = psWrkStruct->padfRowImag; + + // Mark as needing calculation (don't calculate the weights yet, + // because a mask may render it unnecessary) + memset( panCalcX, FALSE, nXDist * sizeof(char) ); + + FilterFuncType pfnGetWeight = apfGWKFilter[poWK->eResample]; + CPLAssert(pfnGetWeight); + + // Skip sampling over edge of image + j = poWK->nFiltInitY; + int jMax= poWK->nYRadius; + if( iSrcY + j < 0 ) + j = -iSrcY; + if( iSrcY + jMax >= nSrcYSize ) + jMax = nSrcYSize - iSrcY - 1; + + int iMin = poWK->nFiltInitX, iMax = poWK->nXRadius; + if( iSrcX + iMin < 0 ) + iMin = -iSrcX; + if( iSrcX + iMax >= nSrcXSize ) + iMax = nSrcXSize - iSrcX - 1; + + const int bXScaleBelow1 = ( dfXScale < 1.0 ); + const int bYScaleBelow1 = ( dfYScale < 1.0 ); + + int iRowOffset = iSrcOffset + (j - 1) * nSrcXSize + iMin; + + // Loop over pixel rows in the kernel + for ( ; j <= jMax; ++j ) + { + double dfWeight1; + + iRowOffset += nSrcXSize; + + // Get pixel values + // We can potentially read extra elements after the "normal" end of the source arrays, + // but the contract of papabySrcImage[iBand], papanBandSrcValid[iBand], + // panUnifiedSrcValid and pafUnifiedSrcDensity is to have WARP_EXTRA_ELTS + // reserved at their end. + if ( !GWKGetPixelRow( poWK, iBand, iRowOffset, (iMax-iMin+2)/2, + padfRowDensity, padfRowReal, padfRowImag ) ) + continue; + + // Calculate the Y weight + dfWeight1 = ( bYScaleBelow1 ) ? + pfnGetWeight((j - dfDeltaY) * dfYScale): + pfnGetWeight(j - dfDeltaY); + + + // Iterate over pixels in row + double dfAccumulatorRealLocal = 0.0; + double dfAccumulatorImagLocal = 0.0; + double dfAccumulatorDensityLocal = 0.0; + double dfAccumulatorWeightLocal = 0.0; + + for (i = iMin; i <= iMax; ++i ) + { + double dfWeight2; + + // Skip sampling if pixel has zero density + if ( padfRowDensity != NULL && + padfRowDensity[i-iMin] < 0.000000001 ) + continue; + + // Make or use a cached set of weights for this row + if ( panCalcX[i-iMin] ) + { + // Use saved weight value instead of recomputing it + dfWeight2 = padfWeightsX[i-iMin]; + } + else + { + // Calculate & save the X weight + padfWeightsX[i-iMin] = dfWeight2 = ( bXScaleBelow1 ) ? + pfnGetWeight((i - dfDeltaX) * dfXScale): + pfnGetWeight(i - dfDeltaX); + + panCalcX[i-iMin] = TRUE; + } + + // Accumulate! + dfAccumulatorRealLocal += padfRowReal[i-iMin] * dfWeight2; + dfAccumulatorImagLocal += padfRowImag[i-iMin] * dfWeight2; + if( padfRowDensity != NULL ) + dfAccumulatorDensityLocal += padfRowDensity[i-iMin] * dfWeight2; + dfAccumulatorWeightLocal += dfWeight2; + } + + dfAccumulatorReal += dfAccumulatorRealLocal * dfWeight1; + dfAccumulatorImag += dfAccumulatorImagLocal * dfWeight1; + dfAccumulatorDensity += dfAccumulatorDensityLocal * dfWeight1; + dfAccumulatorWeight += dfAccumulatorWeightLocal * dfWeight1; + } + + if ( dfAccumulatorWeight < 0.000001 || + (padfRowDensity != NULL && dfAccumulatorDensity < 0.000001) ) + { + *pdfDensity = 0.0; + return FALSE; + } + + // Calculate the output taking into account weighting + if ( dfAccumulatorWeight < 0.99999 || dfAccumulatorWeight > 1.00001 ) + { + *pdfReal = dfAccumulatorReal / dfAccumulatorWeight; + *pdfImag = dfAccumulatorImag / dfAccumulatorWeight; + if( padfRowDensity != NULL ) + *pdfDensity = dfAccumulatorDensity / dfAccumulatorWeight; + else + *pdfDensity = 1.0; + } + else + { + *pdfReal = dfAccumulatorReal; + *pdfImag = dfAccumulatorImag; + if( padfRowDensity != NULL ) + *pdfDensity = dfAccumulatorDensity; + else + *pdfDensity = 1.0; + } + + return TRUE; +} + +/************************************************************************/ +/* GWKResampleOptimizedLanczos() */ +/************************************************************************/ + +static int GWKResampleOptimizedLanczos( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + double *pdfDensity, + double *pdfReal, double *pdfImag, + GWKResampleWrkStruct* psWrkStruct ) + +{ + // Save as local variables to avoid following pointers in loops + const int nSrcXSize = poWK->nSrcXSize; + const int nSrcYSize = poWK->nSrcYSize; + + double dfAccumulatorReal = 0.0, dfAccumulatorImag = 0.0; + double dfAccumulatorDensity = 0.0; + double dfAccumulatorWeight = 0.0; + const int iSrcX = (int) floor( dfSrcX - 0.5 ); + const int iSrcY = (int) floor( dfSrcY - 0.5 ); + const int iSrcOffset = iSrcX + iSrcY * nSrcXSize; + const double dfDeltaX = dfSrcX - 0.5 - iSrcX; + const double dfDeltaY = dfSrcY - 0.5 - iSrcY; + + const double dfXScale = poWK->dfXScale, dfYScale = poWK->dfYScale; + + // Space for saved X weights + double *padfWeightsX = psWrkStruct->padfWeightsX; + double *padfWeightsY = psWrkStruct->padfWeightsY; + + // Space for saving a row of pixels + double *padfRowDensity = psWrkStruct->padfRowDensity; + double *padfRowReal = psWrkStruct->padfRowReal; + double *padfRowImag = psWrkStruct->padfRowImag; + + // Skip sampling over edge of image + int jMin = poWK->nFiltInitY, jMax= poWK->nYRadius; + if( iSrcY + jMin < 0 ) + jMin = -iSrcY; + if( iSrcY + jMax >= nSrcYSize ) + jMax = nSrcYSize - iSrcY - 1; + + int iMin = poWK->nFiltInitX, iMax = poWK->nXRadius; + if( iSrcX + iMin < 0 ) + iMin = -iSrcX; + if( iSrcX + iMax >= nSrcXSize ) + iMax = nSrcXSize - iSrcX - 1; + + if( dfXScale < 1.0 ) + { + while( iMin * dfXScale < -3.0 ) + iMin ++; + while( iMax * dfXScale > 3.0 ) + iMax --; + // padfWeightsX computed in GWKResampleCreateWrkStruct + } + else + { + while( iMin - dfDeltaX < -3.0 ) + iMin ++; + while( iMax - dfDeltaX > 3.0 ) + iMax --; + + if( iSrcX != psWrkStruct->iLastSrcX || + dfDeltaX != psWrkStruct->dfLastDeltaX ) + { + // Optimisation of GWKLanczosSinc(i - dfDeltaX) based on the following + // trigonometric formulas. + + //sin(GWK_PI * (dfBase + k)) = sin(GWK_PI * dfBase) * cos(GWK_PI * k) + cos(GWK_PI * dfBase) * sin(GWK_PI * k) + //sin(GWK_PI * (dfBase + k)) = dfSinPIBase * cos(GWK_PI * k) + dfCosPIBase * sin(GWK_PI * k) + //sin(GWK_PI * (dfBase + k)) = dfSinPIBase * cos(GWK_PI * k) + //sin(GWK_PI * (dfBase + k)) = dfSinPIBase * (((k % 2) == 0) ? 1 : -1) + + //sin(GWK_PI / dfR * (dfBase + k)) = sin(GWK_PI / dfR * dfBase) * cos(GWK_PI / dfR * k) + cos(GWK_PI / dfR * dfBase) * sin(GWK_PI / dfR * k) + //sin(GWK_PI / dfR * (dfBase + k)) = dfSinPIBaseOverR * cos(GWK_PI / dfR * k) + dfCosPIBaseOverR * sin(GWK_PI / dfR * k) + + double dfSinPIDeltaXOver3 = sin((-GWK_PI / 3) * dfDeltaX); + double dfSin2PIDeltaXOver3 = dfSinPIDeltaXOver3 * dfSinPIDeltaXOver3; + /* ok to use sqrt(1-sin^2) since GWK_PI / 3 * dfDeltaX < PI/2 */ + double dfCosPIDeltaXOver3 = sqrt(1 - dfSin2PIDeltaXOver3); + double dfSinPIDeltaX = (3-4*dfSin2PIDeltaXOver3)*dfSinPIDeltaXOver3; + const double dfInvPI2Over3 = 3.0 / (GWK_PI * GWK_PI); + double dfInvPI2Over3xSinPIDeltaX = dfInvPI2Over3 * dfSinPIDeltaX; + double dfInvPI2Over3xSinPIDeltaXxm0d5SinPIDeltaXOver3 = + -0.5 * dfInvPI2Over3xSinPIDeltaX * dfSinPIDeltaXOver3; + const double dfSinPIOver3 = 0.8660254037844386; + double dfInvPI2Over3xSinPIDeltaXxSinPIOver3xCosPIDeltaXOver3 = + dfSinPIOver3 * dfInvPI2Over3xSinPIDeltaX * dfCosPIDeltaXOver3; + double padfCst[] = { + dfInvPI2Over3xSinPIDeltaX * dfSinPIDeltaXOver3, + dfInvPI2Over3xSinPIDeltaXxm0d5SinPIDeltaXOver3 - + dfInvPI2Over3xSinPIDeltaXxSinPIOver3xCosPIDeltaXOver3, + dfInvPI2Over3xSinPIDeltaXxm0d5SinPIDeltaXOver3 + + dfInvPI2Over3xSinPIDeltaXxSinPIOver3xCosPIDeltaXOver3 }; + + for (int i = iMin; i <= iMax; ++i ) + { + const double dfX = i - dfDeltaX; + if (dfX == 0.0) + padfWeightsX[i-poWK->nFiltInitX] = 1.0; + else + padfWeightsX[i-poWK->nFiltInitX] = + padfCst[(i + 3) % 3] / (dfX * dfX); + //CPLAssert(fabs(padfWeightsX[i-poWK->nFiltInitX] - GWKLanczosSinc(dfX, 3.0)) < 1e-10); + } + + psWrkStruct->iLastSrcX = iSrcX; + psWrkStruct->dfLastDeltaX = dfDeltaX; + } + } + + if( dfYScale < 1.0 ) + { + while( jMin * dfYScale < -3.0 ) + jMin ++; + while( jMax * dfYScale > 3.0 ) + jMax --; + // padfWeightsY computed in GWKResampleCreateWrkStruct + } + else + { + while( jMin - dfDeltaY < -3.0 ) + jMin ++; + while( jMax - dfDeltaY > 3.0 ) + jMax --; + + if( iSrcY != psWrkStruct->iLastSrcY || + dfDeltaY != psWrkStruct->dfLastDeltaY ) + { + double dfSinPIDeltaYOver3 = sin((-GWK_PI / 3) * dfDeltaY); + double dfSin2PIDeltaYOver3 = dfSinPIDeltaYOver3 * dfSinPIDeltaYOver3; + /* ok to use sqrt(1-sin^2) since GWK_PI / 3 * dfDeltaY < PI/2 */ + double dfCosPIDeltaYOver3 = sqrt(1 - dfSin2PIDeltaYOver3); + double dfSinPIDeltaY = (3-4*dfSin2PIDeltaYOver3)*dfSinPIDeltaYOver3; + const double dfInvPI2Over3 = 3.0 / (GWK_PI * GWK_PI); + double dfInvPI2Over3xSinPIDeltaY = dfInvPI2Over3 * dfSinPIDeltaY; + double dfInvPI2Over3xSinPIDeltaYxm0d5SinPIDeltaYOver3 = + -0.5 * dfInvPI2Over3xSinPIDeltaY * dfSinPIDeltaYOver3; + const double dfSinPIOver3 = 0.8660254037844386; + double dfInvPI2Over3xSinPIDeltaYxSinPIOver3xCosPIDeltaYOver3 = + dfSinPIOver3 * dfInvPI2Over3xSinPIDeltaY * dfCosPIDeltaYOver3; + double padfCst[] = { + dfInvPI2Over3xSinPIDeltaY * dfSinPIDeltaYOver3, + dfInvPI2Over3xSinPIDeltaYxm0d5SinPIDeltaYOver3 - + dfInvPI2Over3xSinPIDeltaYxSinPIOver3xCosPIDeltaYOver3, + dfInvPI2Over3xSinPIDeltaYxm0d5SinPIDeltaYOver3 + + dfInvPI2Over3xSinPIDeltaYxSinPIOver3xCosPIDeltaYOver3 }; + + for ( int j = jMin; j <= jMax; ++j ) + { + const double dfY = j - dfDeltaY; + if (dfY == 0.0) + padfWeightsY[j-poWK->nFiltInitY] = 1.0; + else + padfWeightsY[j-poWK->nFiltInitY] = + padfCst[(j + 3) % 3] / (dfY * dfY); + //CPLAssert(fabs(padfWeightsY[j-poWK->nFiltInitY] - GWKLanczosSinc(dfY, 3.0)) < 1e-10); + } + + psWrkStruct->iLastSrcY = iSrcY; + psWrkStruct->dfLastDeltaY = dfDeltaY; + } + } + + int iRowOffset = iSrcOffset + (jMin - 1) * nSrcXSize + iMin; + + // If we have no density information, we can simply compute the + // accumulated weight. + if( padfRowDensity == NULL ) + { + double dfRowAccWeight = 0.0; + for (int i = iMin; i <= iMax; ++i ) + { + dfRowAccWeight += padfWeightsX[i-poWK->nFiltInitX]; + } + double dfColAccWeight = 0.0; + for ( int j = jMin; j <= jMax; ++j ) + { + dfColAccWeight += padfWeightsY[j-poWK->nFiltInitY]; + } + dfAccumulatorWeight = dfRowAccWeight * dfColAccWeight; + + if( !GDALDataTypeIsComplex(poWK->eWorkingDataType) ) + padfRowImag = NULL; + } + + // Loop over pixel rows in the kernel + for ( int j = jMin; j <= jMax; ++j ) + { + double dfWeight1; + + iRowOffset += nSrcXSize; + + // Get pixel values + // We can potentially read extra elements after the "normal" end of the source arrays, + // but the contract of papabySrcImage[iBand], papanBandSrcValid[iBand], + // panUnifiedSrcValid and pafUnifiedSrcDensity is to have WARP_EXTRA_ELTS + // reserved at their end. + if ( !GWKGetPixelRow( poWK, iBand, iRowOffset, (iMax-iMin+2)/2, + padfRowDensity, padfRowReal, padfRowImag ) ) + continue; + + dfWeight1 = padfWeightsY[j-poWK->nFiltInitY]; + + // Iterate over pixels in row + if ( padfRowDensity != NULL ) + { + for (int i = iMin; i <= iMax; ++i ) + { + double dfWeight2; + + // Skip sampling if pixel has zero density + if ( padfRowDensity[i - iMin] < 0.000000001 ) + continue; + + // Use a cached set of weights for this row + dfWeight2 = dfWeight1 * padfWeightsX[i- poWK->nFiltInitX]; + + // Accumulate! + dfAccumulatorReal += padfRowReal[i - iMin] * dfWeight2; + dfAccumulatorImag += padfRowImag[i - iMin] * dfWeight2; + dfAccumulatorDensity += padfRowDensity[i - iMin] * dfWeight2; + dfAccumulatorWeight += dfWeight2; + } + } + else if( padfRowImag == NULL ) + { + double dfRowAccReal = 0.0; + for (int i = iMin; i <= iMax; ++i ) + { + double dfWeight2 = padfWeightsX[i- poWK->nFiltInitX]; + + // Accumulate! + dfRowAccReal += padfRowReal[i - iMin] * dfWeight2; + } + + dfAccumulatorReal += dfRowAccReal * dfWeight1; + } + else + { + double dfRowAccReal = 0.0; + double dfRowAccImag = 0.0; + for (int i = iMin; i <= iMax; ++i ) + { + double dfWeight2 = padfWeightsX[i- poWK->nFiltInitX]; + + // Accumulate! + dfRowAccReal += padfRowReal[i - iMin] * dfWeight2; + dfRowAccImag += padfRowImag[i - iMin] * dfWeight2; + } + + dfAccumulatorReal += dfRowAccReal * dfWeight1; + dfAccumulatorImag += dfRowAccImag * dfWeight1; + } + } + + if ( dfAccumulatorWeight < 0.000001 || + (padfRowDensity != NULL && dfAccumulatorDensity < 0.000001) ) + { + *pdfDensity = 0.0; + return FALSE; + } + + // Calculate the output taking into account weighting + if ( dfAccumulatorWeight < 0.99999 || dfAccumulatorWeight > 1.00001 ) + { + const double dfInvAcc = 1.0 / dfAccumulatorWeight; + *pdfReal = dfAccumulatorReal * dfInvAcc; + *pdfImag = dfAccumulatorImag * dfInvAcc; + if( padfRowDensity != NULL ) + *pdfDensity = dfAccumulatorDensity * dfInvAcc; + else + *pdfDensity = 1.0; + } + else + { + *pdfReal = dfAccumulatorReal; + *pdfImag = dfAccumulatorImag; + if( padfRowDensity != NULL ) + *pdfDensity = dfAccumulatorDensity; + else + *pdfDensity = 1.0; + } + + return TRUE; +} + +/************************************************************************/ +/* GWKResampleNoMasksT() */ +/************************************************************************/ + +template +static int GWKResampleNoMasksT( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + T *pValue, double *padfWeight ) + +{ + // Commonly used; save locally + int nSrcXSize = poWK->nSrcXSize; + int nSrcYSize = poWK->nSrcYSize; + + double dfAccumulator = 0.0; + int iSrcX = (int) floor( dfSrcX - 0.5 ); + int iSrcY = (int) floor( dfSrcY - 0.5 ); + int iSrcOffset = iSrcX + iSrcY * nSrcXSize; + double dfDeltaX = dfSrcX - 0.5 - iSrcX; + double dfDeltaY = dfSrcY - 0.5 - iSrcY; + + double dfXScale = poWK->dfXScale; + double dfYScale = poWK->dfYScale; + int nXRadius = poWK->nXRadius; + int nYRadius = poWK->nYRadius; + + T* pSrcBand = (T*) poWK->papabySrcImage[iBand]; + + // Politely refusing to process invalid coordinates or obscenely small image + if ( iSrcX >= nSrcXSize || iSrcY >= nSrcYSize + || nXRadius > nSrcXSize || nYRadius > nSrcYSize ) + return GWKBilinearResampleNoMasks4SampleT( poWK, iBand, dfSrcX, dfSrcY, pValue); + + FilterFuncType pfnGetWeight = apfGWKFilter[poWK->eResample]; + CPLAssert(pfnGetWeight); + FilterFunc4ValuesType pfnGetWeight4Values = apfGWKFilter4Values[poWK->eResample]; + CPLAssert(pfnGetWeight4Values); + + if( dfXScale > 1.0 ) dfXScale = 1.0; + if( dfYScale > 1.0 ) dfYScale = 1.0; + + // Loop over all rows in the kernel + double dfAccumulatorWeightHorizontal = 0.0; + double dfAccumulatorWeightVertical = 0.0; + + int iMin = 1 - nXRadius; + if( iSrcX + iMin < 0 ) + iMin = -iSrcX; + int iMax = nXRadius; + if( iSrcX + iMax >= nSrcXSize-1 ) + iMax = nSrcXSize-1 - iSrcX; + int i, iC; + for(iC = 0, i = iMin; i+2 < iMax; i+=4, iC+=4 ) + { + padfWeight[iC] = (i - dfDeltaX) * dfXScale; + padfWeight[iC+1] = padfWeight[iC] + dfXScale; + padfWeight[iC+2] = padfWeight[iC+1] + dfXScale; + padfWeight[iC+3] = padfWeight[iC+2] + dfXScale; + dfAccumulatorWeightHorizontal += pfnGetWeight4Values(padfWeight+iC); + } + for(; i <= iMax; ++i, ++iC ) + { + double dfWeight = pfnGetWeight((i - dfDeltaX) * dfXScale); + padfWeight[iC] = dfWeight; + dfAccumulatorWeightHorizontal += dfWeight; + } + + int j = 1 - nYRadius; + if( iSrcY + j < 0 ) + j = -iSrcY; + int jMax = nYRadius; + if( iSrcY + jMax >= nSrcYSize-1 ) + jMax = nSrcYSize-1 - iSrcY; + + for ( ; j <= jMax; ++j ) + { + int iSampJ = iSrcOffset + j * nSrcXSize; + + // Loop over all pixels in the row + double dfAccumulatorLocal = 0.0, dfAccumulatorLocal2 = 0.0; + iC = 0; + i = iMin; + /* Process by chunk of 4 cols */ + for(; i+2 < iMax; i+=4, iC+=4 ) + { + // Retrieve the pixel & accumulate + dfAccumulatorLocal += (double)pSrcBand[i+iSampJ] * padfWeight[iC]; + dfAccumulatorLocal += (double)pSrcBand[i+1+iSampJ] * padfWeight[iC+1]; + dfAccumulatorLocal2 += (double)pSrcBand[i+2+iSampJ] * padfWeight[iC+2]; + dfAccumulatorLocal2 += (double)pSrcBand[i+3+iSampJ] * padfWeight[iC+3]; + } + dfAccumulatorLocal += dfAccumulatorLocal2; + if( i < iMax ) + { + dfAccumulatorLocal += (double)pSrcBand[i+iSampJ] * padfWeight[iC]; + dfAccumulatorLocal += (double)pSrcBand[i+1+iSampJ] * padfWeight[iC+1]; + i+=2; + iC+=2; + } + if( i == iMax ) + { + dfAccumulatorLocal += (double)pSrcBand[i+iSampJ] * padfWeight[iC]; + } + + // Calculate the Y weight + double dfWeight = pfnGetWeight((j - dfDeltaY) * dfYScale); + dfAccumulator += dfWeight * dfAccumulatorLocal; + dfAccumulatorWeightVertical += dfWeight; + } + + double dfAccumulatorWeight = dfAccumulatorWeightHorizontal * dfAccumulatorWeightVertical; + + *pValue = GWKClampValueT(dfAccumulator / dfAccumulatorWeight); + + return TRUE; +} + +/* We restrict to 64bit processors because they are guaranteed to have SSE2 */ +/* Could possibly be used too on 32bit, but we would need to check at runtime */ +#if defined(__x86_64) || defined(_M_X64) + +#include + +/************************************************************************/ +/* GWKResampleNoMasks_SSE2_T() */ +/************************************************************************/ + +template +static int GWKResampleNoMasks_SSE2_T( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + T *pValue, double *padfWeight ) +{ + // Commonly used; save locally + int nSrcXSize = poWK->nSrcXSize; + int nSrcYSize = poWK->nSrcYSize; + + double dfAccumulator = 0.0; + int iSrcX = (int) floor( dfSrcX - 0.5 ); + int iSrcY = (int) floor( dfSrcY - 0.5 ); + int iSrcOffset = iSrcX + iSrcY * nSrcXSize; + double dfDeltaX = dfSrcX - 0.5 - iSrcX; + double dfDeltaY = dfSrcY - 0.5 - iSrcY; + + double dfXScale = poWK->dfXScale; + double dfYScale = poWK->dfYScale; + int nXRadius = poWK->nXRadius; + int nYRadius = poWK->nYRadius; + + const T* pSrcBand = (const T*) poWK->papabySrcImage[iBand]; + + // Politely refusing to process invalid coordinates or obscenely small image + if ( iSrcX >= nSrcXSize || iSrcY >= nSrcYSize + || nXRadius > nSrcXSize || nYRadius > nSrcYSize ) + return GWKBilinearResampleNoMasks4SampleT( poWK, iBand, dfSrcX, dfSrcY, pValue); + + FilterFuncType pfnGetWeight = apfGWKFilter[poWK->eResample]; + CPLAssert(pfnGetWeight); + FilterFunc4ValuesType pfnGetWeight4Values = apfGWKFilter4Values[poWK->eResample]; + CPLAssert(pfnGetWeight4Values); + + if( dfXScale > 1.0 ) dfXScale = 1.0; + if( dfYScale > 1.0 ) dfYScale = 1.0; + + // Loop over all rows in the kernel + double dfAccumulatorWeightHorizontal = 0.0; + double dfAccumulatorWeightVertical = 0.0; + + int iMin = 1 - nXRadius; + if( iSrcX + iMin < 0 ) + iMin = -iSrcX; + int iMax = nXRadius; + if( iSrcX + iMax >= nSrcXSize-1 ) + iMax = nSrcXSize-1 - iSrcX; + int i, iC; + for(iC = 0, i = iMin; i+2 < iMax; i+=4, iC+=4 ) + { + padfWeight[iC] = (i - dfDeltaX) * dfXScale; + padfWeight[iC+1] = padfWeight[iC] + dfXScale; + padfWeight[iC+2] = padfWeight[iC+1] + dfXScale; + padfWeight[iC+3] = padfWeight[iC+2] + dfXScale; + dfAccumulatorWeightHorizontal += pfnGetWeight4Values(padfWeight+iC); + } + for(; i <= iMax; ++i, ++iC ) + { + double dfWeight = pfnGetWeight((i - dfDeltaX) * dfXScale); + padfWeight[iC] = dfWeight; + dfAccumulatorWeightHorizontal += dfWeight; + } + + int j = 1 - nYRadius; + if( iSrcY + j < 0 ) + j = -iSrcY; + int jMax = nYRadius; + if( iSrcY + jMax >= nSrcYSize-1 ) + jMax = nSrcYSize-1 - iSrcY; + + /* Process by chunk of 4 rows */ + for ( ; j+2 < jMax; j+=4 ) + { + int iSampJ = iSrcOffset + j * nSrcXSize; + + // Loop over all pixels in the row + iC = 0; + i = iMin; + /* Process by chunk of 4 cols */ + XMMReg4Double v_acc_1 = XMMReg4Double::Zero(); + XMMReg4Double v_acc_2 = XMMReg4Double::Zero(); + XMMReg4Double v_acc_3 = XMMReg4Double::Zero(); + XMMReg4Double v_acc_4 = XMMReg4Double::Zero(); + for(; i+2 < iMax; i+=4, iC+=4 ) + { + // Retrieve the pixel & accumulate + XMMReg4Double v_pixels_1 = XMMReg4Double::Load4Val(pSrcBand+i+iSampJ); + XMMReg4Double v_pixels_2 = XMMReg4Double::Load4Val(pSrcBand+i+iSampJ+nSrcXSize); + XMMReg4Double v_pixels_3 = XMMReg4Double::Load4Val(pSrcBand+i+iSampJ+2*nSrcXSize); + XMMReg4Double v_pixels_4 = XMMReg4Double::Load4Val(pSrcBand+i+iSampJ+3*nSrcXSize); + + XMMReg4Double v_padfWeight = XMMReg4Double::Load4Val(padfWeight + iC); + + v_acc_1 += v_pixels_1 * v_padfWeight; + v_acc_2 += v_pixels_2 * v_padfWeight; + v_acc_3 += v_pixels_3 * v_padfWeight; + v_acc_4 += v_pixels_4 * v_padfWeight; + } + + if( i < iMax ) + { + XMMReg2Double v_pixels_1 = XMMReg2Double::Load2Val(pSrcBand+i+iSampJ); + XMMReg2Double v_pixels_2 = XMMReg2Double::Load2Val(pSrcBand+i+iSampJ+nSrcXSize); + XMMReg2Double v_pixels_3 = XMMReg2Double::Load2Val(pSrcBand+i+iSampJ+2*nSrcXSize); + XMMReg2Double v_pixels_4 = XMMReg2Double::Load2Val(pSrcBand+i+iSampJ+3*nSrcXSize); + + XMMReg2Double v_padfWeight = XMMReg2Double::Load2Val(padfWeight + iC); + + v_acc_1.GetLow() += v_pixels_1 * v_padfWeight; + v_acc_2.GetLow() += v_pixels_2 * v_padfWeight; + v_acc_3.GetLow() += v_pixels_3 * v_padfWeight; + v_acc_4.GetLow() += v_pixels_4 * v_padfWeight; + + i+=2; + iC+=2; + } + + v_acc_1.AddLowAndHigh(); + v_acc_2.AddLowAndHigh(); + v_acc_3.AddLowAndHigh(); + v_acc_4.AddLowAndHigh(); + + double dfAccumulatorLocal_1 = (double)v_acc_1.GetLow(), + dfAccumulatorLocal_2 = (double)v_acc_2.GetLow(), + dfAccumulatorLocal_3 = (double)v_acc_3.GetLow(), + dfAccumulatorLocal_4 = (double)v_acc_4.GetLow(); + + if( i == iMax ) + { + dfAccumulatorLocal_1 += (double)pSrcBand[i+iSampJ] * padfWeight[iC]; + dfAccumulatorLocal_2 += (double)pSrcBand[i+iSampJ + nSrcXSize] * padfWeight[iC]; + dfAccumulatorLocal_3 += (double)pSrcBand[i+iSampJ + 2 * nSrcXSize] * padfWeight[iC]; + dfAccumulatorLocal_4 += (double)pSrcBand[i+iSampJ + 3 * nSrcXSize] * padfWeight[iC]; + } + + // Calculate the Y weight + double adfWeight[4]; + adfWeight[0] = (j - dfDeltaY) * dfYScale; + adfWeight[1] = adfWeight[0] + dfYScale; + adfWeight[2] = adfWeight[1] + dfYScale; + adfWeight[3] = adfWeight[2] + dfYScale; + dfAccumulatorWeightVertical += pfnGetWeight4Values(adfWeight); + dfAccumulator += adfWeight[0] * dfAccumulatorLocal_1; + dfAccumulator += adfWeight[1] * dfAccumulatorLocal_2; + dfAccumulator += adfWeight[2] * dfAccumulatorLocal_3; + dfAccumulator += adfWeight[3] * dfAccumulatorLocal_4; + } + for ( ; j <= jMax; ++j ) + { + int iSampJ = iSrcOffset + j * nSrcXSize; + + // Loop over all pixels in the row + iC = 0; + i = iMin; + /* Process by chunk of 4 cols */ + XMMReg4Double v_acc = XMMReg4Double::Zero(); + for(; i+2 < iMax; i+=4, iC+=4 ) + { + // Retrieve the pixel & accumulate + XMMReg4Double v_pixels= XMMReg4Double::Load4Val(pSrcBand+i+iSampJ); + XMMReg4Double v_padfWeight = XMMReg4Double::Load4Val(padfWeight + iC); + + v_acc += v_pixels * v_padfWeight; + } + + v_acc.AddLowAndHigh(); + + double dfAccumulatorLocal = (double)v_acc.GetLow(); + + if( i < iMax ) + { + dfAccumulatorLocal += (double)pSrcBand[i+iSampJ] * padfWeight[iC]; + dfAccumulatorLocal += (double)pSrcBand[i+1+iSampJ] * padfWeight[iC+1]; + i+=2; + iC+=2; + } + if( i == iMax ) + { + dfAccumulatorLocal += (double)pSrcBand[i+iSampJ] * padfWeight[iC]; + } + + // Calculate the Y weight + double dfWeight = pfnGetWeight((j - dfDeltaY) * dfYScale); + dfAccumulator += dfWeight * dfAccumulatorLocal; + dfAccumulatorWeightVertical += dfWeight; + } + + double dfAccumulatorWeight = dfAccumulatorWeightHorizontal * dfAccumulatorWeightVertical; + + *pValue = GWKClampValueT(dfAccumulator / dfAccumulatorWeight); + + return TRUE; +} + +/************************************************************************/ +/* GWKResampleNoMasksT() */ +/************************************************************************/ + +template<> +int GWKResampleNoMasksT( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + GByte *pValue, double *padfWeight ) +{ + return GWKResampleNoMasks_SSE2_T(poWK, iBand, dfSrcX, dfSrcY, pValue, padfWeight); +} + +/************************************************************************/ +/* GWKResampleNoMasksT() */ +/************************************************************************/ + +template<> +int GWKResampleNoMasksT( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + GInt16 *pValue, double *padfWeight ) +{ + return GWKResampleNoMasks_SSE2_T(poWK, iBand, dfSrcX, dfSrcY, pValue, padfWeight); +} + +/************************************************************************/ +/* GWKResampleNoMasksT() */ +/************************************************************************/ + +template<> +int GWKResampleNoMasksT( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + GUInt16 *pValue, double *padfWeight ) +{ + return GWKResampleNoMasks_SSE2_T(poWK, iBand, dfSrcX, dfSrcY, pValue, padfWeight); +} + +/************************************************************************/ +/* GWKResampleNoMasksT() */ +/************************************************************************/ + +template<> +int GWKResampleNoMasksT( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + float *pValue, double *padfWeight ) +{ + return GWKResampleNoMasks_SSE2_T(poWK, iBand, dfSrcX, dfSrcY, pValue, padfWeight); +} + +#ifdef INSTANCIATE_FLOAT64_SSE2_IMPL + +/************************************************************************/ +/* GWKResampleNoMasksT() */ +/************************************************************************/ + +template<> +int GWKResampleNoMasksT( GDALWarpKernel *poWK, int iBand, + double dfSrcX, double dfSrcY, + double *pValue, double *padfWeight ) +{ + return GWKResampleNoMasks_SSE2_T(poWK, iBand, dfSrcX, dfSrcY, pValue, padfWeight); +} + +#endif /* INSTANCIATE_FLOAT64_SSE2_IMPL */ + +#endif /* defined(__x86_64) || defined(_M_X64) */ + +/************************************************************************/ +/* GWKRoundSourceCoordinates() */ +/************************************************************************/ + +static void GWKRoundSourceCoordinates(int nDstXSize, + double* padfX, + double* padfY, + double* padfZ, + int* pabSuccess, + double dfSrcCoordPrecision, + double dfErrorThreshold, + GDALTransformerFunc pfnTransformer, + void* pTransformerArg, + double dfDstXOff, + double dfDstY) +{ + double dfPct = 0.8; + if( dfErrorThreshold > 0 && dfSrcCoordPrecision / dfErrorThreshold >= 10.0 ) + { + dfPct = 1.0 - 2 * 1.0 / (dfSrcCoordPrecision / dfErrorThreshold); + } + double dfExactTransformThreshold = 0.5 * dfPct * dfSrcCoordPrecision; + + for( int iDstX = 0; iDstX < nDstXSize; iDstX++ ) + { + double dfXBefore = padfX[iDstX], dfYBefore = padfY[iDstX]; + padfX[iDstX] = floor(padfX[iDstX] / dfSrcCoordPrecision + 0.5) * dfSrcCoordPrecision; + padfY[iDstX] = floor(padfY[iDstX] / dfSrcCoordPrecision + 0.5) * dfSrcCoordPrecision; + + /* If we are in an uncertainty zone, go to non approximated transformation */ + /* Due to the 80% of half-precision threshold, dfSrcCoordPrecision must */ + /* be at least 10 times greater than the approximation error */ + if( fabs(dfXBefore - padfX[iDstX]) > dfExactTransformThreshold || + fabs(dfYBefore - padfY[iDstX]) > dfExactTransformThreshold ) + { + padfX[iDstX] = iDstX + dfDstXOff; + padfY[iDstX] = dfDstY; + padfZ[iDstX] = 0.0; + pfnTransformer( pTransformerArg, TRUE, 1, + padfX + iDstX, padfY + iDstX, + padfZ + iDstX, pabSuccess + iDstX ); + padfX[iDstX] = floor(padfX[iDstX] / dfSrcCoordPrecision + 0.5) * dfSrcCoordPrecision; + padfY[iDstX] = floor(padfY[iDstX] / dfSrcCoordPrecision + 0.5) * dfSrcCoordPrecision; + } + } +} + +/************************************************************************/ +/* GWKOpenCLCase() */ +/* */ +/* This is identical to GWKGeneralCase(), but functions via */ +/* OpenCL. This means we have vector optimization (SSE) and/or */ +/* GPU optimization depending on our prefs. The code itsef is */ +/* general and not optimized, but by defining constants we can */ +/* make some pretty darn good code on the fly. */ +/************************************************************************/ + +#if defined(HAVE_OPENCL) +static CPLErr GWKOpenCLCase( GDALWarpKernel *poWK ) +{ + int iDstY, iBand; + int nDstXSize = poWK->nDstXSize, nDstYSize = poWK->nDstYSize; + int nSrcXSize = poWK->nSrcXSize, nSrcYSize = poWK->nSrcYSize; + int nDstXOff = poWK->nDstXOff , nDstYOff = poWK->nDstYOff; + int nSrcXOff = poWK->nSrcXOff , nSrcYOff = poWK->nSrcYOff; + CPLErr eErr = CE_None; + struct oclWarper *warper; + cl_channel_type imageFormat; + int useImag = FALSE; + OCLResampAlg resampAlg; + cl_int err; + + switch ( poWK->eWorkingDataType ) + { + case GDT_Byte: + imageFormat = CL_UNORM_INT8; + break; + case GDT_UInt16: + imageFormat = CL_UNORM_INT16; + break; + case GDT_CInt16: + useImag = TRUE; + case GDT_Int16: + imageFormat = CL_SNORM_INT16; + break; + case GDT_CFloat32: + useImag = TRUE; + case GDT_Float32: + imageFormat = CL_FLOAT; + break; + default: + // We don't support higher precision formats + CPLDebug( "OpenCL", + "Unsupported resampling OpenCL data type %d.", + (int) poWK->eWorkingDataType ); + return CE_Warning; + } + + switch (poWK->eResample) + { + case GRA_Bilinear: + resampAlg = OCL_Bilinear; + break; + case GRA_Cubic: + resampAlg = OCL_Cubic; + break; + case GRA_CubicSpline: + resampAlg = OCL_CubicSpline; + break; + case GRA_Lanczos: + resampAlg = OCL_Lanczos; + break; + default: + // We don't support higher precision formats + CPLDebug( "OpenCL", + "Unsupported resampling OpenCL resampling alg %d.", + (int) poWK->eResample ); + return CE_Warning; + } + + // Using a factor of 2 or 4 seems to have much less rounding error than 3 on the GPU. + // Then the rounding error can cause strange artifacting under the right conditions. + warper = GDALWarpKernelOpenCL_createEnv(nSrcXSize, nSrcYSize, + nDstXSize, nDstYSize, + imageFormat, poWK->nBands, 4, + useImag, poWK->papanBandSrcValid != NULL, + poWK->pafDstDensity, + poWK->padfDstNoDataReal, + resampAlg, &err ); + + if(err != CL_SUCCESS || warper == NULL) + { + eErr = CE_Warning; + if (warper != NULL) + goto free_warper; + return eErr; + } + + CPLDebug( "GDAL", "GDALWarpKernel()::GWKOpenCLCase()\n" + "Src=%d,%d,%dx%d Dst=%d,%d,%dx%d", + nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize, + nDstXOff, nDstYOff, nDstXSize, nDstYSize ); + + if( !poWK->pfnProgress( poWK->dfProgressBase, "", poWK->pProgress ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + goto free_warper; + } + + /* ==================================================================== */ + /* Loop over bands. */ + /* ==================================================================== */ + for( iBand = 0; iBand < poWK->nBands; iBand++ ) { + if( poWK->papanBandSrcValid != NULL && poWK->papanBandSrcValid[iBand] != NULL) { + GDALWarpKernelOpenCL_setSrcValid(warper, (int *)poWK->papanBandSrcValid[iBand], iBand); + if(err != CL_SUCCESS) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OpenCL routines reported failure (%d) on line %d.", (int) err, __LINE__ ); + eErr = CE_Failure; + goto free_warper; + } + } + + err = GDALWarpKernelOpenCL_setSrcImg(warper, poWK->papabySrcImage[iBand], iBand); + if(err != CL_SUCCESS) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OpenCL routines reported failure (%d) on line %d.", (int) err, __LINE__ ); + eErr = CE_Failure; + goto free_warper; + } + + err = GDALWarpKernelOpenCL_setDstImg(warper, poWK->papabyDstImage[iBand], iBand); + if(err != CL_SUCCESS) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OpenCL routines reported failure (%d) on line %d.", (int) err, __LINE__ ); + eErr = CE_Failure; + goto free_warper; + } + } + + /* -------------------------------------------------------------------- */ + /* Allocate x,y,z coordinate arrays for transformation ... one */ + /* scanlines worth of positions. */ + /* -------------------------------------------------------------------- */ + double *padfX, *padfY, *padfZ; + int *pabSuccess; + double dfSrcCoordPrecision; + double dfErrorThreshold; + + padfX = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfY = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfZ = (double *) CPLMalloc(sizeof(double) * nDstXSize); + pabSuccess = (int *) CPLMalloc(sizeof(int) * nDstXSize); + dfSrcCoordPrecision = CPLAtof( + CSLFetchNameValueDef(poWK->papszWarpOptions, "SRC_COORD_PRECISION", "0")); + dfErrorThreshold = CPLAtof( + CSLFetchNameValueDef(poWK->papszWarpOptions, "ERROR_THRESHOLD", "0")); + + /* ==================================================================== */ + /* Loop over output lines. */ + /* ==================================================================== */ + for( iDstY = 0; iDstY < nDstYSize && eErr == CE_None; ++iDstY ) + { + int iDstX; + + /* ---------------------------------------------------------------- */ + /* Setup points to transform to source image space. */ + /* ---------------------------------------------------------------- */ + for( iDstX = 0; iDstX < nDstXSize; ++iDstX ) + { + padfX[iDstX] = iDstX + 0.5 + nDstXOff; + padfY[iDstX] = iDstY + 0.5 + nDstYOff; + padfZ[iDstX] = 0.0; + } + + /* ---------------------------------------------------------------- */ + /* Transform the points from destination pixel/line coordinates*/ + /* to source pixel/line coordinates. */ + /* ---------------------------------------------------------------- */ + poWK->pfnTransformer( poWK->pTransformerArg, TRUE, nDstXSize, + padfX, padfY, padfZ, pabSuccess ); + if( dfSrcCoordPrecision > 0.0 ) + { + GWKRoundSourceCoordinates(nDstXSize, padfX, padfY, padfZ, pabSuccess, + dfSrcCoordPrecision, + dfErrorThreshold, + poWK->pfnTransformer, + poWK->pTransformerArg, + 0.5 + nDstXOff, + iDstY + 0.5 + nDstYOff); + } + + err = GDALWarpKernelOpenCL_setCoordRow(warper, padfX, padfY, + nSrcXOff, nSrcYOff, + pabSuccess, iDstY); + if(err != CL_SUCCESS) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OpenCL routines reported failure (%d) on line %d.", (int) err, __LINE__ ); + return CE_Failure; + } + + //Update the valid & density masks because we don't do so in the kernel + for( iDstX = 0; iDstX < nDstXSize && eErr == CE_None; iDstX++ ) + { + double dfX = padfX[iDstX]; + double dfY = padfY[iDstX]; + int iDstOffset = iDstX + iDstY * nDstXSize; + + //See GWKGeneralCase() for appropriate commenting + if( !pabSuccess[iDstX] || dfX < nSrcXOff || dfY < nSrcYOff ) + continue; + + int iSrcX = ((int) dfX) - nSrcXOff; + int iSrcY = ((int) dfY) - nSrcYOff; + + if( iSrcX < 0 || iSrcX >= nSrcXSize || iSrcY < 0 || iSrcY >= nSrcYSize ) + continue; + + int iSrcOffset = iSrcX + iSrcY * nSrcXSize; + double dfDensity = 1.0; + + if( poWK->pafUnifiedSrcDensity != NULL + && iSrcX >= 0 && iSrcY >= 0 + && iSrcX < nSrcXSize && iSrcY < nSrcYSize ) + dfDensity = poWK->pafUnifiedSrcDensity[iSrcOffset]; + + GWKOverlayDensity( poWK, iDstOffset, dfDensity ); + + //Because this is on the bit-wise level, it can't be done well in OpenCL + if( poWK->panDstValid != NULL ) + poWK->panDstValid[iDstOffset>>5] |= 0x01 << (iDstOffset & 0x1f); + } + } + + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + CPLFree( pabSuccess ); + + err = GDALWarpKernelOpenCL_runResamp(warper, + poWK->pafUnifiedSrcDensity, + poWK->panUnifiedSrcValid, + poWK->pafDstDensity, + poWK->panDstValid, + poWK->dfXScale, poWK->dfYScale, + poWK->dfXFilter, poWK->dfYFilter, + poWK->nXRadius, poWK->nYRadius, + poWK->nFiltInitX, poWK->nFiltInitY); + + if(err != CL_SUCCESS) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OpenCL routines reported failure (%d) on line %d.", (int) err, __LINE__ ); + eErr = CE_Failure; + goto free_warper; + } + + /* ==================================================================== */ + /* Loop over output lines. */ + /* ==================================================================== */ + for( iDstY = 0; iDstY < nDstYSize && eErr == CE_None; iDstY++ ) + { + for( iBand = 0; iBand < poWK->nBands; iBand++ ) + { + int iDstX; + void *rowReal, *rowImag; + GByte *pabyDst = poWK->papabyDstImage[iBand]; + + err = GDALWarpKernelOpenCL_getRow(warper, &rowReal, &rowImag, iDstY, iBand); + if(err != CL_SUCCESS) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OpenCL routines reported failure (%d) on line %d.", (int) err, __LINE__ ); + eErr = CE_Failure; + goto free_warper; + } + + //Copy the data from the warper to GDAL's memory + switch ( poWK->eWorkingDataType ) + { + case GDT_Byte: + memcpy((void **)&(((GByte *)pabyDst)[iDstY*nDstXSize]), + rowReal, sizeof(GByte)*nDstXSize); + break; + case GDT_Int16: + memcpy((void **)&(((GInt16 *)pabyDst)[iDstY*nDstXSize]), + rowReal, sizeof(GInt16)*nDstXSize); + break; + case GDT_UInt16: + memcpy((void **)&(((GUInt16 *)pabyDst)[iDstY*nDstXSize]), + rowReal, sizeof(GUInt16)*nDstXSize); + break; + case GDT_Float32: + memcpy((void **)&(((float *)pabyDst)[iDstY*nDstXSize]), + rowReal, sizeof(float)*nDstXSize); + break; + case GDT_CInt16: + { + GInt16 *pabyDstI16 = &(((GInt16 *)pabyDst)[iDstY*nDstXSize]); + for (iDstX = 0; iDstX < nDstXSize; iDstX++) { + pabyDstI16[iDstX*2 ] = ((GInt16 *)rowReal)[iDstX]; + pabyDstI16[iDstX*2+1] = ((GInt16 *)rowImag)[iDstX]; + } + } + break; + case GDT_CFloat32: + { + float *pabyDstF32 = &(((float *)pabyDst)[iDstY*nDstXSize]); + for (iDstX = 0; iDstX < nDstXSize; iDstX++) { + pabyDstF32[iDstX*2 ] = ((float *)rowReal)[iDstX]; + pabyDstF32[iDstX*2+1] = ((float *)rowImag)[iDstX]; + } + } + break; + default: + // We don't support higher precision formats + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported resampling OpenCL data type %d.", (int) poWK->eWorkingDataType ); + eErr = CE_Failure; + goto free_warper; + } + } + } +free_warper: + if((err = GDALWarpKernelOpenCL_deleteEnv(warper)) != CL_SUCCESS) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OpenCL routines reported failure (%d) on line %d.", (int) err, __LINE__ ); + return CE_Failure; + } + + return eErr; +} +#endif /* defined(HAVE_OPENCL) */ + +/************************************************************************/ +/* GWKCheckAndComputeSrcOffsets() */ +/************************************************************************/ +static CPL_INLINE int GWKCheckAndComputeSrcOffsets(const int* _pabSuccess, + int _iDstX, + const double* _padfX, + const double* _padfY, + const GDALWarpKernel* _poWK, + int _nSrcXSize, + int _nSrcYSize, + int& iSrcOffset) +{ + if( !_pabSuccess[_iDstX] ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Figure out what pixel we want in our source raster, and skip */ +/* further processing if it is well off the source image. */ +/* -------------------------------------------------------------------- */ + /* We test against the value before casting to avoid the */ + /* problem of asymmetric truncation effects around zero. That is */ + /* -0.5 will be 0 when cast to an int. */ + if( _padfX[_iDstX] < _poWK->nSrcXOff + || _padfY[_iDstX] < _poWK->nSrcYOff ) + return FALSE; + + int iSrcX, iSrcY; + + iSrcX = ((int) (_padfX[_iDstX] + 1e-10)) - _poWK->nSrcXOff; + iSrcY = ((int) (_padfY[_iDstX] + 1e-10)) - _poWK->nSrcYOff; + + /* If operating outside natural projection area, padfX/Y can be */ + /* a very huge positive number, that becomes -2147483648 in the */ + /* int trucation. So it is necessary to test now for non negativeness. */ + if( iSrcX < 0 || iSrcX >= _nSrcXSize || iSrcY < 0 || iSrcY >= _nSrcYSize ) + return FALSE; + + iSrcOffset = iSrcX + iSrcY * _nSrcXSize; + + return TRUE; +} + +/************************************************************************/ +/* GWKGeneralCase() */ +/* */ +/* This is the most general case. It attempts to handle all */ +/* possible features with relatively little concern for */ +/* efficiency. */ +/************************************************************************/ + +static void GWKGeneralCaseThread(void* pData); + +static CPLErr GWKGeneralCase( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKGeneralCase", GWKGeneralCaseThread ); +} + +static void GWKGeneralCaseThread( void* pData) + +{ + GWKJobStruct* psJob = (GWKJobStruct*) pData; + GDALWarpKernel *poWK = psJob->poWK; + int iYMin = psJob->iYMin; + int iYMax = psJob->iYMax; + + int iDstY; + int nDstXSize = poWK->nDstXSize; + int nSrcXSize = poWK->nSrcXSize, nSrcYSize = poWK->nSrcYSize; + +/* -------------------------------------------------------------------- */ +/* Allocate x,y,z coordinate arrays for transformation ... one */ +/* scanlines worth of positions. */ +/* -------------------------------------------------------------------- */ + double *padfX, *padfY, *padfZ; + int *pabSuccess; + + padfX = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfY = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfZ = (double *) CPLMalloc(sizeof(double) * nDstXSize); + pabSuccess = (int *) CPLMalloc(sizeof(int) * nDstXSize); + + int bUse4SamplesFormula = (poWK->dfXScale >= 0.95 && poWK->dfYScale >= 0.95); + + GWKResampleWrkStruct* psWrkStruct = NULL; + if (poWK->eResample != GRA_NearestNeighbour) + { + psWrkStruct = GWKResampleCreateWrkStruct(poWK); + } + double dfSrcCoordPrecision = CPLAtof( + CSLFetchNameValueDef(poWK->papszWarpOptions, "SRC_COORD_PRECISION", "0")); + double dfErrorThreshold = CPLAtof( + CSLFetchNameValueDef(poWK->papszWarpOptions, "ERROR_THRESHOLD", "0")); + +/* ==================================================================== */ +/* Loop over output lines. */ +/* ==================================================================== */ + for( iDstY = iYMin; iDstY < iYMax; iDstY++ ) + { + int iDstX; + +/* -------------------------------------------------------------------- */ +/* Setup points to transform to source image space. */ +/* -------------------------------------------------------------------- */ + for( iDstX = 0; iDstX < nDstXSize; iDstX++ ) + { + padfX[iDstX] = iDstX + 0.5 + poWK->nDstXOff; + padfY[iDstX] = iDstY + 0.5 + poWK->nDstYOff; + padfZ[iDstX] = 0.0; + } + +/* -------------------------------------------------------------------- */ +/* Transform the points from destination pixel/line coordinates */ +/* to source pixel/line coordinates. */ +/* -------------------------------------------------------------------- */ + poWK->pfnTransformer( psJob->pTransformerArg, TRUE, nDstXSize, + padfX, padfY, padfZ, pabSuccess ); + if( dfSrcCoordPrecision > 0.0 ) + { + GWKRoundSourceCoordinates(nDstXSize, padfX, padfY, padfZ, pabSuccess, + dfSrcCoordPrecision, + dfErrorThreshold, + poWK->pfnTransformer, + psJob->pTransformerArg, + 0.5 + poWK->nDstXOff, + iDstY + 0.5 + poWK->nDstYOff); + } + +/* ==================================================================== */ +/* Loop over pixels in output scanline. */ +/* ==================================================================== */ + for( iDstX = 0; iDstX < nDstXSize; iDstX++ ) + { + int iDstOffset; + + int iSrcOffset; + if( !GWKCheckAndComputeSrcOffsets(pabSuccess, iDstX, padfX, padfY, + poWK, nSrcXSize, nSrcYSize, iSrcOffset) ) + continue; + +/* -------------------------------------------------------------------- */ +/* Do not try to apply transparent/invalid source pixels to the */ +/* destination. This currently ignores the multi-pixel input */ +/* of bilinear and cubic resamples. */ +/* -------------------------------------------------------------------- */ + double dfDensity = 1.0; + + if( poWK->pafUnifiedSrcDensity != NULL ) + { + dfDensity = poWK->pafUnifiedSrcDensity[iSrcOffset]; + if( dfDensity < 0.00001 ) + continue; + } + + if( poWK->panUnifiedSrcValid != NULL + && !(poWK->panUnifiedSrcValid[iSrcOffset>>5] + & (0x01 << (iSrcOffset & 0x1f))) ) + continue; + +/* ==================================================================== */ +/* Loop processing each band. */ +/* ==================================================================== */ + int iBand; + int bHasFoundDensity = FALSE; + + iDstOffset = iDstX + iDstY * nDstXSize; + for( iBand = 0; iBand < poWK->nBands; iBand++ ) + { + double dfBandDensity = 0.0; + double dfValueReal = 0.0; + double dfValueImag = 0.0; + +/* -------------------------------------------------------------------- */ +/* Collect the source value. */ +/* -------------------------------------------------------------------- */ + if ( poWK->eResample == GRA_NearestNeighbour || + nSrcXSize == 1 || nSrcYSize == 1) + { + GWKGetPixelValue( poWK, iBand, iSrcOffset, + &dfBandDensity, &dfValueReal, &dfValueImag ); + } + else if ( poWK->eResample == GRA_Bilinear && + bUse4SamplesFormula ) + { + GWKBilinearResample4Sample( poWK, iBand, + padfX[iDstX]-poWK->nSrcXOff, + padfY[iDstX]-poWK->nSrcYOff, + &dfBandDensity, + &dfValueReal, &dfValueImag ); + } + else if ( poWK->eResample == GRA_Cubic && + bUse4SamplesFormula ) + { + GWKCubicResample4Sample( poWK, iBand, + padfX[iDstX]-poWK->nSrcXOff, + padfY[iDstX]-poWK->nSrcYOff, + &dfBandDensity, + &dfValueReal, &dfValueImag ); + } + else + { + psWrkStruct->pfnGWKResample( poWK, iBand, + padfX[iDstX]-poWK->nSrcXOff, + padfY[iDstX]-poWK->nSrcYOff, + &dfBandDensity, + &dfValueReal, &dfValueImag, psWrkStruct ); + } + + + // If we didn't find any valid inputs skip to next band. + if ( dfBandDensity < 0.0000000001 ) + continue; + + bHasFoundDensity = TRUE; + +/* -------------------------------------------------------------------- */ +/* We have a computed value from the source. Now apply it to */ +/* the destination pixel. */ +/* -------------------------------------------------------------------- */ + GWKSetPixelValue( poWK, iBand, iDstOffset, + dfBandDensity, + dfValueReal, dfValueImag ); + + } + + if (!bHasFoundDensity) + continue; + +/* -------------------------------------------------------------------- */ +/* Update destination density/validity masks. */ +/* -------------------------------------------------------------------- */ + GWKOverlayDensity( poWK, iDstOffset, dfDensity ); + + if( poWK->panDstValid != NULL ) + { + poWK->panDstValid[iDstOffset>>5] |= + 0x01 << (iDstOffset & 0x1f); + } + + } /* Next iDstX */ + +/* -------------------------------------------------------------------- */ +/* Report progress to the user, and optionally cancel out. */ +/* -------------------------------------------------------------------- */ + if (psJob->pfnProgress && psJob->pfnProgress(psJob)) + break; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup and return. */ +/* -------------------------------------------------------------------- */ + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + CPLFree( pabSuccess ); + if (psWrkStruct) + GWKResampleDeleteWrkStruct(psWrkStruct); +} + +/************************************************************************/ +/* GWKResampleNoMasksOrDstDensityOnlyThreadInternal() */ +/************************************************************************/ + +template +static void GWKResampleNoMasksOrDstDensityOnlyThreadInternal( void* pData ) + +{ + GWKJobStruct* psJob = (GWKJobStruct*) pData; + GDALWarpKernel *poWK = psJob->poWK; + int iYMin = psJob->iYMin; + int iYMax = psJob->iYMax; + + int iDstY; + int nDstXSize = poWK->nDstXSize; + int nSrcXSize = poWK->nSrcXSize, nSrcYSize = poWK->nSrcYSize; + +/* -------------------------------------------------------------------- */ +/* Allocate x,y,z coordinate arrays for transformation ... one */ +/* scanlines worth of positions. */ +/* -------------------------------------------------------------------- */ + double *padfX, *padfY, *padfZ; + int *pabSuccess; + + padfX = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfY = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfZ = (double *) CPLMalloc(sizeof(double) * nDstXSize); + pabSuccess = (int *) CPLMalloc(sizeof(int) * nDstXSize); + + int nXRadius = poWK->nXRadius; + double *padfWeight = (double *)CPLCalloc( 1 + nXRadius * 2, sizeof(double) ); + double dfSrcCoordPrecision = CPLAtof( + CSLFetchNameValueDef(poWK->papszWarpOptions, "SRC_COORD_PRECISION", "0")); + double dfErrorThreshold = CPLAtof( + CSLFetchNameValueDef(poWK->papszWarpOptions, "ERROR_THRESHOLD", "0")); + +/* ==================================================================== */ +/* Loop over output lines. */ +/* ==================================================================== */ + for( iDstY = iYMin; iDstY < iYMax; iDstY++ ) + { + int iDstX; + +/* -------------------------------------------------------------------- */ +/* Setup points to transform to source image space. */ +/* -------------------------------------------------------------------- */ + for( iDstX = 0; iDstX < nDstXSize; iDstX++ ) + { + padfX[iDstX] = iDstX + 0.5 + poWK->nDstXOff; + padfY[iDstX] = iDstY + 0.5 + poWK->nDstYOff; + padfZ[iDstX] = 0.0; + } + +/* -------------------------------------------------------------------- */ +/* Transform the points from destination pixel/line coordinates */ +/* to source pixel/line coordinates. */ +/* -------------------------------------------------------------------- */ + poWK->pfnTransformer( psJob->pTransformerArg, TRUE, nDstXSize, + padfX, padfY, padfZ, pabSuccess ); + if( dfSrcCoordPrecision > 0.0 ) + { + GWKRoundSourceCoordinates(nDstXSize, padfX, padfY, padfZ, pabSuccess, + dfSrcCoordPrecision, + dfErrorThreshold, + poWK->pfnTransformer, + psJob->pTransformerArg, + 0.5 + poWK->nDstXOff, + iDstY + 0.5 + poWK->nDstYOff); + } + +/* ==================================================================== */ +/* Loop over pixels in output scanline. */ +/* ==================================================================== */ + for( iDstX = 0; iDstX < nDstXSize; iDstX++ ) + { + int iSrcOffset; + if( !GWKCheckAndComputeSrcOffsets(pabSuccess, iDstX, padfX, padfY, + poWK, nSrcXSize, nSrcYSize, iSrcOffset) ) + continue; + +/* ==================================================================== */ +/* Loop processing each band. */ +/* ==================================================================== */ + int iBand; + int iDstOffset; + + iDstOffset = iDstX + iDstY * nDstXSize; + + for( iBand = 0; iBand < poWK->nBands; iBand++ ) + { + T value = 0; + if( eResample == GRA_NearestNeighbour ) + { + value = ((T *)poWK->papabySrcImage[iBand])[iSrcOffset]; + } + else if( bUse4SamplesFormula ) + { + if( eResample == GRA_Bilinear ) + GWKBilinearResampleNoMasks4SampleT( poWK, iBand, + padfX[iDstX]-poWK->nSrcXOff, + padfY[iDstX]-poWK->nSrcYOff, + &value ); + else + GWKCubicResampleNoMasks4SampleT( poWK, iBand, + padfX[iDstX]-poWK->nSrcXOff, + padfY[iDstX]-poWK->nSrcYOff, + &value ); + } + else + GWKResampleNoMasksT( poWK, iBand, + padfX[iDstX]-poWK->nSrcXOff, + padfY[iDstX]-poWK->nSrcYOff, + &value, + padfWeight); + ((T *)poWK->papabyDstImage[iBand])[iDstOffset] = value; + } + + if( poWK->pafDstDensity ) + poWK->pafDstDensity[iDstOffset] = 1.0f; + } + +/* -------------------------------------------------------------------- */ +/* Report progress to the user, and optionally cancel out. */ +/* -------------------------------------------------------------------- */ + if (psJob->pfnProgress && psJob->pfnProgress(psJob)) + break; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup and return. */ +/* -------------------------------------------------------------------- */ + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + CPLFree( pabSuccess ); + CPLFree( padfWeight ); +} + +template +static void GWKResampleNoMasksOrDstDensityOnlyThread( void* pData ) +{ + GWKResampleNoMasksOrDstDensityOnlyThreadInternal(pData); +} + +template void GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread( void* pData ) + +{ + GWKJobStruct* psJob = (GWKJobStruct*) pData; + GDALWarpKernel *poWK = psJob->poWK; + CPLAssert(eResample == GRA_Bilinear || eResample == GRA_Cubic); + int bUse4SamplesFormula = (poWK->dfXScale >= 0.95 && poWK->dfYScale >= 0.95); + if( bUse4SamplesFormula ) + GWKResampleNoMasksOrDstDensityOnlyThreadInternal(pData); + else + GWKResampleNoMasksOrDstDensityOnlyThreadInternal(pData); +} + +static CPLErr GWKNearestNoMasksOrDstDensityOnlyByte( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKNearestNoMasksOrDstDensityOnlyByte", + GWKResampleNoMasksOrDstDensityOnlyThread ); +} + +static CPLErr GWKBilinearNoMasksOrDstDensityOnlyByte( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKBilinearNoMasksOrDstDensityOnlyByte", + GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread ); +} + +static CPLErr GWKCubicNoMasksOrDstDensityOnlyByte( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKCubicNoMasksOrDstDensityOnlyByte", + GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread ); +} + +static CPLErr GWKCubicNoMasksOrDstDensityOnlyFloat( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKCubicNoMasksOrDstDensityOnlyFloat", + GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread ); +} + +#ifdef INSTANCIATE_FLOAT64_SSE2_IMPL + +static CPLErr GWKCubicNoMasksOrDstDensityOnlyDouble( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKCubicNoMasksOrDstDensityOnlyDouble", + GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread ); +} +#endif + +static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyByte( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKCubicSplineNoMasksOrDstDensityOnlyByte", + GWKResampleNoMasksOrDstDensityOnlyThread ); +} + +/************************************************************************/ +/* GWKNearestByte() */ +/* */ +/* Case for 8bit input data with nearest neighbour resampling */ +/* using valid flags. Should be as fast as possible for this */ +/* particular transformation type. */ +/************************************************************************/ + +template +static void GWKNearestThread( void* pData ) + +{ + GWKJobStruct* psJob = (GWKJobStruct*) pData; + GDALWarpKernel *poWK = psJob->poWK; + int iYMin = psJob->iYMin; + int iYMax = psJob->iYMax; + + int iDstY; + int nDstXSize = poWK->nDstXSize; + int nSrcXSize = poWK->nSrcXSize, nSrcYSize = poWK->nSrcYSize; + +/* -------------------------------------------------------------------- */ +/* Allocate x,y,z coordinate arrays for transformation ... one */ +/* scanlines worth of positions. */ +/* -------------------------------------------------------------------- */ + double *padfX, *padfY, *padfZ; + int *pabSuccess; + + padfX = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfY = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfZ = (double *) CPLMalloc(sizeof(double) * nDstXSize); + pabSuccess = (int *) CPLMalloc(sizeof(int) * nDstXSize); + + double dfSrcCoordPrecision = CPLAtof( + CSLFetchNameValueDef(poWK->papszWarpOptions, "SRC_COORD_PRECISION", "0")); + double dfErrorThreshold = CPLAtof( + CSLFetchNameValueDef(poWK->papszWarpOptions, "ERROR_THRESHOLD", "0")); + +/* ==================================================================== */ +/* Loop over output lines. */ +/* ==================================================================== */ + for( iDstY = iYMin; iDstY < iYMax; iDstY++ ) + { + int iDstX; + +/* -------------------------------------------------------------------- */ +/* Setup points to transform to source image space. */ +/* -------------------------------------------------------------------- */ + for( iDstX = 0; iDstX < nDstXSize; iDstX++ ) + { + padfX[iDstX] = iDstX + 0.5 + poWK->nDstXOff; + padfY[iDstX] = iDstY + 0.5 + poWK->nDstYOff; + padfZ[iDstX] = 0.0; + } + +/* -------------------------------------------------------------------- */ +/* Transform the points from destination pixel/line coordinates */ +/* to source pixel/line coordinates. */ +/* -------------------------------------------------------------------- */ + poWK->pfnTransformer( psJob->pTransformerArg, TRUE, nDstXSize, + padfX, padfY, padfZ, pabSuccess ); + if( dfSrcCoordPrecision > 0.0 ) + { + GWKRoundSourceCoordinates(nDstXSize, padfX, padfY, padfZ, pabSuccess, + dfSrcCoordPrecision, + dfErrorThreshold, + poWK->pfnTransformer, + psJob->pTransformerArg, + 0.5 + poWK->nDstXOff, + iDstY + 0.5 + poWK->nDstYOff); + } +/* ==================================================================== */ +/* Loop over pixels in output scanline. */ +/* ==================================================================== */ + for( iDstX = 0; iDstX < nDstXSize; iDstX++ ) + { + int iDstOffset; + + int iSrcOffset; + if( !GWKCheckAndComputeSrcOffsets(pabSuccess, iDstX, padfX, padfY, + poWK, nSrcXSize, nSrcYSize, iSrcOffset) ) + continue; + +/* -------------------------------------------------------------------- */ +/* Do not try to apply invalid source pixels to the dest. */ +/* -------------------------------------------------------------------- */ + if( poWK->panUnifiedSrcValid != NULL + && !(poWK->panUnifiedSrcValid[iSrcOffset>>5] + & (0x01 << (iSrcOffset & 0x1f))) ) + continue; + +/* -------------------------------------------------------------------- */ +/* Do not try to apply transparent source pixels to the destination.*/ +/* -------------------------------------------------------------------- */ + double dfDensity = 1.0; + + if( poWK->pafUnifiedSrcDensity != NULL ) + { + dfDensity = poWK->pafUnifiedSrcDensity[iSrcOffset]; + if( dfDensity < 0.00001 ) + continue; + } + +/* ==================================================================== */ +/* Loop processing each band. */ +/* ==================================================================== */ + int iBand; + + iDstOffset = iDstX + iDstY * nDstXSize; + + for( iBand = 0; iBand < poWK->nBands; iBand++ ) + { + T value = 0; + double dfBandDensity = 0.0; + +/* -------------------------------------------------------------------- */ +/* Collect the source value. */ +/* -------------------------------------------------------------------- */ + if ( GWKGetPixelT(poWK, iBand, iSrcOffset, &dfBandDensity, &value) ) + { + if( dfBandDensity < 1.0 ) + { + if( dfBandDensity == 0.0 ) + /* do nothing */; + else + { + /* let the general code take care of mixing */ + GWKSetPixelValueRealT( poWK, iBand, iDstOffset, + dfBandDensity, value ); + } + } + else + { + ((T *)poWK->papabyDstImage[iBand])[iDstOffset] = value; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Mark this pixel valid/opaque in the output. */ +/* -------------------------------------------------------------------- */ + GWKOverlayDensity( poWK, iDstOffset, dfDensity ); + + if( poWK->panDstValid != NULL ) + { + poWK->panDstValid[iDstOffset>>5] |= + 0x01 << (iDstOffset & 0x1f); + } + } /* Next iDstX */ + +/* -------------------------------------------------------------------- */ +/* Report progress to the user, and optionally cancel out. */ +/* -------------------------------------------------------------------- */ + if (psJob->pfnProgress && psJob->pfnProgress(psJob)) + break; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup and return. */ +/* -------------------------------------------------------------------- */ + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + CPLFree( pabSuccess ); +} + +static CPLErr GWKNearestByte( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKNearestByte", GWKNearestThread ); +} + +static CPLErr GWKNearestNoMasksOrDstDensityOnlyShort( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKNearestNoMasksOrDstDensityOnlyShort", + GWKResampleNoMasksOrDstDensityOnlyThread ); +} + +static CPLErr GWKBilinearNoMasksOrDstDensityOnlyShort( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKBilinearNoMasksOrDstDensityOnlyShort", + GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread ); +} + +static CPLErr GWKBilinearNoMasksOrDstDensityOnlyUShort( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKBilinearNoMasksOrDstDensityOnlyUShort", + GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread ); +} + +static CPLErr GWKBilinearNoMasksOrDstDensityOnlyFloat( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKBilinearNoMasksOrDstDensityOnlyFloat", + GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread ); +} + +#ifdef INSTANCIATE_FLOAT64_SSE2_IMPL + +static CPLErr GWKBilinearNoMasksOrDstDensityOnlyDouble( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKBilinearNoMasksOrDstDensityOnlyDouble", + GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread ); +} +#endif + +static CPLErr GWKCubicNoMasksOrDstDensityOnlyShort( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKCubicNoMasksOrDstDensityOnlyShort", + GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread ); +} + +static CPLErr GWKCubicNoMasksOrDstDensityOnlyUShort( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKCubicNoMasksOrDstDensityOnlyUShort", + GWKResampleNoMasksOrDstDensityOnlyHas4SampleThread ); +} + +static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyShort( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKCubicSplineNoMasksOrDstDensityOnlyShort", + GWKResampleNoMasksOrDstDensityOnlyThread ); +} + +static CPLErr GWKCubicSplineNoMasksOrDstDensityOnlyUShort( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKCubicSplineNoMasksOrDstDensityOnlyUShort", + GWKResampleNoMasksOrDstDensityOnlyThread ); +} + +static CPLErr GWKNearestShort( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKNearestShort", GWKNearestThread ); +} + +static CPLErr GWKNearestNoMasksOrDstDensityOnlyFloat( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKNearestNoMasksOrDstDensityOnlyFloat", + GWKResampleNoMasksOrDstDensityOnlyThread ); +} + +static CPLErr GWKNearestFloat( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKNearestFloat", GWKNearestThread ); +} + + +/************************************************************************/ +/* GWKAverageOrMode() */ +/* */ +/************************************************************************/ + +static void GWKAverageOrModeThread(void* pData); + +static CPLErr GWKAverageOrMode( GDALWarpKernel *poWK ) +{ + return GWKRun( poWK, "GWKAverageOrMode", GWKAverageOrModeThread ); +} + +// overall logic based on GWKGeneralCaseThread() +static void GWKAverageOrModeThread( void* pData) +{ + GWKJobStruct* psJob = (GWKJobStruct*) pData; + GDALWarpKernel *poWK = psJob->poWK; + int iYMin = psJob->iYMin; + int iYMax = psJob->iYMax; + + int iDstY, iDstX, iSrcX, iSrcY, iDstOffset; + int nDstXSize = poWK->nDstXSize; + int nSrcXSize = poWK->nSrcXSize, nSrcYSize = poWK->nSrcYSize; + +/* -------------------------------------------------------------------- */ +/* Find out which algorithm to use (small optim.) */ +/* -------------------------------------------------------------------- */ + int nAlgo = 0; + + // these vars only used with nAlgo == 3 + int *panVals = NULL; + int nBins = 0, nBinsOffset = 0; + + // only used with nAlgo = 2 + float* pafVals = NULL; + int* panSums = NULL; + + // only used with nAlgo = 6 + float quant = 0.5; + + if ( poWK->eResample == GRA_Average ) + { + nAlgo = GWKAOM_Average; + } + else if( poWK->eResample == GRA_Mode ) + { + // TODO check color table count > 256 + if ( poWK->eWorkingDataType == GDT_Byte || + poWK->eWorkingDataType == GDT_UInt16 || + poWK->eWorkingDataType == GDT_Int16 ) + { + nAlgo = GWKAOM_Imode; + + /* In the case of a paletted or non-paletted byte band, */ + /* input values are between 0 and 255 */ + if ( poWK->eWorkingDataType == GDT_Byte ) + { + nBins = 256; + } + /* In the case of Int16, input values are between -32768 and 32767 */ + else if ( poWK->eWorkingDataType == GDT_Int16 ) + { + nBins = 65536; + nBinsOffset = 32768; + } + /* In the case of UInt16, input values are between 0 and 65537 */ + else if ( poWK->eWorkingDataType == GDT_UInt16 ) + { + nBins = 65536; + } + panVals = (int*) VSIMalloc(nBins * sizeof(int)); + if( panVals == NULL ) + return; + } + else + { + nAlgo = GWKAOM_Fmode; + + if ( nSrcXSize > 0 && nSrcYSize > 0 ) + { + pafVals = (float*) VSIMalloc3(nSrcXSize, nSrcYSize, sizeof(float)); + panSums = (int*) VSIMalloc3(nSrcXSize, nSrcYSize, sizeof(int)); + if( pafVals == NULL || panSums == NULL ) + { + VSIFree(pafVals); + VSIFree(panSums); + return; + } + } + } + } + else if( poWK->eResample == GRA_Max ) + { + nAlgo = GWKAOM_Max; + } + else if( poWK->eResample == GRA_Min ) + { + nAlgo = GWKAOM_Min; + } + else if( poWK->eResample == GRA_Med ) + { + nAlgo = GWKAOM_Quant; + quant = 0.5; + } + else if( poWK->eResample == GRA_Q1 ) + { + nAlgo = GWKAOM_Quant; + quant = 0.25; + } + else if( poWK->eResample == GRA_Q3 ) + { + nAlgo = GWKAOM_Quant; + quant = 0.75; + } + else + { + // other resample algorithms not permitted here + CPLDebug( "GDAL", "GDALWarpKernel():GWKAverageOrModeThread() ERROR, illegal resample" ); + return; + } + CPLDebug( "GDAL", "GDALWarpKernel():GWKAverageOrModeThread() using algo %d", nAlgo ); + +/* -------------------------------------------------------------------- */ +/* Allocate x,y,z coordinate arrays for transformation ... two */ +/* scanlines worth of positions. */ +/* -------------------------------------------------------------------- */ + double *padfX, *padfY, *padfZ; + double *padfX2, *padfY2, *padfZ2; + int *pabSuccess, *pabSuccess2; + + padfX = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfY = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfZ = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfX2 = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfY2 = (double *) CPLMalloc(sizeof(double) * nDstXSize); + padfZ2 = (double *) CPLMalloc(sizeof(double) * nDstXSize); + pabSuccess = (int *) CPLMalloc(sizeof(int) * nDstXSize); + pabSuccess2 = (int *) CPLMalloc(sizeof(int) * nDstXSize); + + double dfSrcCoordPrecision = CPLAtof( + CSLFetchNameValueDef(poWK->papszWarpOptions, "SRC_COORD_PRECISION", "0")); + double dfErrorThreshold = CPLAtof( + CSLFetchNameValueDef(poWK->papszWarpOptions, "ERROR_THRESHOLD", "0")); + +/* ==================================================================== */ +/* Loop over output lines. */ +/* ==================================================================== */ + for( iDstY = iYMin; iDstY < iYMax; iDstY++ ) + { + +/* -------------------------------------------------------------------- */ +/* Setup points to transform to source image space. */ +/* -------------------------------------------------------------------- */ + for( iDstX = 0; iDstX < nDstXSize; iDstX++ ) + { + padfX[iDstX] = iDstX + poWK->nDstXOff; + padfY[iDstX] = iDstY + poWK->nDstYOff; + padfZ[iDstX] = 0.0; + padfX2[iDstX] = iDstX + 1.0 + poWK->nDstXOff; + padfY2[iDstX] = iDstY + 1.0 + poWK->nDstYOff; + padfZ2[iDstX] = 0.0; + } + +/* -------------------------------------------------------------------- */ +/* Transform the points from destination pixel/line coordinates */ +/* to source pixel/line coordinates. */ +/* -------------------------------------------------------------------- */ + poWK->pfnTransformer( psJob->pTransformerArg, TRUE, nDstXSize, + padfX, padfY, padfZ, pabSuccess ); + poWK->pfnTransformer( psJob->pTransformerArg, TRUE, nDstXSize, + padfX2, padfY2, padfZ2, pabSuccess2 ); + + if( dfSrcCoordPrecision > 0.0 ) + { + GWKRoundSourceCoordinates(nDstXSize, padfX, padfY, padfZ, pabSuccess, + dfSrcCoordPrecision, + dfErrorThreshold, + poWK->pfnTransformer, + psJob->pTransformerArg, + poWK->nDstXOff, + iDstY + poWK->nDstYOff); + GWKRoundSourceCoordinates(nDstXSize, padfX2, padfY2, padfZ2, pabSuccess2, + dfSrcCoordPrecision, + dfErrorThreshold, + poWK->pfnTransformer, + psJob->pTransformerArg, + 1.0 + poWK->nDstXOff, + iDstY + 1.0 + poWK->nDstYOff); + } + +/* ==================================================================== */ +/* Loop over pixels in output scanline. */ +/* ==================================================================== */ + for( iDstX = 0; iDstX < nDstXSize; iDstX++ ) + { + int iSrcOffset = 0; + double dfDensity = 1.0; + int bHasFoundDensity = FALSE; + + if( !pabSuccess[iDstX] || !pabSuccess2[iDstX] ) + continue; + iDstOffset = iDstX + iDstY * nDstXSize; + +/* ==================================================================== */ +/* Loop processing each band. */ +/* ==================================================================== */ + + for( int iBand = 0; iBand < poWK->nBands; iBand++ ) + { + double dfBandDensity = 0.0; + double dfValueReal = 0.0; + double dfValueImag = 0.0; + double dfValueRealTmp = 0.0; + double dfValueImagTmp = 0.0; + +/* -------------------------------------------------------------------- */ +/* Collect the source value. */ +/* -------------------------------------------------------------------- */ + + double dfTotal = 0; + int nCount = 0; // count of pixels used to compute average/mode + int nCount2 = 0; // count of all pixels sampled, including nodata + int iSrcXMin, iSrcXMax,iSrcYMin,iSrcYMax; + + // compute corners in source crs + iSrcXMin = MAX( ((int) floor((padfX[iDstX] + 1e-10))) - poWK->nSrcXOff, 0 ); + iSrcXMax = MIN( ((int) ceil((padfX2[iDstX] - 1e-10))) - poWK->nSrcXOff, nSrcXSize ); + iSrcYMin = MAX( ((int) floor((padfY[iDstX] + 1e-10))) - poWK->nSrcYOff, 0 ); + iSrcYMax = MIN( ((int) ceil((padfY2[iDstX] - 1e-10))) - poWK->nSrcYOff, nSrcYSize ); + + // The transformation might not have preserved ordering of coordinates + // so do the necessary swapping (#5433) + // NOTE: this is really an approximative fix. To do something more precise + // we would for example need to compute the transformation of coordinates + // in the [iDstX,iDstY]x[iDstX+1,iDstY+1] square back to source coordinates, + // and take the bounding box of the got source coordinates. + if( iSrcXMax < iSrcXMin ) + { + iSrcXMin = MAX( ((int) floor((padfX2[iDstX] + 1e-10))) - poWK->nSrcXOff, 0 ); + iSrcXMax = MIN( ((int) ceil((padfX[iDstX] - 1e-10))) - poWK->nSrcXOff, nSrcXSize ); + } + if( iSrcYMax < iSrcYMin ) + { + iSrcYMin = MAX( ((int) floor((padfY2[iDstX] + 1e-10))) - poWK->nSrcYOff, 0 ); + iSrcYMax = MIN( ((int) ceil((padfY[iDstX] - 1e-10))) - poWK->nSrcYOff, nSrcYSize ); + } + if( iSrcXMin == iSrcXMax && iSrcXMax < nSrcXSize ) + iSrcXMax ++; + if( iSrcYMin == iSrcYMax && iSrcYMax < nSrcYSize ) + iSrcYMax ++; + + // loop over source lines and pixels - 3 possible algorithms + + if ( nAlgo == GWKAOM_Average ) // poWK->eResample == GRA_Average + { + // this code adapted from GDALDownsampleChunk32R_AverageT() in gcore/overview.cpp + for( iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++ ) + { + for( iSrcX = iSrcXMin; iSrcX < iSrcXMax; iSrcX++ ) + { + iSrcOffset = iSrcX + iSrcY * nSrcXSize; + + if( poWK->panUnifiedSrcValid != NULL + && !(poWK->panUnifiedSrcValid[iSrcOffset>>5] + & (0x01 << (iSrcOffset & 0x1f))) ) + { + continue; + } + + nCount2++; + if ( GWKGetPixelValue( poWK, iBand, iSrcOffset, + &dfBandDensity, &dfValueRealTmp, &dfValueImagTmp ) && dfBandDensity > 0.0000000001 ) + { + nCount++; + dfTotal += dfValueRealTmp; + } + } + } + + if ( nCount > 0 ) + { + dfValueReal = dfTotal / nCount; + dfBandDensity = 1; + bHasFoundDensity = TRUE; + } + + } // GRA_Average + + else if ( nAlgo == GWKAOM_Imode || nAlgo == GWKAOM_Fmode ) // poWK->eResample == GRA_Mode + { + // this code adapted from GDALDownsampleChunk32R_Mode() in gcore/overview.cpp + + if ( nAlgo == GWKAOM_Fmode ) // int32 or float + { + /* I'm not sure how much sense it makes to run a majority + filter on floating point data, but here it is for the sake + of compatability. It won't look right on RGB images by the + nature of the filter. */ + int iMaxInd = 0, iMaxVal = -1, i = 0; + + for( iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++ ) + { + for( iSrcX = iSrcXMin; iSrcX < iSrcXMax; iSrcX++ ) + { + iSrcOffset = iSrcX + iSrcY * nSrcXSize; + + if( poWK->panUnifiedSrcValid != NULL + && !(poWK->panUnifiedSrcValid[iSrcOffset>>5] + & (0x01 << (iSrcOffset & 0x1f))) ) + continue; + + nCount2++; + if ( GWKGetPixelValue( poWK, iBand, iSrcOffset, + &dfBandDensity, &dfValueRealTmp, &dfValueImagTmp ) && dfBandDensity > 0.0000000001 ) + { + nCount++; + + float fVal = (float)dfValueRealTmp; + + //Check array for existing entry + for( i = 0; i < iMaxInd; ++i ) + if( pafVals[i] == fVal + && ++panSums[i] > panSums[iMaxVal] ) + { + iMaxVal = i; + break; + } + + //Add to arr if entry not already there + if( i == iMaxInd ) + { + pafVals[iMaxInd] = fVal; + panSums[iMaxInd] = 1; + + if( iMaxVal < 0 ) + iMaxVal = iMaxInd; + + ++iMaxInd; + } + } + } + } + + if( iMaxVal != -1 ) + { + dfValueReal = pafVals[iMaxVal]; + dfBandDensity = 1; + bHasFoundDensity = TRUE; + } + } + + else // byte or int16 + { + int nMaxVal = 0, iMaxInd = -1; + + memset(panVals, 0, nBins*sizeof(int)); + + for( iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++ ) + { + for( iSrcX = iSrcXMin; iSrcX < iSrcXMax; iSrcX++ ) + { + iSrcOffset = iSrcX + iSrcY * nSrcXSize; + + if( poWK->panUnifiedSrcValid != NULL + && !(poWK->panUnifiedSrcValid[iSrcOffset>>5] + & (0x01 << (iSrcOffset & 0x1f))) ) + continue; + + nCount2++; + if ( GWKGetPixelValue( poWK, iBand, iSrcOffset, + &dfBandDensity, &dfValueRealTmp, &dfValueImagTmp ) && dfBandDensity > 0.0000000001 ) + { + nCount++; + + int nVal = (int) dfValueRealTmp; + if ( ++panVals[nVal+nBinsOffset] > nMaxVal) + { + //Sum the density + //Is it the most common value so far? + iMaxInd = nVal; + nMaxVal = panVals[nVal+nBinsOffset]; + } + } + } + } + + if( iMaxInd != -1 ) + { + dfValueReal = (float)iMaxInd; + dfBandDensity = 1; + bHasFoundDensity = TRUE; + } + } + + } // GRA_Mode + else if ( nAlgo == GWKAOM_Max ) // poWK->eResample == GRA_Max + { + dfTotal = -DBL_MAX; + // this code adapted from nAlgo 1 method, GRA_Average + for( iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++ ) + { + for( iSrcX = iSrcXMin; iSrcX < iSrcXMax; iSrcX++ ) + { + iSrcOffset = iSrcX + iSrcY * nSrcXSize; + + if( poWK->panUnifiedSrcValid != NULL + && !(poWK->panUnifiedSrcValid[iSrcOffset>>5] + & (0x01 << (iSrcOffset & 0x1f))) ) + { + continue; + } + + // Returns pixel value if it is not no data + if ( GWKGetPixelValue( poWK, iBand, iSrcOffset, + &dfBandDensity, &dfValueRealTmp, &dfValueImagTmp ) && dfBandDensity > 0.0000000001 ) + { + nCount++; + if (dfTotal < dfValueRealTmp) + { + dfTotal = dfValueRealTmp; + } + } + } + } + + if ( nCount > 0 ) + { + dfValueReal = dfTotal; + dfBandDensity = 1; + bHasFoundDensity = TRUE; + } + + } // GRA_Max + else if ( nAlgo == GWKAOM_Min ) // poWK->eResample == GRA_Min + { + dfTotal = DBL_MAX; + // this code adapted from nAlgo 1 method, GRA_Average + for( iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++ ) + { + for( iSrcX = iSrcXMin; iSrcX < iSrcXMax; iSrcX++ ) + { + iSrcOffset = iSrcX + iSrcY * nSrcXSize; + + if( poWK->panUnifiedSrcValid != NULL + && !(poWK->panUnifiedSrcValid[iSrcOffset>>5] + & (0x01 << (iSrcOffset & 0x1f))) ) + { + continue; + } + + // Returns pixel value if it is not no data + if ( GWKGetPixelValue( poWK, iBand, iSrcOffset, + &dfBandDensity, &dfValueRealTmp, &dfValueImagTmp ) && dfBandDensity > 0.0000000001 ) + { + nCount++; + if (dfTotal > dfValueRealTmp) + { + dfTotal = dfValueRealTmp; + } + } + } + } + + if ( nCount > 0 ) + { + dfValueReal = dfTotal; + dfBandDensity = 1; + bHasFoundDensity = TRUE; + } + + } // GRA_Min + else if ( nAlgo == GWKAOM_Quant ) // poWK->eResample == GRA_Med | GRA_Q1 | GRA_Q3 + { + std::vector dfValuesTmp; + + // this code adapted from nAlgo 1 method, GRA_Average + for( iSrcY = iSrcYMin; iSrcY < iSrcYMax; iSrcY++ ) + { + for( iSrcX = iSrcXMin; iSrcX < iSrcXMax; iSrcX++ ) + { + iSrcOffset = iSrcX + iSrcY * nSrcXSize; + + if( poWK->panUnifiedSrcValid != NULL + && !(poWK->panUnifiedSrcValid[iSrcOffset>>5] + & (0x01 << (iSrcOffset & 0x1f))) ) + { + continue; + } + + // Returns pixel value if it is not no data + if ( GWKGetPixelValue( poWK, iBand, iSrcOffset, + &dfBandDensity, &dfValueRealTmp, &dfValueImagTmp ) && dfBandDensity > 0.0000000001 ) + { + nCount++; + dfValuesTmp.push_back(dfValueRealTmp); + } + } + } + + if ( nCount > 0 ) + { + std::sort(dfValuesTmp.begin(), dfValuesTmp.end()); + int quantIdx = std::ceil(quant * dfValuesTmp.size() - 1); + dfValueReal = dfValuesTmp[quantIdx]; + + dfBandDensity = 1; + bHasFoundDensity = TRUE; + dfValuesTmp.clear(); + } + + } // Quantile + +/* -------------------------------------------------------------------- */ +/* We have a computed value from the source. Now apply it to */ +/* the destination pixel. */ +/* -------------------------------------------------------------------- */ + if ( bHasFoundDensity ) + { + // TODO should we compute dfBandDensity in fct of nCount/nCount2 , + // or use as a threshold to set the dest value? + // dfBandDensity = (float) nCount / nCount2; + // if ( (float) nCount / nCount2 > 0.1 ) + // or fix gdalwarp crop_to_cutline to crop partially overlapping pixels + GWKSetPixelValue( poWK, iBand, iDstOffset, + dfBandDensity, + dfValueReal, dfValueImag ); + } + } + + if (!bHasFoundDensity) + continue; + +/* -------------------------------------------------------------------- */ +/* Update destination density/validity masks. */ +/* -------------------------------------------------------------------- */ + GWKOverlayDensity( poWK, iDstOffset, dfDensity ); + + if( poWK->panDstValid != NULL ) + { + poWK->panDstValid[iDstOffset>>5] |= + 0x01 << (iDstOffset & 0x1f); + } + + } /* Next iDstX */ + +/* -------------------------------------------------------------------- */ +/* Report progress to the user, and optionally cancel out. */ +/* -------------------------------------------------------------------- */ + if (psJob->pfnProgress && psJob->pfnProgress(psJob)) + break; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup and return. */ +/* -------------------------------------------------------------------- */ + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + CPLFree( padfX2 ); + CPLFree( padfY2 ); + CPLFree( padfZ2 ); + CPLFree( pabSuccess ); + CPLFree( pabSuccess2 ); + VSIFree( panVals ); + VSIFree(pafVals); + VSIFree(panSums); +} diff --git a/bazaar/plugin/gdal/alg/gdalwarpkernel_opencl.c b/bazaar/plugin/gdal/alg/gdalwarpkernel_opencl.c new file mode 100644 index 000000000..08b9549c6 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalwarpkernel_opencl.c @@ -0,0 +1,2575 @@ +/****************************************************************************** + * $Id: gdalwarpkernel_opencl.c 28300 2015-01-07 08:30:07Z rouault $ + * + * Project: OpenCL Image Reprojector + * Purpose: Implementation of the GDALWarpKernel reprojector in OpenCL. + * Author: Seth Price, seth@pricepages.org + * + ****************************************************************************** + * Copyright (c) 2010, Seth Price + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#if defined(HAVE_OPENCL) + +/* The following line may be uncommented for increased debugging traces and profiling */ +/* #define DEBUG_OPENCL 1 */ + +#include +#include +#include +#include +#include +#include "cpl_string.h" +#include "gdalwarpkernel_opencl.h" + +CPL_CVSID("$Id: gdalwarpkernel_opencl.c 28300 2015-01-07 08:30:07Z rouault $"); + +#define handleErr(err) if((err) != CL_SUCCESS) { \ + CPLError(CE_Failure, CPLE_AppDefined, "Error at file %s line %d: %s", __FILE__, __LINE__, getCLErrorString(err)); \ + return err; \ +} + +#define handleErrRetNULL(err) if((err) != CL_SUCCESS) { \ + (*clErr) = err; \ + CPLError(CE_Failure, CPLE_AppDefined, "Error at file %s line %d: %s", __FILE__, __LINE__, getCLErrorString(err)); \ + return NULL; \ +} + +#define handleErrGoto(err, goto_label) if((err) != CL_SUCCESS) { \ + (*clErr) = err; \ + CPLError(CE_Failure, CPLE_AppDefined, "Error at file %s line %d: %s", __FILE__, __LINE__, getCLErrorString(err)); \ + goto goto_label; \ +} + +#define freeCLMem(clMem, fallBackMem) do { \ + if ((clMem) != NULL) { \ + handleErr(err = clReleaseMemObject(clMem)); \ + clMem = NULL; \ + fallBackMem = NULL; \ + } else if ((fallBackMem) != NULL) { \ + CPLFree(fallBackMem); \ + fallBackMem = NULL; \ + } \ +} while(0) + +static const char* getCLErrorString(cl_int err) +{ + switch (err) + { + case CL_SUCCESS: + return("CL_SUCCESS"); + break; + case CL_DEVICE_NOT_FOUND: + return("CL_DEVICE_NOT_FOUND"); + break; + case CL_DEVICE_NOT_AVAILABLE: + return("CL_DEVICE_NOT_AVAILABLE"); + break; + case CL_COMPILER_NOT_AVAILABLE: + return("CL_COMPILER_NOT_AVAILABLE"); + break; + case CL_MEM_OBJECT_ALLOCATION_FAILURE: + return("CL_MEM_OBJECT_ALLOCATION_FAILURE"); + break; + case CL_OUT_OF_RESOURCES: + return("CL_OUT_OF_RESOURCES"); + break; + case CL_OUT_OF_HOST_MEMORY: + return("CL_OUT_OF_HOST_MEMORY"); + break; + case CL_PROFILING_INFO_NOT_AVAILABLE: + return("CL_PROFILING_INFO_NOT_AVAILABLE"); + break; + case CL_MEM_COPY_OVERLAP: + return("CL_MEM_COPY_OVERLAP"); + break; + case CL_IMAGE_FORMAT_MISMATCH: + return("CL_IMAGE_FORMAT_MISMATCH"); + break; + case CL_IMAGE_FORMAT_NOT_SUPPORTED: + return("CL_IMAGE_FORMAT_NOT_SUPPORTED"); + break; + case CL_BUILD_PROGRAM_FAILURE: + return("CL_BUILD_PROGRAM_FAILURE"); + break; + case CL_MAP_FAILURE: + return("CL_MAP_FAILURE"); + break; + case CL_INVALID_VALUE: + return("CL_INVALID_VALUE"); + break; + case CL_INVALID_DEVICE_TYPE: + return("CL_INVALID_DEVICE_TYPE"); + break; + case CL_INVALID_PLATFORM: + return("CL_INVALID_PLATFORM"); + break; + case CL_INVALID_DEVICE: + return("CL_INVALID_DEVICE"); + break; + case CL_INVALID_CONTEXT: + return("CL_INVALID_CONTEXT"); + break; + case CL_INVALID_QUEUE_PROPERTIES: + return("CL_INVALID_QUEUE_PROPERTIES"); + break; + case CL_INVALID_COMMAND_QUEUE: + return("CL_INVALID_COMMAND_QUEUE"); + break; + case CL_INVALID_HOST_PTR: + return("CL_INVALID_HOST_PTR"); + break; + case CL_INVALID_MEM_OBJECT: + return("CL_INVALID_MEM_OBJECT"); + break; + case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR: + return("CL_INVALID_IMAGE_FORMAT_DESCRIPTOR"); + break; + case CL_INVALID_IMAGE_SIZE: + return("CL_INVALID_IMAGE_SIZE"); + break; + case CL_INVALID_SAMPLER: + return("CL_INVALID_SAMPLER"); + break; + case CL_INVALID_BINARY: + return("CL_INVALID_BINARY"); + break; + case CL_INVALID_BUILD_OPTIONS: + return("CL_INVALID_BUILD_OPTIONS"); + break; + case CL_INVALID_PROGRAM: + return("CL_INVALID_PROGRAM"); + break; + case CL_INVALID_PROGRAM_EXECUTABLE: + return("CL_INVALID_PROGRAM_EXECUTABLE"); + break; + case CL_INVALID_KERNEL_NAME: + return("CL_INVALID_KERNEL_NAME"); + break; + case CL_INVALID_KERNEL_DEFINITION: + return("CL_INVALID_KERNEL_DEFINITION"); + break; + case CL_INVALID_KERNEL: + return("CL_INVALID_KERNEL"); + break; + case CL_INVALID_ARG_INDEX: + return("CL_INVALID_ARG_INDEX"); + break; + case CL_INVALID_ARG_VALUE: + return("CL_INVALID_ARG_VALUE"); + break; + case CL_INVALID_ARG_SIZE: + return("CL_INVALID_ARG_SIZE"); + break; + case CL_INVALID_KERNEL_ARGS: + return("CL_INVALID_KERNEL_ARGS"); + break; + case CL_INVALID_WORK_DIMENSION: + return("CL_INVALID_WORK_DIMENSION"); + break; + case CL_INVALID_WORK_GROUP_SIZE: + return("CL_INVALID_WORK_GROUP_SIZE"); + break; + case CL_INVALID_WORK_ITEM_SIZE: + return("CL_INVALID_WORK_ITEM_SIZE"); + break; + case CL_INVALID_GLOBAL_OFFSET: + return("CL_INVALID_GLOBAL_OFFSET"); + break; + case CL_INVALID_EVENT_WAIT_LIST: + return("CL_INVALID_EVENT_WAIT_LIST"); + break; + case CL_INVALID_EVENT: + return("CL_INVALID_EVENT"); + break; + case CL_INVALID_OPERATION: + return("CL_INVALID_OPERATION"); + break; + case CL_INVALID_GL_OBJECT: + return("CL_INVALID_GL_OBJECT"); + break; + case CL_INVALID_BUFFER_SIZE: + return("CL_INVALID_BUFFER_SIZE"); + break; + case CL_INVALID_MIP_LEVEL: + return("CL_INVALID_MIP_LEVEL"); + break; + case CL_INVALID_GLOBAL_WORK_SIZE: + return("CL_INVALID_GLOBAL_WORK_SIZE"); + break; + } + + return "unknown_error"; +} + +static const char* getCLDataTypeString( cl_channel_type dataType ) +{ + switch( dataType ) + { + case CL_SNORM_INT8: return "CL_SNORM_INT8"; + case CL_SNORM_INT16: return "CL_SNORM_INT16"; + case CL_UNORM_INT8: return "CL_UNORM_INT8"; + case CL_UNORM_INT16: return "CL_UNORM_INT16"; +#if 0 + case CL_UNORM_SHORT_565: return "CL_UNORM_SHORT_565"; + case CL_UNORM_SHORT_555: return "CL_UNORM_SHORT_555"; + case CL_UNORM_INT_101010: return "CL_UNORM_INT_101010"; + case CL_SIGNED_INT8: return "CL_SIGNED_INT8"; + case CL_SIGNED_INT16: return "CL_SIGNED_INT16"; + case CL_SIGNED_INT32: return "CL_SIGNED_INT32"; + case CL_UNSIGNED_INT8: return "CL_UNSIGNED_INT8"; + case CL_UNSIGNED_INT16: return "CL_UNSIGNED_INT16"; + case CL_UNSIGNED_INT32: return "CL_UNSIGNED_INT32"; + case CL_HALF_FLOAT: return "CL_HALF_FLOAT"; +#endif + case CL_FLOAT: return "CL_FLOAT"; + default: return "unknown"; + } +} + +/* + Finds an appropirate OpenCL device. If the user specifies a preference, the + code for it should be here (but not currently supported). For debugging, it's + always easier to use CL_DEVICE_TYPE_CPU because then printf() can be called + from the kernel. If debugging is on, we can print the name and stats about the + device we're using. + */ +cl_device_id get_device(OCLVendor *peVendor) +{ + cl_int err = 0; + cl_device_id device = NULL; + size_t returned_size = 0; + cl_char vendor_name[1024] = {0}; + cl_char device_name[1024] = {0}; + + cl_platform_id platforms[10]; + cl_uint num_platforms; + + err = clGetPlatformIDs( 10, platforms, &num_platforms ); + if( err != CL_SUCCESS || num_platforms == 0 ) + return NULL; + + // Find the GPU CL device, this is what we really want + // If there is no GPU device is CL capable, fall back to CPU + err = clGetDeviceIDs(platforms[0], CL_DEVICE_TYPE_GPU, 1, &device, NULL); + if (err != CL_SUCCESS) + { + // Find the CPU CL device, as a fallback + err = clGetDeviceIDs(platforms[0], CL_DEVICE_TYPE_CPU, 1, &device, NULL); + if( err != CL_SUCCESS || device == 0 ) + return NULL; + } + + // Get some information about the returned device + err = clGetDeviceInfo(device, CL_DEVICE_VENDOR, sizeof(vendor_name), + vendor_name, &returned_size); + err |= clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(device_name), + device_name, &returned_size); + assert(err == CL_SUCCESS); + CPLDebug( "OpenCL", "Connected to %s %s.", vendor_name, device_name); + + if (strncmp((const char*)vendor_name, "Advanced Micro Devices", strlen("Advanced Micro Devices")) == 0) + *peVendor = VENDOR_AMD; + else if (strncmp((const char*)vendor_name, "Intel(R) Corporation", strlen("Intel(R) Corporation")) == 0) + *peVendor = VENDOR_INTEL; + else + *peVendor = VENDOR_OTHER; + + return device; +} + +/* + Given that not all OpenCL devices support the same image formats, we need to + make do with what we have. This leads to wasted space, but as OpenCL matures + I hope it'll get better. + */ +cl_int set_supported_formats(struct oclWarper *warper, + cl_channel_order minOrderSize, + cl_channel_order *chosenOrder, + unsigned int *chosenSize, + cl_channel_type dataType ) +{ + cl_image_format *fmtBuf = (cl_image_format *)calloc(256, sizeof(cl_image_format)); + cl_uint numRet; + int i; + int extraSpace = 9999; + cl_int err; + int bFound = FALSE; + + //Find what we *can* handle + handleErr(err = clGetSupportedImageFormats(warper->context, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + CL_MEM_OBJECT_IMAGE2D, + 256, fmtBuf, &numRet)); + for (i = 0; i < numRet; ++i) { + int thisOrderSize = 0; + switch (fmtBuf[i].image_channel_order) + { + //Only support formats which use the channels in order (x,y,z,w) + case CL_R: + case CL_INTENSITY: + case CL_LUMINANCE: + thisOrderSize = 1; + break; + case CL_RG: + thisOrderSize = 2; + break; + case CL_RGB: + thisOrderSize = 3; + break; + case CL_RGBA: + thisOrderSize = 4; + break; + } + + //Choose an order with the least wasted space + if (fmtBuf[i].image_channel_data_type == dataType && + minOrderSize <= thisOrderSize && + extraSpace > thisOrderSize - minOrderSize ) { + + //Set the vector size, order, & remember wasted space + (*chosenSize) = thisOrderSize; + (*chosenOrder) = fmtBuf[i].image_channel_order; + extraSpace = thisOrderSize - minOrderSize; + bFound = TRUE; + } + } + + free(fmtBuf); + + if( !bFound ) + { + CPLDebug("OpenCL", + "Cannot find supported format for dataType = %s and minOrderSize = %d", + getCLDataTypeString(dataType), (int)minOrderSize); + } + return (bFound) ? CL_SUCCESS : CL_INVALID_OPERATION; +} + +/* + Allocate some pinned memory that we can use as an intermediate buffer. We're + using the pinned memory to assemble the data before transferring it to the + device. The reason we're using pinned RAM is because the transfer speed from + host RAM to device RAM is faster than non-pinned. The disadvantage is that + pinned RAM is a scarce OS resource. I'm making the assumption that the user + has as much pinned host RAM available as total device RAM because device RAM + tends to be similarly scarce. However, if the pinned memory fails we fall back + to using a regular memory allocation. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int alloc_pinned_mem(struct oclWarper *warper, int imgNum, size_t dataSz, + void **wrkPtr, cl_mem *wrkCL) +{ + cl_int err = CL_SUCCESS; + wrkCL[imgNum] = clCreateBuffer(warper->context, + CL_MEM_READ_ONLY | CL_MEM_ALLOC_HOST_PTR, + dataSz, NULL, &err); + + if (err == CL_SUCCESS) { + wrkPtr[imgNum] = (void *)clEnqueueMapBuffer(warper->queue, wrkCL[imgNum], + CL_FALSE, CL_MAP_WRITE, + 0, dataSz, 0, NULL, NULL, &err); + handleErr(err); + } else { + wrkCL[imgNum] = NULL; +#ifdef DEBUG_OPENCL + CPLDebug("OpenCL", "Using fallback non-pinned memory!"); +#endif + //Fallback to regular allocation + wrkPtr[imgNum] = (void *)VSIMalloc(dataSz); + + if (wrkPtr[imgNum] == NULL) + handleErr(err = CL_OUT_OF_HOST_MEMORY); + } + + return CL_SUCCESS; +} + +/* + Allocates the working host memory for all bands of the image in the warper + structure. This includes both the source image buffers and the destination + buffers. This memory is located on the host, so we can assemble the image. + Reasons for buffering it like this include reading each row from disk and + de-interleaving bands and parts of bands. Then they can be copied to the device + as a single operation fit for use as an OpenCL memory object. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int alloc_working_arr(struct oclWarper *warper, + size_t ptrSz, size_t dataSz, size_t *fmtSz) +{ + cl_int err = CL_SUCCESS; + int i, b; + size_t srcDataSz1, dstDataSz1, srcDataSz4, dstDataSz4; + const int numBands = warper->numBands; + + //Find the best channel order for this format + err = set_supported_formats(warper, 1, + &(warper->imgChOrder1), &(warper->imgChSize1), + warper->imageFormat); + handleErr(err); + if(warper->useVec) { + err = set_supported_formats(warper, 4, + &(warper->imgChOrder4), &(warper->imgChSize4), + warper->imageFormat); + handleErr(err); + } + + //Alloc space for pointers to the main image data + warper->realWork.v = (void **)VSICalloc(ptrSz, warper->numImages); + warper->dstRealWork.v = (void **)VSICalloc(ptrSz, warper->numImages); + if (warper->realWork.v == NULL || warper->dstRealWork.v == NULL) + handleErr(err = CL_OUT_OF_HOST_MEMORY); + + if (warper->imagWorkCL != NULL) { + //Alloc space for pointers to the extra channel, if it exists + warper->imagWork.v = (void **)VSICalloc(ptrSz, warper->numImages); + warper->dstImagWork.v = (void **)VSICalloc(ptrSz, warper->numImages); + if (warper->imagWork.v == NULL || warper->dstImagWork.v == NULL) + handleErr(err = CL_OUT_OF_HOST_MEMORY); + } else { + warper->imagWork.v = NULL; + warper->dstImagWork.v = NULL; + } + + //Calc the sizes we need + srcDataSz1 = dataSz * warper->srcWidth * warper->srcHeight * warper->imgChSize1; + dstDataSz1 = dataSz * warper->dstWidth * warper->dstHeight; + srcDataSz4 = dataSz * warper->srcWidth * warper->srcHeight * warper->imgChSize4; + dstDataSz4 = dataSz * warper->dstWidth * warper->dstHeight * 4; + + //Allocate pinned memory for each band's image + for (b = 0, i = 0; b < numBands && i < warper->numImages; ++i) { + if(warper->useVec && b < numBands - numBands % 4) { + handleErr(err = alloc_pinned_mem(warper, i, srcDataSz4, + warper->realWork.v, + warper->realWorkCL)); + + handleErr(err = alloc_pinned_mem(warper, i, dstDataSz4, + warper->dstRealWork.v, + warper->dstRealWorkCL)); + b += 4; + } else { + handleErr(err = alloc_pinned_mem(warper, i, srcDataSz1, + warper->realWork.v, + warper->realWorkCL)); + + handleErr(err = alloc_pinned_mem(warper, i, dstDataSz1, + warper->dstRealWork.v, + warper->dstRealWorkCL)); + ++b; + } + } + + if (warper->imagWorkCL != NULL) { + //Allocate pinned memory for each band's extra channel, if exists + for (b = 0, i = 0; b < numBands && i < warper->numImages; ++i) { + if(warper->useVec && b < numBands - numBands % 4) { + handleErr(err = alloc_pinned_mem(warper, i, srcDataSz4, + warper->imagWork.v, + warper->imagWorkCL)); + + handleErr(err = alloc_pinned_mem(warper, i, dstDataSz4, + warper->dstImagWork.v, + warper->dstImagWorkCL)); + b += 4; + } else { + handleErr(err = alloc_pinned_mem(warper, i, srcDataSz1, + warper->imagWork.v, + warper->imagWorkCL)); + + handleErr(err = alloc_pinned_mem(warper, i, dstDataSz1, + warper->dstImagWork.v, + warper->dstImagWorkCL)); + ++b; + } + } + } + + return CL_SUCCESS; +} + +/* + Assemble and create the kernel. For optimization, portabilaty, and + implimentation limitation reasons, the program is actually assembled from + several strings, then compiled with as many invariants as possible defined by + the preprocessor. There is also quite a bit of error-catching code in here + because the kernel is where many bugs show up. + + Returns CL_SUCCESS on success and other CL_* errors in the error buffer when + something goes wrong. + */ +cl_kernel get_kernel(struct oclWarper *warper, char useVec, + double dfXScale, double dfYScale, double dfXFilter, double dfYFilter, + int nXRadius, int nYRadius, int nFiltInitX, int nFiltInitY, + cl_int *clErr ) +{ + cl_program program; + cl_kernel kernel; + cl_int err = CL_SUCCESS; + char *buffer = (char *)CPLCalloc(128000, sizeof(char)); + char *progBuf = (char *)CPLCalloc(128000, sizeof(char)); + float dstMinVal, dstMaxVal; + + const char *outType; + const char *dUseVec = ""; + const char *dVecf = "float"; + const char *kernGenFuncs = +// ********************* General Funcs ******************** +"void clampToDst(float fReal,\n" + "__global outType *dstPtr,\n" + "unsigned int iDstOffset,\n" + "__constant float *fDstNoDataReal,\n" + "int bandNum);\n" +"void setPixel(__global outType *dstReal,\n" + "__global outType *dstImag,\n" + "__global float *dstDensity,\n" + "__global int *nDstValid,\n" + "__constant float *fDstNoDataReal,\n" + "const int bandNum,\n" + "vecf fDensity, vecf fReal, vecf fImag);\n" +"int getPixel(__read_only image2d_t srcReal,\n" + "__read_only image2d_t srcImag,\n" + "__global float *fUnifiedSrcDensity,\n" + "__global int *nUnifiedSrcValid,\n" + "__constant char *useBandSrcValid,\n" + "__global int *nBandSrcValid,\n" + "const int2 iSrc,\n" + "int bandNum,\n" + "vecf *fDensity, vecf *fReal, vecf *fImag);\n" +"int isValid(__global float *fUnifiedSrcDensity,\n" + "__global int *nUnifiedSrcValid,\n" + "float2 fSrcCoords );\n" +"float2 getSrcCoords(__read_only image2d_t srcCoords);\n" + +"#ifdef USE_CLAMP_TO_DST_FLOAT\n" +"void clampToDst(float fReal,\n" + "__global outType *dstPtr,\n" + "unsigned int iDstOffset,\n" + "__constant float *fDstNoDataReal,\n" + "int bandNum)\n" +"{\n" + "dstPtr[iDstOffset] = fReal;\n" +"}\n" +"#else\n" +"void clampToDst(float fReal,\n" + "__global outType *dstPtr,\n" + "unsigned int iDstOffset,\n" + "__constant float *fDstNoDataReal,\n" + "int bandNum)\n" +"{\n" + "fReal *= dstMaxVal;\n" + + "if (fReal < dstMinVal)\n" + "dstPtr[iDstOffset] = (outType)dstMinVal;\n" + "else if (fReal > dstMaxVal)\n" + "dstPtr[iDstOffset] = (outType)dstMaxVal;\n" + "else\n" + "dstPtr[iDstOffset] = (dstMinVal < 0) ? (outType)floor(fReal + 0.5f) : (outType)(fReal + 0.5f);\n" + + "if (useDstNoDataReal && bandNum >= 0 &&\n" + "fDstNoDataReal[bandNum] == dstPtr[iDstOffset])\n" + "{\n" + "if (dstPtr[iDstOffset] == dstMinVal)\n" + "dstPtr[iDstOffset] = dstMinVal + 1;\n" + "else\n" + "dstPtr[iDstOffset] = dstPtr[iDstOffset] - 1;\n" + "}\n" +"}\n" +"#endif\n" + +"void setPixel(__global outType *dstReal,\n" + "__global outType *dstImag,\n" + "__global float *dstDensity,\n" + "__global int *nDstValid,\n" + "__constant float *fDstNoDataReal,\n" + "const int bandNum,\n" + "vecf fDensity, vecf fReal, vecf fImag)\n" +"{\n" + "unsigned int iDstOffset = get_global_id(1)*iDstWidth + get_global_id(0);\n" + +"#ifdef USE_VEC\n" + "if (fDensity.x < 0.00001f && fDensity.y < 0.00001f &&\n" + "fDensity.z < 0.00001f && fDensity.w < 0.00001f ) {\n" + + "fReal = 0.0f;\n" + "fImag = 0.0f;\n" + + "} else if (fDensity.x < 0.9999f || fDensity.y < 0.9999f ||\n" + "fDensity.z < 0.9999f || fDensity.w < 0.9999f ) {\n" + "vecf fDstReal, fDstImag;\n" + "float fDstDensity;\n" + + "fDstReal.x = dstReal[iDstOffset];\n" + "fDstReal.y = dstReal[iDstOffset+iDstHeight*iDstWidth];\n" + "fDstReal.z = dstReal[iDstOffset+iDstHeight*iDstWidth*2];\n" + "fDstReal.w = dstReal[iDstOffset+iDstHeight*iDstWidth*3];\n" + "if (useImag) {\n" + "fDstImag.x = dstImag[iDstOffset];\n" + "fDstImag.y = dstImag[iDstOffset+iDstHeight*iDstWidth];\n" + "fDstImag.z = dstImag[iDstOffset+iDstHeight*iDstWidth*2];\n" + "fDstImag.w = dstImag[iDstOffset+iDstHeight*iDstWidth*3];\n" + "}\n" +"#else\n" + "if (fDensity < 0.00001f) {\n" + + "fReal = 0.0f;\n" + "fImag = 0.0f;\n" + + "} else if (fDensity < 0.9999f) {\n" + "vecf fDstReal, fDstImag;\n" + "float fDstDensity;\n" + + "fDstReal = dstReal[iDstOffset];\n" + "if (useImag)\n" + "fDstImag = dstImag[iDstOffset];\n" +"#endif\n" + + "if (useDstDensity)\n" + "fDstDensity = dstDensity[iDstOffset];\n" + "else if (useDstValid &&\n" + "!((nDstValid[iDstOffset>>5] & (0x01 << (iDstOffset & 0x1f))) ))\n" + "fDstDensity = 0.0f;\n" + "else\n" + "fDstDensity = 1.0f;\n" + + "vecf fDstInfluence = (1.0f - fDensity) * fDstDensity;\n" + + // Density should be checked for <= 0.0 & handled by the calling function + "fReal = (fReal * fDensity + fDstReal * fDstInfluence) / (fDensity + fDstInfluence);\n" + "if (useImag)\n" + "fImag = (fImag * fDensity + fDstImag * fDstInfluence) / (fDensity + fDstInfluence);\n" + "}\n" + +"#ifdef USE_VEC\n" + "clampToDst(fReal.x, dstReal, iDstOffset, fDstNoDataReal, bandNum);\n" + "clampToDst(fReal.y, dstReal, iDstOffset+iDstHeight*iDstWidth, fDstNoDataReal, bandNum);\n" + "clampToDst(fReal.z, dstReal, iDstOffset+iDstHeight*iDstWidth*2, fDstNoDataReal, bandNum);\n" + "clampToDst(fReal.w, dstReal, iDstOffset+iDstHeight*iDstWidth*3, fDstNoDataReal, bandNum);\n" + "if (useImag) {\n" + "clampToDst(fImag.x, dstImag, iDstOffset, fDstNoDataReal, -1);\n" + "clampToDst(fImag.y, dstImag, iDstOffset+iDstHeight*iDstWidth, fDstNoDataReal, -1);\n" + "clampToDst(fImag.z, dstImag, iDstOffset+iDstHeight*iDstWidth*2, fDstNoDataReal, -1);\n" + "clampToDst(fImag.w, dstImag, iDstOffset+iDstHeight*iDstWidth*3, fDstNoDataReal, -1);\n" + "}\n" +"#else\n" + "clampToDst(fReal, dstReal, iDstOffset, fDstNoDataReal, bandNum);\n" + "if (useImag)\n" + "clampToDst(fImag, dstImag, iDstOffset, fDstNoDataReal, -1);\n" +"#endif\n" +"}\n" + +"int getPixel(__read_only image2d_t srcReal,\n" + "__read_only image2d_t srcImag,\n" + "__global float *fUnifiedSrcDensity,\n" + "__global int *nUnifiedSrcValid,\n" + "__constant char *useBandSrcValid,\n" + "__global int *nBandSrcValid,\n" + "const int2 iSrc,\n" + "int bandNum,\n" + "vecf *fDensity, vecf *fReal, vecf *fImag)\n" +"{\n" + "int iSrcOffset = 0, iBandValidLen = 0, iSrcOffsetMask = 0;\n" + "int bHasValid = FALSE;\n" + + // Clamp the src offset values if needed + "if(useUnifiedSrcDensity | useUnifiedSrcValid | useUseBandSrcValid){\n" + "int iSrcX = iSrc.x;\n" + "int iSrcY = iSrc.y;\n" + + // Needed because the offset isn't clamped in OpenCL hardware + "if(iSrcX < 0)\n" + "iSrcX = 0;\n" + "else if(iSrcX >= iSrcWidth)\n" + "iSrcX = iSrcWidth - 1;\n" + + "if(iSrcY < 0)\n" + "iSrcY = 0;\n" + "else if(iSrcY >= iSrcHeight)\n" + "iSrcY = iSrcHeight - 1;\n" + + "iSrcOffset = iSrcY*iSrcWidth + iSrcX;\n" + "iBandValidLen = 1 + ((iSrcWidth*iSrcHeight)>>5);\n" + "iSrcOffsetMask = (0x01 << (iSrcOffset & 0x1f));\n" + "}\n" + + "if (useUnifiedSrcValid &&\n" + "!((nUnifiedSrcValid[iSrcOffset>>5] & iSrcOffsetMask) ) )\n" + "return FALSE;\n" + +"#ifdef USE_VEC\n" + "if (!useUseBandSrcValid || !useBandSrcValid[bandNum] ||\n" + "((nBandSrcValid[(iSrcOffset>>5)+iBandValidLen*bandNum ] & iSrcOffsetMask)) )\n" + "bHasValid = TRUE;\n" + + "if (!useUseBandSrcValid || !useBandSrcValid[bandNum+1] ||\n" + "((nBandSrcValid[(iSrcOffset>>5)+iBandValidLen*(1+bandNum)] & iSrcOffsetMask)) )\n" + "bHasValid = TRUE;\n" + + "if (!useUseBandSrcValid || !useBandSrcValid[bandNum+2] ||\n" + "((nBandSrcValid[(iSrcOffset>>5)+iBandValidLen*(2+bandNum)] & iSrcOffsetMask)) )\n" + "bHasValid = TRUE;\n" + + "if (!useUseBandSrcValid || !useBandSrcValid[bandNum+3] ||\n" + "((nBandSrcValid[(iSrcOffset>>5)+iBandValidLen*(3+bandNum)] & iSrcOffsetMask)) )\n" + "bHasValid = TRUE;\n" +"#else\n" + "if (!useUseBandSrcValid || !useBandSrcValid[bandNum] ||\n" + "((nBandSrcValid[(iSrcOffset>>5)+iBandValidLen*bandNum ] & iSrcOffsetMask)) )\n" + "bHasValid = TRUE;\n" +"#endif\n" + + "if (!bHasValid)\n" + "return FALSE;\n" + + "const sampler_t samp = CLK_NORMALIZED_COORDS_FALSE |\n" + "CLK_ADDRESS_CLAMP_TO_EDGE |\n" + "CLK_FILTER_NEAREST;\n" + +"#ifdef USE_VEC\n" + "(*fReal) = read_imagef(srcReal, samp, iSrc);\n" + "if (useImag)\n" + "(*fImag) = read_imagef(srcImag, samp, iSrc);\n" +"#else\n" + "(*fReal) = read_imagef(srcReal, samp, iSrc).x;\n" + "if (useImag)\n" + "(*fImag) = read_imagef(srcImag, samp, iSrc).x;\n" +"#endif\n" + + "if (useUnifiedSrcDensity) {\n" + "(*fDensity) = fUnifiedSrcDensity[iSrcOffset];\n" + "} else {\n" + "(*fDensity) = 1.0f;\n" + "return TRUE;\n" + "}\n" + +"#ifdef USE_VEC\n" + "return (*fDensity).x > 0.0000001f || (*fDensity).y > 0.0000001f ||\n" + "(*fDensity).z > 0.0000001f || (*fDensity).w > 0.0000001f;\n" +"#else\n" + "return (*fDensity) > 0.0000001f;\n" +"#endif\n" +"}\n" + +"int isValid(__global float *fUnifiedSrcDensity,\n" + "__global int *nUnifiedSrcValid,\n" + "float2 fSrcCoords )\n" +"{\n" + "if (fSrcCoords.x < 0.0f || fSrcCoords.y < 0.0f)\n" + "return FALSE;\n" + + "int iSrcX = (int) (fSrcCoords.x - 0.5f);\n" + "int iSrcY = (int) (fSrcCoords.y - 0.5f);\n" + + "if( iSrcX < 0 || iSrcX >= iSrcWidth || iSrcY < 0 || iSrcY >= iSrcHeight )\n" + "return FALSE;\n" + + "int iSrcOffset = iSrcX + iSrcY * iSrcWidth;\n" + + "if (useUnifiedSrcDensity && fUnifiedSrcDensity[iSrcOffset] < 0.00001f)\n" + "return FALSE;\n" + + "if (useUnifiedSrcValid &&\n" + "!(nUnifiedSrcValid[iSrcOffset>>5] & (0x01 << (iSrcOffset & 0x1f))) )\n" + "return FALSE;\n" + + "return TRUE;\n" +"}\n" + +"float2 getSrcCoords(__read_only image2d_t srcCoords)\n" +"{\n" + // Find an appropriate place to sample the coordinates so we're still + // accurate after linear interpolation. + "int nDstX = get_global_id(0);\n" + "int nDstY = get_global_id(1);\n" + "float2 fDst = (float2)((0.5f * (float)iCoordMult + nDstX) /\n" + "(float)((ceil((iDstWidth - 1) / (float)iCoordMult) + 1) * iCoordMult), \n" + "(0.5f * (float)iCoordMult + nDstY) /\n" + "(float)((ceil((iDstHeight - 1) / (float)iCoordMult) + 1) * iCoordMult));\n" + + // Check & return when the thread group overruns the image size + "if (nDstX >= iDstWidth || nDstY >= iDstHeight)\n" + "return (float2)(-99.0f, -99.0f);\n" + + "const sampler_t samp = CLK_NORMALIZED_COORDS_TRUE |\n" + "CLK_ADDRESS_CLAMP_TO_EDGE |\n" + "CLK_FILTER_LINEAR;\n" + + "float4 fSrcCoords = read_imagef(srcCoords,samp,fDst);\n" + + "return (float2)(fSrcCoords.x, fSrcCoords.y);\n" +"}\n"; + + const char *kernBilinear = +// ************************ Bilinear ************************ +"__kernel void resamp(__read_only image2d_t srcCoords,\n" + "__read_only image2d_t srcReal,\n" + "__read_only image2d_t srcImag,\n" + "__global float *fUnifiedSrcDensity,\n" + "__global int *nUnifiedSrcValid,\n" + "__constant char *useBandSrcValid,\n" + "__global int *nBandSrcValid,\n" + "__global outType *dstReal,\n" + "__global outType *dstImag,\n" + "__constant float *fDstNoDataReal,\n" + "__global float *dstDensity,\n" + "__global int *nDstValid,\n" + "const int bandNum)\n" +"{\n" + "float2 fSrc = getSrcCoords(srcCoords);\n" + "if (!isValid(fUnifiedSrcDensity, nUnifiedSrcValid, fSrc))\n" + "return;\n" + + "int iSrcX = (int) floor(fSrc.x - 0.5f);\n" + "int iSrcY = (int) floor(fSrc.y - 0.5f);\n" + "float fRatioX = 1.5f - (fSrc.x - iSrcX);\n" + "float fRatioY = 1.5f - (fSrc.y - iSrcY);\n" + "vecf fReal, fImag, fDens;\n" + "vecf fAccumulatorReal = 0.0f, fAccumulatorImag = 0.0f;\n" + "vecf fAccumulatorDensity = 0.0f;\n" + "float fAccumulatorDivisor = 0.0f;\n" + + "if ( iSrcY >= 0 && iSrcY < iSrcHeight ) {\n" + "float fMult1 = fRatioX * fRatioY;\n" + "float fMult2 = (1.0f-fRatioX) * fRatioY;\n" + + // Upper Left Pixel + "if ( iSrcX >= 0 && iSrcX < iSrcWidth\n" + "&& getPixel(srcReal, srcImag, fUnifiedSrcDensity, nUnifiedSrcValid,\n" + "useBandSrcValid, nBandSrcValid, (int2)(iSrcX, iSrcY),\n" + "bandNum, &fDens, &fReal, &fImag))\n" + "{\n" + "fAccumulatorDivisor += fMult1;\n" + "fAccumulatorReal += fReal * fMult1;\n" + "fAccumulatorImag += fImag * fMult1;\n" + "fAccumulatorDensity += fDens * fMult1;\n" + "}\n" + + // Upper Right Pixel + "if ( iSrcX+1 >= 0 && iSrcX+1 < iSrcWidth\n" + "&& getPixel(srcReal, srcImag, fUnifiedSrcDensity, nUnifiedSrcValid,\n" + "useBandSrcValid, nBandSrcValid, (int2)(iSrcX+1, iSrcY),\n" + "bandNum, &fDens, &fReal, &fImag))\n" + "{\n" + "fAccumulatorDivisor += fMult2;\n" + "fAccumulatorReal += fReal * fMult2;\n" + "fAccumulatorImag += fImag * fMult2;\n" + "fAccumulatorDensity += fDens * fMult2;\n" + "}\n" + "}\n" + + "if ( iSrcY+1 >= 0 && iSrcY+1 < iSrcHeight ) {\n" + "float fMult1 = fRatioX * (1.0f-fRatioY);\n" + "float fMult2 = (1.0f-fRatioX) * (1.0f-fRatioY);\n" + + // Lower Left Pixel + "if ( iSrcX >= 0 && iSrcX < iSrcWidth\n" + "&& getPixel(srcReal, srcImag, fUnifiedSrcDensity, nUnifiedSrcValid,\n" + "useBandSrcValid, nBandSrcValid, (int2)(iSrcX, iSrcY+1),\n" + "bandNum, &fDens, &fReal, &fImag))\n" + "{\n" + "fAccumulatorDivisor += fMult1;\n" + "fAccumulatorReal += fReal * fMult1;\n" + "fAccumulatorImag += fImag * fMult1;\n" + "fAccumulatorDensity += fDens * fMult1;\n" + "}\n" + + // Lower Right Pixel + "if ( iSrcX+1 >= 0 && iSrcX+1 < iSrcWidth\n" + "&& getPixel(srcReal, srcImag, fUnifiedSrcDensity, nUnifiedSrcValid,\n" + "useBandSrcValid, nBandSrcValid, (int2)(iSrcX+1, iSrcY+1),\n" + "bandNum, &fDens, &fReal, &fImag))\n" + "{\n" + "fAccumulatorDivisor += fMult2;\n" + "fAccumulatorReal += fReal * fMult2;\n" + "fAccumulatorImag += fImag * fMult2;\n" + "fAccumulatorDensity += fDens * fMult2;\n" + "}\n" + "}\n" + + // Compute and save final pixel + "if ( fAccumulatorDivisor < 0.00001f ) {\n" + "setPixel(dstReal, dstImag, dstDensity, nDstValid, fDstNoDataReal, bandNum,\n" + "0.0f, 0.0f, 0.0f );\n" + "} else if ( fAccumulatorDivisor < 0.99999f || fAccumulatorDivisor > 1.00001f ) {\n" + "setPixel(dstReal, dstImag, dstDensity, nDstValid, fDstNoDataReal, bandNum,\n" + "fAccumulatorDensity / fAccumulatorDivisor,\n" + "fAccumulatorReal / fAccumulatorDivisor,\n" +"#if useImag != 0\n" + "fAccumulatorImag / fAccumulatorDivisor );\n" +"#else\n" + "0.0f );\n" +"#endif\n" + "} else {\n" + "setPixel(dstReal, dstImag, dstDensity, nDstValid, fDstNoDataReal, bandNum,\n" + "fAccumulatorDensity, fAccumulatorReal, fAccumulatorImag );\n" + "}\n" +"}\n"; + + const char *kernCubic = +// ************************ Cubic ************************ +"vecf cubicConvolution(float dist1, float dist2, float dist3,\n" + "vecf f0, vecf f1, vecf f2, vecf f3);\n" + +"vecf cubicConvolution(float dist1, float dist2, float dist3,\n" + "vecf f0, vecf f1, vecf f2, vecf f3)\n" +"{\n" + "return ( -f0 + f1 - f2 + f3) * dist3\n" + "+ (2.0f*(f0 - f1) + f2 - f3) * dist2\n" + "+ ( -f0 + f2 ) * dist1\n" + "+ f1;\n" +"}\n" + +// ************************ Cubic ************************ +"__kernel void resamp(__read_only image2d_t srcCoords,\n" + "__read_only image2d_t srcReal,\n" + "__read_only image2d_t srcImag,\n" + "__global float *fUnifiedSrcDensity,\n" + "__global int *nUnifiedSrcValid,\n" + "__constant char *useBandSrcValid,\n" + "__global int *nBandSrcValid,\n" + "__global outType *dstReal,\n" + "__global outType *dstImag,\n" + "__constant float *fDstNoDataReal,\n" + "__global float *dstDensity,\n" + "__global int *nDstValid,\n" + "const int bandNum)\n" +"{\n" + "int i;\n" + "float2 fSrc = getSrcCoords(srcCoords);\n" + + "if (!isValid(fUnifiedSrcDensity, nUnifiedSrcValid, fSrc))\n" + "return;\n" + + "int iSrcX = (int) floor( fSrc.x - 0.5f );\n" + "int iSrcY = (int) floor( fSrc.y - 0.5f );\n" + "float fDeltaX = fSrc.x - 0.5f - (float)iSrcX;\n" + "float fDeltaY = fSrc.y - 0.5f - (float)iSrcY;\n" + "float fDeltaX2 = fDeltaX * fDeltaX;\n" + "float fDeltaY2 = fDeltaY * fDeltaY;\n" + "float fDeltaX3 = fDeltaX2 * fDeltaX;\n" + "float fDeltaY3 = fDeltaY2 * fDeltaY;\n" + "vecf afReal[4], afDens[4];\n" +"#if useImag != 0\n" + "vecf afImag[4];\n" +"#else\n" + "vecf fImag = 0.0f;\n" +"#endif\n" + + // Loop over rows + "for (i = -1; i < 3; ++i)\n" + "{\n" + "vecf fReal1 = 0.0f, fReal2 = 0.0f, fReal3 = 0.0f, fReal4 = 0.0f;\n" + "vecf fDens1 = 0.0f, fDens2 = 0.0f, fDens3 = 0.0f, fDens4 = 0.0f;\n" + "int hasPx;\n" +"#if useImag != 0\n" + "vecf fImag1 = 0.0f, fImag2 = 0.0f, fImag3 = 0.0f, fImag4 = 0.0f;\n" + + //Get all the pixels for this row + "hasPx = getPixel(srcReal, srcImag, fUnifiedSrcDensity, nUnifiedSrcValid,\n" + "useBandSrcValid, nBandSrcValid, (int2)(iSrcX-1, iSrcY+i),\n" + "bandNum, &fDens1, &fReal1, &fImag1);\n" + + "hasPx |= getPixel(srcReal, srcImag, fUnifiedSrcDensity, nUnifiedSrcValid,\n" + "useBandSrcValid, nBandSrcValid, (int2)(iSrcX , iSrcY+i),\n" + "bandNum, &fDens2, &fReal2, &fImag2);\n" + + "hasPx |= getPixel(srcReal, srcImag, fUnifiedSrcDensity, nUnifiedSrcValid,\n" + "useBandSrcValid, nBandSrcValid, (int2)(iSrcX+1, iSrcY+i),\n" + "bandNum, &fDens3, &fReal3, &fImag3);\n" + + "hasPx |= getPixel(srcReal, srcImag, fUnifiedSrcDensity, nUnifiedSrcValid,\n" + "useBandSrcValid, nBandSrcValid, (int2)(iSrcX+2, iSrcY+i),\n" + "bandNum, &fDens4, &fReal4, &fImag4);\n" +"#else\n" + //Get all the pixels for this row + "hasPx = getPixel(srcReal, srcImag, fUnifiedSrcDensity, nUnifiedSrcValid,\n" + "useBandSrcValid, nBandSrcValid, (int2)(iSrcX-1, iSrcY+i),\n" + "bandNum, &fDens1, &fReal1, &fImag);\n" + + "hasPx |= getPixel(srcReal, srcImag, fUnifiedSrcDensity, nUnifiedSrcValid,\n" + "useBandSrcValid, nBandSrcValid, (int2)(iSrcX , iSrcY+i),\n" + "bandNum, &fDens2, &fReal2, &fImag);\n" + + "hasPx |= getPixel(srcReal, srcImag, fUnifiedSrcDensity, nUnifiedSrcValid,\n" + "useBandSrcValid, nBandSrcValid, (int2)(iSrcX+1, iSrcY+i),\n" + "bandNum, &fDens3, &fReal3, &fImag);\n" + + "hasPx |= getPixel(srcReal, srcImag, fUnifiedSrcDensity, nUnifiedSrcValid,\n" + "useBandSrcValid, nBandSrcValid, (int2)(iSrcX+2, iSrcY+i),\n" + "bandNum, &fDens4, &fReal4, &fImag);\n" +"#endif\n" + + // Shortcut if no px + "if (!hasPx) {\n" + "afDens[i+1] = 0.0f;\n" + "afReal[i+1] = 0.0f;\n" +"#if useImag != 0\n" + "afImag[i+1] = 0.0f;\n" +"#endif\n" + "continue;\n" + "}\n" + + // Process this row + "afDens[i+1] = cubicConvolution(fDeltaX, fDeltaX2, fDeltaX3, fDens1, fDens2, fDens3, fDens4);\n" + "afReal[i+1] = cubicConvolution(fDeltaX, fDeltaX2, fDeltaX3, fReal1, fReal2, fReal3, fReal4);\n" +"#if useImag != 0\n" + "afImag[i+1] = cubicConvolution(fDeltaX, fDeltaX2, fDeltaX3, fImag1, fImag2, fImag3, fImag4);\n" +"#endif\n" + "}\n" + + // Compute and save final pixel + "setPixel(dstReal, dstImag, dstDensity, nDstValid, fDstNoDataReal, bandNum,\n" + "cubicConvolution(fDeltaY, fDeltaY2, fDeltaY3, afDens[0], afDens[1], afDens[2], afDens[3]),\n" + "cubicConvolution(fDeltaY, fDeltaY2, fDeltaY3, afReal[0], afReal[1], afReal[2], afReal[3]),\n" +"#if useImag != 0\n" + "cubicConvolution(fDeltaY, fDeltaY2, fDeltaY3, afImag[0], afImag[1], afImag[2], afImag[3]) );\n" +"#else\n" + "fImag );\n" +"#endif\n" +"}\n"; + + const char *kernResampler = +// ************************ LanczosSinc ************************ + +"float lanczosSinc( float fX, float fR );\n" +"float bSpline( float x );\n" + +"float lanczosSinc( float fX, float fR )\n" +"{\n" + "if ( fX > fR || fX < -fR)\n" + "return 0.0f;\n" + "if ( fX == 0.0f )\n" + "return 1.0f;\n" + + "float fPIX = PI * fX;\n" + "return ( sin(fPIX) / fPIX ) * ( sin(fPIX / fR) * fR / fPIX );\n" +"}\n" + +// ************************ Bicubic Spline ************************ + +"float bSpline( float x )\n" +"{\n" + "float xp2 = x + 2.0f;\n" + "float xp1 = x + 1.0f;\n" + "float xm1 = x - 1.0f;\n" + "float xp2c = xp2 * xp2 * xp2;\n" + + "return (((xp2 > 0.0f)?((xp1 > 0.0f)?((x > 0.0f)?((xm1 > 0.0f)?\n" + "-4.0f * xm1*xm1*xm1:0.0f) +\n" + "6.0f * x*x*x:0.0f) +\n" + "-4.0f * xp1*xp1*xp1:0.0f) +\n" + "xp2c:0.0f) ) * 0.166666666666666666666f;\n" +"}\n" + +// ************************ General Resampler ************************ + +"__kernel void resamp(__read_only image2d_t srcCoords,\n" + "__read_only image2d_t srcReal,\n" + "__read_only image2d_t srcImag,\n" + "__global float *fUnifiedSrcDensity,\n" + "__global int *nUnifiedSrcValid,\n" + "__constant char *useBandSrcValid,\n" + "__global int *nBandSrcValid,\n" + "__global outType *dstReal,\n" + "__global outType *dstImag,\n" + "__constant float *fDstNoDataReal,\n" + "__global float *dstDensity,\n" + "__global int *nDstValid,\n" + "const int bandNum)\n" +"{\n" + "float2 fSrc = getSrcCoords(srcCoords);\n" + + "if (!isValid(fUnifiedSrcDensity, nUnifiedSrcValid, fSrc))\n" + "return;\n" + + "int iSrcX = floor( fSrc.x - 0.5f );\n" + "int iSrcY = floor( fSrc.y - 0.5f );\n" + "float fDeltaX = fSrc.x - 0.5f - (float)iSrcX;\n" + "float fDeltaY = fSrc.y - 0.5f - (float)iSrcY;\n" + + "vecf fAccumulatorReal = 0.0f, fAccumulatorImag = 0.0f;\n" + "vecf fAccumulatorDensity = 0.0f;\n" + "float fAccumulatorWeight = 0.0f;\n" + "int i, j;\n" + + // Loop over pixel rows in the kernel + "for ( j = nFiltInitY; j <= nYRadius; ++j )\n" + "{\n" + "float fWeight1;\n" + "int2 iSrc = (int2)(0, iSrcY + j);\n" + + // Skip sampling over edge of image + "if ( iSrc.y < 0 || iSrc.y >= iSrcHeight )\n" + "continue;\n" + + // Select the resampling algorithm + "if ( doCubicSpline )\n" + // Calculate the Y weight + "fWeight1 = ( fYScale < 1.0f ) ?\n" + "bSpline(((float)j) * fYScale) * fYScale :\n" + "bSpline(((float)j) - fDeltaY);\n" + "else\n" + "fWeight1 = ( fYScale < 1.0f ) ?\n" + "lanczosSinc(j * fYScale, fYFilter) * fYScale :\n" + "lanczosSinc(j - fDeltaY, fYFilter);\n" + + // Iterate over pixels in row + "for ( i = nFiltInitX; i <= nXRadius; ++i )\n" + "{\n" + "float fWeight2;\n" + "vecf fDensity = 0.0f, fReal, fImag;\n" + "iSrc.x = iSrcX + i;\n" + + // Skip sampling at edge of image + // Skip sampling when invalid pixel + "if ( iSrc.x < 0 || iSrc.x >= iSrcWidth || \n" + "!getPixel(srcReal, srcImag, fUnifiedSrcDensity,\n" + "nUnifiedSrcValid, useBandSrcValid, nBandSrcValid,\n" + "iSrc, bandNum, &fDensity, &fReal, &fImag) )\n" + "continue;\n" + + // Choose among possible algorithms + "if ( doCubicSpline )\n" + // Calculate & save the X weight + "fWeight2 = fWeight1 * ((fXScale < 1.0f ) ?\n" + "bSpline((float)i * fXScale) * fXScale :\n" + "bSpline(fDeltaX - (float)i));\n" + "else\n" + // Calculate & save the X weight + "fWeight2 = fWeight1 * ((fXScale < 1.0f ) ?\n" + "lanczosSinc(i * fXScale, fXFilter) * fXScale :\n" + "lanczosSinc(i - fDeltaX, fXFilter));\n" + + // Accumulate! + "fAccumulatorReal += fReal * fWeight2;\n" + "fAccumulatorImag += fImag * fWeight2;\n" + "fAccumulatorDensity += fDensity * fWeight2;\n" + "fAccumulatorWeight += fWeight2;\n" + "}\n" + "}\n" + + "if ( fAccumulatorWeight < 0.000001f ) {\n" + "setPixel(dstReal, dstImag, dstDensity, nDstValid, fDstNoDataReal, bandNum,\n" + "0.0f, 0.0f, 0.0f);\n" + "} else if ( fAccumulatorWeight < 0.99999f || fAccumulatorWeight > 1.00001f ) {\n" + // Calculate the output taking into account weighting + "setPixel(dstReal, dstImag, dstDensity, nDstValid, fDstNoDataReal, bandNum,\n" + "fAccumulatorDensity / fAccumulatorWeight,\n" + "fAccumulatorReal / fAccumulatorWeight,\n" +"#if useImag != 0\n" + "fAccumulatorImag / fAccumulatorWeight );\n" +"#else\n" + "0.0f );\n" +"#endif\n" + "} else {\n" + "setPixel(dstReal, dstImag, dstDensity, nDstValid, fDstNoDataReal, bandNum,\n" + "fAccumulatorDensity, fAccumulatorReal, fAccumulatorImag);\n" + "}\n" +"}\n"; + + //Defines based on image format + switch (warper->imageFormat) { + case CL_FLOAT: + dstMinVal = -FLT_MAX; + dstMaxVal = FLT_MAX; + outType = "float"; + break; + case CL_SNORM_INT8: + dstMinVal = -128.0; + dstMaxVal = 127.0; + outType = "char"; + break; + case CL_UNORM_INT8: + dstMinVal = 0.0; + dstMaxVal = 255.0; + outType = "uchar"; + break; + case CL_SNORM_INT16: + dstMinVal = -32768.0; + dstMaxVal = 32767.0; + outType = "short"; + break; + case CL_UNORM_INT16: + dstMinVal = 0.0; + dstMaxVal = 65535.0; + outType = "ushort"; + break; + } + + //Use vector format? + if (useVec) { + dUseVec = "-D USE_VEC"; + dVecf = "float4"; + } + + //Assemble the kernel from parts. The compiler is unable to handle multiple + //kernels in one string with more than a few __constant modifiers each. + if (warper->resampAlg == OCL_Bilinear) + sprintf(progBuf, "%s\n%s", kernGenFuncs, kernBilinear); + else if (warper->resampAlg == OCL_Cubic) + sprintf(progBuf, "%s\n%s", kernGenFuncs, kernCubic); + else + sprintf(progBuf, "%s\n%s", kernGenFuncs, kernResampler); + + //Actually make the program from assembled source + program = clCreateProgramWithSource(warper->context, 1, (const char**)&progBuf, + NULL, &err); + handleErrGoto(err, error_final); + + //Assemble the compiler arg string for speed. All invariants should be defined here. + sprintf(buffer, "-cl-fast-relaxed-math -Werror -D FALSE=0 -D TRUE=1 " + "%s" + "-D iSrcWidth=%d -D iSrcHeight=%d -D iDstWidth=%d -D iDstHeight=%d " + "-D useUnifiedSrcDensity=%d -D useUnifiedSrcValid=%d " + "-D useDstDensity=%d -D useDstValid=%d -D useImag=%d " + "-D fXScale=%015.15lff -D fYScale=%015.15lff -D fXFilter=%015.15lff -D fYFilter=%015.15lff " + "-D nXRadius=%d -D nYRadius=%d -D nFiltInitX=%d -D nFiltInitY=%d " + "-D PI=%015.15lff -D outType=%s -D dstMinVal=%015.15lff -D dstMaxVal=%015.15lff " + "-D useDstNoDataReal=%d -D vecf=%s %s -D doCubicSpline=%d " + "-D useUseBandSrcValid=%d -D iCoordMult=%d ", + /* FIXME: Is it really a ATI specific thing ? */ + (warper->imageFormat == CL_FLOAT && (warper->eCLVendor == VENDOR_AMD || warper->eCLVendor == VENDOR_INTEL)) ? "-D USE_CLAMP_TO_DST_FLOAT=1 " : "", + warper->srcWidth, warper->srcHeight, warper->dstWidth, warper->dstHeight, + warper->useUnifiedSrcDensity, warper->useUnifiedSrcValid, + warper->useDstDensity, warper->useDstValid, warper->imagWorkCL != NULL, + dfXScale, dfYScale, dfXFilter, dfYFilter, + nXRadius, nYRadius, nFiltInitX, nFiltInitY, + M_PI, outType, dstMinVal, dstMaxVal, warper->fDstNoDataRealCL != NULL, + dVecf, dUseVec, warper->resampAlg == OCL_CubicSpline, + warper->nBandSrcValidCL != NULL, warper->coordMult); + + (*clErr) = err = clBuildProgram(program, 1, &(warper->dev), buffer, NULL, NULL); + + //Detailed debugging info + if (err != CL_SUCCESS) + { + const char* pszStatus = "unknown_status"; + err = clGetProgramBuildInfo(program, warper->dev, CL_PROGRAM_BUILD_LOG, + 128000*sizeof(char), buffer, NULL); + handleErrGoto(err, error_free_program); + + CPLError(CE_Failure, CPLE_AppDefined, "Error: Failed to build program executable!\nBuild Log:\n%s", buffer); + + err = clGetProgramBuildInfo(program, warper->dev, CL_PROGRAM_BUILD_STATUS, + 128000*sizeof(char), buffer, NULL); + handleErrGoto(err, error_free_program); + + if(buffer[0] == CL_BUILD_NONE) + pszStatus = "CL_BUILD_NONE"; + else if(buffer[0] == CL_BUILD_ERROR) + pszStatus = "CL_BUILD_ERROR"; + else if(buffer[0] == CL_BUILD_SUCCESS) + pszStatus = "CL_BUILD_SUCCESS"; + else if(buffer[0] == CL_BUILD_IN_PROGRESS) + pszStatus = "CL_BUILD_IN_PROGRESS"; + + CPLDebug("OpenCL", "Build Status: %s\nProgram Source:\n%s", pszStatus, progBuf); + goto error_free_program; + } + + kernel = clCreateKernel(program, "resamp", &err); + handleErrGoto(err, error_free_program); + + err = clReleaseProgram(program); + handleErrGoto(err, error_final); + + CPLFree(buffer); + CPLFree(progBuf); + return kernel; + +error_free_program: + err = clReleaseProgram(program); + +error_final: + CPLFree(buffer); + CPLFree(progBuf); + return NULL; +} + +/* + Alloc & copy the coordinate data from host working memory to the device. The + working memory should be a pinned, linear, array of floats. This allows us to + allocate and copy all data in one step. The pointer to the device memory is + saved and set as the appropriate argument number. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int set_coord_data (struct oclWarper *warper, cl_mem *xy) +{ + cl_int err = CL_SUCCESS; + cl_image_format imgFmt; + + //Copy coord data to the device + imgFmt.image_channel_order = warper->xyChOrder; + imgFmt.image_channel_data_type = CL_FLOAT; + (*xy) = clCreateImage2D(warper->context, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, &imgFmt, + (size_t) warper->xyWidth, + (size_t) warper->xyHeight, + (size_t) sizeof(float) * warper->xyChSize * warper->xyWidth, + warper->xyWork, &err); + handleErr(err); + + //Free the source memory, now that it's copied we don't need it + freeCLMem(warper->xyWorkCL, warper->xyWork); + + //Set up argument + if (warper->kern1 != NULL) { + handleErr(err = clSetKernelArg(warper->kern1, 0, sizeof(cl_mem), xy)); + } + if (warper->kern4 != NULL) { + handleErr(err = clSetKernelArg(warper->kern4, 0, sizeof(cl_mem), xy)); + } + + return CL_SUCCESS; +} + +/* + Sets the unified density & valid data structures. These are optional structures + from GDAL, and as such if they are NULL a small placeholder memory segment is + defined. This is because the spec is unclear on if a NULL value can be passed + as a kernel argument in place of memory. If it's not NULL, the data is copied + from the working memory to the device memory. After that, we check if we are + using the per-band validity mask, and set that as appropriate. At the end, the + CL mem is passed as the kernel arguments. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int set_unified_data(struct oclWarper *warper, + cl_mem *unifiedSrcDensityCL, cl_mem *unifiedSrcValidCL, + float *unifiedSrcDensity, unsigned int *unifiedSrcValid, + cl_mem *useBandSrcValidCL, cl_mem *nBandSrcValidCL) +{ + cl_int err = CL_SUCCESS; + size_t sz = warper->srcWidth * warper->srcHeight; + int useValid = warper->nBandSrcValidCL != NULL; + //32 bits in the mask + int validSz = sizeof(int) * ((31 + sz) >> 5); + + //Copy unifiedSrcDensity if it exists + if (unifiedSrcDensity == NULL) { + //Alloc dummy device RAM + (*unifiedSrcDensityCL) = clCreateBuffer(warper->context, CL_MEM_READ_ONLY, 1, NULL, &err); + handleErr(err); + } else { + //Alloc & copy all density data + (*unifiedSrcDensityCL) = clCreateBuffer(warper->context, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + sizeof(float) * sz, unifiedSrcDensity, &err); + handleErr(err); + } + + //Copy unifiedSrcValid if it exists + if (unifiedSrcValid == NULL) { + //Alloc dummy device RAM + (*unifiedSrcValidCL) = clCreateBuffer(warper->context, CL_MEM_READ_ONLY, 1, NULL, &err); + handleErr(err); + } else { + //Alloc & copy all validity data + (*unifiedSrcValidCL) = clCreateBuffer(warper->context, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + validSz, unifiedSrcValid, &err); + handleErr(err); + } + + // Set the band validity usage + if(useValid) { + (*useBandSrcValidCL) = clCreateBuffer(warper->context, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + sizeof(char) * warper->numBands, + warper->useBandSrcValid, &err); + handleErr(err); + } else { + //Make a fake image so we don't have a NULL pointer + (*useBandSrcValidCL) = clCreateBuffer(warper->context, CL_MEM_READ_ONLY, 1, NULL, &err); + handleErr(err); + } + + //Do a more thorough check for validity + if (useValid) { + int i; + useValid = FALSE; + for (i = 0; i < warper->numBands; ++i) + if (warper->useBandSrcValid[i]) + useValid = TRUE; + } + + //And the validity mask if needed + if (useValid) { + (*nBandSrcValidCL) = clCreateBuffer(warper->context, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + warper->numBands * validSz, + warper->nBandSrcValid, &err); + handleErr(err); + } else { + //Make a fake image so we don't have a NULL pointer + (*nBandSrcValidCL) = clCreateBuffer(warper->context, CL_MEM_READ_ONLY, 1, NULL, &err); + handleErr(err); + } + + //Set up arguments + if (warper->kern1 != NULL) { + handleErr(err = clSetKernelArg(warper->kern1, 3, sizeof(cl_mem), unifiedSrcDensityCL)); + handleErr(err = clSetKernelArg(warper->kern1, 4, sizeof(cl_mem), unifiedSrcValidCL)); + handleErr(err = clSetKernelArg(warper->kern1, 5, sizeof(cl_mem), useBandSrcValidCL)); + handleErr(err = clSetKernelArg(warper->kern1, 6, sizeof(cl_mem), nBandSrcValidCL)); + } + if (warper->kern4 != NULL) { + handleErr(err = clSetKernelArg(warper->kern4, 3, sizeof(cl_mem), unifiedSrcDensityCL)); + handleErr(err = clSetKernelArg(warper->kern4, 4, sizeof(cl_mem), unifiedSrcValidCL)); + handleErr(err = clSetKernelArg(warper->kern4, 5, sizeof(cl_mem), useBandSrcValidCL)); + handleErr(err = clSetKernelArg(warper->kern4, 6, sizeof(cl_mem), nBandSrcValidCL)); + } + + return CL_SUCCESS; +} + +/* + Here we set the per-band raster data. First priority is the real raster data, + of course. Then, if applicable, we set the additional image channel. Once this + data is copied to the device, it can be freed on the host, so that is done + here. Finally the appropriate kernel arguments are set. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int set_src_rast_data (struct oclWarper *warper, int iNum, size_t sz, + cl_channel_order chOrder, cl_mem *srcReal, cl_mem *srcImag) +{ + cl_image_format imgFmt; + cl_int err = CL_SUCCESS; + int useImagWork = warper->imagWork.v != NULL && warper->imagWork.v[iNum] != NULL; + + //Set up image vars + imgFmt.image_channel_order = chOrder; + imgFmt.image_channel_data_type = warper->imageFormat; + + //Create & copy the source image + (*srcReal) = clCreateImage2D(warper->context, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, &imgFmt, + (size_t) warper->srcWidth, (size_t) warper->srcHeight, + sz * warper->srcWidth, warper->realWork.v[iNum], &err); + handleErr(err); + + //And the source image parts if needed + if (useImagWork) { + (*srcImag) = clCreateImage2D(warper->context, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, &imgFmt, + (size_t) warper->srcWidth, (size_t) warper->srcHeight, + sz * warper->srcWidth, warper->imagWork.v[iNum], &err); + handleErr(err); + } else { + //Make a fake image so we don't have a NULL pointer + + char dummyImageData[16]; + (*srcImag) = clCreateImage2D(warper->context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, &imgFmt, + 1, 1, sz, dummyImageData, &err); + + handleErr(err); + } + + //Free the source memory, now that it's copied we don't need it + freeCLMem(warper->realWorkCL[iNum], warper->realWork.v[iNum]); + if (warper->imagWork.v != NULL) { + freeCLMem(warper->imagWorkCL[iNum], warper->imagWork.v[iNum]); + } + + //Set up per-band arguments + if (warper->kern1 != NULL) { + handleErr(err = clSetKernelArg(warper->kern1, 1, sizeof(cl_mem), srcReal)); + handleErr(err = clSetKernelArg(warper->kern1, 2, sizeof(cl_mem), srcImag)); + } + if (warper->kern4 != NULL) { + handleErr(err = clSetKernelArg(warper->kern4, 1, sizeof(cl_mem), srcReal)); + handleErr(err = clSetKernelArg(warper->kern4, 2, sizeof(cl_mem), srcImag)); + } + + return CL_SUCCESS; +} + +/* + Set the destination data for the raster. Although it's the output, it still + is copied to the device because some blending is done there. First the real + data is allocated and copied, then the imag data is allocated and copied if + needed. They are then set as the appropriate arguments to the kernel. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int set_dst_rast_data(struct oclWarper *warper, int iImg, size_t sz, + cl_mem *dstReal, cl_mem *dstImag) +{ + cl_int err = CL_SUCCESS; + sz *= warper->dstWidth * warper->dstHeight; + + //Copy the dst real data + (*dstReal) = clCreateBuffer(warper->context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, + sz, warper->dstRealWork.v[iImg], &err); + handleErr(err); + + //Copy the dst imag data if exists + if (warper->dstImagWork.v != NULL && warper->dstImagWork.v[iImg] != NULL) { + (*dstImag) = clCreateBuffer(warper->context, + CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, + sz, warper->dstImagWork.v[iImg], &err); + handleErr(err); + } else { + (*dstImag) = clCreateBuffer(warper->context, CL_MEM_READ_WRITE, 1, NULL, &err); + handleErr(err); + } + + //Set up per-band arguments + if (warper->kern1 != NULL) { + handleErr(err = clSetKernelArg(warper->kern1, 7, sizeof(cl_mem), dstReal)); + handleErr(err = clSetKernelArg(warper->kern1, 8, sizeof(cl_mem), dstImag)); + } + if (warper->kern4 != NULL) { + handleErr(err = clSetKernelArg(warper->kern4, 7, sizeof(cl_mem), dstReal)); + handleErr(err = clSetKernelArg(warper->kern4, 8, sizeof(cl_mem), dstImag)); + } + + return CL_SUCCESS; +} + +/* + Read the final raster data back from the graphics card to working memory. This + copies both the real memory and the imag memory if appropriate. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int get_dst_rast_data(struct oclWarper *warper, int iImg, size_t wordSz, + cl_mem dstReal, cl_mem dstImag) +{ + cl_int err = CL_SUCCESS; + size_t sz = warper->dstWidth * warper->dstHeight * wordSz; + + //Copy from dev into working memory + handleErr(err = clEnqueueReadBuffer(warper->queue, dstReal, + CL_FALSE, 0, sz, warper->dstRealWork.v[iImg], + 0, NULL, NULL)); + + //If we are expecting the imag channel, then copy it back also + if (warper->dstImagWork.v != NULL && warper->dstImagWork.v[iImg] != NULL) { + handleErr(err = clEnqueueReadBuffer(warper->queue, dstImag, + CL_FALSE, 0, sz, warper->dstImagWork.v[iImg], + 0, NULL, NULL)); + } + + //The copy requests were non-blocking, so we'll need to make sure they finish. + handleErr(err = clFinish(warper->queue)); + + return CL_SUCCESS; +} + +/* + Set the destination image density & validity mask on the device. This is used + to blend the final output image with the existing buffer. This handles the + unified structures that apply to all bands. After the buffers are created and + copied, they are set as kernel arguments. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int set_dst_data(struct oclWarper *warper, + cl_mem *dstDensityCL, cl_mem *dstValidCL, cl_mem *dstNoDataRealCL, + float *dstDensity, unsigned int *dstValid, float *dstNoDataReal) +{ + cl_int err = CL_SUCCESS; + size_t sz = warper->dstWidth * warper->dstHeight; + + //Copy the no-data value(s) + if (dstNoDataReal == NULL) { + (*dstNoDataRealCL) = clCreateBuffer(warper->context, CL_MEM_READ_ONLY, 1, NULL, &err); + handleErr(err); + } else { + (*dstNoDataRealCL) = clCreateBuffer(warper->context, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + sizeof(float) * warper->numBands, dstNoDataReal, &err); + handleErr(err); + } + + //Copy unifiedSrcDensity if it exists + if (dstDensity == NULL) { + (*dstDensityCL) = clCreateBuffer(warper->context, CL_MEM_READ_ONLY, 1, NULL, &err); + handleErr(err); + } else { + (*dstDensityCL) = clCreateBuffer(warper->context, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + sizeof(float) * sz, dstDensity, &err); + handleErr(err); + } + + //Copy unifiedSrcValid if it exists + if (dstValid == NULL) { + (*dstValidCL) = clCreateBuffer(warper->context, CL_MEM_READ_ONLY, 1, NULL, &err); + handleErr(err); + } else { + (*dstValidCL) = clCreateBuffer(warper->context, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + sizeof(int) * ((31 + sz) >> 5), dstValid, &err); + handleErr(err); + } + + //Set up arguments + if (warper->kern1 != NULL) { + handleErr(err = clSetKernelArg(warper->kern1, 9, sizeof(cl_mem), dstNoDataRealCL)); + handleErr(err = clSetKernelArg(warper->kern1, 10, sizeof(cl_mem), dstDensityCL)); + handleErr(err = clSetKernelArg(warper->kern1, 11, sizeof(cl_mem), dstValidCL)); + } + if (warper->kern4 != NULL) { + handleErr(err = clSetKernelArg(warper->kern4, 9, sizeof(cl_mem), dstNoDataRealCL)); + handleErr(err = clSetKernelArg(warper->kern4, 10, sizeof(cl_mem), dstDensityCL)); + handleErr(err = clSetKernelArg(warper->kern4, 11, sizeof(cl_mem), dstValidCL)); + } + + return CL_SUCCESS; +} + +/* + Go ahead and execute the kernel. This handles some housekeeping stuff like the + run dimensions. When running in debug mode, it times the kernel call and prints + the execution time. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int execute_kern(struct oclWarper *warper, cl_kernel kern, size_t loc_size) +{ + cl_int err = CL_SUCCESS; + cl_event ev; + size_t ceil_runs[2]; + size_t group_size[2]; +#ifdef DEBUG_OPENCL + size_t start_time = 0; + size_t end_time; + char *vecTxt = ""; +#endif + + // Use a likely X-dimension which is a power of 2 + if (loc_size >= 512) + group_size[0] = 32; + else if (loc_size >= 64) + group_size[0] = 16; + else if (loc_size > 8) + group_size[0] = 8; + else + group_size[0] = 1; + + if (group_size[0] > loc_size) + group_size[1] = group_size[0]/loc_size; + else + group_size[1] = 1; + + //Round up num_runs to find the dim of the block of pixels we'll be processing + if(warper->dstWidth % group_size[0]) + ceil_runs[0] = warper->dstWidth + group_size[0] - warper->dstWidth % group_size[0]; + else + ceil_runs[0] = warper->dstWidth; + + if(warper->dstHeight % group_size[1]) + ceil_runs[1] = warper->dstHeight + group_size[1] - warper->dstHeight % group_size[1]; + else + ceil_runs[1] = warper->dstHeight; + +#ifdef DEBUG_OPENCL + handleErr(err = clSetCommandQueueProperty(warper->queue, CL_QUEUE_PROFILING_ENABLE, CL_TRUE, NULL)); +#endif + + // Run the calculation by enqueuing it and forcing the + // command queue to complete the task + handleErr(err = clEnqueueNDRangeKernel(warper->queue, kern, 2, NULL, + ceil_runs, group_size, 0, NULL, &ev)); + handleErr(err = clFinish(warper->queue)); + +#ifdef DEBUG_OPENCL + handleErr(err = clGetEventProfilingInfo(ev, CL_PROFILING_COMMAND_START, + sizeof(size_t), &start_time, NULL)); + handleErr(err = clGetEventProfilingInfo(ev, CL_PROFILING_COMMAND_END, + sizeof(size_t), &end_time, NULL)); + assert(end_time != 0); + assert(start_time != 0); + handleErr(err = clReleaseEvent(ev)); + if (kern == warper->kern4) + vecTxt = "(vec)"; + + CPLDebug("OpenCL", "Kernel Time: %6s %10lu", vecTxt, (long int)((end_time-start_time)/100000)); +#endif + return CL_SUCCESS; +} + +/* + Copy data from a raw source to the warper's working memory. If the imag + channel is expected, then the data will be de-interlaced into component blocks + of memory. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int set_img_data(struct oclWarper *warper, void *srcImgData, + unsigned int width, unsigned int height, int isSrc, + unsigned int bandNum, void **dstRealImgs, void **dstImagImgs) +{ + unsigned int imgChSize = warper->imgChSize1; + unsigned int iSrcY, i; + unsigned int vecOff = 0; + unsigned int imgNum = bandNum; + void *dstReal = NULL; + void *dstImag = NULL; + + // Handle vector if needed + if (warper->useVec && bandNum < warper->numBands - warper->numBands % 4) { + imgChSize = warper->imgChSize4; + vecOff = bandNum % 4; + imgNum = bandNum / 4; + } else if(warper->useVec) { + imgNum = bandNum / 4 + bandNum % 4; + } + + // Set the images as needed + dstReal = dstRealImgs[imgNum]; + if(dstImagImgs == NULL) + dstImag = NULL; + else + dstImag = dstImagImgs[imgNum]; + + // Set stuff for dst imgs + if (!isSrc) { + vecOff *= height * width; + imgChSize = 1; + } + + // Copy values as needed + if (warper->imagWorkCL == NULL && !(warper->useVec && isSrc)) { + //Set memory size & location depending on the data type + //This is the ideal code path for speed + switch (warper->imageFormat) { + case CL_UNORM_INT8: + { + unsigned char *realDst = &(((unsigned char *)dstReal)[vecOff]); + memcpy(realDst, srcImgData, width*height*sizeof(unsigned char)); + break; + } + case CL_SNORM_INT8: + { + char *realDst = &(((char *)dstReal)[vecOff]); + memcpy(realDst, srcImgData, width*height*sizeof(char)); + break; + } + case CL_UNORM_INT16: + { + unsigned short *realDst = &(((unsigned short *)dstReal)[vecOff]); + memcpy(realDst, srcImgData, width*height*sizeof(unsigned short)); + break; + } + case CL_SNORM_INT16: + { + short *realDst = &(((short *)dstReal)[vecOff]); + memcpy(realDst, srcImgData, width*height*sizeof(short)); + break; + } + case CL_FLOAT: + { + float *realDst = &(((float *)dstReal)[vecOff]); + memcpy(realDst, srcImgData, width*height*sizeof(float)); + break; + } + } + } else if (warper->imagWorkCL == NULL) { + //We need to space the values due to OpenCL implementation reasons + for( iSrcY = 0; iSrcY < height; iSrcY++ ) + { + int pxOff = width*iSrcY; + int imgOff = imgChSize*pxOff + vecOff; + //Copy & deinterleave interleaved data + switch (warper->imageFormat) { + case CL_UNORM_INT8: + { + unsigned char *realDst = &(((unsigned char *)dstReal)[imgOff]); + unsigned char *dataSrc = &(((unsigned char *)srcImgData)[pxOff]); + for (i = 0; i < width; ++i) + realDst[imgChSize*i] = dataSrc[i]; + } + break; + case CL_SNORM_INT8: + { + char *realDst = &(((char *)dstReal)[imgOff]); + char *dataSrc = &(((char *)srcImgData)[pxOff]); + for (i = 0; i < width; ++i) + realDst[imgChSize*i] = dataSrc[i]; + } + break; + case CL_UNORM_INT16: + { + unsigned short *realDst = &(((unsigned short *)dstReal)[imgOff]); + unsigned short *dataSrc = &(((unsigned short *)srcImgData)[pxOff]); + for (i = 0; i < width; ++i) + realDst[imgChSize*i] = dataSrc[i]; + } + break; + case CL_SNORM_INT16: + { + short *realDst = &(((short *)dstReal)[imgOff]); + short *dataSrc = &(((short *)srcImgData)[pxOff]); + for (i = 0; i < width; ++i) + realDst[imgChSize*i] = dataSrc[i]; + } + break; + case CL_FLOAT: + { + float *realDst = &(((float *)dstReal)[imgOff]); + float *dataSrc = &(((float *)srcImgData)[pxOff]); + for (i = 0; i < width; ++i) + realDst[imgChSize*i] = dataSrc[i]; + } + break; + } + } + } else { + //Copy, deinterleave, & space interleaved data + for( iSrcY = 0; iSrcY < height; iSrcY++ ) + { + int pxOff = width*iSrcY; + int imgOff = imgChSize*pxOff + vecOff; + //Copy & deinterleave interleaved data + switch (warper->imageFormat) { + case CL_FLOAT: + { + float *realDst = &(((float *)dstReal)[imgOff]); + float *imagDst = &(((float *)dstImag)[imgOff]); + float *dataSrc = &(((float *)srcImgData)[pxOff]); + for (i = 0; i < width; ++i) { + realDst[imgChSize*i] = dataSrc[i*2 ]; + imagDst[imgChSize*i] = dataSrc[i*2+1]; + } + } + break; + case CL_SNORM_INT8: + { + char *realDst = &(((char *)dstReal)[imgOff]); + char *imagDst = &(((char *)dstImag)[imgOff]); + char *dataSrc = &(((char *)srcImgData)[pxOff]); + for (i = 0; i < width; ++i) { + realDst[imgChSize*i] = dataSrc[i*2 ]; + imagDst[imgChSize*i] = dataSrc[i*2+1]; + } + } + break; + case CL_UNORM_INT8: + { + unsigned char *realDst = &(((unsigned char *)dstReal)[imgOff]); + unsigned char *imagDst = &(((unsigned char *)dstImag)[imgOff]); + unsigned char *dataSrc = &(((unsigned char *)srcImgData)[pxOff]); + for (i = 0; i < width; ++i) { + realDst[imgChSize*i] = dataSrc[i*2 ]; + imagDst[imgChSize*i] = dataSrc[i*2+1]; + } + } + break; + case CL_SNORM_INT16: + { + short *realDst = &(((short *)dstReal)[imgOff]); + short *imagDst = &(((short *)dstImag)[imgOff]); + short *dataSrc = &(((short *)srcImgData)[pxOff]); + for (i = 0; i < width; ++i) { + realDst[imgChSize*i] = dataSrc[i*2 ]; + imagDst[imgChSize*i] = dataSrc[i*2+1]; + } + } + break; + case CL_UNORM_INT16: + { + unsigned short *realDst = &(((unsigned short *)dstReal)[imgOff]); + unsigned short *imagDst = &(((unsigned short *)dstImag)[imgOff]); + unsigned short *dataSrc = &(((unsigned short *)srcImgData)[pxOff]); + for (i = 0; i < width; ++i) { + realDst[imgChSize*i] = dataSrc[i*2 ]; + imagDst[imgChSize*i] = dataSrc[i*2+1]; + } + } + break; + } + } + } + + return CL_SUCCESS; +} + +/* + Creates the struct which inits & contains the OpenCL context & environment. + Inits wired(?) space to buffer the image in host RAM. Chooses the OpenCL + device, perhaps the user can choose it later? This would also choose the + appropriate OpenCL image format (R, RG, RGBA, or multiples thereof). Space + for metadata can be allocated as required, though. + + Supported image formats are: + CL_FLOAT, CL_SNORM_INT8, CL_UNORM_INT8, CL_SNORM_INT16, CL_UNORM_INT16 + 32-bit int formats won't keep precision when converted to floats internally + and doubles are generally not supported on the GPU image formats. + */ +struct oclWarper* GDALWarpKernelOpenCL_createEnv(int srcWidth, int srcHeight, + int dstWidth, int dstHeight, + cl_channel_type imageFormat, + int numBands, int coordMult, + int useImag, int useBandSrcValid, + float *fDstDensity, + double *dfDstNoDataReal, + OCLResampAlg resampAlg, cl_int *clErr) +{ + struct oclWarper *warper; + int i; + size_t maxWidth = 0, maxHeight = 0; + cl_int err = CL_SUCCESS; + size_t fmtSize, sz; + cl_device_id device; + cl_bool bool_flag; + OCLVendor eCLVendor = VENDOR_OTHER; + + // Do we have a suitable OpenCL device? + device = get_device(&eCLVendor); + if( device == NULL ) + return NULL; + + err = clGetDeviceInfo(device, CL_DEVICE_IMAGE_SUPPORT, + sizeof(cl_bool), &bool_flag, &sz); + if( err != CL_SUCCESS || !bool_flag ) + { + CPLDebug( "OpenCL", "No image support on selected device." ); + return NULL; + } + + // Set up warper environment. + warper = (struct oclWarper *)CPLCalloc(1, sizeof(struct oclWarper)); + + warper->eCLVendor = eCLVendor; + + //Init passed vars + warper->srcWidth = srcWidth; + warper->srcHeight = srcHeight; + warper->dstWidth = dstWidth; + warper->dstHeight = dstHeight; + + warper->coordMult = coordMult; + warper->numBands = numBands; + warper->imageFormat = imageFormat; + warper->resampAlg = resampAlg; + + warper->useUnifiedSrcDensity = FALSE; + warper->useUnifiedSrcValid = FALSE; + warper->useDstDensity = FALSE; + warper->useDstValid = FALSE; + + warper->imagWorkCL = NULL; + warper->dstImagWorkCL = NULL; + warper->useBandSrcValidCL = NULL; + warper->useBandSrcValid = NULL; + warper->nBandSrcValidCL = NULL; + warper->nBandSrcValid = NULL; + warper->fDstNoDataRealCL = NULL; + warper->fDstNoDataReal = NULL; + warper->kern1 = NULL; + warper->kern4 = NULL; + + warper->dev = device; + + warper->context = clCreateContext(0, 1, &(warper->dev), NULL, NULL, &err); + handleErrGoto(err, error_label); + warper->queue = clCreateCommandQueue(warper->context, warper->dev, 0, &err); + handleErrGoto(err, error_label); + + //Ensure that we hand handle imagery of these dimensions + err = clGetDeviceInfo(warper->dev, CL_DEVICE_IMAGE2D_MAX_WIDTH, sizeof(size_t), &maxWidth, &sz); + handleErrGoto(err, error_label); + err = clGetDeviceInfo(warper->dev, CL_DEVICE_IMAGE2D_MAX_HEIGHT, sizeof(size_t), &maxHeight, &sz); + handleErrGoto(err, error_label); + if (maxWidth < srcWidth || maxHeight < srcHeight) { + err = CL_INVALID_IMAGE_SIZE; + handleErrGoto(err, error_label); + } + + // Split bands into sets of four when possible + // Cubic runs slower as vector, so don't use it (probably register pressure) + // Feel free to do more testing and come up with more precise case statements + if(numBands < 4 || resampAlg == OCL_Cubic) { + warper->numImages = numBands; + warper->useVec = FALSE; + } else { + warper->numImages = numBands/4 + numBands % 4; + warper->useVec = TRUE; + } + + //Make the pointer space for the real images + warper->realWorkCL = (cl_mem *)CPLCalloc(sizeof(cl_mem), warper->numImages); + warper->dstRealWorkCL = (cl_mem *)CPLCalloc(sizeof(cl_mem), warper->numImages); + + //Make space for the per-channel Imag data (if exists) + if (useImag) { + warper->imagWorkCL = (cl_mem *)CPLCalloc(sizeof(cl_mem), warper->numImages); + warper->dstImagWorkCL = (cl_mem *)CPLCalloc(sizeof(cl_mem), warper->numImages); + } + + //Make space for the per-band BandSrcValid data (if exists) + if (useBandSrcValid) { + //32 bits in the mask + size_t sz = warper->numBands * ((31 + warper->srcWidth * warper->srcHeight) >> 5); + + //Allocate some space for the validity of the validity mask + err = alloc_pinned_mem(warper, 0, warper->numBands*sizeof(char), + (void **)&(warper->useBandSrcValid), + &(warper->useBandSrcValidCL)); + handleErrGoto(err, error_label); + + for (i = 0; i < warper->numBands; ++i) + warper->useBandSrcValid[i] = FALSE; + + //Allocate one array for all the band validity masks + //Remember that the masks don't use much memeory (they're bitwise) + err = alloc_pinned_mem(warper, 0, sz * sizeof(int), + (void **)&(warper->nBandSrcValid), + &(warper->nBandSrcValidCL)); + handleErrGoto(err, error_label); + } + + //Make space for the per-band + if (dfDstNoDataReal != NULL) { + alloc_pinned_mem(warper, 0, warper->numBands, + (void **)&(warper->fDstNoDataReal), &(warper->fDstNoDataRealCL)); + + //Copy over values + for (i = 0; i < warper->numBands; ++i) + warper->fDstNoDataReal[i] = dfDstNoDataReal[i]; + } + + //Alloc working host image memory + //We'll be copying into these buffers soon + switch (imageFormat) { + case CL_FLOAT: + err = alloc_working_arr(warper, sizeof(float *), sizeof(float), &fmtSize); + break; + case CL_SNORM_INT8: + err = alloc_working_arr(warper, sizeof(char *), sizeof(char), &fmtSize); + break; + case CL_UNORM_INT8: + err = alloc_working_arr(warper, sizeof(unsigned char *), sizeof(unsigned char), &fmtSize); + break; + case CL_SNORM_INT16: + err = alloc_working_arr(warper, sizeof(short *), sizeof(short), &fmtSize); + break; + case CL_UNORM_INT16: + err = alloc_working_arr(warper, sizeof(unsigned short *), sizeof(unsigned short), &fmtSize); + break; + } + handleErrGoto(err, error_label); + + //Find a good & compable image channel order for the Lat/Long arr + err = set_supported_formats(warper, 2, + &(warper->xyChOrder), &(warper->xyChSize), + CL_FLOAT); + handleErrGoto(err, error_label); + + //Set coordinate image dimensions + warper->xyWidth = ceil(((float)warper->dstWidth + (float)warper->coordMult-1)/(float)warper->coordMult); + warper->xyHeight = ceil(((float)warper->dstHeight + (float)warper->coordMult-1)/(float)warper->coordMult); + + //Alloc coord memory + sz = sizeof(float) * warper->xyChSize * warper->xyWidth * warper->xyHeight; + err = alloc_pinned_mem(warper, 0, sz, (void **)&(warper->xyWork), + &(warper->xyWorkCL)); + handleErrGoto(err, error_label); + + //Ensure everything is finished allocating, copying, & mapping + err = clFinish(warper->queue); + handleErrGoto(err, error_label); + + (*clErr) = CL_SUCCESS; + return warper; + +error_label: + GDALWarpKernelOpenCL_deleteEnv(warper); + return NULL; +} + +/* + Copy the validity mask for an image band to the warper. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int GDALWarpKernelOpenCL_setSrcValid(struct oclWarper *warper, + int *bandSrcValid, int bandNum) +{ + //32 bits in the mask + int stride = (31 + warper->srcWidth * warper->srcHeight) >> 5; + + //Copy bandSrcValid + assert(warper->nBandSrcValid != NULL); + memcpy(&(warper->nBandSrcValid[bandNum*stride]), bandSrcValid, sizeof(int) * stride); + warper->useBandSrcValid[bandNum] = TRUE; + + return CL_SUCCESS; +} + +/* + Sets the source image real & imag into the host memory so that it is + permuted (ex. RGBA) for better graphics card access. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int GDALWarpKernelOpenCL_setSrcImg(struct oclWarper *warper, void *imgData, + int bandNum) +{ + void **imagWorkPtr = NULL; + + if (warper->imagWorkCL != NULL) + imagWorkPtr = warper->imagWork.v; + + return set_img_data(warper, imgData, warper->srcWidth, warper->srcHeight, + TRUE, bandNum, warper->realWork.v, imagWorkPtr); +} + +/* + Sets the destination image real & imag into the host memory so that it is + permuted (ex. RGBA) for better graphics card access. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int GDALWarpKernelOpenCL_setDstImg(struct oclWarper *warper, void *imgData, + int bandNum) +{ + void **dstImagWorkPtr = NULL; + + if (warper->dstImagWorkCL != NULL) + dstImagWorkPtr = warper->dstImagWork.v; + + return set_img_data(warper, imgData, warper->dstWidth, warper->dstHeight, + FALSE, bandNum, warper->dstRealWork.v, dstImagWorkPtr); +} + +/* + Inputs the source coordinates for a row of the destination pixels. Invalid + coordinates are set as -99.0, which should be out of the image bounds. Sets + the coordinates as ready to be used in OpenCL image memory: interleaved and + minus the offset. By using image memory, we can use a smaller texture for + coordinates and use OpenCL's built-in interpolation to save memory. + + What it does: generates a smaller matrix of X/Y coordinate transformation + values from an original matrix. When bilinearly sampled in the GPU hardware, + the generated values are as close as possible to the original matrix. + + Complication: matricies have arbitrary dimensions and the sub-sampling factor + is an arbitrary integer greater than zero. Getting the edge cases right is + difficult. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int GDALWarpKernelOpenCL_setCoordRow(struct oclWarper *warper, + double *rowSrcX, double *rowSrcY, + double srcXOff, double srcYOff, + int *success, int rowNum) +{ + int coordMult = warper->coordMult; + int width = warper->dstWidth; + int height = warper->dstHeight; + int xyWidth = warper->xyWidth; + int i; + int xyChSize = warper->xyChSize; + float *xyPtr, *xyPrevPtr = NULL; + int lastRow = rowNum == height - 1; + double dstHeightMod = 1.0, dstWidthMod = 1.0; + + //Return if we're at an off row + if(!lastRow && rowNum % coordMult != 0) + return CL_SUCCESS; + + //Standard row, adjusted for the skipped rows + xyPtr = &(warper->xyWork[xyWidth * xyChSize * rowNum / coordMult]); + + //Find our row + if(lastRow){ + //Setup for the final row + xyPtr = &(warper->xyWork[xyWidth * xyChSize * (warper->xyHeight - 1)]); + xyPrevPtr = &(warper->xyWork[xyWidth * xyChSize * (warper->xyHeight - 2)]); + + if((height-1) % coordMult) + dstHeightMod = (double)coordMult / (double)((height-1) % coordMult); + } + + //Copy selected coordinates + for (i = 0; i < width; i += coordMult) { + if (success[i]) { + xyPtr[0] = rowSrcX[i] - srcXOff; + xyPtr[1] = rowSrcY[i] - srcYOff; + + if(lastRow) { + //Adjust bottom row so interpolator returns correct value + xyPtr[0] = dstHeightMod * (xyPtr[0] - xyPrevPtr[0]) + xyPrevPtr[0]; + xyPtr[1] = dstHeightMod * (xyPtr[1] - xyPrevPtr[1]) + xyPrevPtr[1]; + } + } else { + xyPtr[0] = -99.0f; + xyPtr[1] = -99.0f; + } + + xyPtr += xyChSize; + xyPrevPtr += xyChSize; + } + + //Copy remaining coordinate + if((width-1) % coordMult){ + dstWidthMod = (double)coordMult / (double)((width-1) % coordMult); + xyPtr -= xyChSize; + xyPrevPtr -= xyChSize; + } else { + xyPtr -= xyChSize*2; + xyPrevPtr -= xyChSize*2; + } + + if(lastRow) { + double origX = rowSrcX[width-1] - srcXOff; + double origY = rowSrcY[width-1] - srcYOff; + double a = 1.0, b = 1.0; + + // Calculate the needed x/y values using an equation from the OpenCL Spec + // section 8.2, solving for Ti1j1 + if((width -1) % coordMult) + a = ((width -1) % coordMult)/(double)coordMult; + + if((height-1) % coordMult) + b = ((height-1) % coordMult)/(double)coordMult; + + xyPtr[xyChSize ] = (((1.0 - a) * (1.0 - b) * xyPrevPtr[0] + + a * (1.0 - b) * xyPrevPtr[xyChSize] + + (1.0 - a) * b * xyPtr[0]) - origX)/(-a * b); + + xyPtr[xyChSize+1] = (((1.0 - a) * (1.0 - b) * xyPrevPtr[1] + + a * (1.0 - b) * xyPrevPtr[xyChSize+1] + + (1.0 - a) * b * xyPtr[1]) - origY)/(-a * b); + } else { + //Adjust last coordinate so interpolator returns correct value + xyPtr[xyChSize ] = dstWidthMod * (rowSrcX[width-1] - srcXOff - xyPtr[0]) + xyPtr[0]; + xyPtr[xyChSize+1] = dstWidthMod * (rowSrcY[width-1] - srcYOff - xyPtr[1]) + xyPtr[1]; + } + + return CL_SUCCESS; +} + +/* + Copies all data to the device RAM, frees the host RAM, runs the + appropriate resampling kernel, mallocs output space, & copies the data + back from the device RAM for each band. Also check to make sure that + setRow*() was called the appropriate number of times to init all image + data. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int GDALWarpKernelOpenCL_runResamp(struct oclWarper *warper, + float *unifiedSrcDensity, + unsigned int *unifiedSrcValid, + float *dstDensity, + unsigned int *dstValid, + double dfXScale, double dfYScale, + double dfXFilter, double dfYFilter, + int nXRadius, int nYRadius, + int nFiltInitX, int nFiltInitY) +{ + int i, nextBandNum = 0, chSize = 1; + cl_int err = CL_SUCCESS; + cl_mem xy, unifiedSrcDensityCL, unifiedSrcValidCL; + cl_mem dstDensityCL, dstValidCL, dstNoDataRealCL; + cl_mem useBandSrcValidCL, nBandSrcValidCL; + size_t groupSize, wordSize; + cl_kernel kern = NULL; + cl_channel_order chOrder; + + warper->useUnifiedSrcDensity = unifiedSrcDensity != NULL; + warper->useUnifiedSrcValid = unifiedSrcValid != NULL; + + //Check the word size + switch (warper->imageFormat) { + case CL_FLOAT: + wordSize = sizeof(float); + break; + case CL_SNORM_INT8: + wordSize = sizeof(char); + break; + case CL_UNORM_INT8: + wordSize = sizeof(unsigned char); + break; + case CL_SNORM_INT16: + wordSize = sizeof(short); + break; + case CL_UNORM_INT16: + wordSize = sizeof(unsigned short); + break; + } + + //Compile the kernel; the invariants are being compiled into the code + if (!warper->useVec || warper->numBands % 4) { + warper->kern1 = get_kernel(warper, FALSE, + dfXScale, dfYScale, dfXFilter, dfYFilter, + nXRadius, nYRadius, nFiltInitX, nFiltInitY, &err); + handleErr(err); + } + if (warper->useVec){ + warper->kern4 = get_kernel(warper, TRUE, + dfXScale, dfYScale, dfXFilter, dfYFilter, + nXRadius, nYRadius, nFiltInitX, nFiltInitY, &err); + handleErr(err); + } + + //Copy coord data to the device + handleErr(err = set_coord_data(warper, &xy)); + + //Copy unified density & valid data + handleErr(err = set_unified_data(warper, &unifiedSrcDensityCL, &unifiedSrcValidCL, + unifiedSrcDensity, unifiedSrcValid, + &useBandSrcValidCL, &nBandSrcValidCL)); + + //Copy output density & valid data + handleErr(set_dst_data(warper, &dstDensityCL, &dstValidCL, &dstNoDataRealCL, + dstDensity, dstValid, warper->fDstNoDataReal)); + + //What's the recommended group size? + if (warper->useVec) { + // Start with the vector kernel + handleErr(clGetKernelWorkGroupInfo(warper->kern4, warper->dev, + CL_KERNEL_WORK_GROUP_SIZE, + sizeof(size_t), &groupSize, NULL)); + kern = warper->kern4; + chSize = warper->imgChSize4; + chOrder = warper->imgChOrder4; + } else { + // We're only using the float kernel + handleErr(clGetKernelWorkGroupInfo(warper->kern1, warper->dev, + CL_KERNEL_WORK_GROUP_SIZE, + sizeof(size_t), &groupSize, NULL)); + kern = warper->kern1; + chSize = warper->imgChSize1; + chOrder = warper->imgChOrder1; + } + + //Loop over each image + for (i = 0; i < warper->numImages; ++i) + { + cl_mem srcImag, srcReal; + cl_mem dstReal, dstImag; + int bandNum = nextBandNum; + + //Switch kernels if needed + if (warper->useVec && nextBandNum < warper->numBands - warper->numBands % 4) { + nextBandNum += 4; + } else { + if (kern == warper->kern4) { + handleErr(clGetKernelWorkGroupInfo(warper->kern1, warper->dev, + CL_KERNEL_WORK_GROUP_SIZE, + sizeof(size_t), &groupSize, NULL)); + kern = warper->kern1; + chSize = warper->imgChSize1; + chOrder = warper->imgChOrder1; + } + ++nextBandNum; + } + + //Create & copy the source image + handleErr(err = set_src_rast_data(warper, i, chSize*wordSize, chOrder, + &srcReal, &srcImag)); + + //Create & copy the output image + if (kern == warper->kern1) { + handleErr(err = set_dst_rast_data(warper, i, wordSize, &dstReal, &dstImag)); + } else { + handleErr(err = set_dst_rast_data(warper, i, wordSize*4, &dstReal, &dstImag)); + } + + //Set the bandNum + handleErr(err = clSetKernelArg(kern, 12, sizeof(int), &bandNum)); + + //Run the kernel + handleErr(err = execute_kern(warper, kern, groupSize)); + + //Free loop CL mem + handleErr(err = clReleaseMemObject(srcReal)); + handleErr(err = clReleaseMemObject(srcImag)); + + //Copy the back output results + if (kern == warper->kern1) { + handleErr(err = get_dst_rast_data(warper, i, wordSize, dstReal, dstImag)); + } else { + handleErr(err = get_dst_rast_data(warper, i, wordSize*4, dstReal, dstImag)); + } + + //Free remaining CL mem + handleErr(err = clReleaseMemObject(dstReal)); + handleErr(err = clReleaseMemObject(dstImag)); + } + + //Free remaining CL mem + handleErr(err = clReleaseMemObject(xy)); + handleErr(err = clReleaseMemObject(unifiedSrcDensityCL)); + handleErr(err = clReleaseMemObject(unifiedSrcValidCL)); + handleErr(err = clReleaseMemObject(useBandSrcValidCL)); + handleErr(err = clReleaseMemObject(nBandSrcValidCL)); + handleErr(err = clReleaseMemObject(dstDensityCL)); + handleErr(err = clReleaseMemObject(dstValidCL)); + handleErr(err = clReleaseMemObject(dstNoDataRealCL)); + + return CL_SUCCESS; +} + +/* + Sets pointers to the floating point data in the warper. The pointers + are internal to the warper structure, so don't free() them. If the imag + channel is in use, it will receive a pointer. Otherwise it'll be set to NULL. + These are pointers to floating point data, so the caller will need to + manipulate the output as appropriate before saving the data. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int GDALWarpKernelOpenCL_getRow(struct oclWarper *warper, + void **rowReal, void **rowImag, + int rowNum, int bandNum) +{ + int memOff = rowNum * warper->dstWidth; + int imgNum = bandNum; + + if (warper->useVec && bandNum < warper->numBands - warper->numBands % 4) { + memOff += warper->dstWidth * warper->dstHeight * (bandNum % 4); + imgNum = bandNum / 4; + } else if(warper->useVec) { + imgNum = bandNum / 4 + bandNum % 4; + } + + //Return pointers into the warper's data + switch (warper->imageFormat) { + case CL_FLOAT: + (*rowReal) = &(warper->dstRealWork.f[imgNum][memOff]); + break; + case CL_SNORM_INT8: + (*rowReal) = &(warper->dstRealWork.c[imgNum][memOff]); + break; + case CL_UNORM_INT8: + (*rowReal) = &(warper->dstRealWork.uc[imgNum][memOff]); + break; + case CL_SNORM_INT16: + (*rowReal) = &(warper->dstRealWork.s[imgNum][memOff]); + break; + case CL_UNORM_INT16: + (*rowReal) = &(warper->dstRealWork.us[imgNum][memOff]); + break; + } + + if (warper->dstImagWorkCL == NULL) { + (*rowImag) = NULL; + } else { + switch (warper->imageFormat) { + case CL_FLOAT: + (*rowImag) = &(warper->dstImagWork.f[imgNum][memOff]); + break; + case CL_SNORM_INT8: + (*rowImag) = &(warper->dstImagWork.c[imgNum][memOff]); + break; + case CL_UNORM_INT8: + (*rowImag) = &(warper->dstImagWork.uc[imgNum][memOff]); + break; + case CL_SNORM_INT16: + (*rowImag) = &(warper->dstImagWork.s[imgNum][memOff]); + break; + case CL_UNORM_INT16: + (*rowImag) = &(warper->dstImagWork.us[imgNum][memOff]); + break; + } + } + + return CL_SUCCESS; +} + +/* + Free the OpenCL warper environment. It should check everything for NULL, so + be sure to mark free()ed pointers as NULL or it'll be double free()ed. + + Returns CL_SUCCESS on success and other CL_* errors when something goes wrong. + */ +cl_int GDALWarpKernelOpenCL_deleteEnv(struct oclWarper *warper) +{ + int i; + cl_int err = CL_SUCCESS; + + for (i = 0; i < warper->numImages; ++i) { + // Run free!! + void* dummy = NULL; + if( warper->realWork.v ) + freeCLMem(warper->realWorkCL[i], warper->realWork.v[i]); + else + freeCLMem(warper->realWorkCL[i], dummy); + if( warper->realWork.v ) + freeCLMem(warper->dstRealWorkCL[i], warper->dstRealWork.v[i]); + else + freeCLMem(warper->dstRealWorkCL[i], dummy); + + //(As applicable) + if(warper->imagWorkCL != NULL && warper->imagWork.v != NULL && warper->imagWork.v[i] != NULL) { + freeCLMem(warper->imagWorkCL[i], warper->imagWork.v[i]); + } + if(warper->dstImagWorkCL != NULL && warper->dstImagWork.v != NULL && warper->dstImagWork.v[i] != NULL) { + freeCLMem(warper->dstImagWorkCL[i], warper->dstImagWork.v[i]); + } + } + + //Free cl_mem + freeCLMem(warper->useBandSrcValidCL, warper->useBandSrcValid); + freeCLMem(warper->nBandSrcValidCL, warper->nBandSrcValid); + freeCLMem(warper->xyWorkCL, warper->xyWork); + freeCLMem(warper->fDstNoDataRealCL, warper->fDstNoDataReal); + + //Free pointers to cl_mem* + if (warper->realWorkCL != NULL) + CPLFree(warper->realWorkCL); + if (warper->dstRealWorkCL != NULL) + CPLFree(warper->dstRealWorkCL); + + if (warper->imagWorkCL != NULL) + CPLFree(warper->imagWorkCL); + if (warper->dstImagWorkCL != NULL) + CPLFree(warper->dstImagWorkCL); + + if (warper->realWork.v != NULL) + CPLFree(warper->realWork.v); + if (warper->dstRealWork.v != NULL) + CPLFree(warper->dstRealWork.v); + + if (warper->imagWork.v != NULL) + CPLFree(warper->imagWork.v); + if (warper->dstImagWork.v != NULL) + CPLFree(warper->dstImagWork.v); + + //Free OpenCL structures + if (warper->kern1 != NULL) + clReleaseKernel(warper->kern1); + if (warper->kern4 != NULL) + clReleaseKernel(warper->kern4); + if (warper->queue != NULL) + clReleaseCommandQueue(warper->queue); + if (warper->context != NULL) + clReleaseContext(warper->context); + + CPLFree(warper); + + return CL_SUCCESS; +} + +#endif /* defined(HAVE_OPENCL) */ + diff --git a/bazaar/plugin/gdal/alg/gdalwarpkernel_opencl.h b/bazaar/plugin/gdal/alg/gdalwarpkernel_opencl.h new file mode 100644 index 000000000..54f96256d --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalwarpkernel_opencl.h @@ -0,0 +1,198 @@ +/****************************************************************************** + * $Id: gdalwarpkernel_opencl.h 25068 2012-10-07 14:01:47Z rouault $ + * + * Project: OpenCL Image Reprojector + * Purpose: Implementation of the GDALWarpKernel reprojector in OpenCL. + * Author: Seth Price, seth@pricepages.org + * + ****************************************************************************** + * Copyright (c) 2010, Seth Price + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#if defined(HAVE_OPENCL) + +/* The following relates to the profiling calls to + clSetCommandQueueProperty() which are not available by default + with some OpenCL implementation (ie. ATI) */ + +#if defined(DEBUG_OPENCL) && DEBUG_OPENCL == 1 +#define CL_USE_DEPRECATED_OPENCL_1_0_APIS +#endif + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */ +extern "C" { +#endif + +typedef enum { + OCL_Bilinear=10, + OCL_Cubic=11, + OCL_CubicSpline=12, + OCL_Lanczos=13 +} OCLResampAlg; + +typedef enum +{ + VENDOR_OTHER, + VENDOR_AMD, + VENDOR_INTEL +} OCLVendor; + +struct oclWarper { + cl_command_queue queue; + cl_context context; + cl_device_id dev; + cl_kernel kern1; + cl_kernel kern4; + + int srcWidth; + int srcHeight; + int dstWidth; + int dstHeight; + + int useUnifiedSrcDensity; + int useUnifiedSrcValid; + int useDstDensity; + int useDstValid; + + int numBands; + int numImages; + OCLResampAlg resampAlg; + + cl_channel_type imageFormat; + cl_mem *realWorkCL; + union { + void **v; + char **c; + unsigned char **uc; + short **s; + unsigned short **us; + float **f; + } realWork; + + cl_mem *imagWorkCL; + union { + void **v; + char **c; + unsigned char **uc; + short **s; + unsigned short **us; + float **f; + } imagWork; + + cl_mem *dstRealWorkCL; + union { + void **v; + char **c; + unsigned char **uc; + short **s; + unsigned short **us; + float **f; + } dstRealWork; + + cl_mem *dstImagWorkCL; + union { + void **v; + char **c; + unsigned char **uc; + short **s; + unsigned short **us; + float **f; + } dstImagWork; + + unsigned int imgChSize1; + cl_channel_order imgChOrder1; + unsigned int imgChSize4; + cl_channel_order imgChOrder4; + char useVec; + + cl_mem useBandSrcValidCL; + char *useBandSrcValid; + + cl_mem nBandSrcValidCL; + float *nBandSrcValid; + + cl_mem xyWorkCL; + float *xyWork; + + int xyWidth; + int xyHeight; + int coordMult; + + unsigned int xyChSize; + cl_channel_order xyChOrder; + + cl_mem fDstNoDataRealCL; + float *fDstNoDataReal; + + OCLVendor eCLVendor; +}; + +struct oclWarper* GDALWarpKernelOpenCL_createEnv(int srcWidth, int srcHeight, + int dstWidth, int dstHeight, + cl_channel_type imageFormat, + int numBands, int coordMult, + int useImag, int useBandSrcValid, + float *fDstDensity, + double *dfDstNoDataReal, + OCLResampAlg resampAlg, cl_int *envErr); + +cl_int GDALWarpKernelOpenCL_setSrcValid(struct oclWarper *warper, + int *bandSrcValid, int bandNum); + +cl_int GDALWarpKernelOpenCL_setSrcImg(struct oclWarper *warper, void *imgData, + int bandNum); + +cl_int GDALWarpKernelOpenCL_setDstImg(struct oclWarper *warper, void *imgData, + int bandNum); + +cl_int GDALWarpKernelOpenCL_setCoordRow(struct oclWarper *warper, + double *rowSrcX, double *rowSrcY, + double srcXOff, double srcYOff, + int *success, int rowNum); + +cl_int GDALWarpKernelOpenCL_runResamp(struct oclWarper *warper, + float *unifiedSrcDensity, + unsigned int *unifiedSrcValid, + float *dstDensity, + unsigned int *dstValid, + double dfXScale, double dfYScale, + double dfXFilter, double dfYFilter, + int nXRadius, int nYRadius, + int nFiltInitX, int nFiltInitY); + +cl_int GDALWarpKernelOpenCL_getRow(struct oclWarper *warper, + void **rowReal, void **rowImag, + int rowNum, int bandNum); + +cl_int GDALWarpKernelOpenCL_deleteEnv(struct oclWarper *warper); + +#ifdef __cplusplus /* If this is a C++ compiler, end C linkage */ +} +#endif + +#endif /* defined(HAVE_OPENCL) */ + diff --git a/bazaar/plugin/gdal/alg/gdalwarpoperation.cpp b/bazaar/plugin/gdal/alg/gdalwarpoperation.cpp new file mode 100644 index 000000000..a150d1af1 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gdalwarpoperation.cpp @@ -0,0 +1,2391 @@ +/****************************************************************************** + * $Id: gdalwarpoperation.cpp 28876 2015-04-08 22:21:55Z rouault $ + * + * Project: High Performance Image Reprojector + * Purpose: Implementation of the GDALWarpOperation class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2007-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdalwarper.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include "ogr_api.h" + +CPL_CVSID("$Id: gdalwarpoperation.cpp 28876 2015-04-08 22:21:55Z rouault $"); + +struct _GDALWarpChunk { + int dx, dy, dsx, dsy; + int sx, sy, ssx, ssy; + int sExtraSx, sExtraSy; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GDALWarpOperation */ +/* ==================================================================== */ +/************************************************************************/ + +/** + * \class GDALWarpOperation "gdalwarper.h" + * + * High level image warping class. + +

Warper Design

+ +The overall GDAL high performance image warper is split into a few components. + + - The transformation between input and output file coordinates is handled +via GDALTransformerFunc() implementations such as the one returned by +GDALCreateGenImgProjTransformer(). The transformers are ultimately responsible +for translating pixel/line locations on the destination image to pixel/line +locations on the source image. + + - In order to handle images too large to hold in RAM, the warper needs to +segment large images. This is the responsibility of the GDALWarpOperation +class. The GDALWarpOperation::ChunkAndWarpImage() invokes +GDALWarpOperation::WarpRegion() on chunks of output and input image that +are small enough to hold in the amount of memory allowed by the application. +This process is described in greater detail in the Image Chunking +section. + + - The GDALWarpOperation::WarpRegion() function creates and loads an output +image buffer, and then calls WarpRegionToBuffer(). + + - GDALWarpOperation::WarpRegionToBuffer() is responsible for loading the +source imagery corresponding to a particular output region, and generating +masks and density masks from the source and destination imagery using +the generator functions found in the GDALWarpOptions structure. Binds this +all into an instance of GDALWarpKernel on which the +GDALWarpKernel::PerformWarp() method is called. + + - GDALWarpKernel does the actual image warping, but is given an input image +and an output image to operate on. The GDALWarpKernel does no IO, and in +fact knows nothing about GDAL. It invokes the transformation function to +get sample locations, builds output values based on the resampling algorithm +in use. It also takes any validity and density masks into account during +this operation. + +

Chunk Size Selection

+ +The GDALWarpOptions ChunkAndWarpImage() method is responsible for invoking +the WarpRegion() method on appropriate sized output chunks such that the +memory required for the output image buffer, input image buffer and any +required density and validity buffers is less than or equal to the application +defined maximum memory available for use. + +It checks the memory requrired by walking the edges of the output region, +transforming the locations back into source pixel/line coordinates and +establishing a bounding rectangle of source imagery that would be required +for the output area. This is actually accomplished by the private +GDALWarpOperation::ComputeSourceWindow() method. + +Then memory requirements are used by totaling the memory required for all +output bands, input bands, validity masks and density masks. If this is +greater than the GDALWarpOptions::dfWarpMemoryLimit then the destination +region is divided in two (splitting the longest dimension), and +ChunkAndWarpImage() recursively invoked on each destination subregion. + +

Validity and Density Masks Generation

+ +Fill in ways in which the validity and density masks may be generated here. +Note that detailed semantics of the masks should be found in +GDALWarpKernel. + +*/ + +/************************************************************************/ +/* GDALWarpOperation() */ +/************************************************************************/ + +GDALWarpOperation::GDALWarpOperation() + +{ + psOptions = NULL; + + hIOMutex = NULL; + hWarpMutex = NULL; + + nChunkListCount = 0; + nChunkListMax = 0; + pasChunkList = NULL; + + bReportTimings = FALSE; + nLastTimeReported = 0; +} + +/************************************************************************/ +/* ~GDALWarpOperation() */ +/************************************************************************/ + +GDALWarpOperation::~GDALWarpOperation() + +{ + WipeOptions(); + + if( hIOMutex != NULL ) + { + CPLDestroyMutex( hIOMutex ); + CPLDestroyMutex( hWarpMutex ); + } + + WipeChunkList(); +} + +/************************************************************************/ +/* GetOptions() */ +/************************************************************************/ + +const GDALWarpOptions *GDALWarpOperation::GetOptions() + +{ + return psOptions; +} + +/************************************************************************/ +/* WipeOptions() */ +/************************************************************************/ + +void GDALWarpOperation::WipeOptions() + +{ + if( psOptions != NULL ) + { + GDALDestroyWarpOptions( psOptions ); + psOptions = NULL; + } +} + +/************************************************************************/ +/* ValidateOptions() */ +/************************************************************************/ + +int GDALWarpOperation::ValidateOptions() + +{ + if( psOptions == NULL ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " no options currently initialized." ); + return FALSE; + } + + if( psOptions->dfWarpMemoryLimit < 100000.0 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " dfWarpMemoryLimit=%g is unreasonably small.", + psOptions->dfWarpMemoryLimit ); + return FALSE; + } + + if( psOptions->eResampleAlg != GRA_NearestNeighbour + && psOptions->eResampleAlg != GRA_Bilinear + && psOptions->eResampleAlg != GRA_Cubic + && psOptions->eResampleAlg != GRA_CubicSpline + && psOptions->eResampleAlg != GRA_Lanczos + && psOptions->eResampleAlg != GRA_Average + && psOptions->eResampleAlg != GRA_Mode + && psOptions->eResampleAlg != GRA_Max + && psOptions->eResampleAlg != GRA_Min + && psOptions->eResampleAlg != GRA_Med + && psOptions->eResampleAlg != GRA_Q1 + && psOptions->eResampleAlg != GRA_Q3) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " eResampleArg=%d is not a supported value.", + psOptions->eResampleAlg ); + return FALSE; + } + + if( (int) psOptions->eWorkingDataType < 1 + && (int) psOptions->eWorkingDataType >= GDT_TypeCount ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " eWorkingDataType=%d is not a supported value.", + psOptions->eWorkingDataType ); + return FALSE; + } + + if( psOptions->hSrcDS == NULL ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " hSrcDS is not set." ); + return FALSE; + } + + if( psOptions->nBandCount == 0 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " nBandCount=0, no bands configured!" ); + return FALSE; + } + + if( psOptions->panSrcBands == NULL ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " panSrcBands is NULL." ); + return FALSE; + } + + if( psOptions->hDstDS != NULL && psOptions->panDstBands == NULL ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " panDstBands is NULL." ); + return FALSE; + } + + for( int iBand = 0; iBand < psOptions->nBandCount; iBand++ ) + { + if( psOptions->panSrcBands[iBand] < 1 + || psOptions->panSrcBands[iBand] + > GDALGetRasterCount( psOptions->hSrcDS ) ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "panSrcBands[%d] = %d ... out of range for dataset.", + iBand, psOptions->panSrcBands[iBand] ); + return FALSE; + } + if( psOptions->hDstDS != NULL + && (psOptions->panDstBands[iBand] < 1 + || psOptions->panDstBands[iBand] + > GDALGetRasterCount( psOptions->hDstDS ) ) ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "panDstBands[%d] = %d ... out of range for dataset.", + iBand, psOptions->panDstBands[iBand] ); + return FALSE; + } + + if( psOptions->hDstDS != NULL + && GDALGetRasterAccess( + GDALGetRasterBand(psOptions->hDstDS, + psOptions->panDstBands[iBand])) + == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Destination band %d appears to be read-only.", + psOptions->panDstBands[iBand] ); + return FALSE; + } + } + + if( psOptions->nBandCount == 0 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " nBandCount=0, no bands configured!" ); + return FALSE; + } + + if( psOptions->padfSrcNoDataReal != NULL + && psOptions->padfSrcNoDataImag == NULL ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " padfSrcNoDataReal set, but padfSrcNoDataImag not set." ); + return FALSE; + } + + if( psOptions->pfnProgress == NULL ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " pfnProgress is NULL." ); + return FALSE; + } + + if( psOptions->pfnTransformer == NULL ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " pfnTransformer is NULL." ); + return FALSE; + } + + if( CSLFetchNameValue( psOptions->papszWarpOptions, + "SAMPLE_STEPS" ) != NULL ) + { + if( atoi(CSLFetchNameValue( psOptions->papszWarpOptions, + "SAMPLE_STEPS" )) < 2 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " SAMPLE_STEPS warp option has illegal value." ); + return FALSE; + } + } + + if( psOptions->nSrcAlphaBand > 0) + { + if ( psOptions->hSrcDS == NULL || + psOptions->nSrcAlphaBand > GDALGetRasterCount(psOptions->hSrcDS) ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "nSrcAlphaBand = %d ... out of range for dataset.", + psOptions->nSrcAlphaBand ); + return FALSE; + } + } + + if( psOptions->nDstAlphaBand > 0) + { + if ( psOptions->hDstDS == NULL || + psOptions->nDstAlphaBand > GDALGetRasterCount(psOptions->hDstDS) ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "nDstAlphaBand = %d ... out of range for dataset.", + psOptions->nDstAlphaBand ); + return FALSE; + } + } + + if( psOptions->nSrcAlphaBand > 0 + && psOptions->pfnSrcDensityMaskFunc != NULL ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " pfnSrcDensityMaskFunc provided as well as a SrcAlphaBand." ); + return FALSE; + } + + if( psOptions->nDstAlphaBand > 0 + && psOptions->pfnDstDensityMaskFunc != NULL ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GDALWarpOptions.Validate()\n" + " pfnDstDensityMaskFunc provided as well as a DstAlphaBand." ); + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +/** + * \fn CPLErr GDALWarpOperation::Initialize( const GDALWarpOptions * ); + * + * This method initializes the GDALWarpOperation's concept of the warp + * options in effect. It creates an internal copy of the GDALWarpOptions + * structure and defaults a variety of additional fields in the internal + * copy if not set in the provides warp options. + * + * Defaulting operations include: + * - If the nBandCount is 0, it will be set to the number of bands in the + * source image (which must match the output image) and the panSrcBands + * and panDstBands will be populated. + * + * @param psNewOptions input set of warp options. These are copied and may + * be destroyed after this call by the application. + * + * @return CE_None on success or CE_Failure if an error occurs. + */ + +CPLErr GDALWarpOperation::Initialize( const GDALWarpOptions *psNewOptions ) + +{ + CPLErr eErr = CE_None; + +/* -------------------------------------------------------------------- */ +/* Copy the passed in options. */ +/* -------------------------------------------------------------------- */ + if( psOptions != NULL ) + WipeOptions(); + + psOptions = GDALCloneWarpOptions( psNewOptions ); + psOptions->papszWarpOptions = CSLSetNameValue(psOptions->papszWarpOptions, + "EXTRA_ELTS", CPLSPrintf("%d", WARP_EXTRA_ELTS)); + +/* -------------------------------------------------------------------- */ +/* Default band mapping if missing. */ +/* -------------------------------------------------------------------- */ + if( psOptions->nBandCount == 0 + && psOptions->hSrcDS != NULL + && psOptions->hDstDS != NULL + && GDALGetRasterCount( psOptions->hSrcDS ) + == GDALGetRasterCount( psOptions->hDstDS ) ) + { + int i; + + psOptions->nBandCount = GDALGetRasterCount( psOptions->hSrcDS ); + + psOptions->panSrcBands = (int *) + CPLMalloc(sizeof(int) * psOptions->nBandCount ); + psOptions->panDstBands = (int *) + CPLMalloc(sizeof(int) * psOptions->nBandCount ); + + for( i = 0; i < psOptions->nBandCount; i++ ) + { + psOptions->panSrcBands[i] = i+1; + psOptions->panDstBands[i] = i+1; + } + } + +/* -------------------------------------------------------------------- */ +/* If no working data type was provided, set one now. */ +/* */ +/* Default to the highest resolution output band. But if the */ +/* input band is higher resolution and has a nodata value "out */ +/* of band" with the output type we may need to use the higher */ +/* resolution input type to ensure we can identify nodata values. */ +/* -------------------------------------------------------------------- */ + if( psOptions->eWorkingDataType == GDT_Unknown + && psOptions->hSrcDS != NULL + && psOptions->hDstDS != NULL + && psOptions->nBandCount >= 1 ) + { + int iBand; + psOptions->eWorkingDataType = GDT_Byte; + + for( iBand = 0; iBand < psOptions->nBandCount; iBand++ ) + { + GDALRasterBandH hDstBand = GDALGetRasterBand( + psOptions->hDstDS, psOptions->panDstBands[iBand] ); + GDALRasterBandH hSrcBand = GDALGetRasterBand( + psOptions->hSrcDS, psOptions->panSrcBands[iBand] ); + + if( hDstBand != NULL ) + psOptions->eWorkingDataType = + GDALDataTypeUnion( psOptions->eWorkingDataType, + GDALGetRasterDataType( hDstBand ) ); + + if( hSrcBand != NULL + && psOptions->padfSrcNoDataReal != NULL ) + { + int bMergeSource = FALSE; + + if( psOptions->padfSrcNoDataImag != NULL + && psOptions->padfSrcNoDataImag[iBand] != 0.0 + && !GDALDataTypeIsComplex( psOptions->eWorkingDataType ) ) + bMergeSource = TRUE; + else if( psOptions->padfSrcNoDataReal[iBand] < 0.0 + && (psOptions->eWorkingDataType == GDT_Byte + || psOptions->eWorkingDataType == GDT_UInt16 + || psOptions->eWorkingDataType == GDT_UInt32) ) + bMergeSource = TRUE; + else if( psOptions->padfSrcNoDataReal[iBand] < -32768.0 + && psOptions->eWorkingDataType == GDT_Int16 ) + bMergeSource = TRUE; + else if( psOptions->padfSrcNoDataReal[iBand] < -2147483648.0 + && psOptions->eWorkingDataType == GDT_Int32 ) + bMergeSource = TRUE; + else if( psOptions->padfSrcNoDataReal[iBand] > 256 + && psOptions->eWorkingDataType == GDT_Byte ) + bMergeSource = TRUE; + else if( psOptions->padfSrcNoDataReal[iBand] > 32767 + && psOptions->eWorkingDataType == GDT_Int16 ) + bMergeSource = TRUE; + else if( psOptions->padfSrcNoDataReal[iBand] > 65535 + && psOptions->eWorkingDataType == GDT_UInt16 ) + bMergeSource = TRUE; + else if( psOptions->padfSrcNoDataReal[iBand] > 2147483648.0 + && psOptions->eWorkingDataType == GDT_Int32 ) + bMergeSource = TRUE; + else if( psOptions->padfSrcNoDataReal[iBand] > 4294967295.0 + && psOptions->eWorkingDataType == GDT_UInt32 ) + bMergeSource = TRUE; + + if( bMergeSource ) + psOptions->eWorkingDataType = + GDALDataTypeUnion( psOptions->eWorkingDataType, + GDALGetRasterDataType( hSrcBand ) ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Default memory available. */ +/* */ +/* For now we default to 64MB of RAM, but eventually we should */ +/* try various schemes to query physical RAM. This can */ +/* certainly be done on Win32 and Linux. */ +/* -------------------------------------------------------------------- */ + if( psOptions->dfWarpMemoryLimit == 0.0 ) + { + psOptions->dfWarpMemoryLimit = 64.0 * 1024*1024; + } + +/* -------------------------------------------------------------------- */ +/* Are we doing timings? */ +/* -------------------------------------------------------------------- */ + bReportTimings = CSLFetchBoolean( psOptions->papszWarpOptions, + "REPORT_TIMINGS", FALSE ); + +/* -------------------------------------------------------------------- */ +/* Support creating cutline from text warpoption. */ +/* -------------------------------------------------------------------- */ + const char *pszCutlineWKT = + CSLFetchNameValue( psOptions->papszWarpOptions, "CUTLINE" ); + + if( pszCutlineWKT ) + { + if( OGR_G_CreateFromWkt( (char **) &pszCutlineWKT, NULL, + (OGRGeometryH *) &(psOptions->hCutline) ) + != OGRERR_NONE ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to parse CUTLINE geometry wkt." ); + } + else + { + const char *pszBD = CSLFetchNameValue( psOptions->papszWarpOptions, + "CUTLINE_BLEND_DIST" ); + if( pszBD ) + psOptions->dfCutlineBlendDist = CPLAtof(pszBD); + } + } + +/* -------------------------------------------------------------------- */ +/* If the options don't validate, then wipe them. */ +/* -------------------------------------------------------------------- */ + if( !ValidateOptions() ) + eErr = CE_Failure; + + if( eErr != CE_None ) + WipeOptions(); + + return eErr; +} + +/************************************************************************/ +/* GDALCreateWarpOperation() */ +/************************************************************************/ + +/** + * @see GDALWarpOperation::Initialize() + */ + +GDALWarpOperationH GDALCreateWarpOperation( + const GDALWarpOptions *psNewOptions ) +{ + GDALWarpOperation *poOperation; + + poOperation = new GDALWarpOperation; + if ( poOperation->Initialize( psNewOptions ) != CE_None ) + { + delete poOperation; + return NULL; + } + + return (GDALWarpOperationH)poOperation; +} + +/************************************************************************/ +/* GDALDestroyWarpOperation() */ +/************************************************************************/ + +/** + * @see GDALWarpOperation::~GDALWarpOperation() + */ + +void GDALDestroyWarpOperation( GDALWarpOperationH hOperation ) +{ + if ( hOperation ) + delete static_cast(hOperation); +} + +/************************************************************************/ +/* ChunkAndWarpImage() */ +/************************************************************************/ + +static int OrderWarpChunk(const void* _a, const void *_b) +{ + const GDALWarpChunk* a = (const GDALWarpChunk* )_a; + const GDALWarpChunk* b = (const GDALWarpChunk* )_b; + if (a->dy < b->dy) + return -1; + else if (a->dy > b->dy) + return 1; + else if (a->dx < b->dx) + return -1; + else if (a->dx > b->dx) + return 1; + else + return 0; +} + +/** + * \fn CPLErr GDALWarpOperation::ChunkAndWarpImage( + int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize ); + * + * This method does a complete warp of the source image to the destination + * image for the indicated region with the current warp options in effect. + * Progress is reported to the installed progress monitor, if any. + * + * This function will subdivide the region and recursively call itself + * until the total memory required to process a region chunk will all fit + * in the memory pool defined by GDALWarpOptions::dfWarpMemoryLimit. + * + * Once an appropriate region is selected GDALWarpOperation::WarpRegion() + * is invoked to do the actual work. + * + * @param nDstXOff X offset to window of destination data to be produced. + * @param nDstYOff Y offset to window of destination data to be produced. + * @param nDstXSize Width of output window on destination file to be produced. + * @param nDstYSize Height of output window on destination file to be produced. + * + * @return CE_None on success or CE_Failure if an error occurs. + */ + +CPLErr GDALWarpOperation::ChunkAndWarpImage( + int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize ) + +{ +/* -------------------------------------------------------------------- */ +/* Collect the list of chunks to operate on. */ +/* -------------------------------------------------------------------- */ + WipeChunkList(); + CollectChunkList( nDstXOff, nDstYOff, nDstXSize, nDstYSize ); + + /* Sort chucks from top to bottom, and for equal y, from left to right */ + qsort(pasChunkList, nChunkListCount, sizeof(GDALWarpChunk), OrderWarpChunk); + +/* -------------------------------------------------------------------- */ +/* Total up output pixels to process. */ +/* -------------------------------------------------------------------- */ + int iChunk; + double dfTotalPixels = 0; + + for( iChunk = 0; iChunk < nChunkListCount; iChunk++ ) + { + GDALWarpChunk *pasThisChunk = pasChunkList + iChunk; + double dfChunkPixels = pasThisChunk->dsx * (double) pasThisChunk->dsy; + + dfTotalPixels += dfChunkPixels; + } + +/* -------------------------------------------------------------------- */ +/* Process them one at a time, updating the progress */ +/* information for each region. */ +/* -------------------------------------------------------------------- */ + double dfPixelsProcessed=0.0; + + for( iChunk = 0; iChunk < nChunkListCount; iChunk++ ) + { + GDALWarpChunk *pasThisChunk = pasChunkList + iChunk; + double dfChunkPixels = pasThisChunk->dsx * (double) pasThisChunk->dsy; + CPLErr eErr; + + double dfProgressBase = dfPixelsProcessed / dfTotalPixels; + double dfProgressScale = dfChunkPixels / dfTotalPixels; + + eErr = WarpRegion( pasThisChunk->dx, pasThisChunk->dy, + pasThisChunk->dsx, pasThisChunk->dsy, + pasThisChunk->sx, pasThisChunk->sy, + pasThisChunk->ssx, pasThisChunk->ssy, + pasThisChunk->sExtraSx, pasThisChunk->sExtraSy, + dfProgressBase, dfProgressScale); + + if( eErr != CE_None ) + return eErr; + + dfPixelsProcessed += dfChunkPixels; + } + + WipeChunkList(); + + psOptions->pfnProgress( 1.00001, "", psOptions->pProgressArg ); + + return CE_None; +} + +/************************************************************************/ +/* GDALChunkAndWarpImage() */ +/************************************************************************/ + +/** + * @see GDALWarpOperation::ChunkAndWarpImage() + */ + +CPLErr GDALChunkAndWarpImage( GDALWarpOperationH hOperation, + int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize ) +{ + VALIDATE_POINTER1( hOperation, "GDALChunkAndWarpImage", CE_Failure ); + + return ( (GDALWarpOperation *)hOperation )-> + ChunkAndWarpImage( nDstXOff, nDstYOff, nDstXSize, nDstYSize ); +} + +/************************************************************************/ +/* ChunkThreadMain() */ +/************************************************************************/ + +typedef struct +{ + GDALWarpOperation *poOperation; + GDALWarpChunk *pasChunkInfo; + CPLJoinableThread *hThreadHandle; + CPLErr eErr; + double dfProgressBase; + double dfProgressScale; + CPLMutex *hIOMutex; + + CPLMutex *hCondMutex; + int bIOMutexTaken; + CPLCond *hCond; +} ChunkThreadData; + + +static void ChunkThreadMain( void *pThreadData ) + +{ + volatile ChunkThreadData* psData = (volatile ChunkThreadData*) pThreadData; + + GDALWarpChunk *pasChunkInfo = psData->pasChunkInfo; + +/* -------------------------------------------------------------------- */ +/* Acquire IO mutex. */ +/* -------------------------------------------------------------------- */ + if( !CPLAcquireMutex( psData->hIOMutex, 600.0 ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to acquire IOMutex in WarpRegion()." ); + psData->eErr = CE_Failure; + } + else + { + if (psData->hCond != NULL) + { + CPLAcquireMutex( psData->hCondMutex, 1.0 ); + psData->bIOMutexTaken = TRUE; + CPLCondSignal(psData->hCond); + CPLReleaseMutex( psData->hCondMutex ); + } + + psData->eErr = psData->poOperation->WarpRegion( + pasChunkInfo->dx, pasChunkInfo->dy, + pasChunkInfo->dsx, pasChunkInfo->dsy, + pasChunkInfo->sx, pasChunkInfo->sy, + pasChunkInfo->ssx, pasChunkInfo->ssy, + pasChunkInfo->sExtraSx, pasChunkInfo->sExtraSy, + psData->dfProgressBase, + psData->dfProgressScale); + + /* -------------------------------------------------------------------- */ + /* Release the IO mutex. */ + /* -------------------------------------------------------------------- */ + CPLReleaseMutex( psData->hIOMutex ); + } +} + +/************************************************************************/ +/* ChunkAndWarpMulti() */ +/************************************************************************/ + +/** + * \fn CPLErr GDALWarpOperation::ChunkAndWarpMulti( + int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize ); + * + * This method does a complete warp of the source image to the destination + * image for the indicated region with the current warp options in effect. + * Progress is reported to the installed progress monitor, if any. + * + * Externally this method operates the same as ChunkAndWarpImage(), but + * internally this method uses multiple threads to interleave input/output + * for one region while the processing is being done for another. + * + * @param nDstXOff X offset to window of destination data to be produced. + * @param nDstYOff Y offset to window of destination data to be produced. + * @param nDstXSize Width of output window on destination file to be produced. + * @param nDstYSize Height of output window on destination file to be produced. + * + * @return CE_None on success or CE_Failure if an error occurs. + */ + +CPLErr GDALWarpOperation::ChunkAndWarpMulti( + int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize ) + +{ + hIOMutex = CPLCreateMutex(); + hWarpMutex = CPLCreateMutex(); + + CPLReleaseMutex( hIOMutex ); + CPLReleaseMutex( hWarpMutex ); + + CPLCond* hCond = CPLCreateCond(); + CPLMutex* hCondMutex = CPLCreateMutex(); + CPLReleaseMutex(hCondMutex); + +/* -------------------------------------------------------------------- */ +/* Collect the list of chunks to operate on. */ +/* -------------------------------------------------------------------- */ + WipeChunkList(); + CollectChunkList( nDstXOff, nDstYOff, nDstXSize, nDstYSize ); + + /* Sort chucks from top to bottom, and for equal y, from left to right */ + qsort(pasChunkList, nChunkListCount, sizeof(GDALWarpChunk), OrderWarpChunk); + +/* -------------------------------------------------------------------- */ +/* Process them one at a time, updating the progress */ +/* information for each region. */ +/* -------------------------------------------------------------------- */ + ChunkThreadData volatile asThreadData[2]; + memset((void*)&asThreadData, 0, sizeof(asThreadData)); + asThreadData[0].poOperation = this; + asThreadData[0].hIOMutex = hIOMutex; + asThreadData[1].poOperation = this; + asThreadData[1].hIOMutex = hIOMutex; + + int iChunk; + double dfPixelsProcessed=0.0, dfTotalPixels = nDstXSize*(double)nDstYSize; + + CPLErr eErr = CE_None; + for( iChunk = 0; iChunk < nChunkListCount+1; iChunk++ ) + { + int iThread = iChunk % 2; + +/* -------------------------------------------------------------------- */ +/* Launch thread for this chunk. */ +/* -------------------------------------------------------------------- */ + if( iChunk < nChunkListCount ) + { + GDALWarpChunk *pasThisChunk = pasChunkList + iChunk; + double dfChunkPixels = pasThisChunk->dsx * (double) pasThisChunk->dsy; + + asThreadData[iThread].dfProgressBase = dfPixelsProcessed / dfTotalPixels; + asThreadData[iThread].dfProgressScale = dfChunkPixels / dfTotalPixels; + + dfPixelsProcessed += dfChunkPixels; + + asThreadData[iThread].pasChunkInfo = pasThisChunk; + + if ( iChunk == 0 ) + { + asThreadData[iThread].hCond = hCond; + asThreadData[iThread].hCondMutex = hCondMutex; + } + else + { + asThreadData[iThread].hCond = NULL; + asThreadData[iThread].hCondMutex = NULL; + } + asThreadData[iThread].bIOMutexTaken = FALSE; + + CPLDebug( "GDAL", "Start chunk %d.", iChunk ); + asThreadData[iThread].hThreadHandle = + CPLCreateJoinableThread( ChunkThreadMain, (void*) &asThreadData[iThread] ); + if( asThreadData[iThread].hThreadHandle == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CPLCreateJoinableThread() failed in ChunkAndWarpMulti()" ); + eErr = CE_Failure; + break; + } + + /* Wait that the first thread has acquired the IO mutex before proceeding. */ + /* (This will ensure that the first thread will run before the second one). */ + if( iChunk == 0 ) + { + CPLAcquireMutex(hCondMutex, 1.0); + while (asThreadData[iThread].bIOMutexTaken == FALSE) + CPLCondWait(hCond, hCondMutex); + CPLReleaseMutex(hCondMutex); + } + } + + +/* -------------------------------------------------------------------- */ +/* Wait for previous chunks thread to complete. */ +/* -------------------------------------------------------------------- */ + if( iChunk > 0 ) + { + iThread = (iChunk-1) % 2; + + /* Wait for thread to finish. */ + CPLJoinThread(asThreadData[iThread].hThreadHandle); + asThreadData[iThread].hThreadHandle = NULL; + + CPLDebug( "GDAL", "Finished chunk %d.", iChunk-1 ); + + eErr = asThreadData[iThread].eErr; + + if( eErr != CE_None ) + break; + } + } + + /* -------------------------------------------------------------------- */ + /* Wait for all threads to complete. */ + /* -------------------------------------------------------------------- */ + int iThread; + for(iThread = 0; iThread < 2; iThread ++) + { + if (asThreadData[iThread].hThreadHandle) + CPLJoinThread(asThreadData[iThread].hThreadHandle); + } + + CPLDestroyCond(hCond); + CPLDestroyMutex(hCondMutex); + + WipeChunkList(); + + return eErr; +} + +/************************************************************************/ +/* GDALChunkAndWarpMulti() */ +/************************************************************************/ + +/** + * @see GDALWarpOperation::ChunkAndWarpMulti() + */ + +CPLErr GDALChunkAndWarpMulti( GDALWarpOperationH hOperation, + int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize ) +{ + VALIDATE_POINTER1( hOperation, "GDALChunkAndWarpMulti", CE_Failure ); + + return ( (GDALWarpOperation *)hOperation )-> + ChunkAndWarpMulti( nDstXOff, nDstYOff, nDstXSize, nDstYSize ); +} + +/************************************************************************/ +/* WipeChunkList() */ +/************************************************************************/ + +void GDALWarpOperation::WipeChunkList() + +{ + CPLFree( pasChunkList ); + pasChunkList = NULL; + nChunkListCount = 0; + nChunkListMax = 0; +} + +/************************************************************************/ +/* CollectChunkList() */ +/************************************************************************/ + +CPLErr GDALWarpOperation::CollectChunkList( + int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize ) + +{ +/* -------------------------------------------------------------------- */ +/* Compute the bounds of the input area corresponding to the */ +/* output area. */ +/* -------------------------------------------------------------------- */ + int nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize; + int nSrcXExtraSize, nSrcYExtraSize; + double dfSrcFillRatio; + CPLErr eErr; + + eErr = ComputeSourceWindow( nDstXOff, nDstYOff, nDstXSize, nDstYSize, + &nSrcXOff, &nSrcYOff, &nSrcXSize, &nSrcYSize, + &nSrcXExtraSize, &nSrcYExtraSize, &dfSrcFillRatio ); + + if( eErr != CE_None ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to compute source region for output window %d,%d,%d,%d, skipping.", + nDstXOff, nDstYOff, nDstXSize, nDstYSize ); + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* If we are allowed to drop no-source regons, do so now if */ +/* appropriate. */ +/* -------------------------------------------------------------------- */ + if( (nSrcXSize == 0 || nSrcYSize == 0) + && CSLFetchBoolean( psOptions->papszWarpOptions, "SKIP_NOSOURCE",0 )) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Based on the types of masks in use, how many bits will each */ +/* source pixel cost us? */ +/* -------------------------------------------------------------------- */ + int nSrcPixelCostInBits; + + nSrcPixelCostInBits = + GDALGetDataTypeSize( psOptions->eWorkingDataType ) + * psOptions->nBandCount; + + if( psOptions->pfnSrcDensityMaskFunc != NULL ) + nSrcPixelCostInBits += 32; /* ?? float mask */ + + GDALRasterBandH hSrcBand = NULL; + if( psOptions->nBandCount > 0 ) + hSrcBand = GDALGetRasterBand(psOptions->hSrcDS, + psOptions->panSrcBands[0]); + + if( psOptions->nSrcAlphaBand > 0 || psOptions->hCutline != NULL ) + nSrcPixelCostInBits += 32; /* UnifiedSrcDensity float mask */ + else if (hSrcBand != NULL && (GDALGetMaskFlags(hSrcBand) & GMF_PER_DATASET)) + nSrcPixelCostInBits += 1; /* UnifiedSrcValid bit mask */ + + if( psOptions->papfnSrcPerBandValidityMaskFunc != NULL + || psOptions->padfSrcNoDataReal != NULL ) + nSrcPixelCostInBits += psOptions->nBandCount; /* bit/band mask */ + + if( psOptions->pfnSrcValidityMaskFunc != NULL ) + nSrcPixelCostInBits += 1; /* bit mask */ + +/* -------------------------------------------------------------------- */ +/* What about the cost for the destination. */ +/* -------------------------------------------------------------------- */ + int nDstPixelCostInBits; + + nDstPixelCostInBits = + GDALGetDataTypeSize( psOptions->eWorkingDataType ) + * psOptions->nBandCount; + + if( psOptions->pfnDstDensityMaskFunc != NULL ) + nDstPixelCostInBits += 32; + + if( psOptions->padfDstNoDataReal != NULL + || psOptions->pfnDstValidityMaskFunc != NULL ) + nDstPixelCostInBits += psOptions->nBandCount; + + if( psOptions->nDstAlphaBand > 0 ) + nDstPixelCostInBits += 32; /* DstDensity float mask */ + +/* -------------------------------------------------------------------- */ +/* Does the cost of the current rectangle exceed our memory */ +/* limit? If so, split the destination along the longest */ +/* dimension and recurse. */ +/* -------------------------------------------------------------------- */ + double dfTotalMemoryUse; + + dfTotalMemoryUse = + (((double) nSrcPixelCostInBits) * nSrcXSize * nSrcYSize + + ((double) nDstPixelCostInBits) * nDstXSize * nDstYSize) / 8.0; + + + int nBlockXSize = 1, nBlockYSize = 1; + if (psOptions->hDstDS) + { + GDALGetBlockSize(GDALGetRasterBand(psOptions->hDstDS, 1), + &nBlockXSize, &nBlockYSize); + } + + // If size of working buffers need exceed the allow limit, then divide + // the target area + // Do it also if the "fill ratio" of the source is too low (#3120), but + // only if there's at least some source pixel intersecting. The + // SRC_FILL_RATIO_HEURISTICS warping option is undocumented and only here + // in case the heuristics would cause issues. + /*CPLDebug("WARP", "dst=(%d,%d,%d,%d) src=(%d,%d,%d,%d) srcfillratio=%.18g", + nDstXOff, nDstYOff, nDstXSize, nDstYSize, + nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize, dfSrcFillRatio);*/ + if( (dfTotalMemoryUse > psOptions->dfWarpMemoryLimit && (nDstXSize > 2 || nDstYSize > 2)) || + (dfSrcFillRatio > 0 && dfSrcFillRatio < 0.5 && (nDstXSize > 100 || nDstYSize > 100) && + CSLFetchBoolean( psOptions->papszWarpOptions, "SRC_FILL_RATIO_HEURISTICS", TRUE )) ) + { + CPLErr eErr2 = CE_None; + + int bStreamableOutput = + CSLFetchBoolean( psOptions->papszWarpOptions, "STREAMABLE_OUTPUT", FALSE ); + int bOptimizeSize = !bStreamableOutput && + CSLFetchBoolean( psOptions->papszWarpOptions, "OPTIMIZE_SIZE", FALSE ); + + /* If the region width is greater than the region height, */ + /* cut in half in the width. When we want to optimize the size */ + /* of a compressed output dataset, do this only if each half part */ + /* is at least as wide as the block width */ + int bHasDivided = FALSE; + if( nDstXSize > nDstYSize && + ((!bOptimizeSize && !bStreamableOutput) || + (bOptimizeSize && (nDstXSize / 2 >= nBlockXSize || nDstYSize == 1)) || + (bStreamableOutput && nDstXSize / 2 >= nBlockXSize && nDstYSize == nBlockYSize)) ) + { + bHasDivided = TRUE; + int nChunk1 = nDstXSize / 2; + + /* In the optimize size case, try to stick on target block boundaries */ + if ((bOptimizeSize || bStreamableOutput) && nChunk1 > nBlockXSize) + nChunk1 = (nChunk1 / nBlockXSize) * nBlockXSize; + + int nChunk2 = nDstXSize - nChunk1; + + eErr = CollectChunkList( nDstXOff, nDstYOff, + nChunk1, nDstYSize ); + + eErr2 = CollectChunkList( nDstXOff+nChunk1, nDstYOff, + nChunk2, nDstYSize ); + } + else if( !(bStreamableOutput && nDstYSize / 2 < nBlockYSize) ) + { + bHasDivided = TRUE; + int nChunk1 = nDstYSize / 2; + + /* In the optimize size case, try to stick on target block boundaries */ + if ((bOptimizeSize || bStreamableOutput) && nChunk1 > nBlockYSize) + nChunk1 = (nChunk1 / nBlockYSize) * nBlockYSize; + + int nChunk2 = nDstYSize - nChunk1; + + eErr = CollectChunkList( nDstXOff, nDstYOff, + nDstXSize, nChunk1 ); + + eErr2 = CollectChunkList( nDstXOff, nDstYOff+nChunk1, + nDstXSize, nChunk2 ); + } + + if( bHasDivided ) + { + if( eErr == CE_None ) + return eErr2; + else + return eErr; + } + } + +/* -------------------------------------------------------------------- */ +/* OK, everything fits, so add to the chunk list. */ +/* -------------------------------------------------------------------- */ + if( nChunkListCount == nChunkListMax ) + { + nChunkListMax = nChunkListMax * 2 + 1; + pasChunkList = (GDALWarpChunk *) + CPLRealloc(pasChunkList,sizeof(GDALWarpChunk)*nChunkListMax ); + } + + pasChunkList[nChunkListCount].dx = nDstXOff; + pasChunkList[nChunkListCount].dy = nDstYOff; + pasChunkList[nChunkListCount].dsx = nDstXSize; + pasChunkList[nChunkListCount].dsy = nDstYSize; + pasChunkList[nChunkListCount].sx = nSrcXOff; + pasChunkList[nChunkListCount].sy = nSrcYOff; + pasChunkList[nChunkListCount].ssx = nSrcXSize; + pasChunkList[nChunkListCount].ssy = nSrcYSize; + pasChunkList[nChunkListCount].sExtraSx = nSrcXExtraSize; + pasChunkList[nChunkListCount].sExtraSy = nSrcYExtraSize; + + nChunkListCount++; + + return CE_None; +} + + +/************************************************************************/ +/* WarpRegion() */ +/************************************************************************/ + +/** + * \fn CPLErr GDALWarpOperation::WarpRegion(int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + int nSrcXOff=0, int nSrcYOff=0, + int nSrcXSize=0, int nSrcYSize=0, + double dfProgressBase = 0, + double dfProgressScale = 1); + * + * This method requests the indicated region of the output file be generated. + * + * Note that WarpRegion() will produce the requested area in one low level warp + * operation without verifying that this does not exceed the stated memory + * limits for the warp operation. Applications should take care not to call + * WarpRegion() on too large a region! This function + * is normally called by ChunkAndWarpImage(), the normal entry point for + * applications. Use it instead if staying within memory constraints is + * desired. + * + * Progress is reported from dfProgressBase to dfProgressBase + dfProgressScale + * for the indicated region. + * + * @param nDstXOff X offset to window of destination data to be produced. + * @param nDstYOff Y offset to window of destination data to be produced. + * @param nDstXSize Width of output window on destination file to be produced. + * @param nDstYSize Height of output window on destination file to be produced. + * @param nSrcXOff source window X offset (computed if window all zero) + * @param nSrcYOff source window Y offset (computed if window all zero) + * @param nSrcXSize source window X size (computed if window all zero) + * @param nSrcYSize source window Y size (computed if window all zero) + * @param dfProgressBase minimum progress value reported + * @param dfProgressScale value such as dfProgressBase + dfProgressScale is the + * maximum progress value reported + * + * @return CE_None on success or CE_Failure if an error occurs. + */ + +CPLErr GDALWarpOperation::WarpRegion( int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + int nSrcXOff, int nSrcYOff, + int nSrcXSize, int nSrcYSize, + double dfProgressBase, + double dfProgressScale) +{ + return WarpRegion(nDstXOff, nDstYOff, + nDstXSize, nDstYSize, + nSrcXOff, nSrcYOff, + nSrcXSize, nSrcYSize, + 0, 0, + dfProgressBase, dfProgressScale); +} + +CPLErr GDALWarpOperation::WarpRegion( int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + int nSrcXOff, int nSrcYOff, + int nSrcXSize, int nSrcYSize, + int nSrcXExtraSize, int nSrcYExtraSize, + double dfProgressBase, + double dfProgressScale) + +{ + CPLErr eErr; + int iBand; + + ReportTiming( NULL ); + +/* -------------------------------------------------------------------- */ +/* Allocate the output buffer. */ +/* -------------------------------------------------------------------- */ + void *pDstBuffer; + int nWordSize = GDALGetDataTypeSize(psOptions->eWorkingDataType)/8; + int nBandSize = nWordSize * nDstXSize * nDstYSize; + + if (nDstXSize > INT_MAX / nDstYSize || + nDstXSize * nDstYSize > INT_MAX / (nWordSize * psOptions->nBandCount)) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Integer overflow : nDstXSize=%d, nDstYSize=%d", + nDstXSize, nDstYSize); + return CE_Failure; + } + + pDstBuffer = VSIMalloc( nBandSize * psOptions->nBandCount ); + if( pDstBuffer == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating %d byte destination buffer.", + nBandSize * psOptions->nBandCount ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* If the INIT_DEST option is given the initialize the output */ +/* destination buffer to the indicated value without reading it */ +/* from the hDstDS. This is sometimes used to optimize */ +/* operation to a new output file ... it doesn't have to */ +/* written out and read back for nothing. */ +/* NOTE:The following code is 99% similar in gdalwarpoperation.cpp and */ +/* vrtwarped.cpp. Be careful to keep it in sync ! */ +/* -------------------------------------------------------------------- */ + const char *pszInitDest = CSLFetchNameValue( psOptions->papszWarpOptions, + "INIT_DEST" ); + + if( pszInitDest != NULL && !EQUAL(pszInitDest, "") ) + { + char **papszInitValues = + CSLTokenizeStringComplex( pszInitDest, ",", FALSE, FALSE ); + int nInitCount = CSLCount(papszInitValues); + + for( iBand = 0; iBand < psOptions->nBandCount; iBand++ ) + { + double adfInitRealImag[2]; + GByte *pBandData; + const char *pszBandInit = papszInitValues[MIN(iBand,nInitCount-1)]; + + if( EQUAL(pszBandInit,"NO_DATA") + && psOptions->padfDstNoDataReal != NULL ) + { + adfInitRealImag[0] = psOptions->padfDstNoDataReal[iBand]; + adfInitRealImag[1] = psOptions->padfDstNoDataImag[iBand]; + } + else + { + CPLStringToComplex( pszBandInit, + adfInitRealImag + 0, adfInitRealImag + 1); + } + + pBandData = ((GByte *) pDstBuffer) + iBand * nBandSize; + + if( psOptions->eWorkingDataType == GDT_Byte ) + memset( pBandData, + MAX(0,MIN(255,(int)adfInitRealImag[0])), + nBandSize); + else if( !CPLIsNan(adfInitRealImag[0]) && adfInitRealImag[0] == 0.0 && + !CPLIsNan(adfInitRealImag[1]) && adfInitRealImag[1] == 0.0 ) + { + memset( pBandData, 0, nBandSize ); + } + else if( !CPLIsNan(adfInitRealImag[1]) && adfInitRealImag[1] == 0.0 ) + { + GDALCopyWords( &adfInitRealImag, GDT_Float64, 0, + pBandData,psOptions->eWorkingDataType,nWordSize, + nDstXSize * nDstYSize ); + } + else + { + GDALCopyWords( &adfInitRealImag, GDT_CFloat64, 0, + pBandData,psOptions->eWorkingDataType,nWordSize, + nDstXSize * nDstYSize ); + } + } + + CSLDestroy( papszInitValues ); + } + +/* -------------------------------------------------------------------- */ +/* If we aren't doing fixed initialization of the output buffer */ +/* then read it from disk so we can overlay on existing imagery. */ +/* -------------------------------------------------------------------- */ + if( pszInitDest == NULL ) + { + eErr = GDALDatasetRasterIO( psOptions->hDstDS, GF_Read, + nDstXOff, nDstYOff, nDstXSize, nDstYSize, + pDstBuffer, nDstXSize, nDstYSize, + psOptions->eWorkingDataType, + psOptions->nBandCount, + psOptions->panDstBands, + 0, 0, 0 ); + + if( eErr != CE_None ) + { + CPLFree( pDstBuffer ); + return eErr; + } + + ReportTiming( "Output buffer read" ); + } + +/* -------------------------------------------------------------------- */ +/* Perform the warp. */ +/* -------------------------------------------------------------------- */ + eErr = WarpRegionToBuffer( nDstXOff, nDstYOff, nDstXSize, nDstYSize, + pDstBuffer, psOptions->eWorkingDataType, + nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize, + nSrcXExtraSize, nSrcYExtraSize, + dfProgressBase, dfProgressScale); + +/* -------------------------------------------------------------------- */ +/* Write the output data back to disk if all went well. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None ) + { + eErr = GDALDatasetRasterIO( psOptions->hDstDS, GF_Write, + nDstXOff, nDstYOff, nDstXSize, nDstYSize, + pDstBuffer, nDstXSize, nDstYSize, + psOptions->eWorkingDataType, + psOptions->nBandCount, + psOptions->panDstBands, + 0, 0, 0 ); + if( eErr == CE_None && + CSLFetchBoolean( psOptions->papszWarpOptions, "WRITE_FLUSH", + FALSE ) ) + { + CPLErr eOldErr = CPLGetLastErrorType(); + CPLString osLastErrMsg = CPLGetLastErrorMsg(); + GDALFlushCache( psOptions->hDstDS ); + CPLErr eNewErr = CPLGetLastErrorType(); + if (eNewErr != eOldErr || osLastErrMsg.compare(CPLGetLastErrorMsg()) != 0) + eErr = CE_Failure; + } + ReportTiming( "Output buffer write" ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup and return. */ +/* -------------------------------------------------------------------- */ + VSIFree( pDstBuffer ); + + return eErr; +} + +/************************************************************************/ +/* GDALWarpRegion() */ +/************************************************************************/ + +/** + * @see GDALWarpOperation::WarpRegion() + */ + +CPLErr GDALWarpRegion( GDALWarpOperationH hOperation, + int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + int nSrcXOff, int nSrcYOff, + int nSrcXSize, int nSrcYSize ) + +{ + VALIDATE_POINTER1( hOperation, "GDALWarpRegion", CE_Failure ); + + return ( (GDALWarpOperation *)hOperation )-> + WarpRegion( nDstXOff, nDstYOff, nDstXSize, nDstYSize, + nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize); +} + +/************************************************************************/ +/* WarpRegionToBuffer() */ +/************************************************************************/ + +/** + * \fn CPLErr GDALWarpOperation::WarpRegionToBuffer( + int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + void *pDataBuf, + GDALDataType eBufDataType, + int nSrcXOff=0, int nSrcYOff=0, + int nSrcXSize=0, int nSrcYSize=0, + double dfProgressBase = 0, + double dfProgressScale = 1 ); + * + * This method requests that a particular window of the output dataset + * be warped and the result put into the provided data buffer. The output + * dataset doesn't even really have to exist to use this method as long as + * the transformation function in the GDALWarpOptions is setup to map to + * a virtual pixel/line space. + * + * This method will do the whole region in one chunk, so be wary of the + * amount of memory that might be used. + * + * @param nDstXOff X offset to window of destination data to be produced. + * @param nDstYOff Y offset to window of destination data to be produced. + * @param nDstXSize Width of output window on destination file to be produced. + * @param nDstYSize Height of output window on destination file to be produced. + * @param pDataBuf the data buffer to place result in, of type eBufDataType. + * @param eBufDataType the type of the output data buffer. For now this + * must match GDALWarpOptions::eWorkingDataType. + * @param nSrcXOff source window X offset (computed if window all zero) + * @param nSrcYOff source window Y offset (computed if window all zero) + * @param nSrcXSize source window X size (computed if window all zero) + * @param nSrcYSize source window Y size (computed if window all zero) + * @param dfProgressBase minimum progress value reported + * @param dfProgressScale value such as dfProgressBase + dfProgressScale is the + * maximum progress value reported + * + * @return CE_None on success or CE_Failure if an error occurs. + */ + +CPLErr GDALWarpOperation::WarpRegionToBuffer( + int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, + void *pDataBuf, GDALDataType eBufDataType, + int nSrcXOff, int nSrcYOff, int nSrcXSize, int nSrcYSize, + double dfProgressBase, double dfProgressScale) +{ + return WarpRegionToBuffer(nDstXOff, nDstYOff, nDstXSize, nDstYSize, + pDataBuf, eBufDataType, + nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize, 0, 0, + dfProgressBase, dfProgressScale); +} + +CPLErr GDALWarpOperation::WarpRegionToBuffer( + int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, + void *pDataBuf, GDALDataType eBufDataType, + int nSrcXOff, int nSrcYOff, int nSrcXSize, int nSrcYSize, + int nSrcXExtraSize, int nSrcYExtraSize, + double dfProgressBase, double dfProgressScale) + +{ + CPLErr eErr = CE_None; + int i; + int nWordSize = GDALGetDataTypeSize(psOptions->eWorkingDataType)/8; + + (void) eBufDataType; + CPLAssert( eBufDataType == psOptions->eWorkingDataType ); + +/* -------------------------------------------------------------------- */ +/* If not given a corresponding source window compute one now. */ +/* -------------------------------------------------------------------- */ + if( nSrcXSize == 0 && nSrcYSize == 0 ) + { + eErr = ComputeSourceWindow( nDstXOff, nDstYOff, nDstXSize, nDstYSize, + &nSrcXOff, &nSrcYOff, + &nSrcXSize, &nSrcYSize, + &nSrcXExtraSize, &nSrcYExtraSize, NULL ); + + if( eErr != CE_None ) + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Prepare a WarpKernel object to match this operation. */ +/* -------------------------------------------------------------------- */ + GDALWarpKernel oWK; + + oWK.eResample = psOptions->eResampleAlg; + oWK.nBands = psOptions->nBandCount; + oWK.eWorkingDataType = psOptions->eWorkingDataType; + + oWK.pfnTransformer = psOptions->pfnTransformer; + oWK.pTransformerArg = psOptions->pTransformerArg; + + oWK.pfnProgress = psOptions->pfnProgress; + oWK.pProgress = psOptions->pProgressArg; + oWK.dfProgressBase = dfProgressBase; + oWK.dfProgressScale = dfProgressScale; + + oWK.papszWarpOptions = psOptions->papszWarpOptions; + + oWK.padfDstNoDataReal = psOptions->padfDstNoDataReal; + +/* -------------------------------------------------------------------- */ +/* Setup the source buffer. */ +/* */ +/* Eventually we may need to take advantage of pixel */ +/* interleaved reading here. */ +/* -------------------------------------------------------------------- */ + oWK.nSrcXOff = nSrcXOff; + oWK.nSrcYOff = nSrcYOff; + oWK.nSrcXSize = nSrcXSize; + oWK.nSrcYSize = nSrcYSize; + oWK.nSrcXExtraSize = nSrcXExtraSize; + oWK.nSrcYExtraSize = nSrcYExtraSize; + + if (nSrcXSize != 0 && nSrcYSize != 0 && + (nSrcXSize > INT_MAX / nSrcYSize || + nSrcXSize * nSrcYSize > INT_MAX / (nWordSize * psOptions->nBandCount) - WARP_EXTRA_ELTS)) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Integer overflow : nSrcXSize=%d, nSrcYSize=%d", + nSrcXSize, nSrcYSize); + return CE_Failure; + } + + oWK.papabySrcImage = (GByte **) + CPLCalloc(sizeof(GByte*),psOptions->nBandCount); + oWK.papabySrcImage[0] = (GByte *) + VSIMalloc( nWordSize * (nSrcXSize * nSrcYSize + WARP_EXTRA_ELTS) * psOptions->nBandCount ); + + if( nSrcXSize != 0 && nSrcYSize != 0 && oWK.papabySrcImage[0] == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Failed to allocate %d byte source buffer.", + nWordSize * (nSrcXSize * nSrcYSize + WARP_EXTRA_ELTS) * psOptions->nBandCount ); + eErr = CE_Failure; + } + + for( i = 0; i < psOptions->nBandCount && eErr == CE_None; i++ ) + oWK.papabySrcImage[i] = ((GByte *) oWK.papabySrcImage[0]) + + nWordSize * (nSrcXSize * nSrcYSize + WARP_EXTRA_ELTS) * i; + + if( eErr == CE_None && nSrcXSize > 0 && nSrcYSize > 0 ) + eErr = + GDALDatasetRasterIO( psOptions->hSrcDS, GF_Read, + nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize, + oWK.papabySrcImage[0], nSrcXSize, nSrcYSize, + psOptions->eWorkingDataType, + psOptions->nBandCount, psOptions->panSrcBands, + 0, 0, nWordSize * (nSrcXSize * nSrcYSize + WARP_EXTRA_ELTS) ); + + ReportTiming( "Input buffer read" ); + +/* -------------------------------------------------------------------- */ +/* Initialize destination buffer. */ +/* -------------------------------------------------------------------- */ + oWK.nDstXOff = nDstXOff; + oWK.nDstYOff = nDstYOff; + oWK.nDstXSize = nDstXSize; + oWK.nDstYSize = nDstYSize; + + oWK.papabyDstImage = (GByte **) + CPLCalloc(sizeof(GByte*),psOptions->nBandCount); + + for( i = 0; i < psOptions->nBandCount && eErr == CE_None; i++ ) + { + oWK.papabyDstImage[i] = ((GByte *) pDataBuf) + + i * nDstXSize * nDstYSize * nWordSize; + } + +/* -------------------------------------------------------------------- */ +/* Eventually we need handling for a whole bunch of the */ +/* validity and density masks here. */ +/* -------------------------------------------------------------------- */ + + /* TODO */ + +/* -------------------------------------------------------------------- */ +/* Generate a source density mask if we have a source alpha band */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && psOptions->nSrcAlphaBand > 0 && + nSrcXSize > 0 && nSrcYSize > 0 ) + { + CPLAssert( oWK.pafUnifiedSrcDensity == NULL ); + + eErr = CreateKernelMask( &oWK, 0, "UnifiedSrcDensity" ); + + if( eErr == CE_None ) + { + int bOutAllOpaque = FALSE; + eErr = + GDALWarpSrcAlphaMasker( psOptions, + psOptions->nBandCount, + psOptions->eWorkingDataType, + oWK.nSrcXOff, oWK.nSrcYOff, + oWK.nSrcXSize, oWK.nSrcYSize, + oWK.papabySrcImage, + TRUE, oWK.pafUnifiedSrcDensity, + &bOutAllOpaque ); + if( bOutAllOpaque ) + { + //CPLDebug("WARP", "No need for a source density mask as all values are opaque"); + CPLFree(oWK.pafUnifiedSrcDensity); + oWK.pafUnifiedSrcDensity = NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Generate a source density mask if we have a source cutline. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && psOptions->hCutline != NULL && + nSrcXSize > 0 && nSrcYSize > 0 ) + { + if( oWK.pafUnifiedSrcDensity == NULL ) + { + int j = oWK.nSrcXSize * oWK.nSrcYSize; + + eErr = CreateKernelMask( &oWK, 0, "UnifiedSrcDensity" ); + + if( eErr == CE_None ) + { + for( j = oWK.nSrcXSize * oWK.nSrcYSize - 1; j >= 0; j-- ) + oWK.pafUnifiedSrcDensity[j] = 1.0; + } + } + + if( eErr == CE_None ) + eErr = + GDALWarpCutlineMasker( psOptions, + psOptions->nBandCount, + psOptions->eWorkingDataType, + oWK.nSrcXOff, oWK.nSrcYOff, + oWK.nSrcXSize, oWK.nSrcYSize, + oWK.papabySrcImage, + TRUE, oWK.pafUnifiedSrcDensity ); + } + +/* -------------------------------------------------------------------- */ +/* Generate a destination density mask if we have a destination */ +/* alpha band. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && psOptions->nDstAlphaBand > 0 ) + { + CPLAssert( oWK.pafDstDensity == NULL ); + + eErr = CreateKernelMask( &oWK, i, "DstDensity" ); + + if( eErr == CE_None ) + eErr = + GDALWarpDstAlphaMasker( psOptions, + psOptions->nBandCount, + psOptions->eWorkingDataType, + oWK.nDstXOff, oWK.nDstYOff, + oWK.nDstXSize, oWK.nDstYSize, + oWK.papabyDstImage, + TRUE, oWK.pafDstDensity ); + } + +/* -------------------------------------------------------------------- */ +/* If we have source nodata values create the validity mask. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && psOptions->padfSrcNoDataReal != NULL && + nSrcXSize > 0 && nSrcYSize > 0 ) + { + CPLAssert( oWK.papanBandSrcValid == NULL ); + + int bAllBandsAllValid = TRUE; + for( i = 0; i < psOptions->nBandCount && eErr == CE_None; i++ ) + { + eErr = CreateKernelMask( &oWK, i, "BandSrcValid" ); + if( eErr == CE_None ) + { + double adfNoData[2]; + + adfNoData[0] = psOptions->padfSrcNoDataReal[i]; + adfNoData[1] = psOptions->padfSrcNoDataImag[i]; + + int bAllValid = FALSE; + eErr = + GDALWarpNoDataMasker( adfNoData, 1, + psOptions->eWorkingDataType, + oWK.nSrcXOff, oWK.nSrcYOff, + oWK.nSrcXSize, oWK.nSrcYSize, + &(oWK.papabySrcImage[i]), + FALSE, oWK.papanBandSrcValid[i], + &bAllValid ); + if( !bAllValid ) + bAllBandsAllValid = FALSE; + } + } + + /* Optimization: if all pixels in all bands are valid, */ + /* we don't need a mask */ + if( bAllBandsAllValid ) + { + //CPLDebug("WARP", "No need for a source nodata mask as all values are valid"); + for( i = 0; i < oWK.nBands; i++ ) + CPLFree( oWK.papanBandSrcValid[i] ); + CPLFree( oWK.papanBandSrcValid ); + oWK.papanBandSrcValid = NULL; + } + +/* -------------------------------------------------------------------- */ +/* If UNIFIED_SRC_NODATA is set, then compute a unified input */ +/* pixel mask if and only if all bands nodata is true. That */ +/* is, we only treat a pixel as nodata if all bands match their */ +/* respective nodata values. */ +/* -------------------------------------------------------------------- */ + if( oWK.papanBandSrcValid != NULL && + CSLFetchBoolean( psOptions->papszWarpOptions, "UNIFIED_SRC_NODATA", + FALSE ) + && eErr == CE_None ) + { + int nBytesInMask = (oWK.nSrcXSize * oWK.nSrcYSize + 31) / 8; + int iWord; + + eErr = CreateKernelMask( &oWK, i, "UnifiedSrcValid" ); + + if( eErr == CE_None ) + { + memset( oWK.panUnifiedSrcValid, 0, nBytesInMask ); + + for( i = 0; i < psOptions->nBandCount; i++ ) + { + for( iWord = nBytesInMask/4 - 1; iWord >= 0; iWord-- ) + oWK.panUnifiedSrcValid[iWord] |= + oWK.papanBandSrcValid[i][iWord]; + CPLFree( oWK.papanBandSrcValid[i] ); + oWK.papanBandSrcValid[i] = NULL; + } + + CPLFree( oWK.papanBandSrcValid ); + oWK.papanBandSrcValid = NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Generate a source validity mask if we have a source mask for */ +/* the whole input dataset (and didn't already treat it as */ +/* alpha band). */ +/* -------------------------------------------------------------------- */ + GDALRasterBandH hSrcBand = NULL; + if( psOptions->nBandCount > 0 ) + hSrcBand = GDALGetRasterBand(psOptions->hSrcDS, + psOptions->panSrcBands[0]); + + if( eErr == CE_None + && oWK.pafUnifiedSrcDensity == NULL + && (GDALGetMaskFlags(hSrcBand) & GMF_PER_DATASET) && + nSrcXSize > 0 && nSrcYSize > 0 ) + + { + eErr = CreateKernelMask( &oWK, 0, "UnifiedSrcValid" ); + + if( eErr == CE_None ) + eErr = + GDALWarpSrcMaskMasker( psOptions, + psOptions->nBandCount, + psOptions->eWorkingDataType, + oWK.nSrcXOff, oWK.nSrcYOff, + oWK.nSrcXSize, oWK.nSrcYSize, + oWK.papabySrcImage, + FALSE, oWK.panUnifiedSrcValid ); + } + +/* -------------------------------------------------------------------- */ +/* If we have destination nodata values create the */ +/* validity mask. We set the DstValid for any pixel that we */ +/* do no have valid data in *any* of the source bands. */ +/* */ +/* Note that we don't support any concept of unified nodata on */ +/* the destination image. At some point that should be added */ +/* and then this logic will be significantly different. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && psOptions->padfDstNoDataReal != NULL ) + { + CPLAssert( oWK.panDstValid == NULL ); + + GUInt32 *panBandMask = NULL; + int nMaskWords = (oWK.nDstXSize * oWK.nDstYSize + 31)/32; + + eErr = CreateKernelMask( &oWK, 0, "DstValid" ); + if( eErr == CE_None ) + { + panBandMask = (GUInt32 *) CPLMalloc(nMaskWords*4); + } + + if( eErr == CE_None && panBandMask != NULL ) + { + int iBand, iWord; + + for( iBand = 0; iBand < psOptions->nBandCount; iBand++ ) + { + double adfNoData[2]; + + memset( panBandMask, 0xff, nMaskWords * 4 ); + + adfNoData[0] = psOptions->padfDstNoDataReal[iBand]; + adfNoData[1] = psOptions->padfDstNoDataImag[iBand]; + + int bAllValid = FALSE; + eErr = + GDALWarpNoDataMasker( adfNoData, 1, + psOptions->eWorkingDataType, + oWK.nDstXOff, oWK.nDstYOff, + oWK.nDstXSize, oWK.nDstYSize, + oWK.papabyDstImage + iBand, + FALSE, panBandMask, + &bAllValid ); + + /* Optimization: if there's a single band and all pixels are */ + /* valid then we don't need a mask */ + if( bAllValid && psOptions->nBandCount == 1 ) + { + //CPLDebug("WARP", "No need for a destination nodata mask as all values are valid"); + CPLFree(oWK.panDstValid); + oWK.panDstValid = NULL; + break; + } + + for( iWord = nMaskWords - 1; iWord >= 0; iWord-- ) + oWK.panDstValid[iWord] |= panBandMask[iWord]; + } + CPLFree( panBandMask ); + } + } + +/* -------------------------------------------------------------------- */ +/* Release IO Mutex, and acquire warper mutex. */ +/* -------------------------------------------------------------------- */ + if( hIOMutex != NULL ) + { + CPLReleaseMutex( hIOMutex ); + if( !CPLAcquireMutex( hWarpMutex, 600.0 ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to acquire WarpMutex in WarpRegion()." ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Optional application provided prewarp chunk processor. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && psOptions->pfnPreWarpChunkProcessor != NULL ) + eErr = psOptions->pfnPreWarpChunkProcessor( + (void *) &oWK, psOptions->pPreWarpProcessorArg ); + +/* -------------------------------------------------------------------- */ +/* Perform the warp. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None ) + { + eErr = oWK.PerformWarp(); + ReportTiming( "In memory warp operation" ); + } + +/* -------------------------------------------------------------------- */ +/* Optional application provided postwarp chunk processor. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && psOptions->pfnPostWarpChunkProcessor != NULL ) + eErr = psOptions->pfnPostWarpChunkProcessor( + (void *) &oWK, psOptions->pPostWarpProcessorArg ); + +/* -------------------------------------------------------------------- */ +/* Release Warp Mutex, and acquire io mutex. */ +/* -------------------------------------------------------------------- */ + if( hIOMutex != NULL ) + { + CPLReleaseMutex( hWarpMutex ); + if( !CPLAcquireMutex( hIOMutex, 600.0 ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to acquire IOMutex in WarpRegion()." ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Write destination alpha if available. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && psOptions->nDstAlphaBand > 0 ) + { + eErr = + GDALWarpDstAlphaMasker( psOptions, + -psOptions->nBandCount, + psOptions->eWorkingDataType, + oWK.nDstXOff, oWK.nDstYOff, + oWK.nDstXSize, oWK.nDstYSize, + oWK.papabyDstImage, + TRUE, oWK.pafDstDensity ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + CPLFree( oWK.papabySrcImage[0] ); + CPLFree( oWK.papabySrcImage ); + CPLFree( oWK.papabyDstImage ); + + if( oWK.papanBandSrcValid != NULL ) + { + for( i = 0; i < oWK.nBands; i++ ) + CPLFree( oWK.papanBandSrcValid[i] ); + CPLFree( oWK.papanBandSrcValid ); + } + CPLFree( oWK.panUnifiedSrcValid ); + CPLFree( oWK.pafUnifiedSrcDensity ); + CPLFree( oWK.panDstValid ); + CPLFree( oWK.pafDstDensity ); + + return eErr; +} + +/************************************************************************/ +/* GDALWarpRegionToBuffer() */ +/************************************************************************/ + +/** + * @see GDALWarpOperation::WarpRegionToBuffer() + */ + +CPLErr GDALWarpRegionToBuffer( GDALWarpOperationH hOperation, + int nDstXOff, int nDstYOff, int nDstXSize, int nDstYSize, + void *pDataBuf, GDALDataType eBufDataType, + int nSrcXOff, int nSrcYOff, int nSrcXSize, int nSrcYSize ) + +{ + VALIDATE_POINTER1( hOperation, "GDALWarpRegionToBuffer", CE_Failure ); + + return ( (GDALWarpOperation *)hOperation )-> + WarpRegionToBuffer( nDstXOff, nDstYOff, nDstXSize, nDstYSize, + pDataBuf, eBufDataType, + nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize ); +} + +/************************************************************************/ +/* CreateKernelMask() */ +/* */ +/* If mask does not yet exist, create it. Supported types are */ +/* the name of the variable in question. That is */ +/* "BandSrcValid", "UnifiedSrcValid", "UnifiedSrcDensity", */ +/* "DstValid", and "DstDensity". */ +/************************************************************************/ + +CPLErr GDALWarpOperation::CreateKernelMask( GDALWarpKernel *poKernel, + int iBand, const char *pszType ) + +{ + void **ppMask; + int nXSize, nYSize, nBitsPerPixel, nDefault; + int nExtraElts = 0; + +/* -------------------------------------------------------------------- */ +/* Get particulars of mask to be updated. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszType,"BandSrcValid") ) + { + if( poKernel->papanBandSrcValid == NULL ) + poKernel->papanBandSrcValid = (GUInt32 **) + CPLCalloc( sizeof(void*),poKernel->nBands); + + ppMask = (void **) &(poKernel->papanBandSrcValid[iBand]); + nExtraElts = WARP_EXTRA_ELTS; + nXSize = poKernel->nSrcXSize; + nYSize = poKernel->nSrcYSize; + nBitsPerPixel = 1; + nDefault = 0xff; + } + else if( EQUAL(pszType,"UnifiedSrcValid") ) + { + ppMask = (void **) &(poKernel->panUnifiedSrcValid); + nExtraElts = WARP_EXTRA_ELTS; + nXSize = poKernel->nSrcXSize; + nYSize = poKernel->nSrcYSize; + nBitsPerPixel = 1; + nDefault = 0xff; + } + else if( EQUAL(pszType,"UnifiedSrcDensity") ) + { + ppMask = (void **) &(poKernel->pafUnifiedSrcDensity); + nExtraElts = WARP_EXTRA_ELTS; + nXSize = poKernel->nSrcXSize; + nYSize = poKernel->nSrcYSize; + nBitsPerPixel = 32; + nDefault = 0; + } + else if( EQUAL(pszType,"DstValid") ) + { + ppMask = (void **) &(poKernel->panDstValid); + nXSize = poKernel->nDstXSize; + nYSize = poKernel->nDstYSize; + nBitsPerPixel = 1; + nDefault = 0; + } + else if( EQUAL(pszType,"DstDensity") ) + { + ppMask = (void **) &(poKernel->pafDstDensity); + nXSize = poKernel->nDstXSize; + nYSize = poKernel->nDstYSize; + nBitsPerPixel = 32; + nDefault = 0; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Internal error in CreateKernelMask(%s).", + pszType ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Allocate if needed. */ +/* -------------------------------------------------------------------- */ + if( *ppMask == NULL ) + { + int nBytes; + + if( nBitsPerPixel == 32 ) + nBytes = (nXSize * nYSize + nExtraElts) * 4; + else + nBytes = (nXSize * nYSize + nExtraElts + 31) / 8; + + *ppMask = VSIMalloc( nBytes ); + + if( *ppMask == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating %d bytes for %s mask.", + nBytes, pszType ); + return CE_Failure; + } + + memset( *ppMask, nDefault, nBytes ); + } + + return CE_None; +} + + + +/************************************************************************/ +/* ComputeSourceWindow() */ +/************************************************************************/ + +CPLErr GDALWarpOperation::ComputeSourceWindow(int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + int *pnSrcXOff, int *pnSrcYOff, + int *pnSrcXSize, int *pnSrcYSize, + int *pnSrcXExtraSize, int *pnSrcYExtraSize, + double *pdfSrcFillRatio) + +{ +/* -------------------------------------------------------------------- */ +/* Figure out whether we just want to do the usual "along the */ +/* edge" sampling, or using a grid. The grid usage is */ +/* important in some weird "inside out" cases like WGS84 to */ +/* polar stereographic around the pole. Also figure out the */ +/* sampling rate. */ +/* -------------------------------------------------------------------- */ + double dfStepSize; + int nSampleMax, nStepCount = 21, bUseGrid; + int *pabSuccess = NULL; + double *padfX, *padfY, *padfZ; + int nSamplePoints; + double dfRatio; + + if( CSLFetchNameValue( psOptions->papszWarpOptions, + "SAMPLE_STEPS" ) != NULL ) + { + nStepCount = + atoi(CSLFetchNameValue( psOptions->papszWarpOptions, + "SAMPLE_STEPS" )); + nStepCount = MAX(2,nStepCount); + } + + dfStepSize = 1.0 / (nStepCount-1); + + bUseGrid = CSLFetchBoolean( psOptions->papszWarpOptions, + "SAMPLE_GRID", FALSE ); + + TryAgainWithGrid: + nSamplePoints = 0; + if( bUseGrid ) + { + if (nStepCount > INT_MAX / nStepCount) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many steps : %d", nStepCount); + return CE_Failure; + } + nSampleMax = nStepCount * nStepCount; + } + else + { + if (nStepCount > INT_MAX / 4) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many steps : %d", nStepCount); + return CE_Failure; + } + nSampleMax = nStepCount * 4; + } + + pabSuccess = (int *) VSIMalloc2(sizeof(int), nSampleMax); + padfX = (double *) VSIMalloc2(sizeof(double) * 3, nSampleMax); + if (pabSuccess == NULL || padfX == NULL) + { + CPLFree( padfX ); + CPLFree( pabSuccess ); + return CE_Failure; + } + padfY = padfX + nSampleMax; + padfZ = padfX + nSampleMax * 2; + +/* -------------------------------------------------------------------- */ +/* Setup sample points on a grid pattern throughout the area. */ +/* -------------------------------------------------------------------- */ + if( bUseGrid ) + { + double dfRatioY; + + for( dfRatioY = 0.0; + dfRatioY <= 1.0 + dfStepSize*0.5; + dfRatioY += dfStepSize ) + { + for( dfRatio = 0.0; + dfRatio <= 1.0 + dfStepSize*0.5; + dfRatio += dfStepSize ) + { + padfX[nSamplePoints] = dfRatio * nDstXSize + nDstXOff; + padfY[nSamplePoints] = dfRatioY * nDstYSize + nDstYOff; + padfZ[nSamplePoints++] = 0.0; + } + } + } + /* -------------------------------------------------------------------- */ + /* Setup sample points all around the edge of the output raster. */ + /* -------------------------------------------------------------------- */ + else + { + for( dfRatio = 0.0; dfRatio <= 1.0 + dfStepSize*0.5; dfRatio += dfStepSize ) + { + // Along top + padfX[nSamplePoints] = dfRatio * nDstXSize + nDstXOff; + padfY[nSamplePoints] = nDstYOff; + padfZ[nSamplePoints++] = 0.0; + + // Along bottom + padfX[nSamplePoints] = dfRatio * nDstXSize + nDstXOff; + padfY[nSamplePoints] = nDstYOff + nDstYSize; + padfZ[nSamplePoints++] = 0.0; + + // Along left + padfX[nSamplePoints] = nDstXOff; + padfY[nSamplePoints] = dfRatio * nDstYSize + nDstYOff; + padfZ[nSamplePoints++] = 0.0; + + // Along right + padfX[nSamplePoints] = nDstXSize + nDstXOff; + padfY[nSamplePoints] = dfRatio * nDstYSize + nDstYOff; + padfZ[nSamplePoints++] = 0.0; + } + } + + CPLAssert( nSamplePoints == nSampleMax ); + +/* -------------------------------------------------------------------- */ +/* Transform them to the input pixel coordinate space */ +/* -------------------------------------------------------------------- */ + if( !psOptions->pfnTransformer( psOptions->pTransformerArg, + TRUE, nSamplePoints, + padfX, padfY, padfZ, pabSuccess ) ) + { + CPLFree( padfX ); + CPLFree( pabSuccess ); + + CPLError( CE_Failure, CPLE_AppDefined, + "GDALWarperOperation::ComputeSourceWindow() failed because\n" + "the pfnTransformer failed." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Collect the bounds, ignoring any failed points. */ +/* -------------------------------------------------------------------- */ + double dfMinXOut=0.0, dfMinYOut=0.0, dfMaxXOut=0.0, dfMaxYOut=0.0; + int bGotInitialPoint = FALSE; + int nFailedCount = 0, i; + + for( i = 0; i < nSamplePoints; i++ ) + { + if( !pabSuccess[i] ) + { + nFailedCount++; + continue; + } + + if( !bGotInitialPoint ) + { + bGotInitialPoint = TRUE; + dfMinXOut = dfMaxXOut = padfX[i]; + dfMinYOut = dfMaxYOut = padfY[i]; + } + else + { + dfMinXOut = MIN(dfMinXOut,padfX[i]); + dfMinYOut = MIN(dfMinYOut,padfY[i]); + dfMaxXOut = MAX(dfMaxXOut,padfX[i]); + dfMaxYOut = MAX(dfMaxYOut,padfY[i]); + } + } + + CPLFree( padfX ); + CPLFree( pabSuccess ); + +/* -------------------------------------------------------------------- */ +/* If we got any failures when not using a grid, we should */ +/* really go back and try again with the grid. Sorry for the */ +/* goto. */ +/* -------------------------------------------------------------------- */ + if( !bUseGrid && nFailedCount > 0 ) + { + bUseGrid = TRUE; + goto TryAgainWithGrid; + } + +/* -------------------------------------------------------------------- */ +/* If we get hardly any points (or none) transforming, we give */ +/* up. */ +/* -------------------------------------------------------------------- */ + if( nFailedCount > nSamplePoints - 5 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many points (%d out of %d) failed to transform,\n" + "unable to compute output bounds.", + nFailedCount, nSamplePoints ); + return CE_Failure; + } + + if( nFailedCount > 0 ) + CPLDebug( "GDAL", + "GDALWarpOperation::ComputeSourceWindow() %d out of %d points failed to transform.", + nFailedCount, nSamplePoints ); + +/* -------------------------------------------------------------------- */ +/* How much of a window around our source pixel might we need */ +/* to collect data from based on the resampling kernel? Even */ +/* if the requested central pixel falls off the source image, */ +/* we may need to collect data if some portion of the */ +/* resampling kernel could be on-image. */ +/* -------------------------------------------------------------------- */ + int nResWinSize = GWKGetFilterRadius(psOptions->eResampleAlg); + + /* Take scaling into account */ + double dfXScale = (double)nDstXSize / (dfMaxXOut - dfMinXOut); + double dfYScale = (double)nDstYSize / (dfMaxYOut - dfMinYOut); + int nXRadius = ( dfXScale < 1.0 ) ? + (int)ceil( nResWinSize / dfXScale ) :nResWinSize; + int nYRadius = ( dfYScale < 1.0 ) ? + (int)ceil( nResWinSize / dfYScale ) : nResWinSize; + nResWinSize = MAX(nXRadius, nYRadius); + +/* -------------------------------------------------------------------- */ +/* Allow addition of extra sample pixels to source window to */ +/* avoid missing pixels due to sampling error. In fact, */ +/* fallback to adding a bit to the window if any points failed */ +/* to transform. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue( psOptions->papszWarpOptions, + "SOURCE_EXTRA" ) != NULL ) + { + nResWinSize += atoi( + CSLFetchNameValue( psOptions->papszWarpOptions, "SOURCE_EXTRA" )); + } + else if( nFailedCount > 0 ) + nResWinSize += 10; + +/* -------------------------------------------------------------------- */ +/* return bounds. */ +/* -------------------------------------------------------------------- */ + /*CPLDebug("WARP", "dst=(%d,%d,%d,%d) raw src=(minx=%.8g,miny=%.8g,maxx=%.8g,maxy=%.8g)", + nDstXOff, nDstYOff, nDstXSize, nDstYSize, + dfMinXOut, dfMinYOut, dfMaxXOut, dfMaxYOut); + */ + *pnSrcXOff = MAX(0,(int) floor( dfMinXOut ) ); + *pnSrcYOff = MAX(0,(int) floor( dfMinYOut ) ); + *pnSrcXOff = MIN(*pnSrcXOff,GDALGetRasterXSize(psOptions->hSrcDS)); + *pnSrcYOff = MIN(*pnSrcYOff,GDALGetRasterYSize(psOptions->hSrcDS)); + + double dfCeilMaxXOut = ceil(dfMaxXOut); + if( dfCeilMaxXOut > INT_MAX ) + dfCeilMaxXOut = INT_MAX; + double dfCeilMaxYOut = ceil(dfMaxYOut); + if( dfCeilMaxYOut > INT_MAX ) + dfCeilMaxYOut = INT_MAX; + + int nSrcXSizeRaw = MIN( GDALGetRasterXSize(psOptions->hSrcDS) - *pnSrcXOff, + ((int) dfCeilMaxXOut) - *pnSrcXOff ); + int nSrcYSizeRaw = MIN( GDALGetRasterYSize(psOptions->hSrcDS) - *pnSrcYOff, + ((int) dfCeilMaxYOut) - *pnSrcYOff ); + nSrcXSizeRaw = MAX(0,nSrcXSizeRaw); + nSrcYSizeRaw = MAX(0,nSrcYSizeRaw); + + *pnSrcXOff = MAX(0,(int) floor( dfMinXOut ) - nResWinSize ); + *pnSrcYOff = MAX(0,(int) floor( dfMinYOut ) - nResWinSize ); + *pnSrcXOff = MIN(*pnSrcXOff,GDALGetRasterXSize(psOptions->hSrcDS)); + *pnSrcYOff = MIN(*pnSrcYOff,GDALGetRasterYSize(psOptions->hSrcDS)); + + *pnSrcXSize = MIN( GDALGetRasterXSize(psOptions->hSrcDS) - *pnSrcXOff, + ((int) dfCeilMaxXOut) - *pnSrcXOff + nResWinSize ); + *pnSrcYSize = MIN( GDALGetRasterYSize(psOptions->hSrcDS) - *pnSrcYOff, + ((int) dfCeilMaxYOut) - *pnSrcYOff + nResWinSize ); + *pnSrcXSize = MAX(0,*pnSrcXSize); + *pnSrcYSize = MAX(0,*pnSrcYSize); + + if( pnSrcXExtraSize ) + *pnSrcXExtraSize = *pnSrcXSize - nSrcXSizeRaw; + if( pnSrcYExtraSize ) + *pnSrcYExtraSize = *pnSrcYSize - nSrcYSizeRaw; + + // Computed the ratio of the clamped source raster window size over + // the unclamped source raster window size + if( pdfSrcFillRatio ) + *pdfSrcFillRatio = *pnSrcXSize * *pnSrcYSize / MAX(1.0, + (dfMaxXOut - dfMinXOut + 2 * nResWinSize) * (dfMaxYOut - dfMinYOut + 2 * nResWinSize)); + + return CE_None; +} + +/************************************************************************/ +/* ReportTiming() */ +/************************************************************************/ + +void GDALWarpOperation::ReportTiming( const char * pszMessage ) + +{ + if( !bReportTimings ) + return; + + unsigned long nNewTime = VSITime(NULL); + + if( pszMessage != NULL ) + { + CPLDebug( "WARP_TIMING", "%s: %lds", + pszMessage, (long)(nNewTime - nLastTimeReported) ); + } + + nLastTimeReported = nNewTime; +} + diff --git a/bazaar/plugin/gdal/alg/gvgcpfit.h b/bazaar/plugin/gdal/alg/gvgcpfit.h new file mode 100644 index 000000000..d289cc271 --- /dev/null +++ b/bazaar/plugin/gdal/alg/gvgcpfit.h @@ -0,0 +1,94 @@ +#ifndef _GVGCPFIT_H_INCLUDED +#define _GVGCPFIT_H_INCLUDED + +#include "cpl_port.h" +#include "cpl_conv.h" +#include "cpl_error.h" + +#define EXTERNAL +#define LOCAL static + +#define SUCCESS 0 +#define ABORT -1 + + +/*------------------------ Start of file CURVEFIT.H -----------------------*/ + +/* +****************************************************************************** +* * +* CURVEFIT.H * +* ========= * +* * +* This file contains the function prototype for CURVEFIT.C. * +****************************************************************************** +*/ + + +#ifndef CURVEFIT_H +#define CURVEFIT_H + +/*- Function prototypes in CURVEFIT.C. -*/ + +EXTERNAL int svdfit(float x[], float y[], int ndata, + double a[], int ma, double **u, double **v, double w[], + double *chisq, void (*funcs)(double, double *, int)); + +EXTERNAL void svbksb(double **u, double w[], double **v, int m,int n, + double b[], double x[]); + +EXTERNAL void svdvar(double **v, int ma, double w[], double **cvm); + +EXTERNAL int svdcmp(double **a, int m, int n, double *w, double **v); + + +#endif + + +/*-------------------------- End of file CURVEFIT.H -----------------------*/ + + + + +/*----------------------------- FILE polyfit.h ----------------------------*/ +#ifndef __POLYFIT_H +#define __POLYFIT_H + +EXTERNAL int OneDPolyFit( double *rms_err, double *coeffs_array, + int fit_order, int no_samples, double *f_array, double *x_array ); + +EXTERNAL double OneDPolyEval( double *coeff, int order, double x ); + +EXTERNAL int TwoDPolyFit( double *rms_err, double *coeffs_array, + int fit_order, int no_samples, double *f_array, double *x_array, + double *y_array ); + +EXTERNAL double TwoDPolyEval( double *coeff, int order, double x, double y ); + +EXTERNAL int TwoDPolyGradFit( double *rms_err, double *coeffs_array, + int fit_order, int no_samples, double *gradxy_array, + double *x_array, double *y_array ); + +EXTERNAL void TwoDPolyGradEval(double *fgradx, double *fgrady, + double *coeff, int order, double x, double y); + +EXTERNAL void GetPolyInX (double *xcoeffs, double *xycoeffs, int order, + double y); + +EXTERNAL void GetPolyInY(double *ycoeffs, double *xycoeffs, int order, + double x); + +EXTERNAL int ThreeDPolyFit( double *rms_err, double *coeffs_array, + int fit_order, int no_samples, double *f_array, double *x_array, + double *y_array, double *z_array ); + +EXTERNAL double ThreeDPolyEval( double *coeff, int order, double x, double y, double z ); + + + +#endif /* __POLYFIT_H */ + + +/*---------------------- End of FILE polyfit.h ----------------------------*/ + +#endif /* ndef _GVGCPFIT_INCLUDED */ diff --git a/bazaar/plugin/gdal/alg/llrasterize.cpp b/bazaar/plugin/gdal/alg/llrasterize.cpp new file mode 100644 index 000000000..815026470 --- /dev/null +++ b/bazaar/plugin/gdal/alg/llrasterize.cpp @@ -0,0 +1,606 @@ +/****************************************************************************** + * $Id: llrasterize.cpp 29117 2015-05-02 20:50:05Z rouault $ + * + * Project: GDAL + * Purpose: Vector polygon rasterization code. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_alg.h" +#include "gdal_alg_priv.h" + +static int llCompareInt(const void *a, const void *b) +{ + return (*(const int *)a) - (*(const int *)b); +} + +static void llSwapDouble(double *a, double *b) +{ + double temp = *a; + *a = *b; + *b = temp; +} + +/************************************************************************/ +/* dllImageFilledPolygon() */ +/* */ +/* Perform scanline conversion of the passed multi-ring */ +/* polygon. Note the polygon does not need to be explicitly */ +/* closed. The scanline function will be called with */ +/* horizontal scanline chunks which may not be entirely */ +/* contained within the valid raster area (in the X */ +/* direction). */ +/* */ +/* NEW: Nodes' coordinate are kept as double in order */ +/* to compute accurately the intersections with the lines */ +/* */ +/* Two methods for determining the border pixels: */ +/* */ +/* 1) method = 0 */ +/* Inherits algorithm from version above but with several bugs */ +/* fixed except for the cone facing down. */ +/* A pixel on which a line intersects a segment of a */ +/* polygon will always be considered as inside the shape. */ +/* Note that we only compute intersections with lines that */ +/* passes through the middle of a pixel (line coord = 0.5, */ +/* 1.5, 2.5, etc.) */ +/* */ +/* 2) method = 1: */ +/* A pixel is considered inside a polygon if its center */ +/* falls inside the polygon. This is somehow more robust unless */ +/* the nodes are placed in the center of the pixels in which */ +/* case, due to numerical inaccuracies, it's hard to predict */ +/* if the pixel will be considered inside or outside the shape. */ +/************************************************************************/ + +/* + * NOTE: This code was originally adapted from the gdImageFilledPolygon() + * function in libgd. + * + * http://www.boutell.com/gd/ + * + * It was later adapted for direct inclusion in GDAL and relicensed under + * the GDAL MIT/X license (pulled from the OpenEV distribution). + */ + +void GDALdllImageFilledPolygon(int nRasterXSize, int nRasterYSize, + int nPartCount, int *panPartSize, + double *padfX, double *padfY, + double *dfVariant, + llScanlineFunc pfnScanlineFunc, void *pCBData ) +{ +/************************************************************************* +2nd Method (method=1): +===================== +No known bug +*************************************************************************/ + + int i; + int y; + int miny, maxy,minx,maxx; + double dminy, dmaxy; + double dx1, dy1; + double dx2, dy2; + double dy; + double intersect; + + int ind1, ind2; + int ints, n, part; + int *polyInts; + + int horizontal_x1, horizontal_x2; + + if (!nPartCount) { + return; + } + + n = 0; + for( part = 0; part < nPartCount; part++ ) + n += panPartSize[part]; + + polyInts = (int *) malloc(sizeof(int) * n); + + dminy = padfY[0]; + dmaxy = padfY[0]; + for (i=1; (i < n); i++) { + + if (padfY[i] < dminy) { + dminy = padfY[i]; + } + if (padfY[i] > dmaxy) { + dmaxy = padfY[i]; + } + } + miny = (int) dminy; + maxy = (int) dmaxy; + + + if( miny < 0 ) + miny = 0; + if( maxy >= nRasterYSize ) + maxy = nRasterYSize-1; + + + minx = 0; + maxx = nRasterXSize - 1; + + /* Fix in 1.3: count a vertex only once */ + for (y=miny; y <= maxy; y++) { + int partoffset = 0; + + dy = y +0.5; /* center height of line*/ + + + part = 0; + ints = 0; + + /*Initialize polyInts, otherwise it can sometimes causes a seg fault */ + memset(polyInts, -1, sizeof(int) * n); + + for (i=0; (i < n); i++) { + + + if( i == partoffset + panPartSize[part] ) { + partoffset += panPartSize[part]; + part++; + } + + if( i == partoffset ) { + ind1 = partoffset + panPartSize[part] - 1; + ind2 = partoffset; + } else { + ind1 = i-1; + ind2 = i; + } + + + dy1 = padfY[ind1]; + dy2 = padfY[ind2]; + + + if( (dy1 < dy && dy2 < dy) || (dy1 > dy && dy2 > dy) ) + continue; + + if (dy1 < dy2) { + dx1 = padfX[ind1]; + dx2 = padfX[ind2]; + } else if (dy1 > dy2) { + dy2 = padfY[ind1]; + dy1 = padfY[ind2]; + dx2 = padfX[ind1]; + dx1 = padfX[ind2]; + } else /* if (fabs(dy1-dy2)< 1.e-6) */ + { + + /*AE: DO NOT skip bottom horizontal segments + -Fill them separately- + They are not taken into account twice.*/ + if (padfX[ind1] > padfX[ind2]) + { + horizontal_x1 = (int) floor(padfX[ind2]+0.5); + horizontal_x2 = (int) floor(padfX[ind1]+0.5); + + if ( (horizontal_x1 > maxx) || (horizontal_x2 <= minx) ) + continue; + + /*fill the horizontal segment (separately from the rest)*/ + pfnScanlineFunc( pCBData, y, horizontal_x1, horizontal_x2 - 1, (dfVariant == NULL)?0:dfVariant[0] ); + continue; + } + else /*skip top horizontal segments (they are already filled in the regular loop)*/ + continue; + + } + + if(( dy < dy2 ) && (dy >= dy1)) + { + + intersect = (dy-dy1) * (dx2-dx1) / (dy2-dy1) + dx1; + + polyInts[ints++] = (int) floor(intersect+0.5); + } + } + + /* + * It would be more efficient to do this inline, to avoid + * a function call for each comparison. + * NOTE - mloskot: make llCompareInt a functor and use std + * algorithm and it will be optimized and expanded + * automatically in compile-time, with modularity preserved. + */ + qsort(polyInts, ints, sizeof(int), llCompareInt); + + + for (i=0; (i < (ints)); i+=2) + { + if( polyInts[i] <= maxx && polyInts[i+1] > minx ) + { + pfnScanlineFunc( pCBData, y, polyInts[i], polyInts[i+1] - 1, (dfVariant == NULL)?0:dfVariant[0] ); + } + } + } + + free( polyInts ); +} + +/************************************************************************/ +/* GDALdllImagePoint() */ +/************************************************************************/ + +void GDALdllImagePoint( int nRasterXSize, int nRasterYSize, + int nPartCount, + CPL_UNUSED int *panPartSize, + double *padfX, double *padfY, double *padfVariant, + llPointFunc pfnPointFunc, void *pCBData ) +{ + int i; + + for ( i = 0; i < nPartCount; i++ ) + { + int nX = (int)floor( padfX[i] ); + int nY = (int)floor( padfY[i] ); + double dfVariant = 0; + if( padfVariant != NULL ) + dfVariant = padfVariant[i]; + + if ( 0 <= nX && nX < nRasterXSize && 0 <= nY && nY < nRasterYSize ) + pfnPointFunc( pCBData, nY, nX, dfVariant ); + } +} + +/************************************************************************/ +/* GDALdllImageLine() */ +/************************************************************************/ + +void GDALdllImageLine( int nRasterXSize, int nRasterYSize, + int nPartCount, int *panPartSize, + double *padfX, double *padfY, double *padfVariant, + llPointFunc pfnPointFunc, void *pCBData ) +{ + int i, n; + + if ( !nPartCount ) + return; + + for ( i = 0, n = 0; i < nPartCount; n += panPartSize[i++] ) + { + int j; + + for ( j = 1; j < panPartSize[i]; j++ ) + { + int iX = (int)floor( padfX[n + j - 1] ); + int iY = (int)floor( padfY[n + j - 1] ); + + const int iX1 = (int)floor( padfX[n + j] ); + const int iY1 = (int)floor( padfY[n + j] ); + + double dfVariant = 0, dfVariant1 = 0; + if( padfVariant != NULL && + ((GDALRasterizeInfo *)pCBData)->eBurnValueSource != + GBV_UserBurnValue ) + { + dfVariant = padfVariant[n + j - 1]; + dfVariant1 = padfVariant[n + j]; + } + + int nDeltaX = ABS( iX1 - iX ); + int nDeltaY = ABS( iY1 - iY ); + + // Step direction depends on line direction. + const int nXStep = ( iX > iX1 ) ? -1 : 1; + const int nYStep = ( iY > iY1 ) ? -1 : 1; + + // Determine the line slope. + if ( nDeltaX >= nDeltaY ) + { + const int nXError = nDeltaY << 1; + const int nYError = nXError - (nDeltaX << 1); + int nError = nXError - nDeltaX; + /* == 0 makes clang -fcatch-undefined-behavior -ftrapv happy, but if */ + /* it is == 0, dfDeltaVariant is not really used, so any value is OK */ + double dfDeltaVariant = (nDeltaX == 0) ? 0 : (dfVariant1 - dfVariant) / + (double)nDeltaX; + + while ( nDeltaX-- >= 0 ) + { + if ( 0 <= iX && iX < nRasterXSize + && 0 <= iY && iY < nRasterYSize ) + pfnPointFunc( pCBData, iY, iX, dfVariant ); + + dfVariant += dfDeltaVariant; + iX += nXStep; + if ( nError > 0 ) + { + iY += nYStep; + nError += nYError; + } + else + nError += nXError; + } + } + else + { + const int nXError = nDeltaX << 1; + const int nYError = nXError - (nDeltaY << 1); + int nError = nXError - nDeltaY; + /* == 0 makes clang -fcatch-undefined-behavior -ftrapv happy, but if */ + /* it is == 0, dfDeltaVariant is not really used, so any value is OK */ + double dfDeltaVariant = (nDeltaY == 0) ? 0 : (dfVariant1 - dfVariant) / + (double)nDeltaY; + + while ( nDeltaY-- >= 0 ) + { + if ( 0 <= iX && iX < nRasterXSize + && 0 <= iY && iY < nRasterYSize ) + pfnPointFunc( pCBData, iY, iX, dfVariant ); + + dfVariant += dfDeltaVariant; + iY += nYStep; + if ( nError > 0 ) + { + iX += nXStep; + nError += nYError; + } + else + nError += nXError; + } + } + } + } +} + +/************************************************************************/ +/* GDALdllImageLineAllTouched() */ +/* */ +/* This alternate line drawing algorithm attempts to ensure */ +/* that every pixel touched at all by the line will get set. */ +/* @param padfVariant should contain the values that are to be */ +/* added to the burn value. The values along the line between the */ +/* points will be linearly interpolated. These values are used */ +/* only if pCBData->eBurnValueSource is set to something other */ +/* than GBV_UserBurnValue. If NULL is passed, a monotonous line */ +/* will be drawn with the burn value. */ +/************************************************************************/ + +void +GDALdllImageLineAllTouched(int nRasterXSize, int nRasterYSize, + int nPartCount, int *panPartSize, + double *padfX, double *padfY, double *padfVariant, + llPointFunc pfnPointFunc, void *pCBData ) + +{ + int i, n; + + if ( !nPartCount ) + return; + + for ( i = 0, n = 0; i < nPartCount; n += panPartSize[i++] ) + { + int j; + + for ( j = 1; j < panPartSize[i]; j++ ) + { + double dfX = padfX[n + j - 1]; + double dfY = padfY[n + j - 1]; + + double dfXEnd = padfX[n + j]; + double dfYEnd = padfY[n + j]; + + double dfVariant = 0, dfVariantEnd = 0; + if( padfVariant != NULL && + ((GDALRasterizeInfo *)pCBData)->eBurnValueSource != + GBV_UserBurnValue ) + { + dfVariant = padfVariant[n + j - 1]; + dfVariantEnd = padfVariant[n + j]; + } + + // Skip segments that are off the target region. + if( (dfY < 0 && dfYEnd < 0) + || (dfY > nRasterYSize && dfYEnd > nRasterYSize) + || (dfX < 0 && dfXEnd < 0) + || (dfX > nRasterXSize && dfXEnd > nRasterXSize) ) + continue; + + // Swap if needed so we can proceed from left2right (X increasing) + if( dfX > dfXEnd ) + { + llSwapDouble( &dfX, &dfXEnd ); + llSwapDouble( &dfY, &dfYEnd ); + llSwapDouble( &dfVariant, &dfVariantEnd ); + } + + // Special case for vertical lines. + if( floor(dfX) == floor(dfXEnd) ) + { + if( dfYEnd < dfY ) + { + llSwapDouble( &dfY, &dfYEnd ); + llSwapDouble( &dfVariant, &dfVariantEnd ); + } + + int iX = (int) floor(dfX); + int iY = (int) floor(dfY); + int iYEnd = (int) floor(dfYEnd); + + if( iX >= nRasterXSize ) + continue; + + double dfDeltaVariant = 0; + if(( dfYEnd - dfY ) > 0) + dfDeltaVariant = ( dfVariantEnd - dfVariant ) + / ( dfYEnd - dfY );//per unit change in iY + + // Clip to the borders of the target region + if( iY < 0 ) + iY = 0; + if( iYEnd >= nRasterYSize ) + iYEnd = nRasterYSize - 1; + dfVariant += dfDeltaVariant * ( (double)iY - dfY ); + + if( padfVariant == NULL ) + for( ; iY <= iYEnd; iY++ ) + pfnPointFunc( pCBData, iY, iX, 0.0 ); + else + for( ; iY <= iYEnd; iY++, dfVariant += dfDeltaVariant ) + pfnPointFunc( pCBData, iY, iX, dfVariant ); + + continue; // next segment + } + + double dfDeltaVariant = ( dfVariantEnd - dfVariant ) + / ( dfXEnd - dfX );//per unit change in iX + + // special case for horizontal lines + if( floor(dfY) == floor(dfYEnd) ) + { + if( dfXEnd < dfX ) + { + llSwapDouble( &dfX, &dfXEnd ); + llSwapDouble( &dfVariant, &dfVariantEnd ); + } + + int iX = (int) floor(dfX); + int iY = (int) floor(dfY); + int iXEnd = (int) floor(dfXEnd); + + if( iY >= nRasterYSize ) + continue; + + // Clip to the borders of the target region + if( iX < 0 ) + iX = 0; + if( iXEnd >= nRasterXSize ) + iXEnd = nRasterXSize - 1; + dfVariant += dfDeltaVariant * ( (double)iX - dfX ); + + if( padfVariant == NULL ) + for( ; iX <= iXEnd; iX++ ) + pfnPointFunc( pCBData, iY, iX, 0.0 ); + else + for( ; iX <= iXEnd; iX++, dfVariant += dfDeltaVariant ) + pfnPointFunc( pCBData, iY, iX, dfVariant ); + + continue; // next segment + } + +/* -------------------------------------------------------------------- */ +/* General case - left to right sloped. */ +/* -------------------------------------------------------------------- */ + double dfSlope = (dfYEnd - dfY) / (dfXEnd - dfX); + + // clip segment in X + if( dfXEnd > nRasterXSize ) + { + dfYEnd -= ( dfXEnd - (double)nRasterXSize ) * dfSlope; + dfXEnd = nRasterXSize; + } + if( dfX < 0 ) + { + dfY += (0 - dfX) * dfSlope; + dfVariant += dfDeltaVariant * (0.0 - dfX); + dfX = 0.0; + } + + // clip segment in Y + double dfDiffX; + if( dfYEnd > dfY ) + { + if( dfY < 0 ) + { + dfX += (dfDiffX = (0 - dfY) / dfSlope); + dfVariant += dfDeltaVariant * dfDiffX; + dfY = 0.0; + } + if( dfYEnd >= nRasterYSize ) + { + dfXEnd += ( dfYEnd - (double)nRasterYSize ) / dfSlope; + dfYEnd = nRasterXSize; + } + } + else + { + if( dfY >= nRasterYSize ) + { + dfX += (dfDiffX = ((double)nRasterYSize - dfY) / dfSlope); + dfVariant += dfDeltaVariant * dfDiffX; + dfY = nRasterYSize; + } + if( dfYEnd < 0 ) + { + dfXEnd -= ( dfYEnd - 0 ) / dfSlope; + dfYEnd = 0; + } + } + + // step from pixel to pixel. + while( dfX >= 0 && dfX < dfXEnd ) + { + int iX = (int) floor(dfX); + int iY = (int) floor(dfY); + + // burn in the current point. + // We should be able to drop the Y check because we cliped in Y, + // but there may be some error with all the small steps. + if( iY >= 0 && iY < nRasterYSize ) + pfnPointFunc( pCBData, iY, iX, dfVariant ); + + double dfStepX = floor(dfX+1.0) - dfX; + double dfStepY = dfStepX * dfSlope; + + // step to right pixel without changing scanline? + if( (int) floor(dfY + dfStepY) == iY ) + { + dfX += dfStepX; + dfY += dfStepY; + dfVariant += dfDeltaVariant * dfStepX; + } + else if( dfSlope < 0 ) + { + dfStepY = iY - dfY; + if( dfStepY > -0.000000001 ) + dfStepY = -0.000000001; + + dfStepX = dfStepY / dfSlope; + dfX += dfStepX; + dfY += dfStepY; + dfVariant += dfDeltaVariant * dfStepX; + } + else + { + dfStepY = (iY + 1) - dfY; + if( dfStepY < 0.000000001 ) + dfStepY = 0.000000001; + + dfStepX = dfStepY / dfSlope; + dfX += dfStepX; + dfY += dfStepY; + dfVariant += dfDeltaVariant * dfStepX; + } + } // next step along sement. + + } // next segment + } // next part +} diff --git a/bazaar/plugin/gdal/alg/makefile.vc b/bazaar/plugin/gdal/alg/makefile.vc new file mode 100644 index 000000000..f1072dbe8 --- /dev/null +++ b/bazaar/plugin/gdal/alg/makefile.vc @@ -0,0 +1,31 @@ + +# +# Algorithms +# + +GDAL_ROOT = .. + +EXTRAFLAGS = -I../ogr/ogrsf_frmts $(GEOS_CFLAGS) + +!INCLUDE ..\nmake.opt + +!IFDEF INCLUDE_OGR_FRMTS +EXTRAFLAGS = $(EXTRAFLAGS) -DOGR_ENABLED +!ENDIF + +OBJ = gdaldither.obj gdalmediancut.obj gdal_crs.obj gdaltransformer.obj \ + gdalsimplewarp.obj gdalwarper.obj gdalwarpkernel.obj \ + thinplatespline.obj gdal_tps.obj gdalrasterize.obj llrasterize.obj \ + gdalwarpoperation.obj gdalchecksum.obj gdal_rpc.obj gdalgeoloc.obj \ + gdalgrid.obj gdalgridsse.obj gdalcutline.obj gdalproximity.obj rasterfill.obj \ + gdalsievefilter.obj gdalrasterpolygonenumerator.obj polygonize.obj \ + gdalrasterfpolygonenumerator.obj fpolygonize.obj contour.obj \ + gdal_octave.obj gdal_simplesurf.obj gdalmatching.obj \ + gdaltransformgeolocs.obj + + +default: $(OBJ) + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/alg/polygonize.cpp b/bazaar/plugin/gdal/alg/polygonize.cpp new file mode 100644 index 000000000..74646f8ac --- /dev/null +++ b/bazaar/plugin/gdal/alg/polygonize.cpp @@ -0,0 +1,761 @@ +/****************************************************************************** + * $Id: polygonize.cpp 28826 2015-03-30 17:51:14Z rouault $ + * Project: GDAL + * Purpose: Raster to Polygon Converter + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_alg_priv.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: polygonize.cpp 28826 2015-03-30 17:51:14Z rouault $"); + +#ifdef OGR_ENABLED + +/************************************************************************/ +/* ==================================================================== */ +/* RPolygon */ +/* */ +/* This is a helper class to hold polygons while they are being */ +/* formed in memory, and to provide services to coalesce a much */ +/* of edge sections into complete rings. */ +/* ==================================================================== */ +/************************************************************************/ + +class RPolygon { +public: + RPolygon( int nValue ) { nPolyValue = nValue; nLastLineUpdated = -1; } + + int nPolyValue; + int nLastLineUpdated; + + std::vector< std::vector > aanXY; + + void AddSegment( int x1, int y1, int x2, int y2 ); + void Dump(); + void Coalesce(); + void Merge( int iBaseString, int iSrcString, int iDirection ); +}; + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ +void RPolygon::Dump() +{ + size_t iString; + + printf( "RPolygon: Value=%d, LastLineUpdated=%d\n", + nPolyValue, nLastLineUpdated ); + + for( iString = 0; iString < aanXY.size(); iString++ ) + { + std::vector &anString = aanXY[iString]; + size_t iVert; + + printf( " String %d:\n", (int) iString ); + for( iVert = 0; iVert < anString.size(); iVert += 2 ) + { + printf( " (%d,%d)\n", anString[iVert], anString[iVert+1] ); + } + } +} + +/************************************************************************/ +/* Coalesce() */ +/************************************************************************/ + +void RPolygon::Coalesce() + +{ + size_t iBaseString; + +/* -------------------------------------------------------------------- */ +/* Iterate over loops starting from the first, trying to merge */ +/* other segments into them. */ +/* -------------------------------------------------------------------- */ + for( iBaseString = 0; iBaseString < aanXY.size(); iBaseString++ ) + { + std::vector &anBase = aanXY[iBaseString]; + int bMergeHappened = TRUE; + +/* -------------------------------------------------------------------- */ +/* Keep trying to merge the following strings into our target */ +/* "base" string till we have tried them all once without any */ +/* mergers. */ +/* -------------------------------------------------------------------- */ + while( bMergeHappened ) + { + size_t iString; + + bMergeHappened = FALSE; + +/* -------------------------------------------------------------------- */ +/* Loop over the following strings, trying to find one we can */ +/* merge onto the end of our base string. */ +/* -------------------------------------------------------------------- */ + for( iString = iBaseString+1; + iString < aanXY.size(); + iString++ ) + { + std::vector &anString = aanXY[iString]; + + if( anBase[anBase.size()-2] == anString[0] + && anBase[anBase.size()-1] == anString[1] ) + { + Merge( iBaseString, iString, 1 ); + bMergeHappened = TRUE; + } + else if( anBase[anBase.size()-2] == anString[anString.size()-2] + && anBase[anBase.size()-1] == anString[anString.size()-1] ) + { + Merge( iBaseString, iString, -1 ); + bMergeHappened = TRUE; + } + } + } + + /* At this point our loop *should* be closed! */ + + CPLAssert( anBase[0] == anBase[anBase.size()-2] + && anBase[1] == anBase[anBase.size()-1] ); + } + +} + +/************************************************************************/ +/* Merge() */ +/************************************************************************/ + +void RPolygon::Merge( int iBaseString, int iSrcString, int iDirection ) + +{ + std::vector &anBase = aanXY[iBaseString]; + std::vector &anString = aanXY[iSrcString]; + int iStart, iEnd, i; + + if( iDirection == 1 ) + { + iStart = 1; + iEnd = anString.size() / 2; + } + else + { + iStart = anString.size() / 2 - 2; + iEnd = -1; + } + + for( i = iStart; i != iEnd; i += iDirection ) + { + anBase.push_back( anString[i*2+0] ); + anBase.push_back( anString[i*2+1] ); + } + + if( iSrcString < ((int) aanXY.size())-1 ) + aanXY[iSrcString] = aanXY[aanXY.size()-1]; + + size_t nSize = aanXY.size(); + aanXY.resize(nSize-1); +} + +/************************************************************************/ +/* AddSegment() */ +/************************************************************************/ + +void RPolygon::AddSegment( int x1, int y1, int x2, int y2 ) + +{ + nLastLineUpdated = MAX(y1, y2); + +/* -------------------------------------------------------------------- */ +/* Is there an existing string ending with this? */ +/* -------------------------------------------------------------------- */ + size_t iString; + + for( iString = 0; iString < aanXY.size(); iString++ ) + { + std::vector &anString = aanXY[iString]; + size_t nSSize = anString.size(); + + if( anString[nSSize-2] == x1 + && anString[nSSize-1] == y1 ) + { + int nTemp; + + nTemp = x2; + x2 = x1; + x1 = nTemp; + + nTemp = y2; + y2 = y1; + y1 = nTemp; + } + + if( anString[nSSize-2] == x2 + && anString[nSSize-1] == y2 ) + { + // We are going to add a segment, but should we just extend + // an existing segment already going in the right direction? + + int nLastLen = MAX(ABS(anString[nSSize-4]-anString[nSSize-2]), + ABS(anString[nSSize-3]-anString[nSSize-1])); + + if( nSSize >= 4 + && (anString[nSSize-4] - anString[nSSize-2] + == (anString[nSSize-2] - x1)*nLastLen) + && (anString[nSSize-3] - anString[nSSize-1] + == (anString[nSSize-1] - y1)*nLastLen) ) + { + anString.pop_back(); + anString.pop_back(); + } + + anString.push_back( x1 ); + anString.push_back( y1 ); + return; + } + } + +/* -------------------------------------------------------------------- */ +/* Create a new string. */ +/* -------------------------------------------------------------------- */ + size_t nSize = aanXY.size(); + aanXY.resize(nSize + 1); + std::vector &anString = aanXY[nSize]; + + anString.push_back( x1 ); + anString.push_back( y1 ); + anString.push_back( x2 ); + anString.push_back( y2 ); + + return; +} + +/************************************************************************/ +/* ==================================================================== */ +/* End of RPolygon */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* AddEdges() */ +/* */ +/* Examine one pixel and compare to its neighbour above */ +/* (previous) and right. If they are different polygon ids */ +/* then add the pixel edge to this polygon and the one on the */ +/* other side of the edge. */ +/************************************************************************/ + +static void AddEdges( GInt32 *panThisLineId, GInt32 *panLastLineId, + GInt32 *panPolyIdMap, GInt32 *panPolyValue, + RPolygon **papoPoly, int iX, int iY ) + +{ + int nThisId = panThisLineId[iX]; + int nRightId = panThisLineId[iX+1]; + int nPreviousId = panLastLineId[iX]; + int iXReal = iX - 1; + + if( nThisId != -1 ) + nThisId = panPolyIdMap[nThisId]; + if( nRightId != -1 ) + nRightId = panPolyIdMap[nRightId]; + if( nPreviousId != -1 ) + nPreviousId = panPolyIdMap[nPreviousId]; + + if( nThisId != nPreviousId ) + { + if( nThisId != -1 ) + { + if( papoPoly[nThisId] == NULL ) + papoPoly[nThisId] = new RPolygon( panPolyValue[nThisId] ); + + papoPoly[nThisId]->AddSegment( iXReal, iY, iXReal+1, iY ); + } + if( nPreviousId != -1 ) + { + if( papoPoly[nPreviousId] == NULL ) + papoPoly[nPreviousId] = new RPolygon(panPolyValue[nPreviousId]); + + papoPoly[nPreviousId]->AddSegment( iXReal, iY, iXReal+1, iY ); + } + } + + if( nThisId != nRightId ) + { + if( nThisId != -1 ) + { + if( papoPoly[nThisId] == NULL ) + papoPoly[nThisId] = new RPolygon(panPolyValue[nThisId]); + + papoPoly[nThisId]->AddSegment( iXReal+1, iY, iXReal+1, iY+1 ); + } + + if( nRightId != -1 ) + { + if( papoPoly[nRightId] == NULL ) + papoPoly[nRightId] = new RPolygon(panPolyValue[nRightId]); + + papoPoly[nRightId]->AddSegment( iXReal+1, iY, iXReal+1, iY+1 ); + } + } +} + +/************************************************************************/ +/* EmitPolygonToLayer() */ +/************************************************************************/ + +static CPLErr +EmitPolygonToLayer( OGRLayerH hOutLayer, int iPixValField, + RPolygon *poRPoly, double *padfGeoTransform ) + +{ + OGRFeatureH hFeat; + OGRGeometryH hPolygon; + +/* -------------------------------------------------------------------- */ +/* Turn bits of lines into coherent rings. */ +/* -------------------------------------------------------------------- */ + poRPoly->Coalesce(); + +/* -------------------------------------------------------------------- */ +/* Create the polygon geometry. */ +/* -------------------------------------------------------------------- */ + size_t iString; + + hPolygon = OGR_G_CreateGeometry( wkbPolygon ); + + for( iString = 0; iString < poRPoly->aanXY.size(); iString++ ) + { + std::vector &anString = poRPoly->aanXY[iString]; + OGRGeometryH hRing = OGR_G_CreateGeometry( wkbLinearRing ); + + int iVert; + + // we go last to first to ensure the linestring is allocated to + // the proper size on the first try. + for( iVert = anString.size()/2 - 1; iVert >= 0; iVert-- ) + { + double dfX, dfY; + int nPixelX, nPixelY; + + nPixelX = anString[iVert*2]; + nPixelY = anString[iVert*2+1]; + + dfX = padfGeoTransform[0] + + nPixelX * padfGeoTransform[1] + + nPixelY * padfGeoTransform[2]; + dfY = padfGeoTransform[3] + + nPixelX * padfGeoTransform[4] + + nPixelY * padfGeoTransform[5]; + + OGR_G_SetPoint_2D( hRing, iVert, dfX, dfY ); + } + + OGR_G_AddGeometryDirectly( hPolygon, hRing ); + } + +/* -------------------------------------------------------------------- */ +/* Create the feature object. */ +/* -------------------------------------------------------------------- */ + hFeat = OGR_F_Create( OGR_L_GetLayerDefn( hOutLayer ) ); + + OGR_F_SetGeometryDirectly( hFeat, hPolygon ); + + if( iPixValField >= 0 ) + OGR_F_SetFieldInteger( hFeat, iPixValField, poRPoly->nPolyValue ); + +/* -------------------------------------------------------------------- */ +/* Write the to the layer. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + + if( OGR_L_CreateFeature( hOutLayer, hFeat ) != OGRERR_NONE ) + eErr = CE_Failure; + + OGR_F_Destroy( hFeat ); + + return eErr; +} + +/************************************************************************/ +/* GPMaskImageData() */ +/* */ +/* Mask out image pixels to a special nodata value if the mask */ +/* band is zero. */ +/************************************************************************/ + +static CPLErr +GPMaskImageData( GDALRasterBandH hMaskBand, GByte* pabyMaskLine, int iY, int nXSize, + GInt32 *panImageLine ) + +{ + CPLErr eErr; + + eErr = GDALRasterIO( hMaskBand, GF_Read, 0, iY, nXSize, 1, + pabyMaskLine, nXSize, 1, GDT_Byte, 0, 0 ); + if( eErr == CE_None ) + { + int i; + for( i = 0; i < nXSize; i++ ) + { + if( pabyMaskLine[i] == 0 ) + panImageLine[i] = GP_NODATA_MARKER; + } + } + + return eErr; +} +#endif // OGR_ENABLED + +/************************************************************************/ +/* GDALPolygonize() */ +/************************************************************************/ + +/** + * Create polygon coverage from raster data. + * + * This function creates vector polygons for all connected regions of pixels in + * the raster sharing a common pixel value. Optionally each polygon may be + * labelled with the pixel value in an attribute. Optionally a mask band + * can be provided to determine which pixels are eligible for processing. + * + * Note that currently the source pixel band values are read into a + * signed 32bit integer buffer (Int32), so floating point or complex + * bands will be implicitly truncated before processing. If you want to use a + * version using 32bit float buffers, see GDALFPolygonize() at fpolygonize.cpp. + * + * Polygon features will be created on the output layer, with polygon + * geometries representing the polygons. The polygon geometries will be + * in the georeferenced coordinate system of the image (based on the + * geotransform of the source dataset). It is acceptable for the output + * layer to already have features. Note that GDALPolygonize() does not + * set the coordinate system on the output layer. Application code should + * do this when the layer is created, presumably matching the raster + * coordinate system. + * + * The algorithm used attempts to minimize memory use so that very large + * rasters can be processed. However, if the raster has many polygons + * or very large/complex polygons, the memory use for holding polygon + * enumerations and active polygon geometries may grow to be quite large. + * + * The algorithm will generally produce very dense polygon geometries, with + * edges that follow exactly on pixel boundaries for all non-interior pixels. + * For non-thematic raster data (such as satellite images) the result will + * essentially be one small polygon per pixel, and memory and output layer + * sizes will be substantial. The algorithm is primarily intended for + * relatively simple thematic imagery, masks, and classification results. + * + * @param hSrcBand the source raster band to be processed. + * @param hMaskBand an optional mask band. All pixels in the mask band with a + * value other than zero will be considered suitable for collection as + * polygons. + * @param hOutLayer the vector feature layer to which the polygons should + * be written. + * @param iPixValField the attribute field index indicating the feature + * attribute into which the pixel value of the polygon should be written. + * @param papszOptions a name/value list of additional options + *
+ *
"8CONNECTED":
May be set to "8" to use 8 connectedness. + * Otherwise 4 connectedness will be applied to the algorithm + *
+ * @param pfnProgress callback for reporting algorithm progress matching the + * GDALProgressFunc() semantics. May be NULL. + * @param pProgressArg callback argument passed to pfnProgress. + * + * @return CE_None on success or CE_Failure on a failure. + */ + +CPLErr CPL_STDCALL +GDALPolygonize( GDALRasterBandH hSrcBand, + GDALRasterBandH hMaskBand, + OGRLayerH hOutLayer, int iPixValField, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ +#ifndef OGR_ENABLED + CPLError(CE_Failure, CPLE_NotSupported, "GDALPolygonize() unimplemented in a non OGR build"); + return CE_Failure; +#else + VALIDATE_POINTER1( hSrcBand, "GDALPolygonize", CE_Failure ); + VALIDATE_POINTER1( hOutLayer, "GDALPolygonize", CE_Failure ); + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + int nConnectedness = CSLFetchNameValue( papszOptions, "8CONNECTED" ) ? 8 : 4; + +/* -------------------------------------------------------------------- */ +/* Confirm our output layer will support feature creation. */ +/* -------------------------------------------------------------------- */ + if( !OGR_L_TestCapability( hOutLayer, OLCSequentialWrite ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Output feature layer does not appear to support creation\n" + "of features in GDALPolygonize()." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Allocate working buffers. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + int nXSize = GDALGetRasterBandXSize( hSrcBand ); + int nYSize = GDALGetRasterBandYSize( hSrcBand ); + GInt32 *panLastLineVal = (GInt32 *) VSIMalloc2(sizeof(GInt32),nXSize + 2); + GInt32 *panThisLineVal = (GInt32 *) VSIMalloc2(sizeof(GInt32),nXSize + 2); + GInt32 *panLastLineId = (GInt32 *) VSIMalloc2(sizeof(GInt32),nXSize + 2); + GInt32 *panThisLineId = (GInt32 *) VSIMalloc2(sizeof(GInt32),nXSize + 2); + GByte *pabyMaskLine = (hMaskBand != NULL) ? (GByte *) VSIMalloc(nXSize) : NULL; + if (panLastLineVal == NULL || panThisLineVal == NULL || + panLastLineId == NULL || panThisLineId == NULL || + (hMaskBand != NULL && pabyMaskLine == NULL)) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Could not allocate enough memory for temporary buffers"); + CPLFree( panThisLineId ); + CPLFree( panLastLineId ); + CPLFree( panThisLineVal ); + CPLFree( panLastLineVal ); + CPLFree( pabyMaskLine ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Get the geotransform, if there is one, so we can convert the */ +/* vectors into georeferenced coordinates. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hSrcDS = GDALGetBandDataset( hSrcBand ); + double adfGeoTransform[6] = { 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; + + if( hSrcDS ) + GDALGetGeoTransform( hSrcDS, adfGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* The first pass over the raster is only used to build up the */ +/* polygon id map so we will know in advance what polygons are */ +/* what on the second pass. */ +/* -------------------------------------------------------------------- */ + int iY; + GDALRasterPolygonEnumerator oFirstEnum(nConnectedness); + + for( iY = 0; eErr == CE_None && iY < nYSize; iY++ ) + { + eErr = GDALRasterIO( + hSrcBand, + GF_Read, 0, iY, nXSize, 1, + panThisLineVal, nXSize, 1, GDT_Int32, 0, 0 ); + + if( eErr == CE_None && hMaskBand != NULL ) + eErr = GPMaskImageData( hMaskBand, pabyMaskLine, iY, nXSize, panThisLineVal ); + + if( iY == 0 ) + oFirstEnum.ProcessLine( + NULL, panThisLineVal, NULL, panThisLineId, nXSize ); + else + oFirstEnum.ProcessLine( + panLastLineVal, panThisLineVal, + panLastLineId, panThisLineId, + nXSize ); + + // swap lines + GInt32 *panTmp = panLastLineVal; + panLastLineVal = panThisLineVal; + panThisLineVal = panTmp; + + panTmp = panThisLineId; + panThisLineId = panLastLineId; + panLastLineId = panTmp; + +/* -------------------------------------------------------------------- */ +/* Report progress, and support interrupts. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None + && !pfnProgress( 0.10 * ((iY+1) / (double) nYSize), + "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Make a pass through the maps, ensuring every polygon id */ +/* points to the final id it should use, not an intermediate */ +/* value. */ +/* -------------------------------------------------------------------- */ + oFirstEnum.CompleteMerges(); + +/* -------------------------------------------------------------------- */ +/* Initialize ids to -1 to serve as a nodata value for the */ +/* previous line, and past the beginning and end of the */ +/* scanlines. */ +/* -------------------------------------------------------------------- */ + int iX; + + panThisLineId[0] = -1; + panThisLineId[nXSize+1] = -1; + + for( iX = 0; iX < nXSize+2; iX++ ) + panLastLineId[iX] = -1; + +/* -------------------------------------------------------------------- */ +/* We will use a new enumerator for the second pass primariliy */ +/* so we can preserve the first pass map. */ +/* -------------------------------------------------------------------- */ + GDALRasterPolygonEnumerator oSecondEnum(nConnectedness); + RPolygon **papoPoly = (RPolygon **) + CPLCalloc(sizeof(RPolygon*),oFirstEnum.nNextPolygonId); + +/* ==================================================================== */ +/* Second pass during which we will actually collect polygon */ +/* edges as geometries. */ +/* ==================================================================== */ + for( iY = 0; eErr == CE_None && iY < nYSize+1; iY++ ) + { +/* -------------------------------------------------------------------- */ +/* Read the image data. */ +/* -------------------------------------------------------------------- */ + if( iY < nYSize ) + { + eErr = GDALRasterIO( hSrcBand, GF_Read, 0, iY, nXSize, 1, + panThisLineVal, nXSize, 1, GDT_Int32, 0, 0 ); + + if( eErr == CE_None && hMaskBand != NULL ) + eErr = GPMaskImageData( hMaskBand, pabyMaskLine, iY, nXSize, panThisLineVal ); + } + + if( eErr != CE_None ) + continue; + +/* -------------------------------------------------------------------- */ +/* Determine what polygon the various pixels belong to (redoing */ +/* the same thing done in the first pass above). */ +/* -------------------------------------------------------------------- */ + if( iY == nYSize ) + { + for( iX = 0; iX < nXSize+2; iX++ ) + panThisLineId[iX] = -1; + } + else if( iY == 0 ) + oSecondEnum.ProcessLine( + NULL, panThisLineVal, NULL, panThisLineId+1, nXSize ); + else + oSecondEnum.ProcessLine( + panLastLineVal, panThisLineVal, + panLastLineId+1, panThisLineId+1, + nXSize ); + +/* -------------------------------------------------------------------- */ +/* Add polygon edges to our polygon list for the pixel */ +/* boundaries within and above this line. */ +/* -------------------------------------------------------------------- */ + for( iX = 0; iX < nXSize+1; iX++ ) + { + AddEdges( panThisLineId, panLastLineId, + oFirstEnum.panPolyIdMap, oFirstEnum.panPolyValue, + papoPoly, iX, iY ); + } + +/* -------------------------------------------------------------------- */ +/* Periodically we scan out polygons and write out those that */ +/* haven't been added to on the last line as we can be sure */ +/* they are complete. */ +/* -------------------------------------------------------------------- */ + if( iY % 8 == 7 ) + { + for( iX = 0; + eErr == CE_None && iX < oSecondEnum.nNextPolygonId; + iX++ ) + { + if( papoPoly[iX] && papoPoly[iX]->nLastLineUpdated < iY-1 ) + { + eErr = + EmitPolygonToLayer( hOutLayer, iPixValField, + papoPoly[iX], adfGeoTransform ); + + delete papoPoly[iX]; + papoPoly[iX] = NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Swap pixel value, and polygon id lines to be ready for the */ +/* next line. */ +/* -------------------------------------------------------------------- */ + GInt32 *panTmp = panLastLineVal; + panLastLineVal = panThisLineVal; + panThisLineVal = panTmp; + + panTmp = panThisLineId; + panThisLineId = panLastLineId; + panLastLineId = panTmp; + +/* -------------------------------------------------------------------- */ +/* Report progress, and support interrupts. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None + && !pfnProgress( 0.10 + 0.90 * ((iY+1) / (double) nYSize), + "", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Make a cleanup pass for all unflushed polygons. */ +/* -------------------------------------------------------------------- */ + for( iX = 0; eErr == CE_None && iX < oSecondEnum.nNextPolygonId; iX++ ) + { + if( papoPoly[iX] ) + { + eErr = + EmitPolygonToLayer( hOutLayer, iPixValField, + papoPoly[iX], adfGeoTransform ); + + delete papoPoly[iX]; + papoPoly[iX] = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( panThisLineId ); + CPLFree( panLastLineId ); + CPLFree( panThisLineVal ); + CPLFree( panLastLineVal ); + CPLFree( pabyMaskLine ); + CPLFree( papoPoly ); + + return eErr; +#endif // OGR_ENABLED +} + diff --git a/bazaar/plugin/gdal/alg/rasterfill.cpp b/bazaar/plugin/gdal/alg/rasterfill.cpp new file mode 100644 index 000000000..e76f1032a --- /dev/null +++ b/bazaar/plugin/gdal/alg/rasterfill.cpp @@ -0,0 +1,886 @@ +/****************************************************************************** + * $Id: rasterfill.cpp 28342 2015-01-22 12:31:07Z rouault $ + * + * Project: GDAL + * Purpose: Interpolate in nodata areas. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * Copyright (c) 2015, Sean Gillies + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ***************************************************************************/ + +#include "gdal_alg.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: rasterfill.cpp 28342 2015-01-22 12:31:07Z rouault $"); + +/************************************************************************/ +/* GDALFilterLine() */ +/* */ +/* Apply 3x3 filtering one one scanline with masking for which */ +/* pixels are to be interpolated (ThisFMask) and which window */ +/* pixels are valid to include in the interpolation (TMask). */ +/************************************************************************/ + +static void +GDALFilterLine( float *pafLastLine, float *pafThisLine, float *pafNextLine, + float *pafOutLine, + GByte *pabyLastTMask, GByte *pabyThisTMask, GByte*pabyNextTMask, + GByte *pabyThisFMask, int nXSize ) + +{ + int iX; + + for( iX = 0; iX < nXSize; iX++ ) + { + if( !pabyThisFMask[iX] ) + { + pafOutLine[iX] = pafThisLine[iX]; + continue; + } + + CPLAssert( pabyThisTMask[iX] ); + + double dfValSum = 0.0; + double dfWeightSum = 0.0; + + // Previous line + if( pafLastLine != NULL ) + { + if( iX > 0 && pabyLastTMask[iX-1] ) + { + dfValSum += pafLastLine[iX-1]; + dfWeightSum += 1.0; + } + if( pabyLastTMask[iX] ) + { + dfValSum += pafLastLine[iX]; + dfWeightSum += 1.0; + } + if( iX < nXSize-1 && pabyLastTMask[iX+1] ) + { + dfValSum += pafLastLine[iX+1]; + dfWeightSum += 1.0; + } + } + + // Current Line + if( iX > 0 && pabyThisTMask[iX-1] ) + { + dfValSum += pafThisLine[iX-1]; + dfWeightSum += 1.0; + } + if( pabyThisTMask[iX] ) + { + dfValSum += pafThisLine[iX]; + dfWeightSum += 1.0; + } + if( iX < nXSize-1 && pabyThisTMask[iX+1] ) + { + dfValSum += pafThisLine[iX+1]; + dfWeightSum += 1.0; + } + + // Next line + if( pafNextLine != NULL ) + { + if( iX > 0 && pabyNextTMask[iX-1] ) + { + dfValSum += pafNextLine[iX-1]; + dfWeightSum += 1.0; + } + if( pabyNextTMask[iX] ) + { + dfValSum += pafNextLine[iX]; + dfWeightSum += 1.0; + } + if( iX < nXSize-1 && pabyNextTMask[iX+1] ) + { + dfValSum += pafNextLine[iX+1]; + dfWeightSum += 1.0; + } + } + + pafOutLine[iX] = (float) (dfValSum / dfWeightSum); + } +} + +/************************************************************************/ +/* GDALMultiFilter() */ +/* */ +/* Apply multiple iterations of a 3x3 smoothing filter over a */ +/* band with masking controlling what pixels should be */ +/* filtered (FiltMaskBand non zero) and which pixels can be */ +/* considered valid contributors to the filter */ +/* (TargetMaskBand non zero). */ +/* */ +/* This implementation attempts to apply many iterations in */ +/* one IO pass by managing the filtering over a rolling buffer */ +/* of nIternations+2 scanlines. While possibly clever this */ +/* makes the algorithm implementation largely */ +/* incomprehensible. */ +/************************************************************************/ + +static CPLErr +GDALMultiFilter( GDALRasterBandH hTargetBand, + GDALRasterBandH hTargetMaskBand, + GDALRasterBandH hFiltMaskBand, + int nIterations, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + float *paf3PassLineBuf; + GByte *pabyTMaskBuf; + GByte *pabyFMaskBuf; + float *pafThisPass, *pafLastPass, *pafSLastPass; + + int nBufLines = nIterations + 2; + int iPassCounter = 0; + int nNewLine; // the line being loaded this time (zero based scanline) + int nXSize = GDALGetRasterBandXSize( hTargetBand ); + int nYSize = GDALGetRasterBandYSize( hTargetBand ); + CPLErr eErr = CE_None; + +/* -------------------------------------------------------------------- */ +/* Report starting progress value. */ +/* -------------------------------------------------------------------- */ + if( !pfnProgress( 0.0, "Smoothing Filter...", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Allocate rotating buffers. */ +/* -------------------------------------------------------------------- */ + pabyTMaskBuf = (GByte *) VSIMalloc2(nXSize, nBufLines); + pabyFMaskBuf = (GByte *) VSIMalloc2(nXSize, nBufLines); + + paf3PassLineBuf = (float *) VSIMalloc3(nXSize, nBufLines, 3 * sizeof(float)); + if (pabyTMaskBuf == NULL || pabyFMaskBuf == NULL || paf3PassLineBuf == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Could not allocate enough memory for temporary buffers"); + eErr = CE_Failure; + goto end; + } + +/* -------------------------------------------------------------------- */ +/* Process rotating buffers. */ +/* -------------------------------------------------------------------- */ + for( nNewLine = 0; + eErr == CE_None && nNewLine < nYSize+nIterations; + nNewLine++ ) + { +/* -------------------------------------------------------------------- */ +/* Rotate pass buffers. */ +/* -------------------------------------------------------------------- */ + iPassCounter = (iPassCounter + 1) % 3; + + pafSLastPass = paf3PassLineBuf + + ((iPassCounter+0)%3) * nXSize*nBufLines; + pafLastPass = paf3PassLineBuf + + ((iPassCounter+1)%3) * nXSize*nBufLines; + pafThisPass = paf3PassLineBuf + + ((iPassCounter+2)%3) * nXSize*nBufLines; + +/* -------------------------------------------------------------------- */ +/* Where does the new line go in the rotating buffer? */ +/* -------------------------------------------------------------------- */ + int iBufOffset = nNewLine % nBufLines; + +/* -------------------------------------------------------------------- */ +/* Read the new data line if it is't off the bottom of the */ +/* image. */ +/* -------------------------------------------------------------------- */ + if( nNewLine < nYSize ) + { + eErr = + GDALRasterIO( hTargetMaskBand, GF_Read, + 0, nNewLine, nXSize, 1, + pabyTMaskBuf + nXSize * iBufOffset, nXSize, 1, + GDT_Byte, 0, 0 ); + + if( eErr != CE_None ) + break; + + eErr = + GDALRasterIO( hFiltMaskBand, GF_Read, + 0, nNewLine, nXSize, 1, + pabyFMaskBuf + nXSize * iBufOffset, nXSize, 1, + GDT_Byte, 0, 0 ); + + if( eErr != CE_None ) + break; + + eErr = + GDALRasterIO( hTargetBand, GF_Read, + 0, nNewLine, nXSize, 1, + pafThisPass + nXSize * iBufOffset, nXSize, 1, + GDT_Float32, 0, 0 ); + + if( eErr != CE_None ) + break; + } + +/* -------------------------------------------------------------------- */ +/* Loop over the loaded data, applying the filter to all loaded */ +/* lines with neighbours. */ +/* -------------------------------------------------------------------- */ + int iFLine; + + for( iFLine = nNewLine-1; + eErr == CE_None && iFLine >= nNewLine-nIterations; + iFLine-- ) + { + int iLastOffset, iThisOffset, iNextOffset; + + iLastOffset = (iFLine-1) % nBufLines; + iThisOffset = (iFLine ) % nBufLines; + iNextOffset = (iFLine+1) % nBufLines; + + // default to preserving the old value. + if( iFLine >= 0 ) + memcpy( pafThisPass + iThisOffset * nXSize, + pafLastPass + iThisOffset * nXSize, + sizeof(float) * nXSize ); + + // currently this skips the first and last line. Eventually + // we will enable these too. TODO + if( iFLine < 1 || iFLine >= nYSize-1 ) + { + continue; + } + + GDALFilterLine( + pafSLastPass + iLastOffset * nXSize, + pafLastPass + iThisOffset * nXSize, + pafThisPass + iNextOffset * nXSize, + pafThisPass + iThisOffset * nXSize, + pabyTMaskBuf + iLastOffset * nXSize, + pabyTMaskBuf + iThisOffset * nXSize, + pabyTMaskBuf + iNextOffset * nXSize, + pabyFMaskBuf + iThisOffset * nXSize, + nXSize ); + } + +/* -------------------------------------------------------------------- */ +/* Write out the top data line that will be rolling out of our */ +/* buffer. */ +/* -------------------------------------------------------------------- */ + int iLineToSave = nNewLine - nIterations; + + if( iLineToSave >= 0 && eErr == CE_None ) + { + iBufOffset = iLineToSave % nBufLines; + + eErr = + GDALRasterIO( hTargetBand, GF_Write, + 0, iLineToSave, nXSize, 1, + pafThisPass + nXSize * iBufOffset, nXSize, 1, + GDT_Float32, 0, 0 ); + } + +/* -------------------------------------------------------------------- */ +/* Report progress. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None + && !pfnProgress( (nNewLine+1) / (double) (nYSize+nIterations), + "Smoothing Filter...", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ +end: + CPLFree( pabyTMaskBuf ); + CPLFree( pabyFMaskBuf ); + CPLFree( paf3PassLineBuf ); + + return eErr; +} + +/************************************************************************/ +/* QUAD_CHECK() */ +/* */ +/* macro for checking whether a point is nearer than the */ +/* existing closest point. */ +/************************************************************************/ +#define QUAD_CHECK(quad_dist, quad_value, \ +target_x, target_y, origin_x, origin_y, target_value ) \ + \ +if( quad_value != nNoDataVal ) \ +{ \ + double dfDx = (double)target_x - (double)origin_x; \ + double dfDy = (double)target_y - (double)origin_y; \ + double dfDistSq = dfDx * dfDx + dfDy * dfDy; \ + \ + if( dfDistSq < quad_dist*quad_dist ) \ + { \ + CPLAssert( dfDistSq > 0.0 ); \ + quad_dist = sqrt(dfDistSq); \ + quad_value = target_value; \ + } \ +} + +/************************************************************************/ +/* GDALFillNodata() */ +/************************************************************************/ + +/** + * Fill selected raster regions by interpolation from the edges. + * + * This algorithm will interpolate values for all designated + * nodata pixels (marked by zeros in hMaskBand). For each pixel + * a four direction conic search is done to find values to interpolate + * from (using inverse distance weighting). Once all values are + * interpolated, zero or more smoothing iterations (3x3 average + * filters on interpolated pixels) are applied to smooth out + * artifacts. + * + * This algorithm is generally suitable for interpolating missing + * regions of fairly continuously varying rasters (such as elevation + * models for instance). It is also suitable for filling small holes + * and cracks in more irregularly varying images (like airphotos). It + * is generally not so great for interpolating a raster from sparse + * point data - see the algorithms defined in gdal_grid.h for that case. + * + * @param hTargetBand the raster band to be modified in place. + * @param hMaskBand a mask band indicating pixels to be interpolated (zero valued + * @param dfMaxSearchDist the maximum number of pixels to search in all + * directions to find values to interpolate from. + * @param bDeprecatedOption unused argument, should be zero. + * @param nSmoothingIterations the number of 3x3 smoothing filter passes to + * run (0 or more). + * @param papszOptions additional name=value options in a string list (the + * temporary file driver can be specified like TEMP_FILE_DRIVER=MEM). + * @param pfnProgress the progress function to report completion. + * @param pProgressArg callback data for progress function. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr CPL_STDCALL +GDALFillNodata( GDALRasterBandH hTargetBand, + GDALRasterBandH hMaskBand, + double dfMaxSearchDist, + int bDeprecatedOption, + int nSmoothingIterations, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( hTargetBand, "GDALFillNodata", CE_Failure ); + + int nXSize = GDALGetRasterBandXSize( hTargetBand ); + int nYSize = GDALGetRasterBandYSize( hTargetBand ); + CPLErr eErr = CE_None; + + // Special "x" pixel values identifying pixels as special. + GUInt32 nNoDataVal; + GDALDataType eType; + + if( dfMaxSearchDist == 0.0 ) + dfMaxSearchDist = MAX(nXSize,nYSize) + 1; + + int nMaxSearchDist = (int) floor(dfMaxSearchDist); + + if( nXSize > 65533 || nYSize > 65533 ) + { + eType = GDT_UInt32; + nNoDataVal = 4000002; + } + else + { + eType = GDT_UInt16; + nNoDataVal = 65535; + } + + if( hMaskBand == NULL ) + hMaskBand = GDALGetMaskBand( hTargetBand ); + + /* If there are smoothing iterations, reserve 10% of the progress for them */ + double dfProgressRatio = (nSmoothingIterations > 0) ? 0.9 : 1.0; + +/* -------------------------------------------------------------------- */ +/* Initialize progress counter. */ +/* -------------------------------------------------------------------- */ + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + if( !pfnProgress( 0.0, "Filling...", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Determine format driver for temp work files. */ +/* -------------------------------------------------------------------- */ + CPLString osTmpFileDriver = CSLFetchNameValueDef( + papszOptions, "TEMP_FILE_DRIVER", "GTiff"); + GDALDriverH hDriver = GDALGetDriverByName((const char *) osTmpFileDriver); + + if (hDriver == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Given driver is not registered"); + return CE_Failure; + } + + if (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, NULL) == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Given driver is incapable of creating temp work files"); + return CE_Failure; + } + + char **papszWorkFileOptions = NULL; + if (osTmpFileDriver == "GTiff") { + papszWorkFileOptions = CSLSetNameValue( + papszWorkFileOptions, "COMPRESS", "LZW"); + papszWorkFileOptions = CSLSetNameValue( + papszWorkFileOptions, "BIGTIFF", "IF_SAFER"); + } + +/* -------------------------------------------------------------------- */ +/* Create a work file to hold the Y "last value" indices. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hYDS; + GDALRasterBandH hYBand; + + CPLString osTmpFile = CPLGenerateTempFilename(""); + CPLString osYTmpFile = osTmpFile + "fill_y_work.tif"; + + hYDS = GDALCreate( hDriver, osYTmpFile, nXSize, nYSize, 1, + eType, (char **) papszWorkFileOptions ); + + if ( hYDS == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Could not create Y index work file. Check driver capabilities."); + return CE_Failure; + } + + hYBand = GDALGetRasterBand( hYDS, 1 ); + +/* -------------------------------------------------------------------- */ +/* Create a work file to hold the pixel value associated with */ +/* the "last xy value" pixel. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hValDS; + GDALRasterBandH hValBand; + CPLString osValTmpFile = osTmpFile + "fill_val_work.tif"; + + hValDS = GDALCreate( hDriver, osValTmpFile, nXSize, nYSize, 1, + GDALGetRasterDataType( hTargetBand ), + (char **) papszWorkFileOptions ); + + if ( hValDS == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Could not create XY value work file. Check driver capabilities."); + return CE_Failure; + } + + hValBand = GDALGetRasterBand( hValDS, 1 ); + +/* -------------------------------------------------------------------- */ +/* Create a mask file to make it clear what pixels can be filtered */ +/* on the filtering pass. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hFiltMaskDS; + GDALRasterBandH hFiltMaskBand; + CPLString osFiltMaskTmpFile = osTmpFile + "fill_filtmask_work.tif"; + + hFiltMaskDS = + GDALCreate( hDriver, osFiltMaskTmpFile, nXSize, nYSize, 1, + GDT_Byte, (char **) papszWorkFileOptions ); + + if ( hFiltMaskDS == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Could not create mask work file. Check driver capabilities."); + return CE_Failure; + } + + hFiltMaskBand = GDALGetRasterBand( hFiltMaskDS, 1 ); + +/* -------------------------------------------------------------------- */ +/* Allocate buffers for last scanline and this scanline. */ +/* -------------------------------------------------------------------- */ + GUInt32 *panLastY, *panThisY, *panTopDownY; + float *pafLastValue, *pafThisValue, *pafScanline, *pafTopDownValue; + GByte *pabyMask, *pabyFiltMask; + int iX; + int iY; + + panLastY = (GUInt32 *) VSICalloc(nXSize,sizeof(GUInt32)); + panThisY = (GUInt32 *) VSICalloc(nXSize,sizeof(GUInt32)); + panTopDownY = (GUInt32 *) VSICalloc(nXSize,sizeof(GUInt32)); + pafLastValue = (float *) VSICalloc(nXSize,sizeof(float)); + pafThisValue = (float *) VSICalloc(nXSize,sizeof(float)); + pafTopDownValue = (float *) VSICalloc(nXSize,sizeof(float)); + pafScanline = (float *) VSICalloc(nXSize,sizeof(float)); + pabyMask = (GByte *) VSICalloc(nXSize,1); + pabyFiltMask = (GByte *) VSICalloc(nXSize,1); + if (panLastY == NULL || panThisY == NULL || panTopDownY == NULL || + pafLastValue == NULL || pafThisValue == NULL || pafTopDownValue == NULL || + pafScanline == NULL || pabyMask == NULL || pabyFiltMask == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Could not allocate enough memory for temporary buffers"); + + eErr = CE_Failure; + goto end; + } + + for( iX = 0; iX < nXSize; iX++ ) + { + panLastY[iX] = nNoDataVal; + } + +/* ==================================================================== */ +/* Make first pass from top to bottom collecting the "last */ +/* known value" for each column and writing it out to the work */ +/* files. */ +/* ==================================================================== */ + + for( iY = 0; iY < nYSize && eErr == CE_None; iY++ ) + { +/* -------------------------------------------------------------------- */ +/* Read data and mask for this line. */ +/* -------------------------------------------------------------------- */ + eErr = + GDALRasterIO( hMaskBand, GF_Read, 0, iY, nXSize, 1, + pabyMask, nXSize, 1, GDT_Byte, 0, 0 ); + + if( eErr != CE_None ) + break; + + eErr = + GDALRasterIO( hTargetBand, GF_Read, 0, iY, nXSize, 1, + pafScanline, nXSize, 1, GDT_Float32, 0, 0 ); + + if( eErr != CE_None ) + break; + +/* -------------------------------------------------------------------- */ +/* Figure out the most recent pixel for each column. */ +/* -------------------------------------------------------------------- */ + + for( iX = 0; iX < nXSize; iX++ ) + { + if( pabyMask[iX] ) + { + pafThisValue[iX] = pafScanline[iX]; + panThisY[iX] = iY; + } + else if( iY <= dfMaxSearchDist + panLastY[iX] ) + { + pafThisValue[iX] = pafLastValue[iX]; + panThisY[iX] = panLastY[iX]; + } + else + { + panThisY[iX] = nNoDataVal; + } + } + +/* -------------------------------------------------------------------- */ +/* Write out best index/value to working files. */ +/* -------------------------------------------------------------------- */ + eErr = GDALRasterIO( hYBand, GF_Write, 0, iY, nXSize, 1, + panThisY, nXSize, 1, GDT_UInt32, 0, 0 ); + if( eErr != CE_None ) + break; + + eErr = GDALRasterIO( hValBand, GF_Write, 0, iY, nXSize, 1, + pafThisValue, nXSize, 1, GDT_Float32, 0, 0 ); + if( eErr != CE_None ) + break; + +/* -------------------------------------------------------------------- */ +/* Flip this/last buffers. */ +/* -------------------------------------------------------------------- */ + { + float *pafTmp = pafThisValue; + pafThisValue = pafLastValue; + pafLastValue = pafTmp; + + GUInt32 *panTmp = panThisY; + panThisY = panLastY; + panLastY = panTmp; + } + +/* -------------------------------------------------------------------- */ +/* report progress. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None + && !pfnProgress( dfProgressRatio * (0.5*(iY+1) / (double)nYSize), + "Filling...", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* ==================================================================== */ +/* Now we will do collect similar this/last information from */ +/* bottom to top and use it in combination with the top to */ +/* bottom search info to interpolate. */ +/* ==================================================================== */ + for( iY = nYSize-1; iY >= 0 && eErr == CE_None; iY-- ) + { + eErr = + GDALRasterIO( hMaskBand, GF_Read, 0, iY, nXSize, 1, + pabyMask, nXSize, 1, GDT_Byte, 0, 0 ); + + if( eErr != CE_None ) + break; + + eErr = + GDALRasterIO( hTargetBand, GF_Read, 0, iY, nXSize, 1, + pafScanline, nXSize, 1, GDT_Float32, 0, 0 ); + + if( eErr != CE_None ) + break; + +/* -------------------------------------------------------------------- */ +/* Figure out the most recent pixel for each column. */ +/* -------------------------------------------------------------------- */ + + for( iX = 0; iX < nXSize; iX++ ) + { + if( pabyMask[iX] ) + { + pafThisValue[iX] = pafScanline[iX]; + panThisY[iX] = iY; + } + else if( panLastY[iX] - iY <= dfMaxSearchDist ) + { + pafThisValue[iX] = pafLastValue[iX]; + panThisY[iX] = panLastY[iX]; + } + else + { + panThisY[iX] = nNoDataVal; + } + } + +/* -------------------------------------------------------------------- */ +/* Load the last y and corresponding value from the top down pass. */ +/* -------------------------------------------------------------------- */ + eErr = + GDALRasterIO( hYBand, GF_Read, 0, iY, nXSize, 1, + panTopDownY, nXSize, 1, GDT_UInt32, 0, 0 ); + + if( eErr != CE_None ) + break; + + eErr = + GDALRasterIO( hValBand, GF_Read, 0, iY, nXSize, 1, + pafTopDownValue, nXSize, 1, GDT_Float32, 0, 0 ); + + if( eErr != CE_None ) + break; + +/* -------------------------------------------------------------------- */ +/* Attempt to interpolate any pixels that are nodata. */ +/* -------------------------------------------------------------------- */ + memset( pabyFiltMask, 0, nXSize ); + for( iX = 0; iX < nXSize; iX++ ) + { + int iStep, iQuad; + int nThisMaxSearchDist = nMaxSearchDist; + + // If this was a valid target - no change. + if( pabyMask[iX] ) + continue; + + // Quadrants 0:topleft, 1:bottomleft, 2:topright, 3:bottomright + double adfQuadDist[4]; + double adfQuadValue[4]; + + for( iQuad = 0; iQuad < 4; iQuad++ ) + { + adfQuadDist[iQuad] = dfMaxSearchDist + 1.0; + adfQuadValue[iQuad] = 0.0; + } + + // Step left and right by one pixel searching for the closest + // target value for each quadrant. + for( iStep = 0; iStep < nThisMaxSearchDist; iStep++ ) + { + int iLeftX = MAX(0,iX - iStep); + int iRightX = MIN(nXSize-1,iX + iStep); + + // top left includes current line + QUAD_CHECK(adfQuadDist[0],adfQuadValue[0], + iLeftX, panTopDownY[iLeftX], iX, iY, + pafTopDownValue[iLeftX] ); + + // bottom left + QUAD_CHECK(adfQuadDist[1],adfQuadValue[1], + iLeftX, panLastY[iLeftX], iX, iY, + pafLastValue[iLeftX] ); + + // top right and bottom right do no include center pixel. + if( iStep == 0 ) + continue; + + // top right includes current line + QUAD_CHECK(adfQuadDist[2],adfQuadValue[2], + iRightX, panTopDownY[iRightX], iX, iY, + pafTopDownValue[iRightX] ); + + // bottom right + QUAD_CHECK(adfQuadDist[3],adfQuadValue[3], + iRightX, panLastY[iRightX], iX, iY, + pafLastValue[iRightX] ); + + // every four steps, recompute maximum distance. + if( (iStep & 0x3) == 0 ) + nThisMaxSearchDist = (int) floor( + MAX(MAX(adfQuadDist[0],adfQuadDist[1]), + MAX(adfQuadDist[2],adfQuadDist[3])) ); + } + + double dfWeightSum = 0.0; + double dfValueSum = 0.0; + + for( iQuad = 0; iQuad < 4; iQuad++ ) + { + if( adfQuadDist[iQuad] <= dfMaxSearchDist ) + { + double dfWeight = 1.0 / adfQuadDist[iQuad]; + + dfWeightSum += dfWeight; + dfValueSum += adfQuadValue[iQuad] * dfWeight; + } + } + + if( dfWeightSum > 0.0 ) + { + pabyMask[iX] = 255; + pabyFiltMask[iX] = 255; + pafScanline[iX] = (float) (dfValueSum / dfWeightSum); + } + + } + +/* -------------------------------------------------------------------- */ +/* Write out the updated data and mask information. */ +/* -------------------------------------------------------------------- */ + eErr = + GDALRasterIO( hTargetBand, GF_Write, 0, iY, nXSize, 1, + pafScanline, nXSize, 1, GDT_Float32, 0, 0 ); + + if( eErr != CE_None ) + break; + + eErr = + GDALRasterIO( hFiltMaskBand, GF_Write, 0, iY, nXSize, 1, + pabyFiltMask, nXSize, 1, GDT_Byte, 0, 0 ); + + if( eErr != CE_None ) + break; + +/* -------------------------------------------------------------------- */ +/* Flip this/last buffers. */ +/* -------------------------------------------------------------------- */ + { + float *pafTmp = pafThisValue; + pafThisValue = pafLastValue; + pafLastValue = pafTmp; + + GUInt32 *panTmp = panThisY; + panThisY = panLastY; + panLastY = panTmp; + } + +/* -------------------------------------------------------------------- */ +/* report progress. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None + && !pfnProgress( dfProgressRatio*(0.5+0.5*(nYSize-iY) / (double)nYSize), + "Filling...", pProgressArg ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + } + +/* ==================================================================== */ +/* Now we will do iterative average filters over the */ +/* interpolated values to smooth things out and make linear */ +/* artifacts less obvious. */ +/* ==================================================================== */ + if( eErr == CE_None && nSmoothingIterations > 0 ) + { + // force masks to be to flushed and recomputed. + GDALFlushRasterCache( hMaskBand ); + + void *pScaledProgress; + pScaledProgress = + GDALCreateScaledProgress( dfProgressRatio, 1.0, pfnProgress, NULL ); + + eErr = GDALMultiFilter( hTargetBand, hMaskBand, hFiltMaskBand, + nSmoothingIterations, + GDALScaledProgress, pScaledProgress ); + + GDALDestroyScaledProgress( pScaledProgress ); + } + +/* -------------------------------------------------------------------- */ +/* Close and clean up temporary files. Free working buffers */ +/* -------------------------------------------------------------------- */ +end: + CPLFree(panLastY); + CPLFree(panThisY); + CPLFree(panTopDownY); + CPLFree(pafLastValue); + CPLFree(pafThisValue); + CPLFree(pafTopDownValue); + CPLFree(pafScanline); + CPLFree(pabyMask); + CPLFree(pabyFiltMask); + + GDALClose( hYDS ); + GDALClose( hValDS ); + GDALClose( hFiltMaskDS ); + + CSLDestroy(papszWorkFileOptions); + + GDALDeleteDataset( hDriver, osYTmpFile ); + GDALDeleteDataset( hDriver, osValTmpFile ); + GDALDeleteDataset( hDriver, osFiltMaskTmpFile ); + + return eErr; +} diff --git a/bazaar/plugin/gdal/alg/thinplatespline.cpp b/bazaar/plugin/gdal/alg/thinplatespline.cpp new file mode 100644 index 000000000..5bbb5abde --- /dev/null +++ b/bazaar/plugin/gdal/alg/thinplatespline.cpp @@ -0,0 +1,812 @@ +/****************************************************************************** + * $Id: thinplatespline.cpp 27546 2014-07-22 22:40:02Z rouault $ + * + * Project: GDAL Warp API + * Purpose: Implemenentation of 2D Thin Plate Spline transformer. + * Author: VIZRT Development Team. + * + * This code was provided by Gilad Ronnen (gro at visrt dot com) with + * permission to reuse under the following license. + * + ****************************************************************************** + * Copyright (c) 2004, VIZRT Inc. + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifdef HAVE_ARMADILLO +/* Include before #define A(r,c) because armadillo uses A in its include files */ +#include "armadillo" +#endif + +#include "thinplatespline.h" + +///////////////////////////////////////////////////////////////////////////////////// +//// vizGeorefSpline2D +///////////////////////////////////////////////////////////////////////////////////// + +#define A(r,c) _AA[ _nof_eqs * (r) + (c) ] +#define Ainv(r,c) _Ainv[ _nof_eqs * (r) + (c) ] + + +#define VIZ_GEOREF_SPLINE_DEBUG 0 + +#ifndef HAVE_ARMADILLO +static int matrixInvert( int N, double input[], double output[] ); +#endif + +void VizGeorefSpline2D::grow_points() + +{ + int new_max = _max_nof_points*2 + 2 + 3; + int i; + + x = (double *) VSIRealloc( x, sizeof(double) * new_max ); + y = (double *) VSIRealloc( y, sizeof(double) * new_max ); + u = (double *) VSIRealloc( u, sizeof(double) * new_max ); + unused = (int *) VSIRealloc( unused, sizeof(int) * new_max ); + index = (int *) VSIRealloc( index, sizeof(int) * new_max ); + for( i = 0; i < VIZGEOREF_MAX_VARS; i++ ) + { + rhs[i] = (double *) + VSIRealloc( rhs[i], sizeof(double) * new_max ); + coef[i] = (double *) + VSIRealloc( coef[i], sizeof(double) * new_max ); + if( _max_nof_points == 0 ) + { + memset(rhs[i], 0, 3 * sizeof(double)); + memset(coef[i], 0, 3 * sizeof(double)); + } + } + + _max_nof_points = new_max - 3; +} + +int VizGeorefSpline2D::add_point( const double Px, const double Py, const double *Pvars ) +{ + type = VIZ_GEOREF_SPLINE_POINT_WAS_ADDED; + int i; + + if( _nof_points == _max_nof_points ) + grow_points(); + + i = _nof_points; + //A new point is added + x[i] = Px; + y[i] = Py; + for ( int j = 0; j < _nof_vars; j++ ) + rhs[j][i+3] = Pvars[j]; + _nof_points++; + return 1; +} + +#if 0 +bool VizGeorefSpline2D::change_point(int index, double Px, double Py, double* Pvars) +{ + if ( index < _nof_points ) + { + int i = index; + x[i] = Px; + y[i] = Py; + for ( int j = 0; j < _nof_vars; j++ ) + rhs[j][i+3] = Pvars[j]; + } + + return( true ); +} + +bool VizGeorefSpline2D::get_xy(int index, double& outX, double& outY) +{ + bool ok; + + if ( index < _nof_points ) + { + ok = true; + outX = x[index]; + outY = y[index]; + } + else + { + ok = false; + outX = outY = 0.0f; + } + + return(ok); +} + +int VizGeorefSpline2D::delete_point(const double Px, const double Py ) +{ + for ( int i = 0; i < _nof_points; i++ ) + { + if ( ( fabs(Px - x[i]) <= _tx ) && ( fabs(Py - y[i]) <= _ty ) ) + { + for ( int j = i; j < _nof_points - 1; j++ ) + { + x[j] = x[j+1]; + y[j] = y[j+1]; + for ( int k = 0; k < _nof_vars; k++ ) + rhs[k][j+3] = rhs[k][j+3+1]; + } + _nof_points--; + type = VIZ_GEOREF_SPLINE_POINT_WAS_DELETED; + return(1); + } + } + return(0); +} +#endif + +#define SQ(x) ((x)*(x)) + +static CPL_INLINE double VizGeorefSpline2DBase_func( const double x1, const double y1, + const double x2, const double y2 ) +{ + double dist = SQ( x2 - x1 ) + SQ( y2 - y1 ); + return dist ? dist * log( dist ) : 0.0; +} + +#if defined(__GNUC__) && defined(__x86_64__) + +/* Derived and adapted from code originating from: */ + +/* @(#)e_log.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_log(x) + * Return the logrithm of x + * + * Method : + * 1. Argument Reduction: find k and f such that + * x = 2^k * (1+f), + * where sqrt(2)/2 < 1+f < sqrt(2) . + * + * 2. Approximation of log(1+f). + * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) + * = 2s + 2/3 s**3 + 2/5 s**5 + ....., + * = 2s + s*R + * We use a special Reme algorithm on [0,0.1716] to generate + * a polynomial of degree 14 to approximate R The maximum error + * of this polynomial approximation is bounded by 2**-58.45. In + * other words, + * 2 4 6 8 10 12 14 + * R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s + * (the values of Lg1 to Lg7 are listed in the program) + * and + * | 2 14 | -58.45 + * | Lg1*s +...+Lg7*s - R(z) | <= 2 + * | | + * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. + * In order to guarantee error in log below 1ulp, we compute log + * by + * log(1+f) = f - s*(f - R) (if f is not too large) + * log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) + * + * 3. Finally, log(x) = k*ln2 + log(1+f). + * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) + * Here ln2 is split into two floating point number: + * ln2_hi + ln2_lo, + * where n*ln2_hi is always exact for |n| < 2000. + * + * Special cases: + * log(x) is NaN with signal if x < 0 (including -INF) ; + * log(+INF) is +INF; log(0) is -INF with signal; + * log(NaN) is that NaN with no signal. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +typedef double V2DF __attribute__ ((__vector_size__ (16))); +typedef union +{ + V2DF v2; + double d[2]; +} v2dfunion; + +typedef union +{ + int i[2]; + long long li; +} i64union; + +static const V2DF +v2_ln2_div_2pow20 = {6.93147180559945286e-01 / 1048576, 6.93147180559945286e-01 / 1048576}, +v2_Lg1 = {6.666666666666735130e-01, 6.666666666666735130e-01}, +v2_Lg2 = {3.999999999940941908e-01, 3.999999999940941908e-01}, +v2_Lg3 = {2.857142874366239149e-01, 2.857142874366239149e-01}, +v2_Lg4 = {2.222219843214978396e-01, 2.222219843214978396e-01}, +v2_Lg5 = {1.818357216161805012e-01, 1.818357216161805012e-01}, +v2_Lg6 = {1.531383769920937332e-01, 1.531383769920937332e-01}, +/*v2_Lg7 = {1.479819860511658591e-01, 1.479819860511658591e-01}, */ +v2_one = { 1.0, 1.0 }, +v2_const1023_mul_2pow20 = { 1023.0 * 1048576, 1023.0 * 1048576}; + +#define GET_HIGH_WORD(hx,x) memcpy(&hx,((char*)&x+4),4) +#define SET_HIGH_WORD(x,hx) memcpy(((char*)&x+4),&hx,4) + +#define MAKE_WIDE_CST(x) ((((long long)(x)) << 32) | (x)) +static const long long cst_expmask = MAKE_WIDE_CST(0xfff00000); +static const long long cst_0x95f64 = MAKE_WIDE_CST(0x00095f64); +static const long long cst_0x100000 = MAKE_WIDE_CST(0x00100000); +static const long long cst_0x3ff00000 = MAKE_WIDE_CST(0x3ff00000); + +/* Modified version of __ieee754_log(), less precise than log() but a bit */ +/* faste, and computing 4 log() at a time. Assumes that the values are > 0 */ +static void FastApproxLog4Val(v2dfunion* x) +{ + V2DF f[2],s[2],z[2],R[2],w[2],t1[2],t2[2]; + v2dfunion dk[2]; + i64union k[2], hx[2], i[2]; + + GET_HIGH_WORD(hx[0].i[0],x[0].d[0]); + GET_HIGH_WORD(hx[0].i[1],x[0].d[1]); + k[0].li = hx[0].li & cst_expmask; + hx[0].li &= ~cst_expmask; + i[0].li = (hx[0].li + cst_0x95f64) & cst_0x100000; + hx[0].li |= i[0].li ^ cst_0x3ff00000; + SET_HIGH_WORD(x[0].d[0],hx[0].i[0]); /* normalize x or x/2 */ + SET_HIGH_WORD(x[0].d[1],hx[0].i[1]); /* normalize x or x/2 */ + k[0].li += i[0].li; + dk[0].d[0] = (double)k[0].i[0]; + dk[0].d[1] = (double)k[0].i[1]; + + GET_HIGH_WORD(hx[1].i[0],x[1].d[0]); + GET_HIGH_WORD(hx[1].i[1],x[1].d[1]); + k[1].li = hx[1].li & cst_expmask; + hx[1].li &= ~cst_expmask; + i[1].li = (hx[1].li + cst_0x95f64) & cst_0x100000; + hx[1].li |= i[1].li ^ cst_0x3ff00000; + SET_HIGH_WORD(x[1].d[0],hx[1].i[0]); /* normalize x or x/2 */ + SET_HIGH_WORD(x[1].d[1],hx[1].i[1]); /* normalize x or x/2 */ + k[1].li += i[1].li; + dk[1].d[0] = (double)k[1].i[0]; + dk[1].d[1] = (double)k[1].i[1]; + + f[0] = x[0].v2-v2_one; + s[0] = f[0]/(x[0].v2+v2_one); + z[0] = s[0]*s[0]; + w[0] = z[0]*z[0]; + t1[0]= w[0]*(v2_Lg2+w[0]*(v2_Lg4+w[0]*v2_Lg6)); + t2[0]= z[0]*(v2_Lg1+w[0]*(v2_Lg3+w[0]*(v2_Lg5/*+w[0]*v2_Lg7*/))); + R[0] = t2[0]+t1[0]; + x[0].v2 = ((dk[0].v2 - v2_const1023_mul_2pow20)*v2_ln2_div_2pow20-(s[0]*(f[0]-R[0])-f[0])); + + f[1] = x[1].v2-v2_one; + s[1] = f[1]/(x[1].v2+v2_one); + z[1] = s[1]*s[1]; + w[1] = z[1]*z[1]; + t1[1]= w[1]*(v2_Lg2+w[1]*(v2_Lg4+w[1]*v2_Lg6)); + t2[1]= z[1]*(v2_Lg1+w[1]*(v2_Lg3+w[1]*(v2_Lg5/*+w[1]*v2_Lg7*/))); + R[1] = t2[1]+t1[1]; + x[1].v2 = ((dk[1].v2- v2_const1023_mul_2pow20)*v2_ln2_div_2pow20-(s[1]*(f[1]-R[1])-f[1])); +} + +static CPL_INLINE void VizGeorefSpline2DBase_func4( double* res, + const double* pxy, + const double* xr, const double* yr ) +{ + v2dfunion x1v, y1v, xv[2], yv[2], dist[2], resv[2]; + xv[0].d[0] = xr[0]; + xv[0].d[1] = xr[1]; + xv[1].d[0] = xr[2]; + xv[1].d[1] = xr[3]; + yv[0].d[0] = yr[0]; + yv[0].d[1] = yr[1]; + yv[1].d[0] = yr[2]; + yv[1].d[1] = yr[3]; + x1v.d[0] = pxy[0]; + x1v.d[1] = pxy[0]; + y1v.d[0] = pxy[1]; + y1v.d[1] = pxy[1]; + dist[0].v2 = SQ( xv[0].v2 - x1v.v2 ) + SQ( yv[0].v2 - y1v.v2 ); + dist[1].v2 = SQ( xv[1].v2 - x1v.v2 ) + SQ( yv[1].v2 - y1v.v2 ); + resv[0] = dist[0]; + resv[1] = dist[1]; + FastApproxLog4Val(dist); + resv[0].v2 *= dist[0].v2; + resv[1].v2 *= dist[1].v2; + res[0] = resv[0].d[0]; + res[1] = resv[0].d[1]; + res[2] = resv[1].d[0]; + res[3] = resv[1].d[1]; +} +#else +static void VizGeorefSpline2DBase_func4( double* res, + const double* pxy, + const double* xr, const double* yr ) +{ + double dist0 = SQ( xr[0] - pxy[0] ) + SQ( yr[0] - pxy[1] ); + res[0] = dist0 ? dist0 * log(dist0) : 0.0; + double dist1 = SQ( xr[1] - pxy[0] ) + SQ( yr[1] - pxy[1] ); + res[1] = dist1 ? dist1 * log(dist1) : 0.0; + double dist2 = SQ( xr[2] - pxy[0] ) + SQ( yr[2] - pxy[1] ); + res[2] = dist2 ? dist2 * log(dist2) : 0.0; + double dist3 = SQ( xr[3] - pxy[0] ) + SQ( yr[3] - pxy[1] ); + res[3] = dist3 ? dist3 * log(dist3) : 0.0; +} +#endif + +int VizGeorefSpline2D::solve(void) +{ + int r, c; + int p; + + // No points at all + if ( _nof_points < 1 ) + { + type = VIZ_GEOREF_SPLINE_ZERO_POINTS; + return(0); + } + + // Only one point + if ( _nof_points == 1 ) + { + type = VIZ_GEOREF_SPLINE_ONE_POINT; + return(1); + } + // Just 2 points - it is necessarily 1D case + if ( _nof_points == 2 ) + { + _dx = x[1] - x[0]; + _dy = y[1] - y[0]; + double fact = 1.0 / ( _dx * _dx + _dy * _dy ); + _dx *= fact; + _dy *= fact; + + type = VIZ_GEOREF_SPLINE_TWO_POINTS; + return(2); + } + + // More than 2 points - first we have to check if it is 1D or 2D case + + double xmax = x[0], xmin = x[0], ymax = y[0], ymin = y[0]; + double delx, dely; + double xx, yy; + double sumx = 0.0f, sumy= 0.0f, sumx2 = 0.0f, sumy2 = 0.0f, sumxy = 0.0f; + double SSxx, SSyy, SSxy; + + for ( p = 0; p < _nof_points; p++ ) + { + xx = x[p]; + yy = y[p]; + + xmax = MAX( xmax, xx ); + xmin = MIN( xmin, xx ); + ymax = MAX( ymax, yy ); + ymin = MIN( ymin, yy ); + + sumx += xx; + sumx2 += xx * xx; + sumy += yy; + sumy2 += yy * yy; + sumxy += xx * yy; + } + delx = xmax - xmin; + dely = ymax - ymin; + + SSxx = sumx2 - sumx * sumx / _nof_points; + SSyy = sumy2 - sumy * sumy / _nof_points; + SSxy = sumxy - sumx * sumy / _nof_points; + + if ( delx < 0.001 * dely || dely < 0.001 * delx || + fabs ( SSxy * SSxy / ( SSxx * SSyy ) ) > 0.99 ) + { + int p1; + + type = VIZ_GEOREF_SPLINE_ONE_DIMENSIONAL; + + _dx = _nof_points * sumx2 - sumx * sumx; + _dy = _nof_points * sumy2 - sumy * sumy; + double fact = 1.0 / sqrt( _dx * _dx + _dy * _dy ); + _dx *= fact; + _dy *= fact; + + for ( p = 0; p < _nof_points; p++ ) + { + double dxp = x[p] - x[0]; + double dyp = y[p] - y[0]; + u[p] = _dx * dxp + _dy * dyp; + unused[p] = 1; + } + + for ( p = 0; p < _nof_points; p++ ) + { + int min_index = -1; + double min_u = 0; + for ( p1 = 0; p1 < _nof_points; p1++ ) + { + if ( unused[p1] ) + { + if ( min_index < 0 || u[p1] < min_u ) + { + min_index = p1; + min_u = u[p1]; + } + } + } + index[p] = min_index; + unused[min_index] = 0; + } + + return(3); + } + + type = VIZ_GEOREF_SPLINE_FULL; + // Make the necessary memory allocations + + _nof_eqs = _nof_points + 3; + + if( _nof_eqs > INT_MAX / _nof_eqs ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Too many coefficients. Computation aborted."); + return 0; + } + + double* _AA = ( double * )VSICalloc( _nof_eqs * _nof_eqs, sizeof( double ) ); + double* _Ainv = ( double * )VSICalloc( _nof_eqs * _nof_eqs, sizeof( double ) ); + + if( _AA == NULL || _Ainv == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Out-of-memory while allocating temporary arrays. Computation aborted."); + VSIFree(_AA); + VSIFree(_Ainv); + return 0; + } + + // Calc the values of the matrix A + for ( r = 0; r < 3; r++ ) + for ( c = 0; c < 3; c++ ) + A(r,c) = 0.0; + + for ( c = 0; c < _nof_points; c++ ) + { + A(0,c+3) = 1.0; + A(1,c+3) = x[c]; + A(2,c+3) = y[c]; + + A(c+3,0) = 1.0; + A(c+3,1) = x[c]; + A(c+3,2) = y[c]; + } + + for ( r = 0; r < _nof_points; r++ ) + for ( c = r; c < _nof_points; c++ ) + { + A(r+3,c+3) = VizGeorefSpline2DBase_func( x[r], y[r], x[c], y[c] ); + if ( r != c ) + A(c+3,r+3 ) = A(r+3,c+3); + } + +#if VIZ_GEOREF_SPLINE_DEBUG + + for ( r = 0; r < _nof_eqs; r++ ) + { + for ( c = 0; c < _nof_eqs; c++ ) + fprintf(stderr, "%f", A(r,c)); + fprintf(stderr, "\n"); + } + +#endif + + int ret = 4; +#ifdef HAVE_ARMADILLO + try + { + arma::mat matA(_AA,_nof_eqs,_nof_eqs,false); + arma::mat matRHS(_nof_eqs, _nof_vars); + int row, col; + for(row = 0; row < _nof_eqs; row++) + for(col = 0; col < _nof_vars; col++) + matRHS.at(row, col) = rhs[col][row]; + arma::mat matCoefs(_nof_vars, _nof_eqs); + if( !arma::solve(matCoefs, matA, matRHS) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "There is a problem to invert the interpolation matrix."); + ret = 0; + } + else + { + for(row = 0; row < _nof_eqs; row++) + for(col = 0; col < _nof_vars; col++) + coef[col][row] = matCoefs.at(row, col); + } + } + catch(...) + { + CPLError(CE_Failure, CPLE_AppDefined, "There is a problem to invert the interpolation matrix."); + ret = 0; + } +#else + // Invert the matrix + int status = matrixInvert( _nof_eqs, _AA, _Ainv ); + + if ( !status ) + { + CPLError(CE_Failure, CPLE_AppDefined, "There is a problem to invert the interpolation matrix."); + ret = 0; + } + else + { + // calc the coefs + for ( int v = 0; v < _nof_vars; v++ ) + for ( r = 0; r < _nof_eqs; r++ ) + { + coef[v][r] = 0.0; + for ( c = 0; c < _nof_eqs; c++ ) + coef[v][r] += Ainv(r,c) * rhs[v][c]; + } + } +#endif + + VSIFree(_AA); + VSIFree(_Ainv); + + return(ret); +} + +int VizGeorefSpline2D::get_point( const double Px, const double Py, double *vars ) +{ + int v, r; + double tmp, Pu; + double fact; + int leftP=0, rightP=0, found = 0; + + switch ( type ) + { + case VIZ_GEOREF_SPLINE_ZERO_POINTS : + for ( v = 0; v < _nof_vars; v++ ) + vars[v] = 0.0; + break; + case VIZ_GEOREF_SPLINE_ONE_POINT : + for ( v = 0; v < _nof_vars; v++ ) + vars[v] = rhs[v][3]; + break; + case VIZ_GEOREF_SPLINE_TWO_POINTS : + fact = _dx * ( Px - x[0] ) + _dy * ( Py - y[0] ); + for ( v = 0; v < _nof_vars; v++ ) + vars[v] = ( 1 - fact ) * rhs[v][3] + fact * rhs[v][4]; + break; + case VIZ_GEOREF_SPLINE_ONE_DIMENSIONAL : + Pu = _dx * ( Px - x[0] ) + _dy * ( Py - y[0] ); + if ( Pu <= u[index[0]] ) + { + leftP = index[0]; + rightP = index[1]; + } + else if ( Pu >= u[index[_nof_points-1]] ) + { + leftP = index[_nof_points-2]; + rightP = index[_nof_points-1]; + } + else + { + for ( r = 1; !found && r < _nof_points; r++ ) + { + leftP = index[r-1]; + rightP = index[r]; + if ( Pu >= u[leftP] && Pu <= u[rightP] ) + found = 1; + } + } + + fact = ( Pu - u[leftP] ) / ( u[rightP] - u[leftP] ); + for ( v = 0; v < _nof_vars; v++ ) + vars[v] = ( 1.0 - fact ) * rhs[v][leftP+3] + + fact * rhs[v][rightP+3]; + break; + case VIZ_GEOREF_SPLINE_FULL : + { + double Pxy[2] = { Px, Py }; + for ( v = 0; v < _nof_vars; v++ ) + vars[v] = coef[v][0] + coef[v][1] * Px + coef[v][2] * Py; + + for ( r = 0; r < (_nof_points & (~3)); r+=4 ) + { + double tmp[4]; + VizGeorefSpline2DBase_func4( tmp, Pxy, &x[r], &y[r] ); + for ( v= 0; v < _nof_vars; v++ ) + vars[v] += coef[v][r+3] * tmp[0] + + coef[v][r+3+1] * tmp[1] + + coef[v][r+3+2] * tmp[2] + + coef[v][r+3+3] * tmp[3]; + } + for ( ; r < _nof_points; r++ ) + { + tmp = VizGeorefSpline2DBase_func( Px, Py, x[r], y[r] ); + for ( v= 0; v < _nof_vars; v++ ) + vars[v] += coef[v][r+3] * tmp; + } + break; + } + case VIZ_GEOREF_SPLINE_POINT_WAS_ADDED : + fprintf(stderr, " A point was added after the last solve\n"); + fprintf(stderr, " NO interpolation - return values are zero\n"); + for ( v = 0; v < _nof_vars; v++ ) + vars[v] = 0.0; + return(0); + break; + case VIZ_GEOREF_SPLINE_POINT_WAS_DELETED : + fprintf(stderr, " A point was deleted after the last solve\n"); + fprintf(stderr, " NO interpolation - return values are zero\n"); + for ( v = 0; v < _nof_vars; v++ ) + vars[v] = 0.0; + return(0); + break; + default : + return(0); + break; + } + return(1); +} + +#ifndef HAVE_ARMADILLO +static int matrixInvert( int N, double input[], double output[] ) +{ + // Receives an array of dimension NxN as input. This is passed as a one- + // dimensional array of N-squared size. It produces the inverse of the + // input matrix, returned as output, also of size N-squared. The Gauss- + // Jordan Elimination method is used. (Adapted from a BASIC routine in + // "Basic Scientific Subroutines Vol. 1", courtesy of Scott Edwards.) + + // Array elements 0...N-1 are for the first row, N...2N-1 are for the + // second row, etc. + + // We need to have a temporary array of size N x 2N. We'll refer to the + // "left" and "right" halves of this array. + + int row, col; + +#if 0 + fprintf(stderr, "Matrix Inversion input matrix (N=%d)\n", N); + for ( row=0; row fabs( temp[max*2*N + k] )) + { + max = row; + } + } + + if (max != k) // swap all the elements in the two rows + { + for (col=k; col<2*N; col++) + { + ftemp = temp[k*2*N + col]; + temp[k*2*N + col] = temp[max*2*N + col]; + temp[max*2*N + col] = ftemp; + } + } + } + + ftemp = temp[ k*2*N + k ]; + if ( ftemp == 0.0f ) // matrix cannot be inverted + { + delete[] temp; + return false; + } + + for ( col=k; col<2*N; col++ ) + { + temp[ k*2*N + col ] /= ftemp; + } + + int i2 = k*2*N ; + for ( row=0; row header file. */ +#define HAVE_ASSERT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +#undef HAVE_LIBDL + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +#define HAVE_FLOAT_H 1 + +#define HAVE_ERRNO_H 1 + +#define HAVE_SEARCH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRECT_H + +/* Define to 1 if you have the `localtime_r' function. */ +#undef HAVE_LOCALTIME_R + +#undef HAVE_DLFCN_H +#undef HAVE_DBMALLOC_H +#undef HAVE_LIBDBMALLOC +#undef WORDS_BIGENDIAN + +/* The size of a `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of a `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of a `unsigned long', as computed by sizeof. */ +#define SIZEOF_UNSIGNED_LONG 4 + +/* The size of `void*', as computed by sizeof. */ +#ifdef _WIN64 +# define SIZEOF_VOIDP 8 +#else +# define SIZEOF_VOIDP 4 +#endif + +/* Set the native cpu bit order */ +#define HOST_FILLORDER FILLORDER_LSB2MSB + +/* Define as 0 or 1 according to the floating point format suported by the + machine */ +#define HAVE_IEEEFP 1 + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +# ifndef inline +# define inline __inline +# endif +#endif + +#define lfind _lfind + +#if defined(_MSC_VER) && (_MSC_VER < 1310) +# define VSI_STAT64 _stat +# define VSI_STAT64_T _stat +#else +# define VSI_STAT64 _stat64 +# define VSI_STAT64_T __stat64 +#endif + +/* VC6 doesn't known intptr_t */ +#if defined(_MSC_VER) && (_MSC_VER <= 1200) + typedef int intptr_t; +#endif + +#pragma warning(disable: 4786) + +/* #define CPL_DISABLE_DLL */ + +#endif diff --git a/bazaar/plugin/gdal/frmts/GNUmakefile b/bazaar/plugin/gdal/frmts/GNUmakefile new file mode 100644 index 000000000..a343a4aa7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/GNUmakefile @@ -0,0 +1,27 @@ + + +include ../GDALmake.opt + +OBJ = o/gdalallregister.o + +FRMT_FLAGS = $(foreach FRMT, $(GDAL_FORMATS), -DFRMT_$(FRMT)) + +%-install-obj: + $(MAKE) -C $* install-obj + +default: $(OBJ:.o=.$(OBJ_EXT)) $(foreach d,$(GDAL_FORMATS),$(d)-install-obj) + +clean: $(foreach d,$(GDAL_FORMATS),$(d)-clean) + rm -f *.o o/*.o o/*.a + $(RM) o/*.lo + +o/gdalallregister.$(OBJ_EXT): gdalallregister.cpp ../GDALmake.opt + $(CXX) -c $(GDAL_INCLUDE) $(CXXFLAGS) $(FRMT_FLAGS) \ + -DGDAL_FORMATS="$(GDAL_FORMATS)" \ + gdalallregister.cpp -o o/gdalallregister.$(OBJ_EXT) + +# We might want to add dynamically generated drivers here eventually. +install: + $(MAKE) -C vrt install + $(MAKE) -C mem install + $(MAKE) -C raw install diff --git a/bazaar/plugin/gdal/frmts/aaigrid/GNUmakefile b/bazaar/plugin/gdal/frmts/aaigrid/GNUmakefile new file mode 100644 index 000000000..9f087669b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aaigrid/GNUmakefile @@ -0,0 +1,15 @@ + +include ../../GDALmake.opt + +OBJ = aaigriddataset.o + +CPPFLAGS := $(CPPFLAGS) $(XTRA_OPT) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/aaigrid/aaigriddataset.cpp b/bazaar/plugin/gdal/frmts/aaigrid/aaigriddataset.cpp new file mode 100644 index 000000000..bb67c5743 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aaigrid/aaigriddataset.cpp @@ -0,0 +1,1353 @@ +/****************************************************************************** + * $Id: aaigriddataset.cpp 28785 2015-03-26 20:46:45Z goatbar $ + * + * Project: GDAL + * Purpose: Implements Arc/Info ASCII Grid Format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam (warmerdam@pobox.com) + * Copyright (c) 2007-2012, Even Rouault + * Copyright (c) 2014, Kyle Shannon + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include +#include +#include "cpl_string.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: aaigriddataset.cpp 28785 2015-03-26 20:46:45Z goatbar $"); + +CPL_C_START +void GDALRegister_AAIGrid(void); +void GDALRegister_GRASSASCIIGrid(void); +CPL_C_END + +static CPLString OSR_GDS( char **papszNV, const char * pszField, + const char *pszDefaultValue ); + +typedef enum +{ + FORMAT_AAIG, + FORMAT_GRASSASCII +} GridFormat; + +/************************************************************************/ +/* ==================================================================== */ +/* AAIGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class AAIGRasterBand; + +class CPL_DLL AAIGDataset : public GDALPamDataset +{ + friend class AAIGRasterBand; + + VSILFILE *fp; + + char **papszPrj; + CPLString osPrjFilename; + char *pszProjection; + + + unsigned char achReadBuf[256]; + GUIntBig nBufferOffset; + int nOffsetInBuffer; + + char Getc(); + GUIntBig Tell(); + int Seek( GUIntBig nOffset ); + + protected: + GDALDataType eDataType; + double adfGeoTransform[6]; + int bNoDataSet; + double dfNoDataValue; + + + virtual int ParseHeader(const char* pszHeader, const char* pszDataType); + + public: + AAIGDataset(); + ~AAIGDataset(); + + virtual char **GetFileList(void); + + static GDALDataset *CommonOpen( GDALOpenInfo * poOpenInfo, + GridFormat eFormat ); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static CPLErr Delete( const char *pszFilename ); + static CPLErr Remove( const char *pszFilename, int bRepError ); + static GDALDataset *CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); + + virtual CPLErr GetGeoTransform( double * ); + virtual const char *GetProjectionRef(void); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GRASSASCIIDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GRASSASCIIDataset : public AAIGDataset +{ + virtual int ParseHeader(const char* pszHeader, const char* pszDataType); + + public: + GRASSASCIIDataset() : AAIGDataset() {} + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* AAIGRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class AAIGRasterBand : public GDALPamRasterBand +{ + friend class AAIGDataset; + + GUIntBig *panLineOffset; + + public: + + AAIGRasterBand( AAIGDataset *, int ); + virtual ~AAIGRasterBand(); + + virtual double GetNoDataValue( int * ); + virtual CPLErr SetNoDataValue( double ); + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* AAIGRasterBand() */ +/************************************************************************/ + +AAIGRasterBand::AAIGRasterBand( AAIGDataset *poDS, int nDataStart ) + +{ + this->poDS = poDS; + + nBand = 1; + eDataType = poDS->eDataType; + + nBlockXSize = poDS->nRasterXSize; + nBlockYSize = 1; + + panLineOffset = + (GUIntBig *) VSICalloc( poDS->nRasterYSize, sizeof(GUIntBig) ); + if (panLineOffset == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "AAIGRasterBand::AAIGRasterBand : Out of memory (nRasterYSize = %d)", + poDS->nRasterYSize); + return; + } + panLineOffset[0] = nDataStart; +} + +/************************************************************************/ +/* ~AAIGRasterBand() */ +/************************************************************************/ + +AAIGRasterBand::~AAIGRasterBand() + +{ + CPLFree( panLineOffset ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr AAIGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + AAIGDataset *poODS = (AAIGDataset *) poDS; + int iPixel; + + if( nBlockYOff < 0 || nBlockYOff > poODS->nRasterYSize - 1 + || nBlockXOff != 0 || panLineOffset == NULL || poODS->fp == NULL ) + return CE_Failure; + + if( panLineOffset[nBlockYOff] == 0 ) + { + int iPrevLine; + + for( iPrevLine = 1; iPrevLine <= nBlockYOff; iPrevLine++ ) + if( panLineOffset[iPrevLine] == 0 ) + IReadBlock( nBlockXOff, iPrevLine-1, NULL ); + } + + if( panLineOffset[nBlockYOff] == 0 ) + return CE_Failure; + + + if( poODS->Seek( panLineOffset[nBlockYOff] ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't seek to offset %lu in input file to read data.", + (long unsigned int)panLineOffset[nBlockYOff] ); + return CE_Failure; + } + + for( iPixel = 0; iPixel < poODS->nRasterXSize; ) + { + char szToken[500]; + char chNext; + int iTokenChar = 0; + + /* suck up any pre-white space. */ + do { + chNext = poODS->Getc(); + } while( isspace( (unsigned char)chNext ) ); + + while( chNext != '\0' && !isspace((unsigned char)chNext) ) + { + if( iTokenChar == sizeof(szToken)-2 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Token too long at scanline %d.", + nBlockYOff ); + return CE_Failure; + } + + szToken[iTokenChar++] = chNext; + chNext = poODS->Getc(); + } + + if( chNext == '\0' && + (iPixel != poODS->nRasterXSize - 1 || + nBlockYOff != poODS->nRasterYSize - 1) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "File short, can't read line %d.", + nBlockYOff ); + return CE_Failure; + } + + szToken[iTokenChar] = '\0'; + + if( pImage != NULL ) + { + if( eDataType == GDT_Float64 ) + ((double *) pImage)[iPixel] = CPLAtofM(szToken); + else if( eDataType == GDT_Float32 ) + ((float *) pImage)[iPixel] = (float) CPLAtofM(szToken); + else + ((GInt32 *) pImage)[iPixel] = (GInt32) atoi(szToken); + } + + iPixel++; + } + + if( nBlockYOff < poODS->nRasterYSize - 1 ) + panLineOffset[nBlockYOff + 1] = poODS->Tell(); + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double AAIGRasterBand::GetNoDataValue( int * pbSuccess ) + +{ + AAIGDataset *poODS = (AAIGDataset *) poDS; + + if( pbSuccess ) + *pbSuccess = poODS->bNoDataSet; + + return poODS->dfNoDataValue; +} + + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr AAIGRasterBand::SetNoDataValue( double dfNoData ) + +{ + AAIGDataset *poODS = (AAIGDataset *) poDS; + + poODS->bNoDataSet = TRUE; + poODS->dfNoDataValue = dfNoData; + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* AAIGDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* AAIGDataset() */ +/************************************************************************/ + +AAIGDataset::AAIGDataset() + +{ + papszPrj = NULL; + pszProjection = CPLStrdup(""); + fp = NULL; + eDataType = GDT_Int32; + bNoDataSet = FALSE; + dfNoDataValue = -9999.0; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + nOffsetInBuffer = 256; + nBufferOffset = 0; +} + +/************************************************************************/ +/* ~AAIGDataset() */ +/************************************************************************/ + +AAIGDataset::~AAIGDataset() + +{ + FlushCache(); + + if( fp != NULL ) + VSIFCloseL( fp ); + + CPLFree( pszProjection ); + CSLDestroy( papszPrj ); +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +GUIntBig AAIGDataset::Tell() + +{ + return nBufferOffset + nOffsetInBuffer; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int AAIGDataset::Seek( GUIntBig nNewOffset ) + +{ + nOffsetInBuffer = sizeof(achReadBuf); + return VSIFSeekL( fp, nNewOffset, SEEK_SET ); +} + +/************************************************************************/ +/* Getc() */ +/* */ +/* Read a single character from the input file (efficiently we */ +/* hope). */ +/************************************************************************/ + +char AAIGDataset::Getc() + +{ + if( nOffsetInBuffer < (int) sizeof(achReadBuf) ) + return achReadBuf[nOffsetInBuffer++]; + + nBufferOffset = VSIFTellL( fp ); + int nRead = VSIFReadL( achReadBuf, 1, sizeof(achReadBuf), fp ); + unsigned int i; + for(i=nRead;inHeaderBytes < 40 + || !( EQUALN((const char *) poOpenInfo->pabyHeader,"ncols",5) || + EQUALN((const char *) poOpenInfo->pabyHeader,"nrows",5) || + EQUALN((const char *) poOpenInfo->pabyHeader,"xllcorner",9)|| + EQUALN((const char *) poOpenInfo->pabyHeader,"yllcorner",9)|| + EQUALN((const char *) poOpenInfo->pabyHeader,"xllcenter",9)|| + EQUALN((const char *) poOpenInfo->pabyHeader,"yllcenter",9)|| + EQUALN((const char *) poOpenInfo->pabyHeader,"dx",2)|| + EQUALN((const char *) poOpenInfo->pabyHeader,"dy",2)|| + EQUALN((const char *) poOpenInfo->pabyHeader,"cellsize",8)) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int GRASSASCIIDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Does this look like a GRASS ASCII grid file? */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 40 + || !( EQUALN((const char *) poOpenInfo->pabyHeader,"north:",6) || + EQUALN((const char *) poOpenInfo->pabyHeader,"south:",6) || + EQUALN((const char *) poOpenInfo->pabyHeader,"east:",5)|| + EQUALN((const char *) poOpenInfo->pabyHeader,"west:",5)|| + EQUALN((const char *) poOpenInfo->pabyHeader,"rows:",5)|| + EQUALN((const char *) poOpenInfo->pabyHeader,"cols:",5) ) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *AAIGDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + if (!Identify(poOpenInfo)) + return NULL; + + return CommonOpen(poOpenInfo, FORMAT_AAIG); +} + +/************************************************************************/ +/* ParseHeader() */ +/************************************************************************/ + +int AAIGDataset::ParseHeader(const char* pszHeader, const char* pszDataType) +{ + int i, j; + char** papszTokens = + CSLTokenizeString2( pszHeader, " \n\r\t" , 0 ); + int nTokens = CSLCount(papszTokens); + double dfCellDX = 0; + double dfCellDY = 0; + + if ( (i = CSLFindString( papszTokens, "ncols" )) < 0 || + i + 1 >= nTokens) + { + CSLDestroy( papszTokens ); + return FALSE; + } + nRasterXSize = atoi(papszTokens[i + 1]); + if ( (i = CSLFindString( papszTokens, "nrows" )) < 0 || + i + 1 >= nTokens) + { + CSLDestroy( papszTokens ); + return FALSE; + } + nRasterYSize = atoi(papszTokens[i + 1]); + + if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize)) + { + CSLDestroy( papszTokens ); + return FALSE; + } + + if ( (i = CSLFindString( papszTokens, "cellsize" )) < 0 ) + { + int iDX, iDY; + if( (iDX = CSLFindString(papszTokens,"dx")) < 0 + || (iDY = CSLFindString(papszTokens,"dy")) < 0 + || iDX+1 >= nTokens + || iDY+1 >= nTokens) + { + CSLDestroy( papszTokens ); + return FALSE; + } + + dfCellDX = CPLAtofM( papszTokens[iDX+1] ); + dfCellDY = CPLAtofM( papszTokens[iDY+1] ); + } + else + { + if (i + 1 >= nTokens) + { + CSLDestroy( papszTokens ); + return FALSE; + } + dfCellDX = dfCellDY = CPLAtofM( papszTokens[i + 1] ); + } + + if ((i = CSLFindString( papszTokens, "xllcorner" )) >= 0 && + (j = CSLFindString( papszTokens, "yllcorner" )) >= 0 && + i + 1 < nTokens && j + 1 < nTokens) + { + adfGeoTransform[0] = CPLAtofM( papszTokens[i + 1] ); + + /* Small hack to compensate from insufficient precision in cellsize */ + /* parameter in datasets of http://ccafs-climate.org/data/A2a_2020s/hccpr_hadcm3 */ + if ((nRasterXSize % 360) == 0 && + fabs(adfGeoTransform[0] - (-180.0)) < 1e-12 && + dfCellDX == dfCellDY && + fabs(dfCellDX - (360.0 / nRasterXSize)) < 1e-9) + { + dfCellDX = dfCellDY = 360.0 / nRasterXSize; + } + + adfGeoTransform[1] = dfCellDX; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = CPLAtofM( papszTokens[j + 1] ) + + nRasterYSize * dfCellDY; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = - dfCellDY; + } + else if ((i = CSLFindString( papszTokens, "xllcenter" )) >= 0 && + (j = CSLFindString( papszTokens, "yllcenter" )) >= 0 && + i + 1 < nTokens && j + 1 < nTokens) + { + SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT ); + + adfGeoTransform[0] = CPLAtofM(papszTokens[i + 1]) - 0.5 * dfCellDX; + adfGeoTransform[1] = dfCellDX; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = CPLAtofM( papszTokens[j + 1] ) + - 0.5 * dfCellDY + + nRasterYSize * dfCellDY; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = - dfCellDY; + } + else + { + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = dfCellDX; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = - dfCellDY; + } + + if( (i = CSLFindString( papszTokens, "NODATA_value" )) >= 0 && + i + 1 < nTokens) + { + const char* pszNoData = papszTokens[i + 1]; + + bNoDataSet = TRUE; + dfNoDataValue = CPLAtofM(pszNoData); + if( pszDataType == NULL && + (strchr( pszNoData, '.' ) != NULL || + strchr( pszNoData, ',' ) != NULL || + INT_MIN > dfNoDataValue || dfNoDataValue > INT_MAX) ) + { + eDataType = GDT_Float32; + } + if( eDataType == GDT_Float32 ) + { + dfNoDataValue = (double) (float) dfNoDataValue; + } + } + + CSLDestroy( papszTokens ); + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GRASSASCIIDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + if (!Identify(poOpenInfo)) + return NULL; + + return CommonOpen(poOpenInfo, FORMAT_GRASSASCII); +} + + +/************************************************************************/ +/* ParseHeader() */ +/************************************************************************/ + +int GRASSASCIIDataset::ParseHeader(const char* pszHeader, const char* pszDataType) +{ + int i; + char** papszTokens = + CSLTokenizeString2( pszHeader, " \n\r\t:" , 0 ); + int nTokens = CSLCount(papszTokens); + if ( (i = CSLFindString( papszTokens, "cols" )) < 0 || + i + 1 >= nTokens) + { + CSLDestroy( papszTokens ); + return FALSE; + } + nRasterXSize = atoi(papszTokens[i + 1]); + if ( (i = CSLFindString( papszTokens, "rows" )) < 0 || + i + 1 >= nTokens) + { + CSLDestroy( papszTokens ); + return FALSE; + } + nRasterYSize = atoi(papszTokens[i + 1]); + + if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize)) + { + CSLDestroy( papszTokens ); + return FALSE; + } + + int iNorth = CSLFindString( papszTokens, "north" ); + int iSouth = CSLFindString( papszTokens, "south" ); + int iEast = CSLFindString( papszTokens, "east" ); + int iWest = CSLFindString( papszTokens, "west" ); + + if (iNorth == -1 || iSouth == -1 || iEast == -1 || iWest == -1 || + MAX(MAX(iNorth, iSouth), MAX(iEast, iWest)) + 1 >= nTokens) + { + CSLDestroy( papszTokens ); + return FALSE; + } + + double dfNorth = CPLAtofM( papszTokens[iNorth + 1] ); + double dfSouth = CPLAtofM( papszTokens[iSouth + 1] ); + double dfEast = CPLAtofM( papszTokens[iEast + 1] ); + double dfWest = CPLAtofM( papszTokens[iWest + 1] ); + double dfPixelXSize = (dfEast - dfWest) / nRasterXSize; + double dfPixelYSize = (dfNorth - dfSouth) / nRasterYSize; + + adfGeoTransform[0] = dfWest; + adfGeoTransform[1] = dfPixelXSize; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = dfNorth; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = - dfPixelYSize; + + if( (i = CSLFindString( papszTokens, "null" )) >= 0 && + i + 1 < nTokens) + { + const char* pszNoData = papszTokens[i + 1]; + + bNoDataSet = TRUE; + dfNoDataValue = CPLAtofM(pszNoData); + if( pszDataType == NULL && + (strchr( pszNoData, '.' ) != NULL || + strchr( pszNoData, ',' ) != NULL || + INT_MIN > dfNoDataValue || dfNoDataValue > INT_MAX) ) + { + eDataType = GDT_Float32; + } + if( eDataType == GDT_Float32 ) + { + dfNoDataValue = (double) (float) dfNoDataValue; + } + } + + if( (i = CSLFindString( papszTokens, "type" )) >= 0 && + i + 1 < nTokens) + { + const char* pszType = papszTokens[i + 1]; + if (EQUAL(pszType, "int")) + eDataType = GDT_Int32; + else if (EQUAL(pszType, "float")) + eDataType = GDT_Float32; + else if (EQUAL(pszType, "double")) + eDataType = GDT_Float64; + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid value for type parameter : %s", pszType); + } + } + + CSLDestroy(papszTokens); + + return TRUE; +} + +/************************************************************************/ +/* CommonOpen() */ +/************************************************************************/ + +GDALDataset *AAIGDataset::CommonOpen( GDALOpenInfo * poOpenInfo, + GridFormat eFormat ) +{ + int i = 0; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + AAIGDataset *poDS; + + if (eFormat == FORMAT_AAIG) + poDS = new AAIGDataset(); + else + poDS = new GRASSASCIIDataset(); + + + const char* pszDataTypeOption = (eFormat == FORMAT_AAIG) ? "AAIGRID_DATATYPE": + "GRASSASCIIGRID_DATATYPE"; + const char* pszDataType = CPLGetConfigOption(pszDataTypeOption, NULL); + if( pszDataType == NULL ) + pszDataType = CSLFetchNameValue( poOpenInfo->papszOpenOptions, "DATATYPE" ); + if (pszDataType != NULL) + { + poDS->eDataType = GDALGetDataTypeByName(pszDataType); + if (!(poDS->eDataType == GDT_Int32 || poDS->eDataType == GDT_Float32 || + poDS->eDataType == GDT_Float64)) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unsupported value for %s : %s", pszDataTypeOption, pszDataType); + poDS->eDataType = GDT_Int32; + pszDataType = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Parse the header. */ +/* -------------------------------------------------------------------- */ + if (!poDS->ParseHeader((const char *) poOpenInfo->pabyHeader, pszDataType)) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open file with large file API. */ +/* -------------------------------------------------------------------- */ + + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r" ); + if( poDS->fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "VSIFOpenL(%s) failed unexpectedly.", + poOpenInfo->pszFilename ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Find the start of real data. */ +/* -------------------------------------------------------------------- */ + int nStartOfData; + + for( i = 2; TRUE ; i++ ) + { + if( poOpenInfo->pabyHeader[i] == '\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Couldn't find data values in ASCII Grid file.\n" ); + delete poDS; + return NULL; + } + + if( poOpenInfo->pabyHeader[i-1] == '\n' + || poOpenInfo->pabyHeader[i-2] == '\n' + || poOpenInfo->pabyHeader[i-1] == '\r' + || poOpenInfo->pabyHeader[i-2] == '\r' ) + { + if( !isalpha(poOpenInfo->pabyHeader[i]) + && poOpenInfo->pabyHeader[i] != '\n' + && poOpenInfo->pabyHeader[i] != '\r') + { + nStartOfData = i; + + /* Beginning of real data found. */ + break; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Recognize the type of data. */ +/* -------------------------------------------------------------------- */ + CPLAssert( NULL != poDS->fp ); + + if( pszDataType == NULL && poDS->eDataType != GDT_Float32) + { + /* Allocate 100K chunk + 1 extra byte for NULL character. */ + const size_t nChunkSize = 1024 * 100; + GByte* pabyChunk = (GByte *) VSICalloc( nChunkSize + 1, sizeof(GByte) ); + if (pabyChunk == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + delete poDS; + return NULL; + } + pabyChunk[nChunkSize] = '\0'; + + VSIFSeekL( poDS->fp, nStartOfData, SEEK_SET ); + + /* Scan for dot in subsequent chunks of data. */ + while( !VSIFEofL( poDS->fp) ) + { + VSIFReadL( pabyChunk, sizeof(GByte), nChunkSize, poDS->fp ); + CPLAssert( pabyChunk[nChunkSize] == '\0' ); + + for(i = 0; i < (int)nChunkSize; i++) + { + GByte ch = pabyChunk[i]; + if (ch == '.' || ch == ',' || ch == 'e' || ch == 'E') + { + poDS->eDataType = GDT_Float32; + break; + } + } + } + + /* Deallocate chunk. */ + VSIFree( pabyChunk ); + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + AAIGRasterBand* band = new AAIGRasterBand( poDS, nStartOfData ); + poDS->SetBand( 1, band ); + if (band->panLineOffset == NULL) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to read projection file. */ +/* -------------------------------------------------------------------- */ + char *pszDirname, *pszBasename; + VSIStatBufL sStatBuf; + + pszDirname = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename)); + pszBasename = CPLStrdup(CPLGetBasename(poOpenInfo->pszFilename)); + + poDS->osPrjFilename = CPLFormFilename( pszDirname, pszBasename, "prj" ); + int nRet = VSIStatL( poDS->osPrjFilename, &sStatBuf ); + + if( nRet != 0 && VSIIsCaseSensitiveFS(poDS->osPrjFilename) ) + { + poDS->osPrjFilename = CPLFormFilename( pszDirname, pszBasename, "PRJ" ); + nRet = VSIStatL( poDS->osPrjFilename, &sStatBuf ); + } + + if( nRet == 0 ) + { + OGRSpatialReference oSRS; + + poDS->papszPrj = CSLLoad( poDS->osPrjFilename ); + + CPLDebug( "AAIGrid", "Loaded SRS from %s", + poDS->osPrjFilename.c_str() ); + + if( oSRS.importFromESRI( poDS->papszPrj ) == OGRERR_NONE ) + { + // If geographic values are in seconds, we must transform. + // Is there a code for minutes too? + if( oSRS.IsGeographic() + && EQUAL(OSR_GDS( poDS->papszPrj, "Units", ""), "DS") ) + { + poDS->adfGeoTransform[0] /= 3600.0; + poDS->adfGeoTransform[1] /= 3600.0; + poDS->adfGeoTransform[2] /= 3600.0; + poDS->adfGeoTransform[3] /= 3600.0; + poDS->adfGeoTransform[4] /= 3600.0; + poDS->adfGeoTransform[5] /= 3600.0; + } + + CPLFree( poDS->pszProjection ); + oSRS.exportToWkt( &(poDS->pszProjection) ); + } + } + + CPLFree( pszDirname ); + CPLFree( pszBasename ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr AAIGDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return( CE_None ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *AAIGDataset::GetProjectionRef() + +{ + return pszProjection; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * AAIGDataset::CreateCopy( + const char * pszFilename, GDALDataset *poSrcDS, + CPL_UNUSED int bStrict, + char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + +/* -------------------------------------------------------------------- */ +/* Some rudimentary checks */ +/* -------------------------------------------------------------------- */ + if( nBands != 1 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "AAIG driver doesn't support %d bands. Must be 1 band.\n", + nBands ); + + return NULL; + } + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpImage; + + fpImage = VSIFOpenL( pszFilename, "wt" ); + if( fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create file %s.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write ASCII Grid file header */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + char szHeader[2000]; + const char *pszForceCellsize = + CSLFetchNameValue( papszOptions, "FORCE_CELLSIZE" ); + + poSrcDS->GetGeoTransform( adfGeoTransform ); + + if( ABS(adfGeoTransform[1]+adfGeoTransform[5]) < 0.0000001 + || ABS(adfGeoTransform[1]-adfGeoTransform[5]) < 0.0000001 + || (pszForceCellsize && CSLTestBoolean(pszForceCellsize)) ) + CPLsprintf( szHeader, + "ncols %d\n" + "nrows %d\n" + "xllcorner %.12f\n" + "yllcorner %.12f\n" + "cellsize %.12f\n", + nXSize, nYSize, + adfGeoTransform[0], + adfGeoTransform[3] - nYSize * adfGeoTransform[1], + adfGeoTransform[1] ); + else + { + if( pszForceCellsize == NULL ) + CPLError( CE_Warning, CPLE_AppDefined, + "Producing a Golden Surfer style file with DX and DY instead\n" + "of CELLSIZE since the input pixels are non-square. Use the\n" + "FORCE_CELLSIZE=TRUE creation option to force use of DX for\n" + "even though this will be distorted. Most ASCII Grid readers\n" + "(ArcGIS included) do not support the DX and DY parameters.\n" ); + CPLsprintf( szHeader, + "ncols %d\n" + "nrows %d\n" + "xllcorner %.12f\n" + "yllcorner %.12f\n" + "dx %.12f\n" + "dy %.12f\n", + nXSize, nYSize, + adfGeoTransform[0], + adfGeoTransform[3] + nYSize * adfGeoTransform[5], + adfGeoTransform[1], + fabs(adfGeoTransform[5]) ); + } + +/* -------------------------------------------------------------------- */ +/* Builds the format string used for printing float values. */ +/* -------------------------------------------------------------------- */ + char szFormatFloat[32]; + strcpy(szFormatFloat, " %.20g"); + const char *pszDecimalPrecision = + CSLFetchNameValue( papszOptions, "DECIMAL_PRECISION" ); + const char *pszSignificantDigits = + CSLFetchNameValue( papszOptions, "SIGNIFICANT_DIGITS" ); + int bIgnoreSigDigits = FALSE; + if( pszDecimalPrecision && pszSignificantDigits ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Conflicting precision arguments, using DECIMAL_PRECISION" ); + bIgnoreSigDigits = TRUE; + } + int nPrecision; + if ( pszSignificantDigits && !bIgnoreSigDigits ) + { + nPrecision = atoi( pszSignificantDigits ); + if (nPrecision >= 0) + sprintf( szFormatFloat, " %%.%dg", nPrecision ); + CPLDebug( "AAIGrid", "Setting precision format: %s", szFormatFloat ); + } + else if( pszDecimalPrecision ) + { + nPrecision = atoi( pszDecimalPrecision ); + if ( nPrecision >= 0 ) + sprintf( szFormatFloat, " %%.%df", nPrecision ); + CPLDebug( "AAIGrid", "Setting precision format: %s", szFormatFloat ); + } + +/* -------------------------------------------------------------------- */ +/* Handle nodata (optionally). */ +/* -------------------------------------------------------------------- */ + GDALRasterBand * poBand = poSrcDS->GetRasterBand( 1 ); + double dfNoData; + int bSuccess; + int bReadAsInt; + bReadAsInt = ( poBand->GetRasterDataType() == GDT_Byte + || poBand->GetRasterDataType() == GDT_Int16 + || poBand->GetRasterDataType() == GDT_UInt16 + || poBand->GetRasterDataType() == GDT_Int32 ); + + // Write `nodata' value to header if it is exists in source dataset + dfNoData = poBand->GetNoDataValue( &bSuccess ); + if ( bSuccess ) + { + sprintf( szHeader+strlen( szHeader ), "NODATA_value " ); + if( bReadAsInt ) + sprintf( szHeader+strlen( szHeader ), "%d", (int)dfNoData ); + else + CPLsprintf( szHeader+strlen( szHeader ), szFormatFloat, dfNoData ); + sprintf( szHeader+strlen( szHeader ), "\n" ); + } + + VSIFWriteL( szHeader, 1, strlen(szHeader), fpImage ); + +/* -------------------------------------------------------------------- */ +/* Loop over image, copying image data. */ +/* -------------------------------------------------------------------- */ + int *panScanline = NULL; + double *padfScanline = NULL; + int iLine, iPixel; + CPLErr eErr = CE_None; + + // Write scanlines to output file + if (bReadAsInt) + panScanline = (int *) CPLMalloc( nXSize * + GDALGetDataTypeSize(GDT_Int32) / 8 ); + else + padfScanline = (double *) CPLMalloc( nXSize * + GDALGetDataTypeSize(GDT_Float64) / 8 ); + + for( iLine = 0; eErr == CE_None && iLine < nYSize; iLine++ ) + { + CPLString osBuf; + eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + (bReadAsInt) ? (void*)panScanline : (void*)padfScanline, + nXSize, 1, (bReadAsInt) ? GDT_Int32 : GDT_Float64, + 0, 0, NULL ); + + if( bReadAsInt ) + { + for ( iPixel = 0; iPixel < nXSize; iPixel++ ) + { + sprintf( szHeader, " %d", panScanline[iPixel] ); + osBuf += szHeader; + if( (iPixel & 1023) == 0 || iPixel == nXSize - 1 ) + { + if ( VSIFWriteL( osBuf, (int)osBuf.size(), 1, fpImage ) != 1 ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_AppDefined, + "Write failed, disk full?\n" ); + break; + } + osBuf = ""; + } + } + } + else + { + for ( iPixel = 0; iPixel < nXSize; iPixel++ ) + { + CPLsprintf( szHeader, szFormatFloat, padfScanline[iPixel] ); + osBuf += szHeader; + if( (iPixel & 1023) == 0 || iPixel == nXSize - 1 ) + { + if ( VSIFWriteL( osBuf, (int)osBuf.size(), 1, fpImage ) != 1 ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_AppDefined, + "Write failed, disk full?\n" ); + break; + } + osBuf = ""; + } + } + } + VSIFWriteL( (void *) "\n", 1, 1, fpImage ); + + if( eErr == CE_None && + !pfnProgress((iLine + 1) / ((double) nYSize), NULL, pProgressData) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + + CPLFree( panScanline ); + CPLFree( padfScanline ); + VSIFCloseL( fpImage ); + + if( eErr != CE_None ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Try to write projection file. */ +/* -------------------------------------------------------------------- */ + const char *pszOriginalProjection; + + pszOriginalProjection = (char *)poSrcDS->GetProjectionRef(); + if( !EQUAL( pszOriginalProjection, "" ) ) + { + char *pszDirname, *pszBasename; + char *pszPrjFilename; + char *pszESRIProjection = NULL; + VSILFILE *fp; + OGRSpatialReference oSRS; + + pszDirname = CPLStrdup( CPLGetPath(pszFilename) ); + pszBasename = CPLStrdup( CPLGetBasename(pszFilename) ); + + pszPrjFilename = CPLStrdup( CPLFormFilename( pszDirname, pszBasename, "prj" ) ); + fp = VSIFOpenL( pszPrjFilename, "wt" ); + if (fp != NULL) + { + oSRS.importFromWkt( (char **) &pszOriginalProjection ); + oSRS.morphToESRI(); + oSRS.exportToWkt( &pszESRIProjection ); + VSIFWriteL( pszESRIProjection, 1, strlen(pszESRIProjection), fp ); + + VSIFCloseL( fp ); + CPLFree( pszESRIProjection ); + } + else + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to create file %s.\n", pszPrjFilename ); + } + CPLFree( pszDirname ); + CPLFree( pszBasename ); + CPLFree( pszPrjFilename ); + } + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + + /* If outputing to stdout, we can't reopen it, so we'll return */ + /* a fake dataset to make the caller happy */ + CPLPushErrorHandler(CPLQuietErrorHandler); + GDALPamDataset* poDS = (GDALPamDataset*) GDALOpen(pszFilename, GA_ReadOnly); + CPLPopErrorHandler(); + if (poDS) + { + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + return poDS; + } + + CPLErrorReset(); + + AAIGDataset* poAAIG_DS = new AAIGDataset(); + poAAIG_DS->nRasterXSize = nXSize; + poAAIG_DS->nRasterYSize = nYSize; + poAAIG_DS->nBands = 1; + poAAIG_DS->SetBand( 1, new AAIGRasterBand( poAAIG_DS, 1 ) ); + return poAAIG_DS; +} + +/************************************************************************/ +/* OSR_GDS() */ +/************************************************************************/ + +static CPLString OSR_GDS( char **papszNV, const char * pszField, + const char *pszDefaultValue ) + +{ + int iLine; + + if( papszNV == NULL || papszNV[0] == NULL ) + return pszDefaultValue; + + for( iLine = 0; + papszNV[iLine] != NULL && + !EQUALN(papszNV[iLine],pszField,strlen(pszField)); + iLine++ ) {} + + if( papszNV[iLine] == NULL ) + return pszDefaultValue; + else + { + CPLString osResult; + char **papszTokens; + + papszTokens = CSLTokenizeString(papszNV[iLine]); + + if( CSLCount(papszTokens) > 1 ) + osResult = papszTokens[1]; + else + osResult = pszDefaultValue; + + CSLDestroy( papszTokens ); + return osResult; + } +} + +/************************************************************************/ +/* GDALRegister_AAIGrid() */ +/************************************************************************/ + +void GDALRegister_AAIGrid() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "AAIGrid" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "AAIGrid" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Arc/Info ASCII Grid" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#AAIGrid" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "asc" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16 Int16 Int32 Float32" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"\n" +" \n" ); + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"\n" +" \n" +"\n" ); + + poDriver->pfnOpen = AAIGDataset::Open; + poDriver->pfnIdentify = AAIGDataset::Identify; + poDriver->pfnCreateCopy = AAIGDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + +/************************************************************************/ +/* GDALRegister_GRASSASCIIGrid() */ +/************************************************************************/ + +void GDALRegister_GRASSASCIIGrid() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GRASSASCIIGrid" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GRASSASCIIGrid" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GRASS ASCII Grid" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#GRASSASCIIGrid" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = GRASSASCIIDataset::Open; + poDriver->pfnIdentify = GRASSASCIIDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/aaigrid/makefile.vc b/bazaar/plugin/gdal/frmts/aaigrid/makefile.vc new file mode 100644 index 000000000..ebde922ba --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aaigrid/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = aaigriddataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/adrg/GNUmakefile b/bazaar/plugin/gdal/frmts/adrg/GNUmakefile new file mode 100644 index 000000000..1005bfe94 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/adrg/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../GDALmake.opt + +OBJ = adrgdataset.o srpdataset.o + +CPPFLAGS := $(CPPFLAGS) -I../iso8211 + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/adrg/adrgdataset.cpp b/bazaar/plugin/gdal/frmts/adrg/adrgdataset.cpp new file mode 100644 index 000000000..14d78c6a5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/adrg/adrgdataset.cpp @@ -0,0 +1,2269 @@ +/****************************************************************************** + * $Id: adrgdataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Purpose: ADRG reader + * Author: Even Rouault, even.rouault at mines-paris.org + * + ****************************************************************************** + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include "iso8211.h" + +CPL_CVSID("$Id: adrgdataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +#define N_ELEMENTS(x) (sizeof(x)/sizeof(x[0])) + +class ADRGDataset : public GDALPamDataset +{ + friend class ADRGRasterBand; + + CPLString osGENFileName; + CPLString osIMGFileName; + + VSILFILE* fdIMG; + int* TILEINDEX; + int offsetInIMG; + int NFC; + int NFL; + double LSO; + double PSO; + int ARV; + int BRV; + + char** papszSubDatasets; + + ADRGDataset* poOverviewDS; + + /* For creation */ + int bCreation; + VSILFILE* fdGEN; + VSILFILE* fdTHF; + int bGeoTransformValid; + double adfGeoTransform[6]; + int nNextAvailableBlock; + CPLString osBaseFileName; + + static char** GetGENListFromTHF(const char* pszFileName); + static char** GetIMGListFromGEN(const char* pszFileName, int* pnRecordIndex = NULL); + static ADRGDataset* OpenDataset(const char* pszGENFileName, const char* pszIMGFileName, DDFRecord* record = NULL); + static DDFRecord* FindRecordInGENForIMG(DDFModule& module, + const char* pszGENFileName, const char* pszIMGFileName); + + public: + ADRGDataset(); + virtual ~ADRGDataset(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * padfGeoTransform ); + virtual CPLErr SetGeoTransform( double * padfGeoTransform ); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain = "" ); + + virtual char **GetFileList(); + + void AddSubDataset( const char* pszGENFileName, const char* pszIMGFileName ); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create(const char* pszFilename, int nXSize, int nYSize, + int nBands, GDALDataType eType, char **papszOptions); + + static double GetLongitudeFromString(const char* str); + static double GetLatitudeFromString(const char* str); + + void WriteGENFile(); + void WriteTHFFile(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* ADRGRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class ADRGRasterBand : public GDALPamRasterBand +{ + friend class ADRGDataset; + + public: + ADRGRasterBand( ADRGDataset *, int ); + + virtual GDALColorInterp GetColorInterpretation(); + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + +// virtual int GetOverviewCount(); +// virtual GDALRasterBand* GetOverview(int i); +}; + + +/************************************************************************/ +/* ADRGRasterBand() */ +/************************************************************************/ + +ADRGRasterBand::ADRGRasterBand( ADRGDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Byte; + + nBlockXSize = 128; + nBlockYSize = 128; +} + +#if 0 + +/* We have a problem with the overview. Its geo bounding box doesn't match */ +/* exactly the one of the main image. We should handle the shift between */ +/* the two top level corners... */ + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int ADRGRasterBand::GetOverviewCount() + +{ + ADRGDataset* poDS = (ADRGDataset*)this->poDS; + if( poDS->poOverviewDS ) + return 1; + else + return GDALRasterBand::GetOverviewCount(); +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *ADRGRasterBand::GetOverview( int i ) + +{ + ADRGDataset* poDS = (ADRGDataset*)this->poDS; + if( poDS->poOverviewDS ) + { + if( i < 0 || i >= 1 ) + return NULL; + else + return poDS->poOverviewDS->GetRasterBand(nBand); + } + else + return GDALRasterBand::GetOverview( i ); +} +#endif + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double ADRGRasterBand::GetNoDataValue( int *pbSuccess ) +{ + if (pbSuccess) + *pbSuccess = TRUE; + + return 0; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp ADRGRasterBand::GetColorInterpretation() + +{ + if( nBand == 1 ) + return GCI_RedBand; + + else if( nBand == 2 ) + return GCI_GreenBand; + + else + return GCI_BlueBand; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr ADRGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + ADRGDataset* poDS = (ADRGDataset*)this->poDS; + int offset; + int nBlock = nBlockYOff * poDS->NFC + nBlockXOff; + if (nBlockXOff >= poDS->NFC || nBlockYOff >= poDS->NFL) + { + CPLError(CE_Failure, CPLE_AppDefined, "nBlockXOff=%d, NFC=%d, nBlockYOff=%d, NFL=%d", + nBlockXOff, poDS->NFC, nBlockYOff, poDS->NFL); + return CE_Failure; + } + CPLDebug("ADRG", "(%d,%d) -> nBlock = %d", nBlockXOff, nBlockYOff, nBlock); + + if (poDS->TILEINDEX) + { + if (poDS->TILEINDEX[nBlock] == 0) + { + memset(pImage, 0, 128 * 128); + return CE_None; + } + offset = poDS->offsetInIMG + (poDS->TILEINDEX[nBlock] - 1) * 128 * 128 * 3 + (nBand - 1) * 128 * 128; + } + else + offset = poDS->offsetInIMG + nBlock * 128 * 128 * 3 + (nBand - 1) * 128 * 128; + + if (VSIFSeekL(poDS->fdIMG, offset, SEEK_SET) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, "Cannot seek to offset %d", offset); + return CE_Failure; + } + if (VSIFReadL(pImage, 1, 128 * 128, poDS->fdIMG) != 128 * 128) + { + CPLError(CE_Failure, CPLE_FileIO, "Cannot read data at offset %d", offset); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr ADRGRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + ADRGDataset* poDS = (ADRGDataset*)this->poDS; + int offset; + int nBlock = nBlockYOff * poDS->NFC + nBlockXOff; + if (poDS->eAccess != GA_Update) + { + return CE_Failure; + } + if (nBlockXOff >= poDS->NFC || nBlockYOff >= poDS->NFL) + { + CPLError(CE_Failure, CPLE_AppDefined, "nBlockXOff=%d, NFC=%d, nBlockYOff=%d, NFL=%d", + nBlockXOff, poDS->NFC, nBlockYOff, poDS->NFL); + return CE_Failure; + } + CPLDebug("ADRG", "(%d,%d) -> nBlock = %d", nBlockXOff, nBlockYOff, nBlock); + + if (poDS->TILEINDEX[nBlock] == 0) + { + unsigned int i; + int* pi = (int*)pImage; + for(i=0;i<128*128 / sizeof(int);i++) + { + if (pi[i]) + break; + } + if (i == 128*128 / sizeof(int)) + { + return CE_None; + } + + poDS->TILEINDEX[nBlock] = poDS->nNextAvailableBlock ++; + } + + offset = poDS->offsetInIMG + (poDS->TILEINDEX[nBlock] - 1) * 128 * 128 * 3 + (nBand - 1) * 128 * 128; + + if (VSIFSeekL(poDS->fdIMG, offset, SEEK_SET) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, "Cannot seek to offset %d", offset); + return CE_Failure; + } + if (VSIFWriteL(pImage, 1, 128 * 128, poDS->fdIMG) != 128 * 128) + { + CPLError(CE_Failure, CPLE_FileIO, "Cannot read data at offset %d", offset); + return CE_Failure; + } + + return CE_None; +} + +static unsigned int WriteSubFieldStr(VSILFILE* fd, const char* pszStr, unsigned int size) +{ + char* str = (char*)CPLMalloc(size+1); + memset(str, ' ', size); + if (strlen(pszStr) > size) + { + CPLError(CE_Failure, CPLE_AppDefined, "strlen(pszStr) > size"); + CPLFree(str); + return size; + } + strcpy(str, pszStr); + str[strlen(pszStr)] = ' '; + VSIFWriteL(str, 1, size, fd); + CPLFree(str); + return size; +} + +static unsigned int WriteSubFieldInt(VSILFILE* fd, int val, unsigned int size) +{ + char* str = (char*)CPLMalloc(size+1); + char formatStr[32]; + sprintf( formatStr, "%%0%dd", size); + sprintf( str, formatStr, val); + VSIFWriteL(str, 1, size, fd); + CPLFree(str); + return size; +} + +static unsigned int WriteFieldTerminator(VSILFILE* fd) +{ + char fieldTerminator = 30; + VSIFWriteL(&fieldTerminator, 1, 1, fd); + return 1; +} + +static unsigned int WriteUnitTerminator(VSILFILE* fd) +{ + char fieldTerminator = 31; + VSIFWriteL(&fieldTerminator, 1, 1, fd); + return 1; +} + +static unsigned int WriteLongitude(VSILFILE* fd, double val) +{ + char str[11+1]; + char sign = (val >= 0) ? '+' : '-'; + if (val < 0) val = -val; + int ddd = (int)val; + int mm = (int)((val - ddd) * 60); + double ssdotss = ((val - ddd) * 60 - mm) * 60; + sprintf(str, "%c%03d%02d%05.2f", sign, ddd, mm, ssdotss); + CPLAssert((int)strlen(str) == 11); + VSIFWriteL(str, 1, 11, fd); + return 11; +} + +static unsigned int WriteLatitude(VSILFILE* fd, double val) +{ + char str[10+1]; + char sign = (val >= 0) ? '+' : '-'; + if (val < 0) val = -val; + int dd = (int)val; + int mm = (int)((val - dd) * 60); + double ssdotss = ((val - dd) * 60 - mm) * 60; + sprintf(str, "%c%02d%02d%05.2f", sign, dd, mm, ssdotss); + CPLAssert((int)strlen(str) == 10); + VSIFWriteL(str, 1, 10, fd); + return 10; +} + +static int BeginLeader(VSILFILE* fd, int sizeFieldLength, int sizeFieldPos, int sizeFieldTag, + int nFields) +{ + int pos = (int)VSIFTellL(fd); + VSIFSeekL(fd, 24 + (sizeFieldLength + sizeFieldPos + sizeFieldTag) * (vsi_l_offset)nFields + 1, SEEK_CUR); + return pos; +} + +static void FinishWriteLeader(VSILFILE* fd, int beginPos, int sizeFieldLength, int sizeFieldPos, int sizeFieldTag, + int nFields, int* sizeOfFields, const char** nameOfFields) +{ + int endPos = (int)VSIFTellL(fd); + VSIFSeekL(fd, beginPos, SEEK_SET); + + int nLeaderSize = 24; + char szLeader[24+1]; + memset(szLeader, ' ', nLeaderSize); + + int i; + int nDataSize = 0; + int nFieldOffset = 0; + for(i=0;i 0 && osIMGFileName.size() > 0) + { + CPLString osMainFilename = GetDescription(); + int bMainFileReal; + VSIStatBufL sStat; + + bMainFileReal = VSIStatL( osMainFilename, &sStat ) == 0; + if (bMainFileReal) + { + CPLString osShortMainFilename = CPLGetFilename(osMainFilename); + CPLString osShortGENFileName = CPLGetFilename(osGENFileName); + if ( !EQUAL(osShortMainFilename.c_str(), osShortGENFileName.c_str()) ) + papszFileList = CSLAddString(papszFileList, osGENFileName.c_str()); + } + else + papszFileList = CSLAddString(papszFileList, osGENFileName.c_str()); + + papszFileList = CSLAddString(papszFileList, osIMGFileName.c_str()); + } + + return papszFileList; +} + +/************************************************************************/ +/* AddSubDataset() */ +/************************************************************************/ + +void ADRGDataset::AddSubDataset( const char* pszGENFileName, const char* pszIMGFileName ) +{ + char szName[80]; + int nCount = CSLCount(papszSubDatasets ) / 2; + + CPLString osSubDatasetName; + osSubDatasetName = "ADRG:"; + osSubDatasetName += pszGENFileName; + osSubDatasetName += ","; + osSubDatasetName += pszIMGFileName; + + sprintf( szName, "SUBDATASET_%d_NAME", nCount+1 ); + papszSubDatasets = + CSLSetNameValue( papszSubDatasets, szName, osSubDatasetName); + + sprintf( szName, "SUBDATASET_%d_DESC", nCount+1 ); + papszSubDatasets = + CSLSetNameValue( papszSubDatasets, szName, osSubDatasetName); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **ADRGDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "SUBDATASETS", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **ADRGDataset::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain != NULL && EQUAL(pszDomain,"SUBDATASETS") ) + return papszSubDatasets; + + return GDALPamDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* ADRGDataset::GetProjectionRef() +{ + return( "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]]" ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr ADRGDataset::GetGeoTransform( double * padfGeoTransform) +{ + if (papszSubDatasets != NULL) + return CE_Failure; + + memcpy( padfGeoTransform, adfGeoTransform, sizeof(double)*6 ); + + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr ADRGDataset::SetGeoTransform( double * padfGeoTransform ) + +{ + memcpy( adfGeoTransform, padfGeoTransform, sizeof(double)*6 ); + bGeoTransformValid = TRUE; + return CE_None; +} + +/************************************************************************/ +/* GetLongitudeFromString() */ +/************************************************************************/ + +double ADRGDataset::GetLongitudeFromString(const char* str) +{ + char ddd[3+1] = { 0 }; + char mm[2+1] = { 0 }; + char ssdotss[5+1] = { 0 }; + int sign = (str[0] == '+') ? 1 : - 1; + str++; + strncpy(ddd, str, 3); + str+=3; + strncpy(mm, str, 2); + str+=2; + strncpy(ssdotss, str, 5); + return sign * (CPLAtof(ddd) + CPLAtof(mm) / 60 + CPLAtof(ssdotss) / 3600); +} + +/************************************************************************/ +/* GetLatitudeFromString() */ +/************************************************************************/ + +double ADRGDataset::GetLatitudeFromString(const char* str) +{ + char ddd[2+1] = { 0 }; + char mm[2+1] = { 0 }; + char ssdotss[5+1] = { 0 }; + int sign = (str[0] == '+') ? 1 : - 1; + str++; + strncpy(ddd, str, 2); + str+=2; + strncpy(mm, str, 2); + str+=2; + strncpy(ssdotss, str, 5); + return sign * (CPLAtof(ddd) + CPLAtof(mm) / 60 + CPLAtof(ssdotss) / 3600); +} + +/************************************************************************/ +/* FindRecordInGENForIMG() */ +/************************************************************************/ + +DDFRecord* ADRGDataset::FindRecordInGENForIMG(DDFModule& module, + const char* pszGENFileName, + const char* pszIMGFileName) +{ + /* Finds the GEN file corresponding to the IMG file */ + if (!module.Open(pszGENFileName, TRUE)) + return NULL; + + + CPLString osShortIMGFilename = CPLGetFilename(pszIMGFileName); + + DDFField* field; + DDFFieldDefn *fieldDefn; + + /* Now finds the record */ + while (TRUE) + { + CPLPushErrorHandler( CPLQuietErrorHandler ); + DDFRecord* record = module.ReadRecord(); + CPLPopErrorHandler(); + CPLErrorReset(); + if (record == NULL) + return NULL; + + if (record->GetFieldCount() >= 5) + { + field = record->GetField(0); + fieldDefn = field->GetFieldDefn(); + if (!(strcmp(fieldDefn->GetName(), "001") == 0 && + fieldDefn->GetSubfieldCount() == 2)) + { + continue; + } + + const char* RTY = record->GetStringSubfield("001", 0, "RTY", 0); + if( RTY == NULL ) + continue; + /* Ignore overviews */ + if ( strcmp(RTY, "OVV") == 0 ) + continue; + + if ( strcmp(RTY, "GIN") != 0 ) + continue; + + field = record->GetField(3); + fieldDefn = field->GetFieldDefn(); + + if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 && + fieldDefn->GetSubfieldCount() == 15)) + { + continue; + } + + const char* pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0); + if( pszBAD == NULL || strlen(pszBAD) != 12 ) + continue; + CPLString osBAD = pszBAD; + { + char* c = (char*) strchr(osBAD.c_str(), ' '); + if (c) + *c = 0; + } + + if (EQUAL(osShortIMGFilename.c_str(), osBAD.c_str())) + { + return record; + } + } + } +} + +/************************************************************************/ +/* OpenDataset() */ +/************************************************************************/ + +ADRGDataset* ADRGDataset::OpenDataset( + const char* pszGENFileName, const char* pszIMGFileName, DDFRecord* record) +{ + DDFModule module; + + int SCA = 0; + int ZNA = 0; + double PSP; + int ARV; + int BRV; + double LSO; + double PSO; + int NFL; + int NFC; + CPLString osBAD; + int TIF; + int* TILEINDEX = NULL; + int i; + + DDFField* field; + DDFFieldDefn *fieldDefn; + DDFSubfieldDefn* subfieldDefn; + + if (record == NULL) + { + record = FindRecordInGENForIMG(module, pszGENFileName, pszIMGFileName); + if (record == NULL) + return NULL; + } + + field = record->GetField(1); + if( field == NULL ) + return NULL; + fieldDefn = field->GetFieldDefn(); + + if (!(strcmp(fieldDefn->GetName(), "DSI") == 0 && + fieldDefn->GetSubfieldCount() == 2)) + { + return NULL; + } + + const char* pszPTR = record->GetStringSubfield("DSI", 0, "PRT", 0); + if( pszPTR == NULL || !EQUAL(pszPTR, "ADRG") ) + return NULL; + + const char* pszNAM = record->GetStringSubfield("DSI", 0, "NAM", 0); + if( pszNAM == NULL || strlen(pszNAM) != 8 ) + return NULL; + CPLString osNAM = pszNAM; + + field = record->GetField(2); + if( field == NULL ) + return NULL; + fieldDefn = field->GetFieldDefn(); + + int isGIN = TRUE; + + if (isGIN) + { + if (!(strcmp(fieldDefn->GetName(), "GEN") == 0 && + fieldDefn->GetSubfieldCount() == 21)) + { + return NULL; + } + + if( record->GetIntSubfield("GEN", 0, "STR", 0) != 3 ) + return NULL; + + SCA = record->GetIntSubfield("GEN", 0, "SCA", 0); + CPLDebug("ADRG", "SCA=%d", SCA); + + ZNA = record->GetIntSubfield("GEN", 0, "ZNA", 0); + CPLDebug("ADRG", "ZNA=%d", ZNA); + + PSP = record->GetFloatSubfield("GEN", 0, "PSP", 0); + CPLDebug("ADRG", "PSP=%f", PSP); + + ARV = record->GetIntSubfield("GEN", 0, "ARV", 0); + CPLDebug("ADRG", "ARV=%d", ARV); + + BRV = record->GetIntSubfield("GEN", 0, "BRV", 0); + CPLDebug("ADRG", "BRV=%d", BRV); + + const char* pszLSO = record->GetStringSubfield("GEN", 0, "LSO", 0); + if( pszLSO == NULL || strlen(pszLSO) != 11 ) + return NULL; + LSO = GetLongitudeFromString(pszLSO); + CPLDebug("ADRG", "LSO=%f", LSO); + + const char* pszPSO = record->GetStringSubfield("GEN", 0, "PSO", 0); + if( pszPSO == NULL || strlen(pszPSO) != 10 ) + return NULL; + PSO = GetLatitudeFromString(pszPSO); + CPLDebug("ADRG", "PSO=%f", PSO); + } + else + { + if (!(strcmp(fieldDefn->GetName(), "OVI") == 0 && + fieldDefn->GetSubfieldCount() == 5)) + { + return NULL; + } + + if( record->GetIntSubfield("OVI", 0, "STR", 0) != 3 ) + return NULL; + + ARV = record->GetIntSubfield("OVI", 0, "ARV", 0); + CPLDebug("ADRG", "ARV=%d", ARV); + + BRV = record->GetIntSubfield("OVI", 0, "BRV", 0); + CPLDebug("ADRG", "BRV=%d", BRV); + + const char* pszLSO = record->GetStringSubfield("OVI", 0, "LSO", 0); + if( pszLSO == NULL || strlen(pszLSO) != 11 ) + return NULL; + LSO = GetLongitudeFromString(pszLSO); + CPLDebug("ADRG", "LSO=%f", LSO); + + const char* pszPSO = record->GetStringSubfield("OVI", 0, "PSO", 0); + if( pszPSO == NULL || strlen(pszPSO) != 10 ) + return NULL; + PSO = GetLatitudeFromString(pszPSO); + CPLDebug("ADRG", "PSO=%f", PSO); + } + + field = record->GetField(3); + if( field == NULL ) + return NULL; + fieldDefn = field->GetFieldDefn(); + + if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 && + fieldDefn->GetSubfieldCount() == 15)) + { + return NULL; + } + + NFL = record->GetIntSubfield("SPR", 0, "NFL", 0); + CPLDebug("ADRG", "NFL=%d", NFL); + + NFC = record->GetIntSubfield("SPR", 0, "NFC", 0); + CPLDebug("ADRG", "NFC=%d", NFC); + + int PNC = record->GetIntSubfield("SPR", 0, "PNC", 0); + CPLDebug("ADRG", "PNC=%d", PNC); + if (PNC != 128) + { + return NULL; + } + + int PNL = record->GetIntSubfield("SPR", 0, "PNL", 0); + CPLDebug("ADRG", "PNL=%d", PNL); + if (PNL != 128) + { + return NULL; + } + + const char* pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0); + if( pszBAD == NULL || strlen(pszBAD) != 12 ) + return NULL; + osBAD = pszBAD; + { + char* c = (char*) strchr(osBAD.c_str(), ' '); + if (c) + *c = 0; + } + CPLDebug("ADRG", "BAD=%s", osBAD.c_str()); + + subfieldDefn = fieldDefn->GetSubfield(14); + if (!(strcmp(subfieldDefn->GetName(), "TIF") == 0 && + (subfieldDefn->GetFormat())[0] == 'A')) + { + return NULL; + } + + const char* pszTIF = record->GetStringSubfield("SPR", 0, "TIF", 0); + if( pszTIF == NULL) + return NULL; + TIF = pszTIF[0] == 'Y'; + CPLDebug("ADRG", "TIF=%d", TIF); + + if (TIF) + { + if (record->GetFieldCount() != 6) + { + return NULL; + } + + field = record->GetField(5); + if( field == NULL ) + return NULL; + fieldDefn = field->GetFieldDefn(); + + if (!(strcmp(fieldDefn->GetName(), "TIM") == 0)) + { + return NULL; + } + + if (field->GetDataSize() != 5 * NFL * NFC + 1) + { + return NULL; + } + + TILEINDEX = new int [NFL * NFC]; + const char* ptr = field->GetData(); + char offset[5+1]={0}; + for(i=0;iosGENFileName = pszGENFileName; + poDS->osIMGFileName = pszIMGFileName; + poDS->NFC = NFC; + poDS->NFL = NFL; + poDS->nRasterXSize = NFC * 128; + poDS->nRasterYSize = NFL * 128; + poDS->LSO = LSO; + poDS->PSO = PSO; + poDS->ARV = ARV; + poDS->BRV = BRV; + poDS->TILEINDEX = TILEINDEX; + poDS->fdIMG = fdIMG; + poDS->offsetInIMG = offsetInIMG; + poDS->poOverviewDS = NULL; + + poDS->adfGeoTransform[0] = LSO; + poDS->adfGeoTransform[1] = 360. / ARV; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = PSO; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = - 360. / BRV; + + if (isGIN) + { + char pszValue[32]; + sprintf(pszValue, "%d", SCA); + poDS->SetMetadataItem( "ADRG_SCA", pszValue ); + } + + poDS->SetMetadataItem( "ADRG_NAM", osNAM.c_str() ); + + poDS->nBands = 3; + for( i = 0; i < poDS->nBands; i++ ) + poDS->SetBand( i+1, new ADRGRasterBand( poDS, i+1 ) ); + + return poDS; +} + +/************************************************************************/ +/* GetGENListFromTHF() */ +/************************************************************************/ + +char** ADRGDataset::GetGENListFromTHF(const char* pszFileName) +{ + DDFModule module; + DDFRecord * record; + DDFField* field; + DDFFieldDefn *fieldDefn; + int i; + int nFilenames = 0; + char** papszFileNames = NULL; + + if (!module.Open(pszFileName, TRUE)) + return papszFileNames; + + while (TRUE) + { + CPLPushErrorHandler( CPLQuietErrorHandler ); + record = module.ReadRecord(); + CPLPopErrorHandler(); + CPLErrorReset(); + if (record == NULL) + break; + + if (record->GetFieldCount() >= 2) + { + field = record->GetField(0); + fieldDefn = field->GetFieldDefn(); + if (!(strcmp(fieldDefn->GetName(), "001") == 0 && + fieldDefn->GetSubfieldCount() == 2)) + { + continue; + } + + const char* RTY = record->GetStringSubfield("001", 0, "RTY", 0); + if ( RTY == NULL || !( strcmp(RTY, "TFN") == 0 )) + { + continue; + } + + int iVFFFieldInstance = 0; + for (i = 1; i < record->GetFieldCount() ; i++) + { + field = record->GetField(i); + fieldDefn = field->GetFieldDefn(); + + if (!(strcmp(fieldDefn->GetName(), "VFF") == 0 && + fieldDefn->GetSubfieldCount() == 1)) + { + continue; + } + + const char* pszVFF = record->GetStringSubfield("VFF", iVFFFieldInstance++, "VFF", 0); + if( pszVFF == NULL ) + continue; + CPLString osSubFileName(pszVFF); + char* c = (char*) strchr(osSubFileName.c_str(), ' '); + if (c) + *c = 0; + if (EQUAL(CPLGetExtension(osSubFileName.c_str()), "GEN")) + { + CPLDebug("ADRG", "Found GEN file in THF : %s", osSubFileName.c_str()); + CPLString osGENFileName(CPLGetDirname(pszFileName)); + char** tokens = CSLTokenizeString2( osSubFileName.c_str(), "/\"", 0); + char** ptr = tokens; + if (ptr == NULL) + continue; + while(*ptr) + { + char** papszDirContent = VSIReadDir(osGENFileName.c_str()); + char** ptrDir = papszDirContent; + if (ptrDir) + { + while(*ptrDir) + { + if (EQUAL(*ptrDir, *ptr)) + { + osGENFileName = CPLFormFilename(osGENFileName.c_str(), *ptrDir, NULL); + CPLDebug("ADRG", "Building GEN full file name : %s", osGENFileName.c_str()); + break; + } + ptrDir ++; + } + } + if (ptrDir == NULL) + break; + CSLDestroy(papszDirContent); + ptr++; + } + int isNameValid = *ptr == NULL; + CSLDestroy(tokens); + if (isNameValid) + { + papszFileNames = (char**)CPLRealloc(papszFileNames, sizeof(char*) * (nFilenames + 2)); + papszFileNames[nFilenames] = CPLStrdup(osGENFileName.c_str()); + papszFileNames[nFilenames + 1] = NULL; + nFilenames ++; + } + } + } + } + } + return papszFileNames; +} + +/************************************************************************/ +/* GetIMGListFromGEN() */ +/************************************************************************/ + +char** ADRGDataset::GetIMGListFromGEN(const char* pszFileName, + int *pnRecordIndex) +{ + DDFRecord * record; + DDFField* field; + DDFFieldDefn *fieldDefn; + int nFilenames = 0; + char** papszFileNames = NULL; + int nRecordIndex = -1; + + if (pnRecordIndex) + *pnRecordIndex = -1; + + DDFModule module; + if (!module.Open(pszFileName, TRUE)) + return NULL; + + while (TRUE) + { + nRecordIndex ++; + + CPLPushErrorHandler( CPLQuietErrorHandler ); + record = module.ReadRecord(); + CPLPopErrorHandler(); + CPLErrorReset(); + if (record == NULL) + break; + + if (record->GetFieldCount() >= 5) + { + field = record->GetField(0); + fieldDefn = field->GetFieldDefn(); + if (!(strcmp(fieldDefn->GetName(), "001") == 0 && + fieldDefn->GetSubfieldCount() == 2)) + { + continue; + } + + const char* RTY = record->GetStringSubfield("001", 0, "RTY", 0); + if( RTY == NULL ) + continue; + /* Ignore overviews */ + if ( strcmp(RTY, "OVV") == 0 ) + continue; + + if ( strcmp(RTY, "GIN") != 0 ) + continue; + + field = record->GetField(3); + if( field == NULL ) + continue; + fieldDefn = field->GetFieldDefn(); + + if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 && + fieldDefn->GetSubfieldCount() == 15)) + { + continue; + } + + const char* pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0); + if( pszBAD == NULL || strlen(pszBAD) != 12 ) + continue; + CPLString osBAD = pszBAD; + { + char* c = (char*) strchr(osBAD.c_str(), ' '); + if (c) + *c = 0; + } + CPLDebug("ADRG", "BAD=%s", osBAD.c_str()); + + /* Build full IMG file name from BAD value */ + CPLString osGENDir(CPLGetDirname(pszFileName)); + + CPLString osFileName = CPLFormFilename(osGENDir.c_str(), osBAD.c_str(), NULL); + VSIStatBufL sStatBuf; + if( VSIStatL( osFileName, &sStatBuf ) == 0 ) + { + osBAD = osFileName; + CPLDebug("ADRG", "Building IMG full file name : %s", osBAD.c_str()); + } + else + { + char** papszDirContent; + if (strcmp(osGENDir.c_str(), "/vsimem") == 0) + { + CPLString osTmp = osGENDir + "/"; + papszDirContent = VSIReadDir(osTmp); + } + else + papszDirContent = VSIReadDir(osGENDir); + char** ptrDir = papszDirContent; + while(ptrDir && *ptrDir) + { + if (EQUAL(*ptrDir, osBAD.c_str())) + { + osBAD = CPLFormFilename(osGENDir.c_str(), *ptrDir, NULL); + CPLDebug("ADRG", "Building IMG full file name : %s", osBAD.c_str()); + break; + } + ptrDir ++; + } + CSLDestroy(papszDirContent); + } + + if (nFilenames == 0 && pnRecordIndex) + *pnRecordIndex = nRecordIndex; + + papszFileNames = (char**)CPLRealloc(papszFileNames, sizeof(char*) * (nFilenames + 2)); + papszFileNames[nFilenames] = CPLStrdup(osBAD.c_str()); + papszFileNames[nFilenames + 1] = NULL; + nFilenames ++; + } + } + + return papszFileNames; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *ADRGDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + int nRecordIndex = -1; + CPLString osGENFileName; + CPLString osIMGFileName; + int bFromSubdataset = FALSE; + + if( EQUALN(poOpenInfo->pszFilename, "ADRG:", 5) ) + { + char** papszTokens = CSLTokenizeString2(poOpenInfo->pszFilename + 5, ",", 0); + if (CSLCount(papszTokens) == 2) + { + osGENFileName = papszTokens[0]; + osIMGFileName = papszTokens[1]; + bFromSubdataset = TRUE; + } + CSLDestroy(papszTokens); + } + else + { + if( poOpenInfo->nHeaderBytes < 500 ) + return NULL; + + CPLString osFileName(poOpenInfo->pszFilename); + if (EQUAL(CPLGetExtension(osFileName.c_str()), "THF")) + { + char** papszFileNames = GetGENListFromTHF(osFileName.c_str()); + if (papszFileNames == NULL) + return NULL; + if (papszFileNames[1] == NULL) + { + osFileName = papszFileNames[0]; + CSLDestroy(papszFileNames); + } + else + { + char** ptr = papszFileNames; + ADRGDataset* poDS = new ADRGDataset(); + while(*ptr) + { + char** papszIMGFileNames = GetIMGListFromGEN(*ptr); + char** papszIMGIter = papszIMGFileNames; + while(papszIMGIter && *papszIMGIter) + { + poDS->AddSubDataset(*ptr, *papszIMGIter); + papszIMGIter ++; + } + CSLDestroy(papszIMGFileNames); + + ptr ++; + } + CSLDestroy(papszFileNames); + return poDS; + } + } + + if (EQUAL(CPLGetExtension(osFileName.c_str()), "GEN")) + { + osGENFileName = osFileName; + + char** papszFileNames = GetIMGListFromGEN(osFileName.c_str(), &nRecordIndex); + if (papszFileNames == NULL) + return NULL; + if (papszFileNames[1] == NULL) + { + osIMGFileName = papszFileNames[0]; + CSLDestroy(papszFileNames); + } + else + { + char** ptr = papszFileNames; + ADRGDataset* poDS = new ADRGDataset(); + while(*ptr) + { + poDS->AddSubDataset(osFileName.c_str(), *ptr); + ptr ++; + } + CSLDestroy(papszFileNames); + return poDS; + } + } + } + + if (osGENFileName.size() > 0 && + osIMGFileName.size() > 0) + { + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The ADRG driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + DDFModule module; + DDFRecord* record = NULL; + if (nRecordIndex >= 0 && + module.Open(osGENFileName.c_str(), TRUE)) + { + int i; + for(i=0;i<=nRecordIndex;i++) + { + CPLPushErrorHandler( CPLQuietErrorHandler ); + record = module.ReadRecord(); + CPLPopErrorHandler(); + CPLErrorReset(); + if (record == NULL) + break; + } + } + + ADRGDataset* poDS = OpenDataset(osGENFileName.c_str(), osIMGFileName.c_str(), record); + + if (poDS) + { + /* -------------------------------------------------------------------- */ + /* Initialize any PAM information. */ + /* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + + /* -------------------------------------------------------------------- */ + /* Check for external overviews. */ + /* -------------------------------------------------------------------- */ + if( bFromSubdataset ) + poDS->oOvManager.Initialize( poDS, osIMGFileName.c_str() ); + else + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return poDS; + } + } + + return NULL; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *ADRGDataset::Create(const char* pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + CPL_UNUSED char **papszOptions) +{ + int i; + + if( eType != GDT_Byte) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create ADRG dataset with an illegal\n" + "data type (%s), only Byte supported by the format.\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + + if( nBands != 3 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "ADRG driver doesn't support %d bands. Must be 3 (rgb) bands.\n", + nBands ); + return NULL; + } + + if(nXSize < 1 || nYSize < 1) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Specified pixel dimensions (% d x %d) are bad.\n", + nXSize, nYSize ); + } + + if (!EQUAL(CPLGetExtension(pszFilename), "gen")) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid filename. Must be ABCDEF01.GEN\n"); + return NULL; + } + + CPLString osBaseFileName(CPLGetBasename(pszFilename)); + if (strlen(osBaseFileName) != 8 || osBaseFileName[6] != '0' || osBaseFileName[7] != '1') + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid filename. Must be xxxxxx01.GEN where x is between A and Z\n"); + return NULL; + } + + for(i=0;i<6;i++) + { + if (!(osBaseFileName[i] >= 'A' && osBaseFileName[i] <= 'Z')) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid filename. Must be xxxxxx01.GEN where x is between A and Z\n"); + return NULL; + } + } + + VSILFILE* fdGEN = VSIFOpenL(pszFilename, "wb"); + if (fdGEN == NULL) + { + CPLError( CE_Failure, CPLE_FileIO, + "Cannot create GEN file : %s.\n", pszFilename); + return NULL; + } + + CPLString osDirname(CPLGetDirname(pszFilename)); + CPLString osTransh01THF(CPLFormFilename(osDirname.c_str(), "TRANSH01.THF", NULL)); + VSILFILE* fdTHF = VSIFOpenL(osTransh01THF.c_str(), "wb"); + if (fdTHF == NULL) + { + VSIFCloseL(fdGEN); + CPLError( CE_Failure, CPLE_FileIO, + "Cannot create THF file : %s.\n", osTransh01THF.c_str()); + return NULL; + } + + CPLString osImgFilename = CPLResetExtension(pszFilename, "IMG"); + VSILFILE* fdIMG = VSIFOpenL(osImgFilename.c_str(), "w+b"); + if (fdIMG == NULL) + { + VSIFCloseL(fdGEN); + VSIFCloseL(fdTHF); + CPLError( CE_Failure, CPLE_FileIO, + "Cannot create image file : %s.\n", osImgFilename.c_str()); + return NULL; + } + + ADRGDataset* poDS = new ADRGDataset(); + + poDS->eAccess = GA_Update; + + poDS->fdGEN = fdGEN; + poDS->fdIMG = fdIMG; + poDS->fdTHF = fdTHF; + + poDS->osBaseFileName = osBaseFileName; + poDS->bCreation = TRUE; + poDS->nNextAvailableBlock = 1; + poDS->NFC = (nXSize + 127) / 128; + poDS->NFL = (nYSize + 127) / 128; + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->bGeoTransformValid = FALSE; + poDS->TILEINDEX = new int [poDS->NFC*poDS->NFL]; + memset(poDS->TILEINDEX, 0, sizeof(int)*poDS->NFC*poDS->NFL); + poDS->offsetInIMG = 2048; + poDS->poOverviewDS = NULL; + + poDS->nBands = 3; + for( i = 0; i < poDS->nBands; i++ ) + poDS->SetBand( i+1, new ADRGRasterBand( poDS, i+1 ) ); + + return poDS; +} + +/************************************************************************/ +/* WriteGENFile_Header() */ +/************************************************************************/ + +static void WriteGENFile_Header(VSILFILE* fd) +{ + int nFields = 0; + int sizeOfFields[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, }; + const char* nameOfFields[] = { "000", "001", "DRF", "DSI", "OVI", "GEN", "SPR", "BDF", "TIM" }; + int pos = BeginHeader(fd, 3, 4, 3, N_ELEMENTS(sizeOfFields)); + + sizeOfFields[nFields++] += WriteFieldDecl(fd, ' ', ' ', "GENERAL_INFORMATION_FILE", "", ""); /* 000 */ + sizeOfFields[nFields++] += WriteFieldDecl(fd, '1', '0', "RECORD_ID_FIELD", /* 001 */ + "RTY!RID", + "(A(3),A(2))"); + sizeOfFields[nFields++] += WriteFieldDecl(fd, '1', '1', "DATA_SET_DESCRIPTION_FIELD", /* DRF */ + "NSH!NSV!NOZ!NOS", + "(4I(2))"); + sizeOfFields[nFields++] += WriteFieldDecl(fd, '1', '0', "DATA_SET-ID_FIELD", /* DSI */ + "PRT!NAM", + "(A(4),A(8))"); + sizeOfFields[nFields++] += WriteFieldDecl(fd, '1', '6', "OVERVIEW_INFORMATION_FIELD", /* OVI */ + "STR!ARV!BRV!LSO!PSO", + "(I(1),I(8),I(8),A(11),A(10))"); + sizeOfFields[nFields++] += WriteFieldDecl(fd, '1', '6', "GENERAL_INFORMATION_FIELD", /* GEN */ + "STR!LOD!LAD!UNIloa!SWO!SWA!NWO!NWA!NEO!NEA!SEO!SEA!SCA!ZNA!PSP!IMR!ARV!BRV!LSO!PSO!TXT", + "(I(1),2R(6),I(3),A(11),A(10),A(11),A(10),A(11),A(10),A(11),A(10),I(9),I(2),R(5),A(1),2I(8),A(11),A(10),A(64))"); + sizeOfFields[nFields++] += WriteFieldDecl(fd, '1', '6', "DATA_SET_PARAMETERS_FIELD", /* SPR */ + "NUL!NUS!NLL!NLS!NFL!NFC!PNC!PNL!COD!ROD!POR!PCB!PVB!BAD!TIF", + "(4I(6),2I(3),2I(6),5I(1),A(12),A(1))"); + sizeOfFields[nFields++] += WriteFieldDecl(fd, '2', '6', "BAND_ID_FIELD", /* BDF */ + "*BID!WS1!WS2", + "(A(5),I(5),I(5))"); + sizeOfFields[nFields++] += WriteFieldDecl(fd, '2', '1', "TILE_INDEX_MAP_FIELD", /* TIM */ + "*TSI", + "(I(5))"); + + FinishWriteHeader(fd, pos, 3, 4, 3, N_ELEMENTS(sizeOfFields), sizeOfFields, nameOfFields); +} + +/************************************************************************/ +/* WriteGENFile_DataSetDescriptionRecord() */ +/************************************************************************/ + +/* Write DATA_SET_DESCRIPTION_RECORD */ +static void WriteGENFile_DataSetDescriptionRecord(VSILFILE* fd) +{ + int nFields = 0; + int sizeOfFields[] = {0, 0}; + const char* nameOfFields[] = { "001", "DRF" }; + int pos = BeginLeader(fd, 3, 4, 3, N_ELEMENTS(sizeOfFields)); + + /* Field 001 */ + sizeOfFields[nFields] += WriteSubFieldStr(fd, "DSS", 3); /* RTY */ + sizeOfFields[nFields] += WriteSubFieldStr(fd, "01", 2); /* RID */ + sizeOfFields[nFields] += WriteFieldTerminator(fd); + nFields++; + + /* Field DRF */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 2); /* NSH */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 2); /* NSV */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 2); /* NOZ */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 2); /* NOS */ + sizeOfFields[nFields] += WriteFieldTerminator(fd); + nFields++; + + FinishWriteLeader(fd, pos, 3, 4, 3, N_ELEMENTS(sizeOfFields), sizeOfFields, nameOfFields); +} + +/************************************************************************/ +/* WriteGENFile_OverviewRecord() */ +/************************************************************************/ + +/* Write OVERVIEW_RECORD */ +static void WriteGENFile_OverviewRecord(VSILFILE* fd, CPLString& osBaseFileName, int ARV, int BRV, double LSO, double PSO, + int nOvSizeX, int nOvSizeY, int NFL, int NFC, int* TILEINDEX) +{ + int nFields = 0; + int sizeOfFields[] = {0, 0, 0, 0, 0, 0}; + const char* nameOfFields[] = { "001", "DSI", "OVI", "SPR", "BDF", "TIM" }; + int pos = BeginLeader(fd, 9, 9, 3, N_ELEMENTS(sizeOfFields)); + + /* Field 001 */ + sizeOfFields[nFields] += WriteSubFieldStr(fd, "OVV", 3); /* RTY */ + sizeOfFields[nFields] += WriteSubFieldStr(fd, "01", 2); /* RID */ + sizeOfFields[nFields] += WriteFieldTerminator(fd); + nFields++; + + /* Field DSI */ + sizeOfFields[nFields] += WriteSubFieldStr(fd, "ADRG", 4); /* PRT */ + sizeOfFields[nFields] += WriteSubFieldStr(fd, osBaseFileName, 8); /* NAM */ + sizeOfFields[nFields] += WriteFieldTerminator(fd); + nFields++; + + /* Field OVI */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 3, 1); /* STR */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, ARV, 8); /* ARV */ /* FIXME */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, BRV, 8); /* BRV */ /* FIXME */ + sizeOfFields[nFields] += WriteLongitude(fd, LSO); /* LSO */ /* FIXME */ + sizeOfFields[nFields] += WriteLatitude(fd, PSO); /* PSO */ /* FIXME */ + sizeOfFields[nFields] += WriteFieldTerminator(fd); + nFields++; + + /* Field SPR */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 6); /* NUL */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, nOvSizeX-1, 6); /* NUS */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, nOvSizeY-1, 6); /* NLL */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 6); /* NLS */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, (nOvSizeY + 127) / 128, 3); /* NFL */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, (nOvSizeX + 127) / 128, 3); /* NFC */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 128, 6); /* PNC */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 128, 6); /* PNL */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 1); /* COD */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 1, 1); /* ROD */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 1); /* POR */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 1); /* PCB */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 8, 1); /* PVB */ + char tmp[12+1]; + sprintf(tmp, "%s.IMG", osBaseFileName.c_str()); /* FIXME */ + sizeOfFields[nFields] += WriteSubFieldStr(fd, tmp, 12); /* BAD */ + sizeOfFields[nFields] += WriteSubFieldStr(fd, "Y", 1); /* TIF */ + sizeOfFields[nFields] += WriteFieldTerminator(fd); + nFields++; + + /* Field BDF */ + sizeOfFields[nFields] += WriteSubFieldStr(fd, "Red", 5); /* BID */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5); /* WS1 */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5); /* WS2 */ + sizeOfFields[nFields] += WriteSubFieldStr(fd, "Green", 5); /* BID */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5); /* WS1 */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5); /* WS2 */ + sizeOfFields[nFields] += WriteSubFieldStr(fd, "Blue", 5); /* BID */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5); /* WS1 */ + sizeOfFields[nFields] += WriteSubFieldInt(fd, 0, 5); /* WS2 */ + sizeOfFields[nFields] += WriteFieldTerminator(fd); + nFields++; + + /* Field TIM */ + int i; + for(i=0;iSetDescription( "ADRG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ARC Digitized Raster Graphics" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#ADRG" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gen" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = ADRGDataset::Open; + poDriver->pfnCreate = ADRGDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/adrg/makefile.vc b/bazaar/plugin/gdal/frmts/adrg/makefile.vc new file mode 100644 index 000000000..4bfea93a4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/adrg/makefile.vc @@ -0,0 +1,16 @@ + +OBJ = adrgdataset.obj srpdataset.obj + +EXTRAFLAGS = -I..\iso8211 + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + + diff --git a/bazaar/plugin/gdal/frmts/adrg/srpdataset.cpp b/bazaar/plugin/gdal/frmts/adrg/srpdataset.cpp new file mode 100644 index 000000000..cb6c48f96 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/adrg/srpdataset.cpp @@ -0,0 +1,1630 @@ +/****************************************************************************** + * $Id: srpdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * Purpose: ASRP/USRP Reader + * Author: Frank Warmerdam (warmerdam@pobox.com) + * + * Derived from ADRG driver by Even Rouault, even.rouault at mines-paris.org. + * + ****************************************************************************** + * Copyright (c) 2009-2013, Even Rouault + * Copyright (c) 2009, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include "iso8211.h" + +// Uncomment to recognize also .gen files in addition to .img files +// #define OPEN_GEN + +CPL_CVSID("$Id: srpdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +class SRPDataset : public GDALPamDataset +{ + friend class SRPRasterBand; + + static CPLString ResetTo01( const char* str ); + + VSILFILE* fdIMG; + int* TILEINDEX; + int offsetInIMG; + CPLString osProduct; + CPLString osSRS; + CPLString osGENFileName; + CPLString osQALFileName; + CPLString osIMGFileName; + int NFC; + int NFL; + int ZNA; + double LSO; + double PSO; + double LOD; + double LAD; + int ARV; + int BRV; + int PCB; + int PVB; + + + char** papszSubDatasets; + + GDALColorTable oCT; + + + static char** GetGENListFromTHF(const char* pszFileName); + static char** GetIMGListFromGEN(const char* pszFileName, int* pnRecordIndex = NULL); + static SRPDataset* OpenDataset(const char* pszGENFileName, const char* pszIMGFileName, DDFRecord* record = NULL); + static DDFRecord* FindRecordInGENForIMG(DDFModule& module, + const char* pszGENFileName, const char* pszIMGFileName); + +public: + SRPDataset(); + virtual ~SRPDataset(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * padfGeoTransform ); + + virtual char **GetMetadata( const char * pszDomain = "" ); + + virtual char **GetFileList(); + + int GetFromRecord( const char* pszFileName, + DDFRecord * record); + void AddSubDataset( const char* pszGENFileName, const char* pszIMGFileName ); + void AddMetadatafromFromTHF(const char* pszFileName); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* SRPRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class SRPRasterBand : public GDALPamRasterBand +{ + friend class SRPDataset; + + public: + SRPRasterBand( SRPDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); +}; + + +/************************************************************************/ +/* SRPRasterBand() */ +/************************************************************************/ + +SRPRasterBand::SRPRasterBand( SRPDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Byte; + + nBlockXSize = 128; + nBlockYSize = 128; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double SRPRasterBand::GetNoDataValue( int *pbSuccess ) +{ + if (pbSuccess) + *pbSuccess = TRUE; + + return 0; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp SRPRasterBand::GetColorInterpretation() + +{ + SRPDataset* poDS = (SRPDataset*)this->poDS; + + if( poDS->oCT.GetColorEntryCount() > 0 ) + return GCI_PaletteIndex; + else + return GCI_GrayIndex; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *SRPRasterBand::GetColorTable() + +{ + SRPDataset* poDS = (SRPDataset*)this->poDS; + + if( poDS->oCT.GetColorEntryCount() > 0 ) + return &(poDS->oCT); + else + return NULL; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr SRPRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + SRPDataset* poDS = (SRPDataset*)this->poDS; + int offset; + int nBlock = nBlockYOff * poDS->NFC + nBlockXOff; + if (nBlockXOff >= poDS->NFC || nBlockYOff >= poDS->NFL) + { + CPLError(CE_Failure, CPLE_AppDefined, "nBlockXOff=%d, NFC=%d, nBlockYOff=%d, NFL=%d", + nBlockXOff, poDS->NFC, nBlockYOff, poDS->NFL); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Is this a null block? */ +/* -------------------------------------------------------------------- */ + if (poDS->TILEINDEX && poDS->TILEINDEX[nBlock] == 0) + { + memset(pImage, 0, 128 * 128); + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Compute the offset to the block. */ +/* -------------------------------------------------------------------- */ + if (poDS->TILEINDEX) + { + if( poDS->PCB == 0 ) // uncompressed + offset = poDS->offsetInIMG + (poDS->TILEINDEX[nBlock] - 1) * 128 * 128; + else // compressed + offset = poDS->offsetInIMG + (poDS->TILEINDEX[nBlock] - 1); + } + else + offset = poDS->offsetInIMG + nBlock * 128 * 128; + +/* -------------------------------------------------------------------- */ +/* Seek to target location. */ +/* -------------------------------------------------------------------- */ + if (VSIFSeekL(poDS->fdIMG, offset, SEEK_SET) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, "Cannot seek to offset %d", offset); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* For uncompressed case we read the 128x128 and return with no */ +/* further processing. */ +/* -------------------------------------------------------------------- */ + if( poDS->PCB == 0 ) + { + if( VSIFReadL(pImage, 1, 128 * 128, poDS->fdIMG) != 128*128 ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Cannot read data at offset %d", offset); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* If this is compressed data, we read a goodly chunk of data */ +/* and then decode it. */ +/* -------------------------------------------------------------------- */ + else if( poDS->PCB != 0 ) + { + int nBufSize = 128*128*2; + int nBytesRead, iPixel, iSrc, bHalfByteUsed = FALSE; + GByte *pabyCData = (GByte *) CPLCalloc(nBufSize,1); + + nBytesRead = VSIFReadL(pabyCData, 1, nBufSize, poDS->fdIMG); + if( nBytesRead == 0 ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Cannot read data at offset %d", offset); + return CE_Failure; + } + + CPLAssert( poDS->PVB == 8 ); + CPLAssert( poDS->PCB == 4 || poDS->PCB == 8 ); + + for( iSrc = 0, iPixel = 0; iPixel < 128 * 128; ) + { + if( iSrc + 2 > nBytesRead ) + { + CPLFree( pabyCData ); + CPLError(CE_Failure, CPLE_AppDefined, + "Out of data decoding image block, only %d available.", + iSrc ); + return CE_Failure; + } + + int nCount = 0; + int nValue = 0; + + if( poDS->PCB == 8 ) + { + nCount = pabyCData[iSrc++]; + nValue = pabyCData[iSrc++]; + } + else if( poDS->PCB == 4 ) + { + if( (iPixel % 128) == 0 && bHalfByteUsed ) + { + iSrc++; + bHalfByteUsed = FALSE; + } + + if( bHalfByteUsed ) + { + nCount = pabyCData[iSrc++] & 0xf; + nValue = pabyCData[iSrc++]; + bHalfByteUsed = FALSE; + } + else + { + nCount = pabyCData[iSrc] >> 4; + nValue = ((pabyCData[iSrc] & 0xf) << 4) + + (pabyCData[iSrc+1] >> 4); + bHalfByteUsed = TRUE; + iSrc++; + } + } + + if( iPixel + nCount > 128 * 128 ) + { + CPLFree( pabyCData ); + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data decoding image block, likely corrupt." ); + return CE_Failure; + } + + while( nCount > 0 ) + { + ((GByte *) pImage)[iPixel++] = (GByte) nValue; + nCount--; + } + } + + CPLFree( pabyCData ); + } + + return CE_None; +} + +/************************************************************************/ +/* SRPDataset() */ +/************************************************************************/ + +SRPDataset::SRPDataset() +{ + fdIMG = NULL; + TILEINDEX = NULL; + offsetInIMG = 0; + papszSubDatasets = NULL; +} + +/************************************************************************/ +/* ~SRPDataset() */ +/************************************************************************/ + +SRPDataset::~SRPDataset() +{ + + CSLDestroy(papszSubDatasets); + + if (fdIMG) + { + VSIFCloseL(fdIMG); + } + + if (TILEINDEX) + { + delete [] TILEINDEX; + } +} + +/************************************************************************/ +/* ResetTo01() */ +/* Replace the DD in ZZZZZZDD.XXX with 01. */ +/************************************************************************/ + +CPLString SRPDataset::ResetTo01( const char* str ) +{ + CPLString osResult = str; + + osResult[6] = '0'; + osResult[7] = '1'; + + return osResult; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* SRPDataset::GetProjectionRef() +{ + return osSRS; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr SRPDataset::GetGeoTransform( double * padfGeoTransform) +{ + if( EQUAL(osProduct,"ASRP") ) + { + if( ZNA == 9 || ZNA == 18 ) + { + padfGeoTransform[0] = -1152000.0; + padfGeoTransform[1] = 500.0; + padfGeoTransform[2] = 0.0; + padfGeoTransform[3] = 1152000.0; + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = -500.0; + + } + else + { + padfGeoTransform[0] = LSO/3600.0; + padfGeoTransform[1] = 360. / ARV; + padfGeoTransform[2] = 0.0; + padfGeoTransform[3] = PSO/3600.0; + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = - 360. / BRV; + } + + return CE_None; + } + else if( EQUAL(osProduct,"USRP") ) + { + padfGeoTransform[0] = LSO; + padfGeoTransform[1] = LOD; + padfGeoTransform[2] = 0.0; + padfGeoTransform[3] = PSO; + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = -LAD; + return CE_None; + } + + return CE_Failure; +} + +/************************************************************************/ +/* GetFromRecord() */ +/************************************************************************/ + +int SRPDataset::GetFromRecord(const char* pszFileName, DDFRecord * record) +{ + CPLString osBAD; + int i; + + DDFField* field; + DDFFieldDefn *fieldDefn; + int bSuccess; + int nSTR; + +/* -------------------------------------------------------------------- */ +/* Read a variety of header fields of interest from the .GEN */ +/* file. */ +/* -------------------------------------------------------------------- */ + nSTR = record->GetIntSubfield( "GEN", 0, "STR", 0, &bSuccess ); + if( !bSuccess || nSTR != 4 ) + { + CPLDebug( "SRP", "Failed to extract STR, or not 4." ); + return FALSE; + } + + int SCA = record->GetIntSubfield( "GEN", 0, "SCA", 0, &bSuccess ); + CPLDebug("SRP", "SCA=%d", SCA); + + ZNA = record->GetIntSubfield( "GEN", 0, "ZNA", 0, &bSuccess ); + CPLDebug("SRP", "ZNA=%d", ZNA); + + double PSP = record->GetFloatSubfield( "GEN", 0, "PSP", 0, &bSuccess ); + CPLDebug("SRP", "PSP=%f", PSP); + + ARV = record->GetIntSubfield( "GEN", 0, "ARV", 0, &bSuccess ); + CPLDebug("SRP", "ARV=%d", ARV); + + BRV = record->GetIntSubfield( "GEN", 0, "BRV", 0, &bSuccess ); + CPLDebug("SRP", "BRV=%d", BRV); + + LSO = record->GetFloatSubfield( "GEN", 0, "LSO", 0, &bSuccess ); + CPLDebug("SRP", "LSO=%f", LSO); + + PSO = record->GetFloatSubfield( "GEN", 0, "PSO", 0, &bSuccess ); + CPLDebug("SRP", "PSO=%f", PSO); + + LAD = record->GetFloatSubfield( "GEN", 0, "LAD", 0 ); + LOD = record->GetFloatSubfield( "GEN", 0, "LOD", 0 ); + + NFL = record->GetIntSubfield( "SPR", 0, "NFL", 0, &bSuccess ); + CPLDebug("SRP", "NFL=%d", NFL); + + NFC = record->GetIntSubfield( "SPR", 0, "NFC", 0, &bSuccess ); + CPLDebug("SRP", "NFC=%d", NFC); + + int PNC = record->GetIntSubfield( "SPR", 0, "PNC", 0, &bSuccess ); + CPLDebug("SRP", "PNC=%d", PNC); + + int PNL = record->GetIntSubfield( "SPR", 0, "PNL", 0, &bSuccess ); + CPLDebug("SRP", "PNL=%d", PNL); + + if( PNL != 128 || PNC != 128 ) + { + CPLError( CE_Failure, CPLE_AppDefined,"Unsupported PNL or PNC value."); + return FALSE; + } + + PCB = record->GetIntSubfield( "SPR", 0, "PCB", 0 ); + PVB = record->GetIntSubfield( "SPR", 0, "PVB", 0 ); + if( (PCB != 8 && PCB != 4 && PCB != 0) || PVB != 8 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "PCB(%d) or PVB(%d) value unsupported.", PCB, PVB ); + return FALSE; + } + + const char* pszBAD = record->GetStringSubfield( "SPR", 0, "BAD", 0, &bSuccess ); + if( pszBAD == NULL ) + return FALSE; + osBAD = pszBAD; + { + char* c = (char*) strchr(osBAD, ' '); + if (c) + *c = 0; + } + CPLDebug("SRP", "BAD=%s", osBAD.c_str()); + +/* -------------------------------------------------------------------- */ +/* Read the tile map if available. */ +/* -------------------------------------------------------------------- */ + const char* pszTIF = record->GetStringSubfield( "SPR", 0, "TIF", 0 ); + int TIF = pszTIF != NULL && EQUAL(pszTIF,"Y"); + CPLDebug("SRP", "TIF=%d", TIF); + + if (TIF) + { + field = record->FindField( "TIM" ); + if( field == NULL ) + return FALSE; + + fieldDefn = field->GetFieldDefn(); + DDFSubfieldDefn *subfieldDefn = fieldDefn->FindSubfieldDefn( "TSI" ); + if( subfieldDefn == NULL ) + return FALSE; + + int nIndexValueWidth = subfieldDefn->GetWidth(); + + /* Should be strict comparison, but apparently a few datasets */ + /* have GetDataSize() greater than the required minimum (#3862) */ + if (field->GetDataSize() < nIndexValueWidth * NFL * NFC + 1) + { + return FALSE; + } + + TILEINDEX = new int [NFL * NFC]; + const char* ptr = field->GetData(); + char offset[30]={0}; + offset[nIndexValueWidth] = '\0'; + + for(i=0;iFindField( "COL" ) != NULL ) + { + int iColor; + int nColorCount = + record->FindField("COL")->GetRepeatCount(); + + for( iColor = 0; iColor < nColorCount; iColor++ ) + { + int bSuccess; + int nCCD, nNSR, nNSG, nNSB; + GDALColorEntry sEntry; + + nCCD = record->GetIntSubfield( "COL", 0, "CCD", iColor, + &bSuccess ); + if( !bSuccess ) + break; + + nNSR = record->GetIntSubfield( "COL", 0, "NSR", iColor ); + nNSG = record->GetIntSubfield( "COL", 0, "NSG", iColor ); + nNSB = record->GetIntSubfield( "COL", 0, "NSB", iColor ); + + sEntry.c1 = (short) nNSR; + sEntry.c2 = (short) nNSG; + sEntry.c3 = (short) nNSB; + sEntry.c4 = 255; + + oCT.SetColorEntry( nCCD, &sEntry ); + } + } + + if (record->FindField( "QUV" ) != NULL ) + { + //Date de production du produit : QAL.QUV.DAT1 + //Num�ro d'�dition du produit : QAL.QUV.EDN + + int EDN = record->GetIntSubfield( "QUV", 0, "EDN", 0, &bSuccess ); + if (bSuccess) + { + CPLDebug("SRP", "EDN=%d", EDN); + char pszValue[5]; + sprintf(pszValue, "%d", EDN); + SetMetadataItem( "SRP_EDN", pszValue ); + } + + + const char* pszCDV07 = record->GetStringSubfield( "QUV", 0, "CDV07", 0 ); + if (pszCDV07!=NULL) + SetMetadataItem( "SRP_CREATIONDATE", pszCDV07 ); + else + { /*USRP1.2*/ + const char* pszDAT = record->GetStringSubfield("QUV", 0, "DAT1", 0); + if( pszDAT != NULL ) + { + char dat[9]; + strncpy(dat,pszDAT+4,8); + dat[8]='\0'; + CPLDebug("SRP", "Record DAT %s",dat); + SetMetadataItem( "SRP_CREATIONDATE", dat ); + } + } + + const char* pszCDV24 = record->GetStringSubfield( "QUV", 0, "CDV24", 0 ); + if (pszCDV24!=NULL) + SetMetadataItem( "SRP_REVISIONDATE", pszCDV24 ); + else + { /*USRP1.2*/ + const char* pszDAT = record->GetStringSubfield("QUV", 0, "DAT2", 0); + if( pszDAT != NULL ) + { + char dat[9]; + strncpy(dat,pszDAT+4,8); + dat[8]='\0'; + CPLDebug("SRP", "Record DAT %s",dat); + SetMetadataItem( "SRP_REVISIONDATE", dat ); + } + } + + const char* pszQSS = record->GetStringSubfield( "QSR", 0, "QSS", 0 ); + if (pszQSS!=NULL) + SetMetadataItem( "SRP_CLASSIFICATION", pszQSS ); + } + } + } + else + { + osQALFileName = ""; + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to find .QAL file, no color table applied." ); + } + +/* -------------------------------------------------------------------- */ +/* Derive the coordinate system. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(osProduct,"ASRP") ) + { + osSRS = SRS_WKT_WGS84; + + if( ZNA == 9 ) + { + osSRS = "PROJCS[\"unnamed\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"Azimuthal_Equidistant\"],PARAMETER[\"latitude_of_center\",90],PARAMETER[\"longitude_of_center\",0],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]]"; + } + + if (ZNA == 18) + { + osSRS = "PROJCS[\"unnamed\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"Azimuthal_Equidistant\"],PARAMETER[\"latitude_of_center\",-90],PARAMETER[\"longitude_of_center\",0],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]]"; + } + } + else + { + OGRSpatialReference oSRS; + + if( ABS(ZNA) >= 1 && ABS(ZNA) <= 60 ) + { + oSRS.SetUTM( ABS(ZNA), ZNA > 0 ); + oSRS.SetWellKnownGeogCS( "WGS84" ); + } + else if( ZNA == 61 ) + { + oSRS.importFromEPSG( 32661 ); // WGS84 UPS North + } + else if( ZNA == -61 ) + { + oSRS.importFromEPSG( 32761 ); // WGS84 UPS South + } + + char *pszWKT = NULL; + oSRS.exportToWkt( &pszWKT ); + osSRS = pszWKT; + CPLFree( pszWKT ); + } + + sprintf(pszValue, "%d", ZNA); + SetMetadataItem( "SRP_ZNA", pszValue ); + + return TRUE; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **SRPDataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + if (osGENFileName.size() > 0 && osIMGFileName.size() > 0) + { + CPLString osMainFilename = GetDescription(); + int bMainFileReal; + VSIStatBufL sStat; + + bMainFileReal = VSIStatL( osMainFilename, &sStat ) == 0; + if (bMainFileReal) + { + CPLString osShortMainFilename = CPLGetFilename(osMainFilename); + CPLString osShortGENFileName = CPLGetFilename(osGENFileName); + if ( !EQUAL(osShortMainFilename.c_str(), osShortGENFileName.c_str()) ) + papszFileList = CSLAddString(papszFileList, osGENFileName.c_str()); + } + else + papszFileList = CSLAddString(papszFileList, osGENFileName.c_str()); + + papszFileList = CSLAddString(papszFileList, osIMGFileName.c_str()); + + + if( strlen(osQALFileName) > 0 ) + papszFileList = CSLAddString( papszFileList, osQALFileName ); + } + return papszFileList; +} + +/************************************************************************/ +/* AddSubDataset() */ +/************************************************************************/ + +void SRPDataset::AddSubDataset( const char* pszGENFileName, const char* pszIMGFileName ) +{ + char szName[80]; + int nCount = CSLCount(papszSubDatasets ) / 2; + + CPLString osSubDatasetName; + osSubDatasetName = "SRP:"; + osSubDatasetName += pszGENFileName; + osSubDatasetName += ","; + osSubDatasetName += pszIMGFileName; + + sprintf( szName, "SUBDATASET_%d_NAME", nCount+1 ); + papszSubDatasets = + CSLSetNameValue( papszSubDatasets, szName, osSubDatasetName); + + sprintf( szName, "SUBDATASET_%d_DESC", nCount+1 ); + papszSubDatasets = + CSLSetNameValue( papszSubDatasets, szName, osSubDatasetName); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **SRPDataset::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain != NULL && EQUAL(pszDomain,"SUBDATASETS") ) + return papszSubDatasets; + + return GDALPamDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* FindRecordInGENForIMG() */ +/************************************************************************/ + +DDFRecord* SRPDataset::FindRecordInGENForIMG(DDFModule& module, + const char* pszGENFileName, + const char* pszIMGFileName) +{ + /* Finds the GEN file corresponding to the IMG file */ + if (!module.Open(pszGENFileName, TRUE)) + return NULL; + + + CPLString osShortIMGFilename = CPLGetFilename(pszIMGFileName); + + DDFField* field; + DDFFieldDefn *fieldDefn; + + /* Now finds the record */ + while (TRUE) + { + CPLPushErrorHandler( CPLQuietErrorHandler ); + DDFRecord* record = module.ReadRecord(); + CPLPopErrorHandler(); + CPLErrorReset(); + if (record == NULL) + return NULL; + + if (record->GetFieldCount() >= 5) + { + field = record->GetField(0); + fieldDefn = field->GetFieldDefn(); + if (!(strcmp(fieldDefn->GetName(), "001") == 0 && + fieldDefn->GetSubfieldCount() == 2)) + { + continue; + } + + const char* RTY = record->GetStringSubfield("001", 0, "RTY", 0); + if( RTY == NULL ) + continue; + /* Ignore overviews */ + if ( strcmp(RTY, "OVV") == 0 ) + continue; + + if ( strcmp(RTY, "GIN") != 0 ) + continue; + + field = record->GetField(3); + fieldDefn = field->GetFieldDefn(); + + if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 && + fieldDefn->GetSubfieldCount() == 15)) + { + continue; + } + + const char* pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0); + if( pszBAD == NULL || strlen(pszBAD) != 12 ) + continue; + CPLString osBAD = pszBAD; + { + char* c = (char*) strchr(osBAD.c_str(), ' '); + if (c) + *c = 0; + } + + if (EQUAL(osShortIMGFilename.c_str(), osBAD.c_str())) + { + return record; + } + } + } +} +/************************************************************************/ +/* OpenDataset() */ +/************************************************************************/ + +SRPDataset* SRPDataset::OpenDataset( + const char* pszGENFileName, const char* pszIMGFileName, DDFRecord* record) +{ + DDFModule module; + DDFField* field; + DDFFieldDefn *fieldDefn; + + if (record == NULL) + { + record = FindRecordInGENForIMG(module, pszGENFileName, pszIMGFileName); + if (record == NULL) + return NULL; + } + + field = record->GetField(1); + if( field == NULL ) + return NULL; + fieldDefn = field->GetFieldDefn(); + + if (!(strcmp(fieldDefn->GetName(), "DSI") == 0 && + fieldDefn->GetSubfieldCount() == 2)) + { + return NULL; + } + + const char* pszPRT = record->GetStringSubfield("DSI", 0, "PRT", 0); + if( pszPRT == NULL) + return NULL; + + CPLString osPRT = pszPRT; + osPRT.resize(4); + CPLDebug("SRP", "osPRT=%s", osPRT.c_str()); + if( !EQUAL(osPRT,"ASRP") && !EQUAL(osPRT,"USRP") ) + return NULL; + + const char* pszNAM = record->GetStringSubfield("DSI", 0, "NAM", 0); + if( pszNAM == NULL ) + return NULL; + + + CPLString osNAM = pszNAM; + CPLDebug("SRP", "osNAM=%s", osNAM.c_str()); + if ( strlen(pszNAM) != 8 ) + { + CPLDebug("SRP", "Name Size=%d", (int)strlen(pszNAM) ); + } + + SRPDataset* poDS = new SRPDataset(); + + poDS->osProduct = osPRT; + poDS->osGENFileName = pszGENFileName; + poDS->osIMGFileName = pszIMGFileName; + + + + poDS->SetMetadataItem( "SRP_NAM", osNAM ); + poDS->SetMetadataItem( "SRP_PRODUCT", osPRT ); + + + if (!poDS->GetFromRecord( pszGENFileName, record ) ) + { + delete poDS; + return NULL; + } + + return poDS; + +} + + + +/************************************************************************/ +/* GetGENListFromTHF() */ +/************************************************************************/ + +char** SRPDataset::GetGENListFromTHF(const char* pszFileName) +{ + DDFModule module; + DDFRecord * record; + DDFField* field; + DDFFieldDefn *fieldDefn; + int i; + int nFilenames = 0; + + char** papszFileNames = NULL; + if (!module.Open(pszFileName, TRUE)) + return papszFileNames; + + CPLString osDirName(CPLGetDirname(pszFileName)); + + while (TRUE) + { + CPLPushErrorHandler( CPLQuietErrorHandler ); + record = module.ReadRecord(); + CPLPopErrorHandler(); + CPLErrorReset(); + if (record == NULL) + break; + if (record->GetFieldCount() > 2) + { + field = record->GetField(0); + fieldDefn = field->GetFieldDefn(); + if (!(strcmp(fieldDefn->GetName(), "001") == 0 && + fieldDefn->GetSubfieldCount() == 2)) + { + continue; + } + + const char* RTY = record->GetStringSubfield("001", 0, "RTY", 0); + if ( RTY == NULL ) + { + continue; + } + + if ( strcmp(RTY, "THF") == 0 ) + { + field = record->GetField(1); + fieldDefn = field->GetFieldDefn(); + if (!(strcmp(fieldDefn->GetName(), "VDR") == 0 && + fieldDefn->GetSubfieldCount() == 8)) + { + continue; + } + + int iFDRFieldInstance = 0; + for (i = 2; i < record->GetFieldCount() ; i++) + { + field = record->GetField(i); + fieldDefn = field->GetFieldDefn(); + + if (!(strcmp(fieldDefn->GetName(), "FDR") == 0 && + fieldDefn->GetSubfieldCount() == 7)) + { + CPLDebug("SRP", "Record FDR %d",fieldDefn->GetSubfieldCount()); + continue; + } + + const char* pszNAM = record->GetStringSubfield("FDR", iFDRFieldInstance++, "NAM", 0); + if( pszNAM == NULL) + continue; + + + CPLString osName = CPLString(pszNAM); + + /* Define a subdirectory from Dataset but with only 6 caracatere */ + CPLString osDirDataset = pszNAM; + osDirDataset.resize(6); + CPLString osDatasetDir = CPLFormFilename(osDirName.c_str(), osDirDataset.c_str(), NULL); + + CPLString osGENFileName=""; + + + int bFound=0; + if (bFound ==0) + { + char** papszDirContent = VSIReadDir(osDatasetDir.c_str()); + char** ptrDir = papszDirContent; + if (ptrDir) + { + while(*ptrDir) + { + if ( EQUAL(CPLGetExtension(*ptrDir), "GEN") ) + { + bFound = 1; + osGENFileName = CPLFormFilename(osDatasetDir.c_str(), *ptrDir, NULL); + CPLDebug("SRP", "Building GEN full file name : %s", osGENFileName.c_str()); + break; + } + ptrDir ++; + } + CSLDestroy(papszDirContent); + } + + } + /* If not found in sub directory then search in the same directory of the THF file */ + if (bFound ==0) + { + char** papszDirContent = VSIReadDir(osDirName.c_str()); + char** ptrDir = papszDirContent; + if (ptrDir) + { + while(*ptrDir) + { + if ( EQUAL(CPLGetExtension(*ptrDir), "GEN") && EQUALN(CPLGetBasename(*ptrDir), osName,6)) + { + bFound = 1; + osGENFileName = CPLFormFilename(osDirName.c_str(), *ptrDir, NULL); + CPLDebug("SRP", "Building GEN full file name : %s", osGENFileName.c_str()); + break; + } + ptrDir ++; + } + CSLDestroy(papszDirContent); + } + + } + + + if (bFound ==1) + { + papszFileNames = (char**)CPLRealloc(papszFileNames, sizeof(char*) * (nFilenames + 2)); + papszFileNames[nFilenames] = CPLStrdup(osGENFileName.c_str()); + papszFileNames[nFilenames + 1] = NULL; + nFilenames ++; + } + + } + } + + } + } + return papszFileNames; +} + + +/************************************************************************/ +/* AddMetadatafromFromTHF() */ +/************************************************************************/ + +void SRPDataset::AddMetadatafromFromTHF(const char* pszFileName) +{ + DDFModule module; + DDFRecord * record; + DDFField* field; + DDFFieldDefn *fieldDefn; + + int bSuccess=0; + if (!module.Open(pszFileName, TRUE)) + return ; + + CPLString osDirName(CPLGetDirname(pszFileName)); + + while (TRUE) + { + CPLPushErrorHandler( CPLQuietErrorHandler ); + record = module.ReadRecord(); + CPLPopErrorHandler(); + CPLErrorReset(); + if (record == NULL || record->GetFieldCount() <= 2) + break; + + field = record->GetField(0); + fieldDefn = field->GetFieldDefn(); + if (!(strcmp(fieldDefn->GetName(), "001") == 0) || + fieldDefn->GetSubfieldCount() != 2) + break; + + const char* RTY = record->GetStringSubfield("001", 0, "RTY", 0); + if ( RTY != NULL && strcmp(RTY, "THF") == 0 ) + { + field = record->GetField(1); + fieldDefn = field->GetFieldDefn(); + if ((strcmp(fieldDefn->GetName(), "VDR") == 0 && + fieldDefn->GetSubfieldCount() == 8)) + { + + const char* pszVOO = record->GetStringSubfield("VDR", 0, "VOO", 0); + if( pszVOO != NULL ) + { + CPLDebug("SRP", "Record VOO %s",pszVOO); + SetMetadataItem( "SRP_VOO", pszVOO ); + } + + + int EDN = record->GetIntSubfield( "VDR", 0, "EDN", 0, &bSuccess ); + if (bSuccess) + { + CPLDebug("SRP", "Record EDN %d",EDN); + char pszValue[5]; + sprintf(pszValue, "%d", EDN); + SetMetadataItem( "SRP_EDN", pszValue ); + } + + + const char* pszCDV07 = record->GetStringSubfield("VDR", 0, "CDV07", 0); + if( pszCDV07 != NULL ) + { + CPLDebug("SRP", "Record pszCDV07 %s",pszCDV07); + SetMetadataItem( "SRP_CREATIONDATE", pszCDV07 ); + } + else + { /*USRP1.2*/ + const char* pszDAT = record->GetStringSubfield("VDR", 0, "DAT", 0); + if( pszDAT != NULL ) + { + char dat[9]; + strncpy(dat,pszDAT+4,8); + dat[8]='\0'; + CPLDebug("SRP", "Record DAT %s",dat); + SetMetadataItem( "SRP_CREATIONDATE", dat ); + } + } + + } + } /* End of THF part */ + + if ( RTY != NULL && strcmp(RTY, "LCF") == 0 ) + { + field = record->GetField(1); + fieldDefn = field->GetFieldDefn(); + if ((strcmp(fieldDefn->GetName(), "QSR") == 0 && + fieldDefn->GetSubfieldCount() == 4)) + { + + const char* pszQSS = record->GetStringSubfield("QSR", 0, "QSS", 0); + if( pszQSS != NULL ) + { + CPLDebug("SRP", "Record Classification %s",pszQSS); + SetMetadataItem( "SRP_CLASSIFICATION", pszQSS ); + } + } + + field = record->GetField(2); + fieldDefn = field->GetFieldDefn(); + if ((strcmp(fieldDefn->GetName(), "QUV") == 0 && + fieldDefn->GetSubfieldCount() == 6)) + { + const char* pszSRC2 = record->GetStringSubfield("QUV", 0, "SRC1", 0); + if( pszSRC2 != NULL ) + { + SetMetadataItem( "SRP_PRODUCTVERSION", pszSRC2 ); + } + else + { + const char* pszSRC = record->GetStringSubfield("QUV", 0, "SRC", 0); + if( pszSRC != NULL ) + { + SetMetadataItem( "SRP_PRODUCTVERSION", pszSRC ); + } + } + } + } /* End of LCF part */ + + } +} + +/************************************************************************/ +/* GetIMGListFromGEN() */ +/************************************************************************/ + +char** SRPDataset::GetIMGListFromGEN(const char* pszFileName, + int *pnRecordIndex) +{ + DDFRecord * record; + DDFField* field; + DDFFieldDefn *fieldDefn; + int nFilenames = 0; + char** papszFileNames = NULL; + int nRecordIndex = -1; + + if (pnRecordIndex) + *pnRecordIndex = -1; + + DDFModule module; + if (!module.Open(pszFileName, TRUE)) + return NULL; + + while (TRUE) + { + nRecordIndex ++; + + CPLPushErrorHandler( CPLQuietErrorHandler ); + record = module.ReadRecord(); + CPLPopErrorHandler(); + CPLErrorReset(); + if (record == NULL) + break; + + if (record->GetFieldCount() >= 5) + { + field = record->GetField(0); + fieldDefn = field->GetFieldDefn(); + if (!(strcmp(fieldDefn->GetName(), "001") == 0 && + fieldDefn->GetSubfieldCount() == 2)) + { + continue; + } + + const char* RTY = record->GetStringSubfield("001", 0, "RTY", 0); + if( RTY == NULL ) + continue; + /* Ignore overviews */ + if ( strcmp(RTY, "OVV") == 0 ) + continue; + + if ( strcmp(RTY, "GIN") != 0 ) + continue; + + field = record->GetField(3); + if( field == NULL ) + continue; + fieldDefn = field->GetFieldDefn(); + + if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 && + fieldDefn->GetSubfieldCount() == 15)) + { + continue; + } + + const char* pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0); + if( pszBAD == NULL || strlen(pszBAD) != 12 ) + continue; + CPLString osBAD = pszBAD; + { + char* c = (char*) strchr(osBAD.c_str(), ' '); + if (c) + *c = 0; + } + CPLDebug("SRP", "BAD=%s", osBAD.c_str()); + + /* Build full IMG file name from BAD value */ + CPLString osGENDir(CPLGetDirname(pszFileName)); + + CPLString osFileName = CPLFormFilename(osGENDir.c_str(), osBAD.c_str(), NULL); + VSIStatBufL sStatBuf; + if( VSIStatL( osFileName, &sStatBuf ) == 0 ) + { + osBAD = osFileName; + CPLDebug("SRP", "Building IMG full file name : %s", osBAD.c_str()); + } + else + { + char** papszDirContent; + if (strcmp(osGENDir.c_str(), "/vsimem") == 0) + { + CPLString osTmp = osGENDir + "/"; + papszDirContent = VSIReadDir(osTmp); + } + else + papszDirContent = VSIReadDir(osGENDir); + char** ptrDir = papszDirContent; + while(ptrDir && *ptrDir) + { + if (EQUAL(*ptrDir, osBAD.c_str())) + { + osBAD = CPLFormFilename(osGENDir.c_str(), *ptrDir, NULL); + CPLDebug("SRP", "Building IMG full file name : %s", osBAD.c_str()); + break; + } + ptrDir ++; + } + CSLDestroy(papszDirContent); + } + + if (nFilenames == 0 && pnRecordIndex) + *pnRecordIndex = nRecordIndex; + + papszFileNames = (char**)CPLRealloc(papszFileNames, sizeof(char*) * (nFilenames + 2)); + papszFileNames[nFilenames] = CPLStrdup(osBAD.c_str()); + papszFileNames[nFilenames + 1] = NULL; + nFilenames ++; + } + } + + return papszFileNames; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *SRPDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + int nRecordIndex = -1; + CPLString osGENFileName; + CPLString osIMGFileName; + int bFromSubdataset = FALSE; + int bTHFWithSingleGEN = FALSE; + + if( EQUALN(poOpenInfo->pszFilename, "SRP:", 4) ) + { + char** papszTokens = CSLTokenizeString2(poOpenInfo->pszFilename + 4, ",", 0); + if (CSLCount(papszTokens) == 2) + { + osGENFileName = papszTokens[0]; + osIMGFileName = papszTokens[1]; + bFromSubdataset = TRUE; + } + CSLDestroy(papszTokens); + } + else + { + if( poOpenInfo->nHeaderBytes < 500 ) + return NULL; + CPLString osFileName(poOpenInfo->pszFilename); + + if (EQUAL(CPLGetExtension(osFileName.c_str()), "THF")) + { + + CPLDebug("SRP", "Read THF"); + + char** papszFileNames = GetGENListFromTHF(osFileName.c_str()); + if (papszFileNames == NULL) + return NULL; + if (papszFileNames[1] == NULL && + CSLTestBoolean(CPLGetConfigOption("SRP_SINGLE_GEN_IN_THF_AS_DATASET", "TRUE"))) + { + osFileName = papszFileNames[0]; + CSLDestroy(papszFileNames); + bTHFWithSingleGEN = TRUE; + } + else + { + char** ptr = papszFileNames; + SRPDataset* poDS = new SRPDataset(); + poDS->AddMetadatafromFromTHF(osFileName.c_str()); + while(*ptr) + { + char** papszIMGFileNames = GetIMGListFromGEN(*ptr); + char** papszIMGIter = papszIMGFileNames; + while(papszIMGIter && *papszIMGIter) + { + poDS->AddSubDataset(*ptr, *papszIMGIter); + papszIMGIter ++; + } + CSLDestroy(papszIMGFileNames); + + ptr ++; + } + CSLDestroy(papszFileNames); + return poDS; + } + } + + if ( bTHFWithSingleGEN +#ifdef OPEN_GEN + || EQUAL(CPLGetExtension(osFileName.c_str()), "GEN") +#endif + ) + { + osGENFileName = osFileName; + + char** papszFileNames = GetIMGListFromGEN(osFileName.c_str(), &nRecordIndex); + if (papszFileNames == NULL) + return NULL; + if (papszFileNames[1] == NULL) + { + osIMGFileName = papszFileNames[0]; + CSLDestroy(papszFileNames); + } + else + { + char** ptr = papszFileNames; + SRPDataset* poDS = new SRPDataset(); + while(*ptr) + { + poDS->AddSubDataset(osFileName.c_str(), *ptr); + ptr ++; + } + CSLDestroy(papszFileNames); + return poDS; + } + } + + if (EQUAL(CPLGetExtension(osFileName.c_str()), "IMG")) + { + + osIMGFileName = osFileName; + + static const size_t nLeaderSize = 24; + int i; + + for( i = 0; i < (int)nLeaderSize; i++ ) + { + if( poOpenInfo->pabyHeader[i] < 32 + || poOpenInfo->pabyHeader[i] > 126 ) + return NULL; + } + + if( poOpenInfo->pabyHeader[5] != '1' + && poOpenInfo->pabyHeader[5] != '2' + && poOpenInfo->pabyHeader[5] != '3' ) + return NULL; + + if( poOpenInfo->pabyHeader[6] != 'L' ) + return NULL; + if( poOpenInfo->pabyHeader[8] != '1' && poOpenInfo->pabyHeader[8] != ' ' ) + return NULL; + + // -------------------------------------------------------------------- + // Find and open the .GEN file. + // -------------------------------------------------------------------- + VSIStatBufL sStatBuf; + + CPLString basename = CPLGetBasename( osFileName ); + if( basename.size() != 8 ) + { + CPLDebug("SRP", "Invalid basename file"); + return NULL; + } + + nRecordIndex = CPLScanLong( basename + 6, 2 ); + + CPLString path = CPLGetDirname( osFileName ); + CPLString basename01 = ResetTo01( basename ); + osFileName = CPLFormFilename( path, basename01, ".IMG" ); + + osFileName = CPLResetExtension( osFileName, "GEN" ); + if( VSIStatL( osFileName, &sStatBuf ) != 0 ) + { + osFileName = CPLResetExtension( osFileName, "gen" ); + if( VSIStatL( osFileName, &sStatBuf ) != 0 ) + return NULL; + } + + osGENFileName = osFileName; + } + } + + if (osGENFileName.size() > 0 && + osIMGFileName.size() > 0) + { + + + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The SRP driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + DDFModule module; + DDFRecord* record = NULL; + if (nRecordIndex >= 0 && + module.Open(osGENFileName.c_str(), TRUE)) + { + int i; + for(i=0;iSetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + + /* ---------------------------------------------------------- */ + /* Check for external overviews. */ + /* ---------------------------------------------------------- */ + if( bFromSubdataset ) + poDS->oOvManager.Initialize( poDS, osIMGFileName.c_str() ); + else + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return poDS; + } + } + + return NULL; +} + +/************************************************************************/ +/* GDALRegister_SRP() */ +/************************************************************************/ + +void GDALRegister_SRP() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "SRP" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SRP" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Standard Raster Product (ASRP/USRP)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#SRP" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "img" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = SRPDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/aigrid/GNUmakefile b/bazaar/plugin/gdal/frmts/aigrid/GNUmakefile new file mode 100644 index 000000000..4d48b5b8c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aigrid/GNUmakefile @@ -0,0 +1,31 @@ + +include ../../GDALmake.opt + +AIGOBJ = gridlib.o aigopen.o aigccitt.o +OBJ = aigdataset.o $(AIGOBJ) + +CPPFLAGS := $(CPPFLAGS) -I../../ogr/ogrsf_frmts/avc + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o aitest$(EXE) $(OBJ) $(O_OBJ) + +aitest$(EXE): aitest.$(OBJ_EXT) + $(LD) $(LDFLAGS) aitest.$(OBJ_EXT) $(CONFIG_LIBS) -o aitest$(EXE) + +aigrid2tif$(EXE): aigrid2tif.$(OBJ_EXT) + $(LD) $(LDFLAGS) aigrid2tif.$(OBJ_EXT) $(CONFIG_LIBS) -o aigrid2tif$(EXE) + +$(OBJ) $(O_OBJ): aigrid.h + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +aitest-dist: + rm -rf aitest_dist + mkdir aitest_dist + cp gridlib.c aitest.c aigrid.h aigopen.c aigccitt.c aitest_dist + cp ../../port/cpl_{vsisimple,conv,error}.cpp aitest_dist + cp ../../port/cpl_{port,conv,vsi,config,error}.h aitest_dist + cp Makefile.dist aitest_dist/Makefile + diff --git a/bazaar/plugin/gdal/frmts/aigrid/Makefile.dist b/bazaar/plugin/gdal/frmts/aigrid/Makefile.dist new file mode 100644 index 000000000..437ccf377 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aigrid/Makefile.dist @@ -0,0 +1,19 @@ + + +OBJ = aigopen.o gridlib.o aigccitt.o \ + cpl_vsisimple.o cpl_conv.o cpl_error.o + +default: aitest + +aitest: $(OBJ) + $(CXX) aitest.c $(OBJ) -o aitest + +cpl_vsisimple.o: cpl_vsisimple.cpp + $(CXX) -c cpl_vsisimple.cpp + +cpl_conv.o: cpl_conv.cpp + $(CXX) -c cpl_conv.cpp + +cpl_error.o: cpl_error.cpp + $(CXX) -c cpl_error.cpp + diff --git a/bazaar/plugin/gdal/frmts/aigrid/aigccitt.c b/bazaar/plugin/gdal/frmts/aigrid/aigccitt.c new file mode 100644 index 000000000..431bf455a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aigrid/aigccitt.c @@ -0,0 +1,1922 @@ +/****************************************************************************** + * $Id: aigccitt.c 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: Arc/Info Binary Grid Translator + * Purpose: Code for decoding CCITT RLE (G1) compressed data. + * Author: Frank Warmerdam, warmerdam@pobox.com + * Code derived from libtiff (tif_fax3), which originally was + * derived from code by Frank Cringle in viewfax. + * + ****************************************************************************** + * Copyright (c) 2002 Frank Warmerdam + * Copyright (c) 1990-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * Copyright (c) 2009-2013, Even Rouault + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + ****************************************************************************/ + +#include +#include "aigrid.h" + +/* ==================================================================== */ +/* Various declarations for the decompression state machine */ +/* cribbed from tif_fax3.h in libtiff. */ +/* ==================================================================== */ + +#define FAXMODE_CLASSIC 0x0000 /* default, include RTC */ +#define FAXMODE_NORTC 0x0001 /* no RTC at end of data */ +#define FAXMODE_NOEOL 0x0002 /* no EOL code at end of row */ +#define FAXMODE_BYTEALIGN 0x0004 /* byte align row */ +#define FAXMODE_WORDALIGN 0x0008 /* word align row */ +#define FAXMODE_CLASSF FAXMODE_NORTC /* TIFF Class F */ + +/* + * To override the default routine used to image decoded + * spans one can use the pseduo tag TIFFTAG_FAXFILLFUNC. + * The routine must have the type signature given below; + * for example: + * + * fillruns(unsigned char* buf, uint32* runs, uint32* erun, uint32 lastx) + * + * where buf is place to set the bits, runs is the array of b&w run + * lengths (white then black), erun is the last run in the array, and + * lastx is the width of the row in pixels. Fill routines can assume + * the run array has room for at least lastx runs and can overwrite + * data in the run array as needed (e.g. to append zero runs to bring + * the count up to a nice multiple). + */ +typedef void (*TIFFFaxFillFunc)(unsigned char*, GUInt32*, GUInt32*, GUInt32); + +/* finite state machine codes */ + + +#define S_Null 0 +#define S_Pass 1 +#define S_Horiz 2 +#define S_V0 3 +#define S_VR 4 +#define S_VL 5 +#define S_Ext 6 +#define S_TermW 7 +#define S_TermB 8 +#define S_MakeUpW 9 +#define S_MakeUpB 10 +#define S_MakeUp 11 +#define S_EOL 12 + +typedef struct { /* state table entry */ + unsigned char State; /* see above */ + unsigned char Width; /* width of code in bits */ + GUInt32 Param; /* unsigned 32-bit run length in bits */ +} TIFFFaxTabEnt; + +#if 0 /* Unused */ +static const TIFFFaxTabEnt aig_TIFFFaxMainTable[128] = { +{12,7,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0}, +{2,3,0},{3,1,0},{4,3,1},{3,1,0},{5,6,2},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0}, +{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{5,7,3},{3,1,0},{5,3,1},{3,1,0}, +{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0}, +{4,6,2},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0}, +{2,3,0},{3,1,0},{4,3,1},{3,1,0},{6,7,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0}, +{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{5,6,2},{3,1,0},{5,3,1},{3,1,0}, +{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0}, +{4,7,3},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0}, +{2,3,0},{3,1,0},{4,3,1},{3,1,0},{4,6,2},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0}, +{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0} +}; +#endif /* Unused */ + +static const TIFFFaxTabEnt aig_TIFFFaxWhiteTable[4096] = { +{12,11,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6}, +{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5}, +{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5}, +{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15}, +{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1792},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6}, +{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5}, +{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14}, +{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6}, +{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15}, +{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7}, +{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6}, +{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{11,11,1856},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6}, +{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5}, +{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5}, +{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15}, +{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6}, +{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5}, +{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14}, +{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6}, +{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2112},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15}, +{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7}, +{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6}, +{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6}, +{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5}, +{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5}, +{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15}, +{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2368},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6}, +{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5}, +{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14}, +{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6}, +{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15}, +{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7}, +{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6}, +{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{11,12,1984},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6}, +{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5}, +{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5}, +{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15}, +{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6}, +{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5}, +{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14}, +{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6}, +{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1920},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15}, +{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7}, +{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6}, +{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6}, +{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5}, +{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5}, +{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15}, +{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2240},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6}, +{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5}, +{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14}, +{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6}, +{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15}, +{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7}, +{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6}, +{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{11,12,2496},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6}, +{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5}, +{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5}, +{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15}, +{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{12,11,0},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6}, +{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5}, +{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14}, +{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6}, +{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1792},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15}, +{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7}, +{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6}, +{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6}, +{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5}, +{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5}, +{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15}, +{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1856},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6}, +{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5}, +{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14}, +{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6}, +{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15}, +{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7}, +{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6}, +{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{11,12,2176},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6}, +{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5}, +{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5}, +{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15}, +{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6}, +{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5}, +{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14}, +{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6}, +{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2432},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15}, +{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7}, +{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6}, +{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6}, +{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5}, +{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5}, +{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15}, +{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2048},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6}, +{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5}, +{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14}, +{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6}, +{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15}, +{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7}, +{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6}, +{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{11,11,1920},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6}, +{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5}, +{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5}, +{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15}, +{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6}, +{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5}, +{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14}, +{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6}, +{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2304},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15}, +{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7}, +{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6}, +{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6}, +{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5}, +{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5}, +{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15}, +{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2560},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6}, +{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5}, +{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14}, +{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6}, +{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7} +}; +static const TIFFFaxTabEnt aig_TIFFFaxBlackTable[8192] = { +{12,11,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1792},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,23},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,20},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,25},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,128},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,56},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,30},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,11,1856},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,57},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,21},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,54},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,52},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,48},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2112},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,44},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,36},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,384},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,28},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,60},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,40},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2368},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,12,1984},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,50},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,34},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1664},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,26},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,13,1408},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,32},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1920},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,61},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,42},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1024},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,768},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,62},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2240},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,46},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,38},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,13,512},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,19},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,24},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,22},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,12,2496},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{12,11,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1792},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,23},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,20},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,25},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,12,192},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1280},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,31},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1856},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,58},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,21},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,13,896},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,640},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,49},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,12,2176},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,45},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,37},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,448},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,29},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,13,1536},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,41},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2432},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2048},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,51},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,35},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,12,320},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,27},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,59},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,33},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,11,1920},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,256},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,43},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1152},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,55},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,63},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2304},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,47},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,39},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,53},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,19},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,24},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,22},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2560},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{12,11,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,11,1792},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,23},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,20},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,25},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,128},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,56},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,30},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1856},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,57},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,21},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,54},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,52},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,48},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2112},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,44},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,36},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,12,384},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,28},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,60},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,40},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,12,2368},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,1984},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,50},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,34},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1728},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,26},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1472},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,32},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1920},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,61},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,42},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,13,1088},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,832},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,62},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,12,2240},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,46},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,38},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,576},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,19},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,24},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,22},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2496},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{12,11,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1792},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,23},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,20},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,25},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,192},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1344},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,31},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,11,1856},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,58},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,21},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,960},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,13,704},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,49},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2176},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,45},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,37},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,448},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,29},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1600},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,41},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2432},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,12,2048},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,51},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,35},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,320},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,27},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,59},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,33},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1920},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,12,256},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,43},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1216},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,55},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,63},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2304},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,47},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,39},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,53},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,19},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,24},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,22},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,12,2560},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2} +}; + +static const unsigned char aig_TIFFBitRevTable[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + +#if 0 /* Unused */ +static const unsigned char aig_TIFFNoBitRevTable[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +}; +#endif /* Unused */ + +/* + * The following macros define the majority of the G3/G4 decoder + * algorithm using the state tables defined elsewhere. To build + * a decoder you need some setup code and some glue code. Note + * that you may also need/want to change the way the NeedBits* + * macros get input data if, for example, you know the data to be + * decoded is properly aligned and oriented (doing so before running + * the decoder can be a big performance win). + * + * Consult the decoder in the TIFF library for an idea of what you + * need to define and setup to make use of these definitions. + * + * NB: to enable a debugging version of these macros define FAX3_DEBUG + * before including this file. Trace output goes to stdout. + */ + +#ifndef EndOfData +#define EndOfData() (cp >= ep) +#endif +/* + * Need <=8 or <=16 bits of input data. Unlike viewfax we + * cannot use/assume a word-aligned, properly bit swizzled + * input data set because data may come from an arbitrarily + * aligned, read-only source such as a memory-mapped file. + * Note also that the viewfax decoder does not check for + * running off the end of the input data buffer. This is + * possible for G3-encoded data because it prescans the input + * data to count EOL markers, but can cause problems for G4 + * data. In any event, we don't prescan and must watch for + * running out of data since we can't permit the library to + * scan past the end of the input data buffer. + * + * Finally, note that we must handle remaindered data at the end + * of a strip specially. The coder asks for a fixed number of + * bits when scanning for the next code. This may be more bits + * than are actually present in the data stream. If we appear + * to run out of data but still have some number of valid bits + * remaining then we makeup the requested amount with zeros and + * return successfully. If the returned data is incorrect then + * we should be called again and get a premature EOF error; + * otherwise we should get the right answer. + */ +#ifndef NeedBits8 +#define NeedBits8(n,eoflab) do { \ + if (BitsAvail < (n)) { \ + if (EndOfData()) { \ + if (BitsAvail == 0) /* no valid bits */ \ + goto eoflab; \ + BitsAvail = (n); /* pad with zeros */ \ + } else { \ + BitAcc |= ((GUInt32) bitmap[*cp++])<>= (n); \ +} while (0) + +#ifdef FAX3_DEBUG +static const char* StateNames[] = { + "Null ", + "Pass ", + "Horiz ", + "V0 ", + "VR ", + "VL ", + "Ext ", + "TermW ", + "TermB ", + "MakeUpW", + "MakeUpB", + "MakeUp ", + "EOL ", +}; +#define DEBUG_SHOW putchar(BitAcc & (1 << t) ? '1' : '0') +#define LOOKUP8(wid,tab,eoflab) do { \ + int t; \ + NeedBits8(wid,eoflab); \ + TabEnt = tab + GetBits(wid); \ + printf("%08lX/%d: %s%5d\t", (long) BitAcc, BitsAvail, \ + StateNames[TabEnt->State], TabEnt->Param); \ + for (t = 0; t < TabEnt->Width; t++) \ + DEBUG_SHOW; \ + putchar('\n'); \ + fflush(stdout); \ + ClrBits(TabEnt->Width); \ +} while (0) +#define LOOKUP16(wid,tab,eoflab) do { \ + int t; \ + NeedBits16(wid,eoflab); \ + TabEnt = tab + GetBits(wid); \ + printf("%08lX/%d: %s%5d\t", (long) BitAcc, BitsAvail, \ + StateNames[TabEnt->State], TabEnt->Param); \ + for (t = 0; t < TabEnt->Width; t++) \ + DEBUG_SHOW; \ + putchar('\n'); \ + fflush(stdout); \ + ClrBits(TabEnt->Width); \ +} while (0) + +#define SETVAL(x) do { \ + *pa++ = RunLength + (x); \ + printf("SETVAL: %d\t%d\n", RunLength + (x), a0); \ + a0 += x; \ + RunLength = 0; \ +} while (0) +#else +#define LOOKUP8(wid,tab,eoflab) do { \ + NeedBits8(wid,eoflab); \ + TabEnt = tab + GetBits(wid); \ + ClrBits(TabEnt->Width); \ +} while (0) +#define LOOKUP16(wid,tab,eoflab) do { \ + NeedBits16(wid,eoflab); \ + TabEnt = tab + GetBits(wid); \ + ClrBits(TabEnt->Width); \ +} while (0) + +/* + * Append a run to the run length array for the + * current row and reset decoding state. + */ +#define SETVAL(x) do { \ + *pa++ = RunLength + (x); \ + a0 += (x); \ + RunLength = 0; \ +} while (0) +#endif + +/* + * Synchronize input decoding at the start of each + * row by scanning for an EOL (if appropriate) and + * skipping any trash data that might be present + * after a decoding error. Note that the decoding + * done elsewhere that recognizes an EOL only consumes + * 11 consecutive zero bits. This means that if EOLcnt + * is non-zero then we still need to scan for the final flag + * bit that is part of the EOL code. + */ +#define SYNC_EOL(eoflab) do { \ + if (EOLcnt == 0) { \ + for (;;) { \ + NeedBits16(11,eoflab); \ + if (GetBits(11) == 0) \ + break; \ + ClrBits(1); \ + } \ + } \ + for (;;) { \ + NeedBits8(8,eoflab); \ + if (GetBits(8)) \ + break; \ + ClrBits(8); \ + } \ + while (GetBits(1) == 0) \ + ClrBits(1); \ + ClrBits(1); /* EOL bit */ \ + EOLcnt = 0; /* reset EOL counter/flag */ \ +} while (0) + +/* + * Cleanup the array of runs after decoding a row. + * We adjust final runs to insure the user buffer is not + * overwritten and/or undecoded area is white filled. + */ +#define CLEANUP_RUNS() do { \ + if (RunLength) \ + SETVAL(0); \ + if (a0 != lastx) { \ + badlength(a0, lastx); \ + while (a0 > lastx && pa > thisrun) \ + a0 -= *--pa; \ + if (a0 < lastx) { \ + if (a0 < 0) \ + a0 = 0; \ + if ((pa-thisrun)&1) \ + SETVAL(0); \ + SETVAL(lastx - a0); \ + } else if (a0 > lastx) { \ + SETVAL(lastx); \ + SETVAL(0); \ + } \ + } \ +} while (0) + +/* + * Decode a line of 1D-encoded data. + * + * The line expanders are written as macros so that they can be reused + * but still have direct access to the local variables of the "calling" + * function. + * + * Note that unlike the original version we have to explicitly test for + * a0 >= lastx after each black/white run is decoded. This is because + * the original code depended on the input data being zero-padded to + * insure the decoder recognized an EOL before running out of data. + */ + +/* + * Update the value of b1 using the array + * of runs for the reference line. + */ +#define CHECK_b1 do { \ + if (pa != thisrun) while (b1 <= a0 && b1 < lastx) { \ + b1 += pb[0] + pb[1]; \ + pb += 2; \ + } \ +} while (0) + +/* ==================================================================== */ +/* Declarations from tif_fax3.c */ +/* ==================================================================== */ + +/* + * Compression+decompression state blocks are + * derived from this ``base state'' block. + */ +typedef struct { + int rw_mode; /* O_RDONLY for decode, else encode */ + int mode; /* operating mode */ + GUInt32 rowbytes; /* bytes in a decoded scanline */ + GUInt32 rowpixels; /* pixels in a scanline */ + + GUInt16 cleanfaxdata; /* CleanFaxData tag */ + GUInt32 badfaxrun; /* BadFaxRun tag */ + GUInt32 badfaxlines; /* BadFaxLines tag */ + GUInt32 groupoptions; /* Group 3/4 options tag */ + GUInt32 recvparams; /* encoded Class 2 session params */ + char* subaddress; /* subaddress string */ + GUInt32 recvtime; /* time spent receiving (secs) */ +} Fax3BaseState; +#define Fax3State(tif) ((Fax3BaseState*) tif) + +typedef struct { + Fax3BaseState b; + const unsigned char* bitmap; /* bit reversal table */ + GUInt32 data; /* current i/o byte/word */ + int bit; /* current i/o bit in byte */ + int EOLcnt; /* count of EOL codes recognized */ + TIFFFaxFillFunc fill; /* fill routine */ + GUInt32* runs; /* b&w runs for current/previous row */ + GUInt32* refruns; /* runs for reference line */ + GUInt32* curruns; /* runs for current line */ +} Fax3DecodeState; +#define DecoderState(tif) ((Fax3DecodeState*) Fax3State(tif)) + +typedef enum { G3_1D, G3_2D } Ttag; +#ifdef notdef +typedef struct { + Fax3BaseState b; + int data; /* current i/o byte */ + int bit; /* current i/o bit in byte */ + Ttag tag; /* encoding state */ + unsigned char* refline; /* reference line for 2d decoding */ + int k; /* #rows left that can be 2d encoded */ + int maxk; /* max #rows that can be 2d encoded */ +} Fax3EncodeState; +#define EncoderState(tif) ((Fax3EncodeState*) Fax3State(tif)) +#endif + +#define is2DEncoding(sp) \ + (sp->b.groupoptions & GROUP3OPT_2DENCODING) +#define isAligned(p,t) ((((size_t)(p)) & (sizeof (t)-1)) == 0) + +/* + * Group 3 and Group 4 Decoding. + */ + +/* + * These macros glue the TIFF library state to + * the state expected by Frank's decoder. + */ +#define DECLARE_STATE(tif, sp) \ + Fax3DecodeState* sp = DecoderState(tif); \ + int a0; /* reference element */ \ + int lastx = sp->b.rowpixels; /* last element in row */ \ + GUInt32 BitAcc; /* bit accumulator */ \ + int BitsAvail; /* # valid bits in BitAcc */ \ + int RunLength; /* length of current run */ \ + unsigned char* cp; /* next byte of input data */ \ + unsigned char* ep; /* end of input data */ \ + GUInt32* pa; /* place to stuff next run */ \ + GUInt32* thisrun; /* current row's run array */ \ + int EOLcnt; /* # EOL codes recognized */ \ + const unsigned char* bitmap = sp->bitmap; /* input data bit reverser */ \ + const TIFFFaxTabEnt* TabEnt +#define DECLARE_STATE_2D(tif, sp, mod) \ + DECLARE_STATE(tif, sp, mod); \ + int b1; /* next change on prev line */ \ + GUInt32* pb /* next run in reference line */\ +/* + * Load any state that may be changed during decoding. + */ +#define CACHE_STATE(sp) do { \ + BitAcc = sp->data; \ + BitsAvail = sp->bit; \ + EOLcnt = sp->EOLcnt; \ + cp = (unsigned char*) rawcp; \ + ep = cp + rawcc; \ +} while (0) +/* + * Save state possibly changed during decoding. + */ +#define UNCACHE_STATE(sp) do { \ + sp->bit = BitsAvail; \ + sp->data = BitAcc; \ + sp->EOLcnt = EOLcnt; \ + rawcc -= (unsigned char *) cp - rawcp; \ + rawcp = (unsigned char *) cp; \ +} while (0) + +/* + * Routine for handling various errors/conditions. + * Note how they are "glued into the decoder" by + * overriding the definitions used by the decoder. + */ + +static void +Fax3Unexpected() +{ + CPLError( CE_Failure, CPLE_AppDefined, + "Bad code word" ); +} +#define unexpected(table, a0) Fax3Unexpected() + +static void +Fax3BadLength(GUInt32 a0, GUInt32 lastx) +{ + CPLError( CE_Warning, CPLE_AppDefined, + "%s (got %lu, expected %lu)", + a0 < lastx ? "Premature EOL" : "Line length mismatch", + (unsigned long) a0, (unsigned long) lastx ); +} + +#define badlength(a0,lastx) Fax3BadLength(a0, lastx) + +static void +Fax3PrematureEOF() +{ + CPLError( CE_Warning, CPLE_AppDefined, + "Premature EOF" ); +} +#define prematureEOF(a0) Fax3PrematureEOF() + +#define Nop + +/* + * The ZERO & FILL macros must handle spans < 2*sizeof(long) bytes. + * For machines with 64-bit longs this is <16 bytes; otherwise + * this is <8 bytes. We optimize the code here to reflect the + * machine characteristics. + */ +#if defined(__alpha) || _MIPS_SZLONG == 64 || defined(__LP64__) || defined(__arch64__) +#define FILL(n, cp) \ + switch (n) { \ + case 15:(cp)[14] = 0xff; case 14:(cp)[13] = 0xff; case 13: (cp)[12] = 0xff;\ + case 12:(cp)[11] = 0xff; case 11:(cp)[10] = 0xff; case 10: (cp)[9] = 0xff;\ + case 9: (cp)[8] = 0xff; case 8: (cp)[7] = 0xff; case 7: (cp)[6] = 0xff;\ + case 6: (cp)[5] = 0xff; case 5: (cp)[4] = 0xff; case 4: (cp)[3] = 0xff;\ + case 3: (cp)[2] = 0xff; case 2: (cp)[1] = 0xff; \ + case 1: (cp)[0] = 0xff; (cp) += (n); case 0: ; \ + } +#define ZERO(n, cp) \ + switch (n) { \ + case 15:(cp)[14] = 0; case 14:(cp)[13] = 0; case 13: (cp)[12] = 0; \ + case 12:(cp)[11] = 0; case 11:(cp)[10] = 0; case 10: (cp)[9] = 0; \ + case 9: (cp)[8] = 0; case 8: (cp)[7] = 0; case 7: (cp)[6] = 0; \ + case 6: (cp)[5] = 0; case 5: (cp)[4] = 0; case 4: (cp)[3] = 0; \ + case 3: (cp)[2] = 0; case 2: (cp)[1] = 0; \ + case 1: (cp)[0] = 0; (cp) += (n); case 0: ; \ + } +#else +#define FILL(n, cp) \ + switch (n) { \ + case 7: (cp)[6] = 0xff; case 6: (cp)[5] = 0xff; case 5: (cp)[4] = 0xff; \ + case 4: (cp)[3] = 0xff; case 3: (cp)[2] = 0xff; case 2: (cp)[1] = 0xff; \ + case 1: (cp)[0] = 0xff; (cp) += (n); case 0: ; \ + } +#define ZERO(n, cp) \ + switch (n) { \ + case 7: (cp)[6] = 0; case 6: (cp)[5] = 0; case 5: (cp)[4] = 0; \ + case 4: (cp)[3] = 0; case 3: (cp)[2] = 0; case 2: (cp)[1] = 0; \ + case 1: (cp)[0] = 0; (cp) += (n); case 0: ; \ + } +#endif + +/************************************************************************/ +/* _TIFFFax3fillruns() */ +/* */ +/* Bit-fill a row according to the white/black */ +/* runs generated during G3/G4 decoding. */ +/************************************************************************/ + +static void +aig_TIFFFax3fillruns(unsigned char* buf, GUInt32* runs, GUInt32* erun, + GUInt32 lastx) +{ + static const unsigned char _fillmasks[] = + { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + unsigned char* cp; + GUInt32 x, bx, run; + GInt32 n, nw; + long* lp; + + if ((erun-runs)&1) + *erun++ = 0; + x = 0; + for (; runs < erun; runs += 2) { + run = runs[0]; + if (x+run > lastx || run > lastx ) + run = runs[0] = (GUInt32) (lastx - x); + if (run) { + cp = buf + (x>>3); + bx = x&7; + if (run > 8-bx) { + if (bx) { /* align to byte boundary */ + *cp++ &= 0xff << (8-bx); + run -= 8-bx; + } + if( (n = run >> 3) != 0 ) { /* multiple bytes to fill */ + if ((n/sizeof (long)) > 1) { + /* + * Align to longword boundary and fill. + */ + for (; n && !isAligned(cp, long); n--) + *cp++ = 0x00; + lp = (long*) cp; + nw = (GInt32)(n / sizeof (long)); + n -= nw * sizeof (long); + do { + *lp++ = 0L; + } while (--nw); + cp = (unsigned char*) lp; + } + ZERO(n, cp); + run &= 7; + } + if (run) + cp[0] &= 0xff >> run; + } else + cp[0] &= ~(_fillmasks[run]>>bx); + x += runs[0]; + } + run = runs[1]; + if (x+run > lastx || run > lastx ) + run = runs[1] = lastx - x; + if (run) { + cp = buf + (x>>3); + bx = x&7; + if (run > 8-bx) { + if (bx) { /* align to byte boundary */ + *cp++ |= 0xff >> bx; + run -= 8-bx; + } + if( (n = run>>3) != 0 ) { /* multiple bytes to fill */ + if ((n/sizeof (long)) > 1) { + /* + * Align to longword boundary and fill. + */ + for (; n && !isAligned(cp, long); n--) + *cp++ = 0xff; + lp = (long*) cp; + nw = (GInt32)(n / sizeof (long)); + n -= nw * sizeof (long); + do { + *lp++ = -1L; + } while (--nw); + cp = (unsigned char*) lp; + } + FILL(n, cp); + run &= 7; + } + if (run) + cp[0] |= 0xff00 >> run; + } else + cp[0] |= _fillmasks[run]>>bx; + x += runs[1]; + } + } + assert(x == lastx); +} +#undef ZERO +#undef FILL + +/************************************************************************/ +/* Fax3DecodeRLE() */ +/* */ +/* Decode the requested amount of RLE-encoded data. */ +/************************************************************************/ + +static int +Fax3DecodeRLE(Fax3BaseState* tif, unsigned char *buf, int occ, + unsigned char *rawcp, int rawcc ) +{ + DECLARE_STATE(tif, sp); + int mode = sp->b.mode; + + CACHE_STATE(sp); + thisrun = sp->curruns; + while ((long)occ > 0) { + a0 = 0; + RunLength = 0; + pa = thisrun; +#ifdef FAX3_DEBUG + printf("\nBitAcc=%08X, BitsAvail = %d\n", BitAcc, BitsAvail); + printf("-------------------- \n"); + fflush(stdout); +#endif + +/* -------------------------------------------------------------------- */ +/* EXPAND1D() */ +/* */ +/* */ +/* Decode a line of 1D-encoded data. */ +/* */ +/* The line expanders are written as macros so that they can */ +/* be reused but still have direct access to the local */ +/* variables of the "calling" function. */ +/* */ +/* Note that unlike the original version we have to explicitly */ +/* test for a0 >= lastx after each black/white run is decoded. */ +/* This is because the original code depended on the input data */ +/* being zero-padded to insure the decoder recognized an EOL */ +/* before running out of data. */ +/* -------------------------------------------------------------------- */ + do { + for (;;) { + for (;;) { + LOOKUP16(12, aig_TIFFFaxWhiteTable, eof1d); + switch (TabEnt->State) { + case S_EOL: + EOLcnt = 1; + goto done1d; + case S_TermW: + SETVAL(TabEnt->Param); + goto doneWhite1d; + case S_MakeUpW: + case S_MakeUp: + a0 += TabEnt->Param; + RunLength += TabEnt->Param; + break; + default: + unexpected("WhiteTable", a0); + goto done1d; + } + } + doneWhite1d: + if (a0 >= lastx) + goto done1d; + for (;;) { + LOOKUP16(13, aig_TIFFFaxBlackTable, eof1d); + switch (TabEnt->State) { + case S_EOL: + EOLcnt = 1; + goto done1d; + case S_TermB: + SETVAL(TabEnt->Param); + goto doneBlack1d; + case S_MakeUpB: + case S_MakeUp: + a0 += TabEnt->Param; + RunLength += TabEnt->Param; + break; + default: + unexpected("BlackTable", a0); + goto done1d; + } + } + doneBlack1d: + if (a0 >= lastx) + goto done1d; + if( *(pa-1) == 0 && *(pa-2) == 0 ) + pa -= 2; + } + eof1d: + prematureEOF(a0); + CLEANUP_RUNS(); + goto EOFRLE; + done1d: + CLEANUP_RUNS(); + } while (0); + +/* -------------------------------------------------------------------- */ +/* Fill */ +/* -------------------------------------------------------------------- */ + (*sp->fill)(buf, thisrun, pa, lastx); + /* + * Cleanup at the end of the row. + */ + if (mode & FAXMODE_BYTEALIGN) { + int n = BitsAvail - (BitsAvail &~ 7); + ClrBits(n); + } else if (mode & FAXMODE_WORDALIGN) { + int n = BitsAvail - (BitsAvail &~ 15); + ClrBits(n); + if (BitsAvail == 0 && !isAligned(cp, GUInt16)) + cp++; + } + buf += sp->b.rowbytes; + occ -= sp->b.rowbytes; +#ifdef notdef + if (occ != 0) + tif->tif_row++; +#endif + continue; + EOFRLE: /* premature EOF */ + (*sp->fill)(buf, thisrun, pa, lastx); + UNCACHE_STATE(sp); + return (-1); + } + UNCACHE_STATE(sp); + + return (1); +} + + +/************************************************************************/ +/* DecompressCCITTRLETile() */ +/************************************************************************/ + +CPLErr DecompressCCITTRLETile( unsigned char *pabySrcData, int nSrcBytes, + unsigned char *pabyDstData, int nDstBytes, + int nBlockXSize, + CPL_UNUSED int nBlockYSize ) +{ + Fax3DecodeState sDecoderState; + Fax3BaseState* sp = (Fax3BaseState *) &sDecoderState; + unsigned char runs_buf[4000]; + long rowbytes, rowpixels; + + memset( &sDecoderState, 0, sizeof(sDecoderState) ); + + sp->groupoptions = 0; + sp->recvparams = 0; + sp->subaddress = NULL; + + DecoderState(sp)->runs = NULL; + DecoderState(sp)->fill = aig_TIFFFax3fillruns; + + if( sizeof(runs_buf) < (size_t)(nBlockXSize * 2 + 3) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Run buffer too small"); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* */ +/* -------------------------------------------------------------------- */ + /* + * Calculate the scanline/tile widths. + */ + rowbytes = nBlockXSize / 8; + rowpixels = nBlockXSize; + + sp->rowbytes = (GUInt32) rowbytes; + sp->rowpixels = (GUInt32) rowpixels; + sp->mode |= FAXMODE_BYTEALIGN; + /* + * Allocate any additional space required for decoding/encoding. + */ + { + Fax3DecodeState* dsp = DecoderState(sp); + + dsp->runs = (GUInt32*) runs_buf; + dsp->curruns = dsp->runs; + dsp->refruns = NULL; + } + +/* -------------------------------------------------------------------- */ +/* */ +/* -------------------------------------------------------------------- */ + DecoderState(sp)->bit = 0; /* force initial read */ + DecoderState(sp)->data = 0; + DecoderState(sp)->EOLcnt = 0; /* force initial scan for EOL */ + + DecoderState(sp)->bitmap = aig_TIFFBitRevTable; + + if (DecoderState(sp)->refruns) { /* init reference line to white */ + DecoderState(sp)->refruns[0] = (GUInt32) DecoderState(sp)->b.rowpixels; + DecoderState(sp)->refruns[1] = 0; + } + + if( Fax3DecodeRLE( sp, pabyDstData, nDstBytes, + pabySrcData, nSrcBytes ) == 1 ) + return CE_None; + else + return CE_Failure; +} diff --git a/bazaar/plugin/gdal/frmts/aigrid/aigdataset.cpp b/bazaar/plugin/gdal/frmts/aigrid/aigdataset.cpp new file mode 100644 index 000000000..584bd468f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aigrid/aigdataset.cpp @@ -0,0 +1,1100 @@ +/****************************************************************************** + * $Id: aigdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: Arc/Info Binary Grid Driver + * Purpose: Implements GDAL interface to underlying library. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" +#include "gdal_rat.h" +#include "aigrid.h" +#include "avc.h" +#include + +CPL_CVSID("$Id: aigdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +CPL_C_START +void GDALRegister_AIGrid(void); +CPL_C_END + +static CPLString OSR_GDS( char **papszNV, const char * pszField, + const char *pszDefaultValue ); + + +/************************************************************************/ +/* ==================================================================== */ +/* AIGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class AIGRasterBand; + +class CPL_DLL AIGDataset : public GDALPamDataset +{ + friend class AIGRasterBand; + + AIGInfo_t *psInfo; + + char **papszPrj; + char *pszProjection; + + GDALColorTable *poCT; + int bHasReadRat; + + void TranslateColorTable( const char * ); + + void ReadRAT(); + GDALRasterAttributeTable *poRAT; + + public: + AIGDataset(); + ~AIGDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual const char *GetProjectionRef(void); + virtual char **GetFileList(void); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* AIGRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class AIGRasterBand : public GDALPamRasterBand + +{ + friend class AIGDataset; + + public: + + AIGRasterBand( AIGDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual double GetMinimum( int *pbSuccess ); + virtual double GetMaximum( int *pbSuccess ); + virtual double GetNoDataValue( int *pbSuccess ); + + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual GDALRasterAttributeTable *GetDefaultRAT(); +}; + +/************************************************************************/ +/* AIGRasterBand() */ +/************************************************************************/ + +AIGRasterBand::AIGRasterBand( AIGDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + nBlockXSize = poDS->psInfo->nBlockXSize; + nBlockYSize = poDS->psInfo->nBlockYSize; + + if( poDS->psInfo->nCellType == AIG_CELLTYPE_INT + && poDS->psInfo->dfMin >= 0.0 && poDS->psInfo->dfMax <= 254.0 ) + { + eDataType = GDT_Byte; + } + else if( poDS->psInfo->nCellType == AIG_CELLTYPE_INT + && poDS->psInfo->dfMin >= -32767 && poDS->psInfo->dfMax <= 32767 ) + { + eDataType = GDT_Int16; + } + else if( poDS->psInfo->nCellType == AIG_CELLTYPE_INT ) + { + eDataType = GDT_Int32; + } + else + { + eDataType = GDT_Float32; + } +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr AIGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + AIGDataset *poODS = (AIGDataset *) poDS; + GInt32 *panGridRaster; + int i; + + if( poODS->psInfo->nCellType == AIG_CELLTYPE_INT ) + { + panGridRaster = (GInt32 *) VSIMalloc3(4,nBlockXSize,nBlockYSize); + if( panGridRaster == NULL || + AIGReadTile( poODS->psInfo, nBlockXOff, nBlockYOff, panGridRaster ) + != CE_None ) + { + CPLFree( panGridRaster ); + return CE_Failure; + } + + if( eDataType == GDT_Byte ) + { + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + { + if( panGridRaster[i] == ESRI_GRID_NO_DATA ) + ((GByte *) pImage)[i] = 255; + else + ((GByte *) pImage)[i] = (GByte) panGridRaster[i]; + } + } + else if( eDataType == GDT_Int16 ) + { + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + { + if( panGridRaster[i] == ESRI_GRID_NO_DATA ) + ((GInt16 *) pImage)[i] = -32768; + else + ((GInt16 *) pImage)[i] = (GInt16) panGridRaster[i]; + } + } + else + { + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + ((GInt32 *) pImage)[i] = panGridRaster[i]; + } + + CPLFree( panGridRaster ); + + return CE_None; + } + else + { + return AIGReadFloatTile( poODS->psInfo, nBlockXOff, nBlockYOff, + (float *) pImage ); + } +} + +/************************************************************************/ +/* GetDefaultRAT() */ +/************************************************************************/ + +GDALRasterAttributeTable *AIGRasterBand::GetDefaultRAT() + +{ + AIGDataset *poODS = (AIGDataset *) poDS; + +/* -------------------------------------------------------------------- */ +/* Read info raster attribute table, if present. */ +/* -------------------------------------------------------------------- */ + if (!poODS->bHasReadRat) + { + poODS->ReadRAT(); + poODS->bHasReadRat = TRUE; + } + + if( poODS->poRAT ) + return poODS->poRAT; + else + return GDALPamRasterBand::GetDefaultRAT(); +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double AIGRasterBand::GetMinimum( int *pbSuccess ) + +{ + AIGDataset *poODS = (AIGDataset *) poDS; + + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return poODS->psInfo->dfMin; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double AIGRasterBand::GetMaximum( int *pbSuccess ) + +{ + AIGDataset *poODS = (AIGDataset *) poDS; + + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return poODS->psInfo->dfMax; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double AIGRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + if( eDataType == GDT_Float32 ) + return ESRI_GRID_FLOAT_NO_DATA; + else if( eDataType == GDT_Int16 ) + return -32768; + else if( eDataType == GDT_Byte ) + return 255; + else + return ESRI_GRID_NO_DATA; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp AIGRasterBand::GetColorInterpretation() + +{ + AIGDataset *poODS = (AIGDataset *) poDS; + + if( poODS->poCT != NULL ) + return GCI_PaletteIndex; + else + return GDALPamRasterBand::GetColorInterpretation(); +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *AIGRasterBand::GetColorTable() + +{ + AIGDataset *poODS = (AIGDataset *) poDS; + + if( poODS->poCT != NULL ) + return poODS->poCT; + else + return GDALPamRasterBand::GetColorTable(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* AIGDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* AIGDataset() */ +/************************************************************************/ + +AIGDataset::AIGDataset() + +{ + psInfo = NULL; + papszPrj = NULL; + pszProjection = CPLStrdup(""); + poCT = NULL; + poRAT = NULL; + bHasReadRat = FALSE; +} + +/************************************************************************/ +/* ~AIGDataset() */ +/************************************************************************/ + +AIGDataset::~AIGDataset() + +{ + FlushCache(); + CPLFree( pszProjection ); + CSLDestroy( papszPrj ); + if( psInfo != NULL ) + AIGClose( psInfo ); + + if( poCT != NULL ) + delete poCT; + + if( poRAT != NULL ) + delete poRAT; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **AIGDataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + + // Add in all files in the cover directory. + char **papszCoverFiles = VSIReadDir( GetDescription() ); + int i; + + for( i = 0; papszCoverFiles != NULL && papszCoverFiles[i] != NULL; i++ ) + { + if( EQUAL(papszCoverFiles[i],".") + || EQUAL(papszCoverFiles[i],"..") ) + continue; + + papszFileList = + CSLAddString( papszFileList, + CPLFormFilename( GetDescription(), + papszCoverFiles[i], + NULL ) ); + } + CSLDestroy(papszCoverFiles); + + return papszFileList; +} + +/************************************************************************/ +/* AIGErrorHandlerVATOpen() */ +/************************************************************************/ + +class AIGErrorDescription +{ + public: + CPLErr eErr; + int no; + CPLString osMsg; +}; + +static void CPL_STDCALL AIGErrorHandlerVATOpen(CPLErr eErr, int no, const char* msg) +{ + std::vector* paoErrors = + (std::vector* )CPLGetErrorHandlerUserData(); + if( EQUALN(msg, "EOF encountered in", strlen("EOF encountered in")) && + strstr(msg, "../info/arc.dir") != NULL ) + return; + if( EQUALN(msg, "Failed to open table ", strlen("Failed to open table ")) ) + return; + AIGErrorDescription oError; + oError.eErr = eErr; + oError.no = no; + oError.osMsg = msg; + paoErrors->push_back(oError); +} + +/************************************************************************/ +/* ReadRAT() */ +/************************************************************************/ + +void AIGDataset::ReadRAT() + +{ +#ifndef OGR_ENABLED +#else +/* -------------------------------------------------------------------- */ +/* Check if we have an associated info directory. If not */ +/* return quietly. */ +/* -------------------------------------------------------------------- */ + CPLString osInfoPath, osTableName; + VSIStatBufL sStatBuf; + + osInfoPath = psInfo->pszCoverName; + osInfoPath += "/../info"; + + if( VSIStatL( osInfoPath, &sStatBuf ) != 0 ) + { + CPLDebug( "AIG", "No associated info directory at: %s, skip RAT.", + osInfoPath.c_str() ); + return; + } + + osInfoPath += "/"; + +/* -------------------------------------------------------------------- */ +/* Attempt to open the VAT table associated with this coverage. */ +/* -------------------------------------------------------------------- */ + osTableName = CPLGetFilename(psInfo->pszCoverName); + osTableName += ".VAT"; + + /* Turn off errors that can be triggered if the info has no VAT */ + /* table related with this coverage */ + std::vector aoErrors; + CPLPushErrorHandlerEx(AIGErrorHandlerVATOpen, &aoErrors ); + + AVCBinFile *psFile = + AVCBinReadOpen( osInfoPath, osTableName, + AVCCoverTypeUnknown, AVCFileTABLE, NULL ); + CPLPopErrorHandler(); + + /* Emit other errors */ + std::vector::const_iterator oIter; + for( oIter = aoErrors.begin(); oIter != aoErrors.end(); ++oIter ) + { + const AIGErrorDescription& oError = *oIter; + CPLError( oError.eErr, oError.no, "%s", oError.osMsg.c_str() ); + } + + CPLErrorReset(); + if( psFile == NULL ) + return; + + AVCTableDef *psTableDef = psFile->hdr.psTableDef; + +/* -------------------------------------------------------------------- */ +/* Setup columns in corresponding RAT. */ +/* -------------------------------------------------------------------- */ + int iField; + + poRAT = new GDALDefaultRasterAttributeTable(); + + for( iField = 0; iField < psTableDef->numFields; iField++ ) + { + AVCFieldInfo *psFDef = psTableDef->pasFieldDef + iField; + GDALRATFieldUsage eFUsage = GFU_Generic; + GDALRATFieldType eFType = GFT_String; + + CPLString osFName = psFDef->szName; + osFName.Trim(); + + if( EQUAL(osFName,"VALUE") ) + eFUsage = GFU_MinMax; + else if( EQUAL(osFName,"COUNT") ) + eFUsage = GFU_PixelCount; + + if( psFDef->nType1 * 10 == AVC_FT_BININT ) + eFType = GFT_Integer; + else if( psFDef->nType1 * 10 == AVC_FT_BINFLOAT ) + eFType = GFT_Real; + + poRAT->CreateColumn( osFName, eFType, eFUsage ); + } + +/* -------------------------------------------------------------------- */ +/* Process all records into RAT. */ +/* -------------------------------------------------------------------- */ + AVCField *pasFields; + int iRecord = 0; + + while( (pasFields = AVCBinReadNextTableRec(psFile)) != NULL ) + { + iRecord++; + + for( iField = 0; iField < psTableDef->numFields; iField++ ) + { + switch( psTableDef->pasFieldDef[iField].nType1 * 10 ) + { + case AVC_FT_DATE: + case AVC_FT_FIXINT: + case AVC_FT_CHAR: + case AVC_FT_FIXNUM: + { + // XXX - I bet mloskot would like to see const_cast + static_cast :-) + const char* pszTmp = (const char*)(pasFields[iField].pszStr); + CPLString osStrValue( pszTmp ); + poRAT->SetValue( iRecord-1, iField, osStrValue.Trim() ); + } + break; + + case AVC_FT_BININT: + if( psTableDef->pasFieldDef[iField].nSize == 4 ) + poRAT->SetValue( iRecord-1, iField, + pasFields[iField].nInt32 ); + else + poRAT->SetValue( iRecord-1, iField, + pasFields[iField].nInt16 ); + break; + + case AVC_FT_BINFLOAT: + if( psTableDef->pasFieldDef[iField].nSize == 4 ) + poRAT->SetValue( iRecord-1, iField, + pasFields[iField].fFloat ); + else + poRAT->SetValue( iRecord-1, iField, + pasFields[iField].dDouble ); + break; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + + AVCBinReadClose( psFile ); + + /* Workaround against #2447 and #3031, to avoid binding languages */ + /* not being able to open the dataset */ + CPLErrorReset(); + +#endif /* OGR_ENABLED */ +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *AIGDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + AIGInfo_t *psInfo; + +/* -------------------------------------------------------------------- */ +/* If the pass name ends in .adf assume a file within the */ +/* coverage has been selected, and strip that off the coverage */ +/* name. */ +/* -------------------------------------------------------------------- */ + CPLString osCoverName; + + osCoverName = poOpenInfo->pszFilename; + if( osCoverName.size() > 4 + && EQUAL(osCoverName.c_str()+osCoverName.size()-4,".adf") ) + { + osCoverName = CPLGetDirname( poOpenInfo->pszFilename ); + if( osCoverName == "" ) + osCoverName = "."; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise verify we were already given a directory. */ +/* -------------------------------------------------------------------- */ + else if( !poOpenInfo->bIsDirectory ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Verify that a few of the "standard" files are available. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + CPLString osTestName; + + osTestName.Printf( "%s/hdr.adf", osCoverName.c_str() ); + if( VSIStatL( osTestName, &sStatBuf ) != 0 ) + + { + osTestName.Printf( "%s/HDR.ADF", osCoverName.c_str() ); + if( VSIStatL( osTestName, &sStatBuf ) != 0 ) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm we have at least one raster data file. These can be */ +/* sparse so we don't require particular ones to exists but if */ +/* there are none this is likely not a grid. */ +/* -------------------------------------------------------------------- */ + char **papszFileList = VSIReadDir( osCoverName ); + int iFile; + int bGotOne = FALSE; + + if (papszFileList == NULL) + { + /* Useful when reading from /vsicurl/ on servers that don't */ + /* return a file list */ + /* such as /vsicurl/http://eros.usgs.gov/archive/nslrsda/GeoTowns/NLCD/89110458 */ + do + { + osTestName.Printf( "%s/W001001.ADF", osCoverName.c_str() ); + if( VSIStatL( osTestName, &sStatBuf ) == 0 ) + { + bGotOne = TRUE; + break; + } + + osTestName.Printf( "%s/w001001.adf", osCoverName.c_str() ); + if( VSIStatL( osTestName, &sStatBuf ) == 0 ) + { + bGotOne = TRUE; + break; + } + } while(0); + } + + for( iFile = 0; + papszFileList != NULL && papszFileList[iFile] != NULL && !bGotOne; + iFile++ ) + { + if( strlen(papszFileList[iFile]) != 11 ) + continue; + + // looking for something like w001001.adf or z001013.adf + if( papszFileList[iFile][0] != 'w' + && papszFileList[iFile][0] != 'W' + && papszFileList[iFile][0] != 'z' + && papszFileList[iFile][0] != 'Z' ) + continue; + + if( strncmp(papszFileList[iFile] + 1, "0010", 4) != 0 ) + continue; + + if( !EQUAL(papszFileList[iFile] + 7, ".adf") ) + continue; + + bGotOne = TRUE; + } + CSLDestroy( papszFileList ); + + if( !bGotOne ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + psInfo = AIGOpen( osCoverName.c_str(), "r" ); + + if( psInfo == NULL ) + { + CPLErrorReset(); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + AIGClose(psInfo); + CPLError( CE_Failure, CPLE_NotSupported, + "The AIG driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + AIGDataset *poDS; + + poDS = new AIGDataset(); + + poDS->psInfo = psInfo; + +/* -------------------------------------------------------------------- */ +/* Try to read a color table (.clr). It seems it is legal to */ +/* have more than one so we just use the first one found. */ +/* -------------------------------------------------------------------- */ + char **papszFiles = CPLReadDir( psInfo->pszCoverName ); + CPLString osClrFilename; + CPLString osCleanPath = CPLCleanTrailingSlash( psInfo->pszCoverName ); + + // first check for any .clr in coverage dir. + for( iFile = 0; papszFiles != NULL && papszFiles[iFile] != NULL; iFile++ ) + { + if( !EQUAL(CPLGetExtension(papszFiles[iFile]),"clr") && !EQUAL(CPLGetExtension(papszFiles[iFile]),"CLR")) + continue; + + osClrFilename = CPLFormFilename( psInfo->pszCoverName, + papszFiles[iFile], NULL ); + break; + } + + CSLDestroy( papszFiles ); + + // Look in parent if we don't find a .clr in the coverage dir. + if( strlen(osClrFilename) == 0 ) + { + osTestName.Printf( "%s/../%s.clr", + psInfo->pszCoverName, + CPLGetFilename( osCleanPath ) ); + + if( VSIStatL( osTestName, &sStatBuf ) != 0 ) + + { + osTestName.Printf( "%s/../%s.CLR", + psInfo->pszCoverName, + CPLGetFilename( osCleanPath ) ); + + if( !VSIStatL( osTestName, &sStatBuf ) ) + osClrFilename = osTestName; + } + else + osClrFilename = osTestName; + } + + + if( strlen(osClrFilename) > 0 ) + poDS->TranslateColorTable( osClrFilename ); + +/* -------------------------------------------------------------------- */ +/* Establish raster info. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = psInfo->nPixels; + poDS->nRasterYSize = psInfo->nLines; + poDS->nBands = 1; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->SetBand( 1, new AIGRasterBand( poDS, 1 ) ); + +/* -------------------------------------------------------------------- */ +/* Try to read projection file. */ +/* -------------------------------------------------------------------- */ + const char *pszPrjFilename; + + pszPrjFilename = CPLFormCIFilename( psInfo->pszCoverName, "prj", "adf" ); + if( VSIStatL( pszPrjFilename, &sStatBuf ) == 0 ) + { + OGRSpatialReference oSRS; + + poDS->papszPrj = CSLLoad( pszPrjFilename ); + + if( oSRS.importFromESRI( poDS->papszPrj ) == OGRERR_NONE ) + { + // If geographic values are in seconds, we must transform. + // Is there a code for minutes too? + if( oSRS.IsGeographic() + && EQUAL(OSR_GDS( poDS->papszPrj, "Units", ""), "DS") ) + { + psInfo->dfLLX /= 3600.0; + psInfo->dfURY /= 3600.0; + psInfo->dfCellSizeX /= 3600.0; + psInfo->dfCellSizeY /= 3600.0; + } + + CPLFree( poDS->pszProjection ); + oSRS.exportToWkt( &(poDS->pszProjection) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( psInfo->pszCoverName ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Open overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, psInfo->pszCoverName ); + + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr AIGDataset::GetGeoTransform( double * padfTransform ) + +{ + padfTransform[0] = psInfo->dfLLX; + padfTransform[1] = psInfo->dfCellSizeX; + padfTransform[2] = 0; + + padfTransform[3] = psInfo->dfURY; + padfTransform[4] = 0; + padfTransform[5] = -psInfo->dfCellSizeY; + + return( CE_None ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *AIGDataset::GetProjectionRef() + +{ + return pszProjection; +} + +/************************************************************************/ +/* TranslateColorTable() */ +/************************************************************************/ + +void AIGDataset::TranslateColorTable( const char *pszClrFilename ) + +{ + int iLine; + char **papszClrLines; + + papszClrLines = CSLLoad( pszClrFilename ); + if( papszClrLines == NULL ) + return; + + poCT = new GDALColorTable(); + + for( iLine = 0; papszClrLines[iLine] != NULL; iLine++ ) + { + char **papszTokens = CSLTokenizeString( papszClrLines[iLine] ); + + if( CSLCount(papszTokens) >= 4 && papszTokens[0][0] != '#' ) + { + int nIndex; + GDALColorEntry sEntry; + + nIndex = atoi(papszTokens[0]); + sEntry.c1 = (short) atoi(papszTokens[1]); + sEntry.c2 = (short) atoi(papszTokens[2]); + sEntry.c3 = (short) atoi(papszTokens[3]); + sEntry.c4 = 255; + + if( (nIndex < 0 || nIndex > 33000) + || (sEntry.c1 < 0 || sEntry.c1 > 255) + || (sEntry.c2 < 0 || sEntry.c2 > 255) + || (sEntry.c3 < 0 || sEntry.c3 > 255) ) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Color table entry appears to be corrupt, skipping the rest. " ); + break; + } + + poCT->SetColorEntry( nIndex, &sEntry ); + } + + CSLDestroy( papszTokens ); + } + + CSLDestroy( papszClrLines ); +} + +/************************************************************************/ +/* OSR_GDS() */ +/************************************************************************/ + +static CPLString OSR_GDS( char **papszNV, const char * pszField, + const char *pszDefaultValue ) + +{ + int iLine; + + if( papszNV == NULL || papszNV[0] == NULL ) + return pszDefaultValue; + + for( iLine = 0; + papszNV[iLine] != NULL && + !EQUALN(papszNV[iLine],pszField,strlen(pszField)); + iLine++ ) {} + + if( papszNV[iLine] == NULL ) + return pszDefaultValue; + else + { + CPLString osResult; + char **papszTokens; + + papszTokens = CSLTokenizeString(papszNV[iLine]); + + if( CSLCount(papszTokens) > 1 ) + osResult = papszTokens[1]; + else + osResult = pszDefaultValue; + + CSLDestroy( papszTokens ); + return osResult; + } +} + +/************************************************************************/ +/* AIGRename() */ +/* */ +/* Custom renamer for AIG dataset. */ +/************************************************************************/ + +static CPLErr AIGRename( const char *pszNewName, const char *pszOldName ) + +{ +/* -------------------------------------------------------------------- */ +/* Make sure we are talking about paths to the coverage */ +/* directory. */ +/* -------------------------------------------------------------------- */ + CPLString osOldPath, osNewPath; + + if( strlen(CPLGetExtension(pszNewName)) > 0 ) + osNewPath = CPLGetPath(pszNewName); + else + osNewPath = pszNewName; + + if( strlen(CPLGetExtension(pszOldName)) > 0 ) + osOldPath = CPLGetPath(pszOldName); + else + osOldPath = pszOldName; + +/* -------------------------------------------------------------------- */ +/* Get file list. */ +/* -------------------------------------------------------------------- */ + + GDALDatasetH hDS = GDALOpen( osOldPath, GA_ReadOnly ); + if( hDS == NULL ) + return CE_Failure; + + char **papszFileList = GDALGetFileList( hDS ); + GDALClose( hDS ); + + if( papszFileList == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Work out the corresponding new names. */ +/* -------------------------------------------------------------------- */ + char **papszNewFileList = NULL; + int i; + + for( i = 0; papszFileList[i] != NULL; i++ ) + { + CPLString osNewFilename; + + if( !EQUALN(papszFileList[i],osOldPath,strlen(osOldPath)) ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + osNewFilename = osNewPath + (papszFileList[i] + strlen(osOldPath)); + + papszNewFileList = CSLAddString( papszNewFileList, osNewFilename ); + } + +/* -------------------------------------------------------------------- */ +/* Try renaming the directory. */ +/* -------------------------------------------------------------------- */ + if( VSIRename( osNewPath, osOldPath ) != 0 ) + { + if( VSIMkdir( osNewPath, 0777 ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create directory %s:\n%s", + osNewPath.c_str(), + VSIStrerror(errno) ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Copy/rename any remaining files. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + + for( i = 0; papszFileList[i] != NULL; i++ ) + { + if( VSIStatL( papszFileList[i], &sStatBuf ) == 0 + && VSI_ISREG( sStatBuf.st_mode ) ) + { + if( CPLMoveFile( papszNewFileList[i], papszFileList[i] ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to move %s to %s:\n%s", + papszFileList[i], + papszNewFileList[i], + VSIStrerror(errno) ); + return CE_Failure; + } + } + } + + if( VSIStatL( osOldPath, &sStatBuf ) == 0 ) + CPLUnlinkTree( osOldPath ); + + return CE_None; +} + +/************************************************************************/ +/* AIGDelete() */ +/* */ +/* Custom dataset deleter for AIG dataset. */ +/************************************************************************/ + +static CPLErr AIGDelete( const char *pszDatasetname ) + +{ +/* -------------------------------------------------------------------- */ +/* Get file list. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hDS = GDALOpen( pszDatasetname, GA_ReadOnly ); + if( hDS == NULL ) + return CE_Failure; + + char **papszFileList = GDALGetFileList( hDS ); + GDALClose( hDS ); + + if( papszFileList == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Delete all regular files. */ +/* -------------------------------------------------------------------- */ + int i; + for( i = 0; papszFileList[i] != NULL; i++ ) + { + VSIStatBufL sStatBuf; + if( VSIStatL( papszFileList[i], &sStatBuf ) == 0 + && VSI_ISREG( sStatBuf.st_mode ) ) + { + if( VSIUnlink( papszFileList[i] ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to delete '%s':\n%s", + papszFileList[i], VSIStrerror( errno ) ); + return CE_Failure; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Delete directories. */ +/* -------------------------------------------------------------------- */ + for( i = 0; papszFileList[i] != NULL; i++ ) + { + VSIStatBufL sStatBuf; + if( VSIStatL( papszFileList[i], &sStatBuf ) == 0 + && VSI_ISDIR( sStatBuf.st_mode ) ) + { + if( CPLUnlinkTree( papszFileList[i] ) != 0 ) + return CE_Failure; + } + } + + return CE_None; +} + +/************************************************************************/ +/* GDALRegister_AIG() */ +/************************************************************************/ + +void GDALRegister_AIGrid() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "AIG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "AIG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Arc/Info Binary Grid" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#AIG" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = AIGDataset::Open; + + poDriver->pfnRename = AIGRename; + poDriver->pfnDelete = AIGDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/aigrid/aigopen.c b/bazaar/plugin/gdal/frmts/aigrid/aigopen.c new file mode 100644 index 000000000..80e036a19 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aigrid/aigopen.c @@ -0,0 +1,479 @@ +/****************************************************************************** + * $Id: aigopen.c 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: Arc/Info Binary Grid Translator + * Purpose: Grid file access cover API for non-GDAL use. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "aigrid.h" + +CPL_CVSID("$Id: aigopen.c 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* AIGOpen() */ +/************************************************************************/ + +AIGInfo_t *AIGOpen( const char * pszInputName, const char * pszAccess ) + +{ + AIGInfo_t *psInfo; + char *pszCoverName; + + (void) pszAccess; + +/* -------------------------------------------------------------------- */ +/* If the pass name ends in .adf assume a file within the */ +/* coverage has been selected, and strip that off the coverage */ +/* name. */ +/* -------------------------------------------------------------------- */ + pszCoverName = CPLStrdup( pszInputName ); + if( EQUAL(pszCoverName+strlen(pszCoverName)-4, ".adf") ) + { + int i; + + for( i = strlen(pszCoverName)-1; i > 0; i-- ) + { + if( pszCoverName[i] == '\\' || pszCoverName[i] == '/' ) + { + pszCoverName[i] = '\0'; + break; + } + } + + if( i == 0 ) + strcpy(pszCoverName,"."); + } + +/* -------------------------------------------------------------------- */ +/* Allocate info structure. */ +/* -------------------------------------------------------------------- */ + psInfo = (AIGInfo_t *) CPLCalloc(sizeof(AIGInfo_t),1); + psInfo->bHasWarned = FALSE; + psInfo->pszCoverName = pszCoverName; + +/* -------------------------------------------------------------------- */ +/* Read the header file. */ +/* -------------------------------------------------------------------- */ + if( AIGReadHeader( pszCoverName, psInfo ) != CE_None ) + { + CPLFree( pszCoverName ); + CPLFree( psInfo ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the extents. */ +/* -------------------------------------------------------------------- */ + if( AIGReadBounds( pszCoverName, psInfo ) != CE_None ) + { + AIGClose( psInfo ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Compute the number of pixels and lines, and the number of */ +/* tile files. */ +/* -------------------------------------------------------------------- */ + if (psInfo->dfCellSizeX <= 0 || psInfo->dfCellSizeY <= 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Illegal cell size : %f x %f", + psInfo->dfCellSizeX, psInfo->dfCellSizeY ); + AIGClose( psInfo ); + return NULL; + } + + psInfo->nPixels = (int) + ((psInfo->dfURX - psInfo->dfLLX + 0.5 * psInfo->dfCellSizeX) + / psInfo->dfCellSizeX); + psInfo->nLines = (int) + ((psInfo->dfURY - psInfo->dfLLY + 0.5 * psInfo->dfCellSizeY) + / psInfo->dfCellSizeY); + + if (psInfo->nPixels <= 0 || psInfo->nLines <= 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid raster dimensions : %d x %d", + psInfo->nPixels, psInfo->nLines ); + AIGClose( psInfo ); + return NULL; + } + + if (psInfo->nBlockXSize <= 0 || psInfo->nBlockYSize <= 0 || + psInfo->nBlocksPerRow <= 0 || psInfo->nBlocksPerColumn <= 0 || + psInfo->nBlockXSize > INT_MAX / psInfo->nBlocksPerRow || + psInfo->nBlockYSize > INT_MAX / psInfo->nBlocksPerColumn) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid block characteristics: nBlockXSize=%d, " + "nBlockYSize=%d, nBlocksPerRow=%d, nBlocksPerColumn=%d", + psInfo->nBlockXSize, psInfo->nBlockYSize, + psInfo->nBlocksPerRow, psInfo->nBlocksPerColumn); + AIGClose( psInfo ); + return NULL; + } + + psInfo->nTileXSize = psInfo->nBlockXSize * psInfo->nBlocksPerRow; + psInfo->nTileYSize = psInfo->nBlockYSize * psInfo->nBlocksPerColumn; + + psInfo->nTilesPerRow = (psInfo->nPixels-1) / psInfo->nTileXSize + 1; + psInfo->nTilesPerColumn = (psInfo->nLines-1) / psInfo->nTileYSize + 1; + + if (psInfo->nTilesPerRow > INT_MAX / psInfo->nTilesPerColumn) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Too many tiles"); + AIGClose( psInfo ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Setup tile infos, but defer reading of tile data. */ +/* -------------------------------------------------------------------- */ + psInfo->pasTileInfo = (AIGTileInfo *) + VSICalloc(sizeof(AIGTileInfo), + psInfo->nTilesPerRow * psInfo->nTilesPerColumn); + if (psInfo->pasTileInfo == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate tile info array"); + AIGClose( psInfo ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the statistics. */ +/* -------------------------------------------------------------------- */ + if( AIGReadStatistics( pszCoverName, psInfo ) != CE_None ) + { + AIGClose( psInfo ); + return NULL; + } + + return( psInfo ); +} + +/************************************************************************/ +/* AIGAccessTile() */ +/************************************************************************/ + +CPLErr AIGAccessTile( AIGInfo_t *psInfo, int iTileX, int iTileY ) + +{ + char szBasename[20]; + char *pszFilename; + AIGTileInfo *psTInfo; + +/* -------------------------------------------------------------------- */ +/* Identify our tile. */ +/* -------------------------------------------------------------------- */ + if( iTileX < 0 || iTileX >= psInfo->nTilesPerRow + || iTileY < 0 || iTileY >= psInfo->nTilesPerColumn ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + psTInfo = psInfo->pasTileInfo + iTileX + iTileY * psInfo->nTilesPerRow; + + if( psTInfo->fpGrid != NULL || psTInfo->bTriedToLoad ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Compute the basename. */ +/* -------------------------------------------------------------------- */ + if( iTileY == 0 ) + sprintf( szBasename, "w%03d001", iTileX + 1 ); + else if( iTileY == 1 ) + sprintf( szBasename, "w%03d000", iTileX + 1 ); + else + sprintf( szBasename, "z%03d%03d", iTileX + 1, iTileY - 1 ); + +/* -------------------------------------------------------------------- */ +/* Open the file w001001.adf file itself. */ +/* -------------------------------------------------------------------- */ + pszFilename = (char *) CPLMalloc(strlen(psInfo->pszCoverName)+40); + sprintf( pszFilename, "%s/%s.adf", psInfo->pszCoverName, szBasename ); + + psTInfo->fpGrid = AIGLLOpen( pszFilename, "rb" ); + psTInfo->bTriedToLoad = TRUE; + + if( psTInfo->fpGrid == NULL ) + { + CPLError( CE_Warning, CPLE_OpenFailed, + "Failed to open grid file, assuming region is nodata:\n%s\n", + pszFilename ); + CPLFree( pszFilename ); + return CE_Warning; + } + + CPLFree( pszFilename ); + pszFilename = NULL; + +/* -------------------------------------------------------------------- */ +/* Read the block index file. */ +/* -------------------------------------------------------------------- */ + return AIGReadBlockIndex( psInfo, psTInfo, szBasename ); +} + +/************************************************************************/ +/* AIGReadTile() */ +/************************************************************************/ + +CPLErr AIGReadTile( AIGInfo_t * psInfo, int nBlockXOff, int nBlockYOff, + GInt32 *panData ) + +{ + int nBlockID; + CPLErr eErr; + int iTileX, iTileY; + AIGTileInfo *psTInfo; + +/* -------------------------------------------------------------------- */ +/* Compute our tile, and ensure it is accessable (open). Then */ +/* reduce block x/y values to be the block within that tile. */ +/* -------------------------------------------------------------------- */ + iTileX = nBlockXOff / psInfo->nBlocksPerRow; + iTileY = nBlockYOff / psInfo->nBlocksPerColumn; + + eErr = AIGAccessTile( psInfo, iTileX, iTileY ); + if( eErr == CE_Failure ) + return eErr; + + psTInfo = psInfo->pasTileInfo + iTileX + iTileY * psInfo->nTilesPerRow; + + nBlockXOff -= iTileX * psInfo->nBlocksPerRow; + nBlockYOff -= iTileY * psInfo->nBlocksPerColumn; + +/* -------------------------------------------------------------------- */ +/* Request for tile from a file which does not exist - treat as */ +/* all nodata. */ +/* -------------------------------------------------------------------- */ + if( psTInfo->fpGrid == NULL ) + { + int i; + for( i = psInfo->nBlockXSize * psInfo->nBlockYSize - 1; i >= 0; i-- ) + panData[i] = ESRI_GRID_NO_DATA; + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* validate block id. */ +/* -------------------------------------------------------------------- */ + nBlockID = nBlockXOff + nBlockYOff * psInfo->nBlocksPerRow; + if( nBlockID < 0 + || nBlockID >= psInfo->nBlocksPerRow * psInfo->nBlocksPerColumn ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Illegal block requested." ); + return CE_Failure; + } + + if( nBlockID >= psTInfo->nBlocks ) + { + int i; + CPLDebug( "AIG", + "Request legal block, but from beyond end of block map.\n" + "Assuming all nodata." ); + for( i = psInfo->nBlockXSize * psInfo->nBlockYSize - 1; i >= 0; i-- ) + panData[i] = ESRI_GRID_NO_DATA; + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Read block. */ +/* -------------------------------------------------------------------- */ + eErr = AIGReadBlock( psTInfo->fpGrid, + psTInfo->panBlockOffset[nBlockID], + psTInfo->panBlockSize[nBlockID], + psInfo->nBlockXSize, psInfo->nBlockYSize, + panData, psInfo->nCellType, psInfo->bCompressed ); + +/* -------------------------------------------------------------------- */ +/* Apply floating point post-processing. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && psInfo->nCellType == AIG_CELLTYPE_FLOAT ) + { + float *pafData = (float *) panData; + int i, nPixels = psInfo->nBlockXSize * psInfo->nBlockYSize; + + for( i = 0; i < nPixels; i++ ) + { + panData[i] = (int) pafData[i]; + } + } + + return( eErr ); +} + +/************************************************************************/ +/* AIGReadFloatTile() */ +/************************************************************************/ + +CPLErr AIGReadFloatTile( AIGInfo_t * psInfo, int nBlockXOff, int nBlockYOff, + float *pafData ) + +{ + int nBlockID; + CPLErr eErr; + int iTileX, iTileY; + AIGTileInfo *psTInfo; + +/* -------------------------------------------------------------------- */ +/* Compute our tile, and ensure it is accessable (open). Then */ +/* reduce block x/y values to be the block within that tile. */ +/* -------------------------------------------------------------------- */ + iTileX = nBlockXOff / psInfo->nBlocksPerRow; + iTileY = nBlockYOff / psInfo->nBlocksPerColumn; + + eErr = AIGAccessTile( psInfo, iTileX, iTileY ); + if( eErr == CE_Failure ) + return eErr; + + psTInfo = psInfo->pasTileInfo + iTileX + iTileY * psInfo->nTilesPerRow; + + nBlockXOff -= iTileX * psInfo->nBlocksPerRow; + nBlockYOff -= iTileY * psInfo->nBlocksPerColumn; + +/* -------------------------------------------------------------------- */ +/* Request for tile from a file which does not exist - treat as */ +/* all nodata. */ +/* -------------------------------------------------------------------- */ + if( psTInfo->fpGrid == NULL ) + { + int i; + for( i = psInfo->nBlockXSize * psInfo->nBlockYSize - 1; i >= 0; i-- ) + pafData[i] = ESRI_GRID_FLOAT_NO_DATA; + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* validate block id. */ +/* -------------------------------------------------------------------- */ + nBlockID = nBlockXOff + nBlockYOff * psInfo->nBlocksPerRow; + if( nBlockID < 0 + || nBlockID >= psInfo->nBlocksPerRow * psInfo->nBlocksPerColumn ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Illegal block requested." ); + return CE_Failure; + } + + if( nBlockID >= psTInfo->nBlocks ) + { + int i; + CPLDebug( "AIG", + "Request legal block, but from beyond end of block map.\n" + "Assuming all nodata." ); + for( i = psInfo->nBlockXSize * psInfo->nBlockYSize - 1; i >= 0; i-- ) + pafData[i] = ESRI_GRID_FLOAT_NO_DATA; + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Read block. */ +/* -------------------------------------------------------------------- */ + eErr = AIGReadBlock( psTInfo->fpGrid, + psTInfo->panBlockOffset[nBlockID], + psTInfo->panBlockSize[nBlockID], + psInfo->nBlockXSize, psInfo->nBlockYSize, + (GInt32 *) pafData, psInfo->nCellType, + psInfo->bCompressed ); + +/* -------------------------------------------------------------------- */ +/* Perform integer post processing. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && psInfo->nCellType == AIG_CELLTYPE_INT ) + { + GUInt32 *panData = (GUInt32 *) pafData; + int i, nPixels = psInfo->nBlockXSize * psInfo->nBlockYSize; + + for( i = 0; i < nPixels; i++ ) + { + pafData[i] = (float) panData[i]; + } + } + + return( eErr ); +} + +/************************************************************************/ +/* AIGClose() */ +/************************************************************************/ + +void AIGClose( AIGInfo_t * psInfo ) + +{ + int nTileCount = psInfo->nTilesPerRow * psInfo->nTilesPerColumn; + int iTile; + + for( iTile = 0; iTile < nTileCount; iTile++ ) + { + if( psInfo->pasTileInfo[iTile].fpGrid ) + { + VSIFCloseL( psInfo->pasTileInfo[iTile].fpGrid ); + + CPLFree( psInfo->pasTileInfo[iTile].panBlockOffset ); + CPLFree( psInfo->pasTileInfo[iTile].panBlockSize ); + } + } + + CPLFree( psInfo->pasTileInfo ); + CPLFree( psInfo->pszCoverName ); + CPLFree( psInfo ); +} + +/************************************************************************/ +/* AIGLLOpen() */ +/* */ +/* Low level fopen() replacement that will try provided, and */ +/* upper cased versions of file names. */ +/************************************************************************/ + +VSILFILE *AIGLLOpen( const char *pszFilename, const char *pszAccess ) + +{ + VSILFILE *fp; + + fp = VSIFOpenL( pszFilename, pszAccess ); + if( fp == NULL ) + { + char *pszUCFilename = CPLStrdup(pszFilename); + int i; + + for( i = strlen(pszUCFilename)-1; + pszUCFilename[i] != '/' && pszUCFilename[i] != '\\'; + i-- ) + { + pszUCFilename[i] = (char) toupper(pszUCFilename[i]); + } + + fp = VSIFOpenL( pszUCFilename, pszAccess ); + + CPLFree( pszUCFilename ); + } + + return fp; +} + diff --git a/bazaar/plugin/gdal/frmts/aigrid/aigrid.h b/bazaar/plugin/gdal/frmts/aigrid/aigrid.h new file mode 100644 index 000000000..db645ba6c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aigrid/aigrid.h @@ -0,0 +1,135 @@ +/****************************************************************************** + * $Id: aigrid.h 22159 2011-04-14 18:18:54Z warmerdam $ + * + * Project: Arc/Info Binary Grid Translator + * Purpose: Grid file access include file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _AIGRID_H_INCLUDED +#define _AIGRID_H_INCLUDED + +#include "cpl_conv.h" + +CPL_C_START + +#define ESRI_GRID_NO_DATA -2147483647 +/*#define ESRI_GRID_FLOAT_NO_DATA -340282306073709652508363335590014353408.0 */ +#define ESRI_GRID_FLOAT_NO_DATA -340282346638528859811704183484516925440.0 + +/* ==================================================================== */ +/* Grid Instance */ +/* ==================================================================== */ + +typedef struct { + int nBlocks; + GUInt32 *panBlockOffset; + int *panBlockSize; + + VSILFILE *fpGrid; /* the w001001.adf file */ + int bTriedToLoad; +} AIGTileInfo; + +typedef struct { + /* Private information */ + + AIGTileInfo *pasTileInfo; + + int bHasWarned; + + /* public information */ + + char *pszCoverName; /* path of coverage directory */ + + GInt32 nCellType; + GInt32 bCompressed; + +#define AIG_CELLTYPE_INT 1 +#define AIG_CELLTYPE_FLOAT 2 + + GInt32 nBlockXSize; + GInt32 nBlockYSize; + + GInt32 nBlocksPerRow; + GInt32 nBlocksPerColumn; + + int nTileXSize; + int nTileYSize; + + int nTilesPerRow; + int nTilesPerColumn; + + double dfLLX; + double dfLLY; + double dfURX; + double dfURY; + + double dfCellSizeX; + double dfCellSizeY; + + int nPixels; + int nLines; + + double dfMin; + double dfMax; + double dfMean; + double dfStdDev; + +} AIGInfo_t; + +/* ==================================================================== */ +/* Private APIs */ +/* ==================================================================== */ + +CPLErr AIGAccessTile( AIGInfo_t *psInfo, int iTileX, int iTileY ); +CPLErr AIGReadBlock( VSILFILE * fp, GUInt32 nBlockOffset, int nBlockSize, + int nBlockXSize, int nBlockYSize, GInt32 * panData, + int nCellType, int bCompressed ); + +CPLErr AIGReadHeader( const char *, AIGInfo_t * ); +CPLErr AIGReadBlockIndex( AIGInfo_t *, AIGTileInfo *, + const char *pszBasename ); +CPLErr AIGReadBounds( const char *, AIGInfo_t * ); +CPLErr AIGReadStatistics( const char *, AIGInfo_t * ); + +CPLErr DecompressCCITTRLETile( unsigned char *pabySrcData, int nSrcBytes, + unsigned char *pabyDstData, int nDstBytes, + int nBlockXSize, int nBlockYSize ); + +/* ==================================================================== */ +/* Public APIs */ +/* ==================================================================== */ + +AIGInfo_t *AIGOpen( const char *, const char * ); + +CPLErr AIGReadTile( AIGInfo_t *, int, int, GInt32 * ); +CPLErr AIGReadFloatTile( AIGInfo_t *, int, int, float * ); + +void AIGClose( AIGInfo_t * ); + +VSILFILE *AIGLLOpen( const char *, const char * ); + +CPL_C_END + +#endif /* ndef _AIGRID_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/aigrid/aigrid_format.html b/bazaar/plugin/gdal/frmts/aigrid/aigrid_format.html new file mode 100644 index 000000000..09a8b583e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aigrid/aigrid_format.html @@ -0,0 +1,790 @@ + + + +Arc/Info Binary Grid Format + + + + +

Arc/Info Binary Grid Format

+ +by Frank Warmerdam +(warmerdam@pobox.com)

+ +The Arc/Info Binary Grid format is the internal working format of the +Arc/Info Grid product. It is also usable and creatable within the +spatial analyst component of ArcView. It is a tiled (blocked) format with run +length compression capable of holding raster data of up to 4 byte integers +or 4 byte floating data.

+ +This format should not be confused with the Arc/Info ASCII Grid format +which is the interchange format for grids. Files can be converted between +binary and ASCII format with the GRIDASCII and ASCIIGRID commands in +Arc/Info. This format is also different than the flat binary raster +output of the GRIDFLOAT command. The Arc/Info binary float, and ASCII +formats are also accessable from within ArcView.

+ +This format should also not be confused with what I know as ESRI BIL format. +This is really a standard ESRI way of creating a header file (.HDR) +describing the data layout a binary raster file containing raster data.

+ + + +

Version

+ +I am not sure yet how the versions work for grid files. I have been +working primarily with grid files generated by ArcView 3.x, and it's associated +gridio API. The hdr.adf files I have examined start with the string +GRID1.2 for what that's worth. Certainly the file naming conventions +seem to follow the Arc/Info 7.x conventions rather than that of earlier +versions.

+ + + +

File Set

+ +A grid coverage actually consists of a number of files. A grid normally +lives in it's own directory named after the grid. For instance, the +grid nwgrd1 lives in the directory nwgrd1, and has the following +component files:

+ +

+-rwxr--r--   1 warmerda users          32 Jan 22 16:07 nwgrd1/dblbnd.adf
+-rwxr--r--   1 warmerda users         308 Jan 22 16:07 nwgrd1/hdr.adf
+-rwxr--r--   1 warmerda users          32 Jan 22 16:07 nwgrd1/sta.adf
+-rwxr--r--   1 warmerda users        2048 Jan 22 16:07 nwgrd1/vat.adf
+-rwxr--r--   1 warmerda users      187228 Jan 22 16:07 nwgrd1/w001001.adf
+-rwxr--r--   1 warmerda users        6132 Jan 22 16:07 nwgrd1/w001001x.adf
+
+ +Sometimes datasets will also include a prj.adf files containing the projection +definition in the usual ESRI format. Grids also normally have associated +tables in the info directory. This is beyond the scope of my discussion +for now.

+ +The files have the following roles: + +

    + +
  • dblbnd.adf: Contains the bounds +(LLX, LLY, URX, URY) of the portion of utilized portion of the grid.

    + +

  • hdr.adf: This is the header, and contains +information on the tile sizes, and number of tiles in the dataset. It also +contains assorted other information I have yet to identify.

    + +

  • sta.adf: This contains raster statistics. In +particular, the raster min, max, mean and standard deviation.

    + +

  • vat.adf: This relates to the value attribute table. This is +the table corresponding integer raster values with a set of attributes. I +presume it is really just a pointer into info in a manner similar to the +pat.adf file in a vector coverage, but I haven't investigated yet.

    + +

  • w001001.adf: +This is the file containing the actual raster data. +

    + +

  • w001001x.adf: +This is an index file containing pointers to +each of the tiles in the w001001.adf raster file.

    + +

+ + + +
+ +

dblbnd.adf - Georef Bounds

+ +Fields:

+ + + + + + + + + + + + + + + + + + +
Start Byte +# of Bytes +Format +Name +Description + +
0 + 8 + MSB double + D_LLX + Lower left X (easting) of the grid. Generally -0.5 for an +ungeoreferenced grid. +
8 + 8 + MSB double + D_LLY + Lower left Y (northing) of the grid. Generally -0.5 for an +ungeoreferenced grid. +
16 + 8 + MSB double + D_URX + Upper right X (easting) of the grid. Generally #Pixels-0.5 for an +ungeoreferenced grid. +
24 + 8 + MSB double + D_URY + Upper right Y (northing) of the grid. Generally #Lines-0.5 for an +ungeoreferenced grid. +
+ +This file is always 32 bytes long. The bounds apply to the portion of the +grid that is in use, not the whole thing.

+ + + +


+ +

w001001x.adf - Tile Index

+ +This is a binary dump of the first 320 bytes of a w001001x.adf +file.

+ +

+       0: 0000270A FFFFFC14 00000000 00000000 ~~'~~~~~~~~~~~~~
+      16: 00000000 00000000 00000BFA 00000000 ~~~~~~~~~~~~~~~~
+      32: 00000000 00000000 00000000 00000000 ~~~~~~~~~~~~~~~~
+      48: 00000000 00000000 00000000 00000000 ~~~~~~~~~~~~~~~~
+      64: 00000000 00000000 00000000 00000000 ~~~~~~~~~~~~~~~~
+      80: 00000000 00000000 00000000 00000000 ~~~~~~~~~~~~~~~~
+      96: 00000000 00000032 00000202 00000235 ~~~~~~~2~~~~~~~5
+     112: 000001D4 0000040A 00000000 0000040B ~~~~~~~~~~~~~~~~
+     128: 00000000 0000040C 00000000 0000040D ~~~~~~~~~~~~~~~~
+     144: 00000000 0000040E 00000000 0000040F ~~~~~~~~~~~~~~~~
+     160: 00000000 00000410 00000202 00000613 ~~~~~~~~~~~~~~~~
+     176: 000001D4 000007E8 00000000 000007E9 ~~~~~~~~~~~~~~~~
+     192: 00000000 000007EA 00000000 000007EB ~~~~~~~~~~~~~~~~
+     208: 00000000 000007EC 00000000 000007ED ~~~~~~~~~~~~~~~~
+     224: 00000000 000007EE 00000202 000009F1 ~~~~~~~~~~~~~~~~
+     240: 000001D4 00000BC6 00000000 00000BC7 ~~~~~~~~~~~~~~~~
+     256: 00000000 00000BC8 00000000 00000BC9 ~~~~~~~~~~~~~~~~
+     272: 00000000 00000BCA 00000000 00000BCB ~~~~~~~~~~~~~~~~
+     288: 00000000 00000BCC 00000202 00000DCF ~~~~~~~~~~~~~~~~
+     304: 000001D4 00000FA4 00000000 00000FA5 ~~~~~~~~~~~~~~~~
+
+ +Fields:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Start Byte +# of Bytes +Format +Description + +
0 + 8 + + Magic Number (always hex 00 00 27 0A FF FF ** **, usually ending in FC 14, FB F8 or FC 08). +
8 + 16 + + zero fill +
24 + 4 + MSB Int32 + Size of whole file in shorts (multiply by two + to get file size in bytes). +
28 + 72 + + zero fill +
100 + t*8 + 4 + MSB Int32 + Offset to tile t in w001001.adf measured in two byte + shorts. +
104 + t*8 + 4 + MSB Int32 + Size of tile t in 2 byte shorts. + +
+ + + +


+ +

sta.adf - Raster Statistics

+ +Fields:

+ + + + + + + + + + + + + + + + + + +
Start Byte +# of Bytes +Format +Name +Description + +
0 + 8 + MSB double + SMin + Minimum value of a raster cell in this grid. +
8 + 8 + MSB double + SMax + Maximum value of a raster cell in this grid. +
16 + 8 + MSB double + SMean + Mean value of a raster cells in this grid. +
24 + 8 + MSB double + SStdDev + Standard deviation of raster cells in this grid. +
+ +This file is always 32 bytes long.

+ + + +


+ +

w001001.adf - Raster Data

+ +This is a binary dump of the first 320 bytes of a w001001.adf +file.

+ +

+       0: 0000270A FFFFFC14 00000000 00000000 ~~'~~~~~~~~~~~~~
+      16: 00000000 00000000 00016DAE 00000000 ~~~~~~~~~~m~~~~~
+      32: 00000000 00000000 00000000 00000000 ~~~~~~~~~~~~~~~~
+      48: 00000000 00000000 00000000 00000000 ~~~~~~~~~~~~~~~~
+      64: 00000000 00000000 00000000 00000000 ~~~~~~~~~~~~~~~~
+      80: 00000000 00000000 00000000 00000000 ~~~~~~~~~~~~~~~~
+      96: 00000000 02020800 00373D42 5C5A4D31 ~~~~~~~~~7=B\ZM1
+     112: 200A0108 0E1D4F89 9C9A9392 8C7E6653  ~~~~~O~~~~~~~fS
+     128: 5151596D 83919290 868A8B87 807A7A7B QQYm~~~~~~~~~zz{
+     144: 7C7A766F 64481D00 0406305F 6B6C6A5B |zvodH~~~~0_klj[
+     160: 5D53513C 2D2D2732 24293F54 40354C55 ]SQ<--'2$)?T@5LU
+     176: 67686258 514E4943 5859534A 41394D70 ghbXQNICXYSJA9Mp
+     192: 75665659 66625A63 737A848E 9090979F ufVYfbZcsz~~~~~~
+     208: 9F908C8F 8F96998E 8778685B 53536274 ~~~~~~~~~xh[SSbt
+     224: 747B838A 8A8C8F92 8D979B94 8C8D9294 t{~~~~~~~~~~~~~~
+     240: 8D8D8D8D 8C8B8989 8B8E908F 8E8E9092 ~~~~~~~~~~~~~~~~
+     256: 90929394 989C9891 92939698 9B9B9C9C ~~~~~~~~~~~~~~~~
+     272: 8E8E8F8F 8E8E8F90 898E918F 8B8A8E93 ~~~~~~~~~~~~~~~~
+     288: 8B8D9093 94918C86 838DA1BC B7CEC9B0 ~~~~~~~~~~~~~~~~
+     304: D4B0BB96 A0929E99 9797999B 9D9C9C9B ~~~~~~~~~~~~~~~~
+
+ +Fields:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Start Byte +# of Bytes +Format +Name +Description + +
0 + 8 + + RMagic + Magic Number (always hex 00 00 27 0A FF FF ** **, usually ending in FC 14, FB F8 or FC 08). +
8 + 16 + + + zero fill +
24 + 4 + MSB Int32 + RFileSize + Size of whole file in shorts (multiply by two + to get file size in bytes). +
28 + 72 + + + zero fill +
100, ... + 2 + MSB Int16 + RTileSize + Size of this tiles data measured in shorts. This matches the size +in the index file, and does not include the tile size itself. The next +tile starts 2*n+2 bytes after the start of this tile, where n +is the value of this field. +
102, ... + 1 + byte + RTileType + Tile type code indicating the organization of the following +data (integer coverages only). +
103, ... + 1 + byte + RMinSize + Number of bytes following to form the minimum value for the tile +(integer coverages only). +
104, ... + (RMinSize bytes) + MSB Int (var size) + RMin + The minimum value pixels for this tile. This number is added +to the pixel values for each pixel in this tile (integer coverages only). +I must stress that if RMinSize is less than 4 this is still a signed quantity. +For instance, if RMinSize is 2, the value is 65536 - byte0*256 - byte1 if +byte0 is > 127. +
104+RMinSize, ... + RTileSize*2 - 3 - RMinSize + variable + RTileData + The data for this tile. Format varies according to RTileType +for integer coverages. +
+

+ +The fields RTileSize, RTileType, RMinSize, RMin, and RTileData occur in the +file for each tile of data present. They are usually packed one after the +other, but this isn't necessarily guaranteed. The index file (w001001x.adf) +should be used to establish the tile locations. Note that tiles that +appear in the index file with a size of zero will appear as just two +bytes (zeros) for the RTileSize for that tile.

+ + + +

Raster Size

+ +The size of a the grid isn't as easy to deduce as one might expect. +The hdr.adf file contains the HTilesPerRow, HTilesPerColumn, +HTileXSize, and HTileYSize fields which imply a particular raster +space. However, it seems that this is created much larger than +necessary to hold the users raster data. I have created 3x1 rasters +which resulted in the standard 8x512 tiles of 256x4 pixels each.

+ +It seems that the user portion of the raster has to be computed +based on the georeferenced bounds in the dblbnd.adf file (assumed +to be anchored at the top left of the total raster space), and +the HPixelSizeX, and HPixelSizeY fields from hdr.adf.

+ + #Pixels = (D_URX - D_LRX) / HPixelSizeX

+ #Lines = (D_URY - D_LRY) / HPixelSizeY

+ +Based on this number of pixels and lines, it is possible to establish +what portion out of the top left of the raster is really of +interest. All regions outside this appear to empty tiles, +or filled with no data markers.

+ + + +

RTileType/RTileData

+ +Each tile contains HBlockXSize * HBlockYSize pixels of data. For floating +point and uncompressed integer files the data is just the tile size (in two +bytes) followed by the pixel data as 4 byte MSB order IEEE floating point +words. For compressed integer +tiles it is necessary to interpret the RTileType to establish the details +of the tile organization: + + +

RTileType = 0x00 (constant block)

+ +All pixels take the value of the RMin. Data is ignored. It appears there is +sometimes a bit of meaningless data (up to four bytes) in the block.

+ +

RTileType = 0x01 (raw 1bit data)

+ +One full tile worth of data pixel values follows the RMin field, with +1bit per pixel.

+ +

RTileType = 0x04 (raw 4bit data)

+ +One full tiles worth of data pixel values follows the RMin field, with +4 bits per pixel. The high order four bits of a byte comes before the low +order four bits.

+ +

RTileType = 0x08 (raw byte data)

+ +One full tiles worth of data pixel values (one byte per pixel) follows the +RMin field.

+ +

RTileType = 0x10 (raw 16bit data)

+ +One full tiles worth of data pixel values follows the RMin field, with +16 bits per pixel (MSB).

+ +

RTileType = 0x20 (raw 32bit data)

+ +One full tiles worth of data pixel values follows the RMin field, with +32 bits per pixel (MSB).

+ +

RTileType = 0xCF (16 bit literal runs/nodata runs)

+ +The data is organized in a series of runs. Each run starts with a marker +which should be interpreted as: + +
    +
  • Marker < 128: The marker is followed by Marker pixels of +literal data with two MSB bytes per pixel.

    + +

  • Marker > 127: The marker indicates that 256-Marker pixels +of no data pixels should be put into the output stream. No data +(other than the next marker) follows this marker. + +
+ +

RTileType = 0xD7 (literal runs/nodata runs)

+ +The data is organized in a series of runs. Each run starts with a marker +which should be interpreted as: + +
    +
  • Marker < 128: The marker is followed by Marker pixels of +literal data with one byte per pixel.

    + +

  • Marker > 127: The marker indicates that 256-Marker pixels +of no data pixels should be put into the output stream. No data +(other than the next marker) follows this marker. + +
+ +

RTileType = 0xDF (RMin runs/nodata runs)

+ +The data is organized in a series of runs. Each run starts with a marker +which should be interpreted as: + +
    +
  • Marker < 128: The marker is followed by Marker pixels of +literal data with one byte per pixel.

    + +

  • Marker > 127: The marker indicates that 256-Marker pixels +of no data pixels should be put into the output stream. No data +(other than the next marker) follows this marker. + +
+ +This is similar to 0xD7, except that the data size is zero bytes instead of +1, so only RMin values are inserted into the output stream.

+ +

RTileType = 0xE0 (run length encoded 32bit)

+ +The data is organized in a series of runs. Each run starts with a marker +which should be interpreted as a count. The four bytes following the +count should be interpreted as an MSB Int32 value. They indicate +that count pixels of value should be inserted into the output +stream.

+ +

RTileType = 0xF0 (run length encoded 16bit)

+ +The data is organized in a series of runs. Each run starts with a marker +which should be interpreted as a count. The two bytes following the +count should be interpreted as an MSB Int16 value. They indicate +that count pixels of value should be inserted into the output +stream.

+ +

RTileType = 0xFC/0xF8 (run length encoded 8bit)

+ +The data is organized in a series of runs. Each run starts with a marker +which should be interpreted as a count. The following byte is the +value. They indicate +that count pixels of value should be inserted into the output +stream.

+ +The intepretation is the same for 0xFC, and 0xF8. I believe that 0xFC has +a lower dynamic (2 bit) range than 0xF8 (4 or 8 bit).

+ +

RTileType = 0xFF (RMin CCITT RLE 1Bit)

+ +The data stream for this file is CCITT RLE (G1 fax) compressed. The +format is complex but source is provided with the sample program (derived +from libtiff) for reading it. The result of uncompressing is 1bit data so +which the RMin value should be added.

+ + + + +


+ +

hdr.adf - Header

+ +This is a binary dump of the first 308 bytes of a hdr.adf +file.

+ +

+       0: 47524944 312E3200 00000000 FFFFFFFF GRID1.2~~~~~~~~~
+      16: 00000001 00000000 0000164E 3F800000 ~~~~~~~~~~~N?~~~
+      32: 00000F00 F6180000 90060000 3603D601 ~~~~~~~~~~~~6~~~
+      48: 6403E301 01000000 7620F808 43012B03 d~~~~~~~v ~~C~+~
+      64: D6019903 E3012B03 D6019903 E301F7BF ~~~~~~+~~~~~~~~~
+      80: 00007406 6E1FC2A4 7A370D00 0B004200 ~~t~n~~~z7~~~~B~
+      96: 4E1654A4 00000000 00000000 00000000 N~T~~~~~~~~~~~~~
+     112: 34A5A89D FF0414A5 A70F0002 00000000 4~~~~~~~~~~~~~~~
+     128: 00000000 3C0B5F06 A8C05F06 08005AC0 ~~~~<~_~~~_~~~Z~
+     144: 0A00E101 36035AC0 72085F06 FAA42F3C ~~~~6~Z~r~_~~~/<
+     160: 0A001667 02000E00 A80B0200 08370200 ~~~g~~~~~~~~~7~~
+     176: 0CA00200 9C0B0200 04370200 36A0E436 ~~~~~~~~~7~~6~~6
+     192: 84000000 36A00200 5F063EA5 0883FF04 ~~~~6~~~_~>~~~~~
+     208: 00008400 00000010 BD810200 5F010000 ~~~~~~~~~~~~_~~~
+     224: 670E0000 5F01560E 4C4F0001 84008CA5 g~~~_~V~LO~~~~~~
+     240: 28008F01 1000E00A 6628F7BF 4076FF04 (~~~~~~~f(~~@v~~
+     256: 3FF00000 00000000 3FF00000 00000000 ?~~~~~~~?~~~~~~~
+     272: C08FFC00 00000000 C0A1BF00 00000000 ~~~~~~~~~~~~~~~~
+     288: 00000008 00000200 00000100 00000001 ~~~~~~~~~~~~~~~~
+     304: 00000004                            ~~~~              
+
+ +Fields:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Start Byte +# of Bytes +Format +Name +Description + +
0 + 8 + Char + HMagic + Magic Number - always "GRID1.2\0" +
8 + 8 +   +   + assorted data, I don't know the purpose. +
16 + 4 + MSB Int32 + HCellType + 1 = int cover, 2 = float cover. +
20 + 4 + MSB Int32 + CompFlag + 0 = compressed, 1 = uncompressed +
24 + 232 +   +   + assorted data, I don't know the purpose. +
256 + 8 + MSB Double + HPixelSizeX + Width of a pixel in georeferenced coordinates. Generally 1.0 +for ungeoreferenced rasters. +
264 + 8 + MSB Double + HPixelSizeY + Height of a pixel in georeferenced coordinates. Generally 1.0 +for ungeoreferenced rasters. +
272 + 8 + MSB Double + XRef + dfLLX-(nBlocksPerRow*nBlockXSize*dfCellSizeX)/2.0 +
280 + 8 + MSB Double + YRef + dfURY-(3*nBlocksPerColumn*nBlockYSize*dfCellSizeY)/2.0 +
288 + 4 + MSB Int32 + HTilesPerRow + The width of the file in tiles (often 8 for files of +less than 2K in width). +
292 + 4 + MSB Int32 + HTilesPerColumn + The height of the file in tiles. Note this may be much more than + the number of tiles actually represented in the index file.

+

296 + 4 + MSB Int32 + HTileXSize + The width of a file in pixels. Normally 256.
300 + 4 + MSB Int32 + + Unknown, usually 1. +
304 + 4 + MSB Int32 + HTileYSize + Height of a tile in pixels, usually 4. +
+ + + +


+ +

Acknowledgements

+ +I would like to thank Geosoft Inc. +for partial funding of my research into this +format. I would also like to thank:

+ +

    +
  • Kenneth R. McVay for providing the statistics file format. +
  • Noureddine Farah of ThinkSpace who dug up lots of datasets that caused +problems. +
  • Luciano Fonseca who worked out RTileType 0x01. +
  • Martin Manningham of Global Geomatics for additional problem sample files. +
  • Harry Anderson of EDX Engineering, for showing me that floating point +tiles don't have RTileType. +
  • Ian Turton for supplying a sample files demonstrating the need to be +careful with the sign of "short" RMin values. +
  • Duncan Chaundy at PCI for poking hard till I finally deduced 0xFF tiles. +
  • Stephen Cheeseman of GeoSoft for yet more problem files. +
  • Geoffrey Williams for a files demonstrating tile type 0x20. +
+ + + diff --git a/bazaar/plugin/gdal/frmts/aigrid/aitest.c b/bazaar/plugin/gdal/frmts/aigrid/aitest.c new file mode 100644 index 000000000..a966ddc81 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aigrid/aitest.c @@ -0,0 +1,246 @@ +/****************************************************************************** + * $Id: aitest.c 28039 2014-11-30 18:24:59Z rouault $ + * + * Project: Arc/Info Binary Grid Translator + * Purpose: Test mainline for examining AIGrid files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "aigrid.h" + +CPL_CVSID("$Id: aitest.c 28039 2014-11-30 18:24:59Z rouault $"); + +/************************************************************************/ +/* DumpMagic() */ +/* */ +/* Dump the magic ``block type byte'' for each existing block. */ +/************************************************************************/ + +static void DumpMagic( AIGInfo_t * psInfo, int bVerbose ) + +{ + int i; + AIGTileInfo *psTInfo = psInfo->pasTileInfo + 0; + + for( i = 0; i < psTInfo->nBlocks; i++ ) + { + GByte byMagic; + int bReport = bVerbose; + unsigned char abyBlockSize[2]; + const char *pszMessage = ""; + + if( psTInfo->panBlockSize[i] == 0 ) + continue; + + VSIFSeekL( psTInfo->fpGrid, psTInfo->panBlockOffset[i], SEEK_SET ); + VSIFReadL( abyBlockSize, 2, 1, psTInfo->fpGrid ); + + if( psInfo->nCellType == AIG_CELLTYPE_INT && psInfo->bCompressed ) + { + VSIFReadL( &byMagic, 1, 1, psTInfo->fpGrid ); + + if( byMagic != 0 && byMagic != 0x43 && byMagic != 0x04 + && byMagic != 0x08 && byMagic != 0x10 && byMagic != 0xd7 + && byMagic != 0xdf && byMagic != 0xe0 && byMagic != 0xfc + && byMagic != 0xf8 && byMagic != 0xff && byMagic != 0x41 + && byMagic != 0x40 && byMagic != 0x42 && byMagic != 0xf0 + && byMagic != 0xcf && byMagic != 0x01 ) + { + pszMessage = "(unhandled magic number)"; + bReport = TRUE; + } + + if( byMagic == 0 && psTInfo->panBlockSize[i] > 8 ) + { + pszMessage = "(wrong size for 0x00 block, should be 8 bytes)"; + bReport = TRUE; + } + + if( (abyBlockSize[0] * 256 + abyBlockSize[1])*2 != + psTInfo->panBlockSize[i] ) + { + pszMessage = "(block size in data doesn't match index)"; + bReport = TRUE; + } + } + else + { + if( psTInfo->panBlockSize[i] != + psInfo->nBlockXSize*psInfo->nBlockYSize*sizeof(float) ) + { + pszMessage = "(floating point block size is wrong)"; + bReport = TRUE; + } + } + + if( bReport ) + { + printf( " %02x %5d %5d @ %d %s\n", byMagic, i, + psTInfo->panBlockSize[i], + psTInfo->panBlockOffset[i], + pszMessage ); + } + } +} + + +/************************************************************************/ +/* main() */ +/************************************************************************/ + +int main( int argc, char ** argv ) + +{ + AIGInfo_t *psInfo; + GInt32 *panRaster; + int i, j; + int bMagic = FALSE, bSuppressMagic = FALSE; + int iTestTileX = 0, iTestTileY = 0; + +/* -------------------------------------------------------------------- */ +/* Process arguments. */ +/* -------------------------------------------------------------------- */ + while( argc > 1 && argv[1][0] == '-' ) + { + if( EQUAL(argv[1],"-magic") ) + bMagic = TRUE; + + else if( EQUAL(argv[1],"-nomagic") ) + bSuppressMagic = TRUE; + + else if( EQUAL(argv[1],"-t") && argc > 2 ) + { + iTestTileX = atoi(argv[2]); + iTestTileY = atoi(argv[3]); + argc -= 2; + argv += 2; + } + + argc--; + argv++; + } + + if( argc < 2 ) { + printf( "Usage: aitest [-magic] coverage [block numbers...]\n" ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Open dataset. */ +/* -------------------------------------------------------------------- */ + psInfo = AIGOpen( argv[1], "r" ); + if( psInfo == NULL ) + exit( 1 ); + + AIGAccessTile( psInfo, iTestTileX, iTestTileY ); + +/* -------------------------------------------------------------------- */ +/* Dump general information */ +/* -------------------------------------------------------------------- */ + printf( "%d pixels x %d lines.\n", psInfo->nPixels, psInfo->nLines ); + printf( "Lower Left = (%f,%f) Upper Right = (%f,%f)\n", + psInfo->dfLLX, + psInfo->dfLLY, + psInfo->dfURX, + psInfo->dfURY ); + + if( psInfo->nCellType == AIG_CELLTYPE_INT ) + printf( "%s Integer coverage, %dx%d blocks.\n", + psInfo->bCompressed ? "Compressed" : "Uncompressed", + psInfo->nBlockXSize, psInfo->nBlockYSize ); + else + printf( "%s Floating point coverage, %dx%d blocks.\n", + psInfo->bCompressed ? "Compressed" : "Uncompressed", + psInfo->nBlockXSize, psInfo->nBlockYSize ); + + printf( "Stats - Min=%f, Max=%f, Mean=%f, StdDev=%f\n", + psInfo->dfMin, + psInfo->dfMax, + psInfo->dfMean, + psInfo->dfStdDev ); + +/* -------------------------------------------------------------------- */ +/* Do we want a dump of all the ``magic'' numbers for */ +/* instantated blocks? */ +/* -------------------------------------------------------------------- */ + if( !bSuppressMagic ) + DumpMagic( psInfo, bMagic ); + +/* -------------------------------------------------------------------- */ +/* Read a block, and report it's contents. */ +/* -------------------------------------------------------------------- */ + panRaster = (GInt32 *) + CPLMalloc(psInfo->nBlockXSize * psInfo->nBlockYSize * 4); + + while( argc > 2 && (atoi(argv[2]) > 0 || argv[2][0] == '0') ) + { + int nBlock = atoi(argv[2]); + CPLErr eErr; + AIGTileInfo *psTInfo = psInfo->pasTileInfo + 0; + + argv++; + argc--; + + eErr = AIGReadBlock( psTInfo->fpGrid, + psTInfo->panBlockOffset[nBlock], + psTInfo->panBlockSize[nBlock], + psInfo->nBlockXSize, psInfo->nBlockYSize, + panRaster, psInfo->nCellType, psInfo->bCompressed); + + printf( "\nBlock %d:\n", nBlock ); + + if( eErr != CE_None ) + { + printf( " Error! Skipping block.\n" ); + continue; + } + + for( j = 0; j < psInfo->nBlockYSize; j++ ) + { + for( i = 0; i < psInfo->nBlockXSize; i++ ) + { + if( i > 18 ) + { + printf( "..." ); + break; + } + + if( panRaster[i+j*psInfo->nBlockXSize] == ESRI_GRID_NO_DATA ) + printf( "-*- " ); + else if( psInfo->nCellType == AIG_CELLTYPE_FLOAT ) + printf( "%f ", + ((float *) panRaster)[i+j*psInfo->nBlockXSize] ); + else + printf( "%3d ", panRaster[i+j*psInfo->nBlockXSize] ); + } + printf( "\n" ); + } + } + + CPLFree( panRaster ); + + AIGClose( psInfo ); + + exit( 0 ); +} diff --git a/bazaar/plugin/gdal/frmts/aigrid/gridlib.c b/bazaar/plugin/gdal/frmts/aigrid/gridlib.c new file mode 100644 index 000000000..b403dfd77 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aigrid/gridlib.c @@ -0,0 +1,1097 @@ +/****************************************************************************** + * $Id: gridlib.c 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: Arc/Info Binary Grid Translator + * Purpose: Grid file reading code. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "aigrid.h" + +CPL_CVSID("$Id: gridlib.c 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* AIGProcessRaw32bitFloatBlock() */ +/* */ +/* Process a block using ``00'' (32 bit) raw format. */ +/************************************************************************/ + +static +CPLErr AIGProcessRaw32BitFloatBlock( GByte *pabyCur, int nDataSize, int nMin, + int nBlockXSize, int nBlockYSize, + float * pafData ) + +{ + int i; + + (void) nMin; + if( nDataSize < nBlockXSize*nBlockYSize*4 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Block too small"); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Collect raw data. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + { + float fWork; + +#ifdef CPL_LSB + ((GByte *) &fWork)[3] = *(pabyCur++); + ((GByte *) &fWork)[2] = *(pabyCur++); + ((GByte *) &fWork)[1] = *(pabyCur++); + ((GByte *) &fWork)[0] = *(pabyCur++); +#else + ((GByte *) &fWork)[0] = *(pabyCur++); + ((GByte *) &fWork)[1] = *(pabyCur++); + ((GByte *) &fWork)[2] = *(pabyCur++); + ((GByte *) &fWork)[3] = *(pabyCur++); +#endif + + pafData[i] = fWork; + } + + return( CE_None ); +} + +/************************************************************************/ +/* AIGProcessIntConstBlock() */ +/* */ +/* Process a block using ``00'' constant 32bit integer format. */ +/************************************************************************/ + +static +CPLErr AIGProcessIntConstBlock( GByte *pabyCur, int nDataSize, int nMin, + int nBlockXSize, int nBlockYSize, + GInt32 * panData ) + +{ + int i; + + (void) pabyCur; + (void) nDataSize; + +/* -------------------------------------------------------------------- */ +/* Apply constant min value. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + panData[i] = nMin; + + return( CE_None ); +} + +/************************************************************************/ +/* AIGProcess32bitRawBlock() */ +/* */ +/* Process a block using ``20'' (thirtytwo bit) raw format. */ +/************************************************************************/ + +static +CPLErr AIGProcessRaw32BitBlock( GByte *pabyCur, int nDataSize, int nMin, + int nBlockXSize, int nBlockYSize, + GInt32 * panData ) + +{ + int i; + + if( nDataSize < nBlockXSize*nBlockYSize*4 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Block too small"); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Collect raw data. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + { + panData[i] = pabyCur[0] * 256 * 256 * 256 + + pabyCur[1] * 256 * 256 + + pabyCur[2] * 256 + + pabyCur[3] + nMin; + pabyCur += 4; + } + + return( CE_None ); +} + +/************************************************************************/ +/* AIGProcess16bitRawBlock() */ +/* */ +/* Process a block using ``10'' (sixteen bit) raw format. */ +/************************************************************************/ + +static +CPLErr AIGProcessRaw16BitBlock( GByte *pabyCur, int nDataSize, int nMin, + int nBlockXSize, int nBlockYSize, + GInt32 * panData ) + +{ + int i; + + if( nDataSize < nBlockXSize*nBlockYSize*2 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Block too small"); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Collect raw data. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + { + panData[i] = pabyCur[0] * 256 + pabyCur[1] + nMin; + pabyCur += 2; + } + + return( CE_None ); +} + +/************************************************************************/ +/* AIGProcess4BitRawBlock() */ +/* */ +/* Process a block using ``08'' raw format. */ +/************************************************************************/ + +static +CPLErr AIGProcessRaw4BitBlock( GByte *pabyCur, int nDataSize, int nMin, + int nBlockXSize, int nBlockYSize, + GInt32 * panData ) + +{ + int i; + + if( nDataSize < (nBlockXSize*nBlockYSize+1)/2 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Block too small"); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Collect raw data. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + { + if( i % 2 == 0 ) + panData[i] = ((*(pabyCur) & 0xf0) >> 4) + nMin; + else + panData[i] = (*(pabyCur++) & 0xf) + nMin; + } + + return( CE_None ); +} + +/************************************************************************/ +/* AIGProcess1BitRawBlock() */ +/* */ +/* Process a block using ``0x01'' raw format. */ +/************************************************************************/ + +static +CPLErr AIGProcessRaw1BitBlock( GByte *pabyCur, int nDataSize, int nMin, + int nBlockXSize, int nBlockYSize, + GInt32 * panData ) + +{ + int i; + + if( nDataSize < (nBlockXSize*nBlockYSize+7)/8 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Block too small"); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Collect raw data. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + { + if( pabyCur[i>>3] & (0x80 >> (i&0x7)) ) + panData[i] = 1 + nMin; + else + panData[i] = 0 + nMin; + } + + return( CE_None ); +} + +/************************************************************************/ +/* AIGProcessRawBlock() */ +/* */ +/* Process a block using ``08'' raw format. */ +/************************************************************************/ + +static +CPLErr AIGProcessRawBlock( GByte *pabyCur, int nDataSize, int nMin, + int nBlockXSize, int nBlockYSize, GInt32 * panData ) + +{ + int i; + + if( nDataSize < nBlockXSize*nBlockYSize ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Block too small"); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Collect raw data. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + { + panData[i] = *(pabyCur++) + nMin; + } + + return( CE_None ); +} + +/************************************************************************/ +/* AIGProcessFFBlock() */ +/* */ +/* Process a type 0xFF (CCITT RLE) compressed block. */ +/************************************************************************/ + +static +CPLErr AIGProcessFFBlock( GByte *pabyCur, int nDataSize, int nMin, + int nBlockXSize, int nBlockYSize, + GInt32 * panData ) + +{ +/* -------------------------------------------------------------------- */ +/* Convert CCITT compress bitstream into 1bit raw data. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + int i, nDstBytes = (nBlockXSize * nBlockYSize + 7) / 8; + unsigned char *pabyIntermediate; + + pabyIntermediate = (unsigned char *) VSIMalloc(nDstBytes); + if (pabyIntermediate == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate %d bytes", nDstBytes); + return CE_Failure; + } + + eErr = DecompressCCITTRLETile( pabyCur, nDataSize, + pabyIntermediate, nDstBytes, + nBlockXSize, nBlockYSize ); + if( eErr != CE_None ) + { + CPLFree(pabyIntermediate); + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Convert the bit buffer into 32bit integers and account for */ +/* nMin. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + { + if( pabyIntermediate[i>>3] & (0x80 >> (i&0x7)) ) + panData[i] = nMin+1; + else + panData[i] = nMin; + } + + CPLFree( pabyIntermediate ); + + return( CE_None ); +} + + + +/************************************************************************/ +/* AIGProcessBlock() */ +/* */ +/* Process a block using ``D7'', ``E0'' or ``DF'' compression. */ +/************************************************************************/ + +static +CPLErr AIGProcessBlock( GByte *pabyCur, int nDataSize, int nMin, int nMagic, + int nBlockXSize, int nBlockYSize, GInt32 * panData ) + +{ + int nTotPixels, nPixels; + int i; + +/* ==================================================================== */ +/* Process runs till we are done. */ +/* ==================================================================== */ + nTotPixels = nBlockXSize * nBlockYSize; + nPixels = 0; + + while( nPixels < nTotPixels && nDataSize > 0 ) + { + int nMarker = *(pabyCur++); + + nDataSize--; + +/* -------------------------------------------------------------------- */ +/* Repeat data - four byte data block (0xE0) */ +/* -------------------------------------------------------------------- */ + if( nMagic == 0xE0 ) + { + GInt32 nValue; + + if( nMarker + nPixels > nTotPixels ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Run too long in AIGProcessBlock, needed %d values, got %d.", + nTotPixels - nPixels, nMarker ); + return CE_Failure; + } + + if( nDataSize < 4 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Block too small"); + return CE_Failure; + } + + nValue = 0; + memcpy( &nValue, pabyCur, 4 ); + pabyCur += 4; + nDataSize -= 4; + + nValue = CPL_MSBWORD32( nValue ); + + nValue += nMin; + for( i = 0; i < nMarker; i++ ) + panData[nPixels++] = nValue; + } + +/* -------------------------------------------------------------------- */ +/* Repeat data - two byte data block (0xF0) */ +/* -------------------------------------------------------------------- */ + else if( nMagic == 0xF0 ) + { + GInt32 nValue; + + if( nMarker + nPixels > nTotPixels ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Run too long in AIGProcessBlock, needed %d values, got %d.", + nTotPixels - nPixels, nMarker ); + return CE_Failure; + } + + if( nDataSize < 2 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Block too small"); + return CE_Failure; + } + + nValue = (pabyCur[0] * 256 + pabyCur[1]) + nMin; + pabyCur += 2; + nDataSize -= 2; + + for( i = 0; i < nMarker; i++ ) + panData[nPixels++] = nValue; + } + +/* -------------------------------------------------------------------- */ +/* Repeat data - one byte data block (0xFC) */ +/* -------------------------------------------------------------------- */ + else if( nMagic == 0xFC || nMagic == 0xF8 ) + { + GInt32 nValue; + + if( nMarker + nPixels > nTotPixels ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Run too long in AIGProcessBlock, needed %d values, got %d.", + nTotPixels - nPixels, nMarker ); + return CE_Failure; + } + + if( nDataSize < 1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Block too small"); + return CE_Failure; + } + + nValue = *(pabyCur++) + nMin; + nDataSize--; + + for( i = 0; i < nMarker; i++ ) + panData[nPixels++] = nValue; + } + +/* -------------------------------------------------------------------- */ +/* Repeat data - no actual data, just assign minimum (0xDF) */ +/* -------------------------------------------------------------------- */ + else if( nMagic == 0xDF && nMarker < 128 ) + { + if( nMarker + nPixels > nTotPixels ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Run too long in AIGProcessBlock, needed %d values, got %d.", + nTotPixels - nPixels, nMarker ); + return CE_Failure; + } + + for( i = 0; i < nMarker; i++ ) + panData[nPixels++] = nMin; + } + +/* -------------------------------------------------------------------- */ +/* Literal data (0xD7): 8bit values. */ +/* -------------------------------------------------------------------- */ + else if( nMagic == 0xD7 && nMarker < 128 ) + { + if( nMarker + nPixels > nTotPixels ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Run too long in AIGProcessBlock, needed %d values, got %d.", + nTotPixels - nPixels, nMarker ); + return CE_Failure; + } + + while( nMarker > 0 && nDataSize > 0 ) + { + panData[nPixels++] = *(pabyCur++) + nMin; + nMarker--; + nDataSize--; + } + } + +/* -------------------------------------------------------------------- */ +/* Literal data (0xCF): 16 bit values. */ +/* -------------------------------------------------------------------- */ + else if( nMagic == 0xCF && nMarker < 128 ) + { + GInt32 nValue; + + if( nMarker + nPixels > nTotPixels ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Run too long in AIGProcessBlock, needed %d values, got %d.", + nTotPixels - nPixels, nMarker ); + return CE_Failure; + } + + while( nMarker > 0 && nDataSize >= 2 ) + { + nValue = pabyCur[0] * 256 + pabyCur[1] + nMin; + panData[nPixels++] = nValue; + pabyCur += 2; + + nMarker--; + nDataSize -= 2; + } + } + +/* -------------------------------------------------------------------- */ +/* Nodata repeat */ +/* -------------------------------------------------------------------- */ + else if( nMarker > 128 ) + { + nMarker = 256 - nMarker; + + if( nMarker + nPixels > nTotPixels ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Run too long in AIGProcessBlock, needed %d values, got %d.", + nTotPixels - nPixels, nMarker ); + return CE_Failure; + } + + while( nMarker > 0 ) + { + panData[nPixels++] = ESRI_GRID_NO_DATA; + nMarker--; + } + } + + else + { + return CE_Failure; + } + + } + + if( nPixels < nTotPixels || nDataSize < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Ran out of data processing block with nMagic=%d.", + nMagic ); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* AIGReadBlock() */ +/* */ +/* Read a single block of integer grid data. */ +/************************************************************************/ + +CPLErr AIGReadBlock( VSILFILE * fp, GUInt32 nBlockOffset, int nBlockSize, + int nBlockXSize, int nBlockYSize, + GInt32 *panData, int nCellType, int bCompressed ) + +{ + GByte *pabyRaw, *pabyCur; + CPLErr eErr; + int i, nMagic, nMinSize=0, nDataSize; + GInt32 nMin = 0; + +/* -------------------------------------------------------------------- */ +/* If the block has zero size it is all dummies. */ +/* -------------------------------------------------------------------- */ + if( nBlockSize == 0 ) + { + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + panData[i] = ESRI_GRID_NO_DATA; + + return( CE_None ); + } + +/* -------------------------------------------------------------------- */ +/* Read the block into memory. */ +/* -------------------------------------------------------------------- */ + if (nBlockSize <= 0 || nBlockSize > 65535 * 2) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid block size : %d", nBlockSize); + return CE_Failure; + } + + pabyRaw = (GByte *) VSIMalloc(nBlockSize+2); + if (pabyRaw == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocate memory for block"); + return CE_Failure; + } + + if( VSIFSeekL( fp, nBlockOffset, SEEK_SET ) != 0 + || VSIFReadL( pabyRaw, nBlockSize+2, 1, fp ) != 1 ) + { + memset( panData, 0, nBlockXSize*nBlockYSize*4 ); + CPLError( CE_Failure, CPLE_AppDefined, + "Read of %d bytes from offset %d for grid block failed.", + nBlockSize+2, nBlockOffset ); + CPLFree( pabyRaw ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Verify the block size. */ +/* -------------------------------------------------------------------- */ + if( nBlockSize != (pabyRaw[0]*256 + pabyRaw[1])*2 ) + { + memset( panData, 0, nBlockXSize*nBlockYSize*4 ); + CPLError( CE_Failure, CPLE_AppDefined, + "Block is corrupt, block size was %d, but expected to be %d.", + (pabyRaw[0]*256 + pabyRaw[1])*2, nBlockSize ); + CPLFree( pabyRaw ); + return CE_Failure; + } + + nDataSize = nBlockSize; + +/* -------------------------------------------------------------------- */ +/* Handle float files and uncompressed integer files directly. */ +/* -------------------------------------------------------------------- */ + if( nCellType == AIG_CELLTYPE_FLOAT ) + { + AIGProcessRaw32BitFloatBlock( pabyRaw + 2, nDataSize, 0, + nBlockXSize, nBlockYSize, + (float *) panData ); + CPLFree( pabyRaw ); + + return CE_None; + } + + if( nCellType == AIG_CELLTYPE_INT && !bCompressed ) + { + AIGProcessRaw32BitBlock( pabyRaw+2, nDataSize, nMin, + nBlockXSize, nBlockYSize, + panData ); + CPLFree( pabyRaw ); + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Collect minimum value. */ +/* -------------------------------------------------------------------- */ + + /* The first 2 bytes that give the block size are not included in nDataSize */ + /* and have already been safely read */ + pabyCur = pabyRaw + 2; + + /* Need at least 2 byte to read the nMinSize and the nMagic */ + if (nDataSize < 2) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Corrupt block. Need 2 bytes to read nMagic and nMinSize, only %d available", + nDataSize); + CPLFree( pabyRaw ); + return CE_Failure; + } + nMagic = pabyCur[0]; + nMinSize = pabyCur[1]; + pabyCur += 2; + nDataSize -= 2; + + /* Need at least nMinSize bytes to read the nMin value */ + if (nDataSize < nMinSize) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Corrupt block. Need %d bytes to read nMin. Only %d available", + nMinSize, nDataSize); + CPLFree( pabyRaw ); + return CE_Failure; + } + + if( nMinSize > 4 ) + { + memset( panData, 0, nBlockXSize*nBlockYSize*4 ); + CPLError( CE_Failure, CPLE_AppDefined, + "Corrupt 'minsize' of %d in block header. Read aborted.", + nMinSize ); + CPLFree( pabyRaw ); + return CE_Failure; + } + + if( nMinSize == 4 ) + { + memcpy( &nMin, pabyCur, 4 ); + nMin = CPL_MSBWORD32( nMin ); + pabyCur += 4; + } + else + { + nMin = 0; + for( i = 0; i < nMinSize; i++ ) + { + nMin = nMin * 256 + *pabyCur; + pabyCur++; + } + + /* If nMinSize = 0, then we might have only 4 bytes in pabyRaw */ + /* don't try to read the 5th one then */ + if( nMinSize != 0 && pabyRaw[4] > 127 ) + { + if( nMinSize == 2 ) + nMin = nMin - 65536; + else if( nMinSize == 1 ) + nMin = nMin - 256; + else if( nMinSize == 3 ) + nMin = nMin - 256*256*256; + } + } + + nDataSize -= nMinSize; + +/* -------------------------------------------------------------------- */ +/* Call an apppropriate handler depending on magic code. */ +/* -------------------------------------------------------------------- */ + + if( nMagic == 0x08 ) + { + AIGProcessRawBlock( pabyCur, nDataSize, nMin, + nBlockXSize, nBlockYSize, + panData ); + } + else if( nMagic == 0x04 ) + { + AIGProcessRaw4BitBlock( pabyCur, nDataSize, nMin, + nBlockXSize, nBlockYSize, + panData ); + } + else if( nMagic == 0x01 ) + { + AIGProcessRaw1BitBlock( pabyCur, nDataSize, nMin, + nBlockXSize, nBlockYSize, + panData ); + } + else if( nMagic == 0x00 ) + { + AIGProcessIntConstBlock( pabyCur, nDataSize, nMin, + nBlockXSize, nBlockYSize, panData ); + } + else if( nMagic == 0x10 ) + { + AIGProcessRaw16BitBlock( pabyCur, nDataSize, nMin, + nBlockXSize, nBlockYSize, + panData ); + } + else if( nMagic == 0x20 ) + { + AIGProcessRaw32BitBlock( pabyCur, nDataSize, nMin, + nBlockXSize, nBlockYSize, + panData ); + } + else if( nMagic == 0xFF ) + { + AIGProcessFFBlock( pabyCur, nDataSize, nMin, + nBlockXSize, nBlockYSize, + panData ); + } + else + { + eErr = AIGProcessBlock( pabyCur, nDataSize, nMin, nMagic, + nBlockXSize, nBlockYSize, panData ); + + if( eErr == CE_Failure ) + { + static int bHasWarned = FALSE; + + for( i = 0; i < nBlockXSize * nBlockYSize; i++ ) + panData[i] = ESRI_GRID_NO_DATA; + + if( !bHasWarned ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unsupported Arc/Info Binary Grid tile of type 0x%X" + " encountered.\n" + "This and subsequent unsupported tile types set to" + " no data value.\n", + nMagic ); + bHasWarned = TRUE; + } + } + } + + CPLFree( pabyRaw ); + + return CE_None; +} + +/************************************************************************/ +/* AIGReadHeader() */ +/* */ +/* Read the hdr.adf file, and populate the given info structure */ +/* appropriately. */ +/************************************************************************/ + +CPLErr AIGReadHeader( const char * pszCoverName, AIGInfo_t * psInfo ) + +{ + char *pszHDRFilename; + VSILFILE *fp; + GByte abyData[308]; + +/* -------------------------------------------------------------------- */ +/* Open the file hdr.adf file. */ +/* -------------------------------------------------------------------- */ + pszHDRFilename = (char *) CPLMalloc(strlen(pszCoverName)+30); + sprintf( pszHDRFilename, "%s/hdr.adf", pszCoverName ); + + fp = AIGLLOpen( pszHDRFilename, "rb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open grid header file:\n%s\n", pszHDRFilename ); + + CPLFree( pszHDRFilename ); + return( CE_Failure ); + } + + CPLFree( pszHDRFilename ); + +/* -------------------------------------------------------------------- */ +/* Read the whole file (we expect it to always be 308 bytes */ +/* long. */ +/* -------------------------------------------------------------------- */ + + VSIFReadL( abyData, 1, 308, fp ); + + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Read the block size information. */ +/* -------------------------------------------------------------------- */ + memcpy( &(psInfo->nCellType), abyData+16, 4 ); + memcpy( &(psInfo->bCompressed), abyData+20, 4 ); + memcpy( &(psInfo->nBlocksPerRow), abyData+288, 4 ); + memcpy( &(psInfo->nBlocksPerColumn), abyData+292, 4 ); + memcpy( &(psInfo->nBlockXSize), abyData+296, 4 ); + memcpy( &(psInfo->nBlockYSize), abyData+304, 4 ); + memcpy( &(psInfo->dfCellSizeX), abyData+256, 8 ); + memcpy( &(psInfo->dfCellSizeY), abyData+264, 8 ); + +#ifdef CPL_LSB + psInfo->nCellType = CPL_SWAP32( psInfo->nCellType ); + psInfo->bCompressed = CPL_SWAP32( psInfo->bCompressed ); + psInfo->nBlocksPerRow = CPL_SWAP32( psInfo->nBlocksPerRow ); + psInfo->nBlocksPerColumn = CPL_SWAP32( psInfo->nBlocksPerColumn ); + psInfo->nBlockXSize = CPL_SWAP32( psInfo->nBlockXSize ); + psInfo->nBlockYSize = CPL_SWAP32( psInfo->nBlockYSize ); + CPL_SWAPDOUBLE( &(psInfo->dfCellSizeX) ); + CPL_SWAPDOUBLE( &(psInfo->dfCellSizeY) ); +#endif + + psInfo->bCompressed = !psInfo->bCompressed; + + return( CE_None ); +} + +/************************************************************************/ +/* AIGReadBlockIndex() */ +/* */ +/* Read the w001001x.adf file, and populate the given info */ +/* structure with the block offsets, and sizes. */ +/************************************************************************/ + +CPLErr AIGReadBlockIndex( AIGInfo_t * psInfo, AIGTileInfo *psTInfo, + const char *pszBasename ) + +{ + char *pszHDRFilename; + VSILFILE *fp; + int nLength, i; + GInt32 nValue; + GUInt32 *panIndex; + GByte abyHeader[8]; + +/* -------------------------------------------------------------------- */ +/* Open the file hdr.adf file. */ +/* -------------------------------------------------------------------- */ + pszHDRFilename = (char *) CPLMalloc(strlen(psInfo->pszCoverName)+40); + sprintf( pszHDRFilename, "%s/%sx.adf", psInfo->pszCoverName, pszBasename ); + + fp = AIGLLOpen( pszHDRFilename, "rb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open grid block index file:\n%s\n", + pszHDRFilename ); + + CPLFree( pszHDRFilename ); + return( CE_Failure ); + } + + CPLFree( pszHDRFilename ); + +/* -------------------------------------------------------------------- */ +/* Verify the magic number. This is often corrupted by CR/LF */ +/* translation. */ +/* -------------------------------------------------------------------- */ + VSIFReadL( abyHeader, 1, 8, fp ); + if( abyHeader[3] == 0x0D && abyHeader[4] == 0x0A ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "w001001x.adf file header has been corrupted by unix to dos text conversion." ); + VSIFCloseL( fp ); + return CE_Failure; + } + + if( abyHeader[0] != 0x00 + || abyHeader[1] != 0x00 + || abyHeader[2] != 0x27 + || abyHeader[3] != 0x0A + || abyHeader[4] != 0xFF + || abyHeader[5] != 0xFF ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "w001001x.adf file header magic number is corrupt." ); + VSIFCloseL( fp ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Get the file length (in 2 byte shorts) */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( fp, 24, SEEK_SET ); + VSIFReadL( &nValue, 1, 4, fp ); + + // FIXME? : risk of overflow in multiplication + nLength = CPL_MSBWORD32(nValue) * 2; + +/* -------------------------------------------------------------------- */ +/* Allocate buffer, and read the file (from beyond the header) */ +/* into the buffer. */ +/* -------------------------------------------------------------------- */ + psTInfo->nBlocks = (nLength-100) / 8; + panIndex = (GUInt32 *) VSIMalloc2(psTInfo->nBlocks, 8); + if (panIndex == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "AIGReadBlockIndex: Out of memory. Probably due to corrupted w001001x.adf file"); + VSIFCloseL( fp ); + return CE_Failure; + } + VSIFSeekL( fp, 100, SEEK_SET ); + if ((int)VSIFReadL( panIndex, 8, psTInfo->nBlocks, fp ) != psTInfo->nBlocks) + { + CPLError(CE_Failure, CPLE_AppDefined, + "AIGReadBlockIndex: Cannot read block info"); + VSIFCloseL( fp ); + CPLFree( panIndex ); + return CE_Failure; + } + + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Allocate AIGInfo block info arrays. */ +/* -------------------------------------------------------------------- */ + psTInfo->panBlockOffset = (GUInt32 *) VSIMalloc2(4, psTInfo->nBlocks); + psTInfo->panBlockSize = (int *) VSIMalloc2(4, psTInfo->nBlocks); + if (psTInfo->panBlockOffset == NULL || + psTInfo->panBlockSize == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "AIGReadBlockIndex: Out of memory. Probably due to corrupted w001001x.adf file"); + CPLFree( psTInfo->panBlockOffset ); + CPLFree( psTInfo->panBlockSize ); + CPLFree( panIndex ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Populate the block information. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTInfo->nBlocks; i++ ) + { + psTInfo->panBlockOffset[i] = CPL_MSBWORD32(panIndex[i*2]) * 2; + psTInfo->panBlockSize[i] = CPL_MSBWORD32(panIndex[i*2+1]) * 2; + } + + CPLFree( panIndex ); + + return( CE_None ); +} + +/************************************************************************/ +/* AIGReadBounds() */ +/* */ +/* Read the dblbnd.adf file for the georeferenced bounds. */ +/************************************************************************/ + +CPLErr AIGReadBounds( const char * pszCoverName, AIGInfo_t * psInfo ) + +{ + char *pszHDRFilename; + VSILFILE *fp; + double adfBound[4]; + +/* -------------------------------------------------------------------- */ +/* Open the file dblbnd.adf file. */ +/* -------------------------------------------------------------------- */ + pszHDRFilename = (char *) CPLMalloc(strlen(pszCoverName)+40); + sprintf( pszHDRFilename, "%s/dblbnd.adf", pszCoverName ); + + fp = AIGLLOpen( pszHDRFilename, "rb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open grid bounds file:\n%s\n", + pszHDRFilename ); + + CPLFree( pszHDRFilename ); + return( CE_Failure ); + } + + CPLFree( pszHDRFilename ); + +/* -------------------------------------------------------------------- */ +/* Get the contents - four doubles. */ +/* -------------------------------------------------------------------- */ + VSIFReadL( adfBound, 1, 32, fp ); + + VSIFCloseL( fp ); + +#ifdef CPL_LSB + CPL_SWAPDOUBLE(adfBound+0); + CPL_SWAPDOUBLE(adfBound+1); + CPL_SWAPDOUBLE(adfBound+2); + CPL_SWAPDOUBLE(adfBound+3); +#endif + + psInfo->dfLLX = adfBound[0]; + psInfo->dfLLY = adfBound[1]; + psInfo->dfURX = adfBound[2]; + psInfo->dfURY = adfBound[3]; + + return( CE_None ); +} + +/************************************************************************/ +/* AIGReadStatistics() */ +/* */ +/* Read the sta.adf file for the layer statistics. */ +/************************************************************************/ + +CPLErr AIGReadStatistics( const char * pszCoverName, AIGInfo_t * psInfo ) + +{ + char *pszHDRFilename; + VSILFILE *fp; + double adfStats[4]; + + psInfo->dfMin = 0.0; + psInfo->dfMax = 0.0; + psInfo->dfMean = 0.0; + psInfo->dfStdDev = 0.0; + +/* -------------------------------------------------------------------- */ +/* Open the file sta.adf file. */ +/* -------------------------------------------------------------------- */ + pszHDRFilename = (char *) CPLMalloc(strlen(pszCoverName)+40); + sprintf( pszHDRFilename, "%s/sta.adf", pszCoverName ); + + fp = AIGLLOpen( pszHDRFilename, "rb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open grid statistics file:\n%s\n", + pszHDRFilename ); + + CPLFree( pszHDRFilename ); + return( CE_Failure ); + } + + CPLFree( pszHDRFilename ); + +/* -------------------------------------------------------------------- */ +/* Get the contents - four doubles. */ +/* -------------------------------------------------------------------- */ + VSIFReadL( adfStats, 1, 32, fp ); + + VSIFCloseL( fp ); + +#ifdef CPL_LSB + CPL_SWAPDOUBLE(adfStats+0); + CPL_SWAPDOUBLE(adfStats+1); + CPL_SWAPDOUBLE(adfStats+2); + CPL_SWAPDOUBLE(adfStats+3); +#endif + + psInfo->dfMin = adfStats[0]; + psInfo->dfMax = adfStats[1]; + psInfo->dfMean = adfStats[2]; + psInfo->dfStdDev = adfStats[3]; + + return( CE_None ); +} + diff --git a/bazaar/plugin/gdal/frmts/aigrid/makefile.vc b/bazaar/plugin/gdal/frmts/aigrid/makefile.vc new file mode 100644 index 000000000..c1a517ab3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/aigrid/makefile.vc @@ -0,0 +1,21 @@ + +OBJ = aigdataset.obj aigopen.obj gridlib.obj aigccitt.obj + +EXTRAFLAGS = -I../../ogr/ogrsf_frmts/avc + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +all: $(OBJ) aitest.exe + +aitest.exe: $(OBJ) aitest.c + $(CC) $(CFLAGS) aitest.c aigopen.obj gridlib.obj \ + $(GDAL_ROOT)/port/cpl.lib + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/airsar/GNUmakefile b/bazaar/plugin/gdal/frmts/airsar/GNUmakefile new file mode 100644 index 000000000..7db8f8bbf --- /dev/null +++ b/bazaar/plugin/gdal/frmts/airsar/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = airsardataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/airsar/airsardataset.cpp b/bazaar/plugin/gdal/frmts/airsar/airsardataset.cpp new file mode 100644 index 000000000..c16a56eb4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/airsar/airsardataset.cpp @@ -0,0 +1,673 @@ +/****************************************************************************** + * $Id: airsardataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: AirSAR Reader + * Purpose: Implements read support for AirSAR Polarimetric data. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2007-2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "cpl_conv.h" +#include "cpl_vsi.h" + +CPL_CVSID("$Id: airsardataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +CPL_C_START +void GDALRegister_AirSAR(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* AirSARDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class AirSARRasterBand; + +class AirSARDataset : public GDALPamDataset +{ + friend class AirSARRasterBand; + + VSILFILE *fp; + + int nLoadedLine; + GByte *pabyCompressedLine; + double *padfMatrix; + + int nDataStart; + int nRecordLength; + + CPLErr LoadLine(int iLine); + + static char **ReadHeader( VSILFILE * fp, int nFileOffset, + const char *pszPrefix, int nMaxLines ); + + public: + AirSARDataset(); + ~AirSARDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* AirSARRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class AirSARRasterBand : public GDALPamRasterBand +{ + public: + AirSARRasterBand( AirSARDataset *, int ); + virtual ~AirSARRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/* locations of stokes matrix values within padfMatrix ... same order as they + are computed in the document. */ + +#define M11 0 +#define M12 1 +#define M13 2 +#define M14 3 +#define M23 4 +#define M24 5 +#define M33 6 +#define M34 7 +#define M44 8 +#define M22 9 + +/************************************************************************/ +/* AirSARRasterBand() */ +/************************************************************************/ + +AirSARRasterBand::AirSARRasterBand( AirSARDataset *poDS, + int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + if( this->nBand == 2 || this->nBand == 3 || this->nBand == 5 ) + eDataType = GDT_CFloat32; + else + eDataType = GDT_Float32; + + switch( nBand ) + { + case 1: + SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_11" ); + SetDescription( "Covariance_11" ); + eDataType = GDT_CFloat32; + break; + + case 2: + SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_12" ); + SetDescription( "Covariance_12" ); + eDataType = GDT_CFloat32; + break; + + case 3: + SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_13" ); + SetDescription( "Covariance_13" ); + eDataType = GDT_CFloat32; + break; + + case 4: + SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_22" ); + SetDescription( "Covariance_22" ); + eDataType = GDT_CFloat32; + break; + + case 5: + SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_23" ); + SetDescription( "Covariance_23" ); + eDataType = GDT_CFloat32; + break; + + case 6: + SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_33" ); + SetDescription( "Covariance_33" ); + eDataType = GDT_CFloat32; + break; + } +} + +/************************************************************************/ +/* ~AirSARRasterBand() */ +/************************************************************************/ + +AirSARRasterBand::~AirSARRasterBand() + +{ +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr AirSARRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + CPLErr eErr; + float *pafLine = (float *) pImage; + int iPixel; + double *padfMatrix; + + eErr = ((AirSARDataset *)poDS)->LoadLine( nBlockYOff ); + if( eErr != CE_None ) + return eErr; + + padfMatrix = ((AirSARDataset *) poDS)->padfMatrix; + +#define SQRT_2 1.4142135623730951 + + if( nBand == 1 ) /* C11 */ + { + for( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + double *m = padfMatrix + 10 * iPixel; + + pafLine[iPixel*2+0] = (float)(m[M11] + m[M22] + 2 * m[M12]); + pafLine[iPixel*2+1] = 0.0; + } + } + else if( nBand == 2 ) /* C12 */ + { + for( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + double *m = padfMatrix + 10 * iPixel; + + // real + pafLine[iPixel*2 + 0] = (float)(SQRT_2 * (m[M13] + m[M23])); + + // imaginary + pafLine[iPixel*2 + 1] = (float)(- SQRT_2 * (m[M24] + m[M14])); + } + } + else if( nBand == 3 ) /* C13 */ + { + for( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + double *m = padfMatrix + 10 * iPixel; + + // real + pafLine[iPixel*2 + 0] = (float)(2*m[M33] + m[M22] - m[M11]); + + // imaginary + pafLine[iPixel*2 + 1] = (float)(-2 * m[M34]); + } + } + else if( nBand == 4 ) /* C22 */ + { + for( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + double *m = padfMatrix + 10 * iPixel; + + pafLine[iPixel*2+0] = (float)(2 * (m[M11] - m[M22])); + pafLine[iPixel*2+1] = 0.0; + } + } + else if( nBand == 5 ) /* C23 */ + { + for( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + double *m = padfMatrix + 10 * iPixel; + + // real + pafLine[iPixel*2 + 0] = (float)(SQRT_2 * (m[M13] - m[M23])); + + // imaginary + pafLine[iPixel*2 + 1] = (float)(SQRT_2 * (m[M23] - m[M14])); + } + } + else if( nBand == 6 ) /* C33 */ + { + for( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + double *m = padfMatrix + 10 * iPixel; + + pafLine[iPixel*2+0] = (float)(m[M11] + m[M22] - 2 * m[M12]); + pafLine[iPixel*2+1] = 0.0; + } + } + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* AirSARDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* AirSARDataset() */ +/************************************************************************/ + +AirSARDataset::AirSARDataset() + +{ + fp = NULL; + + nLoadedLine = -1; + pabyCompressedLine = NULL; + padfMatrix = NULL; +} + +/************************************************************************/ +/* ~AirSARDataset() */ +/************************************************************************/ + +AirSARDataset::~AirSARDataset() + +{ + FlushCache(); + if( pabyCompressedLine != NULL ) + { + CPLFree( pabyCompressedLine ); + CPLFree( padfMatrix ); + } + + if( fp != NULL ) + { + VSIFCloseL( fp ); + fp = NULL; + } +} + +/************************************************************************/ +/* LoadLine() */ +/************************************************************************/ + +CPLErr AirSARDataset::LoadLine( int iLine ) + +{ + if( iLine == nLoadedLine ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* allocate working buffers if we don't have them already. */ +/* -------------------------------------------------------------------- */ + if( pabyCompressedLine == NULL ) + { + pabyCompressedLine = (GByte *) VSIMalloc2(nRasterXSize, 10); + + padfMatrix = (double *) VSIMalloc2(10* sizeof(double), nRasterXSize); + if (pabyCompressedLine == NULL || + padfMatrix == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "AirSARDataset::LoadLine : Out of memory. " + "Probably due to corrupted dataset (nRasterXSize = %d)", + nRasterXSize); + CPLFree (pabyCompressedLine); + CPLFree (padfMatrix); + return CE_Failure; + } + } + + CPLAssert( nRecordLength == nRasterXSize * 10 ); + +/* -------------------------------------------------------------------- */ +/* Load raw compressed data. */ +/* -------------------------------------------------------------------- */ + if( VSIFSeekL( fp, nDataStart + iLine * nRecordLength, SEEK_SET ) != 0 + || ((int) VSIFReadL( pabyCompressedLine, 10, nRasterXSize, fp )) + != nRasterXSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Error reading %d bytes for line %d at offset %d.\n%s", + nRasterXSize * 10, iLine, nDataStart + iLine * nRecordLength, + VSIStrerror( errno ) ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Build stokes matrix */ +/* -------------------------------------------------------------------- */ + for( int iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + double *M = padfMatrix + 10 * iPixel; + signed char *byte = (signed char *) pabyCompressedLine + 10*iPixel - 1; + double gen_fac = 1.0; // should we have a general scale factor? + + M[M11] = (byte[2] / 254.0 + 1.5) * pow(2.0,byte[1]) * gen_fac; + M[M12] = byte[3] * M[M11] / 127.0; + M[M13] = byte[4] * fabs((double) byte[4]) * M[M11] / (127*127); + M[M14] = byte[5] * fabs((double) byte[5]) * M[M11] / (127*127); + M[M23] = byte[6] * fabs((double) byte[6]) * M[M11] / (127*127); + M[M24] = byte[7] * fabs((double) byte[7]) * M[M11] / (127*127); + M[M33] = byte[8] * M[M11] / 127; + M[M34] = byte[9] * M[M11] / 127; + M[M44] = byte[10] * M[M11] / 127; + M[M22] = M[M11] - M[M33] - M[M44]; + } + + return CE_None; +} + +/************************************************************************/ +/* ReadHeader() */ +/* */ +/* Read the AirSAR header. We assume an equal sign seperates */ +/* the keyword name from the value. If not, assume the last */ +/* "blank delimited" word is the value and everything else is a */ +/* keyword. */ +/* */ +/* The records are 50 characters each. Read till we get an all */ +/* blank record or some zero bytes. */ +/************************************************************************/ + +char ** AirSARDataset::ReadHeader( VSILFILE * fp, int nFileOffset, + const char *pszPrefix, int nMaxLines ) + +{ + char **papszHeadInfo = NULL; + char szLine[51]; + int iLine; + + VSIFSeekL( fp, nFileOffset, SEEK_SET ); + +/* ==================================================================== */ +/* Loop collecting one line at a time. */ +/* ==================================================================== */ + for( iLine = 0; iLine < nMaxLines; iLine++ ) + { +/* -------------------------------------------------------------------- */ +/* Read a 50 byte header record. */ +/* -------------------------------------------------------------------- */ + if( VSIFReadL( szLine, 1, 50, fp ) != 50 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Read error collecting AirSAR header." ); + return NULL; + } + + szLine[50] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Is it all spaces, or does it have a zero byte? */ +/* -------------------------------------------------------------------- */ + int bAllSpaces = TRUE; + int bHasIllegalChars = FALSE; + int i; + + for( i = 0; i < 50; i++ ) + { + if( szLine[i] == '\0' ) + break; + + if( szLine[i] != ' ' ) + bAllSpaces = FALSE; + + if( ((unsigned char *) szLine)[i] > 127 + || ((unsigned char *) szLine)[i] < 10 ) + bHasIllegalChars = TRUE; + } + + if( bAllSpaces || bHasIllegalChars ) + break; + +/* -------------------------------------------------------------------- */ +/* Find the pivot between the keyword name and value. */ +/* -------------------------------------------------------------------- */ + int iPivot = -1; + + for( i = 0; i < 50; i++ ) + { + if( szLine[i] == '=' ) + { + iPivot = i; + break; + } + } + + // If no "=" found, split on first double white space + if( iPivot == -1 ) + { + for( i = 48; i >= 0; i-- ) + { + if( szLine[i] == ' ' && szLine[i+1] == ' ' ) + { + iPivot = i; + break; + } + } + } + + if( iPivot == -1 ) // Yikes! + { + CPLDebug( "AIRSAR", "No pivot in line `%s'.", + szLine ); + CPLAssert( iPivot != -1 ); + break; + } + +/* -------------------------------------------------------------------- */ +/* Trace ahead to the first non-white space value character. */ +/* -------------------------------------------------------------------- */ + int iValue = iPivot + 1; + + while( iValue < 50 && szLine[iValue] == ' ' ) + iValue++; + +/* -------------------------------------------------------------------- */ +/* Strip any white space off the keyword. */ +/* -------------------------------------------------------------------- */ + int iKeyEnd = iPivot - 1; + + while( iKeyEnd > 0 && szLine[iKeyEnd] == ' ' ) + iKeyEnd--; + + szLine[iKeyEnd+1] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Convert spaces or colons into underscores in the key name. */ +/* -------------------------------------------------------------------- */ + for( i = 0; szLine[i] != '\0'; i++ ) + { + if( szLine[i] == ' ' || szLine[i] == ':' || szLine[i] == ',' ) + szLine[i] = '_'; + } + +/* -------------------------------------------------------------------- */ +/* Prefix key name with provided prefix string. */ +/* -------------------------------------------------------------------- */ + char szPrefixedKeyName[55]; + + sprintf( szPrefixedKeyName, "%s_%s", pszPrefix, szLine ); + + papszHeadInfo = + CSLSetNameValue( papszHeadInfo, szPrefixedKeyName, szLine+iValue ); + + } + + return papszHeadInfo; +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *AirSARDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->fpL == NULL || poOpenInfo->nHeaderBytes < 800 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Check for AirSAR/ keyword. */ +/* -------------------------------------------------------------------- */ + if( !EQUALN((char *) poOpenInfo->pabyHeader, "RECORD LENGTH IN BYTES",22) ) + return NULL; + + if( strstr((char *) poOpenInfo->pabyHeader, "COMPRESSED") == NULL + || strstr((char *) poOpenInfo->pabyHeader, "JPL AIRCRAFT") == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Parse the header fields. We turn all the transform the */ +/* keywords by converting spaces to underscores so they will be */ +/* "well behaved" as metadata keywords. */ +/* -------------------------------------------------------------------- */ + char **papszMD = ReadHeader( poOpenInfo->fpL, 0, "MH", 20 ); + + if( papszMD == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The AIRSAR driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + AirSARDataset *poDS; + + poDS = new AirSARDataset(); + +/* -------------------------------------------------------------------- */ +/* Extract some key information. */ +/* -------------------------------------------------------------------- */ + + poDS->nRasterXSize = + atoi(CSLFetchNameValue(papszMD,"MH_NUMBER_OF_SAMPLES_PER_RECORD")); + poDS->nRasterYSize = + atoi(CSLFetchNameValue(papszMD,"MH_NUMBER_OF_LINES_IN_IMAGE")); + + poDS->nRecordLength = atoi( + CSLFetchNameValue( papszMD, "MH_RECORD_LENGTH_IN_BYTES" ) ); + + poDS->nDataStart = atoi( + CSLFetchNameValue( papszMD, "MH_BYTE_OFFSET_OF_FIRST_DATA_RECORD" )); + +/* -------------------------------------------------------------------- */ +/* Adopt the openinfo file pointer. */ +/* -------------------------------------------------------------------- */ + poDS->fp = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + +/* -------------------------------------------------------------------- */ +/* Read and merge parameter header into metadata. Prefix */ +/* parameter header values with PH_. */ +/* -------------------------------------------------------------------- */ + int nPHOffset = 0; + + if( CSLFetchNameValue( papszMD, + "MH_BYTE_OFFSET_OF_PARAMETER_HEADER" ) != NULL ) + { + nPHOffset = atoi(CSLFetchNameValue( + papszMD, "MH_BYTE_OFFSET_OF_PARAMETER_HEADER")); + char **papszPHInfo = ReadHeader( poDS->fp, nPHOffset, "PH", 100 ); + + papszMD = CSLInsertStrings( papszMD, CSLCount(papszMD), papszPHInfo ); + + CSLDestroy( papszPHInfo ); + } + +/* -------------------------------------------------------------------- */ +/* Read and merge calibration header into metadata. Prefix */ +/* parameter header values with CH_. */ +/* -------------------------------------------------------------------- */ + if( nPHOffset != 0 ) + { + char **papszCHInfo = ReadHeader( poDS->fp, + nPHOffset+poDS->nRecordLength, + "CH", 18 ); + + papszMD = CSLInsertStrings( papszMD, CSLCount(papszMD), papszCHInfo ); + + CSLDestroy( papszCHInfo ); + } + +/* -------------------------------------------------------------------- */ +/* Assign metadata to dataset. */ +/* -------------------------------------------------------------------- */ + poDS->SetMetadata( papszMD ); + CSLDestroy( papszMD ); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->SetBand( 1, new AirSARRasterBand( poDS, 1 )); + poDS->SetBand( 2, new AirSARRasterBand( poDS, 2 )); + poDS->SetBand( 3, new AirSARRasterBand( poDS, 3 )); + poDS->SetBand( 4, new AirSARRasterBand( poDS, 4 )); + poDS->SetBand( 5, new AirSARRasterBand( poDS, 5 )); + poDS->SetBand( 6, new AirSARRasterBand( poDS, 6 )); + + poDS->SetMetadataItem( "MATRIX_REPRESENTATION", "SYMMETRIZED_COVARIANCE" ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_AirSAR() */ +/************************************************************************/ + +void GDALRegister_AirSAR() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "AirSAR" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "AirSAR" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "AirSAR Polarimetric Image" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_airsar.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = AirSARDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/airsar/frmt_airsar.html b/bazaar/plugin/gdal/frmts/airsar/frmt_airsar.html new file mode 100644 index 000000000..b363cb81a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/airsar/frmt_airsar.html @@ -0,0 +1,57 @@ + + +AIRSAR -- AIRSAR Polarimetric Format + + + + +

AIRSAR -- AIRSAR Polarimetric Format

+ +Most variants of the AIRSAR Polarimetric Format produced by the AIRSAR +Integrated Processor are supported for reading by GDAL. AIRSAR products +normally include various associated data files, but only the imagery +data themselves is supported. Normally these are named mission_l.dat +(L-Band) or mission_c.dat (C-Band).

+ +AIRSAR format contains a polarimetric image in compressed stokes matrix form. +Internally GDAL decompresses the data into a stokes matrix, and then converts +that form into a covariance matrix. The returned six bands are the six values +needed to define the 3x3 Hermitian covariance matrix. The convention used +to represent the covariance matrix in terms of the scattering matrix +elements HH, HV (=VH), and VV is indicated below. Note that the +non-diagonal elements of the matrix are complex values, while the diagonal +values are real (though represented as complex bands).

+ +

    +
  • Band 1: Covariance_11 (Float32) = HH*conj(HH) +
  • Band 2: Covariance_12 (CFloat32) = sqrt(2)*HH*conj(HV) +
  • Band 3: Covariance_13 (CFloat32) = HH*conj(VV) +
  • Band 4: Covariance_22 (Float32) = 2*HV*conj(HV) +
  • Band 5: Covariance_23 (CFloat32) = sqrt(2)*HV*conj(VV) +
  • Band 6: Covariance_33 (Float32) = VV*conj(VV) +
+ + +The identities of the bands are also reflected in metadata and in the band +descriptions.

+ +The AIRSAR product format includes (potentially) several headers of +information. This information is captured and represented as metadata on the +file as a whole. Information items from the main header are prefixed with +"MH_", items from the parameter header are prefixed with "PH_" and +information from the calibration header are prefixed with "CH_". +The metadata item names are derived automatically from the names of the +fields within the header itself.

+ +No effort is made to read files associated with the AIRSAR product such as +mission_l.mocomp, mission_meta.airsar or +mission_meta.podaac.

+ +See Also:

+ +

+ + + diff --git a/bazaar/plugin/gdal/frmts/airsar/makefile.vc b/bazaar/plugin/gdal/frmts/airsar/makefile.vc new file mode 100644 index 000000000..f4618f83e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/airsar/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = airsardataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/arg/GNUmakefile b/bazaar/plugin/gdal/frmts/arg/GNUmakefile new file mode 100644 index 000000000..1e22ece92 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/arg/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../GDALmake.opt + +OBJ = argdataset.o + +CPPFLAGS := $(JSON_INCLUDE) -I../raw $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/arg/argdataset.cpp b/bazaar/plugin/gdal/frmts/arg/argdataset.cpp new file mode 100644 index 000000000..af4a55004 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/arg/argdataset.cpp @@ -0,0 +1,850 @@ +/****************************************************************************** + * $Id: argdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: Azavea Raster Grid format driver. + * Purpose: Implements support for reading and writing Azavea Raster Grid + * format. + * Author: David Zwarg + * + ****************************************************************************** + * Copyright (c) 2012, David Zwarg + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" +#include +#include + +CPL_CVSID("$Id: argdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +#define MAX_FILENAME_LEN 4096 + +#ifndef NAN +# ifdef HUGE_VAL +# define NAN (HUGE_VAL * 0.0) +# else + +static float CPLNaN(void) +{ + float fNan; + int nNan = 0x7FC00000; + memcpy(&fNan, &nNan, 4); + return fNan; +} + +# define NAN CPLNaN() +# endif +#endif + +/************************************************************************/ +/* ==================================================================== */ +/* ARGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class ARGDataset : public RawDataset +{ + VSILFILE *fpImage; // image data file. + + double adfGeoTransform[6]; + char * pszFilename; + + public: + ARGDataset(); + ~ARGDataset(); + + CPLErr GetGeoTransform( double * padfTransform ); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *CreateCopy( const char *, GDALDataset *, int, + char **, GDALProgressFunc, void *); + virtual char ** GetFileList(void); +}; + +/************************************************************************/ +/* ARGDataset() */ +/************************************************************************/ + +ARGDataset::ARGDataset() +{ + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + fpImage = NULL; +} + +/************************************************************************/ +/* ~ARGDataset() */ +/************************************************************************/ + +ARGDataset::~ARGDataset() + +{ + CPLFree(pszFilename); + + FlushCache(); + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr ARGDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + + return CE_None; +} + +/************************************************************************/ +/* GetJsonFilename() */ +/************************************************************************/ +CPLString GetJsonFilename(CPLString pszFilename) +{ + return CPLSPrintf( "%s/%s.json", CPLGetDirname(pszFilename), CPLGetBasename(pszFilename) ); +} + +/************************************************************************/ +/* GetJsonObject() */ +/************************************************************************/ +json_object * GetJsonObject(CPLString pszFilename) +{ + json_object * pJSONObject = NULL; + CPLString osJSONFilename = GetJsonFilename(pszFilename); + + pJSONObject = json_object_from_file((char *)osJSONFilename.c_str()); + if (pJSONObject == NULL) { + CPLDebug("ARGDataset", "GetJsonObject(): " + "Could not parse JSON file."); + return NULL; + } + + return pJSONObject; +} + +/************************************************************************/ +/* GetJsonValueStr() */ +/************************************************************************/ +const char * GetJsonValueStr(json_object * pJSONObject, CPLString pszKey) +{ + json_object * pJSONItem = json_object_object_get(pJSONObject, pszKey.c_str()); + if (pJSONItem == NULL) { + CPLDebug("ARGDataset", "GetJsonValueStr(): " + "Could not find '%s' in JSON.", pszKey.c_str()); + return NULL; + } + + return json_object_get_string(pJSONItem); +} + +/************************************************************************/ +/* GetJsonValueDbl() */ +/************************************************************************/ +double GetJsonValueDbl(json_object * pJSONObject, CPLString pszKey) +{ + const char *pszJSONStr = GetJsonValueStr(pJSONObject, pszKey.c_str()); + char *pszTmp; + double fTmp; + if (pszJSONStr == NULL) { + return NAN; + } + pszTmp = (char *)pszJSONStr; + fTmp = CPLStrtod(pszJSONStr, &pszTmp); + if (pszTmp == pszJSONStr) { + CPLDebug("ARGDataset", "GetJsonValueDbl(): " + "Key value is not a numeric value: %s:%s", pszKey.c_str(), pszTmp); + return NAN; + } + + return fTmp; +} + +/************************************************************************/ +/* GetJsonValueInt() */ +/************************************************************************/ +int GetJsonValueInt(json_object * pJSONObject, CPLString pszKey) +{ + double fTmp = GetJsonValueDbl(pJSONObject, pszKey.c_str()); + if (CPLIsNan(fTmp)) { + return -1; + } + + return (int)fTmp; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ +char ** ARGDataset::GetFileList() +{ + char **papszFileList = GDALPamDataset::GetFileList(); + CPLString osJSONFilename = GetJsonFilename(pszFilename); + + papszFileList = CSLAddString( papszFileList, osJSONFilename ); + + return papszFileList; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int ARGDataset::Identify( GDALOpenInfo *poOpenInfo ) +{ + json_object * pJSONObject; + if (!EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "arg")) { + return FALSE; + } + + pJSONObject = GetJsonObject(poOpenInfo->pszFilename); + if (pJSONObject == NULL) { + return FALSE; + } + + json_object_put(pJSONObject); + pJSONObject = NULL; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ +GDALDataset *ARGDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + json_object * pJSONObject; + const char * pszJSONStr; + char * pszLayer; + /***** items from the json metadata *****/ + GDALDataType eType = GDT_Unknown; + double fXmin = 0.0; + double fYmin = 0.0; + double fXmax = 0.0; + double fYmax = 0.0; + double fCellwidth = 1.0; + double fCellheight = 1.0; + double fXSkew = 0.0; + double fYSkew = 0.0; + int nRows = 0; + int nCols = 0; + int nSrs = 3857; + /***** items from the json metadata *****/ + int nPixelOffset = 0; + double fNoDataValue = NAN; + + char * pszWKT = NULL; + OGRSpatialReference oSRS; + OGRErr nErr = OGRERR_NONE; + + if ( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Check metadata settings in JSON. */ +/* -------------------------------------------------------------------- */ + + pJSONObject = GetJsonObject(poOpenInfo->pszFilename); + + if (pJSONObject == NULL) { + CPLError(CE_Failure, CPLE_AppDefined, "Error parsing JSON."); + return NULL; + } + + // get the type (always 'arg') + pszJSONStr = GetJsonValueStr(pJSONObject, "type"); + if (pszJSONStr == NULL ) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'type' is missing from the JSON file."); + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + else if (!EQUAL(pszJSONStr, "arg")) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'type' is not recognized: '%s'.", pszJSONStr); + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + + // get the datatype + pszJSONStr = GetJsonValueStr(pJSONObject, "datatype"); + if (pszJSONStr == NULL) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'datatype' is missing from the JSON file."); + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + else if (EQUAL(pszJSONStr, "int8")) { + CPLDebug("ARGDataset", "Open(): " + "int8 data is not supported in GDAL -- mapped to uint8"); + eType = GDT_Byte; + nPixelOffset = 1; + fNoDataValue = 128; + } + else if (EQUAL(pszJSONStr, "int16")) { + eType = GDT_Int16; + nPixelOffset = 2; + fNoDataValue = -32767; + } + else if (EQUAL(pszJSONStr, "int32")) { + eType = GDT_Int32; + nPixelOffset = 4; + fNoDataValue = -2e31; + } + else if (EQUAL(pszJSONStr, "uint8")) { + eType = GDT_Byte; + nPixelOffset = 1; + fNoDataValue = 255; + } + else if (EQUAL(pszJSONStr, "uint16")) { + eType = GDT_UInt16; + nPixelOffset = 2; + fNoDataValue = 65535; + } + else if (EQUAL(pszJSONStr, "uint32")) { + eType = GDT_UInt32; + nPixelOffset = 4; + fNoDataValue = -2e31; + } + else if (EQUAL(pszJSONStr, "float32")) { + eType = GDT_Float32; + nPixelOffset = 4; + fNoDataValue = NAN; + } + else if (EQUAL(pszJSONStr, "float64")) { + eType = GDT_Float64; + nPixelOffset = 8; + fNoDataValue = NAN; + } + else { + if (EQUAL(pszJSONStr, "int64") || + EQUAL(pszJSONStr, "uint64")) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'datatype' is unsupported in GDAL: '%s'.", pszJSONStr); + } + else { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'datatype' is unknown: '%s'.", pszJSONStr); + } + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + + // get the xmin of the bounding box + fXmin = GetJsonValueDbl(pJSONObject, "xmin"); + if (CPLIsNan(fXmin)) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'xmin' is missing or invalid."); + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + + // get the ymin of the bounding box + fYmin = GetJsonValueDbl(pJSONObject, "ymin"); + if (CPLIsNan(fYmin)) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'ymin' is missing or invalid."); + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + + // get the xmax of the bounding box + fXmax = GetJsonValueDbl(pJSONObject, "xmax"); + if (CPLIsNan(fXmax)) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'xmax' is missing or invalid."); + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + + // get the ymax of the bounding box + fYmax = GetJsonValueDbl(pJSONObject, "ymax"); + if (CPLIsNan(fYmax)) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'ymax' is missing or invalid."); + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + + // get the cell width + fCellwidth = GetJsonValueDbl(pJSONObject, "cellwidth"); + if (CPLIsNan(fCellwidth)) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'cellwidth' is missing or invalid."); + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + + // get the cell height + fCellheight = GetJsonValueDbl(pJSONObject, "cellheight"); + if (CPLIsNan(fCellheight)) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'cellheight' is missing or invalid."); + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + + fXSkew = GetJsonValueDbl(pJSONObject, "xskew"); + if (CPLIsNan(fXSkew)) { + // not an error -- default to 0.0 + fXSkew = 0.0f; + } + + fYSkew = GetJsonValueDbl(pJSONObject, "yskew"); + if (CPLIsNan(fYSkew)) { + // not an error -- default to 0.0 + fYSkew = 0.0f; + } + + // get the rows + nRows = GetJsonValueInt(pJSONObject, "rows"); + if (nRows < 0) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'rows' is missing or invalid."); + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + + // get the columns + nCols = GetJsonValueInt(pJSONObject, "cols"); + if (nCols < 0) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'cols' is missing or invalid."); + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + + nSrs = GetJsonValueInt(pJSONObject, "epsg"); + if (nSrs < 0) { + // not an error -- default to web mercator + nSrs = 3857; + } + + nErr = oSRS.importFromEPSG(nSrs); + if (nErr != OGRERR_NONE) { + nErr = oSRS.importFromEPSG(3857); + + if (nErr == OGRERR_NONE) { + CPLDebug("ARGDataset", "Open(): " + "The EPSG provided did not import cleanly. Defaulting to EPSG:3857"); + } + else { + CPLError(CE_Failure, CPLE_AppDefined, + "The 'epsg' value did not transate to a known spatial reference." + " Please check the 'epsg' value and try again."); + + json_object_put(pJSONObject); + pJSONObject = NULL; + + return NULL; + } + } + + nErr = oSRS.exportToWkt(&pszWKT); + if (nErr != OGRERR_NONE) { + CPLError(CE_Failure, CPLE_AppDefined, + "The spatial reference is known, but could not be set on the " + "dataset. Please check the 'epsg' value and try again."); + + json_object_put(pJSONObject); + pJSONObject = NULL; + + return NULL; + } + + // get the layer (always the file basename) + pszJSONStr = GetJsonValueStr(pJSONObject, "layer"); + if (pszJSONStr == NULL) { + CPLError(CE_Failure, CPLE_AppDefined, + "The ARG 'layer' is missing from the JSON file."); + json_object_put(pJSONObject); + pJSONObject = NULL; + return NULL; + } + + pszLayer = CPLStrdup(pszJSONStr); + + // done with the json object now + json_object_put(pJSONObject); + pJSONObject = NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + ARGDataset *poDS; + + poDS = new ARGDataset(); + + poDS->pszFilename = CPLStrdup(poOpenInfo->pszFilename); + poDS->SetMetadataItem("LAYER",pszLayer,NULL); + poDS->nRasterXSize = nCols; + poDS->nRasterYSize = nRows; + poDS->SetProjection( pszWKT ); + + // done with the projection string + CPLFree(pszWKT); + CPLFree(pszLayer); + +/* -------------------------------------------------------------------- */ +/* Assume ownership of the file handled from the GDALOpenInfo. */ +/* -------------------------------------------------------------------- */ + poDS->fpImage = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + if (poDS->fpImage == NULL) + { + delete poDS; + CPLError(CE_Failure, CPLE_AppDefined, + "Could not open dataset '%s'", poOpenInfo->pszFilename); + return NULL; + } + + poDS->adfGeoTransform[0] = fXmin; + poDS->adfGeoTransform[1] = fCellwidth; + poDS->adfGeoTransform[2] = fXSkew; + poDS->adfGeoTransform[3] = fYmax; + poDS->adfGeoTransform[4] = fYSkew; + poDS->adfGeoTransform[5] = -fCellheight; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + RawRasterBand *poBand; + +#ifdef CPL_LSB + int bNative = FALSE; +#else + int bNative = TRUE; +#endif + + poBand = new RawRasterBand( poDS, 1, poDS->fpImage, + 0, nPixelOffset, nPixelOffset * nCols, + eType, bNative, TRUE ); + poDS->SetBand( 1, poBand ); + + poBand->SetNoDataValue( fNoDataValue ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ +GDALDataset * ARGDataset::CreateCopy( const char * pszFilename, + GDALDataset * poSrcDS, + CPL_UNUSED int bStrict, + CPL_UNUSED char ** papszOptions, + CPL_UNUSED GDALProgressFunc pfnProgress, + CPL_UNUSED void * pProgressData ) +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int nXBlockSize, nYBlockSize, nPixelOffset = 0; + GDALDataType eType; + CPLString osJSONFilename; + CPLString pszDataType; + json_object * poJSONObject = NULL; + double adfTransform[6]; + GDALRasterBand * poSrcBand = NULL; + RawRasterBand * poDstBand = NULL; + VSILFILE * fpImage = NULL; + void * pabyData; + OGRSpatialReference oSRS; + char * pszWKT = NULL; + char ** pszTokens = NULL; + const char * pszLayer = NULL; + int nSrs = 0; + OGRErr nErr = OGRERR_NONE; + CPLErr eErr; + + if( nBands != 1 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "ARG driver doesn't support %d bands. Must be 1 band.", nBands ); + return NULL; + } + + eType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + if( eType == GDT_Unknown || + eType == GDT_CInt16 || + eType == GDT_CInt32 || + eType == GDT_CFloat32 || + eType == GDT_CFloat64 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "ARG driver doesn't support data type %s.", + GDALGetDataTypeName(eType) ); + return NULL; + } + else if (eType == GDT_Int16) { + pszDataType = "int16"; + nPixelOffset = 2; + } + else if (eType == GDT_Int32) { + pszDataType = "int32"; + nPixelOffset = 4; + } + else if (eType == GDT_Byte) { + pszDataType = "uint8"; + nPixelOffset = 1; + } + else if (eType == GDT_UInt16) { + pszDataType = "uint16"; + nPixelOffset = 2; + } + else if (eType == GDT_UInt32) { + pszDataType = "uint32"; + nPixelOffset = 4; + } + else if (eType == GDT_Float32) { + pszDataType = "float32"; + nPixelOffset = 4; + } + else if (eType == GDT_Float64) { + pszDataType = "float64"; + nPixelOffset = 8; + } + + poSrcDS->GetGeoTransform( adfTransform ); + + pszWKT = (char *)poSrcDS->GetProjectionRef(); + nErr = oSRS.importFromWkt(&pszWKT); + if (nErr != OGRERR_NONE) { + CPLError( CE_Failure, CPLE_NotSupported, + "Cannot import spatial reference WKT from source dataset."); + return NULL; + } + + if (oSRS.GetAuthorityCode("PROJCS") != NULL) { + nSrs = atoi(oSRS.GetAuthorityCode("PROJCS")); + } + else if (oSRS.GetAuthorityCode("GEOGCS") != NULL) { + nSrs = atoi(oSRS.GetAuthorityCode("GEOGCS")); + } + else { + // could not determine projected or geographic code + // default to EPSG:3857 if no code could be found + nSrs = 3857; + } + + /********************************************************************/ + /* Create JSON companion file. */ + /********************************************************************/ + osJSONFilename = GetJsonFilename(pszFilename); + + poJSONObject = json_object_new_object(); + + pszTokens = poSrcDS->GetMetadata(); + pszLayer = CSLFetchNameValue(pszTokens, "LAYER"); + + if ( pszLayer == NULL) { + // Set the layer + json_object_object_add(poJSONObject, "layer", json_object_new_string( + CPLGetBasename(osJSONFilename) + )); + } + else { + // Set the layer + json_object_object_add(poJSONObject, "layer", json_object_new_string( + pszLayer + )); + } + + // Set the type + json_object_object_add(poJSONObject, "type", json_object_new_string("arg")); + // Set the datatype + json_object_object_add(poJSONObject, "datatype", json_object_new_string(pszDataType)); + // Set the number of rows + json_object_object_add(poJSONObject, "rows", json_object_new_int(nYSize)); + // Set the number of columns + json_object_object_add(poJSONObject, "cols", json_object_new_int(nXSize)); + // Set the xmin + json_object_object_add(poJSONObject, "xmin", json_object_new_double(adfTransform[0])); + // Set the ymax + json_object_object_add(poJSONObject, "ymax", json_object_new_double(adfTransform[3])); + // Set the cellwidth + json_object_object_add(poJSONObject, "cellwidth", json_object_new_double(adfTransform[1])); + // Set the cellheight + json_object_object_add(poJSONObject, "cellheight", json_object_new_double(-adfTransform[5])); + // Set the xmax + json_object_object_add(poJSONObject, "xmax", json_object_new_double(adfTransform[0] + nXSize * adfTransform[1])); + // Set the ymin + json_object_object_add(poJSONObject, "ymin", json_object_new_double(adfTransform[3] + nYSize * adfTransform[5])); + // Set the xskew + json_object_object_add(poJSONObject, "xskew", json_object_new_double(adfTransform[2])); + // Set the yskew + json_object_object_add(poJSONObject, "yskew", json_object_new_double(adfTransform[4])); + if (nSrs > 0) { + // Set the epsg + json_object_object_add(poJSONObject, "epsg", json_object_new_int(nSrs)); + } + + if (json_object_to_file((char *)osJSONFilename.c_str(), poJSONObject) < 0) { + CPLError( CE_Failure, CPLE_NotSupported, + "ARG driver can't write companion file."); + + json_object_put(poJSONObject); + poJSONObject = NULL; + + return NULL; + } + + json_object_put(poJSONObject); + poJSONObject = NULL; + + fpImage = VSIFOpenL(pszFilename, "wb"); + if (fpImage == NULL) + { + CPLError( CE_Failure, CPLE_NotSupported, + "ARG driver can't create data file %s.", pszFilename); + + // remove JSON file + VSIUnlink( osJSONFilename.c_str() ); + + return NULL; + } + + // only 1 raster band + poSrcBand = poSrcDS->GetRasterBand( 1 ); + +#ifdef CPL_LSB + int bNative = FALSE; +#else + int bNative = TRUE; +#endif + + poDstBand = new RawRasterBand( fpImage, 0, nPixelOffset, + nPixelOffset * nXSize, eType, bNative, + nXSize, nYSize, TRUE, FALSE); + + poSrcBand->GetBlockSize(&nXBlockSize, &nYBlockSize); + + pabyData = CPLMalloc(nXBlockSize * nPixelOffset); + + // convert any blocks into scanlines + for (int nYBlock = 0; nYBlock * nYBlockSize < nYSize; nYBlock++) { + for (int nYScanline = 0; nYScanline < nYBlockSize; nYScanline++) { + if ((nYScanline+1) + nYBlock * nYBlockSize > poSrcBand->GetYSize() ) { + continue; + } + + for (int nXBlock = 0; nXBlock * nXBlockSize < nXSize; nXBlock++) { + int nXValid; + + if( (nXBlock+1) * nXBlockSize > poSrcBand->GetXSize() ) + nXValid = poSrcBand->GetXSize() - nXBlock * nXBlockSize; + else + nXValid = nXBlockSize; + + eErr = poSrcBand->RasterIO(GF_Read, nXBlock * nXBlockSize, + nYBlock * nYBlockSize + nYScanline, nXValid, 1, pabyData, nXBlockSize, + 1, eType, 0, 0, NULL); + + if (eErr != CE_None) { + CPLError(CE_Failure, CPLE_AppDefined, "Error reading."); + + CPLFree( pabyData ); + delete poDstBand; + VSIFCloseL( fpImage ); + + return NULL; + } + + eErr = poDstBand->RasterIO(GF_Write, nXBlock * nXBlockSize, + nYBlock * nYBlockSize + nYScanline, nXValid, 1, pabyData, nXBlockSize, + 1, eType, 0, 0, NULL); + + if (eErr != CE_None) { + CPLError(CE_Failure, CPLE_AppDefined, "Error writing."); + + CPLFree( pabyData ); + delete poDstBand; + VSIFCloseL( fpImage ); + + return NULL; + } + } + } + } + + CPLFree( pabyData ); + delete poDstBand; + VSIFCloseL( fpImage ); + + return (GDALDataset *)GDALOpen( pszFilename, GA_ReadOnly ); +} + +/************************************************************************/ +/* GDALRegister_ARG() */ +/************************************************************************/ + +void GDALRegister_ARG() +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ARG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ARG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Azavea Raster Grid format" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#ARG" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = ARGDataset::Identify; + poDriver->pfnOpen = ARGDataset::Open; + poDriver->pfnCreateCopy = ARGDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/arg/makefile.vc b/bazaar/plugin/gdal/frmts/arg/makefile.vc new file mode 100644 index 000000000..c7d307867 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/arg/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = argdataset.obj + +EXTRAFLAGS = -I..\..\ogr\ogrsf_frmts\geojson -I../../ogr/ogrsf_frmts/geojson/libjson -I..\raw + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/blx/GNUmakefile b/bazaar/plugin/gdal/frmts/blx/GNUmakefile new file mode 100644 index 000000000..ebe9f1e95 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/blx/GNUmakefile @@ -0,0 +1,15 @@ + +include ../../GDALmake.opt + +XTRA_OPT = -I. -DGDALDRIVER + +OBJ = blxdataset.o blx.o + +CPPFLAGS := $(XTRA_OPT) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/blx/blx.c b/bazaar/plugin/gdal/frmts/blx/blx.c new file mode 100644 index 000000000..8f6b46c0f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/blx/blx.c @@ -0,0 +1,1315 @@ +/* libblx - Magellan BLX topo reader/writer library + * + * Copyright (c) 2008, Henrik Johansson + * Copyright (c) 2008-2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "blx.h" +#include +#include +#include + +#include "cpl_port.h" + +/* Constants */ +#define MAXLEVELS 5 +#define MAXCOMPONENTS 4 + +static const int table1[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + ,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1 + ,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + ,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2 + ,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 + ,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 + ,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5 + ,5,5,5,5,5,5,6,6,6,6,6,6,6,6,7,7,7,7 + ,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10 + ,11,11,11,11,12,12,12,12,13,13,13,13,14 + ,14,15,15,16,16,17,17,18,18,19,19,20,21 + ,22,23,24,25,26,27,28,29,30,31,255,255,255 + ,255,255,255,255,255,255,255,255,255,255,255 + ,255,255,255,255,255,255,255,255,255,255}; + +/* { byte, n of bits when compressed, bit pattern << (13-n of bits) } */ +static const int table2[][3] = {{0,2,0}, {255,3,2048}, {1,3,3072}, {2,4,4096}, + {3,4,4608}, {254,5,5120}, {4,5,5376}, {5,5,5632}, + {253,6,5888}, {6,6,6016}, {252,6,6144}, {7,6,6272}, + {251,6,6400}, {8,6,6528}, {9,7,6656}, {250,7,6720}, + {10,7,6784}, {249,7,6848}, {11,7,6912}, {248,7,6976}, + {12,8,7040}, {247,8,7072}, {16,8,7104}, {246,8,7136}, + {13,8,7168}, {245,8,7200}, {14,8,7232}, {244,8,7264}, + {15,8,7296}, {243,8,7328}, {242,8,7360}, {241,8,7392}, + {17,9,7424}, {18,9,7440}, {240,9,7456}, {239,9,7472}, + {19,9,7488}, {238,9,7504}, {20,9,7520}, {237,9,7536}, + {21,9,7552}, {236,9,7568}, {22,9,7584}, {235,9,7600}, + {234,9,7616}, {23,9,7632}, {233,9,7648}, {24,10,7664}, + {232,10,7672}, {231,10,7680}, {25,10,7688}, {230,10,7696}, + {229,10,7704}, {26,10,7712}, {228,10,7720}, {27,10,7728}, + {227,10,7736}, {225,10,7744}, {226,10,7752}, {28,10,7760}, + {29,10,7768}, {224,10,7776}, {30,10,7784}, {31,10,7792}, + {223,10,7800}, {32,10,7808}, {222,10,7816}, {33,10,7824}, + {221,11,7832}, {220,11,7836}, {34,11,7840}, {219,11,7844}, + {35,11,7848}, {218,11,7852}, {256,11,7856}, {36,11,7860}, + {217,11,7864}, {216,11,7868}, {37,11,7872}, {215,11,7876}, + {38,11,7880}, {214,11,7884}, {193,11,7888}, {213,11,7892}, + {39,11,7896}, {128,11,7900}, {212,11,7904}, {40,11,7908}, + {194,11,7912}, {211,11,7916}, {210,11,7920}, {41,11,7924}, + {209,11,7928}, {208,11,7932}, {42,11,7936}, {207,11,7940}, + {43,11,7944}, {195,11,7948}, {206,11,7952}, {205,11,7956}, + {204,11,7960}, {44,11,7964}, {203,11,7968}, {192,11,7972}, + {196,11,7976}, {45,11,7980}, {201,11,7984}, {200,11,7988}, + {197,11,7992}, {202,11,7996}, {127,11,8000}, {199,11,8004}, + {198,11,8008}, {46,12,8012}, {47,12,8014}, {48,12,8016}, + {49,12,8018}, {50,12,8020}, {51,12,8022}, {191,12,8024}, + {52,12,8026}, {183,12,8028}, {53,12,8030}, {54,12,8032}, + {55,12,8034}, {190,12,8036}, {56,12,8038}, {57,12,8040}, + {189,12,8042}, {58,12,8044}, {176,12,8046}, {59,12,8048}, + {126,12,8050}, {60,12,8052}, {188,12,8054}, {61,12,8056}, + {63,12,8058}, {62,12,8060}, {64,12,8062}, {129,12,8064}, + {187,12,8066}, {186,12,8068}, {65,12,8070}, {66,12,8072}, + {185,12,8074}, {184,12,8076}, {68,12,8078}, {174,12,8080}, + {67,12,8082}, {182,13,8084}, {69,13,8085}, {180,13,8086}, + {181,13,8087}, {71,13,8088}, {70,13,8089}, {179,13,8090}, + {125,13,8091}, {72,13,8092}, {130,13,8093}, {178,13,8094}, + {177,13,8095}, {73,13,8096}, {74,13,8097}, {124,13,8098}, + {76,13,8099}, {175,13,8100}, {75,13,8101}, {131,13,8102}, + {132,13,8103}, {79,13,8104}, {77,13,8105}, {123,13,8106}, + {80,13,8107}, {172,13,8108}, {171,13,8109}, {78,13,8110}, + {173,13,8111}, {81,13,8112}, {169,13,8113}, {122,13,8114}, + {82,13,8115}, {133,13,8116}, {168,13,8117}, {84,13,8118}, + {164,13,8119}, {167,13,8120}, {85,13,8121}, {170,13,8122}, + {166,13,8123}, {165,13,8124}, {121,13,8125}, {160,13,8126}, + {134,13,8127}, {136,13,8128}, {161,13,8129}, {120,13,8130}, + {88,13,8131}, {83,13,8132}, {119,13,8133}, {163,13,8134}, + {162,13,8135}, {159,13,8136}, {91,13,8137}, {135,13,8138}, + {90,13,8139}, {86,13,8140}, {137,13,8141}, {87,13,8142}, + {89,13,8143}, {158,13,8144}, {152,13,8145}, {138,13,8146}, + {139,13,8147}, {116,13,8148}, {140,13,8149}, {92,13,8150}, + {96,13,8151}, {157,13,8152}, {153,13,8153}, {97,13,8154}, + {94,13,8155}, {93,13,8156}, {117,13,8157}, {156,13,8158}, + {155,13,8159}, {95,13,8160}, {118,13,8161}, {143,13,8162}, + {151,13,8163}, {142,13,8164}, {104,13,8165}, {100,13,8166}, + {148,13,8167}, {144,13,8168}, {154,13,8169}, {115,13,8170}, + {113,13,8171}, {98,13,8172}, {146,13,8173}, {112,13,8174}, + {145,13,8175}, {149,13,8176}, {141,13,8177}, {150,13,8178}, + {103,13,8179}, {147,13,8180}, {99,13,8181}, {108,13,8182}, + {101,13,8183}, {114,13,8184}, {105,13,8185}, {102,13,8186}, + {107,13,8187}, {109,13,8188}, {110,13,8189}, {111,13,8190}, + {106,13,8191}, {0,0,8192}}; + +static const int table3[] = {0x20, 0x2f, 0x44, 0x71, 0x95, 0x101}; + +STATIC int compress_chunk(unsigned char *inbuf, int inlen, unsigned char *outbuf, int outbuflen) { + int next, m=0, j, outlen=0; + unsigned reg=0; + + next = *inbuf++; + inlen--; + while(next>=0) { + /* Find index of input byte in table2 and put it in j */ + j=0; + while(next != table2[j][0]) j++; + + if(inlen) { + next = *inbuf++; + inlen--; + } else { + if(next == 0x100) + next = -1; + else + next = 0x100; + } + reg = (reg << table2[j][1]) | (table2[j][2] >> (13-table2[j][1])); + m += table2[j][1]; + + while(m>=8) { + if(outlen>=outbuflen) return -1; + *outbuf++ = (unsigned char)((reg>>(m-8)) & 0xff); + outlen++; + m-=8; + } + } + if(outlen>=outbuflen) return -1; + *outbuf++ = (unsigned char)((reg << (8-m)) & 0xff); + + return outlen+1; +} + + +STATIC int uncompress_chunk(unsigned char *inbuf, int inlen, unsigned char *outbuf, int outbuflen) { + int i,j,k,m=0, outlen=0; + unsigned reg, newdata; + + if (inlen < 4) + return -1; + + reg = *(inbuf+3) | (*(inbuf+2)<<8) | (*(inbuf+1)<<16) | (*(inbuf+0)<<24); + inbuf+=4; inlen-=4; + + newdata = (reg>>19)&0x1fff; + + while(1) { + j = newdata >> 5; + + if(table1[j] == 0xff) { + i=1; + while((int)newdata >= table2[table3[i]][2]) i++; + + j=table3[i-1]; + + k = j + ((newdata-table2[j][2]) >> (13-table2[j][1])); + + if(table2[k][0] == 0x100) + break; + else { + if(outlen>=outbuflen) return -1; + *outbuf++ = (unsigned char)table2[k][0]; + outlen++; + } + } else { + j=table1[j]; + if(outlen>=outbuflen) return -1; + *outbuf++ = (unsigned char)table2[j][0]; + outlen++; + } + + m += table2[j][1]; + + if(m>=19) { + if(m>=8) { + for(i=m>>3; i; i--) { + if(inlen) { + reg = (reg << 8) | *inbuf++; + inlen--; + } else + reg = reg << 8; + } + } + m = m&7; + } + newdata = (reg >> (19-m)) & 0x1fff; + } + return outlen; +} + +/* + Reconstruct a new detail level with double resolution in the horizontal direction + from data from the previous detail level and plus new differential data. +*/ +STATIC blxdata *reconstruct_horiz(blxdata *base, blxdata *diff, unsigned rows, unsigned cols, blxdata *out) { + unsigned int i,j; + blxdata tmp; + + /* Last column */ + for(i=0; i>2); + + /* Intermediate columns */ + for(i=0; i0; j--) + out[2*(cols*i+j)] = diff[cols*i+j] + (((short)(base[cols*i+j] + 2*(base[cols*i+j-1]-out[2*(cols*i+j+1)])-3*base[cols*i+j+1]+1))>>3); + + /* First column */ + for(i=0; i>2); + + for(i=0; i>1); + out[2*cols*i+2*j+1] = tmp-out[2*(cols*i+j)]; + out[2*cols*i+2*j] = tmp; + } + + + return out; +} + + + +/* + Reconstruct a new detail level with double resolution in the vertical direction + from data from the previous detail level and plus new differential data. +*/ +STATIC blxdata *reconstruct_vert(blxdata *base, blxdata *diff, unsigned rows, unsigned cols, blxdata *out) { + unsigned int i,j; + blxdata tmp; + + /* Last row */ + for(i=0; i>2); + + /* Intermediate rows */ + for(i=0; i0; j--) + out[2*cols*j+i] = diff[cols*j+i] + ((short)((base[cols*j+i] + 2*(base[cols*(j-1)+i]-out[2*cols*(j+1)+i])-3*base[cols*(j+1)+i]+1))>>3); + + /* First row */ + for(i=0; i>2); + + for(i=0; i>1); + out[cols*(2*j+1)+i] = tmp-out[2*cols*j+i]; + out[cols*2*j+i] = tmp; + } + return out; +} + +/* + Inverse of reconstruct_horiz + */ +STATIC void decimate_horiz(blxdata *in, unsigned int rows, unsigned int cols, blxdata *base, blxdata *diff) { + unsigned int i,j; + blxdata tmp; + + for(i=0; i>1); + } + } + + + /* First column */ + for(i=0; i>2; + } + + /* Intermediate columns */ + for(i=0; i>3; + + /* Last column */ + for(i=0; i>2; +} + +/* + Inverse of reconstruct_vert + */ +STATIC void decimate_vert(blxdata *in, unsigned int rows, unsigned int cols, blxdata *base, blxdata *diff) { + unsigned int i,j; + blxdata tmp; + + for(i=0; i>1); + } + + /* First row */ + for(j=0; j>2; + + + /* Intermediate rows */ + for(i=1; i>3; + + /* Last row */ + for(j=0; j>2; + +} + +static int get_short_le(unsigned char **data) { + int result; + + result = *(*data) | (*((char *)*data+1)<<8); + *data+=2; + return result; +} + +static int get_short_be(unsigned char **data) { + int result; + + result = *(*data+1) | (*((char *)*data)<<8); + *data+=2; + return result; +} + +static void put_short_le(short data, unsigned char **bufptr) { + *(*bufptr)++ = (unsigned char)(data & 0xff); + *(*bufptr)++ = (unsigned char)((data>>8) & 0xff); +} + +static void put_short_be(short data, unsigned char **bufptr) { + *(*bufptr)++ = (unsigned char)((data>>8) & 0xff); + *(*bufptr)++ = (unsigned char)(data & 0xff); +} + + +static int get_unsigned_short_le(unsigned char **data) { + int result; + + result = *(*data) | (*(*data+1)<<8); + *data+=2; + return result; +} + +static int get_unsigned_short_be(unsigned char **data) { + int result; + + result = *(*data+1) | (*(*data)<<8); + *data+=2; + return result; +} + +static void put_unsigned_short_le(unsigned short data, unsigned char **bufptr) { + *(*bufptr)++ = (unsigned char)(data & 0xff); + *(*bufptr)++ = (unsigned char)((data>>8) & 0xff); +} + +static void put_unsigned_short_be(unsigned short data, unsigned char **bufptr) { + *(*bufptr)++ = (unsigned char)((data>>8) & 0xff); + *(*bufptr)++ = (unsigned char)(data & 0xff); +} + +static int get_short(blxcontext_t *ctx, unsigned char **data) { + + if(ctx->endian == LITTLEENDIAN) + return get_short_le(data); + else + return get_short_be(data); +} + +static int get_unsigned_short(blxcontext_t *ctx, unsigned char **data) { + + if(ctx->endian == LITTLEENDIAN) + return get_unsigned_short_le(data); + else + return get_unsigned_short_be(data); +} + +static void put_short(blxcontext_t *ctx, short data, unsigned char **bufptr) { + if(ctx->endian == LITTLEENDIAN) + put_short_le(data, bufptr); + else + put_short_be(data, bufptr); +} + +static void put_unsigned_short(blxcontext_t *ctx, unsigned short data, unsigned char **bufptr) { + if(ctx->endian == LITTLEENDIAN) + put_unsigned_short_le(data, bufptr); + else + put_unsigned_short_be(data, bufptr); +} + +static int get_int32(blxcontext_t *ctx, unsigned char **data) { + int result; + + if(ctx->endian == LITTLEENDIAN) + result = *(*data) | (*(*data+1)<<8) | (*(*data+2)<<16) | (*((char *)*data+3)<<24); + else + result = *(*data+3) | (*(*data+2)<<8) | (*(*data+1)<<16) | (*((char *)*data)<<24); + *data+=4; + return result; +} + +static void put_int32(blxcontext_t *ctx, int data, unsigned char **bufptr) { + if(ctx->endian == LITTLEENDIAN) { + *(*bufptr)++ = (unsigned char)(data & 0xff); + *(*bufptr)++ = (unsigned char)((data>>8) & 0xff); + *(*bufptr)++ = (unsigned char)((data>>16) & 0xff); + *(*bufptr)++ = (unsigned char)((data>>24) & 0xff); + } else { + *(*bufptr)++ = (unsigned char)((data>>24) & 0xff); + *(*bufptr)++ = (unsigned char)((data>>16) & 0xff); + *(*bufptr)++ = (unsigned char)((data>>8) & 0xff); + *(*bufptr)++ = (unsigned char)(data & 0xff); + } +} + +static int get_unsigned32(blxcontext_t *ctx, unsigned char **data) { + int result; + + if(ctx->endian == LITTLEENDIAN) + result = *(*data) | (*(*data+1)<<8) | (*(*data+2)<<16) | (*(*data+3)<<24); + else + result = *(*data+3) | (*(*data+2)<<8) | (*(*data+1)<<16) | (*(*data)<<24); + *data+=4; + return result; +} + +/* Check native endian order */ +int is_big_endian(void) +{ + short int word = 0x0001; + char *byte = (char *) &word; + return (byte[0] ? 0:1); +} +double doubleSWAP(double df) +{ + union + { + double df; + unsigned char b[8]; + } dat1, dat2; + + dat1.df = df; + dat2.b[0] = dat1.b[7]; + dat2.b[1] = dat1.b[6]; + dat2.b[2] = dat1.b[5]; + dat2.b[3] = dat1.b[4]; + dat2.b[4] = dat1.b[3]; + dat2.b[5] = dat1.b[2]; + dat2.b[6] = dat1.b[1]; + dat2.b[7] = dat1.b[0]; + return dat2.df; +} + +static double get_double(blxcontext_t *ctx, unsigned char **data) { + double result; + memcpy(&result, *data, sizeof(double)); + if((is_big_endian() && ctx->endian == LITTLEENDIAN) || + (!is_big_endian() && ctx->endian == BIGENDIAN)) + result = doubleSWAP(result); + + *data+=sizeof(double); + + return result; +} + +static void put_double(blxcontext_t *ctx, double data, unsigned char **bufptr) { + if((is_big_endian() && ctx->endian == LITTLEENDIAN) || + (!is_big_endian() && ctx->endian == BIGENDIAN)) + data = doubleSWAP(data); + memcpy(*bufptr, &data, sizeof(double)); + *bufptr+=sizeof(double); +} + +static void put_cellindex_entry(blxcontext_t *ctx, struct cellindex_s *ci, unsigned char **buffer) { + put_int32(ctx, (int)ci->offset, buffer); + put_unsigned_short(ctx, (unsigned short)ci->datasize, buffer); + put_unsigned_short(ctx, (unsigned short)ci->compdatasize, buffer); +} + +/* Transpose matrix in-place */ +static void transpose(blxdata *data, int rows, int cols) { + int i,j; + blxdata tmp; + + for(i=0; ifrequency - a->frequency; +} + + +int blx_encode_celldata(blxcontext_t *ctx, + blxdata *indata, + int side, + unsigned char *outbuf, + CPL_UNUSED int outbufsize) { + unsigned char *p=outbuf, *tmpdata, *coutstart, *cout=NULL; + int level, cn, coutsize, zeros; + blxdata *vdec=NULL, *vdiff=NULL, *c[4], *tc1, *clut, *indata_scaled; + + struct lutentry_s lut[256]; + int lutsize=0; + + int i,j; + + *p++ = (unsigned char)(side/32-4); /* Resolution */ + + /* Allocated memory for buffers */ + indata_scaled = BLXmalloc(sizeof(blxdata)*side*side); + vdec = BLXmalloc(sizeof(blxdata)*side*side/2); + vdiff = BLXmalloc(sizeof(blxdata)*side*side/2); + for(cn=0; cn<4; cn++) + c[cn] = BLXmalloc(sizeof(blxdata)*side*side/4); + tc1 = BLXmalloc(sizeof(blxdata)*side*side/4); + tmpdata = BLXmalloc(5*4*side*side/4); + + /* Scale indata and process undefined values*/ + for(i=0; ifillundef) + indata[i] = (blxdata)ctx->fillundefval; + indata_scaled[i] = (blxdata)(indata[i] / ctx->zscale); + } + + indata = indata_scaled; + + cout = tmpdata; + + for(level=0; level < 5; level++) { + if(ctx->debug) { + BLXdebug1("\nlevel=%d\n", level); + } + decimate_vert(indata, side, side, vdec, vdiff); + decimate_horiz(vdec, side/2, side, c[0], c[1]); + decimate_horiz(vdiff, side/2, side, c[2], c[3]); + + /* For some reason the matrix is transposed if the lut is used for vdec_hdiff */ + for(i=0; i= 255) + break; + } else + lut[j].frequency++; + } + } + if(lutsize < 255) { + /* Since the Huffman table is arranged to let smaller number occupy + less bits after the compression, the lookup table is sorted on frequency */ + qsort(lut, lutsize, sizeof(struct lutentry_s), lutcmp); + + zeros = 0; + for(i=0; i0) && (clut[i]!=0)) || (zeros >= 0x100-lutsize)) { + *cout++ = (unsigned char)(0x100-zeros); + zeros=0; + } + if(clut[i] != 0) { + for(j=0; (j0) + *cout++ = (unsigned char)(0x100-zeros); + } + /* Use the lookuptable only when it pays off to do do. + For some reason there cannot be lookup tables in level 4. + Otherwise Mapsend crashes. */ + coutsize = cout-coutstart; + if((lutsize < 255) && (coutsize+2*lutsize+1 < 2*side*side/4) && (level < 4)) { + *p++ = (unsigned char)(lutsize+1); + for(j=0; jdebug) { + BLXdebug2("n=%d dlen=%d\n", lutsize+1, coutsize); + BLXdebug0("lut={"); + for(i=0; i>= 1; + indata = c[0]; + } + + memcpy(p, tmpdata, cout-tmpdata); + p += cout-tmpdata; + + for(i=0; i> div; + + if(side != NULL) + *side = tmp >> overviewlevel; + + cellsize = tmp*tmp; + if (outbufsize < cellsize * (int)sizeof(blxdata)) + { + BLXerror0("Cell will not fit in output buffer\n"); + return NULL; + } + + if(outbuf == NULL) { + BLXerror0("outbuf is NULL"); + return NULL; + } + + if(ctx->debug) { + BLXdebug0("==============================\n"); + } + + base = BLXmalloc(2 * baseside[0] * baseside[0] * sizeof(blxdata)); + diff = BLXmalloc(2 * baseside[0] * baseside[0] * sizeof(blxdata)); + if (base == NULL || diff == NULL) + { + BLXerror0("Not enough memory\n"); + outbuf = NULL; + goto error; + } + + /* Clear level info structure */ + memset(linfo, 0, sizeof(linfo)); + + for(level=0; level < 5; level++) { + for(c=1; c < 4; c++) { + if (len < 1) + { + BLXerror0("Cell corrupt"); + outbuf = NULL; + goto error; + } + n = *inptr++; + len --; + linfo[level][c].n = n; + if(n>0) { + linfo[level][c].lut = BLXmalloc(sizeof(blxdata)*(n-1)); + if (len < (int)sizeof(short) * n) + { + BLXerror0("Cell corrupt"); + outbuf = NULL; + goto error; + } + for(i=0; idebug) { + BLXdebug1("\nlevel=%d\n", level); + } + + linfo[level][0].data = BLXmalloc(baseside[level]*baseside[level]*sizeof(blxdata)); + if (linfo[level][0].data == NULL) + { + BLXerror0("Not enough memory\n"); + outbuf = NULL; + goto error; + } + + for(c=1; c < 4; c++) { + if(ctx->debug) { + BLXdebug2("n=%d dlen=%d\n", linfo[level][c].n, linfo[level][c].dlen); + BLXdebug0("lut={"); + for(i=0; i= linfo[level][c].n-1) { + if(dpos + 256-v > baseside[level]*baseside[level]) { + BLXerror0("Cell corrupt\n"); + outbuf = NULL; + goto error; + } + for(j=0; j<256-v; j++) + linfo[level][c].data[dpos++] = 0; + } + else + { + if(dpos + 1 > baseside[level]*baseside[level]) { + BLXerror0("Cell corrupt\n"); + outbuf = NULL; + goto error; + } + linfo[level][c].data[dpos++]=linfo[level][c].lut[v]; + } + } + len -= linfo[level][c].dlen; + if(c==1) + transpose(linfo[level][c].data, baseside[level], baseside[level]); + } + if(0 && ctx->debug) { + BLXdebug1("baseside:%d\n",baseside[level]); + BLXdebug0("data={"); + for(i=0; i= overviewlevel; level--) { + if(ctx->debug) { + BLXdebug1("baseside:%d\n",baseside[level]); + BLXdebug0("inbase={"); + for(i=0; idebug) { + BLXdebug0("base={"); + for(i=0; idebug) { + BLXdebug0("diff={"); + for(i=0; ioverviewlevel) + reconstruct_vert(base, diff, baseside[level], 2*baseside[level], linfo[level-1][0].data); + else + reconstruct_vert(base, diff, baseside[level], 2*baseside[level], outbuf); + } + + if(overviewlevel == 0) { + if (len < 1) + { + BLXerror0("Cell corrupt"); + outbuf = NULL; + goto error; + } + a = *((char *)inptr++); + len --; + index=0; + while(len >= 3) { + step = inptr[0] | (inptr[1]<<8); inptr+=2; + value = *((char *)inptr++); + len -= 3; + + index += step; + + if(value & 1) + value = (value-1)/2-a; + else + value = value/2+a; + + if(index>=cellsize) { + BLXerror0("Cell data corrupt\n"); + outbuf = NULL; + goto error; + } + + outbuf[index] = outbuf[index] + (blxdata)value; + } + + if (len != 0) + BLXdebug1("remaining len=%d", len); + } + else + { + if (len != 1) + BLXdebug1("remaining len=%d", len); + } + + /* Scale data */ + for(i=0; izscale; + + + error: + if (base != NULL) + BLXfree(base); + if (diff != NULL) + BLXfree(diff); + + /* Free allocated memory */ + for(level=4; level >= 0; level--) + for(c=0; c<4; c++) { + if(linfo[level][c].lut) + BLXfree(linfo[level][c].lut); + if(linfo[level][c].data) + BLXfree(linfo[level][c].data); + } + + return outbuf; +} + +blxcontext_t *blx_create_context() { + blxcontext_t *c; + + c = BLXmalloc(sizeof(blxcontext_t)); + + memset(c,0,sizeof(blxcontext_t)); + + c->cell_ysize = c->cell_xsize = 128; + + c->minval = 32767; + c->maxval = -32768; + + c->debug = 0; + + c->zscale = 1; + + c->fillundef = 1; + c->fillundefval = 0; + + return c; +} + +void blx_free_context(blxcontext_t *ctx) { + if(ctx->cellindex) + BLXfree(ctx->cellindex); + + BLXfree(ctx); +} + +void blxprintinfo(blxcontext_t *ctx) { + BLXnotice2("Lat: %f Lon: %f\n", ctx->lat, ctx->lon); + BLXnotice2("Pixelsize: Lat: %f Lon: %f\n", 3600*ctx->pixelsize_lat, 3600*ctx->pixelsize_lon); + BLXnotice2("Size %dx%d\n", ctx->xsize, ctx->ysize); + BLXnotice2("Cell size %dx%d\n", ctx->cell_xsize, ctx->cell_ysize); + BLXnotice2("Cell grid %dx%d\n", ctx->cell_cols, ctx->cell_rows); + BLXnotice1("Ysize scale factor: %d\n", ctx->zscale); + BLXnotice1("Max Ysize: %d\n", ctx->zscale * ctx->maxval); + BLXnotice1("Min Ysize: %d\n", ctx->zscale * ctx->minval); + BLXnotice1("Max chunksize: %d\n", ctx->maxchunksize); +} + +int blx_checkheader(char *header) { + short *signature=(short *)header; + + return ((signature[0]==0x4) && (signature[1]==0x66)) || + ((signature[0]==0x400) && (signature[1]==0x6600)); +} + +void blx_generate_header(blxcontext_t *ctx, unsigned char *header) { + unsigned char *hptr = header; + + memset(header, 0, 102); + + /* Write signature */ + put_short(ctx, 0x4, &hptr); // 0 + put_short(ctx, 0x66, &hptr); // 2 + + put_int32(ctx, ctx->cell_xsize*ctx->cell_cols, &hptr); // 4 + put_int32(ctx, ctx->cell_ysize*ctx->cell_rows, &hptr); // 8 + + put_short(ctx, (short)ctx->cell_xsize, &hptr); // 12 + put_short(ctx, (short)ctx->cell_ysize, &hptr); // 14 + + put_short(ctx, (short)ctx->cell_cols, &hptr); // 16 + put_short(ctx, (short)ctx->cell_rows, &hptr); // 18 + + put_double(ctx, ctx->lon, &hptr); // 20 + put_double(ctx, -ctx->lat, &hptr); // 28 + + put_double(ctx, ctx->pixelsize_lon, &hptr); // 36 + put_double(ctx, -ctx->pixelsize_lat, &hptr); // 44 + + put_short(ctx, (short)ctx->minval, &hptr); // 52 + put_short(ctx, (short)ctx->maxval, &hptr); // 54 + put_short(ctx, (short)ctx->zscale, &hptr); // 56 + put_int32(ctx, ctx->maxchunksize, &hptr); // 58 + // 62 +} + +int blx_writecell(blxcontext_t *ctx, blxdata *cell, int cellrow, int cellcol) { + unsigned char *uncompbuf=NULL,*outbuf=NULL; + int bufsize, uncompsize, compsize; + int status=0; + int i,allundef; + + /* Calculate statistics and find out if all elements have undefined values */ + allundef=1; + for(i=0; i < ctx->cell_xsize*ctx->cell_ysize; i++) { + if(cell[i] > ctx->maxval) + ctx->maxval = cell[i]; + if(cell[i] < ctx->minval) + ctx->minval = cell[i]; + if(cell[i]!=BLX_UNDEF) + allundef=0; + } + + if(allundef) + return status; + + if(ctx->debug) + BLXdebug2("Writing cell (%d,%d)\n",cellrow, cellcol); + + if(!ctx->open) { + status=-3; + goto error; + } + + if((cellrow >= ctx->cell_rows) || (cellcol >= ctx->cell_cols)) { + status=-2; + goto error; + } + + bufsize = sizeof(blxdata)*ctx->cell_xsize*ctx->cell_ysize+1024; + uncompbuf = BLXmalloc(bufsize); + outbuf = BLXmalloc(bufsize); + + uncompsize = blx_encode_celldata(ctx, cell, ctx->cell_xsize, uncompbuf, bufsize); + compsize = compress_chunk(uncompbuf, uncompsize, outbuf, bufsize); + if (compsize < 0) + { + BLXerror0("Couldn't compress chunk"); + status = -1; + goto error; + } + + if(uncompsize > ctx->maxchunksize) + ctx->maxchunksize = uncompsize; + + ctx->cellindex[cellrow*ctx->cell_cols + cellcol].offset = BLXftell(ctx->fh); + ctx->cellindex[cellrow*ctx->cell_cols + cellcol].datasize = uncompsize; + ctx->cellindex[cellrow*ctx->cell_cols + cellcol].compdatasize = compsize; + + if((int)BLXfwrite(outbuf, 1, compsize, ctx->fh) != compsize) { + status=-1; + goto error; + } + + error: + if(uncompbuf) + BLXfree(uncompbuf); + if(outbuf) + BLXfree(outbuf); + return status; +} + +int blxopen(blxcontext_t *ctx, const char *filename, const char *rw) { + unsigned char header[102],*hptr; + int signature[2]; + int i,j; + struct cellindex_s *ci; + + if(!strcmp(rw, "r") || !strcmp(rw, "rb")) + ctx->write=0; + else if(!strcmp(rw,"w") || !strcmp(rw, "wb")) + ctx->write=1; + else + goto error; + + ctx->fh = BLXfopen(filename, rw); + + if(ctx->fh == NULL) + goto error; + + hptr = header; + if(ctx->write) { + blx_generate_header(ctx, header); + + if(BLXfwrite(header, 1, 102, ctx->fh) != 102) + goto error; + + ctx->cellindex = BLXmalloc(sizeof(struct cellindex_s) * ctx->cell_rows * ctx->cell_cols); + if(ctx->cellindex == NULL) { + goto error; + } + memset(ctx->cellindex, 0, sizeof(struct cellindex_s) * ctx->cell_rows * ctx->cell_cols); + + /* Write the empty cell index (this will be overwritten when we have actual data)*/ + for(i=0;icell_rows;i++) + for(j=0;jcell_cols;j++) { + hptr = header; + put_cellindex_entry(ctx, ctx->cellindex+i*ctx->cell_cols+j, &hptr); + if((int)BLXfwrite(header, 1, hptr-header, ctx->fh) != (int)(hptr-header)) + goto error; + } + + } else { + /* Read header */ + if(BLXfread(header, 1, 102, ctx->fh) != 102) + goto error; + + signature[0] = get_short_le(&hptr); + signature[1] = get_short_le(&hptr); + + /* Determine if the endianess of the BLX file */ + if((signature[0] == 0x4) && (signature[1] == 0x66)) + ctx->endian = LITTLEENDIAN; + else { + hptr = header; + signature[0] = get_short_be(&hptr); + signature[1] = get_short_be(&hptr); + if((signature[0] == 0x4) && (signature[1] == 0x66)) + ctx->endian = BIGENDIAN; + else + goto error; + } + + ctx->xsize = get_int32(ctx, &hptr); + ctx->ysize = get_int32(ctx, &hptr); + if (ctx->xsize <= 0 || ctx->ysize <= 0) + { + BLXerror0("Invalid raster size"); + goto error; + } + + ctx->cell_xsize = get_short(ctx, &hptr); + ctx->cell_ysize = get_short(ctx, &hptr); + if (ctx->cell_xsize <= 0 || + ctx->cell_ysize <= 0) + { + BLXerror0("Invalid cell size"); + goto error; + } + + ctx->cell_cols = get_short(ctx, &hptr); + ctx->cell_rows = get_short(ctx, &hptr); + if (ctx->cell_cols <= 0 || ctx->cell_cols > 10000 || + ctx->cell_rows <= 0 || ctx->cell_rows > 10000) + { + BLXerror0("Invalid cell number"); + goto error; + } + + ctx->lon = get_double(ctx, &hptr); + ctx->lat = -get_double(ctx, &hptr); + + ctx->pixelsize_lon = get_double(ctx, &hptr); + ctx->pixelsize_lat = -get_double(ctx, &hptr); + + ctx->minval = get_short(ctx, &hptr); + ctx->maxval = get_short(ctx, &hptr); + ctx->zscale = get_short(ctx, &hptr); + ctx->maxchunksize = get_int32(ctx, &hptr); + + ctx->cellindex = BLXmalloc(sizeof(struct cellindex_s) * ctx->cell_rows * ctx->cell_cols); + if(ctx->cellindex == NULL) { + goto error; + } + + for(i=0;icell_rows;i++) + for(j=0;jcell_cols;j++) { + /* Read cellindex entry */ + if(BLXfread(header, 1, 8, ctx->fh) != 8) + goto error; + hptr=header; + + ci = &ctx->cellindex[i*ctx->cell_cols + j]; + ci->offset = get_unsigned32(ctx, &hptr); + ci->datasize = get_unsigned_short(ctx, &hptr); + ci->compdatasize = get_unsigned_short(ctx, &hptr); + } + } + ctx->open = 1; + + return 0; + + error: + return -1; +} + +int blxclose(blxcontext_t *ctx) { + unsigned char header[102],*hptr; + int i,j,status=0; + + if(ctx->write) { + /* Write updated header and cellindex */ + BLXfseek(ctx->fh, 0, SEEK_SET); + + blx_generate_header(ctx, header); + + if(BLXfwrite(header, 1, 102, ctx->fh) != 102) { + status=-1; + goto error; + } + for(i=0;icell_rows;i++) + for(j=0;jcell_cols;j++) { + hptr = header; + put_cellindex_entry(ctx, ctx->cellindex+i*ctx->cell_cols+j, &hptr); + if((int)BLXfwrite(header, 1, hptr-header, ctx->fh) != (int)(hptr-header)) { + status=-1; + break; + } + } + } + ctx->open = 1; + + error: + if(ctx->fh) + BLXfclose(ctx->fh); + + return status; +} + +short *blx_readcell(blxcontext_t *ctx, int row, int col, short *buffer, int bufsize, int overviewlevel) { + struct cellindex_s *ci; + unsigned char *chunk=NULL, *cchunk=NULL; + blxdata *tmpbuf=NULL; + int tmpbufsize,i; + short *result=NULL; + int npoints; + + if((ctx==NULL) || (row >= ctx->cell_rows) || (col >= ctx->cell_cols)) + return NULL; + + ci = &ctx->cellindex[row*ctx->cell_cols + col]; + + npoints = (ctx->cell_xsize*ctx->cell_ysize)>>(2*overviewlevel) ; + if (bufsize < npoints * (int)sizeof(short)) + return NULL; + + if(ci->datasize == 0) { + for(i=0; ifh, ci->offset, SEEK_SET); + + chunk = BLXmalloc(ci->datasize); + cchunk = BLXmalloc(ci->compdatasize); + + if((chunk == NULL) || (cchunk == NULL)) + goto error; + + if(BLXfread(cchunk, 1, ci->compdatasize, ctx->fh) != ci->compdatasize) + goto error; + + if((unsigned int)uncompress_chunk(cchunk, ci->compdatasize, chunk, ci->datasize) != ci->datasize) + goto error; + + tmpbufsize = sizeof(blxdata)*ctx->cell_xsize*ctx->cell_ysize; + tmpbuf = BLXmalloc(tmpbufsize); + if (tmpbuf == NULL) + goto error; + + if (decode_celldata(ctx, chunk, ci->datasize, NULL, tmpbuf, tmpbufsize, overviewlevel) == NULL) + goto error; + + for(i=0; i + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _BLX_H_INCLUDED +#define _BLX_H_INCLUDED + +#include + +/* Constants */ +#define BLX_UNDEF -32768 +#define BLX_OVERVIEWLEVELS 4 + +/* Possible values of ctx.endian */ +#define BIGENDIAN 1 +#define LITTLEENDIAN 0 + +/* Data types */ + +typedef short int blxdata; + +struct cellindex_s { + int offset; + unsigned int datasize; /* Uncompressed size */ + unsigned int compdatasize; /* Compressed data size */ +}; + +struct blxcontext_s { + int xsize, ysize; + int cell_xsize, cell_ysize; + int cell_cols, cell_rows; + double lon,lat; + double pixelsize_lon, pixelsize_lat; + + int zscale; + int maxchunksize; + int minval, maxval; + + int endian; + + struct cellindex_s *cellindex; + + int debug; + + int fillundef; /* If non-zero, fillundefval will be used instead of -32768 for undefined values in non-empty cells when + a cell is written */ + int fillundefval; + + FILE *fh; + int write; + int open; +}; + +typedef struct blxcontext_s blxcontext_t; + +struct component_s { + int n; + blxdata *lut; + int dlen; + blxdata *data; +}; + +/* Included all functions in the global name space when testing */ +#ifdef TEST +#define STATIC +#else +#define STATIC static +#endif + +/* Define memory allocation and I/O function macros */ +#ifdef GDALDRIVER +#include "cpl_conv.h" +#define BLXfopen VSIFOpen +#define BLXfclose VSIFClose +#define BLXfread VSIFRead +#define BLXfwrite VSIFWrite +#define BLXfseek VSIFSeek +#define BLXftell VSIFTell +#define BLXmalloc VSIMalloc +#define BLXfree CPLFree +#define BLXdebug0(text) CPLDebug("BLX", text) +#define BLXdebug1(text, arg1) CPLDebug("BLX", text, arg1) +#define BLXdebug2(text, arg1, arg2) CPLDebug("BLX", text, arg1, arg2) +#define BLXerror0(text) CPLError(CE_Failure, CPLE_AppDefined, text) +#define BLXnotice1(text, arg1) CPLDebug("BLX", text, arg1) +#define BLXnotice2(text, arg1, arg2) CPLDebug("BLX", text, arg1, arg2) +#else +#define BLXfopen fopen +#define BLXfclose fclose +#define BLXfread fread +#define BLXfwrite fwrite +#define BLXfseek fseek +#define BLXftell ftell +#define BLXmalloc malloc +#define BLXfree free +#define BLXdebug0 printf +#define BLXdebug1 printf +#define BLXdebug2 printf +#define BLXerror0 printf +#define BLXnotice1 printf +#define BLXnotice2 printf +#endif + +/* Function prototypes */ +#ifdef TEST +STATIC int compress_chunk(unsigned char *inbuf, int inlen, unsigned char *outbuf, int outbuflen); +STATIC int uncompress_chunk(unsigned char *inbuf, int inlen, unsigned char *outbuf, int outbuflen); +STATIC blxdata *reconstruct_horiz(blxdata *base, blxdata *diff, unsigned rows, unsigned cols, blxdata *out); +STATIC blxdata *reconstruct_vert(blxdata *base, blxdata *diff, unsigned rows, unsigned cols, blxdata *out); +STATIC void decimate_horiz(blxdata *in, unsigned rows, unsigned cols, blxdata *base, blxdata *diff); +STATIC void decimate_vert(blxdata *in, unsigned int rows, unsigned int cols, blxdata *base, blxdata *diff); +STATIC blxdata *decode_celldata(blxcontext_t *blxcontext, unsigned char *inbuf, int len, int *side, blxdata *outbuf, int outbufsize, int overviewlevel); +#endif + +blxcontext_t *blx_create_context(void); +void blx_free_context(blxcontext_t *ctx); +int blxopen(blxcontext_t *ctx, const char *filename, const char *rw); +int blxclose(blxcontext_t *ctx); +void blxprintinfo(blxcontext_t *ctx); +short *blx_readcell(blxcontext_t *ctx, int row, int col, short *buffer, int bufsize, int overviewlevel); +int blx_encode_celldata(blxcontext_t *ctx, blxdata *indata, int side, unsigned char *outbuf, int outbufsize); +int blx_checkheader(char *header); +int blx_writecell(blxcontext_t *ctx, blxdata *cell, int cellrow, int cellcol); + +#endif diff --git a/bazaar/plugin/gdal/frmts/blx/blxdataset.cpp b/bazaar/plugin/gdal/frmts/blx/blxdataset.cpp new file mode 100644 index 000000000..9075c2280 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/blx/blxdataset.cpp @@ -0,0 +1,453 @@ +/****************************************************************************** + * $Id: blxdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: BLX Driver + * Purpose: GDAL BLX support. + * Author: Henrik Johansson, henrik@johome.net + * + ****************************************************************************** + * Copyright (c) 2006, Henrik Johansson + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** +* +*/ + +#include "gdal_pam.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: blxdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +#include +CPL_C_END + +CPL_C_START +void GDALRegister_BLX(void); +CPL_C_END + +class BLXDataset : public GDALPamDataset +{ + friend class BLXRasterBand; + + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef(); + + blxcontext_t *blxcontext; + + int nOverviewCount; + int bIsOverview; + BLXDataset *papoOverviewDS[BLX_OVERVIEWLEVELS]; + + public: + BLXDataset(); + ~BLXDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +class BLXRasterBand : public GDALPamRasterBand +{ + int overviewLevel; + public: + BLXRasterBand( BLXDataset *, int, int overviewLevel=0 ); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual GDALColorInterp GetColorInterpretation(void); + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview( int ); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +GDALDataset *BLXDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + // -------------------------------------------------------------------- + // First that the header looks like a BLX header + // -------------------------------------------------------------------- + if( poOpenInfo->fpL == NULL || poOpenInfo->nHeaderBytes < 102 ) + return NULL; + + if(!blx_checkheader((char *)poOpenInfo->pabyHeader)) + return NULL; + + // -------------------------------------------------------------------- + // Create a corresponding GDALDataset. + // -------------------------------------------------------------------- + BLXDataset *poDS; + + poDS = new BLXDataset(); + + // -------------------------------------------------------------------- + // Open BLX file + // -------------------------------------------------------------------- + poDS->blxcontext = blx_create_context(); + if(poDS->blxcontext==NULL) + { + delete poDS; + return NULL; + } + if (blxopen(poDS->blxcontext, poOpenInfo->pszFilename, "rb") != 0) + { + delete poDS; + return NULL; + } + + if ((poDS->blxcontext->cell_xsize % (1 << (1+BLX_OVERVIEWLEVELS))) != 0 || + (poDS->blxcontext->cell_ysize % (1 << (1+BLX_OVERVIEWLEVELS))) != 0) + { + delete poDS; + return NULL; + } + + // Update dataset header from BLX context + poDS->nRasterXSize = poDS->blxcontext->xsize; + poDS->nRasterYSize = poDS->blxcontext->ysize; + + // -------------------------------------------------------------------- + // Create band information objects. + // -------------------------------------------------------------------- + poDS->nBands = 1; + poDS->SetBand( 1, new BLXRasterBand( poDS, 1 )); + + // Create overview bands + poDS->nOverviewCount = BLX_OVERVIEWLEVELS; + for(int i=0; i < poDS->nOverviewCount; i++) { + poDS->papoOverviewDS[i] = new BLXDataset(); + poDS->papoOverviewDS[i]->blxcontext = poDS->blxcontext; + poDS->papoOverviewDS[i]->bIsOverview = TRUE; + poDS->papoOverviewDS[i]->nRasterXSize = poDS->nRasterXSize >> (i+1); + poDS->papoOverviewDS[i]->nRasterYSize = poDS->nRasterYSize >> (i+1); + poDS->nBands = 1; + poDS->papoOverviewDS[i]->SetBand(1, new BLXRasterBand( poDS->papoOverviewDS[i], 1, i+1)); + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + delete poDS; + CPLError( CE_Failure, CPLE_NotSupported, + "The BLX driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + + return( poDS ); +} + +BLXDataset::BLXDataset() { + blxcontext = NULL; + bIsOverview = FALSE; + nOverviewCount = 0; + + for(int i=0; i < nOverviewCount; i++) + papoOverviewDS[i]=NULL; +} + +BLXDataset::~BLXDataset() { + if(!bIsOverview) { + if(blxcontext) { + blxclose(blxcontext); + blx_free_context(blxcontext); + } + for(int i=0; i < nOverviewCount; i++) + if(papoOverviewDS[i]) + delete papoOverviewDS[i]; + } +} + + +CPLErr BLXDataset::GetGeoTransform( double * padfTransform ) + +{ + padfTransform[0] = blxcontext->lon; + padfTransform[1] = blxcontext->pixelsize_lon; + padfTransform[2] = 0.0; + padfTransform[3] = blxcontext->lat; + padfTransform[4] = 0.0; + padfTransform[5] = blxcontext->pixelsize_lat; + + return CE_None; +} + +const char *BLXDataset::GetProjectionRef() +{ + return( "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]]" ); +} + +BLXRasterBand::BLXRasterBand( BLXDataset *poDS, int nBand, int overviewLevel ) + +{ + BLXDataset *poGDS = (BLXDataset *) poDS; + + this->poDS = poDS; + this->nBand = nBand; + this->overviewLevel = overviewLevel; + + eDataType = GDT_Int16; + + nBlockXSize = poGDS->blxcontext->cell_xsize >> overviewLevel; + nBlockYSize = poGDS->blxcontext->cell_ysize >> overviewLevel; +} + +int BLXRasterBand::GetOverviewCount() +{ + BLXDataset *poGDS = (BLXDataset *) poDS; + + return poGDS->nOverviewCount; +} + +GDALRasterBand *BLXRasterBand::GetOverview( int i ) +{ + BLXDataset *poGDS = (BLXDataset *) poDS; + + if( i < 0 || i >= poGDS->nOverviewCount ) + return NULL; + else + return poGDS->papoOverviewDS[i]->GetRasterBand(nBand); +} + +CPLErr BLXRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + BLXDataset *poGDS = (BLXDataset *) poDS; + + if(blx_readcell(poGDS->blxcontext, nBlockYOff, nBlockXOff, (short *)pImage, nBlockXSize*nBlockYSize*2, overviewLevel) == NULL) { + CPLError(CE_Failure, CPLE_AppDefined, + "Failed to read BLX cell"); + return CE_Failure; + } + + return CE_None; +} + +double BLXRasterBand::GetNoDataValue( int * pbSuccess ) +{ + if (pbSuccess) + *pbSuccess = TRUE; + return BLX_UNDEF; +} + +GDALColorInterp BLXRasterBand::GetColorInterpretation(void) { + return GCI_GrayIndex; +} + +/* TODO: check if georeference is the same as for BLX files, WGS84 +*/ +static GDALDataset * +BLXCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + + int zscale = 1; + int fillundef = 1, fillundefval = 0; + int endian = LITTLEENDIAN; + +// -------------------------------------------------------------------- +// Some rudimentary checks +// -------------------------------------------------------------------- + if( nBands != 1 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "BLX driver doesn't support %d bands. Must be 1 (grey) ", + nBands ); + return NULL; + } + + if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int16 && bStrict ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "BLX driver doesn't support data type %s. " + "Only 16 bit byte bands supported.\n", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); + + return NULL; + } + + if( (nXSize % 128 != 0) || (nYSize % 128 != 0) ) { + CPLError( CE_Failure, CPLE_NotSupported, + "BLX driver doesn't support dimensions that are not a multiple of 128.\n"); + + return NULL; + } + +// -------------------------------------------------------------------- +// What options has the user selected? +// -------------------------------------------------------------------- + if( CSLFetchNameValue(papszOptions,"ZSCALE") != NULL ) { + zscale = atoi(CSLFetchNameValue(papszOptions,"ZSCALE")); + if( zscale < 1 ) { + CPLError( CE_Failure, CPLE_IllegalArg, + "ZSCALE=%s is not a legal value in the range >= 1.", + CSLFetchNameValue(papszOptions,"ZSCALE") ); + return NULL; + } + } + + if( CSLFetchNameValue(papszOptions,"FILLUNDEF") != NULL + && EQUAL(CSLFetchNameValue(papszOptions,"FILLUNDEF"),"NO") ) + fillundef = 0; + else + fillundef = 1; + + if( CSLFetchNameValue(papszOptions,"FILLUNDEFVAL") != NULL ) { + fillundefval = atoi(CSLFetchNameValue(papszOptions,"FILLUNDEFVAL")); + if( (fillundefval < -32768) || (fillundefval > 32767) ) { + CPLError( CE_Failure, CPLE_IllegalArg, + "FILLUNDEFVAL=%s is not a legal value in the range -32768, 32767.", + CSLFetchNameValue(papszOptions,"FILLUNDEFVAL") ); + return NULL; + } + } + if( CSLFetchNameValue(papszOptions,"BIGENDIAN") != NULL + && !EQUAL(CSLFetchNameValue(papszOptions,"BIGENDIAN"),"NO") ) + endian = BIGENDIAN; + + + +// -------------------------------------------------------------------- +// Create the dataset. +// -------------------------------------------------------------------- + blxcontext_t *ctx; + + // Create a BLX context + ctx = blx_create_context(); + + // Setup BLX parameters + ctx->cell_rows = nYSize / ctx->cell_ysize; + ctx->cell_cols = nXSize / ctx->cell_xsize; + ctx->zscale = zscale; + ctx->fillundef = fillundef; + ctx->fillundefval = fillundefval; + ctx->endian = endian; + + if(blxopen(ctx, pszFilename, "wb")) { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create blx file %s.\n", + pszFilename ); + blx_free_context(ctx); + return NULL; + } + +// -------------------------------------------------------------------- +// Loop over image, copying image data. +// -------------------------------------------------------------------- + GInt16 *pabyTile; + CPLErr eErr=CE_None; + + pabyTile = (GInt16 *) VSIMalloc( sizeof(GInt16)*ctx->cell_xsize*ctx->cell_ysize ); + if (pabyTile == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + blxclose(ctx); + blx_free_context(ctx); + return NULL; + } + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + eErr = CE_Failure; + + for(int i=0; (i < ctx->cell_rows) && (eErr == CE_None); i++) + for(int j=0; j < ctx->cell_cols; j++) { + blxdata *celldata; + GDALRasterBand * poBand = poSrcDS->GetRasterBand( 1 ); + eErr = poBand->RasterIO( GF_Read, j*ctx->cell_xsize, i*ctx->cell_ysize, + ctx->cell_xsize, ctx->cell_ysize, + pabyTile, ctx->cell_xsize, ctx->cell_ysize, GDT_Int16, + 0, 0, NULL ); + if(eErr >= CE_Failure) + break; + celldata = pabyTile; + if (blx_writecell(ctx, celldata, i, j) != 0) + { + eErr = CE_Failure; + break; + } + + if ( ! pfnProgress( 1.0 * (i * ctx->cell_cols + j) / (ctx->cell_rows * ctx->cell_cols), NULL, pProgressData )) + { + eErr = CE_Failure; + break; + } + } + + pfnProgress( 1.0, NULL, pProgressData ); + + CPLFree( pabyTile ); + + double adfGeoTransform[6]; + if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None ) + { + ctx->lon = adfGeoTransform[0]; + ctx->lat = adfGeoTransform[3]; + ctx->pixelsize_lon = adfGeoTransform[1]; + ctx->pixelsize_lat = adfGeoTransform[5]; + } + + blxclose(ctx); + blx_free_context(ctx); + + if (eErr == CE_None) + return (GDALDataset *) GDALOpen( pszFilename, GA_ReadOnly ); + else + return NULL; +} + + +void GDALRegister_BLX() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "BLX" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "BLX" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Magellan topo (.blx)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#BLX" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "blx" ); + + poDriver->pfnOpen = BLXDataset::Open; + poDriver->pfnCreateCopy = BLXCreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/blx/frmt_blx.html b/bazaar/plugin/gdal/frmts/blx/frmt_blx.html new file mode 100644 index 000000000..9394ca596 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/blx/frmt_blx.html @@ -0,0 +1,48 @@ + + +BLX -- Magellan BLX Topo File Format (available from GDAL 1.6.0) + + + + +

BLX -- Magellan BLX Topo File Format

+ +

+BLX is the format for storing topographic data in Magellan GPS units. This driver +supports both reading and writing. In addition the 4 overview levels inherent in +the BLX format can be used with the driver. +

+ +

+The BLX format is tile based, for the moment the tile size is fixed to 128x128 size. Furthermore the dimensions must be a multiple of the tile size. +

+ +

+The data type is fixed to Int16 and the value for undefined values is fixed to -32768. In the BLX format undefined values are only really supported on tile level. For +undefined pixels in non-empty tiles see the FILLUNDEF/FILLUNDEFVAL options. +

+ +

Georeferencing

+ +

+The BLX projection is fixed to WGS84 and georeferencing from BLX is supported in the form of one tiepoint and +pixelsize. +

+ +

Creation Issues

+ +Creation Options: +

+ +

    + +
  • ZSCALE=1: Set the desired quantization increment for write access. A higher value will result in better compression and lower vertical resolution. +
  • BIGENDIAN=YES: If BIGENDIAN is defined, the output file will be in XLB format (big endian blx). +
  • FILLUNDEF=YES: If FILLUNDEF is yes the value of FILLUNDEFVAL will be used instead of -32768 for non-empty tiles. This is needed since the BLX format only support undefined values for full tiles, not individual pixels. +
  • FILLUNDEFVAL=0: See FILLUNDEF + +
+

+ + + diff --git a/bazaar/plugin/gdal/frmts/blx/makefile.vc b/bazaar/plugin/gdal/frmts/blx/makefile.vc new file mode 100644 index 000000000..dc5e4d68e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/blx/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = blxdataset.obj blx.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I. -DGDALDRIVER + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/bmp/GNUmakefile b/bazaar/plugin/gdal/frmts/bmp/GNUmakefile new file mode 100644 index 000000000..756ed584a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bmp/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = bmpdataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/bmp/bmpdataset.cpp b/bazaar/plugin/gdal/frmts/bmp/bmpdataset.cpp new file mode 100644 index 000000000..ed8be0430 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bmp/bmpdataset.cpp @@ -0,0 +1,1553 @@ +/****************************************************************************** + * $Id: bmpdataset.cpp 28234 2014-12-27 14:22:54Z rouault $ + * + * Project: Microsoft Windows Bitmap + * Purpose: Read/write MS Windows Device Independent Bitmap (DIB) files + * and OS/2 Presentation Manager bitmaps v. 1.x and v. 2.x + * Author: Andrey Kiselev, dron@remotesensing.org + * + ****************************************************************************** + * Copyright (c) 2002, Andrey Kiselev + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: bmpdataset.cpp 28234 2014-12-27 14:22:54Z rouault $"); + +CPL_C_START +void GDALRegister_BMP(void); +CPL_C_END + +// Enable if you want to see lots of BMP debugging output. +// #define BMP_DEBUG + +enum BMPType +{ + BMPT_WIN4, // BMP used in Windows 3.0/NT 3.51/95 + BMPT_WIN5, // BMP used in Windows NT 4.0/98/Me/2000/XP + BMPT_OS21, // BMP used in OS/2 PM 1.x + BMPT_OS22 // BMP used in OS/2 PM 2.x +}; + +// Bitmap file consists of a BMPFileHeader structure followed by a +// BMPInfoHeader structure. An array of BMPColorEntry structures (also called +// a colour table) follows the bitmap information header structure. The colour +// table is followed by a second array of indexes into the colour table (the +// actual bitmap data). Data may be comressed, for 4-bpp and 8-bpp used RLE +// compression. +// +// +---------------------+ +// | BMPFileHeader | +// +---------------------+ +// | BMPInfoHeader | +// +---------------------+ +// | BMPColorEntry array | +// +---------------------+ +// | Colour-index array | +// +---------------------+ +// +// All numbers stored in Intel order with least significant byte first. + +enum BMPComprMethod +{ + BMPC_RGB = 0L, // Uncompressed + BMPC_RLE8 = 1L, // RLE for 8 bpp images + BMPC_RLE4 = 2L, // RLE for 4 bpp images + BMPC_BITFIELDS = 3L, // Bitmap is not compressed and the colour table + // consists of three DWORD color masks that specify + // the red, green, and blue components of each pixel. + // This is valid when used with 16- and 32-bpp bitmaps. + BMPC_JPEG = 4L, // Indicates that the image is a JPEG image. + BMPC_PNG = 5L // Indicates that the image is a PNG image. +}; + +enum BMPLCSType // Type of logical color space. +{ + BMPLT_CALIBRATED_RGB = 0, // This value indicates that endpoints and gamma + // values are given in the appropriate fields. + BMPLT_DEVICE_RGB = 1, + BMPLT_DEVICE_CMYK = 2 +}; + +typedef struct +{ + GInt32 iCIEX; + GInt32 iCIEY; + GInt32 iCIEZ; +} BMPCIEXYZ; + +typedef struct // This structure contains the x, y, and z +{ // coordinates of the three colors that correspond + BMPCIEXYZ iCIERed; // to the red, green, and blue endpoints for + BMPCIEXYZ iCIEGreen; // a specified logical color space. + BMPCIEXYZ iCIEBlue; +} BMPCIEXYZTriple; + +typedef struct +{ + GByte bType[2]; // Signature "BM" + GUInt32 iSize; // Size in bytes of the bitmap file. Should always + // be ignored while reading because of error + // in Windows 3.0 SDK's description of this field + GUInt16 iReserved1; // Reserved, set as 0 + GUInt16 iReserved2; // Reserved, set as 0 + GUInt32 iOffBits; // Offset of the image from file start in bytes +} BMPFileHeader; + +// File header size in bytes: +const int BFH_SIZE = 14; + +typedef struct +{ + GUInt32 iSize; // Size of BMPInfoHeader structure in bytes. + // Should be used to determine start of the + // colour table + GInt32 iWidth; // Image width + GInt32 iHeight; // Image height. If positive, image has bottom left + // origin, if negative --- top left. + GUInt16 iPlanes; // Number of image planes (must be set to 1) + GUInt16 iBitCount; // Number of bits per pixel (1, 4, 8, 16, 24 or 32). + // If 0 then the number of bits per pixel is + // specified or is implied by the JPEG or PNG format. + BMPComprMethod iCompression; // Compression method + GUInt32 iSizeImage; // Size of uncomressed image in bytes. May be 0 + // for BMPC_RGB bitmaps. If iCompression is BI_JPEG + // or BI_PNG, iSizeImage indicates the size + // of the JPEG or PNG image buffer. + GInt32 iXPelsPerMeter; // X resolution, pixels per meter (0 if not used) + GInt32 iYPelsPerMeter; // Y resolution, pixels per meter (0 if not used) + GUInt32 iClrUsed; // Size of colour table. If 0, iBitCount should + // be used to calculate this value (1<poDS = poDS; + this->nBand = nBand; + eDataType = GDT_Byte; + iBytesPerPixel = poDS->sInfoHeader.iBitCount / 8; + + // We will read one scanline per time. Scanlines in BMP aligned at 4-byte + // boundary + nBlockXSize = poDS->GetRasterXSize(); + + if (nBlockXSize < (INT_MAX - 31) / poDS->sInfoHeader.iBitCount) + nScanSize = + ((poDS->GetRasterXSize() * poDS->sInfoHeader.iBitCount + 31) & ~31) / 8; + else + { + pabyScan = NULL; + return; + } + nBlockYSize = 1; + +#ifdef BMP_DEBUG + CPLDebug( "BMP", + "Band %d: set nBlockXSize=%d, nBlockYSize=%d, nScanSize=%d", + nBand, nBlockXSize, nBlockYSize, nScanSize ); +#endif + + pabyScan = (GByte *) VSIMalloc( nScanSize ); +} + +/************************************************************************/ +/* ~BMPRasterBand() */ +/************************************************************************/ + +BMPRasterBand::~BMPRasterBand() +{ + CPLFree( pabyScan ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr BMPRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + BMPDataset *poGDS = (BMPDataset *) poDS; + GUInt32 iScanOffset; + int i; + + if ( poGDS->sInfoHeader.iHeight > 0 ) + iScanOffset = poGDS->sFileHeader.iOffBits + + ( poGDS->GetRasterYSize() - nBlockYOff - 1 ) * nScanSize; + else + iScanOffset = poGDS->sFileHeader.iOffBits + nBlockYOff * nScanSize; + + if ( VSIFSeekL( poGDS->fp, iScanOffset, SEEK_SET ) < 0 ) + { + // XXX: We will not report error here, because file just may be + // in update state and data for this block will be available later + if( poGDS->eAccess == GA_Update ) + { + memset( pImage, 0, nBlockXSize ); + return CE_None; + } + else + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't seek to offset %ld in input file to read data.", + (long) iScanOffset ); + return CE_Failure; + } + } + if ( VSIFReadL( pabyScan, 1, nScanSize, poGDS->fp ) < nScanSize ) + { + // XXX + if( poGDS->eAccess == GA_Update ) + { + memset( pImage, 0, nBlockXSize ); + return CE_None; + } + else + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't read from offset %ld in input file.", + (long) iScanOffset ); + return CE_Failure; + } + } + + if ( poGDS->sInfoHeader.iBitCount == 24 || + poGDS->sInfoHeader.iBitCount == 32 ) + { + GByte *pabyTemp = pabyScan + 3 - nBand; + + for ( i = 0; i < nBlockXSize; i++ ) + { + // Colour triplets in BMP file organized in reverse order: + // blue, green, red. When we have 32-bit BMP the forth byte + // in quadriplet should be discarded as it has no meaning. + // That is why we always use 3 byte count in the following + // pabyTemp index. + ((GByte *) pImage)[i] = *pabyTemp; + pabyTemp += iBytesPerPixel; + } + } + else if ( poGDS->sInfoHeader.iBitCount == 8 ) + { + memcpy( pImage, pabyScan, nBlockXSize ); + } + else if ( poGDS->sInfoHeader.iBitCount == 16 ) + { + // rcg, oct 7/06: Byteswap if necessary, use int16 + // references to file pixels, expand samples to + // 8-bit, support BMPC_BITFIELDS channel mask indicators, + // and generalize band handling. + + GUInt16* pScan16 = (GUInt16*)pabyScan; +#ifdef CPL_MSB + GDALSwapWords( pScan16, sizeof(GUInt16), nBlockXSize, 0); +#endif + + // todo: make these band members and precompute. + int mask[3], shift[3], size[3]; + float fTo8bit[3]; + + if(poGDS->sInfoHeader.iCompression == BMPC_RGB) + { + mask[0] = 0x7c00; + mask[1] = 0x03e0; + mask[2] = 0x001f; + } + else if(poGDS->sInfoHeader.iCompression == BMPC_BITFIELDS) + { + mask[0] = poGDS->sInfoHeader.iRedMask; + mask[1] = poGDS->sInfoHeader.iGreenMask; + mask[2] = poGDS->sInfoHeader.iBlueMask; + } + else + { + CPLError( CE_Failure, CPLE_FileIO, + "Unknown 16-bit compression %d.", + poGDS->sInfoHeader.iCompression); + return CE_Failure; + } + + for(i = 0; i < 3; i++) + { + shift[i] = findfirstonbit(mask[i]); + size[i] = countonbits(mask[i]); + if(size[i] > 14 || size[i] == 0) + { + CPLError( CE_Failure, CPLE_FileIO, + "Bad 16-bit channel mask %8x.", + mask[i]); + return CE_Failure; + } + fTo8bit[i] = 255.0f / ((1 << size[i])-1); + } + + for ( i = 0; i < nBlockXSize; i++ ) + { + ((GByte *) pImage)[i] = (GByte) + (0.5f + fTo8bit[nBand-1] * + ((pScan16[i] & mask[nBand-1]) >> shift[nBand-1])); +#if 0 + // original code + switch ( nBand ) + { + case 1: // Red + ((GByte *) pImage)[i] = pabyScan[i + 1] & 0x1F; + break; + + case 2: // Green + ((GByte *) pImage)[i] = + ((pabyScan[i] & 0x03) << 3) | + ((pabyScan[i + 1] & 0xE0) >> 5); + break; + + case 3: // Blue + ((GByte *) pImage)[i] = (pabyScan[i] & 0x7c) >> 2; + break; + default: + break; + } +#endif // 0 + } + } + else if ( poGDS->sInfoHeader.iBitCount == 4 ) + { + GByte *pabyTemp = pabyScan; + + for ( i = 0; i < nBlockXSize; i++ ) + { + // Most significant part of the byte represents leftmost pixel + if ( i & 0x01 ) + ((GByte *) pImage)[i] = *pabyTemp++ & 0x0F; + else + ((GByte *) pImage)[i] = (*pabyTemp & 0xF0) >> 4; + } + } + else if ( poGDS->sInfoHeader.iBitCount == 1 ) + { + GByte *pabyTemp = pabyScan; + + for ( i = 0; i < nBlockXSize; i++ ) + { + switch ( i & 0x7 ) + { + case 0: + ((GByte *) pImage)[i] = (*pabyTemp & 0x80) >> 7; + break; + case 1: + ((GByte *) pImage)[i] = (*pabyTemp & 0x40) >> 6; + break; + case 2: + ((GByte *) pImage)[i] = (*pabyTemp & 0x20) >> 5; + break; + case 3: + ((GByte *) pImage)[i] = (*pabyTemp & 0x10) >> 4; + break; + case 4: + ((GByte *) pImage)[i] = (*pabyTemp & 0x08) >> 3; + break; + case 5: + ((GByte *) pImage)[i] = (*pabyTemp & 0x04) >> 2; + break; + case 6: + ((GByte *) pImage)[i] = (*pabyTemp & 0x02) >> 1; + break; + case 7: + ((GByte *) pImage)[i] = *pabyTemp++ & 0x01; + break; + default: + break; + } + } + } + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr BMPRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + BMPDataset *poGDS = (BMPDataset *)poDS; + int iInPixel, iOutPixel; + GUInt32 iScanOffset; + + CPLAssert( poGDS != NULL + && nBlockXOff >= 0 + && nBlockYOff >= 0 + && pImage != NULL ); + + iScanOffset = poGDS->sFileHeader.iOffBits + + ( poGDS->GetRasterYSize() - nBlockYOff - 1 ) * nScanSize; + if ( VSIFSeekL( poGDS->fp, iScanOffset, SEEK_SET ) < 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't seek to offset %ld in output file to write data.\n%s", + (long) iScanOffset, VSIStrerror( errno ) ); + return CE_Failure; + } + + if( poGDS->nBands != 1 ) + { + memset( pabyScan, 0, nScanSize ); + VSIFReadL( pabyScan, 1, nScanSize, poGDS->fp ); + VSIFSeekL( poGDS->fp, iScanOffset, SEEK_SET ); + } + + for ( iInPixel = 0, iOutPixel = iBytesPerPixel - nBand; + iInPixel < nBlockXSize; iInPixel++, iOutPixel += poGDS->nBands ) + { + pabyScan[iOutPixel] = ((GByte *) pImage)[iInPixel]; + } + + if ( VSIFWriteL( pabyScan, 1, nScanSize, poGDS->fp ) < nScanSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't write block with X offset %d and Y offset %d.\n%s", + nBlockXOff, nBlockYOff, + VSIStrerror( errno ) ); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *BMPRasterBand::GetColorTable() +{ + BMPDataset *poGDS = (BMPDataset *) poDS; + + return poGDS->poColorTable; +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr BMPRasterBand::SetColorTable( GDALColorTable *poColorTable ) +{ + BMPDataset *poGDS = (BMPDataset *) poDS; + + if ( poColorTable ) + { + GDALColorEntry oEntry; + GUInt32 iULong; + unsigned int i; + + poGDS->sInfoHeader.iClrUsed = poColorTable->GetColorEntryCount(); + if ( poGDS->sInfoHeader.iClrUsed < 1 || + poGDS->sInfoHeader.iClrUsed > (1U << poGDS->sInfoHeader.iBitCount) ) + return CE_Failure; + + VSIFSeekL( poGDS->fp, BFH_SIZE + 32, SEEK_SET ); + + iULong = CPL_LSBWORD32( poGDS->sInfoHeader.iClrUsed ); + VSIFWriteL( &iULong, 4, 1, poGDS->fp ); + poGDS->pabyColorTable = (GByte *) CPLRealloc( poGDS->pabyColorTable, + poGDS->nColorElems * poGDS->sInfoHeader.iClrUsed ); + if ( !poGDS->pabyColorTable ) + return CE_Failure; + + for( i = 0; i < poGDS->sInfoHeader.iClrUsed; i++ ) + { + poColorTable->GetColorEntryAsRGB( i, &oEntry ); + poGDS->pabyColorTable[i * poGDS->nColorElems + 3] = 0; + poGDS->pabyColorTable[i * poGDS->nColorElems + 2] = + (GByte) oEntry.c1; // Red + poGDS->pabyColorTable[i * poGDS->nColorElems + 1] = + (GByte) oEntry.c2; // Green + poGDS->pabyColorTable[i * poGDS->nColorElems] = + (GByte) oEntry.c3; // Blue + } + + VSIFSeekL( poGDS->fp, BFH_SIZE + poGDS->sInfoHeader.iSize, SEEK_SET ); + if ( VSIFWriteL( poGDS->pabyColorTable, 1, + poGDS->nColorElems * poGDS->sInfoHeader.iClrUsed, poGDS->fp ) < + poGDS->nColorElems * (GUInt32) poGDS->sInfoHeader.iClrUsed ) + { + return CE_Failure; + } + } + else + return CE_Failure; + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp BMPRasterBand::GetColorInterpretation() +{ + BMPDataset *poGDS = (BMPDataset *) poDS; + + if( poGDS->sInfoHeader.iBitCount == 24 || + poGDS->sInfoHeader.iBitCount == 32 || + poGDS->sInfoHeader.iBitCount == 16 ) + { + if( nBand == 1 ) + return GCI_RedBand; + else if( nBand == 2 ) + return GCI_GreenBand; + else if( nBand == 3 ) + return GCI_BlueBand; + else + return GCI_Undefined; + } + else + { + return GCI_PaletteIndex; + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* BMPComprRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class BMPComprRasterBand : public BMPRasterBand +{ + friend class BMPDataset; + + GByte *pabyComprBuf; + GByte *pabyUncomprBuf; + + public: + + BMPComprRasterBand( BMPDataset *, int ); + ~BMPComprRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); +// virtual CPLErr IWriteBlock( int, int, void * ); +}; + +/************************************************************************/ +/* BMPComprRasterBand() */ +/************************************************************************/ + +BMPComprRasterBand::BMPComprRasterBand( BMPDataset *poDS, int nBand ) + : BMPRasterBand( poDS, nBand ) +{ + unsigned int i, j, k, iLength = 0; + GUInt32 iComprSize, iUncomprSize; + + iComprSize = poDS->sFileHeader.iSize - poDS->sFileHeader.iOffBits; + iUncomprSize = poDS->GetRasterXSize() * poDS->GetRasterYSize(); + +#ifdef DEBUG + CPLDebug( "BMP", "RLE compression detected." ); + CPLDebug ( "BMP", "Size of compressed buffer %ld bytes," + " size of uncompressed buffer %ld bytes.", + (long) iComprSize, (long) iUncomprSize ); +#endif + /* TODO: it might be interesting to avoid uncompressing the whole data */ + /* in a single pass, especially if nXSize * nYSize is big */ + /* We could read incrementally one row at a time */ + if (poDS->GetRasterXSize() > INT_MAX / poDS->GetRasterYSize()) + { + CPLError(CE_Failure, CPLE_NotSupported, "Too big dimensions : %d x %d", + poDS->GetRasterXSize(), poDS->GetRasterYSize()); + pabyComprBuf = NULL; + pabyUncomprBuf = NULL; + return; + } + pabyComprBuf = (GByte *) VSIMalloc( iComprSize ); + pabyUncomprBuf = (GByte *) VSIMalloc( iUncomprSize ); + if (pabyComprBuf == NULL || + pabyUncomprBuf == NULL) + { + CPLFree(pabyComprBuf); + pabyComprBuf = NULL; + CPLFree(pabyUncomprBuf); + pabyUncomprBuf = NULL; + return; + } + + VSIFSeekL( poDS->fp, poDS->sFileHeader.iOffBits, SEEK_SET ); + VSIFReadL( pabyComprBuf, 1, iComprSize, poDS->fp ); + i = 0; + j = 0; + if ( poDS->sInfoHeader.iBitCount == 8 ) // RLE8 + { + while( j < iUncomprSize && i < iComprSize ) + { + if ( pabyComprBuf[i] ) + { + iLength = pabyComprBuf[i++]; + while( iLength > 0 && j < iUncomprSize && i < iComprSize ) + { + pabyUncomprBuf[j++] = pabyComprBuf[i]; + iLength--; + } + i++; + } + else + { + i++; + if ( pabyComprBuf[i] == 0 ) // Next scanline + { + i++; + } + else if ( pabyComprBuf[i] == 1 ) // End of image + { + break; + } + else if ( pabyComprBuf[i] == 2 ) // Move to... + { + i++; + if ( i < iComprSize - 1 ) + { + j += pabyComprBuf[i] + + pabyComprBuf[i+1] * poDS->GetRasterXSize(); + i += 2; + } + else + break; + } + else // Absolute mode + { + if (i < iComprSize) + iLength = pabyComprBuf[i++]; + for ( k = 0; k < iLength && j < iUncomprSize && i < iComprSize; k++ ) + pabyUncomprBuf[j++] = pabyComprBuf[i++]; + if ( i & 0x01 ) + i++; + } + } + } + } + else // RLE4 + { + while( j < iUncomprSize && i < iComprSize ) + { + if ( pabyComprBuf[i] ) + { + iLength = pabyComprBuf[i++]; + while( iLength > 0 && j < iUncomprSize && i < iComprSize ) + { + if ( iLength & 0x01 ) + pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4; + else + pabyUncomprBuf[j++] = pabyComprBuf[i] & 0x0F; + iLength--; + } + i++; + } + else + { + i++; + if ( pabyComprBuf[i] == 0 ) // Next scanline + { + i++; + } + else if ( pabyComprBuf[i] == 1 ) // End of image + { + break; + } + else if ( pabyComprBuf[i] == 2 ) // Move to... + { + i++; + if ( i < iComprSize - 1 ) + { + j += pabyComprBuf[i] + + pabyComprBuf[i+1] * poDS->GetRasterXSize(); + i += 2; + } + else + break; + } + else // Absolute mode + { + if (i < iComprSize) + iLength = pabyComprBuf[i++]; + for ( k = 0; k < iLength && j < iUncomprSize && i < iComprSize; k++ ) + { + if ( k & 0x01 ) + pabyUncomprBuf[j++] = pabyComprBuf[i++] & 0x0F; + else + pabyUncomprBuf[j++] = (pabyComprBuf[i] & 0xF0) >> 4; + } + if ( i & 0x01 ) + i++; + } + } + } + } + // rcg, release compressed buffer here. + if ( pabyComprBuf ) + CPLFree( pabyComprBuf ); + pabyComprBuf = NULL; + +} + +/************************************************************************/ +/* ~BMPComprRasterBand() */ +/************************************************************************/ + +BMPComprRasterBand::~BMPComprRasterBand() +{ + if ( pabyComprBuf ) + CPLFree( pabyComprBuf ); + if ( pabyUncomprBuf ) + CPLFree( pabyUncomprBuf ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr BMPComprRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + memcpy( pImage, pabyUncomprBuf + + (poDS->GetRasterYSize() - nBlockYOff - 1) * poDS->GetRasterXSize(), + nBlockXSize ); + + return CE_None; +} + +/************************************************************************/ +/* BMPDataset() */ +/************************************************************************/ + +BMPDataset::BMPDataset() +{ + pszFilename = NULL; + fp = NULL; + nBands = 0; + bGeoTransformValid = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + pabyColorTable = NULL; + poColorTable = NULL; + memset( &sFileHeader, 0, sizeof(sFileHeader) ); + memset( &sInfoHeader, 0, sizeof(sInfoHeader) ); +} + +/************************************************************************/ +/* ~BMPDataset() */ +/************************************************************************/ + +BMPDataset::~BMPDataset() +{ + FlushCache(); + + if ( pabyColorTable ) + CPLFree( pabyColorTable ); + if ( poColorTable != NULL ) + delete poColorTable; + if( fp != NULL ) + VSIFCloseL( fp ); + CPLFree( pszFilename ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr BMPDataset::GetGeoTransform( double * padfTransform ) +{ + if( bGeoTransformValid ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0])*6 ); + return CE_None; + } + + if( GDALPamDataset::GetGeoTransform( padfTransform ) == CE_None) + return CE_None; + +#ifdef notdef + // See http://trac.osgeo.org/gdal/ticket/3578 + if (sInfoHeader.iXPelsPerMeter > 0 && sInfoHeader.iYPelsPerMeter > 0) + { + padfTransform[1] = sInfoHeader.iXPelsPerMeter; + padfTransform[5] = -sInfoHeader.iYPelsPerMeter; + padfTransform[0] = -0.5*padfTransform[1]; + padfTransform[3] = -0.5*padfTransform[5]; + return CE_None; + } +#endif + + return CE_Failure; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr BMPDataset::SetGeoTransform( double * padfTransform ) +{ + CPLErr eErr = CE_None; + + if ( pszFilename && bGeoTransformValid ) + { + memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 ); + + if ( GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform ) + == FALSE ) + { + CPLError( CE_Failure, CPLE_FileIO, "Can't write world file." ); + eErr = CE_Failure; + } + return eErr; + } + else + return GDALPamDataset::SetGeoTransform( padfTransform ); +} + +/************************************************************************/ +/* IRasterIO() */ +/* */ +/* Multi-band raster io handler. We will use block based */ +/* loading is used for multiband BMPs. That is because they */ +/* are effectively pixel interleaved, so processing all bands */ +/* for a given block together avoid extra seeks. */ +/************************************************************************/ + +CPLErr BMPDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + if( nBandCount > 1 ) + return GDALDataset::BlockBasedRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); + else + return + GDALDataset::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int BMPDataset::Identify( GDALOpenInfo *poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 2 + || poOpenInfo->pabyHeader[0] != 'B' + || poOpenInfo->pabyHeader[1] != 'M' ) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *BMPDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + BMPDataset *poDS; + VSIStatBufL sStat; + + poDS = new BMPDataset(); + poDS->eAccess = poOpenInfo->eAccess; + + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" ); + if ( !poDS->fp ) + { + delete poDS; + return NULL; + } + + VSIStatL(poOpenInfo->pszFilename, &sStat); + +/* -------------------------------------------------------------------- */ +/* Read the BMPFileHeader. We need iOffBits value only */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( poDS->fp, 10, SEEK_SET ); + VSIFReadL( &poDS->sFileHeader.iOffBits, 1, 4, poDS->fp ); +#ifdef CPL_MSB + CPL_SWAP32PTR( &poDS->sFileHeader.iOffBits ); +#endif + poDS->sFileHeader.iSize = (GUInt32) sStat.st_size; + +#ifdef BMP_DEBUG + CPLDebug( "BMP", "File size %d bytes.", poDS->sFileHeader.iSize ); + CPLDebug( "BMP", "Image offset 0x%x bytes from file start.", + poDS->sFileHeader.iOffBits ); +#endif + +/* -------------------------------------------------------------------- */ +/* Read the BMPInfoHeader. */ +/* -------------------------------------------------------------------- */ + BMPType eBMPType; + + VSIFSeekL( poDS->fp, BFH_SIZE, SEEK_SET ); + VSIFReadL( &poDS->sInfoHeader.iSize, 1, 4, poDS->fp ); +#ifdef CPL_MSB + CPL_SWAP32PTR( &poDS->sInfoHeader.iSize ); +#endif + + if ( poDS->sInfoHeader.iSize == BIH_WIN4SIZE ) + eBMPType = BMPT_WIN4; + else if ( poDS->sInfoHeader.iSize == BIH_OS21SIZE ) + eBMPType = BMPT_OS21; + else if ( poDS->sInfoHeader.iSize == BIH_OS22SIZE || + poDS->sInfoHeader.iSize == 16 ) + eBMPType = BMPT_OS22; + else + eBMPType = BMPT_WIN5; + + if ( eBMPType == BMPT_WIN4 || eBMPType == BMPT_WIN5 || eBMPType == BMPT_OS22 ) + { + VSIFReadL( &poDS->sInfoHeader.iWidth, 1, 4, poDS->fp ); + VSIFReadL( &poDS->sInfoHeader.iHeight, 1, 4, poDS->fp ); + VSIFReadL( &poDS->sInfoHeader.iPlanes, 1, 2, poDS->fp ); + VSIFReadL( &poDS->sInfoHeader.iBitCount, 1, 2, poDS->fp ); + VSIFReadL( &poDS->sInfoHeader.iCompression, 1, 4, poDS->fp ); + VSIFReadL( &poDS->sInfoHeader.iSizeImage, 1, 4, poDS->fp ); + VSIFReadL( &poDS->sInfoHeader.iXPelsPerMeter, 1, 4, poDS->fp ); + VSIFReadL( &poDS->sInfoHeader.iYPelsPerMeter, 1, 4, poDS->fp ); + VSIFReadL( &poDS->sInfoHeader.iClrUsed, 1, 4, poDS->fp ); + VSIFReadL( &poDS->sInfoHeader.iClrImportant, 1, 4, poDS->fp ); + + // rcg, read win4/5 fields. If we're reading a + // legacy header that ends at iClrImportant, it turns + // out that the three DWORD color table entries used + // by the channel masks start here anyway. + if(poDS->sInfoHeader.iCompression == BMPC_BITFIELDS) + { + VSIFReadL( &poDS->sInfoHeader.iRedMask, 1, 4, poDS->fp ); + VSIFReadL( &poDS->sInfoHeader.iGreenMask, 1, 4, poDS->fp ); + VSIFReadL( &poDS->sInfoHeader.iBlueMask, 1, 4, poDS->fp ); + } +#ifdef CPL_MSB + CPL_SWAP32PTR( &poDS->sInfoHeader.iWidth ); + CPL_SWAP32PTR( &poDS->sInfoHeader.iHeight ); + CPL_SWAP16PTR( &poDS->sInfoHeader.iPlanes ); + CPL_SWAP16PTR( &poDS->sInfoHeader.iBitCount ); + CPL_SWAP32PTR( &poDS->sInfoHeader.iCompression ); + CPL_SWAP32PTR( &poDS->sInfoHeader.iSizeImage ); + CPL_SWAP32PTR( &poDS->sInfoHeader.iXPelsPerMeter ); + CPL_SWAP32PTR( &poDS->sInfoHeader.iYPelsPerMeter ); + CPL_SWAP32PTR( &poDS->sInfoHeader.iClrUsed ); + CPL_SWAP32PTR( &poDS->sInfoHeader.iClrImportant ); + // rcg, swap win4/5 fields. + CPL_SWAP32PTR( &poDS->sInfoHeader.iRedMask ); + CPL_SWAP32PTR( &poDS->sInfoHeader.iGreenMask ); + CPL_SWAP32PTR( &poDS->sInfoHeader.iBlueMask ); +#endif + poDS->nColorElems = 4; + } + + if ( eBMPType == BMPT_OS22 ) + { + poDS->nColorElems = 3; // FIXME: different info in different documents regarding this! + } + + if ( eBMPType == BMPT_OS21 ) + { + GInt16 iShort; + + VSIFReadL( &iShort, 1, 2, poDS->fp ); + poDS->sInfoHeader.iWidth = CPL_LSBWORD16( iShort ); + VSIFReadL( &iShort, 1, 2, poDS->fp ); + poDS->sInfoHeader.iHeight = CPL_LSBWORD16( iShort ); + VSIFReadL( &iShort, 1, 2, poDS->fp ); + poDS->sInfoHeader.iPlanes = CPL_LSBWORD16( iShort ); + VSIFReadL( &iShort, 1, 2, poDS->fp ); + poDS->sInfoHeader.iBitCount = CPL_LSBWORD16( iShort ); + poDS->sInfoHeader.iCompression = BMPC_RGB; + poDS->nColorElems = 3; + } + + if ( poDS->sInfoHeader.iBitCount != 1 && + poDS->sInfoHeader.iBitCount != 4 && + poDS->sInfoHeader.iBitCount != 8 && + poDS->sInfoHeader.iBitCount != 16 && + poDS->sInfoHeader.iBitCount != 24 && + poDS->sInfoHeader.iBitCount != 32 ) + { + delete poDS; + return NULL; + } + +#ifdef BMP_DEBUG + CPLDebug( "BMP", "Windows Device Independent Bitmap parameters:\n" + " info header size: %d bytes\n" + " width: %d\n height: %d\n planes: %d\n bpp: %d\n" + " compression: %d\n image size: %d bytes\n X resolution: %d\n" + " Y resolution: %d\n colours used: %d\n colours important: %d", + poDS->sInfoHeader.iSize, + poDS->sInfoHeader.iWidth, poDS->sInfoHeader.iHeight, + poDS->sInfoHeader.iPlanes, poDS->sInfoHeader.iBitCount, + poDS->sInfoHeader.iCompression, poDS->sInfoHeader.iSizeImage, + poDS->sInfoHeader.iXPelsPerMeter, poDS->sInfoHeader.iYPelsPerMeter, + poDS->sInfoHeader.iClrUsed, poDS->sInfoHeader.iClrImportant ); +#endif + + poDS->nRasterXSize = poDS->sInfoHeader.iWidth; + poDS->nRasterYSize = (poDS->sInfoHeader.iHeight > 0)? + poDS->sInfoHeader.iHeight:-poDS->sInfoHeader.iHeight; + + if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid dimensions : %d x %d", + poDS->nRasterXSize, poDS->nRasterYSize); + delete poDS; + return NULL; + } + + switch ( poDS->sInfoHeader.iBitCount ) + { + case 1: + case 4: + case 8: + { + int i; + + poDS->nBands = 1; + // Allocate memory for colour table and read it + if ( poDS->sInfoHeader.iClrUsed ) + poDS->nColorTableSize = poDS->sInfoHeader.iClrUsed; + else + poDS->nColorTableSize = 1 << poDS->sInfoHeader.iBitCount; + poDS->pabyColorTable = + (GByte *)VSIMalloc2( poDS->nColorElems, poDS->nColorTableSize ); + if (poDS->pabyColorTable == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Color palette will be ignored"); + poDS->nColorTableSize = 0; + break; + } + + VSIFSeekL( poDS->fp, BFH_SIZE + poDS->sInfoHeader.iSize, SEEK_SET ); + VSIFReadL( poDS->pabyColorTable, poDS->nColorElems, + poDS->nColorTableSize, poDS->fp ); + + GDALColorEntry oEntry; + poDS->poColorTable = new GDALColorTable(); + for( i = 0; i < poDS->nColorTableSize; i++ ) + { + oEntry.c1 = poDS->pabyColorTable[i * poDS->nColorElems + 2]; // Red + oEntry.c2 = poDS->pabyColorTable[i * poDS->nColorElems + 1]; // Green + oEntry.c3 = poDS->pabyColorTable[i * poDS->nColorElems]; // Blue + oEntry.c4 = 255; + + poDS->poColorTable->SetColorEntry( i, &oEntry ); + } + } + break; + case 16: + case 24: + case 32: + poDS->nBands = 3; + break; + default: + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int iBand; + + if ( poDS->sInfoHeader.iCompression == BMPC_RGB + || poDS->sInfoHeader.iCompression == BMPC_BITFIELDS ) + { + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + { + BMPRasterBand* band = new BMPRasterBand( poDS, iBand ); + poDS->SetBand( iBand, band ); + if (band->pabyScan == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The BMP file is probably corrupted or too large. Image width = %d", poDS->nRasterXSize); + delete poDS; + return NULL; + } + } + } + else if ( poDS->sInfoHeader.iCompression == BMPC_RLE8 + || poDS->sInfoHeader.iCompression == BMPC_RLE4 ) + { + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + { + BMPComprRasterBand* band = new BMPComprRasterBand( poDS, iBand ); + poDS->SetBand( iBand, band); + if (band->pabyUncomprBuf == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The BMP file is probably corrupted or too large. Image width = %d", poDS->nRasterXSize); + delete poDS; + return NULL; + } + } + } + else + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Check for world file. */ +/* -------------------------------------------------------------------- */ + poDS->bGeoTransformValid = + GDALReadWorldFile( poOpenInfo->pszFilename, NULL, + poDS->adfGeoTransform ); + + if( !poDS->bGeoTransformValid ) + poDS->bGeoTransformValid = + GDALReadWorldFile( poOpenInfo->pszFilename, ".wld", + poDS->adfGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *BMPDataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char **papszOptions ) + +{ + if( eType != GDT_Byte ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create BMP dataset with an illegal\n" + "data type (%s), only Byte supported by the format.\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + + if( nBands != 1 && nBands != 3 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "BMP driver doesn't support %d bands. Must be 1 or 3.\n", + nBands ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + BMPDataset *poDS; + + poDS = new BMPDataset(); + + poDS->fp = VSIFOpenL( pszFilename, "wb+" ); + if( poDS->fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create file %s.\n", + pszFilename ); + delete poDS; + return NULL; + } + + poDS->pszFilename = CPLStrdup(pszFilename); + +/* -------------------------------------------------------------------- */ +/* Fill the BMPInfoHeader */ +/* -------------------------------------------------------------------- */ + GUInt32 nScanSize; + + poDS->sInfoHeader.iSize = 40; + poDS->sInfoHeader.iWidth = nXSize; + poDS->sInfoHeader.iHeight = nYSize; + poDS->sInfoHeader.iPlanes = 1; + poDS->sInfoHeader.iBitCount = ( nBands == 3 )?24:8; + poDS->sInfoHeader.iCompression = BMPC_RGB; + + /* XXX: Avoid integer overflow. We can calculate size in one + * step using + * + * nScanSize = ((poDS->sInfoHeader.iWidth * + * poDS->sInfoHeader.iBitCount + 31) & ~31) / 8 + * + * formulae, but we should check for overflow conditions + * during calculation. + */ + nScanSize = + (GUInt32)poDS->sInfoHeader.iWidth * poDS->sInfoHeader.iBitCount + 31; + if ( !poDS->sInfoHeader.iWidth + || !poDS->sInfoHeader.iBitCount + || (nScanSize - 31) / poDS->sInfoHeader.iBitCount + != (GUInt32)poDS->sInfoHeader.iWidth ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Wrong image parameters; " + "can't allocate space for scanline buffer" ); + delete poDS; + + return NULL; + } + nScanSize = (nScanSize & ~31) / 8; + + poDS->sInfoHeader.iSizeImage = nScanSize * poDS->sInfoHeader.iHeight; + poDS->sInfoHeader.iXPelsPerMeter = 0; + poDS->sInfoHeader.iYPelsPerMeter = 0; + poDS->nColorElems = 4; + +/* -------------------------------------------------------------------- */ +/* Do we need colour table? */ +/* -------------------------------------------------------------------- */ + unsigned int i; + + if ( nBands == 1 ) + { + poDS->sInfoHeader.iClrUsed = 1 << poDS->sInfoHeader.iBitCount; + poDS->pabyColorTable = + (GByte *) CPLMalloc( poDS->nColorElems * poDS->sInfoHeader.iClrUsed ); + for ( i = 0; i < poDS->sInfoHeader.iClrUsed; i++ ) + { + poDS->pabyColorTable[i * poDS->nColorElems] = + poDS->pabyColorTable[i * poDS->nColorElems + 1] = + poDS->pabyColorTable[i * poDS->nColorElems + 2] = + poDS->pabyColorTable[i * poDS->nColorElems + 3] = (GByte) i; + } + } + else + { + poDS->sInfoHeader.iClrUsed = 0; + } + poDS->sInfoHeader.iClrImportant = 0; + +/* -------------------------------------------------------------------- */ +/* Fill the BMPFileHeader */ +/* -------------------------------------------------------------------- */ + poDS->sFileHeader.bType[0] = 'B'; + poDS->sFileHeader.bType[1] = 'M'; + poDS->sFileHeader.iSize = BFH_SIZE + poDS->sInfoHeader.iSize + + poDS->sInfoHeader.iClrUsed * poDS->nColorElems + + poDS->sInfoHeader.iSizeImage; + poDS->sFileHeader.iReserved1 = 0; + poDS->sFileHeader.iReserved2 = 0; + poDS->sFileHeader.iOffBits = BFH_SIZE + poDS->sInfoHeader.iSize + + poDS->sInfoHeader.iClrUsed * poDS->nColorElems; + +/* -------------------------------------------------------------------- */ +/* Write all structures to the file */ +/* -------------------------------------------------------------------- */ + if( VSIFWriteL( &poDS->sFileHeader.bType, 1, 2, poDS->fp ) != 2 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Write of first 2 bytes to BMP file %s failed.\n" + "Is file system full?", + pszFilename ); + delete poDS; + + return NULL; + } + + GInt32 iLong; + GUInt32 iULong; + GUInt16 iUShort; + + iULong = CPL_LSBWORD32( poDS->sFileHeader.iSize ); + VSIFWriteL( &iULong, 4, 1, poDS->fp ); + iUShort = CPL_LSBWORD16( poDS->sFileHeader.iReserved1 ); + VSIFWriteL( &iUShort, 2, 1, poDS->fp ); + iUShort = CPL_LSBWORD16( poDS->sFileHeader.iReserved2 ); + VSIFWriteL( &iUShort, 2, 1, poDS->fp ); + iULong = CPL_LSBWORD32( poDS->sFileHeader.iOffBits ); + VSIFWriteL( &iULong, 4, 1, poDS->fp ); + + iULong = CPL_LSBWORD32( poDS->sInfoHeader.iSize ); + VSIFWriteL( &iULong, 4, 1, poDS->fp ); + iLong = CPL_LSBWORD32( poDS->sInfoHeader.iWidth ); + VSIFWriteL( &iLong, 4, 1, poDS->fp ); + iLong = CPL_LSBWORD32( poDS->sInfoHeader.iHeight ); + VSIFWriteL( &iLong, 4, 1, poDS->fp ); + iUShort = CPL_LSBWORD16( poDS->sInfoHeader.iPlanes ); + VSIFWriteL( &iUShort, 2, 1, poDS->fp ); + iUShort = CPL_LSBWORD16( poDS->sInfoHeader.iBitCount ); + VSIFWriteL( &iUShort, 2, 1, poDS->fp ); + iULong = CPL_LSBWORD32( poDS->sInfoHeader.iCompression ); + VSIFWriteL( &iULong, 4, 1, poDS->fp ); + iULong = CPL_LSBWORD32( poDS->sInfoHeader.iSizeImage ); + VSIFWriteL( &iULong, 4, 1, poDS->fp ); + iLong = CPL_LSBWORD32( poDS->sInfoHeader.iXPelsPerMeter ); + VSIFWriteL( &iLong, 4, 1, poDS->fp ); + iLong = CPL_LSBWORD32( poDS->sInfoHeader.iYPelsPerMeter ); + VSIFWriteL( &iLong, 4, 1, poDS->fp ); + iULong = CPL_LSBWORD32( poDS->sInfoHeader.iClrUsed ); + VSIFWriteL( &iULong, 4, 1, poDS->fp ); + iULong = CPL_LSBWORD32( poDS->sInfoHeader.iClrImportant ); + VSIFWriteL( &iULong, 4, 1, poDS->fp ); + + if ( poDS->sInfoHeader.iClrUsed ) + { + if( VSIFWriteL( poDS->pabyColorTable, 1, + poDS->nColorElems * poDS->sInfoHeader.iClrUsed, poDS->fp ) + != poDS->nColorElems * poDS->sInfoHeader.iClrUsed ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Error writing color table. Is disk full?" ); + delete poDS; + + return NULL; + } + } + + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->eAccess = GA_Update; + poDS->nBands = nBands; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + { + poDS->SetBand( iBand, new BMPRasterBand( poDS, iBand ) ); + } + +/* -------------------------------------------------------------------- */ +/* Do we need a world file? */ +/* -------------------------------------------------------------------- */ + if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) ) + poDS->bGeoTransformValid = TRUE; + + return (GDALDataset *) poDS; +} + +/************************************************************************/ +/* GDALRegister_BMP() */ +/************************************************************************/ + +void GDALRegister_BMP() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "BMP" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "BMP" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "MS Windows Device Independent Bitmap" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_bmp.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "bmp" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = BMPDataset::Open; + poDriver->pfnCreate = BMPDataset::Create; + poDriver->pfnIdentify = BMPDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/bmp/frmt_bmp.html b/bazaar/plugin/gdal/frmts/bmp/frmt_bmp.html new file mode 100644 index 000000000..77a9aff8c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bmp/frmt_bmp.html @@ -0,0 +1,45 @@ + + + BMP --- Microsoft Windows Device Independent Bitmap + + + + +

BMP --- Microsoft Windows Device Independent Bitmap

+ +MS Windows Device Independent Bitmaps supported by the Windows kernel and +mostly used for storing system decoration images. Due to the nature of the +BMP format it has several restrictions and could not be used for general image +storing. In particular, you can create only 1-bit monochrome, +8-bit pseudocoloured and 24-bit RGB images only. Even grayscale images must +be saved in pseudocolour form.

+ +This driver supports reading almost any type of the BMP files and could write +ones which should be supported on any Windows system. Only single- or three- +band files could be saved in BMP file. Input values will be resampled to 8 +bit.

+ +If an ESRI world file exists with the .bpw, .bmpw or .wld extension, it will be read and +used to establish the geotransform for the image.

+ +

Creation Options

+
    +
  • WORLDFILE=YES: Force the generation of an associated + ESRI world file (with the extension .wld). +

    +

+ +

See Also:

+ + + + + + diff --git a/bazaar/plugin/gdal/frmts/bmp/makefile.vc b/bazaar/plugin/gdal/frmts/bmp/makefile.vc new file mode 100644 index 000000000..616045869 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bmp/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = bmpdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/bpg/bpgdataset.cpp b/bazaar/plugin/gdal/frmts/bpg/bpgdataset.cpp new file mode 100644 index 000000000..4aefc4966 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bpg/bpgdataset.cpp @@ -0,0 +1,361 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL BPG Driver + * Purpose: Implement GDAL BPG Support based on libbpg + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "gdal_pam.h" +#include "cpl_string.h" + +// NOTE: build instructions +// bpg Makefile needs to be modified to have -fPIC on CFLAGS:= at line 49 and LDFLAGS at line 54 before building bpg +// g++ -fPIC -g -Wall -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -I/home/even/libbpg-0.9.4 frmts/bpg/bpgdataset.cpp -shared -o gdal_BPG.so -L. -lgdal -L/home/even/libbpg-0.9.4/ -lbpg + +CPL_C_START +#include "libbpg.h" +CPL_C_END + +CPL_CVSID("$Id$"); + +CPL_C_START +void GDALRegister_BPG(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* BPGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class BPGRasterBand; + +class BPGDataset : public GDALPamDataset +{ + friend class BPGRasterBand; + + VSILFILE* fpImage; + GByte* pabyUncompressed; + int bHasBeenUncompressed; + CPLErr eUncompressErrRet; + CPLErr Uncompress(); + + public: + BPGDataset(); + ~BPGDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* BPGRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class BPGRasterBand : public GDALPamRasterBand +{ + friend class BPGDataset; + + public: + + BPGRasterBand( BPGDataset *, int nbits ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); +}; + +/************************************************************************/ +/* BPGRasterBand() */ +/************************************************************************/ + +BPGRasterBand::BPGRasterBand( BPGDataset *poDS, int nbits ) +{ + this->poDS = poDS; + + eDataType = (nbits > 8) ? GDT_UInt16 : GDT_Byte; + + nBlockXSize = poDS->nRasterXSize; + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr BPGRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + BPGDataset* poGDS = (BPGDataset*) poDS; + + if( poGDS->Uncompress() != CE_None ) + return CE_Failure; + + int i; + int iBand = nBand; + if( poGDS->nBands == 2 ) + iBand = 4; + if( eDataType == GDT_Byte ) + { + GByte* pabyUncompressed = + &poGDS->pabyUncompressed[nBlockYOff * nRasterXSize * poGDS->nBands + iBand - 1]; + for(i=0;inBands * i]; + } + else + { + GUInt16* pasUncompressed = (GUInt16*) + &poGDS->pabyUncompressed[(nBlockYOff * nRasterXSize * poGDS->nBands + iBand - 1) * 2]; + for(i=0;inBands * i]; + } + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp BPGRasterBand::GetColorInterpretation() + +{ + BPGDataset* poGDS = (BPGDataset*) poDS; + if( poGDS->nBands >= 3 ) + return (GDALColorInterp) (GCI_RedBand + (nBand - 1)); + else if( nBand == 1 ) + return GCI_GrayIndex; + else + return GCI_AlphaBand; +} + +/************************************************************************/ +/* ==================================================================== */ +/* BPGDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* BPGDataset() */ +/************************************************************************/ + +BPGDataset::BPGDataset() + +{ + fpImage = NULL; + pabyUncompressed = NULL; + bHasBeenUncompressed = FALSE; + eUncompressErrRet = CE_None; +} + +/************************************************************************/ +/* ~BPGDataset() */ +/************************************************************************/ + +BPGDataset::~BPGDataset() + +{ + FlushCache(); + if (fpImage) + VSIFCloseL(fpImage); + VSIFree(pabyUncompressed); +} + +/************************************************************************/ +/* Uncompress() */ +/************************************************************************/ + +CPLErr BPGDataset::Uncompress() +{ + if (bHasBeenUncompressed) + return eUncompressErrRet; + bHasBeenUncompressed = TRUE; + eUncompressErrRet = CE_Failure; + + int nOutBands = (nBands < 3) ? nBands + 2 : nBands; + int nDTSize = ( GetRasterBand(1)->GetRasterDataType() == GDT_Byte ) ? 1 : 2; + pabyUncompressed = (GByte*)VSIMalloc3(nRasterXSize, nRasterYSize, + nOutBands * nDTSize); + if (pabyUncompressed == NULL) + return CE_Failure; + + VSIFSeekL(fpImage, 0, SEEK_END); + vsi_l_offset nSize = VSIFTellL(fpImage); + if (nSize != (vsi_l_offset)(int)nSize) + return CE_Failure; + VSIFSeekL(fpImage, 0, SEEK_SET); + uint8_t* pabyCompressed = (uint8_t*)VSIMalloc(nSize); + if (pabyCompressed == NULL) + return CE_Failure; + VSIFReadL(pabyCompressed, 1, nSize, fpImage); + + BPGDecoderContext* ctxt = bpg_decoder_open(); + if( ctxt == NULL ) + { + VSIFree(pabyCompressed); + return CE_Failure; + } + BPGDecoderOutputFormat eOutputFormat; + + if( GetRasterBand(1)->GetRasterDataType() == GDT_Byte ) + eOutputFormat = (nBands == 1 || nBands == 3) ? BPG_OUTPUT_FORMAT_RGB24 : + BPG_OUTPUT_FORMAT_RGBA32; + else + eOutputFormat = (nBands == 1 || nBands == 3) ? BPG_OUTPUT_FORMAT_RGB48 : + BPG_OUTPUT_FORMAT_RGBA64; + if( bpg_decoder_decode(ctxt, pabyCompressed, (int)nSize) < 0 || + bpg_decoder_start(ctxt, eOutputFormat) < 0 ) + { + bpg_decoder_close(ctxt); + VSIFree(pabyCompressed); + return CE_Failure; + } + + for(int i=0;inHeaderBytes < BPG_DECODER_INFO_BUF_SIZE ) + return FALSE; + + return memcmp(poOpenInfo->pabyHeader, "BPG\xfb", 4) == 0; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *BPGDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) || poOpenInfo->fpL == NULL ) + return NULL; + + BPGImageInfo imageInfo; + if( bpg_decoder_get_info_from_buf(&imageInfo, NULL, + poOpenInfo->pabyHeader, + poOpenInfo->nHeaderBytes) < 0 ) + return NULL; + + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The BPG driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + BPGDataset *poDS; + + poDS = new BPGDataset(); + poDS->nRasterXSize = imageInfo.width; + poDS->nRasterYSize = imageInfo.height; + poDS->fpImage = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int nBands = ( imageInfo.format == BPG_FORMAT_GRAY ) ? 1 : 3; + if( imageInfo.has_alpha ) + nBands ++; + + for( int iBand = 0; iBand < nBands; iBand++ ) + poDS->SetBand( iBand+1, new BPGRasterBand( poDS, imageInfo.bit_depth ) ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + + poDS->TryLoadXML( poOpenInfo->GetSiblingFiles() ); + +/* -------------------------------------------------------------------- */ +/* Open overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, + poOpenInfo->GetSiblingFiles() ); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_BPG() */ +/************************************************************************/ + +void GDALRegister_BPG() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "BPG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "BPG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Better Portable Graphics" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_bpg.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "bpg" ); + //poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/bpg" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = BPGDataset::Identify; + poDriver->pfnOpen = BPGDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/bsb/GNUmakefile b/bazaar/plugin/gdal/frmts/bsb/GNUmakefile new file mode 100644 index 000000000..971e9ce73 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bsb/GNUmakefile @@ -0,0 +1,37 @@ + + +VERSION = 1.3 + +include ../../GDALmake.opt + +OBJ = bsb_read.o bsbdataset.o + +DISTDIR = bsb-$(VERSION) + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +bsb2raw$(EXE): bsb2raw.$(OBJ_EXT) + $(LD) $(LDFLAGS) bsb2raw.$(OBJ_EXT) $(CONFIG_LIBS) -o bsb2raw$(EXE) + + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +dist: + rm -rf $(DISTDIR) + mkdir $(DISTDIR) + cp *.cpp *.c *.h $(DISTDIR) + rm $(DISTDIR)/bsbdataset.cpp + cp Makefile.dist $(DISTDIR)/Makefile + cp README.dist $(DISTDIR)/README + cp ../../port/{cpl_error*,cpl_port*,cpl_string*} $(DISTDIR) + cp ../../port/{cpl_vsisimple.cpp,cpl_config.h.in} $(DISTDIR) + cp ../../port/{cpl_vsi.h,cpl_conv.*,cpl_path.cpp} $(DISTDIR) + cp ../../port/cpl_config.h.in $(DISTDIR)/cpl_config.h + rm $(DISTDIR)/*.o + tar czf $(DISTDIR).tar.gz $(DISTDIR) + zip -r $(DISTDIR).zip $(DISTDIR) diff --git a/bazaar/plugin/gdal/frmts/bsb/Makefile.dist b/bazaar/plugin/gdal/frmts/bsb/Makefile.dist new file mode 100644 index 000000000..05725a5a3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bsb/Makefile.dist @@ -0,0 +1,2 @@ +bsb2raw: + $(CXX) $(CFLAGS) *.c *.cpp -o bsb2raw diff --git a/bazaar/plugin/gdal/frmts/bsb/README.dist b/bazaar/plugin/gdal/frmts/bsb/README.dist new file mode 100644 index 000000000..c230232a0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bsb/README.dist @@ -0,0 +1,48 @@ + Sample BSB Reader + ================= + + +Files +----- + + bsb2raw.c: Sample mainline program for converting BSB files to "raw" format. + + bsb_read.c: Core BSB reading code ... you may use this in your application. + bsb_read.h: Declarations for functions and structures in bsb_read.c. #include + this from your application. + + cpl_*: Supporting low level code and include files. + + Makefile: Simple Unix style makefile for building bsb2raw program. + +NOTE: It may be necessary to fool with the cpl_config.h include file for +different platforms. In particular, change the #undef WORDS_BIGENDIAN to +a #define WORDS_BIGENDIAN for big endian systems (Sparc, SGI, etc). Some +changes may be needed in cpl_config.h for Windows. + + +License +------- + +MIT/X license ... see header of bsb_read.c. + +On the Web +---------- + +http://gdal.velocet.ca/projects/bsb/ + +GDAL +---- + +Note that this BSB code is normally incorporated into the larger GDAL +library. To convert BSB to other common formats like GeoTIFF, PNG, etc +consider using the GDAL utilities at: + + http://www.remotesensing.org/gdal + + +July 19, 2002 +Frank Warmerdam + + + diff --git a/bazaar/plugin/gdal/frmts/bsb/bsb2raw.c b/bazaar/plugin/gdal/frmts/bsb/bsb2raw.c new file mode 100644 index 000000000..c7a6dcb1e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bsb/bsb2raw.c @@ -0,0 +1,109 @@ +/****************************************************************************** + * $Id: bsb2raw.c 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: BSB Reader + * Purpose: Test program for dumping BSB to PPM raster format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_error.h" +#include "cpl_vsi.h" +#include "cpl_conv.h" +#include "bsb_read.h" + +CPL_CVSID("$Id: bsb2raw.c 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* main() */ +/************************************************************************/ + +int main( int nArgc, char **papszArgv ) + +{ + BSBInfo *psInfo; + int iLine, i; + GByte *pabyScanline; + FILE *fp; + int nError = 0; + + if( nArgc < 3 ) + { + fprintf( stderr, "Usage: bsb2raw src_file dst_file\n" ); + exit( 1 ); + } + + psInfo = BSBOpen( papszArgv[1] ); + if( psInfo == NULL ) + exit( 1 ); + + fp = VSIFOpen( papszArgv[2], "wb" ); + if( fp == NULL ) + { + perror( "open" ); + exit( 1 ); + } + + pabyScanline = (GByte *) CPLMalloc(psInfo->nXSize); + for( iLine = 0; iLine < psInfo->nYSize; iLine++ ) + { + if( !BSBReadScanline( psInfo, iLine, pabyScanline ) ) + nError++; + + VSIFWrite( pabyScanline, 1, psInfo->nXSize, fp ); + } + + VSIFClose( fp ); + + if( nError > 0 ) + fprintf( stderr, "Read failed for %d scanlines out of %d.\n", + nError, psInfo->nYSize ); + +/* -------------------------------------------------------------------- */ +/* Write .aux file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpen( CPLResetExtension( papszArgv[2], "aux" ), "wt" ); + + fprintf( fp, "AuxilaryTarget: %s\n", + CPLGetFilename(papszArgv[2]) ); + + fprintf( fp, "RawDefinition: %d %d 1\n", + psInfo->nXSize, psInfo->nYSize ); + + fprintf( fp, "ChanDefinition-1: 8U 0 1 %d Swapped\n", + psInfo->nXSize ); + + for( i = 0; i < psInfo->nPCTSize; i++ ) + fprintf( fp, "METADATA_IMG_1_Class_%d_Color: (RGB:%d %d %d)\n", + i, + psInfo->pabyPCT[i*3 + 0], + psInfo->pabyPCT[i*3 + 1], + psInfo->pabyPCT[i*3 + 2] ); + + VSIFClose( fp ); + + + exit( 0 ); +} + + diff --git a/bazaar/plugin/gdal/frmts/bsb/bsb_read.c b/bazaar/plugin/gdal/frmts/bsb/bsb_read.c new file mode 100644 index 000000000..f3f3f4e7f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bsb/bsb_read.c @@ -0,0 +1,1080 @@ +/****************************************************************************** + * $Id: bsb_read.c 28039 2014-11-30 18:24:59Z rouault $ + * + * Project: BSB Reader + * Purpose: Low level BSB Access API Implementation (non-GDAL). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + * NOTE: This code is implemented on the basis of work by Mike Higgins. The + * BSB format is subject to US patent 5,727,090; however, that patent + * apparently only covers *writing* BSB files, not reading them, so this code + * should not be affected. + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "bsb_read.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: bsb_read.c 28039 2014-11-30 18:24:59Z rouault $"); + +static int BSBReadHeaderLine( BSBInfo *psInfo, char* pszLine, int nLineMaxLen, int bNO1 ); +static int BSBSeekAndCheckScanlineNumber ( BSBInfo *psInfo, int nScanline, + int bVerboseIfError ); +/************************************************************************ + +Background: + +To: Frank Warmerdam +From: Mike Higgins +Subject: Re: GISTrans: Maptech / NDI BSB Chart Format +Mime-Version: 1.0 +Content-Type: text/plain; charset="us-ascii"; format=flowed + + I did it! I just wrote a program that reads NOAA BSB chart files +and converts them to BMP files! BMP files are not the final goal of my +project, but it served as a proof-of-concept. Next I will want to write +routines to extract pieces of the file at full resolution for printing, and +routines to filter pieces of the chart for display at lower resolution on +the screen. (One of the terrible things about most chart display programs +is that they all sub-sample the charts instead of filtering it down). How +did I figure out how to read the BSB files? + + If you recall, I have been trying to reverse engineer the file +formats of those nautical charts. When I am between projects I often do a +WEB search for the BSB file format to see if someone else has published a +hack for them. Monday I hit a NOAA project status report that mentioned +some guy named Marty Yellin who had recently completed writing a program to +convert BSB files to other file formats! I searched for him and found him +mentioned as a contact person for some NOAA program. I was composing a +letter to him in my head, or considering calling the NOAA phone number and +asking for his extension number, when I saw another NOAA status report +indicating that he had retired in 1998. His name showed up in a few more +reports, one of which said that he was the inventor of the BSB file format, +that it was patented (#5,727,090), and that the patent had been licensed to +Maptech (the evil company that will not allow anyone using their file +format to convert them to non-proprietary formats). Patents are readily +available on the WEB at the IBM patent server and this one is in the +dtabase! I printed up a copy of the patent and of course it describes very +nicely (despite the usual typos and omissions of referenced items in the +figures) how to write one of these BSB files! + + I was considering talking to a patent lawyer about the legality of +using information in the patent to read files without getting a license, +when I noticed that the patent is only claiming programs that WRITE the +file format. I have noticed this before in RF patents where they describe +how to make a receiver and never bother to claim a transmitter. The logic +is that the transmitter is no good to anybody unless they license receivers +from the patent holder. But I think they did it backwards here! They should +have claimed a program that can READ the described file format. Now I can +read the files, build programs that read the files, and even sell them +without violating the claims in the patent! As long as I never try to write +one of the evil BSB files, I'm OK!!! + + If you ever need to read these BSB chart programs, drop me a +note. I would be happy to send you a copy of this conversion program. + +... later email ... + + Well, here is my little proof of concept program. I hereby give +you permission to distribute it freely, modify for you own use, etc. +I built it as a "WIN32 Console application" which means it runs in an MS +DOS box under Microsoft Windows. But the only Windows specific stuff in it +are the include files for the BMP file headers. If you ripped out the BMP +code it should compile under UNIX or anyplace else. + I'd be overjoyed to have you announce it to GISTrans or anywhere +else. I'm philosophically opposed to the proprietary treatment of the BSB +file format and I want to break it open! Chart data for the People! + + ************************************************************************/ + +/************************************************************************/ +/* BSBUngetc() */ +/************************************************************************/ + +static +void BSBUngetc( BSBInfo *psInfo, int nCharacter ) + +{ + CPLAssert( psInfo->nSavedCharacter == -1000 ); + psInfo->nSavedCharacter = nCharacter; +} + +/************************************************************************/ +/* BSBGetc() */ +/************************************************************************/ + +static +int BSBGetc( BSBInfo *psInfo, int bNO1, int* pbErrorFlag ) + +{ + int nByte; + + if( psInfo->nSavedCharacter != -1000 ) + { + nByte = psInfo->nSavedCharacter; + psInfo->nSavedCharacter = -1000; + return nByte; + } + + if( psInfo->nBufferOffset >= psInfo->nBufferSize ) + { + psInfo->nBufferOffset = 0; + psInfo->nBufferSize = + VSIFReadL( psInfo->pabyBuffer, 1, psInfo->nBufferAllocation, + psInfo->fp ); + if( psInfo->nBufferSize <= 0 ) + { + if (pbErrorFlag) + *pbErrorFlag = TRUE; + return 0; + } + } + + nByte = psInfo->pabyBuffer[psInfo->nBufferOffset++]; + + if( bNO1 ) + { + nByte = nByte - 9; + if( nByte < 0 ) + nByte = nByte + 256; + } + + return nByte; +} + + +/************************************************************************/ +/* BSBOpen() */ +/* */ +/* Read BSB header, and return information. */ +/************************************************************************/ + +BSBInfo *BSBOpen( const char *pszFilename ) + +{ + VSILFILE *fp; + char achTestBlock[1000]; + char szLine[1000]; + int i, bNO1 = FALSE; + BSBInfo *psInfo; + int nSkipped = 0; + const char *pszPalette; + int nOffsetFirstLine; + int bErrorFlag = FALSE; + +/* -------------------------------------------------------------------- */ +/* Which palette do we want to use? */ +/* -------------------------------------------------------------------- */ + pszPalette = CPLGetConfigOption( "BSB_PALETTE", "RGB" ); + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszFilename, "rb" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "File %s not found.", pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the first 1000 bytes, and verify that it contains the */ +/* "BSB/" keyword" */ +/* -------------------------------------------------------------------- */ + if( VSIFReadL( achTestBlock, 1, sizeof(achTestBlock), fp ) + != sizeof(achTestBlock) ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_FileIO, + "Could not read first %d bytes for header!", + (int) sizeof(achTestBlock) ); + return NULL; + } + + for( i = 0; (size_t)i < sizeof(achTestBlock) - 4; i++ ) + { + /* Test for "BSB/" */ + if( achTestBlock[i+0] == 'B' && achTestBlock[i+1] == 'S' + && achTestBlock[i+2] == 'B' && achTestBlock[i+3] == '/' ) + break; + + /* Test for "NOS/" */ + if( achTestBlock[i+0] == 'N' && achTestBlock[i+1] == 'O' + && achTestBlock[i+2] == 'S' && achTestBlock[i+3] == '/' ) + break; + + /* Test for "NOS/" offset by 9 in ASCII for NO1 files */ + if( achTestBlock[i+0] == 'W' && achTestBlock[i+1] == 'X' + && achTestBlock[i+2] == '\\' && achTestBlock[i+3] == '8' ) + { + bNO1 = TRUE; + break; + } + } + + if( i == sizeof(achTestBlock) - 4 ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_AppDefined, + "This does not appear to be a BSB file, no BSB/ header." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create info structure. */ +/* -------------------------------------------------------------------- */ + psInfo = (BSBInfo *) CPLCalloc(1,sizeof(BSBInfo)); + psInfo->fp = fp; + psInfo->bNO1 = bNO1; + + psInfo->nBufferAllocation = 1024; + psInfo->pabyBuffer = (GByte *) CPLMalloc(psInfo->nBufferAllocation); + psInfo->nBufferSize = 0; + psInfo->nBufferOffset = 0; + psInfo->nSavedCharacter = -1000; + +/* -------------------------------------------------------------------- */ +/* Rewind, and read line by line. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( fp, 0, SEEK_SET ); + + while( BSBReadHeaderLine(psInfo, szLine, sizeof(szLine), bNO1) ) + { + char **papszTokens = NULL; + int nCount = 0; + + if( szLine[0] != '\0' && szLine[1] != '\0' && szLine[2] != '\0' && szLine[3] == '/' ) + { + psInfo->papszHeader = CSLAddString( psInfo->papszHeader, szLine ); + papszTokens = CSLTokenizeStringComplex( szLine+4, ",=", + FALSE,FALSE); + nCount = CSLCount(papszTokens); + } + else if( EQUALN(szLine," ",4) && szLine[4] != ' ' ) + { + /* add extension lines to the last header line. */ + int iTargetHeader = CSLCount(psInfo->papszHeader); + + if( iTargetHeader != -1 ) + { + psInfo->papszHeader[iTargetHeader] = (char *) + CPLRealloc(psInfo->papszHeader[iTargetHeader], + strlen(psInfo->papszHeader[iTargetHeader]) + + strlen(szLine) + 5 ); + strcat( psInfo->papszHeader[iTargetHeader], "," ); + strcat( psInfo->papszHeader[iTargetHeader], szLine+4 ); + } + } + + if( EQUALN(szLine,"BSB/",4) ) + { + int nRAIndex; + + nRAIndex = CSLFindString(papszTokens, "RA" ); + if( nRAIndex < 0 || nRAIndex+2 >= nCount ) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to extract RA from BSB/ line." ); + BSBClose( psInfo ); + return NULL; + } + psInfo->nXSize = atoi(papszTokens[nRAIndex+1]); + psInfo->nYSize = atoi(papszTokens[nRAIndex+2]); + } + else if( EQUALN(szLine,"NOS/",4) ) + { + int nRAIndex; + + nRAIndex = CSLFindString(papszTokens, "RA" ); + if( nRAIndex < 0 || nRAIndex+4 >= nCount ) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to extract RA from NOS/ line." ); + BSBClose( psInfo ); + return NULL; + } + psInfo->nXSize = atoi(papszTokens[nRAIndex+3]); + psInfo->nYSize = atoi(papszTokens[nRAIndex+4]); + } + else if( EQUALN(szLine, pszPalette, 3) && szLine[3] == '/' + && nCount >= 4 ) + { + int iPCT = atoi(papszTokens[0]); + if (iPCT < 0 || iPCT > 128) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_OutOfMemory, + "BSBOpen : Invalid color table index. Probably due to corrupted BSB file (iPCT = %d).", + iPCT); + BSBClose( psInfo ); + return NULL; + } + if( iPCT > psInfo->nPCTSize-1 ) + { + unsigned char* pabyNewPCT = (unsigned char *) + VSIRealloc(psInfo->pabyPCT,(iPCT+1) * 3); + if (pabyNewPCT == NULL) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_OutOfMemory, + "BSBOpen : Out of memory. Probably due to corrupted BSB file (iPCT = %d).", + iPCT); + BSBClose( psInfo ); + return NULL; + } + psInfo->pabyPCT = pabyNewPCT; + memset( psInfo->pabyPCT + psInfo->nPCTSize*3, 0, + (iPCT+1-psInfo->nPCTSize) * 3); + psInfo->nPCTSize = iPCT+1; + } + + psInfo->pabyPCT[iPCT*3+0] = (unsigned char)atoi(papszTokens[1]); + psInfo->pabyPCT[iPCT*3+1] = (unsigned char)atoi(papszTokens[2]); + psInfo->pabyPCT[iPCT*3+2] = (unsigned char)atoi(papszTokens[3]); + } + else if( EQUALN(szLine,"VER/",4) && nCount >= 1 ) + { + psInfo->nVersion = (int) (100 * CPLAtof(papszTokens[0]) + 0.5); + } + + CSLDestroy( papszTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Verify we found required keywords. */ +/* -------------------------------------------------------------------- */ + if( psInfo->nXSize == 0 || psInfo->nPCTSize == 0 ) + { + BSBClose( psInfo ); + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find required RGB/ or BSB/ keyword in header." ); + + return NULL; + } + + if( psInfo->nXSize <= 0 || psInfo->nYSize <= 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Wrong dimensions found in header : %d x %d.", + psInfo->nXSize, psInfo->nYSize ); + BSBClose( psInfo ); + return NULL; + } + + if( psInfo->nVersion == 0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "VER (version) keyword not found, assuming 2.0." ); + psInfo->nVersion = 200; + } + +/* -------------------------------------------------------------------- */ +/* If all has gone well this far, we should be pointing at the */ +/* sequence "0x1A 0x00". Read past to get to start of data. */ +/* */ +/* We actually do some funny stuff here to be able to read past */ +/* some garbage to try and find the 0x1a 0x00 sequence since in */ +/* at least some files (ie. optech/World.kap) we find a few */ +/* bytes of extra junk in the way. */ +/* -------------------------------------------------------------------- */ +/* from optech/World.kap + + 11624: 30333237 34353938 2C302E30 35373836 03274598,0.05786 + 11640: 39303232 38332C31 332E3135 39363435 902283,13.159645 + 11656: 35390D0A 1A0D0A1A 00040190 C0510002 59~~~~~~~~~~~Q~~ + 11672: 90C05100 0390C051 000490C0 51000590 ~~Q~~~~Q~~~~Q~~~ + */ + + { + int nChar = -1; + + while( nSkipped < 100 + && (BSBGetc( psInfo, bNO1, &bErrorFlag ) != 0x1A + || (nChar = BSBGetc( psInfo, bNO1, &bErrorFlag )) != 0x00) + && !bErrorFlag) + { + if( nChar == 0x1A ) + { + BSBUngetc( psInfo, nChar ); + nChar = -1; + } + nSkipped++; + } + + if( bErrorFlag ) + { + BSBClose( psInfo ); + CPLError( CE_Failure, CPLE_FileIO, + "Truncated BSB file or I/O error." ); + return NULL; + } + + if( nSkipped == 100 ) + { + BSBClose( psInfo ); + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find compressed data segment of BSB file." ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Read the number of bit size of color numbers. */ +/* -------------------------------------------------------------------- */ + psInfo->nColorSize = BSBGetc( psInfo, bNO1, NULL ); + + /* The USGS files like 83116_1.KAP seem to use the ASCII number instead + of the binary number for the colorsize value. */ + + if( nSkipped > 0 + && psInfo->nColorSize >= 0x31 && psInfo->nColorSize <= 0x38 ) + psInfo->nColorSize -= 0x30; + + if( ! (psInfo->nColorSize > 0 && psInfo->nColorSize < 9) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "BSBOpen : Bad value for nColorSize (%d). Probably due to corrupted BSB file", + psInfo->nColorSize ); + BSBClose( psInfo ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Initialize memory for line offset list. */ +/* -------------------------------------------------------------------- */ + psInfo->panLineOffset = (int *) + VSIMalloc2(sizeof(int), psInfo->nYSize); + if (psInfo->panLineOffset == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "BSBOpen : Out of memory. Probably due to corrupted BSB file (nYSize = %d).", + psInfo->nYSize ); + BSBClose( psInfo ); + return NULL; + } + + /* This is the offset to the data of first line, if there is no index table */ + nOffsetFirstLine = (int)(VSIFTellL( fp ) - psInfo->nBufferSize) + psInfo->nBufferOffset; + +/* -------------------------------------------------------------------- */ +/* Read the line offset list */ +/* -------------------------------------------------------------------- */ + if ( ! CSLTestBoolean(CPLGetConfigOption("BSB_DISABLE_INDEX", "NO")) ) + { + /* build the list from file's index table */ + /* To overcome endian compatibility issues individual + * bytes are being read instead of the whole integers. */ + int nVal; + int listIsOK = 1; + int nOffsetIndexTable; + int nFileLen; + + /* Seek fp to point the last 4 byte integer which points + * the offset of the first line */ + VSIFSeekL( fp, 0, SEEK_END ); + nFileLen = (int)VSIFTellL( fp ); + VSIFSeekL( fp, nFileLen - 4, SEEK_SET ); + + VSIFReadL(&nVal, 1, 4, fp);//last 4 bytes + CPL_MSBPTR32(&nVal); + nOffsetIndexTable = nVal; + + /* For some charts, like 1115A_1.KAP, coming from */ + /* http://www.nauticalcharts.noaa.gov/mcd/Raster/index.htm, */ + /* the index table can have one row less than nYSize */ + /* If we look into the file closely, there is no data for */ + /* that last row (the end of line psInfo->nYSize - 1 is the start */ + /* of the index table), so we can decrement psInfo->nYSize */ + if (nOffsetIndexTable + 4 * (psInfo->nYSize - 1) == nFileLen - 4) + { + CPLDebug("BSB", "Index size is one row shorter than declared image height. Correct this"); + psInfo->nYSize --; + } + + if( nOffsetIndexTable <= nOffsetFirstLine || + nOffsetIndexTable + 4 * psInfo->nYSize > nFileLen - 4) + { + /* The last 4 bytes are not the value of the offset to the index table */ + } + else if (VSIFSeekL( fp, nOffsetIndexTable, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Seek to offset 0x%08x for first line offset failed.", + nOffsetIndexTable); + } + else + { + int nIndexSize = (nFileLen - 4 - nOffsetIndexTable) / 4; + if (nIndexSize != psInfo->nYSize) + { + CPLDebug("BSB", "Index size is %d. Expected %d", + nIndexSize, psInfo->nYSize); + } + + for(i=0; i < psInfo->nYSize; i++) + { + VSIFReadL(&nVal, 1, 4, fp); + CPL_MSBPTR32(&nVal); + psInfo->panLineOffset[i] = nVal; + } + /* Simple checks for the integrity of the list */ + for(i=0; i < psInfo->nYSize; i++) + { + if( psInfo->panLineOffset[i] < nOffsetFirstLine || + psInfo->panLineOffset[i] >= nOffsetIndexTable || + (i < psInfo->nYSize - 1 && psInfo->panLineOffset[i] > psInfo->panLineOffset[i+1]) || + !BSBSeekAndCheckScanlineNumber(psInfo, i, FALSE) ) + { + CPLDebug("BSB", "Index table is invalid at index %d", i); + listIsOK = 0; + break; + } + } + if ( listIsOK ) + { + CPLDebug("BSB", "Index table is valid"); + return psInfo; + } + } + } + + /* If we can't build the offset list for some reason we just + * initialize the offset list to indicate "no value" (except for the first). */ + psInfo->panLineOffset[0] = nOffsetFirstLine; + for( i = 1; i < psInfo->nYSize; i++ ) + psInfo->panLineOffset[i] = -1; + + return psInfo; +} + +/************************************************************************/ +/* BSBReadHeaderLine() */ +/* */ +/* Read one virtual line of text from the BSB header. This */ +/* will end if a 0x1A (EOF) is encountered, indicating the data */ +/* is about to start. It will also merge multiple physical */ +/* lines where appropriate. */ +/************************************************************************/ + +static int BSBReadHeaderLine( BSBInfo *psInfo, char* pszLine, int nLineMaxLen, int bNO1 ) + +{ + char chNext; + int nLineLen = 0; + + while( !VSIFEofL(psInfo->fp) && nLineLen < nLineMaxLen-1 ) + { + chNext = (char) BSBGetc( psInfo, bNO1, NULL ); + if( chNext == 0x1A ) + { + BSBUngetc( psInfo, chNext ); + return FALSE; + } + + /* each CR/LF (or LF/CR) as if just "CR" */ + if( chNext == 10 || chNext == 13 ) + { + char chLF; + + chLF = (char) BSBGetc( psInfo, bNO1, NULL ); + if( chLF != 10 && chLF != 13 ) + BSBUngetc( psInfo, chLF ); + chNext = '\n'; + } + + /* If we are at the end-of-line, check for blank at start + ** of next line, to indicate need of continuation. + */ + if( chNext == '\n' ) + { + char chTest; + + chTest = (char) BSBGetc(psInfo, bNO1, NULL); + /* Are we done? */ + if( chTest != ' ' ) + { + BSBUngetc( psInfo, chTest ); + pszLine[nLineLen] = '\0'; + return TRUE; + } + + /* eat pending spaces */ + while( chTest == ' ' ) + chTest = (char) BSBGetc(psInfo,bNO1, NULL); + BSBUngetc( psInfo,chTest ); + + /* insert comma in data stream */ + pszLine[nLineLen++] = ','; + } + else + { + pszLine[nLineLen++] = chNext; + } + } + + return FALSE; +} + +/************************************************************************/ +/* BSBSeekAndCheckScanlineNumber() */ +/* */ +/* Seek to the beginning of the scanline and check that the */ +/* scanline number in file is consistent with what we expect */ +/* */ +/* @param nScanline zero based line number */ +/************************************************************************/ + +static int BSBSeekAndCheckScanlineNumber ( BSBInfo *psInfo, int nScanline, + int bVerboseIfError ) +{ + int nLineMarker = 0; + int byNext; + VSILFILE *fp = psInfo->fp; + int bErrorFlag = FALSE; + +/* -------------------------------------------------------------------- */ +/* Seek to requested scanline. */ +/* -------------------------------------------------------------------- */ + psInfo->nBufferSize = 0; + if( VSIFSeekL( fp, psInfo->panLineOffset[nScanline], SEEK_SET ) != 0 ) + { + if (bVerboseIfError) + { + CPLError( CE_Failure, CPLE_FileIO, + "Seek to offset %d for scanline %d failed.", + psInfo->panLineOffset[nScanline], nScanline ); + } + else + { + CPLDebug("BSB", "Seek to offset %d for scanline %d failed.", + psInfo->panLineOffset[nScanline], nScanline ); + } + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Read the line number. Pre 2.0 BSB seemed to expect the line */ +/* numbers to be zero based, while 2.0 and later seemed to */ +/* expect it to be one based, and for a 0 to be some sort of */ +/* missing line marker. */ +/* -------------------------------------------------------------------- */ + do { + byNext = BSBGetc( psInfo, psInfo->bNO1, &bErrorFlag ); + + /* Special hack to skip over extra zeros in some files, such + ** as optech/sample1.kap. + */ + while( nScanline != 0 && nLineMarker == 0 && byNext == 0 && !bErrorFlag ) + byNext = BSBGetc( psInfo, psInfo->bNO1, &bErrorFlag ); + + nLineMarker = nLineMarker * 128 + (byNext & 0x7f); + } while( (byNext & 0x80) != 0 ); + + if ( bErrorFlag ) + { + if (bVerboseIfError) + { + CPLError( CE_Failure, CPLE_FileIO, + "Truncated BSB file or I/O error." ); + } + return FALSE; + } + if( nLineMarker != nScanline + && nLineMarker != nScanline + 1 ) + { + int bIgnoreLineNumbers = + CSLTestBoolean(CPLGetConfigOption("BSB_IGNORE_LINENUMBERS", "NO")); + + if (bVerboseIfError && !bIgnoreLineNumbers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Got scanline id %d when looking for %d @ offset %d.\nSet BSB_IGNORE_LINENUMBERS=TRUE configuration option to try file anyways.", + nLineMarker, nScanline+1, psInfo->panLineOffset[nScanline]); + } + else + { + CPLDebug("BSB", "Got scanline id %d when looking for %d @ offset %d.", + nLineMarker, nScanline+1, psInfo->panLineOffset[nScanline]); + } + + if( !bIgnoreLineNumbers ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* BSBReadScanline() */ +/* @param nScanline zero based line number */ +/************************************************************************/ + +int BSBReadScanline( BSBInfo *psInfo, int nScanline, + unsigned char *pabyScanlineBuf ) + +{ + int nValueShift, iPixel = 0; + unsigned char byValueMask, byCountMask; + VSILFILE *fp = psInfo->fp; + int byNext, i; + +/* -------------------------------------------------------------------- */ +/* Do we know where the requested line is? If not, read all */ +/* the preceeding ones to "find" our line. */ +/* -------------------------------------------------------------------- */ + if( nScanline < 0 || nScanline >= psInfo->nYSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Scanline %d out of range.", + nScanline ); + return FALSE; + } + + if( psInfo->panLineOffset[nScanline] == -1 ) + { + for( i = 0; i < nScanline; i++ ) + { + if( psInfo->panLineOffset[i+1] == -1 ) + { + if( !BSBReadScanline( psInfo, i, pabyScanlineBuf ) ) + return FALSE; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Seek to the beginning of the scanline and check that the */ +/* scanline number in file is consistent with what we expect */ +/* -------------------------------------------------------------------- */ + if ( !BSBSeekAndCheckScanlineNumber(psInfo, nScanline, TRUE) ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Setup masking values. */ +/* -------------------------------------------------------------------- */ + nValueShift = 7 - psInfo->nColorSize; + byValueMask = (unsigned char) + ((((1 << psInfo->nColorSize)) - 1) << nValueShift); + byCountMask = (unsigned char) + (1 << (7 - psInfo->nColorSize)) - 1; + +/* -------------------------------------------------------------------- */ +/* Read and expand runs. */ +/* If for some reason the buffer is not filled, */ +/* just repeat the process until the buffer is filled. */ +/* This is the case for IS1612_4.NOS (#2782) */ +/* -------------------------------------------------------------------- */ + do + { + int bErrorFlag = FALSE; + while( (byNext = BSBGetc(psInfo,psInfo->bNO1, &bErrorFlag)) != 0 && + !bErrorFlag) + { + int nPixValue; + int nRunCount, i; + + nPixValue = (byNext & byValueMask) >> nValueShift; + + nRunCount = byNext & byCountMask; + + while( (byNext & 0x80) != 0 && !bErrorFlag) + { + byNext = BSBGetc( psInfo, psInfo->bNO1, &bErrorFlag ); + nRunCount = nRunCount * 128 + (byNext & 0x7f); + } + + /* Prevent over-run of line data */ + if (nRunCount < 0 || nRunCount > INT_MAX - (iPixel + 1)) + { + CPLError( CE_Failure, CPLE_FileIO, + "Corrupted run count : %d", nRunCount ); + return FALSE; + } + if (nRunCount > psInfo->nXSize) + { + static int bHasWarned = FALSE; + if (!bHasWarned) + { + CPLDebug("BSB", "Too big run count : %d", nRunCount ); + bHasWarned = TRUE; + } + } + + if( iPixel + nRunCount + 1 > psInfo->nXSize ) + nRunCount = psInfo->nXSize - iPixel - 1; + + for( i = 0; i < nRunCount+1; i++ ) + pabyScanlineBuf[iPixel++] = (unsigned char) nPixValue; + } + if ( bErrorFlag ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Truncated BSB file or I/O error." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* For reasons that are unclear, some scanlines are exactly one */ +/* pixel short (such as in the BSB 3.0 354704.KAP product from */ +/* NDI/CHS) but are otherwise OK. Just add a zero if this */ +/* appear to have occured. */ +/* -------------------------------------------------------------------- */ + if( iPixel == psInfo->nXSize - 1 ) + pabyScanlineBuf[iPixel++] = 0; + +/* -------------------------------------------------------------------- */ +/* If we have not enough data and no offset table, check that the */ +/* next bytes are not the expected next scanline number. If they are */ +/* not, then we can use them to fill the row again */ +/* -------------------------------------------------------------------- */ + else if (iPixel < psInfo->nXSize && + nScanline != psInfo->nYSize-1 && + psInfo->panLineOffset[nScanline+1] == -1) + { + int nCurOffset = (int)(VSIFTellL( fp ) - psInfo->nBufferSize) + + psInfo->nBufferOffset; + psInfo->panLineOffset[nScanline+1] = nCurOffset; + if (BSBSeekAndCheckScanlineNumber(psInfo, nScanline + 1, FALSE)) + { + CPLDebug("BSB", "iPixel=%d, nScanline=%d, nCurOffset=%d --> found new row marker", iPixel, nScanline, nCurOffset); + break; + } + else + { + CPLDebug("BSB", "iPixel=%d, nScanline=%d, nCurOffset=%d --> did NOT find new row marker", iPixel, nScanline, nCurOffset); + + /* The next bytes are not the expected next scanline number, so */ + /* use them to fill the row */ + VSIFSeekL( fp, nCurOffset, SEEK_SET ); + psInfo->panLineOffset[nScanline+1] = -1; + psInfo->nBufferOffset = 0; + psInfo->nBufferSize = 0; + } + } + } + while ( iPixel < psInfo->nXSize && + (nScanline == psInfo->nYSize-1 || + psInfo->panLineOffset[nScanline+1] == -1 || + VSIFTellL( fp ) - psInfo->nBufferSize + psInfo->nBufferOffset < (vsi_l_offset)psInfo->panLineOffset[nScanline+1]) ); + +/* -------------------------------------------------------------------- */ +/* If the line buffer is not filled after reading the line in the */ +/* file upto the next line offset, just fill it with zeros. */ +/* (The last pixel value from nPixValue could be a better value?) */ +/* -------------------------------------------------------------------- */ + while( iPixel < psInfo->nXSize ) + pabyScanlineBuf[iPixel++] = 0; + +/* -------------------------------------------------------------------- */ +/* Remember the start of the next line. */ +/* But only if it is not already known. */ +/* -------------------------------------------------------------------- */ + if( nScanline < psInfo->nYSize-1 && + psInfo->panLineOffset[nScanline+1] == -1 ) + { + psInfo->panLineOffset[nScanline+1] = (int) + (VSIFTellL( fp ) - psInfo->nBufferSize) + psInfo->nBufferOffset; + } + + return TRUE; +} + +/************************************************************************/ +/* BSBClose() */ +/************************************************************************/ + +void BSBClose( BSBInfo *psInfo ) + +{ + if( psInfo->fp != NULL ) + VSIFCloseL( psInfo->fp ); + + CPLFree( psInfo->pabyBuffer ); + + CSLDestroy( psInfo->papszHeader ); + CPLFree( psInfo->panLineOffset ); + CPLFree( psInfo->pabyPCT ); + CPLFree( psInfo ); +} + +/************************************************************************/ +/* BSBCreate() */ +/************************************************************************/ + +BSBInfo *BSBCreate( const char *pszFilename, + CPL_UNUSED int nCreationFlags, + int nVersion, + int nXSize, int nYSize ) +{ + VSILFILE *fp; + BSBInfo *psInfo; + +/* -------------------------------------------------------------------- */ +/* Open new KAP file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszFilename, "wb" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open output file %s.", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write out BSB line. */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, + "!Copyright unknown\n" ); + VSIFPrintfL( fp, + "VER/%.1f\n", nVersion / 100.0 ); + VSIFPrintfL( fp, + "BSB/NA=UNKNOWN,NU=999502,RA=%d,%d,DU=254\n", + nXSize, nYSize ); + VSIFPrintfL( fp, + "KNP/SC=25000,GD=WGS84,PR=Mercator\n" ); + VSIFPrintfL( fp, + " PP=31.500000,PI=0.033333,SP=,SK=0.000000,TA=90.000000\n"); + VSIFPrintfL( fp, + " UN=Metres,SD=HHWLT,DX=2.500000,DY=2.500000\n"); + + +/* -------------------------------------------------------------------- */ +/* Create info structure. */ +/* -------------------------------------------------------------------- */ + psInfo = (BSBInfo *) CPLCalloc(1,sizeof(BSBInfo)); + psInfo->fp = fp; + psInfo->bNO1 = FALSE; + psInfo->nVersion = nVersion; + psInfo->nXSize = nXSize; + psInfo->nYSize = nYSize; + psInfo->bNewFile = TRUE; + psInfo->nLastLineWritten = -1; + + return psInfo; +} + +/************************************************************************/ +/* BSBWritePCT() */ +/************************************************************************/ + +int BSBWritePCT( BSBInfo *psInfo, int nPCTSize, unsigned char *pabyPCT ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Verify the PCT not too large. */ +/* -------------------------------------------------------------------- */ + if( nPCTSize > 128 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Pseudo-color table too large (%d entries), at most 128\n" + " entries allowed in BSB format.", nPCTSize ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Compute the number of bits required for the colors. */ +/* -------------------------------------------------------------------- */ + for( psInfo->nColorSize = 1; + (1 << psInfo->nColorSize) < nPCTSize; + psInfo->nColorSize++ ) {} + +/* -------------------------------------------------------------------- */ +/* Write out the color table. Note that color table entry zero */ +/* is ignored. Zero is not a legal value. */ +/* -------------------------------------------------------------------- */ + for( i = 1; i < nPCTSize; i++ ) + { + VSIFPrintfL( psInfo->fp, + "RGB/%d,%d,%d,%d\n", + i, pabyPCT[i*3+0], pabyPCT[i*3+1], pabyPCT[i*3+2] ); + } + + return TRUE; +} + +/************************************************************************/ +/* BSBWriteScanline() */ +/************************************************************************/ + +int BSBWriteScanline( BSBInfo *psInfo, unsigned char *pabyScanlineBuf ) + +{ + int nValue, iX; + + if( psInfo->nLastLineWritten == psInfo->nYSize - 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to write too many scanlines." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If this is the first scanline writen out the EOF marker, and */ +/* the introductory info in the image segment. */ +/* -------------------------------------------------------------------- */ + if( psInfo->nLastLineWritten == -1 ) + { + VSIFPutcL( 0x1A, psInfo->fp ); + VSIFPutcL( 0x00, psInfo->fp ); + VSIFPutcL( psInfo->nColorSize, psInfo->fp ); + } + +/* -------------------------------------------------------------------- */ +/* Write the line number. */ +/* -------------------------------------------------------------------- */ + nValue = ++psInfo->nLastLineWritten; + + if( psInfo->nVersion >= 200 ) + nValue++; + + if( nValue >= 128*128 ) + VSIFPutcL( 0x80 | ((nValue & (0x7f<<14)) >> 14), psInfo->fp ); + if( nValue >= 128 ) + VSIFPutcL( 0x80 | ((nValue & (0x7f<<7)) >> 7), psInfo->fp ); + VSIFPutcL( nValue & 0x7f, psInfo->fp ); + +/* -------------------------------------------------------------------- */ +/* Write out each pixel as a separate byte. We don't try to */ +/* actually capture the runs since that radical and futuristic */ +/* concept is patented! */ +/* -------------------------------------------------------------------- */ + for( iX = 0; iX < psInfo->nXSize; iX++ ) + { + VSIFPutcL( pabyScanlineBuf[iX] << (7-psInfo->nColorSize), + psInfo->fp ); + } + + VSIFPutcL( 0x00, psInfo->fp ); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/frmts/bsb/bsb_read.h b/bazaar/plugin/gdal/frmts/bsb/bsb_read.h new file mode 100644 index 000000000..5d288c78d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bsb/bsb_read.h @@ -0,0 +1,83 @@ +/****************************************************************************** + * $Id: bsb_read.h 20996 2010-10-28 18:38:15Z rouault $ + * + * Project: BSB Reader + * Purpose: non-GDAL BSB API Declarations + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _BSBREAD_H_INCLUDED +#define _BSBREAD_H_INCLUDED + +#include "cpl_port.h" +#include "cpl_vsi.h" + +CPL_C_START + +typedef struct { + VSILFILE *fp; + + + GByte *pabyBuffer; + int nBufferOffset; + int nBufferSize; + int nBufferAllocation; + int nSavedCharacter; + + int nXSize; + int nYSize; + + int nPCTSize; + unsigned char *pabyPCT; + + char **papszHeader; + + int *panLineOffset; + + int nColorSize; + + int nVersion; /* times 100 */ + + int bNO1; + + int bNewFile; + int nLastLineWritten; +} BSBInfo; + +BSBInfo CPL_DLL *BSBOpen( const char *pszFilename ); +int CPL_DLL BSBReadScanline( BSBInfo *psInfo, int nScanline, + unsigned char *pabyScanlineBuf ); +void CPL_DLL BSBClose( BSBInfo *psInfo ); + +BSBInfo CPL_DLL *BSBCreate( const char *pszFilename, int nCreationFlags, + int nVersion, int nXSize, int nYSize ); +int CPL_DLL BSBWritePCT( BSBInfo *psInfo, + int nPCTSize, unsigned char *pabyPCT ); +int CPL_DLL BSBWriteScanline( BSBInfo *psInfo, + unsigned char *pabyScanlineBuf ); + +CPL_C_END + + +#endif /* ndef _BSBREAD_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/bsb/bsbdataset.cpp b/bazaar/plugin/gdal/frmts/bsb/bsbdataset.cpp new file mode 100644 index 000000000..ddcd5666e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bsb/bsbdataset.cpp @@ -0,0 +1,1207 @@ +/****************************************************************************** + * $Id: bsbdataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: BSB Reader + * Purpose: BSBDataset implementation for BSB format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "bsb_read.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: bsbdataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_BSB(void); +CPL_C_END + +//Disabled as people may worry about the BSB patent +//#define BSB_CREATE + +/************************************************************************/ +/* ==================================================================== */ +/* BSBDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class BSBRasterBand; + +class BSBDataset : public GDALPamDataset +{ + int nGCPCount; + GDAL_GCP *pasGCPList; + CPLString osGCPProjection; + + double adfGeoTransform[6]; + int bGeoTransformSet; + + void ScanForGCPs( bool isNos, const char *pszFilename ); + void ScanForGCPsNos( const char *pszFilename ); + void ScanForGCPsBSB(); + + static int IdentifyInternal( GDALOpenInfo *, bool & isNosOut ); + + public: + BSBDataset(); + ~BSBDataset(); + + BSBInfo *psInfo; + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* BSBRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class BSBRasterBand : public GDALPamRasterBand +{ + GDALColorTable oCT; + + public: + BSBRasterBand( BSBDataset * ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorTable *GetColorTable(); + virtual GDALColorInterp GetColorInterpretation(); +}; + + +/************************************************************************/ +/* BSBRasterBand() */ +/************************************************************************/ + +BSBRasterBand::BSBRasterBand( BSBDataset *poDS ) + +{ + this->poDS = poDS; + this->nBand = 1; + + eDataType = GDT_Byte; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + // Note that the first color table entry is dropped, everything is + // shifted down. + for( int i = 0; i < poDS->psInfo->nPCTSize-1; i++ ) + { + GDALColorEntry oColor; + + oColor.c1 = poDS->psInfo->pabyPCT[i*3+0+3]; + oColor.c2 = poDS->psInfo->pabyPCT[i*3+1+3]; + oColor.c3 = poDS->psInfo->pabyPCT[i*3+2+3]; + oColor.c4 = 255; + + oCT.SetColorEntry( i, &oColor ); + } +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr BSBRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + BSBDataset *poGDS = (BSBDataset *) poDS; + GByte *pabyScanline = (GByte*) pImage; + + if( BSBReadScanline( poGDS->psInfo, nBlockYOff, pabyScanline ) ) + { + for( int i = 0; i < nBlockXSize; i++ ) + { + /* The indices start at 1, except in case of some charts */ + /* where there are missing values, which are filled to 0 */ + /* by BSBReadScanline */ + if (pabyScanline[i] > 0) + pabyScanline[i] -= 1; + } + + return CE_None; + } + else + return CE_Failure; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *BSBRasterBand::GetColorTable() + +{ + return &oCT; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp BSBRasterBand::GetColorInterpretation() + +{ + return GCI_PaletteIndex; +} + +/************************************************************************/ +/* ==================================================================== */ +/* BSBDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* BSBDataset() */ +/************************************************************************/ + +BSBDataset::BSBDataset() + +{ + psInfo = NULL; + + bGeoTransformSet = FALSE; + + nGCPCount = 0; + pasGCPList = NULL; + osGCPProjection = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",7030]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",6326]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AUTHORITY[\"EPSG\",4326]]"; + + adfGeoTransform[0] = 0.0; /* X Origin (top left corner) */ + adfGeoTransform[1] = 1.0; /* X Pixel size */ + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; /* Y Origin (top left corner) */ + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; /* Y Pixel Size */ + +} + +/************************************************************************/ +/* ~BSBDataset() */ +/************************************************************************/ + +BSBDataset::~BSBDataset() + +{ + FlushCache(); + + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + + if( psInfo != NULL ) + BSBClose( psInfo ); +} + + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr BSBDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + + if( bGeoTransformSet ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *BSBDataset::GetProjectionRef() + +{ + if( bGeoTransformSet ) + return osGCPProjection; + else + return ""; +} + +/************************************************************************/ +/* GDALHeuristicDatelineWrap() */ +/************************************************************************/ + +static void +GDALHeuristicDatelineWrap( int nPointCount, double *padfX ) + +{ + int i; + /* Following inits are useless but keep GCC happy */ + double dfX_PM_Min = 0, dfX_PM_Max = 0, dfX_Dateline_Min = 0, dfX_Dateline_Max = 0; + int bUsePMWrap; + + if( nPointCount < 2 ) + return; + +/* -------------------------------------------------------------------- */ +/* Work out what the longitude range will be centering on the */ +/* prime meridian (-180 to 180) and centering on the dateline */ +/* (0 to 360). */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nPointCount; i++ ) + { + double dfX_PM, dfX_Dateline; + + dfX_PM = padfX[i]; + if( dfX_PM > 180 ) + dfX_PM -= 360.0; + + dfX_Dateline = padfX[i]; + if( dfX_Dateline < 0 ) + dfX_Dateline += 360.0; + + if( i == 0 ) + { + dfX_PM_Min = dfX_PM_Max = dfX_PM; + dfX_Dateline_Min = dfX_Dateline_Max = dfX_Dateline; + } + else + { + dfX_PM_Min = MIN(dfX_PM_Min,dfX_PM); + dfX_PM_Max = MAX(dfX_PM_Max,dfX_PM); + dfX_Dateline_Min = MIN(dfX_Dateline_Min,dfX_Dateline); + dfX_Dateline_Max = MAX(dfX_Dateline_Max,dfX_Dateline); + } + } + +/* -------------------------------------------------------------------- */ +/* Do nothing if the range is always fairly small - no apparent */ +/* wrapping issues. */ +/* -------------------------------------------------------------------- */ + if( (dfX_PM_Max - dfX_PM_Min) < 270.0 + && (dfX_Dateline_Max - dfX_Dateline_Min) < 270.0 ) + return; + +/* -------------------------------------------------------------------- */ +/* Do nothing if both appproach have a wide range - best not to */ +/* fiddle if we aren't sure we are improving things. */ +/* -------------------------------------------------------------------- */ + if( (dfX_PM_Max - dfX_PM_Min) > 270.0 + && (dfX_Dateline_Max - dfX_Dateline_Min) > 270.0 ) + return; + +/* -------------------------------------------------------------------- */ +/* Pick which way to transform things. */ +/* -------------------------------------------------------------------- */ + if( (dfX_PM_Max - dfX_PM_Min) > 270.0 + && (dfX_Dateline_Max - dfX_Dateline_Min) < 270.0 ) + bUsePMWrap = FALSE; + else + bUsePMWrap = TRUE; + + +/* -------------------------------------------------------------------- */ +/* Apply rewrapping. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nPointCount; i++ ) + { + if( bUsePMWrap ) + { + if( padfX[i] > 180 ) + padfX[i] -= 360.0; + } + else + { + if( padfX[i] < 0 ) + padfX[i] += 360.0; + } + } +} + +/************************************************************************/ +/* GDALHeuristicDatelineWrapGCPs() */ +/************************************************************************/ + +static void +GDALHeuristicDatelineWrapGCPs( int nPointCount, GDAL_GCP *pasGCPList ) +{ + std::vector oadfX; + int i; + + oadfX.resize( nPointCount ); + for( i = 0; i < nPointCount; i++ ) + oadfX[i] = pasGCPList[i].dfGCPX; + + GDALHeuristicDatelineWrap( nPointCount, &(oadfX[0]) ); + + for( i = 0; i < nPointCount; i++ ) + pasGCPList[i].dfGCPX = oadfX[i]; +} + +/************************************************************************/ +/* ScanForGCPs() */ +/************************************************************************/ + +void BSBDataset::ScanForGCPs( bool isNos, const char *pszFilename ) + +{ +/* -------------------------------------------------------------------- */ +/* Collect GCPs as appropriate to source. */ +/* -------------------------------------------------------------------- */ + nGCPCount = 0; + + if ( isNos ) + { + ScanForGCPsNos(pszFilename); + } else { + ScanForGCPsBSB(); + } + +/* -------------------------------------------------------------------- */ +/* Apply heuristics to re-wrap GCPs to maintain continguity */ +/* over the international dateline. */ +/* -------------------------------------------------------------------- */ + if( nGCPCount > 1 ) + GDALHeuristicDatelineWrapGCPs( nGCPCount, pasGCPList ); + +/* -------------------------------------------------------------------- */ +/* Collect coordinate system related parameters from header. */ +/* -------------------------------------------------------------------- */ + int i; + const char *pszKNP=NULL, *pszKNQ=NULL; + + for( i = 0; psInfo->papszHeader[i] != NULL; i++ ) + { + if( EQUALN(psInfo->papszHeader[i],"KNP/",4) ) + { + pszKNP = psInfo->papszHeader[i]; + SetMetadataItem( "BSB_KNP", pszKNP + 4 ); + } + if( EQUALN(psInfo->papszHeader[i],"KNQ/",4) ) + { + pszKNQ = psInfo->papszHeader[i]; + SetMetadataItem( "BSB_KNQ", pszKNQ + 4 ); + } + } + + +/* -------------------------------------------------------------------- */ +/* Can we derive a reasonable coordinate system definition for */ +/* this file? For now we keep it simple, just handling */ +/* mercator. In the future we should consider others. */ +/* -------------------------------------------------------------------- */ + CPLString osUnderlyingSRS; + if( pszKNP != NULL ) + { + const char *pszPR = strstr(pszKNP,"PR="); + const char *pszGD = strstr(pszKNP,"GD="); + const char *pszValue, *pszEnd = NULL; + const char *pszGEOGCS = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4326\"]]"; + CPLString osPP; + + // Capture the PP string. + pszValue = strstr(pszKNP,"PP="); + if( pszValue ) + pszEnd = strstr(pszValue,","); + if( pszValue && pszEnd ) + osPP.assign(pszValue+3,pszEnd-pszValue-3); + + // Look at the datum + if( pszGD == NULL ) + { + /* no match. We'll default to EPSG:4326 */ + } + else if( EQUALN(pszGD,"GD=European 1950", 16) ) + { + pszGEOGCS = "GEOGCS[\"ED50\",DATUM[\"European_Datum_1950\",SPHEROID[\"International 1924\",6378388,297,AUTHORITY[\"EPSG\",\"7022\"]],TOWGS84[-87,-98,-121,0,0,0,0],AUTHORITY[\"EPSG\",\"6230\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4230\"]]"; + } + + // Look at the projection + if( pszPR == NULL ) + { + /* no match */ + } + else if( EQUALN(pszPR,"PR=MERCATOR", 11) ) + { + // We somewhat arbitrarily select our first GCPX as our + // central meridian. This is mostly helpful to ensure + // that regions crossing the dateline will be contiguous + // in mercator. + osUnderlyingSRS.Printf( "PROJCS[\"Global Mercator\",%s,PROJECTION[\"Mercator_2SP\"],PARAMETER[\"standard_parallel_1\",0],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",%d],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]]", + pszGEOGCS, (int) pasGCPList[0].dfGCPX ); + } + + else if( EQUALN(pszPR,"PR=TRANSVERSE MERCATOR", 22) + && osPP.size() > 0 ) + { + + osUnderlyingSRS.Printf( + "PROJCS[\"unnamed\",%s,PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",%s],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]]", + pszGEOGCS, osPP.c_str() ); + } + + else if( EQUALN(pszPR,"PR=UNIVERSAL TRANSVERSE MERCATOR", 32) + && osPP.size() > 0 ) + { + // This is not *really* UTM unless the central meridian + // matches a zone which it does not in some (most?) maps. + osUnderlyingSRS.Printf( + "PROJCS[\"unnamed\",%s,PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",%s],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0]]", + pszGEOGCS, osPP.c_str() ); + } + + else if( EQUALN(pszPR,"PR=POLYCONIC", 12) && osPP.size() > 0 ) + { + osUnderlyingSRS.Printf( + "PROJCS[\"unnamed\",%s,PROJECTION[\"Polyconic\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",%s],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0]]", + pszGEOGCS, osPP.c_str() ); + } + + else if( EQUALN(pszPR,"PR=LAMBERT CONFORMAL CONIC", 26) + && osPP.size() > 0 && pszKNQ != NULL ) + { + CPLString osP2, osP3; + + // Capture the KNQ/P2 string. + pszValue = strstr(pszKNQ,"P2="); + if( pszValue ) + pszEnd = strstr(pszValue,","); + if( pszValue && pszEnd ) + osP2.assign(pszValue+3,pszEnd-pszValue-3); + + // Capture the KNQ/P3 string. + pszValue = strstr(pszKNQ,"P3="); + if( pszValue ) + pszEnd = strstr(pszValue,","); + if( pszValue ) + { + if( pszEnd ) + osP3.assign(pszValue+3,pszEnd-pszValue-3); + else + osP3.assign(pszValue+3); + } + + if( osP2.size() > 0 && osP3.size() > 0 ) + osUnderlyingSRS.Printf( + "PROJCS[\"unnamed\",%s,PROJECTION[\"Lambert_Conformal_Conic_2SP\"],PARAMETER[\"standard_parallel_1\",%s],PARAMETER[\"standard_parallel_2\",%s],PARAMETER[\"latitude_of_origin\",0.0],PARAMETER[\"central_meridian\",%s],PARAMETER[\"false_easting\",0.0],PARAMETER[\"false_northing\",0.0]]", + pszGEOGCS, osP2.c_str(), osP3.c_str(), osPP.c_str() ); + + } + } + +/* -------------------------------------------------------------------- */ +/* If we got an alternate underlying coordinate system, try */ +/* converting the GCPs to that coordinate system. */ +/* -------------------------------------------------------------------- */ + if( osUnderlyingSRS.length() > 0 ) + { + OGRSpatialReference oGeog_SRS, oProjected_SRS; + OGRCoordinateTransformation *poCT; + + oProjected_SRS.SetFromUserInput( osUnderlyingSRS ); + oGeog_SRS.CopyGeogCSFrom( &oProjected_SRS ); + + poCT = OGRCreateCoordinateTransformation( &oGeog_SRS, + &oProjected_SRS ); + if( poCT != NULL ) + { + for( i = 0; i < nGCPCount; i++ ) + { + poCT->Transform( 1, + &(pasGCPList[i].dfGCPX), + &(pasGCPList[i].dfGCPY), + &(pasGCPList[i].dfGCPZ) ); + } + + osGCPProjection = osUnderlyingSRS; + + delete poCT; + } + else + CPLErrorReset(); + } + +/* -------------------------------------------------------------------- */ +/* Attempt to prepare a geotransform from the GCPs. */ +/* -------------------------------------------------------------------- */ + if( GDALGCPsToGeoTransform( nGCPCount, pasGCPList, adfGeoTransform, + FALSE ) ) + { + bGeoTransformSet = TRUE; + } +} + +/************************************************************************/ +/* ScanForGCPsNos() */ +/* */ +/* Nos files have an accompanying .geo file, that contains some */ +/* of the information normally contained in the header section */ +/* with BSB files. we try and open a file with the same name, */ +/* but a .geo extension, and look for lines like... */ +/* PointX=long lat line pixel (using the same naming system */ +/* as BSB) Point1=-22.0000 64.250000 197 744 */ +/************************************************************************/ + +void BSBDataset::ScanForGCPsNos( const char *pszFilename ) +{ + char **Tokens; + const char *geofile; + const char *extension; + int fileGCPCount=0; + + extension = CPLGetExtension(pszFilename); + + // pseudointelligently try and guess whether we want a .geo or a .GEO + if (extension[1] == 'O') + { + geofile = CPLResetExtension( pszFilename, "GEO"); + } else { + geofile = CPLResetExtension( pszFilename, "geo"); + } + + FILE *gfp = VSIFOpen( geofile, "r" ); // Text files + if( gfp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Couldn't find a matching .GEO file: %s", geofile ); + return; + } + + char *thisLine = (char *) CPLMalloc( 80 ); // FIXME + + // Count the GCPs (reference points) and seek the file pointer 'gfp' to the starting point + while (fgets(thisLine, 80, gfp)) + { + if( EQUALN(thisLine, "Point", 5) ) + fileGCPCount++; + } + VSIRewind( gfp ); + + // Memory has not been allocated to fileGCPCount yet + pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),fileGCPCount+1); + + while (fgets(thisLine, 80, gfp)) + { + if( EQUALN(thisLine, "Point", 5) ) + { + // got a point line, turn it into a gcp + Tokens = CSLTokenizeStringComplex(thisLine, "= ", FALSE, FALSE); + if (CSLCount(Tokens) >= 5) + { + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + pasGCPList[nGCPCount].dfGCPX = CPLAtof(Tokens[1]); + pasGCPList[nGCPCount].dfGCPY = CPLAtof(Tokens[2]); + pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(Tokens[4]); + pasGCPList[nGCPCount].dfGCPLine = CPLAtof(Tokens[3]); + + CPLFree( pasGCPList[nGCPCount].pszId ); + char szName[50]; + sprintf( szName, "GCP_%d", nGCPCount+1 ); + pasGCPList[nGCPCount].pszId = CPLStrdup( szName ); + + nGCPCount++; + } + CSLDestroy(Tokens); + } + } + + CPLFree(thisLine); + VSIFClose(gfp); +} + + +/************************************************************************/ +/* ScanForGCPsBSB() */ +/************************************************************************/ + +void BSBDataset::ScanForGCPsBSB() +{ +/* -------------------------------------------------------------------- */ +/* Collect standalone GCPs. They look like: */ +/* */ +/* REF/1,115,2727,32.346666666667,-60.881666666667 */ +/* REF/n,pixel,line,lat,long */ +/* -------------------------------------------------------------------- */ + int fileGCPCount=0; + int i; + + // Count the GCPs (reference points) in psInfo->papszHeader + for( i = 0; psInfo->papszHeader[i] != NULL; i++ ) + if( EQUALN(psInfo->papszHeader[i],"REF/",4) ) + fileGCPCount++; + + // Memory has not been allocated to fileGCPCount yet + pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),fileGCPCount+1); + + for( i = 0; psInfo->papszHeader[i] != NULL; i++ ) + { + char **papszTokens; + char szName[50]; + + if( !EQUALN(psInfo->papszHeader[i],"REF/",4) ) + continue; + + papszTokens = + CSLTokenizeStringComplex( psInfo->papszHeader[i]+4, ",", + FALSE, FALSE ); + + if( CSLCount(papszTokens) > 4 ) + { + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + + pasGCPList[nGCPCount].dfGCPX = CPLAtof(papszTokens[4]); + pasGCPList[nGCPCount].dfGCPY = CPLAtof(papszTokens[3]); + pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(papszTokens[1]); + pasGCPList[nGCPCount].dfGCPLine = CPLAtof(papszTokens[2]); + + CPLFree( pasGCPList[nGCPCount].pszId ); + if( CSLCount(papszTokens) > 5 ) + { + pasGCPList[nGCPCount].pszId = CPLStrdup(papszTokens[5]); + } + else + { + sprintf( szName, "GCP_%d", nGCPCount+1 ); + pasGCPList[nGCPCount].pszId = CPLStrdup( szName ); + } + + nGCPCount++; + } + CSLDestroy( papszTokens ); + } +} + +/************************************************************************/ +/* IdentifyInternal() */ +/************************************************************************/ + +int BSBDataset::IdentifyInternal( GDALOpenInfo * poOpenInfo, bool& isNosOut ) + +{ +/* -------------------------------------------------------------------- */ +/* Check for BSB/ keyword. */ +/* -------------------------------------------------------------------- */ + int i; + isNosOut = false; + + if( poOpenInfo->nHeaderBytes < 1000 ) + return FALSE; + + for( i = 0; i < poOpenInfo->nHeaderBytes - 4; i++ ) + { + if( poOpenInfo->pabyHeader[i+0] == 'B' + && poOpenInfo->pabyHeader[i+1] == 'S' + && poOpenInfo->pabyHeader[i+2] == 'B' + && poOpenInfo->pabyHeader[i+3] == '/' ) + break; + if( poOpenInfo->pabyHeader[i+0] == 'N' + && poOpenInfo->pabyHeader[i+1] == 'O' + && poOpenInfo->pabyHeader[i+2] == 'S' + && poOpenInfo->pabyHeader[i+3] == '/' ) + { + isNosOut = true; + break; + } + if( poOpenInfo->pabyHeader[i+0] == 'W' + && poOpenInfo->pabyHeader[i+1] == 'X' + && poOpenInfo->pabyHeader[i+2] == '\\' + && poOpenInfo->pabyHeader[i+3] == '8' ) + break; + } + + if( i == poOpenInfo->nHeaderBytes - 4 ) + return FALSE; + + /* Additional test to avoid false positive. See #2881 */ + const char* pszRA = strstr((const char*)poOpenInfo->pabyHeader + i, "RA="); + if (pszRA == NULL) /* This may be a NO1 file */ + pszRA = strstr((const char*)poOpenInfo->pabyHeader + i, "[JF"); + if (pszRA == NULL || pszRA - ((const char*)poOpenInfo->pabyHeader + i) > 100 ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int BSBDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + bool isNos; + return IdentifyInternal(poOpenInfo, isNos); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *BSBDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + bool isNos = false; + if (!IdentifyInternal(poOpenInfo, isNos)) + return NULL; + + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The BSB driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + BSBDataset *poDS; + + poDS = new BSBDataset(); + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + poDS->psInfo = BSBOpen( poOpenInfo->pszFilename ); + if( poDS->psInfo == NULL ) + { + delete poDS; + return NULL; + } + + poDS->nRasterXSize = poDS->psInfo->nXSize; + poDS->nRasterYSize = poDS->psInfo->nYSize; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->SetBand( 1, new BSBRasterBand( poDS )); + + poDS->ScanForGCPs( isNos, poOpenInfo->pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int BSBDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *BSBDataset::GetGCPProjection() + +{ + return osGCPProjection; +} + +/************************************************************************/ +/* GetGCP() */ +/************************************************************************/ + +const GDAL_GCP *BSBDataset::GetGCPs() + +{ + return pasGCPList; +} + +#ifdef BSB_CREATE + +/************************************************************************/ +/* BSBIsSRSOK() */ +/************************************************************************/ + +static int BSBIsSRSOK(const char *pszWKT) +{ + int bOK = FALSE; + OGRSpatialReference oSRS, oSRS_WGS84, oSRS_NAD83; + + if( pszWKT != NULL && pszWKT[0] != '\0' ) + { + char* pszTmpWKT = (char*)pszWKT; + oSRS.importFromWkt( &pszTmpWKT ); + + oSRS_WGS84.SetWellKnownGeogCS( "WGS84" ); + oSRS_NAD83.SetWellKnownGeogCS( "NAD83" ); + if ( (oSRS.IsSameGeogCS(&oSRS_WGS84) || oSRS.IsSameGeogCS(&oSRS_NAD83)) && + oSRS.IsGeographic() && oSRS.GetPrimeMeridian() == 0.0 ) + { + bOK = TRUE; + } + } + + if (!bOK) + { + CPLError(CE_Warning, CPLE_NotSupported, + "BSB only supports WGS84 or NAD83 geographic projections.\n"); + } + + return bOK; +} + +/************************************************************************/ +/* BSBCreateCopy() */ +/************************************************************************/ + +static GDALDataset * +BSBCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + if( nBands != 1 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "BSB driver only supports one band images.\n" ); + + return NULL; + } + + if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte + && bStrict ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "BSB driver doesn't support data type %s. " + "Only eight bit bands supported.\n", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open the output file. */ +/* -------------------------------------------------------------------- */ + BSBInfo *psBSB; + + psBSB = BSBCreate( pszFilename, 0, 200, nXSize, nYSize ); + if( psBSB == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Prepare initial color table.colortable. */ +/* -------------------------------------------------------------------- */ + GDALRasterBand *poBand = poSrcDS->GetRasterBand(1); + int iColor; + unsigned char abyPCT[771]; + int nPCTSize; + int anRemap[256]; + + abyPCT[0] = 0; + abyPCT[1] = 0; + abyPCT[2] = 0; + + if( poBand->GetColorTable() == NULL ) + { + /* map greyscale down to 63 grey levels. */ + for( iColor = 0; iColor < 256; iColor++ ) + { + int nOutValue = (int) (iColor / 4.1) + 1; + + anRemap[iColor] = nOutValue; + abyPCT[nOutValue*3 + 0] = (unsigned char) iColor; + abyPCT[nOutValue*3 + 1] = (unsigned char) iColor; + abyPCT[nOutValue*3 + 2] = (unsigned char) iColor; + } + nPCTSize = 64; + } + else + { + GDALColorTable *poCT = poBand->GetColorTable(); + int nColorTableSize = poCT->GetColorEntryCount(); + if (nColorTableSize > 255) + nColorTableSize = 255; + + for( iColor = 0; iColor < nColorTableSize; iColor++ ) + { + GDALColorEntry sEntry; + + poCT->GetColorEntryAsRGB( iColor, &sEntry ); + + anRemap[iColor] = iColor + 1; + abyPCT[(iColor+1)*3 + 0] = (unsigned char) sEntry.c1; + abyPCT[(iColor+1)*3 + 1] = (unsigned char) sEntry.c2; + abyPCT[(iColor+1)*3 + 2] = (unsigned char) sEntry.c3; + } + + nPCTSize = nColorTableSize + 1; + + // Add entries for pixel values which apparently will not occur. + for( iColor = nPCTSize; iColor < 256; iColor++ ) + anRemap[iColor] = 1; + } + +/* -------------------------------------------------------------------- */ +/* Boil out all duplicate entries. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 1; i < nPCTSize-1; i++ ) + { + int j; + + for( j = i+1; j < nPCTSize; j++ ) + { + if( abyPCT[i*3+0] == abyPCT[j*3+0] + && abyPCT[i*3+1] == abyPCT[j*3+1] + && abyPCT[i*3+2] == abyPCT[j*3+2] ) + { + int k; + + nPCTSize--; + abyPCT[j*3+0] = abyPCT[nPCTSize*3+0]; + abyPCT[j*3+1] = abyPCT[nPCTSize*3+1]; + abyPCT[j*3+2] = abyPCT[nPCTSize*3+2]; + + for( k = 0; k < 256; k++ ) + { + // merge matching entries. + if( anRemap[k] == j ) + anRemap[k] = i; + + // shift the last PCT entry into the new hole. + if( anRemap[k] == nPCTSize ) + anRemap[k] = j; + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Boil out all duplicate entries. */ +/* -------------------------------------------------------------------- */ + if( nPCTSize > 128 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Having to merge color table entries to reduce %d real\n" + "color table entries down to 127 values.", + nPCTSize ); + } + + while( nPCTSize > 128 ) + { + int nBestRange = 768; + int iBestMatch1=-1, iBestMatch2=-1; + + // Find the closest pair of color table entries. + + for( i = 1; i < nPCTSize-1; i++ ) + { + int j; + + for( j = i+1; j < nPCTSize; j++ ) + { + int nRange = ABS(abyPCT[i*3+0] - abyPCT[j*3+0]) + + ABS(abyPCT[i*3+1] - abyPCT[j*3+1]) + + ABS(abyPCT[i*3+2] - abyPCT[j*3+2]); + + if( nRange < nBestRange ) + { + iBestMatch1 = i; + iBestMatch2 = j; + nBestRange = nRange; + } + } + } + + // Merge the second entry into the first. + nPCTSize--; + abyPCT[iBestMatch2*3+0] = abyPCT[nPCTSize*3+0]; + abyPCT[iBestMatch2*3+1] = abyPCT[nPCTSize*3+1]; + abyPCT[iBestMatch2*3+2] = abyPCT[nPCTSize*3+2]; + + for( i = 0; i < 256; i++ ) + { + // merge matching entries. + if( anRemap[i] == iBestMatch2 ) + anRemap[i] = iBestMatch1; + + // shift the last PCT entry into the new hole. + if( anRemap[i] == nPCTSize ) + anRemap[i] = iBestMatch2; + } + } + +/* -------------------------------------------------------------------- */ +/* Write the PCT. */ +/* -------------------------------------------------------------------- */ + if( !BSBWritePCT( psBSB, nPCTSize, abyPCT ) ) + { + BSBClose( psBSB ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write the GCPs. */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + int nGCPCount = poSrcDS->GetGCPCount(); + if (nGCPCount) + { + const char* pszGCPProjection = poSrcDS->GetGCPProjection(); + if ( BSBIsSRSOK(pszGCPProjection) ) + { + const GDAL_GCP * pasGCPList = poSrcDS->GetGCPs(); + for( i = 0; i < nGCPCount; i++ ) + { + VSIFPrintfL( psBSB->fp, + "REF/%d,%f,%f,%f,%f\n", + i+1, + pasGCPList[i].dfGCPPixel, pasGCPList[i].dfGCPLine, + pasGCPList[i].dfGCPY, pasGCPList[i].dfGCPX); + } + } + } + else if (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None) + { + const char* pszProjection = poSrcDS->GetProjectionRef(); + if ( BSBIsSRSOK(pszProjection) ) + { + VSIFPrintfL( psBSB->fp, + "REF/%d,%d,%d,%f,%f\n", + 1, + 0, 0, + adfGeoTransform[3] + 0 * adfGeoTransform[4] + 0 * adfGeoTransform[5], + adfGeoTransform[0] + 0 * adfGeoTransform[1] + 0 * adfGeoTransform[2]); + VSIFPrintfL( psBSB->fp, + "REF/%d,%d,%d,%f,%f\n", + 2, + nXSize, 0, + adfGeoTransform[3] + nXSize * adfGeoTransform[4] + 0 * adfGeoTransform[5], + adfGeoTransform[0] + nXSize * adfGeoTransform[1] + 0 * adfGeoTransform[2]); + VSIFPrintfL( psBSB->fp, + "REF/%d,%d,%d,%f,%f\n", + 3, + nXSize, nYSize, + adfGeoTransform[3] + nXSize * adfGeoTransform[4] + nYSize * adfGeoTransform[5], + adfGeoTransform[0] + nXSize * adfGeoTransform[1] + nYSize * adfGeoTransform[2]); + VSIFPrintfL( psBSB->fp, + "REF/%d,%d,%d,%f,%f\n", + 4, + 0, nYSize, + adfGeoTransform[3] + 0 * adfGeoTransform[4] + nYSize * adfGeoTransform[5], + adfGeoTransform[0] + 0 * adfGeoTransform[1] + nYSize * adfGeoTransform[2]); + } + } + +/* -------------------------------------------------------------------- */ +/* Loop over image, copying image data. */ +/* -------------------------------------------------------------------- */ + GByte *pabyScanline; + CPLErr eErr = CE_None; + + pabyScanline = (GByte *) CPLMalloc( nXSize ); + + for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ ) + { + eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + pabyScanline, nXSize, 1, GDT_Byte, + nBands, nBands * nXSize ); + if( eErr == CE_None ) + { + for( i = 0; i < nXSize; i++ ) + pabyScanline[i] = (GByte) anRemap[pabyScanline[i]]; + + if( !BSBWriteScanline( psBSB, pabyScanline ) ) + eErr = CE_Failure; + } + } + + CPLFree( pabyScanline ); + +/* -------------------------------------------------------------------- */ +/* cleanup */ +/* -------------------------------------------------------------------- */ + BSBClose( psBSB ); + + if( eErr != CE_None ) + { + VSIUnlink( pszFilename ); + return NULL; + } + else + return (GDALDataset *) GDALOpen( pszFilename, GA_ReadOnly ); +} +#endif + +/************************************************************************/ +/* GDALRegister_BSB() */ +/************************************************************************/ + +void GDALRegister_BSB() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "BSB" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "BSB" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Maptech BSB Nautical Charts" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#BSB" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); +#ifdef BSB_CREATE + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte" ); +#endif + poDriver->pfnOpen = BSBDataset::Open; + poDriver->pfnIdentify = BSBDataset::Identify; +#ifdef BSB_CREATE + poDriver->pfnCreateCopy = BSBCreateCopy; +#endif + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/bsb/makefile.vc b/bazaar/plugin/gdal/frmts/bsb/makefile.vc new file mode 100644 index 000000000..c9159b94b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/bsb/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = bsbdataset.obj bsb_read.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/ceos/GNUmakefile b/bazaar/plugin/gdal/frmts/ceos/GNUmakefile new file mode 100644 index 000000000..9730a2fec --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos/GNUmakefile @@ -0,0 +1,17 @@ + + +include ../../GDALmake.opt + +OBJ = ceosopen.o ceosdataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +ceostest$(EXE): ceostest.$(OBJ_EXT) + $(LD) $(LDFLAGS) ceostest.$(OBJ_EXT) $(CONFIG_LIBS) -o ceostest$(EXE) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/ceos/ceosdataset.cpp b/bazaar/plugin/gdal/frmts/ceos/ceosdataset.cpp new file mode 100644 index 000000000..b786c7a3b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos/ceosdataset.cpp @@ -0,0 +1,257 @@ +/****************************************************************************** + * $Id: ceosdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: CEOS Translator + * Purpose: GDALDataset driver for CEOS translator. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ceosopen.h" +#include "gdal_pam.h" + +CPL_CVSID("$Id: ceosdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +CPL_C_START +void GDALRegister_CEOS(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* CEOSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class CEOSRasterBand; + +class CEOSDataset : public GDALPamDataset +{ + friend class CEOSRasterBand; + + CEOSImage *psCEOS; + + public: + CEOSDataset(); + ~CEOSDataset(); + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* CEOSRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class CEOSRasterBand : public GDALPamRasterBand +{ + friend class CEOSDataset; + + public: + + CEOSRasterBand( CEOSDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + + +/************************************************************************/ +/* CEOSRasterBand() */ +/************************************************************************/ + +CEOSRasterBand::CEOSRasterBand( CEOSDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Byte; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr CEOSRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + CEOSDataset *poCEOS_DS = (CEOSDataset *) poDS; + + CPLAssert( nBlockXOff == 0 ); + + return( CEOSReadScanline(poCEOS_DS->psCEOS, nBand, nBlockYOff+1, pImage) ); +} + +/************************************************************************/ +/* ==================================================================== */ +/* CEOSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* CEOSDataset() */ +/************************************************************************/ + +CEOSDataset::CEOSDataset() + +{ + psCEOS = NULL; +} + +/************************************************************************/ +/* ~CEOSDataset() */ +/************************************************************************/ + +CEOSDataset::~CEOSDataset() + +{ + FlushCache(); + if( psCEOS ) + CEOSClose( psCEOS ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *CEOSDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + CEOSImage *psCEOS; + int i; + +/* -------------------------------------------------------------------- */ +/* Before trying CEOSOpen() we first verify that the first */ +/* record is in fact a CEOS file descriptor record. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 100 ) + return NULL; + + if( poOpenInfo->pabyHeader[4] != 0x3f + || poOpenInfo->pabyHeader[5] != 0xc0 + || poOpenInfo->pabyHeader[6] != 0x12 + || poOpenInfo->pabyHeader[7] != 0x12 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ + psCEOS = CEOSOpen( poOpenInfo->pszFilename, "rb" ); + + if( psCEOS == NULL ) + return( NULL ); + + if( psCEOS->nBitsPerPixel != 8 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The CEOS driver cannot handle nBitsPerPixel = %d", + psCEOS->nBitsPerPixel ); + CEOSClose(psCEOS); + return NULL; + } + + if( !GDALCheckDatasetDimensions(psCEOS->nPixels, psCEOS->nBands) || + !GDALCheckBandCount(psCEOS->nBands, FALSE) ) + { + CEOSClose( psCEOS ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CEOSClose(psCEOS); + CPLError( CE_Failure, CPLE_NotSupported, + "The CEOS driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + CEOSDataset *poDS; + + poDS = new CEOSDataset(); + + poDS->psCEOS = psCEOS; + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = psCEOS->nPixels; + poDS->nRasterYSize = psCEOS->nLines; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = psCEOS->nBands;; + + for( i = 0; i < poDS->nBands; i++ ) + poDS->SetBand( i+1, new CEOSRasterBand( poDS, i+1 ) ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_GTiff() */ +/************************************************************************/ + +void GDALRegister_CEOS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "CEOS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "CEOS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "CEOS Image" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#CEOS" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = CEOSDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/ceos/ceosopen.c b/bazaar/plugin/gdal/frmts/ceos/ceosopen.c new file mode 100644 index 000000000..0c0ae135b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos/ceosopen.c @@ -0,0 +1,379 @@ +/****************************************************************************** + * $Id: ceosopen.c 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: CEOS Translator + * Purpose: Implementation of non-GDAL dependent CEOS support. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2007-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ceosopen.h" + +CPL_CVSID("$Id: ceosopen.c 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* CEOSScanInt() */ +/* */ +/* Read up to nMaxChars from the passed string, and interpret */ +/* as an integer. */ +/************************************************************************/ + +static long CEOSScanInt( const char * pszString, int nMaxChars ) + +{ + char szWorking[33]; + int i; + + if( nMaxChars > 32 || nMaxChars == 0 ) + nMaxChars = 32; + + for( i = 0; i < nMaxChars && pszString[i] != '\0'; i++ ) + szWorking[i] = pszString[i]; + + szWorking[i] = '\0'; + + return( atoi(szWorking) ); +} + +/************************************************************************/ +/* CEOSReadRecord() */ +/* */ +/* Read a single CEOS record at the current point in the file. */ +/* Return NULL after reporting an error if it fails, otherwise */ +/* return the record. */ +/************************************************************************/ + +CEOSRecord * CEOSReadRecord( CEOSImage *psImage ) + +{ + GByte abyHeader[12]; + CEOSRecord *psRecord; + +/* -------------------------------------------------------------------- */ +/* Read the standard CEOS header. */ +/* -------------------------------------------------------------------- */ + if( VSIFEofL( psImage->fpImage ) ) + return NULL; + + if( VSIFReadL( abyHeader, 1, 12, psImage->fpImage ) != 12 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Ran out of data reading CEOS record." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Extract this information. */ +/* -------------------------------------------------------------------- */ + psRecord = (CEOSRecord *) CPLMalloc(sizeof(CEOSRecord)); + if( psImage->bLittleEndian ) + { + CPL_SWAP32PTR( abyHeader + 0 ); + CPL_SWAP32PTR( abyHeader + 8 ); + } + + psRecord->nRecordNum = abyHeader[0] * 256 * 256 * 256 + + abyHeader[1] * 256 * 256 + + abyHeader[2] * 256 + + abyHeader[3]; + + psRecord->nRecordType = abyHeader[4] * 256 * 256 * 256 + + abyHeader[5] * 256 * 256 + + abyHeader[6] * 256 + + abyHeader[7]; + + psRecord->nLength = abyHeader[8] * 256 * 256 * 256 + + abyHeader[9] * 256 * 256 + + abyHeader[10] * 256 + + abyHeader[11]; + +/* -------------------------------------------------------------------- */ +/* Does it look reasonable? We assume there can't be too many */ +/* records and that the length must be between 12 and 200000. */ +/* -------------------------------------------------------------------- */ + if( psRecord->nRecordNum < 0 || psRecord->nRecordNum > 200000 + || psRecord->nLength < 12 || psRecord->nLength > 200000 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CEOS record leader appears to be corrupt.\n" + "Record Number = %d, Record Length = %d\n", + psRecord->nRecordNum, psRecord->nLength ); + CPLFree( psRecord ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the remainder of the record into a buffer. Ensure that */ +/* the first 12 bytes gets moved into this buffer as well. */ +/* -------------------------------------------------------------------- */ + psRecord->pachData = (char *) VSIMalloc(psRecord->nLength ); + if( psRecord->pachData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Out of memory allocated %d bytes for CEOS record data.\n" + "Are you sure you aren't leaking CEOSRecords?\n", + psRecord->nLength ); + CPLFree( psRecord ); + return NULL; + } + + memcpy( psRecord->pachData, abyHeader, 12 ); + + if( (int)VSIFReadL( psRecord->pachData + 12, 1, psRecord->nLength-12, + psImage->fpImage ) + != psRecord->nLength - 12 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Short read on CEOS record data.\n" ); + CPLFree( psRecord ); + return NULL; + } + + return psRecord; +} + +/************************************************************************/ +/* CEOSDestroyRecord() */ +/* */ +/* Free a record. */ +/************************************************************************/ + +void CEOSDestroyRecord( CEOSRecord * psRecord ) + +{ + CPLFree( psRecord->pachData ); + CPLFree( psRecord ); +} + +/************************************************************************/ +/* CEOSOpen() */ +/************************************************************************/ + +/** + * Open a CEOS transfer. + * + * @param pszFilename The name of the CEOS imagery file (ie. imag_01.dat). + * @param pszAccess An fopen() style access string. Should be either "rb" for + * read-only access, or "r+b" for read, and update access. + * + * @return A CEOSImage pointer as a handle to the image. The CEOSImage also + * has various information about the image available. A NULL is returned + * if an error occurs. + */ + +CEOSImage * CEOSOpen( const char * pszFilename, const char * pszAccess ) + +{ + VSILFILE *fp; + CEOSRecord *psRecord; + CEOSImage *psImage; + int nSeqNum, i; + GByte abyHeader[16]; + +/* -------------------------------------------------------------------- */ +/* Try to open the imagery file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszFilename, pszAccess ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open CEOS file `%s' with access `%s'.\n", + pszFilename, pszAccess ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a CEOSImage structure, and initialize it. */ +/* -------------------------------------------------------------------- */ + psImage = (CEOSImage *) CPLCalloc(1,sizeof(CEOSImage)); + psImage->fpImage = fp; + + psImage->nPixels = psImage->nLines = psImage->nBands = 0; + +/* -------------------------------------------------------------------- */ +/* Preread info on the first record, to establish if it is */ +/* little endian. */ +/* -------------------------------------------------------------------- */ + VSIFReadL( abyHeader, 16, 1, fp ); + VSIFSeekL( fp, 0, SEEK_SET ); + + if( abyHeader[0] != 0 || abyHeader[1] != 0 ) + psImage->bLittleEndian = TRUE; + +/* -------------------------------------------------------------------- */ +/* Try to read the header record. */ +/* -------------------------------------------------------------------- */ + psRecord = CEOSReadRecord( psImage ); + if( psRecord == NULL ) + { + CEOSClose( psImage ); + return NULL; + } + + if( psRecord->nRecordType != CRT_IMAGE_FDR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Got a %X type record, instead of the expected\n" + "file descriptor record on file %s.\n", + psRecord->nRecordType, pszFilename ); + + CEOSDestroyRecord( psRecord ); + CEOSClose( psImage ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* The sequence number should be 2 indicating this is the */ +/* imagery file. */ +/* -------------------------------------------------------------------- */ + nSeqNum = CEOSScanInt( psRecord->pachData + 44, 4 ); + if( nSeqNum != 2 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Got a %d file sequence number, instead of the expected\n" + "2 indicating imagery on file %s.\n" + "Continuing to access anyways.\n", + nSeqNum, pszFilename ); + } + +/* -------------------------------------------------------------------- */ +/* Extract various information. */ +/* -------------------------------------------------------------------- */ + psImage->nImageRecCount = CEOSScanInt( psRecord->pachData+180, 6 ); + psImage->nImageRecLength = CEOSScanInt( psRecord->pachData+186, 6 ); + psImage->nBitsPerPixel = CEOSScanInt( psRecord->pachData+216, 4 ); + psImage->nBands = CEOSScanInt( psRecord->pachData+232, 4 ); + psImage->nLines = CEOSScanInt( psRecord->pachData+236, 8 ); + psImage->nPixels = CEOSScanInt( psRecord->pachData+248, 8 ); + + psImage->nPrefixBytes = CEOSScanInt( psRecord->pachData+276, 4 ); + psImage->nSuffixBytes = CEOSScanInt( psRecord->pachData+288, 4 ); + + + if( psImage->nImageRecLength <= 0 || + psImage->nPrefixBytes < 0 || + psImage->nBands > INT_MAX / psImage->nImageRecLength || + (size_t)psImage->nBands > INT_MAX / sizeof(int)) + { + CEOSDestroyRecord( psRecord ); + CEOSClose( psImage ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to establish the layout of the imagery data. */ +/* -------------------------------------------------------------------- */ + psImage->nLineOffset = psImage->nBands * psImage->nImageRecLength; + + psImage->panDataStart = (int *) VSIMalloc(sizeof(int) * psImage->nBands); + if( psImage->panDataStart == NULL ) + { + CEOSDestroyRecord( psRecord ); + CEOSClose( psImage ); + return NULL; + } + + for( i = 0; i < psImage->nBands; i++ ) + { + psImage->panDataStart[i] = + psRecord->nLength + i * psImage->nImageRecLength + + 12 + psImage->nPrefixBytes; + } + + CEOSDestroyRecord( psRecord ); + + return psImage; +} + +/************************************************************************/ +/* CEOSReadScanline() */ +/************************************************************************/ + +/** + * Read a scanline of image. + * + * @param psCEOS The CEOS dataset handle returned by CEOSOpen(). + * @param nBand The band number (ie. 1, 2, 3). + * @param nScanline The scanline requested, one based. + * @param pData The data buffer to read into. Must be at least nPixels * + * nBitesPerPixel bits long. + * + * @return CPLErr Returns error indicator or CE_None if the read succeeds. + */ + +CPLErr CEOSReadScanline( CEOSImage * psCEOS, int nBand, int nScanline, + void * pData ) + +{ + int nOffset, nBytes; + + /* + * As a short cut, I currently just seek to the data, and read it + * raw, rather than trying to read ceos records properly. + */ + + nOffset = psCEOS->panDataStart[nBand-1] + + (nScanline-1) * psCEOS->nLineOffset; + + if( VSIFSeekL( psCEOS->fpImage, nOffset, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Seek to %d for scanline %d failed.\n", + nOffset, nScanline ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Read the data. */ +/* -------------------------------------------------------------------- */ + nBytes = psCEOS->nPixels * psCEOS->nBitsPerPixel / 8; + if( (int) VSIFReadL( pData, 1, nBytes, psCEOS->fpImage ) != nBytes ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Read of %d bytes for scanline %d failed.\n", + nBytes, nScanline ); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* CEOSClose() */ +/************************************************************************/ + +/** + * Close a CEOS transfer. Any open files are closed, and memory deallocated. + * + * @param psCEOS The CEOSImage handle from CEOSOpen to be closed. + */ + +void CEOSClose( CEOSImage * psCEOS ) + +{ + CPLFree( psCEOS->panDataStart ); + VSIFCloseL( psCEOS->fpImage ); + CPLFree( psCEOS ); +} diff --git a/bazaar/plugin/gdal/frmts/ceos/ceosopen.h b/bazaar/plugin/gdal/frmts/ceos/ceosopen.h new file mode 100644 index 000000000..b7034d5c0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos/ceosopen.h @@ -0,0 +1,108 @@ +/****************************************************************************** + * $Id: ceosopen.h 20996 2010-10-28 18:38:15Z rouault $ + * + * Project: CEOS Translator + * Purpose: Public (C callable) interface for CEOS and related formats such + * as Spot CAP. This stuff can be used independently of GDAL. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CEOSOPEN_H_INCLUDED +#define _CEOSOPEN_H_INCLUDED + +/* -------------------------------------------------------------------- */ +/* Include standard portability stuff. */ +/* -------------------------------------------------------------------- */ +#include "cpl_conv.h" +#include "cpl_string.h" + +/* -------------------------------------------------------------------- */ +/* Base ``class'' for ceos records. */ +/* -------------------------------------------------------------------- */ + +CPL_C_START + +typedef struct { + int nRecordNum; + GUInt32 nRecordType; + int nLength; + + char *pachData; +}CEOSRecord; + +/* well known record types */ +#define CRT_IMAGE_FDR 0x3FC01212 +#define CRT_IMAGE_DATA 0xEDED1212 + + +/* -------------------------------------------------------------------- */ +/* Main CEOS info structure. */ +/* -------------------------------------------------------------------- */ + +typedef struct { + + /* public information */ + int nPixels; + int nLines; + int nBands; + + int nBitsPerPixel; + + /* private information */ + VSILFILE *fpImage; + + int bLittleEndian; + + int nImageRecCount; + int nImageRecLength; + + int nPrefixBytes; + int nSuffixBytes; + + int *panDataStart; + int nLineOffset; + +} CEOSImage; + +/* -------------------------------------------------------------------- */ +/* External Prototypes */ +/* -------------------------------------------------------------------- */ + +CEOSImage CPL_ODLL *CEOSOpen( const char *, const char * ); +void CPL_ODLL CEOSClose( CEOSImage * ); +CPLErr CPL_ODLL CEOSReadScanline( CEOSImage *psImage, int nBand, + int nScanline, void * pData ); + +/* -------------------------------------------------------------------- */ +/* Internal prototypes. */ +/* -------------------------------------------------------------------- */ +CEOSRecord CPL_ODLL *CEOSReadRecord( CEOSImage * ); +void CPL_ODLL CEOSDestroyRecord( CEOSRecord * ); + +CPL_C_END + +#endif /* ndef _CEOSOPEN_H_INCLUDED */ + + + diff --git a/bazaar/plugin/gdal/frmts/ceos/ceostest.c b/bazaar/plugin/gdal/frmts/ceos/ceostest.c new file mode 100644 index 000000000..6c8c99238 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos/ceostest.c @@ -0,0 +1,69 @@ +/****************************************************************************** + * $Id: ceostest.c 20504 2010-09-02 02:40:49Z warmerdam $ + * + * Project: CEOS Translator + * Purpose: Test mainline. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ceosopen.h" + +/************************************************************************/ +/* main() */ +/************************************************************************/ + +int main( int nArgc, char ** papszArgv ) + +{ + const char *pszFilename; + FILE *fp; + CEOSRecord *psRecord; + int nPosition = 0; + + if( nArgc > 1 ) + pszFilename = papszArgv[1]; + else + pszFilename = "imag_01.dat"; + + fp = VSIFOpenL( pszFilename, "rb" ); + if( fp == NULL ) + { + fprintf( stderr, "Can't open %s at all.\n", pszFilename ); + exit( 1 ); + } + + while( !VSIFEofL(fp) + && (psRecord = CEOSReadRecord( fp )) != NULL ) + { + printf( "%9d:%4d:%8x:%d\n", + nPosition, psRecord->nRecordNum, + psRecord->nRecordType, psRecord->nLength ); + CEOSDestroyRecord( psRecord ); + + nPosition = (int) VSIFTellL( fp ); + } + VSIFCloseL( fp ); + + exit( 0 ); +} diff --git a/bazaar/plugin/gdal/frmts/ceos/makefile.vc b/bazaar/plugin/gdal/frmts/ceos/makefile.vc new file mode 100644 index 000000000..4b305e482 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = ceosopen.obj ceosdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/ceos2/GNUmakefile b/bazaar/plugin/gdal/frmts/ceos2/GNUmakefile new file mode 100644 index 000000000..3771a74c1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos2/GNUmakefile @@ -0,0 +1,18 @@ + + +OBJ = sar_ceosdataset.o \ + ceosrecipe.o ceossar.o ceos.o link.o + +include ../../GDALmake.opt + + +CPPFLAGS := -I../raw $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +$(O_OBJ): ../raw/rawdataset.h + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/ceos2/ceos.c b/bazaar/plugin/gdal/frmts/ceos2/ceos.c new file mode 100644 index 000000000..f187e4c04 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos2/ceos.c @@ -0,0 +1,408 @@ +/****************************************************************************** + * $Id: ceos.c 20996 2010-10-28 18:38:15Z rouault $ + * + * Project: ASI CEOS Translator + * Purpose: Core CEOS functions. + * Author: Paul Lahaie, pjlahaie@atlsci.com + * + ****************************************************************************** + * Copyright (c) 2000, Atlantis Scientific Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ceos.h" + +CPL_CVSID("$Id: ceos.c 20996 2010-10-28 18:38:15Z rouault $"); + +/* Function implementations of functions described in ceos.h */ + +void CeosUpdateHeaderFromBuffer(CeosRecord_t *record); + +void InitEmptyCeosRecord(CeosRecord_t *record, int32 sequence, CeosTypeCode_t typecode, int32 length) +{ + if(record) + { + if((record->Buffer = HMalloc(length)) == NULL) + { + return; + } + /* First we zero fill the buffer */ + memset(record->Buffer,0,length); + + /* Setup values inside the CeosRecord_t header */ + record->Sequence = sequence; + record->Flavour = 0; + record->FileId = 0; + record->TypeCode = typecode; + record->Subsequence = 0; + record->Length = length; + + /* Now we fill in the buffer portion as well */ + NativeToCeos( record->Buffer+__SEQUENCE_OFF, &(record->Sequence), sizeof(record->Sequence), sizeof( record->Sequence ) ); + memcpy(record->Buffer+__TYPE_OFF, &( record->TypeCode.Int32Code ), sizeof( record->TypeCode.Int32Code ) ); + NativeToCeos( record->Buffer+__LENGTH_OFF, &length, sizeof( length ), sizeof( length ) ); + } +} + +void InitCeosRecord(CeosRecord_t *record, uchar *buffer) +{ + if(record && buffer) + { + InitCeosRecordWithHeader(record, buffer, buffer+__CEOS_HEADER_LENGTH); + } +} + +void InitCeosRecordWithHeader(CeosRecord_t *record, uchar *header, uchar *buffer) +{ + if(record && buffer && header) + { + if( record->Length != 0 ) + record->Length = DetermineCeosRecordBodyLength( header ); + + if((record->Buffer = HMalloc(record->Length)) == NULL) + { + record->Length = 0; + return; + } + + /* First copy the header then the buffer */ + memcpy(record->Buffer,header,__CEOS_HEADER_LENGTH); + /* Now we copy the rest */ + memcpy(record->Buffer+__CEOS_HEADER_LENGTH,buffer,record->Length-__CEOS_HEADER_LENGTH); + + /* Now we fill in the rest of the structure! */ + memcpy(&(record->TypeCode.Int32Code),header+__TYPE_OFF,sizeof(record->TypeCode.Int32Code)); + CeosToNative(&(record->Sequence),header+__SEQUENCE_OFF,sizeof(record->Sequence), sizeof( record->Sequence ) ); + } +} + +int DetermineCeosRecordBodyLength(const uchar *header) +{ + int i; + + if(header) + { + CeosToNative(&i,header+__LENGTH_OFF,sizeof( i ), sizeof( i ) ); + + return i; + } + + return -1; +} + +void DeleteCeosRecord(CeosRecord_t *record) +{ + if(record) + { + if(record->Buffer) + { + HFree(record->Buffer); + record->Buffer = NULL; + } + HFree( record ); + } +} + +void GetCeosRecordStruct(const CeosRecord_t *record,void *struct_ptr) +{ + if(record && struct_ptr && record->Buffer) + { + memcpy(record->Buffer,struct_ptr,record->Length); + } +} + +void PutCeosRecordStruct(CeosRecord_t *record,const void *struct_ptr) +{ + int Length; + + if(record && struct_ptr) + { + CeosToNative( &Length, struct_ptr, sizeof( Length ), sizeof( Length ) ); + memcpy(record->Buffer,struct_ptr,Length); + CeosUpdateHeaderFromBuffer(record); + } +} + +void GetCeosField(CeosRecord_t *record, int32 start_byte, + const char *format, void *value) +{ + int field_size; + char *d_ptr; + char *mod_buf = NULL; + + field_size = atoi(format+1); + + if(field_size < 1) + { + return; + } + + /* Check for out of bounds */ + if(start_byte + field_size - 1 > record->Length) + { + return; + } + + if((mod_buf = (char *) HMalloc(field_size + 1)) == NULL) + { + return; + } + + memcpy(mod_buf,record->Buffer+(start_byte-1), field_size); + mod_buf[field_size] = '\0'; + + /* Switch on format type */ + switch(format[0]) + { + case 'b': + case 'B': + /* Binary data type */ + if(field_size > 1) + { + CeosToNative( value, mod_buf, field_size, field_size ); + } else { + memcpy( value, mod_buf, field_size ); + } + break; + + case 'i': + case 'I': + /* Integer type */ + *( (int *)value) = atoi(mod_buf); + break; + + case 'f': + case 'F': + case 'e': + case 'E': + /* Double precision float data type */ + + /* Change the 'D' exponent separators to 'e' */ + if( ( d_ptr = strchr(mod_buf, 'd') ) != NULL) + { + *d_ptr = 'e'; + } + if( ( d_ptr = strchr(mod_buf, 'D') ) != NULL) + { + *d_ptr = 'e'; + } + + *( (double *)value) = strtod(mod_buf,NULL); + break; + case 'a': + case 'A': + /* ASCII.. We just easily extract it */ + ( (char *)value)[field_size] = '\0'; + memcpy( value, mod_buf, field_size ); + break; + + default: + /* Unknown format */ + return; + } + + HFree(mod_buf); + +} + +void SetCeosField(CeosRecord_t *record, int32 start_byte, char *format, void *value) +{ + int field_size; + char * temp_buf = NULL; + char printf_format[ 20 ]; + + field_size = 0; + sscanf(&format[1], "%d", &field_size); + if(field_size < 1) + { + return; + } + + /* Check for bounds */ + if(start_byte + field_size - 1 > record->Length) + { + return; + } + + /* Make a local buffer to print into */ + if((temp_buf = (char *) HMalloc(field_size+1)) == NULL) + { + return; + } + switch(format[0]) + { + case 'b': + case 'B': + /* Binary data type */ + if(field_size > 1) + { + NativeToCeos( value, temp_buf, field_size, field_size ); + } else { + memcpy(value,temp_buf,field_size); + } + break; + + case 'i': + case 'I': + /* Integer data type */ + sprintf( printf_format,"%%%s%c",format+1, 'd'); + sprintf( temp_buf, printf_format, *(int *) value); + break; + + case 'f': + case 'F': + /* Double precision floating point data type */ + sprintf(printf_format, "%%%s%c", format+1, 'g'); + sprintf(temp_buf, printf_format, *(double *)value); + break; + + case 'e': + case 'E': + /* Double precision floating point data type (forced exponent) */ + sprintf(printf_format,"%%%s%c", format+1, 'e'); + sprintf(temp_buf, printf_format, *(double *)value); + break; + + case 'a': + case 'A': + strncpy(temp_buf,value,field_size+1); + temp_buf[field_size] = '0'; + break; + + default: + /* Unknown format */ + return; + } + + memcpy(record->Buffer + start_byte -1, temp_buf, field_size); + + HFree(temp_buf); +} + +void SetIntCeosField(CeosRecord_t *record, int32 start_byte, int32 length, int32 value) +{ + int integer_value = value; + char total_len[12]; /* 12 because 2^32 -> 4294967296 + I + null */ + + sprintf(total_len,"I%d",length); + SetCeosField(record,start_byte,total_len,&integer_value); +} + +CeosRecord_t *FindCeosRecord(Link_t *record_list, CeosTypeCode_t typecode, int32 fileid, int32 flavour, int32 subsequence) +{ + Link_t *Link; + CeosRecord_t *record; + + for( Link = record_list; Link != NULL; Link = Link->next ) + { + record = (CeosRecord_t *)Link->object; + + if( (record->TypeCode.Int32Code == typecode.Int32Code) + && ( ( fileid == -1 ) || ( record->FileId == fileid ) ) + && ( ( flavour == -1 ) || ( record->Flavour == flavour ) ) + && ( ( subsequence == -1 ) || ( record->Subsequence == subsequence ) ) ) + return record; + } + + return NULL; +} + +void SerializeCeosRecordsToFile(Link_t *record_list, VSILFILE *fp) +{ + Link_t *list; + CeosRecord_t crec; + unsigned char *Buffer; + + list = record_list; + + while(list != NULL) + { + memcpy(&crec,list->object,sizeof(CeosRecord_t)); + Buffer = crec.Buffer; + crec.Buffer = NULL; + VSIFWriteL(&crec,sizeof(CeosRecord_t),1,fp); + VSIFWriteL(Buffer,crec.Length,1,fp); + } +} + +void SerializeCeosRecordsFromFile(Link_t *record_list, VSILFILE *fp) +{ + CeosRecord_t *crec; + Link_t *Link; + + while(!VSIFEofL(fp)) + { + crec = HMalloc(sizeof(CeosRecord_t)); + VSIFReadL(crec,sizeof(CeosRecord_t),1,fp); + crec->Buffer = HMalloc(crec->Length * sizeof(char) ); + VSIFReadL(crec->Buffer,sizeof(char),crec->Length,fp); + Link = ceos2CreateLink(crec); + AddLink(record_list,Link); + } +} + +void CeosUpdateHeaderFromBuffer(CeosRecord_t *record) +{ + if(record && record->Buffer) + { + CeosToNative( &( record->Length ), record->Buffer+__LENGTH_OFF, sizeof(record->Length ), sizeof( record->Length ) ); + memcpy(&(record->TypeCode.Int32Code),record->Buffer+__TYPE_OFF,sizeof(record->TypeCode.Int32Code)); + CeosToNative(&(record->Sequence),record->Buffer+__SEQUENCE_OFF,sizeof(record->Sequence ), sizeof( record->Sequence ) ); + } + record->Subsequence = 0; +} + +#ifdef CPL_LSB + +void swapbyte(void *dst,void *src,int toswap) +{ + int i,e; + unsigned char *in = (unsigned char *) src; + unsigned char *out = (unsigned char *) dst; + + for(i = 0,e=toswap;i < toswap;i++,e--) + { + out[i] = in[e-1]; + } +} + +void NativeToCeos( void *dst, const void *src, const size_t len, const size_t swapunit) +{ + int i; + int remainder; + int units; + + + remainder = len % swapunit; + + units = len - remainder; + + for(i = 0;i < units; i += swapunit ) + { + swapbyte( ( unsigned char *) dst + i, ( unsigned char * ) src + i, swapunit); + } + + if(remainder) + { + memcpy( ( unsigned char * ) dst + i, ( unsigned char * ) src + i, remainder ); + } +} + +#endif diff --git a/bazaar/plugin/gdal/frmts/ceos2/ceos.h b/bazaar/plugin/gdal/frmts/ceos2/ceos.h new file mode 100644 index 000000000..4cfa55c5f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos2/ceos.h @@ -0,0 +1,332 @@ +/****************************************************************************** + * $Id: ceos.h 20996 2010-10-28 18:38:15Z rouault $ + * + * Project: ASI CEOS Translator + * Purpose: CEOS library prototypes + * Author: Paul Lahaie, pjlahaie@atlsci.com + * + ****************************************************************************** + * Copyright (c) 2000, Atlantis Scientific Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + +#ifndef __CEOS_H +#define __CEOS_H + +#include "cpl_conv.h" + +CPL_C_START + +#define int32 GInt32 +#define TBool int +#define uchar unsigned char + +typedef struct Link_t_struct +{ + struct Link_t_struct *next; + void *object; +} Link_t; + +#define HMalloc CPLMalloc +#define HFree CPLFree +#define HCalloc CPLCalloc + +Link_t *ceos2CreateLink( void * pObject ); +Link_t *InsertLink(Link_t *psList, Link_t *psLink); +void DestroyList( Link_t *psList ); +Link_t *AddLink( Link_t *psList, Link_t *psLink ); + +/* Basic CEOS header defs */ + +#define __SEQUENCE_OFF 0 +#define __TYPE_OFF 4 +#define __LENGTH_OFF 8 +#define __CEOS_HEADER_LENGTH 12 + +/* Defines for CEOS banding type */ + +#define __CEOS_IL_PIXEL 1 +#define __CEOS_IL_LINE 2 +#define __CEOS_IL_BAND 3 + +/* Defines for CEOS data types */ + +#define __CEOS_TYP_CHAR 1 +#define __CEOS_TYP_UCHAR 2 +#define __CEOS_TYP_SHORT 3 +#define __CEOS_TYP_USHORT 4 +#define __CEOS_TYP_LONG 5 +#define __CEOS_TYP_ULONG 6 +#define __CEOS_TYP_FLOAT 7 +#define __CEOS_TYP_DOUBLE 8 +#define __CEOS_TYP_COMPLEX_CHAR 9 +#define __CEOS_TYP_COMPLEX_UCHAR 10 +#define __CEOS_TYP_COMPLEX_SHORT 11 +#define __CEOS_TYP_COMPLEX_USHORT 12 +#define __CEOS_TYP_COMPLEX_LONG 13 +#define __CEOS_TYP_COMPLEX_ULONG 14 +#define __CEOS_TYP_COMPLEX_FLOAT 15 +#define __CEOS_TYP_CCP_COMPLEX_FLOAT 16 /* COMPRESSED CROSS PRODUCT */ +#define __CEOS_TYP_PALSAR_COMPLEX_SHORT 17 /* PALSAR - treat as COMPLEX SHORT*/ + +/* Defines for CEOS file names */ + +#define __CEOS_VOLUME_DIR_FILE 0 +#define __CEOS_LEADER_FILE 1 +#define __CEOS_IMAGRY_OPT_FILE 2 +#define __CEOS_TRAILER_FILE 3 +#define __CEOS_NULL_VOL_FILE 4 +#define __CEOS_ANY_FILE -1 + +/* Defines for Recipe values */ + +#define __CEOS_REC_NUMCHANS 1 +#define __CEOS_REC_INTERLEAVE 2 +#define __CEOS_REC_DATATYPE 3 +#define __CEOS_REC_BPR 4 +#define __CEOS_REC_LINES 5 +#define __CEOS_REC_TBP 6 +#define __CEOS_REC_BBP 7 +#define __CEOS_REC_PPL 8 +#define __CEOS_REC_LBP 9 +#define __CEOS_REC_RBP 10 +#define __CEOS_REC_BPP 11 +#define __CEOS_REC_RPL 12 +#define __CEOS_REC_PPR 13 +#define __CEOS_REC_IDS 14 +#define __CEOS_REC_FDL 15 +#define __CEOS_REC_PIXORD 16 +#define __CEOS_REC_LINORD 17 +#define __CEOS_REC_PRODTYPE 18 +#define __CEOS_REC_RECORDSIZE 19 +#define __CEOS_REC_SUFFIX_SIZE 20 +#define __CEOS_REC_PDBPR 21 + +/* Defines for Recipe Types */ + +#define __CEOS_REC_TYP_A 1 +#define __CEOS_REC_TYP_B 2 +#define __CEOS_REC_TYP_I 3 + +/* Defines for SAR Embedded info */ + +#define __CEOS_SAR_ACQ_YEAR 1 +#define __CEOS_SAR_ACQ_DAY 2 +#define __CEOS_SAR_ACQ_MSEC 4 +#define __CEOS_SAR_TRANS_POL 8 +#define __CEOS_SAR_PULSE_REP 16 +#define __CEOS_SAR_SLANT_FIRST 32 +#define __CEOS_SAR_SLANT_MID 64 +#define __CEOS_SAR_SLANT_LAST 128 + +/* Maximum size of LUT for Calibration records */ +#define __CEOS_RADAR_MAX_LUT 512 +#define __CEOS_RADAR_FLIP_DATE 19980101 +#define __CEOS_RADAR_FACILITY "CDPF-RSAT" + + +typedef union +{ + int32 Int32Code; + struct + { + uchar Subtype1; + uchar Type; + uchar Subtype2; + uchar Subtype3; + } UCharCode; +} CeosTypeCode_t; + +typedef struct +{ + int32 Sequence; + CeosTypeCode_t TypeCode; + int32 Length; + int32 Flavour; + int32 Subsequence; + int32 FileId; + uchar * Buffer; +} CeosRecord_t; + +struct CeosSARImageDesc +{ + TBool ImageDescValid; + int NumChannels; + int32 ChannelInterleaving; + int32 DataType; + int BytesPerRecord; + int Lines; + int TopBorderPixels; + int BottomBorderPixels; + int PixelsPerLine; + int LeftBorderPixels; + int RightBorderPixels; + int BytesPerPixel; + int RecordsPerLine; + int PixelsPerRecord; + int ImageDataStart; + int ImageSuffixData; + int FileDescriptorLength; + int32 PixelOrder; + int32 LineOrder; + int PixelDataBytesPerRecord; +}; + +typedef struct +{ + int32 Flavour; + int32 Sensor; + int32 ProductType; + int32 FileNamingConvention; + TBool VolumeDirectoryFile; + TBool SARLeaderFile; + TBool ImagryOptionsFile; + TBool SARTrailerFile; + TBool NullVolumeDirectoryFile; + + struct CeosSARImageDesc ImageDesc; + + Link_t * RecordList; +} CeosSARVolume_t; + +typedef struct +{ + int ImageDescValue; + int Override; + int FileId; + struct { unsigned char Subtype1, + Type, + Subtype2, + Subtype3; + } TypeCode; + int Offset; + int Length; + int Type; +} CeosRecipeType_t; + + +typedef struct +{ + CeosRecipeType_t *Recipe; +} CeosSARImageDescRecipe_t; + + +typedef struct +{ + int32 ValidFields; + TBool SensorUpdate; + int32 AcquisitionYear; + int32 AcquisitionDay; + int32 AcquisitionMsec; + int32 TransmittedPolarization; + int32 ReceivedPolarization; + int32 PulsRepetitionFrequency; + int32 SlantRangeFirstPixel; + int32 SlantRangeMidPixel; + int32 SlantRangeLastPixel; +} CeosSAREmbeddedInfo_t; + +typedef struct +{ + double Slant[ 6 ]; + double Lut[ 512 ]; + double SemiMajorAxis; + double PlatformLatitude; + double CalibrationScale; + int NumberOfSamples; + int Increment; + TBool PossiblyFlipped; + +} CeosRadarCalibration_t; + + +/* Function prototypes */ + +void InitEmptyCeosRecord(CeosRecord_t *record, int32 sequence, CeosTypeCode_t typecode, int32 length); + +void InitCeosRecord(CeosRecord_t *record, uchar *buffer); + +void InitCeosRecordWithHeader(CeosRecord_t *record, uchar *header, uchar *buffer); + +int DetermineCeosRecordBodyLength(const uchar *header); + +void DeleteCeosRecord(CeosRecord_t *record); + +void GetCeosRecordStruct(const CeosRecord_t *record, void *struct_ptr); + +void PutCeosRecordStruct(CeosRecord_t *record, const void *struct_ptr); + +void GetCeosField(CeosRecord_t *, int32, const char *, void *); + +void SetCeosField(CeosRecord_t *record, int32 start_byte, char *format, void *value); + +void SetIntCeosField(CeosRecord_t *record, int32 start_byte, int32 length, int32 value); + +CeosRecord_t *FindCeosRecord(Link_t *record_list, CeosTypeCode_t typecode, int32 fileid, int32 flavour, int32 subsequence); + +void SerializeCeosRecordsToFile(Link_t *record_list, VSILFILE *fp); + +void SerializeCeosRecordsFromFile( Link_t *record_list, VSILFILE *fp ); + +void InitCeosSARVolume( CeosSARVolume_t *volume, int32 file_name_convention ); + +void GetCeosSARImageDesc( CeosSARVolume_t *volume ); + +void GetCeosSARImageDescInfo(CeosSARVolume_t *volume, CeosSARImageDescRecipe_t *recipe); + +void CalcCeosSARImageFilePosition(CeosSARVolume_t *volume, int channel, int line, int *record, int *file_offset); + +int32 GetCeosSARImageData(CeosSARVolume_t *volume, CeosRecord_t *processed_data_record, int channel, int xoff, int xsize, int bufsize, uchar *buffer); + +void DetermineCeosSARPixelOrder(CeosSARVolume_t *volume, CeosRecord_t *record ); + +void GetCeosSAREmbeddedInfo(CeosSARVolume_t *volume, CeosRecord_t *processed_data_record, CeosSAREmbeddedInfo_t *info); + +void DeleteCeosSARVolume(CeosSARVolume_t *volume); + +void RegisterRecipes(void); +void FreeRecipes(); + +void AddRecipe( int ( *function )( CeosSARVolume_t *volume, void *token ), + void *token, const char *name ); + +int CeosDefaultRecipe( CeosSARVolume_t *volume, void *token ); +int ScanSARRecipeFCN( CeosSARVolume_t *volume, void *token ); + +/* ceoscalib.c function declarations */ + +CeosRadarCalibration_t *GetCeosRadarCalibration( CeosSARVolume_t *volume ); + +/* CEOS byte swapping stuff */ + +#if defined(CPL_MSB) +#define NativeToCeos(a,b,c,d) memcpy(a,b,c) +#define CeosToNative(a,b,c,d) memcpy(a,b,c) +#else +void NativeToCeos( void *dst, const void *src, const size_t len, const size_t swap_unit); +#define CeosToNative(a,b,c,d) NativeToCeos(a,b,c,d) +#endif + +/* Recipe defines */ + +CPL_C_END + +#endif diff --git a/bazaar/plugin/gdal/frmts/ceos2/ceosrecipe.c b/bazaar/plugin/gdal/frmts/ceos2/ceosrecipe.c new file mode 100644 index 000000000..33e06103a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos2/ceosrecipe.c @@ -0,0 +1,758 @@ +/****************************************************************************** + * $Id: ceosrecipe.c 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: ASI CEOS Translator + * Purpose: CEOS field layout recipes. + * Author: Paul Lahaie, pjlahaie@atlsci.com + * + ****************************************************************************** + * Copyright (c) 2000, Atlantis Scientific Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ceos.h" + +CPL_CVSID("$Id: ceosrecipe.c 27745 2014-09-27 16:38:57Z goatbar $"); + +/* Array of Datatypes and their names/values */ + +typedef struct { + char *String; + int Type; +} CeosStringType_t; + +typedef struct { + int (*function)(CeosSARVolume_t *volume, void *token); + void *token; + const char *name; +} RecipeFunctionData_t; + + +CeosStringType_t CeosDataType[] = { { "IU1", __CEOS_TYP_UCHAR }, + { "IU2", __CEOS_TYP_USHORT }, + { "UI1", __CEOS_TYP_UCHAR }, + { "UI2", __CEOS_TYP_USHORT }, + { "CI*2", __CEOS_TYP_COMPLEX_CHAR }, + { "CI*4", __CEOS_TYP_COMPLEX_SHORT }, + { "CIS4", __CEOS_TYP_COMPLEX_SHORT }, + { "CI*8", __CEOS_TYP_COMPLEX_LONG }, + { "C*8", __CEOS_TYP_COMPLEX_FLOAT }, + { "R*4", __CEOS_TYP_FLOAT }, + { NULL, 0 } }; + +CeosStringType_t CeosInterleaveType[] = { { "BSQ", __CEOS_IL_BAND }, + { " BSQ", __CEOS_IL_BAND }, + { "BIL", __CEOS_IL_LINE }, + { " BIL", __CEOS_IL_LINE }, + { NULL, 0 } }; + +#define IMAGE_OPT { 63, 192, 18, 18 } +#define IMAGE_JERS_OPT { 50, 192, 18, 18 } /* Some JERS data uses this instead of IMAGE_OPT */ +#define PROC_DATA_REC { 50, 11, 18, 20 } +#define PROC_DATA_REC_ALT { 50, 11, 31, 20 } +#define PROC_DATA_REC_ALT2 { 50, 11, 31, 50 } /* Some cases of ERS 1, 2 */ +#define DATA_SET_SUMMARY { 18, 10, 18, 20 } + +/* NOTE: This seems to be the generic recipe used for most things */ +CeosRecipeType_t RadarSatRecipe[] = +{ + { __CEOS_REC_NUMCHANS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 233, 4, __CEOS_REC_TYP_I }, /* Number of channels */ + { __CEOS_REC_INTERLEAVE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 269, 4, __CEOS_REC_TYP_A }, /* Interleaving type */ + { __CEOS_REC_DATATYPE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 429, 4, __CEOS_REC_TYP_A }, /* Data type */ + { __CEOS_REC_BPR, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 0, 0, __CEOS_REC_TYP_A }, /* For Defeault CEOS, this is done using other vals */ + { __CEOS_REC_LINES, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 237, 8, __CEOS_REC_TYP_I }, /* How many lines */ + { __CEOS_REC_TBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 261, 4, __CEOS_REC_TYP_I }, + { __CEOS_REC_BBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 265, 4, __CEOS_REC_TYP_I }, /* Bottom border pixels */ + { __CEOS_REC_PPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 249, 8, __CEOS_REC_TYP_I }, /* Pixels per line */ + { __CEOS_REC_LBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 245, 4, __CEOS_REC_TYP_I }, /* Left Border Pixels */ + { __CEOS_REC_RBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 257, 4, __CEOS_REC_TYP_I }, /* Isn't available for RadarSAT */ + { __CEOS_REC_BPP, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 225, 4, __CEOS_REC_TYP_I }, /* Bytes Per Pixel */ + { __CEOS_REC_RPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 273, 2, __CEOS_REC_TYP_I }, /* Records per line */ + { __CEOS_REC_PPR, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 0, 0, __CEOS_REC_TYP_I }, /* Pixels Per Record -- need to fill record type */ + { __CEOS_REC_PDBPR, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 281, 8, __CEOS_REC_TYP_I }, /* pixel data bytes per record */ + { __CEOS_REC_IDS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 277, 4, __CEOS_REC_TYP_I }, /* Prefix data per record */ + { __CEOS_REC_FDL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 9, 4, __CEOS_REC_TYP_B }, /* Length of Imagry Options Header */ + { __CEOS_REC_PIXORD, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 0, 0, __CEOS_REC_TYP_I }, /* Must be calculated */ + { __CEOS_REC_LINORD, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 0, 0, __CEOS_REC_TYP_I }, /* Must be calculated */ + { __CEOS_REC_PRODTYPE, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 0, 0, __CEOS_REC_TYP_I }, + + { __CEOS_REC_RECORDSIZE, 1, __CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC, + 9, 4, __CEOS_REC_TYP_B }, /* The processed image record size */ + + /* Some ERS-1 products use an alternate data record subtype2. */ + { __CEOS_REC_RECORDSIZE, 1, __CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC_ALT, + 9, 4, __CEOS_REC_TYP_B }, /* The processed image record size */ + + /* Yet another ERS-1 and ERS-2 alternate data record subtype2. */ + { __CEOS_REC_RECORDSIZE, 1, __CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC_ALT2, + 9, 4, __CEOS_REC_TYP_B }, /* The processed image record size */ + + { __CEOS_REC_SUFFIX_SIZE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 289, 4, __CEOS_REC_TYP_I }, /* Suffix data per record */ + { 0, 0, 0, { 0, 0, 0, 0 }, 0, 0, 0 } /* Last record is Zero */ +} ; + + +CeosRecipeType_t JersRecipe[] = +{ + { __CEOS_REC_NUMCHANS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 233, 4, __CEOS_REC_TYP_I }, /* Number of channels */ + { __CEOS_REC_INTERLEAVE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 269, 4, __CEOS_REC_TYP_A }, /* Interleaving type */ + { __CEOS_REC_DATATYPE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 429, 4, __CEOS_REC_TYP_A }, /* Data type */ + { __CEOS_REC_BPR, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 0, 0, __CEOS_REC_TYP_A }, /* For Defeault CEOS, this is done using other vals */ + { __CEOS_REC_LINES, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 237, 8, __CEOS_REC_TYP_I }, /* How many lines */ + { __CEOS_REC_TBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 261, 4, __CEOS_REC_TYP_I }, + { __CEOS_REC_BBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 265, 4, __CEOS_REC_TYP_I }, /* Bottom border pixels */ + { __CEOS_REC_PPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 249, 8, __CEOS_REC_TYP_I }, /* Pixels per line */ + { __CEOS_REC_LBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 245, 4, __CEOS_REC_TYP_I }, /* Left Border Pixels */ + { __CEOS_REC_RBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 257, 4, __CEOS_REC_TYP_I }, /* Isn't available for RadarSAT */ + { __CEOS_REC_BPP, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 225, 4, __CEOS_REC_TYP_I }, /* Bytes Per Pixel */ + { __CEOS_REC_RPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 273, 2, __CEOS_REC_TYP_I }, /* Records per line */ + { __CEOS_REC_PPR, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 0, 0, __CEOS_REC_TYP_I }, /* Pixels Per Record -- need to fill record type */ + { __CEOS_REC_PDBPR, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 281, 8, __CEOS_REC_TYP_I }, /* pixel data bytes per record */ + { __CEOS_REC_IDS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 277, 4, __CEOS_REC_TYP_I }, /* Prefix data per record */ + { __CEOS_REC_FDL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 9, 4, __CEOS_REC_TYP_B }, /* Length of Imagry Options Header */ + { __CEOS_REC_PIXORD, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 0, 0, __CEOS_REC_TYP_I }, /* Must be calculated */ + { __CEOS_REC_LINORD, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 0, 0, __CEOS_REC_TYP_I }, /* Must be calculated */ + { __CEOS_REC_PRODTYPE, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 0, 0, __CEOS_REC_TYP_I }, + + { __CEOS_REC_RECORDSIZE, 1, __CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC, + 9, 4, __CEOS_REC_TYP_B }, /* The processed image record size */ + + { __CEOS_REC_SUFFIX_SIZE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT, + 289, 4, __CEOS_REC_TYP_I }, /* Suffix data per record */ + { 0, 0, 0, { 0, 0, 0, 0 }, 0, 0, 0 } /* Last record is Zero */ +} ; + +CeosRecipeType_t ScanSARRecipe[] = +{ + { __CEOS_REC_NUMCHANS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 233, 4, __CEOS_REC_TYP_I }, /* Number of channels */ + { __CEOS_REC_INTERLEAVE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 269, 4, __CEOS_REC_TYP_A }, /* Interleaving type */ + { __CEOS_REC_DATATYPE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 429, 4, __CEOS_REC_TYP_A }, /* Data type */ + { __CEOS_REC_LINES, 1, __CEOS_ANY_FILE, DATA_SET_SUMMARY, + 325, 8, __CEOS_REC_TYP_I }, /* How many lines */ + { __CEOS_REC_PPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 249, 8, __CEOS_REC_TYP_I }, /* Pixels per line */ + { __CEOS_REC_BPP, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 225, 4, __CEOS_REC_TYP_I }, /* Bytes Per Pixel */ + { __CEOS_REC_RPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 273, 2, __CEOS_REC_TYP_I }, /* Records per line */ + { __CEOS_REC_IDS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 277, 4, __CEOS_REC_TYP_I }, /* Prefix data per record */ + { __CEOS_REC_FDL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 9, 4, __CEOS_REC_TYP_B }, /* Length of Imagry Options Header */ + { __CEOS_REC_RECORDSIZE, 1, __CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC, + 9, 4, __CEOS_REC_TYP_B }, /* The processed image record size */ + { __CEOS_REC_SUFFIX_SIZE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 289, 4, __CEOS_REC_TYP_I }, /* Suffix data per record */ + { 0, 0, 0, { 0, 0, 0, 0 }, 0, 0, 0 } /* Last record is Zero */ +} ; + +CeosRecipeType_t SIRCRecipe[] = +{ + { __CEOS_REC_NUMCHANS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 233, 4, __CEOS_REC_TYP_I }, /* Number of channels */ + { __CEOS_REC_INTERLEAVE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 269, 4, __CEOS_REC_TYP_A }, /* Interleaving type */ + { __CEOS_REC_DATATYPE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 429, 4, __CEOS_REC_TYP_A }, /* Data type */ + { __CEOS_REC_LINES, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 237, 8, __CEOS_REC_TYP_I }, /* How many lines */ + { __CEOS_REC_TBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 261, 4, __CEOS_REC_TYP_I }, + { __CEOS_REC_BBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 265, 4, __CEOS_REC_TYP_I }, /* Bottom border pixels */ + { __CEOS_REC_PPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 249, 8, __CEOS_REC_TYP_I }, /* Pixels per line */ + { __CEOS_REC_LBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 245, 4, __CEOS_REC_TYP_I }, /* Left Border Pixels */ + { __CEOS_REC_RBP, 0, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 257, 4, __CEOS_REC_TYP_I }, /* Right Border Pixels */ + { __CEOS_REC_BPP, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 225, 4, __CEOS_REC_TYP_I }, /* Bytes Per Pixel */ + { __CEOS_REC_RPL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 273, 2, __CEOS_REC_TYP_I }, /* Records per line */ + { __CEOS_REC_IDS, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 277, 4, __CEOS_REC_TYP_I }, /* Prefix data per record */ + { __CEOS_REC_FDL, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 9, 4, __CEOS_REC_TYP_B }, /* Length of Imagry Options Header */ + { __CEOS_REC_RECORDSIZE, 1, __CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC, + 9, 4, __CEOS_REC_TYP_B }, /* The processed image record size */ + { __CEOS_REC_SUFFIX_SIZE, 1, __CEOS_IMAGRY_OPT_FILE, IMAGE_OPT, + 289, 4, __CEOS_REC_TYP_I }, /* Suffix data per record */ + + { 0, 0, 0, { 0, 0, 0, 0 }, 0, 0, 0 } /* Last record is Zero */ +} ; + +#undef PROC_DATA_REC + +static void ExtractInt( CeosRecord_t *record, int type, unsigned int offset, unsigned int length, int *value ); + +static char *ExtractString( CeosRecord_t *record, unsigned int offset, unsigned int length, char *string ); + +static int GetCeosStringType(const CeosStringType_t *CeosType, const char *string); + +static int SIRCRecipeFCN( CeosSARVolume_t *volume, void *token ); +static int PALSARRecipeFCN( CeosSARVolume_t *volume, void *token ); + +Link_t *RecipeFunctions = NULL; + +void RegisterRecipes( void ) +{ + + AddRecipe( SIRCRecipeFCN, SIRCRecipe, "SIR-C" ); + AddRecipe( ScanSARRecipeFCN, ScanSARRecipe, "ScanSAR" ); + AddRecipe( CeosDefaultRecipe, RadarSatRecipe, "RadarSat" ); + AddRecipe( CeosDefaultRecipe, JersRecipe, "Jers" ); + AddRecipe( PALSARRecipeFCN, RadarSatRecipe, "PALSAR-ALOS" ); + /* AddRecipe( CeosDefaultRecipe, AtlantisRecipe ); */ +} + +void FreeRecipes( void ) + +{ + Link_t *link; + + for( link = RecipeFunctions; link != NULL; link = link->next ) + HFree( link->object ); + + DestroyList( RecipeFunctions ); + RecipeFunctions = NULL; +} + +void AddRecipe( int (*function)(CeosSARVolume_t *volume, + void *token), + void *token, + const char *name ) +{ + + RecipeFunctionData_t *TempData; + + Link_t *Link; + + TempData = HMalloc( sizeof( RecipeFunctionData_t ) ); + + TempData->function = function; + TempData->token = token; + TempData->name = name; + + Link = ceos2CreateLink( TempData ); + + if( RecipeFunctions == NULL) + { + RecipeFunctions = Link; + } else { + RecipeFunctions = InsertLink( RecipeFunctions, Link ); + } +} + +int CeosDefaultRecipe( CeosSARVolume_t *volume, void *token ) +{ + CeosRecipeType_t *recipe; + CeosRecord_t *record; + CeosTypeCode_t TypeCode; + struct CeosSARImageDesc *ImageDesc = &(volume->ImageDesc); + char temp_str[1024]; + int i /*, temp_int */; + +#define DoExtractInt(a) ExtractInt( record, recipe[i].Type, recipe[i].Offset, recipe[i].Length, &a) + + if(token == NULL) + { + return 0; + } + + memset(ImageDesc, 0, sizeof( struct CeosSARImageDesc ) ); + +/* temp_imagerecipe = (CeosSARImageDescRecipe_t *) token; + recipe = temp_imagerecipe->Recipe; */ + + recipe = token; + + for(i = 0; recipe[i].ImageDescValue != 0; i++ ) + { + if(recipe[i].Override) + { + TypeCode.UCharCode.Subtype1 = recipe[i].TypeCode.Subtype1; + TypeCode.UCharCode.Type = recipe[i].TypeCode.Type; + TypeCode.UCharCode.Subtype2 = recipe[i].TypeCode.Subtype2; + TypeCode.UCharCode.Subtype3 = recipe[i].TypeCode.Subtype3; + + record = FindCeosRecord( volume->RecordList, TypeCode, recipe[i].FileId, -1, -1 ); + + if(record == NULL) + { + /* temp_int = 0; */ + } else { + + switch( recipe[i].ImageDescValue ) + { + case __CEOS_REC_NUMCHANS: + DoExtractInt( ImageDesc->NumChannels ); + break; + case __CEOS_REC_LINES: + DoExtractInt( ImageDesc->Lines ); + break; + case __CEOS_REC_BPP: + DoExtractInt( ImageDesc->BytesPerPixel ); + break; + case __CEOS_REC_RPL: + DoExtractInt( ImageDesc->RecordsPerLine ); + break; + case __CEOS_REC_PDBPR: + DoExtractInt( ImageDesc->PixelDataBytesPerRecord ); + break; + case __CEOS_REC_FDL: + DoExtractInt( ImageDesc->FileDescriptorLength ); + break; + case __CEOS_REC_IDS: + DoExtractInt( ImageDesc->ImageDataStart ); + /* + ** This is really reading the quantity of prefix data + ** per data record. We want the offset from the very + ** beginning of the record to the data, so we add another + ** 12 to that. I think some products incorrectly indicate + ** 192 (prefix+12) instead of 180 so if we see 192 assume + ** the 12 bytes of record start data has already been + ** added. Frank Warmerdam. + */ + if( ImageDesc->ImageDataStart != 192 ) + ImageDesc->ImageDataStart += 12; + break; + case __CEOS_REC_SUFFIX_SIZE: + DoExtractInt( ImageDesc->ImageSuffixData ); + break; + case __CEOS_REC_RECORDSIZE: + DoExtractInt( ImageDesc->BytesPerRecord ); + break; + case __CEOS_REC_PPL: + DoExtractInt( ImageDesc->PixelsPerLine ); + break; + case __CEOS_REC_TBP: + DoExtractInt( ImageDesc->TopBorderPixels ); + break; + case __CEOS_REC_BBP: + DoExtractInt( ImageDesc->BottomBorderPixels ); + break; + case __CEOS_REC_LBP: + DoExtractInt( ImageDesc->LeftBorderPixels ); + break; + case __CEOS_REC_RBP: + DoExtractInt( ImageDesc->RightBorderPixels ); + break; + case __CEOS_REC_INTERLEAVE: + ExtractString( record, recipe[i].Offset, recipe[i].Length, temp_str ); + + ImageDesc->ChannelInterleaving = GetCeosStringType( CeosInterleaveType, temp_str ); + break; + case __CEOS_REC_DATATYPE: + ExtractString( record, recipe[i].Offset, recipe[i].Length, temp_str ); + + ImageDesc->DataType = GetCeosStringType( CeosDataType, temp_str ); + break; + } + + } + } + } + + /* Some files (Telaviv) don't record the number of pixel groups per line. + * Try to derive it from the size of a data group, and the number of + * bytes of pixel data if necessary. + */ + + if( ImageDesc->PixelsPerLine == 0 + && ImageDesc->PixelDataBytesPerRecord != 0 + && ImageDesc->BytesPerPixel != 0 ) + { + ImageDesc->PixelsPerLine = + ImageDesc->PixelDataBytesPerRecord / ImageDesc->BytesPerPixel; + CPLDebug( "SAR_CEOS", "Guessing PixelPerLine to be %d\n", + ImageDesc->PixelsPerLine ); + } + + /* Some files don't have the BytesPerRecord stuff, so we calculate it if possible */ + + if( ImageDesc->BytesPerRecord == 0 && ImageDesc->RecordsPerLine == 1 && + ImageDesc->PixelsPerLine > 0 && ImageDesc->BytesPerPixel > 0 ) + { + CeosRecord_t *img_rec; + + ImageDesc->BytesPerRecord = ImageDesc->PixelsPerLine * + ImageDesc->BytesPerPixel + ImageDesc->ImageDataStart + + ImageDesc->ImageSuffixData ; + + TypeCode.UCharCode.Subtype1 = 0xed; + TypeCode.UCharCode.Type = 0xed; + TypeCode.UCharCode.Subtype2 = 0x12; + TypeCode.UCharCode.Subtype3 = 0x12; + + img_rec = FindCeosRecord( volume->RecordList, TypeCode, + __CEOS_IMAGRY_OPT_FILE, -1, -1 ); + if( img_rec == NULL ) + { + CPLDebug( "SAR_CEOS", + "Unable to find imagery rec to check record length." ); + return 0; + } + + if( img_rec->Length != ImageDesc->BytesPerRecord ) + { + CPLDebug( "SAR_CEOS", + "Guessed record length (%d) did not match\n" + "actual imagery record length (%d), recipe fails.", + ImageDesc->BytesPerRecord, img_rec->Length ); + return 0; + } + } + + if( ImageDesc->PixelsPerRecord == 0 && + ImageDesc->BytesPerRecord != 0 && ImageDesc->BytesPerPixel != 0 ) + { + ImageDesc->PixelsPerRecord = ( ( ImageDesc->BytesPerRecord - + (ImageDesc->ImageSuffixData + + ImageDesc->ImageDataStart )) / + ImageDesc->BytesPerPixel ); + + if(ImageDesc->PixelsPerRecord > ImageDesc->PixelsPerLine) + ImageDesc->PixelsPerRecord = ImageDesc->PixelsPerLine; + } + + /* If we didn't get a data type, try guessing. */ + if( ImageDesc->DataType == 0 + && ImageDesc->BytesPerPixel != 0 + && ImageDesc->NumChannels != 0 ) + { + int nDataTypeSize = ImageDesc->BytesPerPixel / ImageDesc->NumChannels; + + if( nDataTypeSize == 1 ) + ImageDesc->DataType = __CEOS_TYP_UCHAR; + else if( nDataTypeSize == 2 ) + ImageDesc->DataType = __CEOS_TYP_USHORT; + } + + /* Sanity checking */ + + if( ImageDesc->PixelsPerLine == 0 || ImageDesc->Lines == 0 || + ImageDesc->RecordsPerLine == 0 || ImageDesc->ImageDataStart == 0 || + ImageDesc->FileDescriptorLength == 0 || ImageDesc->DataType == 0 || + ImageDesc->NumChannels == 0 || ImageDesc->BytesPerPixel == 0 || + ImageDesc->ChannelInterleaving == 0 || ImageDesc->BytesPerRecord == 0) + { + return 0; + } else { + + ImageDesc->ImageDescValid = TRUE; + return 1; + } +} + +int ScanSARRecipeFCN( CeosSARVolume_t *volume, void *token ) +{ + struct CeosSARImageDesc *ImageDesc = &(volume->ImageDesc); + + memset( ImageDesc, 0, sizeof( struct CeosSARImageDesc ) ); + + if( CeosDefaultRecipe( volume, token ) ) + { + ImageDesc->Lines *= 2; + return 1; + } + + return 0; +} + +static int SIRCRecipeFCN( CeosSARVolume_t *volume, void *token ) +{ + struct CeosSARImageDesc *ImageDesc = &(volume->ImageDesc); + CeosTypeCode_t TypeCode; + CeosRecord_t *record; + char szSARDataFormat[29]; + + memset( ImageDesc, 0, sizeof( struct CeosSARImageDesc ) ); + +/* -------------------------------------------------------------------- */ +/* First, we need to check if the "SAR Data Format Type */ +/* identifier" is set to "COMPRESSED CROSS-PRODUCTS" which is */ +/* pretty idiosyncratic to SIRC products. It might also appear */ +/* for some other similarly encoded Polarimetric data I suppose. */ +/* -------------------------------------------------------------------- */ + /* IMAGE_OPT */ + TypeCode.UCharCode.Subtype1 = 63; + TypeCode.UCharCode.Type = 192; + TypeCode.UCharCode.Subtype2 = 18; + TypeCode.UCharCode.Subtype3 = 18; + + record = FindCeosRecord( volume->RecordList, TypeCode, + __CEOS_IMAGRY_OPT_FILE, -1, -1 ); + if( record == NULL ) + return 0; + + ExtractString( record, 401, 28, szSARDataFormat ); + if( !EQUALN( szSARDataFormat, "COMPRESSED CROSS-PRODUCTS", 25) ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Apply normal handling... */ +/* -------------------------------------------------------------------- */ + CeosDefaultRecipe( volume, token ); + +/* -------------------------------------------------------------------- */ +/* Make sure this looks like the SIRC product we are expecting. */ +/* -------------------------------------------------------------------- */ + if( ImageDesc->BytesPerPixel != 10 ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Then fix up a few values. */ +/* -------------------------------------------------------------------- */ + /* It seems the bytes of pixel data per record is just wrong. Fix. */ + ImageDesc->PixelDataBytesPerRecord = + ImageDesc->BytesPerPixel * ImageDesc->PixelsPerLine; + + ImageDesc->DataType = __CEOS_TYP_CCP_COMPLEX_FLOAT; + +/* -------------------------------------------------------------------- */ +/* Sanity checking */ +/* -------------------------------------------------------------------- */ + if( ImageDesc->PixelsPerLine == 0 || ImageDesc->Lines == 0 || + ImageDesc->RecordsPerLine == 0 || ImageDesc->ImageDataStart == 0 || + ImageDesc->FileDescriptorLength == 0 || ImageDesc->DataType == 0 || + ImageDesc->NumChannels == 0 || ImageDesc->BytesPerPixel == 0 || + ImageDesc->ChannelInterleaving == 0 || ImageDesc->BytesPerRecord == 0) + { + return 0; + } else { + + ImageDesc->ImageDescValid = TRUE; + return 1; + } +} + +static int PALSARRecipeFCN( CeosSARVolume_t *volume, void *token ) +{ + struct CeosSARImageDesc *ImageDesc = &(volume->ImageDesc); + CeosTypeCode_t TypeCode; + CeosRecord_t *record; + char szSARDataFormat[29], szProduct[32]; + + memset( ImageDesc, 0, sizeof( struct CeosSARImageDesc ) ); + +/* -------------------------------------------------------------------- */ +/* First, we need to check if the "SAR Data Format Type */ +/* identifier" is set to "COMPRESSED CROSS-PRODUCTS" which is */ +/* pretty idiosyncratic to SIRC products. It might also appear */ +/* for some other similarly encoded Polarimetric data I suppose. */ +/* -------------------------------------------------------------------- */ + /* IMAGE_OPT */ + TypeCode.UCharCode.Subtype1 = 63; + TypeCode.UCharCode.Type = 192; + TypeCode.UCharCode.Subtype2 = 18; + TypeCode.UCharCode.Subtype3 = 18; + + record = FindCeosRecord( volume->RecordList, TypeCode, + __CEOS_IMAGRY_OPT_FILE, -1, -1 ); + if( record == NULL ) + return 0; + + ExtractString( record, 401, 28, szSARDataFormat ); + if( !EQUALN( szSARDataFormat, "INTEGER*18 ", 25) ) + return 0; + + ExtractString( record, 49, 16, szProduct ); + if( !EQUALN( szProduct, "ALOS-", 5 ) ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Apply normal handling... */ +/* -------------------------------------------------------------------- */ + CeosDefaultRecipe( volume, token ); + +/* -------------------------------------------------------------------- */ +/* Make sure this looks like the SIRC product we are expecting. */ +/* -------------------------------------------------------------------- */ + if( ImageDesc->BytesPerPixel != 18 ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Then fix up a few values. */ +/* -------------------------------------------------------------------- */ + ImageDesc->DataType = __CEOS_TYP_PALSAR_COMPLEX_SHORT; + ImageDesc->NumChannels = 6; + +/* -------------------------------------------------------------------- */ +/* Sanity checking */ +/* -------------------------------------------------------------------- */ + if( ImageDesc->PixelsPerLine == 0 || ImageDesc->Lines == 0 || + ImageDesc->RecordsPerLine == 0 || ImageDesc->ImageDataStart == 0 || + ImageDesc->FileDescriptorLength == 0 || ImageDesc->DataType == 0 || + ImageDesc->NumChannels == 0 || ImageDesc->BytesPerPixel == 0 || + ImageDesc->ChannelInterleaving == 0 || ImageDesc->BytesPerRecord == 0) + { + return 0; + } else { + + ImageDesc->ImageDescValid = TRUE; + return 1; + } +} + +void GetCeosSARImageDesc( CeosSARVolume_t *volume ) +{ + Link_t *link; + RecipeFunctionData_t *rec_data; + int (*function)(CeosSARVolume_t *volume, void *token); + + if( RecipeFunctions == NULL ) + { + RegisterRecipes(); + } + + if(RecipeFunctions == NULL ) + { + return ; + } + + for(link = RecipeFunctions; link != NULL; link = link->next) + { + if(link->object) + { + rec_data = link->object; + function = rec_data->function; + if(( *function )( volume, rec_data->token ) ) + { + CPLDebug( "CEOS", "Using recipe '%s'.", + rec_data->name ); + return; + } + } + } + + return ; + +} + + +static void ExtractInt(CeosRecord_t *record, int type, unsigned int offset, unsigned int length, int *value) +{ + void *buffer; + char format[32]; + + buffer = HMalloc( length + 1 ); + + switch(type) + { + case __CEOS_REC_TYP_A: + sprintf( format, "A%u", length ); + GetCeosField( record, offset, format, buffer ); + *value = atoi( buffer ); + break; + case __CEOS_REC_TYP_B: + sprintf( format, "B%u", length ); +#ifdef notdef + GetCeosField( record, offset, format, buffer ); + if( length <= 4 ) + CeosToNative( value, buffer, length, length ); + else + *value = 0; +#else + GetCeosField( record, offset, format, value ); +#endif + break; + case __CEOS_REC_TYP_I: + sprintf( format, "I%u", length ); + GetCeosField( record, offset, format, value ); + break; + } + + HFree( buffer ); + +} + +static char *ExtractString( CeosRecord_t *record, unsigned int offset, unsigned int length, char *string ) +{ + char format[12]; + + if(string == NULL) + { + string = HMalloc( length + 1 ); + } + + sprintf( format, "A%u", length ); + + GetCeosField( record, offset, format, string ); + + return string; +} + +static int GetCeosStringType(const CeosStringType_t *CeosStringType, const char *string) +{ + int i; + + for(i = 0;CeosStringType[i].String != NULL;i++) + { + if(strncmp(CeosStringType[i].String ,string, strlen( CeosStringType[i].String ) ) == 0 ) + { + return CeosStringType[i].Type; + } + } + + return 0; +} diff --git a/bazaar/plugin/gdal/frmts/ceos2/ceossar.c b/bazaar/plugin/gdal/frmts/ceos2/ceossar.c new file mode 100644 index 000000000..a11a39a8a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos2/ceossar.c @@ -0,0 +1,139 @@ +/****************************************************************************** + * $Id: ceossar.c 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: ASI CEOS Translator + * Purpose: Functions related to CeosSARVolume_t. + * Author: Paul Lahaie, pjlahaie@atlsci.com + * + ****************************************************************************** + * Copyright (c) 2000, Atlantis Scientific Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ceos.h" + +CPL_CVSID("$Id: ceossar.c 27745 2014-09-27 16:38:57Z goatbar $"); + +extern Link_t *RecipeFunctions; + +void InitCeosSARVolume(CeosSARVolume_t *volume, int32 file_name_convention) +{ + volume->Flavour = \ + volume->Sensor = \ + volume->ProductType = 0; + + volume->FileNamingConvention = file_name_convention ; + + volume->VolumeDirectoryFile = + volume->SARLeaderFile = + volume->SARTrailerFile = + volume->NullVolumeDirectoryFile = + volume->ImageDesc.ImageDescValid = FALSE; + + volume->RecordList = NULL; +} + + +void CalcCeosSARImageFilePosition(CeosSARVolume_t *volume, int channel, int line, int *record, int *file_offset) +{ + struct CeosSARImageDesc *ImageDesc; + int TotalRecords=0, TotalBytes=0; + + if(record) + *record = 0; + if(file_offset) + *file_offset = 0; + + if( volume ) + { + if( volume->ImageDesc.ImageDescValid ) + { + ImageDesc = &( volume->ImageDesc ); + + switch( ImageDesc->ChannelInterleaving ) + { + case __CEOS_IL_PIXEL: + TotalRecords = (line - 1) * ImageDesc->RecordsPerLine; + TotalBytes = (TotalRecords) * ( ImageDesc->BytesPerRecord ); + break; + case __CEOS_IL_LINE: + TotalRecords = (ImageDesc->NumChannels * (line - 1) + + (channel - 1)) * ImageDesc->RecordsPerLine; + TotalBytes = (TotalRecords) * ( ImageDesc->BytesPerRecord ) ; + break; + case __CEOS_IL_BAND: + TotalRecords = (((channel - 1) * ImageDesc->Lines) * + ImageDesc->RecordsPerLine) + + (line - 1) * ImageDesc->RecordsPerLine; + + TotalBytes = (TotalRecords) * ( ImageDesc->BytesPerRecord ); + break; + } + if(file_offset) + *file_offset = ImageDesc->FileDescriptorLength + TotalBytes; + if(record) + *record = TotalRecords + 1; + } + } +} + +int32 GetCeosSARImageData(CPL_UNUSED CeosSARVolume_t *volume, + CPL_UNUSED CeosRecord_t *processed_data_record, + CPL_UNUSED int channel, + CPL_UNUSED int xoff, + CPL_UNUSED int xsize, + CPL_UNUSED int bufsize, + CPL_UNUSED uchar *buffer) +{ + return 0; +} + +void DetermineCeosSARPixelOrder( CPL_UNUSED CeosSARVolume_t *volume, + CPL_UNUSED CeosRecord_t *record ) +{ +} + +void GetCeosSAREmbeddedInfo(CPL_UNUSED CeosSARVolume_t *volume, + CPL_UNUSED CeosRecord_t *processed_data_record, + CPL_UNUSED CeosSAREmbeddedInfo_t *info) +{ +} + +void DeleteCeosSARVolume(CeosSARVolume_t *volume) +{ + Link_t *Links; + + if( volume ) + { + if( volume->RecordList ) + { + for(Links = volume->RecordList; Links != NULL; Links = Links->next) + { + if(Links->object) + { + DeleteCeosRecord( Links->object ); + Links->object = NULL; + } + } + DestroyList( volume->RecordList ); + } + HFree( volume ); + } +} diff --git a/bazaar/plugin/gdal/frmts/ceos2/link.c b/bazaar/plugin/gdal/frmts/ceos2/link.c new file mode 100644 index 000000000..001356655 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos2/link.c @@ -0,0 +1,94 @@ +/****************************************************************************** + * $Id: link.c 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: ASI CEOS Translator + * Purpose: Link list function replacements. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 2000, Atlantis Scientific Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ceos.h" + +CPL_CVSID("$Id: link.c 10645 2007-01-18 02:22:39Z warmerdam $"); + + +/************************************************************************/ +/* ceos2CreateLink() */ +/************************************************************************/ + +Link_t *ceos2CreateLink( void *pObject ) + +{ + Link_t *psLink = (Link_t *) CPLCalloc(sizeof(Link_t),1); + + psLink->object = pObject; + + return psLink; +} + +/************************************************************************/ +/* DestroyList() */ +/************************************************************************/ + +void DestroyList( Link_t * psList ) + +{ + while( psList != NULL ) + { + Link_t *psNext = psList->next; + + CPLFree( psList ); + psList = psNext; + } +} + +/************************************************************************/ +/* InsertLink() */ +/************************************************************************/ + +Link_t *InsertLink( Link_t *psList, Link_t *psLink ) + +{ + psLink->next = psList; + + return psLink; +} + +/************************************************************************/ +/* AddLink() */ +/************************************************************************/ + +Link_t *AddLink( Link_t *psList, Link_t *psLink ) + +{ + Link_t *psNode; + + if( psList == NULL ) + return psLink; + + for( psNode = psList; psNode->next != NULL; psNode = psNode->next ) {} + + psNode->next = psLink; + + return psList; +} diff --git a/bazaar/plugin/gdal/frmts/ceos2/makefile.vc b/bazaar/plugin/gdal/frmts/ceos2/makefile.vc new file mode 100644 index 000000000..2b884cff8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos2/makefile.vc @@ -0,0 +1,16 @@ + +OBJ = sar_ceosdataset.obj \ + ceosrecipe.obj ceossar.obj ceos.obj link.obj + +GDAL_ROOT = ..\.. + +EXTRAFLAGS = -I..\raw + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/ceos2/sar_ceosdataset.cpp b/bazaar/plugin/gdal/frmts/ceos2/sar_ceosdataset.cpp new file mode 100644 index 000000000..24c76bdad --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ceos2/sar_ceosdataset.cpp @@ -0,0 +1,2204 @@ +/****************************************************************************** + * $Id: sar_ceosdataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: ASI CEOS Translator + * Purpose: GDALDataset driver for CEOS translator. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Atlantis Scientific Inc. + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ceos.h" +#include "gdal_priv.h" +#include "rawdataset.h" +#include "cpl_string.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: sar_ceosdataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_SAR_CEOS(void); +CPL_C_END + +static GInt16 CastToGInt16(float val); + +static GInt16 CastToGInt16(float val) +{ + float temp; + + temp = val; + + if ( temp < -32768.0 ) + temp = -32768.0; + + if ( temp > 32767 ) + temp = 32767.0; + + return (GInt16) temp; +} + +static const char *CeosExtension[][6] = { +{ "vol", "led", "img", "trl", "nul", "ext" }, +{ "vol", "lea", "img", "trl", "nul", "ext" }, +{ "vol", "led", "img", "tra", "nul", "ext" }, +{ "vol", "lea", "img", "tra", "nul", "ext" }, +{ "vdf", "slf", "sdf", "stf", "nvd", "ext" }, + +{ "vdf", "ldr", "img", "tra", "nul", "ext2" }, + +/* Jers from Japan- not sure if this is generalized as much as it could be */ +{ "VOLD", "Sarl_01", "Imop_%02d", "Sart_01", "NULL", "base" }, + + +/* Radarsat: basename, not extension */ +{ "vdf_dat", "lea_%02d", "dat_%02d", "tra_%02d", "nul_vdf", "base" }, + +/* Ers-1: basename, not extension */ +{ "vdf_dat", "lea_%02d", "dat_%02d", "tra_%02d", "nul_dat", "base" }, + +/* Ers-2 from Telaviv */ +{ "volume", "leader", "image", "trailer", "nul_dat", "whole" }, + +/* Ers-1 from D-PAF */ +{ "VDF", "LF", "SLC", "", "", "ext" }, + +/* Radarsat-1 per #2051 */ +{ "vol", "sarl", "sard", "sart", "nvol", "ext" }, + +/* end marker */ +{ NULL, NULL, NULL, NULL, NULL, NULL } +}; + +static int +ProcessData( VSILFILE *fp, int fileid, CeosSARVolume_t *sar, int max_records, + vsi_l_offset max_bytes ); + + +static CeosTypeCode_t QuadToTC( int a, int b, int c, int d ) +{ + CeosTypeCode_t abcd; + + abcd.UCharCode.Subtype1 = (unsigned char) a; + abcd.UCharCode.Type = (unsigned char) b; + abcd.UCharCode.Subtype2 = (unsigned char) c; + abcd.UCharCode.Subtype3 = (unsigned char) d; + + return abcd; +} + +#define LEADER_DATASET_SUMMARY_TC QuadToTC( 18, 10, 18, 20 ) +#define LEADER_DATASET_SUMMARY_ERS2_TC QuadToTC( 10, 10, 31, 20 ) +#define LEADER_RADIOMETRIC_COMPENSATION_TC QuadToTC( 18, 51, 18, 20 ) +#define VOLUME_DESCRIPTOR_RECORD_TC QuadToTC( 192, 192, 18, 18 ) +#define IMAGE_HEADER_RECORD_TC QuadToTC( 63, 192, 18, 18 ) +#define LEADER_RADIOMETRIC_DATA_RECORD_TC QuadToTC( 18, 50, 18, 20 ) +#define LEADER_MAP_PROJ_RECORD_TC QuadToTC( 10, 20, 31, 20 ) + +/* JERS from Japan has MAP_PROJ recond with different identifiers */ +/* see CEOS-SAR-CCT Iss/Rev: 2/0 February 10, 1989 */ +#define LEADER_MAP_PROJ_RECORD_JERS_TC QuadToTC( 18, 20, 18, 20 ) + +/* For ERS calibration and incidence angle information */ +#define ERS_GENERAL_FACILITY_DATA_TC QuadToTC( 10, 200, 31, 50 ) +#define ERS_GENERAL_FACILITY_DATA_ALT_TC QuadToTC( 10, 216, 31, 50 ) + + +#define RSAT_PROC_PARAM_TC QuadToTC( 18, 120, 18, 20 ) + +/************************************************************************/ +/* ==================================================================== */ +/* SAR_CEOSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class SAR_CEOSRasterBand; +class CCPRasterBand; +class PALSARRasterBand; + +class SAR_CEOSDataset : public GDALPamDataset +{ + friend class SAR_CEOSRasterBand; + friend class CCPRasterBand; + friend class PALSARRasterBand; + + CeosSARVolume_t sVolume; + + VSILFILE *fpImage; + + char **papszTempMD; + + int nGCPCount; + GDAL_GCP *pasGCPList; + + void ScanForGCPs(); + void ScanForMetadata(); + int ScanForMapProjection(); + + public: + SAR_CEOSDataset(); + ~SAR_CEOSDataset(); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain ); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* CCPRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class CCPRasterBand : public GDALPamRasterBand +{ + friend class SAR_CEOSDataset; + + public: + CCPRasterBand( SAR_CEOSDataset *, int, GDALDataType ); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* PALSARRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class PALSARRasterBand : public GDALPamRasterBand +{ + friend class SAR_CEOSDataset; + + public: + PALSARRasterBand( SAR_CEOSDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* SAR_CEOSRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class SAR_CEOSRasterBand : public GDALPamRasterBand +{ + friend class SAR_CEOSDataset; + + public: + SAR_CEOSRasterBand( SAR_CEOSDataset *, int, GDALDataType ); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* SAR_CEOSRasterBand() */ +/************************************************************************/ + +SAR_CEOSRasterBand::SAR_CEOSRasterBand( SAR_CEOSDataset *poGDS, int nBand, + GDALDataType eType ) + +{ + this->poDS = poGDS; + this->nBand = nBand; + + eDataType = eType; + + nBlockXSize = poGDS->nRasterXSize; + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr SAR_CEOSRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + struct CeosSARImageDesc *ImageDesc; + int offset; + GByte *pabyRecord; + SAR_CEOSDataset *poGDS = (SAR_CEOSDataset *) poDS; + + ImageDesc = &(poGDS->sVolume.ImageDesc); + + CalcCeosSARImageFilePosition( &(poGDS->sVolume), nBand, + nBlockYOff + 1, NULL, &offset ); + + offset += ImageDesc->ImageDataStart; + +/* -------------------------------------------------------------------- */ +/* Load all the pixel data associated with this scanline. */ +/* Ensure we handle multiple record scanlines properly. */ +/* -------------------------------------------------------------------- */ + int iRecord, nPixelsRead = 0; + + pabyRecord = (GByte *) CPLMalloc( ImageDesc->BytesPerPixel * nBlockXSize ); + + for( iRecord = 0; iRecord < ImageDesc->RecordsPerLine; iRecord++ ) + { + int nPixelsToRead; + + if( nPixelsRead + ImageDesc->PixelsPerRecord > nBlockXSize ) + nPixelsToRead = nBlockXSize - nPixelsRead; + else + nPixelsToRead = ImageDesc->PixelsPerRecord; + + VSIFSeekL( poGDS->fpImage, offset, SEEK_SET ); + VSIFReadL( pabyRecord + nPixelsRead * ImageDesc->BytesPerPixel, + 1, nPixelsToRead * ImageDesc->BytesPerPixel, + poGDS->fpImage ); + + nPixelsRead += nPixelsToRead; + offset += ImageDesc->BytesPerRecord; + } + +/* -------------------------------------------------------------------- */ +/* Copy the desired band out based on the size of the type, and */ +/* the interleaving mode. */ +/* -------------------------------------------------------------------- */ + int nBytesPerSample = GDALGetDataTypeSize( eDataType ) / 8; + + if( ImageDesc->ChannelInterleaving == __CEOS_IL_PIXEL ) + { + GDALCopyWords( pabyRecord + (nBand-1) * nBytesPerSample, + eDataType, ImageDesc->BytesPerPixel, + pImage, eDataType, nBytesPerSample, + nBlockXSize ); + } + else if( ImageDesc->ChannelInterleaving == __CEOS_IL_LINE ) + { + GDALCopyWords( pabyRecord + (nBand-1) * nBytesPerSample * nBlockXSize, + eDataType, nBytesPerSample, + pImage, eDataType, nBytesPerSample, + nBlockXSize ); + } + else if( ImageDesc->ChannelInterleaving == __CEOS_IL_BAND ) + { + memcpy( pImage, pabyRecord, nBytesPerSample * nBlockXSize ); + } + +#ifdef CPL_LSB + GDALSwapWords( pImage, nBytesPerSample, nBlockXSize, nBytesPerSample ); +#endif + + CPLFree( pabyRecord ); + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* CCPRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* CCPRasterBand() */ +/************************************************************************/ + +CCPRasterBand::CCPRasterBand( SAR_CEOSDataset *poGDS, int nBand, + GDALDataType eType ) + +{ + this->poDS = poGDS; + this->nBand = nBand; + + eDataType = eType; + + nBlockXSize = poGDS->nRasterXSize; + nBlockYSize = 1; + + if( nBand == 1 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "HH" ); + else if( nBand == 2 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "HV" ); + else if( nBand == 3 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "VH" ); + else if( nBand == 4 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "VV" ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +/* From: http://southport.jpl.nasa.gov/software/dcomp/dcomp.html + +ysca = sqrt{ [ (Byte(2) / 254 ) + 1.5] 2Byte(1) } + +Re(SHH) = byte(3) ysca/127 + +Im(SHH) = byte(4) ysca/127 + +Re(SHV) = byte(5) ysca/127 + +Im(SHV) = byte(6) ysca/127 + +Re(SVH) = byte(7) ysca/127 + +Im(SVH) = byte(8) ysca/127 + +Re(SVV) = byte(9) ysca/127 + +Im(SVV) = byte(10) ysca/127 + +*/ + +CPLErr CCPRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + struct CeosSARImageDesc *ImageDesc; + int offset; + GByte *pabyRecord; + SAR_CEOSDataset *poGDS = (SAR_CEOSDataset *) poDS; + static float afPowTable[256]; + static int bPowTableInitialized = FALSE; + + ImageDesc = &(poGDS->sVolume.ImageDesc); + + offset = ImageDesc->FileDescriptorLength + + ImageDesc->BytesPerRecord * nBlockYOff + + ImageDesc->ImageDataStart; + +/* -------------------------------------------------------------------- */ +/* Load all the pixel data associated with this scanline. */ +/* -------------------------------------------------------------------- */ + int nBytesToRead = ImageDesc->BytesPerPixel * nBlockXSize; + + pabyRecord = (GByte *) CPLMalloc( nBytesToRead ); + + if( VSIFSeekL( poGDS->fpImage, offset, SEEK_SET ) != 0 + || (int) VSIFReadL( pabyRecord, 1, nBytesToRead, + poGDS->fpImage ) != nBytesToRead ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Error reading %d bytes of CEOS record data at offset %d.\n" + "Reading file %s failed.", + nBytesToRead, offset, poGDS->GetDescription() ); + CPLFree( pabyRecord ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Initialize our power table if this is our first time through. */ +/* -------------------------------------------------------------------- */ + if( !bPowTableInitialized ) + { + int i; + + bPowTableInitialized = TRUE; + + for( i = 0; i < 256; i++ ) + { + afPowTable[i] = (float)pow( 2.0, i-128 ); + } + } + +/* -------------------------------------------------------------------- */ +/* Copy the desired band out based on the size of the type, and */ +/* the interleaving mode. */ +/* -------------------------------------------------------------------- */ + int iX; + + for( iX = 0; iX < nBlockXSize; iX++ ) + { + unsigned char *pabyGroup = pabyRecord + iX * ImageDesc->BytesPerPixel; + signed char *Byte = (signed char*)pabyGroup-1; /* A ones based alias */ + double dfReSHH, dfImSHH, dfReSHV, dfImSHV, + dfReSVH, dfImSVH, dfReSVV, dfImSVV, dfScale; + + dfScale = sqrt( (Byte[2] / 254 + 1.5) * afPowTable[Byte[1] + 128] ); + + if( nBand == 1 ) + { + dfReSHH = Byte[3] * dfScale / 127.0; + dfImSHH = Byte[4] * dfScale / 127.0; + + ((float *) pImage)[iX*2 ] = (float)dfReSHH; + ((float *) pImage)[iX*2+1] = (float)dfImSHH; + } + else if( nBand == 2 ) + { + dfReSHV = Byte[5] * dfScale / 127.0; + dfImSHV = Byte[6] * dfScale / 127.0; + + ((float *) pImage)[iX*2 ] = (float)dfReSHV; + ((float *) pImage)[iX*2+1] = (float)dfImSHV; + } + else if( nBand == 3 ) + { + dfReSVH = Byte[7] * dfScale / 127.0; + dfImSVH = Byte[8] * dfScale / 127.0; + + ((float *) pImage)[iX*2 ] = (float)dfReSVH; + ((float *) pImage)[iX*2+1] = (float)dfImSVH; + } + else if( nBand == 4 ) + { + dfReSVV = Byte[9] * dfScale / 127.0; + dfImSVV = Byte[10]* dfScale / 127.0; + + ((float *) pImage)[iX*2 ] = (float)dfReSVV; + ((float *) pImage)[iX*2+1] = (float)dfImSVV; + } + } + + CPLFree( pabyRecord ); + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* PALSARRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* PALSARRasterBand() */ +/************************************************************************/ + +PALSARRasterBand::PALSARRasterBand( SAR_CEOSDataset *poGDS, int nBand ) + +{ + this->poDS = poGDS; + this->nBand = nBand; + + eDataType = GDT_CInt16; + + nBlockXSize = poGDS->nRasterXSize; + nBlockYSize = 1; + + if( nBand == 1 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_11" ); + else if( nBand == 2 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_22" ); + else if( nBand == 3 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_33" ); + else if( nBand == 4 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_12" ); + else if( nBand == 5 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_13" ); + else if( nBand == 6 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "Covariance_23" ); +} + +/************************************************************************/ +/* IReadBlock() */ +/* */ +/* Based on ERSDAC-VX-CEOS-004 */ +/************************************************************************/ + +CPLErr PALSARRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + struct CeosSARImageDesc *ImageDesc; + int offset; + GByte *pabyRecord; + SAR_CEOSDataset *poGDS = (SAR_CEOSDataset *) poDS; + + ImageDesc = &(poGDS->sVolume.ImageDesc); + + offset = ImageDesc->FileDescriptorLength + + ImageDesc->BytesPerRecord * nBlockYOff + + ImageDesc->ImageDataStart; + +/* -------------------------------------------------------------------- */ +/* Load all the pixel data associated with this scanline. */ +/* -------------------------------------------------------------------- */ + int nBytesToRead = ImageDesc->BytesPerPixel * nBlockXSize; + + pabyRecord = (GByte *) CPLMalloc( nBytesToRead ); + + if( VSIFSeekL( poGDS->fpImage, offset, SEEK_SET ) != 0 + || (int) VSIFReadL( pabyRecord, 1, nBytesToRead, + poGDS->fpImage ) != nBytesToRead ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Error reading %d bytes of CEOS record data at offset %d.\n" + "Reading file %s failed.", + nBytesToRead, offset, poGDS->GetDescription() ); + CPLFree( pabyRecord ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Copy the desired band out based on the size of the type, and */ +/* the interleaving mode. */ +/* -------------------------------------------------------------------- */ + if( nBand == 1 || nBand == 2 || nBand == 3 ) + { + // we need to pre-initialize things to set the imaginary component to 0 + memset( pImage, 0, nBlockXSize * 4 ); + + GDALCopyWords( pabyRecord + 4*(nBand - 1), GDT_Int16, 18, + pImage, GDT_Int16, 4, + nBlockXSize ); +#ifdef CPL_LSB + GDALSwapWords( pImage, 2, nBlockXSize, 4 ); +#endif + } + else + { + GDALCopyWords( pabyRecord + 6 + 4*(nBand - 4), GDT_CInt16, 18, + pImage, GDT_CInt16, 4, + nBlockXSize ); +#ifdef CPL_LSB + GDALSwapWords( pImage, 2, nBlockXSize*2, 2 ); +#endif + } + CPLFree( pabyRecord ); + +/* -------------------------------------------------------------------- */ +/* Convert the values into covariance form as per: */ +/* -------------------------------------------------------------------- */ +/* +** 1) PALSAR- adjust so that it reads bands as a covariance matrix, and +** set polarimetric interpretation accordingly: +** +** Covariance_11=HH*conj(HH): already there +** Covariance_22=2*HV*conj(HV): need a factor of 2 +** Covariance_33=VV*conj(VV): already there +** Covariance_12=sqrt(2)*HH*conj(HV): need the sqrt(2) factor +** Covariance_13=HH*conj(VV): already there +** Covariance_23=sqrt(2)*HV*conj(VV): need to take the conjugate, then +** multiply by sqrt(2) +** +*/ + + if( nBand == 2 ) + { + int i; + GInt16 *panLine = (GInt16 *) pImage; + + for( i = 0; i < nBlockXSize * 2; i++ ) + { + panLine[i] = (GInt16) CastToGInt16((float)2.0 * panLine[i]); + } + } + else if( nBand == 4 ) + { + int i; + double sqrt_2 = pow(2.0,0.5); + GInt16 *panLine = (GInt16 *) pImage; + + for( i = 0; i < nBlockXSize * 2; i++ ) + { + panLine[i] = (GInt16) CastToGInt16((float)floor(panLine[i] * sqrt_2 + 0.5)); + } + } + else if( nBand == 6 ) + { + int i; + GInt16 *panLine = (GInt16 *) pImage; + double sqrt_2 = pow(2.0,0.5); + + // real portion - just multiple by sqrt(2) + for( i = 0; i < nBlockXSize * 2; i += 2 ) + { + panLine[i] = (GInt16) CastToGInt16((float)floor(panLine[i] * sqrt_2 + 0.5)); + } + + // imaginary portion - conjugate and multiply + for( i = 1; i < nBlockXSize * 2; i += 2 ) + { + panLine[i] = (GInt16) CastToGInt16((float)floor(-panLine[i] * sqrt_2 + 0.5)); + } + } + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* SAR_CEOSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* SAR_CEOSDataset() */ +/************************************************************************/ + +SAR_CEOSDataset::SAR_CEOSDataset() + +{ + fpImage = NULL; + nGCPCount = 0; + pasGCPList = NULL; + + papszTempMD = NULL; +} + +/************************************************************************/ +/* ~SAR_CEOSDataset() */ +/************************************************************************/ + +SAR_CEOSDataset::~SAR_CEOSDataset() + +{ + FlushCache(); + + CSLDestroy( papszTempMD ); + + if( fpImage != NULL ) + VSIFCloseL( fpImage ); + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + if( sVolume.RecordList ) + { + Link_t *Links; + + for(Links = sVolume.RecordList; Links != NULL; Links = Links->next) + { + if(Links->object) + { + DeleteCeosRecord( (CeosRecord_t *) Links->object ); + Links->object = NULL; + } + } + DestroyList( sVolume.RecordList ); + } + FreeRecipes(); +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int SAR_CEOSDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *SAR_CEOSDataset::GetGCPProjection() + +{ + if( nGCPCount > 0 ) + return SRS_WKT_WGS84; + else + return ""; +} + +/************************************************************************/ +/* GetGCP() */ +/************************************************************************/ + +const GDAL_GCP *SAR_CEOSDataset::GetGCPs() + +{ + return pasGCPList; +} + + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **SAR_CEOSDataset::GetMetadataDomainList() +{ + return CSLAddString(GDALDataset::GetMetadataDomainList(), "ceos-FFF-n-n-n-n:r"); +} + +/************************************************************************/ +/* GetMetadata() */ +/* */ +/* We provide our own GetMetadata() so that we can override */ +/* behavior for some very specialized domain names intended to */ +/* give us access to raw record data. */ +/* */ +/* The domain must look like: */ +/* ceos-FFF-n-n-n-n:r */ +/* */ +/* FFF - The file id - one of vol, lea, img, trl or nul. */ +/* n-n-n-n - the record type code such as 18-10-18-20 for the */ +/* dataset summary record in the leader file. */ +/* :r - The zero based record number to fetch (optional) */ +/* */ +/* Note that only records that are pre-loaded will be */ +/* accessable, and this normally means that most image records */ +/* are not available. */ +/************************************************************************/ + +char **SAR_CEOSDataset::GetMetadata( const char * pszDomain ) + +{ + if( pszDomain == NULL || !EQUALN(pszDomain,"ceos-",5) ) + return GDALDataset::GetMetadata( pszDomain ); + +/* -------------------------------------------------------------------- */ +/* Identify which file to fetch the file from. */ +/* -------------------------------------------------------------------- */ + int nFileId = -1; + + if( EQUALN(pszDomain,"ceos-vol",8) ) + { + nFileId = __CEOS_VOLUME_DIR_FILE; + } + else if( EQUALN(pszDomain,"ceos-lea",8) ) + { + nFileId = __CEOS_LEADER_FILE; + } + else if( EQUALN(pszDomain,"ceos-img",8) ) + { + nFileId = __CEOS_IMAGRY_OPT_FILE; + } + else if( EQUALN(pszDomain,"ceos-trl",8) ) + { + nFileId = __CEOS_TRAILER_FILE; + } + else if( EQUALN(pszDomain,"ceos-nul",8) ) + { + nFileId = __CEOS_NULL_VOL_FILE; + } + else + return NULL; + + pszDomain += 8; + +/* -------------------------------------------------------------------- */ +/* Identify the record type. */ +/* -------------------------------------------------------------------- */ + CeosTypeCode_t sTypeCode; + int a, b, c, d, nRecordIndex = -1; + + if( sscanf( pszDomain, "-%d-%d-%d-%d:%d", + &a, &b, &c, &d, &nRecordIndex ) != 5 + && sscanf( pszDomain, "-%d-%d-%d-%d", + &a, &b, &c, &d ) != 4 ) + { + return NULL; + } + + sTypeCode = QuadToTC( a, b, c, d ); + +/* -------------------------------------------------------------------- */ +/* Try to fetch the record. */ +/* -------------------------------------------------------------------- */ + CeosRecord_t *record; + + record = FindCeosRecord( sVolume.RecordList, sTypeCode, nFileId, + -1, nRecordIndex ); + + if( record == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Massage the data into a safe textual format. The RawRecord */ +/* just has zero bytes turned into spaces while the */ +/* EscapedRecord has regular backslash escaping applied to zero */ +/* chars, double quotes, and backslashes. */ +/* just turn zero bytes into spaces. */ +/* -------------------------------------------------------------------- */ + char *pszSafeCopy; + int i; + + CSLDestroy( papszTempMD ); + + // Escaped version + pszSafeCopy = CPLEscapeString( (char *) record->Buffer, + record->Length, + CPLES_BackslashQuotable ); + papszTempMD = CSLSetNameValue( NULL, "EscapedRecord", pszSafeCopy ); + CPLFree( pszSafeCopy ); + + + // Copy with '\0' replaced by spaces. + + pszSafeCopy = (char *) CPLCalloc(1,record->Length+1); + memcpy( pszSafeCopy, record->Buffer, record->Length ); + + for( i = 0; i < record->Length; i++ ) + if( pszSafeCopy[i] == '\0' ) + pszSafeCopy[i] = ' '; + + papszTempMD = CSLSetNameValue( papszTempMD, "RawRecord", pszSafeCopy ); + + CPLFree( pszSafeCopy ); + + return papszTempMD; +} + +/************************************************************************/ +/* ScanForMetadata() */ +/************************************************************************/ + +void SAR_CEOSDataset::ScanForMetadata() + +{ + char szField[128], szVolId[128]; + CeosRecord_t *record; + +/* -------------------------------------------------------------------- */ +/* Get the volume id (with the sensor name) */ +/* -------------------------------------------------------------------- */ + record = FindCeosRecord( sVolume.RecordList, VOLUME_DESCRIPTOR_RECORD_TC, + __CEOS_VOLUME_DIR_FILE, -1, -1 ); + szVolId[0] = '\0'; + if( record != NULL ) + { + szVolId[16] = '\0'; + + GetCeosField( record, 61, "A16", szVolId ); + + SetMetadataItem( "CEOS_LOGICAL_VOLUME_ID", szVolId ); + +/* -------------------------------------------------------------------- */ +/* Processing facility */ +/* -------------------------------------------------------------------- */ + szField[0] = '\0'; + szField[12] = '\0'; + + GetCeosField( record, 149, "A12", szField ); + + if( !EQUALN(szField," ",12) ) + SetMetadataItem( "CEOS_PROCESSING_FACILITY", szField ); + +/* -------------------------------------------------------------------- */ +/* Agency */ +/* -------------------------------------------------------------------- */ + szField[8] = '\0'; + + GetCeosField( record, 141, "A8", szField ); + + if( !EQUALN(szField," ",8) ) + SetMetadataItem( "CEOS_PROCESSING_AGENCY", szField ); + +/* -------------------------------------------------------------------- */ +/* Country */ +/* -------------------------------------------------------------------- */ + szField[12] = '\0'; + + GetCeosField( record, 129, "A12", szField ); + + if( !EQUALN(szField," ",12) ) + SetMetadataItem( "CEOS_PROCESSING_COUNTRY", szField ); + +/* -------------------------------------------------------------------- */ +/* software id. */ +/* -------------------------------------------------------------------- */ + szField[12] = '\0'; + + GetCeosField( record, 33, "A12", szField ); + + if( !EQUALN(szField," ",12) ) + SetMetadataItem( "CEOS_SOFTWARE_ID", szField ); + +/* -------------------------------------------------------------------- */ +/* product identifier. */ +/* -------------------------------------------------------------------- */ + szField[8] = '\0'; + + GetCeosField( record, 261, "A8", szField ); + + if( !EQUALN(szField," ",8) ) + SetMetadataItem( "CEOS_PRODUCT_ID", szField ); + +/* -------------------------------------------------------------------- */ +/* volume identifier. */ +/* -------------------------------------------------------------------- */ + szField[16] = '\0'; + + GetCeosField( record, 77, "A16", szField ); + + if( !EQUALN(szField," ",16) ) + SetMetadataItem( "CEOS_VOLSET_ID", szField ); + } + +/* ==================================================================== */ +/* Dataset summary record. */ +/* ==================================================================== */ + record = FindCeosRecord( sVolume.RecordList, LEADER_DATASET_SUMMARY_TC, + __CEOS_LEADER_FILE, -1, -1 ); + + if( record == NULL ) + record = FindCeosRecord( sVolume.RecordList, LEADER_DATASET_SUMMARY_TC, + __CEOS_TRAILER_FILE, -1, -1 ); + + if( record == NULL ) + record = FindCeosRecord( sVolume.RecordList, + LEADER_DATASET_SUMMARY_ERS2_TC, + __CEOS_LEADER_FILE, -1, -1 ); + + if( record != NULL ) + { +/* -------------------------------------------------------------------- */ +/* Get the acquisition date. */ +/* -------------------------------------------------------------------- */ + szField[0] = '\0'; + szField[32] = '\0'; + + GetCeosField( record, 69, "A32", szField ); + + SetMetadataItem( "CEOS_ACQUISITION_TIME", szField ); + +/* -------------------------------------------------------------------- */ +/* Ascending/Descending */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 101, "A16", szField ); + szField[16] = '\0'; + + if( strstr(szVolId,"RSAT") != NULL + && !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_ASC_DES", szField ); + +/* -------------------------------------------------------------------- */ +/* True heading - at least for ERS2. */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 149, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_TRUE_HEADING", szField ); + +/* -------------------------------------------------------------------- */ +/* Ellipsoid */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 165, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_ELLIPSOID", szField ); + +/* -------------------------------------------------------------------- */ +/* Semimajor, semiminor axis */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 181, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_SEMI_MAJOR", szField ); + + GetCeosField( record, 197, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_SEMI_MINOR", szField ); + +/* -------------------------------------------------------------------- */ +/* SCENE LENGTH KM */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 341, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_SCENE_LENGTH_KM", szField ); + +/* -------------------------------------------------------------------- */ +/* SCENE WIDTH KM */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 357, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_SCENE_WIDTH_KM", szField ); + +/* -------------------------------------------------------------------- */ +/* MISSION ID */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 397, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_MISSION_ID", szField ); + +/* -------------------------------------------------------------------- */ +/* SENSOR ID */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 413, "A32", szField ); + szField[32] = '\0'; + + if( !EQUALN(szField," ",32 ) ) + SetMetadataItem( "CEOS_SENSOR_ID", szField ); + + +/* -------------------------------------------------------------------- */ +/* ORBIT NUMBER */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 445, "A8", szField ); + szField[8] = '\0'; + + if( !EQUALN(szField," ",8 ) ) + SetMetadataItem( "CEOS_ORBIT_NUMBER", szField ); + + +/* -------------------------------------------------------------------- */ +/* Platform latitude */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 453, "A8", szField ); + szField[8] = '\0'; + + if( !EQUALN(szField," ",8 ) ) + SetMetadataItem( "CEOS_PLATFORM_LATITUDE", szField ); + +/* -------------------------------------------------------------------- */ +/* Platform longitude */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 461, "A8", szField ); + szField[8] = '\0'; + + if( !EQUALN(szField," ",8 ) ) + SetMetadataItem( "CEOS_PLATFORM_LONGITUDE", szField ); + +/* -------------------------------------------------------------------- */ +/* Platform heading - at least for ERS2. */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 469, "A8", szField ); + szField[8] = '\0'; + + if( !EQUALN(szField," ",8 ) ) + SetMetadataItem( "CEOS_PLATFORM_HEADING", szField ); + +/* -------------------------------------------------------------------- */ +/* Look Angle. */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 477, "A8", szField ); + szField[8] = '\0'; + + if( !EQUALN(szField," ",8 ) ) + SetMetadataItem( "CEOS_SENSOR_CLOCK_ANGLE", szField ); + +/* -------------------------------------------------------------------- */ +/* Incidence angle */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 485, "A8", szField ); + szField[8] = '\0'; + + if( !EQUALN(szField," ",8 ) ) + SetMetadataItem( "CEOS_INC_ANGLE", szField ); + +/* -------------------------------------------------------------------- */ +/* Pixel time direction indicator */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 1527, "A8", szField ); + szField[8] = '\0'; + + if( !EQUALN(szField," ",8 ) ) + SetMetadataItem( "CEOS_PIXEL_TIME_DIR", szField ); + +/* -------------------------------------------------------------------- */ +/* Line spacing */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 1687, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_LINE_SPACING_METERS", szField ); +/* -------------------------------------------------------------------- */ +/* Pixel spacing */ +/* -------------------------------------------------------------------- */ + GetCeosField( record, 1703, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_PIXEL_SPACING_METERS", szField ); + + } + +/* -------------------------------------------------------------------- */ +/* Get the beam mode, for radarsat. */ +/* -------------------------------------------------------------------- */ + record = FindCeosRecord( sVolume.RecordList, + LEADER_RADIOMETRIC_COMPENSATION_TC, + __CEOS_LEADER_FILE, -1, -1 ); + + if( strstr(szVolId,"RSAT") != NULL && record != NULL ) + { + szField[16] = '\0'; + + GetCeosField( record, 4189, "A16", szField ); + + SetMetadataItem( "CEOS_BEAM_TYPE", szField ); + } + +/* ==================================================================== */ +/* ERS calibration and incidence angle info */ +/* ==================================================================== */ + record = FindCeosRecord( sVolume.RecordList, ERS_GENERAL_FACILITY_DATA_TC, + __CEOS_LEADER_FILE, -1, -1 ); + + if( record == NULL ) + record = FindCeosRecord( sVolume.RecordList, + ERS_GENERAL_FACILITY_DATA_ALT_TC, + __CEOS_LEADER_FILE, -1, -1 ); + + if( record != NULL ) + { + GetCeosField( record, 13 , "A64", szField ); + szField[64] = '\0'; + + /* Avoid PCS records, which don't contain necessary info */ + if( strstr( szField, "GENERAL") == NULL ) + record = NULL; + } + + if( record != NULL ) + { + GetCeosField( record, 583 , "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ", 16 ) ) + SetMetadataItem( "CEOS_INC_ANGLE_FIRST_RANGE", szField ); + + GetCeosField( record, 599 , "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ", 16 ) ) + SetMetadataItem( "CEOS_INC_ANGLE_CENTRE_RANGE", szField ); + + GetCeosField( record, 615, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ", 16 ) ) + SetMetadataItem( "CEOS_INC_ANGLE_LAST_RANGE", szField ); + + GetCeosField( record, 663, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ", 16 ) ) + SetMetadataItem( "CEOS_CALIBRATION_CONSTANT_K", szField ); + + GetCeosField( record, 1855, "A20", szField ); + szField[20] = '\0'; + + if( !EQUALN(szField," ", 20 ) ) + SetMetadataItem( "CEOS_GROUND_TO_SLANT_C0", szField ); + + GetCeosField( record, 1875, "A20", szField ); + szField[20] = '\0'; + + if( !EQUALN(szField," ", 20 ) ) + SetMetadataItem( "CEOS_GROUND_TO_SLANT_C1", szField ); + + GetCeosField( record, 1895, "A20", szField ); + szField[20] = '\0'; + + if( !EQUALN(szField," ", 20 ) ) + SetMetadataItem( "CEOS_GROUND_TO_SLANT_C2", szField ); + + GetCeosField( record, 1915, "A20", szField ); + szField[20] = '\0'; + + if( !EQUALN(szField," ", 20 ) ) + SetMetadataItem( "CEOS_GROUND_TO_SLANT_C3", szField ); + + } +/* -------------------------------------------------------------------- */ +/* Detailed Processing Parameters (Radarsat) */ +/* -------------------------------------------------------------------- */ + record = FindCeosRecord( sVolume.RecordList, RSAT_PROC_PARAM_TC, + __CEOS_LEADER_FILE, -1, -1 ); + + if( record == NULL ) + record = FindCeosRecord( sVolume.RecordList, RSAT_PROC_PARAM_TC, + __CEOS_TRAILER_FILE, -1, -1 ); + + if( record != NULL ) + { + GetCeosField( record, 192, "A21", szField ); + szField[21] = '\0'; + + if( !EQUALN(szField," ",21 ) ) + SetMetadataItem( "CEOS_PROC_START", szField ); + + GetCeosField( record, 213, "A21", szField ); + szField[21] = '\0'; + + if( !EQUALN(szField," ",21 ) ) + SetMetadataItem( "CEOS_PROC_STOP", szField ); + + GetCeosField( record, 4649, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_EPH_ORB_DATA_0", szField ); + + GetCeosField( record, 4665, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_EPH_ORB_DATA_1", szField ); + + GetCeosField( record, 4681, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_EPH_ORB_DATA_2", szField ); + + GetCeosField( record, 4697, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_EPH_ORB_DATA_3", szField ); + + GetCeosField( record, 4713, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_EPH_ORB_DATA_4", szField ); + + GetCeosField( record, 4729, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_EPH_ORB_DATA_5", szField ); + + GetCeosField( record, 4745, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_EPH_ORB_DATA_6", szField ); + + GetCeosField( record, 4908, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_GROUND_TO_SLANT_C0", szField ); + + GetCeosField( record, 4924, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_GROUND_TO_SLANT_C1", szField ); + + GetCeosField( record, 4940, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_GROUND_TO_SLANT_C2", szField ); + + GetCeosField( record, 4956, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_GROUND_TO_SLANT_C3", szField ); + + GetCeosField( record, 4972, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_GROUND_TO_SLANT_C4", szField ); + + GetCeosField( record, 4988, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_GROUND_TO_SLANT_C5", szField ); + + GetCeosField( record, 7334, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_INC_ANGLE_FIRST_RANGE", szField ); + + GetCeosField( record, 7350, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ",16 ) ) + SetMetadataItem( "CEOS_INC_ANGLE_LAST_RANGE", szField ); + + } +/* -------------------------------------------------------------------- */ +/* Get process-to-raw data coordinate translation values. These */ +/* are likely specific to Atlantis APP products. */ +/* -------------------------------------------------------------------- */ + record = FindCeosRecord( sVolume.RecordList, + IMAGE_HEADER_RECORD_TC, + __CEOS_IMAGRY_OPT_FILE, -1, -1 ); + + if( record != NULL ) + { + GetCeosField( record, 449, "A4", szField ); + szField[4] = '\0'; + + if( !EQUALN(szField," ",4 ) ) + SetMetadataItem( "CEOS_DM_CORNER", szField ); + + + GetCeosField( record, 453, "A4", szField ); + szField[4] = '\0'; + + if( !EQUALN(szField," ",4 ) ) + SetMetadataItem( "CEOS_DM_TRANSPOSE", szField ); + + + GetCeosField( record, 457, "A4", szField ); + szField[4] = '\0'; + + if( !EQUALN(szField," ",4 ) ) + SetMetadataItem( "CEOS_DM_START_SAMPLE", szField ); + + + GetCeosField( record, 461, "A5", szField ); + szField[5] = '\0'; + + if( !EQUALN(szField," ",5 ) ) + SetMetadataItem( "CEOS_DM_START_PULSE", szField ); + + + GetCeosField( record, 466, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ", 16 ) ) + SetMetadataItem( "CEOS_DM_FAST_ALPHA", szField ); + + + GetCeosField( record, 482, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ", 16 ) ) + SetMetadataItem( "CEOS_DM_FAST_BETA", szField ); + + + GetCeosField( record, 498, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ", 16 ) ) + SetMetadataItem( "CEOS_DM_SLOW_ALPHA", szField ); + + + GetCeosField( record, 514, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ", 16 ) ) + SetMetadataItem( "CEOS_DM_SLOW_BETA", szField ); + + + GetCeosField( record, 530, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ", 16 ) ) + SetMetadataItem( "CEOS_DM_FAST_ALPHA_2", szField ); + + } + +/* -------------------------------------------------------------------- */ +/* Try to find calibration information from Radiometric Data */ +/* Record. */ +/* -------------------------------------------------------------------- */ + record = FindCeosRecord( sVolume.RecordList, + LEADER_RADIOMETRIC_DATA_RECORD_TC, + __CEOS_LEADER_FILE, -1, -1 ); + + if( record == NULL ) + record = FindCeosRecord( sVolume.RecordList, + LEADER_RADIOMETRIC_DATA_RECORD_TC, + __CEOS_TRAILER_FILE, -1, -1 ); + + if( record != NULL ) + { + GetCeosField( record, 8317, "A16", szField ); + szField[16] = '\0'; + + if( !EQUALN(szField," ", 16 ) ) + SetMetadataItem( "CEOS_CALIBRATION_OFFSET", szField ); + } + +/* -------------------------------------------------------------------- */ +/* For ERS Standard Format Landsat scenes we pick up the */ +/* calibration offset and gain from the Radiometric Ancillary */ +/* Record. */ +/* -------------------------------------------------------------------- */ + record = FindCeosRecord( sVolume.RecordList, + QuadToTC( 0x3f, 0x24, 0x12, 0x09 ), + __CEOS_LEADER_FILE, -1, -1 ); + if( record != NULL ) + { + GetCeosField( record, 29, "A20", szField ); + szField[20] = '\0'; + + if( !EQUALN(szField," ", 20 ) ) + SetMetadataItem( "CEOS_OFFSET_A0", szField ); + + GetCeosField( record, 49, "A20", szField ); + szField[20] = '\0'; + + if( !EQUALN(szField," ", 20 ) ) + SetMetadataItem( "CEOS_GAIN_A1", szField ); + } + +/* -------------------------------------------------------------------- */ +/* For ERS Standard Format Landsat scenes we pick up the */ +/* gain setting from the Scene Header Record. */ +/* -------------------------------------------------------------------- */ + record = FindCeosRecord( sVolume.RecordList, + QuadToTC( 0x12, 0x12, 0x12, 0x09 ), + __CEOS_LEADER_FILE, -1, -1 ); + if( record != NULL ) + { + GetCeosField( record, 1486, "A1", szField ); + szField[1] = '\0'; + + if( szField[0] == 'H' || szField[0] == 'V' ) + SetMetadataItem( "CEOS_GAIN_SETTING", szField ); + } +} + +/************************************************************************/ +/* ScanForMapProjection() */ +/* */ +/* Try to find a map projection record, and read corner points */ +/* from it. This has only been tested with ERS products. */ +/************************************************************************/ + +int SAR_CEOSDataset::ScanForMapProjection() + +{ + CeosRecord_t *record; + char szField[100]; + int i; + +/* -------------------------------------------------------------------- */ +/* Find record, and try to determine if it has useful GCPs. */ +/* -------------------------------------------------------------------- */ + + record = FindCeosRecord( sVolume.RecordList, + LEADER_MAP_PROJ_RECORD_TC, + __CEOS_LEADER_FILE, -1, -1 ); + + /* JERS from Japan */ + if( record == NULL ) + record = FindCeosRecord( sVolume.RecordList, + LEADER_MAP_PROJ_RECORD_JERS_TC, + __CEOS_LEADER_FILE, -1, -1 ); + + if( record == NULL ) + return FALSE; + + memset( szField, 0, 17 ); + GetCeosField( record, 29, "A16", szField ); + + if( !EQUALN(szField,"Slant Range",11) && !EQUALN(szField,"Ground Range",12) + && !EQUALN(szField,"GEOCODED",8) ) + return FALSE; + + GetCeosField( record, 1073, "A16", szField ); + if( EQUALN(szField," ",8) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read corner points. */ +/* -------------------------------------------------------------------- */ + nGCPCount = 4; + pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPCount); + + GDALInitGCPs( nGCPCount, pasGCPList ); + + for( i = 0; i < nGCPCount; i++ ) + { + char szId[32]; + + sprintf( szId, "%d", i+1 ); + pasGCPList[i].pszId = CPLStrdup( szId ); + + GetCeosField( record, 1073+32*i, "A16", szField ); + pasGCPList[i].dfGCPY = CPLAtof(szField); + GetCeosField( record, 1089+32*i, "A16", szField ); + pasGCPList[i].dfGCPX = CPLAtof(szField); + pasGCPList[i].dfGCPZ = 0.0; + } + + pasGCPList[0].dfGCPLine = 0.5; + pasGCPList[0].dfGCPPixel = 0.5; + + pasGCPList[1].dfGCPLine = 0.5; + pasGCPList[1].dfGCPPixel = nRasterXSize-0.5; + + pasGCPList[2].dfGCPLine = nRasterYSize-0.5; + pasGCPList[2].dfGCPPixel = nRasterXSize-0.5; + + pasGCPList[3].dfGCPLine = nRasterYSize-0.5; + pasGCPList[3].dfGCPPixel = 0.5; + + return TRUE; +} + +/************************************************************************/ +/* ScanForGCPs() */ +/************************************************************************/ + +void SAR_CEOSDataset::ScanForGCPs() + +{ + int iScanline, nStep, nGCPMax = 15; + +/* -------------------------------------------------------------------- */ +/* Do we have a standard 180 bytes of prefix data (192 bytes */ +/* including the record marker information)? If not, it is */ +/* unlikely that the GCPs are available. */ +/* -------------------------------------------------------------------- */ + if( sVolume.ImageDesc.ImageDataStart < 192 ) + { + ScanForMapProjection(); + return; + } + +/* -------------------------------------------------------------------- */ +/* Just sample fix scanlines through the image for GCPs, to */ +/* return 15 GCPs. That is an adequate coverage for most */ +/* purposes. A GCP is collected from the beginning, middle and */ +/* end of each scanline. */ +/* -------------------------------------------------------------------- */ + nGCPCount = 0; + pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPMax); + + nStep = (GetRasterYSize()-1) / (nGCPMax / 3 - 1); + for( iScanline = 0; iScanline < GetRasterYSize(); iScanline += nStep ) + { + int nFileOffset, iGCP; + GInt32 anRecord[192/4]; + + if( nGCPCount > nGCPMax-3 ) + break; + + CalcCeosSARImageFilePosition( &sVolume, 1, iScanline+1, NULL, + &nFileOffset ); + + if( VSIFSeekL( fpImage, nFileOffset, SEEK_SET ) != 0 + || VSIFReadL( anRecord, 1, 192, fpImage ) != 192 ) + break; + + /* loop over first, middle and last pixel gcps */ + + for( iGCP = 0; iGCP < 3; iGCP++ ) + { + int nLat, nLong; + + nLat = CPL_MSBWORD32( anRecord[132/4 + iGCP] ); + nLong = CPL_MSBWORD32( anRecord[144/4 + iGCP] ); + + if( nLat != 0 || nLong != 0 ) + { + char szId[32]; + + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + + CPLFree( pasGCPList[nGCPCount].pszId ); + + sprintf( szId, "%d", nGCPCount+1 ); + pasGCPList[nGCPCount].pszId = CPLStrdup( szId ); + + pasGCPList[nGCPCount].dfGCPX = nLong / 1000000.0; + pasGCPList[nGCPCount].dfGCPY = nLat / 1000000.0; + pasGCPList[nGCPCount].dfGCPZ = 0.0; + + pasGCPList[nGCPCount].dfGCPLine = iScanline + 0.5; + + if( iGCP == 0 ) + pasGCPList[nGCPCount].dfGCPPixel = 0.5; + else if( iGCP == 1 ) + pasGCPList[nGCPCount].dfGCPPixel = + GetRasterXSize() / 2.0; + else + pasGCPList[nGCPCount].dfGCPPixel = + GetRasterXSize() - 0.5; + + nGCPCount++; + } + } + } + /* If general GCP's weren't found, look for Map Projection (eg. JERS) */ + if( nGCPCount == 0 ) + { + ScanForMapProjection(); + return; + } +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *SAR_CEOSDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i, bNative; + +/* -------------------------------------------------------------------- */ +/* Does this appear to be a valid ceos leader record? */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < __CEOS_HEADER_LENGTH ) + return NULL; + + if( (poOpenInfo->pabyHeader[4] != 0x3f + && poOpenInfo->pabyHeader[4] != 0x32) + || poOpenInfo->pabyHeader[5] != 0xc0 + || poOpenInfo->pabyHeader[6] != 0x12 + || poOpenInfo->pabyHeader[7] != 0x12 ) + return NULL; + + // some products (#1862) have byte swapped record length/number + // values and will blow stuff up -- explicitly ignore if record index + // value appears to be little endian. + if( poOpenInfo->pabyHeader[0] != 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The SAR_CEOS driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + if( fp == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + SAR_CEOSDataset *poDS; + CeosSARVolume_t *psVolume; + + poDS = new SAR_CEOSDataset(); + + psVolume = &(poDS->sVolume); + InitCeosSARVolume( psVolume, 0 ); + +/* -------------------------------------------------------------------- */ +/* Try to read the current file as an imagery file. */ +/* -------------------------------------------------------------------- */ + + psVolume->ImagryOptionsFile = TRUE; + if( ProcessData( fp, __CEOS_IMAGRY_OPT_FILE, psVolume, 4, -1) != CE_None ) + { + delete poDS; + VSIFCloseL(fp); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try the various filenames. */ +/* -------------------------------------------------------------------- */ + char *pszPath; + char *pszBasename; + char *pszExtension; + int nBand, iFile; + + pszPath = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename)); + pszBasename = CPLStrdup(CPLGetBasename(poOpenInfo->pszFilename)); + pszExtension = CPLStrdup(CPLGetExtension(poOpenInfo->pszFilename)); + if( strlen(pszBasename) > 4 ) + nBand = atoi( pszBasename + 4 ); + else + nBand = 0; + + for( iFile = 0; iFile < 5;iFile++ ) + { + int e; + + /* skip image file ... we already did it */ + if( iFile == 2 ) + continue; + + e = 0; + while( CeosExtension[e][iFile] != NULL ) + { + VSILFILE *process_fp; + char *pszFilename = NULL; + + /* build filename */ + if( EQUAL(CeosExtension[e][5],"base") ) + { + char szMadeBasename[32]; + + sprintf( szMadeBasename, CeosExtension[e][iFile], nBand ); + pszFilename = CPLStrdup( + CPLFormFilename(pszPath,szMadeBasename, pszExtension)); + } + else if( EQUAL(CeosExtension[e][5],"ext") ) + { + pszFilename = CPLStrdup( + CPLFormFilename(pszPath,pszBasename, + CeosExtension[e][iFile])); + } + else if( EQUAL(CeosExtension[e][5],"whole") ) + { + pszFilename = CPLStrdup( + CPLFormFilename(pszPath,CeosExtension[e][iFile],"")); + } + + // This is for SAR SLC as per the SAR Toolbox (from ASF). + else if( EQUAL(CeosExtension[e][5],"ext2") ) + { + char szThisExtension[32]; + + if( strlen(pszExtension) > 3 ) + sprintf( szThisExtension, "%s%s", + CeosExtension[e][iFile], + pszExtension+3 ); + else + sprintf( szThisExtension, "%s", + CeosExtension[e][iFile] ); + + pszFilename = CPLStrdup( + CPLFormFilename(pszPath,pszBasename,szThisExtension)); + } + + CPLAssert( pszFilename != NULL ); + if( pszFilename == NULL ) + return NULL; + + /* try to open */ + process_fp = VSIFOpenL( pszFilename, "rb" ); + + /* try upper case */ + if( process_fp == NULL ) + { + for( i = strlen(pszFilename)-1; + i >= 0 && pszFilename[i] != '/' && pszFilename[i] != '\\'; + i-- ) + { + if( pszFilename[i] >= 'a' && pszFilename[i] <= 'z' ) + pszFilename[i] = pszFilename[i] - 'a' + 'A'; + } + + process_fp = VSIFOpenL( pszFilename, "rb" ); + } + + if( process_fp != NULL ) + { + CPLDebug( "CEOS", "Opened %s.\n", pszFilename ); + + VSIFSeekL( process_fp, 0, SEEK_END ); + if( ProcessData( process_fp, iFile, psVolume, -1, + VSIFTellL( process_fp ) ) == 0 ) + { + switch( iFile ) + { + case 0: psVolume->VolumeDirectoryFile = TRUE; + break; + case 1: psVolume->SARLeaderFile = TRUE; + break; + case 3: psVolume->SARTrailerFile = TRUE; + break; + case 4: psVolume->NullVolumeDirectoryFile = TRUE; + break; + } + + VSIFCloseL( process_fp ); + CPLFree( pszFilename ); + break; /* Exit the while loop, we have this data type*/ + } + + VSIFCloseL( process_fp ); + } + + CPLFree( pszFilename ); + + e++; + } + } + + CPLFree( pszPath ); + CPLFree( pszBasename ); + CPLFree( pszExtension ); + +/* -------------------------------------------------------------------- */ +/* Check that we have an image description. */ +/* -------------------------------------------------------------------- */ + struct CeosSARImageDesc *psImageDesc; + GetCeosSARImageDesc( psVolume ); + psImageDesc = &(psVolume->ImageDesc); + if( !psImageDesc->ImageDescValid ) + { + delete poDS; + + CPLDebug( "CEOS", + "Unable to extract CEOS image description\n" + "from %s.", + poOpenInfo->pszFilename ); + + VSIFCloseL(fp); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Establish image type. */ +/* -------------------------------------------------------------------- */ + GDALDataType eType; + + switch( psImageDesc->DataType ) + { + case __CEOS_TYP_CHAR: + case __CEOS_TYP_UCHAR: + eType = GDT_Byte; + break; + + case __CEOS_TYP_SHORT: + eType = GDT_Int16; + break; + + case __CEOS_TYP_COMPLEX_SHORT: + case __CEOS_TYP_PALSAR_COMPLEX_SHORT: + eType = GDT_CInt16; + break; + + case __CEOS_TYP_USHORT: + eType = GDT_UInt16; + break; + + case __CEOS_TYP_LONG: + eType = GDT_Int32; + break; + + case __CEOS_TYP_ULONG: + eType = GDT_UInt32; + break; + + case __CEOS_TYP_FLOAT: + eType = GDT_Float32; + break; + + case __CEOS_TYP_DOUBLE: + eType = GDT_Float64; + break; + + case __CEOS_TYP_COMPLEX_FLOAT: + case __CEOS_TYP_CCP_COMPLEX_FLOAT: + eType = GDT_CFloat32; + break; + + default: + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported CEOS image data type %d.\n", + psImageDesc->DataType ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = psImageDesc->PixelsPerLine; + poDS->nRasterYSize = psImageDesc->Lines; + +#ifdef CPL_LSB + bNative = FALSE; +#else + bNative = TRUE; +#endif + +/* -------------------------------------------------------------------- */ +/* Special case for compressed cross products. */ +/* -------------------------------------------------------------------- */ + if( psImageDesc->DataType == __CEOS_TYP_CCP_COMPLEX_FLOAT ) + { + for( int iBand = 0; iBand < psImageDesc->NumChannels; iBand++ ) + { + poDS->SetBand( poDS->nBands+1, + new CCPRasterBand( poDS, poDS->nBands+1, eType ) ); + } + + /* mark this as a Scattering Matrix product */ + if ( poDS->GetRasterCount() == 4 ) { + poDS->SetMetadataItem( "MATRIX_REPRESENTATION", "SCATTERING" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Special case for PALSAR data. */ +/* -------------------------------------------------------------------- */ + else if( psImageDesc->DataType == __CEOS_TYP_PALSAR_COMPLEX_SHORT ) + { + for( int iBand = 0; iBand < psImageDesc->NumChannels; iBand++ ) + { + poDS->SetBand( poDS->nBands+1, + new PALSARRasterBand( poDS, poDS->nBands+1 ) ); + } + + /* mark this as a Symmetrized Covariance product if appropriate */ + if ( poDS->GetRasterCount() == 6 ) { + poDS->SetMetadataItem( "MATRIX_REPRESENTATION", + "SYMMETRIZED_COVARIANCE" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Roll our own ... */ +/* -------------------------------------------------------------------- */ + else if( psImageDesc->RecordsPerLine > 1 + || psImageDesc->DataType == __CEOS_TYP_CHAR + || psImageDesc->DataType == __CEOS_TYP_LONG + || psImageDesc->DataType == __CEOS_TYP_ULONG + || psImageDesc->DataType == __CEOS_TYP_DOUBLE ) + { + for( int iBand = 0; iBand < psImageDesc->NumChannels; iBand++ ) + { + poDS->SetBand( poDS->nBands+1, + new SAR_CEOSRasterBand( poDS, poDS->nBands+1, + eType ) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Use raw services for well behaved files. */ +/* -------------------------------------------------------------------- */ + else + { + int StartData; + int nLineSize, nLineSize2; + + CalcCeosSARImageFilePosition( psVolume, 1, 1, NULL, &StartData ); + + StartData += psImageDesc->ImageDataStart; + + CalcCeosSARImageFilePosition( psVolume, 1, 1, NULL, &nLineSize ); + CalcCeosSARImageFilePosition( psVolume, 1, 2, NULL, &nLineSize2 ); + + nLineSize = nLineSize2 - nLineSize; + + for( int iBand = 0; iBand < psImageDesc->NumChannels; iBand++ ) + { + int nStartData, nPixelOffset, nLineOffset; + + if( psImageDesc->ChannelInterleaving == __CEOS_IL_PIXEL ) + { + CalcCeosSARImageFilePosition(psVolume,1,1,NULL,&nStartData); + + nStartData += psImageDesc->ImageDataStart; + nStartData += psImageDesc->BytesPerPixel * iBand; + + nPixelOffset = + psImageDesc->BytesPerPixel * psImageDesc->NumChannels; + nLineOffset = nLineSize; + } + else if( psImageDesc->ChannelInterleaving == __CEOS_IL_LINE ) + { + CalcCeosSARImageFilePosition(psVolume, iBand+1, 1, NULL, + &nStartData); + + nStartData += psImageDesc->ImageDataStart; + nPixelOffset = psImageDesc->BytesPerPixel; + nLineOffset = nLineSize * psImageDesc->NumChannels; + } + else if( psImageDesc->ChannelInterleaving == __CEOS_IL_BAND ) + { + CalcCeosSARImageFilePosition(psVolume, iBand+1, 1, NULL, + &nStartData); + + nStartData += psImageDesc->ImageDataStart; + nPixelOffset = psImageDesc->BytesPerPixel; + nLineOffset = nLineSize; + } + else + { + CPLAssert( FALSE ); + return NULL; + } + + + poDS->SetBand( poDS->nBands+1, + new RawRasterBand( + poDS, poDS->nBands+1, fp, + nStartData, nPixelOffset, nLineOffset, + eType, bNative, TRUE ) ); + } + + } + +/* -------------------------------------------------------------------- */ +/* Adopt the file pointer. */ +/* -------------------------------------------------------------------- */ + poDS->fpImage = fp; + +/* -------------------------------------------------------------------- */ +/* Collect metadata. */ +/* -------------------------------------------------------------------- */ + poDS->ScanForMetadata(); + +/* -------------------------------------------------------------------- */ +/* Check for GCPs. */ +/* -------------------------------------------------------------------- */ + poDS->ScanForGCPs(); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Open overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* ProcessData() */ +/************************************************************************/ +static int +ProcessData( VSILFILE *fp, int fileid, CeosSARVolume_t *sar, int max_records, + vsi_l_offset max_bytes ) + +{ + unsigned char temp_buffer[__CEOS_HEADER_LENGTH]; + unsigned char *temp_body = NULL; + int start = 0; + int CurrentBodyLength = 0; + int CurrentType = 0; + int CurrentSequence = 0; + Link_t *TheLink; + CeosRecord_t *record; + int iThisRecord = 0; + + while(max_records != 0 && max_bytes != 0) + { + record = (CeosRecord_t *) CPLMalloc( sizeof( CeosRecord_t ) ); + VSIFSeekL( fp, start, SEEK_SET ); + VSIFReadL( temp_buffer, 1, __CEOS_HEADER_LENGTH, fp ); + record->Length = DetermineCeosRecordBodyLength( temp_buffer ); + + iThisRecord++; + CeosToNative( &(record->Sequence), temp_buffer, 4, 4 ); + + if( iThisRecord != record->Sequence ) + { + if( fileid == __CEOS_IMAGRY_OPT_FILE && iThisRecord == 2 ) + { + CPLDebug( "SAR_CEOS", "Ignoring CEOS file with wrong second record sequence number - likely it has padded records." ); + CPLFree(record); + CPLFree(temp_body); + return CE_Warning; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Corrupt CEOS File - got record seq# %d instead of the expected %d.", + record->Sequence, iThisRecord ); + CPLFree(record); + CPLFree(temp_body); + return CE_Failure; + } + } + + if( record->Length > CurrentBodyLength ) + { + if(CurrentBodyLength == 0 ) + { + temp_body = (unsigned char *) CPLMalloc( record->Length ); + CurrentBodyLength = record->Length; + } + else + { + temp_body = (unsigned char *) + CPLRealloc( temp_body, record->Length ); + CurrentBodyLength = record->Length; + } + } + + VSIFReadL( temp_body, 1, MAX(0,record->Length-__CEOS_HEADER_LENGTH),fp); + + InitCeosRecordWithHeader( record, temp_buffer, temp_body ); + + if( CurrentType == record->TypeCode.Int32Code ) + record->Subsequence = ++CurrentSequence; + else { + CurrentType = record->TypeCode.Int32Code; + record->Subsequence = CurrentSequence = 0; + } + + record->FileId = fileid; + + TheLink = ceos2CreateLink( record ); + + if( sar->RecordList == NULL ) + sar->RecordList = TheLink; + else + sar->RecordList = InsertLink( sar->RecordList, TheLink ); + + start += record->Length; + + if(max_records > 0) + max_records--; + if(max_bytes > 0) + { + if( (vsi_l_offset)record->Length <= max_bytes ) + max_bytes -= record->Length; + else { + CPLDebug( "SAR_CEOS", "Partial record found. %d > " CPL_FRMT_GUIB, + record->Length, max_bytes ); + max_bytes = 0; + } + } + } + + CPLFree(temp_body); + + return CE_None; +} + +/************************************************************************/ +/* GDALRegister_SAR_CEOS() */ +/************************************************************************/ + +void GDALRegister_SAR_CEOS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "SAR_CEOS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SAR_CEOS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "CEOS SAR Image" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#SAR_CEOS" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = SAR_CEOSDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/coasp/GNUmakefile b/bazaar/plugin/gdal/frmts/coasp/GNUmakefile new file mode 100644 index 000000000..44655c238 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/coasp/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = coasp_dataset.o + +CPPFLAGS := $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/coasp/coasp_dataset.cpp b/bazaar/plugin/gdal/frmts/coasp/coasp_dataset.cpp new file mode 100644 index 000000000..fa2db5010 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/coasp/coasp_dataset.cpp @@ -0,0 +1,555 @@ +/****************************************************************************** + * $Id: coasp_dataset.cpp 28831 2015-04-01 16:46:05Z rouault $ + * + * Project: DRDC Configurable Airborne SAR Processor (COASP) data reader + * Purpose: Support in GDAL for the DRDC COASP format data, both Metadata + * and complex imagery. + * Author: Philippe Vachon + * Notes: I have seen a grand total of 2 COASP scenes (3 sets of headers). + * This is based on my best observations, some educated guesses and + * such. So if you have a scene that doesn't work, send it to me + * please and I will make it work... with violence. + * + ****************************************************************************** + * Copyright (c) 2007, Philippe Vachon + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_port.h" +#include "cpl_conv.h" +#include "cpl_vsi.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: coasp_dataset.cpp 28831 2015-04-01 16:46:05Z rouault $"); + +CPL_C_START +void GDALRegister_COASP(void); +CPL_C_END + +#define TYPE_GENERIC 0 +#define TYPE_GEOREF 1 + + + +enum ePolarization { + hh = 0, + hv, + vh, + vv +}; + + +/******************************************************************* + * Declaration of the COASPMetadata classes * + *******************************************************************/ + +class COASPMetadataItem; + +class COASPMetadataReader +{ + VSILFILE *fp; + char **papszMetadata; + int nMetadataCount; + int nCurrentItem; +public: + COASPMetadataReader(char *pszFname); + COASPMetadataItem *GetNextItem(); + COASPMetadataItem *GetItem(int nItem); + int GotoMetadataItem(int nItemNumber); + int GotoMetadataItem(const char *pszName); + int GetCurrentItem() { return nCurrentItem; } +}; + +/* Your average metadata item */ +class COASPMetadataItem +{ +protected: + char *pszItemName; + char *pszItemValue; +public: + COASPMetadataItem() { } + COASPMetadataItem(char *pszItemName, char *pszItemValue); + char *GetItemName(); + char *GetItemValue(); + int GetType() { return TYPE_GENERIC; } +}; + +/* Same as MetadataItem class except parses GCP properly and returns + * a GDAL_GCP struct + */ +class COASPMetadataGeorefGridItem : public COASPMetadataItem +{ + int nId; + int nPixels; + int nLines; + double ndLat; + double ndLong; +public: + COASPMetadataGeorefGridItem(int nId, int nPixels, int nLines, + double ndLat, double ndLong); + const char *GetItemName() { return "georef_grid"; } + GDAL_GCP *GetItemValue(); + int GetType() { return TYPE_GEOREF; } +}; + +/******************************************************************** + * ================================================================ * + * Implementation of the COASPMetadataItem Classes * + * ================================================================ * + ********************************************************************/ + +COASPMetadataItem::COASPMetadataItem(char *pszItemName, char *pszItemValue) +{ + this->pszItemName = VSIStrdup(pszItemName); + this->pszItemValue = VSIStrdup(pszItemValue); +} + +char *COASPMetadataItem::GetItemName() +{ + return VSIStrdup(pszItemName); +} + +char *COASPMetadataItem::GetItemValue() +{ + return VSIStrdup(pszItemValue); +} + +COASPMetadataGeorefGridItem::COASPMetadataGeorefGridItem(int nId, int nPixels, + int nLines, double ndLat, double ndLong) +{ + this->nId = nId; + this->nPixels = nPixels; + this->nLines = nLines; + this->ndLat = ndLat; + this->ndLong = ndLong; + this->pszItemName = VSIStrdup("georef_grid"); + +} + +GDAL_GCP *COASPMetadataGeorefGridItem::GetItemValue() +{ + return NULL; +} + + +/******************************************************************** + * ================================================================ * + * Implementation of the COASPMetadataReader Class * + * ================================================================ * + ********************************************************************/ + +COASPMetadataReader::COASPMetadataReader(char *pszFname) +{ + this->fp = NULL; + this->nCurrentItem = 0; + this->papszMetadata = CSLLoad(pszFname); + this->nMetadataCount = CSLCount(this->papszMetadata); +} + +COASPMetadataItem *COASPMetadataReader::GetNextItem() +{ + COASPMetadataItem *poMetadata; + char **papszMDTokens; + char *pszItemName; + char *pszItemValue; + if (nCurrentItem >= nMetadataCount) + return NULL; + + + papszMDTokens = CSLTokenizeString2(papszMetadata[nCurrentItem], " ", + CSLT_HONOURSTRINGS ); + pszItemName = papszMDTokens[0]; + if (EQUALN(pszItemName, "georef_grid", 11)) { + double ndLat, ndLong; + int nPixels, nLines; + // georef_grid ( pixels lines ) ( lat long ) + // 0 1 2 3 4 5 6 7 8 + nPixels = atoi(papszMDTokens[2]); + nLines = atoi(papszMDTokens[3]); + ndLat = CPLAtof(papszMDTokens[6]); + ndLong = CPLAtof(papszMDTokens[7]); + poMetadata = new COASPMetadataGeorefGridItem(nCurrentItem, nPixels, + nLines, ndLat, ndLong); + } + else { + int nCount = CSLCount(papszMDTokens); + pszItemValue = CPLStrdup(papszMDTokens[1]); + for (int i = 2; i < nCount; i++) { + int nSize = strlen(papszMDTokens[i]); + pszItemValue = (char *)CPLRealloc(pszItemValue, + strlen(pszItemValue) + 1 + nSize); + sprintf(pszItemValue,"%s %s",pszItemValue, + papszMDTokens[i]); + } + + poMetadata = new COASPMetadataItem(pszItemName, + pszItemValue); + + CPLFree(pszItemValue); + } + CSLDestroy(papszMDTokens); + nCurrentItem++; + return poMetadata; +} + +/* Goto a particular metadata item, listed by number */ +int COASPMetadataReader::GotoMetadataItem(int nItemNumber) +{ + if (nItemNumber > nMetadataCount || nItemNumber < 0) { + nItemNumber = 0; + return 0; + } + nCurrentItem = nItemNumber; + return nCurrentItem; +} + +/* Goto the first metadata item with a particular name */ +int COASPMetadataReader::GotoMetadataItem(const char *pszName) +{ + nCurrentItem = CSLPartialFindString(papszMetadata, pszName); + return nCurrentItem; +} + +/******************************************************************* + * Declaration of the COASPDataset class * + *******************************************************************/ + + +class COASPRasterBand; + +/* A couple of observations based on the data I have available to me: + * a) the headers don't really change, beyond indicating data sources + * and such. As such, I only read the first header specified by the + * user. Note that this is agnostic: you can specify hh, vv, vh, hv and + * all the data needed will be immediately available. + * b) Lots of GCPs are present in the headers. This is most excellent. + * c) There is no documentation on this format. All the knowledge contained + * herein is from harassing various Defence Scientists at DRDC Ottawa. + */ + +class COASPDataset : public GDALDataset +{ + friend class COASPRasterBand; + VSILFILE *fpHdr; /* File pointer for the header file */ + VSILFILE *fpBinHH; /* File pointer for the binary matrix */ + VSILFILE *fpBinHV; + VSILFILE *fpBinVH; + VSILFILE *fpBinVV; + + char *pszFileName; /* line and mission ID, mostly, i.e. l27p7 */ + + int nGCPCount; + GDAL_GCP *pasGCP; +public: + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * poOpenInfo ); + int GetGCPCount(); + const GDAL_GCP *GetGCPs(); +}; + +/******************************************************************** + * ================================================================ * + * Declaration and implementation of the COASPRasterBand Class * + * ================================================================ * + ********************************************************************/ + +class COASPRasterBand : public GDALRasterBand { + VSILFILE *fp; + int ePol; +public: + COASPRasterBand( COASPDataset *poDS, GDALDataType eDataType, int ePol, VSILFILE *fp ); + virtual CPLErr IReadBlock( int nBlockXOff, int nBlockYOff, + void *pImage); +}; + +COASPRasterBand::COASPRasterBand( COASPDataset *poDS, GDALDataType eDataType, + int ePol, VSILFILE *fp) +{ + this->fp = fp; + this->ePol = ePol; + this->poDS = poDS; + this->eDataType = eDataType; + this->nBlockXSize = poDS->GetRasterXSize(); + this->nBlockYSize = 1; +} + +CPLErr COASPRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void *pImage ) +{ + if (this->fp == NULL) { + CPLError(CE_Fatal, 1, "file pointer freed unexpectedly\n"); + return CE_Fatal; + } + + /* 8 bytes per pixel: 4 bytes I, 4 bytes Q */ + unsigned long nByteNum = poDS->GetRasterXSize() * 8 * nBlockYOff; + + VSIFSeekL(this->fp, nByteNum, SEEK_SET); + int nReadSize = (GDALGetDataTypeSize(eDataType)/8) * poDS->GetRasterXSize(); + VSIFReadL((char *)pImage, 1, nReadSize, + this->fp); + +#ifdef CPL_LSB + GDALSwapWords( pImage, 4, nBlockXSize * 2, 4 ); +#endif + + + return CE_None; + +} + + +/******************************************************************** + * ================================================================ * + * Implementation of the COASPDataset Class * + * ================================================================ * + ********************************************************************/ + + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int COASPDataset::GetGCPCount() +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *COASPDataset::GetGCPs() +{ + return pasGCP; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int COASPDataset::Identify( GDALOpenInfo *poOpenInfo ) +{ + if(poOpenInfo->fpL == NULL || poOpenInfo->nHeaderBytes < 256) + return 0; + + /* With a COASP .hdr file, the first line or so is: + * time_first_datarec + */ + if(EQUALN((char *)poOpenInfo->pabyHeader,"time_first_datarec",18)) + return 1; + + return 0; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *COASPDataset::Open( GDALOpenInfo *poOpenInfo ) +{ + if (!COASPDataset::Identify(poOpenInfo)) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The COASP driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + /* Create a fresh dataset for us to work with */ + COASPDataset *poDS; + poDS = new COASPDataset(); + + if (poDS == NULL) + return NULL; + + /* Steal the file pointer for the header */ + poDS->fpHdr = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + + /* Set the binary matrix file pointers to NULL, for now */ + poDS->fpBinHH = NULL; + poDS->fpBinHV = NULL; + poDS->fpBinVH = NULL; + poDS->fpBinVV = NULL; + + poDS->pszFileName = VSIStrdup(poOpenInfo->pszFilename); + + /* determine the file name prefix */ + const char *pszFilename; + char *pszBaseName = VSIStrdup(CPLGetBasename(poDS->pszFileName)); + char *pszDir = VSIStrdup(CPLGetPath(poDS->pszFileName)); + const char *pszExt = "rc"; + int nNull = strlen(pszBaseName) - 1; + char *pszBase = (char *)CPLMalloc(nNull); + strncpy(pszBase, pszBaseName, nNull); + pszBase[nNull - 1] = '\0'; + free(pszBaseName); + + char *psChan = strstr(pszBase,"hh");; + if (psChan == NULL) { + psChan = strstr(pszBase, "hv"); + } + if (psChan == NULL) { + psChan = strstr(pszBase, "vh"); + } + if (psChan == NULL) { + psChan = strstr(pszBase, "vv"); + } + + if (psChan == NULL) { + CPLError(CE_Fatal, 1, "unable to recognize file as COASP.\n"); + free(poDS->pszFileName); + free(pszBase); + free(pszDir); + delete poDS; + return NULL; + } + + /* Read Metadata, set GCPs as is appropriate */ + COASPMetadataReader *poReader = new COASPMetadataReader( + poDS->pszFileName); + + + /* Get Image X and Y widths */ + poReader->GotoMetadataItem("number_lines"); + COASPMetadataItem *poItem = poReader->GetNextItem(); + char *nValue = poItem->GetItemValue(); + poDS->nRasterYSize = atoi(nValue); + free(nValue); + + poReader->GotoMetadataItem("number_samples"); + poItem = poReader->GetNextItem(); + nValue = poItem->GetItemValue(); + poDS->nRasterXSize = atoi(nValue); + free(nValue); + + + /* Horizontal transmit, horizontal receive */ + psChan[0] = 'h'; + psChan[1] = 'h'; + pszFilename = CPLFormFilename(pszDir, pszBase, pszExt); + + poDS->fpBinHH = VSIFOpenL(pszFilename, "r"); + + if (poDS->fpBinHH != 0) { + /* Set raster band */ + poDS->SetBand(1, new COASPRasterBand(poDS, GDT_CFloat32, + hh , poDS->fpBinHH)); + } + + /* Horizontal transmit, vertical receive */ + psChan[0] = 'h'; + psChan[1] = 'v'; + pszFilename = CPLFormFilename(pszDir, pszBase, pszExt); + + poDS->fpBinHV = VSIFOpenL(pszFilename, "r"); + + if (poDS->fpBinHV != 0) { + poDS->SetBand(2, new COASPRasterBand(poDS, GDT_CFloat32, + hv, poDS->fpBinHV)); + } + + /* Vertical transmit, horizontal receive */ + psChan[0] = 'v'; + psChan[1] = 'h'; + pszFilename = CPLFormFilename(pszDir, pszBase, pszExt); + + poDS->fpBinVH = VSIFOpenL(pszFilename, "r"); + + if (poDS->fpBinVH != 0) { + poDS->SetBand(3, new COASPRasterBand(poDS, GDT_CFloat32, + vh, poDS->fpBinVH)); + } + + /* Vertical transmit, vertical receive */ + psChan[0] = 'v'; + psChan[1] = 'v'; + pszFilename = CPLFormFilename(pszDir, pszBase, pszExt); + + poDS->fpBinVV = VSIFOpenL(pszFilename, "r"); + + if (poDS->fpBinVV != 0) { + poDS->SetBand(4, new COASPRasterBand(poDS, GDT_CFloat32, + vv, poDS->fpBinVV)); + } + + + /* Oops, missing all the data? */ + + if (poDS->fpBinHH == NULL && poDS->fpBinHV == NULL + && poDS->fpBinVH == NULL && poDS->fpBinVV == NULL) + { + CPLError(CE_Fatal,1,"Unable to find any data! Aborting."); + free(pszBase); + free(pszDir); + delete poDS; + + return NULL; + } + + if ( poDS->GetRasterCount() == 4 ) { + poDS->SetMetadataItem( "MATRIX_REPRESENTATION", "SCATTERING" ); + } + + free(pszBase); + free(pszDir); + + poDS->nGCPCount = 0; + poDS->pasGCP = NULL; + + delete poItem; + delete poReader; + + return poDS; + +} + +/************************************************************************/ +/* GDALRegister_COASP() */ +/************************************************************************/ + +void GDALRegister_COASP(void) +{ + GDALDriver *poDriver; + if ( GDALGetDriverByName( "COASP" ) == NULL ) { + poDriver = new GDALDriver(); + poDriver->SetDescription( "COASP" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "DRDC COASP SAR Processor Raster" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, + "hdr" ); +/* poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_coasp.html"); */ + poDriver->pfnIdentify = COASPDataset::Identify; + poDriver->pfnOpen = COASPDataset::Open; + GetGDALDriverManager()->RegisterDriver( poDriver ); + } + +} diff --git a/bazaar/plugin/gdal/frmts/coasp/makefile.vc b/bazaar/plugin/gdal/frmts/coasp/makefile.vc new file mode 100644 index 000000000..d0d3d95c6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/coasp/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = coasp_dataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/cosar/GNUmakefile b/bazaar/plugin/gdal/frmts/cosar/GNUmakefile new file mode 100644 index 000000000..0f4e20f15 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/cosar/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = cosar_dataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/cosar/cosar_dataset.cpp b/bazaar/plugin/gdal/frmts/cosar/cosar_dataset.cpp new file mode 100644 index 000000000..1cfc06948 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/cosar/cosar_dataset.cpp @@ -0,0 +1,220 @@ +/* TerraSAR-X COSAR Format Driver + * (C)2007 Philippe P. Vachon + * --------------------------------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + + +#include "gdal_priv.h" +#include "cpl_port.h" +#include "cpl_conv.h" +#include "cpl_vsi.h" +#include "cpl_string.h" +#include + +/* Various offsets, in bytes */ +#define BIB_OFFSET 0 /* Bytes in burst, valid only for ScanSAR */ +#define RSRI_OFFSET 4 /* Range Sample Relative Index */ +#define RS_OFFSET 8 /* Range Samples, the length of a range line */ +#define AS_OFFSET 12 /* Azimuth Samples, the length of an azimth column */ +#define BI_OFFSET 16 /* Burst Index, the index number of the burst */ +#define RTNB_OFFSET 20 /* Rangeline total number of bytes, incl. annot. */ +#define TNL_OFFSET 24 /* Total Number of Lines */ +#define MAGIC1_OFFSET 28 /* Magic number 1: 0x43534152 */ +#define MAGIC2_OFFSET 32 /* Magic number 2: Version number */ + +#define COSAR_MAGIC 0x43534152 /* String CSAR */ +#define FILLER_MAGIC 0x7F7F7F7F /* Filler value, we'll use this for a test */ + +CPL_C_START +void GDALRegister_COSAR(void); +CPL_C_END + +class COSARDataset : public GDALDataset +{ + long nSize; +public: + VSILFILE *fp; +/* ~COSARDataset(); */ + + static GDALDataset *Open( GDALOpenInfo * ); + long GetSizeInBytes() { return nSize; } +}; + +class COSARRasterBand : public GDALRasterBand +{ + unsigned long nRTNB; + int nBurstNumber; +public: + COSARRasterBand(COSARDataset *, unsigned long nRTNB); + virtual CPLErr IReadBlock(int, int, void *); +}; + +/***************************************************************************** + * COSARRasterBand Implementation + *****************************************************************************/ + +COSARRasterBand::COSARRasterBand(COSARDataset *pDS, unsigned long nRTNB) { + this->nRTNB = nRTNB; + nBurstNumber = 1; + nBlockXSize = pDS->GetRasterXSize(); + nBlockYSize = 1; + eDataType = GDT_CInt16; +} + +CPLErr COSARRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void *pImage) { + + unsigned long nRSFV = 0; + unsigned long nRSLV = 0; + COSARDataset *pCDS = (COSARDataset *) poDS; + + /* Find the line we want to be at */ + /* To explain some magic numbers: + * 4 bytes for an entire sample (2 I, 2 Q) + * nBlockYOff + 4 = Y offset + 4 annotation lines at beginning + * of file + */ + + VSIFSeekL(pCDS->fp,(this->nRTNB * (nBlockYOff + 4)), SEEK_SET); + + + /* Read RSFV and RSLV (TX-GS-DD-3307) */ + VSIFReadL(&nRSFV,1,4,pCDS->fp); + VSIFReadL(&nRSLV,1,4,pCDS->fp); + +#ifdef CPL_LSB + nRSFV = CPL_SWAP32(nRSFV); + nRSLV = CPL_SWAP32(nRSLV); +#endif + + if (nRSLV < nRSFV || nRSFV == 0 + || nRSFV - 1 >= ((unsigned long) nBlockXSize) + || nRSLV - nRSFV > ((unsigned long) nBlockXSize) + || nRSFV >= this->nRTNB || nRSLV > this->nRTNB) + { + /* throw an error */ + CPLError(CE_Failure, CPLE_AppDefined, + "RSLV/RSFV values are not sane... oh dear.\n"); + return CE_Failure; + } + + + /* zero out the range line */ + for (int i = 0; i < this->nRasterXSize; i++) + { + ((GUInt32 *)pImage)[i] = 0; + } + + /* properly account for validity mask */ + if (nRSFV > 1) + { + VSIFSeekL(pCDS->fp,(this->nRTNB*(nBlockYOff+4)+(nRSFV+1)*4), SEEK_SET); + } + + /* Read the valid samples: */ + VSIFReadL(((char *)pImage)+((nRSFV - 1)*4),1,((nRSLV-1)*4)-((nRSFV-1)*4),pCDS->fp); + +#ifdef CPL_LSB + // GDALSwapWords( pImage, 4, nBlockXSize * nBlockYSize, 4 ); + GDALSwapWords( pImage, 2, nBlockXSize * nBlockYSize * 2, 2 ); +#endif + + + return CE_None; +} + + +/***************************************************************************** + * COSARDataset Implementation + *****************************************************************************/ + +GDALDataset *COSARDataset::Open( GDALOpenInfo * pOpenInfo ) { + long nRTNB; + /* Check if we're actually a COSAR data set. */ + if( pOpenInfo->nHeaderBytes < 4 ) + return NULL; + + if (!EQUALN((char *)pOpenInfo->pabyHeader+MAGIC1_OFFSET, "CSAR",4)) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( pOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The COSAR driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + /* this is a cosar dataset */ + COSARDataset *pDS; + pDS = new COSARDataset(); + + /* steal fp */ + pDS->fp = pOpenInfo->fpL; + pOpenInfo->fpL = NULL; + + /* Calculate the file size */ + VSIFSeekL(pDS->fp,0,SEEK_END); + pDS->nSize = VSIFTellL(pDS->fp); + + VSIFSeekL(pDS->fp, RS_OFFSET, SEEK_SET); + VSIFReadL(&pDS->nRasterXSize, 1, 4, pDS->fp); +#ifdef CPL_LSB + pDS->nRasterXSize = CPL_SWAP32(pDS->nRasterXSize); +#endif + + + VSIFReadL(&pDS->nRasterYSize, 1, 4, pDS->fp); +#ifdef CPL_LSB + pDS->nRasterYSize = CPL_SWAP32(pDS->nRasterYSize); +#endif + + VSIFSeekL(pDS->fp, RTNB_OFFSET, SEEK_SET); + VSIFReadL(&nRTNB, 1, 4, pDS->fp); +#ifdef CPL_LSB + nRTNB = CPL_SWAP32(nRTNB); +#endif + + /* Add raster band */ + pDS->SetBand(1, new COSARRasterBand(pDS, nRTNB)); + return pDS; +} + + +/* register the driver with GDAL */ +void GDALRegister_COSAR() { + GDALDriver *poDriver; + if (GDALGetDriverByName("cosar") == NULL) { + poDriver = new GDALDriver(); + poDriver->SetDescription("COSAR"); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "COSAR Annotated Binary Matrix (TerraSAR-X)"); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_cosar.html"); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->pfnOpen = COSARDataset::Open; + GetGDALDriverManager()->RegisterDriver(poDriver); + } +} diff --git a/bazaar/plugin/gdal/frmts/cosar/frmt_cosar.html b/bazaar/plugin/gdal/frmts/cosar/frmt_cosar.html new file mode 100644 index 000000000..5d09e2d56 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/cosar/frmt_cosar.html @@ -0,0 +1,30 @@ + + +COSAR -- TerraSAR-X Complex SAR Data Product + + + + +

COSAR -- TerraSAR-X Complex SAR Data Product

+ +

This driver provides the capability to read TerraSAR-X complex data. While +most users will receive products in GeoTIFF format (representing detected +radiation reflected from the targets, or geocoded data), ScanSAR products will be distributed in COSAR format.

+ +

Essentially, COSAR is an annotated binary matrix, with each sample held +in 4 bytes (16 bytes real, 16 bytes imaginary) stored with the most significant +byte first (Big Endian). Within a COSAR container there are one or more +"bursts" which represent individual ScanSAR bursts. Note that if a Stripmap or +Spotlight product is held in a COSAR container it is stored in a single +burst.

+ +

Support for ScanSAR data is currently under way, due to the difficulties in fitting +the ScanSAR "burst" identifiers into the GDAL model.

+ +See Also: +
    +
  • DLR Document TX-GS-DD-3307 "Level 1b Product Format Specification."
  • +
+ + + diff --git a/bazaar/plugin/gdal/frmts/cosar/makefile.vc b/bazaar/plugin/gdal/frmts/cosar/makefile.vc new file mode 100644 index 000000000..45537b423 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/cosar/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = cosar_dataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/ctg/GNUmakefile b/bazaar/plugin/gdal/frmts/ctg/GNUmakefile new file mode 100644 index 000000000..8f9185cfc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ctg/GNUmakefile @@ -0,0 +1,15 @@ + +include ../../GDALmake.opt + +OBJ = ctgdataset.o + +CPPFLAGS := $(CPPFLAGS) $(XTRA_OPT) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/ctg/ctgdataset.cpp b/bazaar/plugin/gdal/frmts/ctg/ctgdataset.cpp new file mode 100644 index 000000000..b13d26f99 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ctg/ctgdataset.cpp @@ -0,0 +1,608 @@ +/****************************************************************************** + * $Id: ctgdataset.cpp 28039 2014-11-30 18:24:59Z rouault $ + * + * Project: CTG driver + * Purpose: GDALDataset driver for CTG dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: ctgdataset.cpp 28039 2014-11-30 18:24:59Z rouault $"); + +CPL_C_START +void GDALRegister_CTG(void); +CPL_C_END + +#define HEADER_LINE_COUNT 5 + +typedef struct +{ + int nCode; + const char* pszDesc; +} LULCDescStruct; + +static const LULCDescStruct asLULCDesc[] = +{ + {1, "Urban or Built-Up Land" }, + {2, "Agricultural Land" }, + {3, "Rangeland" }, + {4, "Forest Land" }, + {5, "Water" }, + {6, "Wetland" }, + {7, "Barren Land" }, + {8, "Tundra" }, + {9, "Perennial Snow and Ice" }, + {11, "Residential" }, + {12, "Commercial Services" }, + {13, "Industrial" }, + {14, "Transportation, Communications" }, + {15, "Industrial and Commercial" }, + {16, "Mixed Urban or Built-Up Land" }, + {17, "Other Urban or Built-Up Land" }, + {21, "Cropland and Pasture" }, + {22, "Orchards, Groves, Vineyards, Nurseries" }, + {23, "Confined Feeding Operations" }, + {24, "Other Agricultural Land" }, + {31, "Herbaceous Rangeland" }, + {32, "Shrub and Brush Rangeland" }, + {33, "Mixed Rangeland" }, + {41, "Deciduous Forest Land" }, + {42, "Evergreen Forest Land" }, + {43, "Mixed Forest Land" }, + {51, "Streams and Canals" }, + {52, "Lakes" }, + {53, "Reservoirs" }, + {54, "Bays and Estuaries" }, + {61, "Forested Wetlands" }, + {62, "Nonforested Wetlands" }, + {71, "Dry Salt Flats" }, + {72, "Beaches" }, + {73, "Sandy Areas Other than Beaches" }, + {74, "Bare Exposed Rock" }, + {75, "Strip Mines, Quarries, and Gravel Pits" }, + {76, "Transitional Areas" }, + {77, "Mixed Barren Land" }, + {81, "Shrub and Brush Tundra" }, + {82, "Herbaceous Tundra" }, + {83, "Bare Ground" }, + {84, "Wet Tundra" }, + {85, "Mixed Tundra" }, + {91, "Perennial Snowfields" }, + {92, "Glaciers" } +}; + +static const char* apszBandDescription[] = +{ + "Land Use and Land Cover", + "Political units", + "Census county subdivisions and SMSA tracts", + "Hydrologic units", + "Federal land ownership", + "State land ownership" +}; + +/************************************************************************/ +/* ==================================================================== */ +/* CTGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class CTGRasterBand; + +class CTGDataset : public GDALPamDataset +{ + friend class CTGRasterBand; + + VSILFILE *fp; + + int nNWEasting, nNWNorthing, nCellSize, nUTMZone; + char *pszProjection; + + int bHasReadImagery; + GByte *pabyImage; + + int ReadImagery(); + + static const char* ExtractField(char* szOutput, const char* pszBuffer, + int nOffset, int nLength); + + public: + CTGDataset(); + virtual ~CTGDataset(); + + virtual CPLErr GetGeoTransform( double * ); + virtual const char* GetProjectionRef(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* CTGRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class CTGRasterBand : public GDALPamRasterBand +{ + friend class CTGDataset; + + char** papszCategories; + + public: + + CTGRasterBand( CTGDataset *, int ); + ~CTGRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual char **GetCategoryNames(); +}; + + +/************************************************************************/ +/* CTGRasterBand() */ +/************************************************************************/ + +CTGRasterBand::CTGRasterBand( CTGDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Int32; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = poDS->GetRasterYSize(); + + papszCategories = NULL; +} + +/************************************************************************/ +/* ~CTGRasterBand() */ +/************************************************************************/ + +CTGRasterBand::~CTGRasterBand() + +{ + CSLDestroy(papszCategories); +} +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr CTGRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + CPL_UNUSED int nBlockYOff, + void * pImage ) +{ + CTGDataset* poGDS = (CTGDataset* ) poDS; + + poGDS->ReadImagery(); + memcpy(pImage, + poGDS->pabyImage + (nBand - 1) * nBlockXSize * nBlockYSize * sizeof(int), + nBlockXSize * nBlockYSize * sizeof(int)); + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double CTGRasterBand::GetNoDataValue( int *pbSuccess ) +{ + if (pbSuccess) + *pbSuccess = TRUE; + + return 0.; +} + +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +char **CTGRasterBand::GetCategoryNames() +{ + if (nBand != 1) + return NULL; + + if (papszCategories != NULL) + return papszCategories; + + int i; + int nasLULCDescSize = (int)(sizeof(asLULCDesc) / sizeof(asLULCDesc[0])); + int nCategoriesSize = asLULCDesc[nasLULCDescSize - 1].nCode; + papszCategories = (char**)CPLCalloc(nCategoriesSize + 2, sizeof(char*)); + for(i=0;i= nRasterXSize || nCellY >= nRasterYSize) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Read error at line %d, %s. Unexpected cell coordinates", + nLine, szLine); + return FALSE; + } + int i; + for(i=0;i<6;i++) + { + int nVal = atoi(ExtractField(szField, szLine, 20 + 10*i, 10)); + if (nVal >= 2000000000) + nVal = 0; + ((int*)pabyImage)[i * nCells + nCellY * nRasterXSize + nCellX] = nVal; + } + + nLine ++; + } + + return TRUE; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int CTGDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + CPLString osFilename(poOpenInfo->pszFilename); + + GDALOpenInfo* poOpenInfoToDelete = NULL; + /* GZipped grid_cell.gz files are common, so automagically open them */ + /* if the /vsigzip/ has not been explicitly passed */ + const char* pszFilename = CPLGetFilename(poOpenInfo->pszFilename); + if ((EQUAL(pszFilename, "grid_cell.gz") || + EQUAL(pszFilename, "grid_cell1.gz") || + EQUAL(pszFilename, "grid_cell2.gz")) && + !EQUALN(poOpenInfo->pszFilename, "/vsigzip/", 9)) + { + osFilename = "/vsigzip/"; + osFilename += poOpenInfo->pszFilename; + poOpenInfo = poOpenInfoToDelete = + new GDALOpenInfo(osFilename.c_str(), GA_ReadOnly, + poOpenInfo->GetSiblingFiles()); + } + + if (poOpenInfo->nHeaderBytes < HEADER_LINE_COUNT * 80) + { + delete poOpenInfoToDelete; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Chech that it looks roughly as a CTG dataset */ +/* -------------------------------------------------------------------- */ + const char* pszData = (const char*)poOpenInfo->pabyHeader; + int i; + for(i=0;i<4 * 80;i++) + { + if (!((pszData[i] >= '0' && pszData[i] <= '9') || + pszData[i] == ' ' || pszData[i] == '-')) + { + delete poOpenInfoToDelete; + return FALSE; + } + } + + char szField[11]; + int nRows = atoi(ExtractField(szField, pszData, 0, 10)); + int nCols = atoi(ExtractField(szField, pszData, 20, 10)); + int nMinColIndex = atoi(ExtractField(szField, pszData+80, 0, 5)); + int nMinRowIndex = atoi(ExtractField(szField, pszData+80, 5, 5)); + int nMaxColIndex = atoi(ExtractField(szField, pszData+80, 10, 5)); + int nMaxRowIndex = atoi(ExtractField(szField, pszData+80, 15, 5)); + + if (nRows <= 0 || nCols <= 0 || + nMinColIndex != 1 || nMinRowIndex != 1 || + nMaxRowIndex != nRows || nMaxColIndex != nCols) + { + delete poOpenInfoToDelete; + return FALSE; + } + + delete poOpenInfoToDelete; + return TRUE; +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *CTGDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i; + + if (!Identify(poOpenInfo)) + return NULL; + + CPLString osFilename(poOpenInfo->pszFilename); + + /* GZipped grid_cell.gz files are common, so automagically open them */ + /* if the /vsigzip/ has not been explicitly passed */ + const char* pszFilename = CPLGetFilename(poOpenInfo->pszFilename); + if ((EQUAL(pszFilename, "grid_cell.gz") || + EQUAL(pszFilename, "grid_cell1.gz") || + EQUAL(pszFilename, "grid_cell2.gz")) && + !EQUALN(poOpenInfo->pszFilename, "/vsigzip/", 9)) + { + osFilename = "/vsigzip/"; + osFilename += poOpenInfo->pszFilename; + } + + if (poOpenInfo->eAccess == GA_Update) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The CTG driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Find dataset characteristics */ +/* -------------------------------------------------------------------- */ + VSILFILE* fp = VSIFOpenL(osFilename.c_str(), "rb"); + if (fp == NULL) + return NULL; + + char szHeader[HEADER_LINE_COUNT * 80+1]; + szHeader[HEADER_LINE_COUNT * 80] = 0; + if (VSIFReadL(szHeader, 1, HEADER_LINE_COUNT * 80, fp) != HEADER_LINE_COUNT * 80) + { + VSIFCloseL(fp); + return NULL; + } + + for(i=HEADER_LINE_COUNT * 80 - 1;i>=0;i--) + { + if (szHeader[i] == ' ') + szHeader[i] = 0; + else + break; + } + + char szField[11]; + int nRows = atoi(ExtractField(szField, szHeader, 0, 10)); + int nCols = atoi(ExtractField(szField, szHeader, 20, 10)); + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + CTGDataset *poDS; + + poDS = new CTGDataset(); + poDS->fp = fp; + fp = NULL; + poDS->nRasterXSize = nCols; + poDS->nRasterYSize = nRows; + + poDS->SetMetadataItem("TITLE", szHeader + 4 * 80); + + poDS->nCellSize = atoi(ExtractField(szField, szHeader, 35, 5)); + if (poDS->nCellSize <= 0 || poDS->nCellSize >= 10000) + { + delete poDS; + return NULL; + } + poDS->nNWEasting = atoi(ExtractField(szField, szHeader + 3*80, 40, 10)); + poDS->nNWNorthing = atoi(ExtractField(szField, szHeader + 3*80, 50, 10)); + poDS->nUTMZone = atoi(ExtractField(szField, szHeader, 50, 5)); + if (poDS->nUTMZone <= 0 || poDS->nUTMZone > 60) + { + delete poDS; + return NULL; + } + + OGRSpatialReference oSRS; + oSRS.importFromEPSG(32600 + poDS->nUTMZone); + oSRS.exportToWkt(&poDS->pszProjection); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the imagery */ +/* -------------------------------------------------------------------- */ + GByte* pabyImage = (GByte*)VSICalloc(nCols * nRows, 6 * sizeof(int)); + if (pabyImage == NULL) + { + delete poDS; + return NULL; + } + poDS->pabyImage = pabyImage; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = 6; + for( i = 0; i < poDS->nBands; i++ ) + { + poDS->SetBand( i+1, new CTGRasterBand( poDS, i+1 ) ); + poDS->GetRasterBand(i+1)->SetDescription(apszBandDescription[i]); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr CTGDataset::GetGeoTransform( double * padfTransform ) + +{ + padfTransform[0] = nNWEasting - nCellSize / 2; + padfTransform[1] = nCellSize; + padfTransform[2] = 0; + padfTransform[3] = nNWNorthing + nCellSize / 2; + padfTransform[4] = 0.; + padfTransform[5] = -nCellSize; + + return( CE_None ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* CTGDataset::GetProjectionRef() + +{ + return pszProjection; +} + +/************************************************************************/ +/* GDALRegister_CTG() */ +/************************************************************************/ + +void GDALRegister_CTG() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "CTG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "CTG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "USGS LULC Composite Theme Grid" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#CTG" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = CTGDataset::Open; + poDriver->pfnIdentify = CTGDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/ctg/makefile.vc b/bazaar/plugin/gdal/frmts/ctg/makefile.vc new file mode 100644 index 000000000..efc3d2a59 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ctg/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = ctgdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/dds/GNUmakefile b/bazaar/plugin/gdal/frmts/dds/GNUmakefile new file mode 100644 index 000000000..c432a2179 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dds/GNUmakefile @@ -0,0 +1,24 @@ +# $Id: GNUmakefile 16755 2009-04-09 20:38:33Z chaitanya $ +# +# Makefile to build dds using GNU Make and GCC. +# + +include ../../GDALmake.opt + +OBJ = ddsdataset.o + +CRUNCHINC = -I$(CRUNCHDIR)/include/crunch + +CPPFLAGS := $(XTRA_OPT) $(CRUNCHINC) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +../o/%.$(OBJ_EXT): + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/dds/ddsdataset.cpp b/bazaar/plugin/gdal/frmts/dds/ddsdataset.cpp new file mode 100644 index 000000000..5ea452b49 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dds/ddsdataset.cpp @@ -0,0 +1,376 @@ +/****************************************************************************** + * $Id: $ + * + * Project: DDS Driver + * Purpose: Implement GDAL DDS Support + * Author: Alan Boudreault, aboudreault@mapgears.com + * + ****************************************************************************** + * Copyright (c) 2013, Alan Boudreault + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * THE CURRENT IMPLEMENTATION IS WRITE ONLY. + * + */ + +#include "gdal_pam.h" +#include "crnlib.h" +#include "dds_defs.h" + +CPL_CVSID("$Id: $"); + +CPL_C_START +void GDALRegister_DDS(void); +CPL_C_END + +using namespace crnlib; + +enum { DDS_COLOR_TYPE_RGB, + DDS_COLOR_TYPE_RGB_ALPHA }; + + +/************************************************************************/ +/* ==================================================================== */ +/* DDSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class DDSDataset : public GDALPamDataset +{ +public: + static GDALDataset* CreateCopy(const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData); +}; + + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * +DDSDataset::CreateCopy(const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData) + +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + + /* -------------------------------------------------------------------- */ + /* Some rudimentary checks */ + /* -------------------------------------------------------------------- */ + if (nBands != 3 && nBands != 4) + { + CPLError(CE_Failure, CPLE_NotSupported, + "DDS driver doesn't support %d bands. Must be 3 (rgb) \n" + "or 4 (rgba) bands.\n", + nBands); + + return NULL; + } + + if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "DDS driver doesn't support data type %s. " + "Only eight bit (Byte) bands supported. %s\n", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()), + (bStrict) ? "" : "Defaulting to Byte" ); + + if (bStrict) + return NULL; + } + + /* -------------------------------------------------------------------- */ + /* Setup some parameters. */ + /* -------------------------------------------------------------------- */ + int nColorType = 0; + + if (nBands == 3) + nColorType = DDS_COLOR_TYPE_RGB; + else if (nBands == 4) + nColorType = DDS_COLOR_TYPE_RGB_ALPHA; + + /* -------------------------------------------------------------------- */ + /* Create the dataset. */ + /* -------------------------------------------------------------------- */ + VSILFILE *fpImage; + + fpImage = VSIFOpenL(pszFilename, "wb"); + if (fpImage == NULL) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Unable to create dds file %s.\n", + pszFilename); + return NULL; + } + + /* -------------------------------------------------------------------- */ + /* Create the Crunch compressor */ + /* -------------------------------------------------------------------- */ + + /* Default values */ + crn_format fmt = cCRNFmtDXT3; + const uint cDXTBlockSize = 4; + crn_dxt_quality dxt_quality = cCRNDXTQualityNormal; + bool srgb_colorspace = true; + bool dxt1a_transparency = true; + //bool generate_mipmaps = true; + + /* Check the texture format */ + const char *pszFormat = CSLFetchNameValue( papszOptions, "FORMAT" ); + + if (pszFormat) + { + if (EQUAL(pszFormat, "dxt1")) + fmt = cCRNFmtDXT1; + else if (EQUAL(pszFormat, "dxt1a")) + fmt = cCRNFmtDXT1; + else if (EQUAL(pszFormat, "dxt3")) + fmt = cCRNFmtDXT3; + else if (EQUAL(pszFormat, "dxt5")) + fmt = cCRNFmtDXT5; + else if (EQUAL(pszFormat, "etc1")) + fmt = cCRNFmtETC1; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Illegal FORMAT value '%s', should be DXT1, DXT1A, DXT3, DXT5 or ETC1", + pszFormat ); + return NULL; + } + } + + /* Check the compression quality */ + const char *pszQuality = CSLFetchNameValue( papszOptions, "QUALITY" ); + + if (pszQuality) + { + if (EQUAL(pszQuality, "SUPERFAST")) + dxt_quality = cCRNDXTQualitySuperFast; + else if (EQUAL(pszQuality, "FAST")) + dxt_quality = cCRNDXTQualityFast; + else if (EQUAL(pszQuality, "NORMAL")) + dxt_quality = cCRNDXTQualityNormal; + else if (EQUAL(pszQuality, "BETTER")) + dxt_quality = cCRNDXTQualityBetter; + else if (EQUAL(pszQuality, "UBER")) + dxt_quality = cCRNDXTQualityUber; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Illegal QUALITY value '%s', should be SUPERFAST, FAST, NORMAL, BETTER or UBER.", + pszQuality ); + return NULL; + } + } + + if ((nXSize%4!=0) || (nYSize%4!=0)) { + CPLError(CE_Warning, CPLE_AppDefined, + "Raster size is not a multiple of 4: %dx%d. " + "Extra rows/colums will be ignored during the compression.", + nXSize, nYSize); + } + + crn_comp_params comp_params; + comp_params.m_format = fmt; + comp_params.m_dxt_quality = dxt_quality; + comp_params.set_flag(cCRNCompFlagPerceptual, srgb_colorspace); + comp_params.set_flag(cCRNCompFlagDXT1AForTransparency, dxt1a_transparency); + + crn_block_compressor_context_t pContext = crn_create_block_compressor(comp_params); + + /* -------------------------------------------------------------------- */ + /* Write the DDS header to the file. */ + /* -------------------------------------------------------------------- */ + + VSIFWriteL(&crnlib::cDDSFileSignature, 1, + sizeof(crnlib::cDDSFileSignature), fpImage); + + crnlib::DDSURFACEDESC2 ddsDesc; + memset(&ddsDesc, 0, sizeof(ddsDesc)); + ddsDesc.dwSize = sizeof(ddsDesc); + ddsDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_MIPMAPCOUNT | DDSD_PIXELFORMAT | DDSD_DEPTH ; + ddsDesc.dwWidth = nXSize; + ddsDesc.dwHeight = nYSize; + ddsDesc.dwMipMapCount = 1; + + ddsDesc.ddpfPixelFormat.dwSize = sizeof(crnlib::DDPIXELFORMAT); + ddsDesc.ddpfPixelFormat.dwFlags = DDPF_FOURCC; + ddsDesc.ddpfPixelFormat.dwFourCC = crn_get_format_fourcc(fmt); + ddsDesc.ddsCaps.dwCaps = DDSCAPS_TEXTURE; + + // Set pitch/linearsize field (some DDS readers require this field to be non-zero). + uint bits_per_pixel = crn_get_format_bits_per_texel(fmt); + ddsDesc.lPitch = (((ddsDesc.dwWidth + 3) & ~3) * ((ddsDesc.dwHeight + 3) & ~3) * bits_per_pixel) >> 3; + ddsDesc.dwFlags |= DDSD_LINEARSIZE; + + /* Endianness problems when serializing structure?? dds on-disk format + should be verified */ + VSIFWriteL(&ddsDesc, 1, sizeof(ddsDesc), fpImage); + + /* -------------------------------------------------------------------- */ + /* Loop over image, compressing image data. */ + /* -------------------------------------------------------------------- */ + const uint bytesPerBlock = crn_get_bytes_per_dxt_block(fmt); + CPLErr eErr = CE_None; + const uint nYNumBlocks = (nYSize + cDXTBlockSize - 1) / cDXTBlockSize; + const uint num_blocks_x = (nXSize + cDXTBlockSize - 1) / cDXTBlockSize; + const uint total_compressed_size = num_blocks_x * bytesPerBlock; + + void *pCompressed_data = CPLMalloc(total_compressed_size); + GByte* pabyScanlines = (GByte *) CPLMalloc(nBands * nXSize * cDXTBlockSize); + crn_uint32 *pixels = (crn_uint32*) CPLMalloc(sizeof(crn_uint32)*cDXTBlockSize * cDXTBlockSize); + crn_uint32 *src_image = NULL; + if (nColorType == DDS_COLOR_TYPE_RGB) + src_image = (crn_uint32*) CPLMalloc(sizeof(crn_uint32)*nXSize*cDXTBlockSize); + + for (uint iLine = 0; iLine < nYNumBlocks && eErr == CE_None; iLine++) + { + const uint size_y = (iLine*cDXTBlockSize+cDXTBlockSize) < (uint)nYSize ? + cDXTBlockSize : (cDXTBlockSize-((iLine*cDXTBlockSize+cDXTBlockSize)-(uint)nYSize)); + + eErr = poSrcDS->RasterIO(GF_Read, 0, iLine*cDXTBlockSize, nXSize, size_y, + pabyScanlines, nXSize, size_y, GDT_Byte, + nBands, NULL, + nBands, + nBands * nXSize, 1, NULL); + + if (eErr != CE_None) + break; + + crn_uint32 *pSrc_image = NULL; + if (nColorType == DDS_COLOR_TYPE_RGB_ALPHA) + pSrc_image = (crn_uint32*)pabyScanlines; + else if (nColorType == DDS_COLOR_TYPE_RGB) + { /* crunch needs 32bits integers */ + int nPixels = nXSize*cDXTBlockSize; + for (int i=0; i(pCompressed_data) + block_x * bytesPerBlock); + } + + if (eErr == CE_None) + VSIFWriteL(pCompressed_data, 1, total_compressed_size, fpImage); + + if (eErr == CE_None + && !pfnProgress( (iLine+1) / (double) nYNumBlocks, + NULL, pProgressData)) + { + eErr = CE_Failure; + CPLError(CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()"); + } + + } + + CPLFree(src_image); + CPLFree(pixels); + CPLFree(pCompressed_data); + CPLFree(pabyScanlines); + crn_free_block_compressor(pContext); + pContext = NULL; + + VSIFCloseL(fpImage); + + if (eErr != CE_None) + return NULL; + + DDSDataset *poDsDummy = new DDSDataset(); + + return poDsDummy; +} + +/************************************************************************/ +/* GDALRegister_DDS() */ +/************************************************************************/ + +void GDALRegister_DDS() +{ + GDALDriver *poDriver; + + if (GDALGetDriverByName( "DDS" ) == NULL) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription("DDS"); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, + "DirectDraw Surface"); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_various.html#DDS" ); + poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "dds"); + poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/dds"); + + poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, + "\n" + " \n" + " \n" + "\n" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnCreateCopy = DDSDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver(poDriver); + } +} diff --git a/bazaar/plugin/gdal/frmts/dds/makefile.vc b/bazaar/plugin/gdal/frmts/dds/makefile.vc new file mode 100644 index 000000000..93fa6d0f6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dds/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = ddsdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/dimap/GNUmakefile b/bazaar/plugin/gdal/frmts/dimap/GNUmakefile new file mode 100644 index 000000000..80bfbfee7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dimap/GNUmakefile @@ -0,0 +1,15 @@ +include ../../GDALmake.opt + +OBJ = dimapdataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +install: + $(INSTALL_DATA) memdataset.h $(DESTDIR)$(INST_INCLUDE) diff --git a/bazaar/plugin/gdal/frmts/dimap/dimapdataset.cpp b/bazaar/plugin/gdal/frmts/dimap/dimapdataset.cpp new file mode 100644 index 000000000..1c5f936db --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dimap/dimapdataset.cpp @@ -0,0 +1,1172 @@ +/****************************************************************************** + * $Id $ + * + * Project: SPOT Dimap Driver + * Purpose: Implementation of SPOT Dimap driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + * Docs: http://www.spotimage.fr/dimap/spec/documentation/refdoc.htm + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_minixml.h" +#include "ogr_spatialref.h" +#include "gdal_proxy.h" + +CPL_CVSID("$Id: dimapdataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_DIMAP(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* DIMAPDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class DIMAPDataset : public GDALPamDataset +{ + CPLXMLNode *psProduct; + + CPLXMLNode *psProductDim; /* DIMAP2, DIM_.XML */ + CPLXMLNode *psProductStrip; /* DIMAP2, STRIP_.XML */ + + GDALDataset *poImageDS; + + int nGCPCount; + GDAL_GCP *pasGCPList; + char *pszGCPProjection; + + CPLString osProjection; + + int bHaveGeoTransform; + double adfGeoTransform[6]; + + CPLString osMDFilename; + CPLString osImageDSFilename; + CPLString osDIMAPFilename; + int nProductVersion; + + char **papszXMLDimapMetadata; + + protected: + virtual int CloseDependentDatasets(); + + int ReadImageInformation(); + int ReadImageInformation2(); /* DIMAP 2 */ + + void SetMetadataFromXML(CPLXMLNode *psProduct, const char *apszMetadataTranslation[]); + public: + DIMAPDataset(); + ~DIMAPDataset(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * ); + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char *pszDomain ); + virtual char **GetFileList(void); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + + CPLXMLNode *GetProduct() { return psProduct; } +}; + +/************************************************************************/ +/* ==================================================================== */ +/* DIMAPWrapperRasterBand */ +/* ==================================================================== */ +/************************************************************************/ +class DIMAPWrapperRasterBand : public GDALProxyRasterBand +{ + GDALRasterBand* poBaseBand; + + protected: + virtual GDALRasterBand* RefUnderlyingRasterBand() { return poBaseBand; } + + public: + DIMAPWrapperRasterBand( GDALRasterBand* poBaseBand ) + { + this->poBaseBand = poBaseBand; + eDataType = poBaseBand->GetRasterDataType(); + poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + } + ~DIMAPWrapperRasterBand() {} +}; +/************************************************************************/ +/* ==================================================================== */ +/* DIMAPDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* DIMAPDataset() */ +/************************************************************************/ + +DIMAPDataset::DIMAPDataset() +{ + psProduct = NULL; + + psProductDim = NULL; + psProductStrip = NULL; + + nGCPCount = 0; + pasGCPList = NULL; + pszGCPProjection = CPLStrdup(""); + + poImageDS = NULL; + bHaveGeoTransform = FALSE; + + nProductVersion = 1; + + papszXMLDimapMetadata = NULL; +} + +/************************************************************************/ +/* ~DIMAPDataset() */ +/************************************************************************/ + +DIMAPDataset::~DIMAPDataset() + +{ + FlushCache(); + + CPLDestroyXMLNode( psProduct ); + + if( psProductDim != NULL ) + CPLDestroyXMLNode( psProductDim ); + if( psProductStrip != NULL ) + CPLDestroyXMLNode( psProductStrip ); + + CPLFree( pszGCPProjection ); + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + CSLDestroy(papszXMLDimapMetadata); + + CloseDependentDatasets(); +} + + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int DIMAPDataset::CloseDependentDatasets() +{ + int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets(); + + if( poImageDS != NULL ) + { + delete poImageDS; + poImageDS = NULL; + bHasDroppedRef = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Disconnect the bands so our destructor doesn't try and */ +/* delete them since they really belonged to poImageDS. */ +/* -------------------------------------------------------------------- */ + int iBand; + for( iBand = 0; iBand < nBands; iBand++ ) + delete papoBands[iBand]; + nBands = 0; + + return bHasDroppedRef; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **DIMAPDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "xml:dimap", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/* */ +/* We implement special support for fetching the full product */ +/* metadata as xml. */ +/************************************************************************/ + +char **DIMAPDataset::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain && EQUAL(pszDomain,"xml:dimap") ) + { + if (papszXMLDimapMetadata == NULL) + { + papszXMLDimapMetadata = (char **) CPLCalloc(sizeof(char*),2); + papszXMLDimapMetadata[0] = CPLSerializeXMLTree( psProduct ); + } + return papszXMLDimapMetadata; + } + else + return GDALPamDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *DIMAPDataset::GetProjectionRef() + +{ + if( strlen(osProjection) > 0 ) + return osProjection; + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr DIMAPDataset::GetGeoTransform( double *padfGeoTransform ) + +{ + if( bHaveGeoTransform ) + { + memcpy( padfGeoTransform, adfGeoTransform, sizeof(double)*6 ); + return CE_None; + } + else + return GDALPamDataset::GetGeoTransform( padfGeoTransform ); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **DIMAPDataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + char **papszImageFiles = poImageDS->GetFileList(); + + papszFileList = CSLInsertStrings( papszFileList, -1, papszImageFiles ); + + CSLDestroy( papszImageFiles ); + + return papszFileList; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int DIMAPDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes >= 100 ) + { + if( ( strstr((const char *) poOpenInfo->pabyHeader, + "pabyHeader, + "bIsDirectory ) + { + VSIStatBufL sStat; + + /* DIMAP file */ + CPLString osMDFilename = + CPLFormCIFilename( poOpenInfo->pszFilename, "METADATA.DIM", NULL ); + + if( VSIStatL( osMDFilename, &sStat ) == 0 ) + { + /* Make sure this is really a Dimap format */ + GDALOpenInfo oOpenInfo( osMDFilename, GA_ReadOnly, NULL ); + if( oOpenInfo.nHeaderBytes >= 100 ) + { + if( strstr((const char *) oOpenInfo.pabyHeader, + "pszFilename, "VOL_PHR.XML", NULL ); + + if( VSIStatL( osMDFilename, &sStat ) == 0 ) + return TRUE; + else + return FALSE; + } + } + + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *DIMAPDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int nProductVersion = 1; + + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The DIMAP driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Get the metadata filename. */ +/* -------------------------------------------------------------------- */ + CPLString osMDFilename, osImageDSFilename, osDIMAPFilename; + + if( poOpenInfo->bIsDirectory ) + { + VSIStatBufL sStat; + + osMDFilename = + CPLFormCIFilename( poOpenInfo->pszFilename, "METADATA.DIM", NULL ); + + /* DIMAP2 */ + if( VSIStatL( osMDFilename, &sStat ) != 0 ) + osMDFilename = + CPLFormCIFilename( poOpenInfo->pszFilename, "VOL_PHR.XML", NULL ); + } + else + osMDFilename = poOpenInfo->pszFilename; + +/* -------------------------------------------------------------------- */ +/* Ingest the xml file. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psProduct, *psImageAttributes; + CPLXMLNode *psProductDim = NULL, *psProductStrip = NULL; + + float nMetadataFormatVersion; + + psProduct = CPLParseXMLFile( osMDFilename ); + if( psProduct == NULL ) + return NULL; + + CPLXMLNode *psDoc = CPLGetXMLNode( psProduct, "=Dimap_Document" ); + if( !psDoc ) + psDoc = CPLGetXMLNode( psProduct, "=PHR_DIMAP_Document" ); + + /* We check the for the tag Metadata_Identification.METADATA_FORMAT. + * The metadata will be set to 2.0 for DIMAP2 */ + nMetadataFormatVersion = CPLAtof( CPLGetXMLValue(CPLGetXMLNode(psDoc, "Metadata_Identification.METADATA_FORMAT"), + "version", "1") ); + if( nMetadataFormatVersion >= 2.0 ) + { + nProductVersion = 2; + } + + /* Check needed information for the DIMAP format */ + if (nProductVersion == 1) + { + psImageAttributes = CPLGetXMLNode( psDoc, "Raster_Dimensions" ); + if( psImageAttributes == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to find in document." ); + CPLDestroyXMLNode(psProduct); + return NULL; + } + } + else /* DIMAP2 */ + { + /* Verify the presence of the DIMAP product file */ + CPLXMLNode *psDatasetComponents = CPLGetXMLNode(psDoc, "Dataset_Content.Dataset_Components"); + + if( psDatasetComponents == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to find in document." ); + CPLDestroyXMLNode(psProduct); + return NULL; + } + + CPLXMLNode *psDatasetComponent = psDatasetComponents->psChild; + + if( CPLGetXMLNode(psDoc, "Raster_Data") ) + { + osDIMAPFilename = osMDFilename; + } + + for( ; osDIMAPFilename.size() == 0 && psDatasetComponent != NULL; + psDatasetComponent = psDatasetComponent->psNext ) + { + const char* pszComponentType = CPLGetXMLValue(psDatasetComponent, "COMPONENT_TYPE",""); + if( strcmp(pszComponentType, "DIMAP") == 0 ) + { + const char *pszHref = CPLGetXMLValue( + psDatasetComponent, "COMPONENT_PATH.href", "" ); + + if( strlen(pszHref) > 0 ) /* DIMAP product found*/ + { + if( poOpenInfo->bIsDirectory ) + { + osDIMAPFilename = + CPLFormCIFilename( poOpenInfo->pszFilename, pszHref, NULL ); + } + else + { + CPLString osPath = CPLGetPath(osMDFilename); + osDIMAPFilename = + CPLFormFilename( osPath, pszHref, NULL ); + } + + /* Data file might be specified there */ + const char *pszDataFileHref = CPLGetXMLValue( + psDatasetComponent, "Data_Files.Data_File.DATA_FILE_PATH.href", "" ); + + if( strlen(pszDataFileHref) > 0 ) + { + CPLString osPath = CPLGetPath(osMDFilename); + osImageDSFilename = + CPLFormFilename( osPath, pszDataFileHref, NULL ); + } + + break; + } + } + } + + psProductDim = CPLParseXMLFile( osDIMAPFilename ); + if( psProductDim == NULL ) + { + CPLDestroyXMLNode(psProduct); + return NULL; + } + + /* We need the STRIP_.XML file for a few metadata */ + CPLXMLNode *psDocDim = CPLGetXMLNode( psProductDim, "=Dimap_Document" ); + if( !psDocDim ) + psDocDim = CPLGetXMLNode( psProductDim, "=PHR_DIMAP_Document" ); + + CPLXMLNode *psDatasetSources = CPLGetXMLNode(psDocDim, "Dataset_Sources"); + if( psDatasetSources != NULL ) + { + CPLString osSTRIPFilename; + CPLXMLNode *psDatasetSource = psDatasetSources->psChild; + + for( ; psDatasetSource != NULL; psDatasetSource = psDatasetSource->psNext ) + { + const char* pszSourceType = CPLGetXMLValue(psDatasetSource, "SOURCE_TYPE",""); + if( strcmp(pszSourceType, "Strip_Source") == 0 ) + { + const char *pszHref = CPLGetXMLValue( + psDatasetSource, "Component.COMPONENT_PATH.href", "" ); + + if( strlen(pszHref) > 0 ) /* STRIP product found*/ + { + CPLString osPath = CPLGetPath(osDIMAPFilename); + osSTRIPFilename = + CPLFormCIFilename( osPath, pszHref, NULL ); + + break; + } + } + } + + psProductStrip = CPLParseXMLFile( osSTRIPFilename ); + } + } + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + DIMAPDataset *poDS = new DIMAPDataset(); + + poDS->psProduct = psProduct; + poDS->psProductDim = psProductDim; + poDS->psProductStrip = psProductStrip; + poDS->nProductVersion = nProductVersion; + poDS->osMDFilename = osMDFilename; + poDS->osImageDSFilename = osImageDSFilename; + poDS->osDIMAPFilename = osDIMAPFilename; + + int res = TRUE; + if( nProductVersion == 2 ) + res = poDS->ReadImageInformation2(); + else + res = poDS->ReadImageInformation(); + + if( res == FALSE ) + { + delete poDS; + return NULL; + } + + return( poDS ); +} + + +/************************************************************************/ +/* ReadImageInformation() DIMAP Version 1 */ +/************************************************************************/ + +int DIMAPDataset::ReadImageInformation() +{ + CPLXMLNode *psDoc = CPLGetXMLNode( psProduct, "=Dimap_Document" ); + if( !psDoc ) + psDoc = CPLGetXMLNode( psProduct, "=PHR_DIMAP_Document" ); + + CPLXMLNode *psImageAttributes = CPLGetXMLNode( psDoc, "Raster_Dimensions" ); + +/* -------------------------------------------------------------------- */ +/* Get overall image information. */ +/* -------------------------------------------------------------------- */ +#ifdef DEBUG + int nBands = + atoi(CPLGetXMLValue( psImageAttributes, "NBANDS", "-1" )); +#endif + + nRasterXSize = + atoi(CPLGetXMLValue( psImageAttributes, "NCOLS", "-1" )); + nRasterYSize = + atoi(CPLGetXMLValue( psImageAttributes, "NROWS", "-1" )); + +/* -------------------------------------------------------------------- */ +/* Get the name of the underlying file. */ +/* -------------------------------------------------------------------- */ + + const char *pszHref = CPLGetXMLValue( + psDoc, "Data_Access.Data_File.DATA_FILE_PATH.href", "" ); + CPLString osPath = CPLGetPath(osMDFilename); + CPLString osImageFilename = + CPLFormFilename( osPath, pszHref, NULL ); + +/* -------------------------------------------------------------------- */ +/* Try and open the file. */ +/* -------------------------------------------------------------------- */ + + poImageDS = (GDALDataset *) GDALOpen( osImageFilename, GA_ReadOnly ); + if( poImageDS == NULL ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Attach the bands. */ +/* -------------------------------------------------------------------- */ + int iBand; + CPLAssert( nBands == poImageDS->GetRasterCount() ); + + for( iBand = 1; iBand <= poImageDS->GetRasterCount(); iBand++ ) + SetBand( iBand, new DIMAPWrapperRasterBand(poImageDS->GetRasterBand( iBand )) ); + +/* -------------------------------------------------------------------- */ +/* Try to collect simple insertion point. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psGeoLoc = + CPLGetXMLNode( psDoc, "Geoposition.Geoposition_Insert" ); + + if( psGeoLoc != NULL ) + { + bHaveGeoTransform = TRUE; + adfGeoTransform[0] = CPLAtof(CPLGetXMLValue(psGeoLoc,"ULXMAP","0")); + adfGeoTransform[1] = CPLAtof(CPLGetXMLValue(psGeoLoc,"XDIM","0")); + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = CPLAtof(CPLGetXMLValue(psGeoLoc,"ULYMAP","0")); + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = -CPLAtof(CPLGetXMLValue(psGeoLoc,"YDIM","0")); + } + else + { + // Try to get geotransform from underlying raster. + if ( poImageDS->GetGeoTransform(adfGeoTransform) == CE_None ) + bHaveGeoTransform = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Collect GCPs. */ +/* -------------------------------------------------------------------- */ + psGeoLoc = CPLGetXMLNode( psDoc, "Geoposition.Geoposition_Points" ); + + if( psGeoLoc != NULL ) + { + CPLXMLNode *psNode; + + // count gcps. + nGCPCount = 0; + for( psNode = psGeoLoc->psChild; psNode != NULL; + psNode = psNode->psNext ) + { + if( EQUAL(psNode->pszValue,"Tie_Point") ) + nGCPCount++ ; + } + + pasGCPList = (GDAL_GCP *) + CPLCalloc(sizeof(GDAL_GCP),nGCPCount); + + nGCPCount = 0; + + for( psNode = psGeoLoc->psChild; psNode != NULL; + psNode = psNode->psNext ) + { + char szID[32]; + GDAL_GCP *psGCP = pasGCPList + nGCPCount; + + if( !EQUAL(psNode->pszValue,"Tie_Point") ) + continue; + + nGCPCount++ ; + + sprintf( szID, "%d", nGCPCount ); + psGCP->pszId = CPLStrdup( szID ); + psGCP->pszInfo = CPLStrdup(""); + psGCP->dfGCPPixel = + CPLAtof(CPLGetXMLValue(psNode,"TIE_POINT_DATA_X","0"))-0.5; + psGCP->dfGCPLine = + CPLAtof(CPLGetXMLValue(psNode,"TIE_POINT_DATA_Y","0"))-0.5; + psGCP->dfGCPX = + CPLAtof(CPLGetXMLValue(psNode,"TIE_POINT_CRS_X","")); + psGCP->dfGCPY = + CPLAtof(CPLGetXMLValue(psNode,"TIE_POINT_CRS_Y","")); + psGCP->dfGCPZ = + CPLAtof(CPLGetXMLValue(psNode,"TIE_POINT_CRS_Z","")); + } + } + +/* -------------------------------------------------------------------- */ +/* Collect the CRS. For now we look only for EPSG codes. */ +/* -------------------------------------------------------------------- */ + const char *pszSRS = CPLGetXMLValue( + psDoc, + "Coordinate_Reference_System.Horizontal_CS.HORIZONTAL_CS_CODE", + NULL ); + + if( pszSRS != NULL ) + { + OGRSpatialReference oSRS; + if( oSRS.SetFromUserInput( pszSRS ) == OGRERR_NONE ) + { + if( nGCPCount > 0 ) + { + CPLFree(pszGCPProjection); + oSRS.exportToWkt( &(pszGCPProjection) ); + } + else + { + char *pszProjection = NULL; + oSRS.exportToWkt( &pszProjection ); + osProjection = pszProjection; + CPLFree( pszProjection ); + } + } + } + else + { + // Check underlying raster for SRS. We have cases where + // HORIZONTAL_CS_CODE is empty and the underlying raster + // is georeferenced (rprinceley). + if ( poImageDS->GetProjectionRef() ) + { + osProjection = poImageDS->GetProjectionRef(); + } + } + +/* -------------------------------------------------------------------- */ +/* Translate other metadata of interest. */ +/* -------------------------------------------------------------------- */ + static const char *apszMetadataTranslation[] = + { + "Production", "", + "Production.Facility", "FACILITY_", + "Dataset_Sources.Source_Information.Scene_Source", "", + "Data_Processing", "", + "Image_Interpretation.Spectral_Band_Info", "SPECTRAL_", + NULL, NULL + }; + + SetMetadataFromXML(psProduct, apszMetadataTranslation); + +/* -------------------------------------------------------------------- */ +/* Set Band metadata from the content */ +/* -------------------------------------------------------------------- */ + + CPLXMLNode *psImageInterpretationNode = + CPLGetXMLNode( psDoc, "Image_Interpretation" ); + if (psImageInterpretationNode != NULL) + { + CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild; + while (psSpectralBandInfoNode != NULL) + { + if (psSpectralBandInfoNode->eType == CXT_Element && + EQUAL(psSpectralBandInfoNode->pszValue, "Spectral_Band_Info")) + { + CPLXMLNode *psTag = psSpectralBandInfoNode->psChild; + int nBandIndex = 0; + while(psTag != NULL) + { + if (psTag->eType == CXT_Element && psTag->psChild != NULL && + psTag->psChild->eType == CXT_Text && psTag->pszValue != NULL) + { + if (EQUAL(psTag->pszValue, "BAND_INDEX")) + { + nBandIndex = atoi(psTag->psChild->pszValue); + if (nBandIndex <= 0 || + nBandIndex > poImageDS->GetRasterCount()) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Bad BAND_INDEX value : %s", psTag->psChild->pszValue); + nBandIndex = 0; + } + } + else if (nBandIndex >= 1) + { + GetRasterBand(nBandIndex)->SetMetadataItem( + psTag->pszValue, psTag->psChild->pszValue); + } + } + psTag = psTag->psNext; + } + } + psSpectralBandInfoNode = psSpectralBandInfoNode->psNext; + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + SetDescription( osMDFilename ); + TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + oOvManager.Initialize( this, osMDFilename ); + + return TRUE; +} + + +/************************************************************************/ +/* ReadImageInformation() DIMAP Version 2 */ +/************************************************************************/ + +int DIMAPDataset::ReadImageInformation2() +{ + CPLXMLNode *psDoc = CPLGetXMLNode( psProductDim, "=Dimap_Document" ); + if( !psDoc ) + psDoc = CPLGetXMLNode( psProductDim, "=PHR_DIMAP_Document" ); + + CPLXMLNode *psImageAttributes = CPLGetXMLNode( psDoc, "Raster_Data.Raster_Dimensions" ); + if( psImageAttributes == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to find in document." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Get overall image information. */ +/* -------------------------------------------------------------------- */ +#ifdef DEBUG + int nBands = + atoi(CPLGetXMLValue( psImageAttributes, "NBANDS", "-1" )); +#endif + + nRasterXSize = + atoi(CPLGetXMLValue( psImageAttributes, "NCOLS", "-1" )); + nRasterYSize = + atoi(CPLGetXMLValue( psImageAttributes, "NROWS", "-1" )); + +/* -------------------------------------------------------------------- */ +/* Get the name of the underlying file. */ +/* -------------------------------------------------------------------- */ + + /* If the data file was not in the product file, it should be here */ + if ( osImageDSFilename.size() == 0 ) + { + const char *pszHref = CPLGetXMLValue( + psDoc, "Raster_Data.Data_Access.Data_Files.Data_File.DATA_FILE_PATH.href", "" ); + if( strlen(pszHref) > 0 ) + { + CPLString osPath = CPLGetPath( osDIMAPFilename ); + osImageDSFilename = + CPLFormCIFilename( osPath, pszHref, NULL ); + } + else + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to find in document." ); + return FALSE; + } + } + + +/* -------------------------------------------------------------------- */ +/* Try and open the file. */ +/* -------------------------------------------------------------------- */ + poImageDS = (GDALDataset *) GDALOpen( osImageDSFilename, GA_ReadOnly ); + if( poImageDS == NULL ) + { + return FALSE; + } + + +/* -------------------------------------------------------------------- */ +/* Attach the bands. */ +/* -------------------------------------------------------------------- */ + int iBand; + CPLAssert( nBands == poImageDS->GetRasterCount() ); + + for( iBand = 1; iBand <= poImageDS->GetRasterCount(); iBand++ ) + SetBand( iBand, new DIMAPWrapperRasterBand(poImageDS->GetRasterBand( iBand )) ); + +/* -------------------------------------------------------------------- */ +/* Try to collect simple insertion point. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psGeoLoc = + CPLGetXMLNode( psDoc, "Geoposition.Geoposition_Insert" ); + + if( psGeoLoc != NULL ) + { + bHaveGeoTransform = TRUE; + adfGeoTransform[0] = CPLAtof(CPLGetXMLValue(psGeoLoc,"ULXMAP","0")); + adfGeoTransform[1] = CPLAtof(CPLGetXMLValue(psGeoLoc,"XDIM","0")); + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = CPLAtof(CPLGetXMLValue(psGeoLoc,"ULYMAP","0")); + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = -CPLAtof(CPLGetXMLValue(psGeoLoc,"YDIM","0")); + } + else + { + // Try to get geotransform from underlying raster. + if ( poImageDS->GetGeoTransform(adfGeoTransform) == CE_None ) + bHaveGeoTransform = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Collect the CRS. For now we look only for EPSG codes. */ +/* -------------------------------------------------------------------- */ + const char *pszSRS = CPLGetXMLValue( + psDoc, + "Coordinate_Reference_System.Projected_CRS.PROJECTED_CRS_CODE", + NULL ); + if( pszSRS == NULL ) + pszSRS = CPLGetXMLValue( + psDoc, + "Coordinate_Reference_System.Geodetic_CRS.GEODETIC_CRS_CODE", + NULL ); + + if( pszSRS != NULL ) + { + OGRSpatialReference oSRS; + if( oSRS.SetFromUserInput( pszSRS ) == OGRERR_NONE ) + { + if( nGCPCount > 0 ) + { + CPLFree(pszGCPProjection); + oSRS.exportToWkt( &(pszGCPProjection) ); + } + else + { + char *pszProjection = NULL; + oSRS.exportToWkt( &pszProjection ); + osProjection = pszProjection; + CPLFree( pszProjection ); + } + } + } + else + { + // Check underlying raster for SRS. We have cases where + // HORIZONTAL_CS_CODE is empty and the underlying raster + // is georeferenced (rprinceley). + if ( poImageDS->GetProjectionRef() ) + { + osProjection = poImageDS->GetProjectionRef(); + } + } + +/* -------------------------------------------------------------------- */ +/* Translate other metadata of interest: DIM_.XML */ +/* -------------------------------------------------------------------- */ + + static const char *apszMetadataTranslationDim[] = + { + "Product_Information.Delivery_Identification", "DATASET_", + "Product_Information.Producer_Information", "DATASET_", + "Dataset_Sources.Source_Identification.Strip_Source", "", + "Processing_Information.Production_Facility", "FACILITY_", + "Processing_Information.Product_Settings", "", + "Processing_Information.Product_Settings.Geometric_Settings", "GEOMETRIC_", + "Quality_Assessment.Imaging_Quality_Measurement", "CLOUDCOVER_", + NULL, NULL + }; + + SetMetadataFromXML(psProductDim, apszMetadataTranslationDim); + +/* -------------------------------------------------------------------- */ +/* Translate other metadata of interest: STRIP_.XML */ +/* -------------------------------------------------------------------- */ + + static const char *apszMetadataTranslationStrip[] = + { + "Catalog.Full_Strip.Notations.Cloud_And_Quality_Notation.Data_Strip_Notation", "CLOUDCOVER_", + "Acquisition_Configuration.Platform_Configuration.Ephemeris_Configuration", "EPHEMERIS_", + NULL, NULL + }; + + if( psProductStrip != NULL ) + SetMetadataFromXML(psProductStrip, apszMetadataTranslationStrip); + +/* -------------------------------------------------------------------- */ +/* Set Band metadata from the and */ +/* content */ +/* -------------------------------------------------------------------- */ + + CPLXMLNode *psImageInterpretationNode = + CPLGetXMLNode( psDoc, + "Radiometric_Data.Radiometric_Calibration.Instrument_Calibration.Band_Measurement_List" ); + if (psImageInterpretationNode != NULL) + { + CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild; + while (psSpectralBandInfoNode != NULL) + { + if (psSpectralBandInfoNode->eType == CXT_Element && + (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance") || + EQUAL(psSpectralBandInfoNode->pszValue, "Band_Spectral_Range") || + EQUAL(psSpectralBandInfoNode->pszValue, "Band_Solar_Irradiance"))) + { + CPLString osName; + + if (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance")) + osName = "RADIANCE_"; + else if (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Spectral_Range")) + osName = "SPECTRAL_RANGE_"; + else if (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Solar_Irradiance")) + osName = "SOLAR_IRRADIANCE_"; + + CPLXMLNode *psTag = psSpectralBandInfoNode->psChild; + int nBandIndex = 0; + while(psTag != NULL) + { + if (psTag->eType == CXT_Element && psTag->psChild != NULL && + psTag->psChild->eType == CXT_Text && psTag->pszValue != NULL) + { + if (EQUAL(psTag->pszValue, "BAND_ID")) + { + /* BAND_ID is: B0, B1, .... P */ + if (!EQUAL(psTag->psChild->pszValue, "P")) + { + if (strlen(psTag->psChild->pszValue) < 2) /* shouldn't happen */ + { + CPLError(CE_Warning, CPLE_AppDefined, + "Bad BAND_INDEX value : %s", psTag->psChild->pszValue); + nBandIndex = 0; + } + else + { + nBandIndex = atoi(&psTag->psChild->pszValue[1]) + 1; + if (nBandIndex <= 0 || + nBandIndex > poImageDS->GetRasterCount()) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Bad BAND_INDEX value : %s", psTag->psChild->pszValue); + nBandIndex = 0; + } + } + } + } + else if (nBandIndex >= 1) + { + CPLString osMDName = osName; + osMDName += psTag->pszValue; + + GetRasterBand(nBandIndex)->SetMetadataItem( + osMDName, psTag->psChild->pszValue); + } + + } + psTag = psTag->psNext; + } + } + psSpectralBandInfoNode = psSpectralBandInfoNode->psNext; + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + SetDescription( osMDFilename ); + TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + oOvManager.Initialize( this, osMDFilename ); + + return TRUE; +} + +/************************************************************************/ +/* SetMetadataFromXML() */ +/************************************************************************/ + +void DIMAPDataset::SetMetadataFromXML(CPLXMLNode *psProduct, const char *apszMetadataTranslation[]) +{ + CPLXMLNode *psDoc = CPLGetXMLNode( psProduct, "=Dimap_Document" ); + if( psDoc == NULL ) + { + psDoc = CPLGetXMLNode( psProduct, "=PHR_DIMAP_Document" ); + } + + int iTrItem; + + for( iTrItem = 0; apszMetadataTranslation[iTrItem] != NULL; iTrItem += 2 ) + { + CPLXMLNode *psParent = + CPLGetXMLNode( psDoc, apszMetadataTranslation[iTrItem] ); + + if( psParent == NULL ) + continue; + + // hackey logic to support directly access a name/value entry + // or a parent element with many name/values. + + CPLXMLNode *psTarget; + if( psParent->psChild != NULL + && psParent->psChild->eType == CXT_Text ) + psTarget = psParent; + else + psTarget = psParent->psChild; + + for( ; psTarget != NULL && psTarget != psParent; + psTarget = psTarget->psNext ) + { + if( psTarget->eType == CXT_Element + && psTarget->psChild != NULL) + { + CPLString osName = apszMetadataTranslation[iTrItem+1]; + + if (psTarget->psChild->eType == CXT_Text) + { + osName += psTarget->pszValue; + SetMetadataItem( osName, psTarget->psChild->pszValue ); + } + else if (psTarget->psChild->eType == CXT_Attribute) + { + /* find the tag value, at the end of the attributes */ + CPLXMLNode *psNode = psTarget->psChild; + for( ; psNode != NULL; psNode = psNode->psNext ) + { + if (psNode->eType == CXT_Attribute) + continue; + else if (psNode->eType == CXT_Text) + { + osName += psTarget->pszValue; + SetMetadataItem( osName, psNode->pszValue ); + } + } + } + } + } + } +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int DIMAPDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *DIMAPDataset::GetGCPProjection() + +{ + return pszGCPProjection; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *DIMAPDataset::GetGCPs() + +{ + return pasGCPList; +} + +/************************************************************************/ +/* GDALRegister_DIMAP() */ +/************************************************************************/ + +void GDALRegister_DIMAP() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "DIMAP" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "DIMAP" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "SPOT DIMAP" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#DIMAP" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = DIMAPDataset::Open; + poDriver->pfnIdentify = DIMAPDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/dimap/makefile.vc b/bazaar/plugin/gdal/frmts/dimap/makefile.vc new file mode 100644 index 000000000..2a5fb077e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dimap/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = dimapdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/dods/GNUmakefile b/bazaar/plugin/gdal/frmts/dods/GNUmakefile new file mode 100644 index 000000000..af987c1db --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dods/GNUmakefile @@ -0,0 +1,35 @@ + + +include ../../GDALmake.opt + +OBJ = dodsdataset2.o + +CPPFLAGS := $(CPPFLAGS) $(DODS_INC) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +# By linking with dodsdataset.o explicitly, we don't have to build the +# library to get changes in that code into using_dods for testing. However, +# make sure you do rebuild the library (run make two directories up) once +# you're done. 12/27/02 jhrg +using_dods: using_dods.$(OBJ_EXT) dodsdataset.$(OBJ_EXT) + $(LD) $(LNK_FLAGS) $^ $(XTRAOBJ) $(CONFIG_LIBS) -o $@$(EXE) + +# There's a note in GDALmake.opt that local programs should link against +# CONFIG_LIBS, but that doesn't work here, maybe because dodsdataset_test +# links statically (which it needs to do to run in a debugger). 10/31/03 jhrg +dodsdataset_test: dodsdataset_test.$(OBJ_EXT) dodsdataset.$(OBJ_EXT) + $(LD) $(LNK_FLAGS) -static -g3 $^ $(XTRAOBJ) $(LIBS) $(LIBGDAL) \ + -lcppunit -lxml2 -o $@$(EXE) + +docs: + doxygen + +clean: + -rm -f *.o *.lo *~ $(O_OBJ) + -rm -rf docs + -rm -f dodsdataset_test using_dods + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +dodsdataset.o: dodsdataset.cpp dodsdataset.h diff --git a/bazaar/plugin/gdal/frmts/dods/dodsdataset2.cpp b/bazaar/plugin/gdal/frmts/dods/dodsdataset2.cpp new file mode 100644 index 000000000..41000810d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dods/dodsdataset2.cpp @@ -0,0 +1,1744 @@ +/****************************************************************************** + * $Id: dodsdataset2.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: OPeNDAP Raster Driver + * Purpose: Implements DODSDataset and DODSRasterBand classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2003 OPeNDAP, Inc. + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include +#include +#include + +#include + +#include // DODS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef LIBDAP_310 +/* AISConnect.h/AISConnect class was renamed to Connect.h/Connect in libdap 3.10 */ +#include +#define AISConnect Connect +#else +#include +#endif + +#include +#include +#include +#include +#include + +#include "gdal_priv.h" // GDAL +#include "ogr_spatialref.h" +#include "cpl_string.h" + +using namespace libdap; + +CPL_CVSID("$Id: dodsdataset2.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_DODS(void); +CPL_C_END + +/** Attribute names used to encode geo-referencing information. Note that + these are not C++ objects to avoid problems with static global + constructors. + + @see get_geo_info + + @name Attribute names */ +//@{ +const char *nlat = "Northernmost_Latitude"; ///< +const char *slat = "Southernmost_Latitude"; ///< +const char *wlon = "Westernmost_Longitude"; ///< +const char *elon = "Easternmost_Longitude"; ///< +const char *gcs = "GeographicCS"; ///< +const char *pcs = "ProjectionCS"; ///< +const char *norm_proj_param = "Norm_Proj_Param"; ///< +const char *spatial_ref = "spatial_ref"; ///< +//@} + +/************************************************************************/ +/* get_variable() */ +/************************************************************************/ + +/** Find the variable in the DDS or DataDDS, given its name. This function + first looks for the name as given. If that can't be found, it determines + the leaf name of a fully qualified name and looks for that (the DAP + supports searching for leaf names as a short cut). In this case the + driver is using that feature because of an odd problem in the responses + returned by some servers when they are asked for a single array variable + from a Grid. Instead of returning GRID_NAME.ARRAY_NAME, they return just + ARRAY_NAME. That's really a bug in the spec. However, it means that if a + CE says GRID_NAME.ARRAY_NAME and the code looks only for that, it may not + be found because the nesting has been removed and only an array called + ARRAY_NAME returned. + + @param dds Look in this DDS object + @param n Names the variable to find. + @return A BaseType pointer to the object/variable in the DDS \e dds. */ + +static BaseType * +get_variable(DDS &dds, const string &n) +{ + BaseType *poBT = dds.var(www2id(n)); + if (!poBT) { + try { + string leaf = n.substr(n.find_last_of('.')+1); + poBT = dds.var(www2id(leaf)); + } + catch (const std::exception &e) { + poBT = 0; + } + } + + return poBT; +} + +/************************************************************************/ +/* StripQuotes() */ +/* */ +/* Strip the quotes off a string value and remove internal */ +/* quote escaping. */ +/************************************************************************/ + +static string StripQuotes( string oInput ) + +{ + char *pszResult; + + if( oInput.length() < 2 ) + return oInput; + + oInput = oInput.substr(1,oInput.length()-2); + + pszResult = CPLUnescapeString( oInput.c_str(), NULL, + CPLES_BackslashQuotable ); + + oInput = pszResult; + + CPLFree( pszResult ); + + return oInput; +} + +/************************************************************************/ +/* GetDimension() */ +/* */ +/* Get the dimension for the named constrain dimension, -1 is */ +/* returned if not found. We would pass in a CE like */ +/* "[band][x][y]" or "[1][x][y]" and a dimension name like "y" */ +/* and get back the dimension index (2 if it is the 3rd */ +/* dimension). */ +/* */ +/* eg. GetDimension("[1][y][x]","y") -> 1 */ +/************************************************************************/ + +static int GetDimension( string oCE, const char *pszDimName, + int *pnDirection ) + +{ + int iCount = 0, i; + const char *pszCE = oCE.c_str(); + + if( pnDirection != NULL ) + *pnDirection = 1; + + for( i = 0; pszCE[i] != '\0'; i++ ) + { + if( pszCE[i] == '[' && pszCE[i+1] == pszDimName[0] ) + return iCount; + + else if( pszCE[i] == '[' + && pszCE[i+1] == '-' + && pszCE[i+2] == pszDimName[0] ) + { + if( pnDirection != NULL ) + *pnDirection = -1; + + return iCount; + } + else if( pszCE[i] == '[' ) + iCount++; + } + + return -1; +} + +/************************************************************************/ +/* ==================================================================== */ +/* DODSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class DODSDataset : public GDALDataset +{ +private: + AISConnect *poConnect; //< Virtual connection to the data source + + string oURL; //< data source URL + double adfGeoTransform[6]; + int bGotGeoTransform; + string oWKT; //< Constructed WKT string + + DAS oDAS; + DDS *poDDS; + BaseTypeFactory *poBaseTypeFactory; + + AISConnect *connect_to_server() throw(Error); + + static string SubConstraint( string raw_constraint, + string x_constraint, + string y_constraint ); + + char **CollectBandsFromDDS(); + char **CollectBandsFromDDSVar( string, char ** ); + char **ParseBandsFromURL( string ); + + void HarvestDAS(); + static void HarvestMetadata( GDALMajorObject *, AttrTable * ); + void HarvestMaps( string oVarName, string oCE ); + + friend class DODSRasterBand; + +public: + DODSDataset(); + virtual ~DODSDataset(); + + // Overridden GDALDataset methods + CPLErr GetGeoTransform(double *padfTransform); + const char *GetProjectionRef(); + + /// Open is not a method in GDALDataset; it's the driver. + static GDALDataset *Open(GDALOpenInfo *); + + /// Return the connection object + AISConnect *GetConnect() { return poConnect; } + + /// Return the data source URL + string GetUrl() { return oURL; } + DAS &GetDAS() { return oDAS; } + DDS &GetDDS() { return *poDDS; } +}; + +/************************************************************************/ +/* ==================================================================== */ +/* DODSRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class DODSRasterBand : public GDALRasterBand +{ +private: + string oVarName; + string oCE; // Holds the CE (with [x] and [y] still there + + friend class DODSDataset; + + GDALColorInterp eColorInterp; + GDALColorTable *poCT; + + int nOverviewCount; + DODSRasterBand **papoOverviewBand; + + int nOverviewFactor; // 1 for base, or 2/4/8 for overviews. + int bTranspose; + int bFlipX; + int bFlipY; + int bNoDataSet; + double dfNoDataValue; + + void HarvestDAS(); + +public: + DODSRasterBand( DODSDataset *poDS, string oVarName, string oCE, + int nOverviewFactor ); + virtual ~DODSRasterBand(); + + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview( int ); + virtual CPLErr IReadBlock(int, int, void *); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual CPLErr SetNoDataValue( double ); + virtual double GetNoDataValue( int * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* DODSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* DODSDataset() */ +/************************************************************************/ + +DODSDataset::DODSDataset() + +{ + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + bGotGeoTransform = FALSE; + + poConnect = NULL; + poBaseTypeFactory = new BaseTypeFactory(); + poDDS = new DDS( poBaseTypeFactory ); +} + +/************************************************************************/ +/* ~DODSDataset() */ +/************************************************************************/ + +DODSDataset::~DODSDataset() + +{ + if( poConnect ) + delete poConnect; + + if( poDDS ) + delete poDDS; + if( poBaseTypeFactory ) + delete poBaseTypeFactory; +} + +/************************************************************************/ +/* connect_to_server() */ +/************************************************************************/ + +AISConnect * +DODSDataset::connect_to_server() throw(Error) +{ + // does the string start with 'http?' + if (oURL.find("http://") == string::npos + && oURL.find("https://") == string::npos) + throw Error( + "The URL does not start with 'http' or 'https,' I won't try connecting."); + +/* -------------------------------------------------------------------- */ +/* Do we want to override the .dodsrc file setting? Only do */ +/* the putenv() if there isn't already a DODS_CONF in the */ +/* environment. */ +/* -------------------------------------------------------------------- */ + if( CPLGetConfigOption( "DODS_CONF", NULL ) != NULL + && getenv("DODS_CONF") == NULL ) + { + static char szDODS_CONF[1000]; + + sprintf( szDODS_CONF, "DODS_CONF=%.980s", + CPLGetConfigOption( "DODS_CONF", "" ) ); + putenv( szDODS_CONF ); + } + +/* -------------------------------------------------------------------- */ +/* If we have a overridding AIS file location, apply it now. */ +/* -------------------------------------------------------------------- */ + if( CPLGetConfigOption( "DODS_AIS_FILE", NULL ) != NULL ) + { + string oAISFile = CPLGetConfigOption( "DODS_AIS_FILE", "" ); + RCReader::instance()->set_ais_database( oAISFile ); + } + +/* -------------------------------------------------------------------- */ +/* Connect, and fetch version information. */ +/* -------------------------------------------------------------------- */ + AISConnect *poConnection = new AISConnect(oURL); + string version = poConnection->request_version(); + /* if (version.empty() || version.find("/3.") == string::npos) + { + CPLError( CE_Warning, CPLE_AppDefined, + "I connected to the URL but could not get a DAP 3.x version string\n" + "from the server. I will continue to connect but access may fail."); + } + */ + return poConnection; +} + +/************************************************************************/ +/* SubConstraint() */ +/* */ +/* Substitute into x and y constraint expressions in template */ +/* constraint string for the [x] and [y] parts. */ +/************************************************************************/ + +string DODSDataset::SubConstraint( string raw_constraint, + string x_constraint, + string y_constraint ) + +{ + string::size_type x_off, y_off, x_len=3, y_len=3; + string final_constraint; + + x_off = raw_constraint.find( "[x]" ); + if( x_off == string::npos ) + { + x_off = raw_constraint.find( "[-x]" ); + x_len = 4; + } + + y_off = raw_constraint.find( "[y]" ); + if( y_off == string::npos ) + { + y_off = raw_constraint.find( "[-y]" ); + y_len = 4; + } + + CPLAssert( x_off != string::npos && y_off != string::npos ); + + if( x_off < y_off ) + final_constraint = + raw_constraint.substr( 0, x_off ) + + x_constraint + + raw_constraint.substr( x_off + x_len, y_off - x_off - x_len ) + + y_constraint + + raw_constraint.substr( y_off + y_len ); + else + final_constraint = + raw_constraint.substr( 0, y_off ) + + y_constraint + + raw_constraint.substr( y_off + y_len, x_off - y_off - y_len ) + + x_constraint + + raw_constraint.substr( x_off + x_len ); + + return final_constraint; +} + +/************************************************************************/ +/* CollectBandsFromDDS() */ +/* */ +/* If no constraint/variable list is provided we will scan the */ +/* DDS output for arrays or grids that look like bands and */ +/* return the list of them with "guessed" [y][x] constraint */ +/* strings. */ +/* */ +/* We pick arrays or grids with at least two dimensions as */ +/* candidates. After the first we only accept additional */ +/* objects as bands if they match the size of the original. */ +/* */ +/* Auto-recognision rules will presumably evolve over time to */ +/* recognise different common configurations and to support */ +/* more variations. */ +/************************************************************************/ + +char **DODSDataset::CollectBandsFromDDS() + +{ + DDS::Vars_iter v_i; + char **papszResultList = NULL; + + for( v_i = poDDS->var_begin(); v_i != poDDS->var_end(); v_i++ ) + { + BaseType *poVar = *v_i; + papszResultList = CollectBandsFromDDSVar( poVar->name(), + papszResultList ); + } + + return papszResultList; +} + +/************************************************************************/ +/* CollectBandsFromDDSVar() */ +/* */ +/* Collect zero or more band definitions (varname + CE) for the */ +/* passed variable. If it is inappropriate then nothing is */ +/* added to the list. This method is shared by */ +/* CollectBandsFromDDS(), and by ParseBandsFromURL() when it */ +/* needs a default constraint expression generated. */ +/************************************************************************/ + +char **DODSDataset::CollectBandsFromDDSVar( string oVarName, + char **papszResultList ) + +{ + Array *poArray; + Grid *poGrid = NULL; + +/* -------------------------------------------------------------------- */ +/* Is this a grid or array? */ +/* -------------------------------------------------------------------- */ + BaseType *poVar = get_variable( GetDDS(), oVarName ); + + if( poVar->type() == dods_array_c ) + { + poGrid = NULL; + poArray = dynamic_cast( poVar ); + } + else if( poVar->type() == dods_grid_c ) + { + poGrid = dynamic_cast( poVar ); + poArray = dynamic_cast( poGrid->array_var() ); + } + else + return papszResultList; + +/* -------------------------------------------------------------------- */ +/* Eventually we will want to support arrays with more than two */ +/* dimensions ... but not quite yet. */ +/* -------------------------------------------------------------------- */ + if( poArray->dimensions() != 2 ) + return papszResultList; + +/* -------------------------------------------------------------------- */ +/* Get the dimension information for this variable. */ +/* -------------------------------------------------------------------- */ + Array::Dim_iter dim1 = poArray->dim_begin() + 0; + Array::Dim_iter dim2 = poArray->dim_begin() + 1; + + int nDim1Size = poArray->dimension_size( dim1 ); + int nDim2Size = poArray->dimension_size( dim2 ); + + if( nDim1Size == 1 || nDim2Size == 1 ) + return papszResultList; + +/* -------------------------------------------------------------------- */ +/* Try to guess which is x and y. */ +/* -------------------------------------------------------------------- */ + string dim1_name = poArray->dimension_name( dim1 ); + string dim2_name = poArray->dimension_name( dim2 ); + int iXDim=-1, iYDim=-1; + + if( dim1_name == "easting" && dim2_name == "northing" ) + { + iXDim = 0; + iYDim = 1; + } + else if( dim1_name == "easting" && dim2_name == "northing" ) + { + iXDim = 1; + iYDim = 0; + } + else if( EQUALN(dim1_name.c_str(),"lat",3) + && EQUALN(dim2_name.c_str(),"lon",3) ) + { + iXDim = 0; + iYDim = 1; + } + else if( EQUALN(dim1_name.c_str(),"lon",3) + && EQUALN(dim2_name.c_str(),"lat",3) ) + { + iXDim = 1; + iYDim = 0; + } + else + { + iYDim = 0; + iXDim = 1; + } + +/* -------------------------------------------------------------------- */ +/* Does this match the established dimension? */ +/* -------------------------------------------------------------------- */ + Array::Dim_iter dimx = poArray->dim_begin() + iXDim; + Array::Dim_iter dimy = poArray->dim_begin() + iYDim; + + if( nRasterXSize == 0 && nRasterYSize == 0 ) + { + nRasterXSize = poArray->dimension_size( dimx ); + nRasterYSize = poArray->dimension_size( dimy ); + } + + if( nRasterXSize != poArray->dimension_size( dimx ) + || nRasterYSize != poArray->dimension_size( dimy ) ) + return papszResultList; + +/* -------------------------------------------------------------------- */ +/* OK, we have an acceptable candidate! */ +/* -------------------------------------------------------------------- */ + string oConstraint; + + if( iXDim == 0 && iYDim == 1 ) + oConstraint = "[x][y]"; + else if( iXDim == 1 && iYDim == 0 ) + oConstraint = "[y][x]"; + else + return papszResultList; + + papszResultList = CSLAddString( papszResultList, + poVar->name().c_str() ); + papszResultList = CSLAddString( papszResultList, + oConstraint.c_str() ); + + return papszResultList; +} + +/************************************************************************/ +/* ParseBandsFromURL() */ +/************************************************************************/ + +char **DODSDataset::ParseBandsFromURL( string oVarList ) + +{ + char **papszResultList = NULL; + char **papszVars = CSLTokenizeString2( oVarList.c_str(), ",", 0 ); + int i; + + for( i = 0; papszVars != NULL && papszVars[i] != NULL; i++ ) + { + string oVarName; + string oCE; + +/* -------------------------------------------------------------------- */ +/* Split into a varname and constraint equation. */ +/* -------------------------------------------------------------------- */ + char *pszCEStart = strstr(papszVars[i],"["); + + // If we have no constraints we will have to try to guess + // reasonable values from the DDS. In fact, we might end up + // deriving multiple bands from one variable in this case. + if( pszCEStart == NULL ) + { + oVarName = papszVars[i]; + + papszResultList = + CollectBandsFromDDSVar( oVarName, papszResultList ); + } + else + { + oCE = pszCEStart; + *pszCEStart = '\0'; + oVarName = papszVars[i]; + + // Eventually we should consider supporting a [band] keyword + // to select a constraint variable that should be used to + // identify a band dimension ... but not for now. + + papszResultList = CSLAddString( papszResultList, + oVarName.c_str() ); + papszResultList = CSLAddString( papszResultList, + oCE.c_str() ); + } + } + + CSLDestroy(papszVars); + + return papszResultList; +} + +/************************************************************************/ +/* HarvestMetadata() */ +/* */ +/* Capture metadata items from an AttrTable, and assign as */ +/* metadata to the target object. */ +/************************************************************************/ + +void DODSDataset::HarvestMetadata( GDALMajorObject *poTarget, + AttrTable *poSrcTable ) + +{ + if( poSrcTable == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Find Metadata container. */ +/* -------------------------------------------------------------------- */ + AttrTable *poMDTable = poSrcTable->find_container("Metadata"); + + if( poMDTable == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Collect each data item from it. */ +/* -------------------------------------------------------------------- */ + AttrTable::Attr_iter dv_i; + + for( dv_i=poMDTable->attr_begin(); dv_i != poMDTable->attr_end(); dv_i++ ) + { + if( poMDTable->get_attr_type( dv_i ) != Attr_string ) + continue; + + poTarget->SetMetadataItem( + poMDTable->get_name( dv_i ).c_str(), + StripQuotes( poMDTable->get_attr(dv_i) ).c_str() ); + } +} + +/************************************************************************/ +/* HarvestDAS() */ +/************************************************************************/ + +void DODSDataset::HarvestDAS() + +{ +/* -------------------------------------------------------------------- */ +/* Try and fetch the corresponding DAS subtree if it exists. */ +/* -------------------------------------------------------------------- */ +#ifdef LIBDAP_39 + AttrTable *poFileInfo = oDAS.get_table( "GLOBAL" ); + + if( poFileInfo == NULL ) + { + poFileInfo = oDAS.get_table( "NC_GLOBAL" ); + + if( poFileInfo == NULL ) + { + poFileInfo = oDAS.get_table( "HDF_GLOBAL" ); + + if( poFileInfo == NULL ) + { + CPLDebug( "DODS", "No GLOBAL DAS info." ); + return; + } + } + } +#else + AttrTable *poFileInfo = oDAS.find_container( "GLOBAL" ); + + if( poFileInfo == NULL ) + { + poFileInfo = oDAS.find_container( "NC_GLOBAL" ); + + if( poFileInfo == NULL ) + { + poFileInfo = oDAS.find_container( "HDF_GLOBAL" ); + + if( poFileInfo == NULL ) + { + CPLDebug( "DODS", "No GLOBAL DAS info." ); + return; + } + } + } +#endif + +/* -------------------------------------------------------------------- */ +/* Try and fetch the bounds */ +/* -------------------------------------------------------------------- */ + string oNorth, oSouth, oEast, oWest; + + oNorth = poFileInfo->get_attr( "Northernmost_Northing" ); + oSouth = poFileInfo->get_attr( "Southernmost_Northing" ); + oEast = poFileInfo->get_attr( "Easternmost_Easting" ); + oWest = poFileInfo->get_attr( "Westernmost_Easting" ); + + if( oNorth != "" && oSouth != "" && oEast != "" && oWest != "" ) + { + adfGeoTransform[0] = CPLAtof(oWest.c_str()); + adfGeoTransform[1] = + (CPLAtof(oEast.c_str()) - CPLAtof(oWest.c_str())) / nRasterXSize; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = CPLAtof(oNorth.c_str()); + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = + (CPLAtof(oSouth.c_str()) - CPLAtof(oNorth.c_str())) / nRasterYSize; + + bGotGeoTransform = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Try and fetch a GeoTransform. The result will override the */ +/* geotransform derived from the bounds if it is present. This */ +/* allows us to represent rotated and sheared images. */ +/* -------------------------------------------------------------------- */ + string oValue; + + oValue = StripQuotes(poFileInfo->get_attr( "GeoTransform" )); + if( oValue != "" ) + { + char **papszItems = CSLTokenizeString( oValue.c_str() ); + if( CSLCount(papszItems) == 6 ) + { + adfGeoTransform[0] = CPLAtof(papszItems[0]); + adfGeoTransform[1] = CPLAtof(papszItems[1]); + adfGeoTransform[2] = CPLAtof(papszItems[2]); + adfGeoTransform[3] = CPLAtof(papszItems[3]); + adfGeoTransform[4] = CPLAtof(papszItems[4]); + adfGeoTransform[5] = CPLAtof(papszItems[5]); + bGotGeoTransform = TRUE; + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to parse GeoTransform DAS value: %s", + oValue.c_str() ); + } + CSLDestroy( papszItems ); + } + +/* -------------------------------------------------------------------- */ +/* Get the Projection. If it doesn't look like "pure" WKT then */ +/* try to process it through SetFromUserInput(). This expands */ +/* stuff like "WGS84". */ +/* -------------------------------------------------------------------- */ + oWKT = StripQuotes(poFileInfo->get_attr( "spatial_ref" )); + // strip remaining backslashes (2007-04-26, gaffigan@sfos.uaf.edu) + oWKT.erase(std::remove(oWKT.begin(), oWKT.end(), '\\'), oWKT.end()); + if( oWKT.length() > 0 + && !EQUALN(oWKT.c_str(),"GEOGCS",6) + && !EQUALN(oWKT.c_str(),"PROJCS",6) + && !EQUALN(oWKT.c_str(),"LOCAL_CS",8) ) + { + OGRSpatialReference oSRS; + + if( oSRS.SetFromUserInput( oWKT.c_str() ) != OGRERR_NONE ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to recognise 'spatial_ref' value of: %s", + oWKT.c_str() ); + oWKT = ""; + } + else + { + char *pszProcessedWKT = NULL; + oSRS.exportToWkt( &pszProcessedWKT ); + oWKT = pszProcessedWKT; + CPLFree( pszProcessedWKT ); + } + } + +/* -------------------------------------------------------------------- */ +/* Collect Metadata. */ +/* -------------------------------------------------------------------- */ + HarvestMetadata( this, poFileInfo ); +} + +/************************************************************************/ +/* HarvestMaps() */ +/************************************************************************/ + +void DODSDataset::HarvestMaps( string oVarName, string oCE ) + +{ + BaseType *poDDSDef = get_variable( GetDDS(), oVarName ); + if( poDDSDef == NULL || poDDSDef->type() != dods_grid_c ) + return; + +/* -------------------------------------------------------------------- */ +/* Get the grid. */ +/* -------------------------------------------------------------------- */ + Grid *poGrid = NULL; + poGrid = dynamic_cast( poDDSDef ); + +/* -------------------------------------------------------------------- */ +/* Get the map arrays for x and y. */ +/* -------------------------------------------------------------------- */ + Array *poXMap = NULL, *poYMap = NULL; + int iXDim = GetDimension( oCE, "x", NULL ); + int iYDim = GetDimension( oCE, "y", NULL ); + int iMap; + Grid::Map_iter iterMap; + + for( iterMap = poGrid->map_begin(), iMap = 0; + iterMap != poGrid->map_end(); + iterMap++, iMap++ ) + { + if( iMap == iXDim ) + poXMap = dynamic_cast(*iterMap); + else if( iMap == iYDim ) + poYMap = dynamic_cast(*iterMap); + } + + if( poXMap == NULL || poYMap == NULL ) + return; + + if( poXMap->var()->type() != dods_float64_c + || poYMap->var()->type() != dods_float64_c ) + { + CPLDebug( "DODS", "Ignoring Grid Map - not a supported data type." ); + return; + } + +/* -------------------------------------------------------------------- */ +/* TODO: We ought to validate the dimension of the map against our */ +/* expected size. */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- + * Fetch maps. We need to construct a seperate request like: + * http://dods.gso.uri.edu/cgi-bin/nph-dods/MCSST/Northwest_Atlantic/5km/raw/1982/9/m82258070000.pvu.Z?dsp_band_1.lat,dsp_band_1.lon + * + * to fetch just the maps, and not the actual dataset. + * -------------------------------------------------------------------- */ +/* -------------------------------------------------------------------- */ +/* Build constraint expression. */ +/* -------------------------------------------------------------------- */ + string constraint; + string xdimname = "lon"; + string ydimname = "lat"; + + + constraint = oVarName + "." + xdimname + "," + + oVarName + "." + ydimname; + +/* -------------------------------------------------------------------- */ +/* Request data from server. */ +/* -------------------------------------------------------------------- */ + DataDDS data( poBaseTypeFactory ); + + GetConnect()->request_data(data, constraint ); + +/* -------------------------------------------------------------------- */ +/* Get the DataDDS Array object from the response. */ +/* -------------------------------------------------------------------- */ + BaseType *poBtX = get_variable(data, oVarName + "." + xdimname ); + BaseType *poBtY = get_variable(data, oVarName + "." + ydimname ); + if (!poBtX || !poBtY + || poBtX->type() != dods_array_c + || poBtY->type() != dods_array_c ) + return; + + Array *poAX = dynamic_cast(poBtX); + Array *poAY = dynamic_cast(poBtY); + +/* -------------------------------------------------------------------- */ +/* Pre-initialize the output buffer to zero. */ +/* -------------------------------------------------------------------- */ + double *padfXMap, *padfYMap; + + padfXMap = (double *) CPLCalloc(sizeof(double),nRasterXSize ); + padfYMap = (double *) CPLCalloc(sizeof(double),nRasterYSize ); + +/* -------------------------------------------------------------------- */ +/* Dump the contents of the Array data into our output image */ +/* buffer. */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +/* The cast below is the correct way to handle the problem. */ +/* The (void *) cast is to avoid a GCC warning like: */ +/* "warning: dereferencing type-punned pointer will break \ */ +/* strict-aliasing rules" */ +/* */ +/* (void *) introduces a compatible intermediate type in the cast list.*/ +/* -------------------------------------------------------------------- */ + poAX->buf2val( (void **) (void *) &padfXMap ); + poAY->buf2val( (void **) (void *) &padfYMap ); + +/* -------------------------------------------------------------------- */ +/* Compute a geotransform from the maps. We are implicitly */ +/* assuming the maps are linear and refer to the center of the */ +/* pixels. */ +/* -------------------------------------------------------------------- */ + bGotGeoTransform = TRUE; + + // pixel size. + adfGeoTransform[1] = (padfXMap[nRasterXSize-1] - padfXMap[0]) + / (nRasterXSize-1); + adfGeoTransform[5] = (padfYMap[nRasterYSize-1] - padfYMap[0]) + / (nRasterYSize-1); + + // rotational coefficients. + adfGeoTransform[2] = 0.0; + adfGeoTransform[4] = 0.0; + + // origin/ + adfGeoTransform[0] = padfXMap[0] - adfGeoTransform[1] * 0.5; + adfGeoTransform[3] = padfYMap[0] - adfGeoTransform[5] * 0.5; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset * +DODSDataset::Open(GDALOpenInfo *poOpenInfo) +{ + if( !EQUALN(poOpenInfo->pszFilename,"http://",7) + && !EQUALN(poOpenInfo->pszFilename,"https://",8) ) + return NULL; + + + DODSDataset *poDS = new DODSDataset(); + char **papszVarConstraintList = NULL; + + poDS->nRasterXSize = 0; + poDS->nRasterYSize = 0; + + try { +/* -------------------------------------------------------------------- */ +/* Split the URL from the projection/CE portion of the name. */ +/* -------------------------------------------------------------------- */ + string oWholeName( poOpenInfo->pszFilename ); + string oVarList; + string::size_type t_char; + + t_char = oWholeName.find('?'); + if( t_char == string::npos ) + { + oVarList = ""; + poDS->oURL = oWholeName; + } + else + { + poDS->oURL = oWholeName.substr(0,t_char); + oVarList = oWholeName.substr(t_char+1,oWholeName.length()); + } + +/* -------------------------------------------------------------------- */ +/* Get the AISConnect instance and the DAS and DDS for this */ +/* server. */ +/* -------------------------------------------------------------------- */ + poDS->poConnect = poDS->connect_to_server(); + poDS->poConnect->request_das(poDS->oDAS); + poDS->poConnect->request_dds(*(poDS->poDDS)); + +/* -------------------------------------------------------------------- */ +/* If we are given a constraint/projection list, then parse it */ +/* into a list of varname/constraint pairs. Otherwise walk the */ +/* DDS and try to identify grids or arrays that are good */ +/* targets and return them in the same format. */ +/* -------------------------------------------------------------------- */ + + if( oVarList.length() == 0 ) + papszVarConstraintList = poDS->CollectBandsFromDDS(); + else + papszVarConstraintList = poDS->ParseBandsFromURL( oVarList ); + +/* -------------------------------------------------------------------- */ +/* Did we get any target variables? */ +/* -------------------------------------------------------------------- */ + if( CSLCount(papszVarConstraintList) == 0 ) + throw Error( "No apparent raster grids or arrays found in DDS."); + +/* -------------------------------------------------------------------- */ +/* For now we support only a single band. */ +/* -------------------------------------------------------------------- */ + DODSRasterBand *poBaseBand = + new DODSRasterBand(poDS, + string(papszVarConstraintList[0]), + string(papszVarConstraintList[1]), + 1 ); + + poDS->nRasterXSize = poBaseBand->GetXSize(); + poDS->nRasterYSize = poBaseBand->GetYSize(); + + poDS->SetBand(1, poBaseBand ); + + for( int iBand = 1; papszVarConstraintList[iBand*2] != NULL; iBand++ ) + { + poDS->SetBand( iBand+1, + new DODSRasterBand(poDS, + string(papszVarConstraintList[iBand*2+0]), + string(papszVarConstraintList[iBand*2+1]), + 1 ) ); + } + +/* -------------------------------------------------------------------- */ +/* Harvest DAS dataset level information including */ +/* georeferencing, and metadata. */ +/* -------------------------------------------------------------------- */ + poDS->HarvestDAS(); + +/* -------------------------------------------------------------------- */ +/* If we don't have georeferencing, look for "map" information */ +/* for a grid. */ +/* -------------------------------------------------------------------- */ + if( !poDS->bGotGeoTransform ) + { + poDS->HarvestMaps( string(papszVarConstraintList[0]), + string(papszVarConstraintList[1]) ); + } + } + + catch (Error &e) { + string msg = +"An error occurred while creating a virtual connection to the DAP server:\n"; + msg += e.get_error_message(); + CPLError(CE_Failure, CPLE_AppDefined, "%s", msg.c_str()); + delete poDS; + poDS = NULL; + } + + CSLDestroy(papszVarConstraintList); + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poDS != NULL && poOpenInfo->eAccess == GA_Update ) + { + delete poDS; + CPLError( CE_Failure, CPLE_NotSupported, + "The DODS driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr +DODSDataset::GetGeoTransform( double * padfTransform ) +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + + return bGotGeoTransform ? CE_None : CE_Failure; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char * +DODSDataset::GetProjectionRef() +{ + return oWKT.c_str(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* DODSRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* DODSRasterBand() */ +/************************************************************************/ + +DODSRasterBand::DODSRasterBand(DODSDataset *poDSIn, string oVarNameIn, + string oCEIn, int nOverviewFactorIn ) +{ + poDS = poDSIn; + + bTranspose = FALSE; + bFlipX = FALSE; + bFlipY = FALSE; + + oVarName = oVarNameIn; + oCE = oCEIn; + nOverviewFactor = nOverviewFactorIn; + eColorInterp = GCI_Undefined; + poCT = NULL; + + nOverviewCount = 0; + papoOverviewBand = NULL; + +/* -------------------------------------------------------------------- */ +/* Fetch the DDS definition, and isolate the Array. */ +/* -------------------------------------------------------------------- */ + BaseType *poDDSDef = get_variable( poDSIn->GetDDS(), oVarNameIn ); + if( poDDSDef == NULL ) + { + throw InternalErr( + CPLSPrintf( "Could not find DDS definition for variable %s.", + oVarNameIn.c_str() ) ); + return; + } + + Array *poArray = NULL; + Grid *poGrid = NULL; + + if( poDDSDef->type() == dods_grid_c ) + { + poGrid = dynamic_cast( poDDSDef ); + poArray = dynamic_cast( poGrid->array_var() ); + } + else if( poDDSDef->type() == dods_array_c ) + { + poArray = dynamic_cast( poDDSDef ); + } + else + { + throw InternalErr( + CPLSPrintf( "Variable %s is not a grid or an array.", + oVarNameIn.c_str() ) ); + } + +/* -------------------------------------------------------------------- */ +/* Determine the datatype. */ +/* -------------------------------------------------------------------- */ + + // Now grab the data type of the variable. + switch (poArray->var()->type()) { + case dods_byte_c: eDataType = GDT_Byte; break; + case dods_int16_c: eDataType = GDT_Int16; break; + case dods_uint16_c: eDataType = GDT_UInt16; break; + case dods_int32_c: eDataType = GDT_Int32; break; + case dods_uint32_c: eDataType = GDT_UInt32; break; + case dods_float32_c: eDataType = GDT_Float32; break; + case dods_float64_c: eDataType = GDT_Float64; break; + default: + throw Error("The DODS GDAL driver supports only numeric data types."); + } + +/* -------------------------------------------------------------------- */ +/* For now we hard code to assume that the two dimensions are */ +/* ysize and xsize. */ +/* -------------------------------------------------------------------- */ + if( poArray->dimensions() < 2 ) + { + throw Error("Variable does not have even 2 dimensions. For now this is required." ); + } + + int nXDir = 1, nYDir = 1; + int iXDim = GetDimension( oCE, "x", &nXDir ); + int iYDim = GetDimension( oCE, "y", &nYDir ); + + if( iXDim == -1 || iYDim == -1 ) + { + throw Error("Missing [x] or [y] in constraint." ); + } + + Array::Dim_iter x_dim = poArray->dim_begin() + iXDim; + Array::Dim_iter y_dim = poArray->dim_begin() + iYDim; + + nRasterXSize = poArray->dimension_size( x_dim ) / nOverviewFactor; + nRasterYSize = poArray->dimension_size( y_dim ) / nOverviewFactor; + + bTranspose = iXDim < iYDim; + + bFlipX = (nXDir == -1); + bFlipY = (nYDir == -1); + +/* -------------------------------------------------------------------- */ +/* Decide on a block size. We aim for a block size of roughly */ +/* 256K. This should be a big enough chunk to justify a */ +/* roundtrip to get the data, but small enough to avoid reading */ +/* too much data. */ +/* -------------------------------------------------------------------- */ + int nBytesPerPixel = GDALGetDataTypeSize( eDataType ) / 8; + + if( nBytesPerPixel == 1 ) + { + nBlockXSize = 1024; + nBlockYSize= 256; + } + else if( nBytesPerPixel == 2 ) + { + nBlockXSize = 512; + nBlockYSize= 256; + } + else if( nBytesPerPixel == 4 ) + { + nBlockXSize = 512; + nBlockYSize= 128; + } + else + { + nBlockXSize = 256; + nBlockYSize= 128; + } + + if( nRasterXSize < nBlockXSize * 2 ) + nBlockXSize = nRasterXSize; + + if( nRasterYSize < nBlockYSize * 2 ) + nBlockYSize = nRasterYSize; + +/* -------------------------------------------------------------------- */ +/* Get other information from the DAS for this band. */ +/* -------------------------------------------------------------------- */ + if( nOverviewFactorIn == 1 ) + HarvestDAS(); + +/* -------------------------------------------------------------------- */ +/* Create overview band objects. */ +/* -------------------------------------------------------------------- */ + if( nOverviewFactorIn == 1 ) + { + int iOverview; + + nOverviewCount = 0; + papoOverviewBand = (DODSRasterBand **) + CPLCalloc( sizeof(void*), 8 ); + + for( iOverview = 1; iOverview < 8; iOverview++ ) + { + int nThisFactor = 1 << iOverview; + + if( nRasterXSize / nThisFactor < 128 + && nRasterYSize / nThisFactor < 128 ) + break; + + papoOverviewBand[nOverviewCount++] = + new DODSRasterBand( poDSIn, oVarNameIn, oCEIn, + nThisFactor ); + + papoOverviewBand[nOverviewCount-1]->bFlipX = bFlipX; + papoOverviewBand[nOverviewCount-1]->bFlipY = bFlipY; + } + } +} + +/************************************************************************/ +/* ~DODSRasterBand() */ +/************************************************************************/ + +DODSRasterBand::~DODSRasterBand() + +{ + for( int iOverview = 0; iOverview < nOverviewCount; iOverview++ ) + delete papoOverviewBand[iOverview]; + CPLFree( papoOverviewBand ); + + if( poCT ) + delete poCT; +} + +/************************************************************************/ +/* HarvestDAS() */ +/************************************************************************/ + +void DODSRasterBand::HarvestDAS() + +{ + DODSDataset *poDODS = dynamic_cast(poDS); + +/* -------------------------------------------------------------------- */ +/* Try and fetch the corresponding DAS subtree if it exists. */ +/* -------------------------------------------------------------------- */ +#ifdef LIBDAP_39 + AttrTable *poBandInfo = poDODS->GetDAS().get_table( oVarName ); +#else + AttrTable *poBandInfo = poDODS->GetDAS().find_container( oVarName ); +#endif + + if( poBandInfo == NULL ) + { + CPLDebug( "DODS", "No band DAS info for %s.", oVarName.c_str() ); + return; + } + +/* -------------------------------------------------------------------- */ +/* collect metadata. */ +/* -------------------------------------------------------------------- */ + poDODS->HarvestMetadata( this, poBandInfo ); + +/* -------------------------------------------------------------------- */ +/* Get photometric interpretation. */ +/* -------------------------------------------------------------------- */ + string oValue; + int iInterp; + + oValue = StripQuotes(poBandInfo->get_attr( "PhotometricInterpretation" )); + for( iInterp = 0; iInterp < (int) GCI_Max; iInterp++ ) + { + if( oValue == GDALGetColorInterpretationName( + (GDALColorInterp) iInterp ) ) + eColorInterp = (GDALColorInterp) iInterp; + } + +/* -------------------------------------------------------------------- */ +/* Get band description. */ +/* -------------------------------------------------------------------- */ + oValue = StripQuotes(poBandInfo->get_attr( "Description" )); + if( oValue != "" ) + SetDescription( oValue.c_str() ); + +/* -------------------------------------------------------------------- */ +/* Try missing_value */ +/* -------------------------------------------------------------------- */ + oValue = poBandInfo->get_attr( "missing_value" ); + bNoDataSet=FALSE; + if( oValue != "" ) { + SetNoDataValue( CPLAtof(oValue.c_str()) ); + } else { + +/* -------------------------------------------------------------------- */ +/* Try _FillValue */ +/* -------------------------------------------------------------------- */ + oValue = poBandInfo->get_attr( "_FillValue" ); + if( oValue != "" ) { + SetNoDataValue( CPLAtof(oValue.c_str()) ); + } + } + + + +/* -------------------------------------------------------------------- */ +/* Collect color table */ +/* -------------------------------------------------------------------- */ + AttrTable *poCTable = poBandInfo->find_container("Colormap"); + if( poCTable != NULL ) + { + AttrTable::Attr_iter dv_i; + + poCT = new GDALColorTable(); + + for( dv_i = poCTable->attr_begin(); + dv_i != poCTable->attr_end(); + dv_i++ ) + { + if( !poCTable->is_container( dv_i ) ) + continue; + + AttrTable *poColor = poCTable->get_attr_table( dv_i ); + GDALColorEntry sEntry; + + if( poColor == NULL ) + continue; + + sEntry.c1 = atoi(poColor->get_attr( "red" ).c_str()); + sEntry.c2 = atoi(poColor->get_attr( "green" ).c_str()); + sEntry.c3 = atoi(poColor->get_attr( "blue" ).c_str()); + if( poColor->get_attr( "alpha" ) == "" ) + sEntry.c4 = 255; + else + sEntry.c4 = atoi(poColor->get_attr( "alpha" ).c_str()); + + poCT->SetColorEntry( poCT->GetColorEntryCount(), &sEntry ); + } + } + +/* -------------------------------------------------------------------- */ +/* Check for flipping instructions. */ +/* -------------------------------------------------------------------- */ + oValue = StripQuotes(poBandInfo->get_attr( "FlipX" )); + if( oValue != "no" && oValue != "NO" && oValue != "" ) + bFlipX = TRUE; + + oValue = StripQuotes(poBandInfo->get_attr( "FlipY" )); + if( oValue != "no" && oValue != "NO" && oValue != "" ) + bFlipY = TRUE; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr +DODSRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) +{ + DODSDataset *poDODS = dynamic_cast(poDS); + int nBytesPerPixel = GDALGetDataTypeSize(eDataType) / 8; + +/* -------------------------------------------------------------------- */ +/* What is the actual rectangle we want to read? We can't read */ +/* full blocks that go off the edge of the original data. */ +/* -------------------------------------------------------------------- */ + int nXOff, nXSize, nYOff, nYSize; + + nXOff = nBlockXOff * nBlockXSize; + nYOff = nBlockYOff * nBlockYSize; + nXSize = nBlockXSize; + nYSize = nBlockYSize; + + if( nXOff + nXSize > nRasterXSize ) + nXSize = nRasterXSize - nXOff; + if( nYOff + nYSize > nRasterYSize ) + nYSize = nRasterYSize - nYOff; + +/* -------------------------------------------------------------------- */ +/* If we are working with a flipped image, we need to transform */ +/* the requested window accordingly. */ +/* -------------------------------------------------------------------- */ + if( bFlipY ) + nYOff = (nRasterYSize - nYOff - nYSize); + + if( bFlipX ) + nXOff = (nRasterXSize - nXOff - nXSize); + +/* -------------------------------------------------------------------- */ +/* Prepare constraint expression for this request. */ +/* -------------------------------------------------------------------- */ + string x_constraint, y_constraint, raw_constraint, final_constraint; + + x_constraint = + CPLSPrintf( "[%d:%d:%d]", + nXOff * nOverviewFactor, + nOverviewFactor, + (nXOff + nXSize - 1) * nOverviewFactor ); + y_constraint = + CPLSPrintf( "[%d:%d:%d]", + nYOff * nOverviewFactor, + nOverviewFactor, + (nYOff + nYSize - 1) * nOverviewFactor ); + + raw_constraint = oVarName + oCE; + + final_constraint = poDODS->SubConstraint( raw_constraint, + x_constraint, + y_constraint ); + + CPLDebug( "DODS", "constraint = %s", final_constraint.c_str() ); + +/* -------------------------------------------------------------------- */ +/* Request data from server. */ +/* -------------------------------------------------------------------- */ + try { + DataDDS data( poDODS->poBaseTypeFactory ); + + poDODS->GetConnect()->request_data(data, final_constraint ); + +/* -------------------------------------------------------------------- */ +/* Get the DataDDS Array object from the response. */ +/* -------------------------------------------------------------------- */ + BaseType *poBt = get_variable(data, oVarName ); + if (!poBt) + throw Error(string("I could not read the variable '") + + oVarName + string("' from the data source at:\n") + + poDODS->GetUrl() ); + + Array *poA; + switch (poBt->type()) { + case dods_grid_c: + poA = dynamic_cast(dynamic_cast(poBt)->array_var()); + break; + + case dods_array_c: + poA = dynamic_cast(poBt); + break; + + default: + throw InternalErr("Expected an Array or Grid variable!"); + } + +/* -------------------------------------------------------------------- */ +/* Pre-initialize the output buffer to zero. */ +/* -------------------------------------------------------------------- */ + if( nXSize < nBlockXSize || nYSize < nBlockYSize ) + memset( pImage, 0, + nBlockXSize * nBlockYSize + * GDALGetDataTypeSize(eDataType) / 8 ); + +/* -------------------------------------------------------------------- */ +/* Dump the contents of the Array data into our output image buffer.*/ +/* */ +/* -------------------------------------------------------------------- */ + poA->buf2val(&pImage); // !Suck the data out of the Array! + +/* -------------------------------------------------------------------- */ +/* If the [x] dimension comes before [y], we need to transpose */ +/* the data we just got back. */ +/* -------------------------------------------------------------------- */ + if( bTranspose ) + { + GByte *pabyDataCopy; + int iY; + + CPLDebug( "DODS", "Applying transposition" ); + + // make a copy of the original + pabyDataCopy = (GByte *) + CPLMalloc(nBytesPerPixel * nXSize * nYSize); + memcpy( pabyDataCopy, pImage, nBytesPerPixel * nXSize * nYSize ); + memset( pImage, 0, nBytesPerPixel * nXSize * nYSize ); + + for( iY = 0; iY < nYSize; iY++ ) + { + GDALCopyWords( pabyDataCopy + iY * nBytesPerPixel, + eDataType, nBytesPerPixel * nYSize, + ((GByte *) pImage) + iY * nXSize * nBytesPerPixel, + eDataType, nBytesPerPixel, nXSize ); + } + + // cleanup + CPLFree( pabyDataCopy ); + } + +/* -------------------------------------------------------------------- */ +/* Do we need "x" flipping? */ +/* -------------------------------------------------------------------- */ + if( bFlipX ) + { + GByte *pabyDataCopy; + int iY; + + CPLDebug( "DODS", "Applying X flip." ); + + // make a copy of the original + pabyDataCopy = (GByte *) + CPLMalloc(nBytesPerPixel * nXSize * nYSize); + memcpy( pabyDataCopy, pImage, nBytesPerPixel * nXSize * nYSize ); + memset( pImage, 0, nBytesPerPixel * nXSize * nYSize ); + + for( iY = 0; iY < nYSize; iY++ ) + { + GDALCopyWords( pabyDataCopy + iY*nXSize*nBytesPerPixel, + eDataType, nBytesPerPixel, + ((GByte *) pImage) + ((iY+1)*nXSize-1)*nBytesPerPixel, + eDataType, -nBytesPerPixel, nXSize ); + } + + // cleanup + CPLFree( pabyDataCopy ); + } + +/* -------------------------------------------------------------------- */ +/* Do we need "y" flipping? */ +/* -------------------------------------------------------------------- */ + if( bFlipY ) + { + GByte *pabyDataCopy; + int iY; + + CPLDebug( "DODS", "Applying Y flip." ); + + // make a copy of the original + pabyDataCopy = (GByte *) + CPLMalloc(nBytesPerPixel * nXSize * nYSize); + memcpy( pabyDataCopy, pImage, nBytesPerPixel * nXSize * nYSize ); + + for( iY = 0; iY < nYSize; iY++ ) + { + GDALCopyWords( pabyDataCopy + iY*nXSize*nBytesPerPixel, + eDataType, nBytesPerPixel, + ((GByte *) pImage) + (nYSize-iY-1)*nXSize*nBytesPerPixel, + eDataType, nBytesPerPixel, + nXSize ); + } + + // cleanup + CPLFree( pabyDataCopy ); + } + +/* -------------------------------------------------------------------- */ +/* If we only read a partial block we need to re-organize the */ +/* data. */ +/* -------------------------------------------------------------------- */ + if( nXSize < nBlockXSize ) + { + int iLine; + + for( iLine = nYSize-1; iLine >= 0; iLine-- ) + { + memmove( ((GByte *) pImage) + iLine*nBlockXSize*nBytesPerPixel, + ((GByte *) pImage) + iLine * nXSize * nBytesPerPixel, + nBytesPerPixel * nXSize ); + memset( ((GByte *) pImage) + + (iLine*nBlockXSize + nXSize)*nBytesPerPixel, + 0, nBytesPerPixel * (nBlockXSize - nXSize) ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Catch exceptions */ +/* -------------------------------------------------------------------- */ + catch (Error &e) { + CPLError(CE_Failure, CPLE_AppDefined, "%s", e.get_error_message().c_str()); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int DODSRasterBand::GetOverviewCount() + +{ + return nOverviewCount; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *DODSRasterBand::GetOverview( int iOverview ) + +{ + if( iOverview < 0 || iOverview >= nOverviewCount ) + return NULL; + else + return papoOverviewBand[iOverview]; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp DODSRasterBand::GetColorInterpretation() + +{ + return eColorInterp; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *DODSRasterBand::GetColorTable() + +{ + return poCT; +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr DODSRasterBand::SetNoDataValue( double dfNoData ) + +{ + bNoDataSet = TRUE; + dfNoDataValue = dfNoData; + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double DODSRasterBand::GetNoDataValue( int * pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bNoDataSet; + + return dfNoDataValue; +} + +/************************************************************************/ +/* GDALRegister_DODS() */ +/************************************************************************/ + +void +GDALRegister_DODS() +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("GDAL/DODS driver")) + return; + + if( GDALGetDriverByName( "DODS" ) == NULL ) { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "DODS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "DAP 3.x servers" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#DODS" ); + + poDriver->pfnOpen = DODSDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/dods/frmt_dods.html b/bazaar/plugin/gdal/frmts/dods/frmt_dods.html new file mode 100644 index 000000000..4c39f0c40 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dods/frmt_dods.html @@ -0,0 +1,185 @@ + + +DODS -- OPeNDAP Grid Client + + + + +

DODS -- OPeNDAP Grid Client

+ +GDAL optionally includes read support for 2D grids and arrays via the OPeNDAP +(DODS) protocol.

+ +

Dataset Naming

+ +The full dataset name specification consists of the OPeNDAP dataset +url, the full path to the desired array or grid variable, and an indicator +of the array indices to be accessed.

+ +For instance, if the url http://maps.gdal.org/daac-bin/nph-hdf/3B42.HDF.dds +returns a DDS definition like this:

+ +

+Dataset {
+  Structure {
+    Structure {
+      Float64 percipitate[scan = 5][longitude = 360][latitude = 80];
+      Float64 relError[scan = 5][longitude = 360][latitude = 80];
+    } PlanetaryGrid;
+  } DATA_GRANULE;
+} 3B42.HDF;
+
+

+ +then the percipitate grid can be accessed using the following GDAL +dataset name:

+ +http://maps.gdal.org/daac-bin/nph-hdf/3B42.HDF?DATA_GRANULE.PlanetaryGrid.percipitate[0][x][y] +

+ +The full path to the grid or array to be accessed needs to be specified (not +counting the outer Dataset name). GDAL needs to know which indices of +the array to treat as x (longitude or easting) and y (latitude or northing). +Any other dimensions need to be restricted to a single value.

+ +In cases of data servers with only 2D arrays and grids as immediate +children of the Dataset it may not be necessary to name the grid or array +variable.

+ +In cases where there are a number of 2D arrays or grids at the dataset +level, they may be each automatically treated as seperate bands.

+ +

Specialized AIS/DAS Metadata

+ +A variety of information will be transported via the DAS describing +the dataset. Some DODS drivers (such as the GDAL based one!) already +return the following DAS information, but in other cases it can be +supplied locally using the AIX mechanism. See the DODS documentation +for details of how the AIS mechanism works.

+ +

+Attributes {
+
+    GLOBAL { 
+        Float64 Northernmost_Northing 71.1722;
+	Float64 Southernmost_Northing  4.8278;
+	Float64	Easternmost_Easting  -27.8897;
+	Float64	Westernmost_Easting -112.11;
+        Float64 GeoTransform "71.1722 0.001 0.0 -112.11 0.0 -0.001";
+	String spatial_ref "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]]";
+        Metadata { 
+          String TIFFTAG_XRESOLUTION "400";
+          String TIFFTAG_YRESOLUTION "400";
+          String TIFFTAG_RESOLUTIONUNIT "2 (pixels/inch)";
+        }
+    }
+
+    band_1 {
+        String Description "...";
+        String 
+    }
+}
+
+ +

Dataset

+ +There will be an object in the DAS named GLOBAL containing +attributes of the dataset as a whole.

+ +It will have the following subitems:

+ +

    + +
  • Northernmost_Northing: The latitude or northing of the north edge of the image.

    +

  • Southernmost_Northing: The latitude or northing of the south edge of the image.

    +

  • Easternmost_Easting: The longitude or easting of the east edge of the image.

    +

  • Westernmost_Easting: The longitude or easting of the west edge of the image.

    + +

  • GeoTransform: The six parameters defining the affine transformation +between pixel/line space and georeferenced space if applicable. Stored +as a single string with values seperated by sapces. Note this +allows for rotated or sheared images. (optional)

    + +

  • SpatialRef: The OpenGIS WKT description of the coordinate system. +If not provided it will be assumed that the coordinate system is WGS84. +(optional)

    + +

  • Metadata: a container with a list of string attributes for +each available metadata item. The metadata item keyword name will be used +as the attribute name. Metadata values will always be strings. (optional) + +
  • address GCPs

    + +

+ +Note that the edge northing and easting values can be computed based +on the grid size and the geotransform. They are included primarily as +extra documentation that is easier to interprete by a user than the +GeoTransform. They will also be used to compute a GeoTransform internally +if one is note provided, but if both are provided the GeoTransform will +take precidence.

+ +

Band

+ +There will be an object in the DAS named after each band containing +attribute of the specific band.

+ +It will have the following subitems:

+ +

    + +
  • Metadata: a container with a list of string attributes for +each available metadata item. The metadata item keyword name will be used +as the attribute name. Metadata values will always be strings. (optional)

    + +

  • PhotometricInterpretation: Will have a string value that is +one of "Undefined", "GrayIndex", "PaletteIndex", "Red", "Green", +"Blue", "Alpha", "Hue", "Saturation", "Lightness", "Cyan", "Magenta", +"Yellow" or "Black". (optional)

    + +

  • units: name of units (one of "ft" or "m" for elevation data). +(optional)

    + +

  • add_offset: Offset to be applied to pixel values (after +scale_factor) to compute a "real" pixel value. Defaults to 0.0. (optional)

    + +

  • scale_factor: Scale to be applied to pixel values (before +add_offset) to compute "real" pixel value. Defaults to 1.0. (optional)

    + +

  • Description: Descriptive text about the band. (optional)

    + +

  • missing_value: The nodata value for the raster. (optional)

    + +

  • Colormap: A container with a subcontainer for each color in +the color table, looking like the following. The alpha component is +optional and assumed to be 255 (opaque) if not provided.

    +

    									
    +    Colormap { 
    +      Color_0 { 
    +        Byte red 0;
    +        Byte green 0;
    +        Byte blue 0;
    +        Byte alpha 255;
    +      }
    +      Color_1 { 
    +        Byte red 255;
    +        Byte green 255;
    +        Byte blue 255;
    +        Byte alpha 255;
    +      }
    +      ...
    +    }
    +
    + +
+ +
+ +See Also:

+ +

+ + + diff --git a/bazaar/plugin/gdal/frmts/dods/makefile.vc b/bazaar/plugin/gdal/frmts/dods/makefile.vc new file mode 100644 index 000000000..308ba1e19 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dods/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = dodsdataset2.obj + +GDAL_ROOT = ..\.. + +EXTRAFLAGS = -I$(DODS_DIR) -I$(DODS_DIR)\include -I$(DODS_DIR)\include\gnu -I$(DODS_DIR)\include\xdr -I$(DODS_DIR)\include\pthreads /DWIN32 /DWIN32_LEAN_AND_MEAN -DFRMT_dods $(DODS_FLAGS) /GR + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/dted/GNUmakefile b/bazaar/plugin/gdal/frmts/dted/GNUmakefile new file mode 100644 index 000000000..dc7242af9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dted/GNUmakefile @@ -0,0 +1,17 @@ + + +include ../../GDALmake.opt + +OBJ = dted_api.o dteddataset.o dted_create.o dted_ptstream.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +dted_test$(EXE): dted_test.$(OBJ_EXT) + $(LD) $(LDFLAGS) dted_test.$(OBJ_EXT) $(CONFIG_LIBS) -o dted_test$(EXE) diff --git a/bazaar/plugin/gdal/frmts/dted/dted_api.c b/bazaar/plugin/gdal/frmts/dted/dted_api.c new file mode 100644 index 000000000..5573946ef --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dted/dted_api.c @@ -0,0 +1,1076 @@ +/****************************************************************************** + * $Id: dted_api.c 28831 2015-04-01 16:46:05Z rouault $ + * + * Project: DTED Translator + * Purpose: Implementation of DTED/CDED access functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2007-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "dted_api.h" + +#ifndef AVOID_CPL +CPL_CVSID("$Id: dted_api.c 28831 2015-04-01 16:46:05Z rouault $"); +#endif + +static int bWarnedTwoComplement = FALSE; + +static void DTEDDetectVariantWithMissingColumns(DTEDInfo* psDInfo); + +/************************************************************************/ +/* DTEDGetField() */ +/* */ +/* Extract a field as a zero terminated string. Address is */ +/* deliberately 1 based so the getfield arguments will be the */ +/* same as the numbers in the file format specification. */ +/************************************************************************/ + +static +char *DTEDGetField( char szResult[81], const char *pachRecord, int nStart, int nSize ) + +{ + CPLAssert( nSize < 81 ); + memcpy( szResult, pachRecord + nStart - 1, nSize ); + szResult[nSize] = '\0'; + + return szResult; +} + +/************************************************************************/ +/* StripLeadingZeros() */ +/* */ +/* Return a pointer to the first non-zero character in BUF. */ +/* BUF must be null terminated. */ +/* If buff is all zeros, then it will point to the last non-zero */ +/************************************************************************/ + +static const char* stripLeadingZeros(const char* buf) +{ + const char* ptr = buf; + + /* Go until we run out of characters or hit something non-zero */ + + while( *ptr == '0' && *(ptr+1) != '\0' ) + { + ptr++; + } + + return ptr; +} + +/************************************************************************/ +/* DTEDOpen() */ +/************************************************************************/ + +DTEDInfo * DTEDOpen( const char * pszFilename, + const char * pszAccess, + int bTestOpen ) + +{ + VSILFILE* fp; + +/* -------------------------------------------------------------------- */ +/* Open the physical file. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszAccess,"r") || EQUAL(pszAccess,"rb") ) + pszAccess = "rb"; + else + pszAccess = "r+b"; + + fp = VSIFOpenL( pszFilename, pszAccess ); + + if( fp == NULL ) + { + if( !bTestOpen ) + { +#ifndef AVOID_CPL + CPLError( CE_Failure, CPLE_OpenFailed, +#else + fprintf( stderr, +#endif + "Failed to open file %s.", + pszFilename ); + } + + return NULL; + } + + return DTEDOpenEx( fp, pszFilename, pszAccess, bTestOpen ); +} + +/************************************************************************/ +/* DTEDOpenEx() */ +/************************************************************************/ + +DTEDInfo * DTEDOpenEx( VSILFILE *fp, + const char * pszFilename, + const char * pszAccess, + int bTestOpen ) + +{ + char achRecord[DTED_UHL_SIZE]; + DTEDInfo *psDInfo = NULL; + double dfLLOriginX, dfLLOriginY; + int deg = 0; + int min = 0; + int sec = 0; + int bSwapLatLong = FALSE; + char szResult[81]; + int bIsWeirdDTED; + char chHemisphere; + +/* -------------------------------------------------------------------- */ +/* Read, trying to find the UHL record. Skip VOL or HDR */ +/* records if they are encountered. */ +/* -------------------------------------------------------------------- */ + do + { + if( VSIFReadL( achRecord, 1, DTED_UHL_SIZE, fp ) != DTED_UHL_SIZE ) + { + if( !bTestOpen ) + { +#ifndef AVOID_CPL + CPLError( CE_Failure, CPLE_OpenFailed, +#else + fprintf( stderr, +#endif + "Unable to read header, %s is not DTED.", + pszFilename ); + } + VSIFCloseL( fp ); + return NULL; + } + + } while( EQUALN(achRecord,"VOL",3) || EQUALN(achRecord,"HDR",3) ); + + if( !EQUALN(achRecord,"UHL",3) ) + { + if( !bTestOpen ) + { +#ifndef AVOID_CPL + CPLError( CE_Failure, CPLE_OpenFailed, +#else + fprintf( stderr, +#endif + "No UHL record. %s is not a DTED file.", + pszFilename ); + } + VSIFCloseL( fp ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create and initialize the DTEDInfo structure. */ +/* -------------------------------------------------------------------- */ + psDInfo = (DTEDInfo *) CPLCalloc(1,sizeof(DTEDInfo)); + + psDInfo->fp = fp; + + psDInfo->bUpdate = EQUAL(pszAccess,"r+b"); + psDInfo->bRewriteHeaders = FALSE; + + psDInfo->nUHLOffset = (int)VSIFTellL( fp ) - DTED_UHL_SIZE; + psDInfo->pachUHLRecord = (char *) CPLMalloc(DTED_UHL_SIZE); + memcpy( psDInfo->pachUHLRecord, achRecord, DTED_UHL_SIZE ); + + psDInfo->nDSIOffset = (int)VSIFTellL( fp ); + psDInfo->pachDSIRecord = (char *) CPLMalloc(DTED_DSI_SIZE); + VSIFReadL( psDInfo->pachDSIRecord, 1, DTED_DSI_SIZE, fp ); + + psDInfo->nACCOffset = (int)VSIFTellL( fp ); + psDInfo->pachACCRecord = (char *) CPLMalloc(DTED_ACC_SIZE); + VSIFReadL( psDInfo->pachACCRecord, 1, DTED_ACC_SIZE, fp ); + + if( !EQUALN(psDInfo->pachDSIRecord,"DSI",3) + || !EQUALN(psDInfo->pachACCRecord,"ACC",3) ) + { +#ifndef AVOID_CPL + CPLError( CE_Failure, CPLE_OpenFailed, +#else + fprintf( stderr, +#endif + "DSI or ACC record missing. DTED access to\n%s failed.", + pszFilename ); + + DTEDClose(psDInfo); + return NULL; + } + + psDInfo->nDataOffset = (int)VSIFTellL( fp ); + + /* DTED3 file from http://www.falconview.org/trac/FalconView/downloads/20 */ + /* (co_elevation.zip) has really weird offsets that don't comply with the 89020B specification */ + bIsWeirdDTED = achRecord[4] == ' '; + +/* -------------------------------------------------------------------- */ +/* Parse out position information. Note that we are extracting */ +/* the top left corner of the top left pixel area, not the */ +/* center of the area. */ +/* -------------------------------------------------------------------- */ + if (!bIsWeirdDTED) + { + psDInfo->dfPixelSizeX = + atoi(DTEDGetField(szResult,achRecord,21,4)) / 36000.0; + + psDInfo->dfPixelSizeY = + atoi(DTEDGetField(szResult,achRecord,25,4)) / 36000.0; + + psDInfo->nXSize = atoi(DTEDGetField(szResult,achRecord,48,4)); + psDInfo->nYSize = atoi(DTEDGetField(szResult,achRecord,52,4)); + } + else + { + psDInfo->dfPixelSizeX = + atoi(DTEDGetField(szResult,achRecord,41,4)) / 36000.0; + + psDInfo->dfPixelSizeY = + atoi(DTEDGetField(szResult,achRecord,45,4)) / 36000.0; + + psDInfo->nXSize = atoi(DTEDGetField(szResult,psDInfo->pachDSIRecord,563,4)); + psDInfo->nYSize = atoi(DTEDGetField(szResult,psDInfo->pachDSIRecord,567,4)); + } + + if (psDInfo->nXSize <= 0 || psDInfo->nYSize <= 0) + { +#ifndef AVOID_CPL + CPLError( CE_Failure, CPLE_OpenFailed, +#else + fprintf( stderr, +#endif + "Invalid dimensions : %d x %d. DTED access to\n%s failed.", + psDInfo->nXSize, psDInfo->nYSize, pszFilename ); + + DTEDClose(psDInfo); + return NULL; + } + + /* create a scope so I don't need to declare these up top */ + if (!bIsWeirdDTED) + { + deg = atoi(stripLeadingZeros(DTEDGetField(szResult,achRecord,5,3))); + min = atoi(stripLeadingZeros(DTEDGetField(szResult,achRecord,8,2))); + sec = atoi(stripLeadingZeros(DTEDGetField(szResult,achRecord,10,2))); + chHemisphere = achRecord[11]; + } + else + { + deg = atoi(stripLeadingZeros(DTEDGetField(szResult,achRecord,9,3))); + min = atoi(stripLeadingZeros(DTEDGetField(szResult,achRecord,12,2))); + sec = atoi(stripLeadingZeros(DTEDGetField(szResult,achRecord,14,2))); + chHemisphere = achRecord[15]; + } + + /* NOTE : The first version of MIL-D-89020 was buggy. + The latitude and longitude of the LL cornder of the UHF record was inverted. + This was fixed in MIL-D-89020 Amendement 1, but some products may be affected. + We detect this situation by looking at N/S in the longitude field and + E/W in the latitude one + */ + + dfLLOriginX = deg + min / 60.0 + sec / 3600.0; + if( chHemisphere == 'W' ) + dfLLOriginX *= -1; + else if ( chHemisphere == 'N' ) + bSwapLatLong = TRUE; + else if ( chHemisphere == 'S' ) + { + dfLLOriginX *= -1; + bSwapLatLong = TRUE; + } + + if (!bIsWeirdDTED) + { + deg = atoi(stripLeadingZeros(DTEDGetField(szResult,achRecord,13,3))); + min = atoi(stripLeadingZeros(DTEDGetField(szResult,achRecord,16,2))); + sec = atoi(stripLeadingZeros(DTEDGetField(szResult,achRecord,18,2))); + chHemisphere = achRecord[19]; + } + else + { + deg = atoi(stripLeadingZeros(DTEDGetField(szResult,achRecord,25,3))); + min = atoi(stripLeadingZeros(DTEDGetField(szResult,achRecord,28,2))); + sec = atoi(stripLeadingZeros(DTEDGetField(szResult,achRecord,30,2))); + chHemisphere = achRecord[31]; + } + + dfLLOriginY = deg + min / 60.0 + sec / 3600.0; + if( chHemisphere == 'S' || (bSwapLatLong && chHemisphere == 'W')) + dfLLOriginY *= -1; + + if (bSwapLatLong) + { + double dfTmp = dfLLOriginX; + dfLLOriginX = dfLLOriginY; + dfLLOriginY = dfTmp; + } + + psDInfo->dfULCornerX = dfLLOriginX - 0.5 * psDInfo->dfPixelSizeX; + psDInfo->dfULCornerY = dfLLOriginY - 0.5 * psDInfo->dfPixelSizeY + + psDInfo->nYSize * psDInfo->dfPixelSizeY; + + DTEDDetectVariantWithMissingColumns(psDInfo); + + return psDInfo; +} + +/************************************************************************/ +/* DTEDDetectVariantWithMissingColumns() */ +/************************************************************************/ + +static void DTEDDetectVariantWithMissingColumns(DTEDInfo* psDInfo) +{ +/* -------------------------------------------------------------------- */ +/* Some DTED files have only a subset of all possible columns. */ +/* They can declare for example 3601 columns, but in the file, */ +/* there are just columns 100->500. Detect that situation. */ +/* -------------------------------------------------------------------- */ + + GByte pabyRecordHeader[8]; + int nFirstDataBlockCount, nFirstLongitudeCount; + int nLastDataBlockCount, nLastLongitudeCount; + int nSize; + int nColByteSize = 12 + psDInfo->nYSize*2; + + VSIFSeekL(psDInfo->fp, psDInfo->nDataOffset, SEEK_SET); + if (VSIFReadL(pabyRecordHeader, 1, 8, psDInfo->fp) != 8 || + pabyRecordHeader[0] != 0252) + { + CPLDebug("DTED", "Cannot find signature of first column"); + return; + } + + nFirstDataBlockCount = (pabyRecordHeader[2] << 8) | pabyRecordHeader[3]; + nFirstLongitudeCount = (pabyRecordHeader[4] << 8) | pabyRecordHeader[5]; + + VSIFSeekL(psDInfo->fp, 0, SEEK_END); + nSize = (int)VSIFTellL(psDInfo->fp); + if (nSize < 12 + psDInfo->nYSize*2) + { + CPLDebug("DTED", "File too short"); + return; + } + + VSIFSeekL(psDInfo->fp, nSize - nColByteSize, SEEK_SET); + if (VSIFReadL(pabyRecordHeader, 1, 8, psDInfo->fp) != 8 || + pabyRecordHeader[0] != 0252) + { + CPLDebug("DTED", "Cannot find signature of last column"); + return; + } + + nLastDataBlockCount = (pabyRecordHeader[2] << 8) | pabyRecordHeader[3]; + nLastLongitudeCount = (pabyRecordHeader[4] << 8) | pabyRecordHeader[5]; + + if (nFirstDataBlockCount == 0 && + nFirstLongitudeCount == 0 && + nLastDataBlockCount == psDInfo->nXSize - 1 && + nLastLongitudeCount == psDInfo->nXSize - 1 && + nSize - psDInfo->nDataOffset == psDInfo->nXSize * nColByteSize) + { + /* This is the most standard form of DTED. Return happily now. */ + return; + } + + /* Well, we have an odd DTED file at that point */ + + psDInfo->panMapLogicalColsToOffsets = + (int*)CPLMalloc(psDInfo->nXSize * sizeof(int)); + + if (nFirstDataBlockCount == 0 && + nLastLongitudeCount - nFirstLongitudeCount == + nLastDataBlockCount - nFirstDataBlockCount && + nSize - psDInfo->nDataOffset == + (nLastLongitudeCount - nFirstLongitudeCount + 1) * nColByteSize) + { + int i; + + /* Case seen in a real-world file */ + + CPLDebug("DTED", "The file only contains data from column %d to column %d.", + nFirstLongitudeCount, nLastLongitudeCount); + + for(i = 0; i < psDInfo->nXSize; i++) + { + if (i < nFirstLongitudeCount) + psDInfo->panMapLogicalColsToOffsets[i] = -1; + else if (i <= nLastLongitudeCount) + psDInfo->panMapLogicalColsToOffsets[i] = + psDInfo->nDataOffset + (i - nFirstLongitudeCount) * nColByteSize; + else + psDInfo->panMapLogicalColsToOffsets[i] = -1; + } + } + else + { + int nPhysicalCols = (nSize - psDInfo->nDataOffset) / nColByteSize; + int i; + + /* Theoretical case for now... */ + + CPLDebug("DTED", "There columns appear to be in non sequential order. " + "Scanning the whole file."); + + for(i = 0; i < psDInfo->nXSize; i++) + { + psDInfo->panMapLogicalColsToOffsets[i] = -1; + } + + for(i = 0; i < nPhysicalCols; i++) + { + int nDataBlockCount, nLongitudeCount; + + VSIFSeekL(psDInfo->fp, psDInfo->nDataOffset + i * nColByteSize, SEEK_SET); + if (VSIFReadL(pabyRecordHeader, 1, 8, psDInfo->fp) != 8 || + pabyRecordHeader[0] != 0252) + { + CPLDebug("DTED", "Cannot find signature of physical column %d", i); + return; + } + + nDataBlockCount = (pabyRecordHeader[2] << 8) | pabyRecordHeader[3]; + if (nDataBlockCount != i) + { + CPLDebug("DTED", "Unexpected block count(%d) at physical column %d. " + "Ignoring that and going on...", + nDataBlockCount, i); + } + + nLongitudeCount = (pabyRecordHeader[4] << 8) | pabyRecordHeader[5]; + if (nLongitudeCount < 0 || nLongitudeCount >= psDInfo->nXSize) + { + CPLDebug("DTED", "Invalid longitude count (%d) at physical column %d", + nLongitudeCount, i); + return; + } + + psDInfo->panMapLogicalColsToOffsets[nLongitudeCount] = + psDInfo->nDataOffset + i * nColByteSize; + } + } +} + +/************************************************************************/ +/* DTEDReadPoint() */ +/* */ +/* Read one single sample. The coordinates are given from the */ +/* top-left corner of the file (contrary to the internal */ +/* organisation or a DTED file) */ +/************************************************************************/ + +int DTEDReadPoint( DTEDInfo * psDInfo, int nXOff, int nYOff, GInt16* panVal) +{ + int nOffset; + GByte pabyData[2]; + + if (nYOff < 0 || nXOff < 0 || nYOff >= psDInfo->nYSize || nXOff >= psDInfo->nXSize) + { +#ifndef AVOID_CPL + CPLError( CE_Failure, CPLE_AppDefined, +#else + fprintf( stderr, +#endif + "Invalid raster coordinates (%d,%d) in DTED file.\n", nXOff, nYOff); + return FALSE; + } + + if (psDInfo->panMapLogicalColsToOffsets != NULL) + { + nOffset = psDInfo->panMapLogicalColsToOffsets[nXOff]; + if( nOffset < 0 ) + { + *panVal = DTED_NODATA_VALUE; + return TRUE; + } + } + else + nOffset = psDInfo->nDataOffset + nXOff * (12+psDInfo->nYSize*2); + nOffset += 8 + 2 * (psDInfo->nYSize-1-nYOff); + + if( VSIFSeekL( psDInfo->fp, nOffset, SEEK_SET ) != 0 + || VSIFReadL( pabyData, 2, 1, psDInfo->fp ) != 1) + { +#ifndef AVOID_CPL + CPLError( CE_Failure, CPLE_FileIO, +#else + fprintf( stderr, +#endif + "Failed to seek to, or read (%d,%d) at offset %d\n" + "in DTED file.\n", + nXOff, nYOff, nOffset ); + return FALSE; + } + + *panVal = ((pabyData[0] & 0x7f) << 8) | pabyData[1]; + + if( pabyData[0] & 0x80 ) + { + *panVal *= -1; + + /* + ** It seems that some files are improperly generated in twos + ** complement form for negatives. For these, redo the job + ** in twos complement. eg. w_069_s50.dt0 + */ + if(( *panVal < -16000 ) && (*panVal != DTED_NODATA_VALUE)) + { + *panVal = (pabyData[0] << 8) | pabyData[1]; + + if( !bWarnedTwoComplement ) + { + bWarnedTwoComplement = TRUE; +#ifndef AVOID_CPL + CPLError( CE_Warning, CPLE_AppDefined, +#else + fprintf( stderr, +#endif + "The DTED driver found values less than -16000, and has adjusted\n" + "them assuming they are improperly two-complemented. No more warnings\n" + "will be issued in this session about this operation." ); + } + } + } + + return TRUE; +} + +/************************************************************************/ +/* DTEDReadProfile() */ +/* */ +/* Read one profile line. These are organized in bottom to top */ +/* order starting from the leftmost column (0). */ +/************************************************************************/ + +int DTEDReadProfile( DTEDInfo * psDInfo, int nColumnOffset, + GInt16 * panData ) +{ + return DTEDReadProfileEx( psDInfo, nColumnOffset, panData, FALSE); +} + +int DTEDReadProfileEx( DTEDInfo * psDInfo, int nColumnOffset, + GInt16 * panData, int bVerifyChecksum ) +{ + int nOffset; + int i; + GByte *pabyRecord; + int nLongitudeCount; + +/* -------------------------------------------------------------------- */ +/* Read data record from disk. */ +/* -------------------------------------------------------------------- */ + if (psDInfo->panMapLogicalColsToOffsets != NULL) + { + nOffset = psDInfo->panMapLogicalColsToOffsets[nColumnOffset]; + if( nOffset < 0 ) + { + for( i = 0; i < psDInfo->nYSize; i++ ) + { + panData[i] = DTED_NODATA_VALUE; + } + return TRUE; + } + } + else + nOffset = psDInfo->nDataOffset + nColumnOffset * (12+psDInfo->nYSize*2); + + pabyRecord = (GByte *) CPLMalloc(12 + psDInfo->nYSize*2); + + if( VSIFSeekL( psDInfo->fp, nOffset, SEEK_SET ) != 0 + || VSIFReadL( pabyRecord, (12+psDInfo->nYSize*2), 1, psDInfo->fp ) != 1) + { +#ifndef AVOID_CPL + CPLError( CE_Failure, CPLE_FileIO, +#else + fprintf( stderr, +#endif + "Failed to seek to, or read profile %d at offset %d\n" + "in DTED file.\n", + nColumnOffset, nOffset ); + CPLFree( pabyRecord ); + return FALSE; + } + + nLongitudeCount = (pabyRecord[4] << 8) | pabyRecord[5]; + if( nLongitudeCount != nColumnOffset ) + { +#ifndef AVOID_CPL + CPLError( CE_Warning, CPLE_AppDefined, +#else + fprintf( stderr, +#endif + "Longitude count (%d) of column %d doesn't match expected value.\n", + nLongitudeCount, nColumnOffset ); + } + +/* -------------------------------------------------------------------- */ +/* Translate data values from "signed magnitude" to standard */ +/* binary. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psDInfo->nYSize; i++ ) + { + panData[i] = ((pabyRecord[8+i*2] & 0x7f) << 8) | pabyRecord[8+i*2+1]; + + if( pabyRecord[8+i*2] & 0x80 ) + { + panData[i] *= -1; + + /* + ** It seems that some files are improperly generated in twos + ** complement form for negatives. For these, redo the job + ** in twos complement. eg. w_069_s50.dt0 + */ + if(( panData[i] < -16000 ) && (panData[i] != DTED_NODATA_VALUE)) + { + panData[i] = (pabyRecord[8+i*2] << 8) | pabyRecord[8+i*2+1]; + + if( !bWarnedTwoComplement ) + { + bWarnedTwoComplement = TRUE; +#ifndef AVOID_CPL + CPLError( CE_Warning, CPLE_AppDefined, +#else + fprintf( stderr, +#endif + "The DTED driver found values less than -16000, and has adjusted\n" + "them assuming they are improperly two-complemented. No more warnings\n" + "will be issued in this session about this operation." ); + } + } + } + } + + if (bVerifyChecksum) + { + unsigned int nCheckSum = 0; + unsigned int fileCheckSum; + + /* -------------------------------------------------------------------- */ + /* Verify the checksum. */ + /* -------------------------------------------------------------------- */ + + for( i = 0; i < psDInfo->nYSize*2 + 8; i++ ) + nCheckSum += pabyRecord[i]; + + fileCheckSum = (pabyRecord[8+psDInfo->nYSize*2+0] << 24) | + (pabyRecord[8+psDInfo->nYSize*2+1] << 16) | + (pabyRecord[8+psDInfo->nYSize*2+2] << 8) | + pabyRecord[8+psDInfo->nYSize*2+3]; + + if ((GIntBig)fileCheckSum > (GIntBig)(0xff * (8+psDInfo->nYSize*2))) + { + static int bWarned = FALSE; + if (! bWarned) + { + bWarned = TRUE; +#ifndef AVOID_CPL + CPLError( CE_Warning, CPLE_AppDefined, +#else + fprintf( stderr, +#endif + "The DTED driver has read from the file a checksum " + "with an impossible value (0x%X) at column %d.\n" + "Check with your file producer.\n" + "No more warnings will be issued in this session about this operation.", + fileCheckSum, nColumnOffset); + } + } + else if (fileCheckSum != nCheckSum) + { +#ifndef AVOID_CPL + CPLError( CE_Warning, CPLE_AppDefined, +#else + fprintf( stderr, +#endif + "The DTED driver has found a computed and read checksum " + "that do not match at column %d. Computed 0x%X, read 0x%X\n", + nColumnOffset, nCheckSum, fileCheckSum); + CPLFree( pabyRecord ); + return FALSE; + } + } + + CPLFree( pabyRecord ); + + return TRUE; +} + +/************************************************************************/ +/* DTEDWriteProfile() */ +/************************************************************************/ + +int DTEDWriteProfile( DTEDInfo * psDInfo, int nColumnOffset, + GInt16 * panData ) + +{ + int nOffset; + int i, nCheckSum = 0; + GByte *pabyRecord; + + if (psDInfo->panMapLogicalColsToOffsets != NULL) + { +#ifndef AVOID_CPL + CPLError( CE_Failure, CPLE_NotSupported, +#else + fprintf( stderr, +#endif + "Write to partial file not supported.\n"); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Format the data record. */ +/* -------------------------------------------------------------------- */ + pabyRecord = (GByte *) CPLMalloc(12 + psDInfo->nYSize*2); + + for( i = 0; i < psDInfo->nYSize; i++ ) + { + int nABSVal = ABS(panData[psDInfo->nYSize-i-1]); + pabyRecord[8+i*2] = (GByte) ((nABSVal >> 8) & 0x7f); + pabyRecord[8+i*2+1] = (GByte) (nABSVal & 0xff); + + if( panData[psDInfo->nYSize-i-1] < 0 ) + pabyRecord[8+i*2] |= 0x80; + } + + pabyRecord[0] = 0xaa; + pabyRecord[1] = 0; + pabyRecord[2] = (GByte) (nColumnOffset / 256); + pabyRecord[3] = (GByte) (nColumnOffset % 256); + pabyRecord[4] = (GByte) (nColumnOffset / 256); + pabyRecord[5] = (GByte) (nColumnOffset % 256); + pabyRecord[6] = 0; + pabyRecord[7] = 0; + +/* -------------------------------------------------------------------- */ +/* Compute the checksum. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psDInfo->nYSize*2 + 8; i++ ) + nCheckSum += pabyRecord[i]; + + pabyRecord[8+psDInfo->nYSize*2+0] = (GByte) ((nCheckSum >> 24) & 0xff); + pabyRecord[8+psDInfo->nYSize*2+1] = (GByte) ((nCheckSum >> 16) & 0xff); + pabyRecord[8+psDInfo->nYSize*2+2] = (GByte) ((nCheckSum >> 8) & 0xff); + pabyRecord[8+psDInfo->nYSize*2+3] = (GByte) (nCheckSum & 0xff); + +/* -------------------------------------------------------------------- */ +/* Write the record. */ +/* -------------------------------------------------------------------- */ + nOffset = psDInfo->nDataOffset + nColumnOffset * (12+psDInfo->nYSize*2); + + if( VSIFSeekL( psDInfo->fp, nOffset, SEEK_SET ) != 0 + || VSIFWriteL( pabyRecord,(12+psDInfo->nYSize*2),1,psDInfo->fp ) != 1) + { +#ifndef AVOID_CPL + CPLError( CE_Failure, CPLE_FileIO, +#else + fprintf( stderr, +#endif + "Failed to seek to, or write profile %d at offset %d\n" + "in DTED file.\n", + nColumnOffset, nOffset ); + CPLFree( pabyRecord ); + return FALSE; + } + + CPLFree( pabyRecord ); + + return TRUE; + +} + +/************************************************************************/ +/* DTEDGetMetadataLocation() */ +/************************************************************************/ + +static void DTEDGetMetadataLocation( DTEDInfo *psDInfo, + DTEDMetaDataCode eCode, + char **ppszLocation, int *pnLength ) +{ + int bIsWeirdDTED = psDInfo->pachUHLRecord[4] == ' '; + + switch( eCode ) + { + case DTEDMD_ORIGINLONG: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachUHLRecord + 8; + else + *ppszLocation = psDInfo->pachUHLRecord + 4; + *pnLength = 8; + break; + + case DTEDMD_ORIGINLAT: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachUHLRecord + 24; + else + *ppszLocation = psDInfo->pachUHLRecord + 12; + *pnLength = 8; + break; + + case DTEDMD_VERTACCURACY_UHL: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachUHLRecord + 56; + else + *ppszLocation = psDInfo->pachUHLRecord + 28; + *pnLength = 4; + break; + + case DTEDMD_SECURITYCODE_UHL: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachUHLRecord + 60; + else + *ppszLocation = psDInfo->pachUHLRecord + 32; + *pnLength = 3; + break; + + case DTEDMD_UNIQUEREF_UHL: + if (bIsWeirdDTED) + *ppszLocation = NULL; + else + *ppszLocation = psDInfo->pachUHLRecord + 35; + *pnLength = 12; + break; + + case DTEDMD_DATA_EDITION: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachDSIRecord + 174; + else + *ppszLocation = psDInfo->pachDSIRecord + 87; + *pnLength = 2; + break; + + case DTEDMD_MATCHMERGE_VERSION: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachDSIRecord + 176; + else + *ppszLocation = psDInfo->pachDSIRecord + 89; + *pnLength = 1; + break; + + case DTEDMD_MAINT_DATE: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachDSIRecord + 177; + else + *ppszLocation = psDInfo->pachDSIRecord + 90; + *pnLength = 4; + break; + + case DTEDMD_MATCHMERGE_DATE: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachDSIRecord + 181; + else + *ppszLocation = psDInfo->pachDSIRecord + 94; + *pnLength = 4; + break; + + case DTEDMD_MAINT_DESCRIPTION: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachDSIRecord + 185; + else + *ppszLocation = psDInfo->pachDSIRecord + 98; + *pnLength = 4; + break; + + case DTEDMD_PRODUCER: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachDSIRecord + 189; + else + *ppszLocation = psDInfo->pachDSIRecord + 102; + *pnLength = 8; + break; + + case DTEDMD_VERTDATUM: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachDSIRecord + 267; + else + *ppszLocation = psDInfo->pachDSIRecord + 141; + *pnLength = 3; + break; + + case DTEDMD_HORIZDATUM: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachDSIRecord + 270; + else + *ppszLocation = psDInfo->pachDSIRecord + 144; + *pnLength = 5; + break; + + case DTEDMD_DIGITIZING_SYS: + if (bIsWeirdDTED) + *ppszLocation = NULL; + else + *ppszLocation = psDInfo->pachDSIRecord + 149; + *pnLength = 10; + break; + + case DTEDMD_COMPILATION_DATE: + if (bIsWeirdDTED) + *ppszLocation = NULL; + else + *ppszLocation = psDInfo->pachDSIRecord + 159; + *pnLength = 4; + break; + + case DTEDMD_HORIZACCURACY: + *ppszLocation = psDInfo->pachACCRecord + 3; + *pnLength = 4; + break; + + case DTEDMD_REL_HORIZACCURACY: + *ppszLocation = psDInfo->pachACCRecord + 11; + *pnLength = 4; + break; + + case DTEDMD_REL_VERTACCURACY: + *ppszLocation = psDInfo->pachACCRecord + 15; + *pnLength = 4; + break; + + case DTEDMD_VERTACCURACY_ACC: + *ppszLocation = psDInfo->pachACCRecord + 7; + *pnLength = 4; + break; + + case DTEDMD_SECURITYCODE_DSI: + *ppszLocation = psDInfo->pachDSIRecord + 3; + *pnLength = 1; + break; + + case DTEDMD_UNIQUEREF_DSI: + if (bIsWeirdDTED) + *ppszLocation = NULL; + else + *ppszLocation = psDInfo->pachDSIRecord + 64; + *pnLength = 15; + break; + + case DTEDMD_NIMA_DESIGNATOR: + if (bIsWeirdDTED) + *ppszLocation = psDInfo->pachDSIRecord + 118; + else + *ppszLocation = psDInfo->pachDSIRecord + 59; + *pnLength = 5; + break; + + case DTEDMD_PARTIALCELL_DSI: + if (bIsWeirdDTED) + *ppszLocation = NULL; + else + *ppszLocation = psDInfo->pachDSIRecord + 289; + *pnLength = 2; + break; + + default: + *ppszLocation = NULL; + *pnLength = 0; + } +} + +/************************************************************************/ +/* DTEDGetMetadata() */ +/************************************************************************/ + +char *DTEDGetMetadata( DTEDInfo *psDInfo, DTEDMetaDataCode eCode ) + +{ + int nFieldLen; + char *pszFieldSrc; + char *pszResult; + + DTEDGetMetadataLocation( psDInfo, eCode, &pszFieldSrc, &nFieldLen ); + if( pszFieldSrc == NULL ) + return CPLStrdup( "" ); + + pszResult = (char *) CPLMalloc(nFieldLen+1); + strncpy( pszResult, pszFieldSrc, nFieldLen ); + pszResult[nFieldLen] = '\0'; + + return pszResult; +} + +/************************************************************************/ +/* DTEDSetMetadata() */ +/************************************************************************/ + +int DTEDSetMetadata( DTEDInfo *psDInfo, DTEDMetaDataCode eCode, + const char *pszNewValue ) + +{ + int nFieldLen; + char *pszFieldSrc; + + if( !psDInfo->bUpdate ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get the location in the headers to update. */ +/* -------------------------------------------------------------------- */ + DTEDGetMetadataLocation( psDInfo, eCode, &pszFieldSrc, &nFieldLen ); + if( pszFieldSrc == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Update it, padding with spaces. */ +/* -------------------------------------------------------------------- */ + memset( pszFieldSrc, ' ', nFieldLen ); + strncpy( pszFieldSrc, pszNewValue, + MIN((size_t)nFieldLen,strlen(pszNewValue)) ); + + /* Turn the flag on, so that the headers are rewritten at file */ + /* closing */ + psDInfo->bRewriteHeaders = TRUE; + + return TRUE; +} + +/************************************************************************/ +/* DTEDClose() */ +/************************************************************************/ + +void DTEDClose( DTEDInfo * psDInfo ) + +{ + if( psDInfo->bRewriteHeaders ) + { +/* -------------------------------------------------------------------- */ +/* Write all headers back to disk. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( psDInfo->fp, psDInfo->nUHLOffset, SEEK_SET ); + VSIFWriteL( psDInfo->pachUHLRecord, 1, DTED_UHL_SIZE, psDInfo->fp ); + + VSIFSeekL( psDInfo->fp, psDInfo->nDSIOffset, SEEK_SET ); + VSIFWriteL( psDInfo->pachDSIRecord, 1, DTED_DSI_SIZE, psDInfo->fp ); + + VSIFSeekL( psDInfo->fp, psDInfo->nACCOffset, SEEK_SET ); + VSIFWriteL( psDInfo->pachACCRecord, 1, DTED_ACC_SIZE, psDInfo->fp ); + } + + VSIFCloseL( psDInfo->fp ); + + CPLFree( psDInfo->pachUHLRecord ); + CPLFree( psDInfo->pachDSIRecord ); + CPLFree( psDInfo->pachACCRecord ); + + CPLFree( psDInfo->panMapLogicalColsToOffsets ); + + CPLFree( psDInfo ); +} diff --git a/bazaar/plugin/gdal/frmts/dted/dted_api.h b/bazaar/plugin/gdal/frmts/dted/dted_api.h new file mode 100644 index 000000000..cb78bc311 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dted/dted_api.h @@ -0,0 +1,215 @@ +/****************************************************************************** + * $Id: dted_api.h 28575 2015-02-28 09:41:10Z rouault $ + * + * Project: DTED Translator + * Purpose: Public (C callable) interface for DTED/CDED reading. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2007-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _DTED_API_H_INCLUDED +#define _DTED_API_H_INCLUDED + +/* -------------------------------------------------------------------- */ +/* To avoid dependence on CPL, just define AVOID_CPL when */ +/* compiling. */ +/* -------------------------------------------------------------------- */ +#ifndef AVOID_CPL +# include "cpl_conv.h" +#else + +#include +#include +#include +#include + +#define CPL_C_START +#define CPL_C_END + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +#ifndef EQUAL +#if defined(WIN32) || defined(_WIN32) || defined(_WINDOWS) +# define EQUALN(a,b,n) (strnicmp(a,b,n)==0) +# define EQUAL(a,b) (stricmp(a,b)==0) +#else +# define EQUALN(a,b,n) (strncasecmp(a,b,n)==0) +# define EQUAL(a,b) (strcasecmp(a,b)==0) +#endif +#endif + +#ifndef ABS +#define ABS(x) (((x)>=0) ? (x) : -(x)) +#endif + +#ifndef MIN +#define MIN(x,y) (((x)<(y)) ? (x) : (y)) +#endif + +#define VSIFTellL ftell +#define VSIFOpenL fopen +#define VSIFCloseL fclose +#define VSIFReadL fread +#define VSIFWriteL fwrite +#define CPLMalloc malloc +#define CPLCalloc calloc +#define CPLFree free +#define GInt16 short +#define GByte unsigned char +#define VSIFSeekL fseek +#define CPLAssert assert +#define VSIStrdup strdup + +#endif + +/* -------------------------------------------------------------------- */ +/* DTED information structure. All of this can be considered */ +/* public information. */ +/* -------------------------------------------------------------------- */ +CPL_C_START + +#define DTED_UHL_SIZE 80 +#define DTED_DSI_SIZE 648 +#define DTED_ACC_SIZE 2700 + +#define DTED_NODATA_VALUE -32767 + +typedef struct { + VSILFILE *fp; + int bUpdate; + + int nXSize; + int nYSize; + + double dfULCornerX; /* in long/lat degrees */ + double dfULCornerY; + double dfPixelSizeX; + double dfPixelSizeY; + + int nUHLOffset; + char *pachUHLRecord; + + int nDSIOffset; + char *pachDSIRecord; + + int nACCOffset; + char *pachACCRecord; + + int nDataOffset; + + int bRewriteHeaders; + + int *panMapLogicalColsToOffsets; /* size of nXSize elements. Might be NULL */ + +} DTEDInfo; + +/* -------------------------------------------------------------------- */ +/* DTED access API. Get info directly from the structure */ +/* (DTEDInfo). */ +/* -------------------------------------------------------------------- */ +DTEDInfo *DTEDOpen( const char * pszFilename, const char * pszAccess, + int bTestOpen ); +DTEDInfo *DTEDOpenEx( VSILFILE* fp, const char * pszFilename, + const char * pszAccess, int bTestOpen ); + +/** Read one single sample. The coordinates are given from the + top-left corner of the file (contrary to the internal + organisation or a DTED file) +*/ +int DTEDReadPoint( DTEDInfo * psDInfo, int nXOff, int nYOff, GInt16* panVal); + +/** Read one profile line. These are organized in bottom to top + order starting from the leftmost column (0). +*/ +int DTEDReadProfile( DTEDInfo * psDInfo, int nColumnOffset, + GInt16 * panData ); + +/* Extended version of DTEDReadProfile that enables the user to specify */ +/* whether the checksums should be verified */ +int DTEDReadProfileEx( DTEDInfo * psDInfo, int nColumnOffset, + GInt16 * panData, int bVerifyChecksum ); + +/** Write one profile line. + @warning Contrary to DTEDReadProfile, + the profile should be organized from top to bottom +*/ +int DTEDWriteProfile( DTEDInfo *psDInfo, int nColumnOffset, GInt16 *panData); + +void DTEDClose( DTEDInfo * ); + +const char *DTEDCreate( const char *pszFilename, + int nLevel, int nLLOriginLat, int nLLOriginLong ); + +/* -------------------------------------------------------------------- */ +/* Metadata support. */ +/* -------------------------------------------------------------------- */ +typedef enum { + DTEDMD_VERTACCURACY_UHL = 1, /* UHL 29+4, ACC 8+4 */ + DTEDMD_VERTACCURACY_ACC = 2, + DTEDMD_SECURITYCODE_UHL = 3, /* UHL 33+3, DSI 4+1 */ + DTEDMD_SECURITYCODE_DSI = 4, + DTEDMD_UNIQUEREF_UHL = 5, /* UHL 36+12, DSI 65+15*/ + DTEDMD_UNIQUEREF_DSI = 6, + DTEDMD_DATA_EDITION = 7, /* DSI 88+2 */ + DTEDMD_MATCHMERGE_VERSION = 8, /* DSI 90+1 */ + DTEDMD_MAINT_DATE = 9, /* DSI 91+4 */ + DTEDMD_MATCHMERGE_DATE = 10, /* DSI 95+4 */ + DTEDMD_MAINT_DESCRIPTION = 11, /* DSI 99+4 */ + DTEDMD_PRODUCER = 12, /* DSI 103+8 */ + DTEDMD_VERTDATUM = 13, /* DSI 142+3 */ + DTEDMD_DIGITIZING_SYS = 14, /* DSI 150+10 */ + DTEDMD_COMPILATION_DATE = 15, /* DSI 160+4 */ + DTEDMD_HORIZACCURACY = 16, /* ACC 4+4 */ + DTEDMD_REL_HORIZACCURACY = 17, /* ACC 12+4 */ + DTEDMD_REL_VERTACCURACY = 18, /* ACC 16+4 */ + DTEDMD_HORIZDATUM = 19, /* DSI 145+5 */ + DTEDMD_ORIGINLONG = 20, /* UHL 5+7 */ + DTEDMD_ORIGINLAT = 21, /* UHL 13+7 */ + DTEDMD_NIMA_DESIGNATOR = 22, /* DSI 60 + 5 */ + DTEDMD_PARTIALCELL_DSI = 23, /* DSI 289 + 2 */ + DTEDMD_MAX = 23 +} DTEDMetaDataCode; + + +char *DTEDGetMetadata( DTEDInfo *, DTEDMetaDataCode ); +int DTEDSetMetadata( DTEDInfo *, DTEDMetaDataCode, const char *); + +/* -------------------------------------------------------------------- */ +/* Point stream writer API. */ +/* -------------------------------------------------------------------- */ +void *DTEDCreatePtStream( const char *pszPath, int nLevel ); +int DTEDWritePt( void *hStream, double dfLong, double dfLat, double dfElev ); +void DTEDFillPtStream( void *hStream, int nPixelSearchDist ); +void DTEDPtStreamSetMetadata( void *hStream, DTEDMetaDataCode, const char *); +void DTEDClosePtStream( void *hStream ); +void DTEDPtStreamTrimEdgeOnlyTiles( void *hStream ); + +CPL_C_END + +#endif /* ndef _DTED_API_H_INCLUDED */ + + diff --git a/bazaar/plugin/gdal/frmts/dted/dted_create.c b/bazaar/plugin/gdal/frmts/dted/dted_create.c new file mode 100644 index 000000000..ab76fbdba --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dted/dted_create.c @@ -0,0 +1,282 @@ +/****************************************************************************** + * $Id: dted_create.c 28433 2015-02-06 21:18:50Z rouault $ + * + * Project: DTED Translator + * Purpose: Implementation of DTEDCreate() portion of DTED API. + * Author: Frank Warmerdam, warmerdamm@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "dted_api.h" +#include + +CPL_CVSID("$Id: dted_create.c 28433 2015-02-06 21:18:50Z rouault $"); + +#define DTED_ABS_VERT_ACC "NA " +#define DTED_SECURITY "U" +#define DTED_EDITION 1 + +/************************************************************************/ +/* DTEDFormatDMS() */ +/************************************************************************/ + +static void DTEDFormatDMS( unsigned char *achField, double dfAngle, + const char *pszLatLong, const char *pszFormat ) + +{ + char chHemisphere; + char szWork[128]; + int nDegrees, nMinutes, nSeconds; + double dfRemainder; + + if( pszFormat == NULL ) + pszFormat = "%03d%02d%02d%c"; + + assert( EQUAL(pszLatLong,"LAT") || EQUAL(pszLatLong,"LONG") ); + + if( EQUAL(pszLatLong,"LAT") ) + { + if( dfAngle < 0.0 ) + chHemisphere = 'S'; + else + chHemisphere = 'N'; + } + else + { + if( dfAngle < 0.0 ) + chHemisphere = 'W'; + else + chHemisphere = 'E'; + } + + dfAngle = ABS(dfAngle); + + nDegrees = (int) floor(dfAngle + 0.5/3600.0); + dfRemainder = dfAngle - nDegrees; + nMinutes = (int) floor(dfRemainder*60.0 + 0.5/60.0); + dfRemainder = dfRemainder - nMinutes / 60.0; + nSeconds = (int) floor(dfRemainder * 3600.0 + 0.5); + + sprintf( szWork, pszFormat, + nDegrees, nMinutes, nSeconds, chHemisphere ); + + strncpy( (char *) achField, szWork, strlen(szWork) ); +} + +/************************************************************************/ +/* DTEDFormat() */ +/************************************************************************/ + +static void DTEDFormat( unsigned char *pszTarget, const char *pszFormat, ... ) CPL_PRINT_FUNC_FORMAT (2, 3); + +static void DTEDFormat( unsigned char *pszTarget, const char *pszFormat, ... ) + +{ + va_list args; + char szWork[512]; + + va_start(args, pszFormat); + vsprintf( szWork, pszFormat, args ); + va_end(args); + + strncpy( (char *) pszTarget, szWork, strlen(szWork) ); +} + +/************************************************************************/ +/* DTEDCreate() */ +/************************************************************************/ + +const char *DTEDCreate( const char *pszFilename, int nLevel, + int nLLOriginLat, int nLLOriginLong ) + +{ + VSILFILE *fp; + unsigned char achRecord[3601*2 + 12]; + int nXSize, nYSize, iProfile; + static char szError[512]; + +/* -------------------------------------------------------------------- */ +/* Establish resolution. */ +/* -------------------------------------------------------------------- */ + if( nLevel == 0 ) + { + nXSize = 121; + nYSize = 121; + } + else if( nLevel == 1 ) + { + nXSize = 1201; + nYSize = 1201; + } + else if( nLevel == 2 ) + { + nXSize = 3601; + nYSize = 3601; + } + else + { + sprintf( szError, "Illegal DTED Level value %d, only 0-2 allowed.", + nLevel ); + return szError; + } + + if( ABS(nLLOriginLat) >= 80 ) + nXSize = (nXSize - 1) / 6 + 1; + else if( ABS(nLLOriginLat) >= 75 ) + nXSize = (nXSize - 1) / 4 + 1; + else if( ABS(nLLOriginLat) >= 70 ) + nXSize = (nXSize - 1) / 3 + 1; + else if( ABS(nLLOriginLat) >= 50 ) + nXSize = (nXSize - 1) / 2 + 1; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszFilename, "wb" ); + + if( fp == NULL ) + { + sprintf( szError, "Unable to create file `%s'.", pszFilename ); + return szError; + } + +/* -------------------------------------------------------------------- */ +/* Format and write the UHL record. */ +/* -------------------------------------------------------------------- */ + memset( achRecord, ' ', DTED_UHL_SIZE ); + + DTEDFormat( achRecord + 0, "UHL1" ); + + DTEDFormatDMS( achRecord + 4, nLLOriginLong, "LONG", NULL ); + DTEDFormatDMS( achRecord + 12, nLLOriginLat, "LAT", NULL ); + + DTEDFormat( achRecord + 20, "%04d", (3600 / (nXSize-1)) * 10 ); + DTEDFormat( achRecord + 24, "%04d", (3600 / (nYSize-1)) * 10 ); + + DTEDFormat( achRecord + 28, "%4s", DTED_ABS_VERT_ACC ); + DTEDFormat( achRecord + 32, "%-3s", DTED_SECURITY ); + DTEDFormat( achRecord + 47, "%04d", nXSize ); + DTEDFormat( achRecord + 51, "%04d", nYSize ); + DTEDFormat( achRecord + 55, "%c", '0' ); + + if( VSIFWriteL( achRecord, DTED_UHL_SIZE, 1, fp ) != 1 ) + return "UHL record write failed."; + +/* -------------------------------------------------------------------- */ +/* Format and write the DSI record. */ +/* -------------------------------------------------------------------- */ + memset( achRecord, ' ', DTED_DSI_SIZE ); + + DTEDFormat( achRecord + 0, "DSI" ); + DTEDFormat( achRecord + 3, "%1s", DTED_SECURITY ); + + DTEDFormat( achRecord + 59, "DTED%d", nLevel ); + DTEDFormat( achRecord + 64, "%015d", 0 ); + DTEDFormat( achRecord + 87, "%02d", DTED_EDITION ); + DTEDFormat( achRecord + 89, "%c", 'A' ); + DTEDFormat( achRecord + 90, "%04d", 0 ); + DTEDFormat( achRecord + 94, "%04d", 0 ); + DTEDFormat( achRecord + 98, "%04d", 0 ); + DTEDFormat( achRecord + 126, "PRF89020B"); + DTEDFormat( achRecord + 135, "00"); + DTEDFormat( achRecord + 137, "0005"); + DTEDFormat( achRecord + 141, "MSL" ); + DTEDFormat( achRecord + 144, "WGS84" ); + + /* origin */ + DTEDFormatDMS( achRecord + 185, nLLOriginLat, "LAT", + "%02d%02d%02d.0%c" ); + DTEDFormatDMS( achRecord + 194, nLLOriginLong, "LONG", + "%03d%02d%02d.0%c" ); + + /* SW */ + DTEDFormatDMS( achRecord + 204, nLLOriginLat, "LAT", "%02d%02d%02d%c" ); + DTEDFormatDMS( achRecord + 211, nLLOriginLong, "LONG", NULL ); + + /* NW */ + DTEDFormatDMS( achRecord + 219, nLLOriginLat+1, "LAT", "%02d%02d%02d%c" ); + DTEDFormatDMS( achRecord + 226, nLLOriginLong, "LONG", NULL ); + + /* NE */ + DTEDFormatDMS( achRecord + 234, nLLOriginLat+1, "LAT", "%02d%02d%02d%c" ); + DTEDFormatDMS( achRecord + 241, nLLOriginLong+1, "LONG", NULL ); + + /* SE */ + DTEDFormatDMS( achRecord + 249, nLLOriginLat, "LAT", "%02d%02d%02d%c" ); + DTEDFormatDMS( achRecord + 256, nLLOriginLong+1, "LONG", NULL ); + + DTEDFormat( achRecord + 264, "0000000.0" ); + DTEDFormat( achRecord + 264, "0000000.0" ); + + DTEDFormat( achRecord + 273, "%04d", (3600 / (nYSize-1)) * 10 ); + DTEDFormat( achRecord + 277, "%04d", (3600 / (nXSize-1)) * 10 ); + + DTEDFormat( achRecord + 281, "%04d", nYSize ); + DTEDFormat( achRecord + 285, "%04d", nXSize ); + DTEDFormat( achRecord + 289, "%02d", 0 ); + + if( VSIFWriteL( achRecord, DTED_DSI_SIZE, 1, fp ) != 1 ) + return "DSI record write failed."; + +/* -------------------------------------------------------------------- */ +/* Create and write ACC record. */ +/* -------------------------------------------------------------------- */ + memset( achRecord, ' ', DTED_ACC_SIZE ); + + DTEDFormat( achRecord + 0, "ACC" ); + + DTEDFormat( achRecord + 3, "NA" ); + DTEDFormat( achRecord + 7, "NA" ); + DTEDFormat( achRecord + 11, "NA" ); + DTEDFormat( achRecord + 15, "NA" ); + + DTEDFormat( achRecord + 55, "00" ); + + if( VSIFWriteL( achRecord, DTED_ACC_SIZE, 1, fp ) != 1 ) + return "ACC record write failed."; + +/* -------------------------------------------------------------------- */ +/* Write blank template profile data records. */ +/* -------------------------------------------------------------------- */ + memset( achRecord, 0, nYSize*2 + 12 ); + memset( achRecord + 8, 0xff, nYSize*2 ); + + achRecord[0] = 0252; + + for( iProfile = 0; iProfile < nXSize; iProfile++ ) + { + achRecord[1] = 0; + achRecord[2] = (GByte) (iProfile / 256); + achRecord[3] = (GByte) (iProfile % 256); + + achRecord[4] = (GByte) (iProfile / 256); + achRecord[5] = (GByte) (iProfile % 256); + + if( VSIFWriteL( achRecord, nYSize*2 + 12, 1, fp ) != 1 ) + return "Data record write failed."; + } + + VSIFCloseL( fp ); + + return NULL; +} diff --git a/bazaar/plugin/gdal/frmts/dted/dted_ptstream.c b/bazaar/plugin/gdal/frmts/dted/dted_ptstream.c new file mode 100644 index 000000000..85ced288a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dted/dted_ptstream.c @@ -0,0 +1,629 @@ +/****************************************************************************** + * $Id: dted_ptstream.c 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: DTED Translator + * Purpose: DTED Point Stream Writer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "dted_api.h" + +CPL_CVSID("$Id: dted_ptstream.c 27745 2014-09-27 16:38:57Z goatbar $"); + +typedef struct { + char *pszFilename; + DTEDInfo *psInfo; + + GInt16 **papanProfiles; + + int nLLLong; + int nLLLat; +} DTEDCachedFile; + +typedef struct { + int nLevel; + char *pszPath; + + double dfPixelSize; + + int nOpenFiles; + DTEDCachedFile *pasCF; + + int nLastFile; + + char *apszMetadata[DTEDMD_MAX+1]; +} DTEDPtStream; + +/************************************************************************/ +/* DTEDCreatePtStream() */ +/************************************************************************/ + +void *DTEDCreatePtStream( const char *pszPath, int nLevel ) + +{ + DTEDPtStream *psStream; + int i; + VSIStatBuf sStat; + +/* -------------------------------------------------------------------- */ +/* Does the target directory already exist? If not try to */ +/* create it. */ +/* -------------------------------------------------------------------- */ + if( CPLStat( pszPath, &sStat ) != 0 ) + { + if( VSIMkdir( pszPath, 0755 ) != 0 ) + { +#ifndef AVOID_CPL + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to find, or create directory `%s'.", + pszPath ); +#endif + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Create the stream and initialize it. */ +/* -------------------------------------------------------------------- */ + + psStream = (DTEDPtStream *) CPLCalloc( sizeof(DTEDPtStream), 1 ); + psStream->nLevel = nLevel; + psStream->pszPath = CPLStrdup( pszPath ); + psStream->nOpenFiles = 0; + psStream->pasCF = NULL; + psStream->nLastFile = -1; + + for( i = 0; i < DTEDMD_MAX+1; i++ ) + psStream->apszMetadata[i] = NULL; + + if( nLevel == 0 ) + psStream->dfPixelSize = 1.0 / 120.0; + else if( nLevel == 1 ) + psStream->dfPixelSize = 1.0 / 1200.0; + else if( nLevel == 2 ) + psStream->dfPixelSize = 1.0 / 3600.0; + else + psStream->dfPixelSize = 1.0 / 3600.0; + + return (void *) psStream; +} + +/************************************************************************/ +/* DTEDPtStreamNewTile() */ +/* */ +/* Create a new DTED file file, add it to our list, and make it */ +/* "current". */ +/************************************************************************/ + +static int DTEDPtStreamNewTile( DTEDPtStream *psStream, + int nCrLong, int nCrLat ) + +{ + DTEDInfo *psInfo; + char szFile[128]; + char chNSHemi, chEWHemi; + char *pszFullFilename; + const char *pszError; + + /* work out filename */ + if( nCrLat < 0 ) + chNSHemi = 's'; + else + chNSHemi = 'n'; + + if( nCrLong < 0 ) + chEWHemi = 'w'; + else + chEWHemi = 'e'; + + sprintf( szFile, "%c%03d%c%03d.dt%d", + chEWHemi, ABS(nCrLong), chNSHemi, ABS(nCrLat), + psStream->nLevel ); + + pszFullFilename = + CPLStrdup(CPLFormFilename( psStream->pszPath, szFile, NULL )); + + /* create the dted file */ + pszError = DTEDCreate( pszFullFilename, psStream->nLevel, + nCrLat, nCrLong ); + if( pszError != NULL ) + { +#ifndef AVOID_CPL + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create DTED file `%s'.\n%s", + pszFullFilename, pszError ); +#endif + return FALSE; + } + + psInfo = DTEDOpen( pszFullFilename, "rb+", FALSE ); + + if( psInfo == NULL ) + { + CPLFree( pszFullFilename ); + return FALSE; + } + + /* add cached file to stream */ + psStream->nOpenFiles++; + psStream->pasCF = + CPLRealloc(psStream->pasCF, + sizeof(DTEDCachedFile)*psStream->nOpenFiles); + + psStream->pasCF[psStream->nOpenFiles-1].psInfo = psInfo; + psStream->pasCF[psStream->nOpenFiles-1].papanProfiles = + CPLCalloc(sizeof(GInt16*),psInfo->nXSize); + psStream->pasCF[psStream->nOpenFiles-1].pszFilename = pszFullFilename; + psStream->pasCF[psStream->nOpenFiles-1].nLLLat = nCrLat; + psStream->pasCF[psStream->nOpenFiles-1].nLLLong = nCrLong; + + psStream->nLastFile = psStream->nOpenFiles-1; + + return TRUE; +} + +/************************************************************************/ +/* DTEDWritePtLL() */ +/************************************************************************/ + +static int DTEDWritePtLL( CPL_UNUSED DTEDPtStream *psStream, + DTEDCachedFile *psCF, + double dfLong, + double dfLat, + double dfElev ) +{ +/* -------------------------------------------------------------------- */ +/* Determine what profile this belongs in, and initialize the */ +/* profile if it doesn't already exist. */ +/* -------------------------------------------------------------------- */ + DTEDInfo *psInfo = psCF->psInfo; + int iProfile, i, iRow; + + iProfile = (int) ((dfLong - psInfo->dfULCornerX) / psInfo->dfPixelSizeX); + iProfile = MAX(0,MIN(psInfo->nXSize-1,iProfile)); + + if( psCF->papanProfiles[iProfile] == NULL ) + { + psCF->papanProfiles[iProfile] = + CPLMalloc(sizeof(GInt16) * psInfo->nYSize); + + for( i = 0; i < psInfo->nYSize; i++ ) + psCF->papanProfiles[iProfile][i] = DTED_NODATA_VALUE; + } + +/* -------------------------------------------------------------------- */ +/* Establish where we fit in the profile. */ +/* -------------------------------------------------------------------- */ + iRow = (int) ((psInfo->dfULCornerY-dfLat) / psInfo->dfPixelSizeY); + iRow = MAX(0,MIN(psInfo->nYSize-1,iRow)); + + psCF->papanProfiles[iProfile][iRow] = (GInt16) floor(dfElev+0.5); + + return TRUE; +} + +/************************************************************************/ +/* DTEDWritePt() */ +/* */ +/* Write a single point out, creating a new file if necessary */ +/* to hold it. */ +/************************************************************************/ + +int DTEDWritePt( void *hStream, double dfLong, double dfLat, double dfElev ) + +{ + DTEDPtStream *psStream = (DTEDPtStream *) hStream; + int i; + DTEDInfo *psInfo; + int bOnBoundary = FALSE; + +/* -------------------------------------------------------------------- */ +/* Determine if we are in a boundary region ... that is in the */ +/* area of the edge "pixel" that is shared with adjacent */ +/* tiles. */ +/* -------------------------------------------------------------------- */ + if( (floor(dfLong - 0.5*psStream->dfPixelSize) + != floor(dfLong + 0.5*psStream->dfPixelSize)) + || (floor(dfLat - 0.5*psStream->dfPixelSize) + != floor(dfLat + 0.5*psStream->dfPixelSize)) ) + { + bOnBoundary = TRUE; + psStream->nLastFile = -1; + } + +/* ==================================================================== */ +/* Handle case where the tile is not on a boundary. We only */ +/* need one output tile. */ +/* ==================================================================== */ +/* -------------------------------------------------------------------- */ +/* Is the last file used still applicable? */ +/* -------------------------------------------------------------------- */ + if( !bOnBoundary ) + { + if( psStream->nLastFile != -1 ) + { + psInfo = psStream->pasCF[psStream->nLastFile].psInfo; + + if( dfLat > psInfo->dfULCornerY + || dfLat < psInfo->dfULCornerY - 1.0 - psInfo->dfPixelSizeY + || dfLong < psInfo->dfULCornerX + || dfLong > psInfo->dfULCornerX + 1.0 + psInfo->dfPixelSizeX ) + psStream->nLastFile = -1; + } + +/* -------------------------------------------------------------------- */ +/* Search for the file to write to. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psStream->nOpenFiles && psStream->nLastFile == -1; i++ ) + { + psInfo = psStream->pasCF[i].psInfo; + + if( !(dfLat > psInfo->dfULCornerY + || dfLat < psInfo->dfULCornerY - 1.0 - psInfo->dfPixelSizeY + || dfLong < psInfo->dfULCornerX + || dfLong > psInfo->dfULCornerX + 1.0 + psInfo->dfPixelSizeX) ) + { + psStream->nLastFile = i; + } + } + +/* -------------------------------------------------------------------- */ +/* If none found, create a new file. */ +/* -------------------------------------------------------------------- */ + if( psStream->nLastFile == -1 ) + { + int nCrLong, nCrLat; + + nCrLong = (int) floor(dfLong); + nCrLat = (int) floor(dfLat); + + if( !DTEDPtStreamNewTile( psStream, nCrLong, nCrLat ) ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Write data out to selected tile. */ +/* -------------------------------------------------------------------- */ + return DTEDWritePtLL( psStream, psStream->pasCF + psStream->nLastFile, + dfLong, dfLat, dfElev ); + } + +/* ==================================================================== */ +/* Handle case where we are on a boundary. We may be writing */ +/* the value to as many as four tiles. */ +/* ==================================================================== */ + else + { + int nLatMin, nLatMax, nLongMin, nLongMax; + int nCrLong, nCrLat; + + nLongMin = (int) floor( dfLong - 0.5*psStream->dfPixelSize ); + nLongMax = (int) floor( dfLong + 0.5*psStream->dfPixelSize ); + nLatMin = (int) floor( dfLat - 0.5*psStream->dfPixelSize ); + nLatMax = (int) floor( dfLat + 0.5*psStream->dfPixelSize ); + + for( nCrLong = nLongMin; nCrLong <= nLongMax; nCrLong++ ) + { + for( nCrLat = nLatMin; nCrLat <= nLatMax; nCrLat++ ) + { + psStream->nLastFile = -1; + +/* -------------------------------------------------------------------- */ +/* Find this tile in our existing list. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psStream->nOpenFiles; i++ ) + { + if( psStream->pasCF[i].nLLLong == nCrLong + && psStream->pasCF[i].nLLLat == nCrLat ) + { + psStream->nLastFile = i; + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Create the tile if not found. */ +/* -------------------------------------------------------------------- */ + if( psStream->nLastFile == -1 ) + { + if( !DTEDPtStreamNewTile( psStream, nCrLong, nCrLat ) ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Write to the tile. */ +/* -------------------------------------------------------------------- */ + if( !DTEDWritePtLL( psStream, + psStream->pasCF + psStream->nLastFile, + dfLong, dfLat, dfElev ) ) + return FALSE; + } + } + } + + return TRUE; +} + +/************************************************************************/ +/* DTEDClosePtStream() */ +/************************************************************************/ + +void DTEDClosePtStream( void *hStream ) + +{ + DTEDPtStream *psStream = (DTEDPtStream *) hStream; + int iFile, iMD; + +/* -------------------------------------------------------------------- */ +/* Flush all DTED files. */ +/* -------------------------------------------------------------------- */ + for( iFile = 0; iFile < psStream->nOpenFiles; iFile++ ) + { + int iProfile; + DTEDCachedFile *psCF = psStream->pasCF + iFile; + + for( iProfile = 0; iProfile < psCF->psInfo->nXSize; iProfile++ ) + { + if( psCF->papanProfiles[iProfile] != NULL ) + { + DTEDWriteProfile( psCF->psInfo, iProfile, + psCF->papanProfiles[iProfile] ); + CPLFree( psCF->papanProfiles[iProfile] ); + } + } + + CPLFree( psCF->papanProfiles ); + + for( iMD = 0; iMD < DTEDMD_MAX+1; iMD++ ) + { + if( psStream->apszMetadata[iMD] != NULL ) + DTEDSetMetadata( psCF->psInfo, iMD, + psStream->apszMetadata[iMD] ); + } + + DTEDClose( psCF->psInfo ); + } + +/* -------------------------------------------------------------------- */ +/* Final cleanup. */ +/* -------------------------------------------------------------------- */ + + for( iMD = 0; iMD < DTEDMD_MAX+1; iMD++ ) + CPLFree( psStream->apszMetadata[iMD] ); + + CPLFree( psStream->pasCF ); + CPLFree( psStream->pszPath ); + CPLFree( psStream ); +} + +/************************************************************************/ +/* DTEDFillPixel() */ +/************************************************************************/ + +void DTEDFillPixel( DTEDInfo *psInfo, GInt16 **papanProfiles, + GInt16 **papanDstProfiles, int iX, int iY, + int nPixelSearchDist, float *pafKernel ) + +{ + int nKernelWidth = 2 * nPixelSearchDist + 1; + int nXMin, nXMax, nYMin, nYMax; + double dfCoefSum = 0.0, dfValueSum = 0.0; + int iXS, iYS; + + nXMin = MAX(0,iX - nPixelSearchDist); + nXMax = MIN(psInfo->nXSize-1,iX + nPixelSearchDist); + nYMin = MAX(0,iY - nPixelSearchDist); + nYMax = MIN(psInfo->nYSize-1,iY + nPixelSearchDist); + + for( iXS = nXMin; iXS <= nXMax; iXS++ ) + { + GInt16 *panThisProfile = papanProfiles[iXS]; + + if( panThisProfile == NULL ) + continue; + + for( iYS = nYMin; iYS <= nYMax; iYS++ ) + { + if( panThisProfile[iYS] != DTED_NODATA_VALUE ) + { + int iXK, iYK; + float fKernelCoef; + + iXK = iXS - iX + nPixelSearchDist; + iYK = iYS - iY + nPixelSearchDist; + + fKernelCoef = pafKernel[iXK + iYK * nKernelWidth]; + dfCoefSum += fKernelCoef; + dfValueSum += fKernelCoef * panThisProfile[iYS]; + } + } + } + + if( dfCoefSum == 0.0 ) + papanDstProfiles[iX][iY] = DTED_NODATA_VALUE; + else + papanDstProfiles[iX][iY] = + (GInt16) floor(dfValueSum / dfCoefSum + 0.5); +} + +/************************************************************************/ +/* DTEDFillPtStream() */ +/* */ +/* Apply simple inverse distance interpolator to all no-data */ +/* pixels based on available values within the indicated search */ +/* distance (rectangular). */ +/************************************************************************/ + +void DTEDFillPtStream( void *hStream, int nPixelSearchDist ) + +{ + DTEDPtStream *psStream = (DTEDPtStream *) hStream; + int iFile, nKernelWidth; + float *pafKernel; + int iX, iY; + +/* -------------------------------------------------------------------- */ +/* Setup inverse distance weighting kernel. */ +/* -------------------------------------------------------------------- */ + nKernelWidth = 2 * nPixelSearchDist + 1; + pafKernel = (float *) CPLMalloc(nKernelWidth*nKernelWidth*sizeof(float)); + + for( iX = 0; iX < nKernelWidth; iX++ ) + { + for( iY = 0; iY < nKernelWidth; iY++ ) + { + pafKernel[iX + iY * nKernelWidth] = (float) (1.0 / + sqrt( (nPixelSearchDist-iX) * (nPixelSearchDist-iX) + + (nPixelSearchDist-iY) * (nPixelSearchDist-iY) )); + } + } + +/* ==================================================================== */ +/* Process each cached file. */ +/* ==================================================================== */ + for( iFile = 0; iFile < psStream->nOpenFiles; iFile++ ) + { + DTEDInfo *psInfo = psStream->pasCF[iFile].psInfo; + GInt16 **papanProfiles = psStream->pasCF[iFile].papanProfiles; + GInt16 **papanDstProfiles; + + papanDstProfiles = (GInt16 **) + CPLCalloc(sizeof(GInt16*),psInfo->nXSize); + +/* -------------------------------------------------------------------- */ +/* Setup output image. */ +/* -------------------------------------------------------------------- */ + for( iX = 0; iX < psInfo->nXSize; iX++ ) + { + papanDstProfiles[iX] = (GInt16 *) + CPLMalloc(sizeof(GInt16) * psInfo->nYSize); + } + +/* -------------------------------------------------------------------- */ +/* Interpolate all missing values, and copy over available values. */ +/* -------------------------------------------------------------------- */ + for( iX = 0; iX < psInfo->nXSize; iX++ ) + { + for( iY = 0; iY < psInfo->nYSize; iY++ ) + { + if( papanProfiles[iX] == NULL + || papanProfiles[iX][iY] == DTED_NODATA_VALUE ) + { + DTEDFillPixel( psInfo, papanProfiles, papanDstProfiles, + iX, iY, nPixelSearchDist, pafKernel ); + } + else + { + papanDstProfiles[iX][iY] = papanProfiles[iX][iY]; + } + } + } +/* -------------------------------------------------------------------- */ +/* Push new values back into cache. */ +/* -------------------------------------------------------------------- */ + for( iX = 0; iX < psInfo->nXSize; iX++ ) + { + CPLFree( papanProfiles[iX] ); + papanProfiles[iX] = papanDstProfiles[iX]; + } + + CPLFree( papanDstProfiles ); + } + + CPLFree( pafKernel ); +} + +/************************************************************************/ +/* DTEDPtStreamSetMetadata() */ +/************************************************************************/ + +void DTEDPtStreamSetMetadata( void *hStream, DTEDMetaDataCode eCode, + const char *pszValue ) + +{ + DTEDPtStream *psStream = (DTEDPtStream *) hStream; + + if( (int)eCode >= 0 && eCode < DTEDMD_MAX+1 ) + { + CPLFree( psStream->apszMetadata[eCode] ); + psStream->apszMetadata[eCode] = CPLStrdup( pszValue ); + } +} + +/************************************************************************/ +/* DTEDPtStreamTrimEdgeOnlyTiles() */ +/* */ +/* Erase all tiles that only have boundary values set. */ +/************************************************************************/ + +void DTEDPtStreamTrimEdgeOnlyTiles( void *hStream ) + +{ + DTEDPtStream *psStream = (DTEDPtStream *) hStream; + int iFile; + + for( iFile = psStream->nOpenFiles-1; iFile >= 0; iFile-- ) + { + DTEDInfo *psInfo = psStream->pasCF[iFile].psInfo; + GInt16 **papanProfiles = psStream->pasCF[iFile].papanProfiles; + int iProfile, iPixel, bGotNonEdgeData = FALSE; + + for( iProfile = 1; iProfile < psInfo->nXSize-1; iProfile++ ) + { + if( papanProfiles[iProfile] == NULL ) + continue; + + for( iPixel = 1; iPixel < psInfo->nYSize-1; iPixel++ ) + { + if( papanProfiles[iProfile][iPixel] != DTED_NODATA_VALUE ) + { + bGotNonEdgeData = TRUE; + break; + } + } + } + + if( bGotNonEdgeData ) + continue; + + /* Remove this tile */ + + for( iProfile = 0; iProfile < psInfo->nXSize; iProfile++ ) + { + if( papanProfiles[iProfile] != NULL ) + CPLFree( papanProfiles[iProfile] ); + } + CPLFree( papanProfiles ); + + DTEDClose( psInfo ); + + VSIUnlink( psStream->pasCF[iFile].pszFilename ); + CPLFree( psStream->pasCF[iFile].pszFilename ); + + memmove( psStream->pasCF + iFile, + psStream->pasCF + iFile + 1, + sizeof(DTEDCachedFile) * (psStream->nOpenFiles-iFile-1) ); + psStream->nOpenFiles--; + } +} diff --git a/bazaar/plugin/gdal/frmts/dted/dted_test.c b/bazaar/plugin/gdal/frmts/dted/dted_test.c new file mode 100644 index 000000000..683049880 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dted/dted_test.c @@ -0,0 +1,159 @@ +/****************************************************************************** + * $Id: dted_test.c 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: DTED Translator + * Purpose: Test mainline for DTED writer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2007, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "gdal.h" +#include "dted_api.h" + +/************************************************************************/ +/* Usage() */ +/************************************************************************/ + +static void Usage() + +{ + printf( "Usage: dted_test [-trim] [-fill n] [-level n] []\n" ); + exit(0); +} + +/************************************************************************/ +/* main() */ +/************************************************************************/ +int main( int argc, char ** argv ) + +{ + GDALDatasetH hSrcDS; + int iY, iX, nOutLevel=0, nXSize, nYSize, iArg, nFillDist=0; + void *pStream; + GInt16 *panData; + const char *pszFilename = NULL; + GDALRasterBandH hSrcBand; + double adfGeoTransform[6]; + int bEnableTrim = FALSE; + GInt16 noDataValue = 0; + int bHasNoData; + +/* -------------------------------------------------------------------- */ +/* Identify arguments. */ +/* -------------------------------------------------------------------- */ + + for( iArg = 1; iArg < argc; iArg++ ) + { + if( EQUAL(argv[iArg],"-trim") ) + bEnableTrim = TRUE; + + else if( EQUAL(argv[iArg],"-fill") ) + nFillDist = atoi(argv[++iArg]); + + else if( EQUAL(argv[iArg],"-level") ) + nOutLevel = atoi(argv[++iArg]); + else + { + if( pszFilename != NULL ) + Usage(); + pszFilename = argv[iArg]; + } + } + + if( pszFilename == NULL ) + Usage(); + +/* -------------------------------------------------------------------- */ +/* Open input file. */ +/* -------------------------------------------------------------------- */ + GDALAllRegister(); + hSrcDS = GDALOpen( pszFilename, GA_ReadOnly ); + if( hSrcDS == NULL ) + exit(1); + + hSrcBand = GDALGetRasterBand( hSrcDS, 1 ); + + noDataValue = (GInt16)GDALGetRasterNoDataValue(hSrcBand, &bHasNoData); + + nXSize = GDALGetRasterXSize( hSrcDS ); + nYSize = GDALGetRasterYSize( hSrcDS ); + + GDALGetGeoTransform( hSrcDS, adfGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Create output stream. */ +/* -------------------------------------------------------------------- */ + pStream = DTEDCreatePtStream( ".", nOutLevel ); + + if( pStream == NULL ) + exit( 1 ); + +/* -------------------------------------------------------------------- */ +/* Process all the profiles. */ +/* -------------------------------------------------------------------- */ + panData = (GInt16 *) malloc(sizeof(GInt16) * nXSize); + + for( iY = 0; iY < nYSize; iY++ ) + { + GDALRasterIO( hSrcBand, GF_Read, 0, iY, nXSize, 1, + panData, nXSize, 1, GDT_Int16, 0, 0 ); + + if (bHasNoData) + { + for( iX = 0; iX < nXSize; iX++ ) + { + if (panData[iX] == noDataValue) + panData[iX] = DTED_NODATA_VALUE; + } + } + + for( iX = 0; iX < nXSize; iX++ ) + { + DTEDWritePt( pStream, + adfGeoTransform[0] + + adfGeoTransform[1] * (iX + 0.5) + + adfGeoTransform[2] * (iY + 0.5), + adfGeoTransform[3] + + adfGeoTransform[4] * (iX + 0.5) + + adfGeoTransform[5] * (iY + 0.5), + panData[iX] ); + } + } + + free( panData ); + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + if( bEnableTrim ) + DTEDPtStreamTrimEdgeOnlyTiles( pStream ); + + if( nFillDist > 0 ) + DTEDFillPtStream( pStream, nFillDist ); + + DTEDClosePtStream( pStream ); + GDALClose( hSrcDS ); + + exit( 0 ); +} diff --git a/bazaar/plugin/gdal/frmts/dted/dteddataset.cpp b/bazaar/plugin/gdal/frmts/dted/dteddataset.cpp new file mode 100644 index 000000000..f80173c34 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dted/dteddataset.cpp @@ -0,0 +1,937 @@ +/****************************************************************************** + * $Id: dteddataset.cpp 29017 2015-04-25 19:32:11Z rouault $ + * + * Project: DTED Translator + * Purpose: GDALDataset driver for DTED translator. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2007-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "dted_api.h" +#include "gdal_pam.h" +#include "ogr_spatialref.h" +#include + +CPL_CVSID("$Id: dteddataset.cpp 29017 2015-04-25 19:32:11Z rouault $"); + +CPL_C_START +void GDALRegister_DTED(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* DTEDDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class DTEDRasterBand; + +class DTEDDataset : public GDALPamDataset +{ + friend class DTEDRasterBand; + + char *pszFilename; + DTEDInfo *psDTED; + int bVerifyChecksum; + char *pszProjection; + + public: + DTEDDataset(); + virtual ~DTEDDataset(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * ); + + const char* GetFileName() { return pszFilename; } + void SetFileName(const char* pszFilename); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* DTEDRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class DTEDRasterBand : public GDALPamRasterBand +{ + friend class DTEDDataset; + + int bNoDataSet; + double dfNoDataValue; + + public: + + DTEDRasterBand( DTEDDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + + virtual const char* GetUnitType() { return "m"; } +}; + + +/************************************************************************/ +/* DTEDRasterBand() */ +/************************************************************************/ + +DTEDRasterBand::DTEDRasterBand( DTEDDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Int16; + + bNoDataSet = TRUE; + dfNoDataValue = (double) DTED_NODATA_VALUE; + + /* For some applications, it may be valuable to consider the whole DTED */ + /* file as single block, as the column orientation doesn't fit very well */ + /* with some scanline oriented algorithms */ + /* Of course you need to have a big enough case size, particularly for DTED 2 */ + /* datasets */ + nBlockXSize = CSLTestBoolean(CPLGetConfigOption("GDAL_DTED_SINGLE_BLOCK", "NO")) ? + poDS->GetRasterXSize() : 1; + nBlockYSize = poDS->GetRasterYSize(); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr DTEDRasterBand::IReadBlock( int nBlockXOff, + CPL_UNUSED int nBlockYOff, + void * pImage ) +{ + DTEDDataset *poDTED_DS = (DTEDDataset *) poDS; + int nYSize = poDTED_DS->psDTED->nYSize; + GInt16 *panData; + + (void) nBlockXOff; + CPLAssert( nBlockYOff == 0 ); + + if (nBlockXSize != 1) + { + const int cbs = 32; // optimize for 64 byte cache line size + const int bsy = (nBlockYSize + cbs - 1) / cbs * cbs; + panData = (GInt16 *) pImage; + GInt16* panBuffer = (GInt16*) CPLMalloc(sizeof(GInt16) * cbs * bsy); + for (int i = 0; i < nBlockXSize; i += cbs) { + int n = std::min(cbs, nBlockXSize - i); + for (int j = 0; j < n; ++j) { + if (!DTEDReadProfileEx(poDTED_DS->psDTED, i + j, panBuffer + j * bsy, poDTED_DS->bVerifyChecksum)) { + CPLFree(panBuffer); + return CE_Failure; + } + } + for (int y = 0; y < nBlockYSize; ++y) { + GInt16 *dst = panData + i + (nYSize - y - 1) * nBlockXSize; + GInt16 *src = panBuffer + y; + for (int j = 0; j < n; ++j) { + dst[j] = src[j * bsy]; + } + } + } + + CPLFree(panBuffer); + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Read the data. */ +/* -------------------------------------------------------------------- */ + panData = (GInt16 *) pImage; + if( !DTEDReadProfileEx( poDTED_DS->psDTED, nBlockXOff, panData, + poDTED_DS->bVerifyChecksum ) ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Flip line to orient it top to bottom instead of bottom to */ +/* top. */ +/* -------------------------------------------------------------------- */ + for( int i = nYSize/2; i >= 0; i-- ) + { + std::swap(panData[i], panData[nYSize - i - 1]); + } + + return CE_None; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr DTEDRasterBand::IWriteBlock( int nBlockXOff, + CPL_UNUSED int nBlockYOff, + void * pImage ) +{ + DTEDDataset *poDTED_DS = (DTEDDataset *) poDS; + GInt16 *panData; + + (void) nBlockXOff; + CPLAssert( nBlockYOff == 0 ); + + if (poDTED_DS->eAccess != GA_Update) + return CE_Failure; + + if (nBlockXSize != 1) + { + panData = (GInt16 *) pImage; + GInt16* panBuffer = (GInt16*) CPLMalloc(sizeof(GInt16) * nBlockYSize); + int i; + for(i=0;ipsDTED, i, panBuffer) ) + { + CPLFree(panBuffer); + return CE_Failure; + } + } + + CPLFree(panBuffer); + return CE_None; + } + + panData = (GInt16 *) pImage; + if( !DTEDWriteProfile( poDTED_DS->psDTED, nBlockXOff, panData) ) + return CE_Failure; + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double DTEDRasterBand::GetNoDataValue( int * pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bNoDataSet; + + return dfNoDataValue; +} + +/************************************************************************/ +/* ~DTEDDataset() */ +/************************************************************************/ + +DTEDDataset::DTEDDataset() +{ + pszFilename = CPLStrdup("unknown"); + pszProjection = CPLStrdup(""); + bVerifyChecksum = CSLTestBoolean(CPLGetConfigOption("DTED_VERIFY_CHECKSUM", "NO")); +} + +/************************************************************************/ +/* ~DTEDDataset() */ +/************************************************************************/ + +DTEDDataset::~DTEDDataset() + +{ + FlushCache(); + CPLFree(pszFilename); + CPLFree( pszProjection ); + if( psDTED != NULL ) + DTEDClose( psDTED ); +} + +/************************************************************************/ +/* SetFileName() */ +/************************************************************************/ + +void DTEDDataset::SetFileName(const char* pszFilename) + +{ + CPLFree(this->pszFilename); + this->pszFilename = CPLStrdup(pszFilename); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int DTEDDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Does the file start with one of the possible DTED header */ +/* record types, and do we have a UHL marker? */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 240 ) + return FALSE; + + if( !EQUALN((const char *)poOpenInfo->pabyHeader,"VOL",3) + && !EQUALN((const char *)poOpenInfo->pabyHeader,"HDR",3) + && !EQUALN((const char *)poOpenInfo->pabyHeader,"UHL",3) ) + { + return FALSE; + } + + int bFoundUHL = FALSE; + for(i=0;inHeaderBytes-3 && !bFoundUHL ;i += DTED_UHL_SIZE) + { + if( EQUALN((const char *)poOpenInfo->pabyHeader + i,"UHL", 3) ) + { + bFoundUHL = TRUE; + } + } + if (!bFoundUHL) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *DTEDDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i; + DTEDInfo *psDTED; + + if (!Identify(poOpenInfo) || poOpenInfo->fpL == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ + VSILFILE* fp = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + psDTED = DTEDOpenEx( fp, poOpenInfo->pszFilename, + (poOpenInfo->eAccess == GA_Update) ? "rb+" : "rb", TRUE ); + + if( psDTED == NULL ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + DTEDDataset *poDS; + + poDS = new DTEDDataset(); + poDS->SetFileName(poOpenInfo->pszFilename); + + poDS->eAccess = poOpenInfo->eAccess; + poDS->psDTED = psDTED; + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = psDTED->nXSize; + poDS->nRasterYSize = psDTED->nYSize; + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = 1; + for( i = 0; i < poDS->nBands; i++ ) + poDS->SetBand( i+1, new DTEDRasterBand( poDS, i+1 ) ); + +/* -------------------------------------------------------------------- */ +/* Collect any metadata available. */ +/* -------------------------------------------------------------------- */ + char *pszValue; + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_VERTACCURACY_UHL ); + poDS->SetMetadataItem( "DTED_VerticalAccuracy_UHL", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_VERTACCURACY_ACC ); + poDS->SetMetadataItem( "DTED_VerticalAccuracy_ACC", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_SECURITYCODE_UHL ); + poDS->SetMetadataItem( "DTED_SecurityCode_UHL", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_SECURITYCODE_DSI ); + poDS->SetMetadataItem( "DTED_SecurityCode_DSI", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_UNIQUEREF_UHL ); + poDS->SetMetadataItem( "DTED_UniqueRef_UHL", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_UNIQUEREF_DSI ); + poDS->SetMetadataItem( "DTED_UniqueRef_DSI", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_DATA_EDITION ); + poDS->SetMetadataItem( "DTED_DataEdition", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_MATCHMERGE_VERSION ); + poDS->SetMetadataItem( "DTED_MatchMergeVersion", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_MAINT_DATE ); + poDS->SetMetadataItem( "DTED_MaintenanceDate", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_MATCHMERGE_DATE ); + poDS->SetMetadataItem( "DTED_MatchMergeDate", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_MAINT_DESCRIPTION ); + poDS->SetMetadataItem( "DTED_MaintenanceDescription", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_PRODUCER ); + poDS->SetMetadataItem( "DTED_Producer", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_VERTDATUM ); + poDS->SetMetadataItem( "DTED_VerticalDatum", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_HORIZDATUM ); + poDS->SetMetadataItem( "DTED_HorizontalDatum", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_DIGITIZING_SYS ); + poDS->SetMetadataItem( "DTED_DigitizingSystem", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_COMPILATION_DATE ); + poDS->SetMetadataItem( "DTED_CompilationDate", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_HORIZACCURACY ); + poDS->SetMetadataItem( "DTED_HorizontalAccuracy", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_REL_HORIZACCURACY ); + poDS->SetMetadataItem( "DTED_RelHorizontalAccuracy", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_REL_VERTACCURACY ); + poDS->SetMetadataItem( "DTED_RelVerticalAccuracy", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_ORIGINLAT ); + poDS->SetMetadataItem( "DTED_OriginLatitude", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_ORIGINLONG ); + poDS->SetMetadataItem( "DTED_OriginLongitude", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_NIMA_DESIGNATOR ); + poDS->SetMetadataItem( "DTED_NimaDesignator", pszValue ); + CPLFree( pszValue ); + + pszValue = DTEDGetMetadata( psDTED, DTEDMD_PARTIALCELL_DSI ); + poDS->SetMetadataItem( "DTED_PartialCellIndicator", pszValue ); + CPLFree( pszValue ); + + poDS->SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML( poOpenInfo->GetSiblingFiles() ); + + // if no SR in xml, try aux + const char* pszPrj = poDS->GDALPamDataset::GetProjectionRef(); + if( !pszPrj || strlen(pszPrj) == 0 ) + { + int bTryAux = TRUE; + if( poOpenInfo->GetSiblingFiles() != NULL && + CSLFindString(poOpenInfo->GetSiblingFiles(), CPLResetExtension(CPLGetFilename(poOpenInfo->pszFilename), "aux")) < 0 && + CSLFindString(poOpenInfo->GetSiblingFiles(), CPLSPrintf("%s.aux", CPLGetFilename(poOpenInfo->pszFilename))) < 0 ) + bTryAux = FALSE; + if( bTryAux ) + { + GDALDataset* poAuxDS = GDALFindAssociatedAuxFile( poOpenInfo->pszFilename, GA_ReadOnly, poDS ); + if( poAuxDS ) + { + pszPrj = poAuxDS->GetProjectionRef(); + if( pszPrj && strlen(pszPrj) > 0 ) + { + CPLFree( poDS->pszProjection ); + poDS->pszProjection = CPLStrdup(pszPrj); + } + + GDALClose( poAuxDS ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, + poOpenInfo->GetSiblingFiles() ); + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr DTEDDataset::GetGeoTransform( double * padfTransform ) + +{ + padfTransform[0] = psDTED->dfULCornerX; + padfTransform[1] = psDTED->dfPixelSizeX; + padfTransform[2] = 0.0; + padfTransform[3] = psDTED->dfULCornerY; + padfTransform[4] = 0.0; + padfTransform[5] = psDTED->dfPixelSizeY * -1; + + return( CE_None ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *DTEDDataset::GetProjectionRef() + +{ + // get xml and aux SR first + const char* pszPrj = GDALPamDataset::GetProjectionRef(); + if(pszPrj && strlen(pszPrj) > 0) + return pszPrj; + + if (pszProjection && strlen(pszProjection) > 0) + return pszProjection; + + pszPrj = GetMetadataItem( "DTED_HorizontalDatum"); + if (EQUAL(pszPrj, "WGS84")) + { + return( "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]]" ); + } + else if (EQUAL(pszPrj, "WGS72")) + { + static int bWarned = FALSE; + if (!bWarned) + { + bWarned = TRUE; + CPLError( CE_Warning, CPLE_AppDefined, + "The DTED file %s indicates WGS72 as horizontal datum. \n" + "As this is outdated nowadays, you should contact your data producer to get data georeferenced in WGS84.\n" + "In some cases, WGS72 is a wrong indication and the georeferencing is really WGS84. In that case\n" + "you might consider doing 'gdal_translate -of DTED -mo \"DTED_HorizontalDatum=WGS84\" src.dtX dst.dtX' to\n" + "fix the DTED file.\n" + "No more warnings will be issued in this session about this operation.", GetFileName() ); + } + return "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\",SPHEROID[\"WGS 72\",6378135,298.26]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4322\"]]"; + } + else + { + static int bWarned = FALSE; + if (!bWarned) + { + bWarned = TRUE; + CPLError( CE_Warning, CPLE_AppDefined, + "The DTED file %s indicates %s as horizontal datum, which is not recognized by the DTED driver. \n" + "The DTED driver is going to consider it as WGS84.\n" + "No more warnings will be issued in this session about this operation.", GetFileName(), pszPrj ); + } + return( "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4326\"]]" ); + } +} + +/************************************************************************/ +/* DTEDCreateCopy() */ +/* */ +/* For now we will assume the input is exactly one proper */ +/* cell. */ +/************************************************************************/ + +static GDALDataset * +DTEDCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + (void) pProgressData; + (void) pfnProgress; + (void) papszOptions; + (void) bStrict; + + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "DTED driver does not support source dataset with zero band.\n"); + return NULL; + } + + if (nBands != 1) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "DTED driver only uses the first band of the dataset.\n"); + if (bStrict) + return NULL; + } + + if( pfnProgress && !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Work out the level. */ +/* -------------------------------------------------------------------- */ + int nLevel; + + if( poSrcDS->GetRasterYSize() == 121 ) + nLevel = 0; + else if( poSrcDS->GetRasterYSize() == 1201 ) + nLevel = 1; + else if( poSrcDS->GetRasterYSize() == 3601 ) + nLevel = 2; + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "The source does not appear to be a properly formatted cell." ); + nLevel = 1; + } + +/* -------------------------------------------------------------------- */ +/* Checks the input SRS */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference ogrsr_input; + OGRSpatialReference ogrsr_wgs84; + char* c = (char*)poSrcDS->GetProjectionRef(); + ogrsr_input.importFromWkt(&c); + ogrsr_wgs84.SetWellKnownGeogCS( "WGS84" ); + if ( ogrsr_input.IsSameGeogCS(&ogrsr_wgs84) == FALSE) + { + CPLError( CE_Warning, CPLE_AppDefined, + "The source projection coordinate system is %s. Only WGS 84 is supported.\n" + "The DTED driver will generate a file as if the source was WGS 84 projection coordinate system.", + poSrcDS->GetProjectionRef() ); + } + +/* -------------------------------------------------------------------- */ +/* Work out the LL origin. */ +/* -------------------------------------------------------------------- */ + int nLLOriginLat, nLLOriginLong; + double adfGeoTransform[6]; + + poSrcDS->GetGeoTransform( adfGeoTransform ); + + nLLOriginLat = (int) + floor(adfGeoTransform[3] + + poSrcDS->GetRasterYSize() * adfGeoTransform[5] + 0.5); + + nLLOriginLong = (int) floor(adfGeoTransform[0] + 0.5); + + if (fabs(nLLOriginLat - (adfGeoTransform[3] + + (poSrcDS->GetRasterYSize() - 0.5) * adfGeoTransform[5])) > 1e-10 || + fabs(nLLOriginLong - (adfGeoTransform[0] + 0.5 * adfGeoTransform[1])) > 1e-10) + { + CPLError( CE_Warning, CPLE_AppDefined, + "The corner coordinates of the source are not properly " + "aligned on plain latitude/longitude boundaries."); + } + +/* -------------------------------------------------------------------- */ +/* Check horizontal source size. */ +/* -------------------------------------------------------------------- */ + int expectedXSize; + if( ABS(nLLOriginLat) >= 80 ) + expectedXSize = (poSrcDS->GetRasterYSize() - 1) / 6 + 1; + else if( ABS(nLLOriginLat) >= 75 ) + expectedXSize = (poSrcDS->GetRasterYSize() - 1) / 4 + 1; + else if( ABS(nLLOriginLat) >= 70 ) + expectedXSize = (poSrcDS->GetRasterYSize() - 1) / 3 + 1; + else if( ABS(nLLOriginLat) >= 50 ) + expectedXSize = (poSrcDS->GetRasterYSize() - 1) / 2 + 1; + else + expectedXSize = poSrcDS->GetRasterYSize(); + + if (poSrcDS->GetRasterXSize() != expectedXSize) + { + CPLError( CE_Warning, CPLE_AppDefined, + "The horizontal source size is not conformant with the one " + "expected by DTED Level %d at this latitude (%d pixels found instead of %d).", nLevel, + poSrcDS->GetRasterXSize(), expectedXSize); + } + +/* -------------------------------------------------------------------- */ +/* Create the output dted file. */ +/* -------------------------------------------------------------------- */ + const char *pszError; + + pszError = DTEDCreate( pszFilename, nLevel, nLLOriginLat, nLLOriginLong ); + + if( pszError != NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", pszError ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open the DTED file so we can output the data to it. */ +/* -------------------------------------------------------------------- */ + DTEDInfo *psDTED; + + psDTED = DTEDOpen( pszFilename, "rb+", FALSE ); + if( psDTED == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Read all the data in a single buffer. */ +/* -------------------------------------------------------------------- */ + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( 1 ); + GInt16 *panData; + + panData = (GInt16 *) + VSIMalloc(sizeof(GInt16) * psDTED->nXSize * psDTED->nYSize); + if (panData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + DTEDClose(psDTED); + return NULL; + } + + for( int iY = 0; iY < psDTED->nYSize; iY++ ) + { + poSrcBand->RasterIO( GF_Read, 0, iY, psDTED->nXSize, 1, + (void *) (panData + iY * psDTED->nXSize), psDTED->nXSize, 1, + GDT_Int16, 0, 0, NULL ); + + if( pfnProgress && !pfnProgress(0.5 * (iY+1) / (double) psDTED->nYSize, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + DTEDClose( psDTED ); + CPLFree( panData ); + return NULL; + } + } + + int bSrcBandHasNoData; + double srcBandNoData = poSrcBand->GetNoDataValue(&bSrcBandHasNoData); + +/* -------------------------------------------------------------------- */ +/* Write all the profiles. */ +/* -------------------------------------------------------------------- */ + GInt16 anProfData[3601]; + int dfNodataCount=0; + GByte iPartialCell; + + for( int iProfile = 0; iProfile < psDTED->nXSize; iProfile++ ) + { + for( int iY = 0; iY < psDTED->nYSize; iY++ ) + { + anProfData[iY] = panData[iProfile + iY * psDTED->nXSize]; + if ( bSrcBandHasNoData && anProfData[iY] == srcBandNoData) + { + anProfData[iY] = DTED_NODATA_VALUE; + dfNodataCount++; + } + else if ( anProfData[iY] == DTED_NODATA_VALUE ) + dfNodataCount++; + } + DTEDWriteProfile( psDTED, iProfile, anProfData ); + + if( pfnProgress + && !pfnProgress( 0.5 + 0.5 * (iProfile+1) / (double) psDTED->nXSize, + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + DTEDClose( psDTED ); + CPLFree( panData ); + return NULL; + } + } + CPLFree( panData ); + +/* -------------------------------------------------------------------- */ +/* Partial cell indicator: 0 for complete coverage; 1-99 for incomplete */ +/* -------------------------------------------------------------------- */ + char szPartialCell[3]; + + if ( dfNodataCount == 0 ) + iPartialCell = 0; + else + { + iPartialCell = (GByte)int(floor(100.0 - + (dfNodataCount*100.0/(psDTED->nXSize * psDTED->nYSize)))); + if (iPartialCell < 1) + iPartialCell=1; + } + sprintf(szPartialCell,"%02d",iPartialCell); + DTEDSetMetadata(psDTED, DTEDMD_PARTIALCELL_DSI, szPartialCell); + +/* -------------------------------------------------------------------- */ +/* Try to copy any matching available metadata. */ +/* -------------------------------------------------------------------- */ + if( poSrcDS->GetMetadataItem( "DTED_VerticalAccuracy_UHL" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_VERTACCURACY_UHL, + poSrcDS->GetMetadataItem( "DTED_VerticalAccuracy_UHL" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_VerticalAccuracy_ACC" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_VERTACCURACY_ACC, + poSrcDS->GetMetadataItem( "DTED_VerticalAccuracy_ACC" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_SecurityCode_UHL" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_SECURITYCODE_UHL, + poSrcDS->GetMetadataItem( "DTED_SecurityCode_UHL" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_SecurityCode_DSI" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_SECURITYCODE_DSI, + poSrcDS->GetMetadataItem( "DTED_SecurityCode_DSI" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_UniqueRef_UHL" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_UNIQUEREF_UHL, + poSrcDS->GetMetadataItem( "DTED_UniqueRef_UHL" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_UniqueRef_DSI" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_UNIQUEREF_DSI, + poSrcDS->GetMetadataItem( "DTED_UniqueRef_DSI" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_DataEdition" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_DATA_EDITION, + poSrcDS->GetMetadataItem( "DTED_DataEdition" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_MatchMergeVersion" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_MATCHMERGE_VERSION, + poSrcDS->GetMetadataItem( "DTED_MatchMergeVersion" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_MaintenanceDate" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_MAINT_DATE, + poSrcDS->GetMetadataItem( "DTED_MaintenanceDate" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_MatchMergeDate" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_MATCHMERGE_DATE, + poSrcDS->GetMetadataItem( "DTED_MatchMergeDate" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_MaintenanceDescription" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_MAINT_DESCRIPTION, + poSrcDS->GetMetadataItem( "DTED_MaintenanceDescription" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_Producer" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_PRODUCER, + poSrcDS->GetMetadataItem( "DTED_Producer" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_VerticalDatum" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_VERTDATUM, + poSrcDS->GetMetadataItem( "DTED_VerticalDatum" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_HorizontalDatum" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_HORIZDATUM, + poSrcDS->GetMetadataItem( "DTED_HorizontalDatum" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_DigitizingSystem" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_DIGITIZING_SYS, + poSrcDS->GetMetadataItem( "DTED_DigitizingSystem" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_CompilationDate" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_COMPILATION_DATE, + poSrcDS->GetMetadataItem( "DTED_CompilationDate" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_HorizontalAccuracy" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_HORIZACCURACY, + poSrcDS->GetMetadataItem( "DTED_HorizontalAccuracy" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_RelHorizontalAccuracy" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_REL_HORIZACCURACY, + poSrcDS->GetMetadataItem( "DTED_RelHorizontalAccuracy" ) ); + + if( poSrcDS->GetMetadataItem( "DTED_RelVerticalAccuracy" ) != NULL ) + DTEDSetMetadata( psDTED, DTEDMD_REL_VERTACCURACY, + poSrcDS->GetMetadataItem( "DTED_RelVerticalAccuracy" ) ); + +/* -------------------------------------------------------------------- */ +/* Try to open the resulting DTED file. */ +/* -------------------------------------------------------------------- */ + DTEDClose( psDTED ); + +/* -------------------------------------------------------------------- */ +/* Reopen and copy missing information into a PAM file. */ +/* -------------------------------------------------------------------- */ + GDALPamDataset *poDS = (GDALPamDataset *) + GDALOpen( pszFilename, GA_ReadOnly ); + + if( poDS ) + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_DTED() */ +/************************************************************************/ + +void GDALRegister_DTED() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "DTED" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "DTED" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "DTED Elevation Raster" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "dt0 dt1 dt2" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#DTED" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = DTEDDataset::Open; + poDriver->pfnIdentify = DTEDDataset::Identify; + poDriver->pfnCreateCopy = DTEDCreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/dted/frmt_dted.html b/bazaar/plugin/gdal/frmts/dted/frmt_dted.html new file mode 100644 index 000000000..96e4034cd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dted/frmt_dted.html @@ -0,0 +1,62 @@ + + +DTED -- Military Elevation Data + + + + +

DTED -- Military Elevation Data

+ +GDAL supports DTED Levels 0, 1, and 2 elevation data for read access. +Elevation data is returned as 16 bit signed integer. Appropriate +projection and georeferencing information is also returned. +A variety of header fields are returned dataset level metadata. +

+ +

Read Issues

+ +

Read speed

+ +Elevation data in DTED files are organized per columns. This data organization doesn't fit very well +with some scanline oriented algorithms and can cause slowdowns, especially for DTED Level 2 datasets. +By defining GDAL_DTED_SINGLE_BLOCK=TRUE, a whole DTED dataset will be considered as a single block. The +first access to the file will be slow, but further accesses will be much quicker. Only use that option if +you need to do processing on a whole file. + +

Georeferencing Issues

+ +The DTED specification (MIL-PRF-89020B) states that +horizontal datum shall be the World Geodetic System (WGS 84). However, there are still people using old data files +georeferenced in WGS 72. A header field indicates the horizontal datum code, so we can detect and handle this situation. +
+
    +
  • If the horizontal datum specified in the DTED file is WGS84, the DTED driver will report WGS 84 as SRS.
  • +
  • If the horizontal datum specified in the DTED file is WGS72, the DTED driver will report WGS 72 as SRS and issue a warning.
  • +
  • If the horizontal datum specified in the DTED file is neither WGS84 nor WGS72, the DTED driver will report WGS 84 as SRS and issue a warning.
  • +
+ +

Checksum Issues

+ +The default behaviour of the DTED driver is to ignore the checksum while +reading data from the files. However, you may specify the environment +variable DTED_VERIFY_CHECKSUM=YES if you want the checksums to be verified. +In some cases, the checksum written in the DTED file is wrong (the data producer did a wrong job). +This will be reported as a warning. +If the checksum written in the DTED file and the checksum computed from the data do not match, an +error will be issued. + +

Creation Issues

+ +The DTED driver does support creating new files, but the input data +must be exactly formatted as a Level 0, 1 or 2 cell. That is the size, +and bounds must be appropriate for a cell.

+ +

See Also:

+ +
    +
  • Implemented as gdal/frmts/dted/dteddataset.cpp.

    + +

+ + + diff --git a/bazaar/plugin/gdal/frmts/dted/makefile.vc b/bazaar/plugin/gdal/frmts/dted/makefile.vc new file mode 100644 index 000000000..0a695fa0e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/dted/makefile.vc @@ -0,0 +1,18 @@ + +OBJ = dted_api.obj dteddataset.obj dted_create.obj dted_create.obj \ + dted_ptstream.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + +dted_test.exe: dted_test.obj $(OBJ) + cl dted_test.obj dted_api.obj dted_create.obj dted_ptstream.obj \ + ..\..\port\cpl.lib + diff --git a/bazaar/plugin/gdal/frmts/e00grid/GNUmakefile b/bazaar/plugin/gdal/frmts/e00grid/GNUmakefile new file mode 100644 index 000000000..056bd4673 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/e00grid/GNUmakefile @@ -0,0 +1,15 @@ + +include ../../GDALmake.opt + +OBJ = e00griddataset.o + +CPPFLAGS := $(CPPFLAGS) $(XTRA_OPT) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/e00grid/e00compr.h b/bazaar/plugin/gdal/frmts/e00grid/e00compr.h new file mode 100644 index 000000000..a92bd934d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/e00grid/e00compr.h @@ -0,0 +1,181 @@ +/********************************************************************** + * $Id: e00compr.h,v 1.10 2009-02-24 20:03:50 aboudreault Exp $ + * + * Name: e00compr.h + * Project: Compressed E00 Read/Write library + * Language: ANSI C + * Purpose: Header file containing all definitions for the library. + * Author: Daniel Morissette, dmorissette@mapgears.com + * + * $Log: e00compr.h,v $ + * Revision 1.10 2009-02-24 20:03:50 aboudreault + * Added a short manual pages (#1875) + * Updated documentation and code examples (#247) + * + * Revision 1.9 2005-09-17 14:22:05 daniel + * Switch to MIT license, update refs to website and email address, and + * prepare for 1.0.0 release. + * + * Revision 1.8 1999/03/03 18:47:07 daniel + * Moved extern "C" after #include lines (form MSVC++ 6) + * + * Revision 1.7 1999/02/25 18:47:40 daniel + * Now use CPL for Error handling, Memory allocation, and File access. + * + * Revision 1.6 1999/01/08 17:40:33 daniel + * Added E00Read/WriteCallbakcOpen() + * + * Revision 1.5 1998/11/13 15:39:45 daniel + * Added functions for write support. + * + * Revision 1.4 1998/11/02 18:37:03 daniel + * New file header, and added E00ErrorReset() + * + * Revision 1.1 1998/10/29 13:26:00 daniel + * Initial revision + * + ********************************************************************** + * Copyright (c) 1998-2005, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + **********************************************************************/ + +#ifndef _E00COMPR_H_INCLUDED_ +#define _E00COMPR_H_INCLUDED_ + + +#include + +#include "cpl_port.h" +#include "cpl_conv.h" +#include "cpl_error.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*--------------------------------------------------------------------- + * Current version of the library... always useful! + *--------------------------------------------------------------------*/ +#define E00COMPR_VERSION "1.0.0 (2005-09-17)" + +/*===================================================================== + Data types and constants + =====================================================================*/ + +#define E00_READ_BUF_SIZE 256 /* E00 lines are always 80 chars or less */ + /* for both compressed and uncompressed */ + /* files, except the first line (the EXP)*/ + /* for which there is no known limit */ + /* We'll assume that it can't be longer */ + /* than 256 chars */ + +#define E00_WRITE_BUF_SIZE 256 /* This buffer must be big enough to hold*/ + /* at least 2 lines of compressed output */ + /* (i.e. 160 chars)... but just in case */ + /* compressing a line would ever result */ + /* in it becoming bigger than its source */ + /* we'll set the size to 256 chars! */ + +#define E00_COMPR_NONE 0 /* Compression levels to use when writing*/ +#define E00_COMPR_PARTIAL 1 +#define E00_COMPR_FULL 2 + +/*--------------------------------------------------------------------- + * E00ReadPtr + * + * A E00ReadPtr handle is used to hold information about the compressed + * file currently being read. + *--------------------------------------------------------------------*/ +struct _E00ReadInfo +{ + FILE *fp; /* Input file handle */ + int bEOF; /* Reached EOF? */ + int bIsCompressed; /* 1 if file is compressed, 0 if not */ + int nInputLineNo; + + int iInBufPtr; /* Last character processed in szInBuf */ + char szInBuf[E00_READ_BUF_SIZE]; /* compressed input buffer */ + char szOutBuf[E00_READ_BUF_SIZE];/* uncompressed output buffer */ + + /* pRefData, pfnReadNextLine() and pfnReadRewind() are used only + * when the file is opened with E00ReadCallbackOpen() + * (and in this case the FILE *fp defined above is not used) + */ + void * pRefData; + const char * (*pfnReadNextLine)(void *); + void (*pfnReadRewind)(void *); +}; + +typedef struct _E00ReadInfo *E00ReadPtr; + +/*--------------------------------------------------------------------- + * E00WritePtr + * + * A E00WritePtr handle is used to hold information about the + * file currently being written. + *--------------------------------------------------------------------*/ +struct _E00WriteInfo +{ + FILE *fp; /* Output file handle */ + int nComprLevel; + + int nSrcLineNo; + + int iOutBufPtr; /* Current position in szOutBuf */ + char szOutBuf[E00_WRITE_BUF_SIZE]; /* compressed output buffer */ + + /* pRefData and pfnWriteNextLine() are used only + * when the file is opened with E00WriteCallbackOpen() + * (and in this case the FILE *fp defined above is not used) + */ + void *pRefData; + int (*pfnWriteNextLine)(void *, const char *); +}; + +typedef struct _E00WriteInfo *E00WritePtr; + + +/*===================================================================== + Function prototypes + =====================================================================*/ + +E00ReadPtr E00ReadOpen(const char *pszFname); +E00ReadPtr E00ReadCallbackOpen(void *pRefData, + const char * (*pfnReadNextLine)(void *), + void (*pfnReadRewind)(void *)); +void E00ReadClose(E00ReadPtr psInfo); + +const char *E00ReadNextLine(E00ReadPtr psInfo); +void E00ReadRewind(E00ReadPtr psInfo); + +E00WritePtr E00WriteOpen(const char *pszFname, int nComprLevel); +E00WritePtr E00WriteCallbackOpen(void *pRefData, + int (*pfnWriteNextLine)(void *, const char *), + int nComprLevel); +void E00WriteClose(E00WritePtr psInfo); +int E00WriteNextLine(E00WritePtr psInfo, const char *pszLine); + + +#ifdef __cplusplus +} +#endif + +#endif /* _E00COMPR_H_INCLUDED_ */ diff --git a/bazaar/plugin/gdal/frmts/e00grid/e00griddataset.cpp b/bazaar/plugin/gdal/frmts/e00grid/e00griddataset.cpp new file mode 100644 index 000000000..abd9bf740 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/e00grid/e00griddataset.cpp @@ -0,0 +1,924 @@ +/****************************************************************************** + * $Id: e00griddataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: E00 grid driver + * Purpose: GDALDataset driver for E00 grid dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" +#include "gdal_pam.h" + +/* Private import of e00read.c */ +#define E00ReadOpen GDALE00GRIDReadOpen +#define E00ReadCallbackOpen GDALE00GRIDReadCallbackOpen +#define E00ReadClose GDALE00GRIDReadClose +#define E00ReadNextLine GDALE00GRIDReadNextLine +#define E00ReadRewind GDALE00GRIDReadRewind +#include "e00read.c" + +#define E00_INT_SIZE 10 +#define E00_INT14_SIZE 14 +#define E00_FLOAT_SIZE 14 +#define E00_DOUBLE_SIZE 21 +#define VALS_PER_LINE 5 + +CPL_CVSID("$Id: e00griddataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_E00GRID(void); +CPL_C_END + +/* g++ -fPIC -Wall -g frmts/e00grid/e00griddataset.cpp -shared -o gdal_E00GRID.so -Iport -Igcore -Iogr -L. -lgdal */ + +/* Test data ; (google for "EXP 0" "GRD 2") + +ftp://msdis.missouri.edu/pub/dem/24k/county/ +http://dusk.geo.orst.edu/djl/samoa/data/samoa_bathy.e00 +http://dusk.geo.orst.edu/djl/samoa/FBNMS/RasterGrids-Metadata/ntae02_3m_utm.e00 +http://www.navdat.org/coverages/elevation/iddem1.e00 (int32) +http://delta-vision.projects.atlas.ca.gov/lidar/bare_earth.grids/sac0165.e00 +http://ag.arizona.edu/SRER/maps_e00/srer_dem.e00 +http://ok.water.usgs.gov/projects/norlan/spatial/ntopo0408-10.e00 (compressed) +http://wrri.nmsu.edu/publish/techrpt/tr322/GIS/dem.e00 (compressed) +*/ + +/************************************************************************/ +/* ==================================================================== */ +/* E00GRIDDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class E00GRIDRasterBand; + +class E00GRIDDataset : public GDALPamDataset +{ + friend class E00GRIDRasterBand; + + E00ReadPtr e00ReadPtr; + VSILFILE *fp; + vsi_l_offset nDataStart; + int nBytesEOL; + + vsi_l_offset nPosBeforeReadLine; + vsi_l_offset* panOffsets; + int nLastYOff; + int nMaxYOffset; + + double adfGeoTransform[6]; + CPLString osProjection; + + double dfNoData; + + char** papszPrj; + + const char* ReadLine(); + + int bHasReadMetadata; + void ReadMetadata(); + + int bHasStats; + double dfMin, dfMax, dfMean, dfStddev; + + static const char* ReadNextLine(void * ptr); + static void Rewind(void* ptr); + + public: + E00GRIDDataset(); + virtual ~E00GRIDDataset(); + + virtual CPLErr GetGeoTransform( double * ); + virtual const char* GetProjectionRef(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* E00GRIDRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class E00GRIDRasterBand : public GDALPamRasterBand +{ + friend class E00GRIDDataset; + + public: + + E00GRIDRasterBand( E00GRIDDataset *, int, GDALDataType ); + + virtual CPLErr IReadBlock( int, int, void * ); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual const char *GetUnitType(); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum( int *pbSuccess = NULL ); + virtual CPLErr GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *padfStdDev ); +}; + + +/************************************************************************/ +/* E00GRIDRasterBand() */ +/************************************************************************/ + +E00GRIDRasterBand::E00GRIDRasterBand( E00GRIDDataset *poDS, int nBand, + GDALDataType eDT ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = eDT; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr E00GRIDRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + E00GRIDDataset *poGDS = (E00GRIDDataset *) poDS; + + char szVal[E00_FLOAT_SIZE+1]; + szVal[E00_FLOAT_SIZE] = 0; + + int i; + float* pafImage = (float*)pImage; + int* panImage = (int*)pImage; + const float fNoData = (const float)poGDS->dfNoData; + + /* A new data line begins on a new text line. So if the xsize */ + /* is not a multiple of VALS_PER_LINE, there are padding values */ + /* that must be ignored */ + const int nRoundedBlockXSize = ((nBlockXSize + VALS_PER_LINE - 1) / + VALS_PER_LINE) * VALS_PER_LINE; + + if (poGDS->e00ReadPtr) + { + if (poGDS->nLastYOff < 0) + { + E00ReadRewind(poGDS->e00ReadPtr); + for(i=0;i<6;i++) + E00ReadNextLine(poGDS->e00ReadPtr); + } + + if (nBlockYOff == poGDS->nLastYOff + 1) + { + } + else if (nBlockYOff <= poGDS->nMaxYOffset) + { + //CPLDebug("E00GRID", "Skip to %d from %d", nBlockYOff, poGDS->nLastYOff); + VSIFSeekL(poGDS->fp, poGDS->panOffsets[nBlockYOff], SEEK_SET); + poGDS->nPosBeforeReadLine = poGDS->panOffsets[nBlockYOff]; + poGDS->e00ReadPtr->iInBufPtr = 0; + poGDS->e00ReadPtr->szInBuf[0] = '\0'; + } + else if (nBlockYOff > poGDS->nLastYOff + 1) + { + //CPLDebug("E00GRID", "Forward skip to %d from %d", nBlockYOff, poGDS->nLastYOff); + for(i=poGDS->nLastYOff + 1; i < nBlockYOff;i++) + IReadBlock(0, i, pImage); + } + + if (nBlockYOff > poGDS->nMaxYOffset) + { + poGDS->panOffsets[nBlockYOff] = poGDS->nPosBeforeReadLine + + poGDS->e00ReadPtr->iInBufPtr; + poGDS->nMaxYOffset = nBlockYOff; + } + + const char* pszLine = NULL; + for(i=0;ie00ReadPtr); + if (pszLine == NULL || strlen(pszLine) < 5 * E00_FLOAT_SIZE) + return CE_Failure; + } + if (eDataType == GDT_Float32) + { + pafImage[i] = (float) CPLAtof(pszLine + (i%VALS_PER_LINE) * E00_FLOAT_SIZE); + /* Workaround single vs double precision problems */ + if (fNoData != 0 && fabs((pafImage[i] - fNoData)/fNoData) < 1e-6) + pafImage[i] = fNoData; + } + else + { + panImage[i] = atoi(pszLine + (i%VALS_PER_LINE) * E00_FLOAT_SIZE); + } + } + + poGDS->nLastYOff = nBlockYOff; + + return CE_None; + } + + vsi_l_offset nValsToSkip = (vsi_l_offset)nBlockYOff * nRoundedBlockXSize; + vsi_l_offset nLinesToSkip = nValsToSkip / VALS_PER_LINE; + int nBytesPerLine = VALS_PER_LINE * E00_FLOAT_SIZE + poGDS->nBytesEOL; + vsi_l_offset nPos = poGDS->nDataStart + nLinesToSkip * nBytesPerLine; + VSIFSeekL(poGDS->fp, nPos, SEEK_SET); + + for(i=0;ifp) != 1) + return CE_Failure; + + if (eDataType == GDT_Float32) + { + pafImage[i] = (float) CPLAtof(szVal); + /* Workaround single vs double precision problems */ + if (fNoData != 0 && fabs((pafImage[i] - fNoData)/fNoData) < 1e-6) + pafImage[i] = fNoData; + } + else + { + panImage[i] = atoi(szVal); + } + + if (((i+1) % VALS_PER_LINE) == 0) + VSIFReadL(szVal, poGDS->nBytesEOL, 1, poGDS->fp); + } + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double E00GRIDRasterBand::GetNoDataValue( int *pbSuccess ) +{ + E00GRIDDataset *poGDS = (E00GRIDDataset *) poDS; + + if (pbSuccess) + *pbSuccess = TRUE; + + if (eDataType == GDT_Float32) + return (double)(float) poGDS->dfNoData; + else + return (double)(int)poGDS->dfNoData; +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +const char * E00GRIDRasterBand::GetUnitType() +{ + E00GRIDDataset *poGDS = (E00GRIDDataset *) poDS; + + poGDS->ReadMetadata(); + + if (poGDS->papszPrj == NULL) + return GDALPamRasterBand::GetUnitType(); + + char** papszIter = poGDS->papszPrj; + const char* pszRet = ""; + while(*papszIter) + { + if (EQUALN(*papszIter, "Zunits", 6)) + { + char** papszTokens = CSLTokenizeString(*papszIter); + if (CSLCount(papszTokens) == 2) + { + if (EQUAL(papszTokens[1], "FEET")) + pszRet = "ft"; + else if (EQUAL(papszTokens[1], "METERS")) + pszRet = "m"; + } + CSLDestroy(papszTokens); + break; + } + papszIter ++; + } + + return pszRet; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double E00GRIDRasterBand::GetMinimum( int *pbSuccess ) +{ + E00GRIDDataset *poGDS = (E00GRIDDataset *) poDS; + + poGDS->ReadMetadata(); + + if (poGDS->bHasStats) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return poGDS->dfMin; + } + + return GDALPamRasterBand::GetMinimum( pbSuccess ); +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double E00GRIDRasterBand::GetMaximum( int *pbSuccess ) +{ + E00GRIDDataset *poGDS = (E00GRIDDataset *) poDS; + + poGDS->ReadMetadata(); + + if (poGDS->bHasStats) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return poGDS->dfMax; + } + + return GDALPamRasterBand::GetMaximum( pbSuccess ); +} + +/************************************************************************/ +/* GetStatistics() */ +/************************************************************************/ + +CPLErr E00GRIDRasterBand::GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev ) +{ + E00GRIDDataset *poGDS = (E00GRIDDataset *) poDS; + + poGDS->ReadMetadata(); + + if (poGDS->bHasStats) + { + if (pdfMin) + *pdfMin = poGDS->dfMin; + if (pdfMax) + *pdfMax = poGDS->dfMax; + if (pdfMean) + *pdfMean = poGDS->dfMean; + if (pdfStdDev) + *pdfStdDev = poGDS->dfStddev; + return CE_None; + } + + return GDALPamRasterBand::GetStatistics(bApproxOK, bForce, + pdfMin, pdfMax, + pdfMean, pdfStdDev); +} + +/************************************************************************/ +/* E00GRIDDataset() */ +/************************************************************************/ + +E00GRIDDataset::E00GRIDDataset() +{ + e00ReadPtr = NULL; + fp = NULL; + nDataStart = 0; + nBytesEOL = 1; + + nPosBeforeReadLine = 0; + panOffsets = NULL; + nLastYOff = -1; + nMaxYOffset = -1; + + adfGeoTransform[0] = 0; + adfGeoTransform[1] = 1; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = 0; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = 1; + + dfNoData = 0; + + papszPrj = NULL; + + bHasReadMetadata = FALSE; + + bHasStats = FALSE; + dfMin = 0; + dfMax = 0; + dfMean = 0; + dfStddev = 0; +} + +/************************************************************************/ +/* ~E00GRIDDataset() */ +/************************************************************************/ + +E00GRIDDataset::~E00GRIDDataset() + +{ + FlushCache(); + if (fp) + VSIFCloseL(fp); + CSLDestroy(papszPrj); + E00ReadClose(e00ReadPtr); + CPLFree(panOffsets); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int E00GRIDDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + if (poOpenInfo->nHeaderBytes == 0) + return FALSE; + + if (!(EQUALN((const char*)poOpenInfo->pabyHeader, "EXP 0", 6) || + EQUALN((const char*)poOpenInfo->pabyHeader, "EXP 1", 6))) + return FALSE; + + /* FIXME: handle GRD 3 if that ever exists ? */ + if (strstr((const char*)poOpenInfo->pabyHeader, "GRD 2") == NULL) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* ReadNextLine() */ +/************************************************************************/ + +const char* E00GRIDDataset::ReadNextLine(void * ptr) +{ + E00GRIDDataset* poDS = (E00GRIDDataset*) ptr; + poDS->nPosBeforeReadLine = VSIFTellL(poDS->fp); + return CPLReadLine2L(poDS->fp, 256, NULL); +} + +/************************************************************************/ +/* Rewind() */ +/************************************************************************/ + +void E00GRIDDataset::Rewind(void * ptr) +{ + E00GRIDDataset* poDS = (E00GRIDDataset*) ptr; + VSIRewindL(poDS->fp); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *E00GRIDDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i; + GDALDataType eDT = GDT_Float32; + + if (!Identify(poOpenInfo)) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Find dataset characteristics */ +/* -------------------------------------------------------------------- */ + VSILFILE* fp = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + if (fp == NULL) + return NULL; + + if (poOpenInfo->eAccess == GA_Update) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The E00GRID driver does not support update access to existing" + " datasets.\n" ); + VSIFCloseL(fp); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + E00GRIDDataset *poDS; + + poDS = new E00GRIDDataset(); + if (strstr((const char*)poOpenInfo->pabyHeader, "\r\n") != NULL) + poDS->nBytesEOL = 2; + poDS->fp = fp; + + const char* pszLine; + /* read EXP 0 or EXP 1 line */ + pszLine = CPLReadLine2L(fp, 81, NULL); + if (pszLine == NULL) + { + CPLDebug("E00GRID", "Bad 1st line"); + delete poDS; + return NULL; + } + int bCompressed = EQUALN(pszLine, "EXP 1", 6); + + E00ReadPtr e00ReadPtr = NULL; + if (bCompressed) + { + VSIRewindL(fp); + e00ReadPtr = E00ReadCallbackOpen(poDS, + E00GRIDDataset::ReadNextLine, + E00GRIDDataset::Rewind); + if (e00ReadPtr == NULL) + { + delete poDS; + return NULL; + } + E00ReadNextLine(e00ReadPtr); + poDS->e00ReadPtr = e00ReadPtr; + } + + /* skip GRD 2 line */ + if (e00ReadPtr) + pszLine = E00ReadNextLine(e00ReadPtr); + else + pszLine = CPLReadLine2L(fp, 81, NULL); + if (pszLine == NULL || !EQUALN(pszLine, "GRD 2", 6)) + { + CPLDebug("E00GRID", "Bad 2nd line"); + delete poDS; + return NULL; + } + + /* read ncols, nrows and nodata value */ + if (e00ReadPtr) + pszLine = E00ReadNextLine(e00ReadPtr); + else + pszLine = CPLReadLine2L(fp, 81, NULL); + if (pszLine == NULL || strlen(pszLine) < + E00_INT_SIZE+E00_INT_SIZE+2+E00_DOUBLE_SIZE) + { + CPLDebug("E00GRID", "Bad 3rd line"); + delete poDS; + return NULL; + } + + int nRasterXSize = atoi(pszLine); + int nRasterYSize = atoi(pszLine + E00_INT_SIZE); + + if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize)) + { + delete poDS; + return NULL; + } + + if (EQUALN(pszLine + E00_INT_SIZE + E00_INT_SIZE, " 1", 2)) + eDT = GDT_Int32; + else if (EQUALN(pszLine + E00_INT_SIZE + E00_INT_SIZE, " 2", 2)) + eDT = GDT_Float32; + else + { + CPLDebug("E00GRID", "Unknown data type : %s", pszLine); + } + + double dfNoData = CPLAtof(pszLine + E00_INT_SIZE + E00_INT_SIZE + 2); + + /* read pixel size */ + if (e00ReadPtr) + pszLine = E00ReadNextLine(e00ReadPtr); + else + pszLine = CPLReadLine2L(fp, 81, NULL); + if (pszLine == NULL || strlen(pszLine) < 2*E00_DOUBLE_SIZE) + { + CPLDebug("E00GRID", "Bad 4th line"); + delete poDS; + return NULL; + } +/* + double dfPixelX = CPLAtof(pszLine); + double dfPixelY = CPLAtof(pszLine + E00_DOUBLE_SIZE); +*/ + + /* read xmin, ymin */ + if (e00ReadPtr) + pszLine = E00ReadNextLine(e00ReadPtr); + else + pszLine = CPLReadLine2L(fp, 81, NULL); + if (pszLine == NULL || strlen(pszLine) < 2*E00_DOUBLE_SIZE) + { + CPLDebug("E00GRID", "Bad 5th line"); + delete poDS; + return NULL; + } + double dfMinX = CPLAtof(pszLine); + double dfMinY = CPLAtof(pszLine + E00_DOUBLE_SIZE); + + /* read xmax, ymax */ + if (e00ReadPtr) + pszLine = E00ReadNextLine(e00ReadPtr); + else + pszLine = CPLReadLine2L(fp, 81, NULL); + if (pszLine == NULL || strlen(pszLine) < 2*E00_DOUBLE_SIZE) + { + CPLDebug("E00GRID", "Bad 6th line"); + delete poDS; + return NULL; + } + double dfMaxX = CPLAtof(pszLine); + double dfMaxY = CPLAtof(pszLine + E00_DOUBLE_SIZE); + + poDS->nRasterXSize = nRasterXSize; + poDS->nRasterYSize = nRasterYSize; + poDS->dfNoData = dfNoData; + poDS->adfGeoTransform[0] = dfMinX; + poDS->adfGeoTransform[1] = (dfMaxX - dfMinX) / nRasterXSize; + poDS->adfGeoTransform[2] = 0; + poDS->adfGeoTransform[3] = dfMaxY; + poDS->adfGeoTransform[4] = 0; + poDS->adfGeoTransform[5] = - (dfMaxY - dfMinY) / nRasterYSize; + poDS->nDataStart = VSIFTellL(fp); + if (bCompressed) + { + poDS->panOffsets = (vsi_l_offset*) + VSIMalloc2(sizeof(vsi_l_offset), nRasterYSize); + if (poDS->panOffsets == NULL) + { + delete poDS; + return NULL; + } + } +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = 1; + for( i = 0; i < poDS->nBands; i++ ) + poDS->SetBand( i+1, new E00GRIDRasterBand( poDS, i+1, eDT ) ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr E00GRIDDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double)); + + return( CE_None ); +} + + +/************************************************************************/ +/* ReadLine() */ +/************************************************************************/ + +const char* E00GRIDDataset::ReadLine() +{ + if (e00ReadPtr) + return E00ReadNextLine(e00ReadPtr); + else + return CPLReadLine2L(fp, 81, NULL); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* E00GRIDDataset::GetProjectionRef() + +{ + ReadMetadata(); + return osProjection.c_str(); +} + +/************************************************************************/ +/* ReadMetadata() */ +/************************************************************************/ + +void E00GRIDDataset::ReadMetadata() + +{ + if (bHasReadMetadata) + return; + + bHasReadMetadata = TRUE; + + if (e00ReadPtr == NULL) + { + int nRoundedBlockXSize = ((nRasterXSize + VALS_PER_LINE - 1) / + VALS_PER_LINE) * VALS_PER_LINE; + vsi_l_offset nValsToSkip = + (vsi_l_offset)nRasterYSize * nRoundedBlockXSize; + vsi_l_offset nLinesToSkip = nValsToSkip / VALS_PER_LINE; + int nBytesPerLine = VALS_PER_LINE * E00_FLOAT_SIZE + nBytesEOL; + vsi_l_offset nPos = nDataStart + nLinesToSkip * nBytesPerLine; + VSIFSeekL(fp, nPos, SEEK_SET); + } + else + { + nLastYOff = -1; + + const unsigned int BUFFER_SIZE = 65536; + const unsigned int NEEDLE_SIZE = 3*5; + const unsigned int nToRead = BUFFER_SIZE - NEEDLE_SIZE; + char* pabyBuffer = (char*)CPLCalloc(1, BUFFER_SIZE+NEEDLE_SIZE); + int nRead; + int bEOGFound = FALSE; + + VSIFSeekL(fp, 0, SEEK_END); + vsi_l_offset nEndPos = VSIFTellL(fp); + if (nEndPos > BUFFER_SIZE) + nEndPos -= BUFFER_SIZE; + else + nEndPos = 0; + VSIFSeekL(fp, nEndPos, SEEK_SET); + +#define GOTO_NEXT_CHAR() \ + i ++; \ + if (pabyBuffer[i] == 13 || pabyBuffer[i] == 10) \ + { \ + i++; \ + if (pabyBuffer[i] == 10) \ + i++; \ + } \ + + while ((nRead = VSIFReadL(pabyBuffer, 1, nToRead, fp)) != 0) + { + int i; + for(i = 0; i < nRead; i++) + { + if (pabyBuffer[i] == 'E') + { + GOTO_NEXT_CHAR(); + if (pabyBuffer[i] == 'O') + { + GOTO_NEXT_CHAR(); + if (pabyBuffer[i] == 'G') + { + GOTO_NEXT_CHAR(); + if (pabyBuffer[i] == '~') + { + GOTO_NEXT_CHAR(); + if (pabyBuffer[i] == '}') + { + bEOGFound = TRUE; + break; + } + } + } + } + } + } + + if (bEOGFound) + { + VSIFSeekL(fp, VSIFTellL(fp) - nRead + i + 1, SEEK_SET); + e00ReadPtr->iInBufPtr = 0; + e00ReadPtr->szInBuf[0] = '\0'; + break; + } + + if (nEndPos == 0) + break; + + if ((unsigned int)nRead == nToRead) + { + memmove(pabyBuffer + nToRead, pabyBuffer, NEEDLE_SIZE); + if (nEndPos >= (vsi_l_offset)nToRead) + nEndPos -= nToRead; + else + nEndPos = 0; + VSIFSeekL(fp, nEndPos, SEEK_SET); + } + else + break; + } + CPLFree(pabyBuffer); + if (!bEOGFound) + return; + } + + const char* pszLine; + int bPRJFound = FALSE; + int bStatsFound = FALSE; + while((pszLine = ReadLine()) != NULL) + { + if (EQUALN(pszLine, "PRJ 2", 6)) + { + bPRJFound = TRUE; + while((pszLine = ReadLine()) != NULL) + { + if (EQUAL(pszLine, "EOP")) + { + break; + } + papszPrj = CSLAddString(papszPrj, pszLine); + } + + OGRSpatialReference oSRS; + if( oSRS.importFromESRI( papszPrj ) != OGRERR_NONE ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to parse PRJ section, ignoring." ); + } + else + { + char* pszWKT = NULL; + if (oSRS.exportToWkt(&pszWKT) == OGRERR_NONE && pszWKT != NULL) + osProjection = pszWKT; + CPLFree(pszWKT); + } + if (bStatsFound) + break; + } + else if (strcmp(pszLine, "STDV 8-1 254-1 15 3 60-1 -1 -1-1 4-") == 0) + { + bStatsFound = TRUE; + pszLine = ReadLine(); + if (pszLine) + { + CPLString osStats = pszLine; + pszLine = ReadLine(); + if (pszLine) + { + osStats += pszLine; + char** papszTokens = CSLTokenizeString(osStats); + if (CSLCount(papszTokens) == 4) + { + dfMin = CPLAtof(papszTokens[0]); + dfMax = CPLAtof(papszTokens[1]); + dfMean = CPLAtof(papszTokens[2]); + dfStddev = CPLAtof(papszTokens[3]); + bHasStats = TRUE; + } + CSLDestroy(papszTokens); + } + } + if (bPRJFound) + break; + } + } +} + +/************************************************************************/ +/* GDALRegister_E00GRID() */ +/************************************************************************/ + +void GDALRegister_E00GRID() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "E00GRID" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "E00GRID" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Arc/Info Export E00 GRID" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#E00GRID" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "e00" ); + + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = E00GRIDDataset::Open; + poDriver->pfnIdentify = E00GRIDDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/e00grid/e00read.c b/bazaar/plugin/gdal/frmts/e00grid/e00read.c new file mode 100644 index 000000000..308bf6f7e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/e00grid/e00read.c @@ -0,0 +1,643 @@ +/********************************************************************** + * $Id: e00read.c,v 1.10 2009-02-24 20:03:50 aboudreault Exp $ + * + * Name: e00read.c + * Project: Compressed E00 Read/Write library + * Language: ANSI C + * Purpose: Functions to read Compressed E00 files and return a stream + * of uncompressed lines. + * Author: Daniel Morissette, dmorissette@mapgears.com + * + * $Log: e00read.c,v $ + * Revision 1.10 2009-02-24 20:03:50 aboudreault + * Added a short manual pages (#1875) + * Updated documentation and code examples (#247) + * + * Revision 1.9 2005-09-17 14:22:05 daniel + * Switch to MIT license, update refs to website and email address, and + * prepare for 1.0.0 release. + * + * Revision 1.8 1999/02/25 18:45:56 daniel + * Now use CPL for Error handling, Memory allocation, and File access + * + * Revision 1.7 1999/01/08 17:39:08 daniel + * Added E00ReadCallbackOpen() + * + * Revision 1.6 1998/11/13 16:34:08 daniel + * Fixed '\r' problem when reading E00 files from a PC under Unix + * + * Revision 1.5 1998/11/13 15:48:08 daniel + * Simplified the decoding of the compression codes for numbers + * (use a logical rule instead of going case by case) + * + * Revision 1.4 1998/11/02 18:34:29 daniel + * Added E00ErrorReset() calls. Replace "EXP 1" by "EXP 0" on read. + * + * Revision 1.1 1998/10/29 13:26:00 daniel + * Initial revision + * + ********************************************************************** + * Copyright (c) 1998-2005, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + **********************************************************************/ + +#include +#include +#include +#include + +#include "e00compr.h" + +static void _ReadNextSourceLine(E00ReadPtr psInfo); +static const char *_UncompressNextLine(E00ReadPtr psInfo); + +/********************************************************************** + * _E00ReadTestOpen() + * + * Given a pre-initialized E00ReadPtr, this function will make sure + * that the file is really a E00 file, and also establish if it is + * compressed or not... setting the structure members by the same way. + * + * Returns NULL (and destroys the E00ReadPtr) if the file does not + * appear to be a valid E00 file. + **********************************************************************/ +static E00ReadPtr _E00ReadTestOpen(E00ReadPtr psInfo) +{ + + /* Check that the file is in E00 format. + */ + _ReadNextSourceLine(psInfo); + if (!psInfo->bEOF && strncmp(psInfo->szInBuf, "EXP ", 4) == 0) + { + /* We should be in presence of a valid E00 file... + * Is the file compressed or not? + * + * Note: we cannot really rely on the number that follows the EXP to + * establish if the file is compressed since we sometimes encounter + * uncompressed files that start with a "EXP 1" line!!! + * + * The best test is to read the first non-empty line: if the file is + * compressed, the first line of data should be 79 or 80 characters + * long and contain several '~' characters. + */ + do + { + _ReadNextSourceLine(psInfo); + }while(!psInfo->bEOF && + (psInfo->szInBuf[0] == '\0' || isspace(psInfo->szInBuf[0])) ); + + if (!psInfo->bEOF && + (strlen(psInfo->szInBuf)==79 || strlen(psInfo->szInBuf)==80) && + strchr(psInfo->szInBuf, '~') != NULL ) + psInfo->bIsCompressed = 1; + + /* Move the Read ptr ready to read at the beginning of the file + */ + E00ReadRewind(psInfo); + } + else + { + CPLFree(psInfo); + psInfo = NULL; + } + + return psInfo; +} + +/********************************************************************** + * E00ReadOpen() + * + * Try to open a E00 file given its filename and return a E00ReadPtr handle. + * + * Returns NULL if the file could not be opened or if it does not + * appear to be a valid E00 file. + **********************************************************************/ +E00ReadPtr E00ReadOpen(const char *pszFname) +{ + E00ReadPtr psInfo = NULL; + FILE *fp; + + CPLErrorReset(); + + /* Open the file + */ + fp = VSIFOpen(pszFname, "rt"); + if (fp == NULL) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Failed to open %s: %s", pszFname, strerror(errno)); + return NULL; + } + + /* File was succesfully opened, allocate and initialize a + * E00ReadPtr handle and check that the file is valid. + */ + psInfo = (E00ReadPtr)CPLCalloc(1, sizeof(struct _E00ReadInfo)); + + psInfo->fp = fp; + + psInfo = _E00ReadTestOpen(psInfo); + + if (psInfo == NULL) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "%s is not a valid E00 file.", pszFname); + } + + return psInfo; +} + +/********************************************************************** + * E00ReadCallbackOpen() + * + * This is an alternative to E00ReadOpen() for cases where you want to + * do all the file management yourself. You open/close the file yourself + * and provide 2 callback functions: to read from the file and rewind the + * file pointer. pRefData is your handle on the physical file and can + * be whatever you want... it is not used by the library, it will be + * passed directly to your 2 callback functions when they are called. + * + * The callback functions must have the following C prototype: + * + * const char *myReadNextLine(void *pRefData); + * void myReadRewind(void *pRefData); + * + * myReadNextLine() should return a reference to its own internal + * buffer, or NULL if an error happens or EOF is reached. + * + * E00ReadCallbackOpen() returns a E00ReadPtr handle or NULL if the file + * does not appear to be a valid E00 file. + **********************************************************************/ +E00ReadPtr E00ReadCallbackOpen(void *pRefData, + const char * (*pfnReadNextLine)(void *), + void (*pfnReadRewind)(void *)) +{ + E00ReadPtr psInfo = NULL; + + CPLErrorReset(); + + /* Make sure we received valid function pointers + */ + if (pfnReadNextLine == NULL || pfnReadRewind == NULL) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Invalid function pointers!"); + return NULL; + } + + /* Allocate and initialize a + * E00ReadPtr handle and check that the file is valid. + */ + psInfo = (E00ReadPtr)CPLCalloc(1, sizeof(struct _E00ReadInfo)); + + psInfo->pRefData = pRefData; + psInfo->pfnReadNextLine = pfnReadNextLine; + psInfo->pfnReadRewind = pfnReadRewind; + + psInfo = _E00ReadTestOpen(psInfo); + + if (psInfo == NULL) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "This is not a valid E00 file."); + } + + return psInfo; +} + +/********************************************************************** + * E00ReadClose() + * + * Close input file and release any memory used by the E00ReadPtr. + **********************************************************************/ +void E00ReadClose(E00ReadPtr psInfo) +{ + CPLErrorReset(); + + if (psInfo) + { + if (psInfo->fp) + VSIFClose(psInfo->fp); + CPLFree(psInfo); + } +} + +/********************************************************************** + * E00ReadRewind() + * + * Rewind the E00ReadPtr. Allows starting another read pass on the + * input file. + **********************************************************************/ +void E00ReadRewind(E00ReadPtr psInfo) +{ + CPLErrorReset(); + + psInfo->szInBuf[0] = psInfo->szOutBuf[0] = '\0'; + psInfo->iInBufPtr = 0; + + psInfo->nInputLineNo = 0; + + if (psInfo->pfnReadRewind == NULL) + VSIRewind(psInfo->fp); + else + psInfo->pfnReadRewind(psInfo->pRefData); + + psInfo->bEOF = 0; +} + +/********************************************************************** + * E00ReadNextLine() + * + * Return the next line of input from the E00 file or NULL if we reached EOF. + * + * Returns a reference to an internal buffer whose contents will be valid + * only until the next call to this function. + **********************************************************************/ +const char *E00ReadNextLine(E00ReadPtr psInfo) +{ + const char *pszLine = NULL; + char *pszPtr; + + CPLErrorReset(); + + if (psInfo && !psInfo->bEOF) + { + if (!psInfo->bIsCompressed) + { + /* Uncompressed file... return line directly. + */ + _ReadNextSourceLine(psInfo); + pszLine = psInfo->szInBuf; + } + else if (psInfo->bIsCompressed && psInfo->nInputLineNo == 0) + { + /* Header line in a compressed file... return line + * after replacing "EXP 1" with "EXP 0". E00ReadOpen() + * has already verified that this line starts with "EXP " + */ + _ReadNextSourceLine(psInfo); + if ( (pszPtr = strstr(psInfo->szInBuf, " 1")) != NULL) + pszPtr[1] = '0'; + pszLine = psInfo->szInBuf; + } + else + { + if (psInfo->nInputLineNo == 1) + { + /* We just read the header line... reload the input buffer + */ + _ReadNextSourceLine(psInfo); + } + + /* Uncompress the next line of input and return it + */ + pszLine = _UncompressNextLine(psInfo); + } + + /* If we just reached EOF then make sure we don't add an extra + * empty line at the end of the uncompressed oputput. + */ + if (psInfo->bEOF && strlen(pszLine) == 0) + pszLine = NULL; + } + + return pszLine; +} + +/********************************************************************** + * _ReadNextSourceLine() + * + * Loads the next line from the source file in psInfo. + * + * psInfo->bEOF should be checked after this call. + **********************************************************************/ +static void _ReadNextSourceLine(E00ReadPtr psInfo) +{ + if (!psInfo->bEOF) + { + psInfo->iInBufPtr = 0; + psInfo->szInBuf[0] = '\0'; + + /* Read either using fgets() or psInfo->pfnReadNextLine() + * depending on the way the file was opened... + */ + if (psInfo->pfnReadNextLine == NULL) + { + if (VSIFGets(psInfo->szInBuf,E00_READ_BUF_SIZE,psInfo->fp) == NULL) + { + /* We reached EOF + */ + psInfo->bEOF = 1; + } + } + else + { + const char *pszLine; + pszLine = psInfo->pfnReadNextLine(psInfo->pRefData); + if (pszLine) + { + strncpy(psInfo->szInBuf, pszLine, E00_READ_BUF_SIZE); + } + else + { + /* We reached EOF + */ + psInfo->bEOF = 1; + } + } + + if (!psInfo->bEOF) + { + /* A new line was succesfully read. Remove trailing '\n' if any. + * (Note: For Unix systems, we also have to check for '\r') + */ + int nLen; + nLen = strlen(psInfo->szInBuf); + while(nLen > 0 && (psInfo->szInBuf[nLen-1] == '\n' || + psInfo->szInBuf[nLen-1] == '\r' ) ) + { + nLen--; + psInfo->szInBuf[nLen] = '\0'; + } + + psInfo->nInputLineNo++; + } + } +} + + +/********************************************************************** + * _GetNextSourceChar() + * + * Returns the next char from the source file input buffer... and + * reload the input buffer when necessary... this function makes the + * whole input file appear as one huge null-terminated string with + * no line delimiters. + * + * Will return '\0' when EOF is reached. + **********************************************************************/ +static char _GetNextSourceChar(E00ReadPtr psInfo) +{ + char c = '\0'; + + if (!psInfo->bEOF) + { + if (psInfo->szInBuf[psInfo->iInBufPtr] == '\0') + { + _ReadNextSourceLine(psInfo); + c = _GetNextSourceChar(psInfo); + } + else + { + c = psInfo->szInBuf[psInfo->iInBufPtr++]; + } + } + + return c; +} + +/********************************************************************** + * _UngetSourceChar() + * + * Reverse the effect of the previous call to _GetNextSourceChar() by + * moving the input buffer pointer back 1 character. + * + * This function can be called only once per call to _GetNextSourceChar() + * (i.e. you cannot unget more than one character) otherwise the pointer + * could move before the beginning of the input buffer. + **********************************************************************/ +static void _UngetSourceChar(E00ReadPtr psInfo) +{ + if (psInfo->iInBufPtr > 0) + psInfo->iInBufPtr--; + else + { + /* This error can happen only if _UngetSourceChar() is called + * twice in a row (which should never happen!). + */ + CPLError(CE_Failure, CPLE_AssertionFailed, + "UNEXPECTED INTERNAL ERROR: _UngetSourceChar() " + "failed while reading line %d.", psInfo->nInputLineNo); + } +} + +/********************************************************************** + * _UncompressNextLine() + * + * Uncompress one line of input and return a reference to an internal + * buffer containing the uncompressed output. + **********************************************************************/ +static const char *_UncompressNextLine(E00ReadPtr psInfo) +{ + char c; + int bEOL = 0; /* Set to 1 when End of Line reached */ + int iOutBufPtr = 0, i, n; + int iDecimalPoint, bOddNumDigits, iCurDigit; + char const *pszExp; + int bPreviousCodeWasNumeric = 0; + + while(!bEOL && (c=_GetNextSourceChar(psInfo)) != '\0') + { + if (c != '~') + { + /* Normal character... just copy it */ + psInfo->szOutBuf[iOutBufPtr++] = c; + bPreviousCodeWasNumeric = 0; + } + else /* c == '~' */ + { + /* ======================================================== + * Found an encoded sequence. + * =======================================================*/ + c = _GetNextSourceChar(psInfo); + + /* -------------------------------------------------------- + * Compression level 1: only spaces, '~' and '\n' are encoded + * -------------------------------------------------------*/ + if (c == ' ') + { + /* "~ " followed by number of spaces + */ + c = _GetNextSourceChar(psInfo); + n = c - ' '; + for(i=0; iszOutBuf[iOutBufPtr++] = ' '; + bPreviousCodeWasNumeric = 0; + } + else if (c == '}') + { + /* "~}" == '\n' + */ + bEOL = 1; + bPreviousCodeWasNumeric = 0; + } + else if (bPreviousCodeWasNumeric) + { + /* If the previous code was numeric, then the only valid code + * sequences are the ones above: "~ " and "~}". If we end up + * here, it is because the number was followed by a '~' but + * this '~' was not a code, it only marked the end of a + * number that was not followed by any space. + * + * We should simply ignore the '~' and return the character + * that follows it directly. + */ + psInfo->szOutBuf[iOutBufPtr++] = c; + bPreviousCodeWasNumeric = 0; + } + else if (c == '~' || c == '-') + { + /* "~~" and "~-" are simple escape sequences for '~' and '-' + */ + psInfo->szOutBuf[iOutBufPtr++] = c; + } + /* -------------------------------------------------------- + * Compression level 2: numeric values are encoded. + * + * All codes for this level are in the form "~ c0 c1 c2 ... cn" + * where: + * + * ~ marks the beginning of a new code sequence + * + * c0 is a single character code defining the format + * of the number (decimal position, exponent, + * and even or odd number of digits) + * + * c1 c2 ... cn each of these characters represent a pair of + * digits of the encoded value with '!' == 00 + * values 92..99 are encoded on 2 chars that + * must be added to each other + * (i.e. 92 == }!, 93 == }", ...) + * + * The sequence ends with a ' ' or a '~' character + * -------------------------------------------------------*/ + else if (c >= '!' && c <= 'z') + { + /* The format code defines 3 characteristics of the final number: + * - Presence of a decimal point and its position + * - Presence of an exponent, and its sign + * - Odd or even number of digits + */ + n = c - '!'; + iDecimalPoint = n % 15; /* 0 = no decimal point */ + bOddNumDigits = n / 45; /* 0 = even num.digits, 1 = odd */ + n = n / 15; + if ( n % 3 == 1 ) + pszExp = "E+"; + else if (n % 3 == 2 ) + pszExp = "E-"; + else + pszExp = NULL; + + /* Decode the c1 c2 ... cn value and apply the format. + * Read characters until we encounter a ' ' or a '~' + */ + iCurDigit = 0; + while((c=_GetNextSourceChar(psInfo)) != '\0' && + c != ' ' && c != '~') + { + n = c - '!'; + if (n == 92 && (c=_GetNextSourceChar(psInfo)) != '\0') + n += c - '!'; + + psInfo->szOutBuf[iOutBufPtr++] = '0' + n/10; + + if (++iCurDigit == iDecimalPoint) + psInfo->szOutBuf[iOutBufPtr++] = '.'; + + psInfo->szOutBuf[iOutBufPtr++] = '0' + n%10; + + if (++iCurDigit == iDecimalPoint) + psInfo->szOutBuf[iOutBufPtr++] = '.'; + } + + if (c == '~' || c == ' ') + { + bPreviousCodeWasNumeric = 1; + _UngetSourceChar(psInfo); + } + + /* If odd number of digits, then flush the last one + */ + if (bOddNumDigits) + iOutBufPtr--; + + /* Insert the exponent string before the 2 last digits + * (we assume the exponent string is 2 chars. long) + */ + if (pszExp) + { + for(i=0; i<2;i++) + { + psInfo->szOutBuf[iOutBufPtr] = + psInfo->szOutBuf[iOutBufPtr-2]; + psInfo->szOutBuf[iOutBufPtr-2] = pszExp[i]; + iOutBufPtr++; + } + } + } + else + { + /* Unsupported code sequence... this is a possibility + * given the fact that this library was written by + * reverse-engineering the format! + * + * Send an error to the user and abort. + * + * If this error ever happens, and you are convinced that + * the input file is not corrupted, then please report it to + * me at dmorissette@mapgears.com, quoting the section of the input + * file that produced it, and I'll do my best to add support + * for this code sequence. + */ + CPLError(CE_Failure, CPLE_NotSupported, + "Unexpected code \"~%c\" encountered in line %d.", + c, psInfo->nInputLineNo); + + /* Force the program to abort by simulating a EOF + */ + psInfo->bEOF = 1; + bEOL = 1; + } + + }/* if c == '~' */ + + /* E00 lines should NEVER be longer than 80 chars. if we passed + * that limit, then the input file is likely corrupt. + */ + if (iOutBufPtr > 80) + { + CPLError(CE_Failure, CPLE_FileIO, + "Uncompressed line longer than 80 chars. " + "Input file possibly corrupt around line %d.", + psInfo->nInputLineNo); + /* Force the program to abort by simulating a EOF + */ + psInfo->bEOF = 1; + bEOL = 1; + } + + }/* while !EOL */ + + psInfo->szOutBuf[iOutBufPtr++] = '\0'; + + return psInfo->szOutBuf; +} diff --git a/bazaar/plugin/gdal/frmts/e00grid/makefile.vc b/bazaar/plugin/gdal/frmts/e00grid/makefile.vc new file mode 100644 index 000000000..a65cdc8ca --- /dev/null +++ b/bazaar/plugin/gdal/frmts/e00grid/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = e00griddataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/ecw/GNUmakefile b/bazaar/plugin/gdal/frmts/ecw/GNUmakefile new file mode 100644 index 000000000..0a723d718 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ecw/GNUmakefile @@ -0,0 +1,23 @@ + +include ../../GDALmake.opt + +OBJ = ecwdataset.o ecwcreatecopy.o jp2userbox.o ecwasyncreader.o + +CPPFLAGS := -DFRMT_ecw $(CPPFLAGS) \ + $(ECW_FLAGS) $(ECW_INCLUDE) $(EXTRA_CFLAGS) +PLUGIN_SO = gdal_ECW_JP2ECW.so + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) *.so + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +$(OBJ) $(O_OBJ): gdal_ecw.h + +plugin: $(PLUGIN_SO) + +$(PLUGIN_SO): $(OBJ) + $(LD_SHARED) $(LNK_FLAGS) $(OBJ) $(CONFIG_LIBS) $(EXTRA_LIBS) \ + -o $(PLUGIN_SO) diff --git a/bazaar/plugin/gdal/frmts/ecw/ecwasyncreader.cpp b/bazaar/plugin/gdal/frmts/ecw/ecwasyncreader.cpp new file mode 100644 index 000000000..3bb589e45 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ecw/ecwasyncreader.cpp @@ -0,0 +1,444 @@ +/****************************************************************************** + * $Id: ecwdataset.cpp 21486 2011-01-13 17:38:17Z warmerdam $ + * + * Project: GDAL + * Purpose: ECWAsyncReader implementation + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_ecw.h" + +CPL_CVSID("$Id: ecwdataset.cpp 21486 2011-01-13 17:38:17Z warmerdam $"); + +#if defined(FRMT_ecw) && (ECWSDK_VERSION >= 40) + +/************************************************************************/ +/* BeginAsyncReader() */ +/************************************************************************/ + +GDALAsyncReader* +ECWDataset::BeginAsyncReader( int nXOff, int nYOff, int nXSize, int nYSize, + void *pBuf, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, int nBandSpace, + char **papszOptions) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Provide default packing if needed. */ +/* -------------------------------------------------------------------- */ + if( nPixelSpace == 0 ) + nPixelSpace = GDALGetDataTypeSize(eBufType) / 8; + if( nLineSpace == 0 ) + nLineSpace = nPixelSpace * nBufXSize; + if( nBandSpace == 0 ) + nBandSpace = nLineSpace * nBufYSize; + +/* -------------------------------------------------------------------- */ +/* Do a bit of validation. */ +/* -------------------------------------------------------------------- */ + if( nXSize < 1 || nYSize < 1 || nBufXSize < 1 || nBufYSize < 1 ) + { + CPLDebug( "GDAL", + "BeginAsyncReader() skipped for odd window or buffer size.\n" + " Window = (%d,%d)x%dx%d\n" + " Buffer = %dx%d\n", + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize ); + return NULL; + } + + if( nXOff < 0 || nXOff > INT_MAX - nXSize || nXOff + nXSize > nRasterXSize + || nYOff < 0 || nYOff > INT_MAX - nYSize || nYOff + nYSize > nRasterYSize ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Access window out of range in RasterIO(). Requested\n" + "(%d,%d) of size %dx%d on raster of %dx%d.", + nXOff, nYOff, nXSize, nYSize, nRasterXSize, nRasterYSize ); + return NULL; + } + + if( nBandCount <= 0 || nBandCount > nBands ) + { + ReportError( CE_Failure, CPLE_IllegalArg, "Invalid band count" ); + return NULL; + } + + if( panBandMap != NULL ) + { + for( i = 0; i < nBandCount; i++ ) + { + if( panBandMap[i] < 1 || panBandMap[i] > nBands ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "panBandMap[%d] = %d, this band does not exist on dataset.", + i, panBandMap[i] ); + return NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create the corresponding async reader. */ +/* -------------------------------------------------------------------- */ + ECWAsyncReader *poReader = new ECWAsyncReader(); + + poReader->poDS = this; + + poReader->nXOff = nXOff; + poReader->nYOff = nYOff; + poReader->nXSize = nXSize; + poReader->nYSize = nYSize; + + poReader->pBuf = pBuf; + poReader->nBufXSize = nBufXSize; + poReader->nBufYSize = nBufYSize; + poReader->eBufType = eBufType; + poReader->nBandCount = nBandCount; + poReader->panBandMap = (int *) CPLCalloc(sizeof(int),nBandCount); + if( panBandMap != NULL ) + { + memcpy( poReader->panBandMap, panBandMap, sizeof(int) * nBandCount ); + } + else + { + for( i = 0; i < nBandCount; i++ ) + poReader->panBandMap[i] = i + 1; + } + + poReader->nPixelSpace = nPixelSpace; + poReader->nLineSpace = nLineSpace; + poReader->nBandSpace = nBandSpace; + +/* -------------------------------------------------------------------- */ +/* Create a new view for this request. */ +/* -------------------------------------------------------------------- */ + poReader->poFileView = OpenFileView( GetDescription(), true, + poReader->bUsingCustomStream ); + + if( poReader->poFileView == NULL ) + { + delete poReader; + return NULL; + } + + poReader->poFileView->SetClientData( poReader ); + poReader->poFileView->SetRefreshCallback( ECWAsyncReader::RefreshCB ); + +/* -------------------------------------------------------------------- */ +/* Issue a corresponding SetView command. */ +/* -------------------------------------------------------------------- */ + std::vector anBandIndices; + NCSError eNCSErr; + CNCSError oErr; + + for( i = 0; i < nBandCount; i++ ) + anBandIndices.push_back( panBandMap[i] - 1 ); + + oErr = poReader->poFileView->SetView( nBandCount, &(anBandIndices[0]), + nXOff, nYOff, + nXOff + nXSize - 1, + nYOff + nYSize - 1, + nBufXSize, nBufYSize ); + eNCSErr = oErr.GetErrorNumber(); + + if( eNCSErr != NCS_SUCCESS ) + { + delete poReader; + CPLError( CE_Failure, CPLE_AppDefined, + "%s", NCSGetErrorText(eNCSErr) ); + + return NULL; + } + + return poReader; +} + +/************************************************************************/ +/* EndAsyncReader() */ +/************************************************************************/ +void ECWDataset::EndAsyncReader(GDALAsyncReader *poReader) + +{ + delete poReader; +} + +/************************************************************************/ +/* ECWAsyncReader() */ +/************************************************************************/ + +ECWAsyncReader::ECWAsyncReader() + +{ + hMutex = CPLCreateMutex(); + CPLReleaseMutex( hMutex ); + + poFileView = NULL; + bUpdateReady = FALSE; + bComplete = FALSE; + panBandMap = NULL; +} + +/************************************************************************/ +/* ~ECWAsyncReader() */ +/************************************************************************/ + +ECWAsyncReader::~ECWAsyncReader() + +{ + { + CPLMutexHolderD( &hMutex ); + + // cancel? + + delete poFileView; + // we should also consider cleaning up the io stream if needed. + } + + CPLFree(panBandMap); + panBandMap = NULL; + + CPLDestroyMutex( hMutex ); + hMutex = NULL; +} + +/************************************************************************/ +/* RefreshCB() */ +/* */ +/* This static method is called by the ECW SDK to notify us */ +/* that there is new data ready to refresh from. We just mark */ +/* the async reader as ready for an update. We fetch the data */ +/* and push into into the buffer the application uses. We lock */ +/* this async reader's mutex for this whole operation to avoid */ +/* a conflict with the main application. */ +/************************************************************************/ + +NCSEcwReadStatus ECWAsyncReader::RefreshCB( NCSFileView *pFileView ) + +{ + NCSFileViewSetInfo *psVSI = NULL; + + NCScbmGetViewInfo( pFileView, &psVSI ); + if( psVSI != NULL ) + { + CPLDebug( "ECW", "RefreshCB(): BlockCounts=%d/%d/%d/%d", + psVSI->nBlocksAvailableAtSetView, + psVSI->nBlocksAvailable, + psVSI->nMissedBlocksDuringRead, + psVSI->nBlocksInView ); + } + +/* -------------------------------------------------------------------- */ +/* Identify the reader we are responding on behalf of. */ +/* -------------------------------------------------------------------- */ + CNCSJP2FileView *poFileView = (CNCSJP2FileView *) pFileView; + ECWAsyncReader *poReader = (ECWAsyncReader *)poFileView->GetClientData(); + +/* -------------------------------------------------------------------- */ +/* Acquire the async reader mutex. Currently we make no */ +/* arrangements for failure to acquire it. */ +/* -------------------------------------------------------------------- */ + { + CPLMutexHolderD( &(poReader->hMutex) ); + +/* -------------------------------------------------------------------- */ +/* Mark the buffer as updated unless we are already complete. */ +/* It seems the Update callback keeps getting called even when */ +/* no new data has arrived after completion so we don't want to */ +/* trigger new work elsewhere in that case. */ +/* */ +/* Also record whether we are now complete. */ +/* -------------------------------------------------------------------- */ + if( !poReader->bComplete ) + poReader->bUpdateReady = TRUE; + + if( psVSI->nBlocksAvailable == psVSI->nBlocksInView ) + poReader->bComplete = TRUE; + } + + /* Call CPLCleanupTLS explicitly since this thread isn't managed */ + /* by CPL. This will free the resources taken by the above CPLDebug */ + if( poReader->bComplete ) + CPLCleanupTLS(); + + return NCSECW_READ_OK; +} + +/************************************************************************/ +/* ReadToBuffer() */ +/************************************************************************/ +NCSEcwReadStatus ECWAsyncReader::ReadToBuffer() +{ +/* -------------------------------------------------------------------- */ +/* Setup working scanline, and the pointers into it. */ +/* */ +/* Should we try and optimize some cases that we could read */ +/* directly into the application buffer? Perhaps in the */ +/* future. */ +/* -------------------------------------------------------------------- */ + ECWDataset *poECWDS = (ECWDataset *) poDS; + int i; + int nDataTypeSize = (GDALGetDataTypeSize(poECWDS->eRasterDataType) / 8); + GByte *pabyBILScanline = (GByte *) + CPLMalloc(nBufXSize * nDataTypeSize * nBandCount); + GByte **papabyBIL = (GByte**)CPLMalloc(nBandCount*sizeof(void*)); + + for( i = 0; i < nBandCount; i++ ) + papabyBIL[i] = pabyBILScanline + + i * nBufXSize * nDataTypeSize; + +/* -------------------------------------------------------------------- */ +/* Read back the imagery into the buffer. */ +/* -------------------------------------------------------------------- */ + for( int iScanline = 0; iScanline < nBufYSize; iScanline++ ) + { + NCSEcwReadStatus eRStatus; + + eRStatus = + poFileView->ReadLineBIL( poECWDS->eNCSRequestDataType, + (UINT16) nBandCount, + (void **) papabyBIL ); + if( eRStatus != NCSECW_READ_OK ) + { + CPLFree( papabyBIL ); + CPLFree( pabyBILScanline ); + CPLError( CE_Failure, CPLE_AppDefined, + "NCScbmReadViewLineBIL failed." ); + return eRStatus; + } + + for( i = 0; i < nBandCount; i++ ) + { + GDALCopyWords( + pabyBILScanline + i * nDataTypeSize * nBufXSize, + poECWDS->eRasterDataType, nDataTypeSize, + ((GByte *) pBuf) + + nLineSpace * iScanline + + nBandSpace * i, + eBufType, + nPixelSpace, + nBufXSize ); + } + } + + CPLFree( pabyBILScanline ); + CPLFree( papabyBIL ); + + return NCSECW_READ_OK; +} + +/************************************************************************/ +/* GetNextUpdatedRegion() */ +/************************************************************************/ + +GDALAsyncStatusType +ECWAsyncReader::GetNextUpdatedRegion( double dfTimeout, + int* pnXBufOff, int* pnYBufOff, + int* pnXBufSize, int* pnYBufSize ) + +{ + CPLDebug( "ECW", "GetNextUpdatedRegion()" ); + +/* -------------------------------------------------------------------- */ +/* We always mark the whole raster as updated since the ECW SDK */ +/* does not have a concept of partial update notifications. */ +/* -------------------------------------------------------------------- */ + *pnXBufOff = 0; + *pnYBufOff = 0; + *pnXBufSize = nBufXSize; + *pnYBufSize = nBufYSize; + + if( bComplete && !bUpdateReady ) + { + CPLDebug( "ECW", "return GARIO_COMPLETE" ); + return GARIO_COMPLETE; + } + +/* -------------------------------------------------------------------- */ +/* Wait till our timeout, or until we are notified there is */ +/* data ready. We are trusting the CPLSleep() to be pretty */ +/* accurate instead of keeping track of time elapsed ourselves */ +/* - this is not necessarily a good approach. */ +/* -------------------------------------------------------------------- */ + if( dfTimeout < 0.0 ) + dfTimeout = 100000.0; + + while( !bUpdateReady && dfTimeout > 0.0 ) + { + CPLSleep( MIN(0.1, dfTimeout) ); + dfTimeout -= 0.1; + CPLDebug( "ECW", "wait..." ); + } + + if( !bUpdateReady ) + { + CPLDebug( "ECW", "return GARIO_PENDING" ); + return GARIO_PENDING; + } + + bUpdateReady = FALSE; + +/* -------------------------------------------------------------------- */ +/* Acquire Mutex */ +/* -------------------------------------------------------------------- */ + if( !CPLAcquireMutex( hMutex, dfTimeout ) ) + { + CPLDebug( "ECW", "return GARIO_PENDING" ); + return GARIO_PENDING; + } + +/* -------------------------------------------------------------------- */ +/* Actually decode the imagery into our buffer. */ +/* -------------------------------------------------------------------- */ + NCSEcwReadStatus eRStatus = ReadToBuffer(); + + if( eRStatus != NCSECW_READ_OK ) + { + CPLReleaseMutex( hMutex ); + return GARIO_ERROR; + } + +/* -------------------------------------------------------------------- */ +/* Return indication of complete or just buffer updateded. */ +/* -------------------------------------------------------------------- */ + + if( bComplete && !bUpdateReady ) + { + CPLReleaseMutex( hMutex ); + CPLDebug( "ECW", "return GARIO_COMPLETE" ); + return GARIO_COMPLETE; + } + else + { + CPLReleaseMutex( hMutex ); + CPLDebug( "ECW", "return GARIO_UPDATE" ); + return GARIO_UPDATE; + } +} + +#endif /* def FRMT_ecw */ diff --git a/bazaar/plugin/gdal/frmts/ecw/ecwcreatecopy.cpp b/bazaar/plugin/gdal/frmts/ecw/ecwcreatecopy.cpp new file mode 100644 index 000000000..0c62adfe6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ecw/ecwcreatecopy.cpp @@ -0,0 +1,2113 @@ +/****************************************************************************** + * $Id: ecwcreatecopy.cpp 29054 2015-04-29 19:31:41Z rouault $ + * + * Project: GDAL ECW Driver + * Purpose: ECW CreateCopy method implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, 2004, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_ecw.h" +#include "gdaljp2metadata.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: ecwcreatecopy.cpp 29054 2015-04-29 19:31:41Z rouault $"); + +#if defined(FRMT_ecw) && defined(HAVE_COMPRESS) + +#define OPTIMIZED_FOR_GDALWARP + +#if ECWSDK_VERSION>=50 +static +CPLString GetCompressionSoftwareName(){ + CPLString osRet; + char szProcessName[2048]; + + /* For privacy reason, allow the user to not write the software name in the ECW */ + if( !CSLTestBoolean(CPLGetConfigOption("GDAL_ECW_WRITE_COMPRESSION_SOFTWARE", "YES")) ) + return osRet; + + if( CPLGetExecPath( szProcessName, sizeof(szProcessName) - 1 ) ) + { + szProcessName[sizeof(szProcessName) - 1] = 0; +#ifdef WIN32 + char* szLastSlash = strrchr(szProcessName, '\\'); +#else + char* szLastSlash = strrchr(szProcessName, '/'); +#endif + if( szLastSlash != NULL ) + memmove(szProcessName, szLastSlash + 1, strlen(szLastSlash + 1) + 1); + } + else + strcpy(szProcessName, "Unknown"); + + osRet.Printf("%s/GDAL v%d.%d.%d.%d/ECWJP2 SDK v%s", + szProcessName, + GDAL_VERSION_MAJOR, + GDAL_VERSION_MINOR, + GDAL_VERSION_REV, + GDAL_VERSION_BUILD, + NCS_ECWJP2_FULL_VERSION_STRING_DOT_DEL); + return osRet; +}; +#endif + +class GDALECWCompressor : public CNCSFile { + +public: + GDALECWCompressor(); + virtual ~GDALECWCompressor(); + virtual CNCSError WriteReadLine(UINT32 nNextLine, void **ppInputArray); +#if ECWSDK_VERSION>=50 + virtual void WriteStatus(IEEE4 fPercentComplete, const NCS::CString &sStatusText, const CompressionCounters &Counters); +#else + virtual void WriteStatus(UINT32 nCurrentLine); +#endif + + virtual bool WriteCancel(); + + CPLErr Initialize( const char *pszFilename, char **papszOptions, + int nXSize, int nYSize, int nBands, const char * const * papszBandDescriptions, int bRGBColorSpace, + GDALDataType eType, + const char *pszWKT, double *padfGeoTransform, + int nGCPCount, const GDAL_GCP *pasGCPList, + int bIsJPEG2000, int bPixelIsPoint, char** papszRPCMD, + GDALDataset* poSrcDS = NULL ); + CPLErr CloseDown(); + + CPLErr PrepareCoverageBox( const char *pszWKT, double *padfGeoTransform ); + CPLErr WriteJP2Box( GDALJP2Box * ); + void WriteXMLBoxes(); + CPLErr WriteLineBIL(UINT16 nBands, void **ppOutputLine, UINT32 *pLineSteps = NULL); + virtual NCSEcwCellType WriteReadLineGetCellType() { + return sFileInfo.eCellType; + } +#ifdef ECW_FW + CNCSJP2File::CNCSJPXAssocBox m_oGMLAssoc; +#endif + + // Data + + GDALDataset *m_poSrcDS; + + VSIIOStream m_OStream; + int m_nPercentComplete; + + int m_bCancelled; + + GDALProgressFunc pfnProgress; + void *pProgressData; + + + GDALDataType eWorkDT; + + JP2UserBox** papoJP2UserBox; + int nJP2UserBox; + +private : + NCSFileViewFileInfoEx sFileInfo; +}; + +/************************************************************************/ +/* GDALECWCompressor() */ +/************************************************************************/ + +GDALECWCompressor::GDALECWCompressor() + +{ + m_poSrcDS = NULL; + m_nPercentComplete = -1; + m_bCancelled = FALSE; + pfnProgress = GDALDummyProgress; + pProgressData = NULL; + papoJP2UserBox = NULL; + nJP2UserBox = 0; +#if ECWSDK_VERSION>=50 + NCSInitFileInfo(&sFileInfo); +#else + NCSInitFileInfoEx(&sFileInfo); +#endif +} + +/************************************************************************/ +/* ~GDALECWCompressor() */ +/************************************************************************/ + +GDALECWCompressor::~GDALECWCompressor() + +{ + int i; + for(i=0;i=50 + NCSFreeFileInfo(&sFileInfo); +#else + for( int i = 0; i < sFileInfo.nBands; i++ ) + { + CPLFree( sFileInfo.pBands[i].szDesc ); + } + CPLFree( sFileInfo.pBands ); +#endif + + Close( true ); + m_OStream.Close(); + + return CE_None; +} + +/************************************************************************/ +/* WriteReadLine() */ +/************************************************************************/ + +CNCSError GDALECWCompressor::WriteReadLine( UINT32 nNextLine, + void **ppInputArray ) + +{ + int iBand, *panBandMap; + CPLErr eErr; + GByte *pabyLineBuf; + int nWordSize = GDALGetDataTypeSize( eWorkDT ) / 8; + +#ifdef DEBUG_VERBOSE + CPLDebug("ECW", "nNextLine = %d", nNextLine); +#endif + + panBandMap = (int *) CPLMalloc(sizeof(int) * sFileInfo.nBands); + for( iBand = 0; iBand < sFileInfo.nBands; iBand++ ) + panBandMap[iBand] = iBand+1; + + pabyLineBuf = (GByte *) CPLMalloc( sFileInfo.nSizeX * sFileInfo.nBands + * nWordSize ); + + eErr = m_poSrcDS->RasterIO( GF_Read, 0, nNextLine, sFileInfo.nSizeX, 1, + pabyLineBuf, sFileInfo.nSizeX, 1, + eWorkDT, + sFileInfo.nBands, panBandMap, + nWordSize, 0, nWordSize * sFileInfo.nSizeX, NULL ); + + for( iBand = 0; iBand < (int) sFileInfo.nBands; iBand++ ) + { + memcpy( ppInputArray[iBand], + pabyLineBuf + nWordSize * sFileInfo.nSizeX * iBand, + nWordSize * sFileInfo.nSizeX ); + } + + CPLFree( pabyLineBuf ); + CPLFree( panBandMap ); + + if( eErr == CE_None ) + return NCS_SUCCESS; + else + return NCS_FILEIO_ERROR; +} + +/************************************************************************/ +/* WriteStatus() */ +/************************************************************************/ +#if ECWSDK_VERSION>=50 +void GDALECWCompressor::WriteStatus(IEEE4 fPercentComplete, const NCS::CString &sStatusText, const CompressionCounters &Counters) +{ + std::string sStatusUTF8; + sStatusText.utf8_str(sStatusUTF8); + + m_bCancelled = !pfnProgress( + fPercentComplete/100.0, + sStatusUTF8.c_str(), + pProgressData ); +} +#else + +void GDALECWCompressor::WriteStatus( UINT32 nCurrentLine ) + +{ + m_bCancelled = + !pfnProgress( nCurrentLine / (float) sFileInfo.nSizeY, + NULL, pProgressData ); +} +#endif +/************************************************************************/ +/* WriteCancel() */ +/************************************************************************/ + +bool GDALECWCompressor::WriteCancel() + +{ + return (bool) m_bCancelled; +} + +/************************************************************************/ +/* PrepareCoverageBox() */ +/************************************************************************/ + +CPLErr GDALECWCompressor::PrepareCoverageBox( +#ifndef ECW_FW + CPL_UNUSED +#endif + const char *pszWKT, +#ifndef ECW_FW + CPL_UNUSED +#endif + double *padfGeoTransform ) + +{ +#ifndef ECW_FW + return CE_Failure; +#else +/* -------------------------------------------------------------------- */ +/* Try do determine a PCS or GCS code we can use. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + char *pszWKTCopy = (char *) pszWKT; + int nEPSGCode = 0; + char szSRSName[100]; + + if( oSRS.importFromWkt( &pszWKTCopy ) != OGRERR_NONE ) + return CE_Failure; + + if( oSRS.IsProjected() ) + { + const char *pszAuthName = oSRS.GetAuthorityName( "PROJCS" ); + + if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") ) + { + nEPSGCode = atoi(oSRS.GetAuthorityCode( "PROJCS" )); + } + } + else if( oSRS.IsGeographic() ) + { + const char *pszAuthName = oSRS.GetAuthorityName( "GEOGCS" ); + + if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") ) + { + nEPSGCode = atoi(oSRS.GetAuthorityCode( "GEOGCS" )); + } + } + + if( nEPSGCode != 0 ) + sprintf( szSRSName, "urn:ogc:def:crs:EPSG::%d", nEPSGCode ); + else + strcpy( szSRSName, + "gmljp2://xml/CRSDictionary.gml#ogrcrs1" ); + +/* -------------------------------------------------------------------- */ +/* For now we hardcode for a minimal instance format. */ +/* -------------------------------------------------------------------- */ + char szDoc[4000]; + + CPLsprintf( szDoc, +"\n" +" \n" +" withheld\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" 0 0\n" +" %d %d\n" +" \n" +" \n" +" x\n" +" y\n" +" \n" +" \n" +" %.15g %.15g\n" +" \n" +" \n" +" %.15g %.15g\n" +" %.15g %.15g\n" +" \n" +" \n" +" \n" +" \n" +" urn:ogc:tc:gmljp2:codestream:0\n" +" Record Interleaved\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n", + sFileInfo.nSizeX-1, sFileInfo.nSizeY-1, + szSRSName, + padfGeoTransform[0] + padfGeoTransform[1] * 0.5 + + padfGeoTransform[4] * 0.5, + padfGeoTransform[3] + padfGeoTransform[2] * 0.5 + + padfGeoTransform[5] * 0.5, + szSRSName, + padfGeoTransform[1], padfGeoTransform[2], + szSRSName, + padfGeoTransform[4], padfGeoTransform[5] ); + +/* -------------------------------------------------------------------- */ +/* If we need a user defined CRSDictionary entry, prepare it */ +/* here. */ +/* -------------------------------------------------------------------- */ + char *pszDictBox = NULL; + + if( nEPSGCode == 0 ) + { + char *pszGMLDef = NULL; + + if( oSRS.exportToXML( &pszGMLDef, NULL ) == OGRERR_NONE ) + { + pszDictBox = (char *) CPLMalloc(strlen(pszGMLDef) + 4000); + + sprintf( pszDictBox, +"\n" +" \n" +"%s\n" +" \n" +"\n", + pszGMLDef ); + } + CPLFree( pszGMLDef ); + } + +/* -------------------------------------------------------------------- */ +/* Setup the various required boxes. */ +/* -------------------------------------------------------------------- */ + JP2UserBox *poGMLData; + CNCSJP2File::CNCSJPXAssocBox *poAssoc; + CNCSJP2File::CNCSJPXLabelBox *poLabel; + + poLabel = new CNCSJP2File::CNCSJPXLabelBox(); + poLabel->SetLabel( "gml.data" ); + poLabel->m_bValid = true; + m_oGMLAssoc.m_OtherBoxes.push_back( poLabel ); + m_oGMLAssoc.m_OwnedBoxes.push_back( poLabel ); + + poAssoc = new CNCSJP2File::CNCSJPXAssocBox(); + m_oGMLAssoc.m_OtherBoxes.push_back( poAssoc ); + m_oGMLAssoc.m_OwnedBoxes.push_back( poAssoc ); + poAssoc->m_bValid = true; + + poLabel = new CNCSJP2File::CNCSJPXLabelBox(); + poLabel->SetLabel( "gml.root-instance" ); + poLabel->m_bValid = true; + poAssoc->m_OtherBoxes.push_back( poLabel ); + poAssoc->m_OwnedBoxes.push_back( poLabel ); + + poGMLData = new JP2UserBox(); + poGMLData->m_nTBox = 'xml '; /* Is it correct on a big-endian host ? Does ECW work on big-endian hosts ;-) */ + poGMLData->SetData( strlen( szDoc ), (unsigned char *) szDoc ); + poAssoc->m_OtherBoxes.push_back( poGMLData ); + poAssoc->m_OwnedBoxes.push_back( poGMLData ); + + if( pszDictBox != NULL ) + { + poAssoc = new CNCSJP2File::CNCSJPXAssocBox(); + m_oGMLAssoc.m_OtherBoxes.push_back( poAssoc ); + m_oGMLAssoc.m_OwnedBoxes.push_back( poAssoc ); + poAssoc->m_bValid = true; + + poLabel = new CNCSJP2File::CNCSJPXLabelBox(); + poLabel->SetLabel( "CRSDictionary.gml" ); + poLabel->m_bValid = true; + poAssoc->m_OtherBoxes.push_back( poLabel ); + poAssoc->m_OwnedBoxes.push_back( poLabel ); + + poGMLData = new JP2UserBox(); + poGMLData->m_nTBox = 'xml '; /* Is it correct on a big-endian host ? Does ECW work on big-endian hosts ;-) */ + poGMLData->SetData( strlen(pszDictBox), + (unsigned char *) pszDictBox ); + poAssoc->m_OtherBoxes.push_back( poGMLData ); + poAssoc->m_OwnedBoxes.push_back( poGMLData ); + + CPLFree( pszDictBox ); + } + + m_oGMLAssoc.m_bValid = true; + AddBox( &m_oGMLAssoc ); + + return CE_None; +#endif /* def ECW_FW */ +} + +/************************************************************************/ +/* WriteJP2Box() */ +/************************************************************************/ + +CPLErr GDALECWCompressor::WriteJP2Box( GDALJP2Box * poBox ) + +{ + JP2UserBox *poECWBox; + + if( poBox == NULL ) + return CE_None; + + poECWBox = new JP2UserBox(); + memcpy( &(poECWBox->m_nTBox), poBox->GetType(), 4 ); + CPL_MSBPTR32( &(poECWBox->m_nTBox) ); + + poECWBox->SetData( (int) poBox->GetDataLength(), + poBox->GetWritableData() ); + + AddBox( poECWBox ); + + delete poBox; + + papoJP2UserBox =(JP2UserBox**) CPLRealloc(papoJP2UserBox, + (nJP2UserBox + 1) * sizeof(JP2UserBox*)); + papoJP2UserBox[nJP2UserBox] = poECWBox; + nJP2UserBox ++; + + return CE_None; +} + +/************************************************************************/ +/* WriteXMLBoxes() */ +/************************************************************************/ + +void GDALECWCompressor::WriteXMLBoxes() +{ + int nBoxes = 0; + GDALJP2Box** papoBoxes = GDALJP2Metadata::CreateXMLBoxes(m_poSrcDS, &nBoxes); + for(int i=0;i= 40 + const char* pszECWKey = CSLFetchNameValue( papszOptions, "ECW_ENCODE_KEY"); + if( pszECWKey == NULL ) + pszECWKey = CPLGetConfigOption( "ECW_ENCODE_KEY", NULL ); + + const char* pszECWCompany = + CSLFetchNameValue( papszOptions, "ECW_ENCODE_COMPANY"); + if( pszECWCompany == NULL ) + pszECWCompany = CPLGetConfigOption( "ECW_ENCODE_COMPANY", NULL ); + + if( pszECWKey && pszECWCompany) + { + CPLDebug( "ECW", "SetOEMKey(%s,%s)", pszECWCompany, pszECWKey ); + CNCSFile::SetOEMKey( (char *) pszECWCompany, (char *)pszECWKey ); + } + else if( pszECWKey || pszECWCompany ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Only one of ECW_ENCODE_KEY and ECW_ENCODE_COMPANY were provided.\nBoth are required." ); + return CE_Failure; + }else{ + CPLError( CE_Failure, CPLE_AppDefined, + "None of ECW_ENCODE_KEY and ECW_ENCODE_COMPANY were provided.\nBoth are required." ); + return CE_Failure; + } + +#endif /* ECWSDK_VERSION >= 40 */ + +/* -------------------------------------------------------------------- */ +/* Do some rudimentary checking in input. */ +/* -------------------------------------------------------------------- */ + if( nBands == 0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "ECW driver requires at least one band." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Parse out some known options. */ +/* -------------------------------------------------------------------- */ + float fTargetCompression; + + // Default compression based on image type per request from Paul Beaty. + if( nBands > 1 ) + fTargetCompression = 95.0; + else + fTargetCompression = 90.0; + + if( CSLFetchNameValue(papszOptions, "TARGET") != NULL ) + { + fTargetCompression = (float) + CPLAtof(CSLFetchNameValue(papszOptions, "TARGET")); + + /* The max allowed value should be 100 - 100 / 65535 = 99.9984740978 */ + /* so that nCompressionRate fits on a uint16 (see below) */ + /* No need to be so pedantic, so we will limit to 99.99 % */ + /* (compression rate = 10 000) */ + if( fTargetCompression < 0.0 || fTargetCompression > 99.99 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "TARGET compression of %.3f invalid, should be a\n" + "value between 0 and 99.99 percent.\n", + (double) fTargetCompression ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Create and initialize compressor. */ +/* -------------------------------------------------------------------- */ + NCSFileViewFileInfoEx *psClient = &(sFileInfo); +#if ECWSDK_VERSION >= 50 + if( bIsJPEG2000 == FALSE ) + { + bool bECWV3 = false; + pszOption = CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION"); + if( pszOption != NULL ) + { + bECWV3 = (3 == atoi(pszOption)); + } + psClient->nFormatVersion = (bECWV3)?3:2; + } + else + { + psClient->nFormatVersion = 1; + } +#endif + psClient->nBands = (UINT16) nBands; + psClient->nSizeX = nXSize; + psClient->nSizeY = nYSize; + psClient->nCompressionRate = (UINT16) MAX(1,100/(100-fTargetCompression)); + psClient->eCellSizeUnits = ECW_CELL_UNITS_METERS; + + if( nBands == 1 ) + psClient->eColorSpace = NCSCS_GREYSCALE; + else if( nBands == 3 && bRGBColorSpace) + psClient->eColorSpace = NCSCS_sRGB; +#if ECWSDK_VERSION >= 40 + else if( nBands == 4 && bRGBColorSpace ) + psClient->eColorSpace = NCSCS_sRGB; +#endif + else + psClient->eColorSpace = NCSCS_MULTIBAND; + +/* -------------------------------------------------------------------- */ +/* Figure out the data type. */ +/* -------------------------------------------------------------------- */ + int bSigned = FALSE; + int nBits = 8; + eWorkDT = eType; + + switch( eWorkDT ) + { + case GDT_Byte: +#if ECWSDK_VERSION >=50 + psClient->nCellBitDepth = 8; +#endif + psClient->eCellType = NCSCT_UINT8; + nBits = 8; + bSigned = FALSE; + break; + + case GDT_UInt16: +#if ECWSDK_VERSION >=50 + psClient->nCellBitDepth = 16; +#endif + psClient->eCellType = NCSCT_UINT16; + nBits = 16; + bSigned = FALSE; + break; + + case GDT_UInt32: +#if ECWSDK_VERSION >=50 + psClient->nCellBitDepth = 32; +#endif + psClient->eCellType = NCSCT_UINT32; + nBits = 32; + bSigned = FALSE; + break; + + case GDT_Int16: +#if ECWSDK_VERSION >=50 + psClient->nCellBitDepth = 16; +#endif + psClient->eCellType = NCSCT_INT16; + nBits = 16; + bSigned = TRUE; + break; + + case GDT_Int32: +#if ECWSDK_VERSION >=50 + psClient->nCellBitDepth = 32; +#endif + psClient->eCellType = NCSCT_INT32; + nBits = 32; + bSigned = TRUE; + break; + + case GDT_Float32: + psClient->eCellType = NCSCT_IEEE4; + nBits = 32; + bSigned = TRUE; + break; + + case GDT_Float64: + psClient->eCellType = NCSCT_IEEE8; + nBits = 64; + bSigned = TRUE; + break; + + default: + // We treat complex types as float. + psClient->eCellType = NCSCT_IEEE4; + nBits = 32; + bSigned = TRUE; + eWorkDT = GDT_Float32; + break; + } + +/* -------------------------------------------------------------------- */ +/* Create band information structures. */ +/* -------------------------------------------------------------------- */ + int iBand; + +#if ECWSDK_VERSION>=50 + psClient->pBands = (NCSFileBandInfo *) + NCSMalloc( sizeof(NCSFileBandInfo) * nBands, true ); +#else + psClient->pBands = (NCSFileBandInfo *) + CPLMalloc( sizeof(NCSFileBandInfo) * nBands ); +#endif + for( iBand = 0; iBand < nBands; iBand++ ) + { + psClient->pBands[iBand].nBits = (UINT8) nBits; + psClient->pBands[iBand].bSigned = (BOOLEAN)bSigned; +#if ECWSDK_VERSION>=50 + psClient->pBands[iBand].szDesc = NCSStrDup(papszBandDescriptions[iBand]); +#else + psClient->pBands[iBand].szDesc = CPLStrdup(papszBandDescriptions[iBand]); +#endif + } + +/* -------------------------------------------------------------------- */ +/* Allow CNCSFile::SetParameter() requests. */ +/* -------------------------------------------------------------------- */ + + if( bIsJPEG2000 ) + { + pszOption = CSLFetchNameValue(papszOptions, "PROFILE"); + if( pszOption != NULL && EQUAL(pszOption,"BASELINE_0") ) + SetParameter( + CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_0 ); + else if( pszOption != NULL && EQUAL(pszOption,"BASELINE_1") ) + SetParameter( + CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_1 ); + else if( pszOption != NULL && EQUAL(pszOption,"BASELINE_2") ) + SetParameter( + CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_2 ); + else if( pszOption != NULL && EQUAL(pszOption,"NPJE") ) + SetParameter( + CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_NPJE ); + else if( pszOption != NULL && EQUAL(pszOption,"EPJE") ) + SetParameter( + CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_EPJE ); + + pszOption = CSLFetchNameValue(papszOptions, "CODESTREAM_ONLY" ); + if( pszOption != NULL ) + SetParameter( + CNCSJP2FileView::JP2_COMPRESS_CODESTREAM_ONLY, + (bool) CSLTestBoolean( pszOption ) ); + + pszOption = CSLFetchNameValue(papszOptions, "LEVELS"); + if( pszOption != NULL ) + SetParameter( CNCSJP2FileView::JP2_COMPRESS_LEVELS, + (UINT32) atoi(pszOption) ); + + pszOption = CSLFetchNameValue(papszOptions, "LAYERS"); + if( pszOption != NULL ) + SetParameter( CNCSJP2FileView::JP2_COMPRESS_LAYERS, + (UINT32) atoi(pszOption) ); + + pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_WIDTH"); + if( pszOption != NULL ) + SetParameter( CNCSJP2FileView::JP2_COMPRESS_PRECINCT_WIDTH, + (UINT32) atoi(pszOption) ); + + pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_HEIGHT"); + if( pszOption != NULL ) + SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_HEIGHT, + (UINT32) atoi(pszOption) ); + + pszOption = CSLFetchNameValue(papszOptions, "TILE_WIDTH"); + if( pszOption != NULL ) + SetParameter( CNCSJP2FileView::JP2_COMPRESS_TILE_WIDTH, + (UINT32) atoi(pszOption) ); + + pszOption = CSLFetchNameValue(papszOptions, "TILE_HEIGHT"); + if( pszOption != NULL ) + SetParameter( CNCSJP2FileView::JP2_COMPRESS_TILE_HEIGHT, + (UINT32) atoi(pszOption) ); + + pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_SOP"); + if( pszOption != NULL ) + SetParameter( CNCSJP2FileView::JP2_COMPRESS_INCLUDE_SOP, + (bool) CSLTestBoolean( pszOption ) ); + + pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_EPH"); + if( pszOption != NULL ) + SetParameter( CNCSJP2FileView::JP2_COMPRESS_INCLUDE_EPH, + (bool) CSLTestBoolean( pszOption ) ); + + pszOption = CSLFetchNameValue(papszOptions, "PROGRESSION"); + if( pszOption != NULL && EQUAL(pszOption,"LRCP") ) + SetParameter( + CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_LRCP ); + + else if( pszOption != NULL && EQUAL(pszOption,"RLCP") ) + SetParameter( + CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RLCP ); + + else if( pszOption != NULL && EQUAL(pszOption,"RPCL") ) + SetParameter( + CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RPCL ); + + pszOption = CSLFetchNameValue(papszOptions, "GEODATA_USAGE"); + if( pszOption == NULL ) + // Default to suppressing ECW SDK geodata, just use our own stuff. + SetGeodataUsage( JP2_GEODATA_USE_NONE ); + else if( EQUAL(pszOption,"NONE") ) + SetGeodataUsage( JP2_GEODATA_USE_NONE ); + else if( EQUAL(pszOption,"PCS_ONLY") ) + SetGeodataUsage( JP2_GEODATA_USE_PCS_ONLY ); + else if( EQUAL(pszOption,"GML_ONLY") ) + SetGeodataUsage( JP2_GEODATA_USE_GML_ONLY ); + else if( EQUAL(pszOption,"PCS_GML") ) + SetGeodataUsage( JP2_GEODATA_USE_PCS_GML ); + else if( EQUAL(pszOption,"GML_PCS") ) + SetGeodataUsage( JP2_GEODATA_USE_GML_PCS ); + else if( EQUAL(pszOption,"ALL") ) + SetGeodataUsage( JP2_GEODATA_USE_GML_PCS_WLD ); + + pszOption = CSLFetchNameValue(papszOptions, "DECOMPRESS_LAYERS"); + if( pszOption != NULL ) + SetParameter( + CNCSJP2FileView::JP2_DECOMPRESS_LAYERS, + (UINT32) atoi(pszOption) ); + + pszOption = CSLFetchNameValue(papszOptions, + "DECOMPRESS_RECONSTRUCTION_PARAMETER"); + if( pszOption != NULL ) + SetParameter( + CNCSJP2FileView::JPC_DECOMPRESS_RECONSTRUCTION_PARAMETER, + (IEEE4) CPLAtof(pszOption) ); + } + +/* -------------------------------------------------------------------- */ +/* Georeferencing. */ +/* -------------------------------------------------------------------- */ + + psClient->fOriginX = 0.0; + psClient->fOriginY = psClient->nSizeY; + psClient->fCellIncrementX = 1.0; + psClient->fCellIncrementY = -1.0; + psClient->fCWRotationDegrees = 0.0; + + if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0 ) + CPLError( CE_Warning, CPLE_NotSupported, + "Rotational coefficients ignored, georeferencing of\n" + "output ECW file will be incorrect.\n" ); + else + { + psClient->fOriginX = padfGeoTransform[0]; + psClient->fOriginY = padfGeoTransform[3]; + psClient->fCellIncrementX = padfGeoTransform[1]; + psClient->fCellIncrementY = padfGeoTransform[5]; + } + +/* -------------------------------------------------------------------- */ +/* Projection. */ +/* -------------------------------------------------------------------- */ + char szProjection[128]; + char szDatum[128]; + char szUnits[128]; + + strcpy( szProjection, "RAW" ); + strcpy( szDatum, "RAW" ); + + if( CSLFetchNameValue(papszOptions, "PROJ") != NULL ) + { + strncpy( szProjection, + CSLFetchNameValue(papszOptions, "PROJ"), sizeof(szProjection) ); + szProjection[sizeof(szProjection)-1] = 0; + } + + if( CSLFetchNameValue(papszOptions, "DATUM") != NULL ) + { + strncpy( szDatum, CSLFetchNameValue(papszOptions, "DATUM"), sizeof(szDatum) ); + szDatum[sizeof(szDatum)-1] = 0; + if( EQUAL(szProjection,"RAW") ) + strcpy( szProjection, "GEODETIC" ); + } + + const char* pszUnits = CSLFetchNameValue(papszOptions, "UNITS"); + if( pszUnits != NULL ) + { + psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(pszUnits); + } + + if( EQUAL(szProjection,"RAW") && pszWKT != NULL ) + { + ECWTranslateFromWKT( pszWKT, szProjection, sizeof(szProjection), szDatum, sizeof(szDatum), szUnits ); + psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits); + } + +#if ECWSDK_VERSION>=50 + NCSFree(psClient->szDatum); + psClient->szDatum = NCSStrDup(szDatum); + NCSFree(psClient->szProjection); + psClient->szProjection = NCSStrDup(szProjection); +#else + psClient->szDatum = szDatum; + psClient->szProjection = szProjection; +#endif + + CPLDebug( "ECW", "Writing with PROJ=%s, DATUM=%s, UNITS=%s", + szProjection, szDatum, ECWTranslateFromCellSizeUnits(psClient->eCellSizeUnits) ); + +/* -------------------------------------------------------------------- */ +/* Setup GML and GeoTIFF information. */ +/* -------------------------------------------------------------------- */ + if( (pszWKT != NULL && pszWKT[0] != '\0') || + !(padfGeoTransform[0] == 0.0 && + padfGeoTransform[1] == 1.0 && + padfGeoTransform[2] == 0.0 && + padfGeoTransform[3] == 0.0 && + padfGeoTransform[4] == 0.0 && + padfGeoTransform[5] == 1.0) || + nGCPCount > 0 || papszRPCMD != NULL ) + { + GDALJP2Metadata oJP2MD; + + oJP2MD.SetProjection( pszWKT ); + oJP2MD.SetGeoTransform( padfGeoTransform ); + oJP2MD.SetGCPs( nGCPCount, pasGCPList ); + oJP2MD.bPixelIsPoint = bPixelIsPoint; + oJP2MD.SetRPCMD( papszRPCMD ); + + if (bIsJPEG2000) { + if( CSLFetchBoolean(papszOptions, "WRITE_METADATA", FALSE) ) + { + if( !CSLFetchBoolean(papszOptions, "MAIN_MD_DOMAIN_ONLY", FALSE) ) + { + WriteXMLBoxes(); + } + WriteJP2Box(GDALJP2Metadata::CreateGDALMultiDomainMetadataXMLBox( + m_poSrcDS, CSLFetchBoolean(papszOptions, "MAIN_MD_DOMAIN_ONLY", FALSE))); + } + if( CSLFetchBoolean( papszOptions, "GMLJP2", TRUE ) ) + { + const char* pszGMLJP2V2Def = CSLFetchNameValue( papszOptions, "GMLJP2V2_DEF" ); + if( pszGMLJP2V2Def != NULL ) + WriteJP2Box( oJP2MD.CreateGMLJP2V2(nXSize,nYSize,pszGMLJP2V2Def,poSrcDS) ); + else + WriteJP2Box( oJP2MD.CreateGMLJP2(nXSize,nYSize) ); + } + if( CSLFetchBoolean( papszOptions, "GeoJP2", TRUE ) ) + WriteJP2Box( oJP2MD.CreateJP2GeoTIFF() ); + if( CSLFetchBoolean(papszOptions, "WRITE_METADATA", FALSE) && + !CSLFetchBoolean(papszOptions, "MAIN_MD_DOMAIN_ONLY", FALSE) ) + { + WriteJP2Box(GDALJP2Metadata::CreateXMPBox(m_poSrcDS)); + } + } + } +/* -------------------------------------------------------------------- */ +/* We handle all jpeg2000 files via the VSIIOStream, but ECW */ +/* files cannot be done this way for some reason. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpVSIL = NULL; + + if( bIsJPEG2000 ) + { + int bSeekable = ! + ( strncmp(pszFilename, "/vsistdout/", strlen("/vsistdout/")) == 0 || + strncmp(pszFilename, "/vsizip/", strlen("/vsizip/")) == 0 || + strncmp(pszFilename, "/vsigzip/", strlen("/vsigzip/")) == 0 ); + fpVSIL = VSIFOpenL( pszFilename, (bSeekable) ? "wb+": "wb" ); + if( fpVSIL == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s.", pszFilename ); + return CE_Failure; + } + + m_OStream.Access( fpVSIL, TRUE, (BOOLEAN) bSeekable, pszFilename, + 0, -1 ); + } + +/* -------------------------------------------------------------------- */ +/* Check if we can enable large files. This option should only */ +/* be set when the application is adhering to one of the */ +/* ERMapper options for licensing larger than 500MB input */ +/* files. See Bug 767. This option no longer exists with */ +/* version 4+. */ +/* -------------------------------------------------------------------- */ +#if ECWSDK_VERSION < 40 + const char *pszLargeOK = CSLFetchNameValue(papszOptions, "LARGE_OK"); + if( pszLargeOK == NULL ) + pszLargeOK = "NO"; + + pszLargeOK = CPLGetConfigOption( "ECW_LARGE_OK", pszLargeOK ); + + if( CSLTestBoolean(pszLargeOK) ) + { + CNCSFile::SetKeySize(); + CPLDebug( "ECW", "Large file generation enabled." ); + } +#endif /* ECWSDK_VERSION < 40 */ +/* -------------------------------------------------------------------- */ +/* Infer metadata information from source dataset if possible */ +/* -------------------------------------------------------------------- */ +#if ECWSDK_VERSION>=50 + if (psClient->nFormatVersion>2){ + if (psClient->pFileMetaData == NULL){ + NCSEcwInitMetaData(&psClient->pFileMetaData); + } + if (m_poSrcDS && m_poSrcDS->GetMetadataItem("FILE_METADATA_ACQUISITION_DATE")!=NULL){ + psClient->pFileMetaData->sAcquisitionDate = NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_ACQUISITION_DATE")).c_str()); + } + + if (m_poSrcDS && m_poSrcDS->GetMetadataItem("FILE_METADATA_ACQUISITION_SENSOR_NAME")!=NULL){ + psClient->pFileMetaData->sAcquisitionSensorName = NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_ACQUISITION_SENSOR_NAME")).c_str()); + } + if (m_poSrcDS && m_poSrcDS->GetMetadataItem("FILE_METADATA_ADDRESS")!=NULL){ + psClient->pFileMetaData->sAddress = NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_ADDRESS")).c_str()); + } + if (m_poSrcDS && m_poSrcDS->GetMetadataItem("FILE_METADATA_AUTHOR")!=NULL){ + psClient->pFileMetaData->sAuthor = NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_AUTHOR")).c_str()); + } + if (m_poSrcDS && m_poSrcDS->GetMetadataItem("FILE_METADATA_CLASSIFICATION")!=NULL){ + psClient->pFileMetaData->sClassification = NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_CLASSIFICATION")).c_str()); + } + if ( pszECWCompany != NULL && CSLTestBoolean(CPLGetConfigOption("GDAL_ECW_WRITE_COMPANY", "YES")) ){ + psClient->pFileMetaData->sCompany = NCSStrDupT(NCS::CString(pszECWCompany).c_str()); + } + CPLString osCompressionSoftware = GetCompressionSoftwareName(); + if ( osCompressionSoftware.size() > 0 ) { + psClient->pFileMetaData->sCompressionSoftware = NCSStrDupT(NCS::CString(osCompressionSoftware.c_str()).c_str()); + } + if (m_poSrcDS && m_poSrcDS->GetMetadataItem("FILE_METADATA_COPYRIGHT")!=NULL){ + psClient->pFileMetaData->sCopyright = NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_COPYRIGHT")).c_str()); + } + if (m_poSrcDS && m_poSrcDS->GetMetadataItem("FILE_METADATA_EMAIL")!=NULL){ + psClient->pFileMetaData->sEmail = NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_EMAIL")).c_str()); + } + if (m_poSrcDS && m_poSrcDS->GetMetadataItem("FILE_METADATA_TELEPHONE")!=NULL){ + psClient->pFileMetaData->sTelephone = NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_TELEPHONE")).c_str()); + } + } +#endif +/* -------------------------------------------------------------------- */ +/* Set the file info. */ +/* -------------------------------------------------------------------- */ + CNCSError oError; + + oError = SetFileInfo( sFileInfo ); + + if( oError.GetErrorNumber() == NCS_SUCCESS ) + { + if( fpVSIL == NULL ) + oError = Open( (char *) pszFilename, false, true ); + else + oError = CNCSJP2FileView::Open( &(m_OStream) ); + } + + if( oError.GetErrorNumber() == NCS_SUCCESS ) + return CE_None; + else if( oError.GetErrorNumber() == NCS_INPUT_SIZE_EXCEEDED ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "ECW SDK compress limit exceeded." ); + return CE_Failure; + } + else + { + ECWReportError(oError); + + return CE_Failure; + } +} + +/************************************************************************/ +/* ECWIsInputRGBColorSpace() */ +/************************************************************************/ + +static int ECWIsInputRGBColorSpace(GDALDataset* poSrcDS) +{ + int nBands = poSrcDS->GetRasterCount(); + +/* -------------------------------------------------------------------- */ +/* Is the input RGB or RGBA? */ +/* -------------------------------------------------------------------- */ + int bRGBColorSpace = FALSE; + int bRGB = FALSE; + if ( nBands>=3 ) { + bRGB = (poSrcDS->GetRasterBand(1)->GetColorInterpretation() + == GCI_RedBand); + bRGB &= (poSrcDS->GetRasterBand(2)->GetColorInterpretation() + == GCI_GreenBand); + bRGB &= (poSrcDS->GetRasterBand(3)->GetColorInterpretation() + == GCI_BlueBand); + } + if( nBands == 3 ) + { + bRGBColorSpace = bRGB; + } + else if( nBands == 4 && bRGB ) + { + bRGBColorSpace = (poSrcDS->GetRasterBand(4)->GetColorInterpretation() + == GCI_AlphaBand); + } + + return bRGBColorSpace; +} + +/************************************************************************/ +/* ECWCreateCopy() */ +/************************************************************************/ + +static GDALDataset * +ECWCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData, + int bIsJPEG2000 ) + +{ + ECWInitialize(); + +/* -------------------------------------------------------------------- */ +/* Get various values from the source dataset. */ +/* -------------------------------------------------------------------- */ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "ECW driver does not support source dataset with zero band.\n"); + return NULL; + } + + GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + + const char *pszWKT = poSrcDS->GetProjectionRef(); + double adfGeoTransform[6] = { 0, 1, 0, 0, 0, 1 };; + + poSrcDS->GetGeoTransform( adfGeoTransform ); + + if( poSrcDS->GetGCPCount() > 0 ) + pszWKT = poSrcDS->GetGCPProjection(); + +/* -------------------------------------------------------------------- */ +/* For ECW, confirm the datatype is 8bit (or uint16 for ECW v3) */ +/* -------------------------------------------------------------------- */ + bool bECWV3 = false; + + #if ECWSDK_VERSION >= 50 + if (bIsJPEG2000 == FALSE){ + const char* pszOption = CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION"); + if( pszOption != NULL ) + { + bECWV3 = (3 == atoi(pszOption)); + } + } + #endif + if( !(eType == GDT_Byte || (bECWV3 && eType == GDT_UInt16 ) || bIsJPEG2000 ) ) + { + if( bStrict ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create ECW file with pixel data type %s failed.\n" + "Only Byte data type supported for ECW version 2 files." +#if ECWSDK_VERSION >= 50 + " ECW version 3 files supports UInt16 as well." + " Specify ECW_FORMAT_VERSION=3 creation option to write version 3 file. \n" +#else + ". \n" +#endif + , GDALGetDataTypeName( eType ) ); + } + else + { +#if ECWSDK_VERSION>=50 + if (eType == GDT_UInt16) + { + CPLError( CE_Warning, CPLE_AppDefined, + "ECW version 2 does not support UInt16 data type, truncating to Byte." + " Consider specifying ECW_FORMAT_VERSION=3 for full UInt16 support available in ECW version 3. \n"); + } + else +#endif + CPLError( CE_Warning, CPLE_AppDefined, + "ECW v2 does not support data type, ignoring request for %s. \n", + GDALGetDataTypeName( eType ) ); + + eType = GDT_Byte; + } + } + +/* -------------------------------------------------------------------- */ +/* Is the input RGB or RGBA? */ +/* -------------------------------------------------------------------- */ + int bRGBColorSpace = ECWIsInputRGBColorSpace(poSrcDS); + +/* -------------------------------------------------------------------- */ +/* Setup the compressor. */ +/* -------------------------------------------------------------------- */ + GDALECWCompressor oCompressor; + CNCSError oErr; + + oCompressor.pfnProgress = pfnProgress; + oCompressor.pProgressData = pProgressData; + oCompressor.m_poSrcDS = poSrcDS; + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + + char** papszBandDescriptions = (char**) CPLMalloc(nBands * sizeof(char*)); + int i; + for (i=0;iGetRasterBand(i+1)->GetColorInterpretation(), i)); + } + + const char* pszAreaOrPoint = poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT); + int bPixelIsPoint = pszAreaOrPoint != NULL && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT); + + if( oCompressor.Initialize( pszFilename, papszOptions, + nXSize, nYSize, nBands, papszBandDescriptions, bRGBColorSpace, + eType, pszWKT, adfGeoTransform, + poSrcDS->GetGCPCount(), + poSrcDS->GetGCPs(), + bIsJPEG2000, bPixelIsPoint, + poSrcDS->GetMetadata("RPC"), + poSrcDS ) + != CE_None ) + { + for (i=0;i=50 + for (int i=1;i<=poSrcDS->GetRasterCount();i++){ + double dMin, dMax, dMean, dStdDev; + if (poSrcDS->GetRasterBand(i)->GetStatistics(FALSE, FALSE, &dMin, &dMax, &dMean, &dStdDev)==CE_None){ + poDS->GetRasterBand(i)->SetStatistics(dMin, dMax, dMean, dStdDev); + } + double dHistMin, dHistMax; + int nBuckets; + GUIntBig *pHistogram; + if (poSrcDS->GetRasterBand(i)->GetDefaultHistogram(&dHistMin, &dHistMax,&nBuckets,&pHistogram, FALSE, NULL, NULL) == CE_None){ + poDS->GetRasterBand(i)->SetDefaultHistogram(dHistMin, dHistMax, nBuckets, pHistogram); + VSIFree(pHistogram); + } + } +#endif + + ((ECWDataset *)poDS)->SetPreventCopyingSomeMetadata(TRUE); + int nFlags = GCIF_PAM_DEFAULT; + if( bIsJPEG2000 && + !CSLFetchBoolean(papszOptions, "WRITE_METADATA", FALSE) ) + nFlags &= ~GCIF_METADATA; + poDS->CloneInfo( poSrcDS, nFlags ); + ((ECWDataset *)poDS)->SetPreventCopyingSomeMetadata(FALSE); + } + + return poDS; +} + +/************************************************************************/ +/* ECWCreateCopyECW() */ +/************************************************************************/ + +GDALDataset * +ECWCreateCopyECW( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "ECW driver does not support source dataset with zero band.\n"); + return NULL; + } + + if( !EQUAL(CPLGetExtension(pszFilename),"ecw") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "ECW driver does not support creating ECW files\n" + "with an extension other than .ecw" ); + return NULL; + } + bool bECWV3 = false; + +#if ECWSDK_VERSION >= 50 + + const char* pszOption = CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION"); + if( pszOption != NULL ) + { + bECWV3 = (3 == atoi(pszOption)); + } + +#endif + + GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + if( eDataType != GDT_Byte + && !(bECWV3 && (eDataType == GDT_UInt16)) + && bStrict ) + { +#if ECWSDK_VERSION >= 50 + if (eDataType == GDT_UInt16){ + CPLError( CE_Failure, CPLE_NotSupported, + "ECW v2 does not support UInt16 data type. Consider " + " specifying ECW_FORMAT_VERSION=3 for full UInt16 support available in ECW v3. \n" + ); + } + else +#endif + { + CPLError( CE_Failure, CPLE_NotSupported, + "ECW driver doesn't support data type %s. " + "Only unsigned eight " +#if ECWSDK_VERSION >= 50 + "or sixteen " +#endif + "bit bands supported. \n", + GDALGetDataTypeName(eDataType) ); + } + + return NULL; + } + + if( poSrcDS->GetRasterXSize() < 128 || poSrcDS->GetRasterYSize() < 128 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "ECW driver requires image to be at least 128x128,\n" + "the source image is %dx%d.\n", + poSrcDS->GetRasterXSize(), + poSrcDS->GetRasterYSize() ); + + return NULL; + } + + if (poSrcDS->GetRasterBand(1)->GetColorTable() != NULL) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "ECW driver ignores color table. " + "The source raster band will be considered as grey level.\n" + "Consider using color table expansion (-expand option in gdal_translate)\n"); + if (bStrict) + return NULL; + } + + return ECWCreateCopy( pszFilename, poSrcDS, bStrict, papszOptions, + pfnProgress, pProgressData, FALSE ); +} + +/************************************************************************/ +/* ECWCreateCopyJPEG2000() */ +/************************************************************************/ + +GDALDataset * +ECWCreateCopyJPEG2000( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "JP2ECW driver does not support source dataset with zero band.\n"); + return NULL; + } + + if( EQUAL(CPLGetExtension(pszFilename),"ecw") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JP2ECW driver does not support creating JPEG2000 files\n" + "with a .ecw extension. Please use anything else." ); + return NULL; + } + + GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + if( eDataType != GDT_Byte + && eDataType != GDT_Byte + && eDataType != GDT_Int16 + && eDataType != GDT_UInt16 + && eDataType != GDT_Int32 + && eDataType != GDT_UInt32 + && eDataType != GDT_Float32 + && eDataType != GDT_Float64 + && bStrict ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "JP2ECW driver doesn't support data type %s. ", + GDALGetDataTypeName(eDataType) ); + + return NULL; + } + + if (poSrcDS->GetRasterBand(1)->GetColorTable() != NULL) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "JP2ECW driver ignores color table. " + "The source raster band will be considered as grey level.\n" + "Consider using color table expansion (-expand option in gdal_translate)\n"); + if (bStrict) + return NULL; + } + + return ECWCreateCopy( pszFilename, poSrcDS, bStrict, papszOptions, + pfnProgress, pProgressData, TRUE ); +} + +/************************************************************************/ +/************************************************************************ + + ECW/JPEG200 Create() Support + ---------------------------- + + The remainder of the file is code to implement the Create() method. + New dataset and raster band classes are defined specifically for the + purpose of being write-only. In particular, you cannot read back data + from these datasets, and writing must occur in a pretty specific order. + + That is, you need to write all metadata (projection, georef, etc) first + and then write the image data. All bands data for the first scanline + should be written followed by all bands for the second scanline and so on. + + Creation supports the same virtual subfile names as CreateCopy() supports. + + ************************************************************************/ +/************************************************************************/ + +/************************************************************************/ +/* ==================================================================== */ +/* ECWWriteDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class ECWWriteRasterBand; + +#ifdef OPTIMIZED_FOR_GDALWARP +class IRasterIORequest +{ + public: + GDALRasterBand* poBand; + int nXOff; + int nYOff; + int nXSize; + int nYSize; + GByte* pabyData; + int nBufXSize; + int nBufYSize; + + IRasterIORequest( GDALRasterBand* poBand, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace ) : + poBand(poBand), + nXOff(nXOff), + nYOff(nYOff), + nXSize(nXSize), + nYSize(nYSize), + pabyData(NULL), + nBufXSize(nBufXSize), + nBufYSize(nBufYSize) + { + GDALDataType eDataType = poBand->GetRasterDataType(); + int nDataTypeSize = GDALGetDataTypeSize(eDataType) / 8; + pabyData = (GByte*)CPLMalloc(nBufXSize * nBufYSize * nDataTypeSize); + for(int iY = 0; iY < nBufYSize; iY ++) + { + GDALCopyWords((GByte*)pData + iY * nLineSpace, + eBufType, nPixelSpace, + pabyData + iY * nBufXSize * nDataTypeSize, + eDataType, nDataTypeSize, + nBufXSize); + } + } + ~IRasterIORequest() { CPLFree(pabyData); } +}; +#endif + +class CPL_DLL ECWWriteDataset : public GDALDataset +{ + friend class ECWWriteRasterBand; + + char *pszFilename; + + int bIsJPEG2000; + GDALDataType eDataType; + char **papszOptions; + + char *pszProjection; + double adfGeoTransform[6]; + + GDALECWCompressor oCompressor; + int bCrystalized; + + int nLoadedLine; + GByte *pabyBILBuffer; + + int bOutOfOrderWriteOccured; +#ifdef OPTIMIZED_FOR_GDALWARP + int nPrevIRasterIOBand; +#endif + + CPLErr Crystalize(); + CPLErr FlushLine(); + + public: + ECWWriteDataset( const char *, int, int, int, + GDALDataType, char **papszOptions, + int ); + ~ECWWriteDataset(); + + virtual void FlushCache( void ); + + virtual CPLErr GetGeoTransform( double * ); + virtual const char* GetProjectionRef(); + virtual CPLErr SetGeoTransform( double * ); + virtual CPLErr SetProjection( const char *pszWKT ); + +#ifdef OPTIMIZED_FOR_GDALWARP + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); +#endif +}; + +/************************************************************************/ +/* ==================================================================== */ +/* ECWWriteRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class ECWWriteRasterBand : public GDALRasterBand +{ + friend class ECWWriteDataset; + + // NOTE: poDS may be altered for NITF/JPEG2000 files! + ECWWriteDataset *poGDS; + + GDALColorInterp eInterp; + +#ifdef OPTIMIZED_FOR_GDALWARP + IRasterIORequest *poIORequest; +#endif + + public: + + ECWWriteRasterBand( ECWWriteDataset *, int ); + ~ECWWriteRasterBand(); + + virtual CPLErr SetColorInterpretation( GDALColorInterp eInterpIn ) + { eInterp = eInterpIn; + if( strlen(GetDescription()) == 0 ) + SetDescription(ECWGetColorInterpretationName(eInterp, nBand-1)); + return CE_None; + } + virtual GDALColorInterp GetColorInterpretation() + { return eInterp; } + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + +#ifdef OPTIMIZED_FOR_GDALWARP + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); +#endif +}; + +/************************************************************************/ +/* ECWWriteDataset() */ +/************************************************************************/ + +ECWWriteDataset::ECWWriteDataset( const char *pszFilename, + int nXSize, int nYSize, int nBandCount, + GDALDataType eType, + char **papszOptions, int bIsJPEG2000 ) + +{ + bCrystalized = FALSE; + pabyBILBuffer = NULL; + nLoadedLine = -1; + + eAccess = GA_Update; + + this->bIsJPEG2000 = bIsJPEG2000; + this->eDataType = eType; + this->papszOptions = CSLDuplicate( papszOptions ); + this->pszFilename = CPLStrdup( pszFilename ); + + nRasterXSize = nXSize; + nRasterYSize = nYSize; + pszProjection = NULL; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + // create band objects. + for( int iBand = 1; iBand <= nBandCount; iBand++ ) + { + SetBand( iBand, new ECWWriteRasterBand( this, iBand ) ); + } + + bOutOfOrderWriteOccured = FALSE; +#ifdef OPTIMIZED_FOR_GDALWARP + nPrevIRasterIOBand = -1; +#endif +} + +/************************************************************************/ +/* ~ECWWriteDataset() */ +/************************************************************************/ + +ECWWriteDataset::~ECWWriteDataset() + +{ + FlushCache(); + + if( bCrystalized ) + { + if( bOutOfOrderWriteOccured ) + { + /* Otherwise there's a hang-up in the destruction of the oCompressor object */ + while( nLoadedLine < nRasterYSize - 1 ) + FlushLine(); + } + if( nLoadedLine == nRasterYSize - 1 ) + FlushLine(); + oCompressor.CloseDown(); + } + + CPLFree( pabyBILBuffer ); + CPLFree( pszProjection ); + CSLDestroy( papszOptions ); + CPLFree( pszFilename ); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void ECWWriteDataset::FlushCache() + +{ + BlockBasedFlushCache(); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* ECWWriteDataset::GetProjectionRef() +{ + return pszProjection; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr ECWWriteDataset::GetGeoTransform( double *padfGeoTransform ) + +{ + memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr ECWWriteDataset::SetGeoTransform( double *padfGeoTransform ) + +{ + memcpy( adfGeoTransform, padfGeoTransform, sizeof(double) * 6 ); + return CE_None; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr ECWWriteDataset::SetProjection( const char *pszWKT ) + +{ + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszWKT ); + + return CE_None; +} + + +/************************************************************************/ +/* Crystalize() */ +/************************************************************************/ + +CPLErr ECWWriteDataset::Crystalize() + +{ + int nWordSize = GDALGetDataTypeSize( eDataType ) / 8; + + CPLErr eErr; + CNCSError oError; + + if( bCrystalized ) + return CE_None; + + const char **paszBandDescriptions = (const char **) CPLMalloc(nBands * sizeof(char *)); + for (int i = 0; i < nBands; i++ ){ + paszBandDescriptions[i] = GetRasterBand(i+1)->GetDescription(); + } + + int bRGBColorSpace = ECWIsInputRGBColorSpace(this); + + eErr = oCompressor.Initialize( pszFilename, papszOptions, + nRasterXSize, nRasterYSize, nBands,paszBandDescriptions, bRGBColorSpace, + eDataType, + pszProjection, adfGeoTransform, + 0, NULL, + bIsJPEG2000, FALSE, NULL ); + + if( eErr == CE_None ) + bCrystalized = TRUE; + + nLoadedLine = -1; + pabyBILBuffer = (GByte *) CPLMalloc( nWordSize * nBands * nRasterXSize ); + + CPLFree(paszBandDescriptions); + + return eErr; +} + +/************************************************************************/ +/* FlushLine() */ +/************************************************************************/ + +CPLErr ECWWriteDataset::FlushLine() + +{ + int nWordSize = GDALGetDataTypeSize( eDataType ) / 8; + CPLErr eErr; + +/* -------------------------------------------------------------------- */ +/* Crystalize if not already done. */ +/* -------------------------------------------------------------------- */ + if( !bCrystalized ) + { + eErr = Crystalize(); + + if( eErr != CE_None ) + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Write out the currently loaded line. */ +/* -------------------------------------------------------------------- */ + if( nLoadedLine != -1 ) + { + + void **papOutputLine; + + papOutputLine = (void **) CPLMalloc(sizeof(void*) * nBands); + for( int i = 0; i < nBands; i++ ) + papOutputLine[i] = + (void *) (pabyBILBuffer + i * nWordSize * nRasterXSize); + + + eErr = oCompressor.WriteLineBIL( (UINT16) nBands, papOutputLine ); + CPLFree( papOutputLine ); + if (eErr!=CE_None){ + return eErr; + } + } + +/* -------------------------------------------------------------------- */ +/* Clear the buffer and increment the "current line" indicator. */ +/* -------------------------------------------------------------------- */ + memset( pabyBILBuffer, 0, nWordSize * nRasterXSize * nBands ); + nLoadedLine++; + + return CE_None; +} + +#ifdef OPTIMIZED_FOR_GDALWARP +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr ECWWriteDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) +{ + ECWWriteRasterBand* po4thBand = NULL; + IRasterIORequest* poIORequest = NULL; + + if( bOutOfOrderWriteOccured ) + return CE_Failure; + + if( eRWFlag == GF_Write && nBandCount == 3 && nBands == 4 ) + { + po4thBand = (ECWWriteRasterBand*)GetRasterBand(4); + poIORequest = po4thBand->poIORequest; + if( poIORequest != NULL ) + { + if( nXOff != poIORequest->nXOff || + nYOff != poIORequest->nYOff || + nXSize != poIORequest->nXSize || + nYSize != poIORequest->nYSize || + nBufXSize != poIORequest->nBufXSize || + nBufYSize != poIORequest->nBufYSize ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Out of order write"); + bOutOfOrderWriteOccured = TRUE; + return CE_Failure; + } + } + } + + int nDataTypeSize = GDALGetDataTypeSize(eDataType) / 8; + if( eRWFlag == GF_Write && nXOff == 0 && nXSize == nRasterXSize && + nBufXSize == nXSize && nBufYSize == nYSize && eBufType == eDataType && + (nBandCount == nBands || ( nBandCount == 3 && poIORequest != NULL && nBands == 4) ) && + nPixelSpace == nDataTypeSize && nLineSpace == nPixelSpace * nRasterXSize ) + { + GByte* pabyData = (GByte*)pData; + for(int iY = 0; iY < nYSize; iY ++) + { + for(int iBand = 0; iBand < nBandCount; iBand ++) + { + GetRasterBand(panBandMap[iBand])->WriteBlock(0, iY + nYOff, + pabyData + iY * nLineSpace + iBand * nBandSpace); + } + + if( poIORequest != NULL ) + { + po4thBand->WriteBlock(0, iY + nYOff, + poIORequest->pabyData + iY * nDataTypeSize * nXSize); + } + } + + if( poIORequest != NULL ) + { + delete poIORequest; + po4thBand->poIORequest = NULL; + } + + return CE_None; + } + else + return GDALDataset::IRasterIO(eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg); +} +#endif + +/************************************************************************/ +/* ==================================================================== */ +/* ECWWriteRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ECWWriteRasterBand() */ +/************************************************************************/ + +ECWWriteRasterBand::ECWWriteRasterBand( ECWWriteDataset *poDSIn, + int nBandIn ) + +{ + nBand = nBandIn; + poDS = poDSIn; + poGDS = poDSIn; + nBlockXSize = poDSIn->GetRasterXSize(); + nBlockYSize = 1; + eDataType = poDSIn->eDataType; + eInterp = GCI_Undefined; +#ifdef OPTIMIZED_FOR_GDALWARP + poIORequest = NULL; +#endif +} + +/************************************************************************/ +/* ~ECWWriteRasterBand() */ +/************************************************************************/ + +ECWWriteRasterBand::~ECWWriteRasterBand() + +{ +#ifdef OPTIMIZED_FOR_GDALWARP + delete poIORequest; +#endif +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr ECWWriteRasterBand::IReadBlock( CPL_UNUSED int nBlockX, + CPL_UNUSED int nBlockY, + void *pBuffer ) +{ + int nWordSize = GDALGetDataTypeSize( eDataType ) / 8; + + // We zero stuff out here, but we can't really read stuff from + // a write only stream. + + memset( pBuffer, 0, nBlockXSize * nWordSize ); + + return CE_None; +} + +#ifdef OPTIMIZED_FOR_GDALWARP +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr ECWWriteRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg) +{ + if( eRWFlag == GF_Write && nBand == 4 && poGDS->nBands == 4 && + poGDS->nPrevIRasterIOBand < 0 ) + { + /* Triggered when gdalwarp outputs an alpha band */ + /* It is called before GDALDatasetRasterIO() on the 3 first bands */ + if( poIORequest != NULL ) + return CE_Failure; + poIORequest = new IRasterIORequest( this, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace ); + return CE_None; + } + + poGDS->nPrevIRasterIOBand = nBand; + return GDALRasterBand::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace, psExtraArg ); +} +#endif + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr ECWWriteRasterBand::IWriteBlock( CPL_UNUSED int nBlockX, + int nBlockY, + void *pBuffer ) +{ + int nWordSize = GDALGetDataTypeSize( eDataType ) / 8; + CPLErr eErr; + + if( poGDS->bOutOfOrderWriteOccured ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Flush previous line if needed. */ +/* -------------------------------------------------------------------- */ + if( nBlockY == poGDS->nLoadedLine + 1 ) + { + eErr = poGDS->FlushLine(); + if( eErr != CE_None ) + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Blow a gasket if we have been asked to write something out */ +/* of order. */ +/* -------------------------------------------------------------------- */ + if( nBlockY != poGDS->nLoadedLine ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Apparent attempt to write to ECW non-sequentially.\n" + "Loaded line is %d, but %d of band %d was written to.", + poGDS->nLoadedLine, nBlockY, nBand ); + poGDS->bOutOfOrderWriteOccured = TRUE; + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Copy passed data into current line buffer. */ +/* -------------------------------------------------------------------- */ + memcpy( poGDS->pabyBILBuffer + (nBand-1) * nWordSize * nRasterXSize, + pBuffer, + nWordSize * nRasterXSize ); + + return CE_None; +} + +/************************************************************************/ +/* ECWCreateJPEG2000() */ +/************************************************************************/ + +GDALDataset * +ECWCreateJPEG2000(const char *pszFilename, int nXSize, int nYSize, int nBands, + GDALDataType eType, char **papszOptions ) + +{ + ECWInitialize(); + + return new ECWWriteDataset( pszFilename, nXSize, nYSize, nBands, + eType, papszOptions, TRUE ); +} + +/************************************************************************/ +/* ECWCreateECW() */ +/************************************************************************/ + +GDALDataset * +ECWCreateECW( const char *pszFilename, int nXSize, int nYSize, int nBands, + GDALDataType eType, char **papszOptions ) + +{ + ECWInitialize(); + + return new ECWWriteDataset( pszFilename, nXSize, nYSize, nBands, + eType, papszOptions, FALSE ); +} + +#endif /* def FRMT_ecw && def HAVE_COMPRESS */ diff --git a/bazaar/plugin/gdal/frmts/ecw/ecwdataset.cpp b/bazaar/plugin/gdal/frmts/ecw/ecwdataset.cpp new file mode 100644 index 000000000..ce1f81724 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ecw/ecwdataset.cpp @@ -0,0 +1,3525 @@ +/****************************************************************************** + * $Id: ecwdataset.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: GDAL + * Purpose: ECW (ERDAS Wavelet Compression Format) Driver + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_ecw.h" +#include "cpl_minixml.h" +#include "ogr_spatialref.h" +#include "ogr_api.h" +#include "ogr_geometry.h" + +CPL_CVSID("$Id: ecwdataset.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +#undef NOISY_DEBUG + +#ifdef FRMT_ecw + +static const unsigned char jpc_header[] = {0xff,0x4f}; +static const unsigned char jp2_header[] = + {0x00,0x00,0x00,0x0c,0x6a,0x50,0x20,0x20,0x0d,0x0a,0x87,0x0a}; + +static CPLMutex *hECWDatasetMutex = NULL; +static int bNCSInitialized = FALSE; + +void ECWInitialize( void ); + +extern "C" int CPL_DLL GDALIsInGlobalDestructor(void); + +#define BLOCK_SIZE 256 + +GDALDataset* ECWDatasetOpenJPEG2000(GDALOpenInfo* poOpenInfo); + +/************************************************************************/ +/* ECWReportError() */ +/************************************************************************/ + +void ECWReportError(CNCSError& oErr, const char* pszMsg) +{ +#if ECWSDK_VERSION<50 + char* pszErrorMessage = oErr.GetErrorMessage(); + CPLError( CE_Failure, CPLE_AppDefined, + "%s%s", pszMsg, pszErrorMessage ); + NCSFree(pszErrorMessage); +#else + CPLError( CE_Failure, CPLE_AppDefined, + "%s%s", pszMsg, NCSGetLastErrorText(oErr) ); +#endif +} + +/************************************************************************/ +/* ECWRasterBand() */ +/************************************************************************/ + +ECWRasterBand::ECWRasterBand( ECWDataset *poDS, int nBand, int iOverview, + char** papszOpenOptions ) + +{ + this->poDS = poDS; + poGDS = poDS; + + this->iOverview = iOverview; + this->nBand = nBand; + eDataType = poDS->eRasterDataType; + + nRasterXSize = poDS->GetRasterXSize() / ( 1 << (iOverview+1)); + nRasterYSize = poDS->GetRasterYSize() / ( 1 << (iOverview+1)); + + nBlockXSize = BLOCK_SIZE; + nBlockYSize = BLOCK_SIZE; + +/* -------------------------------------------------------------------- */ +/* Work out band color interpretation. */ +/* -------------------------------------------------------------------- */ + if( poDS->psFileInfo->eColorSpace == NCSCS_NONE ) + eBandInterp = GCI_Undefined; + else if( poDS->psFileInfo->eColorSpace == NCSCS_GREYSCALE ) + { + eBandInterp = GCI_GrayIndex; + //we could also have alpha band. + if ( strcmp(poDS->psFileInfo->pBands[nBand-1].szDesc, NCS_BANDDESC_AllOpacity) == 0 || + strcmp(poDS->psFileInfo->pBands[nBand-1].szDesc, NCS_BANDDESC_GreyscaleOpacity) ==0 ){ + eBandInterp = GCI_AlphaBand; + } + }else if (poDS->psFileInfo->eColorSpace == NCSCS_MULTIBAND ){ + eBandInterp = ECWGetColorInterpretationByName(poDS->psFileInfo->pBands[nBand-1].szDesc); + }else if (poDS->psFileInfo->eColorSpace == NCSCS_sRGB){ + eBandInterp = ECWGetColorInterpretationByName(poDS->psFileInfo->pBands[nBand-1].szDesc); + if( eBandInterp == GCI_Undefined ) + { + if( nBand == 1 ) + eBandInterp = GCI_RedBand; + else if( nBand == 2 ) + eBandInterp = GCI_GreenBand; + else if( nBand == 3 ) + eBandInterp = GCI_BlueBand; + else if (nBand == 4 ) + { + if (strcmp(poDS->psFileInfo->pBands[nBand-1].szDesc, NCS_BANDDESC_AllOpacity) == 0) + eBandInterp = GCI_AlphaBand; + else + eBandInterp = GCI_Undefined; + } + else + { + eBandInterp = GCI_Undefined; + } + } + } + else if( poDS->psFileInfo->eColorSpace == NCSCS_YCbCr ) + { + if( CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB","YES") )) + { + if( nBand == 1 ) + eBandInterp = GCI_RedBand; + else if( nBand == 2 ) + eBandInterp = GCI_GreenBand; + else if( nBand == 3 ) + eBandInterp = GCI_BlueBand; + else + eBandInterp = GCI_Undefined; + } + else + { + if( nBand == 1 ) + eBandInterp = GCI_YCbCr_YBand; + else if( nBand == 2 ) + eBandInterp = GCI_YCbCr_CbBand; + else if( nBand == 3 ) + eBandInterp = GCI_YCbCr_CrBand; + else + eBandInterp = GCI_Undefined; + } + } + else + eBandInterp = GCI_Undefined; + +/* -------------------------------------------------------------------- */ +/* If this is the base level, create a set of overviews. */ +/* -------------------------------------------------------------------- */ + if( iOverview == -1 ) + { + int i; + for( i = 0; + nRasterXSize / (1 << (i+1)) > 128 + && nRasterYSize / (1 << (i+1)) > 128; + i++ ) + { + apoOverviews.push_back( new ECWRasterBand( poDS, nBand, i, papszOpenOptions ) ); + } + } + + bPromoteTo8Bit = + poDS->psFileInfo->nBands == 4 && nBand == 4 && + poDS->psFileInfo->pBands[0].nBits == 8 && + poDS->psFileInfo->pBands[1].nBits == 8 && + poDS->psFileInfo->pBands[2].nBits == 8 && + poDS->psFileInfo->pBands[3].nBits == 1 && + eBandInterp == GCI_AlphaBand && + CSLFetchBoolean(papszOpenOptions, "1BIT_ALPHA_PROMOTION", + CSLTestBoolean(CPLGetConfigOption("GDAL_ECW_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES"))); + if( bPromoteTo8Bit ) + CPLDebug("ECW", "Fourth (alpha) band is promoted from 1 bit to 8 bit"); + + if( (poDS->psFileInfo->pBands[nBand-1].nBits % 8) != 0 && !bPromoteTo8Bit ) + SetMetadataItem("NBITS", + CPLString().Printf("%d",poDS->psFileInfo->pBands[nBand-1].nBits), + "IMAGE_STRUCTURE" ); + + SetDescription(poDS->psFileInfo->pBands[nBand-1].szDesc); +} + +/************************************************************************/ +/* ~ECWRasterBand() */ +/************************************************************************/ + +ECWRasterBand::~ECWRasterBand() + +{ + FlushCache(); + + while( apoOverviews.size() > 0 ) + { + delete apoOverviews.back(); + apoOverviews.pop_back(); + } +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *ECWRasterBand::GetOverview( int iOverview ) + +{ + if( iOverview >= 0 && iOverview < (int) apoOverviews.size() ) + return apoOverviews[iOverview]; + else + return NULL; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp ECWRasterBand::GetColorInterpretation() + +{ + return eBandInterp; +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/* */ +/* This would normally just be used by folks using the ECW code */ +/* to read JP2 streams in other formats (such as NITF) and */ +/* providing their own color interpretation regardless of what */ +/* ECW might think the stream itself says. */ +/************************************************************************/ + +CPLErr ECWRasterBand::SetColorInterpretation( GDALColorInterp eNewInterp ) + +{ + eBandInterp = eNewInterp; + + return CE_None; +} + +/************************************************************************/ +/* AdviseRead() */ +/************************************************************************/ + +CPLErr ECWRasterBand::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + char **papszOptions ) +{ + int nResFactor = 1 << (iOverview+1); + + return poGDS->AdviseRead( nXOff * nResFactor, + nYOff * nResFactor, + nXSize * nResFactor, + nYSize * nResFactor, + nBufXSize, nBufYSize, eDT, + 1, &nBand, papszOptions ); +} + +//statistics support: +#if ECWSDK_VERSION >= 50 + +/************************************************************************/ +/* GetDefaultHistogram() */ +/************************************************************************/ + +CPLErr ECWRasterBand::GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig ** ppanHistogram, + int bForce, + GDALProgressFunc f, void *pProgressData) +{ + int bForceCoalesced = bForce; + // If file version is smaller than 3, there will be no statistics in the file. But if it is version 3 or higher we don't want underlying implementation to compute histogram + // so we set bForceCoalesced to FALSE. + if (poGDS->psFileInfo->nFormatVersion >= 3){ + bForceCoalesced = FALSE; + } + // We check if we have PAM histogram. If we have them we return them. This will allow to override statistics stored in the file. + CPLErr pamError = GDALPamRasterBand::GetDefaultHistogram(pdfMin, pdfMax, pnBuckets, ppanHistogram, bForceCoalesced, f, pProgressData); + if ( pamError == CE_None || poGDS->psFileInfo->nFormatVersion<3 || eBandInterp == GCI_AlphaBand){ + return pamError; + } + + NCS::CError error = poGDS->StatisticsEnsureInitialized(); + if (!error.Success()){ + CPLError( CE_Warning, CPLE_AppDefined, + "ECWRDataset::StatisticsEnsureInitialized failed in ECWRasterBand::GetDefaultHistogram. " ); + return CE_Failure; + } + GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount); + bool bHistogramFromFile = false; + if ( poGDS->pStatistics != NULL ){ + NCSBandStats& bandStats = poGDS->pStatistics->BandsStats[nStatsBandIndex]; + if ( bandStats.Histogram != NULL && bandStats.nHistBucketCount > 0 ){ + *pnBuckets = bandStats.nHistBucketCount; + *ppanHistogram = (GUIntBig *)VSIMalloc(bandStats.nHistBucketCount *sizeof(GUIntBig)); + for (size_t i = 0; i < bandStats.nHistBucketCount; i++){ + (*ppanHistogram)[i] = (GUIntBig) bandStats.Histogram[i]; + } + //JTO: this is not perfect as You can't tell who wrote the histogram !!! + //It will offset it unnecesarilly for files with hists not modified by GDAL. + double dfHalfBucket = (bandStats.fMaxHist - bandStats.fMinHist) / (2 * (*pnBuckets - 1)); + if ( pdfMin != NULL ){ + *pdfMin = bandStats.fMinHist - dfHalfBucket; + } + if ( pdfMax != NULL ){ + *pdfMax = bandStats.fMaxHist + dfHalfBucket; + } + bHistogramFromFile = true; + }else{ + bHistogramFromFile = false; + } + }else{ + bHistogramFromFile = false; + } + + if (!bHistogramFromFile ){ + if (bForce == TRUE){ + //compute. Save. + pamError = GDALPamRasterBand::GetDefaultHistogram(pdfMin, pdfMax, pnBuckets, ppanHistogram, TRUE, f,pProgressData); + if (pamError == CE_None){ + CPLErr error = SetDefaultHistogram(*pdfMin, *pdfMax, *pnBuckets, *ppanHistogram); + if (error != CE_None){ + //Histogram is there but we failed to save it back to file. + CPLError (CE_Warning, CPLE_AppDefined, + "SetDefaultHistogram failed in ECWRasterBand::GetDefaultHistogram. Histogram might not be saved in .ecw file." ); + } + return CE_None; + }else{ + //Something went wrong during histogram computation. + return pamError; + } + }else{ + //No histogram, no forced computation. + return CE_Warning; + } + }else { + //Statistics were already there and were used. + return CE_None; + } +} + +/************************************************************************/ +/* SetDefaultHistogram() */ +/************************************************************************/ + +CPLErr ECWRasterBand::SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram ) +{ + //Only version 3 supports saving statistics. + if (poGDS->psFileInfo->nFormatVersion < 3 || eBandInterp == GCI_AlphaBand){ + return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram); + } + + //determine if there are statistics in PAM file. + double dummy; + int dummy_i; + GUIntBig *dummy_histogram; + bool hasPAMDefaultHistogram = GDALPamRasterBand::GetDefaultHistogram(&dummy, &dummy, &dummy_i, &dummy_histogram, FALSE, NULL, NULL) == CE_None; + if (hasPAMDefaultHistogram){ + VSIFree(dummy_histogram); + } + + //ECW SDK ignores statistics for opacity bands. So we need to compute number of bands without opacity. + GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount); + UINT32 bucketCounts[256]; + std::fill_n(bucketCounts, nStatsBandCount, 0); + bucketCounts[nStatsBandIndex] = nBuckets; + + NCS::CError error = poGDS->StatisticsEnsureInitialized(); + if (!error.Success()){ + CPLError( CE_Warning, CPLE_AppDefined, + "ECWRDataset::StatisticsEnsureInitialized failed in ECWRasterBand::SetDefaultHistogram. Default histogram will be written to PAM. " ); + return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram); + } + + NCSFileStatistics *pStatistics = poGDS->pStatistics; + + if (pStatistics == NULL){ + error = NCSEcwInitStatistics(&pStatistics, nStatsBandCount, bucketCounts); + poGDS->bStatisticsDirty = TRUE; + poGDS->pStatistics = pStatistics; + if (!error.Success()){ + CPLError( CE_Warning, CPLE_AppDefined, + "NCSEcwInitStatistics failed in ECWRasterBand::SetDefaultHistogram." ); + return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram); + } + //no error statistics properly initialized but there were no statistics previously. + }else{ + //is there a room for our band already? + //This should account for following cases: + //1. Existing histogram (for this or different band) has smaller bucket count. + //2. There is no existing histogram but statistics are set for one or more bands (pStatistics->nHistBucketCounts is zero). + if ((int)pStatistics->BandsStats[nStatsBandIndex].nHistBucketCount != nBuckets){ + //no. There is no room. We need more! + NCSFileStatistics *pNewStatistics = NULL; + for (size_t i=0;inNumberOfBands;i++){ + bucketCounts[i] = pStatistics->BandsStats[i].nHistBucketCount; + } + bucketCounts[nStatsBandIndex] = nBuckets; + if (nBuckets < (int)pStatistics->BandsStats[nStatsBandIndex].nHistBucketCount){ + pStatistics->BandsStats[nStatsBandIndex].nHistBucketCount = nBuckets; + } + error = NCSEcwInitStatistics(&pNewStatistics, nStatsBandCount, bucketCounts); + if (!error.Success()){ + CPLError( CE_Warning, CPLE_AppDefined, + "NCSEcwInitStatistics failed in ECWRasterBand::SetDefaultHistogram (realocate)." ); + return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram); + } + //we need to copy existing statistics. + error = NCSEcwCopyStatistics(&pNewStatistics, pStatistics); + if (!error.Success()){ + CPLError( CE_Warning, CPLE_AppDefined, + "NCSEcwCopyStatistics failed in ECWRasterBand::SetDefaultHistogram." ); + NCSEcwFreeStatistics(pNewStatistics); + return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram); + } + pNewStatistics->nNumberOfBands = nStatsBandCount; + NCSEcwFreeStatistics(pStatistics); + pStatistics = pNewStatistics; + poGDS->pStatistics = pStatistics; + poGDS->bStatisticsDirty = TRUE; + } + } + + //at this point we have allocated statistics structure. + double dfHalfBucket = (dfMax - dfMin) / (2 * nBuckets); + pStatistics->BandsStats[nStatsBandIndex].fMinHist = (IEEE4) (dfMin + dfHalfBucket); + pStatistics->BandsStats[nStatsBandIndex].fMaxHist = (IEEE4) (dfMax - dfHalfBucket); + for (int i=0;iBandsStats[nStatsBandIndex].Histogram[i] = (UINT64)panHistogram[i]; + } + + if (hasPAMDefaultHistogram){ + CPLError( CE_Debug, CPLE_AppDefined, + "PAM default histogram will be overwritten." ); + return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram); + } + return CE_None; +} + +/************************************************************************/ +/* GetBandIndexAndCountForStatistics() */ +/************************************************************************/ + +void ECWRasterBand::GetBandIndexAndCountForStatistics(int &bandIndex, int &bandCount){ + bandIndex = nBand-1; + bandCount = poGDS->nBands; + for (int i=0;inBands;i++){ + if (poDS->GetRasterBand(i+1)->GetColorInterpretation() == GCI_AlphaBand){ + bandCount--; + if ( i < nBand-1 ){ + bandIndex--; + } + } + + } +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double ECWRasterBand::GetMinimum(int* pbSuccess) +{ + if( poGDS->psFileInfo->nFormatVersion >= 3 ) + { + NCS::CError error = poGDS->StatisticsEnsureInitialized(); + if ( error.Success() ) + { + GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount); + if ( poGDS->pStatistics != NULL ) + { + NCSBandStats& bandStats = poGDS->pStatistics->BandsStats[nStatsBandIndex]; + if ( bandStats.fMinVal == bandStats.fMinVal ) + { + if( pbSuccess ) + *pbSuccess = TRUE; + return bandStats.fMinVal; + } + } + } + } + return GDALPamRasterBand::GetMinimum(pbSuccess); +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double ECWRasterBand::GetMaximum(int* pbSuccess) +{ + if( poGDS->psFileInfo->nFormatVersion >= 3 ) + { + NCS::CError error = poGDS->StatisticsEnsureInitialized(); + if ( error.Success() ) + { + GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount); + if ( poGDS->pStatistics != NULL ) + { + NCSBandStats& bandStats = poGDS->pStatistics->BandsStats[nStatsBandIndex]; + if ( bandStats.fMaxVal == bandStats.fMaxVal ) + { + if( pbSuccess ) + *pbSuccess = TRUE; + return bandStats.fMaxVal; + } + } + } + } + return GDALPamRasterBand::GetMaximum(pbSuccess); +} +/************************************************************************/ +/* GetStatistics() */ +/************************************************************************/ + +CPLErr ECWRasterBand::GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *padfStdDev ) +{ + int bForceCoalesced = bForce; + // If file version is smaller than 3, there will be no statistics in the file. But if it is version 3 or higher we don't want underlying implementation to compute histogram + // so we set bForceCoalesced to FALSE. + if (poGDS->psFileInfo->nFormatVersion >= 3){ + bForceCoalesced = FALSE; + } + // We check if we have PAM histogram. If we have them we return them. This will allow to override statistics stored in the file. + CPLErr pamError = GDALPamRasterBand::GetStatistics(bApproxOK, bForceCoalesced, pdfMin, pdfMax, pdfMean, padfStdDev); + if ( pamError == CE_None || poGDS->psFileInfo->nFormatVersion<3 || eBandInterp == GCI_AlphaBand){ + return pamError; + } + + NCS::CError error = poGDS->StatisticsEnsureInitialized(); + if (!error.Success()){ + CPLError( CE_Failure, CPLE_AppDefined, + "ECWRDataset::StatisticsEnsureInitialized failed in ECWRasterBand::GetStatistic. " ); + return CE_Failure; + } + GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount); + bool bStatisticsFromFile = false; + + if ( poGDS->pStatistics != NULL ) + { + bStatisticsFromFile = true; + NCSBandStats& bandStats = poGDS->pStatistics->BandsStats[nStatsBandIndex]; + if ( pdfMin != NULL && bandStats.fMinVal == bandStats.fMinVal){ + *pdfMin = bandStats.fMinVal; + }else{ + bStatisticsFromFile = false; + } + if ( pdfMax != NULL && bandStats.fMaxVal == bandStats.fMaxVal){ + *pdfMax = bandStats.fMaxVal; + }else{ + bStatisticsFromFile = false; + } + if ( pdfMean != NULL && bandStats.fMeanVal == bandStats.fMeanVal){ + *pdfMean = bandStats.fMeanVal; + }else{ + bStatisticsFromFile = false; + } + if ( padfStdDev != NULL && bandStats.fStandardDev == bandStats.fStandardDev){ + *padfStdDev = bandStats.fStandardDev; + }else{ + bStatisticsFromFile = false; + } + if (bStatisticsFromFile) return CE_None; + } + //no required statistics. + if (!bStatisticsFromFile && bForce == TRUE){ + double dfMin, dfMax, dfMean,dfStdDev; + pamError = GDALPamRasterBand::GetStatistics(bApproxOK, TRUE, + &dfMin, + &dfMax, + &dfMean, + &dfStdDev); + if (pdfMin!=NULL) { + *pdfMin = dfMin; + } + if (pdfMax !=NULL){ + *pdfMax = dfMax; + } + if (pdfMean !=NULL){ + *pdfMean = dfMean; + } + if (padfStdDev!=NULL){ + *padfStdDev = dfStdDev; + } + if ( pamError == CE_None){ + CPLErr err = SetStatistics(dfMin,dfMax,dfMean,dfStdDev); + if (err !=CE_None){ + CPLError (CE_Warning, CPLE_AppDefined, + "SetStatistics failed in ECWRasterBand::GetDefaultHistogram. Statistics might not be saved in .ecw file." ); + } + return CE_None; + }else{ + //whatever happened we return. + return pamError; + } + }else{ + //no statistics and we are not forced to return. + return CE_Warning; + } +} + +/************************************************************************/ +/* SetStatistics() */ +/************************************************************************/ + +CPLErr ECWRasterBand::SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ){ + if (poGDS->psFileInfo->nFormatVersion < 3 || eBandInterp == GCI_AlphaBand){ + return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev); + } + double dummy; + bool hasPAMStatistics = GDALPamRasterBand::GetStatistics(TRUE, FALSE, &dummy, &dummy, &dummy, &dummy) == CE_None; + + NCS::CError error = poGDS->StatisticsEnsureInitialized(); + if (!error.Success()){ + CPLError( CE_Warning, CPLE_AppDefined, + "ECWRDataset::StatisticsEnsureInitialized failed in ECWRasterBand::SetStatistic. Statistics will be written to PAM. " ); + return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev); + } + GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount); + if (poGDS->pStatistics == NULL){ + error = NCSEcwInitStatistics(&poGDS->pStatistics, nStatsBandCount, NULL); + if (!error.Success()){ + CPLError( CE_Warning, CPLE_AppDefined, + "NCSEcwInitStatistics failed in ECWRasterBand::SetStatistic. Statistics will be written to PAM." ); + return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev); + } + } + + poGDS->pStatistics->BandsStats[nStatsBandIndex].fMinVal = (IEEE4) dfMin; + poGDS->pStatistics->BandsStats[nStatsBandIndex].fMaxVal = (IEEE4)dfMax; + poGDS->pStatistics->BandsStats[nStatsBandIndex].fMeanVal = (IEEE4)dfMean; + poGDS->pStatistics->BandsStats[nStatsBandIndex].fStandardDev = (IEEE4)dfStdDev; + poGDS->bStatisticsDirty = TRUE; + //if we have PAM statistics we need to save them as well. Better option would be to remove them from PAM file but I don't know how to do that without messing in PAM internals. + if ( hasPAMStatistics ){ + CPLError( CE_Debug, CPLE_AppDefined, + "PAM statistics will be overwritten." ); + return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev); + } + return CE_None; + +} +#endif + +//#if !defined(SDK_CAN_DO_SUPERSAMPLING) +/************************************************************************/ +/* OldIRasterIO() */ +/************************************************************************/ + +/* This implementation of IRasterIO(), derived from the one of GDAL 1.9 */ +/* and older versions, is meant at making over-sampling */ +/* work with ECW SDK 3.3. Newer versions of the SDK can do super-sampling in their */ +/* SetView() call. */ + +CPLErr ECWRasterBand::OldIRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + int iBand, bDirect; + GByte *pabyWorkBuffer = NULL; + int nResFactor = 1 << (iOverview+1); + + nXOff *= nResFactor; + nYOff *= nResFactor; + nXSize *= nResFactor; + nYSize *= nResFactor; + +/* -------------------------------------------------------------------- */ +/* Try to do it based on existing "advised" access. */ +/* -------------------------------------------------------------------- */ + int nRet = poGDS->TryWinRasterIO( eRWFlag, + nXOff, nYOff, + nXSize, nYSize, + (GByte *) pData, nBufXSize, nBufYSize, + eBufType, 1, &nBand, + nPixelSpace, nLineSpace, 0 , psExtraArg); + if( nRet == TRUE ) + return CE_None; + else if( nRet < 0 ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* The ECW SDK doesn't supersample, so adjust for this case. */ +/* -------------------------------------------------------------------- */ + + int nNewXSize = nBufXSize, nNewYSize = nBufYSize; + + if ( nXSize < nBufXSize ) + nNewXSize = nXSize; + + if ( nYSize < nBufYSize ) + nNewYSize = nYSize; + +/* -------------------------------------------------------------------- */ +/* Can we perform direct loads, or must we load into a working */ +/* buffer, and transform? */ +/* -------------------------------------------------------------------- */ + int nRawPixelSize = GDALGetDataTypeSize(poGDS->eRasterDataType) / 8; + + bDirect = nPixelSpace == 1 && eBufType == GDT_Byte + && nNewXSize == nBufXSize && nNewYSize == nBufYSize; + if( !bDirect ) + pabyWorkBuffer = (GByte *) CPLMalloc(nNewXSize * nRawPixelSize); + +/* -------------------------------------------------------------------- */ +/* Establish access at the desired resolution. */ +/* -------------------------------------------------------------------- */ + poGDS->CleanupWindow(); + + iBand = nBand-1; + poGDS->nBandIndexToPromoteTo8Bit = ( bPromoteTo8Bit ) ? 0 : -1; + // TODO: Fix writable strings issue. + CNCSError oErr = poGDS->poFileView->SetView( 1, (unsigned int *) (&iBand), + nXOff, nYOff, + nXOff + nXSize - 1, + nYOff + nYSize - 1, + nNewXSize, nNewYSize ); + if( oErr.GetErrorNumber() != NCS_SUCCESS ) + { + CPLFree( pabyWorkBuffer ); + ECWReportError(oErr); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Read back one scanline at a time, till request is satisfied. */ +/* Supersampling is not supported by the ECW API, so we will do */ +/* it ourselves. */ +/* -------------------------------------------------------------------- */ + double dfSrcYInc = (double)nNewYSize / nBufYSize; + double dfSrcXInc = (double)nNewXSize / nBufXSize; + int iSrcLine, iDstLine; + CPLErr eErr = CE_None; + + for( iSrcLine = 0, iDstLine = 0; iDstLine < nBufYSize; iDstLine++ ) + { + NCSEcwReadStatus eRStatus; + GPtrDiff_t iDstLineOff = iDstLine * (GPtrDiff_t)nLineSpace; + unsigned char *pabySrcBuf; + + if( bDirect ) + pabySrcBuf = ((GByte *)pData) + iDstLineOff; + else + pabySrcBuf = pabyWorkBuffer; + + if ( nNewYSize == nBufYSize || iSrcLine == (int)(iDstLine * dfSrcYInc) ) + { + eRStatus = poGDS->poFileView->ReadLineBIL( + poGDS->eNCSRequestDataType, 1, (void **) &pabySrcBuf ); + + if( eRStatus != NCSECW_READ_OK ) + { + CPLDebug( "ECW", "ReadLineBIL status=%d", (int) eRStatus ); + CPLError( CE_Failure, CPLE_AppDefined, + "NCScbmReadViewLineBIL failed." ); + eErr = CE_Failure; + break; + } + + if( bPromoteTo8Bit ) + { + for ( int iX = 0; iX < nNewXSize; iX++ ) + { + pabySrcBuf[iX] *= 255; + } + } + + if( !bDirect ) + { + if ( nNewXSize == nBufXSize ) + { + GDALCopyWords( pabyWorkBuffer, poGDS->eRasterDataType, + nRawPixelSize, + ((GByte *)pData) + iDstLine * nLineSpace, + eBufType, (int)nPixelSpace, nBufXSize ); + } + else + { + int iPixel; + + for ( iPixel = 0; iPixel < nBufXSize; iPixel++ ) + { + GDALCopyWords( pabyWorkBuffer + + nRawPixelSize*((int)(iPixel*dfSrcXInc)), + poGDS->eRasterDataType, nRawPixelSize, + (GByte *)pData + iDstLineOff + + iPixel * nPixelSpace, + eBufType, (int)nPixelSpace, 1 ); + } + } + } + + iSrcLine++; + } + else + { + // Just copy the previous line in this case + GDALCopyWords( (GByte *)pData + (iDstLineOff - nLineSpace), + eBufType, (int)nPixelSpace, + (GByte *)pData + iDstLineOff, + eBufType, (int)nPixelSpace, nBufXSize ); + } + + if( psExtraArg->pfnProgress != NULL && + !psExtraArg->pfnProgress(1.0 * (iDstLine + 1) / nBufYSize, "", + psExtraArg->pProgressData) ) + { + eErr = CE_Failure; + break; + } + } + + CPLFree( pabyWorkBuffer ); + + return eErr; +} +//#endif !defined(SDK_CAN_DO_SUPERSAMPLING) + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr ECWRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ + if( eRWFlag == GF_Write ) + return CE_Failure; + + /* -------------------------------------------------------------------- */ + /* Default line and pixel spacing if needed. */ + /* -------------------------------------------------------------------- */ + if ( nPixelSpace == 0 ) + nPixelSpace = GDALGetDataTypeSize( eBufType ) / 8; + + if ( nLineSpace == 0 ) + nLineSpace = nPixelSpace * nBufXSize; + + CPLDebug( "ECWRasterBand", + "RasterIO(nBand=%d,iOverview=%d,nXOff=%d,nYOff=%d,nXSize=%d,nYSize=%d -> %dx%d)", + nBand, iOverview, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize ); + +#if !defined(SDK_CAN_DO_SUPERSAMPLING) + if( poGDS->bUseOldBandRasterIOImplementation ) + { + return OldIRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + } + +#endif + + int nResFactor = 1 << (iOverview+1); + + return poGDS->IRasterIO(eRWFlag, + nXOff * nResFactor, + nYOff * nResFactor, + (nXSize == nRasterXSize) ? poGDS->nRasterXSize : nXSize * nResFactor, + (nYSize == nRasterYSize) ? poGDS->nRasterYSize : nYSize * nResFactor, + pData, nBufXSize, nBufYSize, + eBufType, 1, &nBand, + nPixelSpace, nLineSpace, nLineSpace*nBufYSize, psExtraArg); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr ECWRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, void * pImage ) + +{ + int nXOff = nBlockXOff * nBlockXSize, + nYOff = nBlockYOff * nBlockYSize, + nXSize = nBlockXSize, + nYSize = nBlockYSize; + + if( nXOff + nXSize > nRasterXSize ) + nXSize = nRasterXSize - nXOff; + if( nYOff + nYSize > nRasterYSize ) + nYSize = nRasterYSize - nYOff; + + int nPixelSpace = GDALGetDataTypeSize(eDataType) / 8; + int nLineSpace = nPixelSpace * nBlockXSize; + + GDALRasterIOExtraArg sExtraArg; + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + + return IRasterIO( GF_Read, + nXOff, nYOff, nXSize, nYSize, + pImage, nXSize, nYSize, + eDataType, nPixelSpace, nLineSpace, &sExtraArg ); +} + +/************************************************************************/ +/* ==================================================================== */ +/* ECWDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* ECWDataset() */ +/************************************************************************/ + +ECWDataset::ECWDataset(int bIsJPEG2000) + +{ + this->bIsJPEG2000 = bIsJPEG2000; + bUsingCustomStream = FALSE; + poFileView = NULL; + bWinActive = FALSE; + panWinBandList = NULL; + eRasterDataType = GDT_Byte; + papszGMLMetadata = NULL; + + bHdrDirty = FALSE; + bGeoTransformChanged = FALSE; + bProjectionChanged = FALSE; + bProjCodeChanged = FALSE; + bDatumCodeChanged = FALSE; + bUnitsCodeChanged = FALSE; + + bUseOldBandRasterIOImplementation = FALSE; +#if ECWSDK_VERSION>=50 + + pStatistics = NULL; + bStatisticsDirty = FALSE; + bStatisticsInitialized = FALSE; + bFileMetaDataDirty = FALSE; + +#endif + + sCachedMultiBandIO.bEnabled = FALSE; + sCachedMultiBandIO.nBandsTried = 0; + sCachedMultiBandIO.nXOff = 0; + sCachedMultiBandIO.nYOff = 0; + sCachedMultiBandIO.nXSize = 0; + sCachedMultiBandIO.nYSize = 0; + sCachedMultiBandIO.nBufXSize = 0; + sCachedMultiBandIO.nBufYSize = 0; + sCachedMultiBandIO.eBufType = GDT_Unknown; + sCachedMultiBandIO.pabyData = NULL; + + bPreventCopyingSomeMetadata = FALSE; + + nBandIndexToPromoteTo8Bit = -1; + + poDriver = (GDALDriver*) GDALGetDriverByName( bIsJPEG2000 ? "JP2ECW" : "ECW" ); +} + +/************************************************************************/ +/* ~ECWDataset() */ +/************************************************************************/ + +ECWDataset::~ECWDataset() + +{ + FlushCache(); + CleanupWindow(); + +#if ECWSDK_VERSION>=50 + NCSFileMetaData* pFileMetaDataCopy = NULL; + if( bFileMetaDataDirty ) + { + NCSCopyMetaData(&pFileMetaDataCopy, psFileInfo->pFileMetaData); + } +#endif + +/* -------------------------------------------------------------------- */ +/* Release / dereference iostream. */ +/* -------------------------------------------------------------------- */ + // The underlying iostream of the CNCSJP2FileView (poFileView) object may + // also be the underlying iostream of other CNCSJP2FileView (poFileView) + // objects. Consequently, when we delete the CNCSJP2FileView (poFileView) + // object, we must decrement the nFileViewCount attribute of the underlying + // VSIIOStream object, and only delete the VSIIOStream object when + // nFileViewCount is equal to zero. + + CPLMutexHolder oHolder( &hECWDatasetMutex ); + + // bInGDALGlobalDestructor is set to TRUE by gdaldllmain.cpp/GDALDestroy() so as + // to avoid an issue with the ECW SDK 3.3 where the destructor of CNCSJP2File::CNCSJP2FileVector CNCSJP2File::sm_Files; + // static resource allocated in NCJP2File.cpp can be called before GDALDestroy(), causing + // ECW SDK resources ( CNCSJP2File files ) to be closed before we get here. + // + // We also have an issue with ECW SDK 5.0 and ECW files on Linux when + // running a multi-threaded test under Java if there's still an ECW dataset + // not explicitly closed at process termination. + /* #0 0x00007fffb26e7a80 in NCSAtomicAdd64 () from /home/even/ecwjp2_sdk/redistributable/x64/libNCSEcw.so + #1 0x00007fffb2aa7684 in NCS::SDK::CBuffer2D::Free() () from /home/even/ecwjp2_sdk/redistributable/x64/libNCSEcw.so + #2 0x00007fffb2aa7727 in NCS::SDK::CBuffer2D::~CBuffer2D() () from /home/even/ecwjp2_sdk/redistributable/x64/libNCSEcw.so + #3 0x00007fffb29aa7be in NCS::ECW::CReader::~CReader() () from /home/even/ecwjp2_sdk/redistributable/x64/libNCSEcw.so + #4 0x00007fffb29aa819 in NCS::ECW::CReader::~CReader() () from /home/even/ecwjp2_sdk/redistributable/x64/libNCSEcw.so + #5 0x00007fffb291fd3a in NCS::CView::Close(bool) () from /home/even/ecwjp2_sdk/redistributable/x64/libNCSEcw.so + #6 0x00007fffb2927529 in NCS::CView::~CView() () from /home/even/ecwjp2_sdk/redistributable/x64/libNCSEcw.so + #7 0x00007fffb29277f9 in NCS::CView::~CView() () from /home/even/ecwjp2_sdk/redistributable/x64/libNCSEcw.so + #8 0x00007fffb71a9a53 in ECWDataset::~ECWDataset (this=0x7fff942cce10, __in_chrg=) at ecwdataset.cpp:1003 + #9 0x00007fffb71a9cca in ECWDataset::~ECWDataset (this=0x7fff942cce10, __in_chrg=) at ecwdataset.cpp:1039 + #10 0x00007fffb7551f98 in GDALDriverManager::~GDALDriverManager (this=0x7ffff01981a0, __in_chrg=) at gdaldrivermanager.cpp:196 + #11 0x00007fffb7552140 in GDALDriverManager::~GDALDriverManager (this=0x7ffff01981a0, __in_chrg=) at gdaldrivermanager.cpp:288 + #12 0x00007fffb7552e18 in GDALDestroyDriverManager () at gdaldrivermanager.cpp:824 + #13 0x00007fffb7551c61 in GDALDestroy () at gdaldllmain.cpp:80 + #14 0x00007ffff7de990e in _dl_fini () at dl-fini.c:254 + */ + // Not replicable with similar test in C++, but this might be just a matter of luck related + // to the order in which the libraries are unloaded, so just don't try + // to delete poFileView from the GDAL destructor. + if( poFileView != NULL && !GDALIsInGlobalDestructor() ) + { + VSIIOStream *poUnderlyingIOStream = (VSIIOStream *)NULL; + + poUnderlyingIOStream = ((VSIIOStream *)(poFileView->GetStream())); + delete poFileView; + + if( bUsingCustomStream ) + { + if( --poUnderlyingIOStream->nFileViewCount == 0 ) + delete poUnderlyingIOStream; + } + } + + /* WriteHeader() must be called after closing the file handle to work */ + /* on Windows */ + if( bHdrDirty ) + WriteHeader(); +#if ECWSDK_VERSION>=50 + if (bStatisticsDirty){ + StatisticsWrite(); + } + CleanupStatistics(); + + if( bFileMetaDataDirty ) + { + WriteFileMetaData(pFileMetaDataCopy); + NCSFreeMetaData(pFileMetaDataCopy); + } +#endif + + CSLDestroy( papszGMLMetadata ); + + CPLFree(sCachedMultiBandIO.pabyData); +} + +#if ECWSDK_VERSION>=50 + +/************************************************************************/ +/* StatisticsEnsureInitialized() */ +/************************************************************************/ + +NCS::CError ECWDataset::StatisticsEnsureInitialized(){ + if (bStatisticsInitialized == TRUE){ + return NCS_SUCCESS; + } + + NCS::CError error = poFileView->GetClientStatistics(&pStatistics); + if (error.Success()){ + bStatisticsInitialized = TRUE; + } + return error; +} + +/************************************************************************/ +/* StatisticsWrite() */ +/************************************************************************/ + +NCS::CError ECWDataset::StatisticsWrite() +{ + CPLDebug("ECW", "In StatisticsWrite()"); + NCSFileView* view = NCSEcwEditOpen( GetDescription() ); + NCS::CError error; + if ( view != NULL ){ + error = NCSEcwEditSetStatistics(view, pStatistics); + if (error.Success()){ + error = NCSEcwEditFlushAll(view); + if (error.Success()){ + error = NCSEcwEditClose(view); + } + } + } + + bStatisticsDirty = FALSE; + + return error; + +} + +/************************************************************************/ +/* CleanupStatistics() */ +/************************************************************************/ + +void ECWDataset::CleanupStatistics(){ + if (bStatisticsInitialized == TRUE && pStatistics !=NULL){ + NCSEcwFreeStatistics(pStatistics); + } +} + +#endif // #if ECWSDK_VERSION>=50 + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr ECWDataset::SetGeoTransform( double * padfGeoTransform ) +{ + if ( bIsJPEG2000 || eAccess == GA_ReadOnly ) + return GDALPamDataset::SetGeoTransform(padfGeoTransform); + + if ( !bGeoTransformValid || + adfGeoTransform[0] != padfGeoTransform[0] || + adfGeoTransform[1] != padfGeoTransform[1] || + adfGeoTransform[2] != padfGeoTransform[2] || + adfGeoTransform[3] != padfGeoTransform[3] || + adfGeoTransform[4] != padfGeoTransform[4] || + adfGeoTransform[5] != padfGeoTransform[5] ) + { + memcpy(adfGeoTransform, padfGeoTransform, 6 * sizeof(double)); + bGeoTransformValid = TRUE; + bHdrDirty = TRUE; + bGeoTransformChanged = TRUE; + } + + return CE_None; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr ECWDataset::SetProjection( const char* pszProjectionIn ) +{ + if ( bIsJPEG2000 || eAccess == GA_ReadOnly ) + return GDALPamDataset::SetProjection(pszProjectionIn); + + if ( !( (pszProjection == NULL && pszProjectionIn == NULL) || + (pszProjection != NULL && pszProjectionIn != NULL && + strcmp(pszProjection, pszProjectionIn) == 0) ) ) + { + CPLFree(pszProjection); + pszProjection = pszProjectionIn ? CPLStrdup(pszProjectionIn) : NULL; + bHdrDirty = TRUE; + bProjectionChanged = TRUE; + } + + return CE_None; +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr ECWDataset::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) +{ + if ( !bIsJPEG2000 && eAccess == GA_Update && + (pszDomain == NULL || EQUAL(pszDomain, "") || + (pszDomain != NULL && EQUAL(pszDomain, "ECW"))) && + pszName != NULL && + (strcmp(pszName, "PROJ") == 0 || strcmp( pszName, "DATUM") == 0 || + strcmp( pszName, "UNITS") == 0 ) ) + { + CPLString osNewVal = pszValue ? pszValue : ""; + if (osNewVal.size() > 31) + osNewVal.resize(31); + if (strcmp(pszName, "PROJ") == 0) + { + bProjCodeChanged = (osNewVal != m_osProjCode); + m_osProjCode = osNewVal; + bHdrDirty |= bProjCodeChanged; + } + else if (strcmp( pszName, "DATUM") == 0) + { + bDatumCodeChanged |= (osNewVal != m_osDatumCode)? TRUE:FALSE ; + m_osDatumCode = osNewVal; + bHdrDirty |= bDatumCodeChanged; + } + else + { + bUnitsCodeChanged |= (osNewVal != m_osUnitsCode)?TRUE:FALSE; + m_osUnitsCode = osNewVal; + bHdrDirty |= bUnitsCodeChanged; + } + return CE_None; + } +#if ECWSDK_VERSION >=50 + else if ( psFileInfo != NULL && + psFileInfo->nFormatVersion >= 3 && + eAccess == GA_Update && + (pszDomain == NULL || EQUAL(pszDomain, "")) && + pszName != NULL && + strncmp(pszName, "FILE_METADATA_", strlen("FILE_METADATA_")) == 0 ) + { + bFileMetaDataDirty = TRUE; + + if( psFileInfo->pFileMetaData == NULL ) + NCSInitMetaData(&(psFileInfo->pFileMetaData)); + + if( strcmp(pszName, "FILE_METADATA_CLASSIFICATION") == 0 ) + { + NCSFree(psFileInfo->pFileMetaData->sClassification); + psFileInfo->pFileMetaData->sClassification = pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : NULL; + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); + } + else if( strcmp(pszName, "FILE_METADATA_ACQUISITION_DATE") == 0 ) + { + NCSFree(psFileInfo->pFileMetaData->sAcquisitionDate); + psFileInfo->pFileMetaData->sAcquisitionDate = pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : NULL; + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); + } + else if( strcmp(pszName, "FILE_METADATA_ACQUISITION_SENSOR_NAME") == 0 ) + { + NCSFree(psFileInfo->pFileMetaData->sAcquisitionSensorName); + psFileInfo->pFileMetaData->sAcquisitionSensorName = pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : NULL; + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); + } + else if( strcmp(pszName, "FILE_METADATA_COMPRESSION_SOFTWARE") == 0 ) + { + NCSFree(psFileInfo->pFileMetaData->sCompressionSoftware); + psFileInfo->pFileMetaData->sCompressionSoftware = pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : NULL; + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); + } + else if( strcmp(pszName, "FILE_METADATA_AUTHOR") == 0 ) + { + NCSFree(psFileInfo->pFileMetaData->sAuthor); + psFileInfo->pFileMetaData->sAuthor = pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : NULL; + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); + } + else if( strcmp(pszName, "FILE_METADATA_COPYRIGHT") == 0 ) + { + NCSFree(psFileInfo->pFileMetaData->sCopyright); + psFileInfo->pFileMetaData->sCopyright = pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : NULL; + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); + } + else if( strcmp(pszName, "FILE_METADATA_COMPANY") == 0 ) + { + NCSFree(psFileInfo->pFileMetaData->sCompany); + psFileInfo->pFileMetaData->sCompany = pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : NULL; + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); + } + else if( strcmp(pszName, "FILE_METADATA_EMAIL") == 0 ) + { + NCSFree(psFileInfo->pFileMetaData->sEmail); + psFileInfo->pFileMetaData->sEmail = pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : NULL; + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); + } + else if( strcmp(pszName, "FILE_METADATA_ADDRESS") == 0 ) + { + NCSFree(psFileInfo->pFileMetaData->sAddress); + psFileInfo->pFileMetaData->sAddress = pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : NULL; + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); + } + else if( strcmp(pszName, "FILE_METADATA_TELEPHONE") == 0 ) + { + NCSFree(psFileInfo->pFileMetaData->sTelephone); + psFileInfo->pFileMetaData->sTelephone = pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : NULL; + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); + } + else + { + return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain); + } + } +#endif + else + return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr ECWDataset::SetMetadata( char ** papszMetadata, + const char * pszDomain ) +{ + /* The bPreventCopyingSomeMetadata is set by ECWCreateCopy() */ + /* just before calling poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); */ + if( bPreventCopyingSomeMetadata && (pszDomain == NULL || EQUAL(pszDomain, "")) ) + { + char** papszMetadataDup = NULL; + char** papszIter = papszMetadata; + while( *papszIter ) + { + char* pszKey = NULL; + CPLParseNameValue(*papszIter, &pszKey); + /* Remove a few metadata item from the source that we don't want in */ + /* the target metadata */ + if( pszKey != NULL && ( + EQUAL(pszKey, "VERSION") || + EQUAL(pszKey, "COMPRESSION_RATE_TARGET") || + EQUAL(pszKey, "COMPRESSION_RATE_ACTUAL") || + EQUAL(pszKey, "CLOCKWISE_ROTATION_DEG") || + EQUAL(pszKey, "COLORSPACE") || + EQUAL(pszKey, "COMPRESSION_DATE") || + EQUALN(pszKey, "FILE_METADATA_", strlen("FILE_METADATA_")) ) ) + { + /* do nothing */ + } + else + { + papszMetadataDup = CSLAddString(papszMetadataDup, *papszIter); + } + CPLFree(pszKey); + papszIter ++; + } + + bPreventCopyingSomeMetadata = FALSE; + CPLErr eErr = SetMetadata(papszMetadataDup, pszDomain); + bPreventCopyingSomeMetadata = TRUE; + CSLDestroy(papszMetadataDup); + return eErr; + } + + if ( ((pszDomain == NULL || EQUAL(pszDomain, "") || EQUAL(pszDomain, "ECW")) && + (CSLFetchNameValue(papszMetadata, "PROJ") != NULL || + CSLFetchNameValue(papszMetadata, "DATUM") != NULL || + CSLFetchNameValue(papszMetadata, "UNITS") != NULL)) +#if ECWSDK_VERSION >=50 + || (psFileInfo != NULL && + psFileInfo->nFormatVersion >= 3 && + eAccess == GA_Update && + (pszDomain == NULL || EQUAL(pszDomain, "")) && + (CSLFetchNameValue(papszMetadata, "FILE_METADATA_CLASSIFICATION") != NULL || + CSLFetchNameValue(papszMetadata, "FILE_METADATA_ACQUISITION_DATE") != NULL || + CSLFetchNameValue(papszMetadata, "FILE_METADATA_ACQUISITION_SENSOR_NAME") != NULL || + CSLFetchNameValue(papszMetadata, "FILE_METADATA_COMPRESSION_SOFTWARE") != NULL || + CSLFetchNameValue(papszMetadata, "FILE_METADATA_AUTHOR") != NULL || + CSLFetchNameValue(papszMetadata, "FILE_METADATA_COPYRIGHT") != NULL || + CSLFetchNameValue(papszMetadata, "FILE_METADATA_COMPANY") != NULL || + CSLFetchNameValue(papszMetadata, "FILE_METADATA_EMAIL") != NULL || + CSLFetchNameValue(papszMetadata, "FILE_METADATA_ADDRESS") != NULL || + CSLFetchNameValue(papszMetadata, "FILE_METADATA_TELEPHONE") != NULL)) +#endif + ) + { + CPLStringList osNewMetadata; + char** papszIter = papszMetadata; + while(*papszIter) + { + if (strncmp(*papszIter, "PROJ=", 5) == 0 || + strncmp(*papszIter, "DATUM=", 6) == 0 || + strncmp(*papszIter, "UNITS=", 6) == 0 || + (strncmp(*papszIter, "FILE_METADATA_", strlen("FILE_METADATA_")) == 0 && strchr(*papszIter, '=') != NULL) ) + { + char* pszKey = NULL; + const char* pszValue = CPLParseNameValue(*papszIter, &pszKey ); + SetMetadataItem(pszKey, pszValue, pszDomain); + CPLFree(pszKey); + } + else + osNewMetadata.AddString(*papszIter); + papszIter ++; + } + if (osNewMetadata.size() != 0) + return GDALPamDataset::SetMetadata(osNewMetadata.List(), pszDomain); + else + return CE_None; + } + else + return GDALPamDataset::SetMetadata(papszMetadata, pszDomain); +} + +/************************************************************************/ +/* WriteHeader() */ +/************************************************************************/ + +void ECWDataset::WriteHeader() +{ + if (!bHdrDirty) + return; + + CPLAssert(eAccess == GA_Update); + CPLAssert(!bIsJPEG2000); + + bHdrDirty = FALSE; + + NCSEcwEditInfo *psEditInfo = NULL; + NCSError eErr; + + /* Load original header info */ +#if ECWSDK_VERSION<50 + eErr = NCSEcwEditReadInfo((char*) GetDescription(), &psEditInfo); +#else + eErr = NCSEcwEditReadInfo( NCS::CString::Utf8Decode(GetDescription()).c_str(), &psEditInfo); +#endif + if (eErr != NCS_SUCCESS) + { + CPLError(CE_Failure, CPLE_AppDefined, "NCSEcwEditReadInfo() failed"); + return; + } + + /* To avoid potential cross-heap issues, we keep the original */ + /* strings, and restore them before freeing the structure */ + char* pszOriginalCode = psEditInfo->szDatum; + char* pszOriginalProj = psEditInfo->szProjection; + + /* Alter the structure with user modified information */ + char szProjCode[32], szDatumCode[32], szUnits[32]; + if (bProjectionChanged) + { + if (ECWTranslateFromWKT( pszProjection, szProjCode, sizeof(szProjCode), + szDatumCode, sizeof(szDatumCode), szUnits ) ) + { + psEditInfo->szDatum = szDatumCode; + psEditInfo->szProjection = szProjCode; + psEditInfo->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits); + CPLDebug("ECW", "Rewrite DATUM : %s", psEditInfo->szDatum); + CPLDebug("ECW", "Rewrite PROJ : %s", psEditInfo->szProjection); + CPLDebug("ECW", "Rewrite UNITS : %s", + ECWTranslateFromCellSizeUnits(psEditInfo->eCellSizeUnits)); + } + } + + if (bDatumCodeChanged) + { + psEditInfo->szDatum = (char*) ((m_osDatumCode.size()) ? m_osDatumCode.c_str() : "RAW"); + CPLDebug("ECW", "Rewrite DATUM : %s", psEditInfo->szDatum); + } + if (bProjCodeChanged) + { + psEditInfo->szProjection = (char*) ((m_osProjCode.size()) ? m_osProjCode.c_str() : "RAW"); + CPLDebug("ECW", "Rewrite PROJ : %s", psEditInfo->szProjection); + } + if (bUnitsCodeChanged) + { + psEditInfo->eCellSizeUnits = ECWTranslateToCellSizeUnits(m_osUnitsCode.c_str()); + CPLDebug("ECW", "Rewrite UNITS : %s", + ECWTranslateFromCellSizeUnits(psEditInfo->eCellSizeUnits)); + } + + if (bGeoTransformChanged) + { + psEditInfo->fOriginX = adfGeoTransform[0]; + psEditInfo->fCellIncrementX = adfGeoTransform[1]; + psEditInfo->fOriginY = adfGeoTransform[3]; + psEditInfo->fCellIncrementY = adfGeoTransform[5]; + CPLDebug("ECW", "Rewrite Geotransform"); + } + + /* Write modified header info */ +#if ECWSDK_VERSION<50 + eErr = NCSEcwEditWriteInfo((char*) GetDescription(), psEditInfo, NULL, NULL, NULL); +#else + eErr = NCSEcwEditWriteInfo( NCS::CString::Utf8Decode(GetDescription()).c_str(), psEditInfo, NULL, NULL, NULL); +#endif + if (eErr != NCS_SUCCESS) + { + CPLError(CE_Failure, CPLE_AppDefined, "NCSEcwEditWriteInfo() failed"); + } + + /* Restore original pointers before free'ing */ + psEditInfo->szDatum = pszOriginalCode; + psEditInfo->szProjection = pszOriginalProj; + + NCSEcwEditFreeInfo(psEditInfo); +} + +/************************************************************************/ +/* AdviseRead() */ +/************************************************************************/ + +CPLErr ECWDataset::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + CPL_UNUSED GDALDataType eDT, + int nBandCount, int *panBandList, + CPL_UNUSED char **papszOptions ) +{ + int *panAdjustedBandList = NULL; + + CPLDebug( "ECW", + "ECWDataset::AdviseRead(%d,%d,%d,%d->%d,%d)", + nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize ); + +#if !defined(SDK_CAN_DO_SUPERSAMPLING) + if( nBufXSize > nXSize || nBufYSize > nYSize ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Supersampling not directly supported by ECW toolkit,\n" + "ignoring AdviseRead() request." ); + return CE_Warning; + } +#endif + +/* -------------------------------------------------------------------- */ +/* Do some validation of parameters. */ +/* -------------------------------------------------------------------- */ + + CPLErr eErr; + int bStopProcessing = FALSE; + eErr = ValidateRasterIOOrAdviseReadParameters( "AdviseRead()", &bStopProcessing, + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + nBandCount, panBandList); + if( eErr != CE_None || bStopProcessing ) + return eErr; + + if( nBandCount > 100 ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "AdviseRead(): Too many bands : %d", nBandCount); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Adjust band numbers to be zero based. */ +/* -------------------------------------------------------------------- */ + panAdjustedBandList = (int *) + CPLMalloc(sizeof(int) * nBandCount ); + nBandIndexToPromoteTo8Bit = -1; + for( int ii= 0; ii < nBandCount; ii++ ) + { + panAdjustedBandList[ii] = (panBandList != NULL) ? panBandList[ii] - 1 : ii; + if( ((ECWRasterBand*)GetRasterBand(panAdjustedBandList[ii] + 1))->bPromoteTo8Bit ) + nBandIndexToPromoteTo8Bit = ii; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup old window cache information. */ +/* -------------------------------------------------------------------- */ + CleanupWindow(); + +/* -------------------------------------------------------------------- */ +/* Set the new requested window. */ +/* -------------------------------------------------------------------- */ + CNCSError oErr; + + oErr = poFileView->SetView( nBandCount, (UINT32 *) panAdjustedBandList, + nXOff, nYOff, + nXOff + nXSize-1, nYOff + nYSize-1, + nBufXSize, nBufYSize ); + + CPLFree( panAdjustedBandList ); + if( oErr.GetErrorNumber() != NCS_SUCCESS ) + { + ECWReportError(oErr); + + bWinActive = FALSE; + return CE_Failure; + } + + bWinActive = TRUE; + +/* -------------------------------------------------------------------- */ +/* Record selected window. */ +/* -------------------------------------------------------------------- */ + nWinXOff = nXOff; + nWinYOff = nYOff; + nWinXSize = nXSize; + nWinYSize = nYSize; + nWinBufXSize = nBufXSize; + nWinBufYSize = nBufYSize; + + panWinBandList = (int *) CPLMalloc(sizeof(int)*nBandCount); + if( panBandList != NULL ) + memcpy( panWinBandList, panBandList, sizeof(int)* nBandCount); + else + { + for( int ii= 0; ii < nBandCount; ii++ ) + { + panWinBandList[ii] = ii + 1; + } + } + nWinBandCount = nBandCount; + + nWinBufLoaded = -1; + +/* -------------------------------------------------------------------- */ +/* Allocate current scanline buffer. */ +/* -------------------------------------------------------------------- */ + papCurLineBuf = (void **) CPLMalloc(sizeof(void*) * nWinBandCount ); + for( int iBand = 0; iBand < nWinBandCount; iBand++ ) + papCurLineBuf[iBand] = + CPLMalloc(nBufXSize * (GDALGetDataTypeSize(eRasterDataType)/8) ); + + return CE_None; +} + +/************************************************************************/ +/* TryWinRasterIO() */ +/* */ +/* Try to satisfy the given request based on the currently */ +/* defined window. Return TRUE on success or FALSE on */ +/* failure. On failure, the caller should satisfy the request */ +/* another way (not report an error). */ +/************************************************************************/ + +int ECWDataset::TryWinRasterIO( CPL_UNUSED GDALRWFlag eFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + GByte *pabyData, int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ + int iBand, i; + +/* -------------------------------------------------------------------- */ +/* Provide default buffer organization. */ +/* -------------------------------------------------------------------- */ + if( nPixelSpace == 0 ) + nPixelSpace = GDALGetDataTypeSize( eDT ) / 8; + if( nLineSpace == 0 ) + nLineSpace = nPixelSpace * nBufXSize; + if( nBandSpace == 0 ) + nBandSpace = nLineSpace * nBufYSize; + +/* -------------------------------------------------------------------- */ +/* Do some simple tests to see if the current window can */ +/* satisfy our requirement. */ +/* -------------------------------------------------------------------- */ +#ifdef NOISY_DEBUG + CPLDebug( "ECW", "TryWinRasterIO(%d,%d,%d,%d,%d,%d)", + nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize ); +#endif + + if( !bWinActive ) + return FALSE; + + if( nXOff != nWinXOff || nXSize != nWinXSize ) + return FALSE; + + if( nBufXSize != nWinBufXSize ) + return FALSE; + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + for( i = 0; i < nWinBandCount; i++ ) + { + if( panWinBandList[i] == panBandList[iBand] ) + break; + } + + if( i == nWinBandCount ) + return FALSE; + } + + if( nYOff < nWinYOff || nYOff + nYSize > nWinYOff + nWinYSize ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Now we try more subtle tests. */ +/* -------------------------------------------------------------------- */ + { + static int nDebugCount = 0; + + if( nDebugCount < 30 ) + CPLDebug( "ECW", + "TryWinRasterIO(%d,%d,%d,%d -> %dx%d) - doing advised read.", + nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize ); + + if( nDebugCount == 29 ) + CPLDebug( "ECW", "No more TryWinRasterIO messages will be reported" ); + + nDebugCount++; + } + +/* -------------------------------------------------------------------- */ +/* Actually load data one buffer line at a time. */ +/* -------------------------------------------------------------------- */ + int iBufLine; + + for( iBufLine = 0; iBufLine < nBufYSize; iBufLine++ ) + { + double fFileLine = ((iBufLine+0.5) / nBufYSize) * nYSize + nYOff; + int iWinLine = + (int) (((fFileLine - nWinYOff) / nWinYSize) * nWinBufYSize); + + if( iWinLine == nWinBufLoaded + 1 ) + LoadNextLine(); + + if( iWinLine != nWinBufLoaded ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Copy out all our target bands. */ +/* -------------------------------------------------------------------- */ + int iWinBand; + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + for( iWinBand = 0; iWinBand < nWinBandCount; iWinBand++ ) + { + if( panWinBandList[iWinBand] == panBandList[iBand] ) + break; + } + + GDALCopyWords( papCurLineBuf[iWinBand], eRasterDataType, + GDALGetDataTypeSize( eRasterDataType ) / 8, + pabyData + nBandSpace * iBand + + iBufLine * nLineSpace, eDT, (int)nPixelSpace, + nBufXSize ); + } + + if( psExtraArg->pfnProgress != NULL && + !psExtraArg->pfnProgress(1.0 * (iBufLine + 1) / nBufYSize, "", + psExtraArg->pProgressData) ) + { + return -1; + } + } + + return TRUE; +} + +/************************************************************************/ +/* LoadNextLine() */ +/************************************************************************/ + +CPLErr ECWDataset::LoadNextLine() + +{ + if( !bWinActive ) + return CE_Failure; + + if( nWinBufLoaded == nWinBufYSize-1 ) + { + CleanupWindow(); + return CE_Failure; + } + + NCSEcwReadStatus eRStatus; + eRStatus = poFileView->ReadLineBIL( eNCSRequestDataType, + (UINT16) nWinBandCount, + papCurLineBuf ); + if( eRStatus != NCSECW_READ_OK ) + return CE_Failure; + + if( nBandIndexToPromoteTo8Bit >= 0 ) + { + for(int iX = 0; iX < nWinBufXSize; iX ++ ) + { + ((GByte*)papCurLineBuf[nBandIndexToPromoteTo8Bit])[iX] *= 255; + } + } + + nWinBufLoaded++; + + return CE_None; +} + +/************************************************************************/ +/* CleanupWindow() */ +/************************************************************************/ + +void ECWDataset::CleanupWindow() + +{ + if( !bWinActive ) + return; + + bWinActive = FALSE; + CPLFree( panWinBandList ); + panWinBandList = NULL; + + for( int iBand = 0; iBand < nWinBandCount; iBand++ ) + CPLFree( papCurLineBuf[iBand] ); + CPLFree( papCurLineBuf ); + papCurLineBuf = NULL; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr ECWDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ + if( eRWFlag == GF_Write ) + return CE_Failure; + + if( nBandCount > 100 ) + return CE_Failure; + + if( bUseOldBandRasterIOImplementation ) + /* Sanity check. Shouldn't happen */ + return CE_Failure; + int nDataTypeSize = (GDALGetDataTypeSize(eRasterDataType) / 8); + + if ( nPixelSpace == 0 ){ + nPixelSpace = nDataTypeSize; + } + + if (nLineSpace == 0 ) { + nLineSpace = nPixelSpace*nBufXSize; + } + if ( nBandSpace == 0 ){ + nBandSpace = nDataTypeSize*nBufXSize*nBufYSize; + } +/* -------------------------------------------------------------------- */ +/* ECW SDK 3.3 has a bug with the ECW format when we query the */ +/* number of bands of the dataset, but not in the "natural order". */ +/* It ignores the content of panBandMap. (#4234) */ +/* -------------------------------------------------------------------- */ +#if ECWSDK_VERSION < 40 + if( !bIsJPEG2000 && nBandCount == nBands ) + { + int i; + int bDoBandIRasterIO = FALSE; + for( i = 0; i < nBandCount; i++ ) + { + if( panBandMap[i] != i + 1 ) + { + bDoBandIRasterIO = TRUE; + } + } + if( bDoBandIRasterIO ) + { + return GDALDataset::IRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg); + } + } +#endif + +/* -------------------------------------------------------------------- */ +/* Check if we can directly return the data in case we have cached */ +/* it from a previous call in a multi-band reading pattern. */ +/* -------------------------------------------------------------------- */ + if( nBandCount == 1 && *panBandMap > 1 && *panBandMap <= nBands && + sCachedMultiBandIO.nXOff == nXOff && + sCachedMultiBandIO.nYOff == nYOff && + sCachedMultiBandIO.nXSize == nXSize && + sCachedMultiBandIO.nYSize == nYSize && + sCachedMultiBandIO.nBufXSize == nBufXSize && + sCachedMultiBandIO.nBufYSize == nBufYSize && + sCachedMultiBandIO.eBufType == eBufType ) + { + sCachedMultiBandIO.nBandsTried ++; + + if( sCachedMultiBandIO.bEnabled && + sCachedMultiBandIO.pabyData != NULL ) + { + int j; + int nDataTypeSize = GDALGetDataTypeSize(eBufType) / 8; + for(j = 0; j < nBufYSize; j++) + { + GDALCopyWords(sCachedMultiBandIO.pabyData + + (*panBandMap - 1) * nBufXSize * nBufYSize * nDataTypeSize + + j * nBufXSize * nDataTypeSize, + eBufType, nDataTypeSize, + ((GByte*)pData) + j * nLineSpace, eBufType, (int)nPixelSpace, + nBufXSize); + } + return CE_None; + } + + if( !(sCachedMultiBandIO.bEnabled) && + sCachedMultiBandIO.nBandsTried == nBands && + CSLTestBoolean(CPLGetConfigOption("ECW_CLEVER", "YES")) ) + { + sCachedMultiBandIO.bEnabled = TRUE; + CPLDebug("ECW", "Detecting successive band reading pattern (for next time)"); + } + } + +/* -------------------------------------------------------------------- */ +/* Try to do it based on existing "advised" access. */ +/* -------------------------------------------------------------------- */ + int nRet = TryWinRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + (GByte *) pData, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); + if( nRet == TRUE ) + return CE_None; + else if( nRet < 0 ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* If we are requesting a single line at 1:1, we do a multi-band */ +/* AdviseRead() and then TryWinRasterIO() again. */ +/* */ +/* Except for reading a 1x1 window when reading a scanline might */ +/* be longer. */ +/* -------------------------------------------------------------------- */ + if( nXSize == 1 && nYSize == 1 && nBufXSize == 1 && nBufYSize == 1 ) + { + /* do nothing */ + } + +#if !defined(SDK_CAN_DO_SUPERSAMPLING) +/* -------------------------------------------------------------------- */ +/* If we are supersampling we need to fall into the general */ +/* purpose logic. */ +/* -------------------------------------------------------------------- */ + else if( nXSize < nBufXSize || nYSize < nBufYSize ) + { + bUseOldBandRasterIOImplementation = TRUE; + CPLErr eErr = + GDALDataset::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg); + bUseOldBandRasterIOImplementation = FALSE; + return eErr; + } +#endif + + else if( nBufYSize == 1 ) + { + //JTO: this is tricky, because it expects the rest of the image with this bufer width to be + //read. The prefered way to achieve this behaviour would be to call AdviseRead before call IRasterIO. + //ERO; indeed, the logic could be improved to detect successive pattern of single line reading + //before doing an AdviseRead. + CPLErr eErr; + + eErr = AdviseRead( nXOff, nYOff, nXSize, GetRasterYSize() - nYOff, + nBufXSize, (nRasterYSize - nYOff) / nYSize, eBufType, + nBandCount, panBandMap, NULL ); + if( eErr == CE_None + && TryWinRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + (GByte *) pData, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ) ) + return CE_None; + } + + CPLDebug( "ECW", + "RasterIO(%d,%d,%d,%d -> %dx%d) - doing interleaved read.", + nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize ); + +/* -------------------------------------------------------------------- */ +/* Setup view. */ +/* -------------------------------------------------------------------- */ + UINT32 anBandIndices[100]; + int i; + NCSError eNCSErr; + CNCSError oErr; + + for( i = 0; i < nBandCount; i++ ) + anBandIndices[i] = panBandMap[i] - 1; + + CleanupWindow(); + +/* -------------------------------------------------------------------- */ +/* Cache data in the context of a multi-band reading pattern. */ +/* -------------------------------------------------------------------- */ + if( nBandCount == 1 && *panBandMap == 1 && (nBands == 3 || nBands == 4) ) + { + if( sCachedMultiBandIO.bEnabled && sCachedMultiBandIO.nBandsTried != nBands ) + { + sCachedMultiBandIO.bEnabled = FALSE; + CPLDebug("ECW", "Disabling successive band reading pattern"); + } + + sCachedMultiBandIO.nXOff = nXOff; + sCachedMultiBandIO.nYOff = nYOff; + sCachedMultiBandIO.nXSize = nXSize; + sCachedMultiBandIO.nYSize = nYSize; + sCachedMultiBandIO.nBufXSize = nBufXSize; + sCachedMultiBandIO.nBufYSize = nBufYSize; + sCachedMultiBandIO.eBufType = eBufType; + sCachedMultiBandIO.nBandsTried = 1; + + int nDataTypeSize = GDALGetDataTypeSize(eBufType) / 8; + + if( sCachedMultiBandIO.bEnabled ) + { + GByte* pNew = (GByte*)VSIRealloc( + sCachedMultiBandIO.pabyData, + nBufXSize * nBufYSize * nBands * nDataTypeSize); + if( pNew == NULL ) + CPLFree(sCachedMultiBandIO.pabyData); + sCachedMultiBandIO.pabyData = pNew; + } + + if( sCachedMultiBandIO.bEnabled && + sCachedMultiBandIO.pabyData != NULL ) + { + nBandIndexToPromoteTo8Bit = -1; + for( i = 0; i < nBands; i++ ) + { + if( ((ECWRasterBand*)GetRasterBand(i+1))->bPromoteTo8Bit ) + nBandIndexToPromoteTo8Bit = i; + anBandIndices[i] = i; + } + + oErr = poFileView->SetView( nBands, anBandIndices, + nXOff, nYOff, + nXOff + nXSize - 1, + nYOff + nYSize - 1, + nBufXSize, nBufYSize ); + eNCSErr = oErr.GetErrorNumber(); + + if( eNCSErr != NCS_SUCCESS ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", NCSGetErrorText(eNCSErr) ); + + return CE_Failure; + } + + CPLErr eErr = ReadBands(sCachedMultiBandIO.pabyData, + nBufXSize, nBufYSize, eBufType, + nBands, + nDataTypeSize, + nBufXSize * nDataTypeSize, + nBufXSize * nBufYSize * nDataTypeSize, + psExtraArg); + if( eErr != CE_None ) + return eErr; + + int j; + for(j = 0; j < nBufYSize; j++) + { + GDALCopyWords(sCachedMultiBandIO.pabyData + + j * nBufXSize * nDataTypeSize, + eBufType, nDataTypeSize, + ((GByte*)pData) + j * nLineSpace, eBufType, (int)nPixelSpace, + nBufXSize); + } + return CE_None; + } + } + + nBandIndexToPromoteTo8Bit = -1; + for( i = 0; i < nBandCount; i++ ) + { + if( ((ECWRasterBand*)GetRasterBand(anBandIndices[i]+1))->bPromoteTo8Bit ) + nBandIndexToPromoteTo8Bit = i; + } + oErr = poFileView->SetView( nBandCount, anBandIndices, + nXOff, nYOff, + nXOff + nXSize - 1, + nYOff + nYSize - 1, + nBufXSize, nBufYSize ); + eNCSErr = oErr.GetErrorNumber(); + + if( eNCSErr != NCS_SUCCESS ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", NCSGetErrorText(eNCSErr) ); + + return CE_Failure; + } + + return ReadBands(pData, nBufXSize, nBufYSize, eBufType, + nBandCount, nPixelSpace, nLineSpace, nBandSpace, + psExtraArg); +} + +/************************************************************************/ +/* ReadBandsDirectly() */ +/************************************************************************/ + +CPLErr ECWDataset::ReadBandsDirectly(void * pData, int nBufXSize, int nBufYSize, + CPL_UNUSED GDALDataType eBufType, + int nBandCount, + CPL_UNUSED GSpacing nPixelSpace, + GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) +{ + CPLDebug( "ECW", + "ReadBandsDirectly(-> %dx%d) - reading lines directly.", + nBufXSize, nBufYSize); + + UINT8 **pBIL = (UINT8**)NCSMalloc(nBandCount * sizeof(UINT8*), FALSE); + + for(int nB = 0; nB < nBandCount; nB++) + { + pBIL[nB] = ((UINT8*)pData) + (nBandSpace*nB);//for any bit depth + } + + CPLErr eErr = CE_None; + for(int nR = 0; nR < nBufYSize; nR++) + { + if (poFileView->ReadLineBIL(eNCSRequestDataType,(UINT16) nBandCount, (void**)pBIL) != NCSECW_READ_OK) + { + eErr = CE_Failure; + break; + } + for(int nB = 0; nB < nBandCount; nB++) + { + if( nB == nBandIndexToPromoteTo8Bit ) + { + for(int iX = 0; iX < nBufXSize; iX ++ ) + { + pBIL[nB][iX] *= 255; + } + } + pBIL[nB] += nLineSpace; + } + + if( psExtraArg->pfnProgress != NULL && + !psExtraArg->pfnProgress(1.0 * (nR + 1) / nBufYSize, "", + psExtraArg->pProgressData) ) + { + eErr = CE_Failure; + break; + } + } + if(pBIL) + { + NCSFree(pBIL); + } + return eErr; +} + +/************************************************************************/ +/* ReadBands() */ +/************************************************************************/ + +CPLErr ECWDataset::ReadBands(void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, + GSpacing nPixelSpace, GSpacing nLineSpace, GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) +{ + int i; +/* -------------------------------------------------------------------- */ +/* Setup working scanline, and the pointers into it. */ +/* -------------------------------------------------------------------- */ + int nDataTypeSize = (GDALGetDataTypeSize(eRasterDataType) / 8); + bool bDirect = (eBufType == eRasterDataType) && nDataTypeSize == nPixelSpace && + nLineSpace == (nPixelSpace*nBufXSize) && nBandSpace == (nDataTypeSize*nBufXSize*nBufYSize) ; + if (bDirect) + { + return ReadBandsDirectly(pData, nBufXSize, nBufYSize,eBufType, + nBandCount, nPixelSpace, nLineSpace, nBandSpace, psExtraArg); + } + CPLDebug( "ECW", + "ReadBands(-> %dx%d) - reading lines using GDALCopyWords.", + nBufXSize, nBufYSize); + CPLErr eErr = CE_None; + GByte *pabyBILScanline = (GByte *) CPLMalloc(nBufXSize * nDataTypeSize * + nBandCount); + GByte **papabyBIL = (GByte **) CPLMalloc(nBandCount * sizeof(void*)); + + for( i = 0; i < nBandCount; i++ ) + papabyBIL[i] = pabyBILScanline + i * nBufXSize * nDataTypeSize; + +/* -------------------------------------------------------------------- */ +/* Read back all the data for the requested view. */ +/* -------------------------------------------------------------------- */ + for( int iScanline = 0; iScanline < nBufYSize; iScanline++ ) + { + NCSEcwReadStatus eRStatus; + + eRStatus = poFileView->ReadLineBIL( eNCSRequestDataType, + (UINT16) nBandCount, + (void **) papabyBIL ); + if( eRStatus != NCSECW_READ_OK ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_AppDefined, + "NCScbmReadViewLineBIL failed." ); + break; + } + + for( i = 0; i < nBandCount; i++ ) + { + if( i == nBandIndexToPromoteTo8Bit ) + { + for(int iX = 0; iX < nBufXSize; iX ++ ) + { + papabyBIL[i][iX] *= 255; + } + } + + GDALCopyWords( + pabyBILScanline + i * nDataTypeSize * nBufXSize, + eRasterDataType, nDataTypeSize, + ((GByte *) pData) + nLineSpace * iScanline + nBandSpace * i, + eBufType, (int)nPixelSpace, + nBufXSize ); + } + + if( psExtraArg->pfnProgress != NULL && + !psExtraArg->pfnProgress(1.0 * (iScanline + 1) / nBufYSize, "", + psExtraArg->pProgressData) ) + { + eErr = CE_Failure; + break; + } + } + + CPLFree( pabyBILScanline ); + CPLFree( papabyBIL ); + + return eErr; +} + +/************************************************************************/ +/* IdentifyJPEG2000() */ +/* */ +/* Open method that only supports JPEG2000 files. */ +/************************************************************************/ + +int ECWDataset::IdentifyJPEG2000( GDALOpenInfo * poOpenInfo ) + +{ + if( EQUALN(poOpenInfo->pszFilename,"J2K_SUBFILE:",12) ) + return TRUE; + + else if( poOpenInfo->nHeaderBytes >= 16 + && (memcmp( poOpenInfo->pabyHeader, jpc_header, + sizeof(jpc_header) ) == 0 + || memcmp( poOpenInfo->pabyHeader, jp2_header, + sizeof(jp2_header) ) == 0) ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* OpenJPEG2000() */ +/* */ +/* Open method that only supports JPEG2000 files. */ +/************************************************************************/ + +GDALDataset *ECWDataset::OpenJPEG2000( GDALOpenInfo * poOpenInfo ) + +{ + if (!IdentifyJPEG2000(poOpenInfo)) + return NULL; + + return Open( poOpenInfo, TRUE ); +} + +/************************************************************************/ +/* IdentifyECW() */ +/* */ +/* Identify method that only supports ECW files. */ +/************************************************************************/ + +int ECWDataset::IdentifyECW( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* This has to either be a file on disk ending in .ecw or a */ +/* ecwp: protocol url. */ +/* -------------------------------------------------------------------- */ + if( (!EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"ecw") + || poOpenInfo->nHeaderBytes == 0) + && !EQUALN(poOpenInfo->pszFilename,"ecwp:",5) + && !EQUALN(poOpenInfo->pszFilename,"ecwps:",5) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* OpenECW() */ +/* */ +/* Open method that only supports ECW files. */ +/************************************************************************/ + +GDALDataset *ECWDataset::OpenECW( GDALOpenInfo * poOpenInfo ) + +{ + if (!IdentifyECW(poOpenInfo)) + return NULL; + + return Open( poOpenInfo, FALSE ); +} + +/************************************************************************/ +/* OpenFileView() */ +/************************************************************************/ + +CNCSJP2FileView *ECWDataset::OpenFileView( const char *pszDatasetName, + bool bProgressive, + int &bUsingCustomStream, + CPL_UNUSED bool bWrite ) +{ +/* -------------------------------------------------------------------- */ +/* First we try to open it as a normal CNCSFile, letting the */ +/* ECW SDK manage the IO itself. This will only work for real */ +/* files, and ecwp: or ecwps: sources. */ +/* -------------------------------------------------------------------- */ + CNCSJP2FileView *poFileView = NULL; + NCSError eErr; + CNCSError oErr; + + bUsingCustomStream = FALSE; + poFileView = new CNCSFile(); + //we always open in read only mode. This should be improved in the future. + try + { + oErr = poFileView->Open( (char *) pszDatasetName, bProgressive, false ); + } + catch(...) + { + CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception occured in ECW SDK"); + delete poFileView; + return NULL; + } + eErr = oErr.GetErrorNumber(); + +/* -------------------------------------------------------------------- */ +/* If that did not work, trying opening as a virtual file. */ +/* -------------------------------------------------------------------- */ + if( eErr != NCS_SUCCESS ) + { + CPLDebug( "ECW", + "NCScbmOpenFileView(%s): eErr=%d, will try VSIL stream.", + pszDatasetName, (int) eErr ); + + delete poFileView; + + VSILFILE *fpVSIL = VSIFOpenL( pszDatasetName, "rb" ); + if( fpVSIL == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s.", pszDatasetName ); + return NULL; + } + + if( hECWDatasetMutex == NULL ) + { + hECWDatasetMutex = CPLCreateMutex(); + } + else if( !CPLAcquireMutex( hECWDatasetMutex, 60.0 ) ) + { + CPLDebug( "ECW", "Failed to acquire mutex in 60s." ); + } + else + { + CPLDebug( "ECW", "Got mutex." ); + } + VSIIOStream *poIOStream = new VSIIOStream(); + poIOStream->Access( fpVSIL, FALSE, TRUE, pszDatasetName, 0, -1 ); + + poFileView = new CNCSJP2FileView(); + oErr = poFileView->Open( poIOStream, bProgressive ); + + // The CNCSJP2FileView (poFileView) object may not use the iostream + // (poIOStream) passed to the CNCSJP2FileView::Open() method if an + // iostream is already available to the ECW JPEG 2000 SDK for a given + // file. Consequently, if the iostream passed to + // CNCSJP2FileView::Open() does not become the underlying iostream + // of the CNCSJP2FileView object, then it should be deleted. + // + // In addition, the underlying iostream of the CNCSJP2FileView object + // should not be deleted until all CNCSJP2FileView objects using the + // underlying iostream are deleted. Consequently, each time a + // CNCSJP2FileView object is created, the nFileViewCount attribute + // of the underlying VSIIOStream object must be incremented for use + // in the ECWDataset destructor. + + VSIIOStream * poUnderlyingIOStream = + ((VSIIOStream *)(poFileView->GetStream())); + + if ( poUnderlyingIOStream ) + poUnderlyingIOStream->nFileViewCount++; + + if ( poIOStream != poUnderlyingIOStream ) + { + delete poIOStream; + } + else + { + bUsingCustomStream = TRUE; + } + + CPLReleaseMutex( hECWDatasetMutex ); + + if( oErr.GetErrorNumber() != NCS_SUCCESS ) + { + if (poFileView) + delete poFileView; + ECWReportError(oErr); + + return NULL; + } + } + + return poFileView; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *ECWDataset::Open( GDALOpenInfo * poOpenInfo, int bIsJPEG2000 ) + +{ + CNCSJP2FileView *poFileView = NULL; + int i; + int bUsingCustomStream = FALSE; + CPLString osFilename = poOpenInfo->pszFilename; + + ECWInitialize(); + + /* Note: J2K_SUBFILE is somehow an obsolete concept that predates /vsisubfile/ */ + /* syntax and was used mainly(only?) by the NITF driver before its switch */ + /* to /vsisubfile */ + +/* -------------------------------------------------------------------- */ +/* If we get a J2K_SUBFILE style name, convert it into the */ +/* corresponding /vsisubfile/ path. */ +/* */ +/* From: J2K_SUBFILE:offset,size,filename */ +/* To: /vsisubfile/offset_size,filename */ +/* -------------------------------------------------------------------- */ + if (EQUALN(osFilename,"J2K_SUBFILE:",12)) + { + char** papszTokens = CSLTokenizeString2(osFilename.c_str()+12, ",", 0); + if (CSLCount(papszTokens) >= 3) + { + osFilename.Printf( "/vsisubfile/%s_%s,%s", + papszTokens[0], papszTokens[1], papszTokens[2]); + } + else + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to parse J2K_SUBFILE specification." ); + CSLDestroy(papszTokens); + return NULL; + } + CSLDestroy(papszTokens); + } + +/* -------------------------------------------------------------------- */ +/* Open the client interface. */ +/* -------------------------------------------------------------------- */ + poFileView = OpenFileView( osFilename.c_str(), false, bUsingCustomStream, poOpenInfo->eAccess == GA_Update ); + if( poFileView == NULL ) + { +#if ECWSDK_VERSION < 50 + /* Detect what is apparently the ECW v3 file format signature */ + if( EQUAL(CPLGetExtension(osFilename), "ECW") && + poOpenInfo->nHeaderBytes > 0x30 && + EQUALN((const char*)(poOpenInfo->pabyHeader + 0x20), "ecw ECW3", 8) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot open %s which looks like a ECW format v3 file, that requires ECW SDK 5.0 or later", + osFilename.c_str()); + } +#endif + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + ECWDataset *poDS; + + poDS = new ECWDataset(bIsJPEG2000); + poDS->poFileView = poFileView; + poDS->eAccess = poOpenInfo->eAccess; + + // Disable .aux.xml writing for subfiles and such. Unfortunately + // this will also disable it in some cases where it might be + // applicable. + if( bUsingCustomStream ) + poDS->nPamFlags |= GPF_DISABLED; + + poDS->bUsingCustomStream = bUsingCustomStream; + +/* -------------------------------------------------------------------- */ +/* Fetch general file information. */ +/* -------------------------------------------------------------------- */ + poDS->psFileInfo = poFileView->GetFileInfo(); + + CPLDebug( "ECW", "FileInfo: SizeXY=%d,%d Bands=%d\n" + " OriginXY=%g,%g CellIncrementXY=%g,%g\n" + " ColorSpace=%d, eCellType=%d\n", + poDS->psFileInfo->nSizeX, + poDS->psFileInfo->nSizeY, + poDS->psFileInfo->nBands, + poDS->psFileInfo->fOriginX, + poDS->psFileInfo->fOriginY, + poDS->psFileInfo->fCellIncrementX, + poDS->psFileInfo->fCellIncrementY, + (int) poDS->psFileInfo->eColorSpace, + (int) poDS->psFileInfo->eCellType ); + +/* -------------------------------------------------------------------- */ +/* Establish raster info. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = poDS->psFileInfo->nSizeX; + poDS->nRasterYSize = poDS->psFileInfo->nSizeY; + +/* -------------------------------------------------------------------- */ +/* Establish the GDAL data type that corresponds. A few NCS */ +/* data types have no direct corresponding value in GDAL so we */ +/* will coerce to something sufficiently similar. */ +/* -------------------------------------------------------------------- */ + poDS->eNCSRequestDataType = poDS->psFileInfo->eCellType; + switch( poDS->psFileInfo->eCellType ) + { + case NCSCT_UINT8: + poDS->eRasterDataType = GDT_Byte; + break; + + case NCSCT_UINT16: + poDS->eRasterDataType = GDT_UInt16; + break; + + case NCSCT_UINT32: + case NCSCT_UINT64: + poDS->eRasterDataType = GDT_UInt32; + poDS->eNCSRequestDataType = NCSCT_UINT32; + break; + + case NCSCT_INT8: + case NCSCT_INT16: + poDS->eRasterDataType = GDT_Int16; + poDS->eNCSRequestDataType = NCSCT_INT16; + break; + + case NCSCT_INT32: + case NCSCT_INT64: + poDS->eRasterDataType = GDT_Int32; + poDS->eNCSRequestDataType = NCSCT_INT32; + break; + + case NCSCT_IEEE4: + poDS->eRasterDataType = GDT_Float32; + break; + + case NCSCT_IEEE8: + poDS->eRasterDataType = GDT_Float64; + break; + + default: + CPLDebug("ECW", "Unhandled case : eCellType = %d", + (int)poDS->psFileInfo->eCellType ); + break; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( i=0; i < poDS->psFileInfo->nBands; i++ ) + poDS->SetBand( i+1, new ECWRasterBand( poDS, i+1, -1, poOpenInfo->papszOpenOptions ) ); + +/* -------------------------------------------------------------------- */ +/* Look for supporting coordinate system information. */ +/* -------------------------------------------------------------------- */ + if( bIsJPEG2000 ) + { + poDS->LoadJP2Metadata(poOpenInfo, osFilename); + } + else + { + poDS->ECW2WKTProjection(); + + /* -------------------------------------------------------------------- */ + /* Check for world file. */ + /* -------------------------------------------------------------------- */ + if( !poDS->bGeoTransformValid ) + { + poDS->bGeoTransformValid |= + GDALReadWorldFile2( osFilename, NULL, + poDS->adfGeoTransform, + poOpenInfo->GetSiblingFiles(), NULL ) + || GDALReadWorldFile2( osFilename, ".wld", + poDS->adfGeoTransform, + poOpenInfo->GetSiblingFiles(), NULL ); + } + } + + poDS->SetMetadataItem("COMPRESSION_RATE_TARGET", CPLString().Printf("%d", poDS->psFileInfo->nCompressionRate)); + poDS->SetMetadataItem("COLORSPACE", ECWGetColorSpaceName(poDS->psFileInfo->eColorSpace)); +#if ECWSDK_VERSION>=50 + if( !bIsJPEG2000 ) + poDS->SetMetadataItem("VERSION", CPLString().Printf("%d", poDS->psFileInfo->nFormatVersion)); +#if ECWSDK_VERSION>=51 + // output jp2 header info + if( bIsJPEG2000 && poDS->poFileView ) { + // comments + char *csComments = NULL; + poDS->poFileView->GetParameter((char*)"JPC:DECOMPRESS:COMMENTS", &csComments); + if (csComments) { + poDS->SetMetadataItem("ALL_COMMENTS", CPLString().Printf("%s", csComments)); + NCSFree(csComments); + } + + // Profile + UINT32 nProfile = 2; + UINT32 nRsiz = 0; + poDS->poFileView->GetParameter((char*)"JP2:COMPLIANCE:PROFILE:TYPE", &nRsiz); + if (nRsiz == 0) + nProfile = 2; // Profile 2 (no restrictions) + else if (nRsiz == 1) + nProfile = 0; // Profile 0 + else if (nRsiz == 2) + nProfile = 1; // Profile 1, NITF_BIIF_NPJE, NITF_BIIF_EPJE + poDS->SetMetadataItem("PROFILE", CPLString().Printf("%d", nProfile), JPEG2000_DOMAIN_NAME); + + // number of tiles on X axis + UINT32 nTileNrX = 1; + poDS->poFileView->GetParameter((char*)"JPC:DECOMPRESS:TILENR:X", &nTileNrX); + poDS->SetMetadataItem("TILES_X", CPLString().Printf("%d", nTileNrX), JPEG2000_DOMAIN_NAME); + + // number of tiles on X axis + UINT32 nTileNrY = 1; + poDS->poFileView->GetParameter((char*)"JPC:DECOMPRESS:TILENR:Y", &nTileNrY); + poDS->SetMetadataItem("TILES_Y", CPLString().Printf("%d", nTileNrY), JPEG2000_DOMAIN_NAME); + + // Tile Width + UINT32 nTileSizeX = 0; + poDS->poFileView->GetParameter((char*)"JPC:DECOMPRESS:TILESIZE:X", &nTileSizeX); + poDS->SetMetadataItem("TILE_WIDTH", CPLString().Printf("%d", nTileSizeX), JPEG2000_DOMAIN_NAME); + + // Tile Height + UINT32 nTileSizeY = 0; + poDS->poFileView->GetParameter((char*)"JPC:DECOMPRESS:TILESIZE:Y", &nTileSizeY); + poDS->SetMetadataItem("TILE_HEIGHT", CPLString().Printf("%d", nTileSizeY), JPEG2000_DOMAIN_NAME); + + // Precinct Sizes on X axis + char *csPreSizeX = NULL; + poDS->poFileView->GetParameter((char*)"JPC:DECOMPRESS:PRECINCTSIZE:X", &csPreSizeX); + if (csPreSizeX) { + poDS->SetMetadataItem("PRECINCT_SIZE_X", csPreSizeX, JPEG2000_DOMAIN_NAME); + NCSFree(csPreSizeX); + } + + // Precinct Sizes on Y axis + char *csPreSizeY = NULL; + poDS->poFileView->GetParameter((char*)"JPC:DECOMPRESS:PRECINCTSIZE:Y", &csPreSizeY); + if (csPreSizeY) { + poDS->SetMetadataItem("PRECINCT_SIZE_Y", csPreSizeY, JPEG2000_DOMAIN_NAME); + NCSFree(csPreSizeY); + } + + // Code Block Size on X axis + UINT32 nCodeBlockSizeX = 0; + poDS->poFileView->GetParameter((char*)"JPC:DECOMPRESS:CODEBLOCK:X", &nCodeBlockSizeX); + poDS->SetMetadataItem("CODE_BLOCK_SIZE_X", CPLString().Printf("%d", nCodeBlockSizeX), JPEG2000_DOMAIN_NAME); + + // Code Block Size on Y axis + UINT32 nCodeBlockSizeY = 0; + poDS->poFileView->GetParameter((char*)"JPC:DECOMPRESS:CODEBLOCK:Y", &nCodeBlockSizeY); + poDS->SetMetadataItem("CODE_BLOCK_SIZE_Y", CPLString().Printf("%d", nCodeBlockSizeY), JPEG2000_DOMAIN_NAME); + + // Bitdepth + char *csBitdepth = NULL; + poDS->poFileView->GetParameter((char*)"JPC:DECOMPRESS:BITDEPTH", &csBitdepth); + if (csBitdepth) { + poDS->SetMetadataItem("PRECISION", csBitdepth, JPEG2000_DOMAIN_NAME); + NCSFree(csBitdepth); + } + + // Resolution Levels + UINT32 nLevels = 0; + poDS->poFileView->GetParameter((char*)"JPC:DECOMPRESS:RESOLUTION:LEVELS", &nLevels); + poDS->SetMetadataItem("RESOLUTION_LEVELS", CPLString().Printf("%d", nLevels), JPEG2000_DOMAIN_NAME); + + // Qualaity Layers + UINT32 nLayers = 0; + poDS->poFileView->GetParameter((char*)"JP2:DECOMPRESS:LAYERS", &nLayers); + poDS->SetMetadataItem("QUALITY_LAYERS", CPLString().Printf("%d", nLayers), JPEG2000_DOMAIN_NAME); + + // Progression Order + char *csOrder = NULL; + poDS->poFileView->GetParameter((char*)"JPC:DECOMPRESS:PROGRESSION:ORDER", &csOrder); + if (csOrder) { + poDS->SetMetadataItem("PROGRESSION_ORDER", csOrder, JPEG2000_DOMAIN_NAME); + NCSFree(csOrder); + } + + // DWT Filter + const char *csFilter = NULL; + UINT32 nFilter; + poDS->poFileView->GetParameter((char*)"JP2:TRANSFORMATION:TYPE", &nFilter); + if (nFilter) + csFilter = "5x3"; + else + csFilter = "9x7"; + poDS->SetMetadataItem("TRANSFORMATION_TYPE", csFilter, JPEG2000_DOMAIN_NAME); + + // SOP used? + bool bSOP = 0; + poDS->poFileView->GetParameter((char*)"JP2:DECOMPRESS:SOP:EXISTS", &bSOP); + poDS->SetMetadataItem("USE_SOP", (bSOP) ? "TRUE" : "FALSE", JPEG2000_DOMAIN_NAME); + + // EPH used? + bool bEPH = 0; + poDS->poFileView->GetParameter((char*)"JP2:DECOMPRESS:EPH:EXISTS", &bEPH); + poDS->SetMetadataItem("USE_EPH", (bEPH) ? "TRUE" : "FALSE", JPEG2000_DOMAIN_NAME); + + // GML JP2 data contained? + bool bGML = 0; + poDS->poFileView->GetParameter((char*)"JP2:GML:JP2:BOX:EXISTS", &bGML); + poDS->SetMetadataItem("GML_JP2_DATA", (bGML) ? "TRUE" : "FALSE", JPEG2000_DOMAIN_NAME); + } + #endif //ECWSDK_VERSION>=51 + if ( !bIsJPEG2000 && poDS->psFileInfo->nFormatVersion >=3 ){ + poDS->SetMetadataItem("COMPRESSION_RATE_ACTUAL", CPLString().Printf("%f", poDS->psFileInfo->fActualCompressionRate)); + poDS->SetMetadataItem("CLOCKWISE_ROTATION_DEG", CPLString().Printf("%f", poDS->psFileInfo->fCWRotationDegrees)); + poDS->SetMetadataItem("COMPRESSION_DATE", poDS->psFileInfo->sCompressionDate); + //Get file metadata. + poDS->ReadFileMetaDataFromFile(); + } +#else + poDS->SetMetadataItem("VERSION", CPLString().Printf("%d",bIsJPEG2000?1:2)); +#endif + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( osFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Vector layers */ +/* -------------------------------------------------------------------- */ + if( bIsJPEG2000 && poOpenInfo->nOpenFlags & GDAL_OF_VECTOR ) + { + poDS->LoadVectorLayers( + CSLFetchBoolean(poOpenInfo->papszOpenOptions, "OPEN_REMOTE_GML", FALSE)); + + // If file opened in vector-only mode and there's no vector, + // return + if( (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 && + poDS->GetLayerCount() == 0 ) + { + delete poDS; + return NULL; + } + } + + return( poDS ); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **ECWDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "ECW", "GML", NULL); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *ECWDataset::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if (!bIsJPEG2000 && pszDomain != NULL && EQUAL(pszDomain, "ECW") && pszName != NULL) + { + if (EQUAL(pszName, "PROJ")) + return m_osProjCode.size() ? m_osProjCode.c_str() : "RAW"; + if (EQUAL(pszName, "DATUM")) + return m_osDatumCode.size() ? m_osDatumCode.c_str() : "RAW"; + if (EQUAL(pszName, "UNITS")) + return m_osUnitsCode.size() ? m_osUnitsCode.c_str() : "METERS"; + } + return GDALPamDataset::GetMetadataItem(pszName, pszDomain); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **ECWDataset::GetMetadata( const char *pszDomain ) + +{ + if( !bIsJPEG2000 && pszDomain != NULL && EQUAL(pszDomain, "ECW") ) + { + oECWMetadataList.Clear(); + oECWMetadataList.AddString(CPLSPrintf("%s=%s", "PROJ", GetMetadataItem("PROJ", "ECW"))); + oECWMetadataList.AddString(CPLSPrintf("%s=%s", "DATUM", GetMetadataItem("DATUM", "ECW"))); + oECWMetadataList.AddString(CPLSPrintf("%s=%s", "UNITS", GetMetadataItem("UNITS", "ECW"))); + return oECWMetadataList.List(); + } + else if( pszDomain == NULL || !EQUAL(pszDomain,"GML") ) + return GDALPamDataset::GetMetadata( pszDomain ); + else + return papszGMLMetadata; +} + +/************************************************************************/ +/* ReadFileMetaDataFromFile() */ +/* */ +/* Gets relevant information from NCSFileMetadata and populates */ +/* GDAL metadata */ +/* */ +/************************************************************************/ +#if ECWSDK_VERSION >= 50 +void ECWDataset::ReadFileMetaDataFromFile() +{ + if (psFileInfo->pFileMetaData == NULL) return; + + if (psFileInfo->pFileMetaData->sClassification != NULL ) + GDALDataset::SetMetadataItem("FILE_METADATA_CLASSIFICATION", NCS::CString(psFileInfo->pFileMetaData->sClassification).a_str()); + if (psFileInfo->pFileMetaData->sAcquisitionDate != NULL ) + GDALDataset::SetMetadataItem("FILE_METADATA_ACQUISITION_DATE", NCS::CString(psFileInfo->pFileMetaData->sAcquisitionDate)); + if (psFileInfo->pFileMetaData->sAcquisitionSensorName != NULL ) + GDALDataset::SetMetadataItem("FILE_METADATA_ACQUISITION_SENSOR_NAME", NCS::CString(psFileInfo->pFileMetaData->sAcquisitionSensorName)); + if (psFileInfo->pFileMetaData->sCompressionSoftware != NULL ) + GDALDataset::SetMetadataItem("FILE_METADATA_COMPRESSION_SOFTWARE", NCS::CString(psFileInfo->pFileMetaData->sCompressionSoftware)); + if (psFileInfo->pFileMetaData->sAuthor != NULL ) + GDALDataset::SetMetadataItem("FILE_METADATA_AUTHOR", NCS::CString(psFileInfo->pFileMetaData->sAuthor)); + if (psFileInfo->pFileMetaData->sCopyright != NULL ) + GDALDataset::SetMetadataItem("FILE_METADATA_COPYRIGHT", NCS::CString(psFileInfo->pFileMetaData->sCopyright)); + if (psFileInfo->pFileMetaData->sCompany != NULL ) + GDALDataset::SetMetadataItem("FILE_METADATA_COMPANY", NCS::CString(psFileInfo->pFileMetaData->sCompany)); + if (psFileInfo->pFileMetaData->sEmail != NULL ) + GDALDataset::SetMetadataItem("FILE_METADATA_EMAIL", NCS::CString(psFileInfo->pFileMetaData->sEmail)); + if (psFileInfo->pFileMetaData->sAddress != NULL ) + GDALDataset::SetMetadataItem("FILE_METADATA_ADDRESS", NCS::CString(psFileInfo->pFileMetaData->sAddress)); + if (psFileInfo->pFileMetaData->sTelephone != NULL ) + GDALDataset::SetMetadataItem("FILE_METADATA_TELEPHONE", NCS::CString(psFileInfo->pFileMetaData->sTelephone)); +} + +/************************************************************************/ +/* WriteFileMetaData() */ +/************************************************************************/ + +void ECWDataset::WriteFileMetaData(NCSFileMetaData* pFileMetaDataCopy) +{ + if (!bFileMetaDataDirty ) + return; + + CPLAssert(eAccess == GA_Update); + CPLAssert(!bIsJPEG2000); + + bFileMetaDataDirty = FALSE; + + NCSFileView *psFileView = NULL; + NCSError eErr; + + psFileView = NCSEditOpen( GetDescription() ); + if (psFileView == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "NCSEditOpen() failed"); + return; + } + + eErr = NCSEditSetFileMetaData(psFileView, pFileMetaDataCopy); + if( eErr != NCS_SUCCESS ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "NCSEditSetFileMetaData() failed : %s", + NCSGetLastErrorText(eErr)); + } + + NCSEditFlushAll(psFileView); + NCSEditClose(psFileView); +} + +#endif +/************************************************************************/ +/* ECW2WKTProjection() */ +/* */ +/* Set the dataset pszProjection string in OGC WKT format by */ +/* looking up the ECW (GDT) coordinate system info in */ +/* ecw_cs.wkt support data file. */ +/* */ +/* This code is likely still broken in some circumstances. For */ +/* instance, I haven't been careful about changing the linear */ +/* projection parameters (false easting/northing) if the units */ +/* is feet. Lots of cases missing here, and in ecw_cs.wkt. */ +/************************************************************************/ + +void ECWDataset::ECW2WKTProjection() + +{ + if( psFileInfo == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Capture Geotransform. */ +/* */ +/* We will try to ignore the provided file information if it is */ +/* origin (0,0) and pixel size (1,1). I think sometimes I have */ +/* also seen pixel increments of 0 on invalid datasets. */ +/* -------------------------------------------------------------------- */ + if( psFileInfo->fOriginX != 0.0 + || psFileInfo->fOriginY != 0.0 + || (psFileInfo->fCellIncrementX != 0.0 + && psFileInfo->fCellIncrementX != 1.0) + || (psFileInfo->fCellIncrementY != 0.0 + && psFileInfo->fCellIncrementY != 1.0) ) + { + bGeoTransformValid = TRUE; + + adfGeoTransform[0] = psFileInfo->fOriginX; + adfGeoTransform[1] = psFileInfo->fCellIncrementX; + adfGeoTransform[2] = 0.0; + + adfGeoTransform[3] = psFileInfo->fOriginY; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = -fabs(psFileInfo->fCellIncrementY); + } + +/* -------------------------------------------------------------------- */ +/* do we have projection and datum? */ +/* -------------------------------------------------------------------- */ + CPLString osUnits = ECWTranslateFromCellSizeUnits(psFileInfo->eCellSizeUnits); + + CPLDebug( "ECW", "projection=%s, datum=%s, units=%s", + psFileInfo->szProjection, psFileInfo->szDatum, + osUnits.c_str()); + + if( EQUAL(psFileInfo->szProjection,"RAW") ) + return; + +/* -------------------------------------------------------------------- */ +/* Set projection if we have it. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + + /* For backward-compatible with previous behaviour. Should we only */ + /* restrict to those 2 values ? */ + if (psFileInfo->eCellSizeUnits != ECW_CELL_UNITS_METERS && + psFileInfo->eCellSizeUnits != ECW_CELL_UNITS_FEET) + osUnits = ECWTranslateFromCellSizeUnits(ECW_CELL_UNITS_METERS); + + m_osDatumCode = psFileInfo->szDatum; + m_osProjCode = psFileInfo->szProjection; + m_osUnitsCode = osUnits; + if( oSRS.importFromERM( psFileInfo->szProjection, + psFileInfo->szDatum, + osUnits ) == OGRERR_NONE ) + { + oSRS.exportToWkt( &pszProjection ); + } + + CPLErrorReset(); /* see #4187 */ +} + +/************************************************************************/ +/* ECWTranslateFromWKT() */ +/************************************************************************/ + +int ECWTranslateFromWKT( const char *pszWKT, + char *pszProjection, + int nProjectionLen, + char *pszDatum, + int nDatumLen, + char *pszUnits) + +{ + OGRSpatialReference oSRS; + char *pszWKTIn = (char *) pszWKT; + + strcpy( pszProjection, "RAW" ); + strcpy( pszDatum, "RAW" ); + strcpy( pszUnits, "METERS" ); + + if( pszWKT == NULL || strlen(pszWKT) == 0 ) + return FALSE; + + oSRS.importFromWkt( &pszWKTIn ); + + if( oSRS.IsLocal() ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Do we have an overall EPSG number for this coordinate system? */ +/* -------------------------------------------------------------------- */ + const char *pszAuthorityCode = NULL; + const char *pszAuthorityName = NULL; + UINT32 nEPSGCode = 0; + + if( oSRS.IsProjected() ) + { + pszAuthorityCode = oSRS.GetAuthorityCode( "PROJCS" ); + pszAuthorityName = oSRS.GetAuthorityName( "PROJCS" ); + } + else if( oSRS.IsGeographic() ) + { + pszAuthorityCode = oSRS.GetAuthorityCode( "GEOGCS" ); + pszAuthorityName = oSRS.GetAuthorityName( "GEOGCS" ); + } + + if( pszAuthorityName != NULL && EQUAL(pszAuthorityName,"EPSG") + && pszAuthorityCode != NULL && atoi(pszAuthorityCode) > 0 ) + nEPSGCode = (UINT32) atoi(pszAuthorityCode); + + if( nEPSGCode != 0 ) + { + char *pszEPSGProj = NULL, *pszEPSGDatum = NULL; + CNCSError oErr; + + oErr = + CNCSJP2FileView::GetProjectionAndDatum( atoi(pszAuthorityCode), + &pszEPSGProj, &pszEPSGDatum ); + + CPLDebug( "ECW", "GetGDTProjDat(%d) = %s/%s", + atoi(pszAuthorityCode), + pszEPSGProj ? pszEPSGProj : "(null)", + pszEPSGDatum ? pszEPSGDatum : "(null)"); + + if( oErr.GetErrorNumber() == NCS_SUCCESS + && pszEPSGProj != NULL && pszEPSGDatum != NULL ) + { + strncpy( pszProjection, pszEPSGProj, nProjectionLen ); + strncpy( pszDatum, pszEPSGDatum, nDatumLen ); + pszProjection[nProjectionLen - 1] = 0; + pszDatum[nDatumLen - 1] = 0; + NCSFree( pszEPSGProj ); + NCSFree( pszEPSGDatum ); + return TRUE; + } + + NCSFree( pszEPSGProj ); + NCSFree( pszEPSGDatum ); + + } + +/* -------------------------------------------------------------------- */ +/* Fallback to translating based on the ecw_cs.wkt file, and */ +/* various jiffy rules. */ +/* -------------------------------------------------------------------- */ + + return oSRS.exportToERM( pszProjection, pszDatum, pszUnits ) == OGRERR_NONE; +} + +/************************************************************************/ +/* ECWTranslateToCellSizeUnits() */ +/************************************************************************/ + +CellSizeUnits ECWTranslateToCellSizeUnits(const char* pszUnits) +{ + if (EQUAL(pszUnits, "METERS")) + return ECW_CELL_UNITS_METERS; + else if (EQUAL(pszUnits, "DEGREES")) + return ECW_CELL_UNITS_DEGREES; + else if (EQUAL(pszUnits, "FEET")) + return ECW_CELL_UNITS_FEET; + else if (EQUAL(pszUnits, "UNKNOWN")) + return ECW_CELL_UNITS_UNKNOWN; + else if (EQUAL(pszUnits, "INVALID")) + return ECW_CELL_UNITS_INVALID; + else + { + CPLError(CE_Warning, CPLE_AppDefined, "Unrecognized value for UNITS : %s", pszUnits); + return ECW_CELL_UNITS_INVALID; + } +} + +/************************************************************************/ +/* ECWGetColorInterpretationByName() */ +/************************************************************************/ + +GDALColorInterp ECWGetColorInterpretationByName(const char *pszName) +{ + if (EQUAL(pszName, NCS_BANDDESC_AllOpacity)) + return GCI_AlphaBand; + else if (EQUAL(pszName, NCS_BANDDESC_Blue)) + return GCI_BlueBand; + else if (EQUAL(pszName, NCS_BANDDESC_Green)) + return GCI_GreenBand; + else if (EQUAL(pszName, NCS_BANDDESC_Red)) + return GCI_RedBand; + else if (EQUAL(pszName, NCS_BANDDESC_Greyscale)) + return GCI_GrayIndex; + else if (EQUAL(pszName, NCS_BANDDESC_GreyscaleOpacity)) + return GCI_AlphaBand; + return GCI_Undefined; +} + +/************************************************************************/ +/* ECWGetColorInterpretationName() */ +/************************************************************************/ + +const char* ECWGetColorInterpretationName(GDALColorInterp eColorInterpretation, int nBandNumber) +{ + const char *result; + switch (eColorInterpretation){ + case GCI_AlphaBand: + result = NCS_BANDDESC_AllOpacity; + break; + case GCI_GrayIndex: + result = NCS_BANDDESC_Greyscale; + break; + case GCI_RedBand: + case GCI_GreenBand: + case GCI_BlueBand: + result = GDALGetColorInterpretationName(eColorInterpretation); + break; + case GCI_Undefined: + if (nBandNumber <=3){ + if (nBandNumber == 0 ) { + result = "Red"; + }else if (nBandNumber == 1) { + result = "Green"; + }else if (nBandNumber == 2) { + result = "Blue"; + } + } + result = CPLSPrintf(NCS_BANDDESC_Band,nBandNumber + 1); + break; + default: + result = CPLSPrintf(NCS_BANDDESC_Band,nBandNumber + 1); + } + return result; +} + +/************************************************************************/ +/* ECWGetColorSpaceName() */ +/************************************************************************/ + +const char* ECWGetColorSpaceName(NCSFileColorSpace colorSpace) +{ + switch (colorSpace) + { + case NCSCS_NONE: + return "NONE"; break; + case NCSCS_GREYSCALE: + return "GREYSCALE"; break; + case NCSCS_YUV: + return "YUV"; break; + case NCSCS_MULTIBAND: + return "MULTIBAND"; break; + case NCSCS_sRGB: + return "RGB"; break; + case NCSCS_YCbCr: + return "YCbCr"; break; + default: + return "unrecognised"; + } +} +/************************************************************************/ +/* ECWTranslateFromCellSizeUnits() */ +/************************************************************************/ + +const char* ECWTranslateFromCellSizeUnits(CellSizeUnits eUnits) +{ + if (eUnits == ECW_CELL_UNITS_METERS) + return "METERS"; + else if (eUnits == ECW_CELL_UNITS_DEGREES) + return "DEGREES"; + else if (eUnits == ECW_CELL_UNITS_FEET) + return "FEET"; + else if (eUnits == ECW_CELL_UNITS_UNKNOWN) + return "UNKNOWN"; + else + return "INVALID"; +} + +#endif /* def FRMT_ecw */ + +/************************************************************************/ +/* ECWInitialize() */ +/* */ +/* Initialize NCS library. We try to defer this as late as */ +/* possible since de-initializing it seems to be expensive/slow */ +/* on some system. */ +/************************************************************************/ + +void ECWInitialize() + +{ + CPLMutexHolder oHolder( &hECWDatasetMutex ); + + if( bNCSInitialized ) + return; + +#ifndef WIN32 + NCSecwInit(); +#endif + bNCSInitialized = TRUE; + +/* -------------------------------------------------------------------- */ +/* This will disable automatic conversion of YCbCr to RGB by */ +/* the toolkit. */ +/* -------------------------------------------------------------------- */ + if( !CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB","YES") ) ) + NCSecwSetConfig(NCSCFG_JP2_MANAGE_ICC, FALSE); +#if ECWSDK_VERSION>= 50 + NCSecwSetConfig(NCSCFG_ECWP_CLIENT_HTTP_USER_AGENT, "ECW GDAL Driver/" NCS_ECWJP2_FULL_VERSION_STRING_DOT_DEL); +#endif +/* -------------------------------------------------------------------- */ +/* Initialize cache memory limit. Default is apparently 1/4 RAM. */ +/* -------------------------------------------------------------------- */ + const char *pszEcwCacheSize = + CPLGetConfigOption("GDAL_ECW_CACHE_MAXMEM",NULL); + if( pszEcwCacheSize == NULL ) + pszEcwCacheSize = CPLGetConfigOption("ECW_CACHE_MAXMEM",NULL); + + if( pszEcwCacheSize != NULL ) + NCSecwSetConfig(NCSCFG_CACHE_MAXMEM, (UINT32) atoi(pszEcwCacheSize) ); + + /* -------------------------------------------------------------------- */ + /* Version 3.x and 4.x of the ECWJP2 SDK did not resolve datum and */ + /* projection to EPSG code using internal mapping. */ + /* Version 5.x do so we provide means to achieve old */ + /* behaviour. */ + /* -------------------------------------------------------------------- */ + #if ECWSDK_VERSION >= 50 + if( CSLTestBoolean( CPLGetConfigOption("ECW_DO_NOT_RESOLVE_DATUM_PROJECTION","NO") ) == TRUE) + NCSecwSetConfig(NCSCFG_PROJECTION_FORMAT, NCS_PROJECTION_ERMAPPER_FORMAT); + #endif +/* -------------------------------------------------------------------- */ +/* Allow configuration of a local cache based on configuration */ +/* options. Setting the location turns things on. */ +/* -------------------------------------------------------------------- */ + const char *pszOpt; + +#if ECWSDK_VERSION >= 40 + pszOpt = CPLGetConfigOption( "ECWP_CACHE_SIZE_MB", NULL ); + if( pszOpt ) + NCSecwSetConfig( NCSCFG_ECWP_CACHE_SIZE_MB, (INT32) atoi( pszOpt ) ); + + pszOpt = CPLGetConfigOption( "ECWP_CACHE_LOCATION", NULL ); + if( pszOpt ) + { + NCSecwSetConfig( NCSCFG_ECWP_CACHE_LOCATION, pszOpt ); + NCSecwSetConfig( NCSCFG_ECWP_CACHE_ENABLED, (BOOLEAN) TRUE ); + } +#endif + +/* -------------------------------------------------------------------- */ +/* Various other configuration items. */ +/* -------------------------------------------------------------------- */ + pszOpt = CPLGetConfigOption( "ECWP_BLOCKING_TIME_MS", NULL ); + if( pszOpt ) + NCSecwSetConfig( NCSCFG_BLOCKING_TIME_MS, + (NCSTimeStampMs) atoi(pszOpt) ); + + // I believe 10s means we wait for complete data back from + // ECWP almost all the time which is good for our blocking model. + pszOpt = CPLGetConfigOption( "ECWP_REFRESH_TIME_MS", "10000" ); + if( pszOpt ) + NCSecwSetConfig( NCSCFG_REFRESH_TIME_MS, + (NCSTimeStampMs) atoi(pszOpt) ); + + pszOpt = CPLGetConfigOption( "ECW_TEXTURE_DITHER", NULL ); + if( pszOpt ) + NCSecwSetConfig( NCSCFG_TEXTURE_DITHER, + (BOOLEAN) CSLTestBoolean( pszOpt ) ); + + + pszOpt = CPLGetConfigOption( "ECW_FORCE_FILE_REOPEN", NULL ); + if( pszOpt ) + NCSecwSetConfig( NCSCFG_FORCE_FILE_REOPEN, + (BOOLEAN) CSLTestBoolean( pszOpt ) ); + + pszOpt = CPLGetConfigOption( "ECW_CACHE_MAXOPEN", NULL ); + if( pszOpt ) + NCSecwSetConfig( NCSCFG_CACHE_MAXOPEN, (UINT32) atoi(pszOpt) ); + +#if ECWSDK_VERSION >= 40 + pszOpt = CPLGetConfigOption( "ECW_AUTOGEN_J2I", NULL ); + if( pszOpt ) + NCSecwSetConfig( NCSCFG_JP2_AUTOGEN_J2I, + (BOOLEAN) CSLTestBoolean( pszOpt ) ); + + pszOpt = CPLGetConfigOption( "ECW_OPTIMIZE_USE_NEAREST_NEIGHBOUR", NULL ); + if( pszOpt ) + NCSecwSetConfig( NCSCFG_OPTIMIZE_USE_NEAREST_NEIGHBOUR, + (BOOLEAN) CSLTestBoolean( pszOpt ) ); + + + pszOpt = CPLGetConfigOption( "ECW_RESILIENT_DECODING", NULL ); + if( pszOpt ) + NCSecwSetConfig( NCSCFG_RESILIENT_DECODING, + (BOOLEAN) CSLTestBoolean( pszOpt ) ); +#endif +} + +/************************************************************************/ +/* GDALDeregister_ECW() */ +/************************************************************************/ + +void GDALDeregister_ECW( GDALDriver * ) + +{ + /* For unknown reason, this cleanup can take up to 3 seconds (see #3134) for SDK 3.3. */ + /* Not worth it */ +#if ECWSDK_VERSION >= 50 +#ifndef WIN32 + if( bNCSInitialized ) + { + bNCSInitialized = FALSE; + if( !GDALIsInGlobalDestructor() ) + { + NCSecwShutdown(); + } + } +#endif +#endif + + if( hECWDatasetMutex != NULL ) + { + CPLDestroyMutex( hECWDatasetMutex ); + hECWDatasetMutex = NULL; + } +} + +/************************************************************************/ +/* GDALRegister_ECW() */ +/************************************************************************/ + +/* Needed for v4.3 and v5.0 */ +#if !defined(NCS_ECWSDK_VERSION_STRING) && defined(NCS_ECWJP2_VERSION_STRING) +#define NCS_ECWSDK_VERSION_STRING NCS_ECWJP2_VERSION_STRING +#endif + +void GDALRegister_ECW() + +{ +#ifdef FRMT_ecw + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("ECW driver")) + return; + + if( GDALGetDriverByName( "ECW" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ECW" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + + CPLString osLongName = "ERDAS Compressed Wavelets (SDK "; + +#ifdef NCS_ECWSDK_VERSION_STRING + osLongName += NCS_ECWSDK_VERSION_STRING; +#else + osLongName += "3.x"; +#endif + osLongName += ")"; + + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, osLongName ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_ecw.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "ecw" ); + + poDriver->pfnIdentify = ECWDataset::IdentifyECW; + poDriver->pfnOpen = ECWDataset::OpenECW; + poDriver->pfnUnloadDriver = GDALDeregister_ECW; +#ifdef HAVE_COMPRESS +// The create method does not work with SDK 3.3 ( crash in CNCSJP2FileView::WriteLineBIL() due to m_pFile being NULL ) +#if ECWSDK_VERSION >= 50 + poDriver->pfnCreate = ECWCreateECW; +#endif + poDriver->pfnCreateCopy = ECWCreateCopyECW; +#if ECWSDK_VERSION >= 50 + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16" ); +#else + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte" ); +#endif + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); +#else + /* In read-only mode, we support VirtualIO. This is not the case */ + /* for ECWCreateCopyECW() */ + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); +#endif + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +#endif /* def FRMT_ecw */ +} + +/************************************************************************/ +/* GDALRegister_ECW_JP2ECW() */ +/* */ +/* This function exists so that when built as a plugin, there */ +/* is a function that will register both drivers. */ +/************************************************************************/ + +void GDALRegister_ECW_JP2ECW() + +{ + GDALRegister_ECW(); + GDALRegister_JP2ECW(); +} + +/************************************************************************/ +/* ECWDatasetOpenJPEG2000() */ +/************************************************************************/ +GDALDataset* ECWDatasetOpenJPEG2000(GDALOpenInfo* poOpenInfo) +{ + return ECWDataset::OpenJPEG2000(poOpenInfo); +} + +/************************************************************************/ +/* GDALRegister_JP2ECW() */ +/************************************************************************/ +void GDALRegister_JP2ECW() + +{ +#ifdef FRMT_ecw + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("JP2ECW driver")) + return; + + if( GDALGetDriverByName( "JP2ECW" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "JP2ECW" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + + CPLString osLongName = "ERDAS JPEG2000 (SDK "; + +#ifdef NCS_ECWSDK_VERSION_STRING + osLongName += NCS_ECWSDK_VERSION_STRING; +#else + osLongName += "3.x"; +#endif + osLongName += ")"; + + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, osLongName ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_jp2ecw.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jp2" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = ECWDataset::IdentifyJPEG2000; + poDriver->pfnOpen = ECWDataset::OpenJPEG2000; + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " ); + +#ifdef HAVE_COMPRESS + poDriver->pfnCreate = ECWCreateJPEG2000; + poDriver->pfnCreateCopy = ECWCreateCopyJPEG2000; + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16 Int16 UInt32 Int32 Float32 Float64" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " + +#if ECWSDK_VERSION < 40 +" " +" " +" " ); +#endif + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +#endif /* def FRMT_ecw */ +} diff --git a/bazaar/plugin/gdal/frmts/ecw/frmt_ecw.html b/bazaar/plugin/gdal/frmts/ecw/frmt_ecw.html new file mode 100644 index 000000000..cc7ffd85d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ecw/frmt_ecw.html @@ -0,0 +1,197 @@ + + +ECW -- Enhanced Compressed Wavelets (.ecw) + + + + +

ECW -- Enhanced Compressed Wavelets (.ecw)

+ +GDAL supports reading and writing ECW files using the ERDAS ECW/JP2 SDK developed by +Hexagon Geospatial (formerly Intergraph, ERDAS, ERMapper). Support is optional and +requires linking in the libraries available from the ECW/JP2 SDK Download page.

+ +

Licensing

+ +The ERDAS ECW/JP2 SDK v5.x is available under multiple license types. For Desktop usage, decoding +any sized ECW/JP2 image is made available free of charge. To compress, deploy on a Server platform, or decode +unlimited sized files on Mobile platforms a license must be purchased from Hexagon Geospatial.

+ + +

History

+
    +
  • v3.x - Last release, 2006
  • +
  • v4.x - Last release, 2012
  • +
  • v5.x - Active development, 2013 - current
  • +
+ +

Creation Options

+ +The ERDAS ECW/JP2 v4.x and v5.x SDK is only free for image decompression. To +compress images it is necessary to build with the read/write SDK and to +provide an OEM licensing key at runtime which may be purchased from ERDAS.

+ +For those still using the ECW 3.3 SDK, images less than 500MB may be +compressed for free, while larger images require licensing from ERDAS. See +the licensing agreement and the LARGE_OK option.

+ +Files to be compressed into ECW format must also +be at least 128x128. ECW currently only supports 8 bits per channel for +ECW Version 2 files. ECW Version 3 files supports 16 bits per channel +(as Uint16 data type). Please see Creation options to enable ECW V3 files writing

+ +When writing coordinate system information to ECW files, many less +common coordinate systems are not mapped properly. If you know the +ECW name for the coordinate system you can force it to be set at +creation time with the PROJ and DATUM creation options.

+ +ECW format does not support creation of overviews since the ECW format +is already considered to be optimized for "arbitrary overviews".

+ +

Creation Options:

+ +
    + +
  • LARGE_OK=YES: (v3.x SDK only) Allow compressing files larger than 500MB in accordance with EULA terms. Deprecated since v4.x and +replaced by ECW_ENCODE_KEY & ECW_ENCODE_COMPANY.

    + +

  • ECW_ENCODE_KEY=key: (v4.x SDK or higher) Provide the OEM encoding key to unlock encoding capability +up to the licensed gigapixel limit. The key is is approximately 129 hex digits long. The Company and Key must match and must be re-generated with each minor release of the SDK. It may also be provided globally as a configuration option.

    + +

  • ECW_ENCODE_COMPANY=name: (v4.x SDK or higher) Provide the name of the company in the issued +OEM key (see ECW_ENCODE_KEY). The Company and Key must match and must be re-generated with each minor release of the SDK. It may also be provided +globally as a configuration option.

    + +

  • TARGET=percent: Set the target size reduction as a percentage of +the original. If not provided defaults to 90% for greyscale images, and 95% +for RGB images.

    + +

  • PROJ=name: Name of the ECW projection string to use. +Common examples are NUTM11, or GEODETIC.

    + +

  • DATUM=name: Name of the ECW datum string to use. +Common examples are WGS84 or NAD83.

    + +

  • UNITS=name: (GDAL >= 1.9.0) Name of the ECW projection units to use : +METERS (default) or FEET (us-foot).

    + +

  • ECW_FORMAT_VERSION=2/3: (GDAL >= 1.10.0) When build with the ECW 5.x SDK this option can be set to +allow ECW Version 3 files to be created. Default, 2 to retain widest compatibility.

    + +

+ +

Configuration Options

+ +The ERDAS ECW SDK supports a variety of +runtime configuration +options to control various features. Most of these are exposed as GDAL +configuration options. See the ECW SDK documentation for full details on the +meaning of these options. + +
    +
  • ECW_CACHE_MAXMEM=bytes: maximum bytes of RAM used for in-memory +caching. If not set, up to one quarter of physical RAM will be used by the +SDK for in-memory caching.

    + +

  • ECWP_CACHE_LOCATION=path: Path to a directory to use for caching +ECWP results. If unset ECWP caching will not be enabled.

    + +

  • ECWP_CACHE_SIZE_MB=number_of_megabytes: The maximum +number of megabytes of space in the ECWP_CACHE_LOCATION to be used for +caching ECWP results.

    + +

  • ECWP_BLOCKING_TIME_MS: time an ecwp:// blocking read will wait +before returning - default 10000 ms.

    + +

  • ECWP_REFRESH_TIME_MS: time delay between blocks arriving and the +next refresh callback - default 10000 ms. For the purposes of GDAL this is +the amount of time the driver will wait for more data on an ecwp connection +for which the final result has not yet been returned. If set small then +RasterIO() requests will often produce low resolution results.

    + +

  • ECW_TEXTURE_DITHER=TRUE/FALSE: This may be set to FALSE to disable +dithering when decompressing ECW files. Defaults to TRUE.

    + +

  • ECW_FORCE_FILE_REOPEN=TRUE/FALSE: This may be set to TRUE to +force open a file handle for each file for each connection made. Defaults to +FALSE.

    + +

  • ECW_CACHE_MAXOPEN=number: The maximum number of files to keep +open for ECW file handle caching. Defaults to unlimited.

    + +

  • ECW_RESILIENT_DECODING=TRUE/FALSE: Controls whether the reader +should be forgiving of errors in a file, trying to return as much data as is +available. Defaults to TRUE. If set to FALSE an invalid file will result +in an error.

    + +

+ +

ECW Version 3 Files

+(Starting with GDAL 1.10.0)

+ +ECW 5.x SDK introduces a new file format version which, +

    +
  1. Storage of data statistics, histograms, metadata, RPC information within the file header
  2. +
  3. Support for UInt16 data type
  4. +
  5. Ability to update regions within an existing ECW v3 file
  6. +
  7. Introduces other space saving optimizations
  8. +
+ +Note: This version is not backward compatible and will fail to decode in v3.x or v4.x ECW/JP2 SDK's. The File VERSION Metadata will advertise whether the file is ECW v2 or ECW v3. + +

ECWP

+In addition to local files, this driver also supports access to streaming network +imagery services using the proprietary "ECWP" protocol from the ERDAS APOLLO product. Use the full ecwp:// prefixed dataset url +as input. When built with ECW/JP2 SDK v4.1 or newer it +is also possible to take advantage of + +RFC 24 for asynchronous / progressive streaming access to ECWP services.

+ +

Metadata / Georeferencing

+ +(Starting with GDAL 1.9.0)

+ +The PROJ, DATUM and UNITS found in the ECW header are +reported in the ECW metadata domain. They can also be set with the SetMetadataItem() +method, in order to update the header information of an existing ECW file, +opened in update mode, without modifying the imagery.

+ +The geotransform and projection can also be modified with the SetGeoTransform() +and SetProjection() methods. If the projection is set with SetProjection() and +the PROJ, DATUM or UNITS with SetMetadataItem(), the later values will override the values +built from the projection string.

+ +

File Metadata Keys:

+ +
    +
  • FILE_METADATA_ACQUISITION_DATE +
  • FILE_METADATA_ACQUISITION_SENSOR_NAME +
  • FILE_METADATA_ADDRESS +
  • FILE_METADATA_AUTHOR +
  • FILE_METADATA_CLASSIFICATION +
  • FILE_METADATA_COMPANY - should be set to ECW_ENCODE_COMPANY +
  • FILE_METADATA_COMPRESSION_SOFTWARE - updated during recompression +
  • FILE_METADATA_COPYRIGHT +
  • FILE_METADATA_EMAIL +
  • FILE_METADATA_TELEPHONE +
  • CLOCKWISE_ROTATION_DEG +
  • COLORSPACE +
  • COMPRESSION_DATE +
  • COMPRESSION_RATE_ACTUAL +
  • COMPRESSION_RATE_TARGET +
  • VERSION +
+ +

See Also

+ + + + + diff --git a/bazaar/plugin/gdal/frmts/ecw/frmt_jp2ecw.html b/bazaar/plugin/gdal/frmts/ecw/frmt_jp2ecw.html new file mode 100644 index 000000000..c4bd5a074 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ecw/frmt_jp2ecw.html @@ -0,0 +1,220 @@ + + +JP2ECW -- ERDAS JPEG2000 (.jp2) + + + + +

JP2ECW -- ERDAS JPEG2000 (.jp2)

+ +GDAL supports reading and writing JPEG2000 files using the ERDAS ECW/JP2 SDK developed by +Hexagon Geospatial (formerly Intergraph, ERDAS, ERMapper). Support is optional and +requires linking in the libraries available from the ECW/JP2 SDK Download page.

+ +Coordinate system and georeferencing transformations are read, and +some degree of support is included for GeoJP2 (tm) (GeoTIFF-in-JPEG2000), +ERDAS GML-in-JPEG2000, and the new GML-in-JPEG2000 specification developed +at OGC.

+ +

Licensing

+ +The ERDAS ECW/JP2 SDK v5.x is available under multiple license types. For Desktop usage, decoding +any sized ECW/JP2 image is made available free of charge. To compress, deploy on a Server platform, or decode +unlimited sized files on Mobile platforms a license must be purchased from Hexagon Geospatial.

+ + +

History

+
    +
  • v3.x - Last release, 2006
  • +
  • v4.x - Last release, 2012
  • +
  • v5.x - Active development, 2013 - current
  • +
+ +

Option Options

+ +(GDAL >= 2.0 ) + +The following open option is available: +
    +
  • 1BIT_ALPHA_PROMOTION=YES/NO: Whether a 1-bit alpha channel should be promoted to 8-bit. +Defaults to YES.

  • +
+ +

Creation Options:

+ +Note: Only Licensing and compression target need to be specified. The ECW/JP2 SDK will default all other options to recommended settings based on the input characteristics. +Changing other options can substantially impact decoding speed and compatibility with other JPEG2000 toolkits. + +
    + +
  • LARGE_OK=YES: (v3.x SDK only) Allow compressing files larger than 500MB in accordance with EULA terms. Deprecated since v4.x and +replaced by ECW_ENCODE_KEY & ECW_ENCODE_COMPANY.

    + +

  • ECW_ENCODE_KEY=key: (v4.x SDK or higher) Provide the OEM encoding key to unlock encoding capability +up to the licensed gigapixel limit. The key is is approximately 129 hex digits long. The Company and Key must match and must be re-generated with each minor release of the SDK. It may also be provided globally as a configuration option.

    + +

  • ECW_ENCODE_COMPANY=name: (v4.x SDK or higher) Provide the name of the company in the issued +OEM key (see ECW_ENCODE_KEY). The Company and Key must match and must be re-generated with each minor release of the SDK. It may also be provided +globally as a configuration option.

    + +

  • TARGET=percent: Set the target size reduction as a percentage of +the original. If not provided defaults to 75 for an 75% reduction. TARGET=0 +uses lossless compression.

    + +

  • PROJ=name: Name of the ECW projection string to use. +Common examples are NUTM11, or GEODETIC.

    + +

  • DATUM=name: Name of the ECW datum string to use. +Common examples are WGS84 or NAD83.

    + +

  • GMLJP2=YES/NO: Indicates whether a GML box +conforming to the OGC GML in JPEG2000 specification should be included in the +file. Unless GMLJP2V2_DEF is used, the version of the GMLJP2 box will be +version 1. Defaults to YES.

    + +

  • GMLJP2V2_DEF=filename: (Starting with GDAL 2.0) Indicates whether a GML box +conforming to the +OGC GML in JPEG2000, version 2 specification should be included in the +file. filename must point to a file with a JSon content that defines how +the GMLJP2 v2 box should be built. See +GMLJP2v2 definition file section in documentation of the JP2OpenJPEG driver +for the syntax of the JSon configuration file. +It is also possible to directly pass the JSon content inlined as a string. +If filename is just set to YES, a minimal instance will be built.

    + +

  • GeoJP2=YES/NO: Indicates whether a UUID/GeoTIFF box conforming to the GeoJP2 (GeoTIFF in JPEG2000) specification should be included in the file. Defaults to YES.

    + +

  • PROFILE=profile: One of BASELINE_0, BASELINE_1, BASELINE_2, +NPJE or EPJE. Review the ECW SDK documentation for details on profile +meanings.

    + +

  • PROGRESSION=LRCP/RLCP/RPCL: Set the progression order with +which the JPEG2000 codestream is written. (Default, RPCL)

    + +

  • CODESTREAM_ONLY=YES/NO: If set to YES, only the compressed +imagery code stream will be written. If NO a JP2 package +will be written around the code stream including a variety of meta +information. (Default, NO)

    + + +

  • LEVELS=n: Resolution levels in pyramid (by default so many that the size of the smallest thumbnail image is 64x64 pixels at maximum)

    +

  • LAYERS=n: Quality layers (default, 1)

    +

  • PRECINCT_WIDTH=n: Precinct Width (default, 64)

    +

  • PRECINCT_HEIGHT=n: Precinct Height (default 64)

    +

  • TILE_WIDTH=n: Tile Width (default, image width eg. 1 tile). Apart from GeoTIFF, in JPEG2000 tiling is not critical for speed if precincts are used. The minimum tile size allowed by the standard is 1024x1024 pixels.

    +

  • TILE_HEIGHT=n: Tile Height (default, image height eg. 1 tile)

    +

  • INCLUDE_SOP=YES/NO: Output Start of Packet Marker (default false)

    +

  • INCLUDE_EPH=YES/NO: Output End of Packet Header Marker (default true)

    +

  • DECOMPRESS_LAYERS=n: The number of quality layers to decode

    +

  • DECOMPRESS_RECONSTRUCTION_PARAMETER=n: IRREVERSIBLE_9x7 or REVERSIBLE_5x3

    + +

  • WRITE_METADATA=YES/NO: (GDAL >= 2.0) Whether metadata should be +written, in a dedicated JP2 XML box. Defaults to NO. +The content of the XML box will be like: +

    +<GDALMultiDomainMetadata>
    +  <Metadata>
    +    <MDI key="foo">bar</MDI>
    +  </Metadata>
    +  <Metadata domain='aux_domain'>
    +    <MDI key="foo">bar</MDI>
    +  </Metadata>
    +  <Metadata domain='a_xml_domain' format='xml'>
    +    <arbitrary_xml_content>
    +    </arbitrary_xml_content>
    +  </Metadata>
    +</GDALMultiDomainMetadata>
    +
    +If there are metadata domain whose name starts with "xml:BOX_", they will be +written each as separate JP2 XML box.

    +If there is a metadata domain whose name is "xml:XMP", its content will be +written as a JP2 UUID XMP box. +

  • + +
  • MAIN_MD_DOMAIN_ONLY=YES/NO: (GDAL >= 2.0) +(Only if WRITE_METADATA=YES) Whether only metadata from the main domain should +be written. Defaults to NO. +

  • + +
+ +"JPEG2000 format does not support creation of GDAL overviews since the format +is already considered to be optimized for "arbitrary overviews". +JP2ECW driver also arranges JP2 codestream to allow optimal access to power of two overviews. +This is controlled with the creation option LEVELS." +

+ +

Configuration Options

+ +The ERDAS ECW/JP2 SDK supports a variety of +runtime configuration +options to control various features. Most of these are exposed as GDAL +configuration options. See the ECW/JP2 SDK documentation for full details on the +meaning of these options. + +
    +
  • ECW_CACHE_MAXMEM=bytes: maximum bytes of RAM used for in-memory +caching. If not set, up to one quarter of physical RAM will be used by the +SDK for in-memory caching.

    + +

  • ECW_TEXTURE_DITHER=TRUE/FALSE: This may be set to FALSE to disable +dithering when decompressing ECW files. Defaults to TRUE.

    + +

  • ECW_FORCE_FILE_REOPEN=TRUE/FALSE: This may be set to TRUE to +force open a file handle for each file for each connection made. Defaults to +FALSE.

    + +

  • ECW_CACHE_MAXOPEN=number: The maximum number of files to keep +open for ECW file handle caching. Defaults to unlimited.

    + +

  • ECW_AUTOGEN_J2I=TRUE/FALSE: Controls whether .j2i index files +should be created when opening jpeg2000 files. Defaults to TRUE.

    + +

  • ECW_RESILIENT_DECODING=TRUE/FALSE: Controls whether the reader +should be forgiving of errors in a file, trying to return as much data as is +available. Defaults to TRUE. If set to FALSE an invalid file will result +in an error.

    + +

+ +

Metadata

+ +Starting with GDAL 1.11.0, XMP metadata can be extracted from JPEG2000 files, and will be +stored as XML raw content in the xml:XMP metadata domain.

+ +ECW/JP2 SDK v5.1+ also advertises JPEG2000 structural information as generic File Metadata reported under "JPEG2000" metadata domain (-mdd):

+
    +
  • ALL_COMMENTS: Generic comment text field
  • +

  • PROFILE: Profile type (0,1,2). Refer to ECW/JP2 SDK documentation for more info
  • +

  • TILES_X: Number of tiles on X (horizontal) Axis
  • +

  • TILES_Y: Number of tiles on Y (vertical) Axis
  • +

  • TILE_WIDTH: Tile size on X Axis
  • +

  • TILE_HEIGHT: Tile size on Y Axis
  • +

  • PRECINCT_SIZE_X: Precinct size for each resolution level (smallest to largest) on X Axis
  • +

  • PRECINCT_SIZE_Y: Precinct size for each resolution level (smallest to largest) on Y Axis
  • +

  • CODE_BLOCK_SIZE_X: Code block size on X Axis
  • +

  • CODE_BLOCK_SIZE_Y: Code block size on Y Axis
  • +

  • PRECISION: Precision / Bit-depth of each component eg. 8,8,8 for 8bit 3 band imagery.
  • +

  • RESOLUTION_LEVELS: Number of resolution levels
  • +

  • QUALITY_LAYERS: Number of quality layers
  • +

  • PROGRESSION_ORDER: Progression order (RPCL, LRCP, CPRL, RLCP)
  • +

  • TRANSFORMATION_TYPE: Filter transformation used (9x7, 5x3)
  • +

  • USE_SOP: Start of Packet marker detected (TRUE/FALSE)
  • +

  • USE_EPH: End of Packet header marker detected (TRUE/FALSE)
  • +

  • GML_JP2_DATA: OGC GML GeoReferencing box detected (TRUE/FALSE)
  • +

  • COMPRESSION_RATE_TARGET: Target compression rate used on encoding
  • +

+ +

See Also

+ + + + + diff --git a/bazaar/plugin/gdal/frmts/ecw/gdal_ecw.h b/bazaar/plugin/gdal/frmts/ecw/gdal_ecw.h new file mode 100644 index 000000000..99c3b1858 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ecw/gdal_ecw.h @@ -0,0 +1,695 @@ +/****************************************************************************** + * $Id: ecwdataset.cpp 21486 2011-01-13 17:38:17Z warmerdam $ + * + * Project: GDAL + * Purpose: ECW (ERDAS Wavelet Compression Format) Driver Definitions + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001-2011, Frank Warmerdam + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_ECW_H_INCLUDED +#define GDAL_ECW_H_INCLUDED + +#include "gdaljp2abstractdataset.h" +#include "gdal_frmts.h" +#include "cpl_string.h" +#include "cpl_conv.h" +#include "cpl_multiproc.h" +#include "cpl_vsi.h" + +#undef NOISY_DEBUG + +#ifdef FRMT_ecw + +// The following is needed on 4.x+ to enable rw support. +#if defined(HAVE_COMPRESS) +# ifndef ECW_COMPRESS_RW_SDK_VERSION +# define ECW_COMPRESS_RW_SDK_VERSION +# endif +#endif + +#if defined(_MSC_VER) +# pragma warning(disable:4800) +#endif + +#include +#include +#include +#include +#include + +#ifdef HAVE_ECW_BUILDNUMBER_H +# include +# if !defined(ECW_VERSION) +# define ECWSDK_VERSION (NCS_ECWJP2_VER_MAJOR*10+NCS_ECWJP2_VER_MINOR) +# endif +#else +/* By default, assume 3.3 SDK Version. */ +# if !defined(ECWSDK_VERSION) +# define ECWSDK_VERSION 33 +# endif +#endif + +#if ECWSDK_VERSION < 40 + +#if !defined(NO_COMPRESS) && !defined(HAVE_COMPRESS) +# define HAVE_COMPRESS +#endif + +#else + #if ECWSDK_VERSION>=50 + #if ECWSDK_VERSION>=51 + #define JPEG2000_DOMAIN_NAME "JPEG2000" + #endif + #include + #include "NCSEcw/SDK/Box.h" + #else + #include + #endif +# define NCS_FASTCALL +#endif + +#if ECWSDK_VERSION >= 40 +#define SDK_CAN_DO_SUPERSAMPLING 1 +#endif + +#ifndef NCSFILEBASE_H +# include +#else +# undef CNCSJP2FileView +# define CNCSJP2FileView CNCSFile +#endif + +void ECWInitialize( void ); +GDALDataset* ECWDatasetOpenJPEG2000(GDALOpenInfo* poOpenInfo); +const char* ECWGetColorInterpretationName(GDALColorInterp eColorInterpretation, int nBandNumber); +GDALColorInterp ECWGetColorInterpretationByName(const char *pszName); +const char* ECWGetColorSpaceName(NCSFileColorSpace colorSpace); +#ifdef HAVE_COMPRESS +GDALDataset * +ECWCreateCopyECW( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); +GDALDataset * +ECWCreateCopyJPEG2000( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); + +GDALDataset * +ECWCreateECW( const char * pszFilename, int nXSize, int nYSize, int nBands, + GDALDataType eType, char **papszOptions ); +GDALDataset * +ECWCreateJPEG2000(const char *pszFilename, int nXSize, int nYSize, int nBands, + GDALDataType eType, char **papszOptions ); +#endif + +void ECWReportError(CNCSError& oErr, const char* pszMsg = ""); + +/************************************************************************/ +/* ==================================================================== */ +/* JP2Userbox */ +/* ==================================================================== */ +/************************************************************************/ +#ifdef HAVE_COMPRESS +#if ECWSDK_VERSION>=50 +class JP2UserBox : public CNCSSDKBox { +#else +class JP2UserBox : public CNCSJP2Box { +#endif +private: + int nDataLength; + unsigned char *pabyData; + +public: + JP2UserBox(); + + virtual ~JP2UserBox(); + +#if ECWSDK_VERSION >= 40 + virtual CNCSError Parse(NCS::SDK::CFileBase &JP2File, + NCS::CIOStream &Stream); + virtual CNCSError UnParse(NCS::SDK::CFileBase &JP2File, + NCS::CIOStream &Stream); +#else + virtual CNCSError Parse(class CNCSJP2File &JP2File, + CNCSJPCIOStream &Stream); + virtual CNCSError UnParse(class CNCSJP2File &JP2File, + CNCSJPCIOStream &Stream); +#endif + virtual void UpdateXLBox(void); + + void SetData( int nDataLength, const unsigned char *pabyDataIn ); + + int GetDataLength() { return nDataLength; } + unsigned char *GetData() { return pabyData; } +}; +#endif /* def HAVE_COMPRESS */ + +/************************************************************************/ +/* ==================================================================== */ +/* VSIIOStream */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIIOStream : public CNCSJPCIOStream + +{ + private: + char *m_Filename; + public: + + INT64 startOfJPData; + INT64 lengthOfJPData; + VSILFILE *fpVSIL; + BOOLEAN bWritable; + BOOLEAN bSeekable; + int nFileViewCount; + + int nCOMState; + int nCOMLength; + GByte abyCOMType[2]; + + VSIIOStream() : m_Filename(NULL){ + nFileViewCount = 0; + startOfJPData = 0; + lengthOfJPData = -1; + fpVSIL = NULL; + bWritable = false; + bSeekable = false; + if( CSLTestBoolean(CPLGetConfigOption("GDAL_ECW_WRITE_COMPRESSION_SOFTWARE", "YES")) ) + nCOMState = -1; + else + nCOMState = 0; + nCOMLength = 0; + abyCOMType[0] = 0; + abyCOMType[1] = 0; + } + virtual ~VSIIOStream() { + Close(); + if (m_Filename!=NULL){ + CPLFree(m_Filename); + } + } + + virtual CNCSError Close() { + CNCSError oErr = CNCSJPCIOStream::Close(); + if( fpVSIL != NULL ) + { + VSIFCloseL( fpVSIL ); + fpVSIL = NULL; + } + return oErr; + } + +#if ECWSDK_VERSION >= 40 + virtual VSIIOStream *Clone() { + + VSILFILE *fpNewVSIL = VSIFOpenL( m_Filename, "rb" ); + if (fpNewVSIL == NULL) + { + return NULL; + }else + { + VSIIOStream *pDst = new VSIIOStream(); + pDst->Access(fpNewVSIL, bWritable, bSeekable, m_Filename, startOfJPData, lengthOfJPData); + return pDst; + } + } +#endif /* ECWSDK_VERSION >= 4 */ + + CNCSError Access( VSILFILE *fpVSILIn, BOOLEAN bWrite, BOOLEAN bSeekableIn, + const char *pszFilename, + INT64 start, INT64 size = -1) { + + fpVSIL = fpVSILIn; + startOfJPData = start; + lengthOfJPData = size; + bWritable = bWrite; + bSeekable = bSeekableIn; + VSIFSeekL(fpVSIL, startOfJPData, SEEK_SET); + m_Filename = CPLStrdup(pszFilename); + // the filename is used to establish where to put temporary files. + // if it does not have a path to a real directory, we will + // substitute something. + CPLString osFilenameUsed = pszFilename; + CPLString osPath = CPLGetPath( pszFilename ); + struct stat sStatBuf; + if( osPath != "" && stat( osPath, &sStatBuf ) != 0 ) + { + osFilenameUsed = CPLGenerateTempFilename( NULL ); + // try to preserve the extension. + if( strlen(CPLGetExtension(pszFilename)) > 0 ) + { + osFilenameUsed += "."; + osFilenameUsed += CPLGetExtension(pszFilename); + } + CPLDebug( "ECW", "Using filename '%s' for temporary directory determination purposes.", osFilenameUsed.c_str() ); + } + return(CNCSJPCIOStream::Open((char *)osFilenameUsed.c_str(), + (bool) bWrite)); + } + + virtual bool NCS_FASTCALL Seek() { + return bSeekable; + } + + virtual bool NCS_FASTCALL Seek(INT64 offset, Origin origin = CURRENT) { + bool success = false; + switch(origin) { + case START: + success = (0 == VSIFSeekL(fpVSIL, offset+startOfJPData, SEEK_SET)); + break; + + case CURRENT: + success = (0 == VSIFSeekL(fpVSIL, offset, SEEK_CUR)); + break; + + case END: + success = (0 == VSIFSeekL(fpVSIL, offset, SEEK_END)); + break; + } + if( !success ) + CPLDebug( "ECW", "VSIIOStream::Seek(%d,%d) failed.", + (int) offset, (int) origin ); + return(success); + } + + virtual INT64 NCS_FASTCALL Tell() { + return VSIFTellL( fpVSIL ) - startOfJPData; + } + + virtual INT64 NCS_FASTCALL Size() { + if( lengthOfJPData != -1 ) + return lengthOfJPData; + else + { + INT64 curPos = Tell(), size; + + Seek( 0, END ); + size = Tell(); + Seek( curPos, START ); + + return size; + } + } + + virtual bool NCS_FASTCALL Read(void* buffer, UINT32 count) { + if( count == 0 ) + return true; + +// return(1 == VSIFReadL( buffer, count, 1, fpVSIL ) ); + + // The following is a hack + if( VSIFReadL( buffer, count, 1, fpVSIL ) != 1 ) + { + CPLDebug( "VSIIOSTREAM", + "Read(%d) failed @ " CPL_FRMT_GIB ", ignoring failure.", + count, (VSIFTellL( fpVSIL ) - startOfJPData) ); + } + + return true; + } + + virtual bool NCS_FASTCALL Write(void* buffer, UINT32 count) { + if( count == 0 ) + return true; + + GByte* paby = (GByte*) buffer; + if( nCOMState == 0 ) + { + if( count == 2 && paby[0] == 0xff && paby[1] == 0x64 ) + { + nCOMState ++; + return true; + } + } + else if( nCOMState == 1 ) + { + if( count == 2 ) + { + nCOMLength = (paby[0] << 8) | paby[1]; + nCOMState ++; + return true; + } + else + { + GByte prevBuffer[] = { 0xff, 0x64 }; + VSIFWriteL(prevBuffer, 2, 1, fpVSIL); + nCOMState = 0; + } + } + else if( nCOMState == 2 ) + { + if( count == 2 ) + { + abyCOMType[0] = paby[0]; + abyCOMType[1] = paby[1]; + nCOMState ++; + return true; + } + else + { + GByte prevBuffer[] = + { (GByte)(nCOMLength >> 8), (GByte) (nCOMLength & 0xff) }; + VSIFWriteL(prevBuffer, 2, 1, fpVSIL); + nCOMState = 0; + } + } + else if( nCOMState == 3 ) + { + if( count == (UINT32)nCOMLength - 4 ) + { + nCOMState = 0; + return true; + } + else + { + VSIFWriteL(abyCOMType, 2, 1, fpVSIL); + nCOMState = 0; + } + } + + if( 1 != VSIFWriteL(buffer, count, 1, fpVSIL) ) + { + CPLDebug( "ECW", "VSIIOStream::Write(%d) failed.", + (int) count ); + return false; + } + else + return true; + } +}; + +/************************************************************************/ +/* ==================================================================== */ +/* ECWAsyncReader */ +/* ==================================================================== */ +/************************************************************************/ +class ECWDataset; + +#if ECWSDK_VERSION >= 40 + +class ECWAsyncReader : public GDALAsyncReader +{ +private: + CNCSJP2FileView *poFileView; + CPLMutex *hMutex; + int bUsingCustomStream; + + int bUpdateReady; + int bComplete; + + static NCSEcwReadStatus RefreshCB( NCSFileView * ); + NCSEcwReadStatus ReadToBuffer(); + +public: + ECWAsyncReader(); + virtual ~ECWAsyncReader(); + virtual GDALAsyncStatusType GetNextUpdatedRegion(double dfTimeout, + int* pnXBufOff, + int* pnYBufOff, + int* pnXBufSize, + int* pnYBufSize); + + friend class ECWDataset; +}; +#endif /* ECWSDK_VERSION >= 40 */ + +/************************************************************************/ +/* ==================================================================== */ +/* ECWDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class ECWRasterBand; + +typedef struct +{ + int bEnabled; + int nBandsTried; + + int nXOff; + int nYOff; + int nXSize; + int nYSize; + int nBufXSize; + int nBufYSize; + GDALDataType eBufType; + GByte* pabyData; +} ECWCachedMultiBandIO; + +class CPL_DLL ECWDataset : public GDALJP2AbstractDataset +{ + friend class ECWRasterBand; + friend class ECWAsyncReader; + + int bIsJPEG2000; + + CNCSJP2FileView *poFileView; + NCSFileViewFileInfoEx *psFileInfo; + + GDALDataType eRasterDataType; + NCSEcwCellType eNCSRequestDataType; + + int bUsingCustomStream; + + // Current view window. + int bWinActive; + int nWinXOff, nWinYOff, nWinXSize, nWinYSize; + int nWinBufXSize, nWinBufYSize; + int nWinBandCount; + int *panWinBandList; + int nWinBufLoaded; + void **papCurLineBuf; + + char **papszGMLMetadata; + + ECWCachedMultiBandIO sCachedMultiBandIO; + + void ECW2WKTProjection(); + + void CleanupWindow(); + int TryWinRasterIO( GDALRWFlag, int, int, int, int, + GByte *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ); + CPLErr LoadNextLine(); + +#if ECWSDK_VERSION>=50 + + NCSFileStatistics* pStatistics; + int bStatisticsDirty; + int bStatisticsInitialized; + NCS::CError StatisticsEnsureInitialized(); + NCS::CError StatisticsWrite(); + void CleanupStatistics(); + void ReadFileMetaDataFromFile(); + + int bFileMetaDataDirty; + void WriteFileMetaData(NCSFileMetaData* pFileMetaDataCopy); + +#endif + + static CNCSJP2FileView *OpenFileView( const char *pszDatasetName, + bool bProgressive, + int &bUsingCustomStream, + bool bWrite=false); + + int bHdrDirty; + CPLString m_osDatumCode; + CPLString m_osProjCode; + CPLString m_osUnitsCode; + int bGeoTransformChanged; + int bProjectionChanged; + int bProjCodeChanged; + int bDatumCodeChanged; + int bUnitsCodeChanged; + void WriteHeader(); + + int bUseOldBandRasterIOImplementation; + + int bPreventCopyingSomeMetadata; + + int nBandIndexToPromoteTo8Bit; + + CPLStringList oECWMetadataList; + CPLErr ReadBands(void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, + GSpacing nPixelSpace, GSpacing nLineSpace, GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + CPLErr ReadBandsDirectly(void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, + GSpacing nPixelSpace, GSpacing nLineSpace, GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + public: + ECWDataset(int bIsJPEG2000); + ~ECWDataset(); + + static GDALDataset *Open( GDALOpenInfo *, int bIsJPEG2000 ); + static int IdentifyJPEG2000( GDALOpenInfo * poOpenInfo ); + static GDALDataset *OpenJPEG2000( GDALOpenInfo * ); + static int IdentifyECW( GDALOpenInfo * poOpenInfo ); + static GDALDataset *OpenECW( GDALOpenInfo * ); + + void SetPreventCopyingSomeMetadata(int b) { bPreventCopyingSomeMetadata = b; } + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + virtual char **GetMetadataDomainList(); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual char **GetMetadata( const char * pszDomain = "" ); + + virtual CPLErr SetGeoTransform( double * padfGeoTransform ); + virtual CPLErr SetProjection( const char* pszProjection ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + char **papszOptions ); + + // progressive methods +#if ECWSDK_VERSION >= 40 + virtual GDALAsyncReader* BeginAsyncReader( int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, + char **papszOptions); + + virtual void EndAsyncReader(GDALAsyncReader *); +#endif /* ECWSDK_VERSION > 40 */ +#if ECWSDK_VERSION >=50 + int GetFormatVersion() const { + return psFileInfo->nFormatVersion; + } +#endif +}; + +/************************************************************************/ +/* ==================================================================== */ +/* ECWRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class ECWRasterBand : public GDALPamRasterBand +{ + friend class ECWDataset; + + // NOTE: poDS may be altered for NITF/JPEG2000 files! + ECWDataset *poGDS; + + GDALColorInterp eBandInterp; + + int iOverview; // -1 for base. + + std::vector apoOverviews; + +#if ECWSDK_VERSION>=50 + + int nStatsBandIndex; + int nStatsBandCount; + +#endif + + int bPromoteTo8Bit; + +//#if !defined(SDK_CAN_DO_SUPERSAMPLING) + CPLErr OldIRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); +//#endif + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); + + public: + + ECWRasterBand( ECWDataset *, int, int iOverview, char** papszOpenOptions ); + ~ECWRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual int HasArbitraryOverviews() { return apoOverviews.size() == 0; } + virtual int GetOverviewCount() { return (int)apoOverviews.size(); } + virtual GDALRasterBand *GetOverview(int); + + virtual GDALColorInterp GetColorInterpretation(); + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ); +#if ECWSDK_VERSION >= 50 + void GetBandIndexAndCountForStatistics(int &bandIndex, int &bandCount); + virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig ** ppanHistogram, + int bForce, + GDALProgressFunc, void *pProgressData); + virtual CPLErr SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram ); + virtual double GetMinimum( int* pbSuccess ); + virtual double GetMaximum( int* pbSuccess ); + virtual CPLErr GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *padfStdDev ); + virtual CPLErr SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ); +#endif + +}; + +int ECWTranslateFromWKT( const char *pszWKT, + char *pszProjection, + int nProjectionLen, + char *pszDatum, + int nDatumLen, + char *pszUnits); + +CellSizeUnits ECWTranslateToCellSizeUnits(const char* pszUnits); +const char* ECWTranslateFromCellSizeUnits(CellSizeUnits eUnits); + +#endif /* def FRMT_ecw */ + +#endif /* ndef GDAL_ECW_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/ecw/jp2userbox.cpp b/bazaar/plugin/gdal/frmts/ecw/jp2userbox.cpp new file mode 100644 index 000000000..913164b76 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ecw/jp2userbox.cpp @@ -0,0 +1,144 @@ +/****************************************************************************** + * $Id: jp2userbox.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: GDAL ECW Driver + * Purpose: JP2UserBox implementation - arbitrary box read/write. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_ecw.h" + +CPL_CVSID("$Id: jp2userbox.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#if defined(HAVE_COMPRESS) + +/************************************************************************/ +/* JP2UserBox() */ +/************************************************************************/ + +JP2UserBox::JP2UserBox() +{ + pabyData = NULL; + nDataLength = 0; + + m_nTBox = 0; +} + +/************************************************************************/ +/* ~JP2UserBox() */ +/************************************************************************/ + +JP2UserBox::~JP2UserBox() + +{ + if( pabyData != NULL ) + { + CPLFree( pabyData ); + pabyData = NULL; + } +} + +/************************************************************************/ +/* SetData() */ +/************************************************************************/ + +void JP2UserBox::SetData( int nLengthIn, const unsigned char *pabyDataIn ) + +{ + if( pabyData != NULL ) + CPLFree( pabyData ); + + nDataLength = nLengthIn; + pabyData = (unsigned char *) CPLMalloc(nDataLength); + memcpy( pabyData, pabyDataIn, nDataLength ); + + m_bValid = true; +} + +/************************************************************************/ +/* UpdateXLBox() */ +/************************************************************************/ + +void JP2UserBox::UpdateXLBox() + +{ + m_nXLBox = 8 + nDataLength; + m_nLDBox = nDataLength; +} + +/************************************************************************/ +/* Parse() */ +/* */ +/* Parse box, and data contents from file into memory. */ +/************************************************************************/ + +#if ECWSDK_VERSION >= 40 +CNCSError JP2UserBox::Parse( CPL_UNUSED NCS::SDK::CFileBase &JP2File, + CPL_UNUSED NCS::CIOStream &Stream ) +#else +CNCSError JP2UserBox::Parse( CPL_UNUSED class CNCSJP2File &JP2File, + CPL_UNUSED CNCSJPCIOStream &Stream ) +#endif +{ + CNCSError Error = NCS_SUCCESS; + + return Error; +} + +/************************************************************************/ +/* UnParse() */ +/* */ +/* Write box meta information, and data to file. */ +/************************************************************************/ + +#if ECWSDK_VERSION >= 40 +CNCSError JP2UserBox::UnParse( NCS::SDK::CFileBase &JP2File, + NCS::CIOStream &Stream ) +#else +CNCSError JP2UserBox::UnParse( class CNCSJP2File &JP2File, + CNCSJPCIOStream &Stream ) +#endif +{ + CNCSError Error = NCS_SUCCESS; + + if( m_nTBox == 0 ) + { + Error = NCS_UNKNOWN_ERROR; + CPLError( CE_Failure, CPLE_AppDefined, + "No box type set in JP2UserBox::UnParse()" ); + return Error; + } +#if ECWSDK_VERSION<50 + Error = CNCSJP2Box::UnParse(JP2File, Stream); +#else + Error = CNCSSDKBox::UnParse(JP2File, Stream); +#endif +// NCSJP2_CHECKIO_BEGIN(Error, Stream); + Stream.Write(pabyData, nDataLength); +// NCSJP2_CHECKIO_END(); + + return Error; +} + +#endif /* defined(HAVE_COMPRESS) */ diff --git a/bazaar/plugin/gdal/frmts/ecw/lookup.py b/bazaar/plugin/gdal/frmts/ecw/lookup.py new file mode 100644 index 000000000..69f6c5be9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ecw/lookup.py @@ -0,0 +1,190 @@ +#****************************************************************************** +# $Id: lookup.py 27632 2014-09-04 17:56:16Z goatbar $ +# +# Project: GDAL ECW Driver +# Purpose: Script to lookup ECW (GDT) coordinate systems and translate +# into OGC WKT for storage in $GDAL_HOME/data/ecw_cs.wkt. +# Author: Frank Warmerdam, warmerdam@pobox.com +# +#****************************************************************************** +# Copyright (c) 2003, Frank Warmerdam +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +#****************************************************************************** + +import string +import sys +import osr + +############################################################################## +# rtod(): radians to degrees. + +def r2d( rad ): + return float(rad) / 0.0174532925199433 + +############################################################################## +# load_dict() + +def load_dict( filename ): + lines = open( filename ).readlines() + + this_dict = {} + for line in lines: + if line[:8] != 'proj_name': + tokens = string.split(line,',') + for i in range(len(tokens)): + tokens[i] = string.strip(tokens[i]) + + this_dict[tokens[0]] = tokens + + return this_dict + +############################################################################## +# Mainline + +#dir = 'M:/software/ER Viewer 2.0c/GDT_Data/' +dir = '/u/data/ecw/gdt/' + +dict_list = [ 'tranmerc', 'lambert1', 'lamcon2', 'utm', 'albersea', 'mercator', + 'obmerc_b', 'grinten', 'cassini', 'lambazea', 'datum_sp' ] + +dict_dict = {} + +for item in dict_list: + dict_dict[item] = load_dict( dir + item + '.dat' ) + #print 'loaded: %s' % item + +pfile = open( dir + 'project.dat' ) +pfile.readline() + +for line in pfile.readlines(): + try: + tokens = string.split(string.strip(line),',') + if len(tokens) < 3: + continue + + for i in range(len(tokens)): + tokens[i] = string.strip(tokens[i]) + + id = tokens[0] + type = tokens[1] + + lsize = float(tokens[2]) + lsize_str = tokens[2] + + dline = dict_dict[type][id] + + srs = osr.SpatialReference() + + # Handle translation of the projection parameters. + + if type != 'utm': + fn = float(dline[1]) + fe = float(dline[2]) + + if type == 'tranmerc': + srs.SetTM( r2d(dline[5]), r2d(dline[4]), float(dline[3]), fe, fn ) + + elif type == 'mercator': + srs.SetMercator( r2d(dline[5]), r2d(dline[4]), float(dline[3]), fe, fn ) + + elif type == 'grinten': + srs.SetVDG( r2d(dline[3]), fe, fn ) + + elif type == 'cassini': + srs.SetCS( r2d(dline[4]), r2d(dline[3]), fe, fn ) + + elif type == 'lambazea': + srs.SetLAEA( r2d(dline[5]), r2d(dline[4]), + fe, fn ) + + elif type == 'lambert1': + srs.SetLCC1SP( r2d(dline[5]), r2d(dline[4]), + float(dline[3]), fe, fn ) + + elif type == 'lamcon2': + srs.SetLCC( r2d(dline[7]), r2d(dline[8]), + r2d(dline[9]), r2d(dline[6]), fe, fn ) + +# elif type == 'lambert2': +# false_en = '+y_0=%.2f +x_0=%.2f' \ +# % (float(dline[12])*lsize, float(dline[13])*lsize) +# result = '+proj=lcc %s +lat_0=%s +lon_0=%s +lat_1=%s +lat_2=%s' \ +# % (false_en, r2d(dline[3]), r2d(dline[4]), +# r2d(dline[7]), r2d(dline[8])) + + elif type == 'albersea': + srs.SetACEA( r2d(dline[3]), r2d(dline[4]), + r2d(dline[5]), r2d(dline[6]), fe, fn ) + +# elif type == 'obmerc_b': +# result = '+proj=omerc %s +lat_0=%s +lonc=%s +alpha=%s +k=%s' \ +# % (false_en, r2d(dline[5]), r2d(dline[6]), r2d(dline[4]), dline[3]) + + elif type == 'utm': + srs.SetUTM( int(dline[1]), dline[2] != 'S' ) + + # Handle Units from projects.dat file. + if srs.IsProjected(): + srs.SetAttrValue( 'PROJCS', id ) + if lsize_str == '0.30480061': + srs.SetLinearUnits( 'US Foot', float(lsize_str) ) + elif lsize_str != '1.0': + srs.SetLinearUnits( 'unnamed', float(lsize_str) ) + + wkt = srs.ExportToWkt() + if len(wkt) > 0: + print '%s,%s' % (id, srs.ExportToWkt()) + else: + print '%s,LOCAL_CS["%s - (unsupported)"]' % (id,id) + + except KeyError: + print '%s,LOCAL_CS["%s - (unsupported)"]' % (id,id) + + except: + print 'cant translate: ', line + raise + +## Translate datums to their underlying spheroid information. + +pfile = open( dir + 'datum.dat' ) +pfile.readline() + +for line in pfile.readlines(): + tokens = string.split(string.strip(line),',') + for i in range(len(tokens)): + tokens[i] = string.strip(tokens[i]) + + id = tokens[0] + + sp_name = tokens[2] + dline = dict_dict['datum_sp'][id] + srs = osr.SpatialReference() + + if id == 'WGS84': + srs.SetWellKnownGeogCS( 'WGS84' ) + elif id == 'NAD27': + srs.SetWellKnownGeogCS( 'NAD27' ) + elif id == 'NAD83': + srs.SetWellKnownGeogCS( 'NAD83' ) + else: + srs.SetGeogCS( tokens[1], id, sp_name, float(dline[2]), float(dline[4]) ) + + print '%s,%s' % (id, srs.ExportToWkt()) + diff --git a/bazaar/plugin/gdal/frmts/ecw/makefile.vc b/bazaar/plugin/gdal/frmts/ecw/makefile.vc new file mode 100644 index 000000000..3d69077dd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ecw/makefile.vc @@ -0,0 +1,48 @@ + +EXTRAFLAGS = $(ECWFLAGS) -DFRMT_ecw + + +OBJ = ecwdataset.obj ecwcreatecopy.obj jp2userbox.obj \ + ecwasyncreader.obj + +PLUGIN_DLL = gdal_ECW_JP2ECW.dll + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IF $(MSVC_VER) == 1500 +EXTRAFLAGS = $(EXTRAFLAGS) -D_WIN32_WINNT=0x0500 +!ENDIF + +default: $(OBJ) + $(INSTALL) *.obj ..\o + +$(OBJ): gdal_ecw.h + +all: default testecw.exe + +clean: + -del *.obj + -del *.dll + -del *.exp + -del *.lib + -del *.manifest + -del *.exe + +testecw.exe: testecw.cpp + $(CC) /Zi /MD $(EXTRAFLAGS) testecw.cpp $(ECWLIB) + +ecw_example1.exe: ecw_example1.c + $(CC) /MD $(EXTRAFLAGS) ecw_example1.c $(ECWLIB) + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(GDALLIB) $(ECWLIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) + diff --git a/bazaar/plugin/gdal/frmts/elas/GNUmakefile b/bazaar/plugin/gdal/frmts/elas/GNUmakefile new file mode 100644 index 000000000..5aa7258a2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/elas/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../GDALmake.opt + +OBJ = elasdataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/elas/elasdataset.cpp b/bazaar/plugin/gdal/frmts/elas/elasdataset.cpp new file mode 100644 index 000000000..38a10ecab --- /dev/null +++ b/bazaar/plugin/gdal/frmts/elas/elasdataset.cpp @@ -0,0 +1,689 @@ +/****************************************************************************** + * $Id: elasdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: ELAS Translator + * Purpose: Complete implementation of ELAS translator module for GDAL. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" + +CPL_CVSID("$Id: elasdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +CPL_C_START +void GDALRegister_ELAS(void); +CPL_C_END + +typedef struct { + GInt32 NBIH; /* bytes in header, normaly 1024 */ + GInt32 NBPR; /* bytes per data record (all bands of scanline) */ + GInt32 IL; /* initial line - normally 1 */ + GInt32 LL; /* last line */ + GInt32 IE; /* initial element (pixel), normally 1 */ + GInt32 LE; /* last element (pixel) */ + GInt32 NC; /* number of channels (bands) */ + GInt32 H4321; /* header record identifier - always 4321. */ + char YLabel[4]; /* Should be "NOR" for UTM */ + GInt32 YOffset;/* topleft pixel center northing */ + char XLabel[4]; /* Should be "EAS" for UTM */ + GInt32 XOffset;/* topleft pixel center easting */ + float YPixSize;/* height of pixel in georef units */ + float XPixSize;/* width of pixel in georef units */ + float Matrix[4]; /* 2x2 transformation matrix. Should be + 1,0,0,1 for pixel/line, or + 1,0,0,-1 for UTM */ + GByte IH19[4];/* data type, and size flags */ + GInt32 IH20; /* number of secondary headers */ + char unused1[8]; + GInt32 LABL; /* used by LABL module */ + char HEAD; /* used by HEAD module */ + char Comment1[64]; + char Comment2[64]; + char Comment3[64]; + char Comment4[64]; + char Comment5[64]; + char Comment6[64]; + GUInt16 ColorTable[256]; /* RGB packed with 4 bits each */ + char unused2[32]; +} ELASHeader; + + +/************************************************************************/ +/* ==================================================================== */ +/* ELASDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class ELASRasterBand; + +class ELASDataset : public GDALPamDataset +{ + friend class ELASRasterBand; + + VSILFILE *fp; + + ELASHeader sHeader; + int bHeaderModified; + + GDALDataType eRasterDataType; + + int nLineOffset; + int nBandOffset; // within a line. + + double adfGeoTransform[6]; + + public: + ELASDataset(); + ~ELASDataset(); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); + + virtual void FlushCache( void ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* ELASRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class ELASRasterBand : public GDALPamRasterBand +{ + friend class ELASDataset; + + public: + + ELASRasterBand( ELASDataset *, int ); + + // should override RasterIO eventually. + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); +}; + + +/************************************************************************/ +/* ELASRasterBand() */ +/************************************************************************/ + +ELASRasterBand::ELASRasterBand( ELASDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + this->eAccess = poDS->eAccess; + + eDataType = poDS->eRasterDataType; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr ELASRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + ELASDataset *poGDS = (ELASDataset *) poDS; + CPLErr eErr = CE_None; + long nOffset; + int nDataSize; + + CPLAssert( nBlockXOff == 0 ); + + nDataSize = GDALGetDataTypeSize(eDataType) * poGDS->GetRasterXSize() / 8; + nOffset = poGDS->nLineOffset * nBlockYOff + 1024 + (nBand-1) * nDataSize; + +/* -------------------------------------------------------------------- */ +/* If we can't seek to the data, we will assume this is a newly */ +/* created file, and that the file hasn't been extended yet. */ +/* Just read as zeros. */ +/* -------------------------------------------------------------------- */ + if( VSIFSeekL( poGDS->fp, nOffset, SEEK_SET ) != 0 + || VSIFReadL( pImage, 1, nDataSize, poGDS->fp ) != (size_t) nDataSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Seek or read of %d bytes at %ld failed.\n", + nDataSize, nOffset ); + eErr = CE_Failure; + } + + return eErr; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr ELASRasterBand::IWriteBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + ELASDataset *poGDS = (ELASDataset *) poDS; + CPLErr eErr = CE_None; + long nOffset; + int nDataSize; + + CPLAssert( nBlockXOff == 0 ); + CPLAssert( eAccess == GA_Update ); + + nDataSize = GDALGetDataTypeSize(eDataType) * poGDS->GetRasterXSize() / 8; + nOffset = poGDS->nLineOffset * nBlockYOff + 1024 + (nBand-1) * nDataSize; + + if( VSIFSeekL( poGDS->fp, nOffset, SEEK_SET ) != 0 + || VSIFWriteL( pImage, 1, nDataSize, poGDS->fp ) != (size_t) nDataSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Seek or write of %d bytes at %ld failed.\n", + nDataSize, nOffset ); + eErr = CE_Failure; + } + + return eErr; +} + +/************************************************************************/ +/* ==================================================================== */ +/* ELASDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* ELASDataset() */ +/************************************************************************/ + +ELASDataset::ELASDataset() + +{ + fp = NULL; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~ELASDataset() */ +/************************************************************************/ + +ELASDataset::~ELASDataset() + +{ + FlushCache(); + + if( fp != NULL ) + { + VSIFCloseL( fp ); + fp = NULL; + } +} + +/************************************************************************/ +/* FlushCache() */ +/* */ +/* We also write out the header, if it is modified. */ +/************************************************************************/ + +void ELASDataset::FlushCache() + +{ + GDALDataset::FlushCache(); + + if( bHeaderModified ) + { + VSIFSeekL( fp, 0, SEEK_SET ); + VSIFWriteL( &sHeader, 1024, 1, fp ); + bHeaderModified = FALSE; + } +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int ELASDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* First we check to see if the file has the expected header */ +/* bytes. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 256 ) + return FALSE; + + if( CPL_MSBWORD32(*((GInt32 *) (poOpenInfo->pabyHeader+0))) != 1024 + || CPL_MSBWORD32(*((GInt32 *) (poOpenInfo->pabyHeader+28))) != 4321 ) + { + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *ELASDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify(poOpenInfo) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + ELASDataset *poDS; + const char *pszAccess; + + if( poOpenInfo->eAccess == GA_Update ) + pszAccess = "r+b"; + else + pszAccess = "rb"; + + poDS = new ELASDataset(); + + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, pszAccess ); + if( poDS->fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to open `%s' with acces `%s' failed.\n", + poOpenInfo->pszFilename, pszAccess ); + delete poDS; + return NULL; + } + + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Read the header information. */ +/* -------------------------------------------------------------------- */ + poDS->bHeaderModified = FALSE; + if( VSIFReadL( &(poDS->sHeader), 1024, 1, poDS->fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Attempt to read 1024 byte header filed on file %s\n", + poOpenInfo->pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Extract information of interest from the header. */ +/* -------------------------------------------------------------------- */ + int nStart, nEnd, nELASDataType, nBytesPerSample; + + poDS->nLineOffset = CPL_MSBWORD32( poDS->sHeader.NBPR ); + + nStart = CPL_MSBWORD32( poDS->sHeader.IL ); + nEnd = CPL_MSBWORD32( poDS->sHeader.LL ); + poDS->nRasterYSize = nEnd - nStart + 1; + + nStart = CPL_MSBWORD32( poDS->sHeader.IE ); + nEnd = CPL_MSBWORD32( poDS->sHeader.LE ); + poDS->nRasterXSize = nEnd - nStart + 1; + + poDS->nBands = CPL_MSBWORD32( poDS->sHeader.NC ); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || + !GDALCheckBandCount(poDS->nBands, FALSE)) + { + delete poDS; + return NULL; + } + + nELASDataType = (poDS->sHeader.IH19[2] & 0x7e) >> 2; + nBytesPerSample = poDS->sHeader.IH19[3]; + + if( nELASDataType == 0 && nBytesPerSample == 1 ) + poDS->eRasterDataType = GDT_Byte; + else if( nELASDataType == 1 && nBytesPerSample == 1 ) + poDS->eRasterDataType = GDT_Byte; + else if( nELASDataType == 16 && nBytesPerSample == 4 ) + poDS->eRasterDataType = GDT_Float32; + else if( nELASDataType == 17 && nBytesPerSample == 8 ) + poDS->eRasterDataType = GDT_Float64; + else + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised image data type %d, with BytesPerSample=%d.\n", + nELASDataType, nBytesPerSample ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Band offsets are always multiples of 256 within a multi-band */ +/* scanline of data. */ +/* -------------------------------------------------------------------- */ + poDS->nBandOffset = + (poDS->nRasterXSize * GDALGetDataTypeSize(poDS->eRasterDataType)/8); + + if( poDS->nBandOffset % 256 != 0 ) + { + poDS->nBandOffset = + poDS->nBandOffset - (poDS->nBandOffset % 256) + 256; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 0; iBand < poDS->nBands; iBand++ ) + { + poDS->SetBand( iBand+1, new ELASRasterBand( poDS, iBand+1 ) ); + } + +/* -------------------------------------------------------------------- */ +/* Extract the projection coordinates, if present. */ +/* -------------------------------------------------------------------- */ + if( poDS->sHeader.XOffset != 0 ) + { + CPL_MSBPTR32(&(poDS->sHeader.XPixSize)); + CPL_MSBPTR32(&(poDS->sHeader.YPixSize)); + + poDS->adfGeoTransform[0] = + (GInt32) CPL_MSBWORD32(poDS->sHeader.XOffset); + poDS->adfGeoTransform[1] = poDS->sHeader.XPixSize; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = + (GInt32) CPL_MSBWORD32(poDS->sHeader.YOffset); + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -1.0 * ABS(poDS->sHeader.YPixSize); + + CPL_MSBPTR32(&(poDS->sHeader.XPixSize)); + CPL_MSBPTR32(&(poDS->sHeader.YPixSize)); + + poDS->adfGeoTransform[0] -= poDS->adfGeoTransform[1] * 0.5; + poDS->adfGeoTransform[3] -= poDS->adfGeoTransform[5] * 0.5; + } + else + { + poDS->adfGeoTransform[0] = 0.0; + poDS->adfGeoTransform[1] = 1.0; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = 1.0; + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return( poDS ); +} + +/************************************************************************/ +/* Create() */ +/* */ +/* Create a new GeoTIFF or TIFF file. */ +/************************************************************************/ + +GDALDataset *ELASDataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** /* notdef: papszParmList */ ) + +{ + int nBandOffset; + +/* -------------------------------------------------------------------- */ +/* Verify input options. */ +/* -------------------------------------------------------------------- */ + if (nBands <= 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "ELAS driver does not support %d bands.\n", nBands); + return NULL; + } + + if( eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_Float64 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create an ELAS dataset with an illegal\n" + "data type (%d).\n", + eType ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to create the file. */ +/* -------------------------------------------------------------------- */ + FILE *fp; + + fp = VSIFOpen( pszFilename, "w" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* How long will each band of a scanline be? */ +/* -------------------------------------------------------------------- */ + nBandOffset = nXSize * GDALGetDataTypeSize(eType)/8; + + if( nBandOffset % 256 != 0 ) + { + nBandOffset = nBandOffset - (nBandOffset % 256) + 256; + } + +/* -------------------------------------------------------------------- */ +/* Setup header data block. */ +/* */ +/* Note that CPL_MSBWORD32() will swap little endian words to */ +/* big endian on little endian platforms. */ +/* -------------------------------------------------------------------- */ + ELASHeader sHeader; + + memset( &sHeader, 0, 1024 ); + + sHeader.NBIH = CPL_MSBWORD32(1024); + + sHeader.NBPR = CPL_MSBWORD32(nBands * nBandOffset); + + sHeader.IL = CPL_MSBWORD32(1); + sHeader.LL = CPL_MSBWORD32(nYSize); + + sHeader.IE = CPL_MSBWORD32(1); + sHeader.LE = CPL_MSBWORD32(nXSize); + + sHeader.NC = CPL_MSBWORD32(nBands); + + sHeader.H4321 = CPL_MSBWORD32(4321); + + sHeader.IH19[0] = 0x04; + sHeader.IH19[1] = 0xd2; + sHeader.IH19[3] = (GByte) (GDALGetDataTypeSize(eType) / 8); + + if( eType == GDT_Byte ) + sHeader.IH19[2] = 1 << 2; + else if( eType == GDT_Float32 ) + sHeader.IH19[2] = 16 << 2; + else if( eType == GDT_Float64 ) + sHeader.IH19[2] = 17 << 2; + +/* -------------------------------------------------------------------- */ +/* Write the header data. */ +/* -------------------------------------------------------------------- */ + VSIFWrite( &sHeader, 1024, 1, fp ); + +/* -------------------------------------------------------------------- */ +/* Now write out zero data for all the imagery. This is */ +/* inefficient, but simplies the IReadBlock() / IWriteBlock() logic.*/ +/* -------------------------------------------------------------------- */ + GByte *pabyLine; + + pabyLine = (GByte *) CPLCalloc(nBandOffset,nBands); + for( int iLine = 0; iLine < nYSize; iLine++ ) + { + if( VSIFWrite( pabyLine, 1, nBandOffset, fp ) != (size_t) nBandOffset ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Error writing ELAS image data ... likely insufficient" + " disk space.\n" ); + VSIFClose( fp ); + CPLFree( pabyLine ); + return NULL; + } + } + + CPLFree( pabyLine ); + + VSIFClose( fp ); + +/* -------------------------------------------------------------------- */ +/* Try to return a regular handle on the file. */ +/* -------------------------------------------------------------------- */ + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr ELASDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + + return( CE_None ); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr ELASDataset::SetGeoTransform( double * padfTransform ) + +{ +/* -------------------------------------------------------------------- */ +/* I don't think it supports rotation, but perhaps it is possible */ +/* for us to use the 2x2 transform matrix to accomplish some */ +/* sort of rotation. */ +/* -------------------------------------------------------------------- */ + if( padfTransform[2] != 0.0 || padfTransform[4] != 0.0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to set rotated geotransform on ELAS file.\n" + "ELAS does not support rotation.\n" ); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Remember the new transform, and update the header. */ +/* -------------------------------------------------------------------- */ + int nXOff, nYOff; + + memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 ); + + bHeaderModified = TRUE; + + nXOff = (int) (adfGeoTransform[0] + adfGeoTransform[1]*0.5); + nYOff = (int) (adfGeoTransform[3] + adfGeoTransform[5]*0.5); + + sHeader.XOffset = CPL_MSBWORD32(nXOff); + sHeader.YOffset = CPL_MSBWORD32(nYOff); + + sHeader.XPixSize = (float) ABS(adfGeoTransform[1]); + sHeader.YPixSize = (float) ABS(adfGeoTransform[5]); + + CPL_MSBPTR32(&(sHeader.XPixSize)); + CPL_MSBPTR32(&(sHeader.YPixSize)); + + strncpy( sHeader.YLabel, "NOR ", 4 ); + strncpy( sHeader.XLabel, "EAS ", 4 ); + + sHeader.Matrix[0] = 1.0; + sHeader.Matrix[1] = 0.0; + sHeader.Matrix[2] = 0.0; + sHeader.Matrix[3] = -1.0; + + CPL_MSBPTR32(&(sHeader.Matrix[0])); + CPL_MSBPTR32(&(sHeader.Matrix[1])); + CPL_MSBPTR32(&(sHeader.Matrix[2])); + CPL_MSBPTR32(&(sHeader.Matrix[3])); + + return( CE_None ); +} + + +/************************************************************************/ +/* GDALRegister_ELAS() */ +/************************************************************************/ + +void GDALRegister_ELAS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ELAS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ELAS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ELAS" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Float32 Float64" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = ELASDataset::Open; + poDriver->pfnIdentify = ELASDataset::Identify; + poDriver->pfnCreate = ELASDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/elas/frmt_elas.html b/bazaar/plugin/gdal/frmts/elas/frmt_elas.html new file mode 100644 index 000000000..aeeba31e0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/elas/frmt_elas.html @@ -0,0 +1,21 @@ + + + ELAS - Earth Resources Laboratory Applications Software + + + + +

ELAS - Earth Resources Laboratory Applications Software

+ +

ELAS is an old remote sensing system still used for a variety of research projects within NASA. +The ELAS format support can be found in gdal/frmts/elas.

+ +

See Also:

+ + + + + diff --git a/bazaar/plugin/gdal/frmts/elas/makefile.vc b/bazaar/plugin/gdal/frmts/elas/makefile.vc new file mode 100644 index 000000000..87b546a0b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/elas/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = elasdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/envisat/EnvisatFile.c b/bazaar/plugin/gdal/frmts/envisat/EnvisatFile.c new file mode 100644 index 000000000..a1d064d52 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/EnvisatFile.c @@ -0,0 +1,1938 @@ +/****************************************************************************** + * $Id: EnvisatFile.c 28843 2015-04-02 21:13:08Z kyle $ + * + * Project: APP ENVISAT Support + * Purpose: Low Level Envisat file access (read/write) API. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Atlantis Scientific, Inc. + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_string.h" + +#ifndef APP_BUILD +# define GDAL_BUILD +# include "cpl_conv.h" +# include "EnvisatFile.h" + +CPL_CVSID("$Id: EnvisatFile.c 28843 2015-04-02 21:13:08Z kyle $"); + +#else +# include "APP/app.h" +# include "util/Files/EnvisatFile.h" +#endif + +typedef struct +{ + char *ds_name; + char *ds_type; + char *filename; + int ds_offset; + int ds_size; + int num_dsr; + int dsr_size; +} EnvisatDatasetInfo; + +typedef struct +{ + char *key; + char *value; + char *units; + char *literal_line; + int value_offset; +} EnvisatNameValue; + +struct EnvisatFile_tag +{ + VSILFILE *fp; + char *filename; + int updatable; + int header_dirty; + int dsd_offset; + + int mph_count; + EnvisatNameValue **mph_entries; + + int sph_count; + EnvisatNameValue **sph_entries; + + int ds_count; + EnvisatDatasetInfo **ds_info; +}; + +#ifdef GDAL_BUILD +# define SUCCESS 0 +# define FAILURE 1 +# define SendError( text ) CPLError( CE_Failure, CPLE_AppDefined, "%s", text ) +#endif + +#define MPH_SIZE 1247 + +/* + * API For handling name/value lists. + */ +int S_NameValueList_Parse( const char *text, int text_offset, + int *entry_count, + EnvisatNameValue ***entries ); +void S_NameValueList_Destroy( int *entry_count, + EnvisatNameValue ***entries ); +int S_NameValueList_FindKey( const char *key, + int entry_count, + EnvisatNameValue **entries ); +const char *S_NameValueList_FindValue( const char *key, + int entry_count, + EnvisatNameValue **entries, + const char * default_value ); + +int S_NameValueList_Rewrite( VSILFILE *fp, int entry_count, + EnvisatNameValue **entries ); + +EnvisatNameValue * + S_EnivsatFile_FindNameValue( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char * key ); + + + + +/*----------------------------------------------------------------------------- + +Name: + Envisat_SetupLevel0 + +Purpose: + Patch up missing information about SPH, and datasets for incomplete + level 0 signal datasets. + +Description: + +Inputs: + self -- Envisat file handle. + +Outputs: + +Returns: + SUCCESS or FAILURE + +-----------------------------------------------------------------------------*/ + +static int EnvisatFile_SetupLevel0( EnvisatFile *self ) + +{ + int file_length; + unsigned char header[68]; + EnvisatDatasetInfo *ds_info; + + self->dsd_offset = 0; + self->ds_count = 1; + self->ds_info = (EnvisatDatasetInfo **) + CPLCalloc(sizeof(EnvisatDatasetInfo*),self->ds_count); + + if( self->ds_info == NULL ) + return FAILURE; + + /* + * Figure out how long the file is. + */ + + VSIFSeekL( self->fp, 0, SEEK_END ); + file_length = (int) VSIFTellL( self->fp ); + + /* + * Read the first record header, and verify the well known values. + */ + VSIFSeekL( self->fp, 3203, SEEK_SET ); + VSIFReadL( header, 68, 1, self->fp ); + + if( header[38] != 0 || header[39] != 0x1d + || header[40] != 0 || header[41] != 0x54 ) + { + SendError( "Didn't get expected Data Field Header Length, or Mode ID\n" + "values for the first data record." ); + return FAILURE; + } + + /* + * Then build the dataset into structure from that. + */ + ds_info = (EnvisatDatasetInfo *) CPLCalloc(sizeof(EnvisatDatasetInfo),1); + + ds_info->ds_name = CPLStrdup( "ASAR SOURCE PACKETS " ); + ds_info->ds_type = CPLStrdup("M"); + ds_info->filename = CPLStrdup(" "); + ds_info->ds_offset = 3203; + ds_info->dsr_size = -1; + ds_info->num_dsr = 0; + ds_info->ds_size = file_length - ds_info->ds_offset; + + self->ds_info[0] = ds_info; + + return SUCCESS; +} + +/*----------------------------------------------------------------------------- + +Name: + Envisat_Open + +Purpose: + Open an ENVISAT formatted file, and read all headers. + +Description: + +Inputs: + filename -- name of Envisat file. + mode -- either "r" for read access, or "r+" for read/write access. + +Outputs: + self -- file handle, NULL on FAILURE. + +Returns: + SUCCESS or FAILURE + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_Open( EnvisatFile **self_ptr, + const char *filename, + const char *mode ) + +{ + VSILFILE *fp; + EnvisatFile *self; + char mph_data[1248]; + char *sph_data, *ds_data; + int sph_size, num_dsd, dsd_size, i; + + *self_ptr = NULL; + + /* + * Check for legal mode argument. Force to be binary for correct + * operation on DOS file systems. + */ + if( strcmp(mode,"r") == 0 ) + mode = "rb"; + else if( strcmp(mode,"r+") == 0 ) + mode = "rb+"; + else + { + SendError( "Illegal mode value used in EnvisatFile_Open(), only " + "\"r\" and \"r+\" are supported." ); + return FAILURE; + } + + /* + * Try to open the file, and report failure. + */ + + fp = VSIFOpenL( filename, mode ); + + if( fp == NULL ) + { + char error_buf[2048]; + + sprintf( error_buf, + "Unable to open file \"%s\" in EnvisatFile_Open().", + filename ); + + SendError( error_buf ); + return FAILURE; + } + + /* + * Create, and initialize the EnvisatFile structure. + */ + self = (EnvisatFile *) CPLCalloc(sizeof(EnvisatFile),1); + if( self == NULL ) + return FAILURE; + + self->fp = fp; + self->filename = CPLStrdup(filename); + self->header_dirty = 0; + self->updatable = (strcmp(mode,"rb+") == 0); + + /* + * Read the MPH, and process it as a group of name/value pairs. + */ + + if( VSIFReadL( mph_data, 1, MPH_SIZE, fp ) != MPH_SIZE ) + { + CPLFree( self ); + SendError( "VSIFReadL() for mph failed." ); + return FAILURE; + } + + mph_data[MPH_SIZE] = '\0'; + if( S_NameValueList_Parse( mph_data, 0, + &(self->mph_count), + &(self->mph_entries) ) == FAILURE ) + return FAILURE; + + /* + * Is this an incomplete level 0 file? + */ + if( EnvisatFile_GetKeyValueAsInt( self, MPH, "SPH_SIZE", -1 ) == 0 + && strncmp(EnvisatFile_GetKeyValueAsString( self, MPH, "PRODUCT", ""), + "ASA_IM__0P", 10) == 0 ) + { + + if( EnvisatFile_SetupLevel0( self ) == FAILURE ) + { + EnvisatFile_Close( self ); + return FAILURE; + } + else + { + *self_ptr = self; + return SUCCESS; + } + } + + /* + * Read the SPH, and process it as a group of name/value pairs. + */ + sph_size = EnvisatFile_GetKeyValueAsInt( self, MPH, "SPH_SIZE", 0 ); + + if( sph_size == 0 ) + { + SendError( "File does not appear to have SPH," + " SPH_SIZE not set, or zero." ); + return FAILURE; + } + + sph_data = (char *) CPLMalloc(sph_size + 1 ); + if( sph_data == NULL ) + return FAILURE; + + if( (int) VSIFReadL( sph_data, 1, sph_size, fp ) != sph_size ) + { + CPLFree( self ); + SendError( "VSIFReadL() for sph failed." ); + return FAILURE; + } + + sph_data[sph_size] = '\0'; + ds_data = strstr(sph_data,"DS_NAME"); + if( ds_data != NULL ) + { + self->dsd_offset = (int) (ds_data - sph_data) + MPH_SIZE; + *(ds_data-1) = '\0'; + } + + if( S_NameValueList_Parse( sph_data, MPH_SIZE, + &(self->sph_count), + &(self->sph_entries) ) == FAILURE ) + return FAILURE; + + /* + * Parse the Dataset Definitions. + */ + num_dsd = EnvisatFile_GetKeyValueAsInt( self, MPH, "NUM_DSD", 0 ); + dsd_size = EnvisatFile_GetKeyValueAsInt( self, MPH, "DSD_SIZE", 0 ); + + if( num_dsd > 0 && ds_data == NULL ) + { + SendError( "DSDs indicated in MPH, but not found in SPH." ); + return FAILURE; + } + + self->ds_info = (EnvisatDatasetInfo **) + CPLCalloc(sizeof(EnvisatDatasetInfo*),num_dsd); + if( self->ds_info == NULL ) + return FAILURE; + + for( i = 0; i < num_dsd; i++ ) + { + int dsdh_count = 0; + EnvisatNameValue **dsdh_entries = NULL; + char *dsd_data; + EnvisatDatasetInfo *ds_info; + + /* + * We parse each DSD grouping into a name/value list. + */ + dsd_data = ds_data + i * dsd_size; + dsd_data[dsd_size-1] = '\0'; + + if( S_NameValueList_Parse( dsd_data, 0, + &dsdh_count, &dsdh_entries ) == FAILURE ) + return FAILURE; + + /* + * Then build the dataset into structure from that. + */ + ds_info = (EnvisatDatasetInfo *) CPLCalloc(sizeof(EnvisatDatasetInfo),1); + + ds_info->ds_name = CPLStrdup( + S_NameValueList_FindValue( "DS_NAME", + dsdh_count, dsdh_entries, "" )); + ds_info->ds_type = CPLStrdup( + S_NameValueList_FindValue( "DS_TYPE", + dsdh_count, dsdh_entries, "" )); + ds_info->filename = CPLStrdup( + S_NameValueList_FindValue( "FILENAME", + dsdh_count, dsdh_entries, "" )); + ds_info->ds_offset = atoi( + S_NameValueList_FindValue( "DS_OFFSET", + dsdh_count, dsdh_entries, "0" )); + ds_info->ds_size = atoi( + S_NameValueList_FindValue( "DS_SIZE", + dsdh_count, dsdh_entries, "0" )); + ds_info->num_dsr = atoi( + S_NameValueList_FindValue( "NUM_DSR", + dsdh_count, dsdh_entries, "0" )); + ds_info->dsr_size = atoi( + S_NameValueList_FindValue( "DSR_SIZE", + dsdh_count, dsdh_entries, "0" )); + + S_NameValueList_Destroy( &dsdh_count, &dsdh_entries ); + + self->ds_info[i] = ds_info; + self->ds_count++; + } + + CPLFree( sph_data ); + + /* + * Return successfully. + */ + *self_ptr = self; + + return SUCCESS; +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_Create + +Purpose: + Create a new ENVISAT formatted file based on a template file. + +Description: + +Inputs: + filename -- name of Envisat file. + template_file -- name of envisat file header to utilize as template. + +Outputs: + self -- file handle, NULL on FAILURE. + +Returns: + SUCCESS or FAILURE + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_Create( EnvisatFile **self_ptr, + const char *filename, + const char *template_file ) + +{ + int template_size; + char *template_data; + VSILFILE *fp; + + /* + * Try to open the template file, and read it into memory. + */ + + fp = VSIFOpenL( template_file, "rb" ); + + if( fp == NULL ) + { + char error_buf[2048]; + + sprintf( error_buf, + "Unable to open file \"%s\" in EnvisatFile_Create().", + template_file ); + + SendError( error_buf ); + return FAILURE; + } + + VSIFSeekL( fp, 0, SEEK_END ); + template_size = (int) VSIFTellL( fp ); + + template_data = (char *) CPLMalloc(template_size); + + VSIFSeekL( fp, 0, SEEK_SET ); + VSIFReadL( template_data, template_size, 1, fp ); + VSIFCloseL( fp ); + + /* + * Try to write the template out to the new filename. + */ + + fp = VSIFOpenL( filename, "wb" ); + if( fp == NULL ) + { + char error_buf[2048]; + + sprintf( error_buf, + "Unable to open file \"%s\" in EnvisatFile_Create().", + filename ); + + SendError( error_buf ); + return FAILURE; + } + + VSIFWriteL( template_data, template_size, 1, fp ); + VSIFCloseL( fp ); + + CPLFree( template_data ); + + /* + * Now just open the file normally. + */ + + return EnvisatFile_Open( self_ptr, filename, "r+" ); +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_GetCurrentLength + +Purpose: + Fetch the current file length. + +Description: + The length is computed by scanning the dataset definitions, not the + physical file length. + +Inputs: + self -- the file to operate on. + +Outputs: + +Returns: + Returns length or -1 on failure. + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_GetCurrentLength( EnvisatFile *self ) + +{ + int length; + int ds; + int ds_offset; + int ds_size; + + length = MPH_SIZE + + EnvisatFile_GetKeyValueAsInt( self, MPH, "SPH_SIZE", 0 ); + + for( ds = 0; + EnvisatFile_GetDatasetInfo( self, ds, NULL, NULL, NULL, + &ds_offset, &ds_size, NULL, NULL ) + != FAILURE; + ds++ ) + { + if( ds_offset != 0 && (ds_offset+ds_size) > length ) + length = ds_offset + ds_size; + } + + return length; +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_RewriteHeader + +Purpose: + Update the envisat file header on disk to match the in-memory image. + +Description: + +Inputs: + self -- handle for file to close. + +Outputs: + +Returns: + SUCCESS or FAILURE. + +-----------------------------------------------------------------------------*/ + +static int EnvisatFile_RewriteHeader( EnvisatFile *self ) + +{ + int dsd, dsd_size; + + /* + * Rewrite MPH and SPH headers. + */ + if( S_NameValueList_Rewrite( self->fp, + self->mph_count, self->mph_entries ) == FAILURE ) + return FAILURE; + + if( S_NameValueList_Rewrite( self->fp, + self->sph_count, self->sph_entries ) == FAILURE ) + return FAILURE; + + /* + * Rewrite DSDs. We actually have to read each, and reparse to set + * the individual parameters properly. + */ + dsd_size = EnvisatFile_GetKeyValueAsInt( self, MPH, "DSD_SIZE", 0 ); + if( dsd_size == 0 ) + return FAILURE; + + for( dsd = 0; dsd < self->ds_count; dsd++ ) + { + char *dsd_text; + int dsdh_count = 0, key_index; + EnvisatNameValue **dsdh_entries = NULL; + + dsd_text = (char *) CPLCalloc(1,dsd_size+1); + if( VSIFSeekL( self->fp, self->dsd_offset + dsd * dsd_size, + SEEK_SET ) != 0 ) + { + SendError( "VSIFSeekL() failed in EnvisatFile_RewriteHeader()" ); + return FAILURE; + } + + if( (int) VSIFReadL( dsd_text, 1, dsd_size, self->fp ) != dsd_size ) + { + SendError( "VSIFReadL() failed in EnvisatFile_RewriteHeader()" ); + return FAILURE; + } + + if( S_NameValueList_Parse( dsd_text, self->dsd_offset + dsd*dsd_size, + &dsdh_count, &dsdh_entries ) == FAILURE ) + return FAILURE; + + CPLFree( dsd_text ); + + key_index = S_NameValueList_FindKey( "DS_OFFSET", + dsdh_count, dsdh_entries ); + if( key_index == -1 ) + continue; + + sprintf( dsdh_entries[key_index]->value, "%+021d", + self->ds_info[dsd]->ds_offset ); + + key_index = S_NameValueList_FindKey( "DS_SIZE", + dsdh_count, dsdh_entries ); + sprintf( dsdh_entries[key_index]->value, "%+021d", + self->ds_info[dsd]->ds_size ); + + key_index = S_NameValueList_FindKey( "NUM_DSR", + dsdh_count, dsdh_entries ); + sprintf( dsdh_entries[key_index]->value, "%+011d", + self->ds_info[dsd]->num_dsr ); + + key_index = S_NameValueList_FindKey( "DSR_SIZE", + dsdh_count, dsdh_entries ); + sprintf( dsdh_entries[key_index]->value, "%+011d", + self->ds_info[dsd]->dsr_size ); + + if( S_NameValueList_Rewrite( self->fp, dsdh_count, dsdh_entries ) + == FAILURE ) + return FAILURE; + + S_NameValueList_Destroy( &dsdh_count, &dsdh_entries ); + } + + self->header_dirty = 0; + + return SUCCESS; +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_Close + +Purpose: + Close an ENVISAT formatted file, releasing all associated resources. + +Description: + +Inputs: + self -- handle for file to close. + +Outputs: + +Returns: + + +-----------------------------------------------------------------------------*/ + +void EnvisatFile_Close( EnvisatFile *self ) + +{ + int i; + + /* + * Do we need to write out the header information? + */ + if( self->header_dirty ) + EnvisatFile_RewriteHeader( self ); + + /* + * Close file. + */ + if( self->fp != NULL ) + VSIFCloseL( self->fp ); + + /* + * Clean up data structures. + */ + S_NameValueList_Destroy( &(self->mph_count), &(self->mph_entries) ); + S_NameValueList_Destroy( &(self->sph_count), &(self->sph_entries) ); + + for( i = 0; i < self->ds_count; i++ ) + { + if( self->ds_info != NULL && self->ds_info[i] != NULL ) + { + CPLFree( self->ds_info[i]->ds_name ); + CPLFree( self->ds_info[i]->ds_type ); + CPLFree( self->ds_info[i]->filename ); + CPLFree( self->ds_info[i] ); + } + } + if( self->ds_info != NULL ) + CPLFree( self->ds_info ); + if( self->filename != NULL ) + CPLFree( self->filename ); + + CPLFree( self ); +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_GetFilename + +Purpose: + Fetch name of this Envisat file. + +Description: + +Inputs: + self -- handle for file to get name of. + +Outputs: + +Returns: + const pointer to internal copy of the filename. Do not alter or free. + + +-----------------------------------------------------------------------------*/ + +const char *EnvisatFile_GetFilename( EnvisatFile *self ) + +{ + return self->filename; +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_GetKeyByIndex() + +Purpose: + Fetch the key with the indicated index. + +Description: + This function can be used to "discover" the set of available keys by + by scanning with index values starting at zero and ending when a NULL + is returned. + +Inputs: + self -- the file to be searched. + mph_or_sph -- Either MPH or SPH depending on the header to be searched. + key_index -- key index, from zero to number of keys-1. + +Outputs: + +Returns: + pointer to key name or NULL on failure. + +-----------------------------------------------------------------------------*/ + +const char *EnvisatFile_GetKeyByIndex( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + int key_index ) + +{ + int entry_count; + EnvisatNameValue **entries; + + /* + * Select source list. + */ + if( mph_or_sph == MPH ) + { + entry_count = self->mph_count; + entries = self->mph_entries; + } + else + { + entry_count = self->sph_count; + entries = self->sph_entries; + } + + if( key_index < 0 || key_index >= entry_count ) + return NULL; + else + return entries[key_index]->key; +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_GetKeyValueAsString() + +Purpose: + Fetch the value associated with the indicated key as a string. + +Description: + +Inputs: + self -- the file to be searched. + mph_or_sph -- Either MPH or SPH depending on the header to be searched. + key -- the key (name) to be searched for. + default_value -- the value to return if the key is not found. + +Outputs: + +Returns: + pointer to value string, or default_value if not found. + +-----------------------------------------------------------------------------*/ + +const char *EnvisatFile_GetKeyValueAsString( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key, + const char *default_value ) + +{ + int entry_count, key_index; + EnvisatNameValue **entries; + + /* + * Select source list. + */ + if( mph_or_sph == MPH ) + { + entry_count = self->mph_count; + entries = self->mph_entries; + } + else + { + entry_count = self->sph_count; + entries = self->sph_entries; + } + + /* + * Find and return the value. + */ + key_index = S_NameValueList_FindKey( key, entry_count, entries ); + if( key_index == -1 ) + return default_value; + else + return entries[key_index]->value; +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_SetKeyValueAsString() + +Purpose: + Set the value associated with the indicated key as a string. + +Description: + +Inputs: + self -- the file to be searched. + mph_or_sph -- Either MPH or SPH depending on the header to be searched. + key -- the key (name) to be searched for. + value -- the value to assign. + +Outputs: + +Returns: + SUCCESS or FAILURE. + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_SetKeyValueAsString( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key, + const char *value ) + +{ + int entry_count, key_index; + EnvisatNameValue **entries; + + if( !self->updatable ) + { + SendError( "File not opened for update access." ); + return FAILURE; + } + + /* + * Select source list. + */ + if( mph_or_sph == MPH ) + { + entry_count = self->mph_count; + entries = self->mph_entries; + } + else + { + entry_count = self->sph_count; + entries = self->sph_entries; + } + + /* + * Find and return the value. + */ + key_index = S_NameValueList_FindKey( key, entry_count, entries ); + if( key_index == -1 ) + { + char error_buf[2048]; + + sprintf( error_buf, + "Unable to set header field \"%s\", field not found.", + key ); + + SendError( error_buf ); + return FAILURE; + } + + self->header_dirty = 1; + if( strlen(value) > strlen(entries[key_index]->value) ) + { + strncpy( entries[key_index]->value, value, + strlen(entries[key_index]->value) ); + } + else + { + memset( entries[key_index]->value, ' ', + strlen(entries[key_index]->value) ); + strncpy( entries[key_index]->value, value, strlen(value) ); + } + + return SUCCESS; +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_GetKeyValueAsInt() + +Purpose: + Fetch the value associated with the indicated key as an integer. + +Description: + +Inputs: + self -- the file to be searched. + mph_or_sph -- Either MPH or SPH depending on the header to be searched. + key -- the key (name) to be searched for. + default_value -- the value to return if the key is not found. + +Outputs: + +Returns: + key value, or default_value if key not found. + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_GetKeyValueAsInt( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key, + int default_value ) + +{ + int entry_count, key_index; + EnvisatNameValue **entries; + + /* + * Select source list. + */ + if( mph_or_sph == MPH ) + { + entry_count = self->mph_count; + entries = self->mph_entries; + } + else + { + entry_count = self->sph_count; + entries = self->sph_entries; + } + + /* + * Find and return the value. + */ + key_index = S_NameValueList_FindKey( key, entry_count, entries ); + if( key_index == -1 ) + return default_value; + else + return atoi(entries[key_index]->value); +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_SetKeyValueAsInt() + +Purpose: + Set the value associated with the indicated key as an integer. + +Description: + +Inputs: + self -- the file to be searched. + mph_or_sph -- Either MPH or SPH depending on the header to be searched. + key -- the key (name) to be searched for. + value -- the value to assign. + +Outputs: + +Returns: + SUCCESS or FAILURE. + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_SetKeyValueAsInt( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key, + int value ) + +{ + char format[32], string_value[128]; + const char *prototype_value; + + + prototype_value = EnvisatFile_GetKeyValueAsString( self, mph_or_sph, key, NULL); + if( prototype_value == NULL ) + { + char error_buf[2048]; + + sprintf( error_buf, + "Unable to set header field \"%s\", field not found.", + key ); + + SendError( error_buf ); + return FAILURE; + } + + sprintf( format, "%%+0%dd", (int) strlen(prototype_value) ); + sprintf( string_value, format, value ); + + return EnvisatFile_SetKeyValueAsString( self, mph_or_sph, key, string_value ); +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_GetKeyValueAsDouble() + +Purpose: + Fetch the value associated with the indicated key as a double. + +Description: + +Inputs: + self -- the file to be searched. + mph_or_sph -- Either MPH or SPH depending on the header to be searched. + key -- the key (name) to be searched for. + default_value -- the value to return if the key is not found. + +Outputs: + +Returns: + key value, or default_value if key not found. + +-----------------------------------------------------------------------------*/ + +double EnvisatFile_GetKeyValueAsDouble( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key, + double default_value ) + +{ + int entry_count, key_index; + EnvisatNameValue **entries; + + /* + * Select source list. + */ + if( mph_or_sph == MPH ) + { + entry_count = self->mph_count; + entries = self->mph_entries; + } + else + { + entry_count = self->sph_count; + entries = self->sph_entries; + } + + /* + * Find and return the value. + */ + key_index = S_NameValueList_FindKey( key, entry_count, entries ); + if( key_index == -1 ) + return default_value; + else + return atof(entries[key_index]->value); +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_SetKeyValueAsDouble() + +Purpose: + Set the value associated with the indicated key as a double. + +Description: + Note that this function attempts to format the new value similarly to + the previous value. In some cases (expecially exponential values) this + may not work out well. In case of problems the caller is encourage to + format the value themselves, and use the EnvisatFile_SetKeyValueAsString + function, but taking extreme care about the string length. + +Inputs: + self -- the file to be searched. + mph_or_sph -- Either MPH or SPH depending on the header to be searched. + key -- the key (name) to be searched for. + value -- the value to assign. + +Outputs: + +Returns: + SUCCESS or FAILURE. + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_SetKeyValueAsDouble( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key, + double value ) + +{ + char format[32], string_value[128]; + const char *prototype_value; + int length; + + prototype_value = EnvisatFile_GetKeyValueAsString( self, mph_or_sph, key, NULL); + if( prototype_value == NULL ) + { + char error_buf[2048]; + + sprintf( error_buf, + "Unable to set header field \"%s\", field not found.", + key ); + + SendError( error_buf ); + return FAILURE; + } + + length = strlen(prototype_value); + if( prototype_value[length-4] == 'E' ) + { + sprintf( format, "%%+%dE", length-4 ); + sprintf( string_value, format, value ); + } + else + { + int decimals = 0, i; + for( i = length-1; i > 0; i-- ) + { + if( prototype_value[i] == '.' ) + break; + + decimals++; + } + + sprintf( format, "%%+0%d.%df", length, decimals ); + CPLsprintf( string_value, format, value ); + + if( (int)strlen(string_value) > length ) + string_value[length] = '\0'; + } + + return EnvisatFile_SetKeyValueAsString( self, mph_or_sph, key, string_value ); +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_GetDatasetIndex() + +Purpose: + Fetch the datasat index give a dataset name. + +Description: + The provided name is extended with spaces, so it isn't necessary for the + application to pass all the passing spaces. + +Inputs: + self -- the file to be searched. + ds_name -- the name (DS_NAME) of the dataset to find. + +Outputs: + +Returns: + Dataset index that matches, or -1 if none found. + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_GetDatasetIndex( EnvisatFile *self, const char *ds_name ) + +{ + int i; + char padded_ds_name[100]; + + /* + * Padd the name. While the normal product spec says the DS_NAME will + * be 28 characters, I try to pad more than this incase the specification + * is changed. + */ + strncpy( padded_ds_name, ds_name, sizeof(padded_ds_name) ); + padded_ds_name[sizeof(padded_ds_name)-1] = 0; + for( i = strlen(padded_ds_name); (size_t)i < sizeof(padded_ds_name)-1; i++ ) + { + padded_ds_name[i] = ' '; + } + padded_ds_name[i] = '\0'; + + /* + * Compare only for the full length of DS_NAME we have saved. + */ + for( i = 0; i < self->ds_count; i++ ) + { + if( strncmp( padded_ds_name, self->ds_info[i]->ds_name, + strlen(self->ds_info[i]->ds_name) ) == 0 ) + { + return i; + } + } + + return -1; +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_GetDatasetInfo() + +Purpose: + Fetch the information associated with a dataset definition. + +Description: + The returned strings are pointers to internal copies, and should not be + modified, or freed. Note, any of the "output" parameters can safely be + NULL if it is not needed. + +Inputs: + self -- the file to be searched. + ds_index -- the dataset index to fetch + +Outputs: + ds_name -- the dataset symbolic name, ie 'MDS1 SQ ADS '. + ds_type -- the dataset type, ie. 'A', not sure of valid values. + filename -- dataset filename, normally spaces, or 'NOT USED '. + ds_offset -- the byte offset in the whole file to the first byte of + dataset data. This is 0 for unused datasets. + ds_size -- the size, in bytes, of the whole dataset. + num_dsr -- the number of records in the dataset. + dsr_size -- the size of one record in the dataset in bytes, -1 if + records are variable sized. + +Returns: + SUCCESS if dataset exists, or FAILURE if ds_index is out of range. + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_GetDatasetInfo( EnvisatFile *self, + int ds_index, + char **ds_name, + char **ds_type, + char **filename, + int *ds_offset, + int *ds_size, + int *num_dsr, + int *dsr_size ) + +{ + if( ds_index < 0 || ds_index >= self->ds_count ) + return FAILURE; + + if( ds_name != NULL ) + *ds_name = self->ds_info[ds_index]->ds_name; + if( ds_type != NULL ) + *ds_type = self->ds_info[ds_index]->ds_type; + if( filename != NULL ) + *filename = self->ds_info[ds_index]->filename; + if( ds_offset != NULL ) + *ds_offset = self->ds_info[ds_index]->ds_offset; + if( ds_size != NULL ) + *ds_size = self->ds_info[ds_index]->ds_size; + if( num_dsr != NULL ) + *num_dsr = self->ds_info[ds_index]->num_dsr; + if( dsr_size != NULL ) + *dsr_size = self->ds_info[ds_index]->dsr_size; + + return SUCCESS; +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_SetDatasetInfo() + +Purpose: + Update the information associated with a dataset definition. + +Description: + +Inputs: + self -- the file to be searched. + ds_index -- the dataset index to fetch + ds_offset -- the byte offset in the whole file to the first byte of + dataset data. This is 0 for unused datasets. + ds_size -- the size, in bytes, of the whole dataset. + num_dsr -- the number of records in the dataset. + dsr_size -- the size of one record in the dataset in bytes, -1 if + records are variable sized. + +Outputs: + +Returns: + SUCCESS or FAILURE. + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_SetDatasetInfo( EnvisatFile *self, + int ds_index, + int ds_offset, + int ds_size, + int num_dsr, + int dsr_size ) + +{ + if( ds_index < 0 || ds_index >= self->ds_count ) + return FAILURE; + + self->ds_info[ds_index]->ds_offset = ds_offset; + self->ds_info[ds_index]->ds_size = ds_size; + self->ds_info[ds_index]->num_dsr = num_dsr; + self->ds_info[ds_index]->dsr_size = dsr_size; + self->header_dirty = 1; + + return SUCCESS; +} + + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_ReadDatasetChunk() + +Purpose: + Read an arbitrary chunk of a dataset. + +Description: + Note that no range checking is made on offset and size, and data may be + read from outside the dataset if they are inappropriate. + +Inputs: + self -- the file to be searched. + ds_index -- the index of dataset to access. + offset -- byte offset within database to read. + size -- size of buffer to fill in bytes. + buffer -- buffer to load data into + +Outputs: + buffer is updated on SUCCESS. + +Returns: + SUCCESS or FAILURE + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_ReadDatasetChunk( EnvisatFile *self, + int ds_index, + int offset, + int size, + void * buffer ) + +{ + if( ds_index < 0 || ds_index >= self->ds_count ) + { + SendError( "Attempt to read non-existant dataset in " + "EnvisatFile_ReadDatasetChunk()" ); + return FAILURE; + } + + if( offset < 0 + || offset + size > self->ds_info[ds_index]->ds_size ) + { + SendError( "Attempt to read beyond end of dataset in " + "EnvisatFile_ReadDatasetChunk()" ); + return FAILURE; + } + + if( VSIFSeekL( self->fp, self->ds_info[ds_index]->ds_offset+offset, SEEK_SET ) + != 0 ) + { + SendError( "seek failed in EnvisatFile_ReadChunk()" ); + return FAILURE; + } + + if( (int) VSIFReadL( buffer, 1, size, self->fp ) != size ) + { + SendError( "read failed in EnvisatFile_ReadChunk()" ); + return FAILURE; + } + + return SUCCESS; +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_WriteDatasetRecord() + +Purpose: + Write an arbitrary dataset record. + +Description: + Note that no range checking is made on offset and size, and data may be + read from outside the dataset if they are inappropriate. + +Inputs: + self -- the file to be searched. + ds_index -- the index of dataset to access. + record_index -- the record to write. + record_buffer -- buffer to load data into + +Outputs: + +Returns: + SUCCESS or FAILURE + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_WriteDatasetRecord( EnvisatFile *self, + int ds_index, + int record_index, + void *buffer ) + +{ + int absolute_offset; + int result; + + if( ds_index < 0 || ds_index >= self->ds_count ) + { + SendError( "Attempt to write non-existant dataset in " + "EnvisatFile_WriteDatasetRecord()" ); + return FAILURE; + } + + if( record_index < 0 + || record_index >= self->ds_info[ds_index]->num_dsr ) + { + SendError( "Attempt to write beyond end of dataset in " + "EnvisatFile_WriteDatasetRecord()" ); + return FAILURE; + } + + absolute_offset = self->ds_info[ds_index]->ds_offset + + record_index * self->ds_info[ds_index]->dsr_size; + + if( VSIFSeekL( self->fp, absolute_offset, SEEK_SET ) != 0 ) + { + SendError( "seek failed in EnvisatFile_WriteDatasetRecord()" ); + return FAILURE; + } + + result = VSIFWriteL( buffer, 1, self->ds_info[ds_index]->dsr_size, self->fp ); + if( result != self->ds_info[ds_index]->dsr_size ) + { + SendError( "write failed in EnvisatFile_WriteDatasetRecord()" ); + return FAILURE; + } + + return SUCCESS; +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_ReadDatasetRecord() + +Purpose: + Read an arbitrary dataset record. + +Description: + Note that no range checking is made on offset and size, and data may be + read from outside the dataset if they are inappropriate. + +Inputs: + self -- the file to be searched. + ds_index -- the index of dataset to access. + record_index -- the record to write. + record_buffer -- buffer to load data into + +Outputs: + +Returns: + SUCCESS or FAILURE + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_ReadDatasetRecordChunk(EnvisatFile*,int,int,void*,int,int); + +int EnvisatFile_ReadDatasetRecord( EnvisatFile *self, + int ds_index, + int record_index, + void *buffer ) +{ + return EnvisatFile_ReadDatasetRecordChunk( self, + ds_index, record_index, buffer, 0 , -1 ) ; +} + +/*----------------------------------------------------------------------------- + +Name: + EnvisatFile_ReadDatasetRecordChunk() + +Purpose: + Read a part of an arbitrary dataset record. + +Description: + Note that no range checking is made on dataset's offset and size, + and data may be read from outside the dataset if they are inappropriate. + +Inputs: + self -- the file to be searched. + ds_index -- the index of dataset to access. + record_index -- the record to write. + record_buffer -- buffer to load data into + offset -- chunk offset relative to the record start (zerro offset) + size -- chunk size (set -1 to read from offset to the records' end) + +Outputs: + +Returns: + SUCCESS or FAILURE + +-----------------------------------------------------------------------------*/ + +int EnvisatFile_ReadDatasetRecordChunk( EnvisatFile *self, + int ds_index, + int record_index, + void *buffer, + int offset, int size ) +{ + int absolute_offset; + int result; + int dsr_size = self->ds_info[ds_index]->dsr_size ; + + if (( offset < 0 )||(offset > dsr_size)) + { + SendError( "Invalid chunk offset in " + "EnvisatFile_ReadDatasetRecordChunk()" ); + return FAILURE; + } + + if ( size < 0 ) + size = dsr_size - offset ; + + if( ds_index < 0 || ds_index >= self->ds_count ) + { + SendError( "Attempt to read non-existant dataset in " + "EnvisatFile_ReadDatasetRecordChunk()" ); + return FAILURE; + } + + if( record_index < 0 + || record_index >= self->ds_info[ds_index]->num_dsr ) + { + SendError( "Attempt to read beyond end of dataset in " + "EnvisatFile_ReadDatasetRecordChunk()" ); + return FAILURE; + } + + if( (offset + size) > dsr_size ) + { + SendError( "Attempt to read beyond the record's boundary" + "EnvisatFile_ReadDatasetRecord()" ); + return FAILURE; + } + + absolute_offset = self->ds_info[ds_index]->ds_offset + + record_index * dsr_size + offset ; + + if( VSIFSeekL( self->fp, absolute_offset, SEEK_SET ) != 0 ) + { + SendError( "seek failed in EnvisatFile_ReadDatasetRecordChunk()" ); + return FAILURE; + } + + result = VSIFReadL( buffer, 1, size, self->fp ); + if( result != size ) + { + SendError( "read failed in EnvisatFile_ReadDatasetRecord()" ); + return FAILURE; + } + + return SUCCESS; +} + +/*----------------------------------------------------------------------------- + +Name: + S_NameValueList_FindKey() + +Purpose: + Search for given key in list of name/value pairs. + +Description: + Scans list looking for index of EnvisatNameValue where the key matches + (case sensitive) the passed in name. + +Inputs: + key -- the key, such as "SLICE_POSITION" being searched for. + entry_count -- the number of items in the entries array. + entries -- array of name/value structures to search. + +Outputs: + +Returns: + array index into entries, or -1 on failure. + +-----------------------------------------------------------------------------*/ + +int S_NameValueList_FindKey( const char *key, + int entry_count, + EnvisatNameValue **entries ) + +{ + int i; + + for( i = 0; i < entry_count; i++ ) + { + if( strcmp(entries[i]->key,key) == 0 ) + return i; + } + + return -1; +} + +/*----------------------------------------------------------------------------- + +Name: + S_NameValueList_FindValue() + +Purpose: + Search for given key in list of name/value pairs, and return value. + +Description: + Returns value string or default if key not found. + +Inputs: + key -- the key, such as "SLICE_POSITION" being searched for. + entry_count -- the number of items in the entries array. + entries -- array of name/value structures to search. + default_value -- value to use if key not found. + +Outputs: + +Returns: + value string, or default if key not found. + +-----------------------------------------------------------------------------*/ + +const char *S_NameValueList_FindValue( const char *key, + int entry_count, + EnvisatNameValue **entries, + const char *default_value ) + +{ + int i; + + i = S_NameValueList_FindKey( key, entry_count, entries ); + if( i == -1 ) + return default_value; + else + return entries[i]->value; +} + +/*----------------------------------------------------------------------------- + +Name: + S_NameValueList_Parse() + +Purpose: + Parse a block of envisat style name/value pairs into an + EnvisatNameValue structure list. + +Description: + The passed in text block should be zero terminated. The entry_count, + and entries should be pre-initialized (normally to 0 and NULL). + +Inputs: + text -- the block of text, multiple lines, to be processed. + +Outputs: + entry_count -- returns with the updated number of entries in the + entries array. + entries -- returns with updated array info structures. + +Returns: + SUCCESS or FAILURE + +-----------------------------------------------------------------------------*/ + +int S_NameValueList_Parse( const char *text, int text_offset, + int *entry_count, + EnvisatNameValue ***entries ) + +{ + const char *next_text = text; + + /* + * Loop over each input line in the text block. + */ + while( *next_text != '\0' ) + { + char line[1024]; + int line_len = 0, equal_index, src_char, line_offset; + EnvisatNameValue *entry; + + /* + * Extract one line of text into the "line" buffer, and remove the + * newline character. Eat leading spaces. + */ + while( *next_text == ' ' ) + { + next_text++; + } + line_offset = (int) (next_text - text) + text_offset; + while( *next_text != '\0' && *next_text != '\n' ) + { + if( line_len > ((int)sizeof(line) - 1) ) + { + SendError( "S_NameValueList_Parse(): " + "Corrupt line, longer than 1024 characters." ); + return FAILURE; + } + + line[line_len++] = *(next_text++); + } + + line[line_len] = '\0'; + if( *next_text == '\n' ) + next_text++; + + /* + * Blank lines are permitted. We will skip processing of any line + * that doesn't have an equal sign, under the assumption it is + * white space. + */ + if( strstr( line, "=") == NULL ) + continue; + + /* + * Create the name/value info structure. + */ + entry = (EnvisatNameValue *) CPLCalloc(sizeof(EnvisatNameValue),1); + entry->literal_line = CPLStrdup(line); + + /* + * Capture the key. We take everything up to the equal sign. There + * shouldn't be any white space, but if so, we take it as part of the + * key. + */ + equal_index = strstr(line, "=") - line; + entry->key = (char *) CPLMalloc(equal_index+1); + strncpy( entry->key, line, equal_index ); + entry->key[equal_index] = '\0'; + entry->value_offset = line_offset + equal_index + 1; + + /* + * If the next character after the equal sign is a double quote, then + * the value is a string. Suck out the text between the double quotes. + */ + if( line[equal_index+1] == '"' ) + { + for( src_char = equal_index + 2; + line[src_char] != '\0' && line[src_char] != '"'; + src_char++ ) {} + + line[src_char] = '\0'; + entry->value = CPLStrdup(line + equal_index + 2); + entry->value_offset += 1; + } + + /* + * The value is numeric, and may include a units field. + */ + else + { + for( src_char = equal_index + 1; + line[src_char] != '\0' && line[src_char] != '<' + && line[src_char] != ' '; + src_char++ ) {} + + /* capture units */ + if( line[src_char] == '<' ) + { + int dst_char; + + for( dst_char = src_char+1; + line[dst_char] != '>' && line[dst_char] != '\0'; + dst_char++ ) {} + + line[dst_char] = '\0'; + entry->units = CPLStrdup( line + src_char + 1 ); + } + + line[src_char] = '\0'; + entry->value = CPLStrdup( line + equal_index + 1 ); + } + + /* + * Add the entry to the name/value list. + */ + (*entry_count)++; + *entries = (EnvisatNameValue **) + CPLRealloc( *entries, *entry_count * sizeof(EnvisatNameValue*) ); + + if( *entries == NULL ) + { + *entry_count = 0; + return FAILURE; + } + + (*entries)[*entry_count-1] = entry; + } + + return SUCCESS; +} + +/*----------------------------------------------------------------------------- + +Name: + S_NameValueList_Rewrite() + +Purpose: + Rewrite the values of a name/value list in the file. + +Description: + +Inputs: + fp -- the VSILFILE to operate on. + entry_count -- number of entries to write. + entries -- array of entry descriptions. + +Returns: + SUCCESS or FAILURE + + +-----------------------------------------------------------------------------*/ + +int S_NameValueList_Rewrite( VSILFILE * fp, int entry_count, + EnvisatNameValue **entries ) + +{ + int i; + + for( i = 0; i < entry_count; i++ ) + { + EnvisatNameValue *entry = entries[i]; + + if( VSIFSeekL( fp, entry->value_offset, SEEK_SET ) != 0 ) + { + SendError( "VSIFSeekL() failed writing name/value list." ); + return FAILURE; + } + + if( VSIFWriteL( entry->value, 1, strlen(entry->value), fp ) != + strlen(entry->value) ) + { + SendError( "VSIFWriteL() failed writing name/value list." ); + return FAILURE; + } + } + + return SUCCESS; +} + + +/*----------------------------------------------------------------------------- + +Name: + S_NameValueList_Destroy() + +Purpose: + Free resources associated with a name/value list. + +Description: + The count, and name/value list pointers are set to 0/NULL on completion. + +Inputs: + entry_count -- returns with the updated number of entries in the + entries array. + entries -- returns with updated array info structures. + +Outputs: + entry_count -- Set to zero. + entries -- Sett o NULL. + +Returns: + + +-----------------------------------------------------------------------------*/ + +void S_NameValueList_Destroy( int *entry_count, + EnvisatNameValue ***entries ) + +{ + int i; + + for( i = 0; i < *entry_count; i++ ) + { + CPLFree( (*entries)[i]->key ); + CPLFree( (*entries)[i]->value ); + CPLFree( (*entries)[i]->units ); + CPLFree( (*entries)[i]->literal_line ); + CPLFree( (*entries)[i] ); + } + + CPLFree( *entries ); + + *entry_count = 0; + *entries = NULL; +} + +/* EOF */ diff --git a/bazaar/plugin/gdal/frmts/envisat/EnvisatFile.h b/bazaar/plugin/gdal/frmts/envisat/EnvisatFile.h new file mode 100644 index 000000000..72c3810bd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/EnvisatFile.h @@ -0,0 +1,136 @@ +/****************************************************************************** + * $Id: EnvisatFile.h 27098 2014-03-27 00:16:11Z rouault $ + * + * Project: APP ENVISAT Support + * Purpose: Low Level Envisat file access (read/write) API. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Atlantis Scientific, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __ENVISAT_FILE_H__ +#define __ENVISAT_FILE_H__ + +typedef struct EnvisatFile_tag EnvisatFile; + +typedef enum +{ + MPH = 0, + SPH = 1 +} EnvisatFile_HeaderFlag; + +int EnvisatFile_Open( EnvisatFile **self, const char *filename, + const char *mode ); +void EnvisatFile_Close( EnvisatFile *self ); +const char *EnvisatFile_GetFilename( EnvisatFile *self ); +int EnvisatFile_Create( EnvisatFile **self, const char *filename, + const char *template_file ); +int EnvisatFile_GetCurrentLength( EnvisatFile *self ); + +const char* EnvisatFile_GetKeyByIndex( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + int key_index ); + +int EnvisatFile_TestKey( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key ); + +const char *EnvisatFile_GetKeyValueAsString( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key, + const char *default_value ); + +int EnvisatFile_SetKeyValueAsString( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key, + const char *value ); + +int EnvisatFile_GetKeyValueAsInt( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key, + int default_value ); + +int EnvisatFile_SetKeyValueAsInt( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key, + int value ); + +double EnvisatFile_GetKeyValueAsDouble( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key, + double default_value ); +int EnvisatFile_SetKeyValueAsDouble( EnvisatFile *self, + EnvisatFile_HeaderFlag mph_or_sph, + const char *key, + double value ); + +int EnvisatFile_GetDatasetIndex( EnvisatFile *self, const char *ds_name ); + +int EnvisatFile_GetDatasetInfo( EnvisatFile *self, + int ds_index, + char **ds_name, + char **ds_type, + char **filename, + int *ds_offset, + int *ds_size, + int *num_dsr, + int *dsr_size ); +int EnvisatFile_SetDatasetInfo( EnvisatFile *self, + int ds_index, + int ds_offset, + int ds_size, + int num_dsr, + int dsr_size ); + +int EnvisatFile_ReadDatasetRecordChunk( EnvisatFile *self, + int ds_index, + int record_index, + void *buffer, + int offset, int size ) ; +int EnvisatFile_ReadDatasetRecord( EnvisatFile *self, + int ds_index, + int record_index, + void *record_buffer ); +int EnvisatFile_WriteDatasetRecord( EnvisatFile *self, + int ds_index, + int record_index, + void *record_buffer ); +int EnvisatFile_ReadDatasetChunk( EnvisatFile *self, + int ds_index, + int offset, + int size, + void *buffer ); + + +#ifndef FAILURE +# define FAILURE 1 +#endif +#ifndef SUCCESS +# define SUCCESS 0 +#endif + +#endif /* __ENVISAT_FILE_H__ */ + +/* EOF */ + + + diff --git a/bazaar/plugin/gdal/frmts/envisat/GNUmakefile b/bazaar/plugin/gdal/frmts/envisat/GNUmakefile new file mode 100644 index 000000000..a073beafb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/GNUmakefile @@ -0,0 +1,26 @@ + +OBJ = EnvisatFile.o records.o adsrange.o unwrapgcps.o envisatdataset.o + +include ../../GDALmake.opt + +XTRA_OPT = -I../raw + +CPPFLAGS := $(XTRA_OPT) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +$(O_OBJ): ../raw/rawdataset.h + +envisat_dump: envisat_dump.$(OBJ_EXT) EnvisatFile.$(OBJ_EXT) + $(CC) $(CFLAGS) envisat_dump.$(OBJ_EXT) EnvisatFile.$(OBJ_EXT) $(GDAL_LIB) -ldl -lm -o envisat_dump + +dumpgeo: dumpgeo.$(OBJ_EXT) EnvisatFile.$(OBJ_EXT) + $(CC) $(CFLAGS) dumpgeo.$(OBJ_EXT) EnvisatFile.$(OBJ_EXT) $(GDAL_LIB) -ldl -lm -o dumpgeo + diff --git a/bazaar/plugin/gdal/frmts/envisat/adsrange.cpp b/bazaar/plugin/gdal/frmts/envisat/adsrange.cpp new file mode 100644 index 000000000..d5d48899e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/adsrange.cpp @@ -0,0 +1,153 @@ +/****************************************************************************** + * $Id: adsrange.cpp 27099 2014-03-27 00:49:30Z rouault $ + * + * Project: APP ENVISAT Support + * Purpose: Detect range of ADS records matching the MDS records. + * Author: Martin Paces, martin.paces@eox.at + * + ****************************************************************************** + * Copyright (c) 2013, EOX IT Services, GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "adsrange.hpp" +#include "timedelta.hpp" + +#include "cpl_string.h" + +CPL_C_START +#include "EnvisatFile.h" +#include "records.h" +CPL_C_END + +#include + +/* -------------------------------------------------------------------- */ +/* + * data-set descriptor (private helper class) + */ + +class DataSet +{ + public: + + EnvisatFile & envfile ; + int index ; + int nrec ; + + DataSet( EnvisatFile & envfile , int index ) : + envfile(envfile), index(index), nrec(0) + { + EnvisatFile_GetDatasetInfo( &envfile, index, NULL, NULL, NULL, + NULL , NULL, &nrec, NULL ) ; + } + + TimeDelta getMJD( int ridx ) + { + GUInt32 mjd[3] ; + + if ( ridx < 0 ) ridx += nrec ; + + EnvisatFile_ReadDatasetRecordChunk(&envfile,index,ridx,mjd,0,12) ; + + #define INT32(x) ((GInt32)CPL_MSBWORD32(x)) + + return TimeDelta( INT32(mjd[0]), INT32(mjd[1]), INT32(mjd[2]) ) ; + + #undef INT32 + } + +} ; + +/* -------------------------------------------------------------------- */ +/* + * constructor of the ADSRangeLastAfter object + * + */ + +ADSRangeLastAfter::ADSRangeLastAfter( EnvisatFile & envfile, + int ads_idx , int mds_idx, const TimeDelta & line_interval ) +{ + int idx ; + TimeDelta t_mds , t_ads , t_ads_prev ; + + /* abs.time tolerance */ + TimeDelta atol = line_interval * 0.5 ; + + /* MDS and ADS descriptor classes */ + DataSet mds( envfile, mds_idx ) ; + DataSet ads( envfile, ads_idx ) ; + + /* read the times of the first and last measurements */ + mjd_m_first = mds.getMJD(0) ; /* MDJ time of the first MDS record */ + mjd_m_last = mds.getMJD(-1) ; /* MDJ time of the last MDS record */ + + /* look-up the the first applicable ADSR */ + + idx = 0 ; + t_mds = mjd_m_first + atol ; /*time of the first MDSR + tolerance */ + t_ads = ads.getMJD(0) ; /*time of the first ADSR */ + t_ads_prev = t_ads ; /* holds previous ADSR */ + + if ( t_ads < t_mds ) + { + for( idx = 1 ; idx < ads.nrec ; ++idx ) + { + t_ads = ads.getMJD(idx) ; + + if ( t_ads >= t_mds ) break ; + + t_ads_prev = t_ads ; + } + } + + /* store the first applicable ASDR */ + idx_first = idx - 1 ; /* sets -1 if no match */ + mjd_first = t_ads_prev ; /* set time of the first rec. if no match */ + + /* look-up the the last applicable ADSR */ + + idx = ads.nrec-2 ; + t_mds = mjd_m_last - atol ; /* time of the last MDSR - tolerance */ + t_ads = ads.getMJD(-1) ; /* time of the first ADSR */ + t_ads_prev = t_ads ; /* holds previous ADSR */ + + if ( t_ads > t_mds ) + { + for( idx = ads.nrec-2 ; idx >= 0 ; --idx ) + { + t_ads = ads.getMJD(idx) ; + + if ( t_ads <= t_mds ) break ; + + t_ads_prev = t_ads ; + } + } + + /* store the last applicable ASDR */ + idx_last = idx + 1 ; /* sets ads.nrec if no match */ + mjd_last = t_ads_prev ; /* set time of the last rec. if no match */ + + /* valuate the line offsets */ + off_first = (int)floor( 0.5 + ( mjd_m_first - mjd_first ) / line_interval ) ; + off_last = (int)floor( 0.5 + ( mjd_last - mjd_m_last ) / line_interval ) ; + +} ; + diff --git a/bazaar/plugin/gdal/frmts/envisat/adsrange.hpp b/bazaar/plugin/gdal/frmts/envisat/adsrange.hpp new file mode 100644 index 000000000..de4ca0d9b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/adsrange.hpp @@ -0,0 +1,173 @@ +/****************************************************************************** + * $Id: adsrange.hpp 27098 2014-03-27 00:16:11Z rouault $ + * + * Project: APP ENVISAT Support + * Purpose: Detect range of ADS records matching the MDS records + * Author: Martin Paces, martin.paces@eox.at + * + ****************************************************************************** + * Copyright (c) 2013, EOX IT Services, GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#ifndef adsrange_hpp +#define adsrange_hpp + +#include "cpl_string.h" + +CPL_C_START +#include "EnvisatFile.h" +#include "records.h" +CPL_C_END + +#include "timedelta.hpp" + +/* -------------------------------------------------------------------- */ +/* + * class ADSRange + * + * Range of ADS record matching the range of the MDS records. + * + */ + +class ADSRange +{ + + protected: + + int idx_first ; /* index of the first matched ADSR */ + int idx_last ; /* index of the last matched ADSR */ + int off_first ; /* num. of lines from 1st matched ADSR to 1st MDSR */ + int off_last ; /* num. of lines from last MDSR to last matched ADSR*/ + + TimeDelta mjd_first ; /* MDJ time of the first matched ADS record */ + TimeDelta mjd_last ; /* MDJ time of the last matched ADS record */ + TimeDelta mjd_m_first ; /* MDJ time of the first MDS record */ + TimeDelta mjd_m_last ; /* MDJ time of the last MDS record */ + + public: + + /* CONSTRUCTOR */ + ADSRange() : + idx_first(0), idx_last(0), off_first(0), off_last(0), + mjd_first(0), mjd_last(0), mjd_m_first(0), mjd_m_last(0) + { + } + + ADSRange( const int idx_first, const int idx_last, + const int off_first, const int off_last, + const TimeDelta &mjd_first, const TimeDelta &mjd_last, + const TimeDelta &mjd_m_first, const TimeDelta &mjd_m_last ) : + idx_first(idx_first), idx_last(idx_last), off_first(off_first), + off_last(off_last), mjd_first(mjd_first), mjd_last(mjd_last), + mjd_m_first(mjd_m_first), mjd_m_last(mjd_m_last) + { + } + + /* get count of matched records */ + inline int getDSRCount( void ) const + { + return ( idx_last - idx_first + 1 ) ; + } + + /* GETTERS */ + + /* get index of the first matched ADS record */ + inline int getFirstIndex( void ) + { + return this->idx_first ; + } + + /* get index of the last matched ADS record */ + inline int getLastIndex( void ) + { + return this->idx_last ; + } + + /* get offset of the first matched ADS record */ + inline int getFirstOffset( void ) + { + return this->off_first ; + } + + /* get offset of the last matched ADS record */ + inline int getLastOffset( void ) + { + return this->off_last ; + } + + /* get MJD time of the first matched ADS record */ + inline TimeDelta getFirstTime( void ) + { + return this->mjd_first ; + } + + /* get MJD time of the last matched ADS record */ + inline TimeDelta getLastTime( void ) + { + return this->mjd_last ; + } + + /* get MJD time of the first MDS record */ + inline TimeDelta getMDSRFirstTime( void ) + { + return this->mjd_m_first ; + } + + /* get MJD time of the last MDS record */ + inline TimeDelta getMDSRLastTime( void ) + { + return this->mjd_m_last ; + } + +} ; + + +/* -------------------------------------------------------------------- */ +/* + * NOTE: There are two kinds of ADS records: + * + * 1) One ADS record appliable to all consequent MDS records until replaced + * by another ADS record, i.e., last MDS records does no need to be + * followed by an ADS record. + * + * 2) Two ADS records applicable to all MDS records between them + * (e.g., tiepoints ADS), i.e., last MDS record should be followed + * by an ADS rescord having the same or later time-stamp. + * + * The type of the ADS afects the way how the ADS records corresponding + * to a set of MDS records should be selected. + */ + + +class ADSRangeLastAfter: public ADSRange +{ + + public: + + /* CONSTRUCTOR */ + ADSRangeLastAfter( EnvisatFile & envfile, int ads_idx , int mds_idx, + const TimeDelta & line_interval ) ; + +} ; + + +#endif /*tiepointrange_hpp*/ + diff --git a/bazaar/plugin/gdal/frmts/envisat/dumpgeo.c b/bazaar/plugin/gdal/frmts/envisat/dumpgeo.c new file mode 100644 index 000000000..4c2cc0e88 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/dumpgeo.c @@ -0,0 +1,171 @@ +/****************************************************************************** + * $Id: dumpgeo.c 9484 2006-04-04 02:44:16Z fwarmerdam $ + * + * Project: APP ENVISAT Support + * Purpose: Test mainline for dumping ENVISAT format files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Atlantis Scientific, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "cpl_conv.h" +#include "EnvisatFile.h" + +int main( int argc, char ** argv ) + +{ + EnvisatFile *es_file; + int ds_index; + int ds_offset, ds_size, num_dsr, dsr_size, i_record; + + if( argc != 2 ) + { + printf( "Usage: dumpgeo filename\n" ); + exit( 1 ); + } + + + if( EnvisatFile_Open( &es_file, argv[1], "r" ) != 0 ) + { + printf( "EnvisatFile_Open(%s) failed.\n", argv[1] ); + exit( 2 ); + } + + ds_index = EnvisatFile_GetDatasetIndex( es_file, "GEOLOCATION GRID ADS" ); + if( ds_index == -1 ) + { + printf( "Can't find geolocation grid ads.\n" ); + exit( 3 ); + } + + EnvisatFile_GetDatasetInfo( es_file, + ds_index, NULL, NULL, NULL, + &ds_offset, &ds_size, + &num_dsr, &dsr_size ); + if( ds_offset == 0 ) + { + printf( "No data for geolocation grid ads.\n" ); + exit( 4 ); + } + + CPLAssert( dsr_size == 521 ); + + for( i_record = 0; i_record < num_dsr; i_record++ ) + { + GByte abyRecord[521]; + GUInt32 unValue; + float fValue; + int sample; + + EnvisatFile_ReadDatasetRecord( es_file, ds_index, i_record, + abyRecord ); + + printf( "<====================== Record %d ==================>\n", + i_record ); + + /* field 1 */ + CPL_SWAP32PTR( abyRecord + 0 ); + CPL_SWAP32PTR( abyRecord + 4 ); + CPL_SWAP32PTR( abyRecord + 8 ); + + printf( "start line: mjd_days = %d, sec = %d, msec = %d\n", + ((int *) abyRecord)[0], + ((unsigned int *) abyRecord)[1], + ((unsigned int *) abyRecord)[2] ); + + /* field 2 */ + printf( "Attachment flag = %d\n", abyRecord[12] ); + + /* field 3 */ + memcpy( &unValue, abyRecord + 13, 4 ); + printf( "range line (first in granule) = %d\n", + CPL_SWAP32( unValue ) ); + + /* field 4 */ + memcpy( &unValue, abyRecord + 17, 4 ); + printf( "lines in granule = %d\n", CPL_SWAP32( unValue ) ); + + /* field 5 */ + memcpy( &fValue, abyRecord + 21, 4 ); + CPL_SWAP32PTR( &fValue ); + printf( "track heading (first line) = %f\n", fValue ); + + /* field 6 */ + + printf( "first line of granule:\n" ); + for( sample = 0; sample < 11; sample++ ) + { + memcpy( &unValue, abyRecord + 25 + sample*4, 4 ); + printf( " sample=%d ", CPL_SWAP32(unValue) ); + + memcpy( &fValue, abyRecord + 25 + 44 + sample * 4, 4 ); + CPL_SWAP32PTR( &fValue ); + printf( "time=%g ", fValue ); + + memcpy( &fValue, abyRecord + 25 + 88 + sample * 4, 4 ); + CPL_SWAP32PTR( &fValue ); + printf( "angle=%g ", fValue ); + + memcpy( &unValue, abyRecord + 25 + 132 + sample*4, 4 ); + printf( "(%.9f,", ((int) CPL_SWAP32(unValue)) * 0.000001 ); + + memcpy( &unValue, abyRecord + 25 + 176 + sample*4, 4 ); + printf( "%.9f)\n", ((int) CPL_SWAP32(unValue)) * 0.000001 ); + } + + /* field 8 */ + CPL_SWAP32PTR( abyRecord + 267 ); + CPL_SWAP32PTR( abyRecord + 271 ); + CPL_SWAP32PTR( abyRecord + 275 ); + + printf( "end line: mjd_days = %d, sec = %d, msec = %d\n", + ((int *) (abyRecord + 267))[0], + ((unsigned int *) (abyRecord + 267))[1], + ((unsigned int *) (abyRecord + 267))[2] ); + + /* field 9 */ + printf( "final line of granule:\n" ); + for( sample = 0; sample < 11; sample++ ) + { + memcpy( &unValue, abyRecord + 279 + sample*4, 4 ); + printf( " sample=%d ", CPL_SWAP32(unValue) ); + + memcpy( &fValue, abyRecord + 279 + 44 + sample * 4, 4 ); + CPL_SWAP32PTR( &fValue ); + printf( "time=%g ", fValue ); + + memcpy( &fValue, abyRecord + 279 + 88 + sample * 4, 4 ); + CPL_SWAP32PTR( &fValue ); + printf( "angle=%g ", fValue ); + + memcpy( &unValue, abyRecord + 279 + 132 + sample*4, 4 ); + printf( "(%.9f,", ((int) CPL_SWAP32(unValue)) * 0.000001 ); + + memcpy( &unValue, abyRecord + 279 + 176 + sample*4, 4 ); + printf( "%.9f)\n", ((int) CPL_SWAP32(unValue)) * 0.000001 ); + } + } + + EnvisatFile_Close( es_file ); + + exit( 0 ); +} diff --git a/bazaar/plugin/gdal/frmts/envisat/envisat_dump.c b/bazaar/plugin/gdal/frmts/envisat/envisat_dump.c new file mode 100644 index 000000000..18bd5acea --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/envisat_dump.c @@ -0,0 +1,118 @@ +/****************************************************************************** + * $Id: envisat_dump.c 9484 2006-04-04 02:44:16Z fwarmerdam $ + * + * Project: APP ENVISAT Support + * Purpose: Test mainline for dumping ENVISAT format files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Atlantis Scientific, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "cpl_conv.h" +#include "EnvisatFile.h" + +int main( int argc, char ** argv ) + +{ + EnvisatFile *es_file; + int i; + const char *key; + + if( argc != 2 ) + { + printf( "Usage: envisatdump filename\n" ); + exit( 1 ); + } + + + if( EnvisatFile_Open( &es_file, argv[1], "r" ) != 0 ) + { + printf( "EnvisatFile_Open(%s) failed.\n", argv[1] ); + exit( 2 ); + } + + printf( "MPH\n" ); + printf( "===\n" ); + + for( i = 0; + (key = EnvisatFile_GetKeyByIndex( es_file, MPH, i )) != NULL; + i++ ) + { + const char *value = EnvisatFile_GetKeyValueAsString( es_file, + MPH, + key, + "" ); + + printf( "%s = [%s]\n", key, value ); + } + + printf( "\n" ); + printf( "SPH\n" ); + printf( "===\n" ); + + for( i = 0; + (key = EnvisatFile_GetKeyByIndex( es_file, SPH, i )) != NULL; + i++ ) + { + const char *value = EnvisatFile_GetKeyValueAsString( es_file, + SPH, + key, + "" ); + + printf( "%s = [%s]\n", key, value ); + } + + printf( "\n" ); + printf( "Datasets\n" ); + printf( "========\n" ); + + for( i = 0; TRUE; i++ ) + { + char *ds_name, *ds_type, *filename; + int ds_offset, ds_size, num_dsr, dsr_size; + + if( EnvisatFile_GetDatasetInfo( es_file, + i, + &ds_name, + &ds_type, + &filename, + &ds_offset, + &ds_size, + &num_dsr, + &dsr_size ) == 1 ) + break; + + printf( "\nDatset %d\n", i ); + + printf( "ds_name = %s\n", ds_name ); + printf( "ds_type = %s\n", ds_type ); + printf( "filename = %s\n", filename ); + printf( "ds_offset = %d\n", ds_offset ); + printf( "ds_size = %d\n", ds_size ); + printf( "num_dsr = %d\n", num_dsr ); + printf( "dsr_size = %d\n", dsr_size ); + } + + EnvisatFile_Close( es_file ); + + exit( 0 ); +} diff --git a/bazaar/plugin/gdal/frmts/envisat/envisatdataset.cpp b/bazaar/plugin/gdal/frmts/envisat/envisatdataset.cpp new file mode 100644 index 000000000..d9caec622 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/envisatdataset.cpp @@ -0,0 +1,1198 @@ +/****************************************************************************** + * $Id: envisatdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: APP ENVISAT Support + * Purpose: Reader for ENVISAT format image data. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Atlantis Scientific, Inc. + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "timedelta.hpp" +#include "adsrange.hpp" +#include "rawdataset.h" +#include "cpl_string.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: envisatdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +CPL_C_START +#include "EnvisatFile.h" +#include "records.h" +CPL_C_END + +CPL_C_START +void GDALRegister_Envisat(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* MerisL2FlagBand */ +/* ==================================================================== */ +/************************************************************************/ +class MerisL2FlagBand : public GDALPamRasterBand +{ + public: + MerisL2FlagBand( GDALDataset *, int, VSILFILE*, off_t, off_t ); + virtual ~MerisL2FlagBand(); + virtual CPLErr IReadBlock( int, int, void * ); + + private: + off_t nImgOffset; + off_t nPrefixBytes; + size_t nBytePerPixel; + size_t nRecordSize; + size_t nDataSize; + GByte *pReadBuf; + VSILFILE *fpImage; +}; + +/************************************************************************/ +/* MerisL2FlagBand() */ +/************************************************************************/ +MerisL2FlagBand::MerisL2FlagBand( GDALDataset *poDS, int nBand, + VSILFILE* fpImage, off_t nImgOffset, + off_t nPrefixBytes ) +{ + this->poDS = (GDALDataset *) poDS; + this->nBand = nBand; + + this->fpImage = fpImage; + this->nImgOffset = nImgOffset; + this->nPrefixBytes = nPrefixBytes; + + eDataType = GDT_UInt32; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + nBytePerPixel = 3; + + nDataSize = nBlockXSize * nBytePerPixel; + nRecordSize = nPrefixBytes + nDataSize; + + pReadBuf = (GByte *) CPLMalloc( nRecordSize ); +} + + +/************************************************************************/ +/* ~MerisL2FlagBand() */ +/************************************************************************/ +MerisL2FlagBand::~MerisL2FlagBand() +{ + CPLFree( pReadBuf ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ +CPLErr MerisL2FlagBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + CPLAssert( nBlockXOff == 0 ); + CPLAssert( pReadBuf != NULL ); + + off_t nOffset = nImgOffset + nPrefixBytes + + nBlockYOff * nBlockYSize * nRecordSize; + + if ( VSIFSeekL( fpImage, nOffset, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Seek to %d for scanline %d failed.\n", + (int)nOffset, nBlockYOff ); + return CE_Failure; + } + + if ( VSIFReadL( pReadBuf, 1, nDataSize, fpImage ) != nDataSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Read of %d bytes for scanline %d failed.\n", + (int)nDataSize, nBlockYOff ); + return CE_Failure; + } + + unsigned iImg, iBuf; + for( iImg = 0, iBuf = 0; + iImg < nBlockXSize * sizeof(GDT_UInt32); + iImg += sizeof(GDT_UInt32), iBuf += nBytePerPixel ) + { +#ifdef CPL_LSB + ((GByte*) pImage)[iImg] = pReadBuf[iBuf + 2]; + ((GByte*) pImage)[iImg + 1] = pReadBuf[iBuf + 1]; + ((GByte*) pImage)[iImg + 2] = pReadBuf[iBuf]; + ((GByte*) pImage)[iImg + 3] = 0; +#else + ((GByte*) pImage)[iImg] = 0; + ((GByte*) pImage)[iImg + 1] = pReadBuf[iBuf]; + ((GByte*) pImage)[iImg + 2] = pReadBuf[iBuf + 1]; + ((GByte*) pImage)[iImg + 3] = pReadBuf[iBuf + 2]; +#endif + } + + return CE_None; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* EnvisatDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class EnvisatDataset : public RawDataset +{ + EnvisatFile *hEnvisatFile; + VSILFILE *fpImage; + + int nGCPCount; + GDAL_GCP *pasGCPList; + + char **papszTempMD; + + void ScanForGCPs_ASAR(); + void ScanForGCPs_MERIS(); + + void UnwrapGCPs(); + + void CollectMetadata( EnvisatFile_HeaderFlag ); + void CollectDSDMetadata(); + void CollectADSMetadata(); + + public: + EnvisatDataset(); + ~EnvisatDataset(); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain ); + + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* EnvisatDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* EnvisatDataset() */ +/************************************************************************/ + +EnvisatDataset::EnvisatDataset() +{ + hEnvisatFile = NULL; + fpImage = NULL; + nGCPCount = 0; + pasGCPList = NULL; + papszTempMD = NULL; +} + +/************************************************************************/ +/* ~EnvisatDataset() */ +/************************************************************************/ + +EnvisatDataset::~EnvisatDataset() + +{ + FlushCache(); + + if( hEnvisatFile != NULL ) + EnvisatFile_Close( hEnvisatFile ); + + if( fpImage != NULL ) + VSIFCloseL( fpImage ); + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + CSLDestroy( papszTempMD ); +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int EnvisatDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *EnvisatDataset::GetGCPProjection() + +{ + if( nGCPCount > 0 ) + return SRS_WKT_WGS84; + else + return ""; +} + +/************************************************************************/ +/* GetGCP() */ +/************************************************************************/ + +const GDAL_GCP *EnvisatDataset::GetGCPs() + +{ + return pasGCPList; +} + +/************************************************************************/ +/* UnwrapGCPs() */ +/************************************************************************/ + +/* external C++ implementation of the in-place unwrapper */ +void EnvisatUnwrapGCPs( int nGCPCount, GDAL_GCP *pasGCPList ) ; + +void EnvisatDataset::UnwrapGCPs() +{ + EnvisatUnwrapGCPs( nGCPCount, pasGCPList ) ; +} + +/************************************************************************/ +/* ScanForGCPs_ASAR() */ +/************************************************************************/ + +void EnvisatDataset::ScanForGCPs_ASAR() + +{ + int nDatasetIndex, nNumDSR, nDSRSize, iRecord; + +/* -------------------------------------------------------------------- */ +/* Do we have a meaningful geolocation grid? */ +/* -------------------------------------------------------------------- */ + nDatasetIndex = EnvisatFile_GetDatasetIndex( hEnvisatFile, + "GEOLOCATION GRID ADS" ); + if( nDatasetIndex == -1 ) + return; + + if( EnvisatFile_GetDatasetInfo( hEnvisatFile, nDatasetIndex, + NULL, NULL, NULL, NULL, NULL, + &nNumDSR, &nDSRSize ) != SUCCESS ) + return; + + if( nNumDSR == 0 || nDSRSize != 521 ) + return; + +/* -------------------------------------------------------------------- */ +/* Collect the first GCP set from each record. */ +/* -------------------------------------------------------------------- */ + GByte abyRecord[521]; + int nRange=0, nSample, iGCP, nRangeOffset=0; + GUInt32 unValue; + + nGCPCount = 0; + pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),(nNumDSR+1) * 11); + + for( iRecord = 0; iRecord < nNumDSR; iRecord++ ) + { + if( EnvisatFile_ReadDatasetRecord( hEnvisatFile, nDatasetIndex, + iRecord, abyRecord ) != SUCCESS ) + continue; + + memcpy( &unValue, abyRecord + 13, 4 ); + nRange = CPL_MSBWORD32( unValue ) + nRangeOffset; + + if((iRecord>1) && (int(pasGCPList[nGCPCount-1].dfGCPLine+0.5) > nRange)) + { + int delta = (int) (pasGCPList[nGCPCount-1].dfGCPLine - + pasGCPList[nGCPCount-12].dfGCPLine); + nRange = int(pasGCPList[nGCPCount-1].dfGCPLine+0.5) + delta; + nRangeOffset = nRange-1; + } + + for( iGCP = 0; iGCP < 11; iGCP++ ) + { + char szId[128]; + + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + + CPLFree( pasGCPList[nGCPCount].pszId ); + + sprintf( szId, "%d", nGCPCount+1 ); + pasGCPList[nGCPCount].pszId = CPLStrdup( szId ); + + memcpy( &unValue, abyRecord + 25 + iGCP*4, 4 ); + nSample = CPL_MSBWORD32(unValue); + + memcpy( &unValue, abyRecord + 25 + 176 + iGCP*4, 4 ); + pasGCPList[nGCPCount].dfGCPX = ((int)CPL_MSBWORD32(unValue))*0.000001; + + memcpy( &unValue, abyRecord + 25 + 132 + iGCP*4, 4 ); + pasGCPList[nGCPCount].dfGCPY = ((int)CPL_MSBWORD32(unValue))*0.000001; + + pasGCPList[nGCPCount].dfGCPZ = 0.0; + + pasGCPList[nGCPCount].dfGCPLine = nRange - 0.5; + pasGCPList[nGCPCount].dfGCPPixel = nSample - 0.5; + + nGCPCount++; + } + } + +/* -------------------------------------------------------------------- */ +/* We also collect the bottom GCPs from the last granule. */ +/* -------------------------------------------------------------------- */ + memcpy( &unValue, abyRecord + 17, 4 ); + nRange = nRange + CPL_MSBWORD32( unValue ) - 1; + + for( iGCP = 0; iGCP < 11; iGCP++ ) + { + char szId[128]; + + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + + CPLFree( pasGCPList[nGCPCount].pszId ); + + sprintf( szId, "%d", nGCPCount+1 ); + pasGCPList[nGCPCount].pszId = CPLStrdup( szId ); + + memcpy( &unValue, abyRecord + 279 + iGCP*4, 4 ); + nSample = CPL_MSBWORD32(unValue); + + memcpy( &unValue, abyRecord + 279 + 176 + iGCP*4, 4 ); + pasGCPList[nGCPCount].dfGCPX = ((int)CPL_MSBWORD32(unValue))*0.000001; + + memcpy( &unValue, abyRecord + 279 + 132 + iGCP*4, 4 ); + pasGCPList[nGCPCount].dfGCPY = ((int)CPL_MSBWORD32(unValue))*0.000001; + + pasGCPList[nGCPCount].dfGCPZ = 0.0; + + pasGCPList[nGCPCount].dfGCPLine = nRange - 0.5; + pasGCPList[nGCPCount].dfGCPPixel = nSample - 0.5; + + nGCPCount++; + } +} + +/************************************************************************/ +/* ScanForGCPs_MERIS() */ +/************************************************************************/ + +void EnvisatDataset::ScanForGCPs_MERIS() + +{ + int nDatasetIndex, nNumDSR, nDSRSize; + bool isBrowseProduct ; + +/* -------------------------------------------------------------------- */ +/* Do we have a meaningful geolocation grid? Seach for a */ +/* DS_TYPE=A and a name containing "geolocation" or "tie */ +/* points". */ +/* -------------------------------------------------------------------- */ + nDatasetIndex = EnvisatFile_GetDatasetIndex( hEnvisatFile, + "Tie points ADS" ); + if( nDatasetIndex == -1 ) + return; + + if( EnvisatFile_GetDatasetInfo( hEnvisatFile, nDatasetIndex, + NULL, NULL, NULL, NULL, NULL, + &nNumDSR, &nDSRSize ) != SUCCESS ) + return; + + if( nNumDSR == 0 ) + return; + +/* -------------------------------------------------------------------- */ +/* Figure out the tiepoint space, and how many we have. */ +/* -------------------------------------------------------------------- */ + int nLinesPerTiePoint, nSamplesPerTiePoint; + int nTPPerLine, nTPPerColumn = nNumDSR; + + if( nNumDSR == 0 ) + return; + + nLinesPerTiePoint = + EnvisatFile_GetKeyValueAsInt( hEnvisatFile, SPH, + "LINES_PER_TIE_PT", 0 ); + nSamplesPerTiePoint = + EnvisatFile_GetKeyValueAsInt( hEnvisatFile, SPH, + "SAMPLES_PER_TIE_PT", 0 ); + + if( nLinesPerTiePoint == 0 || nSamplesPerTiePoint == 0 ) + return; + + nTPPerLine = (GetRasterXSize() + nSamplesPerTiePoint - 1) + / nSamplesPerTiePoint; + +/* -------------------------------------------------------------------- */ +/* Find a Mesurement type dataset to use as a reference raster */ +/* band. */ +/* -------------------------------------------------------------------- */ + + int nMDSIndex; + + for( nMDSIndex = 0; TRUE; nMDSIndex++ ) + { + char *pszDSType; + if( EnvisatFile_GetDatasetInfo( hEnvisatFile, nMDSIndex, + NULL, &pszDSType, NULL, NULL, NULL, NULL, NULL ) == FAILURE ) + { + CPLDebug("EnvisatDataset", + "Unable to find MDS in Envisat file.") ; + return ; + } + if( EQUAL(pszDSType,"M") ) break; + } + +/* -------------------------------------------------------------------- */ +/* Get subset of TP ADS records matching the MDS records */ +/* -------------------------------------------------------------------- */ + + /* get the MDS line sampling time interval */ + TimeDelta tdMDSSamplingInterval( 0 , 0 , + EnvisatFile_GetKeyValueAsInt( hEnvisatFile, SPH, + "LINE_TIME_INTERVAL", 0 ) ); + + /* get range of TiePoint ADS records matching the measurements */ + ADSRangeLastAfter arTP( *hEnvisatFile , nDatasetIndex, + nMDSIndex , tdMDSSamplingInterval ) ; + + /* check if there are any TPs to be used */ + if ( arTP.getDSRCount() <= 0 ) + { + CPLDebug( "EnvisatDataset" , "No tiepoint covering " + "the measurement records." ) ; + return; /* No TPs - no extraction. */ + } + + /* check if TPs cover the whole range of MDSRs */ + if(( arTP.getFirstOffset() < 0 )||( arTP.getLastOffset() < 0 )) + { + CPLDebug( "EnvisatDataset" , "The tiepoints do not cover " + "whole range of measurement records." ) ; + /* Not good but we can still extract some of the TPS, can we? */ + } + + /* check TP records spacing */ + if ((1+(arTP.getFirstOffset()+arTP.getLastOffset()+GetRasterYSize()-1) + / nLinesPerTiePoint ) != arTP.getDSRCount() ) + { + CPLDebug( "EnvisatDataset", "Not enough tieponts per column! " + "received=%d expected=%d", nTPPerColumn , + 1 + (arTP.getFirstOffset()+arTP.getLastOffset()+ + GetRasterYSize()-1) / nLinesPerTiePoint ) ; + return; /* That is far more serious - we risk missplaces TPs. */ + } + + if ( 50*nTPPerLine + 13 == nDSRSize ) /* regular product */ + { + isBrowseProduct = false ; + } + else if ( 8*nTPPerLine + 13 == nDSRSize ) /* browse product */ + { + /* although BPs are rare there is no reason not to support them */ + isBrowseProduct = true ; + } + else + { + CPLDebug( "EnvisatDataset", "Unexpectd size of 'Tie points ADS' !" + " received=%d expected=%d or %d" , nDSRSize , + 50*nTPPerLine+13, 8*nTPPerLine+13 ) ; + return; + } + +/* -------------------------------------------------------------------- */ +/* Collect the first GCP set from each record. */ +/* -------------------------------------------------------------------- */ + + GByte *pabyRecord = (GByte *) CPLMalloc(nDSRSize-13); + int iGCP; + + GUInt32 *tpLat = ((GUInt32*)pabyRecord) + nTPPerLine*0 ; /* latitude */ + GUInt32 *tpLon = ((GUInt32*)pabyRecord) + nTPPerLine*1 ; /* longitude */ + GUInt32 *tpLtc = ((GUInt32*)pabyRecord) + nTPPerLine*4 ; /* lat. DEM correction */ + GUInt32 *tpLnc = ((GUInt32*)pabyRecord) + nTPPerLine*5 ; /* lon. DEM correction */ + + nGCPCount = 0; + pasGCPList = (GDAL_GCP *) CPLCalloc( sizeof(GDAL_GCP), + arTP.getDSRCount() * nTPPerLine ); + + for( int ir = 0 ; ir < arTP.getDSRCount() ; ir++ ) + { + int iRecord = ir + arTP.getFirstIndex() ; + + double dfGCPLine = 0.5 + + ( iRecord*nLinesPerTiePoint - arTP.getFirstOffset() ) ; + + if( EnvisatFile_ReadDatasetRecordChunk( hEnvisatFile, nDatasetIndex, + iRecord , pabyRecord, 13 , -1 ) != SUCCESS ) + continue; + + for( iGCP = 0; iGCP < nTPPerLine; iGCP++ ) + { + char szId[128]; + + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + + CPLFree( pasGCPList[nGCPCount].pszId ); + + sprintf( szId, "%d", nGCPCount+1 ); + pasGCPList[nGCPCount].pszId = CPLStrdup( szId ); + + #define INT32(x) ((GInt32)CPL_MSBWORD32(x)) + + pasGCPList[nGCPCount].dfGCPX = 1e-6*INT32(tpLon[iGCP]) ; + pasGCPList[nGCPCount].dfGCPY = 1e-6*INT32(tpLat[iGCP]) ; + pasGCPList[nGCPCount].dfGCPZ = 0.0; + + if( !isBrowseProduct ) /* add DEM corrections */ + { + pasGCPList[nGCPCount].dfGCPX += 1e-6*INT32(tpLnc[iGCP]) ; + pasGCPList[nGCPCount].dfGCPY += 1e-6*INT32(tpLtc[iGCP]) ; + } + + #undef INT32 + + pasGCPList[nGCPCount].dfGCPLine = dfGCPLine ; + pasGCPList[nGCPCount].dfGCPPixel = iGCP*nSamplesPerTiePoint + 0.5; + + nGCPCount++; + } + } + CPLFree( pabyRecord ); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **EnvisatDataset::GetMetadataDomainList() +{ + return CSLAddString(GDALDataset::GetMetadataDomainList(), "envisat-ds-*-*"); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **EnvisatDataset::GetMetadata( const char * pszDomain ) + +{ + if( pszDomain == NULL || !EQUALN(pszDomain,"envisat-ds-",11) ) + return GDALDataset::GetMetadata( pszDomain ); + +/* -------------------------------------------------------------------- */ +/* Get the dataset name and record number. */ +/* -------------------------------------------------------------------- */ + char szDSName[128]; + int i, nRecord = -1; + + strncpy( szDSName, pszDomain+11, sizeof(szDSName) ); + szDSName[sizeof(szDSName)-1] = 0; + for( i = 0; i < (int) sizeof(szDSName)-1; i++ ) + { + if( szDSName[i] == '-' ) + { + szDSName[i] = '\0'; + nRecord = atoi(szDSName+1); + break; + } + } + + if( nRecord == -1 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get the dataset index and info. */ +/* -------------------------------------------------------------------- */ + int nDSIndex = EnvisatFile_GetDatasetIndex( hEnvisatFile, szDSName ); + int nDSRSize, nNumDSR; + + if( nDSIndex == -1 ) + return NULL; + + EnvisatFile_GetDatasetInfo( hEnvisatFile, nDSIndex, NULL, NULL, NULL, + NULL, NULL, &nNumDSR, &nDSRSize ); + + if( nDSRSize == -1 || nRecord < 0 || nRecord >= nNumDSR ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Read the requested record. */ +/* -------------------------------------------------------------------- */ + char *pszRecord; + + pszRecord = (char *) CPLMalloc(nDSRSize+1); + + if( EnvisatFile_ReadDatasetRecord( hEnvisatFile, nDSIndex, nRecord, + pszRecord ) == FAILURE ) + { + CPLFree( pszRecord ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Massage the data into a safe textual format. For now we */ +/* just turn zero bytes into spaces. */ +/* -------------------------------------------------------------------- */ + char *pszEscapedRecord; + + CSLDestroy( papszTempMD ); + + pszEscapedRecord = CPLEscapeString( pszRecord, nDSRSize, + CPLES_BackslashQuotable ); + papszTempMD = CSLSetNameValue( NULL, "EscapedRecord", pszEscapedRecord ); + CPLFree( pszEscapedRecord ); + + for( i = 0; i < nDSRSize; i++ ) + if( pszRecord[i] == '\0' ) + pszRecord[i] = ' '; + + papszTempMD = CSLSetNameValue( papszTempMD, "RawRecord", pszRecord ); + + CPLFree( pszRecord ); + + return papszTempMD; +} + +/************************************************************************/ +/* CollectDSDMetadata() */ +/* */ +/* Collect metadata based on any DSD entries with filenames */ +/* associated. */ +/************************************************************************/ + +void EnvisatDataset::CollectDSDMetadata() + +{ + char *pszDSName, *pszFilename; + int iDSD; + + for( iDSD = 0; + EnvisatFile_GetDatasetInfo( hEnvisatFile, iDSD, &pszDSName, NULL, + &pszFilename, NULL, NULL, NULL, NULL ) == SUCCESS; + iDSD++ ) + { + if( pszFilename == NULL + || strlen(pszFilename) == 0 + || EQUALN(pszFilename,"NOT USED",8) + || EQUALN(pszFilename," ",8)) + continue; + + char szKey[128], szTrimmedName[128]; + int i; + + strcpy( szKey, "DS_" ); + strcat( szKey, pszDSName ); + + // strip trailing spaces. + for( i = strlen(szKey)-1; i && szKey[i] == ' '; i-- ) + szKey[i] = '\0'; + + // convert spaces into underscores. + for( i = 0; szKey[i] != '\0'; i++ ) + { + if( szKey[i] == ' ' ) + szKey[i] = '_'; + } + + strcat( szKey, "_NAME" ); + + strcpy( szTrimmedName, pszFilename ); + for( i = strlen(szTrimmedName)-1; i && szTrimmedName[i] == ' '; i--) + szTrimmedName[i] = '\0'; + + SetMetadataItem( szKey, szTrimmedName ); + } +} + +/************************************************************************/ +/* CollectADSMetadata() */ +/* */ +/* Collect metadata from envisat ADS and GADS. */ +/************************************************************************/ + +void EnvisatDataset::CollectADSMetadata() +{ + int nDSIndex, nNumDsr, nDSRSize; + int nRecord; + const char *pszDSName, *pszDSType, *pszDSFilename; + const char *pszProduct; + char *pszRecord; + char szPrefix[128], szKey[128], szValue[1024]; + int i; + CPLErr ret; + const EnvisatRecordDescr *pRecordDescr = NULL; + const EnvisatFieldDescr *pField; + + pszProduct = EnvisatFile_GetKeyValueAsString( hEnvisatFile, MPH, + "PRODUCT", "" ); + + for( nDSIndex = 0; + EnvisatFile_GetDatasetInfo( hEnvisatFile, nDSIndex, + (char **) &pszDSName, + (char **) &pszDSType, + (char **) &pszDSFilename, + NULL, NULL, + &nNumDsr, &nDSRSize ) == SUCCESS; + ++nDSIndex ) + { + if( EQUALN(pszDSFilename,"NOT USED",8) || (nNumDsr <= 0) ) + continue; + if( !EQUAL(pszDSType,"A") && !EQUAL(pszDSType,"G") ) + continue; + + for ( nRecord = 0; nRecord < nNumDsr; ++nRecord ) + { + strncpy( szPrefix, pszDSName, sizeof(szPrefix) - 1); + szPrefix[sizeof(szPrefix) - 1] = '\0'; + + // strip trailing spaces + for( i = strlen(szPrefix)-1; i && szPrefix[i] == ' '; --i ) + szPrefix[i] = '\0'; + + // convert spaces into underscores + for( i = 0; szPrefix[i] != '\0'; i++ ) + { + if( szPrefix[i] == ' ' ) + szPrefix[i] = '_'; + } + + pszRecord = (char *) CPLMalloc(nDSRSize+1); + + if( EnvisatFile_ReadDatasetRecord( hEnvisatFile, nDSIndex, nRecord, + pszRecord ) == FAILURE ) + { + CPLFree( pszRecord ); + return; + } + + pRecordDescr = EnvisatFile_GetRecordDescriptor(pszProduct, pszDSName); + if (pRecordDescr) + { + pField = pRecordDescr->pFields; + while ( pField && pField->szName ) + { + ret = EnvisatFile_GetFieldAsString(pszRecord, nDSRSize, + pField, szValue); + if ( ret == CE_None ) + { + if (nNumDsr == 1) + sprintf(szKey, "%s_%s", szPrefix, pField->szName); + else + // sprintf(szKey, "%s_%02d_%s", szPrefix, nRecord, + sprintf(szKey, "%s_%d_%s", szPrefix, nRecord, + pField->szName); + SetMetadataItem(szKey, szValue, "RECORDS"); + } + // silently ignore conversion errors + + ++pField; + } + } + CPLFree( pszRecord ); + } + } +} + +/************************************************************************/ +/* CollectMetadata() */ +/* */ +/* Collect metadata from the SPH or MPH header fields. */ +/************************************************************************/ + +void EnvisatDataset::CollectMetadata( EnvisatFile_HeaderFlag eMPHOrSPH ) + +{ + int iKey; + + for( iKey = 0; TRUE; iKey++ ) + { + const char *pszValue, *pszKey; + char szHeaderKey[128]; + + pszKey = EnvisatFile_GetKeyByIndex(hEnvisatFile, eMPHOrSPH, iKey); + if( pszKey == NULL ) + break; + + pszValue = EnvisatFile_GetKeyValueAsString( hEnvisatFile, eMPHOrSPH, + pszKey, NULL ); + + if( pszValue == NULL ) + continue; + + // skip some uninteresting structural information. + if( EQUAL(pszKey,"TOT_SIZE") + || EQUAL(pszKey,"SPH_SIZE") + || EQUAL(pszKey,"NUM_DSD") + || EQUAL(pszKey,"DSD_SIZE") + || EQUAL(pszKey,"NUM_DATA_SETS") ) + continue; + + if( eMPHOrSPH == MPH ) + sprintf( szHeaderKey, "MPH_%s", pszKey ); + else + sprintf( szHeaderKey, "SPH_%s", pszKey ); + + SetMetadataItem( szHeaderKey, pszValue ); + } +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *EnvisatDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + EnvisatFile *hEnvisatFile; + +/* -------------------------------------------------------------------- */ +/* Check the header. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 8 ) + return NULL; + + if( !EQUALN((const char *) poOpenInfo->pabyHeader, "PRODUCT=",8) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ + int ds_index; + + if( EnvisatFile_Open( &hEnvisatFile, poOpenInfo->pszFilename, "r" ) + == FAILURE ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Find a Mesurement type dataset to use as our reference */ +/* raster band. */ +/* -------------------------------------------------------------------- */ + int dsr_size, num_dsr, ds_offset, bNative; + char *pszDSType; + + for( ds_index = 0; TRUE; ds_index++ ) + { + if( EnvisatFile_GetDatasetInfo( hEnvisatFile, ds_index, + NULL, &pszDSType, NULL, + &ds_offset, NULL, + &num_dsr, &dsr_size ) == FAILURE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find \"MDS1\" measurement datatset in Envisat file." ); + EnvisatFile_Close( hEnvisatFile ); + return NULL; + } + + /* Have we found what we are looking for? A Measurement ds. */ + if( EQUAL(pszDSType,"M") ) + break; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + EnvisatFile_Close( hEnvisatFile ); + CPLError( CE_Failure, CPLE_NotSupported, + "The ENVISAT driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + EnvisatDataset *poDS; + + poDS = new EnvisatDataset(); + + poDS->hEnvisatFile = hEnvisatFile; + +/* -------------------------------------------------------------------- */ +/* Setup image definition. */ +/* -------------------------------------------------------------------- */ + const char *pszDataType, *pszSampleType, *pszProduct; + GDALDataType eDataType; + int nPrefixBytes; + + EnvisatFile_GetDatasetInfo( hEnvisatFile, ds_index, + NULL, NULL, NULL, &ds_offset, NULL, + &num_dsr, &dsr_size ); + + poDS->nRasterXSize = EnvisatFile_GetKeyValueAsInt( hEnvisatFile, SPH, + "LINE_LENGTH", 0 ); + poDS->nRasterYSize = num_dsr; + poDS->eAccess = GA_ReadOnly; + + pszProduct = EnvisatFile_GetKeyValueAsString( hEnvisatFile, MPH, + "PRODUCT", "" ); + pszDataType = EnvisatFile_GetKeyValueAsString( hEnvisatFile, SPH, + "DATA_TYPE", "" ); + pszSampleType = EnvisatFile_GetKeyValueAsString( hEnvisatFile, SPH, + "SAMPLE_TYPE", "" ); + if( EQUAL(pszDataType,"FLT32") && EQUALN(pszSampleType,"COMPLEX",7)) + eDataType = GDT_CFloat32; + else if( EQUAL(pszDataType,"FLT32") ) + eDataType = GDT_Float32; + else if( EQUAL(pszDataType,"UWORD") ) + eDataType = GDT_UInt16; + else if( EQUAL(pszDataType,"SWORD") && EQUALN(pszSampleType,"COMPLEX",7) ) + eDataType = GDT_CInt16; + else if( EQUAL(pszDataType,"SWORD") ) + eDataType = GDT_Int16; + else if( EQUALN(pszProduct,"ATS_TOA_1",8) ) + { + /* all 16bit data, no line length provided */ + eDataType = GDT_Int16; + poDS->nRasterXSize = (dsr_size - 20) / 2; + } + else if( poDS->nRasterXSize == 0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Envisat product format not recognised. Assuming 8bit\n" + "with no per-record prefix data. Results may be useless!" ); + eDataType = GDT_Byte; + poDS->nRasterXSize = dsr_size; + } + else + { + if( dsr_size >= 2 * poDS->nRasterXSize ) + eDataType = GDT_UInt16; + else + eDataType = GDT_Byte; + } + +#ifdef CPL_LSB + bNative = FALSE; +#else + bNative = TRUE; +#endif + + nPrefixBytes = dsr_size - + ((GDALGetDataTypeSize(eDataType) / 8) * poDS->nRasterXSize); + +/* -------------------------------------------------------------------- */ +/* Fail out if we didn't get non-zero sizes. */ +/* -------------------------------------------------------------------- */ + if( poDS->nRasterXSize < 1 || poDS->nRasterYSize < 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to determine organization of dataset. It would\n" + "appear this is an Envisat dataset, but an unsupported\n" + "data product. Unable to utilize." ); + delete poDS; + return NULL; + } + + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + if( poDS->fpImage == NULL ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to collect GCPs. */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +/* Scan for all datasets matching the reference dataset. */ +/* -------------------------------------------------------------------- */ + int num_dsr2, dsr_size2, iBand = 0; + const char *pszDSName; + char szBandName[128]; + bool bMiltiChannel; + + for( ds_index = 0; + EnvisatFile_GetDatasetInfo( hEnvisatFile, ds_index, + (char **) &pszDSName, NULL, NULL, + &ds_offset, NULL, + &num_dsr2, &dsr_size2 ) == SUCCESS; + ds_index++ ) + { + if( !EQUAL(pszDSType,"M") || num_dsr2 != num_dsr ) + continue; + + if( EQUALN(pszProduct,"MER",3) && (pszProduct[8] == '2') && + ( (strstr(pszDSName, "MDS(16)") != NULL) || + (strstr(pszDSName, "MDS(19)") != NULL)) ) + bMiltiChannel = true; + else + bMiltiChannel = false; + + if( (dsr_size2 == dsr_size) && !bMiltiChannel ) + { + poDS->SetBand( iBand+1, + new RawRasterBand( poDS, iBand+1, poDS->fpImage, + ds_offset + nPrefixBytes, + GDALGetDataTypeSize(eDataType) / 8, + dsr_size, + eDataType, bNative, TRUE ) ); + iBand++; + + poDS->GetRasterBand(iBand)->SetDescription( pszDSName ); + } +/* -------------------------------------------------------------------- */ +/* Handle MERIS Level 2 datasets with data type different from */ +/* the one declared in the SPH */ +/* -------------------------------------------------------------------- */ + else if( EQUALN(pszProduct,"MER",3) && + (strstr(pszDSName, "Flags") != NULL) ) + { + if (pszProduct[8] == '1') + { + // Flags + poDS->SetBand( iBand+1, + new RawRasterBand( poDS, iBand+1, poDS->fpImage, + ds_offset + nPrefixBytes, 3, + dsr_size, GDT_Byte, bNative, TRUE ) ); + iBand++; + + poDS->GetRasterBand(iBand)->SetDescription( pszDSName ); + + // Detector indices + poDS->SetBand( iBand+1, + new RawRasterBand( poDS, iBand+1, poDS->fpImage, + ds_offset + nPrefixBytes + 1, + 3, dsr_size, GDT_Int16, + bNative, TRUE ) ); + iBand++; + + const char *pszSuffix = strstr( pszDSName, "MDS" ); + if ( pszSuffix != NULL) + sprintf( szBandName, "Detector index %s", pszSuffix ); + else + sprintf( szBandName, "Detector index" ); + poDS->GetRasterBand(iBand)->SetDescription( szBandName ); + } + else if ( (pszProduct[8] == '2') && + (dsr_size2 >= 3 * poDS->nRasterXSize ) ) + { + int nFlagPrefixBytes = dsr_size2 - 3 * poDS->nRasterXSize; + + poDS->SetBand( iBand+1, + new MerisL2FlagBand( poDS, iBand+1, poDS->fpImage, + ds_offset, nFlagPrefixBytes ) ); + iBand++; + + poDS->GetRasterBand(iBand)->SetDescription( pszDSName ); + } + } + else if( EQUALN(pszProduct,"MER",3) && (pszProduct[8] == '2') ) + { + int nPrefixBytes2, nSubBands, nSubBandIdx, nSubBandOffset; + + int nPixelSize = 1; + GDALDataType eDataType2 = GDT_Byte; + + nSubBands = dsr_size2 / poDS->nRasterXSize; + if( (nSubBands < 1) || (nSubBands > 3) ) + nSubBands = 0; + + nPrefixBytes2 = dsr_size2 - + (nSubBands * nPixelSize * poDS->nRasterXSize); + + for (nSubBandIdx = 0; nSubBandIdx < nSubBands; ++nSubBandIdx) + { + nSubBandOffset = + ds_offset + nPrefixBytes2 + nSubBandIdx * nPixelSize; + poDS->SetBand( iBand+1, + new RawRasterBand( poDS, iBand+1, poDS->fpImage, + nSubBandOffset, + nPixelSize * nSubBands, + dsr_size2, eDataType2, bNative, TRUE ) ); + iBand++; + + if (nSubBands > 1) + { + sprintf( szBandName, "%s (%d)", pszDSName, nSubBandIdx ); + poDS->GetRasterBand(iBand)->SetDescription( szBandName ); + } + else + poDS->GetRasterBand(iBand)->SetDescription( pszDSName ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Collect metadata. */ +/* -------------------------------------------------------------------- */ + poDS->CollectMetadata( MPH ); + poDS->CollectMetadata( SPH ); + poDS->CollectDSDMetadata(); + poDS->CollectADSMetadata(); + + if( EQUALN(pszProduct,"MER",3) ) + poDS->ScanForGCPs_MERIS(); + else + poDS->ScanForGCPs_ASAR(); + + /* unwrap GCPs for products crossing date border */ + poDS->UnwrapGCPs(); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_Envisat() */ +/************************************************************************/ + +void GDALRegister_Envisat() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ESAT" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ESAT" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Envisat Image Format" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#Envisat" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "n1" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = EnvisatDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/envisat/makefile.vc b/bazaar/plugin/gdal/frmts/envisat/makefile.vc new file mode 100644 index 000000000..e3ee2e874 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = envisatdataset.obj EnvisatFile.obj records.obj adsrange.obj unwrapgcps.obj + +GDAL_ROOT = ..\.. + +EXTRAFLAGS = -I..\raw + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/envisat/records.c b/bazaar/plugin/gdal/frmts/envisat/records.c new file mode 100644 index 000000000..f8a9263b8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/records.c @@ -0,0 +1,1414 @@ +/****************************************************************************** + * $Id: records.c 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: APP ENVISAT Support + * Purpose: Low Level Envisat file access (read/write) API. + * Author: Antonio Valentino + * + ****************************************************************************** + * Copyright (c) 2011, Antonio Valentino + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_string.h" +#include "records.h" + +CPL_CVSID("$Id: records.c 27942 2014-11-11 00:57:41Z rouault $"); + +/* --- ASAR record descriptors --------------------------------------------- */ +static const EnvisatFieldDescr ASAR_ANTENNA_ELEV_PATT_ADSR[] = { + {"ZERO_DOPPLER_TIME", 0, EDT_MJD, 1}, + {"ATTACH_FLAG", 12, EDT_UByte, 1}, + {"BEAM_ID", 13, EDT_Char, 3}, + {"ELEVATION_PATTERN.SLANT_RANGE_TIME", 16, EDT_Float32, 11}, + {"ELEVATION_PATTERN.ELEVATION_ANGLES", 60, EDT_Float32, 11}, + {"ELEVATION_PATTERN.ANTENNA_PATTERN", 104, EDT_Float32, 11}, + /*{"SPARE_1", 148, EDT_UByte, 14},*/ + {NULL, 0, EDT_Unknown, 0} +}; + +static const EnvisatFieldDescr ASAR_CHIRP_PARAMS_ADSR[] = { + {"ZERO_DOPPLER_TIME", 0, EDT_MJD, 1}, + {"ATTACH_FLAG", 12, EDT_UByte, 1}, + {"BEAM_ID", 13, EDT_Char, 3}, + {"POLAR", 16, EDT_Char, 3}, + {"CHIRP_WIDTH", 19, EDT_Float32, 1}, + {"CHIRP_SIDELOBE", 23, EDT_Float32, 1}, + {"CHIRP_ISLR", 27, EDT_Float32, 1}, + {"CHIRP_PEAK_LOC", 31, EDT_Float32, 1}, + {"CHIRP_POWER", 35, EDT_Float32, 1}, + {"ELEV_CORR_FACTOR", 39, EDT_Float32, 1}, + /*{"SPARE_1", 43, EDT_UByte, 16},*/ + {"CAL_PULSE_INFO.1.MAX_CAL", 59, EDT_Float32, 3}, + {"CAL_PULSE_INFO.1.AVG_CAL", 71, EDT_Float32, 3}, + {"CAL_PULSE_INFO.1.AVG_VAL_1A", 83, EDT_Float32, 1}, + {"CAL_PULSE_INFO.1.PHS_CAL", 87, EDT_Float32, 4}, + {"CAL_PULSE_INFO.2.MAX_CAL", 103, EDT_Float32, 3}, + {"CAL_PULSE_INFO.2.AVG_CAL", 115, EDT_Float32, 3}, + {"CAL_PULSE_INFO.2.AVG_VAL_1A", 127, EDT_Float32, 1}, + {"CAL_PULSE_INFO.2.PHS_CAL", 131, EDT_Float32, 4}, + {"CAL_PULSE_INFO.3.MAX_CAL", 147, EDT_Float32, 3}, + {"CAL_PULSE_INFO.3.AVG_CAL", 159, EDT_Float32, 3}, + {"CAL_PULSE_INFO.3.AVG_VAL_1A", 171, EDT_Float32, 1}, + {"CAL_PULSE_INFO.3.PHS_CAL", 175, EDT_Float32, 4}, + {"CAL_PULSE_INFO.4.MAX_CAL", 191, EDT_Float32, 3}, + {"CAL_PULSE_INFO.4.AVG_CAL", 203, EDT_Float32, 3}, + {"CAL_PULSE_INFO.4.AVG_VAL_1A", 215, EDT_Float32, 1}, + {"CAL_PULSE_INFO.4.PHS_CAL", 219, EDT_Float32, 4}, + {"CAL_PULSE_INFO.5.MAX_CAL", 235, EDT_Float32, 3}, + {"CAL_PULSE_INFO.5.AVG_CAL", 247, EDT_Float32, 3}, + {"CAL_PULSE_INFO.5.AVG_VAL_1A", 259, EDT_Float32, 1}, + {"CAL_PULSE_INFO.5.PHS_CAL", 263, EDT_Float32, 4}, + {"CAL_PULSE_INFO.6.MAX_CAL", 279, EDT_Float32, 3}, + {"CAL_PULSE_INFO.6.AVG_CAL", 291, EDT_Float32, 3}, + {"CAL_PULSE_INFO.6.AVG_VAL_1A", 303, EDT_Float32, 1}, + {"CAL_PULSE_INFO.6.PHS_CAL", 307, EDT_Float32, 4}, + {"CAL_PULSE_INFO.7.MAX_CAL", 323, EDT_Float32, 3}, + {"CAL_PULSE_INFO.7.AVG_CAL", 335, EDT_Float32, 3}, + {"CAL_PULSE_INFO.7.AVG_VAL_1A", 347, EDT_Float32, 1}, + {"CAL_PULSE_INFO.7.PHS_CAL", 351, EDT_Float32, 4}, + {"CAL_PULSE_INFO.8.MAX_CAL", 367, EDT_Float32, 3}, + {"CAL_PULSE_INFO.8.AVG_CAL", 379, EDT_Float32, 3}, + {"CAL_PULSE_INFO.8.AVG_VAL_1A", 391, EDT_Float32, 1}, + {"CAL_PULSE_INFO.8.PHS_CAL", 395, EDT_Float32, 4}, + {"CAL_PULSE_INFO.9.MAX_CAL", 411, EDT_Float32, 3}, + {"CAL_PULSE_INFO.9.AVG_CAL", 423, EDT_Float32, 3}, + {"CAL_PULSE_INFO.9.AVG_VAL_1A", 435, EDT_Float32, 1}, + {"CAL_PULSE_INFO.9.PHS_CAL", 439, EDT_Float32, 4}, + {"CAL_PULSE_INFO.10.MAX_CAL", 455, EDT_Float32, 3}, + {"CAL_PULSE_INFO.10.AVG_CAL", 467, EDT_Float32, 3}, + {"CAL_PULSE_INFO.10.AVG_VAL_1A", 479, EDT_Float32, 1}, + {"CAL_PULSE_INFO.10.PHS_CAL", 483, EDT_Float32, 4}, + {"CAL_PULSE_INFO.11.MAX_CAL", 499, EDT_Float32, 3}, + {"CAL_PULSE_INFO.11.AVG_CAL", 511, EDT_Float32, 3}, + {"CAL_PULSE_INFO.11.AVG_VAL_1A", 523, EDT_Float32, 1}, + {"CAL_PULSE_INFO.11.PHS_CAL", 527, EDT_Float32, 4}, + {"CAL_PULSE_INFO.12.MAX_CAL", 543, EDT_Float32, 3}, + {"CAL_PULSE_INFO.12.AVG_CAL", 555, EDT_Float32, 3}, + {"CAL_PULSE_INFO.12.AVG_VAL_1A", 567, EDT_Float32, 1}, + {"CAL_PULSE_INFO.12.PHS_CAL", 571, EDT_Float32, 4}, + {"CAL_PULSE_INFO.13.MAX_CAL", 587, EDT_Float32, 3}, + {"CAL_PULSE_INFO.13.AVG_CAL", 599, EDT_Float32, 3}, + {"CAL_PULSE_INFO.13.AVG_VAL_1A", 611, EDT_Float32, 1}, + {"CAL_PULSE_INFO.13.PHS_CAL", 615, EDT_Float32, 4}, + {"CAL_PULSE_INFO.14.MAX_CAL", 631, EDT_Float32, 3}, + {"CAL_PULSE_INFO.14.AVG_CAL", 643, EDT_Float32, 3}, + {"CAL_PULSE_INFO.14.AVG_VAL_1A", 655, EDT_Float32, 1}, + {"CAL_PULSE_INFO.14.PHS_CAL", 659, EDT_Float32, 4}, + {"CAL_PULSE_INFO.15.MAX_CAL", 675, EDT_Float32, 3}, + {"CAL_PULSE_INFO.15.AVG_CAL", 687, EDT_Float32, 3}, + {"CAL_PULSE_INFO.15.AVG_VAL_1A", 699, EDT_Float32, 1}, + {"CAL_PULSE_INFO.15.PHS_CAL", 703, EDT_Float32, 4}, + {"CAL_PULSE_INFO.16.MAX_CAL", 719, EDT_Float32, 3}, + {"CAL_PULSE_INFO.16.AVG_CAL", 731, EDT_Float32, 3}, + {"CAL_PULSE_INFO.16.AVG_VAL_1A", 743, EDT_Float32, 1}, + {"CAL_PULSE_INFO.16.PHS_CAL", 747, EDT_Float32, 4}, + {"CAL_PULSE_INFO.17.MAX_CAL", 763, EDT_Float32, 3}, + {"CAL_PULSE_INFO.17.AVG_CAL", 775, EDT_Float32, 3}, + {"CAL_PULSE_INFO.17.AVG_VAL_1A", 787, EDT_Float32, 1}, + {"CAL_PULSE_INFO.17.PHS_CAL", 791, EDT_Float32, 4}, + {"CAL_PULSE_INFO.18.MAX_CAL", 807, EDT_Float32, 3}, + {"CAL_PULSE_INFO.18.AVG_CAL", 819, EDT_Float32, 3}, + {"CAL_PULSE_INFO.18.AVG_VAL_1A", 831, EDT_Float32, 1}, + {"CAL_PULSE_INFO.18.PHS_CAL", 835, EDT_Float32, 4}, + {"CAL_PULSE_INFO.19.MAX_CAL", 851, EDT_Float32, 3}, + {"CAL_PULSE_INFO.19.AVG_CAL", 863, EDT_Float32, 3}, + {"CAL_PULSE_INFO.19.AVG_VAL_1A", 875, EDT_Float32, 1}, + {"CAL_PULSE_INFO.19.PHS_CAL", 879, EDT_Float32, 4}, + {"CAL_PULSE_INFO.20.MAX_CAL", 895, EDT_Float32, 3}, + {"CAL_PULSE_INFO.20.AVG_CAL", 907, EDT_Float32, 3}, + {"CAL_PULSE_INFO.20.AVG_VAL_1A", 919, EDT_Float32, 1}, + {"CAL_PULSE_INFO.20.PHS_CAL", 923, EDT_Float32, 4}, + {"CAL_PULSE_INFO.21.MAX_CAL", 939, EDT_Float32, 3}, + {"CAL_PULSE_INFO.21.AVG_CAL", 951, EDT_Float32, 3}, + {"CAL_PULSE_INFO.21.AVG_VAL_1A", 963, EDT_Float32, 1}, + {"CAL_PULSE_INFO.21.PHS_CAL", 967, EDT_Float32, 4}, + {"CAL_PULSE_INFO.22.MAX_CAL", 983, EDT_Float32, 3}, + {"CAL_PULSE_INFO.22.AVG_CAL", 995, EDT_Float32, 3}, + {"CAL_PULSE_INFO.22.AVG_VAL_1A", 1007, EDT_Float32, 1}, + {"CAL_PULSE_INFO.22.PHS_CAL", 1011, EDT_Float32, 4}, + {"CAL_PULSE_INFO.23.MAX_CAL", 1027, EDT_Float32, 3}, + {"CAL_PULSE_INFO.23.AVG_CAL", 1039, EDT_Float32, 3}, + {"CAL_PULSE_INFO.23.AVG_VAL_1A", 1051, EDT_Float32, 1}, + {"CAL_PULSE_INFO.23.PHS_CAL", 1055, EDT_Float32, 4}, + {"CAL_PULSE_INFO.24.MAX_CAL", 1071, EDT_Float32, 3}, + {"CAL_PULSE_INFO.24.AVG_CAL", 1083, EDT_Float32, 3}, + {"CAL_PULSE_INFO.24.AVG_VAL_1A", 1095, EDT_Float32, 1}, + {"CAL_PULSE_INFO.24.PHS_CAL", 1099, EDT_Float32, 4}, + {"CAL_PULSE_INFO.25.MAX_CAL", 1115, EDT_Float32, 3}, + {"CAL_PULSE_INFO.25.AVG_CAL", 1127, EDT_Float32, 3}, + {"CAL_PULSE_INFO.25.AVG_VAL_1A", 1139, EDT_Float32, 1}, + {"CAL_PULSE_INFO.25.PHS_CAL", 1143, EDT_Float32, 4}, + {"CAL_PULSE_INFO.26.MAX_CAL", 1159, EDT_Float32, 3}, + {"CAL_PULSE_INFO.26.AVG_CAL", 1171, EDT_Float32, 3}, + {"CAL_PULSE_INFO.26.AVG_VAL_1A", 1183, EDT_Float32, 1}, + {"CAL_PULSE_INFO.26.PHS_CAL", 1187, EDT_Float32, 4}, + {"CAL_PULSE_INFO.27.MAX_CAL", 1203, EDT_Float32, 3}, + {"CAL_PULSE_INFO.27.AVG_CAL", 1215, EDT_Float32, 3}, + {"CAL_PULSE_INFO.27.AVG_VAL_1A", 1227, EDT_Float32, 1}, + {"CAL_PULSE_INFO.27.PHS_CAL", 1231, EDT_Float32, 4}, + {"CAL_PULSE_INFO.28.MAX_CAL", 1247, EDT_Float32, 3}, + {"CAL_PULSE_INFO.28.AVG_CAL", 1259, EDT_Float32, 3}, + {"CAL_PULSE_INFO.28.AVG_VAL_1A", 1271, EDT_Float32, 1}, + {"CAL_PULSE_INFO.28.PHS_CAL", 1275, EDT_Float32, 4}, + {"CAL_PULSE_INFO.29.MAX_CAL", 1291, EDT_Float32, 3}, + {"CAL_PULSE_INFO.29.AVG_CAL", 1303, EDT_Float32, 3}, + {"CAL_PULSE_INFO.29.AVG_VAL_1A", 1315, EDT_Float32, 1}, + {"CAL_PULSE_INFO.29.PHS_CAL", 1319, EDT_Float32, 4}, + {"CAL_PULSE_INFO.30.MAX_CAL", 1335, EDT_Float32, 3}, + {"CAL_PULSE_INFO.30.AVG_CAL", 1347, EDT_Float32, 3}, + {"CAL_PULSE_INFO.30.AVG_VAL_1A", 1359, EDT_Float32, 1}, + {"CAL_PULSE_INFO.30.PHS_CAL", 1363, EDT_Float32, 4}, + {"CAL_PULSE_INFO.31.MAX_CAL", 1379, EDT_Float32, 3}, + {"CAL_PULSE_INFO.31.AVG_CAL", 1391, EDT_Float32, 3}, + {"CAL_PULSE_INFO.31.AVG_VAL_1A", 1403, EDT_Float32, 1}, + {"CAL_PULSE_INFO.31.PHS_CAL", 1407, EDT_Float32, 4}, + {"CAL_PULSE_INFO.32.MAX_CAL", 1423, EDT_Float32, 3}, + {"CAL_PULSE_INFO.32.AVG_CAL", 1435, EDT_Float32, 3}, + {"CAL_PULSE_INFO.32.AVG_VAL_1A", 1447, EDT_Float32, 1}, + {"CAL_PULSE_INFO.32.PHS_CAL", 1451, EDT_Float32, 4}, + /*{"SPARE_2", 1467, EDT_UByte, 16},*/ + {NULL, 0, EDT_Unknown, 0} +}; + +static const EnvisatFieldDescr ASAR_DOP_CENTROID_COEFFS_ADSR[] = { + {"ZERO_DOPPLER_TIME", 0, EDT_MJD, 1}, + {"ATTACH_FLAG", 12, EDT_UByte, 1}, + {"SLANT_RANGE_TIME", 13, EDT_Float32, 1}, + {"DOP_COEF", 17, EDT_Float32, 5}, + {"DOP_CONF", 37, EDT_Float32, 1}, + {"DOP_CONF_BELOW_THRESH_FLAG", 41, EDT_UByte, 1}, + {"DELTA_DOPP_COEFF", 42, EDT_Int16, 5}, + /*{"SPARE_1", 52, EDT_UByte, 3},*/ + {NULL, 0, EDT_Unknown, 0} +}; + +#if 0 /* Unused */ +static const EnvisatFieldDescr ASAR_GEOLOCATION_GRID_ADSR[] = { + {"FIRST_ZERO_DOPPLER_TIME", 0, EDT_MJD, 1}, + {"ATTACH_FLAG", 12, EDT_UByte, 1}, + {"LINE_NUM", 13, EDT_UInt32, 1}, + {"NUM_LINES", 17, EDT_UInt32, 1}, + {"SUB_SAT_TRACK", 21, EDT_Float32, 1}, + {"FIRST_LINE_TIE_POINTS.SAMP_NUMBERS", 25, EDT_UInt32, 11}, + {"FIRST_LINE_TIE_POINTS.SLANT_RANGE_TIMES", 69, EDT_Float32, 11}, + {"FIRST_LINE_TIE_POINTS.ANGLES", 113, EDT_Float32, 11}, + {"FIRST_LINE_TIE_POINTS.LATS", 157, EDT_Int32, 11}, + {"FIRST_LINE_TIE_POINTS.LONGS", 201, EDT_Int32, 11}, + /*{"SPARE_1", 245, EDT_UByte, 22},*/ + {"LAST_ZERO_DOPPLER_TIME", 267, EDT_MJD, 1}, + {"LAST_LINE_TIE_POINTS.SAMP_NUMBERS", 279, EDT_UInt32, 11}, + {"LAST_LINE_TIE_POINTS.SLANT_RANGE_TIMES", 323, EDT_Float32, 11}, + {"LAST_LINE_TIE_POINTS.ANGLES", 367, EDT_Float32, 11}, + {"LAST_LINE_TIE_POINTS.LATS", 411, EDT_Int32, 11}, + {"LAST_LINE_TIE_POINTS.LONGS", 455, EDT_Int32, 11}, + /*{"SPARE_2", 499, EDT_UByte, 22},*/ + {NULL, 0, EDT_Unknown, 0} +}; +#endif /* Unused */ + +static const EnvisatFieldDescr ASAR_MAIN_PROCESSING_PARAMS_ADSR[] = { + {"FIRST_ZERO_DOPPLER_TIME", 0, EDT_MJD, 1}, + {"ATTACH_FLAG", 12, EDT_UByte, 1}, + {"LAST_ZERO_DOPPLER_TIME", 13, EDT_MJD, 1}, + {"WORK_ORDER_ID", 25, EDT_Char, 12}, + {"TIME_DIFF", 37, EDT_Float32, 1}, + {"SWATH_ID", 41, EDT_Char, 3}, + {"RANGE_SPACING", 44, EDT_Float32, 1}, + {"AZIMUTH_SPACING", 48, EDT_Float32, 1}, + {"LINE_TIME_INTERVAL", 52, EDT_Float32, 1}, + {"NUM_OUTPUT_LINES", 56, EDT_UInt32, 1}, + {"NUM_SAMPLES_PER_LINE", 60, EDT_UInt32, 1}, + {"DATA_TYPE", 64, EDT_Char, 5}, + /*{"SPARE_1", 69, EDT_UByte, 51},*/ + {"DATA_ANALYSIS_FLAG", 120, EDT_UByte, 1}, + {"ANT_ELEV_CORR_FLAG", 121, EDT_UByte, 1}, + {"CHIRP_EXTRACT_FLAG", 122, EDT_UByte, 1}, + {"SRGR_FLAG", 123, EDT_UByte, 1}, + {"DOP_CEN_FLAG", 124, EDT_UByte, 1}, + {"DOP_AMB_FLAG", 125, EDT_UByte, 1}, + {"RANGE_SPREAD_COMP_FLAG", 126, EDT_UByte, 1}, + {"DETECTED_FLAG", 127, EDT_UByte, 1}, + {"LOOK_SUM_FLAG", 128, EDT_UByte, 1}, + {"RMS_EQUAL_FLAG", 129, EDT_UByte, 1}, + {"ANT_SCAL_FLAG", 130, EDT_UByte, 1}, + {"VGA_COM_ECHO_FLAG", 131, EDT_UByte, 1}, + {"VGA_COM_PULSE_2_FLAG", 132, EDT_UByte, 1}, + {"VGA_COM_PULSE_ZERO_FLAG", 133, EDT_UByte, 1}, + {"INV_FILT_COMP_FLAG", 134, EDT_UByte, 1}, + /*{"SPARE_2", 135, EDT_UByte, 6},*/ + {"RAW_DATA_ANALYSIS.1.NUM_GAPS", 141, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.1.NUM_MISSING_LINES", 145, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.1.RANGE_SAMP_SKIP", 149, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.1.RANGE_LINES_SKIP", 153, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.1.CALC_I_BIAS", 157, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.CALC_Q_BIAS", 161, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.CALC_I_STD_DEV", 165, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.CALC_Q_STD_DEV", 169, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.CALC_GAIN", 173, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.CALC_QUAD", 177, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.I_BIAS_MAX", 181, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.I_BIAS_MIN", 185, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.Q_BIAS_MAX", 189, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.Q_BIAS_MIN", 193, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.GAIN_MIN", 197, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.GAIN_MAX", 201, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.QUAD_MIN", 205, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.QUAD_MAX", 209, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.I_BIAS_FLAG", 213, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.1.Q_BIAS_FLAG", 214, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.1.GAIN_FLAG", 215, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.1.QUAD_FLAG", 216, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.1.USED_I_BIAS", 217, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.USED_Q_BIAS", 221, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.USED_GAIN", 225, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.USED_QUAD", 229, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.NUM_GAPS", 233, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.2.NUM_MISSING_LINES", 237, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.2.RANGE_SAMP_SKIP", 241, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.2.RANGE_LINES_SKIP", 245, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.2.CALC_I_BIAS", 249, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.CALC_Q_BIAS", 253, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.CALC_I_STD_DEV", 257, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.CALC_Q_STD_DEV", 261, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.CALC_GAIN", 265, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.CALC_QUAD", 269, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.I_BIAS_MAX", 273, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.I_BIAS_MIN", 277, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.Q_BIAS_MAX", 281, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.Q_BIAS_MIN", 285, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.GAIN_MIN", 289, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.GAIN_MAX", 293, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.QUAD_MIN", 297, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.QUAD_MAX", 301, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.I_BIAS_FLAG", 305, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.2.Q_BIAS_FLAG", 306, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.2.GAIN_FLAG", 307, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.2.QUAD_FLAG", 308, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.2.USED_I_BIAS", 309, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.USED_Q_BIAS", 313, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.USED_GAIN", 317, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.USED_QUAD", 321, EDT_Float32, 1}, + /*{"SPARE_3", 325, EDT_UByte, 32},*/ + {"START_TIME.1.FIRST_OBT", 357, EDT_UInt32, 2}, + {"START_TIME.1.FIRST_MJD", 365, EDT_MJD, 1}, + {"START_TIME.2.FIRST_OBT", 377, EDT_UInt32, 2}, + {"START_TIME.2.FIRST_MJD", 385, EDT_MJD, 1}, + {"PARAMETER_CODES.FIRST_SWST_CODE", 397, EDT_UInt16, 5}, + {"PARAMETER_CODES.LAST_SWST_CODE", 407, EDT_UInt16, 5}, + {"PARAMETER_CODES.PRI_CODE", 417, EDT_UInt16, 5}, + {"PARAMETER_CODES.TX_PULSE_LEN_CODE", 427, EDT_UInt16, 5}, + {"PARAMETER_CODES.TX_BW_CODE", 437, EDT_UInt16, 5}, + {"PARAMETER_CODES.ECHO_WIN_LEN_CODE", 447, EDT_UInt16, 5}, + {"PARAMETER_CODES.UP_CODE", 457, EDT_UInt16, 5}, + {"PARAMETER_CODES.DOWN_CODE", 467, EDT_UInt16, 5}, + {"PARAMETER_CODES.RESAMP_CODE", 477, EDT_UInt16, 5}, + {"PARAMETER_CODES.BEAM_ADJ_CODE", 487, EDT_UInt16, 5}, + {"PARAMETER_CODES.BEAM_SET_NUM_CODE", 497, EDT_UInt16, 5}, + {"PARAMETER_CODES.TX_MONITOR_CODE", 507, EDT_UInt16, 5}, + /*{"SPARE_4", 517, EDT_UByte, 60},*/ + {"ERROR_COUNTERS.NUM_ERR_SWST", 577, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_PRI", 581, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_TX_PULSE_LEN", 585, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_TX_PULSE_BW", 589, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_ECHO_WIN_LEN", 593, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_UP", 597, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_DOWN", 601, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_RESAMP", 605, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_BEAM_ADJ", 609, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_BEAM_SET_NUM", 613, EDT_UInt32, 1}, + /*{"SPARE_5", 617, EDT_UByte, 26},*/ + {"IMAGE_PARAMETERS.FIRST_SWST_VALUE", 643, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.LAST_SWST_VALUE", 663, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.SWST_CHANGES", 683, EDT_UInt32, 5}, + {"IMAGE_PARAMETERS.PRF_VALUE", 703, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.TX_PULSE_LEN_VALUE", 723, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.TX_PULSE_BW_VALUE", 743, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.ECHO_WIN_LEN_VALUE", 763, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.UP_VALUE", 783, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.DOWN_VALUE", 803, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.RESAMP_VALUE", 823, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.BEAM_ADJ_VALUE", 843, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.BEAM_SET_VALUE", 863, EDT_UInt16, 5}, + {"IMAGE_PARAMETERS.TX_MONITOR_VALUE", 873, EDT_Float32, 5}, + /*{"SPARE_6", 893, EDT_UByte, 82},*/ + {"FIRST_PROC_RANGE_SAMP", 975, EDT_UInt32, 1}, + {"RANGE_REF", 979, EDT_Float32, 1}, + {"RANGE_SAMP_RATE", 983, EDT_Float32, 1}, + {"RADAR_FREQ", 987, EDT_Float32, 1}, + {"NUM_LOOKS_RANGE", 991, EDT_UInt16, 1}, + {"FILTER_WINDOW", 993, EDT_Char, 7}, + {"WINDOW_COEF_RANGE", 1000, EDT_Float32, 1}, + {"BANDWIDTH.LOOK_BW_RANGE", 1004, EDT_Float32, 5}, + {"BANDWIDTH.TOT_BW_RANGE", 1024, EDT_Float32, 5}, + {"NOMINAL_CHIRP.1.NOM_CHIRP_AMP", 1044, EDT_Float32, 4}, + {"NOMINAL_CHIRP.1.NOM_CHIRP_PHS", 1060, EDT_Float32, 4}, + {"NOMINAL_CHIRP.2.NOM_CHIRP_AMP", 1076, EDT_Float32, 4}, + {"NOMINAL_CHIRP.2.NOM_CHIRP_PHS", 1092, EDT_Float32, 4}, + {"NOMINAL_CHIRP.3.NOM_CHIRP_AMP", 1108, EDT_Float32, 4}, + {"NOMINAL_CHIRP.3.NOM_CHIRP_PHS", 1124, EDT_Float32, 4}, + {"NOMINAL_CHIRP.4.NOM_CHIRP_AMP", 1140, EDT_Float32, 4}, + {"NOMINAL_CHIRP.4.NOM_CHIRP_PHS", 1156, EDT_Float32, 4}, + {"NOMINAL_CHIRP.5.NOM_CHIRP_AMP", 1172, EDT_Float32, 4}, + {"NOMINAL_CHIRP.5.NOM_CHIRP_PHS", 1188, EDT_Float32, 4}, + /*{"SPARE_7", 1204, EDT_UByte, 60},*/ + {"NUM_LINES_PROC", 1264, EDT_UInt32, 1}, + {"NUM_LOOK_AZ", 1268, EDT_UInt16, 1}, + {"LOOK_BW_AZ", 1270, EDT_Float32, 1}, + {"TO_BW_AZ", 1274, EDT_Float32, 1}, + {"FILTER_AZ", 1278, EDT_Char, 7}, + {"FILTER_COEF_AZ", 1285, EDT_Float32, 1}, + {"AZ_FM_RATE", 1289, EDT_Float32, 3}, + {"AX_FM_ORIGIN", 1301, EDT_Float32, 1}, + {"DOP_AMB_CONF", 1305, EDT_Float32, 1}, + /*{"SPARE_8", 1309, EDT_UByte, 68},*/ + {"CALIBRATION_FACTORS.1.PROC_SCALING_FACT", 1377, EDT_Float32, 1}, + {"CALIBRATION_FACTORS.1.EXT_CAL_FACT", 1381, EDT_Float32, 1}, + {"CALIBRATION_FACTORS.2.PROC_SCALING_FACT", 1385, EDT_Float32, 1}, + {"CALIBRATION_FACTORS.2.EXT_CAL_FACT", 1389, EDT_Float32, 1}, + {"NOISE_ESTIMATION.NOISE_POWER_CORR", 1393, EDT_Float32, 5}, + {"NOISE_ESTIMATION.NUM_NOISE_LINES", 1413, EDT_UInt32, 5}, + /*{"SPARE_9", 1433, EDT_UByte, 76},*/ + {"OUTPUT_STATISTICS.1.OUT_MEAN", 1509, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.1.OUT_IMAG_MEAN", 1513, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.1.OUT_STD_DEV", 1517, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.1.OUT_IMAG_STD_DEV", 1521, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.2.OUT_MEAN", 1525, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.2.OUT_IMAG_MEAN", 1529, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.2.OUT_STD_DEV", 1533, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.2.OUT_IMAG_STD_DEV", 1537, EDT_Float32, 1}, + /*{"SPARE_10", 1541, EDT_UByte, 52},*/ + {"ECHO_COMP", 1593, EDT_Char, 4}, + {"ECHO_COMP_RATIO", 1597, EDT_Char, 3}, + {"INIT_CAL_COMP", 1600, EDT_Char, 4}, + {"INIT_CAL_RATIO", 1604, EDT_Char, 3}, + {"PER_CAL_COMP", 1607, EDT_Char, 4}, + {"PER_CAL_RATIO", 1611, EDT_Char, 3}, + {"NOISE_COMP", 1614, EDT_Char, 4}, + {"NOISE_COMP_RATIO", 1618, EDT_Char, 3}, + /*{"SPARE_11", 1621, EDT_UByte, 64},*/ + {"BEAM_MERGE_SL_RANGE", 1685, EDT_UInt32, 4}, + {"BEAM_MERGE_ALG_PARAM", 1701, EDT_Float32, 4}, + {"LINES_PER_BURST", 1717, EDT_UInt32, 5}, + /*{"SPARE_12", 1737, EDT_UByte, 28},*/ + {"ORBIT_STATE_VECTORS.1.STATE_VECT_TIME_1", 1765, EDT_MJD, 1}, + {"ORBIT_STATE_VECTORS.1.X_POS_1", 1777, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.1.Y_POS_1", 1781, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.1.Z_POS_1", 1785, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.1.X_VEL_1", 1789, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.1.Y_VEL_1", 1793, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.1.Z_VEL_1", 1797, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.2.STATE_VECT_TIME_1", 1801, EDT_MJD, 1}, + {"ORBIT_STATE_VECTORS.2.X_POS_1", 1813, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.2.Y_POS_1", 1817, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.2.Z_POS_1", 1821, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.2.X_VEL_1", 1825, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.2.Y_VEL_1", 1829, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.2.Z_VEL_1", 1833, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.3.STATE_VECT_TIME_1", 1837, EDT_MJD, 1}, + {"ORBIT_STATE_VECTORS.3.X_POS_1", 1849, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.3.Y_POS_1", 1853, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.3.Z_POS_1", 1857, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.3.X_VEL_1", 1861, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.3.Y_VEL_1", 1865, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.3.Z_VEL_1", 1869, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.4.STATE_VECT_TIME_1", 1873, EDT_MJD, 1}, + {"ORBIT_STATE_VECTORS.4.X_POS_1", 1885, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.4.Y_POS_1", 1889, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.4.Z_POS_1", 1893, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.4.X_VEL_1", 1897, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.4.Y_VEL_1", 1901, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.4.Z_VEL_1", 1905, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.5.STATE_VECT_TIME_1", 1909, EDT_MJD, 1}, + {"ORBIT_STATE_VECTORS.5.X_POS_1", 1921, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.5.Y_POS_1", 1925, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.5.Z_POS_1", 1929, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.5.X_VEL_1", 1933, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.5.Y_VEL_1", 1937, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.5.Z_VEL_1", 1941, EDT_Int32, 1}, + /*{"SPARE_13", 1945, EDT_UByte, 64},*/ + {NULL, 0, EDT_Unknown, 0} +}; + +static const EnvisatFieldDescr ASAR_MAP_PROJECTION_GADS[] = { + {"MAP_DESCRIPTOR", 0, EDT_Char, 32}, + {"SAMPLES", 32, EDT_UInt32, 1}, + {"LINES", 36, EDT_UInt32, 1}, + {"SAMPLE_SPACING", 40, EDT_Float32, 1}, + {"LINE_SPACING", 44, EDT_Float32, 1}, + {"ORIENTATION", 48, EDT_Float32, 1}, + /*{"SPARE_1", 52, EDT_UByte, 40},*/ + {"HEADING", 92, EDT_Float32, 1}, + {"ELLIPSOID_NAME", 96, EDT_Char, 32}, + {"SEMI_MAJOR", 128, EDT_Float32, 1}, + {"SEMI_MINOR", 132, EDT_Float32, 1}, + {"SHIFT_DX", 136, EDT_Float32, 1}, + {"SHIFT_DY", 140, EDT_Float32, 1}, + {"SHIFT_DZ", 144, EDT_Float32, 1}, + {"AVG_HEIGHT", 148, EDT_Float32, 1}, + /*{"SPARE_2", 152, EDT_UByte, 12},*/ + {"PROJECTION_DESCRIPTION", 164, EDT_Char, 32}, + {"UTM_DESCRIPTOR", 196, EDT_Char, 32}, + {"UTM_ZONE", 228, EDT_Char, 4}, + {"UTM_ORIGIN_EASTING", 232, EDT_Float32, 1}, + {"UTM_ORIGIN_NORTHING", 236, EDT_Float32, 1}, + {"UTM_CENTER_LONG", 240, EDT_Int32, 1}, + {"UTM_CENTER_LAT", 244, EDT_Int32, 1}, + {"UTM_PARA1", 248, EDT_Float32, 1}, + {"UTM_PARA2", 252, EDT_Float32, 1}, + {"UTM_SCALE", 256, EDT_Float32, 1}, + {"UPS_DESCRIPTOR", 260, EDT_Char, 32}, + {"UPS_CENTER_LONG", 292, EDT_Int32, 1}, + {"UPS_CENTER_LAT", 296, EDT_Int32, 1}, + {"UPS_SCALE", 300, EDT_Float32, 1}, + {"NSP_DESCRIPTOR", 304, EDT_Char, 32}, + {"ORIGIN_EASTING", 336, EDT_Float32, 1}, + {"ORIGIN_NORTHING", 340, EDT_Float32, 1}, + {"CENTER_LONG", 344, EDT_Int32, 1}, + {"CENTER_LAT", 348, EDT_Int32, 1}, + {"STANDARD_PARALLEL_PARAMETERS.PARA1", 352, EDT_Float32, 1}, + {"STANDARD_PARALLEL_PARAMETERS.PARA2", 356, EDT_Float32, 1}, + {"STANDARD_PARALLEL_PARAMETERS.PARA3", 360, EDT_Float32, 1}, + {"STANDARD_PARALLEL_PARAMETERS.PARA4", 364, EDT_Float32, 1}, + {"CENTRAL_MERIDIAN_PARAMETERS.CENTRAL_M1", 368, EDT_Float32, 1}, + {"CENTRAL_MERIDIAN_PARAMETERS.CENTRAL_M2", 372, EDT_Float32, 1}, + {"CENTRAL_MERIDIAN_PARAMETERS.CENTRAL_M3", 376, EDT_Float32, 1}, + /*{"PROJECTION_PARAMETERS.SPARE_3", 380, EDT_UByte, 16},*/ + {"POSITION_NORTHINGS_EASTINGS.TL_NORTHING", 396, EDT_Float32, 1}, + {"POSITION_NORTHINGS_EASTINGS.TL_EASTING", 400, EDT_Float32, 1}, + {"POSITION_NORTHINGS_EASTINGS.TR_NORTHING", 404, EDT_Float32, 1}, + {"POSITION_NORTHINGS_EASTINGS.TR_EASTING", 408, EDT_Float32, 1}, + {"POSITION_NORTHINGS_EASTINGS.BR_NORTHING", 412, EDT_Float32, 1}, + {"POSITION_NORTHINGS_EASTINGS.BR_EASTING", 416, EDT_Float32, 1}, + {"POSITION_NORTHINGS_EASTINGS.BL_NORTHING", 420, EDT_Float32, 1}, + {"POSITION_NORTHINGS_EASTINGS.BL_EASTING", 424, EDT_Float32, 1}, + {"POSITION_LAT_LONG.TL_LAT", 428, EDT_Int32, 1}, + {"POSITION_LAT_LONG.TL_LONG", 432, EDT_Int32, 1}, + {"POSITION_LAT_LONG.TR_LAT", 436, EDT_Int32, 1}, + {"POSITION_LAT_LONG.TR_LONG", 440, EDT_Int32, 1}, + {"POSITION_LAT_LONG.BR_LAT", 444, EDT_Int32, 1}, + {"POSITION_LAT_LONG.BR_LONG", 448, EDT_Int32, 1}, + {"POSITION_LAT_LONG.BL_LAT", 452, EDT_Int32, 1}, + {"POSITION_LAT_LONG.BL_LONG", 456, EDT_Int32, 1}, + /*{"SPARE_4", 460, EDT_UByte, 32},*/ + {"IMAGE_TO_MAP_COEFS", 492, EDT_Float32, 8}, + {"MAP_TO_IMAGE_COEFS", 524, EDT_Float32, 8}, + /*{"SPARE_5", 556, EDT_UByte, 35},*/ + {NULL, 0, EDT_Unknown, 0} +}; + +static const EnvisatFieldDescr ASAR_SQ_ADSR[] = { + {"ZERO_DOPPLER_TIME", 0, EDT_MJD, 1}, + {"ATTACH_FLAG", 12, EDT_UByte, 1}, + {"INPUT_MEAN_FLAG", 13, EDT_UByte, 1}, + {"INPUT_STD_DEV_FLAG", 14, EDT_UByte, 1}, + {"INPUT_GAPS_FLAG", 15, EDT_UByte, 1}, + {"INPUT_MISSING_LINES_FLAG", 16, EDT_UByte, 1}, + {"DOP_CEN_FLAG", 17, EDT_UByte, 1}, + {"DOP_AMB_FLAG", 18, EDT_UByte, 1}, + {"OUTPUT_MEAN_FLAG", 19, EDT_UByte, 1}, + {"OUTPUT_STD_DEV_FLAG", 20, EDT_UByte, 1}, + {"CHIRP_FLAG", 21, EDT_UByte, 1}, + {"MISSING_DATA_SETS_FLAG", 22, EDT_UByte, 1}, + {"INVALID_DOWNLINK_FLAG", 23, EDT_UByte, 1}, + /*{"SPARE_1", 24, EDT_UByte, 7},*/ + {"THRESH_CHIRP_BROADENING", 31, EDT_Float32, 1}, + {"THRESH_CHIRP_SIDELOBE", 35, EDT_Float32, 1}, + {"THRESH_CHIRP_ISLR", 39, EDT_Float32, 1}, + {"THRESH_INPUT_MEAN", 43, EDT_Float32, 1}, + {"EXP_INPUT_MEAN", 47, EDT_Float32, 1}, + {"THRESH_INPUT_STD_DEV", 51, EDT_Float32, 1}, + {"EXP_INPUT_STD_DEV", 55, EDT_Float32, 1}, + {"THRESH_DOP_CEN", 59, EDT_Float32, 1}, + {"THRESH_DOP_AMB", 63, EDT_Float32, 1}, + {"THRESH_OUTPUT_MEAN", 67, EDT_Float32, 1}, + {"EXP_OUTPUT_MEAN", 71, EDT_Float32, 1}, + {"THRESH_OUTPUT_STD_DEV", 75, EDT_Float32, 1}, + {"EXP_OUTPUT_STD_DEV", 79, EDT_Float32, 1}, + {"THRESH_INPUT_MISSING_LINES", 83, EDT_Float32, 1}, + {"THRESH_INPUT_GAPS", 87, EDT_Float32, 1}, + {"LINES_PER_GAPS", 91, EDT_UInt32, 1}, + /*{"SPARE_2", 95, EDT_UByte, 15},*/ + {"INPUT_MEAN", 110, EDT_Float32, 2}, + {"INPUT_STD_DEV", 118, EDT_Float32, 2}, + {"NUM_GAPS", 126, EDT_Float32, 1}, + {"NUM_MISSING_LINES", 130, EDT_Float32, 1}, + {"OUTPUT_MEAN", 134, EDT_Float32, 2}, + {"OUTPUT_STD_DEV", 142, EDT_Float32, 2}, + {"TOT_ERRORS", 150, EDT_UInt32, 1}, + /*{"SPARE_3", 154, EDT_UByte, 16},*/ + {NULL, 0, EDT_Unknown, 0} +}; + +static const EnvisatFieldDescr ASAR_SR_GR_ADSR[] = { + {"ZERO_DOPPLER_TIME", 0, EDT_MJD, 1}, + {"ATTACH_FLAG", 12, EDT_UByte, 1}, + {"SLANT_RANGE_TIME", 13, EDT_Float32, 1}, + {"GROUND_RANGE_ORIGIN", 17, EDT_Float32, 1}, + {"SRGR_COEFF", 21, EDT_Float32, 5}, + /*{"SPARE_1", 41, EDT_UByte, 14},*/ + {NULL, 0, EDT_Unknown, 0} +}; + +#if 0 /* Unused */ +static const EnvisatFieldDescr ASAR_GEOLOCATION_ADSR[] = { + {"ZERO_DOPPLER_TIME", 0, EDT_MJD, 1}, + {"ATTACH_FLAG", 12, EDT_UByte, 1}, + {"CENTER_LAT", 13, EDT_Int32, 1}, + {"CENTER_LONG", 17, EDT_Int32, 1}, + /*{"SPARE_1", 21, EDT_UByte, 4},*/ + {NULL, 0, EDT_Unknown, 0} +}; +#endif /* Unused */ + +static const EnvisatFieldDescr ASAR_PROCESSING_PARAMS_ADSR[] = { + {"FIRST_ZERO_DOPPLER_TIME", 0, EDT_MJD, 1}, + {"ATTACH_FLAG", 12, EDT_UByte, 1}, + {"LAST_ZERO_DOPPLER_TIME", 13, EDT_MJD, 1}, + {"WORK_ORDER_ID", 25, EDT_Char, 12}, + {"TIME_DIFF", 37, EDT_Float32, 1}, + {"SWATH_ID", 41, EDT_Char, 3}, + {"RANGE_SPACING", 44, EDT_Float32, 1}, + {"AZIMUTH_SPACING", 48, EDT_Float32, 1}, + {"LINE_TIME_INTERVAL", 52, EDT_Float32, 1}, + {"NUM_OUTPUT_LINES", 56, EDT_UInt32, 1}, + {"NUM_SAMPLES_PER_LINE", 60, EDT_UInt32, 1}, + {"DATA_TYPE", 64, EDT_Char, 5}, + /*{"SPARE_1", 69, EDT_UByte, 51},*/ + {"DATA_ANALYSIS_FLAG", 120, EDT_UByte, 1}, + {"ANT_ELEV_CORR_FLAG", 121, EDT_UByte, 1}, + {"CHIRP_EXTRACT_FLAG", 122, EDT_UByte, 1}, + {"SRGR_FLAG", 123, EDT_UByte, 1}, + {"DOP_CEN_FLAG", 124, EDT_UByte, 1}, + {"DOP_AMB_FLAG", 125, EDT_UByte, 1}, + {"RANGE_SPREAD_COMP_FLAG", 126, EDT_UByte, 1}, + {"DETECTED_FLAG", 127, EDT_UByte, 1}, + {"LOOK_SUM_FLAG", 128, EDT_UByte, 1}, + {"RMS_EQUAL_FLAG", 129, EDT_UByte, 1}, + {"ANT_SCAL_FLAG", 130, EDT_UByte, 1}, + /*{"SPARE_2", 131, EDT_UByte, 10},*/ + {"RAW_DATA_ANALYSIS.1.NUM_GAPS", 141, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.1.NUM_MISSING_LINES", 145, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.1.RANGE_SAMP_SKIP", 149, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.1.RANGE_LINES_SKIP", 153, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.1.CALC_I_BIAS", 157, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.CALC_Q_BIAS", 161, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.CALC_I_STD_DEV", 165, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.CALC_Q_STD_DEV", 169, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.CALC_GAIN", 173, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.CALC_QUAD", 177, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.I_BIAS_MAX", 181, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.I_BIAS_MIN", 185, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.Q_BIAS_MAX", 189, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.Q_BIAS_MIN", 193, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.GAIN_MIN", 197, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.GAIN_MAX", 201, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.QUAD_MIN", 205, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.QUAD_MAX", 209, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.I_BIAS_FLAG", 213, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.1.Q_BIAS_FLAG", 214, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.1.GAIN_FLAG", 215, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.1.QUAD_FLAG", 216, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.1.USED_I_BIAS", 217, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.USED_Q_BIAS", 221, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.USED_GAIN", 225, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.1.USED_QUAD", 229, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.NUM_GAPS", 233, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.2.NUM_MISSING_LINES", 237, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.2.RANGE_SAMP_SKIP", 241, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.2.RANGE_LINES_SKIP", 245, EDT_UInt32, 1}, + {"RAW_DATA_ANALYSIS.2.CALC_I_BIAS", 249, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.CALC_Q_BIAS", 253, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.CALC_I_STD_DEV", 257, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.CALC_Q_STD_DEV", 261, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.CALC_GAIN", 265, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.CALC_QUAD", 269, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.I_BIAS_MAX", 273, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.I_BIAS_MIN", 277, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.Q_BIAS_MAX", 281, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.Q_BIAS_MIN", 285, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.GAIN_MIN", 289, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.GAIN_MAX", 293, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.QUAD_MIN", 297, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.QUAD_MAX", 301, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.I_BIAS_FLAG", 305, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.2.Q_BIAS_FLAG", 306, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.2.GAIN_FLAG", 307, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.2.QUAD_FLAG", 308, EDT_UByte, 1}, + {"RAW_DATA_ANALYSIS.2.USED_I_BIAS", 309, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.USED_Q_BIAS", 313, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.USED_GAIN", 317, EDT_Float32, 1}, + {"RAW_DATA_ANALYSIS.2.USED_QUAD", 321, EDT_Float32, 1}, + /*{"SPARE_3", 325, EDT_UByte, 32},*/ + {"START_TIME.1.FIRST_OBT", 357, EDT_UInt32, 2}, + {"START_TIME.1.FIRST_MJD", 365, EDT_MJD, 1}, + {"START_TIME.2.FIRST_OBT", 377, EDT_UInt32, 2}, + {"START_TIME.2.FIRST_MJD", 385, EDT_MJD, 1}, + {"PARAMETER_CODES.SWST_CODE", 397, EDT_UInt16, 5}, + {"PARAMETER_CODES.LAST_SWST_CODE", 407, EDT_UInt16, 5}, + {"PARAMETER_CODES.PRI_CODE", 417, EDT_UInt16, 5}, + {"PARAMETER_CODES.TX_PULSE_LEN_CODE", 427, EDT_UInt16, 5}, + {"PARAMETER_CODES.TX_BW_CODE", 437, EDT_UInt16, 5}, + {"PARAMETER_CODES.ECHO_WIN_LEN_CODE", 447, EDT_UInt16, 5}, + {"PARAMETER_CODES.UP_CODE", 457, EDT_UInt16, 5}, + {"PARAMETER_CODES.DOWN_CODE", 467, EDT_UInt16, 5}, + {"PARAMETER_CODES.RESAMP_CODE", 477, EDT_UInt16, 5}, + {"PARAMETER_CODES.BEAM_ADJ_CODE", 487, EDT_UInt16, 5}, + {"PARAMETER_CODES.BEAM_SET_NUM_CODE", 497, EDT_UInt16, 5}, + {"PARAMETER_CODES.TX_MONITOR_CODE", 507, EDT_UInt16, 5}, + /*{"SPARE_4", 517, EDT_UByte, 60},*/ + {"ERROR_COUNTERS.NUM_ERR_SWST", 577, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_PRI", 581, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_TX_PULSE_LEN", 585, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_TX_PULSE_BW", 589, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_ECHO_WIN_LEN", 593, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_UP", 597, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_DOWN", 601, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_RESAMP", 605, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_BEAM_ADJ", 609, EDT_UInt32, 1}, + {"ERROR_COUNTERS.NUM_ERR_BEAM_SET_NUM", 613, EDT_UInt32, 1}, + /*{"SPARE_5", 617, EDT_UByte, 26},*/ + {"IMAGE_PARAMETERS.SWST_VALUE", 643, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.LAST_SWST_VALUE", 663, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.SWST_CHANGES", 683, EDT_UInt32, 5}, + {"IMAGE_PARAMETERS.PRF_VALUE", 703, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.TX_PULSE_LEN_VALUE", 723, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.TX_PULSE_BW_VALUE", 743, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.ECHO_WIN_LEN_VALUE", 763, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.UP_VALUE", 783, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.DOWN_VALUE", 803, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.RESAMP_VALUE", 823, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.BEAM_ADJ_VALUE", 843, EDT_Float32, 5}, + {"IMAGE_PARAMETERS.BEAM_SET_VALUE", 863, EDT_UInt16, 5}, + {"IMAGE_PARAMETERS.TX_MONITOR_VALUE", 873, EDT_Float32, 5}, + /*{"SPARE_6", 893, EDT_UByte, 82},*/ + {"FIRST_PROC_RANGE_SAMP", 975, EDT_UInt32, 1}, + {"RANGE_REF", 979, EDT_Float32, 1}, + {"RANGE_SAMP_RATE", 983, EDT_Float32, 1}, + {"RADAR_FREQ", 987, EDT_Float32, 1}, + {"NUM_LOOKS_RANGE", 991, EDT_UInt16, 1}, + {"FILTER_RANGE", 993, EDT_Char, 7}, + {"FILTER_COEF_RANGE", 1000, EDT_Float32, 1}, + {"BANDWIDTH.LOOK_BW_RANGE", 1004, EDT_Float32, 5}, + {"BANDWIDTH.TOT_BW_RANGE", 1024, EDT_Float32, 5}, + {"NOMINAL_CHIRP.1.NOM_CHIRP_AMP", 1044, EDT_Float32, 4}, + {"NOMINAL_CHIRP.1.NOM_CHIRP_PHS", 1060, EDT_Float32, 4}, + {"NOMINAL_CHIRP.2.NOM_CHIRP_AMP", 1076, EDT_Float32, 4}, + {"NOMINAL_CHIRP.2.NOM_CHIRP_PHS", 1092, EDT_Float32, 4}, + {"NOMINAL_CHIRP.3.NOM_CHIRP_AMP", 1108, EDT_Float32, 4}, + {"NOMINAL_CHIRP.3.NOM_CHIRP_PHS", 1124, EDT_Float32, 4}, + {"NOMINAL_CHIRP.4.NOM_CHIRP_AMP", 1140, EDT_Float32, 4}, + {"NOMINAL_CHIRP.4.NOM_CHIRP_PHS", 1156, EDT_Float32, 4}, + {"NOMINAL_CHIRP.5.NOM_CHIRP_AMP", 1172, EDT_Float32, 4}, + {"NOMINAL_CHIRP.5.NOM_CHIRP_PHS", 1188, EDT_Float32, 4}, + /*{"SPARE_7", 1204, EDT_UByte, 60},*/ + {"NUM_LINES_PROC", 1264, EDT_UInt32, 1}, + {"NUM_LOOK_AZ", 1268, EDT_UInt16, 1}, + {"LOOK_BW_AZ", 1270, EDT_Float32, 1}, + {"TO_BW_AZ", 1274, EDT_Float32, 1}, + {"FILTER_AZ", 1278, EDT_Char, 7}, + {"FILTER_COEF_AZ", 1285, EDT_Float32, 1}, + {"AZ_FM_RATE", 1289, EDT_Float32, 3}, + {"AX_FM_ORIGIN", 1301, EDT_Float32, 1}, + {"DOP_AMB_CONF", 1305, EDT_Float32, 1}, + /*{"SPARE_8", 1309, EDT_UByte, 68},*/ + {"CALIBRATION_FACTORS.1.PROC_SCALING_FACT", 1377, EDT_Float32, 1}, + {"CALIBRATION_FACTORS.1.EXT_CAL_FACT", 1381, EDT_Float32, 1}, + {"CALIBRATION_FACTORS.2.PROC_SCALING_FACT", 1385, EDT_Float32, 1}, + {"CALIBRATION_FACTORS.2.EXT_CAL_FACT", 1389, EDT_Float32, 1}, + {"NOISE_ESTIMATION.NOISE_POWER_CORR", 1393, EDT_Float32, 5}, + {"NOISE_ESTIMATION.NUM_NOISE_LINES", 1413, EDT_UInt32, 5}, + /*{"SPARE_9", 1433, EDT_UByte, 76},*/ + {"OUTPUT_STATISTICS.1.OUT_MEAN", 1509, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.1.OUT_IMAG_MEAN", 1513, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.1.OUT_STD_DEV", 1517, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.1.OUT_IMAG_STD_DEV", 1521, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.2.OUT_MEAN", 1525, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.2.OUT_IMAG_MEAN", 1529, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.2.OUT_STD_DEV", 1533, EDT_Float32, 1}, + {"OUTPUT_STATISTICS.2.OUT_IMAG_STD_DEV", 1537, EDT_Float32, 1}, + /*{"SPARE_10", 1541, EDT_UByte, 52},*/ + {"ECHO_COMP", 1593, EDT_Char, 4}, + {"ECHO_COMP_RATIO", 1597, EDT_Char, 3}, + {"INIT_CAL_COMP", 1600, EDT_Char, 4}, + {"INIT_CAL_RATIO", 1604, EDT_Char, 3}, + {"PER_CAL_COMP", 1607, EDT_Char, 4}, + {"PER_CAL_RATIO", 1611, EDT_Char, 3}, + {"NOISE_COMP", 1614, EDT_Char, 4}, + {"NOISE_COMP_RATIO", 1618, EDT_Char, 3}, + /*{"SPARE_11", 1621, EDT_UByte, 64},*/ + {"BEAM_OVERLAP", 1685, EDT_UInt32, 4}, + {"LINES_PER_BURST", 1701, EDT_UInt32, 5}, + /*{"SPARE_12", 1721, EDT_UByte, 44},*/ + {"ORBIT_STATE_VECTORS.1.STATE_VECT_TIME_1", 1765, EDT_MJD, 1}, + {"ORBIT_STATE_VECTORS.1.X_POS_1", 1777, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.1.Y_POS_1", 1781, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.1.Z_POS_1", 1785, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.1.X_VEL_1", 1789, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.1.Y_VEL_1", 1793, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.1.Z_VEL_1", 1797, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.2.STATE_VECT_TIME_1", 1801, EDT_MJD, 1}, + {"ORBIT_STATE_VECTORS.2.X_POS_1", 1813, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.2.Y_POS_1", 1817, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.2.Z_POS_1", 1821, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.2.X_VEL_1", 1825, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.2.Y_VEL_1", 1829, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.2.Z_VEL_1", 1833, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.3.STATE_VECT_TIME_1", 1837, EDT_MJD, 1}, + {"ORBIT_STATE_VECTORS.3.X_POS_1", 1849, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.3.Y_POS_1", 1853, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.3.Z_POS_1", 1857, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.3.X_VEL_1", 1861, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.3.Y_VEL_1", 1865, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.3.Z_VEL_1", 1869, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.4.STATE_VECT_TIME_1", 1873, EDT_MJD, 1}, + {"ORBIT_STATE_VECTORS.4.X_POS_1", 1885, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.4.Y_POS_1", 1889, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.4.Z_POS_1", 1893, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.4.X_VEL_1", 1897, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.4.Y_VEL_1", 1901, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.4.Z_VEL_1", 1905, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.5.STATE_VECT_TIME_1", 1909, EDT_MJD, 1}, + {"ORBIT_STATE_VECTORS.5.X_POS_1", 1921, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.5.Y_POS_1", 1925, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.5.Z_POS_1", 1929, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.5.X_VEL_1", 1933, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.5.Y_VEL_1", 1937, EDT_Int32, 1}, + {"ORBIT_STATE_VECTORS.5.Z_VEL_1", 1941, EDT_Int32, 1}, + /*{"SPARE_13", 1945, EDT_UByte, 64},*/ + {"SLANT_RANGE_TIME", 2009, EDT_Float32, 1}, + {"DOP_COEF", 2013, EDT_Float32, 5}, + {"DOP_CONF", 2033, EDT_Float32, 1}, + /*{"SPARE_14", 2037, EDT_UByte, 14},*/ + {"CHIRP_WIDTH", 2051, EDT_Float32, 1}, + {"CHIRP_SIDELOBE", 2055, EDT_Float32, 1}, + {"CHIRP_ISLR", 2059, EDT_Float32, 1}, + {"CHIRP_PEAK_LOC", 2063, EDT_Float32, 1}, + {"CHIRP_POWER", 2067, EDT_Float32, 1}, + {"ELEV_CORR_FACTOR", 2071, EDT_Float32, 1}, + /*{"SPARE_15", 2075, EDT_UByte, 16},*/ + {"CAL_INFO.1.MAX_CAL", 2091, EDT_Float32, 3}, + {"CAL_INFO.1.AVG_CAL", 2103, EDT_Float32, 3}, + {"CAL_INFO.1.AVG_VAL_1A", 2115, EDT_Float32, 1}, + {"CAL_INFO.1.PHS_CAL", 2119, EDT_Float32, 4}, + {"CAL_INFO.2.MAX_CAL", 2135, EDT_Float32, 3}, + {"CAL_INFO.2.AVG_CAL", 2147, EDT_Float32, 3}, + {"CAL_INFO.2.AVG_VAL_1A", 2159, EDT_Float32, 1}, + {"CAL_INFO.2.PHS_CAL", 2163, EDT_Float32, 4}, + {"CAL_INFO.3.MAX_CAL", 2179, EDT_Float32, 3}, + {"CAL_INFO.3.AVG_CAL", 2191, EDT_Float32, 3}, + {"CAL_INFO.3.AVG_VAL_1A", 2203, EDT_Float32, 1}, + {"CAL_INFO.3.PHS_CAL", 2207, EDT_Float32, 4}, + {"CAL_INFO.4.MAX_CAL", 2223, EDT_Float32, 3}, + {"CAL_INFO.4.AVG_CAL", 2235, EDT_Float32, 3}, + {"CAL_INFO.4.AVG_VAL_1A", 2247, EDT_Float32, 1}, + {"CAL_INFO.4.PHS_CAL", 2251, EDT_Float32, 4}, + {"CAL_INFO.5.MAX_CAL", 2267, EDT_Float32, 3}, + {"CAL_INFO.5.AVG_CAL", 2279, EDT_Float32, 3}, + {"CAL_INFO.5.AVG_VAL_1A", 2291, EDT_Float32, 1}, + {"CAL_INFO.5.PHS_CAL", 2295, EDT_Float32, 4}, + {"CAL_INFO.6.MAX_CAL", 2311, EDT_Float32, 3}, + {"CAL_INFO.6.AVG_CAL", 2323, EDT_Float32, 3}, + {"CAL_INFO.6.AVG_VAL_1A", 2335, EDT_Float32, 1}, + {"CAL_INFO.6.PHS_CAL", 2339, EDT_Float32, 4}, + {"CAL_INFO.7.MAX_CAL", 2355, EDT_Float32, 3}, + {"CAL_INFO.7.AVG_CAL", 2367, EDT_Float32, 3}, + {"CAL_INFO.7.AVG_VAL_1A", 2379, EDT_Float32, 1}, + {"CAL_INFO.7.PHS_CAL", 2383, EDT_Float32, 4}, + {"CAL_INFO.8.MAX_CAL", 2399, EDT_Float32, 3}, + {"CAL_INFO.8.AVG_CAL", 2411, EDT_Float32, 3}, + {"CAL_INFO.8.AVG_VAL_1A", 2423, EDT_Float32, 1}, + {"CAL_INFO.8.PHS_CAL", 2427, EDT_Float32, 4}, + {"CAL_INFO.9.MAX_CAL", 2443, EDT_Float32, 3}, + {"CAL_INFO.9.AVG_CAL", 2455, EDT_Float32, 3}, + {"CAL_INFO.9.AVG_VAL_1A", 2467, EDT_Float32, 1}, + {"CAL_INFO.9.PHS_CAL", 2471, EDT_Float32, 4}, + {"CAL_INFO.10.MAX_CAL", 2487, EDT_Float32, 3}, + {"CAL_INFO.10.AVG_CAL", 2499, EDT_Float32, 3}, + {"CAL_INFO.10.AVG_VAL_1A", 2511, EDT_Float32, 1}, + {"CAL_INFO.10.PHS_CAL", 2515, EDT_Float32, 4}, + {"CAL_INFO.11.MAX_CAL", 2531, EDT_Float32, 3}, + {"CAL_INFO.11.AVG_CAL", 2543, EDT_Float32, 3}, + {"CAL_INFO.11.AVG_VAL_1A", 2555, EDT_Float32, 1}, + {"CAL_INFO.11.PHS_CAL", 2559, EDT_Float32, 4}, + {"CAL_INFO.12.MAX_CAL", 2575, EDT_Float32, 3}, + {"CAL_INFO.12.AVG_CAL", 2587, EDT_Float32, 3}, + {"CAL_INFO.12.AVG_VAL_1A", 2599, EDT_Float32, 1}, + {"CAL_INFO.12.PHS_CAL", 2603, EDT_Float32, 4}, + {"CAL_INFO.13.MAX_CAL", 2619, EDT_Float32, 3}, + {"CAL_INFO.13.AVG_CAL", 2631, EDT_Float32, 3}, + {"CAL_INFO.13.AVG_VAL_1A", 2643, EDT_Float32, 1}, + {"CAL_INFO.13.PHS_CAL", 2647, EDT_Float32, 4}, + {"CAL_INFO.14.MAX_CAL", 2663, EDT_Float32, 3}, + {"CAL_INFO.14.AVG_CAL", 2675, EDT_Float32, 3}, + {"CAL_INFO.14.AVG_VAL_1A", 2687, EDT_Float32, 1}, + {"CAL_INFO.14.PHS_CAL", 2691, EDT_Float32, 4}, + {"CAL_INFO.15.MAX_CAL", 2707, EDT_Float32, 3}, + {"CAL_INFO.15.AVG_CAL", 2719, EDT_Float32, 3}, + {"CAL_INFO.15.AVG_VAL_1A", 2731, EDT_Float32, 1}, + {"CAL_INFO.15.PHS_CAL", 2735, EDT_Float32, 4}, + {"CAL_INFO.16.MAX_CAL", 2751, EDT_Float32, 3}, + {"CAL_INFO.16.AVG_CAL", 2763, EDT_Float32, 3}, + {"CAL_INFO.16.AVG_VAL_1A", 2775, EDT_Float32, 1}, + {"CAL_INFO.16.PHS_CAL", 2779, EDT_Float32, 4}, + {"CAL_INFO.17.MAX_CAL", 2795, EDT_Float32, 3}, + {"CAL_INFO.17.AVG_CAL", 2807, EDT_Float32, 3}, + {"CAL_INFO.17.AVG_VAL_1A", 2819, EDT_Float32, 1}, + {"CAL_INFO.17.PHS_CAL", 2823, EDT_Float32, 4}, + {"CAL_INFO.18.MAX_CAL", 2839, EDT_Float32, 3}, + {"CAL_INFO.18.AVG_CAL", 2851, EDT_Float32, 3}, + {"CAL_INFO.18.AVG_VAL_1A", 2863, EDT_Float32, 1}, + {"CAL_INFO.18.PHS_CAL", 2867, EDT_Float32, 4}, + {"CAL_INFO.19.MAX_CAL", 2883, EDT_Float32, 3}, + {"CAL_INFO.19.AVG_CAL", 2895, EDT_Float32, 3}, + {"CAL_INFO.19.AVG_VAL_1A", 2907, EDT_Float32, 1}, + {"CAL_INFO.19.PHS_CAL", 2911, EDT_Float32, 4}, + {"CAL_INFO.20.MAX_CAL", 2927, EDT_Float32, 3}, + {"CAL_INFO.20.AVG_CAL", 2939, EDT_Float32, 3}, + {"CAL_INFO.20.AVG_VAL_1A", 2951, EDT_Float32, 1}, + {"CAL_INFO.20.PHS_CAL", 2955, EDT_Float32, 4}, + {"CAL_INFO.21.MAX_CAL", 2971, EDT_Float32, 3}, + {"CAL_INFO.21.AVG_CAL", 2983, EDT_Float32, 3}, + {"CAL_INFO.21.AVG_VAL_1A", 2995, EDT_Float32, 1}, + {"CAL_INFO.21.PHS_CAL", 2999, EDT_Float32, 4}, + {"CAL_INFO.22.MAX_CAL", 3015, EDT_Float32, 3}, + {"CAL_INFO.22.AVG_CAL", 3027, EDT_Float32, 3}, + {"CAL_INFO.22.AVG_VAL_1A", 3039, EDT_Float32, 1}, + {"CAL_INFO.22.PHS_CAL", 3043, EDT_Float32, 4}, + {"CAL_INFO.23.MAX_CAL", 3059, EDT_Float32, 3}, + {"CAL_INFO.23.AVG_CAL", 3071, EDT_Float32, 3}, + {"CAL_INFO.23.AVG_VAL_1A", 3083, EDT_Float32, 1}, + {"CAL_INFO.23.PHS_CAL", 3087, EDT_Float32, 4}, + {"CAL_INFO.24.MAX_CAL", 3103, EDT_Float32, 3}, + {"CAL_INFO.24.AVG_CAL", 3115, EDT_Float32, 3}, + {"CAL_INFO.24.AVG_VAL_1A", 3127, EDT_Float32, 1}, + {"CAL_INFO.24.PHS_CAL", 3131, EDT_Float32, 4}, + {"CAL_INFO.25.MAX_CAL", 3147, EDT_Float32, 3}, + {"CAL_INFO.25.AVG_CAL", 3159, EDT_Float32, 3}, + {"CAL_INFO.25.AVG_VAL_1A", 3171, EDT_Float32, 1}, + {"CAL_INFO.25.PHS_CAL", 3175, EDT_Float32, 4}, + {"CAL_INFO.26.MAX_CAL", 3191, EDT_Float32, 3}, + {"CAL_INFO.26.AVG_CAL", 3203, EDT_Float32, 3}, + {"CAL_INFO.26.AVG_VAL_1A", 3215, EDT_Float32, 1}, + {"CAL_INFO.26.PHS_CAL", 3219, EDT_Float32, 4}, + {"CAL_INFO.27.MAX_CAL", 3235, EDT_Float32, 3}, + {"CAL_INFO.27.AVG_CAL", 3247, EDT_Float32, 3}, + {"CAL_INFO.27.AVG_VAL_1A", 3259, EDT_Float32, 1}, + {"CAL_INFO.27.PHS_CAL", 3263, EDT_Float32, 4}, + {"CAL_INFO.28.MAX_CAL", 3279, EDT_Float32, 3}, + {"CAL_INFO.28.AVG_CAL", 3291, EDT_Float32, 3}, + {"CAL_INFO.28.AVG_VAL_1A", 3303, EDT_Float32, 1}, + {"CAL_INFO.28.PHS_CAL", 3307, EDT_Float32, 4}, + {"CAL_INFO.29.MAX_CAL", 3323, EDT_Float32, 3}, + {"CAL_INFO.29.AVG_CAL", 3335, EDT_Float32, 3}, + {"CAL_INFO.29.AVG_VAL_1A", 3347, EDT_Float32, 1}, + {"CAL_INFO.29.PHS_CAL", 3351, EDT_Float32, 4}, + {"CAL_INFO.30.MAX_CAL", 3367, EDT_Float32, 3}, + {"CAL_INFO.30.AVG_CAL", 3379, EDT_Float32, 3}, + {"CAL_INFO.30.AVG_VAL_1A", 3391, EDT_Float32, 1}, + {"CAL_INFO.30.PHS_CAL", 3395, EDT_Float32, 4}, + {"CAL_INFO.31.MAX_CAL", 3411, EDT_Float32, 3}, + {"CAL_INFO.31.AVG_CAL", 3423, EDT_Float32, 3}, + {"CAL_INFO.31.AVG_VAL_1A", 3435, EDT_Float32, 1}, + {"CAL_INFO.31.PHS_CAL", 3439, EDT_Float32, 4}, + {"CAL_INFO.32.MAX_CAL", 3455, EDT_Float32, 3}, + {"CAL_INFO.32.AVG_CAL", 3467, EDT_Float32, 3}, + {"CAL_INFO.32.AVG_VAL_1A", 3479, EDT_Float32, 1}, + {"CAL_INFO.32.PHS_CAL", 3483, EDT_Float32, 4}, + /*{"SPARE_16", 3499, EDT_UByte, 16},*/ + {"FIRST_LINE_TIME", 3515, EDT_MJD, 1}, + {"FIRST_LINE_TIE_POINTS.RANGE_SAMP_NUMS_FIRST", 3527, EDT_UInt32, 3}, + {"FIRST_LINE_TIE_POINTS.SLANT_RANGE_TIMES_FIRST", 3539, EDT_Float32, 3}, + {"FIRST_LINE_TIE_POINTS.INC_ANGLES_FIRST", 3551, EDT_Float32, 3}, + {"FIRST_LINE_TIE_POINTS.LATS_FIRST", 3563, EDT_Int32, 3}, + {"FIRST_LINE_TIE_POINTS.LONGS_FIRST", 3575, EDT_Int32, 3}, + {"MID_LINE_TIME", 3587, EDT_MJD, 1}, + {"MID_RANGE_LINE_NUMS", 3599, EDT_UInt32, 1}, + {"MID_LINE_TIE_POINTS.RANGE_SAMP_NUMS_MID", 3603, EDT_UInt32, 3}, + {"MID_LINE_TIE_POINTS.SLANT_RANGE_TIMES_MID", 3615, EDT_Float32, 3}, + {"MID_LINE_TIE_POINTS.INC_ANGLES_MID", 3627, EDT_Float32, 3}, + {"MID_LINE_TIE_POINTS.LATS_MID", 3639, EDT_Int32, 3}, + {"MID_LINE_TIE_POINTS.LONGS_MID", 3651, EDT_Int32, 3}, + {"LAST_LINE_TIME", 3663, EDT_MJD, 1}, + {"LAST_LINE_NUM", 3675, EDT_UInt32, 1}, + {"LAST_LINE_TIE_POINTS.RANGE_SAMP_NUMS_LAST", 3679, EDT_UInt32, 3}, + {"LAST_LINE_TIE_POINTS.SLANT_RANGE_TIMES_LAST", 3691, EDT_Float32, 3}, + {"LAST_LINE_TIE_POINTS.INC_ANGLES_LAST", 3703, EDT_Float32, 3}, + {"LAST_LINE_TIE_POINTS.LATS_LAST", 3715, EDT_Int32, 3}, + {"LAST_LINE_TIE_POINTS.LONGS_LAST", 3727, EDT_Int32, 3}, + {"SWST_OFFSET", 3739, EDT_Float32, 1}, + {"GROUND_RANGE_BIAS", 3743, EDT_Float32, 1}, + {"ELEV_ANGLE_BIAS", 3747, EDT_Float32, 1}, + {"IMAGETTE_RANGE_LEN", 3751, EDT_Float32, 1}, + {"IMAGETTE_AZ_LEN", 3755, EDT_Float32, 1}, + {"IMAGETTE_RANGE_RES", 3759, EDT_Float32, 1}, + {"GROUND_RES", 3763, EDT_Float32, 1}, + {"IMAGETTE_AZ_RES", 3767, EDT_Float32, 1}, + {"PLATFORM_ALT", 3771, EDT_Float32, 1}, + {"PLATFORM_VEL", 3775, EDT_Float32, 1}, + {"SLANT_RANGE", 3779, EDT_Float32, 1}, + {"CW_DRIFT", 3783, EDT_Float32, 1}, + {"WAVE_SUBCYCLE", 3787, EDT_UInt16, 1}, + {"EARTH_RADIUS", 3789, EDT_Float32, 1}, + {"SAT_HEIGHT", 3793, EDT_Float32, 1}, + {"FIRST_SAMPLE_SLANT_RANGE", 3797, EDT_Float32, 1}, + /*{"SPARE_17", 3801, EDT_UByte, 12},*/ + {"ELEVATION_PATTERN.SLANT_RANGE_TIME", 3813, EDT_Float32, 11}, + {"ELEVATION_PATTERN.ELEVATION_ANGLES", 3857, EDT_Float32, 11}, + {"ELEVATION_PATTERN.ANTENNA_PATTERN", 3901, EDT_Float32, 11}, + /*{"SPARE_18", 3945, EDT_UByte, 14},*/ + {NULL, 0, EDT_Unknown, 0} +}; + +static const EnvisatFieldDescr ASAR_WAVE_SQ_ADSR[] = { + {"ZERO_DOPPLER_TIME", 0, EDT_MJD, 1}, + {"ATTACH_FLAG", 12, EDT_UByte, 1}, + {"INPUT_MEAN_FLAG", 13, EDT_UByte, 1}, + {"INPUT_STD_DEV_FLAG", 14, EDT_UByte, 1}, + {"INPUT_GAPS_FLAG", 15, EDT_UByte, 1}, + {"INPUT_MISSING_LINES_FLAG", 16, EDT_UByte, 1}, + {"DOP_CEN_FLAG", 17, EDT_UByte, 1}, + {"DOP_AMB_FLAG", 18, EDT_UByte, 1}, + {"OUTPUT_MEAN_FLAG", 19, EDT_UByte, 1}, + {"OUTPUT_STD_DEV_FLAG", 20, EDT_UByte, 1}, + {"CHIRP_FLAG", 21, EDT_UByte, 1}, + {"MISSING_DATA_SETS_FLAG", 22, EDT_UByte, 1}, + {"INVALID_DOWNLINK_FLAG", 23, EDT_UByte, 1}, + /*{"SPARE_1", 24, EDT_UByte, 7},*/ + {"THRESH_CHIRP_BROADENING", 31, EDT_Float32, 1}, + {"THRESH_CHIRP_SIDELOBE", 35, EDT_Float32, 1}, + {"THRESH_CHIRP_ISLR", 39, EDT_Float32, 1}, + {"THRESH_INPUT_MEAN", 43, EDT_Float32, 1}, + {"EXP_INPUT_MEAN", 47, EDT_Float32, 1}, + {"THRESH_INPUT_STD_DEV", 51, EDT_Float32, 1}, + {"EXP_INPUT_STD_DEV", 55, EDT_Float32, 1}, + {"THRESH_DOP_CEN", 59, EDT_Float32, 1}, + {"THRESH_DOP_AMB", 63, EDT_Float32, 1}, + {"THRESH_OUTPUT_MEAN", 67, EDT_Float32, 1}, + {"EXP_OUTPUT_MEAN", 71, EDT_Float32, 1}, + {"THRESH_OUTPUT_STD_DEV", 75, EDT_Float32, 1}, + {"EXP_OUTPUT_STD_DEV", 79, EDT_Float32, 1}, + {"THRESH_INPUT_MISSING_LINES", 83, EDT_Float32, 1}, + {"THRESH_INPUT_GAPS", 87, EDT_Float32, 1}, + {"LINES_PER_GAPS", 91, EDT_UInt32, 1}, + /*{"SPARE_2", 95, EDT_UByte, 15},*/ + {"INPUT_MEAN", 110, EDT_Float32, 2}, + {"INPUT_STD_DEV", 118, EDT_Float32, 2}, + {"NUM_GAPS", 126, EDT_Float32, 1}, + {"NUM_MISSING_LINES", 130, EDT_Float32, 1}, + {"OUTPUT_MEAN", 134, EDT_Float32, 2}, + {"OUTPUT_STD_DEV", 142, EDT_Float32, 2}, + {"TOT_ERRORS", 150, EDT_UInt32, 1}, + /*{"SPARE_3", 154, EDT_UByte, 16},*/ + {"LAND_FLAG", 170, EDT_UByte, 1}, + {"LOOK_CONF_FLAG", 171, EDT_UByte, 1}, + {"INTER_LOOK_CONF_FLAG", 172, EDT_UByte, 1}, + {"AZ_CUTOFF_FLAG", 173, EDT_UByte, 1}, + {"AZ_CUTOFF_ITERATION_FLAG", 174, EDT_UByte, 1}, + {"PHASE_FLAG", 175, EDT_UByte, 1}, + /*{"SPARE_4", 176, EDT_UByte, 4},*/ + {"LOOK_CONF_THRESH", 180, EDT_Float32, 2}, + {"INTER_LOOK_CONF_THRESH", 188, EDT_Float32, 1}, + {"AZ_CUTOFF_THRESH", 192, EDT_Float32, 1}, + {"AZ_CUTOFF_ITERATIONS_THRESH", 196, EDT_UInt32, 1}, + {"PHASE_PEAK_THRESH", 200, EDT_Float32, 1}, + {"PHASE_CROSS_THRESH", 204, EDT_Float32, 1}, + /*{"SPARE_5", 208, EDT_UByte, 12},*/ + {"LOOK_CONF", 220, EDT_Float32, 1}, + {"INTER_LOOK_CONF", 224, EDT_Float32, 1}, + {"AZ_CUTOFF", 228, EDT_Float32, 1}, + {"PHASE_PEAK_CONF", 232, EDT_Float32, 1}, + {"PHASE_CROSS_CONF", 236, EDT_Float32, 1}, + /*{"SPARE_6", 240, EDT_UByte, 12},*/ + {NULL, 0, EDT_Unknown, 0} +}; + +/* --- MERIS record descriptors -------------------------------------------- */ +static const EnvisatFieldDescr MERIS_1P_QUALITY_ADSR[] = { + {"DSR_TIME", 0, EDT_MJD, 1}, + {"ATTACH_FLAG", 12, EDT_UByte, 1}, + {"RANGE_FLAG", 13, EDT_UInt16, 5}, + {"RANGE_BLIND_FLAG", 23, EDT_UInt16, 5}, + {NULL, 0, EDT_Unknown, 0} +}; + +static const EnvisatFieldDescr MERIS_1P_SCALING_FACTOR_GADS[] = { + {"SCALING_FACTOR_ALT", 0, EDT_Float32, 1}, + {"SCALING_FACTOR_ROUGH", 4, EDT_Float32, 1}, + {"SCALING_FACTOR_ZON_WIND", 8, EDT_Float32, 1}, + {"SCALING_FACTOR_MERR_WIND", 12, EDT_Float32, 1}, + {"SCALING_FACTOR_ATM_PRES", 16, EDT_Float32, 1}, + {"SCALING_FACTOR_OZONE", 20, EDT_Float32, 1}, + {"SCALING_FACTOR_REL_HUM", 24, EDT_Float32, 1}, + {"SCALING_FACTOR_RAD", 28, EDT_Float32, 15}, + {"GAIN_SETTINGS", 88, EDT_UByte, 80}, + {"SAMPLING_RATE", 168, EDT_UInt32, 1}, + {"SUN_SPECTRAL_FLUX", 172, EDT_Float32, 15}, + /*{"SPARE_1", 232, EDT_UByte, 60},*/ + {NULL, 0, EDT_Unknown, 0} +}; + +static const EnvisatFieldDescr MERIS_2P_QUALITY_ADSR[] = { + {"DSR_TIME", 0, EDT_MJD, 1}, + {"ATTACH_FLAG", 12, EDT_UByte, 1}, + {"PERC_WATER_ABS_AERO", 13, EDT_UByte, 1}, + {"PERC_WATER", 14, EDT_UByte, 1}, + {"PERC_DDV_LAND", 15, EDT_UByte, 1}, + {"PERC_LAND", 16, EDT_UByte, 1}, + {"PERC_CLOUD", 17, EDT_UByte, 1}, + {"PERC_LOW_POLY_PRESS", 18, EDT_UByte, 1}, + {"PERC_LOW_NEURAL_PRESS", 19, EDT_UByte, 1}, + {"PERC_OUT_RAN_INP_WVAPOUR", 20, EDT_UByte, 1}, + {"PER_OUT_RAN_OUTP_WVAPOUR", 21, EDT_UByte, 1}, + {"PERC_OUT_RANGE_INP_CL", 22, EDT_UByte, 1}, + {"PERC_OUT_RAN_OUTP_CL", 23, EDT_UByte, 1}, + {"PERC_IN_RAN_INP_LAND", 24, EDT_UByte, 1}, + {"PERC_OUT_RAN_OUTP_LAND", 25, EDT_UByte, 1}, + {"PERC_OUT_RAN_INP_OCEAN", 26, EDT_UByte, 1}, + {"PERC_OUT_RAN_OUTP_OCEAN", 27, EDT_UByte, 1}, + {"PERC_OUT_RAN_INP_CASE1", 28, EDT_UByte, 1}, + {"PERC_OUT_RAN_OUTP_CASE1", 29, EDT_UByte, 1}, + {"PERC_OUT_RAN_INP_CASE2", 30, EDT_UByte, 1}, + {"PERC_OUT_RAN_OUTP_CASE2", 31, EDT_UByte, 1}, + {NULL, 0, EDT_Unknown, 0} +}; + +static const EnvisatFieldDescr MERIS_2P_SCALING_FACTOR_GADS[] = { + {"SCALING_FACTOR_ALT", 0, EDT_Float32, 1}, + {"SCALING_FACTOR_ROUGH", 4, EDT_Float32, 1}, + {"SCALING_FACTOR_ZON_WIND", 8, EDT_Float32, 1}, + {"SCALING_FACTOR_MERR_WIND", 12, EDT_Float32, 1}, + {"SCALING_FACTOR_ATM_PRES", 16, EDT_Float32, 1}, + {"SCALING_FACTOR_OZONE", 20, EDT_Float32, 1}, + {"SCALING_FACTOR_REL_HUMID", 24, EDT_Float32, 1}, + {"SCALING_FACTOR_REFLEC", 28, EDT_Float32, 13}, + {"SCALING_FACTOR_ALGAL_PIG_IND", 80, EDT_Float32, 1}, + {"SCALING_FACTOR_YELLOW_SUBS", 84, EDT_Float32, 1}, + {"SCALING_FACTOR_SUSP_SED", 88, EDT_Float32, 1}, + {"SCALING_FACTOR_AERO_EPSILON", 92, EDT_Float32, 1}, + {"SCALING_FACTOR_AER_OPT_THICK", 96, EDT_Float32, 1}, + {"SCALING_FACTOR_CL_OPT_THICK", 100, EDT_Float32, 1}, + {"SCALING_FACTOR_SURF_PRES", 104, EDT_Float32, 1}, + {"SCALING_FACTOR_WVAPOUR", 108, EDT_Float32, 1}, + {"SCALING_FACTOR_PHOTOSYN_RAD", 112, EDT_Float32, 1}, + {"SCALING_FACTOR_TOA_VEG", 116, EDT_Float32, 1}, + {"SCALING_FACTOR_BOA_VEG", 120, EDT_Float32, 1}, + {"SCALING_FACTOR_CLOUD_ALBEDO", 124, EDT_Float32, 1}, + {"SCALING_FACTOR_CLOUD_TOP_PRESS", 128, EDT_Float32, 1}, + {"OFF_REFLEC", 132, EDT_Float32, 13}, + {"OFFSET_ALGAL", 184, EDT_Float32, 1}, + {"OFFSET_YELLOW_SUBS", 188, EDT_Float32, 1}, + {"OFFSET_TOTAL_SUSP", 192, EDT_Float32, 1}, + {"OFFSET_AERO_EPSILON", 196, EDT_Float32, 1}, + {"OFFSET_AER_OPT_THICK", 200, EDT_Float32, 1}, + {"OFFSET_CL_OPT_THICK", 204, EDT_Float32, 1}, + {"OFFSET_SURF_PRES", 208, EDT_Float32, 1}, + {"OFFSET_WVAPOUR", 212, EDT_Float32, 1}, + {"OFFSET_PHOTOSYN_RAD", 216, EDT_Float32, 1}, + {"OFFSET_TOA_VEG", 220, EDT_Float32, 1}, + {"OFFSET_BOA_VEG", 224, EDT_Float32, 1}, + {"OFFSET_CLOUD_ALBEDO", 228, EDT_Float32, 1}, + {"OFFSET_CLOUD_TOP_PRESS", 232, EDT_Float32, 1}, + {"GAIN_SETTINGS", 236, EDT_UByte, 80}, + {"SAMPLING_RATE", 316, EDT_UInt32, 1}, + {"SUN_SPECTRAL_FLUX", 320, EDT_Float32, 15}, + {"SCALING_FACTOR_RECT_REFL_NIR", 380, EDT_Float32, 1}, + {"OFFSET_RECT_REFL_NIR", 384, EDT_Float32, 1}, + {"SCALING_FACTOR_RECT_REFL_RED", 388, EDT_Float32, 1}, + {"OFFSET_RECT_REFL_RED", 392, EDT_Float32, 1}, + /*{"SPARE_1", 396, EDT_UByte, 44},*/ + {NULL, 0, EDT_Unknown, 0} +}; + +static const EnvisatFieldDescr MERIS_2P_C_SCALING_FACTOR_GADS[] = { + {"SCALING_FACTOR_CLOUD_OPT_THICK", 0, EDT_Float32, 1}, + {"SCALING_FACTOR_CLOUD_TOP_PRESS", 4, EDT_Float32, 1}, + {"SCALING_FACTOR_WVAPOUR", 8, EDT_Float32, 1}, + {"OFFSET_CL_OPT_THICK", 12, EDT_Float32, 1}, + {"OFFSET_CLOUD_TOP_PRESS", 16, EDT_Float32, 1}, + {"OFFSET_WVAPOUR", 20, EDT_Float32, 1}, + /*{"SPARE_1", 24, EDT_UByte, 52},*/ + {NULL, 0, EDT_Unknown, 0} +}; + +static const EnvisatFieldDescr MERIS_2P_V_SCALING_FACTOR_GADS[] = { + {"SCALING_FACTOR_TOA_VEGETATION_INDEX", 0, EDT_Float32, 1}, + {"SCALING_FACTOR_BOA_VEGETATION_INDEX", 4, EDT_Float32, 1}, + {"OFFSET_TOA_VEGETAYION_INDEX", 8, EDT_Float32, 1}, + {"OFFSET_BOA_VEGETAYION_INDEX", 12, EDT_Float32, 1}, + /*{"SPARE_1", 16, EDT_UByte, 60},*/ + {NULL, 0, EDT_Unknown, 0} +}; + + +static const EnvisatRecordDescr aASAR_Records[] = { + {"MDS1 ANTENNA ELEV PATT ADS", ASAR_ANTENNA_ELEV_PATT_ADSR}, + {"MDS2 ANTENNA ELEV PATT ADS", ASAR_ANTENNA_ELEV_PATT_ADSR}, + {"CHIRP PARAMS ADS", ASAR_CHIRP_PARAMS_ADSR}, + {"DOP CENTROID COEFFS ADS", ASAR_DOP_CENTROID_COEFFS_ADSR}, + /*{"GEOLOCATION GRID ADS", ASAR_GEOLOCATION_GRID_ADSR},*/ + {"MAIN PROCESSING PARAMS ADS", ASAR_MAIN_PROCESSING_PARAMS_ADSR}, + {"MAP PROJECTION GADS", ASAR_MAP_PROJECTION_GADS}, + {"MDS1 SQ ADS", ASAR_SQ_ADSR}, + {"MDS2 SQ ADS", ASAR_SQ_ADSR}, + {"SR GR ADS", ASAR_SR_GR_ADSR}, + /* WAVE */ + /*{"GEOLOCATION ADS", ASAR_GEOLOCATION_ADSR},*/ + {"PROCESSING PARAMS ADS", ASAR_PROCESSING_PARAMS_ADSR}, + {"SQ ADS", ASAR_WAVE_SQ_ADSR}, + {NULL, NULL} +}; + +static const EnvisatRecordDescr aMERIS_1P_Records[] = { + {"Quality ADS", MERIS_1P_QUALITY_ADSR}, + {"Scaling Factor GADS", MERIS_1P_SCALING_FACTOR_GADS}, + {NULL, NULL} +}; + +static const EnvisatRecordDescr aMERIS_2P_Records[] = { + {"Quality ADS", MERIS_2P_QUALITY_ADSR}, + {"Scaling Factor GADS", MERIS_2P_SCALING_FACTOR_GADS}, + {NULL, NULL} +}; + +static const EnvisatRecordDescr aMERIS_2P_C_Records[] = { + {"Quality ADS", MERIS_2P_QUALITY_ADSR}, + {"Scaling Factor GADS", MERIS_2P_C_SCALING_FACTOR_GADS}, + {NULL, NULL} +}; + +static const EnvisatRecordDescr aMERIS_2P_V_Records[] = { + {"Quality ADS", MERIS_2P_QUALITY_ADSR}, + {"Scaling Factor GADS", MERIS_2P_V_SCALING_FACTOR_GADS}, + {NULL, NULL} +}; + +const EnvisatRecordDescr* EnvisatFile_GetRecordDescriptor( + const char* pszProduct, const char* pszDataset) +{ + const EnvisatRecordDescr *paRecords = NULL; + const EnvisatRecordDescr *pRecordDescr = NULL; + int nLen; + + if( EQUALN(pszProduct,"ASA",3) ) + paRecords = aASAR_Records; + else if( EQUALN(pszProduct,"MER",3) ) + { + if ( EQUALN(pszProduct + 6,"C_2P",4) ) + paRecords = aMERIS_2P_C_Records; + else if ( EQUALN(pszProduct + 6,"V_2P",4) ) + paRecords = aMERIS_2P_V_Records; + else if ( EQUALN(pszProduct + 8,"1P",2) ) + paRecords = aMERIS_1P_Records; + else if ( EQUALN(pszProduct + 8,"2P",2) ) + paRecords = aMERIS_2P_Records; + else + return NULL; + } + else if( EQUALN(pszProduct,"SAR",3) ) + /* ERS products in ENVISAT format have the same records of ASAR ones */ + paRecords = aASAR_Records; + else + return NULL; + + /* strip trailing spaces */ + for( nLen = strlen(pszDataset); nLen && pszDataset[nLen-1] == ' '; --nLen ); + + pRecordDescr = paRecords; + while ( pRecordDescr->szName != NULL ) + { + if ( EQUALN(pRecordDescr->szName, pszDataset, nLen) ) + return pRecordDescr; + else + ++pRecordDescr; + } + + return NULL; +} + +CPLErr EnvisatFile_GetFieldAsString(const void *pRecord, int nRecLen, + const EnvisatFieldDescr* pField, char *szBuf) +{ + int i, nOffset = 0; + const void *pData; + + if ( pField->nOffset >= nRecLen ) + { + CPLDebug( "EnvisatDataset", + "Field offset (%d) is greater than the record length (%d).", + pField->nOffset, nRecLen ); + + return CE_Failure; + } + + pData = (GByte*)pRecord + pField->nOffset; + + szBuf[0] = '\0'; + + switch (pField->eType) + { + case EDT_Char: + memcpy((void*)szBuf, pData, pField->nCount); + szBuf[pField->nCount] = '\0'; + break; + case EDT_UByte: + case EDT_SByte: + for (i = 0; i < pField->nCount; ++i) + { + if (i > 0) + szBuf[nOffset++] = ' '; + nOffset += sprintf(szBuf + nOffset, "%d", + ((const char*)pData)[i]); + } + break; + case EDT_Int16: + for (i = 0; i < pField->nCount; ++i) + { + if (i > 0) + szBuf[nOffset++] = ' '; + nOffset += sprintf(szBuf + nOffset, "%d", + CPL_MSBWORD16(((const GInt16*)pData)[i])); + } + break; + case EDT_UInt16: + for (i = 0; i < pField->nCount; ++i) + { + if (i > 0) + szBuf[nOffset++] = ' '; + nOffset += sprintf(szBuf + nOffset, "%d", + CPL_MSBWORD16(((const GUInt16*)pData)[i])); + } + break; + case EDT_Int32: + for (i = 0; i < pField->nCount; ++i) + { + if (i > 0) + szBuf[nOffset++] = ' '; + nOffset += sprintf(szBuf + nOffset, "%d", + CPL_MSBWORD32(((const GInt32*)pData)[i])); + } + break; + case EDT_UInt32: + for (i = 0; i < pField->nCount; ++i) + { + if (i > 0) + szBuf[nOffset++] = ' '; + nOffset += sprintf(szBuf + nOffset, "%d", + CPL_MSBWORD32(((const GUInt32*)pData)[i])); + } + break; + case EDT_Float32: + for (i = 0; i < pField->nCount; ++i) + { + float fValue = ((const float*)pData)[i]; +#ifdef CPL_LSB + CPL_SWAP32PTR( &fValue ); +#endif + + if (i > 0) + szBuf[nOffset++] = ' '; + nOffset += CPLsprintf(szBuf + nOffset, "%f", fValue); + } + break; + case EDT_Float64: + for (i = 0; i < pField->nCount; ++i) + { + double dfValue = ((const double*)pData)[i]; +#ifdef CPL_LSB + CPL_SWAPDOUBLE( &dfValue ); +#endif + if (i > 0) + szBuf[nOffset++] = ' '; + nOffset += CPLsprintf(szBuf + nOffset, "%f", dfValue); + } + break; +/* + case EDT_CInt16: + for (i = 0; i < pField->nCount; ++i) + { + if (i > 0) + szBuf[nOffset++] = ' '; + nOffset += sprintf(szBuf + nOffset, "(%d, %d)", + CPL_MSBWORD16(((const GInt16*)pData)[2 * i]), + CPL_MSBWORD16(((const GInt16*)pData)[2 * i+1])); + } + break; + case EDT_CInt32: + for (i = 0; i < pField->nCount; ++i) + { + if (i > 0) + szBuf[nOffset++] = ' '; + nOffset += sprintf(szBuf + nOffset, "(%d, %d)", + CPL_MSBWORD32(((const GInt32*)pData)[2 * i]), + CPL_MSBWORD32(((const GInt32*)pData)[2 * i+1])); + } + break; + case EDT_CFloat32: + for (i = 0; i < pField->nCount; ++i) + { + float fReal = ((const float*)pData)[2 * i]; + float fImag = ((const float*)pData)[2 * i + 1]; +#ifdef CPL_LSB + CPL_SWAP32PTR( &fReal ); + CPL_SWAP32PTR( &fImag ); +#endif + if (i > 0) + szBuf[nOffset++] = ' '; + nOffset += CPLsprintf(szBuf + nOffset, "(%f, %f)", fReal, fImag); + } + break; + case EDT_CFloat64: + for (i = 0; i < pField->nCount; ++i) + { + double dfReal = ((const double*)pData)[2 * i]; + double dfImag = ((const double*)pData)[2 * i + 1]; +#ifdef CPL_LSB + CPL_SWAPDOUBLE( &dfReal ); + CPL_SWAPDOUBLE( &dfImag ); +#endif + if (i > 0) + szBuf[nOffset++] = ' '; + nOffset += CPLsprintf(szBuf + nOffset, "(%f, %f)", dfReal, dfImag); + } + break; +*/ + case EDT_MJD: + CPLAssert(pField->nCount == 1); + { + GInt32 days; + GUInt32 seconds, microseconds; + + days = CPL_MSBWORD32(((const GInt32*)pData)[0]); + seconds = CPL_MSBWORD32(((const GUInt32*)pData)[1]); + microseconds = CPL_MSBWORD32(((const GUInt32*)pData)[2]); + + sprintf(szBuf, "%d, %d, %d", days, seconds, microseconds); + } + break; + default: + CPLDebug( "EnvisatDataset", + "Unabe to convert '%s' field to string: " + "unsecpected data type '%d'.", + pField->szName, pField->eType ); + return CE_Failure; + } + + return CE_None; +} diff --git a/bazaar/plugin/gdal/frmts/envisat/records.h b/bazaar/plugin/gdal/frmts/envisat/records.h new file mode 100644 index 000000000..4eb8ae25e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/records.h @@ -0,0 +1,85 @@ +/****************************************************************************** + * $Id: records.h 22488 2011-06-02 17:04:47Z rouault $ + * + * Project: APP ENVISAT Support + * Purpose: Low Level Envisat file access (read/write) API. + * Author: Antonio Valentino + * + ****************************************************************************** + * Copyright (c) 2011, Antonio Valentino + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __RECORDS_H__ +#define __RECORDS_H__ + +#include "gdal.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#define MJD_FIELD_SIZE 12 + +/*! Field data types */ +typedef enum { + /*! Unknown or unspecified type */ EDT_Unknown = GDT_Unknown, + /*! Eight bit unsigned integer */ EDT_UByte = GDT_Byte, + /*! Eight bit signed integer */ EDT_SByte = GDT_TypeCount + 0, + /*! Sixteen bit unsigned integer */ EDT_UInt16 = GDT_UInt16, + /*! Sixteen bit signed integer */ EDT_Int16 = GDT_Int16, + /*! Thirty two bit unsigned integer */ EDT_UInt32 = GDT_UInt32, + /*! Thirty two bit signed integer */ EDT_Int32 = GDT_Int32, + /*! Thirty two bit floating point */ EDT_Float32 = GDT_Float32, + /*! Sixty four bit floating point */ EDT_Float64 = GDT_Float64, + /*! Complex Int16 */ EDT_CInt16 = GDT_CInt16, + /*! Complex Int32 */ EDT_CInt32 = GDT_CInt32, + /*! Complex Float32 */ EDT_CFloat32 = GDT_CFloat32, + /*! Complex Float64 */ EDT_CFloat64 = GDT_CFloat64, + /*! Modified Julian Dated */ EDT_MJD = GDT_TypeCount + 1, + /*! ASCII characters */ EDT_Char = GDT_TypeCount + 2, + EDT_TypeCount = GDT_TypeCount + 3 /* maximum type # + 1 */ +} EnvisatDataType; + +typedef struct { + const char* szName; + const int nOffset; + const EnvisatDataType eType; + const int nCount; +} EnvisatFieldDescr; + +typedef struct { + const char* szName; + const EnvisatFieldDescr *pFields; +} EnvisatRecordDescr; + +const EnvisatRecordDescr* EnvisatFile_GetRecordDescriptor(const char* pszProduct, + const char* pszDataset); + +CPLErr EnvisatFile_GetFieldAsString(const void*, int, const EnvisatFieldDescr*, char*); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __RECORDS_H__ */ diff --git a/bazaar/plugin/gdal/frmts/envisat/timedelta.hpp b/bazaar/plugin/gdal/frmts/envisat/timedelta.hpp new file mode 100644 index 000000000..e142a0024 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/timedelta.hpp @@ -0,0 +1,221 @@ +/****************************************************************************** + * $Id: timedelta.hpp 27098 2014-03-27 00:16:11Z rouault $ + * + * Project: APP ENVISAT Support + * Purpose: time difference class for handling of Envisat MJD time + * Author: Martin Paces, martin.paces@eox.at + * + ****************************************************************************** + * Copyright (c) 2013, EOX IT Services, GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#ifndef timedelta_hpp +#define timedelta_hpp +/* + * TimeDelta class represents the time difference. It is used to + * hold the Envisat MJD (Modified Julian Date - which is time + * since 2000-01-01T00:00:00.000000Z) + */ + +class TimeDelta +{ + + private: + + int days ; /* number of days */ + int secs ; /* number of seconds since day start */ + int usecs ; /* number of micro sec. since second start */ + + /* SETTERS */ + + /* set object using number of days, seconds and micro-seconds */ + inline void set( int days , int secs , int usecs ) + { + int tmp0 , tmp1 ; + /* overflow check with proper handling of negative values */ + /* note that division and modulo for negative values is impl.dependent */ + + secs += ( tmp0 = usecs>=0 ? usecs/1000000 : -1-((-usecs)/1000000) ) ; + days += ( tmp1 = secs>=0 ? secs/86400 : -1-((-secs)/86400) ) ; + + this->usecs = usecs - 1000000*tmp0 ; + this->secs = secs - 86400*tmp1 ; + this->days = days ; + } + + /* set object from floating point number of seconds */ + inline void fromSeconds( double secs ) + { + int _days = (int)( secs / 86400 ) ; + int _secs = (int)( secs - 86400*_days ) ; + int _uscs = (int)(( secs - ((int)secs) )*1e6) ; + + this->set( _days , _secs , _uscs ) ; + } + + public: + + /* CONSTRUCTORS */ + TimeDelta( void ) : days(0), secs(0), usecs(0) {} + + /* construct object using number of days, seconds and micro-seconds */ + TimeDelta( int days , int secs , int usecs ) + { + this->set( days, secs, usecs ) ; + } + + /* construct object from floating point number of seconds */ + TimeDelta( double secs ) + { + this->fromSeconds( secs ) ; + } + + /* GETTERS */ + + inline int getDays( void ) const + { + return this->days ; + } + + inline int getSeconds( void ) const + { + return this->secs ; + } + + inline int getMicroseconds( void ) const + { + return this->usecs ; + } + + /* convert to seconds - can handle safely at least 250 years dif. */ + /* ... before loosing the microsecond precision */ + inline operator double( void ) const + { + return (this->days*86400.0) + this->secs + (this->usecs*1e-6) ; + } + + + /* OPERATORS */ + + /* difference */ + inline TimeDelta operator -( const TimeDelta & that ) const + { + return TimeDelta( this->days - that.days, this->secs - that.secs, + this->usecs - that.usecs ) ; + } + + /* addition */ + inline TimeDelta operator +( const TimeDelta & that ) const + { + return TimeDelta( this->days + that.days, this->secs + that.secs, + this->usecs + that.usecs ) ; + } + + /* division */ + inline double operator /( const TimeDelta & that ) const + { + return ( (double)*this / (double)that ) ; + } + + /* integer multiplication */ + inline TimeDelta operator *( const int i ) const + { + return TimeDelta( i*this->days, i*this->secs, i*this->usecs ) ; + } + + /* float multiplication */ + inline TimeDelta operator *( const double f ) const + { + return TimeDelta( f * (double)*this ) ; + } + + /* comparisons operators */ + + inline bool operator ==( const TimeDelta & that ) const + { + return ( (this->usecs == that.usecs)&&(this->secs == that.secs)&& + (this->days == that.days) ) ; + } + + + inline bool operator >( const TimeDelta & that ) const + { + return (this->days > that.days) + ||( + (this->days == that.days) + &&( + (this->secs > that.secs) + ||( + (this->secs == that.secs) + &&(this->usecs > that.usecs) + ) + ) + ) ; + } + + inline bool operator <( const TimeDelta & that ) const + { + return (this->days < that.days) + ||( + (this->days == that.days) + &&( + (this->secs < that.secs) + ||( + (this->secs == that.secs) + &&(this->usecs < that.usecs) + ) + ) + ) ; + } + + + inline bool operator !=( const TimeDelta & that ) const + { + return !( *this == that ) ; + } + + inline bool operator >=( const TimeDelta & that ) const + { + return !( *this < that ) ; + } + + inline bool operator <=( const TimeDelta & that ) const + { + return !( *this > that ) ; + } + +} ; + +/* +#include + +std::ostream & operator<<( std::ostream & out, const TimeDelta & td ) +{ + + out << "TimeDelta(" << td.getDays() + << "," << td.getSeconds() + << "," << td.getMicroseconds() << ")" ; + + return out ; +} +*/ + +#endif /*timedelta_hpp*/ diff --git a/bazaar/plugin/gdal/frmts/envisat/unwrapgcps.cpp b/bazaar/plugin/gdal/frmts/envisat/unwrapgcps.cpp new file mode 100644 index 000000000..7cceb81d8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/envisat/unwrapgcps.cpp @@ -0,0 +1,182 @@ +/****************************************************************************** + * $Id: unwrapgcps.cpp 27098 2014-03-27 00:16:11Z rouault $ + * + * Project: APP ENVISAT Support + * Purpose: GCPs Unwrapping for products crossing the WGS84 date-line + * Author: Martin Paces martin.paces@eox.at + * + ****************************************************************************** + * Copyright (c) 2013, EOX IT Services, GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal.h" +#include +#include + +// number of histogram bins (36 a 10dg) +#define NBIN 36 +// number of empty bins to guess the flip-point +#define NEMPY 7 + +// WGS84 bounds +#define XMIN (-180.0) +#define XMAX (+180.0) +#define XDIF (+360.0) +#define XCNT (0.0) + +// max. allowed longitude extent of the GCP set +#define XLIM (XDIF*(1.0-NEMPY*(1.0/NBIN))) + + +// The algoright is based on assumption that the unwrapped +// GCPs ('flipped' values) have smaller extent along the longitude. +// We further assume that the lenght of the striplines is limitted +// to one orbit and does not exceeded given limit along the longitude, +// e.i., the wrapped-arround coordinates have significantly larger +// extent the unwrapped. If the smaller extend exceedes the limit +// the original tiepoints are returned. + +static double _suggest_flip_point( const int cnt, GDAL_GCP *gcp ) +{ + // the histogram array - it is expected to fit the stack + int hist[NBIN] ; + + // reset the histogram counters + for( int i = 0 ; i < NBIN ; i++ ) hist[i] = 0 ; + + // accumulate the histogram + for( int i = 0 ; i < cnt ; i++ ) + { + double x = (gcp[i].dfGCPX-XMIN)/XDIF ; + int idx = (int)(NBIN*(x-floor(x))) ; + + // the lattitudes should lay in the +/-180 bounds + // although it should never happen we check the outliers + if (idx < 0) idx = 0 ; + if (idx >= NBIN ) idx = NBIN-1; + + hist[idx] += 1 ; + } + + // find a middle of at least NEMPTY consecutive empty bins and get its middle + int i0 = -1 , i1 = -1 , last_is_empty = 0 ; + for( int i = 0 ; i < (2*NBIN-1) ; i++ ) + { + if ( 0 == hist[i%NBIN] ) // empty + { + if ( !last_is_empty ) // re-start counter + { + i0 = i ; + last_is_empty = 1 ; + } + } + else // non-empty + { + if ( last_is_empty ) + { + i1 = i ; + last_is_empty = 0 ; + + // if the segment is long enough -> terminate + if (( i1 - i0 )>=NEMPY) break ; + } + } + } + + // if all full or all empty the returning default value + if ( i1 < 0 ) return XCNT ; + + // return the flip-centre + + double tmp = ((i1-i0)*0.5+i0)/((float)NBIN) ; + + return (tmp-floor(tmp))*XDIF + XMIN ; +} + + +void EnvisatUnwrapGCPs( int cnt, GDAL_GCP *gcp ) +{ + if ( cnt < 1 ) return ; + + // suggest right flip-point + double x_flip = _suggest_flip_point( cnt, gcp ); + + // find the limits allong the longitude (x) for flipped and unflipped values + + int cnt_flip = 0 ; // flipped values' counter + double x0_dif , x1_dif ; + + { + double x0_min, x0_max, x1_min, x1_max ; + + { + double x0 = gcp[0].dfGCPX ; + int flip = (x0>x_flip) ; + x0_min = x0_max = x0 ; + x1_min = x1_max = x0 - flip*XDIF ; + cnt_flip += flip ; // count the flipped values + } + + for ( int i = 1 ; i < cnt ; ++i ) + { + double x0 = gcp[i].dfGCPX ; + int flip = (x0>x_flip) ; + double x1 = x0 - flip*XDIF ; // flipped value + cnt_flip += flip ; // count the flipped values + + if ( x0 > x0_max ) x0_max = x0 ; + if ( x0 < x0_min ) x0_min = x0 ; + if ( x1 > x1_max ) x1_max = x1 ; + if ( x1 < x1_min ) x1_min = x1 ; + } + + x0_dif = x0_max - x0_min ; + x1_dif = x1_max - x1_min ; + } + + // in case all values either flipped or non-flipped + // nothing is to be done + if (( cnt_flip == 0 ) || ( cnt_flip == cnt )) return ; + + // check whether we need to split the segment + // i.e., segment is too long decide the best option + + if (( x0_dif > XLIM ) && ( x1_dif > XLIM )) + { + // this should not happen + // we give-up and return the original tie-point set + + CPLError(CE_Warning,CPLE_AppDefined,"GCPs' set is too large" + " to perform the unwrapping! The unwrapping is not performed!"); + + return ; + } + else if ( x1_dif < x0_dif ) + { + // flipped GCPs' set has smaller extent -> unwrapping is performed + for ( int i = 1 ; i < cnt ; ++i ) + { + double x0 = gcp[i].dfGCPX ; + + gcp[i].dfGCPX = x0 - (x0>XCNT)*XDIF ; + } + } +} diff --git a/bazaar/plugin/gdal/frmts/epsilon/GNUmakefile b/bazaar/plugin/gdal/frmts/epsilon/GNUmakefile new file mode 100644 index 000000000..2bc8853ec --- /dev/null +++ b/bazaar/plugin/gdal/frmts/epsilon/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = epsilondataset.o + +CPPFLAGS := $(EPSILON_INCLUDE) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/epsilon/epsilondataset.cpp b/bazaar/plugin/gdal/frmts/epsilon/epsilondataset.cpp new file mode 100644 index 000000000..4e58529c2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/epsilon/epsilondataset.cpp @@ -0,0 +1,1047 @@ +/****************************************************************************** + * $Id: epsilondataset.cpp 28738 2015-03-16 17:20:21Z rouault $ + * + * Project: GDAL Epsilon driver + * Purpose: Implement GDAL Epsilon support using Epsilon library + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "epsilon.h" +#include "gdal_pam.h" + +CPL_CVSID("$Id: epsilondataset.cpp 28738 2015-03-16 17:20:21Z rouault $"); + +#define RASTERLITE_WAVELET_HEADER "StartWaveletsImage$$" +#define RASTERLITE_WAVELET_FOOTER "$$EndWaveletsImage" + +#define BLOCK_DATA_MAX_SIZE MAX(EPS_MAX_GRAYSCALE_BUF, EPS_MAX_TRUECOLOR_BUF) + +class EpsilonRasterBand; + +typedef struct +{ + int x; + int y; + int w; + int h; + vsi_l_offset offset; +} BlockDesc; + +#ifdef I_WANT_COMPATIBILITY_WITH_EPSILON_0_8_1 +#define GET_FIELD(hdr, field) \ + (hdr.block_type == EPS_GRAYSCALE_BLOCK) ? hdr.gs.field : hdr.tc.field +#else +#define GET_FIELD(hdr, field) \ + (hdr.block_type == EPS_GRAYSCALE_BLOCK) ? hdr.hdr_data.gs.field : hdr.hdr_data.tc.field +#endif + +/************************************************************************/ +/* ==================================================================== */ +/* EpsilonDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class EpsilonDataset : public GDALPamDataset +{ + friend class EpsilonRasterBand; + + VSILFILE* fp; + vsi_l_offset nFileOff; + + GByte* pabyFileBuf; + int nFileBufMaxSize; + int nFileBufCurSize; + int nFileBufOffset; + int bEOF; + int bError; + + GByte* pabyBlockData; + int nBlockDataSize; + vsi_l_offset nStartBlockFileOff; + + int bRegularTiling; + + int nBlocks; + BlockDesc* pasBlocks; + + int nBufferedBlock; + GByte* pabyRGBData; + + void Seek(vsi_l_offset nPos); + int GetNextByte(); + int GetNextBlockData(); + int ScanBlocks(int *pnBands); + + public: + EpsilonDataset(); + virtual ~EpsilonDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* EpsilonRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class EpsilonRasterBand : public GDALPamRasterBand +{ + public: + EpsilonRasterBand(EpsilonDataset* poDS, int nBand); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); +}; + +/************************************************************************/ +/* EpsilonDataset() */ +/************************************************************************/ + +EpsilonDataset::EpsilonDataset() +{ + fp = NULL; + nFileOff = 0; + + pabyFileBuf = NULL; + nFileBufMaxSize = 0; + nFileBufCurSize = 0; + nFileBufOffset = 0; + bEOF = FALSE; + bError = FALSE; + + pabyBlockData = NULL; + nBlockDataSize = 0; + nStartBlockFileOff = 0; + + bRegularTiling = FALSE; + + nBlocks = 0; + pasBlocks = NULL; + + nBufferedBlock = -1; + pabyRGBData = NULL; +} + +/************************************************************************/ +/* ~EpsilonDataset() */ +/************************************************************************/ + +EpsilonDataset::~EpsilonDataset() +{ + if (fp) + VSIFCloseL(fp); + VSIFree(pabyFileBuf); + VSIFree(pasBlocks); + CPLFree(pabyRGBData); +} + +/************************************************************************/ +/* EpsilonRasterBand() */ +/************************************************************************/ + +EpsilonRasterBand::EpsilonRasterBand(EpsilonDataset* poDS, int nBand) +{ + this->poDS = poDS; + this->nBand = nBand; + this->eDataType = GDT_Byte; + this->nBlockXSize = poDS->pasBlocks[0].w; + this->nBlockYSize = poDS->pasBlocks[0].h; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp EpsilonRasterBand::GetColorInterpretation() +{ + EpsilonDataset* poGDS = (EpsilonDataset*) poDS; + if (poGDS->nBands == 1) + { + return GCI_GrayIndex; + } + else + { + if (nBand == 1) + return GCI_RedBand; + else if (nBand == 2) + return GCI_GreenBand; + else + return GCI_BlueBand; + } +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr EpsilonRasterBand::IReadBlock( int nBlockXOff, + int nBlockYOff, void * pImage) +{ + EpsilonDataset* poGDS = (EpsilonDataset*) poDS; + + //CPLDebug("EPSILON", "IReadBlock(nBand=%d,nBlockXOff=%d,nBlockYOff=%d)", + // nBand, nBlockXOff, nBlockYOff); + + int nBlocksPerRow = (poGDS->nRasterXSize + nBlockXSize - 1) / nBlockXSize; + int nBlock = nBlockXOff + nBlockYOff * nBlocksPerRow; + + BlockDesc* psDesc = &poGDS->pasBlocks[nBlock]; +#ifdef DEBUG + int nBlocksPerColumn = (poGDS->nRasterYSize + nBlockYSize - 1) / nBlockYSize; + CPLAssert(psDesc->x == nBlockXOff * nBlockXSize); + CPLAssert(psDesc->y == nBlockYOff * nBlockYSize); + CPLAssert(psDesc->w == (nBlockXOff < nBlocksPerRow - 1) ? + nBlockXSize : poGDS->nRasterXSize - psDesc->x); + CPLAssert(psDesc->h == (nBlockYOff < nBlocksPerColumn - 1) ? + nBlockYSize : poGDS->nRasterYSize - psDesc->y); +#endif + + poGDS->Seek(psDesc->offset); + + if (!poGDS->GetNextBlockData()) + { + memset(pImage, 0, nBlockXSize * nBlockYSize); + return CE_Failure; + } + + eps_block_header hdr; + if (eps_read_block_header (poGDS->pabyBlockData, + poGDS->nBlockDataSize, &hdr) != EPS_OK) + { + CPLError(CE_Warning, CPLE_AppDefined, "cannot read block header"); + memset(pImage, 0, nBlockXSize * nBlockYSize); + return CE_Failure; + } + + if (hdr.chk_flag == EPS_BAD_CRC || + hdr.crc_flag == EPS_BAD_CRC) + { + CPLError(CE_Warning, CPLE_AppDefined, "bad CRC"); + memset(pImage, 0, nBlockXSize * nBlockYSize); + return CE_Failure; + } + + int w = GET_FIELD(hdr, w); + int h = GET_FIELD(hdr, h); + int i; + + if (poGDS->nBands == 1) + { + unsigned char ** pTempData = + (unsigned char **) CPLMalloc(h * sizeof(unsigned char*)); + for(i=0;ipabyBlockData, &hdr) != EPS_OK) + { + CPLFree(pTempData); + memset(pImage, 0, nBlockXSize * nBlockYSize); + return CE_Failure; + } + CPLFree(pTempData); + } + else + { + if (poGDS->pabyRGBData == NULL) + { + poGDS->pabyRGBData = + (GByte*) VSIMalloc3(nBlockXSize, nBlockYSize, 3); + if (poGDS->pabyRGBData == NULL) + { + memset(pImage, 0, nBlockXSize * nBlockYSize); + return CE_Failure; + } + } + + if (poGDS->nBufferedBlock == nBlock) + { + memcpy(pImage, + poGDS->pabyRGBData + (nBand - 1) * nBlockXSize * nBlockYSize, + nBlockXSize * nBlockYSize); + return CE_None; + } + + unsigned char ** pTempData[3]; + int iBand; + for(iBand=0;iBand<3;iBand++) + { + pTempData[iBand] = + (unsigned char **) CPLMalloc(h * sizeof(unsigned char*)); + for(i=0;ipabyRGBData + + iBand * nBlockXSize * nBlockYSize + i * nBlockXSize; + } + + if (w != nBlockXSize || h != nBlockYSize) + memset(poGDS->pabyRGBData, 0, 3 * nBlockXSize * nBlockYSize); + + if (eps_decode_truecolor_block (pTempData[0], pTempData[1], pTempData[2], + poGDS->pabyBlockData, &hdr) != EPS_OK) + { + for(iBand=0;iBandnBands;iBand++) + CPLFree(pTempData[iBand]); + memset(pImage, 0, nBlockXSize * nBlockYSize); + return CE_Failure; + } + + for(iBand=0;iBandnBands;iBand++) + CPLFree(pTempData[iBand]); + + poGDS->nBufferedBlock = nBlock; + memcpy(pImage, + poGDS->pabyRGBData + (nBand - 1) * nBlockXSize * nBlockYSize, + nBlockXSize * nBlockYSize); + + if (nBand == 1) + { + int iOtherBand; + for(iOtherBand=2;iOtherBand<=poGDS->nBands;iOtherBand++) + { + GDALRasterBlock *poBlock; + + poBlock = poGDS->GetRasterBand(iOtherBand)-> + GetLockedBlockRef(nBlockXOff,nBlockYOff, TRUE); + if (poBlock == NULL) + break; + + GByte* pabySrcBlock = (GByte *) poBlock->GetDataRef(); + if( pabySrcBlock == NULL ) + { + poBlock->DropLock(); + break; + } + + memcpy(pabySrcBlock, + poGDS->pabyRGBData + (iOtherBand - 1) * nBlockXSize * nBlockYSize, + nBlockXSize * nBlockYSize); + + poBlock->DropLock(); + } + } + } + + return CE_None; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +void EpsilonDataset::Seek(vsi_l_offset nPos) +{ + bEOF = FALSE; + VSIFSeekL(fp, nPos, SEEK_SET); + nFileBufOffset = 0; + nFileBufCurSize = 0; + nFileOff = nPos; +} + +/************************************************************************/ +/* GetNextByte() */ +/************************************************************************/ + +#define BUFFER_CHUNK 16384 + +int EpsilonDataset::GetNextByte() +{ + if (nFileBufOffset < nFileBufCurSize) + { + nFileOff ++; + return pabyFileBuf[nFileBufOffset ++]; + } + + if (bError || bEOF) + return -1; + + if (nFileBufCurSize + BUFFER_CHUNK > nFileBufMaxSize) + { + GByte* pabyFileBufNew = + (GByte*)VSIRealloc(pabyFileBuf, nFileBufCurSize + BUFFER_CHUNK); + if (pabyFileBufNew == NULL) + { + bError = TRUE; + return -1; + } + pabyFileBuf = pabyFileBufNew; + nFileBufMaxSize = nFileBufCurSize + BUFFER_CHUNK; + } + int nBytesRead = + (int)VSIFReadL(pabyFileBuf + nFileBufCurSize, 1, BUFFER_CHUNK, fp); + nFileBufCurSize += nBytesRead; + if (nBytesRead < BUFFER_CHUNK) + bEOF = TRUE; + if (nBytesRead == 0) + return -1; + + nFileOff ++; + return pabyFileBuf[nFileBufOffset ++]; +} + +/************************************************************************/ +/* GetNextBlockData() */ +/************************************************************************/ + +#define MAX_SIZE_BEFORE_BLOCK_MARKER 100 + +int EpsilonDataset::GetNextBlockData() +{ + int nStartBlockBufOffset = 0; + pabyBlockData = NULL; + nBlockDataSize = 0; + + while (nFileBufOffset < MAX_SIZE_BEFORE_BLOCK_MARKER) + { + int chNextByte = GetNextByte(); + if (chNextByte < 0) + return FALSE; + + if (chNextByte != EPS_MARKER) + { + nStartBlockFileOff = nFileOff - 1; + nStartBlockBufOffset = nFileBufOffset - 1; + nBlockDataSize = 1; + break; + } + } + if (nFileBufOffset == MAX_SIZE_BEFORE_BLOCK_MARKER) + return FALSE; + + while (nFileBufOffset < BLOCK_DATA_MAX_SIZE) + { + int chNextByte = GetNextByte(); + if (chNextByte < 0) + break; + + if (chNextByte == EPS_MARKER) + { + pabyBlockData = pabyFileBuf + nStartBlockBufOffset; + return TRUE; + } + + nBlockDataSize ++; + } + + pabyBlockData = pabyFileBuf + nStartBlockBufOffset; + return TRUE; +} + +/************************************************************************/ +/* ScanBlocks() */ +/************************************************************************/ + +int EpsilonDataset::ScanBlocks(int* pnBands) +{ + int bRet = FALSE; + + int nExpectedX = 0; + int nExpectedY = 0; + + int nTileW = -1; + int nTileH = -1; + + *pnBands = 0; + + bRegularTiling = TRUE; + + eps_block_header hdr; + while(TRUE) + { + Seek(nStartBlockFileOff + nBlockDataSize); + + if (!GetNextBlockData()) + { + break; + } + + /* Ignore rasterlite wavelet header */ + int nRasterliteWaveletHeaderLen = strlen(RASTERLITE_WAVELET_HEADER); + if (nBlockDataSize >= nRasterliteWaveletHeaderLen && + memcmp(pabyBlockData, RASTERLITE_WAVELET_HEADER, + nRasterliteWaveletHeaderLen) == 0) + { + continue; + } + + /* Stop at rasterlite wavelet footer */ + int nRasterlineWaveletFooterLen = strlen(RASTERLITE_WAVELET_FOOTER); + if (nBlockDataSize >= nRasterlineWaveletFooterLen && + memcmp(pabyBlockData, RASTERLITE_WAVELET_FOOTER, + nRasterlineWaveletFooterLen) == 0) + { + break; + } + + if (eps_read_block_header (pabyBlockData, + nBlockDataSize, &hdr) != EPS_OK) + { + CPLError(CE_Warning, CPLE_AppDefined, "cannot read block header"); + continue; + } + + if (hdr.chk_flag == EPS_BAD_CRC || + hdr.crc_flag == EPS_BAD_CRC) + { + CPLError(CE_Warning, CPLE_AppDefined, "bad CRC"); + continue; + } + + int W = GET_FIELD(hdr, W); + int H = GET_FIELD(hdr, H); + int x = GET_FIELD(hdr, x); + int y = GET_FIELD(hdr, y); + int w = GET_FIELD(hdr, w); + int h = GET_FIELD(hdr, h); + + //CPLDebug("EPSILON", "W=%d,H=%d,x=%d,y=%d,w=%d,h=%d,offset=" CPL_FRMT_GUIB, + // W, H, x, y, w, h, nStartBlockFileOff); + + int nNewBands = (hdr.block_type == EPS_GRAYSCALE_BLOCK) ? 1 : 3; + if (nRasterXSize == 0) + { + if (W <= 0 || H <= 0) + { + break; + } + + bRet = TRUE; + nRasterXSize = W; + nRasterYSize = H; + *pnBands = nNewBands; + } + + if (nRasterXSize != W || nRasterYSize != H || *pnBands != nNewBands || + x < 0 || y < 0 || x + w > W || y + h > H) + { + CPLError(CE_Failure, CPLE_AppDefined, "Bad block characteristics"); + bRet = FALSE; + break; + } + + nBlocks++; + pasBlocks = (BlockDesc*)VSIRealloc(pasBlocks, sizeof(BlockDesc) * nBlocks); + pasBlocks[nBlocks-1].x = x; + pasBlocks[nBlocks-1].y = y; + pasBlocks[nBlocks-1].w = w; + pasBlocks[nBlocks-1].h = h; + pasBlocks[nBlocks-1].offset = nStartBlockFileOff; + + if (bRegularTiling) + { + if (nTileW < 0) + { + nTileW = w; + nTileH = h; + } + + if (w > nTileW || h > nTileH) + bRegularTiling = FALSE; + + if (x != nExpectedX) + bRegularTiling = FALSE; + + if (y != nExpectedY || nTileH != h) + { + if (y + h != H) + bRegularTiling = FALSE; + } + + if (nTileW != w) + { + if (x + w != W) + bRegularTiling = FALSE; + else + { + nExpectedX = 0; + nExpectedY += nTileW; + } + } + else + nExpectedX += nTileW; + + //if (!bRegularTiling) + // CPLDebug("EPSILON", "not regular tiling!"); + } + } + + return bRet; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int EpsilonDataset::Identify(GDALOpenInfo* poOpenInfo) +{ + int nRasterliteWaveletHeaderLen = strlen(RASTERLITE_WAVELET_HEADER); + if (poOpenInfo->nHeaderBytes > nRasterliteWaveletHeaderLen + 1 && + EQUALN((const char*)poOpenInfo->pabyHeader, + RASTERLITE_WAVELET_HEADER, nRasterliteWaveletHeaderLen)) + { + return TRUE; + } + + if (poOpenInfo->nHeaderBytes > EPS_MIN_GRAYSCALE_BUF && + (EQUALN((const char*)poOpenInfo->pabyHeader, "type=gs", 7) || + EQUALN((const char*)poOpenInfo->pabyHeader, "type=tc", 7))) + { + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset* EpsilonDataset::Open(GDALOpenInfo* poOpenInfo) +{ + if (!Identify(poOpenInfo)) + return NULL; + + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The EPSILON driver does not support update access to existing" + " files.\n" ); + return NULL; + } + + VSILFILE* fp = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + if (fp == NULL) + return NULL; + + EpsilonDataset* poDS = new EpsilonDataset(); + poDS->fp = fp; + + poDS->nRasterXSize = 0; + poDS->nRasterYSize = 0; + + int nBandsToAdd = 0; + if (!poDS->ScanBlocks(&nBandsToAdd)) + { + delete poDS; + return NULL; + } + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || + !GDALCheckBandCount(nBandsToAdd, FALSE)) + { + delete poDS; + return NULL; + } + if (!poDS->bRegularTiling) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The EPSILON driver does not support reading " + "not regularly blocked files.\n" ); + delete poDS; + return NULL; + } + + int i; + for(i=1;i<=nBandsToAdd;i++) + poDS->SetBand(i, new EpsilonRasterBand(poDS, i)); + + if (nBandsToAdd > 1) + poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); + + return poDS; +} + + +/************************************************************************/ +/* EpsilonDatasetCreateCopy () */ +/************************************************************************/ + +GDALDataset * +EpsilonDatasetCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + CPL_UNUSED int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) +{ + int nBands = poSrcDS->GetRasterCount(); + if ((nBands != 1 && nBands != 3) || + (nBands > 0 && poSrcDS->GetRasterBand(1)->GetColorTable() != NULL)) + { + CPLError(CE_Failure, CPLE_NotSupported, + "The EPSILON driver only supports 1 band (grayscale) " + "or 3 band (RGB) data"); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Fetch and check creation options */ +/* -------------------------------------------------------------------- */ + + int nBlockXSize = + atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "256")); + int nBlockYSize = + atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "256")); + if ((nBlockXSize != 32 && nBlockXSize != 64 && nBlockXSize != 128 && + nBlockXSize != 256 && nBlockXSize != 512 && nBlockXSize != 1024) || + (nBlockYSize != 32 && nBlockYSize != 64 && nBlockYSize != 128 && + nBlockYSize != 256 && nBlockYSize != 512 && nBlockYSize != 1024)) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Block size must be a power of 2 between 32 et 1024"); + return NULL; + } + + const char* pszFilter = + CSLFetchNameValueDef(papszOptions, "FILTER", "daub97lift"); + char** papszFBID = eps_get_fb_info(EPS_FB_ID); + char** papszFBIDIter = papszFBID; + int bFound = FALSE; + int nIndexFB = 0; + while(papszFBIDIter && *papszFBIDIter && !bFound) + { + if (strcmp(*papszFBIDIter, pszFilter) == 0) + bFound = TRUE; + else + nIndexFB ++; + papszFBIDIter ++; + } + eps_free_fb_info(papszFBID); + if (!bFound) + { + CPLError(CE_Failure, CPLE_NotSupported, "FILTER='%s' not supported", + pszFilter); + return NULL; + } + + int eMode = EPS_MODE_OTLPF; + const char* pszMode = CSLFetchNameValueDef(papszOptions, "MODE", "OTLPF"); + if (EQUAL(pszMode, "NORMAL")) + eMode = EPS_MODE_NORMAL; + else if (EQUAL(pszMode, "OTLPF")) + eMode = EPS_MODE_OTLPF; + else + { + CPLError(CE_Failure, CPLE_NotSupported, "MODE='%s' not supported", + pszMode); + return NULL; + } + + char** papszFBType = eps_get_fb_info(EPS_FB_TYPE); + int bIsBiOrthogonal = EQUAL(papszFBType[nIndexFB], "biorthogonal"); + eps_free_fb_info(papszFBType); + + if (eMode == EPS_MODE_OTLPF && !bIsBiOrthogonal) + { + CPLError(CE_Failure, CPLE_NotSupported, + "MODE=OTLPF can only be used with biorthogonal filters. " + "Use MODE=NORMAL instead"); + return NULL; + } + + int bRasterliteOutput = + CSLTestBoolean(CSLFetchNameValueDef(papszOptions, + "RASTERLITE_OUTPUT", "NO")); + + int nYRatio = EPS_Y_RT; + int nCbRatio = EPS_Cb_RT; + int nCrRatio = EPS_Cr_RT; + + int eResample; + if (CSLTestBoolean(CSLFetchNameValueDef(papszOptions, + "RGB_RESAMPLE", "YES"))) + eResample = EPS_RESAMPLE_420; + else + eResample = EPS_RESAMPLE_444; + + const char* pszTarget = CSLFetchNameValueDef(papszOptions, "TARGET", "96"); + double dfReductionFactor = 1 - CPLAtof(pszTarget) / 100; + if (dfReductionFactor > 1) + dfReductionFactor = 1; + else if (dfReductionFactor < 0) + dfReductionFactor = 0; + +/* -------------------------------------------------------------------- */ +/* Open file */ +/* -------------------------------------------------------------------- */ + + VSILFILE* fp = VSIFOpenL(pszFilename, "wb"); + if (fp == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", pszFilename); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Compute number of blocks, block size, etc... */ +/* -------------------------------------------------------------------- */ + + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + if (eMode == EPS_MODE_OTLPF) + { + nBlockXSize ++; + nBlockYSize ++; + } + int nXBlocks = (nXSize + nBlockXSize - 1) / nBlockXSize; + int nYBlocks = (nYSize + nBlockYSize - 1) / nBlockYSize; + int nBlocks = nXBlocks * nYBlocks; + int nUncompressedFileSize = nXSize * nYSize * nBands; + int nUncompressedBlockSize = nUncompressedFileSize / nBlocks; + int nTargetBlockSize = (int) (dfReductionFactor * nUncompressedBlockSize); + if (nBands == 1) + nTargetBlockSize = MAX (nTargetBlockSize, EPS_MIN_GRAYSCALE_BUF + 1); + else + nTargetBlockSize = MAX (nTargetBlockSize, EPS_MIN_TRUECOLOR_BUF + 1); + +/* -------------------------------------------------------------------- */ +/* Allocate work buffers */ +/* -------------------------------------------------------------------- */ + + GByte* pabyBuffer = (GByte*)VSIMalloc3(nBlockXSize, nBlockYSize, nBands); + if (pabyBuffer == NULL) + { + VSIFCloseL(fp); + return NULL; + } + + GByte* pabyOutBuf = (GByte*)VSIMalloc(nTargetBlockSize); + if (pabyOutBuf == NULL) + { + VSIFree(pabyBuffer); + VSIFCloseL(fp); + return NULL; + } + + GByte** apapbyRawBuffer[3]; + int i, j; + for(i=0;i nXSize) + { + bMustMemset = TRUE; + nReqXSize = nXSize - nBlockXOff * nBlockXSize; + } + if ((nBlockYOff+1) * nBlockYSize > nYSize) + { + bMustMemset = TRUE; + nReqYSize = nYSize - nBlockYOff * nBlockYSize; + } + if (bMustMemset) + memset(pabyBuffer, 0, nBands * nBlockXSize * nBlockYSize); + + eErr = poSrcDS->RasterIO(GF_Read, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + nReqXSize, nReqYSize, + pabyBuffer, + nReqXSize, nReqYSize, + GDT_Byte, nBands, NULL, + 1, + nBlockXSize, + nBlockXSize * nBlockYSize, NULL); + + int nOutBufSize = nTargetBlockSize; + if (eErr == CE_None && nBands == 1) + { + if (EPS_OK != eps_encode_grayscale_block(apapbyRawBuffer[0], + nXSize, nYSize, + nReqXSize, nReqYSize, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + pabyOutBuf, &nOutBufSize, + (char*) pszFilter, eMode)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error occured when encoding block (%d, %d)", + nBlockXOff, nBlockYOff); + eErr = CE_Failure; + } + } + else if (eErr == CE_None) + { + if (EPS_OK != eps_encode_truecolor_block( + apapbyRawBuffer[0], + apapbyRawBuffer[1], + apapbyRawBuffer[2], + nXSize, nYSize, + nReqXSize, nReqYSize, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + eResample, + pabyOutBuf, &nOutBufSize, + nYRatio, nCbRatio, nCrRatio, + (char*) pszFilter, eMode)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error occured when encoding block (%d, %d)", + nBlockXOff, nBlockYOff); + eErr = CE_Failure; + } + } + + if (eErr == CE_None) + { + if ((int)VSIFWriteL(pabyOutBuf, 1, nOutBufSize, fp) != + nOutBufSize) + eErr = CE_Failure; + + char chEPSMarker = EPS_MARKER; + VSIFWriteL(&chEPSMarker, 1, 1, fp); + + if (pfnProgress && !pfnProgress( + 1.0 * (nBlockYOff * nXBlocks + nBlockXOff + 1) / nBlocks, + NULL, pProgressData)) + { + eErr = CE_Failure; + } + } + } + } + + if (bRasterliteOutput) + { + const char* pszFooter = RASTERLITE_WAVELET_FOOTER; + VSIFWriteL(pszFooter, 1, strlen(pszFooter) + 1, fp); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup work buffers */ +/* -------------------------------------------------------------------- */ + + for(i=0;iSetDescription( "EPSILON" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Epsilon wavelets" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_epsilon.html" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte" ); + + CPLString osMethods; + char** papszFBID = eps_get_fb_info(EPS_FB_ID); + char** papszFBIDIter = papszFBID; + while(papszFBIDIter && *papszFBIDIter) + { + osMethods += " "; + osMethods += *papszFBIDIter; + osMethods += "\n"; + papszFBIDIter ++; + } + eps_free_fb_info(papszFBID); + + CPLString osOptionList; + osOptionList.Printf( +"" +" " +" " +" ", osMethods.c_str() ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, + osOptionList.c_str() ); + + poDriver->pfnOpen = EpsilonDataset::Open; + poDriver->pfnIdentify = EpsilonDataset::Identify; + poDriver->pfnCreateCopy = EpsilonDatasetCreateCopy; + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/epsilon/frmt_epsilon.html b/bazaar/plugin/gdal/frmts/epsilon/frmt_epsilon.html new file mode 100644 index 000000000..d8dd6cedf --- /dev/null +++ b/bazaar/plugin/gdal/frmts/epsilon/frmt_epsilon.html @@ -0,0 +1,41 @@ + + + + +Epsilon - Wavelet compressed images + + + + +

Epsilon - Wavelet compressed images

+ +

Starting with GDAL 1.7.0, GDAL can read and write wavelet-compressed images through the Epsilon library. +Starting with GDAL 1.9.0, epsilon 0.9.1 is required.

+ +

The driver rely on the Open Source EPSILON library (dual LGPL/GPL licence v3). In its current state, the + driver will only be able to read images with regular internal tiling.

+ +

The EPSILON driver only supports 1 band (grayscale) and 3 bands (RGB) images

+ +

This is mainly intented to be used by the Rasterlite driver.

+ +

Creation options

+ +
    +
  • TARGET Target size reduction as a percentage of the original (0-100). Defaults to 96

  • +
  • FILTER. See EPSILON documentation or 'gdalinfo --format EPSILON' for full list of filter IDs. Defaults to 'daub97lift'

  • +
  • BLOCKXSIZE=n: Sets tile width, defaults to 256. Power of 2 between 32 and 1024

  • +
  • BLOCKYSIZE=n: Sets tile height, defaults to 256. Power of 2 between 32 and 1024

  • +
  • MODE=[NORMAL/OTLPF] : OTLPF is some kind of hack to reduce boundary artefacts when image is broken into several tiles. + Due to mathematical constrains this method can be applied to biorthogonal filters only. Defaults to OTLPF

  • +
  • RGB_RESAMPLE=[YES/NO] : Whether RGB buffer must be resampled to 4:2:0. Defaults to YES

  • +
+ +

See Also:

+ + + + + diff --git a/bazaar/plugin/gdal/frmts/epsilon/makefile.vc b/bazaar/plugin/gdal/frmts/epsilon/makefile.vc new file mode 100644 index 000000000..330aadff0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/epsilon/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = epsilondataset.obj + +EXTRAFLAGS = -I$(EPSILON_DIR)\include + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/ers/GNUmakefile b/bazaar/plugin/gdal/frmts/ers/GNUmakefile new file mode 100644 index 000000000..7b6dbfba2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ers/GNUmakefile @@ -0,0 +1,22 @@ + + +include ../../GDALmake.opt + +OBJ = ersdataset.o ershdrnode.o + +CPPFLAGS := -I../raw $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +$(O_OBJ): ../raw/rawdataset.h + +plugin: gdal_ERS.so + +gdal_ERS.so: $(OBJ) + $(LD_SHARED) $(OBJ) $(GDAL_LIBS) $(LIBS) -o gdal_ERS.so + diff --git a/bazaar/plugin/gdal/frmts/ers/ersdataset.cpp b/bazaar/plugin/gdal/frmts/ers/ersdataset.cpp new file mode 100644 index 000000000..1da4234f4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ers/ersdataset.cpp @@ -0,0 +1,1483 @@ +/****************************************************************************** + * $Id: ersdataset.cpp 28474 2015-02-13 11:42:37Z rouault $ + * + * Project: ERMapper .ers Driver + * Purpose: Implementation of .ers driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include "ershdrnode.h" + +CPL_CVSID("$Id: ersdataset.cpp 28474 2015-02-13 11:42:37Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* ERSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class ERSRasterBand; + +class ERSDataset : public RawDataset +{ + friend class ERSRasterBand; + + VSILFILE *fpImage; // image data file. + GDALDataset *poDepFile; + + int bGotTransform; + double adfGeoTransform[6]; + char *pszProjection; + + CPLString osRawFilename; + + int bHDRDirty; + ERSHdrNode *poHeader; + + const char *Find( const char *, const char * ); + + int nGCPCount; + GDAL_GCP *pasGCPList; + char *pszGCPProjection; + + void ReadGCPs(); + + int bHasNoDataValue; + double dfNoDataValue; + + CPLString osProj, osProjForced; + CPLString osDatum, osDatumForced; + CPLString osUnits, osUnitsForced; + void WriteProjectionInfo(const char* pszProj, + const char* pszDatum, + const char* pszUnits); + + CPLStringList oERSMetadataList; + + protected: + virtual int CloseDependentDatasets(); + + public: + ERSDataset(); + ~ERSDataset(); + + virtual void FlushCache(void); + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual CPLErr SetGeoTransform( double *padfTransform ); + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + virtual char **GetFileList(void); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + virtual char **GetMetadataDomainList(); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual char **GetMetadata( const char * pszDomain = "" ); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); +}; + +/************************************************************************/ +/* ERSDataset() */ +/************************************************************************/ + +ERSDataset::ERSDataset() +{ + fpImage = NULL; + poDepFile = NULL; + pszProjection = CPLStrdup(""); + bGotTransform = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + poHeader = NULL; + bHDRDirty = FALSE; + + nGCPCount = 0; + pasGCPList = NULL; + pszGCPProjection = CPLStrdup(""); + + bHasNoDataValue = FALSE; + dfNoDataValue = 0.0; +} + +/************************************************************************/ +/* ~ERSDataset() */ +/************************************************************************/ + +ERSDataset::~ERSDataset() + +{ + FlushCache(); + + if( fpImage != NULL ) + { + VSIFCloseL( fpImage ); + } + + CloseDependentDatasets(); + + CPLFree( pszProjection ); + + CPLFree( pszGCPProjection ); + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + if( poHeader != NULL ) + delete poHeader; +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int ERSDataset::CloseDependentDatasets() +{ + int bHasDroppedRef = RawDataset::CloseDependentDatasets(); + + if( poDepFile != NULL ) + { + int iBand; + + bHasDroppedRef = TRUE; + + for( iBand = 0; iBand < nBands; iBand++ ) + papoBands[iBand] = NULL; + nBands = 0; + + GDALClose( (GDALDatasetH) poDepFile ); + poDepFile = NULL; + } + + return bHasDroppedRef; +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void ERSDataset::FlushCache() + +{ + if( bHDRDirty ) + { + VSILFILE * fpERS = VSIFOpenL( GetDescription(), "w" ); + if( fpERS == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to rewrite %s header.", + GetDescription() ); + } + else + { + VSIFPrintfL( fpERS, "DatasetHeader Begin\n" ); + poHeader->WriteSelf( fpERS, 1 ); + VSIFPrintfL( fpERS, "DatasetHeader End\n" ); + VSIFCloseL( fpERS ); + } + } + + RawDataset::FlushCache(); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **ERSDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "ERS", NULL); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *ERSDataset::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if (pszDomain != NULL && EQUAL(pszDomain, "ERS") && pszName != NULL) + { + if (EQUAL(pszName, "PROJ")) + return osProj.size() ? osProj.c_str() : NULL; + if (EQUAL(pszName, "DATUM")) + return osDatum.size() ? osDatum.c_str() : NULL; + if (EQUAL(pszName, "UNITS")) + return osUnits.size() ? osUnits.c_str() : NULL; + } + return GDALPamDataset::GetMetadataItem(pszName, pszDomain); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **ERSDataset::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain != NULL && EQUAL(pszDomain, "ERS") ) + { + oERSMetadataList.Clear(); + if (osProj.size()) + oERSMetadataList.AddString(CPLSPrintf("%s=%s", "PROJ", osProj.c_str())); + if (osDatum.size()) + oERSMetadataList.AddString(CPLSPrintf("%s=%s", "DATUM", osDatum.c_str())); + if (osUnits.size()) + oERSMetadataList.AddString(CPLSPrintf("%s=%s", "UNITS", osUnits.c_str())); + return oERSMetadataList.List(); + } + else + return GDALPamDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int ERSDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *ERSDataset::GetGCPProjection() + +{ + return pszGCPProjection; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *ERSDataset::GetGCPs() + +{ + return pasGCPList; +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +CPLErr ERSDataset::SetGCPs( int nGCPCountIn, const GDAL_GCP *pasGCPListIn, + const char *pszGCPProjectionIn ) + +{ +/* -------------------------------------------------------------------- */ +/* Clean old gcps. */ +/* -------------------------------------------------------------------- */ + CPLFree( pszGCPProjection ); + pszGCPProjection = NULL; + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + + pasGCPList = NULL; + nGCPCount = 0; + } + +/* -------------------------------------------------------------------- */ +/* Copy new ones. */ +/* -------------------------------------------------------------------- */ + nGCPCount = nGCPCountIn; + pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPListIn ); + pszGCPProjection = CPLStrdup( pszGCPProjectionIn ); + +/* -------------------------------------------------------------------- */ +/* Setup the header contents corresponding to these GCPs. */ +/* -------------------------------------------------------------------- */ + bHDRDirty = TRUE; + + poHeader->Set( "RasterInfo.WarpControl.WarpType", "Polynomial" ); + if( nGCPCount > 6 ) + poHeader->Set( "RasterInfo.WarpControl.WarpOrder", "2" ); + else + poHeader->Set( "RasterInfo.WarpControl.WarpOrder", "1" ); + poHeader->Set( "RasterInfo.WarpControl.WarpSampling", "Nearest" ); + +/* -------------------------------------------------------------------- */ +/* Translate the projection. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS( pszGCPProjection ); + char szERSProj[32], szERSDatum[32], szERSUnits[32]; + + oSRS.exportToERM( szERSProj, szERSDatum, szERSUnits ); + + /* Write the above computed values, unless they have been overriden by */ + /* the creation options PROJ, DATUM or UNITS */ + + poHeader->Set( "RasterInfo.WarpControl.CoordinateSpace.Datum", + CPLString().Printf( "\"%s\"", + (osDatum.size()) ? osDatum.c_str() : szERSDatum ) ); + poHeader->Set( "RasterInfo.WarpControl.CoordinateSpace.Projection", + CPLString().Printf( "\"%s\"", + (osProj.size()) ? osProj.c_str() : szERSProj ) ); + poHeader->Set( "RasterInfo.WarpControl.CoordinateSpace.CoordinateType", + CPLString().Printf( "EN" ) ); + poHeader->Set( "RasterInfo.WarpControl.CoordinateSpace.Units", + CPLString().Printf( "\"%s\"", + (osUnits.size()) ? osUnits.c_str() : szERSUnits ) ); + poHeader->Set( "RasterInfo.WarpControl.CoordinateSpace.Rotation", + "0:0:0.0" ); + +/* -------------------------------------------------------------------- */ +/* Translate the GCPs. */ +/* -------------------------------------------------------------------- */ + CPLString osControlPoints = "{\n"; + int iGCP; + + for( iGCP = 0; iGCP < nGCPCount; iGCP++ ) + { + CPLString osLine; + + CPLString osId = pasGCPList[iGCP].pszId; + if( strlen(osId) == 0 ) + osId.Printf( "%d", iGCP + 1 ); + + osLine.Printf( "\t\t\t\t\"%s\"\tYes\tYes\t%.6f\t%.6f\t%.15g\t%.15g\t%.15g\n", + osId.c_str(), + pasGCPList[iGCP].dfGCPPixel, + pasGCPList[iGCP].dfGCPLine, + pasGCPList[iGCP].dfGCPX, + pasGCPList[iGCP].dfGCPY, + pasGCPList[iGCP].dfGCPZ ); + osControlPoints += osLine; + } + osControlPoints += "\t\t}"; + + poHeader->Set( "RasterInfo.WarpControl.ControlPoints", osControlPoints ); + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *ERSDataset::GetProjectionRef() + +{ + // try xml first + const char* pszPrj = GDALPamDataset::GetProjectionRef(); + if(pszPrj && strlen(pszPrj) > 0) + return pszPrj; + + return pszProjection; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr ERSDataset::SetProjection( const char *pszSRS ) + +{ + if( pszProjection && EQUAL(pszSRS,pszProjection) ) + return CE_None; + + if( pszSRS == NULL ) + pszSRS = ""; + + CPLFree( pszProjection ); + pszProjection = CPLStrdup(pszSRS); + + OGRSpatialReference oSRS( pszSRS ); + char szERSProj[32], szERSDatum[32], szERSUnits[32]; + + oSRS.exportToERM( szERSProj, szERSDatum, szERSUnits ); + + /* Write the above computed values, unless they have been overriden by */ + /* the creation options PROJ, DATUM or UNITS */ + if( osProjForced.size() ) + osProj = osProjForced; + else + osProj = szERSProj; + if( osDatumForced.size() ) + osDatum = osDatumForced; + else + osDatum = szERSDatum; + if( osUnitsForced.size() ) + osUnits = osUnitsForced; + else + osUnits = szERSUnits; + + WriteProjectionInfo( osProj, osDatum, osUnits ); + + return CE_None; +} + +/************************************************************************/ +/* WriteProjectionInfo() */ +/************************************************************************/ + +void ERSDataset::WriteProjectionInfo(const char* pszProj, + const char* pszDatum, + const char* pszUnits) +{ + bHDRDirty = TRUE; + poHeader->Set( "CoordinateSpace.Datum", + CPLString().Printf( "\"%s\"", pszDatum ) ); + poHeader->Set( "CoordinateSpace.Projection", + CPLString().Printf( "\"%s\"", pszProj ) ); + poHeader->Set( "CoordinateSpace.CoordinateType", + CPLString().Printf( "EN" ) ); + poHeader->Set( "CoordinateSpace.Units", + CPLString().Printf( "\"%s\"", pszUnits ) ); + poHeader->Set( "CoordinateSpace.Rotation", + "0:0:0.0" ); + +/* -------------------------------------------------------------------- */ +/* It seems that CoordinateSpace needs to come before */ +/* RasterInfo. Try moving it up manually. */ +/* -------------------------------------------------------------------- */ + int iRasterInfo = -1; + int iCoordSpace = -1; + int i; + + for( i = 0; i < poHeader->nItemCount; i++ ) + { + if( EQUAL(poHeader->papszItemName[i],"RasterInfo") ) + iRasterInfo = i; + + if( EQUAL(poHeader->papszItemName[i],"CoordinateSpace") ) + { + iCoordSpace = i; + break; + } + } + + if( iCoordSpace > iRasterInfo && iRasterInfo != -1 ) + { + for( i = iCoordSpace; i > 0 && i != iRasterInfo; i-- ) + { + char *pszTemp; + + ERSHdrNode *poTemp = poHeader->papoItemChild[i]; + poHeader->papoItemChild[i] = poHeader->papoItemChild[i-1]; + poHeader->papoItemChild[i-1] = poTemp; + + pszTemp = poHeader->papszItemName[i]; + poHeader->papszItemName[i] = poHeader->papszItemName[i-1]; + poHeader->papszItemName[i-1] = pszTemp; + + pszTemp = poHeader->papszItemValue[i]; + poHeader->papszItemValue[i] = poHeader->papszItemValue[i-1]; + poHeader->papszItemValue[i-1] = pszTemp; + } + } +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr ERSDataset::GetGeoTransform( double * padfTransform ) + +{ + if( bGotTransform ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; + } + else + { + return GDALPamDataset::GetGeoTransform( padfTransform ); + } +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr ERSDataset::SetGeoTransform( double *padfTransform ) + +{ + if( memcmp( padfTransform, adfGeoTransform, sizeof(double)*6 ) == 0 ) + return CE_None; + + if( adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Rotated and skewed geotransforms not currently supported for ERS driver." ); + return CE_Failure; + } + + bGotTransform = TRUE; + memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 ); + + bHDRDirty = TRUE; + + poHeader->Set( "RasterInfo.CellInfo.Xdimension", + CPLString().Printf( "%.15g", fabs(adfGeoTransform[1]) ) ); + poHeader->Set( "RasterInfo.CellInfo.Ydimension", + CPLString().Printf( "%.15g", fabs(adfGeoTransform[5]) ) ); + poHeader->Set( "RasterInfo.RegistrationCoord.Eastings", + CPLString().Printf( "%.15g", adfGeoTransform[0] ) ); + poHeader->Set( "RasterInfo.RegistrationCoord.Northings", + CPLString().Printf( "%.15g", adfGeoTransform[3] ) ); + + if( CPLAtof(poHeader->Find("RasterInfo.RegistrationCellX", "0")) != 0.0 || + CPLAtof(poHeader->Find("RasterInfo.RegistrationCellY", "0")) != 0.0 ) + { + // Reset RegistrationCellX/Y to 0 if the header gets rewritten (#5493) + poHeader->Set("RasterInfo.RegistrationCellX", "0"); + poHeader->Set("RasterInfo.RegistrationCellY", "0"); + } + + return CE_None; +} + +/************************************************************************/ +/* ERSDMS2Dec() */ +/* */ +/* Convert ERS DMS format to decimal degrees. Input is like */ +/* "-180:00:00". */ +/************************************************************************/ + +static double ERSDMS2Dec( const char *pszDMS ) + +{ + char **papszTokens = CSLTokenizeStringComplex( pszDMS, ":", FALSE, FALSE ); + + if( CSLCount(papszTokens) != 3 ) + { + CSLDestroy(papszTokens); + return CPLAtof( pszDMS ); + } + else + { + double dfResult = fabs(CPLAtof(papszTokens[0])) + + CPLAtof(papszTokens[1]) / 60.0 + + CPLAtof(papszTokens[2]) / 3600.0; + + if( CPLAtof(papszTokens[0]) < 0 ) + dfResult *= -1; + + CSLDestroy( papszTokens ); + return dfResult; + } +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **ERSDataset::GetFileList() + +{ + char **papszFileList = NULL; + + // Main data file, etc. + papszFileList = GDALPamDataset::GetFileList(); + + // Add raw data file if we have one. + if( strlen(osRawFilename) > 0 ) + papszFileList = CSLAddString( papszFileList, osRawFilename ); + + // If we have a dependent file, merge it's list of files in. + if( poDepFile ) + { + char **papszDepFiles = poDepFile->GetFileList(); + papszFileList = + CSLInsertStrings( papszFileList, -1, papszDepFiles ); + CSLDestroy( papszDepFiles ); + } + + return papszFileList; +} + +/************************************************************************/ +/* ReadGCPs() */ +/* */ +/* Read the GCPs from the header. */ +/************************************************************************/ + +void ERSDataset::ReadGCPs() + +{ + const char *pszCP = + poHeader->Find( "RasterInfo.WarpControl.ControlPoints", NULL ); + + if( pszCP == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Parse the control points. They will look something like: */ +/* */ +/* "1035" Yes No 2344.650885 3546.419458 483270.73 3620906.21 3.105 */ +/* -------------------------------------------------------------------- */ + char **papszTokens = CSLTokenizeStringComplex( pszCP, "{ \t}", TRUE,FALSE); + int nItemsPerLine; + int nItemCount = CSLCount(papszTokens); + +/* -------------------------------------------------------------------- */ +/* Work out if we have elevation values or not. */ +/* -------------------------------------------------------------------- */ + if( nItemCount == 7 ) + nItemsPerLine = 7; + else if( nItemCount == 8 ) + nItemsPerLine = 8; + else if( nItemCount < 14 ) + { + CPLDebug("ERS", "Invalid item count for ControlPoints"); + CSLDestroy( papszTokens ); + return; + } + else if( EQUAL(papszTokens[8],"Yes") || EQUAL(papszTokens[8],"No") ) + nItemsPerLine = 7; + else if( EQUAL(papszTokens[9],"Yes") || EQUAL(papszTokens[9],"No") ) + nItemsPerLine = 8; + else + { + CPLDebug("ERS", "Invalid format for ControlPoints"); + CSLDestroy( papszTokens ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Setup GCPs. */ +/* -------------------------------------------------------------------- */ + int iGCP; + + CPLAssert( nGCPCount == 0 ); + + nGCPCount = nItemCount / nItemsPerLine; + pasGCPList = (GDAL_GCP *) CPLCalloc(nGCPCount,sizeof(GDAL_GCP)); + GDALInitGCPs( nGCPCount, pasGCPList ); + + for( iGCP = 0; iGCP < nGCPCount; iGCP++ ) + { + GDAL_GCP *psGCP = pasGCPList + iGCP; + + CPLFree( psGCP->pszId ); + psGCP->pszId = CPLStrdup(papszTokens[iGCP*nItemsPerLine+0]); + psGCP->dfGCPPixel = CPLAtof(papszTokens[iGCP*nItemsPerLine+3]); + psGCP->dfGCPLine = CPLAtof(papszTokens[iGCP*nItemsPerLine+4]); + psGCP->dfGCPX = CPLAtof(papszTokens[iGCP*nItemsPerLine+5]); + psGCP->dfGCPY = CPLAtof(papszTokens[iGCP*nItemsPerLine+6]); + if( nItemsPerLine == 8 ) + psGCP->dfGCPZ = CPLAtof(papszTokens[iGCP*nItemsPerLine+7]); + } + + CSLDestroy( papszTokens ); + +/* -------------------------------------------------------------------- */ +/* Parse the GCP projection. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + + osProj = poHeader->Find( "RasterInfo.WarpControl.CoordinateSpace.Projection", "" ); + osDatum = poHeader->Find( "RasterInfo.WarpControl.CoordinateSpace.Datum", "" ); + osUnits = poHeader->Find( "RasterInfo.WarpControl.CoordinateSpace.Units", "" ); + + oSRS.importFromERM( osProj.size() ? osProj.c_str() : "RAW", + osDatum.size() ? osDatum.c_str() : "WGS84", + osUnits.size() ? osUnits.c_str() : "METERS" ); + + CPLFree( pszGCPProjection ); + oSRS.exportToWkt( &pszGCPProjection ); +} + +/************************************************************************/ +/* ==================================================================== */ +/* ERSRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class ERSRasterBand : public RawRasterBand +{ + public: + + ERSRasterBand( GDALDataset *poDS, int nBand, void * fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder, + int bIsVSIL = FALSE, int bOwnsFP = FALSE ); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual CPLErr SetNoDataValue( double ); +}; + +/************************************************************************/ +/* ERSRasterBand() */ +/************************************************************************/ + +ERSRasterBand::ERSRasterBand( GDALDataset *poDS, int nBand, void * fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder, + int bIsVSIL, int bOwnsFP ) : + RawRasterBand(poDS, nBand, fpRaw, nImgOffset, nPixelOffset, + nLineOffset, eDataType, bNativeOrder, bIsVSIL, bOwnsFP) +{ +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double ERSRasterBand::GetNoDataValue( int *pbSuccess ) +{ + ERSDataset* poGDS = (ERSDataset*) poDS; + if (poGDS->bHasNoDataValue) + { + if (pbSuccess) + *pbSuccess = TRUE; + return poGDS->dfNoDataValue; + } + + return RawRasterBand::GetNoDataValue(pbSuccess); +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr ERSRasterBand::SetNoDataValue( double dfNoDataValue ) +{ + ERSDataset* poGDS = (ERSDataset*) poDS; + if (!poGDS->bHasNoDataValue || poGDS->dfNoDataValue != dfNoDataValue) + { + poGDS->bHasNoDataValue = TRUE; + poGDS->dfNoDataValue = dfNoDataValue; + + poGDS->bHDRDirty = TRUE; + poGDS->poHeader->Set( "RasterInfo.NullCellValue", + CPLString().Printf( "%.16g", dfNoDataValue) ); + } + return CE_None; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int ERSDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* We assume the user selects the .ers file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes > 15 + && EQUALN((const char *) poOpenInfo->pabyHeader,"Algorithm Begin",15) ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s appears to be an algorithm ERS file, which is not currently supported.", + poOpenInfo->pszFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* We assume the user selects the .ers file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 15 + || !EQUALN((const char *) poOpenInfo->pabyHeader,"DatasetHeader ",14) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *ERSDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Open the .ers file, and read the first line. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpERS = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + + if( fpERS == NULL ) + return NULL; + + CPLReadLineL( fpERS ); + +/* -------------------------------------------------------------------- */ +/* Now ingest the rest of the file as a tree of header nodes. */ +/* -------------------------------------------------------------------- */ + ERSHdrNode *poHeader = new ERSHdrNode(); + + if( !poHeader->ParseChildren( fpERS ) ) + { + delete poHeader; + VSIFCloseL( fpERS ); + return NULL; + } + + VSIFCloseL( fpERS ); + +/* -------------------------------------------------------------------- */ +/* Do we have the minimum required information from this header? */ +/* -------------------------------------------------------------------- */ + if( poHeader->Find( "RasterInfo.NrOfLines" ) == NULL + || poHeader->Find( "RasterInfo.NrOfCellsPerLine" ) == NULL + || poHeader->Find( "RasterInfo.NrOfBands" ) == NULL ) + { + if( poHeader->FindNode( "Algorithm" ) != NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s appears to be an algorithm ERS file, which is not currently supported.", + poOpenInfo->pszFilename ); + } + delete poHeader; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + ERSDataset *poDS; + + poDS = new ERSDataset(); + poDS->poHeader = poHeader; + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + int nBands = atoi(poHeader->Find( "RasterInfo.NrOfBands" )); + poDS->nRasterXSize = atoi(poHeader->Find( "RasterInfo.NrOfCellsPerLine" )); + poDS->nRasterYSize = atoi(poHeader->Find( "RasterInfo.NrOfLines" )); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || + !GDALCheckBandCount(nBands, FALSE)) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Get the HeaderOffset if it exists in the header */ +/* -------------------------------------------------------------------- */ + GIntBig nHeaderOffset = 0; + if( poHeader->Find( "HeaderOffset" ) != NULL ) + { + nHeaderOffset = atoi(poHeader->Find( "HeaderOffset" )); + } + +/* -------------------------------------------------------------------- */ +/* Establish the data type. */ +/* -------------------------------------------------------------------- */ + GDALDataType eType; + CPLString osCellType = poHeader->Find( "RasterInfo.CellType", + "Unsigned8BitInteger" ); + if( EQUAL(osCellType,"Unsigned8BitInteger") ) + eType = GDT_Byte; + else if( EQUAL(osCellType,"Signed8BitInteger") ) + eType = GDT_Byte; + else if( EQUAL(osCellType,"Unsigned16BitInteger") ) + eType = GDT_UInt16; + else if( EQUAL(osCellType,"Signed16BitInteger") ) + eType = GDT_Int16; + else if( EQUAL(osCellType,"Unsigned32BitInteger") ) + eType = GDT_UInt32; + else if( EQUAL(osCellType,"Signed32BitInteger") ) + eType = GDT_Int32; + else if( EQUAL(osCellType,"IEEE4ByteReal") ) + eType = GDT_Float32; + else if( EQUAL(osCellType,"IEEE8ByteReal") ) + eType = GDT_Float64; + else + { + CPLDebug( "ERS", "Unknown CellType '%s'", osCellType.c_str() ); + eType = GDT_Byte; + } + +/* -------------------------------------------------------------------- */ +/* Pick up the word order. */ +/* -------------------------------------------------------------------- */ + int bNative; + +#ifdef CPL_LSB + bNative = EQUAL(poHeader->Find( "ByteOrder", "LSBFirst" ), + "LSBFirst"); +#else + bNative = EQUAL(poHeader->Find( "ByteOrder", "MSBFirst" ), + "MSBFirst"); +#endif + +/* -------------------------------------------------------------------- */ +/* Figure out the name of the target file. */ +/* -------------------------------------------------------------------- */ + CPLString osPath = CPLGetPath( poOpenInfo->pszFilename ); + CPLString osDataFile = poHeader->Find( "DataFile", "" ); + CPLString osDataFilePath; + + if( osDataFile.length() == 0 ) // just strip off extension. + { + osDataFile = CPLGetFilename( poOpenInfo->pszFilename ); + osDataFile = osDataFile.substr( 0, osDataFile.find_last_of('.') ); + } + + osDataFilePath = CPLFormFilename( osPath, osDataFile, NULL ); + +/* -------------------------------------------------------------------- */ +/* DataSetType = Translated files are links to things like ecw */ +/* files. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(poHeader->Find("DataSetType",""),"Translated") ) + { + poDS->poDepFile = (GDALDataset *) + GDALOpenShared( osDataFilePath, poOpenInfo->eAccess ); + + if( poDS->poDepFile != NULL + && poDS->poDepFile->GetRasterCount() >= nBands ) + { + int iBand; + + for( iBand = 0; iBand < nBands; iBand++ ) + { + // Assume pixel interleaved. + poDS->SetBand( iBand+1, + poDS->poDepFile->GetRasterBand( iBand+1 ) ); + } + } + } + +/* ==================================================================== */ +/* While ERStorage indicates a raw file. */ +/* ==================================================================== */ + else if( EQUAL(poHeader->Find("DataSetType",""),"ERStorage") ) + { + // Open data file. + if( poOpenInfo->eAccess == GA_Update ) + poDS->fpImage = VSIFOpenL( osDataFilePath, "r+" ); + else + poDS->fpImage = VSIFOpenL( osDataFilePath, "r" ); + + poDS->osRawFilename = osDataFilePath; + + if( poDS->fpImage != NULL ) + { + int iWordSize = GDALGetDataTypeSize(eType) / 8; + int iBand; + + for( iBand = 0; iBand < nBands; iBand++ ) + { + // Assume pixel interleaved. + poDS->SetBand( + iBand+1, + new ERSRasterBand( poDS, iBand+1, poDS->fpImage, + nHeaderOffset + + iWordSize * iBand * poDS->nRasterXSize, + iWordSize, + iWordSize * nBands * poDS->nRasterXSize, + eType, bNative, TRUE )); + if( EQUAL(osCellType,"Signed8BitInteger") ) + poDS->GetRasterBand(iBand+1)-> + SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", + "IMAGE_STRUCTURE" ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise we have an error! */ +/* -------------------------------------------------------------------- */ + if( poDS->nBands == 0 ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Look for band descriptions. */ +/* -------------------------------------------------------------------- */ + int iChild, iBand = 0; + ERSHdrNode *poRI = poHeader->FindNode( "RasterInfo" ); + + for( iChild = 0; + poRI != NULL && iChild < poRI->nItemCount && iBand < poDS->nBands; + iChild++ ) + { + if( poRI->papoItemChild[iChild] != NULL + && EQUAL(poRI->papszItemName[iChild],"BandId") ) + { + const char *pszValue = + poRI->papoItemChild[iChild]->Find( "Value", NULL ); + + iBand++; + if( pszValue ) + { + CPLPushErrorHandler( CPLQuietErrorHandler ); + poDS->GetRasterBand( iBand )->SetDescription( pszValue ); + CPLPopErrorHandler(); + } + + pszValue = poRI->papoItemChild[iChild]->Find( "Units", NULL ); + if ( pszValue ) + { + CPLPushErrorHandler( CPLQuietErrorHandler ); + poDS->GetRasterBand( iBand )->SetUnitType( pszValue ); + CPLPopErrorHandler(); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Look for projection. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + + poDS->osProj = poHeader->Find( "CoordinateSpace.Projection", "" ); + poDS->osDatum = poHeader->Find( "CoordinateSpace.Datum", "" ); + poDS->osUnits = poHeader->Find( "CoordinateSpace.Units", "" ); + + oSRS.importFromERM( poDS->osProj.size() ? poDS->osProj.c_str() : "RAW", + poDS->osDatum.size() ? poDS->osDatum.c_str() : "WGS84", + poDS->osUnits.size() ? poDS->osUnits.c_str() : "METERS" ); + + CPLFree( poDS->pszProjection ); + oSRS.exportToWkt( &(poDS->pszProjection) ); + +/* -------------------------------------------------------------------- */ +/* Look for the geotransform. */ +/* -------------------------------------------------------------------- */ + if( poHeader->Find( "RasterInfo.RegistrationCoord.Eastings", NULL ) ) + { + poDS->bGotTransform = TRUE; + poDS->adfGeoTransform[0] = CPLAtof( + poHeader->Find( "RasterInfo.RegistrationCoord.Eastings", "" )); + poDS->adfGeoTransform[1] = CPLAtof( + poHeader->Find( "RasterInfo.CellInfo.Xdimension", "1.0" )); + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = CPLAtof( + poHeader->Find( "RasterInfo.RegistrationCoord.Northings", "" )); + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -CPLAtof( + poHeader->Find( "RasterInfo.CellInfo.Ydimension", "1.0" )); + } + else if( poHeader->Find( "RasterInfo.RegistrationCoord.Latitude", NULL ) + && poHeader->Find( "RasterInfo.CellInfo.Xdimension", NULL ) ) + { + poDS->bGotTransform = TRUE; + poDS->adfGeoTransform[0] = ERSDMS2Dec( + poHeader->Find( "RasterInfo.RegistrationCoord.Longitude", "" )); + poDS->adfGeoTransform[1] = CPLAtof( + poHeader->Find( "RasterInfo.CellInfo.Xdimension", "" )); + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = ERSDMS2Dec( + poHeader->Find( "RasterInfo.RegistrationCoord.Latitude", "" )); + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -CPLAtof( + poHeader->Find( "RasterInfo.CellInfo.Ydimension", "" )); + } + +/* -------------------------------------------------------------------- */ +/* Adjust if we have a registration cell. */ +/* -------------------------------------------------------------------- */ + + /* http://geospatial.intergraph.com/Libraries/Tech_Docs/ERDAS_ER_Mapper_Customization_Guide.sflb.ashx */ + /* Page 27 : */ + /* RegistrationCellX and RegistrationCellY : The image X and Y + coordinates of the cell which corresponds to the Registration + Coordinate. Note that the RegistrationCellX and + RegistrationCellY can be fractional values. If + RegistrationCellX and RegistrationCellY are not specified, + they are assumed to be (0,0), which is the top left corner of the + image. + */ + double dfCellX = CPLAtof(poHeader->Find("RasterInfo.RegistrationCellX", "0")); + double dfCellY = CPLAtof(poHeader->Find("RasterInfo.RegistrationCellY", "0")); + + if( poDS->bGotTransform ) + { + poDS->adfGeoTransform[0] -= + dfCellX * poDS->adfGeoTransform[1] + + dfCellY * poDS->adfGeoTransform[2]; + poDS->adfGeoTransform[3] -= + dfCellX * poDS->adfGeoTransform[4] + + dfCellY * poDS->adfGeoTransform[5]; + } + +/* -------------------------------------------------------------------- */ +/* Check for null values. */ +/* -------------------------------------------------------------------- */ + if( poHeader->Find( "RasterInfo.NullCellValue", NULL ) ) + { + poDS->bHasNoDataValue = TRUE; + poDS->dfNoDataValue = CPLAtofM(poHeader->Find( "RasterInfo.NullCellValue" )); + + if (poDS->poDepFile != NULL) + { + CPLPushErrorHandler( CPLQuietErrorHandler ); + + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + poDS->GetRasterBand(iBand)->SetNoDataValue(poDS->dfNoDataValue); + + CPLPopErrorHandler(); + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have an "All" region? */ +/* -------------------------------------------------------------------- */ + ERSHdrNode *poAll = NULL; + + for( iChild = 0; + poRI != NULL && iChild < poRI->nItemCount; + iChild++ ) + { + if( poRI->papoItemChild[iChild] != NULL + && EQUAL(poRI->papszItemName[iChild],"RegionInfo") ) + { + if( EQUAL(poRI->papoItemChild[iChild]->Find("RegionName",""), + "All") ) + poAll = poRI->papoItemChild[iChild]; + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have statistics? */ +/* -------------------------------------------------------------------- */ + if( poAll && poAll->FindNode( "Stats" ) ) + { + CPLPushErrorHandler( CPLQuietErrorHandler ); + + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + { + const char *pszValue = + poAll->FindElem( "Stats.MinimumValue", iBand-1 ); + + if( pszValue ) + poDS->GetRasterBand(iBand)->SetMetadataItem( + "STATISTICS_MINIMUM", pszValue ); + + pszValue = poAll->FindElem( "Stats.MaximumValue", iBand-1 ); + + if( pszValue ) + poDS->GetRasterBand(iBand)->SetMetadataItem( + "STATISTICS_MAXIMUM", pszValue ); + + pszValue = poAll->FindElem( "Stats.MeanValue", iBand-1 ); + + if( pszValue ) + poDS->GetRasterBand(iBand)->SetMetadataItem( + "STATISTICS_MEAN", pszValue ); + + pszValue = poAll->FindElem( "Stats.MedianValue", iBand-1 ); + + if( pszValue ) + poDS->GetRasterBand(iBand)->SetMetadataItem( + "STATISTICS_MEDIAN", pszValue ); + } + + CPLPopErrorHandler(); + + } + +/* -------------------------------------------------------------------- */ +/* Do we have GCPs. */ +/* -------------------------------------------------------------------- */ + if( poHeader->FindNode( "RasterInfo.WarpControl" ) ) + poDS->ReadGCPs(); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + + // if no SR in xml, try aux + const char* pszPrj = poDS->GDALPamDataset::GetProjectionRef(); + if( !pszPrj || strlen(pszPrj) == 0 ) + { + // try aux + GDALDataset* poAuxDS = GDALFindAssociatedAuxFile( poOpenInfo->pszFilename, GA_ReadOnly, poDS ); + if( poAuxDS ) + { + pszPrj = poAuxDS->GetProjectionRef(); + if( pszPrj && strlen(pszPrj) > 0 ) + { + CPLFree( poDS->pszProjection ); + poDS->pszProjection = CPLStrdup(pszPrj); + } + + GDALClose( poAuxDS ); + } + } +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *ERSDataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify settings. */ +/* -------------------------------------------------------------------- */ + if (nBands <= 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "ERS driver does not support %d bands.\n", nBands); + return NULL; + } + + if( eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_UInt16 + && eType != GDT_Int32 && eType != GDT_UInt32 + && eType != GDT_Float32 && eType != GDT_Float64 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The ERS driver does not supporting creating files of types %s.", + GDALGetDataTypeName( eType ) ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Work out the name we want to use for the .ers and binary */ +/* data files. */ +/* -------------------------------------------------------------------- */ + CPLString osBinFile, osErsFile; + + if( EQUAL(CPLGetExtension( pszFilename ), "ers") ) + { + osErsFile = pszFilename; + osBinFile = osErsFile.substr(0,osErsFile.length()-4); + } + else + { + osBinFile = pszFilename; + osErsFile = osBinFile + ".ers"; + } + +/* -------------------------------------------------------------------- */ +/* Work out some values we will write. */ +/* -------------------------------------------------------------------- */ + const char *pszCellType = "Unsigned8BitInteger"; + + if( eType == GDT_Byte ) + pszCellType = "Unsigned8BitInteger"; + else if( eType == GDT_Int16 ) + pszCellType = "Signed16BitInteger"; + else if( eType == GDT_UInt16 ) + pszCellType = "Unsigned16BitInteger"; + else if( eType == GDT_Int32 ) + pszCellType = "Signed32BitInteger"; + else if( eType == GDT_UInt32 ) + pszCellType = "Unsigned32BitInteger"; + else if( eType == GDT_Float32 ) + pszCellType = "IEEE4ByteReal"; + else if( eType == GDT_Float64 ) + pszCellType = "IEEE8ByteReal"; + else + { + CPLAssert( FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Handling for signed eight bit data. */ +/* -------------------------------------------------------------------- */ + const char *pszPixelType = CSLFetchNameValue( papszOptions, "PIXELTYPE" ); + if( pszPixelType + && EQUAL(pszPixelType,"SIGNEDBYTE") + && eType == GDT_Byte ) + pszCellType = "Signed8BitInteger"; + +/* -------------------------------------------------------------------- */ +/* Write binary file. */ +/* -------------------------------------------------------------------- */ + GUIntBig nSize; + GByte byZero = 0; + + VSILFILE *fpBin = VSIFOpenL( osBinFile, "w" ); + + if( fpBin == NULL ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to create %s:\n%s", + osBinFile.c_str(), VSIStrerror( errno ) ); + return NULL; + } + + nSize = nXSize * (GUIntBig) nYSize + * nBands * (GDALGetDataTypeSize(eType) / 8); + if( VSIFSeekL( fpBin, nSize-1, SEEK_SET ) != 0 + || VSIFWriteL( &byZero, 1, 1, fpBin ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to write %s:\n%s", + osBinFile.c_str(), VSIStrerror( errno ) ); + VSIFCloseL( fpBin ); + return NULL; + } + VSIFCloseL( fpBin ); + + +/* -------------------------------------------------------------------- */ +/* Try writing header file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpERS = VSIFOpenL( osErsFile, "w" ); + + if( fpERS == NULL ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to create %s:\n%s", + osErsFile.c_str(), VSIStrerror( errno ) ); + return NULL; + } + + VSIFPrintfL( fpERS, "DatasetHeader Begin\n" ); + VSIFPrintfL( fpERS, "\tVersion\t\t = \"6.0\"\n" ); + VSIFPrintfL( fpERS, "\tName\t\t= \"%s\"\n", CPLGetFilename(osErsFile) ); + +// Last updated requires timezone info which we don't necessarily get +// get from VSICTime() so perhaps it is better to omit this. +// VSIFPrintfL( fpERS, "\tLastUpdated\t= %s", +// VSICTime( VSITime( NULL ) ) ); + + VSIFPrintfL( fpERS, "\tDataSetType\t= ERStorage\n" ); + VSIFPrintfL( fpERS, "\tDataType\t= Raster\n" ); + VSIFPrintfL( fpERS, "\tByteOrder\t= LSBFirst\n" ); + VSIFPrintfL( fpERS, "\tRasterInfo Begin\n" ); + VSIFPrintfL( fpERS, "\t\tCellType\t= %s\n", pszCellType ); + VSIFPrintfL( fpERS, "\t\tNrOfLines\t= %d\n", nYSize ); + VSIFPrintfL( fpERS, "\t\tNrOfCellsPerLine\t= %d\n", nXSize ); + VSIFPrintfL( fpERS, "\t\tNrOfBands\t= %d\n", nBands ); + VSIFPrintfL( fpERS, "\tRasterInfo End\n" ); + if( VSIFPrintfL( fpERS, "DatasetHeader End\n" ) < 17 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to write %s:\n%s", + osErsFile.c_str(), VSIStrerror( errno ) ); + return NULL; + } + + VSIFCloseL( fpERS ); + +/* -------------------------------------------------------------------- */ +/* Reopen. */ +/* -------------------------------------------------------------------- */ + GDALOpenInfo oOpenInfo( osErsFile, GA_Update ); + ERSDataset* poDS = (ERSDataset*) Open( &oOpenInfo ); + if (poDS == NULL) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Fetch DATUM, PROJ and UNITS creation option */ +/* -------------------------------------------------------------------- */ + const char *pszDatum = CSLFetchNameValue( papszOptions, "DATUM" ); + if (pszDatum) + poDS->osDatumForced = poDS->osDatum = pszDatum; + const char *pszProj = CSLFetchNameValue( papszOptions, "PROJ" ); + if (pszProj) + poDS->osProjForced = poDS->osProj = pszProj; + const char *pszUnits = CSLFetchNameValue( papszOptions, "UNITS" ); + if (pszUnits) + poDS->osUnitsForced = poDS->osUnits = pszUnits; + + if (pszDatum || pszProj || pszUnits) + { + poDS->WriteProjectionInfo(pszProj ? pszProj : "RAW", + pszDatum ? pszDatum : "RAW", + pszUnits ? pszUnits : "METERS"); + } + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_ERS() */ +/************************************************************************/ + +void GDALRegister_ERS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ERS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ERS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ERMapper .ers Labelled" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_ers.html" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 Float32 Float64" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +"" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = ERSDataset::Open; + poDriver->pfnIdentify = ERSDataset::Identify; + poDriver->pfnCreate = ERSDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/ers/ershdrnode.cpp b/bazaar/plugin/gdal/frmts/ers/ershdrnode.cpp new file mode 100644 index 000000000..5aab4d10c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ers/ershdrnode.cpp @@ -0,0 +1,471 @@ +/****************************************************************************** + * $Id: ershdrnode.cpp 21814 2011-02-23 22:55:14Z warmerdam $ + * + * Project: ERMapper .ers Driver + * Purpose: Implementation of ERSHdrNode class for parsing/accessing .ers hdr. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ershdrnode.h" + +CPL_CVSID("$Id: ershdrnode.cpp 21814 2011-02-23 22:55:14Z warmerdam $"); + + +/************************************************************************/ +/* ERSHdrNode() */ +/************************************************************************/ + +ERSHdrNode::ERSHdrNode() + +{ + nItemMax = 0; + nItemCount = 0; + papszItemName = NULL; + papszItemValue = NULL; + papoItemChild = NULL; +} + +/************************************************************************/ +/* ~ERSHdrNode() */ +/************************************************************************/ + +ERSHdrNode::~ERSHdrNode() + +{ + int i; + + for( i = 0; i < nItemCount; i++ ) + { + if( papoItemChild[i] != NULL ) + delete papoItemChild[i]; + if( papszItemValue[i] != NULL ) + CPLFree( papszItemValue[i] ); + CPLFree( papszItemName[i] ); + } + + CPLFree( papszItemName ); + CPLFree( papszItemValue ); + CPLFree( papoItemChild ); +} + +/************************************************************************/ +/* MakeSpace() */ +/* */ +/* Ensure we have room for at least one more entry in our item */ +/* lists. */ +/************************************************************************/ + +void ERSHdrNode::MakeSpace() + +{ + if( nItemCount == nItemMax ) + { + nItemMax = (int) (nItemMax * 1.3) + 10; + papszItemName = (char **) + CPLRealloc(papszItemName,sizeof(char *) * nItemMax); + papszItemValue = (char **) + CPLRealloc(papszItemValue,sizeof(char *) * nItemMax); + papoItemChild = (ERSHdrNode **) + CPLRealloc(papoItemChild,sizeof(void *) * nItemMax); + } +} + +/************************************************************************/ +/* ReadLine() */ +/* */ +/* Read one virtual line from the input source. Multiple lines */ +/* will be appended for objects enclosed in {}. */ +/************************************************************************/ + +int ERSHdrNode::ReadLine( VSILFILE * fp, CPLString &osLine ) + +{ + int nBracketLevel; + + osLine = ""; + + do + { + const char *pszNewLine = CPLReadLineL( fp ); + + if( pszNewLine == NULL ) + return FALSE; + + osLine += pszNewLine; + + int bInQuote = FALSE; + size_t i; + + nBracketLevel = 0; + + for( i = 0; i < osLine.length(); i++ ) + { + if( osLine[i] == '"' ) + bInQuote = !bInQuote; + else if( osLine[i] == '{' && !bInQuote ) + nBracketLevel++; + else if( osLine[i] == '}' && !bInQuote ) + nBracketLevel--; + + // We have to ignore escaped quotes and backslashes in strings. + else if( osLine[i] == '\\' && osLine[i+1] == '"' && bInQuote ) + i++; + else if( osLine[i] == '\\' && osLine[i+1] == '\\' && bInQuote ) + i++; + } + } while( nBracketLevel > 0 ); + + return TRUE; +} + +/************************************************************************/ +/* ParseChildren() */ +/* */ +/* We receive the FILE * positioned after the "Object Begin" */ +/* line for this object, and are responsible for reading all */ +/* children. We should return after consuming the */ +/* corresponding End line for this object. Really the first */ +/* unmatched End since we don't know what object we are. */ +/* */ +/* This function is used recursively to read sub-objects. */ +/************************************************************************/ + +int ERSHdrNode::ParseChildren( VSILFILE * fp ) + +{ + while( TRUE ) + { + size_t iOff; + CPLString osLine; + +/* -------------------------------------------------------------------- */ +/* Read the next line (or multi-line for bracketed value). */ +/* -------------------------------------------------------------------- */ + if( !ReadLine( fp, osLine ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Got a Name=Value. */ +/* -------------------------------------------------------------------- */ + if( (iOff = osLine.find_first_of( '=' )) != std::string::npos ) + { + CPLString osName = osLine.substr(0,iOff-1); + osName.Trim(); + + CPLString osValue = osLine.c_str() + iOff + 1; + osValue.Trim(); + + MakeSpace(); + papszItemName[nItemCount] = CPLStrdup(osName); + papszItemValue[nItemCount] = CPLStrdup(osValue); + papoItemChild[nItemCount] = NULL; + + nItemCount++; + } + +/* -------------------------------------------------------------------- */ +/* Got a Begin for an object. */ +/* -------------------------------------------------------------------- */ + else if( (iOff = osLine.ifind( " Begin" )) != std::string::npos ) + { + CPLString osName = osLine.substr(0,iOff); + osName.Trim(); + + MakeSpace(); + papszItemName[nItemCount] = CPLStrdup(osName); + papszItemValue[nItemCount] = NULL; + papoItemChild[nItemCount] = new ERSHdrNode(); + + nItemCount++; + + if( !papoItemChild[nItemCount-1]->ParseChildren( fp ) ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Got an End for our object. Well, at least we *assume* it */ +/* must be for our object. */ +/* -------------------------------------------------------------------- */ + else if( osLine.ifind( " End" ) != std::string::npos ) + { + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Error? */ +/* -------------------------------------------------------------------- */ + else if( osLine.Trim().length() > 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unexpected line parsing .ecw:\n%s", + osLine.c_str() ); + return FALSE; + } + } +} + +/************************************************************************/ +/* WriteSelf() */ +/* */ +/* Recursively write self and children to file. */ +/************************************************************************/ + +int ERSHdrNode::WriteSelf( VSILFILE * fp, int nIndent ) + +{ + CPLString oIndent; + int i; + + oIndent.assign( nIndent, '\t' ); + + for( i = 0; i < nItemCount; i++ ) + { + if( papszItemValue[i] != NULL ) + { + if( VSIFPrintfL( fp, "%s%s\t= %s\n", + oIndent.c_str(), + papszItemName[i], + papszItemValue[i] ) < 1 ) + return FALSE; + } + else + { + VSIFPrintfL( fp, "%s%s Begin\n", + oIndent.c_str(), papszItemName[i] ); + if( !papoItemChild[i]->WriteSelf( fp, nIndent+1 ) ) + return FALSE; + if( VSIFPrintfL( fp, "%s%s End\n", + oIndent.c_str(), papszItemName[i] ) < 1 ) + return FALSE; + } + } + + return TRUE; +} + +/************************************************************************/ +/* Find() */ +/* */ +/* Find the desired entry value. The input is a path with */ +/* components seperated by dots, relative to the current node. */ +/************************************************************************/ + +const char *ERSHdrNode::Find( const char *pszPath, const char *pszDefault ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* If this is the final component of the path, search for a */ +/* matching child and return the value. */ +/* -------------------------------------------------------------------- */ + if( strchr(pszPath,'.') == NULL ) + { + for( i = 0; i < nItemCount; i++ ) + { + if( EQUAL(pszPath,papszItemName[i]) ) + { + if( papszItemValue[i] != NULL ) + { + if( papszItemValue[i][0] == '"' ) + { + // strip off quotes. + osTempReturn = papszItemValue[i]; + osTempReturn = + osTempReturn.substr( 1, osTempReturn.length()-2 ); + return osTempReturn; + } + else + return papszItemValue[i]; + } + else + return pszDefault; + } + } + return pszDefault; + } + +/* -------------------------------------------------------------------- */ +/* This is a dot path - extract the first element, find a match */ +/* and recurse. */ +/* -------------------------------------------------------------------- */ + CPLString osPathFirst, osPathRest, osPath = pszPath; + int iDot; + + iDot = osPath.find_first_of('.'); + osPathFirst = osPath.substr(0,iDot); + osPathRest = osPath.substr(iDot+1); + + for( i = 0; i < nItemCount; i++ ) + { + if( EQUAL(osPathFirst,papszItemName[i]) ) + { + if( papoItemChild[i] != NULL ) + return papoItemChild[i]->Find( osPathRest, pszDefault ); + else + return pszDefault; + } + } + + return pszDefault; +} + +/************************************************************************/ +/* FindElem() */ +/* */ +/* Find a particular element from an array valued item. */ +/************************************************************************/ + +const char *ERSHdrNode::FindElem( const char *pszPath, int iElem, + const char *pszDefault ) + +{ + const char *pszArray = Find( pszPath, NULL ); + char **papszTokens; + int bDefault = TRUE; + + if( pszArray == NULL ) + return pszDefault; + + papszTokens = CSLTokenizeStringComplex( pszArray, "{ \t}", TRUE, FALSE ); + if( iElem >= 0 && iElem < CSLCount(papszTokens) ) + { + osTempReturn = papszTokens[iElem]; + bDefault = FALSE; + } + + CSLDestroy( papszTokens ); + + if( bDefault ) + return pszDefault; + else + return osTempReturn; +} + +/************************************************************************/ +/* FindNode() */ +/* */ +/* Find the desired node. */ +/************************************************************************/ + +ERSHdrNode *ERSHdrNode::FindNode( const char *pszPath ) + +{ + int i; + CPLString osPathFirst, osPathRest, osPath = pszPath; + int iDot; + + iDot = osPath.find_first_of('.'); + if( iDot == -1 ) + { + osPathFirst = osPath; + } + else + { + osPathFirst = osPath.substr(0,iDot); + osPathRest = osPath.substr(iDot+1); + } + + for( i = 0; i < nItemCount; i++ ) + { + if( EQUAL(osPathFirst,papszItemName[i]) ) + { + if( papoItemChild[i] != NULL ) + { + if( osPathRest.length() > 0 ) + return papoItemChild[i]->FindNode( osPathRest ); + else + return papoItemChild[i]; + } + else + return NULL; + } + } + + return NULL; +} + +/************************************************************************/ +/* Set() */ +/* */ +/* Set a value item. */ +/************************************************************************/ + +void ERSHdrNode::Set( const char *pszPath, const char *pszValue ) + +{ + CPLString osPath = pszPath; + int iDot; + + iDot = osPath.find_first_of('.'); + +/* -------------------------------------------------------------------- */ +/* We have an intermediate node, find or create it and */ +/* recurse. */ +/* -------------------------------------------------------------------- */ + if( iDot != -1 ) + { + CPLString osPathFirst = osPath.substr(0,iDot); + CPLString osPathRest = osPath.substr(iDot+1); + ERSHdrNode *poFirst = FindNode( osPathFirst ); + + if( poFirst == NULL ) + { + poFirst = new ERSHdrNode(); + + MakeSpace(); + papszItemName[nItemCount] = CPLStrdup(osPathFirst); + papszItemValue[nItemCount] = NULL; + papoItemChild[nItemCount] = poFirst; + nItemCount++; + } + + poFirst->Set( osPathRest, pszValue ); + return; + } + +/* -------------------------------------------------------------------- */ +/* This is the final item name. Find or create it. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nItemCount; i++ ) + { + if( EQUAL(osPath,papszItemName[i]) + && papszItemValue[i] != NULL ) + { + CPLFree( papszItemValue[i] ); + papszItemValue[i] = CPLStrdup( pszValue ); + return; + } + } + + MakeSpace(); + papszItemName[nItemCount] = CPLStrdup(osPath); + papszItemValue[nItemCount] = CPLStrdup(pszValue); + papoItemChild[nItemCount] = NULL; + nItemCount++; +} diff --git a/bazaar/plugin/gdal/frmts/ers/ershdrnode.h b/bazaar/plugin/gdal/frmts/ers/ershdrnode.h new file mode 100644 index 000000000..1fbf371a9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ers/ershdrnode.h @@ -0,0 +1,32 @@ + +class ERSHdrNode; + +class ERSHdrNode +{ + CPLString osTempReturn; + + void MakeSpace(); + +public: + int nItemMax; + int nItemCount; + char **papszItemName; + char **papszItemValue; + ERSHdrNode **papoItemChild; + + ERSHdrNode(); + ~ERSHdrNode(); + + int ParseChildren( VSILFILE *fp ); + int WriteSelf( VSILFILE *fp, int nIndent ); + + const char *Find( const char *pszPath, const char *pszDefault = NULL ); + const char *FindElem( const char *pszPath, int iElem, + const char *pszDefault = NULL ); + ERSHdrNode *FindNode( const char *pszPath ); + + void Set( const char *pszPath, const char *pszValue ); + +private: + int ReadLine( VSILFILE *, CPLString & ); +}; diff --git a/bazaar/plugin/gdal/frmts/ers/frmt_ers.html b/bazaar/plugin/gdal/frmts/ers/frmt_ers.html new file mode 100644 index 000000000..3100f6b9b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ers/frmt_ers.html @@ -0,0 +1,45 @@ + + +ERS -- ERMapper .ERS + + + + +

ERS -- ERMapper .ERS

+ +GDAL supports reading and writing raster files with .ers header files, with some +limitations. The .ers ascii format is used by ERMapper for labelling raw +data files, as well as for providing extended metadata, and georeferencing +for some other file formats. The .ERS format, or variants are also used to +hold descriptions of ERMapper algorithms, but these are not supported by GDAL.

+ +Starting with GDAL 1.9.0, the PROJ, DATUM and UNITS values found in the ERS header are reported +in the ERS metadata domain.

+ +

Creation Issues

+ +Creation Options:

+ +

    + +
  • PIXELTYPE=value:.By setting this to SIGNEDBYTE, a new Byte file can be forced to be written as signed byte

    + +

  • PROJ=name: (GDAL >= 1.9.0) Name of the ERS projection string to use. +Common examples are NUTM11, or GEODETIC. If defined, this will override the value computed by SetProjection() or SetGCPs().

    + +

  • DATUM=name: (GDAL >= 1.9.0) Name of the ERS datum string to use. +Common examples are WGS84 or NAD83. If defined, this will override the value computed by SetProjection() or SetGCPs().

    + +

  • UNITS=name: (GDAL >= 1.9.0) Name of the ERS projection units to use : +METERS (default) or FEET (us-foot). If defined, this will override the value computed by SetProjection() or SetGCPs().

    + +

+ +

See Also:

+ +
    +
  • Implemented as gdal/frmts/ers/ersdataset.cpp.

    +

+ + + diff --git a/bazaar/plugin/gdal/frmts/ers/makefile.vc b/bazaar/plugin/gdal/frmts/ers/makefile.vc new file mode 100644 index 000000000..315f877e0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ers/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ersdataset.obj ershdrnode.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I..\raw + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/fit/GNUmakefile b/bazaar/plugin/gdal/frmts/fit/GNUmakefile new file mode 100644 index 000000000..d0055e503 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/fit/GNUmakefile @@ -0,0 +1,15 @@ + +include ../../GDALmake.opt + +OBJ = fitdataset.o fit.o + +CPPFLAGS := $(XTRA_OPT) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/fit/fit.cpp b/bazaar/plugin/gdal/frmts/fit/fit.cpp new file mode 100644 index 000000000..9a0d5610d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/fit/fit.cpp @@ -0,0 +1,197 @@ +/****************************************************************************** + * $Id: fit.cpp 14048 2008-03-20 18:47:21Z rouault $ + * + * Project: FIT Driver + * Purpose: Implement FIT Support - not using the SGI iflFIT library. + * Author: Philip Nemec, nemec@keyholecorp.com + * + ****************************************************************************** + * Copyright (c) 2001, Keyhole, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "fit.h" + +CPL_CVSID("$Id: fit.cpp 14048 2008-03-20 18:47:21Z rouault $"); + +GDALDataType fitDataType(int dtype) { + switch (dtype) { + case 1: // iflBit /* single-bit */ + fprintf(stderr, + "GDAL unsupported data type (single-bit) in fitDataType\n"); + return GDT_Unknown; + case 2: // iflUChar /* unsigned character (byte) */ + return GDT_Byte; + case 4: // iflChar /* signed character (byte) */ + fprintf(stderr, + "GDAL unsupported data type (signed char) in fitDataType\n"); + return GDT_Unknown; +// return Byte; + case 8: // iflUShort /* unsigned short integer (nominally 16 bits) */ + return GDT_UInt16; + case 16: // iflShort /* signed short integer */ + return GDT_Int16; + case 32: // iflUInt /* unsigned integer (nominally 32 bits) */ +// case 32: // iflULong /* deprecated, same as iflUInt */ + return GDT_UInt32; + break; + case 64: // iflInt /* integer */ +// case 64: // iflLong /* deprecated, same as iflULong */ + return GDT_Int32; + case 128: // iflFloat /* floating point */ + return GDT_Float32; + case 256: // iflDouble /* double precision floating point */ + return GDT_Float64; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "FIT - unknown data type %i in fitDataType", dtype); + return GDT_Unknown; + } // switch +} + +int fitGetDataType(GDALDataType eDataType) { + switch (eDataType) { + case GDT_Byte: + return 2; // iflUChar - unsigned character (byte) + case GDT_UInt16: + return 8; // iflUShort - unsigned short integer (nominally 16 bits) + case GDT_Int16: + return 16; // iflShort - signed short integer + case GDT_UInt32: + return 32; // iflUInt - unsigned integer (nominally 32 bits) + case GDT_Int32: + return 64; // iflInt - integer + case GDT_Float32: + return 128; // iflFloat - floating point + case GDT_Float64: + return 256; // iflDouble - double precision floating point + default: + CPLError(CE_Failure, CPLE_NotSupported, + "FIT - unsupported GDALDataType %i in fitGetDataType", + eDataType); + return 0; + } // switch +} + +#define UNSUPPORTED_COMBO() \ + CPLError(CE_Failure, CPLE_NotSupported, \ + "FIT write - unsupported combination (band 1 = %s " \ + "and %i bands) - ignoring color model", \ + GDALGetColorInterpretationName(colorInterp), nBands); \ + return 0 + + +int fitGetColorModel(GDALColorInterp colorInterp, int nBands) { + // XXX - shoould check colorInterp for all bands, not just first one + + switch(colorInterp) { + case GCI_GrayIndex: + switch (nBands) { + case 1: + return 2; // iflLuminance - luminance + case 2: + return 13; // iflLuminanceAlpha - Luminance plus alpha + default: + UNSUPPORTED_COMBO(); + } // switch + + case GCI_PaletteIndex: + CPLError(CE_Failure, CPLE_NotSupported, + "FIT write - unsupported ColorInterp PaletteIndex\n"); + return 0; + + case GCI_RedBand: + switch (nBands) { + case 3: + return 3; // iflRGB - full color (Red, Green, Blue triplets) + case 4: + return 5; // iflRGBA - full color with transparency (alpha channel) + default: + UNSUPPORTED_COMBO(); + } // switch + + case GCI_BlueBand: + switch (nBands) { + case 3: + return 9; // iflBGR - full color (ordered Blue, Green, Red) + default: + UNSUPPORTED_COMBO(); + } // switch + + case GCI_AlphaBand: + switch (nBands) { + case 4: + return 10; // iflABGR - Alpha, Blue, Green, Red (SGI frame buffers) + default: + UNSUPPORTED_COMBO(); + } // switch + + case GCI_HueBand: + switch (nBands) { + case 3: + return 6; // iflHSV - Hue, Saturation, Value + default: + UNSUPPORTED_COMBO(); + } // switch + + case GCI_CyanBand: + switch (nBands) { + case 3: + return 7; // iflCMY - Cyan, Magenta, Yellow + case 4: + return 8; // iflCMYK - Cyan, Magenta, Yellow, Black + default: + UNSUPPORTED_COMBO(); + } // switch + + case GCI_GreenBand: + case GCI_SaturationBand: + case GCI_LightnessBand: + case GCI_MagentaBand: + case GCI_YellowBand: + case GCI_BlackBand: + CPLError(CE_Failure, CPLE_NotSupported, + "FIT write - unsupported combination (band 1 = %s) " + "- ignoring color model", + GDALGetColorInterpretationName(colorInterp)); + return 0; + + default: + CPLDebug("FIT write", "unrecognized colorInterp %i - deriving from " + "number of bands (%i)", colorInterp, nBands); + switch (nBands) { + case 1: + return 2; // iflLuminance - luminance + case 2: + return 13; // iflLuminanceAlpha - Luminance plus alpha + case 3: + return 3; // iflRGB - full color (Red, Green, Blue triplets) + case 4: + return 5; // iflRGBA - full color with transparency (alpha channel) + } // switch + + CPLError(CE_Failure, CPLE_NotSupported, + "FIT write - unrecognized colorInterp %i and " + "unrecognized number of bands (%i)", colorInterp, nBands); + + return 0; + } // switch +} diff --git a/bazaar/plugin/gdal/frmts/fit/fit.h b/bazaar/plugin/gdal/frmts/fit/fit.h new file mode 100644 index 000000000..1315784b2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/fit/fit.h @@ -0,0 +1,115 @@ +/****************************************************************************** + * $Id: fit.h 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: FIT Driver + * Purpose: Implement FIT Support - not using the SGI iflFIT library. + * Author: Philip Nemec, nemec@keyholecorp.com + * + ****************************************************************************** + * Copyright (c) 2001, Keyhole, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __FIT_H__ +#define __FIT_H__ + +#include "gdal.h" + +struct FITinfo { + unsigned short magic; // file ident + unsigned short version; // file version + unsigned int xSize; // image size + unsigned int ySize; + unsigned int zSize; + unsigned int cSize; + int dtype; // data type + int order; // RGBRGB.. or RR..GG..BB.. + int space; // coordinate space + int cm; // color model + unsigned int xPageSize; // page size + unsigned int yPageSize; + unsigned int zPageSize; + unsigned int cPageSize; + // NOTE: a word of padding is inserted here + // due to struct alignment rules + double minValue; // min/max pixel values + double maxValue; + unsigned int dataOffset; // offset to first page of data + + // non-header values + unsigned int userOffset; // offset to area of user data +}; + +struct FIThead02 { // file header for version 02 + unsigned short magic; // file ident + unsigned short version; // file version + unsigned int xSize; // image size + unsigned int ySize; + unsigned int zSize; + unsigned int cSize; + int dtype; // data type + int order; // RGBRGB.. or RR..GG..BB.. + int space; // coordinate space + int cm; // color model + unsigned int xPageSize; // page size + unsigned int yPageSize; + unsigned int zPageSize; + unsigned int cPageSize; + short _padding; // NOTE: a word of padding is inserted here + // due to struct alignment rules + double minValue; // min/max pixel values + double maxValue; + unsigned int dataOffset; // offset to first page of data + // user extensible area... +}; + + +struct FIThead01 { // file header for version 01 + unsigned short magic; // file ident + unsigned short version; // file version + unsigned int xSize; // image size + unsigned int ySize; + unsigned int zSize; + unsigned int cSize; + int dtype; // data type + int order; // RGBRGB.. or RR..GG..BB.. + int space; // coordinate space + int cm; // color model + unsigned int xPageSize; // page size + unsigned int yPageSize; + unsigned int zPageSize; + unsigned int cPageSize; + unsigned int dataOffset; // offset to first page of data + // user extensible area... +}; + +#ifdef __cplusplus +extern "C" { +#endif + +GDALDataType fitDataType(int dtype); +int fitGetDataType(GDALDataType eDataType); +int fitGetColorModel(GDALColorInterp colorInterp, int nBands); + +#ifdef __cplusplus +} +#endif + +#endif // __FIT_H__ diff --git a/bazaar/plugin/gdal/frmts/fit/fitdataset.cpp b/bazaar/plugin/gdal/frmts/fit/fitdataset.cpp new file mode 100644 index 000000000..9d9f4b66b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/fit/fitdataset.cpp @@ -0,0 +1,1376 @@ +/****************************************************************************** + * $Id: fitdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $ + * + * Project: FIT Driver + * Purpose: Implement FIT Support - not using the SGI iflFIT library. + * Author: Philip Nemec, nemec@keyholecorp.com + * + ****************************************************************************** + * Copyright (c) 2001, Keyhole, Inc. + * Copyright (c) 2007-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "fit.h" +#include "gstEndian.h" +#include "gdal_pam.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: fitdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $"); + +CPL_C_START + +void GDALRegister_FIT(void); +CPL_C_END + +#define FIT_WRITE + +#define FIT_PAGE_SIZE 128 + +using namespace gstEndian; + +/************************************************************************/ +/* ==================================================================== */ +/* FITDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class FITRasterBand; + +class FITDataset : public GDALPamDataset +{ + friend class FITRasterBand; + + VSILFILE *fp; + FITinfo *info; + double adfGeoTransform[6]; + + public: + FITDataset(); + ~FITDataset(); + static GDALDataset *Open( GDALOpenInfo * ); +// virtual CPLErr GetGeoTransform( double * ); +}; + +#ifdef FIT_WRITE +static GDALDataset *FITCreateCopy(const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); +#endif // FIT_WRITE + +/************************************************************************/ +/* ==================================================================== */ +/* FITRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class FITRasterBand : public GDALPamRasterBand +{ + friend class FITDataset; + + unsigned long recordSize; // number of bytes of a single page/block/record + unsigned long numXBlocks; // number of pages in the X direction + unsigned long numYBlocks; // number of pages in the Y direction + unsigned long bytesPerComponent; + unsigned long bytesPerPixel; + char *tmpImage; + +public: + + FITRasterBand( FITDataset *, int ); + ~FITRasterBand(); + + // should override RasterIO eventually. + + virtual CPLErr IReadBlock( int, int, void * ); +// virtual CPLErr WriteBlock( int, int, void * ); + virtual double GetMinimum( int *pbSuccess ); + virtual double GetMaximum( int *pbSuccess ); + virtual GDALColorInterp GetColorInterpretation(); +}; + + +/************************************************************************/ +/* FITRasterBand() */ +/************************************************************************/ + +FITRasterBand::FITRasterBand( FITDataset *poDS, int nBand ) : tmpImage( NULL ) + +{ + this->poDS = poDS; + this->nBand = nBand; + +/* -------------------------------------------------------------------- */ +/* Get the GDAL data type. */ +/* -------------------------------------------------------------------- */ + eDataType = fitDataType(poDS->info->dtype); + +/* -------------------------------------------------------------------- */ +/* Get the page sizes. */ +/* -------------------------------------------------------------------- */ + nBlockXSize = poDS->info->xPageSize; + nBlockYSize = poDS->info->yPageSize; + +/* -------------------------------------------------------------------- */ +/* Caculate the values for record offset calculations. */ +/* -------------------------------------------------------------------- */ + bytesPerComponent = (GDALGetDataTypeSize(eDataType) / 8); + bytesPerPixel = poDS->nBands * bytesPerComponent; + recordSize = bytesPerPixel * nBlockXSize * nBlockYSize; + numXBlocks = + (unsigned long) ceil((double) poDS->info->xSize / nBlockXSize); + numYBlocks = + (unsigned long) ceil((double) poDS->info->ySize / nBlockYSize); + + tmpImage = (char *) malloc(recordSize); + if (! tmpImage) + CPLError(CE_Fatal, CPLE_NotSupported, + "FITRasterBand couldn't allocate %lu bytes", recordSize); + +/* -------------------------------------------------------------------- */ +/* Set the access flag. For now we set it the same as the */ +/* whole dataset, but eventually this should take account of */ +/* locked channels, or read-only secondary data files. */ +/* -------------------------------------------------------------------- */ + /* ... */ +} + + +FITRasterBand::~FITRasterBand() +{ + if ( tmpImage ) + free ( tmpImage ); +} + + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +#define COPY_XFIRST(t) { \ + t *dstp = (t *) pImage; \ + t *srcp = (t *) tmpImage; \ + srcp += nBand-1; \ + long i = 0; \ + for(long y=ystart; y != ystop; y+= yinc) \ + for(long x=xstart; x != xstop; x+= xinc, i++) { \ + dstp[i] = srcp[(y * nBlockXSize + x) * \ + poFIT_DS->nBands]; \ + } \ + } + + +#define COPY_YFIRST(t) { \ + t *dstp = (t *) pImage; \ + t *srcp = (t *) tmpImage; \ + srcp += nBand-1; \ + long i = 0; \ + for(long x=xstart; x != xstop; x+= xinc, i++) \ + for(long y=ystart; y != ystop; y+= yinc) { \ + dstp[i] = srcp[(x * nBlockYSize + y) * \ + poFIT_DS->nBands]; \ + } \ + } + +CPLErr FITRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + FITDataset *poFIT_DS = (FITDataset *) poDS; + + uint64 tilenum = 0; + + switch (poFIT_DS->info->space) { + case 1: + // iflUpperLeftOrigin - from upper left corner + // scan right then down + tilenum = nBlockYOff * numXBlocks + nBlockXOff; + break; + case 2: + // iflUpperRightOrigin - from upper right corner + // scan left then down + tilenum = numYBlocks * numXBlocks + (numXBlocks-1-nBlockXOff); + break; + case 3: + // iflLowerRightOrigin - from lower right corner + // scan left then up + tilenum = (numYBlocks-1-nBlockYOff) * numXBlocks + + (numXBlocks-1-nBlockXOff); + break; + case 4: + // iflLowerLeftOrigin - from lower left corner + // scan right then up + tilenum = (numYBlocks-1-nBlockYOff) * numXBlocks + nBlockXOff; + break; + case 5: + // iflLeftUpperOrigin -* from upper left corner + // scan down then right + tilenum = nBlockXOff * numYBlocks + nBlockYOff; + break; + case 6: + // iflRightUpperOrigin - from upper right corner + // scan down then left + tilenum = (numXBlocks-1-nBlockXOff) * numYBlocks + nBlockYOff; + break; + case 7: + // iflRightLowerOrigin - from lower right corner + // scan up then left + tilenum = nBlockXOff * numYBlocks + (numYBlocks-1-nBlockYOff); + break; + case 8: + // iflLeftLowerOrigin -* from lower left corner + // scan up then right + tilenum = (numXBlocks-1-nBlockXOff) * numYBlocks + + (numYBlocks-1-nBlockYOff); + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "FIT - unrecognized image space %i", + poFIT_DS->info->space); + tilenum = 0; + } // switch + + uint64 offset = poFIT_DS->info->dataOffset + recordSize * tilenum; +// CPLDebug("FIT", "%i RasterBand::IReadBlock %i %i (out of %i %i) -- %i", +// poFIT_DS->info->space, +// nBlockXOff, nBlockYOff, numXBlocks, numYBlocks, tilenum); + + if ( VSIFSeekL( poFIT_DS->fp, offset, SEEK_SET ) == -1 ) { + CPLError(CE_Failure, CPLE_NotSupported, + "FIT - 64bit file seek failure, handle=%p", poFIT_DS->fp ); + return CE_Failure; + } + + // XXX - should handle status + // fast path is single component (ll?) - no copy needed + char *p; + int fastpath = FALSE; + + if ((poFIT_DS->nBands == 1) && (poFIT_DS->info->space == 1)) // upper left + fastpath = TRUE; + + if (! fastpath) { + VSIFReadL( tmpImage, recordSize, 1, poFIT_DS->fp ); + // offset to correct component to swap + p = (char *) tmpImage + nBand-1; + } + else { + VSIFReadL( pImage, recordSize, 1, poFIT_DS->fp ); + p = (char *) pImage; + } + + +#ifdef swapping + unsigned long i = 0; + + switch(bytesPerComponent) { + case 1: + // do nothing + break; + case 2: + for(i=0; i < recordSize; i+= bytesPerPixel) + gst_swap16(p + i); + break; + case 4: + for(i=0; i < recordSize; i+= bytesPerPixel) + gst_swap32(p + i); + break; + case 8: + for(i=0; i < recordSize; i+= bytesPerPixel) + gst_swap64(p + i); + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "FITRasterBand::IReadBlock unsupported bytesPerPixel %lu", + bytesPerComponent); + } // switch +#else + (void) p; // avoid warnings. +#endif // swapping + + if (! fastpath) { + long xinc, yinc, xstart, ystart, xstop, ystop; + if (poFIT_DS->info->space <= 4) { + // scan left/right first + + switch (poFIT_DS->info->space) { + case 1: + // iflUpperLeftOrigin - from upper left corner + // scan right then down + xinc = 1; + yinc = 1; + break; + case 2: + // iflUpperRightOrigin - from upper right corner + // scan left then down + xinc = -1; + yinc = 1; + break; + case 3: + // iflLowerRightOrigin - from lower right corner + // scan left then up + xinc = -1; + yinc = -1; + break; + case 4: + // iflLowerLeftOrigin - from lower left corner + // scan right then up + xinc = 1; + yinc = -1; + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "FIT - unrecognized image space %i", + poFIT_DS->info->space); + xinc = 1; + yinc = 1; + } // switch + + + if (xinc == 1) { + xstart = 0; + xstop = nBlockXSize; + } + else { + xstart = nBlockXSize-1; + xstop = -1; + } + if (yinc == 1) { + ystart = 0; + ystop = nBlockYSize; + } + else { + int localBlockYSize = nBlockYSize; + long maxy_full = + (long) floor(poFIT_DS->info->ySize / (double) nBlockYSize); + if (nBlockYOff >= maxy_full) + localBlockYSize = poFIT_DS->info->ySize % nBlockYSize; + ystart = localBlockYSize-1; + ystop = -1; + } + + switch(bytesPerComponent) { + case 1: + COPY_XFIRST(char); + break; + case 2: + COPY_XFIRST(uint16); + break; + case 4: + COPY_XFIRST(uint32); + break; + case 8: + COPY_XFIRST(uint64); + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "FITRasterBand::IReadBlock unsupported " + "bytesPerComponent %lu", bytesPerComponent); + } // switch + + } // scan left/right first + else { + // scan up/down first + + switch (poFIT_DS->info->space) { + case 5: + // iflLeftUpperOrigin -* from upper left corner + // scan down then right + xinc = 1; + yinc = 1; + break; + case 6: + // iflRightUpperOrigin - from upper right corner + // scan down then left + xinc = -1; + yinc = 1; + break; + case 7: + // iflRightLowerOrigin - from lower right corner + // scan up then left + xinc = -1; + yinc = -1; + break; + case 8: + // iflLeftLowerOrigin -* from lower left corner + // scan up then right + xinc = 1; + yinc = -1; + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "FIT - unrecognized image space %i", + poFIT_DS->info->space); + xinc = 1; + yinc = 1; + } // switch + + if (xinc == 1) { + xstart = 0; + xstop = nBlockXSize; + } + else { + int localBlockXSize = nBlockXSize; + long maxx_full = + (long) floor(poFIT_DS->info->xSize / (double) nBlockXSize); + if (nBlockXOff >= maxx_full) + localBlockXSize = poFIT_DS->info->xSize % nBlockXSize; + xstart = localBlockXSize-1; + xstop = -1; + } + if (yinc == 1) { + ystart = 0; + ystop = nBlockYSize; + } + else { + ystart = nBlockYSize-1; + ystop = -1; + } + + switch(bytesPerComponent) { + case 1: + COPY_YFIRST(char); + break; + case 2: + COPY_YFIRST(uint16); + break; + case 4: + COPY_YFIRST(uint32); + break; + case 8: + COPY_YFIRST(uint64); + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "FITRasterBand::IReadBlock unsupported " + "bytesPerComponent %lu", bytesPerComponent); + } // switch + + } // scan up/down first + + } // ! fastpath + return CE_None; +} + +#if 0 +/************************************************************************/ +/* ReadBlock() */ +/************************************************************************/ + +CPLErr FITRasterBand::ReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + FITDataset *poFIT_DS = (FITDataset *) poDS; + + + + return CE_None; +} + +/************************************************************************/ +/* WriteBlock() */ +/************************************************************************/ + +CPLErr FITRasterBand::WriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + FITDataset *poFIT_DS = (FITDataset *) poDS; + + + + return CE_None; +} +#endif + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double FITRasterBand::GetMinimum( int *pbSuccess ) +{ + FITDataset *poFIT_DS = (FITDataset *) poDS; + + if ((! poFIT_DS) || (! poFIT_DS->info)) + return GDALRasterBand::GetMinimum( pbSuccess ); + + if (pbSuccess) + *pbSuccess = TRUE; + + if (poFIT_DS->info->version && + EQUALN((const char *) &(poFIT_DS->info->version), "02", 2)) { + return poFIT_DS->info->minValue; + } + else { + return GDALRasterBand::GetMinimum( pbSuccess ); + } +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double FITRasterBand::GetMaximum( int *pbSuccess ) +{ + FITDataset *poFIT_DS = (FITDataset *) poDS; + + if ((! poFIT_DS) || (! poFIT_DS->info)) + return GDALRasterBand::GetMaximum( pbSuccess ); + + if (pbSuccess) + *pbSuccess = TRUE; + + if (EQUALN((const char *) &poFIT_DS->info->version, "02", 2)) { + return poFIT_DS->info->maxValue; + } + else { + return GDALRasterBand::GetMaximum( pbSuccess ); + } +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp FITRasterBand::GetColorInterpretation() +{ + FITDataset *poFIT_DS = (FITDataset *) poDS; + + if ((! poFIT_DS) || (! poFIT_DS->info)) + return GCI_Undefined; + + switch(poFIT_DS->info->cm) { + case 1: // iflNegative - inverted luminance (min value is white) + CPLError( CE_Warning, CPLE_NotSupported, + "FIT - color model Negative not supported - ignoring model"); + return GCI_Undefined; + + case 2: // iflLuminance - luminance + if (poFIT_DS->nBands != 1) { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model Luminance mismatch with %i bands", + poFIT_DS->nBands); + return GCI_Undefined; + } + switch (nBand) { + case 1: + return GCI_GrayIndex; + default: + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model Luminance unknown band %i", nBand); + return GCI_Undefined; + } // switch nBand + + case 3: // iflRGB - full color (Red, Green, Blue triplets) + if (poFIT_DS->nBands != 3) { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model RGB mismatch with %i bands", + poFIT_DS->nBands); + return GCI_Undefined; + } + switch (nBand) { + case 1: + return GCI_RedBand; + case 2: + return GCI_GreenBand; + case 3: + return GCI_BlueBand; + default: + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model RGB unknown band %i", nBand); + return GCI_Undefined; + } // switch nBand + + case 4: // iflRGBPalette - color mapped values + CPLError( CE_Warning, CPLE_NotSupported, + "FIT - color model RGBPalette not supported - " + "ignoring model"); + return GCI_Undefined; + + case 5: // iflRGBA - full color with transparency (alpha channel) + if (poFIT_DS->nBands != 4) { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model RGBA mismatch with %i bands", + poFIT_DS->nBands); + return GCI_Undefined; + } + switch (nBand) { + case 1: + return GCI_RedBand; + case 2: + return GCI_GreenBand; + case 3: + return GCI_BlueBand; + case 4: + return GCI_AlphaBand; + default: + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model RGBA unknown band %i", nBand); + return GCI_Undefined; + } // switch nBand + + case 6: // iflHSV - Hue, Saturation, Value + if (poFIT_DS->nBands != 3) { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model HSV mismatch with %i bands", + poFIT_DS->nBands); + return GCI_Undefined; + } + switch (nBand) { + case 1: + return GCI_HueBand; + case 2: + return GCI_SaturationBand; + case 3: + return GCI_LightnessBand; + default: + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model HSV unknown band %i", nBand); + return GCI_Undefined; + } // switch nBand + + case 7: // iflCMY - Cyan, Magenta, Yellow + if (poFIT_DS->nBands != 3) { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model CMY mismatch with %i bands", + poFIT_DS->nBands); + return GCI_Undefined; + } + switch (nBand) { + case 1: + return GCI_CyanBand; + case 2: + return GCI_MagentaBand; + case 3: + return GCI_YellowBand; + default: + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model CMY unknown band %i", nBand); + return GCI_Undefined; + } // switch nBand + + case 8: // iflCMYK - Cyan, Magenta, Yellow, Black + if (poFIT_DS->nBands != 4) { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model CMYK mismatch with %i bands", + poFIT_DS->nBands); + return GCI_Undefined; + } + switch (nBand) { + case 1: + return GCI_CyanBand; + case 2: + return GCI_MagentaBand; + case 3: + return GCI_YellowBand; + case 4: + return GCI_BlackBand; + default: + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model CMYK unknown band %i", nBand); + return GCI_Undefined; + } // switch nBand + + case 9: // iflBGR - full color (ordered Blue, Green, Red) + if (poFIT_DS->nBands != 3) { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model BGR mismatch with %i bands", + poFIT_DS->nBands); + return GCI_Undefined; + } + switch (nBand) { + case 1: + return GCI_BlueBand; + case 2: + return GCI_GreenBand; + case 3: + return GCI_RedBand; + default: + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model BGR unknown band %i", nBand); + return GCI_Undefined; + } // switch nBand + + case 10: // iflABGR - Alpha, Blue, Green, Red (SGI frame buffers) + if (poFIT_DS->nBands != 4) { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model ABGR mismatch with %i bands", + poFIT_DS->nBands); + return GCI_Undefined; + } + switch (nBand) { + case 1: + return GCI_AlphaBand; + case 2: + return GCI_BlueBand; + case 3: + return GCI_GreenBand; + case 4: + return GCI_RedBand; + default: + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model ABGR unknown band %i", nBand); + return GCI_Undefined; + } // switch nBand + + case 11: // iflMultiSpectral - multi-spectral data, arbitrary number of + // chans + return GCI_Undefined; + + case 12: // iflYCC PhotoCD color model (Luminance, Chrominance) + CPLError( CE_Warning, CPLE_NotSupported, + "FIT - color model YCC not supported - ignoring model"); + return GCI_Undefined; + + case 13: // iflLuminanceAlpha - Luminance plus alpha + if (poFIT_DS->nBands != 2) { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model LuminanceAlpha mismatch with " + "%i bands", + poFIT_DS->nBands); + return GCI_Undefined; + } + switch (nBand) { + case 1: + return GCI_GrayIndex; + case 2: + return GCI_AlphaBand; + default: + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - color model LuminanceAlpha unknown band %i", + nBand); + return GCI_Undefined; + } // switch nBand + + default: + CPLError( CE_Warning, CPLE_NotSupported, + "FIT - unrecognized color model %i - ignoring model", + poFIT_DS->info->cm); + return GCI_Undefined; + } // switch +} + +/************************************************************************/ +/* FITDataset() */ +/************************************************************************/ + +FITDataset::FITDataset() : fp( NULL ), info( NULL ) +{ + + adfGeoTransform[0] = 0.0; // x origin (top left corner) + adfGeoTransform[1] = 1.0; // x pixel size + adfGeoTransform[2] = 0.0; + + adfGeoTransform[3] = 0.0; // y origin (top left corner) + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; // y pixel size +} + +/************************************************************************/ +/* ~FITDataset() */ +/************************************************************************/ + +FITDataset::~FITDataset() +{ + FlushCache(); + if (info) + delete(info); + if(fp) + VSIFCloseL(fp); +} + +// simple guard object to delete memory +// when the guard goes out of scope +template< class T > +class DeleteGuard +{ +public: + DeleteGuard( T *p ) : _ptr( p ) { } + ~DeleteGuard() + { + delete _ptr; + } + + T *take() + { + T *tmp = _ptr; + _ptr = 0; + return tmp; + } + +private: + T *_ptr; + // prevent default copy constructor and assignment operator + DeleteGuard( const DeleteGuard & ); + DeleteGuard &operator=( const DeleteGuard & ); +}; + +// simple guard object to free memory +// when the guard goes out of scope +template< class T > +class FreeGuard +{ +public: + FreeGuard( T *p ) : _ptr( p ) { } + ~FreeGuard() + { + if ( _ptr ) + free( _ptr ); + } + + T *take() + { + T *tmp = _ptr; + _ptr = 0; + return tmp; + } + +private: + T *_ptr; + // prevent default copy constructor and assignment operator + FreeGuard( const FreeGuard & ); + FreeGuard &operator=( const FreeGuard & ); +}; + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *FITDataset::Open( GDALOpenInfo * poOpenInfo ) +{ +/* -------------------------------------------------------------------- */ +/* First we check to see if the file has the expected header */ +/* bytes. */ +/* -------------------------------------------------------------------- */ + + if( poOpenInfo->nHeaderBytes < 5 ) + return NULL; + + + if( !EQUALN((const char *) poOpenInfo->pabyHeader, "IT01", 4) && + !EQUALN((const char *) poOpenInfo->pabyHeader, "IT02", 4) ) + return NULL; + + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The FIT driver does not support update access to existing" + " files.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + FITDataset *poDS; + + poDS = new FITDataset(); + DeleteGuard guard( poDS ); + + // re-open file for large file (64bit) access + if ( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" ); + + if ( !poDS->fp ) { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to re-open %s with FIT driver.\n", + poOpenInfo->pszFilename ); + return NULL; + } + poDS->eAccess = poOpenInfo->eAccess; + + + poDS->info = new FITinfo; + FITinfo *info = poDS->info; + +/* -------------------------------------------------------------------- */ +/* Read other header values. */ +/* -------------------------------------------------------------------- */ + FIThead02 *head = (FIThead02 *) poOpenInfo->pabyHeader; + + // extract the image attributes from the file header + if (EQUALN((const char *) &head->version, "02", 2)) { + // incomplete header + if( poOpenInfo->nHeaderBytes < (signed) sizeof(FIThead02) ) + return NULL; + + CPLDebug("FIT", "Loading file with header version 02"); + + gst_swapb(head->minValue); + info->minValue = head->minValue; + gst_swapb(head->maxValue); + info->maxValue = head->maxValue; + gst_swapb(head->dataOffset); + info->dataOffset = head->dataOffset; + + info->userOffset = sizeof(FIThead02); + } + else if (EQUALN((const char *) &head->version, "01", 2)) { + // incomplete header + if( poOpenInfo->nHeaderBytes < (signed) sizeof(FIThead01) ) + return NULL; + + CPLDebug("FIT", "Loading file with header version 01"); + + // map old style header into new header structure + FIThead01* head01 = (FIThead01*)head; + gst_swapb(head->dataOffset); + info->dataOffset = head01->dataOffset; + + info->userOffset = sizeof(FIThead01); + } + else { + // unrecognized header version + CPLError( CE_Failure, CPLE_NotSupported, + "FIT - unsupported header version %.2s\n", + (const char*) &head->version); + return NULL; + } + + CPLDebug("FIT", "userOffset %i, dataOffset %i", + info->userOffset, info->dataOffset); + + info->magic = head->magic; + info->version = head->version; + + gst_swapb(head->xSize); + info->xSize = head->xSize; + gst_swapb(head->ySize); + info->ySize = head->ySize; + gst_swapb(head->zSize); + info->zSize = head->zSize; + gst_swapb(head->cSize); + info->cSize = head->cSize; + gst_swapb(head->dtype); + info->dtype = head->dtype; + gst_swapb(head->order); + info->order = head->order; + gst_swapb(head->space); + info->space = head->space; + gst_swapb(head->cm); + info->cm = head->cm; + gst_swapb(head->xPageSize); + info->xPageSize = head->xPageSize; + gst_swapb(head->yPageSize); + info->yPageSize = head->yPageSize; + gst_swapb(head->zPageSize); + info->zPageSize = head->zPageSize; + gst_swapb(head->cPageSize); + info->cPageSize = head->cPageSize; + + CPLDebug("FIT", "size %i %i %i %i, pageSize %i %i %i %i", + info->xSize, info->ySize, info->zSize, info->cSize, + info->xPageSize, info->yPageSize, info->zPageSize, + info->cPageSize); + + CPLDebug("FIT", "dtype %i order %i space %i cm %i", + info->dtype, info->order, info->space, info->cm); + + /**************************/ + + poDS->nRasterXSize = head->xSize; + poDS->nRasterYSize = head->ySize; + poDS->nBands = head->cSize; + +/* -------------------------------------------------------------------- */ +/* Check if 64 bit seek is needed. */ +/* -------------------------------------------------------------------- */ + uint64 bytesPerComponent = + (GDALGetDataTypeSize(fitDataType(poDS->info->dtype)) / 8); + uint64 bytesPerPixel = head->cSize * bytesPerComponent; + uint64 recordSize = bytesPerPixel * head->xPageSize * + head->yPageSize; + uint64 numXBlocks = + (uint64) ceil((double) head->xSize / head->xPageSize); + uint64 numYBlocks = + (uint64) ceil((double) head->ySize / head->yPageSize); + + uint64 maxseek = recordSize * numXBlocks * numYBlocks; + +// CPLDebug("FIT", "(sizeof %i) max seek %llx ==> %llx\n", sizeof(uint64), +// maxseek, maxseek >> 31); + if (maxseek >> 31) // signed long +#ifdef VSI_LARGE_API_SUPPORTED + CPLDebug("FIT", "Using 64 bit version of fseek"); +#else + CPLError(CE_Fatal, CPLE_NotSupported, + "FIT - need 64 bit version of fseek"); +#endif + +/* -------------------------------------------------------------------- */ +/* Verify all "unused" header values. */ +/* -------------------------------------------------------------------- */ + + if( info->zSize != 1 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT driver - unsupported zSize %i\n", info->zSize); + return NULL; + } + + if( info->order != 1 ) // interleaved - RGBRGB + { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT driver - unsupported order %i\n", info->order); + return NULL; + } + + if( info->zPageSize != 1 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT driver - unsupported zPageSize %i\n", info->zPageSize); + return NULL; + } + + if( info->cPageSize != info->cSize ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT driver - unsupported cPageSize %i (!= %i)\n", + info->cPageSize, info->cSize); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( int i = 0; i < poDS->nBands; i++ ) + { + poDS->SetBand( i+1, new FITRasterBand( poDS, i+1 ) ) ; + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return guard.take(); +} + +/************************************************************************/ +/* FITCreateCopy() */ +/************************************************************************/ + +#ifdef FIT_WRITE +static GDALDataset *FITCreateCopy(const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + CPLDebug("FIT", "CreateCopy %s - %i", pszFilename, bStrict); + + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "FIT driver does not support source dataset with zero band.\n"); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpImage; + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return NULL; + } + + fpImage = VSIFOpenL( pszFilename, "wb" ); + if( fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "FIT - unable to create file %s.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Generate header. */ +/* -------------------------------------------------------------------- */ + // XXX - should FIT_PAGE_SIZE be based on file page size ?? + int size = MAX(sizeof(FIThead02), FIT_PAGE_SIZE); + FIThead02 *head = (FIThead02 *) malloc(size); + FreeGuard guardHead( head ); + + // clean header so padding (past real header) is all zeros + memset( head, 0, size ); + + strncpy((char *) &head->magic, "IT", 2); + strncpy((char *) &head->version, "02", 2); + + head->xSize = poSrcDS->GetRasterXSize(); + gst_swapb(head->xSize); + head->ySize = poSrcDS->GetRasterYSize(); + gst_swapb(head->ySize); + head->zSize = 1; + gst_swapb(head->zSize); + + head->cSize = nBands; + gst_swapb(head->cSize); + + GDALRasterBand *firstBand = poSrcDS->GetRasterBand(1); + if (! firstBand) { + VSIFCloseL(fpImage); + return NULL; + } + + head->dtype = fitGetDataType(firstBand->GetRasterDataType()); + if (! head->dtype) { + VSIFCloseL(fpImage); + return NULL; + } + gst_swapb(head->dtype); + head->order = 1; // interleaved - RGBRGB + gst_swapb(head->order); + head->space = 1; // upper left + gst_swapb(head->space); + + // XXX - need to check all bands + head->cm = fitGetColorModel(firstBand->GetColorInterpretation(), nBands); + gst_swapb(head->cm); + + int blockX, blockY; + firstBand->GetBlockSize(&blockX, &blockY); + CPLDebug("FIT write", "inherited block size %ix%i", blockX, blockY); + + if( CSLFetchNameValue(papszOptions,"PAGESIZE") != NULL ) + { + const char *str = CSLFetchNameValue(papszOptions,"PAGESIZE"); + int newBlockX, newBlockY; + sscanf(str, "%i,%i", &newBlockX, &newBlockY); + if (newBlockX && newBlockY) { + blockX = newBlockX; + blockY = newBlockY; + } + else { + CPLError(CE_Failure, CPLE_OpenFailed, + "FIT - Unable to parse option PAGESIZE values [%s]", str); + } + } + + // XXX - need to do lots of checking of block size + // * provide ability to override block size with options + // * handle non-square block size (like scanline) + // - probably default from non-tiled image - have default block size + // * handle block size bigger than image size + // * undesirable block size (non power of 2, others?) + // * mismatched block sizes for different bands + // * image that isn't even pages (ie. partially empty pages at edge) + CPLDebug("FIT write", "using block size %ix%i", blockX, blockY); + + head->xPageSize = blockX; + gst_swapb(head->xPageSize); + head->yPageSize = blockY; + gst_swapb(head->yPageSize); + head->zPageSize = 1; + gst_swapb(head->zPageSize); + head->cPageSize = nBands; + gst_swapb(head->cPageSize); + + // XXX - need to check all bands + head->minValue = firstBand->GetMinimum(); + gst_swapb(head->minValue); + // XXX - need to check all bands + head->maxValue = firstBand->GetMaximum(); + gst_swapb(head->maxValue); + head->dataOffset = size; + gst_swapb(head->dataOffset); + + VSIFWriteL(head, size, 1, fpImage); + +/* -------------------------------------------------------------------- */ +/* Loop over image, copying image data. */ +/* -------------------------------------------------------------------- */ + unsigned long bytesPerComponent = + (GDALGetDataTypeSize(firstBand->GetRasterDataType()) / 8); + unsigned long bytesPerPixel = nBands * bytesPerComponent; + + unsigned long pageBytes = blockX * blockY * bytesPerPixel; + char *output = (char *) malloc(pageBytes); + if (! output) + CPLError(CE_Fatal, CPLE_NotSupported, + "FITRasterBand couldn't allocate %lu bytes", pageBytes); + FreeGuard guardOutput( output ); + + long maxx = (long) ceil(poSrcDS->GetRasterXSize() / (double) blockX); + long maxy = (long) ceil(poSrcDS->GetRasterYSize() / (double) blockY); + long maxx_full = (long) floor(poSrcDS->GetRasterXSize() / (double) blockX); + long maxy_full = (long) floor(poSrcDS->GetRasterYSize() / (double) blockY); + + CPLDebug("FIT", "about to write %ld x %ld blocks", maxx, maxy); + + for(long y=0; y < maxy; y++) + for(long x=0; x < maxx; x++) { + long readX = blockX; + long readY = blockY; + int do_clean = FALSE; + + // handle cases where image size isn't an exact multiple + // of page size + if (x >= maxx_full) { + readX = poSrcDS->GetRasterXSize() % blockX; + do_clean = TRUE; + } + if (y >= maxy_full) { + readY = poSrcDS->GetRasterYSize() % blockY; + do_clean = TRUE; + } + + // clean out image if only doing partial reads + if (do_clean) + memset( output, 0, pageBytes ); + + for( int iBand = 0; iBand < nBands; iBand++ ) { + GDALRasterBand * poBand = poSrcDS->GetRasterBand( iBand+1 ); + CPLErr eErr = + poBand->RasterIO( GF_Read, // eRWFlag + x * blockX, // nXOff + y * blockY, // nYOff + readX, // nXSize + readY, // nYSize + output + iBand * bytesPerComponent, + // pData + blockX, // nBufXSize + blockY, // nBufYSize + firstBand->GetRasterDataType(), + // eBufType + bytesPerPixel, // nPixelSpace + bytesPerPixel * blockX, NULL); // nLineSpace + if (eErr != CE_None) + CPLError(CE_Failure, CPLE_FileIO, + "FIT write - CreateCopy got read error %i", eErr); + } // for iBand + +#ifdef swapping + char *p = output; + unsigned long i; + switch(bytesPerComponent) { + case 1: + // do nothing + break; + case 2: + for(i=0; i < pageBytes; i+= bytesPerComponent) + gst_swap16(p + i); + break; + case 4: + for(i=0; i < pageBytes; i+= bytesPerComponent) + gst_swap32(p + i); + break; + case 8: + for(i=0; i < pageBytes; i+= bytesPerComponent) + gst_swap64(p + i); + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "FIT write - unsupported bytesPerPixel %lu", + bytesPerComponent); + } // switch +#endif // swapping + + VSIFWriteL(output, pageBytes, 1, fpImage); + + double perc = ((double) (y * maxx + x)) / (maxx * maxy); +// printf("progress %f\n", perc); + if( !pfnProgress( perc, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + //free(output); + VSIFCloseL( fpImage ); + VSIUnlink( pszFilename ); + return NULL; + } + } // for x + + //free(output); + + VSIFCloseL( fpImage ); + + pfnProgress( 1.0, NULL, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + GDALPamDataset *poDS = (GDALPamDataset *) + GDALOpen( pszFilename, GA_ReadOnly ); + + if( poDS ) + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} +#endif // FIT_WRITE + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +// CPLErr FITDataset::GetGeoTransform( double * padfTransform ) +// { +// CPLDebug("FIT", "FITDataset::GetGeoTransform"); +// memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); +// return( CE_None ); +// } + +/************************************************************************/ +/* GDALRegister_FIT() */ +/************************************************************************/ + +void GDALRegister_FIT() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "FIT" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "FIT" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "FIT Image" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = FITDataset::Open; +#ifdef FIT_WRITE + poDriver->pfnCreateCopy = FITCreateCopy; + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16 Int16 UInt32 Int32 Float32 Float64" ); +#endif // FIT_WRITE + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/fit/gstEndian.h b/bazaar/plugin/gdal/frmts/fit/gstEndian.h new file mode 100644 index 000000000..a91763c83 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/fit/gstEndian.h @@ -0,0 +1,127 @@ +/****************************************************************************** + * $Id: gstEndian.h 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: FIT Driver + * Purpose: Implement FIT Support - not using the SGI iflFIT library. + * Author: Philip Nemec, nemec@keyholecorp.com + * + ****************************************************************************** + * Copyright (c) 2001, Keyhole, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _gstEndian_h_ +#define _gstEndian_h_ + +// endian swapping tools + +#include +#include + +#include "gstTypes.h" + +namespace gstEndian { + +// have to do swapping on Linux and Windows +#ifdef CPL_LSB +#define swapping +#else +#endif + +#ifdef swapping +size_t swapped_fread(void *ptr, size_t size, size_t nitems, FILE *stream); +size_t swapped_fwrite(const void *ptr, size_t size, size_t nitems, FILE + *stream); + +static inline void gst_swap64(void * value) +{ + // 0x1122334455667788 --> 0x8877665544332211 + + *(uint64 *)(value) = + ( ((*(uint64 *)(value) & 0x00000000000000ff) << 56) | + ((*(uint64 *)(value) & 0x000000000000ff00) << 40) | + ((*(uint64 *)(value) & 0x0000000000ff0000) << 24) | + ((*(uint64 *)(value) & 0x00000000ff000000) << 8) | + ((*(uint64 *)(value) >> 8) & 0x00000000ff000000) | + ((*(uint64 *)(value) >> 24) & 0x0000000000ff0000) | + ((*(uint64 *)(value) >> 40) & 0x000000000000ff00) | + ((*(uint64 *)(value) >> 56) & 0x00000000000000ff) ); +} + +static inline void gst_swap32(void * value) +{ + // 0x12 34 56 78 --> 0x78 56 34 12 + + *(uint32 *)(value) = + ( ((*(uint32 *)(value) & 0x000000ff) << 24) | + ((*(uint32 *)(value) & 0x0000ff00) << 8) | + ((*(uint32 *)(value) >> 8) & 0x0000ff00) | + ((*(uint32 *)(value) >> 24) & 0x000000ff) ); +} + +static inline void gst_swap16(void * value) +{ + *(uint16 *)(value) = + ( ((*(uint16 *)(value) & 0x00ff) << 8) | + ((*(uint16 *)(value) >> 8) & 0x00ff) ); +} + +static inline void gst_swapbytes(void * value, int size) +{ + switch (size) { + case 1: + // do nothing + break; + case 2: + gst_swap16(value); + break; + case 4: + gst_swap32(value); + break; + case 8: + gst_swap64(value); + break; + default: + fprintf(stderr, "gst_swapbytes unsupported size %i - not swapping\n", + size); + break; + } // switch +} + +#define gst_swapb( value ) gst_swapbytes( &value, sizeof(value)) + +#else // swapping + +#define swapped_fread(ptr, size, nitems, stream) \ + fread(ptr, size, nitems, stream) +#define swapped_fwrite(ptr, size, nitems, stream) \ + fwrite(ptr, size, nitems, stream) + +#define gst_swap64( vlaue ) +#define gst_swap32( vlaue ) +#define gst_swap16( vlaue ) +#define gst_swapbytes( value, size ) +#define gst_swapb( value ) + +#endif // swapping + +} // gstEndian namespace + +#endif // ! _gstEndian_h_ diff --git a/bazaar/plugin/gdal/frmts/fit/gstTypes.h b/bazaar/plugin/gdal/frmts/fit/gstTypes.h new file mode 100644 index 000000000..bd34a730c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/fit/gstTypes.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * $Id: gstTypes.h 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: FIT Driver + * Purpose: Implement FIT Support - not using the SGI iflFIT library. + * Author: Philip Nemec, nemec@keyholecorp.com + * + ****************************************************************************** + * Copyright (c) 2001, Keyhole, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _gstTypes_h_ +#define _gstTypes_h_ + +#include +#include "cpl_conv.h" + +typedef int (*gstItemGetFunc)(void *data, int tag, ...); + +typedef GUInt16 uint16; +typedef GInt16 int16; +typedef GUInt32 uint32; +typedef GInt32 int32; +typedef GUIntBig uint64; +typedef GIntBig int64; + +typedef unsigned char uchar; + +#endif // !_gstTypes_h_ diff --git a/bazaar/plugin/gdal/frmts/fit/makefile.vc b/bazaar/plugin/gdal/frmts/fit/makefile.vc new file mode 100644 index 000000000..795a73d20 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/fit/makefile.vc @@ -0,0 +1,14 @@ + +OBJ = \ + fit.obj fitdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/fits/GNUmakefile b/bazaar/plugin/gdal/frmts/fits/GNUmakefile new file mode 100644 index 000000000..d14fce73d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/fits/GNUmakefile @@ -0,0 +1,15 @@ + +include ../../GDALmake.opt + +OBJ = fitsdataset.o + +FITS_OPTS = + +CPPFLAGS := $(FITS_OPTS) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/fits/fitsdataset.cpp b/bazaar/plugin/gdal/frmts/fits/fitsdataset.cpp new file mode 100644 index 000000000..124e5193f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/fits/fitsdataset.cpp @@ -0,0 +1,626 @@ +/****************************************************************************** + * $Id: fitsdataset.cpp 28831 2015-04-01 16:46:05Z rouault $ + * + * Project: FITS Driver + * Purpose: Implement FITS raster read/write support + * Author: Simon Perkins, s.perkins@lanl.gov + * + ****************************************************************************** + * Copyright (c) 2001, Simon Perkins + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + + +#include "gdal_pam.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: fitsdataset.cpp 28831 2015-04-01 16:46:05Z rouault $"); + +CPL_C_START +#include +void GDALRegister_FITS(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* FITSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class FITSRasterBand; + +class FITSDataset : public GDALPamDataset { + + friend class FITSRasterBand; + + fitsfile* hFITS; + + GDALDataType gdalDataType; // GDAL code for the image type + int fitsDataType; // FITS code for the image type + + bool isExistingFile; + long highestOffsetWritten; // How much of image has been written + + FITSDataset(); // Others shouldn't call this constructor explicitly + + CPLErr Init(fitsfile* hFITS_, bool isExistingFile_); + +public: + ~FITSDataset(); + + static GDALDataset* Open(GDALOpenInfo* ); + static GDALDataset* Create(const char* pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char** papszParmList); + +}; + +/************************************************************************/ +/* ==================================================================== */ +/* FITSRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class FITSRasterBand : public GDALPamRasterBand { + + friend class FITSDataset; + +public: + + FITSRasterBand(FITSDataset*, int); + ~FITSRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); +}; + + +/************************************************************************/ +/* FITSRasterBand() */ +/************************************************************************/ + +FITSRasterBand::FITSRasterBand(FITSDataset *poDS, int nBand) { + + this->poDS = poDS; + this->nBand = nBand; + eDataType = poDS->gdalDataType; + nBlockXSize = poDS->nRasterXSize;; + nBlockYSize = 1; +} + +/************************************************************************/ +/* ~FITSRasterBand() */ +/************************************************************************/ + +FITSRasterBand::~FITSRasterBand() { + FlushCache(); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr FITSRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff, + void* pImage ) { + // A FITS block is one row (we assume BSQ formatted data) + FITSDataset* dataset = (FITSDataset*) poDS; + fitsfile* hFITS = dataset->hFITS; + int status = 0; + + // Since a FITS block is a whole row, nBlockXOff must be zero + // and the row number equals the y block offset. Also, nBlockYOff + // cannot be greater than the number of rows + CPLAssert(nBlockXOff == 0); + CPLAssert(nBlockYOff < nRasterYSize); + + // Calculate offsets and read in the data. Note that FITS array offsets + // start at 1... + long offset = (nBand - 1) * nRasterXSize * nRasterYSize + + nBlockYOff * nRasterXSize + 1; + long nElements = nRasterXSize; + + // If we haven't written this block to the file yet, then attempting + // to read causes an error, so in this case, just return zeros. + if (!dataset->isExistingFile && offset > dataset->highestOffsetWritten) { + memset(pImage, 0, nBlockXSize * nBlockYSize + * GDALGetDataTypeSize(eDataType) / 8); + return CE_None; + } + + // Otherwise read in the image data + fits_read_img(hFITS, dataset->fitsDataType, offset, nElements, + 0, pImage, 0, &status); + if (status) { + CPLError(CE_Failure, CPLE_AppDefined, + "Couldn't read image data from FITS file (%d).", status); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/* */ +/************************************************************************/ + +CPLErr FITSRasterBand::IWriteBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff, + void* pImage) { + FITSDataset* dataset = (FITSDataset*) poDS; + fitsfile* hFITS = dataset->hFITS; + int status = 0; + + // Since a FITS block is a whole row, nBlockXOff must be zero + // and the row number equals the y block offset. Also, nBlockYOff + // cannot be greater than the number of rows + + // Calculate offsets and read in the data. Note that FITS array offsets + // start at 1... + long offset = (nBand - 1) * nRasterXSize * nRasterYSize + + nBlockYOff * nRasterXSize + 1; + long nElements = nRasterXSize; + fits_write_img(hFITS, dataset->fitsDataType, offset, nElements, + pImage, &status); + + // Capture special case of non-zero status due to data range + // overflow Standard GDAL policy is to silently truncate, which is + // what CFITSIO does, in addition to returning NUM_OVERFLOW (412) as + // the status. + if (status == NUM_OVERFLOW) + status = 0; + + // Check for other errors + if (status) { + CPLError(CE_Failure, CPLE_AppDefined, + "Error writing image data to FITS file (%d).", status); + return CE_Failure; + } + + // When we write a block, update the offset counter that we've written + if (offset > dataset->highestOffsetWritten) + dataset->highestOffsetWritten = offset; + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* FITSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +// Some useful utility functions + +// Simple static function to determine if FITS header keyword should +// be saved in meta data. +static const int ignorableHeaderCount = 15; +static const char* ignorableFITSHeaders[ignorableHeaderCount] = { + "SIMPLE", "BITPIX", "NAXIS", "NAXIS1", "NAXIS2", "NAXIS3", "END", + "XTENSION", "PCOUNT", "GCOUNT", "EXTEND", "CONTINUE", + "COMMENT", "", "LONGSTRN" +}; +static bool isIgnorableFITSHeader(const char* name) { + for (int i = 0; i < ignorableHeaderCount; ++i) { + if (strcmp(name, ignorableFITSHeaders[i]) == 0) + return true; + } + return false; +} + + +/************************************************************************/ +/* FITSDataset() */ +/************************************************************************/ + +FITSDataset::FITSDataset() { + hFITS = 0; +} + +/************************************************************************/ +/* ~FITSDataset() */ +/************************************************************************/ + +FITSDataset::~FITSDataset() { + + int status; + if (hFITS) { + if(eAccess == GA_Update) { // Only do this if we've successfully opened the file and update capability + // Write any meta data to the file that's compatible with FITS + status = 0; + fits_movabs_hdu(hFITS, 1, 0, &status); + fits_write_key_longwarn(hFITS, &status); + if (status) { + CPLError(CE_Warning, CPLE_AppDefined, + "Couldn't move to first HDU in FITS file %s (%d).\n", + GetDescription(), status); + } + char** metaData = GetMetadata(); + int count = CSLCount(metaData); + for (int i = 0; i < count; ++i) { + const char* field = CSLGetField(metaData, i); + if (strlen(field) == 0) + continue; + else { + char* key = NULL; + const char* value = CPLParseNameValue(field, &key); + // FITS keys must be less than 8 chars + if (key != NULL && strlen(key) <= 8 && !isIgnorableFITSHeader(key)) { + // Although FITS provides support for different value + // types, the GDAL Metadata mechanism works only with + // string values. Prior to about 2003-05-02, this driver + // would attempt to guess the value type from the metadata + // value string amd then would use the appropriate + // type-specific FITS keyword update routine. This was + // found to be troublesome (e.g. a numeric version string + // with leading zeros would be interpreted as a number + // and might get those leading zeros stripped), and so now + // the driver writes every value as a string. In practice + // this is not a problem since most FITS reading routines + // will convert from strings to numbers automatically, but + // if you want finer control, use the underlying FITS + // handle. Note: to avoid a compiler warning we copy the + // const value string to a non const one... + char* valueCpy = CPLStrdup(value); + fits_update_key_longstr(hFITS, key, valueCpy, 0, &status); + CPLFree(valueCpy); + + // Check for errors + if (status) { + CPLError(CE_Warning, CPLE_AppDefined, + "Couldn't update key %s in FITS file %s (%d).", + key, GetDescription(), status); + return; + } + } + // Must free up key + CPLFree(key); + } + } + + // Make sure we flush the raster cache before we close the file! + FlushCache(); + } + + // Close the FITS handle - ignore the error status + status = 0; + fits_close_file(hFITS, &status); + + } +} + + +/************************************************************************/ +/* Init() */ +/************************************************************************/ + +CPLErr FITSDataset::Init(fitsfile* hFITS_, bool isExistingFile_) { + + hFITS = hFITS_; + isExistingFile = isExistingFile_; + highestOffsetWritten = 0; + int status = 0; + + // Move to the primary HDU + fits_movabs_hdu(hFITS, 1, 0, &status); + if (status) { + CPLError(CE_Failure, CPLE_AppDefined, + "Couldn't move to first HDU in FITS file %s (%d).\n", + GetDescription(), status); + return CE_Failure; + } + + // The cftsio driver automatically rescales data on read and write + // if BSCALE and BZERO are defined as header keywords. This behavior + // causes overflows with GDAL and is slightly mysterious, so we + // disable this rescaling here. + fits_set_bscale(hFITS, 1.0, 0.0, &status); + + // Get the image info for this dataset (note that all bands in a FITS dataset + // have the same type) + int bitpix; + int naxis; + const int maxdim = 3; + long naxes[maxdim]; + fits_get_img_param(hFITS, maxdim, &bitpix, &naxis, naxes, &status); + if (status) { + CPLError(CE_Failure, CPLE_AppDefined, + "Couldn't determine image parameters of FITS file %s (%d).", + GetDescription(), status); + return CE_Failure; + } + + // Determine data type + if (bitpix == BYTE_IMG) { + gdalDataType = GDT_Byte; + fitsDataType = TBYTE; + } + else if (bitpix == SHORT_IMG) { + gdalDataType = GDT_Int16; + fitsDataType = TSHORT; + } + else if (bitpix == LONG_IMG) { + gdalDataType = GDT_Int32; + fitsDataType = TINT; + } + else if (bitpix == FLOAT_IMG) { + gdalDataType = GDT_Float32; + fitsDataType = TFLOAT; + } + else if (bitpix == DOUBLE_IMG) { + gdalDataType = GDT_Float64; + fitsDataType = TDOUBLE; + } + else { + CPLError(CE_Failure, CPLE_AppDefined, + "FITS file %s has unknown data type: %d.", GetDescription(), + bitpix); + return CE_Failure; + } + + // Determine image dimensions - we assume BSQ ordering + if (naxis == 2) { + nRasterXSize = naxes[0]; + nRasterYSize = naxes[1]; + nBands = 1; + } + else if (naxis == 3) { + nRasterXSize = naxes[0]; + nRasterYSize = naxes[1]; + nBands = naxes[2]; + } + else { + CPLError(CE_Failure, CPLE_AppDefined, + "FITS file %s does not have 2 or 3 dimensions.", + GetDescription()); + return CE_Failure; + } + + // Create the bands + for (int i = 0; i < nBands; ++i) + SetBand(i+1, new FITSRasterBand(this, i+1)); + + // Read header information from file and use it to set metadata + // This process understands the CONTINUE standard for long strings. + // We don't bother to capture header names that duplicate information + // already captured elsewhere (e.g. image dimensions and type) + int keyNum; + char key[100]; + char value[100]; + + int nKeys = 0, nMoreKeys = 0; + fits_get_hdrspace(hFITS, &nKeys, &nMoreKeys, &status); + for(keyNum = 1; keyNum <= nKeys; keyNum++) + { + fits_read_keyn(hFITS, keyNum, key, value, 0, &status); + if (status) { + CPLError(CE_Failure, CPLE_AppDefined, + "Error while reading key %d from FITS file %s (%d)", + keyNum, GetDescription(), status); + return CE_Failure; + } + if (strcmp(key, "END") == 0) { + /* we shouldn't get here in principle since the END */ + /* keyword shouldn't be counted in nKeys, but who knows... */ + break; + } + else if (isIgnorableFITSHeader(key)) { + // Ignore it! + } + else { // Going to store something, but check for long strings etc + // Strip off leading and trailing quote if present + char* newValue = value; + if (value[0] == '\'' && value[strlen(value) - 1] == '\'') { + newValue = value + 1; + value[strlen(value) - 1] = '\0'; + } + // Check for long string + if (strrchr(newValue, '&') == newValue + strlen(newValue) - 1) { + // Value string ends in "&", so use long string conventions + char* longString = 0; + fits_read_key_longstr(hFITS, key, &longString, 0, &status); + // Note that read_key_longst already strips quotes + if (status) { + CPLError(CE_Failure, CPLE_AppDefined, + "Error while reading long string for key %s from " + "FITS file %s (%d)", key, GetDescription(), status); + return CE_Failure; + } + SetMetadataItem(key, longString); + free(longString); + } + else { // Normal keyword + SetMetadataItem(key, newValue); + } + } + } + + return CE_None; +} + + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset* FITSDataset::Open(GDALOpenInfo* poOpenInfo) { + + const char* fitsID = "SIMPLE = T"; // Spaces important! + size_t fitsIDLen = strlen(fitsID); // Should be 30 chars long + + if ((size_t)poOpenInfo->nHeaderBytes < fitsIDLen) + return NULL; + if (memcmp(poOpenInfo->pabyHeader, fitsID, fitsIDLen)) + return NULL; + + // Get access mode and attempt to open the file + int status = 0; + fitsfile* hFITS = 0; + if (poOpenInfo->eAccess == GA_ReadOnly) + fits_open_file(&hFITS, poOpenInfo->pszFilename, READONLY, &status); + else + fits_open_file(&hFITS, poOpenInfo->pszFilename, READWRITE, &status); + if (status) { + CPLError(CE_Failure, CPLE_AppDefined, + "Error while opening FITS file %s (%d).\n", + poOpenInfo->pszFilename, status); + fits_close_file(hFITS, &status); + return NULL; + } + + // Create a FITSDataset object and initialize it from the FITS handle + FITSDataset* dataset = new FITSDataset(); + dataset->eAccess = poOpenInfo->eAccess; + + // Set up the description and initialize the dataset + dataset->SetDescription(poOpenInfo->pszFilename); + if (dataset->Init(hFITS, true) != CE_None) { + delete dataset; + return NULL; + } + else + { +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + dataset->SetDescription( poOpenInfo->pszFilename ); + dataset->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + dataset->oOvManager.Initialize( dataset, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return dataset; + } +} + + +/************************************************************************/ +/* Create() */ +/* */ +/* Create a new FITS file. */ +/************************************************************************/ + +GDALDataset *FITSDataset::Create(const char* pszFilename, + int nXSize, int nYSize, + int nBands, GDALDataType eType, + CPL_UNUSED char** papszParmList) { + FITSDataset* dataset; + fitsfile* hFITS; + int status = 0; + + // No creation options are defined. The BSCALE/BZERO options were + // removed on 2002-07-02 by Simon Perkins because they introduced + // excessive complications and didn't really fit into the GDAL + // paradigm. + + // Create the file - to force creation, we prepend the name with '!' + char* extFilename = new char[strlen(pszFilename) + 10]; // 10 for margin! + sprintf(extFilename, "!%s", pszFilename); + fits_create_file(&hFITS, extFilename, &status); + delete[] extFilename; + if (status) { + CPLError(CE_Failure, CPLE_AppDefined, + "Couldn't create FITS file %s (%d).\n", pszFilename, status); + return NULL; + } + + // Determine FITS type of image + int bitpix; + if (eType == GDT_Byte) + bitpix = BYTE_IMG; + else if (eType == GDT_Int16) + bitpix = SHORT_IMG; + else if (eType == GDT_Int32) + bitpix = LONG_IMG; + else if (eType == GDT_Float32) + bitpix = FLOAT_IMG; + else if (eType == GDT_Float64) + bitpix = DOUBLE_IMG; + else { + CPLError(CE_Failure, CPLE_AppDefined, + "GDALDataType (%d) unsupported for FITS", eType); + fits_close_file(hFITS, &status); + return NULL; + } + + // Now create an image of appropriate size and type + long naxes[3] = {nXSize, nYSize, nBands}; + int naxis = (nBands == 1) ? 2 : 3; + fits_create_img(hFITS, bitpix, naxis, naxes, &status); + + // Check the status + if (status) { + CPLError(CE_Failure, CPLE_AppDefined, + "Couldn't create image within FITS file %s (%d).", + pszFilename, status); + fits_close_file(hFITS, &status); + return NULL; + } + + dataset = new FITSDataset(); + dataset->nRasterXSize = nXSize; + dataset->nRasterYSize = nYSize; + dataset->eAccess = GA_Update; + dataset->SetDescription(pszFilename); + + // Init recalculates a lot of stuff we already know, but... + if (dataset->Init(hFITS, false) != CE_None) { + delete dataset; + return NULL; + } + else { + return dataset; + } +} + + +/************************************************************************/ +/* GDALRegister_FITS() */ +/************************************************************************/ + +void GDALRegister_FITS() { + + GDALDriver* poDriver; + + if( GDALGetDriverByName( "FITS" ) == NULL) { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "FITS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Flexible Image Transport System" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#FITS" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 Int32 Float32 Float64" ); + + poDriver->pfnOpen = FITSDataset::Open; + poDriver->pfnCreate = FITSDataset::Create; + poDriver->pfnCreateCopy = NULL; + + GetGDALDriverManager()->RegisterDriver(poDriver); + } +} diff --git a/bazaar/plugin/gdal/frmts/fits/makefile.vc b/bazaar/plugin/gdal/frmts/fits/makefile.vc new file mode 100644 index 000000000..405c6520f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/fits/makefile.vc @@ -0,0 +1,31 @@ + +OBJ = fitsdataset.obj + +PLUGIN_DLL = gdal_FITS.dll + +EXTRAFLAGS = -I$(FITS_INC_DIR) + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + $(INSTALL) *.obj ..\o + +clean: + -del *.obj + -del *.dll + -del *.exp + -del *.lib + -del *.manifest + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(GDALLIB) $(FITS_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) + diff --git a/bazaar/plugin/gdal/frmts/formats_list.html b/bazaar/plugin/gdal/frmts/formats_list.html new file mode 100644 index 000000000..ac88c98bc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/formats_list.html @@ -0,0 +1,1154 @@ + + + +GDAL Raster Formats + + + + +

GDAL Raster Formats

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Long Format NameCodeCreationGeoreferencingMaximum file size1Compiled by
default
Arc/Info ASCII Grid + AAIGrid + Yes + Yes + 2GB + Yes +
ACE2 + ACE2 + No + Yes + -- + Yes +
ADRG/ARC Digitilized Raster Graphics (.gen/.thf) + ADRG + Yes + Yes + -- + Yes +
Arc/Info Binary Grid (.adf) + AIG + No + Yes + -- + Yes +
AIRSAR Polarimetric + AIRSAR + No + No + -- + Yes +
Azavea Raster Grid + ARG + Yes + Yes + -- + Yes +
Magellan BLX Topo (.blx, .xlb) + BLX + Yes + Yes + -- + Yes +
Bathymetry Attributed Grid (.bag) + BAG + No + Yes + 2GiB + No, needs libhdf5 +
Microsoft Windows Device Independent Bitmap (.bmp) + BMP + Yes + Yes + 4GiB + Yes +
BPG (Better Portable Graphics) + BPG + No + No + --- + No, needs libbpg (manual build required for now) +
BSB Nautical Chart Format (.kap) + BSB + No + Yes + -- + Yes, can be disabled +
VTP Binary Terrain Format (.bt) + BT + Yes + Yes + -- + Yes +
CEOS (Spot for instance) + CEOS + No + No + -- + Yes +
DRDC COASP SAR Processor Raster + COASP + No + No + -- + Yes +
TerraSAR-X Complex SAR Data Product + COSAR + No + No + -- + Yes +
Convair PolGASP data + CPG + No + Yes + -- + Yes +
USGS LULC Composite Theme Grid + CTG + No + Yes + -- + Yes +
DirectDraw Surface + DDS + Yes + No + + No +
Spot DIMAP (metadata.dim) + DIMAP + No + Yes + -- + Yes +
ELAS DIPEx + DIPEx + No + Yes + -- + Yes +
DODS / OPeNDAP + DODS + No + Yes + -- + No, needs libdap +
First Generation USGS DOQ (.doq) + DOQ1 + No + Yes + -- + Yes +
New Labelled USGS DOQ (.doq) + DOQ2 + No + Yes + -- + Yes +
Military Elevation Data (.dt0, .dt1, .dt2) + DTED + Yes + Yes + -- + Yes +
Arc/Info Export E00 GRID + E00GRID + No + Yes + -- + Yes +
ECRG Table Of Contents (TOC.xml) + ECRGTOC + No + Yes + -- + Yes +
ERDAS Compressed Wavelets (.ecw) + ECW + Yes + Yes +  + No, needs ECW SDK +
ESRI .hdr Labelled + EHdr + Yes + Yes + No limits + Yes +
Erdas Imagine Raw + EIR + No + Yes + -- + Yes +
NASA ELAS + ELAS + Yes + Yes + -- + Yes +
ENVI .hdr Labelled Raster + ENVI + Yes + Yes + No limits + Yes +
Epsilon - Wavelet compressed images + EPSILON + Yes + No + -- + No, needs EPSILON library +
ERMapper (.ers) + ERS + Yes + Yes + + Yes +
Envisat Image Product (.n1) + ESAT + No + No + -- + Yes +
EOSAT FAST Format + FAST + No + Yes + -- + Yes +
FIT + FIT + Yes + No + -- + Yes +
FITS (.fits) + FITS + Yes + No + -- + No, needs libcfitsio +
Fuji BAS Scanner Image + FujiBAS + No + No + -- + Yes +
Generic Binary (.hdr Labelled) + GENBIN + No + No + -- + Yes +
GeoPackage + GPKG + Yes + Yes + No limits + No, needs libsqlite3 (and any or all of PNG, JPEG, WEBP drivers) +
Oracle Spatial GeoRaster + GEORASTER + Yes + Yes + -- + No, needs Oracle client libraries +
GSat File Format +GFF +No +No +-- + Yes +
Graphics Interchange Format (.gif) + GIF + Yes + No + 2GB + Yes (internal GIF library provided) +
WMO GRIB1/GRIB2 (.grb) + GRIB + No + Yes + 2GB + Yes, can be disabled +
GMT Compatible netCDF + GMT + Yes + Yes + 2GB + No, needs libnetcdf +
GRASS Raster Format + GRASS + No + Yes + -- + No, needs libgrass +
GRASS ASCII Grid + GRASSASCIIGrid + No + Yes + -- + Yes +
Golden Software ASCII Grid + GSAG + Yes + Yes + -- + Yes +
Golden Software Binary Grid + GSBG + Yes + Yes + 4GiB (32767x32767 of 4 bytes each + 56 byte header) + Yes +
Golden Software Surfer 7 Binary Grid + GS7BG + Yes + Yes + 4GiB + Yes +
GSC Geogrid + GSC + Yes + No + -- + Yes +
Generic Tagged Arrays (.gta) + GTA + Yes + Yes + + No, needs libgta +
TIFF / BigTIFF / GeoTIFF (.tif) + GTiff + Yes + Yes + 4GiB for classical TIFF / No limits for BigTIFF + Yes (internal libtiff and libgeotiff provided) +
NOAA .gtx vertical datum shift + GTX + Yes + Yes + + Yes +
GXF - Grid eXchange File + GXF + No + Yes + 4GiB + Yes +
Hierarchical Data Format Release 4 (HDF4) + HDF4 + Yes + Yes + 2GiB + No, needs libdf +
Hierarchical Data Format Release 5 (HDF5) + HDF5 + No + Yes + 2GiB + No, needs libhdf5 +
HF2/HFZ heightfield raster + HF2 + Yes + Yes + - + Yes +
Erdas Imagine (.img) + HFA + Yes + Yes + No limits2 + Yes +
Image Display and Analysis (WinDisp) + IDA + Yes + Yes + 2GB + Yes +
ILWIS Raster Map (.mpr,.mpl) + ILWIS + Yes + Yes + -- + Yes +
Intergraph Raster + INGR + Yes + Yes + 2GiB + Yes +
IRIS + IRIS + No + Yes + -- + Yes +
USGS Astrogeology ISIS cube (Version 2) + ISIS2 + Yes + Yes + -- + Yes +
USGS Astrogeology ISIS cube (Version 3) + ISIS3 + No + Yes + -- + Yes +
JAXA PALSAR Product Reader (Level 1.1/1.5) + JAXAPALSAR + No + No + -- + Yes +
Japanese DEM (.mem) + JDEM + No + Yes + -- + Yes +
JPEG JFIF (.jpg) + JPEG + Yes + Yes + 4GiB (max dimentions 65500x65500) + Yes (internal libjpeg provided) +
JPEG-LS + JPEGLS + Yes + No + -- + No, needs CharLS library +
JPEG2000 (.jp2, .j2k) + JPEG2000 + Yes + Yes + 2GiB + No, needs libjasper +
JPEG2000 (.jp2, .j2k) + JP2ECW + Yes + Yes + 500MB + No, needs ECW SDK +
JPEG2000 (.jp2, .j2k) + JP2KAK + Yes + Yes + No limits + No, needs Kakadu library +
JPEG2000 (.jp2, .j2k) + JP2MrSID + Yes + Yes + + No, needs MrSID SDK +
JPEG2000 (.jp2, .j2k) + JP2OpenJPEG + Yes + Yes + + No, needs OpenJPEG library (v2) +
JPIP (based on Kakadu) + JPIPKAK + No + Yes + + No, needs Kakadu library +
KEA + KEA + Yes + Yes + -- + No, needs libkea and libhdf5 libraries +
KMLSUPEROVERLAY + KMLSUPEROVERLAY + Yes + Yes + + Yes +
KRO + KRO + Yes + No + -- + Yes +
NOAA Polar Orbiter Level 1b Data Set (AVHRR) + L1B + No + Yes + -- + Yes +
Erdas 7.x .LAN and .GIS + LAN + No + Yes + 2GB + Yes +
FARSITE v.4 LCP Format + LCP + Yes + Yes + No limits + Yes +
Daylon Leveller Heightfield + Leveller + No + Yes + 2GB + Yes +
NADCON .los/.las Datum Grid Shift + LOSLAS + No + Yes + + Yes +
MBTiles + MBTiles + No + Yes + -- + No (needs OGR SQLite driver) +
OziExplorer .MAP + MAP + No + Yes + -- + Yes +
In Memory Raster + MEM + Yes + Yes + + Yes +
Vexcel MFF + MFF + Yes + Yes + No limits + Yes +
Vexcel MFF2 + MFF2 (HKV) + Yes + Yes + No limits + Yes +
MG4 Encoded Lidar + MG4Lidar + No + Yes + -- + No, needs LIDAR SDK +
Multi-resolution Seamless Image Database + MrSID + No + Yes + -- + No, needs MrSID SDK +
Meteosat Second Generation + MSG + No + Yes + + No, needs msg library +
EUMETSAT Archive native (.nat) + MSGN + No + Yes + + Yes +
NLAPS Data Format + NDF + No + Yes + No limits + Yes +
NOAA NGS Geoid Height Grids + NGSGEOID + No + Yes + + Yes +
NITF (.ntf, .nsf, .gn?, .hr?, .ja?, .jg?, .jn?, .lf?, .on?, .tl?, .tp?, etc.) + NITF + Yes + Yes + 10GB + Yes +
NetCDF + netCDF + Yes + Yes + 2GB + No, needs libnetcdf +
NTv2 Datum Grid Shift + NTv2 + Yes + Yes + + Yes +
Northwood/VerticalMapper Classified Grid Format .grc/.tab + NWT_GRC + No + Yes + -- + Yes +
Northwood/VerticalMapper Numeric Grid Format .grd/.tab + NWT_GRD + No + Yes + -- + Yes +
OGDI Bridge + OGDI + No + Yes + -- + No, needs OGDI library +
OZI OZF2/OZFX3 + OZI + No + Yes + -- + No +
PCI .aux Labelled + PAux + Yes + No + No limits + Yes +
PCI Geomatics Database File + PCIDSK + Yes + Yes + No limits + Yes +
PCRaster + PCRaster + Yes + Yes +  + Yes (internal libcsf provided) +
Geospatial PDF + PDF + Yes + Yes + -- + Yes (but needs libpoppler or libpodofo for read support) +
NASA Planetary Data System + PDS + No + Yes + -- + Yes +
Planet Labs Mosaics API + PLMosaic + No + Yes + -- + No, needs libcurl +
Portable Network Graphics (.png) + PNG + Yes + No +  + Yes (internal libpng provided) +
PostGIS Raster (previously WKTRaster) + PostGISRaster + No + Yes + -- + No, needs PostgreSQL library +
Netpbm (.ppm,.pgm) + PNM + Yes + No + No limits + Yes +
R Object Data Store + R + Yes + No + -- + Yes +
Rasdaman + RASDAMAN + No + No + No limits + No (needs raslib) +
Rasterlite - Rasters in SQLite DB + Rasterlite + Yes + Yes + -- + No (needs OGR SQLite driver) +
Swedish Grid RIK (.rik) + RIK + No + Yes + 4GB + Yes (internal zlib is used if necessary) +
Raster Matrix Format (*.rsw, .mtw) + RMF + Yes + Yes + 4GB + Yes +
ROI_PAC Raster + ROI_PAC + Yes + Yes + -- + Yes +
Raster Product Format/RPF (CADRG, CIB) + RPFTOC + No + Yes + -- + Yes +
RadarSat2 XML (product.xml) + RS2 + No + Yes + 4GB + Yes +
Idrisi Raster + RST + Yes + Yes + No limits + Yes +
SAGA GIS Binary format + SAGA + Yes + Yes + -- + Yes +
SAR CEOS + SAR_CEOS + No + Yes + -- + Yes +
ArcSDE Raster + SDE + No + Yes + -- + No, needs ESRI SDE +
USGS SDTS DEM (*CATD.DDF) + SDTS + No + Yes + -- + Yes +
SGI Image Format + SGI + Yes + Yes + -- + Yes +
Snow Data Assimilation System + SNODAS + No + Yes + -- + Yes +
Standard Raster Product (ASRP/USRP) + SRP + No + Yes + 2GB + Yes +
SRTM HGT Format + SRTMHGT + Yes + Yes + -- + Yes +
Terragen Heightfield (.ter) + TERRAGEN + Yes + No + -- + Yes +
EarthWatch/DigitalGlobe .TIL + TIL + No + No + -- + Yes +
TerraSAR-X Product + TSX + Yes + No + -- + Yes +
USGS ASCII DEM / CDED (.dem) + USGSDEM + Yes + Yes + -- + Yes +
VICAR + VICAR + No + Yes + -- + Yes +
GDAL Virtual (.vrt) + VRT + Yes + Yes + -- + Yes +
OGC Web Coverage Service + WCS + No + Yes + -- + No, needs libcurl +
WEBP + WEBP + Yes + No + -- + No, needs libwebp +
OGC Web Map Service + WMS + No + Yes + -- + No, needs libcurl +
X11 Pixmap (.xpm) + XPM + Yes + No +  + Yes +
ASCII Gridded XYZ + XYZ + Yes + Yes + -- + Yes +
ZMap Plus Grid + ZMap + Yes + Yes + + Yes +
+ +

+1Maximum file size is not only determined by + the file format itself, but operating system/file system capabilities + as well. Look here + for details. +

+ +

+2ERDAS Imagine has different file format for + large files, where 32-bit pointers cannot be used. Look for details + here. +

+ +

+$Id: formats_list.html 28859 2015-04-07 08:40:10Z rouault $ +

+ + + diff --git a/bazaar/plugin/gdal/frmts/frmt_various.html b/bazaar/plugin/gdal/frmts/frmt_various.html new file mode 100644 index 000000000..759091bac --- /dev/null +++ b/bazaar/plugin/gdal/frmts/frmt_various.html @@ -0,0 +1,1368 @@ + + + +Various Supported GDAL Raster Formats + + + + +

Various Support GDAL Raster Formats

+ + + + +

AAIGrid -- Arc/Info ASCII Grid

+ +

+Supported for read and write access, including reading of an affine +georeferencing transform and some projections. +This format is the ASCII interchange format for Arc/Info Grid, +and takes the form of an ASCII file, plus sometimes an associated .prj file. +It is normally produced with the Arc/Info ASCIIGRID command.

+ +

The projections support (read if a *.prj file is available) is quite +limited. Additional sample .prj files may be sent to the maintainer, +warmerdam@pobox.com.

+ +

The NODATA value for the grid read is also preserved when available in the +same format as the band data. +

+ +

By default, the datatype returned for AAIGRID datasets by GDAL is autodetected, +and set to Float32 for grid with floating point values or Int32 otherwise. This is +done by analysing the format of the NODATA value and the first 100k bytes of data of the +grid. From GDAL 1.8.0, you can explictely specify the datatype by setting the +AAIGRID_DATATYPE configuration option (Int32, Float32 and Float64 values are +supported currently)

+ +

If pixels being written are not square (the width and height of a pixel in +georeferenced units differ) then DX and DY parameters will be output instead +of CELLSIZE. Such files can be used in Golden Surfer, but not most other +ascii grid reading programs. For force the X pixel size to be used as CELLSIZE +use the FORCE_CELLSIZE=YES creation option or resample the input to have +square pixels.

+ +

When writing floating-point values, the driver uses the "%.20g" format +pattern as a default. You can consult a +reference manual for printf to have an idea of the exact behaviour of this ;-). +You can alternatively specify the number of decimal places with the DECIMAL_PRECISION creation +option. For example, DECIMAL_PRECISION=3 will output numbers with 3 decimal +places(using %lf format). Starting with GDAL 1.11, another option is +SIGNIFICANT_DIGITS=3, which will output 3 significant digits (using %g format).

+ +

The AIG driver is also available +for Arc/Info Binary Grid format.

+ +

NOTE: Implemented as gdal/frmts/aaigrid/aaigriddataset.cpp.

+ + + +

ACE2 -- ACE2

+ +

(GDAL >= 1.9.0)

+ +

This is a conveniency driver to read ACE2 DEMs. Those files contain raw binary data. The +georeferencing is entirely determined by the filename. Quality, source and confidence +layers are of Int16 type, whereas elevation data is returned as Float32.

+ +

ACE2 product overview

+ +

NOTE: Implemented as gdal/frmts/raw/ace2dataset.cpp.

+ + + +

ADRG/ARC Digitized Raster Graphics (.gen/.thf)

+

Supported by GDAL for read access. Creation is possible, but it must be +considered as experimental and a means of testing read access (although files created +by the driver can be read successfully on another GIS software)

+

An ADRG dataset is made of several files. The file recognised by GDAL is the +General Information File (.GEN). GDAL will also need the image file (.IMG), +where the actual data is.

+

The Transmission Header File (.THF) can also be used as an input to GDAL. If the THF references more than one image, +GDAL will report the images it is composed of as subdatasets. If the THF references just one image, GDAL will open it directly.

+

Overviews, legends and insets are not used. Polar zones (ARC zone 9 and 18) are not supported (due to the lack of test data).

+

This is an alternative to using the OGDI Bridge for ADRG datasets.

+ +

See also : the ADRG specification (MIL-A-89007)

+ + + +

AIG -- Arc/Info Binary Grid

+ +

Supported by GDAL for read access. +This format is the internal binary format for Arc/Info Grid, +and takes the form of a coverage level directory in an Arc/Info database. +To open the coverage select the coverage directory, or an .adf file (such as +hdr.adf) from within it. If the directory does not contain file(s) with names +like w001001.adf then it is not a grid coverage.

+ +

Support includes reading of an affine +georeferencing transform, some projections, and a color +table (.clr) if available.

+ +

This driver is implemented based on a reverse engineering of the format. See +the format +description for more details.

+ +

The projections support (read if a prj.adf file is available) is quite +limited. Additional sample prj.adf files may be sent to the maintainer, +warmerdam@pobox.com.

+ +

NOTE: Implemented as gdal/frmts/aigrid/aigdataset.cpp.

+ + + +

ARG -- Azavea Raster Grid

+ +

Driver implementation for a raw format that is used in +GeoTrellis and called ARG. +ARG format +specification. Format is essentially a raw format, with a companion .JSON file.

+ +

NOTE: Implemented as gdal/frmts/arg/argdataset.cpp.

+ + + +

BSB -- Maptech/NOAA BSB Nautical Chart Format

+ +

BSB Nautical Chart format is supported for read access, including reading +the colour table and the reference points (as GCPs). Note that the .BSB +files cannot be selected directly. Instead select the .KAP files. Versions +1.1, 2.0 and 3.0 have been tested successfully.

+ +

This driver should also support GEO/NOS format as supplied by Softchart. +These files normally have the extension .nos with associated .geo files +containing georeferencing ... the .geo files are currently ignored.

+ +

This driver is based on work by Mike Higgins. See the frmts/bsb/bsb_read.c +files for details on patents affecting BSB format.

+ +

Starting with GDAL 1.6.0, it is possible to select an alternate color palette +via the BSB_PALETTE configuration option. The default value is RGB. +Other common values that can be found are : DAY, DSK, NGT, NGR, GRY, PRC, PRG...

+ +

NOTE: Implemented as gdal/frmts/bsb/bsbdataset.cpp.

+ + + +

BT -- VTP .bt Binary Terrain Format

+ +

The .bt format is used for elevation data in the VTP software. The +driver includes support for reading and writing .bt 1.3 format including +support for Int16, Int32 and Float32 pixel data types.

+ +

The driver does +not support reading or writting gzipped (.bt.gz) .bt files even +though this is supported by the VTP software. Please unpack the files +before using with GDAL using the "gzip -d file.bt.gz".

+ +

Projections in external .prj files are read and written, and support for +most internally defined coordinate systems is also available.

+ +

Read/write imagery access with the GDAL .bt driver is terribly slow due to +a very inefficient access strategy to this column oriented data. This could +be corrected, but it would be a fair effort.

+ +

NOTE: Implemented as gdal/frmts/raw/btdataset.cpp.

+ +

See Also: The +BT file format is defined on the +VTP web site.

+ + + +

CEOS -- CEOS Image

+ +

This is a simple, read-only reader for ceos image files. To use, select +the main imagery file. This driver reads only the image data, and does +not capture any metadata, or georeferencing.

+ +

This driver is known to work with CEOS data produced by Spot Image, but +will have problems with many other data sources. In particular, it will +only work with eight bit unsigned data.

+ +

See the separate SAR_CEOS driver for access to +SAR CEOS data products.

+ +

NOTE: Implemented as gdal/frmts/ceos/ceosdataset.cpp.

+ + + +

CTG -- USGS LULC Composite Theme Grid

+ +

(GDAL >= 1.9.0)

+ +

+This driver can read USGS Land Use and Land Cover (LULC) grids encoded in the +Character Composite Theme Grid (CTG) format. Each file is reported as a 6-band +dataset of type Int32. The meaning of each band is the following one : +

    +
  1. Land Use and Land Cover Code
  2. +
  3. Political units Code
  4. +
  5. Census county subdivisions and SMSA tracts Code
  6. +
  7. Hydrologic units Code
  8. +
  9. Federal land ownership Code
  10. +
  11. State land ownership Code
  12. +
+

+

Those files are typically named grid_cell.gz, grid_cell1.gz or grid_cell2.gz +on the USGS site.

+

+

+

+ +

NOTE: Implemented as gdal/frmts/ctg/ctgdataset.cpp.

+ + + +

DDS -- DirectDraw Surface

+ +

(GDAL >= 1.10.0)

+ +

+Supported for writing and creation. The DirectDraw Surface file format (uses +the filename extension DDS), from Microsoft, is a standard for storing data +compressed with the lossy S3 Texture Compression (S3TC) algorithm. The DDS +format and compression are provided by the crunch library. +

+ +

+The driver supports the following texture formats: DXT1. DXT1A, DXT3 (default) and +DXT5. You can set the texture format using the creation option FORMAT. +

+ +

+The driver supports the following compression quality: SUPERFAST, FAST, NORMAL +(default), BETTER and UBER. You can set the compression quality using the +creation option QUALITY. +

+ +

More information about Crunch Lib

+ +

NOTE: Implemented as gdal/frmts/dds/ddsdataset.cpp.

+ + + +

DIMAP -- Spot DIMAP

+ +

This is a read-only read for Spot DIMAP described images. To use, select +the METADATA.DIM file in a product directory, or the product directory +itself.

+ +

The imagery is in a distinct imagery file, often a TIFF file, but the +DIMAP dataset handles accessing that file, and attaches geolocation and +other metadata to the dataset from the metadata xml file.

+ +

From GDAL 1.6.0, the content of the <Spectral_Band_Info> node is reported as metadata +at the level of the raster band. Note that the content of the Spectral_Band_Info +of the first band is still reported as metadata of the dataset, but this should +be considered as a deprecated way of getting this information.

+ +

NOTE: Implemented as gdal/frmts/dimap/dimapdataset.cpp.

+ + + +

DODS/OPeNDAP -- Read rasters from DODS/OPeNDAP servers

+ +

Support for read access to DODS/OPeNDAP servers. Pass the DODS/OPeNDAP URL to +the driver as you would when accessing a local file. The URL specifies the +remote server, data set and raster within the data set. In addition, you must +tell the driver which dimensions are to be interpreted as distinct bands as +well as which correspond to Latitude and Longitude. See the file README.DODS +for more detailed information.

+ + + +

DOQ1 -- First Generation USGS DOQ

+ +

Support for read access, including reading of an affine georeferencing +transform, and capture of the projection string. This format is the old, +unlabelled DOQ (Digital Ortho Quad) format from the USGS.

+ +

NOTE: Implemented as gdal/frmts/raw/doq1dataset.cpp.

+ + + +

DOQ2 -- New Labelled USGS DOQ

+ +

Support for read access, including reading of an affine georeferencing +transform, capture of the projection string and reading of other auxiliary +fields as metadata. This format is the new, +labelled DOQ (Digital Ortho Quad) format from the USGS.

+ +

This driver was implemented by Derrick J Brashear.

+ +

NOTE: Implemented as gdal/frmts/raw/doq2dataset.cpp.

+ +

See Also: +USGS DOQ Standards

+ + + +

E00GRID -- Arc/Info Export E00 GRID

+ +

(GDAL >= 1.9.0)

+ +

GDAL supports reading DEMs/rasters exported as E00 Grids.

+ +

The driver has been tested against datasets such as the one available on +ftp://msdis.missouri.edu/pub/dem/24k/county/

+ +

NOTE: Implemented as gdal/frmts/e00grid/e00griddataset.cpp.

+ + + +

EHdr -- ESRI .hdr Labelled

+ +

GDAL supports reading and writing the ESRI .hdr labelling format, +often referred to as ESRI BIL format. Eight, sixteen +and thirty-two bit integer raster data types are supported as well as 32 +bit floating point. Coordinate systems (from a .prj file), and georeferencing +are supported. Unrecognised options in the .hdr file are ignored. To +open a dataset select the file with the image file (often with the extension +.bil). If present .clr color table files are read, but not written. If present, +image.rep file will be read to extract the projection system of SpatioCarte Defense 1.0 raster products.

+ +

This driver does not always do well differentiating between floating point and +integer data. The GDAL extension to the .hdr format to differentiate is to add +a field named PIXELTYPE with values of either FLOAT, SIGNEDINT or UNSIGNEDINT. +In combination with the NBITS field it is possible to described all variations +of pixel types.

+ +

eg.

+
+  ncols 1375
+  nrows 649
+  cellsize 0.050401
+  xllcorner -130.128639
+  yllcorner 20.166799
+  nodata_value 9999.000000
+  nbits 32
+  pixeltype float
+  byteorder msbfirst
+
+ +

This driver may be sufficient to read GTOPO30 data.

+ +

NOTE: Implemented as gdal/frmts/raw/ehdrdataset.cpp.

+ +

See Also:

+ + + + +

ECRG Table Of Contents (TOC.xml)

+ +Starting with GDAL 1.9.0 + +

This is a read-only reader for ECRG (Enhanced Compressed Raster Graphic) products, +that uses the table of content file, TOC.xml, and exposes it as a virtual dataset whose +coverage is the set of ECRG frames contained in the table of content.

+

The driver will report a different subdataset for each subdataset found in the TOC.xml +file.

+ +

Result of a gdalinfo on a TOC.xml file.

+
+Subdatasets:
+  SUBDATASET_1_NAME=ECRG_TOC_ENTRY:ECRG:FalconView:ECRG_Sample/EPF/TOC.xml
+  SUBDATASET_1_DESC=ECRG:FalconView
+
+ +

See Also:

+ + + +

NOTE: Implemented as gdal/frmts/nitf/ecrgtocdataset.cpp

+ + + +

EIR -- Erdas Imagine Raw

+ +

GDAL supports the Erdas Imagine Raw format for read access including 1, 2, 4, 8, +16 and 32bit unsigned integers, 16 and 32bit signed integers and 32 and 64bit +complex floating point. Georeferencing is supported.

+ +

To open a dataset select the file with the header information. The driver finds +the image file from the header information. Erdas documents call the header +file the "raw" file and it may have the extension .raw while the image file that +contains the actual raw data may have the extension .bl.

+ +

NOTE: Implemented as gdal/frmts/raw/eirdataset.cpp.

+ + + +

ENVI - ENVI .hdr Labelled Raster

+ +

GDAL supports some variations of raw raster files with associated ENVI +style .hdr files describing the format. To select an existing ENVI raster +file select the binary file containing the data (as opposed to the .hdr +file), and GDAL will find the .hdr file by replacing the dataset extension +with .hdr.

+ +

GDAL should support reading bil, bip and bsq interleaved formats, and +most pixel types are supported, including 8bit unsigned, 16 and 32bit +signed and unsigned integers, 32bit and 64 bit floating point, and +32bit and 64bit complex floating point. There is limited support for +recognising map_info keywords with the +coordinate system and georeferencing. In particular, UTM and State Plane +should work.

+ +

Starting with GDAL 1.10, all ENVI header fields will be stored in the +ENVI metadata domain, and all of these can then be written out to the header file.

+ +

Creation Options:

+ +
    + +
  • INTERLEAVE=BSQ/BIP/BIL: Force the generation specified + type of interleaving. BSQ --- band sequental (default), + BIP --- data interleaved by pixel, + BIL --- data interleaved by line.
  • + +
  • SUFFIX=REPLACE/ADD: Force adding ".hdr" suffix to supplied + filename, e.g. if user selects "file.bin" name for output dataset, + "file.bin.hdr" header file will be created. By default header file + suffix replaces the binary file suffix, e.g. for "file.bin" name + "file.hdr" header file will be created.
  • + +
+ + +

NOTE: Implemented as gdal/frmts/raw/envidataset.cpp.

+ + + +

Envisat -- Envisat Image Product

+ +

GDAL supports the Envisat product format for read access. All sample +types are supported. Files with two matching measurement datasets (MDS) +are represented as having two bands. Currently all ASAR Level 1 and above +products, and some MERIS and AATSR products are supported.

+ +

The control points of the GEOLOCATION GRID ADS dataset are read if available, +generally giving a good coverage of the dataset. The GCPs are in WGS84.

+ +

Virtually all key/value pairs from the MPH and SPH (primary and secondary +headers) are copied through as dataset level metadata.

+ +

ASAR and MERIS parameters contained in the ADS and GADS records (excluded +geolocation ones) can be retrieved as key/value pairs using the "RECORDS" +metadata domain.

+ +

NOTE: Implemented as gdal/frmts/envisat/envisatdataset.cpp.

+ +

See Also: +Envisat Data Products at ESA.

+ + + +

FITS -- Flexible Image Transport System

+ +

FITS is a format used mainly by astronomers, but it is a relatively +simple format that supports arbitrary image types and multi-spectral +images, and so has found its way into GDAL. FITS support is +implemented in terms of the standard CFITSIO +library, which you must have on your system in order for FITS +support to be enabled. Both reading and writing of FITS files is +supported. At the current time, no support for a georeferencing system +is implemented, but WCS (World Coordinate System) support is possible +in the future.

+ +

Non-standard header keywords that are present in the FITS file will +be copied to the dataset's metadata when the file is opened, for +access via GDAL methods. Similarly, non-standard header keywords that +the user defines in the dataset's metadata will be written to the FITS +file when the GDAL handle is closed.

+ +

Note to those familiar with the CFITSIO library: The automatic +rescaling of data values, triggered by the presence of the BSCALE and +BZERO header keywords in a FITS file, is disabled in GDAL. Those +header keywords are accessible and updatable via dataset metadata, in +the same was as any other header keywords, but they do not affect +reading/writing of data values from/to the file.

+ +

NOTE: Implemented as gdal/frmts/fits/fitsdataset.cpp.

+ + + +

GenBin - Generic Binary (.hdr labelled)

+ +

This driver supporting reading "Generic Binary" files labelled with a .hdr +file, but distinct from the more common ESRI labelled .hdr format (EHdr driver). +The origin of this format is not entirely clear. The .hdr +files supported by this driver are look something like this:

+ +
+{{{
+BANDS:      1
+ROWS:    6542
+COLS:    9340
+...
+}}}
+
+ +

+Pixel data types of U8, U16, S16, F32, F64, and U1 (bit) are supported. +Georeferencing and coordinate system information should be supported +when provided.

+ +

NOTE: Implemented as gdal/frmts/raw/genbindataset.cpp.

+ + + +

GRASSASCIIGrid -- GRASS ASCII Grid

+ +

(GDAL >= 1.9.0)

+ +

Supports reading GRASS ASCII grid format (similar to Arc/Info ASCIIGRID command).

+ +

By default, the datatype returned for GRASS ASCII grid datasets by GDAL is autodetected, +and set to Float32 for grid with floating point values or Int32 otherwise. This is +done by analysing the format of the null value and the first 100k bytes of data of the +grid. You can also explictely specify the datatype by setting the +GRASSASCIIGRID_DATATYPE configuration option (Int32, Float32 and Float64 values are +supported currently)

+ +

NOTE: Implemented as gdal/frmts/aaigrid/aaigriddataset.cpp.

+ + + +

GSAG -- Golden Software ASCII Grid File Format

+ +

This is the ASCII-based (human-readable) version of one of the raster formats +used by Golden Software products (such as the Surfer series). This format is +supported for both reading and writing (including create, delete, and copy). +Currently the associated formats for color, metadata, and shapes are not +supported.

+ +

NOTE: Implemented as gdal/frmts/gsg/gsagdataset.cpp.

+ + + +

GSBG -- Golden Software Binary Grid File Format

+ +

This is the binary (non-human-readable) version of one of the raster formats +used by Golden Software products (such as the Surfer series). Like the ASCII +version, this format is supported for both reading and writing (including +create, delete, and copy). Currently the associated formats for color, +metadata, and shapes are not supported.

+ +

NOTE: Implemented as gdal/frmts/gsg/gsbgdataset.cpp.

+ + + +

+ GS7BG -- Golden Software Surfer 7 Binary Grid File Format

+ +

This is the binary (non-human-readable) version of one of the raster formats +used by Golden Software products (such as the Surfer series). This format +differs from the GSBG format (also known as Surfer 6 +binary grid format), it is more complicated and flexible.

+ +

NOTE: Implemented as gdal/frmts/gsg/gs7bgdataset.cpp.

+ + + +

GXF -- Grid eXchange File

+ +

This is a raster exchange format propagated by Geosoft, and made a standard +in the gravity/magnetics field. GDAL supports reading (but not writing) +GXF-3 files, including support for georeferencing information, and projections. +

+ +

By default, the datatype returned for GXF datasets by GDAL is Float32. +From GDAL 1.8.0, you can specify the datatype by setting the GXF_DATATYPE +configuration option (Float64 supported currently)

+ +

Details on the supporting code, and format can be found on the +GXF-3 page.

+ +

NOTE: Implemented as gdal/frmts/gxf/gxfdataset.cpp.

+ + + +

IDA -- Image Display and Analysis

+ +

GDAL supports reading and writing IDA images with some limitations. IDA +images are the image format of WinDisp 4. The files are always one band only +of 8bit data. IDA files often have the extension .img though that is not +required.

+ +

Projection and georeferencing information is read though some projections +(ie. Meteosat, and Hammer-Aitoff) are not supported. When writing IDA files +the projection must have a false easting and false northing of zero. The +support coordinate systems in IDA are Geographic, Lambert Conformal Conic, +Lambert Azimuth Equal Area, Albers Equal-Area Conic and Goodes Homolosine.

+ +

IDA files typically contain values scaled to 8bit via a slope and offset. +These are returned as the slope and offset values of the bands and they must +be used if the data is to be rescaled to original raw values for analysis.

+ +

NOTE: Implemented as gdal/frmts/raw/idadataset.cpp.

+ +

See Also: WinDisp

+ + + +

ILWIS -- Raster Map

+ +

This driver implements reading and writing of ILWIS raster maps and map lists. Select the raster files with the.mpr (for raster map) or .mpl (for maplist) extensions

+

Features:

+
    +
  • Support for Byte, Int16, Int32 and Float64 pixel data types.
  • +
  • Supports map lists with an associated set of ILWIS raster maps.
  • +
  • Read and write geo-reference (.grf). Support for geo-referencing transform is limited to north-oriented GeoRefCorner only. If possible the affine transform is computed from the corner coordinates.
  • +
  • Read and write coordinate files (.csy). Support is limited to: Projection type of Projection and Lat/Lon type that are defined in .csy file, the rest of pre-defined projection types are ignored.
  • +
+ +

Limitations:

+
    +
  • Map lists with internal raster map storage (such as produced through Import General Raster) are not supported.
  • +
  • ILWIS domain (.dom) and representation (.rpr) files are currently ignored.
  • +
+ + +

IRIS - Vaisala's weather radar software format

+ +

This read-only GDAL driver is designed to provide access to the products generated by the IRIS weather radar software.

+ +

IRIS software format includes a lot of products, and some of them aren't even raster. The driver can read currently: +

    +
  • PPI (reflectivity and speed): Plan position indicator
  • +
  • CAPPI: Constant Altitude Plan position indicator
  • +
  • RAIN1: Hourly rainfall accumulation
  • +
  • RAINN: N-Hour rainfall accumulation
  • +
  • TOPS: Height for selectable dBZ contour
  • +
  • VIL: Vertically integrated liquid for selected layer
  • +
  • MAX: Column Max Z WF W/NS Sections
  • +
+Most of the metadata is read. +

+

Vaisala provides information about the format and software at http://www.vaisala.com/en/defense/products/weatherradar/Pages/IRIS.aspx.

+ +

NOTE: Implemented as gdal/frmts/iris/irisdataset.cpp.

+ + + +

JDEM -- Japanese DEM (.mem)

+ +

GDAL includes read support for Japanese DEM files, normally having the +extension .mem. These files are a product of the Japanese Geographic Survey +Institute.

+ +

These files are represented as having one 32bit floating band with elevation +data. The georeferencing of the files is returned as well as the coordinate +system (always lat/long on the Tokyo datum).

+ +

There is no update or creation support for this format.

+ +

NOTE: Implemented as gdal/frmts/jdem/jdemdataset.cpp.

+ +

See Also: Geographic Survey +Institute (GSI) Web Site.

+ + + +

KRO -- KOLOR Raw format

+ +

(GDAL >= 1.11)

+ +

Supported for read access, update and creation. +This format is a binary raw format, that supports data of several depths ( +8 bit, unsigned integer 16 bit and floating point 32 bit) and with several band +number (3 or 4 typically, for RGB and RGBA). There is no file size limit, +except the limitation of the file system.

+ +

Specification of the format

+ +

NOTE: Implemented as gdal/frmts/raw/krodataset.cpp.

+ + + +

LAN -- Erdas 7.x .LAN and .GIS

+ +

GDAL supports reading and writing +Erdas 7.x .LAN and .GIS raster files. Currently +4bit, 8bit and 16bit pixel data types are supported for reading +and 8bit and 16bit for writing.

+ +

GDAL does read the map extents (geotransform) from LAN/GIS files, and +attempts to read the coordinate system informaton. However, this format +of file does not include complete coordinate system information, so for +state plane and UTM coordinate systems a LOCAL_CS definition is returned +with valid linear units but no other meaningful information.

+ +

The .TRL, .PRO and worldfiles are ignored at this time.

+ +

NOTE: Implemented as gdal/frmts/raw/landataset.cpp

+ +

Development of this driver was financially supported by Kevin Flanders of +(PeopleGIS).

+ + + +

MFF -- Vexcel MFF Raster

+ +

GDAL includes read, update, and creation support for Vexcel's +MFF raster format. MFF dataset consist of a header file (typically with +the extension .hdr) and a set of data files with extensions like .x00, .b00 +and so on. To open a dataset select the .hdr file.

+ +

Reading lat/long GCPs (TOP_LEFT_CORNER, ...) is supported but there is no +support for reading affine georeferencing or projection information.

+ +

Unrecognised keywords from the .hdr file are preserved as metadata.

+ +

All data types with GDAL equivelents are supported, including 8, 16, 32 and +64 bit data precisions in integer, real and complex data types. In addition +tile organized files (as produced by the Vexcel SAR Processor - APP) are +supported for reading.

+ +

On creation (with a format code of MFF) a simple, ungeoreferenced raster +file is created.

+ +

MFF files are not normally portable between systems with different byte orders. +However GDAL honours the new BYTE_ORDER keyword which can take a value of +LSB (Integer -- little endian), and MSB (Motorola -- big endian). This may +be manually added to the .hdr file if required.

+ +

NOTE: Implemented as gdal/frmts/raw/mffdataset.cpp.

+ + + + +

NDF -- NLAPS Data Format

+ +

GDAL has limited support for reading NLAPS Data Format files. This is a +format primarily used by the Eros Data Center for distribution of Landsat +data. NDF datasets consist of a header file (often with the extension .H1) +and one or more associated raw data files (often .I1, .I2, ...). To open +a dataset select the header file, often with the extension .H1, .H2 or +.HD.

+ +

The NDF driver only supports 8bit data. The only supported projection is +UTM. NDF version 1 (NDF_VERSION=0.00) and NDF version 2 are both supported. +

+ +

NOTE: Implemented as gdal/frmts/raw/ndfdataset.cpp.

+ +

See Also: NLAPS +Data Format Specification.

+ + + +

GMT -- GMT Compatible netCDF

+ +

GDAL has limited support for reading and writing netCDF grid files. +NetCDF files that are not recognised as grids (they lack variables called +dimension, and z) will be silently ignored by this driver. This driver is +primarily intended to provide a mechanism for grid interchange with the +GMT package. The +netCDF driver should be used for more general netCDF datasets.

+ +

The units information in the file will be ignored, but x_range, and +y_range information will be read to get georeferenced extents of the raster. +All netCDF data types should be supported for reading.

+ +

Newly created files (with a type of GMT) will always have +units of "meters" for x, y and z but the x_range, y_range and z_range should +be correct. Note that netCDF does not have an unsigned byte data type, so +8bit rasters will generally need to be converted to Int16 for export to +GMT.

+ +

NetCDF support in GDAL is optional, and not compiled in by default.

+ +

NOTE: Implemented as gdal/frmts/netcdf/gmtdataset.cpp.

+ +

See Also: Unidata NetCDF Page

+ + + +

PAux -- PCI .aux Labelled Raw Format

+ +

GDAL includes a partial implementation of the PCI .aux labelled raw raster +file for read, write and creation. To open a PCI labelled file, select the +raw data file itself. The .aux file (which must have a common base name) will +be checked for automatically.

+ +

The format type for creating new files is PAux. All PCI data types +(8U, 16U, 16S, and 32R) are supported. Currently georeferencing, projections, +and other metadata is ignored.

+ +

Creation Options:

+ +
    +
  • INTERLEAVE=PIXEL/LINE/BAND: Establish output interleaving, the default is BAND. +
+ +

NOTE: Implemented as gdal/frmts/raw/pauxdataset.cpp.

+ +

See Also: PCI's .aux Format +Description

+ + + +

PCRaster raster file format

+

GDAL includes support for reading and writing PCRaster raster files. PCRaster is a dynamic modelling system for distributed simulation models. The main applications of PCRaster are found in environmental modelling: geography, hydrology, ecology to name a few. Examples include models for research on global hydrology, vegetation competition models, slope stability models and land use change models.

+ +

The driver reads all types of PCRaster maps: booleans, nominal, ordinals, scalar, directional and ldd. The same cell representation used to store values in the file is used to store the values in memory.

+ +

The driver detects whether the source of the GDAL raster is a PCRaster file. When such a raster is written to a file the value scale of the original raster will be used. The driver always writes values using UINT1, INT4 or REAL4 cell representations, depending on the value scale:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Value scaleCell representation
VS_BOOLEANCR_UINT1
VS_NOMINALCR_INT4
VS_ORDINALCR_INT4
VS_SCALARCR_REAL4
VS_DIRECTIONCR_REAL4
VS_LDDCR_UINT1
+ +

For rasters from other sources than a PCRaster raster file a value scale and cell representation is determined according to the folowing rules:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source typeTarget value scaleTarget cell representation
GDT_ByteVS_BOOLEANCR_UINT1
GDT_Int32VS_NOMINALCR_INT4
GDT_Float32VS_SCALARCR_REAL4
GDT_Float64VS_SCALARCR_REAL4
+ +

The driver can convert values from one supported cell representation to another. It cannot convert to unsupported cell representations. For example, it is not possible to write a PCRaster raster file from values which are used as CR_INT2 (GDT_Int16).

+ +

Although the de-facto file extension of a PCRaster raster file is .map, the PCRaster software does not require a standardized file extension.

+ +

NOTE: Implemented as gdal/frmts/pcraster/pcrasterdataset.cpp.

+ +

See also: PCRaster website at Utrecht University.

+ + + +

PNG -- Portable Network Graphics

+ +

GDAL includes support for reading, and creating .png files. Greyscale, +pseudo-colored, Paletted, RGB and RGBA PNG files are supported as well as +precisions of eight and sixteen bits per sample.

+ +

PNG files are linearly compressed, so random reading of large PNG files can +be very inefficient (resulting in many restarts of decompression from the +start of the file).

+ +

Text chunks are translated into metadata, typically with multiple lines per +item. World files with the extensions of .pgw, .pngw or .wld +will be read. Single transparency values in greyscale files will be +recognised as a nodata value in GDAL. Transparent index in paletted images +are preserved when the color table is read.

+ +

PNG files can be created with a type of PNG, using the CreateCopy() method, +requiring a prototype to read from. Writing includes support for the +various image types, and will preserve transparency/nodata values. +Georeferencing .wld files are written if option WORLDFILE setted. All +pixel types other than 16bit unsigned will be written as eight bit.

+ +

Starting with GDAL 1.9.0, XMP metadata can be extracted from the file, and will be +stored as XML raw content in the xml:XMP metadata domain.

+ +

Color Profile Metadata

+ +

Starting with GDAL 1.11, GDAL can deal with the following color profile metadata in the COLOR_PROFILE domain:

+
    +
  • SOURCE_ICC_PROFILE (Base64 encoded ICC profile embedded in file. If available, other tags are ignored.)
  • +
  • SOURCE_ICC_PROFILE_NAME : ICC profile name. sRGB is recognized as a special value.
  • +
  • SOURCE_PRIMARIES_RED (xyY in "x,y,1" format for red primary.)
  • +
  • SOURCE_PRIMARIES_GREEN (xyY in "x,y,1" format for green primary)
  • +
  • SOURCE_PRIMARIES_BLUE (xyY in "x,y,1" format for blue primary)
  • +
  • SOURCE_WHITEPOINT (xyY in "x,y,1" format for whitepoint)
  • +
  • PNG_GAMMA
  • +
+ +

Note that these metadata properties can only be used on the original raw pixel data. If automatic conversion to RGB has been done, the color profile information cannot be used.

+ +

All these metadata tags can be used as creation options.

+ +

Creation Options:

+ +
    + +
  • WORLDFILE=YES: Force the generation of an associated ESRI world +file (with the extension .wld). See World File section for +details.
  • + +
  • ZLEVEL=n: Set the amount of time to spend on compression. +The default is 6. A value of 1 is fast but does no compression, and a value +of 9 is slow but does the best compression.
  • + +
  • TITLE=value: Title, written in a TEXT or iTXt chunk (GDAL >= 2.0 )
  • +
  • DESCRIPTION=value: Description, written in a TEXT or iTXt chunk (GDAL >= 2.0 )
  • +
  • COPYRIGHT=value: Copyright, written in a TEXT or iTXt chunk (GDAL >= 2.0 )
  • +
  • COMMENT=value: Comment, written in a TEXT or iTXt chunk (GDAL >= 2.0 )
  • +
  • WRITE_METADATA_AS_TEXT=YES/NO: Whether to write source dataset metadata in TEXT chunks (GDAL >= 2.0 )
  • + +
+ +

NOTE: Implemented as gdal/frmts/png/pngdataset.cpp.

+ +

PNG support is implemented based on the libpng reference library. +More information is available at +http://www.libpng.org/pub/png.

+ + + +

PNM -- Netpbm (.pgm, .ppm)

+ +

GDAL includes support for reading, and creating .pgm (greyscale), and +.ppm (RGB color) files compatible with the Netpbm tools. Only the binary +(raw) formats are supported.

+ +

Netpbm files can be created with a type of PNM.

+ +

Creation Options:

+ +
    + +
  • MAXVAL=n: Force setting the maximum color value to n +in the output PNM file. May be useful if you plannig use the output files with +software which is not liberal to this value.
  • +
+ +

NOTE: Implemented as gdal/frmts/raw/pnmdataset.cpp.

+ + + +

ROI_PAC

+ +

Driver for the image formats used in the JPL's ROI_PAC +project (https://aws.roipac.org/). +All image type are supported excepted .raw images.

+ +

Metadata are stored in the ROI_PAC domain.

+ +

Georeferencing is supported, but expect problems when using the UTM +projection, as ROI_PAC format do not store any hemisphere field.

+ +

When creating files, you have to be able to specify the right data type +corresponding to the file type (slc, int, etc), else the driver will output +an error.

+ +

NOTE: Implemented as gdal/frmts/raw/roipacdataset.cpp.

+ + + +

Raster Product Format/RPF (a.toc)

+ +

This is a read-only reader for RPF products, like CADRG or CIB, that uses the table of +content file - A.TOC - from a RPF exchange, and exposes it as a virtual dataset whose +coverage is the set of frames contained in the table of content.

+

The driver will report a different subdataset for each subdataset found in the A.TOC +file.

+ +

Result of a gdalinfo on a A.TOC file.

+
+Subdatasets:
+  SUBDATASET_1_NAME=NITF_TOC_ENTRY:CADRG_GNC_5M_1_1:GNCJNCN/rpf/a.toc
+  SUBDATASET_1_DESC=CADRG:GNC:Global Navigation Chart:5M:1:1
+[...]
+  SUBDATASET_5_NAME=NITF_TOC_ENTRY:CADRG_GNC_5M_7_5:GNCJNCN/rpf/a.toc
+  SUBDATASET_5_DESC=CADRG:GNC:Global Navigation Chart:5M:7:5
+  SUBDATASET_6_NAME=NITF_TOC_ENTRY:CADRG_JNC_2M_1_6:GNCJNCN/rpf/a.toc
+  SUBDATASET_6_DESC=CADRG:JNC:Jet Navigation Chart:2M:1:6
+[...]
+  SUBDATASET_13_NAME=NITF_TOC_ENTRY:CADRG_JNC_2M_8_13:GNCJNCN/rpf/a.toc
+  SUBDATASET_13_DESC=CADRG:JNC:Jet Navigation Chart:2M:8:13
+
+ + +

In some situations, NITF tiles inside a subdataset don't share the same palettes. +The RPFTOC driver will do its best to remap palettes to the reported palette by gdalinfo (which +is the palette of the first tile of the subdataset). +In situations where it wouldn't give a good result, you can try to set the RPFTOC_FORCE_RGBA +environment variable to TRUE before opening the subdataset. This will cause the driver to expose +the subdataset as a RGBA dataset, instead of a paletted one.

+ +

It is possible to build external overviews for a subdataset. The overview for the first subdataset +will be named A.TOC.1.ovr for example, for the second dataset it will be A.TOC.2.ovr, etc. +Note that you must re-open the subdataset with the same setting of RPFTOC_FORCE_RGBA as the one you +have used when you have created it. Do not use any method other than NEAREST resampling when building +overviews on a paletted subdataset (RPFTOC_FORCE_RGBA unset)

+ +

A gdalinfo on one of this subdataset will return the various NITF metadata, as well +as the list of the NITF tiles of the subdataset.

+ +

See Also:

+ +
    +
  • OGDI Bridge : the RPFTOC driver gives an equivalent + functionnality (without external dependency) to the RPF driver from the OGDI library.
  • +
  • MIL-PRF-89038 : specification of RPF, CADRG, CIB products
  • +
+ +

NOTE: Implemented as gdal/frmts/nitf/rpftocdataset.cpp

+ + + +

SAR_CEOS -- CEOS SAR Image

+ +

This is a read-only reader for CEOS SAR image files. To use, select +the main imagery file.

+ +

This driver works with most Radarsat and ERS data +products, including single look complex products; however, it is unlikely +to work for non-Radar CEOS products. The simpler CEOS +driver is often appropriate for these.

+ +

This driver will attempt to read 15 lat/long GCPS by sampling the per-scanline +CEOS superstructure information. It also captures various pieces of metadata +from various header files, including:

+ +
+  CEOS_LOGICAL_VOLUME_ID=EERS-1-SAR-MLD  
+  CEOS_PROCESSING_FACILITY=APP         
+  CEOS_PROCESSING_AGENCY=CCRS    
+  CEOS_PROCESSING_COUNTRY=CANADA      
+  CEOS_SOFTWARE_ID=APP 1.62    
+  CEOS_ACQUISITION_TIME=19911029162818919               
+  CEOS_SENSOR_CLOCK_ANGLE=  90.000
+  CEOS_ELLIPSOID=IUGG_75         
+  CEOS_SEMI_MAJOR=    6378.1400000
+  CEOS_SEMI_MINOR=    6356.7550000
+
+ +

The SAR_CEOS driver also includes some support for SIR-C and PALSAR +polarimetric data. The SIR-C format contains an image in compressed +scattering matrix form, described +here. +GDAL decompresses the data as it is read in. The PALSAR format +contains bands that correspond almost exactly to elements of the 3x3 +Hermitian covariance matrix- +see the +ERSDAC-VX-CEOS-004A.pdf document for a complete description (pixel +storage is described on page 193). GDAL converts these to complex floating +point covariance matrix bands as they are read in. The convention used +to represent the covariance matrix in terms of the scattering matrix +elements HH, HV (=VH), and VV is indicated below. Note that the +non-diagonal elements of the matrix are complex values, while the diagonal +values are real (though represented as complex bands).

+ +
    +
  • Band 1: Covariance_11 (Float32) = HH*conj(HH)
  • +
  • Band 2: Covariance_12 (CFloat32) = sqrt(2)*HH*conj(HV)
  • +
  • Band 3: Covariance_13 (CFloat32) = HH*conj(VV)
  • +
  • Band 4: Covariance_22 (Float32) = 2*HV*conj(HV)
  • +
  • Band 5: Covariance_23 (CFloat32) = sqrt(2)*HV*conj(VV)
  • +
  • Band 6: Covariance_33 (Float32) = VV*conj(VV)
  • +
+ + +

The identities of the bands are also reflected in the metadata.

+ +

NOTE: Implemented as gdal/frmts/ceos2/sar_ceosdataset.cpp.

+ + + +

SDAT -- SAGA GIS Binary Grid File Format

+ +

(starting with GDAL 1.7.0)

+ +

The driver supports both reading and writing (including create, delete, +and copy) SAGA GIS binary grids. SAGA binary grid datasets are made of +an ASCII header (.SGRD) and a binary data (.SDAT) file with a common +basename. The .SDAT file should be selected to access the dataset.

+ +

The driver supports reading the following SAGA datatypes +(in brackets the corresponding GDAL types): BIT (GDT_Byte), +BYTE_UNSIGNED (GDT_Byte), BYTE (GDT_Byte), SHORTINT_UNSIGNED (GDT_UInt16), +SHORTINT (GDT_Int16), INTEGER_UNSIGNED (GDT_UInt32), INTEGER (GDT_Int32), +FLOAT (GDT_Float32) and DOUBLE (GDT_Float64).

+ +

The driver supports writing the following SAGA datatypes: BYTE_UNSIGNED +(GDT_Byte), SHORTINT_UNSIGNED (GDT_UInt16), SHORTINT (GDT_Int16), +INTEGER_UNSIGNED (GDT_UInt32), INTEGER (GDT_Int32), FLOAT (GDT_Float32) +and DOUBLE (GDT_Float64).

+ +

Currently the driver does not support zFactors other than 1 and reading +SAGA grids which are written TOPTOBOTTOM.

+ +

NOTE: Implemented as gdal/frmts/saga/sagadataset.cpp.

+ + + + +

SDTS -- USGS SDTS DEM

+ +

GDAL includes support for reading USGS SDTS formatted DEMs. USGS DEMs are +always returned with a data type of signed sixteen bit integer, or 32bit float. +Projection and georeferencing information is also returned.

+ +

SDTS datasets consist of a number of files. Each DEM should have one file +with a name like XXXCATD.DDF. This should be selected to open +the dataset.

+ +

The elevation units of DEMs may be feet or meters. The GetType() method on a +band will attempt to return if the units are Feet ("ft") or Meters ("m").

+ +

NOTE: Implemented as gdal/frmts/sdts/sdtsdataset.cpp.

+ + + +

SGI - SGI Image Format

+ +

The SGI driver currently supports the reading and writing of SGI Image files.

+ +

The driver currently supports 1, 2, 3, and 4 band images. The driver +currently supports "8 bit per channel value" images. The driver supports +both uncompressed and run-length encoded (RLE) images for reading, but +created files are always RLE compressed..

+ +

The GDAL SGI Driver was based on Paul Bourke's SGI image read code.

+ +

See Also:

+ + + +

NOTE: Implemented as gdal/frmts/sgi/sgidataset.cpp.

+ + + +

SNODAS -- Snow Data Assimilation System

+ +

(GDAL >= 1.9.0)

+ +

This is a conveniency driver to read Snow Data Assimilation System data. +Those files contain Int16 raw binary data. The file to provide to GDAL is the .Hdr file.

+ +

Snow Data Assimilation System (SNODAS) Data Products at NSIDC

+ +

NOTE: Implemented as gdal/frmts/raw/snodasdataset.cpp.

+ + + + +

Standard Product Format (ASRP/USRP) (.gen)

+ +

(starting with GDAL 1.7.0)

+ +

The ASRP and USRP raster products (as defined by DGIWG) are variations on a +common standard product format and are supported for reading by GDAL. ASRP and +USRP datasets are made of several files - typically a .GEN, .IMG, .SOU and .QAL +file with a common basename. The .IMG file should be selected to access the dataset.

+ +

ASRP (in a geographic coordinate system) and USRP (in a UTM/UPS coordinate +system) products are single band images with a palette and georeferencing. +

+ +

Starting with GDAL 1.11, the Transmission Header File (.THF) can also be used as an input to GDAL. If the THF references more than one image, +GDAL will report the images it is composed of as subdatasets. If the THF references just one image, GDAL will open it directly.

+ +

NOTE: Implemented as gdal/frmts/adrg/srpdataset.cpp.

+ + + +

SRTMHGT - SRTM HGT Format

+ +

The SRTM HGT driver currently supports the reading of SRTM-3 and SRTM-1 V2 (HGT) files.

+ +

The driver does support creating new files, but the input data +must be exactly formatted as a SRTM-3 or SRTM-1 cell. That is the size, +and bounds must be appropriate for a cell.

+ +

See Also:

+ + + +

NOTE: Implemented as gdal/frmts/srtmhgt/srtmhgtdataset.cpp.

+ + + +

WLD -- ESRI World File

+ +

A world file file is a plain ASCII text file consisting of six values separated +by newlines. The format is:

+ +
+ pixel X size
+ rotation about the Y axis (usually 0.0)
+ rotation about the X axis (usually 0.0)
+ negative pixel Y size
+ X coordinate of upper left pixel center
+ Y coordinate of upper left pixel center
+
+ +

For example:

+ +
+60.0000000000
+0.0000000000
+0.0000000000
+-60.0000000000
+440750.0000000000
+3751290.0000000000
+
+ +

You can construct that file simply by using your favorite text editor.

+ +

World file usually has suffix .wld, but sometimes it may has .tfw, tifw, .jgw +or other suffixes depending on the image file it comes with.

+ + + +

XPM - X11 Pixmap

+ +

GDAL includes support for reading and writing XPM (X11 Pixmap Format) +image files. These are colormapped one band images primarily used for +simple graphics purposes in X11 applications. It has been incorporated in +GDAL primarily to ease translation of GDAL images into a form useable with +the GTK toolkit.

+ +

The XPM support does not support georeferencing (not available from XPM files) +nor does it support XPM files with more than one character per pixel. New +XPM files must be colormapped or greyscale, and colortables will be reduced +to about 70 colors automatically.

+ +

NOTE: Implemented as gdal/frmts/xpm/xpmdataset.cpp.

+ + + +

GFF - Sandia National Laboratories GSAT File Format

+ +

This read-only GDAL driver is designed to provide access to processed data from Sandia National Laboratories' various experimental sensors. The format is essentially an arbitrary length header containing instrument configuration and performance parameters along with a binary matrix of 16- or 32-bit complex or byte real data.

+ +

The GFF format was implemented based on the Matlab code provided by Sandia to read the data. The driver supports all types of data (16-bit or 32-bit complex, real bytes) theoretically, however due to a lack of data only 32-bit complex data has been tested.

+ +

Sandia provides some sample data at http://sandia.gov/RADAR/sar-data.html.

+ +

The extension for GFF formats is .gff.

+ +

NOTE: Implemented as gdal/frmts/gff/gff_dataset.cpp.

+ + + +

ZMap -- ZMap Plus Grid

+ +

(GDAL >= 1.9.0)

+ +

+Supported for read access and creation. +This format is an ASCII interchange format for gridded data +in an ASCII line format for transport and storage. It is commonly used +in applications in the Oil and Gas Exploration field.

+ +

By default, files are interpreted and written according to the PIXEL_IS_AREA convention. +If you define the ZMAP_PIXEL_IS_POINT configuration option to TRUE, the PIXEL_IS_POINT +convention will be followed to interprete/write the file (the georeferenced values in the header +of the file will then be considered as the coordinate of the center of the pixels). Note that in that case, +GDAL will report the extent with its usual PIXEL_IS_AREA convention (the coordinates of the topleft corner +as reported by GDAL will be a half-pixel at the top and left of the values that appear in the file).

+ +

Informal specification given in this GDAL-dev mailing list thread

+ +

NOTE: Implemented as gdal/frmts/zmap/zmapdataset.cpp.

+ +
+

+Full list of GDAL Raster Formats +

+ +

+$Id: frmt_various.html 28861 2015-04-07 09:41:29Z kdejong $ +

+ + + diff --git a/bazaar/plugin/gdal/frmts/gdalallregister.cpp b/bazaar/plugin/gdal/frmts/gdalallregister.cpp new file mode 100644 index 000000000..e03cad0b6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gdalallregister.cpp @@ -0,0 +1,537 @@ +/****************************************************************************** + * $Id: gdalallregister.cpp 28859 2015-04-07 08:40:10Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALAllRegister(), primary format registration. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_frmts.h" +#include "ogrsf_frmts.h" + +CPL_CVSID("$Id: gdalallregister.cpp 28859 2015-04-07 08:40:10Z rouault $"); + +#ifdef notdef +// we may have a use for this some day +static char *szConfiguredFormats = "GDAL_FORMATS"; +#endif + +/************************************************************************/ +/* GDALAllRegister() */ +/* */ +/* Register all identifiably supported formats. */ +/************************************************************************/ + +/** + * Register all known configured GDAL drivers. + * + * This function will drive any of the following that are configured into + * GDAL. See raster list and + * vector full list + * + * This function should generally be called once at the beginning of the application. + */ + +void CPL_STDCALL GDALAllRegister() + +{ + GetGDALDriverManager()->AutoLoadDrivers(); + +#ifdef FRMT_vrt + GDALRegister_VRT(); +#endif + +#ifdef FRMT_gdb + GDALRegister_GDB(); +#endif + +#ifdef FRMT_gtiff + GDALRegister_GTiff(); +#endif + +#ifdef FRMT_nitf + GDALRegister_NITF(); + GDALRegister_RPFTOC(); + GDALRegister_ECRGTOC(); +#endif + +#ifdef FRMT_hfa + GDALRegister_HFA(); +#endif + +#ifdef FRMT_ceos2 + GDALRegister_SAR_CEOS(); +#endif + +#ifdef FRMT_ceos + GDALRegister_CEOS(); +#endif + +#ifdef FRMT_jaxapalsar + GDALRegister_PALSARJaxa(); +#endif + +#ifdef FRMT_gff + GDALRegister_GFF(); +#endif + +#ifdef FRMT_elas + GDALRegister_ELAS(); +#endif + +#ifdef FRMT_aigrid +// GDALRegister_AIGrid2(); + GDALRegister_AIGrid(); +#endif + +#ifdef FRMT_aaigrid + GDALRegister_AAIGrid(); + GDALRegister_GRASSASCIIGrid(); +#endif + +#ifdef FRMT_sdts + GDALRegister_SDTS(); +#endif + +#ifdef FRMT_ogdi + GDALRegister_OGDI(); +#endif + +#ifdef FRMT_dted + GDALRegister_DTED(); +#endif + +#ifdef FRMT_png + GDALRegister_PNG(); +#endif + +#ifdef FRMT_dds + GDALRegister_DDS(); +#endif + +#ifdef FRMT_gta + GDALRegister_GTA(); +#endif + +#ifdef FRMT_jpeg + GDALRegister_JPEG(); +#endif + +#ifdef FRMT_mem + GDALRegister_MEM(); +#endif + +#ifdef FRMT_jdem + GDALRegister_JDEM(); +#endif + +#ifdef FRMT_rasdaman + GDALRegister_RASDAMAN(); +#endif + +#ifdef FRMT_gif + GDALRegister_GIF(); + GDALRegister_BIGGIF(); +#endif + +#ifdef FRMT_envisat + GDALRegister_Envisat(); +#endif + +#ifdef FRMT_fits + GDALRegister_FITS(); +#endif + +#ifdef FRMT_bsb + GDALRegister_BSB(); +#endif + +#ifdef FRMT_xpm + GDALRegister_XPM(); +#endif + +#ifdef FRMT_bmp + GDALRegister_BMP(); +#endif + +#ifdef FRMT_dimap + GDALRegister_DIMAP(); +#endif + +#ifdef FRMT_airsar + GDALRegister_AirSAR(); +#endif + +#ifdef FRMT_rs2 + GDALRegister_RS2(); +#endif + +#ifdef FRMT_pcidsk + GDALRegister_PCIDSK(); +#endif + +#ifdef FRMT_pcraster + GDALRegister_PCRaster(); +#endif + +#ifdef FRMT_ilwis + GDALRegister_ILWIS(); +#endif + +#ifdef FRMT_sgi + GDALRegister_SGI(); +#endif + +#ifdef FRMT_srtmhgt + GDALRegister_SRTMHGT(); +#endif + +#ifdef FRMT_leveller + GDALRegister_Leveller(); +#endif + +#ifdef FRMT_terragen + GDALRegister_Terragen(); +#endif + +#ifdef FRMT_netcdf + GDALRegister_GMT(); + GDALRegister_netCDF(); +#endif + +#ifdef FRMT_hdf4 + GDALRegister_HDF4(); + GDALRegister_HDF4Image(); +#endif + +#ifdef FRMT_pds + GDALRegister_ISIS3(); + GDALRegister_ISIS2(); + GDALRegister_PDS(); + GDALRegister_VICAR(); +#endif + +#ifdef FRMT_til + GDALRegister_TIL(); +#endif + +#ifdef FRMT_ers + GDALRegister_ERS(); +#endif + +#ifdef FRMT_jp2kak +// JPEG2000 support using Kakadu toolkit + GDALRegister_JP2KAK(); +#endif + +#ifdef FRMT_jpipkak +// JPEG2000 support using Kakadu toolkit + GDALRegister_JPIPKAK(); +#endif + +#ifdef FRMT_ecw + GDALRegister_ECW(); + GDALRegister_JP2ECW(); +#endif + +#ifdef FRMT_openjpeg +// JPEG2000 support using OpenJPEG library + GDALRegister_JP2OpenJPEG(); +#endif + +#ifdef FRMT_l1b + GDALRegister_L1B(); +#endif + +#ifdef FRMT_fit + GDALRegister_FIT(); +#endif + +#ifdef FRMT_grib + GDALRegister_GRIB(); +#endif + +#ifdef FRMT_mrsid + GDALRegister_MrSID(); +#endif + +#ifdef FRMT_jpeg2000 +// JPEG2000 support using JasPer toolkit +// This one should always be placed after other JasPer supported formats, +// such as BMP or PNM. In other case we will get bad side effects. + GDALRegister_JPEG2000(); +#endif + +#ifdef FRMT_mrsid_lidar + GDALRegister_MG4Lidar(); +#endif + +#ifdef FRMT_rmf + GDALRegister_RMF(); +#endif + +#ifdef FRMT_wcs + GDALRegister_WCS(); +#endif + +#ifdef FRMT_wms + GDALRegister_WMS(); +#endif + +#ifdef FRMT_sde + GDALRegister_SDE(); +#endif + +#ifdef FRMT_msgn + GDALRegister_MSGN(); +#endif + +#ifdef FRMT_msg + GDALRegister_MSG(); +#endif + +#ifdef FRMT_idrisi + GDALRegister_IDRISI(); +#endif + +#ifdef FRMT_ingr + GDALRegister_INGR(); +#endif + +#ifdef FRMT_gsg + GDALRegister_GSAG(); + GDALRegister_GSBG(); + GDALRegister_GS7BG(); +#endif + +#ifdef FRMT_cosar + GDALRegister_COSAR(); +#endif + +#ifdef FRMT_tsx + GDALRegister_TSX(); +#endif + +#ifdef FRMT_coasp + GDALRegister_COASP(); +#endif + +#ifdef FRMT_tms + GDALRegister_TMS(); +#endif + +#ifdef FRMT_r + GDALRegister_R(); +#endif + +#ifdef FRMT_map + GDALRegister_MAP(); +#endif + +/* -------------------------------------------------------------------- */ +/* Put raw formats at the end of the list. These drivers support */ +/* various ASCII-header labeled formats, so the driver could be */ +/* confused if you have files in some of above formats and such */ +/* ASCII-header in the same directory. */ +/* -------------------------------------------------------------------- */ + +#ifdef FRMT_raw + GDALRegister_PNM(); + GDALRegister_DOQ1(); + GDALRegister_DOQ2(); + GDALRegister_ENVI(); + GDALRegister_EHdr(); + GDALRegister_GenBin(); + GDALRegister_PAux(); + GDALRegister_MFF(); + GDALRegister_HKV(); + GDALRegister_FujiBAS(); + GDALRegister_GSC(); + GDALRegister_FAST(); + GDALRegister_BT(); + GDALRegister_LAN(); + GDALRegister_CPG(); + GDALRegister_IDA(); + GDALRegister_NDF(); + GDALRegister_EIR(); + GDALRegister_DIPEx(); + GDALRegister_LCP(); + GDALRegister_GTX(); + GDALRegister_LOSLAS(); + GDALRegister_NTv2(); + GDALRegister_CTable2(); + GDALRegister_ACE2(); + GDALRegister_SNODAS(); + GDALRegister_KRO(); + GDALRegister_ROIPAC(); +#endif + +#ifdef FRMT_arg + GDALRegister_ARG(); +#endif + +/* -------------------------------------------------------------------- */ +/* Our test for the following is weak or expensive so we try */ +/* them last. */ +/* -------------------------------------------------------------------- */ + +#ifdef FRMT_rik + GDALRegister_RIK(); +#endif + +#ifdef FRMT_usgsdem + GDALRegister_USGSDEM(); +#endif + +#ifdef FRMT_gxf + GDALRegister_GXF(); +#endif + +#ifdef FRMT_grass + GDALRegister_GRASS(); +#endif + +#ifdef FRMT_dods + GDALRegister_DODS(); +#endif + +/* Register KEA before HDF5 */ +#ifdef FRMT_kea + GDALRegister_KEA(); +#endif + +#ifdef FRMT_hdf5 + GDALRegister_BAG(); + GDALRegister_HDF5(); + GDALRegister_HDF5Image(); +#endif + +#ifdef FRMT_northwood + GDALRegister_NWT_GRD(); + GDALRegister_NWT_GRC(); +#endif + +#ifdef FRMT_adrg + GDALRegister_ADRG(); + GDALRegister_SRP(); +#endif + +#ifdef FRMT_blx + GDALRegister_BLX(); +#endif + +#ifdef FRMT_pgchip + GDALRegister_PGCHIP(); +#endif + +#ifdef FRMT_georaster + GDALRegister_GEOR(); +#endif + +#ifdef FRMT_rasterlite + GDALRegister_Rasterlite(); +#endif + +#ifdef FRMT_epsilon + GDALRegister_EPSILON(); +#endif + +#ifdef FRMT_postgisraster + GDALRegister_PostGISRaster(); +#endif + +#ifdef FRMT_saga + GDALRegister_SAGA(); +#endif + +#ifdef FRMT_kmlsuperoverlay + GDALRegister_KMLSUPEROVERLAY(); +#endif + +#ifdef FRMT_xyz + GDALRegister_XYZ(); +#endif + +#ifdef FRMT_hf2 + GDALRegister_HF2(); +#endif + +#ifdef FRMT_pdf + GDALRegister_PDF(); +#endif + +#ifdef FRMT_jpegls + GDALRegister_JPEGLS(); +#endif + +#ifdef FRMT_ozi + GDALRegister_OZI(); +#endif + +#ifdef FRMT_ctg + GDALRegister_CTG(); +#endif + +#ifdef FRMT_e00grid + GDALRegister_E00GRID(); +#endif + +#ifdef FRMT_webp + GDALRegister_WEBP(); +#endif + +#ifdef FRMT_zmap + GDALRegister_ZMap(); +#endif + +#ifdef FRMT_ngsgeoid + GDALRegister_NGSGEOID(); +#endif + +#ifdef FRMT_mbtiles + GDALRegister_MBTiles(); +#endif + +#ifdef FRMT_iris + GDALRegister_IRIS(); +#endif + +#ifdef FRMT_plmosaic + GDALRegister_PLMOSAIC(); +#endif + + OGRRegisterAllInternal(); + +#ifdef FRMT_wcs + GDALRegister_HTTP(); +#endif + +/* -------------------------------------------------------------------- */ +/* Deregister any drivers explicitly marked as suppressed by the */ +/* GDAL_SKIP environment variable. */ +/* -------------------------------------------------------------------- */ + GetGDALDriverManager()->AutoSkipDrivers(); +} diff --git a/bazaar/plugin/gdal/frmts/georaster/GNUmakefile b/bazaar/plugin/gdal/frmts/georaster/GNUmakefile new file mode 100644 index 000000000..78d5d31d7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/georaster/GNUmakefile @@ -0,0 +1,37 @@ +include ../../GDALmake.opt + +ifeq ($(LIBZ_SETTING),internal) + XTRA_OPT = -I../zlib +else + XTRA_OPT = +endif + +OBJ = georaster_dataset.o \ + georaster_rasterband.o \ + oci_wrapper.o \ + georaster_wrapper.o + +CPPFLAGS := $(GDAL_INCLUDE) $(XTRA_OPT) $(OCI_INCLUDE) $(CPPFLAGS) -fPIC + +PLUGIN_SO = gdal_GEOR.so + +ifneq ($(JPEG_SETTING),no) +CPPFLAGS := $(CPPFLAGS) -DJPEG_SUPPORTED +endif + +ifeq ($(JPEG_SETTING),internal) +CPPFLAGS := $(CPPFLAGS) -I../jpeg/libjpeg +endif + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) *.so *.lo + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +plugin: $(PLUGIN_SO) + +$(PLUGIN_SO): $(OBJ) + $(LD_SHARED) $(LNK_FLAGS) $(OBJ) $(CONFIG_LIBS_INS) $(LIBS) \ + -o $(PLUGIN_SO) diff --git a/bazaar/plugin/gdal/frmts/georaster/frmt_georaster.html b/bazaar/plugin/gdal/frmts/georaster/frmt_georaster.html new file mode 100644 index 000000000..6f954984b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/georaster/frmt_georaster.html @@ -0,0 +1,281 @@ + + + GeoRaster --- Oracle Spatial GeoRaster + + + + + +

Oracle Spatial GeoRaster

+

+This driver supports reading and writing raster data in Oracle Spatial +GeoRaster format (10g or later). The Oracle Spatial GeoRaster driver is +optionally built as a GDAL plugin, but it requires Oracle client +libraries. +

+

+When opening a GeoRaster, it's name should be specified in the form: +

+

+georaster:<user>{,/}<pwd>{,@}[db],[schema.][table],[column],[where]
+georaster:<user>{,/}<pwd>{,@}[db],<rdt>,<rid>

+

Where:

+

+user   = Oracle server user's name login
+pwd    = user password
+db     = Oracle server identification (database name)
+schema = name of a schema                      
+table  = name of a GeoRaster table (table that +contains GeoRaster columns)
+column = name of a column data type MDSYS.SDO_GEORASTER
+where  = a simple where clause to identify one or multiples +GeoRaster(s)
+rdt    = name of a raster data table
+rid    = numeric identification of one GeoRaster

+

Examples:

+

geor:scott,tiger,demodb,table,column,id=1
+geor:scott,tiger,demodb,table,column,"id = 1"
+"georaster:scott/tiger@demodb,table,column,gain>10"
+"georaster:scott/tiger@demodb,table,column,city='Brasilia'"
+georaster:scott,tiger,,rdt_10$,10
+geor:scott/tiger,,rdt_10$,10

+

Note: do note use space around the field values and the commas.

+

Note: like in the last two examples, the database name field could +be left empty (",,") and the TNSNAME will be used.

+

Note: If  the query results in more than one GeoRaster it will +be treated as a GDAL metadata's list of sub-datasets (see bellow)

+

Browsing the database for GeoRasters +
+

+

By providing some basic information the +GeoRaster driver is capable of listing the existing rasters stored on +the server:

+To list all the GeoRaster table +on the server that belongs to that user name and database: +

% +gdalinfo georaster:scott/tiger@db1

+To list all the GeoRaster type columns that exist in that +table:
+
% +gdalinfo georaster:scott/tiger@db1,table_name
+

That will list all the GeoRaster objects stored in that table. +

+
% +gdalinfo georaster:scott/tiger@db1,table_name,georaster_column
+

That will list all the GeoRaster existing on that table according to +a Where clause. +

+
% +gdalinfo +georaster:scott/tiger@db1,table_name,georaster_column,city='Brasilia'
+
+Note that the result of those queries +are returned as GDAL metadata sub-datasets, e.g.: +

% +gdalinfo georaster:scott/tiger
+Driver: +GeoRaster/Oracle Spatial GeoRaster
+Subdatasets:

+ +SUBDATASET_1_NAME=georaster:scott,tiger,,LANDSAT
+SUBDATASET_1_DESC=Table:LANDSAT
+SUBDATASET_2_NAME=georaster:scott,tiger,,GDAL_IMPORT
+SUBDATASET_2_DESC=Table:GDAL_IMPORT

+

Creation Options

+
    +
  • BLOCKXSIZE: The number of pixel columns +on raster block.
  • +
  • BLOCKYSIZE: The number of pixel rows +on raster block.
  • +
  • BLOCKBSIZE: The number of +bands on raster block.
  • +
  • BLOCKING: Decline the use of blocking (NO) or request an automatic blocking size (OPTIMUM).
  • +
  • SRID: Assign a specific +EPSG projection/reference system identification to the GeoRaster.
  • +
  • INTERLEAVE: Band interleaving mode, BAND, LINE, +PIXEL (or BSQ, BIL, BIP) for band sequential, Line or Pixel +interleaving. 
  • +
  • DESCRIPTION: A simple description of a newly +created table in SQL syntax. If the table already exist, this create +option will be ignored, e.g.:
  • +
+% +gdal_translate -of georaster landsat_823.tif +geor:scott/tiger@orcl,landsat,raster \
+  -co DESCRIPTION="(ID NUMBER, NAME VARCHAR2(40), RASTER +MDSYS.SDO_GEORASTER)" \
+  -co INSERT="VALUES (1,'Scene 823',
SDO_GEOR.INIT())" +
    +
  • INSERT: A simple SQL insert/values clause to +inform the driver what values to fill up when inserting a new row on +the table, e.g.:
  • +
+% +gdal_translate -of georaster landsat_825.tif +geor:scott/tiger@orcl,landsat,raster \
+  -co INSERT="(ID, RASTER) VALUES (2,
SDO_GEOR.INIT())"
+
    +
  • COMPRESS: Compression options, JPEG-F, +DEFLATE or NONE. The two JPEG options are lossy, meaning that the +original pixel values are changed.
  • +
  • GENPYRAMID: Generate pyramid after a GeoRaster object have been loaded to the database. +The content of that parameter must be the resampling method of +choice NN (nearest neighbor) , BILINEAR, BIQUADRATIC, CUBIC, AVERAGE4 or AVERAGE16. If GENPYRLEVELS is not +informed the PL/SQL function sdo_geor.generatePyramid will calculate the number of levels to generate.
  • +
  • GENPYRLEVELS: Define the number of pyramid levels to be generated. +If GENPYRAMID is not informed the resample method NN (nearest neighbor) will apply.
  • +
  • QUALITY: Quality compression option for JPEG +ranging from 0 to 100. The default is 75.
  • +
  • NBITS: Sub byte data type, options: 1, 2 or 4.
  • +
  • SPATIALEXTENT: Generate Spatial Extents. The default for that options is +TRUE, that means that this option only need to be informed to force the Spatial Extent to +remain as NULL. If EXTENTSRID is not informed the Spatial Extent geometry will be generated with +the same SRID as the GeoGeoraster object.
  • +
  • EXTENTSRID: SRID code to be used on the Spatial Extent geometry. +If the table/column has already a spatial extent, the value informed should be the same as the +SRID on the Spatial Extent of the other existing GeoRaster.
  • +
  • OBJECTTABLE: To create RDT as SDO_RASTER object inform TRUE otherwise, the default + is FALSE and the RDT will be created as regular relational tables. That does not apply for Oracle version older than 11.
  • +
+ +

Importing GeoRaster

+

During the process of importing raster +into a GeoRaster object it is possible to give the driver a simple SQL +table +definition and also a SQL insert/values clause to inform the driver +about the table to be created and the values to be added to the newly +created row. The following example does that:

+

% +gdal_translate -of georaster Newpor.tif georaster:scott/tiger,,landsat,scene +\
+  -co +"DESCRIPTION=(ID NUMBER, SITE VARCHAR2(45), SCENE +MDSYS.SDO_GEORASTER)" \
+
+  -co "INSERT=VALUES(1,'West fields', SDO_GEOR.INIT())" \
+
  -co "BLOCKXSIZE=512" -co "BLOCKYSIZE=512" -co "BLOCKBSIZE=3" \
+  -co "INTERLEAVE=PIXEL
" -co "COMPRESS=JPEG-F"

+

Note that the create option DESCRIPTION +requires to inform table name (in bold). And column name +(underlined) should match the description:

+

% +gdal_translate -of georaster landsat_1.tif georaster:scott/tiger,,landsat,scene +\
+  -co +"DESCRIPTION=(ID NUMBER, SITE VARCHAR2(45), SCENE +MDSYS.SDO_GEORASTER)" \
+  -co "INSERT=VALUES(1,'West fields', 
SDO_GEOR.INIT())"

+

If the table "landsat" exist, the option +"DESCRIPTION" is ignored. The driver can only +update one GeoRaster column per run of gdal_translate. Oracle create +default names and values for RDT and RID during the initialization of +the SDO_GEORASTER object but user are also able to specify a name and +value of their choice.

+

% +gdal_translate -of georaster +landsat_1.tif georaster:scott/tiger,,landsat,scene +\
+  -co "INSERT=VALUES(10,'Main building', 
SDO_GEOR.INIT('RDT', +10))"

+

If no information is given about where +to store the raster the driver will create (if doesn't exist already) +a default table named GDAL_IMPORT with just one GeoRaster column +named RASTER and a table GDAL_RDT as the RDT, the RID will be given automatically by the server, example:

+

% +gdal_translate -of georaster input.tif +“geor:scott/tiger@dbdemoâ€
+

+

Exporting GeoRaster

+A GeoRaster can be identified by a Where clause or by a pair of RDT +& RID:
+
+% +gdal_translate -of gtiff geor:scott/tiger@dbdemo,landsat,scene,id=54 +output.tif
+
% +gdal_translate -of gtiff geor:scott/tiger@dbdemo,st_rdt_1,130 output.tif
+
+

Cross schema access

+As long as the user was granted full access the GeoRaster table and the +Raster Data Table, e.g.:
+
+% sqlplus +scott/tiger
+SQL> grant select,insert,update,delete on gdal_import to spock;
+
SQL> +grant select,insert,update,delete on gdal_rdt to spock;
+

+It is possible to an user access to extract and load GeoRaster from +another user/schema by informing the schema name as showed here:
+
+Browsing:
+
+% gdalinfo +geor:spock/lion@orcl,scott.
+%
gdalinfo +geor:spock/lion@orcl,scott.gdal_import,raster,"t.raster.rasterid > +100"
+% gdalinfo +geor:spock/lion@orcl,scott.gdal_import,raster,t.raster.rasterid=101
+
+
Extracting:
+
+% +gdal_translate geor:spock/lion@orcl,scott.gdal_import,raster,t.raster.rasterid=101 out.tif
+
% +gdal_translate geor:spock/lion@orcl,gdal_rdt,101 +out.tif
+
+
Note: On the above example that acessing by RDT/RID doesn't +need schame name as long as the users is granted full acess to both +tables.
+
+Loading:
+
+% +gdal_translate -of georaster input.tif geor:spock/lion@orcl,scott.
+
% +gdal_translate -of georaster +input.tif geor:spock/lion@orcl,scott.cities,image \
+  -co INSERT="(1,'Rio de Janeiro',sdo_geor.init('cities_rdt'))"

+

General use of GeoRaster

+GeoRaster can be used in any GDAL command line tool with all the +available options. Like a image subset extraction or re-project:
+
+% +gdal_translate -of gtiff geor:scott/tiger@dbdemo,landsat,scene,id=54 +output.tif \
+  -srcwin 0 0 800 600
+
+
% +gdalwarp -of png geor:scott/tiger@dbdemo,st_rdt_1,130 output.png -t_srs +EPSG:9000913
+
+
Two different GeoRaster can be used as input and output on the +same operation:
+
+% +gdal_translate -of georaster +geor:scott/tiger@dbdemo,landsat,scene,id=54 +geor:scott/tiger@proj1,projview,image -co INSERT="VALUES (102, SDO_GEOR.INIT())"
+
+
+Applications that use GDAL can theoretically read and write from +GeoRaster just like any other format but most of then are more inclined +to try to access files on the file system so one alternative is to +create VRT to represent the GeoRaster description, e.g.:
+
+% +gdal_translate -of VRT +geor:scott/tiger@dbdemo,landsat,scene,id=54 view_54.vrt
+% openenv view_54.vrt
+
+

+

+ + + diff --git a/bazaar/plugin/gdal/frmts/georaster/georaster_dataset.cpp b/bazaar/plugin/gdal/frmts/georaster/georaster_dataset.cpp new file mode 100644 index 000000000..21ca86448 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/georaster/georaster_dataset.cpp @@ -0,0 +1,2358 @@ +/****************************************************************************** + * $Id: $ + * + * Name: georaster_dataset.cpp + * Project: Oracle Spatial GeoRaster Driver + * Purpose: Implement GeoRasterDataset Methods + * Author: Ivan Lucena [ivan.lucena at oracle.com] + * + ****************************************************************************** + * Copyright (c) 2008, Ivan Lucena + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files ( the "Software" ), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "cpl_error.h" + +#include "ogr_spatialref.h" + +#include "gdal.h" +#include "gdal_priv.h" +#include "georaster_priv.h" + +CPL_C_START +void CPL_DLL GDALRegister_GEOR(void); +CPL_C_END + +// --------------------------------------------------------------------------- +// GeoRasterDataset() +// --------------------------------------------------------------------------- + +GeoRasterDataset::GeoRasterDataset() +{ + bGeoTransform = false; + bForcedSRID = false; + poGeoRaster = NULL; + papszSubdatasets = NULL; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + pszProjection = NULL; + nGCPCount = 0; + pasGCPList = NULL; + poMaskBand = NULL; + bApplyNoDataArray = false; +} + +// --------------------------------------------------------------------------- +// ~GeoRasterDataset() +// --------------------------------------------------------------------------- + +GeoRasterDataset::~GeoRasterDataset() +{ + FlushCache(); + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + delete poGeoRaster; + + if( poMaskBand ) + { + delete poMaskBand; + } + + CPLFree( pszProjection ); + CSLDestroy( papszSubdatasets ); +} + +// --------------------------------------------------------------------------- +// Identify() +// --------------------------------------------------------------------------- + +int GeoRasterDataset::Identify( GDALOpenInfo* poOpenInfo ) +{ + // ------------------------------------------------------------------- + // Verify georaster prefix + // ------------------------------------------------------------------- + + char* pszFilename = poOpenInfo->pszFilename; + + if( EQUALN( pszFilename, "georaster:", 10 ) == false && + EQUALN( pszFilename, "geor:", 5 ) == false ) + { + return false; + } + + return true; +} + +// --------------------------------------------------------------------------- +// Open() +// --------------------------------------------------------------------------- + +GDALDataset* GeoRasterDataset::Open( GDALOpenInfo* poOpenInfo ) +{ + // ------------------------------------------------------------------- + // It shouldn't have an open file pointer + // ------------------------------------------------------------------- + + if( poOpenInfo->fpL != NULL ) + { + return NULL; + } + + // ------------------------------------------------------------------- + // Check identification string and usage + // ------------------------------------------------------------------- + + if( ! Identify( poOpenInfo ) ) + { + return NULL; + } + + // ------------------------------------------------------------------- + // Create a GeoRaster wrapper object + // ------------------------------------------------------------------- + + GeoRasterWrapper* poGRW = GeoRasterWrapper::Open( + poOpenInfo->pszFilename, + poOpenInfo->eAccess == GA_Update ); + + if( ! poGRW ) + { + return NULL; + } + + // ------------------------------------------------------------------- + // Create a corresponding GDALDataset + // ------------------------------------------------------------------- + + GeoRasterDataset *poGRD; + + poGRD = new GeoRasterDataset(); + + if( ! poGRD ) + { + return NULL; + } + + poGRD->eAccess = poOpenInfo->eAccess; + poGRD->poGeoRaster = poGRW; + + // ------------------------------------------------------------------- + // List Subdatasets + // ------------------------------------------------------------------- + + if( ! poGRW->bUniqueFound ) + { + if( poGRD->eAccess == GA_ReadOnly ) + { + poGRD->SetSubdatasets( poGRW ); + + if( CSLCount( poGRD->papszSubdatasets ) == 0 ) + { + delete poGRD; + poGRD = NULL; + } + } + return (GDALDataset*) poGRD; + } + + // ------------------------------------------------------------------- + // Assign GeoRaster information + // ------------------------------------------------------------------- + + poGRD->poGeoRaster = poGRW; + poGRD->nRasterXSize = poGRW->nRasterColumns; + poGRD->nRasterYSize = poGRW->nRasterRows; + poGRD->nBands = poGRW->nRasterBands; + + if( poGRW->bIsReferenced ) + { + poGRD->adfGeoTransform[1] = poGRW->dfXCoefficient[0]; + poGRD->adfGeoTransform[2] = poGRW->dfXCoefficient[1]; + poGRD->adfGeoTransform[0] = poGRW->dfXCoefficient[2]; + poGRD->adfGeoTransform[4] = poGRW->dfYCoefficient[0]; + poGRD->adfGeoTransform[5] = poGRW->dfYCoefficient[1]; + poGRD->adfGeoTransform[3] = poGRW->dfYCoefficient[2]; + } + + // ------------------------------------------------------------------- + // Copy RPC values to RPC metadata domain + // ------------------------------------------------------------------- + + if( poGRW->phRPC ) + { + char **papszRPC_MD = RPCInfoToMD( poGRW->phRPC ); + char **papszSanitazed = NULL; + + int i = 0; + int n = CSLCount( papszRPC_MD ); + + for( i = 0; i < n; i++ ) + { + if ( EQUALN( papszRPC_MD[i], "MIN_LAT", 7 ) || + EQUALN( papszRPC_MD[i], "MIN_LONG", 8 ) || + EQUALN( papszRPC_MD[i], "MAX_LAT", 7 ) || + EQUALN( papszRPC_MD[i], "MAX_LONG", 8 ) ) + { + continue; + } + papszSanitazed = CSLAddString( papszSanitazed, papszRPC_MD[i] ); + } + + poGRD->SetMetadata( papszSanitazed, "RPC" ); + + CSLDestroy( papszRPC_MD ); + CSLDestroy( papszSanitazed ); + } + + // ------------------------------------------------------------------- + // Load mask band + // ------------------------------------------------------------------- + + poGRW->bHasBitmapMask = EQUAL( "TRUE", CPLGetXMLValue( poGRW->phMetadata, + "layerInfo.objectLayer.bitmapMask", "FALSE" ) ); + + if( poGRW->bHasBitmapMask ) + { + poGRD->poMaskBand = new GeoRasterRasterBand( poGRD, 0, DEFAULT_BMP_MASK ); + } + + // ------------------------------------------------------------------- + // Check for filter Nodata environment variable, default is YES + // ------------------------------------------------------------------- + + const char *pszGEOR_FILTER_NODATA = + CPLGetConfigOption( "GEOR_FILTER_NODATA_VALUES", "NO" ); + + if( ! EQUAL(pszGEOR_FILTER_NODATA, "NO") ) + { + poGRD->bApplyNoDataArray = true; + } + // ------------------------------------------------------------------- + // Create bands + // ------------------------------------------------------------------- + + int i = 0; + int nBand = 0; + + for( i = 0; i < poGRD->nBands; i++ ) + { + nBand = i + 1; + poGRD->SetBand( nBand, new GeoRasterRasterBand( poGRD, nBand, 0 ) ); + } + + // ------------------------------------------------------------------- + // Set IMAGE_STRUCTURE metadata information + // ------------------------------------------------------------------- + + if( poGRW->nBandBlockSize == 1 ) + { + poGRD->SetMetadataItem( "INTERLEAVE", "BAND", "IMAGE_STRUCTURE" ); + } + else + { + if( EQUAL( poGRW->sInterleaving.c_str(), "BSQ" ) ) + { + poGRD->SetMetadataItem( "INTERLEAVE", "BAND", "IMAGE_STRUCTURE" ); + } + else if( EQUAL( poGRW->sInterleaving.c_str(), "BIP" ) ) + { + poGRD->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + } + else if( EQUAL( poGRW->sInterleaving.c_str(), "BIL" ) ) + { + poGRD->SetMetadataItem( "INTERLEAVE", "LINE", "IMAGE_STRUCTURE" ); + } + } + + poGRD->SetMetadataItem( "COMPRESSION", CPLGetXMLValue( poGRW->phMetadata, + "rasterInfo.compression.type", "NONE" ), "IMAGE_STRUCTURE" ); + + if( EQUALN( poGRW->sCompressionType.c_str(), "JPEG", 4 ) ) + { + poGRD->SetMetadataItem( "COMPRESS_QUALITY", + CPLGetXMLValue( poGRW->phMetadata, + "rasterInfo.compression.quality", "0" ), "IMAGE_STRUCTURE" ); + } + + if( EQUAL( poGRW->sCellDepth.c_str(), "1BIT" ) ) + { + poGRD->SetMetadataItem( "NBITS", "1", "IMAGE_STRUCTURE" ); + } + + if( EQUAL( poGRW->sCellDepth.c_str(), "2BIT" ) ) + { + poGRD->SetMetadataItem( "NBITS", "2", "IMAGE_STRUCTURE" ); + } + + if( EQUAL( poGRW->sCellDepth.c_str(), "4BIT" ) ) + { + poGRD->SetMetadataItem( "NBITS", "4", "IMAGE_STRUCTURE" ); + } + + // ------------------------------------------------------------------- + // Set Metadata on "ORACLE" domain + // ------------------------------------------------------------------- + + char* pszDoc = CPLSerializeXMLTree( poGRW->phMetadata ); + + poGRD->SetMetadataItem( "TABLE_NAME", CPLSPrintf( "%s%s", + poGRW->sSchema.c_str(), + poGRW->sTable.c_str()), "ORACLE" ); + + poGRD->SetMetadataItem( "COLUMN_NAME", + poGRW->sColumn.c_str(), "ORACLE" ); + + poGRD->SetMetadataItem( "RDT_TABLE_NAME", + poGRW->sDataTable.c_str(), "ORACLE" ); + + poGRD->SetMetadataItem( "RASTER_ID", CPLSPrintf( "%d", + poGRW->nRasterId ), "ORACLE" ); + + poGRD->SetMetadataItem( "SRID", CPLSPrintf( "%d", + poGRW->nSRID ), "ORACLE" ); + + poGRD->SetMetadataItem( "WKT", poGRW->sWKText.c_str(), "ORACLE" ); + + poGRD->SetMetadataItem( "METADATA", pszDoc, "ORACLE" ); + + CPLFree( pszDoc ); + + // ------------------------------------------------------------------- + // Return a GDALDataset + // ------------------------------------------------------------------- + + return (GDALDataset*) poGRD; +} + +// --------------------------------------------------------------------------- +// Create() +// --------------------------------------------------------------------------- + +GDALDataset *GeoRasterDataset::Create( const char *pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + char **papszOptions ) +{ + // ------------------------------------------------------------------- + // Check for supported Data types + // ------------------------------------------------------------------- + + const char* pszCellDepth = OWSetDataType( eType ); + + if( EQUAL( pszCellDepth, "Unknown" ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create GeoRaster with unsupported data type (%s)", + GDALGetDataTypeName( eType ) ); + return NULL; + } + + // ------------------------------------------------------------------- + // Open the Dataset + // ------------------------------------------------------------------- + + GeoRasterDataset* poGRD = NULL; + + poGRD = (GeoRasterDataset*) GDALOpen( pszFilename, GA_Update ); + + if( ! poGRD ) + { + return NULL; + } + + // ------------------------------------------------------------------- + // Get the GeoRaster + // ------------------------------------------------------------------- + + GeoRasterWrapper* poGRW = poGRD->poGeoRaster; + + if( ! poGRW ) + { + delete poGRD; + return NULL; + } + + // ------------------------------------------------------------------- + // Set basic information and default values + // ------------------------------------------------------------------- + + poGRW->nRasterColumns = nXSize; + poGRW->nRasterRows = nYSize; + poGRW->nRasterBands = nBands; + poGRW->sCellDepth = pszCellDepth; + poGRW->nRowBlockSize = DEFAULT_BLOCK_ROWS; + poGRW->nColumnBlockSize = DEFAULT_BLOCK_COLUMNS; + poGRW->nBandBlockSize = 1; + + if( poGRW->bUniqueFound ) + { + poGRW->PrepareToOverwrite(); + } + + // ------------------------------------------------------------------- + // Check the create options to use in initialization + // ------------------------------------------------------------------- + + const char* pszFetched = ""; + char* pszDescription = NULL; + char* pszInsert = NULL; + int nQuality = -1; + + if( ! poGRW->sTable.empty() ) + { + pszFetched = CSLFetchNameValue( papszOptions, "DESCRIPTION" ); + + if( pszFetched ) + { + pszDescription = CPLStrdup( pszFetched ); + } + } + + if( poGRW->sTable.empty() ) + { + poGRW->sTable = "GDAL_IMPORT"; + poGRW->sDataTable = "GDAL_RDT"; + } + + if( poGRW->sColumn.empty() ) + { + poGRW->sColumn = "RASTER"; + } + + pszFetched = CSLFetchNameValue( papszOptions, "INSERT" ); + + if( pszFetched ) + { + pszInsert = CPLStrdup( pszFetched ); + } + + pszFetched = CSLFetchNameValue( papszOptions, "BLOCKXSIZE" ); + + if( pszFetched ) + { + poGRW->nColumnBlockSize = atoi( pszFetched ); + } + + pszFetched = CSLFetchNameValue( papszOptions, "BLOCKYSIZE" ); + + if( pszFetched ) + { + poGRW->nRowBlockSize = atoi( pszFetched ); + } + + pszFetched = CSLFetchNameValue( papszOptions, "NBITS" ); + + if( pszFetched != NULL ) + { + poGRW->sCellDepth = CPLSPrintf( "%dBIT", atoi( pszFetched ) ); + } + + pszFetched = CSLFetchNameValue( papszOptions, "COMPRESS" ); + + if( pszFetched != NULL && + ( EQUALN( pszFetched, "JPEG", 4 ) || + EQUAL( pszFetched, "DEFLATE" ) ) ) + { + poGRW->sCompressionType = pszFetched; + } + else + { + poGRW->sCompressionType = "NONE"; + } + + pszFetched = CSLFetchNameValue( papszOptions, "QUALITY" ); + + if( pszFetched ) + { + poGRW->nCompressQuality = atoi( pszFetched ); + nQuality = poGRW->nCompressQuality; + } + + pszFetched = CSLFetchNameValue( papszOptions, "INTERLEAVE" ); + + bool bInterleve_ind = false; + + if( pszFetched ) + { + bInterleve_ind = true; + + if( EQUAL( pszFetched, "BAND" ) || EQUAL( pszFetched, "BSQ" ) ) + { + poGRW->sInterleaving = "BSQ"; + } + if( EQUAL( pszFetched, "LINE" ) || EQUAL( pszFetched, "BIL" ) ) + { + poGRW->sInterleaving = "BIL"; + } + if( EQUAL( pszFetched, "PIXEL" ) || EQUAL( pszFetched, "BIP" ) ) + { + poGRW->sInterleaving = "BIP"; + } + } + else + { + if( EQUAL( poGRW->sCompressionType.c_str(), "NONE" ) == false ) + { + poGRW->sInterleaving = "BIP"; + } + } + + pszFetched = CSLFetchNameValue( papszOptions, "BLOCKBSIZE" ); + + if( pszFetched ) + { + poGRW->nBandBlockSize = atoi( pszFetched ); + } + else + { + if( ! EQUAL( poGRW->sCompressionType.c_str(), "NONE" ) && + ( nBands == 3 || nBands == 4 ) ) + { + poGRW->nBandBlockSize = nBands; + } + } + + if( bInterleve_ind == false && + ( poGRW->nBandBlockSize == 3 || poGRW->nBandBlockSize == 4 ) ) + { + poGRW->sInterleaving = "BIP"; + } + + if( EQUALN( poGRW->sCompressionType.c_str(), "JPEG", 4 ) ) + { + if( ! EQUAL( poGRW->sInterleaving.c_str(), "BIP" ) ) + { + CPLError( CE_Warning, CPLE_IllegalArg, + "compress=JPEG assumes interleave=BIP" ); + poGRW->sInterleaving = "BIP"; + } + } + + pszFetched = CSLFetchNameValue( papszOptions, "BLOCKING" ); + + if( pszFetched ) + { + if( EQUAL( pszFetched, "NO" ) ) + { + poGRW->bBlocking = false; + } + + if( EQUAL( pszFetched, "OPTIMALPADDING" ) ) + { + if( poGRW->poConnection->GetVersion() < 11 ) + { + CPLError( CE_Warning, CPLE_IllegalArg, + "BLOCKING=OPTIMALPADDING not supported on Oracle older than 11g" ); + } + else + { + poGRW->bAutoBlocking = true; + poGRW->bBlocking = true; + } + } + } + + // ------------------------------------------------------------------- + // Validate options + // ------------------------------------------------------------------- + + if( pszDescription && poGRW->bUniqueFound ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Option (DESCRIPTION) cannot be used on a existing GeoRaster." ); + delete poGRD; + return NULL; + } + + if( pszInsert && poGRW->bUniqueFound ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Option (INSERT) cannot be used on a existing GeoRaster." ); + delete poGRD; + return NULL; + } + + /* Compression JPEG-B is deprecated. It should be able to read but to + * to create new GeoRaster on databases with that compression option. + * + * TODO: Remove that options on next release. + */ + if( EQUAL( poGRW->sCompressionType.c_str(), "JPEG-B" ) ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Option (COMPRESS=%s) is deprecated and cannot be used.", + poGRW->sCompressionType.c_str() ); + delete poGRD; + return NULL; + } + + if( EQUAL( poGRW->sCompressionType.c_str(), "JPEG-F" ) ) + { + /* JPEG-F can only compress byte data type + */ + if( eType != GDT_Byte ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Option (COMPRESS=%s) can only be used with Byte data type.", + poGRW->sCompressionType.c_str() ); + delete poGRD; + return NULL; + } + + /* JPEG-F can compress one band per block or 3 for RGB + * or 4 for RGBA. + */ + if( ( poGRW->nBandBlockSize != 1 && + poGRW->nBandBlockSize != 3 && + poGRW->nBandBlockSize != 4 ) || + ( ( poGRW->nBandBlockSize != 1 && + ( poGRW->nBandBlockSize != poGRW->nRasterBands ) ) ) ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Option (COMPRESS=%s) requires BLOCKBSIZE to be 1 (for any " + "number of bands), 3 (for 3 bands RGB) and 4 (for 4 bands RGBA).", + poGRW->sCompressionType.c_str() ); + delete poGRD; + return NULL; + } + + /* There is a limite on how big a compressed block can be. + */ + if( ( poGRW->nColumnBlockSize * + poGRW->nRowBlockSize * + poGRW->nBandBlockSize * + ( GDALGetDataTypeSize( eType ) / 8 ) ) > ( 50 * 1024 * 1024 ) ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Option (COMPRESS=%s) each data block must not exceed 50Mb. " + "Consider reducing BLOCK{X,Y,B}XSIZE.", + poGRW->sCompressionType.c_str() ); + delete poGRD; + return NULL; + } + } + + if( EQUAL( poGRW->sCompressionType.c_str(), "DEFLATE" ) ) + { + if( ( poGRW->nColumnBlockSize * + poGRW->nRowBlockSize * + poGRW->nBandBlockSize * + ( GDALGetDataTypeSize( eType ) / 8 ) ) > ( 1024 * 1024 * 1024 ) ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "For (COMPRESS=%s) each data block must not exceed 1Gb. " + "Consider reducing BLOCK{X,Y,B}XSIZE.", + poGRW->sCompressionType.c_str() ); + delete poGRD; + return NULL; + } + } + + pszFetched = CSLFetchNameValue( papszOptions, "OBJECTTABLE" ); + + if( pszFetched ) + { + int nVersion = poGRW->poConnection->GetVersion(); + if( nVersion <= 11 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Driver create-option OBJECTTABLE not " + "supported on Oracle %d", nVersion ); + delete poGRD; + return NULL; + } + } + + poGRD->poGeoRaster->bCreateObjectTable = (bool) + CSLFetchBoolean( papszOptions, "OBJECTTABLE", FALSE ); + + // ------------------------------------------------------------------- + // Create a SDO_GEORASTER object on the server + // ------------------------------------------------------------------- + + bool bSucced = poGRW->Create( pszDescription, pszInsert, poGRW->bUniqueFound ); + + CPLFree( pszInsert ); + CPLFree( pszDescription ); + + if( ! bSucced ) + { + delete poGRD; + return NULL; + } + + // ------------------------------------------------------------------- + // Prepare an identification string + // ------------------------------------------------------------------- + + char szStringId[OWTEXT]; + + strcpy( szStringId, CPLSPrintf( "georaster:%s,%s,%s,%s,%d", + poGRW->poConnection->GetUser(), + poGRW->poConnection->GetPassword(), + poGRW->poConnection->GetServer(), + poGRW->sDataTable.c_str(), + poGRW->nRasterId ) ); + + delete poGRD; + + poGRD = (GeoRasterDataset*) GDALOpen( szStringId, GA_Update ); + + if( ! poGRD ) + { + return NULL; + } + + // ------------------------------------------------------------------- + // Load aditional options + // ------------------------------------------------------------------- + + pszFetched = CSLFetchNameValue( papszOptions, "VATNAME" ); + + if( pszFetched ) + { + poGRW->sValueAttributeTab = pszFetched; + } + + pszFetched = CSLFetchNameValue( papszOptions, "SRID" ); + + if( pszFetched ) + { + poGRD->bForcedSRID = true; + poGRD->poGeoRaster->SetGeoReference( atoi( pszFetched ) ); + } + + poGRD->poGeoRaster->bGenSpatialIndex = (bool) + CSLFetchBoolean( papszOptions, "SPATIALEXTENT", TRUE ); + + pszFetched = CSLFetchNameValue( papszOptions, "EXTENTSRID" ); + + if( pszFetched ) + { + poGRD->poGeoRaster->nExtentSRID = atoi( pszFetched ); + } + + pszFetched = CSLFetchNameValue( papszOptions, "COORDLOCATION" ); + + if( pszFetched ) + { + if( EQUAL( pszFetched, "CENTER" ) ) + { + poGRD->poGeoRaster->eModelCoordLocation = MCL_CENTER; + } + else if( EQUAL( pszFetched, "UPPERLEFT" ) ) + { + poGRD->poGeoRaster->eModelCoordLocation = MCL_UPPERLEFT; + } + else + { + CPLError( CE_Warning, CPLE_IllegalArg, + "Incorrect COORDLOCATION (%s)", pszFetched ); + } + } + + if ( nQuality > 0 ) + { + poGRD->poGeoRaster->nCompressQuality = nQuality; + } + + pszFetched = CSLFetchNameValue( papszOptions, "GENPYRAMID" ); + + if( pszFetched != NULL ) + { + if (!(EQUAL(pszFetched, "NN") || + EQUAL(pszFetched, "BILINEAR") || + EQUAL(pszFetched, "BIQUADRATIC") || + EQUAL(pszFetched, "CUBIC") || + EQUAL(pszFetched, "AVERAGE4") || + EQUAL(pszFetched, "AVERAGE16"))) + { + CPLError( CE_Warning, CPLE_IllegalArg, "Wrong resample method for pyramid (%s)", pszFetched); + } + + poGRD->poGeoRaster->bGenPyramid = true; + poGRD->poGeoRaster->sPyramidResampling = pszFetched; + } + + pszFetched = CSLFetchNameValue( papszOptions, "GENPYRLEVELS" ); + + if( pszFetched != NULL ) + { + poGRD->poGeoRaster->bGenPyramid = true; + poGRD->poGeoRaster->nPyramidLevels = atoi(pszFetched); + } + + // ------------------------------------------------------------------- + // Return a new Dataset + // ------------------------------------------------------------------- + + return (GDALDataset*) poGRD; +} + +// --------------------------------------------------------------------------- +// CreateCopy() +// --------------------------------------------------------------------------- + +GDALDataset *GeoRasterDataset::CreateCopy( const char* pszFilename, + GDALDataset* poSrcDS, + int bStrict, + char** papszOptions, + GDALProgressFunc pfnProgress, + void* pProgressData ) +{ + (void) bStrict; + + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GeoRaster driver does not support source dataset with zero band.\n"); + return NULL; + } + + GDALRasterBand* poBand = poSrcDS->GetRasterBand( 1 ); + GDALDataType eType = poBand->GetRasterDataType(); + + // ----------------------------------------------------------- + // Create a GeoRaster on the server or select one to overwrite + // ----------------------------------------------------------- + + GeoRasterDataset *poDstDS; + + poDstDS = (GeoRasterDataset *) GeoRasterDataset::Create( pszFilename, + poSrcDS->GetRasterXSize(), + poSrcDS->GetRasterYSize(), + poSrcDS->GetRasterCount(), + eType, papszOptions ); + + if( poDstDS == NULL ) + { + return NULL; + } + + // ----------------------------------------------------------- + // Copy information to the dataset + // ----------------------------------------------------------- + + double adfTransform[6]; + + if ( poSrcDS->GetGeoTransform( adfTransform ) == CE_None ) + { + if ( ! ( adfTransform[0] == 0.0 && + adfTransform[1] == 1.0 && + adfTransform[2] == 0.0 && + adfTransform[3] == 0.0 && + adfTransform[4] == 0.0 && + adfTransform[5] == 1.0 ) ) + { + poDstDS->SetGeoTransform( adfTransform ); + } + } + + if( ! poDstDS->bForcedSRID ) /* forced by create option SRID */ + { + poDstDS->SetProjection( poSrcDS->GetProjectionRef() ); + } + + // -------------------------------------------------------------------- + // Copy RPC + // -------------------------------------------------------------------- + + char **papszRPCMetadata = GDALGetMetadata( poSrcDS, "RPC" ); + + if ( papszRPCMetadata != NULL ) + { + poDstDS->poGeoRaster->phRPC = (GDALRPCInfo*) VSIMalloc( sizeof(GDALRPCInfo) ); + GDALExtractRPCInfo( papszRPCMetadata, poDstDS->poGeoRaster->phRPC ); + } + + // -------------------------------------------------------------------- + // Copy information to the raster bands + // -------------------------------------------------------------------- + + int bHasNoDataValue = FALSE; + double dfNoDataValue = 0.0; + double dfMin = 0.0, dfMax = 0.0, dfStdDev = 0.0, dfMean = 0.0; + double dfMedian = 0.0, dfMode = 0.0; + int iBand = 0; + + for( iBand = 1; iBand <= poSrcDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand* poSrcBand = poSrcDS->GetRasterBand( iBand ); + GeoRasterRasterBand* poDstBand = (GeoRasterRasterBand*) + poDstDS->GetRasterBand( iBand ); + + // ---------------------------------------------------------------- + // Copy Color Table + // ---------------------------------------------------------------- + + GDALColorTable* poColorTable = poSrcBand->GetColorTable(); + + if( poColorTable ) + { + poDstBand->SetColorTable( poColorTable ); + } + + // ---------------------------------------------------------------- + // Copy statitics information, without median and mode + // ---------------------------------------------------------------- + + if( poSrcBand->GetStatistics( false, false, &dfMin, &dfMax, + &dfMean, &dfStdDev ) == CE_None ) + { + poDstBand->SetStatistics( dfMin, dfMax, dfMean, dfStdDev ); + + /* That will not be recorded in the GeoRaster metadata since it + * doesn't have median and mode, so those values are only useful + * at runtime. + */ + } + + // ---------------------------------------------------------------- + // Copy statitics metadata information, including median and mode + // ---------------------------------------------------------------- + + const char *pszMin = poSrcBand->GetMetadataItem( "STATISTICS_MINIMUM" ); + const char *pszMax = poSrcBand->GetMetadataItem( "STATISTICS_MAXIMUM" ); + const char *pszMean = poSrcBand->GetMetadataItem( "STATISTICS_MEAN" ); + const char *pszMedian = poSrcBand->GetMetadataItem( "STATISTICS_MEDIAN" ); + const char *pszMode = poSrcBand->GetMetadataItem( "STATISTICS_MODE" ); + const char *pszStdDev = poSrcBand->GetMetadataItem( "STATISTICS_STDDEV" ); + const char *pszSkipFX = poSrcBand->GetMetadataItem( "STATISTICS_SKIPFACTORX" ); + const char *pszSkipFY = poSrcBand->GetMetadataItem( "STATISTICS_SKIPFACTORY" ); + + if ( pszMin != NULL && pszMax != NULL && pszMean != NULL && + pszMedian != NULL && pszMode != NULL && pszStdDev != NULL ) + { + dfMin = CPLScanDouble( pszMin, MAX_DOUBLE_STR_REP ); + dfMax = CPLScanDouble( pszMax, MAX_DOUBLE_STR_REP ); + dfMean = CPLScanDouble( pszMean, MAX_DOUBLE_STR_REP ); + dfMedian = CPLScanDouble( pszMedian, MAX_DOUBLE_STR_REP ); + dfMode = CPLScanDouble( pszMode, MAX_DOUBLE_STR_REP ); + + if ( ! ( ( dfMin > dfMax ) || + ( dfMean > dfMax ) || ( dfMean < dfMin ) || + ( dfMedian > dfMax ) || ( dfMedian < dfMin ) || + ( dfMode > dfMax ) || ( dfMode < dfMin ) ) ) + { + if ( ! pszSkipFX ) + { + pszSkipFX = pszSkipFY != NULL ? pszSkipFY : "1"; + } + + poDstBand->poGeoRaster->SetStatistics( iBand, + pszMin, pszMax, pszMean, + pszMedian, pszMode, + pszStdDev, pszSkipFX ); + } + } + + // ---------------------------------------------------------------- + // Copy Raster Attribute Table (RAT) + // ---------------------------------------------------------------- + + GDALRasterAttributeTableH poRAT = GDALGetDefaultRAT( poSrcBand ); + + if( poRAT != NULL ) + { + poDstBand->SetDefaultRAT( (GDALRasterAttributeTable*) poRAT ); + } + + // ---------------------------------------------------------------- + // Copy NoData Value + // ---------------------------------------------------------------- + + dfNoDataValue = poSrcBand->GetNoDataValue( &bHasNoDataValue ); + + if( bHasNoDataValue ) + { + poDstBand->SetNoDataValue( dfNoDataValue ); + } + } + + // -------------------------------------------------------------------- + // Copy actual imagery. + // -------------------------------------------------------------------- + + int nXSize = poDstDS->GetRasterXSize(); + int nYSize = poDstDS->GetRasterYSize(); + + int nBlockXSize = 0; + int nBlockYSize = 0; + + poDstDS->GetRasterBand( 1 )->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + void *pData = VSIMalloc( nBlockXSize * nBlockYSize * + GDALGetDataTypeSize( eType ) / 8 ); + + if( pData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GeoRaster::CreateCopy : Out of memory " ); + delete poDstDS; + return NULL; + } + + int iYOffset = 0; + int iXOffset = 0; + int iXBlock = 0; + int iYBlock = 0; + int nBlockCols = 0; + int nBlockRows = 0; + CPLErr eErr = CE_None; + + int nPixelSize = GDALGetDataTypeSize( + poSrcDS->GetRasterBand(1)->GetRasterDataType() ) / 8; + + if( poDstDS->poGeoRaster->nBandBlockSize == 1) + { + // ---------------------------------------------------------------- + // Band order + // ---------------------------------------------------------------- + + int nBandCount = poSrcDS->GetRasterCount(); + + for( iBand = 1; iBand <= nBandCount; iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand ); + GDALRasterBand *poDstBand = poDstDS->GetRasterBand( iBand ); + + for( iYOffset = 0, iYBlock = 0; + iYOffset < nYSize; + iYOffset += nBlockYSize, iYBlock++ ) + { + + for( iXOffset = 0, iXBlock = 0; + iXOffset < nXSize; + iXOffset += nBlockXSize, iXBlock++ ) + { + + nBlockCols = MIN( nBlockXSize, nXSize - iXOffset ); + nBlockRows = MIN( nBlockYSize, nYSize - iYOffset ); + + eErr = poSrcBand->RasterIO( GF_Read, + iXOffset, iYOffset, + nBlockCols, nBlockRows, pData, + nBlockCols, nBlockRows, eType, + nPixelSize, + nPixelSize * nBlockXSize, NULL ); + + if( eErr != CE_None ) + { + return NULL; + } + + eErr = poDstBand->WriteBlock( iXBlock, iYBlock, pData ); + + if( eErr != CE_None ) + { + return NULL; + } + } + + if( ( eErr == CE_None ) && ( ! pfnProgress( + ( ( iBand - 1) / (float) nBandCount ) + + ( iYOffset + nBlockRows ) / (float) (nYSize * nBandCount), + NULL, pProgressData ) ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + } + } + else + { + // ---------------------------------------------------------------- + // Block order + // ---------------------------------------------------------------- + + poDstDS->poGeoRaster->SetWriteOnly( true ); + + for( iYOffset = 0, iYBlock = 0; + iYOffset < nYSize; + iYOffset += nBlockYSize, iYBlock++ ) + { + for( iXOffset = 0, iXBlock = 0; + iXOffset < nXSize; + iXOffset += nBlockXSize, iXBlock++ ) + { + nBlockCols = MIN( nBlockXSize, nXSize - iXOffset ); + nBlockRows = MIN( nBlockYSize, nYSize - iYOffset ); + + for( iBand = 1; + iBand <= poSrcDS->GetRasterCount(); + iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand ); + GDALRasterBand *poDstBand = poDstDS->GetRasterBand( iBand ); + + eErr = poSrcBand->RasterIO( GF_Read, + iXOffset, iYOffset, + nBlockCols, nBlockRows, pData, + nBlockCols, nBlockRows, eType, + nPixelSize, + nPixelSize * nBlockXSize, NULL ); + + if( eErr != CE_None ) + { + return NULL; + } + + eErr = poDstBand->WriteBlock( iXBlock, iYBlock, pData ); + + if( eErr != CE_None ) + { + return NULL; + } + } + + } + + if( ( eErr == CE_None ) && ( ! pfnProgress( + ( iYOffset + nBlockRows ) / (double) nYSize, NULL, + pProgressData ) ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + } + + CPLFree( pData ); + + // -------------------------------------------------------------------- + // Finalize + // -------------------------------------------------------------------- + + poDstDS->FlushCache(); + + if( pfnProgress ) + { + printf( "Ouput dataset: (georaster:%s/%s@%s,%s,%d) on %s%s,%s\n", + poDstDS->poGeoRaster->poConnection->GetUser(), + poDstDS->poGeoRaster->poConnection->GetPassword(), + poDstDS->poGeoRaster->poConnection->GetServer(), + poDstDS->poGeoRaster->sDataTable.c_str(), + poDstDS->poGeoRaster->nRasterId, + poDstDS->poGeoRaster->sSchema.c_str(), + poDstDS->poGeoRaster->sTable.c_str(), + poDstDS->poGeoRaster->sColumn.c_str() ); + } + + return poDstDS; +} + +// --------------------------------------------------------------------------- +// IRasterIO() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + if( poGeoRaster->nBandBlockSize > 1 ) + { + return GDALDataset::BlockBasedRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, + nLineSpace, nBandSpace, psExtraArg ); + } + else + { + return GDALDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); + } +} + +// --------------------------------------------------------------------------- +// FlushCache() +// --------------------------------------------------------------------------- + +void GeoRasterDataset::FlushCache() +{ + GDALDataset::FlushCache(); +} + +// --------------------------------------------------------------------------- +// GetGeoTransform() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterDataset::GetGeoTransform( double *padfTransform ) +{ + if( poGeoRaster->phRPC ) + { + return CE_Failure; + } + + if( poGeoRaster->nSRID == 0 ) + { + return CE_Failure; + } + + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + + bGeoTransform = true; + + return CE_None; +} + +// --------------------------------------------------------------------------- +// GetProjectionRef() +// --------------------------------------------------------------------------- + +const char* GeoRasterDataset::GetProjectionRef( void ) +{ + if( poGeoRaster->phRPC ) + { + return ""; + } + + if( ! poGeoRaster->bIsReferenced ) + { + return ""; + } + + if( poGeoRaster->nSRID == UNKNOWN_CRS || poGeoRaster->nSRID == 0 ) + { + return ""; + } + + if( pszProjection ) + { + return pszProjection; + } + + OGRSpatialReference oSRS; + + // -------------------------------------------------------------------- + // Check if the SRID is a valid EPSG code + // -------------------------------------------------------------------- + + CPLPushErrorHandler( CPLQuietErrorHandler ); + + if( oSRS.importFromEPSG( poGeoRaster->nSRID ) == OGRERR_NONE ) + { + /* + * Ignores the WKT from Oracle and use the one from GDAL's + * EPSG tables. That would ensure that other drivers/software + * will recognizize the parameters. + */ + + if( oSRS.exportToWkt( &pszProjection ) == OGRERR_NONE ) + { + CPLPopErrorHandler(); + + return pszProjection; + } + } + + CPLPopErrorHandler(); + + // -------------------------------------------------------------------- + // Try to interpreter the WKT text + // -------------------------------------------------------------------- + + char* pszWKText = CPLStrdup( poGeoRaster->sWKText ); + + if( ! ( oSRS.importFromWkt( &pszWKText ) == OGRERR_NONE && oSRS.GetRoot() ) ) + { + return ""; + } + + // ---------------------------------------------------------------- + // Decorate with ORACLE Authority codes + // ---------------------------------------------------------------- + + oSRS.SetAuthority(oSRS.GetRoot()->GetValue(), "ORACLE", poGeoRaster->nSRID); + + int nSpher = OWParseEPSG( oSRS.GetAttrValue("GEOGCS|DATUM|SPHEROID") ); + + if( nSpher > 0 ) + { + oSRS.SetAuthority( "GEOGCS|DATUM|SPHEROID", "EPSG", nSpher ); + } + + int nDatum = OWParseEPSG( oSRS.GetAttrValue("GEOGCS|DATUM") ); + + if( nDatum > 0 ) + { + oSRS.SetAuthority( "GEOGCS|DATUM", "EPSG", nDatum ); + } + + // ---------------------------------------------------------------- + // Checks for Projection info + // ---------------------------------------------------------------- + + const char *pszProjName = oSRS.GetAttrValue( "PROJECTION" ); + + if( pszProjName ) + { + int nProj = OWParseEPSG( pszProjName ); + + // ---------------------------------------------------------------- + // Decorate with EPSG Authority + // ---------------------------------------------------------------- + + if( nProj > 0 ) + { + oSRS.SetAuthority( "PROJECTION", "EPSG", nProj ); + } + + // ---------------------------------------------------------------- + // Translate projection names to GDAL's standards + // ---------------------------------------------------------------- + + if ( EQUAL( pszProjName, "Transverse Mercator" ) ) + { + oSRS.SetProjection( SRS_PT_TRANSVERSE_MERCATOR ); + } + else if ( EQUAL( pszProjName, "Albers Conical Equal Area" ) ) + { + oSRS.SetProjection( SRS_PT_ALBERS_CONIC_EQUAL_AREA ); + } + else if ( EQUAL( pszProjName, "Azimuthal Equidistant" ) ) + { + oSRS.SetProjection( SRS_PT_AZIMUTHAL_EQUIDISTANT ); + } + else if ( EQUAL( pszProjName, "Miller Cylindrical" ) ) + { + oSRS.SetProjection( SRS_PT_MILLER_CYLINDRICAL ); + } + else if ( EQUAL( pszProjName, "Hotine Oblique Mercator" ) ) + { + oSRS.SetProjection( SRS_PT_HOTINE_OBLIQUE_MERCATOR ); + } + else if ( EQUAL( pszProjName, "Wagner IV" ) ) + { + oSRS.SetProjection( SRS_PT_WAGNER_IV ); + } + else if ( EQUAL( pszProjName, "Wagner VII" ) ) + { + oSRS.SetProjection( SRS_PT_WAGNER_VII ); + } + else if ( EQUAL( pszProjName, "Eckert IV" ) ) + { + oSRS.SetProjection( SRS_PT_ECKERT_IV ); + } + else if ( EQUAL( pszProjName, "Eckert VI" ) ) + { + oSRS.SetProjection( SRS_PT_ECKERT_VI ); + } + else if ( EQUAL( pszProjName, "New Zealand Map Grid" ) ) + { + oSRS.SetProjection( SRS_PT_NEW_ZEALAND_MAP_GRID ); + } + else if ( EQUAL( pszProjName, "Lambert Conformal Conic" ) ) + { + oSRS.SetProjection( SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP ); + } + else if ( EQUAL( pszProjName, "Lambert Azimuthal Equal Area" ) ) + { + oSRS.SetProjection( SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA ); + } + else if ( EQUAL( pszProjName, "Van der Grinten" ) ) + { + oSRS.SetProjection( SRS_PT_VANDERGRINTEN ); + } + else if ( EQUAL( + pszProjName, "Lambert Conformal Conic (Belgium 1972)" ) ) + { + oSRS.SetProjection( SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM ); + } + else if ( EQUAL( pszProjName, "Cylindrical Equal Area" ) ) + { + oSRS.SetProjection( SRS_PT_CYLINDRICAL_EQUAL_AREA ); + } + else if ( EQUAL( pszProjName, "Interrupted Goode Homolosine" ) ) + { + oSRS.SetProjection( SRS_PT_GOODE_HOMOLOSINE ); + } + } + + oSRS.exportToWkt( &pszProjection ); + + return pszProjection; +} + +// --------------------------------------------------------------------------- +// SetGeoTransform() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterDataset::SetGeoTransform( double *padfTransform ) +{ + memcpy( adfGeoTransform, padfTransform, sizeof( double ) * 6 ); + + poGeoRaster->dfXCoefficient[0] = adfGeoTransform[1]; + poGeoRaster->dfXCoefficient[1] = adfGeoTransform[2]; + poGeoRaster->dfXCoefficient[2] = adfGeoTransform[0]; + poGeoRaster->dfYCoefficient[0] = adfGeoTransform[4]; + poGeoRaster->dfYCoefficient[1] = adfGeoTransform[5]; + poGeoRaster->dfYCoefficient[2] = adfGeoTransform[3]; + + bGeoTransform = true; + + return CE_None; +} + +// --------------------------------------------------------------------------- +// SetProjection() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterDataset::SetProjection( const char *pszProjString ) +{ + OGRSpatialReference oSRS; + + char* pszWKT = CPLStrdup( pszProjString ); + + OGRErr eOGRErr = oSRS.importFromWkt( &pszWKT ); + + if( eOGRErr != OGRERR_NONE ) + { + poGeoRaster->SetGeoReference( DEFAULT_CRS ); + + return CE_Failure; + } + + // -------------------------------------------------------------------- + // Try to extract EPGS authority code + // -------------------------------------------------------------------- + + const char *pszAuthName = NULL, *pszAuthCode = NULL; + + if( oSRS.IsGeographic() ) + { + pszAuthName = oSRS.GetAuthorityName( "GEOGCS" ); + pszAuthCode = oSRS.GetAuthorityCode( "GEOGCS" ); + } + else if( oSRS.IsProjected() ) + { + pszAuthName = oSRS.GetAuthorityName( "PROJCS" ); + pszAuthCode = oSRS.GetAuthorityCode( "PROJCS" ); + } + + if( pszAuthName != NULL && pszAuthCode != NULL ) + { + if( EQUAL( pszAuthName, "ORACLE" ) || + EQUAL( pszAuthName, "EPSG" ) ) + { + poGeoRaster->SetGeoReference( atoi( pszAuthCode ) ); + return CE_None; + } + } + + // ---------------------------------------------------------------- + // Convert SRS into old style format (SF-SQL 1.0) + // ---------------------------------------------------------------- + + OGRSpatialReference *poSRS2 = oSRS.Clone(); + + poSRS2->StripCTParms(); + + double dfAngularUnits = poSRS2->GetAngularUnits( NULL ); + + if( fabs(dfAngularUnits - 0.0174532925199433) < 0.0000000000000010 ) + { + /* match the precision used on Oracle for that particular value */ + + poSRS2->SetAngularUnits( "Decimal Degree", 0.0174532925199433 ); + } + + char* pszCloneWKT = NULL; + + if( poSRS2->exportToWkt( &pszCloneWKT ) != OGRERR_NONE ) + { + delete poSRS2; + return CE_Failure; + } + + const char *pszProjName = poSRS2->GetAttrValue( "PROJECTION" ); + + if( pszProjName ) + { + // ---------------------------------------------------------------- + // Translate projection names to Oracle's standards + // ---------------------------------------------------------------- + + if ( EQUAL( pszProjName, SRS_PT_TRANSVERSE_MERCATOR ) ) + { + poSRS2->SetProjection( "Transverse Mercator" ); + } + else if ( EQUAL( pszProjName, SRS_PT_ALBERS_CONIC_EQUAL_AREA ) ) + { + poSRS2->SetProjection( "Albers Conical Equal Area" ); + } + else if ( EQUAL( pszProjName, SRS_PT_AZIMUTHAL_EQUIDISTANT ) ) + { + poSRS2->SetProjection( "Azimuthal Equidistant" ); + } + else if ( EQUAL( pszProjName, SRS_PT_MILLER_CYLINDRICAL ) ) + { + poSRS2->SetProjection( "Miller Cylindrical" ); + } + else if ( EQUAL( pszProjName, SRS_PT_HOTINE_OBLIQUE_MERCATOR ) ) + { + poSRS2->SetProjection( "Hotine Oblique Mercator" ); + } + else if ( EQUAL( pszProjName, SRS_PT_WAGNER_IV ) ) + { + poSRS2->SetProjection( "Wagner IV" ); + } + else if ( EQUAL( pszProjName, SRS_PT_WAGNER_VII ) ) + { + poSRS2->SetProjection( "Wagner VII" ); + } + else if ( EQUAL( pszProjName, SRS_PT_ECKERT_IV ) ) + { + poSRS2->SetProjection( "Eckert IV" ); + } + else if ( EQUAL( pszProjName, SRS_PT_ECKERT_VI ) ) + { + poSRS2->SetProjection( "Eckert VI" ); + } + else if ( EQUAL( pszProjName, SRS_PT_NEW_ZEALAND_MAP_GRID ) ) + { + poSRS2->SetProjection( "New Zealand Map Grid" ); + } + else if ( EQUAL( pszProjName, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP ) ) + { + poSRS2->SetProjection( "Lambert Conformal Conic" ); + } + else if ( EQUAL( pszProjName, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA ) ) + { + poSRS2->SetProjection( "Lambert Azimuthal Equal Area" ); + } + else if ( EQUAL( pszProjName, SRS_PT_VANDERGRINTEN ) ) + { + poSRS2->SetProjection( "Van der Grinten" ); + } + else if ( EQUAL( + pszProjName, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM ) ) + { + poSRS2->SetProjection( "Lambert Conformal Conic (Belgium 1972)" ); + } + else if ( EQUAL( pszProjName, SRS_PT_CYLINDRICAL_EQUAL_AREA ) ) + { + poSRS2->SetProjection( "Cylindrical Equal Area" ); + } + else if ( EQUAL( pszProjName, SRS_PT_GOODE_HOMOLOSINE ) ) + { + poSRS2->SetProjection( "Interrupted Goode Homolosine" ); + } + + // ---------------------------------------------------------------- + // Translate projection's parameters to Oracle's standards + // ---------------------------------------------------------------- + + char* pszStart = NULL; + + CPLFree( pszCloneWKT ); + + if( poSRS2->exportToWkt( &pszCloneWKT ) != OGRERR_NONE ) + { + delete poSRS2; + return CE_Failure; + } + + if( ( pszStart = strstr(pszCloneWKT, SRS_PP_AZIMUTH) ) ) + { + strncpy( pszStart, "Azimuth", strlen(SRS_PP_AZIMUTH) ); + } + + if( ( pszStart = strstr(pszCloneWKT, SRS_PP_CENTRAL_MERIDIAN) ) ) + { + strncpy( pszStart, "Central_Meridian", + strlen(SRS_PP_CENTRAL_MERIDIAN) ); + } + + if( ( pszStart = strstr(pszCloneWKT, SRS_PP_FALSE_EASTING) ) ) + { + strncpy( pszStart, "False_Easting", strlen(SRS_PP_FALSE_EASTING) ); + } + + if( ( pszStart = strstr(pszCloneWKT, SRS_PP_FALSE_NORTHING) ) ) + { + strncpy( pszStart, "False_Northing", + strlen(SRS_PP_FALSE_NORTHING) ); + } + + if( ( pszStart = strstr(pszCloneWKT, SRS_PP_LATITUDE_OF_CENTER) ) ) + { + strncpy( pszStart, "Latitude_Of_Center", + strlen(SRS_PP_LATITUDE_OF_CENTER) ); + } + + if( ( pszStart = strstr(pszCloneWKT, SRS_PP_LATITUDE_OF_ORIGIN) ) ) + { + strncpy( pszStart, "Latitude_Of_Origin", + strlen(SRS_PP_LATITUDE_OF_ORIGIN) ); + } + + if( ( pszStart = strstr(pszCloneWKT, SRS_PP_LONGITUDE_OF_CENTER) ) ) + { + strncpy( pszStart, "Longitude_Of_Center", + strlen(SRS_PP_LONGITUDE_OF_CENTER) ); + } + + if( ( pszStart = strstr(pszCloneWKT, SRS_PP_PSEUDO_STD_PARALLEL_1) ) ) + { + strncpy( pszStart, "Pseudo_Standard_Parallel_1", + strlen(SRS_PP_PSEUDO_STD_PARALLEL_1) ); + } + + if( ( pszStart = strstr(pszCloneWKT, SRS_PP_SCALE_FACTOR) ) ) + { + strncpy( pszStart, "Scale_Factor", strlen(SRS_PP_SCALE_FACTOR) ); + } + + if( ( pszStart = strstr(pszCloneWKT, SRS_PP_STANDARD_PARALLEL_1) ) ) + { + strncpy( pszStart, "Standard_Parallel_1", + strlen(SRS_PP_STANDARD_PARALLEL_1) ); + } + + if( ( pszStart = strstr(pszCloneWKT, SRS_PP_STANDARD_PARALLEL_2) ) ) + { + strncpy( pszStart, "Standard_Parallel_2", + strlen(SRS_PP_STANDARD_PARALLEL_2) ); + } + + if( ( pszStart = strstr(pszCloneWKT, SRS_PP_STANDARD_PARALLEL_2) ) ) + { + strncpy( pszStart, "Standard_Parallel_2", + strlen(SRS_PP_STANDARD_PARALLEL_2) ); + } + + // ---------------------------------------------------------------- + // Fix Unit name + // ---------------------------------------------------------------- + + if( ( pszStart = strstr(pszCloneWKT, "metre") ) ) + { + strncpy( pszStart, SRS_UL_METER, strlen(SRS_UL_METER) ); + } + } + + // -------------------------------------------------------------------- + // Tries to find a SRID compatible with the WKT + // -------------------------------------------------------------------- + + OWConnection* poConnection = poGeoRaster->poConnection; + OWStatement* poStmt = NULL; + + int nNewSRID = 0; + + char *pszFuncName = "FIND_GEOG_CRS"; + + if( poSRS2->IsProjected() ) + { + pszFuncName = "FIND_PROJ_CRS"; + } + + poStmt = poConnection->CreateStatement( CPLSPrintf( + "DECLARE\n" + " LIST SDO_SRID_LIST;" + "BEGIN\n" + " SELECT SDO_CS.%s('%s', null) into LIST FROM DUAL;\n" + " IF LIST.COUNT() > 0 then\n" + " SELECT LIST(1) into :out from dual;\n" + " ELSE\n" + " SELECT 0 into :out from dual;\n" + " END IF;\n" + "END;", + pszFuncName, + pszCloneWKT ) ); + + poStmt->BindName( ":out", &nNewSRID ); + + CPLPushErrorHandler( CPLQuietErrorHandler ); + + if( poStmt->Execute() ) + { + CPLPopErrorHandler(); + + if ( nNewSRID > 0 ) + { + poGeoRaster->SetGeoReference( nNewSRID ); + CPLFree( pszCloneWKT ); + return CE_None; + } + } + + // -------------------------------------------------------------------- + // Search by simplified WKT or insert it as a user defined SRS + // -------------------------------------------------------------------- + + int nCounter = 0; + + poStmt = poConnection->CreateStatement( CPLSPrintf( + "SELECT COUNT(*) FROM MDSYS.CS_SRS WHERE WKTEXT = '%s'", pszCloneWKT)); + + poStmt->Define( &nCounter ); + + CPLPushErrorHandler( CPLQuietErrorHandler ); + + if( poStmt->Execute() && nCounter > 0 ) + { + poStmt = poConnection->CreateStatement( CPLSPrintf( + "SELECT SRID FROM MDSYS.CS_SRS WHERE WKTEXT = '%s'", pszCloneWKT)); + + poStmt->Define( &nNewSRID ); + + if( poStmt->Execute() ) + { + CPLPopErrorHandler(); + + poGeoRaster->SetGeoReference( nNewSRID ); + CPLFree( pszCloneWKT ); + return CE_None; + } + } + + CPLPopErrorHandler(); + + poStmt = poConnection->CreateStatement( CPLSPrintf( + "DECLARE\n" + " MAX_SRID NUMBER := 0;\n" + "BEGIN\n" + " SELECT MAX(SRID) INTO MAX_SRID FROM MDSYS.CS_SRS;\n" + " MAX_SRID := MAX_SRID + 1;\n" + " INSERT INTO MDSYS.CS_SRS (SRID, WKTEXT, CS_NAME)\n" + " VALUES (MAX_SRID, '%s', '%s');\n" + " SELECT MAX_SRID INTO :out FROM DUAL;\n" + "END;", + pszCloneWKT, + oSRS.GetRoot()->GetChild(0)->GetValue() ) ); + + poStmt->BindName( ":out", &nNewSRID ); + + CPLErr eError = CE_None; + + CPLPushErrorHandler( CPLQuietErrorHandler ); + + if( poStmt->Execute() ) + { + CPLPopErrorHandler(); + + poGeoRaster->SetGeoReference( nNewSRID ); + } + else + { + CPLPopErrorHandler(); + + poGeoRaster->SetGeoReference( UNKNOWN_CRS ); + + CPLError( CE_Warning, CPLE_UserInterrupt, + "Insufficient privileges to insert reference system to " + "table MDSYS.CS_SRS." ); + + eError = CE_Warning; + } + + CPLFree( pszCloneWKT ); + + return eError; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **GeoRasterDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), + TRUE, + "SUBDATASETS", NULL); +} + +// --------------------------------------------------------------------------- +// GetMetadata() +// --------------------------------------------------------------------------- + +char **GeoRasterDataset::GetMetadata( const char *pszDomain ) +{ + if( pszDomain != NULL && EQUALN( pszDomain, "SUBDATASETS", 11 ) ) + return papszSubdatasets; + else + return GDALDataset::GetMetadata( pszDomain ); +} + +// --------------------------------------------------------------------------- +// Delete() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterDataset::Delete( const char* pszFilename ) +{ + (void) pszFilename; +/*** + GeoRasterDataset* poGRD = NULL; + + poGRD = (GeoRasterDataset*) GDALOpen( pszFilename, GA_Update ); + + if( ! poGRD ) + { + return CE_Failure; + } + + if( ! poGRD->poGeoRaster->Delete() ) + { + return CE_Failure; + } +***/ + return CE_None; +} + +// --------------------------------------------------------------------------- +// SetSubdatasets() +// --------------------------------------------------------------------------- + +void GeoRasterDataset::SetSubdatasets( GeoRasterWrapper* poGRW ) +{ + OWConnection* poConnection = poGRW->poConnection; + OWStatement* poStmt = NULL; + + // ----------------------------------------------------------- + // List all the GeoRaster Tables of that User/Database + // ----------------------------------------------------------- + + if( poGRW->sTable.empty() && + poGRW->sColumn.empty() ) + { + poStmt = poConnection->CreateStatement( + "SELECT DISTINCT TABLE_NAME, OWNER FROM ALL_SDO_GEOR_SYSDATA\n" + " ORDER BY TABLE_NAME ASC" ); + + char szTable[OWNAME]; + char szOwner[OWNAME]; + + poStmt->Define( szTable ); + poStmt->Define( szOwner ); + + if( poStmt->Execute() ) + { + int nCount = 1; + + do + { + papszSubdatasets = CSLSetNameValue( papszSubdatasets, + CPLSPrintf( "SUBDATASET_%d_NAME", nCount ), + CPLSPrintf( "geor:%s/%s@%s,%s.%s", + poConnection->GetUser(), poConnection->GetPassword(), + poConnection->GetServer(), szOwner, szTable ) ); + + papszSubdatasets = CSLSetNameValue( papszSubdatasets, + CPLSPrintf( "SUBDATASET_%d_DESC", nCount ), + CPLSPrintf( "%s.Table=%s", szOwner, szTable ) ); + + nCount++; + } + while( poStmt->Fetch() ); + } + + return; + } + + // ----------------------------------------------------------- + // List all the GeoRaster Columns of that Table + // ----------------------------------------------------------- + + if( ! poGRW->sTable.empty() && + poGRW->sColumn.empty() ) + { + poStmt = poConnection->CreateStatement( CPLSPrintf( + "SELECT DISTINCT COLUMN_NAME, OWNER FROM ALL_SDO_GEOR_SYSDATA\n" + " WHERE TABLE_NAME = UPPER('%s')\n" + " ORDER BY COLUMN_NAME ASC", + poGRW->sTable.c_str() ) ); + + char szColumn[OWNAME]; + char szOwner[OWNAME]; + + poStmt->Define( szColumn ); + poStmt->Define( szOwner ); + + if( poStmt->Execute() ) + { + int nCount = 1; + + do + { + papszSubdatasets = CSLSetNameValue( papszSubdatasets, + CPLSPrintf( "SUBDATASET_%d_NAME", nCount ), + CPLSPrintf( "geor:%s/%s@%s,%s.%s,%s", + poConnection->GetUser(), poConnection->GetPassword(), + poConnection->GetServer(), szOwner, + poGRW->sTable.c_str(), szColumn ) ); + + papszSubdatasets = CSLSetNameValue( papszSubdatasets, + CPLSPrintf( "SUBDATASET_%d_DESC", nCount ), + CPLSPrintf( "Table=%s.%s Column=%s", szOwner, + poGRW->sTable.c_str(), szColumn ) ); + + nCount++; + } + while( poStmt->Fetch() ); + } + + return; + } + + // ----------------------------------------------------------- + // List all the rows that contains GeoRaster on Table/Column/Where + // ----------------------------------------------------------- + + CPLString osAndWhere = ""; + + if( ! poGRW->sWhere.empty() ) + { + osAndWhere = CPLSPrintf( "AND %s", poGRW->sWhere.c_str() ); + } + + poStmt = poConnection->CreateStatement( CPLSPrintf( + "SELECT T.%s.RASTERDATATABLE, T.%s.RASTERID, \n" + " extractValue(t.%s.metadata, " +"'/georasterMetadata/rasterInfo/dimensionSize[@type=\"ROW\"]/size','%s'),\n" + " extractValue(t.%s.metadata, " +"'/georasterMetadata/rasterInfo/dimensionSize[@type=\"COLUMN\"]/size','%s'),\n" + " extractValue(t.%s.metadata, " +"'/georasterMetadata/rasterInfo/dimensionSize[@type=\"BAND\"]/size','%s'),\n" + " extractValue(t.%s.metadata, " +"'/georasterMetadata/rasterInfo/cellDepth','%s'),\n" + " extractValue(t.%s.metadata, " +"'/georasterMetadata/spatialReferenceInfo/SRID','%s')\n" + " FROM %s%s T\n" + " WHERE %s IS NOT NULL %s\n" + " ORDER BY T.%s.RASTERDATATABLE ASC,\n" + " T.%s.RASTERID ASC", + poGRW->sColumn.c_str(), poGRW->sColumn.c_str(), + poGRW->sColumn.c_str(), OW_XMLNS, + poGRW->sColumn.c_str(), OW_XMLNS, + poGRW->sColumn.c_str(), OW_XMLNS, + poGRW->sColumn.c_str(), OW_XMLNS, + poGRW->sColumn.c_str(), OW_XMLNS, + poGRW->sSchema.c_str(), poGRW->sTable.c_str(), + poGRW->sColumn.c_str(), osAndWhere.c_str(), + poGRW->sColumn.c_str(), poGRW->sColumn.c_str() ) ); + + char szDataTable[OWNAME]; + char szRasterId[OWNAME]; + char szRows[OWNAME]; + char szColumns[OWNAME]; + char szBands[OWNAME]; + char szCellDepth[OWNAME]; + char szSRID[OWNAME]; + + poStmt->Define( szDataTable ); + poStmt->Define( szRasterId ); + poStmt->Define( szRows ); + poStmt->Define( szColumns ); + poStmt->Define( szBands ); + poStmt->Define( szCellDepth ); + poStmt->Define( szSRID ); + + if( poStmt->Execute() ) + { + int nCount = 1; + + do + { + papszSubdatasets = CSLSetNameValue( papszSubdatasets, + CPLSPrintf( "SUBDATASET_%d_NAME", nCount ), + CPLSPrintf( "geor:%s/%s@%s,%s,%s", + poConnection->GetUser(), poConnection->GetPassword(), + poConnection->GetServer(), szDataTable, szRasterId ) ); + + const char* pszXBands = ""; + + if( ! EQUAL( szBands, "" ) ) + { + pszXBands = CPLSPrintf( "x%s", szBands ); + } + + papszSubdatasets = CSLSetNameValue( papszSubdatasets, + CPLSPrintf( "SUBDATASET_%d_DESC", nCount ), + CPLSPrintf( "[%sx%s%s] CellDepth=%s SRID=%s", + szRows, szColumns, pszXBands, + szCellDepth, szSRID ) ); + + nCount++; + } + while( poStmt->Fetch() ); + } +} + +// --------------------------------------------------------------------------- +// SetGCPs() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterDataset::SetGCPs( int, const GDAL_GCP *, const char * ) +{ + return CE_None; +} + +// --------------------------------------------------------------------------- +// GetGCPProjection() +// --------------------------------------------------------------------------- + +const char* GeoRasterDataset::GetGCPProjection() + +{ + if( nGCPCount > 0 ) + return pszProjection; + else + return ""; +} + +// --------------------------------------------------------------------------- +// IBuildOverviews() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterDataset::IBuildOverviews( const char* pszResampling, + int nOverviews, + int* panOverviewList, + int nListBands, + int* panBandList, + GDALProgressFunc pfnProgress, + void* pProgressData ) +{ + (void) panBandList; + (void) nListBands; + + // --------------------------------------------------------------- + // Can't update on read-only access mode + // --------------------------------------------------------------- + + if( GetAccess() != GA_Update ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't build overviews/pyramids on read-only access." ); + return CE_Failure; + } + + // --------------------------------------------------------------- + // Uses internal sdo_generatePyramid at PL/SQL? + // --------------------------------------------------------------- + + bool bInternal = true; + + const char *pszGEOR_INTERNAL_PYR = CPLGetConfigOption( "GEOR_INTERNAL_PYR", + "YES" ); + + if( EQUAL(pszGEOR_INTERNAL_PYR, "NO") ) + { + bInternal = false; + } + + // ----------------------------------------------------------- + // Pyramids applies to the whole dataset not to a specific band + // ----------------------------------------------------------- + + if( nBands < GetRasterCount()) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid GeoRaster Pyramids band selection" ); + return CE_Failure; + } + + // --------------------------------------------------------------- + // Initialize progress reporting + // --------------------------------------------------------------- + + if( ! pfnProgress( 0.1, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + + // --------------------------------------------------------------- + // Clear existing overviews + // --------------------------------------------------------------- + + if( nOverviews == 0 ) + { + poGeoRaster->DeletePyramid(); + return CE_None; + } + + // ----------------------------------------------------------- + // Pyramids levels can not be treated individually + // ----------------------------------------------------------- + + if( nOverviews > 0 ) + { + int i; + for( i = 1; i < nOverviews; i++ ) + { + // ----------------------------------------------------------- + // Power of 2, starting on 2, e.g. 2, 4, 8, 16, 32, 64, 128 + // ----------------------------------------------------------- + + if( panOverviewList[0] != 2 || + ( panOverviewList[i] != panOverviewList[i-1] * 2 ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid GeoRaster Pyramids levels." ); + return CE_Failure; + } + } + } + + // ----------------------------------------------------------- + // Re-sampling method: + // NN, BILINEAR, AVERAGE4, AVERAGE16 and CUBIC + // ----------------------------------------------------------- + + char szMethod[OWNAME]; + + if( EQUAL( pszResampling, "NEAREST" ) ) + { + strcpy( szMethod, "NN" ); + } + else if( EQUALN( pszResampling, "AVERAGE", 7 ) ) + { + strcpy( szMethod, "AVERAGE4" ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid resampling method" ); + return CE_Failure; + } + + // ----------------------------------------------------------- + // Generate pyramids on poGeoRaster + // ----------------------------------------------------------- + + if( ! poGeoRaster->GeneratePyramid( nOverviews, szMethod, bInternal ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Error generating pyramid" ); + return CE_Failure; + } + + // ----------------------------------------------------------- + // If Pyramid was done internally on the server exit here + // ----------------------------------------------------------- + + if( bInternal ) + { + pfnProgress( 1 , NULL, pProgressData ); + return CE_None; + } + + // ----------------------------------------------------------- + // Load the pyramids data using GDAL methods + // ----------------------------------------------------------- + + CPLErr eErr = CE_None; + + int i = 0; + + for( i = 0; i < nBands; i++ ) + { + GeoRasterRasterBand* poBand = (GeoRasterRasterBand*) papoBands[i]; + + // ------------------------------------------------------- + // Clean up previous overviews + // ------------------------------------------------------- + + int j = 0; + + if( poBand->nOverviewCount && poBand->papoOverviews ) + { + for( j = 0; j < poBand->nOverviewCount; j++ ) + { + delete poBand->papoOverviews[j]; + } + CPLFree( poBand->papoOverviews ); + } + + // ------------------------------------------------------- + // Create new band's overviews list + // ------------------------------------------------------- + + poBand->nOverviewCount = poGeoRaster->nPyramidMaxLevel; + poBand->papoOverviews = (GeoRasterRasterBand**) VSIMalloc( + sizeof(GeoRasterRasterBand*) * poBand->nOverviewCount ); + + for( j = 0; j < poBand->nOverviewCount; j++ ) + { + poBand->papoOverviews[j] = new GeoRasterRasterBand( + (GeoRasterDataset*) this, ( i + 1 ), ( j + 1 ) ); + } + } + + // ----------------------------------------------------------- + // Load band's overviews + // ----------------------------------------------------------- + + for( i = 0; i < nBands; i++ ) + { + GeoRasterRasterBand* poBand = (GeoRasterRasterBand*) papoBands[i]; + + void *pScaledProgressData = GDALCreateScaledProgress( + i / (double) nBands, ( i + 1) / (double) nBands, + pfnProgress, pProgressData ); + + eErr = GDALRegenerateOverviews( + (GDALRasterBandH) poBand, + poBand->nOverviewCount, + (GDALRasterBandH*) poBand->papoOverviews, + pszResampling, + GDALScaledProgress, + pScaledProgressData ); + + GDALDestroyScaledProgress( pScaledProgressData ); + } + + return eErr; +} + +// --------------------------------------------------------------------------- +// CreateMaskBand() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterDataset::CreateMaskBand( int nFlags ) +{ + (void) nFlags; + + if( ! poGeoRaster->InitializeMask( DEFAULT_BMP_MASK, + poGeoRaster->nRowBlockSize, + poGeoRaster->nColumnBlockSize, + poGeoRaster->nTotalRowBlocks, + poGeoRaster->nTotalColumnBlocks, + poGeoRaster->nTotalBandBlocks ) ) + { + return CE_Failure; + } + + poGeoRaster->bHasBitmapMask = true; + + return CE_None; +} + +/*****************************************************************************/ +/* GDALRegister_GEOR */ +/*****************************************************************************/ + +void CPL_DLL GDALRegister_GEOR() +{ + GDALDriver* poDriver; + + if (! GDAL_CHECK_VERSION("GeoRaster driver")) + return; + + if( GDALGetDriverByName( "GeoRaster" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GeoRaster" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Oracle Spatial GeoRaster" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_georaster.html" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16 Int16 UInt32 Int32 Float32 " + "Float64 CFloat32 CFloat64" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" " +" " +" " +" " +" " ); + + poDriver->pfnOpen = GeoRasterDataset::Open; + poDriver->pfnCreate = GeoRasterDataset::Create; + poDriver->pfnCreateCopy = GeoRasterDataset::CreateCopy; + poDriver->pfnIdentify = GeoRasterDataset::Identify; + poDriver->pfnDelete = GeoRasterDataset::Delete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/georaster/georaster_priv.h b/bazaar/plugin/gdal/frmts/georaster/georaster_priv.h new file mode 100644 index 000000000..c74a64d41 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/georaster/georaster_priv.h @@ -0,0 +1,477 @@ +/****************************************************************************** + * $Id: $ + * + * Name: georaster_priv.h + * Project: Oracle Spatial GeoRaster Driver + * Purpose: Define C++/Private declarations + * Author: Ivan Lucena [ivan.lucena at oracle.com] + * + ****************************************************************************** + * Copyright (c) 2008, Ivan Lucena + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files ( the "Software" ), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#ifndef _GEORASTER_PRIV_H_INCLUDED +#define _GEORASTER_PRIV_H_INCLUDED + +#include "gdal.h" +#include "gdal_priv.h" +#include "gdal_alg.h" +#include "gdal_rat.h" +#include "ogr_spatialref.h" +#include "cpl_minixml.h" +#include "cpl_list.h" + +// --------------------------------------------------------------------------- +// DEFLATE compression support +// --------------------------------------------------------------------------- + +#include + +// --------------------------------------------------------------------------- +// JPEG compression support +// --------------------------------------------------------------------------- + +CPL_C_START +#include +CPL_C_END + +void jpeg_vsiio_src (j_decompress_ptr cinfo, VSILFILE * infile); +void jpeg_vsiio_dest (j_compress_ptr cinfo, VSILFILE * outfile); + +// --------------------------------------------------------------------------- +// System constants +// --------------------------------------------------------------------------- + +// VAT maximum string len + +#define MAXLEN_VATSTR 128 + +// Geographic system without EPSG parameters + +#define UNKNOWN_CRS 999999 +#define NO_CRS 0 +#define DEFAULT_CRS NO_CRS + +// Bitmap Mask for the whole dataset start with -99999 + +#define DEFAULT_BMP_MASK -99999 + +// Default block size + +#define DEFAULT_BLOCK_ROWS 256 +#define DEFAULT_BLOCK_COLUMNS 256 + +// Default Model Coordinate Location (internal pixel geo-reference) + +#define MCL_CENTER 0 +#define MCL_UPPERLEFT 1 +#define MCL_DEFAULT MCL_CENTER + +// MAX double string representation + +#define MAX_DOUBLE_STR_REP 20 + +struct hLevelDetails { + int nColumnBlockSize; + int nRowBlockSize; + int nTotalColumnBlocks; + int nTotalRowBlocks; + unsigned long nBlockCount; + unsigned long nBlockBytes; + unsigned long nGDALBlockBytes; + unsigned long nOffset; +}; + +// --------------------------------------------------------------------------- +// Support for multi-values NoData support +// --------------------------------------------------------------------------- + +struct hNoDataItem { + int nBand; + double dfLower; + double dfUpper; +}; + +// --------------------------------------------------------------------------- +// GeoRaster wrapper classe definitions +// --------------------------------------------------------------------------- + +#include "oci_wrapper.h" + +class GeoRasterDataset; +class GeoRasterRasterBand; +class GeoRasterWrapper; + +// --------------------------------------------------------------------------- +// GeoRasterDataset, extends GDALDataset to support GeoRaster Datasets +// --------------------------------------------------------------------------- + +class GeoRasterDataset : public GDALDataset +{ + friend class GeoRasterRasterBand; + +public: + GeoRasterDataset(); + virtual ~GeoRasterDataset(); + +private: + + GeoRasterWrapper* poGeoRaster; + bool bGeoTransform; + bool bForcedSRID; + char* pszProjection; + char** papszSubdatasets; + double adfGeoTransform[6]; + int nGCPCount; + GDAL_GCP* pasGCPList; + GeoRasterRasterBand* + poMaskBand; + bool bApplyNoDataArray; + +public: + + void SetSubdatasets( GeoRasterWrapper* poGRW ); + + static int Identify( GDALOpenInfo* poOpenInfo ); + static GDALDataset* Open( GDALOpenInfo* poOpenInfo ); + static CPLErr Delete( const char *pszFilename ); + static GDALDataset* Create( const char* pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + char** papszOptions ); + static GDALDataset* CreateCopy( const char* pszFilename, + GDALDataset* poSrcDS, + int bStrict, + char** papszOptions, + GDALProgressFunc pfnProgress, + void* pProgressData ); + virtual CPLErr GetGeoTransform( double* padfTransform ); + virtual CPLErr SetGeoTransform( double* padfTransform ); + virtual const char* GetProjectionRef( void ); + virtual CPLErr SetProjection( const char* pszProjString ); + virtual char **GetMetadataDomainList(); + virtual char** GetMetadata( const char* pszDomain ); + virtual void FlushCache( void ); + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ); + virtual int GetGCPCount() { return nGCPCount; } + virtual const char* GetGCPProjection(); + virtual const GDAL_GCP* + GetGCPs() { return pasGCPList; } + virtual CPLErr SetGCPs( + int nGCPCount, + const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + virtual CPLErr IBuildOverviews( + const char* pszResampling, + int nOverviews, + int* panOverviewList, + int nListBandsover, + int* panBandList, + GDALProgressFunc pfnProgress, + void* pProgresoversData ); + virtual CPLErr CreateMaskBand( int nFlags ); + virtual OGRErr StartTransaction(int bForce=FALSE) {return CE_None;}; + virtual OGRErr CommitTransaction() {return CE_None;}; + virtual OGRErr RollbackTransaction() {return CE_None;}; + + void AssignGeoRaster( GeoRasterWrapper* poGRW ); +}; + +// --------------------------------------------------------------------------- +// GeoRasterRasterBand, extends GDALRasterBand to support GeoRaster Band +// --------------------------------------------------------------------------- + +class GeoRasterRasterBand : public GDALRasterBand +{ + friend class GeoRasterDataset; + +public: + GeoRasterRasterBand( GeoRasterDataset* poGDS, + int nBand, + int nLevel ); + virtual ~GeoRasterRasterBand(); + +private: + + GeoRasterWrapper* poGeoRaster; + GDALColorTable* poColorTable; + GDALRasterAttributeTable* + poDefaultRAT; + double dfMin; + double dfMax; + double dfMean; + double dfMedian; + double dfMode; + double dfStdDev; + bool bValidStats; + double dfNoData; + char* pszVATName; + int nOverviewLevel; + GeoRasterRasterBand** papoOverviews; + int nOverviewCount; + hNoDataItem* pahNoDataArray; + int nNoDataArraySz; + bool bHasNoDataArray; + + void ApplyNoDataArry( void* pBuffer ); + +public: + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual CPLErr SetNoDataValue( double dfNoDataValue ); + virtual double GetMinimum( int* pbSuccess = NULL ); + virtual double GetMaximum( int* pbSuccess = NULL ); + virtual GDALColorTable* + GetColorTable(); + virtual CPLErr SetColorTable( GDALColorTable *poInColorTable ); + virtual GDALColorInterp + GetColorInterpretation(); + virtual CPLErr IReadBlock( int nBlockXOff, int nBlockYOff, + void *pImage ); + virtual CPLErr IWriteBlock( int nBlockXOff, int nBlockYOff, + void *pImage ); + virtual CPLErr SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ); + virtual CPLErr GetStatistics( int bApproxOK, int bForce, + double* pdfMin, double* pdfMax, + double* pdfMean, double* pdfStdDev ); + virtual GDALRasterAttributeTable *GetDefaultRAT(); + virtual CPLErr SetDefaultRAT( const GDALRasterAttributeTable *poRAT ); + virtual int GetOverviewCount(); + virtual GDALRasterBand* + GetOverview( int ); + virtual CPLErr CreateMaskBand( int nFlags ); + virtual GDALRasterBand* + GetMaskBand(); + virtual int GetMaskFlags(); +}; + +// --------------------------------------------------------------------------- +// GeoRasterWrapper, an interface for Oracle Spatial SDO_GEORASTER objects +// --------------------------------------------------------------------------- + +class GeoRasterWrapper +{ + +public: + + GeoRasterWrapper(); + virtual ~GeoRasterWrapper(); + +private: + + OCILobLocator** pahLocator; + unsigned long nBlockCount; + unsigned long nBlockBytes; + unsigned long nGDALBlockBytes; + GByte* pabyBlockBuf; + GByte* pabyCompressBuf; + OWStatement* poBlockStmt; + OWStatement* poStmtWrite; + + int nCurrentLevel; + long nLevelOffset; + + long nCacheBlockId; + bool bFlushBlock; + unsigned long nFlushBlockSize; + + bool bWriteOnly; + + hLevelDetails* pahLevels; + + int nCellSizeBits; + int nGDALCellBytes; + + bool bUpdate; + bool bInitializeIO; + bool bFlushMetadata; + + void InitializeLayersNode( void ); + bool InitializeIO( void ); + void InitializeLevel( int nLevel ); + bool FlushMetadata( void ); + + void LoadNoDataValues( void ); + + void UnpackNBits( GByte* pabyData ); + void PackNBits( GByte* pabyData ); + unsigned long CompressJpeg( void ); + unsigned long CompressDeflate( void ); + void UncompressJpeg( unsigned long nBufferSize ); + bool UncompressDeflate( unsigned long nBufferSize ); + + struct jpeg_decompress_struct sDInfo; + struct jpeg_compress_struct sCInfo; + struct jpeg_error_mgr sJErr; + +public: + + static char** ParseIdentificator( const char* pszStringID ); + static GeoRasterWrapper* + Open( + const char* pszStringID, + bool bUpdate ); + bool Create( + char* pszDescription, + char* pszInsert, + bool bUpdate ); + bool Delete( void ); + void GetRasterInfo( void ); + bool GetStatistics( int nBand, + char* pszMin, + char* pszMax, + char* pszMean, + char* pszMedian, + char* pszMode, + char* pszStdDev, + char* pszSampling ); + bool SetStatistics( int nBand, + const char* pszMin, + const char* pszMax, + const char* pszMean, + const char* pszMedian, + const char* pszMode, + const char* pszStdDev, + const char* pszSampling ); + bool HasColorMap( int nBand ); + void GetColorMap( int nBand, GDALColorTable* poCT ); + void SetColorMap( int nBand, GDALColorTable* poCT ); + void SetGeoReference( int nSRIDIn ); + bool GetDataBlock( + int nBand, + int nLevel, + int nXOffset, + int nYOffset, + void* pData ); + bool SetDataBlock( + int nBand, + int nLevel, + int nXOffset, + int nYOffset, + void* pData ); + long GetBlockNumber( int nB, int nX, int nY ) + { + return nLevelOffset + + (long) ( ( ceil( (double) + ( ( nB - 1 ) / nBandBlockSize ) ) * + nTotalColumnBlocks * nTotalRowBlocks ) + + ( nY * nTotalColumnBlocks ) + nX ); + } + + bool FlushBlock( long nCacheBlock ); + bool GetNoData( int nLayer, double* pdfNoDataValue ); + bool SetNoData( int nLayer, const char* pszValue ); + CPLXMLNode* GetMetadata() { return phMetadata; }; + bool SetVAT( int nBand, const char* pszName ); + char* GetVAT( int nBand ); + bool GeneratePyramid( + int nLevels, + const char* pszResampling, + bool bInternal = false ); + bool DeletePyramid(); + void PrepareToOverwrite( void ); + bool InitializeMask( int nLevel, + int nBlockColumns, + int nBlockRows, + int nColumnBlocks, + int nRowBlocks, + int nBandBlocks ); + void SetWriteOnly( bool value ) { bWriteOnly = value; }; + void SetRPC(); + void GetRPC(); + +public: + + OWConnection* poConnection; + + CPLString sTable; + CPLString sSchema; + CPLString sOwner; + CPLString sColumn; + CPLString sDataTable; + int nRasterId; + CPLString sWhere; + CPLString sValueAttributeTab; + + int nSRID; + int nExtentSRID; + bool bGenSpatialIndex; + bool bCreateObjectTable; + CPLXMLNode* phMetadata; + CPLString sCellDepth; + + bool bGenPyramid; + CPLString sPyramidResampling; + int nPyramidLevels; + + CPLString sCompressionType; + int nCompressQuality; + CPLString sWKText; + CPLString sAuthority; + CPLList* psNoDataList; + + int nRasterColumns; + int nRasterRows; + int nRasterBands; + + CPLString sInterleaving; + bool bIsReferenced; + + bool bBlocking; + bool bAutoBlocking; + + double dfXCoefficient[3]; + double dfYCoefficient[3]; + + int nColumnBlockSize; + int nRowBlockSize; + int nBandBlockSize; + + int nTotalColumnBlocks; + int nTotalRowBlocks; + int nTotalBandBlocks; + + int iDefaultRedBand; + int iDefaultGreenBand; + int iDefaultBlueBand; + + int nPyramidMaxLevel; + + bool bHasBitmapMask; + bool bUniqueFound; + + int eModelCoordLocation; + unsigned int anULTCoordinate[3]; + + GDALRPCInfo* phRPC; +}; + +#endif /* ifndef _GEORASTER_PRIV_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/georaster/georaster_rasterband.cpp b/bazaar/plugin/gdal/frmts/georaster/georaster_rasterband.cpp new file mode 100644 index 000000000..498d07fb0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/georaster/georaster_rasterband.cpp @@ -0,0 +1,1113 @@ +/****************************************************************************** + * $Id: $ + * + * Name: georaster_rasterband.cpp + * Project: Oracle Spatial GeoRaster Driver + * Purpose: Implement GeoRasterRasterBand methods + * Author: Ivan Lucena [ivan.lucena at oracle.com] + * + ****************************************************************************** + * Copyright (c) 2008, Ivan Lucena + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files ( the "Software" ), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "gdal_priv.h" + +#include + +#include "georaster_priv.h" +#include "cpl_vsi.h" +#include "cpl_error.h" + +// --------------------------------------------------------------------------- +// GeoRasterRasterBand() +// --------------------------------------------------------------------------- + +GeoRasterRasterBand::GeoRasterRasterBand( GeoRasterDataset *poGDS, + int nBand, + int nLevel ) +{ + poDS = (GDALDataset*) poGDS; + poGeoRaster = poGDS->poGeoRaster; + this->nBand = nBand; + this->eDataType = OWGetDataType( poGeoRaster->sCellDepth.c_str() ); + poColorTable = new GDALColorTable(); + poDefaultRAT = NULL; + pszVATName = NULL; + nRasterXSize = poGeoRaster->nRasterColumns; + nRasterYSize = poGeoRaster->nRasterRows; + nBlockXSize = poGeoRaster->nColumnBlockSize; + nBlockYSize = poGeoRaster->nRowBlockSize; + dfNoData = 0.0; + bValidStats = false; + nOverviewLevel = nLevel; + papoOverviews = NULL; + nOverviewCount = 0; + pahNoDataArray = NULL; + nNoDataArraySz = 0; + bHasNoDataArray = false; + + // ----------------------------------------------------------------------- + // Initialize overview list + // ----------------------------------------------------------------------- + + if( nLevel == 0 && poGeoRaster->nPyramidMaxLevel > 0 ) + { + nOverviewCount = poGeoRaster->nPyramidMaxLevel; + papoOverviews = (GeoRasterRasterBand**) VSIMalloc( + sizeof(GeoRasterRasterBand*) * nOverviewCount ); + for( int i = 0; i < nOverviewCount; i++ ) + { + papoOverviews[i] = new GeoRasterRasterBand( + (GeoRasterDataset*) poDS, nBand, i + 1 ); + } + } + + // ----------------------------------------------------------------------- + // Initialize this band as an overview + // ----------------------------------------------------------------------- + + if( nLevel ) + { + double dfScale = pow( (double) 2.0, (double) nLevel ); + + nRasterXSize = (int) floor( nRasterXSize / dfScale ); + nRasterYSize = (int) floor( nRasterYSize / dfScale ); + + if( nRasterXSize <= ( nBlockXSize / 2.0 ) && + nRasterYSize <= ( nBlockYSize / 2.0 ) ) + { + nBlockXSize = nRasterXSize; + nBlockYSize = nRasterYSize; + } + } + + // ----------------------------------------------------------------------- + // Load NoData values and value ranges for this band (layer) + // ----------------------------------------------------------------------- + + if( ( (GeoRasterDataset*) poDS)->bApplyNoDataArray ) + { + CPLList* psList = NULL; + int nLayerCount = 0; + int nObjCount = 0; + + /* + * Count the number of NoData values and value ranges + */ + + for( psList = poGeoRaster->psNoDataList; psList ; psList = psList->psNext ) + { + hNoDataItem* phItem = (hNoDataItem*) psList->pData; + + if( phItem->nBand == nBand ) + { + nLayerCount++; + } + + if( phItem->nBand == 0 ) + { + nObjCount++; + } + + if( phItem->nBand > nBand ) + { + break; + } + } + + /* + * Join the object nodata values to layer NoData values + */ + + nNoDataArraySz = nLayerCount + nObjCount; + + pahNoDataArray = (hNoDataItem*) VSIMalloc2( sizeof(hNoDataItem), + nNoDataArraySz ); + + int i = 0; + bool bFirst = true; + + for( psList = poGeoRaster->psNoDataList ; psList && i < nNoDataArraySz; + psList = psList->psNext ) + { + hNoDataItem* phItem = (hNoDataItem*) psList->pData; + + if( phItem->nBand == nBand || phItem->nBand == 0 ) + { + pahNoDataArray[i].nBand = nBand; + pahNoDataArray[i].dfLower = phItem->dfLower; + pahNoDataArray[i].dfUpper = phItem->dfUpper; + i++; + + if( bFirst ) + { + bFirst = false; + + /* + * Use the first value to assigned pixel values + * on method ApplyNoDataArray() + */ + + dfNoData = phItem->dfLower; + } + } + } + + bHasNoDataArray = nNoDataArraySz > 0; + } +} + +// --------------------------------------------------------------------------- +// ~GeoRasterRasterBand() +// --------------------------------------------------------------------------- + +GeoRasterRasterBand::~GeoRasterRasterBand() +{ + delete poColorTable; + delete poDefaultRAT; + + CPLFree( pszVATName ); + CPLFree( pahNoDataArray ); + + if( nOverviewCount && papoOverviews ) + { + for( int i = 0; i < nOverviewCount; i++ ) + { + delete papoOverviews[i]; + } + + CPLFree( papoOverviews ); + } +} + +// --------------------------------------------------------------------------- +// IReadBlock() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterRasterBand::IReadBlock( int nBlockXOff, + int nBlockYOff, + void *pImage ) +{ + if( poGeoRaster->GetDataBlock( nBand, + nOverviewLevel, + nBlockXOff, + nBlockYOff, + pImage ) ) + { + if( bHasNoDataArray ) + { + ApplyNoDataArry( pImage ); + } + + return CE_None; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error reading GeoRaster ofsett X (%d) offset Y (%d) band (%d)", + nBlockXOff, nBlockYOff, nBand ); + + return CE_Failure; + } +} + +// --------------------------------------------------------------------------- +// IWriteBlock() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterRasterBand::IWriteBlock( int nBlockXOff, + int nBlockYOff, + void *pImage ) +{ + if( poGeoRaster->SetDataBlock( nBand, + nOverviewLevel, + nBlockXOff, + nBlockYOff, + pImage ) ) + { + return CE_None; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error writing GeoRaster ofsett X (%d) offset Y (%d) band (%d)", + nBlockXOff, nBlockYOff, nBand ); + + return CE_Failure; + } +} +// --------------------------------------------------------------------------- +// GetColorInterpretation() +// --------------------------------------------------------------------------- + +GDALColorInterp GeoRasterRasterBand::GetColorInterpretation() +{ + GeoRasterDataset* poGDS = (GeoRasterDataset*) poDS; + + if( eDataType == GDT_Byte && poGDS->nBands > 2 ) + { + if( nBand == poGeoRaster->iDefaultRedBand ) + { + return GCI_RedBand; + } + else if ( nBand == poGeoRaster->iDefaultGreenBand ) + { + return GCI_GreenBand; + } + else if ( nBand == poGeoRaster->iDefaultBlueBand ) + { + return GCI_BlueBand; + } + else + { + if( nBand == 4 && poGDS->nBands == 4 && + poGeoRaster->iDefaultRedBand == 1 && + poGeoRaster->iDefaultGreenBand == 2 && + poGeoRaster->iDefaultBlueBand == 3 ) + { + return GCI_AlphaBand; + } + else + { + return GCI_GrayIndex; + } + } + } + + if( poGeoRaster->HasColorMap( nBand ) ) + { + return GCI_PaletteIndex; + } + else + { + return GCI_GrayIndex; + } +} + +// --------------------------------------------------------------------------- +// GetColorTable() +// --------------------------------------------------------------------------- + +GDALColorTable *GeoRasterRasterBand::GetColorTable() +{ + poGeoRaster->GetColorMap( nBand, poColorTable ); + + if( poColorTable->GetColorEntryCount() == 0 ) + { + return NULL; + } + + return poColorTable; +} + +// --------------------------------------------------------------------------- +// SetColorTable() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterRasterBand::SetColorTable( GDALColorTable *poInColorTable ) +{ + if( poInColorTable == NULL ) + { + return CE_None; + } + + if( poInColorTable->GetColorEntryCount() == 0 ) + { + return CE_None; + } + + delete poColorTable; + + poColorTable = poInColorTable->Clone(); + + poGeoRaster->SetColorMap( nBand, poColorTable ); + + return CE_None; +} + +// --------------------------------------------------------------------------- +// GetMinimum() +// --------------------------------------------------------------------------- + +double GeoRasterRasterBand::GetMinimum( int *pbSuccess ) +{ + *pbSuccess = (int) bValidStats; + + return dfMin; +} + +// --------------------------------------------------------------------------- +// GetMaximum() +// --------------------------------------------------------------------------- + +double GeoRasterRasterBand::GetMaximum( int *pbSuccess ) +{ + *pbSuccess = (int) bValidStats; + + return dfMax; +} + +// --------------------------------------------------------------------------- +// GetStatistics() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterRasterBand::GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev ) +{ + (void) bForce; + (void) bApproxOK; + + char szMin[MAX_DOUBLE_STR_REP + 1]; + char szMax[MAX_DOUBLE_STR_REP + 1]; + char szMean[MAX_DOUBLE_STR_REP + 1]; + char szMedian[MAX_DOUBLE_STR_REP + 1]; + char szMode[MAX_DOUBLE_STR_REP + 1]; + char szStdDev[MAX_DOUBLE_STR_REP + 1]; + char szSampling[MAX_DOUBLE_STR_REP + 1]; + + if( ! bValidStats ) + { + bValidStats = poGeoRaster->GetStatistics( nBand, + szMin, szMax, + szMean, szMedian, + szMode, szStdDev, + szSampling ); + } + + if( bValidStats ) + { + dfMin = CPLScanDouble( szMin, MAX_DOUBLE_STR_REP ); + dfMax = CPLScanDouble( szMax, MAX_DOUBLE_STR_REP ); + dfMean = CPLScanDouble( szMean, MAX_DOUBLE_STR_REP ); + dfMedian = CPLScanDouble( szMedian, MAX_DOUBLE_STR_REP ); + dfMode = CPLScanDouble( szMode, MAX_DOUBLE_STR_REP ); + dfStdDev = CPLScanDouble( szStdDev, MAX_DOUBLE_STR_REP ); + + SetMetadataItem( "STATISTICS_MINIMUM", szMin ); + SetMetadataItem( "STATISTICS_MAXIMUM", szMax ); + SetMetadataItem( "STATISTICS_MEAN", szMean ); + SetMetadataItem( "STATISTICS_MEDIAN", szMedian ); + SetMetadataItem( "STATISTICS_MODE", szMode ); + SetMetadataItem( "STATISTICS_STDDEV", szStdDev ); + SetMetadataItem( "STATISTICS_SKIPFACTORX", szSampling ); + SetMetadataItem( "STATISTICS_SKIPFACTORY", szSampling ); + + *pdfMin = dfMin; + *pdfMax = dfMax; + *pdfMean = dfMean; + *pdfStdDev = dfStdDev; + + return CE_None; + } + + return CE_Failure; +} + +// --------------------------------------------------------------------------- +// SetStatistics() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterRasterBand::SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ) +{ + this->dfMin = dfMin; + this->dfMax = dfMax; + this->dfMean = dfMean; + this->dfStdDev = dfStdDev; + + return CE_None; +} + +// --------------------------------------------------------------------------- +// GetNoDataValue() +// --------------------------------------------------------------------------- + +double GeoRasterRasterBand::GetNoDataValue( int *pbSuccess ) +{ + if( pbSuccess ) + { + if( nNoDataArraySz ) + { + *pbSuccess = true; + } + else + { + *pbSuccess = (int) poGeoRaster->GetNoData( nBand, &dfNoData ); + } + } + + return dfNoData; +} + +// --------------------------------------------------------------------------- +// SetNoDataValue() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterRasterBand::SetNoDataValue( double dfNoDataValue ) +{ + const char* pszFormat = + (eDataType == GDT_Float32 || eDataType == GDT_Float64) ? "%f" : "%.0f"; + + poGeoRaster->SetNoData( (poDS->GetRasterCount() == 1) ? 0 : nBand, + CPLSPrintf( pszFormat, dfNoDataValue ) ); + + return CE_None; +} + +// --------------------------------------------------------------------------- +// SetDefaultRAT() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterRasterBand::SetDefaultRAT( const GDALRasterAttributeTable *poRAT ) +{ + GeoRasterDataset* poGDS = (GeoRasterDataset*) poDS; + + if( ! poRAT ) + { + return CE_Failure; + } + + if( poDefaultRAT ) + { + delete poDefaultRAT; + } + + poDefaultRAT = poRAT->Clone(); + + // ---------------------------------------------------------- + // Check if RAT is just colortable and/or histogram + // ---------------------------------------------------------- + + CPLString sColName = ""; + int iCol = 0; + int nColCount = poRAT->GetColumnCount(); + + for( iCol = 0; iCol < poRAT->GetColumnCount(); iCol++ ) + { + sColName = poRAT->GetNameOfCol( iCol ); + + if( EQUAL( sColName, "histogram" ) || + EQUAL( sColName, "red" ) || + EQUAL( sColName, "green" ) || + EQUAL( sColName, "blue" ) || + EQUAL( sColName, "opacity" ) ) + { + nColCount--; + } + } + + if( nColCount < 2 ) + { + delete poDefaultRAT; + + poDefaultRAT = NULL; + + return CE_None; + } + + // ---------------------------------------------------------- + // Format Table description + // ---------------------------------------------------------- + + char szName[OWTEXT]; + char szDescription[OWTEXT]; + + strcpy( szDescription, "( ID NUMBER" ); + + for( iCol = 0; iCol < poRAT->GetColumnCount(); iCol++ ) + { + strcpy( szName, poRAT->GetNameOfCol( iCol ) ); + + strcpy( szDescription, CPLSPrintf( "%s, %s", + szDescription, szName ) ); + + if( poRAT->GetTypeOfCol( iCol ) == GFT_Integer ) + { + strcpy( szDescription, CPLSPrintf( "%s NUMBER", + szDescription ) ); + } + if( poRAT->GetTypeOfCol( iCol ) == GFT_Real ) + { + strcpy( szDescription, CPLSPrintf( "%s NUMBER", + szDescription ) ); + } + if( poRAT->GetTypeOfCol( iCol ) == GFT_String ) + { + strcpy( szDescription, CPLSPrintf( "%s VARCHAR2(%d)", + szDescription, MAXLEN_VATSTR) ); + } + } + strcpy( szDescription, CPLSPrintf( "%s )", szDescription ) ); + + // ---------------------------------------------------------- + // Create VAT named based on RDT and RID and Layer (nBand) + // ---------------------------------------------------------- + + if ( poGeoRaster->sValueAttributeTab.length() > 0 ) + { + pszVATName = CPLStrdup( poGeoRaster->sValueAttributeTab.c_str() ); + } + + if( ! pszVATName ) + { + pszVATName = CPLStrdup( CPLSPrintf( + "RAT_%s_%d_%d", + poGeoRaster->sDataTable.c_str(), + poGeoRaster->nRasterId, + nBand ) ); + } + + // ---------------------------------------------------------- + // Create VAT table + // ---------------------------------------------------------- + + OWStatement* poStmt = poGeoRaster->poConnection->CreateStatement( CPLSPrintf( + "DECLARE\n" + " TAB VARCHAR2(68) := UPPER(:1);\n" + " CNT NUMBER := 0;\n" + "BEGIN\n" + " EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM USER_TABLES\n" + " WHERE TABLE_NAME = :1' INTO CNT USING TAB;\n" + "\n" + " IF NOT CNT = 0 THEN\n" + " EXECUTE IMMEDIATE 'DROP TABLE '||TAB||' PURGE';\n" + " END IF;\n" + "\n" + " EXECUTE IMMEDIATE 'CREATE TABLE '||TAB||' %s';\n" + "END;", szDescription ) ); + + poStmt->Bind( pszVATName ); + + if( ! poStmt->Execute() ) + { + delete poStmt; + CPLError( CE_Failure, CPLE_AppDefined, "Create VAT Table Error!" ); + return CE_Failure; + } + + delete poStmt; + + // ---------------------------------------------------------- + // Insert Data to VAT + // ---------------------------------------------------------- + + int iEntry = 0; + int nEntryCount = poRAT->GetRowCount(); + int nColunsCount = poRAT->GetColumnCount(); + int nVATStrSize = MAXLEN_VATSTR * poGeoRaster->poConnection->GetCharSize(); + + // --------------------------- + // Allocate array of buffers + // --------------------------- + + void** papWriteFields = (void**) VSIMalloc2(sizeof(void*), nColunsCount + 1); + + papWriteFields[0] = + (void*) VSIMalloc3(sizeof(int), sizeof(int), nEntryCount ); // ID field + + for(iCol = 0; iCol < nColunsCount; iCol++) + { + if( poRAT->GetTypeOfCol( iCol ) == GFT_String ) + { + papWriteFields[iCol + 1] = + (void*) VSIMalloc3(sizeof(char), nVATStrSize, nEntryCount ); + } + if( poRAT->GetTypeOfCol( iCol ) == GFT_Integer ) + { + papWriteFields[iCol + 1] = + (void*) VSIMalloc3(sizeof(int), sizeof(int), nEntryCount ); + } + if( poRAT->GetTypeOfCol( iCol ) == GFT_Real ) + { + papWriteFields[iCol + 1] = + (void*) VSIMalloc3(sizeof(double), sizeof(double), nEntryCount ); + } + } + + // --------------------------- + // Load data to buffers + // --------------------------- + + for( iEntry = 0; iEntry < nEntryCount; iEntry++ ) + { + ((int *)(papWriteFields[0]))[iEntry] = iEntry; // ID field + + for(iCol = 0; iCol < nColunsCount; iCol++) + { + if( poRAT->GetTypeOfCol( iCol ) == GFT_String ) + { + + int nOffset = iEntry * nVATStrSize; + char* pszTarget = ((char*)papWriteFields[iCol + 1]) + nOffset; + const char *pszStrValue = poRAT->GetValueAsString(iEntry, iCol); + int nLen = strlen( pszStrValue ); + nLen = nLen > ( nVATStrSize - 1 ) ? nVATStrSize : ( nVATStrSize - 1 ); + strncpy( pszTarget, pszStrValue, nLen ); + pszTarget[nLen] = '\0'; + } + if( poRAT->GetTypeOfCol( iCol ) == GFT_Integer ) + { + ((int *)(papWriteFields[iCol + 1]))[iEntry] = + poRAT->GetValueAsInt(iEntry, iCol); + } + if( poRAT->GetTypeOfCol( iCol ) == GFT_Real ) + { + ((double *)(papWriteFields[iCol + 1]))[iEntry] = + poRAT->GetValueAsDouble(iEntry, iCol); + } + } + } + + // --------------------------- + // Prepare insert statement + // --------------------------- + + CPLString osInsert = CPLSPrintf( "INSERT INTO %s VALUES (", pszVATName ); + + for( iCol = 0; iCol < ( nColunsCount + 1); iCol++ ) + { + if( iCol > 0 ) + { + osInsert.append(", "); + } + osInsert.append( CPLSPrintf(":%d", iCol + 1) ); + } + osInsert.append(")"); + + poStmt = poGeoRaster->poConnection->CreateStatement( osInsert.c_str() ); + + // --------------------------- + // Bind buffers to columns + // --------------------------- + + poStmt->Bind((int*) papWriteFields[0]); // ID field + + for(iCol = 0; iCol < nColunsCount; iCol++) + { + if( poRAT->GetTypeOfCol( iCol ) == GFT_String ) + { + poStmt->Bind( (char*) papWriteFields[iCol + 1], nVATStrSize ); + } + if( poRAT->GetTypeOfCol( iCol ) == GFT_Integer ) + { + poStmt->Bind( (int*) papWriteFields[iCol + 1]); + } + if( poRAT->GetTypeOfCol( iCol ) == GFT_Real ) + { + poStmt->Bind( (double*) papWriteFields[iCol + 1]); + } + } + + if( poStmt->Execute( iEntry ) ) + { + poGDS->poGeoRaster->SetVAT( nBand, pszVATName ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, "Insert VAT Error!" ); + } + + // --------------------------- + // Clean up + // --------------------------- + + for(iCol = 0; iCol < ( nColunsCount + 1); iCol++) + { + CPLFree( papWriteFields[iCol] ); + } + + CPLFree( papWriteFields ); + + delete poStmt; + + return CE_None; +} + +// --------------------------------------------------------------------------- +// GetDefaultRAT() +// --------------------------------------------------------------------------- + +GDALRasterAttributeTable *GeoRasterRasterBand::GetDefaultRAT() +{ + if( poDefaultRAT ) + { + return poDefaultRAT; + } + else + { + poDefaultRAT = new GDALDefaultRasterAttributeTable(); + } + + GeoRasterDataset* poGDS = (GeoRasterDataset*) poDS; + + // ---------------------------------------------------------- + // Get the name of the VAT Table + // ---------------------------------------------------------- + + char* pszVATName = poGDS->poGeoRaster->GetVAT( nBand ); + + if( pszVATName == NULL ) + { + return NULL; + } + + OCIParam* phDesc = NULL; + + phDesc = poGDS->poGeoRaster->poConnection->GetDescription( pszVATName ); + + if( phDesc == NULL ) + { + return NULL; + } + + // ---------------------------------------------------------- + // Create the RAT and the SELECT statemet based on fields description + // ---------------------------------------------------------- + + int iCol = 0; + char szField[OWNAME]; + int hType = 0; + int nSize = 0; + int nPrecision = 0; + signed short nScale = 0; + + char szColumnList[OWTEXT]; + szColumnList[0] = '\0'; + + while( poGDS->poGeoRaster->poConnection->GetNextField( + phDesc, iCol, szField, &hType, &nSize, &nPrecision, &nScale ) ) + { + switch( hType ) + { + case SQLT_FLT: + poDefaultRAT->CreateColumn( szField, GFT_Real, GFU_Generic ); + break; + case SQLT_NUM: + if( nPrecision == 0 ) + { + poDefaultRAT->CreateColumn( szField, GFT_Integer, + GFU_Generic ); + } + else + { + poDefaultRAT->CreateColumn( szField, GFT_Real, + GFU_Generic ); + } + break; + case SQLT_CHR: + case SQLT_AFC: + case SQLT_DAT: + case SQLT_DATE: + case SQLT_TIMESTAMP: + case SQLT_TIMESTAMP_TZ: + case SQLT_TIMESTAMP_LTZ: + case SQLT_TIME: + case SQLT_TIME_TZ: + poDefaultRAT->CreateColumn( szField, GFT_String, + GFU_Generic ); + break; + default: + CPLDebug("GEORASTER", "VAT (%s) Column (%s) type (%d) not supported" + "as GDAL RAT", pszVATName, szField, hType ); + continue; + } + strcpy( szColumnList, CPLSPrintf( "%s substr(%s,1,%d),", + szColumnList, szField, MIN(nSize,OWNAME) ) ); + + iCol++; + } + + szColumnList[strlen(szColumnList) - 1] = '\0'; // remove the last comma + + // ---------------------------------------------------------- + // Read VAT and load RAT + // ---------------------------------------------------------- + + OWStatement* poStmt = NULL; + + poStmt = poGeoRaster->poConnection->CreateStatement( CPLSPrintf ( + "SELECT %s FROM %s", szColumnList, pszVATName ) ); + + char** papszValue = (char**) CPLMalloc( sizeof(char**) * iCol ); + + int i = 0; + + for( i = 0; i < iCol; i++ ) + { + papszValue[i] = (char*) CPLMalloc( sizeof(char*) * OWNAME ); + poStmt->Define( papszValue[i] ); + } + + if( ! poStmt->Execute() ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Error reading VAT %s", + pszVATName ); + return NULL; + } + + int iRow = 0; + + while( poStmt->Fetch() ) + { + for( i = 0; i < iCol; i++ ) + { + poDefaultRAT->SetValue( iRow, i, papszValue[i] ); + } + iRow++; + } + + for( i = 0; i < iCol; i++ ) + { + CPLFree( papszValue[i] ); + } + CPLFree( papszValue ); + + delete poStmt; + + CPLFree( pszVATName ); + + return poDefaultRAT; +} + +// --------------------------------------------------------------------------- +// GetOverviewCount() +// --------------------------------------------------------------------------- + +int GeoRasterRasterBand::GetOverviewCount() +{ + return nOverviewCount; +} + +// --------------------------------------------------------------------------- +// GetOverviewCount() +// --------------------------------------------------------------------------- + +GDALRasterBand* GeoRasterRasterBand::GetOverview( int nLevel ) +{ + if( nLevel < nOverviewCount && papoOverviews[ nLevel ] ) + { + return (GDALRasterBand*) papoOverviews[ nLevel ]; + } + return (GDALRasterBand*) NULL; +} + +// --------------------------------------------------------------------------- +// CreateMaskBand() +// --------------------------------------------------------------------------- + +CPLErr GeoRasterRasterBand::CreateMaskBand( int nFlags ) +{ + (void) nFlags; + + if( ! poGeoRaster->bHasBitmapMask ) + { + return CE_Failure; + } + + return CE_None; +} + +// --------------------------------------------------------------------------- +// GetMaskBand() +// --------------------------------------------------------------------------- + +GDALRasterBand* GeoRasterRasterBand::GetMaskBand() +{ + GeoRasterDataset* poGDS = (GeoRasterDataset*) this->poDS; + + if( poGDS->poMaskBand != NULL ) + { + return (GDALRasterBand*) poGDS->poMaskBand; + } + + return (GDALRasterBand*) NULL; +} + +// --------------------------------------------------------------------------- +// GetMaskFlags() +// --------------------------------------------------------------------------- + +int GeoRasterRasterBand::GetMaskFlags() +{ + GeoRasterDataset* poGDS = (GeoRasterDataset*) this->poDS; + + if( poGDS->poMaskBand != NULL ) + { + return GMF_PER_DATASET; + } + + return GMF_ALL_VALID; +} + +// --------------------------------------------------------------------------- +// ApplyNoDataArry() +// --------------------------------------------------------------------------- + +void GeoRasterRasterBand::ApplyNoDataArry(void* pBuffer) +{ + int i = 0; + int j = 0; + long n = nBlockXSize * nBlockYSize; + + switch( eDataType ) + { + case GDT_Byte: + { + GByte* pbBuffer = (GByte*) pBuffer; + + for( i = 0; i < n; i++ ) + { + for( j = 0; j < nNoDataArraySz; j++ ) + { + if( pbBuffer[i] == (GByte) pahNoDataArray[j].dfLower || + ( pbBuffer[i] > (GByte) pahNoDataArray[j].dfLower && + pbBuffer[i] < (GByte) pahNoDataArray[j].dfUpper ) ) + { + pbBuffer[i] = (GByte) dfNoData; + } + } + } + + break; + } + case GDT_Float32: + case GDT_CFloat32: + { + float* pfBuffer = (float*) pBuffer; + + for( i = 0; i < n; i++ ) + { + for( j = 0; j < nNoDataArraySz; j++ ) + { + if( pfBuffer[i] == (float) pahNoDataArray[j].dfLower || + ( pfBuffer[i] > (float) pahNoDataArray[j].dfLower && + pfBuffer[i] < (float) pahNoDataArray[j].dfUpper ) ) + { + pfBuffer[i] = (float) dfNoData; + } + } + } + + break; + } + case GDT_Float64: + case GDT_CFloat64: + { + double* pdfBuffer = (double*) pBuffer; + + for( i = 0; i < n; i++ ) + { + for( j = 0; j < nNoDataArraySz; j++ ) + { + if( pdfBuffer[i] == (double) pahNoDataArray[j].dfLower || + ( pdfBuffer[i] > (double) pahNoDataArray[j].dfLower && + pdfBuffer[i] < (double) pahNoDataArray[j].dfUpper ) ) + { + pdfBuffer[i] = (double) dfNoData; + } + } + } + + break; + } + case GDT_Int16: + case GDT_CInt16: + { + GInt16* pnBuffer = (GInt16*) pBuffer; + + for( i = 0; i < n; i++ ) + { + for( j = 0; j < nNoDataArraySz; j++ ) + { + if( pnBuffer[i] == (GInt16) pahNoDataArray[j].dfLower || + ( pnBuffer[i] > (GInt16) pahNoDataArray[j].dfLower && + pnBuffer[i] < (GInt16) pahNoDataArray[j].dfUpper ) ) + { + pnBuffer[i] = (GInt16) dfNoData; + } + } + } + + break; + } + case GDT_Int32: + case GDT_CInt32: + { + GInt32* pnBuffer = (GInt32*) pBuffer; + + for( i = 0; i < n; i++ ) + { + for( j = 0; j < nNoDataArraySz; j++ ) + { + if( pnBuffer[i] == (GInt32) pahNoDataArray[j].dfLower || + ( pnBuffer[i] > (GInt32) pahNoDataArray[j].dfLower && + pnBuffer[i] < (GInt32) pahNoDataArray[j].dfUpper ) ) + { + pnBuffer[i] = (GInt32) dfNoData; + } + } + } + + break; + } + case GDT_UInt16: + { + GUInt16* pnBuffer = (GUInt16*) pBuffer; + + for( i = 0; i < n; i++ ) + { + for( j = 0; j < nNoDataArraySz; j++ ) + { + if( pnBuffer[i] == (GUInt16) pahNoDataArray[j].dfLower || + ( pnBuffer[i] > (GUInt16) pahNoDataArray[j].dfLower && + pnBuffer[i] < (GUInt16) pahNoDataArray[j].dfUpper ) ) + { + pnBuffer[i] = (GUInt16) dfNoData; + } + } + } + + break; + } + case GDT_UInt32: + { + GUInt32* pnBuffer = (GUInt32*) pBuffer; + + for( i = 0; i < n; i++ ) + { + for( j = 0; j < nNoDataArraySz; j++ ) + { + if( pnBuffer[i] == (GUInt32) pahNoDataArray[j].dfLower || + ( pnBuffer[i] > (GUInt32) pahNoDataArray[j].dfLower && + pnBuffer[i] < (GUInt32) pahNoDataArray[j].dfUpper ) ) + { + pnBuffer[i] = (GUInt32) dfNoData; + } + } + } + + break; + } + default: + ; + } +} diff --git a/bazaar/plugin/gdal/frmts/georaster/georaster_wrapper.cpp b/bazaar/plugin/gdal/frmts/georaster/georaster_wrapper.cpp new file mode 100644 index 000000000..9669f9681 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/georaster/georaster_wrapper.cpp @@ -0,0 +1,3990 @@ +/****************************************************************************** + * $Id: $ + * + * Name: georaster_wrapper.cpp + * Project: Oracle Spatial GeoRaster Driver + * Purpose: Implement GeoRasterWrapper methods + * Author: Ivan Lucena [ivan.lucena at oracle.com] + * + ****************************************************************************** + * Copyright (c) 2008, Ivan Lucena + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files ( the "Software" ), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include + +#include "georaster_priv.h" +#include "cpl_error.h" +#include "cpl_string.h" +#include "cpl_minixml.h" + +// --------------------------------------------------------------------------- +// GeoRasterWrapper() +// --------------------------------------------------------------------------- + +GeoRasterWrapper::GeoRasterWrapper() +{ + nRasterId = -1; + phMetadata = NULL; + nRasterRows = 0; + nRasterColumns = 0; + nRasterBands = 0; + nRowBlockSize = 0; + nColumnBlockSize = 0; + nBandBlockSize = 0; + nTotalColumnBlocks = 0; + nTotalRowBlocks = 0; + nTotalBandBlocks = 0; + nCellSizeBits = 0; + nGDALCellBytes = 0; + dfXCoefficient[0] = 1.0; + dfXCoefficient[1] = 0.0; + dfXCoefficient[2] = 0.0; + dfYCoefficient[0] = 0.0; + dfYCoefficient[1] = 1.0; + dfYCoefficient[2] = 0.0; + sCompressionType = "NONE"; + nCompressQuality = 75; + bGenPyramid = false; + nPyramidLevels = 0; + sPyramidResampling = "NN"; + pahLocator = NULL; + pabyBlockBuf = NULL; + pabyCompressBuf = NULL; + bIsReferenced = false; + poBlockStmt = NULL; + nCacheBlockId = -1; + nCurrentLevel = -1; + pahLevels = NULL; + nLevelOffset = 0L; + sInterleaving = "BSQ"; + bUpdate = false; + bInitializeIO = false; + bFlushMetadata = false; + nSRID = 0; + nExtentSRID = 0; + bGenSpatialIndex = false; + bCreateObjectTable = false; + nPyramidMaxLevel = 0; + nBlockCount = 0L; + nGDALBlockBytes = 0L; + sDInfo.global_state = 0; + sCInfo.global_state = 0; + bHasBitmapMask = false; + nBlockBytes = 0L; + bFlushBlock = false; + nFlushBlockSize = 0L; + bUniqueFound = false; + sValueAttributeTab = ""; + psNoDataList = NULL; + bWriteOnly = false; + bBlocking = true; + bAutoBlocking = false; + eModelCoordLocation = MCL_DEFAULT; + phRPC = NULL; +} + +// --------------------------------------------------------------------------- +// GeoRasterDataset() +// --------------------------------------------------------------------------- + +GeoRasterWrapper::~GeoRasterWrapper() +{ + FlushMetadata(); + + if( pahLocator && nBlockCount ) + { + OWStatement::Free( pahLocator, nBlockCount ); + } + + CPLFree( pahLocator ); + CPLFree( pabyBlockBuf ); + CPLFree( pabyCompressBuf ); + CPLFree( pahLevels ); + + if( CPLListCount( psNoDataList ) ) + { + CPLList* psList = NULL; + + for( psList = psNoDataList; psList ; psList = psList->psNext ) + { + CPLFree( psList->pData ); + } + + CPLListDestroy( psNoDataList ); + } + + if( poBlockStmt ) + { + delete poBlockStmt; + } + + CPLDestroyXMLNode( phMetadata ); + + if( sDInfo.global_state ) + { + jpeg_destroy_decompress( &sDInfo ); + } + + if( sCInfo.global_state ) + { + jpeg_destroy_compress( &sCInfo ); + } + + if( poConnection ) + { + delete poConnection; + } + + if( phRPC ) + { + CPLFree( phRPC ); + } +} + +// --------------------------------------------------------------------------- +// ParseIdentificator() +// --------------------------------------------------------------------------- +// +// StringID: +// {georaster,geor}:{/,,}{/,@},,, +// {georaster,geor}:{/,,}{/,@},, +// +// --------------------------------------------------------------------------- + +char** GeoRasterWrapper::ParseIdentificator( const char* pszStringID ) +{ + + char* pszStartPos = (char*) strstr( pszStringID, ":" ) + 1; + + char** papszParam = CSLTokenizeString2( pszStartPos, ",@", + CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS | + CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + + // ------------------------------------------------------------------- + // The "/" should not be catch on the previous parser + // ------------------------------------------------------------------- + + if( CSLCount( papszParam ) > 0 ) + { + char** papszFirst2 = CSLTokenizeString2( papszParam[0], "/", + CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS ); + if( CSLCount( papszFirst2 ) == 2 ) + { + papszParam = CSLInsertStrings( papszParam, 0, papszFirst2 ); + papszParam = CSLRemoveStrings( papszParam, 2, 1, NULL ); + } + CSLDestroy( papszFirst2 ); + } + + return papszParam; + +} + +// --------------------------------------------------------------------------- +// Open() +// --------------------------------------------------------------------------- + +GeoRasterWrapper* GeoRasterWrapper::Open( const char* pszStringId, bool bUpdate ) +{ + char** papszParam = ParseIdentificator( pszStringId ); + + // --------------------------------------------------------------- + // Validate identificator + // --------------------------------------------------------------- + + int nArgc = CSLCount( papszParam ); + + for( ; nArgc < 3; nArgc++ ) + { + papszParam = CSLAddString( papszParam, "" ); + } + + // --------------------------------------------------------------- + // Create a GeoRasterWrapper object + // --------------------------------------------------------------- + + GeoRasterWrapper* poGRW = new GeoRasterWrapper(); + + if( ! poGRW ) + { + return NULL; + } + + poGRW->bUpdate = bUpdate; + + // --------------------------------------------------------------- + // Get a connection with Oracle server + // --------------------------------------------------------------- + + poGRW->poConnection = new OWConnection( papszParam[0], + papszParam[1], + papszParam[2] ); + + if( ! poGRW->poConnection->Succeeded() ) + { + CSLDestroy( papszParam ); + delete poGRW; + return NULL; + } + + // ------------------------------------------------------------------- + // Extract schema name + // ------------------------------------------------------------------- + + if( nArgc > 3 ) + { + char** papszSchema = CSLTokenizeString2( papszParam[3], ".", + CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS ); + + if( CSLCount( papszSchema ) == 2 ) + { + poGRW->sOwner = papszSchema[0]; + poGRW->sSchema = CPLSPrintf( "%s.", poGRW->sOwner.c_str() ); + + papszParam = CSLRemoveStrings( papszParam, 3, 1, NULL ); + + if( ! EQUAL( papszSchema[1], "" ) ) + { + papszParam = CSLInsertString( papszParam, 3, papszSchema[1] ); + } + + nArgc = CSLCount( papszParam ); + } + else + { + poGRW->sSchema = ""; + poGRW->sOwner = poGRW->poConnection->GetUser(); + } + + CSLDestroy( papszSchema ); + } + else + { + poGRW->sSchema = ""; + poGRW->sOwner = poGRW->poConnection->GetUser(); + } + + // ------------------------------------------------------------------- + // Assign parameters from Identification string + // ------------------------------------------------------------------- + + switch( nArgc ) + { + case 6 : + poGRW->sTable = papszParam[3]; + poGRW->sColumn = papszParam[4]; + poGRW->sWhere = papszParam[5]; + break; + case 5 : + if( OWIsNumeric( papszParam[4] ) ) + { + poGRW->sDataTable = papszParam[3]; + poGRW->nRasterId = atoi( papszParam[4]); + break; + } + else + { + poGRW->sTable = papszParam[3]; + poGRW->sColumn = papszParam[4]; + return poGRW; + } + case 4 : + poGRW->sTable = papszParam[3]; + return poGRW; + default : + return poGRW; + } + + CSLDestroy( papszParam ); + + // ------------------------------------------------------------------- + // Query all the basic information at once to reduce round trips + // ------------------------------------------------------------------- + + char szOwner[OWCODE]; + char szTable[OWCODE]; + char szColumn[OWTEXT]; + char szDataTable[OWCODE]; + char szWhere[OWTEXT]; + int nRasterId = -1; + int nSizeX = 0; + int nSizeY = 0; + int nSRID = 0; + OCILobLocator* phLocator = NULL; + double dfULx = 0.0; + double dfURx = 0.0; + double dfLRx = 0.0; + double dfULy = 0.0; + double dfLLy = 0.0; + double dfLRy = 0.0; + char szWKText[3 * OWTEXT]; + char szAuthority[OWTEXT]; + char szMLC[OWTEXT]; + + szOwner[0] = '\0'; + szTable[0] = '\0'; + szColumn[0] = '\0'; + szDataTable[0] = '\0'; + szWhere[0] = '\0'; + szWKText[0] = '\0'; + szAuthority[0] = '\0'; + szMLC[0] = '\0'; + + if( ! poGRW->sOwner.empty() ) + { + strcpy( szOwner, poGRW->sOwner.c_str() ); + } + + if( ! poGRW->sTable.empty() ) + { + strcpy( szTable, poGRW->sTable.c_str() ); + } + + if( ! poGRW->sColumn.empty() ) + { + strcpy( szColumn, poGRW->sColumn.c_str() ); + } + + if( ! poGRW->sDataTable.empty() ) + { + strcpy( szDataTable, poGRW->sDataTable.c_str() ); + } + + nRasterId = poGRW->nRasterId; + + if( ! poGRW->sWhere.empty() ) + { + strcpy( szWhere, poGRW->sWhere.c_str() ); + } + + OWStatement* poStmt = poGRW->poConnection->CreateStatement( + "DECLARE\n" + " SCM VARCHAR2(64) := 'xmlns=\"http://xmlns.oracle.com/spatial/georaster\"';\n" + " GUL SDO_GEOMETRY := null;\n" + " GUR SDO_GEOMETRY := null;\n" + " GLL SDO_GEOMETRY := null;\n" + " GLR SDO_GEOMETRY := null;\n" + "BEGIN\n" + "\n" + " IF :datatable IS NOT NULL AND :rasterid > 0 THEN\n" + "\n" + " EXECUTE IMMEDIATE\n" + " 'SELECT OWNER, TABLE_NAME, COLUMN_NAME\n" + " FROM ALL_SDO_GEOR_SYSDATA\n" + " WHERE RDT_TABLE_NAME = UPPER(:1)\n" + " AND RASTER_ID = :2'\n" + " INTO :owner, :table, :column\n" + " USING :datatable, :rasterid;\n" + "\n" + " EXECUTE IMMEDIATE\n" + " 'SELECT T.'||:column||'.METADATA.getClobVal()\n" + " FROM '||:owner||'.'||:table||' T\n" + " WHERE T.'||:column||'.RASTERDATATABLE = UPPER(:1)\n" + " AND T.'||:column||'.RASTERID = :2'\n" + " INTO :metadata\n" + " USING :datatable, :rasterid;\n" + " :counter := 1;\n" + "\n" + " ELSE\n" + "\n" + " EXECUTE IMMEDIATE\n" + " 'SELECT T.'||:column||'.RASTERDATATABLE,\n" + " T.'||:column||'.RASTERID,\n" + " T.'||:column||'.METADATA.getClobVal()\n" + " FROM '||:owner||'.'||:table||' T\n" + " WHERE '||:where\n" + " INTO :datatable, :rasterid, :metadata;\n" + " :counter := 1;\n" + "\n" + " END IF;\n" + "\n" + " SELECT\n" + " extractValue(XMLType(:metadata)," + "'/georasterMetadata/rasterInfo/dimensionSize[@type=\"ROW\"]/size', " + "SCM),\n" + " extractValue(XMLType(:metadata)," + "'/georasterMetadata/rasterInfo/dimensionSize[@type=\"COLUMN\"]/size', " + "SCM),\n" + " extractValue(XMLType(:metadata)," + "'/georasterMetadata/spatialReferenceInfo/SRID', " + "SCM),\n" + " extractValue(XMLType(:metadata)," + "'/georasterMetadata/spatialReferenceInfo/modelCoordinateLocation', " + "SCM)\n" + " INTO :sizey, :sizex, :srid, :mcl FROM DUAL;\n" + "\n" + " EXECUTE IMMEDIATE\n" + " 'SELECT\n" + " SDO_GEOR.getModelCoordinate('||:column||', 0, " + "SDO_NUMBER_ARRAY(0, 0)),\n" + " SDO_GEOR.getModelCoordinate('||:column||', 0, " + "SDO_NUMBER_ARRAY(0, '||:sizex||')),\n" + " SDO_GEOR.getModelCoordinate('||:column||', 0, " + "SDO_NUMBER_ARRAY('||:sizey||', 0)),\n" + " SDO_GEOR.getModelCoordinate('||:column||', 0, " + "SDO_NUMBER_ARRAY('||:sizey||', '||:sizex||'))\n" + " FROM '||:owner||'.'||:table||' T\n" + " WHERE T.'||:column||'.RASTERDATATABLE = UPPER(:1)\n" + " AND T.'||:column||'.RASTERID = :2'\n" + " INTO GUL, GLL, GUR, GLR\n" + " USING :datatable, :rasterid;\n" + "\n" + " :ULx := GUL.sdo_point.x;\n" + " :URx := GUR.sdo_point.x;\n" + " :LRx := GLR.sdo_point.x;\n" + " :ULy := GUL.sdo_point.y;\n" + " :LLy := GLL.sdo_point.y;\n" + " :LRy := GLR.sdo_point.y;\n" + "\n" + " BEGIN\n" + " EXECUTE IMMEDIATE\n" + " 'SELECT WKTEXT, AUTH_NAME\n" + " FROM MDSYS.CS_SRS\n" + " WHERE SRID = :1 AND WKTEXT IS NOT NULL'\n" + " INTO :wktext, :authority\n" + " USING :srid;\n" + " EXCEPTION\n" + " WHEN no_data_found THEN\n" + " :wktext := '';\n" + " :authority := '';\n" + " END;\n" + "\n" + " EXCEPTION\n" + " WHEN no_data_found THEN :counter := 0;\n" + " WHEN too_many_rows THEN :counter := 2;\n" + "END;" ); + + int nCounter = 0; + + poStmt->BindName( ":datatable", szDataTable ); + poStmt->BindName( ":rasterid", &nRasterId ); + poStmt->BindName( ":owner", szOwner ); + poStmt->BindName( ":table", szTable ); + poStmt->BindName( ":column", szColumn ); + poStmt->BindName( ":where", szWhere ); + poStmt->BindName( ":counter", &nCounter ); + poStmt->BindName( ":metadata", &phLocator ); + poStmt->BindName( ":sizex", &nSizeX ); + poStmt->BindName( ":sizey", &nSizeY ); + poStmt->BindName( ":srid", &nSRID ); + poStmt->BindName( ":mcl", szMLC ); + poStmt->BindName( ":ULx", &dfULx ); + poStmt->BindName( ":URx", &dfURx ); + poStmt->BindName( ":LRx", &dfLRx ); + poStmt->BindName( ":ULy", &dfULy ); + poStmt->BindName( ":LLy", &dfLLy ); + poStmt->BindName( ":LRy", &dfLRy ); + poStmt->BindName( ":wktext", szWKText, sizeof(szWKText) ); + poStmt->BindName( ":authority", szAuthority ); + + CPLErrorReset(); + + if( ! poStmt->Execute() ) + { + delete poStmt; + delete poGRW; + return NULL; + } + + if( nCounter < 1 ) + { + delete poStmt; + delete poGRW; + return NULL; + } + + poGRW->sSchema = CPLSPrintf( "%s.", szOwner ); + poGRW->sOwner = szOwner; + poGRW->sTable = szTable; + poGRW->sColumn = szColumn; + + if( nCounter == 1 ) + { + poGRW->bUniqueFound = true; + } + else + { + poGRW->bUniqueFound = false; + + delete poStmt; + return poGRW; + } + + poGRW->sWKText = szWKText; + poGRW->sAuthority = szAuthority; + poGRW->sDataTable = szDataTable; + poGRW->nRasterId = nRasterId; + poGRW->sWhere = CPLSPrintf( + "T.%s.RASTERDATATABLE = UPPER('%s') AND T.%s.RASTERID = %d", + poGRW->sColumn.c_str(), + poGRW->sDataTable.c_str(), + poGRW->sColumn.c_str(), + poGRW->nRasterId ); + + // ------------------------------------------------------------------- + // Read Metadata XML in text + // ------------------------------------------------------------------- + + char* pszXML = poStmt->ReadCLob( phLocator ); + + if( pszXML ) + { + // ----------------------------------------------------------- + // Get basic information from xml metadata + // ----------------------------------------------------------- + + poGRW->phMetadata = CPLParseXMLString( pszXML ); + poGRW->GetRasterInfo(); + } + else + { + poGRW->sDataTable = ""; + poGRW->nRasterId = 0; + } + + // -------------------------------------------------------------------- + // Load Coefficients matrix + // -------------------------------------------------------------------- + + if ( EQUAL( szMLC, "UPPERLEFT" ) ) + { + poGRW->eModelCoordLocation = MCL_UPPERLEFT; + } + else + { + poGRW->eModelCoordLocation = MCL_DEFAULT; + } + + double dfRotation = 0.0; + + if( ! CPLIsEqual( dfULy, dfLLy ) ) + { + dfRotation = ( dfURx - dfULx ) / ( dfLLy - dfULy ); + } + + poGRW->dfXCoefficient[0] = ( dfLRx - dfULx ) / nSizeX; + poGRW->dfXCoefficient[1] = dfRotation; + poGRW->dfXCoefficient[2] = dfULx; + poGRW->dfYCoefficient[0] = -dfRotation; + poGRW->dfYCoefficient[1] = ( dfLRy - dfULy ) / nSizeY; + poGRW->dfYCoefficient[2] = dfULy; + + if ( poGRW->eModelCoordLocation == MCL_CENTER ) + { + poGRW->dfXCoefficient[2] -= poGRW->dfXCoefficient[0] / 2; + poGRW->dfYCoefficient[2] -= poGRW->dfYCoefficient[1] / 2; + + CPLDebug("GEOR","eModelCoordLocation = MCL_CENTER"); + } + else + { + CPLDebug("GEOR","eModelCoordLocation = MCL_UPPERLEFT"); + } + + // ------------------------------------------------------------------- + // Apply ULTCoordinate + // ------------------------------------------------------------------- + + poGRW->dfXCoefficient[2] += + ( poGRW->anULTCoordinate[0] * poGRW->dfXCoefficient[0] ); + + poGRW->dfYCoefficient[2] += + ( poGRW->anULTCoordinate[1] * poGRW->dfYCoefficient[1] ); + + // ------------------------------------------------------------------- + // Clean up + // ------------------------------------------------------------------- + + OCIDescriptorFree( phLocator, OCI_DTYPE_LOB ); + CPLFree( pszXML ); + delete poStmt; + + // ------------------------------------------------------------------- + // Return a GeoRasterWrapper object + // ------------------------------------------------------------------- + + return poGRW; +} + +// --------------------------------------------------------------------------- +// Create() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::Create( char* pszDescription, + char* pszInsert, + bool bUpdate ) +{ + CPLString sValues; + CPLString sFormat; + + if( sTable.empty() || + sColumn.empty() ) + { + return false; + } + + // ------------------------------------------------------------------- + // Parse RDT/RID from the current szValues + // ------------------------------------------------------------------- + + char szRDT[OWNAME]; + char szRID[OWCODE]; + + if( ! sDataTable.empty() ) + { + strcpy( szRDT, CPLSPrintf( "'%s'", sDataTable.c_str() ) ); + } + else + { + strcpy( szRDT, OWParseSDO_GEOR_INIT( sValues.c_str(), 1 ) ); + } + + if ( nRasterId > 0 ) + { + strcpy( szRID, CPLSPrintf( "%d", nRasterId ) ); + } + else + { + strcpy( szRID, OWParseSDO_GEOR_INIT( sValues.c_str(), 2 ) ); + + if ( EQUAL( szRID, "" ) ) + { + strcpy( szRID, "NULL" ); + } + } + + // ------------------------------------------------------------------- + // Description parameters + // ------------------------------------------------------------------- + + char szDescription[OWTEXT]; + + if( bUpdate == false ) + { + + if ( pszDescription ) + { + strcpy( szDescription, pszDescription ); + } + else + { + strcpy( szDescription, CPLSPrintf( + "(%s MDSYS.SDO_GEORASTER)", sColumn.c_str() ) ); + } + + // --------------------------------------------------------------- + // Insert parameters + // --------------------------------------------------------------- + + if( pszInsert ) + { + sValues = pszInsert; + + if( pszInsert[0] == '(' && sValues.ifind( "VALUES" ) == std::string::npos ) + { + sValues = CPLSPrintf( "VALUES %s", pszInsert ); + } + } + else + { + sValues = CPLSPrintf( "VALUES (SDO_GEOR.INIT(%s,%s))", szRDT, szRID ); + } + } + + // ----------------------------------------------------------- + // Storage parameters + // ----------------------------------------------------------- + + nColumnBlockSize = nColumnBlockSize == 0 ? DEFAULT_BLOCK_COLUMNS : nColumnBlockSize; + nRowBlockSize = nRowBlockSize == 0 ? DEFAULT_BLOCK_ROWS : nRowBlockSize; + nBandBlockSize = nBandBlockSize == 0 ? 1 : nBandBlockSize; + + // ----------------------------------------------------------- + // Blocking storage parameters + // ----------------------------------------------------------- + + CPLString sBlocking; + + if( bBlocking == true ) + { + if( bAutoBlocking == true ) + { + int nBlockXSize = nColumnBlockSize; + int nBlockYSize = nRowBlockSize; + int nBlockBSize = nBandBlockSize; + + OWStatement* poStmt; + + poStmt = poConnection->CreateStatement( + "DECLARE\n" + " dimensionSize sdo_number_array;\n" + " blockSize sdo_number_array;\n" + "BEGIN\n" + " dimensionSize := sdo_number_array(:1, :2, :3);\n" + " blockSize := sdo_number_array(:4, :5, :6);\n" + " sdo_geor_utl.calcOptimizedBlockSize(dimensionSize,blockSize);\n" + " :4 := blockSize(1);\n" + " :5 := blockSize(2);\n" + " :6 := blockSize(3);\n" + "END;" ); + + poStmt->Bind( &nRasterColumns ); + poStmt->Bind( &nRasterRows ); + poStmt->Bind( &nRasterBands ); + poStmt->Bind( &nBlockXSize ); + poStmt->Bind( &nBlockYSize ); + poStmt->Bind( &nBlockBSize ); + + if( poStmt->Execute() ) + { + nColumnBlockSize = nBlockXSize; + nRowBlockSize = nBlockYSize; + nBandBlockSize = nBlockBSize; + } + + delete poStmt; + } + + if( nRasterBands == 1 ) + { + sBlocking = CPLSPrintf( + "blockSize=(%d, %d)", + nRowBlockSize, + nColumnBlockSize ); + } + else + { + sBlocking = CPLSPrintf( + "blockSize=(%d, %d, %d)", + nRowBlockSize, + nColumnBlockSize, + nBandBlockSize ); + } + } + else + { + sBlocking = "blocking=FALSE"; + + nColumnBlockSize = nRasterColumns; + nRowBlockSize = nRasterRows; + nBandBlockSize = nRasterBands; + } + + // ----------------------------------------------------------- + // Complete format parameters + // ----------------------------------------------------------- + + if( poConnection->GetVersion() > 10 ) + { + if( nRasterBands == 1 ) + { + sFormat = CPLSPrintf( + "20001, '" + "dimSize=(%d,%d) ", + nRasterRows, nRasterColumns ); + } + else + { + sFormat = CPLSPrintf( + "21001, '" + "dimSize=(%d,%d,%d) ", + nRasterRows, nRasterColumns, nRasterBands ); + } + + if( EQUALN( sCompressionType.c_str(), "JPEG", 4 ) ) + { + sFormat.append( CPLSPrintf( + "%s " + "cellDepth=%s " + "interleaving=%s " + "compression=%s " + "quality=%d'", + sBlocking.c_str(), + sCellDepth.c_str(), + sInterleaving.c_str(), + sCompressionType.c_str(), + nCompressQuality) ); + } + else + { + sFormat.append( CPLSPrintf( + "%s " + "cellDepth=%s " + "interleaving=%s " + "compression=%s'", + sBlocking.c_str(), + sCellDepth.c_str(), + sInterleaving.c_str(), + sCompressionType.c_str() ) ); + } + } + else + { + // ------------------------------------------------------- + // For versions 10g or older + // ------------------------------------------------------- + + sFormat = CPLSPrintf( + "%s " + "cellDepth=%s " + "interleaving=%s " + "pyramid=FALSE " + "compression=NONE", + sBlocking.c_str(), + sCellDepth.c_str(), + sInterleaving.c_str() ); + } + + nTotalColumnBlocks = (int) + ( ( nRasterColumns + nColumnBlockSize - 1 ) / nColumnBlockSize ); + + nTotalRowBlocks = (int) + ( ( nRasterRows + nRowBlockSize - 1 ) / nRowBlockSize ); + + nTotalBandBlocks = (int) + ( ( nRasterBands + nBandBlockSize - 1) / nBandBlockSize ); + + // ------------------------------------------------------------------- + // Create Georaster Table if needed + // ------------------------------------------------------------------- + + OWStatement* poStmt; + + if( ! bUpdate ) + { + poStmt = poConnection->CreateStatement( CPLSPrintf( + "DECLARE\n" + " TAB VARCHAR2(68) := UPPER('%s');\n" + " COL VARCHAR2(68) := UPPER('%s');\n" + " OWN VARCHAR2(68) := UPPER('%s');\n" + " CNT NUMBER := 0;\n" + "BEGIN\n" + " EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ALL_TABLES\n" + " WHERE TABLE_NAME = :1 AND OWNER = UPPER(:2)'\n" + " INTO CNT USING TAB, OWN;\n" + "\n" + " IF CNT = 0 THEN\n" + " EXECUTE IMMEDIATE 'CREATE TABLE %s%s %s';\n" + " SDO_GEOR_UTL.createDMLTrigger( TAB, COL );\n" + " END IF;\n" + "END;", + sTable.c_str(), + sColumn.c_str(), + sOwner.c_str(), + sSchema.c_str(), + sTable.c_str(), + szDescription ) ); + + if( ! poStmt->Execute() ) + { + delete ( poStmt ); + return false; + } + + delete poStmt; + } + + // ----------------------------------------------------------- + // Prepare UPDATE or INSERT comand + // ----------------------------------------------------------- + + CPLString sCommand; + + if( bUpdate ) + { + sCommand = CPLSPrintf( + "SELECT %s INTO GR1 FROM %s%s T WHERE %s FOR UPDATE;", + sColumn.c_str(), + sSchema.c_str(), + sTable.c_str(), + sWhere.c_str() ); + } + else + { + sCommand = CPLSPrintf( + "INSERT INTO %s%s %s RETURNING %s INTO GR1;", + sSchema.c_str(), + sTable.c_str(), + sValues.c_str(), + sColumn.c_str() ); + } + + // ----------------------------------------------------------- + // Create RTD if needed and insert/update GeoRaster + // ----------------------------------------------------------- + + char szBindRDT[OWNAME]; + int nBindRID = 0; + szBindRDT[0] = '\0'; + + CPLString sObjectTable; + CPLString sSecureFile; + + // For version > 11 create RDT as relational table by default, + // if it is not specified by create-option OBJECTTABLE=TRUE + + if( poConnection->GetVersion() <= 11 || bCreateObjectTable ) + { + sObjectTable = "OF MDSYS.SDO_RASTER\n ("; + } + else + { + sObjectTable = CPLSPrintf("(\n" + " RASTERID NUMBER,\n" + " PYRAMIDLEVEL NUMBER,\n" + " BANDBLOCKNUMBER NUMBER,\n" + " ROWBLOCKNUMBER NUMBER,\n" + " COLUMNBLOCKNUMBER NUMBER,\n" + " BLOCKMBR SDO_GEOMETRY,\n" + " RASTERBLOCK BLOB,\n" + " CONSTRAINT '||:rdt||'_RDT_PK "); + } + + // For version >= 11 create RDT rasterBlock as securefile + + if( poConnection->GetVersion() >= 11 ) + { + sSecureFile = "SECUREFILE(CACHE)"; + } + else + { + sSecureFile = "(NOCACHE NOLOGGING)"; + } + + if( poConnection->GetVersion() > 10 ) + { + poStmt = poConnection->CreateStatement( CPLSPrintf( + "DECLARE\n" + " TAB VARCHAR2(68) := UPPER('%s');\n" + " COL VARCHAR2(68) := UPPER('%s');\n" + " OWN VARCHAR2(68) := UPPER('%s');\n" + " CNT NUMBER := 0;\n" + " GR1 SDO_GEORASTER := NULL;\n" + "BEGIN\n" + "\n" + " %s\n" + "\n" + " GR1.spatialExtent := NULL;\n" + "\n" + " SELECT GR1.RASTERDATATABLE INTO :rdt FROM DUAL;\n" + " SELECT GR1.RASTERID INTO :rid FROM DUAL;\n" + "\n" + " EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ALL_OBJECT_TABLES\n" + " WHERE TABLE_NAME = :1 AND OWNER = UPPER(:2)'\n" + " INTO CNT USING :rdt, OWN;\n" + "\n" + " IF CNT = 0 THEN\n" + " EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ALL_TABLES\n" + " WHERE TABLE_NAME = :1 AND OWNER = UPPER(:2)'\n" + " INTO CNT USING :rdt, OWN;\n" + " END IF;\n" + "\n" + " IF CNT = 0 THEN\n" + " EXECUTE IMMEDIATE 'CREATE TABLE %s'||:rdt||' %s" + "PRIMARY KEY (RASTERID, PYRAMIDLEVEL,\n" + " BANDBLOCKNUMBER, ROWBLOCKNUMBER, COLUMNBLOCKNUMBER))\n" + " LOB(RASTERBLOCK) STORE AS %s';\n" + " END IF;\n" + "\n" + " SDO_GEOR.createTemplate(GR1, %s, null, 'TRUE');\n" + "\n" + " UPDATE %s%s T SET %s = GR1 WHERE\n" + " T.%s.RasterDataTable = :rdt AND\n" + " T.%s.RasterId = :rid;\n" + "\n" + " EXECUTE IMMEDIATE\n" + " 'SELECT T.%s.METADATA.getClobVal()\n" + " FROM %s%s T\n" + " WHERE T.%s.RASTERDATATABLE = UPPER(:1)\n" + " AND T.%s.RASTERID = :2'\n" + " INTO :metadata\n" + " USING :rdt, :rid;\n" + "\n" + " COMMIT;\n" + "\n" + "END;\n", + sTable.c_str(), + sColumn.c_str(), + sOwner.c_str(), + sCommand.c_str(), + sSchema.c_str(), + sObjectTable.c_str(), + sSecureFile.c_str(), + sFormat.c_str(), + sSchema.c_str(), + sTable.c_str(), + sColumn.c_str(), + sColumn.c_str(), + sColumn.c_str(), + sColumn.c_str(), + sSchema.c_str(), + sTable.c_str(), + sColumn.c_str(), + sColumn.c_str() ) ); + + OCILobLocator* phLocator = NULL; + + poStmt->BindName( ":metadata", &phLocator ); + poStmt->BindName( ":rdt", szBindRDT ); + poStmt->BindName( ":rid", &nBindRID ); + + CPLErrorReset(); + + if( ! poStmt->Execute() ) + { + delete poStmt; + return false; + } + + sDataTable = szBindRDT; + nRasterId = nBindRID; + + OCIDescriptorFree( phLocator, OCI_DTYPE_LOB ); + + + delete poStmt; + + return true; + } + + // ----------------------------------------------------------- + // Procedure for Server version older than 11 + // ----------------------------------------------------------- + + char szCreateBlank[OWTEXT]; + + if( nRasterBands == 1 ) + { + strcpy( szCreateBlank, CPLSPrintf( "SDO_GEOR.createBlank(20001, " + "SDO_NUMBER_ARRAY(0, 0), " + "SDO_NUMBER_ARRAY(%d, %d), 0, :rdt, :rid)", + nRasterRows, nRasterColumns ) ); + } + else + { + strcpy( szCreateBlank, CPLSPrintf( "SDO_GEOR.createBlank(21001, " + "SDO_NUMBER_ARRAY(0, 0, 0), " + "SDO_NUMBER_ARRAY(%d, %d, %d), 0, :rdt, :rid)", + nRasterRows, nRasterColumns, nRasterBands ) ); + } + + poStmt = poConnection->CreateStatement( CPLSPrintf( + "DECLARE\n" + " W NUMBER := :1;\n" + " H NUMBER := :2;\n" + " BB NUMBER := :3;\n" + " RB NUMBER := :4;\n" + " CB NUMBER := :5;\n" + " OWN VARCHAR2(68) := UPPER('%s');\n" + " X NUMBER := 0;\n" + " Y NUMBER := 0;\n" + " CNT NUMBER := 0;\n" + " GR1 SDO_GEORASTER := NULL;\n" + " GR2 SDO_GEORASTER := NULL;\n" + " STM VARCHAR2(1024) := '';\n" + "BEGIN\n" + "\n" + " %s\n" + "\n" + " SELECT GR1.RASTERDATATABLE INTO :rdt FROM DUAL;\n" + " SELECT GR1.RASTERID INTO :rid FROM DUAL;\n" + "\n" + " SELECT %s INTO GR2 FROM %s%s T WHERE" + " T.%s.RasterDataTable = :rdt AND" + " T.%s.RasterId = :rid FOR UPDATE;\n" + "\n" + " GR1 := %s;\n" + "\n" + " SDO_GEOR.changeFormatCopy(GR1, '%s', GR2);\n" + "\n" + " UPDATE %s%s T SET %s = GR2 WHERE" + " T.%s.RasterDataTable = :rdt AND" + " T.%s.RasterId = :rid;\n" + "\n" + " EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ALL_OBJECT_TABLES\n" + " WHERE TABLE_NAME = :1 AND OWNER = UPPER(:2)'\n" + " INTO CNT USING :rdt, OWN;\n" + "\n" + " IF CNT = 0 THEN\n" + " EXECUTE IMMEDIATE 'CREATE TABLE %s'||:rdt||' OF MDSYS.SDO_RASTER\n" + " (PRIMARY KEY (RASTERID, PYRAMIDLEVEL, BANDBLOCKNUMBER,\n" + " ROWBLOCKNUMBER, COLUMNBLOCKNUMBER))\n" + " LOB(RASTERBLOCK) STORE AS (NOCACHE NOLOGGING)';\n" + " ELSE\n" + " EXECUTE IMMEDIATE 'DELETE FROM %s'||:rdt||' WHERE RASTERID ='||:rid||' ';\n" + " END IF;\n" + "\n" + " STM := 'INSERT INTO %s'||:rdt||' VALUES (:1,0,:2-1,:3-1,:4-1,\n" + " SDO_GEOMETRY(2003, NULL, NULL, SDO_ELEM_INFO_ARRAY(1, 1003, 3),\n" + " SDO_ORDINATE_ARRAY(:5,:6,:7-1,:8-1)), EMPTY_BLOB() )';\n\n" + " FOR b IN 1..BB LOOP\n" + " Y := 0;\n" + " FOR r IN 1..RB LOOP\n" + " X := 0;\n" + " FOR c IN 1..CB LOOP\n" + " EXECUTE IMMEDIATE STM USING :rid, b, r, c, Y, X, (Y+H), (X+W);\n" + " X := X + W;\n" + " END LOOP;\n" + " Y := Y + H;\n" + " END LOOP;\n" + " END LOOP;\n" + "\n" + " SDO_GEOR.georeference(GR1, %d, %d," + " SDO_NUMBER_ARRAY(1.0, 0.0, 0.0)," + " SDO_NUMBER_ARRAY(0.0, 1.0, 0.0));\n" + "\n" + " UPDATE %s%s T SET %s = GR1 WHERE" + " T.%s.RasterDataTable = :rdt AND" + " T.%s.RasterId = :rid;\n" + "\n" + " COMMIT;\n" + "\n" + "END;", + sOwner.c_str(), + sCommand.c_str(), + sColumn.c_str(), sSchema.c_str(), sTable.c_str(), + sColumn.c_str(), sColumn.c_str(), + szCreateBlank, + sFormat.c_str(), + sSchema.c_str(), sTable.c_str(), + sColumn.c_str(), sColumn.c_str(), sColumn.c_str(), + sSchema.c_str(), sSchema.c_str(), sSchema.c_str(), + DEFAULT_CRS, MCL_DEFAULT, + sSchema.c_str(), sTable.c_str(), + sColumn.c_str(), sColumn.c_str(), sColumn.c_str() ) ); + + poStmt->Bind( &nColumnBlockSize ); + poStmt->Bind( &nRowBlockSize ); + poStmt->Bind( &nTotalBandBlocks ); + poStmt->Bind( &nTotalRowBlocks ); + poStmt->Bind( &nTotalColumnBlocks ); + + poStmt->BindName( ":rdt", szBindRDT ); + poStmt->BindName( ":rid", &nBindRID ); + + if( ! poStmt->Execute() ) + { + delete poStmt; + return false; + } + + sDataTable = szBindRDT; + nRasterId = nBindRID; + + delete poStmt; + + return true; +} + +// --------------------------------------------------------------------------- +// PrepareToOverwrite() +// --------------------------------------------------------------------------- + +void GeoRasterWrapper::PrepareToOverwrite( void ) +{ + nTotalColumnBlocks = 0; + nTotalRowBlocks = 0; + nTotalBandBlocks = 0; + if( sscanf( sCellDepth.c_str(), "%dBIT", &nCellSizeBits ) ) + { + nGDALCellBytes = GDALGetDataTypeSize( + OWGetDataType( sCellDepth.c_str() ) ) / 8; + } + else + { + nGDALCellBytes = 1; + } + dfXCoefficient[0] = 1.0; + dfXCoefficient[1] = 0.0; + dfXCoefficient[2] = 0.0; + dfYCoefficient[0] = 0.0; + dfYCoefficient[1] = 1.0; + dfYCoefficient[2] = 0.0; + sCompressionType = "NONE"; + nCompressQuality = 75; + bGenPyramid = false; + nPyramidLevels = 0; + sPyramidResampling = "NN"; + bIsReferenced = false; + nCacheBlockId = -1; + nCurrentLevel = -1; + pahLevels = NULL; + nLevelOffset = 0L; + sInterleaving = "BSQ"; + bUpdate = false; + bInitializeIO = false; + bFlushMetadata = false; + nSRID = 0; + nExtentSRID = 0; + bGenSpatialIndex = false; + bCreateObjectTable = false; + nPyramidMaxLevel = 0; + nBlockCount = 0L; + sDInfo.global_state = 0; + sCInfo.global_state = 0; + bHasBitmapMask = false; + bWriteOnly = false; + bBlocking = true; + bAutoBlocking = false; + eModelCoordLocation = MCL_DEFAULT; + bFlushBlock = false; + nFlushBlockSize = 0L; + phRPC = NULL; +} + +// --------------------------------------------------------------------------- +// Delete() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::Delete( void ) +{ + if( ! bUniqueFound ) + { + return false; + } + + OWStatement* poStmt = poConnection->CreateStatement( CPLSPrintf( + "UPDATE %s%s T SET %s = NULL WHERE %s\n", + sSchema.c_str(), + sTable.c_str(), + sColumn.c_str(), + sWhere.c_str() ) ); + + bool bReturn = poStmt->Execute(); + + delete poStmt; + + return bReturn; +} + +// --------------------------------------------------------------------------- +// SetGeoReference() +// --------------------------------------------------------------------------- + +void GeoRasterWrapper::SetGeoReference( int nSRIDIn ) +{ + nSRID = nSRIDIn; + + bIsReferenced = true; + + bFlushMetadata = true; +} + +// --------------------------------------------------------------------------- +// GetRasterInfo() +// --------------------------------------------------------------------------- + +void GeoRasterWrapper::GetRasterInfo( void ) +{ + // ------------------------------------------------------------------- + // Get dimensions + // ------------------------------------------------------------------- + + int nCount = 0; + + CPLXMLNode* phDimSize = NULL; + const char* pszType = NULL; + + nCount = atoi( CPLGetXMLValue( phMetadata, + "rasterInfo.totalDimensions", "0" ) ); + phDimSize = CPLGetXMLNode( phMetadata, "rasterInfo.dimensionSize" ); + + int i = 0; + + for( i = 0; i < nCount; i++ ) + { + pszType = CPLGetXMLValue( phDimSize, "type", "0" ); + + if( EQUAL( pszType, "ROW" ) ) + { + nRasterRows = atoi( CPLGetXMLValue( phDimSize, "size", "0" ) ); + } + + if( EQUAL( pszType, "COLUMN" ) ) + { + nRasterColumns = atoi( CPLGetXMLValue( phDimSize, "size", "0" ) ); + } + + if( EQUAL( pszType, "BAND" ) ) + { + nRasterBands = atoi( CPLGetXMLValue( phDimSize, "size", "0" ) ); + } + + phDimSize = phDimSize->psNext; + } + + if( nRasterBands == 0 ) + { + nRasterBands = 1; + } + + // ------------------------------------------------------------------- + // Load NoData Values + // ------------------------------------------------------------------- + + LoadNoDataValues(); + + // ------------------------------------------------------------------- + // Get ULTCoordinate values + // ------------------------------------------------------------------- + + anULTCoordinate[0] = atoi(CPLGetXMLValue( + phMetadata, "rasterInfo.ULTCoordinate.column", "0")); + + anULTCoordinate[1] = atoi(CPLGetXMLValue( + phMetadata, "rasterInfo.ULTCoordinate.row", "0")); + + anULTCoordinate[2] = atoi(CPLGetXMLValue( + phMetadata, "rasterInfo.ULTCoordinate.band", "0")); + + // ------------------------------------------------------------------- + // Get Interleaving mode + // ------------------------------------------------------------------- + + sInterleaving = CPLGetXMLValue( phMetadata, + "rasterInfo.interleaving", "BSQ" ); + + // ------------------------------------------------------------------- + // Get blocking + // ------------------------------------------------------------------- + + nRowBlockSize = atoi( CPLGetXMLValue( phMetadata, + "rasterInfo.blocking.rowBlockSize", + CPLSPrintf( "%d", nRasterRows ) ) ); + + nColumnBlockSize = atoi( CPLGetXMLValue( phMetadata, + "rasterInfo.blocking.columnBlockSize", + CPLSPrintf( "%d", nRasterColumns ) ) ); + + nBandBlockSize = atoi( CPLGetXMLValue( phMetadata, + "rasterInfo.blocking.bandBlockSize", + CPLSPrintf( "%d", nRasterBands ) ) ); + + nTotalColumnBlocks = atoi( CPLGetXMLValue( phMetadata, + "rasterInfo.blocking.totalColumnBlocks","1") ); + + nTotalRowBlocks = atoi( CPLGetXMLValue( phMetadata, + "rasterInfo.blocking.totalRowBlocks", "1" ) ); + + nTotalBandBlocks = atoi( CPLGetXMLValue( phMetadata, + "rasterInfo.blocking.totalBandBlocks", "1" ) ); + + if( nBandBlockSize == -1 ) + { + nBandBlockSize = nRasterBands; + } + + // ------------------------------------------------------------------- + // Get data type + // ------------------------------------------------------------------- + + sCellDepth = CPLGetXMLValue( phMetadata, "rasterInfo.cellDepth", "8BIT_U" ); + + if( sscanf( sCellDepth.c_str(), "%dBIT", &nCellSizeBits ) ) + { + nGDALCellBytes = GDALGetDataTypeSize( + OWGetDataType( sCellDepth.c_str() ) ) / 8; + } + else + { + nGDALCellBytes = 1; + } + + sCompressionType = CPLGetXMLValue( phMetadata, + "rasterInfo.compression.type", "NONE" ); + + if( EQUALN( sCompressionType.c_str(), "JPEG", 4 ) ) + { + nCompressQuality = atoi( CPLGetXMLValue( phMetadata, + "rasterInfo.compression.quality", "75" ) ); + } + + if( EQUALN( sCompressionType.c_str(), "JPEG", 4 ) ) + { + sInterleaving = "BIP"; + } + + // ------------------------------------------------------------------- + // Get default RGB Bands + // ------------------------------------------------------------------- + + iDefaultRedBand = atoi( CPLGetXMLValue( phMetadata, + "objectInfo.defaultRed", "-1" ) ); + + iDefaultGreenBand = atoi( CPLGetXMLValue( phMetadata, + "objectInfo.defaultGreen", "-1" ) ); + + iDefaultBlueBand = atoi( CPLGetXMLValue( phMetadata, + "objectInfo.defaultBlue", "-1" ) ); + + // ------------------------------------------------------------------- + // Get Pyramid details + // ------------------------------------------------------------------- + + char szPyramidType[OWCODE]; + + strcpy( szPyramidType, CPLGetXMLValue( phMetadata, + "rasterInfo.pyramid.type", "None" ) ); + + if( EQUAL( szPyramidType, "DECREASE" ) ) + { + nPyramidMaxLevel = atoi( CPLGetXMLValue( phMetadata, + "rasterInfo.pyramid.maxLevel", "0" ) ); + } + + // ------------------------------------------------------------------- + // Check for RPCs + // ------------------------------------------------------------------- + + const char* pszModelType = CPLGetXMLValue( phMetadata, + "spatialReferenceInfo.modelType", "None" ); + + if( EQUAL( pszModelType, "FunctionalFitting" ) ) + { + GetRPC(); + } + + // ------------------------------------------------------------------- + // Prepare to get Extents + // ------------------------------------------------------------------- + + bIsReferenced = EQUAL( "TRUE", CPLGetXMLValue( phMetadata, + "spatialReferenceInfo.isReferenced", "FALSE" ) ); + + nSRID = atoi( CPLGetXMLValue( phMetadata, + "spatialReferenceInfo.SRID", "0" ) ); +} + +// --------------------------------------------------------------------------- +// GetStatistics() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::GetStatistics( int nBand, + char* pszMin, + char* pszMax, + char* pszMean, + char* pszMedian, + char* pszMode, + char* pszStdDev, + char* pszSampling ) +{ + int n = 1; + + CPLXMLNode *phSubLayer = CPLGetXMLNode( phMetadata, "layerInfo.subLayer" ); + + for( n = 1 ; phSubLayer ; phSubLayer = phSubLayer->psNext, n++ ) + { + if( n == nBand && CPLGetXMLNode( phSubLayer, "statisticDataset" ) ) + { + strncpy( pszSampling, CPLGetXMLValue( phSubLayer, + "statisticDataset.samplingFactor", "0.0" ), MAX_DOUBLE_STR_REP ); + strncpy( pszMin, CPLGetXMLValue( phSubLayer, + "statisticDataset.MIN", "0.0" ), MAX_DOUBLE_STR_REP ); + strncpy( pszMax, CPLGetXMLValue( phSubLayer, + "statisticDataset.MAX", "0.0" ), MAX_DOUBLE_STR_REP ); + strncpy( pszMean, CPLGetXMLValue( phSubLayer, + "statisticDataset.MEAN", "0.0" ), MAX_DOUBLE_STR_REP ); + strncpy( pszMedian, CPLGetXMLValue( phSubLayer, + "statisticDataset.MEDIAN", "0.0" ), MAX_DOUBLE_STR_REP ); + strncpy( pszMode, CPLGetXMLValue( phSubLayer, + "statisticDataset.MODEVALUE", "0.0" ), MAX_DOUBLE_STR_REP ); + strncpy( pszStdDev, CPLGetXMLValue( phSubLayer, + "statisticDataset.STD", "0.0" ), MAX_DOUBLE_STR_REP ); + return true; + } + } + return false; +} + +// --------------------------------------------------------------------------- +// SetStatistics() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::SetStatistics( int nBand, + const char* pszMin, + const char* pszMax, + const char* pszMean, + const char* pszMedian, + const char* pszMode, + const char* pszStdDev, + const char* pszSampling ) +{ + InitializeLayersNode(); + + bFlushMetadata = true; + + int n = 1; + + CPLXMLNode* phSubLayer = CPLGetXMLNode( phMetadata, "layerInfo.subLayer" ); + + for( n = 1 ; phSubLayer ; phSubLayer = phSubLayer->psNext, n++ ) + { + if( n != nBand ) + { + continue; + } + + CPLXMLNode* psSDaset = CPLGetXMLNode( phSubLayer, "statisticDataset" ); + + if( psSDaset != NULL ) + { + CPLRemoveXMLChild( phSubLayer, psSDaset ); + CPLDestroyXMLNode( psSDaset ); + } + + psSDaset = CPLCreateXMLNode(phSubLayer,CXT_Element,"statisticDataset"); + + CPLCreateXMLElementAndValue(psSDaset,"samplingFactor", pszSampling ); + CPLCreateXMLElementAndValue(psSDaset,"MIN", pszMin ); + CPLCreateXMLElementAndValue(psSDaset,"MAX", pszMax ); + CPLCreateXMLElementAndValue(psSDaset,"MEAN", pszMean ); + CPLCreateXMLElementAndValue(psSDaset,"MEDIAN", pszMedian ); + CPLCreateXMLElementAndValue(psSDaset,"MODEVALUE", pszMode ); + CPLCreateXMLElementAndValue(psSDaset,"STD", pszStdDev ); + + return true; + } + return false; +} + +// --------------------------------------------------------------------------- +// HasColorTable() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::HasColorMap( int nBand ) +{ + CPLXMLNode *psLayers; + + int n = 1; + + psLayers = CPLGetXMLNode( phMetadata, "layerInfo.subLayer" ); + + for( ; psLayers; psLayers = psLayers->psNext, n++ ) + { + if( n == nBand ) + { + if( CPLGetXMLNode( psLayers, "colorMap.colors" ) ) + { + return true; + } + } + } + + return false; +} + +// --------------------------------------------------------------------------- +// InitializeLayersNode() +// --------------------------------------------------------------------------- + +void GeoRasterWrapper::InitializeLayersNode() +{ + CPLXMLNode *pslInfo = CPLGetXMLNode( phMetadata, "layerInfo" ); + + int n = 1; + + for( n = 0 ; n < nRasterBands; n++ ) + { + CPLXMLNode *psSLayer = CPLGetXMLNode( pslInfo, "subLayer" ); + + if( psSLayer == NULL ) + { + psSLayer = CPLCreateXMLNode( pslInfo, CXT_Element, "subLayer" ); + + CPLCreateXMLElementAndValue( psSLayer, "layerNumber", + CPLSPrintf( "%d", n + 1 ) ); + + CPLCreateXMLElementAndValue( psSLayer, "layerDimensionOrdinate", + CPLSPrintf( "%d", n ) ); + + CPLCreateXMLElementAndValue( psSLayer, "layerID", "" ); + } + } +} + +// --------------------------------------------------------------------------- +// GetColorTable() +// --------------------------------------------------------------------------- + +void GeoRasterWrapper::GetColorMap( int nBand, GDALColorTable* poCT ) +{ + GDALColorEntry oEntry; + + CPLXMLNode* psLayers; + + int n = 1; + + psLayers = CPLGetXMLNode( phMetadata, "layerInfo.subLayer" ); + + for( ; psLayers; psLayers = psLayers->psNext, n++ ) + { + if( n != nBand ) + { + continue; + } + + CPLXMLNode* psColors = CPLGetXMLNode( psLayers, "colorMap.colors.cell" ); + + int iColor = 0; + + for( ; psColors; psColors = psColors->psNext ) + { + iColor = (short) atoi( CPLGetXMLValue( psColors, "value","0")); + oEntry.c1 = (short) atoi( CPLGetXMLValue( psColors, "red", "0")); + oEntry.c2 = (short) atoi( CPLGetXMLValue( psColors, "green","0")); + oEntry.c3 = (short) atoi( CPLGetXMLValue( psColors, "blue", "0")); + oEntry.c4 = (short) atoi( CPLGetXMLValue( psColors, "alpha","0")); + poCT->SetColorEntry( iColor, &oEntry ); + } + break; + } +} + +// --------------------------------------------------------------------------- +// SetColorTable() +// --------------------------------------------------------------------------- + +void GeoRasterWrapper::SetColorMap( int nBand, GDALColorTable* poCT ) +{ + InitializeLayersNode(); + + bFlushMetadata = true; + + GDALColorEntry oEntry; + + int n = 1; + + CPLXMLNode* phSubLayer = CPLGetXMLNode( phMetadata, "layerInfo.subLayer" ); + + for( n = 1 ; phSubLayer ; phSubLayer = phSubLayer->psNext, n++ ) + { + if( n != nBand ) + { + continue; + } + + CPLXMLNode* psCMap = CPLGetXMLNode( phSubLayer, "colorMap" ); + + if( psCMap != NULL ) + { + CPLRemoveXMLChild( phSubLayer, psCMap ); + CPLDestroyXMLNode( psCMap ); + } + + psCMap = CPLCreateXMLNode( phSubLayer, CXT_Element, "colorMap" ); + + CPLXMLNode* psColor = CPLCreateXMLNode( psCMap, CXT_Element, "colors" ); + + // ------------------------------------------------ + // Clean existing colors entry (RGB color table) + // ------------------------------------------------ + + if( psColor != NULL ) + { + CPLRemoveXMLChild( psCMap, psColor ); + CPLDestroyXMLNode( psColor ); + } + + psColor = CPLCreateXMLNode( psCMap, CXT_Element, "colors" ); + + int iColor = 0; + + int nCount = 0; + + switch( nCellSizeBits ) + { + case 1 : + nCount = 2; + break; + case 2 : + nCount = 4; + break; + case 4: + nCount = 16; + break; + default: + nCount = poCT->GetColorEntryCount(); + } + + + for( iColor = 0; iColor < nCount; iColor++ ) + { + poCT->GetColorEntryAsRGB( iColor, &oEntry ); + + CPLXMLNode* psCell = CPLCreateXMLNode( psColor, CXT_Element, "cell" ); + + CPLSetXMLValue( psCell, "#value", CPLSPrintf("%d", iColor) ); + CPLSetXMLValue( psCell, "#blue", CPLSPrintf("%d", oEntry.c3) ); + CPLSetXMLValue( psCell, "#red", CPLSPrintf("%d", oEntry.c1) ); + CPLSetXMLValue( psCell, "#green", CPLSPrintf("%d", oEntry.c2) ); + CPLSetXMLValue( psCell, "#alpha", CPLSPrintf("%d", oEntry.c4) ); + } + } +} + +// --------------------------------------------------------------------------- +// InitializeIO() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::InitializeIO( void ) +{ + bInitializeIO = true; + + // -------------------------------------------------------------------- + // Initialize Pyramid level details + // -------------------------------------------------------------------- + + pahLevels = (hLevelDetails*) CPLCalloc( sizeof(hLevelDetails), + nPyramidMaxLevel + 1 ); + + // -------------------------------------------------------------------- + // Calculate number and size of the blocks in level zero + // -------------------------------------------------------------------- + + nBlockCount = (unsigned long) ( nTotalColumnBlocks * nTotalRowBlocks * nTotalBandBlocks ); + nBlockBytes = (unsigned long) ( nColumnBlockSize * nRowBlockSize * nBandBlockSize * + nCellSizeBits / 8L ); + nGDALBlockBytes = (unsigned long) ( nColumnBlockSize * nRowBlockSize * nGDALCellBytes ); + + pahLevels[0].nColumnBlockSize = nColumnBlockSize; + pahLevels[0].nRowBlockSize = nRowBlockSize; + pahLevels[0].nTotalColumnBlocks = nTotalColumnBlocks; + pahLevels[0].nTotalRowBlocks = nTotalRowBlocks; + pahLevels[0].nBlockCount = nBlockCount; + pahLevels[0].nBlockBytes = nBlockBytes; + pahLevels[0].nGDALBlockBytes = nGDALBlockBytes; + pahLevels[0].nOffset = 0L; + + // -------------------------------------------------------------------- + // Calculate number and size of the blocks in Pyramid levels + // -------------------------------------------------------------------- + + int iLevel = 1; + + for( iLevel = 1; iLevel <= nPyramidMaxLevel; iLevel++ ) + { + int nRBS = nRowBlockSize; + int nCBS = nColumnBlockSize; + int nTCB = nTotalColumnBlocks; + int nTRB = nTotalRowBlocks; + + // -------------------------------------------------------------------- + // Calculate the actual size of a lower resolution block + // -------------------------------------------------------------------- + + double dfScale = pow( (double) 2.0, (double) iLevel ); + + int nXSize = (int) floor( (double) nRasterColumns / dfScale ); + int nYSize = (int) floor( (double) nRasterRows / dfScale ); + int nXBlock = (int) floor( (double) nCBS / 2.0 ); + int nYBlock = (int) floor( (double) nRBS / 2.0 ); + + if( nXSize <= nXBlock && nYSize <= nYBlock ) + { + // ------------------------------------------------------------ + // Calculate the size of the singe small blocks + // ------------------------------------------------------------ + + nCBS = nXSize; + nRBS = nYSize; + nTCB = 1; + nTRB = 1; + } + else + { + // ------------------------------------------------------------ + // Recalculate blocks quantity + // ------------------------------------------------------------ + + nTCB = (int) ceil( (double) nXSize / nCBS ); + nTRB = (int) ceil( (double) nYSize / nRBS ); + } + + // -------------------------------------------------------------------- + // Save level datails + // -------------------------------------------------------------------- + + pahLevels[iLevel].nColumnBlockSize = nCBS; + pahLevels[iLevel].nRowBlockSize = nRBS; + pahLevels[iLevel].nTotalColumnBlocks = nTCB; + pahLevels[iLevel].nTotalRowBlocks = nTRB; + pahLevels[iLevel].nBlockCount = (unsigned long ) ( nTCB * nTRB * nTotalBandBlocks ); + pahLevels[iLevel].nBlockBytes = (unsigned long ) ( nCBS * nRBS * nBandBlockSize * + nCellSizeBits / 8L ); + pahLevels[iLevel].nGDALBlockBytes = (unsigned long ) ( nCBS * nRBS * nGDALCellBytes ); + pahLevels[iLevel].nOffset = 0L; + } + + // -------------------------------------------------------------------- + // Calculate total row count and level's offsets + // -------------------------------------------------------------------- + + nBlockCount = 0L; + + for( iLevel = 0; iLevel <= nPyramidMaxLevel; iLevel++ ) + { + pahLevels[iLevel].nOffset = nBlockCount; + nBlockCount += pahLevels[iLevel].nBlockCount; + } + + // -------------------------------------------------------------------- + // Allocate buffer for one raster block + // -------------------------------------------------------------------- + + long nMaxBufferSize = MAX( nBlockBytes, nGDALBlockBytes ); + + pabyBlockBuf = (GByte*) VSIMalloc( sizeof(GByte) * nMaxBufferSize ); + + if ( pabyBlockBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "InitializeIO - Block Buffer error\n" + "Cannot allocate memory buffer of (%ld) bytes " + "Consider the use of *smaller* block size", + nMaxBufferSize ); + return false; + } + + // -------------------------------------------------------------------- + // Allocate buffer for one compressed raster block + // -------------------------------------------------------------------- + + if( bUpdate && ! EQUAL( sCompressionType.c_str(), "NONE") ) + { + pabyCompressBuf = (GByte*) VSIMalloc( sizeof(GByte) * nMaxBufferSize ); + + if ( pabyCompressBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "InitializeIO - Compression Buffer error\n" + "Cannot allocate memory buffer of (%ld) bytes " + "Consider the use of *smaller* block size", + nMaxBufferSize ); + return false; + } + } + + // -------------------------------------------------------------------- + // Allocate array of LOB Locators + // -------------------------------------------------------------------- + + pahLocator = (OCILobLocator**) VSIMalloc( sizeof(void*) * nBlockCount ); + + if ( pahLocator == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "InitializeIO - LobLocator Array error\n" + "Cannot allocate memory buffer of (%ld) bytes " + "Consider the use of *bigger* block size", + (sizeof(void*) * nBlockCount) ); + return false; + } + + // -------------------------------------------------------------------- + // Issue a statement to load the locators + // -------------------------------------------------------------------- + + const char* pszUpdate = ""; + + if( bUpdate ) + { + pszUpdate = CPLStrdup( "\nFOR UPDATE" ); + } + + poBlockStmt = poConnection->CreateStatement( CPLSPrintf( + "SELECT RASTERBLOCK\n" + "FROM %s%s\n" + "WHERE RASTERID = :1\n" + "ORDER BY\n" + " PYRAMIDLEVEL ASC,\n" + " BANDBLOCKNUMBER ASC,\n" + " ROWBLOCKNUMBER ASC,\n" + " COLUMNBLOCKNUMBER ASC%s", + sSchema.c_str(), + sDataTable.c_str(), + pszUpdate ) ); + + poBlockStmt->Bind( &nRasterId ); + poBlockStmt->Define( pahLocator, nBlockCount ); + + if( ! poBlockStmt->Execute( nBlockCount ) ) + { + return false; + } + + return true; +} + +// --------------------------------------------------------------------------- +// InitializeLevel() +// --------------------------------------------------------------------------- + +void GeoRasterWrapper::InitializeLevel( int nLevel ) +{ + nCurrentLevel = nLevel; + nColumnBlockSize = pahLevels[nLevel].nColumnBlockSize; + nRowBlockSize = pahLevels[nLevel].nRowBlockSize; + nTotalColumnBlocks = pahLevels[nLevel].nTotalColumnBlocks; + nTotalRowBlocks = pahLevels[nLevel].nTotalRowBlocks; + nBlockBytes = pahLevels[nLevel].nBlockBytes; + nGDALBlockBytes = pahLevels[nLevel].nGDALBlockBytes; + nLevelOffset = pahLevels[nLevel].nOffset; +} + +// --------------------------------------------------------------------------- +// GetDataBlock() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::GetDataBlock( int nBand, + int nLevel, + int nXOffset, + int nYOffset, + void* pData ) +{ + if( ! bInitializeIO ) + { + if( InitializeIO() == false ) + { + return false; + } + } + + if( nCurrentLevel != nLevel ) + { + InitializeLevel( nLevel ); + } + + long nBlock = GetBlockNumber( nBand, nXOffset, nYOffset ); + + CPLDebug( "Read ", + "Block = %4ld Size = %7ld Band = %d Level = %d X = %d Y = %d", + nBlock, nBlockBytes, nBand, nLevel, nXOffset, nYOffset ); + + if( nCacheBlockId != nBlock ) + { + if ( bFlushBlock ) + { + if( ! FlushBlock( nCacheBlockId ) ) + { + return false; + } + } + + nCacheBlockId = nBlock; + + unsigned long nBytesRead = 0; + + nBytesRead = poBlockStmt->ReadBlob( pahLocator[nBlock], + pabyBlockBuf, + nBlockBytes ); + + CPLDebug( "Load ", "Block = %4ld Size = %7ld", nBlock, nBlockBytes ); + + if( nBytesRead == 0 ) + { + memset( pData, 0, nGDALBlockBytes ); + return true; + } + + if( nBytesRead < nBlockBytes && + EQUAL( sCompressionType.c_str(), "NONE") ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "BLOB size (%ld) is smaller than expected (%ld) !", + nBytesRead, nBlockBytes ); + memset( pData, 0, nGDALBlockBytes ); + return true; + } + + if( nBytesRead > nBlockBytes ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "BLOB size (%ld) is bigger than expected (%ld) !", + nBytesRead, nBlockBytes ); + memset( pData, 0, nGDALBlockBytes ); + return true; + } + +#ifndef CPL_MSB + if( nCellSizeBits > 8 ) + { + int nWordSize = nCellSizeBits / 8; + int nWordCount = nColumnBlockSize * nRowBlockSize * nBandBlockSize; + GDALSwapWords( pabyBlockBuf, nWordSize, nWordCount, nWordSize ); + } +#endif + + // ---------------------------------------------------------------- + // Uncompress + // ---------------------------------------------------------------- + + if( EQUALN( sCompressionType.c_str(), "JPEG", 4 ) ) + { + UncompressJpeg( nBytesRead ); + } + else if ( EQUAL( sCompressionType.c_str(), "DEFLATE" ) ) + { + UncompressDeflate( nBytesRead ); + } + + // ---------------------------------------------------------------- + // Unpack NBits + // ---------------------------------------------------------------- + + if( nCellSizeBits < 8 || nLevel == DEFAULT_BMP_MASK ) + { + UnpackNBits( pabyBlockBuf ); + } + } + + // -------------------------------------------------------------------- + // Uninterleaving, extract band from block buffer + // -------------------------------------------------------------------- + + int nStart = ( nBand - 1 ) % nBandBlockSize; + + if( EQUAL( sInterleaving.c_str(), "BSQ" ) || nBandBlockSize == 1 ) + { + nStart *= nGDALBlockBytes; + + memcpy( pData, &pabyBlockBuf[nStart], nGDALBlockBytes ); + } + else + { + int nIncr = nBandBlockSize * nGDALCellBytes; + int nSize = nGDALCellBytes; + + if( EQUAL( sInterleaving.c_str(), "BIL" ) ) + { + nStart *= nColumnBlockSize; + nIncr *= nColumnBlockSize; + nSize *= nColumnBlockSize; + } + + GByte* pabyData = (GByte*) pData; + + unsigned long ii = 0; + unsigned long jj = nStart * nGDALCellBytes; + + for( ii = 0; ii < nGDALBlockBytes; ii += nSize, jj += nIncr ) + { + memcpy( &pabyData[ii], &pabyBlockBuf[jj], nSize ); + } + } + + return true; +} + +// --------------------------------------------------------------------------- +// SetDataBlock() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::SetDataBlock( int nBand, + int nLevel, + int nXOffset, + int nYOffset, + void* pData ) +{ +#ifndef CPL_MSB + if( nCellSizeBits > 8 ) + { + int nWordSize = nCellSizeBits / 8; + int nWordCount = nColumnBlockSize * nRowBlockSize; + GDALSwapWords( pData, nWordSize, nWordCount, nWordSize ); + } +#endif + + if( ! bInitializeIO ) + { + if( InitializeIO() == false ) + { + return false; + } + } + + if( nCurrentLevel != nLevel ) + { + InitializeLevel( nLevel ); + } + + long nBlock = GetBlockNumber( nBand, nXOffset, nYOffset ); + + CPLDebug( "Write ", + "Block = %4ld Size = %7ld Band = %d Level = %d X = %d Y = %d", + nBlock, nBlockBytes, nBand, nLevel, nXOffset, nYOffset ); + + // -------------------------------------------------------------------- + // Flush previous block + // -------------------------------------------------------------------- + + if( nCacheBlockId != nBlock && bFlushBlock ) + { + if( ! FlushBlock( nCacheBlockId ) ) + { + return false; + } + } + + // -------------------------------------------------------------------- + // Re-load interleaved block + // -------------------------------------------------------------------- + + if( nBandBlockSize > 1 && bWriteOnly == false && nCacheBlockId != nBlock ) + { + nCacheBlockId = nBlock; + + unsigned long nBytesRead = 0; + + nBytesRead = poBlockStmt->ReadBlob( pahLocator[nBlock], + pabyBlockBuf, + nBlockBytes ); + + CPLDebug( "Reload", "Block = %4ld Size = %7ld", nBlock, nBlockBytes ); + + if( nBytesRead == 0 ) + { + memset( pabyBlockBuf, 0, nBlockBytes ); + } + else + { + // ------------------------------------------------------------ + // Uncompress + // ------------------------------------------------------------ + + if( EQUALN( sCompressionType.c_str(), "JPEG", 4 ) ) + { + UncompressJpeg( nBytesRead ); + } + else if ( EQUAL( sCompressionType.c_str(), "DEFLATE" ) ) + { + UncompressDeflate( nBytesRead ); + } + + // ------------------------------------------------------------ + // Unpack NBits + // ------------------------------------------------------------ + + if( nCellSizeBits < 8 || nLevel == DEFAULT_BMP_MASK ) + { + UnpackNBits( pabyBlockBuf ); + } + } + } + + GByte *pabyInBuf = (GByte *) pData; + + // -------------------------------------------------------------------- + // Interleave + // -------------------------------------------------------------------- + + int nStart = ( nBand - 1 ) % nBandBlockSize; + + if( EQUAL( sInterleaving.c_str(), "BSQ" ) || nBandBlockSize == 1 ) + { + nStart *= nGDALBlockBytes; + + memcpy( &pabyBlockBuf[nStart], pabyInBuf, nGDALBlockBytes ); + } + else + { + int nIncr = nBandBlockSize * nGDALCellBytes; + int nSize = nGDALCellBytes; + + if( EQUAL( sInterleaving.c_str(), "BIL" ) ) + { + nStart *= nColumnBlockSize; + nIncr *= nColumnBlockSize; + nSize *= nColumnBlockSize; + } + + unsigned long ii = 0; + unsigned long jj = nStart * nGDALCellBytes; + + for( ii = 0; ii < nGDALBlockBytes; ii += nSize, jj += nIncr ) + { + memcpy( &pabyBlockBuf[jj], &pabyInBuf[ii], nSize ); + } + } + + // -------------------------------------------------------------------- + // Flag the flush block + // -------------------------------------------------------------------- + + nCacheBlockId = nBlock; + bFlushBlock = true; + nFlushBlockSize = nBlockBytes; + + return true; +} + +// --------------------------------------------------------------------------- +// FlushBlock() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::FlushBlock( long nCacheBlock ) +{ + GByte* pabyFlushBuffer = (GByte *) pabyBlockBuf; + + // -------------------------------------------------------------------- + // Pack bits ( inside pabyOutBuf ) + // -------------------------------------------------------------------- + + if( nCellSizeBits < 8 || nCurrentLevel == DEFAULT_BMP_MASK ) + { + PackNBits( pabyFlushBuffer ); + } + + // -------------------------------------------------------------------- + // Compress ( from pabyBlockBuf to pabyBlockBuf2 ) + // -------------------------------------------------------------------- + + if( ! EQUAL( sCompressionType.c_str(), "NONE" ) ) + { + if( EQUALN( sCompressionType.c_str(), "JPEG", 4 ) ) + { + nFlushBlockSize = CompressJpeg(); + } + else if ( EQUAL( sCompressionType.c_str(), "DEFLATE" ) ) + { + nFlushBlockSize = CompressDeflate(); + } + + pabyFlushBuffer = pabyCompressBuf; + } + + // -------------------------------------------------------------------- + // Write BLOB + // -------------------------------------------------------------------- + + CPLDebug( "Flush ", "Block = %4ld Size = %7ld", nCacheBlock, + nFlushBlockSize ); + + if( ! poBlockStmt->WriteBlob( pahLocator[nCacheBlock], + pabyFlushBuffer, + nFlushBlockSize ) ) + { + return false; + } + + bFlushBlock = false; + bFlushMetadata = true; + nFlushBlockSize = nBlockBytes; + + return true; +} + +// --------------------------------------------------------------------------- +// LoadNoDataValues() +// --------------------------------------------------------------------------- + +CPLList* AddToNoDataList( CPLXMLNode* phNode, int nNumber, CPLList* poList ) +{ + CPLXMLNode* psChild = phNode->psChild; + + const char* pszMin = NULL; + const char* pszMax = NULL; + + for( ; psChild ; psChild = psChild->psNext ) + { + if( EQUAL( psChild->pszValue, "value" ) ) + { + pszMin = CPLGetXMLValue( psChild, NULL, "NONE" ); + pszMax = pszMin; + } + else if ( EQUAL( psChild->pszValue, "range" ) ) + { + pszMin = CPLGetXMLValue( psChild, "min", "NONE" ); + pszMax = CPLGetXMLValue( psChild, "max", "NONE" ); + } + else + { + continue; + } + + hNoDataItem* poItem = (hNoDataItem*) CPLMalloc( sizeof( hNoDataItem ) ); + + poItem->nBand = nNumber; + poItem->dfLower = CPLAtof( pszMin ); + poItem->dfUpper = CPLAtof( pszMax ); + + poList = CPLListAppend( poList, poItem ); + } + + return poList; +} + +void GeoRasterWrapper::LoadNoDataValues( void ) +{ + CPLListDestroy( psNoDataList ); + + CPLXMLNode* phLayerInfo = CPLGetXMLNode( phMetadata, "layerInfo" ); + + if( phLayerInfo == NULL ) + { + return; + } + + // ------------------------------------------------------------------- + // Load NoDatas from list of values and list of value ranges + // ------------------------------------------------------------------- + + CPLXMLNode* phObjNoData = CPLGetXMLNode( phLayerInfo, "objectLayer.NODATA" ); + + for( ; phObjNoData ; phObjNoData = phObjNoData->psNext ) + { + psNoDataList = AddToNoDataList( phObjNoData, 0, psNoDataList ); + } + + CPLXMLNode* phSubLayer = CPLGetXMLNode( phLayerInfo, "subLayer" ); + + for( ; phSubLayer ; phSubLayer = phSubLayer->psNext ) + { + int nNumber = atoi( CPLGetXMLValue( phSubLayer, "layerNumber", "-1") ); + + CPLXMLNode* phSubNoData = CPLGetXMLNode( phSubLayer, "NODATA" ); + + if( phSubNoData ) + { + psNoDataList = AddToNoDataList( phSubNoData, nNumber, psNoDataList ); + } + } +} + +// --------------------------------------------------------------------------- +// GetRPC() +// --------------------------------------------------------------------------- + +/* This is the order for storing 20 coeffients in GeoRaster Metadata */ + +static const int anOrder[] = { + 1, 2, 8, 12, 3, 5, 15, 9, 13, 16, 4, 6, 18, 7, 11, 19, 10, 14, 17, 20 +}; + +void GeoRasterWrapper::GetRPC() +{ + int i; + + CPLXMLNode* phSRSInfo = CPLGetXMLNode( phMetadata, + "spatialReferenceInfo" ); + + if( phSRSInfo == NULL ) + { + return; + } + + const char* pszModelType = CPLGetXMLValue( phMetadata, + "spatialReferenceInfo.modelType", "None" ); + + if( EQUAL( pszModelType, "FunctionalFitting" ) == false ) + { + return; + } + + CPLXMLNode* phPolyModel = CPLGetXMLNode( phSRSInfo, "polynomialModel" ); + + if ( phPolyModel == NULL ) + { + return; + } + + // pPolynomial refers to LINE_NUM + + CPLXMLNode* phPolynomial = CPLGetXMLNode( phPolyModel, "pPolynomial" ); + + if ( phPolynomial == NULL ) + { + return; + } + + int nNumCoeff = atoi( CPLGetXMLValue( phPolynomial, "nCoefficients", "0" ) ); + + if ( nNumCoeff != 20 ) + { + return; + } + + const char* pszPolyCoeff = CPLGetXMLValue( phPolynomial, "polynomialCoefficients", "None" ); + + if ( EQUAL( pszPolyCoeff, "None" ) ) + { + return; + } + + char** papszCeoff = CSLTokenizeString2( pszPolyCoeff, " ", CSLT_STRIPLEADSPACES ); + + if( CSLCount( papszCeoff ) != 20 ) + { + return; + } + + phRPC = (GDALRPCInfo*) VSIMalloc( sizeof(GDALRPCInfo) ); + + phRPC->dfLINE_OFF = CPLAtof( CPLGetXMLValue( phPolyModel, "rowOff", "0" ) ); + phRPC->dfSAMP_OFF = CPLAtof( CPLGetXMLValue( phPolyModel, "columnOff", "0" ) ); + phRPC->dfLONG_OFF = CPLAtof( CPLGetXMLValue( phPolyModel, "xOff", "0" ) ); + phRPC->dfLAT_OFF = CPLAtof( CPLGetXMLValue( phPolyModel, "yOff", "0" ) ); + phRPC->dfHEIGHT_OFF = CPLAtof( CPLGetXMLValue( phPolyModel, "zOff", "0" ) ); + + phRPC->dfLINE_SCALE = CPLAtof( CPLGetXMLValue( phPolyModel, "rowScale", "0" ) ); + phRPC->dfSAMP_SCALE = CPLAtof( CPLGetXMLValue( phPolyModel, "columnScale", "0" ) ); + phRPC->dfLONG_SCALE = CPLAtof( CPLGetXMLValue( phPolyModel, "xScale", "0" ) ); + phRPC->dfLAT_SCALE = CPLAtof( CPLGetXMLValue( phPolyModel, "yScale", "0" ) ); + phRPC->dfHEIGHT_SCALE = CPLAtof( CPLGetXMLValue( phPolyModel, "zScale", "0" ) ); + + for( i = 0; i < 20; i++ ) + { + phRPC->adfLINE_NUM_COEFF[anOrder[i] - 1] = CPLAtof( papszCeoff[i] ); + } + + // qPolynomial refers to LINE_DEN + + phPolynomial = CPLGetXMLNode( phPolyModel, "qPolynomial" ); + + if ( phPolynomial == NULL ) + { + CPLFree( phRPC ); + phRPC = NULL; + return; + } + + pszPolyCoeff = CPLGetXMLValue( phPolynomial, "polynomialCoefficients", "None" ); + + if ( EQUAL( pszPolyCoeff, "None" ) ) + { + CPLFree( phRPC ); + phRPC = NULL; + return; + } + + papszCeoff = CSLTokenizeString2( pszPolyCoeff, " ", CSLT_STRIPLEADSPACES ); + + if( CSLCount( papszCeoff ) != 20 ) + { + CPLFree( phRPC ); + phRPC = NULL; + return; + } + + for( i = 0; i < 20; i++ ) + { + phRPC->adfLINE_DEN_COEFF[anOrder[i] - 1] = CPLAtof( papszCeoff[i] ); + } + + // rPolynomial refers to SAMP_NUM + + phPolynomial = CPLGetXMLNode( phPolyModel, "rPolynomial" ); + + if ( phPolynomial == NULL ) + { + CPLFree( phRPC ); + phRPC = NULL; + return; + } + + pszPolyCoeff = CPLGetXMLValue( phPolynomial, "polynomialCoefficients", "None" ); + + if ( EQUAL( pszPolyCoeff, "None" ) ) + { + CPLFree( phRPC ); + phRPC = NULL; + return; + } + + papszCeoff = CSLTokenizeString2( pszPolyCoeff, " ", CSLT_STRIPLEADSPACES ); + + if( CSLCount( papszCeoff ) != 20 ) + { + CPLFree( phRPC ); + phRPC = NULL; + return; + } + + for( i = 0; i < 20; i++ ) + { + phRPC->adfSAMP_NUM_COEFF[anOrder[i] - 1] = CPLAtof( papszCeoff[i] ); + } + + // sPolynomial refers to SAMP_DEN + + phPolynomial = CPLGetXMLNode( phPolyModel, "sPolynomial" ); + + if ( phPolynomial == NULL ) + { + CPLFree( phRPC ); + phRPC = NULL; + return; + } + + pszPolyCoeff = CPLGetXMLValue( phPolynomial, "polynomialCoefficients", "None" ); + + if ( EQUAL( pszPolyCoeff, "None" ) ) + { + CPLFree( phRPC ); + phRPC = NULL; + return; + } + + papszCeoff = CSLTokenizeString2( pszPolyCoeff, " ", CSLT_STRIPLEADSPACES ); + + if( CSLCount( papszCeoff ) != 20 ) + { + CPLFree( phRPC ); + phRPC = NULL; + return; + } + + for( i = 0; i < 20; i++ ) + { + phRPC->adfSAMP_DEN_COEFF[anOrder[i] - 1] = CPLAtof( papszCeoff[i] ); + } +} + +// --------------------------------------------------------------------------- +// SetRPC() +// --------------------------------------------------------------------------- + +void GeoRasterWrapper::SetRPC() +{ + // ------------------------------------------------------------------- + // Remove "layerInfo" tree + // ------------------------------------------------------------------- + + CPLXMLNode* phLayerInfo = CPLGetXMLNode( phMetadata, "layerInfo" ); + CPLXMLNode* phClone = NULL; + + if( phLayerInfo ) + { + phClone = CPLCloneXMLTree( phLayerInfo ); + CPLRemoveXMLChild( phMetadata, phLayerInfo ); + } + + // ------------------------------------------------------------------- + // Start loading the RPC to "spatialReferenceInfo" tree + // ------------------------------------------------------------------- + + int i = 0; + CPLString osField, osMultiField; + CPLXMLNode* phPolynomial = NULL; + + CPLXMLNode* phSRSInfo = CPLGetXMLNode( phMetadata, + "spatialReferenceInfo" ); + + if( ! phSRSInfo ) + { + phSRSInfo = CPLCreateXMLNode( phMetadata, CXT_Element, + "spatialReferenceInfo" ); + } + else + { + CPLXMLNode* phNode = NULL; + + phNode = CPLGetXMLNode( phSRSInfo, "isReferenced" ); + if( phNode ) + { + CPLRemoveXMLChild( phSRSInfo, phNode ); + } + + phNode = CPLGetXMLNode( phSRSInfo, "SRID" ); + if( phNode ) + { + CPLRemoveXMLChild( phSRSInfo, phNode ); + } + + phNode = CPLGetXMLNode( phSRSInfo, "modelCoordinateLocation" ); + if( phNode ) + { + CPLRemoveXMLChild( phSRSInfo, phNode ); + } + + phNode = CPLGetXMLNode( phSRSInfo, "modelType" ); + if( phNode ) + { + CPLRemoveXMLChild( phSRSInfo, phNode ); + } + + phNode = CPLGetXMLNode( phSRSInfo, "polynomialModel" ); + if( phNode ) + { + CPLRemoveXMLChild( phSRSInfo, phNode ); + } + } + + CPLCreateXMLElementAndValue( phSRSInfo, "isReferenced", "true" ); + CPLCreateXMLElementAndValue( phSRSInfo, "SRID", "4327" ); + CPLCreateXMLElementAndValue( phSRSInfo, "modelCoordinateLocation", + "CENTER" ); + CPLCreateXMLElementAndValue( phSRSInfo, "modelType", "FunctionalFitting" ); + CPLSetXMLValue( phSRSInfo, "polynomialModel.#rowOff", + CPLSPrintf( "%.15g", phRPC->dfLINE_OFF ) ); + CPLSetXMLValue( phSRSInfo, "polynomialModel.#columnOff", + CPLSPrintf( "%.15g", phRPC->dfSAMP_OFF ) ); + CPLSetXMLValue( phSRSInfo, "polynomialModel.#xOff", + CPLSPrintf( "%.15g", phRPC->dfLONG_OFF ) ); + CPLSetXMLValue( phSRSInfo, "polynomialModel.#yOff", + CPLSPrintf( "%.15g", phRPC->dfLAT_OFF ) ); + CPLSetXMLValue( phSRSInfo, "polynomialModel.#zOff", + CPLSPrintf( "%.15g", phRPC->dfHEIGHT_OFF ) ); + CPLSetXMLValue( phSRSInfo, "polynomialModel.#rowScale", + CPLSPrintf( "%.15g", phRPC->dfLINE_SCALE ) ); + CPLSetXMLValue( phSRSInfo, "polynomialModel.#columnScale", + CPLSPrintf( "%.15g", phRPC->dfSAMP_SCALE ) ); + CPLSetXMLValue( phSRSInfo, "polynomialModel.#xScale", + CPLSPrintf( "%.15g", phRPC->dfLONG_SCALE ) ); + CPLSetXMLValue( phSRSInfo, "polynomialModel.#yScale", + CPLSPrintf( "%.15g", phRPC->dfLAT_SCALE ) ); + CPLSetXMLValue( phSRSInfo, "polynomialModel.#zScale", + CPLSPrintf( "%.15g", phRPC->dfHEIGHT_SCALE ) ); + CPLXMLNode* phPloyModel = CPLGetXMLNode( phSRSInfo, "polynomialModel" ); + + // pPolynomial refers to LINE_NUM + + CPLSetXMLValue( phPloyModel, "pPolynomial.#pType", "1" ); + CPLSetXMLValue( phPloyModel, "pPolynomial.#nVars", "3" ); + CPLSetXMLValue( phPloyModel, "pPolynomial.#order", "3" ); + CPLSetXMLValue( phPloyModel, "pPolynomial.#nCoefficients", "20" ); + for( i = 0; i < 20; i++ ) + { + osField.Printf( "%.15g", phRPC->adfLINE_NUM_COEFF[anOrder[i] - 1] ); + if( i > 0 ) + osMultiField += " "; + else + osMultiField = ""; + osMultiField += osField; + } + phPolynomial = CPLGetXMLNode( phPloyModel, "pPolynomial" ); + CPLCreateXMLElementAndValue( phPolynomial, "polynomialCoefficients", + osMultiField ); + + // qPolynomial refers to LINE_DEN + + CPLSetXMLValue( phPloyModel, "qPolynomial.#pType", "1" ); + CPLSetXMLValue( phPloyModel, "qPolynomial.#nVars", "3" ); + CPLSetXMLValue( phPloyModel, "qPolynomial.#order", "3" ); + CPLSetXMLValue( phPloyModel, "qPolynomial.#nCoefficients", "20" ); + for( i = 0; i < 20; i++ ) + { + osField.Printf( "%.15g", phRPC->adfLINE_DEN_COEFF[anOrder[i] - 1] ); + if( i > 0 ) + osMultiField += " "; + else + osMultiField = ""; + osMultiField += osField; + } + phPolynomial = CPLGetXMLNode( phPloyModel, "qPolynomial" ); + CPLCreateXMLElementAndValue( phPolynomial, "polynomialCoefficients", + osMultiField ); + + // rPolynomial refers to SAMP_NUM + + CPLSetXMLValue( phPloyModel, "rPolynomial.#pType", "1" ); + CPLSetXMLValue( phPloyModel, "rPolynomial.#nVars", "3" ); + CPLSetXMLValue( phPloyModel, "rPolynomial.#order", "3" ); + CPLSetXMLValue( phPloyModel, "rPolynomial.#nCoefficients", "20" ); + for( i = 0; i < 20; i++ ) + { + osField.Printf( "%.15g", phRPC->adfSAMP_NUM_COEFF[anOrder[i] - 1] ); + if( i > 0 ) + osMultiField += " "; + else + osMultiField = ""; + osMultiField += osField; + } + phPolynomial = CPLGetXMLNode( phPloyModel, "rPolynomial" ); + CPLCreateXMLElementAndValue( phPolynomial, "polynomialCoefficients", + osMultiField ); + + // sPolynomial refers to SAMP_DEN + + CPLSetXMLValue( phPloyModel, "sPolynomial.#pType", "1" ); + CPLSetXMLValue( phPloyModel, "sPolynomial.#nVars", "3" ); + CPLSetXMLValue( phPloyModel, "sPolynomial.#order", "3" ); + CPLSetXMLValue( phPloyModel, "sPolynomial.#nCoefficients", "20" ); + for( i = 0; i < 20; i++ ) + { + osField.Printf( "%.15g", phRPC->adfSAMP_DEN_COEFF[anOrder[i] - 1] ); + if( i > 0 ) + osMultiField += " "; + else + osMultiField = ""; + osMultiField += osField; + } + phPolynomial = CPLGetXMLNode( phPloyModel, "sPolynomial" ); + CPLCreateXMLElementAndValue( phPolynomial, "polynomialCoefficients", + osMultiField ); + + // ------------------------------------------------------------------- + // Add "layerInfo" tree back + // ------------------------------------------------------------------- + + CPLAddXMLChild( phMetadata, phClone ); +} + +// --------------------------------------------------------------------------- +// GetNoData() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::GetNoData( int nLayer, double* pdfNoDataValue ) +{ + if( psNoDataList == NULL || CPLListCount( psNoDataList ) == 0 ) + { + return false; + } + + // ------------------------------------------------------------------- + // Get only single value NoData, no list of values or value ranges + // ------------------------------------------------------------------- + + int nCount = 0; + double dfValue = 0.0; + + CPLList* psList = NULL; + + // ------------------------------------------------------------------- + // Process Object Layer values + // ------------------------------------------------------------------- + + for( psList = psNoDataList; psList ; psList = psList->psNext ) + { + hNoDataItem* phItem = (hNoDataItem*) psList->pData; + + if( phItem->nBand == 0 ) + { + if( phItem->dfLower == phItem->dfUpper ) + { + dfValue = phItem->dfLower; + nCount++; + } + else + { + return false; // value range + } + } + } + + // ------------------------------------------------------------------- + // Values from the Object Layer override values from the layers + // ------------------------------------------------------------------- + + if( nCount == 1 ) + { + *pdfNoDataValue = dfValue; + return true; + } + + // ------------------------------------------------------------------- + // Process SubLayer values + // ------------------------------------------------------------------- + + for( psList = psNoDataList; psList ; psList = psList->psNext ) + { + hNoDataItem* phItem = (hNoDataItem*) psList->pData; + + if( phItem->nBand == nLayer ) + { + if( phItem->dfLower == phItem->dfUpper ) + { + dfValue = phItem->dfLower; + nCount++; + } + else + { + return false; // value range + } + } + } + + if( nCount == 1 ) + { + *pdfNoDataValue = dfValue; + return true; + } + + return false; +} + +// --------------------------------------------------------------------------- +// SetNoDataValue() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::SetNoData( int nLayer, const char* pszValue ) +{ + // ------------------------------------------------------------ + // Set one NoData per dataset on "rasterInfo" section (10g) + // ------------------------------------------------------------ + + if( poConnection->GetVersion() < 11 ) + { + CPLXMLNode* psRInfo = CPLGetXMLNode( phMetadata, "rasterInfo" ); + CPLXMLNode* psNData = CPLSearchXMLNode( psRInfo, "NODATA" ); + + if( psNData == NULL ) + { + psNData = CPLCreateXMLNode( NULL, CXT_Element, "NODATA" ); + + CPLXMLNode* psCDepth = CPLGetXMLNode( psRInfo, "cellDepth" ); + + CPLXMLNode* psPointer = psCDepth->psNext; + psCDepth->psNext = psNData; + psNData->psNext = psPointer; + } + + CPLSetXMLValue( psRInfo, "NODATA", pszValue ); + bFlushMetadata = true; + return true; + } + + // ------------------------------------------------------------ + // Add NoData for all bands (layer=0) or for a specific band + // ------------------------------------------------------------ + + char szRDT[OWCODE]; + char szNoData[OWTEXT]; + + strcpy( szRDT, sDataTable.c_str() ); + strcpy( szNoData, pszValue ); + + int nRID = nRasterId; + + // ------------------------------------------------------------ + // Write the in memory XML metada to avoid lossing other changes + // ------------------------------------------------------------ + + char* pszMetadata = CPLSerializeXMLTree( phMetadata ); + + if( pszMetadata == NULL ) + { + return false; + } + + OCILobLocator* phLocatorR = NULL; + OCILobLocator* phLocatorW = NULL; + + OWStatement* poStmt = poConnection->CreateStatement( CPLSPrintf( + "DECLARE\n" + " GR1 sdo_georaster;\n" + "BEGIN\n" + " SELECT %s INTO GR1 FROM %s%s T WHERE %s FOR UPDATE;\n" + "\n" + " GR1.metadata := sys.xmltype.createxml(:1);\n" + "\n" + " SDO_GEOR.addNODATA( GR1, :2, :3 );\n" + "\n" + " UPDATE %s%s T SET %s = GR1 WHERE %s;\n" + "\n" + " EXECUTE IMMEDIATE\n" + " 'SELECT T.%s.METADATA.getClobVal() FROM %s%s T \n" + " WHERE T.%s.RASTERDATATABLE = UPPER(:1)\n" + " AND T.%s.RASTERID = :2'\n" + " INTO :metadata USING :rdt, :rid;\n" + "\n" + " COMMIT;\n" + "END;", + sColumn.c_str(), sSchema.c_str(), sTable.c_str(), sWhere.c_str(), + sSchema.c_str(), sTable.c_str(), sColumn.c_str(), sWhere.c_str(), + sColumn.c_str(), sSchema.c_str(), sTable.c_str(), + sColumn.c_str(), + sColumn.c_str() ) ); + + poStmt->WriteCLob( &phLocatorW, pszMetadata ); + + poStmt->Bind( &phLocatorW ); + poStmt->Bind( &nLayer ); + poStmt->Bind( szNoData ); + poStmt->BindName( ":metadata", &phLocatorR ); + poStmt->BindName( ":rdt", szRDT ); + poStmt->BindName( ":rid", &nRID ); + + CPLFree( pszMetadata ); + + if( ! poStmt->Execute() ) + { + OCIDescriptorFree( phLocatorR, OCI_DTYPE_LOB ); + OCIDescriptorFree( phLocatorW, OCI_DTYPE_LOB ); + delete poStmt; + return false; + } + + OCIDescriptorFree( phLocatorW, OCI_DTYPE_LOB ); + + // ------------------------------------------------------------ + // Read the XML metadata from db to memory with nodata updates + // ------------------------------------------------------------ + + char* pszXML = poStmt->ReadCLob( phLocatorR ); + + if( pszXML ) + { + CPLDestroyXMLNode( phMetadata ); + phMetadata = CPLParseXMLString( pszXML ); + CPLFree( pszXML ); + } + + OCIDescriptorFree( phLocatorR, OCI_DTYPE_LOB ); + + bFlushMetadata = true; + delete poStmt; + return false; +} + +// --------------------------------------------------------------------------- +// SetVAT() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::SetVAT( int nBand, const char* pszName ) +{ + InitializeLayersNode(); + + bFlushMetadata = true; + + int n = 1; + + CPLXMLNode* psLayers = CPLGetXMLNode( phMetadata, "layerInfo.subLayer" ); + + for( ; psLayers; psLayers = psLayers->psNext, n++ ) + { + if( n != nBand ) + { + continue; + } + + CPLXMLNode* psVAT = CPLGetXMLNode( psLayers, "vatTableName" ); + + if( psVAT != NULL ) + { + CPLRemoveXMLChild( psLayers, psVAT ); + CPLDestroyXMLNode( psVAT ); + } + + CPLCreateXMLElementAndValue(psLayers, "vatTableName", pszName ); + + // ------------------------------------------------------------ + // To be updated at Flush Metadata in SDO_GEOR.setVAT() + // ------------------------------------------------------------ + + sValueAttributeTab = pszName; + + return true; + } + + return false; +} + +// --------------------------------------------------------------------------- +// GetVAT() +// --------------------------------------------------------------------------- + +char* GeoRasterWrapper::GetVAT( int nBand ) +{ + CPLXMLNode* psLayers = CPLGetXMLNode( phMetadata, "layerInfo.subLayer" ); + + if( psLayers == NULL ) + { + return NULL; + } + + char* pszTablename = NULL; + + int n = 1; + + for( ; psLayers; psLayers = psLayers->psNext, n++ ) + { + if( n != nBand ) + { + continue; + } + + CPLXMLNode* psVAT = CPLGetXMLNode( psLayers, "vatTableName" ); + + if( psVAT != NULL ) + { + pszTablename = CPLStrdup( + CPLGetXMLValue( psLayers, "vatTableName", "" ) ); + } + + break; + } + + return pszTablename; +} + +// --------------------------------------------------------------------------- +// FlushMetadata() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::FlushMetadata() +{ + if( bFlushBlock ) + { + FlushBlock( nCacheBlockId ); + } + + if( ! bFlushMetadata ) + { + return true; + } + + bFlushMetadata = false; + + // -------------------------------------------------------------------- + // Change the isBlank setting left by SDO_GEOR.createBlank() to 'false' + // -------------------------------------------------------------------- + + CPLXMLNode* psOInfo = CPLGetXMLNode( phMetadata, "objectInfo" ); + CPLXMLNode* psNode = NULL; + + CPLSetXMLValue( psOInfo, "isBlank", "false" ); + + psNode = CPLGetXMLNode( psOInfo, "blankCellValue" ); + + if( psNode != NULL ) + { + CPLRemoveXMLChild( psOInfo, psNode ); + CPLDestroyXMLNode( psNode ); + } + + const char* pszRed = "1"; + const char* pszGreen = "1"; + const char* pszBlue = "1"; + + if( ( nRasterBands > 2 ) && + ( ! HasColorMap( 1 ) ) && + ( ! HasColorMap( 2 ) ) && + ( ! HasColorMap( 3 ) ) ) + { + pszRed = "1"; + pszGreen = "2"; + pszBlue = "3"; + } + + psNode = CPLGetXMLNode( psOInfo, "defaultRed" ); + if( psNode ) + { + CPLRemoveXMLChild( psOInfo, psNode ); + CPLDestroyXMLNode( psNode ); + } + CPLCreateXMLElementAndValue( psOInfo, "defaultRed", pszRed ); + + psNode = CPLGetXMLNode( psOInfo, "defaultGreen" ); + if( psNode ) + { + CPLRemoveXMLChild( psOInfo, psNode ); + CPLDestroyXMLNode( psNode ); + } + CPLCreateXMLElementAndValue( psOInfo, "defaultGreen", pszGreen ); + + psNode = CPLGetXMLNode( psOInfo, "defaultBlue" ); + if( psNode ) + { + CPLRemoveXMLChild( psOInfo, psNode ); + CPLDestroyXMLNode( psNode ); + } + CPLCreateXMLElementAndValue( psOInfo, "defaultBlue", pszBlue ); + + // -------------------------------------------------------------------- + // Set compression + // -------------------------------------------------------------------- + + psNode = CPLGetXMLNode( phMetadata, "rasterInfo.compression" ); + + if( psNode ) + { + CPLSetXMLValue( psNode, "type", sCompressionType.c_str() ); + + if( EQUALN( sCompressionType.c_str(), "JPEG", 4 ) ) + { + CPLSetXMLValue( psNode, "quality", + CPLSPrintf( "%d", nCompressQuality ) ); + } + } + + // -------------------------------------------------------------------- + // Update BitmapMask info + // -------------------------------------------------------------------- + + if( bHasBitmapMask ) + { + CPLXMLNode* psLayers = CPLGetXMLNode( phMetadata, "layerInfo" ); + + if( psLayers ) + { + CPLCreateXMLElementAndValue( psLayers, "bitmapMask", "true" ); + } + } + + // -------------------------------------------------------------------- + // Update the Metadata directly from the XML text + // -------------------------------------------------------------------- + + double dfXCoef[3]; + double dfYCoef[3]; + int nMLC; + + dfXCoef[0] = dfXCoefficient[0]; + dfXCoef[1] = dfXCoefficient[1]; + dfXCoef[2] = dfXCoefficient[2]; + + dfYCoef[0] = dfYCoefficient[0]; + dfYCoef[1] = dfYCoefficient[1]; + dfYCoef[2] = dfYCoefficient[2]; + + if ( eModelCoordLocation == MCL_CENTER ) + { + dfXCoef[2] += dfXCoefficient[0] / 2; + dfYCoef[2] += dfYCoefficient[1] / 2; + nMLC = MCL_CENTER; + } + else + { + nMLC = MCL_UPPERLEFT; + } + + if( phRPC ) + { + SetRPC(); + nSRID = 0; + } + + // -------------------------------------------------------------------- + // Serialize XML metadata to plain text + // -------------------------------------------------------------------- + + char* pszMetadata = CPLSerializeXMLTree( phMetadata ); + + if( pszMetadata == NULL ) + { + return false; + } + + if( bGenSpatialIndex ) + { + nExtentSRID = nExtentSRID == 0 ? nSRID : nExtentSRID; + } + else + { + nExtentSRID = 0; /* Set spatialExtent to null */ + } + + // -------------------------------------------------------------------- + // Update GeoRaster Metadata + // -------------------------------------------------------------------- + + int nException = 0; + + OCILobLocator* phLocator = NULL; + + OWStatement* poStmt = poConnection->CreateStatement( CPLSPrintf( + "DECLARE\n" + " GR1 sdo_georaster;\n" + " GM1 sdo_geometry;\n" + " SRID number := :1;\n" + " EXT_SRID number := :2;\n" + " VAT varchar2(128);\n" + "BEGIN\n" + "\n" + " SELECT %s INTO GR1 FROM %s%s T WHERE %s FOR UPDATE;\n" + "\n" + " GR1.metadata := sys.xmltype.createxml(:3);\n" + "\n" + " IF SRID != 0 THEN\n" + " SDO_GEOR.georeference( GR1, SRID, :4," + " SDO_NUMBER_ARRAY(:5, :6, :7), SDO_NUMBER_ARRAY(:8, :9, :10));\n" + " END IF;\n" + "\n" + " IF EXT_SRID = 0 THEN\n" + " GM1 := NULL;\n" + " ELSE\n" + " GM1 := SDO_GEOR.generateSpatialExtent( GR1 );\n" + " IF EXT_SRID != SRID THEN\n" + " GM1 := SDO_CS.transform( GM1, EXT_SRID );\n" + " END IF;\n" + " END IF;\n" + "\n" + " GR1.spatialExtent := GM1;\n" + "\n" + " VAT := '%s';\n" + " IF VAT != '' THEN\n" + " SDO_GEOR.setVAT(GR1, 1, VAT);\n" + " END IF;\n" + "\n" + " BEGIN\n" + " UPDATE %s%s T SET %s = GR1\n" + " WHERE %s;\n" + " EXCEPTION\n" + " WHEN OTHERS THEN\n" + " :except := SQLCODE;\n" + " IF (SQLCODE != -29877) THEN\n" + " RAISE;\n" + " END IF;\n" + " END\n" + "\n" + " COMMIT;\n" + "END;", + sColumn.c_str(), + sSchema.c_str(), + sTable.c_str(), + sWhere.c_str(), + sValueAttributeTab.c_str(), + sSchema.c_str(), + sTable.c_str(), + sColumn.c_str(), + sWhere.c_str() ) ); + + poStmt->WriteCLob( &phLocator, pszMetadata ); + + poStmt->Bind( &nSRID ); + poStmt->Bind( &nExtentSRID ); + poStmt->Bind( &phLocator ); + poStmt->Bind( &nMLC ); + poStmt->Bind( &dfXCoef[0] ); + poStmt->Bind( &dfXCoef[1] ); + poStmt->Bind( &dfXCoef[2] ); + poStmt->Bind( &dfYCoef[0] ); + poStmt->Bind( &dfYCoef[1] ); + poStmt->Bind( &dfYCoef[2] ); + poStmt->BindName( ":except", &nException ); + + CPLFree( pszMetadata ); + + if( ! poStmt->Execute() ) + { + OCIDescriptorFree( phLocator, OCI_DTYPE_LOB ); + delete poStmt; + return false; + } + + OCIDescriptorFree( phLocator, OCI_DTYPE_LOB ); + + delete poStmt; + + if( nException ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Cannot generate spatialExtent! (ORA-%d) ", nException ); + } + + if (bGenPyramid) + { + if (GeneratePyramid( nPyramidLevels, sPyramidResampling.c_str(), true )) + { + CPLDebug("GEOR", "Generated pyramid successfully."); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, "Error generating pyramid!"); + } + } + + return true; +} + +// --------------------------------------------------------------------------- +// GeneratePyramid() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::GeneratePyramid( int nLevels, + const char* pszResampling, + bool bInternal ) +{ + nPyramidMaxLevel = nLevels; + + if( bInternal ) + { + CPLString sLevels = ""; + + if (nLevels > 0) + { + sLevels = CPLSPrintf("rlevel=%d", nLevels); + } + + OWStatement* poStmt = poConnection->CreateStatement( CPLSPrintf( + "DECLARE\n" + " gr sdo_georaster;\n" + "BEGIN\n" + " SELECT %s INTO gr FROM %s t WHERE %s FOR UPDATE;\n" + " sdo_geor.generatePyramid(gr, '%s resampling=%s');\n" + " UPDATE %s t SET %s = gr WHERE %s;\n" + " COMMIT;\n" + "END;\n", + sColumn.c_str(), + sTable.c_str(), + sWhere.c_str(), + sLevels.c_str(), + pszResampling, + sTable.c_str(), + sColumn.c_str(), + sWhere.c_str() ) ); + + if( poStmt->Execute() ) + { + delete poStmt; + return true; + } + + delete poStmt; + return false; + } + + // ----------------------------------------------------------- + // Create rows for pyramid levels + // ----------------------------------------------------------- + + OWStatement* poStmt = NULL; + + poStmt = poConnection->CreateStatement( + "DECLARE\n" + " SCL NUMBER := 0;\n" + " RC NUMBER := 0;\n" + " RR NUMBER := 0;\n" + " CBS2 NUMBER := 0;\n" + " RBS2 NUMBER := 0;\n" + " TBB NUMBER := 0;\n" + " TRB NUMBER := 0;\n" + " TCB NUMBER := 0;\n" + " X NUMBER := 0;\n" + " Y NUMBER := 0;\n" + " W NUMBER := 0;\n" + " H NUMBER := 0;\n" + " STM VARCHAR2(1024) := '';\n" + "BEGIN\n" + " EXECUTE IMMEDIATE 'DELETE FROM '||:rdt||' \n" + " WHERE RASTERID = '||:rid||' AND PYRAMIDLEVEL > 0';\n" + " STM := 'INSERT INTO '||:rdt||' VALUES (:1, :2, :3-1, :4-1, :5-1 ,\n" + " SDO_GEOMETRY(2003, NULL, NULL, SDO_ELEM_INFO_ARRAY(1, 1003, 3),\n" + " SDO_ORDINATE_ARRAY(:6, :7, :8-1, :9-1)), EMPTY_BLOB() )';\n" + " TBB := :TotalBandBlocks;\n" + " RBS2 := floor(:RowBlockSize / 2);\n" + " CBS2 := floor(:ColumnBlockSize / 2);\n" + " FOR l IN 1..:level LOOP\n" + " SCL := 2 ** l;\n" + " RR := floor(:RasterRows / SCL);\n" + " RC := floor(:RasterColumns / SCL);\n" + " IF (RC <= CBS2) OR (RR <= RBS2) THEN\n" + " H := RR;\n" + " W := RC;\n" + " ELSE\n" + " H := :RowBlockSize;\n" + " W := :ColumnBlockSize;\n" + " END IF;\n" + " TRB := greatest(1, ceil( ceil(:RasterColumns / :ColumnBlockSize) / SCL));\n" + " TCB := greatest(1, ceil( ceil(:RasterRows / :RowBlockSize) / SCL));\n" + " FOR b IN 1..TBB LOOP\n" + " Y := 0;\n" + " FOR r IN 1..TCB LOOP\n" + " X := 0;\n" + " FOR c IN 1..TRB LOOP\n" + " EXECUTE IMMEDIATE STM USING :rid, l, b, r, c, Y, X, (Y+H), (X+W);\n" + " X := X + W;\n" + " END LOOP;\n" + " Y := Y + H;\n" + " END LOOP;\n" + " END LOOP;\n" + " END LOOP;\n" + " COMMIT;\n" + "END;" ); + + const char* pszDataTable = sDataTable.c_str(); + + poStmt->BindName( ":rdt", (char*) pszDataTable ); + poStmt->BindName( ":rid", &nRasterId ); + poStmt->BindName( ":level", &nLevels ); + poStmt->BindName( ":TotalBandBlocks", &nTotalBandBlocks ); + poStmt->BindName( ":RowBlockSize", &nRowBlockSize ); + poStmt->BindName( ":ColumnBlockSize", &nColumnBlockSize ); + poStmt->BindName( ":RasterRows", &nRasterRows ); + poStmt->BindName( ":RasterColumns", &nRasterColumns ); + + if( ! poStmt->Execute() ) + { + delete poStmt; + return false; + } + + CPLXMLNode* psNode = CPLGetXMLNode( phMetadata, "rasterInfo.pyramid" ); + + if( psNode ) + { + CPLSetXMLValue( psNode, "type", "DECREASE" ); + CPLSetXMLValue( psNode, "resampling", pszResampling ); + CPLSetXMLValue( psNode, "maxLevel", CPLSPrintf( "%d", nLevels ) ); + } + + bFlushMetadata = true; + + return true; +} + +// --------------------------------------------------------------------------- +// GeneratePyramid() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::DeletePyramid() +{ + OWStatement* poStmt = poConnection->CreateStatement( CPLSPrintf( + "DECLARE\n" + " gr sdo_georaster;\n" + "BEGIN\n" + " SELECT %s INTO gr FROM %s t WHERE %s FOR UPDATE;\n" + " sdo_geor.deletePyramid(gr);\n" + " UPDATE %s t SET %s = gr WHERE %s;\n" + " COMMIT;\n" + "END;\n", + sColumn.c_str(), + sTable.c_str(), + sWhere.c_str(), + sTable.c_str(), + sColumn.c_str(), + sWhere.c_str() ) ); + + poStmt->Execute(); + + delete poStmt; + return false; +} + +// --------------------------------------------------------------------------- +// CreateBitmapMask() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::InitializeMask( int nLevel, + int nBlockColumns, + int nBlockRows, + int nColumnBlocks, + int nRowBlocks, + int nBandBlocks ) +{ + // ----------------------------------------------------------- + // Create rows for the bitmap mask + // ----------------------------------------------------------- + + OWStatement* poStmt = NULL; + + poStmt = poConnection->CreateStatement( + "DECLARE\n" + " W NUMBER := :1;\n" + " H NUMBER := :2;\n" + " BB NUMBER := :3;\n" + " RB NUMBER := :4;\n" + " CB NUMBER := :5;\n" + " X NUMBER := 0;\n" + " Y NUMBER := 0;\n" + " STM VARCHAR2(1024) := '';\n" + "BEGIN\n" + "\n" + " EXECUTE IMMEDIATE 'DELETE FROM '||:rdt||' \n" + " WHERE RASTERID = '||:rid||' AND PYRAMIDLEVEL = '||:lev||' ';\n" + "\n" + " STM := 'INSERT INTO '||:rdt||' VALUES (:1, :lev, :2-1, :3-1, :4-1 ,\n" + " SDO_GEOMETRY(2003, NULL, NULL, SDO_ELEM_INFO_ARRAY(1, 1003, 3),\n" + " SDO_ORDINATE_ARRAY(:5, :6, :7-1, :8-1)), EMPTY_BLOB() )';\n" + "\n" + " FOR b IN 1..BB LOOP\n" + " Y := 0;\n" + " FOR r IN 1..RB LOOP\n" + " X := 0;\n" + " FOR c IN 1..CB LOOP\n" + " EXECUTE IMMEDIATE STM USING :rid, b, r, c, Y, X, (Y+H), (X+W);\n" + " X := X + W;\n" + " END LOOP;\n" + " Y := Y + H;\n" + " END LOOP;\n" + " END LOOP;\n" + "END;" ); + + char pszDataTable[OWNAME]; + + poStmt->Bind( &nBlockColumns ); + poStmt->Bind( &nBlockRows ); + poStmt->Bind( &nBandBlocks ); + poStmt->Bind( &nRowBlocks ); + poStmt->Bind( &nColumnBlocks ); + poStmt->BindName( ":rdt", pszDataTable ); + poStmt->BindName( ":rid", &nRasterId ); + poStmt->BindName( ":lev", &nLevel ); + + if( ! poStmt->Execute() ) + { + delete poStmt; + return false; + } + + return true; +} + +// --------------------------------------------------------------------------- +// UnpackNBits() +// --------------------------------------------------------------------------- + +void GeoRasterWrapper::UnpackNBits( GByte* pabyData ) +{ + int nPixCount = nColumnBlockSize * nRowBlockSize * nBandBlockSize; + + if( EQUAL( sCellDepth.c_str(), "4BIT" ) ) + { + for( int ii = nPixCount - 2; ii >= 0; ii -= 2 ) + { + int k = ii >> 1; + pabyData[ii+1] = (pabyData[k] ) & 0xf; + pabyData[ii] = (pabyData[k] >> 4) & 0xf; + } + } + else if( EQUAL( sCellDepth.c_str(), "2BIT" ) ) + { + for( int ii = nPixCount - 4; ii >= 0; ii -= 4 ) + { + int k = ii >> 2; + pabyData[ii+3] = (pabyData[k] ) & 0x3; + pabyData[ii+2] = (pabyData[k] >> 2) & 0x3; + pabyData[ii+1] = (pabyData[k] >> 4) & 0x3; + pabyData[ii] = (pabyData[k] >> 6) & 0x3; + } + } + else + { + for( int ii = nPixCount - 1; ii >= 0; ii-- ) + { + if( ( pabyData[ii>>3] & ( 128 >> (ii & 0x7) ) ) ) + pabyData[ii] = 1; + else + pabyData[ii] = 0; + } + } +} + +// --------------------------------------------------------------------------- +// PackNBits() +// --------------------------------------------------------------------------- + +void GeoRasterWrapper::PackNBits( GByte* pabyData ) +{ + int nPixCount = nBandBlockSize * nRowBlockSize * nColumnBlockSize; + + GByte* pabyBuffer = (GByte*) VSIMalloc( nPixCount * sizeof(GByte*) ); + + if( pabyBuffer == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "PackNBits" ); + return; + } + + if( nCellSizeBits == 4 ) + { + for( int ii = 0; ii < nPixCount - 1; ii += 2 ) + { + int k = ii >> 1; + pabyBuffer[k] = + ((((GByte *) pabyData)[ii+1] & 0xf) ) + | ((((GByte *) pabyData)[ii] & 0xf) << 4); + } + } + else if( nCellSizeBits == 2 ) + { + for( int ii = 0; ii < nPixCount - 3; ii += 4 ) + { + int k = ii >> 2; + pabyBuffer[k] = + ((((GByte *) pabyData)[ii+3] & 0x3) ) + | ((((GByte *) pabyData)[ii+2] & 0x3) << 2) + | ((((GByte *) pabyData)[ii+1] & 0x3) << 4) + | ((((GByte *) pabyData)[ii] & 0x3) << 6); + } + } + else + { + for( int ii = 0; ii < nPixCount - 7; ii += 8 ) + { + int k = ii >> 3; + pabyBuffer[k] = + ((((GByte *) pabyData)[ii+7] & 0x1) ) + | ((((GByte *) pabyData)[ii+6] & 0x1) << 1) + | ((((GByte *) pabyData)[ii+5] & 0x1) << 2) + | ((((GByte *) pabyData)[ii+4] & 0x1) << 3) + | ((((GByte *) pabyData)[ii+3] & 0x1) << 4) + | ((((GByte *) pabyData)[ii+2] & 0x1) << 5) + | ((((GByte *) pabyData)[ii+1] & 0x1) << 6) + | ((((GByte *) pabyData)[ii] & 0x1) << 7); + } + } + + memcpy( pabyData, pabyBuffer, nPixCount ); + + CPLFree( pabyBuffer ); +} + +// --------------------------------------------------------------------------- +// UncompressJpeg() +// --------------------------------------------------------------------------- + +const static int K2Chrominance[64] = +{ + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + +static const int AC_BITS[16] = +{ + 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119 +}; + +static const int AC_HUFFVAL[256] = +{ + 0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, + 65, 81, 7, 97, 113, 19, 34, 50, 129, 8, 20, + 66, 145, 161, 177, 193, 9, 35, 51, 82, 240, 21, + 98, 114, 209, 10, 22, 36, 52, 225, 37, 241, 23, + 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, 55, + 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, + 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, + 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, + 121, 122, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 226, 227, 228, 229, 230, 231, 232, 233, 234, 242, + 243, 244, 245, 246, 247, 248, 249, 250 +}; + +static const int DC_BITS[16] = +{ + 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 +}; + +static const int DC_HUFFVAL[256] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +/*** + * + * Load the tables based on the Java's JAI default values. + * + * JPEGQTable.K2Chrominance.getScaledInstance() + * JPEGHuffmanTable.StdACChrominance + * JPEGHuffmanTable.StdDCChrominance + * + ***/ + +void JPEG_LoadTables( JQUANT_TBL* hquant_tbl_ptr, + JHUFF_TBL* huff_ac_ptr, + JHUFF_TBL* huff_dc_ptr, + unsigned int nQuality ) +{ + int i = 0; + float fscale_factor; + + // -------------------------------------------------------------------- + // Scale Quantization table based on quality + // -------------------------------------------------------------------- + + fscale_factor = (float) jpeg_quality_scaling( nQuality ) / (float) 100.0; + + for ( i = 0; i < 64; i++ ) + { + UINT16 temp = (UINT16) floor( K2Chrominance[i] * fscale_factor + 0.5 ); + if ( temp <= 0 ) + temp = 1; + if ( temp > 255 ) + temp = 255; + hquant_tbl_ptr->quantval[i] = (UINT16) temp; + } + + // -------------------------------------------------------------------- + // Load AC huffman table + // -------------------------------------------------------------------- + + for ( i = 1; i <= 16; i++ ) + { + /* counts[i] is number of Huffman codes of length i bits, i=1..16 */ + huff_ac_ptr->bits[i] = (UINT8) AC_BITS[i-1]; + } + + for ( i = 0; i < 256; i++ ) + { + /* symbols[] is the list of Huffman symbols, in code-length order */ + huff_ac_ptr->huffval[i] = (UINT8) AC_HUFFVAL[i]; + } + + // -------------------------------------------------------------------- + // Load DC huffman table + // -------------------------------------------------------------------- + + for ( i = 1; i <= 16; i++ ) + { + /* counts[i] is number of Huffman codes of length i bits, i=1..16 */ + huff_dc_ptr->bits[i] = (UINT8) DC_BITS[i-1]; + } + + for ( i = 0; i < 256; i++ ) + { + /* symbols[] is the list of Huffman symbols, in code-length order */ + huff_dc_ptr->huffval[i] = (UINT8) DC_HUFFVAL[i]; + } +} + +void GeoRasterWrapper::UncompressJpeg( unsigned long nInSize ) +{ + // -------------------------------------------------------------------- + // Load JPEG in a virtual file + // -------------------------------------------------------------------- + + const char* pszMemFile = CPLSPrintf( "/vsimem/geor_%p.jpg", pabyBlockBuf ); + + VSILFILE *fpImage = VSIFOpenL( pszMemFile, "wb" ); + VSIFWriteL( pabyBlockBuf, nInSize, 1, fpImage ); + VSIFCloseL( fpImage ); + + fpImage = VSIFOpenL( pszMemFile, "rb" ); + + // -------------------------------------------------------------------- + // Initialize decompressor + // -------------------------------------------------------------------- + + if( ! sDInfo.global_state ) + { + sDInfo.err = jpeg_std_error( &sJErr ); + jpeg_create_decompress( &sDInfo ); + + // ----------------------------------------------------------------- + // Load table for abbreviated JPEG-B + // ----------------------------------------------------------------- + + int nComponentsToLoad = -1; /* doesn't load any table */ + + if( EQUAL( sCompressionType.c_str(), "JPEG-B") ) + { + nComponentsToLoad = nBandBlockSize; + } + + for( int n = 0; n < nComponentsToLoad; n++ ) + { + sDInfo.quant_tbl_ptrs[n] = + jpeg_alloc_quant_table( (j_common_ptr) &sDInfo ); + sDInfo.ac_huff_tbl_ptrs[n] = + jpeg_alloc_huff_table( (j_common_ptr) &sDInfo ); + sDInfo.dc_huff_tbl_ptrs[n] = + jpeg_alloc_huff_table( (j_common_ptr) &sDInfo ); + + JPEG_LoadTables( sDInfo.quant_tbl_ptrs[n], + sDInfo.ac_huff_tbl_ptrs[n], + sDInfo.dc_huff_tbl_ptrs[n], + nCompressQuality ); + } + + } + + jpeg_vsiio_src( &sDInfo, fpImage ); + jpeg_read_header( &sDInfo, TRUE ); + + sDInfo.out_color_space = ( nBandBlockSize == 1 ? JCS_GRAYSCALE : JCS_RGB ); + + jpeg_start_decompress( &sDInfo ); + + GByte* pabyScanline = pabyBlockBuf; + + for( int iLine = 0; iLine < nRowBlockSize; iLine++ ) + { + JSAMPLE* ppSamples = (JSAMPLE*) pabyScanline; + jpeg_read_scanlines( &sDInfo, &ppSamples, 1 ); + pabyScanline += ( nColumnBlockSize * nBandBlockSize ); + } + + jpeg_finish_decompress( &sDInfo ); + + VSIFCloseL( fpImage ); + + VSIUnlink( pszMemFile ); +} + +// --------------------------------------------------------------------------- +// CompressJpeg() +// --------------------------------------------------------------------------- + +unsigned long GeoRasterWrapper::CompressJpeg( void ) +{ + // -------------------------------------------------------------------- + // Load JPEG in a virtual file + // -------------------------------------------------------------------- + + const char* pszMemFile = CPLSPrintf( "/vsimem/geor_%p.jpg", pabyBlockBuf ); + + VSILFILE *fpImage = VSIFOpenL( pszMemFile, "wb" ); + + bool write_all_tables = TRUE; + + if( EQUAL( sCompressionType.c_str(), "JPEG-B") ) + { + write_all_tables = FALSE; + } + + // -------------------------------------------------------------------- + // Initialize compressor + // -------------------------------------------------------------------- + + if( ! sCInfo.global_state ) + { + sCInfo.err = jpeg_std_error( &sJErr ); + jpeg_create_compress( &sCInfo ); + + jpeg_vsiio_dest( &sCInfo, fpImage ); + + sCInfo.image_width = nColumnBlockSize; + sCInfo.image_height = nRowBlockSize; + sCInfo.input_components = nBandBlockSize; + sCInfo.in_color_space = (nBandBlockSize == 1 ? JCS_GRAYSCALE : JCS_RGB); + jpeg_set_defaults( &sCInfo ); + sCInfo.JFIF_major_version = 1; + sCInfo.JFIF_minor_version = 2; + jpeg_set_quality( &sCInfo, nCompressQuality, TRUE ); + + // ----------------------------------------------------------------- + // Load table for abbreviated JPEG-B + // ----------------------------------------------------------------- + + int nComponentsToLoad = -1; /* doesn't load any table */ + + if( EQUAL( sCompressionType.c_str(), "JPEG-B") ) + { + nComponentsToLoad = nBandBlockSize; + } + + for( int n = 0; n < nComponentsToLoad; n++ ) + { + sCInfo.quant_tbl_ptrs[n] = + jpeg_alloc_quant_table( (j_common_ptr) &sCInfo ); + sCInfo.ac_huff_tbl_ptrs[n] = + jpeg_alloc_huff_table( (j_common_ptr) &sCInfo ); + sCInfo.dc_huff_tbl_ptrs[n] = + jpeg_alloc_huff_table( (j_common_ptr) &sCInfo ); + + JPEG_LoadTables( sCInfo.quant_tbl_ptrs[n], + sCInfo.ac_huff_tbl_ptrs[n], + sCInfo.dc_huff_tbl_ptrs[n], + nCompressQuality ); + } + } + else + { + jpeg_vsiio_dest( &sCInfo, fpImage ); + } + + jpeg_suppress_tables( &sCInfo, ! write_all_tables ); + jpeg_start_compress( &sCInfo, write_all_tables ); + + GByte* pabyScanline = pabyBlockBuf; + + for( int iLine = 0; iLine < nRowBlockSize; iLine++ ) + { + JSAMPLE* ppSamples = (JSAMPLE*) pabyScanline; + jpeg_write_scanlines( &sCInfo, &ppSamples, 1 ); + pabyScanline += ( nColumnBlockSize * nBandBlockSize ); + } + + jpeg_finish_compress( &sCInfo ); + + VSIFCloseL( fpImage ); + + fpImage = VSIFOpenL( pszMemFile, "rb" ); + size_t nSize = VSIFReadL( pabyCompressBuf, 1, nBlockBytes, fpImage ); + VSIFCloseL( fpImage ); + + VSIUnlink( pszMemFile ); + + return (unsigned long) nSize; +} + +// --------------------------------------------------------------------------- +// UncompressDeflate() +// --------------------------------------------------------------------------- + +bool GeoRasterWrapper::UncompressDeflate( unsigned long nBufferSize ) +{ + GByte* pabyBuf = (GByte*) VSIMalloc( nBufferSize ); + + if( pabyBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "UncompressDeflate" ); + return false; + } + + memcpy( pabyBuf, pabyBlockBuf, nBufferSize ); + + // Call ZLib uncompress + + unsigned long nDestLen = nBlockBytes; + + int nRet = uncompress( pabyBlockBuf, &nDestLen, pabyBuf, nBufferSize ); + + CPLFree( pabyBuf ); + + if( nRet != Z_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "ZLib return code (%d)", nRet ); + return false; + } + + if( nDestLen != nBlockBytes ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "ZLib decompressed buffer size (%ld) expected (%ld)", nDestLen, nBlockBytes ); + return false; + } + + return true; +} + +// --------------------------------------------------------------------------- +// CompressDeflate() +// --------------------------------------------------------------------------- + +unsigned long GeoRasterWrapper::CompressDeflate( void ) +{ + unsigned long nLen = ((unsigned long)(nBlockBytes * 1.1)) + 12; + + GByte* pabyBuf = (GByte*) VSIMalloc( nBlockBytes ); + + if( pabyBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "CompressDeflate" ); + return 0; + } + + memcpy( pabyBuf, pabyBlockBuf, nBlockBytes ); + + // Call ZLib compress + + int nRet = compress( pabyCompressBuf, &nLen, pabyBuf, nBlockBytes ); + + CPLFree( pabyBuf ); + + if( nRet != Z_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "ZLib return code (%d)", nRet ); + return 0; + } + + return nLen; +} diff --git a/bazaar/plugin/gdal/frmts/georaster/makefile.vc b/bazaar/plugin/gdal/frmts/georaster/makefile.vc new file mode 100644 index 000000000..4a9931768 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/georaster/makefile.vc @@ -0,0 +1,46 @@ +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +OBJ = oci_wrapper.obj \ + georaster_dataset.obj \ + georaster_rasterband.obj \ + georaster_wrapper.obj + +PLUGIN_DLL = gdal_GEOR.dll + +!IFDEF JPEG_SUPPORTED +!IFDEF JPEG_EXTERNAL_LIB +JPEG_FLAGS = -I$(JPEGDIR) -DJPEG_SUPPORTED +!ELSE +JPEG_FLAGS = -I..\jpeg\libjpeg -DJPEG_SUPPORTED +JPEG_LIB = ..\jpeg\libjpeg\libjpeg.lib +!ENDIF +!ENDIF + +EXTRAFLAGS = $(OCI_INCLUDE) -I..\zlib $(JPEG_FLAGS) + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + cd .. + +fastread: + cl $(OPTFLAGS) fastread.c $(OCI_INCLUDE) $(OCI_LIB) + +clean: + -del *.obj + -del *.dll + -del *.exp + -del *.lib + -del *.manifest + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(GDALLIB) $(OCI_LIB) ..\zlib\*.obj \ + ..\jpeg\vsidataio.obj $(JPEG_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) diff --git a/bazaar/plugin/gdal/frmts/georaster/oci_wrapper.cpp b/bazaar/plugin/gdal/frmts/georaster/oci_wrapper.cpp new file mode 100644 index 000000000..2c2f3ccea --- /dev/null +++ b/bazaar/plugin/gdal/frmts/georaster/oci_wrapper.cpp @@ -0,0 +1,1757 @@ +/****************************************************************************** + * $Id: $ + * + * Name: oci_wrapper.cpp + * Project: Oracle Spatial GeoRaster Driver + * Purpose: Limited wrapper for OCI (Oracle Call Interfaces) + * Author: Ivan Lucena [ivan.lucena at oracle.com] + * + ****************************************************************************** + * Copyright (c) 2008, Ivan Lucena + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "oci_wrapper.h" + +static const OW_CellDepth ahOW_CellDepth[] = { + {"8BIT_U", GDT_Byte}, + {"16BIT_U", GDT_UInt16}, + {"16BIT_S", GDT_Int16}, + {"32BIT_U", GDT_UInt32}, + {"32BIT_S", GDT_Int32}, + {"32BIT_REAL", GDT_Float32}, + {"64BIT_REAL", GDT_Float64}, + {"32BIT_COMPLEX", GDT_CFloat32}, + {"64BIT_COMPLEX", GDT_CFloat64}, + {"1BIT", GDT_Byte}, + {"2BIT", GDT_Byte}, + {"4BIT", GDT_Byte} +}; + +/*****************************************************************************/ +/* OWConnection */ +/*****************************************************************************/ + +OWConnection::OWConnection( const char* pszUserIn, + const char* pszPasswordIn, + const char* pszServerIn ) +{ + pszUser = CPLStrdup( pszUserIn ); + pszPassword = CPLStrdup( pszPasswordIn ); + pszServer = CPLStrdup( pszServerIn ); + hEnv = NULL; + hError = NULL; + hSvcCtx = NULL; + hDescribe = NULL; + hNumArrayTDO = NULL; + hGeometryTDO = NULL; + hGeoRasterTDO = NULL; + hElemArrayTDO = NULL; + hOrdnArrayTDO = NULL; + bSuceeeded = false; + nCharSize = 1; + + // ------------------------------------------------------ + // Operational Systems's authentication option + // ------------------------------------------------------ + + const char* pszUserId = "/"; + + ub4 eCred = OCI_CRED_RDBMS; + + if( EQUAL(pszServer, "") && + EQUAL(pszPassword, "") && + EQUAL(pszUser, "") ) + { + eCred = OCI_CRED_EXT; + } + else + { + pszUserId = pszUser; + } + + // ------------------------------------------------------ + // Initialize Environment handler + // ------------------------------------------------------ + + if( OCIEnvCreate( &hEnv, + (ub4) ( OCI_DEFAULT | OCI_OBJECT | OCI_THREADED ), + (dvoid *) 0, (dvoid * (*)(dvoid *, size_t)) 0, + (dvoid * (*)(dvoid *, dvoid *, size_t)) 0, + (void (*)(dvoid *, dvoid *)) 0, (size_t) 0, + (dvoid **) 0), NULL ) + { + return; + } + + // ------------------------------------------------------ + // Initialize Error handler + // ------------------------------------------------------ + + if( CheckError( OCIHandleAlloc( (dvoid *) hEnv, (dvoid **) &hError, + OCI_HTYPE_ERROR, (size_t) 0, (dvoid **) 0), NULL ) ) + { + return; + } + + // ------------------------------------------------------ + // Initialize Server Context + // ------------------------------------------------------ + + if( CheckError( OCIHandleAlloc( (dvoid *) hEnv, (dvoid **) &hSvcCtx, + OCI_HTYPE_SVCCTX, (size_t) 0, (dvoid **) 0), hError ) ) + { + return; + } + + // ------------------------------------------------------ + // Allocate Server and Authentication (Session) handler + // ------------------------------------------------------ + + if( CheckError( OCIHandleAlloc( (dvoid *) hEnv, (dvoid **) &hServer, + (ub4) OCI_HTYPE_SERVER, (size_t) 0, (dvoid **) 0), hError ) ) + { + return; + } + + if( CheckError( OCIHandleAlloc((dvoid *) hEnv, (dvoid **)&hSession, + (ub4) OCI_HTYPE_SESSION, (size_t) 0, (dvoid **) 0), hError ) ) + { + return; + } + + // ------------------------------------------------------ + // Attach to the server + // ------------------------------------------------------ + + if( CheckError( OCIServerAttach( hServer, hError, (text*) pszServer, + (sb4) strlen((char*) pszServer), (ub4) 0), hError ) ) + { + return; + } + + if( CheckError( OCIAttrSet((dvoid *) hSession, (ub4) OCI_HTYPE_SESSION, + (dvoid *) pszUserId, (ub4) strlen( pszUserId), + (ub4) OCI_ATTR_USERNAME, hError), hError ) ) + { + return; + } + + if( CheckError( OCIAttrSet((dvoid *) hSession, (ub4) OCI_HTYPE_SESSION, + (dvoid *) pszPassword, (ub4) strlen((char *) pszPassword), + (ub4) OCI_ATTR_PASSWORD, hError), hError ) ) + { + return; + } + + if( CheckError( OCIAttrSet( (dvoid *) hSvcCtx, OCI_HTYPE_SVCCTX, (dvoid *)hServer, + (ub4) 0, OCI_ATTR_SERVER, (OCIError *) hError), hError ) ) + { + return; + } + + // ------------------------------------------------------ + // Initialize Session + // ------------------------------------------------------ + + if( CheckError( OCISessionBegin(hSvcCtx, hError, hSession, eCred, + (ub4) OCI_DEFAULT), hError ) ) + { + return; + } + + // ------------------------------------------------------ + // Initialize Service + // ------------------------------------------------------ + + if( CheckError( OCIAttrSet((dvoid *) hSvcCtx, (ub4) OCI_HTYPE_SVCCTX, + (dvoid *) hSession, (ub4) 0, + (ub4) OCI_ATTR_SESSION, hError), hError ) ) + { + return; + } + + bSuceeeded = true; + + // ------------------------------------------------------ + // Get Character Size based on current Locale + // ------------------------------------------------------ + + OCINlsNumericInfoGet( hEnv, hError, + &nCharSize, OCI_NLS_CHARSET_MAXBYTESZ ); + + // ------------------------------------------------------ + // Get Server Version + // ------------------------------------------------------ + + char szVersionTxt[OWTEXT]; + + OCIServerVersion ( + hSvcCtx, + hError, + (text*) szVersionTxt, + (ub4) OWTEXT, + (ub1) OCI_HTYPE_SVCCTX ); + + nVersion = OWParseServerVersion( szVersionTxt ); + + // ------------------------------------------------------ + // Initialize/Describe types + // ------------------------------------------------------ + + CheckError( OCIHandleAlloc( + (dvoid*) hEnv, + (dvoid**) (dvoid*) &hDescribe, + (ub4) OCI_HTYPE_DESCRIBE, + (size_t) 0, + (dvoid**) NULL ), hError ); + + hNumArrayTDO = DescribeType( SDO_NUMBER_ARRAY ); + hGeometryTDO = DescribeType( SDO_GEOMETRY ); + hGeoRasterTDO = DescribeType( SDO_GEORASTER ); + hElemArrayTDO = DescribeType( SDO_ELEM_INFO_ARRAY); + hOrdnArrayTDO = DescribeType( SDO_ORDINATE_ARRAY); + + if( nVersion > 10 ) + { + hPCTDO = DescribeType( SDO_PC ); + } +} + +OWConnection::~OWConnection() +{ + OCIHandleFree( (dvoid*) hDescribe, (ub4) OCI_HTYPE_DESCRIBE); + + if( hSvcCtx && hError && hSession ) + OCISessionEnd( hSvcCtx, hError, hSession, (ub4) 0); + + if( hSvcCtx && hError) + OCIServerDetach( hServer, hError, (ub4) OCI_DEFAULT); + + if( hServer ) + OCIHandleFree((dvoid *) hServer, (ub4) OCI_HTYPE_SERVER); + + if( hSvcCtx ) + OCIHandleFree((dvoid *) hSvcCtx, (ub4) OCI_HTYPE_SVCCTX); + + if( hError ) + OCIHandleFree((dvoid *) hError, (ub4) OCI_HTYPE_ERROR); + + if( hSession ) + OCIHandleFree((dvoid *) hSession, (ub4) OCI_HTYPE_SESSION); +} + +OCIType* OWConnection::DescribeType( const char *pszTypeName ) +{ + OCIParam* hParam = NULL; + OCIRef* hRef = NULL; + OCIType* hType = NULL; + + CheckError( OCIDescribeAny( + hSvcCtx, + hError, + (text*) pszTypeName, + (ub4) strlen( pszTypeName ), + (ub1) OCI_OTYPE_NAME, + (ub1) OCI_DEFAULT, + (ub1) OCI_PTYPE_TYPE, + hDescribe ), hError ); + + CheckError( OCIAttrGet( + hDescribe, + (ub4) OCI_HTYPE_DESCRIBE, + (dvoid*) &hParam, + (ub4*) NULL, + (ub4) OCI_ATTR_PARAM, + hError ), hError ); + + CheckError( OCIAttrGet( + hParam, + (ub4) OCI_DTYPE_PARAM, + (dvoid*) &hRef, + (ub4*) NULL, + (ub4) OCI_ATTR_REF_TDO, + hError ), hError ); + + CheckError( OCIObjectPin( + hEnv, + hError, + hRef, + (OCIComplexObject*) NULL, + (OCIPinOpt) OCI_PIN_ANY, + (OCIDuration) OCI_DURATION_SESSION, + (OCILockOpt) OCI_LOCK_NONE, + (dvoid**) (dvoid*) &hType ), hError ); + + return hType; +} + +void OWConnection::CreateType( sdo_geometry** pphData ) +{ + CheckError( OCIObjectNew( + hEnv, + hError, + hSvcCtx, + OCI_TYPECODE_OBJECT, + hGeometryTDO, + (dvoid *) 0, + OCI_DURATION_CALL, + TRUE, + (dvoid **) pphData), hError ); +} + +void OWConnection::DestroyType( sdo_geometry** pphData ) +{ + CheckError( OCIObjectFree( + hEnv, + hError, + (dvoid*) *pphData, + (ub2) 0), NULL ); +} + +void OWConnection::CreateType( OCIArray** phData, OCIType* otype) +{ + CheckError( OCIObjectNew( hEnv, + hError, + hSvcCtx, + OCI_TYPECODE_VARRAY, + otype, + (dvoid *)NULL, + OCI_DURATION_SESSION, + FALSE, + (dvoid **)phData), hError ); +} + +void OWConnection::DestroyType( OCIArray** phData ) +{ + CheckError( OCIObjectFree( + hEnv, + hError, + (OCIColl*) *phData, + (ub2) 0), NULL ); +} + +OWStatement* OWConnection::CreateStatement( const char* pszStatement ) +{ + OWStatement* poStatement = new OWStatement( this, pszStatement ); + + return poStatement; +} + +OCIParam* OWConnection::GetDescription( char* pszTable ) +{ + OCIParam* phParam = NULL; + OCIParam* phAttrs = NULL; + + CheckError( OCIDescribeAny ( + hSvcCtx, + hError, + (text*) pszTable, + (ub4) strlen( pszTable ), + (ub1) OCI_OTYPE_NAME, + (ub1) OCI_DEFAULT, + (ub1) OCI_PTYPE_TABLE, + hDescribe ), hError ); + + CheckError( OCIAttrGet( + hDescribe, + (ub4) OCI_HTYPE_DESCRIBE, + (dvoid*) &phParam, + (ub4*) NULL, + (ub4) OCI_ATTR_PARAM, + hError ), hError ); + + CheckError( OCIAttrGet( + phParam, + (ub4) OCI_DTYPE_PARAM, + (dvoid*) &phAttrs, + (ub4*) NULL, + (ub4) OCI_ATTR_LIST_COLUMNS, + hError ), hError ); + + return phAttrs; +} + +bool OWConnection::GetNextField( OCIParam* phTable, + int nIndex, + char* pszName, + int* pnType, + int* pnSize, + int* pnPrecision, + signed short* pnScale ) +{ + OCIParam* hParmDesc = NULL; + + sword nStatus = 0; + + nStatus = OCIParamGet( + phTable, + (ub4) OCI_DTYPE_PARAM, + hError, + (dvoid**) &hParmDesc, //Warning + (ub4) nIndex + 1 ); + + if( nStatus != OCI_SUCCESS ) + { + return false; + } + + char* pszFieldName = NULL; + ub4 nNameLength = 0; + + CheckError( OCIAttrGet( + hParmDesc, + (ub4) OCI_DTYPE_PARAM, + (dvoid*) &pszFieldName, + (ub4*) &nNameLength, + (ub4) OCI_ATTR_NAME, + hError ), hError ); + + ub2 nOCIType = 0; + + CheckError( OCIAttrGet( + hParmDesc, + (ub4) OCI_DTYPE_PARAM, + (dvoid*) &nOCIType, + (ub4*) NULL, + (ub4) OCI_ATTR_DATA_TYPE, + hError ), hError ); + + ub2 nOCILen = 0; + + CheckError( OCIAttrGet( + hParmDesc, + (ub4) OCI_DTYPE_PARAM, + (dvoid*) &nOCILen, + (ub4*) NULL, + (ub4) OCI_ATTR_DATA_SIZE, + hError ), hError ); + + unsigned short nOCIPrecision = 0; + sb1 nOCIScale = 0; + + if( nOCIType == SQLT_NUM ) + { + CheckError( OCIAttrGet( + hParmDesc, + (ub4) OCI_DTYPE_PARAM, + (dvoid*) &nOCIPrecision, + (ub4*) 0, + (ub4) OCI_ATTR_PRECISION, + hError ), hError ); + + CheckError( OCIAttrGet( + hParmDesc, + (ub4) OCI_DTYPE_PARAM, + (dvoid*) &nOCIScale, + (ub4*) 0, + (ub4) OCI_ATTR_SCALE, + hError ), hError ); + + if( nOCIPrecision > 255 ) // Lesson learned from ogrocisession.cpp + { + nOCIPrecision = nOCIPrecision / 256; + } + } + + nNameLength = MIN( nNameLength, OWNAME ); + + strncpy( pszName, pszFieldName, nNameLength); + pszName[nNameLength] = '\0'; + + *pnType = (int) nOCIType; + *pnSize = (int) nOCILen; + *pnPrecision = (int) nOCIPrecision; + *pnScale = (signed short) nOCIScale; + + return true; + +} + +bool OWConnection::StartTransaction() +{ + CheckError( OCITransStart ( + hSvcCtx, + hError, + (uword) 30, + OCI_TRANS_NEW), hError ); + + return true; +} + +bool OWConnection::Commit() +{ + CheckError( OCITransCommit ( + hSvcCtx, + hError, + OCI_DEFAULT), hError ); + + return true; +} + +/*****************************************************************************/ +/* OWStatement */ +/*****************************************************************************/ + +OWStatement::OWStatement( OWConnection* pConnect, const char* pszStatement ) +{ + poConnection = pConnect; + nStmtMode = OCI_DEFAULT; + nNextCol = 0; + nNextBnd = 0; + hError = poConnection->hError; + + // ----------------------------------------------------------- + // Create Statement handler + // ----------------------------------------------------------- + + OCIStmt* hStatement; + + CheckError( OCIHandleAlloc( (dvoid*) poConnection->hEnv, + (dvoid**) (dvoid*) &hStatement, + (ub4) OCI_HTYPE_STMT, + (size_t) 0, + (dvoid**) NULL), hError ); + + hStmt = hStatement; // Save Statement Handle + + // ----------------------------------------------------------- + // Prepare Statement + // ----------------------------------------------------------- + + CheckError( OCIStmtPrepare( hStmt, + hError, + (text*) pszStatement, + (ub4) strlen(pszStatement), + (ub4) OCI_NTV_SYNTAX, + (ub4) OCI_DEFAULT ), hError ); + + // ----------------------------------------------------------- + // Get Statement type + // ----------------------------------------------------------- + + ub2 nStmtType; + + CheckError( OCIAttrGet( (dvoid*) hStmt, + (ub4) OCI_HTYPE_STMT, + (dvoid*) &nStmtType, + (ub4*) 0, + (ub4) OCI_ATTR_STMT_TYPE, + hError ), hError ); + + // ----------------------------------------------------------- + // Set Statement mode + // ----------------------------------------------------------- + + if( nStmtType != OCI_STMT_SELECT ) + { + nStmtMode = OCI_DEFAULT; + } + + CPLDebug("PL/SQL","\n%s\n", pszStatement); +} + +OWStatement::~OWStatement() +{ + OCIHandleFree( (dvoid*) hStmt, (ub4) OCI_HTYPE_STMT); +} + +bool OWStatement::Execute( int nRows ) +{ + sword nStatus = OCIStmtExecute( poConnection->hSvcCtx, + hStmt, + hError, + (ub4) nRows, + (ub4) 0, + (OCISnapshot*) NULL, + (OCISnapshot*) NULL, + nStmtMode ); + + if( CheckError( nStatus, hError ) ) + { + return false; + } + + + if( nStatus == OCI_SUCCESS_WITH_INFO || nStatus == OCI_NO_DATA ) + { + return false; + } + + return true; +} + +bool OWStatement::Fetch( int nRows ) +{ + sword nStatus = 0; + + nStatus = OCIStmtFetch2 ( + (OCIStmt*) hStmt, + (OCIError*) poConnection->hError, + (ub4) nRows, + (ub2) OCI_FETCH_NEXT, + (sb4) 0, + (ub4) OCI_DEFAULT ); + + if( nStatus == OCI_NO_DATA ) + { + return false; + } + + if( CheckError( nStatus, poConnection->hError ) ) + { + return false; + } + + return true; +} + +void OWStatement::Bind( int* pnData ) +{ + OCIBind* hBind = NULL; + + nNextBnd++; + + CheckError( OCIBindByPos( + hStmt, + &hBind, + hError, + (ub4) nNextBnd, + (dvoid*) pnData, + (sb4) sizeof(int), + (ub2) SQLT_INT, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) NULL, + (ub4) NULL, + (ub4) OCI_DEFAULT ), + hError ); +} + +void OWStatement::Bind( long* pnData ) +{ + OCIBind* hBind = NULL; + + nNextBnd++; + + CheckError( OCIBindByPos( + hStmt, + &hBind, + hError, + (ub4) nNextBnd, + (dvoid*) pnData, + (sb4) sizeof(long), + (ub2) SQLT_INT, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) NULL, + (ub4) NULL, + (ub4) OCI_DEFAULT ), + hError ); +} + +void OWStatement::Bind( double* pnData ) +{ + OCIBind* hBind = NULL; + + nNextBnd++; + + CheckError( OCIBindByPos( + hStmt, + &hBind, + hError, + (ub4) nNextBnd, + (dvoid*) pnData, + (sb4) sizeof(double), + (ub2) SQLT_BDOUBLE, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) NULL, + (ub4) NULL, + (ub4) OCI_DEFAULT ), + hError ); +} + +void OWStatement::Bind( char* pData, long nData ) +{ + OCIBind* hBind = NULL; + + nNextBnd++; + + CheckError( OCIBindByPos( + hStmt, + &hBind, + hError, + (ub4) nNextBnd, + (dvoid*) pData, + (sb4) nData, + (ub2) SQLT_LBI, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) NULL, + (ub4) NULL, + (ub4) OCI_DEFAULT ), + hError ); +} + +void OWStatement::Bind( sdo_geometry** pphData ) +{ + OCIBind* hBind = NULL; + + nNextBnd++; + + CheckError( OCIBindByPos( + hStmt, + &hBind, + hError, + (ub4) nNextBnd, + (dvoid*) NULL, + (sb4) 0, + (ub2) SQLT_NTY, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) 0, + (ub4) 0, + (ub4) OCI_DEFAULT ), + hError ); + + CheckError( OCIBindObject( + hBind, + hError, + poConnection->hGeometryTDO, + (dvoid**) pphData, + (ub4*) 0, + (dvoid**) 0, + (ub4*) 0), + hError ); + +} + +void OWStatement::Bind( OCILobLocator** pphLocator ) +{ + OCIBind* hBind = NULL; + + nNextBnd++; + + CheckError( OCIBindByPos( + hStmt, + &hBind, + hError, + (ub4) nNextBnd, + (dvoid*) pphLocator, + (sb4) -1, + (ub2) SQLT_CLOB, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) NULL, + (ub4) NULL, + (ub4) OCI_DEFAULT ), + hError ); +} + +void OWStatement::Bind( OCIArray** pphData, OCIType* type ) +{ + OCIBind* hBind = NULL; + + nNextBnd++; + + CheckError( OCIBindByPos( + hStmt, + &hBind, + hError, + (ub4) nNextBnd, + (dvoid*) 0, + (sb4) 0, + (ub2) SQLT_NTY, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) NULL, + (ub4) NULL, + (ub4) OCI_DEFAULT ), + hError ); + + CheckError( OCIBindObject( + hBind, + hError, + type, + (dvoid **)pphData, + (ub4 *)0, + (dvoid **)0, + (ub4 *)0 ), + hError); + +} + +void OWStatement::Bind( char* pszData, int nSize ) +{ + OCIBind* hBind = NULL; + + nNextBnd++; + + CheckError( OCIBindByPos( + hStmt, + &hBind, + hError, + (ub4) nNextBnd, + (dvoid*) pszData, + (sb4) nSize, + (ub2) SQLT_STR, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) NULL, + (ub4) NULL, + (ub4) OCI_DEFAULT ), + hError ); +} + +void OWStatement::Define( int* pnData ) +{ + OCIDefine* hDefine = NULL; + + nNextCol++; + + CheckError( OCIDefineByPos( hStmt, + &hDefine, + hError, + (ub4) nNextCol, + (dvoid*) pnData, + (sb4) sizeof(int), + (ub2) SQLT_INT, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) OCI_DEFAULT ), hError ); +} + +void OWStatement::Define( long* pnData ) +{ + OCIDefine* hDefine = NULL; + + nNextCol++; + + CheckError( OCIDefineByPos( hStmt, + &hDefine, + hError, + (ub4) nNextCol, + (dvoid*) pnData, + (sb4) sizeof(long int), + (ub2) SQLT_INT, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) OCI_DEFAULT ), hError ); +} + +void OWStatement::Define( double* pfdData ) +{ + OCIDefine* hDefine = NULL; + + nNextCol++; + + CheckError( OCIDefineByPos( hStmt, + &hDefine, + hError, + (ub4) nNextCol, + (dvoid*) pfdData, + (sb4) sizeof(double), + (ub2) SQLT_BDOUBLE, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) OCI_DEFAULT ), hError ); +} + +void OWStatement::Define( char* pszData, int nSize ) +{ + OCIDefine* hDefine = NULL; + + nNextCol++; + + CheckError( OCIDefineByPos( + hStmt, + &hDefine, + hError, + (ub4) nNextCol, + (dvoid*) pszData, + (sb4) nSize, + (ub2) SQLT_STR, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) OCI_DEFAULT ), + hError ); +} + +void OWStatement::Define( OCILobLocator** pphLocator ) +{ + OCIDefine* hDefine = NULL; + + nNextCol++; + + CheckError( OCIDescriptorAlloc( + poConnection->hEnv, + (void**) pphLocator, + OCI_DTYPE_LOB, + 0, + 0), + hError ); + + CheckError( OCIDefineByPos( + hStmt, + &hDefine, + hError, + (ub4) nNextCol, + (dvoid*) pphLocator, + (sb4) 0, + (ub2) SQLT_BLOB, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) OCI_DEFAULT ), + hError ); +} + +void OWStatement::WriteCLob( OCILobLocator** pphLocator, char* pszData ) +{ + nNextCol++; + + CheckError( OCIDescriptorAlloc( + poConnection->hEnv, + (void**) pphLocator, + OCI_DTYPE_LOB, + (size_t) 0, + (dvoid **) 0), + hError ); + + CheckError( OCILobCreateTemporary( + poConnection->hSvcCtx, + poConnection->hError, + (OCILobLocator*) *pphLocator, + (ub4) OCI_DEFAULT, + (ub1) OCI_DEFAULT, + (ub1) OCI_TEMP_CLOB, + false, + OCI_DURATION_SESSION ), + hError ); + + ub4 nAmont = (ub4) strlen(pszData); + + CheckError( OCILobWrite( + poConnection->hSvcCtx, + hError, + *pphLocator, + (ub4*) &nAmont, + (ub4) 1, + (dvoid*) pszData, + (ub4) strlen(pszData), + (ub1) OCI_ONE_PIECE, + (dvoid*) NULL, + NULL, + (ub2) 0, + (ub1) SQLCS_IMPLICIT ), + hError ); +} + +void OWStatement::Define( OCIArray** pphData ) +{ + OCIDefine* hDefine = NULL; + + nNextCol++; + + CheckError( OCIDefineByPos( hStmt, + &hDefine, + hError, + (ub4) nNextCol, + (dvoid*) NULL, + (sb4) 0, + (ub2) SQLT_NTY, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) OCI_DEFAULT ), hError ); + + CheckError( OCIDefineObject( hDefine, + hError, + poConnection->hNumArrayTDO, + (dvoid**) pphData, + (ub4*) NULL, + (dvoid**) NULL, + (ub4*) NULL ), hError ); +} + +void OWStatement::Define( sdo_georaster** pphData ) +{ + OCIDefine* hDefine = NULL; + + nNextCol++; + + CheckError( OCIDefineByPos( hStmt, + &hDefine, + hError, + (ub4) nNextCol, + (dvoid*) NULL, + (sb4) 0, + (ub2) SQLT_NTY, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) OCI_DEFAULT ), hError ); + + CheckError( OCIDefineObject( hDefine, + hError, + poConnection->hGeoRasterTDO, + (dvoid**) pphData, + (ub4*) NULL, + (dvoid**) NULL, + (ub4*) NULL ), hError ); +} + +void OWStatement::Define( sdo_geometry** pphData ) +{ + OCIDefine* hDefine = NULL; + + nNextCol++; + + CheckError( OCIDefineByPos( hStmt, + &hDefine, + hError, + (ub4) nNextCol, + (dvoid*) NULL, + (sb4) 0, + (ub2) SQLT_NTY, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) OCI_DEFAULT ), hError ); + + CheckError( OCIDefineObject( hDefine, + hError, + poConnection->hGeometryTDO, + (dvoid**) pphData, + (ub4*) NULL, + (dvoid**) NULL, + (ub4*) NULL ), hError ); +} + +void OWStatement::Define( sdo_pc** pphData ) +{ + OCIDefine* hDefine = NULL; + + nNextCol++; + + CheckError( OCIDefineByPos( hStmt, + &hDefine, + hError, + (ub4) nNextCol, + (dvoid*) NULL, + (sb4) 0, + (ub2) SQLT_NTY, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) OCI_DEFAULT ), hError ); + + CheckError( OCIDefineObject( hDefine, + hError, + poConnection->hPCTDO, + (dvoid**) pphData, + (ub4*) NULL, + (dvoid**) NULL, + (ub4*) NULL ), hError ); +} + +void OWStatement::Define( OCILobLocator** pphLocator, long nIterations ) +{ + OCIDefine* hDefine = NULL; + + nNextCol++; + + long i; + + for (i = 0; i < nIterations; i++) + { + OCIDescriptorAlloc( + poConnection->hEnv, + (void**) &pphLocator[i], + OCI_DTYPE_LOB, (size_t) 0, (void**) 0); + } + + CheckError( OCIDefineByPos( hStmt, + &hDefine, + hError, + (ub4) nNextCol, + (dvoid*) pphLocator, + (sb4) -1, + (ub2) SQLT_BLOB, + (void*) 0, + (ub2*) 0, + (ub2*) 0, + (ub4) OCI_DEFAULT ), hError ); +} + +int OWStatement::GetInteger( OCINumber* ppoData ) +{ + sb4 nRetVal; + + CheckError( OCINumberToInt( + hError, + ppoData, + (uword) sizeof(sb4), + OCI_NUMBER_SIGNED, + (dvoid *) &nRetVal ), + hError ); + + return nRetVal; +} + +double OWStatement::GetDouble( OCINumber* ppoData ) +{ + double dfRetVal = 0.0; + + CheckError( OCINumberToReal( + hError, + ppoData, + (uword) sizeof(dfRetVal), + (dvoid*) &dfRetVal ), + hError ); + + return dfRetVal; +} + +char* OWStatement::GetString( OCIString* ppoData ) +{ + return (char*) OCIStringPtr( + poConnection->hEnv, + ppoData ); +} + +void OWStatement::Free( OCILobLocator** pphLocator, int nCount ) +{ + if( nCount > 0 && pphLocator != NULL ) + { + int i = 0; + for (i = 0; i < nCount; i++) + { + if( pphLocator[i] != NULL ) + { + OCIDescriptorFree(&pphLocator[i], OCI_DTYPE_LOB); + } + } + } +} + +int OWStatement::GetElement( OCIArray** ppoData, int nIndex, int* pnResult ) +{ + boolean exists; + OCINumber *oci_number; + ub4 element_type; + + *pnResult = 0; + + if( CheckError( OCICollGetElem( + poConnection->hEnv, + hError, + (OCIColl*) *ppoData, + (sb4) nIndex, + (boolean*) &exists, + (dvoid**) (dvoid*) &oci_number, + (dvoid**) NULL ), hError ) ) + { + return *pnResult; + } + + if( CheckError( OCINumberToInt( + hError, + oci_number, + (uword) sizeof(ub4), + OCI_NUMBER_UNSIGNED, + (dvoid *) &element_type ), hError ) ) + { + return *pnResult; + } + + *pnResult = (int) element_type; + + return *pnResult; +} + +double OWStatement::GetElement( OCIArray** ppoData, + int nIndex, double* pdfResult ) +{ + boolean exists; + OCINumber *oci_number; + double element_type; + + *pdfResult = 0.0; + + if( CheckError( OCICollGetElem( + poConnection->hEnv, + hError, + (OCIColl*) *ppoData, + (sb4) nIndex, + (boolean*) &exists, + (dvoid**) (dvoid*) &oci_number, NULL ), hError ) ) + { + return *pdfResult; + } + + if( CheckError( OCINumberToReal( + hError, + oci_number, + (uword) sizeof(double), + (dvoid *) &element_type ), hError ) ) + { + return *pdfResult; + } + + *pdfResult = (double) element_type; + + return *pdfResult; +} + +void OWStatement::AddElement( OCIArray* poData, + int nValue ) +{ + OCINumber oci_number; + + CheckError(OCINumberFromInt(hError, + (dvoid*) &nValue, + (uword) sizeof(ub4), + OCI_NUMBER_UNSIGNED, + (OCINumber*) &oci_number), hError); + + CheckError(OCICollAppend(poConnection->hEnv, + hError, + (OCINumber*) &oci_number, + (dvoid*) 0, + (OCIColl*) poData), hError); +} + +void OWStatement::AddElement( OCIArray* poData, + double dfValue ) +{ + OCINumber oci_number; + + CheckError(OCINumberFromReal(hError, + (dvoid*) &dfValue, + (uword) sizeof(double), + (OCINumber*) &oci_number), hError); + + CheckError(OCICollAppend(poConnection->hEnv, + hError, + (OCINumber*) &oci_number, + (dvoid*) 0, + (OCIColl*) poData), hError); +} + +unsigned long OWStatement::ReadBlob( OCILobLocator* phLocator, + void* pBuffer, + int nSize ) +{ + ub4 nAmont = (ub4) 0; + + if( CheckError( OCILobRead( + poConnection->hSvcCtx, + hError, + phLocator, + (ub4*) &nAmont, + (ub4) 1, + (dvoid*) pBuffer, + (ub4) nSize, + (dvoid *) 0, + (OCICallbackLobRead) 0, + (ub2) 0, + (ub1) SQLCS_IMPLICIT), hError ) ) + { + return 0; + } + + return nAmont; +} + +bool OWStatement::WriteBlob( OCILobLocator* phLocator, + void* pBuffer, + int nSize ) +{ + ub4 nAmont = (ub4) nSize; + + if( CheckError( OCILobWrite( + poConnection->hSvcCtx, + hError, + phLocator, + (ub4*) &nAmont, + (ub4) 1, + (dvoid*) pBuffer, + (ub4) nSize, + (ub1) OCI_ONE_PIECE, + (dvoid*) NULL, + NULL, + (ub2) 0, + (ub1) SQLCS_IMPLICIT ), + hError ) ) + { + return false; + } + + return ( nAmont == (ub4) nSize ); +} + +char* OWStatement::ReadCLob( OCILobLocator* phLocator ) +{ + ub4 nSize = 0; + ub4 nAmont = 0; + + char* pszBuffer = NULL; + + if( CheckError( OCILobGetLength ( + poConnection->hSvcCtx, + hError, + phLocator, + (ub4*) &nSize ), + hError ) ) + { + return NULL; + } + + nSize *= this->poConnection->nCharSize; + + pszBuffer = (char*) VSIMalloc( sizeof(char*) * nSize ); + + if( pszBuffer == NULL) + { + return NULL; + } + + if( CheckError( OCILobRead( + poConnection->hSvcCtx, + hError, + phLocator, + (ub4*) &nAmont, + (ub4) 1, + (dvoid*) pszBuffer, + (ub4) nSize, + (dvoid*) NULL, + NULL, + (ub2) 0, + (ub1) SQLCS_IMPLICIT ), + hError ) ) + { + CPLFree( pszBuffer ); + return NULL; + } + + pszBuffer[nAmont] = '\0'; + + return pszBuffer; +} + +void OWStatement::BindName( const char* pszName, int* pnData ) +{ + OCIBind* hBind = NULL; + + CheckError( OCIBindByName( + (OCIStmt*) hStmt, + (OCIBind**) &hBind, + (OCIError*) hError, + (text*) pszName, + (sb4) -1, + (dvoid*) pnData, + (sb4) sizeof(int), + (ub2) SQLT_INT, + (dvoid*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) 0, + (ub4*) NULL, + (ub4) OCI_DEFAULT ), + hError ); +} + +void OWStatement::BindName( const char* pszName, double* pnData ) +{ + OCIBind* hBind = NULL; + + CheckError( OCIBindByName( + (OCIStmt*) hStmt, + (OCIBind**) &hBind, + (OCIError*) hError, + (text*) pszName, + (sb4) -1, + (dvoid*) pnData, + (sb4) sizeof(double), + (ub2) SQLT_BDOUBLE, + (dvoid*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) 0, + (ub4*) NULL, + (ub4) OCI_DEFAULT ), + hError ); +} + +void OWStatement::BindName( const char* pszName, char* pszData, int nSize ) +{ + OCIBind* hBind = NULL; + + CheckError( OCIBindByName( + (OCIStmt*) hStmt, + (OCIBind**) &hBind, + (OCIError*) hError, + (text*) pszName, + (sb4) -1, + (dvoid*) pszData, + (sb4) nSize, + (ub2) SQLT_STR, + (dvoid*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) 0, + (ub4*) NULL, + (ub4) OCI_DEFAULT ), + hError ); +} + +void OWStatement::BindName( const char* pszName, OCILobLocator** pphLocator ) +{ + OCIBind* hBind = NULL; + + CheckError( OCIDescriptorAlloc( + poConnection->hEnv, + (void**) pphLocator, + OCI_DTYPE_LOB, + 0, + 0), + hError ); + + CheckError( OCIBindByName( + (OCIStmt*) hStmt, + (OCIBind**) &hBind, + (OCIError*) hError, + (text*) pszName, + (sb4) -1, + (dvoid*) pphLocator, + (sb4) -1, + (ub2) SQLT_CLOB, + (dvoid*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) 0, + (ub4*) NULL, + (ub4) OCI_DEFAULT ), + hError ); +} + +void OWStatement::BindArray( void* pData, long nSize ) +{ + OCIBind* hBind = NULL; + + nNextBnd++; + + CheckError( OCIBindByPos( + hStmt, + &hBind, + hError, + (ub4) nNextBnd, + (dvoid*) pData, + (sb4) nSize * sizeof(double), + (ub2) SQLT_BIN, + (void*) NULL, + (ub2*) NULL, + (ub2*) NULL, + (ub4) NULL, + (ub4) NULL, + (ub4) OCI_DEFAULT ), hError ); + + CheckError( OCIBindArrayOfStruct( + hBind, + hError, + (ub4) nSize * sizeof(double), + (ub4) 0, + (ub4) 0, + (ub4) 0), hError ); +} + +/*****************************************************************************/ +/* Check for valid integer number in a string */ +/*****************************************************************************/ + +bool OWIsNumeric( const char *pszText ) +{ + if( pszText == NULL ) + { + return false; + } + + const char* pszPos = pszText; + + while( *pszPos != '\0' ) + { + if( *pszPos < '0' || + *pszPos > '9' ) + return false; + pszPos++; + } + + return true; +} + +/*****************************************************************************/ +/* Parse Value after a Hint on a string */ +/*****************************************************************************/ + +const char *OWParseValue( const char* pszText, + const char* pszSeparators, + const char* pszHint, + int nOffset ) +{ + if( pszText == NULL ) return 0; + + int i = 0; + int nCount = 0; + + char **papszTokens = CSLTokenizeString2( pszText, pszSeparators, + CSLT_PRESERVEQUOTES ); + + nCount = CSLCount( papszTokens ); + const char* pszResult = ""; + + for( i = 0; ( i + nOffset ) < nCount; i++ ) + { + if( EQUAL( papszTokens[i], pszHint ) ) + { + pszResult = CPLStrdup( papszTokens[i + nOffset] ); + break; + } + } + + CSLDestroy( papszTokens ); + + return pszResult; +} + +/*****************************************************************************/ +/* Parse SDO_GEOR.INIT entries */ +/*****************************************************************************/ + +/* Input Examples: + * + * "ID, RASTER, NAME VALUES (102, SDO_GEOR.INIT('RDT_80', 80), 'Nashua')" + * + */ + +const char* OWParseSDO_GEOR_INIT( const char* pszInsert, int nField ) +{ + char szUpcase[OWTEXT]; + char* pszIn = NULL; + + strcpy( szUpcase, pszInsert ); + + for( pszIn = szUpcase; *pszIn != '\0'; pszIn++ ) + { + *pszIn = (char) toupper( *pszIn ); + } + + char* pszStart = strstr( szUpcase, "SDO_GEOR.INIT" ); + + if( pszStart == NULL ) + { + return ""; + } + + char* pszEnd = strstr( pszStart, ")" ); + + if( pszEnd == NULL ) + { + return ""; + } + + pszStart += strlen("SDO_GEOR."); + + pszEnd++; + + int nLength = pszEnd - pszStart + 1; + + char szBuffer[OWTEXT]; + + strncpy( szBuffer, pszStart, nLength ); + szBuffer[nLength] = '\0'; + + const char* pszValue = OWParseValue( szBuffer, " (,)", "INIT", nField ); + + return EQUAL( pszValue, "" ) ? "NULL" : pszValue; +} + +/*****************************************************************************/ +/* Parse Release Version */ +/*****************************************************************************/ + +/* Input Examples: + * + * "Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production + * With the Partitioning, OLAP, Data Mining and Real Application Testing options" + * + */ + +int OWParseServerVersion( const char* pszText ) +{ + const char* pszValue = OWParseValue( pszText, " .", "Release", 1 ); + + return pszValue == NULL ? 0 : atoi( pszValue ); +} + +/*****************************************************************************/ +/* Parse EPSG Codes */ +/*****************************************************************************/ + +/* Input Examples: +* +* DATUM["World Geodetic System 1984 (EPSG ID 6326)", +* SPHEROID["WGS 84 (EPSG ID 7030)",6378137,298.257223563]], +* PROJECTION["UTM zone 50N (EPSG OP 16050)"], +*/ + +int OWParseEPSG( const char* pszText ) +{ + const char* pszValue = OWParseValue( pszText, " ()", "EPSG", 2 ); + + return pszValue == NULL ? 0 : atoi( pszValue ); +} + +/*****************************************************************************/ +/* Convert Data type description */ +/*****************************************************************************/ + +const GDALDataType OWGetDataType( const char* pszCellDepth ) +{ + unsigned int i; + + for( i = 0; + i < (sizeof(ahOW_CellDepth) / sizeof(OW_CellDepth)); + i++ ) + { + if( EQUAL( ahOW_CellDepth[i].pszValue, pszCellDepth ) ) + { + return ahOW_CellDepth[i].eDataType; + } + } + + return GDT_Unknown; +} + +/*****************************************************************************/ +/* Convert Data type description */ +/*****************************************************************************/ + +const char* OWSetDataType( const GDALDataType eType ) + +{ + unsigned int i; + + for( i = 0; + i < (sizeof(ahOW_CellDepth) / sizeof(OW_CellDepth)); + i++ ) + { + if( ahOW_CellDepth[i].eDataType == eType ) + { + return ahOW_CellDepth[i].pszValue; + } + } + + return "Unknown"; +} + +/*****************************************************************************/ +/* Check for Failure */ +/*****************************************************************************/ + +bool CheckError( sword nStatus, OCIError* hError ) +{ + text szMsg[OWTEXT]; + sb4 nCode = 0; + + switch ( nStatus ) + { + case OCI_SUCCESS: + return false; + break; + case OCI_NEED_DATA: + CPLError( CE_Failure, CPLE_AppDefined, "OCI_NEED_DATA\n" ); + break; + case OCI_NO_DATA: + CPLError( CE_Failure, CPLE_AppDefined, "OCI_NODATA\n" ); + break; + case OCI_INVALID_HANDLE: + CPLError( CE_Failure, CPLE_AppDefined, "OCI_INVALID_HANDLE" ); + break; + case OCI_STILL_EXECUTING: + CPLError( CE_Failure, CPLE_AppDefined, "OCI_STILL_EXECUTE\n" ); + break; + case OCI_CONTINUE: + CPLError( CE_Failure, CPLE_AppDefined, "OCI_CONTINUE\n" ); + break; + case OCI_ERROR: case OCI_SUCCESS_WITH_INFO: + + if( hError == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OCI_ERROR with no error handler" ); + } + + OCIErrorGet( (dvoid *) hError, (ub4) 1, + (text *) NULL, &nCode, szMsg, + (ub4) sizeof(szMsg), OCI_HTYPE_ERROR); + + if( nCode == 1405 ) // Null field + { + return false; + } + + CPLError( CE_Failure, CPLE_AppDefined, "%.*s", + static_cast(sizeof(szMsg)), szMsg ); + break; + + default: + + if( hError == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OCI_ERROR with no error handler" ); + } + + OCIErrorGet( (dvoid *) hError, (ub4) 1, + (text *) NULL, &nCode, szMsg, + (ub4) sizeof(szMsg), OCI_HTYPE_ERROR); + + CPLError( CE_Failure, CPLE_AppDefined, "%.*s", + static_cast(sizeof(szMsg)), szMsg ); + break; + + } + + return true; +} diff --git a/bazaar/plugin/gdal/frmts/georaster/oci_wrapper.h b/bazaar/plugin/gdal/frmts/georaster/oci_wrapper.h new file mode 100644 index 000000000..3b017324a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/georaster/oci_wrapper.h @@ -0,0 +1,412 @@ +/****************************************************************************** + * $Id: $ + * + * Name: oci_wrapper.h + * Project: Oracle Spatial GeoRaster Driver + * Purpose: Limited wrapper for OCI (Oracle Call Interfaces) + * Author: Ivan Lucena [ivan.lucena at oracle.com] + * + ****************************************************************************** + * Copyright (c) 2008, Ivan Lucena + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OCI_WRAPPER_H_INCLUDED +#define _OCI_WRAPPER_H_INCLUDED + +// GDAL supporting types + +#include "gdal.h" +#include "gdal_priv.h" + +// Oracle Class Interface + +#include + +/***************************************************************************/ +/* Data type conversion table record type */ +/***************************************************************************/ + +struct OW_CellDepth { + const char* pszValue; + GDALDataType eDataType; +}; + +/***************************************************************************/ +/* OCI Error check */ +/***************************************************************************/ + +bool CheckError( sword nStatus, OCIError* hError ); + +/***************************************************************************/ +/* Auxiliar functions */ +/***************************************************************************/ + +const GDALDataType OWGetDataType( const char* pszCellDepth ); +const char* OWSetDataType( const GDALDataType eType ); +int OWParseServerVersion( const char* pszText ); +int OWParseEPSG( const char* pszText ); +bool OWIsNumeric( const char *pszText ); +const char* OWParseSDO_GEOR_INIT( const char* pszInsert, int nField ); + +/***************************************************************************/ +/* Arbitrary limits */ +/***************************************************************************/ + +#define OWCODE 64 +#define OWNAME 512 +#define OWTEXT 1024 + +/***************************************************************************/ +/* TYPES */ +/***************************************************************************/ + +#define TYPE_OWNER "MDSYS" +#define SDO_GEOMETRY TYPE_OWNER".SDO_GEOMETRY" +#define SDO_GEORASTER TYPE_OWNER".SDO_GEORASTER" +#define SDO_PC TYPE_OWNER".SDO_PC" +#define SDO_NUMBER_ARRAY TYPE_OWNER".SDO_NUMBER_ARRAY" +#define SDO_ORDINATE_ARRAY TYPE_OWNER".SDO_ORDINATE_ARRAY" +#define SDO_ELEM_INFO_ARRAY TYPE_OWNER".SDO_ELEM_INFO_ARRAY" + +#define OW_XMLNS "xmlns=\"http://xmlns.oracle.com/spatial/georaster\"" + +/***************************************************************************/ +/* USER DEFINED (actualy Oracle's) types */ +/***************************************************************************/ + +typedef OCIRef SDO_GEORASTER_ref; +typedef OCIRef SDO_GEOMETRY_ref; +typedef OCIRef SDO_POINT_TYPE_ref; + +typedef OCIArray sdo_elem_info_array; +typedef OCIArray sdo_ordinate_array; +typedef OCIArray SDO_NUMBER_ARRAY_TYPE; + +/***************************************************************************/ +/* Point type */ +/***************************************************************************/ + +struct sdo_point_type +{ + OCINumber x; + OCINumber y; + OCINumber z; +}; + +typedef struct sdo_point_type sdo_point_type; + +struct sdo_point_type_ind +{ + OCIInd _atomic; + OCIInd x; + OCIInd y; + OCIInd z; +}; + +typedef struct sdo_point_type_ind sdo_point_type_ind; + +/***************************************************************************/ +/* Geometry type */ +/***************************************************************************/ + +struct sdo_geometry +{ + OCINumber sdo_gtype; + OCINumber sdo_srid; + sdo_point_type sdo_point; + OCIArray* sdo_elem_info; + OCIArray* sdo_ordinates; +}; + +typedef struct sdo_geometry SDO_GEOMETRY_TYPE; + +struct sdo_geometry_ind +{ + OCIInd _atomic; + OCIInd sdo_gtype; + OCIInd sdo_srid; + struct sdo_point_type_ind sdo_point; + OCIInd sdo_elem_info; + OCIInd sdo_ordinates; +}; + +typedef struct SDO_GEOMETRY_ind SDO_GEOMETRY_ind; + +/***************************************************************************/ +/* GeoRaster type */ +/***************************************************************************/ + +struct sdo_georaster +{ + OCINumber rastertype; + SDO_GEOMETRY_TYPE spatialextent; + OCIString* rasterdatatable; + OCINumber rasterid; + void* metadata; +}; + +typedef struct sdo_georaster SDO_GEORASTER_TYPE; + +struct sdo_georaster_ind +{ + OCIInd _atomic; + OCIInd rastertype; + sdo_geometry_ind spatialextent; + OCIInd rasterdatatable; + OCIInd rasterid; + OCIInd metadata; +}; + +typedef struct sdo_georaster_ind SDO_GEORASTER_ind; + +/***************************************************************************/ +/* Point Cloud type */ +/***************************************************************************/ + +struct sdo_mbr +{ + OCIArray* lower_left; + OCIArray* upper_right; +}; +typedef struct sdo_mbr SDO_MBR_TYPE; + +struct sdo_mbr_ind +{ + OCIInd _atomic; + OCIInd lower_left; + OCIInd upper_right; +}; +typedef struct sdo_mbr_ind SDO_MBR_ind; + +struct sdo_orgscl_type +{ + SDO_MBR_TYPE extent; + OCIArray* scale; + OCIArray* ord_cmp_type; +}; +typedef struct sdo_orgscl_type SDO_ORGSCL_TYPE; + +struct sdo_orgscl_type_ind +{ + OCIInd _atomic; + SDO_MBR_ind extent; + OCIInd scale; + OCIInd ord_cmp_type; +}; +typedef struct sdo_orgscl_type_ind SDO_ORGSCL_TYPE_ind; + +struct sdo_pc +{ + OCIString* base_table; + OCIString* base_column; + OCINumber pc_id; + OCIString* blk_table; + OCIString* ptn_params; + SDO_GEOMETRY_TYPE pc_geometry; + OCINumber pc_tol; + OCINumber pc_tot_dimensions; + SDO_ORGSCL_TYPE pc_domain; + OCIString* pc_val_attr_tables; + void* pc_other_attrs; +}; +typedef struct sdo_pc SDO_PC_TYPE; + +struct sdo_pc_ind +{ + OCIInd _atomic; + OCIInd base_table; + OCIInd base_column; + OCIInd pc_id; + OCIInd blk_table; + OCIInd ptn_params; + sdo_geometry_ind pc_geometry; + OCIInd pc_tol; + OCIInd pc_tot_dimensions; + OCIInd pc_domain; + OCIInd pc_val_attr_tables; + OCIInd pc_other_attrs; +}; +typedef struct sdo_pc_ind SDO_PC_ind; + +/***************************************************************************/ +/* Oracle class wrappers */ +/***************************************************************************/ + +class OWConnection; +class OWStatement; + +// --------------------------------------------------------------------------- +// OWConnection +// --------------------------------------------------------------------------- + +class OWConnection +{ + friend class OWStatement; + +public: + + OWConnection( + const char* pszUserIn, + const char* pszPasswordIn, + const char* pszServerIn ); + virtual ~OWConnection(); + +private: + + OCIEnv* hEnv; + OCIError* hError; + OCISvcCtx* hSvcCtx; + OCIServer* hServer; + OCISession* hSession; + OCIDescribe* hDescribe; + + int nVersion; + sb4 nCharSize; + + bool bSuceeeded; + + char* pszUser; + char* pszPassword; + char* pszServer; + + OCIType* hNumArrayTDO; + OCIType* hGeometryTDO; + OCIType* hGeoRasterTDO; + OCIType* hPCTDO; + OCIType* hElemArrayTDO; + OCIType* hOrdnArrayTDO; + +public: + + OWStatement* CreateStatement( const char* pszStatement ); + OCIParam* GetDescription( char* pszTable ); + bool GetNextField( + OCIParam* phTable, + int nIndex, + char* pszName, + int* pnType, + int* pnSize, + int* pnPrecision, + signed short* pnScale ); + + void CreateType( sdo_geometry** pphData ); + void DestroyType( sdo_geometry** pphData ); + void CreateType( OCIArray** phData , OCIType* type); + void DestroyType( OCIArray** phData ); + OCIType* DescribeType( const char *pszTypeName ); + + bool Succeeded() { return bSuceeeded; }; + + char* GetUser() { return pszUser; }; + char* GetPassword() { return pszPassword; }; + char* GetServer() { return pszServer; }; + int GetVersion () { return nVersion; }; + sb4 GetCharSize () { return nCharSize; }; + + OCIType* GetGeometryType() { return hGeometryTDO; } + OCIType* GetGeoRasterType() { return hGeoRasterTDO; } + OCIType* GetElemInfoType() {return hElemArrayTDO; } + OCIType* GetOrdinateType() {return hOrdnArrayTDO; } + + bool Commit(); // OCITransCommit() + bool StartTransaction(); // //OCITransStart() + bool EndTransaction() {return Commit(); } + +}; + +/***************************************************************************/ +/* OWStatement */ +/***************************************************************************/ + +class OWStatement +{ + +public: + + OWStatement( OWConnection* poConnect, + const char* pszStatement ); + virtual ~OWStatement(); + +private: + + OWConnection* poConnection; + OCIStmt* hStmt; + OCIError* hError; + + int nNextCol; + int nNextBnd; + + ub4 nStmtMode; + +public: + + bool Execute( int nRows = 1 ); + bool Fetch( int nRows = 1 ); + unsigned int nFetchCount; + + int GetInteger( OCINumber* ppoData ); + double GetDouble( OCINumber* ppoData ); + char* GetString( OCIString* ppoData ); + + void Bind( int* pnData ); + void Bind( long* pnData ); + void Bind( double* pnData ); + void Bind( char* pData, long nData ); + void Bind( sdo_geometry** pphData ); + void Bind( OCILobLocator** pphLocator ); + void Bind( OCIArray** pphData, OCIType* type ); + void Bind( char* pszData, int nSize = OWNAME ); + void Define( int* pnData ); + void Define( long* pnData ); + void Define( double* pnData ); + void Define( char* pszData, int nSize = OWNAME ); + void Define( OCILobLocator** pphLocator ); + void Define( OCIArray** pphData ); + void Define( sdo_georaster** pphData ); + void Define( sdo_geometry** pphData ); + void Define( sdo_pc** pphData ); + void Define( OCILobLocator** pphLocator, long nIterations ); + void BindName( const char* pszName, int* pnData ); + void BindName( const char* pszName, double* pnData ); + void BindName( const char* pszName, char* pszData, + int nSize = OWNAME ); + void BindName( const char* pszName, + OCILobLocator** pphLocator ); + void BindArray( void* pData, long nSize = 1); + static void Free( OCILobLocator** ppphLocator, + int nCount ); + unsigned long ReadBlob( OCILobLocator* phLocator, + void* pBuffer, int nSize ); + char* ReadCLob( OCILobLocator* phLocator ); + void WriteCLob( OCILobLocator** pphLocator, char* pszData ); + bool WriteBlob( OCILobLocator* phLocator, + void* pBuffer, int nSize ); + int GetElement( OCIArray** ppoData, + int nIndex, int* pnResult ); + double GetElement( OCIArray** ppoData, + int nIndex, double* pdfResult ); + void AddElement( OCIArray* ppoData, + int nValue ); + void AddElement( OCIArray* ppoData, + double dfValue ); +}; + +#endif /* ifndef _ORCL_WRAP_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/gff/GNUmakefile b/bazaar/plugin/gdal/frmts/gff/GNUmakefile new file mode 100644 index 000000000..2e4a360e8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gff/GNUmakefile @@ -0,0 +1,14 @@ + +include ../../GDALmake.opt + +OBJ = gff_dataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + diff --git a/bazaar/plugin/gdal/frmts/gff/gff_dataset.cpp b/bazaar/plugin/gdal/frmts/gff/gff_dataset.cpp new file mode 100644 index 000000000..da0a5207b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gff/gff_dataset.cpp @@ -0,0 +1,351 @@ +/****************************************************************************** + * $Id: gff_dataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: Ground-based SAR Applitcations Testbed File Format driver + * Purpose: Support in GDAL for Sandia National Laboratory's GFF format + * blame Tisham for putting me up to this + * Author: Philippe Vachon + * + ****************************************************************************** + * Copyright (c) 2007, Philippe Vachon + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_pam.h" +#include "cpl_port.h" +#include "cpl_conv.h" +#include "cpl_vsi.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: gff_dataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/******************************************************************* + * Declaration of the GFFDataset class * + *******************************************************************/ + +class GFFRasterBand; + +class GFFDataset : public GDALPamDataset +{ + friend class GFFRasterBand; + VSILFILE *fp; + GDALDataType eDataType; + unsigned int nEndianess; + /* Some relevant headers */ + unsigned short nVersionMajor; + unsigned short nVersionMinor; + unsigned int nLength; + //char *pszCreator; + /* I am taking this at face value (are they freakin' insane?) */ + //float fBPP; + unsigned int nBPP; + + /* Good information to know */ + unsigned int nFrameCnt; + unsigned int nImageType; + unsigned int nRowMajor; + unsigned int nRgCnt; + unsigned int nAzCnt; + //long nScaleExponent; + //long nScaleMantissa; + //long nOffsetExponent; + //long nOffsetMantissa; +public: + GFFDataset(); + ~GFFDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * poOpenInfo ); +}; + +GFFDataset::GFFDataset() +{ + fp = NULL; +} + +GFFDataset::~GFFDataset() +{ + if (fp != NULL) + VSIFCloseL(fp); +} + +/********************************************************************* + * Declaration and implementation of the GFFRasterBand Class * + *********************************************************************/ + +class GFFRasterBand : public GDALPamRasterBand { + long nRasterBandMemory; + int nSampleSize; +public: + GFFRasterBand( GFFDataset *, int, GDALDataType ); + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* GFFRasterBand() */ +/************************************************************************/ +GFFRasterBand::GFFRasterBand( GFFDataset *poDS, int nBand, + GDALDataType eDataType ) +{ + unsigned long nBytes; + this->poDS = poDS; + this->nBand = nBand; + + this->eDataType = eDataType; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + /* Determine the number of bytes per sample */ + switch (eDataType) { + case GDT_CInt16: + nBytes = 4; + break; + case GDT_CInt32: + case GDT_CFloat32: + nBytes = 8; + break; + default: + nBytes = 1; + } + + nRasterBandMemory = nBytes * poDS->GetRasterXSize(); + nSampleSize = nBytes; + +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GFFRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void *pImage ) +{ + GFFDataset *poGDS = (GFFDataset *)poDS; + long nOffset = poGDS->nLength; + + VSIFSeekL(poGDS->fp, nOffset + (poGDS->GetRasterXSize() * nBlockYOff * (nSampleSize)),SEEK_SET); + + /* Ingest entire range line */ + if (VSIFReadL(pImage,nRasterBandMemory,1,poGDS->fp) != 1) + return CE_Failure; + +#if defined(CPL_MSB) + if( GDALDataTypeIsComplex( eDataType ) ) + { + int nWordSize; + + nWordSize = GDALGetDataTypeSize(eDataType)/16; + GDALSwapWords( pImage, nWordSize, nBlockXSize, 2*nWordSize ); + GDALSwapWords( ((GByte *) pImage)+nWordSize, + nWordSize, nBlockXSize, 2*nWordSize ); + } +#endif + + return CE_None; + +} + +/******************************************************************** + * ================================================================ * + * Implementation of the GFFDataset Class * + * ================================================================ * + ********************************************************************/ + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ +int GFFDataset::Identify( GDALOpenInfo *poOpenInfo ) +{ + if(poOpenInfo->nHeaderBytes < 7) + return 0; + + if (EQUALN((char *)poOpenInfo->pabyHeader,"GSATIMG",7)) + return 1; + + return 0; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GFFDataset::Open( GDALOpenInfo *poOpenInfo ) +{ + unsigned short nCreatorLength = 0; + + /* Check that the dataset is indeed a GSAT File Format (GFF) file */ + if (!GFFDataset::Identify(poOpenInfo)) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GFF driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + GFFDataset *poDS; + poDS = new GFFDataset(); + + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r" ); + if( poDS->fp == NULL ) + { + delete poDS; + return NULL; + } + + /* Check the endianess of the file */ + VSIFSeekL(poDS->fp,54,SEEK_SET); + VSIFReadL(&(poDS->nEndianess),2,1,poDS->fp); + +#if defined(CPL_LSB) + int bSwap = 0; +#else + int bSwap = 1; +#endif + + VSIFSeekL(poDS->fp,8,SEEK_SET); + VSIFReadL(&poDS->nVersionMinor,2,1,poDS->fp); + if (bSwap) CPL_SWAP16PTR(&poDS->nVersionMinor); + VSIFReadL(&poDS->nVersionMajor,2,1,poDS->fp); + if (bSwap) CPL_SWAP16PTR(&poDS->nVersionMajor); + VSIFReadL(&poDS->nLength,4,1,poDS->fp); + if (bSwap) CPL_SWAP32PTR(&poDS->nLength); + VSIFReadL(&nCreatorLength,2,1,poDS->fp); + if (bSwap) CPL_SWAP16PTR(&nCreatorLength); + /* Hack for now... I should properly load the date metadata, for + * example + */ + VSIFSeekL(poDS->fp,56,SEEK_SET); + + /* By looking at the Matlab code, one should write something like the following test */ + /* but the results don't seem to be the ones really expected */ + /*if ((poDS->nVersionMajor == 1 && poDS->nVersionMinor > 7) || (poDS->nVersionMajor > 1)) + { + float fBPP; + VSIFRead(&fBPP,4,1,poDS->fp); + poDS->nBPP = fBPP; + } + else*/ + { + VSIFReadL(&poDS->nBPP,4,1,poDS->fp); + if (bSwap) CPL_SWAP32PTR(&poDS->nBPP); + } + VSIFReadL(&poDS->nFrameCnt,4,1,poDS->fp); + if (bSwap) CPL_SWAP32PTR(&poDS->nFrameCnt); + VSIFReadL(&poDS->nImageType,4,1,poDS->fp); + if (bSwap) CPL_SWAP32PTR(&poDS->nImageType); + VSIFReadL(&poDS->nRowMajor,4,1,poDS->fp); + if (bSwap) CPL_SWAP32PTR(&poDS->nRowMajor); + VSIFReadL(&poDS->nRgCnt,4,1,poDS->fp); + if (bSwap) CPL_SWAP32PTR(&poDS->nRgCnt); + VSIFReadL(&poDS->nAzCnt,4,1,poDS->fp); + if (bSwap) CPL_SWAP32PTR(&poDS->nAzCnt); + + /* We now have enough information to determine the number format */ + switch (poDS->nImageType) { + case 0: + poDS->eDataType = GDT_Byte; + break; + + case 1: + if (poDS->nBPP == 4) + poDS->eDataType = GDT_CInt16; + else + poDS->eDataType = GDT_CInt32; + break; + + case 2: + poDS->eDataType = GDT_CFloat32; + break; + + default: + CPLError(CE_Failure, CPLE_AppDefined, "Unknown image type found!"); + delete poDS; + return NULL; + } + + /* Set raster width/height + * Note that the images that are complex are listed as having twice the + * number of X-direction values than there are actual pixels. This is + * because whoever came up with the format was crazy (actually, my + * hunch is that they designed it very much for Matlab) + * */ + if (poDS->nRowMajor) { + poDS->nRasterXSize = poDS->nRgCnt/(poDS->nImageType == 0 ? 1 : 2); + poDS->nRasterYSize = poDS->nAzCnt; + } + else { + poDS->nRasterXSize = poDS->nAzCnt/(poDS->nImageType == 0 ? 1 : 2); + poDS->nRasterYSize = poDS->nRgCnt; + } + + if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid raster dimensions : %d x %d", + poDS->nRasterXSize, poDS->nRasterYSize); + delete poDS; + return NULL; + } + + poDS->SetBand(1, new GFFRasterBand(poDS, 1, poDS->eDataType)); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_GFF() */ +/************************************************************************/ + +void GDALRegister_GFF(void) +{ + GDALDriver *poDriver; + if ( GDALGetDriverByName("GFF") == NULL ) { + poDriver = new GDALDriver(); + poDriver->SetDescription("GFF"); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, + "Ground-based SAR Applications Testbed File Format (.gff)"); + poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "frmt_various.html#GFF"); + poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gff"); + poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); + poDriver->pfnOpen = GFFDataset::Open; + GetGDALDriverManager()->RegisterDriver(poDriver); + } +} diff --git a/bazaar/plugin/gdal/frmts/gff/makefile.vc b/bazaar/plugin/gdal/frmts/gff/makefile.vc new file mode 100644 index 000000000..48b8abcc1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gff/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = gff_dataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/gif/GNUmakefile b/bazaar/plugin/gdal/frmts/gif/GNUmakefile new file mode 100644 index 000000000..d625298e0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/GNUmakefile @@ -0,0 +1,28 @@ + +include ../../GDALmake.opt + +ifeq ($(GIF_SETTING),internal) +XTRA_OPT = -Igiflib +OBJ = egif_lib.o dgif_lib.o gifalloc.o gif_err.o gif_hash.o \ + \ + gifdataset.o biggifdataset.o gifabstractdataset.o +else +OBJ = gifdataset.o biggifdataset.o gifabstractdataset.o +endif + +CPPFLAGS := $(CPPFLAGS) $(XTRA_OPT) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +$(O_OBJ): gifabstractdataset.h + +clean: + rm -f *.o $(O_OBJ) + +../o/%.$(OBJ_EXT): giflib/%.c + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/gif/biggifdataset.cpp b/bazaar/plugin/gdal/frmts/gif/biggifdataset.cpp new file mode 100644 index 000000000..7f95a8010 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/biggifdataset.cpp @@ -0,0 +1,425 @@ +/****************************************************************************** + * $Id: biggifdataset.cpp 28279 2015-01-03 14:37:52Z rouault $ + * + * Project: BIGGIF Driver + * Purpose: Implement GDAL support for reading large GIF files in a + * streaming fashion rather than the slurp-into-memory approach + * of the normal GIF driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001-2008, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "gifabstractdataset.h" + +CPL_CVSID("$Id: biggifdataset.cpp 28279 2015-01-03 14:37:52Z rouault $"); + +CPL_C_START +void GDALRegister_BIGGIF(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* BIGGIFDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class BIGGifRasterBand; + +class BIGGIFDataset : public GIFAbstractDataset +{ + friend class BIGGifRasterBand; + + int nLastLineRead; + + GDALDataset *poWorkDS; + + CPLErr ReOpen(); + + protected: + virtual int CloseDependentDatasets(); + + public: + BIGGIFDataset(); + ~BIGGIFDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* BIGGifRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class BIGGifRasterBand : public GIFAbstractRasterBand +{ + friend class BIGGIFDataset; + + public: + + BIGGifRasterBand( BIGGIFDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* BIGGifRasterBand() */ +/************************************************************************/ + +BIGGifRasterBand::BIGGifRasterBand( BIGGIFDataset *poDS, int nBackground ) : + GIFAbstractRasterBand(poDS, 1, poDS->hGifFile->SavedImages, nBackground, TRUE) + +{ +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr BIGGifRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + BIGGIFDataset *poGDS = (BIGGIFDataset *) poDS; + + CPLAssert( nBlockXOff == 0 ); + + if( panInterlaceMap != NULL ) + nBlockYOff = panInterlaceMap[nBlockYOff]; + +/* -------------------------------------------------------------------- */ +/* Do we already have this line in the work dataset? */ +/* -------------------------------------------------------------------- */ + if( poGDS->poWorkDS != NULL && nBlockYOff <= poGDS->nLastLineRead ) + { + return poGDS->poWorkDS-> + RasterIO( GF_Read, 0, nBlockYOff, nBlockXSize, 1, + pImage, nBlockXSize, 1, GDT_Byte, + 1, NULL, 0, 0, 0, NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Do we need to restart from the start of the image? */ +/* -------------------------------------------------------------------- */ + if( nBlockYOff <= poGDS->nLastLineRead ) + { + if( poGDS->ReOpen() == CE_Failure ) + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Read till we get our target line. */ +/* -------------------------------------------------------------------- */ + while( poGDS->nLastLineRead < nBlockYOff ) + { + if( DGifGetLine( poGDS->hGifFile, (GifPixelType*)pImage, + nBlockXSize ) == GIF_ERROR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failure decoding scanline of GIF file." ); + return CE_Failure; + } + + poGDS->nLastLineRead++; + + if( poGDS->poWorkDS != NULL ) + { + poGDS->poWorkDS->RasterIO( GF_Write, + 0, poGDS->nLastLineRead, nBlockXSize, 1, + pImage, nBlockXSize, 1, GDT_Byte, + 1, NULL, 0, 0, 0, NULL ); + } + } + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* BIGGIFDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* BIGGIFDataset() */ +/************************************************************************/ + +BIGGIFDataset::BIGGIFDataset() + +{ + nLastLineRead = -1; + poWorkDS = NULL; +} + +/************************************************************************/ +/* ~BIGGIFDataset() */ +/************************************************************************/ + +BIGGIFDataset::~BIGGIFDataset() + +{ + FlushCache(); + + CloseDependentDatasets(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int BIGGIFDataset::CloseDependentDatasets() +{ + int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets(); + + if( poWorkDS != NULL ) + { + bHasDroppedRef = TRUE; + + CPLString osTempFilename = poWorkDS->GetDescription(); + GDALDriver* poDrv = poWorkDS->GetDriver(); + + GDALClose( (GDALDatasetH) poWorkDS ); + poWorkDS = NULL; + + if( poDrv != NULL ) + poDrv->Delete( osTempFilename ); + + poWorkDS = NULL; + } + + return bHasDroppedRef; +} + +/************************************************************************/ +/* ReOpen() */ +/* */ +/* (Re)Open the gif file and process past the first image */ +/* descriptor. */ +/************************************************************************/ + +CPLErr BIGGIFDataset::ReOpen() + +{ +/* -------------------------------------------------------------------- */ +/* If the file is already open, close it so we can restart. */ +/* -------------------------------------------------------------------- */ + if( hGifFile != NULL ) + GIFAbstractDataset::myDGifCloseFile( hGifFile ); + +/* -------------------------------------------------------------------- */ +/* If we are actually reopening, then we assume that access to */ +/* the image data is not strictly once through sequential, and */ +/* we will try to create a working database in a temporary */ +/* directory to hold the image as we read through it the second */ +/* time. */ +/* -------------------------------------------------------------------- */ + if( hGifFile != NULL ) + { + GDALDriver *poGTiffDriver = (GDALDriver*) GDALGetDriverByName("GTiff"); + + if( poGTiffDriver != NULL ) + { + /* Create as a sparse file to avoid filling up the whole file */ + /* while closing and then destroying this temporary dataset */ + const char* apszOptions[] = { "COMPRESS=LZW", "SPARSE_OK=YES", NULL }; + CPLString osTempFilename = CPLGenerateTempFilename("biggif"); + + osTempFilename += ".tif"; + + poWorkDS = poGTiffDriver->Create( osTempFilename, + nRasterXSize, nRasterYSize, 1, + GDT_Byte, const_cast(apszOptions)); + } + } + +/* -------------------------------------------------------------------- */ +/* Open */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( fp, 0, SEEK_SET ); + + nLastLineRead = -1; + hGifFile = GIFAbstractDataset::myDGifOpen( fp, GIFAbstractDataset::ReadFunc ); + if( hGifFile == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "DGifOpen() failed. Perhaps the gif file is corrupt?\n" ); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Find the first image record. */ +/* -------------------------------------------------------------------- */ + GifRecordType RecordType = TERMINATE_RECORD_TYPE; + + while( DGifGetRecordType(hGifFile, &RecordType) != GIF_ERROR + && RecordType != TERMINATE_RECORD_TYPE + && RecordType != IMAGE_DESC_RECORD_TYPE ) + { + /* Skip extension records found before IMAGE_DESC_RECORD_TYPE */ + if (RecordType == EXTENSION_RECORD_TYPE) + { + int nFunction; + GifByteType *pExtData; + if (DGifGetExtension(hGifFile, &nFunction, &pExtData) == GIF_ERROR) + break; + while (pExtData != NULL) + { + if (DGifGetExtensionNext(hGifFile, &pExtData) == GIF_ERROR) + break; + } + } + } + + if( RecordType != IMAGE_DESC_RECORD_TYPE ) + { + GIFAbstractDataset::myDGifCloseFile( hGifFile ); + hGifFile = NULL; + + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to find image description record in GIF file." ); + return CE_Failure; + } + + if (DGifGetImageDesc(hGifFile) == GIF_ERROR) + { + GIFAbstractDataset::myDGifCloseFile( hGifFile ); + hGifFile = NULL; + + CPLError( CE_Failure, CPLE_OpenFailed, + "Image description reading failed in GIF file." ); + return CE_Failure; + } + + return CE_None; +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *BIGGIFDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) || poOpenInfo->fpL == NULL ) + return NULL; + + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GIF driver does not support update access to existing" + " files.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + BIGGIFDataset *poDS; + + poDS = new BIGGIFDataset(); + + poDS->fp = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + poDS->eAccess = GA_ReadOnly; + if( poDS->ReOpen() == CE_Failure ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + + poDS->nRasterXSize = poDS->hGifFile->SavedImages[0].ImageDesc.Width; + poDS->nRasterYSize = poDS->hGifFile->SavedImages[0].ImageDesc.Height; + if( poDS->hGifFile->SavedImages[0].ImageDesc.ColorMap == NULL && + poDS->hGifFile->SColorMap == NULL ) + { + CPLDebug("GIF", "Skipping image without color table"); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->SetBand( 1, + new BIGGifRasterBand( poDS, + poDS->hGifFile->SBackGroundColor )); + +/* -------------------------------------------------------------------- */ +/* Check for georeferencing. */ +/* -------------------------------------------------------------------- */ + poDS->DetectGeoreferencing(poOpenInfo); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML( poOpenInfo->GetSiblingFiles() ); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, + poOpenInfo->GetSiblingFiles() ); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_BIGGIF() */ +/************************************************************************/ + +void GDALRegister_BIGGIF() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "BIGGIF" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "BIGGIF" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Graphics Interchange Format (.gif)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_gif.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gif" ); + poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/gif" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = BIGGIFDataset::Open; + poDriver->pfnIdentify = GIFAbstractDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/gif/frmt_gif.html b/bazaar/plugin/gdal/frmts/gif/frmt_gif.html new file mode 100644 index 000000000..fdb0e3004 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/frmt_gif.html @@ -0,0 +1,51 @@ + + +GIF -- Graphics Interchange Format + + + + +

GIF -- Graphics Interchange Format

+ +GDAL supports reading and writing of normal, and interlaced GIF files. Gif +files always appear as having one colormapped eight bit band. GIF files have +no support for georeferencing.

+ +A GIF image with transparency will have that entry marked as having +an alpha value of 0.0 (transparent). Also, the transparent value will +be returned as the NoData value for the band.

+ +If an ESRI world file exists with the .gfw, .gifw or .wld extension, it will be read and +used to establish the geotransform for the image.

+ +Starting with GDAL 1.9.0, XMP metadata can be extracted from the file, and will be +stored as XML raw content in the xml:XMP metadata domain.

+ +

Creation Issues

+ +GIF files can only be created as 1 8bit band using the "CreateCopy" mechanism. +If written from a file that is not colormapped, a default greyscale colormap +is generated. Tranparent GIFs are not currently supported on creation.

+ +WORLDFILE=ON: Force the generation of an associated ESRI world +file (.wld).

+ +Interlaced (progressive) GIF files can be generated by supplying the +INTERLACING=ON option on creation.

+ +Starting with GDAL 1.7.0, GDAL's internal GIF support is implemented based +on source from the giflib 4.1.6 library (written by Gershon Elbor, Eric Raymond +and Toshio Kuratomi), hence generating LZW compressed GIF.

+ +The driver was written with the financial support of the DM +Solutions Group, and CIET +International.

+ +See Also:

+ +

+ + + diff --git a/bazaar/plugin/gdal/frmts/gif/gifabstractdataset.cpp b/bazaar/plugin/gdal/frmts/gif/gifabstractdataset.cpp new file mode 100644 index 000000000..c9d2b3b1a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/gifabstractdataset.cpp @@ -0,0 +1,585 @@ +/****************************************************************************** + * $Id: gifabstractdataset.cpp 29222 2015-05-21 15:06:39Z rouault $ + * + * Project: GIF Driver + * Purpose: GIF Abstract Dataset + * Author: Even Rouault + * + **************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gifabstractdataset.h" + +CPL_CVSID("$Id: gifabstractdataset.cpp 29222 2015-05-21 15:06:39Z rouault $"); + +static const int InterlacedOffset[] = { 0, 4, 2, 1 }; +static const int InterlacedJumps[] = { 8, 8, 4, 2 }; + +/************************************************************************/ +/* ==================================================================== */ +/* GIFAbstractDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* GIFAbstractDataset() */ +/************************************************************************/ + +GIFAbstractDataset::GIFAbstractDataset() + +{ + hGifFile = NULL; + fp = NULL; + + pszProjection = NULL; + bGeoTransformValid = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + nGCPCount = 0; + pasGCPList = NULL; + + bHasReadXMPMetadata = FALSE; +} + +/************************************************************************/ +/* ~GIFAbstractDataset() */ +/************************************************************************/ + +GIFAbstractDataset::~GIFAbstractDataset() + +{ + FlushCache(); + + if ( pszProjection ) + CPLFree( pszProjection ); + + if ( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + if( hGifFile ) + myDGifCloseFile( hGifFile ); + + if( fp != NULL ) + VSIFCloseL( fp ); +} + + +/************************************************************************/ +/* GIFCollectXMPMetadata() */ +/************************************************************************/ + +/* See §2.1.2 of http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf */ + +static CPLString GIFCollectXMPMetadata(VSILFILE* fp) + +{ + CPLString osXMP; + + /* Save current position to avoid disturbing GIF stream decoding */ + vsi_l_offset nCurOffset = VSIFTellL(fp); + + char abyBuffer[2048+1]; + + VSIFSeekL( fp, 0, SEEK_SET ); + + /* Loop over file */ + + int iStartSearchOffset = 1024; + while(TRUE) + { + int nRead = VSIFReadL( abyBuffer + 1024, 1, 1024, fp ); + if (nRead <= 0) + break; + abyBuffer[1024 + nRead] = 0; + + int i; + int iFoundOffset = -1; + for(i=iStartSearchOffset;i<1024+nRead - 14;i++) + { + if (memcmp(abyBuffer + i, "\x21\xff\x0bXMP DataXMP", 14) == 0) + { + iFoundOffset = i + 14; + break; + } + } + + iStartSearchOffset = 0; + + if (iFoundOffset >= 0) + { + int nSize = 1024 + nRead - iFoundOffset; + char* pszXMP = (char*)VSIMalloc(nSize + 1); + if (pszXMP == NULL) + break; + + pszXMP[nSize] = 0; + memcpy(pszXMP, abyBuffer + iFoundOffset, nSize); + + /* Read from file until we find a NUL character */ + int nLen = (int)strlen(pszXMP); + while(nLen == nSize) + { + char* pszNewXMP = (char*)VSIRealloc(pszXMP, nSize + 1024 + 1); + if (pszNewXMP == NULL) + break; + pszXMP = pszNewXMP; + + nRead = VSIFReadL( pszXMP + nSize, 1, 1024, fp ); + if (nRead <= 0) + break; + + pszXMP[nSize + nRead] = 0; + nLen += (int)strlen(pszXMP + nSize); + nSize += nRead; + } + + if (nLen > 256 && pszXMP[nLen - 1] == '\x01' && + pszXMP[nLen - 2] == '\x02' && pszXMP[nLen - 255] == '\xff' && + pszXMP[nLen - 256] == '\x01') + { + pszXMP[nLen - 256] = 0; + + osXMP = pszXMP; + } + + VSIFree(pszXMP); + + break; + } + + if (nRead != 1024) + break; + + memcpy(abyBuffer, abyBuffer + 1024, 1024); + } + + VSIFSeekL( fp, nCurOffset, SEEK_SET ); + + return osXMP; +} + +/************************************************************************/ +/* CollectXMPMetadata() */ +/************************************************************************/ + +void GIFAbstractDataset::CollectXMPMetadata() + +{ + if (fp == NULL || bHasReadXMPMetadata) + return; + + CPLString osXMP = GIFCollectXMPMetadata(fp); + if (osXMP.size()) + { + /* Avoid setting the PAM dirty bit just for that */ + int nOldPamFlags = nPamFlags; + + char *apszMDList[2]; + apszMDList[0] = (char*) osXMP.c_str(); + apszMDList[1] = NULL; + SetMetadata(apszMDList, "xml:XMP"); + + nPamFlags = nOldPamFlags; + } + + bHasReadXMPMetadata = TRUE; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **GIFAbstractDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "xml:XMP", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GIFAbstractDataset::GetMetadata( const char * pszDomain ) +{ + if (fp == NULL) + return NULL; + if (eAccess == GA_ReadOnly && !bHasReadXMPMetadata && + (pszDomain != NULL && EQUAL(pszDomain, "xml:XMP"))) + CollectXMPMetadata(); + return GDALPamDataset::GetMetadata(pszDomain); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GIFAbstractDataset::GetProjectionRef() + +{ + if ( pszProjection && bGeoTransformValid ) + return pszProjection; + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GIFAbstractDataset::GetGeoTransform( double * padfTransform ) + +{ + if( bGeoTransformValid ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + return CE_None; + } + else + return GDALPamDataset::GetGeoTransform( padfTransform ); +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int GIFAbstractDataset::GetGCPCount() + +{ + if (nGCPCount > 0) + return nGCPCount; + else + return GDALPamDataset::GetGCPCount(); +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *GIFAbstractDataset::GetGCPProjection() + +{ + if ( pszProjection && nGCPCount > 0 ) + return pszProjection; + else + return GDALPamDataset::GetGCPProjection(); +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *GIFAbstractDataset::GetGCPs() + +{ + if (nGCPCount > 0) + return pasGCPList; + else + return GDALPamDataset::GetGCPs(); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int GIFAbstractDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 8 ) + return FALSE; + + if( strncmp((const char *) poOpenInfo->pabyHeader, "GIF87a",5) != 0 + && strncmp((const char *) poOpenInfo->pabyHeader, "GIF89a",5) != 0 ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **GIFAbstractDataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + + if (osWldFilename.size() != 0 && + CSLFindString(papszFileList, osWldFilename) == -1) + { + papszFileList = CSLAddString( papszFileList, osWldFilename ); + } + + return papszFileList; +} + +/************************************************************************/ +/* DetectGeoreferencing() */ +/************************************************************************/ + +void GIFAbstractDataset::DetectGeoreferencing( GDALOpenInfo * poOpenInfo ) +{ + char* pszWldFilename = NULL; + + bGeoTransformValid = + GDALReadWorldFile2( poOpenInfo->pszFilename, NULL, + adfGeoTransform, poOpenInfo->GetSiblingFiles(), + &pszWldFilename ); + if ( !bGeoTransformValid ) + { + bGeoTransformValid = + GDALReadWorldFile2( poOpenInfo->pszFilename, ".wld", + adfGeoTransform, poOpenInfo->GetSiblingFiles(), + &pszWldFilename ); + } + + if (pszWldFilename) + { + osWldFilename = pszWldFilename; + CPLFree(pszWldFilename); + } +} + +/************************************************************************/ +/* myDGifOpen() */ +/************************************************************************/ + +GifFileType* GIFAbstractDataset::myDGifOpen( void *userPtr, InputFunc readFunc ) +{ +#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5 + int nErrorCode; + return DGifOpen( userPtr, readFunc, &nErrorCode ); +#else + return DGifOpen( userPtr, readFunc ); +#endif +} + +/************************************************************************/ +/* myDGifCloseFile() */ +/************************************************************************/ + +int GIFAbstractDataset::myDGifCloseFile( GifFileType *hGifFile ) +{ +#if defined(GIFLIB_MAJOR) && ((GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || GIFLIB_MAJOR > 5) + int nErrorCode; + return DGifCloseFile( hGifFile, &nErrorCode ); +#else + return DGifCloseFile( hGifFile ); +#endif +} + +/************************************************************************/ +/* myEGifCloseFile() */ +/************************************************************************/ + +int GIFAbstractDataset::myEGifCloseFile( GifFileType *hGifFile ) +{ +#if defined(GIFLIB_MAJOR) && ((GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || GIFLIB_MAJOR > 5) + int nErrorCode; + return EGifCloseFile( hGifFile, &nErrorCode ); +#else + return EGifCloseFile( hGifFile ); +#endif +} + +/************************************************************************/ +/* VSIGIFReadFunc() */ +/* */ +/* Proxy function for reading from GIF file. */ +/************************************************************************/ + +int GIFAbstractDataset::ReadFunc( GifFileType *psGFile, GifByteType *pabyBuffer, + int nBytesToRead ) + +{ + return VSIFReadL( pabyBuffer, 1, nBytesToRead, + (VSILFILE *) psGFile->UserData ); +} + +/************************************************************************/ +/* GIFAbstractRasterBand() */ +/************************************************************************/ + +GIFAbstractRasterBand::GIFAbstractRasterBand( + GIFAbstractDataset *poDS, int nBand, + SavedImage *psSavedImage, int nBackground, + int bAdvertizeInterlacedMDI ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Byte; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + psImage = psSavedImage; + + poColorTable = NULL; + panInterlaceMap = NULL; + nTransparentColor = 0; + + if (psImage == NULL) + return; + +/* -------------------------------------------------------------------- */ +/* Setup interlacing map if required. */ +/* -------------------------------------------------------------------- */ + panInterlaceMap = NULL; + if( psImage->ImageDesc.Interlace ) + { + int i, j, iLine = 0; + + if( bAdvertizeInterlacedMDI ) + poDS->SetMetadataItem( "INTERLACED", "YES", "IMAGE_STRUCTURE" ); + + panInterlaceMap = (int *) CPLCalloc(poDS->nRasterYSize,sizeof(int)); + + for (i = 0; i < 4; i++) + { + for (j = InterlacedOffset[i]; + j < poDS->nRasterYSize; + j += InterlacedJumps[i]) + panInterlaceMap[j] = iLine++; + } + } + else if( bAdvertizeInterlacedMDI ) + poDS->SetMetadataItem( "INTERLACED", "NO", "IMAGE_STRUCTURE" ); + +/* -------------------------------------------------------------------- */ +/* Check for transparency. We just take the first graphic */ +/* control extension block we find, if any. */ +/* -------------------------------------------------------------------- */ + int iExtBlock; + + nTransparentColor = -1; + for( iExtBlock = 0; iExtBlock < psImage->ExtensionBlockCount; iExtBlock++ ) + { + unsigned char *pExtData; + + if( psImage->ExtensionBlocks[iExtBlock].Function != 0xf9 || + psImage->ExtensionBlocks[iExtBlock].ByteCount < 4 ) + continue; + + pExtData = (unsigned char *) psImage->ExtensionBlocks[iExtBlock].Bytes; + + /* check if transparent color flag is set */ + if( !(pExtData[0] & 0x1) ) + continue; + + nTransparentColor = pExtData[3]; + } + +/* -------------------------------------------------------------------- */ +/* Setup colormap. */ +/* -------------------------------------------------------------------- */ + ColorMapObject *psGifCT = psImage->ImageDesc.ColorMap; + if( psGifCT == NULL ) + psGifCT = poDS->hGifFile->SColorMap; + + poColorTable = new GDALColorTable(); + for( int iColor = 0; iColor < psGifCT->ColorCount; iColor++ ) + { + GDALColorEntry oEntry; + + oEntry.c1 = psGifCT->Colors[iColor].Red; + oEntry.c2 = psGifCT->Colors[iColor].Green; + oEntry.c3 = psGifCT->Colors[iColor].Blue; + + if( iColor == nTransparentColor ) + oEntry.c4 = 0; + else + oEntry.c4 = 255; + + poColorTable->SetColorEntry( iColor, &oEntry ); + } + +/* -------------------------------------------------------------------- */ +/* If we have a background value, return it here. Some */ +/* applications might want to treat this as transparent, but in */ +/* many uses this is inappropriate so we don't return it as */ +/* nodata or transparent. */ +/* -------------------------------------------------------------------- */ + if( nBackground != 255 ) + { + char szBackground[10]; + + sprintf( szBackground, "%d", nBackground ); + SetMetadataItem( "GIF_BACKGROUND", szBackground ); + } +} + +/************************************************************************/ +/* ~GIFAbstractRasterBand() */ +/************************************************************************/ + +GIFAbstractRasterBand::~GIFAbstractRasterBand() + +{ + if( poColorTable != NULL ) + delete poColorTable; + + CPLFree( panInterlaceMap ); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp GIFAbstractRasterBand::GetColorInterpretation() + +{ + return GCI_PaletteIndex; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *GIFAbstractRasterBand::GetColorTable() + +{ + return poColorTable; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double GIFAbstractRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = nTransparentColor != -1; + + return nTransparentColor; +} diff --git a/bazaar/plugin/gdal/frmts/gif/gifabstractdataset.h b/bazaar/plugin/gdal/frmts/gif/gifabstractdataset.h new file mode 100644 index 000000000..66c9ddf2a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/gifabstractdataset.h @@ -0,0 +1,122 @@ +/****************************************************************************** + * $Id: gifabstractdataset.h 29222 2015-05-21 15:06:39Z rouault $ + * + * Project: GIF Driver + * Purpose: GIF Abstract Dataset + * Author: Even Rouault + * + **************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _GIFABSTRACTDATASET_H_INCLUDED +#define _GIFABSTRACTDATASET_H_INCLUDED + +#include "gdal_pam.h" + +CPL_C_START +#include "gif_lib.h" +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* GIFAbstractDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GIFAbstractDataset : public GDALPamDataset +{ + protected: + friend class GIFAbstractRasterBand; + + VSILFILE *fp; + + GifFileType *hGifFile; + + char *pszProjection; + int bGeoTransformValid; + double adfGeoTransform[6]; + + int nGCPCount; + GDAL_GCP *pasGCPList; + + int bHasReadXMPMetadata; + void CollectXMPMetadata(); + + CPLString osWldFilename; + + void DetectGeoreferencing( GDALOpenInfo * poOpenInfo ); + + public: + GIFAbstractDataset(); + ~GIFAbstractDataset(); + + virtual const char *GetProjectionRef(); + virtual CPLErr GetGeoTransform( double * ); + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain = "" ); + + virtual char **GetFileList(void); + + static int Identify( GDALOpenInfo * ); + + static GifFileType* myDGifOpen( void *userPtr, InputFunc readFunc ); + static int myDGifCloseFile( GifFileType *hGifFile ); + static int myEGifCloseFile( GifFileType *hGifFile ); + static int ReadFunc( GifFileType *psGFile, GifByteType *pabyBuffer, + int nBytesToRead ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GIFAbstractRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GIFAbstractRasterBand : public GDALPamRasterBand +{ + protected: + SavedImage *psImage; + + int *panInterlaceMap; + + GDALColorTable *poColorTable; + + int nTransparentColor; + + public: + + GIFAbstractRasterBand(GIFAbstractDataset *poDS, int nBand, + SavedImage *psSavedImage, int nBackground, + int bAdvertizeInterlacedMDI ); + virtual ~GIFAbstractRasterBand(); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); +}; + + +#endif diff --git a/bazaar/plugin/gdal/frmts/gif/gifdataset.cpp b/bazaar/plugin/gdal/frmts/gif/gifdataset.cpp new file mode 100644 index 000000000..1483b2fcc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/gifdataset.cpp @@ -0,0 +1,739 @@ +/****************************************************************************** + * $Id: gifdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $ + * + * Project: GIF Driver + * Purpose: Implement GDAL GIF Support using libungif code. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2007-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "gifabstractdataset.h" + +CPL_CVSID("$Id: gifdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $"); + +CPL_C_START +void GDALRegister_GIF(void); + +#if !(defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5) + +// This prototype seems to have been messed up! +GifFileType * EGifOpen(void* userData, OutputFunc writeFunc); + +// Define alias compatible with giflib >= 5.0.0 +#define GifMakeMapObject MakeMapObject +#define GifFreeMapObject FreeMapObject + +#endif // defined(GIFLIB_MAJOR) && GIFLIB_MAJOR < 5 + +CPL_C_END + +static const int InterlacedOffset[] = { 0, 4, 2, 1 }; +static const int InterlacedJumps[] = { 8, 8, 4, 2 }; + +static int VSIGIFWriteFunc( GifFileType *, const GifByteType *, int ); + +/************************************************************************/ +/* ==================================================================== */ +/* GIFDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GIFRasterBand; + +class GIFDataset : public GIFAbstractDataset +{ + friend class GIFRasterBand; + + public: + GIFDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + + static GDALDataset* CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GIFRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GIFRasterBand : public GIFAbstractRasterBand +{ + public: + + GIFRasterBand( GIFDataset *, int, SavedImage *, int ); + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* GIFRasterBand() */ +/************************************************************************/ + +GIFRasterBand::GIFRasterBand( GIFDataset *poDS, int nBand, + SavedImage *psSavedImage, int nBackground ) : + GIFAbstractRasterBand(poDS, nBand, psSavedImage, nBackground, FALSE) + +{ +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GIFRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + CPLAssert( nBlockXOff == 0 ); + + if (psImage == NULL) + { + memset(pImage, 0, nBlockXSize); + return CE_None; + } + + if( panInterlaceMap != NULL ) + nBlockYOff = panInterlaceMap[nBlockYOff]; + + memcpy( pImage, psImage->RasterBits + nBlockYOff * nBlockXSize, + nBlockXSize ); + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GIFDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* GIFDataset() */ +/************************************************************************/ + +GIFDataset::GIFDataset() +{ +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GIFDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) || poOpenInfo->fpL == NULL ) + return NULL; + + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GIF driver does not support update access to existing" + " files.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Ingest. */ +/* -------------------------------------------------------------------- */ + GifFileType *hGifFile; + VSILFILE *fp; + int nGifErr; + + fp = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + + hGifFile = GIFAbstractDataset::myDGifOpen( fp, GIFAbstractDataset::ReadFunc ); + if( hGifFile == NULL ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_OpenFailed, + "DGifOpen() failed for %s.\n" + "Perhaps the gif file is corrupt?\n", + poOpenInfo->pszFilename ); + + return NULL; + } + + /* The following code enables us to detect GIF datasets eligible */ + /* for BIGGIF driver even with an unpatched giflib */ + + /* -------------------------------------------------------------------- */ + /* Find the first image record. */ + /* -------------------------------------------------------------------- */ + GifRecordType RecordType = TERMINATE_RECORD_TYPE; + + while( DGifGetRecordType(hGifFile, &RecordType) != GIF_ERROR + && RecordType != TERMINATE_RECORD_TYPE + && RecordType != IMAGE_DESC_RECORD_TYPE ) + { + /* Skip extension records found before IMAGE_DESC_RECORD_TYPE */ + if (RecordType == EXTENSION_RECORD_TYPE) + { + int nFunction; + GifByteType *pExtData; + if (DGifGetExtension(hGifFile, &nFunction, &pExtData) == GIF_ERROR) + break; + while (pExtData != NULL) + { + if (DGifGetExtensionNext(hGifFile, &pExtData) == GIF_ERROR) + break; + } + } + } + + if( RecordType == IMAGE_DESC_RECORD_TYPE && + DGifGetImageDesc(hGifFile) != GIF_ERROR) + { + int width = hGifFile->SavedImages[0].ImageDesc.Width; + int height = hGifFile->SavedImages[0].ImageDesc.Height; + if ((double) width * (double) height > 100000000.0 ) + { + CPLDebug( "GIF", + "Due to limitations of the GDAL GIF driver we deliberately avoid\n" + "opening large GIF files (larger than 100 megapixels)."); + GIFAbstractDataset::myDGifCloseFile( hGifFile ); + /* Reset poOpenInfo->fpL since BIGGIF may need it */ + poOpenInfo->fpL = fp; + VSIFSeekL(fp, 0, SEEK_SET); + return NULL; + } + } + + GIFAbstractDataset::myDGifCloseFile( hGifFile ); + + VSIFSeekL( fp, 0, SEEK_SET); + + hGifFile = GIFAbstractDataset::myDGifOpen( fp, GIFAbstractDataset::ReadFunc ); + if( hGifFile == NULL ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_OpenFailed, + "DGifOpen() failed for %s.\n" + "Perhaps the gif file is corrupt?\n", + poOpenInfo->pszFilename ); + + return NULL; + } + + nGifErr = DGifSlurp( hGifFile ); + + if( nGifErr != GIF_OK || hGifFile->SavedImages == NULL ) + { + VSIFCloseL( fp ); + GIFAbstractDataset::myDGifCloseFile(hGifFile); + + if( nGifErr == D_GIF_ERR_DATA_TOO_BIG ) + { + CPLDebug( "GIF", + "DGifSlurp() failed for %s because it was too large.\n" + "Due to limitations of the GDAL GIF driver we deliberately avoid\n" + "opening large GIF files (larger than 100 megapixels).", + poOpenInfo->pszFilename ); + return NULL; + } + else + CPLError( CE_Failure, CPLE_OpenFailed, + "DGifSlurp() failed for %s.\n" + "Perhaps the gif file is corrupt?\n", + poOpenInfo->pszFilename ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GIFDataset *poDS; + + poDS = new GIFDataset(); + + poDS->fp = fp; + poDS->eAccess = GA_ReadOnly; + poDS->hGifFile = hGifFile; + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = hGifFile->SavedImages[0].ImageDesc.Width; + poDS->nRasterYSize = hGifFile->SavedImages[0].ImageDesc.Height; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( int iImage = 0; iImage < hGifFile->ImageCount; iImage++ ) + { + SavedImage *psImage = hGifFile->SavedImages + iImage; + + if( psImage->ImageDesc.Width != poDS->nRasterXSize + || psImage->ImageDesc.Height != poDS->nRasterYSize ) + continue; + + if( psImage->ImageDesc.ColorMap == NULL && + poDS->hGifFile->SColorMap == NULL ) + { + CPLDebug("GIF", "Skipping image without color table"); + continue; + } +#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5 + /* Since giflib 5, de-interlacing is done by DGifSlurp() */ + psImage->ImageDesc.Interlace = 0; +#endif + poDS->SetBand( poDS->nBands+1, + new GIFRasterBand( poDS, poDS->nBands+1, psImage, + hGifFile->SBackGroundColor )); + } + if( poDS->nBands == 0 ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Check for georeferencing. */ +/* -------------------------------------------------------------------- */ + poDS->DetectGeoreferencing(poOpenInfo); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML( poOpenInfo->GetSiblingFiles() ); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, + poOpenInfo->GetSiblingFiles() ); + + return poDS; +} + +/************************************************************************/ +/* GDALPrintGifError() */ +/************************************************************************/ + +static void GDALPrintGifError(CPL_UNUSED GifFileType *hGifFile, const char* pszMsg) +{ +/* GIFLIB_MAJOR is only defined in libgif >= 4.2.0 */ +/* libgif 4.2.0 has retired PrintGifError() and added GifErrorString() */ +#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR) && \ + ((GIFLIB_MAJOR == 4 && GIFLIB_MINOR >= 2) || GIFLIB_MAJOR > 4) + /* Static string actually, hence the const char* cast */ + +#if GIFLIB_MAJOR >= 5 + const char* pszGIFLIBError = (const char*) GifErrorString(hGifFile->Error); +#else + const char* pszGIFLIBError = (const char*) GifErrorString(); +#endif + if (pszGIFLIBError == NULL) + pszGIFLIBError = "Unknown error"; + CPLError( CE_Failure, CPLE_AppDefined, + "%s. GIFLib Error : %s", pszMsg, pszGIFLIBError ); +#else + PrintGifError(); + CPLError( CE_Failure, CPLE_AppDefined, "%s", pszMsg ); +#endif +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * +GIFDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int bInterlace = FALSE; + +/* -------------------------------------------------------------------- */ +/* Check for interlaced option. */ +/* -------------------------------------------------------------------- */ + bInterlace = CSLFetchBoolean(papszOptions, "INTERLACING", FALSE); + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + if( nBands != 1 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GIF driver only supports one band images.\n" ); + + return NULL; + } + + if (nXSize > 65535 || nYSize > 65535) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GIF driver only supports datasets up to 65535x65535 size.\n" ); + + return NULL; + } + + if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte + && bStrict ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GIF driver doesn't support data type %s. " + "Only eight bit bands supported.\n", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open the output file. */ +/* -------------------------------------------------------------------- */ + GifFileType *hGifFile; + VSILFILE *fp; + + fp = VSIFOpenL( pszFilename, "wb" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create %s:\n%s", + pszFilename, VSIStrerror( errno ) ); + return NULL; + } + +#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5 + int nError; + hGifFile = EGifOpen( fp, VSIGIFWriteFunc, &nError ); +#else + hGifFile = EGifOpen( fp, VSIGIFWriteFunc ); +#endif + if( hGifFile == NULL ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_OpenFailed, + "EGifOpenFilename(%s) failed. Does file already exist?", + pszFilename ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Prepare colortable. */ +/* -------------------------------------------------------------------- */ + GDALRasterBand *poBand = poSrcDS->GetRasterBand(1); + ColorMapObject *psGifCT; + int iColor; + + if( poBand->GetColorTable() == NULL ) + { + psGifCT = GifMakeMapObject( 256, NULL ); + for( iColor = 0; iColor < 256; iColor++ ) + { + psGifCT->Colors[iColor].Red = (GifByteType) iColor; + psGifCT->Colors[iColor].Green = (GifByteType) iColor; + psGifCT->Colors[iColor].Blue = (GifByteType) iColor; + } + } + else + { + GDALColorTable *poCT = poBand->GetColorTable(); + int nFullCount = 1; + + while( nFullCount < poCT->GetColorEntryCount() ) + nFullCount = nFullCount * 2; + + psGifCT = GifMakeMapObject( nFullCount, NULL ); + for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ ) + { + GDALColorEntry sEntry; + + poCT->GetColorEntryAsRGB( iColor, &sEntry ); + psGifCT->Colors[iColor].Red = (GifByteType) sEntry.c1; + psGifCT->Colors[iColor].Green = (GifByteType) sEntry.c2; + psGifCT->Colors[iColor].Blue = (GifByteType) sEntry.c3; + } + for( ; iColor < nFullCount; iColor++ ) + { + psGifCT->Colors[iColor].Red = 0; + psGifCT->Colors[iColor].Green = 0; + psGifCT->Colors[iColor].Blue = 0; + } + } + +/* -------------------------------------------------------------------- */ +/* Setup parameters. */ +/* -------------------------------------------------------------------- */ + if (EGifPutScreenDesc(hGifFile, nXSize, nYSize, + 8, /* ColorRes */ + 255, /* Background */ + psGifCT) == GIF_ERROR) + { + GifFreeMapObject(psGifCT); + GDALPrintGifError(hGifFile, "Error writing gif file."); + GIFAbstractDataset::myEGifCloseFile(hGifFile); + VSIFCloseL( fp ); + return NULL; + } + + GifFreeMapObject(psGifCT); + psGifCT = NULL; + + /* Support for transparency */ + int bNoDataValue; + double noDataValue = poBand->GetNoDataValue(&bNoDataValue); + if (bNoDataValue && noDataValue >= 0 && noDataValue <= 255) + { + unsigned char extensionData[4]; + extensionData[0] = 1; /* Transparent Color Flag */ + extensionData[1] = 0; + extensionData[2] = 0; + extensionData[3] = (unsigned char)noDataValue; + EGifPutExtension(hGifFile, 0xf9, 4, extensionData); + } + + if (EGifPutImageDesc(hGifFile, 0, 0, nXSize, nYSize, bInterlace, NULL) == GIF_ERROR ) + { + GDALPrintGifError(hGifFile, "Error writing gif file."); + GIFAbstractDataset::myEGifCloseFile(hGifFile); + VSIFCloseL( fp ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Loop over image, copying image data. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + GDALPamDataset *poDS; + GByte *pabyScanline; + + pabyScanline = (GByte *) CPLMalloc( nXSize ); + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + eErr = CE_Failure; + + if( !bInterlace ) + { + for( int iLine = 0; iLine < nYSize; iLine++ ) + { + eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + pabyScanline, nXSize, 1, GDT_Byte, + nBands, nBands * nXSize, NULL ); + + if( eErr != CE_None || EGifPutLine( hGifFile, pabyScanline, nXSize ) == GIF_ERROR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error writing gif file." ); + goto error; + } + + if( !pfnProgress( (iLine + 1) * 1.0 / nYSize, NULL, pProgressData ) ) + { + goto error; + } + + } + } + else + { + int i, j; + int nLinesRead = 0; + int nLinesToRead = 0; + for ( i = 0; i < 4; i++) + { + for (j = InterlacedOffset[i]; j < nYSize; j += InterlacedJumps[i]) + { + nLinesToRead ++; + } + } + + /* Need to perform 4 passes on the images: */ + for ( i = 0; i < 4; i++) + { + for (j = InterlacedOffset[i]; j < nYSize; j += InterlacedJumps[i]) + { + eErr= poBand->RasterIO( GF_Read, 0, j, nXSize, 1, + pabyScanline, nXSize, 1, GDT_Byte, + 1, nXSize, NULL ); + + if (eErr != CE_None || EGifPutLine(hGifFile, pabyScanline, nXSize) == GIF_ERROR) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error writing gif file." ); + goto error; + } + + nLinesRead ++; + if( !pfnProgress( nLinesRead * 1.0 / nYSize, NULL, pProgressData ) ) + { + goto error; + } + } + } + } + + CPLFree( pabyScanline ); + pabyScanline = NULL; + +/* -------------------------------------------------------------------- */ +/* cleanup */ +/* -------------------------------------------------------------------- */ + if (GIFAbstractDataset::myEGifCloseFile(hGifFile) == GIF_ERROR) + { + CPLError( CE_Failure, CPLE_AppDefined, + "EGifCloseFile() failed.\n" ); + hGifFile = NULL; + goto error; + } + hGifFile = NULL; + + VSIFCloseL( fp ); + fp = NULL; + +/* -------------------------------------------------------------------- */ +/* Do we need a world file? */ +/* -------------------------------------------------------------------- */ + if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) ) + { + double adfGeoTransform[6]; + + if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None ) + GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform ); + } + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + + /* If outputing to stdout, we can't reopen it, so we'll return */ + /* a fake dataset to make the caller happy */ + CPLPushErrorHandler(CPLQuietErrorHandler); + poDS = (GDALPamDataset*) GDALOpen(pszFilename, GA_ReadOnly); + CPLPopErrorHandler(); + if (poDS) + { + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + return poDS; + } + else + { + CPLErrorReset(); + + GIFDataset* poGIF_DS = new GIFDataset(); + poGIF_DS->nRasterXSize = nXSize; + poGIF_DS->nRasterYSize = nYSize; + for(int i=0;iSetBand( i+1, new GIFRasterBand( poGIF_DS, i+1, NULL, 0 ) ); + return poGIF_DS; + } + +error: + if (hGifFile) + GIFAbstractDataset::myEGifCloseFile(hGifFile); + if (fp) + VSIFCloseL( fp ); + if (pabyScanline) + CPLFree( pabyScanline ); + return NULL; +} + +/************************************************************************/ +/* VSIGIFWriteFunc() */ +/* */ +/* Proxy write function. */ +/************************************************************************/ + +static int VSIGIFWriteFunc( GifFileType *psGFile, + const GifByteType *pabyBuffer, int nBytesToWrite ) + +{ + VSILFILE* fp = (VSILFILE *) psGFile->UserData; + if ( VSIFTellL(fp) == 0 && nBytesToWrite >= 6 && + memcmp(pabyBuffer, "GIF87a", 6) == 0 ) + { + /* This is a hack to write a GIF89a instead of GIF87a */ + /* (we have to, since we are using graphical extension block) */ + /* EGifSpew would write GIF89a when it detects an extension block if we were using it */ + /* As we don't, we could have used EGifSetGifVersion instead, but the version of libungif */ + /* in GDAL has a bug : it writes on read-only memory ! */ + /* (this is a well-known problem. Just google for "EGifSetGifVersion segfault") */ + /* Most readers don't even care if it is GIF87a or GIF89a, but it is */ + /* better to write the right version */ + + int nRet = VSIFWriteL("GIF89a", 1, 6, fp); + nRet += VSIFWriteL( (char *) pabyBuffer + 6, 1, nBytesToWrite - 6, fp ); + return nRet; + } + else + return VSIFWriteL( (void *) pabyBuffer, 1, nBytesToWrite, fp ); +} + +/************************************************************************/ +/* GDALRegister_GIF() */ +/************************************************************************/ + +void GDALRegister_GIF() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GIF" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GIF" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Graphics Interchange Format (.gif)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_gif.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gif" ); + poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/gif" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"\n" +" \n" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = GIFDataset::Open; + poDriver->pfnCreateCopy = GIFDataset::CreateCopy; + poDriver->pfnIdentify = GIFAbstractDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/gif/giflib/COPYING b/bazaar/plugin/gdal/frmts/gif/giflib/COPYING new file mode 100644 index 000000000..b9c0b5012 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/giflib/COPYING @@ -0,0 +1,19 @@ +The GIFLIB distribution is Copyright (c) 1997 Eric S. Raymond + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/bazaar/plugin/gdal/frmts/gif/giflib/README b/bazaar/plugin/gdal/frmts/gif/giflib/README new file mode 100644 index 000000000..6e0bb1446 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/giflib/README @@ -0,0 +1,31 @@ +This code is from giflib 4.1.6 + +This seems to be the new location of the giflib project: +http://sourceforge.net/projects/giflib + +Changes: + o Select only lib files needed. + o Hacked in O_BINARY support whereever O_BINARY is defined. + o Modify include section of c files + o Apply patch for http://trac.osgeo.org/gdal/ticket/2542 + o Apply Debian 01-cve.dpatch to fix CVE-2005-2974 and CVE-2005-3350 + +History: + +This package was originally written by Gershon Elber in 1990 on an IBM PC +under MS-DOS using Borland Turbo C. He made it portable to several UNIX +environments. + +v1.0 (4 Jul 89) created by Gershon Elber. +v2.1 featured substantial changes and additions by Eric S. Raymond. + +Status: + +Current maintainer is Eric S. Raymond. +GIFLIB is not under active development, but bug fixes are being accepted. + +License: + +The GIFLIB is available under terms of MIT License. +Also, refer to COPYING file. + diff --git a/bazaar/plugin/gdal/frmts/gif/giflib/dgif_lib.c b/bazaar/plugin/gdal/frmts/gif/giflib/dgif_lib.c new file mode 100644 index 000000000..9b791f49e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/giflib/dgif_lib.c @@ -0,0 +1,1116 @@ +/****************************************************************************** +* "Gif-Lib" - Yet another gif library. * +* * +* Written by: Gershon Elber IBM PC Ver 1.1, Aug. 1990 * +******************************************************************************* +* The kernel of the GIF Decoding process can be found here. * +******************************************************************************* +* History: * +* 16 Jun 89 - Version 1.0 by Gershon Elber. * +* 3 Sep 90 - Version 1.1 by Gershon Elber (Support for Gif89, Unique names). * +******************************************************************************/ + + +#if (defined(_MSC_VER) || defined(__MSDOS__)) && !defined(__DJGPP__) && !defined(__GNUC__) +# include +# include +# include +# ifndef _MSC_VER +# include +# endif /* _MSC_VER */ +#else +# include +# include +#endif /* _MSC_VER || __MSDOS__ */ + +#ifdef unix +#include +#endif + +#ifndef __MSDOS__ +#include +#endif +#include +#include +#include +#include "gif_lib.h" +#include "gif_lib_private.h" + +#define COMMENT_EXT_FUNC_CODE 0xfe /* Extension function code for + comment. */ + +/* avoid extra function call in case we use fread (TVT) */ +#define READ(_gif,_buf,_len) \ + (((GifFilePrivateType*)_gif->Private)->Read ? \ + ((GifFilePrivateType*)_gif->Private)->Read(_gif,_buf,_len) : \ + fread(_buf,1,_len,((GifFilePrivateType*)_gif->Private)->File)) + +static int DGifGetWord(GifFileType *GifFile, GifWord *Word); +static int DGifSetupDecompress(GifFileType *GifFile); +static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, + int LineLen); +static int DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode); +static int DGifDecompressInput(GifFileType *GifFile, int *Code); +static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, + GifByteType *NextByte); +#ifndef _GBA_NO_FILEIO + +/****************************************************************************** + * Open a new gif file for read, given by its name. + * Returns GifFileType pointer dynamically allocated which serves as the gif + * info record. _GifError is cleared if succesfull. + *****************************************************************************/ +GifFileType * +DGifOpenFileName(const char *FileName) { + int FileHandle; + GifFileType *GifFile; + + if ((FileHandle = open(FileName, O_RDONLY +#if defined(O_BINARY) + | O_BINARY +#endif /* __MSDOS__ || _OPEN_BINARY */ + )) == -1) { + _GifError = D_GIF_ERR_OPEN_FAILED; + return NULL; + } + + GifFile = DGifOpenFileHandle(FileHandle); + return GifFile; +} + +/****************************************************************************** + * Update a new gif file, given its file handle. + * Returns GifFileType pointer dynamically allocated which serves as the gif + * info record. _GifError is cleared if succesfull. + *****************************************************************************/ +GifFileType * +DGifOpenFileHandle(int FileHandle) { + + unsigned char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + FILE *f; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + close(FileHandle); + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (Private == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + close(FileHandle); + free((char *)GifFile); + return NULL; + } +#if defined(O_BINARY) + setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ +#endif /* __MSDOS__ */ + + f = fdopen(FileHandle, "rb"); /* Make it into a stream: */ + +#if defined(__MSDOS__) || defined(WIN32) + setvbuf(f, NULL, _IOFBF, GIF_FILE_BUFFER_SIZE); /* And inc. stream + buffer. */ +#endif /* __MSDOS__ */ + + GifFile->Private = (VoidPtr)Private; + Private->FileHandle = FileHandle; + Private->File = f; + Private->FileState = FILE_STATE_READ; + Private->Read = 0; /* don't use alternate input method (TVT) */ + GifFile->UserData = 0; /* TVT */ + + /* Lets see if this is a GIF file: */ + if (READ(GifFile, Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { + _GifError = D_GIF_ERR_READ_FAILED; + fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* The GIF Version number is ignored at this time. Maybe we should do + * something more useful with it. */ + Buf[GIF_STAMP_LEN] = 0; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + _GifError = D_GIF_ERR_NOT_GIF_FILE; + fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + _GifError = 0; + + return GifFile; +} + +#endif /* _GBA_NO_FILEIO */ + +/****************************************************************************** + * GifFileType constructor with user supplied input function (TVT) + *****************************************************************************/ +GifFileType * +DGifOpen(void *userData, + InputFunc readFunc) { + + unsigned char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (!Private) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + free((char *)GifFile); + return NULL; + } + + GifFile->Private = (VoidPtr)Private; + Private->FileHandle = 0; + Private->File = 0; + Private->FileState = FILE_STATE_READ; + + Private->Read = readFunc; /* TVT */ + GifFile->UserData = userData; /* TVT */ + + /* Lets see if this is a GIF file: */ + if (READ(GifFile, Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { + _GifError = D_GIF_ERR_READ_FAILED; + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* The GIF Version number is ignored at this time. Maybe we should do + * something more useful with it. */ + Buf[GIF_STAMP_LEN] = 0; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + _GifError = D_GIF_ERR_NOT_GIF_FILE; + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + _GifError = 0; + + return GifFile; +} + +/****************************************************************************** + * This routine should be called before any other DGif calls. Note that + * this routine is called automatically from DGif file open routines. + *****************************************************************************/ +int +DGifGetScreenDesc(GifFileType * GifFile) { + + int i, BitsPerPixel; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + /* Put the screen descriptor into the file: */ + if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR) + return GIF_ERROR; + + if (READ(GifFile, Buf, 3) != 3) { + _GifError = D_GIF_ERR_READ_FAILED; + FreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + return GIF_ERROR; + } + GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1; + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->SBackGroundColor = Buf[1]; + if (Buf[0] & 0x80) { /* Do we have global color map? */ + + GifFile->SColorMap = MakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->SColorMap == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the global color map: */ + for (i = 0; i < GifFile->SColorMap->ColorCount; i++) { + if (READ(GifFile, Buf, 3) != 3) { + FreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + GifFile->SColorMap->Colors[i].Red = Buf[0]; + GifFile->SColorMap->Colors[i].Green = Buf[1]; + GifFile->SColorMap->Colors[i].Blue = Buf[2]; + } + } else { + GifFile->SColorMap = NULL; + } + + return GIF_OK; +} + +/****************************************************************************** + * This routine should be called before any attempt to read an image. + *****************************************************************************/ +int +DGifGetRecordType(GifFileType * GifFile, + GifRecordType * Type) { + + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (READ(GifFile, &Buf, 1) != 1) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + switch (Buf) { + case ',': + *Type = IMAGE_DESC_RECORD_TYPE; + break; + case '!': + *Type = EXTENSION_RECORD_TYPE; + break; + case ';': + *Type = TERMINATE_RECORD_TYPE; + break; + default: + *Type = UNDEFINED_RECORD_TYPE; + _GifError = D_GIF_ERR_WRONG_RECORD; + return GIF_ERROR; + } + + return GIF_OK; +} + +/****************************************************************************** + * This routine should be called before any attempt to read an image. + * Note it is assumed the Image desc. header (',') has been read. + *****************************************************************************/ +int +DGifGetImageDesc(GifFileType * GifFile) { + + int i, BitsPerPixel; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + SavedImage *sp; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR) + return GIF_ERROR; + if (READ(GifFile, Buf, 1) != 1) { + _GifError = D_GIF_ERR_READ_FAILED; + FreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->Image.Interlace = (Buf[0] & 0x40); + if (Buf[0] & 0x80) { /* Does this image have local color map? */ + + /*** FIXME: Why do we check both of these in order to do this? + * Why do we have both Image and SavedImages? */ + if (GifFile->Image.ColorMap && GifFile->SavedImages == NULL) + FreeMapObject(GifFile->Image.ColorMap); + + GifFile->Image.ColorMap = MakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->Image.ColorMap == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the image local color map: */ + for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) { + if (READ(GifFile, Buf, 3) != 3) { + FreeMapObject(GifFile->Image.ColorMap); + _GifError = D_GIF_ERR_READ_FAILED; + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + GifFile->Image.ColorMap->Colors[i].Red = Buf[0]; + GifFile->Image.ColorMap->Colors[i].Green = Buf[1]; + GifFile->Image.ColorMap->Colors[i].Blue = Buf[2]; + } + } else if (GifFile->Image.ColorMap) { + FreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + + if (GifFile->SavedImages) { + if ((GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages, + sizeof(SavedImage) * + (GifFile->ImageCount + 1))) == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } else { + if ((GifFile->SavedImages = + (SavedImage *) malloc(sizeof(SavedImage))) == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + + sp = &GifFile->SavedImages[GifFile->ImageCount]; + memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc)); + if (GifFile->Image.ColorMap != NULL) { + sp->ImageDesc.ColorMap = MakeMapObject( + GifFile->Image.ColorMap->ColorCount, + GifFile->Image.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + sp->RasterBits = (unsigned char *)NULL; + sp->ExtensionBlockCount = 0; + sp->ExtensionBlocks = (ExtensionBlock *) NULL; + + GifFile->ImageCount++; + + Private->PixelCount = (long)GifFile->Image.Width * + (long)GifFile->Image.Height; + + DGifSetupDecompress(GifFile); /* Reset decompress algorithm parameters. */ + + return GIF_OK; +} + +/****************************************************************************** + * Get one full scanned line (Line) of length LineLen from GIF file. + *****************************************************************************/ +int +DGifGetLine(GifFileType * GifFile, + GifPixelType * Line, + int LineLen) { + + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (!LineLen) + LineLen = GifFile->Image.Width; + +#if defined(__MSDOS__) || defined(WIN32) || defined(__GNUC__) + if ((Private->PixelCount -= LineLen) > 0xffff0000UL) { +#else + if ((Private->PixelCount -= LineLen) > 0xffff0000) { +#endif /* __MSDOS__ */ + _GifError = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably would not be called any more, so lets clean + * everything before we return: need to flush out all rest of + * image until empty block (size 0) detected. We use GetCodeNext. */ + do + if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) + return GIF_ERROR; + while (Dummy != NULL) ; + } + return GIF_OK; + } else + return GIF_ERROR; +} + +/****************************************************************************** + * Put one pixel (Pixel) into GIF file. + *****************************************************************************/ +int +DGifGetPixel(GifFileType * GifFile, + GifPixelType Pixel) { + + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } +#if defined(__MSDOS__) || defined(WIN32) || defined(__GNUC__) + if (--Private->PixelCount > 0xffff0000UL) +#else + if (--Private->PixelCount > 0xffff0000) +#endif /* __MSDOS__ */ + { + _GifError = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably would not be called any more, so lets clean + * everything before we return: need to flush out all rest of + * image until empty block (size 0) detected. We use GetCodeNext. */ + do + if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) + return GIF_ERROR; + while (Dummy != NULL) ; + } + return GIF_OK; + } else + return GIF_ERROR; +} + +/****************************************************************************** + * Get an extension block (see GIF manual) from gif file. This routine only + * returns the first data block, and DGifGetExtensionNext should be called + * after this one until NULL extension is returned. + * The Extension should NOT be freed by the user (not dynamically allocated). + * Note it is assumed the Extension desc. header ('!') has been read. + *****************************************************************************/ +int +DGifGetExtension(GifFileType * GifFile, + int *ExtCode, + GifByteType ** Extension) { + + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (READ(GifFile, &Buf, 1) != 1) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *ExtCode = Buf; + + return DGifGetExtensionNext(GifFile, Extension); +} + +/****************************************************************************** + * Get a following extension block (see GIF manual) from gif file. This + * routine should be called until NULL Extension is returned. + * The Extension should NOT be freed by the user (not dynamically allocated). + *****************************************************************************/ +int +DGifGetExtensionNext(GifFileType * GifFile, + GifByteType ** Extension) { + + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (READ(GifFile, &Buf, 1) != 1) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + if (Buf > 0) { + *Extension = Private->Buf; /* Use private unused buffer. */ + (*Extension)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ + if (READ(GifFile, &((*Extension)[1]), Buf) != Buf) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else + *Extension = NULL; + + return GIF_OK; +} + +/****************************************************************************** + * This routine should be called last, to close the GIF file. + *****************************************************************************/ +int +DGifCloseFile(GifFileType * GifFile) { + + GifFilePrivateType *Private; + FILE *File; + + if (GifFile == NULL) + return GIF_ERROR; + + Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + File = Private->File; + + if (GifFile->Image.ColorMap) { + FreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + + if (GifFile->SColorMap) { + FreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + } + + if (Private) { + free((char *)Private); + Private = NULL; + } + + if (GifFile->SavedImages) { + FreeSavedImages(GifFile); + GifFile->SavedImages = NULL; + } + + free(GifFile); + + if (File && (fclose(File) != 0)) { + _GifError = D_GIF_ERR_CLOSE_FAILED; + return GIF_ERROR; + } + return GIF_OK; +} + +/****************************************************************************** + * Get 2 bytes (word) from the given file: + *****************************************************************************/ +static int +DGifGetWord(GifFileType * GifFile, + GifWord *Word) { + + unsigned char c[2]; + + if (READ(GifFile, c, 2) != 2) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + *Word = (((unsigned int)c[1]) << 8) + c[0]; + return GIF_OK; +} + +/****************************************************************************** + * Get the image code in compressed form. This routine can be called if the + * information needed to be piped out as is. Obviously this is much faster + * than decoding and encoding again. This routine should be followed by calls + * to DGifGetCodeNext, until NULL block is returned. + * The block should NOT be freed by the user (not dynamically allocated). + *****************************************************************************/ +int +DGifGetCode(GifFileType * GifFile, + int *CodeSize, + GifByteType ** CodeBlock) { + + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + *CodeSize = Private->BitsPerPixel; + + return DGifGetCodeNext(GifFile, CodeBlock); +} + +/****************************************************************************** + * Continue to get the image code in compressed form. This routine should be + * called until NULL block is returned. + * The block should NOT be freed by the user (not dynamically allocated). + *****************************************************************************/ +int +DGifGetCodeNext(GifFileType * GifFile, + GifByteType ** CodeBlock) { + + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (READ(GifFile, &Buf, 1) != 1) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + if (Buf > 0) { + *CodeBlock = Private->Buf; /* Use private unused buffer. */ + (*CodeBlock)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ + if (READ(GifFile, &((*CodeBlock)[1]), Buf) != Buf) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else { + *CodeBlock = NULL; + Private->Buf[0] = 0; /* Make sure the buffer is empty! */ + Private->PixelCount = 0; /* And local info. indicate image read. */ + } + + return GIF_OK; +} + +/****************************************************************************** + * Setup the LZ decompression for this image: + *****************************************************************************/ +static int +DGifSetupDecompress(GifFileType * GifFile) { + + int i, BitsPerPixel; + GifByteType CodeSize; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + READ(GifFile, &CodeSize, 1); /* Read Code size from file. */ + BitsPerPixel = CodeSize; + + Private->Buf[0] = 0; /* Input Buffer empty. */ + Private->BitsPerPixel = BitsPerPixel; + Private->ClearCode = (1 << BitsPerPixel); + Private->EOFCode = Private->ClearCode + 1; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ + Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ + Private->StackPtr = 0; /* No pixels on the pixel stack. */ + Private->LastCode = NO_SUCH_CODE; + Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ + Private->CrntShiftDWord = 0; + + Prefix = Private->Prefix; + for (i = 0; i <= LZ_MAX_CODE; i++) + Prefix[i] = NO_SUCH_CODE; + + return GIF_OK; +} + +/****************************************************************************** + * The LZ decompression routine: + * This version decompress the given gif file into Line of length LineLen. + * This routine can be called few times (one per scan line, for example), in + * order the complete the whole image. + *****************************************************************************/ +static int +DGifDecompressLine(GifFileType * GifFile, + GifPixelType * Line, + int LineLen) { + + int i = 0; + int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr; + GifByteType *Stack, *Suffix; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + StackPtr = Private->StackPtr; + Prefix = Private->Prefix; + Suffix = Private->Suffix; + Stack = Private->Stack; + EOFCode = Private->EOFCode; + ClearCode = Private->ClearCode; + LastCode = Private->LastCode; + + if (StackPtr > LZ_MAX_CODE) { + return GIF_ERROR; + } + + if (StackPtr != 0) { + /* Let pop the stack off before continueing to read the gif file: */ + while (StackPtr != 0 && i < LineLen) + Line[i++] = Stack[--StackPtr]; + } + + while (i < LineLen) { /* Decode LineLen items. */ + if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR) + return GIF_ERROR; + + if (CrntCode == EOFCode) { + /* Note however that usually we will not be here as we will stop + * decoding as soon as we got all the pixel, or EOF code will + * not be read at all, and DGifGetLine/Pixel clean everything. */ + if (i != LineLen - 1 || Private->PixelCount != 0) { + _GifError = D_GIF_ERR_EOF_TOO_SOON; + return GIF_ERROR; + } + i++; + } else if (CrntCode == ClearCode) { + /* We need to start over again: */ + for (j = 0; j <= LZ_MAX_CODE; j++) + Prefix[j] = NO_SUCH_CODE; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + LastCode = Private->LastCode = NO_SUCH_CODE; + } else { + /* Its regular code - if in pixel range simply add it to output + * stream, otherwise trace to codes linked list until the prefix + * is in pixel range: */ + if (CrntCode < ClearCode) { + /* This is simple - its pixel scalar, so add it to output: */ + Line[i++] = CrntCode; + } else { + /* Its a code to needed to be traced: trace the linked list + * until the prefix is a pixel, while pushing the suffix + * pixels on our stack. If we done, pop the stack in reverse + * (thats what stack is good for!) order to output. */ + if (Prefix[CrntCode] == NO_SUCH_CODE) { + /* Only allowed if CrntCode is exactly the running code: + * In that case CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + if (CrntCode == Private->RunningCode - 2) { + CrntPrefix = LastCode; + Suffix[Private->RunningCode - 2] = + Stack[StackPtr++] = DGifGetPrefixChar(Prefix, + LastCode, + ClearCode); + } else { + _GifError = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + } else + CrntPrefix = CrntCode; + + /* Now (if image is O.K.) we should not get an NO_SUCH_CODE + * During the trace. As we might loop forever, in case of + * defective image, we count the number of loops we trace + * and stop if we got LZ_MAX_CODE. obviously we can not + * loop more than that. */ + j = 0; + while (j++ <= LZ_MAX_CODE && + CrntPrefix > ClearCode && CrntPrefix <= LZ_MAX_CODE) { + Stack[StackPtr++] = Suffix[CrntPrefix]; + CrntPrefix = Prefix[CrntPrefix]; + } + if (j >= LZ_MAX_CODE || CrntPrefix > LZ_MAX_CODE) { + _GifError = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + /* Push the last character on stack: */ + Stack[StackPtr++] = CrntPrefix; + + /* Now lets pop all the stack into output: */ + while (StackPtr != 0 && i < LineLen) + Line[i++] = Stack[--StackPtr]; + } + if (LastCode != NO_SUCH_CODE) { + Prefix[Private->RunningCode - 2] = LastCode; + + if (CrntCode == Private->RunningCode - 2) { + /* Only allowed if CrntCode is exactly the running code: + * In that case CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, LastCode, ClearCode); + } else { + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, CrntCode, ClearCode); + } + } + LastCode = CrntCode; + } + } + + Private->LastCode = LastCode; + Private->StackPtr = StackPtr; + + return GIF_OK; +} + +/****************************************************************************** + * Routine to trace the Prefixes linked list until we get a prefix which is + * not code, but a pixel value (less than ClearCode). Returns that pixel value. + * If image is defective, we might loop here forever, so we limit the loops to + * the maximum possible if image O.k. - LZ_MAX_CODE times. + *****************************************************************************/ +static int +DGifGetPrefixChar(GifPrefixType *Prefix, + int Code, + int ClearCode) { + + int i = 0; + + while (Code > ClearCode && i++ <= LZ_MAX_CODE) { + if (Code > LZ_MAX_CODE) { + return NO_SUCH_CODE; + } + Code = Prefix[Code]; + } + return Code; +} + +/****************************************************************************** + * Interface for accessing the LZ codes directly. Set Code to the real code + * (12bits), or to -1 if EOF code is returned. + *****************************************************************************/ +int +DGifGetLZCodes(GifFileType * GifFile, + int *Code) { + + GifByteType *CodeBlock; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifDecompressInput(GifFile, Code) == GIF_ERROR) + return GIF_ERROR; + + if (*Code == Private->EOFCode) { + /* Skip rest of codes (hopefully only NULL terminating block): */ + do { + if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) + return GIF_ERROR; + } while (CodeBlock != NULL) ; + + *Code = -1; + } else if (*Code == Private->ClearCode) { + /* We need to start over again: */ + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + } + + return GIF_OK; +} + +/****************************************************************************** + * The LZ decompression input routine: + * This routine is responsable for the decompression of the bit stream from + * 8 bits (bytes) packets, into the real codes. + * Returns GIF_OK if read succesfully. + *****************************************************************************/ +static int +DGifDecompressInput(GifFileType * GifFile, + int *Code) { + + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + GifByteType NextByte; + static unsigned short CodeMasks[] = { + 0x0000, 0x0001, 0x0003, 0x0007, + 0x000f, 0x001f, 0x003f, 0x007f, + 0x00ff, 0x01ff, 0x03ff, 0x07ff, + 0x0fff + }; + /* The image can't contain more than LZ_BITS per code. */ + if (Private->RunningBits > LZ_BITS) { + _GifError = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + + while (Private->CrntShiftState < Private->RunningBits) { + /* Needs to get more bytes from input stream for next code: */ + if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == GIF_ERROR) { + return GIF_ERROR; + } + Private->CrntShiftDWord |= + ((unsigned long)NextByte) << Private->CrntShiftState; + Private->CrntShiftState += 8; + } + *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits]; + + Private->CrntShiftDWord >>= Private->RunningBits; + Private->CrntShiftState -= Private->RunningBits; + + /* If code cannot fit into RunningBits bits, must raise its size. Note + * however that codes above 4095 are used for special signaling. + * If we're using LZ_BITS bits already and we're at the max code, just + * keep using the table as it is, don't increment Private->RunningCode. + */ + if (Private->RunningCode < LZ_MAX_CODE + 2 && + ++Private->RunningCode > Private->MaxCode1 && + Private->RunningBits < LZ_BITS) { + Private->MaxCode1 <<= 1; + Private->RunningBits++; + } + return GIF_OK; +} + +/****************************************************************************** + * This routines read one gif data block at a time and buffers it internally + * so that the decompression routine could access it. + * The routine returns the next byte from its internal buffer (or read next + * block in if buffer empty) and returns GIF_OK if succesful. + *****************************************************************************/ +static int +DGifBufferedInput(GifFileType * GifFile, + GifByteType * Buf, + GifByteType * NextByte) { + + if (Buf[0] == 0) { + /* Needs to read the next buffer - this one is empty: */ + if (READ(GifFile, Buf, 1) != 1) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + /* There shouldn't be any empty data blocks here as the LZW spec + * says the LZW termination code should come first. Therefore we + * shouldn't be inside this routine at that point. + */ + if (Buf[0] == 0) { + _GifError = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + /* There shouldn't be any empty data blocks here as the LZW spec + * says the LZW termination code should come first. Therefore we + * shouldn't be inside this routine at that point. + */ + if (Buf[0] == 0) { + _GifError = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + if (READ(GifFile, &Buf[1], Buf[0]) != Buf[0]) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *NextByte = Buf[1]; + Buf[1] = 2; /* We use now the second place as last char read! */ + Buf[0]--; + } else { + *NextByte = Buf[Buf[1]++]; + Buf[0]--; + } + + return GIF_OK; +} +#ifndef _GBA_NO_FILEIO + +/****************************************************************************** + * This routine reads an entire GIF into core, hanging all its state info off + * the GifFileType pointer. Call DGifOpenFileName() or DGifOpenFileHandle() + * first to initialize I/O. Its inverse is EGifSpew(). + ******************************************************************************/ +int +DGifSlurp(GifFileType * GifFile) { + + int ImageSize; + GifRecordType RecordType; + SavedImage *sp; + GifByteType *ExtData; + SavedImage temp_save; + + temp_save.ExtensionBlocks = NULL; + temp_save.ExtensionBlockCount = 0; + + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) + return (GIF_ERROR); + + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFile) == GIF_ERROR) + return (GIF_ERROR); + + sp = &GifFile->SavedImages[GifFile->ImageCount - 1]; + + if( (double) sp->ImageDesc.Width + * (double) sp->ImageDesc.Height > 100000000.0 ) + { + /* for GDAL we prefer to not process very large images. */ + /* http://trac.osgeo.org/gdal/ticket/2542 */ + return D_GIF_ERR_DATA_TOO_BIG; + } + + ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height; + + sp->RasterBits = (unsigned char *)malloc(ImageSize * + sizeof(GifPixelType)); + if (sp->RasterBits == NULL) { + return GIF_ERROR; + } + if (DGifGetLine(GifFile, sp->RasterBits, ImageSize) == + GIF_ERROR) + return (GIF_ERROR); + if (temp_save.ExtensionBlocks) { + sp->ExtensionBlocks = temp_save.ExtensionBlocks; + sp->ExtensionBlockCount = temp_save.ExtensionBlockCount; + + temp_save.ExtensionBlocks = NULL; + temp_save.ExtensionBlockCount = 0; + + /* FIXME: The following is wrong. It is left in only for + * backwards compatibility. Someday it should go away. Use + * the sp->ExtensionBlocks->Function variable instead. */ + sp->Function = sp->ExtensionBlocks[0].Function; + } + break; + + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(GifFile, &temp_save.Function, &ExtData) == + GIF_ERROR) + return (GIF_ERROR); + while (ExtData != NULL) { + + /* Create an extension block with our data */ + if (AddExtensionBlock(&temp_save, ExtData[0], &ExtData[1]) + == GIF_ERROR) + return (GIF_ERROR); + + if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR) + return (GIF_ERROR); + temp_save.Function = 0; + } + break; + + case TERMINATE_RECORD_TYPE: + break; + + default: /* Should be trapped by DGifGetRecordType */ + break; + } + } while (RecordType != TERMINATE_RECORD_TYPE); + + /* Just in case the Gif has an extension block without an associated + * image... (Should we save this into a savefile structure with no image + * instead? Have to check if the present writing code can handle that as + * well.... */ + if (temp_save.ExtensionBlocks) + FreeExtension(&temp_save); + + return (GIF_OK); +} +#endif /* _GBA_NO_FILEIO */ diff --git a/bazaar/plugin/gdal/frmts/gif/giflib/egif_lib.c b/bazaar/plugin/gdal/frmts/gif/giflib/egif_lib.c new file mode 100644 index 000000000..410797301 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/giflib/egif_lib.c @@ -0,0 +1,1091 @@ +/****************************************************************************** + * "Gif-Lib" - Yet another gif library. + * + * Written by: Gershon Elber Ver 1.1, Aug. 1990 + ****************************************************************************** + * The kernel of the GIF Encoding process can be found here. + ****************************************************************************** + * History: + * 14 Jun 89 - Version 1.0 by Gershon Elber. + * 3 Sep 90 - Version 1.1 by Gershon Elber (Support for Gif89, Unique names). + * 26 Jun 96 - Version 3.0 by Eric S. Raymond (Full GIF89 support) + *****************************************************************************/ + +#if defined(_MSC_VER) || defined(__MSDOS__) +# include +# include +# ifndef _MSC_VER +# include +# endif +#else +# include +# include +# ifdef R6000 +# include +# endif +#endif /* _MSC_VER || __MSDOS__ */ + +#ifdef unix +#include +#endif + +#include +#include +#include +#include +#include "gif_lib.h" +#include "gif_lib_private.h" + +#if !defined(S_IREAD) && defined(S_IRUSR) +# define S_IREAD S_IRUSR +# define S_IWRITE S_IRUSR +#endif + +/* #define DEBUG_NO_PREFIX Dump only compressed data. */ + +/* Masks given codes to BitsPerPixel, to make sure all codes are in range: */ +static GifPixelType CodeMask[] = { + 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff +}; + +static char GifVersionPrefix[GIF_STAMP_LEN + 1] = GIF87_STAMP; + +#define WRITE(_gif,_buf,_len) \ + (((GifFilePrivateType*)_gif->Private)->Write ? \ + ((GifFilePrivateType*)_gif->Private)->Write(_gif,_buf,_len) : \ + fwrite(_buf, 1, _len, ((GifFilePrivateType*)_gif->Private)->File)) + +static int EGifPutWord(int Word, GifFileType * GifFile); +static int EGifSetupCompress(GifFileType * GifFile); +static int EGifCompressLine(GifFileType * GifFile, GifPixelType * Line, + int LineLen); +static int EGifCompressOutput(GifFileType * GifFile, int Code); +static int EGifBufferedOutput(GifFileType * GifFile, GifByteType * Buf, + int c); + +/****************************************************************************** + * Open a new gif file for write, given by its name. If TestExistance then + * if the file exists this routines fails (returns NULL). + * Returns GifFileType pointer dynamically allocated which serves as the gif + * info record. _GifError is cleared if succesfull. + *****************************************************************************/ +GifFileType * +EGifOpenFileName(const char *FileName, + int TestExistance) { + + int FileHandle; + GifFileType *GifFile; + + if (TestExistance) + FileHandle = open(FileName, O_WRONLY | O_CREAT | O_EXCL +#if defined(O_BINARY) + | O_BINARY +#endif /* __MSDOS__ */ + , S_IREAD | S_IWRITE); + else + FileHandle = open(FileName, O_WRONLY | O_CREAT | O_TRUNC +#if defined(O_BINARY) + | O_BINARY +#endif /* __MSDOS__ */ + , S_IREAD | S_IWRITE); + + if (FileHandle == -1) { + _GifError = E_GIF_ERR_OPEN_FAILED; + return NULL; + } + GifFile = EGifOpenFileHandle(FileHandle); + if (GifFile == (GifFileType *) NULL) + close(FileHandle); + return GifFile; +} + +/****************************************************************************** + * Update a new gif file, given its file handle, which must be opened for + * write in binary mode. + * Returns GifFileType pointer dynamically allocated which serves as the gif + * info record. _GifError is cleared if succesfull. + *****************************************************************************/ +GifFileType * +EGifOpenFileHandle(int FileHandle) { + + GifFileType *GifFile; + GifFilePrivateType *Private; + FILE *f; + + GifFile = (GifFileType *) malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + _GifError = E_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (Private == NULL) { + free(GifFile); + _GifError = E_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + if ((Private->HashTable = _InitHashTable()) == NULL) { + free(GifFile); + free(Private); + _GifError = E_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + +#if defined(O_BINARY) + setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ +#endif /* __MSDOS__ */ + + f = fdopen(FileHandle, "wb"); /* Make it into a stream: */ + +#if defined (__MSDOS__) || defined(WIN32) + setvbuf(f, NULL, _IOFBF, GIF_FILE_BUFFER_SIZE); /* And inc. stream + * buffer. */ +#endif /* __MSDOS__ */ + + GifFile->Private = (VoidPtr)Private; + Private->FileHandle = FileHandle; + Private->File = f; + Private->FileState = FILE_STATE_WRITE; + + Private->Write = (OutputFunc) 0; /* No user write routine (MRB) */ + GifFile->UserData = (VoidPtr) 0; /* No user write handle (MRB) */ + + _GifError = 0; + + return GifFile; +} + +/****************************************************************************** + * Output constructor that takes user supplied output function. + * Basically just a copy of EGifOpenFileHandle. (MRB) + *****************************************************************************/ +GifFileType * +EGifOpen(void *userData, + OutputFunc writeFunc) { + + GifFileType *GifFile; + GifFilePrivateType *Private; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + _GifError = E_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (Private == NULL) { + free(GifFile); + _GifError = E_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + Private->HashTable = _InitHashTable(); + if (Private->HashTable == NULL) { + free (GifFile); + free (Private); + _GifError = E_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + GifFile->Private = (VoidPtr) Private; + Private->FileHandle = 0; + Private->File = (FILE *) 0; + Private->FileState = FILE_STATE_WRITE; + + Private->Write = writeFunc; /* User write routine (MRB) */ + GifFile->UserData = userData; /* User write handle (MRB) */ + + _GifError = 0; + + return GifFile; +} + +/****************************************************************************** + * Routine to set current GIF version. All files open for write will be + * using this version until next call to this routine. Version consists of + * 3 characters as "87a" or "89a". No test is made to validate the version. + *****************************************************************************/ +void +EGifSetGifVersion(const char *Version) { + strncpy(GifVersionPrefix + GIF_VERSION_POS, Version, 3); +} + +/****************************************************************************** + * This routine should be called before any other EGif calls, immediately + * follows the GIF file openning. + *****************************************************************************/ +int +EGifPutScreenDesc(GifFileType * GifFile, + int Width, + int Height, + int ColorRes, + int BackGround, + const ColorMapObject * ColorMap) { + + int i; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (Private->FileState & FILE_STATE_SCREEN) { + /* If already has screen descriptor - something is wrong! */ + _GifError = E_GIF_ERR_HAS_SCRN_DSCR; + return GIF_ERROR; + } + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + _GifError = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + +/* First write the version prefix into the file. */ +#ifndef DEBUG_NO_PREFIX + if (WRITE(GifFile, (unsigned char *)GifVersionPrefix, + strlen(GifVersionPrefix)) != strlen(GifVersionPrefix)) { + _GifError = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } +#endif /* DEBUG_NO_PREFIX */ + + GifFile->SWidth = Width; + GifFile->SHeight = Height; + GifFile->SColorResolution = ColorRes; + GifFile->SBackGroundColor = BackGround; + if (ColorMap) { + GifFile->SColorMap = MakeMapObject(ColorMap->ColorCount, + ColorMap->Colors); + if (GifFile->SColorMap == NULL) { + _GifError = E_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } else + GifFile->SColorMap = NULL; + + /* + * Put the logical screen descriptor into the file: + */ + /* Logical Screen Descriptor: Dimensions */ + EGifPutWord(Width, GifFile); + EGifPutWord(Height, GifFile); + + /* Logical Screen Descriptor: Packed Fields */ + /* Note: We have actual size of the color table default to the largest + * possible size (7+1 == 8 bits) because the decoder can use it to decide + * how to display the files. + */ + Buf[0] = (ColorMap ? 0x80 : 0x00) | /* Yes/no global colormap */ + ((ColorRes - 1) << 4) | /* Bits allocated to each primary color */ + (ColorMap ? ColorMap->BitsPerPixel - 1 : 0x07 ); /* Actual size of the + color table. */ + Buf[1] = BackGround; /* Index into the ColorTable for background color */ + Buf[2] = 0; /* Pixel Aspect Ratio */ +#ifndef DEBUG_NO_PREFIX + WRITE(GifFile, Buf, 3); +#endif /* DEBUG_NO_PREFIX */ + + /* If we have Global color map - dump it also: */ +#ifndef DEBUG_NO_PREFIX + if (ColorMap != NULL) + for (i = 0; i < ColorMap->ColorCount; i++) { + /* Put the ColorMap out also: */ + Buf[0] = ColorMap->Colors[i].Red; + Buf[1] = ColorMap->Colors[i].Green; + Buf[2] = ColorMap->Colors[i].Blue; + if (WRITE(GifFile, Buf, 3) != 3) { + _GifError = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + } +#endif /* DEBUG_NO_PREFIX */ + + /* Mark this file as has screen descriptor, and no pixel written yet: */ + Private->FileState |= FILE_STATE_SCREEN; + + return GIF_OK; +} + +/****************************************************************************** + * This routine should be called before any attempt to dump an image - any + * call to any of the pixel dump routines. + *****************************************************************************/ +int +EGifPutImageDesc(GifFileType * GifFile, + int Left, + int Top, + int Width, + int Height, + int Interlace, + const ColorMapObject * ColorMap) { + + int i; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (Private->FileState & FILE_STATE_IMAGE && +#if defined(__MSDOS__) || defined(WIN32) || defined(__GNUC__) + Private->PixelCount > 0xffff0000UL) { +#else + Private->PixelCount > 0xffff0000) { +#endif /* __MSDOS__ */ + /* If already has active image descriptor - something is wrong! */ + _GifError = E_GIF_ERR_HAS_IMAG_DSCR; + return GIF_ERROR; + } + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + _GifError = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + GifFile->Image.Left = Left; + GifFile->Image.Top = Top; + GifFile->Image.Width = Width; + GifFile->Image.Height = Height; + GifFile->Image.Interlace = Interlace; + if (ColorMap) { + GifFile->Image.ColorMap = MakeMapObject(ColorMap->ColorCount, + ColorMap->Colors); + if (GifFile->Image.ColorMap == NULL) { + _GifError = E_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } else { + GifFile->Image.ColorMap = NULL; + } + + /* Put the image descriptor into the file: */ + Buf[0] = ','; /* Image seperator character. */ +#ifndef DEBUG_NO_PREFIX + WRITE(GifFile, Buf, 1); +#endif /* DEBUG_NO_PREFIX */ + EGifPutWord(Left, GifFile); + EGifPutWord(Top, GifFile); + EGifPutWord(Width, GifFile); + EGifPutWord(Height, GifFile); + Buf[0] = (ColorMap ? 0x80 : 0x00) | + (Interlace ? 0x40 : 0x00) | + (ColorMap ? ColorMap->BitsPerPixel - 1 : 0); +#ifndef DEBUG_NO_PREFIX + WRITE(GifFile, Buf, 1); +#endif /* DEBUG_NO_PREFIX */ + + /* If we have Global color map - dump it also: */ +#ifndef DEBUG_NO_PREFIX + if (ColorMap != NULL) + for (i = 0; i < ColorMap->ColorCount; i++) { + /* Put the ColorMap out also: */ + Buf[0] = ColorMap->Colors[i].Red; + Buf[1] = ColorMap->Colors[i].Green; + Buf[2] = ColorMap->Colors[i].Blue; + if (WRITE(GifFile, Buf, 3) != 3) { + _GifError = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + } +#endif /* DEBUG_NO_PREFIX */ + if (GifFile->SColorMap == NULL && GifFile->Image.ColorMap == NULL) { + _GifError = E_GIF_ERR_NO_COLOR_MAP; + return GIF_ERROR; + } + + /* Mark this file as has screen descriptor: */ + Private->FileState |= FILE_STATE_IMAGE; + Private->PixelCount = (long)Width *(long)Height; + + EGifSetupCompress(GifFile); /* Reset compress algorithm parameters. */ + + return GIF_OK; +} + +/****************************************************************************** + * Put one full scanned line (Line) of length LineLen into GIF file. + *****************************************************************************/ +int +EGifPutLine(GifFileType * GifFile, + GifPixelType * Line, + int LineLen) { + + int i; + GifPixelType Mask; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + _GifError = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + if (!LineLen) + LineLen = GifFile->Image.Width; + if (Private->PixelCount < (unsigned)LineLen) { + _GifError = E_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + Private->PixelCount -= LineLen; + + /* Make sure the codes are not out of bit range, as we might generate + * wrong code (because of overflow when we combine them) in this case: */ + Mask = CodeMask[Private->BitsPerPixel]; + for (i = 0; i < LineLen; i++) + Line[i] &= Mask; + + return EGifCompressLine(GifFile, Line, LineLen); +} + +/****************************************************************************** + * Put one pixel (Pixel) into GIF file. + *****************************************************************************/ +int +EGifPutPixel(GifFileType * GifFile, + GifPixelType Pixel) { + + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + _GifError = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + if (Private->PixelCount == 0) { + _GifError = E_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + --Private->PixelCount; + + /* Make sure the code is not out of bit range, as we might generate + * wrong code (because of overflow when we combine them) in this case: */ + Pixel &= CodeMask[Private->BitsPerPixel]; + + return EGifCompressLine(GifFile, &Pixel, 1); +} + +/****************************************************************************** + * Put a comment into GIF file using the GIF89 comment extension block. + *****************************************************************************/ +int +EGifPutComment(GifFileType * GifFile, + const char *Comment) { + + unsigned int length = strlen(Comment); + char *buf; + + length = strlen(Comment); + if (length <= 255) { + return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE, + length, Comment); + } else { + buf = (char *)Comment; + if (EGifPutExtensionFirst(GifFile, COMMENT_EXT_FUNC_CODE, 255, buf) + == GIF_ERROR) { + return GIF_ERROR; + } + length -= 255; + buf = buf + 255; + + /* Break the comment into 255 byte sub blocks */ + while (length > 255) { + if (EGifPutExtensionNext(GifFile, 0, 255, buf) == GIF_ERROR) { + return GIF_ERROR; + } + buf = buf + 255; + length -= 255; + } + /* Output any partial block and the clear code. */ + if (length > 0) { + if (EGifPutExtensionLast(GifFile, 0, length, buf) == GIF_ERROR) { + return GIF_ERROR; + } + } else { + if (EGifPutExtensionLast(GifFile, 0, 0, NULL) == GIF_ERROR) { + return GIF_ERROR; + } + } + } + return GIF_OK; +} + +/****************************************************************************** + * Put a first extension block (see GIF manual) into gif file. Here more + * extensions can be dumped using EGifPutExtensionNext until + * EGifPutExtensionLast is invoked. + *****************************************************************************/ +int +EGifPutExtensionFirst(GifFileType * GifFile, + int ExtCode, + int ExtLen, + const VoidPtr Extension) { + + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + _GifError = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + if (ExtCode == 0) { + WRITE(GifFile, (GifByteType *)&ExtLen, 1); + } else { + Buf[0] = '!'; + Buf[1] = ExtCode; + Buf[2] = ExtLen; + WRITE(GifFile, Buf, 3); + } + + WRITE(GifFile, Extension, ExtLen); + + return GIF_OK; +} + +/****************************************************************************** + * Put a middle extension block (see GIF manual) into gif file. + *****************************************************************************/ +int +EGifPutExtensionNext(GifFileType * GifFile, + int ExtCode, + int ExtLen, + const VoidPtr Extension) { + + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + _GifError = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + Buf = ExtLen; + WRITE(GifFile, &Buf, 1); + WRITE(GifFile, Extension, ExtLen); + + return GIF_OK; +} + +/****************************************************************************** + * Put a last extension block (see GIF manual) into gif file. + *****************************************************************************/ +int +EGifPutExtensionLast(GifFileType * GifFile, + int ExtCode, + int ExtLen, + const VoidPtr Extension) { + + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + _GifError = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + /* If we are given an extension sub-block output it now. */ + if (ExtLen > 0) { + Buf = ExtLen; + WRITE(GifFile, &Buf, 1); + WRITE(GifFile, Extension, ExtLen); + } + + /* Write the block terminator */ + Buf = 0; + WRITE(GifFile, &Buf, 1); + + return GIF_OK; +} + +/****************************************************************************** + * Put an extension block (see GIF manual) into gif file. + * Warning: This function is only useful for Extension blocks that have at + * most one subblock. Extensions with more than one subblock need to use the + * EGifPutExtension{First,Next,Last} functions instead. + *****************************************************************************/ +int +EGifPutExtension(GifFileType * GifFile, + int ExtCode, + int ExtLen, + const VoidPtr Extension) { + + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + _GifError = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + if (ExtCode == 0) + WRITE(GifFile, (GifByteType *)&ExtLen, 1); + else { + Buf[0] = '!'; /* Extension Introducer 0x21 */ + Buf[1] = ExtCode; /* Extension Label */ + Buf[2] = ExtLen; /* Extension length */ + WRITE(GifFile, Buf, 3); + } + WRITE(GifFile, Extension, ExtLen); + Buf[0] = 0; + WRITE(GifFile, Buf, 1); + + return GIF_OK; +} + +/****************************************************************************** + * Put the image code in compressed form. This routine can be called if the + * information needed to be piped out as is. Obviously this is much faster + * than decoding and encoding again. This routine should be followed by calls + * to EGifPutCodeNext, until NULL block is given. + * The block should NOT be freed by the user (not dynamically allocated). + *****************************************************************************/ +int +EGifPutCode(GifFileType * GifFile, + int CodeSize, + const GifByteType * CodeBlock) { + + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + _GifError = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + /* No need to dump code size as Compression set up does any for us: */ + /* + * Buf = CodeSize; + * if (WRITE(GifFile, &Buf, 1) != 1) { + * _GifError = E_GIF_ERR_WRITE_FAILED; + * return GIF_ERROR; + * } + */ + + return EGifPutCodeNext(GifFile, CodeBlock); +} + +/****************************************************************************** + * Continue to put the image code in compressed form. This routine should be + * called with blocks of code as read via DGifGetCode/DGifGetCodeNext. If + * given buffer pointer is NULL, empty block is written to mark end of code. + *****************************************************************************/ +int +EGifPutCodeNext(GifFileType * GifFile, + const GifByteType * CodeBlock) { + + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (CodeBlock != NULL) { + if (WRITE(GifFile, CodeBlock, CodeBlock[0] + 1) + != (unsigned)(CodeBlock[0] + 1)) { + _GifError = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + } else { + Buf = 0; + if (WRITE(GifFile, &Buf, 1) != 1) { + _GifError = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + Private->PixelCount = 0; /* And local info. indicate image read. */ + } + + return GIF_OK; +} + +/****************************************************************************** + * This routine should be called last, to close GIF file. + *****************************************************************************/ +int +EGifCloseFile(GifFileType * GifFile) { + + GifByteType Buf; + GifFilePrivateType *Private; + FILE *File; + + if (GifFile == NULL) + return GIF_ERROR; + + Private = (GifFilePrivateType *) GifFile->Private; + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + _GifError = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + File = Private->File; + + Buf = ';'; + WRITE(GifFile, &Buf, 1); + + if (GifFile->Image.ColorMap) { + FreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + if (GifFile->SColorMap) { + FreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + } + if (Private) { + if (Private->HashTable) { + free((char *) Private->HashTable); + } + free((char *) Private); + } + free(GifFile); + + if (File && fclose(File) != 0) { + _GifError = E_GIF_ERR_CLOSE_FAILED; + return GIF_ERROR; + } + return GIF_OK; +} + +/****************************************************************************** + * Put 2 bytes (word) into the given file: + *****************************************************************************/ +static int +EGifPutWord(int Word, + GifFileType * GifFile) { + + unsigned char c[2]; + + c[0] = Word & 0xff; + c[1] = (Word >> 8) & 0xff; +#ifndef DEBUG_NO_PREFIX + if (WRITE(GifFile, c, 2) == 2) + return GIF_OK; + else + return GIF_ERROR; +#else + return GIF_OK; +#endif /* DEBUG_NO_PREFIX */ +} + +/****************************************************************************** + * Setup the LZ compression for this image: + *****************************************************************************/ +static int +EGifSetupCompress(GifFileType * GifFile) { + + int BitsPerPixel; + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + /* Test and see what color map to use, and from it # bits per pixel: */ + if (GifFile->Image.ColorMap) + BitsPerPixel = GifFile->Image.ColorMap->BitsPerPixel; + else if (GifFile->SColorMap) + BitsPerPixel = GifFile->SColorMap->BitsPerPixel; + else { + _GifError = E_GIF_ERR_NO_COLOR_MAP; + return GIF_ERROR; + } + + Buf = BitsPerPixel = (BitsPerPixel < 2 ? 2 : BitsPerPixel); + WRITE(GifFile, &Buf, 1); /* Write the Code size to file. */ + + Private->Buf[0] = 0; /* Nothing was output yet. */ + Private->BitsPerPixel = BitsPerPixel; + Private->ClearCode = (1 << BitsPerPixel); + Private->EOFCode = Private->ClearCode + 1; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ + Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ + Private->CrntCode = FIRST_CODE; /* Signal that this is first one! */ + Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ + Private->CrntShiftDWord = 0; + + /* Clear hash table and send Clear to make sure the decoder do the same. */ + _ClearHashTable(Private->HashTable); + + if (EGifCompressOutput(GifFile, Private->ClearCode) == GIF_ERROR) { + _GifError = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + return GIF_OK; +} + +/****************************************************************************** + * The LZ compression routine: + * This version compresses the given buffer Line of length LineLen. + * This routine can be called a few times (one per scan line, for example), in + * order to complete the whole image. +******************************************************************************/ +static int +EGifCompressLine(GifFileType * GifFile, + GifPixelType * Line, + int LineLen) { + + int i = 0, CrntCode, NewCode; + unsigned long NewKey; + GifPixelType Pixel; + GifHashTableType *HashTable; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + HashTable = Private->HashTable; + + if (Private->CrntCode == FIRST_CODE) /* Its first time! */ + CrntCode = Line[i++]; + else + CrntCode = Private->CrntCode; /* Get last code in compression. */ + + while (i < LineLen) { /* Decode LineLen items. */ + Pixel = Line[i++]; /* Get next pixel from stream. */ + /* Form a new unique key to search hash table for the code combines + * CrntCode as Prefix string with Pixel as postfix char. + */ + NewKey = (((UINT32) CrntCode) << 8) + Pixel; + if ((NewCode = _ExistsHashTable(HashTable, NewKey)) >= 0) { + /* This Key is already there, or the string is old one, so + * simple take new code as our CrntCode: + */ + CrntCode = NewCode; + } else { + /* Put it in hash table, output the prefix code, and make our + * CrntCode equal to Pixel. + */ + if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) { + _GifError = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + CrntCode = Pixel; + + /* If however the HashTable if full, we send a clear first and + * Clear the hash table. + */ + if (Private->RunningCode >= LZ_MAX_CODE) { + /* Time to do some clearance: */ + if (EGifCompressOutput(GifFile, Private->ClearCode) + == GIF_ERROR) { + _GifError = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + _ClearHashTable(HashTable); + } else { + /* Put this unique key with its relative Code in hash table: */ + _InsertHashTable(HashTable, NewKey, Private->RunningCode++); + } + } + + } + + /* Preserve the current state of the compression algorithm: */ + Private->CrntCode = CrntCode; + + if (Private->PixelCount == 0) { + /* We are done - output last Code and flush output buffers: */ + if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) { + _GifError = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + if (EGifCompressOutput(GifFile, Private->EOFCode) == GIF_ERROR) { + _GifError = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + if (EGifCompressOutput(GifFile, FLUSH_OUTPUT) == GIF_ERROR) { + _GifError = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + } + + return GIF_OK; +} + +/****************************************************************************** + * The LZ compression output routine: + * This routine is responsible for the compression of the bit stream into + * 8 bits (bytes) packets. + * Returns GIF_OK if written succesfully. + *****************************************************************************/ +static int +EGifCompressOutput(GifFileType * GifFile, + int Code) { + + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + int retval = GIF_OK; + + if (Code == FLUSH_OUTPUT) { + while (Private->CrntShiftState > 0) { + /* Get Rid of what is left in DWord, and flush it. */ + if (EGifBufferedOutput(GifFile, Private->Buf, + Private->CrntShiftDWord & 0xff) == GIF_ERROR) + retval = GIF_ERROR; + Private->CrntShiftDWord >>= 8; + Private->CrntShiftState -= 8; + } + Private->CrntShiftState = 0; /* For next time. */ + if (EGifBufferedOutput(GifFile, Private->Buf, + FLUSH_OUTPUT) == GIF_ERROR) + retval = GIF_ERROR; + } else { + Private->CrntShiftDWord |= ((long)Code) << Private->CrntShiftState; + Private->CrntShiftState += Private->RunningBits; + while (Private->CrntShiftState >= 8) { + /* Dump out full bytes: */ + if (EGifBufferedOutput(GifFile, Private->Buf, + Private->CrntShiftDWord & 0xff) == GIF_ERROR) + retval = GIF_ERROR; + Private->CrntShiftDWord >>= 8; + Private->CrntShiftState -= 8; + } + } + + /* If code cannt fit into RunningBits bits, must raise its size. Note */ + /* however that codes above 4095 are used for special signaling. */ + if (Private->RunningCode >= Private->MaxCode1 && Code <= 4095) { + Private->MaxCode1 = 1 << ++Private->RunningBits; + } + + return retval; +} + +/****************************************************************************** + * This routines buffers the given characters until 255 characters are ready + * to be output. If Code is equal to -1 the buffer is flushed (EOF). + * The buffer is Dumped with first byte as its size, as GIF format requires. + * Returns GIF_OK if written succesfully. + *****************************************************************************/ +static int +EGifBufferedOutput(GifFileType * GifFile, + GifByteType * Buf, + int c) { + + if (c == FLUSH_OUTPUT) { + /* Flush everything out. */ + if (Buf[0] != 0 + && WRITE(GifFile, Buf, Buf[0] + 1) != (unsigned)(Buf[0] + 1)) { + _GifError = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + /* Mark end of compressed data, by an empty block (see GIF doc): */ + Buf[0] = 0; + if (WRITE(GifFile, Buf, 1) != 1) { + _GifError = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + } else { + if (Buf[0] == 255) { + /* Dump out this buffer - it is full: */ + if (WRITE(GifFile, Buf, Buf[0] + 1) != (unsigned)(Buf[0] + 1)) { + _GifError = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + Buf[0] = 0; + } + Buf[++Buf[0]] = c; + } + + return GIF_OK; +} + +/****************************************************************************** + * This routine writes to disk an in-core representation of a GIF previously + * created by DGifSlurp(). + *****************************************************************************/ +int +EGifSpew(GifFileType * GifFileOut) { + + int i, j, gif89 = FALSE; + int bOff; /* Block Offset for adding sub blocks in Extensions */ + char SavedStamp[GIF_STAMP_LEN + 1]; + + for (i = 0; i < GifFileOut->ImageCount; i++) { + for (j = 0; j < GifFileOut->SavedImages[i].ExtensionBlockCount; j++) { + int function = + GifFileOut->SavedImages[i].ExtensionBlocks[j].Function; + + if (function == COMMENT_EXT_FUNC_CODE + || function == GRAPHICS_EXT_FUNC_CODE + || function == PLAINTEXT_EXT_FUNC_CODE + || function == APPLICATION_EXT_FUNC_CODE) + gif89 = TRUE; + } + } + + strncpy(SavedStamp, GifVersionPrefix, GIF_STAMP_LEN); + if (gif89) { + strncpy(GifVersionPrefix, GIF89_STAMP, GIF_STAMP_LEN); + } else { + strncpy(GifVersionPrefix, GIF87_STAMP, GIF_STAMP_LEN); + } + if (EGifPutScreenDesc(GifFileOut, + GifFileOut->SWidth, + GifFileOut->SHeight, + GifFileOut->SColorResolution, + GifFileOut->SBackGroundColor, + GifFileOut->SColorMap) == GIF_ERROR) { + strncpy(GifVersionPrefix, SavedStamp, GIF_STAMP_LEN); + return (GIF_ERROR); + } + strncpy(GifVersionPrefix, SavedStamp, GIF_STAMP_LEN); + + for (i = 0; i < GifFileOut->ImageCount; i++) { + SavedImage *sp = &GifFileOut->SavedImages[i]; + int SavedHeight = sp->ImageDesc.Height; + int SavedWidth = sp->ImageDesc.Width; + ExtensionBlock *ep; + + /* this allows us to delete images by nuking their rasters */ + if (sp->RasterBits == NULL) + continue; + + if (sp->ExtensionBlocks) { + for (j = 0; j < sp->ExtensionBlockCount; j++) { + ep = &sp->ExtensionBlocks[j]; + if (j == sp->ExtensionBlockCount - 1 || (ep+1)->Function != 0) { + /*** FIXME: Must check whether outputting + * is ever valid or if we should just + * drop anything with a 0 for the Function. (And whether + * we should drop here or in EGifPutExtension) + */ + if (EGifPutExtension(GifFileOut, + (ep->Function != 0) ? ep->Function : '\0', + ep->ByteCount, + ep->Bytes) == GIF_ERROR) { + return (GIF_ERROR); + } + } else { + EGifPutExtensionFirst(GifFileOut, ep->Function, ep->ByteCount, ep->Bytes); + for (bOff = j+1; bOff < sp->ExtensionBlockCount; bOff++) { + ep = &sp->ExtensionBlocks[bOff]; + if (ep->Function != 0) { + break; + } + EGifPutExtensionNext(GifFileOut, 0, + ep->ByteCount, ep->Bytes); + } + EGifPutExtensionLast(GifFileOut, 0, 0, NULL); + j = bOff-1; + } + } + } + + if (EGifPutImageDesc(GifFileOut, + sp->ImageDesc.Left, + sp->ImageDesc.Top, + SavedWidth, + SavedHeight, + sp->ImageDesc.Interlace, + sp->ImageDesc.ColorMap) == GIF_ERROR) + return (GIF_ERROR); + + for (j = 0; j < SavedHeight; j++) { + if (EGifPutLine(GifFileOut, + sp->RasterBits + j * SavedWidth, + SavedWidth) == GIF_ERROR) + return (GIF_ERROR); + } + } + + if (EGifCloseFile(GifFileOut) == GIF_ERROR) + return (GIF_ERROR); + + return (GIF_OK); +} diff --git a/bazaar/plugin/gdal/frmts/gif/giflib/gif_err.c b/bazaar/plugin/gdal/frmts/gif/giflib/gif_err.c new file mode 100644 index 000000000..ea977bdf9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/giflib/gif_err.c @@ -0,0 +1,120 @@ +/***************************************************************************** + * "Gif-Lib" - Yet another gif library. + * + * Written by: Gershon Elber IBM PC Ver 0.1, Jun. 1989 + ***************************************************************************** + * Handle error reporting for the GIF library. + ***************************************************************************** + * History: + * 17 Jun 89 - Version 1.0 by Gershon Elber. + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "gif_lib.h" + +int _GifError = 0; + +/***************************************************************************** + * Return the last GIF error (0 if none) and reset the error. + ****************************************************************************/ +int +GifLastError(void) { + int i = _GifError; + + _GifError = 0; + + return i; +} +#ifndef _GBA_NO_FILEIO + +/***************************************************************************** + * Print the last GIF error to stderr. + ****************************************************************************/ +void +PrintGifError(void) { + char *Err; + + switch (_GifError) { + case E_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case E_GIF_ERR_WRITE_FAILED: + Err = "Failed to Write to given file"; + break; + case E_GIF_ERR_HAS_SCRN_DSCR: + Err = "Screen Descriptor already been set"; + break; + case E_GIF_ERR_HAS_IMAG_DSCR: + Err = "Image Descriptor is still active"; + break; + case E_GIF_ERR_NO_COLOR_MAP: + Err = "Neither Global Nor Local color map"; + break; + case E_GIF_ERR_DATA_TOO_BIG: + Err = "#Pixels bigger than Width * Height"; + break; + case E_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Fail to allocate required memory"; + break; + case E_GIF_ERR_DISK_IS_FULL: + Err = "Write failed (disk full?)"; + break; + case E_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case E_GIF_ERR_NOT_WRITEABLE: + Err = "Given file was not opened for write"; + break; + case D_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case D_GIF_ERR_READ_FAILED: + Err = "Failed to Read from given file"; + break; + case D_GIF_ERR_NOT_GIF_FILE: + Err = "Given file is NOT GIF file"; + break; + case D_GIF_ERR_NO_SCRN_DSCR: + Err = "No Screen Descriptor detected"; + break; + case D_GIF_ERR_NO_IMAG_DSCR: + Err = "No Image Descriptor detected"; + break; + case D_GIF_ERR_NO_COLOR_MAP: + Err = "Neither Global Nor Local color map"; + break; + case D_GIF_ERR_WRONG_RECORD: + Err = "Wrong record type detected"; + break; + case D_GIF_ERR_DATA_TOO_BIG: + Err = "#Pixels bigger than Width * Height"; + break; + case D_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Fail to allocate required memory"; + break; + case D_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case D_GIF_ERR_NOT_READABLE: + Err = "Given file was not opened for read"; + break; + case D_GIF_ERR_IMAGE_DEFECT: + Err = "Image is defective, decoding aborted"; + break; + case D_GIF_ERR_EOF_TOO_SOON: + Err = "Image EOF detected, before image complete"; + break; + default: + Err = NULL; + break; + } + if (Err != NULL) + fprintf(stderr, "\nGIF-LIB error: %s.\n", Err); + else + fprintf(stderr, "\nGIF-LIB undefined error %d.\n", _GifError); +} +#endif /* _GBA_NO_FILEIO */ diff --git a/bazaar/plugin/gdal/frmts/gif/giflib/gif_hash.c b/bazaar/plugin/gdal/frmts/gif/giflib/gif_hash.c new file mode 100644 index 000000000..0365ad8bc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/giflib/gif_hash.c @@ -0,0 +1,131 @@ +/***************************************************************************** +* "Gif-Lib" - Yet another gif library. * +* * +* Written by: Gershon Elber IBM PC Ver 0.1, Jun. 1989 * +****************************************************************************** +* Module to support the following operations: * +* * +* 1. InitHashTable - initialize hash table. * +* 2. ClearHashTable - clear the hash table to an empty state. * +* 2. InsertHashTable - insert one item into data structure. * +* 3. ExistsHashTable - test if item exists in data structure. * +* * +* This module is used to hash the GIF codes during encoding. * +****************************************************************************** +* History: * +* 14 Jun 89 - Version 1.0 by Gershon Elber. * +*****************************************************************************/ + +#include +#include +#include +#include "gif_lib.h" +#include "gif_hash.h" +#include "gif_lib_private.h" + +/* #define DEBUG_HIT_RATE Debug number of misses per hash Insert/Exists. */ + +#ifdef DEBUG_HIT_RATE +static long NumberOfTests = 0, + NumberOfMisses = 0; +#endif /* DEBUG_HIT_RATE */ + +static int KeyItem(UINT32 Item); + +/****************************************************************************** +* Initialize HashTable - allocate the memory needed and clear it. * +******************************************************************************/ +GifHashTableType *_InitHashTable(void) +{ + GifHashTableType *HashTable; + + if ((HashTable = (GifHashTableType *) malloc(sizeof(GifHashTableType))) + == NULL) + return NULL; + + _ClearHashTable(HashTable); + + return HashTable; +} + +/****************************************************************************** +* Routine to clear the HashTable to an empty state. * +* This part is a little machine depended. Use the commented part otherwise. * +******************************************************************************/ +void _ClearHashTable(GifHashTableType *HashTable) +{ + memset(HashTable -> HTable, 0xFF, HT_SIZE * sizeof(UINT32)); +} + +/****************************************************************************** +* Routine to insert a new Item into the HashTable. The data is assumed to be * +* new one. * +******************************************************************************/ +void _InsertHashTable(GifHashTableType *HashTable, UINT32 Key, int Code) +{ + int HKey = KeyItem(Key); + UINT32 *HTable = HashTable -> HTable; + +#ifdef DEBUG_HIT_RATE + NumberOfTests++; + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + + while (HT_GET_KEY(HTable[HKey]) != 0xFFFFFL) { +#ifdef DEBUG_HIT_RATE + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + HKey = (HKey + 1) & HT_KEY_MASK; + } + HTable[HKey] = HT_PUT_KEY(Key) | HT_PUT_CODE(Code); +} + +/****************************************************************************** +* Routine to test if given Key exists in HashTable and if so returns its code * +* Returns the Code if key was found, -1 if not. * +******************************************************************************/ +int _ExistsHashTable(GifHashTableType *HashTable, UINT32 Key) +{ + int HKey = KeyItem(Key); + UINT32 *HTable = HashTable -> HTable, HTKey; + +#ifdef DEBUG_HIT_RATE + NumberOfTests++; + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + + while ((HTKey = HT_GET_KEY(HTable[HKey])) != 0xFFFFFL) { +#ifdef DEBUG_HIT_RATE + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + if (Key == HTKey) return HT_GET_CODE(HTable[HKey]); + HKey = (HKey + 1) & HT_KEY_MASK; + } + + return -1; +} + +/****************************************************************************** +* Routine to generate an HKey for the hashtable out of the given unique key. * +* The given Key is assumed to be 20 bits as follows: lower 8 bits are the * +* new postfix character, while the upper 12 bits are the prefix code. * +* Because the average hit ratio is only 2 (2 hash references per entry), * +* evaluating more complex keys (such as twin prime keys) does not worth it! * +******************************************************************************/ +static int KeyItem(UINT32 Item) +{ + return ((Item >> 12) ^ Item) & HT_KEY_MASK; +} + +#ifdef DEBUG_HIT_RATE +/****************************************************************************** +* Debugging routine to print the hit ratio - number of times the hash table * +* was tested per operation. This routine was used to test the KeyItem routine * +******************************************************************************/ +void HashTablePrintHitRatio(void) +{ + printf("Hash Table Hit Ratio is %ld/%ld = %ld%%.\n", + NumberOfMisses, NumberOfTests, + NumberOfMisses * 100 / NumberOfTests); +} +#endif /* DEBUG_HIT_RATE */ diff --git a/bazaar/plugin/gdal/frmts/gif/giflib/gif_hash.h b/bazaar/plugin/gdal/frmts/gif/giflib/gif_hash.h new file mode 100644 index 000000000..a82595fed --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/giflib/gif_hash.h @@ -0,0 +1,62 @@ +/****************************************************************************** +* Declarations, global to other of the GIF-HASH.C module. * +* * +* Written by Gershon Elber, Jun 1989 * +******************************************************************************* +* History: * +* 14 Jun 89 - Version 1.0 by Gershon Elber. * +******************************************************************************/ + +#ifndef _GIF_HASH_H_ +#define _GIF_HASH_H_ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* Find a thirty-two bit int type */ +#ifdef HAVE_STDINT_H +#include +#endif +#ifdef HAVE_INTTYPES_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_BASETSD_H +#include +#endif + +#define HT_SIZE 8192 /* 12bits = 4096 or twice as big! */ +#define HT_KEY_MASK 0x1FFF /* 13bits keys */ +#define HT_KEY_NUM_BITS 13 /* 13bits keys */ +#define HT_MAX_KEY 8191 /* 13bits - 1, maximal code possible */ +#define HT_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ + +/* The 32 bits of the long are divided into two parts for the key & code: */ +/* 1. The code is 12 bits as our compression algorithm is limited to 12bits */ +/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */ +/* The key is the upper 20 bits. The code is the lower 12. */ +#define HT_GET_KEY(l) (l >> 12) +#define HT_GET_CODE(l) (l & 0x0FFF) +#define HT_PUT_KEY(l) (l << 12) +#define HT_PUT_CODE(l) (l & 0x0FFF) + +/* GDAL added */ +typedef unsigned int UINT32; + +typedef struct GifHashTableType { + UINT32 HTable[HT_SIZE]; +} GifHashTableType; + +GifHashTableType *_InitHashTable(void); +void _ClearHashTable(GifHashTableType *HashTable); +void _InsertHashTable(GifHashTableType *HashTable, UINT32 Key, int Code); +int _ExistsHashTable(GifHashTableType *HashTable, UINT32 Key); + +#endif /* _GIF_HASH_H_ */ diff --git a/bazaar/plugin/gdal/frmts/gif/giflib/gif_lib.h b/bazaar/plugin/gdal/frmts/gif/giflib/gif_lib.h new file mode 100644 index 000000000..54acd91b4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/giflib/gif_lib.h @@ -0,0 +1,336 @@ +/****************************************************************************** + * In order to make life a little bit easier when using the GIF file format, + * this library was written, and which does all the dirty work... + * + * Written by Gershon Elber, Jun. 1989 + * Hacks by Eric S. Raymond, Sep. 1992 + ****************************************************************************** + * History: + * 14 Jun 89 - Version 1.0 by Gershon Elber. + * 3 Sep 90 - Version 1.1 by Gershon Elber (Support for Gif89, Unique names) + * 15 Sep 90 - Version 2.0 by Eric S. Raymond (Changes to suoport GIF slurp) + * 26 Jun 96 - Version 3.0 by Eric S. Raymond (Full GIF89 support) + * 17 Dec 98 - Version 4.0 by Toshio Kuratomi (Fix extension writing code) + *****************************************************************************/ + +#ifndef _GIF_LIB_H_ +#define _GIF_LIB_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GIF_LIB_VERSION " Version 4.1, " + +#define GIF_ERROR 0 +#define GIF_OK 1 + +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ +#ifndef FALSE +#define FALSE 0 +#endif /* FALSE */ + +#ifndef NULL +#define NULL 0 +#endif /* NULL */ + +#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */ +#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1 +#define GIF_VERSION_POS 3 /* Version first character in stamp. */ +#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */ +#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */ + +#define GIF_FILE_BUFFER_SIZE 16384 /* Files uses bigger buffers than usual. */ + +typedef int GifBooleanType; +typedef unsigned char GifPixelType; +typedef unsigned char *GifRowType; +typedef unsigned char GifByteType; +#ifdef _GBA_OPTMEM + typedef unsigned short GifPrefixType; + typedef short GifWord; +#else + typedef unsigned int GifPrefixType; + typedef int GifWord; +#endif + +#define GIF_MESSAGE(Msg) fprintf(stderr, "\n%s: %s\n", PROGRAM_NAME, Msg) +#define GIF_EXIT(Msg) { GIF_MESSAGE(Msg); exit(-3); } + +#ifdef SYSV +#define VoidPtr char * +#else +#define VoidPtr void * +#endif /* SYSV */ + +typedef struct GifColorType { + GifByteType Red, Green, Blue; +} GifColorType; + +typedef struct ColorMapObject { + int ColorCount; + int BitsPerPixel; + GifColorType *Colors; /* on malloc(3) heap */ +} ColorMapObject; + +typedef struct GifImageDesc { + GifWord Left, Top, Width, Height, /* Current image dimensions. */ + Interlace; /* Sequential/Interlaced lines. */ + ColorMapObject *ColorMap; /* The local color map */ +} GifImageDesc; + +typedef struct GifFileType { + GifWord SWidth, SHeight, /* Screen dimensions. */ + SColorResolution, /* How many colors can we generate? */ + SBackGroundColor; /* I hope you understand this one... */ + ColorMapObject *SColorMap; /* NULL if not exists. */ + int ImageCount; /* Number of current image */ + GifImageDesc Image; /* Block describing current image */ + struct SavedImage *SavedImages; /* Use this to accumulate file state */ + VoidPtr UserData; /* hook to attach user data (TVT) */ + VoidPtr Private; /* Don't mess with this! */ +} GifFileType; + +typedef enum { + UNDEFINED_RECORD_TYPE, + SCREEN_DESC_RECORD_TYPE, + IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */ + EXTENSION_RECORD_TYPE, /* Begin with '!' */ + TERMINATE_RECORD_TYPE /* Begin with ';' */ +} GifRecordType; + +/* DumpScreen2Gif routine constants identify type of window/screen to dump. + * Note all values below 1000 are reserved for the IBMPC different display + * devices (it has many!) and are compatible with the numbering TC2.0 + * (Turbo C 2.0 compiler for IBM PC) gives to these devices. + */ +typedef enum { + GIF_DUMP_SGI_WINDOW = 1000, + GIF_DUMP_X_WINDOW = 1001 +} GifScreenDumpType; + +/* func type to read gif data from arbitrary sources (TVT) */ +typedef int (*InputFunc) (GifFileType *, GifByteType *, int); + +/* func type to write gif data ro arbitrary targets. + * Returns count of bytes written. (MRB) + */ +typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int); + +/****************************************************************************** + * GIF89 extension function codes +******************************************************************************/ + +#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */ +#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control */ +#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */ +#define APPLICATION_EXT_FUNC_CODE 0xff /* application block */ + +/****************************************************************************** + * O.K., here are the routines one can access in order to encode GIF file: + * (GIF_LIB file EGIF_LIB.C). +******************************************************************************/ + +GifFileType *EGifOpenFileName(const char *GifFileName, + int GifTestExistance); +GifFileType *EGifOpenFileHandle(int GifFileHandle); +GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc); + +int EGifSpew(GifFileType * GifFile); +void EGifSetGifVersion(const char *Version); +int EGifPutScreenDesc(GifFileType * GifFile, + int GifWidth, int GifHeight, int GifColorRes, + int GifBackGround, + const ColorMapObject * GifColorMap); +int EGifPutImageDesc(GifFileType * GifFile, int GifLeft, int GifTop, + int Width, int GifHeight, int GifInterlace, + const ColorMapObject * GifColorMap); +int EGifPutLine(GifFileType * GifFile, GifPixelType * GifLine, + int GifLineLen); +int EGifPutPixel(GifFileType * GifFile, GifPixelType GifPixel); +int EGifPutComment(GifFileType * GifFile, const char *GifComment); +int EGifPutExtensionFirst(GifFileType * GifFile, int GifExtCode, + int GifExtLen, const VoidPtr GifExtension); +int EGifPutExtensionNext(GifFileType * GifFile, int GifExtCode, + int GifExtLen, const VoidPtr GifExtension); +int EGifPutExtensionLast(GifFileType * GifFile, int GifExtCode, + int GifExtLen, const VoidPtr GifExtension); +int EGifPutExtension(GifFileType * GifFile, int GifExtCode, int GifExtLen, + const VoidPtr GifExtension); +int EGifPutCode(GifFileType * GifFile, int GifCodeSize, + const GifByteType * GifCodeBlock); +int EGifPutCodeNext(GifFileType * GifFile, + const GifByteType * GifCodeBlock); +int EGifCloseFile(GifFileType * GifFile); + +#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */ +#define E_GIF_ERR_WRITE_FAILED 2 +#define E_GIF_ERR_HAS_SCRN_DSCR 3 +#define E_GIF_ERR_HAS_IMAG_DSCR 4 +#define E_GIF_ERR_NO_COLOR_MAP 5 +#define E_GIF_ERR_DATA_TOO_BIG 6 +#define E_GIF_ERR_NOT_ENOUGH_MEM 7 +#define E_GIF_ERR_DISK_IS_FULL 8 +#define E_GIF_ERR_CLOSE_FAILED 9 +#define E_GIF_ERR_NOT_WRITEABLE 10 + +/****************************************************************************** + * O.K., here are the routines one can access in order to decode GIF file: + * (GIF_LIB file DGIF_LIB.C). + *****************************************************************************/ +#ifndef _GBA_NO_FILEIO +GifFileType *DGifOpenFileName(const char *GifFileName); +GifFileType *DGifOpenFileHandle(int GifFileHandle); +int DGifSlurp(GifFileType * GifFile); +#endif /* _GBA_NO_FILEIO */ +GifFileType *DGifOpen(void *userPtr, InputFunc readFunc); /* new one + * (TVT) */ +int DGifGetScreenDesc(GifFileType * GifFile); +int DGifGetRecordType(GifFileType * GifFile, GifRecordType * GifType); +int DGifGetImageDesc(GifFileType * GifFile); +int DGifGetLine(GifFileType * GifFile, GifPixelType * GifLine, int GifLineLen); +int DGifGetPixel(GifFileType * GifFile, GifPixelType GifPixel); +int DGifGetComment(GifFileType * GifFile, char *GifComment); +int DGifGetExtension(GifFileType * GifFile, int *GifExtCode, + GifByteType ** GifExtension); +int DGifGetExtensionNext(GifFileType * GifFile, GifByteType ** GifExtension); +int DGifGetCode(GifFileType * GifFile, int *GifCodeSize, + GifByteType ** GifCodeBlock); +int DGifGetCodeNext(GifFileType * GifFile, GifByteType ** GifCodeBlock); +int DGifGetLZCodes(GifFileType * GifFile, int *GifCode); +int DGifCloseFile(GifFileType * GifFile); + +#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */ +#define D_GIF_ERR_READ_FAILED 102 +#define D_GIF_ERR_NOT_GIF_FILE 103 +#define D_GIF_ERR_NO_SCRN_DSCR 104 +#define D_GIF_ERR_NO_IMAG_DSCR 105 +#define D_GIF_ERR_NO_COLOR_MAP 106 +#define D_GIF_ERR_WRONG_RECORD 107 +#define D_GIF_ERR_DATA_TOO_BIG 108 +#define D_GIF_ERR_NOT_ENOUGH_MEM 109 +#define D_GIF_ERR_CLOSE_FAILED 110 +#define D_GIF_ERR_NOT_READABLE 111 +#define D_GIF_ERR_IMAGE_DEFECT 112 +#define D_GIF_ERR_EOF_TOO_SOON 113 + +/****************************************************************************** + * O.K., here are the routines from GIF_LIB file QUANTIZE.C. +******************************************************************************/ +int QuantizeBuffer(unsigned int Width, unsigned int Height, + int *ColorMapSize, GifByteType * RedInput, + GifByteType * GreenInput, GifByteType * BlueInput, + GifByteType * OutputBuffer, + GifColorType * OutputColorMap); + +/****************************************************************************** + * O.K., here are the routines from GIF_LIB file QPRINTF.C. +******************************************************************************/ +extern int GifQuietPrint; + +#ifdef HAVE_STDARG_H + extern void GifQprintf(char *Format, ...); +#elif defined (HAVE_VARARGS_H) + extern void GifQprintf(); +#endif /* HAVE_STDARG_H */ + +/****************************************************************************** + * O.K., here are the routines from GIF_LIB file GIF_ERR.C. +******************************************************************************/ +#ifndef _GBA_NO_FILEIO +extern void PrintGifError(void); +#endif /* _GBA_NO_FILEIO */ +extern int GifLastError(void); + +/****************************************************************************** + * O.K., here are the routines from GIF_LIB file DEV2GIF.C. +******************************************************************************/ +extern int DumpScreen2Gif(const char *FileName, + int ReqGraphDriver, + long ReqGraphMode1, + long ReqGraphMode2, + long ReqGraphMode3); + +/***************************************************************************** + * + * Everything below this point is new after version 1.2, supporting `slurp + * mode' for doing I/O in two big belts with all the image-bashing in core. + * + *****************************************************************************/ + +/****************************************************************************** + * Color Map handling from ALLOCGIF.C + *****************************************************************************/ + +extern ColorMapObject *MakeMapObject(int ColorCount, + const GifColorType * ColorMap); +extern void FreeMapObject(ColorMapObject * Object); +extern ColorMapObject *UnionColorMap(const ColorMapObject * ColorIn1, + const ColorMapObject * ColorIn2, + GifPixelType ColorTransIn2[]); +extern int BitSize(int n); + +/****************************************************************************** + * Support for the in-core structures allocation (slurp mode). + *****************************************************************************/ + +/* This is the in-core version of an extension record */ +typedef struct { + int ByteCount; + char *Bytes; /* on malloc(3) heap */ + int Function; /* Holds the type of the Extension block. */ +} ExtensionBlock; + +/* This holds an image header, its unpacked raster bits, and extensions */ +typedef struct SavedImage { + GifImageDesc ImageDesc; + unsigned char *RasterBits; /* on malloc(3) heap */ + int Function; /* DEPRECATED: Use ExtensionBlocks[x].Function instead */ + int ExtensionBlockCount; + ExtensionBlock *ExtensionBlocks; /* on malloc(3) heap */ +} SavedImage; + +extern void ApplyTranslation(SavedImage * Image, GifPixelType Translation[]); +extern void MakeExtension(SavedImage * New, int Function); +extern int AddExtensionBlock(SavedImage * New, int Len, + unsigned char ExtData[]); +extern void FreeExtension(SavedImage * Image); +extern SavedImage *MakeSavedImage(GifFileType * GifFile, + const SavedImage * CopyFrom); +extern void FreeSavedImages(GifFileType * GifFile); + +/****************************************************************************** + * The library's internal utility font + *****************************************************************************/ + +#define GIF_FONT_WIDTH 8 +#define GIF_FONT_HEIGHT 8 +extern unsigned char AsciiTable[][GIF_FONT_WIDTH]; + +#ifdef _WIN32 + extern void DrawGifText(SavedImage * Image, +#else + extern void DrawText(SavedImage * Image, +#endif + const int x, const int y, + const char *legend, const int color); + +extern void DrawBox(SavedImage * Image, + const int x, const int y, + const int w, const int d, const int color); + +void DrawRectangle(SavedImage * Image, + const int x, const int y, + const int w, const int d, const int color); + +extern void DrawBoxedText(SavedImage * Image, + const int x, const int y, + const char *legend, + const int border, const int bg, const int fg); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _GIF_LIB_H */ diff --git a/bazaar/plugin/gdal/frmts/gif/giflib/gif_lib_private.h b/bazaar/plugin/gdal/frmts/gif/giflib/gif_lib_private.h new file mode 100644 index 000000000..b31c9c5d7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/giflib/gif_lib_private.h @@ -0,0 +1,59 @@ +#ifndef _GIF_LIB_PRIVATE_H +#define _GIF_LIB_PRIVATE_H + +#include "gif_lib.h" +#include "gif_hash.h" + +#define PROGRAM_NAME "GIFLIB" + +#ifdef SYSV +#define VersionStr "Gif library module,\t\tEric S. Raymond\n\ + (C) Copyright 1997 Eric S. Raymond\n" +#else +#define VersionStr PROGRAM_NAME " IBMPC " GIF_LIB_VERSION \ + " Eric S. Raymond, " __DATE__ ", " \ + __TIME__ "\n" "(C) Copyright 1997 Eric S. Raymond\n" +#endif /* SYSV */ + +#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ +#define LZ_BITS 12 + +#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */ +#define FIRST_CODE 4097 /* Impossible code, to signal first. */ +#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */ + +#define FILE_STATE_WRITE 0x01 +#define FILE_STATE_SCREEN 0x02 +#define FILE_STATE_IMAGE 0x04 +#define FILE_STATE_READ 0x08 + +#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ) +#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE) + +typedef struct GifFilePrivateType { + GifWord FileState, FileHandle, /* Where all this data goes to! */ + BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */ + ClearCode, /* The CLEAR LZ code. */ + EOFCode, /* The EOF LZ code. */ + RunningCode, /* The next code algorithm can generate. */ + RunningBits, /* The number of bits required to represent RunningCode. */ + MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. */ + LastCode, /* The code before the current code. */ + CrntCode, /* Current algorithm code. */ + StackPtr, /* For character stack (see below). */ + CrntShiftState; /* Number of bits in CrntShiftDWord. */ + unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */ + unsigned long PixelCount; /* Number of pixels in image. */ + FILE *File; /* File as stream. */ + InputFunc Read; /* function to read gif input (TVT) */ + OutputFunc Write; /* function to write gif output (MRB) */ + GifByteType Buf[256]; /* Compressed input is buffered here. */ + GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */ + GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */ + GifPrefixType Prefix[LZ_MAX_CODE + 1]; + GifHashTableType *HashTable; +} GifFilePrivateType; + +extern int _GifError; + +#endif /* _GIF_LIB_PRIVATE_H */ diff --git a/bazaar/plugin/gdal/frmts/gif/giflib/gifalloc.c b/bazaar/plugin/gdal/frmts/gif/giflib/gifalloc.c new file mode 100644 index 000000000..79d23325b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/giflib/gifalloc.c @@ -0,0 +1,443 @@ +/***************************************************************************** + * "Gif-Lib" - Yet another gif library. + * + * Written by: Gershon Elber Ver 0.1, Jun. 1989 + * Extensively hacked by: Eric S. Raymond Ver 1.?, Sep 1992 + ***************************************************************************** + * GIF construction tools + ***************************************************************************** + * History: + * 15 Sep 92 - Version 1.0 by Eric Raymond. + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include "gif_lib.h" + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +/****************************************************************************** + * Miscellaneous utility functions + *****************************************************************************/ + +/* return smallest bitfield size n will fit in */ +int +BitSize(int n) { + + register int i; + + for (i = 1; i <= 8; i++) + if ((1 << i) >= n) + break; + return (i); +} + +/****************************************************************************** + * Color map object functions + *****************************************************************************/ + +/* + * Allocate a color map of given size; initialize with contents of + * ColorMap if that pointer is non-NULL. + */ +ColorMapObject * +MakeMapObject(int ColorCount, + const GifColorType * ColorMap) { + + ColorMapObject *Object; + + /*** FIXME: Our ColorCount has to be a power of two. Is it necessary to + * make the user know that or should we automatically round up instead? */ + if (ColorCount != (1 << BitSize(ColorCount))) { + return ((ColorMapObject *) NULL); + } + + Object = (ColorMapObject *)malloc(sizeof(ColorMapObject)); + if (Object == (ColorMapObject *) NULL) { + return ((ColorMapObject *) NULL); + } + + Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType)); + if (Object->Colors == (GifColorType *) NULL) { + return ((ColorMapObject *) NULL); + } + + Object->ColorCount = ColorCount; + Object->BitsPerPixel = BitSize(ColorCount); + + if (ColorMap) { + memcpy((char *)Object->Colors, + (char *)ColorMap, ColorCount * sizeof(GifColorType)); + } + + return (Object); +} + +/* + * Free a color map object + */ +void +FreeMapObject(ColorMapObject * Object) { + + if (Object != NULL) { + free(Object->Colors); + free(Object); + /*** FIXME: + * When we are willing to break API we need to make this function + * FreeMapObject(ColorMapObject **Object) + * and do this assignment to NULL here: + * *Object = NULL; + */ + } +} + +#ifdef DEBUG +void +DumpColorMap(ColorMapObject * Object, + FILE * fp) { + + if (Object) { + int i, j, Len = Object->ColorCount; + + for (i = 0; i < Len; i += 4) { + for (j = 0; j < 4 && j < Len; j++) { + fprintf(fp, "%3d: %02x %02x %02x ", i + j, + Object->Colors[i + j].Red, + Object->Colors[i + j].Green, + Object->Colors[i + j].Blue); + } + fprintf(fp, "\n"); + } + } +} +#endif /* DEBUG */ + +/* + * Compute the union of two given color maps and return it. If result can't + * fit into 256 colors, NULL is returned, the allocated union otherwise. + * ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are + * copied iff they didn't exist before. ColorTransIn2 maps the old + * ColorIn2 into ColorUnion color map table. + */ +ColorMapObject * +UnionColorMap(const ColorMapObject * ColorIn1, + const ColorMapObject * ColorIn2, + GifPixelType ColorTransIn2[]) { + + int i, j, CrntSlot, RoundUpTo, NewBitSize; + ColorMapObject *ColorUnion; + + /* + * Allocate table which will hold the result for sure. + */ + ColorUnion = MakeMapObject(MAX(ColorIn1->ColorCount, + ColorIn2->ColorCount) * 2, NULL); + + if (ColorUnion == NULL) + return (NULL); + + /* Copy ColorIn1 to ColorUnionSize; */ + /*** FIXME: What if there are duplicate entries into the colormap to begin + * with? */ + for (i = 0; i < ColorIn1->ColorCount; i++) + ColorUnion->Colors[i] = ColorIn1->Colors[i]; + CrntSlot = ColorIn1->ColorCount; + + /* + * Potentially obnoxious hack: + * + * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end + * of table 1. This is very useful if your display is limited to + * 16 colors. + */ + while (ColorIn1->Colors[CrntSlot - 1].Red == 0 + && ColorIn1->Colors[CrntSlot - 1].Green == 0 + && ColorIn1->Colors[CrntSlot - 1].Blue == 0) + CrntSlot--; + + /* Copy ColorIn2 to ColorUnionSize (use old colors if they exist): */ + for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) { + /* Let's see if this color already exists: */ + /*** FIXME: Will it ever occur that ColorIn2 will contain duplicate + * entries? So we should search from 0 to CrntSlot rather than + * ColorIn1->ColorCount? + */ + for (j = 0; j < ColorIn1->ColorCount; j++) + if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i], + sizeof(GifColorType)) == 0) + break; + + if (j < ColorIn1->ColorCount) + ColorTransIn2[i] = j; /* color exists in Color1 */ + else { + /* Color is new - copy it to a new slot: */ + ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i]; + ColorTransIn2[i] = CrntSlot++; + } + } + + if (CrntSlot > 256) { + FreeMapObject(ColorUnion); + return ((ColorMapObject *) NULL); + } + + NewBitSize = BitSize(CrntSlot); + RoundUpTo = (1 << NewBitSize); + + if (RoundUpTo != ColorUnion->ColorCount) { + register GifColorType *Map = ColorUnion->Colors; + + /* + * Zero out slots up to next power of 2. + * We know these slots exist because of the way ColorUnion's + * start dimension was computed. + */ + for (j = CrntSlot; j < RoundUpTo; j++) + Map[j].Red = Map[j].Green = Map[j].Blue = 0; + + /* perhaps we can shrink the map? */ + if (RoundUpTo < ColorUnion->ColorCount) + ColorUnion->Colors = (GifColorType *)realloc(Map, + sizeof(GifColorType) * RoundUpTo); + } + + ColorUnion->ColorCount = RoundUpTo; + ColorUnion->BitsPerPixel = NewBitSize; + + return (ColorUnion); +} + +/* + * Apply a given color translation to the raster bits of an image + */ +void +ApplyTranslation(SavedImage * Image, + GifPixelType Translation[]) { + + register int i; + register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width; + + for (i = 0; i < RasterSize; i++) + Image->RasterBits[i] = Translation[Image->RasterBits[i]]; +} + +/****************************************************************************** + * Extension record functions + *****************************************************************************/ + +void +MakeExtension(SavedImage * New, + int Function) { + + New->Function = Function; + /*** FIXME: + * Someday we might have to deal with multiple extensions. + * ??? Was this a note from Gershon or from me? Does the multiple + * extension blocks solve this or do we need multiple Functions? Or is + * this an obsolete function? (People should use AddExtensionBlock + * instead?) + * Looks like AddExtensionBlock needs to take the int Function argument + * then it can take the place of this function. Right now people have to + * use both. Fix AddExtensionBlock and add this to the deprecation list. + */ +} + +int +AddExtensionBlock(SavedImage * New, + int Len, + unsigned char ExtData[]) { + + ExtensionBlock *ep; + + if (New->ExtensionBlocks == NULL) + New->ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock)); + else + New->ExtensionBlocks = (ExtensionBlock *)realloc(New->ExtensionBlocks, + sizeof(ExtensionBlock) * + (New->ExtensionBlockCount + 1)); + + if (New->ExtensionBlocks == NULL) + return (GIF_ERROR); + + ep = &New->ExtensionBlocks[New->ExtensionBlockCount++]; + + ep->ByteCount=Len; + ep->Bytes = (char *)malloc(ep->ByteCount); + if (ep->Bytes == NULL) + return (GIF_ERROR); + + if (ExtData) { + memcpy(ep->Bytes, ExtData, Len); + ep->Function = New->Function; + } + + return (GIF_OK); +} + +void +FreeExtension(SavedImage * Image) +{ + ExtensionBlock *ep; + + if ((Image == NULL) || (Image->ExtensionBlocks == NULL)) { + return; + } + for (ep = Image->ExtensionBlocks; + ep < (Image->ExtensionBlocks + Image->ExtensionBlockCount); ep++) + (void)free((char *)ep->Bytes); + free((char *)Image->ExtensionBlocks); + Image->ExtensionBlocks = NULL; +} + +/****************************************************************************** + * Image block allocation functions +******************************************************************************/ + +/* Private Function: + * Frees the last image in the GifFile->SavedImages array + */ +void +FreeLastSavedImage(GifFileType *GifFile) { + + SavedImage *sp; + + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) + return; + + /* Remove one SavedImage from the GifFile */ + GifFile->ImageCount--; + sp = &GifFile->SavedImages[GifFile->ImageCount]; + + /* Deallocate its Colormap */ + if (sp->ImageDesc.ColorMap) { + FreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + + /* Deallocate the image data */ + if (sp->RasterBits) + free((char *)sp->RasterBits); + + /* Deallocate any extensions */ + if (sp->ExtensionBlocks) + FreeExtension(sp); + + /*** FIXME: We could realloc the GifFile->SavedImages structure but is + * there a point to it? Saves some memory but we'd have to do it every + * time. If this is used in FreeSavedImages then it would be inefficient + * (The whole array is going to be deallocated.) If we just use it when + * we want to free the last Image it's convenient to do it here. + */ +} + +/* + * Append an image block to the SavedImages array + */ +SavedImage * +MakeSavedImage(GifFileType * GifFile, + const SavedImage * CopyFrom) { + + SavedImage *sp; + + if (GifFile->SavedImages == NULL) + GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage)); + else + GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages, + sizeof(SavedImage) * (GifFile->ImageCount + 1)); + + if (GifFile->SavedImages == NULL) + return ((SavedImage *)NULL); + else { + sp = &GifFile->SavedImages[GifFile->ImageCount++]; + memset((char *)sp, '\0', sizeof(SavedImage)); + + if (CopyFrom) { + memcpy((char *)sp, CopyFrom, sizeof(SavedImage)); + + /* + * Make our own allocated copies of the heap fields in the + * copied record. This guards against potential aliasing + * problems. + */ + + /* first, the local color map */ + if (sp->ImageDesc.ColorMap) { + sp->ImageDesc.ColorMap = MakeMapObject( + CopyFrom->ImageDesc.ColorMap->ColorCount, + CopyFrom->ImageDesc.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + } + + /* next, the raster */ + sp->RasterBits = (unsigned char *)malloc(sizeof(GifPixelType) * + CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width); + if (sp->RasterBits == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->RasterBits, CopyFrom->RasterBits, + sizeof(GifPixelType) * CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width); + + /* finally, the extension blocks */ + if (sp->ExtensionBlocks) { + sp->ExtensionBlocks = (ExtensionBlock *)malloc( + sizeof(ExtensionBlock) * + CopyFrom->ExtensionBlockCount); + if (sp->ExtensionBlocks == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks, + sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount); + + /* + * For the moment, the actual blocks can take their + * chances with free(). We'll fix this later. + *** FIXME: [Better check this out... Toshio] + * 2004 May 27: Looks like this was an ESR note. + * It means the blocks are shallow copied from InFile to + * OutFile. However, I don't see that in this code.... + * Did ESR fix it but never remove this note (And other notes + * in gifspnge?) + */ + } + } + + return (sp); + } +} + +void +FreeSavedImages(GifFileType * GifFile) { + + SavedImage *sp; + + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { + return; + } + for (sp = GifFile->SavedImages; + sp < GifFile->SavedImages + GifFile->ImageCount; sp++) { + if (sp->ImageDesc.ColorMap) { + FreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + + if (sp->RasterBits) + free((char *)sp->RasterBits); + + if (sp->ExtensionBlocks) + FreeExtension(sp); + } + free((char *)GifFile->SavedImages); + GifFile->SavedImages=NULL; +} diff --git a/bazaar/plugin/gdal/frmts/gif/giflib/makefile.vc b/bazaar/plugin/gdal/frmts/gif/giflib/makefile.vc new file mode 100644 index 000000000..0f36be0aa --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/giflib/makefile.vc @@ -0,0 +1,16 @@ + +OBJ = \ + dgif_lib.obj egif_lib.obj gif_err.obj gifalloc.obj gif_hash.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = $(SOFTWARNFLAGS) + +default: $(OBJ) + xcopy /D /Y *.obj ..\..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/gif/makefile.vc b/bazaar/plugin/gdal/frmts/gif/makefile.vc new file mode 100644 index 000000000..7ed85c834 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gif/makefile.vc @@ -0,0 +1,23 @@ + +OBJ = \ + gifdataset.obj biggifdataset.obj gifabstractdataset.obj + +GDAL_ROOT = ..\.. + +EXTRAFLAGS = -Igiflib + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + cd giflib + $(MAKE) /f makefile.vc + cd .. + +clean: + -del *.obj + cd giflib + $(MAKE) /f makefile.vc clean + cd .. + + diff --git a/bazaar/plugin/gdal/frmts/grass/GNUmakefile b/bazaar/plugin/gdal/frmts/grass/GNUmakefile new file mode 100644 index 000000000..4863db42b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grass/GNUmakefile @@ -0,0 +1,31 @@ + + +include ../../GDALmake.opt + +#OBJ = grassdataset.o + +ifeq ($(GRASS_SETTING),libgrass) +OBJ = grassdataset.o +else +OBJ = grass57dataset.o +endif + +CPPFLAGS := -DGRASS_GISBASE=\"$(GRASS_GISBASE)\" $(GRASS_INCLUDE) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) $(OBJ) + rm -f *~ + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +dist: + cp -r pkg gdal-grass-$(GDAL_VER) + rm -rf gdal-grass-$(GDAL_VER)/.svn + cp grass57dataset.cpp gdal-grass-$(GDAL_VER) + cp ../../ogr/ogrsf_frmts/grass/*.cpp gdal-grass-$(GDAL_VER) + cp ../../ogr/ogrsf_frmts/grass/*.h gdal-grass-$(GDAL_VER) + tar czvf gdal-grass-$(GDAL_VER).tar.gz ./gdal-grass-$(GDAL_VER) + rm -rf gdal-grass-$(GDAL_VER) + diff --git a/bazaar/plugin/gdal/frmts/grass/frmt_grass.html b/bazaar/plugin/gdal/frmts/grass/frmt_grass.html new file mode 100644 index 000000000..6cffa8a48 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grass/frmt_grass.html @@ -0,0 +1,91 @@ + + +GRASS Raster Format + + + + +

GRASS Raster Format

+ +GDAL optionally supports reading of existing GRASS raster maps or +imagery groups, but not writing or export. The support for GRASS +raster format is determined when the library is configured, and +requires libgrass to be pre-installed (see Notes below). + +

+GRASS raster maps/imagery groups can be selected in several ways. + +

    +
  1. The full path to the cellhd file can be specified. This +is not a relative path, or at least it must contain all the path +components within the GRASS database including the database root +itself. The following example opens the raster map "proj_tm" +within the GRASS mapset "PERMANENT" of the GRASS location +"proj_tm" in the GRASS database located at +/u/data/grassdb. + +

    +For example: + +

    +gdalinfo /u/data/grassdb/proj_tm/PERMANENT/cellhd/proj_tm
    +
    + +
  2. The full path to the directory containing information about an +imagery group (or the REF file within it) can be specified to refer to +the whole group as a single dataset. The following examples do the +same thing. + +

    +For example: + +

    +gdalinfo /usr2/data/grassdb/imagery/raw/group/testmff/REF
    +gdalinfo /usr2/data/grassdb/imagery/raw/group/testmff
    +
    + +
  3. If there is a correct .grassrc5 (GRASS +5), .grassrc6 (GRASS 6) .grass7/rc (GRASS 7) setup +file in the users home directory then raster maps or imagery groups +may be opened just by the cell name. This only works for raster maps +or imagery groups in the current GRASS location and mapset as defined +in the GRASS setup file. +
+ +

+The following features are supported by the GDAL/GRASS link. + +

    +
  • Up to 256 entries from raster colormaps are read (0-255). +
  • Compressed and uncompressed integer (CELL), floating point +(FCELL) and double precision (DCELL) raster maps are all +supported. Integer raster maps are classified with a band type of +"Byte" if the 1-byte per pixel format is used, or +"UInt16" if the two byte per pixel format is used. Otherwise +integer raster maps are treated as "UInt32". +
  • Georeferencing information is properly read from GRASS format. +
  • An attempt is made to translate coordinate systems, but some +conversions may be flawed, in particular in handling of datums and +units. +
+ +

Notes on driver variations

+ +For GRASS 5.7 Radim Blazek has moved the driver to using the GRASS +shared libraries directly instead of using libgrass. Currently (GDAL +1.2.2 and later) both version of the driver are available and can be +configured using "--with-libgrass" for the libgrass variant +or "--with-grass=<dir>" for the new GRASS 5.7+ library +based version. The GRASS 5.7+ driver version is currently not +supporting coordinate system access, though it is hoped that will be +corrected at some point. + +

See Also

+ + + + + diff --git a/bazaar/plugin/gdal/frmts/grass/grass57dataset.cpp b/bazaar/plugin/gdal/frmts/grass/grass57dataset.cpp new file mode 100644 index 000000000..0b97887c5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grass/grass57dataset.cpp @@ -0,0 +1,1061 @@ +/****************************************************************************** + * $Id: grass57dataset.cpp 28534 2015-02-21 14:34:39Z rouault $ + * + * Project: GRASS Driver + * Purpose: Implement GRASS raster read/write support + * This version is for GRASS 5.7+ and uses GRASS libraries + * directly instead of using libgrass. + * Author: Frank Warmerdam + * Radim Blazek + * + ****************************************************************************** + * Copyright (c) 2000 Frank Warmerdam + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include + +extern "C" { +#ifdef __cplusplus +#define class _class +#endif +#include +#ifdef __cplusplus +#undef class +#endif + +#include +#include +#include + +#if GRASS_VERSION_MAJOR >= 7 +char *GPJ_grass_to_wkt(const struct Key_Value *, + const struct Key_Value *, + int, int); +#else +char *GPJ_grass_to_wkt(struct Key_Value *, + struct Key_Value *, + int, int); +#endif +} + +#include "gdal_priv.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +#define GRASS_MAX_COLORS 100000 // what is the right value + +CPL_CVSID("$Id: grass57dataset.cpp 28534 2015-02-21 14:34:39Z rouault $"); + +CPL_C_START +void GDALRegister_GRASS(void); +CPL_C_END + +#if GRASS_VERSION_MAJOR >= 7 +#define G_get_cellhd Rast_get_cellhd +#define G_raster_map_type Rast_map_type +#define G_read_fp_range Rast_read_fp_range +#define G_get_fp_range_min_max Rast_get_fp_range_min_max +#define G_set_c_null_value Rast_set_c_null_value +#define G_set_f_null_value Rast_set_f_null_value +#define G_set_d_null_value Rast_set_d_null_value +#define G_open_cell_old Rast_open_old +#define G_copy memcpy +#define G_read_colors Rast_read_colors +#define G_get_color_range Rast_get_c_color_range +#define G_colors_count Rast_colors_count +#define G_get_f_color_rule Rast_get_fp_color_rule +#define G_free_colors Rast_free_colors +#define G_close_cell Rast_close +#define G_allocate_c_raster_buf Rast_allocate_c_buf +#define G_get_c_raster_row Rast_get_c_row +#define G_is_c_null_value Rast_is_c_null_value +#define G_get_f_raster_row Rast_get_f_row +#define G_get_d_raster_row Rast_get_d_row +#define G_allocate_f_raster_buf Rast_allocate_f_buf +#define G_allocate_d_raster_buf Rast_allocate_d_buf +#define G__setenv G_setenv_nogisrc +#endif + +/************************************************************************/ +/* Grass2CPLErrorHook() */ +/************************************************************************/ + +int Grass2CPLErrorHook( char * pszMessage, int bFatal ) + +{ + if( !bFatal ) + //CPLDebug( "GRASS", "%s", pszMessage ); + CPLError( CE_Warning, CPLE_AppDefined, "GRASS warning: %s", pszMessage ); + else + CPLError( CE_Warning, CPLE_AppDefined, "GRASS fatal error: %s", pszMessage ); + + return 0; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GRASSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GRASSRasterBand; + +class GRASSDataset : public GDALDataset +{ + friend class GRASSRasterBand; + + char *pszGisdbase; + char *pszLocation; /* LOCATION_NAME */ + char *pszElement; /* cellhd or group */ + + struct Cell_head sCellInfo; /* raster region */ + + char *pszProjection; + + double adfGeoTransform[6]; + + public: + GRASSDataset(); + ~GRASSDataset(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * ); + + static GDALDataset *Open( GDALOpenInfo * ); + + private: + static bool SplitPath ( char *, char **, char **, char **, char **, char ** ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GRASSRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GRASSRasterBand : public GDALRasterBand +{ + friend class GRASSDataset; + + char *pszCellName; + char *pszMapset; + int hCell; + int nGRSType; // GRASS raster type: CELL_TYPE, FCELL_TYPE, DCELL_TYPE + bool nativeNulls; // use GRASS native NULL values + + struct Colors sGrassColors; + GDALColorTable *poCT; + + struct Cell_head sOpenWindow; /* the region when the raster was opened */ + + int bHaveMinMax; + double dfCellMin; + double dfCellMax; + + double dfNoData; + + bool valid; + + public: + + GRASSRasterBand( GRASSDataset *, int, + const char *, const char * ); + virtual ~GRASSRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IRasterIO ( GDALRWFlag, int, int, int, int, void *, int, int, GDALDataType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum( int *pbSuccess = NULL ); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + + private: + CPLErr ResetReading( struct Cell_head * ); + +}; + + +/************************************************************************/ +/* GRASSRasterBand() */ +/************************************************************************/ + +GRASSRasterBand::GRASSRasterBand( GRASSDataset *poDS, int nBand, + const char * pszMapset, + const char * pszCellName ) + +{ + struct Cell_head sCellInfo; + + // Note: GISDBASE, LOCATION_NAME ans MAPSET was set in GRASSDataset::Open + + this->poDS = poDS; + this->nBand = nBand; + this->valid = false; + + this->pszCellName = G_store ( (char *) pszCellName ); + this->pszMapset = G_store ( (char *) pszMapset ); + + G_get_cellhd( (char *) pszCellName, (char *) pszMapset, &sCellInfo ); + nGRSType = G_raster_map_type( (char *) pszCellName, (char *) pszMapset ); + +/* -------------------------------------------------------------------- */ +/* Get min/max values. */ +/* -------------------------------------------------------------------- */ + struct FPRange sRange; + + if( G_read_fp_range( (char *) pszCellName, (char *) pszMapset, + &sRange ) == -1 ) + { + bHaveMinMax = FALSE; + } + else + { + bHaveMinMax = TRUE; + G_get_fp_range_min_max( &sRange, &dfCellMin, &dfCellMax ); + } + +/* -------------------------------------------------------------------- */ +/* Setup band type, and preferred nodata value. */ +/* -------------------------------------------------------------------- */ + // Negative values are also (?) stored as 4 bytes (format = 3) + // => raster with format < 3 has only positive values + + // GRASS modules usually do not waste space and only the format necessary to keep + // full raster values range is used -> no checks if shorter type could be used + + if( nGRSType == CELL_TYPE ) { + if ( sCellInfo.format == 0 ) { // 1 byte / cell -> possible range 0,255 + if ( bHaveMinMax && dfCellMin > 0 ) { + this->eDataType = GDT_Byte; + dfNoData = 0.0; + } else if ( bHaveMinMax && dfCellMax < 255 ) { + this->eDataType = GDT_Byte; + dfNoData = 255.0; + } else { // maximum is not known or full range is used + this->eDataType = GDT_UInt16; + dfNoData = 256.0; + } + nativeNulls = false; + } else if ( sCellInfo.format == 1 ) { // 2 bytes / cell -> possible range 0,65535 + if ( bHaveMinMax && dfCellMin > 0 ) { + this->eDataType = GDT_UInt16; + dfNoData = 0.0; + } else if ( bHaveMinMax && dfCellMax < 65535 ) { + this->eDataType = GDT_UInt16; + dfNoData = 65535; + } else { // maximum is not known or full range is used + CELL cval; + this->eDataType = GDT_Int32; + G_set_c_null_value ( &cval, 1); + dfNoData = (double) cval; + nativeNulls = true; + } + nativeNulls = false; + } else { // 3-4 bytes + CELL cval; + this->eDataType = GDT_Int32; + G_set_c_null_value ( &cval, 1); + dfNoData = (double) cval; + nativeNulls = true; + } + } + else if( nGRSType == FCELL_TYPE ) { + FCELL fval; + this->eDataType = GDT_Float32; + G_set_f_null_value ( &fval, 1); + dfNoData = (double) fval; + nativeNulls = true; + } + else if( nGRSType == DCELL_TYPE ) + { + DCELL dval; + this->eDataType = GDT_Float64; + G_set_d_null_value ( &dval, 1); + dfNoData = (double) dval; + nativeNulls = true; + } + + nBlockXSize = poDS->nRasterXSize;; + nBlockYSize = 1; + + G_set_window( &(((GRASSDataset *)poDS)->sCellInfo) ); + if ( (hCell = G_open_cell_old((char *) pszCellName, (char *) pszMapset)) < 0 ) { + CPLError( CE_Warning, CPLE_AppDefined, "GRASS: Cannot open raster '%s'", pszCellName ); + return; + } + G_copy((void *) &sOpenWindow, (void *) &(((GRASSDataset *)poDS)->sCellInfo), sizeof(struct Cell_head)); + +/* -------------------------------------------------------------------- */ +/* Do we have a color table? */ +/* -------------------------------------------------------------------- */ + poCT = NULL; + if( G_read_colors( (char *) pszCellName, (char *) pszMapset, &sGrassColors ) == 1 ) + { + int maxcolor; + CELL min, max; + + G_get_color_range ( &min, &max, &sGrassColors); + + if ( bHaveMinMax ) { + if ( max < dfCellMax ) { + maxcolor = max; + } else { + maxcolor = (int) ceil ( dfCellMax ); + } + if ( maxcolor > GRASS_MAX_COLORS ) { + maxcolor = GRASS_MAX_COLORS; + CPLDebug( "GRASS", "Too many values, color table cut to %d entries.", maxcolor ); + } + } else { + if ( max < GRASS_MAX_COLORS ) { + maxcolor = max; + } else { + maxcolor = GRASS_MAX_COLORS; + CPLDebug( "GRASS", "Too many values, color table set to %d entries.", maxcolor ); + } + } + + poCT = new GDALColorTable(); + for( int iColor = 0; iColor <= maxcolor; iColor++ ) + { + int nRed, nGreen, nBlue; + GDALColorEntry sColor; + +#if GRASS_VERSION_MAJOR >= 7 + if( Rast_get_c_color( &iColor, &nRed, &nGreen, &nBlue, &sGrassColors ) ) +#else + if( G_get_color( iColor, &nRed, &nGreen, &nBlue, &sGrassColors ) ) +#endif + { + sColor.c1 = nRed; + sColor.c2 = nGreen; + sColor.c3 = nBlue; + sColor.c4 = 255; + + poCT->SetColorEntry( iColor, &sColor ); + } + else + { + sColor.c1 = 0; + sColor.c2 = 0; + sColor.c3 = 0; + sColor.c4 = 0; + + poCT->SetColorEntry( iColor, &sColor ); + } + } + + /* Create metadata enries for color table rules */ + char key[200], value[200]; + int rcount = G_colors_count ( &sGrassColors ); + + sprintf ( value, "%d", rcount ); + this->SetMetadataItem( "COLOR_TABLE_RULES_COUNT", value ); + + /* Add the rules in reverse order */ + for ( int i = rcount-1; i >= 0; i-- ) { + DCELL val1, val2; + unsigned char r1, g1, b1, r2, g2, b2; + + G_get_f_color_rule ( &val1, &r1, &g1, &b1, &val2, &r2, &g2, &b2, &sGrassColors, i ); + + + sprintf ( key, "COLOR_TABLE_RULE_RGB_%d", rcount-i-1 ); + sprintf ( value, "%e %e %d %d %d %d %d %d", val1, val2, r1, g1, b1, r2, g2, b2 ); + this->SetMetadataItem( key, value ); + } + } else { + this->SetMetadataItem( "COLOR_TABLE_RULES_COUNT", "0" ); + } + + this->valid = true; +} + +/************************************************************************/ +/* ~GRASSRasterBand() */ +/************************************************************************/ + +GRASSRasterBand::~GRASSRasterBand() +{ + if( poCT != NULL ) { + G_free_colors( &sGrassColors ); + delete poCT; + } + + if( hCell >= 0 ) + G_close_cell( hCell ); + + if ( pszCellName ) + G_free ( pszCellName ); + + if ( pszMapset ) + G_free ( pszMapset ); +} + +/************************************************************************/ +/* ResetReading */ +/* */ +/* Reset current window and reopen cell if the window has changed, */ +/* reset GRASS variables */ +/* */ +/* Returns CE_Failure if fails, otherwise CE_None */ +/************************************************************************/ +CPLErr GRASSRasterBand::ResetReading ( struct Cell_head *sNewWindow ) +{ + + /* Check if the window has changed */ + if ( sNewWindow->north != sOpenWindow.north || sNewWindow->south != sOpenWindow.south || + sNewWindow->east != sOpenWindow.east || sNewWindow->west != sOpenWindow.west || + sNewWindow->ew_res != sOpenWindow.ew_res || sNewWindow->ns_res != sOpenWindow.ns_res || + sNewWindow->rows != sOpenWindow.rows || sNewWindow->cols != sOpenWindow.cols ) + { + if( hCell >= 0 ) { + G_close_cell( hCell ); + hCell = -1; + } + + /* Set window */ + G_set_window( sNewWindow ); + + /* Open raster */ + G__setenv( "GISDBASE", ((GRASSDataset *)poDS)->pszGisdbase ); + G__setenv( "LOCATION_NAME", ((GRASSDataset *)poDS)->pszLocation ); + G__setenv( "MAPSET", pszMapset); + G_reset_mapsets(); + G_add_mapset_to_search_path ( pszMapset ); + + if ( (hCell = G_open_cell_old( pszCellName, pszMapset)) < 0 ) { + CPLError( CE_Warning, CPLE_AppDefined, "GRASS: Cannot open raster '%s'", pszCellName ); + this->valid = false; + return CE_Failure; + } + + G_copy((void *) &sOpenWindow, (void *) sNewWindow, sizeof(struct Cell_head)); + + } + else + { + /* The windows are identical, check current window */ + struct Cell_head sCurrentWindow; + + G_get_window ( &sCurrentWindow ); + + if ( sNewWindow->north != sCurrentWindow.north || sNewWindow->south != sCurrentWindow.south || + sNewWindow->east != sCurrentWindow.east || sNewWindow->west != sCurrentWindow.west || + sNewWindow->ew_res != sCurrentWindow.ew_res || sNewWindow->ns_res != sCurrentWindow.ns_res || + sNewWindow->rows != sCurrentWindow.rows || sNewWindow->cols != sCurrentWindow.cols + ) + { + /* Reset window */ + G_set_window( sNewWindow ); + } + } + + + return CE_None; +} + +/************************************************************************/ +/* IReadBlock() */ +/* */ +/************************************************************************/ + +CPLErr GRASSRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, void * pImage ) + +{ + if ( ! this->valid ) return CE_Failure; + + // Reset window because IRasterIO could be previosly called + if ( ResetReading ( &(((GRASSDataset *)poDS)->sCellInfo) ) != CE_None ) { + return CE_Failure; + } + + if ( eDataType == GDT_Byte || eDataType == GDT_UInt16 ) { + CELL *cbuf; + + cbuf = G_allocate_c_raster_buf(); + G_get_c_raster_row ( hCell, cbuf, nBlockYOff ); + + /* Reset NULLs */ + for ( int col = 0; col < nBlockXSize; col++ ) { + if ( G_is_c_null_value(&(cbuf[col])) ) + cbuf[col] = (CELL) dfNoData; + } + + GDALCopyWords ( (void *) cbuf, GDT_Int32, sizeof(CELL), + pImage, eDataType, GDALGetDataTypeSize(eDataType)/8, + nBlockXSize ); + + G_free ( cbuf ); + + } else if ( eDataType == GDT_Int32 ) { + G_get_c_raster_row ( hCell, (CELL *) pImage, nBlockYOff ); + } else if ( eDataType == GDT_Float32 ) { + G_get_f_raster_row ( hCell, (FCELL *) pImage, nBlockYOff ); + } else if ( eDataType == GDT_Float64 ) { + G_get_d_raster_row ( hCell, (DCELL *) pImage, nBlockYOff ); + } + + return CE_None; +} + +/************************************************************************/ +/* IRasterIO() */ +/* */ +/************************************************************************/ + +CPLErr GRASSRasterBand::IRasterIO ( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ + /* GRASS library does that, we have only calculate and reset the region in map units + * and if the region has changed, reopen the raster */ + + /* Calculate the region */ + struct Cell_head sWindow; + struct Cell_head *psDsWindow; + + if ( ! this->valid ) return CE_Failure; + + psDsWindow = &(((GRASSDataset *)poDS)->sCellInfo); + + sWindow.north = psDsWindow->north - nYOff * psDsWindow->ns_res; + sWindow.south = sWindow.north - nYSize * psDsWindow->ns_res; + sWindow.west = psDsWindow->west + nXOff * psDsWindow->ew_res; + sWindow.east = sWindow.west + nXSize * psDsWindow->ew_res; + sWindow.proj = psDsWindow->proj; + sWindow.zone = psDsWindow->zone; + + sWindow.cols = nBufXSize; + sWindow.rows = nBufYSize; + + /* Reset resolution */ + G_adjust_Cell_head ( &sWindow, 1, 1); + + if ( ResetReading ( &sWindow ) != CE_None ) + { + return CE_Failure; + } + + /* Read Data */ + CELL *cbuf = NULL; + FCELL *fbuf = NULL; + DCELL *dbuf = NULL; + bool direct = false; + + /* Reset space if default (0) */ + if ( nPixelSpace == 0 ) + nPixelSpace = GDALGetDataTypeSize ( eBufType ) / 8; + + if ( nLineSpace == 0 ) + nLineSpace = nBufXSize * nPixelSpace; + + if ( nGRSType == CELL_TYPE && ( !nativeNulls || eBufType != GDT_Int32 || sizeof(CELL) != 4 || + nPixelSpace != sizeof(CELL) ) ) + { + cbuf = G_allocate_c_raster_buf(); + } else if( nGRSType == FCELL_TYPE && ( eBufType != GDT_Float32 || nPixelSpace != sizeof(FCELL) ) ) { + fbuf = G_allocate_f_raster_buf(); + } else if( nGRSType == DCELL_TYPE && ( eBufType != GDT_Float64 || nPixelSpace != sizeof(DCELL) ) ) { + dbuf = G_allocate_d_raster_buf(); + } else { + direct = true; + } + + for ( int row = 0; row < nBufYSize; row++ ) { + char *pnt = (char *)pData + row * nLineSpace; + + if ( nGRSType == CELL_TYPE ) { + if ( direct ) { + G_get_c_raster_row ( hCell, (CELL *) pnt, row ); + } else { + G_get_c_raster_row ( hCell, cbuf, row ); + + /* Reset NULLs */ + for ( int col = 0; col < nBufXSize; col++ ) { + if ( G_is_c_null_value(&(cbuf[col])) ) + cbuf[col] = (CELL) dfNoData; + } + + GDALCopyWords ( (void *) cbuf, GDT_Int32, sizeof(CELL), + (void *) pnt, eBufType, nPixelSpace, + nBufXSize ); + } + } else if( nGRSType == FCELL_TYPE ) { + if ( direct ) { + G_get_f_raster_row ( hCell, (FCELL *) pnt, row ); + } else { + G_get_f_raster_row ( hCell, fbuf, row ); + + GDALCopyWords ( (void *) fbuf, GDT_Float32, sizeof(FCELL), + (void *) pnt, eBufType, nPixelSpace, + nBufXSize ); + } + } else if( nGRSType == DCELL_TYPE ) { + if ( direct ) { + G_get_d_raster_row ( hCell, (DCELL *) pnt, row ); + } else { + G_get_d_raster_row ( hCell, dbuf, row ); + + GDALCopyWords ( (void *) dbuf, GDT_Float64, sizeof(DCELL), + (void *) pnt, eBufType, nPixelSpace, + nBufXSize ); + } + } + } + + if ( cbuf ) G_free ( cbuf ); + if ( fbuf ) G_free ( fbuf ); + if ( dbuf ) G_free ( dbuf ); + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp GRASSRasterBand::GetColorInterpretation() + +{ + if( poCT != NULL ) + return GCI_PaletteIndex; + else + return GCI_GrayIndex; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *GRASSRasterBand::GetColorTable() + +{ + return poCT; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double GRASSRasterBand::GetMinimum( int *pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bHaveMinMax; + + if( bHaveMinMax ) + return dfCellMin; + + else if( eDataType == GDT_Float32 || eDataType == GDT_Float64 ) + return -4294967295.0; + else + return 0; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double GRASSRasterBand::GetMaximum( int *pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bHaveMinMax; + + if( bHaveMinMax ) + return dfCellMax; + + else if( eDataType == GDT_Float32 || eDataType == GDT_Float64 ) + return 4294967295.0; + else if( eDataType == GDT_UInt32 ) + return 4294967295.0; + else if( eDataType == GDT_UInt16 ) + return 65535; + else + return 255; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double GRASSRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + return dfNoData; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GRASSDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* GRASSDataset() */ +/************************************************************************/ + +GRASSDataset::GRASSDataset() +{ + pszProjection = NULL; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~GRASSDataset() */ +/************************************************************************/ + +GRASSDataset::~GRASSDataset() +{ + + if ( pszGisdbase ) + G_free ( pszGisdbase ); + + if ( pszLocation ) + G_free ( pszLocation ); + + if ( pszElement ) + G_free ( pszElement ); + + G_free( pszProjection ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GRASSDataset::GetProjectionRef() +{ + if( pszProjection == NULL ) + return ""; + else + return pszProjection; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GRASSDataset::GetGeoTransform( double * padfGeoTransform ) +{ + memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 ); + + return CE_None; +} + +/************************************************************************/ +/* SplitPath() */ +/* Split full path to cell or group to: */ +/* gisdbase, location, mapset, element, name */ +/* New string are allocated and should be freed when no longer needed. */ +/* */ +/* Returns: true - OK */ +/* false - failed */ +/************************************************************************/ +bool GRASSDataset::SplitPath( char *path, char **gisdbase, char **location, + char **mapset, char **element, char **name ) +{ + char *p, *ptr[5], *tmp; + int i = 0; + + *gisdbase = *location = *mapset = *element = *name = NULL; + + if ( !path || strlen(path) == 0 ) + return false; + + tmp = G_store ( path ); + + while ( (p = strrchr(tmp,'/')) != NULL && i < 4 ) { + *p = '\0'; + + if ( strlen(p+1) == 0 ) /* repeated '/' */ + continue; + + ptr[i++] = p+1; + } + + /* Note: empty GISDBASE == 0 is not accepted (relative path) */ + if ( i != 4 ) { + G_free ( tmp ); + return false; + } + + *gisdbase = G_store ( tmp ); + *location = G_store ( ptr[3] ); + *mapset = G_store ( ptr[2] ); + *element = G_store ( ptr[1] ); + *name = G_store ( ptr[0] ); + + G_free ( tmp ); + return true; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +#if (GRASS_VERSION_MAJOR >= 6 && GRASS_VERSION_MINOR >= 3) || GRASS_VERSION_MAJOR >= 7 +typedef int (*GrassErrorHandler)(const char *, int); +#else +typedef int (*GrassErrorHandler)(char *, int); +#endif + +GDALDataset *GRASSDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + char *pszGisdb = NULL, *pszLoc = NULL; + char *pszMapset = NULL, *pszElem = NULL, *pszName = NULL; + char **papszCells = NULL; + char **papszMapsets = NULL; + +/* -------------------------------------------------------------------- */ +/* Does this even look like a grass file path? */ +/* -------------------------------------------------------------------- */ + if( strstr(poOpenInfo->pszFilename,"/cellhd/") == NULL + && strstr(poOpenInfo->pszFilename,"/group/") == NULL ) + return NULL; + + /* Always init, if no rasters are opened G_no_gisinit resets the projection and + * rasters in different projection may be then opened */ + + // Don't use GISRC file and read/write GRASS variables (from location G_VAR_GISRC) to memory only. + G_set_gisrc_mode ( G_GISRC_MODE_MEMORY ); + + // Init GRASS libraries (required) + G_no_gisinit(); // Doesn't check write permissions for mapset compare to G_gisinit + + // Set error function + G_set_error_routine ( (GrassErrorHandler) Grass2CPLErrorHook ); + + + // GISBASE is path to the directory where GRASS is installed, + if ( !getenv( "GISBASE" ) ) { + static char* gisbaseEnv = NULL; + const char *gisbase = GRASS_GISBASE; + CPLError( CE_Warning, CPLE_AppDefined, "GRASS warning: GISBASE " + "environment variable was not set, using:\n%s", gisbase ); + char buf[2000]; + snprintf ( buf, sizeof(buf), "GISBASE=%s", gisbase ); + buf[sizeof(buf)-1] = '\0'; + + CPLFree(gisbaseEnv); + gisbaseEnv = CPLStrdup ( buf ); + putenv( gisbaseEnv ); + } + + if ( !SplitPath( poOpenInfo->pszFilename, &pszGisdb, &pszLoc, &pszMapset, + &pszElem, &pszName) ) { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Check element name */ +/* -------------------------------------------------------------------- */ + if ( strcmp(pszElem,"cellhd") != 0 && strcmp(pszElem,"group") != 0 ) { + G_free(pszGisdb); + G_free(pszLoc); + G_free(pszMapset); + G_free(pszElem); + G_free(pszName); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Set GRASS variables */ +/* -------------------------------------------------------------------- */ + + G__setenv( "GISDBASE", pszGisdb ); + G__setenv( "LOCATION_NAME", pszLoc ); + G__setenv( "MAPSET", pszMapset); // group is searched only in current mapset + G_reset_mapsets(); + G_add_mapset_to_search_path ( pszMapset ); + +/* -------------------------------------------------------------------- */ +/* Check if this is a valid grass cell. */ +/* -------------------------------------------------------------------- */ + if ( strcmp(pszElem,"cellhd") == 0 ) { + + if ( G_find_file2("cell", pszName, pszMapset) == NULL ) { + G_free(pszGisdb); G_free(pszLoc); G_free(pszMapset); G_free(pszElem); G_free(pszName); + return NULL; + } + + papszMapsets = CSLAddString( papszMapsets, pszMapset ); + papszCells = CSLAddString( papszCells, pszName ); + } +/* -------------------------------------------------------------------- */ +/* Check if this is a valid GRASS imagery group. */ +/* -------------------------------------------------------------------- */ + else { + struct Ref ref; + + I_init_group_ref( &ref ); + if ( I_get_group_ref( pszName, &ref ) == 0 ) { + G_free(pszGisdb); G_free(pszLoc); G_free(pszMapset); G_free(pszElem); G_free(pszName); + return NULL; + } + + for( int iRef = 0; iRef < ref.nfiles; iRef++ ) + { + papszCells = CSLAddString( papszCells, ref.file[iRef].name ); + papszMapsets = CSLAddString( papszMapsets, ref.file[iRef].mapset ); + G_add_mapset_to_search_path ( ref.file[iRef].mapset ); + } + + I_free_group_ref( &ref ); + } + + G_free( pszMapset ); + G_free( pszName ); + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GRASSDataset *poDS; + + poDS = new GRASSDataset(); + + /* notdef: should only allow read access to an existing cell, right? */ + poDS->eAccess = poOpenInfo->eAccess; + + poDS->pszGisdbase = pszGisdb; + poDS->pszLocation = pszLoc; + poDS->pszElement = pszElem; + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + +#if GRASS_VERSION_MAJOR >= 7 + Rast_get_cellhd( papszCells[0], papszMapsets[0], &(poDS->sCellInfo) ); +#else + if( G_get_cellhd( papszCells[0], papszMapsets[0], &(poDS->sCellInfo) ) != 0 ) { + CPLError( CE_Warning, CPLE_AppDefined, "GRASS: Cannot open raster header"); + delete poDS; + return NULL; + } +#endif + + poDS->nRasterXSize = poDS->sCellInfo.cols; + poDS->nRasterYSize = poDS->sCellInfo.rows; + + poDS->adfGeoTransform[0] = poDS->sCellInfo.west; + poDS->adfGeoTransform[1] = poDS->sCellInfo.ew_res; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = poDS->sCellInfo.north; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -1 * poDS->sCellInfo.ns_res; + +/* -------------------------------------------------------------------- */ +/* Try to get a projection definition. */ +/* -------------------------------------------------------------------- */ + struct Key_Value *projinfo, *projunits; + + projinfo = G_get_projinfo(); + projunits = G_get_projunits(); + poDS->pszProjection = GPJ_grass_to_wkt ( projinfo, projunits, 0, 0); + if (projinfo) G_free_key_value(projinfo); + if (projunits) G_free_key_value(projunits); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; papszCells[iBand] != NULL; iBand++ ) + { + GRASSRasterBand *rb = new GRASSRasterBand( poDS, iBand+1, papszMapsets[iBand], + papszCells[iBand] ); + + if ( !rb->valid ) { + CPLError( CE_Warning, CPLE_AppDefined, "GRASS: Cannot open raster band %d", iBand); + delete rb; + delete poDS; + return NULL; + } + + poDS->SetBand( iBand+1, rb ); + } + + CSLDestroy(papszCells); + CSLDestroy(papszMapsets); + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + delete poDS; + CPLError( CE_Failure, CPLE_NotSupported, + "The GRASS driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_GRASS() */ +/************************************************************************/ + +void GDALRegister_GRASS() +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("GDAL/GRASS57 driver")) + return; + + if( GDALGetDriverByName( "GRASS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GRASS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GRASS Rasters (5.7+)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_grass.html" ); + + poDriver->pfnOpen = GRASSDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/grass/grassdataset.cpp b/bazaar/plugin/gdal/frmts/grass/grassdataset.cpp new file mode 100644 index 000000000..f413298f7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grass/grassdataset.cpp @@ -0,0 +1,610 @@ +/****************************************************************************** + * $Id: grassdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: GRASS Driver + * Purpose: Implement GRASS raster read/write support + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2007-2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include + +#include "gdal_priv.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: grassdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +CPL_C_START +void GDALRegister_GRASS(void); +CPL_C_END + +/************************************************************************/ +/* Grass2CPLErrorHook() */ +/************************************************************************/ + +int Grass2CPLErrorHook( char * pszMessage, int bFatal ) + +{ + if( !bFatal ) + CPLDebug( "libgrass", "%s", pszMessage ); + else + CPLError( CE_Fatal, CPLE_AppDefined, "libgrass: %s", pszMessage ); + + return 0; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GRASSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GRASSRasterBand; + +class GRASSDataset : public GDALDataset +{ + friend class GRASSRasterBand; + + char *pszProjection; + + double adfGeoTransform[6]; + + public: + GRASSDataset(); + ~GRASSDataset(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * ); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GRASSRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GRASSRasterBand : public GDALRasterBand +{ + friend class GRASSDataset; + + int hCell; + int nGRSType; + + GDALColorTable *poCT; + + int bHaveMinMax; + double dfCellMin; + double dfCellMax; + + double dfNoData; + + public: + + GRASSRasterBand( GRASSDataset *, int, + const char *, const char * ); + virtual ~GRASSRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum( int *pbSuccess = NULL ); + virtual double GetNoDataValue( int *pbSuccess = NULL ); +}; + + +/************************************************************************/ +/* GRASSRasterBand() */ +/************************************************************************/ + +GRASSRasterBand::GRASSRasterBand( GRASSDataset *poDS, int nBand, + const char * pszMapset, + const char * pszCellName ) + +{ + struct Cell_head sCellInfo; + + this->poDS = poDS; + this->nBand = nBand; + + G_get_cellhd( (char *) pszCellName, (char *) pszMapset, &sCellInfo ); + nGRSType = G_raster_map_type( (char *) pszCellName, (char *) pszMapset ); + +/* -------------------------------------------------------------------- */ +/* Get min/max values. */ +/* -------------------------------------------------------------------- */ + struct FPRange sRange; + + if( G_read_fp_range( (char *) pszCellName, (char *) pszMapset, + &sRange ) == -1 ) + { + bHaveMinMax = FALSE; + } + else + { + bHaveMinMax = TRUE; + G_get_fp_range_min_max( &sRange, &dfCellMin, &dfCellMax ); + } + +/* -------------------------------------------------------------------- */ +/* Setup band type, and preferred nodata value. */ +/* -------------------------------------------------------------------- */ + dfNoData = 0.0; + if( nGRSType == CELL_TYPE && sCellInfo.format == 0 ) + { + if( bHaveMinMax && dfCellMin < 1.0 && dfCellMax > 254.0 ) + { + this->eDataType = GDT_UInt16; + dfNoData = 256.0; + } + else + { + this->eDataType = GDT_Byte; + if( dfCellMax < 255.0 ) + dfNoData = 255.0; + else + dfNoData = 0.0; + } + } + else if( nGRSType == CELL_TYPE && sCellInfo.format == 1 ) + { + this->eDataType = GDT_UInt16; + dfNoData = 65535.0; + } + else if( nGRSType == CELL_TYPE ) + { + this->eDataType = GDT_UInt32; + dfNoData = 65535.0; + } + else if( nGRSType == FCELL_TYPE ) + { + this->eDataType = GDT_Float32; + dfNoData = -12345.0; + } + else if( nGRSType == DCELL_TYPE ) + { + this->eDataType = GDT_Float64; + dfNoData = -12345.0; + } + + nBlockXSize = poDS->nRasterXSize;; + nBlockYSize = 1; + + hCell = G_open_cell_old((char *) pszCellName, (char *) pszMapset); + +/* -------------------------------------------------------------------- */ +/* Do we have a color table? */ +/* -------------------------------------------------------------------- */ + struct Colors sGrassColors; + + poCT = NULL; + if( G_read_colors( (char *) pszCellName, (char *) pszMapset, + &sGrassColors ) == 1 ) + { + poCT = new GDALColorTable(); + for( int iColor = 0; iColor < 256; iColor++ ) + { + int nRed, nGreen, nBlue; + GDALColorEntry sColor; + + if( G_get_color( iColor, &nRed, &nGreen, &nBlue, &sGrassColors ) ) + { + sColor.c1 = nRed; + sColor.c2 = nGreen; + sColor.c3 = nBlue; + sColor.c4 = 255; + + poCT->SetColorEntry( iColor, &sColor ); + } + else + { + sColor.c1 = 0; + sColor.c2 = 0; + sColor.c3 = 0; + sColor.c4 = 0; + + poCT->SetColorEntry( iColor, &sColor ); + } + } + + G_free_colors( &sGrassColors ); + } +} + +/************************************************************************/ +/* ~GRASSRasterBand() */ +/************************************************************************/ + +GRASSRasterBand::~GRASSRasterBand() + +{ + if( poCT != NULL ) + delete poCT; + + if( hCell >= 0 ) + G_close_cell( hCell ); +} + + +/************************************************************************/ +/* IReadBlock() */ +/* */ +/* We only do "null" testing for floating point values. We */ +/* assume integer values are having the null raster entries set */ +/* to zero which is the "nodata" value for integer layers. */ +/************************************************************************/ + +CPLErr GRASSRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + char *pachNullBuf; + + pachNullBuf = (char *) CPLMalloc(nBlockXSize); + G_get_null_value_row( hCell, pachNullBuf, nBlockYOff ); + + if( eDataType == GDT_Float32 || eDataType == GDT_Float64 + || eDataType == GDT_UInt32 ) + { + G_get_raster_row( hCell, pImage, nBlockYOff, nGRSType ); + + for( int i = 0; i < nBlockXSize; i++ ) + { + if( pachNullBuf[i] != 0 ) + { + if( eDataType == GDT_UInt32 ) + ((GUInt32 *) pImage)[i] = (GUInt32) dfNoData; + else if( eDataType == GDT_Float32 ) + ((float *) pImage)[i] = dfNoData; + else + ((double *) pImage)[i] = dfNoData; + } + } + + } + else + { + GUInt32 *panRow = (GUInt32 *) CPLMalloc(4 * nBlockXSize); + + G_get_raster_row( hCell, panRow, nBlockYOff, nGRSType ); + + for( int i = 0; i < nBlockXSize; i++ ) + { + if( pachNullBuf[i] != 0 ) + panRow[i] = (GUInt32) dfNoData; + } + + GDALCopyWords( panRow, GDT_UInt32, 4, + pImage, eDataType, GDALGetDataTypeSize(eDataType)/8, + nBlockXSize ); + + CPLFree( panRow ); + } + + CPLFree( pachNullBuf ); + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp GRASSRasterBand::GetColorInterpretation() + +{ + if( poCT != NULL ) + return GCI_PaletteIndex; + else + return GCI_GrayIndex; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *GRASSRasterBand::GetColorTable() + +{ + return poCT; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double GRASSRasterBand::GetMinimum( int *pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bHaveMinMax; + + if( bHaveMinMax ) + return dfCellMin; + + else if( eDataType == GDT_Float32 || eDataType == GDT_Float64 ) + return -4294967295.0; + else + return 0; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double GRASSRasterBand::GetMaximum( int *pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bHaveMinMax; + + if( bHaveMinMax ) + return dfCellMax; + + else if( eDataType == GDT_Float32 || eDataType == GDT_Float64 ) + return 4294967295.0; + else if( eDataType == GDT_UInt32 ) + return 4294967295.0; + else if( eDataType == GDT_UInt16 ) + return 65535; + else + return 255; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double GRASSRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + return dfNoData; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GRASSDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* GRASSDataset() */ +/************************************************************************/ + +GRASSDataset::GRASSDataset() + +{ + pszProjection = NULL; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~GRASSDataset() */ +/************************************************************************/ + +GRASSDataset::~GRASSDataset() + +{ + CPLFree( pszProjection ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GRASSDataset::GetProjectionRef() +{ + if( pszProjection == NULL ) + return ""; + else + return pszProjection; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GRASSDataset::GetGeoTransform( double * padfGeoTransform ) +{ + memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 ); + + return CE_None; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +typedef int (*GrassErrorHandler)(); + +GDALDataset *GRASSDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + static int bDoneGISInit = FALSE; + char *pszMapset = NULL, *pszCell = NULL; + char **papszCells = NULL; + char **papszMapsets = NULL; + + if( !bDoneGISInit ) + { + G_set_error_routine( (GrassErrorHandler) Grass2CPLErrorHook ); + G_gisinit_2( "GDAL", NULL, NULL, NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Check if this is a valid grass cell. */ +/* -------------------------------------------------------------------- */ + if( G_check_cell( poOpenInfo->pszFilename, &pszMapset, &pszCell ) ) + { + papszCells = CSLAddString( papszCells, pszCell ); + papszMapsets = CSLAddString( papszMapsets, pszMapset ); + + G_free( pszMapset ); + G_free( pszCell ); + } + +/* -------------------------------------------------------------------- */ +/* Check if this is a valid GRASS imagery group. */ +/* -------------------------------------------------------------------- */ + else if( I_check_group( poOpenInfo->pszFilename, &pszMapset, &pszCell ) ) + { + struct Ref ref; + + I_init_group_ref( &ref ); + I_get_group_ref( pszCell, &ref ); + + for( int iRef = 0; iRef < ref.nfiles; iRef++ ) + { + papszCells = CSLAddString( papszCells, ref.file[iRef].name ); + papszMapsets = CSLAddString( papszMapsets, ref.file[iRef].mapset ); + } + + I_free_group_ref( &ref ); + + G_free( pszMapset ); + G_free( pszCell ); + } + + else + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GRASSDataset *poDS; + + poDS = new GRASSDataset(); + + /* notdef: should only allow read access to an existing cell, right? */ + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + struct Cell_head sCellInfo; + + if( G_get_cellhd( papszCells[0], papszMapsets[0], &sCellInfo ) != 0 ) + { + /* notdef: report failure. */ + return NULL; + } + + poDS->nRasterXSize = sCellInfo.cols; + poDS->nRasterYSize = sCellInfo.rows; + + G_set_window( &sCellInfo ); + + poDS->adfGeoTransform[0] = sCellInfo.west; + poDS->adfGeoTransform[1] = sCellInfo.ew_res; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = sCellInfo.north; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -1 * sCellInfo.ns_res; + +/* -------------------------------------------------------------------- */ +/* Try to get a projection definition. */ +/* -------------------------------------------------------------------- */ + char *pszProj4; + + pszProj4 = G_get_cell_as_proj4( papszCells[0], papszMapsets[0] ); + if( pszProj4 != NULL ) + { + OGRSpatialReference oSRS; + + if( oSRS.importFromProj4( pszProj4 ) == OGRERR_NONE ) + { + oSRS.exportToWkt( &(poDS->pszProjection) ); + } + + G_free( pszProj4 ); + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; papszCells[iBand] != NULL; iBand++ ) + { + poDS->SetBand( iBand+1, + new GRASSRasterBand( poDS, iBand+1, + papszMapsets[iBand], + papszCells[iBand] ) ); + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + delete poDS; + CPLError( CE_Failure, CPLE_NotSupported, + "The GRASS driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_GRASS() */ +/************************************************************************/ + +void GDALRegister_GRASS() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("GDAL/GRASS driver")) + return; + + if( GDALGetDriverByName( "GRASS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GRASS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GRASS Database Rasters" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_grass.html" ); + + poDriver->pfnOpen = GRASSDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/grass/pkg/Makefile.in b/bazaar/plugin/gdal/frmts/grass/pkg/Makefile.in new file mode 100644 index 000000000..007df6d61 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grass/pkg/Makefile.in @@ -0,0 +1,57 @@ +CC = @CC@ +CXX = @CXX@ +LD = @CXX@ + +CPPFLAGS = -DUSE_CPL -DGRASS_GISBASE=\"@GRASS_GISBASE@\" \ + @GDAL_INC@ @GRASS_INCLUDE@ @PQ_INCLUDE@ @CPPFLAGS@ +CXXFLAGS = @CXX_WFLAGS@ @CXX_PIC@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ + +RANLIB = @RANLIB@ +SO_EXT = @SO_EXT@ +LD_SHARED = @LD_SHARED@ + +LIBS = @LIBS@ + +GRASSTABLES_DIR = @prefix@/share/gdal/grass + +AUTOLOAD_DIR = @AUTOLOAD_DIR@ + +GLIBNAME = gdal_GRASS.so +OLIBNAME = ogr_GRASS.so + +default: $(GLIBNAME) $(OLIBNAME) + +install: default + install -d $(AUTOLOAD_DIR) + cp $(GLIBNAME) $(AUTOLOAD_DIR) + cp $(OLIBNAME) $(AUTOLOAD_DIR) + test -d ${GRASSTABLES_DIR} || mkdir ${GRASSTABLES_DIR} + test -d ${GRASSTABLES_DIR}/etc || mkdir ${GRASSTABLES_DIR}/etc + test ! -e @GRASS_GISBASE@/etc/ellipse.table || cp @GRASS_GISBASE@/etc/ellipse.table ${GRASSTABLES_DIR}/etc + test ! -e @GRASS_GISBASE@/etc/datum.table || cp @GRASS_GISBASE@/etc/datum.table ${GRASSTABLES_DIR}/etc + test ! -e @GRASS_GISBASE@/etc/datumtransform.table || cp @GRASS_GISBASE@/etc/datumtransform.table ${GRASSTABLES_DIR}/etc + test ! -e @GRASS_GISBASE@/etc/proj/ellipse.table || cp @GRASS_GISBASE@/etc/proj/ellipse.table ${GRASSTABLES_DIR}/etc + test ! -e @GRASS_GISBASE@/etc/proj/datum.table || cp @GRASS_GISBASE@/etc/proj/datum.table ${GRASSTABLES_DIR}/etc + test ! -e @GRASS_GISBASE@/etc/proj/datumtransform.table || cp @GRASS_GISBASE@/etc/proj/datumtransform.table ${GRASSTABLES_DIR}/etc + test -d ${GRASSTABLES_DIR}/driver || mkdir ${GRASSTABLES_DIR}/driver + test -d ${GRASSTABLES_DIR}/driver/db || mkdir ${GRASSTABLES_DIR}/driver/db + cp -r @GRASS_GISBASE@/driver/db/* ${GRASSTABLES_DIR}/driver/db/ + +clean: + rm -f $(OLIBNAME) $(GLIBNAME) *.o + +distclean: clean + rm -fr Makefile config.status config.log autom*.cache + + +$(GLIBNAME): grass57dataset.o + $(LD_SHARED) $(LDFLAGS) grass57dataset.o $(LIBS) -o $(GLIBNAME) + +$(OLIBNAME): ogrgrassdriver.o ogrgrassdatasource.o ogrgrasslayer.o + $(LD_SHARED) $(LDFLAGS) ogrgrassdriver.o ogrgrassdatasource.o ogrgrasslayer.o $(LIBS) -o $(OLIBNAME) + +%.o: %.cpp + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + diff --git a/bazaar/plugin/gdal/frmts/grass/pkg/README b/bazaar/plugin/gdal/frmts/grass/pkg/README new file mode 100644 index 000000000..cd545cd07 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grass/pkg/README @@ -0,0 +1,63 @@ +Standalone GRASS Drivers for GDAL and OGR +========================================= + +This package contains standalone drivers for GRASS raster and vector +files that can be built after GDAL has been built and installed as an +"autoload" driver. + +This is particularly useful in resolving problems with GRASS depending +on GDAL, but GDAL with GRASS support depending on GRASS. With this +package you can configure and install GDAL normally (--without-grass), then +build and install GRASS normally and finally build and install this driver. + +To build this driver it is necessary for it to find GDAL and GRASS support +files. Typically the configure and build process would look something like: + +./configure --with-gdal=/usr/local/bin/gdal-config --with-grass=/usr/local/grass-7.0.0 +make +sudo make install + +See also: + + http://www.gdal.org/ + http://grass.osgeo.org + + +--- + +FAQs +---- + + +Question: + +I am trying to install gdal-grass 1.3.1 on Red hat enterprise linux +advanced server 3.0. I have previously installed gdal 1.3.1 without- +grass, and Grass 6.0.1 with-gdal. I have tried to configure gdal-grass +with: + +./configure --with-gdal=/usr/local/gdal/bin/gdal-config --with- +grass=/usr/local/grass-6.0.1 + +It seems to find gdal alright, but then balks at the Grass location. The +Grass location specified above is indeed the correct location. I have +also tried adding --with-grass=/usr/local/grass-6.0.1/lib, but with no +success. My error is: + +... +checking for G_asprintf in -lgrass_gis ... no +configure: error: --with-grass=/usr/local/grass-6.0.1 requested, but +libraries not found? + + +Answer: + +Your problem is likely to be solved by editing /etc/ld.so.conf to +include the locations of proj, gdal, grass, and geos. Specifically, +the full path to both gdal-config and geos-config, and the full paths +to the library locations of proj (often /usr/local/lib) and grass (/ +usr/local/grass-6.0.1/lib). After editing ld.so.conf, run ldconfig, +and you should be good to go. + +I ran into this problem this weekend (and posted for help to this +list), so it seems to be a pretty common issue. diff --git a/bazaar/plugin/gdal/frmts/grass/pkg/aclocal.m4 b/bazaar/plugin/gdal/frmts/grass/pkg/aclocal.m4 new file mode 100644 index 000000000..7bffcf46d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grass/pkg/aclocal.m4 @@ -0,0 +1,202 @@ +AC_DEFUN(AC_COMPILER_LOCALHACK, +[ + AC_MSG_CHECKING([if local/include already standard]) + + rm -f comp.out + echo 'int main() { int i = 1; if( *((unsigned char *) &i) == 0 ) printf( "BIGENDIAN"); return 0; }' >> conftest.c + ${CC} $CPPFLAGS $EXTRA_INCLUDES -o conftest conftest.c 2> comp.out + COMP_CHECK=`grep "system directory" comp.out | grep /usr/local/include` + if test -z "$COMP_CHECK" ; then + AC_MSG_RESULT([no, everything is ok]) + else + AC_MSG_RESULT([yes, stripping extras]) + CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-I\/usr\/local\/include //"` + CFLAGS=`echo "$CFLAGS " | sed "s/-I\/usr\/local\/include //"` + EXTRA_INCLUDES=`echo "$EXTRA_INCLUDES " | sed "s/-I\/usr\/local\/include //"` + fi + rm -f comp.out +]) + +AC_DEFUN(AC_COMPILER_WFLAGS, +[ + # Remove -g from compile flags, we will add via CFG variable if + # we need it. + CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-g //"` + CFLAGS=`echo "$CFLAGS " | sed "s/-g //"` + + # check for GNU compiler, and use -Wall + if test "$GCC" = "yes"; then + C_WFLAGS="-Wall" + AC_DEFINE(USE_GNUCC, 1, [Define to 1, if you have GNU C + compiler]) + fi + if test "$GXX" = "yes"; then + CXX_WFLAGS="-Wall" + AC_DEFINE(USE_GNUCC, 1, [Define to 1, if you have GNU C + compiler]) + fi + AC_SUBST(CXX_WFLAGS,$CXX_WFLAGS) + AC_SUBST(C_WFLAGS,$C_WFLAGS) +]) + +AC_DEFUN(AC_COMPILER_PIC, +[ + echo 'void f(){}' > conftest.c + if test -z "`${CC-cc} -fPIC -c conftest.c 2>&1`"; then + C_PIC=-fPIC + else + C_PIC= + fi + if test -z "`${CXX-g++} -fPIC -c conftest.c 2>&1`"; then + CXX_PIC=-fPIC + else + CXX_PIC= + fi + rm -f conftest* + + AC_SUBST(CXX_PIC,$CXX_PIC) + AC_SUBST(C_PIC,$C_PIC) +]) + +dnl +dnl Try to find something to link shared libraries with. Use "c++ -shared" +dnl in preference to "ld -shared" because it will link in required c++ +dnl run time support for us. +dnl +AC_DEFUN(AC_LD_SHARED, +[ + echo 'void g(); int main(){ g(); return 0; }' > conftest1.c + + echo '#include ' > conftest2.c + echo 'void g(); void g(){printf("");}' >> conftest2.c + ${CC} ${C_PIC} -c conftest2.c + + SO_EXT="so" + export SO_EXT + LD_SHARED="/bin/true" + if test ! -z "`uname -a | grep IRIX`" ; then + IRIX_ALL=-all + else + IRIX_ALL= + fi + + AC_ARG_WITH(ld-shared,[ --with-ld-shared=cmd provide shared library link],,) + + if test "$with_ld_shared" != "" ; then + if test "$with_ld_shared" = "no" ; then + echo "user disabled shared library support." + else + echo "using user supplied .so link command ... $with_ld_shared" + fi + LD_SHARED="$with_ld_shared" + fi + + dnl Check For Cygwin case. Actually verify that the produced DLL works. + + if test ! -z "`uname -a | grep CYGWIN`" \ + -a "$LD_SHARED" = "/bin/true" \ + -a -z "`gcc -shared conftest2.o -o libconftest.dll`" ; then + if test -z "`${CC} conftest1.c -L./ -lconftest -o conftest1 2>&1`"; then + LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH" + if test -z "$LD_LIBRARY_PATH" ; then + LD_LIBRARY_PATH="`pwd`" + else + LD_LIBRARY_PATH="`pwd`:$LD_LIBRARY_PATH" + fi + export LD_LIBRARY_PATH + if test -z "`./conftest1 2>&1`" ; then + echo "checking for Cygwin gcc -shared ... yes" + LD_SHARED="c++ -shared" + SO_EXT="dll" + fi + LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD" + fi + fi + + dnl Test special MacOS (Darwin) case. + + if test ! -z "`uname | grep Darwin`" \ + -a "$LD_SHARED" = "/bin/true" \ + -a -z "`${CXX} -dynamiclib conftest2.o -o libconftest.so 2>&1`" ; then + ${CC} -c conftest1.c + if test -z "`${CXX} conftest1.o libconftest.so -o conftest1 2>&1`"; then + DYLD_LIBRARY_PATH_OLD="$DYLD_LIBRARY_PATH" + if test -z "$DYLD_LIBRARY_PATH" ; then + DYLD_LIBRARY_PATH="`pwd`" + else + DYLD_LIBRARY_PATH="`pwd`:$DYLD_LIBRARY_PATH" + fi + export DYLD_LIBRARY_PATH + if test -z "`./conftest1 2>&1`" ; then + echo "checking for ${CXX} -dynamiclib ... yes" + LD_SHARED="${CXX} -dynamiclib" + SO_EXT=dylib + fi + DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH_OLD" + fi + rm -f conftest1.o + fi + + if test "$LD_SHARED" = "/bin/true" \ + -a -z "`${CXX} -shared $IRIX_ALL conftest2.o -o libconftest.so 2>&1|grep -v WARNING`" ; then + if test -z "`${CC} conftest1.c libconftest.so -o conftest1 2>&1`"; then + LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH" + if test -z "$LD_LIBRARY_PATH" ; then + LD_LIBRARY_PATH="`pwd`" + else + LD_LIBRARY_PATH="`pwd`:$LD_LIBRARY_PATH" + fi + export LD_LIBRARY_PATH + if test -z "`./conftest1 2>&1`" ; then + echo "checking for ${CXX} -shared ... yes" + LD_SHARED="${CXX} -shared $IRIX_ALL" + else + echo "checking for ${CXX} -shared ... no(3)" + fi + LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD" + else + echo "checking for ${CXX} -shared ... no(2)" + fi + else + if test "$LD_SHARED" = "/bin/true" ; then + echo "checking for ${CXX} -shared ... no(1)" + fi + fi + + if test "$LD_SHARED" = "/bin/true" \ + -a -z "`ld -shared conftest2.o -o libconftest.so 2>&1`" ; then + if test -z "`${CC} conftest1.c libconftest.so -o conftest1 2>&1`"; then + LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH" + if test -z "$LD_LIBRARY_PATH" ; then + LD_LIBRARY_PATH="`pwd`" + else + LD_LIBRARY_PATH="`pwd`:$LD_LIBRARY_PATH" + fi + export LD_LIBRARY_PATH + if test -z "`./conftest1 2>&1`" ; then + echo "checking for ld -shared ... yes" + LD_SHARED="ld -shared" + fi + LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD" + fi + fi + + if test "$LD_SHARED" = "/bin/true" ; then + echo "checking for ld -shared ... no" + if test ! -x /bin/true ; then + LD_SHARED=/usr/bin/true + fi + fi + if test "$LD_SHARED" = "no" ; then + if test -x /bin/true ; then + LD_SHARED=/bin/true + else + LD_SHARED=/usr/bin/true + fi + fi + + rm -f conftest* libconftest* + + AC_SUBST(LD_SHARED,$LD_SHARED) + AC_SUBST(SO_EXT,$SO_EXT) +]) diff --git a/bazaar/plugin/gdal/frmts/grass/pkg/configure b/bazaar/plugin/gdal/frmts/grass/pkg/configure new file mode 100644 index 000000000..ae5e319ea --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grass/pkg/configure @@ -0,0 +1,4489 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69. +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= +PACKAGE_URL= + +ac_unique_file="Makefile.in" +ac_subst_vars='LTLIBOBJS +LIBOBJS +PQ_INCLUDE +GRASS_GISBASE +GRASS_INCLUDE +AUTOLOAD_DIR +GDAL_INC +GDAL_CONFIG +C_WFLAGS +CXX_WFLAGS +SO_EXT +LD_SHARED +C_PIC +CXX_PIC +RANLIB +ac_ct_CXX +CXXFLAGS +CXX +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +with_ld_shared +with_gdal +with_autoload +with_grass +with_postgres_includes +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CXX +CXXFLAGS +CCC' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-ld-shared=cmd provide shared library link + --with-gdal=PATH GDAL (PATH is path to gdal-config) + --with-autoload=DIR Directory for autoload drivers + --with-grass=ARG Include GRASS support (ARG=GRASS install tree dir) + --with-postgres-includes=DIR use PostgreSQL includes in DIR + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CXX C++ compiler command + CXXFLAGS C++ compiler flags + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + + + echo 'void f(){}' > conftest.c + if test -z "`${CC-cc} -fPIC -c conftest.c 2>&1`"; then + C_PIC=-fPIC + else + C_PIC= + fi + if test -z "`${CXX-g++} -fPIC -c conftest.c 2>&1`"; then + CXX_PIC=-fPIC + else + CXX_PIC= + fi + rm -f conftest* + + CXX_PIC=$CXX_PIC + + C_PIC=$C_PIC + + + + echo 'void g(); int main(){ g(); return 0; }' > conftest1.c + + echo '#include ' > conftest2.c + echo 'void g(); void g(){printf("");}' >> conftest2.c + ${CC} ${C_PIC} -c conftest2.c + + SO_EXT="so" + export SO_EXT + LD_SHARED="/bin/true" + if test ! -z "`uname -a | grep IRIX`" ; then + IRIX_ALL=-all + else + IRIX_ALL= + fi + + +# Check whether --with-ld-shared was given. +if test "${with_ld_shared+set}" = set; then : + withval=$with_ld_shared; +fi + + + if test "$with_ld_shared" != "" ; then + if test "$with_ld_shared" = "no" ; then + echo "user disabled shared library support." + else + echo "using user supplied .so link command ... $with_ld_shared" + fi + LD_SHARED="$with_ld_shared" + fi + + + if test ! -z "`uname -a | grep CYGWIN`" \ + -a "$LD_SHARED" = "/bin/true" \ + -a -z "`gcc -shared conftest2.o -o libconftest.dll`" ; then + if test -z "`${CC} conftest1.c -L./ -lconftest -o conftest1 2>&1`"; then + LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH" + if test -z "$LD_LIBRARY_PATH" ; then + LD_LIBRARY_PATH="`pwd`" + else + LD_LIBRARY_PATH="`pwd`:$LD_LIBRARY_PATH" + fi + export LD_LIBRARY_PATH + if test -z "`./conftest1 2>&1`" ; then + echo "checking for Cygwin gcc -shared ... yes" + LD_SHARED="c++ -shared" + SO_EXT="dll" + fi + LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD" + fi + fi + + + if test ! -z "`uname | grep Darwin`" \ + -a "$LD_SHARED" = "/bin/true" \ + -a -z "`${CXX} -dynamiclib conftest2.o -o libconftest.so 2>&1`" ; then + ${CC} -c conftest1.c + if test -z "`${CXX} conftest1.o libconftest.so -o conftest1 2>&1`"; then + DYLD_LIBRARY_PATH_OLD="$DYLD_LIBRARY_PATH" + if test -z "$DYLD_LIBRARY_PATH" ; then + DYLD_LIBRARY_PATH="`pwd`" + else + DYLD_LIBRARY_PATH="`pwd`:$DYLD_LIBRARY_PATH" + fi + export DYLD_LIBRARY_PATH + if test -z "`./conftest1 2>&1`" ; then + echo "checking for ${CXX} -dynamiclib ... yes" + LD_SHARED="${CXX} -dynamiclib" + SO_EXT=dylib + fi + DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH_OLD" + fi + rm -f conftest1.o + fi + + if test "$LD_SHARED" = "/bin/true" \ + -a -z "`${CXX} -shared $IRIX_ALL conftest2.o -o libconftest.so 2>&1|grep -v WARNING`" ; then + if test -z "`${CC} conftest1.c libconftest.so -o conftest1 2>&1`"; then + LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH" + if test -z "$LD_LIBRARY_PATH" ; then + LD_LIBRARY_PATH="`pwd`" + else + LD_LIBRARY_PATH="`pwd`:$LD_LIBRARY_PATH" + fi + export LD_LIBRARY_PATH + if test -z "`./conftest1 2>&1`" ; then + echo "checking for ${CXX} -shared ... yes" + LD_SHARED="${CXX} -shared $IRIX_ALL" + else + echo "checking for ${CXX} -shared ... no(3)" + fi + LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD" + else + echo "checking for ${CXX} -shared ... no(2)" + fi + else + if test "$LD_SHARED" = "/bin/true" ; then + echo "checking for ${CXX} -shared ... no(1)" + fi + fi + + if test "$LD_SHARED" = "/bin/true" \ + -a -z "`ld -shared conftest2.o -o libconftest.so 2>&1`" ; then + if test -z "`${CC} conftest1.c libconftest.so -o conftest1 2>&1`"; then + LD_LIBRARY_PATH_OLD="$LD_LIBRARY_PATH" + if test -z "$LD_LIBRARY_PATH" ; then + LD_LIBRARY_PATH="`pwd`" + else + LD_LIBRARY_PATH="`pwd`:$LD_LIBRARY_PATH" + fi + export LD_LIBRARY_PATH + if test -z "`./conftest1 2>&1`" ; then + echo "checking for ld -shared ... yes" + LD_SHARED="ld -shared" + fi + LD_LIBRARY_PATH="$LD_LIBRARY_PATH_OLD" + fi + fi + + if test "$LD_SHARED" = "/bin/true" ; then + echo "checking for ld -shared ... no" + if test ! -x /bin/true ; then + LD_SHARED=/usr/bin/true + fi + fi + if test "$LD_SHARED" = "no" ; then + if test -x /bin/true ; then + LD_SHARED=/bin/true + else + LD_SHARED=/usr/bin/true + fi + fi + + rm -f conftest* libconftest* + + LD_SHARED=$LD_SHARED + + SO_EXT=$SO_EXT + + + + # Remove -g from compile flags, we will add via CFG variable if + # we need it. + CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-g //"` + CFLAGS=`echo "$CFLAGS " | sed "s/-g //"` + + # check for GNU compiler, and use -Wall + if test "$GCC" = "yes"; then + C_WFLAGS="-Wall" + +$as_echo "#define USE_GNUCC 1" >>confdefs.h + + fi + if test "$GXX" = "yes"; then + CXX_WFLAGS="-Wall" + +$as_echo "#define USE_GNUCC 1" >>confdefs.h + + fi + CXX_WFLAGS=$CXX_WFLAGS + + C_WFLAGS=$C_WFLAGS + + + + + +# Check whether --with-gdal was given. +if test "${with_gdal+set}" = set; then : + withval=$with_gdal; +fi + + +if test "$with_gdal" = "yes" -o "$with_gdal" = "" ; then + + if test "`basename xx/$with_gdal`" = "gdal-config" ; then + GDAL_CONFIG="$with_gdal" + fi + + if test -z "$GDAL_CONFIG" ; then + # Extract the first word of "gdal-config", so it can be a program name with args. +set dummy gdal-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_GDAL_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $GDAL_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_GDAL_CONFIG="$GDAL_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_GDAL_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_GDAL_CONFIG" && ac_cv_path_GDAL_CONFIG="no" + ;; +esac +fi +GDAL_CONFIG=$ac_cv_path_GDAL_CONFIG +if test -n "$GDAL_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GDAL_CONFIG" >&5 +$as_echo "$GDAL_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi + + if test "$GDAL_CONFIG" = "no" ; then + as_fn_error $? "couldn't find gdal-config" "$LINENO" 5 + fi + +elif test -n "$with_gdal" -a "$with_gdal" != "no" ; then + + GDAL_CONFIG=$with_gdal + + if test -f "$GDAL_CONFIG" -a -x "$GDAL_CONFIG" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: user supplied gdal-config ($GDAL_CONFIG)" >&5 +$as_echo "user supplied gdal-config ($GDAL_CONFIG)" >&6; } + else + as_fn_error $? "'$GDAL_CONFIG' is not an executable. Make sure you use --with-gdal=/path/to/gdal-config" "$LINENO" 5 + fi + +else + + as_fn_error $? "gdal required to build GDAL GRASS driver" "$LINENO" 5 + +fi + +LIBS="`$GDAL_CONFIG --libs` $LIBS" +GDAL_INC=`$GDAL_CONFIG --cflags` + +GDAL_INC=$GDAL_INC + + + +# Check whether --with-autoload was given. +if test "${with_autoload+set}" = set; then : + withval=$with_autoload; +fi + + +if test "$with_autoload" != "" ; then + AUTOLOAD_DIR=$with_autoload +else + if $GDAL_CONFIG --autoload > /dev/null 2>&1 ; then + AUTOLOAD_DIR=`$GDAL_CONFIG --autoload` + else + AUTOLOAD_DIR=`$GDAL_CONFIG --prefix`/lib/gdalplugins + fi +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: using $AUTOLOAD_DIR as GDAL shared library autoload directory" >&5 +$as_echo "using $AUTOLOAD_DIR as GDAL shared library autoload directory" >&6; } +AUTOLOAD_DIR=$AUTOLOAD_DIR + + + +GRASS_SETTING=no +GRASS_INCLUDE= +GRASS_GISBASE= +export GRASS_INCLUDE GRASS_SETTING GRASS_GISBASE + + +# Check whether --with-grass was given. +if test "${with_grass+set}" = set; then : + withval=$with_grass; +fi + + +if test "$with_grass" = "no" ; then + as_fn_error $? "grass required for this driver, please install GRASS 5.7 or later and rebuild" "$LINENO" 5 +fi + +if test "$with_grass" != "yes" ; then + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for G_is_initialized in -lgrass_gis" >&5 +$as_echo_n "checking for G_is_initialized in -lgrass_gis... " >&6; } +if ${ac_cv_lib_grass_gis_G_is_initialized+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lgrass_gis -L$with_grass/lib -lgrass_datetime $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char G_is_initialized (); +int +main () +{ +return G_is_initialized (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_grass_gis_G_is_initialized=yes +else + ac_cv_lib_grass_gis_G_is_initialized=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_grass_gis_G_is_initialized" >&5 +$as_echo "$ac_cv_lib_grass_gis_G_is_initialized" >&6; } +if test "x$ac_cv_lib_grass_gis_G_is_initialized" = xyes; then : + GRASS_SETTING=grass70+ +else + GRASS_SETTING=no +fi + + if test "$GRASS_SETTING" = "no" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for G_asprintf in -lgrass_gis" >&5 +$as_echo_n "checking for G_asprintf in -lgrass_gis... " >&6; } +if ${ac_cv_lib_grass_gis_G_asprintf+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lgrass_gis -L$with_grass/lib -lgrass_datetime $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char G_asprintf (); +int +main () +{ +return G_asprintf (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_grass_gis_G_asprintf=yes +else + ac_cv_lib_grass_gis_G_asprintf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_grass_gis_G_asprintf" >&5 +$as_echo "$ac_cv_lib_grass_gis_G_asprintf" >&6; } +if test "x$ac_cv_lib_grass_gis_G_asprintf" = xyes; then : + GRASS_SETTING=grass57+ +else + GRASS_SETTING=no +fi + + fi + + if test "$GRASS_SETTING" != "no" ; then + if test "$GRASS_SETTING" = "grass70+" ; then + G_RASTLIBS="-lgrass_raster -lgrass_imagery" + G_VECTLIBS="-lgrass_vector -lgrass_dig2 -lgrass_dgl -lgrass_rtree -lgrass_linkm -lgrass_dbmiclient -lgrass_dbmibase" + LIBS="-L$with_grass/lib $G_VECTLIBS $G_RASTLIBS -lgrass_gproj -lgrass_gmath -lgrass_gis -lgrass_datetime $LIBS" + else + G_RASTLIBS="-lgrass_I" + G_VECTLIBS="-lgrass_vect -lgrass_dig2 -lgrass_dgl -lgrass_rtree -lgrass_linkm -lgrass_dbmiclient -lgrass_dbmibase" + LIBS="-L$with_grass/lib $G_VECTLIBS $G_RASTLIBS -lgrass_gproj -lgrass_vask -lgrass_gmath -lgrass_gis -lgrass_datetime $LIBS" + fi + GRASS_INCLUDE="-I$with_grass/include" + GRASS_GISBASE="$with_grass" + HAVE_GRASS=yes + else + as_fn_error $? "--with-grass=$with_grass requested, but libraries not found!" "$LINENO" 5 + fi +fi + +GRASS_INCLUDE=$GRASS_INCLUDE + +GRASS_GISBASE=$GRASS_GISBASE + + + + + +# Check whether --with-postgres_includes was given. +if test "${with_postgres_includes+set}" = set; then : + withval=$with_postgres_includes; postgres_includes="$withval" +else + postgres_includes=no +fi + + +PQ_INCLUDE= +if test "x$postgres_includes" != "xno"; then +# With PostgreSQL includes directory +PQ_INCLUDE="-I$postgres_includes" +fi + + + + +rm -f conftest* + +ac_config_files="$ac_config_files Makefile" + + + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +ac_script=' +:mline +/\\$/{ + N + s,\\\n,, + b mline +} +t clear +:clear +s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g +t quote +s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g +t quote +b any +:quote +s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g +s/\[/\\&/g +s/\]/\\&/g +s/\$/$$/g +H +:any +${ + g + s/^\n// + s/\n/ /g + p +} +' +DEFS=`sed -n "$ac_script" confdefs.h` + + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h | --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + + +eval set X " :F $CONFIG_FILES " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + + + diff --git a/bazaar/plugin/gdal/frmts/grass/pkg/configure.in b/bazaar/plugin/gdal/frmts/grass/pkg/configure.in new file mode 100644 index 000000000..0ac90cb0d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grass/pkg/configure.in @@ -0,0 +1,175 @@ +dnl *************************************************************************** +dnl $Id: configure.in 28598 2015-03-03 07:34:28Z martinl $ +dnl +dnl Project: GDAL GRASS Plugin +dnl Purpose: Configure source file. +dnl Author: Frank Warmerdam, warmerdam@pobox.com +dnl +dnl *************************************************************************** +dnl Copyright (c) 2005, Frank Warmerdam +dnl +dnl Permission is hereby granted, free of charge, to any person obtaining a +dnl copy of this software and associated documentation files (the "Software"), +dnl to deal in the Software without restriction, including without limitation +dnl the rights to use, copy, modify, merge, publish, distribute, sublicense, +dnl and/or sell copies of the Software, and to permit persons to whom the +dnl Software is furnished to do so, subject to the following conditions: +dnl +dnl The above copyright notice and this permission notice shall be included +dnl in all copies or substantial portions of the Software. +dnl +dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +dnl OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +dnl FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +dnl THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +dnl LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +dnl FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +dnl DEALINGS IN THE SOFTWARE. +dnl *************************************************************************** + +dnl Disable configure caching ... it causes lots of hassles. +define([AC_CACHE_LOAD], ) +define([AC_CACHE_SAVE], ) + +dnl Process this file with autoconf to produce a configure script. +AC_INIT(Makefile.in) + +dnl We require autoconf 2.52+ for libtool support on cygwin/mingw hosts +AC_PREREQ(2.52) + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_CXX + +AC_PROG_RANLIB +AC_COMPILER_PIC +AC_LD_SHARED +AC_COMPILER_WFLAGS + +dnl --------------------------------------------------------------------------- +dnl Find GDAL +dnl --------------------------------------------------------------------------- + +AC_ARG_WITH(gdal, +[ --with-gdal[=PATH] GDAL (PATH is path to gdal-config)],,) + +if test "$with_gdal" = "yes" -o "$with_gdal" = "" ; then + + if test "`basename xx/$with_gdal`" = "gdal-config" ; then + GDAL_CONFIG="$with_gdal" + fi + + if test -z "$GDAL_CONFIG" ; then + AC_PATH_PROG(GDAL_CONFIG, gdal-config, no) + fi + + if test "$GDAL_CONFIG" = "no" ; then + AC_MSG_ERROR([couldn't find gdal-config]) + fi + +elif test -n "$with_gdal" -a "$with_gdal" != "no" ; then + + GDAL_CONFIG=$with_gdal + + if test -f "$GDAL_CONFIG" -a -x "$GDAL_CONFIG" ; then + AC_MSG_RESULT([user supplied gdal-config ($GDAL_CONFIG)]) + else + AC_MSG_ERROR(['$GDAL_CONFIG' is not an executable. Make sure you use --with-gdal=/path/to/gdal-config]) + fi + +else + + AC_MSG_ERROR([gdal required to build GDAL GRASS driver]) + +fi + +LIBS="`$GDAL_CONFIG --libs` $LIBS" +GDAL_INC=`$GDAL_CONFIG --cflags` + +AC_SUBST(GDAL_INC, $GDAL_INC) + +dnl --------------------------------------------------------------------------- +dnl Where to put driver? +dnl --------------------------------------------------------------------------- +AC_ARG_WITH(autoload,[ --with-autoload[=DIR] Directory for autoload drivers],,) + +if test "$with_autoload" != "" ; then + AUTOLOAD_DIR=$with_autoload +else + if $GDAL_CONFIG --autoload > /dev/null 2>&1 ; then + AUTOLOAD_DIR=`$GDAL_CONFIG --autoload` + else + AUTOLOAD_DIR=`$GDAL_CONFIG --prefix`/lib/gdalplugins + fi +fi + +AC_MSG_RESULT(using $AUTOLOAD_DIR as GDAL shared library autoload directory) +AC_SUBST(AUTOLOAD_DIR,$AUTOLOAD_DIR) + +dnl --------------------------------------------------------------------------- +dnl Find GRASS 5.7 or GRASS 7 +dnl --------------------------------------------------------------------------- + +GRASS_SETTING=no +GRASS_INCLUDE= +GRASS_GISBASE= +export GRASS_INCLUDE GRASS_SETTING GRASS_GISBASE + +AC_ARG_WITH(grass,[ --with-grass[=ARG] Include GRASS support (ARG=GRASS install tree dir)],,) + +if test "$with_grass" = "no" ; then + AC_MSG_ERROR([grass required for this driver, please install GRASS 5.7 or later and rebuild]) +fi + +if test "$with_grass" != "yes" ; then + + AC_CHECK_LIB(grass_gis,G_is_initialized,GRASS_SETTING=grass70+,GRASS_SETTING=no,-L$with_grass/lib -lgrass_datetime) + if test "$GRASS_SETTING" = "no" ; then + AC_CHECK_LIB(grass_gis,G_asprintf,GRASS_SETTING=grass57+,GRASS_SETTING=no,-L$with_grass/lib -lgrass_datetime) + fi + + if test "$GRASS_SETTING" != "no" ; then + if test "$GRASS_SETTING" = "grass70+" ; then + G_RASTLIBS="-lgrass_raster -lgrass_imagery" + G_VECTLIBS="-lgrass_vector -lgrass_dig2 -lgrass_dgl -lgrass_rtree -lgrass_linkm -lgrass_dbmiclient -lgrass_dbmibase" + LIBS="-L$with_grass/lib $G_VECTLIBS $G_RASTLIBS -lgrass_gproj -lgrass_gmath -lgrass_gis -lgrass_datetime $LIBS" + else + G_RASTLIBS="-lgrass_I" + G_VECTLIBS="-lgrass_vect -lgrass_dig2 -lgrass_dgl -lgrass_rtree -lgrass_linkm -lgrass_dbmiclient -lgrass_dbmibase" + LIBS="-L$with_grass/lib $G_VECTLIBS $G_RASTLIBS -lgrass_gproj -lgrass_vask -lgrass_gmath -lgrass_gis -lgrass_datetime $LIBS" + fi + GRASS_INCLUDE="-I$with_grass/include" + GRASS_GISBASE="$with_grass" + HAVE_GRASS=yes + else + AC_MSG_ERROR([--with-grass=$with_grass requested, but libraries not found!]) + fi +fi + +AC_SUBST(GRASS_INCLUDE,$GRASS_INCLUDE) +AC_SUBST(GRASS_GISBASE,$GRASS_GISBASE) + +dnl --------------------------------------------------------------------------- + +dnl --------------------------------------------------------------------------- +dnl Find PostgreSQL (GRASS optional dependency) +dnl --------------------------------------------------------------------------- + +AC_ARG_WITH(postgres_includes,[ --with-postgres-includes=DIR use PostgreSQL includes in DIR], postgres_includes="$withval", postgres_includes=no) + +PQ_INCLUDE= +if test "x$postgres_includes" != "xno"; then +# With PostgreSQL includes directory +PQ_INCLUDE="-I$postgres_includes" +fi + +AC_SUBST(PQ_INCLUDE) + +dnl --------------------------------------------------------------------------- + +rm -f conftest* + +AC_OUTPUT(Makefile) + + + diff --git a/bazaar/plugin/gdal/frmts/grib/GNUmakefile b/bazaar/plugin/gdal/frmts/grib/GNUmakefile new file mode 100644 index 000000000..65fec6af2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/GNUmakefile @@ -0,0 +1,59 @@ + + +EXTRAFLAGS = -Idegrib18/degrib + +include ../../GDALmake.opt + +OBJ = gribdataset.o \ + clock.o \ + degrib1.o \ + degrib2.o inventory.o metaname.o myerror.o tdlpack.o filedatasource.o memorydatasource.o grib1tab.o myutil.o metaparse.o weather.o metaprint.o engribapi.o grib2api.o myassert.o scan.o memendian.o fileendian.o gridtemplates.o drstemplates.o pdstemplates.o gbits.o g2_free.o g2_unpack1.o g2_unpack2.o g2_unpack3.o g2_unpack4.o g2_unpack5.o g2_unpack6.o g2_unpack7.o g2_info.o g2_getfld.o simunpack.o comunpack.o pack_gp.o reduce.o specpack.o specunpack.o rdieee.o mkieee.o int_power.o simpack.o compack.o cmplxpack.o misspack.o g2_create.o g2_addlocal.o g2_addgrid.o g2_addfield.o g2_gribend.o getdim.o g2_miss.o getpoly.o seekgb.o \ + dec_jpeg2000.o jpcunpack.o jpcpack.o enc_jpeg2000.o + +ifeq ($(HAVE_JASPER),yes) +EXTRAFLAGS := $(EXTRAFLAGS) -DHAVE_JASPER +endif + +CPPFLAGS := $(CPPFLAGS) $(EXTRAFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +../o/%.$(OBJ_EXT): degrib18/degrib/%.c + $(CC) -c $(GDAL_INCLUDE) $(CPPFLAGS) $(CFLAGS) -Idegrib18/g2clib-1.0.4 $< -o $@ + +../o/%.$(OBJ_EXT): degrib18/degrib/%.cpp + $(CXX) -c $(GDAL_INCLUDE) $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +../o/%.$(OBJ_EXT): degrib18/g2clib-1.0.4/%.c + $(CC) -c $(GDAL_INCLUDE) $(CPPFLAGS) $(CFLAGS) $< -o $@ + +../o/%.$(OBJ_EXT): degrib18/g2clib-1.0.4/%.cpp + $(CXX) -c $(GDAL_INCLUDE) $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +%.$(OBJ_EXT): degrib18/degrib/%.c + $(CC) -c $(GDAL_INCLUDE) $(CPPFLAGS) $(CFLAGS) -Idegrib18/g2clib-1.0.4 $< -o $@ + +%.$(OBJ_EXT): degrib18/degrib/%.cpp + $(CXX) -c $(GDAL_INCLUDE) $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +%.$(OBJ_EXT): degrib18/g2clib-1.0.4/%.c + $(CC) -c $(GDAL_INCLUDE) $(CPPFLAGS) $(CFLAGS) $< -o $@ + +%.$(OBJ_EXT): degrib18/g2clib-1.0.4/%.cpp + $(CXX) -c $(GDAL_INCLUDE) $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +defaultold: $(OBJ:.o=.$(OBJ_EXT)) + (cd degrib18/degrib; $(MAKE)); cd ../.. + (cd degrib18/g2clib-1.0.4; $(MAKE)) +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +plugin: gdal_GRIB.so + +gdal_GRIB.so: $(OBJ) + $(LD_SHARED) $(OBJ) $(GDAL_LIBS) $(LIBS) -o gdal_GRIB.so + + + diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/LICENSE.TXT b/bazaar/plugin/gdal/frmts/grib/degrib18/LICENSE.TXT new file mode 100644 index 000000000..b5702f009 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/LICENSE.TXT @@ -0,0 +1,38 @@ +gdal/frmts/grib/degrib/* +------------------------ + +The degrib and g2clib source code are modified versions of code produced +by NOAA NWS and are in the public domain subject to the following +restrictions: + +http://www.weather.gov/im/softa.htm + +DISCLAIMER The United States Government makes no warranty, expressed or +implied, as to the usefulness of the software and documentation for any +purpose. The U.S. Government, its instrumentalities, officers, employees, +and agents assumes no responsibility (1) for the use of the software and +documentation listed below, or (2) to provide technical support to users. + +http://www.weather.gov/disclaimer.php + + The information on government servers are in the public domain, unless +specifically annotated otherwise, and may be used freely by the public so +long as you do not 1) claim it is your own (e.g. by claiming copyright for +NWS information -- see below), 2) use it in a manner that implies an +endorsement or affiliation with NOAA/NWS, or 3) modify it in content and +then present it as official government material. You also cannot present +information of your own in a way that makes it appear to be official +government information.. + + The user assumes the entire risk related to its use of this data. NWS is +providing this data "as is," and NWS disclaims any and all warranties, +whether express or implied, including (without limitation) any implied +warranties of merchantability or fitness for a particular purpose. In no +event will NWS be liable to you or to any third party for any direct, +indirect, incidental, consequential, special or exemplary damages or lost +profit resulting from any use or misuse of this data. + + As required by 17 U.S.C. 403, third parties producing copyrighted works +consisting predominantly of the material appearing in NWS Web pages must +provide notice with such work(s) identifying the NWS material incorporated +and stating that such material is not subject to copyright protection. \ No newline at end of file diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/clock.c b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/clock.c new file mode 100644 index 000000000..4d4305612 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/clock.c @@ -0,0 +1,2441 @@ +#include +#include +#include +#include +#include +#include +#include "clock.h" +#include "myutil.h" +#include "myassert.h" +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#include "cpl_port.h" + +/* Take a look at the options in: + * http://www.unet.univie.ac.at/aix/cmds/aixcmds2/date.htm#A270961 + */ +/* Timezone is defined through out as the time needed to add to local time + * to get UTC, rather than the reverse. So EST is +5 not -5. */ + +#define PERIOD_YEARS 146097L +#define SEC_DAY 86400L +#define ISLEAPYEAR(y) (((y)%400 == 0) || (((y)%4 == 0) && ((y)%100 != 0))) + +/***************************************************************************** + * ThirdMonday() -- + * + * Carl McCalla / MDL + * + * PURPOSE + * Compute the day-of-the-month which is the third Monday of the month. + * + * ARGUMENTS + * monthStartDOW = starting day of the week (e.g., 0 = Sunday, 1 = Monday, + * etc.) (Input) + * + * RETURNS + * int (the day-of-the-month which is the third Monday of the month) + * + * HISTORY + * 6/2006 Carl McCalla, Sr. (MDL): Created + * + * NOTES + * *************************************************************************** + */ +static int ThirdMonday (int monthStartDOW) +{ + if (monthStartDOW == 0) { + return 16; + } else if (monthStartDOW == 1) { + return 15; + } else { + return ((7 - monthStartDOW) + 16); + } +} + +/***************************************************************************** + * Memorialday() -- + * + * Carl McCalla / MDL + * + * PURPOSE + * For the month of May, compute the day-of-the-month which is Memorial Day. + * + * ARGUMENTS + * monthStartDOW = starting day of the week (e.g., 0 = Sunday, 1 = Monday, + * etc.) (Input) + * + * RETURNS + * int (the day-of-the-month which is Memorial Day) + * + * HISTORY + * 6/2006 Carl McCalla, Sr. (MDL): Created + * + * NOTES + * *************************************************************************** + */ +static int Memorialday (int monthStartDOW) +{ + if (monthStartDOW == 0) { + return 30; + } else if (monthStartDOW == 6) { + return 31; + } else { + return ((5 - monthStartDOW) + 25); + } +} + +/***************************************************************************** + * Laborday() -- + * + * Carl McCalla / MDL + * + * PURPOSE + * For the month of September, compute the day-of-the-month which is Labor + * Day. + * + * ARGUMENTS + * monthStartDOW = starting day of the week (e.g., 0 = Sunday, 1 = Monday, + * etc.) (Input) + * + * RETURNS + * int (the day-of-the-month which is Labor Day) + * + * HISTORY + * 6/2006 Carl McCalla, Sr. (MDL): Created + * + * NOTES + * *************************************************************************** + */ +static int Laborday (int monthStartDOW) +{ + if (monthStartDOW == 0) { + return 2; + } else if (monthStartDOW == 1) { + return 1; + } else { + return ((6 - monthStartDOW) + 3); + } +} + +/***************************************************************************** + * Columbusday() -- + * + * Carl McCalla /MDL + * + * PURPOSE + * For the month of October, compute the day-of-the-month which is Columbus + * Day. + * + * ARGUMENTS + * monthStartDOW = starting day of the week (e.g., 0 = Sunday, 1 = Monday, + * etc.) (Input) + * + * RETURNS + * int (the day-of-the-month which is Columbus Day) + * + * HISTORY + * 6/2006 Carl McCalla, Sr. (MDL): Created + * + * NOTES + * *************************************************************************** + */ +static int Columbusday (int monthStartDOW) +{ + if ((monthStartDOW == 0) || (monthStartDOW == 1)) { + return (9 - monthStartDOW); + } else { + return (16 - monthStartDOW); + } +} + +/***************************************************************************** + * Thanksgivingday() -- + * + * Carl McCalla /MDL + * + * PURPOSE + * For the month of November, compute the day-of-the-month which is + * Thanksgiving Day. + * + * ARGUMENTS + * monthStartDOW = starting day of the week (e.g., 0 = Sunday, 1 = Monday, + * etc.) (Input) + * + * RETURNS + * int (the day-of-the-month which is Thanksgiving Day) + * + * HISTORY + * 6/2006 Carl McCalla, Sr. (MDL): Created + * + * NOTES + * *************************************************************************** + */ +static int Thanksgivingday (int monthStartDOW) +{ + if ((monthStartDOW >= 0) && (monthStartDOW <= 4)) { + return (26 - monthStartDOW); + } else if (monthStartDOW == 5) { + return 28; + } else { + return 27; + } +} + +/***************************************************************************** + * Clock_Holiday() -- + * + * Carl McCalla /MDL + * + * PURPOSE + * Return a holiday string (e.g., Christmas Day, Thanksgiving Day, etc.), if + * the current day of the month is a federal holiday. + * + * ARGUMENTS + * month = month of the year (e.g., 1 = Jan, 2 = Feb, etc.) (Input) + * day = the current day of the month (e.g., 1, 2, 3 ...) (Input) + * monthStartDOW = the day-of-the-month which is the first day of the month + * (e.g., 0 = Sunday, 1 = Monday, etc.) + * answer = String containing the holiday string, if the current day is + * a federal holiday, or a "", if the current day is not a + * federal holiday. + * + * RETURNS + * void + * + * HISTORY + * 6/2006 Carl McCalla, Sr. (MDL): Created + * + * NOTES + * *************************************************************************** + */ +static void Clock_Holiday (int month, int day, int monthStartDOW, + char answer[100]) +{ + switch (month) { + case 1: /* January */ + if (day == 1) { + strcpy (answer, "New Years Day"); + return; + } else if (ThirdMonday (monthStartDOW) == day) { + strcpy (answer, "Martin Luther King Jr Day"); + return; + } + break; + case 2: /* February */ + if (ThirdMonday (monthStartDOW) == day) { + strcpy (answer, "Presidents Day"); + return; + } + break; + case 5: /* May */ + if (Memorialday (monthStartDOW) == day) { + strcpy (answer, "Memorial Day"); + return; + } + break; + case 7: /* July */ + if (day == 4) { + strcpy (answer, "Independence Day"); + return; + } + break; + case 9: /* September */ + if (Laborday (monthStartDOW) == day) { + strcpy (answer, "Labor Day"); + return; + } + break; + case 10: /* October */ + if (Columbusday (monthStartDOW) == day) { + strcpy (answer, "Columbus Day"); + return; + } + break; + case 11: /* November */ + if (day == 11) { + strcpy (answer, "Veterans Day"); + return; + } else if (Thanksgivingday (monthStartDOW) == day) { + strcpy (answer, "Thanksgiving Day"); + return; + } + break; + case 12: /* December */ + if (day == 25) { + strcpy (answer, "Christmas Day"); + return; + } + break; + } + strcpy (answer, ""); + return; +} + +/***************************************************************************** + * Clock_Epock2YearDay() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To convert the days since the beginning of the epoch to days since + * beginning of the year and years since the beginning of the epoch. + * + * ARGUMENTS + * totDay = Number of days since the beginning of the epoch. (Input) + * Day = The days since the beginning of the year. (Output) + * Yr = The years since the epoch. (Output) + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 6/2004 AAT (MDL): Updated. + * + * NOTES + ***************************************************************************** + */ +void Clock_Epoch2YearDay (sInt4 totDay, int *Day, sInt4 *Yr) +{ + sInt4 year; /* Local copy of the year. */ + + year = 1970; + /* Jump to the correct 400 year period of time. */ + if ((totDay <= -PERIOD_YEARS) || (totDay >= PERIOD_YEARS)) { + year += 400 * (totDay / PERIOD_YEARS); + totDay -= PERIOD_YEARS * (totDay / PERIOD_YEARS); + } + if (totDay >= 0) { + while (totDay >= 366) { + if (ISLEAPYEAR (year)) { + if (totDay >= 1461) { + year += 4; + totDay -= 1461; + } else if (totDay >= 1096) { + year += 3; + totDay -= 1096; + } else if (totDay >= 731) { + year += 2; + totDay -= 731; + } else { + year++; + totDay -= 366; + } + } else { + year++; + totDay -= 365; + } + } + if ((totDay == 365) && (!ISLEAPYEAR (year))) { + year++; + totDay -= 365; + } + } else { + while (totDay <= -366) { + year--; + if (ISLEAPYEAR (year)) { + if (totDay <= -1461) { + year -= 3; + totDay += 1461; + } else if (totDay <= -1096) { + year -= 2; + totDay += 1096; + } else if (totDay <= -731) { + year--; + totDay += 731; + } else { + totDay += 366; + } + } else { + totDay += 365; + } + } + if (totDay < 0) { + year--; + if (ISLEAPYEAR (year)) { + totDay += 366; + } else { + totDay += 365; + } + } + } + *Day = (int) totDay; + *Yr = year; +} + +/***************************************************************************** + * Clock_MonthNum() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Determine which numbered month it is given the day since the beginning of + * the year, and the year since the beginning of the epoch. + * + * ARGUMENTS + * day = Day since the beginning of the year. (Input) + * year = Year since the beginning of the epoch. (Input) + * + * RETURNS: int (which month it is) + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 6/2004 AAT (MDL): Updated. + * + * NOTES + ***************************************************************************** + */ +int Clock_MonthNum (int day, sInt4 year) +{ + if (day < 31) + return 1; + if (ISLEAPYEAR (year)) + day -= 1; + if (day < 59) + return 2; + if (day <= 89) + return 3; + if (day == 242) + return 8; + return ((day + 64) * 5) / 153 - 1; +} + +/***************************************************************************** + * Clock_NumDay() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Returns either the number of days in the month or the number of days + * since the begining of the year. + * + * ARGUMENTS + * month = Month in question. (Input) + * day = Day of month in question (Input) + * year = years since the epoch (Input) + * f_tot = 1 if we want total days from begining of year, + * 0 if we want total days in the month. (Input) + * + * RETURNS: int + * Either the number of days in the month, or + * the number of days since the beginning of they year. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +int Clock_NumDay (int month, int day, sInt4 year, char f_tot) +{ + if (f_tot == 1) { + if (month > 2) { + if (ISLEAPYEAR (year)) { + return ((month + 1) * 153) / 5 - 63 + day; + } else { + return ((month + 1) * 153) / 5 - 64 + day; + } + } else { + return (month - 1) * 31 + day - 1; + } + } else { + if (month == 1) { + return 31; + } else if (month != 2) { + if ((((month - 3) % 5) % 2) == 1) { + return 30; + } else { + return 31; + } + } else { + if (ISLEAPYEAR (year)) { + return 29; + } else { + return 28; + } + } + } +} + +/***************************************************************************** + * Clock_FormatParse() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To format part of the output clock string. + * + * ARGUMENTS + * buffer = The output string to write to. (Output) + * sec = Seconds since beginning of day. (Input) + * floatSec = Part of a second since beginning of second. (Input) + * totDay = Days since the beginning of the epoch. (Input) + * year = Years since the beginning of the epoch (Input) + * month = Month since the beginning of the year (Input) + * day = Days since the beginning of the year (Input) + * format = Which part of the format string we are working on. (Input) + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 6/2004 AAT (MDL): Updated. + * + * NOTES + ***************************************************************************** + */ +static void Clock_FormatParse (char buffer[100], sInt4 sec, float floatSec, + sInt4 totDay, sInt4 year, int month, int day, + char format) +{ + static char *MonthName[] = { + "January", "February", "March", "April", "May", "June", "July", + "August", "September", "October", "November", "December" + }; + static char *DayName[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", + "Saturday" + }; + int dy; /* # of days from start of year to start of month. */ + int i; /* Temporary variable to help with computations. */ + int DOM; /* Day of the Month (e.g., 1-31) */ + int DOW; /* Numeric day of the week (e.g., 0 = Sunday, 1 = + * Monday, etc. */ + int monthStartDOW; /* Numeric day of the week of the 1st day of the + * month */ + char temp[100]; /* Helps parse the %D, %T, %r, and %R options. */ + + switch (format) { + case 'd': + dy = (Clock_NumDay (month, 1, year, 1) - 1); + sprintf (buffer, "%02d", day - dy); + return; + case 'm': + sprintf (buffer, "%02d", month); + return; + case 'E': + sprintf (buffer, "%2d", month); + return; + case 'Y': + sprintf (buffer, "%04d", year); + return; + case 'H': + sprintf (buffer, "%02d", (int) ((sec % 86400L) / 3600)); + return; + case 'G': + sprintf (buffer, "%2d", (int) ((sec % 86400L) / 3600)); + return; + case 'M': + sprintf (buffer, "%02d", (int) ((sec % 3600) / 60)); + return; + case 'S': + sprintf (buffer, "%02d", (int) (sec % 60)); + return; + case 'f': + sprintf (buffer, "%05.2f", ((int) (sec % 60)) + floatSec); + return; + case 'n': + sprintf (buffer, "\n"); + return; + case '%': + sprintf (buffer, "%%"); + return; + case 't': + sprintf (buffer, "\t"); + return; + case 'y': + sprintf (buffer, "%02d", (int) (year % 100)); + return; + case 'I': + i = ((sec % 43200L) / 3600); + if (i == 0) { + sprintf (buffer, "12"); + } else { + sprintf (buffer, "%02d", i); + } + return; + case 'p': + if (((sec % 86400L) / 3600) >= 12) { + sprintf (buffer, "PM"); + } else { + sprintf (buffer, "AM"); + } + return; + case 'B': + strcpy (buffer, MonthName[month - 1]); + return; + case 'A': + strcpy (buffer, DayName[(4 + totDay) % 7]); + return; + case 'b': + case 'h': + strcpy (buffer, MonthName[month - 1]); + buffer[3] = '\0'; + return; + case 'a': + strcpy (buffer, DayName[(4 + totDay) % 7]); + buffer[3] = '\0'; + return; + case 'w': + sprintf (buffer, "%d", (int) ((4 + totDay) % 7)); + return; + case 'j': + sprintf (buffer, "%03d", day + 1); + return; + case 'e': + dy = (Clock_NumDay (month, 1, year, 1) - 1); + sprintf (buffer, "%d", (int) (day - dy)); + return; + case 'W': + i = (1 - ((4 + totDay - day) % 7)) % 7; + if (day < i) + sprintf (buffer, "00"); + else + sprintf (buffer, "%02d", ((day - i) / 7) + 1); + return; + case 'U': + i = (-((4 + totDay - day) % 7)) % 7; + if (day < i) + sprintf (buffer, "00"); + else + sprintf (buffer, "%02d", ((day - i) / 7) + 1); + return; + case 'D': + Clock_FormatParse (buffer, sec, floatSec, totDay, year, month, + day, 'm'); + strcat (buffer, "/"); + Clock_FormatParse (temp, sec, floatSec, totDay, year, month, + day, 'd'); + strcat (buffer, temp); + strcat (buffer, "/"); + Clock_FormatParse (temp, sec, floatSec, totDay, year, month, + day, 'Y'); + strcat (buffer, temp); + return; + case 'T': + Clock_FormatParse (buffer, sec, floatSec, totDay, year, month, + day, 'H'); + strcat (buffer, ":"); + Clock_FormatParse (temp, sec, floatSec, totDay, year, month, + day, 'M'); + strcat (buffer, temp); + strcat (buffer, ":"); + Clock_FormatParse (temp, sec, floatSec, totDay, year, month, + day, 'S'); + strcat (buffer, temp); + return; + case 'r': + Clock_FormatParse (buffer, sec, floatSec, totDay, year, month, + day, 'I'); + strcat (buffer, ":"); + Clock_FormatParse (temp, sec, floatSec, totDay, year, month, + day, 'M'); + strcat (buffer, temp); + strcat (buffer, ":"); + Clock_FormatParse (temp, sec, floatSec, totDay, year, month, + day, 'S'); + strcat (buffer, temp); + strcat (buffer, " "); + Clock_FormatParse (temp, sec, floatSec, totDay, year, month, + day, 'p'); + strcat (buffer, temp); + return; + case 'R': + Clock_FormatParse (buffer, sec, floatSec, totDay, year, month, + day, 'H'); + strcat (buffer, ":"); + Clock_FormatParse (temp, sec, floatSec, totDay, year, month, + day, 'M'); + strcat (buffer, temp); + return; + + /* If the current day is a federal holiday, then return a pointer to + * the appropriate holiday string (e.g., "Martin Luther King Day") */ + case 'v': + /* Clock_FormatParse 'd' */ + dy = (Clock_NumDay (month, 1, year, 1) - 1); + DOM = day - dy; + /* Clock_FormatParse 'w' */ + DOW = (int) ((4 + totDay) % 7); + + if ((DOM % 7) != 1) { + monthStartDOW = DOW - ((DOM % 7) - 1); + if (monthStartDOW < 0) { + monthStartDOW = 7 + monthStartDOW; + } + } else { + monthStartDOW = DOW; + } + + Clock_Holiday (month, DOM, monthStartDOW, temp); + if (temp[0] != '\0') { + strcpy (buffer, temp); + } else { + Clock_FormatParse (buffer, sec, floatSec, totDay, year, month, + day, 'A'); + } + return; + default: + sprintf (buffer, "unknown %c", format); + return; + } +} + +/***************************************************************************** + * Clock_GetTimeZone() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Returns the time zone offset in hours to add to local time to get UTC. + * So EST is +5 not -5. + * + * ARGUMENTS + * + * RETURNS: int + * + * HISTORY + * 6/2004 Arthur Taylor (MDL): Created. + * 3/2005 AAT: Found bug... Used to use 1/1/1970 00Z and find the local + * hour. If CET, this means use 1969 date, which causes it to die. + * Switched to 1/2/1970 00Z. + * 3/2005 AAT: timeZone (see CET) can be < 0. don't add 24 if timeZone < 0 + * + * NOTES + ***************************************************************************** + */ +sChar Clock_GetTimeZone () +{ + struct tm time; + time_t ansTime; + struct tm *gmTime; + static int timeZone = 9999; + + if (timeZone == 9999) { + /* Cheap method of getting global time_zone variable. */ + memset (&time, 0, sizeof (struct tm)); + time.tm_year = 70; + time.tm_mday = 2; + ansTime = mktime (&time); + gmTime = gmtime (&ansTime); + timeZone = gmTime->tm_hour; + if (gmTime->tm_mday != 2) { + timeZone -= 24; + } + } + return timeZone; +} + +/***************************************************************************** + * Clock_IsDaylightSaving() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To determine if daylight savings is in effect. Daylight savings is in + * effect from the first sunday in April to the last sunday in October. + * At 2 AM ST (or 3 AM DT) in April -> 3 AM DT (and we return 1) + * At 2 AM DT (or 1 AM ST) in October -> 1 AM ST (and we return 0) + * + * ARGUMENTS + * clock = The time stored as a double. (Input) + * TimeZone = hours to add to local time to get UTC. (Input) + * + * RETURNS: int + * 0 if not in daylight savings time. + * 1 if in daylight savings time. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 6/2004 AAT (MDL): Updated. + * + * NOTES + ***************************************************************************** + */ +int Clock_IsDaylightSaving2 (double clock, sChar TimeZone) +{ + sInt4 totDay, year; + int day, first; + double secs; + + clock = clock - TimeZone * 3600.; + /* Clock should now be in Standard Time, so comparisons later have to be + * based on Standard Time. */ + + totDay = (sInt4) floor (clock / SEC_DAY); + Clock_Epoch2YearDay (totDay, &day, &year); + /* Figure out number of seconds since beginning of year. */ + secs = clock - (totDay - day) * SEC_DAY; + + /* figure out if 1/1/year is mon/tue/.../sun */ + first = ((4 + (totDay - day)) % 7); /* -day should get 1/1 but may need + * -day+1 => sun == 0, ... sat == 6 */ + + /* figure out if year is a leap year */ + if (((year % 4) == 0) && (((year % 100) != 0) || ((year % 400) == 0))) { + /* look up extents of daylight savings. for leap year. */ + switch (first) { + case 0: + if ((secs >= 7869600.0) && (secs <= 26010000.0)) + return 1; + else + return 0; + case 1: + if ((secs >= 8388000.0) && (secs <= 25923600.0)) + return 1; + else + return 0; + case 2: + if ((secs >= 8301600.0) && (secs <= 25837200.0)) + return 1; + else + return 0; + case 3: + if ((secs >= 8215200.0) && (secs <= 25750800.0)) + return 1; + else + return 0; + case 4: + if ((secs >= 8128800.0) && (secs <= 26269200.0)) + return 1; + else + return 0; + case 5: + if ((secs >= 8042400.0) && (secs <= 26182800.0)) + return 1; + else + return 0; + case 6: + if ((secs >= 7956000.0) && (secs <= 26096400.0)) + return 1; + else + return 0; + } + } else { + switch (first) { + case 0: + if ((secs >= 7869600.0) && (secs <= 26010000.0)) + return 1; + else + return 0; + case 1: + if ((secs >= 7783200.0) && (secs <= 25923600.0)) + return 1; + else + return 0; + case 2: + if ((secs >= 8301600.0) && (secs <= 25837200.0)) + return 1; + else + return 0; + case 3: + if ((secs >= 8215200.0) && (secs <= 25750800.0)) + return 1; + else + return 0; + case 4: + if ((secs >= 8128800.0) && (secs <= 26664400.0)) + return 1; + else + return 0; + case 5: + if ((secs >= 8042400.0) && (secs <= 26182800.0)) + return 1; + else + return 0; + case 6: + if ((secs >= 7956000.0) && (secs <= 26096400.0)) + return 1; + else + return 0; + } + } + return 0; +} + +/***************************************************************************** + * Clock_PrintDate() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * + * ARGUMENTS + * clock = The time stored as a double. (Input) + * year = The year. (Output) + * month = The month. (Output) + * day = The day. (Output) + * hour = The hour. (Output) + * min = The min. (Output) + * sec = The second. (Output) + * + * RETURNS: void + * + * HISTORY + * 3/2005 Arthur Taylor (MDL): Commented. + * + * NOTES + ***************************************************************************** + */ +void Clock_PrintDate (double clock, sInt4 *year, int *month, int *day, + int *hour, int *min, double *sec) +{ + sInt4 totDay; + sInt4 intSec; + + totDay = (sInt4) floor (clock / SEC_DAY); + Clock_Epoch2YearDay (totDay, day, year); + *month = Clock_MonthNum (*day, *year); + *day = *day - Clock_NumDay (*month, 1, *year, 1) + 1; + *sec = clock - ((double) totDay) * SEC_DAY; + intSec = (sInt4) (*sec); + *hour = (int) ((intSec % 86400L) / 3600); + *min = (int) ((intSec % 3600) / 60); + *sec = (intSec % 60) + (*sec - intSec); +} + +/***************************************************************************** + * Clock_Print() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To create formated output from a time structure that is stored as a + * double. + * + * ARGUMENTS + * buffer = Destination to write the format to. (Output) + * n = The number of characters in buffer. (Input) + * clock = The time stored as a double. (Input) + * format = The desired output format. (Input) + * f_gmt = 0 output GMT, 1 output LDT, 2 output LST. (Input) + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 6/2004 AAT (MDL): Updated. + * + * NOTES + ***************************************************************************** + */ +void Clock_Print (char *buffer, int n, double clock, const char *format, + char f_gmt) +{ + sInt4 totDay, year; + sInt4 sec; + double floatSec; + int month, day; + size_t i; + int j; + char f_perc; + char locBuff[100]; + sChar timeZone; /* Hours to add to local time to get UTC. */ + + /* Handle gmt problems. */ + if (f_gmt != 0) { + timeZone = Clock_GetTimeZone (); + /* clock is currently in UTC */ + clock -= timeZone * 3600; + /* clock is now in local standard time Note: A 0 is passed to + * DaylightSavings so it converts from local to local standard time. */ + if ((f_gmt == 1) && (Clock_IsDaylightSaving2 (clock, 0) == 1)) { + clock = clock + 3600; + } + } + /* Convert from seconds to days and seconds. */ + totDay = (sInt4) floor (clock / SEC_DAY); + Clock_Epoch2YearDay (totDay, &day, &year); + month = Clock_MonthNum (day, year); + floatSec = clock - ((double) totDay) * SEC_DAY; + sec = (sInt4) floatSec; + floatSec = floatSec - sec; + + f_perc = 0; + j = 0; + for (i = 0; i < strlen (format); i++) { + if (j >= n) + return; + if (format[i] == '%') { + f_perc = 1; + } else { + if (f_perc == 0) { + buffer[j] = format[i]; + j++; + buffer[j] = '\0'; + } else { + Clock_FormatParse (locBuff, sec, floatSec, totDay, year, month, + day, format[i]); + buffer[j] = '\0'; + strncat (buffer, locBuff, n - j); + j += strlen (locBuff); + f_perc = 0; + } + } + } +} + +/***************************************************************************** + * Clock_Print2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To create formated output from a time structure that is stored as a + * double. This is similar to Clock_Print, except it bases the timezone + * shift on what the user supplies rather than the system timezone, and + * accepts a flag that indicates whether to inquire about daylight savings. + * If f_dayCheck, then it looks at the local time and see's if daylight is + * in effect. This allows for points where daylight is never in effect + * (f_dayCheck = 0). + * + * ARGUMENTS + * buffer = Destination to write the format to. (Output) + * n = The number of characters in buffer. (Input) + * clock = The time stored as a double (asumed in UTC). (Input) + * format = The desired output format. (Input) + * timeZone = Hours to add to local time to get UTC. (Input) + * f_dayCheck = True if we should check if daylight savings is in effect, + * after converting to LST. (Input) + * + * RETURNS: void + * + * HISTORY + * 1/2006 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +void Clock_Print2 (char *buffer, int n, double clock, char *format, + sChar timeZone, sChar f_dayCheck) +{ + sInt4 totDay, year; + sInt4 sec; + double floatSec; + int month, day; + size_t i; + int j; + char f_perc; + char locBuff[100]; + + /* clock is currently in UTC */ + clock -= timeZone * 3600; + /* clock is now in local standard time */ + if (f_dayCheck) { + /* Note: A 0 is passed to DaylightSavings so it converts from local to + * local standard time. */ + if (Clock_IsDaylightSaving2 (clock, 0) == 1) { + clock += 3600; + } + } + + /* Convert from seconds to days and seconds. */ + totDay = (sInt4) floor (clock / SEC_DAY); + Clock_Epoch2YearDay (totDay, &day, &year); + month = Clock_MonthNum (day, year); + floatSec = clock - ((double) totDay) * SEC_DAY; + sec = (sInt4) floatSec; + floatSec = floatSec - sec; + + f_perc = 0; + j = 0; + for (i = 0; i < strlen (format); i++) { + if (j >= n) + return; + if (format[i] == '%') { + f_perc = 1; + } else { + if (f_perc == 0) { + buffer[j] = format[i]; + j++; + buffer[j] = '\0'; + } else { + Clock_FormatParse (locBuff, sec, floatSec, totDay, year, month, + day, format[i]); + buffer[j] = '\0'; + strncat (buffer, locBuff, n - j); + j += strlen (locBuff); + f_perc = 0; + } + } + } +} + +/***************************************************************************** + * Clock_Clicks() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Returns the number of clicks since the program started execution. + * + * ARGUMENTS + * + * RETURNS: double + * Number of clicks since the beginning of the program. + * + * HISTORY + * 6/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +double Clock_Clicks (void) +{ + double ans; + + ans = (double) clock (); + return ans; +} + +/***************************************************************************** + * Clock_Seconds() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Returns the current number of seconds since the beginning of the epoch. + * Using the local system time zone. + * + * ARGUMENTS + * + * RETURNS: double + * Number of seconds since beginning of the epoch. + * + * HISTORY + * 6/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +int Clock_SetSeconds (double *time, sChar f_set) +{ + static double ans = 0; + static int f_ansSet = 0; + + if (f_set) { + ans = *time; + f_ansSet = 1; + } else if (f_ansSet) { + *time = ans; + } + return f_ansSet; +} + +double Clock_Seconds (void) +{ + double ans; + + if (Clock_SetSeconds (&ans, 0) == 0) { + ans = time (NULL); + } + return ans; +} + +/***************************************************************************** + * Clock_PrintZone() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Prints the time zone based on the shift from UTC and if it is daylight + * savings or not. + * + * ARGUMENTS + * ptr = The character string to scan. (Output) + * TimeZone = Hours to add to local time to get UTC. (Input) + * f_day = True if we are dealing with daylight savings. (Input) + * + * RETURNS: int + * 0 if we read TimeZone, -1 if not. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 6/2004 AAT (MDL): Updated. + * + * NOTES + ***************************************************************************** + */ +int Clock_PrintZone2 (char *ptr, sChar TimeZone, char f_day) +{ + if (TimeZone == 0) { + sprintf (ptr, "UTC"); + return 0; + } else if (TimeZone == 5) { + if (f_day) { + sprintf (ptr, "EDT"); + } else { + sprintf (ptr, "EST"); + } + return 0; + } else if (TimeZone == 6) { + if (f_day) { + sprintf (ptr, "CDT"); + } else { + sprintf (ptr, "CST"); + } + return 0; + } else if (TimeZone == 7) { + if (f_day) { + sprintf (ptr, "MDT"); + } else { + sprintf (ptr, "MST"); + } + return 0; + } else if (TimeZone == 8) { + if (f_day) { + sprintf (ptr, "PDT"); + } else { + sprintf (ptr, "PST"); + } + return 0; + } else if (TimeZone == 9) { + if (f_day) { + sprintf (ptr, "YDT"); + } else { + sprintf (ptr, "YST"); + } + return 0; + } + ptr[0] = '\0'; + return -1; +} + +/***************************************************************************** + * Clock_ScanZone() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Scans a character string to determine the timezone. + * + * ARGUMENTS + * ptr = The character string to scan. (Input) + * TimeZone = Hours to add to local time to get UTC. (Output) + * f_day = True if we are dealing with daylight savings. (Output) + * + * RETURNS: int + * 0 if we read TimeZone, -1 if not. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 6/2004 AAT (MDL): Updated. + * + * NOTES + ***************************************************************************** + */ +int Clock_ScanZone2 (char *ptr, sChar *TimeZone, char *f_day) +{ + switch (ptr[0]) { + case 'G': + if (strcmp (ptr, "GMT") == 0) { + *f_day = 0; + *TimeZone = 0; + return 0; + } + return -1; + case 'U': + if (strcmp (ptr, "UTC") == 0) { + *f_day = 0; + *TimeZone = 0; + return 0; + } + return -1; + case 'E': + if (strcmp (ptr, "EDT") == 0) { + *f_day = 1; + *TimeZone = 5; + return 0; + } else if (strcmp (ptr, "EST") == 0) { + *f_day = 0; + *TimeZone = 5; + return 0; + } + return -1; + case 'C': + if (strcmp (ptr, "CDT") == 0) { + *f_day = 1; + *TimeZone = 6; + return 0; + } else if (strcmp (ptr, "CST") == 0) { + *f_day = 0; + *TimeZone = 6; + return 0; + } + return -1; + case 'M': + if (strcmp (ptr, "MDT") == 0) { + *f_day = 1; + *TimeZone = 7; + return 0; + } else if (strcmp (ptr, "MST") == 0) { + *f_day = 0; + *TimeZone = 7; + return 0; + } + return -1; + case 'P': + if (strcmp (ptr, "PDT") == 0) { + *f_day = 1; + *TimeZone = 8; + return 0; + } else if (strcmp (ptr, "PST") == 0) { + *f_day = 0; + *TimeZone = 8; + return 0; + } + return -1; + case 'Y': + if (strcmp (ptr, "YDT") == 0) { + *f_day = 1; + *TimeZone = 9; + return 0; + } else if (strcmp (ptr, "YST") == 0) { + *f_day = 0; + *TimeZone = 9; + return 0; + } + return -1; + case 'Z': + if (strcmp (ptr, "Z") == 0) { + *f_day = 0; + *TimeZone = 0; + return 0; + } + return -1; + } + return -1; +} + +/***************************************************************************** + * Clock_ScanMonth() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Scans a string looking for a month word. Assumes string is all caps. + * + * ARGUMENTS + * ptr = The character string to scan. (Input) + * + * RETURNS: int + * Returns the month number read, or -1 if no month word seen. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 6/2004 AAT (MDL): Updated. + * + * NOTES + ***************************************************************************** + */ +int Clock_ScanMonth (char *ptr) +{ + switch (*ptr) { + case 'A': + if ((strcmp (ptr, "APR") == 0) || (strcmp (ptr, "APRIL") == 0)) + return 4; + else if ((strcmp (ptr, "AUG") == 0) || (strcmp (ptr, "AUGUST") == 0)) + return 8; + return -1; + case 'D': + if ((strcmp (ptr, "DEC") == 0) || (strcmp (ptr, "DECEMBER") == 0)) + return 12; + return -1; + case 'F': + if ((strcmp (ptr, "FEB") == 0) || (strcmp (ptr, "FEBRUARY") == 0)) + return 2; + return -1; + case 'J': + if ((strcmp (ptr, "JAN") == 0) || (strcmp (ptr, "JANUARY") == 0)) + return 1; + else if ((strcmp (ptr, "JUN") == 0) || (strcmp (ptr, "JUNE") == 0)) + return 6; + else if ((strcmp (ptr, "JUL") == 0) || (strcmp (ptr, "JULY") == 0)) + return 7; + return -1; + case 'M': + if ((strcmp (ptr, "MAR") == 0) || (strcmp (ptr, "MARCH") == 0)) + return 3; + else if (strcmp (ptr, "MAY") == 0) + return 5; + return -1; + case 'N': + if ((strcmp (ptr, "NOV") == 0) || (strcmp (ptr, "NOVEMBER") == 0)) + return 11; + return -1; + case 'O': + if ((strcmp (ptr, "OCT") == 0) || (strcmp (ptr, "OCTOBER") == 0)) + return 10; + return -1; + case 'S': + if ((strcmp (ptr, "SEP") == 0) || (strcmp (ptr, "SEPTEMBER") == 0)) + return 9; + return -1; + } + return -1; +} + +/***************************************************************************** + * Clock_PrintMonth3() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * + * ARGUMENTS + * + * RETURNS: void + * + * HISTORY + * 3/2005 Arthur Taylor (MDL/RSIS): Commented. + * + * NOTES + ***************************************************************************** + */ +void Clock_PrintMonth3 (int mon, char *buffer, CPL_UNUSED int buffLen) +{ + static char *MonthName[] = { + "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", + "NOV", "DEC" + }; + myAssert ((mon > 0) && (mon < 13)); + myAssert (buffLen > 3); + strcpy (buffer, MonthName[mon - 1]); +} + +/***************************************************************************** + * Clock_PrintMonth() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * + * ARGUMENTS + * + * RETURNS: void + * + * HISTORY + * 3/2005 Arthur Taylor (MDL/RSIS): Commented. + * + * NOTES + ***************************************************************************** + */ +void Clock_PrintMonth (int mon, char *buffer, CPL_UNUSED int buffLen) +{ + static char *MonthName[] = { + "January", "February", "March", "April", "May", "June", "July", + "August", "September", "October", "November", "December" + }; + myAssert ((mon > 0) && (mon < 13)); + myAssert (buffLen > 9); + strcpy (buffer, MonthName[mon - 1]); +} + +/***************************************************************************** + * Clock_ScanWeekday() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Scans a string looking for a day word. Assumes string is all caps. + * + * ARGUMENTS + * ptr = The character string to scan. (Input) + * + * RETURNS: int + * Returns the day number read, or -1 if no day word seen. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 6/2004 AAT (MDL): Updated. + * + * NOTES + ***************************************************************************** + */ +static int Clock_ScanWeekday (char *ptr) +{ + switch (*ptr) { + case 'S': + if ((strcmp (ptr, "SUN") == 0) || (strcmp (ptr, "SUNDAY") == 0)) + return 0; + else if ((strcmp (ptr, "SAT") == 0) || + (strcmp (ptr, "SATURDAY") == 0)) + return 6; + return -1; + case 'M': + if ((strcmp (ptr, "MON") == 0) || (strcmp (ptr, "MONDAY") == 0)) + return 1; + return -1; + case 'T': + if ((strcmp (ptr, "TUE") == 0) || (strcmp (ptr, "TUESDAY") == 0)) + return 2; + else if ((strcmp (ptr, "THU") == 0) || + (strcmp (ptr, "THURSDAY") == 0)) + return 4; + return -1; + case 'W': + if ((strcmp (ptr, "WED") == 0) || (strcmp (ptr, "WEDNESDAY") == 0)) + return 3; + return -1; + case 'F': + if ((strcmp (ptr, "FRI") == 0) || (strcmp (ptr, "FRIDAY") == 0)) + return 5; + return -1; + } + return -1; +} + +/***************************************************************************** + * Clock_ScanColon() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Parses a word assuming it is : separated and is dealing with + * hours:minutes:seconds or hours:minutes. Returns the resulting time as + * a double. + * + * ARGUMENTS + * ptr = The character string to scan. (Input) + * + * RETURNS: double + * The time after converting the : separated string. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 6/2004 AAT (MDL): Updated. + * + * NOTES + ***************************************************************************** + */ +static double Clock_ScanColon (char *ptr) +{ + sInt4 hour, min; + double sec; + char *ptr3; + + ptr3 = strchr (ptr, ':'); + *ptr3 = '\0'; + hour = atoi (ptr); + *ptr3 = ':'; + ptr = ptr3 + 1; + /* Check for second :, other wise it is hh:mm */ + if ((ptr3 = strchr (ptr, ':')) == NULL) { + min = atoi (ptr); + sec = 0; + } else { + *ptr3 = '\0'; + min = atoi (ptr); + *ptr3 = ':'; + ptr = ptr3 + 1; + sec = atof (ptr); + } + return (sec + 60 * min + 3600 * hour); +} + +/***************************************************************************** + * Clock_ScanSlash() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Parses a word assuming it is / separated and is dealing with + * months/days/years or months/days. + * + * ARGUMENTS + * word = The character string to scan. (Input) + * mon = The month that was seen. (Output) + * day = The day that was seen. (Output) + * year = The year that was seen. (Output) + * f_year = True if the year is valid. (Output) + * + * RETURNS: int + * -1 if mon or day is out of range. + * 0 if no problems. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 6/2004 AAT (MDL): Updated. + * + * NOTES + ***************************************************************************** + */ +static int Clock_ScanSlash (char *word, int *mon, int *day, sInt4 *year, + char *f_year) +{ + char *ptr3; + char *ptr = word; + + ptr3 = strchr (ptr, '/'); + *ptr3 = '\0'; + *mon = atoi (ptr); + *ptr3 = '/'; + ptr = ptr3 + 1; + /* Check for second /, other wise it is mm/dd */ + if ((ptr3 = strchr (ptr, '/')) == NULL) { + *day = atoi (ptr); + *year = 1970; + *f_year = 0; + } else { + *ptr3 = '\0'; + *day = atoi (ptr); + *ptr3 = '/'; + ptr = ptr3 + 1; + *year = atoi (ptr); + *f_year = 1; + } + if ((*mon < 1) || (*mon > 12) || (*day < 1) || (*day > 31)) { + printf ("Errors parsing %s\n", word); + return -1; + } + return 0; +} + +/* http://www.w3.org/TR/NOTE-datetime + Year and month: + YYYY-MM (eg 1997-07) + Complete date: + YYYY-MM-DD (eg 1997-07-16) + Complete date plus hours and minutes: + YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00) + Complete date plus hours, minutes and seconds: + YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00) + Complete date plus hours, minutes, seconds and a decimal fraction of a +second + YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00) + +Example: +1994-11-05T08:15:30-05:00 corresponds to November 5, 1994, 8:15:30 am, + US Eastern Standard Time. +1994-11-05T13:15:30Z corresponds to the same instant. +*/ +static int Clock_ScanDash (char *word, int *mon, int *day, sInt4 *year, + double *time, char *f_time) +{ + char *ptr3; + char *ptr = word; + sInt4 hour, min; + double sec; + char temp; + sInt4 offset; + + ptr3 = strchr (ptr, '-'); + *ptr3 = '\0'; + *year = atoi (ptr); + *ptr3 = '-'; + ptr = ptr3 + 1; + /* Check for second -, other wise it is yyyy-mm */ + if ((ptr3 = strchr (ptr, '-')) == NULL) { + /* Don't touch time or f_time */ + *mon = atoi (ptr); + *day = 1; + if ((*mon < 1) || (*mon > 12)) { + printf ("Errors parsing %s\n", word); + return -1; + } + return 0; + } + *ptr3 = '\0'; + *mon = atoi (ptr); + *ptr3 = '-'; + ptr = ptr3 + 1; + if ((ptr3 = strchr (ptr, 'T')) == NULL) { + /* Don't touch time or f_time */ + *day = atoi (ptr); + if ((*mon < 1) || (*mon > 12) || (*day < 1) || (*day > 31)) { + printf ("Errors parsing %s\n", word); + return -1; + } + return 0; + } + *ptr3 = '\0'; + *day = atoi (ptr); + *ptr3 = 'T'; + ptr = ptr3 + 1; + /* hh:mmTZD */ + /* hh:mm:ssTZD */ + /* hh:mm:ss.sTZD */ + if (strlen (ptr) < 5) { + printf ("Errors parsing %s\n", word); + return -1; + } + ptr[2] = '\0'; + hour = atoi (ptr); + ptr[2] = ':'; + ptr += 3; + offset = 0; + sec = 0; + if (strlen (ptr) == 2) { + min = atoi (ptr); + } else { + temp = ptr[2]; + ptr[2] = '\0'; + min = atoi (ptr); + ptr[2] = temp; + if (temp == ':') { + ptr += 3; + if ((ptr3 = strchr (ptr, '+')) == NULL) { + if ((ptr3 = strchr (ptr, '-')) == NULL) { + ptr3 = strchr (ptr, 'Z'); + } + } + if (ptr3 == NULL) { + sec = atof (ptr); + } else { + temp = *ptr3; + *ptr3 = '\0'; + sec = atof (ptr); + *ptr3 = temp; + if (temp != 'Z') { + ptr = ptr3; + ptr[3] = '\0'; + offset = atoi (ptr) * 3600; + ptr[3] = ':'; + ptr += 4; + offset += atoi (ptr) * 60; + } + } + } else if (temp != 'Z') { + ptr += 2; + ptr[3] = '\0'; + offset = atoi (ptr) * 3600; + ptr[3] = ':'; + ptr += 4; + offset += atoi (ptr) * 60; + } + } + *f_time = 1; + *time = sec + min * 60 + hour * 3600 - offset; + return 0; +} + +/***************************************************************************** + * Clock_ScanDate() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * + * ARGUMENTS + * + * RETURNS: void + * + * HISTORY + * 3/2005 Arthur Taylor (MDL/RSIS): Commented. + * + * NOTES + ***************************************************************************** + */ +/* prj::slosh prj::stm2trk and prj::degrib use this with clock zero'ed + out, so I have now made sure clock is zero'ed. */ +void Clock_ScanDate (double *clock, sInt4 year, int mon, int day) +{ + int i; + sInt4 delt, temp, totDay; + + myAssert ((mon >= 1) && (mon <= 12)); + + /* Makes sure clock is zero'ed out. */ + *clock = 0; + + if ((mon < 1) || (mon > 12) || (day < 0) || (day > 31)) + return; + totDay = Clock_NumDay (mon, day, year, 0); + if (day > totDay) + return; + totDay = Clock_NumDay (mon, day, year, 1); + temp = 1970; + delt = year - temp; + if ((delt >= 400) || (delt <= -400)) { + i = (delt / 400); + temp += 400 * i; + totDay += 146097L * i; + } + if (temp < year) { + while (temp < year) { + if (((temp % 4) == 0) && + (((temp % 100) != 0) || ((temp % 400) == 0))) { + if ((temp + 4) < year) { + totDay += 1461; + temp += 4; + } else if ((temp + 3) < year) { + totDay += 1096; + temp += 3; + } else if ((temp + 2) < year) { + totDay += 731; + temp += 2; + } else { + totDay += 366; + temp++; + } + } else { + totDay += 365; + temp++; + } + } + } else if (temp > year) { + while (temp > year) { + temp--; + if (((temp % 4) == 0) && + (((temp % 100) != 0) || ((temp % 400) == 0))) { + if (year < temp - 3) { + totDay -= 1461; + temp -= 3; + } else if (year < (temp - 2)) { + totDay -= 1096; + temp -= 2; + } else if (year < (temp - 1)) { + totDay -= 731; + temp--; + } else { + totDay -= 366; + } + } else { + totDay -= 365; + } + } + } + *clock = *clock + ((double) (totDay)) * 24 * 3600; +} + +int Clock_ScanDateNumber (double *clock, char *buffer) +{ + int buffLen = strlen (buffer); + sInt4 year; + int mon = 1; + int day = 1; + int hour = 0; + int min = 0; + int sec = 0; + char c_temp; + + *clock = 0; + if ((buffLen != 4) && (buffLen != 6) && (buffLen != 8) && + (buffLen != 10) && (buffLen != 12) && (buffLen != 14)) { + return 1; + } + c_temp = buffer[4]; + buffer[4] = '\0'; + year = atoi (buffer); + buffer[4] = c_temp; + if (buffLen > 4) { + c_temp = buffer[6]; + buffer[6] = '\0'; + mon = atoi (buffer + 4); + buffer[6] = c_temp; + } + if (buffLen > 6) { + c_temp = buffer[8]; + buffer[8] = '\0'; + day = atoi (buffer + 6); + buffer[8] = c_temp; + } + if (buffLen > 8) { + c_temp = buffer[10]; + buffer[10] = '\0'; + hour = atoi (buffer + 8); + buffer[10] = c_temp; + } + if (buffLen > 10) { + c_temp = buffer[12]; + buffer[12] = '\0'; + min = atoi (buffer + 10); + buffer[12] = c_temp; + } + if (buffLen > 12) { + c_temp = buffer[14]; + buffer[14] = '\0'; + sec = atoi (buffer + 12); + buffer[14] = c_temp; + } + Clock_ScanDate (clock, year, mon, day); + *clock = *clock + sec + min * 60 + hour * 3600; + return 0; +} + +void Clock_PrintDateNumber (double clock, char buffer[15]) +{ + sInt4 year; + int month, day, hour, min, sec; + double d_sec; + + Clock_PrintDate (clock, &year, &month, &day, &hour, &min, &d_sec); + sec = (int) d_sec; + sprintf (buffer, "%04d%02d%02d%02d%02d%02d", year, month, day, hour, min, + sec); +} + +/* Word_types: none, ':' word, '/' word, '-' word, integer word, 'AM'/'PM' + * word, timeZone word, month word, weekDay word, Preceeder to a relativeDate + * word, Postceeder to a relativeDate word, relativeDate word unit, Adjust Day + * word + */ +enum { + WT_NONE, WT_COLON, WT_SLASH, WT_DASH, WT_INTEGER, WT_AMPM, WT_TIMEZONE, + WT_MONTH, WT_DAY, WT_PRE_RELATIVE, WT_POST_RELATIVE, WT_RELATIVE_UNIT, + WT_ADJDAY +}; + +/***************************************************************************** + * Clock_GetWord() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * + * ARGUMENTS + * + * RETURNS: void + * + * HISTORY + * 3/2005 Arthur Taylor (MDL/RSIS): Commented. + * + * NOTES + ***************************************************************************** + */ +/* Start at *Start. Advance Start until it is at first non-space, + * non-',' non-'.' character. Move End to first space, ',' or '.' after + * new Start location. Copy upto 30 characters (in caps) into word. */ +/* return -1 if no next word, 0 otherwise */ +static int Clock_GetWord (char **Start, char **End, char word[30], + int *wordType) +{ + char *ptr; + int cnt; + int f_integer; + + *wordType = WT_NONE; + if (*Start == NULL) { + return -1; + } + ptr = *Start; + /* Find start of next word (first non-space non-',' non-'.' char.) */ + while ((*ptr == ' ') || (*ptr == ',') || (*ptr == '.')) { + ptr++; + } + /* There is no next word. */ + if (*ptr == '\0') { + return -1; + } + *Start = ptr; + /* Find end of next word. */ + cnt = 0; + f_integer = 1; + while ((*ptr != ' ') && (*ptr != ',') && (*ptr != '\0')) { + if (cnt < 29) { + word[cnt] = (char) toupper (*ptr); + cnt++; + } + if (*ptr == ':') { + if (*wordType == WT_NONE) + *wordType = WT_COLON; + f_integer = 0; + } else if (*ptr == '/') { + if (*wordType == WT_NONE) + *wordType = WT_SLASH; + f_integer = 0; + } else if (*ptr == '-') { + if (ptr != *Start) { + if (*wordType == WT_NONE) + *wordType = WT_DASH; + f_integer = 0; + } + } else if (*ptr == '.') { + if (!isdigit (*(ptr + 1))) { + break; + } else { + f_integer = 0; + } + } else if (!isdigit (*ptr)) { + f_integer = 0; + } + ptr++; + } + word[cnt] = '\0'; + *End = ptr; + if (f_integer) { + *wordType = WT_INTEGER; + } + return 0; +} + +typedef struct { + sInt4 val; + int len; /* read from len char string? */ +} stackType; + +typedef struct { + int relUnit; + int f_negate; + int amount; +} relType; + +/***************************************************************************** + * Clock_Scan() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * + * ARGUMENTS + * + * RETURNS: void + * + * HISTORY + * + * NOTES + * * f_gmt == 0 no adjust, 1 adjust as LDT, 2 adjust as LST * + * Adjusted from: + * if ((f_gmt == 2) && (Clock_IsDaylightSaving2 (*clock, 0) == 1)) { + * to: + * if ((f_gmt == 1) && (Clock_IsDaylightSaving2 (*clock, 0) == 1)) { + + ***************************************************************************** + */ +int Clock_Scan (double *clock, char *buffer, char f_gmt) +{ + char *ptr, *ptr2; + char *ptr3; + char word[30]; + int wordType; + int lastWordType; + sChar TimeZone = Clock_GetTimeZone (); + /* hours to add to local time to get UTC. */ + char f_dayLight = 0; + int month = 0; + int day; + sInt4 year; + char f_year = 0; + int index; + int ans; + stackType *Stack = NULL; + relType *Rel = NULL; + int lenRel = 0; + int lenStack = 0; + static char *PreRel[] = { "LAST", "THIS", "NEXT", NULL }; + static char *RelUnit[] = { + "YEAR", "YEARS", "MONTH", "MONTHS", "FORTNIGHT", "FORTNIGHTS", "WEEK", + "WEEKS", "DAY", "DAYS", "HOUR", "HOURS", "MIN", "MINS", "MINUTE", + "MINUTES", "SEC", "SECS", "SECOND", "SECONDS", NULL + }; + static char *AdjDay[] = { "YESTERDAY", "TODAY", "TOMORROW", NULL }; + sChar f_ampm = -1; + char f_timeZone = 0; + char f_time = 0; + /* char f_date = 0; */ + char f_slashWord = 0; + char f_dateWord = 0; + char f_monthWord = 0; + char f_dayWord = 0; + double curTime; + sInt4 sec; + int i; + int monthAdj; + int yearAdj; + + /* Check that they gave us a string */ + ptr = buffer; + if (*ptr == '\0') + return 0; + + f_time = 0; + /* f_date = 0; */ + lastWordType = WT_NONE; + curTime = 0; + while (Clock_GetWord (&ptr, &ptr2, word, &wordType) == 0) { + if (wordType == WT_COLON) { + if (f_time) { + printf ("Detected multiple time pieces\n"); + goto errorReturn; + } + curTime = Clock_ScanColon (word); + f_time = 1; + } else if (wordType == WT_SLASH) { + if ((f_slashWord) || (f_dateWord)) { + printf ("Detected multiple date pieces\n"); + goto errorReturn; + } + Clock_ScanSlash (word, &month, &day, &year, &f_year); + f_slashWord = 1; + } else if (wordType == WT_DASH) { + if ((f_slashWord) || (f_dateWord)) { + printf ("Detected multiple date pieces\n"); + goto errorReturn; + } + Clock_ScanDash (word, &month, &day, &year, &curTime, &f_time); + f_year = 1; + f_slashWord = 1; + TimeZone = 0; + } else if (wordType == WT_INTEGER) { + lenStack++; + Stack = (stackType *) realloc ((void *) Stack, + lenStack * sizeof (stackType)); + Stack[lenStack - 1].val = atoi (word); + Stack[lenStack - 1].len = strlen (word); + } else if (strcmp (word, "AM") == 0) { + if (f_ampm != -1) { + printf ("Detected multiple am/pm\n"); + goto errorReturn; + } + f_ampm = 1; + wordType = WT_AMPM; + } else if (strcmp (word, "PM") == 0) { + if (f_ampm != -1) { + printf ("Detected multiple am/pm\n"); + goto errorReturn; + } + f_ampm = 2; + wordType = WT_AMPM; + } else if (Clock_ScanZone2 (word, &TimeZone, &f_dayLight) == 0) { + if (f_timeZone) { + printf ("Detected multiple time zones.\n"); + goto errorReturn; + } + if (f_dayLight == 0) { + f_gmt = 2; + } else { + f_gmt = 1; + } + f_timeZone = 1; + wordType = WT_TIMEZONE; + } else if ((index = Clock_ScanMonth (word)) != -1) { + if ((f_slashWord) || (f_monthWord)) { + printf ("Detected multiple months or already defined month.\n"); + goto errorReturn; + } + month = index; + /* Get the next word? First preserve the pointer */ + ptr3 = ptr2; + ptr = ptr2; + ans = Clock_GetWord (&ptr, &ptr2, word, &wordType); + if ((ans != 0) || (wordType != WT_INTEGER)) { + /* Next word not integer, so previous word is integral day. */ + if (lastWordType != WT_INTEGER) { + printf ("Problems with month word and finding the day.\n"); + goto errorReturn; + } + lenStack--; + day = Stack[lenStack].val; + /* Put the next word back under consideration. */ + wordType = WT_MONTH; + ptr2 = ptr3; + } else { + /* If word is trailed by comma, then it is day, and the next one + * is the year, otherwise it is a year, and the number before the + * month is the day. */ + if (*ptr2 == ',') { + day = atoi (word); + ptr = ptr2; + ans = Clock_GetWord (&ptr, &ptr2, word, &wordType); + if ((ans != 0) || (wordType != WT_INTEGER)) { + printf ("Couldn't find the year after the day.\n"); + goto errorReturn; + } + year = atoi (word); + f_year = 1; + } else { + year = atoi (word); + f_year = 1; + if (lastWordType != WT_INTEGER) { + printf ("Problems with month word and finding the day.\n"); + goto errorReturn; + } + lenStack--; + day = Stack[lenStack].val; + } + } + f_monthWord = 1; + f_dateWord = 1; + + /* Ignore the day of the week info? */ + } else if ((index = Clock_ScanWeekday (word)) != -1) { + if ((f_slashWord) || (f_dayWord)) { + printf ("Detected multiple day of week or already defined " + "day.\n"); + goto errorReturn; + } + wordType = WT_DAY; + f_dayWord = 1; + f_dateWord = 1; + } else if (GetIndexFromStr (word, PreRel, &index) != -1) { + wordType = WT_PRE_RELATIVE; + /* Next word must be a unit word. */ + ptr = ptr2; + if (Clock_GetWord (&ptr, &ptr2, word, &wordType) != 0) { + printf ("Couldn't get the next word after Pre-Relative time " + "word\n"); + goto errorReturn; + } + if (GetIndexFromStr (word, RelUnit, &ans) == -1) { + printf ("Couldn't get the Relative unit\n"); + goto errorReturn; + } + if (index != 1) { + lenRel++; + Rel = (relType *) realloc ((void *) Rel, + lenRel * sizeof (relType)); + Rel[lenRel - 1].relUnit = ans; + Rel[lenRel - 1].amount = 1; + if (index == 0) { + Rel[lenRel - 1].f_negate = 1; + } else { + Rel[lenRel - 1].f_negate = 0; + } + } + printf ("Pre Relative Word: %s %d\n", word, index); + + } else if (strcmp (word, "AGO") == 0) { + if ((lastWordType != WT_PRE_RELATIVE) || + (lastWordType != WT_RELATIVE_UNIT)) { + printf ("Ago did not follow relative words\n"); + goto errorReturn; + } + Rel[lenRel - 1].f_negate = 1; + wordType = WT_POST_RELATIVE; + } else if (GetIndexFromStr (word, RelUnit, &index) != -1) { + lenRel++; + Rel = (relType *) realloc ((void *) Rel, lenRel * sizeof (relType)); + Rel[lenRel - 1].relUnit = index; + Rel[lenRel - 1].amount = 1; + Rel[lenRel - 1].f_negate = 0; + if (lastWordType == WT_INTEGER) { + lenStack--; + Rel[lenRel - 1].amount = Stack[lenStack].val; + } + wordType = WT_RELATIVE_UNIT; + } else if (GetIndexFromStr (word, AdjDay, &index) != -1) { + if (index != 1) { + lenRel++; + Rel = (relType *) realloc ((void *) Rel, + lenRel * sizeof (relType)); + Rel[lenRel - 1].relUnit = 13; /* DAY in RelUnit list */ + Rel[lenRel - 1].amount = 1; + if (index == 0) { + Rel[lenRel - 1].f_negate = 1; + } else { + Rel[lenRel - 1].f_negate = 0; + } + } + wordType = WT_ADJDAY; + } else { + printf ("unknown: %s\n", word); + goto errorReturn; + } + ptr = ptr2; + lastWordType = wordType; + } + + /* Deal with time left on the integer stack. */ + if (lenStack > 1) { + printf ("Too many integers on the stack?\n"); + goto errorReturn; + } + if (lenStack == 1) { + if (Stack[0].val < 0) { + printf ("Unable to deduce a negative time?\n"); + goto errorReturn; + } + if (f_time) { + if (f_dateWord || f_slashWord) { + printf ("Already have date and time...\n"); + goto errorReturn; + } + if ((Stack[0].len == 6) || (Stack[0].len == 8)) { + year = Stack[0].val / 10000; + f_year = 1; + month = (Stack[0].val % 10000) / 100; + day = Stack[0].val % 100; + f_slashWord = 1; + if ((month < 1) || (month > 12) || (day < 1) || (day > 31)) { + printf ("Unable to deduce the integer value\n"); + return -1; + } + } else { + printf ("Unable to deduce the integer value\n"); + goto errorReturn; + } + } else { + if (Stack[0].len < 3) { + curTime = Stack[0].val * 3600; + f_time = 1; + } else if (Stack[0].len < 5) { + curTime = ((Stack[0].val / 100) * 3600. + + (Stack[0].val % 100) * 60.); + f_time = 1; + } else if ((Stack[0].len == 6) || (Stack[0].len == 8)) { + year = Stack[0].val / 10000; + f_year = 1; + month = (Stack[0].val % 10000) / 100; + day = Stack[0].val % 100; + f_slashWord = 1; + if ((month < 1) || (month > 12) || (day < 1) || (day > 31)) { + printf ("Unable to deduce the integer value\n"); + return -1; + } + } else { + printf ("Unable to deduce the time\n"); + goto errorReturn; + } + } + lenStack = 0; + } + if (!f_time) { + if (f_ampm != -1) { + printf ("Problems setting the time to 0\n"); + goto errorReturn; + } + curTime = 0; + } + if (f_ampm == 1) { + /* Adjust for 12 am */ + sec = (sInt4) (curTime - (floor (curTime / SEC_DAY)) * SEC_DAY); + if (((sec % 43200L) / 3600) == 0) { + curTime -= 43200L; + } + } else if (f_ampm == 2) { + /* Adjust for 12 pm */ + curTime += 43200L; + sec = (sInt4) (curTime - (floor (curTime / SEC_DAY)) * SEC_DAY); + if (((sec % 43200L) / 3600) == 0) { + curTime -= 43200L; + } + } + for (i = 0; i < lenRel; i++) { + if (Rel[i].f_negate) { + Rel[i].amount = -1 * Rel[i].amount; + } + } + /* Deal with adjustments by year or month. */ + if (f_dateWord || f_slashWord) { + /* Check if we don't have the year. */ + if (!f_year) { + *clock = Clock_Seconds (); + Clock_Epoch2YearDay ((sInt4) (floor (*clock / SEC_DAY)), &i, &year); + } + /* Deal with relative adjust by year and month. */ + for (i = 0; i < lenRel; i++) { + if ((Rel[i].relUnit == 0) || (Rel[i].relUnit == 1)) { + year += Rel[i].amount; + } else if ((Rel[i].relUnit == 2) || (Rel[i].relUnit == 3)) { + month += Rel[i].amount; + } + } + while (month < 1) { + year--; + month += 12; + } + while (month > 12) { + year++; + month -= 12; + } + *clock = 0; + Clock_ScanDate (clock, year, month, day); + + } else { + /* Pure Time words. */ + *clock = Clock_Seconds (); + /* round off to start of day */ + *clock = (floor (*clock / SEC_DAY)) * SEC_DAY; + /* Deal with relative adjust by year and month. */ + monthAdj = 0; + yearAdj = 0; + for (i = 0; i < lenRel; i++) { + if ((Rel[i].relUnit == 0) || (Rel[i].relUnit == 1)) { + if (Rel[i].f_negate) { + yearAdj -= Rel[i].amount; + } else { + yearAdj += Rel[i].amount; + } + } else if ((Rel[i].relUnit == 2) || (Rel[i].relUnit == 3)) { + if (Rel[i].f_negate) { + monthAdj -= Rel[i].amount; + } else { + monthAdj += Rel[i].amount; + } + } + } + if ((monthAdj != 0) || (yearAdj != 0)) { + /* Break clock into mon/day/year */ + Clock_Epoch2YearDay ((sInt4) (floor (*clock / SEC_DAY)), + &day, &year); + month = Clock_MonthNum (day, year); + day -= (Clock_NumDay (month, 1, year, 1) - 1); + month += monthAdj; + year += yearAdj; + while (month < 1) { + year--; + month += 12; + } + while (month > 12) { + year++; + month -= 12; + } + *clock = 0; + Clock_ScanDate (clock, year, month, day); + } + } + + /* Join the date and the time. */ + *clock += curTime; + + /* Finish the relative adjustments. */ + for (i = 0; i < lenRel; i++) { + switch (Rel[i].relUnit) { + case 3: /* Fortnight. */ + case 4: + *clock += (Rel[i].amount * 14 * 24 * 3600.); + break; + case 5: /* Week. */ + case 6: + *clock += (Rel[i].amount * 7 * 24 * 3600.); + break; + case 7: /* Day. */ + case 8: + *clock += (Rel[i].amount * 24 * 3600.); + break; + case 9: /* Hour. */ + case 10: + *clock += (Rel[i].amount * 3600.); + break; + case 11: /* Minute. */ + case 12: + case 13: + case 14: + *clock += (Rel[i].amount * 60.); + break; + case 15: /* Second. */ + case 16: + case 17: + case 18: + *clock += Rel[i].amount; + break; + } + } + + if (f_gmt != 0) { + /* IsDaylightSaving takes clock in GMT, and Timezone. */ + /* Note: A 0 is passed to DaylightSavings so it converts from LST to + * LST. */ + if ((f_gmt == 1) && (Clock_IsDaylightSaving2 (*clock, 0) == 1)) { + *clock = *clock - 3600; + } + /* Handle gmt problems. We are going from Local time to GMT so we add + * the TimeZone here. */ + *clock = *clock + TimeZone * 3600; + } + + free (Stack); + free (Rel); + return 0; + + errorReturn: + free (Stack); + free (Rel); + return -1; +} + +#ifdef CLOCK_PROGRAM +/* See clockstart.c */ +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/clock.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/clock.h new file mode 100644 index 000000000..0268701d8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/clock.h @@ -0,0 +1,42 @@ +#ifndef CLOCK_H +#define CLOCK_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "type.h" + +#define PERIOD_YEARS 146097L +#define SEC_DAY 86400L +#define ISLEAPYEAR(y) (((y)%400 == 0) || (((y)%4 == 0) && ((y)%100 != 0))) + +void Clock_Epoch2YearDay (sInt4 totDay, int *Day, sInt4 * Yr); +int Clock_MonthNum (int day, sInt4 year); +int Clock_NumDay (int month, int day, sInt4 year, char f_tot); +sChar Clock_GetTimeZone (); +int Clock_IsDaylightSaving2 (double clock, sChar TimeZone); +void Clock_PrintDate (double clock, sInt4 *year, int *month, int *day, + int *hour, int *min, double *sec); +void Clock_Print (char *buffer, int n, double clock, const char *format, + char f_gmt); +void Clock_Print2 (char *buffer, int n, double clock, char *format, + sChar timeZone, sChar f_dayCheck); +double Clock_Clicks (void); +int Clock_SetSeconds (double *time, sChar f_set); +double Clock_Seconds (void); +int Clock_PrintZone2 (char *ptr, sChar TimeZone, char f_day); +int Clock_ScanZone2 (char *ptr, sChar * TimeZone, char *f_day); +int Clock_ScanMonth (char *ptr); +void Clock_PrintMonth3 (int mon, char *buffer, int buffLen); +void Clock_PrintMonth (int mon, char *buffer, int buffLen); +void Clock_ScanDate (double *clock, sInt4 year, int mon, int day); +int Clock_ScanDateNumber (double *clock, char *buffer); +void Clock_PrintDateNumber (double clock, char buffer[15]); +int Clock_Scan (double *clock, char *buffer, char f_gmt); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CLOCK_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/datasource.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/datasource.h new file mode 100644 index 000000000..0eceb0727 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/datasource.h @@ -0,0 +1,18 @@ +#ifndef DATASOURCE_H +#define DATASOURCE_H + +#include + +class DataSource +{ +public: + virtual ~DataSource() {} + virtual size_t DataSourceFread(void* lpBuf, size_t size, size_t count) = 0; + virtual int DataSourceFgetc() = 0; + virtual int DataSourceUngetc(int c) = 0; + virtual int DataSourceFseek(long offset, int origin) = 0; + virtual int DataSourceFeof() = 0; + virtual long DataSourceFtell() = 0; +}; + +#endif /* DATASOURCE_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/degrib1.cpp b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/degrib1.cpp new file mode 100644 index 000000000..195344cff --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/degrib1.cpp @@ -0,0 +1,1973 @@ +/***************************************************************************** + * degrib1.c + * + * DESCRIPTION + * This file contains the main driver routines to unpack GRIB 1 files. + * + * HISTORY + * 4/2003 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + * GRIB 1 files are assumed to be big endian. + ***************************************************************************** + */ + +#include +#include +#include +#include +#include "degrib2.h" +#include "myerror.h" +#include "myassert.h" +#include "memendian.h" +#include "scan.h" +#include "degrib1.h" +#include "metaname.h" +#include "clock.h" +#include "cpl_error.h" + +/* default missing data value (see: bitmap GRIB1: sect3 and sect4) */ +/* UNDEFINED is default, UNDEFINED_PRIM is desired choice. */ +#define UNDEFINED 9.999e20 +#define UNDEFINED_PRIM 9999 + +#define GRIB_UNSIGN_INT3(a,b,c) ((a<<16)+(b<<8)+c) +#define GRIB_UNSIGN_INT2(a,b) ((a<<8)+b) +#define GRIB_SIGN_INT3(a,b,c) ((1-(int) ((unsigned) (a & 0x80) >> 6)) * (int) (((a & 127) << 16)+(b<<8)+c)) +#define GRIB_SIGN_INT2(a,b) ((1-(int) ((unsigned) (a & 0x80) >> 6)) * (int) (((a & 0x7f) << 8) + b)) + +/* various centers */ +#define NMC 7 +#define US_OTHER 9 +#define CPTEC 46 +/* Canada Center */ +#define CMC 54 +#define AFWA 57 +#define DWD 78 +#define ECMWF 98 +#define ATHENS 96 + +/* various subcenters */ +#define SUBCENTER_MDL 14 +#define SUBCENTER_TDL 11 + +/* The idea of rean or opn is to give a warning about default choice of + which table to use. */ +#define DEF_NCEP_TABLE rean_nowarn +enum Def_NCEP_Table { rean, opn, rean_nowarn, opn_nowarn }; + +extern GRIB1ParmTable parm_table_ncep_opn[256]; +extern GRIB1ParmTable parm_table_ncep_reanal[256]; +extern GRIB1ParmTable parm_table_ncep_tdl[256]; +extern GRIB1ParmTable parm_table_ncep_mdl[256]; +extern GRIB1ParmTable parm_table_omb[256]; +extern GRIB1ParmTable parm_table_nceptab_129[256]; +extern GRIB1ParmTable parm_table_nceptab_130[256]; +extern GRIB1ParmTable parm_table_nceptab_131[256]; + +extern GRIB1ParmTable parm_table_nohrsc[256]; + +extern GRIB1ParmTable parm_table_cptec_254[256]; + +extern GRIB1ParmTable parm_table_afwa_000[256]; +extern GRIB1ParmTable parm_table_afwa_001[256]; +extern GRIB1ParmTable parm_table_afwa_002[256]; +extern GRIB1ParmTable parm_table_afwa_003[256]; +extern GRIB1ParmTable parm_table_afwa_010[256]; +extern GRIB1ParmTable parm_table_afwa_011[256]; + +extern GRIB1ParmTable parm_table_dwd_002[256]; +extern GRIB1ParmTable parm_table_dwd_201[256]; +extern GRIB1ParmTable parm_table_dwd_202[256]; +extern GRIB1ParmTable parm_table_dwd_203[256]; + +extern GRIB1ParmTable parm_table_ecmwf_128[256]; +extern GRIB1ParmTable parm_table_ecmwf_129[256]; +extern GRIB1ParmTable parm_table_ecmwf_130[256]; +extern GRIB1ParmTable parm_table_ecmwf_131[256]; +extern GRIB1ParmTable parm_table_ecmwf_140[256]; +extern GRIB1ParmTable parm_table_ecmwf_150[256]; +extern GRIB1ParmTable parm_table_ecmwf_160[256]; +extern GRIB1ParmTable parm_table_ecmwf_170[256]; +extern GRIB1ParmTable parm_table_ecmwf_180[256]; + +extern GRIB1ParmTable parm_table_athens[256]; + +extern GRIB1ParmTable parm_table_cmc[256]; + +extern GRIB1ParmTable parm_table_undefined[256]; + +/***************************************************************************** + * Choose_ParmTable() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Chooses the correct Parameter table depending on what is in the GRIB1 + * message's "Product Definition Section". + * + * ARGUMENTS + * pdsMeta = The filled out pdsMeta data structure to base choice on. (Input) + * center = The Center that created the data (Input) + * subcenter = The Sub Center that created the data (Input) + * + * FILES/DATABASES: None + * + * RETURNS: ParmTable (appropriate parameter table.) + * + * HISTORY + * : wgrib library : cnames.c + * 4/2003 Arthur Taylor (MDL/RSIS): Modified + * 10/2005 AAT: Adjusted to take center, subcenter + * + * NOTES + ***************************************************************************** + */ +static GRIB1ParmTable *Choose_ParmTable (pdsG1Type *pdsMeta, + unsigned short int center, + unsigned short int subcenter) +{ + int process; /* The process ID from the GRIB1 message. */ + + switch (center) { + case NMC: + if (pdsMeta->mstrVersion <= 3) { + switch (subcenter) { + case 1: + return &parm_table_ncep_reanal[0]; + case SUBCENTER_TDL: + return &parm_table_ncep_tdl[0]; + case SUBCENTER_MDL: + return &parm_table_ncep_mdl[0]; + } + } + /* figure out if NCEP opn or reanalysis */ + switch (pdsMeta->mstrVersion) { + case 0: + return &parm_table_ncep_opn[0]; + case 1: + case 2: + process = pdsMeta->genProcess; + if ((subcenter != 0) || ((process != 80) && (process != 180))) { + return &parm_table_ncep_opn[0]; + } + /* At this point could be either the opn or reanalysis table */ + switch (DEF_NCEP_TABLE) { + case opn_nowarn: + return &parm_table_ncep_opn[0]; + case rean_nowarn: + return &parm_table_ncep_reanal[0]; + } + break; + case 3: + return &parm_table_ncep_opn[0]; + case 128: + return &parm_table_omb[0]; + case 129: + return &parm_table_nceptab_129[0]; + case 130: + return &parm_table_nceptab_130[0]; + case 131: + return &parm_table_nceptab_131[0]; + } + break; + case AFWA: + switch (subcenter) { + case 0: + return &parm_table_afwa_000[0]; + case 1: + case 4: + return &parm_table_afwa_001[0]; + case 2: + return &parm_table_afwa_002[0]; + case 3: + return &parm_table_afwa_003[0]; + case 10: + return &parm_table_afwa_010[0]; + case 11: + return &parm_table_afwa_011[0]; +/* case 5:*/ + /* Didn't have a table 5. */ + } + break; + case ECMWF: + switch (pdsMeta->mstrVersion) { + case 128: + return &parm_table_ecmwf_128[0]; + case 129: + return &parm_table_ecmwf_129[0]; + case 130: + return &parm_table_ecmwf_130[0]; + case 131: + return &parm_table_ecmwf_131[0]; + case 140: + return &parm_table_ecmwf_140[0]; + case 150: + return &parm_table_ecmwf_150[0]; + case 160: + return &parm_table_ecmwf_160[0]; + case 170: + return &parm_table_ecmwf_170[0]; + case 180: + return &parm_table_ecmwf_180[0]; + } + break; + case DWD: + switch (pdsMeta->mstrVersion) { + case 2: + return &parm_table_dwd_002[0]; + case 201: + return &parm_table_dwd_201[0]; + case 202: + return &parm_table_dwd_202[0]; + case 203: + return &parm_table_dwd_203[0]; + } + break; + case CPTEC: + switch (pdsMeta->mstrVersion) { + case 254: + return &parm_table_cptec_254[0]; + } + break; + case US_OTHER: + switch (subcenter) { + case 163: + return &parm_table_nohrsc[0]; + } + break; + case ATHENS: + return &parm_table_athens[0]; + break; + case CMC: + return &parm_table_cmc[0]; + break; + } + if ((pdsMeta->mstrVersion > 3) || (pdsMeta->cat > 127)) { + CPLDebug ( + "GRIB", + "Undefined parameter table (center %d-%d table %d).", + center, subcenter, pdsMeta->mstrVersion); + } + return &parm_table_undefined[0]; +} + +/***************************************************************************** + * GRIB1_Table2LookUp() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Returns the variable name (type of data) and comment (longer form of the + * name) for the data that is in the GRIB1 message. + * + * ARGUMENTS + * name = A pointer to the resulting short name. (Output) + * comment = A pointer to the resulting long name. (Output) + * pdsMeta = The filled out pdsMeta data structure to base choice on. (Input) + * center = The Center that created the data (Input) + * subcenter = The Sub Center that created the data (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created + * 10/2005 AAT: Adjusted to take center, subcenter + * + * NOTES + ***************************************************************************** + */ +static void GRIB1_Table2LookUp (pdsG1Type *pdsMeta, const char **name, + const char **comment, const char **unit, + int *convert, + unsigned short int center, + unsigned short int subcenter) +{ + GRIB1ParmTable *table; /* The parameter table choosen by the pdsMeta data */ + + table = Choose_ParmTable (pdsMeta, center, subcenter); + if ((center == NMC) && (pdsMeta->mstrVersion == 129) + && (pdsMeta->cat == 180)) { + if (pdsMeta->timeRange == 3) { + *name = "AVGOZCON"; + *comment = "Average Ozone Concentration"; + *unit = "PPB"; + *convert = UC_NONE; + return; + } + } + *name = table[pdsMeta->cat].name; + *comment = table[pdsMeta->cat].comment; + *unit = table[pdsMeta->cat].unit; + *convert = table[pdsMeta->cat].convert; +/* printf ("%s %s %s\n", *name, *comment, *unit);*/ +} + +extern GRIB1SurfTable GRIB1Surface[256]; + +/* Similar to metaname.c :: ParseLevelName() */ +static void GRIB1_Table3LookUp (pdsG1Type *pdsMeta, char **shortLevelName, + char **longLevelName) +{ + uChar type = pdsMeta->levelType; + uChar level1, level2; + + free (*shortLevelName); + *shortLevelName = NULL; + free (*longLevelName); + *longLevelName = NULL; + /* Find out if val is a 2 part value or not */ + if (GRIB1Surface[type].f_twoPart) { + level1 = (pdsMeta->levelVal >> 8); + level2 = (pdsMeta->levelVal & 0xff); + reallocSprintf (shortLevelName, "%d-%d-%s", level1, level2, + GRIB1Surface[type].name); + reallocSprintf (longLevelName, "%d-%d[%s] %s (%s)", level1, level2, + GRIB1Surface[type].unit, GRIB1Surface[type].name, + GRIB1Surface[type].comment); + } else { + reallocSprintf (shortLevelName, "%d-%s", pdsMeta->levelVal, + GRIB1Surface[type].name); + reallocSprintf (longLevelName, "%d[%s] %s (%s)", pdsMeta->levelVal, + GRIB1Surface[type].unit, GRIB1Surface[type].name, + GRIB1Surface[type].comment); + } +} + +/***************************************************************************** + * fval_360() -- + * + * Albion Taylor / ARL + * + * PURPOSE + * Converts an IBM360 floating point number to an IEEE floating point + * number. The IBM floating point spec represents the fraction as the last + * 3 bytes of the number, with 0xffffff being just shy of 1.0. The first byte + * leads with a sign bit, and the last seven bits represent the powers of 16 + * (not 2), with a bias of 0x40 giving 16^0. + * + * ARGUMENTS + * aval = A sInt4 containing the original IBM 360 number. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: double = the value that aval represents. + * + * HISTORY + * Albion Taylor (ARL): Created + * 4/2003 Arthur Taylor (MDL/RSIS): Cleaned up. + * 5/2003 AAT: some kind of Bug due to optimizations... + * -1055916032 => 0 instead of -1 + * + * NOTES + ***************************************************************************** + */ +static double fval_360 (uInt4 aval) +{ + short int ptr[4]; +#ifdef LITTLE_ENDIAN + ptr[3] = ((((aval >> 24) & 0x7f) << 2) + (0x3ff - 0x100)) << 4; + ptr[2] = 0; + ptr[1] = 0; + ptr[0] = 0; +#else + ptr[0] = ((((aval >> 24) & 0x7f) << 2) + (0x3ff - 0x100)) << 4; + ptr[1] = 0; + ptr[2] = 0; + ptr[3] = 0; +#endif + double pow16; + memcpy(&pow16, ptr, 8); + return ((aval & 0x80000000) ? -pow16 : pow16) * + (aval & 0xffffff) / ((double) 0x1000000); +} + +/***************************************************************************** + * ReadGrib1Sect1() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Parses the GRIB1 "Product Definition Section" or section 1, filling out + * the pdsMeta data structure. + * + * ARGUMENTS + * pds = The compressed part of the message dealing with "PDS". (Input) + * gribLen = The total length of the GRIB1 message. (Input) + * curLoc = Current location in the GRIB1 message. (Output) + * pdsMeta = The filled out pdsMeta data structure. (Output) + * f_gds = boolean if there is a Grid Definition Section. (Output) + * gridID = The Grid ID. (Output) + * f_bms = boolean if there is a Bitmap Section. (Output) + * DSF = Decimal Scale Factor for unpacking the data. (Output) + * center = The Center that created the data (Output) + * subcenter = The Sub Center that created the data (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = gribLen is too small. + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created. + * 5/2004 AAT: Paid attention to table 5 (Time range indicator) and which of + * P1 and P2 are the valid times. + * 10/2005 AAT: Adjusted to take center, subcenter + * + * NOTES + ***************************************************************************** + */ +static int ReadGrib1Sect1 (uChar *pds, uInt4 gribLen, uInt4 *curLoc, + pdsG1Type *pdsMeta, char *f_gds, uChar *gridID, + char *f_bms, short int *DSF, + unsigned short int *center, + unsigned short int *subcenter) +{ + sInt4 sectLen; /* Length in bytes of the current section. */ + int year; /* The year of the GRIB1 Message. */ + double P1_DeltaTime; /* Used to parse the time for P1 */ + double P2_DeltaTime; /* Used to parse the time for P2 */ + uInt4 uli_temp; +#ifdef DEBUG +/* + int i; +*/ +#endif + + sectLen = GRIB_UNSIGN_INT3 (*pds, pds[1], pds[2]); +#ifdef DEBUG +/* + printf ("Section 1 length = %ld\n", sectLen); + for (i = 0; i < sectLen; i++) { + printf ("Sect1: item %d = %d\n", i + 1, pds[i]); + } + printf ("Century is item 25\n"); +*/ +#endif + *curLoc += sectLen; + if (*curLoc > gribLen) { + errSprintf ("Ran out of data in PDS (GRIB 1 Section 1)\n"); + return -1; + } + pds += 3; + pdsMeta->mstrVersion = *(pds++); + *center = *(pds++); + pdsMeta->genProcess = *(pds++); + *gridID = *(pds++); + *f_gds = GRIB2BIT_1 & *pds; + *f_bms = GRIB2BIT_2 & *pds; + pds++; + pdsMeta->cat = *(pds++); + pdsMeta->levelType = *(pds++); + pdsMeta->levelVal = GRIB_UNSIGN_INT2 (*pds, pds[1]); + pds += 2; + if (*pds == 0) { + /* The 12 is because we have increased pds by 12. (but 25 is in + * reference of 1..25, so we need another -1) */ + year = (pds[25 - 13] * 100); + } else { + /* The 12 is because we have increased pds by 12. (but 25 is in + * reference of 1..25, so we need another -1) */ + year = *pds + ((pds[25 - 13] - 1) * 100); + + /* It seems like some old files (such as spring/I000176.grb) + do not have a century byte, and assum 19xx. */ +// if( (year < 1900 || year > 2100) && *pds >= 0 && *pds < 100 ) +// year = *pds + 1900; + } + + if (ParseTime (&(pdsMeta->refTime), year, pds[1], pds[2], pds[3], pds[4], + 0) != 0) { + preErrSprintf ("Error In call to ParseTime\n"); + errSprintf ("(Probably a corrupt file)\n"); + return -1; + } + pds += 5; + pdsMeta->timeRange = pds[3]; + if (ParseSect4Time2secV1 (pds[1], *pds, &P1_DeltaTime) == 0) { + pdsMeta->P1 = pdsMeta->refTime + P1_DeltaTime; + } else { + pdsMeta->P1 = pdsMeta->refTime; + printf ("Warning! : Can't figure out time unit of %d\n", *pds); + } + if (ParseSect4Time2secV1 (pds[2], *pds, &P2_DeltaTime) == 0) { + pdsMeta->P2 = pdsMeta->refTime + P2_DeltaTime; + } else { + pdsMeta->P2 = pdsMeta->refTime; + printf ("Warning! : Can't figure out time unit of %d\n", *pds); + } + /* The following is based on Table 5. */ + /* Note: For ensemble forecasts, 119 has meaning. */ + switch (pdsMeta->timeRange) { + case 0: + case 1: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + case 123: + case 124: + pdsMeta->validTime = pdsMeta->P1; + break; + case 2: + /* Puzzling case. */ + pdsMeta->validTime = pdsMeta->P2; + break; + case 3: + case 4: + case 5: + case 51: + pdsMeta->validTime = pdsMeta->P2; + break; + case 10: + if (ParseSect4Time2secV1 (GRIB_UNSIGN_INT2 (pds[1], pds[2]), *pds, + &P1_DeltaTime) == 0) { + pdsMeta->P2 = pdsMeta->P1 = pdsMeta->refTime + P1_DeltaTime; + } else { + pdsMeta->P2 = pdsMeta->P1 = pdsMeta->refTime; + printf ("Warning! : Can't figure out time unit of %d\n", *pds); + } + pdsMeta->validTime = pdsMeta->P1; + break; + default: + pdsMeta->validTime = pdsMeta->P1; + } + pds += 4; + pdsMeta->Average = GRIB_UNSIGN_INT2 (*pds, pds[1]); + pds += 2; + pdsMeta->numberMissing = *(pds++); + /* Skip over centry of reference time. */ + pds++; + *subcenter = *(pds++); + *DSF = GRIB_SIGN_INT2 (*pds, pds[1]); + pds += 2; + pdsMeta->f_hasEns = 0; + pdsMeta->f_hasProb = 0; + pdsMeta->f_hasCluster = 0; + if (sectLen < 41) { + return 0; + } + /* Following is based on: + * http://www.emc.ncep.noaa.gov/gmb/ens/info/ens_grib.html */ + if ((*center == NMC) && (*subcenter == 2)) { + if (sectLen < 45) { + printf ("Warning! Problems with Ensemble section\n"); + return 0; + } + pdsMeta->f_hasEns = 1; + pdsMeta->ens.BitFlag = *(pds++); +/* octet21 = pdsMeta->timeRange; = 119 has meaning now */ + pds += 11; + pdsMeta->ens.Application = *(pds++); + pdsMeta->ens.Type = *(pds++); + pdsMeta->ens.Number = *(pds++); + pdsMeta->ens.ProdID = *(pds++); + pdsMeta->ens.Smooth = *(pds++); + if ((pdsMeta->cat == 191) || (pdsMeta->cat == 192) || + (pdsMeta->cat == 193)) { + if (sectLen < 60) { + printf ("Warning! Problems with Ensemble Probability section\n"); + return 0; + } + pdsMeta->f_hasProb = 1; + pdsMeta->prob.Cat = pdsMeta->cat; + pdsMeta->cat = *(pds++); + pdsMeta->prob.Type = *(pds++); + MEMCPY_BIG (&uli_temp, pds, sizeof (sInt4)); + pdsMeta->prob.lower = fval_360 (uli_temp); + pds += 4; + MEMCPY_BIG (&uli_temp, pds, sizeof (sInt4)); + pdsMeta->prob.upper = fval_360 (uli_temp); + pds += 4; + pds += 4; + } + if ((pdsMeta->ens.Type == 4) || (pdsMeta->ens.Type == 5)) { + /* 87 ... 100 was reserved, but may not be encoded */ + if ((sectLen < 100) && (sectLen != 86)) { + printf ("Warning! Problems with Ensemble Clustering section\n"); + printf ("Section length == %d\n", sectLen); + return 0; + } + if (pdsMeta->f_hasProb == 0) { + pds += 14; + } + pdsMeta->f_hasCluster = 1; + pdsMeta->cluster.ensSize = *(pds++); + pdsMeta->cluster.clusterSize = *(pds++); + pdsMeta->cluster.Num = *(pds++); + pdsMeta->cluster.Method = *(pds++); + pdsMeta->cluster.NorLat = GRIB_UNSIGN_INT3 (*pds, pds[1], pds[2]); + pdsMeta->cluster.NorLat = pdsMeta->cluster.NorLat / 1000.; + pds += 3; + pdsMeta->cluster.SouLat = GRIB_UNSIGN_INT3 (*pds, pds[1], pds[2]); + pdsMeta->cluster.SouLat = pdsMeta->cluster.SouLat / 1000.; + pds += 3; + pdsMeta->cluster.EasLon = GRIB_UNSIGN_INT3 (*pds, pds[1], pds[2]); + pdsMeta->cluster.EasLon = pdsMeta->cluster.EasLon / 1000.; + pds += 3; + pdsMeta->cluster.WesLon = GRIB_UNSIGN_INT3 (*pds, pds[1], pds[2]); + pdsMeta->cluster.WesLon = pdsMeta->cluster.WesLon / 1000.; + pds += 3; + memcpy (pdsMeta->cluster.Member, pds, 10); + pdsMeta->cluster.Member[10] = '\0'; + } + /* Following based on: + * http://www.ecmwf.int/publications/manuals/libraries/gribex/ + * localGRIBUsage.html */ + } else if (*center == ECMWF) { + if (sectLen < 45) { + printf ("Warning! Problems with ECMWF PDS extension\n"); + return 0; + } + /* + sInt4 i_temp; + pds += 12; + i_temp = GRIB_SIGN_INT2 (pds[3], pds[4]); + printf ("ID %d Class %d Type %d Stream %d", pds[0], pds[1], pds[2], + i_temp); + pds += 5; + printf (" Ver %c%c%c%c, ", pds[0], pds[1], pds[2], pds[3]); + pds += 4; + printf ("Octet-50 %d, Octet-51 %d SectLen %d\n", pds[0], pds[1], + sectLen); + */ + } else { + printf ("Un-handled possible ensemble section center %d " + "subcenter %d\n", *center, *subcenter); + } + return 0; +} + +/***************************************************************************** + * Grib1_Inventory() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Parses the GRIB1 "Product Definition Section" for enough information to + * fill out the inventory data structure so we can do a simple inventory on + * the file in a similar way to how we did it for GRIB2. + * + * ARGUMENTS + * fp = An opened GRIB2 file already at the correct message. (Input) + * gribLen = The total length of the GRIB1 message. (Input) + * inv = The inventory data structure that we need to fill. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = gribLen is too small. + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +int GRIB1_Inventory (DataSource &fp, uInt4 gribLen, inventoryType *inv) +{ + char temp[3]; /* Used to determine the section length. */ + uInt4 sectLen; /* Length in bytes of the current section. */ + uChar *pds; /* The part of the message dealing with the PDS. */ + pdsG1Type pdsMeta; /* The pds parsed into a usable data structure. */ + char f_gds; /* flag if there is a gds section. */ + char f_bms; /* flag if there is a bms section. */ + short int DSF; /* Decimal Scale Factor for unpacking the data. */ + uChar gridID; /* Which GDS specs to use. */ + const char *varName; /* The name of the data stored in the grid. */ + const char *varComment; /* Extra comments about the data stored in grid. */ + const char *varUnit; /* Holds the name of the unit [K] [%] .. etc */ + int convert; /* Conversion method for this variable's unit. */ + uInt4 curLoc; /* Where we are in the current GRIB message. */ + unsigned short int center; /* The Center that created the data */ + unsigned short int subcenter; /* The Sub Center that created the data */ + + curLoc = 8; + if (fp.DataSourceFread(temp, sizeof (char), 3) != 3) { + errSprintf ("Ran out of file.\n"); + return -1; + } + sectLen = GRIB_UNSIGN_INT3 (*temp, temp[1], temp[2]); + if (curLoc + sectLen > gribLen) { + errSprintf ("Ran out of data in PDS (GRIB1_Inventory)\n"); + return -1; + } + pds = (uChar *) malloc (sectLen * sizeof (uChar)); + *pds = *temp; + pds[1] = temp[1]; + pds[2] = temp[2]; + if (fp.DataSourceFread(pds + 3, sizeof (char), sectLen - 3) + 3 != sectLen) { + errSprintf ("Ran out of file.\n"); + free (pds); + return -1; + } + + if (ReadGrib1Sect1 (pds, gribLen, &curLoc, &pdsMeta, &f_gds, &gridID, + &f_bms, &DSF, ¢er, &subcenter) != 0) { + preErrSprintf ("Inside GRIB1_Inventory\n"); + free (pds); + return -1; + } + free (pds); + inv->refTime = pdsMeta.refTime; + inv->validTime = pdsMeta.validTime; + inv->foreSec = inv->validTime - inv->refTime; + GRIB1_Table2LookUp (&(pdsMeta), &varName, &varComment, &varUnit, + &convert, center, subcenter); + inv->element = (char *) malloc ((1 + strlen (varName)) * sizeof (char)); + strcpy (inv->element, varName); + inv->unitName = (char *) malloc ((1 + 2 + strlen (varUnit)) * + sizeof (char)); + sprintf (inv->unitName, "[%s]", varUnit); + inv->comment = (char *) malloc ((1 + strlen (varComment) + + strlen (varUnit) + 2 + 1) * + sizeof (char)); + sprintf (inv->comment, "%s [%s]", varComment, varUnit); + + GRIB1_Table3LookUp (&(pdsMeta), &(inv->shortFstLevel), + &(inv->longFstLevel)); + + /* Get to the end of the GRIB1 message. */ + /* (inventory.c : GRIB2Inventory), is responsible for this. */ + /* fseek (fp, gribLen - sectLen, SEEK_CUR); */ + return 0; +} + +int GRIB1_RefTime (DataSource &fp, uInt4 gribLen, double *refTime) +{ + char temp[3]; /* Used to determine the section length. */ + uInt4 sectLen; /* Length in bytes of the current section. */ + uChar *pds; /* The part of the message dealing with the PDS. */ + pdsG1Type pdsMeta; /* The pds parsed into a usable data structure. */ + char f_gds; /* flag if there is a gds section. */ + char f_bms; /* flag if there is a bms section. */ + short int DSF; /* Decimal Scale Factor for unpacking the data. */ + uChar gridID; /* Which GDS specs to use. */ + uInt4 curLoc; /* Where we are in the current GRIB message. */ + unsigned short int center; /* The Center that created the data */ + unsigned short int subcenter; /* The Sub Center that created the data */ + + curLoc = 8; + if (fp.DataSourceFread (temp, sizeof (char), 3) != 3) { + errSprintf ("Ran out of file.\n"); + return -1; + } + sectLen = GRIB_UNSIGN_INT3 (*temp, temp[1], temp[2]); + if (curLoc + sectLen > gribLen) { + errSprintf ("Ran out of data in PDS (GRIB1_Inventory)\n"); + return -1; + } + pds = (uChar *) malloc (sectLen * sizeof (uChar)); + *pds = *temp; + pds[1] = temp[1]; + pds[2] = temp[2]; + if (fp.DataSourceFread (pds + 3, sizeof (char), sectLen - 3) + 3 != sectLen) { + errSprintf ("Ran out of file.\n"); + free (pds); + return -1; + } + + if (ReadGrib1Sect1 (pds, gribLen, &curLoc, &pdsMeta, &f_gds, &gridID, + &f_bms, &DSF, ¢er, &subcenter) != 0) { + preErrSprintf ("Inside GRIB1_Inventory\n"); + free (pds); + return -1; + } + free (pds); + + *refTime = pdsMeta.refTime; + + /* Get to the end of the GRIB1 message. */ + /* (inventory.c : GRIB2Inventory), is responsible for this. */ + /* fseek (fp, gribLen - sectLen, SEEK_CUR); */ + return 0; +} + +/***************************************************************************** + * ReadGrib1Sect2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Parses the GRIB1 "Grid Definition Section" or section 2, filling out + * the gdsMeta data structure. + * + * ARGUMENTS + * gds = The compressed part of the message dealing with "GDS". (Input) + * gribLen = The total length of the GRIB1 message. (Input) + * curLoc = Current location in the GRIB1 message. (Output) + * gdsMeta = The filled out gdsMeta data structure. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = gribLen is too small. + * -2 = unexpected values in gds. + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created. + * 12/2003 AAT: adas data encoder seems to have # of vertical data = 1, but + * parameters of vertical data = 255, which doesn't make sense. + * Changed the error from "fatal" to a warning in debug mode. + * 6/2004 AAT: Modified to allow "extended" lat/lon grids (ie stretched or + * stretched and rotated). + * + * NOTES + ***************************************************************************** + */ +static int ReadGrib1Sect2 (uChar *gds, uInt4 gribLen, uInt4 *curLoc, + gdsType *gdsMeta) +{ + uInt4 sectLen; /* Length in bytes of the current section. */ + int gridType; /* Which type of grid. (see enumerated types). */ + double unit = 1e-3; /* Used for converting to the correct unit. */ + uInt4 uli_temp; /* Used for reading a GRIB1 float. */ + int i; + int f_allZero; /* Used to find out if the "lat/lon" extension part + * is all 0 hence missing. */ + int f_allOne; /* Used to find out if the "lat/lon" extension part + * is all 1 hence missing. */ + + sectLen = GRIB_UNSIGN_INT3 (*gds, gds[1], gds[2]); +#ifdef DEBUG +/* + printf ("Section 2 length = %ld\n", sectLen); +*/ +#endif + *curLoc += sectLen; + if (*curLoc > gribLen) { + errSprintf ("Ran out of data in GDS (GRIB 1 Section 2)\n"); + return -1; + } + gds += 3; +/* +#ifdef DEBUG + if ((*gds != 0) || (gds[1] != 255)) { + printf ("GRIB1 GDS: Expect (NV = 0) != %d, (PV = 255) != %d\n", + *gds, gds[1]); + errSprintf ("SectLen == %ld\n", sectLen); + errSprintf ("GridType == %d\n", gds[2]); + } +#endif +*/ +#ifdef DEBUG + if (gds[1] != 255) { + printf ("\n\tCaution: GRIB1 GDS: FOR ALL NWS products, PV should be " + "255 rather than %d\n", gds[1]); + } +#endif + if ((gds[1] != 255) && (gds[1] > 6)) { + errSprintf ("GRIB1 GDS: Expect PV = 255 != %d\n", gds[1]); + return -2; + } + gds += 2; + gridType = *(gds++); + switch (gridType) { + case GB1S2_LATLON: // Latitude/Longitude Grid + case GB1S2_GAUSSIAN_LATLON: // Gaussian Latitude/Longitude + case GB1S2_ROTATED_LATLON: // Rotated Latitude/Longitude + if ((sectLen != 32) && (sectLen != 42) && (sectLen != 52)) { + errSprintf ("For LatLon GDS, should have 32 or 42 or 52 bytes " + "of data\n"); + return -1; + } + switch(gridType) { + case GB1S2_GAUSSIAN_LATLON: + gdsMeta->projType = GS3_GAUSSIAN_LATLON; + break; + case GB1S2_ROTATED_LATLON: + gdsMeta->projType = GS3_ROTATED_LATLON; + break; + default: + gdsMeta->projType = GS3_LATLON; + break; + } + gdsMeta->orientLon = 0; + gdsMeta->meshLat = 0; + gdsMeta->scaleLat1 = 0; + gdsMeta->scaleLat2 = 0; + gdsMeta->southLat = 0; + gdsMeta->southLon = 0; + gdsMeta->center = 0; + + gdsMeta->Nx = GRIB_UNSIGN_INT2 (*gds, gds[1]); + gds += 2; + gdsMeta->Ny = GRIB_UNSIGN_INT2 (*gds, gds[1]); + gds += 2; + gdsMeta->lat1 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->lon1 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + + gdsMeta->resFlag = *(gds++); + if (gdsMeta->resFlag & 0x40) { + gdsMeta->f_sphere = 0; + gdsMeta->majEarth = 6378.160; + gdsMeta->minEarth = 6356.775; + } else { + gdsMeta->f_sphere = 1; + gdsMeta->majEarth = 6367.47; + gdsMeta->minEarth = 6367.47; + } + + gdsMeta->lat2 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->lon2 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->Dx = GRIB_UNSIGN_INT2 (*gds, gds[1]) * unit; + gds += 2; + if (gridType == GB1S2_GAUSSIAN_LATLON) { + int np = GRIB_UNSIGN_INT2 (*gds, gds[1]); /* parallels between a pole and the equator */ + gdsMeta->Dy = 90.0 / np; + } else + gdsMeta->Dy = GRIB_UNSIGN_INT2 (*gds, gds[1]) * unit; + gds += 2; + gdsMeta->scan = *gds; + gdsMeta->f_typeLatLon = 0; +#ifdef DEBUG +/* + printf ("sectLen %ld\n", sectLen); +*/ +#endif + if (sectLen == 42) { + /* Check if all 0's or all 1's, which means f_typeLatLon == 0 */ + f_allZero = 1; + f_allOne = 1; + for (i = 0; i < 10; i++) { + if (gds[i] != 0) + f_allZero = 0; + if (gds[i] != 255) + f_allOne = 0; + } + if (!f_allZero && !f_allOne) { + gdsMeta->f_typeLatLon = 1; + gds += 5; + gdsMeta->poleLat = (GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * + unit); + gds += 3; + gdsMeta->poleLon = (GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * + unit); + gds += 3; + MEMCPY_BIG (&uli_temp, gds, sizeof (sInt4)); + gdsMeta->stretchFactor = fval_360 (uli_temp); + } + } else if (sectLen == 52) { + gds += 5; + /* Check if all 0's or all 1's, which means f_typeLatLon == 0 */ + f_allZero = 1; + f_allOne = 1; + for (i = 0; i < 20; i++) { + if (gds[i] != 0) + f_allZero = 0; + if (gds[i] != 255) + f_allOne = 0; + } + if (!f_allZero && !f_allOne) { + gdsMeta->f_typeLatLon = 2; + gdsMeta->southLat = (GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * + unit); + gds += 3; + gdsMeta->southLon = (GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * + unit); + gds += 3; + MEMCPY_BIG (&uli_temp, gds, sizeof (sInt4)); + gdsMeta->angleRotate = fval_360 (uli_temp); + gds += 4; + gdsMeta->poleLat = (GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * + unit); + gds += 3; + gdsMeta->poleLon = (GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * + unit); + gds += 3; + MEMCPY_BIG (&uli_temp, gds, sizeof (sInt4)); + gdsMeta->stretchFactor = fval_360 (uli_temp); + } +#ifdef DEBUG +/* + if (gdsMeta->lon2 == 360.25) + gdsMeta->lon2 = 359.75; +*/ +/* + printf ("south %f %f rotate %f pole %f %f stretch %f\n", + gdsMeta->southLat, gdsMeta->southLon, + gdsMeta->angleRotate, gdsMeta->poleLat, gdsMeta->poleLon, + gdsMeta->stretchFactor); + printf ("lat/lon type %d \n", gdsMeta->f_typeLatLon); +*/ +#endif + } + break; + + case GB1S2_POLAR: + if (sectLen != 32) { + errSprintf ("For Polar GDS, should have 32 bytes of data\n"); + return -1; + } + gdsMeta->projType = GS3_POLAR; + gdsMeta->lat2 = 0; + gdsMeta->lon2 = 0; + gdsMeta->southLat = 0; + gdsMeta->southLon = 0; + + gdsMeta->Nx = GRIB_UNSIGN_INT2 (*gds, gds[1]); + gds += 2; + gdsMeta->Ny = GRIB_UNSIGN_INT2 (*gds, gds[1]); + gds += 2; + gdsMeta->lat1 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->lon1 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + + gdsMeta->resFlag = *(gds++); + if (gdsMeta->resFlag & 0x40) { + gdsMeta->f_sphere = 0; + gdsMeta->majEarth = 6378.160; + gdsMeta->minEarth = 6356.775; + } else { + gdsMeta->f_sphere = 1; + gdsMeta->majEarth = 6367.47; + gdsMeta->minEarth = 6367.47; + } + + gdsMeta->orientLon = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->Dx = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]); + gds += 3; + gdsMeta->Dy = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]); + gds += 3; + gdsMeta->meshLat = 60; /* Depends on hemisphere. */ + gdsMeta->center = *(gds++); + if (gdsMeta->center & GRIB2BIT_1) { + /* South polar stereographic. */ + gdsMeta->scaleLat1 = gdsMeta->scaleLat2 = -90; + } else { + /* North polar stereographic. */ + gdsMeta->scaleLat1 = gdsMeta->scaleLat2 = 90; + } + gdsMeta->scan = *gds; + break; + + case GB1S2_LAMBERT: + if (sectLen != 42) { + errSprintf ("For Lambert GDS, should have 42 bytes of data\n"); + return -1; + } + gdsMeta->projType = GS3_LAMBERT; + gdsMeta->lat2 = 0; + gdsMeta->lon2 = 0; + + gdsMeta->Nx = GRIB_UNSIGN_INT2 (*gds, gds[1]); + gds += 2; + gdsMeta->Ny = GRIB_UNSIGN_INT2 (*gds, gds[1]); + gds += 2; + gdsMeta->lat1 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->lon1 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + + gdsMeta->resFlag = *(gds++); + if (gdsMeta->resFlag & 0x40) { + gdsMeta->f_sphere = 0; + gdsMeta->majEarth = 6378.160; + gdsMeta->minEarth = 6356.775; + } else { + gdsMeta->f_sphere = 1; + gdsMeta->majEarth = 6367.47; + gdsMeta->minEarth = 6367.47; + } + + gdsMeta->orientLon = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->Dx = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]); + gds += 3; + gdsMeta->Dy = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]); + gds += 3; + gdsMeta->center = *(gds++); + gdsMeta->scan = *(gds++); + gdsMeta->scaleLat1 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->scaleLat2 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->meshLat = gdsMeta->scaleLat1; + gdsMeta->southLat = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->southLon = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + break; + + case GB1S2_MERCATOR: + if (sectLen != 42) { + errSprintf ("For Mercator GDS, should have 42 bytes of data\n"); + return -1; + } + gdsMeta->projType = GS3_MERCATOR; + gdsMeta->southLat = 0; + gdsMeta->southLon = 0; + gdsMeta->orientLon = 0; + gdsMeta->center = 0; + + gdsMeta->Nx = GRIB_UNSIGN_INT2 (*gds, gds[1]); + gds += 2; + gdsMeta->Ny = GRIB_UNSIGN_INT2 (*gds, gds[1]); + gds += 2; + gdsMeta->lat1 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->lon1 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + + gdsMeta->resFlag = *(gds++); + if (gdsMeta->resFlag & 0x40) { + gdsMeta->f_sphere = 0; + gdsMeta->majEarth = 6378.160; + gdsMeta->minEarth = 6356.775; + } else { + gdsMeta->f_sphere = 1; + gdsMeta->majEarth = 6367.47; + gdsMeta->minEarth = 6367.47; + } + + gdsMeta->lat2 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->lon2 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->scaleLat1 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) * unit; + gds += 3; + gdsMeta->scaleLat2 = gdsMeta->scaleLat1; + gdsMeta->meshLat = gdsMeta->scaleLat1; + /* Reserved set to 0. */ + gds++; + gdsMeta->scan = *(gds++); + gdsMeta->Dx = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]); + gds += 3; + gdsMeta->Dy = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]); + break; + + default: + errSprintf ("Grid projection number is %d\n", gridType); + errSprintf ("Don't know how to handle this grid projection.\n"); + return -2; + } + gdsMeta->numPts = gdsMeta->Nx * gdsMeta->Ny; +#ifdef DEBUG +/* +printf ("NumPts = %ld\n", gdsMeta->numPts); +printf ("Nx = %ld, Ny = %ld\n", gdsMeta->Nx, gdsMeta->Ny); +*/ +#endif + return 0; +} + +/***************************************************************************** + * ReadGrib1Sect3() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Parses the GRIB1 "Bit Map Section" or section 3, filling out the bitmap + * as needed. + * + * ARGUMENTS + * bms = The compressed part of the message dealing with "BMS". (Input) + * gribLen = The total length of the GRIB1 message. (Input) + * curLoc = Current location in the GRIB1 message. (Output) + * bitmap = The extracted bitmap. (Output) + * NxNy = The total size of the grid. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = gribLen is too small. + * -2 = unexpected values in bms. + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +static int ReadGrib1Sect3 (uChar *bms, uInt4 gribLen, uInt4 *curLoc, + uChar *bitmap, uInt4 NxNy) +{ + uInt4 sectLen; /* Length in bytes of the current section. */ + short int numeric; /* Determine if this is a predefined bitmap */ + uChar bits; /* Used to locate which bit we are currently using. */ + uInt4 i; /* Helps traverse the bitmap. */ + + sectLen = GRIB_UNSIGN_INT3 (*bms, bms[1], bms[2]); +#ifdef DEBUG +/* + printf ("Section 3 length = %ld\n", sectLen); +*/ +#endif + *curLoc += sectLen; + if (*curLoc > gribLen) { + errSprintf ("Ran out of data in BMS (GRIB 1 Section 3)\n"); + return -1; + } + bms += 3; + /* Assert: *bms currently points to number of unused bits at end of BMS. */ + if (NxNy + *bms + 6 * 8 != sectLen * 8) { + errSprintf ("NxNy + # of unused bits %ld != # of available bits %ld\n", + (sInt4) (NxNy + *bms), (sInt4) ((sectLen - 6) * 8)); + return -2; + } + bms++; + /* Assert: Non-zero "numeric" means predefined bitmap. */ + numeric = GRIB_UNSIGN_INT2 (*bms, bms[1]); + bms += 2; + if (numeric != 0) { + errSprintf ("Don't handle predefined bitmaps yet.\n"); + return -2; + } + bits = 0x80; + for (i = 0; i < NxNy; i++) { + *(bitmap++) = (*bms) & bits; + bits = bits >> 1; + if (bits == 0) { + bms++; + bits = 0x80; + } + } + return 0; +} + +#ifdef DEBUG +static int UnpackCmplx (uChar *bds, CPL_UNUSED uInt4 gribLen, CPL_UNUSED uInt4 *curLoc, + CPL_UNUSED short int DSF, CPL_UNUSED double *data, CPL_UNUSED grib_MetaData *meta, + CPL_UNUSED char f_bms, CPL_UNUSED uChar *bitmap, CPL_UNUSED double unitM, + CPL_UNUSED double unitB, CPL_UNUSED short int ESF, CPL_UNUSED double refVal, + uChar numBits, uChar f_octet14) +{ + uInt4 secLen; + int N1; + int N2; + int P1; + int P2; + uChar octet14; + uChar f_maxtrixValues; + uChar f_secBitmap = 0; + uChar f_secValDiffWid; + int i; + uInt4 uli_temp; /* Used to store sInt4s (temporarily) */ + uChar bufLoc; /* Keeps track of where to start getting more data + * out of the packed data stream. */ + size_t numUsed; /* How many bytes were used in a given call to + * memBitRead. */ + uChar *width; + + secLen = 11; + N1 = GRIB_UNSIGN_INT2 (bds[0], bds[1]); + octet14 = bds[2]; + printf ("octet14, %d\n", octet14); + if (f_octet14) { + f_maxtrixValues = octet14 & GRIB2BIT_2; + f_secBitmap = octet14 & GRIB2BIT_3; + f_secValDiffWid = octet14 & GRIB2BIT_4; + printf ("f_matrixValues, f_secBitmap, f_secValeDiffWid %d %d %d\n", + f_maxtrixValues, f_secBitmap, f_secValDiffWid); + } + N2 = GRIB_UNSIGN_INT2 (bds[3], bds[4]); + P1 = GRIB_UNSIGN_INT2 (bds[5], bds[6]); + P2 = GRIB_UNSIGN_INT2 (bds[7], bds[8]); + printf ("N1 N2 P1 P2 : %d %d %d %d\n", N1, N2, P1, P2); + printf ("Reserved %d\n", bds[9]); + bds += 10; + secLen += 10; + + width = (uChar *) malloc (P1 * sizeof (uChar)); + + for (i = 0; i < P1; i++) { + width[i] = *bds; + printf ("(Width %d %d)\n", i, width[i]); + bds++; + secLen++; + } + if (f_secBitmap) { + bufLoc = 8; + for (i = 0; i < P2; i++) { + memBitRead (&uli_temp, sizeof (sInt4), bds, 1, &bufLoc, &numUsed); + printf ("(%d %d) ", i, uli_temp); + if (numUsed != 0) { + printf ("\n"); + bds += numUsed; + secLen++; + } + } + if (bufLoc != 8) { + bds++; + secLen++; + } + printf ("Observed Sec Len %d\n", secLen); + } else { + /* Jump over widths and secondary bitmap */ + bds += (N1 - 21); + secLen += (N1 - 21); + } + + bufLoc = 8; + for (i = 0; i < P1; i++) { + memBitRead (&uli_temp, sizeof (sInt4), bds, numBits, &bufLoc, &numUsed); + printf ("(%d %d) (numUsed %ld numBits %d)", i, uli_temp, + (long) numUsed, numBits); + if (numUsed != 0) { + printf ("\n"); + bds += numUsed; + secLen++; + } + } + if (bufLoc != 8) { + bds++; + secLen++; + } + + printf ("Observed Sec Len %d\n", secLen); + printf ("N2 = %d\n", N2); + + errSprintf ("Don't know how to handle Complex GRIB1 packing yet.\n"); + free (width); + return -2; + +} +#endif /* DEBUG */ + +/***************************************************************************** + * ReadGrib1Sect4() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Unpacks the "Binary Data Section" or section 4. + * + * ARGUMENTS + * bds = The compressed part of the message dealing with "BDS". (Input) + * gribLen = The total length of the GRIB1 message. (Input) + * curLoc = Current location in the GRIB1 message. (Output) + * DSF = Decimal Scale Factor for unpacking the data. (Input) + * data = The extracted grid. (Output) + * meta = The meta data associated with the grid (Input/Output) + * f_bms = True if bitmap is to be used. (Input) + * bitmap = 0 if missing data, 1 if valid data. (Input) + * unitM = The M unit conversion value in equation y = Mx + B. (Input) + * unitB = The B unit conversion value in equation y = Mx + B. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = gribLen is too small. + * -2 = unexpected values in bds. + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created + * 3/2004 AAT: Switched {# Pts * (# Bits in a Group) + + * # of unused bits != # of available bits} to a warning from an + * error. + * + * NOTES + * 1) See metaparse.c : ParseGrid() + * 2) Currently, only handles "Simple pack". + ***************************************************************************** + */ +static int ReadGrib1Sect4 (uChar *bds, uInt4 gribLen, uInt4 *curLoc, + short int DSF, double *data, grib_MetaData *meta, + char f_bms, uChar *bitmap, double unitM, + double unitB) +{ + uInt4 sectLen; /* Length in bytes of the current section. */ + short int ESF; /* Power of 2 scaling factor. */ + uInt4 uli_temp; /* Used to store sInt4s (temporarily) */ + double refVal; /* The refrence value for the grid, also the minimum + * value. */ + uChar numBits; /* # of bits for a single element of data. */ + uChar numUnusedBit; /* # of extra bits at end of record. */ + uChar f_spherHarm; /* Flag if data contains Spherical Harmonics. */ + uChar f_cmplxPack; /* Flag if complex packing was used. */ +#ifdef DEBUG + uChar f_octet14; /* Flag if octet 14 was used. */ +#endif + uChar bufLoc; /* Keeps track of where to start getting more data + * out of the packed data stream. */ + uChar f_convert; /* Determine if scan mode implies that we have to do + * manipulation as we read the grid to get desired + * internal scan mode. */ + uInt4 i; /* Used to traverse the grid. */ + size_t numUsed; /* How many bytes were used in a given call to + * memBitRead. */ + double d_temp; /* Holds the extracted data until we put it in data */ + sInt4 newIndex; /* Where to put the answer (primarily if f_convert) */ + sInt4 x; /* Used to help compute newIndex , if f_convert. */ + sInt4 y; /* Used to help compute newIndex , if f_convert. */ + double resetPrim; /* If possible, used to reset the primary missing + * value from 9.999e20 to a reasonable # (9999) */ + + if (meta->gds.Nx * meta->gds.Ny != meta->gds.numPts) { + errSprintf ("(Nx * Ny != numPts) ?? in BDS (GRIB 1 Section 4)\n"); + return -2; + } + sectLen = GRIB_UNSIGN_INT3 (*bds, bds[1], bds[2]); +#ifdef DEBUG +/* + printf ("Section 4 length = %ld\n", sectLen); +*/ +#endif + *curLoc += sectLen; + if (*curLoc > gribLen) { + errSprintf ("Ran out of data in BDS (GRIB 1 Section 4)\n"); + return -1; + } + bds += 3; + + /* Assert: bds now points to the main pack flag. */ + f_spherHarm = (*bds) & GRIB2BIT_1; + f_cmplxPack = (*bds) & GRIB2BIT_2; + meta->gridAttrib.fieldType = (*bds) & GRIB2BIT_3; +#ifdef DEBUG + f_octet14 = (*bds) & GRIB2BIT_4; +#endif + + numUnusedBit = (*bds) & 0x0f; +#ifdef DEBUG +/* + printf ("bds byte flag = %d\n", *bds); + printf ("Number of unused bits = %d\n", numUnusedBit); +*/ +#endif + if (f_spherHarm) { + errSprintf ("Don't know how to handle Spherical Harmonics yet.\n"); + return -2; + } +/* + if (f_octet14) { + errSprintf ("Don't know how to handle Octet 14 data yet.\n"); + errSprintf ("bds byte flag = %d\n", *bds); + errSprintf ("bds byte: %d %d %d %d\n", f_spherHarm, f_cmplxPack, + meta->gridAttrib.fieldType, f_octet14); + return -2; + } +*/ + if (f_cmplxPack) { + meta->gridAttrib.packType = 2; + } else { + meta->gridAttrib.packType = 0; + } + bds++; + + /* Assert: bds now points to E (power of 2 scaling factor). */ + ESF = GRIB_SIGN_INT2 (*bds, bds[1]); + bds += 2; + MEMCPY_BIG (&uli_temp, bds, sizeof (sInt4)); + refVal = fval_360 (uli_temp); + bds += 4; + + /* Assert: bds is now the number of bits in a group. */ + numBits = *bds; +/* +#ifdef DEBUG + printf ("refValue %f numBits %d\n", refVal, numBits); + printf ("ESF %d DSF %d\n", ESF, DSF); +#endif +*/ + if (f_cmplxPack) { + bds++; +#ifdef DEBUG + return UnpackCmplx (bds, gribLen, curLoc, DSF, data, meta, f_bms, + bitmap, unitM, unitB, ESF, refVal, numBits, + f_octet14); +#else + errSprintf ("Don't know how to handle Complex GRIB1 packing yet.\n"); + return -2; +#endif + } + + if (!f_bms && (meta->gds.numPts * numBits + numUnusedBit) != + (sectLen - 11) * 8) { + printf ("numPts * (numBits in a Group) + # of unused bits %d != " + "# of available bits %d\n", + (sInt4) (meta->gds.numPts * numBits + numUnusedBit), + (sInt4) ((sectLen - 11) * 8)); +/* + errSprintf ("numPts * (numBits in a Group) + # of unused bits %ld != " + "# of available bits %ld\n", + (sInt4) (meta->gds.numPts * numBits + numUnusedBit), + (sInt4) ((sectLen - 11) * 8)); + return -2; +*/ + } + if (numBits > 32) { + errSprintf ("The number of bits per number is larger than 32?\n"); + return -2; + } + bds++; + + /* Convert Units. */ + if (unitM == -10) { + meta->gridAttrib.min = pow (10.0, (refVal * pow (2.0, ESF) / + pow (10.0, DSF))); + } else { +/* meta->gridAttrib.min = unitM * (refVal / pow (10.0, DSF)) + unitB; */ + meta->gridAttrib.min = unitM * (refVal * pow (2.0, ESF) / + pow (10.0, DSF)) + unitB; + } + meta->gridAttrib.max = meta->gridAttrib.min; + meta->gridAttrib.f_maxmin = 1; + meta->gridAttrib.numMiss = 0; + meta->gridAttrib.refVal = refVal; + meta->gridAttrib.ESF = ESF; + meta->gridAttrib.DSF = DSF; + bufLoc = 8; + /* Internally we use scan = 0100. Scan is usually 0100 but if need be, we + * can convert it. */ + f_convert = ((meta->gds.scan & 0xe0) != 0x40); + + if (f_bms) { +/* +#ifdef DEBUG + printf ("There is a bitmap?\n"); +#endif +*/ + /* Start unpacking the data, assuming there is a bitmap. */ + meta->gridAttrib.f_miss = 1; + meta->gridAttrib.missPri = UNDEFINED; + for (i = 0; i < meta->gds.numPts; i++) { + /* Find the destination index. */ + if (f_convert) { + /* ScanIndex2XY returns value as if scan was 0100 */ + ScanIndex2XY (i, &x, &y, meta->gds.scan, meta->gds.Nx, + meta->gds.Ny); + newIndex = (x - 1) + (y - 1) * meta->gds.Nx; + } else { + newIndex = i; + } + /* A 0 in bitmap means no data. A 1 in bitmap means data. */ + if (!bitmap[i]) { + meta->gridAttrib.numMiss++; + data[newIndex] = UNDEFINED; + } else { + if (numBits != 0) { + memBitRead (&uli_temp, sizeof (sInt4), bds, numBits, + &bufLoc, &numUsed); + bds += numUsed; + d_temp = (refVal + (uli_temp * pow (2.0, ESF))) / pow (10.0, DSF); + /* Convert Units. */ + if (unitM == -10) { + d_temp = pow (10.0, d_temp); + } else { + d_temp = unitM * d_temp + unitB; + } + if (meta->gridAttrib.max < d_temp) { + meta->gridAttrib.max = d_temp; + } + data[newIndex] = d_temp; + } else { + /* Assert: d_temp = unitM * refVal / pow (10.0,DSF) + unitB. */ + /* Assert: min = unitM * refVal / pow (10.0, DSF) + unitB. */ + data[newIndex] = meta->gridAttrib.min; + } + } + } + /* Reset the missing value to UNDEFINED_PRIM if possible. If not + * possible, make sure UNDEFINED is outside the range. If UNDEFINED + * is_ in the range, choose max + 1 for missing. */ + resetPrim = 0; + if ((meta->gridAttrib.max < UNDEFINED_PRIM) || + (meta->gridAttrib.min > UNDEFINED_PRIM)) { + resetPrim = UNDEFINED_PRIM; + } else if ((meta->gridAttrib.max >= UNDEFINED) && + (meta->gridAttrib.min <= UNDEFINED)) { + resetPrim = meta->gridAttrib.max + 1; + } + if (resetPrim != 0) { + meta->gridAttrib.missPri = resetPrim; + for (i = 0; i < meta->gds.numPts; i++) { + /* Find the destination index. */ + if (f_convert) { + /* ScanIndex2XY returns value as if scan was 0100 */ + ScanIndex2XY (i, &x, &y, meta->gds.scan, meta->gds.Nx, + meta->gds.Ny); + newIndex = (x - 1) + (y - 1) * meta->gds.Nx; + } else { + newIndex = i; + } + if (!bitmap[i]) { + data[newIndex] = resetPrim; + } + } + } + + } else { + +#ifdef DEBUG +/* + printf ("There is no bitmap?\n"); +*/ +#endif + + /* Start unpacking the data, assuming there is NO bitmap. */ + meta->gridAttrib.f_miss = 0; + for (i = 0; i < meta->gds.numPts; i++) { + if (numBits != 0) { + /* Find the destination index. */ + if (f_convert) { + /* ScanIndex2XY returns value as if scan was 0100 */ + ScanIndex2XY (i, &x, &y, meta->gds.scan, meta->gds.Nx, + meta->gds.Ny); + newIndex = (x - 1) + (y - 1) * meta->gds.Nx; + } else { + newIndex = i; + } + + memBitRead (&uli_temp, sizeof (sInt4), bds, numBits, &bufLoc, + &numUsed); + bds += numUsed; + d_temp = (refVal + (uli_temp * pow (2.0, ESF))) / pow (10.0, DSF); + +#ifdef DEBUG +/* + if (i == 1) { + printf ("refVal %f, uli_temp %ld, ans %f\n", refVal, uli_temp, + d_temp); + printf ("numBits %d, bufLoc %d, numUsed %d\n", numBits, + bufLoc, numUsed); + } +*/ +#endif + + /* Convert Units. */ + if (unitM == -10) { + d_temp = pow (10.0, d_temp); + } else { + d_temp = unitM * d_temp + unitB; + } + if (meta->gridAttrib.max < d_temp) { + meta->gridAttrib.max = d_temp; + } + data[newIndex] = d_temp; + } else { + /* Assert: whole array = unitM * refVal + unitB. */ + /* Assert: *min = unitM * refVal + unitB. */ + data[i] = meta->gridAttrib.min; + } + } + } + return 0; +} + +/***************************************************************************** + * ReadGrib1Record() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Reads in a GRIB1 message, and parses the data into various data + * structures, for use with other code. + * + * ARGUMENTS + * fp = An opened GRIB2 file already at the correct message. (Input) + * f_unit = 0 use GRIB2 units, 1 use English, 2 use metric. (Input) + * Grib_Data = The read in GRIB2 grid. (Output) + * grib_DataLen = Size of Grib_Data. (Output) + * meta = A filled in meta structure (Output) + * IS = The structure containing all the arrays that the + * unpacker uses (Output) + * sect0 = Already read in section 0 data. (Input) + * gribLen = Length of the GRIB1 message. (Input) + * majEarth = Used to override the GRIB major axis of earth. (Input) + * minEarth = Used to override the GRIB minor axis of earth. (Input) + * + * FILES/DATABASES: + * An already opened file pointing to the desired GRIB1 message. + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = Problems reading in the PDS. + * -2 = Problems reading in the GDS. + * -3 = Problems reading in the BMS. + * -4 = Problems reading in the BDS. + * -5 = Problems reading the closing section. + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created + * 5/2003 AAT: Was not updating offset. It should be updated by + * calling routine anyways, so I got rid of the parameter. + * 7/2003 AAT: Allowed user to override the radius of earth. + * 8/2003 AAT: Found a memory Leak (Had been setting unitName to NULL). + * 2/2004 AAT: Added maj/min earth override. + * 3/2004 AAT: Added ability to change units. + * + * NOTES + * 1) Could also compare GDS with the one specified by gridID + * 2) Could add gridID support. + * 3) Should add unitM / unitB support. + ***************************************************************************** + */ +int ReadGrib1Record (DataSource &fp, sChar f_unit, double **Grib_Data, + uInt4 *grib_DataLen, grib_MetaData *meta, + IS_dataType *IS, sInt4 sect0[SECT0LEN_WORD], + uInt4 gribLen, double majEarth, double minEarth) +{ + sInt4 nd5; /* Size of grib message rounded up to the nearest * + * sInt4. */ + uChar *c_ipack; /* A char ptr to the message stored in IS->ipack */ + uInt4 curLoc; /* Current location in the GRIB message. */ + char f_gds; /* flag if there is a gds section. */ + char f_bms; /* flag if there is a bms section. */ + double *grib_Data; /* A pointer to Grib_Data for ease of manipulation. */ + uChar *bitmap = NULL; /* A char field (0=noData, 1=data) set up in BMS. */ + short int DSF; /* Decimal Scale Factor for unpacking the data. */ + double unitM = 1; /* M in y = Mx + B, for unit conversion. */ + double unitB = 0; /* B in y = Mx + B, for unit conversion. */ + uChar gridID; /* Which GDS specs to use. */ + const char *varName; /* The name of the data stored in the grid. */ + const char *varComment; /* Extra comments about the data stored in grid. */ + const char *varUnit; /* Holds the name of the unit [K] [%] .. etc */ + sInt4 li_temp; /* Used to make sure section 5 is 7777. */ + char unitName[15]; /* Holds the string name of the current unit. */ + int unitLen; /* String length of string name of current unit. */ + + /* Make room for entire message, and read it in. */ + /* nd5 needs to be gribLen in (sInt4) units rounded up. */ + nd5 = (gribLen + 3) / 4; + if (nd5 > IS->ipackLen) { + IS->ipackLen = nd5; + IS->ipack = (sInt4 *) realloc ((void *) (IS->ipack), + (IS->ipackLen) * sizeof (sInt4)); + } + c_ipack = (uChar *) IS->ipack; + /* Init last sInt4 to 0, to make sure that the padded bytes are 0. */ + IS->ipack[nd5 - 1] = 0; + /* Init first 2 sInt4 to sect0. */ + memcpy (c_ipack, sect0, SECT0LEN_WORD * 2); + /* Read in the rest of the message. */ + if (fp.DataSourceFread (c_ipack + SECT0LEN_WORD * 2, sizeof (char), + (gribLen - SECT0LEN_WORD * 2)) + SECT0LEN_WORD * 2 != gribLen) { + errSprintf ("Ran out of file\n"); + return -1; + } + + /* Preceeding was in degrib2, next part is specific to GRIB1. */ + curLoc = 8; + if (ReadGrib1Sect1 (c_ipack + curLoc, gribLen, &curLoc, &(meta->pds1), + &f_gds, &gridID, &f_bms, &DSF, &(meta->center), + &(meta->subcenter)) != 0) { + preErrSprintf ("Inside ReadGrib1Record\n"); + return -1; + } + + /* Get the Grid Definition Section. */ + if (f_gds) { + if (ReadGrib1Sect2 (c_ipack + curLoc, gribLen, &curLoc, + &(meta->gds)) != 0) { + preErrSprintf ("Inside ReadGrib1Record\n"); + return -2; + } + /* Could also compare GDS with the one specified by gridID? */ + } else { + errSprintf ("Don't know how to handle a gridID lookup yet.\n"); + return -2; + } + meta->pds1.gridID = gridID; + /* Allow data originating from NCEP to be 6371.2 by default. */ + if (meta->center == NMC) { + if (meta->gds.majEarth == 6367.47) { + meta->gds.f_sphere = 1; + meta->gds.majEarth = 6371.2; + meta->gds.minEarth = 6371.2; + } + } + if ((majEarth > 6300) && (majEarth < 6400)) { + if ((minEarth > 6300) && (minEarth < 6400)) { + meta->gds.f_sphere = 0; + meta->gds.majEarth = majEarth; + meta->gds.minEarth = minEarth; + if (majEarth == minEarth) { + meta->gds.f_sphere = 1; + } + } else { + meta->gds.f_sphere = 1; + meta->gds.majEarth = majEarth; + meta->gds.minEarth = majEarth; + } + } + + /* Allocate memory for the grid. */ + if (meta->gds.numPts > *grib_DataLen) { + *grib_DataLen = meta->gds.numPts; + *Grib_Data = (double *) realloc ((void *) (*Grib_Data), + (*grib_DataLen) * sizeof (double)); + if (!(*Grib_Data)) + { + *grib_DataLen = 0; + return -1; + } + } + grib_Data = *Grib_Data; + + /* Get the Bit Map Section. */ + if (f_bms) { + bitmap = (uChar *) malloc (meta->gds.numPts * sizeof (char)); + if (ReadGrib1Sect3 (c_ipack + curLoc, gribLen, &curLoc, bitmap, + meta->gds.numPts) != 0) { + free (bitmap); + preErrSprintf ("Inside ReadGrib1Record\n"); + return -3; + } + } + + /* Figure out some basic stuff about the grid. */ + /* Following is similar to metaparse.c : ParseElemName */ + GRIB1_Table2LookUp (&(meta->pds1), &varName, &varComment, &varUnit, + &(meta->convert), meta->center, meta->subcenter); + meta->element = (char *) realloc ((void *) (meta->element), + (1 + strlen (varName)) * sizeof (char)); + strcpy (meta->element, varName); + meta->unitName = (char *) realloc ((void *) (meta->unitName), + (1 + 2 + strlen (varUnit)) * + sizeof (char)); + sprintf (meta->unitName, "[%s]", varUnit); + meta->comment = (char *) realloc ((void *) (meta->comment), + (1 + strlen (varComment) + + strlen (varUnit) + + 2 + 1) * sizeof (char)); + sprintf (meta->comment, "%s [%s]", varComment, varUnit); + + if (ComputeUnit (meta->convert, meta->unitName, f_unit, &unitM, &unitB, + unitName) == 0) { + unitLen = strlen (unitName); + meta->unitName = (char *) realloc ((void *) (meta->unitName), + 1 + unitLen * sizeof (char)); + strncpy (meta->unitName, unitName, unitLen); + meta->unitName[unitLen] = '\0'; + } + + /* Read the GRID. */ + if (ReadGrib1Sect4 (c_ipack + curLoc, gribLen, &curLoc, DSF, grib_Data, + meta, f_bms, bitmap, unitM, unitB) != 0) { + free (bitmap); + preErrSprintf ("Inside ReadGrib1Record\n"); + return -4; + } + if (f_bms) { + free (bitmap); + } + + GRIB1_Table3LookUp (&(meta->pds1), &(meta->shortFstLevel), + &(meta->longFstLevel)); +/* printf ("%s .. %s\n", meta->shortFstLevel, meta->longFstLevel);*/ + +/* + strftime (meta->refTime, 20, "%Y%m%d%H%M", + gmtime (&(meta->pds1.refTime))); +*/ + Clock_Print (meta->refTime, 20, meta->pds1.refTime, "%Y%m%d%H%M", 0); + +/* + strftime (meta->validTime, 20, "%Y%m%d%H%M", + gmtime (&(meta->pds1.validTime))); +*/ + Clock_Print (meta->validTime, 20, meta->pds1.validTime, "%Y%m%d%H%M", 0); + + meta->deltTime = (sInt4) (meta->pds1.validTime - meta->pds1.refTime); + + /* Read section 5. If it is "7777" == 926365495 we are done. */ + if (curLoc == gribLen) { + printf ("Warning: either gribLen did not account for section 5, or " + "section 5 is missing\n"); + return 0; + } + if (curLoc + 4 > gribLen) { + errSprintf ("Ran out of bytes looking for the end of the message.\n"); + return -5; + } + myAssert (curLoc + 4 == gribLen); + memcpy (&li_temp, c_ipack + curLoc, 4); + if (li_temp != 926365495L) { + errSprintf ("Did not find the end of the message.\n"); + return -5; + } + + return 0; +} + +/***************************************************************************** + * main() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To test the capabilities of this module, and give an example as to how + * ReadGrib1Record expects to be called. + * + * ARGUMENTS + * argc = The number of arguments on the command line. (Input) + * argv = The arguments on the command line. (Input) + * + * FILES/DATABASES: + * A GRIB1 file. + * + * RETURNS: int + * 0 = OK + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created + * 6/2003 Matthew T. Kallio (matt@wunderground.com): + * "wmo" dimension increased to WMO_HEADER_LEN + 1 (for '\0' char) + * + * NOTES + ***************************************************************************** + */ +#ifdef DEBUG_DEGRIB1 +int main (int argc, char **argv) +{ + DataSource grib_fp; /* The opened grib2 file for input. */ + sInt4 offset; /* Where we currently are in grib_fp. */ + sInt4 sect0[SECT0LEN_WORD]; /* Holds the current Section 0. */ + char wmo[WMO_HEADER_LEN + 1]; /* Holds the current wmo message. */ + sInt4 gribLen; /* Length of the current GRIB message. */ + sInt4 wmoLen; /* Length of current wmo Message. */ + char *msg; + int version; + sChar f_unit = 0; + double *grib_Data; + sInt4 grib_DataLen; + grib_MetaData meta; + IS_dataType is; /* Un-parsed meta data for this GRIB2 message. As + * well as some memory used by the unpacker. */ + + //if ((grib_fp = fopen (argv[1], "rb")) == NULL) { + // printf ("Problems opening %s for read\n", argv[1]); + // return 1; + //} + grib_fp = FileDataSource(argv[1]); + IS_Init (&is); + MetaInit (&meta); + + offset = 0; + if (ReadSECT0 (grib_fp, offset, WMO_HEADER_LEN, WMO_SECOND_LEN, wmo, + sect0, &gribLen, &wmoLen, &version) < 0) { + msg = errSprintf (NULL); + printf ("%s\n", msg); + return -1; + } + grib_DataLen = 0; + grib_Data = NULL; + if (version == 1) { + meta.GribVersion = version; + ReadGrib1Record (grib_fp, f_unit, &grib_Data, &grib_DataLen, &meta, + &is, sect0, gribLen); + offset = offset + gribLen + wmoLen; + } + + MetaFree (&meta); + IS_Free (&is); + free (grib_Data); + //fclose (grib_fp); + return 0; +} +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/degrib1.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/degrib1.h new file mode 100644 index 000000000..c504f41ac --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/degrib1.h @@ -0,0 +1,29 @@ +#ifndef DEGRIB1_H +#define DEGRIB1_H + +#include +#include "type.h" +#include "meta.h" +#include "degrib2.h" +#include "inventory.h" + +typedef struct { + const char *name, *comment, *unit; + unit_convert convert; +} GRIB1ParmTable; + +typedef struct { + const char *name, *comment, *unit; + char f_twoPart; +} GRIB1SurfTable; + + +int GRIB1_Inventory (DataSource &fp, uInt4 gribLen, inventoryType * inv); + +int GRIB1_RefTime (DataSource &fp, uInt4 gribLen, double *refTime); + +int ReadGrib1Record (DataSource &fp, sChar f_unit, double **Grib_Data, + uInt4 *grib_DataLen, grib_MetaData * meta, + IS_dataType * IS, sInt4 sect0[SECT0LEN_WORD], + uInt4 gribLen, double majEarth, double minEarth); +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/degrib2.cpp b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/degrib2.cpp new file mode 100644 index 000000000..2caf42aa3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/degrib2.cpp @@ -0,0 +1,1196 @@ +/***************************************************************************** + * degrib2.c + * + * DESCRIPTION + * This file contains the main driver routines to call the unpack grib2 + * library functions. It also contains the code needed to figure out the + * dimensions of the arrays before calling the FORTRAN library. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * 12/2002 Tim Kempisty, Ana Canizares, Tim Boyer, & Marc Saccucci + * (TK,AC,TB,&MS): Code Review 1. + * + * NOTES + ***************************************************************************** + */ +#include +#include +#include +#include +#include "myassert.h" +#include "myerror.h" +#include "memendian.h" +#include "meta.h" +#include "metaname.h" +//#include "write.h" +#include "degrib2.h" +#include "degrib1.h" +#include "tdlpack.h" +#include "grib2api.h" +//#include "mymapf.h" +#include "clock.h" + +#define GRIB_UNSIGN_INT3(a,b,c) ((a<<16)+(b<<8)+c) + +/***************************************************************************** + * ReadSect0() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * Looks for the next GRIB message, by looking for the keyword "GRIB". It + * expects the message in "expect" bytes from the start, but could find the + * message in "expect2" bytes or 0 bytes from the start. Returns -1 if it + * can't find "GRIB", 1 if "GRIB" is not 0, "expect", or "expect2" bytes from + * the start. + * It stores the bytes it reads (a max of "expect") upto but not including + * the 'G' in "GRIB" in wmo. + * + * After it finds section 0, it then parses the 16 bytes that make up + * section 0 so that it can return the length of the entire GRIB message. + * + * When done, it sets fp to point to the end of Sect0. + * + * The reason for this procedure is so that we can read in the size of the + * grib message, and thus allocate enough memory to read the message in before + * making it Big endian, and passing it to the library for unpacking. + * + * ARGUMENTS + * fp = A pointer to an opened file in which to read. + * When done, this points to the start of section 1. (Input/Output) + * buff = The data between messages. (Input/Output) + * buffLen = The length of buff (Output) + * limit = How many bytes to read before giving up and stating it is not + * a proper message. (-1 means no limit). (Input) + * sect0 = The read in Section 0 (as seen on disk). (Output) + * gribLen = Length of this GRIB message. (Output) + * version = 1 if GRIB1 message, 2 if GRIB2 message, -1 if TDLP message. + * (Output) + * expect = The expected number of bytes to find "GRIB" in. (Input) + * expect2 = The second possible number of bytes to find "GRIB" in. (Input) + * wmo = Assumed allocated to be at least size "expect". + * Holds the bytes before the first "GRIB" message. + * expect should be > expect2, but is up to caller (Output) + * wmoLen = Length of wmo (total number of bytes read - SECT0LEN_WORD * 4). + * (Output) + * + * FILES/DATABASES: + * An already opened "GRIB2" File + * + * RETURNS: int (could use errSprintf()) + * 1 = Length of wmo was != 0 and was != expect + * 0 = OK + * -1 = Couldn't find "GRIB" part of message. + * -2 = Ran out of file while reading this section. + * -3 = Grib version was not 1 or 2. + * -4 = Most significant sInt4 of GRIB length was not 0 + * -5 = Grib message length was <= 16 (can't be smaller than just sect 0) + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 11/2002 AAT: Combined with ReadWMOHeader + * 12/2002 (TK,AC,TB,&MS): Code Review. + * 1/2003 AAT: Bug found. wmo access out of bounds of expect when setting + * the /0 element, if wmoLen > expect. + * 4/2003 AAT: Added ability to handle GRIB version 1. + * 5/2003 AAT: Added limit option. + * 8/2003 AAT: Removed dependence on offset, and fileLen. + * 10/2004 AAT: Modified to allow for TDLP files + * + * NOTES + * 1a) 1196575042L == ASCII representation of "GRIB" (GRIB in MSB) + * 1b) 1112101447L == ASCII representation of "BIRG" (GRIB in LSB) + * 1c) 1413762128L == ASCII representation of "TDLP" (TDLP in MSB) + * 1d) 1347175508L == ASCII representation of "PLDT" (TDLP in LSB) + * 2) Takes advantage of the wordType to check that the edition is correct. + * 3) May want to return prodType. + * 4) WMO_HEADER_ORIG_LEN was added for backward compatibility... should be + * removed when we no longer use old format. (say in a year from 11/2002) + * + ***************************************************************************** + */ +int ReadSECT0 (DataSource &fp, char **buff, uInt4 *buffLen, sInt4 limit, + sInt4 sect0[SECT0LEN_WORD], uInt4 *gribLen, int *version) +{ + typedef union { + sInt4 li; + unsigned char buffer[4]; + } wordType; + + uChar gribMatch = 0; /* Counts how many letters in GRIB we've matched. */ + uChar tdlpMatch = 0; /* Counts how many letters in TDLP we've matched. */ + wordType word; /* Used to check that the edition is correct. */ + uInt4 curLen; /* Where we currently are in buff. */ + uInt4 i; /* Used to loop over the first few char's */ + uInt4 stillNeed; /* Number of bytes still needed to get 1st 8 bytes of + * message into memory. */ + + /* Get first 8 bytes. If GRIB we don't care. If TDLP, this is the length + * of record. Read at least 1 record (length + 2 * 8) + 8 (next record + * length) + 8 bytes before giving up. */ + curLen = 8; + if (*buffLen < curLen) { + *buffLen = curLen; + *buff = (char *) realloc ((void *) *buff, *buffLen * sizeof (char)); + } + if (fp.DataSourceFread(*buff, sizeof (char), curLen) != curLen) { + errSprintf ("ERROR: Couldn't find 'GRIB' or 'TDLP'\n"); + return -1; + } +/* + Can't do the following because we don't know if the file is a GRIB file or + not, or if it was a FORTRAN file. + if (limit > 0) { + MEMCPY_BIG (&recLen, *buff, 4); + limit = (limit > recLen + 32) ? limit : recLen + 32; + } +*/ + while ((tdlpMatch != 4) && (gribMatch != 4)) { + for (i = curLen - 8; i + 3 < curLen; i++) { + if ((*buff)[i] == 'G') { + if (((*buff)[i + 1] == 'R') && ((*buff)[i + 2] == 'I') && + ((*buff)[i + 3] == 'B')) { + gribMatch = 4; + break; + } + } else if ((*buff)[i] == 'T') { + if (((*buff)[i + 1] == 'D') && ((*buff)[i + 2] == 'L') && + ((*buff)[i + 3] == 'P')) { + tdlpMatch = 4; + break; + } + } + } + stillNeed = i - (curLen - 8); + /* Read enough of message to have the first 8 bytes (including ID). */ + if (stillNeed != 0) { + curLen += stillNeed; + if ((limit >= 0) && (curLen > (size_t) limit)) { + errSprintf ("ERROR: Couldn't find type in %ld bytes\n", limit); + return -1; + } + if (*buffLen < curLen) { + *buffLen = curLen; + *buff = (char *) realloc ((void *) *buff, + *buffLen * sizeof (char)); + } + if (fp.DataSourceFread((*buff) + (curLen - stillNeed), sizeof (char), stillNeed) != stillNeed) { + errSprintf ("ERROR: Ran out of file reading SECT0\n"); + return -1; + } + } + } + + /* curLen and (*buff) hold 8 bytes of section 0. */ + curLen -= 8; + memcpy (&(sect0[0]), (*buff) + curLen, 4); +#ifdef DEBUG +#ifdef LITTLE_ENDIAN + myAssert ((sect0[0] == 1112101447L) || (sect0[0] == 1347175508L)); +#else + myAssert ((sect0[0] == 1196575042L) || (sect0[0] == 1413762128L)); +#endif +#endif + memcpy (&(sect0[1]), *buff + curLen + 4, 4); + /* Make sure we don't pass back part of "GRIB" in the buffer. */ + (*buff)[curLen] = '\0'; + *buffLen = curLen; + + word.li = sect0[1]; + if (tdlpMatch == 4) { + if (word.buffer[3] != 0) { + errSprintf ("ERROR: unexpected version of TDLP in SECT0\n"); + return -2; + } + *version = -1; + /* Find out the GRIB Message Length */ + *gribLen = GRIB_UNSIGN_INT3 (word.buffer[0], word.buffer[1], + word.buffer[2]); + /* Min message size: GRIB1=52, TDLP=59, GRIB2=86. */ + if (*gribLen < 59) { + errSprintf ("TDLP length %ld was < 59?\n", *gribLen); + return -5; + } + } else if (word.buffer[3] == 1) { + *version = 1; + /* Find out the GRIB Message Length */ + *gribLen = GRIB_UNSIGN_INT3 (word.buffer[0], word.buffer[1], + word.buffer[2]); + /* Min message size: GRIB1=52, TDLP=59, GRIB2=86. */ + if (*gribLen < 52) { + errSprintf ("GRIB1 length %ld was < 52?\n", *gribLen); + return -5; + } + } else if (word.buffer[3] == 2) { + *version = 2; + /* Make sure we still have enough file for the rest of section 0. */ + if (fp.DataSourceFread(sect0 + 2, sizeof (sInt4), 2) != 2) { + errSprintf ("ERROR: Ran out of file reading SECT0\n"); + return -2; + } + if (sect0[2] != 0) { + errSprintf ("Most significant sInt4 of GRIB length was not 0?\n"); + errSprintf ("This is either an error, or we have a single GRIB " + "message which is larger than 2^31 = 2,147,283,648 " + "bytes.\n"); + return -4; + } +#ifdef LITTLE_ENDIAN + revmemcpy (gribLen, &(sect0[3]), sizeof (sInt4)); +#else + *gribLen = sect0[3]; +#endif + } else { + errSprintf ("ERROR: Not TDLPack, and Grib edition is not 1 or 2\n"); + return -3; + } + return 0; +} + +/***************************************************************************** + * FindGRIBMsg() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * Jumps through a GRIB2 file looking for a specific message. Currently + * that message is determined by msgNum which is in the range of 1..n. + * In the future we may be searching based on projection or date. + * + * ARGUMENTS + * fp = The current GRIB2 file to look through. (Input) + * msgNum = Which message to look for. (Input) + * offset = Where in the file the message starts (this is before the + * wmo ASCII part if there is one.) (Output) + * curMsg = The current # of messages we have looked through. (In/Out) + * + * FILES/DATABASES: + * An already opened "GRIB2" File + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = Problems reading Section 0. + * -2 = Ran out of file. + * + * HISTORY + * 11/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (TK,AC,TB,&MS): Code Review. + * 6/2003 Matthew T. Kallio (matt@wunderground.com): + * "wmo" dimension increased to WMO_HEADER_LEN + 1 (for '\0' char) + * 8/2003 AAT: Removed dependence on offset and fileLen. + * + * NOTES + ***************************************************************************** + */ +int FindGRIBMsg (DataSource &fp, int msgNum, sInt4 *offset, int *curMsg) +{ + int cnt; /* The current message we are looking at. */ + char *buff; /* Holds the info between records. */ + uInt4 buffLen; /* Length of info between records. */ + sInt4 sect0[SECT0LEN_WORD]; /* Holds the current Section 0. */ + uInt4 gribLen; /* Length of the current GRIB message. */ + int version; /* Which version of GRIB is in this message. */ + int c; /* Determine if end of the file without fileLen. */ + sInt4 jump; /* How far to jump to get to past GRIB message. */ + + cnt = *curMsg + 1; + buff = NULL; + buffLen = 0; + while ((c = fp.DataSourceFgetc()) != EOF) { + fp.DataSourceUngetc(c); + if (cnt >= msgNum) { + /* 12/1/2004 version 1.63 forgot to free buff */ + free (buff); + *curMsg = cnt; + return 0; + } + /* Read section 0 to find gribLen and wmoLen. */ + if (ReadSECT0 (fp, &buff, &buffLen, GRIB_LIMIT, sect0, &gribLen, + &version) < 0) { + preErrSprintf ("Inside FindGRIBMsg\n"); + free (buff); + return -1; + } + myAssert ((version == 1) || (version == 2) || (version == -1)); + /* Continue on to the next grib message. */ + if ((version == 1) || (version == -1)) { + jump = gribLen - 8; + } else { + jump = gribLen - 16; + } + fp.DataSourceFseek(jump, SEEK_CUR); + *offset = *offset + gribLen + buffLen; + cnt++; + } + free (buff); + *curMsg = cnt - 1; + /* Return -2 since we reached the end of file. This may not be an error + * (multiple file option). */ + return -2; +/* + errSprintf ("ERROR: Ran out of file looking for msgNum %d.\n", msgNum); + errSprintf (" Current msgNum %d\n", cnt); +*/ +} + +/***************************************************************************** + * FindSectLen2to7() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Looks through a GRIB message and finds out the maximum size of each + * section. Simpler if there is only one grid in the message. + * + * ARGUMENTS + * c_ipack = The current GRIB2 message. (Input) + * gribLen = Length of c_ipack. (Input) + * ns = Array of section lengths. (Output) + * sectNum = Which section to start with. (Input) + * curTot = on going total read from c_ipack. (Input) + * nd2x3 = Total number of grid points (Output) + * table50 = Type of packing used. (See code table 5.0) (GS5_SIMPLE = 0, + * GS5_CMPLX = 2, GS5_CMPLXSEC = 3) (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = Ran of data in a section + * -2 = Section not properly labeled. + * + * HISTORY + * 3/2003 AAT: Created + * + * NOTES + * 1) Assumes that the pack method of multiple grids are the same. + ***************************************************************************** + */ +static int FindSectLen2to7 (char *c_ipack, sInt4 gribLen, sInt4 ns[8], + char sectNum, sInt4 *curTot, sInt4 *nd2x3, + short int *table50) +{ + sInt4 sectLen; /* The length of the current section. */ + sInt4 li_temp; /* A temporary holder of sInt4s. */ + + if ((sectNum == 2) || (sectNum == 3)) { + /* Figure out the size of section 2 and 3. */ + if (*curTot + 5 > gribLen) { + errSprintf ("ERROR: Ran out of data in Section 2 or 3\n"); + return -1; + } + /* Handle optional section 2. */ + if (c_ipack[*curTot + 4] == 2) { + MEMCPY_BIG (§Len, c_ipack + *curTot, 4); + *curTot = *curTot + sectLen; + if (ns[2] < sectLen) + ns[2] = sectLen; + if (*curTot + 5 > gribLen) { + errSprintf ("ERROR: Ran out of data in Section 3\n"); + return -1; + } + } + /* Handle section 3. */ + if (c_ipack[*curTot + 4] != 3) { + errSprintf ("ERROR: Section 3 labeled as %d\n", + c_ipack[*curTot + 4]); + return -2; + } + MEMCPY_BIG (§Len, c_ipack + *curTot, 4); + if (ns[3] < sectLen) + ns[3] = sectLen; + /* While we are here, grab the total number of grid points nd2x3. */ + MEMCPY_BIG (&li_temp, c_ipack + *curTot + 6, 4); + if (*nd2x3 < li_temp) + *nd2x3 = li_temp; + *curTot = *curTot + sectLen; + } +/* +#ifdef DEBUG + printf ("Section len (2=%ld) (3=%ld)\n", ns[2], ns[3]); +#endif +*/ + + /* Figure out the size of section 4. */ + if (*curTot + 5 > gribLen) { + errSprintf ("ERROR: Ran out of data in Section 4\n"); + return -1; + } + if (c_ipack[*curTot + 4] != 4) { + errSprintf ("ERROR: Section 4 labeled as %d\n", c_ipack[*curTot + 4]); + return -2; + } + MEMCPY_BIG (§Len, c_ipack + *curTot, 4); + if (ns[4] < sectLen) + ns[4] = sectLen; + *curTot = *curTot + sectLen; +/* +#ifdef DEBUG + printf ("Section len (4=%ld < %ld)\n", sectLen, ns[4]); +#endif +*/ + + /* Figure out the size of section 5. */ + if (*curTot + 5 > gribLen) { + errSprintf ("ERROR: Ran out of data in Section 5\n"); + return -1; + } + if (c_ipack[*curTot + 4] != 5) { + errSprintf ("ERROR: Section 5 labeled as %d\n", c_ipack[*curTot + 4]); + return -2; + } + MEMCPY_BIG (§Len, c_ipack + *curTot, 4); + /* While we are here, grab the packing method. */ + MEMCPY_BIG (table50, c_ipack + *curTot + 9, 2); + if (ns[5] < sectLen) + ns[5] = sectLen; + *curTot = *curTot + sectLen; +/* +#ifdef DEBUG + printf ("Section len (5=%ld < %ld)\n", sectLen, ns[5]); +#endif +*/ + + /* Figure out the size of section 6. */ + if (*curTot + 5 > gribLen) { + errSprintf ("ERROR: Ran out of data in Section 6\n"); + return -1; + } + if (c_ipack[*curTot + 4] != 6) { + errSprintf ("ERROR: Section 6 labeled as %d\n", c_ipack[*curTot + 4]); + return -2; + } + MEMCPY_BIG (§Len, c_ipack + *curTot, 4); + if (ns[6] < sectLen) + ns[6] = sectLen; + *curTot = *curTot + sectLen; +/* +#ifdef DEBUG + printf ("Section len (6=%ld < %ld)\n", sectLen, ns[6]); +#endif +*/ + + /* Figure out the size of section 7. */ + if (*curTot + 5 > gribLen) { + errSprintf ("ERROR: Ran out of data in Section 7\n"); + return -1; + } + if (c_ipack[*curTot + 4] != 7) { + errSprintf ("ERROR: Section 7 labeled as %d\n", c_ipack[*curTot + 4]); + return -2; + } + MEMCPY_BIG (§Len, c_ipack + *curTot, 4); + if (ns[7] < sectLen) + ns[7] = sectLen; + *curTot = *curTot + sectLen; +/* +#ifdef DEBUG + printf ("Section len (7=%ld < %ld)\n", sectLen, ns[7]); +#endif +*/ + return 0; +} + +/***************************************************************************** + * FindSectLen() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * Looks through a GRIB message and finds out how big each section is. + * + * ARGUMENTS + * c_ipack = The current GRIB2 message. (Input) + * gribLen = Length of c_ipack. (Input) + * ns = Array of section lengths. (Output) + * nd2x3 = Total number of grid points (Output) + * table50 = Type of packing used. (See code table 5.0) (GS5_SIMPLE = 0, + * GS5_CMPLX = 2, GS5_CMPLXSEC = 3) (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = Ran of data in a section + * -2 = Section not properly labeled. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 11/2002 AAT: Updated. + * 12/2002 (TK,AC,TB,&MS): Code Review. + * 3/2003 AAT: Made it handle multiple grids in the same GRIB2 message. + * 5/2003 AAT: Bug: Initialized size of section 2..6 to -1, instead + * of 2..7. + * + * NOTES + * 1) Assumes that the pack method of multiple grids are the same. + ***************************************************************************** + */ +static int FindSectLen (char *c_ipack, sInt4 gribLen, sInt4 ns[8], + sInt4 *nd2x3, short int *table50) +{ + sInt4 curTot; /* Where we are in the current GRIB message. */ + char sectNum; /* Which section we are working with. */ + int ans; /* The return error code of FindSectLen2to7. */ + sInt4 sectLen; /* The length of the current section. */ + int i; /* counter as we init ns[]. */ + + ns[0] = SECT0LEN_WORD * 4; + curTot = ns[0]; + + /* Figure out the size of section 1. */ + if (curTot + 5 > gribLen) { + errSprintf ("ERROR: Ran out of data in Section 1\n"); + return -1; + } + if (c_ipack[curTot + 4] != 1) { + errSprintf ("ERROR: Section 1 labeled as %d\n", c_ipack[curTot + 4]); + return -2; + } + MEMCPY_BIG (&(ns[1]), c_ipack + curTot, 4); + curTot += ns[1]; +/* +#ifdef DEBUG + printf ("Section len (0=%ld) (1=%ld)\n", ns[0], ns[1]); +#endif +*/ + sectNum = 2; + for (i = 2; i < 8; i++) { + ns[i] = -1; + } + *nd2x3 = -1; + do { + if ((ans = FindSectLen2to7 (c_ipack, gribLen, ns, sectNum, &curTot, + nd2x3, table50)) != 0) { + return ans; + } + /* Try to read section 8. If it is "7777" == 926365495 regardless of + * endian'ness then we have a simple message, otherwise it is complex, + * and we need to read more. */ + memcpy (§Len, c_ipack + curTot, 4); + if (sectLen == 926365495L) { + sectNum = 8; + } else { + sectNum = c_ipack[curTot + 4]; + if ((sectNum < 2) || (sectNum > 7)) { + errSprintf ("ERROR (FindSectLen): Couldn't find the end of the " + "message\n"); + errSprintf ("and it doesn't appear to repeat sections.\n"); + errSprintf ("so it is probably an ASCII / binary bug\n"); + errSprintf ("Max Sect Lengths: %ld %ld %ld %ld %ld %ld %ld" + " %ld\n", ns[0], ns[1], ns[2], ns[3], ns[4], ns[5], + ns[6], ns[7]); + return -2; + } + } + } while (sectNum != 8); + return 0; +} + +/***************************************************************************** + * IS_Init() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * Initialize the IS data structure. The IS_dataType is used to organize + * and allocate all the arrays that the unpack library uses. + * This makes an initial guess for the size of the arrays, and we use + * realloc to increase the size if needed. The write up: "UNPK_GRIB2 + * 3/15/02" by Bryon Lawrence, Bob Glahn, David Rudack suggested these numbers + * + * ARGUMENTS + * is = The data structure to initialize. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 11/2002 AAT : Updated. + * 12/2002 (TK,AC,TB,&MS): Code Review. + * + * NOTES + * 1) Numbers not found in document were discused with Bob Glahn on 8/29/2002 + * 2) Possible exceptions: + * template 3.120 could need ns[3] = 1600 + * template 4.30 could need a different ns4. + * 3) sizeof(float) == sizeof(sInt4), and unpacker does not use both ain + * and iain, so it is possible to have ain and iain point to the same + * memory. Not sure if this is safe, so I haven't done it. + ***************************************************************************** + */ +void IS_Init (IS_dataType *is) +{ + int i; /* A simple loop counter. */ + is->ns[0] = 16; + is->ns[1] = 21; + is->ns[2] = 7; + is->ns[3] = 96; + is->ns[4] = 130; /* 60->130 in case there are some S4 time intervals */ + is->ns[5] = 49; + is->ns[6] = 6; + is->ns[7] = 8; + for (i = 0; i < 8; i++) { + is->is[i] = (sInt4 *) calloc (is->ns[i], sizeof (sInt4)); + } + /* Allocate grid memory. */ + is->nd2x3 = 0; + is->iain = NULL; + is->ib = NULL; + /* Allocate section 2 int memory. */ + is->nidat = 0; + is->idat = NULL; + /* Allocate section 2 float memory. */ + is->nrdat = 0; + is->rdat = NULL; + /* Allocate storage for ipack. */ + is->ipackLen = 0; + is->ipack = NULL; +} + +/***************************************************************************** + * IS_Free() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * Free the memory allocated in the IS data structure. + * The IS_dataType is used to organize and allocate all the arrays that the + * unpack library uses. + * + * ARGUMENTS + * is = The data structure to Free. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 11/2002 AAT : Updated. + * 12/2002 (TK,AC,TB,&MS): Code Review. + * + * NOTES + ***************************************************************************** + */ +void IS_Free (IS_dataType *is) +{ + int i; /* A simple loop counter. */ + for (i = 0; i < 8; i++) { + free (is->is[i]); + is->is[i] = NULL; + is->ns[i] = 0; + } + /* Free grid memory. */ + free (is->iain); + is->iain = NULL; + free (is->ib); + is->ib = NULL; + is->nd2x3 = 0; + /* Free section 2 int memory. */ + free (is->idat); + is->idat = NULL; + is->nidat = 0; + /* Free section 2 float memory. */ + free (is->rdat); + is->rdat = NULL; + is->nrdat = 0; + /* Free storage for ipack. */ + free (is->ipack); + is->ipack = NULL; + is->ipackLen = 0; +} + +/***************************************************************************** + * ReadGrib2Record() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * Reads a GRIB2 message from a file which is already opened and is pointing + * at the correct message. It reads in the message storing the results in + * Grib_Data which is of size grib_DataLen. If needed, it increases + * grib_DataLen enough to fit the current message's grid. It converts (if + * appropriate) the data in Grib_Data to the units specified in f_unit. + * + * In addition it updates offset, and stores the meta data returned by the + * unpacker library in both IS, and (after parsing it) in meta. + * + * Note: It expects meta and IS to already be initialized through calls to + * MetaInit(&meta) and IS_Init(&is) respectively. + * + * ARGUMENTS + * fp = An opened GRIB2 file already at the correct message. (Input) + * fileLen = Length of the opened file. (Input) + * f_unit = 0 use GRIB2 units, 1 use English, 2 use metric. (Input) + * Grib_Data = The read in GRIB2 grid. (Output) + * grib_DataLen = Size of Grib_Data. (Output) + * meta = A filled in meta structure (Output) + * IS = The structure containing all the arrays that the + * unpacker uses (Output) + * subgNum = Which subgrid in the GRIB2 message is of interest. + * (0 = first grid), if it can't find message subgNum, + * returns -5, and an error message (Input) + * majEarth = Used to override the GRIB major axis of earth. (Input) + * minEarth = Used to override the GRIB minor axis of earth. (Input) + * simpVer = The version of the simple weather code to use when parsing + * the WxString. (Input) + * f_endMsg = 1 means we finished reading the previous GRIB message, or + * there was no previous GRIB message. 0 means that we need + * to read a subgrid of the previous message. (Input/Output) + * lwlt, uprt = If the lat is not -100, then lwlt, and uprt define a + * subgrid that the user is interested in. Get the map + * projection out of the GRIB2 message, and do everything + * on the subgrid. (if lwlt, and uprt are not "correct", the + * lat/lons may get swapped) (Input/Output) + * + * FILES/DATABASES: + * An already opened "GRIB2" File + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = Problems in section 0 + * -2 = Problems figuring out the Section Lengths. + * -3 = Error returned by unpack library. + * -4 = Problems parsing the Meta Data. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 11/2002 AAT: Updated. + * 12/2002 (TK,AC,TB,&MS): Code Review. + * 1/2003 AAT: It wasn't error coded 208, but rather 202 to look for. + * 3/2003 AAT: Modified handling of section 2 stuff (no loop) + * 3/2003 AAT: Added ability to handle multiple grids in same message. + * 4/2003 AAT: Added ability to call GRIB1 decoder for GRIB1 messages. + * 5/2003 AAT: Update the offset for ReadGrib1. + * 6/2003 Matthew T. Kallio (matt@wunderground.com): + * "wmo" dimension increased to WMO_HEADER_LEN + 1 (for '\0' char) + * 7/2003 AAT: switched to checking against element name for Wx instead + * of pds2.sect2.ptrType == GS2_WXTYPE + * 7/2003 AAT: Allowed user to override the radius of earth. + * 8/2003 AAT: Removed dependence on fileLen and offset. + * 2/2004 AAT: Added "f_endMsg" logic. + * 2/2004 AAT: Added subgrid potential. + * 2/2004 AAT: Added maj/min Earth override. + * + * NOTES + * 1) Reason ns[7] is not MAX (IS.ns[], local_ns[]) is because local_ns[7] + * is size of the packed message, but ns[7] refers to the returned meta + * data which unpacker library found in section 7, which is a lot smaller. + * 2) Problem: MDL's sect2 is packed and we have no idea how large it is + * when unpacked. So we allocate room for 4000 sInt4s and 500 floats. + * We then check 'jer' for error "202", if we find it we double the size + * and call the unpacker again. + * 3/26/2003: Changed this to be: try once with size + * = max (32 * packed size, 4000) + * Should be fewer calls (more memory intensive) same result, since we had + * been doubling it 5 times. + * 3) For Complex second order packing (GS5_CMPLXSEC) the unpacker needs nd5 + * (which is size of message) to be >= nd2x3 (size of grid). + * 3a) Appears to also need this if simple packing, and has a bitmap. + * 4) inew = 1: Currently we only expect 1 grid in 1 GRIB message, although + * the library does allow for multiple grids in a GRIB message. + * 5) iclean = 1: This only maters if there is bitmap data, otherwise it is + * ignored. For bitmap data, if == 0, it embeds the given values for + * xmissp, and xmisss. We don't embed because we don't know what to set + * xmissp or xmisss to. Instead after we know the range, we choose a value + * and walk through the bitmap setting grib_Data appropriately. + * 5a) iclean = 0; This is because we do want the missing values embeded. + * that is we want the missing values to be place holders. + * 6) f_endMsg is true if in the past we either completed reading a message, + * or we haven't read any messages. In either case we need to read the + * next message from file. If f_endMsg is false, then there is more to read + * from IS->ipack, so we don't want to throw it out, nor have to re-read + * ipack from disk. + * + * Question: Should we double ns[2] when we double nrdat, and nidat? + ***************************************************************************** + */ +#define SECT2_INIT_SIZE 4000 +#define UNPK_NUM_ERRORS 22 +int ReadGrib2Record (DataSource &fp, sChar f_unit, double **Grib_Data, + uInt4 *grib_DataLen, grib_MetaData *meta, + IS_dataType *IS, int subgNum, double majEarth, + double minEarth, int simpVer, sInt4 *f_endMsg, + CPL_UNUSED LatLon *lwlf, + CPL_UNUSED LatLon *uprt) +{ + sInt4 l3264b; /* Number of bits in a sInt4. Needed by FORTRAN + * unpack library to determine if system has a 4 + * byte_ sInt4 or an 8 byte sInt4. */ + char *buff; /* Holds the info between records. */ + uInt4 buffLen; /* Length of info between records. */ + sInt4 sect0[SECT0LEN_WORD]; /* Holds the current Section 0. */ + uInt4 gribLen; /* Length of the current GRIB message. */ + sInt4 nd5; /* Size of grib message rounded up to the nearest + * sInt4. */ + char *c_ipack; /* A char ptr to the message stored in IS->ipack */ + sInt4 local_ns[8]; /* Local copy of section lengths. */ + sInt4 nd2x3; /* Total number of grid points. */ + short int table50; /* Type of packing used. (See code table 5.0) + * (GS5_SIMPLE==0, GS5_CMPLX==2, GS5_CMPLXSEC==3) */ + sInt4 nidat; /* Size of section 2 if it contains integer data. */ + sInt4 nrdat; /* Size of section 2 if it contains float data. */ + sInt4 inew; /* 1 if this is the first grid we are reading. 0 if + * this is the second or later grid from the same + * GRIB message. */ + sInt4 iclean = 0; /* 0 embed the missing values, 1 don't. */ + int j; /* Counter used to find the desired subgrid. */ + sInt4 kfildo = 5; /* FORTRAN Unit number for diagnostic info. Ignored, + * unless library is compiled a particular way. */ + sInt4 ibitmap; /* 0 means no bitmap returned, otherwise 1. */ + float xmissp; /* The primary missing value. If iclean = 0, this + * value is embeded in grid, otherwise it is the + * value returned from the GRIB message. */ + float xmisss; /* The secondary missing value. If iclean = 0, this + * value is embeded in grid, otherwise it is the + * value returned from the GRIB message. */ + sInt4 jer[UNPK_NUM_ERRORS * 2]; /* Any Error codes along with their * + * severity levels generated using the * + * unpack GRIB2 library. */ + sInt4 ndjer = UNPK_NUM_ERRORS; /* The number of rows in JER( ). */ + sInt4 kjer; /* The actual number of errors returned in JER. */ + size_t i; /* counter as we loop through jer. */ + double unitM, unitB; /* values in y = m x + b used for unit conversion. */ + char unitName[15]; /* Holds the string name of the current unit. */ + int unitLen; /* String length of string name of current unit. */ + int version; /* Which version of GRIB is in this message. */ + sInt4 cnt; /* Used to help compact the weather table. */ + int x1, y1; /* The original grid coordinates of the lower left + * corner of the subgrid. */ + int x2, y2; /* The original grid coordinates of the upper right + * corner of the subgrid. */ + uChar f_subGrid; /* True if we have a subgrid. */ + sInt4 Nx, Ny; /* original size of the data. */ + + /* + * f_endMsg is 1 if in the past we either completed reading a message, + * or we haven't read any messages. In either case we need to read the + * next message from file. + * If f_endMsg is false, then there is more to read from IS->ipack, so we + * don't want to throw it out, nor have to re-read ipack from disk. + */ + l3264b = sizeof (sInt4) * 8; + buff = NULL; + buffLen = 0; + if (*f_endMsg == 1) { + if (ReadSECT0 (fp, &buff, &buffLen, -1, sect0, &gribLen, &version) < 0) { + preErrSprintf ("Inside ReadGrib2Record\n"); + free (buff); + return -1; + } + meta->GribVersion = version; + if (version == 1) { + if (ReadGrib1Record (fp, f_unit, Grib_Data, grib_DataLen, meta, IS, + sect0, gribLen, majEarth, minEarth) != 0) { + preErrSprintf ("Problems with ReadGrib1Record called by " + "ReadGrib2Record\n"); + free (buff); + return -1; + } + *f_endMsg = 1; + free (buff); + return 0; + } else if (version == -1) { + if (ReadTDLPRecord (fp, Grib_Data, grib_DataLen, meta, IS, + sect0, gribLen, majEarth, minEarth) != 0) { + preErrSprintf ("Problems with ReadGrib1Record called by " + "ReadGrib2Record\n"); + free (buff); + return -1; + } + free (buff); + return 0; + } + + /* + * Make room for entire message, and read it in. + */ + /* nd5 needs to be gribLen in (sInt4) units rounded up. */ + nd5 = (gribLen + 3) / 4; + if (nd5 > IS->ipackLen) { + IS->ipackLen = nd5; + IS->ipack = (sInt4 *) realloc ((void *) (IS->ipack), + (IS->ipackLen) * sizeof (sInt4)); + } + c_ipack = (char *) IS->ipack; + /* Init last sInt4 to 0, to make sure that the padded bytes are 0. */ + IS->ipack[nd5 - 1] = 0; + /* Init first 4 sInt4 to sect0. */ + memcpy (c_ipack, sect0, SECT0LEN_WORD * 4); + /* Read in the rest of the message. */ + if (fp.DataSourceFread (c_ipack + SECT0LEN_WORD * 4, sizeof (char), + (gribLen - SECT0LEN_WORD * 4)) != (gribLen - SECT0LEN_WORD * 4)) { + errSprintf ("GribLen = %ld, SECT0Len_WORD = %d\n", gribLen, + SECT0LEN_WORD); + errSprintf ("Ran out of file\n"); + free (buff); + return -1; + } + + /* + * Make sure the arrays are large enough for call to unpacker library. + */ + /* FindSectLen Does not want (ipack / c_ipack) word swapped, because + * that would make it much more confusing to find bytes in c_ipack. */ + if (FindSectLen (c_ipack, gribLen, local_ns, &nd2x3, &table50) < 0) { + preErrSprintf ("Inside ReadGrib2Record.. Calling FindSectLen\n"); + free (buff); + return -2; + } + + /* Make sure all 'is' arrays except ns[7] are MAX (IS.ns[] , + * local_ns[]). See note 1 for reason to exclude ns[7] from MAX (). */ + for (i = 0; i < 7; i++) { + if (local_ns[i] > IS->ns[i]) { + IS->ns[i] = local_ns[i]; + IS->is[i] = (sInt4 *) realloc ((void *) (IS->is[i]), + IS->ns[i] * sizeof (sInt4)); + } + } + + /* Allocate room for sect 2. If local_ns[2] = -1 there is no sect 2. */ + if (local_ns[2] == -1) { + nidat = 10; + nrdat = 10; + } else { + /* + * See note 2) We have a section 2, so use: + * MAX (32 * local_ns[2],SECT2_INTSIZE) + * and MAX (32 * local_ns[2],SECT2_FLOATSIZE) + * for size of section 2 unpacked. + */ + nidat = (32 * local_ns[2] < SECT2_INIT_SIZE) ? SECT2_INIT_SIZE : + 32 * local_ns[2]; + nrdat = nidat; + } + if (nidat > IS->nidat) { + IS->nidat = nidat; + IS->idat = (sInt4 *) realloc ((void *) IS->idat, + IS->nidat * sizeof (sInt4)); + } + if (nrdat > IS->nrdat) { + IS->nrdat = nrdat; + IS->rdat = (float *) realloc ((void *) IS->rdat, + IS->nrdat * sizeof (float)); + } + /* Make sure we have room for the GRID part of the output. */ + if (nd2x3 > IS->nd2x3) { + IS->nd2x3 = nd2x3; + IS->iain = (sInt4 *) realloc ((void *) IS->iain, + IS->nd2x3 * sizeof (sInt4)); + IS->ib = (sInt4 *) realloc ((void *) IS->ib, + IS->nd2x3 * sizeof (sInt4)); + } + /* See note 3) If table50 == 3, unpacker library needs nd5 >= nd2x3. */ + if ((table50 == 3) || (table50 == 0)) { + if (nd5 < nd2x3) { + nd5 = nd2x3; + if (nd5 > IS->ipackLen) { + IS->ipackLen = nd5; + IS->ipack = (sInt4 *) realloc ((void *) (IS->ipack), + IS->ipackLen * sizeof (sInt4)); + } + /* Don't need to do the following, but we do in case code + * changes. */ + c_ipack = (char *) IS->ipack; + } + } + IS->nd5 = nd5; + /* Unpacker library requires ipack to be MSB. */ +/* +#ifdef DEBUG + if (1==1) { + FILE *fp = fopen ("test.bin", "wb"); + fwrite (IS->ipack, sizeof (sInt4), IS->nd5, fp); + fclose (fp); + } +#endif +*/ +#ifdef LITTLE_ENDIAN + memswp (IS->ipack, sizeof (sInt4), IS->nd5); +#endif + } else { + gribLen = IS->ipack[3]; + } + free (buff); + + /* Loop through the grib message looking for the subgNum grid. subgNum + * goes from 0 to n-1. */ + for (j = 0; j <= subgNum; j++) { + if (j == 0) { + inew = 1; + } else { + inew = 0; + } + + /* Note we are getting data back either as a float or an int, but not + * both, so we don't need to allocated room for both. */ + unpk_grib2 (&kfildo, (float *) (IS->iain), IS->iain, &(IS->nd2x3), + IS->idat, &(IS->nidat), IS->rdat, &(IS->nrdat), IS->is[0], + &(IS->ns[0]), IS->is[1], &(IS->ns[1]), IS->is[2], + &(IS->ns[2]), IS->is[3], &(IS->ns[3]), IS->is[4], + &(IS->ns[4]), IS->is[5], &(IS->ns[5]), IS->is[6], + &(IS->ns[6]), IS->is[7], &(IS->ns[7]), IS->ib, &ibitmap, + IS->ipack, &(IS->nd5), &xmissp, &xmisss, &inew, &iclean, + &l3264b, f_endMsg, jer, &ndjer, &kjer); + /* + * Check for error messages... + * If we get an error message, print it, and return. + */ + for (i = 0; i < (uInt4) kjer; i++) { + if (jer[ndjer + i] == 0) { + /* no error. */ + } else if (jer[ndjer + i] == 1) { + /* Warning. */ +#ifdef DEBUG + printf ("Warning: Unpack library warning code (%d %d)\n", + jer[i], jer[ndjer + i]); +#endif + } else { + /* BAD Error. */ + errSprintf ("ERROR: Unpack library error code (%ld %ld)\n", + jer[i], jer[ndjer + i]); + return -3; + } + } + } + + /* Parse the meta data out. */ + if (MetaParse (meta, IS->is[0], IS->ns[0], IS->is[1], IS->ns[1], + IS->is[2], IS->ns[2], IS->rdat, IS->nrdat, IS->idat, + IS->nidat, IS->is[3], IS->ns[3], IS->is[4], IS->ns[4], + IS->is[5], IS->ns[5], gribLen, xmissp, xmisss, simpVer) + != 0) { +#ifdef DEBUG + FILE *fp; + if ((fp = fopen ("dump.is0", "wt")) != NULL) { + for (i = 0; i < 8; i++) { + fprintf (fp, "---Section %d---\n", (int) i); + for (j = 1; j <= IS->ns[i]; j++) { + fprintf (fp, "IS%d Item %d = %d\n", (int) i, (int) j, IS->is[i][j - 1]); + } + } + fclose (fp); + } +#endif + preErrSprintf ("Inside ReadGrib2Record.. Problems in MetaParse\n"); + return -4; + } + + if ((majEarth > 6000) && (majEarth < 7000)) { + if ((minEarth > 6000) && (minEarth < 7000)) { + meta->gds.f_sphere = 0; + meta->gds.majEarth = majEarth; + meta->gds.minEarth = minEarth; + } else { + meta->gds.f_sphere = 1; + meta->gds.majEarth = majEarth; + meta->gds.minEarth = majEarth; + } + } + + /* Figure out an equation to pass to ParseGrid to convert the units for + * this grid. */ +/* + if (ComputeUnit (meta->pds2.prodType, meta->pds2.sect4.templat, + meta->pds2.sect4.cat, meta->pds2.sect4.subcat, f_unit, + &unitM, &unitB, unitName) == 0) { +*/ + if (ComputeUnit (meta->convert, meta->unitName, f_unit, &unitM, &unitB, + unitName) == 0) { + unitLen = strlen (unitName); + meta->unitName = (char *) realloc ((void *) (meta->unitName), + 1 + unitLen * sizeof (char)); + strncpy (meta->unitName, unitName, unitLen); + meta->unitName[unitLen] = '\0'; + } + + /* compute the subgrid. */ + /* + if ((lwlf->lat != -100) && (uprt->lat != -100)) { + Nx = meta->gds.Nx; + Ny = meta->gds.Ny; + if (computeSubGrid (lwlf, &x1, &y1, uprt, &x2, &y2, &(meta->gds), + &newGds) != 0) { + preErrSprintf ("ERROR: In compute subgrid.\n"); + return 1; + } + // I couldn't decide if I should "permanently" change the GDS or not. + // when I wrote computeSubGrid. If next line stays, really should + // rewrite computeSubGrid. + memcpy (&(meta->gds), &newGds, sizeof (gdsType)); + f_subGrid = 1; + } else { + Nx = meta->gds.Nx; + Ny = meta->gds.Ny; + x1 = 1; + x2 = Nx; + y1 = 1; + y2 = Ny; + f_subGrid = 0; + } + */ + Nx = meta->gds.Nx; + Ny = meta->gds.Ny; + x1 = 1; + x2 = Nx; + y1 = 1; + y2 = Ny; + f_subGrid = 0; + + /* Figure out if we need iain or ain, and set it to Grib_Data. At the + * same time handle any bitmaps, and compute some statistics. */ + if ((f_subGrid) && (meta->gds.scan != 64)) { + errSprintf ("Can not do a subgrid of non scanmode 64 grid yet.\n"); + return -3; + } + + if (strcmp (meta->element, "Wx") != 0) { + ParseGrid (&(meta->gridAttrib), Grib_Data, grib_DataLen, Nx, Ny, + meta->gds.scan, IS->iain, ibitmap, IS->ib, unitM, unitB, 0, + NULL, f_subGrid, x1, y1, x2, y2); + } else { + /* Handle weather grid. ParseGrid looks up the values... If they are + * "" it sets it to missing (or creates one). If the table + * entry is used it sets f_valid to 2. */ + ParseGrid (&(meta->gridAttrib), Grib_Data, grib_DataLen, Nx, Ny, + meta->gds.scan, IS->iain, ibitmap, IS->ib, unitM, unitB, 1, + (sect2_WxType *) &(meta->pds2.sect2.wx), f_subGrid, x1, y1, + x2, y2); + + /* compact the table to only those which are actually used. */ + cnt = 0; + for (i = 0; i < meta->pds2.sect2.wx.dataLen; i++) { + if (meta->pds2.sect2.wx.ugly[i].f_valid == 2) { + meta->pds2.sect2.wx.ugly[i].validIndex = cnt; + cnt++; + } else if (meta->pds2.sect2.wx.ugly[i].f_valid == 3) { + meta->pds2.sect2.wx.ugly[i].f_valid = 0; + meta->pds2.sect2.wx.ugly[i].validIndex = cnt; + cnt++; + } else { + meta->pds2.sect2.wx.ugly[i].validIndex = -1; + } + } + } + + /* Figure out some other non-section oriented meta data. */ +/* strftime (meta->refTime, 20, "%Y%m%d%H%M", + gmtime (&(meta->pds2.refTime))); +*/ + Clock_Print (meta->refTime, 20, meta->pds2.refTime, "%Y%m%d%H%M", 0); +/* + strftime (meta->validTime, 20, "%Y%m%d%H%M", + gmtime (&(meta->pds2.sect4.validTime))); +*/ + Clock_Print (meta->validTime, 20, meta->pds2.sect4.validTime, + "%Y%m%d%H%M", 0); + + meta->deltTime = (sInt4) (meta->pds2.sect4.validTime - meta->pds2.refTime); + + return 0; +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/degrib2.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/degrib2.h new file mode 100644 index 000000000..7b30da270 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/degrib2.h @@ -0,0 +1,82 @@ +/***************************************************************************** + * degrib2.h + * + * DESCRIPTION + * This file contains the main driver routines to call the unpack grib2 + * library functions. It also contains the code needed to figure out the + * dimensions of the arrays before calling the FORTRAN library. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifndef DEGRIB2_H +#define DEGRIB2_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +/* Include type.h for uChar and sChar */ +#include "type.h" +#include "meta.h" + +#include "datasource.h" + +/* The IS_dataType is used to organize and allocate all the arrays that the + * unpack library uses. */ +typedef struct { + sInt4 ns[8]; /* Size of each section in bytes. */ + sInt4 *is[8]; /* Section data as sInt4s. */ + sInt4 nd2x3; /* Nx * Ny */ +/* float *ain; */ /* Size = Nd2x3. Holds the unpacked array if float. */ + sInt4 *iain; /* Size = Nd2x3. Holds the unpacked array if int. */ + sInt4 *ib; /* Size = Nd2x3. Hold and bitmasks.. */ + sInt4 nidat; /* Size of section 2 data if int. */ + sInt4 *idat; /* Section 2 data if int */ + sInt4 nrdat; /* Size of section 2 data if float. */ + float *rdat; /* Section 2 data if float. */ + sInt4 *ipack; /* The grib2 message in MSB as a sInt4 (input) */ + sInt4 ipackLen; /* The length of ipack. */ + sInt4 nd5; /* Size of current GRIB message rounded up to the + * nearest sInt4. nd5 <= ipackLen */ +} IS_dataType; + +void IS_Init (IS_dataType *is); +void IS_Free (IS_dataType *is); + +/* + * WMO_HEADER_LEN should be 19 + 21 + 21 (+19?) bytes for the first header + * WMO_SECOND_LEN should be 21 (+19?) bytes for subsequent ones. + * WMO_ORIG_LEN was 21, so I "grandfathered that in, in ReadSECT0. + * GRIB_LIMIT how many bytes to search for the GRIB message before giving up. + */ +#define WMO_HEADER_LEN 80 +#define WMO_SECOND_LEN 40 +#define WMO_HEADER_ORIG_LEN 21 +#define GRIB_LIMIT 300 + +#define SECT0LEN_WORD 4 +/* Possible error messages left in errSprintf() */ +int ReadSECT0 (DataSource &fp, char **buff, uInt4 *buffLen, sInt4 limit, + sInt4 sect0[SECT0LEN_WORD], uInt4 *gribLen, + int *version); + +/* Possible error messages left in errSprintf() */ +int ReadGrib2Record (DataSource &fp, sChar f_unit, double **Grib_Data, + uInt4 *grib_DataLen, grib_MetaData * meta, + IS_dataType * IS, int subgNum, double majEarth, + double minEarth, int simpVer, sInt4 * f_endMsg, + LatLon *lwlf, LatLon *uprt); + +/* Possible error messages left in errSprintf() */ +int FindGRIBMsg (DataSource &fp, int msg, sInt4 *offset, int *curMsg); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* DEGRIB2_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/engribapi.c b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/engribapi.c new file mode 100644 index 000000000..a115a4066 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/engribapi.c @@ -0,0 +1,1463 @@ +/***************************************************************************** + * engrib.c + * + * DESCRIPTION + * This file contains simple tools to fill out meta data section prior to + * calling NCEP GRIB2 encoding routines. + * + * HISTORY + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +#include +#include +#include +#include + +#include "grib2api.h" +#include "engribapi.h" +#include "myassert.h" +#include "gridtemplates.h" +#include "pdstemplates.h" +#include "drstemplates.h" + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#define GRIB2MISSING_1 (int) (0xff) +#define GRIB2MISSING_2 (int) (0xffff) +#define GRIB2MISSING_4 (sInt4) (0xffffffff) + +/***************************************************************************** + * NearestInt() -- Arthur Taylor / MDL + * + * PURPOSE + * Find the nearest integer to the given double. + * + * ARGUMENTS + * a = The given float. (Input) + * + * RETURNS: sInt4 (The nearest integer) + * + * 4/2006 Arthur Taylor (MDL): Commented (copied from pack.c). + * + * NOTES: + ***************************************************************************** + */ +static sInt4 NearestInt (double a) +{ + return (sInt4) floor (a + .5); +} + +/***************************************************************************** + * AdjustLon() -- Arthur Taylor / MDL + * + * PURPOSE + * Adjust the longitude so that it is in the range of 0..360. + * + * ARGUMENTS + * lon = The given longitude. (Input) + * + * RETURNS: double (a longitude in the correct range) + * + * 4/2006 Arthur Taylor (MDL): Created + * + * NOTES: + ***************************************************************************** + */ +static double AdjustLon (double lon) +{ + while (lon < 0) + lon += 360; + while (lon > 360) + lon -= 360; + return lon; +} + +/***************************************************************************** + * initEnGribMeta() -- Arthur Taylor / MDL + * + * PURPOSE + * Initialize the dynamic memory in the enGribMeta data structure. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * + * RETURNS: void + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +void initEnGribMeta (enGribMeta *en) +{ + en->sec2 = NULL; + en->lenSec2 = 0; + en->gdsTmpl = NULL; + en->lenGdsTmpl = 0; + en->idefList = NULL; + en->idefnum = 0; + en->pdsTmpl = NULL; + en->lenPdsTmpl = 0; + en->coordlist = NULL; + en->numcoord = 0; + en->drsTmpl = NULL; + en->lenDrsTmpl = 0; + en->fld = NULL; + en->ngrdpts = 0; + en->bmap = NULL; + en->ibmap = GRIB2MISSING_1; +} + +/***************************************************************************** + * freeEnGribMeta() -- Arthur Taylor / MDL + * + * PURPOSE + * Free the dynamic memory in the enGribMeta data structure. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * + * RETURNS: void + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +void freeEnGribMeta (enGribMeta *en) +{ + if (en->sec2 != NULL) { + free (en->sec2); + en->sec2 = NULL; + } + en->lenSec2 = 0; + if (en->gdsTmpl != NULL) { + free (en->gdsTmpl); + en->gdsTmpl = NULL; + } + en->lenGdsTmpl = 0; + if (en->idefList != NULL) { + free (en->idefList); + en->idefList = NULL; + } + en->idefnum = 0; + if (en->pdsTmpl != NULL) { + free (en->pdsTmpl); + en->pdsTmpl = NULL; + } + en->lenPdsTmpl = 0; + if (en->coordlist != NULL) { + free (en->coordlist); + en->coordlist = NULL; + } + en->numcoord = 0; + if (en->drsTmpl != NULL) { + free (en->drsTmpl); + en->drsTmpl = NULL; + } + en->lenDrsTmpl = 0; + if (en->fld != NULL) { + printf ("Freeing fld\n"); + free (en->fld); + en->fld = NULL; + } + en->ngrdpts = 0; + if (en->bmap != NULL) { + free (en->bmap); + en->bmap = NULL; + } + en->ibmap = GRIB2MISSING_1; +} + +/***************************************************************************** + * fillSect0() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 0 data. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * prodType = Discipline-GRIB Master Table [Code:0.0] (Input) + * + * RETURNS: void + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +void fillSect0 (enGribMeta *en, uChar prodType) +{ + en->sec0[0] = prodType; + en->sec0[1] = 2; +} + +/***************************************************************************** + * fillSect1() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 1 data. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * center = center of origin of data. (Input) + * subCenter = subCenter of origin of data. (Input) + * mstrVer = GRIB2 master table Version (currently 3) (Input) + * lclVer = GRIB2 local table version (typically 0) (Input) + * refCode = Significance of refTime [Code:1.2] (Input) + * refYear = The year of the reference time. (Input) + * refMonth = The month of the reference time. (Input) + * refDay = The day of the reference time. (Input) + * refHour = The hour of the reference time. (Input) + * refMin = The min of the reference time. (Input) + * refSec = The sec of the reference time. (Input) + * prodStat = Production Status of data [Code:1.3] (Input) + * typeData = Type of Data [Code:1.4] (Input) + * + * RETURNS: void + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +void fillSect1 (enGribMeta *en, uShort2 center, uShort2 subCenter, + uChar mstrVer, uChar lclVer, uChar refCode, sInt4 refYear, + int refMonth, int refDay, int refHour, int refMin, int refSec, + uChar prodStat, uChar typeData) +{ + en->sec1[0] = center; + en->sec1[1] = subCenter; + en->sec1[2] = mstrVer; + en->sec1[3] = lclVer; + en->sec1[4] = refCode; + en->sec1[5] = refYear; + en->sec1[6] = refMonth; + en->sec1[7] = refDay; + en->sec1[8] = refHour; + en->sec1[9] = refMin; + en->sec1[10] = refSec; + en->sec1[11] = prodStat; + en->sec1[12] = typeData; +} + +/***************************************************************************** + * fillSect2() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 2 data. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * sec2 = Character array with info to be added in Sect 2. (Input) + * lenSec2 = Num of bytes of sec2 to be added to Sect 2. (Input) + * + * RETURNS: void + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +void fillSect2 (enGribMeta *en, uChar *sec2, sInt4 lenSec2) +{ + if (lenSec2 == 0) { + if (en->sec2 != NULL) { + free (en->sec2); + en->sec2 = NULL; + } + en->lenSec2 = 0; + return; + } + if (lenSec2 > en->lenSec2) { + if (en->sec2 != NULL) { + free (en->sec2); + } + en->sec2 = (uChar *) malloc (lenSec2 * sizeof (char)); + } + en->lenSec2 = lenSec2; + memcpy (en->sec2, sec2, lenSec2); +} + +/***************************************************************************** + * getShpEarth() -- Arthur Taylor / MDL + * + * PURPOSE + * Given a major Earth axis and a minor Earth axis, determine how to store + * it in GRIB2. + * + * ARGUMENTS + * majEarth = major axis of earth in km (Input) + * minEarth = minor axis of earth in km (Input) + * shapeEarth = [Code:3.2] shape of the Earth defined by GRIB2 (Output) + * factRad = Scale factor of radius of spherical Earth. (Output) + * valRad = Value of radius of spherical Earth (Output) + * factMaj = Scale factor of major axis of eliptical Earth. (Output) + * valMaj = Value of major axis of eliptical Earth (Output) + * factMin = Scale factor of minor axis of eliptical Earth. (Output) + * valMin = Value of minor axis of eliptical Earth (Output) + * + * RETURNS: void + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +static void getShpEarth (double majEarth, double minEarth, sInt4 * shapeEarth, + sInt4 * factRad, sInt4 * valRad, sInt4 * factMaj, + sInt4 * valMaj, sInt4 * factMin, sInt4 * valMin) +{ + *factRad = 0; + *factMaj = 0; + *factMin = 0; + *valRad = 0; + *valMaj = 0; + *valMin = 0; + if (majEarth == minEarth) { + if (majEarth == 6367.47) { + *shapeEarth = 0; + *valRad = 6367470; + } else if (majEarth == 6371.229) { + *shapeEarth = 6; + *valRad = 6371229; + } else { + *shapeEarth = 1; + *valRad = NearestInt (majEarth * 1000); + } + } else { + if ((majEarth == 6378.16) && (minEarth == 6356.775)) { + *shapeEarth = 2; + *valMaj = 6378160; + *valMin = 6356775; + } else if ((majEarth == 6378.137) && (minEarth == 6356.752314)) { + *shapeEarth = 4; + *valMaj = 6378137; + /* Should this be 3 or -3? */ + *factMin = 2; + /* 6 356 752 314 > Max unsigned Long Int */ + *valMin = 635675231; + } else { + *shapeEarth = 7; + *valMaj = NearestInt (majEarth * 1000); + *valMin = NearestInt (majEarth * 1000); + } + } +} + +/***************************************************************************** + * fillSect3() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 3 data. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * tmplNum = [Code Table 3.1] Grid Definition Template Number. (Input) + * majEarth = major axis of earth in km (Input) + * minEarth = minor axis of earth in km (Input) + * Nx = Number of X coordinates (Input) + * Ny = Number of Y coordinates (Input) + * lat1 = latitude of first grid point (Input) + * lon1 = longitude of first grid point (Input) + * [tmplNum=0,10] + * lat2 = latitude of last grid point (Input) + * lon2 = longitude of last grid point (Input) + * [tmplNum=*] + * Dx = Grid Length in X direction (Input) + * Dy = Grid length in Y direction (Input) + * resFlag = [Code Table 3.3] Resolution and components flag (Input) + * (bit 3 = flag of i direction given) + * (bit 4 = flag of j direction given) + * (bit 5 = flag of u/v relative to grid) (typically 0) + * scanFlag = [Code Table 3.4] Scanning mode flag (Input) + * (bit 1 = flag of flow in -i direction) + * (bit 2 = flag of flow in +j direction) + * (bit 3 = column oriented (varies by column faster than row) + * (bit 4 = flag of boustophotonic) (typically 64) + * [tmplNum=20,30] + * centerFlag = [Code Table 3.5] Center flag (Input) + * (bit 1 = flag of south pole on plane) + * (bit 2 = flag of bi-polar projection) (typically 0) + * [tmplNum=0] + * angle = rule 92.1.6 may not hold, in which case, angle != 0, and + * unit = angle/subdivision. Typically 0 (Input) + * subDivis = 0 or see angle explaination. (Input) + * [tmplNum=10,20,30] + * meshLat = Latitude where Dx and Dy are specified (Input) + * orientLon = Orientation of the grid (Input) + * [tmplNum=30] + * scaleLat1 = The tangent latitude. If differs from scaleLat2, then they + * the two are the latitudes where the scale should be equal. + * One can compute a tangent latitude so that the scale at + * scaleLat1 is the same as the scale at scaleLat2. (Input) + * scaleLat2 = see scaleLat1. (Input) + * southLat = latitude of the south pole of the projection (Input) + * southLon = longitude of the south pole of the projection (Input) + * + * RETURNS: int + * > 0 (length of section 3). + * -1 if numExOctet != 0 (can't handle idefList yet) + * -2 not in list of templates supported by NCEP + * -3 can't determine the unit. (see angle / subDivis) + * -4 haven't finished mapping this projection to the template. + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +int fillSect3 (enGribMeta *en, uShort2 tmplNum, double majEarth, + double minEarth, sInt4 Nx, sInt4 Ny, double lat1, double lon1, + double lat2, double lon2, double Dx, double Dy, uChar resFlag, + uChar scanFlag, uChar centerFlag, sInt4 angle, sInt4 subDivis, + double meshLat, double orientLon, double scaleLat1, + double scaleLat2, double southLat, double southLon) +{ + const struct gridtemplate *templatesgrid = get_templatesgrid(); + int i; /* loop counter over number of GDS templates. */ + double unit; /* Used to convert from stored value to degrees + * lat/lon. See GRIB2 Regulation 92.1.6 */ + + if (tmplNum == 65535) { + /* can't handle lack of a grid definition template */ + return -1; + } + /* srcGridDef = [Code:3.0] 0 => Use a grid template + * 1 => predetermined grid (may not have grid template) + * 255 => means no grid applies (no grid def applies) + * for 1,255 tempateNum = 65535 means no grid template. + */ + en->gds[0] = 0; + en->gds[1] = Nx * Ny; + /* numExOctet = Number of octets needed for each additional grid points + * definition. Used to define number of points in each row (or column) + * for non-regular grids. 0, if using regular grid. */ + en->gds[2] = 0; + /* interpList = [Code Table 3.11] Interpretation of list for optional points + * definition. 0 if no appended list. */ + en->gds[3] = 0; + en->gds[4] = tmplNum; + + /* Find NCEP's template match */ + for (i = 0; i < MAXGRIDTEMP; i++) { + if (templatesgrid[i].template_num == tmplNum) { + break; + } + } + if (i == MAXGRIDTEMP) { + /* not in list of templates supported by NCEP */ + return -2; + } + if (templatesgrid[i].needext) { + /* can't handle idefList yet. */ + return -1; + } + + if (en->lenGdsTmpl < templatesgrid[i].mapgridlen) { + if (en->gdsTmpl != NULL) { + free (en->gdsTmpl); + } + en->gdsTmpl = (sInt4 *) malloc (templatesgrid[i].mapgridlen * + sizeof (sInt4)); + } + en->lenGdsTmpl = templatesgrid[i].mapgridlen; + + /* using 1 / 10^-6 to reduce division later */ + unit = 1e6; + /* lat/lon grid */ + if (tmplNum == 0) { + getShpEarth (majEarth, minEarth, &(en->gdsTmpl[0]), &(en->gdsTmpl[1]), + &(en->gdsTmpl[2]), &(en->gdsTmpl[3]), &(en->gdsTmpl[4]), + &(en->gdsTmpl[5]), &(en->gdsTmpl[6])); + en->gdsTmpl[7] = Nx; + en->gdsTmpl[8] = Ny; + en->gdsTmpl[9] = angle; + en->gdsTmpl[10] = subDivis; + if (angle != 0) { + if (subDivis == 0) { + /* can't determine the unit. */ + return -3; + } + /* using 1 / (angle / subdivis) to reduce division later */ + unit = subDivis / (double) angle; + } + en->gdsTmpl[11] = NearestInt (lat1 * unit); + en->gdsTmpl[12] = NearestInt (AdjustLon (lon1) * unit); + en->gdsTmpl[13] = resFlag; + en->gdsTmpl[14] = NearestInt (lat2 * unit); + en->gdsTmpl[15] = NearestInt (AdjustLon (lon2) * unit); + en->gdsTmpl[16] = NearestInt (Dx * unit); + en->gdsTmpl[17] = NearestInt (Dy * unit); + en->gdsTmpl[18] = scanFlag; + return 72; + /* mercator grid */ + } else if (tmplNum == 10) { + getShpEarth (majEarth, minEarth, &(en->gdsTmpl[0]), &(en->gdsTmpl[1]), + &(en->gdsTmpl[2]), &(en->gdsTmpl[3]), &(en->gdsTmpl[4]), + &(en->gdsTmpl[5]), &(en->gdsTmpl[6])); + en->gdsTmpl[7] = Nx; + en->gdsTmpl[8] = Ny; + en->gdsTmpl[9] = NearestInt (lat1 * unit); + en->gdsTmpl[10] = NearestInt (AdjustLon (lon1) * unit); + en->gdsTmpl[11] = resFlag; + en->gdsTmpl[12] = NearestInt (meshLat * unit); + en->gdsTmpl[13] = NearestInt (lat2 * unit); + en->gdsTmpl[14] = NearestInt (AdjustLon (lon2) * unit); + en->gdsTmpl[15] = scanFlag; + en->gdsTmpl[16] = NearestInt (AdjustLon (orientLon) * unit); + en->gdsTmpl[17] = NearestInt (Dx * 1000.); + en->gdsTmpl[18] = NearestInt (Dy * 1000.); + return 72; + /* polar grid */ + } else if (tmplNum == 20) { + getShpEarth (majEarth, minEarth, &(en->gdsTmpl[0]), &(en->gdsTmpl[1]), + &(en->gdsTmpl[2]), &(en->gdsTmpl[3]), &(en->gdsTmpl[4]), + &(en->gdsTmpl[5]), &(en->gdsTmpl[6])); + en->gdsTmpl[7] = Nx; + en->gdsTmpl[8] = Ny; + en->gdsTmpl[9] = NearestInt (lat1 * unit); + en->gdsTmpl[10] = NearestInt (AdjustLon (lon1) * unit); + en->gdsTmpl[11] = resFlag; + en->gdsTmpl[12] = NearestInt (meshLat * unit); + en->gdsTmpl[13] = NearestInt (AdjustLon (orientLon) * unit); + en->gdsTmpl[14] = NearestInt (Dx * 1000.); + en->gdsTmpl[15] = NearestInt (Dy * 1000.); + en->gdsTmpl[16] = centerFlag; + en->gdsTmpl[17] = scanFlag; + return 65; + /* lambert grid */ + } else if (tmplNum == 30) { + getShpEarth (majEarth, minEarth, &(en->gdsTmpl[0]), &(en->gdsTmpl[1]), + &(en->gdsTmpl[2]), &(en->gdsTmpl[3]), &(en->gdsTmpl[4]), + &(en->gdsTmpl[5]), &(en->gdsTmpl[6])); + en->gdsTmpl[7] = Nx; + en->gdsTmpl[8] = Ny; + en->gdsTmpl[9] = NearestInt (lat1 * unit); + en->gdsTmpl[10] = NearestInt (AdjustLon (lon1) * unit); + en->gdsTmpl[11] = resFlag; + en->gdsTmpl[12] = NearestInt (meshLat * unit); + en->gdsTmpl[13] = NearestInt (AdjustLon (orientLon) * unit); + en->gdsTmpl[14] = NearestInt (Dx * 1000.); + en->gdsTmpl[15] = NearestInt (Dy * 1000.); + en->gdsTmpl[16] = centerFlag; + en->gdsTmpl[17] = scanFlag; + en->gdsTmpl[18] = NearestInt (scaleLat1 * unit); + en->gdsTmpl[19] = NearestInt (scaleLat2 * unit); + en->gdsTmpl[20] = NearestInt (southLat * unit); + en->gdsTmpl[21] = NearestInt (AdjustLon (southLon) * unit); + return 81; + } + /* Haven't finished mapping this projection to the template. */ + return -4; +} + +/***************************************************************************** + * getCodedTime() -- Arthur Taylor / MDL + * + * PURPOSE + * Change from seconds to the time units provided in timeCode. + * + * ARGUMENTS + * timeCode = The time units to convert into (see code table 4.4). (Input) + * time = The time in seconds to convert. (Input) + * ans = The converted answer. (Output) + * + * RETURNS: int + * 0 = OK + * -1 = could not determine. + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +static int getCodedTime (uChar timeCode, double time, sInt4 *ans) +{ + /* Following is a lookup table for unit conversion (see code table 4.4). */ + static sInt4 unit2sec[] = { + 60, 3600, 86400L, 0, 0, + 0, 0, 0, 0, 0, + 10800, 21600L, 43200L, 1 + }; + + if (timeCode < 14) { + if (unit2sec[timeCode] != 0) { + *ans = NearestInt (time / unit2sec[timeCode]); + return 0; + } + } + *ans = 0; + return -1; +} + +/***************************************************************************** + * fillSect4_0() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 4 (using template 0) data. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * tmplNum = [Code Table 4.0] Product Definition Template Number (Input) + * cat = [Code Table 4.1] General category of Meteo Product. (Input) + * subCat = [Code Table 4.2] Specific subcategory of Meteo Product. (In) + * genProcess = [Code Table 4.3] type of generating process (Analysis, + * Forecast, Probability Forecast, etc) (Input) + * bgGenID = Background generating process id. (Input) + * genID = Analysis/Forecast generating process id. (Input) + * f_valCutOff = Flag if we have a valid cutoff time (Input) + * cutOff = Cut off time for forecast (Input) + * timeCode = [Code Table 4.4] Unit of time to store in (Input) + * foreSec = Forecast time in seconds (Input) + * surfType1 = [Code Table 4.5] Type of the first surface (Input) + * surfScale1 = scale amount for the first surface (Input) + * dSurfVal1 = value of the first surface (before scaling) (Input) + * surfType2 = [Code Table 4.5] Type of the second surface (Input) + * surfScale2 = scale amount for the second surface (Input) + * dSurfVal2 = value of the second surface (before scaling) (Input) + * + * RETURNS: int + * > 0 (length of section 4). + * -1 This is specifically for template 4.0 (1,2,5,8,8,12) + * -2 not in list of templates supported by NCEP + * -3 can't handle the timeCode. + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +int fillSect4_0 (enGribMeta *en, uShort2 tmplNum, uChar cat, uChar subCat, + uChar genProcess, uChar bgGenID, uChar genID, + uChar f_valCutOff, sInt4 cutOff, uChar timeCode, + double foreSec, uChar surfType1, sChar surfScale1, + double dSurfVal1, uChar surfType2, sChar surfScale2, + double dSurfVal2) +{ + int i; /* loop counter over number of PDS templates. */ + const struct pdstemplate *templatespds = get_templatespds(); + + /* analysis template (0) */ + /* In addition templates (1, 2, 5, 8, 9, 12) begin with 4.0 info. */ + if ((tmplNum != 0) && (tmplNum != 1) && (tmplNum != 2) && (tmplNum != 5) && + (tmplNum != 8) && (tmplNum != 9) && (tmplNum != 10) && + (tmplNum != 12)) { + /* This is specifically for template 4.0 (1,2,5,8,9,10,12) */ + return -1; + } + en->ipdsnum = tmplNum; + + /* Find NCEP's template match */ + for (i = 0; i < MAXPDSTEMP; i++) { + if (templatespds[i].template_num == tmplNum) { + break; + } + } + if (i == MAXPDSTEMP) { + /* not in list of templates supported by NCEP */ + return -2; + } + /* Allocate memory for it. */ + if (en->lenPdsTmpl < templatespds[i].mappdslen) { + if (en->pdsTmpl != NULL) { + free (en->pdsTmpl); + } + en->pdsTmpl = (sInt4 *) malloc (templatespds[i].mappdslen * + sizeof (sInt4)); + } + en->lenPdsTmpl = templatespds[i].mappdslen; + + en->pdsTmpl[0] = cat; + en->pdsTmpl[1] = subCat; + en->pdsTmpl[2] = genProcess; + en->pdsTmpl[3] = bgGenID; + en->pdsTmpl[4] = genID; + if (f_valCutOff) { + en->pdsTmpl[5] = cutOff / 3600; + en->pdsTmpl[6] = (cutOff - en->pdsTmpl[5] * 3600) / 60; + } else { + en->pdsTmpl[5] = GRIB2MISSING_2; + en->pdsTmpl[6] = GRIB2MISSING_1; + } + en->pdsTmpl[7] = timeCode; + if (getCodedTime (timeCode, foreSec, &(en->pdsTmpl[8])) != 0) { + /* can't handle this time code yet. */ + return -3; + } + en->pdsTmpl[9] = surfType1; + if (surfType1 == GRIB2MISSING_1) { + en->pdsTmpl[10] = GRIB2MISSING_1; + en->pdsTmpl[11] = GRIB2MISSING_4; + } else { + en->pdsTmpl[10] = surfScale1; + en->pdsTmpl[11] = NearestInt (dSurfVal1 * pow (10.0, surfScale1)); + } + en->pdsTmpl[12] = surfType2; + if (surfType2 == GRIB2MISSING_1) { + en->pdsTmpl[13] = GRIB2MISSING_1; + en->pdsTmpl[14] = GRIB2MISSING_4; + } else { + en->pdsTmpl[13] = surfScale2; + en->pdsTmpl[14] = NearestInt (dSurfVal2 * pow (10.0, surfScale2)); + } + return 34; +} + +/***************************************************************************** + * fillSect4_1() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 4 (using template 1) data. Call fillSect4_0 first. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * tmplNum = [Code Table 4.0] Product Definition Template Number (Input) + * typeEnsemble = [Code Table 4.6] Type of ensemble (Input) + * perturbNum = Perturbation number (Input) + * numFcsts = number of forecasts (Input) + * + * RETURNS: int + * > 0 (length of section 4). + * -1 if not template 4.1, or fillSect4_0 wasn't already called. + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +int fillSect4_1 (enGribMeta *en, uShort2 tmplNum, uChar typeEnsemble, + uChar perturbNum, uChar numFcsts) +{ + /* ensemble tempate (1) */ + if (tmplNum != 1) { + /* This is specifically for template 4.1 */ + return -1; + } + if (en->ipdsnum != tmplNum) { + /* Didn't call fillSect4_0 first */ + return -1; + } + en->pdsTmpl[15] = typeEnsemble; + en->pdsTmpl[16] = perturbNum; + en->pdsTmpl[17] = numFcsts; + return 37; +} + +/***************************************************************************** + * fillSect4_2() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 4 (using template 2) data. Call fillSect4_0 first. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * tmplNum = [Code Table 4.0] Product Definition Template Number (Input) + * numFcsts = number of forecasts (Input) + * derivedFcst = [Code Table 4.7] Derived forecast type (Input) + * + * RETURNS: int + * > 0 (length of section 4). + * -1 if not template 4.2, or fillSect4_0 wasn't already called. + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +int fillSect4_2 (enGribMeta *en, uShort2 tmplNum, uChar numFcsts, + uChar derivedFcst) +{ + /* derived template (2) */ + if (tmplNum != 2) { + /* This is specifically for template 4.2 */ + return -1; + } + if (en->ipdsnum != tmplNum) { + /* Didn't call fillSect4_0 first */ + return -1; + } + en->pdsTmpl[15] = derivedFcst; + en->pdsTmpl[16] = numFcsts; + return 36; +} + +/***************************************************************************** + * fillSect4_5() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 4 (using template 5) data. Call fillSect4_0 first. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * tmplNum = [Code Table 4.0] Product Definition Template Number (Input) + * numFcsts = number of forecasts (Input) + * foreProbNum = Forecast Probability number (Input) + * probType = [Code Table 4.9] (Input) + * 0 probability of event below lower limit + * 1 probability of event above upper limit + * 2 probability of event between lower (inclusive) and upper + * 3 probability of event above lower limit + * 4 probability of event below upper limit + * lowScale = scale amount for the lower limit (Input) + * dlowVal = value of the lower limit (before scaling) (Input) + * upScale = scale amount for the upper limit (Input) + * dupVal = value of the upper limit (before scaling) (Input) + * + * RETURNS: int + * > 0 (length of section 4). + * -1 if not template 4.5, or fillSect4_0 wasn't already called. + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +int fillSect4_5 (enGribMeta *en, uShort2 tmplNum, uChar numFcsts, + uChar foreProbNum, uChar probType, sChar lowScale, + double dlowVal, sChar upScale, double dupVal) +{ + /* Point Probability template */ + if (tmplNum != 5) { + /* This is specifically for template 4.5 */ + return -1; + } + if (en->ipdsnum != tmplNum) { + /* Didn't call fillSect4_0 first */ + return -1; + } + en->pdsTmpl[15] = foreProbNum; + en->pdsTmpl[16] = numFcsts; + en->pdsTmpl[17] = probType; + if ((uChar) lowScale == GRIB2MISSING_1) { + en->pdsTmpl[18] = GRIB2MISSING_1; + en->pdsTmpl[19] = GRIB2MISSING_4; + } else { + en->pdsTmpl[18] = lowScale; + en->pdsTmpl[19] = NearestInt (dlowVal * pow (10.0, lowScale)); + } + if ((uChar) upScale == GRIB2MISSING_1) { + en->pdsTmpl[20] = GRIB2MISSING_1; + en->pdsTmpl[21] = GRIB2MISSING_4; + } else { + en->pdsTmpl[20] = upScale; + en->pdsTmpl[21] = NearestInt (dupVal * pow (10.0, upScale)); + } + return 47; +} + +/***************************************************************************** + * fillSect4_8() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 4 (using template 8) data. Call fillSect4_0 first. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * tmplNum = [Code Table 4.0] Product Definition Template Number (Input) + * endYear = The year of the end time (valid Time). (Input) + * endMonth = The month of the end time (valid Time). (Input) + * endDay = The day of the end time (valid Time). (Input) + * endHour = The hour of the end time (valid Time). (Input) + * endMin = The min of the end time (valid Time). (Input) + * endSec = The sec of the end time (valid Time). (Input) + * numInterval = num of time range specifications (Has to = 1) (Input) + * numMissing = total num of missing values in statistical process (Input) + * interval = time range intervals. + * + * RETURNS: int + * > 0 (length of section 4). + * -1 if not template 4.8, or fillSect4_0 wasn't already called. + * -4 can only handle 1 and only 1 time interval + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +int fillSect4_8 (enGribMeta *en, uShort2 tmplNum, sInt4 endYear, int endMonth, + int endDay, int endHour, int endMin, int endSec, + uChar numInterval, sInt4 numMissing, + sect4IntervalType * interval) +{ + int j; /* loop counter over number of intervals. */ + + /* statistic template (8) */ + if (tmplNum != 8) { + /* This is specifically for template 4.8 */ + return -1; + } + if (en->ipdsnum != tmplNum) { + /* Didn't call fillSect4_0 first */ + return -1; + } + en->pdsTmpl[15] = endYear; + en->pdsTmpl[16] = endMonth; + en->pdsTmpl[17] = endDay; + en->pdsTmpl[18] = endHour; + en->pdsTmpl[19] = endMin; + en->pdsTmpl[20] = endSec; + en->pdsTmpl[21] = numInterval; + if (numInterval != 1) { + /* can only handle 1 and only 1 time interval */ + return -4; + } + en->pdsTmpl[22] = numMissing; + for (j = 0; j < numInterval; j++) { + en->pdsTmpl[23] = interval[j].processID; + en->pdsTmpl[24] = interval[j].incrType; + en->pdsTmpl[25] = interval[j].timeRangeUnit; + en->pdsTmpl[26] = interval[j].lenTime; + en->pdsTmpl[27] = interval[j].incrUnit; + en->pdsTmpl[28] = interval[j].timeIncr; + } + return 58; +} + +/***************************************************************************** + * fillSect4_9() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 4 (using template 9) data. Call fillSect4_0 first. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * tmplNum = [Code Table 4.0] Product Definition Template Number (Input) + * numFcsts = number of forecasts (Input) + * foreProbNum = Forecast Probability number (Input) + * probType = [Code Table 4.9] (Input) + * 0 probability of event below lower limit + * 1 probability of event above upper limit + * 2 probability of event between lower (inclusive) and upper + * 3 probability of event above lower limit + * 4 probability of event below upper limit + * lowScale = scale amount for the lower limit (Input) + * dlowVal = value of the lower limit (before scaling) (Input) + * upScale = scale amount for the upper limit (Input) + * dupVal = value of the upper limit (before scaling) (Input) + * endYear = The year of the end time (valid Time). (Input) + * endMonth = The month of the end time (valid Time). (Input) + * endDay = The day of the end time (valid Time). (Input) + * endHour = The hour of the end time (valid Time). (Input) + * endMin = The min of the end time (valid Time). (Input) + * endSec = The sec of the end time (valid Time). (Input) + * numInterval = num of time range specifications (Has to = 1) (Input) + * numMissing = total num of missing values in statistical process (Input) + * interval = time range intervals. + * + * RETURNS: int + * > 0 (length of section 4). + * -1 if not template 4.9, or fillSect4_0 wasn't already called. + * -4 can only handle 1 and only 1 time interval + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +int fillSect4_9 (enGribMeta *en, uShort2 tmplNum, uChar numFcsts, + uChar foreProbNum, uChar probType, sChar lowScale, + double dlowVal, sChar upScale, double dupVal, sInt4 endYear, + int endMonth, int endDay, int endHour, int endMin, + int endSec, uChar numInterval, sInt4 numMissing, + sect4IntervalType * interval) +{ + int j; /* loop counter over number of intervals. */ + + /* probability time template (9) */ + if (tmplNum != 9) { + /* This is specifically for template 4.9 */ + return -1; + } + if (en->ipdsnum != tmplNum) { + /* Didn't call fillSect4_0 first */ + return -1; + } + en->pdsTmpl[15] = foreProbNum; + en->pdsTmpl[16] = numFcsts; + en->pdsTmpl[17] = probType; + if ((uChar) lowScale == GRIB2MISSING_1) { + en->pdsTmpl[18] = GRIB2MISSING_1; + en->pdsTmpl[19] = GRIB2MISSING_4; + } else { + en->pdsTmpl[18] = lowScale; + en->pdsTmpl[19] = NearestInt (dlowVal * pow (10.0, lowScale)); + } + if ((uChar) upScale == GRIB2MISSING_1) { + en->pdsTmpl[20] = GRIB2MISSING_1; + en->pdsTmpl[21] = GRIB2MISSING_4; + } else { + en->pdsTmpl[20] = upScale; + en->pdsTmpl[21] = NearestInt (dupVal * pow (10.0, upScale)); + } + en->pdsTmpl[22] = endYear; + en->pdsTmpl[23] = endMonth; + en->pdsTmpl[24] = endDay; + en->pdsTmpl[25] = endHour; + en->pdsTmpl[26] = endMin; + en->pdsTmpl[27] = endSec; + en->pdsTmpl[28] = numInterval; + if (numInterval != 1) { + /* can only handle 1 and only 1 time interval */ + return -4; + } + en->pdsTmpl[29] = numMissing; + for (j = 0; j < numInterval; j++) { + en->pdsTmpl[30] = interval[j].processID; + en->pdsTmpl[31] = interval[j].incrType; + en->pdsTmpl[32] = interval[j].timeRangeUnit; + en->pdsTmpl[33] = interval[j].lenTime; + en->pdsTmpl[34] = interval[j].incrUnit; + en->pdsTmpl[35] = interval[j].timeIncr; + } + return 71; +} + +/***************************************************************************** + * fillSect4_10() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 4 (using template 10) data. Call fillSect4_0 first. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * tmplNum = [Code Table 4.0] Product Definition Template Number (Input) + * percentile = Percentile value. (Input) + * endYear = The year of the end time (valid Time). (Input) + * endMonth = The month of the end time (valid Time). (Input) + * endDay = The day of the end time (valid Time). (Input) + * endHour = The hour of the end time (valid Time). (Input) + * endMin = The min of the end time (valid Time). (Input) + * endSec = The sec of the end time (valid Time). (Input) + * numInterval = num of time range specifications (Has to = 1) (Input) + * numMissing = total num of missing values in statistical process (Input) + * interval = time range intervals. + * + * RETURNS: int + * > 0 (length of section 4). + * -1 if not template 4.9, or fillSect4_0 wasn't already called. + * -4 can only handle 1 and only 1 time interval + * + * 5/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +int fillSect4_10 (enGribMeta *en, uShort2 tmplNum, int percentile, + sInt4 endYear, int endMonth, int endDay, int endHour, + int endMin, int endSec, uChar numInterval, sInt4 numMissing, + sect4IntervalType * interval) +{ + int j; /* loop counter over number of intervals. */ + + /* percentile template (10) */ + if (tmplNum != 10) { + /* This is specifically for template 4.10 */ + return -1; + } + if (en->ipdsnum != tmplNum) { + /* Didn't call fillSect4_0 first */ + return -1; + } + en->pdsTmpl[15] = percentile; + en->pdsTmpl[16] = endYear; + en->pdsTmpl[17] = endMonth; + en->pdsTmpl[18] = endDay; + en->pdsTmpl[19] = endHour; + en->pdsTmpl[20] = endMin; + en->pdsTmpl[21] = endSec; + en->pdsTmpl[22] = numInterval; + if (numInterval != 1) { + /* can only handle 1 and only 1 time interval */ + return -4; + } + en->pdsTmpl[23] = numMissing; + for (j = 0; j < numInterval; j++) { + en->pdsTmpl[24] = interval[j].processID; + en->pdsTmpl[25] = interval[j].incrType; + en->pdsTmpl[26] = interval[j].timeRangeUnit; + en->pdsTmpl[27] = interval[j].lenTime; + en->pdsTmpl[28] = interval[j].incrUnit; + en->pdsTmpl[29] = interval[j].timeIncr; + } + return 59; +} + +/***************************************************************************** + * fillSect4_12() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 4 (using template 12) data. Call fillSect4_0 first. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * tmplNum = [Code Table 4.0] Product Definition Template Number (Input) + * numFcsts = number of forecasts (Input) + * derivedFcst = [Code Table 4.7] Derived forecast type (Input) + * endYear = The year of the end time (valid Time). (Input) + * endMonth = The month of the end time (valid Time). (Input) + * endDay = The day of the end time (valid Time). (Input) + * endHour = The hour of the end time (valid Time). (Input) + * endMin = The min of the end time (valid Time). (Input) + * endSec = The sec of the end time (valid Time). (Input) + * numInterval = num of time range specifications (Has to = 1) (Input) + * numMissing = total num of missing values in statistical process (Input) + * interval = time range intervals. + * + * RETURNS: int + * > 0 (length of section 4). + * -1 if not template 4.12, or fillSect4_0 wasn't already called. + * -4 can only handle 1 and only 1 time interval + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +int fillSect4_12 (enGribMeta *en, uShort2 tmplNum, uChar numFcsts, + uChar derivedFcst, sInt4 endYear, int endMonth, int endDay, + int endHour, int endMin, int endSec, uChar numInterval, + sInt4 numMissing, sect4IntervalType * interval) +{ + int j; /* loop counter over number of intervals. */ + + /* derived interval template (12) */ + if (tmplNum != 12) { + /* This is specifically for template 4.12 */ + return -1; + } + if (en->ipdsnum != tmplNum) { + /* Didn't call fillSect4_0 first */ + return -1; + } + en->pdsTmpl[15] = derivedFcst; + en->pdsTmpl[16] = numFcsts; + en->pdsTmpl[17] = endYear; + en->pdsTmpl[18] = endMonth; + en->pdsTmpl[19] = endDay; + en->pdsTmpl[20] = endHour; + en->pdsTmpl[21] = endMin; + en->pdsTmpl[22] = endSec; + en->pdsTmpl[23] = numInterval; + if (numInterval != 1) { + /* can only handle 1 and only 1 time interval */ + return -4; + } + en->pdsTmpl[24] = numMissing; + for (j = 0; j < numInterval; j++) { + en->pdsTmpl[25] = interval[j].processID; + en->pdsTmpl[26] = interval[j].incrType; + en->pdsTmpl[27] = interval[j].timeRangeUnit; + en->pdsTmpl[28] = interval[j].lenTime; + en->pdsTmpl[29] = interval[j].incrUnit; + en->pdsTmpl[30] = interval[j].timeIncr; + } + return 60; +} + +/***************************************************************************** + * fillSect5() -- Arthur Taylor / MDL + * + * PURPOSE + * Complete section 5 data. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * tmplNum = [Code Table 5.0] Product Definition Template Number (Input) + * BSF = Binary scale factor (Input) + * DSF = Decimal scale factor (Input) + * [tmplNum=0,2,3,40,41] + * fieldType = [Code Table 5.1] type of original field values. (Input) + * [tmplNum=2,3] + * f_miss = [Code Table 5.5] missing value management used. (Input) + * missPri = Primary missing value (Input) + * missSec = Secondary missing value (Input) + * [tmplNum=3] + * orderOfDiff = Order of differencing (1 or 2) (Input) + * + * RETURNS: int + * > 0 (length of section 5). + * -1 can't handle extended lists yet + * -2 not in list of templates supported by NCEP + * -3 can't handle this order of differencing. + * -4 haven't finished mapping this projection to the template. + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +int fillSect5 (enGribMeta *en, uShort2 tmplNum, sShort2 BSF, sShort2 DSF, + uChar fieldType, uChar f_miss, float missPri, float missSec, + uChar orderOfDiff) +{ + int i; /* loop counter over number of DRS templates. */ + const struct drstemplate *templatesdrs = get_templatesdrs(); + + /* Find NCEP's template match */ + for (i = 0; i < MAXDRSTEMP; i++) { + if (templatesdrs[i].template_num == tmplNum) { + break; + } + } + if (i == MAXDRSTEMP) { + /* not in list of templates supported by NCEP */ + return -2; + } + if (templatesdrs[i].needext) { + /* can't handle extended data yet. */ + return -1; + } + + if (en->lenDrsTmpl < templatesdrs[i].mapdrslen) { + if (en->drsTmpl != NULL) { + free (en->drsTmpl); + } + en->drsTmpl = (sInt4 *) malloc (templatesdrs[i].mapdrslen * + sizeof (sInt4)); + } + en->lenDrsTmpl = templatesdrs[i].mapdrslen; + + en->idrsnum = tmplNum; + /* simple packing */ + if (tmplNum == 0) { + en->drsTmpl[0] = 9999; /* missing for Ref value (set later) */ + en->drsTmpl[1] = BSF; + en->drsTmpl[2] = DSF; + en->drsTmpl[3] = 9999; /* missing for numBits used (set later) */ + en->drsTmpl[4] = fieldType; /* code table 5.1 */ + return 21; + /* complex packing */ + } else if (tmplNum == 2) { + en->drsTmpl[0] = 9999; /* missing for Ref value (set later) */ + en->drsTmpl[1] = BSF; + en->drsTmpl[2] = DSF; + en->drsTmpl[3] = 9999; /* missing for numBits used (set later) */ + en->drsTmpl[4] = fieldType; /* code table 5.1 */ + en->drsTmpl[5] = 9999; /* missing for group splitting method used */ + en->drsTmpl[6] = f_miss; + memcpy (&(en->drsTmpl[7]), &missPri, sizeof (float)); + memcpy (&(en->drsTmpl[8]), &missSec, sizeof (float)); + en->drsTmpl[9] = 9999; /* number of groups */ + en->drsTmpl[10] = 9999; /* group widths */ + en->drsTmpl[11] = 9999; /* numBits for group widths */ + en->drsTmpl[12] = 9999; /* ref for group len */ + en->drsTmpl[13] = 9999; /* len increment for group lengths */ + en->drsTmpl[14] = 9999; /* true len of last group */ + en->drsTmpl[15] = 9999; /* numBits used for scaled group lens */ + return 47; + /* complex spatial packing */ + } else if (tmplNum == 3) { + en->drsTmpl[0] = 9999; /* missing for Ref value (set later) */ + en->drsTmpl[1] = BSF; + en->drsTmpl[2] = DSF; + en->drsTmpl[3] = 9999; /* missing for numBits used (set later) */ + en->drsTmpl[4] = fieldType; /* code table 5.1 */ + en->drsTmpl[5] = 9999; /* missing for group splitting method used */ + en->drsTmpl[6] = f_miss; + memcpy (&(en->drsTmpl[7]), &missPri, sizeof (float)); + memcpy (&(en->drsTmpl[8]), &missSec, sizeof (float)); + en->drsTmpl[9] = 9999; /* number of groups */ + en->drsTmpl[10] = 9999; /* group widths */ + en->drsTmpl[11] = 9999; /* numBits for group widths */ + en->drsTmpl[12] = 9999; /* ref for group len */ + en->drsTmpl[13] = 9999; /* len increment for group lengths */ + en->drsTmpl[14] = 9999; /* true len of last group */ + en->drsTmpl[15] = 9999; /* numBits used for scaled group lens */ + if (orderOfDiff > 2) { + /* NCEP can not handle order of differencing > 2 */ + return -3; + } + en->drsTmpl[16] = orderOfDiff; + en->drsTmpl[17] = 9999; /* num extra octets need for spatial differ */ + return 49; + /* jpeg2000 packing */ + } else if ((tmplNum == 40) || (tmplNum == 40000)) { + en->drsTmpl[0] = 9999; /* missing for Ref value (set later) */ + en->drsTmpl[1] = BSF; + en->drsTmpl[2] = DSF; + en->drsTmpl[3] = 9999; /* depth of grayscale image (set later) */ + en->drsTmpl[4] = fieldType; /* code table 5.1 */ + en->drsTmpl[5] = 9999; /* type of compression used (0 is lossless) + * (code table 5.40) */ + en->drsTmpl[6] = 9999; /* compression ratio */ + return 23; + /* png packing */ + } else if ((tmplNum == 41) || (tmplNum == 40010)) { + en->drsTmpl[0] = 9999; /* missing for Ref value (set later) */ + en->drsTmpl[1] = BSF; + en->drsTmpl[2] = DSF; + en->drsTmpl[3] = 9999; /* depth of grayscale image (set later) */ + en->drsTmpl[4] = fieldType; /* code table 5.1 */ + return 21; + /* spectral packing */ + } else if (tmplNum == 50) { + en->drsTmpl[0] = 9999; /* missing for Ref value (set later) */ + en->drsTmpl[1] = BSF; + en->drsTmpl[2] = DSF; + en->drsTmpl[3] = 9999; /* num bits used for each packed value */ + en->drsTmpl[4] = 9999; /* real part of (0,0) coefficient */ + return 24; + /* harmonic packing */ + } else if (tmplNum == 51) { + en->drsTmpl[0] = 9999; /* missing for Ref value (set later) */ + en->drsTmpl[1] = BSF; + en->drsTmpl[2] = DSF; + en->drsTmpl[3] = 9999; /* num bits used for each packed value */ + en->drsTmpl[4] = 9999; /* P - Laplacian scaling factor */ + en->drsTmpl[5] = 9999; /* Js - pentagonal resolution parameter */ + en->drsTmpl[6] = 9999; /* Ks - pentagonal resolution parameter */ + en->drsTmpl[7] = 9999; /* Ms - pentagonal resolution parameter */ + en->drsTmpl[8] = 9999; /* Ts - total num values in subset */ + en->drsTmpl[9] = 9999; /* Precision of unpacked subset */ + return 35; + } + /* Haven't finished mapping this drs to a template. */ + return -4; +} + +/***************************************************************************** + * fillGrid() -- Arthur Taylor / MDL + * + * PURPOSE + * Completes the data portion. If f_boustify, then it walks through the + * data winding back and forth. Note it does this in a row oriented fashion + * If you need a column oriented fashion because your grid is defined the + * other way, then swap your Nx and Ny in your call. + * + * ARGUMENTS + * en = A pointer to meta data to pass to GRIB2 encoder. (Output) + * data = Data array to add. (Input) + * lenData = Length of Data array. (Input) + * Nx = Number of X coordinates (Input) + * Ny = Number of Y coordinates (Input) + * ibmap = [Code 6.0] Bitmap indicator (Input) + * 0 = bitmap applies and is included in Section 6. + * 1-253 = Predefined bitmap applies + * 254 = Previously defined bitmap applies to this field + * 255 = Bit map does not apply to this product. + * f_boustify = true if we should re-Wrap the grid. (Input) + * f_miss = 1 if missPri valid, 2 if missSec valid. (Input) + * missPri = Primary missing value (Input) + * missSec = Secondary missing value (Input) + * + * RETURNS: int + * > 0 (max length of sect 6 and sect 7). + * -1 Can't handle this kind of bitmap (pre-defined). + * -2 No missing value when trying to create the bmap. + * -3 Can't handle Nx * Ny != lenData. + * + * 4/2006 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +int fillGrid (enGribMeta *en, double *data, sInt4 lenData, sInt4 Nx, sInt4 Ny, + sInt4 ibmap, sChar f_boustify, uChar f_miss, float missPri, + float missSec) +{ + uChar f_flip; /* Used to help keep track of the direction when + * "boustifying" the data. */ + sInt4 x; /* loop counter over Nx. */ + sInt4 y; /* loop counter over Ny. */ + sInt4 ind1; /* index to copy to. */ + sInt4 ind2; /* index to copy from. */ + + if ((ibmap != 0) && (ibmap != 255)) { + /* Can't handle this kind of bitmap (pre-defined). */ + return -1; + } + if ((ibmap == 0) && (f_miss != 1) && (f_miss != 2)) { + /* No missing value when trying to create the bmap. */ + return -2; + } + if (Nx * Ny != lenData) { + /* Can't handle Nx * Ny != lenData. */ + return -3; + } + + if (en->ngrdpts < lenData) { + if (en->fld != NULL) { + free (en->fld); + } + en->fld = (float *) malloc (lenData * sizeof (float)); + if (ibmap == 0) { + if (en->bmap != NULL) { + free (en->bmap); + } + en->bmap = (sInt4 *) malloc (lenData * sizeof (sInt4)); + } + } + en->ngrdpts = lenData; + en->ibmap = ibmap; + + /* Now need to walk over data and boustify it and create bmap. */ + + if (ibmap == 0) { + /* boustify uses row oriented boustification, however for column + * oriented, swap the Ny and Nx in the call to the procedure. */ + if (f_boustify) { + f_flip = 0; + for (y = 0; y < Ny; y++) { + for (x = 0; x < Nx; x++) { + ind1 = x + y * Nx; + if (!f_flip) { + ind2 = ind1; + } else { + ind2 = (Nx - x - 1) + y * Nx; + } + en->fld[ind1] = (float) data[ind2]; + if ((data[ind2] == missPri) || + ((f_miss == 2) && (data[ind2] == missSec))) { + en->bmap[ind1] = 0; + } else { + en->bmap[ind1] = 1; + } + } + f_flip = (!f_flip); + } + } else { + for (ind1 = 0; ind1 < lenData; ind1++) { + en->fld[ind1] = (float) data[ind1]; + if ((data[ind1] == missPri) || + ((f_miss == 2) && (data[ind1] == missSec))) { + en->bmap[ind1] = 0; + } else { + en->bmap[ind1] = 1; + } + } + } + /* len(sect6) < 6 + (lenData/8 + 1), len(sect7) < 5 + lenData * 4 */ + return (6 + lenData / 8 + 1) + (5 + lenData * 4); + } else { + /* boustify uses row oriented boustification, however for column + * oriented, swap the Ny and Nx in the call to the procedure. */ + if (f_boustify) { + f_flip = 0; + for (y = 0; y < Ny; y++) { + for (x = 0; x < Nx; x++) { + ind1 = x + y * Nx; + if (!f_flip) { + ind2 = ind1; + } else { + ind2 = (Nx - x - 1) + y * Nx; + } + en->fld[ind1] = (float) data[ind2]; + } + f_flip = (!f_flip); + } + } else { + for (ind1 = 0; ind1 < lenData; ind1++) { + en->fld[ind1] = (float) data[ind1]; + } + } + /* len(sect6) = 6, len(sect7) < 5 + lenData * 4 */ + return 6 + (5 + lenData * 4); + } +} + diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/engribapi.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/engribapi.h new file mode 100644 index 000000000..228350715 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/engribapi.h @@ -0,0 +1,119 @@ +#ifndef ENGRIBAPI_H +#define ENGRIBAPI_H + +#include "type.h" + +typedef struct { + sInt4 sec0[2]; /* info for section 0 */ + sInt4 sec1[13]; /* info for section 1 */ + uChar *sec2; /* section 2 free form info */ + sInt4 lenSec2; /* length of section 2 free form info */ + sInt4 gds[5]; + sInt4 *gdsTmpl; /* grid definition template (mapgrid) */ + sInt4 lenGdsTmpl; /* length of grid definition template (mapgrid) */ + sInt4 *idefList; + sInt4 idefnum; + + sInt4 ipdsnum; /* Product Definition Template Number (Code Table 4.0) */ + sInt4 *pdsTmpl; /* Contains the data values for the specified Product + * Definition Template (N=ipdsnum). Each element of this + * integer array contains an entry (in the order + * specified) of Product Definition Template 4.N */ + sInt4 lenPdsTmpl; /* length of product definition template*/ + float *coordlist; /* Array containing floating point values intended to + * document the vertical discretization associated to + * model data on hybrid coordinate vertical levels. */ + sInt4 numcoord; /* number of values in array coordlist. */ + + sInt4 idrsnum; /* Data Representation Template Number (Code Table 5.0) */ + sInt4 *drsTmpl; /* Contains the data values for the specified Data + * Representation Template (N=idrsnum). Each element of + * this integer array contains an entry (in the order + * specified) of Data Representation Template 5.N. Note + * that some values in this template (eg. reference + * values, number of bits, etc...) may be changed by the + * data packing algorithms. Use this to specify scaling + * factors and order of spatial differencing, if desired. */ + sInt4 lenDrsTmpl; /* length of data representation template*/ + + float *fld; /* Array of data points to pack. */ + sInt4 ngrdpts; /* Number of data points in grid. i.e. size of fld and bmap. */ + sInt4 ibmap; /* Bitmap indicator ( see Code Table 6.0 ) + * 0 = bitmap applies and is included in Section 6. + * 1-253 = Predefined bitmap applies + * 254 = Previously defined bitmap applies to this field + * 255 = Bit map does not apply to this product. */ + sInt4 *bmap; /* Integer array containing bitmap to be added. (if ibmap=0) */ +} enGribMeta; + +typedef struct { + uChar processID; /* Statistical process method used. */ + uChar incrType; /* Type of time increment between intervals */ + uChar timeRangeUnit; /* Time range unit. [Code Table 4.4] */ + sInt4 lenTime; /* Range or length of time interval. */ + uChar incrUnit; /* Unit of time increment. [Code Table 4.4] */ + sInt4 timeIncr; /* Time increment between intervals. */ +} sect4IntervalType; + +void initEnGribMeta (enGribMeta *en); + +void freeEnGribMeta (enGribMeta *en); + +void fillSect0 (enGribMeta *en, uChar prodType); + +void fillSect1 (enGribMeta *en, uShort2 center, uShort2 subCenter, + uChar mstrVer, uChar lclVer, uChar refCode, sInt4 refYear, + int refMonth, int refDay, int refHour, int refMin, int refSec, + uChar prodStat, uChar typeData); + +void fillSect2 (enGribMeta *en, uChar *sec2, sInt4 lenSec2); + +int fillSect3 (enGribMeta *en, uShort2 tmplNum, double majEarth, + double minEarth, sInt4 Nx, sInt4 Ny, double lat1, double lon1, + double lat2, double lon2, double Dx, double Dy, uChar resFlag, + uChar scanFlag, uChar centerFlag, sInt4 angle, sInt4 subDivis, + double meshLat, double orientLon, double scaleLat1, + double scaleLat2, double southLat, double southLon); + + +int fillSect4_0 (enGribMeta *en, uShort2 tmplNum, uChar cat, uChar subCat, + uChar genProcess, uChar bgGenID, uChar genID, + uChar f_valCutOff, sInt4 cutOff, uChar timeCode, + double foreSec, uChar surfType1, sChar surfScale1, + double dSurfVal1, uChar surfType2, sChar surfScale2, + double dSurfVal2); +int fillSect4_1 (enGribMeta *en, uShort2 tmplNum, uChar typeEnsemble, + uChar perturbNum, uChar numFcsts); +int fillSect4_2 (enGribMeta *en, uShort2 tmplNum, uChar numFcsts, + uChar derivedFcst); +int fillSect4_5 (enGribMeta *en, uShort2 tmplNum, uChar numFcsts, + uChar foreProbNum, uChar probType, sChar lowScale, + double dlowVal, sChar upScale, double dupVal); +int fillSect4_8 (enGribMeta *en, uShort2 tmplNum, sInt4 endYear, int endMonth, + int endDay, int endHour, int endMin, int endSec, + uChar numInterval, sInt4 numMissing, + sect4IntervalType * interval); +int fillSect4_9 (enGribMeta *en, uShort2 tmplNum, uChar numFcsts, + uChar foreProbNum, uChar probType, sChar lowScale, + double dlowVal, sChar upScale, double dupVal, sInt4 endYear, + int endMonth, int endDay, int endHour, int endMin, + int endSec, uChar numInterval, sInt4 numMissing, + sect4IntervalType * interval); +int fillSect4_10 (enGribMeta *en, uShort2 tmplNum, int percentile, + sInt4 endYear, int endMonth, int endDay, int endHour, + int endMin, int endSec, uChar numInterval, sInt4 numMissing, + sect4IntervalType * interval); +int fillSect4_12 (enGribMeta *en, uShort2 tmplNum, uChar numFcsts, + uChar derivedFcst, sInt4 endYear, int endMonth, int endDay, + int endHour, int endMin, int endSec, uChar numInterval, + sInt4 numMissing, sect4IntervalType * interval); + +int fillSect5 (enGribMeta *en, uShort2 tmplNum, sShort2 BSF, sShort2 DSF, + uChar fieldType, uChar f_miss, float missPri, float missSec, + uChar orderOfDiff); + +int fillGrid (enGribMeta *en, double *data, sInt4 lenData, sInt4 Nx, sInt4 Ny, + sInt4 ibmap, sChar f_boustify, uChar f_miss, float missPri, + float missSec); + +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/filedatasource.cpp b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/filedatasource.cpp new file mode 100644 index 000000000..a45f4fbba --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/filedatasource.cpp @@ -0,0 +1,60 @@ +#include "filedatasource.h" +#include "cpl_error.h" + +FileDataSource::FileDataSource(const char * fileName) + : closeFile(true) +{ + fp = VSIFOpenL(fileName, "rb"); +} + +FileDataSource::FileDataSource(VSILFILE* fp) +: closeFile(false) +{ + this->fp = fp; +} + +FileDataSource::~FileDataSource() +{ + if (closeFile) + VSIFCloseL(fp); +} + +size_t FileDataSource::DataSourceFread(void* lpBuf, size_t size, size_t count) +{ + return VSIFReadL(lpBuf, size, count, (VSILFILE*)fp); +} + +int FileDataSource::DataSourceFgetc() +{ + unsigned char byData; + + if( VSIFReadL( &byData, 1, 1, fp ) == 1 ) + return byData; + else + return EOF; +} + +int FileDataSource::DataSourceUngetc(int c) +{ + DataSourceFseek(-1, SEEK_CUR ); + + return c; +} + +int FileDataSource::DataSourceFseek(long offset, int origin) +{ + if (origin == SEEK_CUR && offset < 0) + return VSIFSeekL(fp, VSIFTellL(fp) + offset, SEEK_SET); + else + return VSIFSeekL(fp, offset, origin); +} + +int FileDataSource::DataSourceFeof() +{ + return VSIFEofL( fp ); +} + +long FileDataSource::DataSourceFtell() +{ + return VSIFTellL( fp ); +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/filedatasource.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/filedatasource.h new file mode 100644 index 000000000..aae3afa79 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/filedatasource.h @@ -0,0 +1,24 @@ +#ifndef FILEDATASOURCE_H +#define FILEDATASOURCE_H + +#include "datasource.h" +#include "cpl_vsi.h" + +class FileDataSource : public DataSource +{ +public: + FileDataSource(const char * fileName); + FileDataSource(VSILFILE* fp); + virtual ~FileDataSource(); + virtual size_t DataSourceFread(void* lpBuf, size_t size, size_t count); + virtual int DataSourceFgetc(); + virtual int DataSourceUngetc(int c); + virtual int DataSourceFseek(long offset, int origin); + virtual int DataSourceFeof(); + virtual long DataSourceFtell(); +private: + VSILFILE * fp; + bool closeFile; +}; + +#endif /* FILEDATASOURCE_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/fileendian.cpp b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/fileendian.cpp new file mode 100644 index 000000000..dfc4fdd0f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/fileendian.cpp @@ -0,0 +1,591 @@ +/***************************************************************************** + * fileendian.c + * + * DESCRIPTION + * This file contains all the utility functions that the Driver uses to + * solve endian'ness related issues. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * 12/2002 Rici Yu, Fangyu Chi, Mark Armstrong, & Tim Boyer + * (RY,FC,MA,&TB): Code Review 2. + * + * NOTES + ***************************************************************************** + */ +#include +#include +#include "fileendian.h" + +/***************************************************************************** + * norfread() -- Review 12/2006 + * + * Bas Retsios / ITC + * + * PURPOSE + * To map the #defined FREAD_BIG and FREAD_LIT to DataSource fread instead of "fread" + * + * ARGUMENTS + * Dst = The destination for the data. (Output) + * elem_size = The size of a single element. (Input) + * num_elem = The number of elements in Src. (Input) + * fp = The file to read from. (Input) + * + * FILES/DATABASES: + * It is assumed that file is already opened and in the correct seek position. + * + * RETURNS: size_t + * Number of elements read. + * + * HISTORY + * 12/2006 Bas Retsios (ITC): Created. + ***************************************************************************** + */ + +size_t norfread (void *Dst, size_t elem_size, size_t num_elem, DataSource &fp) +{ + return fp.DataSourceFread(Dst, elem_size, num_elem); +} + +/***************************************************************************** + * revfread() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * To do an "fread", but in a reverse manner. + * + * ARGUMENTS + * Dst = The destination for the data. (Output) + * elem_size = The size of a single element. (Input) + * num_elem = The number of elements in Src. (Input) + * fp = The file to read from. (Input) + * + * FILES/DATABASES: + * It is assumed that file is already opened and in the correct place. + * + * RETURNS: size_t + * Number of elements read. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (RY,FC,MA,&TB): Code Review. + * + * NOTES + * Decided to read it in and then swap. The thought here being that it is + * faster than a bunch of fgetc. This is the exact opposite method as + * revfwrite. + ***************************************************************************** + */ +size_t revfread (void *Dst, size_t elem_size, size_t num_elem, DataSource &fp) +{ + size_t ans; /* The answer from fread. */ + size_t j; /* Byte count. */ + char *dst; /* Allows us to treat Dst as an array of char. */ + char temp; /* A temporary holder of a byte when swapping. */ + char *ptr, *ptr2; /* Pointers to the two bytes to swap. */ + + ans = fp.DataSourceFread(Dst, elem_size, num_elem); + if (elem_size == 1) { + return ans; + } + if (ans == num_elem) { + dst = (char *) Dst; + for (j = 0; j < elem_size * num_elem; j += elem_size) { + ptr = dst + j; + ptr2 = ptr + elem_size - 1; + while (ptr2 > ptr) { + temp = *ptr; + *(ptr++) = *ptr2; + *(ptr2--) = temp; + } + } + } + return ans; +} + +/***************************************************************************** + * revfwrite() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * To do an "fwrite", but in a reverse manner. + * + * ARGUMENTS + * Src = The source of the data. (Input) + * elem_size = The size of a single element. (Input) + * num_elem = The number of elements in Src. (Input) + * fp = The file to write to. (Output) + * + * FILES/DATABASES: + * It is assumed that file is already opened and in the correct place. + * + * RETURNS: + * Returns number of elements written, or EOF on error. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 11/2002 Arthur Taylor (MDL/RSIS): Updated. + * 12/2002 (RY,FC,MA,&TB): Code Review. + * + * NOTES + * Decided to write using a bunch of fput, since this is buffered. The + * thought here, is that it is faster than swapping memory and then writing. + * This is the exact opposite method as revfread. + ***************************************************************************** + */ +size_t revfwrite (void *Src, size_t elem_size, size_t num_elem, FILE * fp) +{ + char *ptr; /* Current byte to put to file. */ + size_t i; /* Byte count */ + size_t j; /* Element count */ + char *src; /* Allows us to treat Src as an array of char. */ + + if (elem_size == 1) { + return fwrite (Src, elem_size, num_elem, fp); + } else { + src = (char *) Src; + ptr = src - elem_size - 1; + for (j = 0; j < num_elem; ++j) { + ptr += 2 * elem_size; + for (i = 0; i < elem_size; ++i) { + if (fputc ((int) *(ptr--), fp) == EOF) { + return 0; + } + } + } + return num_elem; + } +} + +/***************************************************************************** + * FREAD_ODDINT_BIG() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To do an "fread" into a sInt4, but in a reverse manner with not + * necessarily all 4 bytes. It reads big endian data from disk. + * + * ARGUMENTS + * dst = Where to store the data. (Output) + * len = The number of bytes to read. (<= 4) (Input) + * fp = The file to read from. (Input) + * + * FILES/DATABASES: + * It is assumed that file is already opened and in the correct place. + * + * RETURNS: + * Returns number of elements read, or EOF on error. + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +size_t FREAD_ODDINT_BIG (sInt4 * dst, uChar len, DataSource &fp) +{ + *dst = 0; +#ifdef LITTLE_ENDIAN + return revfread (dst, len, 1, fp); +#else + return norfread ((((char *) dst) + (4 - len)), len, 1, fp); +#endif +} + +/***************************************************************************** + * FREAD_ODDINT_LIT() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To do an "fread" into a sInt4, but in a reverse manner with not + * necessarily all 4 bytes. It reads little endian data from disk. + * + * ARGUMENTS + * dst = Where to store the data. (Output) + * len = The number of bytes to read. (<= 4) (Input) + * fp = The file to read from. (Input) + * + * FILES/DATABASES: + * It is assumed that file is already opened and in the correct place. + * + * RETURNS: + * Returns number of elements read, or EOF on error. + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +size_t FREAD_ODDINT_LIT (sInt4 * dst, uChar len, DataSource &fp) +{ + *dst = 0; +#ifdef LITTLE_ENDIAN + return norfread (dst, len, 1, fp); +#else + return revfread ((((char *) dst) + (4 - len)), len, 1, fp); +#endif +} + +/***************************************************************************** + * FWRITE_ODDINT_BIG() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To do an "fwrite" from a sInt4, but in a reverse manner with not + * necessarily all 4 bytes. It writes big endian data to disk. + * + * ARGUMENTS + * src = Where to read the data from. (Output) + * len = The number of bytes to read. (<= 4) (Input) + * fp = The file to write the data to. (Input) + * + * FILES/DATABASES: + * It is assumed that file is already opened and in the correct place. + * + * RETURNS: + * Returns number of elements written, or EOF on error. + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +size_t FWRITE_ODDINT_BIG (sInt4 * src, uChar len, FILE * fp) +{ +#ifdef LITTLE_ENDIAN + return revfwrite (src, len, 1, fp); +#else + return fwrite ((((char *) src) + (4 - len)), len, 1, fp); +#endif +} + +/***************************************************************************** + * FWRITE_ODDINT_LIT() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To do an "fwrite" from a sInt4, but in a reverse manner with not + * necessarily all 4 bytes. It writes little endian data to disk. + * + * ARGUMENTS + * src = Where to read the data from. (Output) + * len = The number of bytes to read. (<= 4) (Input) + * fp = The file to write the data to. (Input) + * + * FILES/DATABASES: + * It is assumed that file is already opened and in the correct place. + * + * RETURNS: + * Returns number of elements written, or EOF on error. + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +size_t FWRITE_ODDINT_LIT (sInt4 * src, uChar len, FILE * fp) +{ +#ifdef LITTLE_ENDIAN + return fwrite (src, len, 1, fp); +#else + return revfwrite ((((char *) src) + (4 - len)), len, 1, fp); +#endif +} + +/***************************************************************************** + * fileBitRead() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To get bits from the file. Stores the current byte, and passes the + * bits that were requested to the user. Leftover bits, are stored in a + * gbuf, which should be passed in for future reads. + * If numBits == 0, then flush the gbuf. + * + * ARGUMENTS + * dst = The storage place for the data read from file. (Output) + * dstLen = The size of dst (in bytes) (Input) + * num_bits = The number of bits to read from the file. (Input) + * fp = The open file to read from. (Input) + * gbuf = The current bit buffer (Input/Output) + * gbufLoc = Where we are in the current bit buffer. (Input/Output) + * + * RETURNS: + * EOF if EOF, 1 if error, 0 otherwise. + * + * NOTES + ***************************************************************************** + */ +int fileBitRead (void *Dst, size_t dstLen, uShort2 num_bits, FILE * fp, + uChar * gbuf, sChar * gbufLoc) +{ + static uChar BitRay[] = { 0, 1, 3, 7, 15, 31, 63, 127, 255 }; + register uChar buf_loc, buf, *ptr; + uChar *dst = (uChar*)Dst; + size_t num_bytes; + uChar dst_loc; + int c; + + memset (Dst, 0, dstLen); + + if (num_bits == 0) { + *gbuf = 0; + *gbufLoc = 0; + return 0; + } + + /* Since num_bits is always used with -1, I might as well do --num_bits + * here. */ + num_bytes = ((--num_bits) / 8) + 1; /* 1..8 bits = 1 byte, ... */ + /* Check if dst has enough room for num_bits. */ + if (dstLen < num_bytes) { + return 1; + } + + /* num_bits was modified earlier. */ + dst_loc = (uChar) ((num_bits % 8) + 1); + buf_loc = *gbufLoc; + buf = *gbuf; + +#ifdef LITTLE_ENDIAN + ptr = dst + (num_bytes - 1); +#else + ptr = dst + (dstLen - num_bytes); +#endif + + /* Deal with initial "remainder" part (most significant byte) in dst. */ + if (buf_loc >= dst_loc) { + /* can now deal with entire "remainder". */ +#ifdef LITTLE_ENDIAN + *(ptr--) |= (uChar) ((buf & BitRay[buf_loc]) >> (buf_loc - dst_loc)); +#else + *(ptr++) |= (uChar) ((buf & BitRay[buf_loc]) >> (buf_loc - dst_loc)); +#endif + buf_loc -= dst_loc; + } else { + /* need to do 2 calls to deal with entire "remainder". */ + if (buf_loc != 0) { + *ptr |= (uChar) ((buf & BitRay[buf_loc]) << (dst_loc - buf_loc)); + } + /* buf_loc is now 0. so we need more data. */ + /* dst_loc is now dst_loc - buf_loc. */ + if ((c = fgetc (fp)) == EOF) { + *gbufLoc = buf_loc; + *gbuf = buf; + return EOF; + } + /* buf_loc should be 8 */ + buf = (uChar) c; + /* 8 - (dst_loc - buf_loc) */ + buf_loc += (uChar) (8 - dst_loc); + /* Need mask in case right shift with sign extension? Should be ok + * since buf is a uChar, so it fills with 0s. */ +#ifdef LITTLE_ENDIAN + *(ptr--) |= (uChar) (buf >> buf_loc); +#else + *(ptr++) |= (uChar) (buf >> buf_loc); +#endif + /* buf_loc should now be 8 - (dst_loc - buf_loc) */ + } + + /* Note buf_loc < dst_loc from here on. Either it is 0 or < 8. */ + /* Also dst_loc is always 8 from here out. */ +#ifdef LITTLE_ENDIAN + while (ptr >= dst) { +#else + while (ptr < dst + dstLen) { +#endif + if (buf_loc != 0) { + *ptr |= (uChar) ((buf & BitRay[buf_loc]) << (8 - buf_loc)); + } + /* buf_loc is now 0. so we need more data. */ + if ((c = fgetc (fp)) == EOF) { + *gbufLoc = buf_loc; + *gbuf = buf; + return EOF; + } + buf = (uChar) c; + /* Need mask in case right shift with sign extension? Should be ok + * since buf is a uChar, so it fills with 0s. */ +#ifdef LITTLE_ENDIAN + *(ptr--) |= (uChar) (buf >> buf_loc); +#else + *(ptr++) |= (uChar) (buf >> buf_loc); +#endif + } + + *gbufLoc = buf_loc; + *gbuf = buf; + return 0; +} + +/***************************************************************************** + * fileBitWrite() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To write bits from src out to file. First writes out any leftover bits + * in pbuf, then bits from src. Any leftover bits that aren't on a full byte + * boundary, are stored in pbuf. + * If numBits == 0, then flush the pbuf. + * + * ARGUMENTS + * src = The data to put out to file. (Input) + * srcLen = Length in bytes of src. (Input) + * numBits = The number of bits to write to file. (Input) + * fp = The opened file ptr to write to. (Input) + * pbuf = The extra bit buffer (Input/Output) + * pBufLoc = The location in the bit buffer. + * + * FILES/DATABASES: None + * + * RETURNS: + * 1 if error, 0 otherwise + * + * HISTORY + * 8/2004 Arthur Taylor (MDL): Created + * + * NOTES + ***************************************************************************** + */ +char fileBitWrite (void *Src, size_t srcLen, uShort2 numBits, FILE * fp, + uChar * pbuf, sChar * pbufLoc) +{ + uChar buf_loc, buf, *ptr; + uChar *src = (uChar*)Src; + size_t num_bytes; + uChar src_loc; + + if (numBits == 0) { + if (*pbufLoc != 8) { + fputc ((int) *pbuf, fp); + *pbuf = 0; + *pbufLoc = 8; + return 8; + } else { + *pbuf = 0; + *pbufLoc = 8; + return 0; + } + } + /* Since numBits is always used with -1, I might as well do --numBits + * here. */ + num_bytes = ((--numBits) / 8) + 1; /* 1..8 bits = 1 byte, ... */ + /* Check if src has enough bits for us to put out. */ + if (srcLen < num_bytes) { + return 1; + } + + /* num_bits was modified earlier. */ + src_loc = (uChar) ((numBits % 8) + 1); + buf_loc = *pbufLoc; + buf = *pbuf; + + /* Get to start of interesting part of src. */ +#ifdef LITTLE_ENDIAN + ptr = src + (num_bytes - 1); +#else + ptr = src + (srcLen - num_bytes); +#endif + + /* Deal with most significant byte in src. */ + if (buf_loc >= src_loc) { + /* can store entire MSB in buf. */ + /* Mask? ... Safer to do so... Particularly if user has a number where + * she wants us to start saving half way through. */ +#ifdef LITTLE_ENDIAN + buf |= (uChar) ((*(ptr--) & ((1 << src_loc) - 1)) << + (buf_loc - src_loc)); +#else + buf |= (uChar) ((*(ptr++) & ((1 << src_loc) - 1)) << + (buf_loc - src_loc)); +#endif + buf_loc -= src_loc; + } else { + /* need to do 2 calls to store the MSB. */ + if (buf_loc != 0) { + buf |= (uChar) ((*ptr & ((1 << src_loc) - 1)) >> + (src_loc - buf_loc)); + } + /* buf_loc is now 0, so we write it out. */ + if (fputc ((int) buf, fp) == EOF) { + *pbufLoc = buf_loc; + *pbuf = buf; + return 1; + } + buf = (uChar) 0; + /* src_loc is now src_loc - buf_loc */ + /* store rest of ptr in buf. So left shift by 8 - (src_loc -buf_loc) + * and set buf_loc to 8 - (src_loc - buf_loc) */ + buf_loc += (uChar) (8 - src_loc); +#ifdef LITTLE_ENDIAN + buf |= (uChar) (*(ptr--) << buf_loc); +#else + buf |= (uChar) (*(ptr++) << buf_loc); +#endif + } + /* src_loc should always be considered 8 from now on.. */ + +#ifdef LITTLE_ENDIAN + while (ptr >= src) { +#else + while (ptr < src + srcLen) { +#endif + if (buf_loc == 0) { + /* Simple case where buf and src line up.. */ + if (fputc ((int) buf, fp) == EOF) { + *pbufLoc = buf_loc; + *pbuf = buf; + return 1; + } +#ifdef LITTLE_ENDIAN + buf = (uChar) * (ptr--); +#else + buf = (uChar) * (ptr++); +#endif + } else { + /* No mask since src_loc is considered 8. */ + /* Need mask in case right shift with sign extension? Should be ok + * since *ptr is a uChar so it fills with 0s. */ + buf |= (uChar) ((*ptr) >> (8 - buf_loc)); + /* buf_loc is now 0, so we write it out. */ + if (fputc ((int) buf, fp) == EOF) { + *pbufLoc = buf_loc; + *pbuf = buf; + return 1; + } + buf = (uChar) 0; + /* src_loc is 8-buf_loc... */ + /* need to left shift by 8 - (8-buf_loc) */ +#ifdef LITTLE_ENDIAN + buf |= (uChar) (*(ptr--) << buf_loc); +#else + buf |= (uChar) (*(ptr++) << buf_loc); +#endif + } + } + /* We would rather not keep a full bit buffer. */ + if (buf_loc == 0) { + if (fputc ((int) buf, fp) == EOF) { + *pbufLoc = buf_loc; + *pbuf = buf; + return 1; + } + buf_loc = 8; + buf = (uChar) 0; + } + *pbufLoc = buf_loc; + *pbuf = buf; + return 0; +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/fileendian.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/fileendian.h new file mode 100644 index 000000000..73212f365 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/fileendian.h @@ -0,0 +1,62 @@ +/***************************************************************************** + * fileendian.h + * + * DESCRIPTION + * This file contains all the utility functions that the Driver uses to + * solve endian'ness related issues. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifndef FILEENDIAN_H +#define FILEENDIAN_H + +#include +#include "type.h" +#include "datasource.h" +#include "cpl_port.h" + +/* + * MadeOnIntel ==> LittleEndian + * NotMadeOnIntel ==> BigEndian + */ +#undef BIG_ENDIAN +#undef LITTLE_ENDIAN + +#ifdef WORDS_BIGENDIAN + #define BIG_ENDIAN +#else + #define LITTLE_ENDIAN +#endif + +/* The following #defines are used to make the code easier to read. */ +#ifdef BIG_ENDIAN + #define FREAD_BIG norfread + #define FREAD_LIT revfread + #define FWRITE_BIG fwrite + #define FWRITE_LIT revfwrite +#else + #define FREAD_BIG revfread + #define FREAD_LIT norfread + #define FWRITE_BIG revfwrite + #define FWRITE_LIT fwrite +#endif + +size_t norfread (void *Dst, size_t elem_size, size_t num_elem, DataSource &fp); +size_t revfread (void *Dst, size_t elem_size, size_t num_elem, DataSource &fp); +size_t revfwrite (void *Src, size_t elem_size, size_t num_elem, FILE *fp); + +size_t FREAD_ODDINT_BIG (sInt4 * dst, uChar len, DataSource &fp); +size_t FREAD_ODDINT_LIT (sInt4 * dst, uChar len, DataSource &fp); +size_t FWRITE_ODDINT_BIG (sInt4 * src, uChar len, FILE *fp); +size_t FWRITE_ODDINT_LIT (sInt4 * src, uChar len, FILE *fp); + +int fileBitRead (void *Dst, size_t dstLen, uShort2 num_bits, FILE *fp, + uChar * gbuf, sChar * gbufLoc); +char fileBitWrite (void *Src, size_t srcLen, uShort2 numBits, FILE *fp, + uChar * pbuf, sChar * pbufLoc); + +#endif /* FILEENDIAN_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/grib1tab.cpp b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/grib1tab.cpp new file mode 100644 index 000000000..3b092d5df --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/grib1tab.cpp @@ -0,0 +1,9336 @@ +/***************************************************************************** + * grib1tab.c + * + * DESCRIPTION + * This file contains the tables used to lookup the variables in GRIB1. + * + * HISTORY + * 4/2003 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ + +#include "degrib1.h" + +/***************************************************************************** + * NMC Parameter Tables (GRIB1 Section 1 Table 3) + * Surface tables + * based on :http://www.nco.ncep.noaa.gov/pmb/docs/on388/table3.html + * updated last on 3/14/2006 + ***************************************************************************** + */ +/* *INDENT-OFF* */ +GRIB1SurfTable GRIB1Surface[256] = { + /* 0 */ {"var0", "reserved", "-", 0}, + /* 1 */ {"SFC", "Ground or water surface", "-", 0}, + /* 2 */ {"CBL", "Cloud base level", "-", 0}, + /* 3 */ {"CTL", "Cloud top level", "-", 0}, + /* 4 */ {"0DEG", "Level of 0 deg (C) isotherm", "-", 0}, + /* 5 */ {"ADCL", "Level of adiabatic condensation lifted from the surface", + "-", 0}, + /* 6 */ {"MWSL", "Maximum wind level", "-", 0}, + /* 7 */ {"TRO", "Tropopause", "-", 0}, + /* 8 */ {"NTAT", "Nominal top of atmosphere", "-", 0}, + /* 9 */ {"SEAB", "Sea bottom", "-", 0}, + /* 10 */ {"var10", "reserved", "-", 0}, + /* 11 */ {"var11", "reserved", "-", 0}, + /* 12 */ {"var12", "reserved", "-", 0}, + /* 13 */ {"var13", "reserved", "-", 0}, + /* 14 */ {"var14", "reserved", "-", 0}, + /* 15 */ {"var15", "reserved", "-", 0}, + /* 16 */ {"var16", "reserved", "-", 0}, + /* 17 */ {"var17", "reserved", "-", 0}, + /* 18 */ {"var18", "reserved", "-", 0}, + /* 19 */ {"var19", "reserved", "-", 0}, + /* 20 */ {"TMPL", "Isothermal level", "1/100 K", 0}, + /* 21 */ {"var21", "reserved", "-", 0}, + /* 22 */ {"var22", "reserved", "-", 0}, + /* 23 */ {"var23", "reserved", "-", 0}, + /* 24 */ {"var24", "reserved", "-", 0}, + /* 25 */ {"var25", "reserved", "-", 0}, + /* 26 */ {"var26", "reserved", "-", 0}, + /* 27 */ {"var27", "reserved", "-", 0}, + /* 28 */ {"var28", "reserved", "-", 0}, + /* 29 */ {"var29", "reserved", "-", 0}, + /* 30 */ {"var30", "reserved", "-", 0}, + /* 31 */ {"var31", "reserved", "-", 0}, + /* 32 */ {"var32", "reserved", "-", 0}, + /* 33 */ {"var33", "reserved", "-", 0}, + /* 34 */ {"var34", "reserved", "-", 0}, + /* 35 */ {"var35", "reserved", "-", 0}, + /* 36 */ {"var36", "reserved", "-", 0}, + /* 37 */ {"var37", "reserved", "-", 0}, + /* 38 */ {"var38", "reserved", "-", 0}, + /* 39 */ {"var39", "reserved", "-", 0}, + /* 40 */ {"var40", "reserved", "-", 0}, + /* 41 */ {"var41", "reserved", "-", 0}, + /* 42 */ {"var42", "reserved", "-", 0}, + /* 43 */ {"var43", "reserved", "-", 0}, + /* 44 */ {"var44", "reserved", "-", 0}, + /* 45 */ {"var45", "reserved", "-", 0}, + /* 46 */ {"var46", "reserved", "-", 0}, + /* 47 */ {"var47", "reserved", "-", 0}, + /* 48 */ {"var48", "reserved", "-", 0}, + /* 49 */ {"var49", "reserved", "-", 0}, + /* 50 */ {"var50", "reserved", "-", 0}, + /* 51 */ {"var51", "reserved", "-", 0}, + /* 52 */ {"var52", "reserved", "-", 0}, + /* 53 */ {"var53", "reserved", "-", 0}, + /* 54 */ {"var54", "reserved", "-", 0}, + /* 55 */ {"var55", "reserved", "-", 0}, + /* 56 */ {"var56", "reserved", "-", 0}, + /* 57 */ {"var57", "reserved", "-", 0}, + /* 58 */ {"var58", "reserved", "-", 0}, + /* 59 */ {"var59", "reserved", "-", 0}, + /* 60 */ {"var60", "reserved", "-", 0}, + /* 61 */ {"var61", "reserved", "-", 0}, + /* 62 */ {"var62", "reserved", "-", 0}, + /* 63 */ {"var63", "reserved", "-", 0}, + /* 64 */ {"var64", "reserved", "-", 0}, + /* 65 */ {"var65", "reserved", "-", 0}, + /* 66 */ {"var66", "reserved", "-", 0}, + /* 67 */ {"var67", "reserved", "-", 0}, + /* 68 */ {"var68", "reserved", "-", 0}, + /* 69 */ {"var69", "reserved", "-", 0}, + /* 70 */ {"var70", "reserved", "-", 0}, + /* 71 */ {"var71", "reserved", "-", 0}, + /* 72 */ {"var72", "reserved", "-", 0}, + /* 73 */ {"var73", "reserved", "-", 0}, + /* 74 */ {"var74", "reserved", "-", 0}, + /* 75 */ {"var75", "reserved", "-", 0}, + /* 76 */ {"var76", "reserved", "-", 0}, + /* 77 */ {"var77", "reserved", "-", 0}, + /* 78 */ {"var78", "reserved", "-", 0}, + /* 79 */ {"var79", "reserved", "-", 0}, + /* 80 */ {"var80", "reserved", "-", 0}, + /* 81 */ {"var81", "reserved", "-", 0}, + /* 82 */ {"var82", "reserved", "-", 0}, + /* 83 */ {"var83", "reserved", "-", 0}, + /* 84 */ {"var84", "reserved", "-", 0}, + /* 85 */ {"var85", "reserved", "-", 0}, + /* 86 */ {"var86", "reserved", "-", 0}, + /* 87 */ {"var87", "reserved", "-", 0}, + /* 88 */ {"var88", "reserved", "-", 0}, + /* 89 */ {"var89", "reserved", "-", 0}, + /* 90 */ {"var90", "reserved", "-", 0}, + /* 91 */ {"var91", "reserved", "-", 0}, + /* 92 */ {"var92", "reserved", "-", 0}, + /* 93 */ {"var93", "reserved", "-", 0}, + /* 94 */ {"var94", "reserved", "-", 0}, + /* 95 */ {"var95", "reserved", "-", 0}, + /* 96 */ {"var96", "reserved", "-", 0}, + /* 97 */ {"var97", "reserved", "-", 0}, + /* 98 */ {"var98", "reserved", "-", 0}, + /* 99 */ {"var99", "reserved", "-", 0}, + /* 100 */ {"ISBL", "Isobaric surface", "hPa", 0}, + /* 101 */ {"ISBY", "layer between 2 isobaric levels", "kPa", 1}, + /* 102 */ {"MSL", "Mean sea level", "-", 0}, + /* 103 */ {"GPML", "Specified altitude above MSL", "m", 0}, + /* 104 */ {"GPMY", "layer between 2 specified altitudes above MSL", + "hm", 1}, + /* 105 */ {"HTGL", "Specified height level above ground", "m", 0}, + /* 106 */ {"HTGY", "layer between 2 specified height levels above ground", + "hm", 1}, + /* 107 */ {"SIGL", "Sigma level", "1/10000", 0}, + /* 108 */ {"SIGY", "layer between 2 sigma levels", "1/100", 1}, + /* 109 */ {"HYBL", "Hybrid level", "-", 0}, + /* 110 */ {"HYBY", "layer between 2 hybrid levels", "-", 1}, + /* 111 */ {"DBLL", "Depth below land surface", "cm", 0}, + /* 112 */ {"DBLY", "layer between 2 depths below land surface", "cm", 1}, + /* 113 */ {"THEL", "Isentropic (theta) level", "K", 0}, + /* 114 */ {"THEY", "layer between 2 isentropic levels", "K", 1}, + /* 115 */ {"SPDL", "Level at specified pressure difference from ground to" + " level", "hPa", 0}, + /* 116 */ {"SPDY", "Level between 2 levels at specified pressure " + "difference from ground to level", "hPa", 1}, + /* 117 */ {"PVL", "Potential vorticity surface", "(10^-6 K M^2)/(kg s)", + 0}, + /* 118 */ {"var118", "reserved", "-", 0}, + /* 119 */ {"EtaL", "Eta level", "1/10000", 0}, + /* 120 */ {"EtaY", "layer between 2 Eta levels", "1/100", 0}, + /* 121 */ {"IBYH", "layer between 2 isobaric surfaces", "1100 hPa", 1}, + /* 122 */ {"var122", "reserved", "-", 0}, + /* 123 */ {"var123", "reserved", "-", 0}, + /* 124 */ {"var124", "reserved", "-", 0}, + /* 125 */ {"HGLH", "Specified height level above ground", "cm", 0}, + /* 126 */ {"ISBP", "Isobaric level", "Pa", 0}, + /* 127 */ {"var127", "reserved", "-", 0}, + /* 128 */ {"SGYH", "layer between 2 sigma levels", "1/1000", 1}, + /* 129 */ {"var129", "reserved", "-", 0}, + /* 130 */ {"var130", "reserved", "-", 0}, + /* 131 */ {"var131", "reserved", "-", 0}, + /* 132 */ {"var132", "reserved", "-", 0}, + /* 133 */ {"var133", "reserved", "-", 0}, + /* 134 */ {"var134", "reserved", "-", 0}, + /* 135 */ {"var135", "reserved", "-", 0}, + /* 136 */ {"var136", "reserved", "-", 0}, + /* 137 */ {"var137", "reserved", "-", 0}, + /* 138 */ {"var138", "reserved", "-", 0}, + /* 139 */ {"var139", "reserved", "-", 0}, + /* 140 */ {"var140", "reserved", "-", 0}, + /* 141 */ {"IBYM", "layer between 2 isobaric surfaces", "1100 hPa", 1}, + /* 142 */ {"var142", "reserved", "-", 0}, + /* 143 */ {"var143", "reserved", "-", 0}, + /* 144 */ {"var144", "reserved", "-", 0}, + /* 145 */ {"var145", "reserved", "-", 0}, + /* 146 */ {"var146", "reserved", "-", 0}, + /* 147 */ {"var147", "reserved", "-", 0}, + /* 148 */ {"var148", "reserved", "-", 0}, + /* 149 */ {"var149", "reserved", "-", 0}, + /* 150 */ {"var150", "reserved", "-", 0}, + /* 151 */ {"var151", "reserved", "-", 0}, + /* 152 */ {"var152", "reserved", "-", 0}, + /* 153 */ {"var153", "reserved", "-", 0}, + /* 154 */ {"var154", "reserved", "-", 0}, + /* 155 */ {"var155", "reserved", "-", 0}, + /* 156 */ {"var156", "reserved", "-", 0}, + /* 157 */ {"var157", "reserved", "-", 0}, + /* 158 */ {"var158", "reserved", "-", 0}, + /* 159 */ {"var159", "reserved", "-", 0}, + /* 160 */ {"DBSL", "Depth below sea level", "m", 0}, + /* 161 */ {"var161", "reserved", "-", 0}, + /* 162 */ {"var162", "reserved", "-", 0}, + /* 163 */ {"var163", "reserved", "-", 0}, + /* 164 */ {"var164", "reserved", "-", 0}, + /* 165 */ {"var165", "reserved", "-", 0}, + /* 166 */ {"var166", "reserved", "-", 0}, + /* 167 */ {"var167", "reserved", "-", 0}, + /* 168 */ {"var168", "reserved", "-", 0}, + /* 169 */ {"var169", "reserved", "-", 0}, + /* 170 */ {"var170", "reserved", "-", 0}, + /* 171 */ {"var171", "reserved", "-", 0}, + /* 172 */ {"var172", "reserved", "-", 0}, + /* 173 */ {"var173", "reserved", "-", 0}, + /* 174 */ {"var174", "reserved", "-", 0}, + /* 175 */ {"var175", "reserved", "-", 0}, + /* 176 */ {"var176", "reserved", "-", 0}, + /* 177 */ {"var177", "reserved", "-", 0}, + /* 178 */ {"var178", "reserved", "-", 0}, + /* 179 */ {"var179", "reserved", "-", 0}, + /* 180 */ {"var180", "reserved", "-", 0}, + /* 181 */ {"var181", "reserved", "-", 0}, + /* 182 */ {"var182", "reserved", "-", 0}, + /* 183 */ {"var183", "reserved", "-", 0}, + /* 184 */ {"var184", "reserved", "-", 0}, + /* 185 */ {"var185", "reserved", "-", 0}, + /* 186 */ {"var186", "reserved", "-", 0}, + /* 187 */ {"var187", "reserved", "-", 0}, + /* 188 */ {"var188", "reserved", "-", 0}, + /* 189 */ {"var189", "reserved", "-", 0}, + /* 190 */ {"var190", "reserved", "-", 0}, + /* 191 */ {"var191", "reserved", "-", 0}, + /* 192 */ {"var192", "reserved", "-", 0}, + /* 193 */ {"var193", "reserved", "-", 0}, + /* 194 */ {"var194", "reserved", "-", 0}, + /* 195 */ {"var195", "reserved", "-", 0}, + /* 196 */ {"var196", "reserved", "-", 0}, + /* 197 */ {"var197", "reserved", "-", 0}, + /* 198 */ {"var198", "reserved", "-", 0}, + /* 199 */ {"var199", "reserved", "-", 0}, + /* 200 */ {"EATM", "entire atmosphere (considerd as a single layer)", "-", + 0}, + /* 201 */ {"EOCN", "entire ocean (considered as a single layer)", "-", 0}, + /* 202 */ {"var202", "reserved", "-", 0}, + /* 203 */ {"var203", "reserved", "-", 0}, + /* 204 */ {"HTFL", "Highest troposphereic freezing level", "-", 0}, + /* 205 */ {"var205", "reserved", "-", 0}, + /* 206 */ {"GCBL", "Grid scale cloud bottom level", "-", 0}, + /* 207 */ {"GCTL", "Grid scale cloud top level", "-", 0}, + /* 208 */ {"var208", "reserved", "-", 0}, + /* 209 */ {"BCBL", "Boundary layer cloud bottom level", "-", 0}, + /* 210 */ {"BCTL", "Boundary layer cloud top level", "-", 0}, + /* 211 */ {"BCY", "Boundary layer cloud level", "-", 0}, + /* 212 */ {"LCBL", "Low cloud bottom level", "-", 0}, + /* 213 */ {"LCTL", "Low cloud top level", "-", 0}, + /* 214 */ {"LCY", "Low cloud level", "-", 0}, + /* 215 */ {"CEIL", "Cloud ceiling", "-", 0}, + /* 216 */ {"var216", "reserved", "-", 0}, + /* 217 */ {"var217", "reserved", "-", 0}, + /* 218 */ {"var218", "reserved", "-", 0}, + /* 219 */ {"var219", "reserved", "-", 0}, + /* 220 */ {"var220", "reserved", "-", 0}, + /* 221 */ {"var221", "reserved", "-", 0}, + /* 222 */ {"MCBL", "Middle cloud bottom level", "-", 0}, + /* 223 */ {"MCTL", "Middle cloud top level", "-", 0}, + /* 224 */ {"MCY", "Middle cloud level", "-", 0}, + /* 225 */ {"var225", "reserved", "-", 0}, + /* 226 */ {"var226", "reserved", "-", 0}, + /* 227 */ {"var227", "reserved", "-", 0}, + /* 228 */ {"var228", "reserved", "-", 0}, + /* 229 */ {"var229", "reserved", "-", 0}, + /* 230 */ {"var230", "reserved", "-", 0}, + /* 231 */ {"var231", "reserved", "-", 0}, + /* 232 */ {"HCBL", "High cloud bottom level", "-", 0}, + /* 233 */ {"HCTL", "High cloud top level", "-", 0}, + /* 234 */ {"HCY", "High cloud level", "-", 0}, + /* 235 */ {"OITL", "Ocean Isotherm Level (1/10 deg C)", "-", 0}, + /* 236 */ {"OLYR", "Layer between two depths below ocean surface", "-", 0}, + /* 237 */ {"OBML", "Bottom of Ocean Mixed Layer (m)", "-", 0}, + /* 238 */ {"OBIL", "Bottom of Ocean Isothermal Layer (m)", "-", 0}, + /* 239 */ {"var239", "reserved", "-", 0}, + /* 240 */ {"var240", "reserved", "-", 0}, + /* 241 */ {"var241", "reserved", "-", 0}, + /* 242 */ {"CCBL", "Convective cloud bottom level", "-", 0}, + /* 243 */ {"CCTL", "Convective cloud top level", "-", 0}, + /* 244 */ {"CCY", "Convective cloud level", "-", 0}, + /* 245 */ {"LLTW", "Lowest level of the wet bulb zero", "-", 0}, + /* 246 */ {"MTHE", "Maximum equivalent potential temperature level", "-", + 0}, + /* 247 */ {"EHLT", "Equilibrium level", "-", 0}, + /* 248 */ {"SCBL", "Shallow convective cloud bottom level", "-", 0}, + /* 249 */ {"SCTL", "Shallow convective cloud top level", "-", 0}, + /* 250 */ {"var250", "reserved", "-", 0}, + /* 251 */ {"DCBL", "Deep convective cloud bottom level", "-", 0}, + /* 252 */ {"DCTL", "Deep convective cloud top level", "-", 0}, + /* 253 */ {"LBLSW", "Lowest bottom level of supercooled liquid water layer", "-", 0}, + /* 254 */ {"HTLSW", "Highest top level of supercooled liquid water layer", "-", 0}, + /* 255 */ {"var255", "undefined", "-", 0}, +}; +/* *INDENT-ON* */ + +/***************************************************************************** + * NMC Parameter Tables (GRIB1 Section 1 Table 2) + * Table versions opn, reanal, omb, 129 + ***************************************************************************** + */ + +/* + * parameter table for NCEP (operations) + * center = 7, subcenter != 2 parameter table = 1, 2, 3 etc + * note: see reanalysis parameter table for problems + * updated 11/26/2002 + */ + +/* http://www.nco.ncep.noaa.gov/pmb/docs/on388/table2.html */ +/* Updated last on 5/24/2003 */ +/* *INDENT-OFF* */ +GRIB1ParmTable parm_table_ncep_opn[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "km^2/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice concentration (ice=1;no ice=0)", "proportion", + UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"MSLSA", "Mean sea level pressure (Std Atm Reduction)", "Pa", + UC_NONE}, + /* 129 */ {"MSLMA", "Mean sea level pressure (MAPS system Reduction)", "Pa", + UC_NONE}, + /* 130 */ {"MSLET", "Mean sea level pressure (ETA model Reduction)", "Pa", + UC_NONE}, + /* 131 */ {"LFTX", "Surface lifted index", "K", UC_NONE}, + /* Delta temp. */ + /* 132 */ {"4LFTX", "Best (4-layer) lifted index", "K", UC_NONE}, + /* Delta temp. */ + /* 133 */ {"KX", "K index", "K", UC_NONE}, /* Delta temp. */ + /* 134 */ {"SX", "Sweat index", "K", UC_NONE}, /* Delta temp. */ + /* 135 */ {"MCONV", "Horizontal moisture divergence", "kg/kg/s", UC_NONE}, + /* 136 */ {"VWSH", "Vertical speed shear", "1/s", UC_NONE}, + /* 137 */ {"TSLSA", "3-hr pressure tendency (Std Atmos Reduction)", "Pa/s", + UC_NONE}, + /* 138 */ {"BVF2", "Brunt-Vaisala frequency (squared)", "1/s^2", UC_NONE}, + /* 139 */ {"PVMW", "Potential vorticity (density weighted)", "1/s/m", + UC_NONE}, + /* 140 */ {"CRAIN", "Categorical rain", "yes=1;no=0", UC_NONE}, + /* 141 */ {"CFRZR", "Categorical freezing rain", "yes=1;no=0", UC_NONE}, + /* 142 */ {"CICEP", "Categorical ice pellets", "yes=1;no=0", UC_NONE}, + /* 143 */ {"CSNOW", "Categorical snow", "yes=1;no=0", UC_NONE}, + /* 144 */ {"SOILW", "Volumetric soil moisture", "fraction", UC_NONE}, + /* 145 */ {"PEVPR", "Potential evaporation rate", "W/m^2", UC_NONE}, + /* 146 */ {"CWORK", "Cloud work function", "J/kg", UC_NONE}, + /* 147 */ {"U-GWD", "Zonal flux of gravity wave stress", "N/m^2", UC_NONE}, + /* 148 */ {"V-GWD", "Meridional flux of gravity wave stress", "N/m^2", UC_NONE}, + /* 149 */ {"PV", "Potential vorticity", "m^2/s/kg", UC_NONE}, + /* 150 */ {"COVMZ", "Covariance between u and v", "m^2/s^2", UC_NONE}, + /* 151 */ {"COVTZ", "Covariance between u and T", "K*m/s", UC_NONE}, + /* 152 */ {"COVTM", "Covariance between v and T", "K*m/s", UC_NONE}, + /* 153 */ {"CLWMR", "Cloud water", "kg/kg", UC_NONE}, + /* 154 */ {"O3MR", "Ozone mixing ratio", "kg/kg", UC_NONE}, + /* 155 */ {"GFLUX", "Ground Heat Flux", "W/m^2", UC_NONE}, + /* 156 */ {"CIN", "Convective inhibition", "J/kg", UC_NONE}, + /* 157 */ {"CAPE", "Convective Available Potential Energy", "J/kg", UC_NONE}, + /* 158 */ {"TKE", "Turbulent Kinetic Energy", "J/kg", UC_NONE}, + /* 159 */ {"CONDP", "Condensation pressure of parcel lifted from indicated surface", "Pa", UC_NONE}, + /* 160 */ {"CSUSF", "Clear Sky Upward Solar Flux", "W/m^2", UC_NONE}, + /* 161 */ {"CSDSF", "Clear Sky Downward Solar Flux", "W/m^2", UC_NONE}, + /* 162 */ {"CSULF", "Clear sky upward long wave flux", "W/m^2", UC_NONE}, + /* 163 */ {"CSDLF", "Clear sky downward long wave flux", "W/m^2", UC_NONE}, + /* 164 */ {"CFNSF", "Cloud forcing net solar flux", "W/m^2", UC_NONE}, + /* 165 */ {"CFNLF", "Cloud forcing net long wave flux", "W/m^2", UC_NONE}, + /* 166 */ {"VBDSF", "Visible beam downward solar flux", "W/m^2", UC_NONE}, + /* 167 */ {"VDDSF", "Visible diffuse downward solar flux", "W/m^2", + UC_NONE}, + /* 168 */ {"NBDSF", "Near IR beam downward solar flux", "W/m^2", UC_NONE}, + /* 169 */ {"NDDSF", "Near IR diffuse downward solar flux", "W/m^2", + UC_NONE}, + /* 170 */ {"RWMR", "Rain water mixing ratio", "kg/kg", UC_NONE}, + /* 171 */ {"SNMR", "Snow mixing ratio", "kg/kg", UC_NONE}, + /* 172 */ {"MFLX", "Momentum flux", "N/m^2", UC_NONE}, + /* 173 */ {"LMH", "Mass point model surface", "-", UC_NONE}, + /* 174 */ {"LMV", "Velocity point model surface", "-", UC_NONE}, + /* 175 */ {"MLYNO", "Model layer number (from bottom up)", "-", UC_NONE}, + /* 176 */ {"NLAT", "Latitude (-90 to +90)", "deg", UC_NONE}, + /* 177 */ {"ELON", "East longitude (0-360)", "deg", UC_NONE}, + /* 178 */ {"ICMR", "Ice mixing ratio", "kg/kg", UC_NONE}, + /* 179 */ {"GRMR", "Graupel mixing ratio", "kg/kg", UC_NONE}, + /* 180 */ {"GUST", "Surface wind gust", "m/s", UC_NONE}, + /* 181 */ {"LPSX", "x-gradient of log pressure", "1/m", UC_NONE}, + /* 182 */ {"LPSY", "y-gradient of log pressure", "1/m", UC_NONE}, + /* 183 */ {"HGTX", "x-gradient of height", "m/m", UC_NONE}, + /* 184 */ {"HGTY", "y-gradient of height", "m/m", UC_NONE}, + /* 185 */ /* {"TURB", "Turbulence SIGMET/AIRMET", "-", UC_NONE}, */ + /* 185 */ {"TPFI", "Turbulence Potential Forecast Index", "-", UC_NONE}, + /* 186 */ /* {"ICNG", "Icing SIGMET/AIRMET", "-", UC_NONE}, */ + /* 186 */ {"TIPD", "Turbulence Icing Potential Diagnostic", "-", UC_NONE}, + /* 187 */ {"LTNG", "Lightning", "-", UC_NONE}, + /* 188 */ {"RDRIP", "Rate of water dropping from canopy to ground", "-", + UC_NONE}, + /* 189 */ {"VPTMP", "Virtual potential temperature", "K", UC_K2F}, + /* 190 */ {"HLCY", "Storm relative helicity", "m^2/s^2", UC_NONE}, + /* 191 */ {"PROB", "Probability from ensemble", "-", UC_NONE}, + /* 192 */ {"PROBN", "Probability from ensemble normalized with respect to climate expectancy", "-", + UC_NONE}, + /* 193 */ {"POP", "Probability of precipitation", "%", UC_NONE}, + /* 194 */ {"CPOFP", "Probability of frozen precipitation", "%", UC_NONE}, + /* 195 */ {"CPOZP", "Probability of freezing precipitation", "%", UC_NONE}, + /* 196 */ {"USTM", "u-component of storm motion", "m/s", UC_NONE}, + /* 197 */ {"VSTM", "v-component of storm motion", "m/s", UC_NONE}, + /* 198 */ {"NCIP", "Number concentration for ice particles", "-", UC_NONE}, + /* 199 */ {"EVBS", "Direct evaporation from bare soil", "W/m^2", UC_NONE}, + /* 200 */ {"EVCW", "Canopy water evaporation", "W/m^2", UC_NONE}, + /* 201 */ {"ICWAT", "Ice-free water surface", "%", UC_NONE}, + /* 202 */ {"CWDI", "Convective weather detection index", "-", UC_NONE}, + /* 203 */ {"VAFTD", "VAFTAD", "-", UC_NONE}, + /* 204 */ {"DSWRF", "Downward short wave rad. flux", "W/m^2", UC_NONE}, + /* 205 */ {"DLWRF", "Downward long wave rad. flux", "W/m^2", UC_NONE}, + /* 206 */ {"UVI", "Ultra violet index (1 hour centered at solar noon)", + "J/m^2", UC_NONE}, + /* 207 */ {"MSTAV", "Moisture availability", "%", UC_NONE}, + /* 208 */ {"SFEXC", "Exchange coefficient", "(kg/m^3)(m/s)", UC_NONE}, + /* 209 */ {"MIXLY", "No. of mixed layers next to surface", "integer", + UC_NONE}, + /* 210 */ {"TRANS", "Transpiration", "W/m^2", UC_NONE}, + /* 211 */ {"USWRF", "Upward short wave flux", "W/m^2", UC_NONE}, + /* 212 */ {"ULWRF", "Upward long wave flux", "W/m^2", UC_NONE}, + /* 213 */ {"CDLYR", "Amount of non-convective cloud", "%", UC_NONE}, + /* 214 */ {"CPRAT", "Convective Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 215 */ {"TTDIA", "Temperature tendency by all physics", "K/s", UC_NONE}, + /* 216 */ {"TTRAD", "Temperature tendency by all radiation", "K/s", UC_NONE}, + /* 217 */ {"TTPHY", "Temperature tendency by non-radiation physics", "K/s", + UC_NONE}, + /* 218 */ {"PREIX", "Precip. index (0.0-1.00)", "fraction", UC_NONE}, + /* 219 */ {"TSD1D", "Std. dev. of IR T over 1x1 deg area", "K", UC_NONE}, + /* 220 */ {"NLGSP", "Natural log of surface pressure", "ln(kPa)", UC_NONE}, + /* 221 */ {"HPBL", "Planetary boundary layer height", "m", UC_NONE}, + /* 222 */ {"5WAVH", "5-wave geopotential height", "gpm", UC_NONE}, + /* 223 */ {"CNWAT", "Plant canopy surface water", "kg/m^2", UC_NONE}, + /* 224 */ {"SOTYP", "Soil type", "Zobler 0..9", UC_NONE}, + /* 225 */ {"VGTYP", "Vegetation type", "as in SiB 0..13", UC_NONE}, + /* 226 */ {"BMIXL", "Blackadar's mixing length scale", "m", UC_NONE}, + /* 227 */ {"AMIXL", "Asymptotic mixing length scale", "m", UC_NONE}, + /* 228 */ {"PEVAP", "Potential evaporation", "kg/m^2", UC_NONE}, + /* 229 */ {"SNOHF", "Snow phase-change heat flux", "W/m^2", UC_NONE}, + /* 230 */ {"5WAVA", "5-wave geopotential height anomaly", "gpm", UC_NONE}, + /* 231 */ {"MFLUX", "Convective cloud mass flux", "Pa/s", UC_NONE}, + /* 232 */ {"DTRF", "Downward total radiation flux", "W/m^2", UC_NONE}, + /* 233 */ {"UTRF", "Upward total radiation flux", "W/m^2", UC_NONE}, + /* 234 */ {"BGRUN", "Baseflow-groundwater runoff", "kg/m^2", UC_NONE}, + /* 235 */ {"SSRUN", "Storm surface runoff", "kg/m^2", UC_NONE}, + /* 236 */ {"SIPD", "Supercooled Large Droplet (SLD) Icing Potential Diagnostic", + "-", UC_NONE}, + /* 237 */ {"O3TOT", "Total ozone", "kg/m^2", UC_NONE}, + /* 238 */ {"SNOWC", "Snow cover", "%", UC_NONE}, + /* 239 */ {"SNOT", "Snow temperature", "K", UC_K2F}, + /* 240 */ {"COVTW", "Covariance between T and w", "K*m/s", UC_NONE}, + /* 241 */ {"LRGHR", "Large scale condensation heat rate", "K/s", UC_NONE}, + /* 242 */ {"CNVHR", "Deep convective heating rate", "K/s", UC_NONE}, + /* 243 */ {"CNVMR", "Deep convective moistening rate", "kg/kg/s", UC_NONE}, + /* 244 */ {"SHAHR", "Shallow convective heating rate", "K/s", UC_NONE}, + /* 245 */ {"SHAMR", "Shallow convective moistening rate", "kg/kg/s", UC_NONE}, + /* 246 */ {"VDFHR", "Vertical diffusion heating rate", "K/s", UC_NONE}, + /* 247 */ {"VDFUA", "Vertical diffusion zonal acceleration", "m/s^2", UC_NONE}, + /* 248 */ {"VDFVA", "Vertical diffusion meridional accel", "m/s^2", + UC_NONE}, + /* 249 */ {"VDFMR", "Vertical diffusion moistening rate", "kg/kg/s", UC_NONE}, + /* 250 */ {"SWHR", "Solar radiative heating rate", "K/s", UC_NONE}, + /* 251 */ {"LWHR", "Longwave radiative heating rate", "K/s", UC_NONE}, + /* 252 */ {"CD", "Drag coefficient", "-", UC_NONE}, + /* 253 */ {"FRICV", "Friction velocity", "m/s", UC_NONE}, + /* 254 */ {"RI", "Richardson number", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +/* http://www.nco.ncep.noaa.gov/pmb/docs/on388/table2.html */ +/* Updated last on 5/24/2003 */ +GRIB1ParmTable parm_table_nceptab_129[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "km^2/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice concentration (ice=1;no ice=0)", "proportion", + UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"PAOT", "Probability anomaly of temperature", "%", UC_NONE}, + /* 129 */ {"PAOP", "Probability anomaly of precipitation", "%", UC_NONE}, + /* 130 */ {"var130", "undefined", "-", UC_NONE}, + /* 131 */ {"FRAIN", "Rain fraction of total liquid water", "", UC_NONE}, + /* 132 */ {"FICE", "Ice fraction of total condensate", "", UC_NONE}, + /* 133 */ {"FRIME", "Rime factor", "", UC_NONE}, + /* 134 */ {"CUEFI", "Convective cloud efficiency", "", UC_NONE}, + /* 135 */ {"TCOND", "Total condensate", "kg/kg", UC_NONE}, + /* 136 */ {"TCOLW", "Total column-integrated cloud water", "kg/m/m", UC_NONE}, + /* 137 */ {"TCOLI", "Total column-integrated cloud ice", "kg/m/m", UC_NONE}, + /* 138 */ {"TCOLR", "Total column-integrated rain", "kg/m/m", UC_NONE}, + /* 139 */ {"TCOLS", "Total column-integrated snow", "kg/m/m", UC_NONE}, + /* 140 */ {"TCOLC", "Total column-integrated condensate", "kg/m/m", UC_NONE}, + /* 141 */ {"PLPL", "Pressure of level from which parcel was lifted", "Pa", UC_NONE}, + /* 142 */ {"HLPL", "Height of level from which parcel was lifted", "m", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"var144", "undefined", "-", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"var155", "undefined", "-", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"var165", "undefined", "-", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"ELRDI", "Ellrod Index", "-", UC_NONE}, + /* 171 */ {"TSEC", "Seconds prior to initial reference time (defined in bytes 18-20)", + "sec", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"OZCON", "Ozone concentration", "PPB", UC_NONE}, + /* 181 */ {"OZCAT", "Categorical ozone concentration", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +/* http://www.nco.ncep.noaa.gov/pmb/docs/on388/table2.html */ +/* Updated last on 5/24/2003 */ +GRIB1ParmTable parm_table_nceptab_130[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "km^2/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice concentration (ice=1;no ice=0)", "proportion", + UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"var130", "undefined", "-", UC_NONE}, + /* 131 */ {"var131", "undefined", "-", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"var141", "undefined", "-", UC_NONE}, + /* 142 */ {"var142", "undefined", "-", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"SOILW", "Volumetric soil moisture (frozen + liquid)", + "fraction", UC_NONE}, + /* 145 */ {"PEVPR", "Potential latent heat flux (potential evaporation", + "W/m^2", UC_NONE}, + /* 146 */ {"VEGT", "Vegetation canopy temperature", "K", UC_K2F}, + /* 147 */ {"BARET", "Bare soil surface skin temperature", "K", UC_K2F}, + /* 148 */ {"AVSFT", "Average surface skin temperature", "K", UC_K2F}, + /* 149 */ {"RADT", "Effective radiative skin temperature", "K", UC_K2F}, + /* 150 */ {"SSTOR", "Surface water storage", "Kg/m^2", UC_NONE}, + /* 151 */ {"LSOIL", "Liquid soil moisture content (non-frozen)", "Kg/m^2", + UC_NONE}, + /* 152 */ {"EWATR", "Open water evaporation (standing water)", "W/m^2", + UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"GFLUX", "Ground Heat Flux", "W/m^2", UC_NONE}, + /* 156 */ {"CIN", "Convective inhibition", "J/Kg", UC_NONE}, + /* 157 */ {"CAPE", "Convective available potential energy", "J/Kg", + UC_NONE}, + /* 158 */ {"TKE", "Turbulent Kinetic Energy", "J/Kg", UC_NONE}, + /* 159 */ {"MXSALB", "Maximum snow albedo", "%", UC_NONE}, + /* 160 */ {"SOILL", "Liquid volumetric soil moisture (non-frozen)", + "Kg/m^2", UC_NONE}, + /* 161 */ {"ASNOW", "Frozen precipitation (e.g. snowfall)", "Kg/m^2", + UC_NONE}, + /* 162 */ {"ARAIN", "Liquid precipitation (rainfall)", "Kg/m^2", UC_NONE}, + /* 163 */ {"GWREC", "Groundwater recharge", "Kg/m^2", UC_NONE}, + /* 164 */ {"QREC", "Flood plain recharge", "Kg/m^2", UC_NONE}, + /* 165 */ {"SNOWT", "Snow temperature, depth-avg", "K", UC_K2F}, + /* 166 */ {"VBDSF", "Visible beam downward solar flux", "W/m^2", UC_NONE}, + /* 167 */ {"VDDSF", "Visible diffuse downward solar flux", "W/m^2", + UC_NONE}, + /* 168 */ {"NBDSF", "Near IR beam downward solar flux", "W/m^2", UC_NONE}, + /* 169 */ {"NDDSF", "Near IR diffuse downward solar flux", "W/m^2", + UC_NONE}, + /* 170 */ {"SNFALB", "Snow-free albedo", "%", UC_NONE}, +/* Not sure if 171 is reserved or is # of soil layers... */ + /* 171 */ {"RLYRS", "Number of soil layers in root zone", "-", UC_NONE}, + /* 172 */ {"MFLX", "Momentum flux", "N/m^2", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"NLAT", "Latitude (-90 to +90)", "deg", UC_NONE}, + /* 177 */ {"ELON", "East Longitude (0-360)", "deg", UC_NONE}, + /* 178 */ {"FLDCAP", "Field Capacity (soil moisture)", "fraction", + UC_NONE}, + /* 179 */ {"ACOND", "Aerodynamic conductance", "m/s", UC_NONE}, + /* 180 */ {"SNOAG", "Snow age", "s", UC_NONE}, + /* 181 */ {"CCOND", "Conopy conductance", "m/s", UC_NONE}, + /* 182 */ {"LAI", "Leaf area index (0-9)", "-", UC_NONE}, + /* 183 */ {"SFCRH", "Roughness length for heat", "m", UC_NONE}, + /* 184 */ {"SALBD", "Snow albedo (over snow area only)", "%", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"NDVI", "Normalized Difference Vegetation Index (NDVI)", "-", + UC_NONE}, + /* 188 */ {"DRIP", "Canopy drip", "Kg/m^2", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"SBSNO", "Sublimation (evaporation from snow)", "W/m^2", + UC_NONE}, + /* 199 */ {"EVBS", "Direct evaporation from bare soil", "W/m^2", UC_NONE}, + /* 200 */ {"EVCW", "Canopy water evaporation", "W/m^2", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"RSMIN", "Minimal stomatal resistance", "s/m", UC_NONE}, + /* 204 */ {"DSWRF", "Downward shortwave radiation flux", "W/m^2", UC_NONE}, + /* 205 */ {"DLWRF", "Downward longwave radiation flux", "W/m^2", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"MSTAV", "Moisture availability", "%", UC_NONE}, + /* 208 */ {"SFEXC", "Exchange coefficient", "(Kg/m^3)(m/s)", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"TRANS", "Transpiration", "W/m^2", UC_NONE}, + /* 211 */ {"USWRF", "Upward short wave radiation flux", "W/m^2", UC_NONE}, + /* 212 */ {"ULWRF", "Upward long wave radiation flux", "W/m^2", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"WILT", "Wilting point", "fraction", UC_NONE}, + /* 220 */ {"FLDCP", "Field Capacity", "fraction", UC_NONE}, + /* 221 */ {"HPBL", "Planetary boundary layer height", "-", UC_NONE}, + /* 222 */ {"SLTYP", "Surface slope type", "Index", UC_NONE}, + /* 223 */ {"CNWAT", "Plant canopy surface water", "Kg/m^2", UC_NONE}, + /* 224 */ {"SOTYP", "Soil type", "Index (0-9)", UC_NONE}, + /* 225 */ {"VGTYP", "Vegetation type", "Index (0-13)", UC_NONE}, + /* 226 */ {"BMIXL", "Blackadar's mixing length scale", "m", UC_NONE}, + /* 227 */ {"AMIXL", "Asymptotic mixing length scale", "m", UC_NONE}, + /* 228 */ {"PEVAP", "Potential evaporation", "Kg/m^2", UC_NONE}, + /* 229 */ {"SNOHF", "Snow phase-change heat flux", "W/m^2", UC_NONE}, + /* 230 */ {"SMREF", "Transpiration stress-onset (soil moisture)", + "fraction", UC_NONE}, + /* 231 */ {"SMDRY", "Direct evaporation cease (soil moisture)", "fraction", + UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"BGRUN", "Subsurface runoff (baseflow)", "Kg/m^2", UC_NONE}, + /* 235 */ {"SSRUN", "Surface runoff (non-infiltrating)", "Kg/m^2", + UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"SNOWC", "Snow cover", "%", UC_NONE}, + /* 239 */ {"SNOT", "Snow temperature", "K", UC_K2F}, + /* 240 */ {"POROS", "Soil porosity", "fraction", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"RCS", "Solar parameter in canopy conductance", "fraction", + UC_NONE}, + /* 247 */ {"RCT", "Temperature parameter in canopy conductance", + "fraction", UC_NONE}, + /* 248 */ {"RCQ", "Humidity parameter in canopy conductance", "fraction", + UC_NONE}, + /* 249 */ {"RCSOL", "Soil moisture parameter in canopy conductance", + "fraction", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"CD", "Surface drag coefficient", "-", UC_NONE}, + /* 253 */ {"FRICV", "Surface friction velocity", "m/s", UC_NONE}, + /* 254 */ {"RI", "Richardson number", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +/* Updated last on 9/8/2005 */ +GRIB1ParmTable parm_table_nceptab_131[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "km^2/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice concentration (ice=1;no ice=0)", "proportion", + UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"MSLSA", "Mean sea level pressure (Std Atm)", "Pa", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"MSLET", "Mean sea level pressure (ETA model)", "Pa", UC_NONE}, + /* 131 */ {"LFTX", "Surface lifted index", "K", UC_NONE}, + /* Delta temp. */ + /* 132 */ {"4LFTX", "Best (4-layer) lifted index", "K", UC_NONE}, + /* Delta temp. */ + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"PRESN", "Pressure (nearest grid point)", "Pa", UC_NONE}, + /* 135 */ {"MCONV", "Horizontal moisture divergence", "kg/kg/s", UC_NONE}, + /* 136 */ {"VWSH", "Vertical speed shear", "1/s", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"PVMW", "Potential vorticity (density weighted)", "1/s/m", + UC_NONE}, + /* 140 */ {"CRAIN", "Categorical rain", "yes=1;no=0", UC_NONE}, + /* 141 */ {"CFRZR", "Categorical freezing rain", "yes=1;no=0", UC_NONE}, + /* 142 */ {"CICEP", "Categorical ice pellets", "yes=1;no=0", UC_NONE}, + /* 143 */ {"CSNOW", "Categorical snow", "yes=1;no=0", UC_NONE}, + /* 144 */ {"SOILW", "Volumetric soil moisture", "fraction", UC_NONE}, + /* 145 */ {"PEVPR", "Potential evaporation rate", "W/m^2", UC_NONE}, + /* 146 */ {"VEGT", "Vegetation canopy temperature", "K", UC_K2F}, + /* 147 */ {"BARET", "Bare soil surface skin temperature", "K", UC_K2F}, + /* 148 */ {"AVSFT", "Average surface skin temperature", "K", UC_K2F}, + /* 149 */ {"RADT", "Effective radiative skin temperature", "K", UC_K2F}, + /* 150 */ {"SSTOR", "Surface water storage", "kg/m^2", UC_NONE}, + /* 151 */ {"LSOIL", "Liquid soil moisture content (non-frozen)", "kg/m^2", UC_NONE}, + /* 152 */ {"EWATR", "Open water evaporation (standing water)", "W/m^2", UC_NONE}, + /* 153 */ {"CLWMR", "Cloud water", "kg/kg", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"GFLUX", "Ground Heat Flux", "W/m^2", UC_NONE}, + /* 156 */ {"CIN", "Convective inhibition", "J/kg", UC_NONE}, + /* 157 */ {"CAPE", "Convective Available Potential Energy", "J/kg", UC_NONE}, + /* 158 */ {"TKE", "Turbulent Kinetic Energy", "J/kg", UC_NONE}, + /* 159 */ {"MXSALB", "Maximum snow albedo", "%", UC_NONE}, + /* 160 */ {"SOILL", "Liquid volumetric soil moisture (non-frozen)", "fraction", UC_NONE}, + /* 161 */ {"ASNOW", "Frozen precipitation (e.g. snowfall)", "kg/m^2", UC_NONE}, + /* 162 */ {"ARAIN", "Liquid precipitation (rainfall)", "kg/m^2", UC_NONE}, + /* 163 */ {"GWREC", "Groundwater recharge", "kg/m^2", UC_NONE}, + /* 164 */ {"QREC", "Flood plain recharge", "kg/m^2", UC_NONE}, + /* 165 */ {"SNOWT", "Snow temperature depth-avg", "K", UC_K2F}, + /* 166 */ {"VBDSF", "Visible beam downward solar flux", "W/m^2", UC_NONE}, + /* 167 */ {"VDDSF", "Visible diffuse downward solar flux", "W/m^2", + UC_NONE}, + /* 168 */ {"NBDSF", "Near IR beam downward solar flux", "W/m^2", UC_NONE}, + /* 169 */ {"NDDSF", "Near IR diffuse downward solar flux", "W/m^2", + UC_NONE}, + /* 170 */ {"SNFALB", "Snow-free albedo", "%", UC_NONE}, + /* 171 */ {"RLYRS", "Number of soil layers in root zone", "-", UC_NONE}, + /* 172 */ {"FLX", "Momentum flux", "N/m^2", UC_NONE}, + /* 173 */ {"LMH", "Mass point model surface", "-", UC_NONE}, + /* 174 */ {"LMV", "Velocity point model surface", "-", UC_NONE}, + /* 175 */ {"MLYNO", "Model layer number (from bottom up)", "-", UC_NONE}, + /* 176 */ {"NLAT", "Latitude (-90 to +90)", "deg", UC_NONE}, + /* 177 */ {"ELON", "East longitude (0-360)", "deg", UC_NONE}, + /* 178 */ {"ICMR", "Ice mixing ratio", "kg/kg", UC_NONE}, + /* 179 */ {"ACOND", "Aerodynamic conductance", "m/s", UC_NONE}, + /* 180 */ {"SNOAG", "Snow age", "s", UC_NONE}, + /* 181 */ {"CCOND", "Canopy conductance", "m/s", UC_NONE}, + /* 182 */ {"LAI", "Leaf area index (0-9)", "-", UC_NONE}, + /* 183 */ {"SFCRH", "Roughness length for heat", "m", UC_NONE}, + /* 184 */ {"SALBD", "Snow albedo (over snow cover area only)", "%", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"NDVI", "Normalized Difference Vegetation Index", "-", UC_NONE}, + /* 188 */ {"DRIP", "Rate of water dropping from canopy to gnd", "kg/m^2", UC_NONE}, + /* 189 */ {"LANDN", "Land-sea coverage (nearest neighbor)", "land=1,sea=0", UC_NONE}, + /* 190 */ {"HLCY", "Storm relative helicity", "m^2/s^2", UC_NONE}, + /* 191 */ {"NLATN", "Latitude (nearest neigbhbor) (-90 to +90)", "deg", UC_NONE}, + /* 192 */ {"ELONN", "East longitude (nearest neigbhbor) (0-360)", "deg", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"CPOFP", "Probability of frozen precipitation", "%", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"USTM", "u-component of storm motion", "m/s", UC_NONE}, + /* 197 */ {"VSTM", "v-component of storm motion", "m/s", UC_NONE}, + /* 198 */ {"SBSNO", "Sublimation (evaporation from snow)", "W/m2", UC_NONE}, + /* 199 */ {"EVBS", "Direct evaporation from bare soil", "W/m^2", UC_NONE}, + /* 200 */ {"EVCW", "Canopy water evaporation", "W/m^2", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"APCPN", "Total precipitation (nearest grid point)", "kg/m^2", UC_NONE}, + /* 203 */ {"RSMIN", "Minimal stomatal resistance", "s/m", UC_NONE}, + /* 204 */ {"DSWRF", "Downward short wave rad. flux", "W/m^2", UC_NONE}, + /* 205 */ {"DLWRF", "Downward long wave rad. flux", "W/m^2", UC_NONE}, + /* 206 */ {"ACPCPN", "Convective precipitation (nearest grid point)", "kg/m^2", UC_NONE}, + /* 207 */ {"MSTAV", "Moisture availability", "%", UC_NONE}, + /* 208 */ {"SFEXC", "Exchange coefficient", "(kg/m^3)(m/s)", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"TRANS", "Transpiration", "W/m^2", UC_NONE}, + /* 211 */ {"USWRF", "Upward short wave flux", "W/m^2", UC_NONE}, + /* 212 */ {"ULWRF", "Upward long wave flux", "W/m^2", UC_NONE}, + /* 213 */ {"CDLYR", "Amount of non-convective cloud", "%", UC_NONE}, + /* 214 */ {"CPRAT", "Convective Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"TTRAD", "Temperature tendency by all radiation", "K/s", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"HGTN", "Geopotential Height (nearest grid point)", "gpm", UC_NONE}, + /* 219 */ {"WILT", "Wilting point", "fraction", UC_NONE}, + /* 220 */ {"FLDCP", "Field Capacity", "fraction", UC_NONE}, + /* 221 */ {"HPBL", "Planetary boundary layer height", "m", UC_NONE}, + /* 222 */ {"SLTYP", "Surface slope type", "Index", UC_NONE}, + /* 223 */ {"CNWAT", "Plant canopy surface water", "kg/m^2", UC_NONE}, + /* 224 */ {"SOTYP", "Soil type", "Zobler 0..9", UC_NONE}, + /* 225 */ {"VGTYP", "Vegetation type", "as in SiB 0..13", UC_NONE}, + /* 226 */ {"BMIXL", "Blackadar's mixing length scale", "m", UC_NONE}, + /* 227 */ {"AMIXL", "Asymptotic mixing length scale", "m", UC_NONE}, + /* 228 */ {"PEVAP", "Potential evaporation", "kg/m^2", UC_NONE}, + /* 229 */ {"SNOHF", "Snow phase-change heat flux", "W/m^2", UC_NONE}, + /* 230 */ {"SMREF", "Transpiration stress-onset (soil moisture)", "fraction", UC_NONE}, + /* 231 */ {"SMDRY", "Direct evaporation cease (soil moisture)", "fraction", UC_NONE}, + /* 232 */ {"WVINC", "water vapor added by precip assimilation", "kg/m^2", UC_NONE}, + /* 233 */ {"WCINC", "water condensate added by precip assimilaition", "kg/m^2", UC_NONE}, + /* 234 */ {"BGRUN", "Baseflow-groundwater runoff", "kg/m^2", UC_NONE}, + /* 235 */ {"SSRUN", "Storm surface runoff", "kg/m^2", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"WVCONV", "Water vapor flux convergence (vertical int)", "kg/m^2", UC_NONE}, + /* 238 */ {"SNOWC", "Snow cover", "%", UC_NONE}, + /* 239 */ {"SNOT", "Snow temperature", "K", UC_K2F}, + /* 240 */ {"POROS", "Soil porosity", "fraction", UC_NONE}, + /* 241 */ {"WCCONV", "Water condensate flux convergence (vertical int)", "kg/m^2", UC_NONE}, + /* 242 */ {"WVUFLX", "Water vapor zonal flux (vertical int)", "kg/m", UC_NONE}, + /* 243 */ {"WVVFLX", "Water vapor meridional flux (vertical int)", "kg/m", UC_NONE}, + /* 244 */ {"WCUFLX", "Water condensate zonal flux (vertical int)", "kg/m", UC_NONE}, + /* 245 */ {"WCVFLX", "Water condensate meridional flux (vertical int)", "kg/m", UC_NONE}, + /* 246 */ {"RCS", "Solar parameter in canopy conductance", "fraction", UC_NONE}, + /* 247 */ {"RCT", "Temperature parameter in canopy conductance", "fraction", UC_NONE}, + /* 248 */ {"RCQ", "Humidity parameter in canopy conductance", "fraction", UC_NONE}, + /* 249 */ {"RCSOL", "Soil moisture parameter in canopy conductance", "fraction", UC_NONE}, + /* 250 */ {"SWHR", "Solar radiative heating rate", "K/s", UC_NONE}, + /* 251 */ {"LWHR", "Longwave radiative heating rate", "K/s", UC_NONE}, + /* 252 */ {"CD", "Drag coefficient", "-", UC_NONE}, + /* 253 */ {"FRICV", "Friction velocity", "m/s", UC_NONE}, + /* 254 */ {"RI", "Richardson number", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; +/* *INDENT-ON* */ + +/* NOHSRC */ +/* See: http://www.nohrsc.noaa.gov/technology/pdf/nohrsc_product_identifier.pdf */ +/* Updated last on 10/17/2005 */ +/* *INDENT-OFF* */ +GRIB1ParmTable parm_table_nohrsc[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "m^2K/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice cover (ice=1;no ice=0)", "proportion", UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"var130", "undefined", "-", UC_NONE}, + /* 131 */ {"var131", "undefined", "-", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"var141", "undefined", "-", UC_NONE}, + /* 142 */ {"var142", "undefined", "-", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"var144", "undefined", "-", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"var155", "undefined", "-", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"var165", "undefined", "-", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"var171", "undefined", "-", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"SC", "Snow cover", "50=no-snow/no-clouds,100=clouds,250=snow", + UC_NONE}, + /* 208 */ {"SCE", "Snow cover by elevation", + "0-252=Elevation in 100s of m,253=no-snow/no-cloud,254=no-cloud", + UC_NONE}, + /* 209 */ {"SWE", "Snow water equivalent", "2cm", UC_NONE}, + /* 210 */ {"SWEPN", "Snow water equivalent percent of normal", "%", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +/* + * parameter table for the NCEP/NCAR Reanalysis Project + * center = 7, subcenter = 0/2, parameter table = 1/2 + * in a SNAFU the operational and reanalysis tables diverged + * and both retained the same parameter table numbers (1,2) + * + * some of the Reanalysis files have subcenter=2 while others + * use subcenter=0 (subcenter field is not standard (7/97)) + * + * Some ways to tell Reanalysis files from OPN files + * Reanalysis: always generated by process 80 - T62 28 level model + * Original subcenter=0 Reanalysis files had + * 2.5x2.5 (144x73) lat-long grid or 192x94 Gaussian grid (PDS grid=255?) + */ +GRIB1ParmTable parm_table_ncep_reanal[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"var4", "undefined", "-", UC_NONE}, + /* 5 */ {"var5", "undefined", "-", UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Std dev of height", "m", UC_NONE}, + /* 10 */ {"HVAR", "Variance of height", "m^2", UC_NONE}, + /* 11 */ {"TMP", "Temp.", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temp.", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temp.", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic pot. temp.", "K", UC_K2F}, + /* 15 */ {"TMAX", "Max. temp.", "K", UC_K2F}, + /* 16 */ {"TMIN", "Min. temp.", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temp.", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar spectra (3)", "-", UC_NONE}, + /* 24 */ {"var24", "undefined", "-", UC_NONE}, + /* 25 */ {"TMPA", "Temp. anomaly", "K", UC_NONE}, /* Delta temp?? */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coord. vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Pressure vertical velocity", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Geometric vertical velocity", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/kg", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equiv.", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Accum. snow", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low level cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Mid level cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High level cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water temp.", "K", UC_K2F}, + /* 81 */ {"LAND", "Land-sea mask", "1=land; 0=sea", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temp.", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"RUNOF", "Runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice concentration", "ice=1;no ice=0", UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Sig height of wind waves and swell", "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Sig height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Sig height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave (surface)", "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave (surface)", "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave (top)", "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave (top)", "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation", "W/m^2", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Zonal momentum flux", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Meridional momentum flux", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "integer", UC_NONE}, + /* 128 */ {"MSLSA", "Mean sea level pressure (Std Atm)", "Pa", UC_NONE}, + /* 129 */ {"MSLMA", "Mean sea level pressure (MAPS)", "Pa", UC_NONE}, + /* 130 */ {"MSLET", "Mean sea level pressure (ETA model)", "Pa", UC_NONE}, + /* 131 */ {"LFTX", "Surface lifted index", "K", UC_NONE}, /* Delta temp. */ + /* 132 */ {"4LFTX", "Best (4-layer) lifted index", "K", UC_NONE}, + /* Delta temp. */ + /* 133 */ {"KX", "K index", "K", UC_NONE}, /* Delta temp. */ + /* 134 */ {"SX", "Sweat index", "K", UC_NONE}, /* Delta temp. */ + /* 135 */ {"MCONV", "Horizontal moisture divergence", "kg/kg/s", UC_NONE}, + /* 136 */ {"VSSH", "Vertical speed shear", "1/s", UC_NONE}, + /* 137 */ {"TSLSA", "3-hr pressure tendency", "Pa/s", UC_NONE}, + /* 138 */ {"BVF2", "Brunt-Vaisala frequency^2", "1/s^2", UC_NONE}, + /* 139 */ {"PVMW", "Potential vorticity (mass-weighted)", "1/s/m", + UC_NONE}, + /* 140 */ {"CRAIN", "Categorical rain", "yes=1;no=0", UC_NONE}, + /* 141 */ {"CFRZR", "Categorical freezing rain", "yes=1;no=0", UC_NONE}, + /* 142 */ {"CICEP", "Categorical ice pellets", "yes=1;no=0", UC_NONE}, + /* 143 */ {"CSNOW", "Categorical snow", "yes=1;no=0", UC_NONE}, + /* 144 */ {"SOILW", "Volumetric soil moisture", "fraction", UC_NONE}, + /* 145 */ {"PEVPR", "Potential evaporation rate", "W/m^2", UC_NONE}, + /* 146 */ {"CWORK", "Cloud work function", "J/kg", UC_NONE}, + /* 147 */ {"U-GWD", "Zonal gravity wave stress", "N/m^2", UC_NONE}, + /* 148 */ {"V-GWD", "Meridional gravity wave stress", "N/m^2", UC_NONE}, + /* 149 */ {"PV", "Potential vorticity", "m^2/s/kg", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"MFXDV", "Moisture flux divergence", "gr/gr*m/s/m", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"GFLUX", "Ground heat flux", "W/m^2", UC_NONE}, + /* 156 */ {"CIN", "Convective inhibition", "J/kg", UC_NONE}, + /* 157 */ {"CAPE", "Convective Avail. Pot. Energy", "J/kg", UC_NONE}, + /* 158 */ {"TKE", "Turbulent kinetic energy", "J/kg", UC_NONE}, + /* 159 */ {"CONDP", "Lifted parcel condensation pressure", "Pa", UC_NONE}, + /* 160 */ {"CSUSF", "Clear sky upward solar flux", "W/m^2", UC_NONE}, + /* 161 */ {"CSDSF", "Clear sky downward solar flux", "W/m^2", UC_NONE}, + /* 162 */ {"CSULF", "Clear sky upward long wave flux", "W/m^2", UC_NONE}, + /* 163 */ {"CSDLF", "Clear sky downward long wave flux", "W/m^2", UC_NONE}, + /* 164 */ {"CFNSF", "Cloud forcing net solar flux", "W/m^2", UC_NONE}, + /* 165 */ {"CFNLF", "Cloud forcing net long wave flux", "W/m^2", UC_NONE}, + /* 166 */ {"VBDSF", "Visible beam downward solar flux", "W/m^2", UC_NONE}, + /* 167 */ {"VDDSF", "Visible diffuse downward solar flux", "W/m^2", + UC_NONE}, + /* 168 */ {"NBDSF", "Near IR beam downward solar flux", "W/m^2", UC_NONE}, + /* 169 */ {"NDDSF", "Near IR diffuse downward solar flux", "W/m^2", + UC_NONE}, + /* 170 */ {"USTR", "U wind stress", "N/m^2", UC_NONE}, + /* 171 */ {"VSTR", "V wind stress", "N/m^2", UC_NONE}, + /* 172 */ {"MFLX", "Momentum flux", "N/m^2", UC_NONE}, + /* 173 */ {"LMH", "Mass point model surface", "integer", UC_NONE}, + /* 174 */ {"LMV", "Velocity point model surface", "integer", UC_NONE}, + /* 175 */ {"SGLYR", "Nearby model level", "integer", UC_NONE}, + /* 176 */ {"NLAT", "Latitude", "deg", UC_NONE}, + /* 177 */ {"NLON", "Longitude", "deg", UC_NONE}, + /* 178 */ {"UMAS", "Mass weighted u", "gm/m*K*s", UC_NONE}, + /* 179 */ {"VMAS", "Mass weighted v", "gm/m*K*s", UC_NONE}, + /* 180 */ {"XPRATE", "corrected precip", "kg/m^2/s", UC_NONE}, + /* 181 */ {"LPSX", "x-gradient of log pressure", "1/m", UC_NONE}, + /* 182 */ {"LPSY", "y-gradient of log pressure", "1/m", UC_NONE}, + /* 183 */ {"HGTX", "x-gradient of height", "m/m", UC_NONE}, + /* 184 */ {"HGTY", "y-gradient of height", "m/m", UC_NONE}, + /* 185 */ {"STDZ", "Std dev of Geop. hgt.", "m", UC_NONE}, + /* 186 */ {"STDU", "Std dev of zonal wind", "m/s", UC_NONE}, + /* 187 */ {"STDV", "Std dev of meridional wind", "m/s", UC_NONE}, + /* 188 */ {"STDQ", "Std dev of spec. hum.", "gm/gm", UC_NONE}, + /* 189 */ {"STDT", "Std dev of temp.", "K", UC_NONE}, + /* 190 */ {"CBUW", "Covar. u and omega", "m/s*Pa/s", UC_NONE}, + /* 191 */ {"CBVW", "Covar. v and omega", "m/s*Pa/s", UC_NONE}, + /* 192 */ {"CBUQ", "Covar. u and specific hum", "m/s*gm/gm", UC_NONE}, + /* 193 */ {"CBVQ", "Covar. v and specific hum", "m/s*gm/gm", UC_NONE}, + /* 194 */ {"CBTW", "Covar. T and omega", "K*Pa/s", UC_NONE}, + /* 195 */ {"CBQW", "Covar. spec. hum and omega", "gm/gm*Pa/s", UC_NONE}, + /* 196 */ {"CBMZW", "Covar. v and u", "m^2/s^2", UC_NONE}, + /* 197 */ {"CBTZW", "Covar. u and T", "K*m/s", UC_NONE}, + /* 198 */ {"CBTMW", "Covar. v and T", "K*m/s", UC_NONE}, + /* 199 */ {"STDRH", "Std dev of Rel. Hum.", "%", UC_NONE}, + /* 200 */ {"SDTZ", "Std dev of time tend of geop. hgt", "m", UC_NONE}, + /* 201 */ {"ICWAT", "Ice-free water surface", "%", UC_NONE}, + /* 202 */ {"SDTU", "Std dev of time tend of zonal wind", "m/s", UC_NONE}, + /* 203 */ {"SDTV", "Std dev of time tend of merid wind", "m/s", UC_NONE}, + /* 204 */ {"DSWRF", "Downward solar radiation flux", "W/m^2", UC_NONE}, + /* 205 */ {"DLWRF", "Downward long wave flux", "W/m^2", UC_NONE}, + /* 206 */ {"SDTQ", "Std dev of time tend of spec. hum", "gm/gm", UC_NONE}, + /* 207 */ {"MSTAV", "Moisture availability", "%", UC_NONE}, + /* 208 */ {"SFEXC", "Exchange coefficient", "kg*m/m^3/s", UC_NONE}, + /* 209 */ {"MIXLY", "No. of mixed layers next to sfc", "integer", UC_NONE}, + /* 210 */ {"SDTT", "Std dev of time tend of temp.", "K", UC_NONE}, + /* 211 */ {"USWRF", "Upward solar radiation flux", "W/m^2", UC_NONE}, + /* 212 */ {"ULWRF", "Upward long wave flux", "W/m^2", UC_NONE}, + /* 213 */ {"CDLYR", "Non-convective cloud", "%", UC_NONE}, + /* 214 */ {"CPRAT", "Convective precip. rate", "kg/m^2/s", UC_NONE}, + /* 215 */ {"TTDIA", "Temp. tendency by all physics", "K/s", UC_NONE}, + /* 216 */ {"TTRAD", "Temp. tendency by all radiation", "K/s", UC_NONE}, + /* 217 */ {"TTPHY", "Temp. tendency by nonrad physics", "K/s", UC_NONE}, + /* 218 */ {"PREIX", "Precipitation index", "fraction", UC_NONE}, + /* 219 */ {"TSD1D", "Std dev of IR T over 1x1 deg area", "K", UC_NONE}, + /* 220 */ {"NLSGP", "Natural log of surface pressure", "ln(kPa)", UC_NONE}, + /* 221 */ {"SDTRH", "Std dev of time tend of rel hum", "%", UC_NONE}, + /* 222 */ {"5WAVH", "5-wave geopotential height", "gpm", UC_NONE}, + /* 223 */ {"CNWAT", "Plant canopy surface water", "kg/m^2", UC_NONE}, + /* 224 */ {"PLTRS", "Max. stomato plant resistance", "s/m", UC_NONE}, + /* 225 */ {"RHCLD", "RH-type cloud cover", "%", UC_NONE}, + /* 226 */ {"BMIXL", "Blackadar's mixing length scale", "m", UC_NONE}, + /* 227 */ {"AMIXL", "Asymptotic mixing length scale", "m", UC_NONE}, + /* 228 */ {"PEVAP", "Pot. evaporation", "kg/m^2", UC_NONE}, + /* 229 */ {"SNOHF", "Snow melt heat flux", "W/m^2", UC_NONE}, + /* 230 */ {"SNOEV", "Snow sublimation heat flux", "W/m^2", UC_NONE}, + /* 231 */ {"MFLUX", "Convective cloud mass flux", "Pa/s", UC_NONE}, + /* 232 */ {"DTRF", "Downward total radiation flux", "W/m^2", UC_NONE}, + /* 233 */ {"UTRF", "Upward total radiation flux", "W/m^2", UC_NONE}, + /* 234 */ {"BGRUN", "Baseflow-groundwater runoff", "kg/m^2", UC_NONE}, + /* 235 */ {"SSRUN", "Storm surface runoff", "kg/m^2", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"OZONE", "Total column ozone", "Dobson", UC_NONE}, + /* 238 */ {"SNOWC", "Snow cover", "%", UC_NONE}, + /* 239 */ {"SNOT", "Snow temp.", "K", UC_K2F}, + /* 240 */ {"GLCR", "Permanent snow points", "mask", UC_NONE}, + /* 241 */ {"LRGHR", "Large scale condensation heating", "K/s", UC_NONE}, + /* 242 */ {"CNVHR", "Deep convective heating", "K/s", UC_NONE}, + /* 243 */ {"CNVMR", "Deep convective moistening", "kg/kg/s", UC_NONE}, + /* 244 */ {"SHAHR", "Shallow convective heating", "K/s", UC_NONE}, + /* 245 */ {"SHAMR", "Shallow convective moistening", "kg/kg/s", UC_NONE}, + /* 246 */ {"VDFHR", "Vertical diffusion heating", "K/s", UC_NONE}, + /* 247 */ {"VDFUA", "Vertical diffusion zonal accel", "m/s^2", UC_NONE}, + /* 248 */ {"VDFVA", "Vertical diffusion meridional accel", "m/s^2", + UC_NONE}, + /* 249 */ {"VDFMR", "Vertical diffusion moistening", "kg/kg/s", UC_NONE}, + /* 250 */ {"SWHR", "Solar radiative heating", "K/s", UC_NONE}, + /* 251 */ {"LWHR", "Longwave radiative heating", "K/s", UC_NONE}, + /* 252 */ {"CD", "Drag coefficient", "-", UC_NONE}, + /* 253 */ {"FRICV", "Friction velocity", "m/s", UC_NONE}, + /* 254 */ {"RI", "Richardson number", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +GRIB1ParmTable parm_table_ncep_tdl[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Pot. vorticity", "km^2/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "M", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Std dev of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temp.", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temp.", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temp.", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic pot. temp.", "K", UC_K2F}, + /* 15 */ {"TMAX", "Max. temp.", "K", UC_K2F}, + /* 16 */ {"TMIN", "Min. temp.", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temp.", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta temp */ + /* 25 */ {"TMPA", "Temp. anomaly", "K", UC_NONE}, /* Delta temp?? */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coord. vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Pressure vertical velocity", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Geometric vertical velocity", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/kg", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equiv.", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Accum. snow", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low level cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Mid level cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High level cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta temp?? */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water temp.", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1;sea=0)", "fraction", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temp.", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice concentration (ice=1;no ice=0)", "fraction", + UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Sig height of wind waves and swell", "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Sig height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Sig height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave (surface)", "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave (surface)", "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave (top)", "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave (top)", "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance with respect to wave no.", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance with respect to wave len.", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Zonal momentum flux", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Meridional momentum flux", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "", UC_NONE}, + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"var130", "undefined", "-", UC_NONE}, + /* 131 */ {"MINF", "Nighttime Min Temp", "F", UC_NONE}, + /* 132 */ {"MAXF", "Daytime Max Temp", "F", UC_NONE}, + /* 133 */ {"POP", "Prob of 0.01 In. of Precip (PoP)", "%", UC_NONE}, + /* 134 */ {"NMINF", "Normal Min Temperature", "F", UC_NONE}, + /* 135 */ {"NMAXF", "Normal Max Temperature", "F", UC_NONE}, + /* 136 */ {"NPOP", "Normal Rel. Freq. of 0.01 In of Pcp.", "%", UC_NONE}, + /* 137 */ {"DMINF", "Departure from Normal Min", "F", UC_NONE}, + /* 138 */ {"DMAXF", "Departure from Normal Max", "F", UC_NONE}, + /* 139 */ {"DPOP", "Departure from Normal PoP", "%", UC_NONE}, + /* 140 */ {"P--I", "Expected Value of Precip", "in", UC_NONE}, + /* 141 */ {"PQPF1", "Prob. of QPF >= 0.01 Inches", "%", UC_NONE}, + /* 142 */ {"PQPF2", "Prob. of QPF >= 0.10 Inches", "%", UC_NONE}, + /* 143 */ {"PQPF3", "Prob. of QPF >= 0.25 Inches", "%", UC_NONE}, + /* 144 */ {"PQPF4", "Prob. of QPF >= 0.50 Inches", "%", UC_NONE}, + /* 145 */ {"PQPF5", "Prob. of QPF >= 1.00 Inches", "%", UC_NONE}, + /* 146 */ {"PQPF6", "Prob. of QPF >= 2.00 Inches", "%", UC_NONE}, + /* 147 */ {"PTSTM", "Prob. of Tstms (Radar)", "%", UC_NONE}, + /* 148 */ {"TMPK", "Temperature", "K", UC_K2F}, + /* 149 */ {"PTSTL", "Prob. of Tstms (Ltng)", "%", UC_NONE}, + /* 150 */ {"CPSVR", "Cond Prob. of Severe Weather", "%", UC_NONE}, + /* 151 */ {"CPSVG", "Cond Prob. of Severe Wx (GRID)", "%", UC_NONE}, + /* 152 */ {"DWPK", "Dewpoint", "K", UC_K2F}, + /* 153 */ {"CATMH", "Index of LGT-MDT Clr Air Turb(HI)", "num", UC_NONE}, + /* 154 */ {"CATSH", "Index of MDT-SVR Clr Air Turb(HI)", "num", UC_NONE}, + /* 155 */ {"ICEH", "Index of Icing (HIGH)", "num", UC_NONE}, + /* 156 */ {"CATML", "Index of LGT-MDT Clr Air Turb(LO)", "num", UC_NONE}, + /* 157 */ {"CATSL", "Index of MDT-SVR Clr Air Turb(HI)", "num", UC_NONE}, + /* 158 */ {"ICEL", "Index of Icing (Low)", "num", UC_NONE}, + /* 159 */ {"TMPF", "Temperature", "F", UC_NONE}, + /* 160 */ {"DWPF", "Dewpoint", "F", UC_NONE}, + /* 161 */ {"IWSPK", "Inflated Wind Speed", "kts", UC_NONE}, + /* 162 */ {"PSKCL", "Prob of OPQ Sky:Clear", "%", UC_NONE}, + /* 163 */ {"PSKSC", "Prob of OPQ Sky:Scattered", "%", UC_NONE}, + /* 164 */ {"PSKBK", "Prob of OPQ Sky:Broken", "%", UC_NONE}, + /* 165 */ {"PSKOV", "Prob of OPQ Sky:Overcast", "%", UC_NONE}, + /* 166 */ {"PCIG1", "Prob of CIG HGT < 200 Ft", "%", UC_NONE}, + /* 167 */ {"PCIG2", "Prob of CIG HGT 200-400 Ft", "%", UC_NONE}, + /* 168 */ {"PCIG3", "Prob of CIG HGT 500-900 Ft", "%", UC_NONE}, + /* 169 */ {"PCIG4", "Prob of CIG HGT 1000-3000 Ft", "%", UC_NONE}, + /* 170 */ {"PCIG5", "Prob of CIG HGT 3100-6500 Ft", "%", UC_NONE}, + /* 171 */ {"PCIG6", "Prob of CIG HGT 6600-12000 Ft", "%", UC_NONE}, + /* 172 */ {"PCIG7", "Prob of CIG HGT > 12000 Ft", "%", UC_NONE}, + /* 173 */ {"PVIS1", "Prob of VIS < 1/2 Mile", "%", UC_NONE}, + /* 174 */ {"PVIS2", "Prob of VIS 1/2-7/8 Mile", "%", UC_NONE}, + /* 175 */ {"PVIS3", "Prob of VIS 1 - 2 3/4 Mile", "%", UC_NONE}, + /* 176 */ {"PVIS4", "Prob of VIS 3-5 Mile", "%", UC_NONE}, + /* 177 */ {"PVIS5", "Prob of VIS > 5 Mile", "%", UC_NONE}, + /* 178 */ {"NMINK", "Normal Min Temperature", "K", UC_K2F}, + /* 179 */ {"NMAXK", "Normal Max Temperature", "K", UC_K2F}, + /* 180 */ {"DMINK", "Departure From Normal Min", "K", UC_NONE}, + /* Delta Temp. */ + /* 181 */ {"DMAXK", "Departure From Normal Max", "K", UC_NONE}, + /* Delta Temp. */ + /* 182 */ {"KINDX", "K Index", "num", UC_NONE}, + /* 183 */ {"NVD", "Net Vertical Displacement", "Pa", UC_NONE}, + /* 184 */ {"SOLEN", "Solar Energy", "J/m^2", UC_NONE}, + /* 185 */ {"POVBL", "Prob. of Blowing Obvis", "%", UC_NONE}, + /* 186 */ {"POVHZ", "Prob. of Haze Obvis", "%", UC_NONE}, + /* 187 */ {"POVFG", "Prob. of Fog Obvis", "%", UC_NONE}, + /* 188 */ {"POVNO", "Prob. of No Obvis", "%", UC_NONE}, + /* 189 */ {"BCIG", "Best Category of Ceiling Height", "num", UC_NONE}, + /* 190 */ {"BVIS", "Best Category of Visibility", "num", UC_NONE}, + /* 191 */ {"BOBV", "Best Category of Obvis", "num", UC_NONE}, + /* 192 */ {"CPOS", "Cond. Prob. of Snow (CPoS)", "%", UC_NONE}, + /* 193 */ {"NCPOS", "Normal CPoS", "%", UC_NONE}, + /* 194 */ {"WINDM", "Mean Wind Speed (Wind)", "m/s", UC_NONE}, + /* 195 */ {"NWNDM", "Normal Mean Wind Speed", "m/s", UC_NONE}, + /* 196 */ {"CLDS", "Mean Opaque Cloudiness (Clds)", "tnths", UC_NONE}, + /* 197 */ {"NCLDS", "Normal Mean Opaque Cloudiness", "tnths", UC_NONE}, + /* 198 */ {"SUNSH", "Percent of possible sunshine", "%", UC_NONE}, + /* 199 */ {"HRSUN", "Hours of sunshine", "hrs", UC_NONE}, + /* 200 */ {"CPOSN", "Cond. Prob. of Snow", "%", UC_NONE}, + /* 201 */ {"CPOZP", "Cond. Prob. of Freezing Precip", "%", UC_NONE}, + /* 202 */ {"CPORA", "Cond. Prob. of Rain", "%", UC_NONE}, + /* 203 */ {"DPDK", "Dewpoint Depression", "K", UC_NONE}, /* Delta Temp. */ + /* 204 */ {"RELH", "Relative Humidity", "%", UC_NONE}, + /* 205 */ {"DPDF", "Dew Point Depression", "F", UC_NONE}, + /* 206 */ {"PSNA1", "Prob. of Snow Amount >= Trace", "%", UC_NONE}, + /* 207 */ {"PSNA2", "Prob. of Snow Amount >= 2 Inches", "%", UC_NONE}, + /* 208 */ {"PSNA3", "Prob. of Snow Amount >= 4 Inches", "%", UC_NONE}, + /* 209 */ {"PSNA4", "Prob. of Snow Amount >= 6 Inches", "%", UC_NONE}, + /* 210 */ {"BSNA", "Best Category for Snow Amount", "num", UC_NONE}, + /* 211 */ {"CPDRZ", "Cond. Prob. of Drizzle", "%", UC_NONE}, + /* 212 */ {"CPSTY", "Cond. Prob. of Cont (Steady) Precip", "%", UC_NONE}, + /* 213 */ {"CPSHW", "Cond. Prob. of Showers", "%", UC_NONE}, + /* 214 */ {"BPCPT", "Best Category of Precip Type", "num", UC_NONE}, + /* 215 */ {"BQPF", "Best Category of QPF", "num", UC_NONE}, + /* 216 */ {"BPCHR", "Best Cat of Precip Char Type", "num", UC_NONE}, + /* 217 */ {"BSKY", "Best Cat of Opaque Sky Cover", "num", UC_NONE}, + /* 218 */ {"DRCT", "Wind Direction", "deg true", UC_NONE}, + /* 219 */ {"HTINF", "Heat Index", "F", UC_NONE}, + /* 220 */ {"WNCHF", "Wind Chill Temperature", "F", UC_NONE}, + /* 221 */ {"HDGDY", "Heating/Cooling Degree Days", "num", UC_NONE}, + /* 222 */ {"MINK", "Nighttime Min Temp", "K", UC_K2F}, + /* 223 */ {"MAXK", "Daytime Max Temp", "K", UC_K2F}, + /* 224 */ {"P--M", "Expected Value of Precipitation", "kg/m^2", UC_NONE}, + /* 225 */ {"IWSPM", "Inflated Wind Speed", "m/s", UC_NONE}, + /* 226 */ {"HTINK", "Heat Index", "K", UC_K2F}, + /* 227 */ {"WNCHK", "Wind Chill Temperature", "K", UC_K2F}, + /* 228 */ {"DINDM", "Departure from Normal Wind", "m/s", UC_NONE}, + /* 229 */ {"WINDK", "Mean Wind Speed (Wind)", "kts", UC_NONE}, + /* 230 */ {"NWNDK", "Normal Mean Wind Speed", "kts", UC_NONE}, + /* 231 */ {"DINDK", "Departure from Normal Wind", "kts", UC_NONE}, + /* 232 */ {"WSPK", "Wind Speed", "kts", UC_NONE}, + /* 233 */ {"WSPM", "Wind Speed", "m/s", UC_NONE}, + /* 234 */ {"DCLDS", "Departure from Normal Clds", "tnths", UC_NONE}, + /* 235 */ {"DCPOS", "Departure from Normal CPoS", "%", UC_NONE}, + /* 236 */ {"TOTOT", "Total Totals Index", "num", UC_NONE}, + /* 237 */ {"CINST", "Convective Instability(SFC-700mb)", "K", UC_NONE}, + /* Delta Temp. */ + /* 238 */ {"KSOLN", "Solar Energy", "KWH/m^2", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"BTSTM", "Categorical T-Storm (Ltng)", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"BSVR", "Categorical Severe Weather", "num", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE} +}; + +GRIB1ParmTable parm_table_ncep_mdl[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Pot. vorticity", "km^2/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "M", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Std dev of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temp.", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temp.", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temp.", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic pot. temp.", "K", UC_K2F}, + /* 15 */ {"TMAX", "Max. temp.", "K", UC_K2F}, + /* 16 */ {"TMIN", "Min. temp.", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temp.", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta Temp. */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temp. anomaly", "K", UC_NONE}, /* delta temp. */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coord. vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Pressure vertical velocity", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Geometric vertical velocity", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/kg", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equiv.", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Accum. snow", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low level cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Mid level cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High level cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water temp.", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1;sea=0)", "fraction", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temp.", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice concentration (ice=1;no ice=0)", "fraction", + UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Sig height of wind waves and swell", "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Sig height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Sig height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave (surface)", "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave (surface)", "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave (top)", "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave (top)", "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance with respect to wave no.", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance with respect to wave len.", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Zonal momentum flux", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Meridional momentum flux", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "", UC_NONE}, + /* 128 */ {"TMPF", "Temperature", "F", UC_NONE}, + /* 129 */ {"MAXK", "Daytime Max Temp", "K", UC_K2F}, + /* 130 */ {"MAXF", "Daytime Max Temp", "F", UC_NONE}, + /* 131 */ {"NMAXK", "Normal Max Temperature", "K", UC_K2F}, + /* 132 */ {"NMAXF", "Normal Max Temperature", "F", UC_NONE}, + /* 133 */ {"DMAXK", "Departure From Normal Max", "K", UC_K2F}, + /* 134 */ {"DMAXF", "Departure From Normal Max", "F", UC_NONE}, + /* 135 */ {"MINK", "Nighttime Min Temp", "K", UC_K2F}, + /* 136 */ {"MINF", "Nighttime Min Temp", "F", UC_NONE}, + /* 137 */ {"NMINK", "Normal Min Temperature", "K", UC_K2F}, + /* 138 */ {"NMINF", "Normal Nighttime Min Temp", "F", UC_NONE}, + /* 139 */ {"DMINK", "Departure From Normal Min", "K", UC_K2F}, + /* 140 */ {"DMINF", "Departure From Normal Min", "F", UC_NONE}, + /* 141 */ {"DWPF", "Dew Point Temperature", "F", UC_NONE}, + /* 142 */ {"DPDF", "Dew Point Depression", "F", UC_NONE}, + /* 143 */ {"HTINF", "Heat Index", "F", UC_NONE}, + /* 144 */ {"WNCHF", "Wind Chill", "F", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"POP", "Prob of 0.01 In. of Precip (PoP)", "%", UC_NONE}, + /* 147 */ {"PQPF2", "Prob of QPF >= 0.10 Inches", "%", UC_NONE}, + /* 148 */ {"PQPF3", "Prob of QPF >= 0.25 Inches", "%", UC_NONE}, + /* 149 */ {"PQPF4", "Prob of QPF >= 0.50 Inches", "%", UC_NONE}, + /* 150 */ {"PQPF5", "Prob of QPF >= 1.00 Inches", "%", UC_NONE}, + /* 151 */ {"PQPF6", "Prob of QPF >= 2.00 Inches", "%", UC_NONE}, + /* 152 */ {"PQPF7", "Prob of QPF >= 3.00 Inches Future", "%", UC_NONE}, + /* 153 */ {"BQPF", "Best Category of QPF", "-", UC_NONE}, + /* 154 */ {"NPOP", "Nml Rel. Freq. of 0.01 In of Pcp", "%", UC_NONE}, + /* 155 */ {"DPOP", "Departure From Nml of 0.01 PoP", "%", UC_NONE}, + /* 156 */ {"PCPM", "Expected Value of Precipitation", "mm", UC_NONE}, + /* 157 */ {"PCPI", "Expected Value of Precipitation", "in", UC_NONE}, + /* 158 */ {"CPCPM", "Conditional Expected Precip Amt", "mm", UC_NONE}, + /* 159 */ {"CPCPI", "Conditional Expected Precip Amt", "in", UC_NONE}, + /* 160 */ {"PSNA1", "Prob of Snow Amount >= 0.10", "%", UC_NONE}, + /* 161 */ {"PSNA2", "Prob of Snow Amount >= 2 Inches", "%", UC_NONE}, + /* 162 */ {"PSNA3", "Prob of Snow Amount >= 4 Inches", "%", UC_NONE}, + /* 163 */ {"PSNA4", "Prob of Snow Amount >= 6 Inches", "%", UC_NONE}, + /* 164 */ {"PSNA5", "Prob of Snow Amount >= 8 Inches", "%", UC_NONE}, + /* 165 */ {"BSNA", "Best Category For Snow Amount", "-", UC_NONE}, + /* 166 */ {"SNWM", "Expected Value of Snow Amount", "mm", UC_NONE}, + /* 167 */ {"SNWI", "Expected Value of Snow Amount", "in", UC_NONE}, + /* 168 */ {"MWSPK", "Inflated Max Wind Speed", "kts", UC_NONE}, + /* 169 */ {"IWSPM", "Inflated Wind Speed", "m/s", UC_NONE}, + /* 170 */ {"SKNT", "Inflated Wind Speed", "kts", UC_NONE}, + /* 171 */ {"PWSP1", "Prob of Max Wind Speed 0-12 kts", "%", UC_NONE}, + /* 172 */ {"PWSP2", "Prob of Max Wind Speed 13-21 kts", "%", UC_NONE}, + /* 173 */ {"PWSP3", "Prob of Max Wind Speed 22-31 kts", "%", UC_NONE}, + /* 174 */ {"PWSP4", "Prob of Max Wind Speed >= 32 kts", "%", UC_NONE}, + /* 175 */ {"WSPDC", "Categorical Max Wind Speed", "-", UC_NONE}, + /* 176 */ {"XSPDM", "Expected Value of Max Wind Speed", "m/s", UC_NONE}, + /* 177 */ {"XSPDK", "Expected Value of Max Wind Speed", "kts", UC_NONE}, + /* 178 */ {"PWDRN", "Prob of Wind Direction North", "%", UC_NONE}, + /* 179 */ {"PWDRNE", "Prob of Wind Direction NorthEast", "%", UC_NONE}, + /* 180 */ {"PWDRE", "Prob of Wind Direction East", "%", UC_NONE}, + /* 181 */ {"PWDRSE", "Prob of Wind Direction SouthEast", "%", UC_NONE}, + /* 182 */ {"PWDRS", "Prob of Wind Direction South", "%", UC_NONE}, + /* 183 */ {"PWDRSW", "Prob of Wind Direction SouthWest", "%", UC_NONE}, + /* 184 */ {"PWDRW", "Prob of Wind Direction West", "%", UC_NONE}, + /* 185 */ {"PWDRNW", "Prob of Wind Direction NorthWest", "%", UC_NONE}, + /* 186 */ {"WDIRC", "Categorical Wind Direction", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"PSKCL", "Prob of Total Sky:Clear", "%", UC_NONE}, + /* 189 */ {"PSKFW", "Prob of Total Sky:Few (Future)", "%", UC_NONE}, + /* 190 */ {"PSKSC", "Prob of Total Sky:Scattered", "%", UC_NONE}, + /* 191 */ {"PSKBK", "Prob of Total Sky:Broken", "%", UC_NONE}, + /* 192 */ {"PSKOV", "Prob of Total Sky:Overcast", "%", UC_NONE}, + /* 193 */ {"SKYC", "Categorical Total Sky Cover", "-", UC_NONE}, + /* 194 */ {"MSKCL", "Prob Mean Sky Cvr:Clear (Future)", "%", UC_NONE}, + /* 195 */ {"MSKOV", "Prob Mean Sky Cvr:Overcast (Future)", "%", UC_NONE}, + /* 196 */ {"MSKMC", "Prob Mean Sky Cvr:Mostly Clr", "%", UC_NONE}, + /* 197 */ {"MSKPC", "Prob Mean Sky Cvr:Partly Cldy", "%", UC_NONE}, + /* 198 */ {"MSKMO", "Prob Mean Sky Cvr:Mostly Cldy", "%", UC_NONE}, + /* 199 */ {"MSKYC", "Categorical Mean Sky Cover", "-", UC_NONE}, + /* 200 */ {"PCIG1", "Prob of CIG Hgt < 200 Ft", "%", UC_NONE}, + /* 201 */ {"PCIG2", "Prob of CIG Hgt 200-400 Ft", "%", UC_NONE}, + /* 202 */ {"PCIG3", "Prob of CIG Hgt 500-900 Ft", "%", UC_NONE}, + /* 203 */ {"PCIG4", "Prob of CIG Hgt 1000-3000 Ft", "%", UC_NONE}, + /* 204 */ {"PCIG5", "Prob of CIG Hgt 3100-6500 Ft", "%", UC_NONE}, + /* 205 */ {"PCIG6", "Prob of CIG Hgt 6600-12000 Ft", "%", UC_NONE}, + /* 206 */ {"PCIG7", "Prob of CIG Hgt > 12000 Ft", "%", UC_NONE}, + /* 207 */ {"BCIG", "Best Category of Ceiling Height", "-", UC_NONE}, + /* 208 */ {"PVIS1", "Prob of Vis <=1/4 Mile", "%", UC_NONE}, + /* 209 */ {"PVIS2", "Prob of Vis <=1/2 Mile", "%", UC_NONE}, + /* 210 */ {"PVIS3", "Prob of Vis <=7/8 Mile", "%", UC_NONE}, + /* 211 */ {"PVIS4", "Prob of Vis <=2 3/4 Miles", "%", UC_NONE}, + /* 212 */ {"PVIS5", "Prob of Vis <=5 Miles", "%", UC_NONE}, + /* 213 */ {"PVIS6", "Prob of Vis <=6 Miles", "%", UC_NONE}, + /* 214 */ {"VISC", "Categorical Visibility", "-", UC_NONE}, + /* 215 */ {"POBVN", "Prob of Obstruction to Vis: None", "%", UC_NONE}, + /* 216 */ {"POBVH", "Prob of Obstruction to Vis: Haze", "%", UC_NONE}, + /* 217 */ {"POBVM", "Prob of Obstruction to Vis: Mist", "%", UC_NONE}, + /* 218 */ {"POBVF", "Prob of Obstruction to Vis: Fog", "%", UC_NONE}, + /* 219 */ {"POVBL", "Prob of Blowing Ob-Vis", "%", UC_NONE}, + /* 220 */ {"OBVC", "Best Category of Ob-Vis", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"NTSM", "Normal Prob of Thunderstorms", "%", UC_NONE}, + /* 223 */ {"CSVR", "Cond Prob of Severe Weather", "%", UC_NONE}, + /* 224 */ {"USVR", "Uncond Prob of Severe Wx", "%", UC_NONE}, + /* 225 */ {"NSVR", "Normal Prob of Severe Wx", "%", UC_NONE}, + /* 226 */ {"UHAI", "Unconditional Prob of Hail", "%", UC_NONE}, + /* 227 */ {"UTOR", "Unconditional Prob of Tornado", "%", UC_NONE}, + /* 228 */ {"UTSW", "Uncond Prob of Damaging Wind", "%", UC_NONE}, + /* 229 */ {"CFZI", "Cond Prob Frzing Precip (Instant)", "%", UC_NONE}, + /* 230 */ {"UFZI", "Uncnd Prob Frzing Precip (Instnt)", "%", UC_NONE}, + /* 231 */ {"CZNI", "Cond Prob Frozen Precip (Instant)", "%", UC_NONE}, + /* 232 */ {"UZNI", "Uncnd Prob Frozen Precip (Instnt)", "%", UC_NONE}, + /* 233 */ {"CLQI", "Cond Prob Liquid Precip (Instant)", "%", UC_NONE}, + /* 234 */ {"ULQI", "Uncnd Prob Liquid Precip (Instnt)", "%", UC_NONE}, + /* 235 */ {"PTYPI", "Categorical Precip Type (Instant)", "-", UC_NONE}, + /* 236 */ {"CPOZP", "Cond Prob of Frzing Precip", "%", UC_NONE}, + /* 237 */ {"UPOZP", "Uncond Prob of Frzing Precip", "%", UC_NONE}, + /* 238 */ {"CPOS", "Cond Prob of Snow (CPoS)", "%", UC_NONE}, + /* 239 */ {"UPOS", "Uncond Prob of Snow (UPoS)", "%", UC_NONE}, + /* 240 */ {"CPORS", "Cond Prob of Rain/Snow Mixed", "%", UC_NONE}, + /* 241 */ {"UPORS", "Uncond Prob of Rain/Snow Mixed", "%", UC_NONE}, + /* 242 */ {"CPORA", "Cond Prob of Rain", "%", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"BPCPT", "Best Category of Precip Type", "-", UC_NONE}, + /* 245 */ {"POPOH", "POPO Precip Occurring at an Hour", "%", UC_NONE}, + /* 246 */ {"POPOP", "POPO Precip During a Period", "%", UC_NONE}, + /* 247 */ {"CPDRZ", "Cond Prob of Drizzle", "%", UC_NONE}, + /* 248 */ {"CPSTY", "Cond Prob of Cont (Steady) Precip", "%", UC_NONE}, + /* 249 */ {"CPSHW", "Cond Prob of Showers", "%", UC_NONE}, + /* 250 */ {"BPCHR", "Best Cat Precip Characteristic", "-", UC_NONE}, + /* 251 */ {"SUNSH", "Percent of Possible Sunshine", "%", UC_NONE}, + /* 252 */ {"HRSUN", "Hours of Sunshine", "hrs", UC_NONE}, + /* 253 */ {"SCQP", "Scan 0-3H Categorical QPF", "-", UC_NONE}, + /* 254 */ {"SCTS", "Scan 0-3H C-G Lightning Prob", "%", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +/* + * parameter table for ocean modeling branch (OMB) of NCEP + * center = 7, subcenter = EMC, parameter table = 128 + * 12/31/2001 added REV + */ + +GRIB1ParmTable parm_table_omb[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"var1", "undefined", "-", UC_NONE}, + /* 2 */ {"GHz6", "6.6 GHz", "K", UC_NONE}, + /* 3 */ {"GHz10", "10.7 GHz", "K", UC_NONE}, + /* 4 */ {"GHz18", "18.0 GHz", "K", UC_NONE}, + /* 5 */ {"GHz19V", "SSMI 19 GHz, Vertical Polarization", "K", UC_NONE}, + /* 6 */ {"GHz19H", "SSMI 19 GHz, Horizontal Polarization", "K", UC_NONE}, + /* 7 */ {"GHz21", "21.0 GHz", "K", UC_NONE}, + /* 8 */ {"GHz22V", "SSMI 22 GHz, Vertical Polarization", "K", UC_NONE}, + /* 9 */ {"GHz37V", "SSMI 37 GHz, Vertical Polarization", "K", UC_NONE}, + /* 10 */ {"GHz37H", "SSMI 37 GHz, Horizontal Polarization", "K", UC_NONE}, + /* 11 */ {"MSU1", "MSU Ch 1 - 50.30 GHz", "K", UC_NONE}, + /* 12 */ {"MSU2", "MSU Ch 2 - 53.74 GHz", "K", UC_NONE}, + /* 13 */ {"MSU3", "MSU Ch 3 - 54.96 GHz", "K", UC_NONE}, + /* 14 */ {"MSU4", "MSU Ch 4 - 57.95 GHz", "K", UC_NONE}, + /* 15 */ {"GHz85V", "SSMI 85 GHz, Vertical Polarization", "K", UC_NONE}, + /* 16 */ {"GHz85H", "SSMI 85 GHz, Horizontal Polarization", "K", UC_NONE}, + /* 17 */ {"GHz91", "91.65 GHz", "K", UC_NONE}, + /* 18 */ {"GHz150", "150 GHz", "K", UC_NONE}, + /* 19 */ {"GHz183pm7", "183 +- 7 GHz", "K", UC_NONE}, + /* 20 */ {"GHz183pm3", "183 +- 3 GHz", "K", UC_NONE}, + /* 21 */ {"GHz183pm1", "183 +- 1 GHz", "K", UC_NONE}, + /* 22 */ {"SSMT1C1", "SSM/T1 - ch 1", "K", UC_NONE}, + /* 23 */ {"SSMT1C2", "SSM/T1 - ch 2", "K", UC_NONE}, + /* 24 */ {"SSMT1C3", "SSM/T1 - ch 3", "K", UC_NONE}, + /* 25 */ {"SSMT1C4", "SSM/T1 - ch 4", "K", UC_NONE}, + /* 26 */ {"SSMT1C5", "SSM/T1 - ch 5", "K", UC_NONE}, + /* 27 */ {"SSMT1C6", "SSM/T1 - ch 6", "K", UC_NONE}, + /* 28 */ {"SSMT1C7", "SSM/T1 - ch 7", "K", UC_NONE}, + /* 29 */ {"var29", "undefined", "-", UC_NONE}, + /* 30 */ {"var30", "undefined", "-", UC_NONE}, + /* 31 */ {"var31", "undefined", "-", UC_NONE}, + /* 32 */ {"var32", "undefined", "-", UC_NONE}, + /* 33 */ {"var33", "undefined", "-", UC_NONE}, + /* 34 */ {"var34", "undefined", "-", UC_NONE}, + /* 35 */ {"var35", "undefined", "-", UC_NONE}, + /* 36 */ {"var36", "undefined", "-", UC_NONE}, + /* 37 */ {"var37", "undefined", "-", UC_NONE}, + /* 38 */ {"var38", "undefined", "-", UC_NONE}, + /* 39 */ {"var39", "undefined", "-", UC_NONE}, + /* 40 */ {"var40", "undefined", "-", UC_NONE}, + /* 41 */ {"var41", "undefined", "-", UC_NONE}, + /* 42 */ {"var42", "undefined", "-", UC_NONE}, + /* 43 */ {"var43", "undefined", "-", UC_NONE}, + /* 44 */ {"var44", "undefined", "-", UC_NONE}, + /* 45 */ {"var45", "undefined", "-", UC_NONE}, + /* 46 */ {"var46", "undefined", "-", UC_NONE}, + /* 47 */ {"var47", "undefined", "-", UC_NONE}, + /* 48 */ {"var48", "undefined", "-", UC_NONE}, + /* 49 */ {"var49", "undefined", "-", UC_NONE}, + /* 50 */ {"var50", "undefined", "-", UC_NONE}, + /* 51 */ {"var51", "undefined", "-", UC_NONE}, + /* 52 */ {"var52", "undefined", "-", UC_NONE}, + /* 53 */ {"var53", "undefined", "-", UC_NONE}, + /* 54 */ {"var54", "undefined", "-", UC_NONE}, + /* 55 */ {"var55", "undefined", "-", UC_NONE}, + /* 56 */ {"var56", "undefined", "-", UC_NONE}, + /* 57 */ {"var57", "undefined", "-", UC_NONE}, + /* 58 */ {"var58", "undefined", "-", UC_NONE}, + /* 59 */ {"var59", "undefined", "-", UC_NONE}, + /* 60 */ {"MI14_95", "HIRS/2 ch 1 - 14.95 micron", "K", UC_NONE}, + /* 61 */ {"MI14_71", "HIRS/2, GOES 14.71 micron", "K", UC_NONE}, + /* 62 */ {"MI14_49", "HIRS/2 ch 3 - 14.49 micron", "K", UC_NONE}, + /* 63 */ {"MI14_37", "GOES I-M - 14.37 micron", "K", UC_NONE}, + /* 64 */ {"MI14_22", "HIRS/2 ch 4 - 14.22 micron", "K", UC_NONE}, + /* 65 */ {"MI14_06", "GOES I-M - 14.06 micron", "K", UC_NONE}, + /* 66 */ {"MI13_97", "HIRS/2 ch 5 - 13.97 micron", "K", UC_NONE}, + /* 67 */ {"MI13_64", "HIRS/2, GOES 13.64 micron", "K", UC_NONE}, + /* 68 */ {"MI13_37", "GOES I-M - 13.37 micron", "K", UC_NONE}, + /* 69 */ {"MI13_35", "HIRS/2 ch 7 - 13.35 micron", "K", UC_NONE}, + /* 70 */ {"MI12_66", "GOES I-M - 12.66 micron", "K", UC_NONE}, + /* 71 */ {"MI12_02", "GOES I-M - 12.02 micron", "K", UC_NONE}, + /* 72 */ {"MI12_00", "AVHRR ch 5 - 12.0 micron", "K", UC_NONE}, + /* 73 */ {"MI11_11", "HIRS/2 ch 8 - 11.11 micron", "K", UC_NONE}, + /* 74 */ {"MI11_03", "GOES I-M - 11.03 micron", "K", UC_NONE}, + /* 75 */ {"MI10_80", "AVHRR ch 4 - 10.8 micron", "K", UC_NONE}, + /* 76 */ {"MI9_71", "HIRS/2, GOES - 9.71 micron", "K", UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"MI8_16", "HIRS/2 ch 10 - 8.16 micron", "K", UC_NONE}, + /* 81 */ {"MI7_43", "GOES I-M - 7.43 micron", "K", UC_NONE}, + /* 82 */ {"MI7_33", "HIRS/2 ch 11 - 7.33 micron", "K", UC_NONE}, + /* 83 */ {"MI7_02", "GOES I-M - 7.02 micron", "K", UC_NONE}, + /* 84 */ {"MI6_72", "HIRS/2 ch 12 - 6.72 micron", "K", UC_NONE}, + /* 85 */ {"MI6_51", "GOES I-M - 6.51 micron", "K", UC_NONE}, + /* 86 */ {"MI4_57", "HIRS/2, GOES - 4.57 micron", "K", UC_NONE}, + /* 87 */ {"MI4_52", "HIRS/2, GOES - 4.52 micron", "K", UC_NONE}, + /* 88 */ {"MI4_46", "HIRS/2 ch 15 - 4.46 micron", "K", UC_NONE}, + /* 89 */ {"MI4_45", "GOES I-M - 4.45 micron", "K", UC_NONE}, + /* 90 */ {"MI4_40", "HIRS/2 ch 16 - 4.40 micron", "K", UC_NONE}, + /* 91 */ {"MI4_24", "HIRS/2 ch 17 - 4.24 micron", "K", UC_NONE}, + /* 92 */ {"MI4_13", "GOES I-M - 4.13 micron", "K", UC_NONE}, + /* 93 */ {"MI4_00", "HIRS/2 ch 18 - 4.00 micron", "K", UC_NONE}, + /* 94 */ {"MI8_16", "GOES I-M - 3.98 micron", "K", UC_NONE}, + /* 95 */ {"MI8_16", "HIRS/2 Window - 3.76 micron", "K", UC_NONE}, + /* 96 */ {"MI8_16", "AVHRR, GOES - 3.74 micron", "K", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"var99", "undefined", "-", UC_NONE}, + /* 100 */ {"MI0_91", "AVHRR ch 2 - 0.91 micron", "K", UC_NONE}, + /* 101 */ {"MI0_696", "GOES I-M - 0.696 micron", "K", UC_NONE}, + /* 102 */ {"MI0_69", "HIRS/2 Vis - 0.69 micron", "K", UC_NONE}, + /* 103 */ {"MI0_63", "AVHRR ch 1 - 0.63 micron", "K", UC_NONE}, + /* 104 */ {"var104", "undefined", "-", UC_NONE}, + /* 105 */ {"var105", "undefined", "-", UC_NONE}, + /* 106 */ {"var106", "undefined", "-", UC_NONE}, + /* 107 */ {"var107", "undefined", "-", UC_NONE}, + /* 108 */ {"var108", "undefined", "-", UC_NONE}, + /* 109 */ {"var109", "undefined", "-", UC_NONE}, + /* 110 */ {"var110", "undefined", "-", UC_NONE}, + /* 111 */ {"var111", "undefined", "-", UC_NONE}, + /* 112 */ {"var112", "undefined", "-", UC_NONE}, + /* 113 */ {"var113", "undefined", "-", UC_NONE}, + /* 114 */ {"var114", "undefined", "-", UC_NONE}, + /* 115 */ {"var115", "undefined", "-", UC_NONE}, + /* 116 */ {"var116", "undefined", "-", UC_NONE}, + /* 117 */ {"var117", "undefined", "-", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"var121", "undefined", "-", UC_NONE}, + /* 122 */ {"var122", "undefined", "-", UC_NONE}, + /* 123 */ {"var123", "undefined", "-", UC_NONE}, + /* 124 */ {"var124", "undefined", "-", UC_NONE}, + /* 125 */ {"var125", "undefined", "-", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"var127", "undefined", "-", UC_NONE}, + /* 128 */ {"AVDEPTH", "Ocean depth - mean", "m", UC_NONE}, + /* 129 */ {"DEPTH", "Ocean depth - instantaneous", "m", UC_NONE}, + /* 130 */ {"ELEV", "Ocean surface elevation relative to geoid", "m", + UC_NONE}, + /* 131 */ {"MXEL24", "Max ocean surface elevation in last 24 hours", "m", + UC_NONE}, + /* 132 */ {"MNEL24", "Min ocean surface elevation in last 24 hours", "m", + UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"O2", "Oxygen", "Mol/kg", UC_NONE}, + /* 136 */ {"PO4", "PO4", "Mol/kg", UC_NONE}, + /* 137 */ {"NO3", "NO3", "Mol/kg", UC_NONE}, + /* 138 */ {"SiO4", "SiO4", "Mol/kg", UC_NONE}, + /* 139 */ {"CO2aq", "CO2 (aq)", "Mol/kg", UC_NONE}, + /* 140 */ {"HCO3", "HCO3", "Mol/kg", UC_NONE}, + /* 141 */ {"CO3", "CO3", "Mol/kg", UC_NONE}, + /* 142 */ {"TCO2", "TCO2", "Mol/kg", UC_NONE}, + /* 143 */ {"TALK", "TALK", "Mol/kg", UC_NONE}, + /* 144 */ {"var144", "undefined", "-", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"S11", "S11 - 1,1 component of ice stress tensor", "-", + UC_NONE}, + /* 147 */ {"S12", "S12 - 1,2 component of ice stress tensor", "-", + UC_NONE}, + /* 148 */ {"S22", "S22 - 2,2 component of ice stress tensor", "-", + UC_NONE}, + /* 149 */ {"INV1", "T1 - First invariant of stress tensor", "-", UC_NONE}, + /* 150 */ {"INV2", "T2 - Second invariant of stress tensor", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"WVRGH", "Wave Roughness", "-", UC_NONE}, + /* 156 */ {"WVSTRS", "Wave Stresses", "-", UC_NONE}, + /* 157 */ {"WHITE", "Whitecap coverage", "-", UC_NONE}, + /* 158 */ {"SWDIRWID", "Swell direction width", "-", UC_NONE}, + /* 159 */ {"SWFREWID", "Swell frequency width", "-", UC_NONE}, + /* 160 */ {"WVAGE", "Wave age", "-", UC_NONE}, + /* 161 */ {"PWVAGE", "Physical Wave age", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"LTURB", "Master length scale (turbulence)", "m", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"AIHFLX", "Net Air-Ice heat flux", "W/m^2", UC_NONE}, + /* 171 */ {"AOHFLX", "Net Air-Ocean heat flux", "W/m^2", UC_NONE}, + /* 172 */ {"IOHFLX", "Net Ice-Ocean heat flux", "W/m^2", UC_NONE}, + /* 173 */ {"IOSFLX", "Net Ice-Ocean salt flux", "kg/s", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"OMLT", "Ocean Mixed Layer Temperature", "K", UC_NONE}, + /* 176 */ {"OMLS", "Ocean Mixed Layer Salinity", "kg/kg", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"REV", "Relative Error Variance", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE} +}; + +/***************************************************************************** + * ECMWF Parameter Tables (GRIB1 Section 1 Table 2) + * Table versions 128, 129, 130, 131, 140, 150, 160, 170, 180 + ***************************************************************************** + */ + +GRIB1ParmTable parm_table_ecmwf_128[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 2 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 3 */ {"POT", "Potential Temperature", "K", UC_K2F}, + /* 4 */ {"var4", "Equivalent Potential Temperature", "K", UC_K2F}, + /* 5 */ {"var5", "Saturated Equivalent Potential Temperature", "K", + UC_K2F}, + /* 6 */ {"var6", "undefined", "-", UC_NONE}, + /* 7 */ {"var7", "undefined", "-", UC_NONE}, + /* 8 */ {"var8", "undefined", "-", UC_NONE}, + /* 9 */ {"var9", "undefined", "-", UC_NONE}, + /* 10 */ {"var10", "undefined", "-", UC_NONE}, + /* 11 */ {"var11", "u-component of Divergent Wind", "m/s", UC_NONE}, + /* 12 */ {"var12", "v-component of Divergent Wind", "m/s", UC_NONE}, + /* 13 */ {"var13", "u-component of Rotational Wind", "m/s", UC_NONE}, + /* 14 */ {"var14", "v-component of Rotational Wind", "m/s", UC_NONE}, + /* 15 */ {"var15", "undefined", "-", UC_NONE}, + /* 16 */ {"var16", "undefined", "-", UC_NONE}, + /* 17 */ {"var17", "undefined", "-", UC_NONE}, + /* 18 */ {"var18", "undefined", "-", UC_NONE}, + /* 19 */ {"var19", "undefined", "-", UC_NONE}, + /* 20 */ {"var20", "undefined", "-", UC_NONE}, + /* 21 */ {"UCTP", "Unbalanced component of temperature", "K", UC_NONE}, + /* 22 */ {"UCLN", "Unbalanced component of lnsp", "-", UC_NONE}, + /* 23 */ {"UCDV", "Unbalanced component of divergence", "1/s", UC_NONE}, + /* 24 */ {"var24", "undefined", "-", UC_NONE}, + /* 25 */ {"var25", "undefined", "-", UC_NONE}, + /* 26 */ {"CL", "Lake cover (0-1)", "-", UC_NONE}, + /* 27 */ {"CVL", "Low vegetation cover (0-1)", "-", UC_NONE}, + /* 28 */ {"CVH", "High vegetation cover (0-1)", "-", UC_NONE}, + /* 29 */ {"TVL", "Type of low vegetation", "-", UC_NONE}, + /* 30 */ {"TVH", "Type of high vegetation", "-", UC_NONE}, + /* 31 */ {"CI", "Sea ice cover (0-1)", "-", UC_NONE}, + /* 32 */ {"ASN", "Snow albedo (0-1)", "-", UC_NONE}, + /* 33 */ {"RSN", "Snow density", "1/kg^3", UC_NONE}, + /* 34 */ {"SSTK", "Sea surface temperature (absolute)", "K", UC_NONE}, + /* 35 */ {"ISTL1", "Ice surface temperature layer 1", "K", UC_NONE}, + /* 36 */ {"ISTL2", "Ice surface temperature layer 2", "K", UC_NONE}, + /* 37 */ {"ISTL3", "Ice surface temperature layer 3", "K", UC_NONE}, + /* 38 */ {"ISTL4", "Ice surface temperature layer 4", "K", UC_NONE}, + /* 39 */ {"SWVL1", "Volumetric soil water layer 1", "m^3/m^3", UC_NONE}, + /* 40 */ {"SWVL2", "Volumetric soil water layer 2", "m^3/m^3", UC_NONE}, + /* 41 */ {"SWVL3", "Volumetric soil water layer 3", "m^3/m^3", UC_NONE}, + /* 42 */ {"SWVL4", "Volumetric soil water layer 4", "m^3/m^3", UC_NONE}, + /* 43 */ {"SLT", "Soil Type", "K", UC_NONE}, + /* 44 */ {"var44", "undefined", "-", UC_NONE}, + /* 45 */ {"var45", "undefined", "-", UC_NONE}, + /* 46 */ {"var46", "undefined", "-", UC_NONE}, + /* 47 */ {"var47", "undefined", "-", UC_NONE}, + /* 48 */ {"var48", "undefined", "-", UC_NONE}, + /* 49 */ {"var49", "undefined", "-", UC_NONE}, + /* 50 */ {"var50", "undefined", "-", UC_NONE}, + /* 51 */ {"var51", "undefined", "-", UC_NONE}, + /* 52 */ {"var52", "undefined", "-", UC_NONE}, + /* 53 */ {"var53", "undefined", "-", UC_NONE}, + /* 54 */ {"var54", "undefined", "-", UC_NONE}, + /* 55 */ {"var55", "undefined", "-", UC_NONE}, + /* 56 */ {"var56", "undefined", "-", UC_NONE}, + /* 57 */ {"var57", "undefined", "-", UC_NONE}, + /* 58 */ {"var58", "undefined", "-", UC_NONE}, + /* 59 */ {"var59", "undefined", "-", UC_NONE}, + /* 60 */ {"var60", "undefined", "-", UC_NONE}, + /* 61 */ {"var61", "undefined", "-", UC_NONE}, + /* 62 */ {"var62", "undefined", "-", UC_NONE}, + /* 63 */ {"var63", "undefined", "-", UC_NONE}, + /* 64 */ {"var64", "undefined", "-", UC_NONE}, + /* 65 */ {"var65", "undefined", "-", UC_NONE}, + /* 66 */ {"var66", "undefined", "-", UC_NONE}, + /* 67 */ {"var67", "undefined", "-", UC_NONE}, + /* 68 */ {"var68", "undefined", "-", UC_NONE}, + /* 69 */ {"var69", "undefined", "-", UC_NONE}, + /* 70 */ {"var70", "undefined", "-", UC_NONE}, + /* 71 */ {"var71", "undefined", "-", UC_NONE}, + /* 72 */ {"var72", "undefined", "-", UC_NONE}, + /* 73 */ {"var73", "undefined", "-", UC_NONE}, + /* 74 */ {"var74", "undefined", "-", UC_NONE}, + /* 75 */ {"var75", "undefined", "-", UC_NONE}, + /* 76 */ {"var76", "undefined", "-", UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"var80", "undefined", "-", UC_NONE}, + /* 81 */ {"var81", "undefined", "-", UC_NONE}, + /* 82 */ {"var82", "undefined", "-", UC_NONE}, + /* 83 */ {"var83", "undefined", "-", UC_NONE}, + /* 84 */ {"var84", "undefined", "-", UC_NONE}, + /* 85 */ {"var85", "undefined", "-", UC_NONE}, + /* 86 */ {"var86", "undefined", "-", UC_NONE}, + /* 87 */ {"var87", "undefined", "-", UC_NONE}, + /* 88 */ {"var88", "undefined", "-", UC_NONE}, + /* 89 */ {"var89", "undefined", "-", UC_NONE}, + /* 90 */ {"var90", "undefined", "-", UC_NONE}, + /* 91 */ {"var91", "undefined", "-", UC_NONE}, + /* 92 */ {"var92", "undefined", "-", UC_NONE}, + /* 93 */ {"var93", "undefined", "-", UC_NONE}, + /* 94 */ {"var94", "undefined", "-", UC_NONE}, + /* 95 */ {"var95", "undefined", "-", UC_NONE}, + /* 96 */ {"var96", "undefined", "-", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"var99", "undefined", "-", UC_NONE}, + /* 100 */ {"var100", "undefined", "-", UC_NONE}, + /* 101 */ {"var101", "undefined", "-", UC_NONE}, + /* 102 */ {"var102", "undefined", "-", UC_NONE}, + /* 103 */ {"var103", "undefined", "-", UC_NONE}, + /* 104 */ {"var104", "undefined", "-", UC_NONE}, + /* 105 */ {"var105", "undefined", "-", UC_NONE}, + /* 106 */ {"var106", "undefined", "-", UC_NONE}, + /* 107 */ {"var107", "undefined", "-", UC_NONE}, + /* 108 */ {"var108", "undefined", "-", UC_NONE}, + /* 109 */ {"var109", "undefined", "-", UC_NONE}, + /* 110 */ {"var110", "undefined", "-", UC_NONE}, + /* 111 */ {"var111", "undefined", "-", UC_NONE}, + /* 112 */ {"var112", "undefined", "-", UC_NONE}, + /* 113 */ {"var113", "undefined", "-", UC_NONE}, + /* 114 */ {"var114", "undefined", "-", UC_NONE}, + /* 115 */ {"var115", "undefined", "-", UC_NONE}, + /* 116 */ {"var116", "undefined", "-", UC_NONE}, + /* 117 */ {"var117", "undefined", "-", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"var121", "undefined", "-", UC_NONE}, + /* 122 */ {"var122", "undefined", "-", UC_NONE}, + /* 123 */ {"var123", "undefined", "-", UC_NONE}, + /* 124 */ {"var124", "undefined", "-", UC_NONE}, + /* 125 */ {"var125", "undefined", "-", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"AT", "Atmospheric tide", "-", UC_NONE}, + /* 128 */ {"BV", "Budget values", "-", UC_NONE}, + /* 129 */ {"Z", "Geopotential (at the surface = orography)", "m^2/s^2", + UC_NONE}, + /* 130 */ {"T", "Temperature", "K", UC_K2F}, + /* 131 */ {"U", "U-velocity", "m/s", UC_NONE}, + /* 132 */ {"V", "V-velocity", "m/s", UC_NONE}, + /* 133 */ {"Q", "Specific humidity", "kg/kg", UC_NONE}, + /* 134 */ {"SP", "Surface pressure", "Pa", UC_NONE}, + /* 135 */ {"W", "Vertical velocity", "Pa/s", UC_NONE}, + /* 136 */ {"TWC", "Total column water", "kg/m^2", UC_NONE}, + /* 137 */ {"PWC", "Precipitable water content", "kg/m^2", UC_NONE}, + /* 138 */ {"VO", "Vorticity (relative)", "1/s", UC_NONE}, + /* 139 */ {"ST", "Surf.temp/soil temp lev 1 (from 930804)", "K", UC_NONE}, + /* 140 */ {"SSW", "Surf soil wet/soil wet lev1(from 930803) (of water)", + "m", UC_NONE}, + /* 141 */ {"SD", "Snow depth (water equivalent)", "m", UC_NONE}, + /* 142 */ {"LSP", "Large scale precipitation", "m", UC_NONE}, + /* 143 */ {"CP", "Convective precipitation", "m", UC_NONE}, + /* 144 */ {"SF", "Snow fall (of water equivalent)", "m", UC_NONE}, + /* 145 */ {"BLD", "Boundary layer dissipation", "W*s/m^2", UC_NONE}, + /* 146 */ {"SSHF", "Surface sensible heat flux", "W*s/m^2", UC_NONE}, + /* 147 */ {"SLHF", "Surface latent heat flux", "W*s/m^2", UC_NONE}, + /* 148 */ {"SS-CHNK", "Surface stress/Charnock (from 980519)", "-", + UC_NONE}, + /* 149 */ {"SNR", "Surface net radiation", "-", UC_NONE}, + /* 150 */ {"AIW", "Top net radiation", "-", UC_NONE}, + /* 151 */ {"MSL", "Mean sea level pressure", "Pa", UC_NONE}, + /* 152 */ {"LNSP", "Log surface pressure", "-", UC_NONE}, + /* 153 */ {"SWHR", "Short wave heating rate", "K", UC_NONE}, + /* 154 */ {"LWH", "Long wave heating rate", "K", UC_NONE}, + /* 155 */ {"D", "Divergence", "1/s", UC_NONE}, + /* 156 */ {"GH", "Height (geopotential)", "m", UC_NONE}, + /* 157 */ {"R", "Relative humidity", "%", UC_NONE}, + /* 158 */ {"TSP", "Tendency of surface pressure", "Pa/s", UC_NONE}, + /* 159 */ {"BLH", "Boundary layer height", "m", UC_NONE}, + /* 160 */ {"SDOR", "Standard deviation of orography", "-", UC_NONE}, + /* 161 */ {"ISOR", "Anisotropy of subgrid scale orography", "-", UC_NONE}, + /* 162 */ {"ANOR", "Angle of subgrid scale orography", "-", UC_NONE}, + /* 163 */ {"SLOR", "Slope of subgrid scale orography", "-", UC_NONE}, + /* 164 */ {"TCC", "Total cloud cover (0 - 1)", "-", UC_NONE}, + /* 165 */ {"10U", "10 metre u wind component", "m/s", UC_NONE}, + /* 166 */ {"10V", "10 metre v wind component", "m/s", UC_NONE}, + /* 167 */ {"2T", "2 metre temperature", "K", UC_NONE}, + /* 168 */ {"2D", "2 metre dewpoint temperature", "K", UC_NONE}, + /* 169 */ {"SSRD", "Surface solar radiation downwards", "W*s/m^2", + UC_NONE}, + /* 170 */ {"DST", "Deep soil tmp/soil temp lev2(from 930804)", "K", + UC_NONE}, + /* 171 */ {"DSW", "Deep soil wet/soil wet lev2(from 930803) (of water)", + "m", UC_NONE}, + /* 172 */ {"LSM", "Land/sea mask", "-", UC_NONE}, + /* 173 */ {"SR", "Surface roughness", "m", UC_NONE}, + /* 174 */ {"AL", "Albedo", "-", UC_NONE}, + /* 175 */ {"STRD", "Surface thermal radiation downwards", "W*s/m^2", + UC_NONE}, + /* 176 */ {"SSR", "Surface solar radiation", "W*s/m^2", UC_NONE}, + /* 177 */ {"STR", "Surface thermal radiation", "W*s/m^2", UC_NONE}, + /* 178 */ {"TSR", "Top solar radiation", "W*s/m^2", UC_NONE}, + /* 179 */ {"TTR", "Top thermal radiation", "W*s/m^2", UC_NONE}, + /* 180 */ {"EWSS", "East/West surface stress", "N*s/m^2", UC_NONE}, + /* 181 */ {"NSSS", "North/South surface stress", "N*s/m^2", UC_NONE}, + /* 182 */ {"E", "Evaporation (of water)", "m", UC_NONE}, + /* 183 */ {"CDST", "Clim deep soil tmp/soil tmp lev3(930804)", "K", + UC_NONE}, + /* 184 */ {"CDSW", "Clim deep soil wet/soil wet lev3(930803) (of water)", + "m", UC_NONE}, + /* 185 */ {"CCC", "Convective cloud cover (0 - 1)", "-", UC_NONE}, + /* 186 */ {"LCC", "Low cloud cover (0 - 1)", "-", UC_NONE}, + /* 187 */ {"MCC", "Medium cloud cover (0 - 1)", "-", UC_NONE}, + /* 188 */ {"HCC", "High cloud cover (0 - 1)", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"EWOV", "EW component subgrid orographic variance", "m^2", + UC_NONE}, + /* 191 */ {"NSOV", "NS component subgrid orographic variance", "m^2", + UC_NONE}, + /* 192 */ {"NWOV", "NWSE component subgrid orographic variance", "m^2", + UC_NONE}, + /* 193 */ {"NEOV", "NESW component subgrid orographic variance", "m^2", + UC_NONE}, + /* 194 */ {"BTMP", "Brightness temperature", "K", UC_NONE}, + /* 195 */ {"LGWS", "Lat. component of gravity wave stress", "N*s/m^2", + UC_NONE}, + /* 196 */ {"MGWS", "Meridional component gravity wave stress", "N*s/m^2", + UC_NONE}, + /* 197 */ {"GWD", "Gravity wave dissipation", "W*s/m^2", UC_NONE}, + /* 198 */ {"SRC", "Skin reservoir content (of water)", "m", UC_NONE}, + /* 199 */ {"VEG", "Percentage of vegetation", "%", UC_NONE}, + /* 200 */ {"VSO", "Variance of sub-grid scale orography", "m^2", UC_NONE}, + /* 201 */ {"MX2T", "Max 2m temp since previous post-processing", "K", + UC_NONE}, + /* 202 */ {"MN2T", "Min 2m temp since previous post-processing", "K", + UC_NONE}, + /* 203 */ {"O3", "Ozone mass mixing ratio", "kg/kg", UC_NONE}, + /* 204 */ {"PAW", "Precip. analysis weights", "-", UC_NONE}, + /* 205 */ {"RO", "Runoff", "m", UC_NONE}, + /* 206 */ {"TCO3", "Total column ozone Dobson", "kg/m^2", UC_NONE}, + /* 207 */ {"10SI", "10m. Windspeed (irresp of dir.)", "m/s", UC_NONE}, + /* 208 */ {"TSRC", "Top net solar radiation clear sky", "W/m^2", UC_NONE}, + /* 209 */ {"TTRC", "Top upward thermal radiation clear sky", "W/m^2", + UC_NONE}, + /* 210 */ {"SSRC", "Surface net solar radiation clear sky", "W/m^2", + UC_NONE}, + /* 211 */ {"STRC", "Surface net thermal radiation clear sky", "W/m^2", + UC_NONE}, + /* 212 */ {"SI", "Solar insulation", "W/m^2", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"DHR", "Diabatic heating by radiation", "K", UC_NONE}, + /* 215 */ {"DHVD", "Diabatic heating by vertical diffusion", "K", UC_NONE}, + /* 216 */ {"DHCC", "Diabatic heating by cumulus convection", "K", UC_NONE}, + /* 217 */ {"DHLC", "Diabatic heating large-scale condensation", "K", + UC_NONE}, + /* 218 */ {"VDZW", "Vertical diffusion of zonal wind", "m/s", UC_NONE}, + /* 219 */ {"VDMW", "Vertical diffusion of meridional wind", "m/s", + UC_NONE}, + /* 220 */ {"EWGD", "EW gravity wave drag", "m/s", UC_NONE}, + /* 221 */ {"NSGD", "NS gravity wave drag", "m/s", UC_NONE}, + /* 222 */ {"CTZW", "Convective tendency of zonal wind", "m/s", UC_NONE}, + /* 223 */ {"CTMW", "Convective tendency of meridional wind", "m/s", + UC_NONE}, + /* 224 */ {"VDH", "Vertical diffusion of humidity", "kg/kg", UC_NONE}, + /* 225 */ {"HTCC", "Humidity tendency by cumulus convection", "kg/kg", + UC_NONE}, + /* 226 */ {"HTLC", "Humidity tendency large-scale condensation", "kg/kg", + UC_NONE}, + /* 227 */ {"CRNH", "Change from removing negative humidity", "kg/kg s-1", + UC_NONE}, + /* 228 */ {"TP", "Total precipitation", "m", UC_NONE}, + /* 229 */ {"IEWS", "Instantaneous X surface stress", "N/m^2", UC_NONE}, + /* 230 */ {"INSS", "Instantaneous Y surface stress", "N/m^2", UC_NONE}, + /* 231 */ {"ISHF", "Instantaneous surface Heat Flux", "W/m^2", UC_NONE}, + /* 232 */ {"IE", "Instantaneous Moisture Flux (evaporation)", "kg*s/m^2", + UC_NONE}, + /* 233 */ {"ASQ", "Apparent Surface Humidity", "kg/kg", UC_NONE}, + /* 234 */ {"LSRH", "Log of surface roughness length for heat", "-", + UC_NONE}, + /* 235 */ {"SKT", "Skin Temperature", "K", UC_NONE}, + /* 236 */ {"STL4", "Soil temperature level 4", "K", UC_NONE}, + /* 237 */ {"SWL4", "Soil wetness level 4", "m", UC_NONE}, + /* 238 */ {"TSN", "Temperature of snow layer", "K", UC_NONE}, + /* 239 */ {"CSF", "Convective snow-fall (of water equivalent)", "m", + UC_NONE}, + /* 240 */ {"LSF", "Large scale snow-fall (of water equivalent)", "m", + UC_NONE}, + /* 241 */ {"ACF", "Accumulated cloud fraction tendency (-1 to 1)", "-", + UC_NONE}, + /* 242 */ {"ALW", "Accumulated liquid water tendency (-1 to 1)", "-", + UC_NONE}, + /* 243 */ {"FAL", "Forecast albedo", "-", UC_NONE}, + /* 244 */ {"FSR", "Forecast surface roughness", "m", UC_NONE}, + /* 245 */ {"FLSR", "Forecast log of surface roughness for heat", "-", + UC_NONE}, + /* 246 */ {"CLWC", "Cloud liquid water content", "kg/kg", UC_NONE}, + /* 247 */ {"CIWC", "Cloud ice water content", "kg/kg", UC_NONE}, + /* 248 */ {"CC", "Cloud cover (0 - 1)", "-", UC_NONE}, + /* 249 */ {"AIW", "Accumulated ice water tendency (-1 to 1)", "-", + UC_NONE}, + /* 250 */ {"ICE", "Ice Age (0 first-year 1 multi-year)", "-", UC_NONE}, + /* 251 */ {"ATTE", "Adiabatic tendency of temperature", "K", UC_NONE}, + /* 252 */ {"ATHE", "Adiabatic tendency of humidity", "kg/kg", UC_NONE}, + /* 253 */ {"ATZE", "Adiabatic tendency of zonal wind", "m/s", UC_NONE}, + /* 254 */ {"ATMW", "Adiabatic tendency of meridional wind", "m/s", + UC_NONE}, + /* 255 */ {"-", "Indicates a missing value", "-", UC_NONE}, +}; + +GRIB1ParmTable parm_table_ecmwf_129[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"var1", "undefined", "-", UC_NONE}, + /* 2 */ {"var2", "undefined", "-", UC_NONE}, + /* 3 */ {"var3", "undefined", "-", UC_NONE}, + /* 4 */ {"var4", "undefined", "-", UC_NONE}, + /* 5 */ {"var5", "undefined", "-", UC_NONE}, + /* 6 */ {"var6", "undefined", "-", UC_NONE}, + /* 7 */ {"var7", "undefined", "-", UC_NONE}, + /* 8 */ {"var8", "undefined", "-", UC_NONE}, + /* 9 */ {"var9", "undefined", "-", UC_NONE}, + /* 10 */ {"var10", "undefined", "-", UC_NONE}, + /* 11 */ {"var11", "undefined", "-", UC_NONE}, + /* 12 */ {"var12", "undefined", "-", UC_NONE}, + /* 13 */ {"var13", "undefined", "-", UC_NONE}, + /* 14 */ {"var14", "undefined", "-", UC_NONE}, + /* 15 */ {"var15", "undefined", "-", UC_NONE}, + /* 16 */ {"var16", "undefined", "-", UC_NONE}, + /* 17 */ {"var17", "undefined", "-", UC_NONE}, + /* 18 */ {"var18", "undefined", "-", UC_NONE}, + /* 19 */ {"var19", "undefined", "-", UC_NONE}, + /* 20 */ {"var20", "undefined", "-", UC_NONE}, + /* 21 */ {"var21", "undefined", "-", UC_NONE}, + /* 22 */ {"var22", "undefined", "-", UC_NONE}, + /* 23 */ {"var23", "undefined", "-", UC_NONE}, + /* 24 */ {"var24", "undefined", "-", UC_NONE}, + /* 25 */ {"var25", "undefined", "-", UC_NONE}, + /* 26 */ {"var26", "undefined", "-", UC_NONE}, + /* 27 */ {"var27", "undefined", "-", UC_NONE}, + /* 28 */ {"var28", "undefined", "-", UC_NONE}, + /* 29 */ {"var29", "undefined", "-", UC_NONE}, + /* 30 */ {"var30", "undefined", "-", UC_NONE}, + /* 31 */ {"var31", "undefined", "-", UC_NONE}, + /* 32 */ {"var32", "undefined", "-", UC_NONE}, + /* 33 */ {"var33", "undefined", "-", UC_NONE}, + /* 34 */ {"var34", "undefined", "-", UC_NONE}, + /* 35 */ {"var35", "undefined", "-", UC_NONE}, + /* 36 */ {"var36", "undefined", "-", UC_NONE}, + /* 37 */ {"var37", "undefined", "-", UC_NONE}, + /* 38 */ {"var38", "undefined", "-", UC_NONE}, + /* 39 */ {"var39", "undefined", "-", UC_NONE}, + /* 40 */ {"var40", "undefined", "-", UC_NONE}, + /* 41 */ {"var41", "undefined", "-", UC_NONE}, + /* 42 */ {"var42", "undefined", "-", UC_NONE}, + /* 43 */ {"var43", "undefined", "-", UC_NONE}, + /* 44 */ {"var44", "undefined", "-", UC_NONE}, + /* 45 */ {"var45", "undefined", "-", UC_NONE}, + /* 46 */ {"var46", "undefined", "-", UC_NONE}, + /* 47 */ {"var47", "undefined", "-", UC_NONE}, + /* 48 */ {"var48", "undefined", "-", UC_NONE}, + /* 49 */ {"var49", "undefined", "-", UC_NONE}, + /* 50 */ {"var50", "undefined", "-", UC_NONE}, + /* 51 */ {"var51", "undefined", "-", UC_NONE}, + /* 52 */ {"var52", "undefined", "-", UC_NONE}, + /* 53 */ {"var53", "undefined", "-", UC_NONE}, + /* 54 */ {"var54", "undefined", "-", UC_NONE}, + /* 55 */ {"var55", "undefined", "-", UC_NONE}, + /* 56 */ {"var56", "undefined", "-", UC_NONE}, + /* 57 */ {"var57", "undefined", "-", UC_NONE}, + /* 58 */ {"var58", "undefined", "-", UC_NONE}, + /* 59 */ {"var59", "undefined", "-", UC_NONE}, + /* 60 */ {"var60", "undefined", "-", UC_NONE}, + /* 61 */ {"var61", "undefined", "-", UC_NONE}, + /* 62 */ {"var62", "undefined", "-", UC_NONE}, + /* 63 */ {"var63", "undefined", "-", UC_NONE}, + /* 64 */ {"var64", "undefined", "-", UC_NONE}, + /* 65 */ {"var65", "undefined", "-", UC_NONE}, + /* 66 */ {"var66", "undefined", "-", UC_NONE}, + /* 67 */ {"var67", "undefined", "-", UC_NONE}, + /* 68 */ {"var68", "undefined", "-", UC_NONE}, + /* 69 */ {"var69", "undefined", "-", UC_NONE}, + /* 70 */ {"var70", "undefined", "-", UC_NONE}, + /* 71 */ {"var71", "undefined", "-", UC_NONE}, + /* 72 */ {"var72", "undefined", "-", UC_NONE}, + /* 73 */ {"var73", "undefined", "-", UC_NONE}, + /* 74 */ {"var74", "undefined", "-", UC_NONE}, + /* 75 */ {"var75", "undefined", "-", UC_NONE}, + /* 76 */ {"var76", "undefined", "-", UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"var80", "undefined", "-", UC_NONE}, + /* 81 */ {"var81", "undefined", "-", UC_NONE}, + /* 82 */ {"var82", "undefined", "-", UC_NONE}, + /* 83 */ {"var83", "undefined", "-", UC_NONE}, + /* 84 */ {"var84", "undefined", "-", UC_NONE}, + /* 85 */ {"var85", "undefined", "-", UC_NONE}, + /* 86 */ {"var86", "undefined", "-", UC_NONE}, + /* 87 */ {"var87", "undefined", "-", UC_NONE}, + /* 88 */ {"var88", "undefined", "-", UC_NONE}, + /* 89 */ {"var89", "undefined", "-", UC_NONE}, + /* 90 */ {"var90", "undefined", "-", UC_NONE}, + /* 91 */ {"var91", "undefined", "-", UC_NONE}, + /* 92 */ {"var92", "undefined", "-", UC_NONE}, + /* 93 */ {"var93", "undefined", "-", UC_NONE}, + /* 94 */ {"var94", "undefined", "-", UC_NONE}, + /* 95 */ {"var95", "undefined", "-", UC_NONE}, + /* 96 */ {"var96", "undefined", "-", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"var99", "undefined", "-", UC_NONE}, + /* 100 */ {"var100", "undefined", "-", UC_NONE}, + /* 101 */ {"var101", "undefined", "-", UC_NONE}, + /* 102 */ {"var102", "undefined", "-", UC_NONE}, + /* 103 */ {"var103", "undefined", "-", UC_NONE}, + /* 104 */ {"var104", "undefined", "-", UC_NONE}, + /* 105 */ {"var105", "undefined", "-", UC_NONE}, + /* 106 */ {"var106", "undefined", "-", UC_NONE}, + /* 107 */ {"var107", "undefined", "-", UC_NONE}, + /* 108 */ {"var108", "undefined", "-", UC_NONE}, + /* 109 */ {"var109", "undefined", "-", UC_NONE}, + /* 110 */ {"var110", "undefined", "-", UC_NONE}, + /* 111 */ {"var111", "undefined", "-", UC_NONE}, + /* 112 */ {"var112", "undefined", "-", UC_NONE}, + /* 113 */ {"var113", "undefined", "-", UC_NONE}, + /* 114 */ {"var114", "undefined", "-", UC_NONE}, + /* 115 */ {"var115", "undefined", "-", UC_NONE}, + /* 116 */ {"var116", "undefined", "-", UC_NONE}, + /* 117 */ {"var117", "undefined", "-", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"var121", "undefined", "-", UC_NONE}, + /* 122 */ {"var122", "undefined", "-", UC_NONE}, + /* 123 */ {"var123", "undefined", "-", UC_NONE}, + /* 124 */ {"var124", "undefined", "-", UC_NONE}, + /* 125 */ {"var125", "undefined", "-", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"AT", "Atmospheric tide+", "-", UC_NONE}, + /* 128 */ {"BV", "Budget values+", "-", UC_NONE}, + /* 129 */ {"Z", "Geopotential (at the surface=orography)", "m^2/s^2", + UC_NONE}, + /* 130 */ {"T", "Temperature", "K", UC_NONE}, + /* 131 */ {"U", "U-velocity", "m/s", UC_NONE}, + /* 132 */ {"V", "V-velocity", "m/s", UC_NONE}, + /* 133 */ {"Q", "Specific humidity", "kg/kg", UC_NONE}, + /* 134 */ {"SP", "Surface pressure", "Pa", UC_NONE}, + /* 135 */ {"W", "Vertical velocity", "Pa/s", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"PWC", "Precipitable water content", "kg/m^2", UC_NONE}, + /* 138 */ {"VO", "Vorticity (relative)", "1/s", UC_NONE}, + /* 139 */ {"ST", "Surf.temp/soil temp lev 1 (from 930804)", "K", UC_NONE}, + /* 140 */ {"SSW", "Surf soil wet/soil wet lev1(from 930803) (of water)", + "m", UC_NONE}, + /* 141 */ {"SD", "Snow depth (of water equivalent)", "m", UC_NONE}, + /* 142 */ {"LSP", "Large scale precipitation*", "m", UC_NONE}, + /* 143 */ {"CP", "Convective precipitation*", "m", UC_NONE}, + /* 144 */ {"SF", "Snow fall* (of water equivalent)", "m", UC_NONE}, + /* 145 */ {"BLD", "Boundary layer dissipation*", "W*s/m^2", UC_NONE}, + /* 146 */ {"SSHF", "Surface sensible heat flux*", "W*s/m^2", UC_NONE}, + /* 147 */ {"SLHF", "Surface latent heat flux*", "W*s/m^2", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"MSL", "Mean sea level pressure", "Pa", UC_NONE}, + /* 152 */ {"LNSP", "Log surface pressure", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"D", "Divergence", "1/s", UC_NONE}, + /* 156 */ {"GH", "Height (geopotential)", "m", UC_NONE}, + /* 157 */ {"R", "Relative humidity", "%", UC_NONE}, + /* 158 */ {"TSP", "Tendency of surface pressure", "Pa/s", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"SDOR", "Standard deviation of orography", "-", UC_NONE}, + /* 161 */ {"ISOR", "Anisotropy of subgrid scale orography", "-", UC_NONE}, + /* 162 */ {"ANOR", "Angle of subgrid scale orography", "-", UC_NONE}, + /* 163 */ {"SLOR", "Slope of subgrid scale orography", "-", UC_NONE}, + /* 164 */ {"TCC", "Total cloud cover (0 - 1)", "-", UC_NONE}, + /* 165 */ {"10U", "10 metre u wind component", "m/s", UC_NONE}, + /* 166 */ {"10V", "10 metre v wind component", "m/s", UC_NONE}, + /* 167 */ {"2T", "2 metre temperature", "K", UC_NONE}, + /* 168 */ {"2D", "2 metre dewpoint temperature", "K", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"DST", "Deep soil tmp/soil temp lev2(frm 930804)", "K", + UC_NONE}, + /* 171 */ {"DSW", "Deep soil wet/soil wet lev2(from 930803) (of water)", + "m", UC_NONE}, + /* 172 */ {"LSM", "Land/sea mask (0", "-", UC_NONE}, + /* 173 */ {"SR", "Surface roughness", "m", UC_NONE}, + /* 174 */ {"AL", "Albedo", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"SSR", "Surface solar radiation", "W*s/m^2", UC_NONE}, + /* 177 */ {"STR", "Surface thermal radiation", "W*s/m^2", UC_NONE}, + /* 178 */ {"TSR", "Top solar radiation", "W*s/m^2", UC_NONE}, + /* 179 */ {"TTR", "Top thermal radiation", "W*s/m^2", UC_NONE}, + /* 180 */ {"EWSS", "East/West surface stress", "N*s/m^2", UC_NONE}, + /* 181 */ {"NSSS", "North/South surface stress", "N*s/m^2", UC_NONE}, + /* 182 */ {"E", "Evaporation (of water)", "m", UC_NONE}, + /* 183 */ {"CDST", "Clim deep soil tmp/soil tmp lev3(930804)", "K", + UC_NONE}, + /* 184 */ {"CDSW", "Clim deep soil wet/soil wet lev3(930803) (of water)", + "m", UC_NONE}, + /* 185 */ {"CCC", "Convective cloud cover (0 - 1)", "-", UC_NONE}, + /* 186 */ {"LCC", "Low cloud cover (0 - 1)", "-", UC_NONE}, + /* 187 */ {"MCC", "Medium cloud cover (0 - 1)", "-", UC_NONE}, + /* 188 */ {"HCC", "High cloud cover (0 - 1)", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"EWOV", "EW component subgrid scale orographic variance", "m^2", + UC_NONE}, + /* 191 */ {"NSOV", "NS component subgrid scale orographic variance", "m^2", + UC_NONE}, + /* 192 */ {"NWOV", "NWSE component subgrid scale orographic variance", + "m^2", UC_NONE}, + /* 193 */ {"NEOV", "NESW component subgrid scale orographic variance", + "m^2", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"LGWS", "Lat. component of gravity wave stress", "N*s/m^2", + UC_NONE}, + /* 196 */ {"MGWS", "Meridional component gravity wave stress", "N*s/m^2", + UC_NONE}, + /* 197 */ {"GWD", "Gravity wave dissipation", "W*s/m^2", UC_NONE}, + /* 198 */ {"SRC", "Skin reservoir content (of water)", "m", UC_NONE}, + /* 199 */ {"VEG", "Percentage of vegetation", "%", UC_NONE}, + /* 200 */ {"VSO", "Variance of sub-grid scale orography", "m^2", UC_NONE}, + /* 201 */ {"MX2T", "Max 2m temp since previous post-processing", "K", + UC_NONE}, + /* 202 */ {"MN2T", "Min 2m temp since previous post-processing", "K", + UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"PAW", "Precip. analysis weights", "-", UC_NONE}, + /* 205 */ {"RO", "Runoff", "m", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"TP", "Total precipitation", "m", UC_NONE}, + /* 229 */ {"IEWS", "Instantaneous X surface stress", "N/m^2", UC_NONE}, + /* 230 */ {"INSS", "Instantaneous Y surface stress", "N/m^2", UC_NONE}, + /* 231 */ {"ISHF", "Instantaneous surface Heat Flux", "W/m^2", UC_NONE}, + /* 232 */ {"IE", "Instantaneous Moisture Flux (evaporation)", "kg*s/m^2", + UC_NONE}, + /* 233 */ {"ASQ", "Apparent Surface Humidity", "kg/kg", UC_NONE}, + /* 234 */ {"LSRH", "Log of surface roughness length for heat", "-", + UC_NONE}, + /* 235 */ {"SKT", "Skin Temperature", "K", UC_NONE}, + /* 236 */ {"STL4", "Soil temperature level 4", "K", UC_NONE}, + /* 237 */ {"SWL4", "Soil wetness level 4", "m", UC_NONE}, + /* 238 */ {"TSN", "Temperature of snow layer", "K", UC_NONE}, + /* 239 */ {"CSF", "Convective snow-fall (of water equivalent)", "m", + UC_NONE}, + /* 240 */ {"LSF", "Large scale snow-fall (of water equivalent)", "m", + UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"FAL", "Forecast albedo", "-", UC_NONE}, + /* 244 */ {"FSR", "Forecast surface roughness", "m", UC_NONE}, + /* 245 */ {"FLSR", "Forecast log of surface roughness for heat", "-", + UC_NONE}, + /* 246 */ {"CLWC", "Cloud liquid water content", "kg/kg", UC_NONE}, + /* 247 */ {"CIWC", "Cloud ice water content", "kg/kg", UC_NONE}, + /* 248 */ {"CC", "Cloud cover (0 - 1)", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"ICE", "Ice Age (0 first-year 1 multi-year)", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +GRIB1ParmTable parm_table_ecmwf_130[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"var1", "undefined", "-", UC_NONE}, + /* 2 */ {"var2", "undefined", "-", UC_NONE}, + /* 3 */ {"var3", "undefined", "-", UC_NONE}, + /* 4 */ {"var4", "undefined", "-", UC_NONE}, + /* 5 */ {"var5", "undefined", "-", UC_NONE}, + /* 6 */ {"var6", "undefined", "-", UC_NONE}, + /* 7 */ {"var7", "undefined", "-", UC_NONE}, + /* 8 */ {"var8", "undefined", "-", UC_NONE}, + /* 9 */ {"var9", "undefined", "-", UC_NONE}, + /* 10 */ {"var10", "undefined", "-", UC_NONE}, + /* 11 */ {"var11", "undefined", "-", UC_NONE}, + /* 12 */ {"var12", "undefined", "-", UC_NONE}, + /* 13 */ {"var13", "undefined", "-", UC_NONE}, + /* 14 */ {"var14", "undefined", "-", UC_NONE}, + /* 15 */ {"var15", "undefined", "-", UC_NONE}, + /* 16 */ {"var16", "undefined", "-", UC_NONE}, + /* 17 */ {"var17", "undefined", "-", UC_NONE}, + /* 18 */ {"var18", "undefined", "-", UC_NONE}, + /* 19 */ {"var19", "undefined", "-", UC_NONE}, + /* 20 */ {"var20", "undefined", "-", UC_NONE}, + /* 21 */ {"var21", "undefined", "-", UC_NONE}, + /* 22 */ {"var22", "undefined", "-", UC_NONE}, + /* 23 */ {"var23", "undefined", "-", UC_NONE}, + /* 24 */ {"var24", "undefined", "-", UC_NONE}, + /* 25 */ {"var25", "undefined", "-", UC_NONE}, + /* 26 */ {"var26", "undefined", "-", UC_NONE}, + /* 27 */ {"var27", "undefined", "-", UC_NONE}, + /* 28 */ {"var28", "undefined", "-", UC_NONE}, + /* 29 */ {"var29", "undefined", "-", UC_NONE}, + /* 30 */ {"var30", "undefined", "-", UC_NONE}, + /* 31 */ {"var31", "undefined", "-", UC_NONE}, + /* 32 */ {"var32", "undefined", "-", UC_NONE}, + /* 33 */ {"var33", "undefined", "-", UC_NONE}, + /* 34 */ {"var34", "undefined", "-", UC_NONE}, + /* 35 */ {"var35", "undefined", "-", UC_NONE}, + /* 36 */ {"var36", "undefined", "-", UC_NONE}, + /* 37 */ {"var37", "undefined", "-", UC_NONE}, + /* 38 */ {"var38", "undefined", "-", UC_NONE}, + /* 39 */ {"var39", "undefined", "-", UC_NONE}, + /* 40 */ {"var40", "undefined", "-", UC_NONE}, + /* 41 */ {"var41", "undefined", "-", UC_NONE}, + /* 42 */ {"var42", "undefined", "-", UC_NONE}, + /* 43 */ {"var43", "undefined", "-", UC_NONE}, + /* 44 */ {"var44", "undefined", "-", UC_NONE}, + /* 45 */ {"var45", "undefined", "-", UC_NONE}, + /* 46 */ {"var46", "undefined", "-", UC_NONE}, + /* 47 */ {"var47", "undefined", "-", UC_NONE}, + /* 48 */ {"var48", "undefined", "-", UC_NONE}, + /* 49 */ {"var49", "undefined", "-", UC_NONE}, + /* 50 */ {"var50", "undefined", "-", UC_NONE}, + /* 51 */ {"var51", "undefined", "-", UC_NONE}, + /* 52 */ {"var52", "undefined", "-", UC_NONE}, + /* 53 */ {"var53", "undefined", "-", UC_NONE}, + /* 54 */ {"var54", "undefined", "-", UC_NONE}, + /* 55 */ {"var55", "undefined", "-", UC_NONE}, + /* 56 */ {"var56", "undefined", "-", UC_NONE}, + /* 57 */ {"var57", "undefined", "-", UC_NONE}, + /* 58 */ {"var58", "undefined", "-", UC_NONE}, + /* 59 */ {"var59", "undefined", "-", UC_NONE}, + /* 60 */ {"var60", "undefined", "-", UC_NONE}, + /* 61 */ {"var61", "undefined", "-", UC_NONE}, + /* 62 */ {"var62", "undefined", "-", UC_NONE}, + /* 63 */ {"var63", "undefined", "-", UC_NONE}, + /* 64 */ {"var64", "undefined", "-", UC_NONE}, + /* 65 */ {"var65", "undefined", "-", UC_NONE}, + /* 66 */ {"var66", "undefined", "-", UC_NONE}, + /* 67 */ {"var67", "undefined", "-", UC_NONE}, + /* 68 */ {"var68", "undefined", "-", UC_NONE}, + /* 69 */ {"var69", "undefined", "-", UC_NONE}, + /* 70 */ {"var70", "undefined", "-", UC_NONE}, + /* 71 */ {"var71", "undefined", "-", UC_NONE}, + /* 72 */ {"var72", "undefined", "-", UC_NONE}, + /* 73 */ {"var73", "undefined", "-", UC_NONE}, + /* 74 */ {"var74", "undefined", "-", UC_NONE}, + /* 75 */ {"var75", "undefined", "-", UC_NONE}, + /* 76 */ {"var76", "undefined", "-", UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"var80", "undefined", "-", UC_NONE}, + /* 81 */ {"var81", "undefined", "-", UC_NONE}, + /* 82 */ {"var82", "undefined", "-", UC_NONE}, + /* 83 */ {"var83", "undefined", "-", UC_NONE}, + /* 84 */ {"var84", "undefined", "-", UC_NONE}, + /* 85 */ {"var85", "undefined", "-", UC_NONE}, + /* 86 */ {"var86", "undefined", "-", UC_NONE}, + /* 87 */ {"var87", "undefined", "-", UC_NONE}, + /* 88 */ {"var88", "undefined", "-", UC_NONE}, + /* 89 */ {"var89", "undefined", "-", UC_NONE}, + /* 90 */ {"var90", "undefined", "-", UC_NONE}, + /* 91 */ {"var91", "undefined", "-", UC_NONE}, + /* 92 */ {"var92", "undefined", "-", UC_NONE}, + /* 93 */ {"var93", "undefined", "-", UC_NONE}, + /* 94 */ {"var94", "undefined", "-", UC_NONE}, + /* 95 */ {"var95", "undefined", "-", UC_NONE}, + /* 96 */ {"var96", "undefined", "-", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"var99", "undefined", "-", UC_NONE}, + /* 100 */ {"var100", "undefined", "-", UC_NONE}, + /* 101 */ {"var101", "undefined", "-", UC_NONE}, + /* 102 */ {"var102", "undefined", "-", UC_NONE}, + /* 103 */ {"var103", "undefined", "-", UC_NONE}, + /* 104 */ {"var104", "undefined", "-", UC_NONE}, + /* 105 */ {"var105", "undefined", "-", UC_NONE}, + /* 106 */ {"var106", "undefined", "-", UC_NONE}, + /* 107 */ {"var107", "undefined", "-", UC_NONE}, + /* 108 */ {"var108", "undefined", "-", UC_NONE}, + /* 109 */ {"var109", "undefined", "-", UC_NONE}, + /* 110 */ {"var110", "undefined", "-", UC_NONE}, + /* 111 */ {"var111", "undefined", "-", UC_NONE}, + /* 112 */ {"var112", "undefined", "-", UC_NONE}, + /* 113 */ {"var113", "undefined", "-", UC_NONE}, + /* 114 */ {"var114", "undefined", "-", UC_NONE}, + /* 115 */ {"var115", "undefined", "-", UC_NONE}, + /* 116 */ {"var116", "undefined", "-", UC_NONE}, + /* 117 */ {"var117", "undefined", "-", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"var121", "undefined", "-", UC_NONE}, + /* 122 */ {"var122", "undefined", "-", UC_NONE}, + /* 123 */ {"var123", "undefined", "-", UC_NONE}, + /* 124 */ {"var124", "undefined", "-", UC_NONE}, + /* 125 */ {"var125", "undefined", "-", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"var127", "undefined", "-", UC_NONE}, + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"var130", "undefined", "-", UC_NONE}, + /* 131 */ {"var131", "undefined", "-", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"var141", "undefined", "-", UC_NONE}, + /* 142 */ {"var142", "undefined", "-", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"var144", "undefined", "-", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"var155", "undefined", "-", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"var165", "undefined", "-", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"var171", "undefined", "-", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"TSRU", "Top solar radiation upward", "W/m^2", UC_NONE}, + /* 209 */ {"TTRU", "Top thermal radiation upward", "W/m^2", UC_NONE}, + /* 210 */ {"TSUC", "Top solar radiation upward clear sky", "W/m^2", + UC_NONE}, + /* 211 */ {"TTUC", "Top thermal radiation upward clear sky", "W/m^2", + UC_NONE}, + /* 212 */ {"CLW", "Cloud liquid water", "kg/kg", UC_NONE}, + /* 213 */ {"CF", "Cloud fraction 0-1", "-", UC_NONE}, + /* 214 */ {"DHR", "Diabatic heating by radiation", "K/s", UC_NONE}, + /* 215 */ {"DHVD", "Diabatic heating by vertical diffusion", "K/s", + UC_NONE}, + /* 216 */ {"DHCC", "Diabatic heating by cumulus convection", "K/s", + UC_NONE}, + /* 217 */ {"DHLC", "Diabatic heating by large-scale condensation", "K/s", + UC_NONE}, + /* 218 */ {"VDZW", "Vertical diffusion of zonal wind", "m^2/s^3", UC_NONE}, + /* 219 */ {"VDMW", "Vertical diffusion of meridional wind", "m^2/s^3", + UC_NONE}, + /* 220 */ {"EWGD", "EW gravity wave drag", "m^2/s^3", UC_NONE}, + /* 221 */ {"NSGD", "NS gravity wave drag", "m^2/s^3", UC_NONE}, + /* 222 */ {"CTZW", "Convective tendency of zonal wind", "m^2/s^3", + UC_NONE}, + /* 223 */ {"CTMW", "Convective tendency of meridional wind", "m^2/s^3", + UC_NONE}, + /* 224 */ {"VDH", "Vertical diffusion of humidity", "kg/(kg*s)", UC_NONE}, + /* 225 */ {"HTCC", "Humidity tendency by cumulus convection", "kg/(kg*s)", + UC_NONE}, + /* 226 */ {"HTLC", "Humidity tendency by large-scale condensation", + "kg/(kg*s)", UC_NONE}, + /* 227 */ {"CRNH", "Change from removing negative humidity", "kg/(kg*s)", + UC_NONE}, + /* 228 */ {"ATT", "Adiabatic tendency of temperature", "K/s", UC_NONE}, + /* 229 */ {"ATH", "Adiabatic tendency of humidity", "kg/(kg/s)", UC_NONE}, + /* 230 */ {"ATZW", "Adiabatic tendency of zonal wind", "m^2/s^3", UC_NONE}, + /* 231 */ {"ATMW", "Adiabatic tendency of meridional wind", "m^2/s^3", + UC_NONE}, + /* 232 */ {"MVV", "Mean vertical velocity", "Pa/s", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +GRIB1ParmTable parm_table_ecmwf_131[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"var1", "undefined", "-", UC_NONE}, + /* 2 */ {"var2", "undefined", "-", UC_NONE}, + /* 3 */ {"var3", "undefined", "-", UC_NONE}, + /* 4 */ {"var4", "undefined", "-", UC_NONE}, + /* 5 */ {"var5", "undefined", "-", UC_NONE}, + /* 6 */ {"var6", "undefined", "-", UC_NONE}, + /* 7 */ {"var7", "undefined", "-", UC_NONE}, + /* 8 */ {"var8", "undefined", "-", UC_NONE}, + /* 9 */ {"var9", "undefined", "-", UC_NONE}, + /* 10 */ {"var10", "undefined", "-", UC_NONE}, + /* 11 */ {"var11", "undefined", "-", UC_NONE}, + /* 12 */ {"var12", "undefined", "-", UC_NONE}, + /* 13 */ {"var13", "undefined", "-", UC_NONE}, + /* 14 */ {"var14", "undefined", "-", UC_NONE}, + /* 15 */ {"var15", "undefined", "-", UC_NONE}, + /* 16 */ {"var16", "undefined", "-", UC_NONE}, + /* 17 */ {"var17", "undefined", "-", UC_NONE}, + /* 18 */ {"var18", "undefined", "-", UC_NONE}, + /* 19 */ {"var19", "undefined", "-", UC_NONE}, + /* 20 */ {"var20", "undefined", "-", UC_NONE}, + /* 21 */ {"var21", "undefined", "-", UC_NONE}, + /* 22 */ {"var22", "undefined", "-", UC_NONE}, + /* 23 */ {"var23", "undefined", "-", UC_NONE}, + /* 24 */ {"var24", "undefined", "-", UC_NONE}, + /* 25 */ {"var25", "undefined", "-", UC_NONE}, + /* 26 */ {"var26", "undefined", "-", UC_NONE}, + /* 27 */ {"var27", "undefined", "-", UC_NONE}, + /* 28 */ {"var28", "undefined", "-", UC_NONE}, + /* 29 */ {"var29", "undefined", "-", UC_NONE}, + /* 30 */ {"var30", "undefined", "-", UC_NONE}, + /* 31 */ {"var31", "undefined", "-", UC_NONE}, + /* 32 */ {"var32", "undefined", "-", UC_NONE}, + /* 33 */ {"var33", "undefined", "-", UC_NONE}, + /* 34 */ {"var34", "undefined", "-", UC_NONE}, + /* 35 */ {"var35", "undefined", "-", UC_NONE}, + /* 36 */ {"var36", "undefined", "-", UC_NONE}, + /* 37 */ {"var37", "undefined", "-", UC_NONE}, + /* 38 */ {"var38", "undefined", "-", UC_NONE}, + /* 39 */ {"var39", "undefined", "-", UC_NONE}, + /* 40 */ {"var40", "undefined", "-", UC_NONE}, + /* 41 */ {"var41", "undefined", "-", UC_NONE}, + /* 42 */ {"var42", "undefined", "-", UC_NONE}, + /* 43 */ {"var43", "undefined", "-", UC_NONE}, + /* 44 */ {"var44", "undefined", "-", UC_NONE}, + /* 45 */ {"var45", "undefined", "-", UC_NONE}, + /* 46 */ {"var46", "undefined", "-", UC_NONE}, + /* 47 */ {"var47", "undefined", "-", UC_NONE}, + /* 48 */ {"var48", "undefined", "-", UC_NONE}, + /* 49 */ {"var49", "undefined", "-", UC_NONE}, + /* 50 */ {"var50", "undefined", "-", UC_NONE}, + /* 51 */ {"var51", "undefined", "-", UC_NONE}, + /* 52 */ {"var52", "undefined", "-", UC_NONE}, + /* 53 */ {"var53", "undefined", "-", UC_NONE}, + /* 54 */ {"var54", "undefined", "-", UC_NONE}, + /* 55 */ {"var55", "undefined", "-", UC_NONE}, + /* 56 */ {"var56", "undefined", "-", UC_NONE}, + /* 57 */ {"var57", "undefined", "-", UC_NONE}, + /* 58 */ {"var58", "undefined", "-", UC_NONE}, + /* 59 */ {"var59", "undefined", "-", UC_NONE}, + /* 60 */ {"var60", "undefined", "-", UC_NONE}, + /* 61 */ {"var61", "undefined", "-", UC_NONE}, + /* 62 */ {"var62", "undefined", "-", UC_NONE}, + /* 63 */ {"var63", "undefined", "-", UC_NONE}, + /* 64 */ {"var64", "undefined", "-", UC_NONE}, + /* 65 */ {"var65", "undefined", "-", UC_NONE}, + /* 66 */ {"var66", "undefined", "-", UC_NONE}, + /* 67 */ {"var67", "undefined", "-", UC_NONE}, + /* 68 */ {"var68", "undefined", "-", UC_NONE}, + /* 69 */ {"var69", "undefined", "-", UC_NONE}, + /* 70 */ {"var70", "undefined", "-", UC_NONE}, + /* 71 */ {"var71", "undefined", "-", UC_NONE}, + /* 72 */ {"var72", "undefined", "-", UC_NONE}, + /* 73 */ {"var73", "undefined", "-", UC_NONE}, + /* 74 */ {"var74", "undefined", "-", UC_NONE}, + /* 75 */ {"var75", "undefined", "-", UC_NONE}, + /* 76 */ {"var76", "undefined", "-", UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"var80", "undefined", "-", UC_NONE}, + /* 81 */ {"var81", "undefined", "-", UC_NONE}, + /* 82 */ {"var82", "undefined", "-", UC_NONE}, + /* 83 */ {"var83", "undefined", "-", UC_NONE}, + /* 84 */ {"var84", "undefined", "-", UC_NONE}, + /* 85 */ {"var85", "undefined", "-", UC_NONE}, + /* 86 */ {"var86", "undefined", "-", UC_NONE}, + /* 87 */ {"var87", "undefined", "-", UC_NONE}, + /* 88 */ {"var88", "undefined", "-", UC_NONE}, + /* 89 */ {"var89", "undefined", "-", UC_NONE}, + /* 90 */ {"var90", "undefined", "-", UC_NONE}, + /* 91 */ {"var91", "undefined", "-", UC_NONE}, + /* 92 */ {"var92", "undefined", "-", UC_NONE}, + /* 93 */ {"var93", "undefined", "-", UC_NONE}, + /* 94 */ {"var94", "undefined", "-", UC_NONE}, + /* 95 */ {"var95", "undefined", "-", UC_NONE}, + /* 96 */ {"var96", "undefined", "-", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"var99", "undefined", "-", UC_NONE}, + /* 100 */ {"var100", "undefined", "-", UC_NONE}, + /* 101 */ {"var101", "undefined", "-", UC_NONE}, + /* 102 */ {"var102", "undefined", "-", UC_NONE}, + /* 103 */ {"var103", "undefined", "-", UC_NONE}, + /* 104 */ {"var104", "undefined", "-", UC_NONE}, + /* 105 */ {"var105", "undefined", "-", UC_NONE}, + /* 106 */ {"var106", "undefined", "-", UC_NONE}, + /* 107 */ {"var107", "undefined", "-", UC_NONE}, + /* 108 */ {"var108", "undefined", "-", UC_NONE}, + /* 109 */ {"var109", "undefined", "-", UC_NONE}, + /* 110 */ {"var110", "undefined", "-", UC_NONE}, + /* 111 */ {"var111", "undefined", "-", UC_NONE}, + /* 112 */ {"var112", "undefined", "-", UC_NONE}, + /* 113 */ {"var113", "undefined", "-", UC_NONE}, + /* 114 */ {"var114", "undefined", "-", UC_NONE}, + /* 115 */ {"var115", "undefined", "-", UC_NONE}, + /* 116 */ {"var116", "undefined", "-", UC_NONE}, + /* 117 */ {"var117", "undefined", "-", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"var121", "undefined", "-", UC_NONE}, + /* 122 */ {"var122", "undefined", "-", UC_NONE}, + /* 123 */ {"var123", "undefined", "-", UC_NONE}, + /* 124 */ {"var124", "undefined", "-", UC_NONE}, + /* 125 */ {"var125", "undefined", "-", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"var127", "undefined", "-", UC_NONE}, + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"TAP", "Temperature anomaly probability", "% K", UC_NONE}, + /* 131 */ {"var131", "undefined", "-", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"var141", "undefined", "-", UC_NONE}, + /* 142 */ {"var142", "undefined", "-", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"var144", "undefined", "-", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"var155", "undefined", "-", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"10SP", "10 metre speed probability", "% m/s", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"2TP", "2 metre temperature probability", "%", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"var171", "undefined", "-", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"TTP", "Total precipitation probability", "% m", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +GRIB1ParmTable parm_table_ecmwf_140[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"var1", "undefined", "-", UC_NONE}, + /* 2 */ {"var2", "undefined", "-", UC_NONE}, + /* 3 */ {"var3", "undefined", "-", UC_NONE}, + /* 4 */ {"var4", "undefined", "-", UC_NONE}, + /* 5 */ {"var5", "undefined", "-", UC_NONE}, + /* 6 */ {"var6", "undefined", "-", UC_NONE}, + /* 7 */ {"var7", "undefined", "-", UC_NONE}, + /* 8 */ {"var8", "undefined", "-", UC_NONE}, + /* 9 */ {"var9", "undefined", "-", UC_NONE}, + /* 10 */ {"var10", "undefined", "-", UC_NONE}, + /* 11 */ {"var11", "undefined", "-", UC_NONE}, + /* 12 */ {"var12", "undefined", "-", UC_NONE}, + /* 13 */ {"var13", "undefined", "-", UC_NONE}, + /* 14 */ {"var14", "undefined", "-", UC_NONE}, + /* 15 */ {"var15", "undefined", "-", UC_NONE}, + /* 16 */ {"var16", "undefined", "-", UC_NONE}, + /* 17 */ {"var17", "undefined", "-", UC_NONE}, + /* 18 */ {"var18", "undefined", "-", UC_NONE}, + /* 19 */ {"var19", "undefined", "-", UC_NONE}, + /* 20 */ {"var20", "undefined", "-", UC_NONE}, + /* 21 */ {"var21", "undefined", "-", UC_NONE}, + /* 22 */ {"var22", "undefined", "-", UC_NONE}, + /* 23 */ {"var23", "undefined", "-", UC_NONE}, + /* 24 */ {"var24", "undefined", "-", UC_NONE}, + /* 25 */ {"var25", "undefined", "-", UC_NONE}, + /* 26 */ {"var26", "undefined", "-", UC_NONE}, + /* 27 */ {"var27", "undefined", "-", UC_NONE}, + /* 28 */ {"var28", "undefined", "-", UC_NONE}, + /* 29 */ {"var29", "undefined", "-", UC_NONE}, + /* 30 */ {"var30", "undefined", "-", UC_NONE}, + /* 31 */ {"var31", "undefined", "-", UC_NONE}, + /* 32 */ {"var32", "undefined", "-", UC_NONE}, + /* 33 */ {"var33", "undefined", "-", UC_NONE}, + /* 34 */ {"var34", "undefined", "-", UC_NONE}, + /* 35 */ {"var35", "undefined", "-", UC_NONE}, + /* 36 */ {"var36", "undefined", "-", UC_NONE}, + /* 37 */ {"var37", "undefined", "-", UC_NONE}, + /* 38 */ {"var38", "undefined", "-", UC_NONE}, + /* 39 */ {"var39", "undefined", "-", UC_NONE}, + /* 40 */ {"var40", "undefined", "-", UC_NONE}, + /* 41 */ {"var41", "undefined", "-", UC_NONE}, + /* 42 */ {"var42", "undefined", "-", UC_NONE}, + /* 43 */ {"var43", "undefined", "-", UC_NONE}, + /* 44 */ {"var44", "undefined", "-", UC_NONE}, + /* 45 */ {"var45", "undefined", "-", UC_NONE}, + /* 46 */ {"var46", "undefined", "-", UC_NONE}, + /* 47 */ {"var47", "undefined", "-", UC_NONE}, + /* 48 */ {"var48", "undefined", "-", UC_NONE}, + /* 49 */ {"var49", "undefined", "-", UC_NONE}, + /* 50 */ {"var50", "undefined", "-", UC_NONE}, + /* 51 */ {"var51", "undefined", "-", UC_NONE}, + /* 52 */ {"var52", "undefined", "-", UC_NONE}, + /* 53 */ {"var53", "undefined", "-", UC_NONE}, + /* 54 */ {"var54", "undefined", "-", UC_NONE}, + /* 55 */ {"var55", "undefined", "-", UC_NONE}, + /* 56 */ {"var56", "undefined", "-", UC_NONE}, + /* 57 */ {"var57", "undefined", "-", UC_NONE}, + /* 58 */ {"var58", "undefined", "-", UC_NONE}, + /* 59 */ {"var59", "undefined", "-", UC_NONE}, + /* 60 */ {"var60", "undefined", "-", UC_NONE}, + /* 61 */ {"var61", "undefined", "-", UC_NONE}, + /* 62 */ {"var62", "undefined", "-", UC_NONE}, + /* 63 */ {"var63", "undefined", "-", UC_NONE}, + /* 64 */ {"var64", "undefined", "-", UC_NONE}, + /* 65 */ {"var65", "undefined", "-", UC_NONE}, + /* 66 */ {"var66", "undefined", "-", UC_NONE}, + /* 67 */ {"var67", "undefined", "-", UC_NONE}, + /* 68 */ {"var68", "undefined", "-", UC_NONE}, + /* 69 */ {"var69", "undefined", "-", UC_NONE}, + /* 70 */ {"var70", "undefined", "-", UC_NONE}, + /* 71 */ {"var71", "undefined", "-", UC_NONE}, + /* 72 */ {"var72", "undefined", "-", UC_NONE}, + /* 73 */ {"var73", "undefined", "-", UC_NONE}, + /* 74 */ {"var74", "undefined", "-", UC_NONE}, + /* 75 */ {"var75", "undefined", "-", UC_NONE}, + /* 76 */ {"var76", "undefined", "-", UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"var80", "undefined", "-", UC_NONE}, + /* 81 */ {"var81", "undefined", "-", UC_NONE}, + /* 82 */ {"var82", "undefined", "-", UC_NONE}, + /* 83 */ {"var83", "undefined", "-", UC_NONE}, + /* 84 */ {"var84", "undefined", "-", UC_NONE}, + /* 85 */ {"var85", "undefined", "-", UC_NONE}, + /* 86 */ {"var86", "undefined", "-", UC_NONE}, + /* 87 */ {"var87", "undefined", "-", UC_NONE}, + /* 88 */ {"var88", "undefined", "-", UC_NONE}, + /* 89 */ {"var89", "undefined", "-", UC_NONE}, + /* 90 */ {"var90", "undefined", "-", UC_NONE}, + /* 91 */ {"var91", "undefined", "-", UC_NONE}, + /* 92 */ {"var92", "undefined", "-", UC_NONE}, + /* 93 */ {"var93", "undefined", "-", UC_NONE}, + /* 94 */ {"var94", "undefined", "-", UC_NONE}, + /* 95 */ {"var95", "undefined", "-", UC_NONE}, + /* 96 */ {"var96", "undefined", "-", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"var99", "undefined", "-", UC_NONE}, + /* 100 */ {"var100", "undefined", "-", UC_NONE}, + /* 101 */ {"var101", "undefined", "-", UC_NONE}, + /* 102 */ {"var102", "undefined", "-", UC_NONE}, + /* 103 */ {"var103", "undefined", "-", UC_NONE}, + /* 104 */ {"var104", "undefined", "-", UC_NONE}, + /* 105 */ {"var105", "undefined", "-", UC_NONE}, + /* 106 */ {"var106", "undefined", "-", UC_NONE}, + /* 107 */ {"var107", "undefined", "-", UC_NONE}, + /* 108 */ {"var108", "undefined", "-", UC_NONE}, + /* 109 */ {"var109", "undefined", "-", UC_NONE}, + /* 110 */ {"var110", "undefined", "-", UC_NONE}, + /* 111 */ {"var111", "undefined", "-", UC_NONE}, + /* 112 */ {"var112", "undefined", "-", UC_NONE}, + /* 113 */ {"var113", "undefined", "-", UC_NONE}, + /* 114 */ {"var114", "undefined", "-", UC_NONE}, + /* 115 */ {"var115", "undefined", "-", UC_NONE}, + /* 116 */ {"var116", "undefined", "-", UC_NONE}, + /* 117 */ {"var117", "undefined", "-", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"var121", "undefined", "-", UC_NONE}, + /* 122 */ {"var122", "undefined", "-", UC_NONE}, + /* 123 */ {"var123", "undefined", "-", UC_NONE}, + /* 124 */ {"var124", "undefined", "-", UC_NONE}, + /* 125 */ {"var125", "undefined", "-", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"var127", "undefined", "-", UC_NONE}, + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"var130", "undefined", "-", UC_NONE}, + /* 131 */ {"var131", "undefined", "-", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"var141", "undefined", "-", UC_NONE}, + /* 142 */ {"var142", "undefined", "-", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"var144", "undefined", "-", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"var155", "undefined", "-", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"var165", "undefined", "-", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"var171", "undefined", "-", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"SWH", "Significant wave height", "m", UC_NONE}, + /* 230 */ {"MWD", "Mean wave direction", "degrees", UC_NONE}, + /* 231 */ {"PP1D", "Peak period of 1d spectra", "s", UC_NONE}, + /* 232 */ {"MWP", "Mean wave period", "s", UC_NONE}, + /* 233 */ {"CDWW", "Coefficient of drag with waves", "-", UC_NONE}, + /* 234 */ {"SHWW", "Significant height of wind waves", "m", UC_NONE}, + /* 235 */ {"MDWW", "Mean direction of wind waves", "degrees", UC_NONE}, + /* 236 */ {"MPWW", "Mean period of wind waves", "s", UC_NONE}, + /* 237 */ {"SHPS", "Significant height of primary swell", "m", UC_NONE}, + /* 238 */ {"MDPS", "Mean direction of primary swell", "degrees", UC_NONE}, + /* 239 */ {"MPPS", "Mean period of primary swell", "s", UC_NONE}, + /* 240 */ {"SDHS", "Standard deviation wave height", "m", UC_NONE}, + /* 241 */ {"MU10", "Mean of 10m windspeed", "m/s", UC_NONE}, + /* 242 */ {"MDWI", "Mean wind direction", "degrees", UC_NONE}, + /* 243 */ {"SDU", "Standard deviation 10m wind speed", "m/s", UC_NONE}, + /* 244 */ {"MSQS", "Mean square slope of waves", "-", UC_NONE}, + /* 245 */ {"10MS", "Ten metre windspeed", "m/s", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"2DSP", "2D wave spectra", "m^2*s/radiance", UC_NONE}, + /* 251 */ {"var251", "2D wave spectra(single direction/single frequency)", + "m^2*s/radiance", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +GRIB1ParmTable parm_table_ecmwf_150[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"var1", "undefined", "-", UC_NONE}, + /* 2 */ {"var2", "undefined", "-", UC_NONE}, + /* 3 */ {"var3", "undefined", "-", UC_NONE}, + /* 4 */ {"var4", "undefined", "-", UC_NONE}, + /* 5 */ {"var5", "undefined", "-", UC_NONE}, + /* 6 */ {"var6", "undefined", "-", UC_NONE}, + /* 7 */ {"var7", "undefined", "-", UC_NONE}, + /* 8 */ {"var8", "undefined", "-", UC_NONE}, + /* 9 */ {"var9", "undefined", "-", UC_NONE}, + /* 10 */ {"var10", "undefined", "-", UC_NONE}, + /* 11 */ {"var11", "undefined", "-", UC_NONE}, + /* 12 */ {"var12", "undefined", "-", UC_NONE}, + /* 13 */ {"var13", "undefined", "-", UC_NONE}, + /* 14 */ {"var14", "undefined", "-", UC_NONE}, + /* 15 */ {"var15", "undefined", "-", UC_NONE}, + /* 16 */ {"var16", "undefined", "-", UC_NONE}, + /* 17 */ {"var17", "undefined", "-", UC_NONE}, + /* 18 */ {"var18", "undefined", "-", UC_NONE}, + /* 19 */ {"var19", "undefined", "-", UC_NONE}, + /* 20 */ {"var20", "undefined", "-", UC_NONE}, + /* 21 */ {"var21", "undefined", "-", UC_NONE}, + /* 22 */ {"var22", "undefined", "-", UC_NONE}, + /* 23 */ {"var23", "undefined", "-", UC_NONE}, + /* 24 */ {"var24", "undefined", "-", UC_NONE}, + /* 25 */ {"var25", "undefined", "-", UC_NONE}, + /* 26 */ {"var26", "undefined", "-", UC_NONE}, + /* 27 */ {"var27", "undefined", "-", UC_NONE}, + /* 28 */ {"var28", "undefined", "-", UC_NONE}, + /* 29 */ {"var29", "undefined", "-", UC_NONE}, + /* 30 */ {"var30", "undefined", "-", UC_NONE}, + /* 31 */ {"var31", "undefined", "-", UC_NONE}, + /* 32 */ {"var32", "undefined", "-", UC_NONE}, + /* 33 */ {"var33", "undefined", "-", UC_NONE}, + /* 34 */ {"var34", "undefined", "-", UC_NONE}, + /* 35 */ {"var35", "undefined", "-", UC_NONE}, + /* 36 */ {"var36", "undefined", "-", UC_NONE}, + /* 37 */ {"var37", "undefined", "-", UC_NONE}, + /* 38 */ {"var38", "undefined", "-", UC_NONE}, + /* 39 */ {"var39", "undefined", "-", UC_NONE}, + /* 40 */ {"var40", "undefined", "-", UC_NONE}, + /* 41 */ {"var41", "undefined", "-", UC_NONE}, + /* 42 */ {"var42", "undefined", "-", UC_NONE}, + /* 43 */ {"var43", "undefined", "-", UC_NONE}, + /* 44 */ {"var44", "undefined", "-", UC_NONE}, + /* 45 */ {"var45", "undefined", "-", UC_NONE}, + /* 46 */ {"var46", "undefined", "-", UC_NONE}, + /* 47 */ {"var47", "undefined", "-", UC_NONE}, + /* 48 */ {"var48", "undefined", "-", UC_NONE}, + /* 49 */ {"var49", "undefined", "-", UC_NONE}, + /* 50 */ {"var50", "undefined", "-", UC_NONE}, + /* 51 */ {"var51", "undefined", "-", UC_NONE}, + /* 52 */ {"var52", "undefined", "-", UC_NONE}, + /* 53 */ {"var53", "undefined", "-", UC_NONE}, + /* 54 */ {"var54", "undefined", "-", UC_NONE}, + /* 55 */ {"var55", "undefined", "-", UC_NONE}, + /* 56 */ {"var56", "undefined", "-", UC_NONE}, + /* 57 */ {"var57", "undefined", "-", UC_NONE}, + /* 58 */ {"var58", "undefined", "-", UC_NONE}, + /* 59 */ {"var59", "undefined", "-", UC_NONE}, + /* 60 */ {"var60", "undefined", "-", UC_NONE}, + /* 61 */ {"var61", "undefined", "-", UC_NONE}, + /* 62 */ {"var62", "undefined", "-", UC_NONE}, + /* 63 */ {"var63", "undefined", "-", UC_NONE}, + /* 64 */ {"var64", "undefined", "-", UC_NONE}, + /* 65 */ {"var65", "undefined", "-", UC_NONE}, + /* 66 */ {"var66", "undefined", "-", UC_NONE}, + /* 67 */ {"var67", "undefined", "-", UC_NONE}, + /* 68 */ {"var68", "undefined", "-", UC_NONE}, + /* 69 */ {"var69", "undefined", "-", UC_NONE}, + /* 70 */ {"var70", "undefined", "-", UC_NONE}, + /* 71 */ {"var71", "undefined", "-", UC_NONE}, + /* 72 */ {"var72", "undefined", "-", UC_NONE}, + /* 73 */ {"var73", "undefined", "-", UC_NONE}, + /* 74 */ {"var74", "undefined", "-", UC_NONE}, + /* 75 */ {"var75", "undefined", "-", UC_NONE}, + /* 76 */ {"var76", "undefined", "-", UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"var80", "undefined", "-", UC_NONE}, + /* 81 */ {"var81", "undefined", "-", UC_NONE}, + /* 82 */ {"var82", "undefined", "-", UC_NONE}, + /* 83 */ {"var83", "undefined", "-", UC_NONE}, + /* 84 */ {"var84", "undefined", "-", UC_NONE}, + /* 85 */ {"var85", "undefined", "-", UC_NONE}, + /* 86 */ {"var86", "undefined", "-", UC_NONE}, + /* 87 */ {"var87", "undefined", "-", UC_NONE}, + /* 88 */ {"var88", "undefined", "-", UC_NONE}, + /* 89 */ {"var89", "undefined", "-", UC_NONE}, + /* 90 */ {"var90", "undefined", "-", UC_NONE}, + /* 91 */ {"var91", "undefined", "-", UC_NONE}, + /* 92 */ {"var92", "undefined", "-", UC_NONE}, + /* 93 */ {"var93", "undefined", "-", UC_NONE}, + /* 94 */ {"var94", "undefined", "-", UC_NONE}, + /* 95 */ {"var95", "undefined", "-", UC_NONE}, + /* 96 */ {"var96", "undefined", "-", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"var99", "undefined", "-", UC_NONE}, + /* 100 */ {"var100", "undefined", "-", UC_NONE}, + /* 101 */ {"var101", "undefined", "-", UC_NONE}, + /* 102 */ {"var102", "undefined", "-", UC_NONE}, + /* 103 */ {"var103", "undefined", "-", UC_NONE}, + /* 104 */ {"var104", "undefined", "-", UC_NONE}, + /* 105 */ {"var105", "undefined", "-", UC_NONE}, + /* 106 */ {"var106", "undefined", "-", UC_NONE}, + /* 107 */ {"var107", "undefined", "-", UC_NONE}, + /* 108 */ {"var108", "undefined", "-", UC_NONE}, + /* 109 */ {"var109", "undefined", "-", UC_NONE}, + /* 110 */ {"var110", "undefined", "-", UC_NONE}, + /* 111 */ {"var111", "undefined", "-", UC_NONE}, + /* 112 */ {"var112", "undefined", "-", UC_NONE}, + /* 113 */ {"var113", "undefined", "-", UC_NONE}, + /* 114 */ {"var114", "undefined", "-", UC_NONE}, + /* 115 */ {"var115", "undefined", "-", UC_NONE}, + /* 116 */ {"var116", "undefined", "-", UC_NONE}, + /* 117 */ {"var117", "undefined", "-", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"var121", "undefined", "-", UC_NONE}, + /* 122 */ {"var122", "undefined", "-", UC_NONE}, + /* 123 */ {"var123", "undefined", "-", UC_NONE}, + /* 124 */ {"var124", "undefined", "-", UC_NONE}, + /* 125 */ {"var125", "undefined", "-", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"var127", "undefined", "-", UC_NONE}, + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "Ocean potential temperature", "C", UC_NONE}, + /* 130 */ {"var130", "Ocean salinity", "psu", UC_NONE}, + /* 131 */ {"var131", "Ocean potential density(reference = surface)", + "kg/m^3 -1000", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"var133", "Ocean u velocity", "m/s", UC_NONE}, + /* 134 */ {"var134", "Ocean v velocity", "m/s", UC_NONE}, + /* 135 */ {"var135", "Ocean w velocity", "m/s", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "Richardson number", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"var139", "u*v product", "m/s^2", UC_NONE}, + /* 140 */ {"var140", "u*T product", "(m*C)/s", UC_NONE}, + /* 141 */ {"var141", "v*T product", "(m*C)/s", UC_NONE}, + /* 142 */ {"var142", "u*u product", "m/s^2", UC_NONE}, + /* 143 */ {"var143", "v*v product", "m/s^2", UC_NONE}, + /* 144 */ {"var144", "uv - u~v~ (u~ is time-mean of u)", "m/s^2", UC_NONE}, + /* 145 */ {"var145", "uT - u~T~", "(m*C)/s", UC_NONE}, + /* 146 */ {"var146", "vT - v~T~", "(m*C)/s", UC_NONE}, + /* 147 */ {"var147", "uu - u~u~", "m/s^2", UC_NONE}, + /* 148 */ {"var148", "vv - v~v~", "m/s^2", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "Sea level (departure from geoid tides removed)", "-", + UC_NONE}, + /* 153 */ {"var153", "Barotropic stream function", "-", UC_NONE}, + /* 154 */ {"var154", "Mixed layer depth (Tcr=0.5 C for HOPE model)", "m", + UC_NONE}, + /* 155 */ {"var155", "Depth (eg of isothermal surface)", "m", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"var165", "undefined", "-", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "U-stress", "Pa", UC_NONE}, + /* 169 */ {"var169", "V-stress", "Pa", UC_NONE}, + /* 170 */ {"var170", "Turbulent Kinetic Energy input", "-", UC_NONE}, + /* 171 */ {"var171", "Net surface heat flux (+ve = down)", "-", UC_NONE}, + /* 172 */ {"var172", "Surface solar radiation", "-", UC_NONE}, + /* 173 */ {"var173", "P-E", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"var180", "Diagnosed SST eror", "C", UC_NONE}, + /* 181 */ {"var181", "Heat flux correction", "W/m^2", UC_NONE}, + /* 182 */ {"var182", "Observed SST", "C", UC_NONE}, + /* 183 */ {"var183", "Observed heat flux", "W/m^2", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +GRIB1ParmTable parm_table_ecmwf_160[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"var1", "undefined", "-", UC_NONE}, + /* 2 */ {"var2", "undefined", "-", UC_NONE}, + /* 3 */ {"var3", "undefined", "-", UC_NONE}, + /* 4 */ {"var4", "undefined", "-", UC_NONE}, + /* 5 */ {"var5", "undefined", "-", UC_NONE}, + /* 6 */ {"var6", "undefined", "-", UC_NONE}, + /* 7 */ {"var7", "undefined", "-", UC_NONE}, + /* 8 */ {"var8", "undefined", "-", UC_NONE}, + /* 9 */ {"var9", "undefined", "-", UC_NONE}, + /* 10 */ {"var10", "undefined", "-", UC_NONE}, + /* 11 */ {"var11", "undefined", "-", UC_NONE}, + /* 12 */ {"var12", "undefined", "-", UC_NONE}, + /* 13 */ {"var13", "undefined", "-", UC_NONE}, + /* 14 */ {"var14", "undefined", "-", UC_NONE}, + /* 15 */ {"var15", "undefined", "-", UC_NONE}, + /* 16 */ {"var16", "undefined", "-", UC_NONE}, + /* 17 */ {"var17", "undefined", "-", UC_NONE}, + /* 18 */ {"var18", "undefined", "-", UC_NONE}, + /* 19 */ {"var19", "undefined", "-", UC_NONE}, + /* 20 */ {"var20", "undefined", "-", UC_NONE}, + /* 21 */ {"var21", "undefined", "-", UC_NONE}, + /* 22 */ {"var22", "undefined", "-", UC_NONE}, + /* 23 */ {"var23", "undefined", "-", UC_NONE}, + /* 24 */ {"var24", "undefined", "-", UC_NONE}, + /* 25 */ {"var25", "undefined", "-", UC_NONE}, + /* 26 */ {"var26", "undefined", "-", UC_NONE}, + /* 27 */ {"var27", "undefined", "-", UC_NONE}, + /* 28 */ {"var28", "undefined", "-", UC_NONE}, + /* 29 */ {"var29", "undefined", "-", UC_NONE}, + /* 30 */ {"var30", "undefined", "-", UC_NONE}, + /* 31 */ {"var31", "undefined", "-", UC_NONE}, + /* 32 */ {"var32", "undefined", "-", UC_NONE}, + /* 33 */ {"var33", "undefined", "-", UC_NONE}, + /* 34 */ {"var34", "undefined", "-", UC_NONE}, + /* 35 */ {"var35", "undefined", "-", UC_NONE}, + /* 36 */ {"var36", "undefined", "-", UC_NONE}, + /* 37 */ {"var37", "undefined", "-", UC_NONE}, + /* 38 */ {"var38", "undefined", "-", UC_NONE}, + /* 39 */ {"var39", "undefined", "-", UC_NONE}, + /* 40 */ {"var40", "undefined", "-", UC_NONE}, + /* 41 */ {"var41", "undefined", "-", UC_NONE}, + /* 42 */ {"var42", "undefined", "-", UC_NONE}, + /* 43 */ {"var43", "undefined", "-", UC_NONE}, + /* 44 */ {"var44", "undefined", "-", UC_NONE}, + /* 45 */ {"var45", "undefined", "-", UC_NONE}, + /* 46 */ {"var46", "undefined", "-", UC_NONE}, + /* 47 */ {"var47", "undefined", "-", UC_NONE}, + /* 48 */ {"var48", "undefined", "-", UC_NONE}, + /* 49 */ {"var49", "undefined", "-", UC_NONE}, + /* 50 */ {"var50", "undefined", "-", UC_NONE}, + /* 51 */ {"var51", "undefined", "-", UC_NONE}, + /* 52 */ {"var52", "undefined", "-", UC_NONE}, + /* 53 */ {"var53", "undefined", "-", UC_NONE}, + /* 54 */ {"var54", "undefined", "-", UC_NONE}, + /* 55 */ {"var55", "undefined", "-", UC_NONE}, + /* 56 */ {"var56", "undefined", "-", UC_NONE}, + /* 57 */ {"var57", "undefined", "-", UC_NONE}, + /* 58 */ {"var58", "undefined", "-", UC_NONE}, + /* 59 */ {"var59", "undefined", "-", UC_NONE}, + /* 60 */ {"var60", "undefined", "-", UC_NONE}, + /* 61 */ {"var61", "undefined", "-", UC_NONE}, + /* 62 */ {"var62", "undefined", "-", UC_NONE}, + /* 63 */ {"var63", "undefined", "-", UC_NONE}, + /* 64 */ {"var64", "undefined", "-", UC_NONE}, + /* 65 */ {"var65", "undefined", "-", UC_NONE}, + /* 66 */ {"var66", "undefined", "-", UC_NONE}, + /* 67 */ {"var67", "undefined", "-", UC_NONE}, + /* 68 */ {"var68", "undefined", "-", UC_NONE}, + /* 69 */ {"var69", "undefined", "-", UC_NONE}, + /* 70 */ {"var70", "undefined", "-", UC_NONE}, + /* 71 */ {"var71", "undefined", "-", UC_NONE}, + /* 72 */ {"var72", "undefined", "-", UC_NONE}, + /* 73 */ {"var73", "undefined", "-", UC_NONE}, + /* 74 */ {"var74", "undefined", "-", UC_NONE}, + /* 75 */ {"var75", "undefined", "-", UC_NONE}, + /* 76 */ {"var76", "undefined", "-", UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"var80", "undefined", "-", UC_NONE}, + /* 81 */ {"var81", "undefined", "-", UC_NONE}, + /* 82 */ {"var82", "undefined", "-", UC_NONE}, + /* 83 */ {"var83", "undefined", "-", UC_NONE}, + /* 84 */ {"var84", "undefined", "-", UC_NONE}, + /* 85 */ {"var85", "undefined", "-", UC_NONE}, + /* 86 */ {"var86", "undefined", "-", UC_NONE}, + /* 87 */ {"var87", "undefined", "-", UC_NONE}, + /* 88 */ {"var88", "undefined", "-", UC_NONE}, + /* 89 */ {"var89", "undefined", "-", UC_NONE}, + /* 90 */ {"var90", "undefined", "-", UC_NONE}, + /* 91 */ {"var91", "undefined", "-", UC_NONE}, + /* 92 */ {"var92", "undefined", "-", UC_NONE}, + /* 93 */ {"var93", "undefined", "-", UC_NONE}, + /* 94 */ {"var94", "undefined", "-", UC_NONE}, + /* 95 */ {"var95", "undefined", "-", UC_NONE}, + /* 96 */ {"var96", "undefined", "-", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"var99", "undefined", "-", UC_NONE}, + /* 100 */ {"var100", "undefined", "-", UC_NONE}, + /* 101 */ {"var101", "undefined", "-", UC_NONE}, + /* 102 */ {"var102", "undefined", "-", UC_NONE}, + /* 103 */ {"var103", "undefined", "-", UC_NONE}, + /* 104 */ {"var104", "undefined", "-", UC_NONE}, + /* 105 */ {"var105", "undefined", "-", UC_NONE}, + /* 106 */ {"var106", "undefined", "-", UC_NONE}, + /* 107 */ {"var107", "undefined", "-", UC_NONE}, + /* 108 */ {"var108", "undefined", "-", UC_NONE}, + /* 109 */ {"var109", "undefined", "-", UC_NONE}, + /* 110 */ {"var110", "undefined", "-", UC_NONE}, + /* 111 */ {"var111", "undefined", "-", UC_NONE}, + /* 112 */ {"var112", "undefined", "-", UC_NONE}, + /* 113 */ {"var113", "undefined", "-", UC_NONE}, + /* 114 */ {"var114", "undefined", "-", UC_NONE}, + /* 115 */ {"var115", "undefined", "-", UC_NONE}, + /* 116 */ {"var116", "undefined", "-", UC_NONE}, + /* 117 */ {"var117", "undefined", "-", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"var121", "undefined", "-", UC_NONE}, + /* 122 */ {"var122", "undefined", "-", UC_NONE}, + /* 123 */ {"var123", "undefined", "-", UC_NONE}, + /* 124 */ {"var124", "undefined", "-", UC_NONE}, + /* 125 */ {"var125", "undefined", "-", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"AT", "Atmospheric tide+", "-", UC_NONE}, + /* 128 */ {"BV", "Budget values+", "-", UC_NONE}, + /* 129 */ {"Z", "Geopotential / orography", "m^2/s^2", UC_NONE}, + /* 130 */ {"T", "Temperature", "K", UC_NONE}, + /* 131 */ {"U", "U-velocity", "m/s", UC_NONE}, + /* 132 */ {"V", "V-velocity", "m/s", UC_NONE}, + /* 133 */ {"Q", "Specific humidity", "kg/kg", UC_NONE}, + /* 134 */ {"SP", "Surface pressure", "Pa", UC_NONE}, + /* 135 */ {"W", "Vertical velocity", "Pa/s", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"PWC", "Precipitable water content", "kg/m^2", UC_NONE}, + /* 138 */ {"VO", "Vorticity (relative)", "1/s", UC_NONE}, + /* 139 */ {"STL1", "Soil temperature level 1", "K", UC_NONE}, + /* 140 */ {"SWL1", "Soil wetness level 1", "m", UC_NONE}, + /* 141 */ {"SD", "Snow depth (of water)", "m", UC_NONE}, + /* 142 */ {"LSP", "Large scale precipitation", "kg/(m^2*s)", UC_NONE}, + /* 143 */ {"CP", "Convective precipitation", "kg/(m^2*s)", UC_NONE}, + /* 144 */ {"SF", "Snow fall", "kg/(m^2*s)", UC_NONE}, + /* 145 */ {"BLD", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 146 */ {"SSHF", "Surface sensible heat flux", "W/m^2", UC_NONE}, + /* 147 */ {"SLHF", "Surface latent heat flux", "W/m^2", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"MSL", "Mean sea level pressure", "Pa", UC_NONE}, + /* 152 */ {"LNSP", "Ln surface pressure", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"D", "Divergence", "1/s", UC_NONE}, + /* 156 */ {"GH", "Height (geopotential)", "m", UC_NONE}, + /* 157 */ {"R", "Relative humidity (0 - 1)", "-", UC_NONE}, + /* 158 */ {"TSP", "Tendency of surface pressure", "Pa/s", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"TCC", "Total cloud cover (0 - 1)", "-", UC_NONE}, + /* 165 */ {"10U", "10 metre u wind component", "m/s", UC_NONE}, + /* 166 */ {"10V", "10 metre v wind component", "m/s", UC_NONE}, + /* 167 */ {"2T", "2 metre temperature", "K", UC_NONE}, + /* 168 */ {"2D", "2 metre dewpoint temperature", "K", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"STL2", "Soil temperature level 2", "K", UC_NONE}, + /* 171 */ {"SWL2", "Soil wetness level 2", "m", UC_NONE}, + /* 172 */ {"LSM", "Land/sea mask (0 - 1)", "-", UC_NONE}, + /* 173 */ {"SR", "Surface roughness", "m", UC_NONE}, + /* 174 */ {"AL", "Albedo (0 - 1)", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"SSR", "Surface solar radiation", "W/m^2", UC_NONE}, + /* 177 */ {"STR", "Surface thermal radiation", "W/m^2", UC_NONE}, + /* 178 */ {"TSR", "Top solar radiation", "W/m^2", UC_NONE}, + /* 179 */ {"TTR", "Top thermal radiation", "W/m^2", UC_NONE}, + /* 180 */ {"EWSS", "East/west surface stress", "N/(m^2*s)", UC_NONE}, + /* 181 */ {"NSSS", "North/south surface stress", "N/(m^2*s)", UC_NONE}, + /* 182 */ {"E", "Evaporation", "kg/(m^2*s)", UC_NONE}, + /* 183 */ {"STL3", "Soil temperature level 3", "K", UC_NONE}, + /* 184 */ {"SWL3", "Soil wetness level 3", "m", UC_NONE}, + /* 185 */ {"CCC", "Convective cloud cover (0 - 1)", "-", UC_NONE}, + /* 186 */ {"LCC", "Low cloud cover (0 - 1)", "-", UC_NONE}, + /* 187 */ {"MCC", "Medium cloud cover (0 - 1)", "-", UC_NONE}, + /* 188 */ {"HCC", "High cloud cover (0 - 1)", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"EWOV", "EW component of sub-grid scale orographic variance", + "m^2", UC_NONE}, + /* 191 */ {"NSOV", "NS component of sub-grid scale orographic variance", + "m^2", UC_NONE}, + /* 192 */ {"NWOV", "NWSE component sub-grid scale orographic variance", + "m^2", UC_NONE}, + /* 193 */ {"NEOV", "NESW component sub-grid scale orographic variance", + "m^2", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"LGWS", "Latitudinal component of gravity wave stress", + "(N*s)/m^2", UC_NONE}, + /* 196 */ {"MGWS", "Meridional component of gravity wave stress", + "(N*s)/m^2", UC_NONE}, + /* 197 */ {"GWD", "Gravity wave dissipation", "(W*s)/m^2", UC_NONE}, + /* 198 */ {"SRC", "Skin reservoir content (of water)", "m", UC_NONE}, + /* 199 */ {"VEG", "Percentage of vegetation", "%", UC_NONE}, + /* 200 */ {"VSO", "Variance of sub-grid scale orography", "m^2", UC_NONE}, + /* 201 */ {"MX2T", "Max temp.2m during averaging time", "K", UC_NONE}, + /* 202 */ {"MN2T", "Min temp.2m during averaging time", "K", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"PAW", "Precip. analysis weights", "-", UC_NONE}, + /* 205 */ {"RO", "Runoff", "kg/(m^2*s)", UC_NONE}, + /* 206 */ {"ZZ", "St.Dev. of Geopotential", "m^2/s^2", UC_NONE}, + /* 207 */ {"TZ", "Covar Temp & Geopotential", "(K*m^2)/s^2", UC_NONE}, + /* 208 */ {"TT", "St.Dev. of Temperature", "K", UC_NONE}, + /* 209 */ {"QZ", "Covar Sp.Hum. & Geopotential", "m^2/s^2", UC_NONE}, + /* 210 */ {"QT", "Covar Sp.Hum & Temp.", "K", UC_NONE}, + /* 211 */ {"QQ", "St.Dev. of Specific humidity (0 - 1)", "-", UC_NONE}, + /* 212 */ {"UZ", "Covar U-comp. & Geopotential", "m^3/s^3", UC_NONE}, + /* 213 */ {"UT", "Covar U-comp. & Temp.", "(K*m)/s", UC_NONE}, + /* 214 */ {"UQ", "Covar U-comp. & Sp.Hum.", "m/s", UC_NONE}, + /* 215 */ {"UU", "St.Dev. of U-velocity", "m/s", UC_NONE}, + /* 216 */ {"VZ", "Covar V-comp. & Geopotential", "m^3/s^3", UC_NONE}, + /* 217 */ {"VT", "Covar V-comp. & Temp.", "(K*m)/s", UC_NONE}, + /* 218 */ {"VQ", "Covar V-comp. & Sp.Hum.", "m/s", UC_NONE}, + /* 219 */ {"VU", "Covar V-comp. & U-comp", "m^2/s^2", UC_NONE}, + /* 220 */ {"VV", "St.Dev. of V-comp", "m/s", UC_NONE}, + /* 221 */ {"WZ", "Covar W-comp. & Geopotential", "(Pa*m^2)/s^3", UC_NONE}, + /* 222 */ {"WT", "Covar W-comp. & Temp.", "(K*Pa)/s", UC_NONE}, + /* 223 */ {"WQ", "Covar W-comp. & Sp.Hum.", "Pa/s", UC_NONE}, + /* 224 */ {"WU", "Covar W-comp. & U-comp.", "(Pa*m)/s^2", UC_NONE}, + /* 225 */ {"WV", "Covar W-comp. & V-comp.", "(Pa*m)/s^2", UC_NONE}, + /* 226 */ {"WW", "St.Dev. of Vertical velocity", "Pa/s", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"TP", "Total precipitation", "m", UC_NONE}, + /* 229 */ {"IEWS", "Instantaneous X surface stress", "N/m^2", UC_NONE}, + /* 230 */ {"INSS", "Instantaneous Y surface stress", "N/m^2", UC_NONE}, + /* 231 */ {"ISHF", "Instantaneous surface Heat Flux", "W/m^2", UC_NONE}, + /* 232 */ {"IE", "Instantaneous Moisture Flux (evaporation)", "kg/(m^2*s)", + UC_NONE}, + /* 233 */ {"ASQ", "Apparent Surface Humidity", "kg/kg", UC_NONE}, + /* 234 */ {"LSRH", "Logarithm of surface roughness length for heat.", "-", + UC_NONE}, + /* 235 */ {"SKT", "Skin Temperature", "K", UC_NONE}, + /* 236 */ {"STL4", "Soil temperature level 4", "K", UC_NONE}, + /* 237 */ {"SWL4", "Soil wetness level 4", "m", UC_NONE}, + /* 238 */ {"TSN", "Temperature of snow layer", "K", UC_NONE}, + /* 239 */ {"CSF", "Convective snow-fall", "kg/(m^2*s)", UC_NONE}, + /* 240 */ {"LSF", "Large scale snow-fall", "kg/(m^2*s)", UC_NONE}, + /* 241 */ {"CLWC", "Cloud liquid water content", "kg/kg", UC_NONE}, + /* 242 */ {"CC", "Cloud cover (at given level) (0 - 1)", "-", UC_NONE}, + /* 243 */ {"FAL", "Forecast albedo", "-", UC_NONE}, + /* 244 */ {"FSR", "Forecast surface roughness", "m", UC_NONE}, + /* 245 */ {"FLSR", "Forecast logarithm of surface roughness for heat.", + "-", UC_NONE}, + /* 246 */ {"10WS", "10m. Windspeed (irresp of dir.)", "m/s", UC_NONE}, + /* 247 */ {"MOFL", "Momentum flux (irresp of dir.)", "N/m^2", UC_NONE}, + /* 248 */ {"HSD", "Heaviside (beta) function (0 - 1)", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +GRIB1ParmTable parm_table_ecmwf_170[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"var1", "undefined", "-", UC_NONE}, + /* 2 */ {"var2", "undefined", "-", UC_NONE}, + /* 3 */ {"var3", "undefined", "-", UC_NONE}, + /* 4 */ {"var4", "undefined", "-", UC_NONE}, + /* 5 */ {"var5", "undefined", "-", UC_NONE}, + /* 6 */ {"var6", "undefined", "-", UC_NONE}, + /* 7 */ {"var7", "undefined", "-", UC_NONE}, + /* 8 */ {"var8", "undefined", "-", UC_NONE}, + /* 9 */ {"var9", "undefined", "-", UC_NONE}, + /* 10 */ {"var10", "undefined", "-", UC_NONE}, + /* 11 */ {"var11", "undefined", "-", UC_NONE}, + /* 12 */ {"var12", "undefined", "-", UC_NONE}, + /* 13 */ {"var13", "undefined", "-", UC_NONE}, + /* 14 */ {"var14", "undefined", "-", UC_NONE}, + /* 15 */ {"var15", "undefined", "-", UC_NONE}, + /* 16 */ {"var16", "undefined", "-", UC_NONE}, + /* 17 */ {"var17", "undefined", "-", UC_NONE}, + /* 18 */ {"var18", "undefined", "-", UC_NONE}, + /* 19 */ {"var19", "undefined", "-", UC_NONE}, + /* 20 */ {"var20", "undefined", "-", UC_NONE}, + /* 21 */ {"var21", "undefined", "-", UC_NONE}, + /* 22 */ {"var22", "undefined", "-", UC_NONE}, + /* 23 */ {"var23", "undefined", "-", UC_NONE}, + /* 24 */ {"var24", "undefined", "-", UC_NONE}, + /* 25 */ {"var25", "undefined", "-", UC_NONE}, + /* 26 */ {"var26", "undefined", "-", UC_NONE}, + /* 27 */ {"var27", "undefined", "-", UC_NONE}, + /* 28 */ {"var28", "undefined", "-", UC_NONE}, + /* 29 */ {"var29", "undefined", "-", UC_NONE}, + /* 30 */ {"var30", "undefined", "-", UC_NONE}, + /* 31 */ {"var31", "undefined", "-", UC_NONE}, + /* 32 */ {"var32", "undefined", "-", UC_NONE}, + /* 33 */ {"var33", "undefined", "-", UC_NONE}, + /* 34 */ {"var34", "undefined", "-", UC_NONE}, + /* 35 */ {"var35", "undefined", "-", UC_NONE}, + /* 36 */ {"var36", "undefined", "-", UC_NONE}, + /* 37 */ {"var37", "undefined", "-", UC_NONE}, + /* 38 */ {"var38", "undefined", "-", UC_NONE}, + /* 39 */ {"var39", "undefined", "-", UC_NONE}, + /* 40 */ {"var40", "undefined", "-", UC_NONE}, + /* 41 */ {"var41", "undefined", "-", UC_NONE}, + /* 42 */ {"var42", "undefined", "-", UC_NONE}, + /* 43 */ {"var43", "undefined", "-", UC_NONE}, + /* 44 */ {"var44", "undefined", "-", UC_NONE}, + /* 45 */ {"var45", "undefined", "-", UC_NONE}, + /* 46 */ {"var46", "undefined", "-", UC_NONE}, + /* 47 */ {"var47", "undefined", "-", UC_NONE}, + /* 48 */ {"var48", "undefined", "-", UC_NONE}, + /* 49 */ {"var49", "undefined", "-", UC_NONE}, + /* 50 */ {"var50", "undefined", "-", UC_NONE}, + /* 51 */ {"var51", "undefined", "-", UC_NONE}, + /* 52 */ {"var52", "undefined", "-", UC_NONE}, + /* 53 */ {"var53", "undefined", "-", UC_NONE}, + /* 54 */ {"var54", "undefined", "-", UC_NONE}, + /* 55 */ {"var55", "undefined", "-", UC_NONE}, + /* 56 */ {"var56", "undefined", "-", UC_NONE}, + /* 57 */ {"var57", "undefined", "-", UC_NONE}, + /* 58 */ {"var58", "undefined", "-", UC_NONE}, + /* 59 */ {"var59", "undefined", "-", UC_NONE}, + /* 60 */ {"var60", "undefined", "-", UC_NONE}, + /* 61 */ {"var61", "undefined", "-", UC_NONE}, + /* 62 */ {"var62", "undefined", "-", UC_NONE}, + /* 63 */ {"var63", "undefined", "-", UC_NONE}, + /* 64 */ {"var64", "undefined", "-", UC_NONE}, + /* 65 */ {"var65", "undefined", "-", UC_NONE}, + /* 66 */ {"var66", "undefined", "-", UC_NONE}, + /* 67 */ {"var67", "undefined", "-", UC_NONE}, + /* 68 */ {"var68", "undefined", "-", UC_NONE}, + /* 69 */ {"var69", "undefined", "-", UC_NONE}, + /* 70 */ {"var70", "undefined", "-", UC_NONE}, + /* 71 */ {"var71", "undefined", "-", UC_NONE}, + /* 72 */ {"var72", "undefined", "-", UC_NONE}, + /* 73 */ {"var73", "undefined", "-", UC_NONE}, + /* 74 */ {"var74", "undefined", "-", UC_NONE}, + /* 75 */ {"var75", "undefined", "-", UC_NONE}, + /* 76 */ {"var76", "undefined", "-", UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"var80", "undefined", "-", UC_NONE}, + /* 81 */ {"var81", "undefined", "-", UC_NONE}, + /* 82 */ {"var82", "undefined", "-", UC_NONE}, + /* 83 */ {"var83", "undefined", "-", UC_NONE}, + /* 84 */ {"var84", "undefined", "-", UC_NONE}, + /* 85 */ {"var85", "undefined", "-", UC_NONE}, + /* 86 */ {"var86", "undefined", "-", UC_NONE}, + /* 87 */ {"var87", "undefined", "-", UC_NONE}, + /* 88 */ {"var88", "undefined", "-", UC_NONE}, + /* 89 */ {"var89", "undefined", "-", UC_NONE}, + /* 90 */ {"var90", "undefined", "-", UC_NONE}, + /* 91 */ {"var91", "undefined", "-", UC_NONE}, + /* 92 */ {"var92", "undefined", "-", UC_NONE}, + /* 93 */ {"var93", "undefined", "-", UC_NONE}, + /* 94 */ {"var94", "undefined", "-", UC_NONE}, + /* 95 */ {"var95", "undefined", "-", UC_NONE}, + /* 96 */ {"var96", "undefined", "-", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"var99", "undefined", "-", UC_NONE}, + /* 100 */ {"var100", "undefined", "-", UC_NONE}, + /* 101 */ {"var101", "undefined", "-", UC_NONE}, + /* 102 */ {"var102", "undefined", "-", UC_NONE}, + /* 103 */ {"var103", "undefined", "-", UC_NONE}, + /* 104 */ {"var104", "undefined", "-", UC_NONE}, + /* 105 */ {"var105", "undefined", "-", UC_NONE}, + /* 106 */ {"var106", "undefined", "-", UC_NONE}, + /* 107 */ {"var107", "undefined", "-", UC_NONE}, + /* 108 */ {"var108", "undefined", "-", UC_NONE}, + /* 109 */ {"var109", "undefined", "-", UC_NONE}, + /* 110 */ {"var110", "undefined", "-", UC_NONE}, + /* 111 */ {"var111", "undefined", "-", UC_NONE}, + /* 112 */ {"var112", "undefined", "-", UC_NONE}, + /* 113 */ {"var113", "undefined", "-", UC_NONE}, + /* 114 */ {"var114", "undefined", "-", UC_NONE}, + /* 115 */ {"var115", "undefined", "-", UC_NONE}, + /* 116 */ {"var116", "undefined", "-", UC_NONE}, + /* 117 */ {"var117", "undefined", "-", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"var121", "undefined", "-", UC_NONE}, + /* 122 */ {"var122", "undefined", "-", UC_NONE}, + /* 123 */ {"var123", "undefined", "-", UC_NONE}, + /* 124 */ {"var124", "undefined", "-", UC_NONE}, + /* 125 */ {"var125", "undefined", "-", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"var127", "undefined", "-", UC_NONE}, + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"Z", "Geopotential", "m^2/s^2", UC_NONE}, + /* 130 */ {"T", "Temperature", "K", UC_NONE}, + /* 131 */ {"U", "U-velocity", "m/s", UC_NONE}, + /* 132 */ {"V", "V-velocity", "m/s", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"VO", "Vorticity (relative)", "1/s", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"SWL1", "Soil wetness level 1", "m", UC_NONE}, + /* 141 */ {"SD", "Snow depth (of water equivalent)", "m", UC_NONE}, + /* 142 */ {"var142", "undefined", "-", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"var144", "undefined", "-", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"TSW", "Total soil moisture", "m", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"MSL", "Mean sea level pressure", "Pa", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"D", "Divergence", "1/s", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"var165", "undefined", "-", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"SWL2", "Soil wetness level 2", "m", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"TTR", "Top thermal radiation", "W m-2", UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"SWL3", "Soil wetness level 3", "m", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"MX2T", "Max temp at 2m since previous postprocess", "K", + UC_NONE}, + /* 202 */ {"MN2T", "Min temp at 2m since previous postprocess", "K", + UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"TP", "Total precipitation", "m", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +GRIB1ParmTable parm_table_ecmwf_180[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"var1", "undefined", "-", UC_NONE}, + /* 2 */ {"var2", "undefined", "-", UC_NONE}, + /* 3 */ {"var3", "undefined", "-", UC_NONE}, + /* 4 */ {"var4", "undefined", "-", UC_NONE}, + /* 5 */ {"var5", "undefined", "-", UC_NONE}, + /* 6 */ {"var6", "undefined", "-", UC_NONE}, + /* 7 */ {"var7", "undefined", "-", UC_NONE}, + /* 8 */ {"var8", "undefined", "-", UC_NONE}, + /* 9 */ {"var9", "undefined", "-", UC_NONE}, + /* 10 */ {"var10", "undefined", "-", UC_NONE}, + /* 11 */ {"var11", "undefined", "-", UC_NONE}, + /* 12 */ {"var12", "undefined", "-", UC_NONE}, + /* 13 */ {"var13", "undefined", "-", UC_NONE}, + /* 14 */ {"var14", "undefined", "-", UC_NONE}, + /* 15 */ {"var15", "undefined", "-", UC_NONE}, + /* 16 */ {"var16", "undefined", "-", UC_NONE}, + /* 17 */ {"var17", "undefined", "-", UC_NONE}, + /* 18 */ {"var18", "undefined", "-", UC_NONE}, + /* 19 */ {"var19", "undefined", "-", UC_NONE}, + /* 20 */ {"var20", "undefined", "-", UC_NONE}, + /* 21 */ {"var21", "undefined", "-", UC_NONE}, + /* 22 */ {"var22", "undefined", "-", UC_NONE}, + /* 23 */ {"var23", "undefined", "-", UC_NONE}, + /* 24 */ {"var24", "undefined", "-", UC_NONE}, + /* 25 */ {"var25", "undefined", "-", UC_NONE}, + /* 26 */ {"var26", "undefined", "-", UC_NONE}, + /* 27 */ {"var27", "undefined", "-", UC_NONE}, + /* 28 */ {"var28", "undefined", "-", UC_NONE}, + /* 29 */ {"var29", "undefined", "-", UC_NONE}, + /* 30 */ {"var30", "undefined", "-", UC_NONE}, + /* 31 */ {"var31", "undefined", "-", UC_NONE}, + /* 32 */ {"var32", "undefined", "-", UC_NONE}, + /* 33 */ {"var33", "undefined", "-", UC_NONE}, + /* 34 */ {"var34", "undefined", "-", UC_NONE}, + /* 35 */ {"var35", "undefined", "-", UC_NONE}, + /* 36 */ {"var36", "undefined", "-", UC_NONE}, + /* 37 */ {"var37", "undefined", "-", UC_NONE}, + /* 38 */ {"var38", "undefined", "-", UC_NONE}, + /* 39 */ {"var39", "undefined", "-", UC_NONE}, + /* 40 */ {"var40", "undefined", "-", UC_NONE}, + /* 41 */ {"var41", "undefined", "-", UC_NONE}, + /* 42 */ {"var42", "undefined", "-", UC_NONE}, + /* 43 */ {"var43", "undefined", "-", UC_NONE}, + /* 44 */ {"var44", "undefined", "-", UC_NONE}, + /* 45 */ {"var45", "undefined", "-", UC_NONE}, + /* 46 */ {"var46", "undefined", "-", UC_NONE}, + /* 47 */ {"var47", "undefined", "-", UC_NONE}, + /* 48 */ {"var48", "undefined", "-", UC_NONE}, + /* 49 */ {"var49", "undefined", "-", UC_NONE}, + /* 50 */ {"var50", "undefined", "-", UC_NONE}, + /* 51 */ {"var51", "undefined", "-", UC_NONE}, + /* 52 */ {"var52", "undefined", "-", UC_NONE}, + /* 53 */ {"var53", "undefined", "-", UC_NONE}, + /* 54 */ {"var54", "undefined", "-", UC_NONE}, + /* 55 */ {"var55", "undefined", "-", UC_NONE}, + /* 56 */ {"var56", "undefined", "-", UC_NONE}, + /* 57 */ {"var57", "undefined", "-", UC_NONE}, + /* 58 */ {"var58", "undefined", "-", UC_NONE}, + /* 59 */ {"var59", "undefined", "-", UC_NONE}, + /* 60 */ {"var60", "undefined", "-", UC_NONE}, + /* 61 */ {"var61", "undefined", "-", UC_NONE}, + /* 62 */ {"var62", "undefined", "-", UC_NONE}, + /* 63 */ {"var63", "undefined", "-", UC_NONE}, + /* 64 */ {"var64", "undefined", "-", UC_NONE}, + /* 65 */ {"var65", "undefined", "-", UC_NONE}, + /* 66 */ {"var66", "undefined", "-", UC_NONE}, + /* 67 */ {"var67", "undefined", "-", UC_NONE}, + /* 68 */ {"var68", "undefined", "-", UC_NONE}, + /* 69 */ {"var69", "undefined", "-", UC_NONE}, + /* 70 */ {"var70", "undefined", "-", UC_NONE}, + /* 71 */ {"var71", "undefined", "-", UC_NONE}, + /* 72 */ {"var72", "undefined", "-", UC_NONE}, + /* 73 */ {"var73", "undefined", "-", UC_NONE}, + /* 74 */ {"var74", "undefined", "-", UC_NONE}, + /* 75 */ {"var75", "undefined", "-", UC_NONE}, + /* 76 */ {"var76", "undefined", "-", UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"var80", "undefined", "-", UC_NONE}, + /* 81 */ {"var81", "undefined", "-", UC_NONE}, + /* 82 */ {"var82", "undefined", "-", UC_NONE}, + /* 83 */ {"var83", "undefined", "-", UC_NONE}, + /* 84 */ {"var84", "undefined", "-", UC_NONE}, + /* 85 */ {"var85", "undefined", "-", UC_NONE}, + /* 86 */ {"var86", "undefined", "-", UC_NONE}, + /* 87 */ {"var87", "undefined", "-", UC_NONE}, + /* 88 */ {"var88", "undefined", "-", UC_NONE}, + /* 89 */ {"var89", "undefined", "-", UC_NONE}, + /* 90 */ {"var90", "undefined", "-", UC_NONE}, + /* 91 */ {"var91", "undefined", "-", UC_NONE}, + /* 92 */ {"var92", "undefined", "-", UC_NONE}, + /* 93 */ {"var93", "undefined", "-", UC_NONE}, + /* 94 */ {"var94", "undefined", "-", UC_NONE}, + /* 95 */ {"var95", "undefined", "-", UC_NONE}, + /* 96 */ {"var96", "undefined", "-", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"var99", "undefined", "-", UC_NONE}, + /* 100 */ {"var100", "undefined", "-", UC_NONE}, + /* 101 */ {"var101", "undefined", "-", UC_NONE}, + /* 102 */ {"var102", "undefined", "-", UC_NONE}, + /* 103 */ {"var103", "undefined", "-", UC_NONE}, + /* 104 */ {"var104", "undefined", "-", UC_NONE}, + /* 105 */ {"var105", "undefined", "-", UC_NONE}, + /* 106 */ {"var106", "undefined", "-", UC_NONE}, + /* 107 */ {"var107", "undefined", "-", UC_NONE}, + /* 108 */ {"var108", "undefined", "-", UC_NONE}, + /* 109 */ {"var109", "undefined", "-", UC_NONE}, + /* 110 */ {"var110", "undefined", "-", UC_NONE}, + /* 111 */ {"var111", "undefined", "-", UC_NONE}, + /* 112 */ {"var112", "undefined", "-", UC_NONE}, + /* 113 */ {"var113", "undefined", "-", UC_NONE}, + /* 114 */ {"var114", "undefined", "-", UC_NONE}, + /* 115 */ {"var115", "undefined", "-", UC_NONE}, + /* 116 */ {"var116", "undefined", "-", UC_NONE}, + /* 117 */ {"var117", "undefined", "-", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"var121", "undefined", "-", UC_NONE}, + /* 122 */ {"var122", "undefined", "-", UC_NONE}, + /* 123 */ {"var123", "undefined", "-", UC_NONE}, + /* 124 */ {"var124", "undefined", "-", UC_NONE}, + /* 125 */ {"var125", "undefined", "-", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"var127", "undefined", "-", UC_NONE}, + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"Z", "Geopotential (at the surface=orography)", "m^2/s^2", + UC_NONE}, + /* 130 */ {"T", "Temperature", "K", UC_NONE}, + /* 131 */ {"U", "U-velocity", "m/s", UC_NONE}, + /* 132 */ {"V", "V-velocity", "m/s", UC_NONE}, + /* 133 */ {"Q", "Specific humidity", "kg/kg", UC_NONE}, + /* 134 */ {"SP", "Surface pressure", "Pa", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"TCWV", "Total column water vapour", "kg/m^2", UC_NONE}, + /* 138 */ {"VO", "Vorticity (relative)", "1/s", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"SD", "Snow depth (of water equivalent)", "m", UC_NONE}, + /* 142 */ {"LSP", "Large scale precipitation*", "m", UC_NONE}, + /* 143 */ {"CP", "Convective precipitation*", "m", UC_NONE}, + /* 144 */ {"SF", "Snow fall (of water equivalent)", "m", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"SSHF", "Surface sensible heat flux", "(W*s)/m^2", UC_NONE}, + /* 147 */ {"SLHF", "Surface latent heat flux", "(W*s)/m^2", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"TSW", "Total soil wetness", "m", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"MSL", "Mean sea level pressure", "Pa", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"D", "Divergence", "1/s", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"TCC", "Total cloud cover (0 - 1)", "-", UC_NONE}, + /* 165 */ {"10U", "10 metre u wind component", "m/s", UC_NONE}, + /* 166 */ {"10V", "10 metre v wind component", "m/s", UC_NONE}, + /* 167 */ {"2T", "2 metre temperature", "K", UC_NONE}, + /* 168 */ {"2D", "2 metre dewpoint temperature", "K", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"var171", "undefined", "-", UC_NONE}, + /* 172 */ {"LSM", "Land/sea mask (0", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"SSR", "Surface solar radiation (net)", "(J*s)/m^2", UC_NONE}, + /* 177 */ {"STR", "Surface thermal radiation (net)", "(J*s)/m^2", UC_NONE}, + /* 178 */ {"TSR", "Top solar radiation (net)", "(J*s)/m^2", UC_NONE}, + /* 179 */ {"TTR", "Top thermal radiation (net)", "(J*s)/m^2", UC_NONE}, + /* 180 */ {"EWSS", "East/West surface stress", "(N*s)/m^2", UC_NONE}, + /* 181 */ {"NSSS", "North/South surface stress", "(N*s)/m^2", UC_NONE}, + /* 182 */ {"E", "Evaporation (surface) (of water)", "m", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"RO", "Runoff (total)", "m", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +/***************************************************************************** + * DWD Parameter Tables (GRIB1 Section 1 Table 2) + * Table versions 002, 201, 202, 203 + ***************************************************************************** + */ + +/* + * GRIB table 2 at DWD + * Helmut P. Frank, 30.08.2001 + */ + +GRIB1ParmTable parm_table_dwd_002[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PS", "pressure", "Pa", UC_NONE}, + /* 2 */ {"PS_msl", "pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"p-tendency", "pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"var4", "undefined", "-", UC_NONE}, + /* 5 */ {"var5", "undefined", "-", UC_NONE}, + /* 6 */ {"FI", "geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Std dev of height", "m", UC_NONE}, + /* 10 */ {"TO3", "total ozone", "Dobson Units", UC_NONE}, + /* 11 */ {"T", "temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temp.", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temp.", "K", UC_K2F}, + /* 14 */ {"pseudo-pot", "pseudo-adiabatic potential temperature", "K", + UC_K2F}, + /* 15 */ {"TMAX", "maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "minimum temperature", "K", UC_K2F}, + /* 17 */ {"TD", "dew-point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"visibility", "visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta temp */ + /* 25 */ {"TMPA", "Temp. anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "degree true", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"U", "u-component (zonal) of wind", "m/s", UC_NONE}, + /* 34 */ {"V", "v-component (merdional) of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coord. vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"OMEGA", "Pressure vertical velocity", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Geometric vertical velocity", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v of current", "m/s", UC_NONE}, + /* 51 */ {"QV", "specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RELHUM", "relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"TQV", "total precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"TQI", "total cloud ice content", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/(m^2*s)", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"TOT_PREC", "total precipitation", "kg/m^2", UC_NONE}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equiv.", "kg/(m^2*s)", UC_NONE}, + /* 65 */ {"W_SNOW", "water equivalent of accumulated snow depth", "kg/m^2", + UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"CLCT", "total cloud cover", "%", UC_NONE}, + /* 72 */ {"CLC_CON", "convective cloud cover", "%", UC_NONE}, + /* 73 */ {"CLCL", "low cloud cover", "%", UC_NONE}, + /* 74 */ {"CLCM", "medium cloud cover", "%", UC_NONE}, + /* 75 */ {"CLCH", "high cloud cover", "%", UC_NONE}, + /* 76 */ {"TQC", "total cloud water content", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta temp */ + /* 78 */ {"SNOW_CON", "convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOW_GSP", "large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water temp.", "K", UC_K2F}, + /* 81 */ {"FR_LAND", "land cover (1=land, 0=sea)", "1", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"Z0", "surface roughness", "m", UC_NONE}, + /* 84 */ {"ALB_RAD", "albedo", "%", UC_NONE}, + /* 85 */ {"T_soil", "soil temperature", "K", UC_K2F}, + /* 86 */ {"W_soil", "soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"PLCOV", "vegetation (plant cover)", "%", UC_NONE}, + /* 88 */ {"salinity", "salinity", "kg/kg", UC_NONE}, + /* 89 */ {"density", "density", "kg/m^3", UC_NONE}, + /* 90 */ {"RUNOFF", "water run-off", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "ice cover (1=ice, 0=no ice)", "proportion", + UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Sig height of wind waves and swell", "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Sig height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Sig height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"ASOB_S", "net short-wave radiation (surface)", "W/m^2", + UC_NONE}, + /* 112 */ {"ATHB_S", "net long-wave radiation (surface)", "W/m^2", + UC_NONE}, + /* 113 */ {"ASOB_T", "net short-wave radiation (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"ATHB_T", "net long-wave radiation (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation", "W/m^2", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"ALHFL_S", "latent heat flux", "W/m^2", UC_NONE}, + /* 122 */ {"ASHFL_S", "sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"AUMFL_S", "momentum flux, u component", "N/m^2", UC_NONE}, + /* 125 */ {"AVMFL_S", "momentum flux, v component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"HGT_ECMF", "Geopotential height (ECMF)", "gpm", UC_NONE}, + /* 130 */ {"TMP_ECMF", "Temp. (ECMF)", "K", UC_K2F}, + /* 131 */ {"UGRD_ECMF", "u wind (ECMF)", "m/s", UC_NONE}, + /* 132 */ {"VGRD_ECMF", "v wind (ECMF)", "m/s", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"TSOIL_ECMF", "Soil temp. (ECMF)", "K", UC_K2F}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"var141", "undefined", "-", UC_NONE}, + /* 142 */ {"NCPCP_ECMF", "Large scale precipitation (ECMF)", "kg/m^2", + UC_NONE}, + /* 143 */ {"ACPCP_ECMF", "Convective precipitation (ECMF)", "kg/m^2", + UC_NONE}, + /* 144 */ {"snowfall", "snowfall (ECMF)", "m of water equivalent", + UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"pressure", "pressure reduced to MSL (ECMF)", "Pa", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"var155", "undefined", "-", UC_NONE}, + /* 156 */ {"HGT_ECMF", "Geopotential height (ECMF)", "gpm", UC_NONE}, + /* 157 */ {"RH_ECMF", "Relative humidity (ECMF)", "%", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"TCDC_ECMF", "Total cloud cover (ECMF)", "%", UC_NONE}, + /* 165 */ {"UGRD_10mECMF", "u of 10m-wind (ECMF)", "m/s", UC_NONE}, + /* 166 */ {"VGRD_10mECMF", "v of 10m-wind (ECMF)", "m/s", UC_NONE}, + /* 167 */ {"TMP_2mECMF", "2m temperature (ECMF)", "K", UC_K2F}, + /* 168 */ {"DPT_2mECMF", "2m due-point temperature (ECMF)", "K", UC_K2F}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"var171", "undefined", "-", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"APCP_ECMF", "Total precipitation (ECMF)", "m", UC_NONE}, + /* 229 */ {"seaway_01", "seaway 01 (ECMF)", "", UC_NONE}, + /* 230 */ {"seaway_02", "seaway 02 (ECMF)", "", UC_NONE}, + /* 231 */ {"seaway_03", "seaway 03 (ECMF)", "", UC_NONE}, + /* 232 */ {"seaway_04", "seaway 04 (ECMF)", "", UC_NONE}, + /* 233 */ {"seaway_05", "seaway 05 (ECMF)", "", UC_NONE}, + /* 234 */ {"seaway_06", "seaway 06 (ECMF)", "", UC_NONE}, + /* 235 */ {"seaway_07", "seaway 07 (ECMF)", "", UC_NONE}, + /* 236 */ {"seaway_08", "seaway 08 (ECMF)", "", UC_NONE}, + /* 237 */ {"seaway_09", "seaway 09 (ECMF)", "", UC_NONE}, + /* 238 */ {"seaway_10", "seaway 10 (ECMF)", "", UC_NONE}, + /* 239 */ {"seaway_11", "seaway 11 (ECMF)", "", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +/* + * GRIB table 201 at DWD + * Helmut P. Frank, 30.08.2001 + */ + +GRIB1ParmTable parm_table_dwd_201[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"DSWRF", "Downward shortwave radiation flux", "W/m^2", UC_NONE}, + /* 2 */ {"USWRF", "Upward short wave radiation flux", "W/m^2", UC_NONE}, + /* 3 */ {"DLWRF", "Downward longwave radiation flux", "W/m^2", UC_NONE}, + /* 4 */ {"ULWRF", "Upward long wave radiation flux", "W/m^2", UC_NONE}, + /* 5 */ {"APAB_S", "downwd photosynthetic active radiant flux density", + "W/m^2", UC_NONE}, + /* 6 */ {"NSWRF", "net shortwave flux", "W/m^2", UC_NONE}, + /* 7 */ {"NLWRF", "net longwave flux", "W/m^2", UC_NONE}, + /* 8 */ {"TNRF", "total net radiative flux density", "W/m^2", UC_NONE}, + /* 9 */ {"DSWRF_CLFR", "downw shortw radiant flux density, cloudfree part", + "W/m^2", UC_NONE}, + /* 10 */ {"USWRF_CLDY", "upw shortw radiant flux density, cloudy part", + "W/m^2", UC_NONE}, + /* 11 */ {"DLWRF_CLFR", "downw longw radiant flux density, cloudfree part", + "W/m^2", UC_NONE}, + /* 12 */ {"ULWRF_CLDY", "upw longw radiant flux density, cloudy part", + "W/m^2", UC_NONE}, + /* 13 */ {"SOHR_RAD", "shortwave radiative heating rate", "K/s", UC_NONE}, + /* 14 */ {"THHR_RAD", "longwave radiative heating rate", "K/s", UC_NONE}, + /* 15 */ {"TOT_RAD", "total radiative heating rate", "K/s", UC_NONE}, + /* 16 */ {"soilheat_SFC", "soil heat flux, surface", "W/m^2", UC_NONE}, + /* 17 */ {"soilheat_LAY", "soil heat flux, bottom of layer", "W/m^2", + UC_NONE}, + /* 18 */ {"var18", "undefined", "-", UC_NONE}, + /* 19 */ {"var19", "undefined", "-", UC_NONE}, + /* 20 */ {"var20", "undefined", "-", UC_NONE}, + /* 21 */ {"var21", "undefined", "-", UC_NONE}, + /* 22 */ {"var22", "undefined", "-", UC_NONE}, + /* 23 */ {"var23", "undefined", "-", UC_NONE}, + /* 24 */ {"var24", "undefined", "-", UC_NONE}, + /* 25 */ {"var25", "undefined", "-", UC_NONE}, + /* 26 */ {"var26", "undefined", "-", UC_NONE}, + /* 27 */ {"var27", "undefined", "-", UC_NONE}, + /* 28 */ {"var28", "undefined", "-", UC_NONE}, + /* 29 */ {"CLC", "cloud cover, grid scale + convective", "1", UC_NONE}, + /* 30 */ {"CLC_GS", "cloud cover, grid scale (0...1)", "1", UC_NONE}, + /* 31 */ {"QC", "specific cloud water content, grid scale", "kg/kg", + UC_NONE}, + /* 32 */ {"CLW_GS_VI", "cloud water content, grid scale, vert integrated", + "kg/m^2", UC_NONE}, + /* 33 */ {"QI", "specific cloud ice content, grid scale", "kg/kg", + UC_NONE}, + /* 34 */ {"CLI_GS_VI", "cloud ice content, grid scale, vert integrated", + "kg/m^2", UC_NONE}, + /* 35 */ {"SRC_GS", "specific rainwater content, grid scale", "kg/kg", + UC_NONE}, + /* 36 */ {"SSC_GS", "specific snow content, grid scale", "kg/kg", UC_NONE}, + /* 37 */ {"SRC_GS_VI", "specific rainwater content, gs, vert. integrated", + "kg/m^2", UC_NONE}, + /* 38 */ {"SSC_GS_VI", "specific snow content, gs, vert. integrated", + "kg/m^2", UC_NONE}, + /* 39 */ {"var39", "undefined", "-", UC_NONE}, + /* 40 */ {"var40", "undefined", "-", UC_NONE}, + /* 41 */ {"TOT_WATER", "vert. integral of humidity, cloud water (and ice)", + "kg/m^2", UC_NONE}, + /* 42 */ {"HUM_DIV", "vert. integral of divergence of tot. water content", + "kg/m^2", UC_NONE}, + /* 43 */ {"var43", "undefined", "-", UC_NONE}, + /* 44 */ {"var44", "undefined", "-", UC_NONE}, + /* 45 */ {"var45", "undefined", "-", UC_NONE}, + /* 46 */ {"var46", "undefined", "-", UC_NONE}, + /* 47 */ {"var47", "undefined", "-", UC_NONE}, + /* 48 */ {"var48", "undefined", "-", UC_NONE}, + /* 49 */ {"var49", "undefined", "-", UC_NONE}, + /* 50 */ {"CH_CM_CL", "cloud covers CH_CM_CL (000...888)", "1", UC_NONE}, + /* 51 */ {"CL_COV_CH", "cloud cover CH (0..8)", "1", UC_NONE}, + /* 52 */ {"CL_COV_CM", "cloud cover CM (0..8)", "1", UC_NONE}, + /* 53 */ {"CL_COV_CL", "cloud cover CL (0..8)", "1", UC_NONE}, + /* 54 */ {"TOT_CL_COV", "total cloud cover (0..8)", "1", UC_NONE}, + /* 55 */ {"fog", "fog (0..8)", "1", UC_NONE}, + /* 56 */ {"fog", "fog", "1", UC_NONE}, + /* 57 */ {"var57", "undefined", "-", UC_NONE}, + /* 58 */ {"var58", "undefined", "-", UC_NONE}, + /* 59 */ {"var59", "undefined", "-", UC_NONE}, + /* 60 */ {"CLC_CON_CI", "cloud cover, convective cirrus (0...1)", "1", + UC_NONE}, + /* 61 */ {"CLW_CON", "specific cloud water content, convective clouds", + "kg/kg", UC_NONE}, + /* 62 */ {"CLW_CON_VI", + "cloud water content, conv clouds, vert integrated", "kg/m^2", + UC_NONE}, + /* 63 */ {"CLI_CON", "specific cloud ice content, convective clouds", + "kg/kg", UC_NONE}, + /* 64 */ {"CLI_CON_VI", "cloud ice content, conv clouds, vert integrated", + "kg/m^2", UC_NONE}, + /* 65 */ {"MASS_FL_CO", "convective mass flux", "kg/(s*m^2)", UC_NONE}, + /* 66 */ {"UPD_VEL_CO", "updraft velocity, convection", "m/s", UC_NONE}, + /* 67 */ {"ENTR_P_CO", "entrainment parameter, convection", "1/m", + UC_NONE}, + /* 68 */ {"HBAS_CON", "cloud base, convective clouds (above msl)", "m", + UC_NONE}, + /* 69 */ {"HTOP_CON", "cloud top, convective clouds (above msl)", "m", + UC_NONE}, + /* 70 */ {"CON_LAYERS", "convective layers (00...77) (BKE)", "1", + UC_NONE}, + /* 71 */ {"KO-index", "KO-index", "1", UC_NONE}, + /* 72 */ {"BAS_CON", "convection base index", "1", UC_NONE}, + /* 73 */ {"TOP_CON", "convection top index", "1", UC_NONE}, + /* 74 */ {"DT_CON", "convective temperature tendency", "K/s", UC_NONE}, + /* 75 */ {"DQV_CON", "convective tendency of specific humidity", "1/s", + UC_NONE}, + /* 76 */ {"H_TEN_CO", "convective tendency of total heat", "J/(kg*s)", + UC_NONE}, + /* 77 */ {"QDW_TEN_CO", "convective tendency of total water", "1/s", + UC_NONE}, + /* 78 */ {"DU_CON", "convective momentum tendency (X-component)", "m/s^2", + UC_NONE}, + /* 79 */ {"DV_CON", "convective momentum tendency (Y-component)", "m/s^2", + UC_NONE}, + /* 80 */ {"VOR_TEN_CO", "convective vorticity tendency", "1/s^2", UC_NONE}, + /* 81 */ {"DIV_TEN_CO", "convective divergence tendency", "1/s^2", + UC_NONE}, + /* 82 */ {"HTOP_DC", "top of dry convection (above msl)", "m", UC_NONE}, + /* 83 */ {"TOP_IND_DC", "dry convection top index", "1", UC_NONE}, + /* 84 */ {"HZEROCL", "height of 0 degree Celsius isotherm above msl", "m", + UC_NONE}, + /* 85 */ {"var85", "undefined", "-", UC_NONE}, + /* 86 */ {"var86", "undefined", "-", UC_NONE}, + /* 87 */ {"var87", "undefined", "-", UC_NONE}, + /* 88 */ {"var88", "undefined", "-", UC_NONE}, + /* 89 */ {"var89", "undefined", "-", UC_NONE}, + /* 90 */ {"var90", "undefined", "-", UC_NONE}, + /* 91 */ {"var91", "undefined", "-", UC_NONE}, + /* 92 */ {"var92", "undefined", "-", UC_NONE}, + /* 93 */ {"var93", "undefined", "-", UC_NONE}, + /* 94 */ {"var94", "undefined", "-", UC_NONE}, + /* 95 */ {"var95", "undefined", "-", UC_NONE}, + /* 96 */ {"var96", "undefined", "-", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"QRS_GSP", "undefined", "-", UC_NONE}, + /* 100 */ {"PRR_GSP", "surface precipitation rate, rain, grid scale", + "kg/(s*m^2)", UC_NONE}, + /* 101 */ {"PRS_GSP", "surface precipitation rate, snow, grid scale", + "kg/(s*m^2)", UC_NONE}, + /* 102 */ {"RAIN_GSP", "surface precipitation amount, rain, grid scale", + "kg/m^2", UC_NONE}, + /* 103 */ {"CONDENS_GS", "condensation rate, grid scale", "kg/(kg*s)", + UC_NONE}, + /* 104 */ {"AUTOCON_GS", "autoconversion rate, grid scale (C+C --> R)", + "kg/(kg*s)", UC_NONE}, + /* 105 */ {"ACCRET_GS", "accretion rate, grid scale (R+C --> R)", + "kg/(kg*s)", UC_NONE}, + /* 106 */ {"NUCLEAT_GS", "nucleation rate, grid scale (C+C --> S)", + "kg/(kg*s)", UC_NONE}, + /* 107 */ {"RIMING_GS", "riming rate, grid scale (S+C --> S)", "kg/(kg*s)", + UC_NONE}, + /* 108 */ {"DEPOSIT_GS", "deposition rate, grid scale (S+V <--> S)", + "kg/(kg*s)", UC_NONE}, + /* 109 */ {"MELTING_GS", "melting rate, grid scale (S --> R)", "kg/(kg*s)", + UC_NONE}, + /* 110 */ {"EVAPOR_GS", "evaporation rate, grid scale (R+V <-- R)", + "kg/(kg*s)", UC_NONE}, + /* 111 */ {"PRR_CON", "surface precipitation rate, rain, convective", + "kg/(s*m^2)", UC_NONE}, + /* 112 */ {"PRS_CON", "surface precipitation rate, snow, convective", + "kg/(s*m^2)", UC_NONE}, + /* 113 */ {"RAIN_CON", "surface precipitation amount, rain, convective", + "kg/m^2", UC_NONE}, + /* 114 */ {"CONDENS_CO", "condensation rate, convective", "kg/(kg*s)", + UC_NONE}, + /* 115 */ {"AUTOCON_CO", "autoconversion rate, convective", "kg/(kg*s)", + UC_NONE}, + /* 116 */ {"ACCRET_CO", "accretion rate, convective", "kg/(kg*s)", + UC_NONE}, + /* 117 */ {"NUCLEAT_CO", "nucleation rate, convective", "kg/(kg*s)", + UC_NONE}, + /* 118 */ {"RIMING_CO", "riming rate, convective", "kg/(kg*s)", UC_NONE}, + /* 119 */ {"SUBLIM_CO", "sublimation rate, convective", "kg/(kg*s)", + UC_NONE}, + /* 120 */ {"MELTING_CO", "melting rate, convective", "kg/(kg*s)", UC_NONE}, + /* 121 */ {"EVAPOR_CO", "evaporation rate, convective", "kg/(kg*s)", + UC_NONE}, + /* 122 */ {"RAIN_AM", "rain amount, grid-scale plus convective", "kg/m^2", + UC_NONE}, + /* 123 */ {"SNOW_AM", "snow amount, grid-scale plus convective", "kg/m^2", + UC_NONE}, + /* 124 */ {"DT_GSP", "temperature tendency, grid-scale condensation", + "K/s", UC_NONE}, + /* 125 */ {"DQV_GSP", "tendency of specific humidity, grid-scale condens", + "1/s", UC_NONE}, + /* 126 */ {"H_TEN_GS", "tendency of total heat, grid-scale condensation", + "J/(kg*s)", UC_NONE}, + /* 127 */ {"DQL_GSP", "tendency of total water, grid-scale condensation", + "1/s", UC_NONE}, + /* 128 */ {"snowfall", "snowfall (dimension", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"var130", "undefined", "-", UC_NONE}, + /* 131 */ {"var131", "undefined", "-", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"pprime", "deviation of pressure from reference value", "Pa", + UC_NONE}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"var141", "undefined", "-", UC_NONE}, + /* 142 */ {"var142", "undefined", "-", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"var144", "undefined", "-", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"HDI_COEFF", "coefficient of horizontal diffusion", "m^2/s", + UC_NONE}, + /* 151 */ {"DISSP_RATE", "dissipation rate", "W/(Pa*m^2)", UC_NONE}, + /* 152 */ {"TKE", "turbulent kinetic energy", "(m/s)^2", UC_NONE}, + /* 153 */ {"TKVM", "coefficient of vertical diffusion, momentum", "m^2/s", + UC_NONE}, + /* 154 */ {"TKVH", "coefficient of vertical diffusion, heat", "m^2/s", + UC_NONE}, + /* 155 */ {"VDI_COE_CW", "coefficient of vertical diffusion, cloud water", + "m^2/s", UC_NONE}, + /* 156 */ {"VDI_COE_CI", "coefficient of vertical diffusion, cloud ice", + "m^2/s", UC_NONE}, + /* 157 */ {"VDI_COE_VP", "coefficient of vertical diffusion, water vapour", + "m^2/s", UC_NONE}, + /* 158 */ {"DIS_LEN_M", "turbulent dissipation length for momentum", "m", + UC_NONE}, + /* 159 */ {"DIS_LEN_H", "turbulent dissipation length for heat", "m", + UC_NONE}, + /* 160 */ {"VAR_U_MOM", "variance of u-component of momentum", "(m/s)^2", + UC_NONE}, + /* 161 */ {"VAR_V_MOM", "variance of v-component of momentum", "(m/s)^2", + UC_NONE}, + /* 162 */ {"VAR_W_MOM", "variance of w-component of momentum", "(m/s)^2", + UC_NONE}, + /* 163 */ {"VAR_TEMP", "variance of temperature", "K^2", UC_NONE}, + /* 164 */ {"VAR_CL_WAT", "variance of specific cloud water content", + "(kg/kg)^2", UC_NONE}, + /* 165 */ {"VAR_CL_ICE", "variance of specific cloud ice content", + "(kg/kg)^2", UC_NONE}, + /* 166 */ {"VAR_VAP_MR", "variance of water vapour mixing ratio", + "(kg/kg)^2", UC_NONE}, + /* 167 */ {"C_WAT_FLUX", "turbulent vertical flux of spec cloud water", + "m/s", UC_NONE}, + /* 168 */ {"C_ICE_FLUX", "turbulent vertical flux of spec cloud ice", + "m/s", UC_NONE}, + /* 169 */ {"W_VAP_FLUX", + "turbulent vertical flux of water vapour mix ratio", "m/s", + UC_NONE}, + /* 170 */ {"TCM", "drag coefficient CD", "1", UC_NONE}, + /* 171 */ {"TCH", "transfer coefficient CH (sensible heat)", "1", UC_NONE}, + /* 172 */ {"TR_COEF_CQ", "transfer coefficient CQ (latent heat)", "1", + UC_NONE}, + /* 173 */ {"PBL_TOP_H", "PBL-top h", "m", UC_NONE}, + /* 174 */ {"T_JUMP_H", "temperature jump at PBL-top", "K", UC_NONE}, + /* Delta temp */ + /* 175 */ {"Q_JUMP_H", "specific humidity jump at PBL-top", "kg/kg", + UC_NONE}, + /* 176 */ {"ENTR_AT_H", "entrainment at PBL-top", "kg/(s*m^2)", UC_NONE}, + /* 177 */ {"MASS_FL_H", "upward mass flux at PBL-top", "kg/(s*m^2)", + UC_NONE}, + /* 178 */ {"CL_COV_PBL", "cloud cover of PBL-clouds (0...1)", "1", + UC_NONE}, + /* 179 */ {"CL_WAT_PBL", "specific cloud water content of PBL-clouds", + "kg/kg", UC_NONE}, + /* 180 */ {"CL_TOP_PBL", "cloud top of PBL-clouds", "m", UC_NONE}, + /* 181 */ {"CL_BAS_PBL", "cloud base of PBL-clouds", "m", UC_NONE}, + /* 182 */ {"MOUN_WAV_X", + "vertical mountain wave momentum flux (X component)", + "kg/(m*s^2)", UC_NONE}, + /* 183 */ {"MOUN_WAV_Y", + "vertical mountain wave momentum flux (Y component)", + "kg/(m*s^2)", UC_NONE}, + /* 184 */ {"WAVE_RI", "wave Richardson number", "1", UC_NONE}, + /* 185 */ {"WAV_DIV_X", "mountain wave momentum flux divergence (X comp)", + "m/s^2", UC_NONE}, + /* 186 */ {"WAV_DIV_Y", "mountain wave momentum flux divergence (Y comp)", + "m/s^2", UC_NONE}, + /* 187 */ {"VMAX_10M", "maximum wind velocity", "m/s", UC_NONE}, + /* 188 */ {"WAV_DIS_VI", "mountain wave dissipation, vert integrated", + "W/m^2", UC_NONE}, + /* 189 */ {"WV_EN_FLUX", "vertical wave energy flux", "kg*m/s^4", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"so_temp", "temperature of soil layers", "K", UC_K2F}, + /* 198 */ {"so_wa_ice", "water + ice content of soil layers", "kg/m^2", + UC_NONE}, + /* 199 */ {"so_ice", "ice content of soil layers", "kg/m^2", UC_NONE}, + /* 200 */ {"W_I", "water content of interception store", "kg/m^2", + UC_NONE}, + /* 201 */ {"INTERC_ICE", "icebit for interception store", "1", UC_NONE}, + /* 202 */ {"SNOW_FRACT", "snow fraction", "1", UC_NONE}, + /* 203 */ {"T_SNOW", "snow temperature", "K", UC_K2F}, + /* 204 */ {"FOLIAG_TMP", "foliage temperature", "K", UC_K2F}, + /* 205 */ {"infiltrat", "infiltration", "m/s", UC_NONE}, + /* 206 */ {"runoff", "runoff", "m/s", UC_NONE}, + /* 207 */ {"SOIL_EVAP", "bare soil evaporation", "m/s", UC_NONE}, + /* 208 */ {"PLANT_TRANS", "plant transpiration", "m/s", UC_NONE}, + /* 209 */ {"INTER_EVAP", "interception store evaporation", "m/s", UC_NONE}, + /* 210 */ {"WATER_EVAP", "evaporation from water surfaces", "m/s", + UC_NONE}, + /* 211 */ {"AERO_RESIS", "aerodynamic resistance", "s/m", UC_NONE}, + /* 212 */ {"PLANT_RES", "plant resistance", "s/m", UC_NONE}, + /* 213 */ {"SOIL_RES", "soil resistance", "s/m", UC_NONE}, + /* 214 */ {"TOT_EVAP", "total evaporation (water, soil, plants)", "m/s", + UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"XYZ", "S1", "1", UC_NONE}, + /* 231 */ {"S2", "S2", "1", UC_NONE}, + /* 232 */ {"S3", "S3", "1", UC_NONE}, + /* 233 */ {"S4", "S4", "1", UC_NONE}, + /* 234 */ {"S5", "S5", "1", UC_NONE}, + /* 235 */ {"S6", "S6", "1", UC_NONE}, + /* 236 */ {"S7", "S7", "1", UC_NONE}, + /* 237 */ {"S8", "S8", "1", UC_NONE}, + /* 238 */ {"S9", "S9", "1", UC_NONE}, + /* 239 */ {"S10", "S10", "1", UC_NONE}, + /* 240 */ {"S11", "S11", "1", UC_NONE}, + /* 241 */ {"OBS_TS_OC", "OBS Gewitter (occasional)", "1", UC_NONE}, + /* 242 */ {"OBS_TS_FQ", "OBS Gewitter (frequent)", "1", UC_NONE}, + /* 243 */ {"MOS_PTS_OC", "MOS Gewitter-Wahrscheinlichkeit (occasional)", + "1", UC_NONE}, + /* 244 */ {"MOS_PTS_FQ", "MOS Gewitter-Wahrscheinlichkeit (frequent)", "1", + UC_NONE}, + /* 245 */ {"MOS_TS_COV", + "MOS Gewitteranteil (occasional - frequent (1 - 2))", "1", + UC_NONE}, + /* 246 */ {"S17", "S17", "1", UC_NONE}, + /* 247 */ {"S18", "S18", "1", UC_NONE}, + /* 248 */ {"S19", "S19", "1", UC_NONE}, + /* 249 */ {"S20", "S20", "1", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +/* + * GRIB table 202 at DWD + * Helmut P. Frank, 30.08.2001 + */ + +GRIB1ParmTable parm_table_dwd_202[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"Seeg_peak", "jonswap parameter fm", "1/s", UC_NONE}, + /* 2 */ {"Seeg_alpha", "jonswap parameter alpha", "1", UC_NONE}, + /* 3 */ {"Seeg_gamma", "jonswap parameter gamma", "1", UC_NONE}, + /* 4 */ {"Seeg_dir", "Seegang direction", "degree true", UC_NONE}, + /* 5 */ {"Seeg_energ", "Seegang energy densitiy", "(m^2)(s^2)", UC_NONE}, + /* 6 */ {"Seeg_icemk", "Seegang ice mask", "1", UC_NONE}, + /* 7 */ {"peak_p_sw", "peak period of swell", "s", UC_NONE}, + /* 8 */ {"peak_p_ww", "peak period of wind waves", "s", UC_NONE}, + /* 9 */ {"var9", "undefined", "-", UC_NONE}, + /* 10 */ {"var10", "undefined", "-", UC_NONE}, + /* 11 */ {"var11", "undefined", "-", UC_NONE}, + /* 12 */ {"var12", "undefined", "-", UC_NONE}, + /* 13 */ {"var13", "undefined", "-", UC_NONE}, + /* 14 */ {"var14", "undefined", "-", UC_NONE}, + /* 15 */ {"var15", "undefined", "-", UC_NONE}, + /* 16 */ {"var16", "undefined", "-", UC_NONE}, + /* 17 */ {"var17", "undefined", "-", UC_NONE}, + /* 18 */ {"var18", "undefined", "-", UC_NONE}, + /* 19 */ {"var19", "undefined", "-", UC_NONE}, + /* 20 */ {"Var_Geop", "Varianz Geopotential", "(m/s)^4", UC_NONE}, + /* 21 */ {"Var_T", "Varianz Temperatur", "K^2", UC_NONE}, + /* 22 */ {"Var_u", "Varianz Zonalwind", "(m/s)^2", UC_NONE}, + /* 23 */ {"Var_v", "Varianz Meridionalwind", "(m/s)^2", UC_NONE}, + /* 24 */ {"Var_q", "Varianz spezifische Feuchte", "(kg/kg)^2", UC_NONE}, + /* 25 */ {"Mer_Imptr", "Meridionaler Impulstransport", "(m/s)^2", UC_NONE}, + /* 26 */ {"Mer_TrEpt", "Meridionaler Transport potentieller Energie", + "(m/s)^3", UC_NONE}, + /* 27 */ {"Mer_TrsW", "Meridionaler Transport sensibler Waerme", "K*(m/s)", + UC_NONE}, + /* 28 */ {"Mer_TrlW", "Meridionaler Transport latenter Waerme", + "(kg/kg)*(m/s)", UC_NONE}, + /* 29 */ {"Ver_TrEpt", "Vertikaler Transport potentieller Energie", + "(m/s)^2*(Pa/s)", UC_NONE}, + /* 30 */ {"Ver_TrsW", "Vertikaler Transport sensibler Waerme", "K*(Pa/s)", + UC_NONE}, + /* 31 */ {"Ver_TrlW", "Vertikaler Transport latenter Waerme", + "(kg/kg)*(Pa/s)", UC_NONE}, + /* 32 */ {"var32", "undefined", "-", UC_NONE}, + /* 33 */ {"var33", "undefined", "-", UC_NONE}, + /* 34 */ {"var34", "undefined", "-", UC_NONE}, + /* 35 */ {"var35", "undefined", "-", UC_NONE}, + /* 36 */ {"var36", "undefined", "-", UC_NONE}, + /* 37 */ {"var37", "undefined", "-", UC_NONE}, + /* 38 */ {"var38", "undefined", "-", UC_NONE}, + /* 39 */ {"var39", "undefined", "-", UC_NONE}, + /* 40 */ {"VarAF_Geop", "Varianz des Analyse-Fehlers Geopotential", + "(m/s)^4", UC_NONE}, + /* 41 */ {"VarAF_u", "Varianz des Analyse-Fehlers Zonalwind", "(m/s)^2", + UC_NONE}, + /* 42 */ {"VarAF_v", "Varianz des Analyse-Fehlers Meridionalwind", + "(m/s)^2", UC_NONE}, + /* 43 */ {"var43", "undefined", "-", UC_NONE}, + /* 44 */ {"DU_SSO", "undefined", "-", UC_NONE}, + /* 45 */ {"DV_SSO", "undefined", "-", UC_NONE}, + /* 46 */ {"SSO_STDH", "standard deviation of subgrid scale orogr. height", + "m", UC_NONE}, + /* 47 */ {"SSO_GAMMA", "anisotropy of topography", "1", UC_NONE}, + /* 48 */ {"SSO_THETA", "angle betw. principal axis of orogr. and global E", + "1", UC_NONE}, + /* 49 */ {"SSO_SIGMA", "mean slope of subgrid scale orography", "1", + UC_NONE}, + /* 50 */ {"oro_varian", "subgrid-scale variance of orography", "m^2", + UC_NONE}, + /* 51 */ {"E-W_oro_va", "E-W component of subgrid-scale variance of orogr", + "m^2", UC_NONE}, + /* 52 */ {"N-S_oro_va", "N-S component of subgrid-scale variance of orogr", + "m^2", UC_NONE}, + /* 53 */ {"NW-SE_o_va", + "NW-SE component of subgrid-scale variance of orogr", "m^2", + UC_NONE}, + /* 54 */ {"NE-SW_o_va", + "NE-SW component of subgrid-scale variance of orogr", "m^2", + UC_NONE}, + /* 55 */ {"inl_w_frac", "fraction of inland water", "1", UC_NONE}, + /* 56 */ {"surf_emiss", "surface emissivity", "1", UC_NONE}, + /* 57 */ {"SOILTYP", "soil texture", "1", UC_NONE}, + /* 58 */ {"soil_color", "soil color", "1", UC_NONE}, + /* 59 */ {"soil_drain", "soil drainage", "1", UC_NONE}, + /* 60 */ {"ground_wat", "ground water table", "m", UC_NONE}, + /* 61 */ {"LAI", "leaf area index", "1", UC_NONE}, + /* 62 */ {"ROOT", "root depth", "m", UC_NONE}, + /* 63 */ {"root_dens", "root density", "1", UC_NONE}, + /* 64 */ {"HMO3", "height of maximum of ozone concentration", "Pa", + UC_NONE}, + /* 65 */ {"VIO3", "total vertically integrated ozone content", "Pa", + UC_NONE}, + /* 66 */ {"ld-sea_msk", "land-sea mask", "1", UC_NONE}, + /* 67 */ {"PLCOV_MX", "ground fraction covered by plants (vegetation p.)", + "1", UC_NONE}, + /* 68 */ {"PLCOV_MN", "ground fraction covered by plants (time of rest)", + "1", UC_NONE}, + /* 69 */ {"LAI_MX", "leaf area index (vegetation period)", "1", UC_NONE}, + /* 70 */ {"LAI_MN", "leaf area index (time of rest)", "1", UC_NONE}, + /* 71 */ {"Orographie", "Orographie + Land-Meer-Verteilung", "m", UC_NONE}, + /* 72 */ {"r_length_m", "roughness length momentum", "m", UC_NONE}, + /* 73 */ {"r_length_h", "roughness length heat", "m", UC_NONE}, + /* 74 */ {"var_smc", "variance of soil moisture content", "kg^2/m^4", + UC_NONE}, + /* 75 */ {"FOR_E", "fractional coverage with evergreen forest", "1", + UC_NONE}, + /* 76 */ {"FOR_D", "fractional coverage with deciduous forest", "1", + UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"var80", "undefined", "-", UC_NONE}, + /* 81 */ {"var81", "undefined", "-", UC_NONE}, + /* 82 */ {"var82", "undefined", "-", UC_NONE}, + /* 83 */ {"var83", "undefined", "-", UC_NONE}, + /* 84 */ {"var84", "undefined", "-", UC_NONE}, + /* 85 */ {"var85", "undefined", "-", UC_NONE}, + /* 86 */ {"var86", "undefined", "-", UC_NONE}, + /* 87 */ {"var87", "undefined", "-", UC_NONE}, + /* 88 */ {"var88", "undefined", "-", UC_NONE}, + /* 89 */ {"var89", "undefined", "-", UC_NONE}, + /* 90 */ {"var90", "undefined", "-", UC_NONE}, + /* 91 */ {"var91", "undefined", "-", UC_NONE}, + /* 92 */ {"var92", "undefined", "-", UC_NONE}, + /* 93 */ {"var93", "undefined", "-", UC_NONE}, + /* 94 */ {"var94", "undefined", "-", UC_NONE}, + /* 95 */ {"var95", "undefined", "-", UC_NONE}, + /* 96 */ {"var96", "undefined", "-", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"AER_DES", "undefined", "-", UC_NONE}, + /* 100 */ {"var100", "undefined", "-", UC_NONE}, + /* 101 */ {"tidal_tend", "tidal tendencies", "(m/s)^2", UC_NONE}, + /* 102 */ {"diab_heatg", "sum of diabatic heating terms", "K/s", UC_NONE}, + /* 103 */ {"adiab_heat", "total adiabatic heating", "K/s", UC_NONE}, + /* 104 */ {"adv_q_tend", "advective tendency of specific humidity", "1/s", + UC_NONE}, + /* 105 */ {"nadv_q_ten", "non-advective tendency of specific humidity", + "1/s", UC_NONE}, + /* 106 */ {"adv_m_te_X", "advective momentum tendency (X component)", + "m/s^2", UC_NONE}, + /* 107 */ {"adv_m_te_Y", "advective momentum tendency (Y component)", + "m/s^2", UC_NONE}, + /* 108 */ {"nad_m_te_X", "non-advective momentum tendency (X component)", + "m/s^2", UC_NONE}, + /* 109 */ {"nad_m_te_Y", "non-advective momentum tendency (Y component)", + "m/s^2", UC_NONE}, + /* 110 */ {"torque", "sum of mountain and frictional torque", "kg*(m/s)^2", + UC_NONE}, + /* 111 */ {"budget_val", "budget values", "1", UC_NONE}, + /* 112 */ {"scale_fact", "scale factor", "1", UC_NONE}, + /* 113 */ {"Coriol_par", "Coriolis parameter", "1/s", UC_NONE}, + /* 114 */ {"PHI", "latitude", "degr N", UC_NONE}, + /* 115 */ {"RLA", "longitude", "degr E", UC_NONE}, + /* 116 */ {"relax_fact", "relaxation factor (lateral boundary, LAM)", "1", + UC_NONE}, + /* 117 */ {"climsstint", "climatic sea surface temp interpolated in time", + "degr C", UC_NONE}, + /* 118 */ {"pot_vortic", "potential vorticity", "K*m^2/(s*kg)", UC_NONE}, + /* 119 */ {"ln_ps", "log surface pressure", "1", UC_NONE}, + /* 120 */ {"EXP_SI", "undefined", "-", UC_NONE}, + /* 121 */ {"RHS_SI", "undefined", "-", UC_NONE}, + /* 122 */ {"DTTDIV", "undefined", "-", UC_NONE}, + /* 123 */ {"var123", "undefined", "-", UC_NONE}, + /* 124 */ {"var124", "undefined", "-", UC_NONE}, + /* 125 */ {"var125", "undefined", "-", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"var127", "undefined", "-", UC_NONE}, + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"var130", "undefined", "-", UC_NONE}, + /* 131 */ {"var131", "undefined", "-", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"var141", "undefined", "-", UC_NONE}, + /* 142 */ {"var142", "undefined", "-", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"var144", "undefined", "-", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"SO2-conc", "SO2-concentration", "10^(-6)*g/m^3", UC_NONE}, + /* 151 */ {"SO2-dryd", "SO2-dry deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 152 */ {"SO2-wetd", "SO2-wet deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 153 */ {"SO4-conc", "SO4-concentration", "10^(-6)*g/m^3", UC_NONE}, + /* 154 */ {"SO4-dryd", "SO4-dry deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 155 */ {"SO4-wetd", "SO4-wet deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 156 */ {"NO-conc", "NO-concentration", "10^(-6)*g/m^3", UC_NONE}, + /* 157 */ {"NO-dryd", "NO-dry deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 158 */ {"NO-wetd", "NO-wet deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 159 */ {"NO2-conc", "NO2-concentration", "10^(-6)*g/m^3", UC_NONE}, + /* 160 */ {"NO2-dryd", "NO2-dry deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 161 */ {"NO2-wetd", "NO2-wet deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 162 */ {"NO3-conc", "NO3-concentration", "10^(-6)*g/m^3", UC_NONE}, + /* 163 */ {"NO3-dryd", "NO3-dry deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 164 */ {"NO3-wetd", "NO3-wet deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 165 */ {"HNO3-conc", "HNO3-concentration", "10^(-6)*g/m^3", UC_NONE}, + /* 166 */ {"HNO3-dryd", "HNO3-dry deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 167 */ {"HNO3-wetd", "HNO3-wet deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 168 */ {"NH3-conc", "NH3-concentration", "10^(-6)*g/m^3", UC_NONE}, + /* 169 */ {"NH3-dryd", "NH3-dry deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 170 */ {"NH3-wetd", "NH3-wet deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 171 */ {"NH4-conc", "NH4-concentration", "10^(-6)*g/m^3", UC_NONE}, + /* 172 */ {"NH4-dryd", "NH4-dry deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 173 */ {"NH4-wetd", "NH4-wet deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 174 */ {"O3-conc", "O3-concentration", "10^(-6)*g/m^3", UC_NONE}, + /* 175 */ {"PAN-conc", "PAN-concentration", "10^(-6)*g/m^3", UC_NONE}, + /* 176 */ {"PAN-dryd", "PAN-dry deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 177 */ {"OH-conc", "OH-concentration", "10^(-6)*g/m^3", UC_NONE}, + /* 178 */ {"O3-dryd", "O3-dry deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 179 */ {"O3-wetd", "O3-wet deposition", "10^(-3)*g/m^2", UC_NONE}, + /* 180 */ {"O3", "specific ozone content", "kg/kg", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"I131-conc", "I131-concentration", "Bq/m^3", UC_NONE}, + /* 201 */ {"I131-dryd", "I131-dry deposition", "Bq/m^2", UC_NONE}, + /* 202 */ {"I131-wetd", "I131-wet deposition", "Bq/m^2", UC_NONE}, + /* 203 */ {"Cs137-conc", "Cs137-concentration", "Bq/m^3", UC_NONE}, + /* 204 */ {"Cs137-dryd", "Cs1370dry deposition", "Bq/m^2", UC_NONE}, + /* 205 */ {"Cs137-wetd", "Cs137-wet deposition", "Bq/m^2", UC_NONE}, + /* 206 */ {"Te132-conc", "Te132-concentration", "Bq/m^3", UC_NONE}, + /* 207 */ {"Te132-dryd", "Te132-dry deposition", "Bq/m^2", UC_NONE}, + /* 208 */ {"Te132-wetd", "Te132-wet deposition", "Bq/m^2", UC_NONE}, + /* 209 */ {"Zr95-conc", "Zr95-concentration", "Bq/m^3", UC_NONE}, + /* 210 */ {"Zr95-dryd", "Zr95-dry deposition", "Bq/m^2", UC_NONE}, + /* 211 */ {"Zr95-wetd", "Zr95-wet deposition", "Bq/m^2", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"UV-IndmaxF", "UV-Index, cloudless (wolkenlos=F) Maximum", "1", + UC_NONE}, + /* 244 */ {"SB-Index", "Sonnenbrand-Index", "(W*10^(-3))/m^2", UC_NONE}, + /* 245 */ {"SB-Index_W", + "Sonnenbrand-Index bei mittl. Bewoelkung (08z-12z)", + "(W*10^(-3))/m^2", UC_NONE}, + /* 246 */ {"Kan_UVB-WI", "Kanadischer UVB-Warnindex (bew|lkungsreduziert)", + "(W*10^(-3))/m^2", UC_NONE}, + /* 247 */ {"gesamt_O3", "total column ozone (Gesamtozon)", "Dobson", + UC_NONE}, + /* 248 */ {"UV-IndmaxW", "UV-Index, clouded (bewoelkt=W) Maximum", "1", + UC_NONE}, + /* 249 */ {"h_UV-IndMx", "time (Zeit) of UV-Index-Maximum", "h UTC", + UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +/* + * GRIB table 203 at DWD + * Helmut P. Frank, 30.08.2001 + */ + +GRIB1ParmTable parm_table_dwd_203[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"pressure", "pressure", "hPa", UC_NONE}, + /* 2 */ {"geopot_h", "geopotential height", "10 * gpm", UC_NONE}, + /* 3 */ {"var3", "undefined", "-", UC_NONE}, + /* 4 */ {"temperatur", "temperature", "C", UC_NONE}, + /* 5 */ {"dew-pnt_te", "dew-point temperature", "C", UC_NONE}, + /* 6 */ {"windcompXY", "wind components X/Y (X*100000 + ((Y*10)+5000))", + "m/s", UC_NONE}, + /* 7 */ {"geomet_h", "geometrical height", "kft", UC_NONE}, + /* 8 */ {"geomet_h", "geometrical height", "hft", UC_NONE}, + /* 9 */ {"wind_di_sp", "wind direction and speed (dd*1000 + ff)", + "1*degree, 1*kt", UC_NONE}, + /* 10 */ {"3_h_pr_cha", "3 hour pressure change", "Pa/(3*h)", UC_NONE}, + /* 11 */ {"Schnee-Mge", "Schneemenge", "mm", UC_NONE}, + /* 12 */ {"var12", "undefined", "-", UC_NONE}, + /* 13 */ {"Bod-Wass-G", "Bodenwassergehalt", "mm", UC_NONE}, + /* 14 */ {"var14", "undefined", "-", UC_NONE}, + /* 15 */ {"stab_ind", "stability index", "K", UC_NONE}, /* Delta temp */ + /* 16 */ {"var16", "undefined", "-", UC_NONE}, + /* 17 */ {"var17", "undefined", "-", UC_NONE}, + /* 18 */ {"var18", "undefined", "-", UC_NONE}, + /* 19 */ {"max_wind", "maximum wind velocity", "kt", UC_NONE}, + /* 20 */ {"wind_di_sp", "wind direction and speed (dd*1000 + ff)", + "5*degrees, 1*(m/s)", UC_NONE}, + /* 21 */ {"wind_di_sp", "wind direction and speed (dd*1000 + ff)", + "5*degrees, 1*kt", UC_NONE}, + /* 22 */ {"wave_di_he", "direction and height of wind waves (dd*1000 + h)", + "1*degree, 1*cm", UC_NONE}, + /* 23 */ {"swe_di_he", "direction and height of swell (dd*1000 + h)", + "1*degree, 1*cm", UC_NONE}, + /* 24 */ {"wave_m_d_h", "mean direction and height of waves (dd*1000 + h)", + "1*degree, 1*cm", UC_NONE}, + /* 25 */ {"wind_speed", "wind speed", "kt", UC_NONE}, + /* 26 */ {"var26", "undefined", "-", UC_NONE}, + /* 27 */ {"wind_compX", "wind component X-direction", "kt", UC_NONE}, + /* 28 */ {"wind_compY", "wind component Y-direction", "kt", UC_NONE}, + /* 29 */ {"var29", "undefined", "-", UC_NONE}, + /* 30 */ {"var30", "undefined", "-", UC_NONE}, + /* 31 */ {"var31", "undefined", "-", UC_NONE}, + /* 32 */ {"var32", "undefined", "-", UC_NONE}, + /* 33 */ {"abs_voradv", "absolute vorticity advection", "1/s^2", UC_NONE}, + /* 34 */ {"var34", "undefined", "-", UC_NONE}, + /* 35 */ {"var35", "undefined", "-", UC_NONE}, + /* 36 */ {"var36", "undefined", "-", UC_NONE}, + /* 37 */ {"var37", "undefined", "-", UC_NONE}, + /* 38 */ {"var38", "undefined", "-", UC_NONE}, + /* 39 */ {"var39", "undefined", "-", UC_NONE}, + /* 40 */ {"var40", "undefined", "-", UC_NONE}, + /* 41 */ {"var41", "undefined", "-", UC_NONE}, + /* 42 */ {"vert_vel", "vertical velocity", "hPa/h", UC_NONE}, + /* 43 */ {"var43", "undefined", "-", UC_NONE}, + /* 44 */ {"var44", "undefined", "-", UC_NONE}, + /* 45 */ {"var45", "undefined", "-", UC_NONE}, + /* 46 */ {"var46", "undefined", "-", UC_NONE}, + /* 47 */ {"var47", "undefined", "-", UC_NONE}, + /* 48 */ {"var48", "undefined", "-", UC_NONE}, + /* 49 */ {"var49", "undefined", "-", UC_NONE}, + /* 50 */ {"var50", "undefined", "-", UC_NONE}, + /* 51 */ {"var51", "undefined", "-", UC_NONE}, + /* 52 */ {"var52", "undefined", "-", UC_NONE}, + /* 53 */ {"var53", "undefined", "-", UC_NONE}, + /* 54 */ {"var54", "undefined", "-", UC_NONE}, + /* 55 */ {"max_temp", "maximum temperature", "C", UC_NONE}, + /* 56 */ {"min_temp", "minimum temperature", "C", UC_NONE}, + /* 57 */ {"var57", "undefined", "-", UC_NONE}, + /* 58 */ {"clo", "value of isolation of clothes", "1", UC_NONE}, + /* 59 */ {"pmva", "predected mean vote (angepasst)", "1", UC_NONE}, + /* 60 */ {"feeled_t", "feeled temperature", "C", UC_NONE}, + /* 61 */ {"sea_temper", "sea temperature", "C", UC_NONE}, + /* 62 */ {"var62", "undefined", "-", UC_NONE}, + /* 63 */ {"var63", "undefined", "-", UC_NONE}, + /* 64 */ {"var64", "undefined", "-", UC_NONE}, + /* 65 */ {"var65", "undefined", "-", UC_NONE}, + /* 66 */ {"var66", "undefined", "-", UC_NONE}, + /* 67 */ {"var67", "undefined", "-", UC_NONE}, + /* 68 */ {"var68", "undefined", "-", UC_NONE}, + /* 69 */ {"var69", "undefined", "-", UC_NONE}, + /* 70 */ {"var70", "undefined", "-", UC_NONE}, + /* 71 */ {"var71", "undefined", "-", UC_NONE}, + /* 72 */ {"var72", "undefined", "-", UC_NONE}, + /* 73 */ {"var73", "undefined", "-", UC_NONE}, + /* 74 */ {"var74", "undefined", "-", UC_NONE}, + /* 75 */ {"var75", "undefined", "-", UC_NONE}, + /* 76 */ {"var76", "undefined", "-", UC_NONE}, + /* 77 */ {"var77", "undefined", "-", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"var80", "undefined", "-", UC_NONE}, + /* 81 */ {"var81", "undefined", "-", UC_NONE}, + /* 82 */ {"var82", "undefined", "-", UC_NONE}, + /* 83 */ {"var83", "undefined", "-", UC_NONE}, + /* 84 */ {"var84", "undefined", "-", UC_NONE}, + /* 85 */ {"var85", "undefined", "-", UC_NONE}, + /* 86 */ {"Globalstr", "Summe der Globalstrahlung ueber einen Zeitraum", + "kWh/m^2", UC_NONE}, + /* 87 */ {"Nied-GW-GE", + "Niederschlagsart+Gewitter+Glatteis (T23-i) (0..99)", "1", + UC_NONE}, + /* 88 */ {"NiedGW-Art", "Niederschlagsart+Gewitter (T23-intern) (0..99)", + "1", UC_NONE}, + /* 89 */ {"NiedGE-Art", "Niederschlagsart+Glatteis (T23-intern) (0..99)", + "1", UC_NONE}, + /* 90 */ {"NiedBewArt", + "Kombination Niederschl.-Bew.-Blautherm. (283..407)", "1", + UC_NONE}, + /* 91 */ {"Konv_U-Gr", "Hoehe der Konvektionsuntergrenze ueber Grund", + "m", UC_NONE}, + /* 92 */ {"Nied-Art", "Niederschlagsart -ww- (T23-intern) (0..99)", "1", + UC_NONE}, + /* 93 */ {"Konv-Art", "Konvektionsart (0..4)", "1", UC_NONE}, + /* 94 */ {"KonvUG-nn", "Hoehe der Konvektionsuntergrenze ueber nn", "m", + UC_NONE}, + /* 95 */ {"var95", "undefined", "-", UC_NONE}, + /* 96 */ {"var96", "undefined", "-", UC_NONE}, + /* 97 */ {"var97", "undefined", "-", UC_NONE}, + /* 98 */ {"var98", "undefined", "-", UC_NONE}, + /* 99 */ {"Wetter_ww", "Wetter (verschluesselt nach ww-Tabelle", "-", UC_NONE}, + /* 100 */ {"geostr_Vor", "geostrophische Vorticity", "1/s", UC_NONE}, + /* 101 */ {"Geo_VorAdv", "geostrophische Vorticityadvektion", "1/s^2", + UC_NONE}, + /* 102 */ {"VerGraVoAd", "vert. Gradient der geostr. Vorticityadvektion", + "m/(kg*s)", UC_NONE}, + /* 103 */ {"Geo_TemAdv", "geostrophische Schichtdickenadvektion", + "m^3/(kg*s)", UC_NONE}, + /* 104 */ {"Lap_TemAdv", "Kruemmung der geostr. Schichtdickenadvektion", + "m/(kg*s)", UC_NONE}, + /* 105 */ {"Omega_Forc", "Forcing rechte Seite Omegagleichung", "m/(kg*s)", + UC_NONE}, + /* 106 */ {"var106", "undefined", "-", UC_NONE}, + /* 107 */ {"Schichtd_A", "Schichtdicken-Advektion", "m^3/(kg*s)", UC_NONE}, + /* 108 */ {"AdGeVoThWi", + "Advektion von geostr. Vorticity mit dem therm Wind", + "m/(kg*s)", UC_NONE}, + /* 109 */ {"Wind-Div", "Winddivergenz", "1/s", UC_NONE}, + /* 110 */ {"Q", "Q-vector direction and speed (dd*1000 + fff*1E13)", + "5*deg,1E13*m^2/kg/s", UC_NONE}, + /* 111 */ {"Qx", "Q-Vektor X-Komponente", "m^2/(kg*s)", UC_NONE}, + /* 112 */ {"Qy", "Q-Vektor Y-Komponente", "m^2/(kg*s)", UC_NONE}, + /* 113 */ {"Div_Q", "Divergenz Q", "m/(kg*s)", UC_NONE}, + /* 114 */ {"FrontoGeQn", + "Frontogenesefunktion, Q isother-senkrecht-Kompon.", + "m^2/(kg*s)", UC_NONE}, + /* 115 */ {"Qs_geo", "Qs (geo),Komp. Q-Vektor parallel zu den Isothermen", + "m^2/(kg*s)", UC_NONE}, + /* 116 */ {"DivQn_geo", "Divergenz Qn geostrophisch", "m/(kg*s)", + UC_NONE}, + /* 117 */ {"DivQs_geo", "Divergenz Qs geostrophisch", "m/(kg*s)", + UC_NONE}, + /* 118 */ {"Fronto_Gen", "Frontogenesefunktion", "K^2/(m^2*s)", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"var121", "undefined", "-", UC_NONE}, + /* 122 */ {"var122", "undefined", "-", UC_NONE}, + /* 123 */ {"var123", "undefined", "-", UC_NONE}, + /* 124 */ {"FrontoGenP", "Frontogenese-Parameter", "1", UC_NONE}, + /* 125 */ {"Qs-Vektor", "Qs, Komp. Q-Vektor parallel zu den Isothermen", + "m^2/(kg*s)", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"Div_Qs", "Divergenz Qs", "m/(kg*s)", UC_NONE}, + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"IPV", "Isentrope potentielle Vorticity", "K*m^2/(s*kg)", + UC_NONE}, + /* 131 */ {"Wind_KompX", "Wind X-Komponente auf isentropen Flaechen", + "m/s", UC_NONE}, + /* 132 */ {"Wind_KompY", "Wind Y-Komponente auf isentropen Flaechen", + "m/s", UC_NONE}, + /* 133 */ {"Druck-Ise", "Druck einer isentropen Flaeche", "hPa", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"KO-Index", "KO-Index", "K", UC_NONE}, /* Delta temp */ + /* 141 */ {"TT-Index", "Totals-Totals-Index", "K", UC_NONE}, + /* Delta temp */ + /* 142 */ {"S-Index", "S-Index", "K", UC_NONE}, /* Delta temp */ + /* 143 */ {"Stein-Ind", "Steinbeck-Index", "1", UC_NONE}, + /* 144 */ {"Baily-Ind", "Baily-Index", "1", UC_NONE}, + /* 145 */ {"Microburst", "Microburst-Index", "1", UC_NONE}, + /* 146 */ {"Cat-Index", "Clear Air Turbulence Index", "1/s", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"Lab-Energ", "Labilit{tsenergie", "J/g", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"VTMP", "Virtual temp", "K", UC_NONE}, + /* 151 */ {"Pseudo_T", "Pseudo-Temperatur", "K", UC_NONE}, + /* 152 */ {"Pseudo_Pot", "Pseudopotentielle Temperatur", "K", UC_NONE}, + /* 153 */ {"Aequi_T", "Aequivalent-Temperatur", "K", UC_NONE}, + /* 154 */ {"Aequi_Pot", "Aequivalentpotentielle Temperatur", "K", UC_NONE}, + /* 155 */ {"var155", "undefined", "-", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"Bas_St_Wol", "Untergrenze strat. Bew|lkung", "hft", UC_NONE}, + /* 161 */ {"Bas_St_Wol", "Untergrenze strat. Bew|lkung", "hPa", UC_NONE}, + /* 162 */ {"Bas_Cu_Wol", "Untergrenze cumul. Bew|lkung", "hft", UC_NONE}, + /* 163 */ {"Bas_Cu_Wol", "Untergrenze cumul. Bew|lkung", "hPa", UC_NONE}, + /* 164 */ {"Top_St_Wol", "Obergrenze strat. Bew|lkung", "hft", UC_NONE}, + /* 165 */ {"Top_St_Wol", "Obergrenze strat. Bew|lkung", "hPa", UC_NONE}, + /* 166 */ {"Top_Cu_Wol", "Obergrenze cumul. Bew|lkung", "hft", UC_NONE}, + /* 167 */ {"Top_Cu_Wol", "Obergrenze cumul. Bew|lkung", "hPa", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"Bas_Tur_Wo", "Untergrenze Wolkenturbulenz", "hft", UC_NONE}, + /* 171 */ {"Bas_Tur_Wo", "Untergrenze Wolkenturbulenz", "hPa", UC_NONE}, + /* 172 */ {"Top_Tur_Wo", "Obergrenze Wolkenturbulenz", "hft", UC_NONE}, + /* 173 */ {"Top_Tur_Wo", "Obergrenze Wolkenturbulenz", "hPa", UC_NONE}, + /* 174 */ {"Bas_Eis_Wo", "Untergrenze Vereisung in Wolken", "hft", + UC_NONE}, + /* 175 */ {"Bas_Eis_Wo", "Untergrenze Vereisung in Wolken", "hPa", + UC_NONE}, + /* 176 */ {"Top_Eis_Wo", "Obergrenze Vereisung in Wolken", "hft", UC_NONE}, + /* 177 */ {"Top_Eis_Wo", "Obergrenze Vereisung in Wolken", "hPa", UC_NONE}, + /* 178 */ {"Int_Tur_Wo", "Intensitaet der Turbulenz in Wolken (0..4)", + "1", UC_NONE}, + /* 179 */ {"Int_Eis_Wo", "Intensitaet der Vereisung (0..4)", "1", + UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"Sichtweite", "Sichtweite", "m", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"IcingGuess", + "Icing Regime 1.Guess(1=gen,2=conv,3=strat,4=freez)", "1", + UC_NONE}, + /* 196 */ {"IcingGrade", "Icing Grade (1=LGT,2=MOD,3=SEV)", "1", UC_NONE}, + /* 197 */ {"IcingRegim", + "Icing Regime(1=general,2=convect,3=strat,4=freez)", "1", + UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"Gru_Wetter", "Wetter - Grundzustand (ww", "-", UC_NONE}, + /* 201 */ {"Lok_Wetter", "Wetter - 1. lokale Abweichung (ww", "-", + UC_NONE}, + /* 202 */ {"Lok_Wetter", "Wetter - 2. lokale Abweichung (ww", "-", + UC_NONE}, + /* 203 */ {"CLDEPTH", "cloud depth (grey scale", "-", UC_NONE}, + /* 204 */ {"CLCT_MOD", "modified total cloud cover (0..1)", "1", UC_NONE}, + /* 205 */ {"curr_weath", "current weather (symbol number", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"Cu", "Cumulus (0..1)", "1", UC_NONE}, + /* 212 */ {"Cb", "Cumulimbus (0..1)", "1", UC_NONE}, + /* 213 */ {"Sc", "Stratocumulus (0..1)", "1", UC_NONE}, + /* 214 */ {"Ac", "Altocumulus (0..1)", "1", UC_NONE}, + /* 215 */ {"Ci", "Cirrus (0..1)", "1", UC_NONE}, + /* 216 */ {"St", "Stratus (0..1)", "1", UC_NONE}, + /* 217 */ {"As", "Altostratus (0..1)", "1", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"Bedeckung", "Bedeckung in Stufen", "1", UC_NONE}, + /* 222 */ {"Konvektion", "Konvektion ja/nein", "1", UC_NONE}, + /* 223 */ {"MN_GT_90", "Gesamtbedeckung > 90% ja/nein", "1", UC_NONE}, + /* 224 */ {"RF700_GT_89", "relative Feuchte 700 hPa >= 90% ja/nein", "1", + UC_NONE}, + /* 225 */ {"RR12_zentr", "Niederschlag 12 std. zentriert", "mm", UC_NONE}, + /* 226 */ {"RR12_LE_half", + "Niederschlag 12 std. zentriert, Werte <= 0.5mm", "mm", + UC_NONE}, + /* 227 */ {"RR12_SA_GT_60", "RR12 zentriert, Schneeanteil > 60% ja/nein", + "1", UC_NONE}, + /* 228 */ {"RR12_Kv_GT_60", + "RR12 zentriert, konvektiver Anteil > 60% ja/nein", "1", + UC_NONE}, + /* 229 */ {"SRR12ff", "Starkniederschlag in Stufen (12 std. Folgezeitr)", + "1", UC_NONE}, + /* 230 */ {"RRMAX_STD", "Maximaler Starkniederschlag / std", "mm/h", + UC_NONE}, + /* 231 */ {"RRMAX_MIN", "Maximaler Starkniederschlag / min", "mm/min", + UC_NONE}, + /* 232 */ {"SN12ff_GT_15", + "Schneefall (12std. Folgezeitraum) > 15 mm ja/nein", "1", + UC_NONE}, + /* 233 */ {"RRgefr12ff", + "gefrierender Regen (12std. Folgezeitraum) ja/nein", "1", + UC_NONE}, + /* 234 */ {"FFboe", "Boeenstaerke in Stufen", "1", UC_NONE}, + /* 235 */ {"Gewitter", "Gewitter in Stufen", "1", UC_NONE}, + /* 236 */ {"Tx2m12h_ze", "2m Maximumtemperatur 12h zentriert", + "Grad Celsius", UC_NONE}, + /* 237 */ {"Tn2m12h_ze", "2m Minimumtemperatur 12h zentriert", + "Grad Celsius", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"SCHWUELIND", "Schwuele-Index", "1", UC_NONE}, + /* 252 */ {"SMOGSTUFEN", "Smog-Intensitaetsstufen", "1", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"SMOGHOEHE", "Obergrenze Smog ( Inversionshoehe )", "m", + UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; + +/***************************************************************************** + * CPTEC Parameter Tables (GRIB1 Section 1 Table 2) + * Table versions 254 + ***************************************************************************** + */ + +GRIB1ParmTable parm_table_cptec_254[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "hPa", UC_NONE}, + /* 2 */ {"psnm", "Pressure reduced to MSL", "hPa", UC_NONE}, + /* 3 */ {"tsps", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"var4", "undefined", "-", UC_NONE}, + /* 5 */ {"var5", "undefined", "-", UC_NONE}, + /* 6 */ {"geop", "Geopotential", "dam", UC_NONE}, + /* 7 */ {"zgeo", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"gzge", "Geometric height", "m", UC_NONE}, + /* 9 */ {"var9", "undefined", "-", UC_NONE}, + /* 10 */ {"var10", "undefined", "-", UC_NONE}, + /* 11 */ {"temp", "ABSOLUTE TEMPERATURE", "K", UC_K2F}, + /* 12 */ {"vtmp", "VIRTUAL TEMPERATURE", "K", UC_K2F}, + /* 13 */ {"ptmp", "POTENTIAL TEMPERATURE", "K", UC_K2F}, + /* 14 */ {"psat", "PSEUDO-ADIABATIC POTENTIAL TEMPERATURE", "K", UC_K2F}, + /* 15 */ {"mxtp", "MAXIMUM TEMPERATURE", "K", UC_K2F}, + /* 16 */ {"mntp", "MINIMUM TEMPERATURE", "K", UC_K2F}, + /* 17 */ {"tpor", "DEW POINT TEMPERATURE", "K", UC_K2F}, + /* 18 */ {"dptd", "DEW POINT DEPRESSION", "K", UC_NONE}, + /* 19 */ {"lpsr", "LAPSE RATE", "K/m", UC_NONE}, + /* 20 */ {"var20", "undefined", "-", UC_NONE}, + /* 21 */ {"rds1", "RADAR SPECTRA(1)", "-", UC_NONE}, + /* 22 */ {"rds2", "RADAR SPECTRA(2)", "-", UC_NONE}, + /* 23 */ {"rds3", "RADAR SPECTRA(3)", "-", UC_NONE}, + /* 24 */ {"var24", "undefined", "-", UC_NONE}, + /* 25 */ {"tpan", "TEMPERATURE ANOMALY", "K", UC_NONE}, + /* 26 */ {"psan", "PRESSURE ANOMALY", "Pa hPa", UC_NONE}, + /* 27 */ {"zgan", "GEOPOT HEIGHT ANOMALY", "m", UC_NONE}, + /* 28 */ {"wvs1", "WAVE SPECTRA(1)", "-", UC_NONE}, + /* 29 */ {"wvs2", "WAVE SPECTRA(2)", "-", UC_NONE}, + /* 30 */ {"wvs3", "WAVE SPECTRA(3)", "-", UC_NONE}, + /* 31 */ {"wind", "WIND DIRECTION", "deg", UC_NONE}, + /* 32 */ {"wins", "WIND SPEED", "m/s", UC_NONE}, + /* 33 */ {"uvel", "ZONAL WIND (U)", "m/s", UC_NONE}, + /* 34 */ {"vvel", "MERIDIONAL WIND (V)", "m/s", UC_NONE}, + /* 35 */ {"fcor", "STREAM FUNCTION", "m2/s", UC_NONE}, + /* 36 */ {"potv", "VELOCITY POTENTIAL", "m2/s", UC_NONE}, + /* 37 */ {"var37", "undefined", "-", UC_NONE}, + /* 38 */ {"sgvv", "SIGMA COORD VERT VEL", "sec/sec", UC_NONE}, + /* 39 */ {"omeg", "OMEGA", "Pa/s", UC_NONE}, + /* 40 */ {"omg2", "VERTICAL VELOCITY", "m/s", UC_NONE}, + /* 41 */ {"abvo", "ABSOLUTE VORTICITY", "10^5/sec", UC_NONE}, + /* 42 */ {"abdv", "ABSOLUTE DIVERGENCE", "10^5/sec", UC_NONE}, + /* 43 */ {"vort", "VORTICITY", "1/s", UC_NONE}, + /* 44 */ {"divg", "DIVERGENCE", "1/s", UC_NONE}, + /* 45 */ {"vucs", "VERTICAL U-COMP SHEAR", "1/sec", UC_NONE}, + /* 46 */ {"vvcs", "VERT V-COMP SHEAR", "1/sec", UC_NONE}, + /* 47 */ {"dirc", "DIRECTION OF CURRENT", "deg", UC_NONE}, + /* 48 */ {"spdc", "SPEED OF CURRENT", "m/s", UC_NONE}, + /* 49 */ {"ucpc", "U-COMPONENT OF CURRENT", "m/s", UC_NONE}, + /* 50 */ {"vcpc", "V-COMPONENT OF CURRENT", "m/s", UC_NONE}, + /* 51 */ {"umes", "SPECIFIC HUMIDITY", "kg/kg", UC_NONE}, + /* 52 */ {"umrl", "RELATIVE HUMIDITY", "no Dim", UC_NONE}, + /* 53 */ {"hmxr", "HUMIDITY MIXING RATIO", "kg/kg", UC_NONE}, + /* 54 */ {"agpl", "INST. PRECIPITABLE WATER", "Kg/m2", UC_NONE}, + /* 55 */ {"vapp", "VAPOUR PRESSURE", "Pa hpa", UC_NONE}, + /* 56 */ {"sadf", "SATURATION DEFICIT", "Pa hPa", UC_NONE}, + /* 57 */ {"evap", "EVAPORATION", "Kg/m2/day", UC_NONE}, + /* 58 */ {"var58", "undefined", "-", UC_NONE}, + /* 59 */ {"prcr", "PRECIPITATION RATE", "kg/m2/day", UC_NONE}, + /* 60 */ {"thpb", "THUNDER PROBABILITY", "%", UC_NONE}, + /* 61 */ {"prec", "TOTAL PRECIPITATION", "Kg/m2/day", UC_NONE}, + /* 62 */ {"prge", "LARGE SCALE PRECIPITATION", "Kg/m2/day", UC_NONE}, + /* 63 */ {"prcv", "CONVECTIVE PRECIPITATION", "Kg/m2/day", UC_NONE}, + /* 64 */ {"neve", "SNOWFALL", "Kg/m2/day", UC_NONE}, + /* 65 */ {"wenv", "WAT EQUIV ACC SNOW DEPTH", "kg/m2", UC_NONE}, + /* 66 */ {"nvde", "SNOW DEPTH", "cm", UC_NONE}, + /* 67 */ {"mxld", "MIXED LAYER DEPTH", "m cm", UC_NONE}, + /* 68 */ {"tthd", "TRANS THERMOCLINE DEPTH", "m cm", UC_NONE}, + /* 69 */ {"mthd", "MAIN THERMOCLINE DEPTH", "m cm", UC_NONE}, + /* 70 */ {"mtha", "MAIN THERMOCLINE ANOM", "m cm", UC_NONE}, + /* 71 */ {"cbnv", "CLOUD COVER", "0-1", UC_NONE}, + /* 72 */ {"cvnv", "CONVECTIVE CLOUD COVER", "0-1", UC_NONE}, + /* 73 */ {"lwnv", "LOW CLOUD COVER", "0-1", UC_NONE}, + /* 74 */ {"mdnv", "MEDIUM CLOUD COVER", "0-1", UC_NONE}, + /* 75 */ {"hinv", "HIGH CLOUD COVER", "0-1", UC_NONE}, + /* 76 */ {"wtnv", "CLOUD WATER", "kg/m2", UC_NONE}, + /* 77 */ {"bli", "BEST LIFTED INDEX (TO 500 HPA)", "K", UC_NONE}, + /* 78 */ {"var78", "undefined", "-", UC_NONE}, + /* 79 */ {"var79", "undefined", "-", UC_NONE}, + /* 80 */ {"var80", "undefined", "-", UC_NONE}, + /* 81 */ {"lsmk", "LAND SEA MASK", "0,1", UC_NONE}, + /* 82 */ {"dslm", "DEV SEA_LEV FROM MEAN", "m", UC_NONE}, + /* 83 */ {"zorl", "ROUGHNESS LENGTH", "m", UC_NONE}, + /* 84 */ {"albe", "ALBEDO", "%", UC_NONE}, + /* 85 */ {"dstp", "DEEP SOIL TEMPERATURE", "K", UC_K2F}, + /* 86 */ {"soic", "SOIL MOISTURE CONTENT", "Kg/m2", UC_NONE}, + /* 87 */ {"vege", "VEGETATION", "%", UC_NONE}, + /* 88 */ {"var88", "undefined", "-", UC_NONE}, + /* 89 */ {"dens", "DENSITY", "kg/m3", UC_NONE}, + /* 90 */ {"var90", "Undefined", "-", UC_NONE}, + /* 91 */ {"icec", "ICE CONCENTRATION", "fraction", UC_NONE}, + /* 92 */ {"icet", "ICE THICKNESS", "m", UC_NONE}, + /* 93 */ {"iced", "DIRECTION OF ICE DRIFT", "deg", UC_NONE}, + /* 94 */ {"ices", "SPEED OF ICE DRIFT", "m/s", UC_NONE}, + /* 95 */ {"iceu", "U-COMP OF ICE DRIFT", "m/s", UC_NONE}, + /* 96 */ {"icev", "V-COMP OF ICE DRIFT", "m/s", UC_NONE}, + /* 97 */ {"iceg", "ICE GROWTH", "m", UC_NONE}, + /* 98 */ {"icdv", "ICE DIVERGENCE", "sec/sec", UC_NONE}, + /* 99 */ {"var99", "undefined", "-", UC_NONE}, + /* 100 */ {"shcw", "SIG HGT COM WAVE/SWELL", "m", UC_NONE}, + /* 101 */ {"wwdi", "DIRECTION OF WIND WAVE", "deg", UC_NONE}, + /* 102 */ {"wwsh", "SIG HGHT OF WIND WAVES", "m", UC_NONE}, + /* 103 */ {"wwmp", "MEAN PERIOD WIND WAVES", "sec", UC_NONE}, + /* 104 */ {"swdi", "DIRECTION OF SWELL WAVE", "deg", UC_NONE}, + /* 105 */ {"swsh", "SIG HEIGHT SWELL WAVES", "m", UC_NONE}, + /* 106 */ {"swmp", "MEAN PERIOD SWELL WAVES", "sec", UC_NONE}, + /* 107 */ {"prwd", "PRIMARY WAVE DIRECTION", "deg", UC_NONE}, + /* 108 */ {"prmp", "PRIM WAVE MEAN PERIOD", "s", UC_NONE}, + /* 109 */ {"swdi", "SECOND WAVE DIRECTION", "deg", UC_NONE}, + /* 110 */ {"swmp", "SECOND WAVE MEAN PERIOD", "s", UC_NONE}, + /* 111 */ {"ocas", "SHORT WAVE ABSORBED AT GROUND", "W/m2", UC_NONE}, + /* 112 */ {"slds", "NET LONG WAVE AT BOTTOM", "W/m2", UC_NONE}, + /* 113 */ {"nswr", "NET SHORT-WAV RAD(TOP)", "W/m2", UC_NONE}, + /* 114 */ {"role", "OUTGOING LONG WAVE AT TOP", "W/m2", UC_NONE}, + /* 115 */ {"lwrd", "LONG-WAV RAD", "W/m2", UC_NONE}, + /* 116 */ {"swea", "SHORT WAVE ABSORBED BY EARTH/ATMOSPHERE", "W/m2", + UC_NONE}, + /* 117 */ {"glbr", "GLOBAL RADIATION", "W/m2 ", UC_NONE}, + /* 118 */ {"var118", "undefined", "-", UC_NONE}, + /* 119 */ {"var119", "undefined", "-", UC_NONE}, + /* 120 */ {"var120", "undefined", "-", UC_NONE}, + /* 121 */ {"clsf", "LATENT HEAT FLUX FROM SURFACE", "W/m2", UC_NONE}, + /* 122 */ {"cssf", "SENSIBLE HEAT FLUX FROM SURFACE", "W/m2", UC_NONE}, + /* 123 */ {"blds", "BOUND LAYER DISSIPATION", "W/m2", UC_NONE}, + /* 124 */ {"var124", "undefined", "-", UC_NONE}, + /* 125 */ {"var125", "undefined", "-", UC_NONE}, + /* 126 */ {"var126", "undefined", "-", UC_NONE}, + /* 127 */ {"imag", "IMAGE", "image^data", UC_NONE}, + /* 128 */ {"tp2m", "2 METRE TEMPERATURE", "K", UC_K2F}, + /* 129 */ {"dp2m", "2 METRE DEWPOINT TEMPERATURE", "K", UC_K2F}, + /* 130 */ {"u10m", "10 METRE U-WIND COMPONENT", "m/s", UC_NONE}, + /* 131 */ {"v10m", "10 METRE V-WIND COMPONENT", "m/s", UC_NONE}, + /* 132 */ {"topo", "TOPOGRAPHY", "m", UC_NONE}, + /* 133 */ {"gsfp", "GEOMETRIC MEAN SURFACE PRESSURE", "hPa", UC_NONE}, + /* 134 */ {"lnsp", "LN SURFACE PRESSURE", "hPa", UC_NONE}, + /* 135 */ {"pslc", "SURFACE PRESSURE", "hPa", UC_NONE}, + /* 136 */ {"pslm", "M S L PRESSURE (MESINGER METHOD)", "hPa", UC_NONE}, + /* 137 */ {"mask", "MASK", "-/+", UC_NONE}, + /* 138 */ {"mxwu", "MAXIMUM U-WIND", "m/s", UC_NONE}, + /* 139 */ {"mxwv", "MAXIMUM V-WIND", "m/s", UC_NONE}, + /* 140 */ {"cape", "CONVECTIVE AVAIL. POT.ENERGY", "m2/s2", UC_NONE}, + /* 141 */ {"cine", "CONVECTIVE INHIB. ENERGY", "m2/s2", UC_NONE}, + /* 142 */ {"lhcv", "CONVECTIVE LATENT HEATING", "K/s", UC_NONE}, + /* 143 */ {"mscv", "CONVECTIVE MOISTURE SOURCE", "1/s", UC_NONE}, + /* 144 */ {"scvm", "SHALLOW CONV. MOISTURE SOURCE", "1/s", UC_NONE}, + /* 145 */ {"scvh", "SHALLOW CONVECTIVE HEATING", "K/s", UC_NONE}, + /* 146 */ {"mxwp", "MAXIMUM WIND PRESS. LVL", "hPa", UC_NONE}, + /* 147 */ {"ustr", "STORM MOTION U-COMPONENT", "m/s", UC_NONE}, + /* 148 */ {"vstr", "STORM MOTION V-COMPONENT", "m/s", UC_NONE}, + /* 149 */ {"cbnt", "MEAN CLOUD COVER", "0-1", UC_NONE}, + /* 150 */ {"pcbs", "PRESSURE AT CLOUD BASE", "hPa", UC_NONE}, + /* 151 */ {"pctp", "PRESSURE AT CLOUD TOP", "hPa", UC_NONE}, + /* 152 */ {"fzht", "FREEZING LEVEL HEIGHT", "m", UC_NONE}, + /* 153 */ {"fzrh", "FREEZING LEVEL RELATIVE HUMIDITY", "%", UC_NONE}, + /* 154 */ {"fdlt", "FLIGHT LEVELS TEMPERATURE", "K", UC_K2F}, + /* 155 */ {"fdlu", "FLIGHT LEVELS U-WIND", "m/s", UC_NONE}, + /* 156 */ {"fdlv", "FLIGHT LEVELS V-WIND", "m/s", UC_NONE}, + /* 157 */ {"tppp", "TROPOPAUSE PRESSURE", "hPa", UC_NONE}, + /* 158 */ {"tppt", "TROPOPAUSE TEMPERATURE", "K", UC_K2F}, + /* 159 */ {"tppu", "TROPOPAUSE U-WIND COMPONENT", "m/s", UC_NONE}, + /* 160 */ {"tppv", "TROPOPAUSE v-WIND COMPONENT", "m/s", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"gvdu", "GRAVITY WAVE DRAG DU/DT", "m/s2", UC_NONE}, + /* 163 */ {"gvdv", "GRAVITY WAVE DRAG DV/DT", "m/s2", UC_NONE}, + /* 164 */ {"gvus", "GRAVITY WAVE DRAG SFC ZONAL STRESS", "Pa", UC_NONE}, + /* 165 */ {"gvvs", "GRAVITY WAVE DRAG SFC MERIDIONAL STRESS", "Pa", + UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"dvsh", "DIVERGENCE OF SPECIFIC HUMIDITY", "1/s", UC_NONE}, + /* 168 */ {"hmfc", "HORIZ. MOISTURE FLUX CONV.", "1/s", UC_NONE}, + /* 169 */ {"vmfl", "VERT. INTEGRATED MOISTURE FLUX CONV.", "kg/(m2*s)", + UC_NONE}, + /* 170 */ {"vadv", "VERTICAL MOISTURE ADVECTION", "kg/(kg*s)", UC_NONE}, + /* 171 */ {"nhcm", "NEG. HUM. CORR. MOISTURE SOURCE", "kg/(kg*s)", + UC_NONE}, + /* 172 */ {"lglh", "LARGE SCALE LATENT HEATING", "K/s", UC_NONE}, + /* 173 */ {"lgms", "LARGE SCALE MOISTURE SOURCE", "1/s", UC_NONE}, + /* 174 */ {"smav", "SOIL MOISTURE AVAILABILITY", "0-1", UC_NONE}, + /* 175 */ {"tgrz", "SOIL TEMPERATURE OF ROOT ZONE", "K", UC_K2F}, + /* 176 */ {"bslh", "BARE SOIL LATENT HEAT", "Ws/m2", UC_NONE}, + /* 177 */ {"evpp", "POTENTIAL SFC EVAPORATION", "m", UC_NONE}, + /* 178 */ {"rnof", "RUNOFF", "kg/m2/s)", UC_NONE}, + /* 179 */ {"pitp", "INTERCEPTION LOSS", "W/m2", UC_NONE}, + /* 180 */ {"vpca", "VAPOR PRESSURE OF CANOPY AIR SPACE", "mb", UC_NONE}, + /* 181 */ {"qsfc", "SURFACE SPEC HUMIDITY", "kg/kg", UC_NONE}, + /* 182 */ {"ussl", "SOIL WETNESS OF SURFACE", "0-1", UC_NONE}, + /* 183 */ {"uzrs", "SOIL WETNESS OF ROOT ZONE", "0-1", UC_NONE}, + /* 184 */ {"uzds", "SOIL WETNESS OF DRAINAGE ZONE", "0-1", UC_NONE}, + /* 185 */ {"amdl", "STORAGE ON CANOPY", "m", UC_NONE}, + /* 186 */ {"amsl", "STORAGE ON GROUND", "m", UC_NONE}, + /* 187 */ {"tsfc", "SURFACE TEMPERATURE", "K", UC_K2F}, + /* 188 */ {"tems", "SURFACE ABSOLUTE TEMPERATURE", "K", UC_K2F}, + /* 189 */ {"tcas", "TEMPERATURE OF CANOPY AIR SPACE", "K", UC_K2F}, + /* 190 */ {"ctmp", "TEMPERATURE AT CANOPY", "K", UC_K2F}, + /* 191 */ {"tgsc", "GROUND/SURFACE COVER TEMPERATURE", "K", UC_K2F}, + /* 192 */ {"uves", "SURFACE ZONAL WIND (U)", "m/s", UC_NONE}, + /* 193 */ {"usst", "SURFACE ZONAL WIND STRESS", "Pa", UC_NONE}, + /* 194 */ {"vves", "SURFACE MERIDIONAL WIND (V)", "m/s", UC_NONE}, + /* 195 */ {"vsst", "SURFACE MERIDIONAL WIND STRESS", "Pa", UC_NONE}, + /* 196 */ {"suvf", "SURFACE MOMENTUM FLUX", "W/m2", UC_NONE}, + /* 197 */ {"iswf", "INCIDENT SHORT WAVE FLUX", "W/m2", UC_NONE}, + /* 198 */ {"ghfl", "TIME AVE GROUND HT FLX", "W/m2", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"lwbc", "NET LONG WAVE AT BOTTOM (CLEAR)", "W/m2", UC_NONE}, + /* 201 */ {"lwtc", "OUTGOING LONG WAVE AT TOP (CLEAR)", "W/m2", UC_NONE}, + /* 202 */ {"swec", "SHORT WV ABSRBD BY EARTH/ATMOS (CLEAR)", "W/m2", + UC_NONE}, + /* 203 */ {"ocac", "SHORT WAVE ABSORBED AT GROUND (CLEAR)", "W/m2", + UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"lwrh", "LONG WAVE RADIATIVE HEATING", "K/s", UC_NONE}, + /* 206 */ {"swrh", "SHORT WAVE RADIATIVE HEATING", "K/s", UC_NONE}, + /* 207 */ {"olis", "DOWNWARD LONG WAVE AT BOTTOM", "W/m2", UC_NONE}, + /* 208 */ {"olic", "DOWNWARD LONG WAVE AT BOTTOM (CLEAR)", "W/m2", + UC_NONE}, + /* 209 */ {"ocis", "DOWNWARD SHORT WAVE AT GROUND", "W/m2", UC_NONE}, + /* 210 */ {"ocic", "DOWNWARD SHORT WAVE AT GROUND (CLEAR)", "W/m2", + UC_NONE}, + /* 211 */ {"oles", "UPWARD LONG WAVE AT BOTTOM", "W/m2", UC_NONE}, + /* 212 */ {"oces", "UPWARD SHORT WAVE AT GROUND", "W/m2", UC_NONE}, + /* 213 */ {"swgc", "UPWARD SHORT WAVE AT GROUND (CLEAR)", "W/m2", UC_NONE}, + /* 214 */ {"roce", "UPWARD SHORT WAVE AT TOP", "W/m2", UC_NONE}, + /* 215 */ {"swtc", "UPWARD SHORT WAVE AT TOP (CLEAR)", "W/m2", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"hhdf", "HORIZONTAL HEATING DIFFUSION", "K/s", UC_NONE}, + /* 219 */ {"hmdf", "HORIZONTAL MOISTURE DIFFUSION", "1/s", UC_NONE}, + /* 220 */ {"hddf", "HORIZONTAL DIVERGENCE DIFFUSION", "1/s2", UC_NONE}, + /* 221 */ {"hvdf", "HORIZONTAL VORTICITY DIFFUSION", "1/s2", UC_NONE}, + /* 222 */ {"vdms", "VERTICAL DIFF. MOISTURE SOURCE", "1/s", UC_NONE}, + /* 223 */ {"vdfu", "VERTICAL DIFFUSION DU/DT", "m/s2", UC_NONE}, + /* 224 */ {"vdfv", "VERTICAL DIFFUSION DV/DT", "m/s2", UC_NONE}, + /* 225 */ {"vdfh", "VERTICAL DIFFUSION HEATING", "K/s", UC_NONE}, + /* 226 */ {"umrs", "SURFACE RELATIVE HUMIDITY", "no Dim", UC_NONE}, + /* 227 */ {"vdcc", "VERTICAL DIST TOTAL CLOUD COVER", "no Dim", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"usmt", "TIME MEAN SURFACE ZONAL WIND (U)", "m/s", UC_NONE}, + /* 231 */ {"vsmt", "TIME MEAN SURFACE MERIDIONAL WIND (V)", "m/s", + UC_NONE}, + /* 232 */ {"tsmt", "TIME MEAN SURFACE ABSOLUTE TEMPERATURE", "K", UC_K2F}, + /* 233 */ {"rsmt", "TIME MEAN SURFACE RELATIVE HUMIDITY", "no Dim", + UC_NONE}, + /* 234 */ {"atmt", "TIME MEAN ABSOLUTE TEMPERATURE", "K", UC_K2F}, + /* 235 */ {"stmt", "TIME MEAN DEEP SOIL TEMPERATURE", "K", UC_K2F}, + /* 236 */ {"ommt", "TIME MEAN DERIVED OMEGA", "Pa/s", UC_NONE}, + /* 237 */ {"dvmt", "TIME MEAN DIVERGENCE", "1/s", UC_NONE}, + /* 238 */ {"zhmt", "TIME MEAN GEOPOTENTIAL HEIGHT", "m", UC_NONE}, + /* 239 */ {"lnmt", "TIME MEAN LOG SURFACE PRESSURE", "ln(cbar)", UC_NONE}, + /* 240 */ {"mkmt", "TIME MEAN MASK", "-/+", UC_NONE}, + /* 241 */ {"vvmt", "TIME MEAN MERIDIONAL WIND (V)", "m/s", UC_NONE}, + /* 242 */ {"omtm", "TIME MEAN OMEGA", "cbar/s", UC_NONE}, + /* 243 */ {"ptmt", "TIME MEAN POTENTIAL TEMPERATURE", "K", UC_K2F}, + /* 244 */ {"pcmt", "TIME MEAN PRECIP. WATER", "kg/m2", UC_NONE}, + /* 245 */ {"rhmt", "TIME MEAN RELATIVE HUMIDITY", "%", UC_NONE}, + /* 246 */ {"mpmt", "TIME MEAN SEA LEVEL PRESSURE", "hPa", UC_NONE}, + /* 247 */ {"simt", "TIME MEAN SIGMADOT", "1/s", UC_NONE}, + /* 248 */ {"uemt", "TIME MEAN SPECIFIC HUMIDITY", "kg/kg", UC_NONE}, + /* 249 */ {"fcmt", "TIME MEAN STREAM FUNCTION", "m2/s", UC_NONE}, + /* 250 */ {"psmt", "TIME MEAN SURFACE PRESSURE", "hPa", UC_NONE}, + /* 251 */ {"tmmt", "TIME MEAN SURFACE TEMPERATURE", "K", UC_K2F}, + /* 252 */ {"pvmt", "TIME MEAN VELOCITY POTENTIAL", "m2/s", UC_NONE}, + /* 253 */ {"tvmt", "TIME MEAN VIRTUAL TEMPERATURE", "K", UC_K2F}, + /* 254 */ {"vtmt", "TIME MEAN VORTICITY", "1/s", UC_NONE}, + /* 255 */ {"uvmt", "TIME MEAN ZONAL WIND (U)", "m/s", UC_NONE}, +}; +/* *INDENT-ON* */ + +/* + * parameter table for AFWA (Air force) + */ + +/* AFWA center = 57, subcenter = 0 */ +/* Updated last on 7/22/2004 */ +/* *INDENT-OFF* */ +GRIB1ParmTable parm_table_afwa_000[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "m^2K/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice cover (ice=1;no ice=0)", "proportion", UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"3HRPCP", "Average 3hr Precip.", "mm/hr", UC_NONE}, + /* 129 */ {"AERTP", "Aerosol type", "(0=none, 1=present)", UC_NONE}, + /* 130 */ {"HSL", "Hours since land", "hours", UC_NONE}, + /* 131 */ {"PRESS", "Pressure", "hPa", UC_NONE}, + /* 132 */ {"MSLPRS", "Pressure reduced to MSL", "hPa", UC_NONE}, + /* 133 */ {"VERVEL", "Pressure Vertical Velocity", "hPa/s", UC_NONE}, + /* 134 */ {"DENALT", "Density Altitude", "m", UC_NONE}, + /* 135 */ {"WBZHGT", "Wet Bulb Zero Height", "m", UC_NONE}, + /* 136 */ {"HAIL", "Hail", "cm", UC_NONE}, + /* 137 */ {"PBLHGT", "Planetary Boundary Layer (PBL) Height", "m", UC_NONE}, + /* 138 */ {"TSMAX", "Thunderstorm Max Tops", "m", UC_NONE}, + /* 139 */ {"TSCOV", "Thunderstorm coverage", + "(0=none,1=isolated1-2%,2=few3-5%,3=scattered16-45%,4=numerous)", + UC_NONE}, + /* 140 */ {"CLDCIG", "Ceiling", "m", UC_NONE}, + /* 141 */ {"WGST", "Wind speed of gusts", "m", UC_NONE}, + /* 142 */ {"UGST", "u wind of gusts", "m/s", UC_NONE}, + /* 143 */ {"VGST", "v wind of gusts", "m/s", UC_NONE}, + /* 144 */ {"PCPCAT", "Precip Category", "code?", UC_NONE}, + /* 145 */ {"CT1TOP", "Contrail Engine Type 1 Top", "m", UC_NONE}, + /* 146 */ {"CT1BAS", "Contrail Engine Type 1 Base", "m", UC_NONE}, + /* 147 */ {"CT1INT", "Contrail Engine Type 1 Intensity", "0=none,1=present", + UC_NONE}, + /* 148 */ {"CT2TOP", "Contrail Engine Type 2 Top", "m", UC_NONE}, + /* 149 */ {"CT2BAS", "Contrail Engine Type 2 Base", "m", UC_NONE}, + /* 150 */ {"CT2INT", "Contrail Engine Type 2 Intensity", "0=none,1=present", + UC_NONE}, + /* 151 */ {"CT3TOP", "Contrail Engine Type 3 Top", "m", UC_NONE}, + /* 152 */ {"CT3BAS", "Contrail Engine Type 3 Base", "m", UC_NONE}, + /* 153 */ {"CT3INT", "Contrail Engine Type 3 Intensity", "0=none,1=present", + UC_NONE}, + /* 154 */ {"IRTSM", "IR Transmissivity (IWA vs TDA's)", "%", UC_NONE}, + /* 155 */ {"ABSHUM", "Absolute Humidity", "g/m^3", UC_NONE}, + /* 156 */ {"IRVIS", "IR Visibility", "m", UC_NONE}, + /* 157 */ {"XTRAJ", "X-component of trajectory", "16th mesh grid pts/hr", UC_NONE}, + /* 158 */ {"YTRAJ", "Y-component of trajectory", "16th mesh grid pts/hr", UC_NONE}, + /* 159 */ {"PTRAJ", "J-component of trajectory", "millibars/hr", UC_NONE}, + /* 160 */ {"TERID", "Terrain identifier", + "0-free atmosphere,2-gradient level trajectory,3-off hemisphere", + UC_NONE}, + /* 161 */ {"POVORT", "Potential Vorticity", "10^-6xm^2/sK K/g", UC_NONE}, + /* 162 */ {"SEATP", "Sea Surface Temp", ".1Deg C", UC_NONE}, + /* 163 */ {"CLDA", "Cloud Amount (layer)", "%", UC_NONE}, + /* 164 */ {"CLDTYP", "Cloud Type (layer)", + "1=CB,2=ST,3=SC,4=CU,5=AS,6=NS,7=AC,8=CS,9=CI,0=none", UC_NONE}, + /* 165 */ {"TPI", "Thunderstorm Potential Indicator", "-", UC_NONE}, + /* 166 */ {"SVTFG", "Severe Turbulence Flag (series with 250-254)", + "0=off,1=on", UC_NONE}, + /* 167 */ {"RAFG", "Precipitation Rain Flag", "0=off,1=on", UC_NONE}, + /* 168 */ {"TSFG", "Precipitation Thunderstorm Flag", "0=off,1=on", UC_NONE}, + /* 169 */ {"SVTSFG", "Precipitation Severe TS Flag", "0=off,1=on", UC_NONE}, + /* 170 */ {"SNFG", "Precipitation Snow Flag", "0=off,1=on", UC_NONE}, + /* 171 */ {"MXDFG", "Precipitation Mixed Flag", "0=off,1=on", UC_NONE}, + /* 172 */ {"IPFG", "Precipitation Ice Pellets Flag", "0=off,1=on", UC_NONE}, + /* 173 */ {"ZRAFG", "Precipitation Freezing Rain Flag", "0=off,1=on", UC_NONE}, + /* 174 */ {"DUSCO", "Dust Concentration", "mg/m^3", UC_NONE}, + /* 175 */ {"DUSFX", "Dust Flux", "mg/(m^2s)", UC_NONE}, + /* 176 */ {"VERDIF", "Vertical Diffusion Coefficient Kz term", "cm^2/s", UC_NONE}, + /* 177 */ {"VISBY", "Visibility", "code_2E", UC_NONE}, + /* 178 */ {"PNSTWX", "Weather", "code_2F", UC_NONE}, + /* 179 */ {"CLDAM", "Layer Cloud Amount", "%_2G", UC_NONE}, + /* 180 */ {"CLDBAS", "Layer Cloud Base", "code_2H", UC_NONE}, + /* 181 */ {"CLDTOP", "Layer Cloud Top", "code_2H", UC_NONE}, + /* 182 */ {"CLDTP", "Layer Cloud Type", "code_2I", UC_NONE}, + /* 183 */ {"UPDTTM", "Time of last update from base time", "minutes", UC_NONE}, + /* 184 */ {"SORDAT", "Source Data", "-", UC_NONE}, + /* 185 */ {"ICTOP", "Icing Top", "m", UC_NONE}, + /* 186 */ {"ICBAS", "Icing Base", "m", UC_NONE}, + /* 187 */ {"ICINT", "Icing", "0=none,1=light,2=moderate,3=severe", UC_NONE}, + /* 188 */ {"TBTOP", "Turbulence Top", "m", UC_NONE}, + /* 189 */ {"TBBAS", "Turbulence Base", "m", UC_NONE}, + /* 190 */ {"TBINT", "Turbulence", + "0=none,1=light,2=moderate,3=severe,4=extreme", UC_NONE}, + /* 191 */ {"PSTAR", "Pstar (sfc perssure - model top pressure)", "cbar", + UC_NONE}, + /* 192 */ {"PRSPRT", "Pressure perturbation", "Pa", UC_NONE}, + /* 193 */ {"PBLREG", "PBL Regime", + "1=stable,2=mechanicallydriven,3=forced,4=free", UC_NONE}, + /* 194 */ {"FRIVEL", "Friction velocity", "m/s", UC_NONE}, + /* 195 */ {"GRAPL", "Graupel", "kg/kg", UC_NONE}, + /* 196 */ {"ICCON", "Number concentration of ice", "number/m^3", UC_NONE}, + /* 197 */ {"ATRAD", "Atmospheric radiative tendency", "K/day", UC_NONE}, + /* 198 */ {"TKE", "Turbulent Kenetic Energy", "J/kg", UC_NONE}, + /* 199 */ {"MAPSCL", "Map scale factor", "-", UC_NONE}, + /* 200 */ {"UPSLTP", "Upper Layer Soil Temp", "K", UC_NONE}, + /* 201 */ {"UPSLMS", "Upper Layer Soil Moisture", "g/m^3(noDim*10^3)", + UC_NONE}, + /* 202 */ {"LOSLMS", "Lower Layer Soil Moisture", "g/m^3(noDim*10^3)", + UC_NONE}, + /* 203 */ {"MRGPRP", "Merged Precipitation", "mm/24hrs", UC_NONE}, + /* 204 */ {"EVAPTN", "Evapotranspiration", "mm/24hrs(mm*10^2)", UC_NONE}, + /* 205 */ {"SOILTP", "Soil Type", "code_2K", UC_NONE}, + /* 206 */ {"VOLASH", "Volcanic Ash", "1=present,0=absent", UC_NONE}, + /* 207 */ {"KETI", "Knapp-Ellrod Turbulence Index", + "0=smooth,1=light,2=moderate,3=severe", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"ALSTG", "Altimeter Setting", "inches Hg", UC_NONE}, + /* 210 */ {"KX", "K index", "K", UC_NONE}, + /* 211 */ {"KOX", "KO index", "K", UC_NONE}, + /* 212 */ {"TTX", "Total totals index", "K", UC_NONE}, + /* 213 */ {"SX", "Sweat index", "-", UC_NONE}, + /* 214 */ {"CAPE", "Convective available potential eng.", "J/kg", UC_NONE}, + /* 215 */ {"CIN", "Convective inhibition", "J/kg", UC_NONE}, + /* 216 */ {"SRHEL", "Storm relative helicity", "J/kg", UC_NONE}, + /* 217 */ {"EHI", "Energy helicity index", "-", UC_NONE}, + /* 218 */ {"ILW", "Integrated liquid water", "g/m^2", UC_NONE}, + /* 219 */ {"COND", "Condensate", "kg/kg", UC_NONE}, + /* 220 */ {"CWMR", "Cloud water mixing ratio", "kg/kg", UC_NONE}, + /* 221 */ {"IWMR", "Ice water mixing ratio", "kg/kg", UC_NONE}, + /* 222 */ {"RWMR", "Rain water mixing ratio", "kg/kg", UC_NONE}, + /* 223 */ {"SWMR", "Snow mixing ratio", "kg/kg", UC_NONE}, + /* 224 */ {"HEATX", "Heat index", "K", UC_NONE}, + /* 225 */ {"MCONV", "Horizontal moisture convergence", "kg/kg/s", UC_NONE}, + /* 226 */ {"TB", "Turbulence (Intensity)", + "0=smooth,1=light,2=moderate,3=severe", UC_NONE}, + /* 227 */ {"CLDB", "Cloud base", "m", UC_NONE}, + /* 228 */ {"CLDT", "Cloud top", "m", UC_NONE}, + /* 229 */ {"TCLDCV", "Total cloud cover", "%", UC_NONE}, + /* 230 */ {"LAT", "Latitude", "deg", UC_NONE}, + /* 231 */ {"LON", "Longitude", "deg", UC_NONE}, + /* 232 */ {"THKNS", "Thickness", "m", UC_NONE}, + /* 233 */ {"TERHT", "Model terrain height", "m", UC_NONE}, + /* 234 */ {"WCHILL", "Wind Chill", "F", UC_NONE}, + /* 235 */ {"DVAL", "Height D-values", "feet", UC_NONE}, + /* 236 */ {"MXRH", "Maximum relative humidity", "%", UC_NONE}, + /* 237 */ {"MXABSH", "Maximum absolute humidity", "g/m^3", UC_NONE}, + /* 238 */ {"MXWIND", "Maximum wind speed", "m/s", UC_NONE}, + /* 239 */ {"MNDEPR", "Minimum dewpoint depression", "K", UC_K2F}, + /* 240 */ {"TOTACP", "Total accumulated precipitation", "kg/m^2", UC_NONE}, + /* 241 */ {"LNDUSE", "Land-use", + "1=urban,2=dryland,3=irrigated,4=mixed,5=crop/grassland," + "6=crop/woodland,7=grassland,8=shrubland,9=grass/shrubland," + "10=savanna,11=deciduous/broad,12=deciduous/needle," + "13=evergreen/broad,14=evergreen/needle,15=forest,16=water," + "17=HerbaceousWetland,18=WoodedWetland,19=Baren," + "20=HerbaceousTundra,21=WoodedTundra,22=MixedTundra," + "23=BareTundra,24=Snow", UC_NONE}, + /* 242 */ {"INCSNOF", "Snowfall", "in", UC_NONE}, + /* 243 */ {"TOTSNO", "Total snowfall", "in", UC_NONE}, + /* 244 */ {"PCPTYPE", "Precipitation type", + "0=none,1=rain,2=TRW,3=ZR,4=mixedIce,5=snow,6=SvrTRW", UC_NONE}, + /* 245 */ {"ICING", "Icing (Intensity)", + "0=none,1=light,2=moderate,3=severe", UC_NONE}, + /* 246 */ {"RDREF", "Radio Refractivity", "-", UC_NONE}, + /* 247 */ {"VRPOTP", "Virtual Potential Temperature", "K", UC_NONE}, + /* 248 */ {"LSI", "Lid Strength Index", "K", UC_NONE}, + /* 249 */ {"RADRF", "Radar Reflectivity", "db", UC_NONE}, + /* 250 */ {"LGIFG", "Light Icing Flag", "0=off,1=on", UC_NONE}, + /* 251 */ {"MDIFG", "Moderate Icing Flag", "0=off,1=on", UC_NONE}, + /* 252 */ {"SVIFG", "Severe Icing Flag", "0=off,1=on", UC_NONE}, + /* 253 */ {"LGTFG", "Light Turbulence Flag", "0=off,1=on", UC_NONE}, + /* 254 */ {"MDTFG", "Moderate Turbulence Flag", "0=off,1=on", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; +/* *INDENT-ON* */ + +/* AFWA center = 57, subcenter = 1 */ +/* Updated last on 7/22/2004 */ +/* *INDENT-OFF* */ +GRIB1ParmTable parm_table_afwa_001[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "m^2K/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice cover (ice=1;no ice=0)", "proportion", UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"3HRPCP", "Average 3hr Precip.", "mm/hr", UC_NONE}, + /* 129 */ {"AERTP", "Aerosol type", "(0=none, 1=present)", UC_NONE}, + /* 130 */ {"HSL", "Hours since land", "hours", UC_NONE}, + /* 131 */ {"var131", "undefined", "-", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"VERVEL", "Pressure Vertical Velocity", "hPa/s", UC_NONE}, + /* 134 */ {"DENALT", "Density Altitude", "m", UC_NONE}, + /* 135 */ {"WBZHGT", "Wet Bulb Zero Height", "m", UC_NONE}, + /* 136 */ {"HAIL", "Hail", "cm", UC_NONE}, + /* 137 */ {"PBLHGT", "Planetary Boundary Layer (PBL) Height", "m", UC_NONE}, + /* 138 */ {"TSMAX", "Thunderstorm Max Tops", "m", UC_NONE}, + /* 139 */ {"TSCOV", "Thunderstorm coverage", + "(0=none,1=isolated1-2%,2=few3-5%,3=scattered16-45%,4=numerous)", + UC_NONE}, + /* 140 */ {"CLDCIG", "Ceiling", "m", UC_NONE}, + /* 141 */ {"WGST", "Wind speed of gusts", "m", UC_NONE}, + /* 142 */ {"UGST", "u wind of gusts", "m/s", UC_NONE}, + /* 143 */ {"VGST", "v wind of gusts", "m/s", UC_NONE}, + /* 144 */ {"PRECAT", "Precip Category", "code?", UC_NONE}, + /* 145 */ {"CT1TOP", "Contrail Engine Type 1 Top", "m", UC_NONE}, + /* 146 */ {"CT1BAS", "Contrail Engine Type 1 Base", "m", UC_NONE}, + /* 147 */ {"CT1INT", "Contrail Engine Type 1 Intensity", "0=none,1=present", + UC_NONE}, + /* 148 */ {"CT2TOP", "Contrail Engine Type 2 Top", "m", UC_NONE}, + /* 149 */ {"CT2BAS", "Contrail Engine Type 2 Base", "m", UC_NONE}, + /* 150 */ {"CT2INT", "Contrail Engine Type 2 Intensity", "0=none,1=present", + UC_NONE}, + /* 151 */ {"CT3TOP", "Contrail Engine Type 3 Top", "m", UC_NONE}, + /* 152 */ {"CT3BAS", "Contrail Engine Type 3 Base", "m", UC_NONE}, + /* 153 */ {"CT3INT", "Contrail Engine Type 3 Intensity", "0=none,1=present", + UC_NONE}, + /* 154 */ {"IRTRAN", "IR Transmissivity (IWA vs TDA's)", "%", UC_NONE}, + /* 155 */ {"ABSHUM", "Absolute Humidity", "g/m^3", UC_NONE}, + /* 156 */ {"IRVIS", "IR Visibility", "m", UC_NONE}, + /* 157 */ {"SNCVR", "Snow Cover", "0=noSnow,1=snow", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"POVORT", "Potential Vorticity", "10^-6xm^2/sK K/g", UC_NONE}, + /* 162 */ {"SEATP", "Sea Surface Temp", ".1Deg C", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"TPI", "Thunderstorm Potential Indicator", "-", UC_NONE}, + /* 166 */ {"SVTFG", "Severe Turbulence Flag (series with 250-254)", + "0=off,1=on", UC_NONE}, + /* 167 */ {"RAFG", "Precipitation Rain Flag", "0=off,1=on", UC_NONE}, + /* 168 */ {"TSFG", "Precipitation Thunderstorm Flag", "0=off,1=on", UC_NONE}, + /* 169 */ {"SVTSFG", "Precipitation Severe TS Flag", "0=off,1=on", UC_NONE}, + /* 170 */ {"SNFG", "Precipitation Snow Flag", "0=off,1=on", UC_NONE}, + /* 171 */ {"MXDFG", "Precipitation Mixed Flag", "0=off,1=on", UC_NONE}, + /* 172 */ {"IPFG", "Precipitation Ice Pellets Flag", "0=off,1=on", UC_NONE}, + /* 173 */ {"ZRAFG", "Precipitation Freezing Rain Flag", "0=off,1=on", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"VISBY", "Visibility", "code_2E", UC_NONE}, + /* 178 */ {"PWX", "Weather", "code_2F", UC_NONE}, + /* 179 */ {"CLDAMT", "Layer Cloud Amount", "%", UC_NONE}, + /* 180 */ {"CLDBAS", "Layer Cloud Base", "m", UC_NONE}, + /* 181 */ {"CLDTOP", "Layer Cloud Top", "m", UC_NONE}, + /* 182 */ {"CLDTP", "Layer Cloud Type", "code_2I", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"ICTOP", "Icing Top", "m", UC_NONE}, + /* 186 */ {"ICBAS", "Icing Base", "m", UC_NONE}, + /* 187 */ {"ICINT", "Icing", "0=none,1=light,2=moderate,3=severe", UC_NONE}, + /* 188 */ {"TBTOP", "Turbulence Top", "m", UC_NONE}, + /* 189 */ {"TBBAS", "Turbulence Base", "m", UC_NONE}, + /* 190 */ {"TBINT", "Turbulence", + "0=none,1=light,2=moderate,3=severe,4=extreme", UC_NONE}, + /* 191 */ {"PSTAR", "Pstar (sfc perssure - model top pressure)", "cbar", + UC_NONE}, + /* 192 */ {"PRESPB", "Pressure perturbation", "Pa", UC_NONE}, + /* 193 */ {"PBLREG", "PBL Regime", + "1=stable,2=mechanicallyDriven,3=forced,4=free", UC_NONE}, + /* 194 */ {"FRIVEL", "Friction velocity", "m/s", UC_NONE}, + /* 195 */ {"GRAPL", "Graupel", "kg/kg", UC_NONE}, + /* 196 */ {"ICCON", "Number concentration of ice", "number/m^3", UC_NONE}, + /* 197 */ {"ATRAD", "Atmospheric radiative tendency", "K/day", UC_NONE}, + /* 198 */ {"TKE", "Turbulent Kenetic Energy", "J/kg", UC_NONE}, + /* 199 */ {"MAPSCL", "Map scale factor", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"KETI", "Knapp-Ellrod Turbulence Index", + "0=smooth,1=light,2=moderate,3=severe", UC_NONE}, + /* 208 */ {"PANIND", "Panofsky Turbulence Index", "-200-500", UC_NONE}, + /* 209 */ {"ALSTG", "Altimeter Setting", "inches Hg", UC_NONE}, + /* 210 */ {"KX", "K index", "K", UC_NONE}, + /* 211 */ {"KOX", "KO index", "K", UC_NONE}, + /* 212 */ {"TTX", "Total totals index", "K", UC_NONE}, + /* 213 */ {"SX", "Sweat index", "-", UC_NONE}, + /* 214 */ {"CAPE", "Convective available potential eng.", "J/kg", UC_NONE}, + /* 215 */ {"CIN", "Convective inhibition", "J/kg", UC_NONE}, + /* 216 */ {"SRHEL", "Storm relative helicity", "J/kg", UC_NONE}, + /* 217 */ {"EHI", "Energy helicity index", "-", UC_NONE}, + /* 218 */ {"ILW", "Integrated liquid water", "g/m^2", UC_NONE}, + /* 219 */ {"COND", "Condensate", "kg/kg", UC_NONE}, + /* 220 */ {"CWMR", "Cloud water mixing ratio", "kg/kg", UC_NONE}, + /* 221 */ {"IWMR", "Ice water mixing ratio", "kg/kg", UC_NONE}, + /* 222 */ {"RWMR", "Rain water mixing ratio", "kg/kg", UC_NONE}, + /* 223 */ {"SWMR", "Snow mixing ratio", "kg/kg", UC_NONE}, + /* 224 */ {"HEATX", "Heat index", "K", UC_NONE}, + /* 225 */ {"MCONV", "Horizontal moisture convergence", "kg/kg/s", UC_NONE}, + /* 226 */ {"TB", "Turbulence (Intensity)", + "0=smooth,1=light,2=moderate,3=severe", UC_NONE}, + /* 227 */ {"CLDB", "Cloud base", "m", UC_NONE}, + /* 228 */ {"CLDT", "Cloud top", "m", UC_NONE}, + /* 229 */ {"TCLDCV", "Total cloud cover", "%", UC_NONE}, + /* 230 */ {"LAT", "Latitude", "deg", UC_NONE}, + /* 231 */ {"LON", "Longitude", "deg", UC_NONE}, + /* 232 */ {"THKNS", "Thickness", "m", UC_NONE}, + /* 233 */ {"TERHT", "Model terrain height", "m", UC_NONE}, + /* 234 */ {"WCHILL", "Wind Chill", "F", UC_NONE}, + /* 235 */ {"DVAL", "Height D-values", "feet", UC_NONE}, + /* 236 */ {"MXRH", "Maximum relative humidity", "%", UC_NONE}, + /* 237 */ {"MXABSH", "Maximum absolute humidity", "g/m^3", UC_NONE}, + /* 238 */ {"MXWIND", "Maximum wind speed", "m/s", UC_NONE}, + /* 239 */ {"MNDEPR", "Minimum dewpoint depression", "K", UC_K2F}, + /* 240 */ {"TOTACP", "Total accumulated precipitation", "kg/m^2", UC_NONE}, + /* 241 */ {"LNDUSE", "Land-use", + "1=urban,2=dryland3=irrigated,4=mixed,5=crop/grassland," + "6=crop/woodland,7=grassland,8=shrubland,9=grass/shrubland," + "10=savanna,11=deciduous/broad,12=deciduous/needle," + "13=evergreen/broad,14=evergreen/needle,15=forest,16=water," + "17=HerbaceousWetland,18=WoodedWetland,19=Baren," + "20=HerbaceousTundra,21=WoodedTundra,22=MixedTundra," + "23=BareTundra,24=Snow", UC_NONE}, + /* 242 */ {"INCSNOF", "Snowfall", "in", UC_NONE}, + /* 243 */ {"TOTSNO", "Total snowfall", "in", UC_NONE}, + /* 244 */ {"PCPTYPE", "Precipitation type", + "0=none,1=rain,2=TRW,3=ZR,4=mixedIce,5=snow,6=SvrTRW", UC_NONE}, + /* 245 */ {"ICING", "Icing (Intensity)", + "0=none,1=light,2=moderate,3=severe", UC_NONE}, + /* 246 */ {"RDREF", "Radio Refractivity", "-", UC_NONE}, + /* 247 */ {"VRPOTP", "Virtual Potential Temperature", "K", UC_NONE}, + /* 248 */ {"LSI", "Lid Strength Index", "K", UC_NONE}, + /* 249 */ {"RADRF", "Radar Reflectivity", "db", UC_NONE}, + /* 250 */ {"LGIFG", "Light Icing Flag", "0=off,1=on", UC_NONE}, + /* 251 */ {"MDIFG", "Moderate Icing Flag", "0=off,1=on", UC_NONE}, + /* 252 */ {"SVIFG", "Severe Icing Flag", "0=off,1=on", UC_NONE}, + /* 253 */ {"LGTFG", "Light Turbulence Flag", "0=off,1=on", UC_NONE}, + /* 254 */ {"MDTFG", "Moderate Turbulence Flag", "0=off,1=on", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; +/* *INDENT-ON* */ + +/* AFWA center = 57, subcenter = 2 */ +/* Updated last on 7/22/2004 */ +/* *INDENT-OFF* */ +GRIB1ParmTable parm_table_afwa_002[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "m^2K/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice cover (ice=1;no ice=0)", "proportion", UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"var130", "undefined", "-", UC_NONE}, + /* 131 */ {"var131", "undefined", "-", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"DUST", "Dust Concentration", "log(Micrograms/m^3)", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"var141", "undefined", "-", UC_NONE}, + /* 142 */ {"var142", "undefined", "-", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"DNLWF", "Downward longwave radiation flux", "W*m^2", UC_NONE}, + /* 145 */ {"DNSWF", "Downward shortwave radiation flux", "W*m^2", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"GFLUX", "Ground Heat Flux", "W*m^2", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"TRAJX", "X-component of trajectory", + "16th_mesh_gridPts/hr", UC_NONE}, + /* 158 */ {"TRAJY", "Y-component of trajectory", + "16th_mesh_gridPts/hr", UC_NONE}, + /* 159 */ {"TRAJP", "P-component of trajectory", "millibars/hr", UC_NONE}, + /* 160 */ {"TERRID", "Terrain identifier", + "0=freeAtmospher,2=gradiantTrajectory,3=hemisphere", UC_NONE}, + /* 161 */ {"TERR", "Terrain height", "m", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"CLAMT", "Cloud amount (layer)", "%", UC_NONE}, + /* 164 */ {"CLTYP", "Cloud type (layer)", + "1=CB,2=ST,3=SC,4=CU,5=AS,6=Ns,7=AC,8=CS,9=CI,0=noCld", UC_NONE}, + /* 165 */ {"CLIRB", "Cloud Brightness IR", "-", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"var171", "undefined", "-", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"SNODEP", "Snow Depth", "inches", UC_NONE}, + /* 175 */ {"SNOAGE", "Snow Age", "days", UC_NONE}, + /* 176 */ {"SNOCLM", "Snow Depth Climo", "inches", UC_NONE}, + /* 177 */ {"VISBY", "Visibility", "code_2E", UC_NONE}, + /* 178 */ {"PWX", "Weather", "code_2F", UC_NONE}, + /* 179 */ {"CLDAMT", "Layer Cloud Amount", "%", UC_NONE}, + /* 180 */ {"CLDBAS", "Layer Cloud Base", "m", UC_NONE}, + /* 181 */ {"CLDTOP", "Layer Cloud Top", "m", UC_NONE}, + /* 182 */ {"CLDTYP", "Layer Cloud Type", "code_2I", UC_NONE}, + /* 183 */ {"LASUPD", "Time of last update from base time", "minutes", + UC_NONE}, + /* 184 */ {"SORDAT", "Source data", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"PREQF", "Precipitation Quality Factor (Goodness)", "[1-19]", + UC_NONE}, + /* 194 */ {"SATID", "Satellite Identifier (CDFS II uses 200)", + "0=noDate,1-15=F13-F27,16-19=F29-F31,20-39=N14-N33," + "41-57=SOI-SOZ,58-62=ME5-ME9,63-67=GM5-GM9,68-76=MS1-MS9" + "77-85=MT1-MT9,86=UND", UC_NONE}, + /* 195 */ {"SATMRG", "Satellite Merged - CDFS II", + "0=none,1=conventional,2=GOES,3=GOES+conventional,4=DMSP," + "5=DMSP+conventional,6=DMSP+GOES,7=DMSP+GOES+conventional" + "8=Tiros,9=Tiros+Conventional,10=Tiros+Goes," + "11=Tiros+Goes+conventional,12=Tiros+DMSP," + "13=Tiros+DMSP+Conventional,14=Tiros+DMSP+GOES," + "15=Tiros+DMSP+GOES+Conventional", UC_NONE}, + /* 196 */ {"RTPREC", "Estimated precipitation - CDFS II based", "mm/24hrs", + UC_NONE}, + /* 197 */ {"ALPREC", "Estimated precipitation - all source", "mm/24hrs", + UC_NONE}, + /* 198 */ {"SATPRE", + "Estimated precipitation - geostationary satellite based", "mm", + UC_NONE}, + /* 199 */ {"SATPRK", "Geostationary Satellite estimated", + "1=betterThanPresentWx,2=betterThanSSMI,3=betterThanCDFSII," + "4=betterThanClimo,5=worseThanClimo", UC_NONE}, + /* 200 */ {"RELSOL", "Relative soil moisture", "0-1", UC_NONE}, + /* 201 */ {"SOLMST", "Volumetric Soil Moisture (liquid & frozen)", + "m^3/m^3", UC_NONE}, + /* 202 */ {"PRECIP", "Precipitation - real amounts", "mm", UC_NONE}, + /* 203 */ {"MRGPRP", "Precipitation - merged analysis", "mm/24hrs", UC_NONE}, + /* 204 */ {"EVAPTS", "Evapotranspiration - actual", "mm/24hrs", UC_NONE}, + /* 205 */ {"SOLTYP", "Soil Type", + "1=sand,2=loamySand,3=sandyLoam,4=siltLoam,5=silt,6=loam" + "7=sandyClayLoam,8=siltyClayLoam,9=clayLoam,10=sandyClay" + "11=siltyClay,12=clay,13=organic,14=water,15=bedrock,16=other", + UC_NONE}, + /* 206 */ {"VOLASH", "Volcanic Ash", "1=present,0=notPresent", UC_NONE}, + /* 207 */ {"CANOPY", "Plant Canopy moisture content", "mm", UC_NONE}, + /* 208 */ {"EVAPTP", "Evapotranspiration - potential", "mm/hr", UC_NONE}, + /* 209 */ {"WDRUN", "Wind run", "km/24hr", UC_NONE}, + /* 210 */ {"MINRH", "Relative Humidity at minimum temperature", "%", UC_NONE}, + /* 211 */ {"SOLLIQ", "Volumetric Soil Moisture (liquid only)", + "m^3/m^3", UC_NONE}, + /* 212 */ {"VEGTYP", "Vegetation Type Category", + "1=urban,2=dryland,3=irrigated,4=mixed,5=crop/grassland," + "6=crop/woodland,7=grassland,8=shrubland,9=grass/shrubland," + "10=savanna,11=deciduous/broad,12=deciduous/needle," + "13=evergreen/broad,14=evergreen/needle,15=forest,16=water," + "17=HerbaceousWetland,18=WoodedWetland,19=Baren," + "20=HerbaceousTundra,21=WoodedTundra,22=MixedTundra," + "23=BareTundra,24=Snow", UC_NONE}, + /* 213 */ {"VGREEN", "Vegetation Greenness", "%", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"CLDB", "Cloud base", "m", UC_NONE}, + /* 228 */ {"CLDT", "Cloud top", "m", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"BGRUN", "Baseflow - groundwater runoff", "mm", UC_NONE}, + /* 235 */ {"SSRUN", "Storm surface runoff", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; +/* *INDENT-ON* */ + +/* AFWA center = 57, subcenter = 3 */ +/* Updated last on 7/22/2004 */ +/* *INDENT-OFF* */ +GRIB1ParmTable parm_table_afwa_003[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "m^2K/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice cover (ice=1;no ice=0)", "proportion", UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"EDEN", "Electron Density", "1/cm^3", UC_NONE}, + /* 129 */ {"OPLUSDEN", "Oxygen Ion Density", "1/cm^3", UC_NONE}, + /* 130 */ {"HPLUSDEN", "Hydrogen Density", "1/cm^3", UC_NONE}, + /* 131 */ {"N2O2DEN", "Sum of Molecular Nitrogen and Oxygen Density", + "1/cm^3", UC_NONE}, + /* 132 */ {"ITEMP", "Ion Temperature", "K", UC_NONE}, + /* 133 */ {"ETEMP", "Electron Temperature", "K", UC_NONE}, + /* 134 */ {"FOF2", "F2 Layer Critical Frequency", "MHz", UC_NONE}, + /* 135 */ {"HMF2", "F2 Layer Height", "Km", UC_NONE}, + /* 136 */ {"FOF1", "F1 Layer Critical Frequency", "MHz", UC_NONE}, + /* 137 */ {"HMF1", "F1 Layer Height", "Km", UC_NONE}, + /* 138 */ {"FOE", "E Layer Critical Frequency", "MHz", UC_NONE}, + /* 139 */ {"HME", "E Layer Height", "Km", UC_NONE}, + /* 140 */ {"TEC", "Total Electron Content", "TECU", UC_NONE}, + /* 141 */ {"MLT", "Magnetic Local Time", "hours", UC_NONE}, + /* 142 */ {"GMLAT", "Geomagnetic Latitude", "Deg", UC_NONE}, + /* 143 */ {"GMLON", "Geomagnetic Longitude", "Deg", UC_NONE}, + /* 144 */ {"ALTIONO", "Altitude", "Km", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"var155", "undefined", "-", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"var165", "undefined", "-", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"var171", "undefined", "-", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; +/* *INDENT-ON* */ + +/* AFWA center = 57, subcenter = 10 */ +/* Updated last on 7/30/2004 */ +/* *INDENT-OFF* */ +GRIB1ParmTable parm_table_afwa_010[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "m^2K/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice cover (ice=1;no ice=0)", "proportion", UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"DTEMP", "Standard Deviation of Temperature", "K", UC_NONE}, + /* 129 */ {"DDEW", "Standard Deviation of Dew Point", "K", UC_NONE}, + /* 130 */ {"DPREC", "Standard Deviation of Precipitation", "mm", UC_NONE}, + /* 131 */ {"DMXTP", "Standard Deviation of Max Temperature", "K", UC_NONE}, + /* 132 */ {"DMNTP", "Standard Deviation of Min Temperature", "K", UC_NONE}, + /* 133 */ {"DSNFL", "Standard Deviation of Snowfall", "m", UC_NONE}, + /* 134 */ {"DPALT", "Standard Deviation of Pressure Altitude", "m", UC_NONE}, + /* 135 */ {"DDALT", "Standard Deviation of Density Altitude", "m", UC_NONE}, + /* 136 */ {"MXDEW", "Max Dew Point", "K", UC_NONE}, + /* 137 */ {"MNDEW", "Min Dew Point", "K", UC_NONE}, + /* 138 */ {"ALSTG", "Altimeter Setting", "hPa", UC_NONE}, + /* 139 */ {"MXALST", "Max Altimeter Setting", "hPa", UC_NONE}, + /* 140 */ {"MNALST", "Min Altimeter Setting", "hPa", UC_NONE}, + /* 141 */ {"PFREQ", "Precipitation Frequency", "%", UC_NONE}, + /* 142 */ {"MXPREC", "Max Precipitation", "mm", UC_NONE}, + /* 143 */ {"SFREQ", "Snowfall Frequency", "%", UC_NONE}, + /* 144 */ {"MXSNO", "Max Snowfall", "m", UC_NONE}, + /* 145 */ {"MXWSP", "Max Wind Speed", "m/s", UC_NONE}, + /* 146 */ {"MNWSP", "Min Wind Speed", "m/s", UC_NONE}, + /* 147 */ {"MXABH", "Max Absolute Humidity", "g/m^3", UC_NONE}, + /* 148 */ {"MNABH", "Min Absolute Humidity", "g/m^3", UC_NONE}, + /* 149 */ {"MNPREC", "Min Precipitation", "mm", UC_NONE}, + /* 150 */ {"NEWND", "NE Wind Direction Occurance", "%", UC_NONE}, + /* 151 */ {"EWND", "E Wind Direction Occurance", "%", UC_NONE}, + /* 152 */ {"SEWND", "SE Wind Direction Occurance", "%", UC_NONE}, + /* 153 */ {"SWND", "S Wind Direction Occurance", "%", UC_NONE}, + /* 154 */ {"SWWND", "SW Wind Direction Occurance", "%", UC_NONE}, + /* 155 */ {"WWND", "W Wind Direction Occurance", "%", UC_NONE}, + /* 156 */ {"NWWND", "NW Wind Direction Occurance", "%", UC_NONE}, + /* 157 */ {"NWND", "N Wind Direction Occurance", "%", UC_NONE}, + /* 158 */ {"NEWSP", "NE Wind Speed", "m/s", UC_NONE}, + /* 159 */ {"EWSP", "E Wind Speed", "m/s", UC_NONE}, + /* 160 */ {"SEWSP", "SE Wind Speed", "m/s", UC_NONE}, + /* 161 */ {"SWSP", "S Wind Speed", "m/s", UC_NONE}, + /* 162 */ {"SWWSP", "SW Wind Speed", "m/s", UC_NONE}, + /* 163 */ {"WWSP", "W Wind Speed", "m/s", UC_NONE}, + /* 164 */ {"NWWSP", "NW Wind Speed", "m/s", UC_NONE}, + /* 165 */ {"NWSP", "N Wind Speed", "m/s", UC_NONE}, + /* 166 */ {"P01OC", "Precipitation Greater than .01 inch", "-", UC_NONE}, + /* 167 */ {"P4OC", "Precipitation Greater than .4 inch", "-", UC_NONE}, + /* 168 */ {"MNSNO", "Min Snowfall", "m", UC_NONE}, + /* 169 */ {"PALT", "Pressure Altitude", "m", UC_NONE}, + /* 170 */ {"DALT", "Density Altitude", "m", UC_NONE}, + /* 171 */ {"V7FREQ", "Frequency Visibility Less than 9999 meters", "%", UC_NONE}, + /* 172 */ {"V6FREQ", "Frequency Visibility Less than 9000 meters", "%", UC_NONE}, + /* 173 */ {"V5FREQ", "Frequency Visibility Less than 8000 meters", "%", UC_NONE}, + /* 174 */ {"V4FREQ", "Frequency Visibility Less than 6000 meters", "%", UC_NONE}, + /* 175 */ {"V3FREQ", "Frequency Visibility Less than 4800 meters", "%", UC_NONE}, + /* 176 */ {"V2FREQ", "Frequency Visibility Less than 3200 meters", "%", UC_NONE}, + /* 177 */ {"V1FREQ", "Frequency Visibility Less than 1600 meters", "%", UC_NONE}, + /* 178 */ {"V0FREQ", "Frequency Visibility Less than 800 meters", "%", UC_NONE}, + /* 179 */ {"OBFREQ", "Frequency Obstruction to Vision", "%", UC_NONE}, + /* 180 */ {"MXGST", "Max Wind Gust", "m/s", UC_NONE}, + /* 181 */ {"G25FRE", "Frequency Max Wind Gust Greater than 25kt", "%", UC_NONE}, + /* 182 */ {"G35FRE", "Frequency Max Wind Gust Greater than 35kt", "%", UC_NONE}, + /* 183 */ {"G45FRE", "Frequency Max Wind Gust Greater than 50kt", "%", UC_NONE}, + /* 184 */ {"TRANS", "Transmissivity (8-12 um)", "frac", UC_NONE}, + /* 185 */ {"TFREQ", "Frequency Thunderstorms", "%", UC_NONE}, + /* 186 */ {"TBFREQ", "Frequency Turbulence", "%", UC_NONE}, + /* 187 */ {"ICFREQ", "Frequency Icing", "%", UC_NONE}, + /* 188 */ {"IPFREQ", "Frequency Ice Pellets Precipitation", "%", UC_NONE}, + /* 189 */ {"ZRFREQ", "Frequency Freezing Rain Precipitation", "%", UC_NONE}, + /* 190 */ {"HZFREQ", "Frequency Haze", "%", UC_NONE}, + /* 191 */ {"FGFREQ", "Frequency Fog", "%", UC_NONE}, + /* 192 */ {"BDFREQ", "Frequency Blowing Dust", "%", UC_NONE}, + /* 193 */ {"BSFREQ", "Frequency Blowing Snow", "%", UC_NONE}, + /* 194 */ {"T90FRQ", "Frequency Temperature Above 90 F", "%", UC_NONE}, + /* 195 */ {"T32FRQ", "Frequency Temperature Above 32 F", "%", UC_NONE}, + /* 196 */ {"T0FRQ", "Frequency Temperature Above 0 F", "%", UC_NONE}, + /* 197 */ {"CLDBAS", "Cloud Base", "m", UC_NONE}, + /* 198 */ {"CLDCIG", "Cloud Ceiling", "m", UC_NONE}, + /* 199 */ {"CLDTOP", "Cloud Top", "m", UC_NONE}, + /* 200 */ {"CIGFRQ", "Frequency Ceiling", "%", UC_NONE}, + /* 201 */ {"CTPFRQ", "Frequency Cloud Tops", "%", UC_NONE}, + /* 202 */ {"CV107F", "Frequency Ceiling/Visibility Less than 10kft/7mi", + "%", UC_NONE}, + /* 203 */ {"CV33F", "Frequency Ceiling/Visibility Less than 3kft/3mi", "%", + UC_NONE}, + /* 204 */ {"CV153F", "Frequency Ceiling/Visibility Less than 1.5kft/3mi", + "%", UC_NONE}, + /* 205 */ {"CV12F", "Frequency Ceiling/Visibility Less than 1kft/2mi", + "%", UC_NONE}, + /* 206 */ {"CV55F", "Frequency Ceiling/Visibility Less than .5kft/.5mi", + "%", UC_NONE}, + /* 207 */ {"CV1510F", "Frequency Ceiling/Visibility Less than 15kft/10mi", + "%", UC_NONE}, + /* 208 */ {"CV105F", "Frequency Ceiling/Visibility Less than 10kft/5mi", + "%", UC_NONE}, + /* 209 */ {"CV52F", "Frequency Ceiling/Visibility Less than .5kft/2mi", + "%", UC_NONE}, + /* 210 */ {"CV15F", "Frequency Ceiling/Visibility Less than 1kft/.5mi", + "%", UC_NONE}, + /* 211 */ {"CV2015F", "Frequency Ceiling/Visibility Less than 20kft/15mi", + "%", UC_NONE}, + /* 212 */ {"ICCAT", "Icing Category", "cat", UC_NONE}, + /* 213 */ {"TBCAT", "Turbulence Category", "cat", UC_NONE}, + /* 214 */ {"SOLSAT", "Soil Moisture Saturation", "%", UC_NONE}, + /* 215 */ {"MAXRH", "Max Relative Humidity", "%", UC_NONE}, + /* 216 */ {"MINRH", "Min Relative Humidity", "%", UC_NONE}, + /* 217 */ {"MXSND", "Max Snow Depth", "m", UC_NONE}, + /* 218 */ {"MNSND", "Min Snow Depth", "m", UC_NONE}, + /* 219 */ {"MXSSAT", "Max Soil Moisture Saturation", "%", UC_NONE}, + /* 220 */ {"MNSSAT", "Min Soil Moisture Saturation", "%", UC_NONE}, + /* 221 */ {"ALCIG", "Frequency All Ceilings", "%", UC_NONE}, + /* 222 */ {"FQFZG", "Frequency Frozen Ground", "%", UC_NONE}, + /* 223 */ {"ABSHUM", "Absolute Humidity", "g*m^3", UC_NONE}, + /* 224 */ {"PRECIP", "Precipitation", "mm", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; +/* *INDENT-ON* */ + +/* AFWA center = 57, subcenter = 11 */ +/* Updated last on 7/30/2004 */ +/* *INDENT-OFF* */ +GRIB1ParmTable parm_table_afwa_011[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "m^2K/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice cover (ice=1;no ice=0)", "proportion", UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"HSL", "Hours since land", "hours", UC_NONE}, + /* 131 */ {"var131", "undefined", "-", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"var141", "undefined", "-", UC_NONE}, + /* 142 */ {"var142", "undefined", "-", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"var144", "undefined", "-", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"var155", "undefined", "-", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"var165", "undefined", "-", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"var171", "undefined", "-", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"TPERR", "Temperature Error Estimate", "K", UC_NONE}, + /* 201 */ {"MRERR", "Mixing Ratio Error Estimate", "kg/kg", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"LNDUSE", "Land-use", + "1=urban,2=dryland,3=irrigated,4=mixed,5=crop/grassland," + "6=crop/woodland,7=grassland,8=shrubland,9=grass/shrubland," + "10=savanna,11=deciduous/broad,12=deciduous/needle", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; +/* *INDENT-ON* */ + +/* athens grid */ +/* Updated last on 3/13/2006 */ +/* *INDENT-OFF* */ +GRIB1ParmTable parm_table_athens[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "m^2K/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice cover (ice=1;no ice=0)", "proportion", UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"CLW", "Cloud water", "Kg/m^2", UC_NONE}, + /* 129 */ {"RNW", "Total precipitation", "Kg/m^2", UC_NONE}, + /* 130 */ {"RadTend", "Atmospheric radiative tendency", "K/day", UC_NONE}, + /* 131 */ {"PP", "Pressure perturbation", "Pa", UC_NONE}, + /* 132 */ {"PSTARCRS", "Surface Pressure minus PTOP", "-", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"TERRAIN", "Terrain Elevation", "m", UC_NONE}, + /* 136 */ {"MAPFACCR", "Map Scale Factor", "-", UC_NONE}, + /* 137 */ {"MAPFACDT", "Map Scale Factor", "-", UC_NONE}, + /* 138 */ {"CORIOLIS", "Coriolis Parameter", "1/s", UC_NONE}, + /* 139 */ {"ResTemp", "Infinite Reservoir Slab Temperature", "K", UC_NONE}, + /* 140 */ {"LATITCRS", "Latitude cross points (south negative)", "Degrees", UC_NONE}, + /* 141 */ {"LONGICRS", "Longitude cross points (west negative)", "Degrees", UC_NONE}, + /* 142 */ {"LandUse", "Land use category", "-", UC_NONE}, + /* 143 */ {"Regime", "PBL Regime", "-", UC_NONE}, + /* 144 */ {"UST", "Frictional Velocity", "m/s", UC_NONE}, + /* 145 */ {"SWDOWN", "Surface Downward Shortwave Radiation", "W/m^2", UC_NONE}, + /* 146 */ {"LWDOWN", "Surface Downward Longwave Radiation", "W/m^2", UC_NONE}, + /* 147 */ {"SWOUT", "Top Outgoing Shortwave Radiation", "W/m^2", UC_NONE}, + /* 148 */ {"LWOUT", "Top Outgoing Longwave Radiation", "W/m^2", UC_NONE}, + /* 149 */ {"SOIL_T", "Soil Temperature in Layer N", "K", UC_NONE}, + /* 150 */ {"LATITDOT", "Latitude dot points", "Degrees", UC_NONE}, + /* 151 */ {"LONGIDOT", "Longitude dot points", "Degrees", UC_NONE}, + /* 152 */ {"PRESSURE", "Pressure", "-", UC_NONE}, + /* 153 */ {"SUMFB", "SUMFB", "-", UC_NONE}, + /* 154 */ {"SPSRC", "SPSRC", "-", UC_NONE}, + /* 155 */ {"var155", "undefined", "-", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"ICE", "Ice cloud mixing ratio", "kg/kg", UC_NONE}, + /* 161 */ {"SNOW", "Snow mixing ratio", "kg/kg", UC_NONE}, + /* 162 */ {"GRAUPEL", "Graupel", "kg/kg", UC_NONE}, + /* 163 */ {"NCI", "Number concentration of ice", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"var165", "undefined", "-", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"var171", "undefined", "-", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; +/* *INDENT-ON* */ + +/* Canada Met grid */ +/* see: + * http://weatheroffice.ec.gc.ca/grib/High-resolution_GRIB_e.html#convention + */ +/* Updated last on 11/13/2006 */ +/* *INDENT-OFF* */ +GRIB1ParmTable parm_table_cmc[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "m^2K/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice cover (ice=1;no ice=0)", "proportion", UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat net flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"var130", "undefined", "-", UC_NONE}, + /* 131 */ {"var131", "undefined", "-", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"var141", "undefined", "-", UC_NONE}, + /* 142 */ {"var142", "undefined", "-", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"SOILW", "Volumetric Soil Moisture Content", "fraction", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"var155", "undefined", "-", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"var165", "undefined", "-", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"var171", "undefined", "-", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"DSWRF", "Downward Short Wave Radiation Flux", "W/m^2", UC_NONE}, + /* 205 */ {"DLWRF", "Downward Long Wave Radiation Flux", "W/m^2", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"USWRF", "Upward Short Wave Radiation Flux", "W/m^2", UC_NONE}, + /* 212 */ {"ULWRF", "Upward Long Wave Radiation Flux", "W/m^2", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; +/* *INDENT-ON* */ + +/* UNDEFINED GRID */ +/* Updated last on 7/30/2004 */ +/* *INDENT-OFF* */ +GRIB1ParmTable parm_table_undefined[256] = { + /* 0 */ {"var0", "undefined", "-", UC_NONE}, + /* 1 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 2 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 3 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 4 */ {"PVORT", "Potential vorticity", "m^2K/kg/s", UC_NONE}, + /* 5 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", + UC_NONE}, + /* 6 */ {"GP", "Geopotential", "m^2/s^2", UC_NONE}, + /* 7 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 8 */ {"DIST", "Geometric height", "m", UC_NONE}, + /* 9 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 10 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, + /* 11 */ {"TMP", "Temperature", "K", UC_K2F}, + /* 12 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 13 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 14 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 15 */ {"TMAX", "Maximum temperature", "K", UC_K2F}, + /* 16 */ {"TMIN", "Minimum temperature", "K", UC_K2F}, + /* 17 */ {"DPT", "Dew point temperature", "K", UC_K2F}, + /* 18 */ {"DEPR", "Dew point depression", "K", UC_NONE}, /* Delta temp */ + /* 19 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 20 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 21 */ {"RDSP1", "Radar Spectra (1)", "-", UC_NONE}, + /* 22 */ {"RDSP2", "Radar Spectra (2)", "-", UC_NONE}, + /* 23 */ {"RDSP3", "Radar Spectra (3)", "-", UC_NONE}, + /* 24 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* delta temp. */ + /* 25 */ {"TMPA", "Temperature anomaly", "K", UC_NONE}, /* Delta temp */ + /* 26 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 27 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE}, + /* 28 */ {"WVSP1", "Wave Spectra (1)", "-", UC_NONE}, + /* 29 */ {"WVSP2", "Wave Spectra (2)", "-", UC_NONE}, + /* 30 */ {"WVSP3", "Wave Spectra (3)", "-", UC_NONE}, + /* 31 */ {"WDIR", "Wind direction", "deg", UC_NONE}, + /* 32 */ {"WIND", "Wind speed", "m/s", UC_NONE}, + /* 33 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 34 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 35 */ {"STRM", "Stream function", "m^2/s", UC_NONE}, + /* 36 */ {"VPOT", "Velocity potential", "m^2/s", UC_NONE}, + /* 37 */ {"MNTSF", "Montgomery stream function", "m^2/s^2", UC_NONE}, + /* 38 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 39 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, + /* 40 */ {"DZDT", "Vertical velocity (geometric)", "m/s", UC_NONE}, + /* 41 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 42 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 43 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 44 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 45 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 46 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 47 */ {"DIRC", "Direction of current", "deg", UC_NONE}, + /* 48 */ {"SPC", "Speed of current", "m/s", UC_NONE}, + /* 49 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 50 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, + /* 51 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 52 */ {"RH", "Relative humidity", "%", UC_NONE}, + /* 53 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 54 */ {"PWAT", "Precipitable water", "kg/m^2", UC_NONE}, + /* 55 */ {"VAPP", "Vapor pressure", "Pa", UC_NONE}, + /* 56 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 57 */ {"EVP", "Evaporation", "kg/m^2", UC_NONE}, + /* 58 */ {"CICE", "Cloud Ice", "kg/m^2", UC_NONE}, + /* 59 */ {"PRATE", "Precipitation rate", "kg/m^2/s", UC_NONE}, + /* 60 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 61 */ {"APCP", "Total precipitation", "kg/m^2", UC_InchWater}, + /* 62 */ {"NCPCP", "Large scale precipitation", "kg/m^2", UC_NONE}, + /* 63 */ {"ACPCP", "Convective precipitation", "kg/m^2", UC_NONE}, + /* 64 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/m^2/s", UC_NONE}, + /* 65 */ {"WEASD", "Water equiv. of accum. snow depth", "kg/m^2", UC_NONE}, + /* 66 */ {"SNOD", "Snow depth", "m", UC_NONE}, + /* 67 */ {"MIXHT", "Mixed layer depth", "m", UC_NONE}, + /* 68 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 69 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 70 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 71 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, + /* 72 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 73 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 74 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 75 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 76 */ {"CWAT", "Cloud water", "kg/m^2", UC_NONE}, + /* 77 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* Delta Temp. */ + /* 78 */ {"SNOC", "Convective snow", "kg/m^2", UC_NONE}, + /* 79 */ {"SNOL", "Large scale snow", "kg/m^2", UC_NONE}, + /* 80 */ {"WTMP", "Water Temperature", "K", UC_K2F}, + /* 81 */ {"LAND", "Land cover (land=1, sea=0)", "proportion", UC_NONE}, + /* 82 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, + /* 83 */ {"SFCR", "Surface roughness", "m", UC_NONE}, + /* 84 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 85 */ {"TSOIL", "Soil temperature", "K", UC_K2F}, + /* 86 */ {"SOILM", "Soil moisture content", "kg/m^2", UC_NONE}, + /* 87 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 88 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, + /* 89 */ {"DEN", "Density", "kg/m^3", UC_NONE}, + /* 90 */ {"WATR", "Water runoff", "kg/m^2", UC_NONE}, + /* 91 */ {"ICEC", "Ice cover (ice=1;no ice=0)", "proportion", UC_NONE}, + /* 92 */ {"ICETK", "Ice thickness", "m", UC_NONE}, + /* 93 */ {"DICED", "Direction of ice drift", "deg", UC_NONE}, + /* 94 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 95 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 96 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 97 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 98 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, + /* 99 */ {"SNOM", "Snow melt", "kg/m^2", UC_NONE}, + /* 100 */ {"HTSGW", "Significant height of combined wind waves and swell", + "m", UC_NONE}, + /* 101 */ {"WVDIR", "Direction of wind waves (from which)", "deg", UC_NONE}, + /* 102 */ {"WVHGT", "Significant height of wind waves", "m", UC_NONE}, + /* 103 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 104 */ {"SWDIR", "Direction of swell waves", "deg", UC_NONE}, + /* 105 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 106 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 107 */ {"DIRPW", "Primary wave direction", "deg", UC_NONE}, + /* 108 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 109 */ {"DIRSW", "Secondary wave direction", "deg", UC_NONE}, + /* 110 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, + /* 111 */ {"NSWRS", "Net short wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 112 */ {"NLWRS", "Net long wave radiation flux (surface)", + "W/m^2", UC_NONE}, + /* 113 */ {"NSWRT", "Net short wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 114 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/m^2", UC_NONE}, + /* 115 */ {"LWAVR", "Long wave radiation flux", "W/m^2", UC_NONE}, + /* 116 */ {"SWAVR", "Short wave radiation flux", "W/m^2", UC_NONE}, + /* 117 */ {"GRAD", "Global radiation flux", "W/m^2", UC_NONE}, + /* 118 */ {"BRTMP", "Brightness temperature", "K", UC_K2F}, + /* 119 */ {"LWRAD", "Radiance (with respect to wave number)", "W/m/sr", + UC_NONE}, + /* 120 */ {"SWRAD", "Radiance (with respect to wave length)", "W/m^3/sr", + UC_NONE}, + /* 121 */ {"LHTFL", "Latent heat net flux", "W/m^2", UC_NONE}, + /* 122 */ {"SHTFL", "Sensible heat flux", "W/m^2", UC_NONE}, + /* 123 */ {"BLYDP", "Boundary layer dissipation", "W/m^2", UC_NONE}, + /* 124 */ {"UFLX", "Momentum flux, u-component", "N/m^2", UC_NONE}, + /* 125 */ {"VFLX", "Momentum flux, v-component", "N/m^2", UC_NONE}, + /* 126 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 127 */ {"IMGD", "Image data", "-", UC_NONE}, + + /* 128 */ {"var128", "undefined", "-", UC_NONE}, + /* 129 */ {"var129", "undefined", "-", UC_NONE}, + /* 130 */ {"var130", "undefined", "-", UC_NONE}, + /* 131 */ {"var131", "undefined", "-", UC_NONE}, + /* 132 */ {"var132", "undefined", "-", UC_NONE}, + /* 133 */ {"var133", "undefined", "-", UC_NONE}, + /* 134 */ {"var134", "undefined", "-", UC_NONE}, + /* 135 */ {"var135", "undefined", "-", UC_NONE}, + /* 136 */ {"var136", "undefined", "-", UC_NONE}, + /* 137 */ {"var137", "undefined", "-", UC_NONE}, + /* 138 */ {"var138", "undefined", "-", UC_NONE}, + /* 139 */ {"var139", "undefined", "-", UC_NONE}, + /* 140 */ {"var140", "undefined", "-", UC_NONE}, + /* 141 */ {"var141", "undefined", "-", UC_NONE}, + /* 142 */ {"var142", "undefined", "-", UC_NONE}, + /* 143 */ {"var143", "undefined", "-", UC_NONE}, + /* 144 */ {"var144", "undefined", "-", UC_NONE}, + /* 145 */ {"var145", "undefined", "-", UC_NONE}, + /* 146 */ {"var146", "undefined", "-", UC_NONE}, + /* 147 */ {"var147", "undefined", "-", UC_NONE}, + /* 148 */ {"var148", "undefined", "-", UC_NONE}, + /* 149 */ {"var149", "undefined", "-", UC_NONE}, + /* 150 */ {"var150", "undefined", "-", UC_NONE}, + /* 151 */ {"var151", "undefined", "-", UC_NONE}, + /* 152 */ {"var152", "undefined", "-", UC_NONE}, + /* 153 */ {"var153", "undefined", "-", UC_NONE}, + /* 154 */ {"var154", "undefined", "-", UC_NONE}, + /* 155 */ {"var155", "undefined", "-", UC_NONE}, + /* 156 */ {"var156", "undefined", "-", UC_NONE}, + /* 157 */ {"var157", "undefined", "-", UC_NONE}, + /* 158 */ {"var158", "undefined", "-", UC_NONE}, + /* 159 */ {"var159", "undefined", "-", UC_NONE}, + /* 160 */ {"var160", "undefined", "-", UC_NONE}, + /* 161 */ {"var161", "undefined", "-", UC_NONE}, + /* 162 */ {"var162", "undefined", "-", UC_NONE}, + /* 163 */ {"var163", "undefined", "-", UC_NONE}, + /* 164 */ {"var164", "undefined", "-", UC_NONE}, + /* 165 */ {"var165", "undefined", "-", UC_NONE}, + /* 166 */ {"var166", "undefined", "-", UC_NONE}, + /* 167 */ {"var167", "undefined", "-", UC_NONE}, + /* 168 */ {"var168", "undefined", "-", UC_NONE}, + /* 169 */ {"var169", "undefined", "-", UC_NONE}, + /* 170 */ {"var170", "undefined", "-", UC_NONE}, + /* 171 */ {"var171", "undefined", "-", UC_NONE}, + /* 172 */ {"var172", "undefined", "-", UC_NONE}, + /* 173 */ {"var173", "undefined", "-", UC_NONE}, + /* 174 */ {"var174", "undefined", "-", UC_NONE}, + /* 175 */ {"var175", "undefined", "-", UC_NONE}, + /* 176 */ {"var176", "undefined", "-", UC_NONE}, + /* 177 */ {"var177", "undefined", "-", UC_NONE}, + /* 178 */ {"var178", "undefined", "-", UC_NONE}, + /* 179 */ {"var179", "undefined", "-", UC_NONE}, + /* 180 */ {"var180", "undefined", "-", UC_NONE}, + /* 181 */ {"var181", "undefined", "-", UC_NONE}, + /* 182 */ {"var182", "undefined", "-", UC_NONE}, + /* 183 */ {"var183", "undefined", "-", UC_NONE}, + /* 184 */ {"var184", "undefined", "-", UC_NONE}, + /* 185 */ {"var185", "undefined", "-", UC_NONE}, + /* 186 */ {"var186", "undefined", "-", UC_NONE}, + /* 187 */ {"var187", "undefined", "-", UC_NONE}, + /* 188 */ {"var188", "undefined", "-", UC_NONE}, + /* 189 */ {"var189", "undefined", "-", UC_NONE}, + /* 190 */ {"var190", "undefined", "-", UC_NONE}, + /* 191 */ {"var191", "undefined", "-", UC_NONE}, + /* 192 */ {"var192", "undefined", "-", UC_NONE}, + /* 193 */ {"var193", "undefined", "-", UC_NONE}, + /* 194 */ {"var194", "undefined", "-", UC_NONE}, + /* 195 */ {"var195", "undefined", "-", UC_NONE}, + /* 196 */ {"var196", "undefined", "-", UC_NONE}, + /* 197 */ {"var197", "undefined", "-", UC_NONE}, + /* 198 */ {"var198", "undefined", "-", UC_NONE}, + /* 199 */ {"var199", "undefined", "-", UC_NONE}, + /* 200 */ {"var200", "undefined", "-", UC_NONE}, + /* 201 */ {"var201", "undefined", "-", UC_NONE}, + /* 202 */ {"var202", "undefined", "-", UC_NONE}, + /* 203 */ {"var203", "undefined", "-", UC_NONE}, + /* 204 */ {"var204", "undefined", "-", UC_NONE}, + /* 205 */ {"var205", "undefined", "-", UC_NONE}, + /* 206 */ {"var206", "undefined", "-", UC_NONE}, + /* 207 */ {"var207", "undefined", "-", UC_NONE}, + /* 208 */ {"var208", "undefined", "-", UC_NONE}, + /* 209 */ {"var209", "undefined", "-", UC_NONE}, + /* 210 */ {"var210", "undefined", "-", UC_NONE}, + /* 211 */ {"var211", "undefined", "-", UC_NONE}, + /* 212 */ {"var212", "undefined", "-", UC_NONE}, + /* 213 */ {"var213", "undefined", "-", UC_NONE}, + /* 214 */ {"var214", "undefined", "-", UC_NONE}, + /* 215 */ {"var215", "undefined", "-", UC_NONE}, + /* 216 */ {"var216", "undefined", "-", UC_NONE}, + /* 217 */ {"var217", "undefined", "-", UC_NONE}, + /* 218 */ {"var218", "undefined", "-", UC_NONE}, + /* 219 */ {"var219", "undefined", "-", UC_NONE}, + /* 220 */ {"var220", "undefined", "-", UC_NONE}, + /* 221 */ {"var221", "undefined", "-", UC_NONE}, + /* 222 */ {"var222", "undefined", "-", UC_NONE}, + /* 223 */ {"var223", "undefined", "-", UC_NONE}, + /* 224 */ {"var224", "undefined", "-", UC_NONE}, + /* 225 */ {"var225", "undefined", "-", UC_NONE}, + /* 226 */ {"var226", "undefined", "-", UC_NONE}, + /* 227 */ {"var227", "undefined", "-", UC_NONE}, + /* 228 */ {"var228", "undefined", "-", UC_NONE}, + /* 229 */ {"var229", "undefined", "-", UC_NONE}, + /* 230 */ {"var230", "undefined", "-", UC_NONE}, + /* 231 */ {"var231", "undefined", "-", UC_NONE}, + /* 232 */ {"var232", "undefined", "-", UC_NONE}, + /* 233 */ {"var233", "undefined", "-", UC_NONE}, + /* 234 */ {"var234", "undefined", "-", UC_NONE}, + /* 235 */ {"var235", "undefined", "-", UC_NONE}, + /* 236 */ {"var236", "undefined", "-", UC_NONE}, + /* 237 */ {"var237", "undefined", "-", UC_NONE}, + /* 238 */ {"var238", "undefined", "-", UC_NONE}, + /* 239 */ {"var239", "undefined", "-", UC_NONE}, + /* 240 */ {"var240", "undefined", "-", UC_NONE}, + /* 241 */ {"var241", "undefined", "-", UC_NONE}, + /* 242 */ {"var242", "undefined", "-", UC_NONE}, + /* 243 */ {"var243", "undefined", "-", UC_NONE}, + /* 244 */ {"var244", "undefined", "-", UC_NONE}, + /* 245 */ {"var245", "undefined", "-", UC_NONE}, + /* 246 */ {"var246", "undefined", "-", UC_NONE}, + /* 247 */ {"var247", "undefined", "-", UC_NONE}, + /* 248 */ {"var248", "undefined", "-", UC_NONE}, + /* 249 */ {"var249", "undefined", "-", UC_NONE}, + /* 250 */ {"var250", "undefined", "-", UC_NONE}, + /* 251 */ {"var251", "undefined", "-", UC_NONE}, + /* 252 */ {"var252", "undefined", "-", UC_NONE}, + /* 253 */ {"var253", "undefined", "-", UC_NONE}, + /* 254 */ {"var254", "undefined", "-", UC_NONE}, + /* 255 */ {"var255", "undefined", "-", UC_NONE}, +}; +/* *INDENT-ON* */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/grib2api.c b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/grib2api.c new file mode 100644 index 000000000..48a69c40c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/grib2api.c @@ -0,0 +1,2136 @@ +/***************************************************************************** + * grib2api.c + * + * DESCRIPTION + * This file contains the API to the GRIB2 libraries which is as close as + * possible to the "official" NWS GRIB2 Library's API. The reason for this + * is so we can use NCEP's unofficial GRIB2 library while having minimal + * impact on the already written drivers. + * + * HISTORY + * 12/2003 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#include +#include +#include +#include "grib2api.h" +#include "grib2.h" +#include "scan.h" +#include "memendian.h" +#include "myassert.h" +#include "gridtemplates.h" +#include "pdstemplates.h" +#include "drstemplates.h" + +#include "cpl_port.h" + +//#include "config.h" /*config.h created by configure - ADT mod*/ + +/* Declare the external FORTRAN routine + * gcc has two __ if there is one _ in the procedure name. */ +#ifdef _FORTRAN + extern void UNPK_G2MDL + (sInt4 * kfildo, sInt4 * jmin, sInt4 * lbit, sInt4 * nov, sInt4 * iwork, + float * ain, sInt4 * iain, sInt4 * nd2x3, sInt4 * idat, sInt4 * nidat, + float * rdat, sInt4 * nrdat, sInt4 * is0, sInt4 * ns0, sInt4 * is1, + sInt4 * ns1, sInt4 * is2, sInt4 * ns2, sInt4 * is3, sInt4 * ns3, + sInt4 * is4, sInt4 * ns4, sInt4 * is5, sInt4 * ns5, sInt4 * is6, + sInt4 * ns6, sInt4 * is7, sInt4 * ns7, sInt4 * ib, sInt4 * ibitmap, + sInt4 * ipack, sInt4 * nd5, float * xmissp, float * xmisss, + sInt4 * inew, sInt4 * iclean, sInt4 * l3264b, sInt4 * iendpk, sInt4 * jer, + sInt4 * ndjer, sInt4 * kjer); + + extern void PK_G2MDL + (sInt4 * kfildo, sInt4 * jmax, sInt4 * jmin, sInt4 * lbit, sInt4 * nov, + sInt4 * misslx, float * a, sInt4 * ia, sInt4 * newbox, sInt4 * newboxp, + float * ain, sInt4 * iain, sInt4 * nx, sInt4 * ny, sInt4 * idat, + sInt4 * nidat, float * rdat, sInt4 * nrdat, sInt4 * is0, sInt4 * ns0, + sInt4 * is1, sInt4 * ns1, sInt4 * is3, sInt4 * ns3, sInt4 * is4, + sInt4 * ns4, sInt4 * is5, sInt4 * ns5, sInt4 * is6, sInt4 * ns6, + sInt4 * is7, sInt4 * ns7, sInt4 * ib, sInt4 * ibitmap, sInt4 * ipack, + sInt4 * nd5, sInt4 * missp, float * xmissp, sInt4 * misss, + float * xmisss, sInt4 * inew, sInt4 * minpk, sInt4 * iclean, + sInt4 * l3264b, sInt4 * jer, sInt4 * ndjer, sInt4 * kjer); +#endif + +/***************************************************************************** + * mdl_LocalUnpack() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Unpack the local use data assuming that it was packed using the MDL + * encoder. This assumes "local" starts at octet 6 (ie skipping over the + * length and section ID octets) + * + * In Section 2, GRIB2 provides for local use data. The MDL encoder packs + * that data, and signifies that it has done so by setting octet 6 to 1. + * This may have been inadvisable, since the GRIB2 specs did not state that + * octet 6 was special. + * + * ARGUMENTS + * local = The section 2 data prior to being unpacked. (Input) + * locallen = The length of "local" (Input) + * idat = Unpacked MDL local data (if it was an integer) (Output) + * nidat = length of idat. (Input) + * rdat = Unpacked MDL local data (if it was a float) (Output) + * nrdat = length of rdat. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = Ok. + * 1 = Mixed local data types? + * 2 = nrdat is not large enough. + * 3 = nidat is not large enough. + * 4 = Too many bits to unpack, should be a max of 32 bits. + * 5 = Locallen is too small + * + * HISTORY + * 12/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#define GRIB_UNSIGN_INT2(a,b) ((a<<8)+b) +static int mdl_LocalUnpack (unsigned char *local, sInt4 locallen, + sInt4 * idat, sInt4 * nidat, float * rdat, + sInt4 * nrdat) +{ + int BytesUsed = 0; /* How many bytes have been used. */ + unsigned int numGroup; /* Number of groups */ + int i; /* Counter over the groups. */ + sInt4 numVal; /* Number of values in a given group. */ + float refVal; /* The reference value in a given group. */ + unsigned int scale; /* The power of 10 scale value in the group. */ + sInt4 recScale10; /* 1 / 10**scale.. For faster computations. */ + unsigned char numBits; /* # of bits for a single element in the group. */ + char f_dataType; /* If the local data is a float or integer. */ + char f_firstType = 0; /* The type of the first group of local data. */ + int curIndex = 0; /* Where to store the current data. */ + int j; /* Counter over the number of values in a group. */ + size_t numUsed; /* Number of bytes used in call to memBitRead. */ + uChar bufLoc; /* Where to read for more bits in the data stream. */ + uInt4 uli_temp; /* Temporary storage to hold unpacked data. */ + + if (locallen < BytesUsed + 3) { +#ifdef DEBUG + printf ("Locallen is too small.\n"); +#endif + return 5; + } + /* The calling routine should check octet 6, which is local[0], to be 1, + * so we just assert it is 1. */ + myAssert (local[0] == 1); + numGroup = GRIB_UNSIGN_INT2 (local[1], local[2]); + local += 3; + BytesUsed += 3; + myAssert (*nrdat > 1); + myAssert (*nidat > 1); + idat[0] = 0; + rdat[0] = 0; + + for (i = 0; (unsigned int)i < numGroup; i++) { + if (locallen < BytesUsed + 12) { +#ifdef DEBUG + printf ("Locallen is too small.\n"); +#endif + return 5; + } + MEMCPY_BIG (&numVal, local, sizeof (sInt4)); + MEMCPY_BIG (&refVal, local + 4, sizeof (float)); + scale = GRIB_UNSIGN_INT2 (local[8], local[9]); + recScale10 = 1 / pow (10.0, scale); + numBits = local[10]; + if (numBits >= 32) { +#ifdef DEBUG + printf ("Too many bits too unpack.\n"); +#endif + return 4; + } + f_dataType = local[11]; + local += 12; + BytesUsed += 12; + if (locallen < BytesUsed + ((numBits * numVal) + 7) / 8) { +#ifdef DEBUG + printf ("Locallen is too small.\n"); +#endif + return 5; + } + if (i == 0) { + f_firstType = f_dataType; + } else if (f_firstType != f_dataType) { +#ifdef DEBUG + printf ("Local use data has mixed float/integer type?\n"); +#endif + return 1; + } + bufLoc = 8; + if (f_dataType == 0) { + /* Floating point data. */ + if (*nrdat < (curIndex + numVal + 3)) { +#ifdef DEBUG + printf ("nrdat is not large enough.\n"); +#endif + return 2; + } + rdat[curIndex] = numVal; + curIndex++; + rdat[curIndex] = scale; + curIndex++; + for (j = 0; j < numVal; j++) { + memBitRead (&uli_temp, sizeof (sInt4), local, numBits, + &bufLoc, &numUsed); + local += numUsed; + BytesUsed += numUsed; + rdat[curIndex] = (refVal + uli_temp) * recScale10; + curIndex++; + } + rdat[curIndex] = 0; + } else { + /* Integer point data. */ + if (*nidat < (curIndex + numVal + 3)) { +#ifdef DEBUG + printf ("nidat is not large enough.\n"); +#endif + return 3; + } + idat[curIndex] = numVal; + curIndex++; + idat[curIndex] = scale; + curIndex++; + for (j = 0; j < numVal; j++) { + memBitRead (&uli_temp, sizeof (sInt4), local, numBits, + &bufLoc, &numUsed); + local += numUsed; + BytesUsed += numUsed; + idat[curIndex] = (refVal + uli_temp) * recScale10; + curIndex++; + } + idat[curIndex] = 0; + } + } + return 0; +} + +/***************************************************************************** + * fillOutSectLen() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * The GRIB2 API returns the lengths of each GRIB2 section along with the + * meta data. NCEP's routines did not, so this routine fills in that part. + * This routine has to consider which grid is being unpacked. + * + * c_ipack is passed in after section 1 (since section 1 is not repeated) + * + * ARGUMENTS + * c_ipack = Complete GRIB2 message to look for the section lengths in. (In) + * lenCpack = Length of c_ipack (Input) + * subgNum = Which sub rid to find the section lengths of. (Input) + * is2 = Section 2 data (Output) + * is3 = Section 3 data (Output) + * is4 = Section 4 data (Output) + * is5 = Section 5 data (Output) + * is6 = Section 6 data (Output) + * is7 = Section 7 data (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = Ok. + * 1 = c_ipack is not large enough + * 2 = invalid section number + * + * HISTORY + * 12/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +static int fillOutSectLen (unsigned char *c_ipack, int lenCpack, + int subgNum, sInt4 * is2, sInt4 * is3, + sInt4 * is4, sInt4 * is5, sInt4 * is6, + sInt4 * is7) +{ + sInt4 offset = 0; /* How far in c_ipack we have read. */ + sInt4 sectLen; /* The length of the current section. */ + int sectId; /* The current section number. */ + unsigned int gNum = 0; /* Which sub group we are currently working with. */ + + if (lenCpack < 5) { +#ifdef DEBUG + printf ("Cpack is not large enough.\n"); +#endif + return 1; + } + /* assert that we start with data in either section 2 or 3. */ + myAssert ((c_ipack[4] == 2) || (c_ipack[4] == 3)); + while (gNum <= (unsigned int)subgNum) { + if (lenCpack < offset + 5) { +#ifdef DEBUG + printf ("Cpack is not large enough.\n"); +#endif + return 1; + } + MEMCPY_BIG (§Len, c_ipack + offset, sizeof (sInt4)); + /* Check if we just read section 8. If so, then it is "7777" = + * 926365495 regardless of endian'ness. */ + if (sectLen == 926365495L) { +#ifdef DEBUG + printf ("Shouldn't see sect 8. Should stop after correct sect 7\n"); +#endif + return 2; + } + sectId = c_ipack[offset + 4]; + switch (sectId) { + case 2: + is2[0] = sectLen; + break; + case 3: + is3[0] = sectLen; + break; + case 4: + is4[0] = sectLen; + break; + case 5: + is5[0] = sectLen; + break; + case 6: + is6[0] = sectLen; + break; + case 7: + is7[0] = sectLen; + gNum++; + break; + default: +#ifdef DEBUG + printf ("Invalid section id %d.\n", sectId); +#endif + return 2; + } + offset += sectLen; + } + return 0; +} + +/***************************************************************************** + * TransferInt() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To transfer the data from "fld" and "bmap" to "iain" and "ib". The API + * attempts to rearrange the order, so that anything returned from it has + * scan mode 0100???? + * + * ARGUMENTS + * fld = The expanded grid from NCEPs routines (Input) + * ngrdpts = Length of fld (Input) + * ibitmap = flag if we have a bitmap or not. (Input) + * bmap = bitmap from NCEPs routines. (Input) + * f_ignoreScan = Flag to ignore the attempt at changing the scan (Input) + * scan = The scan orientation of fld/bmap/iain/ib (Input/Output) + * nx, ny = The dimmensions of the grid. (Input) + * iclean = 1 means the user wants the unpacked data returned without + * missing values in it. 0 means embed the missing values. (In) + * xmissp = The primary missing value to use if iclean = 0. (Input). + * iain = The grid to return. (Output) + * nd2x3 = The length of iain. (Input) + * ib = Bitmap if it was packed (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = Ok. + * 1 = nd2x3 is too small. + * 2 = nx*ny != ngrdpts + * + * HISTORY + * 12/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * May want to disable the scan adjustment in the future. + ***************************************************************************** + */ +static int TransferInt (float * fld, sInt4 ngrdpts, sInt4 ibitmap, + sInt4 * bmap, char f_ignoreScan, sInt4 * scan, + sInt4 nx, sInt4 ny, sInt4 iclean, float xmissp, + sInt4 * iain, sInt4 nd2x3, sInt4 * ib) +{ + int i; /* loop counter over all grid points. */ + sInt4 x, y; /* Where we are in a grid of scan value 0100???? */ + int curIndex; /* Where in iain to store the current data. */ + + if (nd2x3 < ngrdpts) { +#ifdef DEBUG + printf ("nd2x3(%d) is < ngrdpts(%d)\n", nd2x3, ngrdpts); +#endif + return 1; + } + if (f_ignoreScan || ((*scan & 0xf0) == 64)) { + if (ibitmap) { + for (i = 0; i < ngrdpts; i++) { + ib[i] = bmap[i]; + /* Check if we are supposed to insert xmissp into the field */ + if ((iclean != 0) && (ib[i] == 0)) { + iain[i] = xmissp; + } else { + iain[i] = fld[i]; + } + } + } else { + for (i = 0; i < ngrdpts; i++) { + iain[i] = fld[i]; + } + } + } else { + if (nx * ny != ngrdpts) { +#ifdef DEBUG + printf ("nx * ny (%d) != ngrdpts(%d)\n", nx * ny, ngrdpts); +#endif + return 2; + } + if (ibitmap) { + for (i = 0; i < ngrdpts; i++) { + ScanIndex2XY (i, &x, &y, *scan, nx, ny); + /* ScanIndex returns value as if scan was 0100(0000) */ + curIndex = (x - 1) + (y - 1) * nx; + myAssert (curIndex < nd2x3); + ib[curIndex] = bmap[i]; + /* Check if we are supposed to insert xmissp into the field */ + if ((iclean != 0) && (ib[curIndex] == 0)) { + iain[i] = xmissp; + } else { + iain[curIndex] = fld[i]; + } + } + } else { + for (i = 0; i < ngrdpts; i++) { + ScanIndex2XY (i, &x, &y, *scan, nx, ny); + /* ScanIndex returns value as if scan was 0100(0000) */ + curIndex = (x - 1) + (y - 1) * nx; + myAssert (curIndex < nd2x3); + iain[curIndex] = fld[i]; + } + } + *scan = 64 + (*scan & 0x0f); + } + return 0; +} + +/***************************************************************************** + * TransferFloat() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To transfer the data from "fld" and "bmap" to "ain" and "ib". The API + * attempts to rearrange the order, so that anything returned from it has + * scan mode 0100???? + * + * ARGUMENTS + * fld = The expanded grid from NCEPs routines (Input) + * ngrdpts = Length of fld (Input) + * ibitmap = flag if we have a bitmap or not. (Input) + * bmap = bitmap from NCEPs routines. (Input) + * scan = The scan orientation of fld/bmap/iain/ib (Input/Output) + * f_ignoreScan = Flag to ignore the attempt at changing the scan (Input) + * nx, ny = The dimmensions of the grid. (Input) + * iclean = 1 means the user wants the unpacked data returned without + * missing values in it. 0 means embed the missing values. (In) + * xmissp = The primary missing value to use if iclean = 0. (Input). + * ain = The grid to return. (Output) + * nd2x3 = The length of iain. (Input) + * ib = Bitmap if it was packed (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = Ok. + * 1 = nd2x3 is too small. + * 2 = nx*ny != ngrdpts + * + * HISTORY + * 12/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * May want to disable the scan adjustment in the future. + ***************************************************************************** + */ +static int TransferFloat (float * fld, sInt4 ngrdpts, sInt4 ibitmap, + sInt4 * bmap, char f_ignoreScan, sInt4 * scan, + sInt4 nx, sInt4 ny, sInt4 iclean, float xmissp, + float * ain, sInt4 nd2x3, sInt4 * ib) +{ + int i; /* loop counter over all grid points. */ + sInt4 x, y; /* Where we are in a grid of scan value 0100???? */ + int curIndex; /* Where in ain to store the current data. */ + + if (nd2x3 < ngrdpts) { +#ifdef DEBUG + printf ("nd2x3(%d) is < ngrdpts(%d)\n", nd2x3, ngrdpts); +#endif + return 1; + } + if (f_ignoreScan || ((*scan & 0xf0) == 64)) { + if (ibitmap) { + for (i = 0; i < ngrdpts; i++) { + ib[i] = bmap[i]; + /* Check if we are supposed to insert xmissp into the field */ + if ((iclean != 0) && (ib[i] == 0)) { + ain[i] = xmissp; + } else { + ain[i] = fld[i]; + } + } + } else { + for (i = 0; i < ngrdpts; i++) { + ain[i] = fld[i]; + } + } + } else { + if (nx * ny != ngrdpts) { +#ifdef DEBUG + printf ("nx * ny (%d) != ngrdpts(%d)\n", nx * ny, ngrdpts); +#endif + return 2; + } + if (ibitmap) { + for (i = 0; i < ngrdpts; i++) { + ScanIndex2XY (i, &x, &y, *scan, nx, ny); + /* ScanIndex returns value as if scan was 0100(0000) */ + curIndex = (x - 1) + (y - 1) * nx; + myAssert (curIndex < nd2x3); + ib[curIndex] = bmap[i]; + /* Check if we are supposed to insert xmissp into the field */ + if ((iclean != 0) && (ib[curIndex] == 0)) { + ain[i] = xmissp; + } else { + ain[curIndex] = fld[i]; + } + } + } else { + for (i = 0; i < ngrdpts; i++) { + ScanIndex2XY (i, &x, &y, *scan, nx, ny); + /* ScanIndex returns value as if scan was 0100(0000) */ + curIndex = (x - 1) + (y - 1) * nx; + myAssert (curIndex < nd2x3); + ain[curIndex] = fld[i]; + } + } + *scan = 64 + (*scan & 0x0f); + } +/* + if (1==1) { + int i; + for (i=0; i < ngrdpts; i++) { + if (ain[i] != 9999) { + fprintf (stderr, "grib2api.c: ain[%d] %f, fld[%d] %f\n", i, ain[i], + i, fld[i]); + } + } + } +*/ + return 0; +} + +#ifdef PKNCEP +int pk_g2ncep (sInt4 * kfildo, float * ain, sInt4 * iain, sInt4 * nx, + sInt4 * ny, sInt4 * idat, sInt4 * nidat, float * rdat, + sInt4 * nrdat, sInt4 * is0, sInt4 * ns0, sInt4 * is1, + sInt4 * ns1, sInt4 * is3, sInt4 * ns3, sInt4 * is4, + sInt4 * ns4, sInt4 * is5, sInt4 * ns5, sInt4 * is6, + sInt4 * ns6, sInt4 * is7, sInt4 * ns7, sInt4 * ib, + sInt4 * ibitmap, unsigned char *cgrib, sInt4 * nd5, + sInt4 * missp, float * xmissp, sInt4 * misss, + float * xmisss, sInt4 * inew, sInt4 * minpk, sInt4 * iclean, + sInt4 * l3264b, sInt4 * jer, sInt4 * ndjer, sInt4 * kjer) +{ + g2int listsec0[2]; + g2int listsec1[13]; + g2int igds[5]; + g2int igdstmpl[]; + int ierr; /* Holds the error code from a called routine. */ + int i; + + listsec0[0] = is0[6]; + listsec0[1] = is0[7]; + + listsec1[0] = is1[5]; + listsec1[1] = is1[7]; + listsec1[2] = is1[9]; + listsec1[3] = is1[10]; + listsec1[4] = is1[11]; + listsec1[5] = is1[12]; + listsec1[6] = is1[14]; + listsec1[7] = is1[15]; + listsec1[8] = is1[16]; + listsec1[9] = is1[17]; + listsec1[10] = is1[18]; + listsec1[11] = is1[19]; + listsec1[12] = is1[20]; + + ierr = g2_create (cgrib, listsec0, listsec1); + printf ("Length = %d\n", ierr); + + if ((idat[0] != 0) || (rdat[0] != 0)) { + printf ("Don't handle this yet.\n"); +/* + ierr = g2_addlocal (cgrib, unsigned char *csec2, g2int lcsec2); +*/ + } + + igds[0] = is3[5]; + igds[1] = is3[6]; + igds[2] = is3[10]; + igds[3] = is3[11]; + igds[4] = is3[12]; + +IS3(15) - IS3(nn) = Grid Definition Template, stored in bytes 15-nn (*) + + ierr = g2_addgrid (cgrib, igds, g2int *igdstmpl, g2int *ideflist, + g2int idefnum) + + + + + return 0; +/* + +To start a new GRIB2 message, call function g2_create. G2_create +encodes Sections 0 and 1 at the beginning of the message. This routine +must be used to create each message. + +Routine g2_addlocal can be used to add a Local Use Section ( Section 2 ). +Note that this section is optional and need not appear in a GRIB2 message. + +Function g2_addgrid is used to encode a grid definition into Section 3. +This grid definition defines the geometry of the the data values in the +fields that follow it. g2_addgrid can be called again to change the grid +definition describing subsequent data fields. + +Each data field is added to the GRIB2 message using routine g2_addfield, +which adds Sections 4, 5, 6, and 7 to the message. + +After all desired data fields have been added to the GRIB2 message, a +call to function g2_gribend is needed to add the final section 8 to the +message and to update the length of the message. A call to g2_gribend +is required for each GRIB2 message. +*/ + +} +#endif + +/***************************************************************************** + * unpk_g2ncep() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * This procedure is a wrapper around the NCEP GRIB2 routines to interface + * their routines with the "official NWS" GRIB2 API. The reason for this is + * so drivers that have been written to use the "official NWS" GRIB2 API, can + * use the NCEP library with minimal disruption. + * + * ARGUMENTS + * kfildo = Unit number for output diagnostics (C ignores this). (Input) + * ain = contains the data if the original data was float data. + * (size = nd2x3) (Output) + * iain = contains the data if the original data was integer data. + * (size = nd2x3) (Output) + * nd2x3 = length of ain, iain, ib (Input) + * (at least the size of num grid points) + * idat = local use data if any that were unpacked from Section 2. (Output) + * nidat = length of idat. (Input) + * rdat = floating point local use data (Output) + * nrdat = length of rdat. (Input) + * is0 = Section 0 data (Output) + * ns0 = length of is0 (16 is fine) (Input) + * is1 = Section 1 data (Output) + * ns1 = length of is1 (21 is fine) (Input) + * is2 = Section 2 data (Output) + * ns2 = length of is2 () (Input) + * is3 = Section 3 data (Output) + * ns3 = length of is3 (96 or 1600) (Input) + * is4 = Section 4 data (Output) + * ns4 = length of is4 (60) (Input) + * is5 = Section 5 data (Output) + * ns5 = length of is5 (49 is fine) (Input) + * is6 = Section 6 data (Output) + * ns6 = length of is6 (6 is fine) (Input) + * is7 = Section 7 data (Output) + * ns7 = length of is7 (8 is fine) (Input) + * ib = Bitmap if user requested it, and it was packed (Output) + * ibitmap = 0 means ib is invalid, 1 means ib is valid. (Output) + * c_ipack = The message to unpack (Input) + * nd5 = 1/4 the size of c_ipack (Input) + * xmissp = The floating point representation for the primary missing value. + * (Input/Output) + * xmisss = The floating point representation for the secondary missing + * value (Input/Output) + * new = 1 if this is the first grid to be unpacked, else 0. (Input) + * iclean = 1 means the user wants the unpacked data returned without + * missing values in it. 0 means embed the missing values. (Input) + * l3264b = Integer word length in bits (32 or 64) (Input) + * iendpk = A 1 means no more grids in this message, a 0 means more grids. + * (Output) + * jer(ndjer,2) = error codes along with severity. (Output) + * ndjer = 1/2 length of jer. (>= 15) (Input) + * kjer = number of error messages stored in jer. + * + * FILES/DATABASES: None + * + * RETURNS: void + * "jer" is used to store error messages, and kjer is used to denote how + * many there were. Jer is set up as a 2 column array, with the first + * column in the first half of the array, and denoting the GRIB2 section + * an error occurred in. The second column denoting the severity. + * + * The possibilities from unpk_g2ncep() are as follows: + * ker=1 jer[0,0]=0 jer[0,1]=2: Message is not formed correctly + * Request for an invalid subgrid + * Problems unpacking the data. + * problems expanding the data. + * Calling dimmensions were too small. + * ker=2 jer[1,0]=100 jer[1,1]=2: Error unpacking section 1. + * ker=3 jer[2,0]=200 jer[2,1]=2: Error unpacking section 2. + * ker=4 jer[3,0]=300 jer[3,1]=2: Error unpacking section 3. + * ker=5 jer[4,0]=400 jer[4,1]=2: Error unpacking section 4. + * ker=6 jer[5,0]=500 jer[5,1]=2: Error unpacking section 5. + * Data Template not implemented. + * Durring Transfer, nx * ny != ngrdpts. + * ker=7 jer[6,0]=600 jer[6,1]=2: Error unpacking section 6. + * ker=8 jer[7,0]=700 jer[7,1]=2: Error unpacking section 7. + * ker=9 jer[8,0]=2001 jer[8,1]=2: nd2x3 is not large enough. + * ker=9 jer[8,0]=2003 jer[8,1]=2: undefined sect 3 template + * (see gridtemplates.h). + * ker=9 jer[8,0]=2004 jer[8,1]=2: undefined sect 4 template + * (see pdstemplates.h). + * ker=9 jer[8,0]=2005 jer[8,1]=2: undefined sect 5 template + * (see drstemplates.h). + * ker=9 jer[8,0]=9999 jer[8,1]=2: NCEP returns an unrecognized error. + * + * HISTORY + * 12/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * MDL handles is5[12], is5[23], and is5[27] in an "interesting" manner. + * MDL attempts to always return grids in scan mode 0100???? + * + * ToDo: Check length of parameters better. + * ToDo: Probably shouldn't abort if they have problems expanding the data. + * ToDo: Better handling of: + * gfld->list_opt = (Used if gfld->numoct_opt .ne. 0) This array contains + * the number of grid points contained in each row (or column). (part of + * Section 3) This element is a pointer to an array that holds the data. + * This pointer is nullified if gfld->numoct_opt=0. + * gfld->num_opt = (Used if gfld->numoct_opt .ne. 0) The number of entries in + * array ideflist. i.e. number of rows (or columns) for which optional + * grid points are defined. This value is set to zero, if + * gfld->numoct_opt=0. + * gfld->coord_list = Real array containing floating point values intended to + * document the vertical discretisation associated to model data on + * hybrid coordinate vertical levels. (part of Section 4) This element + * is a pointer to an array that holds the data. + * gfld->num_coord = number of values in array gfld->coord_list[]. + ***************************************************************************** + */ +void unpk_g2ncep (CPL_UNUSED sInt4 * kfildo, float * ain, sInt4 * iain, sInt4 * nd2x3, + sInt4 * idat, sInt4 * nidat, float * rdat, sInt4 * nrdat, + sInt4 * is0, CPL_UNUSED sInt4 * ns0, sInt4 * is1, CPL_UNUSED sInt4 * ns1, + sInt4 * is2, sInt4 * ns2, sInt4 * is3, CPL_UNUSED sInt4 * ns3, + sInt4 * is4, CPL_UNUSED sInt4 * ns4, sInt4 * is5, CPL_UNUSED sInt4 * ns5, + sInt4 * is6, CPL_UNUSED sInt4 * ns6, sInt4 * is7, CPL_UNUSED sInt4 * ns7, + sInt4 * ib, sInt4 * ibitmap, unsigned char *c_ipack, + sInt4 * nd5, float * xmissp, float * xmisss, + sInt4 * inew, sInt4 * iclean, CPL_UNUSED sInt4 * l3264b, + sInt4 * iendpk, sInt4 * jer, sInt4 * ndjer, sInt4 * kjer) +{ + int i; /* A counter used for a number of purposes. */ + static unsigned int subgNum = 0; /* The sub grid we read most recently. + * This is primarily to help with the + * inew option. */ + int ierr; /* Holds the error code from a called routine. */ + sInt4 listsec0[3]; + sInt4 listsec1[13]; + static sInt4 numfields = 1; /* Number of sub Grids in this message */ + sInt4 numlocal; /* Number of local sections in this message. */ + int unpack; /* Tell g2_getfld to unpack the message. */ + int expand; /* Tell g2_getflt to attempt to expand the bitmap. */ + gribfield *gfld; /* Holds the data after g2_getfld unpacks it. */ + sInt4 gridIndex; /* index in templatesgrid[] for this sect 3 templat */ + sInt4 pdsIndex; /* index in templatespds[] for this sect 4 template */ + sInt4 drsIndex; /* index in templatesdrs[] for this sect 5 template */ + int curIndex; /* Where in is3, is4, or is5 to store meta data */ + int scanIndex; /* Where in is3 to find the scan mode. */ + int nxIndex; /* Where in is3 to find the number of x values. */ + int nyIndex; /* Where in is3 to find the number of y values. */ + float f_temp; /* Assist with handling weird MDL behavior in is5[] */ + char f_ignoreScan; /* Flag to ignore the attempt at changing the scan */ + sInt4 dummyScan; /* Dummy place holder for call to Transfer routines + * if ignoring scan. */ + + myAssert (*ndjer >= 8); + /* Init the error handling array. */ + memset ((void *) jer, 0, 2 * *ndjer * sizeof (sInt4)); + for (i = 0; i < 8; i++) { + jer[i] = i * 100; + } + *kjer = 8; + + /* The first time in, figure out how many grids there are, and store it + * in numfields for subsequent calls with inew != 1. */ + if (*inew == 1) { + subgNum = 0; + ierr = g2_info (c_ipack, listsec0, listsec1, &numfields, &numlocal); + if (ierr != 0) { + switch (ierr) { + case 1: /* Beginning characters "GRIB" not found. */ + case 2: /* GRIB message is not Edition 2. */ + case 3: /* Could not find Section 1, where expected. */ + case 4: /* End string "7777" found, but not where expected. */ + case 5: /* End string "7777" not found at end of message. */ + case 6: /* Invalid section number found. */ + jer[0 + *ndjer] = 2; + *kjer = 1; + break; + default: + jer[8 + *ndjer] = 2; + jer[8] = 9999; /* Unknown error message. */ + *kjer = 9; + } + return; + } + } else { + if (subgNum + 1 >= (unsigned int)numfields) { + /* Field request error. */ + jer[0 + *ndjer] = 2; + *kjer = 1; + return; + } + subgNum++; + } + + /* Expand the desired subgrid. */ + unpack = 1; + expand = 1; + ierr = g2_getfld (c_ipack, subgNum + 1, unpack, expand, &gfld); + if (ierr != 0) { + switch (ierr) { + case 1: /* Beginning characters "GRIB" not found. */ + case 2: /* GRIB message is not Edition 2. */ + case 3: /* The data field request number was not positive. */ + case 4: /* End string "7777" found, but not where expected. */ + case 6: /* message did not contain requested # of fields */ + case 7: /* End string "7777" not found at end of message. */ + case 8: /* Unrecognized Section encountered. */ + jer[0 + *ndjer] = 2; + *kjer = 1; + break; + case 15: /* Error unpacking Section 1. */ + jer[1 + *ndjer] = 2; + *kjer = 2; + break; + case 16: /* Error unpacking Section 2. */ + jer[2 + *ndjer] = 2; + *kjer = 3; + break; + case 10: /* Error unpacking Section 3. */ + jer[3 + *ndjer] = 2; + *kjer = 4; + break; + case 11: /* Error unpacking Section 4. */ + jer[4 + *ndjer] = 2; + *kjer = 5; + break; + case 9: /* Data Template 5.NN not implemented. */ + case 12: /* Error unpacking Section 5. */ + jer[5 + *ndjer] = 2; + *kjer = 6; + break; + case 13: /* Error unpacking Section 6. */ + jer[6 + *ndjer] = 2; + *kjer = 7; + break; + case 14: /* Error unpacking Section 7. */ + jer[7 + *ndjer] = 2; + *kjer = 8; + break; + default: + jer[8 + *ndjer] = 2; + jer[8] = 9999; /* Unknown error message. */ + *kjer = 9; + break; + } + g2_free (gfld); + return; + } + /* Check if data wasn't unpacked. */ + if (!gfld->unpacked) { + jer[0 + *ndjer] = 2; + *kjer = 1; + g2_free (gfld); + return; + } + + /* Start going through the gfld structure and converting it to the needed + * data output formats. */ + myAssert (*ns0 >= 16); + MEMCPY_BIG (&(is0[0]), c_ipack, sizeof (sInt4)); + is0[6] = gfld->discipline; + is0[7] = gfld->version; + MEMCPY_BIG (&(is0[8]), c_ipack + 8, sizeof (sInt4)); + /* The following assert fails only if the GRIB message is more that 4 + * giga-bytes large, which I think would break the fortran library. */ + myAssert (is0[8] == 0); + MEMCPY_BIG (&(is0[8]), c_ipack + 12, sizeof (sInt4)); + + myAssert (*ns1 >= 21); + myAssert (gfld->idsectlen >= 13); + MEMCPY_BIG (&(is1[0]), c_ipack + 16, sizeof (sInt4)); + is1[4] = c_ipack[20]; + is1[5] = gfld->idsect[0]; + is1[7] = gfld->idsect[1]; + is1[9] = gfld->idsect[2]; + is1[10] = gfld->idsect[3]; + is1[11] = gfld->idsect[4]; + is1[12] = gfld->idsect[5]; /* Year */ + is1[14] = gfld->idsect[6]; /* Month */ + is1[15] = gfld->idsect[7]; /* Day */ + is1[16] = gfld->idsect[8]; /* Hour */ + is1[17] = gfld->idsect[9]; /* Min */ + is1[18] = gfld->idsect[10]; /* Sec */ + is1[19] = gfld->idsect[11]; + is1[20] = gfld->idsect[12]; + + /* Fill out section lengths (separate procedure because of possibility of + * having multiple grids. Should combine fillOutSectLen g2_info, and + * g2_getfld into one procedure to optimize it. */ + fillOutSectLen (c_ipack + 16 + is1[0], 4 * *nd5 - 15 - is1[0], subgNum, + is2, is3, is4, is5, is6, is7); + + /* Check if there is section 2 data. */ + if (gfld->locallen > 0) { + /* The + 1 is so we don't overwrite the section length */ + memset ((void *) (is2 + 1), 0, (*ns2 - 1) * sizeof (sInt4)); + is2[4] = 2; + is2[5] = gfld->local[0]; + /* check if MDL Local use simple packed data */ + if (is2[5] == 1) { + mdl_LocalUnpack (gfld->local, gfld->locallen, idat, nidat, + rdat, nrdat); + } else { + /* local use section was not MDL packed, return it in is2. */ + for (i = 0; i < gfld->locallen; i++) { + is2[i + 5] = gfld->local[i]; + } + } + } else { + /* API specified that is2[0] = 0; idat[0] = 0, and rdat[0] = 0 */ + is2[0] = 0; + idat[0] = 0; + rdat[0] = 0; + } + + is3[4] = 3; + is3[5] = gfld->griddef; + is3[6] = gfld->ngrdpts; + if (*nd2x3 < gfld->ngrdpts) { + jer[8 + *ndjer] = 2; + jer[8] = 2001; /* nd2x3 is not large enough */ + *kjer = 9; + g2_free (gfld); + return; + } + is3[10] = gfld->numoct_opt; + is3[11] = gfld->interp_opt; + is3[12] = gfld->igdtnum; + gridIndex = getgridindex (gfld->igdtnum); + if (gridIndex == -1) { + jer[8 + *ndjer] = 2; + jer[8] = 2003; /* undefined sect 3 template */ + *kjer = 9; + g2_free (gfld); + return; + } + curIndex = 14; + for (i = 0; i < gfld->igdtlen; i++) { + const struct gridtemplate *templatesgrid = get_templatesgrid(); + is3[curIndex] = gfld->igdtmpl[i]; + curIndex += abs (templatesgrid[gridIndex].mapgrid[i]); + } + /* API attempts to return grid in scan mode 0100????. Find the necessary + * indexes into the is3 array for the attempt. */ + switch (gfld->igdtnum) { + case 0: + case 1: + case 2: + case 3: + case 40: + case 41: + case 42: + case 43: + scanIndex = 72 - 1; + nxIndex = 31 - 1; + nyIndex = 35 - 1; + break; + case 10: + scanIndex = 60 - 1; + nxIndex = 31 - 1; + nyIndex = 35 - 1; + break; + case 20: + case 30: + case 31: + scanIndex = 65 - 1; + nxIndex = 31 - 1; + nyIndex = 35 - 1; + break; + case 90: + scanIndex = 64 - 1; + nxIndex = 31 - 1; + nyIndex = 35 - 1; + break; + case 110: + scanIndex = 57 - 1; + nxIndex = 31 - 1; + nyIndex = 35 - 1; + break; + case 50: + case 51: + case 52: + case 53: + case 100: + case 120: + case 1000: + case 1200: + default: + scanIndex = -1; + nxIndex = -1; + nyIndex = -1; + } + + is4[4] = 4; + is4[5] = gfld->num_coord; + is4[7] = gfld->ipdtnum; + pdsIndex = getpdsindex (gfld->ipdtnum); + if (pdsIndex == -1) { + jer[8 + *ndjer] = 2; + jer[8] = 2004; /* undefined sect 4 template */ + *kjer = 9; + g2_free (gfld); + return; + } + curIndex = 9; + for (i = 0; i < gfld->ipdtlen; i++) { + const struct pdstemplate *templatespds = get_templatespds(); + is4[curIndex] = gfld->ipdtmpl[i]; + curIndex += abs (templatespds[pdsIndex].mappds[i]); + } + + is5[4] = 5; + is5[5] = gfld->ndpts; + is5[9] = gfld->idrtnum; + drsIndex = getdrsindex (gfld->idrtnum); + if (drsIndex == -1) { + jer[8 + *ndjer] = 2; + jer[8] = 2005; /* undefined sect 5 template */ + *kjer = 9; + g2_free (gfld); + return; + } + curIndex = 11; + for (i = 0; i < gfld->idrtlen; i++) { + const struct drstemplate *templatesdrs = get_templatesdrs(); + is5[curIndex] = gfld->idrtmpl[i]; + curIndex += abs (templatesdrs[drsIndex].mapdrs[i]); + } + /* Mimic MDL's handling of reference value (IS5(12)) */ + memcpy (&f_temp, &(is5[11]), sizeof (float)); + is5[11] = (sInt4) f_temp; + if ((is5[9] == 2) || (is5[9] == 3)) { + if (is5[20] == 0) { + memcpy (&(f_temp), &(is5[23]), sizeof (float)); + *xmissp = f_temp; + is5[23] = (sInt4) f_temp; + memcpy (&(f_temp), &(is5[27]), sizeof (float)); + *xmisss = f_temp; + is5[27] = (sInt4) f_temp; + } else { + *xmissp = is5[23]; + *xmisss = is5[27]; + } + } + + is6[4] = 6; + is6[5] = gfld->ibmap; + is7[4] = 7; + + if ((subgNum + 1) == (unsigned int)numfields) { + *iendpk = 1; + } else { + *iendpk = 0; + } + + if ((gfld->ibmap == 0) || (gfld->ibmap == 254)) { + *ibitmap = 1; + } else { + *ibitmap = 0; + } + + /* Check type of original field, before transferring the memory. */ + myAssert (*ns5 > 20); + /* Check if NCEP had problems expanding the data. If so we currently + * abort. May need to revisit this behavior. */ + if (!gfld->expanded) { + jer[0 + *ndjer] = 2; + *kjer = 1; + g2_free (gfld); + return; + } + f_ignoreScan = 0; + /* Check if integer type... is5[20] == 1 implies integer (code table + * 5.1), but only for certain templates. (0,1,2,3,40,41,40000,40010). + * (not 50,51) */ + if ((is5[20] == 1) && ((is5[9] != 50) && (is5[9] != 51))) { + /* integer data, use iain */ + if ((scanIndex < 0) || (nxIndex < 0) || (nyIndex < 0)) { + ierr = TransferInt (gfld->fld, gfld->ngrdpts, *ibitmap, gfld->bmap, + 1, &(dummyScan), 0, 0, *iclean, *xmissp, + iain, *nd2x3, ib); + } else { + ierr = TransferInt (gfld->fld, gfld->ngrdpts, *ibitmap, gfld->bmap, + f_ignoreScan, &(is3[scanIndex]), is3[nxIndex], + is3[nyIndex], *iclean, *xmissp, iain, *nd2x3, + ib); + } + } else { + /* float data, use ain */ + if ((scanIndex < 0) || (nxIndex < 0) || (nyIndex < 0)) { + ierr = TransferFloat (gfld->fld, gfld->ngrdpts, *ibitmap, + gfld->bmap, 1, &(dummyScan), 0, 0, *iclean, + *xmissp, ain, *nd2x3, ib); + } else { + ierr = TransferFloat (gfld->fld, gfld->ngrdpts, *ibitmap, + gfld->bmap, f_ignoreScan, &(is3[scanIndex]), + is3[nxIndex], is3[nyIndex], *iclean, *xmissp, + ain, *nd2x3, ib); + } + } + if (ierr != 0) { + switch (ierr) { + case 1: /* nd2x3 is too small */ + jer[0 + *ndjer] = 2; + *kjer = 1; + break; + case 2: /* nx * ny != ngrdpts */ + jer[5 + *ndjer] = 2; + *kjer = 6; + break; + default: + jer[8 + *ndjer] = 2; + jer[8] = 9999; /* Unknown error message. */ + *kjer = 9; + } + g2_free (gfld); + return; + } + g2_free (gfld); +} + +/***************************************************************************** + * validate() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Output all the return values from the API to a file. This is primarily + * for diagnostics purposes. + * + * ARGUMENTS + * filename = File to write the data to. (Input) + * ain = The data if the original data was float data. (nd2x3) (Input) + * iain = The data if the original data was integer data. (nd2x3) (Input) + * nd2x3 = length of ain, iain, ib (Input) + * idat = MDL local use data (if any were unpacked from Sect 2). (Input) + * nidat = length of idat. (Input) + * rdat = floating point local use data (Input) + * nrdat = length of rdat. (Input) + * is0, ns0 = Section 0 data, length of is0 (16) (Input) + * is1, ns1 = Section 1 data, length of is1 (21) (Input) + * is2, ns2 = Section 2 data, length of is2 (??) (Input) + * is3, ns3 = Section 3 data, length of is3 (96 or 1600) (Input) + * is4, ns4 = Section 4 data, length of is4 (60) (Input) + * is5, ns5 = Section 5 data, length of is5 (49) (Input) + * is6, ns6 = Section 6 data, length of is6 (6) (Input) + * is7, ns7 = Section 7 data, length of ns7 (8) (Input) + * ib = Bitmap if user requested it, and it was packed (Input) + * ibitmap = 0 means ib is invalid, 1 means ib is valid. (Input) + * xmissp = Primary missing value. (Input) + * xmisss = Secondary missing value (Input) + * iendpk = flag if there is more grids in the message. (Input) + * jer(ndjer,2) = error codes along with severity. (Input) + * ndjer = length of jer. (15) (Input) + * kjer = number of error messages stored in jer. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = Ok. + * -1 = Problems opening the file. + * + * HISTORY + * 12/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifdef notdef +static int validate (char *filename, float * ain, sInt4 * iain, + sInt4 * nd2x3, sInt4 * idat, sInt4 * nidat, + float * rdat, sInt4 * nrdat, sInt4 * is0, sInt4 * ns0, + sInt4 * is1, sInt4 * ns1, sInt4 * is2, sInt4 * ns2, + sInt4 * is3, sInt4 * ns3, sInt4 * is4, sInt4 * ns4, + sInt4 * is5, sInt4 * ns5, sInt4 * is6, sInt4 * ns6, + sInt4 * is7, sInt4 * ns7, sInt4 * ib, sInt4 * ibitmap, + float * xmissp, float * xmisss, sInt4 * iendpk, + sInt4 * jer, sInt4 * ndjer, sInt4 * kjer) +{ + FILE *fp; /* Open file to write to. */ + int i; /* Counter for printing to file. */ + + if ((fp = fopen (filename, "wt")) == NULL) { +#ifdef DEBUG + printf ("unable to open %s for write\n", filename); +#endif + return -1; + } + for (i = 0; i < *ns0; i++) { + fprintf (fp, "Sect 0 : %d of %d : %d\n", i, *ns0, is0[i]); + } + for (i = 0; i < *ns1; i++) { + fprintf (fp, "Sect 1 : %d of %d : %d\n", i, *ns1, is1[i]); + } + for (i = 0; i < *ns2; i++) { + fprintf (fp, "Sect 2 : %d of %d : %d\n", i, *ns2, is2[i]); + } + for (i = 0; i < idat[0]; i++) { + fprintf (fp, "idat : %d of %d : %d\n", i, idat[0], idat[i]); + } + for (i = 0; i < rdat[0]; i++) { + fprintf (fp, "rdat : %d of %f : %f\n", i, rdat[0], rdat[i]); + } + for (i = 0; i < *ns3; i++) { + fprintf (fp, "Sect 3 : %d of %d : %d\n", i, *ns3, is3[i]); + } + for (i = 0; i < *ns4; i++) { + fprintf (fp, "Sect 4 : %d of %d : %d\n", i, *ns4, is4[i]); + } + for (i = 0; i < *ns5; i++) { + fprintf (fp, "Sect 5 : %d of %d : %d\n", i, *ns5, is5[i]); + } + for (i = 0; i < *ns6; i++) { + fprintf (fp, "Sect 6 : %d of %d : %d\n", i, *ns6, is6[i]); + } + for (i = 0; i < *ns7; i++) { + fprintf (fp, "Sect 7 : %d of %d : %d\n", i, *ns7, is7[i]); + } + fprintf (fp, "Xmissp = %f\n", *xmissp); + fprintf (fp, "xmisss = %f\n", *xmisss); + if ((is5[9] == 0) || (is5[9] == 1) || (is5[9] == 2) || (is5[9] == 3)) { + if (is5[20] == 1) { + for (i = 0; i < *nd2x3; i++) { + fprintf (fp, "Int Data : %d of %d : %d\n", i, *nd2x3, iain[i]); + } + } + } else { + for (i = 0; i < *nd2x3; i++) { + fprintf (fp, "Float Data : %d of %d : %f\n", i, *nd2x3, ain[i]); + } + } + fprintf (fp, "ibitmap = %d\n", *ibitmap); + if (*ibitmap) { + for (i = 0; i < *nd2x3; i++) { + fprintf (fp, "Bitmap Data : %d of %d : %d\n", i, *nd2x3, ib[i]); + } + } + for (i = 0; i < *ndjer; i++) { + fprintf (fp, "jer(i,1) %d jer(i,2) %d\n", jer[i], jer[i + *ndjer]); + } + fprintf (fp, "kjer = %d\n", *kjer); + fprintf (fp, "iendpk = %d\n", *iendpk); + fclose (fp); + return 0; +} +#endif /* def notdef */ + +/***************************************************************************** + * clear() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Clear all the return values from the API. This is primarily for + * diagnostics purposes. + * + * ARGUMENTS + * ain = The data if the original data was float data. (nd2x3) (Output) + * iain = The data if the original data was integer data. (nd2x3) (Output) + * nd2x3 = length of ain, iain, ib (Input) + * idat = MDL local use data (if any were unpacked from Sect 2). (Output) + * nidat = length of idat. (Input) + * rdat = floating point local use data (Output) + * nrdat = length of rdat. (Input) + * is0, ns0 = Section 0 data, length of is0 (16) (Output) (Input) + * is1, ns1 = Section 1 data, length of is1 (21) (Output) (Input) + * is2, ns2 = Section 2 data, length of is2 (??) (Output) (Input) + * is3, ns3 = Section 3 data, length of is3 (96 or 1600) (Output) (Input) + * is4, ns4 = Section 4 data, length of is4 (60) (Output) (Input) + * is5, ns5 = Section 5 data, length of is5 (49) (Output) (Input) + * is6, ns6 = Section 6 data, length of is6 (6) (Output) (Input) + * is7, ns7 = Section 7 data, length of ns7 (8) (Output) (Input) + * ib = Bitmap if user requested it, and it was packed (Output) + * ibitmap = 0 means ib is invalid, 1 means ib is valid. (Output) + * xmissp = Primary missing value. (Output) + * xmisss = Secondary missing value (Output) + * iendpk = flag if there is more grids in the message. (Output) + * jer(ndjer,2) = error codes along with severity. (Output) + * ndjer = length of jer. (15) (Input) + * kjer = number of error messages stored in jer. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = Ok. + * -1 = Problems opening the file. + * + * HISTORY + * 12/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +/* +static void clear (float * ain, sInt4 * iain, sInt4 * nd2x3, sInt4 * idat, + sInt4 * nidat, float * rdat, sInt4 * nrdat, sInt4 * is0, + sInt4 * ns0, sInt4 * is1, sInt4 * ns1, sInt4 * is2, + sInt4 * ns2, sInt4 * is3, sInt4 * ns3, sInt4 * is4, + sInt4 * ns4, sInt4 * is5, sInt4 * ns5, sInt4 * is6, + sInt4 * ns6, sInt4 * is7, sInt4 * ns7, sInt4 * ib, + sInt4 * ibitmap, float * xmissp, float * xmisss, + sInt4 * iendpk, sInt4 * jer, sInt4 * ndjer, sInt4 * kjer) +{ + memset ((void *) ain, 0, *nd2x3 * sizeof (float)); + memset ((void *) iain, 0, *nd2x3 * sizeof (sInt4)); + memset ((void *) idat, 0, *nidat * sizeof (sInt4)); + memset ((void *) rdat, 0, *nrdat * sizeof (float)); + memset ((void *) is0, 0, *ns0 * sizeof (sInt4)); + memset ((void *) is1, 0, *ns1 * sizeof (sInt4)); + memset ((void *) is2, 0, *ns2 * sizeof (sInt4)); + memset ((void *) is3, 0, *ns3 * sizeof (sInt4)); + memset ((void *) is4, 0, *ns4 * sizeof (sInt4)); + if ((is5[9] == 2) || (is5[9] == 3)) { + *xmissp = 0; + *xmisss = 0; + } + memset ((void *) is5, 0, *ns5 * sizeof (sInt4)); + memset ((void *) is6, 0, *ns6 * sizeof (sInt4)); + memset ((void *) is7, 0, *ns7 * sizeof (sInt4)); + memset ((void *) ib, 0, *nd2x3 * sizeof (sInt4)); + *ibitmap = 0; + *iendpk = 0; + memset ((void *) jer, 0, 2 * *ndjer * sizeof (sInt4)); + *kjer = 0; +} +*/ + +/***************************************************************************** + * BigByteCpy() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * This is so we can copy upto 4 bytes from a big endian 4 byte int data + * stream. + * + * The reason this is needed is because the GRIB2 API required the GRIB2 + * message to be passed in as a big endian 4 byte int data stream instead of + * something more reasonable (such as a stream of 1 byte char's) By having + * this routine we reduce the number of memswaps of the message on a little + * endian system. + * + * ARGUMENTS + * dst = Where to copy the data to. (Output) + * ipack = The 4 byte int data stream. (Input) + * nd5 = length of ipack (Input) + * startInt = Which integer to start reading in ipack. (Input) + * startByte = Which byte in the startInt to start reading from. (Input) + * numByte = How many bytes to read. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: NULL + * + * HISTORY + * 12/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +static void BigByteCpy (sInt4 * dst, sInt4 * ipack, CPL_UNUSED sInt4 nd5, + unsigned int startInt, unsigned int startByte, + int numByte) +{ + static int Lshift[] = { 0, 8, 16, 24 }; /* Amounts to shift left by. */ + unsigned int intIndex; /* Where in ipack to read from. */ + unsigned int byteIndex; /* Where in intIndex to read from. */ + uInt4 curInt; /* An unsigned version of the current int. */ + unsigned int curByte; /* The current byte we have read. */ + int i; /* Loop counter over number of bytes to read. */ + + myAssert (numByte <= 4); + myAssert (startByte < 4); + *dst = 0; + intIndex = startInt; + byteIndex = startByte; + for (i = 0; i < numByte; i++) { + curInt = (uInt4) ipack[intIndex]; + curByte = (curInt << Lshift[byteIndex]) >> 24; + *dst = (*dst << 8) + curByte; + byteIndex++; + if (byteIndex == 4) { + byteIndex = 0; + intIndex++; + } + } +} + +/***************************************************************************** + * FindTemplateIDs() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * This is so we can find out which templates are in this GRIB2 message. + * Which allows us to determine if we can call the MDL routines, or if we + * should call the NCEP routines instead. + * + * ARGUMENTS + * ipack = The 4 byte int data stream. (Input) + * nd5 = length of ipack (Input) + * subgNum = Which subgrid to look at. (Input) + * gdsTmpl = The gds template number for this subgrid. (Output) + * pdsTmpl = The pds template number for this subgrid. (Output) + * drsTmpl = The drs template number for this subgrid. (Output) + * f_noBitmap = 0 has a bitmap, else doesn't have a bitmap. (Output) + * orderDiff = The order of the differencing in template 5.3 (1st, 2nd) (out) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = Ok. + * 2 = invalid section number + * + * HISTORY + * 12/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +static int FindTemplateIDs (sInt4 * ipack, sInt4 nd5, int subgNum, + sInt4 * gdsTmpl, sInt4 * pdsTmpl, + sInt4 * drsTmpl, sInt4 * numGrps, + uChar * f_noBitmap, sInt4 * orderDiff) +{ + unsigned int gNum = 0; /* Which sub group we are currently working with. */ + sInt4 offset; /* Where in the bytes stream we currently are. */ + sInt4 sectLen; /* The length of the current section. */ + sInt4 sectId; /* The current section number. */ + sInt4 li_temp; /* A temporary holder for the bitmap flag. */ + + /* Jump over section 0. */ + offset = 16; + while (gNum <= (unsigned int)subgNum) { + BigByteCpy (§Len, ipack, nd5, (offset / 4), (offset % 4), 4); + /* Check if we just read section 8. If so, then it is "7777" = + * 926365495 regardless of endian'ness. */ + if (sectLen == 926365495L) { +#ifdef DEBUG + printf ("Shouldn't see sect 8. Should stop after correct sect 5\n"); +#endif + return 2; + } + BigByteCpy (§Id, ipack, nd5, ((offset + 4) / 4), + ((offset + 4) % 4), 1); + switch (sectId) { + case 1: + case 2: + break; + case 3: + BigByteCpy (gdsTmpl, ipack, nd5, ((offset + 12) / 4), + ((offset + 12) % 4), 2); + break; + case 4: + BigByteCpy (pdsTmpl, ipack, nd5, ((offset + 7) / 4), + ((offset + 7) % 4), 2); + break; + case 5: + BigByteCpy (drsTmpl, ipack, nd5, ((offset + 9) / 4), + ((offset + 9) % 4), 2); + if ((*drsTmpl == 2) || (*drsTmpl == 3)) { + BigByteCpy (numGrps, ipack, nd5, ((offset + 31) / 4), + ((offset + 31) % 4), 4); + } else { + *numGrps = 0; + } + if (*drsTmpl == 3) { + BigByteCpy (&li_temp, ipack, nd5, ((offset + 44) / 4), + ((offset + 44) % 4), 1); + *orderDiff = li_temp; + } else { + *orderDiff = 0; + } + break; + case 6: + BigByteCpy (&li_temp, ipack, nd5, ((offset + 5) / 4), + ((offset + 5) % 4), 1); + if (li_temp == 255) { + *f_noBitmap = 1; + } + gNum++; + break; + case 7: + break; + default: +#ifdef DEBUG + printf ("Invalid section id %d.\n", sectId); +#endif + return 2; + } + offset += sectLen; + } + return 0; +} + +/***************************************************************************** + * unpk_grib2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * This procedure is the main API for decoding GRIB2 messages. It is + * intended to be a branching routine to call either the MDL GRIB2 library, + * or the NCEP GRIB2 library, depending on which template it sees in the + * message. + * + * ARGUMENTS + * kfildo = Unit number for output diagnostics (C ignores this). (Input) + * ain = contains the data if the original data was float data. + * (size = nd2x3) (Output) + * iain = contains the data if the original data was integer data. + * (size = nd2x3) (Output) + * nd2x3 = length of ain, iain, ib (Input) + * (at least the size of num grid points) + * idat = local use data if any that were unpacked from Section 2. (Output) + * nidat = length of idat. (Input) + * rdat = floating point local use data (Output) + * nrdat = length of rdat. (Input) + * is0 = Section 0 data (Output) + * ns0 = length of is0 (16 is fine) (Input) + * is1 = Section 1 data (Output) + * ns1 = length of is1 (21 is fine) (Input) + * is2 = Section 2 data (Output) + * ns2 = length of is2 () (Input) + * is3 = Section 3 data (Output) + * ns3 = length of is3 (96 or 1600) (Input) + * is4 = Section 4 data (Output) + * ns4 = length of is4 (60) (Input) + * is5 = Section 5 data (Output) + * ns5 = length of is5 (49 is fine) (Input) + * is6 = Section 6 data (Output) + * ns6 = length of is6 (6 is fine) (Input) + * is7 = Section 7 data (Output) + * ns7 = length of is7 (8 is fine) (Input) + * ib = Bitmap if user requested it, and it was packed (Output) + * ibitmap = 0 means ib is invalid, 1 means ib is valid. (Output) + * ipack = The message to unpack (This is assumed to be Big endian) (Input) + * nd5 = The size of ipack (Input) + * xmissp = The floating point representation for the primary missing value. + * (Input/Output) + * xmisss = The floating point representation for the secondary missing + * value (Input/Output) + * new = 1 if this is the first grid to be unpacked, else 0. (Input) + * iclean = 1 means the user wants the unpacked data returned without + * missing values in it. 0 means embed the missing values. (Input) + * l3264b = Integer word length in bits (32 or 64) (Input) + * iendpk = A 1 means no more grids in this message, a 0 means more grids. + * (Output) + * jer(ndjer,2) = error codes along with severity. (Output) + * ndjer = 1/2 length of jer. (>= 15) (Input) + * kjer = number of error messages stored in jer. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 12/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * Inefficiencies: have to memswap ipack multiple times. + * MDL handles is5[12], is5[23], and is5[27] in an "interesting" manner. + * MDL attempts to always return grids in scan mode 0100???? + * ToDo: Check length of parameters better. + * + * According to MDL's unpk_grib2.f, it currently supports (so for others we + * call the NCEP routines): + * TEMPLATE 3.0 EQUIDISTANT CYLINDRICAL LATITUDE/LONGITUDE + * TEMPLATE 3.10 MERCATOR + * TEMPLATE 3.20 POLAR STEREOGRAPHIC + * TEMPLATE 3.30 LAMBERT + * TEMPLATE 3.90 ORTHOGRAPHIC SPACE VIEW + * TEMPLATE 3.110 EQUATORIAL AZIMUTHAL EQUIDISTANT + * TEMPLATE 3.120 AZIMUTH-RANGE (RADAR) + * + * TEMPLATE 4.0 ANALYSIS OR FORECAST AT A LEVEL AND POINT + * TEMPLATE 4.1 INDIVIDUAL ENSEMBLE + * TEMPLATE 4.2 DERIVED FORECAST BASED ON ENSEMBLES + * TEMPLATE 4.8 AVERAGE, ACCUMULATION, EXTREMES + * TEMPLATE 4.20 RADAR + * TEMPLATE 4.30 SATELLITE + * + * TEMPLATE 5.0 SIMPLE PACKING + * TEMPLATE 5.2 COMPLEX PACKING + * TEMPLATE 5.3 COMPLEX PACKING AND SPATIAL DIFFERENCING + * + * Correction to "unpk_grib2.f" : It also supports: + * TEMPLATE 4.9 Probability forecast in a time interval + * + ***************************************************************************** + */ +void unpk_grib2 (sInt4 * kfildo, float * ain, sInt4 * iain, sInt4 * nd2x3, + sInt4 * idat, sInt4 * nidat, float * rdat, sInt4 * nrdat, + sInt4 * is0, sInt4 * ns0, sInt4 * is1, sInt4 * ns1, + sInt4 * is2, sInt4 * ns2, sInt4 * is3, sInt4 * ns3, + sInt4 * is4, sInt4 * ns4, sInt4 * is5, sInt4 * ns5, + sInt4 * is6, sInt4 * ns6, sInt4 * is7, sInt4 * ns7, + sInt4 * ib, sInt4 * ibitmap, sInt4 * ipack, sInt4 * nd5, + float * xmissp, float * xmisss, sInt4 * inew, + sInt4 * iclean, sInt4 * l3264b, sInt4 * iendpk, sInt4 * jer, + sInt4 * ndjer, sInt4 * kjer) +{ + unsigned char *c_ipack; /* The compressed data as char instead of sInt4 + * so it is easier to work with. */ + sInt4 gdsTmpl; + sInt4 pdsTmpl; + sInt4 drsTmpl; + sInt4 numGrps; + /* char f_useMDL = 0; */ /* Instructed 3/8/2005 10:30 to not use MDL. */ + uChar f_noBitmap; /* 0 if bitmap, else no bitmap. */ + sInt4 orderDiff; + + if (FindTemplateIDs (ipack, *nd5, 0, &gdsTmpl, &pdsTmpl, &drsTmpl, + &numGrps, &f_noBitmap, &orderDiff) != 0) { + jer[0 + *ndjer] = 2; + jer[0] = 3000; /* Couldn't figure out which templates are used. */ + *kjer = 1; + } + if ((gdsTmpl != 0) && (gdsTmpl != 10) && (gdsTmpl != 20) && + (gdsTmpl != 30) && (gdsTmpl != 90) && (gdsTmpl != 110) && + (gdsTmpl != 120)) { + /* f_useMDL = 0; */ + } + if ((pdsTmpl != 0) && (pdsTmpl != 1) && (pdsTmpl != 2) && + (pdsTmpl != 8) && (pdsTmpl != 9) && (pdsTmpl != 20) && + (pdsTmpl != 30)) { + /* f_useMDL = 0; */ + } + if ((drsTmpl != 0) && (drsTmpl != 2) && (drsTmpl != 3)) { + /* f_useMDL = 0; */ + } + /* MDL GRIB2 lib does not support drsTmpl 2 or 3 if there is a bitmap. */ + if ((!f_noBitmap) && ((drsTmpl == 2) || (drsTmpl == 3))) { + /* f_useMDL = 0; */ + } + /* MDL GRIB2 lib does not support anything but second order differencing. */ + if ((drsTmpl == 3) && (orderDiff != 2) && (orderDiff != 0)) { + /* f_useMDL = 0; */ + } + +#ifdef _FORTRAN + if (f_useMDL) { + jmin = (sInt4 *) malloc (numGrps * sizeof (sInt4)); + lbit = (sInt4 *) malloc (numGrps * sizeof (sInt4)); + nov = (sInt4 *) malloc (numGrps * sizeof (sInt4)); + iwork = (sInt4 *) malloc ((*nd2x3) * sizeof (sInt4)); + + UNPK_G2MDL (kfildo, jmin, lbit, nov, iwork, ain, iain, nd2x3, idat, + nidat, rdat, nrdat, is0, ns0, is1, ns1, is2, ns2, is3, ns3, + is4, ns4, is5, ns5, is6, ns6, is7, ns7, ib, ibitmap, ipack, + nd5, xmissp, xmisss, inew, iclean, l3264b, iendpk, jer, + ndjer, kjer); +/* + if (1==1) { + int i; + for (i=0; i < *nd2x3; i++) { + if (ain[i] != 9999) { + fprintf (stderr, "here grib2api.c: ain[%d] %f\n", i, ain[i]); + } + } + } +*/ + free (jmin); + free (lbit); + free (nov); + free (iwork); + + /* Check the behavior of the NCEP routines. */ +/* +#ifdef DEBUG + validate ("check2.txt", ain, iain, nd2x3, idat, nidat, rdat, nrdat, + is0, ns0, is1, ns1, is2, ns2, is3, ns3, is4, ns4, is5, ns5, + is6, ns6, is7, ns7, ib, ibitmap, xmissp, xmisss, iendpk, + jer, ndjer, kjer); + clear (ain, iain, nd2x3, idat, nidat, rdat, nrdat, is0, ns0, is1, ns1, + is2, ns2, is3, ns3, is4, ns4, is5, ns5, is6, ns6, is7, ns7, ib, + ibitmap, xmissp, xmisss, iendpk, jer, ndjer, kjer); +#ifdef LITTLE_ENDIAN + memswp (ipack, sizeof (sInt4), *nd5); +#endif + c_ipack = (unsigned char *) ipack; + unpk_g2ncep (kfildo, ain, iain, nd2x3, idat, nidat, rdat, nrdat, is0, + ns0, is1, ns1, is2, ns2, is3, ns3, is4, ns4, is5, ns5, + is6, ns6, is7, ns7, ib, ibitmap, c_ipack, nd5, xmissp, + xmisss, inew, iclean, l3264b, iendpk, jer, ndjer, kjer); +#ifdef LITTLE_ENDIAN + memswp (ipack, sizeof (sInt4), *nd5); +#endif + validate ("check1.txt", ain, iain, nd2x3, idat, nidat, rdat, nrdat, + is0, ns0, is1, ns1, is2, ns2, is3, ns3, is4, ns4, is5, ns5, + is6, ns6, is7, ns7, ib, ibitmap, xmissp, xmisss, iendpk, jer, + ndjer, kjer); +#endif +*/ + } else { +#endif + /* Since NCEP's code works with byte level stuff, we need to un-do the + * byte swap of the (int *) data, then cast it to an (unsigned char *) */ +#ifdef LITTLE_ENDIAN + /* Can't make this dependent on inew, since they could have a sequence + * of get first message... do stuff, get second message, which + * unfortunately means they would have to get the first message again, + * causing 2 swaps, and breaking their second request for the first + * message (as well as their second message). */ + memswp (ipack, sizeof (sInt4), *nd5); +#endif + c_ipack = (unsigned char *) ipack; + unpk_g2ncep (kfildo, ain, iain, nd2x3, idat, nidat, rdat, nrdat, is0, + ns0, is1, ns1, is2, ns2, is3, ns3, is4, ns4, is5, ns5, + is6, ns6, is7, ns7, ib, ibitmap, c_ipack, nd5, xmissp, + xmisss, inew, iclean, l3264b, iendpk, jer, ndjer, kjer); + +#ifdef LITTLE_ENDIAN + /* Swap back because we could be called again for the subgrid data. */ + memswp (ipack, sizeof (sInt4), *nd5); +#endif +#ifdef _FORTRAN + } +#endif +} + +/* Not sure I need this... It is intended to provide a way to call it from + * FORTRAN, but I'm not sure it is needed. */ +/* gcc has two __ if there is one _ in the procedure name. */ +void unpk_grib2__ (sInt4 * kfildo, float * ain, sInt4 * iain, + sInt4 * nd2x3, sInt4 * idat, sInt4 * nidat, float * rdat, + sInt4 * nrdat, sInt4 * is0, sInt4 * ns0, sInt4 * is1, + sInt4 * ns1, sInt4 * is2, sInt4 * ns2, sInt4 * is3, + sInt4 * ns3, sInt4 * is4, sInt4 * ns4, sInt4 * is5, + sInt4 * ns5, sInt4 * is6, sInt4 * ns6, sInt4 * is7, + sInt4 * ns7, sInt4 * ib, sInt4 * ibitmap, sInt4 * ipack, + sInt4 * nd5, float * xmissp, float * xmisss, + sInt4 * inew, sInt4 * iclean, sInt4 * l3264b, + sInt4 * iendpk, sInt4 * jer, sInt4 * ndjer, sInt4 * kjer) +{ + unpk_grib2 (kfildo, ain, iain, nd2x3, idat, nidat, rdat, nrdat, is0, ns0, + is1, ns1, is2, ns2, is3, ns3, is4, ns4, is5, ns5, is6, ns6, + is7, ns7, ib, ibitmap, ipack, nd5, xmissp, xmisss, inew, + iclean, l3264b, iendpk, jer, ndjer, kjer); +} + +/***************************************************************************** + * C_pkGrib2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * This procedure is the main API for encoding GRIB2 messages. It is + * intended to call NCEP's GRIB2 library. + * + * ARGUMENTS + * ain = Contains the data to pack if the original data was float data. + * (size = nd2x3) (Input) + * iain = Contains the data to pack if the original data was integer data. + * (size = nd2x3) (Input) + * nx = The number of rows in the gridded product. (Input) + * ny = The number of columns in the gridded products. (Input) + * idat = local use data if it is integer (stored in section 2). (Input) + * nidat = length of idat. (Input) + * rdat = local use data if it is a float (Input) + * nrdat = length of rdat. (Input) + * is0 = Section 0 data (element 7 should be set by caller) (Input/Output) + * ns0 = length of is0 (16 is fine) (Input) + * is1 = Section 1 data (Input/Output) + * ns1 = length of is1 (21 is fine) (Input) + * is3 = Section 3 data (Input/Output) + * ns3 = length of is3 (96 or 1600) (Input) + * is4 = Section 4 data (Input/Output) + * ns4 = length of is4 (60) (Input) + * is5 = Section 5 data (Input/Output) + * ns5 = length of is5 (49 is fine) (Input) + * is6 = Section 6 data (Input/Output) + * ns6 = length of is6 (6 is fine) (Input) + * is7 = Section 7 data (Input/Output) + * ns7 = length of is7 (8 is fine) (Input) + * ib = Bitmap if user requested it, and it was packed (Input/Output) + * ibitmap = 0 means ib is invalid, 1 means ib is valid. (Input/Output) + * ipack = The message to unpack (This is assumed to be Big endian) (Output) + * nd5 = The size of ipack (250 + NX*NY + (NX*NY)/8 + num local data) (In) + * missp = The integer representation for the primary missing value. (Input) + * xmissp = The float representation for the primary missing value. (Input) + * misss = The integer representation for the secondary missing value. (In) + * xmisss = The float representation for the secondary missing value (Input) + * new = 1 if this is the first grid to be unpacked, else 0. (Input) + * minpk = minimum size of groups in complex and second order differencing + * methods. Recommended value 14 (Input) + * iclean = 1 means no primary missing values embedded in the data field, + * 0 means there are primary missing values in the data (Input/Out) + * l3264b = Integer word length in bits (32 or 64) (Input) + * jer(ndjer,2) = error codes along with severity. (Output) + * ndjer = 1/2 length of jer. (>= 15) (Input) + * kjer = number of error messages stored in jer. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2004 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * Inefficiencies: have to memswap ipack multiple times. + * MDL handles is5[12], is5[23], and is5[27] in an "interesting" manner. + * MDL attempts to always return grids in scan mode 0100???? + * ToDo: Check length of parameters better. + * + * According to MDL's pk_grib2.f, it currently supports (so for others we + * call the NCEP routines): + * TEMPLATE 3.0 EQUIDISTANT CYLINDRICAL LATITUDE/LONGITUDE + * TEMPLATE 3.10 MERCATOR + * TEMPLATE 3.20 POLAR STEREOGRAPHIC + * TEMPLATE 3.30 LAMBERT + * TEMPLATE 3.90 ORTHOGRAPHIC SPACE VIEW + * TEMPLATE 3.110 EQUATORIAL AZIMUTHAL EQUIDISTANT + * TEMPLATE 3.120 AZIMUTH-RANGE (RADAR) + * + * TEMPLATE 4.0 ANALYSIS OR FORECAST AT A LEVEL AND POINT + * TEMPLATE 4.1 INDIVIDUAL ENSEMBLE + * TEMPLATE 4.2 DERIVED FORECAST BASED ON ENSEMBLES + * TEMPLATE 4.8 AVERAGE, ACCUMULATION, EXTREMES + * TEMPLATE 4.20 RADAR + * TEMPLATE 4.30 SATELLITE + * + * TEMPLATE 5.0 SIMPLE PACKING + * TEMPLATE 5.2 COMPLEX PACKING + * TEMPLATE 5.3 COMPLEX PACKING AND SPATIAL DIFFERENCING + * + * Correction to "pk_grib2.f" : It also supports: + * TEMPLATE 4.9 Probability forecast in a time interval + * + ***************************************************************************** + */ +int C_pkGrib2 (unsigned char *cgrib, sInt4 *sec0, sInt4 *sec1, + unsigned char *csec2, sInt4 lcsec2, + sInt4 *igds, sInt4 *igdstmpl, sInt4 *ideflist, + sInt4 idefnum, sInt4 ipdsnum, sInt4 *ipdstmpl, + float *coordlist, sInt4 numcoord, sInt4 idrsnum, + sInt4 *idrstmpl, float *fld, sInt4 ngrdpts, + sInt4 ibmap, sInt4 *bmap) +{ + int ierr; /* error value from grib2 library. */ + + if ((ierr = g2_create (cgrib, sec0, sec1)) == -1) { + /* Tried to use for version other than GRIB Ed 2 */ + return -1; + } + + if ((ierr = g2_addlocal (cgrib, csec2, lcsec2)) < 0) { + /* Some how got a bad section 2. Should be impossible unless an + * assert was broken. */ + return -2; + } + + if ((ierr = g2_addgrid (cgrib, igds, igdstmpl, ideflist, idefnum)) < 0) { + /* Some how got a bad section 3. Only way would be should be with an + * unsupported template number unless an assert was broken. */ + return -3; + } + + if ((ierr = g2_addfield (cgrib, ipdsnum, ipdstmpl, coordlist, numcoord, + idrsnum, idrstmpl, fld, ngrdpts, ibmap, + bmap)) < 0) { + return -4; + } + + if ((ierr = g2_gribend (cgrib)) < 0) { + return -5; + } + + return ierr; +} + +/***************************************************************************** + * pk_grib2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * This procedure is the main API for encoding GRIB2 messages. It is + * intended to be a branching routine to call either the MDL GRIB2 library, + * or the NCEP GRIB2 library, depending on which template it sees in the + * message. + * Currently it calls both for debug purposes. + * + * ARGUMENTS + * kfildo = Unit number for output diagnostics (C ignores this). (Input) + * ain = Contains the data to pack if the original data was float data. + * (size = nd2x3) (Input) + * iain = Contains the data to pack if the original data was integer data. + * (size = nd2x3) (Input) + * nx = The number of rows in the gridded product. (Input) + * ny = The number of columns in the gridded products. (Input) + * idat = local use data if it is integer (stored in section 2). (Input) + * nidat = length of idat. (Input) + * rdat = local use data if it is a float (Input) + * nrdat = length of rdat. (Input) + * is0 = Section 0 data (element 7 should be set by caller) (Input/Output) + * ns0 = length of is0 (16 is fine) (Input) + * is1 = Section 1 data (Input/Output) + * ns1 = length of is1 (21 is fine) (Input) + * is3 = Section 3 data (Input/Output) + * ns3 = length of is3 (96 or 1600) (Input) + * is4 = Section 4 data (Input/Output) + * ns4 = length of is4 (60) (Input) + * is5 = Section 5 data (Input/Output) + * ns5 = length of is5 (49 is fine) (Input) + * is6 = Section 6 data (Input/Output) + * ns6 = length of is6 (6 is fine) (Input) + * is7 = Section 7 data (Input/Output) + * ns7 = length of is7 (8 is fine) (Input) + * ib = Bitmap if user requested it, and it was packed (Input/Output) + * ibitmap = 0 means ib is invalid, 1 means ib is valid. (Input/Output) + * ipack = The message to unpack (This is assumed to be Big endian) (Output) + * nd5 = The size of ipack (250 + NX*NY + (NX*NY)/8 + num local data) (In) + * missp = The integer representation for the primary missing value. (Input) + * xmissp = The float representation for the primary missing value. (Input) + * misss = The integer representation for the secondary missing value. (In) + * xmisss = The float representation for the secondary missing value (Input) + * new = 1 if this is the first grid to be unpacked, else 0. (Input) + * minpk = minimum size of groups in complex and second order differencing + * methods. Recommended value 14 (Input) + * iclean = 1 means no primary missing values embedded in the data field, + * 0 means there are primary missing values in the data (Input/Out) + * l3264b = Integer word length in bits (32 or 64) (Input) + * jer(ndjer,2) = error codes along with severity. (Output) + * ndjer = 1/2 length of jer. (>= 15) (Input) + * kjer = number of error messages stored in jer. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2004 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * Inefficiencies: have to memswap ipack multiple times. + * MDL handles is5[12], is5[23], and is5[27] in an "interesting" manner. + * MDL attempts to always return grids in scan mode 0100???? + * ToDo: Check length of parameters better. + * + * According to MDL's pk_grib2.f, it currently supports (so for others we + * call the NCEP routines): + * TEMPLATE 3.0 EQUIDISTANT CYLINDRICAL LATITUDE/LONGITUDE + * TEMPLATE 3.10 MERCATOR + * TEMPLATE 3.20 POLAR STEREOGRAPHIC + * TEMPLATE 3.30 LAMBERT + * TEMPLATE 3.90 ORTHOGRAPHIC SPACE VIEW + * TEMPLATE 3.110 EQUATORIAL AZIMUTHAL EQUIDISTANT + * TEMPLATE 3.120 AZIMUTH-RANGE (RADAR) + * + * TEMPLATE 4.0 ANALYSIS OR FORECAST AT A LEVEL AND POINT + * TEMPLATE 4.1 INDIVIDUAL ENSEMBLE + * TEMPLATE 4.2 DERIVED FORECAST BASED ON ENSEMBLES + * TEMPLATE 4.8 AVERAGE, ACCUMULATION, EXTREMES + * TEMPLATE 4.20 RADAR + * TEMPLATE 4.30 SATELLITE + * + * TEMPLATE 5.0 SIMPLE PACKING + * TEMPLATE 5.2 COMPLEX PACKING + * TEMPLATE 5.3 COMPLEX PACKING AND SPATIAL DIFFERENCING + * + * Correction to "pk_grib2.f" : It also supports: + * TEMPLATE 4.9 Probability forecast in a time interval + * + ***************************************************************************** + */ +/* TODO: Make a better ifdef */ +void pk_grib2 (CPL_UNUSED sInt4 * kfildo, CPL_UNUSED float * ain, + CPL_UNUSED sInt4 * iain, CPL_UNUSED sInt4 * nx, + CPL_UNUSED sInt4 * ny, CPL_UNUSED sInt4 * idat, + CPL_UNUSED sInt4 * nidat, CPL_UNUSED float * rdat, + CPL_UNUSED sInt4 * nrdat, CPL_UNUSED sInt4 * is0, + CPL_UNUSED sInt4 * ns0, CPL_UNUSED sInt4 * is1, + CPL_UNUSED sInt4 * ns1, CPL_UNUSED sInt4 * is3, + CPL_UNUSED sInt4 * ns3, CPL_UNUSED sInt4 * is4, + CPL_UNUSED sInt4 * ns4, CPL_UNUSED sInt4 * is5, + CPL_UNUSED sInt4 * ns5, CPL_UNUSED sInt4 * is6, + CPL_UNUSED sInt4 * ns6, CPL_UNUSED sInt4 * is7, + CPL_UNUSED sInt4 * ns7, CPL_UNUSED sInt4 * ib, + CPL_UNUSED sInt4 * ibitmap, CPL_UNUSED sInt4 * ipack, + CPL_UNUSED sInt4 * nd5, CPL_UNUSED sInt4 * missp, + CPL_UNUSED float * xmissp, CPL_UNUSED sInt4 * misss, + CPL_UNUSED float * xmisss, CPL_UNUSED sInt4 * inew, + CPL_UNUSED sInt4 * minpk, CPL_UNUSED sInt4 * iclean, + CPL_UNUSED sInt4 * l3264b, CPL_UNUSED sInt4 * jer, + CPL_UNUSED sInt4 * ndjer, CPL_UNUSED sInt4 * kjer) +{ +#ifndef _FORTRAN + + printf ("Can not pack things unless using FORTRAN!\n"); + return; + +#else + + sInt4 gdsTmpl; + sInt4 pdsTmpl; + sInt4 drsTmpl; + sInt4 *jmax; + sInt4 *jmin; + sInt4 *lbit; + sInt4 *nov; + sInt4 *misslx; + sInt4 *newbox; + sInt4 *newboxp; + sInt4 *ia; +/* float *a;*/ + char f_useMDL = 1; + int i; + + myAssert (*ndjer >= 8); + /* Init the error handling array. */ + memset ((void *) jer, 0, 2 * *ndjer * sizeof (sInt4)); + for (i = 0; i < 8; i++) { + jer[i] = i * 100; + } + *kjer = 8; + + gdsTmpl = is3[13 - 1]; + pdsTmpl = is4[8 - 1]; + drsTmpl = is5[10 - 1]; + if ((gdsTmpl != 0) && (gdsTmpl != 10) && (gdsTmpl != 20) && + (gdsTmpl != 30) && (gdsTmpl != 90) && (gdsTmpl != 110) && + (gdsTmpl != 120)) { + f_useMDL = 0; + } + if ((pdsTmpl != 0) && (pdsTmpl != 1) && (pdsTmpl != 2) && + (pdsTmpl != 8) && (pdsTmpl != 9) && (pdsTmpl != 20) && + (pdsTmpl != 30)) { + f_useMDL = 0; + } + if ((drsTmpl != 0) && (drsTmpl != 2) && (drsTmpl != 3)) { + f_useMDL = 0; + } +/* + printf ("Forcing it to not use MDL encoder. \n"); + f_useMDL = 0; +*/ + if (f_useMDL) { + /* pk_cmplx.f ==> jmax(M), jmin(M), nov(M), lbit(M) */ + jmax = (sInt4 *) malloc ((*nx) * (*ny) * sizeof (sInt4)); + jmin = (sInt4 *) malloc ((*nx) * (*ny) * sizeof (sInt4)); + nov = (sInt4 *) malloc ((*nx) * (*ny) * sizeof (sInt4)); + lbit = (sInt4 *) malloc ((*nx) * (*ny) * sizeof (sInt4)); + /* pk_grib2.f ==> a(NX,NY), ia(NX,NY) */ +/* a = (float *) malloc ((*nx) * (*ny) * sizeof (float));*/ + ia = (sInt4 *) malloc ((*nx) * (*ny) * sizeof (sInt4)); + /* pack_gp.f ==> misslx(NDG = NXY = nx*ny) */ + misslx = (sInt4 *) malloc ((*nx) * (*ny) * sizeof (sInt4)); + /* Able to use jmax(nxy) for iwork(nxy) in int_map.f, and pk_missp.f */ + /* Able to use jmax(nxy) for work(nxy) in flt_map.f */ + /* reduce.f ==> newbox(ndg=nxy), newboxp(ndg=nxy) */ + newbox = (sInt4 *) malloc ((*nx) * (*ny) * sizeof (sInt4)); + newboxp = (sInt4 *) malloc ((*nx) * (*ny) * sizeof (sInt4)); + + /* other known automatic arrays... itemp(nidat) pk_sect2.f */ + /* other known automatic arrays... rtemp(nrdat) pk_sect2.f */ + + /* a and ia are equivalenced here. */ +#ifdef _FORTRAN + PK_G2MDL (kfildo, jmax, jmin, lbit, nov, misslx, (float *) ia, ia, + newbox, newboxp, ain, iain, nx, ny, idat, nidat, rdat, nrdat, + is0, ns0, is1, ns1, is3, ns3, is4, ns4, is5, ns5, is6, ns6, + is7, ns7, ib, ibitmap, ipack, nd5, missp, xmissp, misss, + xmisss, inew, minpk, iclean, l3264b, jer, ndjer, kjer); +#endif /* _FORTRAN */ + + free (jmax); + free (jmin); + free (nov); + free (lbit); + free (misslx); +/* free (a);*/ + free (ia); + free (newbox); + free (newboxp); + } else { + + +#ifdef PKNCEP +#ifdef LITTLE_ENDIAN + /* Can't make this dependent on inew, since they could have a sequence + * of get first message... do stuff, get second message, which + * unfortunately means they would have to get the first message again, + * causing 2 swaps, and breaking their second request for the first + * message (as well as their second message). */ +/* + memswp (ipack, sizeof (sInt4), *nd5); +*/ +#endif + c_ipack = (unsigned char *) ipack; + pk_g2ncep (kfildo, ain, iain, nx, ny, idat, nidat, rdat, nrdat, is0, + ns0, is1, ns1, is3, ns3, is4, ns4, is5, ns5, is6, ns6, is7, + ns7, ib, ibitmap, c_ipack, nd5, missp, xmissp, misss, xmisss, + inew, minpk, iclean, l3264b, jer, ndjer, kjer); + +#ifdef LITTLE_ENDIAN + /* Swap back because we could be called again for the subgrid data. */ + memswp (ipack, sizeof (sInt4), *nd5); +#endif +#else + + printf ("Unable to use MDL Pack library?\n"); + printf ("gdsTmpl : %d , pdsTmpl %d : drsTmpl %d\n", gdsTmpl, + pdsTmpl, drsTmpl); + jer[0 + *ndjer] = 31415926; + *kjer = 1; +#endif + } + +#endif /* _FORTRAN */ +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/grib2api.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/grib2api.h new file mode 100644 index 000000000..c72f1726b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/grib2api.h @@ -0,0 +1,56 @@ +/***************************************************************************** + * grib2api.h + * + * DESCRIPTION + * This file contains the header information needed to call the grib2 + * decoder library. + * + * HISTORY + * 12/2003 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifndef GRIB2API_H +#define GRIB2API_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "type.h" + +void unpk_grib2 (sInt4 *kfildo, float *ain, sInt4 *iain, sInt4 *nd2x3, + sInt4 *idat, sInt4 *nidat, float *rdat, sInt4 *nrdat, + sInt4 *is0, sInt4 *ns0, sInt4 *is1, sInt4 *ns1, sInt4 *is2, + sInt4 *ns2, sInt4 *is3, sInt4 *ns3, sInt4 *is4, sInt4 *ns4, + sInt4 *is5, sInt4 *ns5, sInt4 *is6, sInt4 *ns6, sInt4 *is7, + sInt4 *ns7, sInt4 *ib, sInt4 *ibitmap, sInt4 *ipack, + sInt4 *nd5, float *xmissp, float *xmisss, sInt4 *inew, + sInt4 *iclean, sInt4 *l3264b, sInt4 *iendpk, sInt4 *jer, + sInt4 *ndjer, sInt4 *kjer); + +int C_pkGrib2 (unsigned char *cgrib, sInt4 *sec0, sInt4 *sec1, + unsigned char *csec2, sInt4 lcsec2, + sInt4 *igds, sInt4 *igdstmpl, sInt4 *ideflist, + sInt4 idefnum, sInt4 ipdsnum, sInt4 *ipdstmpl, + float *coordlist, sInt4 numcoord, sInt4 idrsnum, + sInt4 *idrstmpl, float *fld, sInt4 ngrdpts, + sInt4 ibmap, sInt4 *bmap); + +void pk_grib2 (sInt4 * kfildo, float * ain, sInt4 * iain, sInt4 * nx, + sInt4 * ny, sInt4 * idat, sInt4 * nidat, float * rdat, + sInt4 * nrdat, sInt4 * is0, sInt4 * ns0, sInt4 * is1, + sInt4 * ns1, sInt4 * is3, sInt4 * ns3, sInt4 * is4, + sInt4 * ns4, sInt4 * is5, sInt4 * ns5, sInt4 * is6, + sInt4 * ns6, sInt4 * is7, sInt4 * ns7, sInt4 * ib, + sInt4 * ibitmap, sInt4 * ipack, sInt4 * nd5, sInt4 * missp, + float * xmissp, sInt4 * misss, float * xmisss, sInt4 * inew, + sInt4 * minpk, sInt4 * iclean, sInt4 * l3264b, sInt4 * jer, + sInt4 * ndjer, sInt4 * kjer); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* GRIB2API_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/inventory.cpp b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/inventory.cpp new file mode 100644 index 000000000..950c63906 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/inventory.cpp @@ -0,0 +1,1195 @@ +/***************************************************************************** + * inventory.c + * + * DESCRIPTION + * This file contains the code needed to do a quick inventory of the GRIB2 + * file. The intent is to enable one to figure out which message in a GRIB + * file one is after without needing to call the FORTRAN library. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * 12/2002 Tim Kempisty, Ana Canizares, Tim Boyer, & Marc Saccucci + * (TK,AC,TB,&MS): Code Review 1. + * + * NOTES + ***************************************************************************** + */ +#include +#include +#include +#include +#include "clock.h" +#include "memendian.h" +#include "fileendian.h" +#include "degrib2.h" +#include "degrib1.h" +#include "tdlpack.h" +#include "myerror.h" +#include "myutil.h" +#include "myassert.h" +#include "inventory.h" +#include "metaname.h" +#include "filedatasource.h" + +#define SECT0LEN_BYTE 16 + +typedef union { + sInt4 li; + char buffer[4]; +} wordType; + +/***************************************************************************** + * GRIB2InventoryFree() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * Free's any memory that was allocated for the inventory of a single grib + * message + * + * ARGUMENTS + * inv = Pointer to the inventory of a single grib message. (Input/Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (TK,AC,TB,&MS): Code Review. + * 7/2003 AAT: memwatch detected unfreed inv->unitName + * + * NOTES + ***************************************************************************** + */ +void GRIB2InventoryFree (inventoryType *inv) +{ + free (inv->element); + inv->element = NULL; + free (inv->comment); + inv->comment = NULL; + free (inv->unitName); + inv->unitName = NULL; + free (inv->shortFstLevel); + inv->shortFstLevel = NULL; + free (inv->longFstLevel); + inv->longFstLevel = NULL; +} + +/***************************************************************************** + * GRIB2InventoryPrint() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * Prints to standard out, an inventory of the file, assuming one has an + * array of invenories of single grib messages. + * + * ARGUMENTS + * Inv = Pointer to an Array of inventories to print. (Input) + * LenInv = Length of the Array Inv (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (TK,AC,TB,&MS): Code Review. + * 1/2004 AAT: Added short form of First level to print out. + * 3/2004 AAT: Switched from "#, Byte, ..." to "MsgNum, Byte, ..." + * + * NOTES + ***************************************************************************** + */ +void GRIB2InventoryPrint (inventoryType *Inv, uInt4 LenInv) +{ + uInt4 i; /* Counter of which inventory we are printing. */ + double delta; /* Difference between valid and reference time. */ + char refTime[25]; /* Used to store the formatted reference time. */ + char validTime[25]; /* Used to store the formatted valid time. */ + + printf ("MsgNum, Byte, GRIB-Version, elem, level, reference(UTC)," + " valid(UTC), Proj(hr)\n"); + fflush (stdout); + for (i = 0; i < LenInv; i++) { +/* strftime (refTime, 25, "%m/%d/%Y %H:%M", gmtime (&(Inv[i].refTime)));*/ + Clock_Print (refTime, 25, Inv[i].refTime, "%m/%d/%Y %H:%M", 0); +/* strftime (validTime, 25, "%m/%d/%Y %H:%M", + gmtime (&(Inv[i].validTime)));*/ + Clock_Print (validTime, 25, Inv[i].validTime, "%m/%d/%Y %H:%M", 0); + delta = (Inv[i].validTime - Inv[i].refTime) / 3600.; + delta = myRound (delta, 2); + if (Inv[i].comment == NULL) { + printf ("%d.%d, %d, %d, %s, %s, %s, %s, %.2f\n", + Inv[i].msgNum, Inv[i].subgNum, Inv[i].start, + Inv[i].GribVersion, Inv[i].element, Inv[i].shortFstLevel, + refTime, validTime, delta); + fflush (stdout); + } else { + printf ("%d.%d, %d, %d, %s=\"%s\", %s, %s, %s, %.2f\n", + Inv[i].msgNum, Inv[i].subgNum, Inv[i].start, + Inv[i].GribVersion, Inv[i].element, Inv[i].comment, + Inv[i].shortFstLevel, refTime, validTime, delta); + fflush (stdout); + } + } +} + +/***************************************************************************** + * InventoryParseTime() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * To parse the time data from a grib2 char array to a time_t in UTC + * seconds from the epoch. This is very similar to metaparse.c:ParseTime + * except using char * instead of sInt4 + * + * ARGUMENTS + * is = The char array to read the time info from. (Input) + * AnsTime = The time_t value to fill with the resulting time. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 11/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (TK,AC,TB,&MS): Code Review. + * + * NOTES + * 1) Couldn't use the default time_zone variable (concern over portability + * issues), so we print the hours, and compare them to the hours we had + * intended. Then subtract the difference from the AnsTime. + * 2) Similar to metaparse.c:ParseTime except using char * instead of sInt4 + ***************************************************************************** + */ +static int InventoryParseTime (char *is, double *AnsTime) +{ + /* struct tm time; *//* A temporary variable to put the time info into. */ + /* char buffer[10]; *//* Used when printing the AnsTime's Hr. */ + /* int timeZone; *//* The adjustment in Hr needed to get the right UTC * time. */ + short int si_temp; /* Temporarily stores the year as a short int to fix + * possible endian problems. */ + +/* memset (&time, 0, sizeof (struct tm));*/ + MEMCPY_BIG (&si_temp, is + 0, sizeof (short int)); + if ((si_temp < 1900) || (si_temp > 2100)) { + return -1; + } + if ((is[2] > 12) || (is[3] == 0) || (is[3] > 31) || (is[4] > 24) || + (is[5] > 60) || (is[6] > 61)) { + return -1; + } + Clock_ScanDate (AnsTime, si_temp, is[2], is[3]); + *AnsTime += is[4] * 3600. + is[5] * 60. + is[6]; +/* + time.tm_year = si_temp - 1900; + time.tm_mon = is[2] - 1; + time.tm_mday = is[3]; + time.tm_hour = is[4]; + time.tm_min = is[5]; + time.tm_sec = is[6]; + *AnsTime = mktime (&time) - (Clock_GetTimeZone () * 3600); +*/ + /* Cheap method of getting global time_zone variable. */ +/* + strftime (buffer, 10, "%H", gmtime (AnsTime)); + timeZone = atoi (buffer) - is[4]; + if (timeZone < 0) { + timeZone += 24; + } + *AnsTime = *AnsTime - (timeZone * 3600); +*/ + return 0; +} + +/***************************************************************************** + * GRIB2SectToBuffer() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * To read in a GRIB2 section into a buffer. Reallocates space for the + * section if buffLen < secLen. Reads in secLen and checks that the section + * is valid, and the file is large enough to hold the entire section. + * + * ARGUMENTS + * fp = Opened file pointing to the section in question. (Input/Output) + * gribLen = The total length of the grib message. (Input) + * sect = Which section we think we are reading. + * If it is -1, then set it to the section the file says we are + * reading (useful for optional sect 2)) (Input/Output). + * secLen = The length of this section (Output) + * buffLen = Allocated length of buff (Input/Output) + * buff = Stores the section (Output) + * + * FILES/DATABASES: + * An already opened GRIB2 file pointer, already at section in question. + * + * RETURNS: int (could use errSprintf()) + * 0 = Ok. + * -1 = Ran out of file. + * -2 = Section was miss-labeled. + * + * HISTORY + * 11/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (TK,AC,TB,&MS): Code Review. + * 8/2003 AAT: Removed dependence on curTot + * + * NOTES + * May want to put this in degrib2.c + ***************************************************************************** + */ +static int GRIB2SectToBuffer (DataSource &fp, + CPL_UNUSED uInt4 gribLen, + sChar *sect, + uInt4 *secLen, uInt4 *buffLen, char **buff) +{ + char *buffer = *buff; /* Local ptr to buff to reduce ptr confusion. */ + + if (FREAD_BIG (secLen, sizeof (sInt4), 1, fp) != 1) { + if (*sect != -1) { + errSprintf ("ERROR: Ran out of file in Section %d\n", *sect); + } else { + errSprintf ("ERROR: Ran out of file in GRIB2SectToBuffer\n"); + } + return -1; + } + if (*buffLen < *secLen) { + *buffLen = *secLen; + *buff = (char *) realloc ((void *) *buff, *buffLen * sizeof (char)); + buffer = *buff; + } + + if (fp.DataSourceFread (buffer, sizeof (char), *secLen - sizeof (sInt4)) != + *secLen - sizeof (sInt4)) { + if (*sect != -1) { + errSprintf ("ERROR: Ran out of file in Section %d\n", *sect); + } else { + errSprintf ("ERROR: Ran out of file in GRIB2SectToBuffer\n"); + } + return -1; + } + if (*sect == -1) { + *sect = buffer[5 - 5]; + } else if (buffer[5 - 5] != *sect) { + errSprintf ("ERROR: Section %d misslabeled\n", *sect); + return -2; + } + return 0; +} + +/***************************************************************************** + * GRIB2SectJump() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To jump past a GRIB2 section. Reads in secLen and checks that the + * section is valid. + * + * ARGUMENTS + * fp = Opened file pointing to the section in question. (Input/Output) + * gribLen = The total length of the grib message. (Input) + * sect = Which section we think we are reading. + * If it is -1, then set it to the section the file says we are + * reading (useful for optional sect 2)) (Input/Output). + * secLen = The length of this section (Output) + * + * FILES/DATABASES: + * An already opened GRIB2 file pointer, already at section in question. + * + * RETURNS: int (could use errSprintf()) + * 0 = Ok. + * -1 = Ran out of file. + * -2 = Section was miss-labeled. + * + * HISTORY + * 3/2003 Arthur Taylor (MDL/RSIS): Created. + * 8/2003 AAT: Removed dependence on curTot, which was used to compute if + * the file should be large enough for the fseek, but didn't check + * if it actually was. + * + * NOTES + * May want to put this in degrib2.c + ***************************************************************************** + */ +static int GRIB2SectJump (DataSource &fp, + CPL_UNUSED sInt4 gribLen, sChar *sect, uInt4 *secLen) +{ + char sectNum; /* Validates that we are on the correct section. */ + int c; /* Check that the fseek is still inside the file. */ + + if (FREAD_BIG (secLen, sizeof (sInt4), 1, fp) != 1) { + if (*sect != -1) { + errSprintf ("ERROR: Ran out of file in Section %d\n", *sect); + } else { + errSprintf ("ERROR: Ran out of file in GRIB2SectSkip\n"); + } + return -1; + } + if (fp.DataSourceFread (§Num, sizeof (char), 1) != 1) { + if (*sect != -1) { + errSprintf ("ERROR: Ran out of file in Section %d\n", *sect); + } else { + errSprintf ("ERROR: Ran out of file in GRIB2SectSkip\n"); + } + return -1; + } + if (*sect == -1) { + *sect = sectNum; + } else if (sectNum != *sect) { + errSprintf ("ERROR: Section %d misslabeled\n", *sect); + return -2; + } + /* Since fseek does not give an error if we jump outside the file, we test + * it by using fgetc / ungetc. */ + fp.DataSourceFseek (*secLen - 5, SEEK_CUR); + if ((c = fp.DataSourceFgetc()) == EOF) { + errSprintf ("ERROR: Ran out of file in Section %d\n", *sect); + return -1; + } else { + fp.DataSourceUngetc(c); + } + return 0; +} + +/***************************************************************************** + * GRIB2Inventory2to7() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Inventories sections 3 to 7, filling out the inv record with the data in + * section 4. (Note: No Call to FORTRAN routines here). + * + * ARGUMENTS + * sectNum = Which section we are currently reading. (Input) + * fp = An opened file pointer to the file to the inventory of (In/Out) + * gribLen = The total length of the grib message. (Input) + * buffLen = length of buffer. (Input) + * buffer = Holds a given section. (Input) + * inv = The current inventory record to fill out. (Output) + * prodType = The GRIB2 type of product: 0 is meteo product, 1 is hydro, + * 2 is land, 3 is space, 10 is oceanographic. (Input) + * center = Who produced it (Input) + * subcenter = A sub group of center that actually produced it (Input) + * + * FILES/DATABASES: + * + * RETURNS: int (could use errSprintf()) + * 0 = "Ok" + * -5 = Problems Reading in section 2 or 3 + * -6 = Problems Reading in section 3 + * -7 = Problems Reading in section 4 + * -8 = Problems Parsing section 4. + * -9 = Problems Reading in section 5 + * -10 = Problems Reading in section 6 + * -11 = Problems Reading in section 7 + * + * HISTORY + * 3/2003 Arthur Taylor (MDL/RSIS): Created. + * 4/2003 AAT: Modified to not have prodType, cat, subcat, templat in + * inventoryType structure. + * 8/2003 AAT: curTot no longer serves a purpose. + * 1/2004 AAT: Added center/subcenter. + * + * NOTES + ***************************************************************************** + */ +static int GRIB2Inventory2to7 (sChar sectNum, DataSource &fp, sInt4 gribLen, + uInt4 *buffLen, char **buffer, + inventoryType *inv, uChar prodType, + unsigned short int center, + unsigned short int subcenter) +{ + uInt4 secLen; /* The length of the current section. */ + sInt4 foreTime; /* forecast time (NDFD treats as "projection") */ + uChar foreTimeUnit; /* The time unit of the "forecast time". */ + /* char *element; *//* Holds the name of the current variable. */ + /* char *comment; *//* Holds more comments about current variable. */ + /* char *unitName; *//* Holds the name of the unit [K] [%] .. etc */ + int convert; /* Enum type of unit conversions (metaname.c), + * Conversion method for this variable's unit. */ + uChar cat; /* General category of Meteo Product. */ + unsigned short int templat; /* The section 4 template number. */ + uChar subcat; /* Specific subcategory of Product. */ + uChar fstSurfType; /* Type of the first fixed surface. */ + double fstSurfValue; /* Value of first fixed surface. */ + sInt4 value; /* The scaled value from GRIB2 file. */ + sChar factor; /* The scaled factor from GRIB2 file */ + sChar scale; /* Surface scale as opposed to probility factor. */ + uChar sndSurfType; /* Type of the second fixed surface. */ + double sndSurfValue; /* Value of second fixed surface. */ + sChar f_sndValue; /* flag if SndValue is valid. */ + uChar timeRangeUnit; + sInt4 lenTime; /* Used by parseTime to tell difference betweeen 8hr + * average and 1hr average ozone. */ + uChar genID; /* The Generating process ID (used for GFS MOS) */ + uChar probType; /* The probability type */ + double lowerProb; /* The lower limit on probability forecast if + * template 4.5 or 4.9 */ + double upperProb; /* The upper limit on probability forecast if + * template 4.5 or 4.9 */ + uChar timeIncrType; + sChar percentile = 0; + + if ((sectNum == 2) || (sectNum == 3)) { + /* Jump past section (2 or 3). */ + sectNum = -1; + if (GRIB2SectJump (fp, gribLen, §Num, &secLen) != 0) { + errSprintf ("ERROR: Problems Jumping past section 2 || 3\n"); + return -6; + } + if ((sectNum != 2) && (sectNum != 3)) { + errSprintf ("ERROR: Section 2 or 3 misslabeled\n"); + return -5; + } else if (sectNum == 2) { + /* Jump past section 3. */ + sectNum = 3; + if (GRIB2SectJump (fp, gribLen, §Num, &secLen) != 0) { + errSprintf ("ERROR: Problems Jumping past section 3\n"); + return -6; + } + } + } + /* Read section 4 into buffer. */ + sectNum = 4; + if (GRIB2SectToBuffer (fp, gribLen, §Num, &secLen, buffLen, + buffer) != 0) { + errSprintf ("ERROR: Problems with section 4\n"); + return -7; + } +/* +enum { GS4_ANALYSIS, GS4_ENSEMBLE, GS4_DERIVED, GS4_PROBABIL_PNT = 5, + GS4_STATISTIC = 8, GS4_PROBABIL_TIME = 9, GS4_PERCENTILE = 10, + GS4_RADAR = 20, GS4_SATELLITE = 30 +}; +*/ + /* Parse the interesting data out of sect 4. */ + MEMCPY_BIG (&templat, *buffer + 8 - 5, sizeof (short int)); + if ((templat != GS4_ANALYSIS) && (templat != GS4_ENSEMBLE) + && (templat != GS4_DERIVED) + && (templat != GS4_PROBABIL_PNT) && (templat != GS4_STATISTIC) + && (templat != GS4_PROBABIL_TIME) && (templat != GS4_PERCENTILE) + && (templat != GS4_ENSEMBLE_STAT) + && (templat != GS4_RADAR) && (templat != GS4_SATELLITE) + && (templat != GS4_DERIVED_INTERVAL)) { + errSprintf ("This was only designed for templates 0, 1, 2, 5, 8, 9, " + "10, 11, 12, 20, 30\n"); + return -8; + } + cat = (*buffer)[10 - 5]; + subcat = (*buffer)[11 - 5]; + genID = 0; + probType = 0; + lowerProb = 0; + upperProb = 0; + if ((templat == GS4_RADAR) || (templat == GS4_SATELLITE) || + (templat == 254)) { + inv->foreSec = 0; + inv->validTime = inv->refTime; + timeIncrType = 255; + timeRangeUnit = 255; + lenTime = 0; + } else { + genID = (*buffer)[14 - 5]; + /* Compute forecast time. */ + foreTimeUnit = (*buffer)[18 - 5]; + MEMCPY_BIG (&foreTime, *buffer + 19 - 5, sizeof (sInt4)); + if (ParseSect4Time2sec (foreTime, foreTimeUnit, &(inv->foreSec)) != 0) { + errSprintf ("unable to convert TimeUnit: %d \n", foreTimeUnit); + return -8; + } + /* Compute valid time. */ + inv->validTime = inv->refTime + inv->foreSec; + timeIncrType = 255; + timeRangeUnit = 1; + lenTime = (sInt4) (inv->foreSec / 3600); + switch (templat) { + case GS4_PROBABIL_PNT: /* 4.5 */ + probType = (*buffer)[37 - 5]; + factor = (sChar) (*buffer)[38 - 5]; + MEMCPY_BIG (&value, *buffer + 39 - 5, sizeof (sInt4)); + lowerProb = value * pow (10.0, -1 * factor); + factor = (sChar) (*buffer)[43 - 5]; + MEMCPY_BIG (&value, *buffer + 44 - 5, sizeof (sInt4)); + upperProb = value * pow (10.0, -1 * factor); + break; + case GS4_DERIVED_INTERVAL: /* 4.12 */ + if (InventoryParseTime (*buffer + 37 - 5, &(inv->validTime)) != 0) { + printf ("Warning: Investigate Template 4.12 bytes 37-43\n"); + inv->validTime = inv->refTime + inv->foreSec; + } + timeIncrType = (*buffer)[50 - 5]; + timeRangeUnit = (*buffer)[51 - 5]; + MEMCPY_BIG (&lenTime, *buffer + 52 - 5, sizeof (sInt4)); +/* If lenTime == missing (2^32 -1) we might do something, but not with 255.*/ +/* + if (lenTime == 255) { + lenTime = (inv->validTime - + (inv->refTime + inv->foreSec)) / 3600; + } +*/ + break; + case GS4_PERCENTILE: /* 4.10 */ + percentile = (*buffer)[35 - 5]; + if (InventoryParseTime (*buffer + 36 - 5, &(inv->validTime)) != 0) { + printf ("Warning: Investigate Template 4.10 bytes 36-42\n"); + inv->validTime = inv->refTime + inv->foreSec; + } + timeIncrType = (*buffer)[49 - 5]; + timeRangeUnit = (*buffer)[50 - 5]; + MEMCPY_BIG (&lenTime, *buffer + 51 - 5, sizeof (sInt4)); +/* If lenTime == missing (2^32 -1) we might do something, but not with 255.*/ +/* + if (lenTime == 255) { + lenTime = (inv->validTime - + (inv->refTime + inv->foreSec)) / 3600; + } +*/ + break; + case GS4_STATISTIC: /* 4.8 */ + if (InventoryParseTime (*buffer + 35 - 5, &(inv->validTime)) != 0) { + printf ("Warning: Investigate Template 4.8 bytes 35-41\n"); + inv->validTime = inv->refTime + inv->foreSec; + } + timeIncrType = (*buffer)[48 - 5]; + timeRangeUnit = (*buffer)[49 - 5]; + MEMCPY_BIG (&lenTime, *buffer + 50 - 5, sizeof (sInt4)); +/* If lenTime == missing (2^32 -1) we might do something, but not with 255.*/ +/* + if (lenTime == 255) { + lenTime = (inv->validTime - + (inv->refTime + inv->foreSec)) / 3600; + } +*/ + break; + case GS4_ENSEMBLE_STAT: /* 4.11 */ + if (InventoryParseTime (*buffer + 38 - 5, &(inv->validTime)) != 0) { + printf ("Warning: Investigate Template 4.11 bytes 38-44\n"); + inv->validTime = inv->refTime + inv->foreSec; + } + timeIncrType = (*buffer)[51 - 5]; + timeRangeUnit = (*buffer)[52 - 5]; + MEMCPY_BIG (&lenTime, *buffer + 53 - 5, sizeof (sInt4)); +/* If lenTime == missing (2^32 -1) we might do something, but not with 255.*/ +/* + if (lenTime == 255) { + lenTime = (inv->validTime - + (inv->refTime + inv->foreSec)) / 3600; + } +*/ + break; + case GS4_PROBABIL_TIME: /* 4.9 */ + probType = (*buffer)[37 - 5]; + if ((uChar) (*buffer)[38 - 5] > 128) { + factor = 128 - (uChar) (*buffer)[38 - 5]; + } else { + factor = (*buffer)[38 - 5]; + } + MEMCPY_BIG (&value, *buffer + 39 - 5, sizeof (sInt4)); + lowerProb = value * pow (10.0, -1 * factor); + + if ((uChar) (*buffer)[43 - 5] > 128) { + factor = 128 - (uChar) (*buffer)[43 - 5]; + } else { + factor = (*buffer)[43 - 5]; + } + MEMCPY_BIG (&value, *buffer + 44 - 5, sizeof (sInt4)); + upperProb = value * pow (10.0, -1 * factor); + + if (InventoryParseTime (*buffer + 48 - 5, &(inv->validTime)) != 0) { + printf ("Warning: Investigate Template 4.9 bytes 48-54\n"); + inv->validTime = inv->refTime + inv->foreSec; + } + timeIncrType = (*buffer)[61 - 5]; + timeRangeUnit = (*buffer)[62 - 5]; + MEMCPY_BIG (&lenTime, *buffer + 63 - 5, sizeof (sInt4)); +/* If lenTime == missing (2^32 -1) we might do something, but not with 255.*/ +/* + if (lenTime == 255) { + lenTime = (inv->validTime - + (inv->refTime + inv->foreSec)) / 3600; + } +*/ + break; + } + } + + if (timeRangeUnit == 255) { + timeRangeUnit = 1; + lenTime = (sInt4) ((inv->validTime - inv->foreSec - inv->refTime) / + 3600); + } +/* myAssert (timeRangeUnit == 1);*/ + /* Try to convert lenTime to hourly. */ + if (timeRangeUnit == 0) { + lenTime = (sInt4) (lenTime / 60.); + timeRangeUnit = 1; + } else if (timeRangeUnit == 1) { + } else if (timeRangeUnit == 2) { + lenTime = lenTime * 24; + timeRangeUnit = 1; + } else if (timeRangeUnit == 10) { + lenTime = lenTime * 3; + timeRangeUnit = 1; + } else if (timeRangeUnit == 11) { + lenTime = lenTime * 6; + timeRangeUnit = 1; + } else if (timeRangeUnit == 12) { + lenTime = lenTime * 12; + timeRangeUnit = 1; + } else if (timeRangeUnit == 13) { + lenTime = (sInt4) (lenTime / 3600.); + timeRangeUnit = 1; + } else { + printf ("Can't handle this timeRangeUnit\n"); + myAssert (timeRangeUnit == 1); + } + if (lenTime == GRIB2MISSING_s4) { + lenTime = 0; + } + /* Find out what the name of this variable is. */ + ParseElemName (center, subcenter, prodType, templat, cat, subcat, + lenTime, timeIncrType, genID, probType, lowerProb, + upperProb, &(inv->element), &(inv->comment), + &(inv->unitName), &convert, percentile); +/* + if (strcmp (element, "") == 0) { + mallocSprintf (&(inv->element), "unknown"); + mallocSprintf (&(inv->unitName), "[%s]", unitName); + if (strcmp (comment, "unknown") == 0) { + mallocSprintf (&(inv->comment), "(prodType %d, cat %d, subcat %d)" + " [%s]", prodType, cat, subcat, unitName); + } else { + mallocSprintf (&(inv->comment), "%s [%s]", comment, unitName); + } + } else { + if (IsData_MOS (center, subcenter)) { + * See : http://www.nco.ncep.noaa.gov/pmb/docs/on388/tablea.html * + if (genID == 96) { + inv->element = (char *) malloc ((1 + 7 + strlen (element)) + * sizeof (char)); + sprintf (inv->element, "MOSGFS-%s", element); + } else { + inv->element = (char *) malloc ((1 + 4 + strlen (element)) + * sizeof (char)); + sprintf (inv->element, "MOS-%s", element); + } + } else { + inv->element = (char *) malloc ((1 + strlen (element)) + * sizeof (char)); + strcpy (inv->element, element); + } + mallocSprintf (&(inv->unitName), "[%s]", unitName); + mallocSprintf (&(inv->comment), "%s [%s]", comment, unitName); +* + inv->unitName = (char *) malloc ((1 + 2 + strlen (unitName)) + * sizeof (char)); + sprintf (inv->unitName, "[%s]", unitName); + inv->comment = (char *) malloc ((1 + 3 + strlen (unitName) + strlen (comment)) + * sizeof (char)); + sprintf (inv->comment, "%s [%s]", comment, unitName); +* + } +*/ + + if ((templat == GS4_RADAR) || (templat == GS4_SATELLITE) + || (templat == 254) || (templat == 1000) || (templat == 1001) + || (templat == 1002)) { + reallocSprintf (&(inv->shortFstLevel), "0 undefined"); + reallocSprintf (&(inv->longFstLevel), "0.000[-] undefined ()"); + } else { + fstSurfType = (*buffer)[23 - 5]; + scale = (*buffer)[24 - 5]; + MEMCPY_BIG (&value, *buffer + 25 - 5, sizeof (sInt4)); + if ((value == GRIB2MISSING_s4) || (scale == GRIB2MISSING_s1)) { + fstSurfValue = 0; + } else { + fstSurfValue = value * pow (10.0, (int) (-1 * scale)); + } + sndSurfType = (*buffer)[29 - 5]; + scale = (*buffer)[30 - 5]; + MEMCPY_BIG (&value, *buffer + 31 - 5, sizeof (sInt4)); + if ((value == GRIB2MISSING_s4) || (scale == GRIB2MISSING_s1) || + (sndSurfType == GRIB2MISSING_u1)) { + sndSurfValue = 0; + f_sndValue = 0; + } else { + sndSurfValue = value * pow (10.0, -1 * scale); + f_sndValue = 1; + } + + ParseLevelName (center, subcenter, fstSurfType, fstSurfValue, + f_sndValue, sndSurfValue, &(inv->shortFstLevel), + &(inv->longFstLevel)); + } + + /* Jump past section 5. */ + sectNum = 5; + if (GRIB2SectJump (fp, gribLen, §Num, &secLen) != 0) { + errSprintf ("ERROR: Problems Jumping past section 5\n"); + return -9; + } + /* Jump past section 6. */ + sectNum = 6; + if (GRIB2SectJump (fp, gribLen, §Num, &secLen) != 0) { + errSprintf ("ERROR: Problems Jumping past section 6\n"); + return -10; + } + /* Jump past section 7. */ + sectNum = 7; + if (GRIB2SectJump (fp, gribLen, §Num, &secLen) != 0) { + errSprintf ("ERROR: Problems Jumping past section 7\n"); + return -11; + } + return 0; +} + +/***************************************************************************** + * GRIB2Inventory() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * Fills out an inventory structure for each GRIB message in a GRIB file, + * without calling the FORTRAN routines to unpack the message. It returns + * the number of messages it found, or a negative number signifying an error. + * + * ARGUMENTS + * filename = File to do the inventory of. (Input) + * Inv = The resultant array of inventories. (Output) + * LenInv = Length of the Array Inv (Output) + * numMsg = # of messages to inventory (0 = all, 1 = just first) (In) + * msgNum = MsgNum to start with, MsgNum of last message (Input/Output) + * + * FILES/DATABASES: + * Opens a GRIB2 file for reading given its filename. + * + * RETURNS: int (could use errSprintf()) + * +# = number of GRIB2 messages in the file. + * -1 = Problems opening file for read. + * -2 = Problems in section 0 + * -3 = Ran out of file. + * -4 = Problems Reading in section 1 + * -5 = Problems Reading in section 2 or 3 + * -6 = Problems Reading in section 3 + * -7 = Problems Reading in section 4 + * -8 = Problems Parsing section 4. + * -9 = Problems Reading in section 5 + * -10 = Problems Reading in section 6 + * -11 = Problems Reading in section 7 + * -12 = Problems inventory'ing a GRIB1 record + * -13 = Problems inventory'ing a TDLP record + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 11/2002 AAT: Revised. + * 12/2002 (TK,AC,TB,&MS): Code Review. + * 3/2003 AAT: Corrected some satelite type mistakes. + * 3/2003 AAT: Implemented multiple grid inventories in the same GRIB2 + * message. + * 4/2003 AAT: Started adding GRIB1 support + * 6/2003 Matthew T. Kallio (matt@wunderground.com): + * "wmo" dimension increased to WMO_HEADER_LEN + 1 (for '\0' char) + * 7/2003 AAT: Added numMsg so we can quickly find the reference time for + * a file by inventorying just the first message. + * 8/2003 AAT: Adjusted use of GRIB_LIMIT to only affect the first message + * after we know we have a GRIB file, we don't want "trailing" bytes + * to break the program. + * 8/2003 AAT: switched fileLen to only be computed for an error message. + * 8/2003 AAT: curTot no longer serves a purpse. + * 5/2004 AAT: Added a check for section number 2..8 for the repeated + * section (otherwise error) + * 10/2004 AAT: Added ability to inventory TDLP records. + * + * NOTES + ***************************************************************************** + */ +int GRIB2Inventory (DataSource &fp, inventoryType **Inv, uInt4 *LenInv, + int numMsg, int *MsgNum) +{ + //FileDataSource fp (filename); /* The opened GRIB2 file. */ + sInt4 offset = 0; /* Where we are in the file. */ + sInt4 msgNum; /* Which GRIB2 message we are on. */ + uInt4 gribLen; /* Length of the current GRIB message. */ + uInt4 secLen; /* Length of current section. */ + sChar sectNum; /* Which section we are reading. */ + char *buff; /* Holds the info between records. */ + uInt4 buffLen; /* Length of info between records. */ + sInt4 sect0[SECT0LEN_WORD]; /* Holds the current Section 0. */ + char *buffer = NULL; /* Holds a given section. */ + uInt4 bufferLen = 0; /* Size of buffer. */ + inventoryType *inv; /* Local ptr to Inv to reduce ptr confusion. */ + inventoryType *lastInv; /* Used to point to last inventory record when + * there are multiple grids in the same message. */ + wordType word; /* Used to parse the prodType out of Sect 0. */ + int ans; /* The return error code of ReadSect0. */ + char *msg; /* Used to pop messages off the error Stack. */ + int version; /* Which version of GRIB is in this message. */ + uChar prodType; /* Which GRIB2 type of product, 0 is meteo, 1 is + * hydro, 2 is land, 3 is space, 10 is oceanographic. + */ + int grib_limit; /* How many bytes to look for before the first "GRIB" + * in the file. If not found, is not a GRIB file. */ + int c; /* Determine if end of the file without fileLen. */ + sInt4 fileLen; /* Length of the GRIB2 file. */ + unsigned short int center, subcenter; /* Who produced it. */ + // char *ptr; /* used to find the file extension. */ + + grib_limit = GRIB_LIMIT; + /* + if (filename != NULL) { + //if ((fp = fopen (filename, "rb")) == NULL) { + // errSprintf ("ERROR: Problems opening %s for read.", filename); + // return -1; + //} + //fp = FileDataSource(filename); + ptr = strrchr (filename, '.'); + if (ptr != NULL) { + if (strcmp (ptr, ".tar") == 0) { + grib_limit = 5000; + } + } + } else { + //fp = stdin; // TODO!! + } + */ + msgNum = *MsgNum; + + buff = NULL; + buffLen = 0; + while ((c = fp.DataSourceFgetc()) != EOF) { + fp.DataSourceUngetc(c); + // ungetc (c, fp); + /* msgNum++ done first so any error messages range from 1..n, instead + * of 0.. n-1. Note msgNum should end up as n not (n-1) */ + msgNum++; +/* Used when testing inventory of large TDLPack files. */ +/* +#ifdef DEBUG + myAssert (msgNum < 32500L); + if (msgNum % 10 == 0) { + printf ("%ld :: %f\n", msgNum, clock () / (double) CLOCKS_PER_SEC); + } +#endif +*/ + /* Make it so the second, third, etc messages have no limit to finding + * the "GRIB" keyword. */ + if (msgNum > 1) { + grib_limit = -1; + } + /* Read in the wmo header and sect0. */ + if (ReadSECT0 (fp, &buff, &buffLen, grib_limit, sect0, &gribLen, + &version) < 0) { + if (msgNum == 1) { + /* Handle case where we couldn't find 'GRIB' in the message. */ + preErrSprintf ("Inside GRIB2Inventory, Message # %d\n", msgNum); + free (buffer); + free (buff); + //fclose (fp); + return -2; + } else { + /* Handle case where there are trailing bytes. */ + msg = errSprintf (NULL); + printf ("Warning: Inside GRIB2Inventory, Message # %d\n", + msgNum); + printf ("%s", msg); + free (msg); + /* find out how big the file is. */ + fp.DataSourceFseek (0L, SEEK_END); + fileLen = fp.DataSourceFtell(); + /* fseek (fp, 0L, SEEK_SET); */ + printf ("There were %d trailing bytes in the file.\n", + fileLen - offset); + free (buffer); + free (buff); + //fclose (fp); + return msgNum; + } + } + + /* Make room for this GRIB message in the inventory list. */ + *LenInv = *LenInv + 1; + *Inv = (inventoryType *) realloc ((void *) *Inv, + *LenInv * sizeof (inventoryType)); + inv = *Inv + (*LenInv - 1); + + /* Start parsing the message. */ + inv->GribVersion = version; + inv->msgNum = msgNum; + inv->subgNum = 0; + inv->start = offset; + inv->element = NULL; + inv->comment = NULL; + inv->unitName = NULL; + inv->shortFstLevel = NULL; + inv->longFstLevel = NULL; + + if (version == 1) { + if (GRIB1_Inventory (fp, gribLen, inv) != 0) { + preErrSprintf ("Inside GRIB2Inventory \n"); + free (buffer); + free (buff); + //fclose (fp); + return -12; + } + } else if (version == -1) { + if (TDLP_Inventory (fp, gribLen, inv) != 0) { + preErrSprintf ("Inside GRIB2Inventory \n"); + free (buffer); + free (buff); + //fclose (fp); + return -13; + } + } else { + word.li = sect0[1]; + prodType = word.buffer[2]; + + /* Read section 1 into buffer. */ + sectNum = 1; + if (GRIB2SectToBuffer (fp, gribLen, §Num, &secLen, &bufferLen, + &buffer) != 0) { + errSprintf ("ERROR: Problems with section 1\n"); + free (buffer); + free (buff); + //fclose (fp); + return -4; + } + /* Parse the interesting data out of sect 1. */ + InventoryParseTime (buffer + 13 - 5, &(inv->refTime)); + MEMCPY_BIG (¢er, buffer + 6 - 5, sizeof (short int)); + MEMCPY_BIG (&subcenter, buffer + 8 - 5, sizeof (short int)); + + sectNum = 2; + do { + /* Look at sections 2 to 7 */ + if ((ans = GRIB2Inventory2to7 (sectNum, fp, gribLen, &bufferLen, + &buffer, inv, prodType, center, + subcenter)) != 0) { + //fclose (fp); + free (buffer); + free (buff); + return ans; + } + /* Try to read section 8. If it is "7777" = 926365495 regardless + * of endian'ness then we have a simple message, otherwise it is + * complex, and we need to read more. */ + if (FREAD_BIG (&secLen, sizeof (sInt4), 1, fp) != 1) { + errSprintf ("ERROR: Ran out of file looking for Sect 8.\n"); + free (buffer); + free (buff); + // fclose (fp); + return -4; + } + if (secLen == 926365495L) { + sectNum = 8; + } else { + if (fp.DataSourceFread (§Num, sizeof (char), 1) != 1) { + errSprintf ("ERROR: Ran out of file looking for " + "subMessage.\n"); + free (buffer); + free (buff); + //fclose (fp); + return -4; + } + if ((sectNum < 2) || (sectNum > 7)) { + errSprintf ("ERROR (GRIB2Inventory): Couldn't find the end" + " of message\n"); + errSprintf ("and it doesn't appear to repeat sections.\n"); + errSprintf ("so it is probably an ASCII / binary bug\n"); + free (buffer); + free (buff); + //fclose (fp); + return -4; + } + fp.DataSourceFseek (-5, SEEK_CUR); + /* Make room for the next part of this GRIB message in the + * inventory list. This is for when we have sub-grids. */ + *LenInv = *LenInv + 1; + *Inv = (inventoryType *) realloc ((void *) *Inv, + *LenInv * + sizeof (inventoryType)); + inv = *Inv + (*LenInv - 1); + lastInv = *Inv + (*LenInv - 2); + + inv->GribVersion = version; + inv->msgNum = msgNum; + inv->subgNum = lastInv->subgNum + 1; + inv->start = offset; + inv->element = NULL; + inv->comment = NULL; + inv->unitName = NULL; + inv->shortFstLevel = NULL; + inv->longFstLevel = NULL; + + word.li = sect0[1]; + prodType = word.buffer[2]; + inv->refTime = lastInv->refTime; + } + } while (sectNum != 8); + } + + /* added to inventory either first msgNum messages, or all messages */ + if (numMsg == msgNum) { + break; + } + /* Continue on to the next GRIB2 message. */ + if (version == -1) { + /* TDLPack uses 4 bytes for FORTRAN record size, then another 8 + * bytes for the size of the record (so FORTRAN can see it), then + * the data rounded up to an 8 byte boundary, then a trailing 4 + * bytes for a final FORTRAN record size. However it only stores + * in_ the gribLen the non-rounded amount, so we need to take care + * of the rounding, and the trailing 4 bytes here. */ + offset += buffLen + ((sInt4) ceil (gribLen / 8.0)) * 8 + 4; + } else { + offset += buffLen + gribLen; + } + fp.DataSourceFseek (offset, SEEK_SET); + } + free (buffer); + free (buff); + //fclose (fp); + *MsgNum = msgNum; + return msgNum; +} + +int GRIB2RefTime (char *filename, double *refTime) +{ + FileDataSource fp (filename); /* The opened GRIB2 file. */ + sInt4 offset = 0; /* Where we are in the file. */ + sInt4 msgNum; /* Which GRIB2 message we are on. */ + uInt4 gribLen; /* Length of the current GRIB message. */ + uInt4 secLen; /* Length of current section. */ + sChar sectNum; /* Which section we are reading. */ + char *buff; /* Holds the info between records. */ + uInt4 buffLen; /* Length of info between records. */ + sInt4 sect0[SECT0LEN_WORD]; /* Holds the current Section 0. */ + char *buffer = NULL; /* Holds a given section. */ + uInt4 bufferLen = 0; /* Size of buffer. */ + /* wordType word; */ /* Used to parse the prodType out of Sect 0. */ + int ans; /* The return error code of ReadSect0. */ + char *msg; /* Used to pop messages off the error Stack. */ + int version; /* Which version of GRIB is in this message. */ + /* uChar prodType; */ /* Which GRIB2 type of product, 0 is meteo, 1 is + * hydro, 2 is land, 3 is space, 10 is oceanographic. + */ + int grib_limit; /* How many bytes to look for before the first "GRIB" + * in the file. If not found, is not a GRIB file. */ + int c; /* Determine if end of the file without fileLen. */ + sInt4 fileLen; /* Length of the GRIB2 file. */ + char *ptr; /* used to find the file extension. */ + double refTime1; + + grib_limit = GRIB_LIMIT; + if (filename != NULL) { + //if ((fp = fopen (filename, "rb")) == NULL) { + // errSprintf ("ERROR: Problems opening %s for read.", filename); + // return -1; + //} + //fp = DataSource(filename); + ptr = strrchr (filename, '.'); + if (ptr != NULL) { + if (strcmp (ptr, ".tar") == 0) { + grib_limit = 5000; + } + } + } else { + // fp = stdin; // TODO!! + } + msgNum = 0; + + buff = NULL; + buffLen = 0; + while ((c = fp.DataSourceFgetc()) != EOF) { + fp.DataSourceUngetc(c); + /* msgNum++ done first so any error messages range from 1..n, instead + * of 0.. n-1. Note msgNum should end up as n not (n-1) */ + msgNum++; + /* Make it so the second, third, etc messages have no limit to finding + * the "GRIB" keyword. */ + if (msgNum > 1) { + grib_limit = -1; + } + /* Read in the wmo header and sect0. */ + if ((ans = ReadSECT0 (fp, &buff, &buffLen, grib_limit, sect0, &gribLen, + &version)) < 0) { + if (msgNum == 1) { + /* Handle case where we couldn't find 'GRIB' in the message. */ + preErrSprintf ("Inside GRIB2RefTime, Message # %d\n", msgNum); + free (buffer); + free (buff); + //fclose (fp); + return -2; + } else { + /* Handle case where there are trailing bytes. */ + msg = errSprintf (NULL); + printf ("Warning: Inside GRIB2RefTime, Message # %d\n", msgNum); + printf ("%s", msg); + free (msg); + /* find out how big the file is. */ + fp.DataSourceFseek (0L, SEEK_END); + fileLen = fp.DataSourceFtell(); + /* fseek (fp, 0L, SEEK_SET); */ + printf ("There were %d trailing bytes in the file.\n", + fileLen - offset); + free (buffer); + free (buff); + //fclose (fp); + return msgNum; + } + } + + if (version == 1) { + if (GRIB1_RefTime (fp, gribLen, &(refTime1)) != 0) { + preErrSprintf ("Inside GRIB1_RefTime\n"); + free (buffer); + free (buff); + //fclose (fp); + return -12; + } + } else if (version == -1) { + if (TDLP_RefTime (fp, gribLen, &(refTime1)) != 0) { + preErrSprintf ("Inside TDLP_RefTime\n"); + free (buffer); + free (buff); + //fclose (fp); + return -13; + } + } else { + /* word.li = sect0[1]; */ + /* prodType = word.buffer[2]; */ + + /* Read section 1 into buffer. */ + sectNum = 1; + if (GRIB2SectToBuffer (fp, gribLen, §Num, &secLen, &bufferLen, + &buffer) != 0) { + errSprintf ("ERROR: Problems with section 1\n"); + free (buffer); + //fclose (fp); + return -4; + } + /* Parse the interesting data out of sect 1. */ + InventoryParseTime (buffer + 13 - 5, &(refTime1)); + } + if (msgNum == 1) { + *refTime = refTime1; + } else { + if (*refTime > refTime1) { + *refTime = refTime1; + } + } + + /* Continue on to the next GRIB2 message. */ + offset += gribLen + buffLen; + fp.DataSourceFseek (offset, SEEK_SET); + } + free (buffer); + free (buff); + //fclose (fp); + return 0; +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/inventory.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/inventory.h new file mode 100644 index 000000000..35652a3a1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/inventory.h @@ -0,0 +1,64 @@ +/***************************************************************************** + * inventory.h + * + * DESCRIPTION + * This file contains the code needed to do a quick inventory of the GRIB2 + * file. The intent is to enable one to figure out which message in a GRIB + * file one is after without needing to call the FORTRAN library. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifndef INVENTORY_H +#define INVENTORY_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include "type.h" + +#include "datasource.h" + + +typedef struct { + sChar GribVersion; /* 1 if GRIB1, 2 if GRIB2, -1 if it is TDLP */ + sInt4 start; /* Where this message starts in file. */ + unsigned short int msgNum; /* Which "GRIB2" message we are working on. */ + unsigned short int subgNum; /* 0 for the first grid in the GRIB2 message + * NOT file, 1 for the second, etc. */ +/* char *wmo; */ /* The ASCII descriptor string that is before the + * "GRIB" part of the message. */ + double refTime; /* Reference time in seconds UTC */ + double validTime; /* The ending time, or valid time, in seconds + * UTC. This is specified in template 4.8, 4.9, + * for the others it is refTime + foreSec. */ + char *element; /* Character look up of variable type. */ + char *comment; /* A more descriptive look up of variable type. */ + char *unitName; /* The unit of this element. */ + double foreSec; /* Forecast element in seconds. */ + char *shortFstLevel; /* Short description of the level of this data + (above ground) (500 mb), etc */ + char *longFstLevel; /* Long description of the level of this data + (above ground) (500 mb), etc */ +} inventoryType; + +void GRIB2InventoryFree (inventoryType *inv); + +void GRIB2InventoryPrint (inventoryType *Inv, uInt4 LenInv); + +/* Possible error messages left in errSprintf() */ +int GRIB2Inventory (DataSource &fp, inventoryType ** Inv, uInt4 *LenInv, + int numMsg, int *MsgNum); + +int GRIB2RefTime (char *filename, double *refTime); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* INVENTORY_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/makefile.vc b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/makefile.vc new file mode 100644 index 000000000..b96a23a45 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/makefile.vc @@ -0,0 +1,13 @@ +OBJ = clock.obj degrib1.obj degrib2.obj inventory.obj metaname.obj myerror.obj tdlpack.obj filedatasource.obj memorydatasource.obj grib1tab.obj myutil.obj metaparse.obj weather.obj metaprint.obj engribapi.obj grib2api.obj myassert.obj scan.obj memendian.obj fileendian.obj + +EXTRAFLAGS = -I ../g2clib-1.0.4 $(SOFTWARNFLAGS) + +GDAL_ROOT = ..\..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\..\..\o + +clean: + -del *.obj diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/memendian.c b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/memendian.c new file mode 100644 index 000000000..1fe0ba89e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/memendian.c @@ -0,0 +1,503 @@ +/***************************************************************************** + * memendian.c + * + * DESCRIPTION + * This file contains all the utility functions that the Driver uses to + * solve endian'ness related issues. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * 12/2002 Rici Yu, Fangyu Chi, Mark Armstrong, & Tim Boyer + * (RY,FC,MA,&TB): Code Review 2. + * + * NOTES + ***************************************************************************** + */ +#include +#include +#include "memendian.h" + +/***************************************************************************** + * memswp() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * To swap memory in the Data array based on the knownledge that there are + * "num_elem" elements, each of size "elem_size". + * + * ARGUMENTS + * Data = A pointer to the data to be swapped. (Input/Output) + * elem_size = The size of an individual element. (Input) + * num_elem = The number of elements to swap. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (RY,FC,MA,&TB): Code Review. + * + * NOTES + * 1) A similar routine was provided with the GRIB2 library. It was called: + * "unpk_swap". Since it had the restriction that it only dealt with long + * ints, I felt that I needed more flexibility. In addition this procedure + * may be more efficient. I did an operation count for swapping an array + * that consisted of 1 4 byte int. + * "unpk_swap" = 46 operations, "memswp" = 33. + * 2) Could try this with exclusive or? + ***************************************************************************** + */ +void memswp (void *Data, const size_t elem_size, const size_t num_elem) +{ + size_t j; /* Element count */ + char *data; /* Allows us to treat Data as an array of char. */ + char temp; /* A temporary holder of a byte when swapping. */ + char *ptr, *ptr2; /* Pointers to the two bytes to swap. */ + + if (elem_size == 1) { + return; + } + data = (char *) Data; + for (j = 0; j < elem_size * num_elem; j += elem_size) { + ptr = data + j; + ptr2 = ptr + elem_size - 1; + while (ptr2 > ptr) { + temp = *ptr; + *(ptr++) = *ptr2; + *(ptr2--) = temp; + } + } +} + +/***************************************************************************** + * revmemcpy() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * To copy memory similar to memcpy, but in a reverse manner. In order to + * have the same arguments as memcpy, this can not handle arrays... For + * arrays use revmemcpyRay(). Returns the same thing that memcpy does. + * + * ARGUMENTS + * Dst = The destination for the data. (Output) + * Src = The source of the data. (Input) + * len = The length of Src in bytes. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void * + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (RY,FC,MA,&TB): Code Review. + * + * NOTES + * 1) This came about as I was trying to improve on the use of memcpy. I + * figured that revmemcpy would be faster than memcpy followed by memswp. + * 2) Assumes that Dst is allocated to a size of Src. + * 3) Problems with MEMCPY if len != sizeof (dst)... Is it left or right + * justified? + ***************************************************************************** + */ +void *revmemcpy (void *Dst, void *Src, const size_t len) +{ + size_t j; /* Byte count */ + char *src = (char *) Src; /* Allows us to treat Src as an array of char. */ + char *dst = (char *) Dst; /* Allows us to treat Dst as an array of char. */ + + src = src + len - 1; + for (j = 0; j < len; ++j) { + *(dst++) = *(src--); + } + return Dst; +} + +/***************************************************************************** + * revmemcpyRay() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * To copy memory similar to memcpy, but in a reverse manner. This handles + * the case when we need to reverse memcpy an array of data. + * + * ARGUMENTS + * Dst = The destination for the data. (Output) + * Src = The source of the data. (Input) + * elem_size = The size of a single element. (Input) + * num_elem = The number of elements in Src. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void * + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (RY,FC,MA,&TB): Code Review. + * + * NOTES + * 1) Assumes that Dst is allocated to a size of Src. + ***************************************************************************** + */ +void *revmemcpyRay (void *Dst, void *Src, const size_t elem_size, + const size_t num_elem) +{ + size_t i; /* Element count. */ + size_t j; /* Byte count. */ + char *src = (char *) Src; /* Allows us to treat Src as an array of char. */ + char *dst = (char *) Dst; /* Allows us to treat Dst as an array of char. */ + + if (elem_size == 1) { + return memcpy (Dst, Src, num_elem); + } + src -= (elem_size + 1); + for (i = 0; i < num_elem; ++i) { + src += 2 * elem_size; + for (j = 0; j < elem_size; ++j) { + *(dst++) = *(src--); + } + } + return Dst; +} + +/***************************************************************************** + * memBitRead() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To read bits from an uChar buffer array of memory. Assumes BufLoc is + * valid before first call. Typically this means do a "bufLoc = 8;" before + * the first call. + * + * ARGUMENTS + * Dst = Where to put the results. (Output) + * dstLen = Length in bytes of Dst. (Input) + * Src = The data to read the bits from. (Input) + * numBits = How many bits to read. (Input) + * BufLoc = Which bit to start reading from in Src. + * Starts at 8 goes to 1. (Input/Output) + * numUsed = How many bytes from Src were used while reading (Output) + * + * FILES/DATABASES: None + * + * RETURNS: + * Returns 1 on error, 0 if ok. + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created + * 5/2004 AAT: Bug in call to MEMCPY_BIG when numBytes != dstLen. + * On big endian machines we need to right justify the number. + * + * NOTES + * 1) Assumes binary bit stream is "big endian". Resulting in no byte + * boundaries ie 00100110101101 => 001001 | 10101101 + ***************************************************************************** + */ +char memBitRead (void *Dst, size_t dstLen, void *Src, size_t numBits, + uChar * bufLoc, size_t * numUsed) +{ + uChar *src = (uChar *) Src; /* Allows us to treat Src as an array of + * char. */ + uChar *dst = (uChar *) Dst; /* Allows us to treat Dst as an array of + * char. */ + size_t numBytes; /* How many bytes are needed in dst. */ + uChar dstLoc; /* Where we are writing to in dst. */ + uChar *ptr; /* Current byte we are writing to in dst. */ + static uChar BitMask[] = { + 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff + }; + + if (numBits == 0) { + memset (Dst, 0, dstLen); + (*numUsed) = 0; + return 0; + } + numBytes = ((numBits - 1) / 8) + 1; + if (dstLen < numBytes) { + return 1; + } + memset (Dst, 0, dstLen); + dstLoc = ((numBits - 1) % 8) + 1; + if ((*bufLoc == 8) && (dstLoc == 8)) { +#ifdef LITTLE_ENDIAN + MEMCPY_BIG (Dst, Src, numBytes); +#else + /* If numBytes != dstLen, then we need to right justify the ans */ + MEMCPY_BIG (dst + (dstLen - numBytes), Src, numBytes); +#endif + (*numUsed) = numBytes; + return 0; + } +#ifdef LITTLE_ENDIAN + ptr = dst + (numBytes - 1); +#else + ptr = dst + (dstLen - numBytes); +#endif + + *numUsed = 0; + /* Deal with most significant byte in dst. */ + if (*bufLoc >= dstLoc) { +#ifdef LITTLE_ENDIAN + (*ptr--) |= ((*src & BitMask[*bufLoc]) >> (*bufLoc - dstLoc)); +#else + (*ptr++) |= ((*src & BitMask[*bufLoc]) >> (*bufLoc - dstLoc)); +#endif + (*bufLoc) -= dstLoc; + } else { + if (*bufLoc != 0) { + *ptr |= ((*src & BitMask[*bufLoc]) << (dstLoc - *bufLoc)); + /* Assert: dstLoc should now be dstLoc - InitBufLoc */ + dstLoc = dstLoc - *bufLoc; + /* Assert: bufLoc should now be 0 */ + } + src++; + (*numUsed)++; + /* Assert: bufLoc should now be 8 */ + /* Assert: We want to >> by bufLoc - dstLoc = 8 - dstLoc */ +#ifdef LITTLE_ENDIAN + *(ptr--) |= (*src >> (8 - dstLoc)); +#else + *(ptr++) |= (*src >> (8 - dstLoc)); +#endif + (*bufLoc) = 8 - dstLoc; + } + /* Assert: dstLoc should now be 8, but we don't use again in procedure. */ + + /* We have now reached the state which we want after each iteration of + * the loop. That is initDstLoc == 8, initBufLoc = bufLoc < dstLoc. */ +#ifdef LITTLE_ENDIAN + while (ptr >= dst) { +#else + while (ptr < dst + dstLen) { +#endif + if (*bufLoc != 0) { + *ptr |= ((*src & BitMask[*bufLoc]) << (8 - *bufLoc)); + /* Assert: dstLoc should now be initDstLoc (8) - initBufLoc */ + /* Assert: bufLoc should now be 0 */ + } + src++; + (*numUsed)++; + /* Assert: bufLoc should now be 8 */ + /* Assert: dstLoc should now be initDstLoc (8) - initBufLoc */ + /* Assert: We want to >> by bufLoc - dstLoc = (8 - (8 - initbufLoc)). */ +#ifdef LITTLE_ENDIAN + *(ptr--) |= (*src >> *bufLoc); +#else + *(ptr++) |= (*src >> *bufLoc); +#endif + } + if (*bufLoc == 0) { + (*numUsed)++; + *bufLoc = 8; + } + return 0; +} + +/***************************************************************************** + * memBitWrite() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To write bits from a data structure to an array of uChar. + * Assumes that the part of Dst we don't write to have been correctly + * initialized. Typically this means do a "memset (dst, 0, sizeof (dst));" + * before the first call. + * Also assumes BufLoc is valid before first call. Typically this means do + * a "bufLoc = 8;" before the first call. + * + * ARGUMENTS + * Src = The data to read from. (Input) + * srcLen = Length in bytes of Src. (Input) + * Dst = The char buffer to write the bits to. (Output) + * numBits = How many bits to write. (Input) + * BufLoc = Which bit in Dst to start writing to. + * Starts at 8 goes to 1. (Input/Output) + * numUsed = How many bytes were written to Dst. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: + * Returns 1 on error, 0 if ok. + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created + * + * NOTES + * 1) Assumes binary bit stream should be "big endian". Resulting in no byte + * boundaries ie 00100110101101 => 001001 | 1010110 + * 2) Assumes that Dst is already zero'ed out. + ***************************************************************************** + */ +char memBitWrite (void *Src, size_t srcLen, void *Dst, size_t numBits, + uChar * bufLoc, size_t * numUsed) +{ + uChar *src = (uChar *) Src; /* Allows us to treat Src as an array of + * char. */ + uChar *dst = (uChar *) Dst; /* Allows us to treat Dst as an array of + * char. */ + size_t numBytes; /* How many bytes are needed from src. */ + uChar srcLoc; /* Which bit we are reading from in src. */ + uChar *ptr; /* Current byte we are reading from in src. */ + static uChar BitMask[] = { + 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff + }; + + if (numBits == 0) { + return 0; + } + numBytes = ((numBits - 1) / 8) + 1; + if (srcLen < numBytes) { + return 1; + } + srcLoc = ((numBits - 1) % 8) + 1; + + if ((*bufLoc == 8) && (srcLoc == 8)) { + MEMCPY_BIG (Dst, Src, numBytes); + (*numUsed) = numBytes; + return 0; + } +#ifdef LITTLE_ENDIAN + ptr = src + (numBytes - 1); +#else + ptr = src + (srcLen - numBytes); +#endif + + *numUsed = 0; + /* Deal with most significant byte in src. */ + if (*bufLoc >= srcLoc) { +#ifdef LITTLE_ENDIAN + (*dst) |= ((*(ptr--) & BitMask[srcLoc]) << (*bufLoc - srcLoc)); +#else + (*dst) |= ((*(ptr++) & BitMask[srcLoc]) << (*bufLoc - srcLoc)); +#endif + (*bufLoc) -= srcLoc; + } else { + if (*bufLoc != 0) { + (*dst) |= ((*ptr & BitMask[srcLoc]) >> (srcLoc - *bufLoc)); + /* Assert: srcLoc should now be srcLoc - InitBufLoc */ + srcLoc = srcLoc - *bufLoc; + /* Assert: bufLoc should now be 0 */ + } + dst++; + (*dst) = 0; + (*numUsed)++; + /* Assert: bufLoc should now be 8 */ + /* Assert: We want to >> by bufLoc - srcLoc = 8 - srcLoc */ +#ifdef LITTLE_ENDIAN + (*dst) |= (*(ptr--) << (8 - srcLoc)); +#else + (*dst) |= (*(ptr++) << (8 - srcLoc)); +#endif + (*bufLoc) = 8 - srcLoc; + } + /* Assert: dstLoc should now be 8, but we don't use again in procedure. */ + + /* We have now reached the state which we want after each iteration of + * the loop. That is initSrcLoc == 8, initBufLoc = bufLoc < srcLoc. */ +#ifdef LITTLE_ENDIAN + while (ptr >= src) { +#else + while (ptr < src + srcLen) { +#endif + if (*bufLoc == 0) { + dst++; + (*numUsed)++; +#ifdef LITTLE_ENDIAN + (*dst) = *(ptr--); +#else + (*dst) = *(ptr++); +#endif + } else { + (*dst) |= ((*ptr) >> (8 - *bufLoc)); + dst++; + (*numUsed)++; + (*dst) = 0; +#ifdef LITTLE_ENDIAN + (*dst) |= (*(ptr--) << *bufLoc); +#else + (*dst) |= (*(ptr++) << *bufLoc); +#endif + } + } + if (*bufLoc == 0) { + dst++; + (*numUsed)++; + (*bufLoc) = 8; + (*dst) = 0; + } + return 0; +} + +/***************************************************************************** + * main() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To test the memBitRead, and memBitWrite routines, to make sure that they + * function correctly on some sample data.. + * + * ARGUMENTS + * argc = The number of arguments on the command line. (Input) + * argv = The arguments on the command line. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifdef DEBUG_ENDIAN +int main (int argc, char **argv) +{ + uChar buff[5], buff2[5]; + uChar bufLoc = 8; + uChar *ptr, *ptr2; + int numUsed; + + buff[0] = 0x8f; + buff[1] = 0x8f; + buff[2] = 0x8f; + buff[3] = 0x8f; + buff[4] = 0x8f; + + bufLoc = 7; + memBitRead (buff2, sizeof (buff2), buff, 39, &bufLoc, &numUsed); + printf ("%d %d %d %d %d ", buff2[0], buff2[1], buff2[2], buff2[3], + buff2[4]); + printf ("-------should be----- "); + printf ("143 143 143 143 15\n"); + + memset (buff, 0, sizeof (buff)); + bufLoc = 8; + ptr = buff; + ptr2 = buff2; + memBitWrite (ptr2, sizeof (buff2), ptr, 9, &bufLoc, &numUsed); + ptr += numUsed; + ptr2++; + memBitWrite (ptr2, sizeof (buff2), ptr, 7, &bufLoc, &numUsed); + ptr += numUsed; + ptr2++; + memBitWrite (ptr2, sizeof (buff2), ptr, 7, &bufLoc, &numUsed); + ptr += numUsed; + ptr2++; + memBitWrite (ptr2, sizeof (buff2), ptr, 9, &bufLoc, &numUsed); + ptr += numUsed; + ptr2++; + memBitWrite (ptr2, sizeof (buff2), ptr, 8, &bufLoc, &numUsed); + ptr += numUsed; + printf ("%d %d %d %d %d ", buff[0], buff[1], buff[2], buff[3], buff[4]); + printf ("-------should be----- "); + printf ("199 143 31 143 15\n"); + return 0; +} +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/memendian.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/memendian.h new file mode 100644 index 000000000..8c3b153a0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/memendian.h @@ -0,0 +1,61 @@ +/***************************************************************************** + * memendian.h + * + * DESCRIPTION + * This file contains all the utility functions that the Driver uses to + * solve endian'ness related issues. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifndef MEMENDIAN_H +#define MEMENDIAN_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "type.h" +#include "cpl_port.h" + +/* + * MadeOnIntel ==> LittleEndian + * NotMadeOnIntel ==> BigEndian + */ +#undef BIG_ENDIAN +#undef LITTLE_ENDIAN + +#ifdef WORDS_BIGENDIAN + #define BIG_ENDIAN +#else + #define LITTLE_ENDIAN +#endif + +/* The following #defines are used to make the code easier to read. */ +#ifdef BIG_ENDIAN + #define MEMCPY_BIG memcpy + #define MEMCPY_LIT revmemcpy +#else + #define MEMCPY_BIG revmemcpy + #define MEMCPY_LIT memcpy +#endif + +void memswp (void *Data, const size_t elem_size, const size_t num_elem); + +void *revmemcpy (void *Dst, void *Src, const size_t len); +void *revmemcpyRay (void *Dst, void *Src, const size_t elem_size, + const size_t num_elem); + +char memBitRead (void *Dst, size_t dstLen, void *Src, size_t numBits, + uChar * bufLoc, size_t *numUsed); +char memBitWrite (void *Src, size_t srcLen, void *Dst, size_t numBits, + uChar * bufLoc, size_t *numUsed); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MEMENDIAN_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/memorydatasource.cpp b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/memorydatasource.cpp new file mode 100644 index 000000000..7b21bcaea --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/memorydatasource.cpp @@ -0,0 +1,91 @@ +#include "memorydatasource.h" +#include +#include + +MemoryDataSource::MemoryDataSource(unsigned char * block, long length) +: seekPos(0) +, blockLength(length) +, eof(false) +, memoryBlock(block) +{ +} + +MemoryDataSource::~MemoryDataSource() +{ +} + +size_t MemoryDataSource::DataSourceFread(void* lpBuf, size_t size, size_t count) +{ + if (seekPos + size * count > (size_t) blockLength) + { + count = (blockLength - seekPos) / size; + eof = true; + } + else + eof = false; // feof also "resets" after a good read + + memcpy(lpBuf, memoryBlock + seekPos, size * count); + seekPos += size * count; + + return count; + +} + +int MemoryDataSource::DataSourceFgetc() +{ + int returnVal = EOF; + if (seekPos >= blockLength) + eof = true; + else + { + unsigned char c = *((unsigned char*)(memoryBlock + seekPos)); + ++seekPos; + returnVal = (int)c; + eof = false; + } + return returnVal; +} + +int MemoryDataSource::DataSourceUngetc(int c) +{ + eof = false; + int returnVal = c; + if ((c != EOF) && (seekPos > 0)) + { + --seekPos; + *((unsigned char*)(memoryBlock + seekPos)) = c; + } + else + returnVal = EOF; + return returnVal; +} + +int MemoryDataSource::DataSourceFseek(long offset, int origin) +{ + switch (origin) + { + case SEEK_CUR : + seekPos += offset; + break; + case SEEK_END : + seekPos = blockLength + offset; // offset MUST be negative + break; + case SEEK_SET : + seekPos = offset; + break; + } + + eof = false; + + return 0; +} + +int MemoryDataSource::DataSourceFeof() +{ + return eof; +} + +long MemoryDataSource::DataSourceFtell() +{ + return seekPos; +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/memorydatasource.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/memorydatasource.h new file mode 100644 index 000000000..324d2eeff --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/memorydatasource.h @@ -0,0 +1,25 @@ +#ifndef MEMORYDATASOURCE_H +#define MEMORYDATASOURCE_H + +#include "datasource.h" +#include + +class MemoryDataSource : public DataSource +{ +public: + MemoryDataSource(unsigned char * block, long length); + virtual ~MemoryDataSource(); + virtual size_t DataSourceFread(void* lpBuf, size_t size, size_t count); + virtual int DataSourceFgetc(); + virtual int DataSourceUngetc(int c); + virtual int DataSourceFseek(long offset, int origin); + virtual int DataSourceFeof(); + virtual long DataSourceFtell(); +private: + long seekPos; + long blockLength; + bool eof; + unsigned char * memoryBlock; +}; + +#endif /* MEMORYDATASOURCE_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/meta.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/meta.h new file mode 100644 index 000000000..d9a3f6e4f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/meta.h @@ -0,0 +1,532 @@ +/***************************************************************************** + * meta.h + * + * DESCRIPTION + * This file contains the code necessary to handle the meta data that + * comes out of the GRIB2 decoder. This includes parsing the integer arrays + * to create a meta structure, and providing routines to write the meta + * structure to disk. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifndef META_H +#define META_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +/* Include type.h for uChar and sChar */ +#include "type.h" +#ifdef MEMWATCH + #include "memwatch.h" +#endif + +#ifndef GRIB2BIT_ENUM +#define GRIB2BIT_ENUM +/* See rule (8) bit 1 is most significant, bit 8 least significant. */ +enum {GRIB2BIT_1=128, GRIB2BIT_2=64, GRIB2BIT_3=32, GRIB2BIT_4=16, + GRIB2BIT_5=8, GRIB2BIT_6=4, GRIB2BIT_7=2, GRIB2BIT_8=1}; +#endif + +#ifndef UNITCONVERT_ENUM +#define UNITCONVERT_ENUM +typedef enum { UC_NONE, UC_K2F, UC_InchWater, UC_M2Feet, UC_M2Inch, + UC_MS2Knots, UC_LOG10 +} unit_convert; +#endif + +/* NDFD_UNDEF is for matching variables that don't have a defined + * NDFD enumerated type... User sets elements in genElemDesc + * themselves. + * NDFD_MATCHALL says give me all elements back. + * NDFD_MATCHALL is maximum. + */ +#ifndef NDFD_ENUM +#define NDFD_ENUM +/* +enum { NDFD_MAX, NDFD_MIN, NDFD_POP, NDFD_TEMP, NDFD_WD, NDFD_WS, + NDFD_TD, NDFD_SKY, NDFD_QPF, NDFD_SNOW, NDFD_WX, NDFD_WH, + NDFD_AT, NDFD_RH, NDFD_WG, NDFD_INC34, NDFD_INC50, NDFD_INC64, + NDFD_CUM34, NDFD_CUM50, NDFD_CUM64, NDFD_UNDEF, NDFD_MATCHALL +}; +*/ +enum { NDFD_MAX, NDFD_MIN, NDFD_POP, NDFD_TEMP, NDFD_WD, NDFD_WS, + NDFD_TD, NDFD_SKY, NDFD_QPF, NDFD_SNOW, NDFD_WX, NDFD_WH, + NDFD_AT, NDFD_RH, NDFD_UNDEF, NDFD_MATCHALL +}; +#endif + +/* For GRIB1 GDS Types. */ +enum { GB1S2_LATLON = 0, GB1S2_MERCATOR = 1, GB1S2_LAMBERT = 3, + GB1S2_GAUSSIAN_LATLON = 4, GB1S2_POLAR = 5, GB1S2_ROTATED_LATLON = 10 +}; + +/* For TDLP GDS Types. */ +enum { TDLP_MERCATOR = 7, TDLP_LAMBERT = 3, TDLP_POLAR = 5}; + +#define GRIB2MISSING_u1 (uChar) (0xff) +#define GRIB2MISSING_s1 (sChar) -1 * (0x7f) +#define GRIB2MISSING_u2 (uShort2) (0xffff) +#define GRIB2MISSING_s2 (sShort2) -1 * (0x7fff) +#define GRIB2MISSING_u4 (uInt4) (0xffffffff) +/* following is -1 * 2&31 because of the way signed integers are stored in + GRIB2. */ +#define GRIB2MISSING_s4 (sInt4) -2147483647 + +#define NUM_UGLY_WORD 5 +#define NUM_UGLY_ATTRIB 5 + +typedef struct { + uChar numValid; /* (0..5) How many valid "types" */ + uChar wx[NUM_UGLY_WORD]; /* (see WxCode) */ + uChar cover[NUM_UGLY_WORD]; /* (see WxCover) */ + uChar intens[NUM_UGLY_WORD]; /* (see WxIntens) */ + uChar vis[NUM_UGLY_WORD]; /* 255 no vis, otherwise in units of 1/32 SM + * so 6SM -> 192. P6SM -> 224 */ + uChar f_or[NUM_UGLY_WORD]; /* true if OR, or MX was a hazzard. */ + uChar f_priority[NUM_UGLY_WORD]; /* 0 if normal, + * 1 if 'include unconditional' + * 2 if 'high priority' */ + uChar attrib[NUM_UGLY_WORD][NUM_UGLY_ATTRIB]; /* (see WxAttrib) */ + uChar minVis; /* vis[] should be constant, but just in case + * we use the minimum value. */ + uChar f_valid; /* 1 valid may be not-used, 2 valid and used, + * 0 invalid may be not-used, + * temporarily 3 (invalid and used). */ + sInt4 validIndex; /* Which index this is, counting only used + * valid indexes. If it is not used it is -1 */ + char *english[NUM_UGLY_WORD]; /* The english translation of ugly string. */ + uChar wx_inten[NUM_UGLY_WORD]; /* A code to represent the wx and + intensity for an "ugly word". */ + sInt4 HazCode[NUM_UGLY_WORD]; /* A code to represent all the attributes. */ + int SimpleCode; /* Simple weather code for this ugly string. */ + char *errors; /* if STORE_ERRORS, then it contains any error + * messages found while parsing this string. */ +} UglyStringType; + +typedef struct { + char **data; /* Array of text strings (aka "ugly strings) */ + uInt4 dataLen; /* number of text strings in data. */ + int maxLen; /* Max Length of all of the "ugly strings" + * It includes 1 for the \0 character. */ + UglyStringType *ugly; /* The parsed Ugly string. */ + int maxEng[NUM_UGLY_WORD]; /* Max length of english phrases for all ugly + * word number X. */ +} sect2_WxType; + +typedef struct { + double *data; /* Array of actual values (cast from int or + float to double). */ + uInt4 dataLen; /* Number of elements in data. */ +} sect2_UnknownType; + +enum { GS2_NONE, GS2_WXTYPE, GS2_UNKNOWN }; + +typedef struct { +/* void *ptr; */ /* Pointer to section 2 data. */ + sect2_WxType wx; /* wx data. */ + sect2_UnknownType unknown; /* unknown type of section 2 data. */ + uChar ptrType; /* Which structure is valid. + GS2_WXTYPE => wx + GS2_UNKNOWN => unknown */ +} sect2_type; + +enum { CAT_TEMP, CAT_MOIST, CAT_MOMENT, CAT_MASS, CAT_SW_RAD, + CAT_LW_RAD, CAT_CLOUD, CAT_THERMO_INDEX, CAT_KINEMATIC_INDEX, + CAT_TEMP_PROB, CAT_MOISTURE_PROB, CAT_MOMENT_PROB, CAT_MASS_PROB, + CAT_AEROSOL, CAT_TRACE, CAT_RADAR, CAT_RADAR_IMAGERY, CAT_ELECTRO, + CAT_NUCLEAR, CAT_PHYS_ATMOS }; +enum { TEMP_TEMP, TEMP_VIRT, TEMP_POTENTIAL, TEMP_PSEUDO_POTENTIAL, + TEMP_MAXT, TEMP_MINT, TEMP_DEW_TEMP, TEMP_DEW_DEPRESS, + TEMP_LAPSE, TEMP_ANOMALY, TEMP_LATENT_FLUX, TEMP_SENSIBLE_FLUX, + TEMP_HEAT, TEMP_WINDCHILL, TEMP_MIN_DEW_DEPRESS, TEMP_VIRT_POTENTIAL }; +enum { CLOUD_ICE, CLOUD_COVER, CLOUD_CONVECT_COVER, CLOUD_LOW, + CLOUD_MEDIUM, CLOUD_HIGH, CLOUD_WATER, CLOUD_AMNT, CLOUD_TYPE, + CLOUD_THUDER_MAX, CLOUD_THUNDER_COVER, CLOUD_BASE, CLOUD_TOP, + CLOUD_CEIL }; +enum { MOMENT_WINDDIR, MOMENT_WINDSPD, MOMENT_U_WIND, MOMENT_V_WIND, + MOMENT_STREAM, MOMENT_VEL_POTENT, MOMENT_MONT_STREAM, + MOMENT_SIGMA_VERTVEL, MOMENT_VERTVEL_PRESS, MOMENT_VERTVEL_GEOMETRIC, + MOMENT_ABS_VORT, MOMENT_ABS_DIV, MOMENT_REL_VORT, MOMENT_REL_DIV, + MOMENT_POT_VORT, MOMENT_VERT_U_SHEAR, MOMENT_VERT_V_SHEAR, + MOMENT_U_FLUX, MOMENT_V_FLUX, MOMENT_MIX_ENERGY, + MOMENT_BOUNDARY_DISSPATE, MOMENT_MAX_WINDSPD, MOMENT_GUSTSPD, + MOMENT_U_GUSTSPD, MOMENT_V_GUSTSPD }; +enum { MOIST_SPEC_HUMID, MOIST_REL_HUMID, MOIST_HUMID_MIX, MOIST_PRECIP_WATER, + MOIST_VAPOR_PRESS, MOIST_SAT_DEFICIT, MOIST_EVAP, MOIST_PRECIP_RATE, + MOIST_PRECIP_TOT, MOIST_LARGE_SCALE, MOIST_CONVECT_PRECIP, + MOIST_SNOWAMT, MOIST_SNOWRATE_WATER, MOIST_SNOWAMT_WATER, + MOIST_CONVECT_SNOW, MOIST_LARGE_SCALE_SNOW, MOIST_SNOWMELT, + MOIST_SNOWAGE, MOIST_ABS_HUMID, MOIST_PRECIP_TYPE, + MOIST_INTEGRATE_WATER, MOIST_CONDENSATE, MOIST_CLOUDMIX_RATIO, + MOIST_ICEMIX_RATIO, MOIST_RAINMIX_RATIO, MOIST_SNOWMIX_RATIO, + MOIST_HORIZ_CONVERGE, MOIST_MAXREL_HUMID, MOIST_MAXABS_HUMID, + MOIST_TOT_SNOW, MOIST_PRECIP_WATER_CAT, MOIST_HAIL, MOIST_GRAUPEL }; +enum { OCEAN_CAT_WAVES, OCEAN_CAT_CURRENT, OCEAN_CAT_ICE, + OCEAN_CAT_SURF, OCEAN_CAT_SUBSURF }; +enum { OCEAN_WAVE_SPECTRA1, OCEAN_WAVE_SPECTRA2, OCEAN_WAVE_SPECTRA3, + OCEAN_WAVE_SIG_HT_WV_SWELL, OCEAN_WAVE_DIR_WV, + OCEAN_WAVE_SIG_HT_WV, OCEAN_WAVE_PD_WV, OCEAN_WAVE_DIR_SWELL, + OCEAN_WAVE_SIG_HT_SWELL, OCEAN_WAVE_PD_SWELL, OCEAN_WAVE_PRIM_DIR, + OCEAN_WAVE_PRIM_PD, OCEAN_WAVE_SEC_DIR, OCEAN_WAVE_SEC_PD }; + +typedef struct { + uChar processID; /* Statistical process method used. */ + uChar incrType; /* Type of time increment between intervals */ + uChar timeRangeUnit; /* Time range unit. */ + sInt4 lenTime; /* Range or length of time interval. */ + uChar incrUnit; /* Unit of time increment. */ + sInt4 timeIncr; /* Time increment between intervals. */ +} sect4_IntervalType; + +typedef struct { /* Used to store data where unpacked value = + value / 10**factor */ + sInt4 value; /* scale value in equation */ + sChar factor; /* scale factor in equation. */ +} ScaleType; + +typedef struct { /* See Template 4.30. */ + unsigned short int series; /* Satellite series of band. */ + unsigned short int numbers; /* Satellite numbers of band. */ + uChar instType; /* Instrument type of band */ + ScaleType centWaveNum; /* scaled value for central wave number of + band. */ +} sect4_BandType; + +enum { GS4_ANALYSIS, GS4_ENSEMBLE, GS4_DERIVED, GS4_PROBABIL_PNT = 5, + GS4_STATISTIC = 8, GS4_PROBABIL_TIME = 9, GS4_PERCENTILE = 10, + GS4_ENSEMBLE_STAT = 11, GS4_DERIVED_INTERVAL = 12, GS4_RADAR = 20, + GS4_SATELLITE = 30 +}; + +typedef struct { + uShort2 templat; /* The section 4 template number. */ + uChar cat; /* General category of Meteo Product. */ + uChar subcat; /* Specific subcategory of Meteo Product. */ + uChar genProcess; /* What type of generate process (Analysis, + Forecast, Probability Forecast, etc). */ + uChar bgGenID; /* Background generating process id. */ + uChar genID; /* Analysis/Forecast generating process id. */ + uChar f_validCutOff; /* 1 if cutOff is valid, 0 if cutoff missing. */ + sInt4 cutOff; /* Data Cutoff in seconds after reference time + (see sect 1 for reference time). */ + double foreSec; /* forecast "projection time" in seconds. */ + uChar fstSurfType; /* Type of the first fixed surface. */ + double fstSurfValue; /* Value of first fixed surface. */ + sChar fstSurfScale; /* Scale factor used when storing value. */ + uChar sndSurfType; /* Type of the second fixed surface. */ + double sndSurfValue; /* Value of second fixed surface. */ + sChar sndSurfScale; /* Scale factor used when storing value. */ + double validTime; /* The ending time, or valid time, in seconds + UTC. This is specified in template 4.8, 4.9, + for the others it is refTime + foreSec. */ + /* The following are somewhat template specific. */ + uChar typeEnsemble; /* The type of Ensemble forecast + Template 4.1, (Code Table 4.5) */ + uChar perturbNum; /* Perturbation number, Template 4.1 */ + uChar numberFcsts; /* Number of forecasts in Ensemble. + Template, 4.1, 4.2 */ + uChar derivedFcst; /* Derived Forecast (from Ensemble). + Template 4.2 */ + uChar numInterval; /* Number of time intervals. Template 4.8,4.9 */ + sInt4 numMissing; /* Number of missing values. Template 4.8,4.9 */ + sect4_IntervalType *Interval; /* Stores the array of time intervals. + Template 4.8,4.9 */ + uChar numBands; /* Number of Spectral Bands. Template 4.30 */ + sect4_BandType *bands; /* Holds info about each Band Template 4.30 */ + uChar percentile; /* Which percentile this forecast is for */ + uChar foreProbNum; /* Forecast Probability number (Template 4.9) */ + uChar numForeProbs; /* Total # of forecast probabilities (4.9) */ + uChar probType; /* Type of probability range. (Template 4.9) + 0 below lower limit. 1 above upper limit. + 2 between lower (inclusive) and + upper limit (exclusive) + 3 above lower limit. 4 below upper limit. */ + ScaleType lowerLimit; /* Lower Limit probability field. Template 4.9*/ + ScaleType upperLimit; /* Upper Limit probability field. Template 4.9*/ +} sect4_type; + +/* This is the structure needed for the PDS (Product Definition Section) + * for GRIB2. I hope to combine stuff, but there is a lot more to GRIB2 + * than GRIB1 + */ +typedef struct { + /* Section 0 Data */ + uChar prodType; /* 0 is meteo product, 1 is hydro, 2 is land + 3 is space, 10 is oceanographic. */ + /* Section 1 Data */ + uChar mstrVersion; /* Master table version. */ + uChar lclVersion; /* Local table version. */ + uChar sigTime; /* Significance of reference time */ + double refTime; /* Reference time in seconds UTC */ + uChar operStatus; /* What is operational status of data. */ + uChar dataType; /* What type of data. (analysis, forecast, + analysis & forecast, ...) */ + + /* Rest of PDS. */ + uChar f_sect2; /* 1 if sect2 exists, 0 otherwise. */ + sInt4 sect2NumGroups; /* total number of groups in section 2 data. */ + sect2_type sect2; /* sect 2 info */ + sect4_type sect4; /* sect 4 info */ +} pdsG2Type; + +/* see: http://www.emc.ncep.noaa.gov/gmb/ens/info/ens_grib.html */ +typedef struct { + uChar BitFlag; /* Octet 29 */ + uChar Application; /* Octet 41 (should be 1) */ + uChar Type; /* Octet 42 */ + uChar Number; /* Octet 43 */ + uChar ProdID; /* Octet 44 */ + uChar Smooth; /* Octet 45 */ +} pdsG1EnsType; + +/* see: http://www.emc.ncep.noaa.gov/gmb/ens/info/ens_grib.html */ +typedef struct { + uChar Cat; /* swapped octet 46 for octet 9. + * This holds 191,192,193 (octet 9) + * pdsMeta->cat holds category (octet 46) */ + uChar Type; /* Octet 47 */ + double lower; /* 48-51 */ + double upper; /* 52-55 */ +} pdsG1ProbType; + +/* see: http://www.emc.ncep.noaa.gov/gmb/ens/info/ens_grib.html */ +typedef struct { + uChar ensSize; /* Octet 61 */ + uChar clusterSize; /* Octet 62 */ + uChar Num; /* Octet 63 */ + uChar Method; /* Octet 64 */ + double NorLat; /* Octet 65-67 / 1000 (deg) */ + double SouLat; /* Octet 68-70 / 1000 (deg) */ + double EasLon; /* Octet 71-73 / 1000 (deg) */ + double WesLon; /* Octet 74-76 / 1000 (deg) */ + char Member[11]; /* Octet 77-86 */ +} pdsG1ClusterType; + +/* This is the structure needed for the PDS (Product Definition Section) + * for GRIB1 + */ +typedef struct { + uChar mstrVersion; /* Parameter Table Version #. */ +/* uChar center, subcenter; */ /* Who produced it. */ + uChar genProcess; /* Generating Process ID. ?Sect 4? */ + uChar cat; /* General category of Meteo Product. */ + uChar gridID; /* The Grid Defin ID number (GRIB1 specific) */ + uChar levelType; /* Type of level. ?sect 4 fstSurf? */ + int levelVal; /* Value of level. */ + double refTime; /* Reference time in seconds UTC */ + double P1; /* Period of time1 ?sect 4 valid time? */ + double P2; /* Period of time2 */ + double validTime; /* Valid time in seconds UTC */ + uChar timeRange; /* Time Range Indicator. */ + /* Specific for averages or accumulations. (determined by timeRange) */ + int Average; /* number included in average. */ + uChar numberMissing; /* number missing from averages. */ + uChar f_hasEns; /* 1 = has ens type, 0 = doesn't */ + pdsG1EnsType ens; /* Ensemble information */ + uChar f_hasProb; /* 1 = has prob type, 0 = doesn't */ + pdsG1ProbType prob; /* Probability information */ + uChar f_hasCluster; /* 1 = has cluster type, 0 = doesn't */ + pdsG1ClusterType cluster; /* Cluster information */ +} pdsG1Type; + +/* This is the structure needed for the PDS (Product Definition Section) + * for TDLP + */ +typedef struct { + double refTime; /* Reference time in seconds UTC */ + /* CCCFFF :: is the class and subclass (variable type). */ + /* B :: is the binary indicator. */ + /* DD :: is the Data source (NGM/ETA/AVN/ etc). */ + sInt4 ID1; /* First word of ID (CCCFFFBDD) */ + int CCC, FFF, B, DD; + /* V :: is the type of surface processing */ + /* LLL :: is the lower level. */ + /* UUU :: is the upper level. */ + sInt4 ID2; /* Second word of ID (VLLLLUUUU) */ + int V, LLLL, UUUU; + /* T :: indicates non-linear transformation of the data. */ + /* RR :: is the run time offset in hours. Example RR=24 with model field + * means the data from the model run 24 hours ago. */ + /* O :: time operator... 2 fields are involved in time computation. + * 1-4 indicate both times were for run of NDATE-RR, but projections of + * var1 = ttt, var2 = ttt + HH. */ + /* 5-8 indicate var1 has run of NDATE-RR, while var2 has run of NDATE, + * with projections of var1 = ttt + HH, var2 = ttt. */ + sInt4 ID3; /* Third word of ID (TRROHHttt) */ + int T, RR, Oper, HH, ttt; + /* WXXXXYY :: Threshold (W=0 positive, =1 negative) (xxxx is fraction) + * (YY > 50 is negative) (YY is the exponent applied to xxxx). */ + /* I is type of interpolation. */ + /* S is smoothing indicator. */ + /* G is reserved. */ + sInt4 ID4; /* Forth word of ID (WXXXXYY ISG) */ + double thresh; + int I, S, G; + sInt4 project; /* Projection in seconds */ + uChar procNum; /* model or process number (see DD in ID1) */ + uChar seqNum; /* sequence number within the model number */ + char Descriptor[33]; /* Plain language Descriptor. */ +} pdsTDLPType; + +/* This is the structure and enumerated types needed for the GDS (Grid + * Definition Section. In GRIB2 the GDS was in section 3, in GRIB1 it was in + * section 2. + */ +enum { GS3_LATLON = 0, GS3_MERCATOR = 10, GS3_POLAR = 20, + GS3_LAMBERT = 30, GS3_GAUSSIAN_LATLON = 40, GS3_ORTHOGRAPHIC = 90, + GS3_ROTATED_LATLON = 100, GS3_EQUATOR_EQUIDIST = 110, GS3_AZIMUTH_RANGE = 120}; + +/* Note: It appears that compilers break up a struct based on the largest + element type. In this case a double. So to avoid wasted memory, we need + things to stop at 8 byte borders. sInt4, double, sInt4 is larger than + sInt4, sInt4, double. */ +typedef struct { + uInt4 numPts; /* Number of data points. Typically Nx * Ny, but + for some exotic grids they don't have Nx,Ny */ + uChar projType; /* Projection type / template type. Valid choices + 0(lat/lon), 10(mercator), 20(Polar Stereo), + 30(Lambert Conformal), + in future maybe 90, 110, 120. */ +/* Shape of Earth */ + uChar f_sphere; /* 1 is a sphere, => majEarth == minEarth */ + double majEarth, minEarth; /* semi major and minor axis of earth in km. */ +/* Projection info. */ + uInt4 Nx, Ny; /* Dimensions of grid. */ + double lat1, lon1; /* lat,lon position of first grid point. */ + /* resFlag: moved to save memory. */ + double orientLon; /* Where up is North. (0 for lat/lon grids) */ + double Dx, Dy; /* Mesh delta x,y in degrees or meters. */ + double meshLat; /* Where the mesh size is defined + (0 for lat/lon grids.) */ + uChar resFlag; /* Table 7 GRIB1 : Section 2 */ + uChar center; /* For lambert and polar stereographic, answers: + (south/north?) and (bi-polar?) */ + uChar scan; /* describes how the grid was traversed when it + was stored. (ie top/down left/right etc.) + Internally we use 0100. (start lower left) */ + double lat2, lon2; /* lat,lon position of last grid point. + (0 if unused) */ +/* Specific to Lambert Conformal grids. */ + double scaleLat1, scaleLat2; /* The tangent latitude. If different: + then the latitude where the scale should be + equal, which allows one to compute the correct + tangent latitude. (0 for lat/lon and mercator, + 90 for north polar stereographic). */ + double southLat, southLon; /* Not needed. 0 except lambert. + (and rotated lat/lon) */ + /* Following is for stretched Lat/Lon grids. */ + double poleLat, poleLon; /* Pole of stretching. */ + double stretchFactor; /* Factor of stretching. */ + int f_typeLatLon; /* 0 regular, 1 stretch, 2 stretch / rotate. */ + double angleRotate; /* Rotation angle. */ +} gdsType; + +typedef struct { + uChar projType; /* Projection type / template type. Valid choices + 0(lat/lon), 10(mercator), 20(Polar Stereo), + 30(Lambert Conformal), + in future maybe 90, 110, 120. */ + double majEarth, minEarth; /* semi major and minor axis of earth in km. */ +} gdsType2; + +enum { GS5_SIMPLE = 0, GS5_CMPLX = 2, GS5_CMPLXSEC = 3, GS5_JPEG2000 = 40, + GS5_PNG = 41, GS5_SPECTRAL = 50, GS5_HARMONIC = 51, + GS5_JPEG2000_ORG = 40000, GS5_PNG_ORG = 40010 +}; +typedef struct { + sInt4 packType; /* What kind of packing was used. */ + float refVal; /* The refrence value for the grid, also the + * minimum value? */ + short int ESF; /* Power of 2 scaling factor. */ + short int DSF; /* Decimal Scale Factor */ + uChar fieldType; /* 0 data was float, 1 if int. */ + uChar f_maxmin; /* boolean, if min/max are valid*/ + double min, max; /* Min / Max values in data. */ + uChar f_miss; /* Missing value management + 0 none, 1 primary, 2 primary & secondary. */ + double missPri, missSec; /* primary and secondary missing values. */ + sInt4 numMiss; /* Number of missing values detected, + both primary and secondary. */ +} gridAttribType; + +typedef struct { + sChar GribVersion; /* 1 if GRIB1, 2 if GRIB2, -1 if TDLP */ + pdsTDLPType pdsTdlp; /* Product Definition section for TDLP. */ + pdsG1Type pds1; /* Product Definition section for GRIB1. */ + pdsG2Type pds2; /* Product Definition section for GRIB2. */ + gdsType gds; /* Grid Definition section (sect3 for GRIB2) */ + gridAttribType gridAttrib; /* Attributes of the Grid (min/max values etc) */ + char *element; /* A short char look up of variable type. */ + char *comment; /* A more descriptive look up of variable type. */ + char *unitName; /* Unit if not the one specified in the GRIB2 + * document, otherwise NULL. */ + int convert; /* Enum type of unit conversions (meta.h), + Conversion method for this variable's unit. */ + char *shortFstLevel; /* Short description of the level of this data + (above ground) (500 mb), etc */ + char *longFstLevel; /* Long description of the level of this data + (above ground) (500 mb), etc */ + uShort2 center, subcenter; /* Who produced it. */ + char refTime[20]; /* When forecast was issued. */ + char validTime[20]; /* When forecast is valid. */ + sInt4 deltTime; /* validTime - refTime in seconds. */ + +/* int *size_wx; */ /* (idat[0] + 2) * sizeof (int) */ +/* char **release_datetime; *//* pds2.refTime + pds2.cutOffHour */ +} grib_MetaData; + +typedef enum { + Prt_D, Prt_DS, Prt_DSS, Prt_S, + Prt_F, Prt_FS, Prt_E, Prt_ES, Prt_G, Prt_GS, Prt_SS, Prt_NULL +} Prt_TYPE; + +char *Print(const char *label, const char *varName, Prt_TYPE fmt, ...); + +void MetaInit (grib_MetaData *meta); + +void MetaSect2Free (grib_MetaData * meta); + +void MetaFree (grib_MetaData *meta); + +int ParseTime (double * AnsTime, int year, uChar mon, uChar day, uChar hour, + uChar min, uChar sec); + +int ParseSect4Time2secV1 (sInt4 time, int unit, double *ans); + +int ParseSect4Time2sec (sInt4 time, int unit, double *ans); + +/* Possible error messages left in errSprintf() */ +int MetaParse (grib_MetaData * meta, sInt4 *is0, sInt4 ns0, + sInt4 *is1, sInt4 ns1, sInt4 *is2, sInt4 ns2, + float *rdat, sInt4 nrdat, sInt4 *idat, sInt4 nidat, + sInt4 *is3, sInt4 ns3, sInt4 *is4, sInt4 ns4, + sInt4 *is5, sInt4 ns5, sInt4 grib_len, + float xmissp, float xmisss, int simpVer); + +void ParseGrid (gridAttribType * attrib, double **Grib_Data, + uInt4 *grib_DataLen, uInt4 Nx, uInt4 Ny, int scan, + sInt4 *iain, sInt4 ibitmap, sInt4 *ib, double unitM, + double unitB, uChar f_wxType, sect2_WxType * WxType, + uChar f_subGrid, int startX, int startY, int stopX, int stopY); + +void FreqPrint (char **ans, double *Data, sInt4 DataLen, sInt4 Nx, + sInt4 Ny, sChar decimal, char *comment); + +/* Possible error messages left in errSprintf() */ +int MetaPrintGDS (gdsType * gds, int version, char **ans); + +/* Possible error messages left in errSprintf() */ +int MetaPrint (grib_MetaData *meta, char **ans, sChar decimal, sChar f_unit); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* META_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/metaname.cpp b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/metaname.cpp new file mode 100644 index 000000000..3586383aa --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/metaname.cpp @@ -0,0 +1,2519 @@ +/***************************************************************************** + * metaname.c + * + * DESCRIPTION + * This file contains the code necessary to parse the GRIB2 product + * definition information into human readable text. In addition to the + * tables in the GRIB2 specs, it also attempts to handle local table + * definitions that NCEP and NDFD have developed. + * + * HISTORY + * 1/2004 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#include +#include +#include "meta.h" +#include "metaname.h" +#include "myerror.h" +#include "myassert.h" +#include "myutil.h" + +#include "cpl_port.h" + +const char *centerLookup (unsigned short int center) +{ + /* see: + * http://www.wmo.ch/web/www/WMOCodes/Operational/CommonTables/BUFRCommon-2005feb.pdf + * http://www.wmo.int/web/www/WMOCodes/Operational/CommonTables/BUFRCommon-2005nov.pdf + * also see: + * http://www.nco.ncep.noaa.gov/pmb/docs/on388/table0.html + * I typed this in on 11/2/2006 */ +/* *INDENT-OFF* */ + static struct { + unsigned short int num; + const char *name; + } Center[] = { + {0, "WMO Secretariat"}, + {1, "Melbourne"}, + {2, "Melbourne"}, + {3, ") Melbourne"}, + {4, "Moscow"}, + {5, "Moscow"}, + {6, ") Moscow"}, + {7, "US-NCEP"}, + {8, "US-NWSTG"}, + {9, "US-Other"}, + {10, "Cairo"}, + {11, ") Cairo"}, + {12, "Dakar"}, + {13, ") Dakar"}, + {14, "Nairobi"}, + {15, ") Nairobi"}, + {16, "Casablanca"}, + {17, "Tunis"}, + {18, "Tunis Casablanca"}, + {19, ") Tunis Casablanca"}, + {20, "Las Palmas"}, + {21, "Algiers"}, + {22, "ACMAD"}, + {23, "Mozambique"}, + {24, "Pretoria"}, + {25, "La R\xE9" "union"}, + {26, "Khabarovsk"}, + {27, ") Khabarovsk"}, + {28, "New Delhi"}, + {29, ") New Delhi"}, + {30, "Novosibirsk"}, + {31, ") Novosibirsk"}, + {32, "Tashkent"}, + {33, "Jeddah"}, + {34, "Tokyo"}, + {35, ") Tokyo"}, + {36, "Bangkok"}, + {37, "Ulan Bator"}, + {38, "Beijing"}, + {39, ") Beijing"}, + {40, "Seoul"}, + {41, "Buenos Aires"}, + {42, ") Buenos Aires"}, + {43, "Brasilia"}, + {44, ") Brasilia"}, + {45, "Santiago"}, + {46, "Brazilian Space Agency"}, + {47, "Colombia"}, + {48, "Ecuador"}, + {49, "Peru"}, + {50, "Venezuela"}, + {51, "Miami"}, + {52, "Miami-NHC"}, + {53, "Montreal"}, + {54, ") Montreal"}, + {55, "San Francisco"}, + {56, "ARINC Centre"}, + {57, "US-Air Force Weather"}, + {58, "US-Fleet Meteorology and Oceanography"}, + {59, "US-FSL"}, + {60, "US-NCAR"}, + {61, "US-Service ARGOS"}, + {62, "US-Naval Oceanographic Office"}, +/* {63, "Reserved for another centre in Region IV"},*/ + {64, "Honolulu"}, + {65, "Darwin"}, + {66, ") Darwin"}, + {67, "Melbourne"}, +/* {68, "Reserved"},*/ + {69, "Wellington"}, + {70, ") Wellington"}, + {71, "Nadi"}, + {72, "Singapore"}, + {73, "Malaysia"}, + {74, "UK-Met-Exeter"}, + {75, ") UK-Met-Exeter"}, + {76, "Moscow"}, +/* {77, "Reserved"},*/ + {78, "Offenbach"}, + {79, ") Offenbach"}, + {80, "Rome"}, + {81, ") Rome"}, + {82, "Norrk\xF6" "ping"}, + {83, ") Norrk\xF6" "ping"}, + {84, "Toulouse"}, + {85, "Toulouse"}, + {86, "Helsinki"}, + {87, "Belgrade"}, + {88, "Oslo"}, + {89, "Prague"}, + {90, "Episkopi"}, + {91, "Ankara"}, + {92, "Frankfurt/Main"}, + {93, "London"}, + {94, "Copenhagen"}, + {95, "Rota"}, + {96, "Athens"}, + {97, "ESA-European Space Agency"}, + {98, "ECMWF"}, + {99, "DeBilt"}, + {100, "Brazzaville"}, + {101, "Abidjan"}, + {102, "Libyan Arab Jamahiriya"}, + {103, "Madagascar"}, + {104, "Mauritius"}, + {105, "Niger"}, + {106, "Seychelles"}, + {107, "Uganda"}, + {108, "Tanzania"}, + {109, "Zimbabwe"}, + {110, "Hong-Kong, China"}, + {111, "Afghanistan"}, + {112, "Bahrain"}, + {113, "Bangladesh"}, + {114, "Bhutan"}, + {115, "Cambodia"}, + {116, "Democratic People's Republic of Korea"}, + {117, "Islamic Republic of Iran"}, + {118, "Iraq"}, + {119, "Kazakhstan"}, + {120, "Kuwait"}, + {121, "Kyrgyz Republic"}, + {122, "Lao People's Democratic Republic"}, + {123, "Macao, China"}, + {124, "Maldives"}, + {125, "Myanmar"}, + {126, "Nepal"}, + {127, "Oman"}, + {128, "Pakistan"}, + {129, "Qatar"}, + {130, "Republic of Yemen"}, + {131, "Sri Lanka"}, + {132, "Tajikistan"}, + {133, "Turkmenistan"}, + {134, "United Arab Emirates"}, + {135, "Uzbekistan"}, + {136, "Socialist Republic of Viet Nam"}, +/* {137, "Reserved"},*/ +/* {138, "Reserved"},*/ +/* {139, "Reserved"},*/ + {140, "Bolivia"}, + {141, "Guyana"}, + {142, "Paraguay"}, + {143, "Suriname"}, + {144, "Uruguay"}, + {145, "French Guyana"}, + {146, "Brazilian Navy Hydrographic Centre"}, +/* {147, "Reserved"},*/ +/* {148, "Reserved"},*/ +/* {149, "Reserved"},*/ + {150, "Antigua and Barbuda"}, + {151, "Bahamas"}, + {152, "Barbados"}, + {153, "Belize"}, + {154, "British Caribbean Territories"}, + {155, "San Jose"}, + {156, "Cuba"}, + {157, "Dominica"}, + {158, "Dominican Republic"}, + {159, "El Salvador"}, + {160, "US-NESDIS"}, + {161, "US-OAR"}, + {162, "Guatemala"}, + {163, "Haiti"}, + {164, "Honduras"}, + {165, "Jamaica"}, + {166, "Mexico"}, + {167, "Netherlands Antilles and Aruba"}, + {168, "Nicaragua"}, + {169, "Panama"}, + {170, "Saint Lucia NMC"}, + {171, "Trinidad and Tobago"}, + {172, "French Departments"}, +/* {173, "Reserved"},*/ +/* {174, "Reserved"},*/ +/* {175, "Reserved"},*/ +/* {176, "Reserved"},*/ +/* {177, "Reserved"},*/ +/* {178, "Reserved"},*/ +/* {179, "Reserved"},*/ +/* {180, "Reserved"},*/ +/* {181, "Reserved"},*/ +/* {182, "Reserved"},*/ +/* {183, "Reserved"},*/ +/* {184, "Reserved"},*/ +/* {185, "Reserved"},*/ +/* {186, "Reserved"},*/ +/* {187, "Reserved"},*/ +/* {188, "Reserved"},*/ +/* {189, "Reserved"},*/ + {190, "Cook Islands"}, + {191, "French Polynesia"}, + {192, "Tonga"}, + {193, "Vanuatu"}, + {194, "Brunei"}, + {195, "Indonesia"}, + {196, "Kiribati"}, + {197, "Federated States of Micronesia"}, + {198, "New Caledonia"}, + {199, "Niue"}, + {200, "Papua New Guinea"}, + {201, "Philippines"}, + {202, "Samoa"}, + {203, "Solomon Islands"}, +/* {204, "Reserved"},*/ +/* {205, "Reserved"},*/ +/* {206, "Reserved"},*/ +/* {207, "Reserved"},*/ +/* {208, "Reserved"},*/ +/* {209, "Reserved"},*/ + {210, "Frascati (ESA/ESRIN)"}, + {211, "Lanion"}, + {212, "Lisboa"}, + {213, "Reykiavik"}, + {214, "Madrid"}, + {215, "Z\xFC" "rich"}, + {216, "Service ARGOS Toulouse"}, + {217, "Bratislava"}, + {218, "Budapest"}, + {219, "Ljubljana"}, + {220, "Warsaw"}, + {221, "Zagreb"}, + {222, "Albania"}, + {223, "Armenia"}, + {224, "Austria"}, + {225, "Azerbaijan"}, + {226, "Belarus"}, + {227, "Belgium"}, + {228, "Bosnia and Herzegovina"}, + {229, "Bulgaria"}, + {230, "Cyprus"}, + {231, "Estonia"}, + {232, "Georgia"}, + {233, "Dublin"}, + {234, "Israel"}, + {235, "Jordan"}, + {236, "Latvia"}, + {237, "Lebanon"}, + {238, "Lithuania"}, + {239, "Luxembourg"}, + {240, "Malta"}, + {241, "Monaco"}, + {242, "Romania"}, + {243, "Syrian Arab Republic"}, + {244, "The former Yugoslav Republic of Macedonia"}, + {245, "Ukraine"}, + {246, "Republic of Moldova"}, +/* {247, "Reserved"},*/ +/* {248, "Reserved"},*/ +/* {249, "Reserved"},*/ +/* {250, "Reserved"},*/ +/* {251, "Reserved"},*/ +/* {252, "Reserved"},*/ +/* {253, "Reserved"},*/ + {254, "EUMETSAT Operation Centre"}, +/* {255, "Reserved"},*/ + {256, "Angola"}, + {257, "Benin"}, + {258, "Botswana"}, + {259, "Burkina Faso"}, + {260, "Burundi"}, + {261, "Cameroon"}, + {262, "Cape Verde"}, + {263, "Central African republic"}, + {264, "Chad"}, + {265, "Comoros"}, + {266, "Democratic Republic of the Congo"}, + {267, "Djibouti"}, + {268, "Eritrea"}, + {269, "Ethiopia"}, + {270, "Gabon"}, + {271, "Gambia"}, + {272, "Ghana"}, + {273, "Guinea"}, + {274, "Guinea Bissau"}, + {275, "Lesotho"}, + {276, "Liberia"}, + {277, "Malawi"}, + {278, "Mali"}, + {279, "Mauritania"}, + {280, "Namibia"}, + {281, "Nigeria"}, + {282, "Rwanda"}, + {283, "Sao Tome and Principe"}, + {284, "Sierra Leone"}, + {285, "Somalia"}, + {286, "Sudan"}, + {287, "Swaziland"}, + {288, "Togo"}, + {289, "Zambia"} + }; +/* *INDENT-ON* */ + int numCenter = sizeof (Center) / sizeof (Center[0]); + int i; + + for (i = 0; i < numCenter; i++) { + if (Center[i].num == center) { + return Center[i].name; + } + } + return NULL; +} + +const char *subCenterLookup(unsigned short int center, + unsigned short int subcenter) +{ +/* *INDENT-OFF* */ +/* see: + * http://www.wmo.ch/web/www/WMOCodes/Operational/CommonTables/ + * BUFRCommon-2005feb.pdf as of 10/12/2005 + * http://www.nco.ncep.noaa.gov/pmb/docs/on388/tablec.html as of 11/9/2005 + * http://www.ecmwf.int/publications/manuals/libraries/gribex/ + * localGRIBUsage.html as of 4/5/2006 + */ + static struct { + unsigned short int center, subcenter; + const char *name; + } SubCenter[] = { + {7, 1, "NCEP Re-Analysis Project"}, {7, 2, "NCEP Ensemble Products"}, + {7, 3, "NCEP Central Operations"}, + {7, 4, "Environmental Modeling Center"}, + {7, 5, "Hydrometeorological Prediction Center"}, + {7, 6, "Ocean Prediction Center"}, {7, 7, "Climate Prediction Center"}, + {7, 8, "Aviation Weather Center"}, {7, 9, "Storm Prediction Center"}, + {7, 10, "Tropical Prediction Center"}, + {7, 11, "Techniques Development Laboratory"}, + {7, 12, "NESDIS Office of Research and Applications"}, + {7, 13, "FAA"}, + {7, 14, "Meteorological Development Laboratory (MDL)"}, + {7, 15, "North American Regional Reanalysis (NARR) Project"}, + {7, 16, "Space Environment Center"}, + {8, 0, "National Digital Forecast Database"}, +/* {8, GRIB2MISSING_2, "National Digital Forecast Database"},*/ + {161, 1, "Great Lakes Environmental Research Laboratory"}, + {161, 2, "Forecast Systems Laboratory"}, + {74, 1, "Shanwick Oceanic Area Control Centre"}, + {74, 2, "Fucino"}, {74, 3, "Gatineau"}, {74, 4, "Maspalomas"}, + {74, 5, "ESA ERS Central Facility"}, {74, 6, "Prince Albert"}, + {74, 7, "West Freugh"}, {74, 13, "Tromso"}, + {74, 21, "Agenzia Spaziale Italiana (Italy)"}, + {74, 22, "Centre National de la Recherche Scientifique (France)"}, + {74, 23, "GeoForschungsZentrum (Germany)"}, + {74, 24, "Geodetic Observatory Pecny (Czech Republic)"}, + {74, 25, "Institut d'Estudis Espacials de Catalunya (Spain)"}, + {74, 26, "Swiss Federal Office of Topography"}, + {74, 27, "Nordic Commission of Geodesy (Norway)"}, + {74, 28, "Nordic Commission of Geodesy (Sweden)"}, + {74, 29, "Institute de Geodesie National (France)"}, + {74, 30, "Bundesamt f\xFC" "r Kartographie und Geod\xE4" "sie (Germany)"}, + {74, 31, "Institute of Engineering Satellite Surveying and Geodesy (U.K.)"}, + {254, 10, "Tromso (Norway)"}, {254, 10, "Maspalomas (Spain)"}, + {254, 30, "Kangerlussuaq (Greenland)"}, {254, 40, "Edmonton (Canada)"}, + {254, 50, "Bedford (Canada)"}, {254, 60, "Gander (Canada)"}, + {254, 70, "Monterey (USA)"}, {254, 80, "Wallops Island (USA)"}, + {254, 90, "Gilmor Creek (USA)"}, {254, 100, "Athens (Greece)"}, + {98, 231, "CNRM, Meteo France Climate Centre (HIRETYCS)"}, + {98, 232, "MPI, Max Planck Institute Climate Centre (HIRETYCS)"}, + {98, 233, "UKMO Climate Centre (HIRETYCS)"}, + {98, 234, "ECMWF (DEMETER)"}, + {98, 235, "INGV-CNR (Bologna, Italy)(DEMETER)"}, + {98, 236, "LODYC (Paris, France)(DEMETER)"}, + {98, 237, "DMI (Copenhagen, Denmark)(DEMETER)"}, + {98, 238, "INM (Madrid, Spain)(DEMETER)"}, + {98, 239, "CERFACS (Toulouse, France)(DEMETER)"}, + {98, 240, "ECMWF (PROVOST)"}, + {98, 241, "Meteo France (PROVOST)"}, + {98, 242, "EDF (PROVOST)"}, + {98, 243, "UKMO (PROVOST)"}, + {98, 244, "Biometeorology group, University of Veterinary Medicine, Vienna (ELDAS)"}, + }; +/* *INDENT-ON* */ + int numSubCenter = sizeof (SubCenter) / sizeof (SubCenter[0]); + int i; + + for (i = 0; i < numSubCenter; i++) { + if ((SubCenter[i].center == center) && + (SubCenter[i].subcenter == subcenter)) { + return SubCenter[i].name; + } + } + return NULL; +} + +const char *processLookup (unsigned short int center, unsigned char process) +{ + /* see: http://www.nco.ncep.noaa.gov/pmb/docs/on388/tablea.html I typed + * this in on 10/12/2005 */ +/* *INDENT-OFF* */ + static struct { + unsigned short int center; + unsigned char process; + const char *name; + } Process[] = { + {7, 2, "Ultra Violet Index Model"}, + {7, 3, "NCEP/ARL Transport and Dispersion Model"}, + {7, 4, "NCEP/ARL Smoke Model"}, + {7, 5, "Satellite Derived Precipitation and temperatures, from IR"}, + {7, 10, "Global Wind-Wave Forecast Model"}, + {7, 19, "Limited-area Fine Mesh (LFM) analysis"}, + {7, 25, "Snow Cover Analysis"}, + {7, 30, "Forecaster generated field"}, + {7, 31, "Value added post processed field"}, + {7, 39, "Nested Grid forecast Model (NGM)"}, + {7, 42, "Global Optimum Interpolation Analysis (GOI) from GFS model"}, + {7, 43, "Global Optimum Interpolation Analysis (GOI) from 'Final' run"}, + {7, 44, "Sea Surface Temperature Analysis"}, + {7, 45, "Coastal Ocean Circulation Model"}, + {7, 46, "HYCOM - Global"}, + {7, 47, "HYCOM - North Pacific basin"}, + {7, 48, "HYCOM - North Atlantic basin"}, + {7, 49, "Ozone Analysis from TIROS Observations"}, + {7, 52, "Ozone Analysis from Nimbus 7 Observations"}, + {7, 53, "LFM-Fourth Order Forecast Model"}, + {7, 64, "Regional Optimum Interpolation Analysis (ROI)"}, + {7, 68, "80 wave triangular, 18-layer Spectral model from GFS model"}, + {7, 69, "80 wave triangular, 18 layer Spectral model from 'Medium Range Forecast' run"}, + {7, 70, "Quasi-Lagrangian Hurricane Model (QLM)"}, + {7, 73, "Fog Forecast model - Ocean Prod. Center"}, + {7, 74, "Gulf of Mexico Wind/Wave"}, + {7, 75, "Gulf of Alaska Wind/Wave"}, + {7, 76, "Bias corrected Medium Range Forecast"}, + {7, 77, "126 wave triangular, 28 layer Spectral model from GFS model"}, + {7, 78, "126 wave triangular, 28 layer Spectral model from 'Medium Range Forecast' run"}, + {7, 79, "Backup from the previous run"}, + {7, 80, "62 wave triangular, 28 layer Spectral model from 'Medium Range Forecast' run"}, + {7, 81, "Spectral Statistical Interpolation (SSI) analysis from GFS model"}, + {7, 82, "Spectral Statistical Interpolation (SSI) analysis from 'Final' run."}, + {7, 84, "MESO ETA Model (currently 12 km)"}, + {7, 86, "RUC Model from FSL (isentropic; scale: 60km at 40N)"}, + {7, 87, "CAC Ensemble Forecasts from Spectral (ENSMB)"}, + {7, 88, "NOAA Wave Watch III (NWW3) Ocean Wave Model"}, + {7, 89, "Non-hydrostatic Meso Model (NMM) Currently 8 km)"}, + {7, 90, "62 wave triangular, 28 layer spectral model extension of the 'Medium Range Forecast' run"}, + {7, 91, "62 wave triangular, 28 layer spectral model extension of the GFS model"}, + {7, 92, "62 wave triangular, 28 layer spectral model run from the 'Medium Range Forecast' final analysis"}, + {7, 93, "62 wave triangular, 28 layer spectral model run from the T62 GDAS analysis of the 'Medium Range Forecast' run"}, + {7, 94, "T170/L42 Global Spectral Model from MRF run"}, + {7, 95, "T126/L42 Global Spectral Model from MRF run"}, + {7, 96, "Global Forecast System Model"}, + {7, 98, "Climate Forecast System Model"}, + {7, 100, "RUC Surface Analysis (scale: 60km at 40N)"}, + {7, 101, "RUC Surface Analysis (scale: 40km at 40N)"}, + {7, 105, "RUC Model from FSL (isentropic; scale: 20km at 40N)"}, + {7, 110, "ETA Model - 15km version"}, + {7, 111, "Eta model, generic resolution"}, + {7, 112, "WRF-NMM (Nondydrostatic Mesoscale Model) model, generic resolution"}, + {7, 113, "Products from NCEP SREF processing"}, + {7, 115, "Downscaled GFS from Eta eXtension"}, + {7, 116, "WRF-EM (Eulerian Mass-core) model, generic resolution "}, + {7, 120, "Ice Concentration Analysis"}, + {7, 121, "Western North Atlantic Regional Wave Model"}, + {7, 122, "Alaska Waters Regional Wave Model"}, + {7, 123, "North Atlantic Hurricane Wave Model"}, + {7, 124, "Eastern North Pacific Regional Wave Model"}, + {7, 125, "North Pacific Hurricane Wave Model"}, + {7, 126, "Sea Ice Forecast Model"}, + {7, 127, "Lake Ice Forecast Model"}, + {7, 128, "Global Ocean Forecast Model"}, + {7, 129, "Global Ocean Data Analysis System (GODAS)"}, + {7, 130, "Merge of fields from the RUC, Eta, and Spectral Model"}, + {7, 131, "Great Lakes Wave Model"}, + {7, 140, "North American Regional Reanalysis (NARR)"}, + {7, 141, "Land Data Assimilation and Forecast System"}, + {7, 150, "NWS River Forecast System (NWSRFS)"}, + {7, 151, "NWS Flash Flood Guidance System (NWSFFGS)"}, + {7, 152, "WSR-88D Stage II Precipitation Analysis"}, + {7, 153, "WSR-88D Stage III Precipitation Analysis"}, + {7, 180, "Quantitative Precipitation Forecast"}, + {7, 181, "River Forecast Center Quantitative Precipitation Forecast mosaic"}, + {7, 182, "River Forecast Center Quantitative Precipitation estimate mosaic"}, + {7, 183, "NDFD product generated by NCEP/HPC"}, + {7, 190, "National Convective Weather Diagnostic"}, + {7, 191, "Current Icing Potential automated product"}, + {7, 192, "Analysis product from NCEP/AWC"}, + {7, 193, "Forecast product from NCEP/AWC"}, + {7, 195, "Climate Data Assimilation System 2 (CDAS2)"}, + {7, 196, "Climate Data Assimilation System 2 (CDAS2)"}, + {7, 197, "Climate Data Assimilation System (CDAS)"}, + {7, 198, "Climate Data Assimilation System (CDAS)"}, + {7, 200, "CPC Manual Forecast Product"}, + {7, 201, "CPC Automated Product"}, + {7, 210, "EPA Air Quality Forecast"}, + {7, 211, "EPA Air Quality Forecast"}, + {7, 220, "NCEP/OPC automated product"} + }; +/* *INDENT-ON* */ + int numProcess = sizeof (Process) / sizeof (Process[0]); + int i; + + for (i = 0; i < numProcess; i++) { + if ((Process[i].center == center) && (Process[i].process == process)) { + return Process[i].name; + } + } + return NULL; +} + +typedef struct { + const char *name, *comment, *unit; + unit_convert convert; +} GRIB2ParmTable; + +typedef struct { + int prodType, cat, subcat; + const char *name, *comment, *unit; + unit_convert convert; +} GRIB2LocalTable; + +typedef struct { + const char *GRIB2name, *NDFDname; +} NDFD_AbrevOverideTable; + +/* *INDENT-OFF* */ +/* Updated based on: + * http://www.wmo.ch/web/www/WMOCodes/Operational/GRIB2/FM92-GRIB2-2005nov.pdf + * 1/3/2006 + */ +/* GRIB2 Code table 4.2 : 0.0 */ +GRIB2ParmTable MeteoTemp[] = { + /* 0 */ {"TMP", "Temperature", "K", UC_K2F}, /* Need NDFD override. T */ + /* 1 */ {"VTMP", "Virtual temperature", "K", UC_K2F}, + /* 2 */ {"POT", "Potential temperature", "K", UC_K2F}, + /* 3 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F}, + /* 4 */ {"TMAX", "Maximum Temperature", "K", UC_K2F}, /* Need NDFD override MaxT */ + /* 5 */ {"TMIN", "Minimum Temperature", "K", UC_K2F}, /* Need NDFD override MinT */ + /* 6 */ {"DPT", "Dew point temperature", "K", UC_K2F}, /* Need NDFD override Td */ + /* 7 */ {"DEPR", "Dew point depression", "K", UC_K2F}, + /* 8 */ {"LAPR", "Lapse rate", "K/m", UC_NONE}, + /* 9 */ {"TMPA", "Temperature anomaly", "K", UC_K2F}, + /* 10 */ {"LHTFL", "Latent heat net flux", "W/(m^2)", UC_NONE}, + /* 11 */ {"SHTFL", "Sensible heat net flux", "W/(m^2)", UC_NONE}, + /* NDFD */ + /* 12 */ {"HeatIndex", "Heat index", "K", UC_K2F}, + /* NDFD */ + /* 13 */ {"WCI", "Wind chill factor", "K", UC_K2F}, + /* 14 */ {"", "Minimum dew point depression", "K", UC_K2F}, + /* 15 */ {"VPTMP", "Virtual potential temperature", "K", UC_K2F}, +/* 16 */ {"SNOHF", "Snow phase change heat flux", "W/m^2", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.1 */ +/* NCEP added "Water" to items 22, 24, 25 */ +GRIB2ParmTable MeteoMoist[] = { + /* 0 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE}, + /* 1 */ {"RH", "Relative Humidity", "%", UC_NONE}, + /* 2 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE}, + /* 3 */ {"PWAT", "Precipitable water", "kg/(m^2)", UC_NONE}, + /* 4 */ {"VAPP", "Vapor Pressure", "Pa", UC_NONE}, + /* 5 */ {"SATD", "Saturation deficit", "Pa", UC_NONE}, + /* 6 */ {"EVP", "Evaporation", "kg/(m^2)", UC_NONE}, + /* 7 */ {"PRATE", "Precipitation rate", "kg/(m^2 s)", UC_NONE}, + /* 8 */ {"APCP", "Total precipitation", "kg/(m^2)", UC_InchWater}, /* Need NDFD override QPF */ + /* 9 */ {"NCPCP", "Large scale precipitation", "kg/(m^2)", UC_NONE}, + /* 10 */ {"ACPCP", "Convective precipitation", "kg/(m^2)", UC_NONE}, + /* 11 */ {"SNOD", "Snow depth", "m", UC_M2Inch}, /* Need NDFD override SnowDepth ? */ + /* 12 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/(m^2 s)", UC_NONE}, + /* 13 */ {"WEASD", "Water equivalent of accumulated snow depth", + "kg/(m^2)", UC_NONE}, + /* 14 */ {"SNOC", "Convective snow", "kg/(m^2)", UC_NONE}, + /* 15 */ {"SNOL", "Large scale snow", "kg/(m^2)", UC_NONE}, + /* 16 */ {"SNOM", "Snow melt", "kg/(m^2)", UC_NONE}, + /* 17 */ {"SNOAG", "Snow age", "day", UC_NONE}, + /* 18 */ {"", "Absolute humidity", "kg/(m^3)", UC_NONE}, + /* 19 */ {"", "Precipitation type", "(1 Rain, 2 Thunderstorm, " + "3 Freezing Rain, 4 Mixed/ice, 5 snow, 255 missing)", UC_NONE}, + /* 20 */ {"", "Integrated liquid water", "kg/(m^2)", UC_NONE}, + /* 21 */ {"TCOND", "Condensate", "kg/kg", UC_NONE}, +/* CLWMR Did not make it to tables yet should be "-" */ + /* 22 */ {"CLWMR", "Cloud Water Mixing Ratio", "kg/kg", UC_NONE}, + /* 23 */ {"ICMR", "Ice water mixing ratio", "kg/kg", UC_NONE}, /* ICMR? */ + /* 24 */ {"RWMR", "Rain Water Mixing Ratio", "kg/kg", UC_NONE}, + /* 25 */ {"SNMR", "Snow Water Mixing Ratio", "kg/kg", UC_NONE}, + /* 26 */ {"MCONV", "Horizontal moisture convergence", "kg/(kg s)", UC_NONE}, + /* 27 */ {"", "Maximum relative humidity", "%", UC_NONE}, + /* 28 */ {"", "Maximum absolute humidity", "kg/(m^3)", UC_NONE}, + /* NDFD */ + /* 29 */ {"ASNOW", "Total snowfall", "m", UC_M2Inch}, + /* 30 */ {"", "Precipitable water category", "(undefined)", UC_NONE}, + /* 31 */ {"", "Hail", "m", UC_NONE}, + /* 32 */ {"", "Graupel (snow pellets)", "kg/kg", UC_NONE}, +/* 33 */ {"CRAIN", "Categorical rain", "0=no, 1=yes", UC_NONE}, +/* 34 */ {"CFRZR", "Categorical freezing rain", "0=no, 1=yes", UC_NONE}, +/* 35 */ {"CICEP", "Categorical ice pellets", "0=no, 1=yes", UC_NONE}, +/* 36 */ {"CSNOW", "Categorical snow", "0=no, 1=yes", UC_NONE}, +/* 37 */ {"CPRAT", "Convective precipitation rate", "kg/(m^2*s)", UC_NONE}, +/* 38 */ {"MCONV", "Horizontal moisture divergence", "kg/(kg*s)", UC_NONE}, +/* 39 */ {"CPOFP", "Percent frozen precipitation", "%", UC_NONE}, +/* 40 */ {"PEVAP", "Potential evaporation", "kg/m^2", UC_NONE}, +/* 41 */ {"PEVPR", "Potential evaporation rate", "W/m^2", UC_NONE}, +/* 42 */ {"SNOWC", "Snow Cover", "%", UC_NONE}, +/* 43 */ {"FRAIN", "Rain fraction of total cloud water", "-", UC_NONE}, +/* 44 */ {"RIME", "Rime factor", "-", UC_NONE}, +/* 45 */ {"TCOLR", "Total column integrated rain", "kg/m^2", UC_NONE}, +/* 46 */ {"TCOLS", "Total column integrated snow", "kg/m^2", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.2 */ +GRIB2ParmTable MeteoMoment[] = { + /* 0 */ {"WDIR", "Wind direction (from which blowing)", "deg true", + UC_NONE}, /* Need NDFD override WindDir */ + /* 1 */ {"WIND", "Wind speed", "m/s", UC_MS2Knots}, /* Need NDFD override WindSpd */ + /* 2 */ {"UGRD", "u-component of wind", "m/s", UC_NONE}, + /* 3 */ {"VGRD", "v-component of wind", "m/s", UC_NONE}, + /* 4 */ {"STRM", "Stream function", "(m^2)/s", UC_NONE}, + /* 5 */ {"VPOT", "Velocity potential", "(m^2)/s", UC_NONE}, + /* 6 */ {"MNTSF", "Montgomery stream function", "(m^2)/(s^2)", UC_NONE}, + /* 7 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE}, + /* 8 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, /* NCEP override WEL? */ + /* 9 */ {"DZDT", "Verical velocity (geometric)", "m/s", UC_NONE}, + /* 10 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE}, + /* 11 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE}, + /* 12 */ {"RELV", "Relative vorticity", "1/s", UC_NONE}, + /* 13 */ {"RELD", "Relative divergence", "1/s", UC_NONE}, + /* 14 */ {"PV", "Potential vorticity", "K(m^2)/(kg s)", UC_NONE}, + /* 15 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE}, + /* 16 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE}, + /* 17 */ {"UFLX", "Momentum flux; u component", "N/(m^2)", UC_NONE}, + /* 18 */ {"VFLX", "Momentum flux; v component", "N/(m^2)", UC_NONE}, + /* 19 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE}, + /* 20 */ {"BLYDP", "Boundary layer dissipation", "W/(m^2)", UC_NONE}, + /* 21 */ {"", "Maximum wind speed", "m/s", UC_NONE}, + /* 22 */ {"GUST", "Wind speed (gust)", "m/s", UC_MS2Knots}, /* GUST? */ + /* 23 */ {"", "u-component of wind (gust)", "m/s", UC_NONE}, + /* 24 */ {"", "v-component of wind (gust)", "m/s", UC_NONE}, +/* 25 */ {"VWSH", "Vertical speed shear", "1/s", UC_NONE}, +/* 26 */ {"MFLX", "Horizontal momentum flux", "N/(m^2)", UC_NONE}, +/* 27 */ {"USTM", "U-component storm motion", "m/s", UC_NONE}, +/* 28 */ {"VSTM", "V-component storm motion", "m/s", UC_NONE}, +/* 29 */ {"CD", "Drag coefficient", "-", UC_NONE}, +/* 30 */ {"FRICV", "Frictional velocity", "m/s", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.3 */ +GRIB2ParmTable MeteoMass[] = { + /* 0 */ {"PRES", "Pressure", "Pa", UC_NONE}, + /* 1 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE}, + /* 2 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE}, + /* 3 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", UC_NONE}, + /* 4 */ {"GP", "Geopotential", "(m^2)/(s^2)", UC_NONE}, + /* 5 */ {"HGT", "Geopotential height", "gpm", UC_NONE}, + /* 6 */ {"DIST", "Geometric Height", "m", UC_NONE}, + /* 7 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE}, + /* 8 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE}, + /* 9 */ {"GPA", "Geopotential height anomally", "gpm", UC_NONE}, + /* 10 */ {"DEN", "Density", "kg/(m^3)", UC_NONE}, + /* 11 */ {"", "Altimeter setting", "Pa", UC_NONE}, + /* 12 */ {"", "Thickness", "m", UC_NONE}, + /* 13 */ {"", "Pressure altitude", "m", UC_NONE}, + /* 14 */ {"", "Density altitude", "m", UC_NONE}, +/* 15 */ {"5WAVH", "5-wave geopotential height", "gpm", UC_NONE}, +/* 16 */ {"U-GWD", "Zonal flux of gravity wave stress", "N/(m^2)", UC_NONE}, +/* 17 */ {"V-GWD", "Meridional flux of gravity wave stress", "N/(m^2)", UC_NONE}, +/* 18 */ {"HPBL", "Planetary boundary layer height", "m", UC_NONE}, +/* 19 */ {"5WAVA", "5-Wave geopotential height anomaly", "gpm", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.4 */ +GRIB2ParmTable MeteoShortRadiate[] = { + /* 0 */ {"NSWRS", "Net short-wave radiation flux (surface)", "W/(m^2)", UC_NONE}, + /* 1 */ {"NSWRT", "Net short-wave radiation flux (top of atmosphere)", + "W/(m^2)", UC_NONE}, + /* 2 */ {"SWAVR", "Short wave radiation flux", "W/(m^2)", UC_NONE}, + /* 3 */ {"GRAD", "Global radiation flux", "W/(m^2)", UC_NONE}, + /* 4 */ {"BRTMP", "Brightness temperature", "K", UC_NONE}, + /* 5 */ {"LWRAD", "Radiance (with respect to wave number)", "W/(m sr)", UC_NONE}, + /* 6 */ {"SWRAD", "Radiance (with respect to wave length)", "W/(m^3 sr)", UC_NONE}, +/* 7 */ {"DSWRF", "Downward short-wave radiation flux", "W/(m^2)", UC_NONE}, +/* 8 */ {"USWRF", "Upward short-wave radiation flux", "W/(m^2)", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.5 */ +GRIB2ParmTable MeteoLongRadiate[] = { + /* 0 */ {"NLWRS", "Net long wave radiation flux (surface)", "W/(m^2)", UC_NONE}, + /* 1 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)", + "W/(m^2)", UC_NONE}, + /* 2 */ {"LWAVR", "Long wave radiation flux", "W/(m^2)", UC_NONE}, +/* 3 */ {"DLWRF", "Downward Long-Wave Rad. Flux", "W/(m^2)", UC_NONE}, +/* 4 */ {"ULWRF", "Upward Long-Wave Rad. Flux", "W/(m^2)", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.6 */ +GRIB2ParmTable MeteoCloud[] = { + /* 0 */ {"CICE", "Cloud Ice", "kg/(m^2)", UC_NONE}, + /* 1 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, /* Need NDFD override Sky */ + /* 2 */ {"CDCON", "Convective cloud cover", "%", UC_NONE}, + /* 3 */ {"LCDC", "Low cloud cover", "%", UC_NONE}, + /* 4 */ {"MCDC", "Medium cloud cover", "%", UC_NONE}, + /* 5 */ {"HCDC", "High cloud cover", "%", UC_NONE}, + /* 6 */ {"CWAT", "Cloud water", "kg/(m^2)", UC_NONE}, + /* 7 */ {"", "Cloud amount", "%", UC_NONE}, + /* 8 */ {"", "Cloud type", "(0 clear, 1 Cumulonimbus, 2 Stratus, " + "3 Stratocumulus, 4 Cumulus, 5 Altostratus, 6 Nimbostratus, " + "7 Altocumulus, 8 Cirrostratus, 9 Cirrocumulus, 10 Cirrus, " + "11 Cumulonimbus (fog), 12 Stratus (fog), 13 Stratocumulus (fog)," + " 14 Cumulus (fog), 15 Altostratus (fog), 16 Nimbostratus (fog), " + "17 Altocumulus (fog), 18 Cirrostratus (fog), " + "19 Cirrocumulus (fog), 20 Cirrus (fog), 191 unknown, " + "255 missing)", UC_NONE}, + /* 9 */ {"", "Thunderstorm maximum tops", "m", UC_NONE}, + /* 10 */ {"", "Thunderstorm coverage", "(0 none, 1 isolated (1%-2%), " + "2 few (3%-15%), 3 scattered (16%-45%), 4 numerous (> 45%), " + "255 missing)", UC_NONE}, + /* 11 */ {"", "Cloud base", "m", UC_NONE}, + /* 12 */ {"", "Cloud top", "m", UC_NONE}, + /* 13 */ {"", "Ceiling", "m", UC_NONE}, +/* 14 */ {"CDLYR", "Non-convective cloud cover", "%", UC_NONE}, +/* 15 */ {"CWORK", "Cloud work function", "J/kg", UC_NONE}, +/* 16 */ {"CUEFI", "Convective cloud efficiency", "-", UC_NONE}, +/* 17 */ {"TCOND", "Total condensate", "kg/kg", UC_NONE}, +/* 18 */ {"TCOLW", "Total column-integrated cloud water", "kg/(m^2)", UC_NONE}, +/* 19 */ {"TCOLI", "Total column-integrated cloud ice", "kg/(m^2)", UC_NONE}, +/* 20 */ {"TCOLC", "Total column-integrated condensate", "kg/(m^2)", UC_NONE}, +/* 21 */ {"FICE", "Ice fraction of total condensate", "-", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.7 */ +/* NCEP capitalized items 6, 7, 8 */ +GRIB2ParmTable MeteoStability[] = { + /* 0 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE}, + /* 1 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE}, + /* 2 */ {"KX", "K index", "K", UC_NONE}, + /* 3 */ {"", "KO index", "K", UC_NONE}, + /* 4 */ {"", "Total totals index", "K", UC_NONE}, + /* 5 */ {"SX", "Sweat index", "numeric", UC_NONE}, + /* 6 */ {"CAPE", "Convective available potential energy", "J/kg", UC_NONE}, + /* 7 */ {"CIN", "Convective inhibition", "J/kg", UC_NONE}, + /* 8 */ {"HLCY", "Storm relative helicity", "J/kg", UC_NONE}, + /* 9 */ {"", "Energy helicity index", "numeric", UC_NONE}, +/* 10 */ {"LFTX", "Surface fifted index", "K", UC_NONE}, +/* 11 */ {"4LFTX", "Best (4-layer) lifted index", "K", UC_NONE}, +/* 12 */ {"RI", "Richardson number", "-", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.13 */ +GRIB2ParmTable MeteoAerosols[] = { + /* 0 */ {"", "Aerosol type", "(0 Aerosol not present, 1 Aerosol present, " + "255 missing)", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.14 */ +GRIB2ParmTable MeteoGases[] = { + /* 0 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE}, +/* 1 */ {"O3MR", "Ozone Mixing Ratio", "kg/kg", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.15 */ +GRIB2ParmTable MeteoRadar[] = { + /* 0 */ {"", "Base spectrum width", "m/s", UC_NONE}, + /* 1 */ {"", "Base reflectivity", "dB", UC_NONE}, + /* 2 */ {"", "Base radial velocity", "m/s", UC_NONE}, + /* 3 */ {"", "Vertically-integrated liquid", "kg/m", UC_NONE}, + /* 4 */ {"", "Layer-maximum base reflectivity", "dB", UC_NONE}, + /* 5 */ {"", "Precipitation", "kg/(m^2)", UC_NONE}, + /* 6 */ {"RDSP1", "Radar spectra (1)", "-", UC_NONE}, + /* 7 */ {"RDSP2", "Radar spectra (2)", "-", UC_NONE}, + /* 8 */ {"RDSP3", "Radar spectra (3)", "-", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.18 */ +GRIB2ParmTable MeteoNuclear[] = { + /* 0 */ {"", "Air concentration of Caesium 137", "Bq/(m^3)", UC_NONE}, + /* 1 */ {"", "Air concentration of Iodine 131", "Bq/(m^3)", UC_NONE}, + /* 2 */ {"", "Air concentration of radioactive pollutant", "Bq/(m^3)", UC_NONE}, + /* 3 */ {"", "Ground deposition of Caesium 137", "Bq/(m^2)", UC_NONE}, + /* 4 */ {"", "Ground deposition of Iodine 131", "Bq/(m^2)", UC_NONE}, + /* 5 */ {"", "Ground deposition of radioactive pollutant", "Bq/(m^2)", UC_NONE}, + /* 6 */ {"", "Time-integrated air concentration of caesium pollutant", + "(Bq s)/(m^3)", UC_NONE}, + /* 7 */ {"", "Time-integrated air concentration of iodine pollutant", + "(Bq s)/(m^3)", UC_NONE}, + /* 8 */ {"", "Time-integrated air concentration of radioactive pollutant", + "(Bq s)/(m^3)", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.19 */ +/* NCEP capitalized items 11 */ +GRIB2ParmTable MeteoAtmos[] = { + /* 0 */ {"VIS", "Visibility", "m", UC_NONE}, + /* 1 */ {"ALBDO", "Albedo", "%", UC_NONE}, + /* 2 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE}, + /* 3 */ {"MIXHT", "mixed layer depth", "m", UC_NONE}, + /* 4 */ {"", "Volcanic ash", "(0 not present, 1 present, 255 missing)", UC_NONE}, + /* 5 */ {"", "Icing top", "m", UC_NONE}, + /* 6 */ {"", "Icing base", "m", UC_NONE}, + /* 7 */ {"", "Icing", "(0 None, 1 Light, 2 Moderate, 3 Severe, " + "255 missing)", UC_NONE}, + /* 8 */ {"", "Turbulance top", "m", UC_NONE}, + /* 9 */ {"", "Turbulence base", "m", UC_NONE}, + /* 10 */ {"", "Turbulance", "(0 None(smooth), 1 Light, 2 Moderate, " + "3 Severe, 4 Extreme, 255 missing)", UC_NONE}, + /* 11 */ {"TKE", "Turbulent Kinetic Energy", "J/kg", UC_NONE}, + /* 12 */ {"", "Planetary boundary layer regime", "(0 Reserved, 1 Stable, " + "2 Mechanically driven turbulence, 3 Forced convection, " + "4 Free convection, 255 missing)", UC_NONE}, + /* 13 */ {"", "Contrail intensity", "(0 Contrail not present, " + "1 Contrail present, 255 missing)", UC_NONE}, + /* 14 */ {"", "Contrail engine type", "(0 Low bypass, 1 High bypass, " + "2 Non bypass, 255 missing)", UC_NONE}, + /* 15 */ {"", "Contrail top", "m", UC_NONE}, + /* 16 */ {"", "Contrail base", "m", UC_NONE}, +/* 17 */ {"MXSALB", "Maximum snow albedo", "%", UC_NONE}, +/* 18 */ {"SNFALB", "Snow free albedo", "%", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 0.253 or 0.190 (Document is inconsistent.) */ +GRIB2ParmTable MeteoText[] = { + /* 0 */ {"", "Arbitrary text string", "CCITTIA5", UC_NONE}, +}; + +GRIB2ParmTable MeteoMisc[] = { + /* 0 */ {"TSEC", "Seconds prior to initial reference time (defined in Section" + " 1)", "s", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 1.0 */ +GRIB2ParmTable HydroBasic[] = { + /* 0 */ {"", "Flash flood guidance", "kg/(m^2)", UC_NONE}, + /* 1 */ {"", "Flash flood runoff", "kg/(m^2)", UC_NONE}, + /* 2 */ {"", "Remotely sensed snow cover", "(50 no-snow/no-cloud, " + "100 Clouds, 250 Snow, 255 missing)", UC_NONE}, + /* 3 */ {"", "Elevation of snow covered terrain", "(0-90 elevation in " + "increments of 100m, 254 clouds, 255 missing)", UC_NONE}, + /* 4 */ {"", "Snow water equivalent percent of normal", "%", UC_NONE}, +/* 5 */ {"BGRUN", "Baseflow-groundwater runoff", "kg/(m^2)", UC_NONE}, +/* 6 */ {"SSRUN", "Storm surface runoff", "kg/(m^2)", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 1.1 */ +GRIB2ParmTable HydroProb[] = { + /* 0 */ {"", "Conditional percent precipitation amount fractile for an " + "overall period", "kg/(m^2)", UC_NONE}, + /* 1 */ {"", "Percent precipitation in a sub-period of an overall period", + "%", UC_NONE}, + /* 2 */ {"PoP", "Probability of 0.01 inch of precipitation", "%", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 2.0 */ +GRIB2ParmTable LandVeg[] = { + /* 0 */ {"LAND", "Land cover (1=land; 2=sea)", "Proportion", UC_NONE}, + /* 1 */ {"SFCR", "Surface roughness", "m", UC_NONE}, /*NCEP override SFRC? */ + /* 2 */ {"TSOIL", "Soil temperature", "K", UC_NONE}, + /* 3 */ {"SOILM", "Soil moisture content", "kg/(m^2)", UC_NONE}, + /* 4 */ {"VEG", "Vegetation", "%", UC_NONE}, + /* 5 */ {"WATR", "Water runoff", "kg/(m^2)", UC_NONE}, + /* 6 */ {"", "Evapotranspiration", "1/(kg^2 s)", UC_NONE}, + /* 7 */ {"", "Model terrain height", "m", UC_NONE}, + /* 8 */ {"", "Land use", "(1 Urban land, 2 agriculture, 3 Range Land, " + "4 Deciduous forest, 5 Coniferous forest, 6 Forest/wetland, " + "7 Water, 8 Wetlands, 9 Desert, 10 Tundra, 11 Ice, " + "12 Tropical forest, 13 Savannah)", UC_NONE}, +/* 9 */ {"SOILW", "Volumetric soil moisture content", "fraction", UC_NONE}, +/* 10 */ {"GFLUX", "Ground heat flux", "W/(m^2)", UC_NONE}, +/* 11 */ {"MSTAV", "Moisture availability", "%", UC_NONE}, +/* 12 */ {"SFEXC", "Exchange coefficient", "(kg/(m^3))(m/s)", UC_NONE}, +/* 13 */ {"CNWAT", "Plant canopy surface water", "kg/(m^2)", UC_NONE}, +/* 14 */ {"BMIXL", "Blackadar's mixing length scale", "m", UC_NONE}, +/* 15 */ {"CCOND", "Canopy conductance", "m/s", UC_NONE}, +/* 16 */ {"RSMIN", "Minimal stomatal resistance", "s/m", UC_NONE}, +/* 17 */ {"WILT", "Wilting point", "fraction", UC_NONE}, +/* 18 */ {"RCS", "Solar parameter in canopy conductance", "fraction", UC_NONE}, +/* 19 */ {"RCT", "Temperature parameter in canopy conductance", "fraction", UC_NONE}, +/* 20 */ {"RCSOL", "Soil moisture parameter in canopy conductance", "fraction", UC_NONE}, +/* 21 */ {"RCQ", "Humidity parameter in canopy conductance", "fraction", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 2.3 */ +/* NCEP changed 0 to be "Soil type (as in Zobler)" I ignored them */ +GRIB2ParmTable LandSoil[] = { + /* 0 */ {"SOTYP", "Soil type", "(1 Sand, 2 Loamy sand, 3 Sandy loam, " + "4 Silt loam, 5 Organic (redefined), 6 Sandy clay loam, " + "7 Silt clay loam, 8 Clay loam, 9 Sandy clay, 10 Silty clay, " + "11 Clay)", UC_NONE}, + /* 1 */ {"", "Upper layer soil temperature", "K", UC_NONE}, + /* 2 */ {"", "Upper layer soil moisture", "kg/(m^3)", UC_NONE}, + /* 3 */ {"", "Lower layer soil moisture", "kg/(m^3)", UC_NONE}, + /* 4 */ {"", "Bottom layer soil temperature", "K", UC_NONE}, +/* 5 */ {"SOILL", "Liquid volumetric soil moisture (non-frozen)", "fraction", UC_NONE}, +/* 6 */ {"RLYRS", "Number of soil layers in root zone", "-", UC_NONE}, +/* 7 */ {"SMREF", "Transpiration stress-onset (soil moisture)", "fraction", UC_NONE}, +/* 8 */ {"SMDRY", "Direct evaporation cease (soil moisture)", "fraction", UC_NONE}, +/* 9 */ {"POROS", "Soil porosity", "fraction", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 3.0 */ +GRIB2ParmTable SpaceImage[] = { + /* 0 */ {"", "Scaled radiance", "numeric", UC_NONE}, + /* 1 */ {"", "Scaled albedo", "numeric", UC_NONE}, + /* 2 */ {"", "Scaled brightness temperature", "numeric", UC_NONE}, + /* 3 */ {"", "Scaled precipitable water", "numeric", UC_NONE}, + /* 4 */ {"", "Scaled lifted index", "numeric", UC_NONE}, + /* 5 */ {"", "Scaled cloud top pressure", "numeric", UC_NONE}, + /* 6 */ {"", "Scaled skin temperature", "numeric", UC_NONE}, + /* 7 */ {"", "Cloud mask", "(0 clear over water, 1 clear over land, " + "2 cloud)", UC_NONE}, +/* 8 */ {"", "Pixel scene type", "(0 No scene, 1 needle, 2 broad-leafed, " + "3 Deciduous needle, 4 Deciduous broad-leafed, 5 Deciduous mixed, " + "6 Closed shrub, 7 Open shrub, 8 Woody savannah, 9 Savannah, " + "10 Grassland, 11 wetland, 12 Cropland, 13 Urban, 14 crops, " + "15 snow, 16 Desert, 17 Water, 18 Tundra, 97 Snow on land, " + "98 Snow on water, 99 Sun-glint, 100 General cloud, " + "101 (fog, Stratus), 102 Stratocumulus, 103 Low cloud, " + "104 Nimbotratus, 105 Altostratus, 106 Medium cloud, 107 Cumulus, " + "108 Cirrus, 109 High cloud, 110 Unknown cloud)", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 3.1 */ +GRIB2ParmTable SpaceQuantitative[] = { + /* 0 */ {"", "Estimated precipitation", "kg/(m^2)", UC_NONE}, +/* 1 */ {"", "Instantaneous rain rate", "kg/(m^2*s)", UC_NONE}, +/* 2 */ {"", "Cloud top height", "kg/(m^2*s)", UC_NONE}, +/* 3 */ {"", "Cloud top height quality indicator", "(0 Nominal cloud top " + "height quality, 1 Fog in segment, 2 Poor quality height estimation " + "3 Fog in segment and poor quality height estimation)", UC_NONE}, +/* 4 */ {"", "Estimated u component of wind", "m/s", UC_NONE}, +/* 5 */ {"", "Estimated v component of wind", "m/s", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 10.0 */ +GRIB2ParmTable OceanWaves[] = { + /* 0 */ {"WVSP1", "Wave spectra (1)", "-", UC_NONE}, + /* 1 */ {"WVSP2", "Wave spectra (2)", "-", UC_NONE}, + /* 2 */ {"WVSP3", "Wave spectra (3)", "-", UC_NONE}, + /* 3 */ {"HTSGW", "Significant height of combined wind waves and swell", "m", UC_NONE}, + /* 4 */ {"WVDIR", "Direction of wind waves", "Degree true", UC_NONE}, + /* 5 */ {"WVHGT", "Significant height of wind waves", "m", UC_M2Feet}, /* NDFD override needed WaveHeight */ + /* 6 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE}, + /* 7 */ {"SWDIR", "Direction of swell waves", "Degree true", UC_NONE}, + /* 8 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE}, + /* 9 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE}, + /* 10 */ {"DIRPW", "Primary wave direction", "Degree true", UC_NONE}, + /* 11 */ {"PERPW", "Primary wave mean period", "s", UC_NONE}, + /* 12 */ {"DIRSW", "Secondary wave direction", "Degree true", UC_NONE}, + /* 13 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 10.1 */ +GRIB2ParmTable OceanCurrents[] = { + /* 0 */ {"DIRC", "Current direction", "Degree true", UC_NONE}, + /* 1 */ {"SPC", "Current speed", "m/s", UC_NONE}, + /* 2 */ {"UOGRD", "u-component of current", "m/s", UC_NONE}, + /* 3 */ {"VOGRD", "v-component of current", "m/s", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 10.2 */ +GRIB2ParmTable OceanIce[] = { + /* 0 */ {"ICEC", "Ice cover", "Proportion", UC_NONE}, + /* 1 */ {"ICETK", "Ice thinkness", "m", UC_NONE}, + /* 2 */ {"DICED", "Direction of ice drift", "Degree true", UC_NONE}, + /* 3 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE}, + /* 4 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE}, + /* 5 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE}, + /* 6 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE}, + /* 7 */ {"ICED", "Ice divergence", "1/s", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 10.3 */ +GRIB2ParmTable OceanSurface[] = { + /* 0 */ {"WTMP", "Water temperature", "K", UC_NONE}, + /* 1 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE}, +}; + +/* GRIB2 Code table 4.2 : 10.4 */ +GRIB2ParmTable OceanSubSurface[] = { + /* 0 */ {"MTHD", "Main thermocline depth", "m", UC_NONE}, + /* 1 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE}, + /* 2 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE}, + /* 3 */ {"SALTY", "Salinity", "kg/kg", UC_NONE}, +}; + +/* *INDENT-ON* */ + +/***************************************************************************** + * Choose_GRIB2ParmTable() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Chooses the correct Parameter table depending on what is in the GRIB2 + * message's "Product Definition Section". + * + * ARGUMENTS + * prodType = The product type (meteo, hydro, land, space, ocean, etc) (In) + * cat = The category inside the product (Input) + * tableLen = The length of the returned table (Output) + * + * FILES/DATABASES: None + * + * RETURNS: ParmTable (appropriate parameter table.) + * + * HISTORY + * 1/2004 Arthur Taylor (MDL/RSIS): Created + * + * NOTES + ***************************************************************************** + */ +static GRIB2ParmTable *Choose_GRIB2ParmTable (int prodType, int cat, + size_t *tableLen) +{ + enum { METEO_TEMP = 0, METEO_MOIST = 1, METEO_MOMENT = 2, METEO_MASS = 3, + METEO_SW_RAD = 4, METEO_LW_RAD = 5, METEO_CLOUD = 6, + METEO_THERMO_INDEX = 7, METEO_KINEMATIC_INDEX = 8, METEO_TEMP_PROB = 9, + METEO_MOISTURE_PROB = 10, METEO_MOMENT_PROB = 11, METEO_MASS_PROB = 12, + METEO_AEROSOL = 13, METEO_GAS = 14, METEO_RADAR = 15, + METEO_RADAR_IMAGERY = 16, METEO_ELECTRO = 17, METEO_NUCLEAR = 18, + METEO_ATMOS = 19, METEO_CCITT = 190, METEO_MISC = 191, + METEO_CCITT2 = 253 + }; + enum { HYDRO_BASIC = 0, HYDRO_PROB = 1 }; + enum { LAND_VEG = 0, LAND_SOIL = 3 }; + enum { SPACE_IMAGE = 0, SPACE_QUANTIT = 1 }; + enum { OCEAN_WAVES = 0, OCEAN_CURRENTS = 1, OCEAN_ICE = 2, OCEAN_SURF = 3, + OCEAN_SUBSURF = 4 + }; + + switch (prodType) { + case 0: /* Meteo type. */ + switch (cat) { + case METEO_TEMP: + *tableLen = sizeof (MeteoTemp) / sizeof (GRIB2ParmTable); + return &MeteoTemp[0]; + case METEO_MOIST: + *tableLen = sizeof (MeteoMoist) / sizeof (GRIB2ParmTable); + return &MeteoMoist[0]; + case METEO_MOMENT: + *tableLen = sizeof (MeteoMoment) / sizeof (GRIB2ParmTable); + return &MeteoMoment[0]; + case METEO_MASS: + *tableLen = sizeof (MeteoMass) / sizeof (GRIB2ParmTable); + return &MeteoMass[0]; + case METEO_SW_RAD: + *tableLen = (sizeof (MeteoShortRadiate) / + sizeof (GRIB2ParmTable)); + return &MeteoShortRadiate[0]; + case METEO_LW_RAD: + *tableLen = (sizeof (MeteoLongRadiate) / + sizeof (GRIB2ParmTable)); + return &MeteoLongRadiate[0]; + case METEO_CLOUD: + *tableLen = sizeof (MeteoCloud) / sizeof (GRIB2ParmTable); + return &MeteoCloud[0]; + case METEO_THERMO_INDEX: + *tableLen = sizeof (MeteoStability) / sizeof (GRIB2ParmTable); + return &MeteoStability[0]; + case METEO_KINEMATIC_INDEX: + case METEO_TEMP_PROB: + case METEO_MOISTURE_PROB: + case METEO_MOMENT_PROB: + case METEO_MASS_PROB: + *tableLen = 0; + return NULL; + case METEO_AEROSOL: + *tableLen = sizeof (MeteoAerosols) / sizeof (GRIB2ParmTable); + return &MeteoAerosols[0]; + case METEO_GAS: + *tableLen = sizeof (MeteoGases) / sizeof (GRIB2ParmTable); + return &MeteoGases[0]; + case METEO_RADAR: + *tableLen = sizeof (MeteoRadar) / sizeof (GRIB2ParmTable); + return &MeteoRadar[0]; + case METEO_RADAR_IMAGERY: + case METEO_ELECTRO: + *tableLen = 0; + return NULL; + case METEO_NUCLEAR: + *tableLen = sizeof (MeteoNuclear) / sizeof (GRIB2ParmTable); + return &MeteoNuclear[0]; + case METEO_ATMOS: + *tableLen = sizeof (MeteoAtmos) / sizeof (GRIB2ParmTable); + return &MeteoAtmos[0]; + case METEO_CCITT: + case METEO_CCITT2: + *tableLen = sizeof (MeteoText) / sizeof (GRIB2ParmTable); + return &MeteoText[0]; + case METEO_MISC: + *tableLen = sizeof (MeteoMisc) / sizeof (GRIB2ParmTable); + return &MeteoMisc[0]; + default: + *tableLen = 0; + return NULL; + } + case 1: /* Hydro type. */ + switch (cat) { + case HYDRO_BASIC: + *tableLen = sizeof (HydroBasic) / sizeof (GRIB2ParmTable); + return &HydroBasic[0]; + case HYDRO_PROB: + *tableLen = sizeof (HydroProb) / sizeof (GRIB2ParmTable); + return &HydroProb[0]; + default: + *tableLen = 0; + return NULL; + } + case 2: /* Land type. */ + switch (cat) { + case LAND_VEG: + *tableLen = sizeof (LandVeg) / sizeof (GRIB2ParmTable); + return &LandVeg[0]; + case LAND_SOIL: + *tableLen = sizeof (LandSoil) / sizeof (GRIB2ParmTable); + return &LandSoil[0]; + default: + *tableLen = 0; + return NULL; + } + case 3: /* Space type. */ + switch (cat) { + case SPACE_IMAGE: + *tableLen = sizeof (SpaceImage) / sizeof (GRIB2ParmTable); + return &SpaceImage[0]; + case SPACE_QUANTIT: + *tableLen = (sizeof (SpaceQuantitative) / + sizeof (GRIB2ParmTable)); + return &SpaceQuantitative[0]; + default: + *tableLen = 0; + return NULL; + } + case 10: /* ocean type. */ + switch (cat) { + case OCEAN_WAVES: + *tableLen = sizeof (OceanWaves) / sizeof (GRIB2ParmTable); + return &OceanWaves[0]; + case OCEAN_CURRENTS: + *tableLen = sizeof (OceanCurrents) / sizeof (GRIB2ParmTable); + return &OceanCurrents[0]; + case OCEAN_ICE: + *tableLen = sizeof (OceanIce) / sizeof (GRIB2ParmTable); + return &OceanIce[0]; + case OCEAN_SURF: + *tableLen = sizeof (OceanSurface) / sizeof (GRIB2ParmTable); + return &OceanSurface[0]; + default: + *tableLen = 0; + return NULL; + } + default: + *tableLen = 0; + return NULL; + } +} + +/* *INDENT-OFF* */ +NDFD_AbrevOverideTable NDFD_Overide[] = { + /* 0 */ {"TMP", "T"}, + /* 1 */ {"TMAX", "MaxT"}, + /* 2 */ {"TMIN", "MinT"}, + /* 3 */ {"DPT", "Td"}, + /* 4 */ {"APCP", "QPF"}, + /* Don't need SNOD for now. */ + /* 5 */ /* {"SNOD", "SnowDepth"}, */ + /* 6 */ {"WDIR", "WindDir"}, + /* 7 */ {"WIND", "WindSpd"}, + /* 8 */ {"TCDC", "Sky"}, + /* 9 */ {"WVHGT", "WaveHeight"}, + /* 10 */ {"ASNOW", "SnowAmt"}, + /* 11 */ {"GUST", "WindGust"}, +}; + +GRIB2LocalTable NDFD_LclTable[] = { + /* 0 */ {0, 1, 192, "Wx", "Weather string", "-", UC_NONE}, + /* 1 */ {0, 0, 193, "ApparentT", "Apparent Temperature", "K", UC_K2F}, + /* 2 */ {0, 14, 192, "O3MR", "Ozone Mixing Ratio", "kg/kg", UC_NONE}, + /* 3 */ {0, 14, 193, "OZCON", "Ozone Concentration", "PPB", UC_NONE}, + /* grandfather'ed in a NDFD choice for POP. */ + /* 4 */ {0, 10, 8, "PoP12", "Prob of 0.01 In. of Precip", "%", UC_NONE}, + {0, 13, 194, "smokes", "Surface level smoke from fires", + "log10(\xB5" "g/m^3)", UC_LOG10}, + {0, 13, 195, "smokec", "Average vertical column smoke from fires", + "log10(\xB5" "g/m^3)", UC_LOG10}, + /* Arthur Added this to both NDFD and NCEP local tables. (5/1/2006) */ + {10, 3, 192, "Surge", "Hurricane Storm Surge", "m", UC_M2Feet}, + {10, 3, 193, "ETSurge", "Extra Tropical Storm Surge", "m", UC_M2Feet}, +}; + +GRIB2LocalTable HPC_LclTable[] = { + /* 0 */ {0, 1, 192, "HPC-Wx", "HPC Code", "-", UC_NONE}, +}; + +/* +Updated this table last on 12/29/2005 +Based on: +http://www.nco.ncep.noaa.gov/pmb/docs/grib2/GRIB2_parmeter_conversion_table.html +Better source is: +http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_table4-1.shtml +For abreviations see: +http://www.nco.ncep.noaa.gov/pmb/docs/on388/table2.html + +Updated again on 2/14/2006 +Updated again on 3/15/2006 +*/ +GRIB2LocalTable NCEP_LclTable[] = { + /* 0 */ {0, 0, 192, "SNOHF", "Snow Phase Change Heat Flux", "W/(m^2)", UC_NONE}, + {0, 0, 193, "TTRAD", "Temperature tendency by all radiation", "K/s", UC_NONE}, + + /* 1 */ {0, 1, 192, "CRAIN", "Categorical Rain", "(0 no; 1 yes)", UC_NONE}, + /* 2 */ {0, 1, 193, "CFRZR", "Categorical Freezing Rain", "(0 no; 1 yes)", UC_NONE}, + /* 3 */ {0, 1, 194, "CICEP", "Categorical Ice Pellets", "(0 no; 1 yes)", UC_NONE}, + /* 4 */ {0, 1, 195, "CSNOW", "Categorical Snow", "(0 no; 1 yes)", UC_NONE}, + /* 5 */ {0, 1, 196, "CPRAT", "Convective Precipitation Rate", "kg/(m^2*s)", UC_NONE}, + /* 6 */ {0, 1, 197, "MCONV", "Horizontal Moisture Divergence", "kg/(kg*s)", UC_NONE}, +/* Following was grandfathered in... Should use: 1, 1, 193 */ + /* 7 */ {0, 1, 198, "CPOFP", "Percent Frozen Precipitation", "%", UC_NONE}, + /* 8 */ {0, 1, 199, "PEVAP", "Potential Evaporation", "kg/(m^2)", UC_NONE}, + /* 9 */ {0, 1, 200, "PEVPR", "Potential Evaporation Rate", "W/(m^2)", UC_NONE}, + /* 10 */ {0, 1, 201, "SNOWC", "Snow Cover", "%", UC_NONE}, + /* 11 */ {0, 1, 202, "FRAIN", "Rain Fraction of Total Liquid Water", "-", UC_NONE}, +/* FRIME -> RIME 12/29/2005 */ + /* 12 */ {0, 1, 203, "RIME", "Rime Factor", "-", UC_NONE}, + /* 13 */ {0, 1, 204, "TCOLR", "Total Column Integrated Rain", "kg/(m^2)", UC_NONE}, + /* 14 */ {0, 1, 205, "TCOLS", "Total Column Integrated Snow", "kg/(m^2)", UC_NONE}, + {0, 1, 206, "TIPD", "Total Icing Potential Diagnostic", "-", UC_NONE}, + {0, 1, 207, "NCIP", "Number concentration for ice particles", "-", UC_NONE}, + {0, 1, 208, "SNOT", "Snow temperature", "K", UC_NONE}, + + /* 15 */ {0, 2, 192, "VWSH", "Vertical speed sheer", "1/s", UC_NONE}, + /* 16 */ {0, 2, 193, "MFLX", "Horizontal Momentum Flux", "N/(m^2)", UC_NONE}, + /* 17 */ {0, 2, 194, "USTM", "U-Component Storm Motion", "m/s", UC_NONE}, + /* 18 */ {0, 2, 195, "VSTM", "V-Component Storm Motion", "m/s", UC_NONE}, + /* 19 */ {0, 2, 196, "CD", "Drag Coefficient", "-", UC_NONE}, + /* 20 */ {0, 2, 197, "FRICV", "Frictional Velocity", "m/s", UC_NONE}, + + /* 21 */ {0, 3, 192, "MSLET", "Mean Sea Level Pressure (Eta Reduction)", "Pa", UC_NONE}, + /* 22 */ {0, 3, 193, "5WAVH", "5-Wave Geopotential Height", "gpm", UC_NONE}, + /* 23 */ {0, 3, 194, "U-GWD", "Zonal Flux of Gravity Wave Stress", "N/(m^2)", UC_NONE}, + /* 24 */ {0, 3, 195, "V-GWD", "Meridional Flux of Gravity Wave Stress", "N/(m^2)", UC_NONE}, + /* 25 */ {0, 3, 196, "HPBL", "Planetary Boundary Layer Height", "m", UC_NONE}, + /* 26 */ {0, 3, 197, "5WAVA", "5-Wave Geopotential Height Anomaly", "gpm", UC_NONE}, + {0, 3, 198, "MSLMA", "Mean Sea Level Pressure (MAPS System Reduction)", "Pa", UC_NONE}, + {0, 3, 199, "TSLSA", "3-hr pressure tendency (Std. Atmos. Reduction)", "Pa/s", UC_NONE}, + {0, 3, 200, "PLPL", "Pressure of level from which parcel was lifted", "Pa", UC_NONE}, + + /* 27 */ {0, 4, 192, "DSWRF", "Downward Short-Wave Rad. Flux", "W/(m^2)", UC_NONE}, + /* 28 */ {0, 4, 193, "USWRF", "Upward Short-Wave Rad. Flux", "W/(m^2)", UC_NONE}, + {0, 4, 194, "DUVB", "UV-B downward solar flux", "W/(m^2)", UC_NONE}, + {0, 4, 195, "CDUVB", "Clear sky UV-B downward solar flux", "W/(m^2)", UC_NONE}, + + /* 29 */ {0, 5, 192, "DLWRF", "Downward Long-Wave Rad. Flux", "W/(m^2)", UC_NONE}, + /* 30 */ {0, 5, 193, "ULWRF", "Upward Long-Wave Rad. Flux", "W/(m^2)", UC_NONE}, + + /* 31 */ {0, 6, 192, "CDLYR", "Non-Convective Cloud Cover", "%", UC_NONE}, + /* 32 */ {0, 6, 193, "CWORK", "Cloud Work Function", "J/kg", UC_NONE}, + /* 33 */ {0, 6, 194, "CUEFI", "Convective Cloud Efficiency", "-", UC_NONE}, + /* 34 */ {0, 6, 195, "TCOND", "Total Condensate", "kg/kg", UC_NONE}, + /* 35 */ {0, 6, 196, "TCOLW", "Total Column-Integrated Cloud Water", "kg/(m^2)", UC_NONE}, + /* 36 */ {0, 6, 197, "TCOLI", "Total Column-Integrated Cloud Ice", "kg/(m^2)", UC_NONE}, + /* 37 */ {0, 6, 198, "TCOLC", "Total Column-Integrated Condensate", "kg/(m^2)", UC_NONE}, + /* 38 */ {0, 6, 199, "FICE", "Ice fraction of total condensate", "-", UC_NONE}, + + /* 39 */ {0, 7, 192, "LFTX", "Surface Lifted Index", "K", UC_NONE}, + /* 40 */ {0, 7, 193, "4LFTX", "Best (4 layer) Lifted Index", "K", UC_NONE}, + /* 41 */ {0, 7, 194, "RI", "Richardson Number", "-", UC_NONE}, + + {0, 13, 192, "PMTC", "Particulate matter (coarse)", "\xB5" "g/m^3", UC_NONE}, + {0, 13, 193, "PMTF", "Particulate matter (fine)", "\xB5" "g/m^3", UC_NONE}, + {0, 13, 194, "LPMTF", "Particulate matter (fine)", + "log10(\xB5" "g/m^3)", UC_LOG10}, + {0, 13, 195, "LIPMF", "Integrated column particulate matter " + "(fine)", "log10(\xB5" "g/m^3)", UC_LOG10}, + + /* 42 */ {0, 14, 192, "O3MR", "Ozone Mixing Ratio", "kg/kg", UC_NONE}, + /* 43 */ {0, 14, 193, "OZCON", "Ozone Concentration", "PPB", UC_NONE}, + /* 44 */ {0, 14, 194, "OZCAT", "Categorical Ozone Concentration", "-", UC_NONE}, + + {0, 16, 192, "REFZR", "Derived radar reflectivity backscatter from rain", "mm^6/m^3", UC_NONE}, + {0, 16, 193, "REFZI", "Derived radar reflectivity backscatter from ice", "mm^6/m^3", UC_NONE}, + {0, 16, 194, "REFZC", "Derived radar reflectivity backscatter from parameterized convection", "mm^6/m^3", UC_NONE}, + {0, 16, 195, "REFD", "Derived radar reflectivity", "dB", UC_NONE}, + {0, 16, 196, "REFC", "Maximum / Composite radar reflectivity", "dB", UC_NONE}, + + {0, 17, 192, "LTNG", "Lightning", "-", UC_NONE}, + + /* 45 */ {0, 19, 192, "MXSALB", "Maximum Snow Albedo", "%", UC_NONE}, + /* 46 */ {0, 19, 193, "SNFALB", "Snow-Free Albedo", "%", UC_NONE}, + {0, 19, 194, "", "Slight risk convective outlook", "categorical", UC_NONE}, + {0, 19, 195, "", "Moderate risk convective outlook", "categorical", UC_NONE}, + {0, 19, 196, "", "High risk convective outlook", "categorical", UC_NONE}, + {0, 19, 197, "", "Tornado probability", "%", UC_NONE}, + {0, 19, 198, "", "Hail probability", "%", UC_NONE}, + {0, 19, 199, "", "Wind probability", "%", UC_NONE}, + {0, 19, 200, "", "Significant Tornado probability", "%", UC_NONE}, + {0, 19, 201, "", "Significant Hail probability", "%", UC_NONE}, + {0, 19, 202, "", "Significant Wind probability", "%", UC_NONE}, + {0, 19, 203, "TSTMC", "Categorical Thunderstorm", "0=no, 1=yes", UC_NONE}, + {0, 19, 204, "MIXLY", "Number of mixed layers next to surface", "integer", UC_NONE}, + + /* 47 */ {0, 191, 192, "NLAT", "Latitude (-90 to 90)", "deg", UC_NONE}, + /* 48 */ {0, 191, 193, "ELON", "East Longitude (0 to 360)", "deg", UC_NONE}, + /* 49 */ {0, 191, 194, "TSEC", "Seconds prior to initial reference time", "s", UC_NONE}, + + /* 50 */ {1, 0, 192, "BGRUN", "Baseflow-Groundwater Runoff", "kg/(m^2)", UC_NONE}, + /* 51 */ {1, 0, 193, "SSRUN", "Storm Surface Runoff", "kg/(m^2)", UC_NONE}, + + {1, 1, 192, "CPOZP", "Probability of Freezing Precipitation", "%", UC_NONE}, + {1, 1, 193, "CPOFP", "Probability of Frozen Precipitation", "%", UC_NONE}, + {1, 1, 194, "PPFFG", "Probability of precipitation exceeding flash flood guidance values", "%", UC_NONE}, + + /* 52 */ {2, 0, 192, "SOILW", "Volumetric Soil Moisture Content", "Fraction", UC_NONE}, + /* 53 */ {2, 0, 193, "GFLUX", "Ground Heat Flux", "W/(m^2)", UC_NONE}, + /* 54 */ {2, 0, 194, "MSTAV", "Moisture Availability", "%", UC_NONE}, + /* 55 */ {2, 0, 195, "SFEXC", "Exchange Coefficient", "(kg/(m^3))(m/s)", UC_NONE}, + /* 56 */ {2, 0, 196, "CNWAT", "Plant Canopy Surface Water", "kg/(m^2)", UC_NONE}, + /* 57 */ {2, 0, 197, "BMIXL", "Blackadar's Mixing Length Scale", "m", UC_NONE}, + /* 58 */ {2, 0, 198, "VGTYP", "Vegetation Type", "0..13", UC_NONE}, + /* 59 */ {2, 0, 199, "CCOND", "Canopy Conductance", "m/s", UC_NONE}, + /* 60 */ {2, 0, 200, "RSMIN", "Minimal Stomatal Resistance", "s/m", UC_NONE}, + /* 61 */ {2, 0, 201, "WILT", "Wilting Point", "Fraction", UC_NONE}, + /* 62 */ {2, 0, 202, "RCS", "Solar parameter in canopy conductance", "Fraction", UC_NONE}, + /* 63 */ {2, 0, 203, "RCT", "Temperature parameter in canopy conductance", "Fraction", UC_NONE}, + /* 64 */ {2, 0, 204, "RCQ", "Humidity parameter in canopy conductance", "Fraction", UC_NONE}, + /* 65 */ {2, 0, 205, "RCSOL", "Soil moisture parameter in canopy conductance", "Fraction", UC_NONE}, + {2, 0, 206, "RDRIP", "Rate of water dropping from canopy to ground", "unknown", UC_NONE}, + {2, 0, 207, "ICWAT", "Ice-free water surface", "%", UC_NONE}, + + /* 66 */ {2, 3, 192, "SOILL", "Liquid Volumetric Soil Moisture (non Frozen)", "Proportion", UC_NONE}, + /* 67 */ {2, 3, 193, "RLYRS", "Number of Soil Layers in Root Zone", "-", UC_NONE}, + /* 68 */ {2, 3, 194, "SLTYP", "Surface Slope Type", "Index", UC_NONE}, + /* 69 */ {2, 3, 195, "SMREF", "Transpiration Stress-onset (soil moisture)", "Proportion", UC_NONE}, + /* 70 */ {2, 3, 196, "SMDRY", "Direct Evaporation Cease (soil moisture)", "Proportion", UC_NONE}, + /* 71 */ {2, 3, 197, "POROS", "Soil Porosity", "Proportion", UC_NONE}, + +/* ScatEstUWind -> USCT, ScatEstVWind -> VSCT as of 7/5/2006 (pre 1.80) */ + /* 72 */ {3, 1, 192, "USCT", "Scatterometer Estimated U Wind", "m/s", UC_NONE}, + /* 73 */ {3, 1, 193, "VSCT", "Scatterometer Estimated V Wind", "m/s", UC_NONE}, + + /* Arthur Added this to both NDFD and NCEP local tables. (5/1/2006) */ + {10, 3, 192, "SURGE", "Hurricane Storm Surge", "m", UC_M2Feet}, + {10, 3, 193, "ETSRG", "Extra Tropical Storm Surge", "m", UC_M2Feet}, +}; +/* *INDENT-ON* */ + +int IsData_NDFD (unsigned short int center, unsigned short int subcenter) +{ + return ((center == 8) && + ((subcenter == GRIB2MISSING_u2) || (subcenter == 0))); +} + +int IsData_MOS (unsigned short int center, unsigned short int subcenter) +{ + return ((center == 7) && (subcenter == 14)); +} + +/***************************************************************************** + * Choose_LocalParmTable() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Chooses the local parameter table for a given center/subcenter. + * Typically this is called after the default Choose_ParmTable was tried, + * since it consists of all the local specs, and one has to linearly walk + * through the table. + * + * ARGUMENTS + * center = The center that created the data. (Input) + * subcenter = The subcenter that created the data. (Input) + * tableLen = The length of the returned table (Output) + * + * FILES/DATABASES: None + * + * RETURNS: LocalParmTable (appropriate parameter table.) + * + * HISTORY + * 1/2004 Arthur Taylor (MDL/RSIS): Created + * + * NOTES + ***************************************************************************** + */ +static GRIB2LocalTable *Choose_LocalParmTable (unsigned short int center, + unsigned short int subcenter, + size_t *tableLen) +{ + switch (center) { + case 7: /* NWS NCEP */ + /* Check if all subcenters of NCEP use the same local table. */ +/* + *tableLen = sizeof (NCEP_LclTable) / sizeof (GRIB2LocalTable); + return &NCEP_LclTable[0]; +*/ + switch (subcenter) { + case 5: /* Have HPC use NDFD table. */ + *tableLen = sizeof (HPC_LclTable) / sizeof (GRIB2LocalTable); + return &HPC_LclTable[0]; + default: + *tableLen = sizeof (NCEP_LclTable) / sizeof (GRIB2LocalTable); + return &NCEP_LclTable[0]; +/* + *tableLen = 0; + return NULL; +*/ + } + case 8: /* NWS Telecomunications gateway */ + switch (subcenter) { + case GRIB2MISSING_u2: /* NDFD */ + case 0: /* NDFD */ + *tableLen = sizeof (NDFD_LclTable) / sizeof (GRIB2LocalTable); + return &NDFD_LclTable[0]; + default: + *tableLen = 0; + return NULL; + } + default: + *tableLen = 0; + return NULL; + } +} + +/***************************************************************************** + * ParseElemName() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Converts a prodType, template, category and subcategory quadruple to the + * ASCII string abreviation of that variable. + * For example: 0, 0, 0, 0, = "T" for temperature. + * + * ARGUMENTS + * center = The center that created the data. (Input) + * subcenter = The subcenter that created the data. (Input) + * prodType = The GRIB2, section 0 product type. (Input) + * templat = The GRIB2 section 4 template number. (Input) + * cat = The GRIB2 section 4 "General category of Product." (Input) + * subcat = The GRIB2 section 4 "Specific subcategory of Product". (Input) + * lenTime = The length of time over which statistics are done + * (see template 4.8). (Input) + * genID = The Generating process ID (used for GFS MOS) (Input) + * probType = For Probability templates (Input) + * lowerProb = Lower Limit for probability templates. (Input) + * upperProb = Upper Limit for probability templates. (Input) + * name = Short name for the data set (T, MaxT, etc) (Output) + * comment = Long form of the name (Temperature, etc) (Output) + * unit = What unit this variable is originally in (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2004 Arthur Taylor (MDL/RSIS): Re-Created. + * 6/2004 AAT: Added deltTime (because of Ozone issues). + * 8/2004 AAT: Adjusted so template 9 gets units of % and no convert. + * 3/2005 AAT: ReWrote to handle template 5, 9 and MOS. + * 9/2005 AAT: Added code to handle MOS PoP06 vs MOS PoP12. + * + * NOTES + ***************************************************************************** + */ +/* Deal with probability templates 2/16/2006 */ +static void ElemNameProb (uShort2 center, uShort2 subcenter, int prodType, + CPL_UNUSED int templat, + uChar cat, uChar subcat, sInt4 lenTime, + uChar timeIncrType, + CPL_UNUSED uChar genID, + uChar probType, + double lowerProb, double upperProb, char **name, + char **comment, char **unit, int *convert) +{ + GRIB2ParmTable *table; + GRIB2LocalTable *local; + size_t tableLen; + size_t i; + char f_isNdfd = IsData_NDFD (center, subcenter); + char f_isMos = IsData_MOS (center, subcenter); + + *unit = (char *) malloc (strlen ("[%]") + 1); + strcpy (*unit, "[%]"); + + if (f_isNdfd || f_isMos) { + /* Deal with NDFD/MOS handling of Prob Precip_Tot -> PoP12 */ + if ((prodType == 0) && (cat == 1) && (subcat == 8)) { + myAssert (probType == 1); + if (lenTime > 0) { + mallocSprintf (name, "PoP%02d", lenTime); + mallocSprintf (comment, "%02d hr Prob of Precip > 0.01 " + "In. [%%]", lenTime); + } else { + *name = (char *) malloc (strlen ("PoP") + 1); + strcpy (*name, "PoP"); + *comment = + (char *) malloc (strlen ("Prob of Precip > 0.01 In. [%]") + + 1); + strcpy (*comment, "Prob of Precip > 0.01 In. [%]"); + } + *convert = UC_NONE; + return; + } + /* + * Deal with NDFD handling of Prob. Wind speeds. + * There are different solutions for naming the Prob. Wind fields + * AAT(Mine): ProbSurge5c + */ + if ((prodType == 10) && (cat == 3) && (subcat == 192)) { + myAssert (probType == 1); + myAssert (lenTime > 0); + if (timeIncrType == 2) { + /* Incremental */ + mallocSprintf (name, "ProbSurge%02di", + (int) ((upperProb / 0.3048) + .5)); + } else { + /* Cumulative */ + myAssert (timeIncrType == 192); + mallocSprintf (name, "ProbSurge%02dc", + (int) ((upperProb / 0.3048) + .5)); + } + mallocSprintf (comment, "%02d hr Prob of Hurricane Storm Surge > %g " + "m [%%]", lenTime, upperProb); + *convert = UC_NONE; + return; + } + } + if (f_isNdfd) { + /* + * Deal with NDFD handling of Prob. Wind speeds. + * There are different solutions for naming the Prob. Wind fields + * Tim Boyer: TCWindSpdIncr34 TCWindSpdIncr50 TCWindSpdIncr64 + * TCWindSpdCumu34 TCWindSpdCumu50 TCWindSpdCumu64 + * Dave Ruth: tcwspdabv34i tcwspdabv50i tcwspdabv64i + * tcwspdabv34c tcwspdabv50c tcwspdabv64c + * AAT(Mine): ProbWindSpd34c ProbWindSpd50c ProbWindSpd64c + * ProbWindSpd34i ProbWindSpd50i ProbWindSpd64i + */ + if ((prodType == 0) && (cat == 2) && (subcat == 1)) { + myAssert (probType == 1); + myAssert (lenTime > 0); + if (timeIncrType == 2) { + /* Incremental */ + mallocSprintf (name, "ProbWindSpd%02di", + (int) ((upperProb * 3600. / 1852.) + .5)); + } else { + /* Cumulative */ + myAssert (timeIncrType == 192); + mallocSprintf (name, "ProbWindSpd%02dc", + (int) ((upperProb * 3600. / 1852.) + .5)); + } + mallocSprintf (comment, "%02d hr Prob of Wind speed > %g m/s [%%]", + lenTime, upperProb); + *convert = UC_NONE; + return; + } + } + + /* Generic tables. */ + table = Choose_GRIB2ParmTable (prodType, cat, &tableLen); + if (table != NULL) { + if (subcat < tableLen) { + /* Check for NDFD over-rides. */ + /* The NDFD over-rides for probability templates have already been + * handled. */ + if (lenTime > 0) { + mallocSprintf (name, "Prob%s%02d", table[subcat].name, lenTime); + mallocSprintf (comment, "%02d hr Prob of %s ", lenTime, + table[subcat].comment); + } else { + mallocSprintf (name, "Prob%s", table[subcat].name); + mallocSprintf (comment, "Prob of %s ", table[subcat].comment); + } + if (probType == 0) { + reallocSprintf (comment, "< %g %s [%%]", lowerProb, + table[subcat].unit); + } else if (probType == 1) { + reallocSprintf (comment, "> %g %s [%%]", upperProb, + table[subcat].unit); + } else if (probType == 2) { + reallocSprintf (comment, ">= %g, < %g %s [%%]", lowerProb, + upperProb, table[subcat].unit); + } else if (probType == 3) { + reallocSprintf (comment, "> %g %s [%%]", lowerProb, + table[subcat].unit); + } else if (probType == 4) { + reallocSprintf (comment, "< %g %s [%%]", upperProb, + table[subcat].unit); + } else { + reallocSprintf (comment, "%s [%%]", table[subcat].unit); + } + *convert = UC_NONE; + return; + } + } + + /* Local use tables. */ + local = Choose_LocalParmTable (center, subcenter, &tableLen); + if (local != NULL) { + for (i = 0; i < tableLen; i++) { + if ((prodType == local[i].prodType) && (cat == local[i].cat) && + (subcat == local[i].subcat)) { + if (lenTime > 0) { + mallocSprintf (name, "Prob%s%02d", local[i].name, lenTime); + mallocSprintf (comment, "%02d hr Prob of %s ", lenTime, + local[i].comment); + } else { + mallocSprintf (name, "Prob%s", local[i].name); + mallocSprintf (comment, "Prob of %s ", local[i].comment); + } + if (probType == 0) { + reallocSprintf (comment, "< %g %s [%%]", lowerProb, + local[i].unit); + } else if (probType == 1) { + reallocSprintf (comment, "> %g %s [%%]", upperProb, + local[i].unit); + } else if (probType == 2) { + reallocSprintf (comment, ">= %g, < %g %s [%%]", lowerProb, + upperProb, local[i].unit); + } else if (probType == 3) { + reallocSprintf (comment, "> %g %s [%%]", lowerProb, + local[i].unit); + } else if (probType == 4) { + reallocSprintf (comment, "< %g %s [%%]", upperProb, + local[i].unit); + } else { + reallocSprintf (comment, "%s [%%]", local[i].unit); + } + *convert = UC_NONE; + return; + } + } + } + + *name = (char *) malloc (strlen ("ProbUnknown") + 1); + strcpy (*name, "ProbUnknown"); + mallocSprintf (comment, "Prob of (prodType %d, cat %d, subcat %d) [-]", + prodType, cat, subcat); + *convert = UC_NONE; + return; +} + +/* Deal with percentile templates 5/1/2006 */ +static void ElemNamePerc (uShort2 center, uShort2 subcenter, int prodType, + CPL_UNUSED int templat, + uChar cat, uChar subcat, sInt4 lenTime, + sChar percentile, char **name, char **comment, + char **unit, int *convert) +{ + GRIB2ParmTable *table; + GRIB2LocalTable *local; + size_t tableLen; + size_t i; + + /* Generic tables. */ + table = Choose_GRIB2ParmTable (prodType, cat, &tableLen); + if (table != NULL) { + if (subcat < tableLen) { + /* Check for NDFD over-rides. */ + if (IsData_NDFD (center, subcenter) || + IsData_MOS (center, subcenter)) { + for (i = 0; i < (sizeof (NDFD_Overide) / + sizeof (NDFD_AbrevOverideTable)); i++) { + if (strcmp (NDFD_Overide[i].GRIB2name, table[subcat].name) == + 0) { + mallocSprintf (name, "%s%02d", NDFD_Overide[i].NDFDname, + percentile); + if (lenTime > 0) { + mallocSprintf (comment, "%02d hr %s Percentile(%d) [%s]", + lenTime, table[subcat].comment, + percentile, table[subcat].unit); + } else { + mallocSprintf (comment, "%s Percentile(%d) [%s]", + table[subcat].comment, percentile, + table[subcat].unit); + } + mallocSprintf (unit, "[%s]", table[subcat].unit); + *convert = table[subcat].convert; + return; + } + } + } + mallocSprintf (name, "%s%02d", table[subcat].name, percentile); + if (lenTime > 0) { + mallocSprintf (comment, "%02d hr %s Percentile(%d) [%s]", + lenTime, table[subcat].comment, percentile, + table[subcat].unit); + } else { + mallocSprintf (comment, "%s Percentile(%d) [%s]", + table[subcat].comment, percentile, + table[subcat].unit); + } + mallocSprintf (unit, "[%s]", table[subcat].unit); + *convert = table[subcat].convert; + return; + } + } + + /* Local use tables. */ + local = Choose_LocalParmTable (center, subcenter, &tableLen); + if (local != NULL) { + for (i = 0; i < tableLen; i++) { + if ((prodType == local[i].prodType) && (cat == local[i].cat) && + (subcat == local[i].subcat)) { + mallocSprintf (name, "%s%02d", local[i].name, percentile); + if (lenTime > 0) { + mallocSprintf (comment, "%02d hr %s Percentile(%d) [%s]", + lenTime, local[i].comment, percentile, + local[i].unit); + } else { + mallocSprintf (comment, "%s Percentile(%d) [%s]", + local[i].comment, percentile, local[i].unit); + } + mallocSprintf (unit, "[%s]", local[i].unit); + *convert = local[i].convert; + return; + } + } + } + + *name = (char *) malloc (strlen ("unknown") + 1); + strcpy (*name, "unknown"); + mallocSprintf (comment, "(prodType %d, cat %d, subcat %d) [-]", prodType, + cat, subcat); + *unit = (char *) malloc (strlen ("[-]") + 1); + strcpy (*unit, "[-]"); + *convert = UC_NONE; + return; +} + +/* Deal with non-prob templates 2/16/2006 */ +static void ElemNameNorm (uShort2 center, uShort2 subcenter, int prodType, + int templat, uChar cat, uChar subcat, sInt4 lenTime, + CPL_UNUSED uChar timeIncrType, + CPL_UNUSED uChar genID, + CPL_UNUSED uChar probType, + CPL_UNUSED double lowerProb, + CPL_UNUSED double upperProb, + char **name, + char **comment, char **unit, int *convert) +{ + GRIB2ParmTable *table; + GRIB2LocalTable *local; + size_t tableLen; + size_t i; + sChar f_accum; + + /* Check for over-ride case for ozone. Originally just for NDFD, but I + * think it is useful for ozone data that originated elsewhere. */ + if ((prodType == 0) && (templat == 8) && (cat == 14) && (subcat == 193)) { + if (lenTime > 0) { + mallocSprintf (name, "Ozone%02d", lenTime); + mallocSprintf (comment, "%d hr Average Ozone Concentration " + "[PPB]", lenTime); + } else { + *name = (char *) malloc (strlen ("AVGOZCON") + 1); + strcpy (*name, "AVGOZCON"); + *comment = + (char *) malloc (strlen ("Average Ozone Concentration [PPB]") + + 1); + strcpy (*comment, "Average Ozone Concentration [PPB]"); + } + *unit = (char *) malloc (strlen ("[PPB]") + 1); + strcpy (*unit, "[PPB]"); + *convert = UC_NONE; + return; + } + + /* Generic tables. */ + table = Choose_GRIB2ParmTable (prodType, cat, &tableLen); + if (table != NULL) { + if (subcat < tableLen) { + /* Check for NDFD over-rides. */ + if (IsData_NDFD (center, subcenter) || + IsData_MOS (center, subcenter)) { + for (i = 0; i < (sizeof (NDFD_Overide) / + sizeof (NDFD_AbrevOverideTable)); i++) { + if (strcmp (NDFD_Overide[i].GRIB2name, table[subcat].name) == + 0) { + *name = + (char *) malloc (strlen (NDFD_Overide[i].NDFDname) + + 1); + strcpy (*name, NDFD_Overide[i].NDFDname); + mallocSprintf (comment, "%s [%s]", table[subcat].comment, + table[subcat].unit); + mallocSprintf (unit, "[%s]", table[subcat].unit); + *convert = table[subcat].convert; + return; + } + } + } + /* Allow hydrologic PoP, thunderstorm probability (TSTM), or APCP to + * have lenTime labels. */ + f_accum = (((prodType == 1) && (cat == 1) && (subcat == 2)) || + ((prodType == 0) && (cat == 19) && (subcat == 2)) || + ((prodType == 0) && (cat == 1) && (subcat == 8)) || + ((prodType == 0) && (cat == 19) && (subcat == 203))); + if (f_accum && (lenTime > 0)) { + mallocSprintf (name, "%s%02d", table[subcat].name, lenTime); + mallocSprintf (comment, "%02d hr %s [%s]", lenTime, + table[subcat].comment, table[subcat].unit); + } else { + *name = (char *) malloc (strlen (table[subcat].name) + 1); + strcpy (*name, table[subcat].name); + mallocSprintf (comment, "%s [%s]", table[subcat].comment, + table[subcat].unit); + } + mallocSprintf (unit, "[%s]", table[subcat].unit); + *convert = table[subcat].convert; + return; + } + } + + /* Local use tables. */ + local = Choose_LocalParmTable (center, subcenter, &tableLen); + if (local != NULL) { + for (i = 0; i < tableLen; i++) { + if ((prodType == local[i].prodType) && (cat == local[i].cat) && + (subcat == local[i].subcat)) { + /* Allow specific products with non-zero lenTime to reflect that. + */ + f_accum = 0; + if (f_accum && (lenTime > 0)) { + mallocSprintf (name, "%s%02d", local[i].name, lenTime); + mallocSprintf (comment, "%02d hr %s [%s]", lenTime, + local[i].comment, local[i].unit); + } else { + *name = (char *) malloc (strlen (local[i].name) + 1); + strcpy (*name, local[i].name); + mallocSprintf (comment, "%s [%s]", local[i].comment, + local[i].unit); + } + mallocSprintf (unit, "[%s]", local[i].unit); + *convert = local[i].convert; + return; + } + } + } + + *name = (char *) malloc (strlen ("unknown") + 1); + strcpy (*name, "unknown"); + mallocSprintf (comment, "(prodType %d, cat %d, subcat %d) [-]", prodType, + cat, subcat); + *unit = (char *) malloc (strlen ("[-]") + 1); + strcpy (*unit, "[-]"); + *convert = UC_NONE; + return; +} + +void ParseElemName (uShort2 center, uShort2 subcenter, int prodType, + int templat, int cat, int subcat, sInt4 lenTime, + uChar timeIncrType, uChar genID, uChar probType, + double lowerProb, double upperProb, char **name, + char **comment, char **unit, int *convert, + sChar percentile) +{ + myAssert (*name == NULL); + myAssert (*comment == NULL); + myAssert (*unit == NULL); + + /* Check if this is Probability data */ + if ((templat == GS4_PROBABIL_TIME) || (templat == GS4_PROBABIL_PNT)) { + ElemNameProb (center, subcenter, prodType, templat, cat, subcat, + lenTime, timeIncrType, genID, probType, lowerProb, + upperProb, name, comment, unit, convert); + } else if (templat == GS4_PERCENTILE) { + ElemNamePerc (center, subcenter, prodType, templat, cat, subcat, + lenTime, percentile, name, comment, unit, convert); + } else { + ElemNameNorm (center, subcenter, prodType, templat, cat, subcat, + lenTime, timeIncrType, genID, probType, lowerProb, + upperProb, name, comment, unit, convert); + } +} + +/***************************************************************************** + * ParseElemName2() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * Converts a prodType, template, category and subcategory quadruple to the + * ASCII string abreviation of that variable. + * For example: 0, 0, 0, 0, = "T" for temperature. + * + * ARGUMENTS + * prodType = The GRIB2, section 0 product type. (Input) + * templat = The GRIB2 section 4 template number. (Input) + * cat = The GRIB2 section 4 "General category of Product." (Input) + * subcat = The GRIB2 section 4 "Specific subcategory of Product". (Input) + * name = Where to store the result (assumed already allocated to at + * least 15 bytes) (Output) + * comment = Extra info about variable (assumed already allocated to at + * least 100 bytes) (Output) + * unit = What unit this variable is in. (assumed already allocated to at + * least 20 bytes) (Output) + * + * FILES/DATABASES: None + * + * RETURNS: char * + * Same as 'strcpy', ie it returns name. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 11/2002 AAT: Added MOIST_TOT_SNOW (and switched MOIST_SNOWAMT to + * SnowDepth) + * 12/2002 (TK,AC,TB,&MS): Code Review. + * 2/2003 AAT: moved from degrib.c to metaparse.c + * (Reason: primarily for Sect2 Parsing) + * (renamed from ElementName to ParseElemName) + * 4/2003 AAT: Added the comment as a return element.(see GRIB2 discipline) + * 6/2003 AAT: Added the unit as a return element. + * 6/2003 AAT: Added Wave Height. + * + * NOTES + * Similar to GRIB1_Table2LookUp... May want to take this and the unit + * stuff and combine them into a module. + ***************************************************************************** + */ +/* +static void ParseElemName2 (int prodType, int templat, int cat, int subcat, + char *name, char *comment, char *unit) +{ + if (prodType == 0) { + if (cat == CAT_TEMP) { * 0 * + switch (subcat) { + case TEMP_TEMP: * 0 * + strcpy (comment, "Temperature [K]"); + strcpy (name, "T"); + strcpy (unit, "[K]"); + return; + case TEMP_MAXT: * 4 * + strcpy (comment, "Maximum temperature [K]"); + strcpy (name, "MaxT"); + strcpy (unit, "[K]"); + return; + case TEMP_MINT: * 5 * + strcpy (comment, "Minimum temperature [K]"); + strcpy (name, "MinT"); + strcpy (unit, "[K]"); + return; + case TEMP_DEW_TEMP: * 6 * + strcpy (comment, "Dew point temperature [K]"); + strcpy (name, "Td"); + strcpy (unit, "[K]"); + return; + case TEMP_WINDCHILL: * 13 * + strcpy (comment, "Wind chill factor [K]"); + strcpy (name, "WCI"); + strcpy (unit, "[K]"); + return; + case TEMP_HEAT: * 12 * + strcpy (comment, "Heat index [K]"); + strcpy (name, "HeatIndex"); + strcpy (unit, "[K]"); + return; + } + } else if (cat == CAT_MOIST) { * 1 * + switch (subcat) { + case MOIST_REL_HUMID: * 1 * + strcpy (comment, "Relative Humidity [%]"); + strcpy (name, "RH"); + strcpy (unit, "[%]"); + return; + case MOIST_PRECIP_TOT: * 8 * + if (templat == GS4_PROBABIL_TIME) { * template number 9 implies prob. * + strcpy (comment, "Prob of 0.01 In. of Precip [%]"); + strcpy (name, "PoP12"); + strcpy (unit, "[%]"); + return; + } else { + strcpy (comment, "Total precipitation [kg/(m^2)]"); + strcpy (name, "QPF"); + strcpy (unit, "[kg/(m^2)]"); + return; + } + case MOIST_SNOWAMT: * 11 * + strcpy (comment, "Snow Depth [m]"); + strcpy (name, "SnowDepth"); + strcpy (unit, "[m]"); + return; + case MOIST_TOT_SNOW: * 29 * + strcpy (comment, "Total snowfall [m]"); + strcpy (name, "SnowAmt"); + strcpy (unit, "[m]"); + return; + case 192: * local use moisture. * + strcpy (comment, "Weather (local use moisture) [-]"); + strcpy (name, "Wx"); + strcpy (unit, "[-]"); + return; + } + } else if (cat == CAT_MOMENT) { * 2 * + switch (subcat) { + case MOMENT_WINDDIR: * 0 * + strcpy (comment, "Wind direction (from which blowing) " + "[deg true]"); + strcpy (name, "WindDir"); + strcpy (unit, "[deg true]"); + return; + case MOMENT_WINDSPD: * 1 * + strcpy (comment, "Wind speed [m/s]"); + strcpy (name, "WindSpd"); + strcpy (unit, "[m/s]"); + return; + } + } else if (cat == CAT_CLOUD) { * 6 * + switch (subcat) { + case CLOUD_COVER: * 1 * + strcpy (comment, "Total cloud cover [%]"); + strcpy (name, "Sky"); + strcpy (unit, "[%]"); + return; + } + } else if (cat == CAT_MOISTURE_PROB) { * 10 * + if (subcat == 8) { * grandfather'ed in. * + strcpy (comment, "Prob of 0.01 In. of Precip [%]"); + strcpy (name, "PoP12"); + strcpy (unit, "[%]"); + return; + } + } + } else if (prodType == 10) { + if (cat == OCEAN_CAT_WAVES) { * 0 * + if (subcat == OCEAN_WAVE_SIG_HT_WV) { * 5 * + strcpy (comment, "Significant height of wind waves [m]"); + strcpy (name, "WaveHeight"); + strcpy (unit, "[m]"); + return; + } + } + } + strcpy (name, ""); + strcpy (comment, "unknown"); + strcpy (unit, "[-]"); + return; +} +*/ + +/***************************************************************************** + * ComputeUnit() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Sets m, and b for equation y = mx + b, where x is in the unit + * specified by GRIB2, and y is the one specified by f_unit. The default + * is m = 1, b = 0. + * + * Currently: + * For f_unit = 1 (english) we return Fahrenheit, knots, and inches for + * temperature, wind speed, and amount of snow or rain. The original units + * are Kelvin, m/s, kg/m**2. + * For f_unit = 2 (metric) we return Celsius instead of Kelvin. + * + * ARGUMENTS + * convert = The enumerated type describing the type of conversion. (Input) + * origName = Original unit name (needed for log10 option) (Input) + * f_unit = What type of unit to return (see above) (Input). + * unitM = M in equation y = m x + b (Output) + * unitB = B in equation y = m x + b (Output) + * name = Where to store the result (assumed already allocated to at + * least 15 bytes) (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 if we set M and B, 1 if we used defaults. + * + * HISTORY + * 1/2004 Arthur Taylor (MDL/RSIS): Re-Created. + * + * NOTES + ***************************************************************************** + */ +int ComputeUnit (int convert, char *origName, sChar f_unit, double *unitM, + double *unitB, char *name) +{ + switch (convert) { + case UC_NONE: + break; + case UC_K2F: /* Convert from Kelvin to F or C. */ + if (f_unit == 1) { + strcpy (name, "[F]"); + *unitM = 9. / 5.; + /* 32 - (9/5 * 273.15) = 32 - 491.67 = -459.67. */ + *unitB = -459.67; + return 0; + } else if (f_unit == 2) { + strcpy (name, "[C]"); + *unitM = 1; + *unitB = -273.15; + return 0; + } + break; + case UC_InchWater: /* Convert from kg/(m^2) to inches water. */ + if (f_unit == 1) { + strcpy (name, "[inch]"); + /* + * kg/m**2 / density of water (1000 kg/m**3) + * 1/1000 m * 1/2.54 in/cm * 100 cm/m = 1/25.4 inches + */ + *unitM = 1. / 25.4; + *unitB = 0; + return 0; + } + break; + case UC_M2Feet: /* Convert from meters to feet. */ + if (f_unit == 1) { + /* 1 (m) * (100cm/m) * (inch/2.54cm) * (ft/12inch) = X (ft) */ + strcpy (name, "[feet]"); + *unitM = 100. / 30.48; + *unitB = 0; + return 0; + } + break; + case UC_M2Inch: /* Convert from meters to inches. */ + if (f_unit == 1) { + strcpy (name, "[inch]"); + *unitM = 100. / 2.54; /* inch / m */ + *unitB = 0; + return 0; + } + break; + /* NCEP goes with a convention of 1 nm = 1853.248 m. + * http://www.sizes.com/units/mile_USnautical.htm Shows that on + * 7/1/1954 US Department of Commerce switched to 1 nm = 1852 m + * (International standard.) */ + case UC_MS2Knots: /* Convert from m/s to knots. */ + if (f_unit == 1) { + strcpy (name, "[knots]"); + *unitM = 3600. / 1852.; /* knot / m s**-1 */ + *unitB = 0; + return 0; + } + break; + case UC_LOG10: /* convert from log10 (x) to x */ + if ((f_unit == 1) || (f_unit == 2)) { + origName[strlen (origName) - 2] = '\0'; + if (strlen (origName) > 21) + origName[21] = '\0'; + sprintf (name, "[%s]", origName + 7); + *unitM = -10; /* M = -10 => take 10^(x) */ + *unitB = 0; + return 0; + } + break; + } + /* Default case is for the unit in the GRIB2 document. */ + strcpy (name, "[GRIB2 unit]"); + *unitM = 1; + *unitB = 0; + return 1; +} + +/***************************************************************************** + * ComputeUnit2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Sets m, and b for equation y = mx + b, where x is in the unit + * specified by GRIB2, and y is the one specified by f_unit. The default + * is m = 1, b = 0. + * + * Currently: + * For f_unit = 1 (english) we return Fahrenheit, knots, and inches for + * temperature, wind speed, and amount of snow or rain. The original units + * are Kelvin, m/s, kg/m**2. + * For f_unit = 2 (metric) we return Celsius instead of Kelvin. + * + * ARGUMENTS + * prodType = The GRIB2, section 0 product type. (Input) + * templat = The GRIB2 section 4 template number. (Input) + * cat = The GRIB2 section 4 "General category of Product." (Input) + * subcat = The GRIB2 section 4 "Specific subcategory of Product". (Input) + * f_unit = What type of unit to return (see above) (Input). + * unitM = M in equation y = m x + b (Output) + * unitB = B in equation y = m x + b (Output) + * name = Where to store the result (assumed already allocated to at + * least 15 bytes) (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 if we set M and B, 1 if we used defaults. + * + * HISTORY + * 11/2002 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +/* +static int ComputeUnit2 (int prodType, int templat, int cat, int subcat, + sChar f_unit, double *unitM, double *unitB, + char *name) +{ + if (prodType == 0) { + switch (cat) { + case CAT_TEMP: + * subcat 8 is K/m, 10, 11 is W/m**2 * + if ((subcat < 16) && (subcat != 8) && + (subcat != 10) && (subcat != 11)) { + if (f_unit == 1) { + strcpy (name, "[F]"); + *unitM = 9. / 5.; + * 32 - (9/5 * 273.15) = 32 - 491.67 = -459.67. * + *unitB = -459.67; + return 0; + } else if (f_unit == 2) { + strcpy (name, "[C]"); + *unitM = 1; + *unitB = -273.15; + return 0; + } + } + break; + case CAT_MOIST: + if (subcat == MOIST_PRECIP_TOT) { + if (templat != 9) { * template number != 9 implies QPF. * + if (f_unit == 1) { + strcpy (name, "[inch]"); + * + * kg/m**2 / density of water (1000 kg/m**3) + * 1/1000 m * 1/2.54 in/cm * 100 cm/m = 1/25.4 inches + * + *unitM = 1. / 25.4; + *unitB = 0; + return 0; + } + } + } + if ((subcat == MOIST_SNOWAMT) || (subcat == MOIST_TOT_SNOW)) { + if (f_unit == 1) { + strcpy (name, "[inch]"); + *unitM = 100. / 2.54; * inch / m * + *unitB = 0; + return 0; + } + } + break; + case CAT_MOMENT: + if (subcat == MOMENT_WINDSPD) { + if (f_unit == 1) { + strcpy (name, "[knots]"); + *unitM = 3600. / 1852.; * knot / m s**-1 * + *unitB = 0; + return 0; + } + } + break; + } + } else if (prodType == 10) { + if (cat == OCEAN_CAT_WAVES) { * 0 * + if (subcat == OCEAN_WAVE_SIG_HT_WV) { * 5 * + if (f_unit == 1) { + * 1 (m) * (100cm/m) * (inch/2.54cm) * (ft/12inch) = X (ft) * + strcpy (name, "[feet]"); + *unitM = 100. / 30.48; + *unitB = 0; + return 0; + } + } + } + } + * Default case is for the unit in the GRIB2 document. * + strcpy (name, "[GRIB2 unit]"); + *unitM = 1; + *unitB = 0; + return 1; +} +*/ + +/* GRIB2 Code Table 4.5 */ +/* *INDENT-OFF* */ +GRIB2SurfTable Surface[] = { + /* 0 */ {"RESERVED", "Reserved", "-"}, + /* 1 */ {"SFC", "Ground or water surface", "-"}, + /* 2 */ {"CBL", "Cloud base level", "-"}, + /* 3 */ {"CTL", "Level of cloud tops", "-"}, + /* 4 */ {"0DEG", "Level of 0 degree C isotherm", "-"}, + /* 5 */ {"ADCL", "Level of adiabatic condensation lifted from the surface", "-"}, + /* 6 */ {"MWSL", "Maximum wind level", "-"}, + /* 7 */ {"TRO", "Tropopause", "-"}, + /* 8 */ {"NTAT", "Nominal top of atmosphere", "-"}, + /* 9 */ {"SEAB", "Sea bottom", "-"}, + /* 10: 10-19 */ {"RESERVED", "Reserved", "-"}, + /* 11: 20 */ {"TMPL", "Isothermal level", "K"}, + /* 12: 21-99 */ {"RESERVED", "Reserved", "-"}, + /* 13: 100 */ {"ISBL", "Isobaric surface", "Pa"}, + /* 14: 101 */ {"MSL", "Mean sea level", "-"}, + /* 15: 102 */ {"GPML", "Specific altitude above mean sea level", "m"}, + /* 16: 103 */ {"HTGL", "Specified height level above ground", "m"}, + /* 17: 104 */ {"SIGL", "Sigma level", "'sigma' value"}, + /* 18: 105 */ {"HYBL", "Hybrid level", "-"}, + /* 19: 106 */ {"DBLL", "Depth below land surface", "m"}, + /* 20: 107 */ {"THEL", "Isentropic (theta) level", "K"}, + /* 21: 108 */ {"SPDL", "Level at specified pressure difference from ground to level", "Pa"}, + /* 22: 109 */ {"PVL", "Potential vorticity surface", "(K m^2)/(kg s)"}, + /* 23: 110 */ {"RESERVED", "Reserved", "-"}, + /* 24: 111 */ {"EtaL", "Eta* level", "-"}, + /* 25: 112-116 */ {"RESERVED", "Reserved", "-"}, + /* 26: 117 */ {"unknown", "Mixed layer depth", "m"}, /* unknown abbrev */ + /* 27: 118-159 */ {"RESERVED", "Reserved", "-"}, + /* 28: 160 */ {"DBSL", "Depth below sea level", "m"}, + /* 29: 161-191 */ {"RESERVED", "Reserved", "-"}, + /* 30: 192-254 */ {"RESERVED", "Reserved Local use", "-"}, + /* 31: 255 */ {"MISSING", "Missing", "-"}, +}; + +typedef struct { + int index; + GRIB2SurfTable surface; +} GRIB2LocalSurface; + +/* based on http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_table4-5.shtml + * updated last on 3/14/2006 */ +GRIB2LocalSurface NCEP_Surface[] = { + {200, {"EATM", "Entire atmosphere (considerd as a single layer)", "-"}}, + {201, {"EOCN", "Entire ocean (considered as a single layer)", "-"}}, + {204, {"HTFL", "Highest tropospheric freezing level", "-"}}, + {206, {"GCBL", "Grid scale cloud bottom level", "-"}}, + {207, {"GCTL", "Grid scale cloud top level", "-"}}, + {209, {"BCBL", "Boundary layer cloud bottom level", "-"}}, + {210, {"BCTL", "Boundary layer cloud top level", "-"}}, + {211, {"BCY", "Boundary layer cloud level", "-"}}, + {212, {"LCBL", "Low cloud bottom level", "-"}}, + {213, {"LCTL", "Low cloud top level", "-"}}, + {214, {"LCY", "Low cloud level", "-"}}, + {215, {"CEIL", "Cloud ceiling", "-"}}, + {222, {"MCBL", "Middle cloud bottom level", "-"}}, + {223, {"MCTL", "Middle cloud top level", "-"}}, + {224, {"MCY", "Middle cloud level", "-"}}, + {232, {"HCBL", "High cloud bottom level", "-"}}, + {233, {"HCTL", "High cloud top level", "-"}}, + {234, {"HCY", "High cloud level", "-"}}, + {235, {"OITL", "Ocean Isotherm Level (1/10 deg C)", "-"}}, + {236, {"OLYR", "Layer between two depths below ocean surface", "-"}}, + {237, {"OBML", "Bottom of Ocean Mixed Layer (m)", "-"}}, + {238, {"OBIL", "Bottom of Ocean Isothermal Layer (m)", "-"}}, + {242, {"CCBL", "Convective cloud bottom level", "-"}}, + {243, {"CCTL", "Convective cloud top level", "-"}}, + {244, {"CCY", "Convective cloud level", "-"}}, + {245, {"LLTW", "Lowest level of the wet bulb zero", "-"}}, + {246, {"MTHE", "Maximum equivalent potential temperature level", "-"}}, + {247, {"EHLT", "Equilibrium level", "-"}}, + {248, {"SCBL", "Shallow convective cloud bottom level", "-"}}, + {249, {"SCTL", "Shallow convective cloud top level", "-"}}, + {251, {"DCBL", "Deep convective cloud bottom level", "-"}}, + {252, {"DCTL", "Deep convective cloud top level", "-"}}, + {253, {"LBLSW", "Lowest bottom level of supercooled liquid water layer", "-"}}, + {254, {"HTLSW", "Highest top level of supercooled liquid water layer", "-"}}, +}; +/* *INDENT-ON* */ + +/***************************************************************************** + * Table45Index() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To figure out the entry in the "Surface" table (used for Code Table 4.5) + * + * ARGUMENTS + * i = The original index to look up. (Input) + * f_reserved = If the index is a "reserved" index (Output) + * + * FILES/DATABASES: None + * + * RETURNS: GRIB2SurfTable + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2004 Arthur Taylor (RSIS): Modified to return SurfaceTable. + * + * NOTES + ***************************************************************************** + */ +GRIB2SurfTable Table45Index (int i, + int *f_reserved, + uShort2 center, + CPL_UNUSED uShort2 subcenter) +{ + size_t j; + + *f_reserved = 1; + if ((i > 255) || (i < 0)) { +#ifdef DEBUG + printf ("Surface index is out of 0..255 range?\n"); +#endif + return Surface[0]; + } + if (i == 255) + return Surface[31]; + if (i > 191) { + if (center == 7) { + for (j = 0; j < sizeof (NCEP_Surface) / sizeof (NCEP_Surface[0]); + j++) { + if (i == NCEP_Surface[j].index) { + *f_reserved = 0; + return (NCEP_Surface[j].surface); + } + } + } + return Surface[30]; + } + if (i > 160) + return Surface[29]; + if (i == 160) { + *f_reserved = 0; + return Surface[28]; + } + if (i > 117) + return Surface[27]; + if (i == 117) { + *f_reserved = 0; + return Surface[26]; + } + if (i > 111) + return Surface[25]; + if (i == 111) { + *f_reserved = 0; + return Surface[i - 87]; + } + if (i == 110) + return Surface[i - 87]; + if (i > 99) { + *f_reserved = 0; + return Surface[i - 87]; + } + if (i > 20) + return Surface[12]; + if (i == 20) { + *f_reserved = 0; + return Surface[11]; + } + if (i > 9) + return Surface[10]; + if (i > 0) { + *f_reserved = 0; + return Surface[i]; + } + return Surface[0]; +} + +void ParseLevelName (unsigned short int center, unsigned short int subcenter, + uChar surfType, double value, sChar f_sndValue, + double sndValue, char **shortLevelName, + char **longLevelName) +{ + int f_reserved; + char valBuff[512]; + char sndBuff[512]; + GRIB2SurfTable surf = Table45Index (surfType, &f_reserved, center, + subcenter); + + /* Check if index is defined... 191 is undefined. */ + free (*shortLevelName); + *shortLevelName = NULL; + free (*longLevelName); + *longLevelName = NULL; + sprintf (valBuff, "%f", value); + strTrimRight (valBuff, '0'); + if (valBuff[strlen (valBuff) - 1] == '.') { + valBuff[strlen (valBuff) - 1] = '\0'; + } + if (f_sndValue) { + sprintf (sndBuff, "%f", sndValue); + strTrimRight (sndBuff, '0'); + if (sndBuff[strlen (sndBuff) - 1] == '.') { + sndBuff[strlen (sndBuff) - 1] = '\0'; + } + if (f_reserved) { + reallocSprintf (shortLevelName, "%s-%s-%s(%d)", valBuff, sndBuff, + surf.name, surfType); + reallocSprintf (longLevelName, "%s-%s[%s] %s(%d) (%s)", valBuff, + sndBuff, surf.unit, surf.name, surfType, + surf.comment); + } else { + reallocSprintf (shortLevelName, "%s-%s-%s", valBuff, sndBuff, + surf.name); + reallocSprintf (longLevelName, "%s-%s[%s] %s=\"%s\"", valBuff, + sndBuff, surf.unit, surf.name, surf.comment); + } + } else { + if (f_reserved) { + reallocSprintf (shortLevelName, "%s-%s(%d)", valBuff, surf.name, + surfType); + reallocSprintf (longLevelName, "%s[%s] %s(%d) (%s)", valBuff, + surf.unit, surf.name, surfType, surf.comment); + } else { + reallocSprintf (shortLevelName, "%s-%s", valBuff, surf.name); + reallocSprintf (longLevelName, "%s[%s] %s=\"%s\"", valBuff, + surf.unit, surf.name, surf.comment); + } + } +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/metaname.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/metaname.h new file mode 100644 index 000000000..f34176514 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/metaname.h @@ -0,0 +1,55 @@ +#ifndef METANAME_H +#define METANAME_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "type.h" +#include "meta.h" + +const char *centerLookup(unsigned short int center); + +const char *subCenterLookup(unsigned short int center, + unsigned short int subcenter); + +const char *processLookup(unsigned short int center, unsigned char process); + +void ParseElemName (unsigned short int center, unsigned short int subcenter, + int prodType, int templat, int cat, int subcat, + sInt4 lenTime, uChar timeIncrType, uChar genID, + uChar probType, double lowerProb, double upperProb, + char **name, char **comment, char **unit, int *convert, + sChar percentile); + +int ComputeUnit (int convert, char * origName, sChar f_unit, double *unitM, + double *unitB, char *name); +/* +int ComputeUnit (int prodType, int templat, int cat, int subcat, sChar f_unit, + double *unitM, double *unitB, char *name); +*/ +typedef struct { + const char *name, *comment, *unit; +} GRIB2SurfTable; + +GRIB2SurfTable Table45Index (int i, int *f_reserved, uShort2 center, + uShort2 subcenter); +/* +GRIB2SurfTable Table45Index (int i, int *f_reserved); +int Table45Index (int i); +*/ + +int IsData_NDFD (unsigned short int center, unsigned short int subcenter); + +int IsData_MOS (unsigned short int center, unsigned short int subcenter); + +void ParseLevelName (unsigned short int center, unsigned short int subcenter, + uChar surfType, double value, sChar f_sndValue, + double sndValue, char **shortLevelName, + char **longLevelName); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* METANAME_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/metaparse.cpp b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/metaparse.cpp new file mode 100644 index 000000000..4b3adeeb6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/metaparse.cpp @@ -0,0 +1,2646 @@ +/***************************************************************************** + * metaparse.c + * + * DESCRIPTION + * This file contains the code necessary to initialize the meta data + * structure, and parse the meta data that comes out of the GRIB2 decoder. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + * 1) Need to add support for GS3_ORTHOGRAPHIC = 90, + * GS3_EQUATOR_EQUIDIST = 110, GS3_AZIMUTH_RANGE = 120 + * 2) Need to add support for GS4_RADAR = 20 + ***************************************************************************** + */ +#include +#include +#include +#include +#include "clock.h" +#include "meta.h" +#include "metaname.h" +#include "myassert.h" +#include "myerror.h" +#include "scan.h" +#include "weather.h" +#include "memendian.h" +#include "myutil.h" + +/***************************************************************************** + * MetaInit() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To initialize a grib_metaData structure. + * + * ARGUMENTS + * meta = The structure to fill. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +void MetaInit (grib_MetaData *meta) +{ + meta->element = NULL; + meta->comment = NULL; + meta->unitName = NULL; + meta->convert = 0; + meta->shortFstLevel = NULL; + meta->longFstLevel = NULL; + meta->pds2.sect2.ptrType = GS2_NONE; + + meta->pds2.sect2.wx.data = NULL; + meta->pds2.sect2.wx.dataLen = 0; + meta->pds2.sect2.wx.maxLen = 0; + meta->pds2.sect2.wx.ugly = NULL; + meta->pds2.sect2.unknown.data = NULL; + meta->pds2.sect2.unknown.dataLen = 0; + + meta->pds2.sect4.numInterval = 0; + meta->pds2.sect4.Interval = NULL; + meta->pds2.sect4.numBands = 0; + meta->pds2.sect4.bands = NULL; + return; +} + +/***************************************************************************** + * MetaSect2Free() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To free the section 2 data in the grib_metaData structure. + * + * ARGUMENTS + * meta = The structure to free. (Input/Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 2/2003 Arthur Taylor (MDL/RSIS): Created. + * 3/2003 AAT: Cleaned up declaration of variable: WxType. + * + * NOTES + ***************************************************************************** + */ +void MetaSect2Free (grib_MetaData *meta) +{ + size_t i; /* Counter for use when freeing Wx data. */ + + for (i = 0; i < meta->pds2.sect2.wx.dataLen; i++) { + free (meta->pds2.sect2.wx.data[i]); + FreeUglyString (&(meta->pds2.sect2.wx.ugly[i])); + } + free (meta->pds2.sect2.wx.ugly); + meta->pds2.sect2.wx.ugly = NULL; + free (meta->pds2.sect2.wx.data); + meta->pds2.sect2.wx.data = NULL; + meta->pds2.sect2.wx.dataLen = 0; + meta->pds2.sect2.wx.maxLen = 0; + meta->pds2.sect2.ptrType = GS2_NONE; + + free (meta->pds2.sect2.wx.data); + meta->pds2.sect2.unknown.data = NULL; + meta->pds2.sect2.unknown.dataLen = 0; +} + +/***************************************************************************** + * MetaFree() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To free a grib_metaData structure. + * + * ARGUMENTS + * meta = The structure to free. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +void MetaFree (grib_MetaData *meta) +{ + free (meta->pds2.sect4.bands); + meta->pds2.sect4.bands = NULL; + meta->pds2.sect4.numBands = 0; + free (meta->pds2.sect4.Interval); + meta->pds2.sect4.Interval = NULL; + meta->pds2.sect4.numInterval = 0; + MetaSect2Free (meta); + free (meta->unitName); + meta->unitName = NULL; + meta->convert = 0; + free (meta->comment); + meta->comment = NULL; + free (meta->element); + meta->element = NULL; + free (meta->shortFstLevel); + meta->shortFstLevel = NULL; + free (meta->longFstLevel); + meta->longFstLevel = NULL; +} + +/***************************************************************************** + * ParseTime() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To parse the time data from the grib2 integer array to a time_t in + * UTC seconds from the epoch. + * + * ARGUMENTS + * AnsTime = The time_t value to fill with the resulting time. (Output) + * year = The year to parse. (Input) + * mon = The month to parse. (Input) + * day = The day to parse. (Input) + * hour = The hour to parse. (Input) + * min = The minute to parse. (Input) + * sec = The second to parse. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = gribLen is too small. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 4/2003 AAT: Modified to use year/mon/day/hour/min/sec instead of an + * integer array. + * 2/2004 AAT: Added error checks (because of corrupt GRIB1 files) + * + * NOTES + * 1) Couldn't use the default time_zone variable (concern over portability + * issues), so we print the hours, and compare them to the hours we had + * intended. Then subtract the difference from the AnsTime. + * 2) Need error check for times outside of 1902..2037. + ***************************************************************************** + */ +int ParseTime (double *AnsTime, int year, uChar mon, uChar day, uChar hour, + uChar min, uChar sec) +{ + /* struct tm time; *//* A temporary variable to put the time info into. */ + /* char buffer[10]; *//* Used when printing the AnsTime's Hr. */ + /* int timeZone; *//* The adjustment in Hr needed to get the right UTC * time. */ + + if ((year < 1900) || (year > 2100)) { + errSprintf ("ParseTime:: year %d is invalid\n", year); + return -1; + } + /* sec is allowed to be 61 for leap seconds. */ + if ((mon > 12) || (day == 0) || (day > 31) || (hour > 24) || (min > 60) || + (sec > 61)) { + errSprintf ("ParseTime:: Problems with %d/%d %d:%d:%d\n", mon, day, + hour, min, sec); + return -1; + } + Clock_ScanDate (AnsTime, year, mon, day); + *AnsTime += hour * 3600. + min * 60. + sec; +/* *AnsTime -= Clock_GetTimeZone() * 3600;*/ + +/* + memset (&time, 0, sizeof (struct tm)); + time.tm_year = year - 1900; + time.tm_mon = mon - 1; + time.tm_mday = day; + time.tm_hour = hour; + time.tm_min = min; + time.tm_sec = sec; + printf ("%ld\n", mktime (&time)); + *AnsTime = mktime (&time) - (Clock_GetTimeZone () * 3600); +*/ + /* Cheap method of getting global time_zone variable. */ +/* + strftime (buffer, 10, "%H", gmtime (AnsTime)); + timeZone = atoi (buffer) - hour; + if (timeZone < 0) { + timeZone += 24; + } + *AnsTime = *AnsTime - (timeZone * 3600); +*/ + return 0; +} + +/***************************************************************************** + * ParseSect0() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To verify and parse section 0 data. + * + * ARGUMENTS + * is0 = The unpacked section 0 array. (Input) + * ns0 = The size of section 0. (Input) + * grib_len = The length of the entire grib message. (Input) + * meta = The structure to fill. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = ns0 is too small. + * -2 = unexpected values in is0. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * 1) 1196575042L == ASCII representation of "GRIB" + ***************************************************************************** + */ +static int ParseSect0 (sInt4 *is0, sInt4 ns0, sInt4 grib_len, + grib_MetaData *meta) +{ + if (ns0 < 9) { + return -1; + } + if ((is0[0] != 1196575042L) || (is0[7] != 2) || (is0[8] != grib_len)) { + errSprintf ("ERROR IS0 has unexpected values: %ld %ld %ld\n", + is0[0], is0[7], is0[8]); + errSprintf ("Should be %ld %d %ld\n", 1196575042L, 2, grib_len); + return -2; + } + meta->pds2.prodType = (uChar) is0[6]; + return 0; +} + +/***************************************************************************** + * ParseSect1() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To verify and parse section 1 data. + * + * ARGUMENTS + * is1 = The unpacked section 1 array. (Input) + * ns1 = The size of section 1. (Input) + * meta = The structure to fill. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = ns1 is too small. + * -2 = unexpected values in is1. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +static int ParseSect1 (sInt4 *is1, sInt4 ns1, grib_MetaData *meta) +{ + if (ns1 < 21) { + return -1; + } + if (is1[4] != 1) { + errSprintf ("ERROR IS1 not labeled correctly. %ld\n", is1[4]); + return -2; + } + meta->center = (unsigned short int) is1[5]; + meta->subcenter = (unsigned short int) is1[7]; + meta->pds2.mstrVersion = (uChar) is1[9]; + meta->pds2.lclVersion = (uChar) is1[10]; + if (((meta->pds2.mstrVersion < 1) || (meta->pds2.mstrVersion > 3)) || + (meta->pds2.lclVersion > 1)) { + if (meta->pds2.mstrVersion == 0) { + printf ("Warning: Master table version == 0, was experimental\n" + "I don't have a copy, and don't know where to get one\n" + "Use meta data at your own risk.\n"); + } else { + errSprintf ("Master table version supported (1,2,3) yours is %d... " + "Local table version supported (0,1) yours is %d...\n", + meta->pds2.mstrVersion, meta->pds2.lclVersion); + return -2; + } + } + meta->pds2.sigTime = (uChar) is1[11]; + if (ParseTime (&(meta->pds2.refTime), is1[12], is1[14], is1[15], is1[16], + is1[17], is1[18]) != 0) { + preErrSprintf ("Error in call to ParseTime from ParseSect1 (GRIB2)"); + return -2; + } + meta->pds2.operStatus = (uChar) is1[19]; + meta->pds2.dataType = (uChar) is1[20]; + return 0; +} + +/***************************************************************************** + * ParseSect2_Wx() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To verify and parse section 2 data when we know the variable is of type + * Wx (Weather). + * + * ARGUMENTS + * rdat = The float data in section 2. (Input) + * nrdat = Length of rdat. (Input) + * idat = The integer data in section 2. (Input) + * nidat = Length of idat. (Input) + * Wx = The weather structure to fill. (Output) + * simpVer = The version of the simple weather code to use when parsing the + * WxString. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = nrdat or nidat is too small. + * -2 = unexpected values in rdat. + * + * HISTORY + * 2/2003 Arthur Taylor (MDL/RSIS): Created. + * 5/2003 AAT: Stopped messing around with the way buffer and data[i] + * were allocated. It was confusing the free routine. + * 5/2003 AAT: Added maxLen to Wx structure. + * 6/2003 AAT: Revisited after Matt (matt@wunderground.com) informed me of + * memory problems. + * 1) I had a memory leak caused by a buffer+= buffLen + * 2) buffLen could have increased out of bounds of buffer. + * 8/2003 AAT: Found an invalid "assertion" when dealing with non-NULL + * terminated weather groups. + * + * NOTES + * 1) May want to rewrite so that we don't need 'meta->sect2NumGroups' + ***************************************************************************** + */ +static int ParseSect2_Wx (float *rdat, sInt4 nrdat, sInt4 *idat, + uInt4 nidat, sect2_WxType *Wx, int simpVer) +{ + size_t loc; /* Where we currently are in idat. */ + size_t groupLen; /* Length of current group in idat. */ + size_t j; /* Counter over the length of the current group. */ + char *buffer; /* Used to store the current "ugly" string. */ + int buffLen; /* Length of current "ugly" string. */ + int len; /* length of current english phrases during creation + * of the maxEng[] data. */ + int i; /* assists in traversing the maxEng[] array. */ + + if (nrdat < 1) { + return -1; + } + + if (rdat[0] != 0) { + errSprintf ("ERROR: Expected rdat to be empty when dealing with " + "section 2 Weather data\n"); + return -2; + } + Wx->dataLen = 0; + Wx->data = NULL; + Wx->maxLen = 0; + for (i = 0; i < NUM_UGLY_WORD; i++) { + Wx->maxEng[i] = 0; + } + + loc = 0; + if (nidat <= loc) { + errSprintf ("ERROR: Ran out of idat data\n"); + return -1; + } + groupLen = idat[loc++]; + + loc++; /* Skip the decimal scale factor data. */ + /* Note: This also assures that buffLen stays <= nidat. */ + if (loc + groupLen >= nidat) { + errSprintf ("ERROR: Ran out of idat data\n"); + return -1; + } + + buffLen = 0; + buffer = (char *) malloc ((nidat + 1) * sizeof (char)); + while (groupLen > 0) { + for (j = 0; j < groupLen; j++) { + buffer[buffLen] = (char) idat[loc]; + buffLen++; + loc++; + if (buffer[buffLen - 1] == '\0') { + Wx->dataLen++; + Wx->data = (char **) realloc ((void *) Wx->data, + Wx->dataLen * sizeof (char *)); + /* This is done after the realloc, just to make sure we have + * enough memory allocated. */ + /* Assert: buffLen is 1 more than strlen(buffer). */ + Wx->data[Wx->dataLen - 1] = (char *) + malloc (buffLen * sizeof (char)); + strcpy (Wx->data[Wx->dataLen - 1], buffer); + if (Wx->maxLen < buffLen) { + Wx->maxLen = buffLen; + } + buffLen = 0; + } + } + if (loc >= nidat) { + groupLen = 0; + } else { + groupLen = idat[loc]; + loc++; + if (groupLen != 0) { + loc++; /* Skip the decimal scale factor data. */ + /* Note: This also assures that buffLen stays <= nidat. */ + if (loc + groupLen >= nidat) { + errSprintf ("ERROR: Ran out of idat data\n"); + free (buffer); + return -1; + } + } + } + } + if (buffLen != 0) { + buffer[buffLen] = '\0'; + Wx->dataLen++; + Wx->data = (char **) realloc ((void *) Wx->data, + Wx->dataLen * sizeof (char *)); + /* Assert: buffLen is 1 more than strlen(buffer). -- FALSE -- */ + buffLen = strlen (buffer) + 1; + + Wx->data[Wx->dataLen - 1] = (char *) malloc (buffLen * sizeof (char)); + if (Wx->maxLen < buffLen) { + Wx->maxLen = buffLen; + } + strcpy (Wx->data[Wx->dataLen - 1], buffer); + } + free (buffer); + Wx->ugly = (UglyStringType *) malloc (Wx->dataLen * + sizeof (UglyStringType)); + for (j = 0; j < Wx->dataLen; j++) { + ParseUglyString (&(Wx->ugly[j]), Wx->data[j], simpVer); + } + /* We want to know how many bytes we need for each english phrase column, + * so we walk through each column calculating that value. */ + for (i = 0; i < NUM_UGLY_WORD; i++) { + /* Assert: Already initialized Wx->maxEng[i]. */ + for (j = 0; j < Wx->dataLen; j++) { + if (Wx->ugly[j].english[i] != NULL) { + len = strlen (Wx->ugly[j].english[i]); + if (len > Wx->maxEng[i]) { + Wx->maxEng[i] = len; + } + } + } + } + return 0; +} + +/***************************************************************************** + * ParseSect2_Unknown() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To verify and parse section 2 data when we don't know anything more + * about the data. + * + * ARGUMENTS + * rdat = The float data in section 2. (Input) + * nrdat = Length of rdat. (Input) + * idat = The integer data in section 2. (Input) + * nidat = Length of idat. (Input) + * meta = The structure to fill. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = nrdat or nidat is too small. + * -2 = unexpected values in rdat. + * + * HISTORY + * 2/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * In the extremely improbable case that there is both idat data and rdat + * data, we process the rdat data first. + ***************************************************************************** + */ +static int ParseSect2_Unknown (float *rdat, sInt4 nrdat, sInt4 *idat, + sInt4 nidat, grib_MetaData *meta) +{ + /* Used for easier access to answer. */ + int loc; /* Where we currently are in idat. */ + int ansLoc; /* Where we are in the answer data structure. */ + sInt4 groupLen; /* Length of current group in idat. */ + int j; /* Counter over the length of the current group. */ + + meta->pds2.sect2.unknown.dataLen = 0; + meta->pds2.sect2.unknown.data = NULL; + ansLoc = 0; + + /* Work with rdat data. */ + loc = 0; + if (nrdat <= loc) { + errSprintf ("ERROR: Ran out of rdat data\n"); + return -1; + } + groupLen = (sInt4) rdat[loc++]; + loc++; /* Skip the decimal scale factor data. */ + if (nrdat <= loc + groupLen) { + errSprintf ("ERROR: Ran out of rdat data\n"); + return -1; + } + while (groupLen > 0) { + meta->pds2.sect2.unknown.dataLen += groupLen; + meta->pds2.sect2.unknown.data = (double *) + realloc ((void *) meta->pds2.sect2.unknown.data, + meta->pds2.sect2.unknown.dataLen * sizeof (double)); + for (j = 0; j < groupLen; j++) { + meta->pds2.sect2.unknown.data[ansLoc++] = rdat[loc++]; + } + if (nrdat <= loc) { + groupLen = 0; + } else { + groupLen = (sInt4) rdat[loc++]; + if (groupLen != 0) { + loc++; /* Skip the decimal scale factor data. */ + if (nrdat <= loc + groupLen) { + errSprintf ("ERROR: Ran out of rdat data\n"); + return -1; + } + } + } + } + + /* Work with idat data. */ + loc = 0; + if (nidat <= loc) { + errSprintf ("ERROR: Ran out of idat data\n"); + return -1; + } + groupLen = idat[loc++]; + loc++; /* Skip the decimal scale factor data. */ + if (nidat <= loc + groupLen) { + errSprintf ("ERROR: Ran out of idat data\n"); + return -1; + } + while (groupLen > 0) { + meta->pds2.sect2.unknown.dataLen += groupLen; + meta->pds2.sect2.unknown.data = (double *) + realloc ((void *) meta->pds2.sect2.unknown.data, + meta->pds2.sect2.unknown.dataLen * sizeof (double)); + for (j = 0; j < groupLen; j++) { + meta->pds2.sect2.unknown.data[ansLoc++] = idat[loc++]; + } + if (nidat <= loc) { + groupLen = 0; + } else { + groupLen = idat[loc++]; + if (groupLen != 0) { + loc++; /* Skip the decimal scale factor data. */ + if (nidat <= loc + groupLen) { + errSprintf ("ERROR: Ran out of idat data\n"); + return -1; + } + } + } + } + return 0; +} + +/***************************************************************************** + * ParseSect3() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To verify and parse section 3 data. + * + * ARGUMENTS + * is3 = The unpacked section 3 array. (Input) + * ns3 = The size of section 3. (Input) + * meta = The structure to fill. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = ns3 is too small. + * -2 = unexpected values in is3. + * -3 = un-supported map Projection. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 9/2003 AAT: Adjusted Radius Earth case 1,6 to be based on: + * Y * 10^D = R + * Where Y = original value, D is scale factor, R is scale value. + * 1/2004 AAT: Adjusted Radius Earth case 6 to always be 6371.229 km + * + * NOTES + * Need to add support for GS3_ORTHOGRAPHIC = 90, + * GS3_EQUATOR_EQUIDIST = 110, GS3_AZIMUTH_RANGE = 120 + ***************************************************************************** + */ +static int ParseSect3 (sInt4 *is3, sInt4 ns3, grib_MetaData *meta) +{ + double unit; /* Used to convert from stored value to degrees + * lat/lon. See GRIB2 Regulation 92.1.6 */ + sInt4 angle; /* For Lat/Lon, 92.1.6 may not hold, in which case, + * angle != 0, and unit = angle/subdivision. */ + sInt4 subdivision; /* see angle explaination. */ + + if (ns3 < 14) { + return -1; + } + if (is3[4] != 3) { + errSprintf ("ERROR IS3 not labeled correctly. %ld\n", is3[4]); + return -2; + } + if (is3[5] != 0) { + errSprintf ("Can not handle 'Source of Grid Definition' = %ld\n", + is3[5]); + errSprintf ("Can only handle grids defined in Code table 3.1\n"); + // return -3; + } + meta->gds.numPts = is3[6]; + if ((is3[10] != 0) || (is3[11] != 0)) { + errSprintf ("Un-supported Map Projection.\n All Supported " + "projections have 0 bytes following the template.\n"); + // return -3; + } + meta->gds.projType = (uChar) is3[12]; + + // Don't refuse to convert the GRIB file if only the projection is unknown to us + /* + if ((is3[12] != GS3_LATLON) && (is3[12] != GS3_MERCATOR) && + (is3[12] != GS3_POLAR) && (is3[12] != GS3_LAMBERT)) { + errSprintf ("Un-supported Map Projection %ld\n", is3[12]); + return -3; + } + */ + + /* + * Handle variables common to the supported templates. + */ + if (ns3 < 38) { + return -1; + } + /* Assert: is3[14] is the shape of the earth. */ + switch (is3[14]) { + case 0: + meta->gds.f_sphere = 1; + meta->gds.majEarth = 6367.47; + meta->gds.minEarth = 6367.47; + break; + case 6: + meta->gds.f_sphere = 1; + meta->gds.majEarth = 6371.229; + meta->gds.minEarth = 6371.229; + break; + case 1: + meta->gds.f_sphere = 1; + /* Following assumes scale factor and scale value refer to + * scientific notation. */ + /* Incorrect Assumption (9/8/2003): scale factor / value are based + * on: Y * 10^D = R, where Y = original value, D = scale factor, ___ + * R = scale value. */ + + if ((is3[16] != GRIB2MISSING_s4) && (is3[15] != GRIB2MISSING_s1)) { + /* Assumes data is given in m (not km). */ + meta->gds.majEarth = is3[16] / (pow (10.0, is3[15]) * 1000.); + meta->gds.minEarth = meta->gds.majEarth; + } else { + errSprintf ("Missing info on radius of Earth.\n"); + return -2; + } + /* Check if our m assumption was valid. If it wasn't, they give us + * 6371 km, which we convert to 6.371 < 6.4 */ + if (meta->gds.majEarth < 6.4) { + meta->gds.majEarth = meta->gds.majEarth * 1000.; + meta->gds.minEarth = meta->gds.minEarth * 1000.; + } + break; + case 2: + meta->gds.f_sphere = 0; + meta->gds.majEarth = 6378.160; + meta->gds.minEarth = 6356.775; + break; + case 4: + meta->gds.f_sphere = 0; + meta->gds.majEarth = 6378.137; + meta->gds.minEarth = 6356.752314; + break; + case 5: + meta->gds.f_sphere = 0; + meta->gds.majEarth = 6378.137; + meta->gds.minEarth = 6356.7523; + break; + case 3: + meta->gds.f_sphere = 0; + /* Following assumes scale factor and scale value refer to + * scientific notation. */ + /* Incorrect Assumption (9/8/2003): scale factor / value are based + * on: Y * 10^D = R, where Y = original value, D = scale factor, ___ + * R = scale value. */ + if ((is3[21] != GRIB2MISSING_s4) && (is3[20] != GRIB2MISSING_s1) && + (is3[26] != GRIB2MISSING_s4) && (is3[25] != GRIB2MISSING_s1)) { + /* Assumes data is given in km (not m). */ + meta->gds.majEarth = is3[21] / (pow (10.0, is3[20])); + meta->gds.minEarth = is3[26] / (pow (10.0, is3[25])); + } else { + errSprintf ("Missing info on major / minor axis of Earth.\n"); + return -2; + } + /* Check if our km assumption was valid. If it wasn't, they give us + * 6371000 m, which is > 6400. */ + if (meta->gds.majEarth > 6400) { + meta->gds.majEarth = meta->gds.majEarth / 1000.; + } + if (meta->gds.minEarth > 6400) { + meta->gds.minEarth = meta->gds.minEarth / 1000.; + } + break; + case 7: + meta->gds.f_sphere = 0; + /* Following assumes scale factor and scale value refer to + * scientific notation. */ + /* Incorrect Assumption (9/8/2003): scale factor / value are based + * on: Y * 10^D = R, where Y = original value, D = scale factor, ___ + * R = scale value. */ + if ((is3[21] != GRIB2MISSING_s4) && (is3[20] != GRIB2MISSING_s1) && + (is3[26] != GRIB2MISSING_s4) && (is3[25] != GRIB2MISSING_s1)) { + /* Assumes data is given in m (not km). */ + meta->gds.majEarth = is3[21] / (pow (10.0, is3[20]) * 1000.); + meta->gds.minEarth = is3[26] / (pow (10.0, is3[25]) * 1000.); + } else { + errSprintf ("Missing info on major / minor axis of Earth.\n"); + return -2; + } + /* Check if our m assumption was valid. If it wasn't, they give us + * 6371 km, which we convert to 6.371 < 6.4 */ + if (meta->gds.majEarth < 6.4) { + meta->gds.majEarth = meta->gds.majEarth * 1000.; + } + if (meta->gds.minEarth < 6.4) { + meta->gds.minEarth = meta->gds.minEarth * 1000.; + } + break; + default: + errSprintf ("Undefined shape of earth? %ld\n", is3[14]); + return -2; + } + /* Validate the radEarth is reasonable. */ + if ((meta->gds.majEarth > 6400) || (meta->gds.majEarth < 6300) || + (meta->gds.minEarth > 6400) || (meta->gds.minEarth < 6300)) { + errSprintf ("Bad shape of earth? %f %f\n", meta->gds.majEarth, + meta->gds.minEarth); + return -2; + } + meta->gds.Nx = is3[30]; + meta->gds.Ny = is3[34]; + if (meta->gds.Nx * meta->gds.Ny != meta->gds.numPts) { + errSprintf ("Nx * Ny != number of points?\n"); + return -2; + } + + /* Initialize variables prior to parsing the specific templates. */ + unit = 1e-6; + meta->gds.center = 0; + meta->gds.scaleLat1 = meta->gds.scaleLat2 = 0; + meta->gds.southLat = meta->gds.southLon = 0; + meta->gds.lat2 = meta->gds.lon2 = 0; + switch (is3[12]) { + case GS3_LATLON: /* 0: Regular lat/lon grid. */ + case GS3_GAUSSIAN_LATLON: /* 40: Gaussian lat/lon grid. */ + if (ns3 < 72) { + return -1; + } + angle = is3[38]; + subdivision = is3[42]; + if (angle != 0) { + if (subdivision == 0) { + errSprintf ("subdivision of 0? Could not determine unit" + " for latlon grid\n"); + return -2; + } + unit = angle / (double) (subdivision); + } + if ((is3[46] == GRIB2MISSING_s4) || (is3[50] == GRIB2MISSING_s4) || + (is3[55] == GRIB2MISSING_s4) || (is3[59] == GRIB2MISSING_s4) || + (is3[63] == GRIB2MISSING_s4) || (is3[67] == GRIB2MISSING_s4)) { + errSprintf ("Lat/Lon grid is not defined completely.\n"); + return -2; + } + meta->gds.lat1 = is3[46] * unit; + meta->gds.lon1 = is3[50] * unit; + meta->gds.resFlag = (uChar) is3[54]; + meta->gds.lat2 = is3[55] * unit; + meta->gds.lon2 = is3[59] * unit; + meta->gds.Dx = is3[63] * unit; /* degrees. */ + if (is3[12] == GS3_GAUSSIAN_LATLON) { + int np = is3[67]; /* parallels between a pole and the equator */ + meta->gds.Dy = 90.0 / np; + } else + meta->gds.Dy = is3[67] * unit; /* degrees. */ + meta->gds.scan = (uChar) is3[71]; + meta->gds.meshLat = 0; + meta->gds.orientLon = 0; + /* Resolve resolution flag(bit 3,4). Copy Dx,Dy as appropriate. */ + if ((meta->gds.resFlag & GRIB2BIT_3) && + (!(meta->gds.resFlag & GRIB2BIT_4))) { + meta->gds.Dy = meta->gds.Dx; + } else if ((!(meta->gds.resFlag & GRIB2BIT_3)) && + (meta->gds.resFlag & GRIB2BIT_4)) { + meta->gds.Dx = meta->gds.Dy; + } + break; + case GS3_MERCATOR: /* 10: Mercator grid. */ + if (ns3 < 72) { + return -1; + } + if ((is3[38] == GRIB2MISSING_s4) || (is3[42] == GRIB2MISSING_s4) || + (is3[47] == GRIB2MISSING_s4) || (is3[51] == GRIB2MISSING_s4) || + (is3[55] == GRIB2MISSING_s4) || (is3[60] == GRIB2MISSING_s4)) { + errSprintf ("Mercator grid is not defined completely.\n"); + return -2; + } + meta->gds.lat1 = is3[38] * unit; + meta->gds.lon1 = is3[42] * unit; + meta->gds.resFlag = (uChar) is3[46]; + meta->gds.meshLat = is3[47] * unit; + meta->gds.lat2 = is3[51] * unit; + meta->gds.lon2 = is3[55] * unit; + meta->gds.scan = (uChar) is3[59]; + meta->gds.orientLon = is3[60] * unit; + meta->gds.Dx = is3[64] / 1000.; /* mm -> m */ + meta->gds.Dy = is3[68] / 1000.; /* mm -> m */ + /* Resolve resolution flag(bit 3,4). Copy Dx,Dy as appropriate. */ + if ((meta->gds.resFlag & GRIB2BIT_3) && + (!(meta->gds.resFlag & GRIB2BIT_4))) { + if (is3[64] == GRIB2MISSING_s4) { + errSprintf ("Mercator grid is not defined completely.\n"); + return -2; + } + meta->gds.Dy = meta->gds.Dx; + } else if ((!(meta->gds.resFlag & GRIB2BIT_3)) && + (meta->gds.resFlag & GRIB2BIT_4)) { + if (is3[68] == GRIB2MISSING_s4) { + errSprintf ("Mercator grid is not defined completely.\n"); + return -2; + } + meta->gds.Dx = meta->gds.Dy; + } + break; + case GS3_POLAR: /* 20: Polar Stereographic grid. */ + if (ns3 < 65) { + return -1; + } + if ((is3[38] == GRIB2MISSING_s4) || (is3[42] == GRIB2MISSING_s4) || + (is3[47] == GRIB2MISSING_s4) || (is3[51] == GRIB2MISSING_s4)) { + errSprintf ("Polar Stereographic grid is not defined " + "completely.\n"); + return -2; + } + meta->gds.lat1 = is3[38] * unit; + meta->gds.lon1 = is3[42] * unit; + meta->gds.resFlag = (uChar) is3[46]; + /* Note (1) resFlag (bit 3,4) not applicable. */ + meta->gds.meshLat = is3[47] * unit; + meta->gds.orientLon = is3[51] * unit; + meta->gds.Dx = is3[55] / 1000.; /* mm -> m */ + meta->gds.Dy = is3[59] / 1000.; /* mm -> m */ + meta->gds.center = (uChar) is3[63]; + if (meta->gds.center & GRIB2BIT_1) { + /* South polar stereographic. */ + meta->gds.scaleLat1 = meta->gds.scaleLat2 = -90; + } else { + /* North polar stereographic. */ + meta->gds.scaleLat1 = meta->gds.scaleLat2 = 90; + } + if (meta->gds.center & GRIB2BIT_2) { + errSprintf ("Note (4) specifies no 'bi-polar stereograhic" + " projections'.\n"); + return -2; + } + meta->gds.scan = (uChar) is3[64]; + break; + case GS3_LAMBERT: /* 30: Lambert Conformal grid. */ + if (ns3 < 81) { + return -1; + } + if ((is3[38] == GRIB2MISSING_s4) || (is3[42] == GRIB2MISSING_s4) || + (is3[47] == GRIB2MISSING_s4) || (is3[51] == GRIB2MISSING_s4) || + (is3[65] == GRIB2MISSING_s4) || (is3[69] == GRIB2MISSING_s4) || + (is3[73] == GRIB2MISSING_s4) || (is3[77] == GRIB2MISSING_s4)) { + errSprintf ("Lambert Conformal grid is not defined " + "completely.\n"); + return -2; + } + meta->gds.lat1 = is3[38] * unit; + meta->gds.lon1 = is3[42] * unit; + meta->gds.resFlag = (uChar) is3[46]; + /* Note (3) resFlag (bit 3,4) not applicable. */ + meta->gds.meshLat = is3[47] * unit; + meta->gds.orientLon = is3[51] * unit; + meta->gds.Dx = is3[55] / 1000.; /* mm -> m */ + meta->gds.Dy = is3[59] / 1000.; /* mm -> m */ + meta->gds.center = (uChar) is3[63]; + meta->gds.scan = (uChar) is3[64]; + meta->gds.scaleLat1 = is3[65] * unit; + meta->gds.scaleLat2 = is3[69] * unit; + meta->gds.southLat = is3[73] * unit; + meta->gds.southLon = is3[77] * unit; + break; + case GS3_ORTHOGRAPHIC: /* 90: Orthographic grid. */ + // Misusing gdsType elements (gdsType needs extension) + meta->gds.lat1 = is3[38]; + meta->gds.lon1 = is3[42]; + meta->gds.resFlag = (uChar) is3[46]; + meta->gds.Dx = is3[47]; + meta->gds.Dy = is3[51]; + + meta->gds.lon2 = is3[55] / 1000.; /* xp - X-coordinateSub-satellite, mm -> m */ + meta->gds.lat2 = is3[59] / 1000.; /* yp - Y-coordinateSub-satellite, mm -> m */ + meta->gds.scan = (uChar) is3[63]; + meta->gds.orientLon = is3[64]; /* angle */ + meta->gds.stretchFactor = is3[68] * 1000000.; /* altitude */ + + meta->gds.southLon = is3[72]; /* x0 - X-coordinateOrigin */ + meta->gds.southLat = is3[76]; /* y0 - Y-coordinateOrigin */ + break; + default: + errSprintf ("Un-supported Map Projection. %ld\n", is3[12]); + // Don't abandon the conversion only because of an unknown projection + break; + //return -3; + } + if (meta->gds.scan != GRIB2BIT_2) { +#ifdef DEBUG + printf ("Scan mode is expected to be 0100 (ie %d) not %d\n", + GRIB2BIT_2, meta->gds.scan); + printf ("The merged GRIB2 Library should return it in 0100\n"); + printf ("The merged library swaps both NCEP and MDL data to scan " + "mode 0100\n"); +#endif +/* + errSprintf ("Scan mode is expected to be 0100 (ie %d) not %d", + GRIB2BIT_2, meta->gds.scan); + return -2; +*/ + } + return 0; +} + +/***************************************************************************** + * ParseSect4Time2secV1() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Attempt to parse time data in units provided by GRIB1 table 4, to + * seconds. + * + * ARGUMENTS + * time = The delta time to convert. (Input) + * unit = The unit to convert. (Input) + * ans = The converted answer. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = OK + * -1 = could not determine. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 1/2005 AAT: Fixed unit2sec[] table to have element 10 be 10800 (3 hours) + * instead of 0. + * + * NOTES + ***************************************************************************** + */ +int ParseSect4Time2secV1 (sInt4 time, int unit, double *ans) +{ + /* Following is a lookup table for unit conversion (see code table 4.4). */ + static sInt4 unit2sec[] = { + 60, 3600, 86400L, 0, 0, + 0, 0, 0, 0, 0, + 10800, 21600L, 43200L + }; + if ((unit >= 0) && (unit < 13)) { + if (unit2sec[unit] != 0) { + *ans = (double) (time * unit2sec[unit]); + return 0; + } + } else if (unit == 254) { + *ans = (double) (time); + return 0; + } + *ans = 0; + return -1; +} + +/***************************************************************************** + * ParseSect4Time2sec() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Attempt to parse time data in units provided by GRIB2 table 4.4, to + * seconds. + * + * ARGUMENTS + * time = The delta time to convert. (Input) + * unit = The unit to convert. (Input) + * ans = The converted answer. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = OK + * -1 = could not determine. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 1/2005 AAT: Fixed unit2sec[] table to have element 10 be 10800 (3 hours) + * instead of 0. + * + * NOTES + ***************************************************************************** + */ +int ParseSect4Time2sec (sInt4 time, int unit, double *ans) +{ + /* Following is a lookup table for unit conversion (see code table 4.4). */ + static sInt4 unit2sec[] = { + 60, 3600, 86400L, 0, 0, + 0, 0, 0, 0, 0, + 10800, 21600L, 43200L, 1 + }; + if ((unit >= 0) && (unit < 14)) { + if (unit2sec[unit] != 0) { + *ans = (double) (time * unit2sec[unit]); + return 0; + } + } + *ans = 0; + return -1; +} + +/***************************************************************************** + * ParseSect4() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To verify and parse section 4 data. + * + * ARGUMENTS + * is4 = The unpacked section 4 array. (Input) + * ns4 = The size of section 4. (Input) + * meta = The structure to fill. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = ns4 is too small. + * -2 = unexpected values in is4. + * -4 = un-supported Sect 4 template. + * -5 = unsupported forecast time unit. + * -6 = Ran out of memory. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 3/2003 AAT: Added support for GS4_SATELLITE. + * 3/2003 AAT: Adjusted allocing of sect4.Interval (should be safer now). + * 9/2003 AAT: Adjusted interpretation of scale factor / value. + * 5/2004 AAT: Added some memory checks. + * 3/2005 AAT: Added a cast to (uChar) when comparing to GRIB2MISSING_1 + * 3/2005 AAT: Added GS4_PROBABIL_PNT. + * + * NOTES + * Need to add support for GS4_RADAR = 20 + ***************************************************************************** + */ +static int ParseSect4 (sInt4 *is4, sInt4 ns4, grib_MetaData *meta) +{ + int i; /* Counter for time intervals in template 4.8, 4.9 + * (typically 1) or counter for satellite band in + * template 4.30. */ + void *temp_ptr; /* A temporary pointer when reallocating memory. */ + char *msg; /* A pointer to the current error message. */ + + if (ns4 < 9) { + return -1; + } + if (is4[4] != 4) { +#ifdef DEBUG + printf ("ERROR IS4 not labeled correctly. %d\n", is4[4]); +#endif + errSprintf ("ERROR IS4 not labeled correctly. %d\n", is4[4]); + return -2; + } + if (is4[5] != 0) { +#ifdef DEBUG + printf ("Un-supported template.\n All Supported template " + "have 0 coordinate vertical values after template."); +#endif + errSprintf ("Un-supported template.\n All Supported template " + "have 0 coordinate vertical values after template."); + return -4; + } + if ((is4[7] != GS4_ANALYSIS) && (is4[7] != GS4_ENSEMBLE) && + (is4[7] != GS4_DERIVED) && (is4[7] != GS4_PROBABIL_PNT) && + (is4[7] != GS4_STATISTIC) && (is4[7] != GS4_PROBABIL_TIME) && + (is4[7] != GS4_PERCENTILE) && (is4[7] != GS4_ENSEMBLE_STAT) && + (is4[7] != GS4_SATELLITE) && (is4[7] != GS4_DERIVED_INTERVAL)) { +#ifdef DEBUG + printf ("Un-supported Template. %d\n", is4[7]); +#endif + errSprintf ("Un-supported Template. %d\n", is4[7]); + return -4; + } + meta->pds2.sect4.templat = (unsigned short int) is4[7]; + + /* + * Handle variables common to the supported templates. + */ + if (ns4 < 34) { + return -1; + } + meta->pds2.sect4.cat = (uChar) is4[9]; + meta->pds2.sect4.subcat = (uChar) is4[10]; + meta->pds2.sect4.genProcess = (uChar) is4[11]; + + /* Initialize variables prior to parsing the specific templates. */ + meta->pds2.sect4.typeEnsemble = 0; + meta->pds2.sect4.perturbNum = 0; + meta->pds2.sect4.numberFcsts = 0; + meta->pds2.sect4.derivedFcst = 0; + meta->pds2.sect4.validTime = meta->pds2.refTime; + + if (meta->pds2.sect4.templat == GS4_SATELLITE) { + meta->pds2.sect4.genID = (uChar) is4[12]; + meta->pds2.sect4.numBands = (uChar) is4[13]; + meta->pds2.sect4.bands = + (sect4_BandType *) realloc ((void *) meta->pds2.sect4.bands, + meta->pds2.sect4.numBands * + sizeof (sect4_BandType)); + for (i = 0; i < meta->pds2.sect4.numBands; i++) { + meta->pds2.sect4.bands[i].series = + (unsigned short int) is4[14 + 10 * i]; + meta->pds2.sect4.bands[i].numbers = + (unsigned short int) is4[16 + 10 * i]; + meta->pds2.sect4.bands[i].instType = (uChar) is4[18 + 10 * i]; + meta->pds2.sect4.bands[i].centWaveNum.factor = + (uChar) is4[19 + 10 * i]; + meta->pds2.sect4.bands[i].centWaveNum.value = is4[20 + 10 * i]; + } + + meta->pds2.sect4.fstSurfType = GRIB2MISSING_u1; + meta->pds2.sect4.fstSurfScale = GRIB2MISSING_s1; + meta->pds2.sect4.fstSurfValue = 0; + meta->pds2.sect4.sndSurfType = GRIB2MISSING_u1; + meta->pds2.sect4.sndSurfScale = GRIB2MISSING_s1; + meta->pds2.sect4.sndSurfValue = 0; + + return 0; + } + meta->pds2.sect4.bgGenID = (uChar) is4[12]; + meta->pds2.sect4.genID = (uChar) is4[13]; + if ((is4[14] == GRIB2MISSING_u2) || (is4[16] == GRIB2MISSING_u1)) { + meta->pds2.sect4.f_validCutOff = 0; + meta->pds2.sect4.cutOff = 0; + } else { + meta->pds2.sect4.f_validCutOff = 1; + meta->pds2.sect4.cutOff = is4[14] * 3600 + is4[16] * 60; + } + if (is4[18] == GRIB2MISSING_s4) { + errSprintf ("Missing 'forecast' time?\n"); + return -5; + } + if (ParseSect4Time2sec (is4[18], is4[17], + &(meta->pds2.sect4.foreSec)) != 0) { + errSprintf ("Unable to convert this TimeUnit: %ld\n", is4[17]); + return -5; + } + + meta->pds2.sect4.validTime = (time_t) (meta->pds2.refTime + + meta->pds2.sect4.foreSec); + + /* + * Following is based on what was needed to get correct Radius of Earth in + * section 3. (Hopefully they are consistent). + */ + meta->pds2.sect4.fstSurfType = (uChar) is4[22]; + if ((is4[24] == GRIB2MISSING_s4) || (is4[23] == GRIB2MISSING_s1) || + (meta->pds2.sect4.fstSurfType == GRIB2MISSING_u1)) { + meta->pds2.sect4.fstSurfScale = GRIB2MISSING_s1; + meta->pds2.sect4.fstSurfValue = 0; + } else { + meta->pds2.sect4.fstSurfScale = is4[23]; + meta->pds2.sect4.fstSurfValue = is4[24] / pow (10.0, is4[23]); + } + meta->pds2.sect4.sndSurfType = (uChar) is4[28]; + if ((is4[30] == GRIB2MISSING_s4) || (is4[29] == GRIB2MISSING_s1) || + (meta->pds2.sect4.sndSurfType == GRIB2MISSING_u1)) { + meta->pds2.sect4.sndSurfScale = GRIB2MISSING_s1; + meta->pds2.sect4.sndSurfValue = 0; + } else { + meta->pds2.sect4.sndSurfScale = is4[29]; + meta->pds2.sect4.sndSurfValue = is4[30] / pow (10.0, is4[29]); + } + switch (meta->pds2.sect4.templat) { + case GS4_ANALYSIS: /* 4.0 */ + break; + case GS4_ENSEMBLE: /* 4.1 */ + meta->pds2.sect4.typeEnsemble = (uChar) is4[34]; + meta->pds2.sect4.perturbNum = (uChar) is4[35]; + meta->pds2.sect4.numberFcsts = (uChar) is4[36]; + break; + case GS4_ENSEMBLE_STAT: /* 4.1 */ + meta->pds2.sect4.typeEnsemble = (uChar) is4[34]; + meta->pds2.sect4.perturbNum = (uChar) is4[35]; + meta->pds2.sect4.numberFcsts = (uChar) is4[36]; + if (ParseTime (&(meta->pds2.sect4.validTime), is4[37], is4[39], + is4[40], is4[41], is4[42], is4[43]) != 0) { + msg = errSprintf (NULL); + uChar numInterval = (uChar) is4[44]; + if (numInterval != 1) { + errSprintf ("ERROR: in call to ParseTime from ParseSect4\n%s", + msg); + errSprintf ("Most likely they didn't complete bytes 38-44 of " + "Template 4.11\n"); + free (msg); + return -1; + } + meta->pds2.sect4.numInterval = numInterval; + printf ("Warning: in call to ParseTime from ParseSect4\n%s", msg); + free (msg); + meta->pds2.sect4.validTime = (time_t) (meta->pds2.refTime + + meta->pds2.sect4.foreSec); + printf ("Most likely they didn't complete bytes 38-44 of " + "Template 4.11\n"); + } else { + meta->pds2.sect4.numInterval = (uChar) is4[44]; + } + + /* Added this check because some MOS grids didn't finish the + * template. */ + if (meta->pds2.sect4.numInterval != 0) { + temp_ptr = realloc ((void *) meta->pds2.sect4.Interval, + meta->pds2.sect4.numInterval * + sizeof (sect4_IntervalType)); + if (temp_ptr == NULL) { + printf ("Ran out of memory.\n"); + return -6; + } + meta->pds2.sect4.Interval = (sect4_IntervalType *) temp_ptr; + meta->pds2.sect4.numMissing = is4[45]; + for (i = 0; i < meta->pds2.sect4.numInterval; i++) { + meta->pds2.sect4.Interval[i].processID = + (uChar) is4[49 + i * 12]; + meta->pds2.sect4.Interval[i].incrType = + (uChar) is4[50 + i * 12]; + meta->pds2.sect4.Interval[i].timeRangeUnit = + (uChar) is4[51 + i * 12]; + meta->pds2.sect4.Interval[i].lenTime = is4[52 + i * 12]; + meta->pds2.sect4.Interval[i].incrUnit = + (uChar) is4[56 + i * 12]; + meta->pds2.sect4.Interval[i].timeIncr = + (uChar) is4[57 + i * 12]; + } + } else { +#ifdef DEBUG + printf ("Caution: Template 4.11 had no Intervals.\n"); +#endif + meta->pds2.sect4.numMissing = is4[45]; + } + break; + case GS4_DERIVED: /* 4.2 */ + meta->pds2.sect4.derivedFcst = (uChar) is4[34]; + meta->pds2.sect4.numberFcsts = (uChar) is4[35]; + break; + case GS4_DERIVED_INTERVAL: /* 4.12 */ + meta->pds2.sect4.derivedFcst = (uChar) is4[34]; + meta->pds2.sect4.numberFcsts = (uChar) is4[35]; + + if (ParseTime (&(meta->pds2.sect4.validTime), is4[36], is4[38], + is4[39], is4[40], is4[41], is4[42]) != 0) { + msg = errSprintf (NULL); + uChar numInterval = (uChar) is4[43]; + if (numInterval != 1) { + errSprintf ("ERROR: in call to ParseTime from ParseSect4\n%s", + msg); + errSprintf ("Most likely they didn't complete bytes 37-43 of " + "Template 4.12\n"); + free (msg); + return -1; + } + meta->pds2.sect4.numInterval = numInterval; + printf ("Warning: in call to ParseTime from ParseSect4\n%s", msg); + free (msg); + meta->pds2.sect4.validTime = (time_t) (meta->pds2.refTime + + meta->pds2.sect4.foreSec); + printf ("Most likely they didn't complete bytes 37-43 of " + "Template 4.12\n"); + } else { + meta->pds2.sect4.numInterval = (uChar) is4[43]; + } + + /* Added this check because some MOS grids didn't finish the + * template. */ + if (meta->pds2.sect4.numInterval != 0) { + temp_ptr = realloc ((void *) meta->pds2.sect4.Interval, + meta->pds2.sect4.numInterval * + sizeof (sect4_IntervalType)); + if (temp_ptr == NULL) { + printf ("Ran out of memory.\n"); + return -6; + } + meta->pds2.sect4.Interval = (sect4_IntervalType *) temp_ptr; + meta->pds2.sect4.numMissing = is4[44]; + for (i = 0; i < meta->pds2.sect4.numInterval; i++) { + meta->pds2.sect4.Interval[i].processID = + (uChar) is4[48 + i * 12]; + meta->pds2.sect4.Interval[i].incrType = + (uChar) is4[49 + i * 12]; + meta->pds2.sect4.Interval[i].timeRangeUnit = + (uChar) is4[50 + i * 12]; + meta->pds2.sect4.Interval[i].lenTime = is4[51 + i * 12]; + meta->pds2.sect4.Interval[i].incrUnit = + (uChar) is4[55 + i * 12]; + meta->pds2.sect4.Interval[i].timeIncr = + (uChar) is4[56 + i * 12]; + } + } else { +#ifdef DEBUG + printf ("Caution: Template 4.12 had no Intervals.\n"); +#endif + meta->pds2.sect4.numMissing = is4[44]; + } + break; + case GS4_STATISTIC: /* 4.8 */ + if (ParseTime (&(meta->pds2.sect4.validTime), is4[34], is4[36], + is4[37], is4[38], is4[39], is4[40]) != 0) { + msg = errSprintf (NULL); + uChar numInterval = (uChar) is4[41]; + if (numInterval != 1) { + errSprintf ("ERROR: in call to ParseTime from ParseSect4\n%s", + msg); + errSprintf ("Most likely they didn't complete bytes 35-41 of " + "Template 4.8\n"); + free (msg); + return -1; + } + meta->pds2.sect4.numInterval = numInterval; + printf ("Warning: in call to ParseTime from ParseSect4\n%s", msg); + free (msg); + meta->pds2.sect4.validTime = (time_t) (meta->pds2.refTime + + meta->pds2.sect4.foreSec); + printf ("Most likely they didn't complete bytes 35-41 of " + "Template 4.8\n"); + } else { + meta->pds2.sect4.numInterval = (uChar) is4[41]; + } + + /* Added this check because some MOS grids didn't finish the + * template. */ + if (meta->pds2.sect4.numInterval != 0) { + temp_ptr = realloc ((void *) meta->pds2.sect4.Interval, + meta->pds2.sect4.numInterval * + sizeof (sect4_IntervalType)); + if (temp_ptr == NULL) { + printf ("Ran out of memory.\n"); + return -6; + } + meta->pds2.sect4.Interval = (sect4_IntervalType *) temp_ptr; + meta->pds2.sect4.numMissing = is4[42]; + for (i = 0; i < meta->pds2.sect4.numInterval; i++) { + meta->pds2.sect4.Interval[i].processID = + (uChar) is4[46 + i * 12]; + meta->pds2.sect4.Interval[i].incrType = + (uChar) is4[47 + i * 12]; + meta->pds2.sect4.Interval[i].timeRangeUnit = + (uChar) is4[48 + i * 12]; + meta->pds2.sect4.Interval[i].lenTime = is4[49 + i * 12]; + meta->pds2.sect4.Interval[i].incrUnit = + (uChar) is4[53 + i * 12]; + meta->pds2.sect4.Interval[i].timeIncr = + (uChar) is4[54 + i * 12]; + } + } else { +#ifdef DEBUG + printf ("Caution: Template 4.8 had no Intervals.\n"); +#endif + meta->pds2.sect4.numMissing = is4[42]; + } + break; + case GS4_PERCENTILE: /* 4.10 */ + meta->pds2.sect4.percentile = is4[34]; + if (ParseTime (&(meta->pds2.sect4.validTime), is4[35], is4[37], + is4[38], is4[39], is4[40], is4[41]) != 0) { + msg = errSprintf (NULL); + uChar numInterval = (uChar) is4[42]; + if (numInterval != 1) { + errSprintf ("ERROR: in call to ParseTime from ParseSect4\n%s", + msg); + errSprintf ("Most likely they didn't complete bytes 35-41 of " + "Template 4.8\n"); + free (msg); + return -1; + } + meta->pds2.sect4.numInterval = numInterval; + printf ("Warning: in call to ParseTime from ParseSect4\n%s", msg); + free (msg); + meta->pds2.sect4.validTime = (time_t) (meta->pds2.refTime + + meta->pds2.sect4.foreSec); + printf ("Most likely they didn't complete bytes 35-41 of " + "Template 4.8\n"); + } else { + meta->pds2.sect4.numInterval = (uChar) is4[42]; + } + + /* Added this check because some MOS grids didn't finish the + * template. */ + if (meta->pds2.sect4.numInterval != 0) { + temp_ptr = realloc ((void *) meta->pds2.sect4.Interval, + meta->pds2.sect4.numInterval * + sizeof (sect4_IntervalType)); + if (temp_ptr == NULL) { + printf ("Ran out of memory.\n"); + return -6; + } + meta->pds2.sect4.Interval = (sect4_IntervalType *) temp_ptr; + meta->pds2.sect4.numMissing = is4[43]; + for (i = 0; i < meta->pds2.sect4.numInterval; i++) { + meta->pds2.sect4.Interval[i].processID = + (uChar) is4[47 + i * 12]; + meta->pds2.sect4.Interval[i].incrType = + (uChar) is4[48 + i * 12]; + meta->pds2.sect4.Interval[i].timeRangeUnit = + (uChar) is4[49 + i * 12]; + meta->pds2.sect4.Interval[i].lenTime = is4[50 + i * 12]; + meta->pds2.sect4.Interval[i].incrUnit = + (uChar) is4[54 + i * 12]; + meta->pds2.sect4.Interval[i].timeIncr = + (uChar) is4[55 + i * 12]; + } + } else { +#ifdef DEBUG + printf ("Caution: Template 4.10 had no Intervals.\n"); +#endif + meta->pds2.sect4.numMissing = is4[43]; + } + break; + case GS4_PROBABIL_PNT: /* 4.5 */ + meta->pds2.sect4.foreProbNum = (uChar) is4[34]; + meta->pds2.sect4.numForeProbs = (uChar) is4[35]; + meta->pds2.sect4.probType = (uChar) is4[36]; + meta->pds2.sect4.lowerLimit.factor = (sChar) is4[37]; + meta->pds2.sect4.lowerLimit.value = is4[38]; + meta->pds2.sect4.upperLimit.factor = (sChar) is4[42]; + meta->pds2.sect4.upperLimit.value = is4[43]; + break; + case GS4_PROBABIL_TIME: /* 4.9 */ + meta->pds2.sect4.foreProbNum = (uChar) is4[34]; + meta->pds2.sect4.numForeProbs = (uChar) is4[35]; + meta->pds2.sect4.probType = (uChar) is4[36]; + meta->pds2.sect4.lowerLimit.factor = (sChar) is4[37]; + meta->pds2.sect4.lowerLimit.value = is4[38]; + meta->pds2.sect4.upperLimit.factor = (sChar) is4[42]; + meta->pds2.sect4.upperLimit.value = is4[43]; + if (ParseTime (&(meta->pds2.sect4.validTime), is4[47], is4[49], + is4[50], is4[51], is4[52], is4[53]) != 0) { + msg = errSprintf (NULL); + uChar numInterval = (uChar) is4[54]; + if (numInterval != 1) { + errSprintf ("ERROR: in call to ParseTime from ParseSect4\n%s", + msg); + errSprintf ("Most likely they didn't complete bytes 48-54 of " + "Template 4.9\n"); + free (msg); + return -1; + } + meta->pds2.sect4.numInterval = numInterval; + printf ("Warning: in call to ParseTime from ParseSect4\n%s", msg); + free (msg); + meta->pds2.sect4.validTime = (time_t) (meta->pds2.refTime + + meta->pds2.sect4.foreSec); + printf ("Most likely they didn't complete bytes 48-54 of " + "Template 4.9\n"); + } else { + meta->pds2.sect4.numInterval = (uChar) is4[54]; + } + temp_ptr = realloc ((void *) meta->pds2.sect4.Interval, + meta->pds2.sect4.numInterval * + sizeof (sect4_IntervalType)); + if (temp_ptr == NULL) { + printf ("Ran out of memory.\n"); + return -6; + } + meta->pds2.sect4.Interval = (sect4_IntervalType *) temp_ptr; + meta->pds2.sect4.numMissing = is4[55]; + for (i = 0; i < meta->pds2.sect4.numInterval; i++) { + meta->pds2.sect4.Interval[i].processID = (uChar) is4[59 + i * 12]; + meta->pds2.sect4.Interval[i].incrType = (uChar) is4[60 + i * 12]; + meta->pds2.sect4.Interval[i].timeRangeUnit = + (uChar) is4[61 + i * 12]; + meta->pds2.sect4.Interval[i].lenTime = is4[62 + i * 12]; + meta->pds2.sect4.Interval[i].incrUnit = (uChar) is4[66 + i * 12]; + meta->pds2.sect4.Interval[i].timeIncr = (uChar) is4[67 + i * 12]; + } + break; + default: + errSprintf ("Un-supported Template. %ld\n", is4[7]); + return -4; + } + return 0; +} + +/***************************************************************************** + * ParseSect5() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To verify and parse section 5 data. + * + * ARGUMENTS + * is5 = The unpacked section 5 array. (Input) + * ns5 = The size of section 5. (Input) + * meta = The structure to fill. (Output) + * xmissp = The primary missing value. (Input) + * xmisss = The secondary missing value. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = ns5 is too small. + * -2 = unexpected values in is5. + * -6 = unsupported packing. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +static int ParseSect5 (sInt4 *is5, sInt4 ns5, grib_MetaData *meta, + float xmissp, float xmisss) +{ + if (ns5 < 22) { + return -1; + } + if (is5[4] != 5) { + errSprintf ("ERROR IS5 not labeled correctly. %ld\n", is5[5]); + return -2; + } + if ((is5[9] != GS5_SIMPLE) && (is5[9] != GS5_CMPLX) && + (is5[9] != GS5_CMPLXSEC) && (is5[9] != GS5_SPECTRAL) && + (is5[9] != GS5_HARMONIC) && (is5[9] != GS5_JPEG2000) && + (is5[9] != GS5_PNG) && (is5[9] != GS5_JPEG2000_ORG) && + (is5[9] != GS5_PNG_ORG)) { + errSprintf ("Un-supported Packing? %ld\n", is5[9]); + return -6; + } + meta->gridAttrib.packType = (sInt4) is5[9]; + meta->gridAttrib.f_maxmin = 0; + meta->gridAttrib.missPri = xmissp; + meta->gridAttrib.missSec = xmisss; + if ((is5[9] == GS5_SPECTRAL) || (is5[9] == GS5_HARMONIC)) { + meta->gridAttrib.fieldType = 0; + meta->gridAttrib.f_miss = 0; + return 0; + } + if (is5[20] > 1) { + errSprintf ("Invalid field type. %ld\n", is5[20]); + return -2; + } + MEMCPY_BIG (&meta->gridAttrib.refVal, &(is5[11]), 4); + meta->gridAttrib.ESF = is5[15]; + meta->gridAttrib.DSF = is5[17]; + meta->gridAttrib.fieldType = (uChar) is5[20]; + if ((is5[9] == GS5_JPEG2000) || (is5[9] == GS5_JPEG2000_ORG) || + (is5[9] == GS5_PNG) || (is5[9] == GS5_PNG_ORG)) { + meta->gridAttrib.f_miss = 0; + return 0; + } + if (meta->gridAttrib.packType == 0) { + meta->gridAttrib.f_miss = 0; + } else { + if (ns5 < 23) { + return -1; + } + if (is5[22] > 2) { + errSprintf ("Invalid missing management type, f_miss = %ld\n", + is5[22]); + return -2; + } + meta->gridAttrib.f_miss = (uChar) is5[22]; + } + return 0; +} + +/***************************************************************************** + * MetaParse() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To parse all the meta data from a grib2 message. + * + * ARGUMENTS + * meta = The structure to fill. (Output) + * is0 = The unpacked section 0 array. (Input) + * ns0 = The size of section 0. (Input) + * is1 = The unpacked section 1 array. (Input) + * ns1 = The size of section 1. (Input) + * is2 = The unpacked section 2 array. (Input) + * ns2 = The size of section 2. (Input) + * rdat = The float data in section 2. (Input) + * nrdat = Length of rdat. (Input) + * idat = The integer data in section 2. (Input) + * nidat = Length of idat. (Input) + * is3 = The unpacked section 3 array. (Input) + * ns3 = The size of section 3. (Input) + * is4 = The unpacked section 4 array. (Input) + * ns4 = The size of section 4. (Input) + * is5 = The unpacked section 5 array. (Input) + * ns5 = The size of section 5. (Input) + * grib_len = The length of the entire grib message. (Input) + * xmissp = The primary missing value. (Input) + * xmisss = The secondary missing value. (Input) + * simpVer = The version of the simple weather code to use when parsing the + * WxString (if applicable). (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = A dimension is too small. + * -2 = unexpected values in a grib section. + * -3 = un-supported map Projection. + * -4 = un-supported Sect 4 template. + * -5 = unsupported forecast time unit. + * -6 = unsupported sect 5 packing. + * -10 = Something the driver can't handle yet. + * (prodType != 0, f_sphere != 1, etc) + * -11 = Weather grid without a lookup table. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +int MetaParse (grib_MetaData *meta, sInt4 *is0, sInt4 ns0, + sInt4 *is1, sInt4 ns1, sInt4 *is2, sInt4 ns2, + float *rdat, sInt4 nrdat, sInt4 *idat, sInt4 nidat, + sInt4 *is3, sInt4 ns3, sInt4 *is4, sInt4 ns4, + sInt4 *is5, sInt4 ns5, sInt4 grib_len, + float xmissp, float xmisss, int simpVer) +{ + int ierr; /* The error code of a called routine */ + /* char *element; *//* Holds the name of the current variable. */ + /* char *comment; *//* Holds more comments about current variable. */ + /* char *unitName; *//* Holds the name of the unit [K] [%] .. etc */ + uChar probType; /* The probability type */ + double lowerProb; /* The lower limit on probability forecast if + * template 4.5 or 4.9 */ + double upperProb; /* The upper limit on probability forecast if + * template 4.5 or 4.9 */ + sInt4 lenTime; /* Length of time for element (see 4.8 and 4.9) */ + + if ((ierr = ParseSect0 (is0, ns0, grib_len, meta)) != 0) { + preErrSprintf ("Parse error Section 0\n"); + //return ierr; + } + if ((ierr = ParseSect1 (is1, ns1, meta)) != 0) { + preErrSprintf ("Parse error Section 1\n"); + //return ierr; + } + if (ns2 < 7) { + errSprintf ("ns2 was too small in MetaParse\n"); + //return -1; + } + meta->pds2.f_sect2 = (uChar) (is2[0] != 0); + if (meta->pds2.f_sect2) { + meta->pds2.sect2NumGroups = is2[7 - 1]; + } else { + meta->pds2.sect2NumGroups = 0; + } + if ((ierr = ParseSect3 (is3, ns3, meta)) != 0) { + preErrSprintf ("Parse error Section 3\n"); + //return ierr; + } + if (meta->gds.f_sphere != 1) { + errSprintf ("Driver Filter: Can only handle spheres.\n"); + //return -10; + } + if ((ierr = ParseSect4 (is4, ns4, meta)) != 0) { + preErrSprintf ("Parse error Section 4\n"); + //return ierr; + } + if ((ierr = ParseSect5 (is5, ns5, meta, xmissp, xmisss)) != 0) { + preErrSprintf ("Parse error Section 5\n"); + //return ierr; + } + /* Compute ElementName. */ + if (meta->element) { + free (meta->element); + meta->element = NULL; + } + if (meta->unitName) { + free (meta->unitName); + meta->unitName = NULL; + } + if (meta->comment) { + free (meta->comment); + meta->comment = NULL; + } + + if ((meta->pds2.sect4.templat == GS4_PROBABIL_TIME) || + (meta->pds2.sect4.templat == GS4_PROBABIL_PNT)) { + probType = meta->pds2.sect4.probType; + lowerProb = meta->pds2.sect4.lowerLimit.value * + pow (10.0, -1 * meta->pds2.sect4.lowerLimit.factor); + upperProb = meta->pds2.sect4.upperLimit.value * + pow (10.0, -1 * meta->pds2.sect4.upperLimit.factor); + } else { + probType = 0; + lowerProb = 0; + upperProb = 0; + } + if (meta->pds2.sect4.numInterval > 0) { + /* Try to convert lenTime to hourly. */ + if (meta->pds2.sect4.Interval[0].timeRangeUnit == 255) { + lenTime = (sInt4) ((meta->pds2.sect4.validTime - + meta->pds2.sect4.foreSec - + meta->pds2.refTime) / 3600); + } else if (meta->pds2.sect4.Interval[0].timeRangeUnit == 0) { + lenTime = (sInt4) (meta->pds2.sect4.Interval[0].lenTime / 60.); + } else if (meta->pds2.sect4.Interval[0].timeRangeUnit == 1) { + lenTime = meta->pds2.sect4.Interval[0].lenTime; + } else if (meta->pds2.sect4.Interval[0].timeRangeUnit == 2) { + lenTime = meta->pds2.sect4.Interval[0].lenTime * 24; + } else if (meta->pds2.sect4.Interval[0].timeRangeUnit == 10) { + lenTime = meta->pds2.sect4.Interval[0].lenTime * 3; + } else if (meta->pds2.sect4.Interval[0].timeRangeUnit == 11) { + lenTime = meta->pds2.sect4.Interval[0].lenTime * 6; + } else if (meta->pds2.sect4.Interval[0].timeRangeUnit == 12) { + lenTime = meta->pds2.sect4.Interval[0].lenTime * 12; + } else if (meta->pds2.sect4.Interval[0].timeRangeUnit == 13) { + lenTime = (sInt4) (meta->pds2.sect4.Interval[0].lenTime / 3600.); + } else { + lenTime = 0; + printf ("Can't handle this timeRangeUnit\n"); + myAssert (meta->pds2.sect4.Interval[0].timeRangeUnit == 1); + } +/* + } else { + lenTime = 255; + } + if (lenTime == 255) { + lenTime = (meta->pds2.sect4.validTime - meta->pds2.sect4.foreSec - + meta->pds2.refTime) / 3600; + } +*/ + if (lenTime == GRIB2MISSING_s4) { + lenTime = 0; + } + ParseElemName (meta->center, meta->subcenter, + meta->pds2.prodType, meta->pds2.sect4.templat, + meta->pds2.sect4.cat, meta->pds2.sect4.subcat, + lenTime, meta->pds2.sect4.Interval[0].incrType, + meta->pds2.sect4.genID, probType, lowerProb, + upperProb, &(meta->element), &(meta->comment), + &(meta->unitName), &(meta->convert), + meta->pds2.sect4.percentile); + } else { + ParseElemName (meta->center, meta->subcenter, + meta->pds2.prodType, meta->pds2.sect4.templat, + meta->pds2.sect4.cat, meta->pds2.sect4.subcat, 0, 255, + meta->pds2.sect4.genID, probType, lowerProb, upperProb, + &(meta->element), &(meta->comment), &(meta->unitName), + &(meta->convert), meta->pds2.sect4.percentile); + } +#ifdef DEBUG +/* + printf ("Element: %s\nunitName: %s\ncomment: %s\n", meta->element, + meta->comment, meta->unitName); +*/ +#endif + +/* + if (strcmp (element, "") == 0) { + meta->element = (char *) realloc ((void *) (meta->element), + (1 + strlen ("unknown")) * + sizeof (char)); + strcpy (meta->element, "unknown"); + } else { + if (IsData_MOS (meta->pds2.center, meta->pds2.subcenter)) { + * See : http://www.nco.ncep.noaa.gov/pmb/docs/on388/tablea.html * + if (meta->pds2.sect4.genID == 96) { + meta->element = (char *) realloc ((void *) (meta->element), + (1 + 7 + strlen (element)) * + sizeof (char)); + sprintf (meta->element, "MOSGFS-%s", element); + } else { + meta->element = (char *) realloc ((void *) (meta->element), + (1 + 4 + strlen (element)) * + sizeof (char)); + sprintf (meta->element, "MOS-%s", element); + } + } else { + meta->element = (char *) realloc ((void *) (meta->element), + (1 + strlen (element)) * + sizeof (char)); + strcpy (meta->element, element); + } + } + meta->unitName = (char *) realloc ((void *) (meta->unitName), + (1 + 2 + strlen (unitName)) * + sizeof (char)); + sprintf (meta->unitName, "[%s]", unitName); + meta->comment = (char *) realloc ((void *) (meta->comment), + (1 + strlen (comment) + + strlen (unitName) + + 2 + 1) * sizeof (char)); + sprintf (meta->comment, "%s [%s]", comment, unitName); +*/ + if ((meta->pds2.sect4.sndSurfScale == GRIB2MISSING_s1) || + (meta->pds2.sect4.sndSurfType == GRIB2MISSING_u1)) { +/* + if ((meta->pds2.sect4.fstSurfScale == GRIB2MISSING_s1) || + (meta->pds2.sect4.fstSurfType == GRIB2MISSING_u1)) { + ParseLevelName (meta->center, meta->subcenter, + meta->pds2.sect4.fstSurfType, 0, 0, 0, + &(meta->shortFstLevel), &(meta->longFstLevel)); + } else { +*/ + ParseLevelName (meta->center, meta->subcenter, + meta->pds2.sect4.fstSurfType, + meta->pds2.sect4.fstSurfValue, 0, 0, + &(meta->shortFstLevel), &(meta->longFstLevel)); +/* + } +*/ + } else { + ParseLevelName (meta->center, meta->subcenter, + meta->pds2.sect4.fstSurfType, + meta->pds2.sect4.fstSurfValue, 1, + meta->pds2.sect4.sndSurfValue, &(meta->shortFstLevel), + &(meta->longFstLevel)); + } + + /* Continue parsing section 2 data. */ + if (meta->pds2.f_sect2) { + MetaSect2Free (meta); + if (strcmp (meta->element, "Wx") == 0) { + meta->pds2.sect2.ptrType = GS2_WXTYPE; + if ((ierr = ParseSect2_Wx (rdat, nrdat, idat, nidat, + &(meta->pds2.sect2.wx), simpVer)) != 0) { + preErrSprintf ("Parse error Section 2 : Weather Data\n"); + //return ierr; + } + } else { + meta->pds2.sect2.ptrType = GS2_UNKNOWN; + if ((ierr = ParseSect2_Unknown (rdat, nrdat, idat, nidat, meta)) + != 0) { + preErrSprintf ("Parse error Section 2 : Unknown Data type\n"); + //return ierr; + } + } + } else { + if (strcmp (meta->element, "Wx") == 0) { + errSprintf ("Weather grid does not have look up table?"); + //return -11; + } + } + return 0; +} + +/***************************************************************************** + * ParseGridNoMiss() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * A helper function for ParseGrid. In this particular case it is dealing + * with a field that has NO missing value type. + * Walks through either a float or an integer grid, computing the min/max + * values in the grid, and converts the units. It uses gridAttrib info for the + * missing values and it updates gridAttrib with the observed min/max values. + * + * ARGUMENTS + * attrib = Grid Attribute structure already filled in (Input/Output) + * grib_Data = The place to store the grid data. (Output) + * Nx, Ny = The dimensions of the grid (Input) + * iain = Place to find data if it is an Integer (or float). (Input) + * unitM = M in unit conversion equation y(new) = m x(orig) + b (Input) + * unitB = B in unit conversion equation y(new) = m x(orig) + b (Input) + * f_wxType = true if we have a valid wx type. (Input) + * WxType = table to look up values in. (Input) + * startX = The start of the X values. (Input) + * startY = The start of the Y values. (Input) + * subNx = The Nx dimmension of the subgrid (Input) + * subNy = The Ny dimmension of the subgrid (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 12/2002 Arthur Taylor (MDL/RSIS): Created to optimize part of ParseGrid. + * 5/2003 AAT: Added ability to see if wxType occurs. If so sets table + * valid to 2, otherwise leaves it at 1. If table valid is 0 then + * sets value to missing value (if applicable). + * 2/2004 AAT: Added the subgrid capability. + * + * NOTES + * 1) Don't have to check if value became missing value, because we can check + * if missing falls in the range of the min/max converted units. If + * missing does fall in that range we need to move missing. + * (See f_readjust in ParseGrid) + ***************************************************************************** + */ +static void ParseGridNoMiss (gridAttribType *attrib, double *grib_Data, + sInt4 Nx, sInt4 Ny, sInt4 *iain, + double unitM, double unitB, uChar f_wxType, + sect2_WxType *WxType, int startX, int startY, + int subNx, int subNy) +{ + sInt4 x, y; /* Where we are in the grid. */ + double value; /* The data in the new units. */ + uChar f_maxmin = 0; /* Flag if max/min is valid yet. */ + uInt4 index; /* Current index into Wx table. */ + sInt4 *itemp = NULL; + float *ftemp = NULL; + + /* Resolve possibility that the data is an integer or a float and find + * max/min values. (see note 1) */ + for (y = 0; y < subNy; y++) { + if (((startY + y - 1) < 0) || ((startY + y - 1) >= Ny)) { + for (x = 0; x < subNx; x++) { + *grib_Data++ = 9999; + } + } else { + if (attrib->fieldType) { + itemp = iain + (startY + y - 1) * Nx + (startX - 1); + } else { + ftemp = ((float *) iain) + (startY + y - 1) * Nx + (startX - 1); + } + for (x = 0; x < subNx; x++) { + if (((startX + x - 1) < 0) || ((startX + x - 1) >= Nx)) { + *grib_Data++ = 9999; + } else { + /* Convert the units. */ + if (attrib->fieldType) { + if (unitM == -10) { + value = pow (10.0, (*itemp++)); + } else { + value = unitM * (*itemp++) + unitB; + } + } else { + if (unitM == -10) { + value = pow (10.0, (double) (*ftemp++)); + } else { + value = unitM * (*ftemp++) + unitB; + } + } + if (f_wxType) { + index = (uInt4) value; + if (index < WxType->dataLen) { + if (WxType->ugly[index].f_valid == 1) { + WxType->ugly[index].f_valid = 2; + } else if (WxType->ugly[index].f_valid == 0) { + /* Table is not valid here so set value to missing? */ + /* No missing value, so use index = WxType->dataLen? */ + /* No... set f_valid to 3 so we know we used this + * invalid element, then handle it in degrib2.c :: + * ReadGrib2Record() where we set it back to 0. */ + WxType->ugly[index].f_valid = 3; + } + } + } + if (f_maxmin) { + if (value < attrib->min) { + attrib->min = value; + } else if (value > attrib->max) { + attrib->max = value; + } + } else { + attrib->min = attrib->max = value; + f_maxmin = 1; + } + *grib_Data++ = value; + } + } + } + } + attrib->f_maxmin = f_maxmin; +} + +/***************************************************************************** + * ParseGridPrimMiss() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * A helper function for ParseGrid. In this particular case it is dealing + * with a field that has primary missing value type. + * Walks through either a float or an integer grid, computing the min/max + * values in the grid, and converts the units. It uses gridAttrib info for the + * missing values and it updates gridAttrib with the observed min/max values. + * + * ARGUMENTS + * attrib = sect 5 structure already filled in by ParseSect5 (In/Output) + * grib_Data = The place to store the grid data. (Output) + * Nx, Ny = The dimensions of the grid (Input) + * iain = Place to find data if it is an Integer (or float). (Input) + * unitM = M in unit conversion equation y(new) = m x(orig) + b (Input) + * unitB = B in unit conversion equation y(new) = m x(orig) + b (Input) + * f_wxType = true if we have a valid wx type. (Input) + * WxType = table to look up values in. (Input) + * startX = The start of the X values. (Input) + * startY = The start of the Y values. (Input) + * subNx = The Nx dimmension of the subgrid (Input) + * subNy = The Ny dimmension of the subgrid (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 12/2002 Arthur Taylor (MDL/RSIS): Created to optimize part of ParseGrid. + * 5/2003 AAT: Added ability to see if wxType occurs. If so sets table + * valid to 2, otherwise leaves it at 1. If table valid is 0 then + * sets value to missing value (if applicable). + * 2/2004 AAT: Added the subgrid capability. + * + * NOTES + * 1) Don't have to check if value became missing value, because we can check + * if missing falls in the range of the min/max converted units. If + * missing does fall in that range we need to move missing. + * (See f_readjust in ParseGrid) + ***************************************************************************** + */ +static void ParseGridPrimMiss (gridAttribType *attrib, double *grib_Data, + sInt4 Nx, sInt4 Ny, sInt4 *iain, + double unitM, double unitB, sInt4 *missCnt, + uChar f_wxType, sect2_WxType *WxType, + int startX, int startY, int subNx, int subNy) +{ + sInt4 x, y; /* Where we are in the grid. */ + double value; /* The data in the new units. */ + uChar f_maxmin = 0; /* Flag if max/min is valid yet. */ + uInt4 index; /* Current index into Wx table. */ + sInt4 *itemp = NULL; + float *ftemp = NULL; +/* float *ain = (float *) iain;*/ + + /* Resolve possibility that the data is an integer or a float and find + * max/min values. (see note 1) */ + for (y = 0; y < subNy; y++) { + if (((startY + y - 1) < 0) || ((startY + y - 1) >= Ny)) { + for (x = 0; x < subNx; x++) { + *grib_Data++ = attrib->missPri; + (*missCnt)++; + } + } else { + if (attrib->fieldType) { + itemp = iain + (startY + y - 1) * Nx + (startX - 1); + } else { + ftemp = ((float *) iain) + (startY + y - 1) * Nx + (startX - 1); + } + for (x = 0; x < subNx; x++) { + if (((startX + x - 1) < 0) || ((startX + x - 1) >= Nx)) { + *grib_Data++ = attrib->missPri; + (*missCnt)++; + } else { + if (attrib->fieldType) { + value = (*itemp++); + } else { + value = (*ftemp++); + } + + /* Make sure value is not a missing value when converting + * units, and while computing max/min. */ + if (value == attrib->missPri) { + (*missCnt)++; + } else { + /* Convert the units. */ + if (unitM == -10) { + value = pow (10.0, value); + } else { + value = unitM * value + unitB; + } + if (f_wxType) { + index = (uInt4) value; + if (index < WxType->dataLen) { + if (WxType->ugly[index].f_valid) { + WxType->ugly[index].f_valid = 2; + } else { + /* Table is not valid here so set value to missPri + */ + value = attrib->missPri; + (*missCnt)++; + } + } + } + if ((!f_wxType) || (value != attrib->missPri)) { + if (f_maxmin) { + if (value < attrib->min) { + attrib->min = value; + } else if (value > attrib->max) { + attrib->max = value; + } + } else { + attrib->min = attrib->max = value; + f_maxmin = 1; + } + } + } + *grib_Data++ = value; + } + } + } + } + attrib->f_maxmin = f_maxmin; +} + +/***************************************************************************** + * ParseGridSecMiss() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * A helper function for ParseGrid. In this particular case it is dealing + * with a field that has NO missing value type. + * Walks through either a float or an integer grid, computing the min/max + * values in the grid, and converts the units. It uses gridAttrib info for the + * missing values and it updates gridAttrib with the observed min/max values. + * + * ARGUMENTS + * attrib = sect 5 structure already filled in by ParseSect5 (In/Output) + * grib_Data = The place to store the grid data. (Output) + * Nx, Ny = The dimensions of the grid (Input) + * iain = Place to find data if it is an Integer (or float). (Input) + * unitM = M in unit conversion equation y(new) = m x(orig) + b (Input) + * unitB = B in unit conversion equation y(new) = m x(orig) + b (Input) + * f_wxType = true if we have a valid wx type. (Input) + * WxType = table to look up values in. (Input) + * startX = The start of the X values. (Input) + * startY = The start of the Y values. (Input) + * subNx = The Nx dimmension of the subgrid (Input) + * subNy = The Ny dimmension of the subgrid (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 12/2002 Arthur Taylor (MDL/RSIS): Created to optimize part of ParseGrid. + * 5/2003 AAT: Added ability to see if wxType occurs. If so sets table + * valid to 2, otherwise leaves it at 1. If table valid is 0 then + * sets value to missing value (if applicable). + * 2/2004 AAT: Added the subgrid capability. + * + * NOTES + * 1) Don't have to check if value became missing value, because we can check + * if missing falls in the range of the min/max converted units. If + * missing does fall in that range we need to move missing. + * (See f_readjust in ParseGrid) + ***************************************************************************** + */ +static void ParseGridSecMiss (gridAttribType *attrib, double *grib_Data, + sInt4 Nx, sInt4 Ny, sInt4 *iain, + double unitM, double unitB, sInt4 *missCnt, + uChar f_wxType, sect2_WxType *WxType, + int startX, int startY, int subNx, int subNy) +{ + sInt4 x, y; /* Where we are in the grid. */ + double value; /* The data in the new units. */ + uChar f_maxmin = 0; /* Flag if max/min is valid yet. */ + uInt4 index; /* Current index into Wx table. */ + sInt4 *itemp = NULL; + float *ftemp = NULL; +/* float *ain = (float *) iain;*/ + + /* Resolve possibility that the data is an integer or a float and find + * max/min values. (see note 1) */ + for (y = 0; y < subNy; y++) { + if (((startY + y - 1) < 0) || ((startY + y - 1) >= Ny)) { + for (x = 0; x < subNx; x++) { + *grib_Data++ = attrib->missPri; + (*missCnt)++; + } + } else { + if (attrib->fieldType) { + itemp = iain + (startY + y - 1) * Nx + (startX - 1); + } else { + ftemp = ((float *) iain) + (startY + y - 1) * Nx + (startX - 1); + } + for (x = 0; x < subNx; x++) { + if (((startX + x - 1) < 0) || ((startX + x - 1) >= Nx)) { + *grib_Data++ = attrib->missPri; + (*missCnt)++; + } else { + if (attrib->fieldType) { + value = (*itemp++); + } else { + value = (*ftemp++); + } + + /* Make sure value is not a missing value when converting + * units, and while computing max/min. */ + if ((value == attrib->missPri) || (value == attrib->missSec)) { + (*missCnt)++; + } else { + /* Convert the units. */ + if (unitM == -10) { + value = pow (10.0, value); + } else { + value = unitM * value + unitB; + } + if (f_wxType) { + index = (uInt4) value; + if (index < WxType->dataLen) { + if (WxType->ugly[index].f_valid) { + WxType->ugly[index].f_valid = 2; + } else { + /* Table is not valid here so set value to missPri + */ + value = attrib->missPri; + (*missCnt)++; + } + } + } + if ((!f_wxType) || (value != attrib->missPri)) { + if (f_maxmin) { + if (value < attrib->min) { + attrib->min = value; + } else if (value > attrib->max) { + attrib->max = value; + } + } else { + attrib->min = attrib->max = value; + f_maxmin = 1; + } + } + } + *grib_Data++ = value; + } + } + } + } + attrib->f_maxmin = f_maxmin; +} + +/***************************************************************************** + * ParseGrid() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To walk through the 2 possible grids (and possible bitmap) created by + * UNPK_GRIB2, and combine the info into 1 grid, at the same time computing + * the min/max values in the grid. It uses gridAttrib info for the missing values + * and it then updates the gridAttrib structure for the min/max values that it + * found. + * It also uses scan, and ScanIndex2XY, to parse the data and organize the + * Grib_Data so that 0,0 is the lower left part of the grid, it then traverses + * the row and then moved up to the next row starting on the left. + * + * ARGUMENTS + * attrib = sect 5 structure already filled in by ParseSect5 (In/Output) + * Grib_Data = The place to store the grid data. (Output) + * grib_DataLen = The current size of Grib_Data (can increase) (Input/Output) + * Nx, Ny = The dimensions of the grid (Input) + * scan = How to walk through the original grid. (Input) + * iain = Place to find data if it is an Integer (or float). (Input) + * ibitmap = Flag stating the data has a bitmap for missing values (In) + * ib = Where to find the bitmap if we have one (Input) + * unitM = M in unit conversion equation y(new) = m x(orig) + b (Input) + * unitB = B in unit conversion equation y(new) = m x(orig) + b (Input) + * f_wxType = true if we have a valid wx type. (Input) + * WxType = table to look up values in. (Input) + * f_subGrid = True if we have a subgrid, false if not. (Input) + * startX stopX = The bounds of the subgrid in X. (0,-1) means full grid (In) + * startY stopY = The bounds of the subgrid in Y. (0,-1) means full grid (In) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 11/2002 AAT: Added unit conversion to metaparse.c + * 12/2002 AAT: Optimized first loop to make it assume scan 0100 (64) + * (valid 99.9%), but still have slow loop for generic case. + * 5/2003 AAT: Added ability to see if wxType occurs. If so sets table + * valid to 2, otherwise leaves it at 1. If table valid is 0 then + * sets value to missing value (if applicable). + * 7/2003 AAT: added check if f_maxmin before checking if missing was in + * range of max, min for "readjust" check. + * 2/2004 AAT: Added startX / startY / stopX / stopY + * 5/2004 AAT: Found out that I used the opposite definition for bitmap + * 0 = missing, 1 = valid. + * + * NOTES + ***************************************************************************** + */ +void ParseGrid (gridAttribType *attrib, double **Grib_Data, + uInt4 *grib_DataLen, uInt4 Nx, uInt4 Ny, int scan, + sInt4 *iain, sInt4 ibitmap, sInt4 *ib, double unitM, + double unitB, uChar f_wxType, sect2_WxType *WxType, + CPL_UNUSED uChar f_subGrid, + int startX, int startY, int stopX, int stopY) +{ + double xmissp; /* computed missing value needed for ibitmap = 1, + * Also used if unit conversion causes confusion + * over_ missing values. */ + double xmisss; /* Used if unit conversion causes confusion over + * missing values. */ + uChar f_readjust; /* True if unit conversion caused confusion over + * missing values. */ + uInt4 scanIndex; /* Where we are in the original grid. */ + sInt4 x, y; /* Where we are in a grid of scan value 0100 */ + sInt4 newIndex; /* x,y in a 1 dimensional array. */ + double value; /* The data in the new units. */ + double *grib_Data; /* A pointer to Grib_Data for ease of manipulation. */ + sInt4 missCnt = 0; /* Number of detected missing values. */ + uInt4 index; /* Current index into Wx table. */ + float *ain = (float *) iain; + uInt4 subNx; /* The Nx dimmension of the subgrid. */ + uInt4 subNy; /* The Ny dimmension of the subgrid. */ + + subNx = stopX - startX + 1; + subNy = stopY - startY + 1; + + myAssert (((!f_subGrid) && (subNx == Nx)) || (f_subGrid)); + myAssert (((!f_subGrid) && (subNy == Ny)) || (f_subGrid)); + + if (subNx * subNy > *grib_DataLen) { + *grib_DataLen = subNx * subNy; + *Grib_Data = (double *) realloc ((void *) (*Grib_Data), + (*grib_DataLen) * sizeof (double)); + } + grib_Data = *Grib_Data; + + /* Resolve possibility that the data is an integer or a float, find + * max/min values, and do unit conversion. (see note 1) */ + if (scan == 64) { + if (attrib->f_miss == 0) { + ParseGridNoMiss (attrib, grib_Data, Nx, Ny, iain, unitM, unitB, + f_wxType, WxType, startX, startY, subNx, subNy); + } else if (attrib->f_miss == 1) { + ParseGridPrimMiss (attrib, grib_Data, Nx, Ny, iain, unitM, unitB, + &missCnt, f_wxType, WxType, startX, startY, + subNx, subNy); + } else if (attrib->f_miss == 2) { + ParseGridSecMiss (attrib, grib_Data, Nx, Ny, iain, unitM, unitB, + &missCnt, f_wxType, WxType, startX, startY, subNx, + subNy); + } + } else { + /* Internally we use scan = 0100. Scan is usually 0100 from the + * unpacker library, but if scan is not, the following code converts + * it. We optimized the previous (scan 0100) case by calling a + * dedicated procedure. Here we don't since for scan != 0100, we + * would_ need a different unpacker library, which is extremely + * unlikely. */ + for (scanIndex = 0; scanIndex < Nx * Ny; scanIndex++) { + if (attrib->fieldType) { + value = iain[scanIndex]; + } else { + value = ain[scanIndex]; + } + /* Make sure value is not a missing value when converting units, and + * while computing max/min. */ + if ((attrib->f_miss == 0) || + ((attrib->f_miss == 1) && (value != attrib->missPri)) || + ((attrib->f_miss == 2) && (value != attrib->missPri) && + (value != attrib->missSec))) { + /* Convert the units. */ + if (unitM == -10) { + value = pow (10.0, value); + } else { + value = unitM * value + unitB; + } + /* Don't have to check if value became missing value, because we + * can check if missing falls in the range of min/max. If + * missing does fall in that range we need to move missing. See + * f_readjust */ + if (f_wxType) { + index = (uInt4) value; + if (index < WxType->dataLen) { + if (WxType->ugly[index].f_valid == 1) { + WxType->ugly[index].f_valid = 2; + } else if (WxType->ugly[index].f_valid == 0) { + /* Table is not valid here so set value to missPri */ + if (attrib->f_miss != 0) { + value = attrib->missPri; + missCnt++; + } else { + /* No missing value, so use index = WxType->dataLen */ + /* No... set f_valid to 3 so we know we used this + * invalid element, then handle it in degrib2.c :: + * ReadGrib2Record() where we set it back to 0. */ + WxType->ugly[index].f_valid = 3; + } + } + } + } + if ((!f_wxType) || + ((attrib->f_miss == 0) || (value != attrib->missPri))) { + if (attrib->f_maxmin) { + if (value < attrib->min) { + attrib->min = value; + } else if (value > attrib->max) { + attrib->max = value; + } + } else { + attrib->min = attrib->max = value; + attrib->f_maxmin = 1; + } + } + } else { + missCnt++; + } + ScanIndex2XY (scanIndex, &x, &y, scan, Nx, Ny); + /* ScanIndex returns value as if scan was 0100 */ + newIndex = (x - 1) + (y - 1) * Nx; + grib_Data[newIndex] = value; + } + } + + /* Deal with possibility that unit conversion ended up with valid numbers + * being interpreted as missing. */ + f_readjust = 0; + xmissp = attrib->missPri; + xmisss = attrib->missSec; + if (attrib->f_maxmin) { + if ((attrib->f_miss == 1) || (attrib->f_miss == 2)) { + if ((attrib->missPri >= attrib->min) && + (attrib->missPri <= attrib->max)) { + xmissp = attrib->max + 1; + f_readjust = 1; + } + if (attrib->f_miss == 2) { + if ((attrib->missSec >= attrib->min) && + (attrib->missSec <= attrib->max)) { + xmisss = attrib->max + 2; + f_readjust = 1; + } + } + } + } + + /* Walk through the grid, resetting the missing values, as determined by + * the original grid. */ + if (f_readjust) { + for (scanIndex = 0; scanIndex < Nx * Ny; scanIndex++) { + ScanIndex2XY (scanIndex, &x, &y, scan, Nx, Ny); + /* ScanIndex returns value as if scan was 0100 */ + newIndex = (x - 1) + (y - 1) * Nx; + if (attrib->fieldType) { + value = iain[scanIndex]; + } else { + value = ain[scanIndex]; + } + if (value == attrib->missPri) { + grib_Data[newIndex] = xmissp; + } else if ((attrib->f_miss == 2) && (value == attrib->missSec)) { + grib_Data[newIndex] = xmisss; + } + } + attrib->missPri = xmissp; + if (attrib->f_miss == 2) { + attrib->missSec = xmisss; + } + } + + /* Resolve bitmap (if there is one) in the data. */ + if (ibitmap) { + attrib->f_maxmin = 0; + if ((attrib->f_miss != 1) && (attrib->f_miss != 2)) { + missCnt = 0; + /* Figure out a missing value. */ + xmissp = 9999; + if (attrib->f_maxmin) { + if ((xmissp <= attrib->max) && (xmissp >= attrib->min)) { + xmissp = attrib->max + 1; + } + } + /* embed the missing value. */ + for (scanIndex = 0; scanIndex < Nx * Ny; scanIndex++) { + ScanIndex2XY (scanIndex, &x, &y, scan, Nx, Ny); + /* ScanIndex returns value as if scan was 0100 */ + newIndex = (x - 1) + (y - 1) * Nx; + /* Corrected this on 5/10/2004 */ + if (ib[scanIndex] != 1) { + grib_Data[newIndex] = xmissp; + missCnt++; + } else { + if (!attrib->f_maxmin) { + attrib->f_maxmin = 1; + attrib->max = attrib->min = grib_Data[newIndex]; + } else { + if (attrib->max < grib_Data[newIndex]) + attrib->max = grib_Data[newIndex]; + if (attrib->min > grib_Data[newIndex]) + attrib->min = grib_Data[newIndex]; + } + } + } + attrib->f_miss = 1; + attrib->missPri = xmissp; + } + if (!attrib->f_maxmin) { + attrib->f_maxmin = 1; + attrib->max = attrib->min = xmissp; + } + } + attrib->numMiss = missCnt; +} + +typedef struct { + double value; + int cnt; +} freqType; + +int freqCompare (const void *A, const void *B) +{ + const freqType *a = (freqType *) A; + const freqType *b = (freqType *) B; + + if (a->value < b->value) + return -1; + if (a->value > b->value) + return 1; + return 0; +} + +void FreqPrint (char **ans, double *Data, sInt4 DataLen, sInt4 Nx, + sInt4 Ny, sChar decimal, char *comment) +{ + int x, y, i; + double *ptr; + double value; + freqType *freq = NULL; + int numFreq = 0; + char format[20]; + + myAssert (*ans == NULL); + + if ((Nx < 0) || (Ny < 0) || (Nx * Ny > DataLen)) { + return; + } + + ptr = Data; + for (y = 0; y < Ny; y++) { + for (x = 0; x < Nx; x++) { + /* 2/28/2006 Introduced value to round before putting the data in + * the Freq table. */ + value = myRound (*ptr, decimal); + for (i = 0; i < numFreq; i++) { + if (value == freq[i].value) { + freq[i].cnt++; + break; + } + } + if (i == numFreq) { + numFreq++; + freq = (freqType *) realloc (freq, numFreq * sizeof (freqType)); + freq[i].value = value; + freq[i].cnt = 1; + } + ptr++; + } + } + + qsort (freq, numFreq, sizeof (freq[0]), freqCompare); + + mallocSprintf (ans, "%s | count\n", comment); + sprintf (format, "%%.%df | %%d\n", decimal); + for (i = 0; i < numFreq; i++) { + reallocSprintf (ans, format, myRound (freq[i].value, decimal), + freq[i].cnt); + } + free (freq); +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/metaprint.cpp b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/metaprint.cpp new file mode 100644 index 000000000..dc6d77056 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/metaprint.cpp @@ -0,0 +1,1409 @@ +/***************************************************************************** + * metaprint.c + * + * DESCRIPTION + * This file contains the code necessary to write out the meta data + * structure. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + * 1) Need to add support for GS3_ORTHOGRAPHIC = 90, + * GS3_EQUATOR_EQUIDIST = 110, GS3_AZIMUTH_RANGE = 120 + * 2) Need to add support for GS4_RADAR = 20, GS4_SATELLITE = 30 + ***************************************************************************** + */ +#include +#include +#include +#include +#include "meta.h" +#include "metaname.h" +#include "myerror.h" +#include "myutil.h" +#include "tdlpack.h" +#include "myassert.h" +#include "clock.h" + +/***************************************************************************** + * Lookup() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To lookup the string value in a table, given the table, the index, and + * some default values. + * + * ARGUMENTS + * table = The table to look in. (Input) + * n = Size of table. (Input) + * index = Index to look up. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: char * + * The desired index value, or the appropriate default message. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * Can not do a sizeof(table) here because table is now of arbitrary length. + * Instead do sizeof(table) in calling procedure. + ***************************************************************************** + */ +static const char *Lookup(const char **table, size_t n, size_t index) +{ + static const char *def[] = + { "Reserved", "Reserved for local use", "Missing" }; + if (index < (n / sizeof (char *))) { + return table[index]; + } else if (index < 192) { + return def[0]; + } else if (index < 255) { + return def[1]; + } else { + return def[2]; + } +} + +/***************************************************************************** + * Print() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To print the message to a local static array in a way similar to + * myerror.c::errSprintf. This allows us to pass the results back to Tcl/Tk, + * as well as to save it to disk. It also serves as a central place to + * change if we want a different style of output. + * + * The caller gives a series of calls with fmt != NULL, followed by + * a fmt == NULL. This last call will return the constructed message to the + * caller, and reset the message to NULL. It is caller's responsibility to + * free the message, and to make sure that last call to Print has fmt = NULL, + * so that the routine doesn't accidently keep memory. + * + * ARGUMENTS + * label = A label for this set of data. (Input) + * varName = A char string describing the variable.. (Input) + * fmt = Prt_NULL, Prt_D, Prt_DS, Prt_DSS, Prt_S, Prt_F, Prt_FS, Prt_E, + * Prt_ES. (Input) + * determines what to expect in the rest of the arguments. + * d = sInt4, s = char *, f = double, + * NULL = return the constructed answer, and reset answer to NULL. + * + * FILES/DATABASES: None + * + * RETURNS: char * + * NULL if (fmt != NULL) (ie we added to message) + * message if (fmt == NULL) (ie return the message). + * It is caller's responsibility to free the message, and to make sure + * that last call to Print has fmt = NULL. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 4/2003 AAT: Changed so it could print different types of labels + * "GDS" instead of "S3" + * 10/2004 AAT: Added Prt_SS + * + * NOTES + * Using enumerated type instead of "ds" "dss" etc. For speed considerations. + ***************************************************************************** + */ +char *Print(const char *label, const char *varName, Prt_TYPE fmt, ...) +{ + static char *buffer = NULL; /* Copy of message generated so far. */ + va_list ap; /* pointer to variable argument list. */ + sInt4 lival; /* Store a sInt4 val from argument list. */ + char *sval; /* Store a string val from argument. */ + char *unit; /* Second string val is usually a unit string. */ + double dval; /* Store a double val from argument list. */ + char *ans; /* Final message to return if fmt = Prt_NULL. */ + + if (fmt == Prt_NULL) { + ans = buffer; + buffer = NULL; + return ans; + } + va_start (ap, fmt); /* make ap point to 1st unnamed arg. */ + switch (fmt) { + case Prt_D: + lival = va_arg (ap, sInt4); + reallocSprintf (&buffer, "%s | %s | %ld\n", label, varName, lival); + break; + case Prt_DS: + lival = va_arg (ap, sInt4); + sval = va_arg (ap, char *); + reallocSprintf (&buffer, "%s | %s | %ld (%s)\n", label, varName, + lival, sval); + break; + case Prt_DSS: + lival = va_arg (ap, sInt4); + sval = va_arg (ap, char *); + unit = va_arg (ap, char *); + reallocSprintf (&buffer, "%s | %s | %ld (%s [%s])\n", label, + varName, lival, sval, unit); + break; + case Prt_S: + sval = va_arg (ap, char *); + reallocSprintf (&buffer, "%s | %s | %s\n", label, varName, sval); + break; + case Prt_SS: + sval = va_arg (ap, char *); + unit = va_arg (ap, char *); + reallocSprintf (&buffer, "%s | %s | %s (%s)\n", label, varName, + sval, unit); + break; + case Prt_F: + dval = va_arg (ap, double); + reallocSprintf (&buffer, "%s | %s | %f\n", label, varName, dval); + break; + case Prt_E: + dval = va_arg (ap, double); + reallocSprintf (&buffer, "%s | %s | %e\n", label, varName, dval); + break; + case Prt_G: + dval = va_arg (ap, double); + reallocSprintf (&buffer, "%s | %s | %g\n", label, varName, dval); + break; + case Prt_FS: + dval = va_arg (ap, double); + unit = va_arg (ap, char *); + reallocSprintf (&buffer, "%s | %s | %f (%s)\n", label, varName, + dval, unit); + break; + case Prt_ES: + dval = va_arg (ap, double); + unit = va_arg (ap, char *); + reallocSprintf (&buffer, "%s | %s | %e (%s)\n", label, varName, + dval, unit); + break; + case Prt_GS: + dval = va_arg (ap, double); + unit = va_arg (ap, char *); + reallocSprintf (&buffer, "%s | %s | %g (%s)\n", label, varName, + dval, unit); + break; + default: + reallocSprintf (&buffer, "ERROR: Invalid Print option '%d'\n", fmt); + } + va_end (ap); /* clean up when done. */ + return NULL; +} + +/***************************************************************************** + * PrintSect1() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To generate the message for GRIB2 section 1. + * + * ARGUMENTS + * pds2 = The GRIB2 Product Definition Section to print. (Input) + * center = The Center that created the data (Input) + * subcenter = The Sub Center that created the data (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 4/2003 AAT: Changed to accept pointer to pdsG2Type pds2 + * 10/2005 AAT: Adjusted to take center, subcenter as we moved that out of + * the pdsG2 type. + * + * NOTES + ***************************************************************************** + */ +static void PrintSect1 (pdsG2Type * pds2, unsigned short int center, + unsigned short int subcenter) +{ + /* Based on Grib2 Code Table 1.2 */ + static const char *table12[] = { "Analysis", "Start of Forecast", + "Verifying time of forecast", "Observation time" + }; + + /* Based on Grib2 Code Table 1.3 */ + static const char *table13[] = { "Operational products", + "Operational test products", "Research products", + "Re-analysis products" + }; + + /* Based on Grib2 Code Table 1.4 */ + static const char *table14[] = { "Analysis products", + "Forecast products", "Analysis and forecast products", + "Control forecast products", "Perturbed forecast products", + "Control and perturbed forecast products", + "Processed satellite observations", "Processed radar observations" + }; + + char buffer[25]; /* Stores format of pds2->refTime. */ + const char *ptr; + + ptr = centerLookup (center); + if (ptr != NULL) { + Print ("PDS-S1", "Originating center", Prt_DS, center, ptr); + } else { + Print ("PDS-S1", "Originating center", Prt_D, center); + } + if (subcenter != GRIB2MISSING_u2) { + ptr = subCenterLookup (center, subcenter); + if (ptr != NULL) { + Print ("PDS-S1", "Originating sub-center", Prt_DS, subcenter, ptr); + } else { + Print ("PDS-S1", "Originating sub-center", Prt_D, subcenter); + } + } + Print ("PDS-S1", "GRIB Master Tables Version", Prt_D, pds2->mstrVersion); + Print ("PDS-S1", "GRIB Local Tables Version", Prt_D, pds2->lclVersion); + Print ("PDS-S1", "Significance of reference time", Prt_DS, pds2->sigTime, + Lookup (table12, sizeof (table12), pds2->sigTime)); + +/* strftime (buffer, 25, "%m/%d/%Y %H:%M:%S UTC", gmtime (&(pds2->refTime)));*/ + Clock_Print (buffer, 25, pds2->refTime, "%m/%d/%Y %H:%M:%S UTC", 0); + + Print ("PDS-S1", "Reference Time", Prt_S, buffer); + Print ("PDS-S1", "Operational Status", Prt_DS, pds2->operStatus, + Lookup (table13, sizeof (table13), pds2->operStatus)); + Print ("PDS-S1", "Type of Data", Prt_DS, pds2->dataType, + Lookup (table14, sizeof (table14), pds2->dataType)); +} + +/***************************************************************************** + * PrintSect2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To generate a message for the section 2 data. This may be more + * appropriate in its own file (particularly the weather table.) + * + * ARGUMENTS + * sect2 = The sect2 structure to print (initialized by ParseSect2). (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 2/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +static void PrintSect2 (sect2_type * sect2) +{ + size_t i; /* loop counter over number of sect2 data. */ + char buffer[25]; /* Assists with labeling. */ + + switch (sect2->ptrType) { + case GS2_WXTYPE: + Print ("PDS-S2", "Number of Elements in Section 2", Prt_D, + sect2->wx.dataLen); + for (i = 0; i < sect2->wx.dataLen; i++) { + if (sect2->wx.ugly[i].validIndex != -1) { + sprintf (buffer, "Elem %3d Is Used", (int) i); + } else { + sprintf (buffer, "Elem %3d NOT Used", (int) i); + } + Print ("PDS-S2", buffer, Prt_S, sect2->wx.data[i]); + } + break; + case GS2_UNKNOWN: + Print ("PDS-S2", "Number of Elements in Section 2", Prt_D, + sect2->unknown.dataLen); + for (i = 0; i < sect2->unknown.dataLen; i++) { + sprintf (buffer, "Element %d", (int) i); + Print ("PDS-S2", buffer, Prt_F, sect2->unknown.data[i]); + } + break; + default: + return; + } +} + +/***************************************************************************** + * PrintSect4_Category() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To generate the category message for section 4. + * + * ARGUMENTS + * meta = The meta file structure to generate the message for. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 6/2003 Arthur Taylor (MDL/RSIS): Extracted this part from PrintSect4(). + * 1/2004 AAT: Combined PrintSect4_Meteo and PrintSect4_Ocean into this. + * 5/2004 AAT: Found out that I skipped "Momentum Probabilities" in tbl41_0. + * + * NOTES + ***************************************************************************** + */ +static void PrintSect4_Category (grib_MetaData *meta) +{ + sect4_type *sect4 = &(meta->pds2.sect4); + + /* Based on Grib2 Code Table 4.1 discipline 0 */ + static const char *tbl41_0[] = { + "Temperature", "Moisture", "Momentum", "Mass", "Short-wave Radiation", + "Long-wave Radiation", "Cloud", "Thermodynamic Stability indicies", + "Kinematic Stability indicies", "Temperature Probabilities", + "Moisture Probabilities", "Momentum Probabilities", + "Mass Probabilities", "Aerosols", "Trace gases (e.g. ozone, C02)", + "Radar", "Forecast Radar Imagery", "Electro-dynamics", + "Nuclear/radiology", "Physical atmospheric properties" + }; + /* Based on Grib2 Code Table 4.1 discipline 1 */ + static const char *tbl41_1[] = { + "Hydrology basic products", "Hydrology probabilities" + }; + /* Based on Grib2 Code Table 4.1 discipline 2 */ + static const char *tbl41_2[] = { + "Vegetation/Biomass", "Agri-/aquacultural Special Products", + "Transportation-related Products", "Soil Products" + }; + /* Based on Grib2 Code Table 4.1 discipline 3 */ + static const char *tbl41_3[] = { + "Image format products", "Quantitative products" + }; + /* Based on Grib2 Code Table 4.1 discipline 10 */ + static const char *tbl41_10[] = { + "Waves", "Currents", "Ice", "Surface Properties", + "Sub-surface Properties" + }; + + switch (meta->pds2.prodType) { + case 0: /* Meteo category. */ + switch (sect4->cat) { + case 190: + Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat, + "CCITT IA5 string"); + break; + case 191: + Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat, + "Miscellaneous"); + break; + default: + Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat, + Lookup (tbl41_0, sizeof (tbl41_0), sect4->cat)); + } + break; + case 1: /* Hydrological */ + Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat, + Lookup (tbl41_1, sizeof (tbl41_1), sect4->cat)); + break; + case 2: /* Land surface */ + Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat, + Lookup (tbl41_2, sizeof (tbl41_2), sect4->cat)); + break; + case 3: /* Space */ + Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat, + Lookup (tbl41_3, sizeof (tbl41_3), sect4->cat)); + break; + case 10: /* Oceanographic */ + Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat, + Lookup (tbl41_10, sizeof (tbl41_10), sect4->cat)); + break; + default: + Print ("PDS-S4", "PrintSect4() does not handle this prodType", + Prt_D, meta->pds2.prodType); + } +} + +/***************************************************************************** + * PrintSect4() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To generate the message for section 4. + * + * ARGUMENTS + * meta = The meta file structure to generate the message for. (Input) + * f_unit = 0 (GRIB unit), 1 (english), 2 (metric) (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 if no error. + * -2 if asked to print data for a template that we don't support. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 2/2003 AAT: Adjusted the interpretation of the scale vactor and value. + * to be consistent with what Matt found from email conversations + * with WMO GRIB2 experts. + * 2/2003 AAT: Switched from: value / pow (10, factor) + * to: value * pow (10, -1 * factor) + * 6/2003 AAT: Extracted the prodType = 0 only info and put in _Meteo + * 6/2003 AAT: Created a PrintSect4_Ocean for ocean stuff. + * 1/2004 AAT: Updated table 4.7 + * 1/2004 AAT: Modified to use "comment" from metaname.c instead of + * PrintSect4_Meteo, and PrintSect4_Ocean (that way local tables are + * enabled.) (Fixed meta file for PoP12). + * 3/2004 AAT: Added emphasis on "computation" of unit conversion. + * 3/2005 AAT: Added support for GS4_PROBABIL_PNT + * + * NOTES + * Need to add support for GS4_RADAR = 20 + ***************************************************************************** + */ +static int PrintSect4 (grib_MetaData *meta, sChar f_unit) +{ + sect4_type *sect4 = &(meta->pds2.sect4); + /* Based on Grib2 Code Table 4.0 */ + static const char *tbl40[] = { + "Analysis at a horizontal layer at a point in time", + "Individual ensemble forecast at a horizontal layer at a point in time", + "Derived forecast based on ensemble members at a horizontal layer at a" + " point in time", + "Probability forecast at a horizontal layer or level at a point in " + "time", + "Statistically processed data at a horizontal layer or level in a time" + " interval", + "Probability forecast at a horizontal layer or level in a time " + "interval", + "Percentile forecasts at a horizontal layer or level in a time " + "interval", + "Individual ensemble forecast at a horizontal layer or level in a time" + " interval", + "Derived forecasts based in all ensemble members at a horizontal level " + "or layer in a time interval", + "Radar product", "Satellite product" + }; + + /* Based on Grib2 Code Table 4.3 */ + static const char *tbl43[] = { + "Analysis", "Initialization", "Forecast", "Bias corrected forecast", + "Ensemble forecast", "Probability forecast", "Forecast error", + "Analysis error", "Observation" + }; + + /* Based on Grib2 Code Table 4.4 */ + static const char *tbl44[] = { + "Minute", "Hour", "Day", "Month", "Year", "Decade", + "Normal (30 years)", "Century", "Reserved", "Reserved", + "3 hours", "6 hours", "12 hours", "Second" + }; + + /* Based on Grib2 Code Table 4.5 */ + /* See "metaname.c :: Surface[]" */ + + /* Based on Grib2 Code Table 4.6 */ + static const char *tbl46[] = { + "Unperturbed high-resolution control forecast", + "Unperturbed low-reosulution control foreacst", + "Negatively perturbed forecast", "Positively perturbed forecast" + }; + + /* Based on Grib2 Code Table 4.7 */ + static const char *tbl47[] = { + "Unweighted mean of all members", "Weighted mean of all members", + "Standard deviation with respect to cluster mean", + "Standard deviation with respect to cluster mean, normalized", + "Spread of all members", + "Large anomally index of all memebers", + "Unweighted mean of the cluster members" + }; + + /* Based on Grib2 Code Table 4.9 */ + static const char *tbl49[] = { + "Probability of event below lower limit", + "Probability of event above upper limit", + "Probability of event between limits (include lower, exclude upper)", + "Probability of event above lower limit", + "Probability of event below upper limit" + }; + + /* Based on Grib2 Code Table 4.10 */ + static const char *tbl410[] = { + "Average", "Accumulation", "Maximum", "Minimum", + "Difference (Value at end of time minus beginning)", + "Root mean square", "Standard deviation", + "Covariance (Temporal variance)", + "Difference (Value at beginning of time minus end)", "Ratio" + }; + + /* Based on Grib2 Code Table 4.11 */ + static const char *tbl411[] = { + "Reserved", + "Successive times; same forecast time, start time incremented", + "Successive times; same start time, forecast time incremented", + "Successive times; start time incremented, forecast time decremented, " + "valid time constant", + "Successive times; start time decremented, forecast time incremented, " + "valid time constant", + "Floating subinterval of time between forecast time, and end" + }; + + char buffer[50]; /* Temp storage for various uses including time + * format. */ + int i; /* counter for templat 4.8/4.9 for num time range + * specs. */ + int f_reserved; /* Whether Table4.5 is a reserved entry or not. */ + GRIB2SurfTable surf; /* Surface look up in Table4.5. */ + const char *ptr; + + switch (sect4->templat) { + case GS4_ANALYSIS: + case GS4_ENSEMBLE: + case GS4_DERIVED: + Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, + tbl40[sect4->templat]); + break; + case GS4_PROBABIL_PNT: + Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[3]); + break; + case GS4_STATISTIC: + Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[4]); + break; + case GS4_PROBABIL_TIME: + Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[5]); + break; + case GS4_PERCENTILE: + Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[6]); + break; + case GS4_ENSEMBLE_STAT: + Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[7]); + break; + case GS4_DERIVED_INTERVAL: + Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[8]); + break; +/* + * The following lines were removed until such time that the rest of this + * procedure can properly handle this template type. + * + case GS4_RADAR: + Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[9]); + break; +*/ + case GS4_SATELLITE: + Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[10]); + break; + default: + Print ("PDS-S4", "Product type", Prt_D, sect4->templat); + errSprintf ("Un-supported Sect4 template %ld\n", sect4->templat); + return -2; + } + + PrintSect4_Category (meta); + Print ("PDS-S4", "Category Sub-Description", Prt_DS, sect4->subcat, + meta->comment); + + if (f_unit == 1) { + Print ("PDS-S4", "Output grid, (COMPUTED) english unit is", Prt_S, + meta->unitName); + } else if (f_unit == 2) { + Print ("PDS-S4", "Output grid, (COMPUTED) metric unit is", Prt_S, + meta->unitName); + } + Print ("PDS-S4", "Generation process", Prt_DS, sect4->genProcess, + Lookup (tbl43, sizeof (tbl43), sect4->genProcess)); + if (sect4->templat == GS4_SATELLITE) { + Print ("PDS-S4", "Observation generating process", Prt_D, sect4->genID); + Print ("PDS-S4", "Number of contributing spectral bands", Prt_D, + sect4->numBands); + for (i = 0; i < sect4->numBands; i++) { + Print ("PDS-S4", "Satellite series", Prt_D, sect4->bands[i].series); + Print ("PDS-S4", "Satellite numbers", Prt_D, + sect4->bands[i].numbers); + Print ("PDS-S4", "Instrument type", Prt_D, sect4->bands[i].instType); + Print ("PDS-S4", "Scale Factor of central wave number", Prt_D, + sect4->bands[i].centWaveNum.factor); + Print ("PDS-S4", "Scale Value of central wave number", Prt_D, + sect4->bands[i].centWaveNum.value); + } + return 0; + } + if (sect4->bgGenID != GRIB2MISSING_u1) { + ptr = processLookup (meta->center, sect4->bgGenID); + if (ptr != NULL) { + Print ("PDS-S4", "Background generating process ID", Prt_DS, + sect4->bgGenID, ptr); + } else { + Print ("PDS-S4", "Background generating process ID", Prt_D, + sect4->bgGenID); + } + } + if (sect4->genID != GRIB2MISSING_u1) { + ptr = processLookup (meta->center, sect4->genID); + if (ptr != NULL) { + Print ("PDS-S4", "Forecast generating process ID", Prt_DS, + sect4->genID, ptr); + } else { + Print ("PDS-S4", "Forecast generating process ID", Prt_D, + sect4->genID); + } + } + if (sect4->f_validCutOff) { + Print ("PDS-S4", "Data cut off after reference time in seconds", Prt_D, + sect4->cutOff); + } + Print ("PDS-S4", "Forecast time in hours", Prt_F, + (double) (sect4->foreSec / 3600.)); + surf = Table45Index (sect4->fstSurfType, &f_reserved, meta->center, + meta->subcenter); + Print ("PDS-S4", "Type of first fixed surface", Prt_DSS, + sect4->fstSurfType, surf.comment, surf.unit); + Print ("PDS-S4", "Value of first fixed surface", Prt_F, + sect4->fstSurfValue); + if (sect4->sndSurfType != GRIB2MISSING_u1) { + surf = Table45Index (sect4->sndSurfType, &f_reserved, meta->center, + meta->subcenter); + Print ("PDS-S4", "Type of second fixed surface", Prt_DSS, + sect4->sndSurfType, surf.comment, surf.unit); + Print ("PDS-S4", "Value of second fixed surface", Prt_F, + sect4->sndSurfValue); + } + switch (sect4->templat) { + case GS4_ANALYSIS: + break; + case GS4_ENSEMBLE: + Print ("PDS-S4", "Type of Ensemble forecast", Prt_DS, + sect4->typeEnsemble, + Lookup (tbl46, sizeof (tbl46), sect4->typeEnsemble)); + Print ("PDS-S4", "Perturbation number", Prt_D, sect4->perturbNum); + Print ("PDS-S4", "Number of forecasts in ensemble", Prt_D, + sect4->numberFcsts); + break; + case GS4_ENSEMBLE_STAT: + Print ("PDS-S4", "Type of Ensemble forecast", Prt_DS, + sect4->typeEnsemble, + Lookup (tbl46, sizeof (tbl46), sect4->typeEnsemble)); + Print ("PDS-S4", "Perturbation number", Prt_D, sect4->perturbNum); + Print ("PDS-S4", "Number of forecasts in ensemble", Prt_D, + sect4->numberFcsts); + Clock_Print (buffer, 100, sect4->validTime, "%m/%d/%Y %H:%M:%S UTC", + 0); + Print ("PDS-S4", "End of overall time interval", Prt_S, buffer); + Print ("PDS-S4", "Total number of missing values", Prt_D, + sect4->numMissing); + Print ("PDS-S4", "Number of time range specifications", Prt_D, + sect4->numInterval); + for (i = 0; i < sect4->numInterval; i++) { + Print ("PDS-S4", "Interval number", Prt_D, i + 1); + Print ("PDS-S4", "Statistical process", Prt_DS, + sect4->Interval[i].processID, + Lookup (tbl410, sizeof (tbl410), + sect4->Interval[i].processID)); + Print ("PDS-S4", "Type of time increment", Prt_DS, + sect4->Interval[i].incrType, + Lookup (tbl411, sizeof (tbl411), + sect4->Interval[i].incrType)); + /* Following is so we get "# str" not "# (str)" */ + sprintf (buffer, "%d %s", sect4->Interval[i].lenTime, + Lookup (tbl44, sizeof (tbl44), + sect4->Interval[i].timeRangeUnit)); + Print ("PDS-S4", "Time range for processing", Prt_S, buffer); + /* Following is so we get "# str" not "# (str)" */ + sprintf (buffer, "%d %s", sect4->Interval[i].timeIncr, + Lookup (tbl44, sizeof (tbl44), + sect4->Interval[i].incrUnit)); + Print ("PDS-S4", "Time increment", Prt_S, buffer); + } + break; + case GS4_DERIVED: + Print ("PDS-S4", "Derived forecast", Prt_DS, sect4->derivedFcst, + Lookup (tbl47, sizeof (tbl47), sect4->derivedFcst)); + Print ("PDS-S4", "Number of forecasts in ensemble", Prt_D, + sect4->numberFcsts); + break; + case GS4_DERIVED_INTERVAL: + Print ("PDS-S4", "Derived forecast", Prt_DS, sect4->derivedFcst, + Lookup (tbl47, sizeof (tbl47), sect4->derivedFcst)); + Print ("PDS-S4", "Number of forecasts in ensemble", Prt_D, + sect4->numberFcsts); + Clock_Print (buffer, 100, sect4->validTime, "%m/%d/%Y %H:%M:%S UTC", + 0); + + Print ("PDS-S4", "End of overall time interval", Prt_S, buffer); + Print ("PDS-S4", "Total number of missing values", Prt_D, + sect4->numMissing); + Print ("PDS-S4", "Number of time range specifications", Prt_D, + sect4->numInterval); + for (i = 0; i < sect4->numInterval; i++) { + Print ("PDS-S4", "Interval number", Prt_D, i + 1); + Print ("PDS-S4", "Statistical process", Prt_DS, + sect4->Interval[i].processID, + Lookup (tbl410, sizeof (tbl410), + sect4->Interval[i].processID)); + Print ("PDS-S4", "Type of time increment", Prt_DS, + sect4->Interval[i].incrType, + Lookup (tbl411, sizeof (tbl411), + sect4->Interval[i].incrType)); + /* Following is so we get "# str" not "# (str)" */ + sprintf (buffer, "%d %s", sect4->Interval[i].lenTime, + Lookup (tbl44, sizeof (tbl44), + sect4->Interval[i].timeRangeUnit)); + Print ("PDS-S4", "Time range for processing", Prt_S, buffer); + /* Following is so we get "# str" not "# (str)" */ + sprintf (buffer, "%d %s", sect4->Interval[i].timeIncr, + Lookup (tbl44, sizeof (tbl44), + sect4->Interval[i].incrUnit)); + Print ("PDS-S4", "Time increment", Prt_S, buffer); + } + break; + case GS4_PROBABIL_PNT: + Print ("PDS-S4", "Forecast Probability Number", Prt_D, + sect4->foreProbNum); + Print ("PDS-S4", "Total Number of Forecast Probabilities", Prt_D, + sect4->numForeProbs); + Print ("PDS-S4", "Probability type", Prt_DS, sect4->probType, + Lookup (tbl49, sizeof (tbl49), sect4->probType)); + sprintf (buffer, "%d, %d", sect4->lowerLimit.value, + sect4->lowerLimit.factor); + Print ("PDS-S4", "Lower limit (scale value, scale factor)", Prt_GS, + sect4->lowerLimit.value * + pow (10.0, -1 * sect4->lowerLimit.factor), buffer); + sprintf (buffer, "%d, %d", sect4->upperLimit.value, + sect4->upperLimit.factor); + Print ("PDS-S4", "Upper limit (scale value, scale factor)", Prt_GS, + sect4->upperLimit.value * + pow (10.0, -1 * sect4->upperLimit.factor), buffer); +/* printf ("Hello world 1\n");*/ + break; + case GS4_PERCENTILE: + Print ("PDS-S4", "Percentile", Prt_DS, sect4->percentile, "[%]"); +/* strftime (buffer, 100, "%m/%d/%Y %H:%M:%S UTC", + gmtime (&(sect4->validTime)));*/ + Clock_Print (buffer, 100, sect4->validTime, "%m/%d/%Y %H:%M:%S UTC", + 0); + + Print ("PDS-S4", "End of overall time interval", Prt_S, buffer); + Print ("PDS-S4", "Total number of missing values", Prt_D, + sect4->numMissing); + Print ("PDS-S4", "Number of time range specifications", Prt_D, + sect4->numInterval); + for (i = 0; i < sect4->numInterval; i++) { + Print ("PDS-S4", "Interval number", Prt_D, i + 1); + Print ("PDS-S4", "Statistical process", Prt_DS, + sect4->Interval[i].processID, + Lookup (tbl410, sizeof (tbl410), + sect4->Interval[i].processID)); + Print ("PDS-S4", "Type of time increment", Prt_DS, + sect4->Interval[i].incrType, + Lookup (tbl411, sizeof (tbl411), + sect4->Interval[i].incrType)); + /* Following is so we get "# str" not "# (str)" */ + sprintf (buffer, "%d %s", sect4->Interval[i].lenTime, + Lookup (tbl44, sizeof (tbl44), + sect4->Interval[i].timeRangeUnit)); + Print ("PDS-S4", "Time range for processing", Prt_S, buffer); + /* Following is so we get "# str" not "# (str)" */ + sprintf (buffer, "%d %s", sect4->Interval[i].timeIncr, + Lookup (tbl44, sizeof (tbl44), + sect4->Interval[i].incrUnit)); + Print ("PDS-S4", "Time increment", Prt_S, buffer); + } + break; + case GS4_PROBABIL_TIME: + Print ("PDS-S4", "Forecast Probability Number", Prt_D, + sect4->foreProbNum); + Print ("PDS-S4", "Total Number of Forecast Probabilities", Prt_D, + sect4->numForeProbs); + Print ("PDS-S4", "Probability type", Prt_DS, sect4->probType, + Lookup (tbl49, sizeof (tbl49), sect4->probType)); + sprintf (buffer, "%d, %d", sect4->lowerLimit.value, + sect4->lowerLimit.factor); + Print ("PDS-S4", "Lower limit (scale value, scale factor)", Prt_GS, + sect4->lowerLimit.value * + pow (10.0, -1 * sect4->lowerLimit.factor), buffer); + sprintf (buffer, "%d, %d", sect4->upperLimit.value, + sect4->upperLimit.factor); + Print ("PDS-S4", "Upper limit (scale value, scale factor)", Prt_GS, + sect4->upperLimit.value * + pow (10.0, -1 * sect4->upperLimit.factor), buffer); + /* Intentionally fall through. */ + case GS4_STATISTIC: +/* strftime (buffer, 100, "%m/%d/%Y %H:%M:%S UTC", + gmtime (&(sect4->validTime)));*/ + Clock_Print (buffer, 100, sect4->validTime, "%m/%d/%Y %H:%M:%S UTC", + 0); + + Print ("PDS-S4", "End of overall time interval", Prt_S, buffer); + Print ("PDS-S4", "Total number of missing values", Prt_D, + sect4->numMissing); + Print ("PDS-S4", "Number of time range specifications", Prt_D, + sect4->numInterval); + for (i = 0; i < sect4->numInterval; i++) { + Print ("PDS-S4", "Interval number", Prt_D, i + 1); + Print ("PDS-S4", "Statistical process", Prt_DS, + sect4->Interval[i].processID, + Lookup (tbl410, sizeof (tbl410), + sect4->Interval[i].processID)); + Print ("PDS-S4", "Type of time increment", Prt_DS, + sect4->Interval[i].incrType, + Lookup (tbl411, sizeof (tbl411), + sect4->Interval[i].incrType)); + /* Following is so we get "# str" not "# (str)" */ + sprintf (buffer, "%d %s", sect4->Interval[i].lenTime, + Lookup (tbl44, sizeof (tbl44), + sect4->Interval[i].timeRangeUnit)); + Print ("PDS-S4", "Time range for processing", Prt_S, buffer); + /* Following is so we get "# str" not "# (str)" */ + sprintf (buffer, "%d %s", sect4->Interval[i].timeIncr, + Lookup (tbl44, sizeof (tbl44), + sect4->Interval[i].incrUnit)); + Print ("PDS-S4", "Time increment", Prt_S, buffer); + } + break; + default: + /* This case should have been handled in first switch statement of + * this procedure, but just in case... */ + errSprintf ("Un-supported Sect4 template %d\n", sect4->templat); + return -2; + } + return 0; +} + +/***************************************************************************** + * PrintPDS2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To generate the message for the Product Definition Sections of the GRIB2 + * Message. + * + * ARGUMENTS + * meta = The meta file structure to generate the message for. (Input) + * f_unit = 0 (GRIB unit), 1 (english), 2 (metric) (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +static int PrintPDS2 (grib_MetaData *meta, sChar f_unit) +{ + pdsG2Type *pds2 = &(meta->pds2); + /* Based on Grib2 Code Table 0.0 */ + static const char *table0[] = { + "Meteorological products", "Hydrological products", + "Land surface products", "Space products", "Oceanographic products" + }; + int ierr; /* The error code of a called routine */ + + /* Print the data from Section 0 */ + switch (pds2->prodType) { + case 10: /* Oceanographic Product. */ + Print ("PDS-S0", "DataType", Prt_DS, pds2->prodType, table0[4]); + break; + case 5: /* Reserved. */ + Print ("PDS-S0", "DataType", Prt_DS, pds2->prodType, + Lookup (table0, sizeof (table0), 191)); + break; + default: + Print ("PDS-S0", "DataType", Prt_DS, pds2->prodType, + Lookup (table0, sizeof (table0), pds2->prodType)); + } + PrintSect1 (pds2, meta->center, meta->subcenter); + PrintSect2 (&(pds2->sect2)); + if ((ierr = PrintSect4 (meta, f_unit)) != 0) { + return ierr; + } + return 0; +} + +/***************************************************************************** + * PrintPDS1() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To generate the message for the Product Definition Sections of the GRIB1 + * Message. + * + * ARGUMENTS + * pds1 = The GRIB1 Product Definition Section to print. (Input) + * comment = A description about this element. See GRIB1_Table2LookUp (Input) + * center = The Center that created the data (Input) + * subcenter = The Sub Center that created the data (Input) + * f_unit = The unit conversion method used on the output data (Input) + * unitName = The name of the output unit type. (Input) + * convert = Conversion method used. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created. + * 10/2005 AAT: Adjusted to take center, subcenter as we moved that out of + * the pdsG1 type. + * 11/2005 AAT: Added f_utit variable. + * + * NOTES + ***************************************************************************** + */ +static void PrintPDS1 (pdsG1Type *pds1, char *comment, + unsigned short int center, + unsigned short int subcenter, sChar f_unit, + char *unitName, int convert) +{ + char buffer[25]; /* Stores format of pds1->refTime. */ + const char *ptr; + + Print ("PDS-S1", "Parameter Tables Version", Prt_D, pds1->mstrVersion); + ptr = centerLookup (center); + if (ptr != NULL) { + Print ("PDS-S1", "Originating center", Prt_DS, center, ptr); + } else { + Print ("PDS-S1", "Originating center", Prt_D, center); + } + ptr = subCenterLookup (center, subcenter); + if (ptr != NULL) { + Print ("PDS-S1", "Originating sub-center", Prt_DS, subcenter, ptr); + } else { + Print ("PDS-S1", "Originating sub-center", Prt_D, subcenter); + } + ptr = processLookup (center, pds1->genProcess); + if (ptr != NULL) { + Print ("PDS-S1", "Generation process", Prt_DS, pds1->genProcess, ptr); + } else { + Print ("PDS-S1", "Generation process", Prt_D, pds1->genProcess); + } + Print ("PDS-S1", "Grid Identification Number", Prt_D, pds1->gridID); + Print ("PDS-S1", "Indicator of parameter and units", Prt_DS, pds1->cat, + comment); + if (convert != UC_NONE) { + if (f_unit == 1) { + Print ("PDS-S1", "Output grid, (COMPUTED) english unit is", Prt_S, + unitName); + } else if (f_unit == 2) { + Print ("PDS-S1", "Output grid, (COMPUTED) metric unit is", Prt_S, + unitName); + } + } + Print ("PDS-S1", "Type of fixed surface", Prt_D, pds1->levelType); + Print ("PDS-S1", "Value of fixed surface", Prt_D, pds1->levelVal); + +/* strftime (buffer, 25, "%m/%d/%Y %H:%M:%S UTC", gmtime (&(pds1->refTime))); */ + Clock_Print (buffer, 25, pds1->refTime, "%m/%d/%Y %H:%M:%S UTC", 0); + + Print ("PDS-S1", "Reference Time", Prt_S, buffer); + +/* strftime (buffer, 25, "%m/%d/%Y %H:%M:%S UTC", + gmtime (&(pds1->validTime))); */ + Clock_Print (buffer, 25, pds1->validTime, "%m/%d/%Y %H:%M:%S UTC", 0); + + Print ("PDS-S1", "Valid Time", Prt_S, buffer); + +/* strftime (buffer, 25, "%m/%d/%Y %H:%M:%S UTC", gmtime (&(pds1->P1))); */ + Clock_Print (buffer, 25, pds1->P1, "%m/%d/%Y %H:%M:%S UTC", 0); + + Print ("PDS-S1", "P1 Time", Prt_S, buffer); + +/* strftime (buffer, 25, "%m/%d/%Y %H:%M:%S UTC", gmtime (&(pds1->P2))); */ + Clock_Print (buffer, 25, pds1->P2, "%m/%d/%Y %H:%M:%S UTC", 0); + + Print ("PDS-S1", "P2 Time", Prt_S, buffer); + Print ("PDS-S1", "Time range indicator", Prt_D, pds1->timeRange); + Print ("PDS-S1", "Number included in average", Prt_D, pds1->Average); + Print ("PDS-S1", "Number missing from average or accumulation", Prt_D, + pds1->numberMissing); + + if (pds1->f_hasEns) { + Print ("PDS-S1", "Ensemble BitFlag (octet 29)", Prt_D, + pds1->ens.BitFlag); + Print ("PDS-S1", "Ensemble Application", Prt_D, pds1->ens.Application); + Print ("PDS-S1", "Ensemble Type", Prt_D, pds1->ens.Type); + Print ("PDS-S1", "Ensemble Number", Prt_D, pds1->ens.Number); + Print ("PDS-S1", "Ensemble ProdID", Prt_D, pds1->ens.ProdID); + Print ("PDS-S1", "Ensemble Smoothing", Prt_D, pds1->ens.Smooth); + } + if (pds1->f_hasProb) { + Print ("PDS-S1", "Prob Category", Prt_D, pds1->prob.Cat); + Print ("PDS-S1", "Prob Type", Prt_D, pds1->prob.Type); + Print ("PDS-S1", "Prob lower", Prt_F, pds1->prob.lower); + Print ("PDS-S1", "Prob upper", Prt_F, pds1->prob.upper); + } + if (pds1->f_hasCluster) { + Print ("PDS-S1", "Cluster Ens Size", Prt_D, pds1->cluster.ensSize); + Print ("PDS-S1", "Cluster Size", Prt_D, pds1->cluster.clusterSize); + Print ("PDS-S1", "Cluster Number", Prt_D, pds1->cluster.Num); + Print ("PDS-S1", "Cluster Method", Prt_D, pds1->cluster.Method); + Print ("PDS-S1", "Cluster North Latitude", Prt_F, pds1->cluster.NorLat); + Print ("PDS-S1", "Cluster South Latitude", Prt_F, pds1->cluster.SouLat); + Print ("PDS-S1", "Cluster East Longitude", Prt_F, pds1->cluster.EasLon); + Print ("PDS-S1", "Cluster West Longitude", Prt_F, pds1->cluster.WesLon); + sprintf (buffer, "'%10s'", pds1->cluster.Member); + Print ("PDS-S1", "Cluster Membership", Prt_S, buffer); + } +} + +/***************************************************************************** + * PrintGDS() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To generate the message for the Grid Definition Section. + * + * ARGUMENTS + * gds = The gds structure to print. (Input) + * version = The GRIB version number (so we know what type of projection) (In) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 if no error. + * -1 if asked to print a map projection that we don't support. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 4/2003 AAT: Switched from sect3 to gds + * 5/2003 AAT: Since the number for ProjectionType changed from GRIB1 to + * GRIB2, we use the GRIB2 internally, but for meta data we want to + * print the appropriate one. + * 5/2003 AAT: Decided to have 1,1 be lower left corner in .shp files. + * 10/2004 AAT: Added TDLP support. + * + * NOTES + * Need to add support for GS3_ORTHOGRAPHIC = 90, + * GS3_EQUATOR_EQUIDIST = 110, GS3_AZIMUTH_RANGE = 120 + ***************************************************************************** + */ +static int PrintGDS (gdsType *gds, int version) +{ + /* Based on Grib2 Code Table 3.1 */ + static const char *table31[] = { "Latitude/Longitude", "Mercator", + "Polar Stereographic", "Lambert Conformal", + "Space view perspective orthographic", + "Equatorial azimuthal equidistant projection", + "Azimuth-range projection" + }; + char buffer[50]; /* Temporary storage for info about scan flag. */ + + Print ("GDS", "Number of Points", Prt_D, gds->numPts); + switch (gds->projType) { + case GS3_LATLON: /* 0 */ + if (version == 1) { + Print ("GDS", "Projection Type", Prt_DS, GB1S2_LATLON, + table31[0]); + } else { + Print ("GDS", "Projection Type", Prt_DS, gds->projType, + table31[0]); + } + break; + case GS3_MERCATOR: /* 10 */ + if (version == 1) { + Print ("GDS", "Projection Type", Prt_DS, GB1S2_MERCATOR, + table31[1]); + } else if (version == -1) { + Print ("GDS", "Projection Type", Prt_DS, TDLP_MERCATOR, + table31[1]); + } else { + Print ("GDS", "Projection Type", Prt_DS, gds->projType, + table31[1]); + } + break; + case GS3_POLAR: /* 20 */ + if (version == 1) { + Print ("GDS", "Projection Type", Prt_DS, GB1S2_POLAR, table31[2]); + } else if (version == -1) { + Print ("GDS", "Projection Type", Prt_DS, TDLP_POLAR, table31[2]); + } else { + Print ("GDS", "Projection Type", Prt_DS, gds->projType, + table31[2]); + } + break; + case GS3_LAMBERT: /* 30 */ + if (version == 1) { + Print ("GDS", "Projection Type", Prt_DS, GB1S2_LAMBERT, + table31[3]); + } else if (version == -1) { + Print ("GDS", "Projection Type", Prt_DS, TDLP_LAMBERT, + table31[3]); + } else { + Print ("GDS", "Projection Type", Prt_DS, gds->projType, + table31[3]); + } + break; +/* + * The following lines were removed until such time that the rest of this + * procedure can properly handle these three projection types. + * + case GS3_ORTHOGRAPHIC: * 90 * + Print ("GDS", "Projection Type", Prt_DS, gds->projType, table31[4]); + break; + case GS3_EQUATOR_EQUIDIST: * 110 * + Print ("GDS", "Projection Type", Prt_DS, gds->projType, table31[5]); + break; + case GS3_AZIMUTH_RANGE: * 120 * + Print ("GDS", "Projection Type", Prt_DS, gds->projType, table31[6]); + break; +*/ + default: + Print ("GDS", "Projection Type", Prt_D, gds->projType); + errSprintf ("Un-supported Map Projection %d\n", gds->projType); + return -1; + } + if (gds->f_sphere) { + Print ("GDS", "Shape of Earth", Prt_S, "sphere"); + Print ("GDS", "Radius", Prt_FS, gds->majEarth, "km"); + } else { + Print ("GDS", "Shape of Earth", Prt_S, "oblate spheroid"); + Print ("GDS", "semi Major axis", Prt_FS, gds->majEarth, "km"); + Print ("GDS", "semi Minor axis", Prt_FS, gds->minEarth, "km"); + } + Print ("GDS", "Nx (Number of points on parallel)", Prt_D, gds->Nx); + Print ("GDS", "Ny (Number of points on meridian)", Prt_D, gds->Ny); + Print ("GDS", "Lat1", Prt_F, gds->lat1); + Print ("GDS", "Lon1", Prt_F, gds->lon1); + if (gds->resFlag & GRIB2BIT_5) { + Print ("GDS", "u/v vectors relative to", Prt_S, "grid"); + } else { + Print ("GDS", "u/v vectors relative to", Prt_S, "easterly/northerly"); + } + if (gds->projType == GS3_LATLON) { + Print ("GDS", "Lat2", Prt_F, gds->lat2); + Print ("GDS", "Lon2", Prt_F, gds->lon2); + Print ("GDS", "Dx", Prt_FS, gds->Dx, "degrees"); + Print ("GDS", "Dy", Prt_FS, gds->Dy, "degrees"); + } else if (gds->projType == GS3_MERCATOR) { + Print ("GDS", "Lat2", Prt_F, gds->lat2); + Print ("GDS", "Lon2", Prt_F, gds->lon2); + Print ("GDS", "Dx", Prt_FS, gds->Dx, "m"); + Print ("GDS", "Dy", Prt_FS, gds->Dy, "m"); + } else if ((gds->projType == GS3_POLAR) + || (gds->projType == GS3_LAMBERT)) { + Print ("GDS", "Dx", Prt_FS, gds->Dx, "m"); + Print ("GDS", "Dy", Prt_FS, gds->Dy, "m"); + } + /* For scan mode... The user of this data doesn't necesarily care how it + * was stored in the Grib2 grid (ie gds->scan), they just care about how + * the data they are accessing is scanned (ie scan=0000) */ + sprintf (buffer, "%d%d%d%d", ((gds->scan & GRIB2BIT_1) / GRIB2BIT_1), + ((gds->scan & GRIB2BIT_2) / GRIB2BIT_2), + ((gds->scan & GRIB2BIT_3) / GRIB2BIT_3), + ((gds->scan & GRIB2BIT_4) / GRIB2BIT_4)); + Print ("GDS", "Input GRIB2 grid, scan mode", Prt_DS, gds->scan, buffer); +/* + Print ("GDS", "Output grid, scan mode", Prt_DS, 0, "0000"); + Print ("GDS", "Output grid, scan i/x direction", Prt_S, "positive"); + Print ("GDS", "Output grid, scan j/y direction", Prt_S, "negative"); +*/ + Print ("GDS", "Output grid, scan mode", Prt_DS, 64, "0100"); + Print ("GDS", "(.flt file grid), scan mode", Prt_DS, 0, "0000"); + Print ("GDS", "Output grid, scan i/x direction", Prt_S, "positive"); + Print ("GDS", "Output grid, scan j/y direction", Prt_S, "positive"); + Print ("GDS", "(.flt file grid), scan j/y direction", Prt_S, "negative"); + Print ("GDS", "Output grid, consecutive points in", Prt_S, + "i/x direction"); + Print ("GDS", "Output grid, adjacent rows scan in", Prt_S, + "same direction"); + + /* Meshlat/orient lon/scale lat have no meaning for lat/lon grids. */ + if (gds->projType != GS3_LATLON) { + Print ("GDS", "MeshLat", Prt_F, gds->meshLat); + Print ("GDS", "OrientLon", Prt_F, gds->orientLon); + if ((gds->projType == GS3_POLAR) || (gds->projType == GS3_LAMBERT)) { + if (gds->center & GRIB2BIT_1) { + Print ("GDS", "Which pole is on the plane", Prt_S, "South"); + } else { + Print ("GDS", "Which pole is on the plane", Prt_S, "North"); + } + if (gds->center & GRIB2BIT_2) { + Print ("GDS", "bi-polar projection", Prt_S, "Yes"); + } else { + Print ("GDS", "bi-polar projection", Prt_S, "No"); + } + } + Print ("GDS", "Tangent Lat1", Prt_F, gds->scaleLat1); + Print ("GDS", "Tangent Lat2", Prt_F, gds->scaleLat2); + Print ("GDS", "Southern Lat", Prt_F, gds->southLat); + Print ("GDS", "Southern Lon", Prt_F, gds->southLon); + } + return 0; +} + +/***************************************************************************** + * PrintGridAttrib() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To generate the message for the various attributes of the grid. + * + * ARGUMENTS + * attrib = The Grid Attribute structure to print. (Input) + * decimal = How many decimals to round to. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 5/2003 AAT: Added rounding to decimal. + * + * NOTES + ***************************************************************************** + */ +static void PrintGridAttrib (gridAttribType *attrib, sChar decimal) +{ + /* Based on Grib2 Code Table 5.0 */ + static const char *table50[] = { + "Grid point data - simple packing", "Matrix value - simple packing", + "Grid point data - complex packing", + "Grid point data - complex packing and spatial differencing" + }; + + /* Based on Grib2 Code Table 5.1 */ + static const char *table51[] = { "Floating point", "Integer" }; + + /* Based on Grib2 Code Table 5.5 */ + static const char *table55[] = { + "No explicit missing value included with data", + "Primary missing value included with data", + "Primary and Secondary missing values included with data" + }; + + if ((attrib->packType == GS5_JPEG2000) || + (attrib->packType == GS5_JPEG2000_ORG)) { + Print ("Info", "Packing that was used", Prt_DS, attrib->packType, + "JPEG 2000"); + } else if ((attrib->packType == GS5_PNG) || + (attrib->packType == GS5_PNG_ORG)) { + Print ("Info", "Packing that was used", Prt_DS, attrib->packType, + "Portable Network Graphics (PNG)"); + } else { + Print ("Info", "Packing that was used", Prt_DS, attrib->packType, + Lookup (table50, sizeof (table50), attrib->packType)); + } + /* Added next two 1/27/2006 because of questions from Val. */ + Print ("Info", "Decimal Scale Factor", Prt_D, attrib->DSF); + Print ("Info", "Binary Scale Factor", Prt_D, attrib->ESF); + Print ("Info", "Original field type", Prt_DS, attrib->fieldType, + Lookup (table51, sizeof (table51), attrib->fieldType)); + Print ("Info", "Missing value management", Prt_DS, attrib->f_miss, + Lookup (table55, sizeof (table55), attrib->f_miss)); + if (attrib->f_miss == 1) { + Print ("Info", "Primary missing value", Prt_F, + myRound (attrib->missPri, decimal)); + } else if (attrib->f_miss == 2) { + Print ("Info", "Primary missing value", Prt_F, + myRound (attrib->missPri, decimal)); + Print ("Info", "Secondary missing value", Prt_F, + myRound (attrib->missSec, decimal)); + } + Print ("Info", "Detected number of Missing", Prt_D, attrib->numMiss); + if (attrib->f_maxmin) { + Print ("Info", "Field minimum value", Prt_F, + myRound (attrib->min, decimal)); + Print ("Info", "Field maximum value", Prt_F, + myRound (attrib->max, decimal)); + } +} + +/***************************************************************************** + * MetaPrintGDS() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To generate a message specific for the GDS. Basically a wrapper for + * PrintGDS and Print. + * + * ARGUMENTS + * gds = The Grid Definition Section to generate the message for. (Input) + * version = The GRIB version number (so we know what type of projection) (In) + * ans = The resulting message. Up to caller to free. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 if no error. + * -1 if asked to print a map projection that we don't support. + * -2 if asked to print data for a template that we don't support. + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created. + * 5/2003 AAT: Commented out. Purpose was mainly for debugging degrib1.c, + * which is now working. + * + * NOTES + ***************************************************************************** + */ +int MetaPrintGDS (gdsType *gds, int version, char **ans) +{ + int ierr; /* The error code of a called routine */ + + if ((ierr = PrintGDS (gds, version)) != 0) { + *ans = Print (NULL, NULL, Prt_NULL); + preErrSprintf ("Print error Section 3\n"); + return ierr; + } + *ans = Print (NULL, NULL, Prt_NULL); + return 0; +} + +/***************************************************************************** + * MetaPrint() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To generate the meta file message. + * + * ARGUMENTS + * meta = The meta file structure to generate the message for. (Input) + * ans = The resulting message. Up to caller to free. (Output) + * decimal = How many decimals to round to. (Input) + * f_unit = 0 (GRIB unit), 1 (english), 2 (metric) (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 if no error. + * -1 if asked to print a map projection that we don't support. + * -2 if asked to print data for a template that we don't support. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 5/2003 AAT: Added rounding to decimal. + * + * NOTES + ***************************************************************************** + */ +int MetaPrint (grib_MetaData *meta, char **ans, sChar decimal, sChar f_unit) +{ + int ierr; /* The error code of a called routine */ + + if (meta->GribVersion == 1) { + PrintPDS1 (&(meta->pds1), meta->comment, meta->center, + meta->subcenter, f_unit, meta->unitName, meta->convert); + } else if (meta->GribVersion == -1) { + PrintPDS_TDLP (&(meta->pdsTdlp)); + } else { + if ((ierr = PrintPDS2 (meta, f_unit)) != 0) { + *ans = Print (NULL, NULL, Prt_NULL); + preErrSprintf ("Print error in PDS for GRIB2\n"); + return ierr; + } + } + if ((ierr = PrintGDS (&(meta->gds), meta->GribVersion)) != 0) { + *ans = Print (NULL, NULL, Prt_NULL); + preErrSprintf ("Print error Section 3\n"); + return ierr; + } + PrintGridAttrib (&(meta->gridAttrib), decimal); + *ans = Print (NULL, NULL, Prt_NULL); + return 0; +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myassert.c b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myassert.c new file mode 100644 index 000000000..5c16c1fa7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myassert.c @@ -0,0 +1,50 @@ +/***************************************************************************** + * myasert.c + * + * DESCRIPTION + * This file contains the code to handle assert statements. There is no + * actual code unless DEBUG is defined. + * + * HISTORY + * 12/2003 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#include +#include +#include "myassert.h" + +/***************************************************************************** + * myAssert() -- Arthur Taylor / MDL + * + * PURPOSE + * This is an Assert routine from "Writing Solid Code" by Steve Maguire. + * + * Advantages of this over "assert" is that assert stores the expression + * string for printing. Where does assert store it? Probably in global data, + * but that means assert is gobbling up space that the program may need for + * no real advantage. If you trigger assert, you're going to look in the file + * and see the code. + * + * ARGUMENTS + * file = Filename that assert was in. (Input) + * lineNum = Line number in file of the assert. (Input) + * + * RETURNS: void + * + * 8/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifdef DEBUG +void _myAssert (const char *file, int lineNum) +{ + fflush (NULL); + fprintf (stderr, "\nAssertion failed: %s, line %d\n", file, lineNum); + fflush (stderr); + abort (); +/* exit (EXIT_FAILURE);*/ +} +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myassert.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myassert.h new file mode 100644 index 000000000..f23d752e7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myassert.h @@ -0,0 +1,41 @@ +/***************************************************************************** + * myassert.h + * + * DESCRIPTION + * This file contains the code to handle assert statements. There is no + * actual code unless DEBUG is defined. + * + * HISTORY + * 12/2003 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifndef MYASSERT_H +#define MYASSERT_H + +#ifndef CPL_C_START +#ifdef __cplusplus +# define CPL_C_START extern "C" { +# define CPL_C_END } +#else +# define CPL_C_START +# define CPL_C_END +#endif +#endif + +#ifdef DEBUG +CPL_C_START + void _myAssert (const char *file, int lineNum); +CPL_C_END + + #define myAssert(f) \ + if (f) \ + {} \ + else \ + _myAssert (__FILE__, __LINE__) +#else + #define myAssert(f) +#endif + +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myerror.c b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myerror.c new file mode 100644 index 000000000..02d29418e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myerror.c @@ -0,0 +1,1029 @@ +/***************************************************************************** + * myerror.c + * + * DESCRIPTION + * This file contains the code to handle error messages. Instead of simply + * printing the error to stdio, it allocates some memory and stores the + * message in it. This is so that one can pass the error message back to + * Tcl/Tk or another GUI program when there is no stdio. + * In addition a version of sprintf is provided which allocates memory for + * the calling routine, so that one doesn't have to guess the maximum bounds + * of the message. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * 12/2002 Rici Yu, Fangyu Chi, Mark Armstrong, & Tim Boyer + * (RY,FC,MA,&TB): Code Review 2. + * 12/2005 AAT Added myWarn routines. + * + * NOTES + * See Kernighan & Ritchie C book (2nd edition) page 156. + ***************************************************************************** + */ +#include +#include +#include +#include "myassert.h" +#include "myerror.h" +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +/***************************************************************************** + * AllocSprintf() -- Arthur Taylor / MDL (Review 12/2002) + * + * PURPOSE + * Based on minprintf (see K&R C book (2nd edition) page 156. This code + * tries to provide some of the functionality of sprintf, while at the same + * time it handles the memory allocation. + * In addition, it provides a %S option, which allows one to pass in an + * array of strings, and get back a comma delimited string. + * + * ARGUMENTS + * Ptr = An array of data that is of size LenBuff. (Input/Output) + * LenBuff = The allocated length of Ptr. (Input/Output) + * fmt = Format similar to the one used by sprintf to define how to + * print the message (Input) + * ap = argument list initialized by a call to va_start. Contains the + * data needed by fmt. (Input) + * + * RETURNS: void + * + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (RY,FC,MA,&TB): Code Review. + * 12/2002 AAT: Fixed the mallocSprintf ("") error. + * 2/2003 AAT: increased bufpart[80] to bufpart[330] because the largest + * 64 bit double is: +1.7E+308, and I want 20 "slots" for stuff + * after the decimal place. There is the possibility of "Long + * doubles" (80 bits) which would have a max of: +3.4E+4932, but + * that is excessive for now. + * 2/2004 AAT: if lenBuff != 0, switch from ipos-- to strlen (buffer); + * 3/2004 AAT: Added %c option. + * 11/2005 AAT: Added %e option. + * 1/2006 AAT: Found a bug with multiple errSprintf. Doesn't seem to be + * able to handle lenBuff > strlen(buffer) when procedure is + * first called. Something like format = "aaa%s", lenBuff = 3, + * buff = 'n' would result in 'naaa__', instead of + * 'naaa'. Simple solution set lenBuff = strlen (buff). + * better solution: Maybe calculate correct place for ipos + * before switch. + * + * NOTES + * Supported formats: + * %0.4f => float, double + * %03d %ld %10ld => int, sInt4. + * %s => Null terminated char string. (no range specification) + * %S => take a char ** and turn it into a comma delimited string. + * + * Assumes that no individual float or int will be more than 80 characters + * Assumes that no % option is more than 20 char. + ***************************************************************************** + */ +static void AllocSprintf (char **Ptr, size_t *LenBuff, const char *fmt, + va_list ap) +{ + char *buffer = *Ptr; /* Local copy of Ptr. */ + size_t lenBuff = *LenBuff; /* Local copy of LenBuff. */ + const char *p; /* Points to % char in % option. */ + const char *p1; /* Points to end of % option. */ + char bufpart[330]; /* Used for formating the int / float options. */ + char format[20]; /* Used to store the % option. */ + char *sval; /* For pulling strings off va_list. */ + char **Sval; /* For pulling lists of strings off va_list. */ + size_t slen; /* Length of used part of temp. */ + char f_inLoop; /* Flag to state whether we got into %S , loop. */ + char flag; /* If they have a l,L,h in string. */ + /* size_t ipos = *LenBuff; *//* The current index to start storing data. */ + size_t ipos; /* The current index to start storing data. */ + int c_type; /* Used when handling %c option. */ + + myAssert (sizeof (char) == 1); + + if ((fmt == NULL) || (strlen (fmt) == 0)) { + return; + } + p = fmt; + /* If lenBuff = 0, then make room for the '\0' character. */ + if (lenBuff == 0) { + lenBuff++; + buffer = (char *) realloc ((void *) buffer, lenBuff); + /* Added following 1 line on 1/2006 */ + ipos = 0; + } else { + /* Added following 3 lines on 1/2006 */ + myAssert (lenBuff >= strlen (buffer) + 1); + lenBuff = strlen (buffer) + 1; + ipos = lenBuff - 1; +/* ipos = strlen (buffer); */ + } + while (p < fmt + strlen (fmt)) { + p1 = p; + p = strchr (p1, '%'); + /* Handle simple case when no more % in format string. */ + if (p == NULL) { + /* No more format strings; copy rest of format and return */ + lenBuff += strlen (p1); + buffer = (char *) realloc ((void *) buffer, lenBuff); + strcpy (buffer + ipos, p1); + goto done; + } + /* Handle data up to the current % in format string. */ + lenBuff += p - p1; + buffer = (char *) realloc ((void *) buffer, lenBuff); + strncpy (buffer + ipos, p1, p - p1); + ipos = lenBuff - 1; + /* Start dealing with % of format. */ + p1 = p + strspn (p + 1, "0123456789."); + p1++; + /* p1 points to first letter after %. */ + switch (*p1) { + case 'h': + case 'l': + case 'L': + flag = *p1; + p1++; + break; + case '\0': + /* Handle improper use of '%' for example: '%##' */ + lenBuff += p1 - p - 1; + buffer = (char *) realloc ((void *) buffer, lenBuff); + strncpy (buffer + ipos, p + 1, p1 - p - 1); + goto done; + default: + flag = ' '; + } + if ((p1 - p + 1) > (int) (sizeof (format)) - 1) { + /* Protect against overflow of format string. */ + lenBuff += p1 - p + 1; + buffer = (char *) realloc ((void *) buffer, lenBuff); + strncpy (buffer + ipos, p, p1 - p + 1); + ipos = lenBuff - 1; + } else { + strncpy (format, p, p1 - p + 1); + format[p1 - p + 1] = '\0'; + switch (*p1) { + case 'd': + switch (flag) { + case 'l': + case 'L': + sprintf (bufpart, format, va_arg (ap, sInt4)); + break; + /* + * gcc warning for 'h': "..." promotes short int to + * int. Could get rid of 'h' option but decided to + * leave it in since we might have a different + * compiler. + */ +/* + case 'h': + sprintf (bufpart, format, va_arg(ap, short int)); + break; +*/ + default: + sprintf (bufpart, format, va_arg (ap, int)); + } + slen = strlen (bufpart); + lenBuff += slen; + buffer = (char *) realloc ((void *) buffer, lenBuff); + strncpy (buffer + ipos, bufpart, slen); + ipos = lenBuff - 1; + break; + case 'f': + sprintf (bufpart, format, va_arg (ap, double)); + slen = strlen (bufpart); + lenBuff += slen; + buffer = (char *) realloc ((void *) buffer, lenBuff); + strncpy (buffer + ipos, bufpart, slen); + ipos = lenBuff - 1; + break; + case 'e': + sprintf (bufpart, format, va_arg (ap, double)); + slen = strlen (bufpart); + lenBuff += slen; + buffer = (char *) realloc ((void *) buffer, lenBuff); + strncpy (buffer + ipos, bufpart, slen); + ipos = lenBuff - 1; + break; + case 'g': + sprintf (bufpart, format, va_arg (ap, double)); + slen = strlen (bufpart); + lenBuff += slen; + buffer = (char *) realloc ((void *) buffer, lenBuff); + strncpy (buffer + ipos, bufpart, slen); + ipos = lenBuff - 1; + break; + case 'c': + c_type = va_arg (ap, int); + lenBuff += 1; + buffer = (char *) realloc ((void *) buffer, lenBuff); + buffer[ipos] = (char) c_type; + buffer[ipos + 1] = '\0'; + ipos = lenBuff - 1; + break; + case 's': + if ((p1 - p) == 1) { + sval = va_arg (ap, char *); +/* printf (":: sval :: '%s'\n", sval);*/ + slen = strlen (sval); + lenBuff += slen; + buffer = (char *) realloc ((void *) buffer, lenBuff); + strncpy (buffer + ipos, sval, slen); + ipos = lenBuff - 1; + break; + } + /* Intentionally fall through. */ + case 'S': + if ((p1 - p) == 1) { + f_inLoop = 0; + for (Sval = va_arg (ap, char **); *Sval; Sval++) { + slen = strlen (*Sval); + lenBuff += slen + 1; + buffer = (char *) realloc ((void *) buffer, lenBuff); + strcpy (buffer + ipos, *Sval); + strcat (buffer + ipos + slen, ","); + ipos = lenBuff - 1; + f_inLoop = 1; + } + if (f_inLoop) { + lenBuff--; + buffer[lenBuff] = '\0'; + ipos = lenBuff - 1; + } + break; + } + /* Intentionally fall through. */ + default: + lenBuff += p1 - p; + buffer = (char *) realloc ((void *) buffer, lenBuff); + strncpy (buffer + ipos, p + 1, p1 - p); + ipos = lenBuff - 1; + } + } + p = p1 + 1; + } + done: + buffer[lenBuff - 1] = '\0'; + *Ptr = buffer; + *LenBuff = lenBuff; +} + +/***************************************************************************** + * mallocSprintf() -- Arthur Taylor / MDL (Review 12/2002) + * + * PURPOSE + * This is a front end for AllocSprintf, when you want to malloc memory. + * In other words when the pointer is not pointing to anything in particular. + * It allocates the memory, prints the message, and then sets Ptr to point to + * it. + * + * ARGUMENTS + * Ptr = Place to point to new memory which contains the message (Output) + * fmt = Format similar to the one used by sprintf to define how to print the + * message (Input) + * + * RETURNS: void + * + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (RY,FC,MA,&TB): Code Review. + * + * NOTES + * Supported formats: See AllocSprintf + ***************************************************************************** + */ +void mallocSprintf (char **Ptr, const char *fmt, ...) +{ + va_list ap; /* Contains the data needed by fmt. */ + size_t buff_len = 0; /* Allocated length of buffer. */ + + *Ptr = NULL; + if (fmt != NULL) { + va_start (ap, fmt); /* make ap point to 1st unnamed arg. */ + AllocSprintf (Ptr, &buff_len, fmt, ap); + va_end (ap); /* clean up when done. */ + } +} + +/***************************************************************************** + * reallocSprintf() -- Arthur Taylor / MDL (Review 12/2002) + * + * PURPOSE + * This is a front end for AllocSprintf, when you want to realloc memory. + * In other words, the pointer is pointing to NULL, or to some memory that + * you want to tack a message onto the end of. It allocates extra memory, + * and prints the message. + * + * KEY WORDS: "Tack a message onto the end of" + * + * ARGUMENTS + * Ptr = Pointer to memory to add the message to. (Input/Output) + * fmt = Format similar to the one used by sprintf to define how to print the + * message (Input) + * + * RETURNS: void + * + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (RY,FC,MA,&TB): Code Review. + * + * NOTES + * Supported formats: See AllocSprintf + ***************************************************************************** + */ +void reallocSprintf (char **Ptr, const char *fmt, ...) +{ + va_list ap; /* Contains the data needed by fmt. */ + size_t buff_len; /* Allocated length of buffer. */ + + if (fmt != NULL) { + va_start (ap, fmt); /* make ap point to 1st unnamed arg. */ + if (*Ptr == NULL) { + buff_len = 0; + } else { + buff_len = strlen (*Ptr) + 1; + } + AllocSprintf (Ptr, &buff_len, fmt, ap); + va_end (ap); /* clean up when done. */ + } +} + +/***************************************************************************** + * errSprintf() -- Arthur Taylor / MDL (Review 12/2002) + * + * PURPOSE + * This uses AllocSprintf to generate a message, which it stores in a static + * variable. If it is called with a (NULL), it returns the built up message, + * and resets its pointer to NULL. The idea being that errors can be stacked + * up, and you pop them off when you need to report them. The reporting could + * be done by printing them to stdio, or by passing them back to Tcl/Tk. + * Note: It is the caller's responsibility to free the memory, and it is + * the caller's responsibility to make sure the last call to this is with + * (NULL), or else the memory won't get freed. + * + * ARGUMENTS + * fmt = Format similar to the one used by sprintf to define how to print the + * message (Input) + * + * RETURNS: char * + * if (fmt == NULL) returns built up string + * else returns NULL. + * + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (RY,FC,MA,&TB): Code Review. + * + * NOTES + * Supported formats: See AllocSprintf + ***************************************************************************** + */ +/* Following 2 variables used in both errSprintf and preErrSprintf */ +static char *errBuffer = NULL; /* Stores the current built up message. */ +static size_t errBuff_len = 0; /* Allocated length of errBuffer. */ + +char *errSprintf (const char *fmt, ...) +{ + va_list ap; /* Contains the data needed by fmt. */ + char *ans; /* Pointer to the final message while we reset + * buffer. */ + + if (fmt == NULL) { + ans = errBuffer; + errBuffer = NULL; + errBuff_len = 0; + return ans; + } + va_start (ap, fmt); /* make ap point to 1st unnamed arg. */ + AllocSprintf (&errBuffer, &errBuff_len, fmt, ap); + va_end (ap); /* clean up when done. */ + return NULL; +} + +/***************************************************************************** + * preErrSprintf() -- Arthur Taylor / MDL + * + * PURPOSE + * This uses AllocSprintf to generate a message, which it prepends to the + * static variable used by errSprinf. If it is called with a (NULL), it + * does nothing... Use errSprintf (NULL) to get the message, and reset the + * pointer to NULL. + * The idea here is that we want to prepend calling info when there was an + * error. + * Note: It is the caller's responsibility to free the memory, by + * eventually making one last call to errSprintf (NULL) and freeing the + * returned memory. + * + * ARGUMENTS + * fmt = Format similar to the one used by sprintf to define how to print the + * message (Input) + * + * RETURNS: void + * + * 12/2002 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * Supported formats: See AllocSprintf + ***************************************************************************** + */ +void preErrSprintf (const char *fmt, ...) +{ + char *preBuffer = NULL; /* Stores the prepended message. */ + size_t preBuff_len = 0; /* Allocated length of preBuffer. */ + va_list ap; /* Contains the data needed by fmt. */ + + myAssert (sizeof (char) == 1); + + if (fmt == NULL) { + return; + } + va_start (ap, fmt); /* make ap point to 1st unnamed arg. */ + AllocSprintf (&preBuffer, &preBuff_len, fmt, ap); + va_end (ap); /* clean up when done. */ + + if (errBuff_len != 0) { + /* Increase preBuffer to have enough room for errBuffer */ + preBuff_len += errBuff_len; + preBuffer = (char *) realloc ((void *) preBuffer, preBuff_len); + /* concat errBuffer to end of preBuffer, and free errBuffer */ + strcat (preBuffer, errBuffer); + free (errBuffer); + } + /* Finally point errBuffer to preBuffer, and update errBuff_len. */ + errBuffer = preBuffer; + errBuff_len = preBuff_len; + return; +} + +/***************************************************************************** + * _myWarn() -- Arthur Taylor / MDL + * + * PURPOSE + * This is an update to my errSprintf routines. This procedure uses + * AllocSprintf to generate a message, which it stores in a static variable. + * It allows for prepending or appending error messages, and allows one to + * set the error level of a message. + * + * ARGUMENTS + * f_errCode = 0 => append notation msg, 1 => append warning msg + * 2 => append error msg, 3 => prepend notation msg + * 4 => prepend warning msg, 5 => prepend error msg (Input) + * fmt = Format to define how to print the msg (Input) + * ap = The arguments for the message. (Input) + * + * RETURNS: void + * + * 12/2005 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +/* Following variables used in the myWarn routines */ +static char *warnBuff = NULL; /* Stores the current built up message. */ +static size_t warnBuffLen = 0; /* Allocated length of warnBuff. */ +static sChar warnLevel = -1; /* Current warning level. */ +static uChar warnOutType = 0; /* Output type as set in myWarnSet. */ +static uChar warnDetail = 0; /* Detail level as set in myWarnSet. */ +static uChar warnFileDetail = 0; /* Detail level as set in myWarnSet. */ +static FILE *warnFP = NULL; /* Warn File as set in myWarnSet. */ + +static void _myWarn (uChar f_errCode, const char *fmt, va_list ap) +{ + char *buff = NULL; /* Stores the message. */ + size_t buffLen = 0; /* Allocated length of buff. */ + uChar f_prepend = 0; /* Flag to prepend (or not) the message. */ + uChar f_filePrt = 1; /* Flag to print to file. */ + uChar f_memPrt = 1; /* Flag to print to memory. */ + + if (fmt == NULL) { + return; + } + if (f_errCode > 5) { + f_errCode = 0; + } + if (f_errCode > 2) { + f_errCode -= (uChar) 3; + f_prepend = 1; + } + /* Update the warning level */ + if (f_errCode > warnLevel) { + warnLevel = f_errCode; + } + + /* Check if the warnDetail level allows this message. */ + if ((warnOutType >= 4) || + (warnDetail == 2) || ((warnDetail == 1) && (f_errCode < 2))) { + f_memPrt = 0; + } + if ((warnOutType == 0) || + (warnFileDetail == 2) || ((warnFileDetail == 1) && (f_errCode < 2))) { + if (!f_memPrt) { + return; + } + f_filePrt = 0; + } + + AllocSprintf (&buff, &buffLen, fmt, ap); + + /* Handle the file writing. */ + if (f_filePrt) { + fprintf (warnFP, "%s", buff); + } + /* Handle the memory writing. */ + if (f_memPrt) { + if (f_prepend) { + if (warnBuffLen != 0) { + /* Add warnBuff to end of buff, and free warnBuff. */ + buffLen += warnBuffLen; + myAssert (sizeof (char) == 1); + buff = (char *) realloc (buff, buffLen); + strcat (buff, warnBuff); + free (warnBuff); + } + /* Point warnBuff to buff. */ + warnBuff = buff; + warnBuffLen = buffLen; + } else { + if (warnBuffLen == 0) { + warnBuff = buff; + warnBuffLen = buffLen; + } else { + warnBuffLen += buffLen; + myAssert (sizeof (char) == 1); + warnBuff = (char *) realloc (warnBuff, warnBuffLen); + strcat (warnBuff, buff); + free (buff); + } + } + } +} + +/***************************************************************************** + * myWarn() -- Arthur Taylor / MDL + * + * PURPOSE + * This does the transformation of the "..." parameters, and calls _myWarn. + * This was broken out when we started to implement myWarnRet, so we had two + * ways to call _myWarn. A complicated way (myWarnRet), and a simpler way + * (myWarn). After creating the myWarnW# #defines, thought to depricate use + * of myWarn by making it static. Still need it, because myWarnRet uses it. + * + * ARGUMENTS + * f_errCode = 0 => append notation msg, 1 => append warning msg + * 2 => append error msg, 3 => prepend notation msg + * 4 => prepend warning msg, 5 => prepend error msg (Input) + * fmt = Format to define how to print the msg (Input) + * ... = The actual message arguments. (Input) + * + * RETURNS: void + * + * 12/2005 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +static void myWarn (uChar f_errCode, const char *fmt, ...) +{ + va_list ap; /* Contains the data needed by fmt. */ + + /* Create the message in buff. */ + va_start (ap, fmt); /* make ap point to 1st unnamed arg. */ + _myWarn (f_errCode, fmt, ap); + va_end (ap); /* clean up when done. */ +} + +/***************************************************************************** + * myWarnRet() -- Arthur Taylor / MDL + * + * PURPOSE + * This does the transformation of the "..." parameters, and calls _myWarn. + * This was created, so that the user could pass in where (file and line + * number) the error took place, and get a uniform handling of the file and + * line numbers. In addition the user could pass in a value for the procedure + * to return, which allows the user to have something like: + * "return myWarnW2 (-1, "foobar\n");" + * Which after the #define is evaluated becomes: + * "return myWarnRet (1, -1, __FILE__, __LINE__, "foobar\n"); + * + * Without myWarnRet, one would need something like: + * "myWarn (1, "(%s line %d) foobar\n", __FILE__, __LINE__);" + * "return (-1); + * Trying to come up with a #define to make that easier on the user was + * difficult. The first attempt was: + * #define myWarnLine myWarn(1, "(%s, line %d) " __FILE__, __LINE__); myWarn + * but this had difficulties with "if () myWarnLine" since it became two + * statements, which could confuse the use of {}. A better solition was: + * #define myWarnLineW1(f) myWarnLine (1, __FILE__, __LINE__, f) + * Particularly since the user didn't have to remember that Warn is flag of 1, + * and error is flag of 2. Since I already had to create myWarnW# #defines, + * it was easy to add the user specified return values. + * + * ARGUMENTS + * f_errCode = 0 => append notation msg, 1 => append warning msg + * 2 => append error msg, 3 => prepend notation msg + * 4 => prepend warning msg, 5 => prepend error msg (Input) + * appErrCode = User defined error code for myWarnRet to return. + * file = Filename that the call to myWarnRet was in. (Input) + * If NULL, then it skips the __FILE__, __LINE__ print routine. + * lineNum = Line number of call to myWarnRet. (Input) + * fmt = Format to define how to print the msg (Input) + * ... = The actual message arguments. (Input) + * + * RETURNS: int + * The value of appErrCode. + * + * 12/2005 Arthur Taylor (MDL): Created. + * + * NOTES: + * Is in "Quiet" mode if "file" is NULL (no __FILE__, __LINE__ prints) + ***************************************************************************** + */ +int myWarnRet (uChar f_errCode, int appErrCode, const char *file, + int lineNum, const char *fmt, ...) +{ + va_list ap; /* Contains the data needed by fmt. */ + + if (fmt != NULL) { + if (file != NULL) { + myWarn (f_errCode, "(%s, line %d) ", file, lineNum); + } + /* Create the message in buff. */ + va_start (ap, fmt); /* make ap point to 1st unnamed arg. */ + _myWarn (f_errCode, fmt, ap); + va_end (ap); /* clean up when done. */ + } else if (file != NULL) { + myWarn (f_errCode, "(%s, line %d)\n", file, lineNum); + } + return appErrCode; +} + +/***************************************************************************** + * myWarnSet() -- Arthur Taylor / MDL + * + * PURPOSE + * This sets warnOutType, warnDetail, and warnFile for myWarn. + * + * ARGUMENTS + * f_outType = 0 => memory, 1 => memory + stdout, 2 => memory + stderr, + * 3 => memory + warnFile, 4 => stdout, 5 => stderr, + * 6 => warnFile. (Input) + * f_detail = 0 => report all, 1 => report errors, 2 => silent. (Input) + * f_fileDetail = 0 => report all, 1 => report errors, 2 => silent. (Input) + * warnFile = An already opened alternate file to log errors to. (Input) + * + * RETURNS: void + * + * 12/2005 Arthur Taylor (MDL): Created. + * + * NOTES: + * The reason someone may want memory + warnFile is so that they can log the + * errors in a logFile, but still have something come to stdout. + ***************************************************************************** + */ +void myWarnSet (uChar f_outType, uChar f_detail, uChar f_fileDetail, + FILE *warnFile) +{ + if (f_outType > 6) { + f_outType = 0; + } + if (f_detail > 2) { + f_detail = 0; + } + warnOutType = f_outType; + warnDetail = f_detail; + warnFileDetail = f_fileDetail; + if ((f_outType == 1) || (f_outType == 4)) { + warnFP = stdout; + } else if ((f_outType == 2) || (f_outType == 5)) { + warnFP = stderr; + } else if ((f_outType == 3) || (f_outType == 6)) { + if (warnFile == NULL) { + warnFP = stderr; + } else { + warnFP = warnFile; + } + } else { + warnFP = NULL; + } +} + +/***************************************************************************** + * myWarnClear() -- Arthur Taylor / MDL + * + * PURPOSE + * This clears the warning stack, returns what is on there in msg, resets + * the memory to NULL, and returns the error code. + * + * ARGUMENTS + * msg = Whatever has been written to the warning memory + * (NULL, or allocated memory) (Out) + * f_closeFile = flag to close the warnFile or not (Input) + * + * RETURNS: sChar + * -1 means no messages in msg (msg should be null) + * 0 means upto notation msg in msg, but msg should not be null. + * 1 means upto warning messages in msg, msg should not be null. + * 2 means upto error messages in msg, msg should not be null. + * + * 12/2005 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +sChar myWarnClear (char **msg, uChar f_closeFile) +{ + sChar ans; + + *msg = warnBuff; + warnBuff = NULL; + warnBuffLen = 0; + ans = warnLevel; + warnLevel = -1; + if (f_closeFile) { + fclose (warnFP); + } + return ans; +} + +/***************************************************************************** + * myWarnNotEmpty() -- Arthur Taylor / MDL + * + * PURPOSE + * This returns whether the warning message is null or not. + * + * ARGUMENTS + * + * RETURNS: uChar + * 0 => msg == null, 1 => msg != null + * + * 12/2005 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +uChar myWarnNotEmpty () +{ + return (uChar) ((warnBuff != NULL) ? 1 : 0); +} + +/***************************************************************************** + * myWarnLevel() -- Arthur Taylor / MDL + * + * PURPOSE + * This returns the status of the warnLevel. + * + * ARGUMENTS + * + * RETURNS: sChar + * -1 means no messages in msg (msg should be null) + * 0 means upto notation msg in msg, but msg should not be null. + * 1 means upto warning messages in msg, msg should not be null. + * 2 means upto error messages in msg, msg should not be null. + * + * 12/2005 Arthur Taylor (MDL): Created. + * + * NOTES: + ***************************************************************************** + */ +sChar myWarnLevel () +{ + return warnLevel; +} + +#ifdef TEST_MYERROR +/***************************************************************************** + * The following 2 procedures are included only to test myerror.c, and only + * if TEST_MYERROR is defined. + ***************************************************************************** + */ + +/***************************************************************************** + * checkAns() -- Arthur Taylor / MDL (Review 12/2002) + * + * PURPOSE + * To verify that a test gives the expected result. + * + * ARGUMENTS + * ptr = The results of the test. (Input) + * Ans = An array of correct answers. (Input) + * test = Which test we are checking. (Input) + * + * RETURNS: void + * + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (RY,FC,MA,&TB): Code Review. + * + * NOTES + ***************************************************************************** + */ +static void checkAns (char *ptr, char **Ans, int test) +{ + if (ptr == NULL) { + printf ("-----Check test (%d)--(ptr == NULL)-----\n", test); + return; + } + if (strcmp (ptr, Ans[test]) != 0) { + printf ("-----Failed test %d-------\n", test); + printf ("%s %d =?= %s %d\n", ptr, strlen (ptr), + Ans[test], strlen (Ans[test])); + } else { + printf ("passed test %d\n", test); + } +} + +/***************************************************************************** + * main() -- Arthur Taylor / MDL (Review 12/2002) + * + * PURPOSE + * To test reallocSprint, mallocSprint, and errSprintf, to make sure that + * they pass certain basic tests. I will be adding more tests, as more bugs + * are found, and features added. + * + * ARGUMENTS + * argc = The number of arguments on the command line. (Input) + * argv = The arguments on the command line. (Input) + * + * RETURNS: int + * + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (RY,FC,MA,&TB): Code Review. + * + * NOTES + ***************************************************************************** + */ +int main (int argc, char **argv) +{ + char *ptr; + uChar warn; + static char *Cmd[] = { "configure", "inquire", "convert", NULL }; + sInt4 li_temp = 100000L; + short int sect = 5; + char varName[] = "Helium is a gas"; + sInt4 lival = 22; + char unit[] = "km", sval[] = "ans"; + double dval = 2.71828; + + char *buffer = NULL; + short int ssect = 0; + char vvarName[] = "DataType"; + sInt4 llival = 0; + char ssval[] = "Meteorological products"; + + static char *Ans[] = { "S0 | DataType | 0 (Meteorological products)\n", + "", "<05><3.1415><20>", + " ?options?", + "100000", "25.123", "02s", "01234567890123456789012345", + "25.123,05, hello world", + "This is a test 5... Here I am\n", + "Parse error Section 0\nErrorERROR: Problems opening c:--goober for " + "write.Projection code requires Earth with Rad = 6367.47 not " + "6400.010000", + "ERROR IS1 not labeled correctly. 5000000\n" + "Should be 1196575042 2 25\nERROR IS0 has unexpected values: " + "100000 100000 100000\n", + "S5 | Helium is a gas | 22 (ans)\nS5 | Helium is a gas | 22\n" + "S5 | Helium is a gas | 22 (ans (km))\nS5 | Helium is a gas | ans\n" + "S5 | Helium is a gas | 2.718280\nS5 | Helium is a gas | " + "2.718280 (km)\n", + "ERROR IS1 not labeled correctly. 5000000\n" + "Should be 1196575042 2 25\nERROR IS0 has unexpected values: " + "100000 100000 100000\n", + "5.670000e+001" + }; + +/* Test -2. (See if it can handle blank). */ + mallocSprintf (&ptr, ""); + free (ptr); + ptr = NULL; + + mallocSprintf (&ptr, " "); + free (ptr); + ptr = NULL; + + +/* Test -1. (see if checkAns is ok) */ + ptr = errSprintf (NULL); + checkAns (ptr, Ans, -1); + +/* Test 0 */ + reallocSprintf (&buffer, "S%d | %s | %ld (%s)\n", ssect, vvarName, + llival, ssval); + checkAns (buffer, Ans, 0); + free (buffer); + +/* Test 1. */ + ptr = NULL; + reallocSprintf (&ptr, ""); + checkAns (ptr, Ans, 1); + free (ptr); + +/* Test 2. */ + ptr = NULL; + reallocSprintf (&ptr, "<%02d><%.4f><%D><%ld>", 5, 3.1415, 20, 24); + checkAns (ptr, Ans, 2); + free (ptr); + +/* Test 3. */ + ptr = NULL; + reallocSprintf (&ptr, "<%S> ?options?", Cmd); + checkAns (ptr, Ans, 3); + free (ptr); + +/* Test 4. */ + ptr = NULL; + reallocSprintf (&ptr, "%ld", li_temp); + checkAns (ptr, Ans, 4); + free (ptr); + +/* Test 5. */ + ptr = NULL; + reallocSprintf (&ptr, "%.3f", 25.1234); + checkAns (ptr, Ans, 5); + free (ptr); + +/* Test 6. */ + ptr = NULL; + reallocSprintf (&ptr, "%02s", 25.1234); + checkAns (ptr, Ans, 6); + free (ptr); + +/* Test 7. */ + ptr = NULL; + reallocSprintf (&ptr, "%01234567890123456789012345"); + checkAns (ptr, Ans, 7); + free (ptr); + +/* Test 8. */ + mallocSprintf (&ptr, "%.3f", 25.1234); + reallocSprintf (&ptr, ",%02d", 5); + reallocSprintf (&ptr, ", %s", "hello world"); + checkAns (ptr, Ans, 8); + free (ptr); + ptr = NULL; + +/* Test 9. */ + errSprintf ("This is a test %d... ", 5); + errSprintf ("Here I am\n"); + ptr = errSprintf (NULL); + checkAns (ptr, Ans, 9); + free (ptr); + +/* Test 10. */ + errSprintf ("Parse error Section 0\n%s", "Error"); + errSprintf ("ERROR: Problems opening %s for write.", "c:--goober"); + errSprintf ("Projection code requires Earth with Rad = 6367.47 not %f", + 6400.01); + ptr = errSprintf (NULL); + checkAns (ptr, Ans, 10); + free (ptr); + +/* Test 11. */ + errSprintf ("ERROR IS1 not labeled correctly. %ld\n", 5000000L); + errSprintf ("Should be %ld %d %ld\n", 1196575042L, 2, 25); + errSprintf ("ERROR IS0 has unexpected values: %ld %ld %ld\n", li_temp, + li_temp, li_temp); + ptr = errSprintf (NULL); + checkAns (ptr, Ans, 11); + free (ptr); + +/* Test 12. */ + ptr = NULL; + reallocSprintf (&ptr, "S%d | %s | %ld (%s)\n", sect, varName, lival, sval); + reallocSprintf (&ptr, "S%d | %s | %ld\n", sect, varName, lival); + reallocSprintf (&ptr, "S%d | %s | %ld (%s (%s))\n", sect, varName, lival, + sval, unit); + reallocSprintf (&ptr, "S%d | %s | %s\n", sect, varName, sval); + reallocSprintf (&ptr, "S%d | %s | %f\n", sect, varName, dval); + reallocSprintf (&ptr, "S%d | %s | %f (%s)\n", sect, varName, dval, unit); + checkAns (ptr, Ans, 12); + free (ptr); + +/* Test 13. */ + preErrSprintf ("Should be %ld %d %ld\n", 1196575042L, 2, 25); + errSprintf ("ERROR IS0 has unexpected values: %ld %ld %ld\n", li_temp, + li_temp, li_temp); + preErrSprintf ("ERROR IS1 not labeled correctly. %ld\n", 5000000L); + ptr = errSprintf (NULL); + checkAns (ptr, Ans, 13); + free (ptr); + +/* Test 14. */ + ptr = NULL; + reallocSprintf (&ptr, "%e", 56.7); + checkAns (ptr, Ans, 14); + free (ptr); + + myWarnSet (1, 0, 1, NULL); + myWarnW2 (0, "This is a test of Warn\n"); + myWarnE2 (0, "This is a test of Err\n"); + myWarnQ2 (0, "This is a quiet note\n"); + myWarnPW2 (0, "This is a test2 of Error\n"); + myWarnW4 (0, "This is a test of WarnLnW3 %d %d\n", 10, 20); + myWarnE3 (0, "This is a test of WarnLnE2 %d\n", 10); + printf ("\tTest myWarnRet: %d\n", myWarnW1 (-1)); + printf ("\tTest myWarnRet: %d\n", myWarnE2 (-2, "Hello nurse\n")); + if (myWarnNotEmpty ()) { + ptr = NULL; + warn = myWarnClear (&ptr, 0); + printf ("WarnLevel=%d\n%s", warn, ptr); + free (ptr); + } + + return 0; +} +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myerror.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myerror.h new file mode 100644 index 000000000..9c4380a85 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myerror.h @@ -0,0 +1,96 @@ +/***************************************************************************** + * myerror.h + * + * DESCRIPTION + * This file contains the code to handle error messages. Instead of simply + * printing the error to stdio, it allocates some memory and stores the + * message in it. This is so that one can pass the error message back to + * Tcl/Tk or another GUI program when there is no stdio. + * In addition a version of sprintf is provided which allocates memory for + * the calling routine, so that one doesn't have to guess the maximum bounds + * of the message. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifndef MYERROR_H +#define MYERROR_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include "type.h" + +void mallocSprintf (char **Ptr, const char *fmt, ...); + +void reallocSprintf (char **Ptr, const char *fmt, ...); + +/* If fmt == NULL return buffer and reset, otherwise add. */ +/* You are responsible for free'ing the result of errSprintf(NULL). */ +char *errSprintf (const char *fmt, ...); + +void preErrSprintf (const char *fmt, ...); + +int myWarnRet (uChar f_errCode, int appErrCode, const char *file, + int lineNum, const char *fmt, ...); + +void myWarnSet (uChar f_outType, uChar f_detail, uChar f_fileDetail, + FILE * warnFile); + +/* You are responsible for free'ing the result of myWarnClear. */ +sChar myWarnClear (char **msg, uChar f_closeFile); + +uChar myWarnNotEmpty (); + +sChar myWarnLevel (); + +/* Use myWarnQ# for (quiet file/line) notes. */ +/* Use myWarnN# for notes. */ +/* Use myWarnW# for warnings. */ +/* Use myWarnE# for errors. */ +#define myWarnQ1(f) myWarnRet(0, f, NULL, __LINE__, NULL) +#define myWarnN1(f) myWarnRet(0, f, __FILE__, __LINE__, NULL) +#define myWarnW1(f) myWarnRet(1, f, __FILE__, __LINE__, NULL) +#define myWarnE1(f) myWarnRet(2, f, __FILE__, __LINE__, NULL) +#define myWarnPQ1(f) myWarnRet(3, f, NULL, __LINE__, NULL) +#define myWarnPN1(f) myWarnRet(3, f, __FILE__, __LINE__, NULL) +#define myWarnPW1(f) myWarnRet(4, f, __FILE__, __LINE__, NULL) +#define myWarnPE1(f) myWarnRet(5, f, __FILE__, __LINE__, NULL) + +#define myWarnQ2(f,g) myWarnRet(0, f, NULL, __LINE__, g) +#define myWarnN2(f,g) myWarnRet(0, f, __FILE__, __LINE__, g) +#define myWarnW2(f,g) myWarnRet(1, f, __FILE__, __LINE__, g) +#define myWarnE2(f,g) myWarnRet(2, f, __FILE__, __LINE__, g) +#define myWarnPQ2(f,g) myWarnRet(3, f, NULL, __LINE__, g) +#define myWarnPN2(f,g) myWarnRet(3, f, __FILE__, __LINE__, g) +#define myWarnPW2(f,g) myWarnRet(4, f, __FILE__, __LINE__, g) +#define myWarnPE2(f,g) myWarnRet(5, f, __FILE__, __LINE__, g) + +#define myWarnQ3(f,g,h) myWarnRet(0, f, NULL, __LINE__, g, h) +#define myWarnN3(f,g,h) myWarnRet(0, f, __FILE__, __LINE__, g, h) +#define myWarnW3(f,g,h) myWarnRet(1, f, __FILE__, __LINE__, g, h) +#define myWarnE3(f,g,h) myWarnRet(2, f, __FILE__, __LINE__, g, h) +#define myWarnPQ3(f,g,h) myWarnRet(3, f, NULL, __LINE__, g, h) +#define myWarnPN3(f,g,h) myWarnRet(3, f, __FILE__, __LINE__, g, h) +#define myWarnPW3(f,g,h) myWarnRet(4, f, __FILE__, __LINE__, g, h) +#define myWarnPE3(f,g,h) myWarnRet(5, f, __FILE__, __LINE__, g, h) + +#define myWarnQ4(f,g,h,ff) myWarnRet(0, f, NULL, __LINE__, g, h, ff) +#define myWarnN4(f,g,h,ff) myWarnRet(0, f, __FILE__, __LINE__, g, h, ff) +#define myWarnW4(f,g,h,ff) myWarnRet(1, f, __FILE__, __LINE__, g, h, ff) +#define myWarnE4(f,g,h,ff) myWarnRet(2, f, __FILE__, __LINE__, g, h, ff) +#define myWarnPQ4(f,g,h,ff) myWarnRet(3, f, NULL, __LINE__, g, h, ff) +#define myWarnPN4(f,g,h,ff) myWarnRet(3, f, __FILE__, __LINE__, g, h, ff) +#define myWarnPW4(f,g,h,ff) myWarnRet(4, f, __FILE__, __LINE__, g, h, ff) +#define myWarnPE4(f,g,h,ff) myWarnRet(5, f, __FILE__, __LINE__, g, h, ff) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MYERROR_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myutil.c b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myutil.c new file mode 100644 index 000000000..ec2290d06 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myutil.c @@ -0,0 +1,1233 @@ +/***************************************************************************** + * myutil.c + * + * DESCRIPTION + * This file contains some simple utility functions. + * + * HISTORY + * 12/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ + +/* For S_IFDIR */ +#define _XOPEN_SOURCE 500 +#include +#include +#include +#include +#include +#include +//#include +//#include +#include "myutil.h" +#include "myassert.h" + +#include "cpl_port.h" + +/* Android compat */ +#ifndef S_IREAD +#define S_IREAD S_IRUSR +#endif + +#ifndef S_IWRITE +#define S_IWRITE S_IWUSR +#endif + +#ifndef S_IEXEC +#define S_IEXEC S_IXUSR +#endif +/* End of Android compat */ + +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +/***************************************************************************** + * reallocFGets() -- Arthur Taylor / MDL + * + * PURPOSE + * Read in data from file until a \n is read. Reallocate memory as needed. + * Similar to fgets, except we don't know ahead of time that the line is a + * specific length. + * Assumes that Ptr is either NULL, or points to lenBuff memory. + * Responsibility of caller to free the memory. + * + * ARGUMENTS + * Ptr = An array of data that is of size LenBuff. (Input/Output) + * LenBuff = The Allocated length of Ptr. (Input/Output) + * fp = Input file stream (Input) + * + * RETURNS: size_t + * strlen (buffer) + * 0 = We read only EOF + * 1 = We have "\nEOF" or "EOF" + * + * 12/2002 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * 1) Based on getline (see K&R C book (2nd edition) p 29) and on the + * behavior of Tcl's gets routine. + * 2) Chose MIN_STEPSIZE = 80 because pages are usually 80 columns. + * 3) Could switch lenBuff = i + 1 / lenBuff = i to always true. + * Rather not... Less allocs... This way code behaves almost the + * same as fgets except it can expand as needed. + ***************************************************************************** + */ +#define MIN_STEPSIZE 80 +size_t reallocFGets (char **Ptr, size_t *LenBuff, FILE *fp) +{ + char *buffer = *Ptr; /* Local copy of Ptr. */ + size_t lenBuff = *LenBuff; /* Local copy of LenBuff. */ + int c; /* Current char read from stream. */ + size_t i; /* Where to store c. */ + + myAssert (sizeof (char) == 1); + for (i = 0; ((c = getc (fp)) != EOF) && (c != '\n'); ++i) { + if (i >= lenBuff) { + lenBuff += MIN_STEPSIZE; + buffer = (char *) realloc ((void *) buffer, lenBuff); + } + buffer[i] = (char) c; + } + if (c == '\n') { + if (lenBuff <= i + 1) { + lenBuff = i + 2; /* Make room for \n\0. */ + buffer = (char *) realloc ((void *) buffer, lenBuff); + } + buffer[i] = (char) c; + ++i; + } else { + if (lenBuff <= i) { + lenBuff = i + 1; /* Make room for \0. */ + buffer = (char *) realloc ((void *) buffer, lenBuff); + } + } + buffer[i] = '\0'; + *Ptr = buffer; + *LenBuff = lenBuff; + return i; +} + +#undef MIN_STEPSIZE + +/***************************************************************************** + * mySplit() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Split a character array according to a given symbol. + * Responsibility of caller to free the memory. + * + * ARGUMENTS + * data = character string to look through. (Input) + * symbol = character to split based on. (Input) + * argc = number of groupings found. (Output) + * argv = characters in each grouping. (Output) + * f_trim = True if we should white space trim each element in list. (Input) + * + * RETURNS: void + * + * HISTORY + * 5/2004 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +void mySplit (const char *data, char symbol, size_t *Argc, char ***Argv, + char f_trim) +{ + const char *head; /* The head of the current string */ + const char *ptr; /* a pointer to walk over the data. */ + size_t argc = 0; /* Local copy of Argc */ + char **argv = NULL; /* Local copy of Argv */ + size_t len; /* length of current string. */ + + myAssert (*Argc == 0); + myAssert (*Argv == NULL); + myAssert (sizeof (char) == 1); + + head = data; + while (head != NULL) { + argv = (char **) realloc ((void *) argv, (argc + 1) * sizeof (char *)); + ptr = strchr (head, symbol); + if (ptr != NULL) { + len = ptr - head; + argv[argc] = (char *) malloc (len + 1); + strncpy (argv[argc], head, len); + argv[argc][len] = '\0'; + if (f_trim) { + strTrim (argv[argc]); + } + argc++; + head = ptr + 1; + /* The following head != NULL is in case data is not '\0' terminated + */ + if ((head != NULL) && (*head == '\0')) { + /* Handle a break character just before the \0 */ + /* This results in not adding a "" to end of list. */ + head = NULL; + } + } else { + /* Handle from here to end of text. */ + len = strlen (head); + argv[argc] = (char *) malloc (len + 1); + strcpy (argv[argc], head); + if (f_trim) { + strTrim (argv[argc]); + } + argc++; + head = NULL; + } + } + *Argc = argc; + *Argv = argv; +} + +int myAtoI (const char *ptr, sInt4 *value) +{ + char *extra; /* The data after the end of the double. */ + + myAssert (ptr != NULL); + *value = 0; + while (*ptr != '\0') { + if (isdigit (*ptr) || (*ptr == '+') || (*ptr == '-')) { + *value = strtol (ptr, &extra, 10); + myAssert (extra != NULL); + if (*extra == '\0') { + return 1; + } + break; + } else if (!isspace ((unsigned char)*ptr)) { + return 0; + } + ptr++; + } + /* Check if all white space. */ + if (*ptr == '\0') { + return 0; + } + myAssert (extra != NULL); + /* Allow first trailing char for ',' */ + if (!isspace ((unsigned char)*extra)) { + if (*extra != ',') { + *value = 0; + return 0; + } + } + extra++; + /* Make sure the rest is all white space. */ + while (*extra != '\0') { + if (!isspace ((unsigned char)*extra)) { + *value = 0; + return 0; + } + extra++; + } + return 1; +} + +/***************************************************************************** + * myAtoF() -- used to be myIsReal() + * + * Arthur Taylor / MDL + * + * PURPOSE + * Returns true if all char are digits except a leading + or -, or a + * trailing ','. Ignores leading or trailing white space. Value is set to + * atof (ptr). + * + * ARGUMENTS + * ptr = character string to look at. (Input) + * value = the converted value of ptr, if ptr is a number. (Output) + * + * RETURNS: int + * 0 = Not a real number, + * 1 = Real number. + * + * HISTORY + * 7/2004 Arthur Taylor (MDL): Updated + * 4/2005 AAT (MDL): Did a code walk through. + * + * NOTES + ***************************************************************************** + */ +int myAtoF (const char *ptr, double *value) +{ + char *extra; /* The data after the end of the double. */ + + myAssert (ptr != NULL); + *value = 0; + while (*ptr != '\0') { + if (isdigit (*ptr) || (*ptr == '+') || (*ptr == '-') || (*ptr == '.')) { + *value = strtod (ptr, &extra); + myAssert (extra != NULL); + if (*extra == '\0') { + return 1; + } + break; + } else if (!isspace ((unsigned char)*ptr)) { + return 0; + } + ptr++; + } + /* Check if all white space. */ + if (*ptr == '\0') { + return 0; + } + myAssert (extra != NULL); + /* Allow first trailing char for ',' */ + if (!isspace ((unsigned char)*extra)) { + if (*extra != ',') { + *value = 0; + return 0; + } + } + extra++; + /* Make sure the rest is all white space. */ + while (*extra != '\0') { + if (!isspace ((unsigned char)*extra)) { + *value = 0; + return 0; + } + extra++; + } + return 1; +} + +/* Change of name was to deprecate usage... Switch to myAtoF */ +int myIsReal_old (const char *ptr, double *value) +{ + size_t len, i; + + *value = 0; + if ((!isdigit (*ptr)) && (*ptr != '.')) + if (*ptr != '-') + return 0; + len = strlen (ptr); + for (i = 1; i < len - 1; i++) { + if ((!isdigit (ptr[i])) && (ptr[i] != '.')) + return 0; + } + if ((!isdigit (ptr[len - 1])) && (ptr[len - 1] != '.')) { + if (ptr[len - 1] != ',') { + return 0; + } else { +/* ptr[len - 1] = '\0';*/ + *value = atof (ptr); +/* ptr[len - 1] = ',';*/ + return 1; + } + } + *value = atof (ptr); + return 1; +} + +/* Return: + * 0 if 'can't stat the file' (most likely not a file) + * 1 if it is a directory + * 2 if it is a file + * 3 if it doesn't understand the file + */ +/* mtime may behave oddly... + * stat appeared correct if I was in EST and the file was in EST, + * but was off by 1 hour if I was in EST and the file was in EDT. + * rddirlst.c solved this through use of "clock". + * + * Could return mode: RDCF___rwxrwxrwx where R is 1/0 based on regular file + * D is 1/0 based on directory, first rwx is user permissions... + */ +int myStat (char *filename, char *perm, sInt4 *size, double *mtime) +{ + struct stat stbuf; + char f_cnt; + char *ptr; + int ans; + + myAssert (filename != NULL); + + /* Check for unmatched quotes (apparently stat on MS-Windows lets: + * ./data/ndfd/geodata\" pass, which causes issues later. */ + f_cnt = 0; + for (ptr = filename; *ptr != '\0'; ptr++) { + if (*ptr == '"') + f_cnt = !f_cnt; + } + if (f_cnt) { + /* unmatched quotes. */ + if (size) + *size = 0; + if (mtime) + *mtime = 0; + if (perm) + *perm = 0; + return 0; + } + + /* Try to stat file. */ + if ((ans = stat (filename, &stbuf)) == -1) { + if ((filename[strlen (filename) - 1] == '/') || + (filename[strlen (filename) - 1] == '\\')) { + filename[strlen (filename) - 1] = '\0'; + ans = stat (filename, &stbuf); + } + } + /* Can't stat */ + if (ans == -1) { + if (size) + *size = 0; + if (mtime) + *mtime = 0; + if (perm) + *perm = 0; + return 0; + } + + if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { + /* Is a directory */ + if (size) + *size = stbuf.st_size; + if (mtime) + *mtime = stbuf.st_mtime; + if (perm) { + *perm = (stbuf.st_mode & S_IREAD) ? 4 : 0; + if (stbuf.st_mode & S_IWRITE) + *perm += 2; + if (stbuf.st_mode & S_IEXEC) + *perm += 1; + } + return MYSTAT_ISDIR; + } else if ((stbuf.st_mode & S_IFMT) == S_IFREG) { + /* Is a file */ + if (size) + *size = stbuf.st_size; + if (mtime) + *mtime = stbuf.st_mtime; + if (perm) { + *perm = (stbuf.st_mode & S_IREAD) ? 4 : 0; + if (stbuf.st_mode & S_IWRITE) + *perm += 2; + if (stbuf.st_mode & S_IEXEC) + *perm += 1; + } + return MYSTAT_ISFILE; + } else { + /* unrecognized file type */ + if (size) + *size = 0; + if (mtime) + *mtime = 0; + if (perm) + *perm = 0; + return 3; + } +} + +/** +static int FileMatch (const char *filename, const char *filter) +{ + const char *ptr1; + const char *ptr2; + + ptr2 = filename; + for (ptr1 = filter; *ptr1 != '\0'; ptr1++) { + if (*ptr1 == '*') { + if (ptr1[1] == '\0') { + return 1; + } else { + ptr2 = strchr (ptr2, ptr1[1]); + if (ptr2 == NULL) { + return 0; + } + } + } else if (*ptr2 == '\0') { + return 0; + } else if (*ptr1 == '?') { + ptr2++; + } else { + if (*ptr1 == *ptr2) { + ptr2++; + } else { + return 0; + } + } + } + return (*ptr2 == '\0'); +} +**/ + +int myGlob (CPL_UNUSED const char *dirName, + CPL_UNUSED const char *filter, + CPL_UNUSED size_t *Argc, + CPL_UNUSED char ***Argv) +{ +return 0; // TODO: reimplement for Win32 +/* + size_t argc = 0; // Local copy of Argc + char **argv = NULL; // Local copy of Argv + struct dirent *dp; + DIR *dir; + + myAssert (*Argc == 0); + myAssert (*Argv == NULL); + + if ((dir = opendir (dirName)) == NULL) + return -1; + + while ((dp = readdir (dir)) != NULL) { + // Skip self and parent. + if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0) + continue; + if (FileMatch (dp->d_name, filter)) { + argv = (char **) realloc (argv, (argc + 1) * sizeof (char *)); + argv[argc] = (char *) malloc ((strlen (dirName) + 1 + + strlen (dp->d_name) + + 1) * sizeof (char)); + sprintf (argv[argc], "%s/%s", dirName, dp->d_name); + argc++; + } + } + *Argc = argc; + *Argv = argv; + return 0; +*/ +} + +/***************************************************************************** + * FileCopy() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Copy a file from one location to another. + * + * ARGUMENTS + * fileIn = source file to read from. (Input) + * fileOut = destation file to write to. (Input) + * + * RETURNS: int + * 0 = success. + * 1 = problems opening fileIn + * 2 = problems opening fileOut + * + * HISTORY + * 5/2004 Arthur Taylor (MDL/RSIS): Created. + * 4/2005 AAT (MDL): Did a code walk through. + * + * NOTES + ***************************************************************************** + */ +int FileCopy (const char *fileIn, const char *fileOut) +{ + FILE *ifp; /* The file pointer to read from. */ + FILE *ofp; /* The file pointer to write to. */ + int c; /* temporary variable while reading / writing. */ + + if ((ifp = fopen (fileIn, "rb")) == NULL) { +#ifdef DEBUG + printf ("Couldn't open %s for read\n", fileIn); +#endif + return 1; + } + if ((ofp = fopen (fileOut, "wb")) == NULL) { +#ifdef DEBUG + printf ("Couldn't open %s for write\n", fileOut); +#endif + fclose (ifp); + return 2; + } + while ((c = getc (ifp)) != EOF) { + putc (c, ofp); + } + fclose (ifp); + fclose (ofp); + return 0; +} + +/***************************************************************************** + * FileTail() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Returns the characters in a filename after the last directory separator. + * Responsibility of caller to free the memory. + * + * ARGUMENTS + * fileName = fileName to look at. (Input) + * tail = Tail of the filename. (Output) + * + * RETURNS: void + * + * HISTORY + * 5/2004 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +void FileTail (const char *fileName, char **tail) +{ + const char *ptr; /* A pointer to last \ or // in fileName. */ + + myAssert (fileName != NULL); + myAssert (sizeof (char) == 1); + + ptr = strrchr (fileName, '/'); + if (ptr == NULL) { + ptr = strrchr (fileName, '\\'); + if (ptr == NULL) { + ptr = fileName; + } else { + ptr++; + } + } else { + ptr++; + } + *tail = (char *) malloc (strlen (ptr) + 1); + strcpy (*tail, ptr); +} + +/***************************************************************************** + * myRound() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Round a number to a given number of decimal places. + * + * ARGUMENTS + * data = number to round (Input) + * place = How many decimals to round to (Input) + * + * RETURNS: double (rounded value) + * + * HISTORY + * 5/2003 Arthur Taylor (MDL/RSIS): Created. + * 2/2006 AAT: Added the (double) (.5) cast, and the mult by POWERS_OVER_ONE + * instead of division. + * + * NOTES + * 1) It is probably inadvisable to make a lot of calls to this routine, + * considering the fact that a context swap is made, so this is provided + * primarily as an example, but it can be used for some rounding. + ***************************************************************************** + */ +double POWERS_ONE[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17 +}; + +double myRound (double data, uChar place) +{ + if (place > 17) + place = 17; + + return (floor (data * POWERS_ONE[place] + 5e-1)) / POWERS_ONE[place]; + + /* Tried some other options to see if I could fix test 40 on linux, but + * changing it appears to make other tests fail on other OS's. */ +/* + return (((sInt4) (data * POWERS_ONE[place] + .5)) / POWERS_ONE[place]); +*/ +/* + return (floor (data * POWERS_ONE[place] + .5)) / POWERS_ONE[place]; +*/ +} + +/***************************************************************************** + * strTrim() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Trim the white space from both sides of a char string. + * + * ARGUMENTS + * str = The string to trim (Input/Output) + * + * RETURNS: void + * + * HISTORY + * 10/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * See K&R p106 for strcpy part. + ***************************************************************************** + */ +void strTrim (char *str) +{ + size_t i; /* loop counter for traversing str. */ + size_t len; /* The length of str. */ + char *ptr; /* Pointer to where first non-white space is. */ + + /* str shouldn't be null, but if it is, we want to handle it. */ + myAssert (str != NULL); + if (str == NULL) { + return; + } + + /* Remove the trailing white space before working on the leading ones. */ + len = strlen (str); + for (i = len - 1; (/* (i >= 0) && */ (isspace ((unsigned char)str[i]))); i--) { + } + len = i + 1; + str[len] = '\0'; + + /* Find first non-white space char. */ + for (ptr = str; (*ptr != '\0') && (isspace ((unsigned char)*ptr)); ptr++) { + } + + if (ptr != str) { + /* Can't do a strcpy here since we don't know that they start at left + * and go right. */ + while ((*str++ = *ptr++) != '\0') { + } + *str = '\0'; + } +} + +/***************************************************************************** + * strTrimRight() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Trim white space and a given char from the right. + * + * ARGUMENTS + * str = The string to trim (Input/Output) + * c = The character to remove. (Input) + * + * RETURNS: void + * + * HISTORY + * 7/2004 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +void strTrimRight (char *str, char c) +{ + size_t i; /* loop counter for traversing str. */ + + /* str shouldn't be null, but if it is, we want to handle it. */ + myAssert (str != NULL); + if (str == NULL) { + return; + } + + for (i = strlen (str) - 1; + (/* (i >= 0) && */ ((isspace ((unsigned char)str[i])) || (str[i] == c))); i--) { + } + str[i + 1] = '\0'; +} + +/***************************************************************************** + * strCompact() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Replace any multiple instances of 'c' in the string with 1 instance. + * + * ARGUMENTS + * str = The string to compact (Input/Output) + * c = The character to look for. (Input) + * + * RETURNS: void + * + * HISTORY + * 10/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +void strCompact (char *str, char c) +{ + char *ptr; /* The next good value in str to keep. */ + + /* str shouldn't be null, but if it is, we want to handle it. */ + myAssert (str != NULL); + if (str == NULL) { + return; + } + + ptr = str; + while ((*str = *(ptr++)) != '\0') { + if (*(str++) == c) { + while ((*ptr != '\0') && (*ptr == c)) { + ptr++; + } + } + } +} + +/***************************************************************************** + * strReplace() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Replace all instances of c1 in str with c2. + * + * ARGUMENTS + * str = The string to trim (Input/Output) + * c1 = The character(s) in str to be replaced. (Input) + * c2 = The char to replace c1 with. (Input) + * + * RETURNS: void + * + * HISTORY + * 7/2004 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +void strReplace (char *str, char c1, char c2) +{ + char *ptr = str; + + /* str shouldn't be null, but if it is, we want to handle it. */ + myAssert (str != NULL); + if (str == NULL) { + return; + } + + for (ptr = str; *ptr != '\0'; ptr++) { + if (*ptr == c1) { + *ptr = c2; + } + } +} + +/***************************************************************************** + * strToUpper() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Convert a string to all uppercase. + * + * ARGUMENTS + * str = The string to adjust (Input/Output) + * + * RETURNS: void + * + * HISTORY + * 10/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +void strToUpper (char *str) +{ + char *ptr = str; /* Used to traverse str. */ + + /* str shouldn't be null, but if it is, we want to handle it. */ + myAssert (str != NULL); + if (str == NULL) { + return; + } + + while ((*ptr++ = toupper (*str++)) != '\0') { + } +} + +/***************************************************************************** + * strToLower() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Convert a string to all lowercase. + * + * ARGUMENTS + * str = The string to adjust (Input/Output) + * + * RETURNS: void + * + * HISTORY + * 5/2004 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +void strToLower (char *str) +{ + char *ptr = str; /* Used to traverse str. */ + + /* str shouldn't be null, but if it is, we want to handle it. */ + myAssert (str != NULL); + if (str == NULL) { + return; + } + + while ((*ptr++ = tolower (*str++)) != '\0') { + } +} + +/* + * Returns: Length of the string. + * History: 1/29/98 AAT Commented. + * +int str2lw (char *s) { + int i = 0, len = strlen (s); + while (i < len) { + s[i] = (char) tolower(s[i]); + i++; + } + return len; +} +*/ + +/***************************************************************************** + * strcmpNoCase() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Compare two strings without concern for case. + * + * ARGUMENTS + * str1 = String1 to compare (Input) + * str2 = String2 to compare (Input) + * + * RETURNS: int + * -1 = (str1 < str2) + * 0 = (str1 == str2) + * 1 = (str1 > str2) + * + * HISTORY + * 5/2004 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * See K&R p 106 + ***************************************************************************** + */ +int strcmpNoCase (const char *str1, const char *str2) +{ + /* str1, str2 shouldn't be null, but if it is, we want to handle it. */ + myAssert (str1 != NULL); + myAssert (str2 != NULL); + if (str1 == NULL) { + if (str2 == NULL) { + return 0; + } else { + return -1; + } + } + if (str2 == NULL) { + return 1; + } + + for (; tolower (*str1) == tolower (*str2); str1++, str2++) { + if (*str1 == '\0') + return 0; + } + return (tolower (*str1) - tolower (*str2) < 0) ? -1 : 1; +/* + strlen1 = strlen (str1); + strlen2 = strlen (str2); + min = (strlen1 < strlen2) ? strlen1 : strlen2; + for (i = 0; i < min; i++) { + c1 = tolower (str1[i]); + c2 = tolower (str2[i]); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } + if (strlen1 < strlen2) { + return -1; + } + if (strlen1 > strlen2) { + return 1; + } + return 0; +*/ +} + + /***************************************************************************** + * GetIndexFromStr() -- Review 12/2002 + * + * Arthur Taylor / MDL + * + * PURPOSE + * Looks through a list of strings (with a NULL value at the end) for a + * given string. Returns the index where it found it. + * + * ARGUMENTS + * str = The string to look for. (Input) + * Opt = The list to look for arg in. (Input) + * Index = The location of arg in Opt (or -1 if it couldn't find it) (Output) + * + * RETURNS: int + * # = Where it found it. + * -1 = Couldn't find it. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL/RSIS): Created. + * 12/2002 (TK,AC,TB,&MS): Code Review. + * + * NOTES + * Why not const char **Opt? + ***************************************************************************** + */ +int GetIndexFromStr (const char *str, char **Opt, int *Index) +{ + int cnt = 0; /* Current Count in Opt. */ + + myAssert (str != NULL); + if (str == NULL) { + *Index = -1; + return -1; + } + + for (; *Opt != NULL; Opt++, cnt++) { + if (strcmp (str, *Opt) == 0) { + *Index = cnt; + return cnt; + } + } + *Index = -1; + return -1; +} + +/***************************************************************************** + * Clock_GetTimeZone() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Returns the time zone offset in hours to add to local time to get UTC. + * So EST is +5 not -5. + * + * ARGUMENTS + * + * RETURNS: sInt2 + * + * HISTORY + * 6/2004 Arthur Taylor (MDL): Created. + * 3/2005 AAT: Found bug... Used to use 1/1/1970 00Z and find the local + * hour. If CET, this means use 1969 date, which causes it to die. + * Switched to 1/2/1970 00Z. + * 3/2005 AAT: timeZone (see CET) can be < 0. don't add 24 if timeZone < 0 + * + * NOTES + ***************************************************************************** + */ +static sChar Clock_GetTimeZone () +{ + struct tm time; + time_t ansTime; + struct tm *gmTime; + static sChar timeZone = 127; + + if (timeZone == 127) { + /* Cheap method of getting global time_zone variable. */ + memset (&time, 0, sizeof (struct tm)); + time.tm_year = 70; + time.tm_mday = 2; + ansTime = mktime (&time); + gmTime = gmtime (&ansTime); + timeZone = gmTime->tm_hour; + if (gmTime->tm_mday != 2) { + timeZone -= 24; + } + } + return timeZone; +} + +/***************************************************************************** + * myParseTime() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Parse a string such as "19730724000000" and return time since the + * beginning of the epoch. + * + * ARGUMENTS + * is = String to read the date from (Input) + * AnsTime = Time to String2 to compare (Input) + * + * RETURNS: int + * 0 = success + * 1 = error + * + * HISTORY + * 4/2005 Arthur Taylor (MDL): Commented + * + * NOTES + * Rename (myParseTime -> myParseTime2) because changed error return from + * -1 to 1 + * Rename (myParseTime2 -> myParseTime3) because I'm trying to phase it out. + * Use: int Clock_ScanDateNumber (double *clock, char *buffer) instead. + ***************************************************************************** + */ +int myParseTime3 (const char *is, time_t * AnsTime) +{ + char buffer[5]; /* A temporary variable for parsing "is". */ + sShort2 year; /* The year. */ + uChar mon; /* The month. */ + uChar day; /* The day. */ + uChar hour; /* The hour. */ + uChar min; /* The minute. */ + uChar sec; /* The second. */ + struct tm time; /* A temporary variable to put the time info into. */ + + memset (&time, 0, sizeof (struct tm)); + myAssert (strlen (is) == 14); + if (strlen (is) != 14) { + printf ("%s is not formated correctly\n", is); + return 1; + } + strncpy (buffer, is, 4); + buffer[4] = '\0'; + year = atoi (buffer); + strncpy (buffer, is + 4, 2); + buffer[2] = '\0'; + mon = atoi (buffer); + strncpy (buffer, is + 6, 2); + day = atoi (buffer); + strncpy (buffer, is + 8, 2); + hour = atoi (buffer); + strncpy (buffer, is + 10, 2); + min = atoi (buffer); + strncpy (buffer, is + 12, 2); + sec = atoi (buffer); + if ((year > 2001) || (year < 1900) || (mon > 12) || (mon < 1) || + (day > 31) || (day < 1) || (hour > 23) || (min > 59) || (sec > 60)) { + printf ("date %s is invalid\n", is); + printf ("%d %d %d %d %d %d\n", year, mon, day, hour, min, sec); + return 1; + } + time.tm_year = year - 1900; + time.tm_mon = mon - 1; + time.tm_mday = day; + time.tm_hour = hour; + time.tm_min = min; + time.tm_sec = sec; + *AnsTime = mktime (&time) - (Clock_GetTimeZone () * 3600); + return 0; +} + +#ifdef MYUTIL_TEST +int main (int argc, char **argv) +{ + char buffer[] = "Hello , World, This, is, a , test\n"; + char buffer2[] = ""; + size_t listLen = 0; + char **List = NULL; + size_t i; + size_t j; + char ans; + double value; + char *tail; + +/* + printf ("1 :: %f\n", clock() / (double) (CLOCKS_PER_SEC)); + for (j = 0; j < 25000; j++) { + mySplit (buffer, ',', &listLen, &List, 1); + for (i = 0; i < listLen; i++) { + free (List[i]); + } + free (List); + List = NULL; + listLen = 0; + } + printf ("1 :: %f\n", clock() / (double) (CLOCKS_PER_SEC)); +*/ + mySplit (buffer, ',', &listLen, &List, 1); + for (i = 0; i < listLen; i++) { + printf ("%d:'%s'\n", i, List[i]); + free (List[i]); + } + free (List); + List = NULL; + listLen = 0; + + mySplit (buffer2, ',', &listLen, &List, 1); + for (i = 0; i < listLen; i++) { + printf ("%d:'%s'\n", i, List[i]); + free (List[i]); + } + free (List); + List = NULL; + listLen = 0; + + strcpy (buffer, " 0.95"); + ans = myAtoF (buffer, &value); + printf ("%d %f : ", ans, value); + ans = myIsReal_old (buffer, &value); + printf ("%d %f : '%s'\n", ans, value, buffer); + + strcpy (buffer, "0.95"); + ans = myAtoF (buffer, &value); + printf ("%d %f : ", ans, value); + ans = myIsReal_old (buffer, &value); + printf ("%d %f : '%s'\n", ans, value, buffer); + + strcpy (buffer, "+0.95"); + ans = myAtoF (buffer, &value); + printf ("%d %f : ", ans, value); + ans = myIsReal_old (buffer, &value); + printf ("%d %f : '%s'\n", ans, value, buffer); + + strcpy (buffer, "0.95, "); + ans = myAtoF (buffer, &value); + printf ("%d %f : ", ans, value); + ans = myIsReal_old (buffer, &value); + printf ("%d %f : '%s'\n", ans, value, buffer); + + strcpy (buffer, "0.95,"); + ans = myAtoF (buffer, &value); + printf ("%d %f : ", ans, value); + ans = myIsReal_old (buffer, &value); + printf ("%d %f : '%s'\n", ans, value, buffer); + + strcpy (buffer, "0.9.5"); + ans = myAtoF (buffer, &value); + printf ("%d %f : ", ans, value); + ans = myIsReal_old (buffer, &value); + printf ("%d %f : '%s'\n", ans, value, buffer); + + strcpy (buffer, " alph 0.9.5"); + ans = myAtoF (buffer, &value); + printf ("%d %f : ", ans, value); + ans = myIsReal_old (buffer, &value); + printf ("%d %f : '%s'\n", ans, value, buffer); + + strcpy (buffer, " "); + ans = myAtoF (buffer, &value); + printf ("%d %f : ", ans, value); + ans = myIsReal_old (buffer, &value); + printf ("%d %f : '%s'\n", ans, value, buffer); + + strcpy (buffer, ""); + ans = myAtoF (buffer, &value); + printf ("%d %f : ", ans, value); + ans = myIsReal_old (buffer, &value); + printf ("%d %f : '%s'\n", ans, value, buffer); + + tail = NULL; + FileTail ("test\\me/now", &tail); + printf ("%s \n", tail); + free (tail); + tail = NULL; + FileTail ("test/me\\now", &tail); + printf ("%s \n", tail); + free (tail); + + strcpy (buffer, " here "); + strTrim (buffer); + printf ("%s\n", buffer); + + strcpy (buffer, " here "); + strCompact (buffer, ' '); + printf ("'%s'\n", buffer); + return 0; +} +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myutil.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myutil.h new file mode 100644 index 000000000..af773e2db --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/myutil.h @@ -0,0 +1,72 @@ +/***************************************************************************** + * myutil.c + * + * DESCRIPTION + * This file contains some simple utility functions. + * + * HISTORY + * 12/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifndef MYUTIL_H +#define MYUTIL_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include +#include "type.h" + +size_t reallocFGets (char **Ptr, size_t *LenBuff, FILE * fp); + +void mySplit (const char *data, char symbol, size_t *Argc, char ***Argv, + char f_trim); + +int myAtoI (const char *ptr, sInt4 *value); + +int myAtoF (const char *ptr, double *value); +/* Change of name was to deprecate usage... Switch to myAtoF */ +int myIsReal_old (const char *ptr, double *value); + +#define MYSTAT_ISFILE 2 +#define MYSTAT_ISDIR 1 +int myStat (char *filename, char *perm, sInt4 *size, double *mtime); + +int myGlob (const char *dirName, const char *filter, size_t * Argc, + char ***Argv); + +int FileCopy (const char *fileIn, const char *fileOut); + +void FileTail (const char *fileName, char **tail); + +double myRound (double data, uChar place); + +void strTrim (char *str); + +void strTrimRight (char *str, char c); + +void strCompact (char *str, char c); + +void strReplace (char *str, char c1, char c2); + +void strToUpper (char *str); + +void strToLower (char *str); + +int strcmpNoCase (const char *str1, const char *str2); + +int GetIndexFromStr (const char *str, char **Opt, int *Index); + +/* Rename because changed error return from -1 to 1 */ +/* Rename because trying to phase it out. */ +int myParseTime3 (const char *is, time_t * AnsTime); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MYUTIL_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/scan.c b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/scan.c new file mode 100644 index 000000000..7f905d43f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/scan.c @@ -0,0 +1,260 @@ +/***************************************************************************** + * scan.c + * + * DESCRIPTION + * This file contains the code that is used to assist with handling the + * possible scan values of the grid. + * + * HISTORY + * 10/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#include "scan.h" + +/***************************************************************************** + * ScanIndex2XY() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To convert from the index of the GRIB2 message which is defined by the + * scan parameter, to one that seemed reasonable. The choice for internal + * array orientation boiled down to either (scan = 0000) (start from upper + * left and accross similar to a CRT screen) or (scan = 0100) (start at lower + * left and go up ). + * It was decided that (scan 0100) was what people expected. The only catch + * is that Spatial Analyst requires (scan = 0000), so when writing to that + * format we have to switch. + * For more info on scan flags: see Grib2 "Flag" Table 3.4 + * + * ARGUMENTS + * row = The index in the scaned in data. (Input) + * X, Y = The x,y position in a scan == 0100 world. (Output) + * scan = The orientation of the GRIB2 grid. (Input) + * Nx, Ny = The Dimensions of the grid (Input). + * + * FILES/DATABASES: None + * + * RETURNS: void + * Returns x, y, in bounds of [1..Nx], [1..Ny] + * Assuming row is in [0..Nx*Ny) + * + * HISTORY + * 10/2002 Arthur Taylor (MDL/RSIS): Created. + * 7/2003 AAT: Switched to x, y [1..Nx] because that is what the map + * routines give. + * + * NOTES + * scan based on Grib2 "Flag" Table 3.4 + * scan & GRIB2BIT_1 => decrease x + * scan & GRIB2BIT_2 => increase y + * scan & GRIB2BIT_3 => adjacent points in y direction consecutive. + * scan & GRIB2BIT_4 => adjacent rows scan in opposite directions. + ***************************************************************************** + */ +void ScanIndex2XY (sInt4 row, sInt4 * X, sInt4 * Y, uChar scan, sInt4 Nx, + sInt4 Ny) +{ + sInt4 x; /* local copy of x */ + sInt4 y; /* local copy of y */ + + if (scan & GRIB2BIT_3) { + x = row / Ny; + if ((scan & GRIB2BIT_4) && ((x % 2) == 1)) { + y = (Ny - 1) - (row % Ny); + } else { + y = row % Ny; + } + } else { + y = row / Nx; + if ((scan & GRIB2BIT_4) && ((y % 2) == 1)) { + x = (Nx - 1) - (row % Nx); + } else { + x = row % Nx; + } + } + if (scan & GRIB2BIT_1) { + x = (Nx - 1 - x); + } + if (!(scan & GRIB2BIT_2)) { + y = (Ny - 1 - y); + } + /* Changed following two lines (with the + 1) on 7/22/2003 */ + *X = x + 1; + *Y = y + 1; +} + +/***************************************************************************** + * XY2ScanIndex() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To convert from an x,y coordinate system that matches scan = 0100 to the + * scan index of the GRIB2 message as defined by the scan parameter. + * This tends to be less important than ScanIndex2XY, but is provided for + * testing purposes, and in case it is useful. + * + * ARGUMENTS + * Row = The index in the scaned in data. (Output) + * x, y = The x,y position in a (scan = 0100) world. (Input) + * scan = The orientation of the GRIB2 grid. (Input) + * Nx, Ny = The Dimensions of the grid (Input). + * + * FILES/DATABASES: None + * + * RETURNS: void + * Returns row in [0..Nx*Ny) + * Assuming x, y, is in bounds of [1..Nx], [1..Ny] + * + * HISTORY + * 10/2002 Arthur Taylor (MDL/RSIS): Created. + * 7/2003 AAT: Switched to x, y [1..Nx] because that is what the map + * routines give. + * + * NOTES + * scan based on Grib2 "Flag" Table 3.4 + * scan & GRIB2BIT_1 => decrease x + * scan & GRIB2BIT_2 => increase y + * scan & GRIB2BIT_3 => adjacent points in y direction consecutive. + * scan & GRIB2BIT_4 => adjacent rows scan in opposite directions. + ***************************************************************************** + */ +void XY2ScanIndex (sInt4 * Row, sInt4 x, sInt4 y, uChar scan, sInt4 Nx, + sInt4 Ny) +{ + sInt4 row; /* local copy of row */ + + /* Added following two lines on 7/22/2003 */ + x = x - 1; + y = y - 1; + if (scan & GRIB2BIT_1) { + x = (Nx - 1 - x); + } + if (!(scan & GRIB2BIT_2)) { + y = (Ny - 1 - y); + } + if (scan & GRIB2BIT_3) { + if ((scan & GRIB2BIT_4) && ((x % 2) == 1)) { + row = Ny - 1 - y + x * Ny; + } else { + row = y + x * Ny; + } + } else { + if ((scan & GRIB2BIT_4) && ((y % 2) == 1)) { + row = Nx - 1 - x + y * Nx; + } else { + row = x + y * Nx; + } + } + *Row = row; +} + +/***************************************************************************** + * main() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To test the ScanIndex2XY, and XY2ScanIndex routines, to make sure that + * they are inverses of each other, for all possible scan values. Also to + * see what a sample array looks like in the various scans, and to make sure + * that we are generating (scan = 0100) data. + * + * ARGUMENTS + * argc = The number of arguments on the command line. (Input) + * argv = The arguments on the command line. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 10/2002 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifdef TEST_SCAN +#include +int main (int argc, char **argv) +{ + int data[3][4]; + int ray1[6]; + int ray2[6]; + sInt4 Nx = 2, Ny = 3; + sInt4 NxNy = 6; + sInt4 row, x, y; + sInt4 x1, y1; + int i; + int scan; + + /* Set up sample data. */ + for (x = 1; x <= Nx; x++) { + for (y = 1; y <= Ny; y++) { + data[x][y] = 1 + x + (y * 2); + } + } + for (i = 0; i < 16; i++) { + scan = i << 4; + /* Print scan info. */ + printf ("Checking xy2row -> row2xy for scan %d ", i); + if (scan & GRIB2BIT_1) + printf ("-1"); + else + printf ("-0"); + if (scan & GRIB2BIT_2) + printf ("-1"); + else + printf ("-0"); + if (scan & GRIB2BIT_3) + printf ("-1"); + else + printf ("-0"); + if (scan & GRIB2BIT_4) + printf ("-1"); + else + printf ("-0"); + printf ("\n"); + + /* Test invertiblity of functions. */ + for (x = 1; x <= Nx; x++) { + for (y = 1; y <= Ny; y++) { + XY2ScanIndex (&row, x, y, scan, Nx, Ny); + ScanIndex2XY (row, &x1, &y1, scan, Nx, Ny); + if ((x1 != x) || (y1 != y)) { + printf (" %ld %ld .. %ld .. %ld %ld \n", x, y, row, x1, y1); + } + } + } + + /* Set up sample scan data. */ + for (x = 1; x <= Nx; x++) { + for (y = 1; y <= Ny; y++) { + XY2ScanIndex (&row, x, y, scan, Nx, Ny); + ray1[row] = data[x][y]; + } + } + + /* Convert from ray1[] to ray2[] where ray2[] is scan value 0100. */ + for (x = 0; x < NxNy; x++) { + printf ("%d ", ray1[x]); + ScanIndex2XY (x, &x1, &y1, scan, Nx, Ny); + /* + * To get scan 0000 do the following: + * row = x1 + ((Ny-1) - y1) * Nx; + */ + row = (x1 - 1) + (y1 - 1) * Nx; + ray2[row] = ray1[x]; + } + printf ("\n"); + for (x = 0; x < NxNy; x++) { + printf ("%d ", ray2[x]); + } + printf ("\n"); + } + return 0; +} +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/scan.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/scan.h new file mode 100644 index 000000000..c8e8181ea --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/scan.h @@ -0,0 +1,40 @@ +/***************************************************************************** + * scan.h + * + * DESCRIPTION + * This file contains the code that is used to assist with handling the + * possible scan values of the grid. + * + * HISTORY + * 9/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifndef SCAN_H +#define SCAN_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef GRIB2BIT_ENUM +#define GRIB2BIT_ENUM +/* See rule (8) bit 1 is most significant, bit 8 least significant. */ +enum {GRIB2BIT_1=128, GRIB2BIT_2=64, GRIB2BIT_3=32, GRIB2BIT_4=16, + GRIB2BIT_5=8, GRIB2BIT_6=4, GRIB2BIT_7=2, GRIB2BIT_8=1}; +#endif + +#include "type.h" + +void XY2ScanIndex (sInt4 *Row, sInt4 x, sInt4 y, uChar scan, sInt4 Nx, + sInt4 Ny); + +void ScanIndex2XY (sInt4 row, sInt4 *X, sInt4 *Y, uChar scan, sInt4 Nx, + sInt4 Ny); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SCAN_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/tdlpack.cpp b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/tdlpack.cpp new file mode 100644 index 000000000..ba6a43c15 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/tdlpack.cpp @@ -0,0 +1,4477 @@ +#include +#include +#include +#include +#include "tdlpack.h" +#include "myerror.h" +#include "meta.h" +#include "memendian.h" +#include "fileendian.h" +#include "myassert.h" +#include "myutil.h" +#include "clock.h" +#ifdef MEMWATCH +#include "memwatch.h" +#endif + +#define GRIB_UNSIGN_INT3(a,b,c) ((a<<16)+(b<<8)+c) +#define GRIB_UNSIGN_INT2(a,b) ((a<<8)+b) +#define GRIB_SIGN_INT3(a,b,c) ((1-(int) ((unsigned) (a & 0x80) >> 6)) * (int) (((a & 127) << 16)+(b<<8)+c)) +#define GRIB_SIGN_INT2(a,b) ((1-(int) ((unsigned) (a & 0x80) >> 6)) * (int) (((a & 0x7f) << 8) + b)) + +/* *INDENT-OFF* */ +TDLP_TableType TDLP_B_Table[5] = { + /* 0 */ {0, "Continuous field"}, + /* 1 */ {1, "Point Binary - cumulative from above"}, + /* 2 */ {2, "Point Binary - cumulative from below"}, + /* 3 */ {3, "Point Binary - discrete"}, + /* 4 */ {5, "Grid Binary - "}, +}; + +TDLP_TableType TDLP_DD_Table[9] = { + /* 0 */ {0, "Independent of model"}, + /* 1 */ {6, "NGM model"}, + /* 2 */ {7, "Eta model"}, + /* 3 */ {8, "AVN model"}, + /* 4 */ {9, "MRF model"}, + /* 5 */ {79, "NDFD forecast"}, + /* 6 */ {80, "MOS AEV forecast"}, + /* 7 */ {81, "Local AEV Firecasts"}, + /* 8 */ {82, "Obs matching AEV Forecasts"}, +}; + +TDLP_TableType TDLP_V_Table[4] = { + /* 0 */ {0, "No vertical processing"}, + /* 1 */ {1, "Difference Levels (UUUU - LLLL)"}, + /* 2 */ {2, "Sum Levels (UUUU + LLLL)"}, + /* 3 */ {3, "Mean Levels (UUUU + LLLL) / 2."}, +}; + +TDLP_TableType TDLP_T_Table[3] = { + /* 0 */ {0, "No nolinear tranform"}, + /* 1 */ {1, "Square transform"}, + /* 2 */ {2, "Square root tranform"}, +}; + +TDLP_TableType TDLP_Oper_Table[9] = { + /* 0 */ {0, "No time operator"}, + /* 1 */ {1, "Mean (Var 1, Var 2)"}, + /* 2 */ {2, "Difference (Var 1 - Var 2)"}, + /* 3 */ {3, "Maximum (Var 1, Var 2)"}, + /* 4 */ {4, "Minimum (Var 1, Var 2)"}, + /* 5 */ {5, "Mean (Var 1, Var 2)"}, + /* 6 */ {6, "Difference (Var 2 - Var 1)"}, + /* 7 */ {7, "Maximum (Var 1, Var 2)"}, + /* 8 */ {8, "Minimum (Var 1, Var 2)"}, +}; + +TDLP_TableType TDLP_I_Table[4] = { + /* 0 */ {0, "No interpolation"}, + /* 1 */ {1, "Bi-quadratic interpolation"}, + /* 2 */ {2, "Bi-linear interpolation"}, + /* 3 */ {3, "Special interpolation for QPF"}, +}; + +TDLP_TableType TDLP_S_Table[6] = { + /* 0 */ {0, "No smoothing"}, + /* 1 */ {1, "5-point smoothing"}, + /* 2 */ {2, "9-point smoothing"}, + /* 3 */ {3, "25-point smoothing"}, + /* 4 */ {4, "81-point smoothing"}, + /* 5 */ {5, "168-point smoothing"}, +}; +/* *INDENT-ON* */ + +typedef struct { + sInt4 min; /* Minimum value in a group. Can cause problems if + * this is unsigned. */ + uChar bit; /* # of bits in a group. 2^31 is the largest # of + * bits that can be used to hold # of bits in a + * group. However, the # of bits for a number can't + * be > 64, and is probably < 32, so bit is < 6 and + * probably < 5. */ + uInt4 num; /* number of values in the group. May need this to be + * signed. */ + sInt4 max; /* Max value for the group */ + uInt4 start; /* index in Data where group starts. */ + uChar f_trySplit; /* Flag, if we have tried to split this group. */ + uChar f_tryShift; /* Flag, if we have tried to shift this group. */ +} TDLGroupType; + +/***************************************************************************** + * ReadTDLPSect1() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Parses the TDLP "Product Definition Section" or section 1, filling out + * the pdsMeta data structure. + * + * ARGUMENTS + * pds = The compressed part of the message dealing with "PDS". (Input) + * gribLen = The total length of the TDLP message. (Input) + * curLoc = Current location in the TDLP message. (Output) + * pdsMeta = The filled out pdsMeta data structure. (Output) + * f_gds = boolean if there is a Grid Definition Section. (Output) + * f_bms = boolean if there is a Bitmap Section. (Output) + * DSF = Decimal Scale Factor for unpacking the data. (Output) + * BSF = Binary Scale Factor for unpacking the data. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = gribLen is too small. + * + * HISTORY + * 10/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +static int ReadTDLPSect1 (uChar *pds, sInt4 tdlpLen, sInt4 *curLoc, + pdsTDLPType * pdsMeta, char *f_gds, char *f_bms, + short int *DSF, short int *BSF) +{ + char sectLen; /* Length of section. */ + sInt4 li_temp; /* Temporary variable. */ + int W, XXXX, YY; /* Helps with Threshold calculation. */ + int year, t_year; /* The reference year, and a consistency test */ + uChar month, t_month; /* The reference month, and a consistency test */ + uChar day, t_day; /* The reference day, and a consistency test */ + uChar hour, t_hour; /* The reference hour, and a consistency test */ + uChar min; /* The reference minute */ + uShort2 project_hr; /* The projection in hours. */ + sInt4 tau; /* Used to cross check project_hr */ + int lenPL; /* Length of the Plain Language descriptor. */ + + sectLen = *(pds++); + *curLoc += sectLen; + if (*curLoc > tdlpLen) { + errSprintf ("Ran out of data in PDS (TDLP Section 1)\n"); + return -1; + } + myAssert (sectLen <= 71); + if (sectLen < 39) { + errSprintf ("TDLP Section 1 is too small.\n"); + return -1; + } + *f_bms = (GRIB2BIT_7 & *pds) ? 1 : 0; + *f_gds = (GRIB2BIT_8 & *pds) ? 1 : 0; + pds++; + year = GRIB_UNSIGN_INT2 (*pds, pds[1]); + pds += 2; + month = *(pds++); + day = *(pds++); + hour = *(pds++); + min = *(pds++); + MEMCPY_BIG (&li_temp, pds, sizeof (sInt4)); + pds += 4; + t_year = li_temp / 1000000L; + li_temp -= t_year * 1000000L; + t_month = li_temp / 10000L; + li_temp -= t_month * 10000L; + t_day = li_temp / 100; + t_hour = li_temp - t_day * 100; + if ((t_year != year) || (t_month != month) || (t_day != day) || + (t_hour != hour)) { + errSprintf ("Error Inconsistent Times in ReadTDLPSect1.\n"); + return -1; + } + if (ParseTime (&(pdsMeta->refTime), year, month, day, hour, min, 0) != 0) { + preErrSprintf ("Error In call to ParseTime in ReadTDLPSect1.\n"); + return -1; + } + MEMCPY_BIG (&(li_temp), pds, sizeof (sInt4)); + pds += 4; + pdsMeta->ID1 = li_temp; + pdsMeta->CCC = li_temp / 1000000L; + li_temp -= pdsMeta->CCC * 1000000L; + pdsMeta->FFF = li_temp / 1000; + li_temp -= pdsMeta->FFF * 1000; + pdsMeta->B = li_temp / 100; + pdsMeta->DD = li_temp - pdsMeta->B * 100; + MEMCPY_BIG (&(li_temp), pds, sizeof (sInt4)); + pds += 4; + pdsMeta->ID2 = li_temp; + pdsMeta->V = li_temp / 100000000L; + li_temp -= pdsMeta->V * 100000000L; + pdsMeta->LLLL = li_temp / 10000; + pdsMeta->UUUU = li_temp - pdsMeta->LLLL * 10000; + MEMCPY_BIG (&(li_temp), pds, sizeof (sInt4)); + pds += 4; + pdsMeta->ID3 = li_temp; + pdsMeta->T = li_temp / 100000000L; + li_temp -= pdsMeta->T * 100000000L; + pdsMeta->RR = li_temp / 1000000L; + li_temp -= pdsMeta->RR * 1000000L; + pdsMeta->Oper = li_temp / 100000L; + li_temp -= pdsMeta->Oper * 100000L; + pdsMeta->HH = li_temp / 1000; + pdsMeta->ttt = li_temp - pdsMeta->HH * 1000; + MEMCPY_BIG (&(li_temp), pds, sizeof (sInt4)); + pds += 4; + pdsMeta->ID4 = li_temp; + W = li_temp / 1000000000L; + li_temp -= W * 1000000000L; + XXXX = li_temp / 100000L; + li_temp -= XXXX * 100000L; + if (W) { + XXXX = -1 * XXXX; + } + YY = li_temp / 1000L; + li_temp -= YY * 1000L; + if (YY >= 50) { + YY = -1 * (YY - 50); + } + pdsMeta->thresh = (XXXX / 10000.) * pow (10.0, YY); + pdsMeta->I = li_temp / 100; + li_temp -= pdsMeta->I * 100L; + pdsMeta->S = li_temp / 10; + pdsMeta->G = li_temp - pdsMeta->S * 10; + project_hr = GRIB_UNSIGN_INT2 (*pds, pds[1]); + tau = pdsMeta->ID3 - ((pdsMeta->ID3 / 1000) * 1000); + if (tau != project_hr) { + printf ("Warning: Inconsistent Projections in hours in " + "ReadTDLPSect1 (%d vs %d)\n", tau, project_hr); +/* + errSprintf ("Warning: Inconsistent Projections in hours in " + "ReadTDLPSect1 (%ld vs %d)\n", tau, project_hr); +*/ + project_hr = tau; +/* return -1; */ + } + pds += 2; + pdsMeta->project = project_hr * 3600 + (*(pds++)) * 60; + pdsMeta->procNum = (*(pds++)); + pdsMeta->seqNum = (*(pds++)); + *DSF = (*pds > 128) ? 128 - (*(pds++)) : (*(pds++)); + *BSF = (*pds > 128) ? 128 - (*(pds++)) : (*(pds++)); + if ((*pds != 0) || (pds[1] != 0) || (pds[2] != 0)) { + errSprintf ("Error Reserved was not set to 0 in ReadTDLPSect1.\n"); + return -1; + } + pds += 3; + lenPL = (*(pds++)); + if (sectLen - lenPL != 39) { + errSprintf ("Error sectLen(%d) - lenPL(%d) != 39 in ReadTDLPSect1.\n", + sectLen, lenPL); + return -1; + } + if (lenPL > 32) { + lenPL = 32; + } + strncpy (pdsMeta->Descriptor, (char *) pds, lenPL); + pdsMeta->Descriptor[lenPL] = '\0'; + strTrim (pdsMeta->Descriptor); + return 0; +} + +/***************************************************************************** + * TDLP_TableLookUp() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To look up TDL Ids information in a given table. + * + * ARGUMENTS + * table = The Table to look up the Id in. (Input) + * tableLen = The length of the Table. (Input) + * index = The index into the Table. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: char * + * Returns the meta data that is associated with index in the table. + * + * HISTORY + * 10/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +static const char *TDLP_TableLookUp(TDLP_TableType * table, int tableLen, + int index) +{ + int i; /* Loop counter. */ + + for (i = 0; i < tableLen; i++) { + if (table[i].index == index) + return (table[i].data); + } + return "Unknown"; +} + +/***************************************************************************** + * PrintPDS_TDLP() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To generate the message for the Product Definition Sections of the TDLP + * Message. + * + * ARGUMENTS + * pds = The TDLP Product Definition Section to print. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 10/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +void PrintPDS_TDLP (pdsTDLPType * pds) +{ + char buffer[25]; /* Stores format of pds1->refTime. */ + +/* + strftime (buffer, 25, "%m/%d/%Y %H:%M:%S UTC", gmtime (&(pds->refTime))); +*/ + Clock_Print (buffer, 25, pds->refTime, "%m/%d/%Y %H:%M:%S UTC", 0); + + Print ("PDS-TDLP", "Reference Time", Prt_S, buffer); + Print ("PDS-TDLP", "Plain Language", Prt_S, pds->Descriptor); + sprintf (buffer, "%09d", pds->ID1); + Print ("PDS-TDLP", "ID 1", Prt_S, buffer); + sprintf (buffer, "%09d", pds->ID2); + Print ("PDS-TDLP", "ID 2", Prt_S, buffer); + sprintf (buffer, "%09d", pds->ID3); + Print ("PDS-TDLP", "ID 3", Prt_S, buffer); + Print ("PDS-TDLP", "ID 4", Prt_D, pds->ID4); + Print ("PDS-TDLP", "Model or Process Number", Prt_D, pds->procNum); + Print ("PDS-TDLP", "Sequence Number", Prt_D, pds->seqNum); + + sprintf (buffer, "%03d", pds->CCC); + Print ("PDS-TDLP", "ID1-CCC", Prt_S, buffer); + sprintf (buffer, "%03d", pds->FFF); + Print ("PDS-TDLP", "ID1-FFF", Prt_S, buffer); + Print ("PDS-TDLP", "ID1-B", Prt_DS, pds->B, + TDLP_TableLookUp (TDLP_B_Table, sizeof (TDLP_B_Table), pds->B)); + sprintf (buffer, "%02d", pds->DD); + Print ("PDS-TDLP", "ID1-DD", Prt_SS, buffer, + TDLP_TableLookUp (TDLP_DD_Table, sizeof (TDLP_DD_Table), pds->DD)); + + Print ("PDS-TDLP", "ID2-V", Prt_DS, pds->V, + TDLP_TableLookUp (TDLP_V_Table, sizeof (TDLP_V_Table), pds->V)); + sprintf (buffer, "%04d", pds->LLLL); + Print ("PDS-TDLP", "ID2-LLLL", Prt_S, buffer); + sprintf (buffer, "%04d", pds->UUUU); + Print ("PDS-TDLP", "ID2-UUUU", Prt_S, buffer); + + if (pds->Oper != 0) { + Print ("PDS-TDLP", "ID3-T", Prt_DS, pds->T, + TDLP_TableLookUp (TDLP_T_Table, sizeof (TDLP_T_Table), pds->T)); + sprintf (buffer, "%02d", pds->RR); + Print ("PDS-TDLP", "ID3-RR", Prt_SS, buffer, + "Run time offset in hours"); + Print ("PDS-TDLP", "ID3-Oper", Prt_DS, pds->Oper, + TDLP_TableLookUp (TDLP_Oper_Table, sizeof (TDLP_Oper_Table), + pds->Oper)); + sprintf (buffer, "%02d", pds->HH); + Print ("PDS-TDLP", "ID3-HH", Prt_SS, buffer, + "Number of hours between variables"); + } else { + Print ("PDS-TDLP", "ID3-Oper", Prt_DS, pds->Oper, + TDLP_TableLookUp (TDLP_Oper_Table, sizeof (TDLP_Oper_Table), + pds->Oper)); + } + sprintf (buffer, "%03d", pds->ttt); + Print ("PDS-TDLP", "ID3-ttt", Prt_SS, buffer, "Forecast Projection"); + + Print ("PDS-TDLP", "ID4-thresh", Prt_F, pds->thresh); + Print ("PDS-TDLP", "ID4-I", Prt_DS, pds->I, + TDLP_TableLookUp (TDLP_I_Table, sizeof (TDLP_I_Table), pds->I)); + Print ("PDS-TDLP", "ID4-S", Prt_DS, pds->S, + TDLP_TableLookUp (TDLP_S_Table, sizeof (TDLP_S_Table), pds->S)); + Print ("PDS-TDLP", "ID4-G", Prt_D, pds->G); +} + +/***************************************************************************** + * TDLP_ElemSurfUnit() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Deal with element name, unit, and comment for TDLP items. Also deals + * with the surface information stored in ID2. + * + * ARGUMENTS + * pds = The TDLP Product Definition Section to parse. (Input) + * element = The resulting element name. (Output) + * unitName = The resulting unit name. (Output) + * comment = The resulting comment. (Output) + * shortFstLevel = The resulting short forecast level. (Output) + * longFstLevel = The resulting long forecast level. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 10/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +static void TDLP_ElemSurfUnit (pdsTDLPType * pds, char **element, + char **unitName, char **comment, + char **shortFstLevel, char **longFstLevel) +{ + char *ptr; /* Help guess unitName, and clean the elemName. */ + char *ptr2; /* Help guess unitName, and clean the elemName. */ + + myAssert (*element == NULL); + myAssert (*unitName == NULL); + myAssert (*comment == NULL); + myAssert (*shortFstLevel == NULL); + myAssert (*longFstLevel == NULL); + + *element = (char *) malloc (1 + strlen (pds->Descriptor) * sizeof (char)); + strcpy (*element, pds->Descriptor); + (*element)[strlen (pds->Descriptor)] = '\0'; + + ptr = strchr (*element, '('); + if (ptr != NULL) { + ptr2 = strchr (ptr, ')'); + *ptr2 = '\0'; + if (strcmp (ptr + 1, "unofficial id") == 0) { + *unitName = (char *) malloc ((1 + 3) * sizeof (char)); + strcpy (*unitName, "[-]"); + } else { + reallocSprintf (unitName, "[%s]", ptr + 1); + } + /* Trim any parens from element. */ + *ptr = '\0'; + strTrimRight (*element, ' '); + } else { + *unitName = (char *) malloc ((1 + 3) * sizeof (char)); + strcpy (*unitName, "[-]"); + } + ptr = *element; + while (*ptr != '\0') { + if (*ptr == ' ') { + *ptr = '-'; + } + ptr++; + } + strCompact (*element, '-'); + + reallocSprintf (comment, "%09ld-%09ld-%09ld-%ld %s", pds->ID1, + pds->ID2, pds->ID3, pds->ID4, *unitName); + reallocSprintf (shortFstLevel, "%09ld", pds->ID2); + reallocSprintf (longFstLevel, "%09ld", pds->ID2); +} + +/***************************************************************************** + * TDLP_Inventory() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Parses the TDLP "Product Definition Section" for enough information to + * fill out the inventory data structure so we can do a simple inventory on + * the file in a similar way to how we did it for GRIB1 and GRIB2. + * + * ARGUMENTS + * fp = An opened TDLP file already at the correct message. (Input) + * tdlpLen = The total length of the TDLP message. (Input) + * inv = The inventory data structure that we need to fill. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = tdlpLen is too small. + * + * HISTORY + * 10/2004 Arthur Taylor (MDL): Created. + * + * NOTES + * Speed improvements... + * 1) pds doen't need to be allocated each time. + * 2) Not all data is needed, do something like TDLP_RefTime + * 3) TDLP_ElemSurfUnit may be slow? + ***************************************************************************** + */ +int TDLP_Inventory (DataSource &fp, sInt4 tdlpLen, inventoryType *inv) +{ + sInt4 curLoc; /* Where we are in the current TDLP message. */ + int i_temp; + uChar sectLen; /* Length of section. */ + uChar *pds; /* The part of the message dealing with the PDS. */ + pdsTDLPType pdsMeta; /* The pds parsed into a usable data structure. */ + char f_gds; /* flag if there is a GDS section. */ + char f_bms; /* flag if there is a BMS section. */ + short int DSF; /* Decimal Scale Factor for unpacking the data. */ + short int BSF; /* Binary Scale Factor for unpacking the data. */ + + curLoc = 8; + if ((i_temp = fp.DataSourceFgetc()) == EOF) { + errSprintf ("Ran out of file in PDS (TDLP_Inventory).\n"); + return -1; + } + sectLen = (uChar) i_temp; + curLoc += sectLen; + if (curLoc > tdlpLen) { + errSprintf ("Ran out of data in PDS (TDLP_Inventory)\n"); + return -1; + } + pds = (uChar *) malloc (sectLen * sizeof (uChar)); + *pds = sectLen; + if (fp.DataSourceFread (pds + 1, sizeof (char), sectLen - 1) + 1 != sectLen) { + errSprintf ("Ran out of file.\n"); + free (pds); + return -1; + } + + if (ReadTDLPSect1 (pds, tdlpLen, &curLoc, &pdsMeta, &f_gds, &f_bms, + &DSF, &BSF) != 0) { + preErrSprintf ("Inside TDLP_Inventory\n"); + free (pds); + return -1; + } + free (pds); + + inv->element = NULL; + inv->unitName = NULL; + inv->comment = NULL; + free (inv->shortFstLevel); + inv->shortFstLevel = NULL; + free (inv->longFstLevel); + inv->longFstLevel = NULL; + TDLP_ElemSurfUnit (&pdsMeta, &(inv->element), &(inv->unitName), + &(inv->comment), &(inv->shortFstLevel), + &(inv->longFstLevel)); + + inv->refTime = pdsMeta.refTime; + inv->validTime = pdsMeta.refTime + pdsMeta.project; + inv->foreSec = pdsMeta.project; + return 0; +} + +/***************************************************************************** + * TDLP_RefTime() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Return just the reference time of a TDLP message. + * + * ARGUMENTS + * fp = An opened TDLP file already at the correct message. (Input) + * tdlpLen = The total length of the TDLP message. (Input) + * refTime = The reference time of interest. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = tdlpLen is too small. + * + * HISTORY + * 10/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +int TDLP_RefTime (DataSource &fp, sInt4 tdlpLen, double *refTime) +{ + int sectLen; /* Length of section. */ + sInt4 curLoc; /* Where we are in the current TDLP message. */ + int c_temp; /* Temporary variable for use with fgetc */ + short int si_temp; /* Temporary variable. */ + int year, t_year; /* The reference year, and a consistency test */ + uChar month, t_month; /* The reference month, and a consistency test */ + uChar day, t_day; /* The reference day, and a consistency test */ + uChar hour, t_hour; /* The reference hour, and a consistency test */ + uChar min; /* The reference minute */ + sInt4 li_temp; /* Temporary variable. */ + + if ((sectLen = fp.DataSourceFgetc ()) == EOF) + goto error; + curLoc = 8 + sectLen; + if (curLoc > tdlpLen) { + errSprintf ("Ran out of data in PDS (TDLP_RefTime)\n"); + return -1; + } + myAssert (sectLen <= 71); + if (sectLen < 39) { + errSprintf ("TDLP Section 1 is too small.\n"); + return -1; + } + + if ((c_temp = fp.DataSourceFgetc()) == EOF) + goto error; + if (FREAD_BIG (&si_temp, sizeof (short int), 1, fp) != 1) + goto error; + year = si_temp; + if ((c_temp = fp.DataSourceFgetc()) == EOF) + goto error; + month = c_temp; + if ((c_temp = fp.DataSourceFgetc()) == EOF) + goto error; + day = c_temp; + if ((c_temp = fp.DataSourceFgetc()) == EOF) + goto error; + hour = c_temp; + if ((c_temp = fp.DataSourceFgetc()) == EOF) + goto error; + min = c_temp; + + if (FREAD_BIG (&li_temp, sizeof (sInt4), 1, fp) != 1) + goto error; + t_year = li_temp / 1000000L; + li_temp -= t_year * 1000000L; + t_month = li_temp / 10000L; + li_temp -= t_month * 10000L; + t_day = li_temp / 100; + t_hour = li_temp - t_day * 100; + + if ((t_year != year) || (t_month != month) || (t_day != day) || + (t_hour != hour)) { + errSprintf ("Error Inconsistent Times in TDLP_RefTime.\n"); + return -1; + } + if (ParseTime (refTime, year, month, day, hour, min, 0) != 0) { + preErrSprintf ("Error In call to ParseTime in TDLP_RefTime.\n"); + return -1; + } + + /* Get to the end of the TDLP message. */ + /* (inventory.c : GRIB2Inventory), is responsible for this. */ + /* fseek (fp, gribLen - sectLen, SEEK_CUR); */ + return 0; + error: + errSprintf ("Ran out of file in PDS (TDLP_RefTime).\n"); + return -1; +} + +/***************************************************************************** + * ReadTDLPSect2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Parses the TDLP "Grid Definition Section" or section 2, filling out + * the gdsMeta data structure. + * + * ARGUMENTS + * gds = The compressed part of the message dealing with "GDS". (Input) + * tdlpLen = The total length of the TDLP message. (Input) + * curLoc = Current location in the TDLP message. (Output) + * gdsMeta = The filled out gdsMeta data structure. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = tdlpLen is too small. + * -2 = unexpected values in gds. + * + * HISTORY + * 10/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +static int ReadTDLPSect2 (uChar *gds, sInt4 tdlpLen, sInt4 *curLoc, + gdsType *gdsMeta) +{ + char sectLen; /* Length of section. */ + int gridType; /* Which type of grid. (see enumerated types). */ + sInt4 li_temp; /* Temporary variable. */ + + sectLen = *(gds++); + *curLoc += sectLen; + if (*curLoc > tdlpLen) { + errSprintf ("Ran out of data in GDS (TDLP Section 2)\n"); + return -1; + } + myAssert (sectLen == 28); + + gridType = *(gds++); + gdsMeta->Nx = GRIB_UNSIGN_INT2 (*gds, gds[1]); + gds += 2; + gdsMeta->Ny = GRIB_UNSIGN_INT2 (*gds, gds[1]); + gds += 2; + gdsMeta->lat1 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) / 10000.0; + gds += 3; + gdsMeta->lon1 = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) / 10000.0; + gdsMeta->lon1 = 360 - gdsMeta->lon1; + if (gdsMeta->lon1 < 0) { + gdsMeta->lon1 += 360; + } + if (gdsMeta->lon1 > 360) { + gdsMeta->lon1 -= 360; + } + gds += 3; + gdsMeta->orientLon = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) / 10000.0; + gdsMeta->orientLon = 360 - gdsMeta->orientLon; + if (gdsMeta->orientLon < 0) { + gdsMeta->orientLon += 360; + } + if (gdsMeta->orientLon > 360) { + gdsMeta->orientLon -= 360; + } + gds += 3; + MEMCPY_BIG (&li_temp, gds, sizeof (sInt4)); + gdsMeta->Dx = li_temp / 1000.0; + gds += 4; + gdsMeta->meshLat = GRIB_SIGN_INT3 (*gds, gds[1], gds[2]) / 10000.0; + gds += 3; + if ((*gds != 0) || (gds[1] != 0) || (gds[2] != 0) || (gds[3] != 0) || + (gds[4] != 0) || (gds[5] != 0)) { + errSprintf ("Error Reserved was not set to 0 in ReadTDLPSect2.\n"); + return -1; + } + + gdsMeta->numPts = gdsMeta->Nx * gdsMeta->Ny; + gdsMeta->f_sphere = 1; + gdsMeta->majEarth = 6371.2; + gdsMeta->minEarth = 6371.2; + gdsMeta->Dy = gdsMeta->Dx; + gdsMeta->resFlag = 0; + gdsMeta->center = 0; + gdsMeta->scan = 64; + gdsMeta->lat2 = 0; + gdsMeta->lon2 = 0; + gdsMeta->scaleLat1 = gdsMeta->meshLat; + gdsMeta->scaleLat2 = gdsMeta->meshLat; + gdsMeta->southLat = 0; + gdsMeta->southLon = 0; + switch (gridType) { + case TDLP_POLAR: + gdsMeta->projType = GS3_POLAR; + /* 4/24/2006 Added the following. */ + gdsMeta->scaleLat1 = 90; + gdsMeta->scaleLat2 = 90; + break; + case TDLP_LAMBERT: + gdsMeta->projType = GS3_LAMBERT; + break; + case TDLP_MERCATOR: + gdsMeta->projType = GS3_MERCATOR; + /* 4/24/2006 Added the following. */ + gdsMeta->scaleLat1 = 0; + gdsMeta->scaleLat2 = 0; + break; + default: + errSprintf ("Grid projection number is %d\n", gridType); + return -2; + } + return 0; +} + +/***************************************************************************** + * ReadTDLPSect3() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Parses the TDLP "Bit Map Section" or section 3, filling out the bitmap + * as needed. + * + * ARGUMENTS + * bms = The compressed part of the message dealing with "BMS". (Input) + * tdlpLen = The total length of the TDLP message. (Input) + * curLoc = Current location in the TDLP message. (Output) + * bitmap = The extracted bitmap. (Output) + * NxNy = The total size of the grid. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = tdlpLen is too small. + * -2 = unexpected values in bms. + * + * HISTORY + * 10/2004 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +static int ReadTDLPSect3 (CPL_UNUSED uChar *bms, + CPL_UNUSED sInt4 tdlpLen, + CPL_UNUSED sInt4 *curLoc, + CPL_UNUSED uChar *bitmap, + CPL_UNUSED sInt4 NxNy) +{ + errSprintf ("Bitmap data is Not Supported\n"); + return -1; +} + +/***************************************************************************** + * ReadTDLPSect4() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Unpacks the "Binary Data Section" or section 4. + * + * ARGUMENTS + * bds = The compressed part of the message dealing with "BDS". (Input) + * tdlpLen = The total length of the TDLP message. (Input) + * curLoc = Current location in the TDLP message. (Output) + * DSF = Decimal Scale Factor for unpacking the data. (Input) + * BSF = Binary Scale Factor for unpacking the data. (Input) + * data = The extracted grid. (Output) + * meta = The meta data associated with the grid (Input/Output) + * unitM = The M unit conversion value in equation y = Mx + B. (Input) + * unitB = The B unit conversion value in equation y = Mx + B. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = gribLen is too small. + * -2 = unexpected values in bds. + * + * HISTORY + * 4/2003 Arthur Taylor (MDL/RSIS): Created + * 3/2004 AAT: Switched {# Pts * (# Bits in a Group) + + * # of unused bits != # of available bits} to a warning from an + * error. + * 2/2005 AAT: Found bug: memBitRead grp[i].bit was sizeof sInt4 instead + * of uChar. + * 2/2005 AAT: Second order diff, no miss value bug (lastData - 1) should + * be lastData. + * 2/2005 AAT: Added test to see if the number of bits needed matches the + * section length. + * + * NOTES + * 1) See metaparse.c : ParseGrid() + ***************************************************************************** + */ +static int ReadTDLPSect4 (uChar *bds, sInt4 tdlpLen, sInt4 *curLoc, + short int DSF, short int BSF, double *data, + grib_MetaData *meta, + CPL_UNUSED double unitM, + CPL_UNUSED double unitB) +{ + uInt4 sectLen; /* Length in bytes of the current section. */ + uChar f_notGridPnt; /* Not Grid point data? */ + uChar f_complexPack; /* Complex packing? */ + uChar f_sndOrder; /* Second order differencing? */ + uChar f_primMiss; /* Primary missing value? */ + uChar f_secMiss; /* Secondary missing value? */ + uInt4 numPack; /* Number of points packed. */ + sInt4 li_temp; /* Temporary variable. */ + uInt4 uli_temp; /* Temporary variable. */ + uChar bufLoc; /* Keeps track of where to start getting more data + * out of the packed data stream. */ + uChar f_negative; /* used to help with signs of numbers. */ + size_t numUsed; /* How many bytes were used in a given call to + * memBitRead. */ + sInt4 origVal = 0; /* Original value. */ + uChar mbit; /* # of bits for abs (first first order difference) */ + sInt4 fstDiff = 0; /* First first order difference. */ + sInt4 diff = 0; /* general first order difference. */ + uChar nbit; /* # of bits for abs (overall min value) */ + sInt4 minVal; /* Minimum value. */ + size_t LX; /* Number of groups. */ + uChar ibit; /* # of bits for group min values. */ + uChar jbit; /* # of bits for # of bits for group. */ + uChar kbit; /* # of bits for # values in a group. */ + TDLGroupType *grp; /* Holds the info about each group. */ + size_t i, j; /* Loop counters. */ + uInt4 t_numPack; /* Used to total number of values in a group to check + * the numPack value. */ + uInt4 t_numBits; /* Used to total number of bits used in the groups. */ + uInt4 t_numBytes; /* Used to total number of bytes used to compare to + * sectLen. */ + sInt4 maxVal; /* The max value in a group. */ + uInt4 dataCnt; /* How many values (miss or othewise) we have read. */ + uInt4 lastData; /* Index to last actual data. */ + uInt4 numVal; /* # of actual (non-missing values) we have. */ + double scale; /* Amount to scale values by. */ + uInt4 dataInd; /* Index into data for this value (used to switch + * from a11..a1n,a2n..a21 to normal grid of + * a11..a1n,a21..a2n. */ + uChar f_missing; /* Used to help with primary missing values, and the + * 0 bit possibility. */ +#ifdef DEBUG + sInt4 t_UK1 = 0; /* Used to test theories about un defined values. */ + sInt4 t_UK2 = 0; /* Used to test theories about un defined values. */ +#endif + + sectLen = GRIB_UNSIGN_INT3 (*bds, bds[1], bds[2]); + *curLoc += sectLen; + if (*curLoc > tdlpLen) { + errSprintf ("Ran out of data in BDS (TDLP Section 4)\n"); + return -1; + } + bds += 3; + t_numBytes = 3; + f_notGridPnt = (GRIB2BIT_4 & *bds) ? 1 : 0; + f_complexPack = (GRIB2BIT_5 & *bds) ? 1 : 0; + f_sndOrder = (GRIB2BIT_6 & *bds) ? 1 : 0; + f_primMiss = (GRIB2BIT_7 & *bds) ? 1 : 0; + f_secMiss = (GRIB2BIT_8 & *bds) ? 1 : 0; + + if (f_secMiss && (!f_primMiss)) { + errSprintf ("Secondary missing value without a primary!\n"); + return -1; + } + if (f_complexPack) { + if (!f_sndOrder) { + meta->gridAttrib.packType = GS5_CMPLX; + } else { + meta->gridAttrib.packType = GS5_CMPLXSEC; + } + } else { + errSprintf ("Simple pack is not supported at this time.\n"); + return -1; + } + bds++; + MEMCPY_BIG (&numPack, bds, sizeof (sInt4)); + bds += 4; + t_numBytes += 5; + if (!f_notGridPnt) { + if (numPack != meta->gds.numPts) { + errSprintf ("Number packed %d != number of points %d\n", numPack, + meta->gds.numPts); + return -1; + } + } + meta->gridAttrib.DSF = DSF; + meta->gridAttrib.ESF = BSF; + meta->gridAttrib.fieldType = 0; + if (!f_primMiss) { + meta->gridAttrib.f_miss = 0; + meta->gridAttrib.missPri = 0; + meta->gridAttrib.missSec = 0; + } else { + MEMCPY_BIG (&li_temp, bds, sizeof (sInt4)); + bds += 4; + t_numBytes += 4; + meta->gridAttrib.missPri = li_temp / 10000.0; + if (!f_secMiss) { + meta->gridAttrib.f_miss = 1; + meta->gridAttrib.missSec = 0; + } else { + MEMCPY_BIG (&li_temp, bds, sizeof (sInt4)); + bds += 4; + t_numBytes += 4; + meta->gridAttrib.missSec = li_temp / 10000.0; + meta->gridAttrib.f_miss = 2; + } + } + /* Init the buffer location. */ + bufLoc = 8; + /* The origValue and fstDiff are only present if sndOrder packed. */ + if (f_sndOrder) { + memBitRead (&f_negative, sizeof (f_negative), bds, 1, &bufLoc, + &numUsed); + memBitRead (&uli_temp, sizeof (sInt4), bds, 31, &bufLoc, &numUsed); + myAssert (numUsed == 4); + bds += numUsed; + t_numBytes += numUsed; + origVal = (f_negative) ? -1 * uli_temp : uli_temp; + memBitRead (&mbit, sizeof (mbit), bds, 5, &bufLoc, &numUsed); + memBitRead (&f_negative, sizeof (f_negative), bds, 1, &bufLoc, + &numUsed); + myAssert (numUsed == 0); + myAssert ((mbit > 0) && (mbit < 32)); + memBitRead (&uli_temp, sizeof (sInt4), bds, mbit, &bufLoc, &numUsed); + bds += numUsed; + t_numBytes += numUsed; + fstDiff = (f_negative) ? -1 * uli_temp : uli_temp; + } + memBitRead (&nbit, sizeof (nbit), bds, 5, &bufLoc, &numUsed); + bds += numUsed; + t_numBytes += numUsed; + memBitRead (&f_negative, sizeof (f_negative), bds, 1, &bufLoc, &numUsed); + bds += numUsed; + t_numBytes += numUsed; + myAssert ((nbit > 0) && (nbit < 32)); + memBitRead (&uli_temp, sizeof (sInt4), bds, nbit, &bufLoc, &numUsed); + bds += numUsed; + t_numBytes += numUsed; + minVal = (f_negative) ? -1 * uli_temp : uli_temp; + memBitRead (&LX, sizeof (LX), bds, 16, &bufLoc, &numUsed); + bds += numUsed; + t_numBytes += numUsed; + grp = (TDLGroupType *) malloc (LX * sizeof (TDLGroupType)); + memBitRead (&ibit, sizeof (ibit), bds, 5, &bufLoc, &numUsed); + bds += numUsed; + t_numBytes += numUsed; + memBitRead (&jbit, sizeof (jbit), bds, 5, &bufLoc, &numUsed); + /* Following assert is because it is the # of bits of # of bits. Which + * means that # of bits of value that has a max of 64. */ + myAssert (jbit < 6); + bds += numUsed; + t_numBytes += numUsed; + memBitRead (&kbit, sizeof (kbit), bds, 5, &bufLoc, &numUsed); + bds += numUsed; + t_numBytes += numUsed; + myAssert (ibit < 33); + for (i = 0; i < LX; i++) { + if (ibit == 0) { + grp[i].min = 0; + } else { + memBitRead (&(grp[i].min), sizeof (sInt4), bds, ibit, &bufLoc, + &numUsed); + bds += numUsed; + t_numBytes += numUsed; + } + } + myAssert (jbit < 8); + for (i = 0; i < LX; i++) { + if (jbit == 0) { + grp[i].bit = 0; + } else { + myAssert (jbit <= sizeof (uChar) * 8); + memBitRead (&(grp[i].bit), sizeof (uChar), bds, jbit, &bufLoc, + &numUsed); + bds += numUsed; + t_numBytes += numUsed; + } + myAssert (grp[i].bit < 32); + } + myAssert (kbit < 33); + t_numPack = 0; + t_numBits = 0; + for (i = 0; i < LX; i++) { + if (kbit == 0) { + grp[i].num = 0; + } else { + memBitRead (&(grp[i].num), sizeof (sInt4), bds, kbit, &bufLoc, + &numUsed); + bds += numUsed; + t_numBytes += numUsed; + } + t_numPack += grp[i].num; + t_numBits += grp[i].num * grp[i].bit; + } + if (t_numPack != numPack) { + errSprintf ("Number packed %d != number of values in groups %d\n", + numPack, t_numPack); + free (grp); + return -1; + } + if ((t_numBytes + ceil (t_numBits / 8.)) > sectLen) { + errSprintf ("# bytes in groups %ld (%ld + %ld / 8) > sectLen %ld\n", + (sInt4) (t_numBytes + ceil (t_numBits / 8.)), + t_numBytes, t_numBits, sectLen); + free (grp); + return -1; + } + dataCnt = 0; + dataInd = 0; + +#ifdef DEBUG + printf ("nbit %d, ibit %d, jbit %d, kbit %d\n", nbit, ibit, jbit, kbit); + if ((t_numBytes + ceil (t_numBits / 8.)) != sectLen) { + printf ("Caution: # bytes in groups %d (%d + %d / 8) != " + "sectLen %d\n", (sInt4) (t_numBytes + ceil (t_numBits / 8.)), + t_numBytes, t_numBits, sectLen); + } +#endif + + /* Binary scale factor in TDLP has reverse sign from GRIB definition. */ + scale = pow (10.0, -1 * DSF) * pow (2.0, -1 * BSF); + + meta->gridAttrib.f_maxmin = 0; + /* Work with Second order complex packed data. */ + if (f_sndOrder) { + /* *INDENT-OFF* */ + /* The algorithm appears to be: + * Data: a1 a2 a3 a4 a5 ... + * 1st diff: 0 b2 b3 b4 b5 ... + * 2nd diff: UK1 UK2 c3 c4 c5 ... + * We already know a1 and b2, and unpack a stream of UK1 UK2 c3 c4 + * The problem is that UK1, UK2 is undefined. Originally I thought + * this was 0, or c3, but it appears that if b2 != 0, then + * UK2 = c3 + 2 b2, and UK1 = c3 + 1 * b2, otherwise it appears that + * UK1 == UK2, and typically UK1 == c3 (but not always). */ + /* *INDENT-ON* */ + myAssert (numPack >= 2); + if (f_secMiss) { + numVal = 0; + lastData = 0; + for (i = 0; i < LX; i++) { + maxVal = (1 << grp[i].bit) - 1; + for (j = 0; j < grp[i].num; j++) { + /* signed int. */ + memBitRead (&(li_temp), sizeof (sInt4), bds, grp[i].bit, + &bufLoc, &numUsed); + bds += numUsed; + if (li_temp == maxVal) { + data[dataInd] = meta->gridAttrib.missPri; + } else if (li_temp == (maxVal - 1)) { + data[dataInd] = meta->gridAttrib.missSec; + } else { + if (numVal > 1) { +#ifdef DEBUG + if (numVal == 2) { + if (fstDiff != 0) { +/* + myAssert (t_UK1 == li_temp + fstDiff); + myAssert (t_UK2 == li_temp + 2 * fstDiff); +*/ + } else { + myAssert (t_UK1 == t_UK2); + } + } +#endif + diff += (li_temp + grp[i].min + minVal); + data[dataInd] = data[lastData] + diff * scale; + lastData = dataInd; + } else if (numVal == 1) { + data[dataInd] = (origVal + fstDiff) * scale; + lastData = dataInd; + diff = fstDiff; +#ifdef DEBUG + t_UK2 = li_temp; +#endif + } else { + data[dataInd] = origVal * scale; +#ifdef DEBUG + t_UK1 = li_temp; +#endif + } + numVal++; + if (!meta->gridAttrib.f_maxmin) { + meta->gridAttrib.min = data[dataInd]; + meta->gridAttrib.max = data[dataInd]; + meta->gridAttrib.f_maxmin = 1; + } else { + if (data[dataInd] < meta->gridAttrib.min) { + meta->gridAttrib.min = data[dataInd]; + } + if (data[dataInd] > meta->gridAttrib.max) { + meta->gridAttrib.max = data[dataInd]; + } + } + } + dataCnt++; + dataInd = ((dataCnt / meta->gds.Nx) % 2) ? + (2 * (dataCnt / meta->gds.Nx) + 1) * + meta->gds.Nx - dataCnt - 1 : dataCnt; + myAssert ((dataInd < numPack) || (dataCnt == numPack)); + } + } + } else if (f_primMiss) { + numVal = 0; + lastData = 0; + for (i = 0; i < LX; i++) { + maxVal = (1 << grp[i].bit) - 1; + for (j = 0; j < grp[i].num; j++) { + /* signed int. */ + memBitRead (&(li_temp), sizeof (sInt4), bds, grp[i].bit, + &bufLoc, &numUsed); + bds += numUsed; + f_missing = 0; + if (li_temp == maxVal) { + data[dataInd] = meta->gridAttrib.missPri; + /* In the case of grp[i].bit == 0, if grp[i].min == 0, then + * it is the missing value, otherwise regular value. Only + * need to be concerned for primary missing values. */ + f_missing = 1; + if ((grp[i].bit == 0) && (grp[i].min != 0)) { +#ifdef DEBUG + printf ("This doesn't happen often.\n"); + printf ("%d %d %d\n", (int) i, grp[i].bit, grp[i].min); +#endif + myAssert (1 == 2); + f_missing = 0; + } + } + if (!f_missing) { + if (numVal > 1) { +#ifdef DEBUG + if (numVal == 2) { + if (fstDiff != 0) { +/* + myAssert (t_UK1 == li_temp + fstDiff); + myAssert (t_UK2 == li_temp + 2 * fstDiff); +*/ + } else { + myAssert (t_UK1 == t_UK2); + } + } +#endif + diff += (li_temp + grp[i].min + minVal); + data[dataInd] = data[lastData] + diff * scale; + lastData = dataInd; + } else if (numVal == 1) { + data[dataInd] = (origVal + fstDiff) * scale; + lastData = dataInd; + diff = fstDiff; +#ifdef DEBUG + t_UK2 = li_temp; +#endif + } else { + data[dataInd] = origVal * scale; +#ifdef DEBUG + t_UK1 = li_temp; +#endif + } + numVal++; + if (!meta->gridAttrib.f_maxmin) { + meta->gridAttrib.min = data[dataInd]; + meta->gridAttrib.max = data[dataInd]; + meta->gridAttrib.f_maxmin = 1; + } else { + if (data[dataInd] < meta->gridAttrib.min) { + meta->gridAttrib.min = data[dataInd]; + } + if (data[dataInd] > meta->gridAttrib.max) { + meta->gridAttrib.max = data[dataInd]; + } + } + } + dataCnt++; + dataInd = ((dataCnt / meta->gds.Nx) % 2) ? + (2 * (dataCnt / meta->gds.Nx) + 1) * + meta->gds.Nx - dataCnt - 1 : dataCnt; + myAssert ((dataInd < numPack) || (dataCnt == numPack)); + } + } + } else { + lastData = 0; + for (i = 0; i < LX; i++) { + for (j = 0; j < grp[i].num; j++) { + /* signed int. */ + memBitRead (&(li_temp), sizeof (sInt4), bds, grp[i].bit, + &bufLoc, &numUsed); + bds += numUsed; + if (dataCnt > 1) { +#ifdef DEBUG + if (dataCnt == 2) { + if (fstDiff != 0) { +/* + myAssert (t_UK1 == li_temp + fstDiff); + myAssert (t_UK2 == li_temp + 2 * fstDiff); +*/ + } else { + myAssert (t_UK1 == t_UK2); + } + } +#endif + diff += (li_temp + grp[i].min + minVal); + data[dataInd] = data[lastData] + diff * scale; + lastData = dataInd; + } else if (dataCnt == 1) { + data[dataInd] = (origVal + fstDiff) * scale; + lastData = dataInd; + diff = fstDiff; +#ifdef DEBUG + t_UK2 = li_temp; +#endif + } else { + data[dataInd] = origVal * scale; +#ifdef DEBUG + t_UK1 = li_temp; +#endif + } +#ifdef DEBUG +/* + if (i >= 4153) { +*/ +/* + if ((data[dataInd] > 100) || (data[dataInd] < -100)) { +*/ +/* + if ((diff > 50) || (diff < -50)) { + printf ("li_temp :: %ld, diff = %ld\n", li_temp, diff); + printf ("data[dataInd] :: %f\n", data[dataInd]); + printf ("Group # %d element %d, grp[i].min %ld, " + "grp[i].bit %d, minVal %ld\n", i, j, grp[i].min, + grp[i].bit, minVal); + } +*/ +#endif + if (!meta->gridAttrib.f_maxmin) { + meta->gridAttrib.min = data[dataInd]; + meta->gridAttrib.max = data[dataInd]; + meta->gridAttrib.f_maxmin = 1; + } else { + if (data[dataInd] < meta->gridAttrib.min) { + meta->gridAttrib.min = data[dataInd]; + } + if (data[dataInd] > meta->gridAttrib.max) { + meta->gridAttrib.max = data[dataInd]; + } + } + dataCnt++; + dataInd = ((dataCnt / meta->gds.Nx) % 2) ? + (2 * (dataCnt / meta->gds.Nx) + 1) * + meta->gds.Nx - dataCnt - 1 : dataCnt; + myAssert ((dataInd < numPack) || (dataCnt == numPack)); + } + } + numVal = dataCnt; + } + + /* Work with regular complex packed data. */ + } else { +#ifdef DEBUG +/* + printf ("Work with regular complex packed data\n"); +*/ +#endif + if (f_secMiss) { + numVal = 0; + for (i = 0; i < LX; i++) { + maxVal = (1 << grp[i].bit) - 1; + for (j = 0; j < grp[i].num; j++) { + /* signed int. */ + memBitRead (&(li_temp), sizeof (sInt4), bds, grp[i].bit, + &bufLoc, &numUsed); + bds += numUsed; + if (li_temp == maxVal) { + data[dataInd] = meta->gridAttrib.missPri; + } else if (li_temp == (maxVal - 1)) { + data[dataInd] = meta->gridAttrib.missSec; + } else { + data[dataInd] = (li_temp + grp[i].min + minVal) * scale; + numVal++; + if (!meta->gridAttrib.f_maxmin) { + meta->gridAttrib.min = data[dataInd]; + meta->gridAttrib.max = data[dataInd]; + meta->gridAttrib.f_maxmin = 1; + } else { + if (data[dataInd] < meta->gridAttrib.min) { + meta->gridAttrib.min = data[dataInd]; + } + if (data[dataInd] > meta->gridAttrib.max) { + meta->gridAttrib.max = data[dataInd]; + } + } + } + dataCnt++; + dataInd = ((dataCnt / meta->gds.Nx) % 2) ? + (2 * (dataCnt / meta->gds.Nx) + 1) * + meta->gds.Nx - dataCnt - 1 : dataCnt; + myAssert ((dataInd < numPack) || (dataCnt == numPack)); + } + } + } else if (f_primMiss) { +#ifdef DEBUG +/* + printf ("Work with primary missing data\n"); +*/ +#endif + numVal = 0; + for (i = 0; i < LX; i++) { + maxVal = (1 << grp[i].bit) - 1; + for (j = 0; j < grp[i].num; j++) { + memBitRead (&(li_temp), sizeof (sInt4), bds, grp[i].bit, + &bufLoc, &numUsed); + bds += numUsed; + f_missing = 0; + if (li_temp == maxVal) { + data[dataInd] = meta->gridAttrib.missPri; + /* In the case of grp[i].bit == 0, if grp[i].min == 0, then + * it is the missing value, otherwise regular value. Only + * need to be concerned for primary missing values. */ + f_missing = 1; + if ((grp[i].bit == 0) && (grp[i].min != 0)) { +#ifdef DEBUG + printf ("This doesn't happen often.\n"); + printf ("%d %d %d\n", (int) i, grp[i].bit, grp[i].min); + myAssert (1 == 2); +#endif + f_missing = 0; + } + } + if (!f_missing) { + data[dataInd] = (li_temp + grp[i].min + minVal) * scale; + numVal++; + if (!meta->gridAttrib.f_maxmin) { + meta->gridAttrib.min = data[dataInd]; + meta->gridAttrib.max = data[dataInd]; + meta->gridAttrib.f_maxmin = 1; + } else { + if (data[dataInd] < meta->gridAttrib.min) { + meta->gridAttrib.min = data[dataInd]; + } + if (data[dataInd] > meta->gridAttrib.max) { + meta->gridAttrib.max = data[dataInd]; + } + } + } + dataCnt++; + dataInd = ((dataCnt / meta->gds.Nx) % 2) ? + (2 * (dataCnt / meta->gds.Nx) + 1) * + meta->gds.Nx - dataCnt - 1 : dataCnt; + myAssert ((dataInd < numPack) || (dataCnt == numPack)); + } + } + } else { + for (i = 0; i < LX; i++) { + for (j = 0; j < grp[i].num; j++) { + memBitRead (&(li_temp), sizeof (sInt4), bds, grp[i].bit, + &bufLoc, &numUsed); + bds += numUsed; + data[dataInd] = (li_temp + grp[i].min + minVal) * scale; + if (!meta->gridAttrib.f_maxmin) { + meta->gridAttrib.min = data[dataInd]; + meta->gridAttrib.max = data[dataInd]; + meta->gridAttrib.f_maxmin = 1; + } else { + if (data[dataInd] < meta->gridAttrib.min) { + meta->gridAttrib.min = data[dataInd]; + } + if (data[dataInd] > meta->gridAttrib.max) { + meta->gridAttrib.max = data[dataInd]; + } + } + dataCnt++; + dataInd = ((dataCnt / meta->gds.Nx) % 2) ? + (2 * (dataCnt / meta->gds.Nx) + 1) * + meta->gds.Nx - dataCnt - 1 : dataCnt; + myAssert ((dataInd < numPack) || (dataCnt == numPack)); + } + } + numVal = dataCnt; + } + } + meta->gridAttrib.numMiss = dataCnt - numVal; + meta->gridAttrib.refVal = minVal * scale; + + free (grp); + return 0; +} + +/***************************************************************************** + * ReadTDLPRecord() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Reads in a TDLP message, and parses the data into various data + * structures, for use with other code. + * + * ARGUMENTS + * fp = An opened TDLP file already at the correct message. (Input) + * TDLP_Data = The read in TDLP data. (Output) + * tdlp_DataLen = Size of TDLP_Data. (Output) + * meta = A filled in meta structure (Output) + * IS = The structure containing all the arrays that the + * unpacker uses (Output) + * sect0 = Already read in section 0 data. (Input) + * tdlpLen = Length of the TDLP message. (Input) + * majEarth = Used to override the TDLP major axis of earth. (Input) + * minEarth = Used to override the TDLP minor axis of earth. (Input) + * + * FILES/DATABASES: + * An already opened file pointing to the desired TDLP message. + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = Problems reading in the PDS. + * -2 = Problems reading in the GDS. + * -3 = Problems reading in the BMS. + * -4 = Problems reading in the BDS. + * -5 = Problems reading the closing section. + * + * HISTORY + * 10/2004 Arthur Taylor (MDL): Created + * + * NOTES + ***************************************************************************** + */ +int ReadTDLPRecord (DataSource &fp, double **TDLP_Data, uInt4 *tdlp_DataLen, + grib_MetaData *meta, IS_dataType *IS, + sInt4 sect0[SECT0LEN_WORD], uInt4 tdlpLen, + double majEarth, double minEarth) +{ + sInt4 nd5; /* Size of TDLP message rounded up to the nearest * + * sInt4. */ + uChar *c_ipack; /* A char ptr to the message stored in IS->ipack */ + sInt4 curLoc; /* Current location in the GRIB message. */ + char f_gds; /* flag if there is a GDS section. */ + char f_bms; /* flag if there is a BMS section. */ + short int DSF; /* Decimal Scale Factor for unpacking the data. */ + short int BSF; /* Binary Scale Factor for unpacking the data. */ + double *tdlp_Data; /* A pointer to TDLP_Data for ease of manipulation. */ + double unitM = 1; /* M in y = Mx + B, for unit conversion. */ + double unitB = 0; /* B in y = Mx + B, for unit conversion. */ + sInt4 li_temp; /* Used to make sure section 5 is 7777. */ + size_t pad; /* Number of bytes to pad the message to get to the + * correct byte boundary. */ + char buffer[24]; /* Read the trailing bytes in the TDLPack record. */ + uChar *bitmap; /* Would contain bitmap data if it was supported. */ + + /* Make room for entire message, and read it in. */ + /* nd5 needs to be tdlpLen in (sInt4) units rounded up. */ + nd5 = (tdlpLen + 3) / 4; + if (nd5 > IS->ipackLen) { + IS->ipackLen = nd5; + IS->ipack = (sInt4 *) realloc ((void *) (IS->ipack), + (IS->ipackLen) * sizeof (sInt4)); + } + c_ipack = (uChar *) IS->ipack; + /* Init last sInt4 to 0, to make sure that the padded bytes are 0. */ + IS->ipack[nd5 - 1] = 0; + /* Init first 2 sInt4 to sect0. */ + memcpy (c_ipack, sect0, SECT0LEN_WORD * 2); + /* Read in the rest of the message. */ + if (fp.DataSourceFread(c_ipack + SECT0LEN_WORD * 2, sizeof (char), + (tdlpLen - SECT0LEN_WORD * 2)) + SECT0LEN_WORD * 2 != tdlpLen) { + errSprintf ("Ran out of file\n"); + return -1; + } + + /* Preceeding was in degrib2, next part is specific to TDLP. */ + curLoc = 8; + if (ReadTDLPSect1 (c_ipack + curLoc, tdlpLen, &curLoc, &(meta->pdsTdlp), + &f_gds, &f_bms, &DSF, &BSF) != 0) { + preErrSprintf ("Inside ReadGrib1Record\n"); + return -1; + } + + /* Figure out some basic stuff about the grid. */ + free (meta->element); + meta->element = NULL; + free (meta->unitName); + meta->unitName = NULL; + free (meta->comment); + meta->comment = NULL; + free (meta->shortFstLevel); + meta->shortFstLevel = NULL; + free (meta->longFstLevel); + meta->longFstLevel = NULL; + TDLP_ElemSurfUnit (&(meta->pdsTdlp), &(meta->element), &(meta->unitName), + &(meta->comment), &(meta->shortFstLevel), + &(meta->longFstLevel)); + meta->center = 7; /* US NWS, NCEP */ + meta->subcenter = 14; /* NWS Meteorological Development Laboratory */ + +/* strftime (meta->refTime, 20, "%Y%m%d%H%M", + gmtime (&(meta->pdsTdlp.refTime))); +*/ + Clock_Print (meta->refTime, 20, meta->pdsTdlp.refTime, "%Y%m%d%H%M", 0); + +/* + validTime = meta->pdsTdlp.refTime + meta->pdsTdlp.project; + strftime (meta->validTime, 20, "%Y%m%d%H%M", gmtime (&(validTime))); +*/ + Clock_Print (meta->validTime, 20, meta->pdsTdlp.refTime + + meta->pdsTdlp.project, "%Y%m%d%H%M", 0); + + meta->deltTime = meta->pdsTdlp.project; + + /* Get the Grid Definition Section. */ + if (f_gds) { + if (ReadTDLPSect2 (c_ipack + curLoc, tdlpLen, &curLoc, + &(meta->gds)) != 0) { + preErrSprintf ("Inside ReadGrib1Record\n"); + return -2; + } + } else { + errSprintf ("Don't know how to handle vector data yet.\n"); + return -2; + } + + /* Allow over ride of the earth radii. */ + if ((majEarth > 6300) && (majEarth < 6400)) { + if ((minEarth > 6300) && (minEarth < 6400)) { + meta->gds.f_sphere = 0; + meta->gds.majEarth = majEarth; + meta->gds.minEarth = minEarth; + if (majEarth == minEarth) { + meta->gds.f_sphere = 1; + } + } else { + meta->gds.f_sphere = 1; + meta->gds.majEarth = majEarth; + meta->gds.minEarth = majEarth; + } + } + + /* Allocate memory for the grid. */ + if (meta->gds.numPts > *tdlp_DataLen) { + *tdlp_DataLen = meta->gds.numPts; + *TDLP_Data = (double *) realloc ((void *) (*TDLP_Data), + (*tdlp_DataLen) * sizeof (double)); + } + tdlp_Data = *TDLP_Data; + + /* Get the Bit Map Section. */ + if (f_bms) { +/* errSprintf ("Bitmap data is Not Supported\n");*/ + /* Need to allocate bitmap when this is implemented. */ + bitmap = NULL; + ReadTDLPSect3 (c_ipack + curLoc, tdlpLen, &curLoc, bitmap, + meta->gds.numPts); + return -1; + } + + /* Read the GRID. */ + if (ReadTDLPSect4 (c_ipack + curLoc, tdlpLen, &curLoc, DSF, BSF, + tdlp_Data, meta, unitM, unitB) != 0) { + preErrSprintf ("Inside ReadTDLPRecord\n"); + return -4; + } + + /* Read section 5. If it is "7777" == 926365495 we are done. */ + memcpy (&li_temp, c_ipack + curLoc, 4); + if (li_temp != 926365495L) { + errSprintf ("Did not find the end of the message.\n"); + return -5; + } + curLoc += 4; + /* Read the trailing part of the message. */ + /* TDLPack uses 4 bytes for FORTRAN record size, then another 8 bytes for + * the size of the record (so FORTRAN can see it), then the data rounded + * up to an 8 byte boundary, then a trailing 4 bytes for a final FORTRAN + * record size. However it only stores in the gribLen the non-rounded + * amount, so we need to take care of the rounding, and the trailing 4 + * bytes here. */ + pad = ((sInt4) (ceil (tdlpLen / 8.0))) * 8 - tdlpLen + 4; + if (fp.DataSourceFread(buffer, sizeof (char), pad) != pad) { + errSprintf ("Ran out of file\n"); + return -1; + } + return 0; +} + +/***************************************************************************** + * TDL_ScaleData() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Deal with scaling while excluding the primary and secondary missing + * values. After this, dst should contain scaled data + primary or secondary + * missing values + * "tdlpack library"::pack2d.f line 257 or search for: + "the above statement" + * + * ARGUMENTS + * Src = The original data. (Input) + * Dst = The scaled data. (Output) + * numData = The number of elements in data. (Input) + * DSF = Decimal Scale Factor for scaling the data. (Input) + * BSF = Binary Scale Factor for scaling the data. (Input) + * f_primMiss = Flag saying if we have a primary missing value (In/Out) + * primMiss = primary missing value. (In/Out) + * f_secMiss = Flag saying if we have a secondary missing value (In/Out) + * secMiss = secondary missing value. (In/Out) + * f_min = Flag saying if we have the minimum value. (Output) + * min = minimum scaled value in the grid. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Updated from "group.c" in "C" tdlpack code. + * + * NOTES + ***************************************************************************** + */ +#define SCALE_MISSING 10000L +static void TDL_ScaleData (double *Src, sInt4 *Dst, sInt4 numData, + int DSF, int BSF, char *f_primMiss, + double *primMiss, char *f_secMiss, + double *secMiss, char *f_min, sInt4 *min) +{ + sInt4 cnt; + double *src = Src; + sInt4 *dst = Dst; + double scale = pow (10.0, -1 * DSF) * pow (2.0, -1 * BSF); + char f_actualPrim = 0; + char f_actualSec = 0; + sInt4 li_primMiss = (sInt4) (*primMiss * SCALE_MISSING + .5); + sInt4 li_secMiss = (sInt4) (*secMiss * SCALE_MISSING + .5); + + *f_min = 0; + for (cnt = 0; cnt < numData; cnt++) { + if (((*f_primMiss) || (*f_secMiss)) && (*src == *primMiss)) { + *(dst++) = li_primMiss; + src++; + f_actualPrim = 1; + } else if ((*f_secMiss) && (*src == *secMiss)) { + *(dst++) = li_secMiss; + src++; + f_actualSec = 1; + } else { + *(dst) = (long int) (floor ((*(src++) / scale) + .5)); + /* Check if scaled value == primary missing value. */ + if (((*f_primMiss) || (*f_secMiss)) && (*dst == li_primMiss)) { + *dst = *dst - 1; + } + /* Check if scaled value == secondary missing value. */ + if ((*f_secMiss) && (*dst == li_secMiss)) { + *dst = *dst - 1; + /* Check if adjustment caused scaled value == primary missing. */ + if (*dst == li_primMiss) { + *dst = *dst - 1; + } + } + if (!(*f_min)) { + *min = *dst; + *f_min = 1; + } else if (*min > *dst) { + *min = *dst; + } + dst++; + } + } + if ((*f_secMiss) && (!f_actualSec)) { + *f_secMiss = 0; + } + if (((*f_secMiss) || (*f_primMiss)) && (!f_actualPrim)) { + *f_primMiss = 0; + /* Check consistency. */ + if (*f_secMiss) { + *f_secMiss = 0; + *f_primMiss = 1; + *primMiss = *secMiss; + } + } +} + +/***************************************************************************** + * TDL_ReorderGrid() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Loop through the data, so that + * data is: "a1,1 ... a1,n a2,n ... a2,1 ..." + * instead of: "a1,1 ... a1,n a2,1 ... a2,n ..." + * + * ARGUMENTS + * Src = The data. (Input/Output) + * NX = The number of X values. (Input) + * NY = The number of Y values. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Updated from "group.c" in "C" tdlpack code. + * + * NOTES + ***************************************************************************** + */ +static void TDL_ReorderGrid (sInt4 *Src, short int NX, short int NY) +{ + int i, j; + sInt4 *src1, *src2; + sInt4 li_temp; + + for (j = 1; j < NY; j += 2) { + src1 = Src + j * NX; + src2 = Src + (j + 1) * NX - 1; + for (i = 0; i < (NX / 2); i++) { + li_temp = *src1; + *(src1++) = *src2; + *(src2--) = li_temp; + } + } +} + +/***************************************************************************** + * TDL_GetSecDiff() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Get the second order difference where we have special values for missing, + * and for actual data we have the following scheme. + * Data: a1 a2 a3 a4 a5 ... + * 1st diff: 0 b2 b3 b4 b5 ... + * 2nd diff: UK1 UK2 c3 c4 c5 ... + * where UK1 = c3 + b2, and UK2 = c3 + 2 * b2. Note: The choice of UK1, and + * UK2 doesn't matter because of the following FORTRAN unpacking code: + * IWORK(1)=IFIRST + * IWORK(2)=IWORK(1)+IFOD + * ISUM=IFOD + * DO 385 K=3,IS4(3) + * ISUM=IWORK(K)+ISUM + * IWORK(K)=IWORK(K-1)+ISUM + * 385 CONTINUE + * So ISUM is a function of IWORK(3), not IWORK(1). + * + * ARGUMENTS + * Data = The data. (Input) + * numData = The number of elements in data. (Input) + * SecDiff = The secondary differences of the data. (Output) + * f_primMiss = Flag saying if we have a primary missing value (Input) + * li_primMiss = Scaled primary missing value. (Input) + * a1 = First non-missing value in the field. (Output) + * b2 = First non-missing value in the 1st order delta field (Out) + * min = The minimum value (Input). + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = Success + * 1 = Couldn't find second differences (don't use). + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Updated from "group.c" in "C" tdlpack code. + * + * NOTES + ***************************************************************************** + */ +static int TDL_GetSecDiff (sInt4 *Data, int numData, sInt4 *SecDiff, + char f_primMiss, sInt4 li_primMiss, + sInt4 *a1, sInt4 *b2, sInt4 *min) +{ + int i; + char f_min = 0; + sInt4 last = 0, before_last = 0; + int a1Index = -1; + int a2Index = -1; + + if (numData < 3) { + return 1; + } + if (f_primMiss) { + for (i = 0; i < numData; i++) { + if (Data[i] == li_primMiss) { + SecDiff[i] = li_primMiss; + } else if (a1Index == -1) { + a1Index = i; + *a1 = Data[a1Index]; + } else if (a2Index == -1) { + a2Index = i; + *b2 = Data[a2Index] - Data[a1Index]; + before_last = Data[a1Index]; + last = Data[a2Index]; + } else { + SecDiff[i] = Data[i] - 2 * last + before_last; + before_last = last; + last = Data[i]; + if (!f_min) { + /* Set the UK1, UK2 values. */ + *min = SecDiff[i]; + f_min = 1; + SecDiff[a1Index] = SecDiff[i] + *b2; + SecDiff[a2Index] = SecDiff[i] + 2 * (*b2); + } else if (*min > SecDiff[i]) { + *min = SecDiff[i]; + } + } + } + if (!f_min) { + return 1; + } + } else { + *a1 = Data[0]; + *b2 = Data[1] - Data[0]; + for (i = 3; i < numData; i++) { + SecDiff[i] = Data[i] - 2 * Data[i - 1] - Data[i - 2]; + if (i == 3) { + *min = SecDiff[i]; + /* Set the UK1, UK2 values. */ + SecDiff[0] = SecDiff[i] + *b2; + SecDiff[1] = SecDiff[i] + 2 * (*b2); + } else if (*min > SecDiff[i]) { + *min = SecDiff[i]; + } + } + } + return 0; +} + +/***************************************************************************** + * TDL_UseSecDiff_Prim() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Checks if the average range of 2nd order differences < average range of + * 0 order differnces, to determine if we should use second order differences + * This deals with the case when we have primary missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * numData = The number of elements in data. (Input) + * SecDiff = The secondary differences of the data. (Input) + * li_primMiss = Scaled primary missing value. (Input) + * minGroup = The minimum group size. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = Don't use 2nd order differences. + * 1 = Use 2nd order differences. + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Updated from "group.c" in "C" tdlpack code. + * + * NOTES + ***************************************************************************** + */ +static int TDL_UseSecDiff_Prim (sInt4 *Data, sInt4 numData, + sInt4 *SecDiff, sInt4 li_primMiss, + int minGroup) +{ + int i, locCnt; + int range0, range2; + int tot0, tot2; + char f_min; + sInt4 min = 0, max = 0; + + locCnt = 0; + range0 = 0; + tot0 = 0; + f_min = 0; + /* Compute scores for no differences */ + for (i = 0; i < numData; i++) { + if (Data[i] != li_primMiss) { + if (!f_min) { + min = Data[i]; + max = Data[i]; + f_min = 1; + } else { + if (min > Data[i]) + min = Data[i]; + if (max < Data[i]) + max = Data[i]; + } + } + locCnt++; + /* Fake a "group" by using the minimum group size. */ + if (locCnt == minGroup) { + if (f_min) { + range0 += (max - min); + tot0++; + f_min = 0; + } + locCnt = 0; + } + } + if (locCnt != 0) { + range0 += (max - min); + tot0++; + } + + locCnt = 0; + range2 = 0; + tot2 = 0; + f_min = 0; + /* Compute scores for second order differences */ + for (i = 0; i < numData; i++) { + if (SecDiff[i] != li_primMiss) { + if (!f_min) { + min = SecDiff[i]; + max = SecDiff[i]; + f_min = 1; + } else { + if (min > SecDiff[i]) + min = SecDiff[i]; + if (max < SecDiff[i]) + max = SecDiff[i]; + } + } + locCnt++; + /* Fake a "group" by using the minimum group size. */ + if (locCnt == minGroup) { + if (f_min) { + range2 += (max - min); + tot2++; + f_min = 0; + } + locCnt = 0; + } + } + if (locCnt != 0) { + range2 += (max - min); + tot2++; + } + + /* Compare average group size of no differencing to second order. */ + if ((range0 / (tot0 + 0.0)) <= (range2 / (tot2 + 0.0))) { + return 0; + } else { + return 1; + } +} + +/***************************************************************************** + * TDL_UseSecDiff() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Checks if the average range of 2nd order differences < average range of + * 0 order differnces, to determine if we should use second order differences + * + * ARGUMENTS + * Data = The data. (Input) + * numData = The number of elements in data. (Input) + * SecDiff = The secondary differences of the data. (Input) + * minGroup = The minimum group size. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = Don't use 2nd order differences. + * 1 = Use 2nd order differences. + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Updated from "group.c" in "C" tdlpack code. + * + * NOTES + ***************************************************************************** + */ +static int TDL_UseSecDiff (sInt4 *Data, sInt4 numData, + sInt4 *SecDiff, int minGroup) +{ + int i, locCnt; + int range0, range2; + int tot0, tot2; + sInt4 min = 0, max = 0; + + locCnt = 0; + range0 = 0; + tot0 = 0; + /* Compute scores for no differences */ + for (i = 0; i < numData; i++) { + if (locCnt == 0) { + min = Data[i]; + max = Data[i]; + } else { + if (min > Data[i]) + min = Data[i]; + if (max < Data[i]) + max = Data[i]; + } + locCnt++; + /* Fake a "group" by using the minimum group size. */ + if (locCnt == minGroup) { + range0 += (max - min); + tot0++; + locCnt = 0; + } + } + if (locCnt != 0) { + range0 += (max - min); + tot0++; + } + + locCnt = 0; + range2 = 0; + tot2 = 0; + /* Compute scores for second order differences */ + for (i = 0; i < numData; i++) { + if (locCnt == 0) { + min = SecDiff[i]; + max = SecDiff[i]; + } else { + if (min > SecDiff[i]) + min = SecDiff[i]; + if (max < SecDiff[i]) + max = SecDiff[i]; + } + locCnt++; + /* Fake a "group" by using the minimum group size. */ + if (locCnt == minGroup) { + range2 += (max - min); + tot2++; + locCnt = 0; + } + } + if (locCnt != 0) { + range2 += (max - min); + tot2++; + } + + /* Compare average group size of no differencing to second order. */ + if ((range0 / (tot0 + 0.0)) <= (range2 / (tot2 + 0.0))) { + return 0; + } else { + return 1; + } +} + +/***************************************************************************** + * power() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Calculate the number of bits required to store a given positive number. + * + * ARGUMENTS + * val = The number to store (Input) + * extra = number of slots to allocate for prim/sec missing values (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int + * The number of bits needed to store this number. + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Updated from "group.c" in "C" tdlpack code. + * + * NOTES + ***************************************************************************** + */ +static int power (uInt4 val, int extra) +{ + int i; + + val += extra; + if (val == 0) { + return 1; + } + for (i = 0; val != 0; i++) { + val = val >> 1; + } + return i; +} + +/***************************************************************************** + * findMaxMin2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Find the min/max value between start/stop index values in the data + * Assuming primary and secondary missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * start = The starting index in data (Input) + * stop = The stoping index in data (Input) + * li_primMiss = scaled primary missing value (Input) + * li_secMiss = scaled secondary missing value (Input) + * min = The min value found (Output) + * max = The max value found (Output) + * + * FILES/DATABASES: None + * + * RETURNS: char + * Flag if min/max are valid. + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Updated from "group.c" in "C" tdlpack code. + * + * NOTES + ***************************************************************************** + */ +static char findMaxMin2 (sInt4 *Data, int start, int stop, + sInt4 li_primMiss, sInt4 li_secMiss, + sInt4 *min, sInt4 *max) +{ + char f_min = 0; /* Flag if we found the max/min values */ + int i; /* Loop counter. */ + + *max = *min = Data[start]; + for (i = start; i < stop; i++) { + if ((Data[i] != li_secMiss) && (Data[i] != li_primMiss)) { + if (!f_min) { + *max = Data[i]; + *min = Data[i]; + f_min = 1; + } else { + if (*max < Data[i]) { + *max = Data[i]; + } else if (*min > Data[i]) { + *min = Data[i]; + } + } + } + } + return f_min; +} + +/***************************************************************************** + * findMaxMin1() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Find the min/max value between start/stop index values in the data + * Assuming primary missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * start = The starting index in data (Input) + * stop = The stoping index in data (Input) + * li_primMiss = scaled primary missing value (Input) + * min = The min value found (Output) + * max = The max value found (Output) + * + * FILES/DATABASES: None + * + * RETURNS: char + * Flag if min/max are valid. + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Updated from "group.c" in "C" tdlpack code. + * + * NOTES + ***************************************************************************** + */ +static char findMaxMin1 (sInt4 *Data, int start, int stop, + sInt4 li_primMiss, sInt4 *min, sInt4 *max) +{ + char f_min = 0; /* Flag if we found the max/min values */ + int i; /* Loop counter. */ + + *max = *min = Data[start]; + for (i = start; i < stop; i++) { + if (Data[i] != li_primMiss) { + if (!f_min) { + *max = Data[i]; + *min = Data[i]; + f_min = 1; + } else { + if (*max < Data[i]) { + *max = Data[i]; + } else if (*min > Data[i]) { + *min = Data[i]; + } + } + } + } + return f_min; +} + +/***************************************************************************** + * findMaxMin0() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Find the min/max value between start/stop index values in the data + * Assuming no missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * start = The starting index in data (Input) + * stop = The stoping index in data (Input) + * min = The min value found (Output) + * max = The max value found (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Updated from "group.c" in "C" tdlpack code. + * + * NOTES + ***************************************************************************** + */ +static void findMaxMin0 (sInt4 *Data, int start, int stop, sInt4 *min, + sInt4 *max) +{ + int i; /* Loop counter. */ + + *max = *min = Data[start]; + for (i = start + 1; i < stop; i++) { + if (*max < Data[i]) { + *max = Data[i]; + } else if (*min > Data[i]) { + *min = Data[i]; + } + } +} + +/***************************************************************************** + * findGroup2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Find "split" so that the numbers between start and split are within + * "range" of each other... stops if it reaches "stop". + * Assumes primary and secondary missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * start = The starting index in data (Input) + * stop = The stoping index in data (Input) + * li_primMiss = scaled primary missing value (Input) + * li_secMiss = scaled secondary missing value (Input) + * range = The range to use (Input) + * split = The first index that is out of the range (Output) + * min = The min value for the group. (Output) + * max = The max value for the group. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created + * + * NOTES + ***************************************************************************** + */ +static void findGroup2 (sInt4 *Data, int start, int stop, + sInt4 li_primMiss, sInt4 li_secMiss, + sInt4 range, int *split, sInt4 *min, sInt4 *max) +{ + char f_min = 0; /* Flag if we found the max/min values */ + int i; /* Loop counter. */ + + *min = *max = 0; + for (i = start; i < stop; i++) { + if ((Data[i] != li_secMiss) && (Data[i] != li_primMiss)) { + if (!f_min) { + *max = *min = Data[i]; + f_min = 1; + } else { + if (*max < Data[i]) { + if ((Data[i] - *min) > range) { + *split = i; + return; + } + *max = Data[i]; + } else if (*min > Data[i]) { + if ((*max - Data[i]) > range) { + *split = i; + return; + } + *min = Data[i]; + } + } + } + } + *split = stop; +} + +/***************************************************************************** + * findGroup1() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Find "split" so that the numbers between start and split are within + * "range" of each other... stops if it reaches "stop". + * Assumes primary missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * start = The starting index in data (Input) + * stop = The stoping index in data (Input) + * li_primMiss = scaled primary missing value (Input) + * range = The range to use (Input) + * split = The first index that is out of the range (Output) + * min = The min value for the group. (Output) + * max = The max value for the group. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created + * + * NOTES + ***************************************************************************** + */ +static void findGroup1 (sInt4 *Data, int start, int stop, + sInt4 li_primMiss, sInt4 range, int *split, + sInt4 *min, sInt4 *max) +{ + char f_min = 0; /* Flag if we found the max/min values */ + int i; /* Loop counter. */ + + *min = *max = 0; + for (i = start; i < stop; i++) { + if (Data[i] != li_primMiss) { + if (!f_min) { + *max = *min = Data[i]; + f_min = 1; + } else { + if (*max < Data[i]) { + if ((Data[i] - *min) > range) { + *split = i; + return; + } + *max = Data[i]; + } else if (*min > Data[i]) { + if ((*max - Data[i]) > range) { + *split = i; + return; + } + *min = Data[i]; + } + } + } + } + *split = stop; +} + +/***************************************************************************** + * findGroup0() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Find "split" so that the numbers between start and split are within + * "range" of each other... stops if it reaches "stop". + * Assumes no missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * start = The starting index in data (Input) + * stop = The stoping index in data (Input) + * range = The range to use (Input) + * split = The first index that is out of the range (Output) + * min = The min value for the group. (Output) + * max = The max value for the group. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created + * + * NOTES + ***************************************************************************** + */ +static void findGroup0 (sInt4 *Data, int start, int stop, + sInt4 range, int *split, sInt4 *min, sInt4 *max) +{ + int i; /* Loop counter. */ + + *max = *min = Data[0]; + for (i = start + 1; i < stop; i++) { + if (*max < Data[i]) { + if ((Data[i] - *min) > range) { + *split = i; + return; + } + *max = Data[i]; + } else if (*min > Data[i]) { + if ((*max - Data[i]) > range) { + *split = i; + return; + } + *min = Data[i]; + } + } + *split = stop; +} + +/***************************************************************************** + * findGroupRev2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Find "split" so that the numbers between split and stop are within + * "range" of each other... stops if it reaches "start". + * Assumes primary and secondary missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * start = The starting index in data (Input) + * stop = The stoping index in data (Input) + * li_primMiss = scaled primary missing value (Input) + * li_secMiss = scaled secondary missing value (Input) + * range = The range to use (Input) + * split = The first index that is still in the range (Output) + * min = The min value for the group. (Output) + * max = The max value for the group. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created + * + * NOTES + ***************************************************************************** + */ +static void findGroupRev2 (sInt4 *Data, int start, int stop, + sInt4 li_primMiss, sInt4 li_secMiss, + sInt4 range, int *split, sInt4 *min, sInt4 *max) +{ + char f_min = 0; /* Flag if we found the max/min values */ + int i; /* Loop counter. */ + + *min = *max = 0; + for (i = stop - 1; i >= start; i--) { + if ((Data[i] != li_secMiss) && (Data[i] != li_primMiss)) { + if (!f_min) { + *max = *min = Data[i]; + f_min = 1; + } else { + if (*max < Data[i]) { + if ((Data[i] - *min) > range) { + *split = i + 1; + return; + } + *max = Data[i]; + } else if (*min > Data[i]) { + if ((*max - Data[i]) > range) { + *split = i + 1; + return; + } + *min = Data[i]; + } + } + } + } + *split = start; +} + +/***************************************************************************** + * findGroupRev1() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Find "split" so that the numbers between split and stop are within + * "range" of each other... stops if it reaches "start". + * Assumes primary missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * start = The starting index in data (Input) + * stop = The stoping index in data (Input) + * li_primMiss = scaled primary missing value (Input) + * range = The range to use (Input) + * split = The first index that is still in the range (Output) + * min = The min value for the group. (Output) + * max = The max value for the group. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created + * + * NOTES + ***************************************************************************** + */ +static void findGroupRev1 (sInt4 *Data, int start, int stop, + sInt4 li_primMiss, sInt4 range, int *split, + sInt4 *min, sInt4 *max) +{ + char f_min = 0; /* Flag if we found the max/min values */ + int i; /* Loop counter. */ + + *min = *max = 0; + for (i = stop - 1; i >= start; i--) { + if (Data[i] != li_primMiss) { + if (!f_min) { + *max = *min = Data[i]; + f_min = 1; + } else { + if (*max < Data[i]) { + if ((Data[i] - *min) > range) { + *split = i + 1; + return; + } + *max = Data[i]; + } else if (*min > Data[i]) { + if ((*max - Data[i]) > range) { + *split = i + 1; + return; + } + *min = Data[i]; + } + } + } + } + *split = start; +} + +/***************************************************************************** + * findGroupRev0() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Find "split" so that the numbers between split and stop are within + * "range" of each other... stops if it reaches "start". + * Assumes no missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * start = The starting index in data (Input) + * stop = The stoping index in data (Input) + * li_primMiss = scaled primary missing value (Input) + * range = The range to use (Input) + * split = The first index that is still in the range (Output) + * min = The min value for the group. (Output) + * max = The max value for the group. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created + * + * NOTES + ***************************************************************************** + */ +static void findGroupRev0 (sInt4 *Data, int start, int stop, + sInt4 range, int *split, sInt4 *min, sInt4 *max) +{ + int i; /* Loop counter. */ + + *max = *min = Data[stop - 1]; + for (i = stop - 2; i >= start; i--) { + if (*max < Data[i]) { + if ((Data[i] - *min) > range) { + *split = i + 1; + return; + } + *max = Data[i]; + } else if (*min > Data[i]) { + if ((*max - Data[i]) > range) { + *split = i + 1; + return; + } + *min = Data[i]; + } + } + *split = start; +} + +/***************************************************************************** + * shiftGroup2() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Find "split" so that the numbers between split and start1 are all inside + * the range defined by max, min and bit. It allows max and min to change, + * as long as it doesn't exceed the range defined by bit. + * This is very similar to findGroupRev?() but here we already know + * information about the min/max values, and are just expanding the group a + * little, while the other one knew nothing about the group, and just wanted + * a group of a given range. + * Assumes primary and secondary missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * start1 = The starting index in data (Input) + * start2 = The starting index of the earlier group (ie don't go to any + * earlier indicies than this. (Input) + * li_primMiss = scaled primary missing value (Input) + * li_secMiss = scaled secondary missing value (Input) + * bit = The range we are allowed to store this in. (Input) + * min = The min value for the group. (Input/Output) + * max = The max value for the group. (Input/Output) + * split = The first index that is still in the range (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created + * + * NOTES + ***************************************************************************** + */ +static void shiftGroup2 (sInt4 *Data, int start1, int start2, + sInt4 li_primMiss, sInt4 li_secMiss, int bit, + sInt4 *min, sInt4 *max, size_t *split) +{ + int i; /* Loop counter. */ + int range; /* The range defined by bit. */ + + range = (int) (pow (2.0, bit) - 1) - 1; + myAssert (start2 <= start1); + for (i = start1; i >= start2; i--) { + if ((Data[i] != li_primMiss) && (Data[i] != li_secMiss)) { + if (Data[i] > *max) { + if ((Data[i] - *min) <= range) { + *max = Data[i]; + } else { + *split = i + 1; + return; + } + } else if (Data[i] < *min) { + if ((*max - Data[i]) <= range) { + *min = Data[i]; + } else { + *split = i + 1; + return; + } + } + } + } + *split = start2; +} + +/***************************************************************************** + * shiftGroup1() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Find "split" so that the numbers between split and start1 are all inside + * the range defined by max, min and bit. It allows max and min to change, + * as long as it doesn't exceed the range defined by bit. + * This is very similar to findGroupRev?() but here we already know + * information about the min/max values, and are just expanding the group a + * little, while the other one knew nothing about the group, and just wanted + * a group of a given range. + * Assumes primary missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * start1 = The starting index in data (Input) + * start2 = The starting index of the earlier group (ie don't go to any + * earlier indicies than this. (Input) + * li_primMiss = scaled primary missing value (Input) + * bit = The range we are allowed to store this in. (Input) + * min = The min value for the group. (Input/Output) + * max = The max value for the group. (Input/Output) + * split = The first index that is still in the range (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created + * + * NOTES + ***************************************************************************** + */ +static void shiftGroup1 (sInt4 *Data, int start1, int start2, + sInt4 li_primMiss, int bit, + sInt4 *min, sInt4 *max, size_t *split) +{ + int i; /* Loop counter. */ + int range; /* The range defined by bit. */ + + range = (int) (pow (2.0, bit) - 1) - 1; + myAssert (start2 <= start1); + for (i = start1; i >= start2; i--) { + if (Data[i] != li_primMiss) { + if (Data[i] > *max) { + if ((Data[i] - *min) <= range) { + *max = Data[i]; + } else { + *split = i + 1; + return; + } + } else if (Data[i] < *min) { + if ((*max - Data[i]) <= range) { + *min = Data[i]; + } else { + *split = i + 1; + return; + } + } + } + } + *split = start2; +} + +/***************************************************************************** + * shiftGroup0() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Find "split" so that the numbers between split and start1 are all inside + * the range defined by max, min and bit. It allows max and min to change, + * as long as it doesn't exceed the range defined by bit. + * This is very similar to findGroupRev?() but here we already know + * information about the min/max values, and are just expanding the group a + * little, while the other one knew nothing about the group, and just wanted + * a group of a given range. + * Assumes no missing values. + * + * ARGUMENTS + * Data = The data. (Input) + * start1 = The starting index in data (Input) + * start2 = The starting index of the earlier group (ie don't go to any + * earlier indicies than this. (Input) + * bit = The range we are allowed to store this in. (Input) + * min = The min value for the group. (Input/Output) + * max = The max value for the group. (Input/Output) + * split = The first index that is still in the range (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created + * + * NOTES + ***************************************************************************** + */ +static void shiftGroup0 (sInt4 *Data, int start1, int start2, int bit, + sInt4 *min, sInt4 *max, size_t *split) +{ + int i; /* Loop counter. */ + int range; /* The range defined by bit. */ + + range = (int) (pow (2.0, bit) - 1) - 0; + myAssert (start2 <= start1); + for (i = start1; i >= start2; i--) { + if (Data[i] > *max) { + if ((Data[i] - *min) <= range) { + *max = Data[i]; + } else { + *split = i + 1; + return; + } + } else if (Data[i] < *min) { + if ((*max - Data[i]) <= range) { + *min = Data[i]; + } else { + *split = i + 1; + return; + } + } + } + *split = start2; +} + +/***************************************************************************** + * doSplit() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Reduce the "bit range", and create groups that grab as much as they can + * to the right. Then reduce those groups if they improve the score. + * + * ARGUMENTS + * Data = The data. (Input) + * numData = The number of elements in data. (Input) + * G = The group to split. (Input) + * lclGroup = The resulting groups (Output) + * numLclGroup = The number of resulting groups. (Output) + * f_primMiss = Flag if we have a primary missing value (Input) + * li_primMiss = scaled primary missing value (Input) + * f_secMiss = Flag if we have a secondary missing value (Input) + * li_secMiss = scaled secondary missing value (Input) + * xFactor = Estimate of cost (in bits) of a group. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +static void doSplit (sInt4 *Data, + CPL_UNUSED int numData, + TDLGroupType * G, + TDLGroupType ** lclGroup, int *numLclGroup, + char f_primMiss, sInt4 li_primMiss, + char f_secMiss, sInt4 li_secMiss, int xFactor) +{ + int start; /* Where to start the current group. */ + int range; /* The range to make the groups. */ + int final; /* One more than the last index in the group G. */ + int split; /* Where to split the group. */ + TDLGroupType G1; /* The current group to add. */ + TDLGroupType G2; /* The group if we evaporated the previous group. */ + int evaporate; /* How many groups we have "evaporated". */ + int i; /* Loop counter to help "evaporate" groups. */ + sInt4 scoreA; /* The original score for 2 groups */ + sInt4 scoreB; /* The new score (having evaporated a group) */ + int GroupLen; /* Actual alloc'ed group len. */ + + /* *INDENT-OFF* */ + /* The (pow (2, ..) -1) is because 2^n - 1 is max range of a group. + * Example n = 1, 2^1 -1 = 1 range of (1,0) is 1. + * Example n = 2, 2^2 -1 = 3 range of (3,2,1,0) is 3. + * The G.bit - 1 is because we are trying to reduce the range. */ + /* *INDENT-ON* */ + range = (int) (pow (2.0, G->bit - 1) - 1) - (f_secMiss + f_primMiss); + split = G->start; + start = G->start; + final = G->start + G->num; + myAssert (final <= numData); + *numLclGroup = 0; + GroupLen = 1; + *lclGroup = (TDLGroupType *) malloc (GroupLen * sizeof (TDLGroupType)); + while (split < final) { + if (f_secMiss) { + findGroup2 (Data, start, final, li_primMiss, li_secMiss, range, + &split, &(G1.min), &(G1.max)); + } else if (f_primMiss) { + findGroup1 (Data, start, final, li_primMiss, range, &split, + &(G1.min), &(G1.max)); + } else { + findGroup0 (Data, start, final, range, &split, &(G1.min), &(G1.max)); + } + G1.bit = (char) power ((uInt4) (G1.max - G1.min), + f_secMiss + f_primMiss); + G1.num = split - start; + G1.start = start; + G1.f_trySplit = 1; + G1.f_tryShift = 1; + /* Test if we should add to previous group, or create a new group. */ + if (*numLclGroup == 0) { + *numLclGroup = 1; + (*lclGroup)[0] = G1; + } else { + G2.start = (*lclGroup)[*numLclGroup - 1].start; + G2.num = (*lclGroup)[*numLclGroup - 1].num + G1.num; + G2.min = ((*lclGroup)[*numLclGroup - 1].min < G1.min) ? + (*lclGroup)[*numLclGroup - 1].min : G1.min; + G2.max = ((*lclGroup)[*numLclGroup - 1].max > G1.max) ? + (*lclGroup)[*numLclGroup - 1].max : G1.max; + G2.bit = (char) power ((uInt4) (G2.max - G2.min), + f_secMiss + f_primMiss); + G2.f_trySplit = 1; + G2.f_tryShift = 1; + scoreA = ((*lclGroup)[*numLclGroup - 1].bit * + (*lclGroup)[*numLclGroup - 1].num) + xFactor; + scoreA += G1.bit * G1.num + xFactor; + scoreB = G2.bit * G2.num + xFactor; + if (scoreB < scoreA) { + (*lclGroup)[*numLclGroup - 1] = G2; + /* See if we can evaporate any of the old groups */ + evaporate = 0; + for (i = *numLclGroup - 1; i > 0; i--) { + G1.start = (*lclGroup)[i - 1].start; + G1.num = (*lclGroup)[i].num + (*lclGroup)[i - 1].num; + G1.min = ((*lclGroup)[i].min < (*lclGroup)[i - 1].min) ? + (*lclGroup)[i].min : (*lclGroup)[i - 1].min; + G1.max = ((*lclGroup)[i].max > (*lclGroup)[i - 1].max) ? + (*lclGroup)[i].max : (*lclGroup)[i - 1].max; + G1.bit = (char) power ((uInt4) (G1.max - G1.min), + f_secMiss + f_primMiss); + G1.f_trySplit = 1; + G1.f_tryShift = 1; + scoreA = (*lclGroup)[i].bit * (*lclGroup)[i].num + xFactor; + scoreA += ((*lclGroup)[i - 1].bit * (*lclGroup)[i - 1].num + + xFactor); + scoreB = G1.bit * G1.num + xFactor; + if (scoreB < scoreA) { + evaporate++; + (*lclGroup)[i - 1] = G1; + } else { + break; + } + } + if (evaporate != 0) { + *numLclGroup = *numLclGroup - evaporate; + } + } else { + *numLclGroup = *numLclGroup + 1; + if (*numLclGroup > GroupLen) { + GroupLen = *numLclGroup; + *lclGroup = (TDLGroupType *) realloc ((void *) *lclGroup, + GroupLen * + sizeof (TDLGroupType)); + } + (*lclGroup)[*numLclGroup - 1] = G1; + } + } + start = split; + } +} + +/***************************************************************************** + * doSplitRight() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Break into two groups right has range n - 1, left has range n. + * + * ARGUMENTS + * Data = The data. (Input) + * numData = The number of elements in data. (Input) + * G = The group to split. (Input) + * G1 = The right most group (Output) + * G2 = The remainder. (Output) + * f_primMiss = Flag if we have a primary missing value (Input) + * li_primMiss = scaled primary missing value (Input) + * f_secMiss = Flag if we have a secondary missing value (Input) + * li_secMiss = scaled secondary missing value (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +static void doSplitRight (sInt4 *Data, + CPL_UNUSED int numData, + TDLGroupType * G, + TDLGroupType * G1, TDLGroupType * G2, + char f_primMiss, sInt4 li_primMiss, + char f_secMiss, sInt4 li_secMiss) +{ + int range; /* The range to make the right most group. */ + int final; /* One more than the last index in the group. */ + int split; /* Where to split the group. */ + + /* *INDENT-OFF* */ + /* The (pow (2, ..) -1) is because 2^n - 1 is max range of a group. + * Example n = 1, 2^1 -1 = 1 range of (1,0) is 1. + * Example n = 2, 2^2 -1 = 3 range of (3,2,1,0) is 3. + * The G.bit - 1 is because we are trying to reduce the range. */ + /* *INDENT-ON* */ + range = (int) (pow (2.0, G->bit - 1) - 1) - (f_secMiss + f_primMiss); + final = G->start + G->num; + split = final; + myAssert (final <= numData); + + if (f_secMiss) { + findGroupRev2 (Data, G->start, final, li_primMiss, li_secMiss, range, + &split, &(G1->min), &(G1->max)); + findMaxMin2 (Data, G->start, split, li_primMiss, li_secMiss, + &(G2->min), &(G2->max)); + } else if (f_primMiss) { + findGroupRev1 (Data, G->start, final, li_primMiss, range, &split, + &(G1->min), &(G1->max)); + findMaxMin1 (Data, G->start, split, li_primMiss, &(G2->min), + &(G2->max)); + } else { + findGroupRev0 (Data, G->start, final, range, &split, &(G1->min), + &(G1->max)); + findMaxMin0 (Data, G->start, split, &(G2->min), &(G2->max)); + } + + G1->bit = (char) power ((uInt4) (G1->max - G1->min), + f_secMiss + f_primMiss); + G2->bit = (char) power ((uInt4) (G2->max - G2->min), + f_secMiss + f_primMiss); + G1->start = split; + G2->start = G->start; + G1->num = final - split; + G2->num = split - G->start; + G1->f_trySplit = 1; + G1->f_tryShift = 1; + G2->f_trySplit = 1; + G2->f_tryShift = 1; +} + +/***************************************************************************** + * ComputeGroupSize() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Compute the number of bits needed for the various elements of the groups + * as well as the total number of bits needed. + * + * ARGUMENTS + * group = Groups (Input) + * numGroup = Number of groups (Input) + * ibit = Number of bits needed for the minimum values of each group. + * Find max absolute value of group mins. Note: all group mins + * are positive (Output) + * jbit = Number of bits needed for the number of bits for each group. + * Find max absolute value of number of bits. (Output) + * kbit = Number of bits needed for the number of values for each group. + * Find max absolute value of number of values. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: sInt4 + * number of bits needed by the groups + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Updated from "tdlpack.c" in "C" tdlpack code + * + * NOTES + ***************************************************************************** + */ +static sInt4 ComputeGroupSize (TDLGroupType * group, int numGroup, + size_t *ibit, size_t *jbit, size_t *kbit) +{ + int i; /* loop counter. */ + sInt4 ans = 0; /* The number of bits needed. */ + sInt4 maxMin = 0; /* The largest min value in the groups */ + uChar maxBit = 0; /* The largest needed bits in the groups */ + uInt4 maxNum = 0; /* The largest number of values in the groups. */ + + for (i = 0; i < numGroup; i++) { + ans += group[i].bit * group[i].num; + if (group[i].min > maxMin) { + maxMin = group[i].min; + } + if (group[i].bit > maxBit) { + maxBit = group[i].bit; + } + if (group[i].num > maxNum) { + maxNum = group[i].num; + } + } + /* This only works for pos numbers... */ + for (i = 0; (maxMin != 0); i++) { + maxMin = maxMin >> 1; + } + /* Allow 0 bits for min. Assumes that decoder allows 0 bits */ + *ibit = i; + /* This only works for pos numbers... */ + for (i = 0; (maxBit != 0); i++) { + maxBit = maxBit >> 1; + } + /* Allow 0 bits for min. Assumes that decoder allows 0 bits */ + *jbit = i; + /* This only works for pos numbers... */ + for (i = 0; (maxNum != 0); i++) { + maxNum = maxNum >> 1; + } + /* Allow 0 bits for min. Assumes that decoder allows 0 bits */ + *kbit = i; + ans += ((*ibit) + (*jbit) + (*kbit)) * numGroup; + return ans; +} + +/***************************************************************************** + * splitGroup() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Tries to reduce (split) each group by 1 bit. It does this by: + * A) reduce the "bit range", and create groups that grab as much as they + * can to the right. Then reduce those groups if they improve the score. + * B) reduce the bit range and grab the left most group only, leaving the + * rest unchanged. + * C) reduce the bit range and grab the right most group only, leaving the + * rest unchanged. + * + * ARGUMENTS + * Data = The data. (Input) + * numData = The number of elements in data. (Input) + * group = The groups using the "best minGroup. (Input) + * numGroup = Number of groups (Input) + * lclGroup = The local copy of the groups (Output) + * numLclGroup = Number of local groups (Output) + * f_primMiss = Flag if we have a primary missing value (Input) + * li_primMiss = scaled primary missing value (Input) + * f_secMiss = Flag if we have a secondary missing value (Input) + * li_secMiss = scaled secondary missing value (Input) + * xFactor = Estimate of cost (in bits) of a group. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +static int splitGroup (sInt4 *Data, int numData, TDLGroupType * group, + int numGroup, TDLGroupType ** lclGroup, + int *numLclGroup, char f_primMiss, + sInt4 li_primMiss, char f_secMiss, + sInt4 li_secMiss, size_t xFactor) +{ + uInt4 minBit; /* The fewest # of bits, with no subdivision. */ + TDLGroupType *subGroup; /* The subgroups that we tried splitting the + * primary group into. */ + int numSubGroup; /* The number of groups in subGroup. */ + sInt4 A_max; /* Max value of a given group. */ + sInt4 A_min; /* Min value of a given group. */ + sInt4 scoreA; /* The original score for a given group */ + sInt4 scoreB; /* The new score */ + int f_adjust = 0; /* Flag if group has changed. */ + int f_keep; /* Flag to keep the subgroup instead of original */ + int i; /* Loop counters */ + int sub; /* loop counter over the sub group. */ + int lclIndex; /* Used to help copy data from subGroup to answer. */ + int GroupLen; /* Actual alloc'ed group len. */ + int extra; /* Used to reduce number of allocs. */ + + /* Figure out how few bits a group can have without being able to further + * divide it. */ + if (f_secMiss) { + /* 11 = primMiss 10 = secMiss 01, 00 = data. */ + minBit = 2; + } else if (f_primMiss) { + /* 1 = primMiss 0 = data. */ + /* might try minBit = 1 here. */ + minBit = 1; + } else { + /* 1, 0 = data. */ + minBit = 1; + } + + *numLclGroup = 0; + *lclGroup = (TDLGroupType *) malloc (numGroup * sizeof (TDLGroupType)); + GroupLen = numGroup; + extra = 0; + for (i = 0; i < numGroup; i++) { + /* Check if we have already tried to split this group, or it has too + * few members, or it doesn't have enough bits to split. If so, skip + * this group. */ + if ((group[i].f_trySplit) && (group[i].num > xFactor) && + (group[i].bit > minBit)) { + f_keep = 0; + doSplit (Data, numData, &(group[i]), &subGroup, &numSubGroup, + f_primMiss, li_primMiss, f_secMiss, li_secMiss, xFactor); + if (numSubGroup != 1) { + scoreA = group[i].bit * group[i].num + xFactor; + scoreB = 0; + for (sub = 0; sub < numSubGroup; sub++) { + scoreB += subGroup[sub].bit * subGroup[sub].num + xFactor; + } + if (scoreB < scoreA) { + f_keep = 1; + } else if (numSubGroup > 2) { + /* We can do "doSplitLeft" (which is breaking it into 2 groups + * the first having range n - 1, the second having range n, + * using what we know from doSplit. */ + subGroup[1].num = group[i].num - subGroup[0].num; + if (f_secMiss) { + findMaxMin2 (Data, subGroup[1].start, + subGroup[1].start + subGroup[1].num, + li_primMiss, li_secMiss, &A_min, &A_max); + } else if (f_primMiss) { + findMaxMin1 (Data, subGroup[1].start, + subGroup[1].start + subGroup[1].num, + li_primMiss, &A_min, &A_max); + } else { + findMaxMin0 (Data, subGroup[1].start, + subGroup[1].start + subGroup[1].num, + &A_min, &A_max); + } + subGroup[1].min = A_min; + subGroup[1].max = A_max; + subGroup[1].bit = power ((uInt4) (A_max - A_min), + f_secMiss + f_primMiss); + subGroup[1].f_trySplit = 1; + subGroup[1].f_tryShift = 1; + numSubGroup = 2; + scoreB = subGroup[0].bit * subGroup[0].num + xFactor; + scoreB += subGroup[1].bit * subGroup[1].num + xFactor; + if (scoreB < scoreA) { + f_keep = 1; + } + } + } + if (!f_keep) { + if (numSubGroup == 1) { + subGroup = (TDLGroupType *) realloc (subGroup, + 2 * + sizeof (TDLGroupType)); + } + numSubGroup = 2; + doSplitRight (Data, numData, &(group[i]), &(subGroup[1]), + &(subGroup[0]), f_primMiss, li_primMiss, f_secMiss, + li_secMiss); + scoreA = group[i].bit * group[i].num + xFactor; + scoreB = subGroup[0].bit * subGroup[0].num + xFactor; + scoreB += subGroup[1].bit * subGroup[1].num + xFactor; + if (scoreB < scoreA) { + f_keep = 1; + } + } + if (f_keep) { + lclIndex = *numLclGroup; + *numLclGroup = *numLclGroup + numSubGroup; + if (*numLclGroup > GroupLen) { + GroupLen += extra; + extra = 0; + if (*numLclGroup > GroupLen) { + GroupLen = *numLclGroup; + } + *lclGroup = (TDLGroupType *) realloc ((void *) *lclGroup, + GroupLen * + sizeof (TDLGroupType)); + } else { + extra += numSubGroup - 1; + } + memcpy ((*lclGroup) + lclIndex, subGroup, + numSubGroup * sizeof (TDLGroupType)); + f_adjust = 1; + } else { + *numLclGroup = *numLclGroup + 1; + if (*numLclGroup > GroupLen) { + GroupLen += extra; + extra = 0; + if (*numLclGroup > GroupLen) { + GroupLen = *numLclGroup; + } + *lclGroup = (TDLGroupType *) realloc ((void *) *lclGroup, + GroupLen * + sizeof (TDLGroupType)); + } + (*lclGroup)[*numLclGroup - 1] = group[i]; + (*lclGroup)[*numLclGroup - 1].f_trySplit = 0; + } + free (subGroup); + subGroup = NULL; + } else { + *numLclGroup = *numLclGroup + 1; + if (*numLclGroup > GroupLen) { + GroupLen += extra; + extra = 0; + if (*numLclGroup > GroupLen) { + GroupLen = *numLclGroup; + } + *lclGroup = (TDLGroupType *) realloc ((void *) *lclGroup, + GroupLen * + sizeof (TDLGroupType)); + } + (*lclGroup)[*numLclGroup - 1] = group[i]; + (*lclGroup)[*numLclGroup - 1].f_trySplit = 0; + } + } + myAssert (GroupLen == *numLclGroup); + return f_adjust; +} + +/***************************************************************************** + * shiftGroup() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Tries to shift / join the groups together. It does this by first + * calculating if a group should still exist. If it should, then it has + * each group grab as much as it can to the left without increasing its "bit + * range". + * + * ARGUMENTS + * Data = The data. (Input) + * numData = The number of elements in data. (Input) + * group = The resulting groups. (Output) + * numGroup = Number of groups (Output) + * f_primMiss = Flag if we have a primary missing value (Input) + * li_primMiss = scaled primary missing value (Input) + * f_secMiss = Flag if we have a secondary missing value (Input) + * li_secMiss = scaled secondary missing value (Input) + * xFactor = Estimate of cost (in bits) of a group. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created. + * + * NOTES + ***************************************************************************** + */ +static void shiftGroup (sInt4 *Data, + CPL_UNUSED int numData, + TDLGroupType ** Group, + size_t *NumGroup, char f_primMiss, sInt4 li_primMiss, + char f_secMiss, sInt4 li_secMiss, int xFactor) +{ + TDLGroupType *group = (*Group); /* Local pointer to Group. */ + int numGroup = (*NumGroup); /* # elements in group. */ + int i, j; /* loop counters. */ + sInt4 A_max; /* Max value of a given group. */ + sInt4 A_min; /* Min value of a given group. */ + size_t begin; /* New start to the group. */ + sInt4 scoreA; /* The original score for group i and i - 1 */ + sInt4 scoreB; /* The new score for group i and i - 1 */ + TDLGroupType G1; /* The "new" group[i - 1]. */ + TDLGroupType G2; /* The "new" group[i]. */ + int evaporate = 0; /* number of groups that "evaporated" */ + int index; /* index while getting rid of "evaporated groups". */ + + for (i = numGroup - 1; i > 0; i--) { + myAssert (group[i].num > 0); + /* See if we can evaporate the group n - 1 */ + G1.start = group[i - 1].start; + G1.num = group[i].num + group[i - 1].num; + G1.min = (group[i].min < group[i - 1].min) ? + group[i].min : group[i - 1].min; + G1.max = (group[i].max > group[i - 1].max) ? + group[i].max : group[i - 1].max; + G1.bit = (char) power ((uInt4) (G1.max - G1.min), + f_secMiss + f_primMiss); + G1.f_trySplit = 1; + G1.f_tryShift = 1; + scoreA = group[i].bit * group[i].num + xFactor; + scoreA += group[i - 1].bit * group[i - 1].num + xFactor; + scoreB = G1.bit * G1.num + xFactor; + if (scoreB < scoreA) { + /* One of the groups evaporated. */ + evaporate++; + group[i - 1] = G1; + group[i].num = 0; + /* See if that affects any of the previous groups. */ + for (j = i + 1; j < numGroup; j++) { + if (group[j].num != 0) { + G1.start = G1.start; + G1.num = group[i - 1].num + group[j].num; + G1.min = (group[i - 1].min < group[j].min) ? + group[i - 1].min : group[j].min; + G1.max = (group[i - 1].max > group[j].max) ? + group[i - 1].max : group[j].max; + G1.bit = (char) power ((uInt4) (G1.max - G1.min), + f_secMiss + f_primMiss); + G1.f_trySplit = 1; + G1.f_tryShift = 1; + scoreA = group[i - 1].bit * group[i - 1].num + xFactor; + scoreA += group[j].bit * group[j].num + xFactor; + scoreB = G1.bit * G1.num + xFactor; + if (scoreB < scoreA) { + evaporate++; + group[i - 1] = G1; + group[j].num = 0; + } else { + break; + } + } + } + } else if (group[i].f_tryShift) { + /* Group did not evaporate, so do the "grabby" algorithm. */ + if ((group[i].bit != 0) && (group[i - 1].bit >= group[i].bit)) { + if (f_secMiss) { + A_max = group[i].max; + A_min = group[i].min; + shiftGroup2 (Data, group[i].start - 1, group[i - 1].start, + li_primMiss, li_secMiss, group[i].bit, &A_min, + &A_max, &begin); + } else if (f_primMiss) { + A_max = group[i].max; + A_min = group[i].min; + shiftGroup1 (Data, group[i].start - 1, group[i - 1].start, + li_primMiss, group[i].bit, &A_min, &A_max, + &begin); + } else { + A_max = group[i].max; + A_min = group[i].min; + shiftGroup0 (Data, group[i].start - 1, group[i - 1].start, + group[i].bit, &A_min, &A_max, &begin); + } + if (begin != group[i].start) { + /* Re-Calculate min/max of group[i - 1], since it could have + * moved to group[i] */ + G1 = group[i - 1]; + G2 = group[i]; + G2.min = A_min; + G2.max = A_max; + G1.num -= (group[i].start - begin); + if (f_secMiss) { + findMaxMin2 (Data, G1.start, G1.start + G1.num, + li_primMiss, li_secMiss, &A_min, &A_max); + } else if (f_primMiss) { + findMaxMin1 (Data, G1.start, G1.start + G1.num, + li_primMiss, &A_min, &A_max); + } else { + findMaxMin0 (Data, G1.start, G1.start + G1.num, + &A_min, &A_max); + } + if ((A_min != G1.min) || (A_max != G1.max)) { + G1.min = A_min; + G1.max = A_max; + G1.bit = (char) power ((uInt4) (A_max - A_min), + f_secMiss + f_primMiss); + G1.f_trySplit = 1; + G1.f_tryShift = 1; + } + G2.num += (group[i].start - begin); + G2.start = begin; + G2.f_trySplit = 1; + G2.f_tryShift = 1; + scoreA = group[i].bit * group[i].num + xFactor; + scoreA += group[i - 1].bit * group[i - 1].num + xFactor; + scoreB = G2.bit * G2.num + xFactor; + if (G1.num != 0) { + scoreB += G1.bit * G1.num + xFactor; + } +#ifdef DEBUG + if (scoreB > scoreA) { + printf ("Made score worse!\n"); + } +#endif + if (begin == group[i - 1].start) { + /* Grabby algorithm evaporated a group. */ + evaporate++; + myAssert (G1.num == 0); + /* Switch the evaporating group to other side so we have + * potential to continue evaporating the next group down. */ + group[i - 1] = G2; + group[i] = G1; + } else { + group[i - 1] = G1; + group[i] = G2; + } + } else { + group[i].f_tryShift = 0; + } + } + } + } + /* Loop through the grid removing the evaporated groups. */ + if (evaporate != 0) { + index = 0; + for (i = 0; i < numGroup; i++) { + if (group[i].num != 0) { + group[index] = group[i]; + index++; + } + } + *NumGroup = numGroup - evaporate; + *Group = (TDLGroupType *) realloc ((void *) (*Group), + *NumGroup * sizeof (TDLGroupType)); + } +} + +/***************************************************************************** + * GroupIt() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Attempts to find groups for packing the data. It starts by preparing + * the data, by removing the overall min value. + * + * Next it Creates any 0 bit groups (primary missing: missings are all 0 + * bit groups, const values are 0 bit groups. No missing: const values are + * 0 bit groups.) + * + * Next it tries to reduce (split) each group by 1 bit. It does this by: + * A) reduce the "bit range", and create groups that grab as much as they + * can to the right. Then reduce those groups if they improve the score. + * B) reduce the bit range and grab the left most group only, leaving the + * rest unchanged. + * C) reduce the bit range and grab the right most group only, leaving the + * rest unchanged. + * + * Next it tries to shift / join those groups together. It does this by + * first calculating if a group should still exist. If it should, then it + * has each group grab as much as it can to the left without increasing its + * "bit range". + * + * ARGUMENTS + * OverallMin = The overall min value in the data. (Input) + * Data = The data. (Input) + * numData = The number of elements in data. (Input) + * group = The resulting groups. (Output) + * numGroup = Number of groups (Output) + * f_primMiss = Flag if we have a primary missing value (Input) + * li_primMiss = scaled primary missing value (Input) + * f_secMiss = Flag if we have a secondary missing value (Input) + * li_secMiss = scaled secondary missing value (Input) + * groupSize = How many bytes the groups and data will take. (Output) + * ibit = # of bits for largest minimum value in groups (Output) + * jbit = # of bits for largest # bits in groups (Output) + * kbit = # of bits for largest # values in groups (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 1/2005 Arthur Taylor (MDL): Created. + * + * NOTES + * 1) Have not implemented const 0 bit groups for prim miss or no miss. + * 2) MAX_GROUP_LEN (found experimentally) is useful to keep cost of groups + * down. + ***************************************************************************** + */ +#define MAX_GROUP_LEN 255 +static void GroupIt (sInt4 OverallMin, sInt4 *Data, size_t numData, + TDLGroupType ** group, size_t *numGroup, char f_primMiss, + sInt4 li_primMiss, char f_secMiss, + sInt4 li_secMiss, sInt4 *groupSize, size_t *ibit, + size_t *jbit, size_t *kbit) +{ + sInt4 A_max; /* Max value of a given group. */ + sInt4 A_min; /* Min value of a given group. */ + TDLGroupType G; /* Used to init the groups. */ + int f_adjust; /* Flag if we have changed the groups. */ + TDLGroupType *lclGroup; /* A temporary copy of the groups. */ + int numLclGroup; /* # of groups in lclGroup. */ + size_t xFactor; /* Estimate of cost (in bits) of a group. */ + size_t i; /* loop counter. */ + + /* Subtract the Overall Min Value. */ + if (OverallMin != 0) { + if (f_secMiss) { + for (i = 0; i < numData; i++) { + if ((Data[i] != li_secMiss) && (Data[i] != li_primMiss)) { + Data[i] -= OverallMin; + /* Check if we accidently adjusted to prim or sec, if so add + * 1. */ + if ((Data[i] == li_secMiss) || (Data[i] == li_primMiss)) { + myAssert (1 == 2); + Data[i]++; + if ((Data[i] == li_secMiss) || (Data[i] == li_primMiss)) { + myAssert (1 == 2); + Data[i]++; + } + } + } + } + } else if (f_primMiss) { + for (i = 0; i < numData; i++) { + if (Data[i] != li_primMiss) { + Data[i] -= OverallMin; + /* Check if we accidently adjusted to prim or sec, if so add + * 1. */ + if (Data[i] == li_primMiss) { + myAssert (1 == 2); + Data[i]++; + } + } + } + } else { + for (i = 0; i < numData; i++) { + Data[i] -= OverallMin; + } + } + } + + myAssert ((f_secMiss == 0) || (f_secMiss == 1)); + myAssert ((f_primMiss == 0) || (f_primMiss == 1)); + + /* Create zero groups. */ + *numGroup = 0; + *group = NULL; + if (f_primMiss) { + G.min = Data[0]; + G.max = Data[0]; + G.num = 1; + G.start = 0; + for (i = 1; i < numData; i++) { + if (G.min == li_primMiss) { + if (Data[i] == li_primMiss) { + G.num++; + if (G.num == (MAX_GROUP_LEN + 1)) { + /* Close a missing group */ + G.f_trySplit = 0; + G.f_tryShift = 1; + G.bit = 0; + G.min = 0; + G.max = 0; + G.num = MAX_GROUP_LEN; + (*numGroup)++; + *group = (TDLGroupType *) realloc ((void *) *group, + *numGroup * + sizeof (TDLGroupType)); + (*group)[(*numGroup) - 1] = G; + /* Init a missing group. */ + G.min = Data[i]; + G.max = Data[i]; + G.num = 1; + G.start = i; + } + } else { + /* Close a missing group */ + G.f_trySplit = 0; + G.f_tryShift = 1; + G.bit = 0; + G.min = 0; + G.max = 0; + (*numGroup)++; + *group = (TDLGroupType *) realloc ((void *) *group, + *numGroup * + sizeof (TDLGroupType)); + (*group)[(*numGroup) - 1] = G; + /* Init a non-missing group. */ + G.min = Data[i]; + G.max = Data[i]; + G.num = 1; + G.start = i; + } + } else { + if (Data[i] == li_primMiss) { + /* Close a non-missing group */ + G.f_trySplit = 1; + G.f_tryShift = 1; + G.bit = (char) power ((uInt4) (G.max - G.min), + f_secMiss + f_primMiss); + myAssert (G.bit != 0); + if ((G.min == 0) && (G.bit == 0) && (f_primMiss == 1)) { + printf ("Warning: potential confusion between const value " + "and prim-missing.\n"); + G.bit = 1; + } + (*numGroup)++; + *group = (TDLGroupType *) realloc ((void *) *group, + *numGroup * + sizeof (TDLGroupType)); + (*group)[(*numGroup) - 1] = G; + /* Init a missing group. */ + G.min = Data[i]; + G.max = Data[i]; + G.num = 1; + G.start = i; + } else { + if (G.min > Data[i]) { + G.min = Data[i]; + } else if (G.max < Data[i]) { + G.max = Data[i]; + } + G.num++; + } + } + } + if (G.min == li_primMiss) { + /* Close a missing group */ + G.f_trySplit = 0; + G.f_tryShift = 1; + G.bit = 0; + G.min = 0; + G.max = 0; + (*numGroup)++; + *group = (TDLGroupType *) realloc ((void *) *group, + *numGroup * + sizeof (TDLGroupType)); + (*group)[(*numGroup) - 1] = G; + } else { + /* Close a non-missing group */ + G.f_trySplit = 1; + G.f_tryShift = 1; + G.bit = (char) power ((uInt4) (G.max - G.min), + f_secMiss + f_primMiss); + myAssert (G.bit != 0); + if ((G.min == 0) && (G.bit == 0) && (f_primMiss == 1)) { + printf ("Warning: potential confusion between const value and " + "prim-missing.\n"); + G.bit = 1; + } + (*numGroup)++; + *group = (TDLGroupType *) realloc ((void *) *group, + *numGroup * + sizeof (TDLGroupType)); + (*group)[(*numGroup) - 1] = G; + } + } else { + /* Already handled the f_primMiss case */ + if (f_secMiss) { + findMaxMin2 (Data, 0, numData, li_primMiss, li_secMiss, &A_min, + &A_max); + } else { + findMaxMin0 (Data, 0, numData, &A_min, &A_max); + } + G.start = 0; + G.num = numData; + G.min = A_min; + G.max = A_max; + G.bit = (char) power ((uInt4) (A_max - A_min), f_secMiss + f_primMiss); + G.f_trySplit = 1; + G.f_tryShift = 1; + *numGroup = 1; + *group = (TDLGroupType *) malloc (sizeof (TDLGroupType)); + (*group)[0] = G; + } + + lclGroup = NULL; + numLclGroup = 0; + *groupSize = ComputeGroupSize (*group, *numGroup, ibit, jbit, kbit); + xFactor = *ibit + *jbit + *kbit; +#ifdef DEBUG +/* + printf ("NumGroup = %d: Bytes = %ld: XFactor %d\n", *numGroup, + (*groupSize / 8) + 1, xFactor); +*/ +#endif + + f_adjust = 1; + while (f_adjust) { + f_adjust = splitGroup (Data, numData, *group, *numGroup, &lclGroup, + &numLclGroup, f_primMiss, li_primMiss, + f_secMiss, li_secMiss, xFactor); + free (*group); + *group = lclGroup; + *numGroup = numLclGroup; + + if (f_adjust) { + shiftGroup (Data, numData, group, numGroup, f_primMiss, + li_primMiss, f_secMiss, li_secMiss, xFactor); + *groupSize = ComputeGroupSize (*group, *numGroup, ibit, jbit, kbit); + if (xFactor != *ibit + *jbit + *kbit) { + for (i = 0; i < *numGroup; i++) { + if (((*group)[i].num > *ibit + *jbit + *kbit) && + ((*group)[i].num <= xFactor)) { + (*group)[i].f_trySplit = 1; + } + } + } + xFactor = *ibit + *jbit + *kbit; +#ifdef DEBUG +/* + printf ("NumGroup = %d: Bytes = %ld: XFactor %d\n", *numGroup, + (*groupSize / 8) + 1, xFactor); + fflush (stdout); +*/ +#endif + } + } +} + +/***************************************************************************** + * GroupPack() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To compute groups for packing the data using complex or second order + * complex packing. + * + * ARGUMENTS + * Src = The original data. (Input) + * Dst = The scaled data. (Output) + * numData = The number of elements in data. (Input) + * DSF = Decimal Scale Factor for scaling the data. (Input) + * BSF = Binary Scale Factor for scaling the data. (Input) + * f_primMiss = Flag saying if we have a primary missing value (In/Out) + * primMiss = primary missing value. (In/Out) + * f_secMiss = Flag saying if we have a secondary missing value (In/Out) + * secMiss = secondary missing value. (In/Out) + * f_grid = Flag if this is grid data (or vector) (Input) + * NX = The number of X values. (Input) + * NY = The number of Y values. (Input) + * f_sndOrder = Flag if we should do second order diffencing (Output) + * group = Resulting groups. (Output) + * numGroup = Number of groups. (Output) + * Min = Overall minimum. (Output) + * a1 = if f_sndOrder, the first first order difference (Output) + * b2 = if f_sndOrder, the first second order difference (Output) + * groupSize = How many bytes the groups and data will take. (Output) + * ibit = # of bits for largest minimum value in groups (Output) + * jbit = # of bits for largest # bits in groups (Output) + * kbit = # of bits for largest # values in groups (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = Primary or Secondary missing value == 0. + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Updated from "group.c" in "C" tdlpack code. + * 1/2005 AAT: Cleaned up. + * + * NOTES + ***************************************************************************** + */ +static int GroupPack (double *Src, sInt4 **Dst, sInt4 numData, + int DSF, int BSF, char *f_primMiss, double *primMiss, + char *f_secMiss, double *secMiss, char f_grid, + short int NX, short int NY, char *f_sndOrder, + TDLGroupType ** group, size_t *numGroup, + sInt4 *Min, sInt4 *a1, sInt4 *b2, + sInt4 *groupSize, size_t *ibit, size_t *jbit, + size_t *kbit) +{ + sInt4 *SecDiff = NULL; /* Consists of the 2nd order differences if * + * requested. */ + sInt4 *Data; /* The scaled data. */ + char f_min; /* Flag saying overallMin is valid. */ + sInt4 overallMin = 0; /* The overall min of the scaled data. */ + sInt4 secMin; /* The overall min of the 2nd order differences */ + sInt4 li_primMiss = 0; /* The scaled primary missing value */ + sInt4 li_secMiss = 0; /* The scaled secondary missing value */ + int minGroup = 20; /* The minimum group size. Equivalent to xFactor? + * Chose 20 because that was a good estimate of + * XFactor. */ + + /* Check consistency of f_primMiss and f_secMiss. */ + if (*primMiss == *secMiss) { + *f_secMiss = 0; + } + if ((*f_secMiss) && (!(*f_primMiss))) { + *f_primMiss = *f_secMiss; + *primMiss = *secMiss; + *f_secMiss = 0; + } + if (*f_secMiss && (*secMiss == 0)) { + errSprintf ("Error: Secondary missing value not allowed to = 0.\n"); + return -1; + } + if (*f_primMiss && (*primMiss == 0)) { + errSprintf ("Error: Primary missing value not allowed to = 0.\n"); + return -1; + } + + /* Check minGroup size. */ + if (minGroup > numData) { + minGroup = numData; + } + + /* Scale the data and check if we can change f_prim or f_sec. */ + /* Note: if we use sec_diff, we have a different overall min. */ + f_min = 0; + Data = (sInt4 *) malloc (numData * sizeof (sInt4)); + TDL_ScaleData (Src, Data, numData, DSF, BSF, f_primMiss, primMiss, + f_secMiss, secMiss, &f_min, &overallMin); + /* Note: ScaleData also scales missing values. */ + if (*f_primMiss) { + li_primMiss = (sInt4) (*primMiss * SCALE_MISSING + .5); + } + if (*f_secMiss) { + li_secMiss = (sInt4) (*secMiss * SCALE_MISSING + .5); + } + + /* Reason this is after TDL_ScaleData is we don't want to reoder the + * caller's copy of the data. */ + if (f_grid) { + TDL_ReorderGrid (Data, NX, NY); + } else { + /* TDLPack requires the following (see pack2d.f and pack1d.f) */ + *f_sndOrder = 0; + } + /* TDLPack requires the following (see pack2d.f) */ + if (*f_secMiss) { + *f_sndOrder = 0; + } + /* If overallMin is "invalid" then they are all prim or sec values. */ + /* See pack2d.f line 336 */ + /* IF ALL VALUES ARE MISSING, JUST PACK; DON'T CONSIDER 2ND ORDER + * DIFFERENCES. */ + if (!f_min) { + *f_sndOrder = 0; + } + + /* This has to be after TDL_ReorderGrid */ + if (*f_sndOrder) { + SecDiff = (sInt4 *) malloc (numData * sizeof (sInt4)); + if (TDL_GetSecDiff (Data, numData, SecDiff, *f_primMiss, li_primMiss, + a1, b2, &secMin)) { + /* Problem finding SecDiff, so we don't bother with it. */ + *f_sndOrder = 0; + } else { + /* Check if it is worth doing second order differences. */ + if (*f_primMiss) { + *f_sndOrder = TDL_UseSecDiff_Prim (Data, numData, SecDiff, + li_primMiss, minGroup); + } else { + *f_sndOrder = TDL_UseSecDiff (Data, numData, SecDiff, minGroup); + } + } + } + + /* Side affect of GroupIt2: it subtracts OverallMin from Data. */ + if (!(*f_sndOrder)) { + GroupIt (overallMin, Data, numData, group, numGroup, *f_primMiss, + li_primMiss, *f_secMiss, li_secMiss, groupSize, ibit, jbit, + kbit); + *Min = overallMin; + *a1 = 0; + *b2 = 0; + *Dst = Data; + free (SecDiff); + } else { + GroupIt (secMin, SecDiff, numData, group, numGroup, *f_primMiss, + li_primMiss, *f_secMiss, li_secMiss, groupSize, ibit, jbit, + kbit); + *Min = secMin; + *Dst = SecDiff; + free (Data); + } + return 0; +} + +/***************************************************************************** + * WriteTDLPRecord() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Writes a TDLP message to file. + * + * ARGUMENTS + * fp = An opened TDLP file already at the correct location. (Input) + * Data = The data to write. (Input) + * DataLen = Length of Data. (Input) + * DSF = Decimal scale factor to apply to the data (Input) + * BSF = Binary scale factor to apply to the data (Input) + * f_primMiss = Flag saying if we have a primary missing value (Input) + * primMiss = primary missing value. (Input) + * f_secMiss = Flag saying if we have a secondary missing value (Input) + * secMiss = secondary missing value. (Input) + * gds = The grid definition section (Input) + * comment = Describes the kind of data (max 32 bytes). (Input) + * refTime = The reference (creation) time of this message. (Input) + * ID1 = TDLPack ID1 (Input) + * ID2 = TDLPack ID2 (Input) + * ID3 = TDLPack ID3 (Input) + * ID4 = TDLPack ID4 (Input) + * projSec = The projection in seconds (Input) + * processNum = The process number that created it (Input) + * seqNum = The sequence number that created it (Input) + * + * FILES/DATABASES: + * An already opened file pointing to the desired TDLP message. + * + * RETURNS: int (could use errSprintf()) + * 0 = OK + * -1 = comment is too long. + * -2 = projHr is inconsistent with ID3. + * -3 = Type of map projection that TDLP can't handle. + * -4 = Primary or Secondary missing value == 0. + * + * HISTORY + * 12/2004 Arthur Taylor (MDL): Created + * 1/2005 AAT: Cleaned up. + * + * NOTES + ***************************************************************************** + */ +int WriteTDLPRecord (FILE * fp, double *Data, sInt4 DataLen, int DSF, + int BSF, char f_primMiss, double primMiss, + char f_secMiss, double secMiss, gdsType *gds, + char *comment, double refTime, sInt4 ID1, + sInt4 ID2, sInt4 ID3, sInt4 ID4, + sInt4 projSec, sInt4 processNum, sInt4 seqNum) +{ + sInt4 *Scaled; /* The scaled data. */ + TDLGroupType *group; /* The groups used to pack the data. */ + size_t numGroup; /* Number of groups. */ + char f_grid = 1; /* Flag if this is gridded data. In theory can handle + * vector data, but haven't tested it. */ + char f_sndOrder; /* Flag if we should try second order packing. */ + + /* TODO: Trace overallMin to figure out how it could be used uninitialized */ + sInt4 overallMin; /* Overall min value of the scaled data. */ + + sInt4 a1; /* 2nd order difference: 1st value. */ + sInt4 b2; /* 2nd order difference: 1st 1st order difference */ + sInt4 li_primMiss; /* Scaled primary missing value. */ + sInt4 li_secMiss; /* Scaled secondary missing value. */ + int mbit; /* # of bits for b2. */ + int nbit; /* # of bits for overallMin. */ + size_t ibit; /* # of bits for largest minimum value in groups */ + size_t jbit; /* # of bits for largest # bits in groups */ + size_t kbit; /* # of bits for largest # values in groups */ + int sec1Len; /* Length of section 1. */ + size_t pad; /* Number of bytes to pad the message to get to the + * correct byte boundary. */ + sInt4 groupSize; /* How many bytes the groups and data will take. */ + sInt4 sec4Len; /* Length of section 4. */ + sInt4 tdlRecSize; /* Size of the TDLP message. */ + sInt4 recSize; /* Actual record size (including FORTRAN bytes). */ + int commentLen; /* Length of comment */ + short int projHr; /* The hours part of the forecast projection. */ + char projMin; /* The minutes part of the forecast projection. */ + sInt4 year; /* The reference year. */ + int month, day; /* The reference month day. */ + int hour, min; /* The reference hour minute. */ + double sec; /* The reference second. */ + char f_bitmap = 0; /* Bitmap flag: not implemented in specs. */ + char f_simple = 0; /* Simple Pack flag: not implemented in specs. */ + int gridType; /* Which type of grid. (Polar, Mercator, Lambert). */ + int dataCnt; /* Keeps track of which element we are writing. */ + sInt4 max0; /* The max value in a group. Represents primary or * + * secondary missing value depending on scheme. */ + sInt4 max1; /* The next to max value in a group. Represents * + * secondary missing value. */ + size_t i, j; /* loop counters */ + sInt4 li_temp; /* Temporary variable (sInt4). */ + short int si_temp; /* Temporary variable (short int). */ + double d_temp; /* Temporary variable (double). */ + char buffer[6]; /* Used to write reserved values */ + uChar pbuf; /* A buffer of bits that weren't written to disk */ + sChar pbufLoc; /* Where in pbuf to add more bits. */ + + commentLen = strlen (comment); + if (commentLen > 32) { + errSprintf ("Error: '%s' is > 32 bytes long\n", comment); + return -1; + } + projHr = projSec / 3600; + projMin = (projSec % 3600) / 60; + if (projHr != (ID3 - ((ID3 / 1000) * 1000))) { + errSprintf ("Error: projHr = %d is inconsistent with ID3 = %ld\n", + projHr, ID3); + return -2; + } + if (f_grid) { + switch (gds->projType) { + case GS3_POLAR: + gridType = TDLP_POLAR; + break; + case GS3_LAMBERT: + gridType = TDLP_LAMBERT; + break; + case GS3_MERCATOR: + gridType = TDLP_MERCATOR; + break; + default: + errSprintf ("TDLPack can't handle GRIB projection type %d\n", + gds->projType); + return -3; + } + } + + if (GroupPack (Data, &Scaled, DataLen, DSF, BSF, &f_primMiss, &primMiss, + &f_secMiss, &secMiss, f_grid, gds->Nx, gds->Ny, + &f_sndOrder, &group, &numGroup, &overallMin, &a1, &b2, + &groupSize, &ibit, &jbit, &kbit) != 0) { + return -4; + } + + /* Make sure missing data is properly scaled. */ + if (f_primMiss) { + li_primMiss = (sInt4) (primMiss * SCALE_MISSING + .5); + } + if (f_secMiss) { + li_secMiss = (sInt4) (secMiss * SCALE_MISSING + .5); + } + + /* Compute TDL record size. */ +/* *INDENT-OFF* */ + /* TDL Record size + * 8 (section 0), + * 39 + strlen(comment) (section 1), + * 0 or 28 (depending on if you have a section 2), + * 0 (section 3), + * 16 + 5 + 1 + nbit(min val) + 16 + 5 + 5 + 5 + GroupSize() (section 4) + * 4 (section 5) + * pad (to even 8 bytes...) + * Group size uses: + * ibit * num_groups + jbit * num_groups + + * kbit * num_groups + group.nbit * group.nvalues */ +/* *INDENT-ON* */ + sec1Len = 39 + commentLen; + if (overallMin < 0) { + nbit = power (-1 * overallMin, 0); + } else { + nbit = power (overallMin, 0); + } + if (!f_sndOrder) { + if (f_secMiss) { + sec4Len = 16 + (sInt4) (ceil ((5 + 1 + nbit + 16 + 5 + 5 + 5 + + groupSize) / 8.)); + } else if (f_primMiss) { + sec4Len = 12 + (sInt4) (ceil ((5 + 1 + nbit + 16 + 5 + 5 + 5 + + groupSize) / 8.)); + } else { + sec4Len = 8 + (sInt4) (ceil ((5 + 1 + nbit + 16 + 5 + 5 + 5 + + groupSize) / 8.)); + } + } else { + if (b2 < 0) { + mbit = power (-1 * b2, 0); + } else { + mbit = power (b2, 0); + } + if (f_secMiss) { + sec4Len = + 16 + + (sInt4) (ceil + ((32 + 5 + 1 + mbit + 5 + 1 + nbit + 16 + 5 + 5 + 5 + + groupSize) / 8.)); + } else if (f_primMiss) { + sec4Len = + 12 + + (sInt4) (ceil + ((32 + 5 + 1 + mbit + 5 + 1 + nbit + 16 + 5 + 5 + 5 + + groupSize) / 8.)); + } else { + sec4Len = + 8 + + (sInt4) (ceil + ((32 + 5 + 1 + mbit + 5 + 1 + nbit + 16 + 5 + 5 + 5 + + groupSize) / 8.)); + } + } + if (f_grid) { + tdlRecSize = 8 + sec1Len + 28 + 0 + sec4Len + 4; + } else { + tdlRecSize = 8 + sec1Len + 0 + 0 + sec4Len + 4; + } + /* Actual recSize is 8 + round(tdlRecSize) to nearest 8 byte boundary. */ + recSize = 8 + (sInt4) (ceil (tdlRecSize / 8.0)) * 8; + pad = (int) ((recSize - 8) - tdlRecSize); + + /* --- Start Writing record. --- */ + /* First write FORTRAN record information */ + const size_t read_size = fread(&(recSize), sizeof (sInt4), 1, fp); + if (read_size != 1) { + fprintf(stderr, "WARNING: tdlpack.cpp read of recSize failed.\n"); + } + li_temp = 0; + FWRITE_BIG (&(li_temp), sizeof (sInt4), 1, fp); + li_temp = recSize - 8; /* FORTRAN rec length. */ + FWRITE_BIG (&(li_temp), sizeof (sInt4), 1, fp); + + /* Now write section 0. */ + fwrite ("TDLP", sizeof (char), 4, fp); + FWRITE_ODDINT_BIG (&(tdlRecSize), 3, fp); + /* version number */ + fputc (0, fp); + + /* Now write section 1. */ + fputc (sec1Len, fp); + /* output type specification... */ + i = 0; + if (f_grid) { + i |= 1; + } + if (f_bitmap) { + i |= 2; + } + fputc (i, fp); +/* tempTime = gmtime (&(refTime));*/ + Clock_PrintDate (refTime, &year, &month, &day, &hour, &min, &sec); +/* year = tempTime->tm_year + 1900; + month = tempTime->tm_mon + 1; + day = tempTime->tm_mday; + hour = tempTime->tm_hour; + min = tempTime->tm_min; +*/ + si_temp = year; + FWRITE_BIG (&si_temp, sizeof (short int), 1, fp); + fputc (month, fp); + fputc (day, fp); + fputc (hour, fp); + fputc (min, fp); + li_temp = (year * 1000000L + month * 10000L + day * 100 + hour); + FWRITE_BIG (&li_temp, sizeof (sInt4), 1, fp); + FWRITE_BIG (&ID1, sizeof (sInt4), 1, fp); + FWRITE_BIG (&ID2, sizeof (sInt4), 1, fp); + FWRITE_BIG (&ID3, sizeof (sInt4), 1, fp); + FWRITE_BIG (&ID4, sizeof (sInt4), 1, fp); + FWRITE_BIG (&projHr, sizeof (short int), 1, fp); + fputc (projMin, fp); + fputc (processNum, fp); + fputc (seqNum, fp); + i = (DSF < 0) ? 128 - DSF : DSF; + fputc (i, fp); + i = (BSF < 0) ? 128 - BSF : BSF; + fputc (i, fp); + /* Reserved: 3 bytes of 0. */ + li_temp = 0; + fwrite (&li_temp, sizeof (char), 3, fp); + fputc (commentLen, fp); + fwrite (comment, sizeof (char), commentLen, fp); + + /* Now write section 2. */ + if (f_grid) { + fputc (28, fp); + fputc (gridType, fp); + si_temp = gds->Nx; + FWRITE_BIG (&si_temp, sizeof (short int), 1, fp); + si_temp = gds->Ny; + FWRITE_BIG (&si_temp, sizeof (short int), 1, fp); + li_temp = (sInt4) (gds->lat1 * 10000. + .5); + if (li_temp < 0) { + pbuf = 128; + li_temp = -1 * li_temp; + } else { + pbuf = 0; + } + pbufLoc = 7; + fileBitWrite (&(li_temp), sizeof (li_temp), 23, fp, &pbuf, &pbufLoc); + myAssert (pbufLoc == 8); + myAssert (pbuf == 0); + d_temp = 360 - gds->lon1; + if (d_temp < 0) + d_temp += 360; + if (d_temp > 360) + d_temp -= 360; + li_temp = (sInt4) (d_temp * 10000. + .5); + if (li_temp < 0) { + pbuf = 128; + li_temp = -1 * li_temp; + } + pbufLoc = 7; + fileBitWrite (&(li_temp), sizeof (li_temp), 23, fp, &pbuf, &pbufLoc); + myAssert (pbufLoc == 8); + myAssert (pbuf == 0); + d_temp = 360 - gds->orientLon; + if (d_temp < 0) + d_temp += 360; + if (d_temp > 360) + d_temp -= 360; + li_temp = (sInt4) (d_temp * 10000. + .5); + if (li_temp < 0) { + pbuf = 128; + li_temp = -1 * li_temp; + } + pbufLoc = 7; + fileBitWrite (&(li_temp), sizeof (li_temp), 23, fp, &pbuf, &pbufLoc); + myAssert (pbufLoc == 8); + myAssert (pbuf == 0); + li_temp = (sInt4) (gds->Dx * 1000. + .5); + FWRITE_BIG (&li_temp, sizeof (sInt4), 1, fp); + li_temp = (sInt4) (gds->meshLat * 10000. + .5); + if (li_temp < 0) { + pbuf = 128; + li_temp = -1 * li_temp; + } + pbufLoc = 7; + fileBitWrite (&(li_temp), sizeof (li_temp), 23, fp, &pbuf, &pbufLoc); + myAssert (pbufLoc == 8); + myAssert (pbuf == 0); + memset (buffer, 0, 6); + fwrite (buffer, sizeof (char), 6, fp); + } + + /* Now write section 3. */ + /* Bitmap is not supported, skipping. */ + myAssert (!f_bitmap); + + /* Now write section 4. */ + FWRITE_ODDINT_BIG (&(sec4Len), 3, fp); + i = 0; + if (f_secMiss) + i |= 1; + if (f_primMiss) + i |= 2; + if (f_sndOrder) + i |= 4; + if (!f_simple) + i |= 8; + if (!f_grid) + i |= 16; + fputc (i, fp); + li_temp = DataLen; + FWRITE_BIG (&li_temp, sizeof (sInt4), 1, fp); + if (f_primMiss) { + FWRITE_BIG (&(li_primMiss), sizeof (sInt4), 1, fp); + if (f_secMiss) { + FWRITE_BIG (&(li_secMiss), sizeof (sInt4), 1, fp); + } + } + if (f_sndOrder) { + if (a1 < 0) { + pbuf = 128; + li_temp = -1 * a1; + } else { + pbuf = 0; + li_temp = a1; + } + pbufLoc = 7; + fileBitWrite (&(li_temp), sizeof (li_temp), 31, fp, &pbuf, &pbufLoc); + myAssert (pbufLoc == 8); + myAssert (pbuf == 0); + fileBitWrite (&mbit, sizeof (mbit), 5, fp, &pbuf, &pbufLoc); + if (b2 < 0) { + i = 1; + li_temp = -1 * b2; + } else { + i = 0; + li_temp = b2; + } + fileBitWrite (&i, sizeof (i), 1, fp, &pbuf, &pbufLoc); + myAssert (pbufLoc == 2); + fileBitWrite (&li_temp, sizeof (li_temp), (unsigned short int) mbit, + fp, &pbuf, &pbufLoc); + } + fileBitWrite (&nbit, sizeof (nbit), 5, fp, &pbuf, &pbufLoc); + if (overallMin < 0) { + i = 1; + li_temp = -1 * overallMin; + } else { + i = 0; + li_temp = overallMin; + } + fileBitWrite (&i, sizeof (i), 1, fp, &pbuf, &pbufLoc); + fileBitWrite (&li_temp, sizeof (li_temp), (unsigned short int) nbit, fp, + &pbuf, &pbufLoc); + fileBitWrite (&numGroup, sizeof (numGroup), 16, fp, &pbuf, &pbufLoc); + fileBitWrite (&ibit, sizeof (ibit), 5, fp, &pbuf, &pbufLoc); + fileBitWrite (&jbit, sizeof (jbit), 5, fp, &pbuf, &pbufLoc); + fileBitWrite (&kbit, sizeof (kbit), 5, fp, &pbuf, &pbufLoc); + for (i = 0; i < numGroup; i++) { + fileBitWrite (&(group[i].min), sizeof (sInt4), + (unsigned short int) ibit, fp, &pbuf, &pbufLoc); + } + for (i = 0; i < numGroup; i++) { + fileBitWrite (&(group[i].bit), sizeof (char), + (unsigned short int) jbit, fp, &pbuf, &pbufLoc); + } +#ifdef DEBUG + li_temp = 0; +#endif + for (i = 0; i < numGroup; i++) { + fileBitWrite (&(group[i].num), sizeof (sInt4), + (unsigned short int) kbit, fp, &pbuf, &pbufLoc); +#ifdef DEBUG + li_temp += group[i].num; +#endif + } +#ifdef DEBUG + /* Sanity check ! */ + if (li_temp != DataLen) { + printf ("Total packed in groups %d != DataLen %d\n", li_temp, + DataLen); + } + myAssert (li_temp == DataLen); +#endif + + dataCnt = 0; + /* Start going through the data. Grid data has already been reordered. + * Already taken care of 2nd order differences. Data at this point should + * contain a stream of either 2nd order differences or 0 order + * differences. We have also removed overallMin from all non-missing + * elements in the stream */ + if (f_secMiss) { + for (i = 0; i < numGroup; i++) { + max0 = (1 << group[i].bit) - 1; + max1 = (1 << group[i].bit) - 2; + for (j = 0; j < group[i].num; j++) { + if (Scaled[dataCnt] == li_primMiss) { + li_temp = max0; + } else if (Scaled[dataCnt] == li_secMiss) { + li_temp = max1; + } else { + li_temp = Scaled[dataCnt] - group[i].min; + } + fileBitWrite (&(li_temp), sizeof (sInt4), group[i].bit, fp, + &pbuf, &pbufLoc); + dataCnt++; + } + } + } else if (f_primMiss) { + for (i = 0; i < numGroup; i++) { + /* see what happens when bit == 0. */ + max0 = (1 << group[i].bit) - 1; + for (j = 0; j < group[i].num; j++) { + if (group[i].bit != 0) { + if (Scaled[dataCnt] == li_primMiss) { + li_temp = max0; + } else { + li_temp = Scaled[dataCnt] - group[i].min; + } + fileBitWrite (&(li_temp), sizeof (sInt4), group[i].bit, fp, + &pbuf, &pbufLoc); + } + dataCnt++; + } + } + } else { + for (i = 0; i < numGroup; i++) { + for (j = 0; j < group[i].num; j++) { + li_temp = Scaled[dataCnt] - group[i].min; + if (group[i].bit != 0) { + fileBitWrite (&(li_temp), sizeof (sInt4), group[i].bit, fp, + &pbuf, &pbufLoc); + } + dataCnt++; + } + } + } + myAssert (dataCnt == DataLen); + /* flush the PutBit buffer... */ + if (pbufLoc != 8) { + fputc ((int) pbuf, fp); + } + + /* Now write section 5. */ + fwrite ("7777", sizeof (char), 4, fp); + + /* Deal with padding at end of record... */ + for (i = 0; i < pad; i++) { + fputc (0, fp); + } + /* Now write FORTRAN record information */ + FWRITE_BIG (&(recSize), sizeof (sInt4), 1, fp); + + free (Scaled); + free (group); + return 0; +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/tdlpack.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/tdlpack.h new file mode 100644 index 000000000..fa8994b9b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/tdlpack.h @@ -0,0 +1,35 @@ +#ifndef TDLPACK_H +#define TDLPACK_H + +#include +#include "inventory.h" +#include "degrib2.h" + +#ifndef GRIB2BIT_ENUM +#define GRIB2BIT_ENUM +/* See rule (8) bit 1 is most significant, bit 8 least significant. */ +enum {GRIB2BIT_1=128, GRIB2BIT_2=64, GRIB2BIT_3=32, GRIB2BIT_4=16, + GRIB2BIT_5=8, GRIB2BIT_6=4, GRIB2BIT_7=2, GRIB2BIT_8=1}; +#endif + +typedef struct { + int index; + const char *data; +} TDLP_TableType; + + +int TDLP_Inventory (DataSource &fp, sInt4 tdlpLen, inventoryType * inv); +int TDLP_RefTime (DataSource &fp, sInt4 tdlpLen, double * refTime); +int ReadTDLPRecord (DataSource &fp, double **TDLP_Data, uInt4 *tdlp_DataLen, + grib_MetaData * meta, IS_dataType * IS, + sInt4 sect0[SECT0LEN_WORD], uInt4 tdlpLen, + double majEarth, double minEarth); +void PrintPDS_TDLP (pdsTDLPType * pds); +int WriteTDLPRecord (FILE * fp, double *Data, sInt4 DataLen, + int DSF, int BSF, char f_primMiss, double primMiss, + char f_secMiss, double secMiss, gdsType *gds, + char *comment, double refTime, sInt4 ID1, + sInt4 ID2, sInt4 ID3, sInt4 ID4, + sInt4 projSec, sInt4 processNum, sInt4 seqNum); + +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/type.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/type.h new file mode 100644 index 000000000..fe0916055 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/type.h @@ -0,0 +1,51 @@ +/***************************************************************************** + * type.h + * + * DESCRIPTION + * This file contains some simple common types used by this project. + * + * HISTORY + * 12/2002 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +#ifndef TYPE_H +#define TYPE_H + +#ifndef SINT4_TYPE + #define SINT4_TYPE +/* #ifdef _64Bit + * typedef of sInt4 and uInt4 determination now moved to config.h + * A D T Aug 26, 2006 + * Moved back to here with assumption that SIZEOF_LONG_INT is set + * by makefile. + * AATaylor 9/20/2006 + */ + +#if SIZEOF_LONG_INT != 4 + typedef signed int sInt4; + typedef unsigned int uInt4; + #else + typedef signed long int sInt4; + typedef unsigned long int uInt4; + #endif + typedef unsigned char uChar; + typedef signed char sChar; + typedef unsigned short int uShort2; + typedef signed short int sShort2; + /* Use size_t for unsigned int when you don't care the size (>= 2) */ + /* Use char (0, 1) for boolean */ +#endif + +/* #define LATLON_DECIMALS 6 */ +typedef struct { + double lat, lon; +} LatLon; + +typedef struct { + uChar f_valid; + double X, Y; +} Point; + +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/weather.c b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/weather.c new file mode 100644 index 000000000..7d9c93c0c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/weather.c @@ -0,0 +1,2639 @@ +/***************************************************************************** + * weather.c + * + * DESCRIPTION + * This file contains all the utility functions needed to handle weather + * "ugly" strings. Originally I didn't need to parse them, but for people + * to use them in ArcView, I had to. + * + * HISTORY + * 5/2003 Arthur Taylor (MDL / RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +/* + * Uncomment the following to have error messages stored in the UglyStringType + * This uses myerror.* + */ +#define STORE_ERRORS + +/* + * Uncomment the following to have error messages sent to stdout. + */ +/* #define VERBOSE */ +#undef VERBOSE + +/* + * Uncomment the following to test Weather names. + */ +/* #define DEBUG_WEATHER */ + + +#include +#include +#include +#include "weather.h" + +#ifdef STORE_ERRORS +#include "myerror.h" +#endif + +typedef struct { + char *abrev, *name; + uChar number; +} WxTable; + +/* Unknown :EW:::10to20g30 */ +/* Original enumeration. +enum { + WX_NOWX, WX_K, WX_BD, WX_BS, WX_H, WX_F, WX_L, WX_R, WX_RW, + WX_A, WX_FR, WX_ZL, WX_ZR, WX_IP, WX_S, WX_SW, WX_T +}; +*/ +enum { + WX_NOWX, WX_K, WX_BD, WX_BS, WX_H, WX_F, WX_L, WX_R, WX_RW, + WX_A, WX_FR, WX_ZL, WX_ZR, WX_IP, WX_S, WX_SW, WX_T, WX_BN, + WX_ZF, WX_IC, WX_IF, WX_VA, WX_ZY, WX_WP, WX_UNKNOWN +}; + +/* SA -> Snowfall aob freezing */ +/* LC -> Caution Advised on area Lakes */ +/* {"WG", "Frequent Gusts", WX_WG},*/ +WxTable WxCode[] = { + /* 0 */ {"", "No Weather", WX_NOWX}, + /* Dry Obstruction to visibility. */ + /* 14 */ {"K", "Smoke", WX_K}, + /* 15 */ {"BD", "Blowing Dust", WX_BD}, + /* 13 */ {"BS", "Blowing Snow", WX_BS}, + /* Moist Obstruction to visibility. */ + /* 12 */ {"H", "Haze", WX_H}, + /* 11 */ {"F", "Fog", WX_F}, + /* 5 */ {"L", "Drizzle", WX_L}, + /* Warm moisture. */ + /* 3 */ {"R", "Rain", WX_R}, + /* 4 */ {"RW", "Rain Showers", WX_RW}, +/* 'A' has have been dropped as of 8/12/2004 */ + /* 2 */ {"A", "Hail", WX_A}, +/* 'A' has have been dropped as of 8/12/2004 */ + /* Freezing / Mix moisture. */ + /* 16 */ {"FR", "Frost", WX_FR}, + /* 7 */ {"ZL", "Freezing Drizzle", WX_ZL}, + /* 6 */ {"ZR", "Freezing Rain", WX_ZR}, + /* Frozen moisture. */ + /* 10 */ {"IP", "Ice Pellets (sleet)", WX_IP}, + /* 8 */ {"S", "Snow", WX_S}, + /* 9 */ {"SW", "Snow Showers", WX_SW}, + /* Extra. */ + /* 1 */ {"T", "Thunderstorms", WX_T}, + {"BN", "Blowing Sand", WX_BN}, + {"ZF", "Freezing Fog", WX_ZF}, + {"IC", "Ice Crystals", WX_IC}, + {"IF", "Ice Fog", WX_IF}, + {"VA", "Volcanic Ash", WX_VA}, + {"ZY", "Freezing Spray", WX_ZY}, + {"WP", "Water Spouts", WX_WP}, + {"", "Unknown Weather", WX_UNKNOWN} +}; + +/* GChc found in output streams... not allowed to add yet. */ +/* Original enumeration. +enum { + COV_NOCOV, COV_ISO, COV_SCT, COV_NUM, COV_WIDE, COV_OCNL, COV_SCHC, + COV_CHC, COV_LKLY, COV_DEF, COV_PATCHY, COV_AREAS +}; +*/ +enum { + COV_NOCOV, COV_ISO, COV_SCT, COV_NUM, COV_WIDE, COV_OCNL, COV_SCHC, + COV_CHC, COV_LKLY, COV_DEF, COV_PATCHY, COV_AREAS, COV_PDS, COV_FRQ, + COV_INTER, COV_BRIEF, COV_UNKNOWN +}; + +WxTable WxCover[] = { + /* 0 */ {"", "No Coverage/Probability", COV_NOCOV}, + /* 1 */ {"Iso", "Isolated", COV_ISO}, + /* 2 */ {"Sct", "Scattered", COV_SCT}, + /* 3 */ {"Num", "Numerous", COV_NUM}, + /* 4 */ {"Wide", "Widespread", COV_WIDE}, + /* 5 */ {"Ocnl", "Occasional", COV_OCNL}, + /* 6 */ {"SChc", "Slight Chance of", COV_SCHC}, + /* 7 */ {"Chc", "Chance of", COV_CHC}, + /* 8 */ {"Lkly", "Likely", COV_LKLY}, + /* 9 */ {"Def", "Definite", COV_DEF}, + /* 10 */ {"Patchy", "Patchy", COV_PATCHY}, + /* 11 */ {"Areas", "Areas of", COV_AREAS}, +/* Added 8/13/2004 */ + /* 12 */ {"Pds", "Periods of", COV_PDS}, + /* 13 */ {"Frq", "Frequent", COV_FRQ}, + /* 14 */ {"Inter", "Intermittent", COV_INTER}, + /* 15 */ {"Brf", "Brief", COV_BRIEF}, +/* Finished Added 8/13/2004 */ + {"", "Unknown Coverage", COV_UNKNOWN} +}; + +enum { INT_NOINT, INT_DD, INT_D, INT_M, INT_P, INT_UNKNOWN }; + +WxTable WxIntens[] = { + /* 0 */ {"", "No Intensity", INT_NOINT}, + /* 1 */ {"--", "Very Light", INT_DD}, + /* 2 */ {"-", "Light", INT_D}, + /* 3 */ {"m", "Moderate", INT_M}, + /* 4 */ {"+", "Heavy", INT_P}, + {"", "Unknown Intensity", INT_UNKNOWN} +}; + +enum { + VIS_NOVIS, VIS_0, VIS_8, VIS_16, VIS_24, VIS_32, VIS_48, VIS_64, VIS_80, + VIS_96, VIS_128, VIS_160, VIS_192, VIS_224, VIS_UNKNOWN = 255 +}; + +WxTable WxVisib[] = { + /* 0 */ {"", "255", VIS_NOVIS}, + /* 1 */ {"0SM", "0", VIS_0}, + /* 2 */ {"1/4SM", "8", VIS_8}, + /* 3 */ {"1/2SM", "16", VIS_16}, + /* 4 */ {"3/4SM", "24", VIS_24}, + /* 5 */ {"1SM", "32", VIS_32}, + /* 6 */ {"11/2SM", "48", VIS_48}, + /* 7 */ {"2SM", "64", VIS_64}, + /* 8 */ {"21/2SM", "80", VIS_80}, + /* 9 */ {"3SM", "96", VIS_96}, + /* 10 */ {"4SM", "128", VIS_128}, + /* 11 */ {"5SM", "160", VIS_160}, + /* 12 */ {"6SM", "192", VIS_192}, + /* Past 6 SM (encode as 7 SM). */ + /* 13 */ {"P6SM", "224", VIS_224}, + {"", "Unknown Visibility", VIS_UNKNOWN} +}; + +enum { + HAZ_NOHAZ, HAZ_FL, HAZ_GW, HAZ_HVYRN, HAZ_DMGW, HAZ_A, HAZ_LGA, HAZ_OLA, + HAZ_OBO, HAZ_OGA, HAZ_DRY, HAZ_TOR, HAZ_UNKNOWN, HAZ_PRI1 = 253, + HAZ_PRI2 = 254, HAZ_OR = 255 +}; + +/* Note: HazCode currently can handle upto (21 + 4) different WxAttrib + * numbers because it is stored in a "sInt4" (2^31 = 21,47,48,36,48) */ +WxTable WxAttrib[] = { + /* 0 */ {"", "None", HAZ_NOHAZ}, + /* 1 */ {"FL", "Frequent Lightning", HAZ_FL}, + /* 2 */ {"GW", "Gusty Winds", HAZ_GW}, + /* 3 */ {"HvyRn", "Heavy Rain", HAZ_HVYRN}, + /* 4 */ {"DmgW", "Damaging Wind", HAZ_DMGW}, + /* 5 */ {"SmA", "Small Hail", HAZ_A}, + /* 6 */ {"LgA", "Large Hail", HAZ_LGA}, + /* 7 */ {"OLA", "Outlying Areas", HAZ_OLA}, + /* 8 */ {"OBO", "on Bridges and Overpasses", HAZ_OBO}, +/* Added 8/13/2004 */ + /* 9 */ {"OGA", "On Grassy Areas", HAZ_OGA}, + /* 10 */ {"Dry", "dry", HAZ_DRY}, + /* 11 */ {"TOR", "Tornado", HAZ_TOR}, + /* 12 */ {"Primary", "Highest Ranking", HAZ_PRI2}, + /* 13 */ {"Mention", "Include Unconditionally", HAZ_PRI1}, +/* Finished Added 8/13/2004 */ + /* 14 */ {"OR", "or", HAZ_OR}, + /* 15 */ {"MX", "mixture", HAZ_OR}, + {"", "Unknown Hazard", HAZ_UNKNOWN} +}; + +/***************************************************************************** + * NDFD_WxTable1() -- + * + * Original: makeWxImageCodes() Marc Saccucci (MDL) + * Adapted to NDFD_WxTable() Arthur Taylor / MDL + * + * PURPOSE + * To use the same weather table scheme used by Marc Saccucci in + * makeWxImageCodes() in the NDFD source tree. The purpose of both + * procedures is to simplify the weather string (aka ugly string) to a single + * integral code number, which contains the most releavent weather. The + * intent is to create a simpler field which can more readily be viewed as + * an image. + * + * ARGUMENTS + * ugly = The ugly weather string to encode. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (the encoded number.) + * + * HISTORY + * 11/2002 Marc Saccucci (MDL): Created matching algorithm in + * makeWxImageCodes(). + * 6/2003 MS: Altered matching combinations in makeWxImageCodes(). + * 7/2003 Arthur Taylor (MDL/RSIS): Created NDFD_WxTable() + * + * NOTES + * 1) The table used: + * new_code primary weather/probability Description (sample value) + * ======== =========================== ========================== + * 0 No weather + * 1 L/Sct,SChc,Patchy,Iso,Chc Rain (LoProb L) + * 2 R-/Sct,SChc,Patchy,Iso,Chc Rain (LoProb R-) + * 3 R/Sct,SChc,Patchy,Iso,Chc Rain (LoProb R) + * 4 R+/Sct,SChc,Patchy,Iso,Chc Rain (LoProb R+) + * 5 R/T;Sct,SChc,Patchy,Iso,Chc Rain (LoProb R/T) + * 6 RW/Sct,SChc,Patchy,Iso,Chc Rain (LoProb Rw) + * 7 RW/T;Sct,SChc,Patchy,Iso,Chc Rain (LoProb RW/T) + * 8 T/Sct,SChc,Patchy,Iso,Chc Rain (LoProb T) + * 9 L/Wide,Lkly,Num,Ocnl,Def,Areas Rain (HiProb L) + * 10 R-/Wide,Lkly,Num,Ocnl,Def,Areas Rain (HiProb R-) + * 11 R/Wide,Lkly,Num,Ocnl,Def,Areas Rain (HiProb R) + * 12 R+/Wide,Lkly,Num,Ocnl,Def,Areas Rain (HiProb R+) + * 13 R/T;Wide,Lkly,Num,Ocnl,Def,Areas Rain (HiProb R/T) + * 14 RW/Wide,Lkly,Num,Ocnl,Def,Areas Rain (HiProb RW) + * 15 RW/T;Wide,Lkly,Num,Ocnl,Def,Areas Rain (HiProb RW/T) + * 16 T/Wide,Lkly,Num,Ocnl,Def,Areas Rain (HiProb T) + * 17 T+ Severe Tstorms + * 18 R/S;Sct,SChc,Patchy,Iso,Chc Wintry Mix (LoProb R/S) + * 19 RW/SW;Sct,SChc,Patchy,Iso,Chc Wintry Mix (LoProb RW/SW) + * 20 R/IP;Sct,SChc,Patchy,Iso,Chc Wintry Mix (LoProb R/IP) + * 21 S/IP;Sct,SChc,Patchy,Iso,Chc Wintry Mix (LoProb S/IP) + * 22 R/S;Wide,Lkly,Num,Ocnl,Def,Areas Wintry Mix (HiProb R/S) + * 23 RW/SW;Wide,Lkly,Num,Ocnl,Def,AreasWintry Mix (HiProb RW/SW) + * 24 R/IP;Wide,Lkly,Num,Ocnl,Def,Areas Wintry Mix (HiProb R/IP) + * 25 S/IP;Wide,Lkly,Num,Ocnl,Def,Areas Wintry Mix (HiProb S/IP) + * 26 IP-/Sct,SChc,Patchy,Iso,Chc Ice (LoProb IP-) + * 27 IP/Sct,SChc,Patchy,Iso,Chc Ice (LoProb IP) + * 28 IP+/Sct,SChc,Patchy,Iso,Chc Ice (LoProb IP+) + * 29 ZL/Sct,SChc,Patchy,Iso,Chc Ice (LoProb ZL) + * 30 ZL/R;Sct,SChc,Patchy,Iso,Chc Ice (LoProb R/ZL) + * 31 ZR-/Sct,SChc,Patchy,Iso,Chc Ice (LoProb ZR-) + * 32 ZR/Sct,SChc,Patchy,Iso,Chc Ice (LoProb ZR) + * 33 ZR+/Sct,SChc,Patchy,Iso,Chc Ice (LoProb ZR+) + * 34 ZR/R;Sct,SChc,Patchy,Iso,Chc Ice (LoProb R/ZR) + * 35 ZR/IP;Sct,SChc,Patchy,Iso,Chc Ice (LoProb ZR/IP) + * 36 IP-/Wide,Lkly,Num,Ocnl,Def,Areas Ice (HiProb IP-) + * 37 IP/Wide,Lkly,Num,Ocnl,Def,Areas Ice (HiProb IP) + * 38 IP+/Wide,Lkly,Num,Ocnl,Def,Areas Ice (HiProb IP+) + * 39 ZL/Wide,Lkly,Num,Ocnl,Def,Areas Ice (HiProb ZL) + * 40 ZL/R;Wide,Lkly,Num,Ocnl,Def,Areas Ice (HiProb R/ZL) + * 41 ZR-/Wide,Lkly,Num,Ocnl,Def,Areas Ice (HiProb ZR-) + * 42 ZR/Wide,Lkly,Num,Ocnl,Def,Areas Ice (HiProb ZR) + * 43 ZR+/Wide,Lkly,Num,Ocnl,Def,Areas Ice (HiProb ZR+) + * 44 ZR/R;Wide,Lkly,Num,Ocnl,Def,Areas Ice (HiProb R/ZR) + * 45 ZR/IP;Wide,Lkly,Num,Ocnl,Def,AreasIce (HiProb ZR/IP) + * 46 SW/Sct,SChc,Patchy,Iso,Chc Snow (LoProb SW) + * 47 S-/Sct,SChc,Patchy,Iso,Chc Snow (LoProb S-) + * 48 S/Sct,SChc,Patchy,Iso,Chc Snow (LoProb S) + * 49 S+/Sct,SChc,Patchy,Iso,Chc Snow (LoProb S+) + * 50 SW/Wide,Lkly,Num,Ocnl,Def,Areas Snow (HiProb SW) + * 51 S-/Wide,Lkly,Num,Ocnl,Def,Areas Snow (HiProb S-) + * 52 S/Wide,Lkly,Num,Ocnl,Def,Areas Snow (HiProb S) + * 53 S+/Wide,Lkly,Num,Ocnl,Def,Areas Snow (HiProb S+) + * 54 F Fog + * 55 H Haze + * 56 K Smoke + * 57 BS Blowing Snow + * 58 BD Blowing Dust + ***************************************************************************** + */ +static int NDFD_WxTable1 (UglyStringType * ugly) +{ + switch (ugly->wx[0]) { + case WX_NOWX: + return 0; + case WX_R: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_S: + case WX_SW: + return 18; /* Rain/Snow Showers */ + case WX_ZR: + return 34; /* Rain/Freezing Rain */ + case WX_IP: + return 20; /* Rain/Sleet */ + case WX_ZL: + return 30; /* Rain/Freezing Drizzle */ + case WX_T: + return 5; /* Rain/Thunderstorms */ + default: + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 2; /* Light Rain */ + case INT_P: + return 4; /* Heavy Rain */ + default: + return 3; /* Normal Rain */ + } + } + } else { + switch (ugly->wx[1]) { + case WX_S: + case WX_SW: + return 22; /* Rain/Snow Showers */ + case WX_ZR: + return 44; /* Rain/Freezing Rain */ + case WX_IP: + return 24; /* Rain/Sleet */ + case WX_ZL: + return 40; /* Rain/Freezing Drizzle */ + case WX_T: + return 13; /* Rain/Thunderstorms */ + default: + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 10; /* Light Rain */ + case INT_P: + return 12; /* Heavy Rain */ + default: + return 11; /* Normal Rain */ + } + } + } + case WX_RW: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_T: + return 7; /* Rain Showers/Thunderstorms */ + case WX_SW: + return 19; /* Rain Showers/Snow Showers */ + default: + return 6; /* Rain Showers */ + } + } else { + switch (ugly->wx[1]) { + case WX_T: + return 15; /* Rain Showers/Thunderstorms */ + case WX_SW: + return 23; /* Rain Showers/Snow Showers */ + default: + return 14; /* Rain Showers */ + } + } + case WX_L: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_ZL: + return 29; /* Drizzle/Freezing Drizzle */ + case WX_F: + default: + return 1; /* Drizzle */ + } + } else { + switch (ugly->wx[1]) { + case WX_ZL: + return 40; /* Drizzle/Freezing Drizzle */ + case WX_F: + default: + return 9; /* Drizzle */ + } + } + case WX_ZL: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: + return 30; /* Freezing Drizzle/Rain */ + case WX_L: + default: + return 29; /* Freezing Drizzle */ + } + } else { + switch (ugly->wx[1]) { + case WX_R: + return 40; /* Freezing Drizzle/Rain */ + case WX_L: + default: + return 39; /* Freezing Drizzle */ + } + } + case WX_ZR: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: + return 34; /* Freezing Rain/Rain */ + case WX_IP: + return 35; /* Freezing Rain/Sleet */ + default: + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 31; /* Light Freezing Rain */ + case INT_P: + return 33; /* Heavy Freezing Rain */ + default: + return 32; /* Normal Freezing Rain */ + } + } + } else { + switch (ugly->wx[1]) { + case WX_R: + return 44; /* Freezing Rain/Rain */ + case WX_IP: + return 45; /* Freezing Rain/Sleet */ + default: + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 41; /* Light Freezing Rain */ + case INT_P: + return 43; /* Heavy Freezing Rain */ + default: + return 42; /* Normal Freezing Rain */ + } + } + } + case WX_IP: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: + return 20; /* Sleet/Rain */ + case WX_S: + return 21; /* Sleet/Snow */ + case WX_ZR: + return 35; /* Sleet/Freezing Rain */ + default: + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 26; /* Light Sleet */ + case INT_P: + return 28; /* Heavy Sleet */ + default: + return 27; /* Normal Sleet */ + } + } + } else { + switch (ugly->wx[1]) { + case WX_R: + return 24; /* Sleet/Rain */ + case WX_S: + return 25; /* Sleet/Snow */ + case WX_ZR: + return 45; /* Sleet/Freezing Rain */ + default: + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 36; /* Light Sleet */ + case INT_P: + return 38; /* Heavy Sleet */ + default: + return 37; /* Normal Sleet */ + } + } + } + case WX_SW: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: + return 18; /* Snow Showers/Rain */ + case WX_RW: + return 19; /* Snow Showers/Rain Showers */ + default: + return 46; /* Snow Showers */ + } + } else { + switch (ugly->wx[1]) { + case WX_R: + return 22; /* Snow Showers/Rain */ + case WX_RW: + return 23; /* Snow Showers/Rain Showers */ + default: + return 50; /* Snow Showers */ + } + } + case WX_S: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: + case WX_RW: + return 18; /* Snow/Rain */ + case WX_IP: + return 21; /* Snow/Sleet */ + default: + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 47; /* Light Snow */ + case INT_P: + return 49; /* Heavy Snow */ + default: + return 48; /* Normal Snow */ + } + } + } else { + switch (ugly->wx[1]) { + case WX_R: + case WX_RW: + return 22; /* Snow/Rain */ + case WX_IP: + return 25; /* Snow/Sleet */ + default: + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 51; /* Light Snow */ + case INT_P: + return 53; /* Heavy Snow */ + default: + return 52; /* Normal Snow */ + } + } + } + case WX_T: + /* + * Check Severe storms. If so, this is most important weather + * type. + */ + if (ugly->intens[0] == INT_P) { + return 17; /* Severe Thunderstorms */ + } + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: + return 5; /* Thunderstorms/Rain */ + case WX_RW: + return 7; /* Thunderstorms/Rain Showers */ + default: + return 8; /* Thunderstorms. */ + } + } else { + switch (ugly->wx[1]) { + case WX_R: + return 13; /* Thunderstorms/Rain */ + case WX_RW: + return 15; /* Thunderstorms/Rain Showers */ + default: + return 16; /* Thunderstorms. */ + } + } + case WX_F: + return 54; /* Fog */ + case WX_H: + return 55; /* Haze */ + case WX_K: + return 56; /* Smoke */ + case WX_BS: + return 57; /* Blowing Snow */ + case WX_BD: + return 58; /* Blowing Dust */ + case WX_FR: /* Ignore Frost */ + case WX_A: /* Ignore Hail */ + default: + return 0; + } +} + +/***************************************************************************** + * NDFD_WxTable2_StdInten() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * A helper routine to NDFD_WxTable2() to assist with adjusting the + * intensity. For the most part if intens is INT_D or INT_DD, we want to + * subtract 1 from the base value. If it is INT_P, we want to add 1, + * otherwise just return base. + * + * ARGUMENTS + * base = The base encoded number to adjust. (Input) + * intens = The intensity of the first weather key. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (the resulting encoded number.) + * + * HISTORY + * 1/2004 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +static int NDFD_WxTable2_StdInten (int base, int intens) +{ + switch (intens) { + case INT_D: + case INT_DD: + return base - 1; + case INT_P: + return base + 1; + default: + return base; + } +} + +/***************************************************************************** + * NDFD_WxTable2() -- + * + * Original: makeWxImageCodes() Marc Saccucci Jan 2004 (MDL) + * Adapted to NDFD_WxTable() Arthur Taylor / MDL + * + * PURPOSE + * To use the same weather table scheme used by Marc Saccucci in + * makeWxImageCodes() in the NDFD source tree. The purpose of both + * procedures is to simplify the weather string (aka ugly string) to a single + * integral code number, which contains the most relevant weather. The + * intent is to create a simpler field which can more readily be viewed as + * an image. + * + * ARGUMENTS + * ugly = The ugly weather string to encode. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (the encoded number.) + * + * HISTORY + * 11/2002 Marc Saccucci (MDL): Created matching algorithm in + * makeWxImageCodes(). + * 6/2003 MS: Altered matching combinations in makeWxImageCodes(). + * 1/2004 MS: Updated to include intensity considerations for all Precip + types. + * 1/2004 Arthur Taylor (MDL/RSIS): Created NDFD_WxTable2() + * + * NOTES + * 1) The table used: + * new_code Sample Value Legend Value Description + * -------- ------------ -------------- ------------ + * 0 - - No Predominant Weather + * 1 L- Rain (LoProb L-) + * 2 L Rain (LoProb L) + * 3 L+ Rain (LoProb L+) + * 4 R- Rain (LoProb R-) + * 5 R Rain (LoProb R) + * 6 R+ Rain (LoProb R+) + * 7 R/T+ Severe (LoProb R/T+) + * 8 T/R+ Rain (LoProb T/R+) + * 9 T/R- Rain (LoProb T/R-) + * 10 R/T Rain (LoProb R/T) + * 11 RW- Rain (LoProb RW-) + * 12 RW Rain (LoProb RW) + * 13 RW+ Rain (LoProb RW+) + * 14 RW/T+ Severe (LoProb RW/T+) + * 15 RW/T Rain (LoProb RW/T) + * 16 T/RW+ Rain (LoProb T/RW+) + * 17 T/RW- Rain (LoProb T/RW-) + * 18 T Rain (LoProb T) + * 19 T+ Severe (LoProb T+) + * 20 L- Rain (HiProb L-) + * 21 L Rain (HiProb L) + * 22 L+ Rain (HiProb L+) + * 23 R- Rain (HiProb R-) + * 24 R Rain (HiProb R) + * 25 R+ Rain (HiProb R+) + * 26 R/T+ Severe (HiProb R/T+) + * 27 R/T Rain (HiProb R/T) + * 28 T/R+ Rain (HiProb T/R+) + * 29 T/R- Rain (HiProb T/R-) + * 30 RW- Rain (HiProb RW-) + * 31 RW Rain (HiProb RW) + * 32 RW+ Rain (HiProb RW+) + * 33 RW/T Rain (HiProb RW/T) + * 34 RW/T+ Severe (HiProb RW/T+) + * 35 T/RW+ Rain (HiProb T/RW+) + * 36 T/RW- Rain (HiProb T/RW-) + * 37 T Rain (HiProb T) + * 38 T+ Severe (HiProb T+) + * 39 R/S- Mix (LoProb R/S-) + * 40 R/S Mix (LoProb R/S) + * 41 R/S+ Mix (LoProb R/S+) + * 42 RW/SW- Mix (LoProb RW/SW-) + * 43 RW/SW Mix (LoProb RW/SW) + * 44 RW/SW+ Mix (LoProb RW/SW+) + * 45 R/IP- Mix (LoProb R/IP-) + * 46 R/IP Mix (LoProb R/IP) + * 47 R/IP+ Mix (LoProb R/IP+) + * 48 S/IP- Mix (LoProb S/IP-) + * 49 S/IP Mix (LoProb S/IP) + * 50 S/IP+ Mix (LoProb S/IP+) + * 51 R/S- Mix (HiProb R/S-) + * 52 R/S Mix (HiProb R/S) + * 53 R/S+ Mix (HiProb R/S+) + * 54 RW/SW- Mix (HiProb RW/SW-) + * 55 RW/SW Mix (HiProb RW/SW) + * 56 RW/SW+ Mix (HiProb RW/SW+) + * 57 R/IP- Mix (HiProb R/IP-) + * 58 R/IP Mix (HiProb R/IP) + * 59 R/IP+ Mix (HiProb R/IP+) + * 60 S/IP- Mix (HiProb S/IP-) + * 61 S/IP Mix (HiProb S/IP) + * 62 S/IP+ Mix (HiProb S/IP+) + * 63 IP- Ice (LoProb IP-) + * 64 IP Ice (LoProb IP) + * 65 IP+ Ice (LoProb IP+) + * 66 ZL- Ice (LoProb ZL-) + * 67 ZL Ice (LoProb ZL) + * 68 ZL+ Ice (LoProb ZL+) + * 69 R/ZL- Ice (LoProb R/ZL-) + * 70 R/ZL Ice (LoProb R/ZL) + * 71 R/ZL+ Ice (LoProb R/ZL+) + * 72 ZR- Ice (LoProb ZR-) + * 73 ZR Ice (LoProb ZR) + * 74 ZR+ Ice (LoProb ZR+) + * 75 R/ZR- Ice (LoProb R/ZR-) + * 76 R/ZR Ice (LoProb R/ZR) + * 77 R/ZR+ Ice (LoProb R/ZR+) + * 78 IP/ZR- Ice (LoProb IP/ZR-) + * 79 IP/ZR Ice (LoProb IP/ZR) + * 80 IP/ZR+ Ice (LoProb IP/ZR+) + * 81 IP- Ice (HiProb IP-) + * 82 IP Ice (HiProb IP) + * 83 IP+ Ice (HiProb IP+) + * 84 ZL- Ice (HiProb ZL-) + * 85 ZL Ice (HiProb ZL) + * 86 ZL+ Ice (HiProb ZL+) + * 87 R/ZL- Ice (HiProb R/ZL-) + * 88 R/ZL Ice (HiProb R/ZL) + * 89 R/ZL+ Ice (HiProb R/ZL+) + * 90 ZR- Ice (HiProb ZR-) + * 91 ZR Ice (HiProb ZR) + * 92 ZR+ Ice (HiProb ZR+) + * 93 R/ZR- Ice (HiProb R/ZR-) + * 94 R/ZR Ice (HiProb R/ZR) + * 95 R/ZR+ Ice (HiProb R/ZR+) + * 96 IP/ZR- Ice (HiProb IP/ZR-) + * 97 IP/ZR Ice (HiProb IP/ZR) + * 98 IP/ZR+ Ice (HiProb IP/ZR+) + * 99 L/ZL- Ice (LoProb L/ZL-) + * 100 L/ZL Ice (LoProb L/ZL) + * 101 L/ZL+ Ice (LoProb L/ZL+) + * 102 L/ZL- Ice (HiProb L/ZL-) + * 103 L/ZL Ice (HiProb L/ZL) + * 104 L/ZL+ Ice (HiProb L/ZL+) + * 105 SW- Snow (LoProb SW-) + * 106 SW Snow (LoProb SW) + * 107 SW+ Snow (LoProb SW+) + * 108 S- Snow (LoProb S-) + * 109 S Snow (LoProb S) + * 110 S+ Snow (LoProb S+) + * 111 SW- Snow (HiProb SW-) + * 112 SW Snow (HiProb SW) + * 113 SW+ Snow (HiProb SW+) + * 114 S- Snow (HiProb S-) + * 115 S Snow (HiProb S) + * 116 S+ Snow (HiProb S+) + * 117 F Fog (Fog) + * 118 F+ Fog (Dense Fog) + * 119 H Haze + * 120 K Smoke + * 121 BS Blowing (Blowing Snow) + * 122 BD Blowing (Blowing Dust) + ***************************************************************************** + */ +static int NDFD_WxTable2 (UglyStringType * ugly) +{ + switch (ugly->wx[0]) { + case WX_NOWX: + return 0; + case WX_R: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_S: + return (NDFD_WxTable2_StdInten (40, ugly->intens[0])); + case WX_ZR: /* Rain/Freezing Rain */ + return (NDFD_WxTable2_StdInten (76, ugly->intens[0])); + case WX_IP: /* Rain/Sleet */ + return (NDFD_WxTable2_StdInten (46, ugly->intens[0])); + case WX_ZL: /* Rain/Freezing Drizzle */ + return (NDFD_WxTable2_StdInten (70, ugly->intens[0])); + case WX_SW: /* Rain/Snow Showers */ + return (NDFD_WxTable2_StdInten (40, ugly->intens[0])); + case WX_T: /* Rain/Thunderstorms */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 9; + case INT_P: + return 8; + default: + return 27; + } + default: + return (NDFD_WxTable2_StdInten (5, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_S: /* Rain/Snow */ + return (NDFD_WxTable2_StdInten (52, ugly->intens[0])); + case WX_ZR: /* Rain/Freezing Rain */ + return (NDFD_WxTable2_StdInten (94, ugly->intens[0])); + case WX_IP: /* Rain/Sleet */ + return (NDFD_WxTable2_StdInten (58, ugly->intens[0])); + case WX_ZL: /* Rain/Freezing Drizzle */ + return (NDFD_WxTable2_StdInten (88, ugly->intens[0])); + case WX_SW: /* Rain/Snow Showers */ + return (NDFD_WxTable2_StdInten (52, ugly->intens[0])); + case WX_T: /* Rain/Thunderstorms */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 29; + case INT_P: + return 28; + default: + return 27; + } + default: + return (NDFD_WxTable2_StdInten (24, ugly->intens[0])); + } + } + case WX_RW: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_T: /* Rain Showers/Thunderstorms */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 17; + case INT_P: + return 16; + default: + return 15; + } + case WX_SW: /* Rain Showers/Snow Showers */ + case WX_S: /* Rain Showers/Snow */ + return (NDFD_WxTable2_StdInten (43, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (12, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_T: /* Rain Showers/Thunderstorms */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 36; + case INT_P: + return 35; + default: + return 33; + } + case WX_SW: /* Rain Showers/Snow Showers */ + case WX_S: /* Rain Showers/Snow */ + return (NDFD_WxTable2_StdInten (55, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (31, ugly->intens[0])); + } + } + case WX_L: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_ZL: /* Drizzle/Freezing Drizzle */ + return (NDFD_WxTable2_StdInten (100, ugly->intens[0])); + case WX_F: + default: /* Drizzle */ + return (NDFD_WxTable2_StdInten (2, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_ZL: /* Drizzle/Freezing Drizzle */ + return (NDFD_WxTable2_StdInten (103, ugly->intens[0])); + case WX_F: + default: /* Drizzle */ + return (NDFD_WxTable2_StdInten (21, ugly->intens[0])); + } + } + case WX_ZL: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: /* Freezing Drizzle/Rain */ + return (NDFD_WxTable2_StdInten (70, ugly->intens[0])); + case WX_L: /* Freezing Drizzle/Drizzle */ + return (NDFD_WxTable2_StdInten (100, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (67, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_R: /* Freezing Drizzle/Rain */ + return (NDFD_WxTable2_StdInten (88, ugly->intens[0])); + case WX_L: /* Freezing Drizzle/Drizzle */ + return (NDFD_WxTable2_StdInten (103, ugly->intens[0])); + default: /* Freezing Drizzle */ + return (NDFD_WxTable2_StdInten (85, ugly->intens[0])); + } + } + case WX_ZR: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: /* Freezing Rain/Rain */ + return (NDFD_WxTable2_StdInten (76, ugly->intens[0])); + case WX_IP: /* Freezing Rain/Sleet */ + return (NDFD_WxTable2_StdInten (79, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (73, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_R: /* Freezing Rain/Rain */ + return (NDFD_WxTable2_StdInten (94, ugly->intens[0])); + case WX_IP: /* Freezing Rain/Sleet */ + return (NDFD_WxTable2_StdInten (97, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (91, ugly->intens[0])); + } + } + case WX_IP: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: /* Sleet/Rain */ + return (NDFD_WxTable2_StdInten (46, ugly->intens[0])); + case WX_S: /* Sleet/Snow */ + return (NDFD_WxTable2_StdInten (49, ugly->intens[0])); + case WX_ZR: /* Sleet/Freezing Rain */ + return (NDFD_WxTable2_StdInten (79, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (64, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_R: /* Sleet/Rain */ + return (NDFD_WxTable2_StdInten (58, ugly->intens[0])); + case WX_S: /* Sleet/Snow */ + return (NDFD_WxTable2_StdInten (61, ugly->intens[0])); + case WX_ZR: /* Sleet/Freezing Rain */ + return (NDFD_WxTable2_StdInten (97, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (82, ugly->intens[0])); + } + } + case WX_SW: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: /* Snow Showers/Rain */ + case WX_RW: /* Snow Showers/Rain Showers */ + return (NDFD_WxTable2_StdInten (43, ugly->intens[0])); + default: /* Snow Showers */ + return (NDFD_WxTable2_StdInten (106, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_R: /* Snow Showers/Rain */ + case WX_RW: /* Snow Showers/Rain Showers */ + return (NDFD_WxTable2_StdInten (55, ugly->intens[0])); + default: /* Snow Showers */ + return (NDFD_WxTable2_StdInten (112, ugly->intens[0])); + } + } + case WX_S: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: /* Snow/Rain */ + case WX_RW: /* Snow/Rain Showers */ + return (NDFD_WxTable2_StdInten (40, ugly->intens[0])); + case WX_IP: /* Snow/Sleet */ + return (NDFD_WxTable2_StdInten (49, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (109, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_R: /* Snow/Rain */ + case WX_RW: + return (NDFD_WxTable2_StdInten (52, ugly->intens[0])); + case WX_IP: /* Snow/Sleet */ + return (NDFD_WxTable2_StdInten (61, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (115, ugly->intens[0])); + } + } + case WX_T: + /* + * Check Severe storms. If so, this is most important weather + * type. + */ +/* + if (ugly->intens[0] == INT_P) { + return 17; * Severe Thunderstorms * + } +*/ + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_RW: /* Thunderstorms/Rain Showers */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 17; + case INT_P: + return 14; + default: + return 15; + } + case WX_R: /* Thunderstorms/Rain */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 9; + case INT_P: + return 7; + default: + return 10; + } + default: /* Thunderstorms. */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 18; + case INT_P: + return 19; + default: + return 18; + } + } + } else { + switch (ugly->wx[1]) { + case WX_RW: /* Thunderstorms/Rain Showers */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 36; + case INT_P: + return 34; + default: + return 33; /* corrected from 37 */ + } + case WX_R: /* Thunderstorms/Rain */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 29; + case INT_P: + return 26; + default: + return 27; + } + default: /* Thunderstorms. */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 37; + case INT_P: + return 38; + default: + return 37; + } + } + } + case WX_A: /* Ignore Hail */ + return 0; + case WX_F: /* Fog */ + switch (ugly->intens[0]) { + case INT_P: + return 118; + default: + return 117; + } + case WX_H: /* Haze */ + return 119; + case WX_K: /* Smoke */ + return 120; + case WX_FR: /* Ignore Frost */ + return 0; + case WX_BS: /* Blowing Snow */ + return 121; + case WX_BD: /* Blowing Dust */ + return 122; + default: + return 0; + } +} + +/***************************************************************************** + * NDFD_WxTable3() -- + * + * Original: makeWxImageCodes() Marc Saccucci Feb 2004 (MDL) + * Adapted to NDFD_WxTable3() Arthur Taylor / MDL + * + * PURPOSE + * To use the same weather table scheme used by Marc Saccucci in + * makeWxImageCodes() in the NDFD source tree. The purpose of both + * procedures is to simplify the weather string (aka ugly string) to a single + * integral code number, which contains the most relevant weather. The + * intent is to create a simpler field which can more readily be viewed as + * an image. + * + * ARGUMENTS + * ugly = The ugly weather string to encode. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (the encoded number.) + * + * HISTORY + * 11/2002 Marc Saccucci (MDL): Created matching algorithm in + * makeWxImageCodes(). + * 6/2003 MS: Altered matching combinations in makeWxImageCodes(). + * 1/2004 MS: Updated to include intensity considerations for all Precip + * types. + * 2/2004 MS: Updated to include: 123..129 ZF, IF, IC, BN, ZY, VA, WP + * 2/2004 Arthur Taylor (MDL/RSIS): Created NDFD_WxTable3() + * + * NOTES + * 1) The table used: + * new_code Sample Value Legend Value Description + * -------- ------------ -------------- ------------ + * 0 - - No Predominant Weather + * 1 L- Rain (LoProb L-) + * 2 L Rain (LoProb L) + * 3 L+ Rain (LoProb L+) + * 4 R- Rain (LoProb R-) + * 5 R Rain (LoProb R) + * 6 R+ Rain (LoProb R+) + * 7 R/T+ Severe (LoProb R/T+) + * 8 T/R+ Rain (LoProb T/R+) + * 9 T/R- Rain (LoProb T/R-) + * 10 R/T Rain (LoProb R/T) + * 11 RW- Rain (LoProb RW-) + * 12 RW Rain (LoProb RW) + * 13 RW+ Rain (LoProb RW+) + * 14 RW/T+ Severe (LoProb RW/T+) + * 15 RW/T Rain (LoProb RW/T) + * 16 T/RW+ Rain (LoProb T/RW+) + * 17 T/RW- Rain (LoProb T/RW-) + * 18 T Rain (LoProb T) + * 19 T+ Severe (LoProb T+) + * 20 L- Rain (HiProb L-) + * 21 L Rain (HiProb L) + * 22 L+ Rain (HiProb L+) + * 23 R- Rain (HiProb R-) + * 24 R Rain (HiProb R) + * 25 R+ Rain (HiProb R+) + * 26 R/T+ Severe (HiProb R/T+) + * 27 R/T Rain (HiProb R/T) + * 28 T/R+ Rain (HiProb T/R+) + * 29 T/R- Rain (HiProb T/R-) + * 30 RW- Rain (HiProb RW-) + * 31 RW Rain (HiProb RW) + * 32 RW+ Rain (HiProb RW+) + * 33 RW/T Rain (HiProb RW/T) + * 34 RW/T+ Severe (HiProb RW/T+) + * 35 T/RW+ Rain (HiProb T/RW+) + * 36 T/RW- Rain (HiProb T/RW-) + * 37 T Rain (HiProb T) + * 38 T+ Severe (HiProb T+) + * 39 R/S- Mix (LoProb R/S-) + * 40 R/S Mix (LoProb R/S) + * 41 R/S+ Mix (LoProb R/S+) + * 42 RW/SW- Mix (LoProb RW/SW-) + * 43 RW/SW Mix (LoProb RW/SW) + * 44 RW/SW+ Mix (LoProb RW/SW+) + * 45 R/IP- Mix (LoProb R/IP-) + * 46 R/IP Mix (LoProb R/IP) + * 47 R/IP+ Mix (LoProb R/IP+) + * 48 S/IP- Mix (LoProb S/IP-) + * 49 S/IP Mix (LoProb S/IP) + * 50 S/IP+ Mix (LoProb S/IP+) + * 51 R/S- Mix (HiProb R/S-) + * 52 R/S Mix (HiProb R/S) + * 53 R/S+ Mix (HiProb R/S+) + * 54 RW/SW- Mix (HiProb RW/SW-) + * 55 RW/SW Mix (HiProb RW/SW) + * 56 RW/SW+ Mix (HiProb RW/SW+) + * 57 R/IP- Mix (HiProb R/IP-) + * 58 R/IP Mix (HiProb R/IP) + * 59 R/IP+ Mix (HiProb R/IP+) + * 60 S/IP- Mix (HiProb S/IP-) + * 61 S/IP Mix (HiProb S/IP) + * 62 S/IP+ Mix (HiProb S/IP+) + * 63 IP- Ice (LoProb IP-) + * 64 IP Ice (LoProb IP) + * 65 IP+ Ice (LoProb IP+) + * 66 ZL- Ice (LoProb ZL-) + * 67 ZL Ice (LoProb ZL) + * 68 ZL+ Ice (LoProb ZL+) + * 69 R/ZL- Ice (LoProb R/ZL-) + * 70 R/ZL Ice (LoProb R/ZL) + * 71 R/ZL+ Ice (LoProb R/ZL+) + * 72 ZR- Ice (LoProb ZR-) + * 73 ZR Ice (LoProb ZR) + * 74 ZR+ Ice (LoProb ZR+) + * 75 R/ZR- Ice (LoProb R/ZR-) + * 76 R/ZR Ice (LoProb R/ZR) + * 77 R/ZR+ Ice (LoProb R/ZR+) + * 78 IP/ZR- Ice (LoProb IP/ZR-) + * 79 IP/ZR Ice (LoProb IP/ZR) + * 80 IP/ZR+ Ice (LoProb IP/ZR+) + * 81 IP- Ice (HiProb IP-) + * 82 IP Ice (HiProb IP) + * 83 IP+ Ice (HiProb IP+) + * 84 ZL- Ice (HiProb ZL-) + * 85 ZL Ice (HiProb ZL) + * 86 ZL+ Ice (HiProb ZL+) + * 87 R/ZL- Ice (HiProb R/ZL-) + * 88 R/ZL Ice (HiProb R/ZL) + * 89 R/ZL+ Ice (HiProb R/ZL+) + * 90 ZR- Ice (HiProb ZR-) + * 91 ZR Ice (HiProb ZR) + * 92 ZR+ Ice (HiProb ZR+) + * 93 R/ZR- Ice (HiProb R/ZR-) + * 94 R/ZR Ice (HiProb R/ZR) + * 95 R/ZR+ Ice (HiProb R/ZR+) + * 96 IP/ZR- Ice (HiProb IP/ZR-) + * 97 IP/ZR Ice (HiProb IP/ZR) + * 98 IP/ZR+ Ice (HiProb IP/ZR+) + * 99 L/ZL- Ice (LoProb L/ZL-) + * 100 L/ZL Ice (LoProb L/ZL) + * 101 L/ZL+ Ice (LoProb L/ZL+) + * 102 L/ZL- Ice (HiProb L/ZL-) + * 103 L/ZL Ice (HiProb L/ZL) + * 104 L/ZL+ Ice (HiProb L/ZL+) + * 105 SW- Snow (LoProb SW-) + * 106 SW Snow (LoProb SW) + * 107 SW+ Snow (LoProb SW+) + * 108 S- Snow (LoProb S-) + * 109 S Snow (LoProb S) + * 110 S+ Snow (LoProb S+) + * 111 SW- Snow (HiProb SW-) + * 112 SW Snow (HiProb SW) + * 113 SW+ Snow (HiProb SW+) + * 114 S- Snow (HiProb S-) + * 115 S Snow (HiProb S) + * 116 S+ Snow (HiProb S+) + * 117 F Fog (Fog) + * 118 F+ Fog (Dense Fog) + * 119 H Haze + * 120 K Smoke + * 121 BS Blowing (Blowing Snow) + * 122 BD Blowing (Blowing Dust) + * 123 ZF Fog (Freezing Fog) + * 124 IF Fog (Ice Fog) + * 125 IC Ice (Ice Crystals) + * 126 BN Blowing (Blowing Sand) + * 127 ZY Blowing (Freezing Spray) + * 128 VA Smoke (Volcanic Ash) + * 129 WP Severe (Water Spouts) + ***************************************************************************** + */ +static int NDFD_WxTable3 (UglyStringType * ugly) +{ + switch (ugly->wx[0]) { + case WX_NOWX: + return 0; + case WX_R: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_S: + return (NDFD_WxTable2_StdInten (40, ugly->intens[0])); + case WX_ZR: /* Rain/Freezing Rain */ + return (NDFD_WxTable2_StdInten (76, ugly->intens[0])); + case WX_IP: /* Rain/Sleet */ + return (NDFD_WxTable2_StdInten (46, ugly->intens[0])); + case WX_ZL: /* Rain/Freezing Drizzle */ + return (NDFD_WxTable2_StdInten (70, ugly->intens[0])); + case WX_SW: /* Rain/Snow Showers */ + return (NDFD_WxTable2_StdInten (40, ugly->intens[0])); + case WX_T: /* Rain/Thunderstorms */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 9; + case INT_P: + return 8; + default: + return 27; + } + default: + return (NDFD_WxTable2_StdInten (5, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_S: /* Rain/Snow */ + return (NDFD_WxTable2_StdInten (52, ugly->intens[0])); + case WX_ZR: /* Rain/Freezing Rain */ + return (NDFD_WxTable2_StdInten (94, ugly->intens[0])); + case WX_IP: /* Rain/Sleet */ + return (NDFD_WxTable2_StdInten (58, ugly->intens[0])); + case WX_ZL: /* Rain/Freezing Drizzle */ + return (NDFD_WxTable2_StdInten (88, ugly->intens[0])); + case WX_SW: /* Rain/Snow Showers */ + return (NDFD_WxTable2_StdInten (52, ugly->intens[0])); + case WX_T: /* Rain/Thunderstorms */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 29; + case INT_P: + return 28; + default: + return 27; + } + default: + return (NDFD_WxTable2_StdInten (24, ugly->intens[0])); + } + } + case WX_RW: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_T: /* Rain Showers/Thunderstorms */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 17; + case INT_P: + return 16; + default: + return 15; + } + case WX_SW: /* Rain Showers/Snow Showers */ + case WX_S: /* Rain Showers/Snow */ + return (NDFD_WxTable2_StdInten (43, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (12, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_T: /* Rain Showers/Thunderstorms */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 36; + case INT_P: + return 35; + default: + return 33; + } + case WX_SW: /* Rain Showers/Snow Showers */ + case WX_S: /* Rain Showers/Snow */ + return (NDFD_WxTable2_StdInten (55, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (31, ugly->intens[0])); + } + } + case WX_L: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_ZL: /* Drizzle/Freezing Drizzle */ + return (NDFD_WxTable2_StdInten (100, ugly->intens[0])); + case WX_F: + default: /* Drizzle */ + return (NDFD_WxTable2_StdInten (2, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_ZL: /* Drizzle/Freezing Drizzle */ + return (NDFD_WxTable2_StdInten (103, ugly->intens[0])); + case WX_F: + default: /* Drizzle */ + return (NDFD_WxTable2_StdInten (21, ugly->intens[0])); + } + } + case WX_ZL: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: /* Freezing Drizzle/Rain */ + return (NDFD_WxTable2_StdInten (70, ugly->intens[0])); + case WX_L: /* Freezing Drizzle/Drizzle */ + return (NDFD_WxTable2_StdInten (100, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (67, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_R: /* Freezing Drizzle/Rain */ + return (NDFD_WxTable2_StdInten (88, ugly->intens[0])); + case WX_L: /* Freezing Drizzle/Drizzle */ + return (NDFD_WxTable2_StdInten (103, ugly->intens[0])); + default: /* Freezing Drizzle */ + return (NDFD_WxTable2_StdInten (85, ugly->intens[0])); + } + } + case WX_ZR: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: /* Freezing Rain/Rain */ + return (NDFD_WxTable2_StdInten (76, ugly->intens[0])); + case WX_IP: /* Freezing Rain/Sleet */ + return (NDFD_WxTable2_StdInten (79, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (73, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_R: /* Freezing Rain/Rain */ + return (NDFD_WxTable2_StdInten (94, ugly->intens[0])); + case WX_IP: /* Freezing Rain/Sleet */ + return (NDFD_WxTable2_StdInten (97, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (91, ugly->intens[0])); + } + } + case WX_IP: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: /* Sleet/Rain */ + return (NDFD_WxTable2_StdInten (46, ugly->intens[0])); + case WX_S: /* Sleet/Snow */ + return (NDFD_WxTable2_StdInten (49, ugly->intens[0])); + case WX_ZR: /* Sleet/Freezing Rain */ + return (NDFD_WxTable2_StdInten (79, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (64, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_R: /* Sleet/Rain */ + return (NDFD_WxTable2_StdInten (58, ugly->intens[0])); + case WX_S: /* Sleet/Snow */ + return (NDFD_WxTable2_StdInten (61, ugly->intens[0])); + case WX_ZR: /* Sleet/Freezing Rain */ + return (NDFD_WxTable2_StdInten (97, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (82, ugly->intens[0])); + } + } + case WX_SW: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: /* Snow Showers/Rain */ + case WX_RW: /* Snow Showers/Rain Showers */ + return (NDFD_WxTable2_StdInten (43, ugly->intens[0])); + default: /* Snow Showers */ + return (NDFD_WxTable2_StdInten (106, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_R: /* Snow Showers/Rain */ + case WX_RW: /* Snow Showers/Rain Showers */ + return (NDFD_WxTable2_StdInten (55, ugly->intens[0])); + default: /* Snow Showers */ + return (NDFD_WxTable2_StdInten (112, ugly->intens[0])); + } + } + case WX_S: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_R: /* Snow/Rain */ + case WX_RW: /* Snow/Rain Showers */ + return (NDFD_WxTable2_StdInten (40, ugly->intens[0])); + case WX_IP: /* Snow/Sleet */ + return (NDFD_WxTable2_StdInten (49, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (109, ugly->intens[0])); + } + } else { + switch (ugly->wx[1]) { + case WX_R: /* Snow/Rain */ + case WX_RW: + return (NDFD_WxTable2_StdInten (52, ugly->intens[0])); + case WX_IP: /* Snow/Sleet */ + return (NDFD_WxTable2_StdInten (61, ugly->intens[0])); + default: + return (NDFD_WxTable2_StdInten (115, ugly->intens[0])); + } + } + case WX_T: + if ((ugly->cover[0] == COV_SCT) || (ugly->cover[0] == COV_SCHC) || + (ugly->cover[0] == COV_PATCHY) || (ugly->cover[0] == COV_ISO) || + (ugly->cover[0] == COV_CHC)) { + switch (ugly->wx[1]) { + case WX_RW: /* Thunderstorms/Rain Showers */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 17; + case INT_P: + return 14; + default: + return 15; + } + case WX_R: /* Thunderstorms/Rain */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 9; + case INT_P: + return 7; + default: + return 10; + } + default: /* Thunderstorms. */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 18; + case INT_P: + return 19; + default: + return 18; + } + } + } else { + switch (ugly->wx[1]) { + case WX_RW: /* Thunderstorms/Rain Showers */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 36; + case INT_P: + return 34; + default: + return 33; + } + case WX_R: /* Thunderstorms/Rain */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 29; + case INT_P: + return 26; + default: + return 27; + } + default: /* Thunderstorms. */ + switch (ugly->intens[0]) { + case INT_D: + case INT_DD: + return 37; + case INT_P: + return 38; + default: + return 37; + } + } + } + case WX_A: /* Ignore Hail */ + return 0; + case WX_F: /* Fog */ + switch (ugly->intens[0]) { + case INT_P: + return 118; + default: + return 117; + } + case WX_H: /* Haze */ + return 119; + case WX_K: /* Smoke */ + return 120; + case WX_FR: /* Ignore Frost */ + return 0; + case WX_BS: /* Blowing Snow */ + return 121; + case WX_BD: /* Blowing Dust */ + return 122; + case WX_ZF: /* Freezing Fog */ + return 123; + case WX_IF: /* Ice Fog */ + return 124; + case WX_IC: /* Ice Crystals */ + return 125; + case WX_BN: /* Blowing Sand */ + return 126; + case WX_ZY: /* Freezing Spray */ + return 127; + case WX_VA: /* Volcanic Ash */ + return 128; + case WX_WP: /* Water Spouts */ + return 129; + default: + return 0; + } +} + +/***************************************************************************** + * NDFD_Wx2Code4() -- + * + * Original: wx2code() Mark Armstrong Nov 2004 (MDL) + * Adapted to NDFD_Wx2Code() Arthur Taylor (MDL) + * + * PURPOSE + * Converts from a Weather Type to the code value used in makeWxImageCodes. + * + * ARGUMENTS + * wxtype = The weather type to encode. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (the encoded number.) + * + * HISTORY + * 11/2004 Mark Armstrong (MDL): Created matching algorithm "wx2code" + * 11/2004 Arthur Taylor (MDL): Modified to assist with NDFD_WxTable4() + * + * NOTES + ***************************************************************************** + */ +static int NDFD_Wx2Code4 (int wxtype) +{ + switch (wxtype) { + case WX_R: + return 0; + case WX_RW: + return 10; + case WX_L: + return 20; + case WX_ZL: + return 30; + case WX_ZR: + return 40; + case WX_IP: + return 50; + case WX_SW: + return 60; + case WX_S: + return 70; + case WX_T: + return 80; + case WX_F: + return 90; + default: + return 0; + } +} + +/***************************************************************************** + * NDFD_CodeIntens4() -- + * + * Original: code_intensity() Mark Armstrong Nov 2004 (MDL) + * Adapted to NDFD_CodeIntens4() Arthur Taylor (MDL) + * + * PURPOSE + * Converts from two types of weather intensities to the code value used in + * makeWxImageCodes when dealing with intensities. + * + * ARGUMENTS + * inten1 = The first intensity to encode. (Input) + * inten2 = The second intensity to encode. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (the encoded number.) + * + * HISTORY + * 11/2004 Mark Armstrong (MDL): Created matching algorithm "code_intensity" + * 11/2004 Arthur Taylor (MDL): Modified to assist with NDFD_WxTable4() + * + * NOTES + ***************************************************************************** + */ +static int NDFD_CodeIntens4 (int inten1, int inten2) +{ + switch (inten2) { + case INT_NOINT: + case INT_UNKNOWN: + case INT_M: + if ((inten1 == INT_NOINT) || (inten1 == INT_UNKNOWN) || + (inten1 == INT_M)) { + return 0; + } else if ((inten1 == INT_D) || (inten1 == INT_DD)) { + return 1; + } + /* Default case */ + /* else if (inten1 == INT_P) */ + return 2; + case INT_D: + case INT_DD: + if ((inten1 == INT_NOINT) || (inten1 == INT_UNKNOWN) || + (inten1 == INT_M)) { + return 3; + } else if ((inten1 == INT_D) || (inten1 == INT_DD)) { + return 4; + } + /* Default case */ + /* else if (inten1 == INT_P) */ + return 5; + case INT_P: + default: + if ((inten1 == INT_NOINT) || (inten1 == INT_UNKNOWN) || + (inten1 == INT_M)) { + return 6; + } else if ((inten1 == INT_D) || (inten1 == INT_DD)) { + return 7; + } + /* Default case */ + /* else if (inten1 == INT_P) */ + return 8; + } +} + +/***************************************************************************** + * NDFD_WxTable4() -- + * + * Original: makeWxImageCodes() Mark Armstrong Nov 2004 (MDL) + * Adapted to NDFD_WxTable4() Arthur Taylor (MDL) + * + * PURPOSE + * To use the same weather table scheme used by Mark Armstrong in + * makeWxImageCodes(). The purpose of both procedures is to simplify the + * weather string (aka ugly string) to a single integral code number, which + * contains the most releavent weather. The intent is to create a simpler + * field which can more readily be viewed as an image. + * + * ARGUMENTS + * ugly = The ugly weather string to encode. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int (the encoded number.) + * + * HISTORY + * 11/2004 Mark Armstrong (MDL): Created "makeWxImageCodes" + * 11/2004 Arthur Taylor (MDL): Created NDFD_WxTable4 + * + * NOTES + * 1) The table used... In the past I have included the table as part of the + * documentation here, but since the table is now > 1000 lines long, I think + * it best to look in "/degrib/data/imageGen/colortable/Wx_200411.colortable" + ***************************************************************************** + */ +static int NDFD_WxTable4 (UglyStringType * ugly) +{ + int code = 0; + int numValid = ugly->numValid; + int cover1 = ugly->cover[1]; + int intens1 = ugly->intens[1]; + + if (numValid > 1) { + if ((ugly->wx[1] != WX_R) && (ugly->wx[1] != WX_S) && + (ugly->wx[1] != WX_RW) && (ugly->wx[1] != WX_SW) && + (ugly->wx[1] != WX_T) && (ugly->wx[1] != WX_ZR) && + (ugly->wx[1] != WX_IP) && (ugly->wx[1] != WX_ZL) && + (ugly->wx[1] != WX_L) && (ugly->wx[1] != WX_F)) { + numValid = 1; + cover1 = COV_UNKNOWN; + intens1 = INT_UNKNOWN; + } + } + + switch (ugly->wx[0]) { + case WX_NOWX: /* NoWx */ + case WX_A: /* Hail */ + case WX_FR: /* Frost */ + code = 0; + break; + case WX_R: /* Rain */ + code = 1; + if (numValid > 1) { + code = 100 + NDFD_Wx2Code4 (ugly->wx[1]); + } + break; + case WX_RW: /* Rain Showers */ + code = 4; + if (numValid > 1) { + code = 200 + NDFD_Wx2Code4 (ugly->wx[1]); + } + break; + case WX_L: /* Drizzle */ + code = 7; + if (numValid > 1) { + code = 300 + NDFD_Wx2Code4 (ugly->wx[1]); + } + break; + case WX_ZL: /* Freezing Drizzle */ + code = 10; + if (numValid > 1) { + code = 400 + NDFD_Wx2Code4 (ugly->wx[1]); + } + break; + case WX_ZR: /* Freezing Rain */ + code = 13; + if (numValid > 1) { + code = 500 + NDFD_Wx2Code4 (ugly->wx[1]); + } + break; + case WX_IP: /* Sleet */ + code = 16; + if (numValid > 1) { + code = 600 + NDFD_Wx2Code4 (ugly->wx[1]); + } + break; + case WX_SW: /* Snow Showers */ + code = 19; + if (numValid > 1) { + code = 700 + NDFD_Wx2Code4 (ugly->wx[1]); + } + break; + case WX_S: /* Snow */ + code = 22; + if (numValid > 1) { + code = 800 + NDFD_Wx2Code4 (ugly->wx[1]); + } + break; + case WX_T: /* Thunderstorms */ + code = 25; + if (numValid > 1) { + code = 900 + NDFD_Wx2Code4 (ugly->wx[1]); + } + break; + case WX_F: /* Fog */ + code = 28; + if (numValid > 1) { + code = 1000 + NDFD_Wx2Code4 (ugly->wx[1]); + } + break; + case WX_K: /* Smoke */ + code = 31; + break; + case WX_BS: /* Blowing Snow */ + code = 32; + break; + case WX_BD: /* Blowing Dust */ + code = 33; + break; + case WX_ZF: /* Freezing Fog */ + code = 34; + break; + case WX_IF: /* Ice Fog */ + code = 35; + break; + case WX_IC: /* Ice Crystals */ + code = 36; + break; + case WX_BN: /* Blowing Sand */ + code = 37; + break; + case WX_ZY: /* Freezing Spray */ + code = 38; + break; + case WX_VA: /* Volcanic Ash */ + code = 39; + break; + case WX_WP: /* Water Spouts */ + code = 40; + break; + case WX_H: /* Haze */ + code = 41; + break; + default: + code = 0; + } /* End of Switch statement. */ + + if ((ugly->wx[0] == WX_R) || (ugly->wx[0] == WX_S) || + (ugly->wx[0] == WX_RW) || (ugly->wx[0] == WX_SW) || + (ugly->wx[0] == WX_T) || (ugly->wx[0] == WX_ZR) || + (ugly->wx[0] == WX_IP) || (ugly->wx[0] == WX_ZL) || + (ugly->wx[0] == WX_L) || (ugly->wx[0] == WX_F)) { + code += NDFD_CodeIntens4 (ugly->intens[0], intens1); + } + + if ((ugly->cover[0] == COV_WIDE) || (ugly->cover[0] == COV_LKLY) || + (ugly->cover[0] == COV_NUM) || (ugly->cover[0] == COV_OCNL) || + (ugly->cover[0] == COV_DEF) || (ugly->cover[0] == COV_AREAS) || + (ugly->cover[0] == COV_PDS) || (ugly->cover[0] == COV_FRQ) || + (ugly->cover[0] == COV_INTER) || (ugly->cover[0] == COV_BRIEF) || + (cover1 == COV_WIDE) || (cover1 == COV_LKLY) || (cover1 == COV_NUM) || + (cover1 == COV_OCNL) || (cover1 == COV_DEF) || + (cover1 == COV_AREAS) || (cover1 == COV_PDS) || (cover1 == COV_FRQ) || + (cover1 == COV_INTER) || (cover1 == COV_BRIEF)) { + code += 1100; + } + + return code; +} + +/***************************************************************************** + * FreeUglyString() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To free the data structure used to hold the parsed ugly string. + * + * ARGUMENTS + * ugly = The ugly string structure to free. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 9/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +void FreeUglyString (UglyStringType * ugly) +{ + int j; /* Used to free all the english words. */ + + for (j = 0; j < NUM_UGLY_ATTRIB; j++) { + free (ugly->english[j]); + } + free (ugly->errors); +} + +/***************************************************************************** + * InitUglyString() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To initialize the structure used to hold the parsed ugly string. + * + * ARGUMENTS + * ugly = The ugly string structure to modify. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 5/2003 Arthur Taylor (MDL/RSIS): Created. + * 7/2003 AAT: Made it initialize whole UglyString structure instead of + * just some of the word in the structure. + * + * NOTES + ***************************************************************************** + */ +static void InitUglyString (UglyStringType * ugly) +{ + int i; /* Used to traverse all the words. */ + int j; /* Used to traverse all the attributes. */ + + ugly->numValid = 0; + ugly->f_valid = 1; + ugly->minVis = 0; + ugly->validIndex = 0; + ugly->SimpleCode = 0; + ugly->errors = NULL; + for (i = 0; i < NUM_UGLY_WORD; i++) { + ugly->wx[i] = 0; + ugly->cover[i] = 0; + ugly->intens[i] = 0; + ugly->vis[i] = VIS_UNKNOWN; + for (j = 0; j < NUM_UGLY_ATTRIB; j++) { + ugly->attrib[i][j] = 0; + } + ugly->f_or[i] = 0; + ugly->f_priority[i] = 0; + ugly->english[i] = NULL; + ugly->wx_inten[i] = 0; + ugly->HazCode[i] = 0; + } +} + +/***************************************************************************** + * FindInTable() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Look through a given table for a particular "phrase". + * + * ARGUMENTS + * table = The table to look in. (Input) + * tableLen = The length of the table (Input) + * data = The string (or phrase) to look for. (Input) + * ans = The index where the string was found. (Output) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 1 = Found it but string is "invalid" or "missing". + * 0 = Found it. + * -1 = Did not find it. + * + * HISTORY + * 5/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +static int FindInTable (WxTable * table, int tableLen, char *data, uChar *ans) +{ + int i; /* Index used to walk through the table. */ + + for (i = 0; i < tableLen; i++) { + if (strcmp (data, table[i].abrev) == 0) { + *ans = i; + return 0; + } + } + if (strcmp (data, "") == 0) { + return 1; + } else { + return -1; + } +} + +/***************************************************************************** + * UglyLookUp() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Determine which table to look in based on how many ':' we have seen in + * the current word. After looking up the data in the appropriate table, + * Places the result in the appropriate places in the UglyStringType data + * structure. + * + * ARGUMENTS + * ugly = The ugly string structure to modify. (Output) + * data = The string (or phrase) to look for. (Input) + * word = Which word we are currently working on. (Input) + * place = What part of the word (ie # of :'s) (Input) + * attNum = What part of attribute piece (ie # of ,'s) (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = No problems + * -1 = 'place' was invalid (larger than 4) + * -2 = Couldn't find the phrase 'data' in the look-up tables. + * + * HISTORY + * 5/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +static int UglyLookUp (UglyStringType * ugly, char *data, uChar word, + uChar place, uChar attNum) +{ + int ans; + + switch (place) { + case 0: /* Cover */ + ans = FindInTable (WxCover, (sizeof (WxCover) / sizeof (WxTable)), + data, &(ugly->cover[word])); + if (ans == 1) { + ugly->f_valid = 0; + return 0; + } else if (ans != 0) { + if (strlen (data) == 0) { + ugly->cover[word] = COV_NOCOV; + } else { + ugly->cover[word] = COV_UNKNOWN; +#ifdef VERBOSE + printf ("No '%s' in WxCover\n", data); +#endif +#ifdef STORE_ERRORS + reallocSprintf (&(ugly->errors), "No '%s' in WxCover ", data); +#endif +/* + return -2; +*/ + } + } + break; + case 1: /* Weather */ + ans = FindInTable (WxCode, (sizeof (WxCode) / sizeof (WxTable)), + data, &(ugly->wx[word])); + if (ans == 1) { + ugly->f_valid = 0; + return 0; + } else if (ans != 0) { + if (strlen (data) == 0) { + ugly->wx[word] = WX_NOWX; + } else { +#ifdef VERBOSE + printf ("No '%s' in WxCode\n", data); +#endif +#ifdef STORE_ERRORS + reallocSprintf (&(ugly->errors), "No '%s' in WxCode ", data); +#endif + return -2; + } + } + break; + case 2: /* Intensity */ + ans = FindInTable (WxIntens, (sizeof (WxIntens) / sizeof (WxTable)), + data, &(ugly->intens[word])); + if (ans == 1) { + ugly->f_valid = 0; + return 0; + } else if (ans != 0) { + if (strlen (data) == 0) { + ugly->intens[word] = INT_NOINT; + } else { +#ifdef VERBOSE + printf ("No '%s' in WxIntens\n", data); +#endif +#ifdef STORE_ERRORS + reallocSprintf (&(ugly->errors), "No '%s' in WxIntens ", data); +#endif + return -2; + } + } + break; + case 3: /* Vis */ + ans = FindInTable (WxVisib, (sizeof (WxVisib) / sizeof (WxTable)), + data, &(ugly->vis[word])); + if (ans == 1) { + ugly->f_valid = 0; + return 0; + } else if (ans != 0) { + if (strlen (data) == 0) { + ugly->vis[word] = 0; + } else { +#ifdef VERBOSE + printf ("No '%s' in WxVisib\n", data); +#endif +#ifdef STORE_ERRORS + reallocSprintf (&(ugly->errors), "No '%s' in WxVisib ", data); +#endif + return -2; + } + } + ugly->vis[word] = atoi (WxVisib[ugly->vis[word]].name); + if (word == 0) { + ugly->minVis = ugly->vis[word]; + } else if (ugly->minVis > ugly->vis[word]) { + ugly->minVis = ugly->vis[word]; + } + break; + case 4: /* Attrib */ + ans = FindInTable (WxAttrib, (sizeof (WxAttrib) / sizeof (WxTable)), + data, &(ugly->attrib[word][attNum])); + if (ans == 1) { + ugly->f_valid = 0; + return 0; + } else if (ans != 0) { +#ifdef VERBOSE + printf ("No '%s' in WxAttrib\n", data); +#endif +#ifdef STORE_ERRORS + reallocSprintf (&(ugly->errors), "No '%s' in WxAttrib ", data); +#endif + return -2; + } else { + /* Check if it is the "OR" or "MX" case. */ + if (ugly->attrib[word][attNum] == HAZ_OR) { + ugly->attrib[word][attNum] = 0; + ugly->f_or[word] = 1; + } else if (ugly->attrib[word][attNum] == HAZ_PRI2) { + ugly->attrib[word][attNum] = 0; + ugly->f_priority[word] = 2; + } else if (ugly->attrib[word][attNum] == HAZ_PRI1) { + ugly->attrib[word][attNum] = 0; + ugly->f_priority[word] = 1; + } + } + break; + default: + return -1; + } + return 0; +} + +/***************************************************************************** + * Ugly2English() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Converts an Ugly string to an English Phrase. + * Example: "Iso:T:::" -> "Isolated Thunderstorms" + * English phrase does not include visibility. + * + * ARGUMENTS + * ugly = The ugly string structure to modify. (Input/Output) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 5/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * 1) buffer size is choosen so each of the 8 parts has 50 bytes for the + * table entry and the ' ' and ', ' and ' with '. If NUM_UGLY_ATTRIB + * increases (from 5), we may need more. + * 2) Instead of static buffer, we could use myerror.c :: AllocSprintf. + ***************************************************************************** + */ +static void Ugly2English (UglyStringType * ugly) +{ + int i; /* Loop counter over number of words. */ + int j; /* Loop counter over number of attributes. */ + char buffer[400]; /* Temporary storage as we build up the phrase. */ + uChar f_first; /* Flag for first attribute. */ + int HazCode[NUM_UGLY_ATTRIB]; /* Sorted list of hazard numbers used to * + * create hazard code. */ + int k; /* A counter used to help sort hazard. */ + int temp; /* A temp variable used to help sort hazard. */ + + for (i = 0; i < ugly->numValid; i++) { + buffer[0] = '\0'; + /* Handle Coverage. */ + if (ugly->cover[i] != 0) { + strcat (buffer, WxCover[ugly->cover[i]].name); + strcat (buffer, " "); + } + /* Handle Intensity. */ + if (ugly->intens[i] != 0) { + strcat (buffer, WxIntens[ugly->intens[i]].name); + strcat (buffer, " "); + } + strcat (buffer, WxCode[ugly->wx[i]].name); + /* Handle Attributes. */ + f_first = 1; + for (j = 0; j < NUM_UGLY_ATTRIB; j++) { + if (ugly->attrib[i][j] != 0) { + if (ugly->f_priority == 0) { + if (f_first) { + strcat (buffer, " with "); + f_first = 0; + } else { + strcat (buffer, ", "); + } + strcat (buffer, WxAttrib[ugly->attrib[i][j]].name); + } + } + } + ugly->english[i] = (char *) malloc ((strlen (buffer) + 1) * + sizeof (char)); + strcpy (ugly->english[i], buffer); + /* Compute a code number for wx&inten, as well as attrib. */ + if (WxCode[ugly->wx[i]].number != 0) { + ugly->wx_inten[i] = 1 + (WxCode[ugly->wx[i]].number - 1) * + (sizeof (WxIntens) / sizeof (WxTable)) + + WxIntens[ugly->intens[i]].number; + } else { + ugly->wx_inten[i] = 0; + } + /* Compute a code number for hazards. */ + for (j = 0; j < NUM_UGLY_ATTRIB; j++) { + HazCode[j] = WxAttrib[ugly->attrib[i][j]].number; + if (HazCode[j] > 250) { + HazCode[j] = 0; + } + } + for (j = 0; j < NUM_UGLY_ATTRIB - 1; j++) { + for (k = j + 1; k < NUM_UGLY_ATTRIB; k++) { + if (HazCode[j] > HazCode[k]) { + temp = HazCode[j]; + HazCode[j] = HazCode[k]; + HazCode[k] = temp; + } + } + } + /* Hazard is now smallest number first... we now convert from "00 00 00 + * 04 05" to 405 */ + ugly->HazCode[i] = 0; + for (j = 0; j < NUM_UGLY_ATTRIB; j++) { + ugly->HazCode[i] = (ugly->HazCode[i] * 100) + HazCode[j]; + } + } +} + +/***************************************************************************** + * ParseUglyString() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Parse an ASCII ugly string describing weather into a data structure + * which is more easily manipulated. + * + * ARGUMENTS + * ugly = The ugly string structure to modify. (Output) + * wxData = The ugly string to parse. (Input) + * simpleVer = The version of the simple Wx table to use. + * (1 is 6/2003 version), (2 is 1/2004 version). (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int + * 0 = No problems + * -1 = Had difficulties parseing the Ugly string. + * + * HISTORY + * 5/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + * 1) Assumes it is ok to modify the wxData ascii string. This means that + * You can NOT pass in constant strings. + ***************************************************************************** + */ +int ParseUglyString (UglyStringType * ugly, char *wxData, int simpleVer) +{ + char *cur; /* Used to help walk though the ascii string. */ + char *start; /* Where current phrase starts. */ + uChar attNum = 0; /* piece of attribute phrase (# of , seen) */ + uChar place = 0; /* location in a word (# of : seen) */ + uChar word = 0; /* Which word in sentence (# of ^ seen) */ + + /* Init first message */ + ugly->SimpleCode = 0; + InitUglyString (ugly); + start = wxData; + for (cur = wxData; *cur != '\0'; cur++) { + switch (*cur) { + case '^': + *cur = '\0'; + if (UglyLookUp (ugly, start, word, place, attNum) != 0) { + *cur = '^'; +#ifdef VERBOSE + printf ("(A) %s\n", wxData); +#endif +#ifdef STORE_ERRORS + reallocSprintf (&(ugly->errors), "(A) '%s'\n", wxData); +#endif + /* Convert what we have to "English phrase" */ + ugly->numValid = word + 1; + Ugly2English (ugly); + if (simpleVer == 1) { + ugly->SimpleCode = NDFD_WxTable1 (ugly); + } else if (simpleVer == 2) { + ugly->SimpleCode = NDFD_WxTable2 (ugly); + } else if (simpleVer == 3) { + ugly->SimpleCode = NDFD_WxTable3 (ugly); + } else { + ugly->SimpleCode = NDFD_WxTable4 (ugly); + } + return -1; + } + *cur = '^'; + word++; + /* Make sure we don't start writing out of bounds. */ + if (word >= NUM_UGLY_WORD) { +#ifdef VERBOSE + printf ("(B) %s\n", wxData); +#endif +#ifdef STORE_ERRORS + reallocSprintf (&(ugly->errors), "(B) '%s'\n", wxData); +#endif + /* Convert what we have to "English phrase" */ +/* + ugly->numValid = word + 1; +*/ + Ugly2English (ugly); + if (simpleVer == 1) { + ugly->SimpleCode = NDFD_WxTable1 (ugly); + } else if (simpleVer == 2) { + ugly->SimpleCode = NDFD_WxTable2 (ugly); + } else if (simpleVer == 3) { + ugly->SimpleCode = NDFD_WxTable3 (ugly); + } else { + ugly->SimpleCode = NDFD_WxTable4 (ugly); + } + return -1; + } + place = 0; + attNum = 0; + start = cur + 1; + break; + case ':': + *cur = '\0'; + if (UglyLookUp (ugly, start, word, place, attNum) != 0) { + *cur = ':'; +#ifdef VERBOSE + printf ("(C) %s\n", wxData); +#endif +#ifdef STORE_ERRORS + reallocSprintf (&(ugly->errors), "(C) '%s'\n", wxData); +#endif + /* Convert what we have to "English phrase" */ + ugly->numValid = word + 1; + Ugly2English (ugly); + if (simpleVer == 1) { + ugly->SimpleCode = NDFD_WxTable1 (ugly); + } else if (simpleVer == 2) { + ugly->SimpleCode = NDFD_WxTable2 (ugly); + } else if (simpleVer == 3) { + ugly->SimpleCode = NDFD_WxTable3 (ugly); + } else { + ugly->SimpleCode = NDFD_WxTable4 (ugly); + } + return -1; + } + *cur = ':'; + place++; + attNum = 0; + start = cur + 1; + break; + case ',': + if (place == 4) { + *cur = '\0'; + if (UglyLookUp (ugly, start, word, place, attNum) != 0) { + *cur = ','; +#ifdef VERBOSE + printf ("(D) %s\n", wxData); +#endif +#ifdef STORE_ERRORS + reallocSprintf (&(ugly->errors), "(D) '%s'\n", wxData); +#endif + /* Convert what we have to "English phrase" */ + ugly->numValid = word + 1; + Ugly2English (ugly); + if (simpleVer == 1) { + ugly->SimpleCode = NDFD_WxTable1 (ugly); + } else if (simpleVer == 2) { + ugly->SimpleCode = NDFD_WxTable2 (ugly); + } else if (simpleVer == 3) { + ugly->SimpleCode = NDFD_WxTable3 (ugly); + } else { + ugly->SimpleCode = NDFD_WxTable4 (ugly); + } + return -1; + } + *cur = ','; + attNum++; + start = cur + 1; + } + break; + default: + break; + } +/* + if (word == 2) { + return; + } +*/ + } + if (start != '\0') { + if (UglyLookUp (ugly, start, word, place, attNum) != 0) { +#ifdef VERBOSE + printf ("(E) '%s'\n", wxData); +#endif +#ifdef STORE_ERRORS + reallocSprintf (&(ugly->errors), "(E) '%s'\n", wxData); +#endif + /* Convert what we have to "English phrase" */ + ugly->numValid = word + 1; + Ugly2English (ugly); + if (simpleVer == 1) { + ugly->SimpleCode = NDFD_WxTable1 (ugly); + } else if (simpleVer == 2) { + ugly->SimpleCode = NDFD_WxTable2 (ugly); + } else if (simpleVer == 3) { + ugly->SimpleCode = NDFD_WxTable3 (ugly); + } else { + ugly->SimpleCode = NDFD_WxTable4 (ugly); + } + return -1; + } + } + + ugly->numValid = word + 1; + /* Convert what we have to "English phrase" */ + Ugly2English (ugly); + if (simpleVer == 1) { + ugly->SimpleCode = NDFD_WxTable1 (ugly); + } else if (simpleVer == 2) { + ugly->SimpleCode = NDFD_WxTable2 (ugly); + } else if (simpleVer == 3) { + ugly->SimpleCode = NDFD_WxTable3 (ugly); + } else { + ugly->SimpleCode = NDFD_WxTable4 (ugly); + } +#ifdef VERBOSE + if (place != 4) { + printf ("Too few ugly words? '%s'\n", wxData); + } +#endif +#ifdef STORE_ERRORS +/* + if (place != 4) { + reallocSprintf (&(ugly->errors), "Too few ugly words? '%s'\n", wxData); + } +*/ +#endif + return 0; +} + +/***************************************************************************** + * PrintUglyString() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * Prints all the relevant data in an ugly string. Mainly for debugging + * purposes. + * + * ARGUMENTS + * ugly = The ugly string structure to print. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: void + * + * HISTORY + * 5/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +void PrintUglyString (UglyStringType * ugly) +{ + int i; /* Used to traverse the ugly string structure. */ + double vis; /* Used to determine if we have "missing" vis. */ + + printf ("numValid %d\n", ugly->numValid); + for (i = 0; i < ugly->numValid; i++) { + if (ugly->vis[i] == VIS_UNKNOWN) { + vis = 9999; + } else { + vis = ugly->vis[i] / 32.; + } + printf ("Wx=%d, Cov=%d, inten=%d, vis=%d, attrib=%d,%d,%d,%d,%d\n", + ugly->wx[i], ugly->cover[i], ugly->intens[i], + ugly->vis[i], ugly->attrib[i][0], ugly->attrib[i][1], + ugly->attrib[i][2], ugly->attrib[i][3], ugly->attrib[i][4]); + printf ("Wx=%s, Cov=%s, intens=%s, vis=%f, attrib=%s,%s,%s,%s,%s\n", + WxCode[ugly->wx[i]].name, WxCover[ugly->cover[i]].name, + WxIntens[ugly->intens[i]].name, vis, + WxAttrib[ugly->attrib[i][0]].name, + WxAttrib[ugly->attrib[i][1]].name, + WxAttrib[ugly->attrib[i][2]].name, + WxAttrib[ugly->attrib[i][3]].name, + WxAttrib[ugly->attrib[i][4]].name); + } + printf ("\n"); +} + +#ifdef DEBUG_WEATHER +/***************************************************************************** + * main() -- + * + * Arthur Taylor / MDL + * + * PURPOSE + * To test the ParseUglyString() procedure. + * + * ARGUMENTS + * argc = The number of arguments on the command line. (Input) + * argv = The arguments on the command line. (Input) + * + * FILES/DATABASES: None + * + * RETURNS: int + * + * HISTORY + * 5/2003 Arthur Taylor (MDL/RSIS): Created. + * + * NOTES + ***************************************************************************** + */ +int main (int argc, char **argv) +{ + UglyStringType ugly; + char buffer[100]; + + strcpy (buffer, "Pds:R:+::Mention^Ocnl:R:m::^Sct:" + "T:::"); + ParseUglyString (&ugly, buffer, 3); + PrintUglyString (&ugly); + if (ugly.errors != NULL) { + printf ("Errors: %s\n", ugly.errors); + } + FreeUglyString (&ugly); + return 0; + + strcpy (buffer, "Sct:SW:-::"); + ParseUglyString (&ugly, buffer, 3); + PrintUglyString (&ugly); + strcpy (buffer, "Ocnl:R:-::^Ocnl:S:-::^SChc:ZR:-::"); + ParseUglyString (&ugly, buffer, 3); + PrintUglyString (&ugly); + strcpy (buffer, "Wide:FR:-::OLA"); + ParseUglyString (&ugly, buffer, 3); + PrintUglyString (&ugly); + strcpy (buffer, "::::"); + ParseUglyString (&ugly, buffer, 3); + PrintUglyString (&ugly); + strcpy (buffer, "Sct:RW:-::^Iso:T:m::"); + ParseUglyString (&ugly, buffer, 3); + PrintUglyString (&ugly); + strcpy (buffer, "Sct:T:+::DmgW,LgA"); + ParseUglyString (&ugly, buffer, 3); + PrintUglyString (&ugly); + return 0; +} +#endif diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/weather.h b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/weather.h new file mode 100644 index 000000000..b93692508 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/degrib/weather.h @@ -0,0 +1,20 @@ +#ifndef WEATHER_H +#define WEATHER_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "meta.h" + +void FreeUglyString (UglyStringType * ugly); + +int ParseUglyString (UglyStringType * ugly, char *wxData, int simpleVer); + +void PrintUglyString (UglyStringType *ugly); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* WEATHER_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/README b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/README new file mode 100644 index 000000000..d17845bf1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/README @@ -0,0 +1,67 @@ + Sep 02, 2003 + W/NP11:SAG + +g2clib Library. + +This library contains "C" decoder/encoder +routines for GRIB edition 2. The user API for the GRIB2 routines +is described in file "grib2c.doc". + +This "C" source code conatins many uses of the C++ +comment style "//". Please make sure you include the +appropriate compiler option in the CFLAGS variable in the +makefile to allow the use of "//" comment indicators. + + +We have added support for PNG and JPEG2000 image compression +algorithms within the GRIB2 standard. If you would like +to compile this library to utilize these GRIB2 Templates, +make sure that -DUSE_PNG and -DUSE_JPEG2000 are specified +in the DEFS variable in the makefile. You will also need +to download and install the external libraries listed below, +if they are not already installed on your system. + +If you do not wish to bother with the external libs and +don't need PNG and JPEG2000 support, you can remove the +-DUSE_PNG and -DUSE_JPEG2000 flags from the DEFS variable +in the makefile. + + +------------------------------------------------------------------------------- + + External Libraries: + +libjasper.a - This library is a C implementation of the JPEG-2000 Part-1 + standard (i.e., ISO/IEC 15444-1). This library is required + if JPEG2000 support in GRIB2 is desired. If not, remove + the -DUSE_JPEG2000 option from the DEFS variable + in the makefile. + + Download version jasper-1.700.2 from the JasPer Project's + home page, http://www.ece.uvic.ca/~mdadams/jasper/. + + More information about JPEG2000 can be found at + http://www.jpeg.org/JPEG2000.html. + +libpng.a This library is a C implementation of the Portable Network + Graphics PNG image compression format. This library is required + if PNG support in GRIB2 is desired. If not, remove + the -DUSE_PNG option from the DEFS variable + in the makefile. + + If not already installed on your system, download version + libpng-1.2.5 from http://www.libpng.org/pub/png/libpng.html. + + More information about PNG can be found at + http://www.libpng.org/pub/png/. + +libz.a This library contains compression/decompression routines + used by libpng.a for PNG image compression support. + This library is required if PNG support in GRIB2 is desired. + If not, remove the -DUSE_PNG option from the DEFS variable + in g2lib/makefile. + + If not already installed on your system, download version + zlib-1.1.4 from http://www.gzip.org/zlib/. + + diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/cmplxpack.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/cmplxpack.c new file mode 100644 index 000000000..4d9908cc0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/cmplxpack.c @@ -0,0 +1,75 @@ +#include "grib2.h" + +void cmplxpack(g2float *fld,g2int ndpts, g2int idrsnum,g2int *idrstmpl, + unsigned char *cpack, g2int *lcpack) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: cmplxpack +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2004-08-27 +// +// ABSTRACT: This subroutine packs up a data field using a complex +// packing algorithm as defined in the GRIB2 documention. It +// supports GRIB2 complex packing templates with or without +// spatial differences (i.e. DRTs 5.2 and 5.3). +// It also fills in GRIB2 Data Representation Template 5.2 or 5.3 +// with the appropriate values. +// +// PROGRAM HISTORY LOG: +// 2004-08-27 Gilbert +// +// USAGE: cmplxpack(g2float *fld,g2int ndpts, g2int idrsnum,g2int *idrstmpl, +// unsigned char *cpack, g2int *lcpack) +// INPUT ARGUMENT LIST: +// fld[] - Contains the data values to pack +// ndpts - The number of data values in array fld[] +// idrsnum - Data Representation Template number 5.N +// Must equal 2 or 3. +// idrstmpl - Contains the array of values for Data Representation +// Template 5.2 or 5.3 +// [0] = Reference value - ignored on input +// [1] = Binary Scale Factor +// [2] = Decimal Scale Factor +// . +// . +// [6] = Missing value management +// [7] = Primary missing value +// [8] = Secondary missing value +// . +// . +// [16] = Order of Spatial Differencing ( 1 or 2 ) +// . +// . +// +// OUTPUT ARGUMENT LIST: +// idrstmpl - Contains the array of values for Data Representation +// Template 5.3 +// [0] = Reference value - set by compack routine. +// [1] = Binary Scale Factor - unchanged from input +// [2] = Decimal Scale Factor - unchanged from input +// . +// . +// cpack - The packed data field (character*1 array) +// lcpack - length of packed field cpack[]. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: IBM SP +// +//$$$ +{ + + + if ( idrstmpl[6] == 0 ) { // No internal missing values + compack(fld,ndpts,idrsnum,idrstmpl,cpack,lcpack); + } + else if ( idrstmpl[6] == 1 || idrstmpl[6] == 2) { + misspack(fld,ndpts,idrsnum,idrstmpl,cpack,lcpack); + } + else { + printf("cmplxpack: Don:t recognize Missing value option."); + *lcpack=-1; + } + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/compack.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/compack.c new file mode 100644 index 000000000..4f9bd2c0f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/compack.c @@ -0,0 +1,416 @@ +#include +#include +#include "grib2.h" + + +void compack(g2float *fld,g2int ndpts,g2int idrsnum,g2int *idrstmpl, + unsigned char *cpack,g2int *lcpack) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: compack +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-11-07 +// +// ABSTRACT: This subroutine packs up a data field using a complex +// packing algorithm as defined in the GRIB2 documention. It +// supports GRIB2 complex packing templates with or without +// spatial differences (i.e. DRTs 5.2 and 5.3). +// It also fills in GRIB2 Data Representation Template 5.2 or 5.3 +// with the appropriate values. +// +// PROGRAM HISTORY LOG: +// 2002-11-07 Gilbert +// +// USAGE: void compack(g2float *fld,g2int ndpts,g2int idrsnum, +// g2int *idrstmpl,unsigned char *cpack,g2int *lcpack) +// +// INPUT ARGUMENTS: +// fld[] - Contains the data values to pack +// ndpts - The number of data values in array fld[] +// idrsnum - Data Representation Template number 5.N +// Must equal 2 or 3. +// idrstmpl - Contains the array of values for Data Representation +// Template 5.2 or 5.3 +// [0] = Reference value - ignored on input +// [1] = Binary Scale Factor +// [2] = Decimal Scale Factor +// . +// . +// [6] = Missing value management +// [7] = Primary missing value +// [8] = Secondary missing value +// . +// . +// [16] = Order of Spatial Differencing ( 1 or 2 ) +// . +// . +// +// OUTPUT ARGUMENTS: +// idrstmpl - Contains the array of values for Data Representation +// Template 5.3 +// [0] = Reference value - set by compack routine. +// [1] = Binary Scale Factor - unchanged from input +// [2] = Decimal Scale Factor - unchanged from input +// . +// . +// cpack - The packed data field +// lcpack - length of packed field cpack. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + + static g2int zero=0; + g2int *ifld,*gref,*glen,*gwidth; + g2int *jmin, *jmax, *lbit; + g2int i,j,n, /* nbits, */ imin,imax,left; + g2int isd,itemp,ilmax,ngwidthref=0,nbitsgwidth=0; + g2int nglenref=0,nglenlast=0,iofst,ival1,ival2; + g2int minsd,nbitsd=0,maxorig,nbitorig,ngroups; + g2int lg,ng,igmax,iwmax,nbitsgref; + g2int glength,grpwidth,nbitsglen=0; + g2int kfildo, minpk, inc, maxgrps, ibit, jbit, kbit, novref, lbitref; + g2int missopt, miss1, miss2, ier; + g2float bscale,dscale,rmax,rmin,temp; + static g2int simple_alg = 0; + static g2float alog2=0.69314718; // ln(2.0) + static g2int one=1; + + bscale=int_power(2.0,-idrstmpl[1]); + dscale=int_power(10.0,idrstmpl[2]); +// +// Find max and min values in the data +// + rmax=fld[0]; + rmin=fld[0]; + for (j=1;j rmax) rmax=fld[j]; + if (fld[j] < rmin) rmin=fld[j]; + } + +// +// If max and min values are not equal, pack up field. +// If they are equal, we have a constant field, and the reference +// value (rmin) is the value for each point in the field and +// set nbits to 0. +// + if (rmin != rmax) { + iofst=0; + ifld=calloc(ndpts,sizeof(g2int)); + gref=calloc(ndpts,sizeof(g2int)); + gwidth=calloc(ndpts,sizeof(g2int)); + glen=calloc(ndpts,sizeof(g2int)); + // + // Scale original data + // + if (idrstmpl[1] == 0) { // No binary scaling + imin=(g2int)RINT(rmin*dscale); + //imax=(g2int)rint(rmax*dscale); + rmin=(g2float)imin; + for (j=0;j0;j--) + ifld[j]=ifld[j]-ifld[j-1]; + ifld[0]=0; + } + else if (idrstmpl[16] == 2) { // second order + ival1=ifld[0]; + ival2=ifld[1]; + for (j=ndpts-1;j>1;j--) + ifld[j]=ifld[j]-(2*ifld[j-1])+ifld[j-2]; + ifld[0]=0; + ifld[1]=0; + } + // + // subtract min value from spatial diff field + // + isd=idrstmpl[16]; + minsd=ifld[isd]; + for (j=isd;jival1) maxorig=ival2; + temp=log((double)(maxorig+1))/alog2; + nbitorig=(g2int)ceil(temp)+1; + if (nbitorig > nbitsd) nbitsd=nbitorig; + // increase number of bits to even multiple of 8 ( octet ) + if ( (nbitsd%8) != 0) nbitsd=nbitsd+(8-(nbitsd%8)); + // + // Store extra spatial differencing info into the packed + // data section. + // + if (nbitsd != 0) { + // pack first original value + if (ival1 >= 0) { + sbit(cpack,&ival1,iofst,nbitsd); + iofst=iofst+nbitsd; + } + else { + sbit(cpack,&one,iofst,1); + iofst=iofst+1; + itemp=abs(ival1); + sbit(cpack,&itemp,iofst,nbitsd-1); + iofst=iofst+nbitsd-1; + } + if (idrstmpl[16] == 2) { + // pack second original value + if (ival2 >= 0) { + sbit(cpack,&ival2,iofst,nbitsd); + iofst=iofst+nbitsd; + } + else { + sbit(cpack,&one,iofst,1); + iofst=iofst+1; + itemp=abs(ival2); + sbit(cpack,&itemp,iofst,nbitsd-1); + iofst=iofst+nbitsd-1; + } + } + // pack overall min of spatial differences + if (minsd >= 0) { + sbit(cpack,&minsd,iofst,nbitsd); + iofst=iofst+nbitsd; + } + else { + sbit(cpack,&one,iofst,1); + iofst=iofst+1; + itemp=abs(minsd); + sbit(cpack,&itemp,iofst,nbitsd-1); + iofst=iofst+nbitsd-1; + } + } + //printf("SDp %ld %ld %ld %ld\n",ival1,ival2,minsd,nbitsd); + } // end of spatial diff section + // + // Determine Groups to be used. + // + if ( simple_alg == 1 ) { + // set group length to 10; calculate number of groups + // and length of last group + ngroups=ndpts/10; + for (j=0;j imax) imax=ifld[j]; + j++; + } + // calc num of bits needed to hold data + if ( gref[ng] != imax ) { + temp=log((double)(imax-gref[ng]+1))/alog2; + gwidth[ng]=(g2int)ceil(temp); + } + else + gwidth[ng]=0; + // Subtract min from data + j=n; + for (lg=0;lg igmax) igmax=gref[j]; + if (igmax != 0) { + temp=log((double)(igmax+1))/alog2; + nbitsgref=(g2int)ceil(temp); + sbits(cpack,gref,iofst,nbitsgref,0,ngroups); + itemp=nbitsgref*ngroups; + iofst=iofst+itemp; + // Pad last octet with Zeros, if necessary, + if ( (itemp%8) != 0) { + left=8-(itemp%8); + sbit(cpack,&zero,iofst,left); + iofst=iofst+left; + } + } + else + nbitsgref=0; + // + // Find max/min of the group widths and calc num of bits needed + // to pack each groups width value, then + // pack up group width values + // + iwmax=gwidth[0]; + ngwidthref=gwidth[0]; + for (j=1;j iwmax) iwmax=gwidth[j]; + if (gwidth[j] < ngwidthref) ngwidthref=gwidth[j]; + } + if (iwmax != ngwidthref) { + temp=log((double)(iwmax-ngwidthref+1))/alog2; + nbitsgwidth=(g2int)ceil(temp); + for (i=0;i ilmax) ilmax=glen[j]; + if (glen[j] < nglenref) nglenref=glen[j]; + } + nglenlast=glen[ngroups-1]; + if (ilmax != nglenref) { + temp=log((double)(ilmax-nglenref+1))/alog2; + nbitsglen=(g2int)ceil(temp); + for (i=0;i +#include +#include "grib2.h" + + +int comunpack(unsigned char *cpack,g2int lensec,g2int idrsnum,g2int *idrstmpl,g2int ndpts,g2float *fld) +////$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: comunpack +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-29 +// +// ABSTRACT: This subroutine unpacks a data field that was packed using a +// complex packing algorithm as defined in the GRIB2 documention, +// using info from the GRIB2 Data Representation Template 5.2 or 5.3. +// Supports GRIB2 complex packing templates with or without +// spatial differences (i.e. DRTs 5.2 and 5.3). +// +// PROGRAM HISTORY LOG: +// 2002-10-29 Gilbert +// 2004-12-16 Gilbert - Added test ( provided by Arthur Taylor/MDL ) +// to verify that group widths and lengths are +// consistent with section length. +// +// USAGE: int comunpack(unsigned char *cpack,g2int lensec,g2int idrsnum, +// g2int *idrstmpl, g2int ndpts,g2float *fld) +// INPUT ARGUMENT LIST: +// cpack - pointer to the packed data field. +// lensec - length of section 7 (used for error checking). +// idrsnum - Data Representation Template number 5.N +// Must equal 2 or 3. +// idrstmpl - pointer to the array of values for Data Representation +// Template 5.2 or 5.3 +// ndpts - The number of data values to unpack +// +// OUTPUT ARGUMENT LIST: +// fld - Contains the unpacked data values. fld must be allocated +// with at least ndpts*sizeof(g2float) bytes before +// calling this routine. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$// +{ + + g2int nbitsd=0,isign; + g2int j,iofst,ival1,ival2,minsd,itemp,l,k,n,non=0; + g2int *ifld,*ifldmiss=0; + g2int *gref,*gwidth,*glen; + g2int itype,ngroups,nbitsgref,nbitsgwidth,nbitsglen; + g2int msng1,msng2; + g2float ref,bscale,dscale,rmiss1,rmiss2; + g2int totBit, totLen; + + //printf('IDRSTMPL: ',(idrstmpl(j),j=1,16) + rdieee(idrstmpl+0,&ref,1); +// printf("SAGTref: %f\n",ref); + bscale = (g2float)int_power(2.0,idrstmpl[1]); + dscale = (g2float)int_power(10.0,-idrstmpl[2]); + nbitsgref = idrstmpl[3]; + itype = idrstmpl[4]; + ngroups = idrstmpl[9]; + nbitsgwidth = idrstmpl[11]; + nbitsglen = idrstmpl[15]; + if (idrsnum == 3) + nbitsd=idrstmpl[17]*8; + + // Constant field + + if (ngroups == 0) { + for (j=0;j lensec) { + return 1; + } +// +// For each group, unpack data values +// + if ( idrstmpl[6] == 0 ) { // no missing values + n=0; + for (j=0;j + +#include "grib2.h" + +#include +#include +#include + +/* -------------------------------------------------------------------- */ +/* ==================================================================== */ +/* We prefer to use JasPer directly if it is available. If not */ +/* we fallback on calling back to GDAL to try and process the */ +/* jpeg2000 chunks. */ +/* ==================================================================== */ +/* -------------------------------------------------------------------- */ + +#ifdef HAVE_JASPER +#include +#define JAS_1_700_2 +#else +#include +#include +#endif + +CPL_C_START +// Cripes ... shouldn't this go in an include files! +int dec_jpeg2000(char *injpc,g2int bufsize,g2int *outfld); +CPL_C_END + +int dec_jpeg2000(char *injpc,g2int bufsize,g2int *outfld) +/*$$$ SUBPROGRAM DOCUMENTATION BLOCK +* . . . . +* SUBPROGRAM: dec_jpeg2000 Decodes JPEG2000 code stream +* PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-12-02 +* +* ABSTRACT: This Function decodes a JPEG2000 code stream specified in the +* JPEG2000 Part-1 standard (i.e., ISO/IEC 15444-1) using JasPer +* Software version 1.500.4 (or 1.700.2) written by the University of British +* Columbia and Image Power Inc, and others. +* JasPer is available at http://www.ece.uvic.ca/~mdadams/jasper/. +* +* PROGRAM HISTORY LOG: +* 2002-12-02 Gilbert +* +* USAGE: int dec_jpeg2000(char *injpc,g2int bufsize,g2int *outfld) +* +* INPUT ARGUMENTS: +* injpc - Input JPEG2000 code stream. +* bufsize - Length (in bytes) of the input JPEG2000 code stream. +* +* OUTPUT ARGUMENTS: +* outfld - Output matrix of grayscale image values. +* +* RETURN VALUES : +* 0 = Successful decode +* -3 = Error decode jpeg2000 code stream. +* -5 = decoded image had multiple color components. +* Only grayscale is expected. +* +* REMARKS: +* +* Requires JasPer Software version 1.500.4 or 1.700.2 +* +* ATTRIBUTES: +* LANGUAGE: C +* MACHINE: IBM SP +* +*$$$*/ + +{ +#ifndef HAVE_JASPER + // J2K_SUBFILE method + + // create "memory file" from buffer + int fileNumber = 0; + VSIStatBufL sStatBuf; + CPLString osFileName = "/vsimem/work.jpc"; + + // ensure we don't overwrite an existing file accidentally + while ( VSIStatL( osFileName, &sStatBuf ) == 0 ) { + osFileName.Printf( "/vsimem/work%d.jpc", ++fileNumber ); + } + + VSIFCloseL( VSIFileFromMemBuffer( + osFileName, (unsigned char*)injpc, bufsize, + FALSE ) ); // TRUE to let vsi delete the buffer when done + + // Open memory buffer for reading + GDALDataset* poJ2KDataset = (GDALDataset *) + GDALOpen( osFileName, GA_ReadOnly ); + + if( poJ2KDataset == NULL ) + { + printf("dec_jpeg2000: Unable to open JPEG2000 image within GRIB file.\n" + "Is the JPEG2000 driver available?" ); + return -3; + } + + if( poJ2KDataset->GetRasterCount() != 1 ) + { + printf("dec_jpeg2000: Found color image. Grayscale expected.\n"); + return (-5); + } + + // Fulfill administration: initialize parameters required for RasterIO + int nXSize = poJ2KDataset->GetRasterXSize(); + int nYSize = poJ2KDataset->GetRasterYSize(); + int nXOff = 0; + int nYOff = 0; + int nBufXSize = nXSize; + int nBufYSize = nYSize; + GDALDataType eBufType = GDT_Int32; // map to type of "outfld" buffer: g2int* + int nBandCount = 1; + int* panBandMap = NULL; + int nPixelSpace = 0; + int nLineSpace = 0; + int nBandSpace = 0; + + // Decompress the JPEG2000 into the output integer array. + poJ2KDataset->RasterIO( GF_Read, nXOff, nYOff, nXSize, nYSize, + outfld, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, NULL ); + + // close source file, and "unlink" it. + GDALClose( poJ2KDataset ); + VSIUnlink( osFileName ); + + return 0; + +#else + + // JasPer method + + int ier; + g2int i,j,k; + jas_image_t *image=0; + jas_stream_t *jpcstream; + jas_image_cmpt_t *pcmpt; + char *opts=0; + jas_matrix_t *data; + +// jas_init(); + + ier=0; +// +// Create jas_stream_t containing input JPEG200 codestream in memory. +// + + jpcstream=jas_stream_memopen(injpc,bufsize); + +// +// Decode JPEG200 codestream into jas_image_t structure. +// + + image=jpc_decode(jpcstream,opts); + if ( image == 0 ) { + printf(" jpc_decode return = %d \n",ier); + return -3; + } + + pcmpt=image->cmpts_[0]; + +// Expecting jpeg2000 image to be grayscale only. +// No color components. +// + if (image->numcmpts_ != 1 ) { + printf("dec_jpeg2000: Found color image. Grayscale expected.\n"); + return (-5); + } + +// +// Create a data matrix of grayscale image values decoded from +// the jpeg2000 codestream. +// + data=jas_matrix_create(jas_image_height(image), jas_image_width(image)); + jas_image_readcmpt(image,0,0,0,jas_image_width(image), + jas_image_height(image),data); +// +// Copy data matrix to output integer array. +// + k=0; + for (i=0;iheight_;i++) + for (j=0;jwidth_;j++) + outfld[k++]=data->rows_[i][j]; +// +// Clean up JasPer work structures. +// + jas_matrix_destroy(data); + ier=jas_stream_close(jpcstream); + jas_image_destroy(image); + + return 0; +#endif +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/dec_png.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/dec_png.c new file mode 100644 index 000000000..4ae027c74 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/dec_png.c @@ -0,0 +1,138 @@ +#include "grib2.h" +#ifndef USE_PNG +int dec_png(unsigned char *pngbuf,g2int *width,g2int *height,char *cout){return 0;} +#else /* USE_PNG */ +#include +#include +#include +#include + + +struct png_stream { + unsigned char *stream_ptr; /* location to write PNG stream */ + g2int stream_len; /* number of bytes written */ +}; +typedef struct png_stream png_stream; + +void user_read_data(png_structp , png_bytep , png_uint_32 ); + +void user_read_data(png_structp png_ptr,png_bytep data, png_uint_32 length) +/* + Custom read function used so that libpng will read a PNG stream + from memory instead of a file on disk. +*/ +{ + char *ptr; + g2int offset; + png_stream *mem; + + mem=(png_stream *)png_get_io_ptr(png_ptr); + ptr=(void *)mem->stream_ptr; + offset=mem->stream_len; +/* printf("SAGrd %ld %ld %x\n",offset,length,ptr); */ + memcpy(data,ptr+offset,length); + mem->stream_len += length; +} + + + +int dec_png(unsigned char *pngbuf,g2int *width,g2int *height,char *cout) +{ + int interlace,color,compres,filter,bit_depth; + g2int j,k,n,bytes,clen; + png_structp png_ptr; + png_infop info_ptr,end_info; + png_bytepp row_pointers; + png_stream read_io_ptr; + +/* check if stream is a valid PNG format */ + + if ( png_sig_cmp(pngbuf,0,8) != 0) + return (-3); + +/* create and initialize png_structs */ + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, + NULL, NULL); + if (!png_ptr) + return (-1); + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr,(png_infopp)NULL,(png_infopp)NULL); + return (-2); + } + + end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + png_destroy_read_struct(&png_ptr,(png_infopp)info_ptr,(png_infopp)NULL); + return (-2); + } + +/* Set Error callback */ + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr,&end_info); + return (-3); + } + +/* Initialize info for reading PNG stream from memory */ + + read_io_ptr.stream_ptr=(png_voidp)pngbuf; + read_io_ptr.stream_len=0; + +/* Set new custom read function */ + + png_set_read_fn(png_ptr,(voidp)&read_io_ptr,(png_rw_ptr)user_read_data); +/* png_init_io(png_ptr, fptr); */ + +/* Read and decode PNG stream */ + + png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); + +/* Get pointer to each row of image data */ + + row_pointers = png_get_rows(png_ptr, info_ptr); + +/* Get image info, such as size, depth, colortype, etc... */ + + /*printf("SAGT:png %d %d %d\n",info_ptr->width,info_ptr->height,info_ptr->bit_depth);*/ + (void)png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *)width, (png_uint_32 *)height, + &bit_depth, &color, &interlace, &compres, &filter); + +/* Check if image was grayscale */ + +/* + if (color != PNG_COLOR_TYPE_GRAY ) { + fprintf(stderr,"dec_png: Grayscale image was expected. \n"); + } +*/ + if ( color == PNG_COLOR_TYPE_RGB ) { + bit_depth=24; + } + else if ( color == PNG_COLOR_TYPE_RGB_ALPHA ) { + bit_depth=32; + } +/* Copy image data to output string */ + + n=0; + bytes=bit_depth/8; + clen=(*width)*bytes; + for (j=0;j<*height;j++) { + for (k=0;k +#include "grib2.h" +#include "drstemplates.h" + + +static const struct drstemplate templatesdrs[MAXDRSTEMP] = { + // 5.0: Grid point data - Simple Packing + { 0, 5, 0, {4,-2,-2,1,1} }, + // 5.2: Grid point data - Complex Packing + { 2, 16, 0, {4,-2,-2,1,1,1,1,4,4,4,1,1,4,1,4,1} }, + // 5.3: Grid point data - Complex Packing and spatial differencing + { 3, 18, 0, {4,-2,-2,1,1,1,1,4,4,4,1,1,4,1,4,1,1,1} }, + // 5.50: Spectral Data - Simple Packing + { 50, 5, 0, {4,-2,-2,1,4} }, + // 5.51: Spherical Harmonics data - Complex packing + { 51, 10, 0, {4,-2,-2,1,-4,2,2,2,4,1} }, +// // 5.1: Matrix values at gridpoint - Simple packing +// { 1, 15, 1, {4,-2,-2,1,1,1,4,2,2,1,1,1,1,1,1} }, + // 5.40: Grid point data - JPEG2000 encoding + { 40, 7, 0, {4,-2,-2,1,1,1,1} }, + // 5.41: Grid point data - PNG encoding + { 41, 5, 0, {4,-2,-2,1,1} }, + // 5.40000: Grid point data - JPEG2000 encoding + { 40000, 7, 0, {4,-2,-2,1,1,1,1} }, + // 5.40010: Grid point data - PNG encoding + { 40010, 5, 0, {4,-2,-2,1,1} } + } ; + +const struct drstemplate *get_templatesdrs() +{ + return templatesdrs; +} + + +g2int getdrsindex(g2int number) +/*!$$$ SUBPROGRAM DOCUMENTATION BLOCK +! . . . . +! SUBPROGRAM: getdrsindex +! PRGMMR: Gilbert ORG: W/NP11 DATE: 2001-06-28 +! +! ABSTRACT: This function returns the index of specified Data +! Representation Template 5.NN (NN=number) in array templates. +! +! PROGRAM HISTORY LOG: +! 2001-06-28 Gilbert +! +! USAGE: index=getdrsindex(number) +! INPUT ARGUMENT LIST: +! number - NN, indicating the number of the Data Representation +! Template 5.NN that is being requested. +! +! RETURNS: Index of DRT 5.NN in array templates, if template exists. +! = -1, otherwise. +! +! REMARKS: None +! +! ATTRIBUTES: +! LANGUAGE: C +! MACHINE: IBM SP +! +!$$$*/ +{ + g2int j,getdrsindex=-1; + + for (j=0;jtype=5; + new->num=templatesdrs[index].template_num; + new->maplen=templatesdrs[index].mapdrslen; + new->needext=templatesdrs[index].needext; + new->map=(g2int *)templatesdrs[index].mapdrs; + new->extlen=0; + new->ext=0; //NULL + return(new); + } + else { + printf("getdrstemplate: DRS Template 5.%d not defined.\n",(int)number); + return(0); //NULL + } + + return(0); //NULL +} + +xxtemplate *extdrstemplate(g2int number,g2int *list) +/*!$$$ SUBPROGRAM DOCUMENTATION BLOCK +! . . . . +! SUBPROGRAM: extdrstemplate +! PRGMMR: Gilbert ORG: W/NP11 DATE: 2000-05-11 +! +! ABSTRACT: This subroutine generates the remaining octet map for a +! given Data Representation Template, if required. Some Templates can +! vary depending on data values given in an earlier part of the +! Template, and it is necessary to know some of the earlier entry +! values to generate the full octet map of the Template. +! +! PROGRAM HISTORY LOG: +! 2000-05-11 Gilbert +! +! USAGE: new=extdrstemplate(number,list); +! INPUT ARGUMENT LIST: +! number - NN, indicating the number of the Data Representation +! Template 5.NN that is being requested. +! list() - The list of values for each entry in the +! the Data Representation Template 5.NN. +! +! RETURN VALUE: +! - Pointer to the returned template struct. +! Returns NULL pointer, if template not found. +! +! ATTRIBUTES: +! LANGUAGE: C +! MACHINE: IBM SP +! +!$$$*/ +{ + xxtemplate *new; + g2int index,i; + + index=getdrsindex(number); + if (index == -1) return(0); + + new=getdrstemplate(number); + + if ( ! new->needext ) return(new); + + if ( number == 1 ) { + new->extlen=list[10]+list[12]; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + for (i=0;iextlen;i++) { + new->ext[i]=4; + } + } + return(new); + +} + diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/drstemplates.h b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/drstemplates.h new file mode 100644 index 000000000..845586a09 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/drstemplates.h @@ -0,0 +1,48 @@ +#ifndef _drstemplates_H +#define _drstemplates_H +#include "grib2.h" + +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-26 +// +// ABSTRACT: This Fortran Module contains info on all the available +// GRIB2 Data Representation Templates used in Section 5 (DRS). +// The information decribing each template is stored in the +// drstemplate structure defined below. +// +// Each Template has three parts: The number of entries in the template +// (mapdrslen); A map of the template (mapdrs), which contains the +// number of octets in which to pack each of the template values; and +// a logical value (needext) that indicates whether the Template needs +// to be extended. In some cases the number of entries in a template +// can vary depending upon values specified in the "static" part of +// the template. ( See Template 5.1 as an example ) +// +// NOTE: Array mapdrs contains the number of octets in which the +// corresponding template values will be stored. A negative value in +// mapdrs is used to indicate that the corresponding template entry can +// contain negative values. This information is used later when packing +// (or unpacking) the template data values. Negative data values in GRIB +// are stored with the left most bit set to one, and a negative number +// of octets value in mapdrs[] indicates that this possibility should +// be considered. The number of octets used to store the data value +// in this case would be the absolute value of the negative value in +// mapdrs[]. +// +// +/////////////////////////////////////////////////////////////////////// + + #define MAXDRSTEMP 9 // maximum number of templates + #define MAXDRSMAPLEN 200 // maximum template map length + + struct drstemplate + { + g2int template_num; + g2int mapdrslen; + g2int needext; + g2int mapdrs[MAXDRSMAPLEN]; + }; + +const struct drstemplate *get_templatesdrs(); +g2int getdrsindex(g2int number); + +#endif /* _drstemplates_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/enc_jpeg2000.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/enc_jpeg2000.c new file mode 100644 index 000000000..c6076cf12 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/enc_jpeg2000.c @@ -0,0 +1,207 @@ +#include "grib2.h" + +#include "cpl_port.h" + +#ifndef USE_JPEG2000 +int enc_jpeg2000(CPL_UNUSED unsigned char *cin, + CPL_UNUSED g2int width, + CPL_UNUSED g2int height, + CPL_UNUSED g2int nbits, + CPL_UNUSED g2int ltype, + CPL_UNUSED g2int ratio, + CPL_UNUSED g2int retry, + CPL_UNUSED char *outjpc, + CPL_UNUSED g2int jpclen) { return 0; } + +#else /* USE_JPEG2000 */ + +#include +#include + +#ifdef USE_JPEG2000_J2KSUBFILE +// J2KSUBFILE includes .. TODO!! +#else +#include +#define JAS_1_700_2 +#endif /* USE_JPEG2000_J2KSUBFILE */ + +int enc_jpeg2000(unsigned char *cin,g2int width,g2int height,g2int nbits, + g2int ltype, g2int ratio, g2int retry, char *outjpc, + g2int jpclen) +/*$$$ SUBPROGRAM DOCUMENTATION BLOCK +* . . . . +* SUBPROGRAM: enc_jpeg2000 Encodes JPEG2000 code stream +* PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-12-02 +* +* ABSTRACT: This Function encodes a grayscale image into a JPEG2000 code stream +* specified in the JPEG2000 Part-1 standard (i.e., ISO/IEC 15444-1) +* using JasPer Software version 1.500.4 (or 1.700.2 ) written by the +* University of British Columbia, Image Power Inc, and others. +* JasPer is available at http://www.ece.uvic.ca/~mdadams/jasper/. +* +* PROGRAM HISTORY LOG: +* 2002-12-02 Gilbert +* 2004-12-16 Gilbert - Added retry argument/option to allow option of +* increasing the maximum number of guard bits to the +* JPEG2000 algorithm. +* +* USAGE: int enc_jpeg2000(unsigned char *cin,g2int width,g2int height, +* g2int nbits, g2int ltype, g2int ratio, +* g2int retry, char *outjpc, g2int jpclen) +* +* INPUT ARGUMENTS: +* cin - Packed matrix of Grayscale image values to encode. +* width - width of image +* height - height of image +* nbits - depth (in bits) of image. i.e number of bits +* used to hold each data value +* ltype - indicator of lossless or lossy compression +* = 1, for lossy compression +* != 1, for lossless compression +* ratio - target compression ratio. (ratio:1) +* Used only when ltype == 1. +* retry - Pointer to option type. +* 1 = try increasing number of guard bits +* otherwise, no additional options +* jpclen - Number of bytes allocated for new JPEG2000 code stream in +* outjpc. +* +* INPUT ARGUMENTS: +* outjpc - Output encoded JPEG2000 code stream +* +* RETURN VALUES : +* > 0 = Length in bytes of encoded JPEG2000 code stream +* -3 = Error decode jpeg2000 code stream. +* -5 = decoded image had multiple color components. +* Only grayscale is expected. +* +* REMARKS: +* +* Requires JasPer Software version 1.500.4 or 1.700.2 +* +* ATTRIBUTES: +* LANGUAGE: C +* MACHINE: IBM SP +* +*$$$*/ +{ + +#ifdef USE_JPEG2000_J2KSUBFILE + + // J2KSUBFILE method ... TODO!! + return 0; + +#else /* USE_JPEG2000_J2KSUBFILE */ + + // JasPer method + + int ier,rwcnt; + jas_image_t image; + jas_stream_t *jpcstream,*istream; + jas_image_cmpt_t cmpt,*pcmpt; +#define MAXOPTSSIZE 1024 + char opts[MAXOPTSSIZE]; + +/* + printf(" enc_jpeg2000:width %ld\n",width); + printf(" enc_jpeg2000:height %ld\n",height); + printf(" enc_jpeg2000:nbits %ld\n",nbits); + printf(" enc_jpeg2000:jpclen %ld\n",jpclen); +*/ +// jas_init(); + +// +// Set lossy compression options, if requested. +// + if ( ltype != 1 ) { + opts[0]=(char)0; + } + else { + sprintf(opts,"mode=real\nrate=%f",1.0/(float)ratio); + } + if ( retry == 1 ) { // option to increase number of guard bits + strcat(opts,"\nnumgbits=4"); + } + //printf("SAGopts: %s\n",opts); + +// +// Initialize the JasPer image structure describing the grayscale +// image to encode into the JPEG2000 code stream. +// + image.tlx_=0; + image.tly_=0; +#ifdef JAS_1_500_4 + image.brx_=(uint_fast32_t)width; + image.bry_=(uint_fast32_t)height; +#endif +#ifdef JAS_1_700_2 + image.brx_=(jas_image_coord_t)width; + image.bry_=(jas_image_coord_t)height; +#endif + image.numcmpts_=1; + image.maxcmpts_=1; +#ifdef JAS_1_500_4 + image.colormodel_=JAS_IMAGE_CM_GRAY; /* grayscale Image */ +#endif +#ifdef JAS_1_700_2 + image.clrspc_=JAS_CLRSPC_SGRAY; /* grayscale Image */ + image.cmprof_=0; +#endif + image.inmem_=1; + + cmpt.tlx_=0; + cmpt.tly_=0; + cmpt.hstep_=1; + cmpt.vstep_=1; +#ifdef JAS_1_500_4 + cmpt.width_=(uint_fast32_t)width; + cmpt.height_=(uint_fast32_t)height; +#endif +#ifdef JAS_1_700_2 + cmpt.width_=(jas_image_coord_t)width; + cmpt.height_=(jas_image_coord_t)height; + cmpt.type_=JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y); +#endif + cmpt.prec_=nbits; + cmpt.sgnd_=0; + cmpt.cps_=(nbits+7)/8; + + pcmpt=&cmpt; + image.cmpts_=&pcmpt; + +// +// Open a JasPer stream containing the input grayscale values +// + istream=jas_stream_memopen((char *)cin,height*width*cmpt.cps_); + cmpt.stream_=istream; + +// +// Open an output stream that will contain the encoded jpeg2000 +// code stream. +// + jpcstream=jas_stream_memopen(outjpc,(int)jpclen); + +// +// Encode image. +// + ier=jpc_encode(&image,jpcstream,opts); + if ( ier != 0 ) { + printf(" jpc_encode return = %d \n",ier); + return -3; + } +// +// Clean up JasPer work structures. +// + rwcnt=jpcstream->rwcnt_; + ier=jas_stream_close(istream); + ier=jas_stream_close(jpcstream); +// +// Return size of jpeg2000 code stream +// + return (rwcnt); + +#endif /* USE_JPEG2000_J2KSUBFILE */ + +} + +#endif /* USE_JPEG2000 */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/enc_png.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/enc_png.c new file mode 100644 index 000000000..0e4afde52 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/enc_png.c @@ -0,0 +1,134 @@ +#include "grib2.h" +#ifndef USE_PNG +int enc_png(char *data,g2int width,g2int height,g2int nbits,char *pngbuf){return 0;} +#else /* USE_PNG */ + +#include +#include +#include +#include + + +struct png_stream { + unsigned char *stream_ptr; /* location to write PNG stream */ + g2int stream_len; /* number of bytes written */ +}; +typedef struct png_stream png_stream; + +void user_write_data(png_structp ,png_bytep , png_uint_32 ); +void user_flush_data(png_structp ); + +void user_write_data(png_structp png_ptr,png_bytep data, png_uint_32 length) +/* + Custom write function used to that libpng will write + to memory location instead of a file on disk +*/ +{ + unsigned char *ptr; + g2int offset; + png_stream *mem; + + mem=(png_stream *)png_get_io_ptr(png_ptr); + ptr=mem->stream_ptr; + offset=mem->stream_len; +/* printf("SAGwr %ld %ld %x\n",offset,length,ptr); */ + /*for (j=offset,k=0;kstream_len += length; +} + + +void user_flush_data(png_structp png_ptr) +/* + Dummy Custom flush function +*/ +{ + int *do_nothing; + do_nothing=NULL; +} + + +int enc_png(char *data,g2int width,g2int height,g2int nbits,char *pngbuf) +{ + + int color_type; + g2int j,bytes,pnglen,bit_depth; + png_structp png_ptr; + png_infop info_ptr; +// png_bytep *row_pointers[height]; + png_bytep **row_pointers; + png_stream write_io_ptr; + +/* create and initialize png_structs */ + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, + NULL, NULL); + if (!png_ptr) + return (-1); + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr,(png_infopp)NULL); + return (-2); + } + +/* Set Error callback */ + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_write_struct(&png_ptr, &info_ptr); + return (-3); + } + +/* Initialize info for writing PNG stream to memory */ + + write_io_ptr.stream_ptr=(png_voidp)pngbuf; + write_io_ptr.stream_len=0; + +/* Set new custom write functions */ + + png_set_write_fn(png_ptr,(voidp)&write_io_ptr,(png_rw_ptr)user_write_data, + (png_flush_ptr)user_flush_data); +/* png_init_io(png_ptr, fptr); */ +/* png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); */ + +/* Set the image size, colortype, filter type, etc... */ + +/* printf("SAGTsettingIHDR %d %d %d\n",width,height,bit_depth); */ + bit_depth=nbits; + color_type=PNG_COLOR_TYPE_GRAY; + if (nbits == 24 ) { + bit_depth=8; + color_type=PNG_COLOR_TYPE_RGB; + } + else if (nbits == 32 ) { + bit_depth=8; + color_type=PNG_COLOR_TYPE_RGB_ALPHA; + } + png_set_IHDR(png_ptr, info_ptr, width, height, + bit_depth, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + +/* Put image data into the PNG info structure */ + + /*bytes=bit_depth/8;*/ + bytes=nbits/8; + row_pointers=malloc(height*sizeof(png_bytep)); + for (j=0;j +#include +#include "grib2.h" + +g2int getdim(unsigned char *,g2int *,g2int *,g2int *); +g2int getpoly(unsigned char *,g2int *,g2int *,g2int *); +void simpack(g2float *, g2int, g2int *, unsigned char *, g2int *); +void cmplxpack(g2float *, g2int, g2int, g2int *, unsigned char *, g2int *); +void specpack(g2float *,g2int,g2int,g2int,g2int,g2int *,unsigned char *, + g2int *); +void jpcpack(g2float *,g2int,g2int,g2int *,unsigned char *,g2int *); +#ifdef USE_PNG + void pngpack(g2float *,g2int,g2int,g2int *,unsigned char *,g2int *); +#endif /* USE_PNG */ + + + +g2int g2_addfield(unsigned char *cgrib,g2int ipdsnum,g2int *ipdstmpl, + g2float *coordlist,g2int numcoord,g2int idrsnum,g2int *idrstmpl, + g2float *fld,g2int ngrdpts,g2int ibmap,g2int *bmap) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_addfield +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-11-05 +// +// ABSTRACT: This routine packs up Sections 4 through 7 for a given field +// and adds them to a GRIB2 message. They are Product Definition Section, +// Data Representation Section, Bit-Map Section and Data Section, +// respectively. +// This routine is used with routines "g2_create", "g2_addlocal", +// "g2_addgrid", and "g2_gribend" to create a complete GRIB2 message. +// g2_create must be called first to initialize a new GRIB2 message. +// Also, routine g2_addgrid must be called after g2_create and +// before this routine to add the appropriate grid description to +// the GRIB2 message. Also, a call to g2_gribend is required to complete +// GRIB2 message after all fields have been added. +// +// PROGRAM HISTORY LOG: +// 2002-11-05 Gilbert +// 2002-12-23 Gilbert - Added complex spherical harmonic packing +// 2003-08-27 Gilbert - Added support for new templates using +// PNG and JPEG2000 algorithms/templates. +// 2004-11-29 Gilbert - JPEG2000 now allowed to use WMO Template no. 5.40 +// PNG now allowed to use WMO Template no. 5.41 +// - Added check to determine if packing algorithm failed. +// 2005-05-10 Gilbert - Imposed minimum size on cpack, used to hold encoded +// bit string. +// +// USAGE: int g2_addfield(unsigned char *cgrib,g2int ipdsnum,g2int *ipdstmpl, +// g2float *coordlist,g2int numcoord,g2int idrsnum,g2int *idrstmpl, +// g2float *fld,g2int ngrdpts,g2int ibmap,g2int *bmap) +// INPUT ARGUMENT LIST: +// cgrib - Char array that contains the GRIB2 message to which sections +// 4 through 7 should be added. +// ipdsnum - Product Definition Template Number ( see Code Table 4.0) +// ipdstmpl - Contains the data values for the specified Product Definition +// Template ( N=ipdsnum ). Each element of this integer +// array contains an entry (in the order specified) of Product +// Defintion Template 4.N +// coordlist- Array containg floating point values intended to document +// the vertical discretisation associated to model data +// on hybrid coordinate vertical levels. +// numcoord - number of values in array coordlist. +// idrsnum - Data Representation Template Number ( see Code Table 5.0 ) +// idrstmpl - Contains the data values for the specified Data Representation +// Template ( N=idrsnum ). Each element of this integer +// array contains an entry (in the order specified) of Data +// Representation Template 5.N +// Note that some values in this template (eg. reference +// values, number of bits, etc...) may be changed by the +// data packing algorithms. +// Use this to specify scaling factors and order of +// spatial differencing, if desired. +// fld[] - Array of data points to pack. +// ngrdpts - Number of data points in grid. +// i.e. size of fld and bmap. +// ibmap - Bitmap indicator ( see Code Table 6.0 ) +// 0 = bitmap applies and is included in Section 6. +// 1-253 = Predefined bitmap applies +// 254 = Previously defined bitmap applies to this field +// 255 = Bit map does not apply to this product. +// bmap[] - Integer array containing bitmap to be added. ( if ibmap=0 ) +// +// OUTPUT ARGUMENT LIST: +// cgrib - Character array to contain the updated GRIB2 message. +// Must be allocated large enough to store the entire +// GRIB2 message. +// +// RETURN VALUES: +// ierr - Return code. +// > 0 = Current size of updated GRIB2 message +// -1 = GRIB message was not initialized. Need to call +// routine g2_create first. +// -2 = GRIB message already complete. Cannot add new section. +// -3 = Sum of Section byte counts doesn't add to total byte count +// -4 = Previous Section was not 3 or 7. +// -5 = Could not find requested Product Definition Template. +// -6 = Section 3 (GDS) not previously defined in message +// -7 = Tried to use unsupported Data Representationi Template +// -8 = Specified use of a previously defined bitmap, but one +// does not exist in the GRIB message. +// -9 = GDT of one of 5.50 through 5.53 required to pack field +// using DRT 5.51. +// -10 = Error packing data field. +// +// REMARKS: Note that the Sections 4 through 7 can only follow +// Section 3 or Section 7 in a GRIB2 message. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + g2int ierr; + static unsigned char G=0x47; // 'G' + static unsigned char R=0x52; // 'R' + static unsigned char I=0x49; // 'I' + static unsigned char B=0x42; // 'B' + static unsigned char s7=0x37; // '7' + + unsigned char *cpack; + static g2int zero=0,one=1,four=4,five=5,six=6,seven=7; + const g2int minsize=50000; + g2int iofst,ibeg,lencurr,len,nsize; + g2int ilen,isecnum,i,nbits,temp,left; + g2int ibmprev,j,lcpack,ioctet,newlen,ndpts; + g2int lensec4,lensec5,lensec6,lensec7; + g2int issec3,isprevbmap,lpos3=0,JJ,KK,MM; + g2int *coordieee; + g2int width,height,iscan,itemp; + g2float *pfld; + xxtemplate *mappds,*mapdrs; + unsigned int allones=4294967295u; + + ierr=0; +// +// Check to see if beginning of GRIB message exists +// + if ( cgrib[0]!=G || cgrib[1]!=R || cgrib[2]!=I || cgrib[3]!=B ) { + printf("g2_addfield: GRIB not found in given message.\n"); + printf("g2_addfield: Call to routine g2_create required to initialize GRIB messge.\n"); + ierr=-1; + return(ierr); + } +// +// Get current length of GRIB message +// + gbit(cgrib,&lencurr,96,32); +// +// Check to see if GRIB message is already complete +// + if ( cgrib[lencurr-4]==s7 && cgrib[lencurr-3]==s7 && + cgrib[lencurr-2]==s7 && cgrib[lencurr-1]==s7 ) { + printf("g2_addfield: GRIB message already complete. Cannot add new section.\n"); + ierr=-2; + return(ierr); + } +// +// Loop through all current sections of the GRIB message to +// find the last section number. +// + issec3=0; + isprevbmap=0; + len=16; // length of Section 0 + for (;;) { + // Get number and length of next section + iofst=len*8; + gbit(cgrib,&ilen,iofst,32); + iofst=iofst+32; + gbit(cgrib,&isecnum,iofst,8); + iofst=iofst+8; + // Check if previous Section 3 exists + if (isecnum == 3) { + issec3=1; + lpos3=len; + } + // Check if a previous defined bitmap exists + if (isecnum == 6) { + gbit(cgrib,&ibmprev,iofst,8); + iofst=iofst+8; + if ((ibmprev >= 0) && (ibmprev <= 253)) isprevbmap=1; + } + len=len+ilen; + // Exit loop if last section reached + if ( len == lencurr ) break; + // If byte count for each section doesn't match current + // total length, then there is a problem. + if ( len > lencurr ) { + printf("g2_addfield: Section byte counts don''t add to total.\n"); + printf("g2_addfield: Sum of section byte counts = %d\n",len); + printf("g2_addfield: Total byte count in Section 0 = %d\n",lencurr); + ierr=-3; + return(ierr); + } + } +// +// Sections 4 through 7 can only be added after section 3 or 7. +// + if ( (isecnum != 3) && (isecnum != 7) ) { + printf("g2_addfield: Sections 4-7 can only be added after Section 3 or 7.\n"); + printf("g2_addfield: Section ',isecnum,' was the last found in given GRIB message.\n"); + ierr=-4; + return(ierr); +// +// Sections 4 through 7 can only be added if section 3 was previously defined. +// + } + else if ( ! issec3) { + printf("g2_addfield: Sections 4-7 can only be added if Section 3 was previously included.\n"); + printf("g2_addfield: Section 3 was not found in given GRIB message.\n"); + printf("g2_addfield: Call to routine addgrid required to specify Grid definition.\n"); + ierr=-6; + return(ierr); + } +// +// Add Section 4 - Product Definition Section +// + ibeg=lencurr*8; // Calculate offset for beginning of section 4 + iofst=ibeg+32; // leave space for length of section + sbit(cgrib,&four,iofst,8); // Store section number ( 4 ) + iofst=iofst+8; + sbit(cgrib,&numcoord,iofst,16); // Store num of coordinate values + iofst=iofst+16; + sbit(cgrib,&ipdsnum,iofst,16); // Store Prod Def Template num. + iofst=iofst+16; + // + // Get Product Definition Template + // + mappds=getpdstemplate(ipdsnum); + if (mappds == 0) { // undefined template + ierr=-5; + return(ierr); + } + // + // Extend the Product Definition Template, if necessary. + // The number of values in a specific template may vary + // depending on data specified in the "static" part of the + // template. + // + if ( mappds->needext ) { + free(mappds); + mappds=extpdstemplate(ipdsnum,ipdstmpl); + } + // + // Pack up each input value in array ipdstmpl into the + // the appropriate number of octets, which are specified in + // corresponding entries in array mappds. + // + for (i=0;imaplen;i++) { + nbits=abs(mappds->map[i])*8; + if ( (mappds->map[i] >= 0) || (ipdstmpl[i] >= 0) ) + sbit(cgrib,ipdstmpl+i,iofst,nbits); + else { + sbit(cgrib,&one,iofst,1); + temp=abs(ipdstmpl[i]); + sbit(cgrib,&temp,iofst+1,nbits-1); + } + iofst=iofst+nbits; + } + // Pack template extension, if appropriate + j=mappds->maplen; + if ( mappds->needext && (mappds->extlen > 0) ) { + for (i=0;iextlen;i++) { + nbits=abs(mappds->ext[i])*8; + if ( (mappds->ext[i] >= 0) || (ipdstmpl[j] >= 0) ) + sbit(cgrib,ipdstmpl+j,iofst,nbits); + else { + sbit(cgrib,&one,iofst,1); + temp=abs(ipdstmpl[j]); + sbit(cgrib,&temp,iofst+1,nbits-1); + } + iofst=iofst+nbits; + j++; + } + } + free(mappds); + // + // Add Optional list of vertical coordinate values + // after the Product Definition Template, if necessary. + // + if ( numcoord != 0 ) { + coordieee=(g2int *)calloc(numcoord,sizeof(g2int)); + mkieee(coordlist,coordieee,numcoord); + sbits(cgrib,coordieee,iofst,32,0,numcoord); + iofst=iofst+(32*numcoord); + free(coordieee); + } + // + // Calculate length of section 4 and store it in octets + // 1-4 of section 4. + // + lensec4=(iofst-ibeg)/8; + sbit(cgrib,&lensec4,ibeg,32); +// +// Pack Data using appropriate algorithm +// + // + // Get Data Representation Template + // + mapdrs=getdrstemplate(idrsnum); + if (mapdrs == 0) { + ierr=-5; + return(ierr); + } + // + // contract data field, removing data at invalid grid points, + // if bit-map is provided with field. + // + if ( ibmap == 0 || ibmap==254 ) { + pfld=(g2float *)malloc(ngrdpts*sizeof(g2float)); + ndpts=0; + for (j=0;jmaplen;i++) { + nbits=abs(mapdrs->map[i])*8; + if ( (mapdrs->map[i] >= 0) || (idrstmpl[i] >= 0) ) + sbit(cgrib,idrstmpl+i,iofst,nbits); + else { + sbit(cgrib,&one,iofst,1); + temp=abs(idrstmpl[i]); + sbit(cgrib,&temp,iofst+1,nbits-1); + } + iofst=iofst+nbits; + } + free(mapdrs); + // + // Calculate length of section 5 and store it in octets + // 1-4 of section 5. + // + lensec5=(iofst-ibeg)/8; + sbit(cgrib,&lensec5,ibeg,32); + +// +// Add Section 6 - Bit-Map Section +// + ibeg=iofst; // Calculate offset for beginning of section 6 + iofst=ibeg+32; // leave space for length of section + sbit(cgrib,&six,iofst,8); // Store section number ( 6 ) + iofst=iofst+8; + sbit(cgrib,&ibmap,iofst,8); // Store Bit Map indicator + iofst=iofst+8; + // + // Store bitmap, if supplied + // + if (ibmap == 0) { + sbits(cgrib,bmap,iofst,1,0,ngrdpts); // Store BitMap + iofst=iofst+ngrdpts; + } + // + // If specifying a previously defined bit-map, make sure + // one already exists in the current GRIB message. + // + if ((ibmap==254) && ( ! isprevbmap)) { + printf("g2_addfield: Requested previously defined bitmap,"); + printf(" but one does not exist in the current GRIB message.\n"); + ierr=-8; + return(ierr); + } + // + // Calculate length of section 6 and store it in octets + // 1-4 of section 6. Pad to end of octect, if necessary. + // + left=8-(iofst%8); + if (left != 8) { + sbit(cgrib,&zero,iofst,left); // Pad with zeros to fill Octet + iofst=iofst+left; + } + lensec6=(iofst-ibeg)/8; + sbit(cgrib,&lensec6,ibeg,32); + +// +// Add Section 7 - Data Section +// + ibeg=iofst; // Calculate offset for beginning of section 7 + iofst=ibeg+32; // leave space for length of section + sbit(cgrib,&seven,iofst,8); // Store section number ( 7 ) + iofst=iofst+8; + // Store Packed Binary Data values, if non-constant field + if (lcpack != 0) { + ioctet=iofst/8; + //cgrib(ioctet+1:ioctet+lcpack)=cpack(1:lcpack) + for (j=0;j +#include +#include "grib2.h" + + +g2int g2_addgrid(unsigned char *cgrib,g2int *igds,g2int *igdstmpl,g2int *ideflist,g2int idefnum) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_addgrid +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-11-01 +// +// ABSTRACT: This routine packs up a Grid Definition Section (Section 3) +// and adds it to a GRIB2 message. It is used with routines "g2_create", +// "g2_addlocal", "g2_addfield", +// and "g2_gribend" to create a complete GRIB2 message. +// g2_create must be called first to initialize a new GRIB2 message. +// +// PROGRAM HISTORY LOG: +// 2002-11-01 Gilbert +// +// USAGE: int g2_addgrid(unsigned char *cgrib,g2int *igds,g2int *igdstmpl, +// g2int *ideflist,g2int idefnum) +// INPUT ARGUMENTS: +// cgrib - Char array that contains the GRIB2 message to which +// section should be added. +// igds - Contains information needed for GRIB Grid Definition Section 3 +// Must be dimensioned >= 5. +// igds[0]=Source of grid definition (see Code Table 3.0) +// igds[1]=Number of grid points in the defined grid. +// igds[2]=Number of octets needed for each +// additional grid points definition. +// Used to define number of +// points in each row ( or column ) for +// non-regular grids. +// = 0, if using regular grid. +// igds[3]=Interpretation of list for optional points +// definition. (Code Table 3.11) +// igds[4]=Grid Definition Template Number (Code Table 3.1) +// igdstmpl - Contains the data values for the specified Grid Definition +// Template ( NN=igds[4] ). Each element of this integer +// array contains an entry (in the order specified) of Grid +// Defintion Template 3.NN +// ideflist - (Used if igds[2] != 0) This array contains the +// number of grid points contained in each row ( or column ) +// idefnum - (Used if igds[2] != 0) The number of entries +// in array ideflist. i.e. number of rows ( or columns ) +// for which optional grid points are defined. +// +// OUTPUT ARGUMENTS: +// cgrib - Char array to contain the updated GRIB2 message. +// Must be allocated large enough to store the entire +// GRIB2 message. +// +// RETURN VALUES: +// ierr - Return code. +// > 0 = Current size of updated GRIB2 message +// -1 = GRIB message was not initialized. Need to call +// routine gribcreate first. +// -2 = GRIB message already complete. Cannot add new section. +// -3 = Sum of Section byte counts doesn't add to total byte count +// -4 = Previous Section was not 1, 2 or 7. +// -5 = Could not find requested Grid Definition Template. +// +// REMARKS: Note that the Grid Def Section ( Section 3 ) can only follow +// Section 1, 2 or Section 7 in a GRIB2 message. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + + g2int ierr; + static unsigned char G=0x47; // 'G' + static unsigned char R=0x52; // 'R' + static unsigned char I=0x49; // 'I' + static unsigned char B=0x42; // 'B' + static unsigned char seven=0x37; // '7' + + static g2int one=1,three=3,miss=65535; + g2int lensec3,iofst,ibeg,lencurr,len; + g2int i,j,temp,ilen,isecnum,nbits; + xxtemplate *mapgrid=0; + + ierr=0; +// +// Check to see if beginning of GRIB message exists +// + if ( cgrib[0]!=G || cgrib[1]!=R || cgrib[2]!=I || cgrib[3]!=B ) { + printf("g2_addgrid: GRIB not found in given message.\n"); + printf("g2_addgrid: Call to routine gribcreate required to initialize GRIB messge.\n"); + ierr=-1; + return(ierr); + } +// +// Get current length of GRIB message +// + gbit(cgrib,&lencurr,96,32); +// +// Check to see if GRIB message is already complete +// + if ( cgrib[lencurr-4]==seven && cgrib[lencurr-3]==seven && + cgrib[lencurr-2]==seven && cgrib[lencurr-1]==seven ) { + printf("g2_addgrid: GRIB message already complete. Cannot add new section.\n"); + ierr=-2; + return(ierr); + } +// +// Loop through all current sections of the GRIB message to +// find the last section number. +// + len=16; // length of Section 0 + for (;;) { + // Get section number and length of next section + iofst=len*8; + gbit(cgrib,&ilen,iofst,32); + iofst=iofst+32; + gbit(cgrib,&isecnum,iofst,8); + len=len+ilen; + // Exit loop if last section reached + if ( len == lencurr ) break; + // If byte count for each section doesn't match current + // total length, then there is a problem. + if ( len > lencurr ) { + printf("g2_addgrid: Section byte counts don''t add to total.\n"); + printf("g2_addgrid: Sum of section byte counts = %d\n",len); + printf("g2_addgrid: Total byte count in Section 0 = %d\n",lencurr); + ierr=-3; + return(ierr); + } + } +// +// Section 3 can only be added after sections 1, 2 and 7. +// + if ( (isecnum!=1) && (isecnum!=2) && (isecnum!=7) ) { + printf("g2_addgrid: Section 3 can only be added after Section 1, 2 or 7.\n"); + printf("g2_addgrid: Section ',isecnum,' was the last found in given GRIB message.\n"); + ierr=-4; + return(ierr); + } +// +// Add Section 3 - Grid Definition Section +// + ibeg=lencurr*8; // Calculate offset for beginning of section 3 + iofst=ibeg+32; // leave space for length of section + sbit(cgrib,&three,iofst,8); // Store section number ( 3 ) + iofst=iofst+8; + sbit(cgrib,igds+0,iofst,8); // Store source of Grid def. + iofst=iofst+8; + sbit(cgrib,igds+1,iofst,32); // Store number of data pts. + iofst=iofst+32; + sbit(cgrib,igds+2,iofst,8); // Store number of extra octets. + iofst=iofst+8; + sbit(cgrib,igds+3,iofst,8); // Store interp. of extra octets. + iofst=iofst+8; + // if Octet 6 is not equal to zero, Grid Definition Template may + // not be supplied. + if ( igds[0] == 0 ) + sbit(cgrib,igds+4,iofst,16); // Store Grid Def Template num. + else + sbit(cgrib,&miss,iofst,16); // Store missing value as Grid Def Template num. + iofst=iofst+16; + // + // Get Grid Definition Template + // + if (igds[0] == 0) { + mapgrid=getgridtemplate(igds[4]); + if (mapgrid == 0) { // undefined template + ierr=-5; + return(ierr); + } + // + // Extend the Grid Definition Template, if necessary. + // The number of values in a specific template may vary + // depending on data specified in the "static" part of the + // template. + // + if ( mapgrid->needext ) { + free(mapgrid); + mapgrid=extgridtemplate(igds[4],igdstmpl); + } + } + // + // Pack up each input value in array igdstmpl into the + // the appropriate number of octets, which are specified in + // corresponding entries in array mapgrid. + // + for (i=0;imaplen;i++) { + nbits=abs(mapgrid->map[i])*8; + if ( (mapgrid->map[i] >= 0) || (igdstmpl[i] >= 0) ) + sbit(cgrib,igdstmpl+i,iofst,nbits); + else { + sbit(cgrib,&one,iofst,1); + temp=abs(igdstmpl[i]); + sbit(cgrib,&temp,iofst+1,nbits-1); + } + iofst=iofst+nbits; + } + // Pack template extension, if appropriate + j=mapgrid->maplen; + if ( mapgrid->needext && (mapgrid->extlen > 0) ) { + for (i=0;iextlen;i++) { + nbits=abs(mapgrid->ext[i])*8; + if ( (mapgrid->ext[i] >= 0) || (igdstmpl[j] >= 0) ) + sbit(cgrib,igdstmpl+j,iofst,nbits); + else { + sbit(cgrib,&one,iofst,1); + temp=abs(igdstmpl[j]); + sbit(cgrib,&temp,iofst+1,nbits-1); + } + iofst=iofst+nbits; + j++; + } + } + free(mapgrid); + // + // If requested, + // Insert optional list of numbers defining number of points + // in each row or column. This is used for non regular + // grids. + // + if ( igds[2] != 0 ) { + nbits=igds[2]*8; + sbits(cgrib,ideflist,iofst,nbits,0,idefnum); + iofst=iofst+(nbits*idefnum); + } + // + // Calculate length of section 3 and store it in octets + // 1-4 of section 3. + // + lensec3=(iofst-ibeg)/8; + sbit(cgrib,&lensec3,ibeg,32); + +// +// Update current byte total of message in Section 0 +// + lencurr+=lensec3; + sbit(cgrib,&lencurr,96,32); + + return(lencurr); + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_addlocal.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_addlocal.c new file mode 100644 index 000000000..7e0a15e1d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_addlocal.c @@ -0,0 +1,147 @@ +#include +#include "grib2.h" + +g2int g2_addlocal(unsigned char *cgrib,unsigned char *csec2,g2int lcsec2) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_addlocal +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-11-01 +// +// ABSTRACT: This routine adds a Local Use Section (Section 2) to +// a GRIB2 message. It is used with routines "g2_create", +// "g2_addgrid", "g2_addfield", +// and "g2_gribend" to create a complete GRIB2 message. +// g2_create must be called first to initialize a new GRIB2 message. +// +// PROGRAM HISTORY LOG: +// 2002-11-01 Gilbert +// +// USAGE: int g2_addlocal(unsigned char *cgrib,unsigned char *csec2, +// g2int lcsec2) +// INPUT ARGUMENTS: +// cgrib - Char array that contains the GRIB2 message to which section +// 2 should be added. +// csec2 - Character array containing information to be added in +// Section 2. +// lcsec2 - Number of bytes of character array csec2 to be added to +// Section 2. +// +// OUTPUT ARGUMENT: +// cgrib - Char array to contain the updated GRIB2 message. +// Must be allocated large enough to store the entire +// GRIB2 message. +// +// RETURN VALUES: +// ierr - Return code. +// > 0 = Current size of updated GRIB2 message +// -1 = GRIB message was not initialized. Need to call +// routine gribcreate first. +// -2 = GRIB message already complete. Cannot add new section. +// -3 = Sum of Section byte counts doesn't add to total byte count +// -4 = Previous Section was not 1 or 7. +// +// REMARKS: Note that the Local Use Section ( Section 2 ) can only follow +// Section 1 or Section 7 in a GRIB2 message. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + + g2int ierr; + static unsigned char G=0x47; // 'G' + static unsigned char R=0x52; // 'R' + static unsigned char I=0x49; // 'I' + static unsigned char B=0x42; // 'B' + static unsigned char seven=0x37; // '7' + + static g2int two=2; + g2int j,k,lensec2,iofst,ibeg,lencurr,ilen,len,istart; + g2int isecnum; + + ierr=0; +// +// Check to see if beginning of GRIB message exists +// + if ( cgrib[0]!=G || cgrib[1]!=R || cgrib[2]!=I || cgrib[3]!=B ) { + printf("g2_addlocal: GRIB not found in given message.\n"); + printf("g2_addlocal: Call to routine g2_create required to initialize GRIB messge.\n"); + ierr=-1; + return(ierr); + } +// +// Get current length of GRIB message +// + gbit(cgrib,&lencurr,96,32); +// +// Check to see if GRIB message is already complete +// + if ( cgrib[lencurr-4]==seven && cgrib[lencurr-3]==seven && + cgrib[lencurr-2]==seven && cgrib[lencurr-1]==seven ) { + printf("g2_addlocal: GRIB message already complete. Cannot add new section.\n"); + ierr=-2; + return(ierr); + } +// +// Loop through all current sections of the GRIB message to +// find the last section number. +// + len=16; // length of Section 0 + for (;;) { + // Get section number and length of next section + iofst=len*8; + gbit(cgrib,&ilen,iofst,32); + iofst=iofst+32; + gbit(cgrib,&isecnum,iofst,8); + len=len+ilen; + // Exit loop if last section reached + if ( len == lencurr ) break; + // If byte count for each section doesn't match current + // total length, then there is a problem. + if ( len > lencurr ) { + printf("g2_addlocal: Section byte counts don't add to total.\n"); + printf("g2_addlocal: Sum of section byte counts = %d\n",len); + printf("g2_addlocal: Total byte count in Section 0 = %d\n",lencurr); + ierr=-3; + return(ierr); + } + } +// +// Section 2 can only be added after sections 1 and 7. +// + if ( (isecnum!=1) && (isecnum!=7) ) { + printf("g2_addlocal: Section 2 can only be added after Section 1 or Section 7.\n"); + printf("g2_addlocal: Section %d was the last found in given GRIB message.\n",isecnum); + ierr=-4; + return(ierr); + } +// +// Add Section 2 - Local Use Section +// + ibeg=lencurr*8; // Calculate offset for beginning of section 2 + iofst=ibeg+32; // leave space for length of section + sbit(cgrib,&two,iofst,8); // Store section number ( 2 ) + istart=lencurr+5; + //cgrib(istart+1:istart+lcsec2)=csec2(1:lcsec2) + k=0; + for (j=istart;j +#include "grib2.h" + +#define MAPSEC1LEN 13 + +g2int g2_create(unsigned char *cgrib,g2int *listsec0,g2int *listsec1) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_create +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-31 +// +// ABSTRACT: This routine initializes a new GRIB2 message and packs +// GRIB2 sections 0 (Indicator Section) and 1 (Identification Section). +// This routine is used with routines "g2_addlocal", "g2_addgrid", +// "g2_addfield", and "g2_gribend" to create a complete GRIB2 message. +// g2_create must be called first to initialize a new GRIB2 message. +// Also, a call to g2_gribend is required to complete GRIB2 message +// after all fields have been added. +// +// PROGRAM HISTORY LOG: +// 2002-10-31 Gilbert +// +// USAGE: int g2_create(unsigned char *cgrib,g2int *listsec0,g2int *listsec1) +// INPUT ARGUMENTS: +// cgrib - Character array to contain the GRIB2 message +// listsec0 - Contains information needed for GRIB Indicator Section 0. +// Must be dimensioned >= 2. +// listsec0[0]=Discipline-GRIB Master Table Number +// (see Code Table 0.0) +// listsec0[1]=GRIB Edition Number (currently 2) +// listsec1 - Contains information needed for GRIB Identification Section 1. +// Must be dimensioned >= 13. +// listsec1[0]=Id of orginating centre (Common Code Table C-1) +// listsec1[1]=Id of orginating sub-centre (local table) +// listsec1[2]=GRIB Master Tables Version Number (Code Table 1.0) +// listsec1[3]=GRIB Local Tables Version Number (Code Table 1.1) +// listsec1[4]=Significance of Reference Time (Code Table 1.2) +// listsec1[5]=Reference Time - Year (4 digits) +// listsec1[6]=Reference Time - Month +// listsec1[7]=Reference Time - Day +// listsec1[8]=Reference Time - Hour +// listsec1[9]=Reference Time - Minute +// listsec1[10]=Reference Time - Second +// listsec1[11]=Production status of data (Code Table 1.3) +// listsec1[12]=Type of processed data (Code Table 1.4) +// +// OUTPUT ARGUMENTS: +// cgrib - Char array to contain the new GRIB2 message. +// Must be allocated large enough to store the entire +// GRIB2 message. +// +// RETURN VALUES: +// ierr - return code. +// > 0 = Current size of new GRIB2 message +// -1 = Tried to use for version other than GRIB Edition 2 +// +// REMARKS: This routine is intended for use with routines "g2_addlocal", +// "g2_addgrid", "g2_addfield", and "g2_gribend" to create a complete +// GRIB2 message. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + + g2int ierr; + g2int zero=0,one=1; + g2int mapsec1len=MAPSEC1LEN; + g2int mapsec1[MAPSEC1LEN]={ 2,2,1,1,1,2,1,1,1,1,1,1,1 }; + g2int i,lensec0,lensec1,iofst,ibeg,nbits,len; + + ierr=0; +// +// Currently handles only GRIB Edition 2. +// + if (listsec0[1] != 2) { + printf("g2_create: can only code GRIB edition 2."); + ierr=-1; + return (ierr); + } +// +// Pack Section 0 - Indicator Section +// ( except for total length of GRIB message ) +// + cgrib[0]=0x47; // 'G' // Beginning of GRIB message + cgrib[1]=0x52; // 'R' + cgrib[2]=0x49; // 'I' + cgrib[3]=0x42; // 'B' + sbit(cgrib,&zero,32,16); // reserved for future use + sbit(cgrib,listsec0+0,48,8); // Discipline + sbit(cgrib,listsec0+1,56,8); // GRIB edition number + lensec0=16; // bytes (octets) +// +// Pack Section 1 - Identification Section +// + ibeg=lensec0*8; // Calculate offset for beginning of section 1 + iofst=ibeg+32; // leave space for length of section + sbit(cgrib,&one,iofst,8); // Store section number ( 1 ) + iofst=iofst+8; + // + // Pack up each input value in array listsec1 into the + // the appropriate number of octets, which are specified in + // corresponding entries in array mapsec1. + // + for (i=0;i +#include "grib2.h" + +void g2_free(gribfield *gfld) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_free +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-28 +// +// ABSTRACT: This routine frees up memory that was allocated for +// struct gribfield. +// +// PROGRAM HISTORY LOG: +// 2002-10-28 Gilbert +// +// USAGE: g2_free(gribfield *gfld) +// ARGUMENT: +// gfld - pointer to gribfield structure (defined in include file grib2.h) +// returned from routine g2_getfld. +// +// REMARKS: This routine must be called to free up memory used by +// the decode routine, g2_getfld, when user no longer needs to +// reference this data. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + + if (gfld->idsect != 0 ) free(gfld->idsect); + if (gfld->local != 0 ) free(gfld->local); + if (gfld->list_opt != 0 ) free(gfld->list_opt); + if (gfld->igdtmpl != 0 ) free(gfld->igdtmpl); + if (gfld->ipdtmpl != 0 ) free(gfld->ipdtmpl); + if (gfld->coord_list != 0 ) free(gfld->coord_list); + if (gfld->idrtmpl != 0 ) free(gfld->idrtmpl); + if (gfld->bmap != 0 ) free(gfld->bmap); + if (gfld->fld != 0 ) free(gfld->fld); + free(gfld); + + return; +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_getfld.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_getfld.c new file mode 100644 index 000000000..9a68f199b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_getfld.c @@ -0,0 +1,551 @@ +#include +#include +#include "grib2.h" + +g2int g2_unpack1(unsigned char *,g2int *,g2int **,g2int *); +g2int g2_unpack2(unsigned char *,g2int *,g2int *,unsigned char **); +g2int g2_unpack3(unsigned char *,g2int *,g2int **,g2int **, + g2int *,g2int **,g2int *); +g2int g2_unpack4(unsigned char *,g2int *,g2int *,g2int **, + g2int *,g2float **,g2int *); +g2int g2_unpack5(unsigned char *,g2int *,g2int *,g2int *, g2int **,g2int *); +g2int g2_unpack6(unsigned char *,g2int *,g2int ,g2int *, g2int **); +g2int g2_unpack7(unsigned char *,g2int *,g2int ,g2int *, + g2int ,g2int *,g2int ,g2float **); + +g2int g2_getfld(unsigned char *cgrib,g2int ifldnum,g2int unpack,g2int expand, + gribfield **gfld) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_getfld +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-28 +// +// ABSTRACT: This subroutine returns all the metadata, template values, +// Bit-map ( if applicable ), and the unpacked data for a given data +// field. All of the information returned is stored in a gribfield +// structure, which is defined in file grib2.h. +// Users of this routine will need to include "grib2.h" in their source +// code that calls this routine. Each component of the gribfield +// struct is also described in the OUTPUT ARGUMENTS section below. +// +// Since there can be multiple data fields packed into a GRIB2 +// message, the calling routine indicates which field is being requested +// with the ifldnum argument. +// +// PROGRAM HISTORY LOG: +// 2002-10-28 Gilbert +// +// USAGE: #include "grib2.h" +// int g2_getfld(unsigned char *cgrib,g2int ifldnum,g2int unpack, +// g2int expand,gribfield **gfld) +// INPUT ARGUMENTS: +// cgrib - Character pointer to the GRIB2 message +// ifldnum - Specifies which field in the GRIB2 message to return. +// unpack - Boolean value indicating whether to unpack bitmap/data field +// 1 = unpack bitmap (if present) and data values +// 0 = do not unpack bitmap and data values +// expand - Boolean value indicating whether the data points should be +// expanded to the correspond grid, if a bit-map is present. +// 1 = if possible, expand data field to grid, inserting zero +// values at gridpoints that are bitmapped out. +// (SEE REMARKS2) +// 0 = do not expand data field, leaving it an array of +// consecutive data points for each "1" in the bitmap. +// This argument is ignored if unpack == 0 OR if the +// returned field does not contain a bit-map. +// +// OUTPUT ARGUMENT: +// gribfield gfld; - pointer to structure gribfield containing +// all decoded data for the data field. +// +// gfld->version = GRIB edition number ( currently 2 ) +// gfld->discipline = Message Discipline ( see Code Table 0.0 ) +// gfld->idsect = Contains the entries in the Identification +// Section ( Section 1 ) +// This element is a pointer to an array +// that holds the data. +// gfld->idsect[0] = Identification of originating Centre +// ( see Common Code Table C-1 ) +// 7 - US National Weather Service +// gfld->idsect[1] = Identification of originating Sub-centre +// gfld->idsect[2] = GRIB Master Tables Version Number +// ( see Code Table 1.0 ) +// 0 - Experimental +// 1 - Initial operational version number +// gfld->idsect[3] = GRIB Local Tables Version Number +// ( see Code Table 1.1 ) +// 0 - Local tables not used +// 1-254 - Number of local tables version used +// gfld->idsect[4] = Significance of Reference Time (Code Table 1.2) +// 0 - Analysis +// 1 - Start of forecast +// 2 - Verifying time of forecast +// 3 - Observation time +// gfld->idsect[5] = Year ( 4 digits ) +// gfld->idsect[6] = Month +// gfld->idsect[7) = Day +// gfld->idsect[8] = Hour +// gfld->idsect[9] = Minute +// gfld->idsect[10] = Second +// gfld->idsect[11] = Production status of processed data +// ( see Code Table 1.3 ) +// 0 - Operational products +// 1 - Operational test products +// 2 - Research products +// 3 - Re-analysis products +// gfld->idsect[12] = Type of processed data ( see Code Table 1.4 ) +// 0 - Analysis products +// 1 - Forecast products +// 2 - Analysis and forecast products +// 3 - Control forecast products +// 4 - Perturbed forecast products +// 5 - Control and perturbed forecast products +// 6 - Processed satellite observations +// 7 - Processed radar observations +// gfld->idsectlen = Number of elements in gfld->idsect[]. +// gfld->local = Pointer to character array containing contents +// of Local Section 2, if included +// gfld->locallen = length of array gfld->local[] +// gfld->ifldnum = field number within GRIB message +// gfld->griddef = Source of grid definition (see Code Table 3.0) +// 0 - Specified in Code table 3.1 +// 1 - Predetermined grid Defined by originating centre +// gfld->ngrdpts = Number of grid points in the defined grid. +// gfld->numoct_opt = Number of octets needed for each +// additional grid points definition. +// Used to define number of +// points in each row ( or column ) for +// non-regular grids. +// = 0, if using regular grid. +// gfld->interp_opt = Interpretation of list for optional points +// definition. (Code Table 3.11) +// gfld->igdtnum = Grid Definition Template Number (Code Table 3.1) +// gfld->igdtmpl = Contains the data values for the specified Grid +// Definition Template ( NN=gfld->igdtnum ). Each +// element of this integer array contains an entry (in +// the order specified) of Grid Defintion Template 3.NN +// This element is a pointer to an array +// that holds the data. +// gfld->igdtlen = Number of elements in gfld->igdtmpl[]. i.e. number of +// entries in Grid Defintion Template 3.NN +// ( NN=gfld->igdtnum ). +// gfld->list_opt = (Used if gfld->numoct_opt .ne. 0) This array +// contains the number of grid points contained in +// each row ( or column ). (part of Section 3) +// This element is a pointer to an array +// that holds the data. This pointer is nullified +// if gfld->numoct_opt=0. +// gfld->num_opt = (Used if gfld->numoct_opt .ne. 0) +// The number of entries +// in array ideflist. i.e. number of rows ( or columns ) +// for which optional grid points are defined. This value +// is set to zero, if gfld->numoct_opt=0. +// gfdl->ipdtnum = Product Definition Template Number(see Code Table 4.0) +// gfld->ipdtmpl = Contains the data values for the specified Product +// Definition Template ( N=gfdl->ipdtnum ). Each element +// of this integer array contains an entry (in the +// order specified) of Product Defintion Template 4.N. +// This element is a pointer to an array +// that holds the data. +// gfld->ipdtlen = Number of elements in gfld->ipdtmpl[]. i.e. number of +// entries in Product Defintion Template 4.N +// ( N=gfdl->ipdtnum ). +// gfld->coord_list = Real array containing floating point values +// intended to document the vertical discretisation +// associated to model data on hybrid coordinate +// vertical levels. (part of Section 4) +// This element is a pointer to an array +// that holds the data. +// gfld->num_coord = number of values in array gfld->coord_list[]. +// gfld->ndpts = Number of data points unpacked and returned. +// gfld->idrtnum = Data Representation Template Number +// ( see Code Table 5.0) +// gfld->idrtmpl = Contains the data values for the specified Data +// Representation Template ( N=gfld->idrtnum ). Each +// element of this integer array contains an entry +// (in the order specified) of Product Defintion +// Template 5.N. +// This element is a pointer to an array +// that holds the data. +// gfld->idrtlen = Number of elements in gfld->idrtmpl[]. i.e. number +// of entries in Data Representation Template 5.N +// ( N=gfld->idrtnum ). +// gfld->unpacked = logical value indicating whether the bitmap and +// data values were unpacked. If false, +// gfld->bmap and gfld->fld pointers are nullified. +// gfld->expanded = Logical value indicating whether the data field +// was expanded to the grid in the case where a +// bit-map is present. If true, the data points in +// gfld->fld match the grid points and zeros were +// inserted at grid points where data was bit-mapped +// out. If false, the data values in gfld->fld were +// not expanded to the grid and are just a consecutive +// array of data points corresponding to each value of +// "1" in gfld->bmap. +// gfld->ibmap = Bitmap indicator ( see Code Table 6.0 ) +// 0 = bitmap applies and is included in Section 6. +// 1-253 = Predefined bitmap applies +// 254 = Previously defined bitmap applies to this field +// 255 = Bit map does not apply to this product. +// gfld->bmap = integer array containing decoded bitmap, +// if gfld->ibmap=0 or gfld->ibap=254. Otherwise nullified +// This element is a pointer to an array +// that holds the data. +// gfld->fld = Array of gfld->ndpts unpacked data points. +// This element is a pointer to an array +// that holds the data. +// +// +// RETURN VALUES: +// ierr - Error return code. +// 0 = no error +// 1 = Beginning characters "GRIB" not found. +// 2 = GRIB message is not Edition 2. +// 3 = The data field request number was not positive. +// 4 = End string "7777" found, but not where expected. +// 6 = GRIB message did not contain the requested number of +// data fields. +// 7 = End string "7777" not found at end of message. +// 8 = Unrecognized Section encountered. +// 9 = Data Representation Template 5.NN not yet implemented. +// 15 = Error unpacking Section 1. +// 16 = Error unpacking Section 2. +// 10 = Error unpacking Section 3. +// 11 = Error unpacking Section 4. +// 12 = Error unpacking Section 5. +// 13 = Error unpacking Section 6. +// 14 = Error unpacking Section 7. +// 17 = Previous bitmap specified, yet none exists. +// +// REMARKS: Note that struct gribfield is allocated by this routine and it +// also contains pointers to many arrays of data that were allocated +// during decoding. Users are encouraged to free up this memory, +// when it is no longer needed, by an explicit call to routine g2_free. +// EXAMPLE: +// #include "grib2.h" +// gribfield *gfld; +// ret=g2_getfld(cgrib,1,1,1,&gfld); +// ... +// g2_free(gfld); +// +// Routine g2_info can be used to first determine +// how many data fields exist in a given GRIB message. +// +// REMARKS2: It may not always be possible to expand a bit-mapped data field. +// If a pre-defined bit-map is used and not included in the GRIB2 +// message itself, this routine would not have the necessary +// information to expand the data. In this case, gfld->expanded would +// would be set to 0 (false), regardless of the value of input +// argument expand. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + + g2int have3,have4,have5,have6,have7,ierr,jerr; + g2int numfld,j,n,istart,iofst,ipos; + g2int disc,ver,lensec0,lengrib,lensec,isecnum; + g2int *igds; + g2int *bmpsave; + g2float *newfld; + gribfield *lgfld; + + have3=0; + have4=0; + have5=0; + have6=0; + have7=0; + ierr=0; + numfld=0; + + lgfld=(gribfield *)malloc(sizeof(gribfield)); + *gfld=lgfld; + + lgfld->locallen=0; + lgfld->idsect=0; + lgfld->local=0; + lgfld->list_opt=0; + lgfld->igdtmpl=0; + lgfld->ipdtmpl=0; + lgfld->idrtmpl=0; + lgfld->coord_list=0; + lgfld->bmap=0; + lgfld->fld=0; +// +// Check for valid request number +// + if (ifldnum <= 0) { + printf("g2_getfld: Request for field number must be positive.\n"); + ierr=3; + return(ierr); + } +// +// Check for beginning of GRIB message in the first 100 bytes +// + istart=-1; + for (j=0;j<100;j++) { + if (cgrib[j]=='G' && cgrib[j+1]=='R' &&cgrib[j+2]=='I' && + cgrib[j+3]=='B') { + istart=j; + break; + } + } + if (istart == -1) { + printf("g2_getfld: Beginning characters GRIB not found.\n"); + ierr=1; + return(ierr); + } +// +// Unpack Section 0 - Indicator Section +// + iofst=8*(istart+6); + gbit(cgrib,&disc,iofst,8); // Discipline + iofst=iofst+8; + gbit(cgrib,&ver,iofst,8); // GRIB edition number + iofst=iofst+8; + iofst=iofst+32; + gbit(cgrib,&lengrib,iofst,32); // Length of GRIB message + iofst=iofst+32; + lensec0=16; + ipos=istart+lensec0; +// +// Currently handles only GRIB Edition 2. +// + if (ver != 2) { + printf("g2_getfld: can only decode GRIB edition 2.\n"); + ierr=2; + return(ierr); + } +// +// Loop through the remaining sections keeping track of the +// length of each. Also keep the latest Grid Definition Section info. +// Unpack the requested field number. +// + for (;;) { + // Check to see if we are at end of GRIB message + if (cgrib[ipos]=='7' && cgrib[ipos+1]=='7' && cgrib[ipos+2]=='7' && + cgrib[ipos+3]=='7') { + ipos=ipos+4; + // If end of GRIB message not where expected, issue error + if (ipos != (istart+lengrib)) { + printf("g2_getfld: '7777' found, but not where expected.\n"); + ierr=4; + return(ierr); + } + break; + } + // Get length of Section and Section number + iofst=(ipos-1)*8; + iofst=ipos*8; + gbit(cgrib,&lensec,iofst,32); // Get Length of Section + iofst=iofst+32; + gbit(cgrib,&isecnum,iofst,8); // Get Section number + iofst=iofst+8; + //printf(" lensec= %ld secnum= %ld \n",lensec,isecnum); + // + // Check to see if section number is valid + // + if ( isecnum<1 || isecnum>7 ) { + printf("g2_getfld: Unrecognized Section Encountered=%d\n",isecnum); + ierr=8; + return(ierr); + } + // + // If found Section 1, decode elements in Identification Section + // + if (isecnum == 1) { + iofst=iofst-40; // reset offset to beginning of section + jerr=g2_unpack1(cgrib,&iofst,&lgfld->idsect,&lgfld->idsectlen); + if (jerr !=0 ) { + ierr=15; + return(ierr); + } + } + // + // If found Section 2, Grab local section + // Save in case this is the latest one before the requested field. + // + if (isecnum == 2) { + iofst=iofst-40; // reset offset to beginning of section + if (lgfld->local!=0) free(lgfld->local); + jerr=g2_unpack2(cgrib,&iofst,&lgfld->locallen,&lgfld->local); + if (jerr != 0) { + ierr=16; + return(ierr); + } + } + // + // If found Section 3, unpack the GDS info using the + // appropriate template. Save in case this is the latest + // grid before the requested field. + // + if (isecnum == 3) { + iofst=iofst-40; // reset offset to beginning of section + if (lgfld->igdtmpl!=0) free(lgfld->igdtmpl); + if (lgfld->list_opt!=0) free(lgfld->list_opt); + jerr=g2_unpack3(cgrib,&iofst,&igds,&lgfld->igdtmpl, + &lgfld->igdtlen,&lgfld->list_opt,&lgfld->num_opt); + if (jerr == 0) { + have3=1; + lgfld->griddef=igds[0]; + lgfld->ngrdpts=igds[1]; + lgfld->numoct_opt=igds[2]; + lgfld->interp_opt=igds[3]; + lgfld->igdtnum=igds[4]; + free( igds ); + } + else { + ierr=10; + return(ierr); + } + } + // + // If found Section 4, check to see if this field is the + // one requested. + // + if (isecnum == 4) { + numfld=numfld+1; + if (numfld == ifldnum) { + lgfld->discipline=disc; + lgfld->version=ver; + lgfld->ifldnum=ifldnum; + lgfld->unpacked=unpack; + lgfld->expanded=0; + iofst=iofst-40; // reset offset to beginning of section + jerr=g2_unpack4(cgrib,&iofst,&lgfld->ipdtnum, + &lgfld->ipdtmpl,&lgfld->ipdtlen,&lgfld->coord_list, + &lgfld->num_coord); + if (jerr == 0) + have4=1; + else { + ierr=11; + return(ierr); + } + } + } + // + // If found Section 5, check to see if this field is the + // one requested. + // + if (isecnum == 5 && numfld == ifldnum) { + iofst=iofst-40; // reset offset to beginning of section + jerr=g2_unpack5(cgrib,&iofst,&lgfld->ndpts,&lgfld->idrtnum, + &lgfld->idrtmpl,&lgfld->idrtlen); + if (jerr == 0) + have5=1; + else { + ierr=12; + return(ierr); + } + } + // + // If found Section 6, Unpack bitmap. + // Save in case this is the latest + // bitmap before the requested field. + // + if (isecnum == 6) { + if (unpack) { // unpack bitmap + iofst=iofst-40; // reset offset to beginning of section + bmpsave=lgfld->bmap; // save pointer to previous bitmap + jerr=g2_unpack6(cgrib,&iofst,lgfld->ngrdpts,&lgfld->ibmap, + &lgfld->bmap); + if (jerr == 0) { + have6=1; + if (lgfld->ibmap == 254) // use previously specified bitmap + if( bmpsave!=0 ) + lgfld->bmap=bmpsave; + else { + printf("g2_getfld: Prev bit-map specified, but none exist.\n"); + ierr=17; + return(ierr); + } + else // get rid of it + if( bmpsave!=0 ) free(bmpsave); + } + else { + ierr=13; + return(ierr); + } + } + else { // do not unpack bitmap + gbit(cgrib,&lgfld->ibmap,iofst,8); // Get BitMap Indicator + have6=1; + } + } + // + // If found Section 7, check to see if this field is the + // one requested. + // + if (isecnum==7 && numfld==ifldnum && unpack) { + iofst=iofst-40; // reset offset to beginning of section + jerr=g2_unpack7(cgrib,&iofst,lgfld->igdtnum,lgfld->igdtmpl, + lgfld->idrtnum,lgfld->idrtmpl,lgfld->ndpts, + &lgfld->fld); + if (jerr == 0) { + have7=1; + // If bitmap is used with this field, expand data field + // to grid, if possible. + if ( lgfld->ibmap != 255 && lgfld->bmap != 0 ) { + if ( expand == 1 ) { + n=0; + newfld=(g2float *)calloc(lgfld->ngrdpts,sizeof(g2float)); + for (j=0;jngrdpts;j++) { + if (lgfld->bmap[j]==1) newfld[j]=lgfld->fld[n++]; + } + free(lgfld->fld); + lgfld->fld=newfld; + lgfld->expanded=1; + } + else { + lgfld->expanded=0; + } + } + else { + lgfld->expanded=1; + } + } + else { + printf("g2_getfld: return from g2_unpack7 = %d \n",(int)jerr); + ierr=14; + return(ierr); + } + } + // + // Check to see if we read pass the end of the GRIB + // message and missed the terminator string '7777'. + // + ipos=ipos+lensec; // Update beginning of section pointer + if (ipos > (istart+lengrib)) { + printf("g2_getfld: '7777' not found at end of GRIB message.\n"); + ierr=7; + return(ierr); + } + // + // If unpacking requested, return when all sections have been + // processed + // + if (unpack && have3 && have4 && have5 && have6 && have7) + return(ierr); + // + // If unpacking is not requested, return when sections + // 3 through 6 have been processed + // + if ((! unpack) && have3 && have4 && have5 && have6) + return(ierr); + + } + +// +// If exited from above loop, the end of the GRIB message was reached +// before the requested field was found. +// + printf("g2_getfld: GRIB message contained %d different fields.\n",numfld); + printf("g2_getfld: The request was for field %d.\n",ifldnum); + ierr=6; + + return(ierr); + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_gribend.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_gribend.c new file mode 100644 index 000000000..4674beb29 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_gribend.c @@ -0,0 +1,122 @@ +#include +#include "grib2.h" + +g2int g2_gribend(unsigned char *cgrib) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_gribend +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-31 +// +// ABSTRACT: This routine finalizes a GRIB2 message after all grids +// and fields have been added. It adds the End Section ( "7777" ) +// to the end of the GRIB message and calculates the length and stores +// it in the appropriate place in Section 0. +// This routine is used with routines "g2_create", "g2_addlocal", +// "g2_addgrid", and "g2_addfield" to create a complete GRIB2 message. +// g2_create must be called first to initialize a new GRIB2 message. +// +// PROGRAM HISTORY LOG: +// 2002-10-31 Gilbert +// +// USAGE: int g2_gribend(unsigned char *cgrib) +// INPUT ARGUMENT: +// cgrib - Char array containing all the data sections added +// be previous calls to g2_create, g2_addlocal, g2_addgrid, +// and g2_addfield. +// +// OUTPUT ARGUMENTS: +// cgrib - Char array containing the finalized GRIB2 message +// +// RETURN VALUES: +// ierr - Return code. +// > 0 = Length of the final GRIB2 message in bytes. +// -1 = GRIB message was not initialized. Need to call +// routine g2_create first. +// -2 = GRIB message already complete. +// -3 = Sum of Section byte counts doesn't add to total byte count +// -4 = Previous Section was not 7. +// +// REMARKS: This routine is intended for use with routines "g2_create", +// "g2_addlocal", "g2_addgrid", and "g2_addfield" to create a complete +// GRIB2 message. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + + g2int iofst,lencurr,len,ilen,isecnum; + g2int ierr,lengrib; + static unsigned char G=0x47; // 'G' + static unsigned char R=0x52; // 'R' + static unsigned char I=0x49; // 'I' + static unsigned char B=0x42; // 'B' + static unsigned char seven=0x37; // '7' + + ierr=0; +// +// Check to see if beginning of GRIB message exists +// + if ( cgrib[0]!=G || cgrib[1]!=R || cgrib[2]!=I || cgrib[3]!=B ) { + printf("g2_gribend: GRIB not found in given message.\n"); + ierr=-1; + return (ierr); + } +// +// Get current length of GRIB message +// + gbit(cgrib,&lencurr,96,32); +// +// Loop through all current sections of the GRIB message to +// find the last section number. +// + len=16; // Length of Section 0 + for (;;) { + // Get number and length of next section + iofst=len*8; + gbit(cgrib,&ilen,iofst,32); + iofst=iofst+32; + gbit(cgrib,&isecnum,iofst,8); + len=len+ilen; + // Exit loop if last section reached + if ( len == lencurr ) break; + // If byte count for each section doesn't match current + // total length, then there is a problem. + if ( len > lencurr ) { + printf("g2_gribend: Section byte counts don''t add to total.\n"); + printf("g2_gribend: Sum of section byte counts = %d\n",(int)len); + printf("g2_gribend: Total byte count in Section 0 = %d\n",(int)lencurr); + ierr=-3; + return (ierr); + } + } +// +// Can only add End Section (Section 8) after Section 7. +// + if ( isecnum != 7 ) { + printf("g2_gribend: Section 8 can only be added after Section 7.\n"); + printf("g2_gribend: Section %d was the last found in given GRIB message.\n",isecnum); + ierr=-4; + return (ierr); + } +// +// Add Section 8 - End Section +// + //cgrib(lencurr+1:lencurr+4)=c7777 + cgrib[lencurr]=seven; + cgrib[lencurr+1]=seven; + cgrib[lencurr+2]=seven; + cgrib[lencurr+3]=seven; + +// +// Update current byte total of message in Section 0 +// + lengrib=lencurr+4; + sbit(cgrib,&lengrib,96,32); + + return (lengrib); + +} + diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_info.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_info.c new file mode 100644 index 000000000..738afe3f5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_info.c @@ -0,0 +1,190 @@ +#include +#include +#include "grib2.h" + +g2int g2_info(unsigned char *cgrib,g2int *listsec0,g2int *listsec1, + g2int *numfields,g2int *numlocal) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_info +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-28 +// +// ABSTRACT: This subroutine searches through a GRIB2 message and +// returns the number of gridded fields found in the message and +// the number (and maximum size) of Local Use Sections. +// Also various checks are performed +// to see if the message is a valid GRIB2 message. +// +// PROGRAM HISTORY LOG: +// 2002-10-28 Gilbert +// +// USAGE: int g2_info(unsigned char *cgrib,g2int *listsec0,g2int *listsec1, +// g2int *numfields,g2int *numlocal) +// INPUT ARGUMENT: +// cgrib - Character pointer to the GRIB2 message +// +// OUTPUT ARGUMENTS: +// listsec0 - pointer to an array containing information decoded from +// GRIB Indicator Section 0. +// Must be allocated with >= 3 elements. +// listsec0[0]=Discipline-GRIB Master Table Number +// (see Code Table 0.0) +// listsec0[1]=GRIB Edition Number (currently 2) +// listsec0[2]=Length of GRIB message +// listsec1 - pointer to an array containing information read from GRIB +// Identification Section 1. +// Must be allocated with >= 13 elements. +// listsec1[0]=Id of orginating centre (Common Code Table C-1) +// listsec1[1]=Id of orginating sub-centre (local table) +// listsec1[2]=GRIB Master Tables Version Number (Code Table 1.0) +// listsec1[3]=GRIB Local Tables Version Number +// listsec1[4]=Significance of Reference Time (Code Table 1.1) +// listsec1[5]=Reference Time - Year (4 digits) +// listsec1[6]=Reference Time - Month +// listsec1[7]=Reference Time - Day +// listsec1[8]=Reference Time - Hour +// listsec1[9]=Reference Time - Minute +// listsec1[10]=Reference Time - Second +// listsec1[11]=Production status of data (Code Table 1.2) +// listsec1[12]=Type of processed data (Code Table 1.3) +// numfields- The number of gridded fields found in the GRIB message. +// That is, the number of occurences of Sections 4 - 7. +// numlocal - The number of Local Use Sections ( Section 2 ) found in +// the GRIB message. +// +// RETURN VALUES: +// ierr - Error return code. +// 0 = no error +// 1 = Beginning characters "GRIB" not found. +// 2 = GRIB message is not Edition 2. +// 3 = Could not find Section 1, where expected. +// 4 = End string "7777" found, but not where expected. +// 5 = End string "7777" not found at end of message. +// 6 = Invalid section number found. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + + g2int ierr,mapsec1len=13; + g2int mapsec1[13]={2,2,1,1,1,2,1,1,1,1,1,1,1}; + g2int i,j,istart,iofst,lengrib,lensec0,lensec1; + g2int ipos,isecnum,nbits,lensec; + + ierr=0; + *numlocal=0; + *numfields=0; +// +// Check for beginning of GRIB message in the first 100 bytes +// + istart=-1; + for (j=0;j<100;j++) { + if (cgrib[j]=='G' && cgrib[j+1]=='R' &&cgrib[j+2]=='I' && + cgrib[j+3]=='B') { + istart=j; + break; + } + } + if (istart == -1) { + printf("g2_info: Beginning characters GRIB not found."); + ierr=1; + return(ierr); + } +// +// Unpack Section 0 - Indicator Section +// + iofst=8*(istart+6); + gbit(cgrib,listsec0+0,iofst,8); // Discipline + iofst=iofst+8; + gbit(cgrib,listsec0+1,iofst,8); // GRIB edition number + iofst=iofst+8; + iofst=iofst+32; + gbit(cgrib,&lengrib,iofst,32); // Length of GRIB message + iofst=iofst+32; + listsec0[2]=lengrib; + lensec0=16; + ipos=istart+lensec0; +// +// Currently handles only GRIB Edition 2. +// + if (listsec0[1] != 2) { + printf("g2_info: can only decode GRIB edition 2."); + ierr=2; + return(ierr); + } +// +// Unpack Section 1 - Identification Section +// + gbit(cgrib,&lensec1,iofst,32); // Length of Section 1 + iofst=iofst+32; + gbit(cgrib,&isecnum,iofst,8); // Section number ( 1 ) + iofst=iofst+8; + if (isecnum != 1) { + printf("g2_info: Could not find section 1."); + ierr=3; + return(ierr); + } + // + // Unpack each input value in array listsec1 into the + // the appropriate number of octets, which are specified in + // corresponding entries in array mapsec1. + // + for (i=0;i (istart+lengrib)) { + printf("g2_info: '7777' not found at end of GRIB message.\n"); + ierr=5; + return(ierr); + } + if ( isecnum>=2 && isecnum<=7 ) { + if (isecnum == 2) // Local Section 2 + // increment counter for total number of local sections found + (*numlocal)++; + + else if (isecnum == 4) + // increment counter for total number of fields found + (*numfields)++; + } + else { + printf("g2_info: Invalid section number found in GRIB message: %d\n" ,isecnum); + ierr=6; + return(ierr); + } + + } + + return(0); + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_miss.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_miss.c new file mode 100644 index 000000000..3dab744b0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_miss.c @@ -0,0 +1,69 @@ +#include "grib2.h" + +void g2_miss( gribfield *gfld, float *rmiss, int *nmiss ) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_miss +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2004-12-16 +// +// ABSTRACT: This routine checks the Data Representation Template to see if +// missing value management is used, and returns the missing value(s) +// in the data field. +// +// PROGRAM HISTORY LOG: +// 2004-12-16 Gilbert +// +// USAGE: g2_miss( gribfield *gfld, float *rmiss, int *nmiss ) +// +// INPUT ARGUMENT LIST: +// *gfld - pointer to gribfield structure (defined in include file +// grib2.h) +// +// OUTPUT ARGUMENT LIST: +// rmiss - List of the missing values used +// nmiss - NUmber of the missing values included in the field +// +// REMARKS: rmiss must be allocated in the calling program with enough space +// hold all the missing values. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: IBM SP +// +//$$$ +{ + g2int itype; + + /* + * Missing value management currnetly only used in + * DRT's 5.2 and 5.3. + */ + if ( gfld->idrtnum != 2 && gfld->idrtnum != 3 ) { + *nmiss=0; + return; + } + + itype = gfld->idrtmpl[4]; + if ( gfld->idrtmpl[6] == 1 ) { + *nmiss=1; + if (itype == 0) + rdieee(gfld->idrtmpl+7,rmiss+0,1); + else + rmiss[0]=(float)gfld->idrtmpl[7]; + } + else if ( gfld->idrtmpl[6] == 2 ) { + *nmiss=2; + if (itype == 0) { + rdieee(gfld->idrtmpl+7,rmiss+0,1); + rdieee(gfld->idrtmpl+8,rmiss+1,1); + } + else { + rmiss[0]=(float)gfld->idrtmpl[7]; + rmiss[1]=(float)gfld->idrtmpl[8]; + } + } + else { + *nmiss=0; + } + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack1.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack1.c new file mode 100644 index 000000000..02114e909 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack1.c @@ -0,0 +1,99 @@ +#include +#include +#include "grib2.h" + +g2int g2_unpack1(unsigned char *cgrib,g2int *iofst,g2int **ids,g2int *idslen) +/*//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_unpack1 +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-29 +// +// ABSTRACT: This subroutine unpacks Section 1 (Identification Section) +// as defined in GRIB Edition 2. +// +// PROGRAM HISTORY LOG: +// 2002-10-29 Gilbert +// +// USAGE: int g2_unpack1(unsigned char *cgrib,g2int *iofst,g2int **ids, +// g2int *idslen) +// INPUT ARGUMENTS: +// cgrib - char array containing Section 1 of the GRIB2 message +// iofst - Bit offset for the beginning of Section 1 in cgrib. +// +// OUTPUT ARGUMENTS: +// iofst - Bit offset at the end of Section 1, returned. +// ids - address of pointer to integer array containing information +// read from Section 1, the Identification section. +// ids[0] = Identification of originating Centre +// ( see Common Code Table C-1 ) +// ids[1] = Identification of originating Sub-centre +// ids[2] = GRIB Master Tables Version Number +// ( see Code Table 1.0 ) +// ids[3] = GRIB Local Tables Version Number +// ( see Code Table 1.1 ) +// ids[4] = Significance of Reference Time (Code Table 1.2) +// ids[5] = Year ( 4 digits ) +// ids[6] = Month +// ids[7] = Day +// ids[8] = Hour +// ids[9] = Minute +// ids[10] = Second +// ids[11] = Production status of processed data +// ( see Code Table 1.3 ) +// ids[12] = Type of processed data ( see Code Table 1.4 ) +// idslen - Number of elements in ids[]. +// +// RETURN VALUES: +// ierr - Error return code. +// 0 = no error +// 2 = Array passed is not section 1 +// 6 = memory allocation error +// +// REMARKS: +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +*/ +{ + + g2int i,lensec,nbits,ierr,isecnum; + g2int mapid[13]={2,2,1,1,1,2,1,1,1,1,1,1,1}; + + ierr=0; + *idslen=13; + *ids=0; + + gbit(cgrib,&lensec,*iofst,32); // Get Length of Section + *iofst=*iofst+32; + gbit(cgrib,&isecnum,*iofst,8); // Get Section Number + *iofst=*iofst+8; + + if ( isecnum != 1 ) { + ierr=2; + *idslen=13; + fprintf(stderr,"g2_unpack1: Not Section 1 data.\n"); + return(ierr); + } + + // + // Unpack each value into array ids from the + // the appropriate number of octets, which are specified in + // corresponding entries in array mapid. + // + *ids=(g2int *)calloc(*idslen,sizeof(g2int)); + if (*ids == 0) { + ierr=6; + return(ierr); + } + + for (i=0;i<*idslen;i++) { + nbits=mapid[i]*8; + gbit(cgrib,*ids+i,*iofst,nbits); + *iofst=*iofst+nbits; + } + + return(ierr); // End of Section 1 processing +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack2.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack2.c new file mode 100644 index 000000000..ebab94b04 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack2.c @@ -0,0 +1,79 @@ +#include +#include +#include "grib2.h" + +g2int g2_unpack2(unsigned char *cgrib,g2int *iofst,g2int *lencsec2,unsigned char **csec2) +////$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_unpack2 +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-31 +// +// ABSTRACT: This subroutine unpacks Section 2 (Local Use Section) +// as defined in GRIB Edition 2. +// +// PROGRAM HISTORY LOG: +// 2002-10-31 Gilbert +// +// USAGE: int g2_unpack2(unsigned char *cgrib,g2int *iofst,g2int *lencsec2, +// unsigned char **csec2) +// INPUT ARGUMENT LIST: +// cgrib - char array containing Section 2 of the GRIB2 message +// iofst - Bit offset for the beginning of Section 2 in cgrib. +// +// OUTPUT ARGUMENT LIST: +// iofst - Bit offset at the end of Section 2, returned. +// lencsec2 - Length (in octets) of Local Use data +// csec2 - Pointer to a char array containing local use data +// +// RETURN VALUES: +// ierr - Error return code. +// 0 = no error +// 2 = Array passed is not section 2 +// 6 = memory allocation error +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$// +{ + + g2int ierr,isecnum; + g2int lensec,ipos,j; + + ierr=0; + *lencsec2=0; + *csec2=0; // NULL + + gbit(cgrib,&lensec,*iofst,32); // Get Length of Section + *iofst=*iofst+32; + *lencsec2=lensec-5; + gbit(cgrib,&isecnum,*iofst,8); // Get Section Number + *iofst=*iofst+8; + ipos=(*iofst/8); + + if ( isecnum != 2 ) { + ierr=2; + *lencsec2=0; + fprintf(stderr,"g2_unpack2: Not Section 2 data.\n"); + return(ierr); + } + + *csec2=(unsigned char *)malloc(*lencsec2); + if (*csec2 == 0) { + ierr=6; + *lencsec2=0; + return(ierr); + } + + //printf(" SAGIPO %d \n",(int)ipos); + for (j=0;j<*lencsec2;j++) { + *(*csec2+j)=cgrib[ipos+j]; + } + *iofst=*iofst+(*lencsec2*8); + + return(ierr); // End of Section 2 processing + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack3.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack3.c new file mode 100644 index 000000000..911fc807b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack3.c @@ -0,0 +1,213 @@ +#include +#include +#include "grib2.h" + + +g2int g2_unpack3(unsigned char *cgrib,g2int *iofst,g2int **igds,g2int **igdstmpl, + g2int *mapgridlen,g2int **ideflist,g2int *idefnum) +////$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_unpack3 +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-31 +// +// ABSTRACT: This routine unpacks Section 3 (Grid Definition Section) +// as defined in GRIB Edition 2. +// +// PROGRAM HISTORY LOG: +// 2002-10-31 Gilbert +// +// USAGE: int g2_unpack3(unsigned char *cgrib,g2int *iofst,g2int **igds, +// g2int **igdstmpl,g2int *mapgridlen, +// g2int **ideflist,g2int *idefnum) +// INPUT ARGUMENTS: +// cgrib - Char array ontaining Section 3 of the GRIB2 message +// iofst - Bit offset for the beginning of Section 3 in cgrib. +// +// OUTPUT ARGUMENTS: +// iofst - Bit offset at the end of Section 3, returned. +// igds - Contains information read from the appropriate GRIB Grid +// Definition Section 3 for the field being returned. +// igds[0]=Source of grid definition (see Code Table 3.0) +// igds[1]=Number of grid points in the defined grid. +// igds[2]=Number of octets needed for each +// additional grid points definition. +// Used to define number of +// points in each row ( or column ) for +// non-regular grids. +// = 0, if using regular grid. +// igds[3]=Interpretation of list for optional points +// definition. (Code Table 3.11) +// igds[4]=Grid Definition Template Number (Code Table 3.1) +// igdstmpl - Pointer to integer array containing the data values for +// the specified Grid Definition +// Template ( NN=igds[4] ). Each element of this integer +// array contains an entry (in the order specified) of Grid +// Defintion Template 3.NN +// mapgridlen- Number of elements in igdstmpl[]. i.e. number of entries +// in Grid Defintion Template 3.NN ( NN=igds[4] ). +// ideflist - (Used if igds[2] .ne. 0) Pointer to integer array containing +// the number of grid points contained in each row ( or column ). +// (part of Section 3) +// idefnum - (Used if igds[2] .ne. 0) The number of entries +// in array ideflist. i.e. number of rows ( or columns ) +// for which optional grid points are defined. +// ierr - Error return code. +// 0 = no error +// 2 = Not Section 3 +// 5 = "GRIB" message contains an undefined Grid Definition +// Template. +// 6 = memory allocation error +// +// REMARKS: +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ + +{ + g2int ierr,i,j,nbits,isecnum; + g2int lensec,ibyttem=0,isign,newlen; + g2int *ligds,*ligdstmpl=0,*lideflist=0; + xxtemplate *mapgrid; + + ierr=0; + *igds=0; // NULL + *igdstmpl=0; // NULL + *ideflist=0; // NULL + + gbit(cgrib,&lensec,*iofst,32); // Get Length of Section + *iofst=*iofst+32; + gbit(cgrib,&isecnum,*iofst,8); // Get Section Number + *iofst=*iofst+8; + + if ( isecnum != 3 ) { + ierr=2; + *idefnum=0; + *mapgridlen=0; + // fprintf(stderr,"g2_unpack3: Not Section 3 data.\n"); + return(ierr); + } + + ligds=(g2int *)calloc(5,sizeof(g2int)); + *igds=ligds; + + gbit(cgrib,ligds+0,*iofst,8); // Get source of Grid def. + *iofst=*iofst+8; + gbit(cgrib,ligds+1,*iofst,32); // Get number of grid pts. + *iofst=*iofst+32; + gbit(cgrib,ligds+2,*iofst,8); // Get num octets for opt. list + *iofst=*iofst+8; + gbit(cgrib,ligds+3,*iofst,8); // Get interpret. for opt. list + *iofst=*iofst+8; + gbit(cgrib,ligds+4,*iofst,16); // Get Grid Def Template num. + *iofst=*iofst+16; + + if (ligds[4] != 65535) { + // Get Grid Definition Template + mapgrid=getgridtemplate(ligds[4]); + if (mapgrid == 0) { // undefined template + ierr=5; + return(ierr); + } + *mapgridlen=mapgrid->maplen; + // + // Unpack each value into array igdstmpl from the + // the appropriate number of octets, which are specified in + // corresponding entries in array mapgrid. + // + if (*mapgridlen > 0) { + ligdstmpl=0; + ligdstmpl=(g2int *)calloc(*mapgridlen,sizeof(g2int)); + if (ligdstmpl == 0) { + ierr=6; + *mapgridlen=0; + *igdstmpl=0; //NULL + if( mapgrid != 0 ) free(mapgrid); + return(ierr); + } + else { + *igdstmpl=ligdstmpl; + } + } + ibyttem=0; + for (i=0;i<*mapgridlen;i++) { + nbits=abs(mapgrid->map[i])*8; + if ( mapgrid->map[i] >= 0 ) { + gbit(cgrib,ligdstmpl+i,*iofst,nbits); + } + else { + gbit(cgrib,&isign,*iofst,1); + gbit(cgrib,ligdstmpl+i,*iofst+1,nbits-1); + if (isign == 1) ligdstmpl[i]=-1*ligdstmpl[i]; + } + *iofst=*iofst+nbits; + ibyttem=ibyttem+abs(mapgrid->map[i]); + } + // + // Check to see if the Grid Definition Template needs to be + // extended. + // The number of values in a specific template may vary + // depending on data specified in the "static" part of the + // template. + // + if ( mapgrid->needext == 1 ) { + free(mapgrid); + mapgrid=extgridtemplate(ligds[4],ligdstmpl); + // Unpack the rest of the Grid Definition Template + newlen=mapgrid->maplen+mapgrid->extlen; + ligdstmpl=(g2int *)realloc(ligdstmpl,newlen*sizeof(g2int)); + *igdstmpl=ligdstmpl; + j=0; + for (i=*mapgridlen;iext[j])*8; + if ( mapgrid->ext[j] >= 0 ) { + gbit(cgrib,ligdstmpl+i,*iofst,nbits); + } + else { + gbit(cgrib,&isign,*iofst,1); + gbit(cgrib,ligdstmpl+i,*iofst+1,nbits-1); + if (isign == 1) ligdstmpl[i]=-1*ligdstmpl[i]; + } + *iofst=*iofst+nbits; + ibyttem=ibyttem+abs(mapgrid->ext[j]); + j++; + } + *mapgridlen=newlen; + } + if( mapgrid->ext != 0 ) free(mapgrid->ext); + if( mapgrid != 0 ) free(mapgrid); + } + else { // No Grid Definition Template + *mapgridlen=0; + *igdstmpl=0; + } + // + // Unpack optional list of numbers defining number of points + // in each row or column, if included. This is used for non regular + // grids. + // + if ( ligds[2] != 0 ) { + nbits=ligds[2]*8; + *idefnum=(lensec-14-ibyttem)/ligds[2]; + if (*idefnum > 0) lideflist=(g2int *)calloc(*idefnum,sizeof(g2int)); + if (lideflist == 0) { + ierr=6; + *idefnum=0; + *ideflist=0; //NULL + return(ierr); + } + else { + *ideflist=lideflist; + } + gbits(cgrib,lideflist,*iofst,nbits,0,*idefnum); + *iofst=*iofst+(nbits*(*idefnum)); + } + else { + *idefnum=0; + *ideflist=0; // NULL + } + + return(ierr); // End of Section 3 processing +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack4.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack4.c new file mode 100644 index 000000000..8af7576e6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack4.c @@ -0,0 +1,184 @@ +#include +#include +#include "grib2.h" + + +g2int g2_unpack4(unsigned char *cgrib,g2int *iofst,g2int *ipdsnum,g2int **ipdstmpl, + g2int *mappdslen,g2float **coordlist,g2int *numcoord) +////$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_unpack4 +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-31 +// +// ABSTRACT: This subroutine unpacks Section 4 (Product Definition Section) +// as defined in GRIB Edition 2. +// +// PROGRAM HISTORY LOG: +// 2002-10-31 Gilbert +// +// USAGE: int g2_unpack4(unsigned char *cgrib,g2int *iofst,g2int *ipdsnum, +// g2int **ipdstmpl,g2int *mappdslen, +// g2float **coordlist,g2int *numcoord) +// INPUT ARGUMENTS: +// cgrib - Char array containing Section 4 of the GRIB2 message +// iofst - Bit offset of the beginning of Section 4 in cgrib. +// +// OUTPUT ARGUMENTS: +// iofst - Bit offset of the end of Section 4, returned. +// ipdsnum - Product Definition Template Number ( see Code Table 4.0) +// ipdstmpl - Pointer to integer array containing the data values for +// the specified Product Definition +// Template ( N=ipdsnum ). Each element of this integer +// array contains an entry (in the order specified) of Product +// Defintion Template 4.N +// mappdslen- Number of elements in ipdstmpl[]. i.e. number of entries +// in Product Defintion Template 4.N ( N=ipdsnum ). +// coordlist- Pointer to real array containing floating point values +// intended to document +// the vertical discretisation associated to model data +// on hybrid coordinate vertical levels. (part of Section 4) +// numcoord - number of values in array coordlist. +// +// RETURN VALUES: +// ierr - Error return code. +// 0 = no error +// 2 = Not section 4 +// 5 = "GRIB" message contains an undefined Product Definition +// Template. +// 6 = memory allocation error +// +// REMARKS: +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$// +{ + + g2int ierr,needext,i,j,nbits,isecnum; + g2int lensec,isign,newlen; + g2int *coordieee; + g2int *lipdstmpl=0; + g2float *lcoordlist; + xxtemplate *mappds; + + ierr=0; + *ipdstmpl=0; // NULL + *coordlist=0; // NULL + + gbit(cgrib,&lensec,*iofst,32); // Get Length of Section + *iofst=*iofst+32; + gbit(cgrib,&isecnum,*iofst,8); // Get Section Number + *iofst=*iofst+8; + + if ( isecnum != 4 ) { + ierr=2; + *numcoord=0; + *mappdslen=0; + // fprintf(stderr,"g2_unpack4: Not Section 4 data.\n"); + return(ierr); + } + + gbit(cgrib,numcoord,*iofst,16); // Get num of coordinate values + *iofst=*iofst+16; + gbit(cgrib,ipdsnum,*iofst,16); // Get Prod. Def Template num. + *iofst=*iofst+16; + + // Get Product Definition Template + mappds=getpdstemplate(*ipdsnum); + if (mappds == 0) { // undefine template + ierr=5; + *mappdslen=0; + return(ierr); + } + *mappdslen=mappds->maplen; + needext=mappds->needext; + // + // Unpack each value into array ipdstmpl from the + // the appropriate number of octets, which are specified in + // corresponding entries in array mappds. + // + if (*mappdslen > 0) lipdstmpl=(g2int *)calloc(*mappdslen,sizeof(g2int)); + if (lipdstmpl == 0) { + ierr=6; + *mappdslen=0; + *ipdstmpl=0; //NULL + if ( mappds != 0 ) free(mappds); + return(ierr); + } + else { + *ipdstmpl=lipdstmpl; + } + for (i=0;imaplen;i++) { + nbits=abs(mappds->map[i])*8; + if ( mappds->map[i] >= 0 ) { + gbit(cgrib,lipdstmpl+i,*iofst,nbits); + } + else { + gbit(cgrib,&isign,*iofst,1); + gbit(cgrib,lipdstmpl+i,*iofst+1,nbits-1); + if (isign == 1) lipdstmpl[i]=-1*lipdstmpl[i]; + } + *iofst=*iofst+nbits; + } + // + // Check to see if the Product Definition Template needs to be + // extended. + // The number of values in a specific template may vary + // depending on data specified in the "static" part of the + // template. + // + if ( needext ==1 ) { + free(mappds); + mappds=extpdstemplate(*ipdsnum,lipdstmpl); + newlen=mappds->maplen+mappds->extlen; + lipdstmpl=(g2int *)realloc(lipdstmpl,newlen*sizeof(g2int)); + *ipdstmpl=lipdstmpl; + // Unpack the rest of the Product Definition Template + j=0; + for (i=*mappdslen;iext[j])*8; + if ( mappds->ext[j] >= 0 ) { + gbit(cgrib,lipdstmpl+i,*iofst,nbits); + } + else { + gbit(cgrib,&isign,*iofst,1); + gbit(cgrib,lipdstmpl+i,*iofst+1,nbits-1); + if (isign == 1) lipdstmpl[i]=-1*lipdstmpl[i]; + } + *iofst=*iofst+nbits; + j++; + } + *mappdslen=newlen; + } + if( mappds->ext != 0 ) free(mappds->ext); + if( mappds != 0 ) free(mappds); + // + // Get Optional list of vertical coordinate values + // after the Product Definition Template, if necessary. + // + *coordlist=0; // NULL + if ( *numcoord != 0 ) { + coordieee=(g2int *)calloc(*numcoord,sizeof(g2int)); + lcoordlist=(g2float *)calloc(*numcoord,sizeof(g2float)); + if (coordieee == 0 || lcoordlist == 0) { + ierr=6; + *numcoord=0; + *coordlist=0; // NULL + if( coordieee != 0 ) free(coordieee); + if( lcoordlist != 0 ) free(lcoordlist); + return(ierr); + } + else { + *coordlist=lcoordlist; + } + gbits(cgrib,coordieee,*iofst,32,0,*numcoord); + rdieee(coordieee,*coordlist,*numcoord); + free(coordieee); + *iofst=*iofst+(32*(*numcoord)); + } + + return(ierr); // End of Section 4 processing + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack5.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack5.c new file mode 100644 index 000000000..61cb6dd93 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack5.c @@ -0,0 +1,151 @@ +#include +#include +#include "grib2.h" + + +g2int g2_unpack5(unsigned char *cgrib,g2int *iofst,g2int *ndpts,g2int *idrsnum, + g2int **idrstmpl,g2int *mapdrslen) +////$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_unpack5 +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-31 +// +// ABSTRACT: This subroutine unpacks Section 5 (Data Representation Section) +// as defined in GRIB Edition 2. +// +// PROGRAM HISTORY LOG: +// 2002-10-31 Gilbert +// +// USAGE: int g2_unpack5(unsigned char *cgrib,g2int *iofst,g2int *ndpts, +// g2int *idrsnum,g2int **idrstmpl,g2int *mapdrslen) +// INPUT ARGUMENTS: +// cgrib - char array containing Section 5 of the GRIB2 message +// iofst - Bit offset for the beginning of Section 5 in cgrib. +// +// OUTPUT ARGUMENTS: +// iofst - Bit offset at the end of Section 5, returned. +// ndpts - Number of data points unpacked and returned. +// idrsnum - Data Representation Template Number ( see Code Table 5.0) +// idrstmpl - Pointer to an integer array containing the data values for +// the specified Data Representation +// Template ( N=idrsnum ). Each element of this integer +// array contains an entry (in the order specified) of Data +// Representation Template 5.N +// mapdrslen- Number of elements in idrstmpl[]. i.e. number of entries +// in Data Representation Template 5.N ( N=idrsnum ). +// +// RETURN VALUES: +// ierr - Error return code. +// 0 = no error +// 2 = Not Section 5 +// 6 = memory allocation error +// 7 = "GRIB" message contains an undefined Data +// Representation Template. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$// +{ + g2int ierr,needext,i,j,nbits,isecnum; + g2int lensec,isign,newlen; + g2int *lidrstmpl=0; + xxtemplate *mapdrs; + + ierr=0; + *idrstmpl=0; //NULL + + gbit(cgrib,&lensec,*iofst,32); // Get Length of Section + *iofst=*iofst+32; + gbit(cgrib,&isecnum,*iofst,8); // Get Section Number + *iofst=*iofst+8; + + if ( isecnum != 5 ) { + ierr=2; + *ndpts=0; + *mapdrslen=0; + // fprintf(stderr,"g2_unpack5: Not Section 5 data.\n"); + return(ierr); + } + + gbit(cgrib,ndpts,*iofst,32); // Get num of data points + *iofst=*iofst+32; + gbit(cgrib,idrsnum,*iofst,16); // Get Data Rep Template Num. + *iofst=*iofst+16; + + // Gen Data Representation Template + mapdrs=getdrstemplate(*idrsnum); + if (mapdrs == 0) { + ierr=7; + *mapdrslen=0; + return(ierr); + } + *mapdrslen=mapdrs->maplen; + needext=mapdrs->needext; + // + // Unpack each value into array ipdstmpl from the + // the appropriate number of octets, which are specified in + // corresponding entries in array mapdrs. + // + if (*mapdrslen > 0) lidrstmpl=(g2int *)calloc(*mapdrslen,sizeof(g2int)); + if (lidrstmpl == 0) { + ierr=6; + *mapdrslen=0; + *idrstmpl=0; //NULL + if ( mapdrs != 0 ) free(mapdrs); + return(ierr); + } + else { + *idrstmpl=lidrstmpl; + } + for (i=0;imaplen;i++) { + nbits=abs(mapdrs->map[i])*8; + if ( mapdrs->map[i] >= 0 ) { + gbit(cgrib,lidrstmpl+i,*iofst,nbits); + } + else { + gbit(cgrib,&isign,*iofst,1); + gbit(cgrib,lidrstmpl+i,*iofst+1,nbits-1); + if (isign == 1) lidrstmpl[i]=-1*lidrstmpl[i]; + } + *iofst=*iofst+nbits; + } + // + // Check to see if the Data Representation Template needs to be + // extended. + // The number of values in a specific template may vary + // depending on data specified in the "static" part of the + // template. + // + if ( needext == 1 ) { + free(mapdrs); + mapdrs=extdrstemplate(*idrsnum,lidrstmpl); + newlen=mapdrs->maplen+mapdrs->extlen; + lidrstmpl=(g2int *)realloc(lidrstmpl,newlen*sizeof(g2int)); + *idrstmpl=lidrstmpl; + // Unpack the rest of the Data Representation Template + j=0; + for (i=*mapdrslen;iext[j])*8; + if ( mapdrs->ext[j] >= 0 ) { + gbit(cgrib,lidrstmpl+i,*iofst,nbits); + } + else { + gbit(cgrib,&isign,*iofst,1); + gbit(cgrib,lidrstmpl+i,*iofst+1,nbits-1); + if (isign == 1) lidrstmpl[i]=-1*lidrstmpl[i]; + } + *iofst=*iofst+nbits; + j++; + } + *mapdrslen=newlen; + } + if( mapdrs->ext != 0 ) free(mapdrs->ext); + if( mapdrs != 0 ) free(mapdrs); + + return(ierr); // End of Section 5 processing + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack6.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack6.c new file mode 100644 index 000000000..9f6da7ee5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/g2_unpack6.c @@ -0,0 +1,97 @@ +#include +#include +#include "grib2.h" + +g2int g2_unpack6(unsigned char *cgrib,g2int *iofst,g2int ngpts,g2int *ibmap, + g2int **bmap) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_unpack6 +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-31 +// +// ABSTRACT: This subroutine unpacks Section 6 (Bit-Map Section) +// as defined in GRIB Edition 2. +// +// PROGRAM HISTORY LOG: +// 2002-10-31 Gilbert +// +// USAGE: int g2_unpack6(unsigned char *cgrib,g2int *iofst,g2int ngpts, +// g2int *ibmap,g2int **bmap) +// INPUT ARGUMENTS: +// cgrib - char array containing Section 6 of the GRIB2 message +// iofst - Bit offset of the beginning of Section 6 in cgrib. +// ngpts - Number of grid points specified in the bit-map +// +// OUTPUT ARGUMENTS: +// iofst - Bit offset at the end of Section 6, returned. +// ibmap - Bitmap indicator ( see Code Table 6.0 ) +// 0 = bitmap applies and is included in Section 6. +// 1-253 = Predefined bitmap applies +// 254 = Previously defined bitmap applies to this field +// 255 = Bit map does not apply to this product. +// bmap - Pointer to an integer array containing decoded bitmap. +// ( if ibmap=0 ) +// +// RETURN VALUES: +// ierr - Error return code. +// 0 = no error +// 2 = Not Section 6 +// 4 = Unrecognized pre-defined bit-map. +// 6 = memory allocation error +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$// +{ + g2int j,ierr,isecnum; + g2int *lbmap=0; + g2int *intbmap; + + ierr=0; + *bmap=0; //NULL + + *iofst=*iofst+32; // skip Length of Section + gbit(cgrib,&isecnum,*iofst,8); // Get Section Number + *iofst=*iofst+8; + + if ( isecnum != 6 ) { + ierr=2; + fprintf(stderr,"g2_unpack6: Not Section 6 data.\n"); + return(ierr); + } + + gbit(cgrib,ibmap,*iofst,8); // Get bit-map indicator + *iofst=*iofst+8; + + if (*ibmap == 0) { // Unpack bitmap + if (ngpts > 0) lbmap=(g2int *)calloc(ngpts,sizeof(g2int)); + if (lbmap == 0) { + ierr=6; + return(ierr); + } + else { + *bmap=lbmap; + } + intbmap=(g2int *)calloc(ngpts,sizeof(g2int)); + gbits(cgrib,intbmap,*iofst,1,0,ngpts); + *iofst=*iofst+ngpts; + for (j=0;j +#include +#include +#include +#include "grib2.h" + +g2int simunpack(unsigned char *,g2int *, g2int,g2float *); +int comunpack(unsigned char *,g2int,g2int,g2int *,g2int,g2float *); +g2int specunpack(unsigned char *,g2int *,g2int,g2int,g2int, g2int, g2float *); +g2int jpcunpack(unsigned char *,g2int,g2int *,g2int, g2float *); + +#ifdef USE_PNG + g2int pngunpack(unsigned char *,g2int,g2int *,g2int, g2float *); +#endif /* USE_PNG */ + + + +g2int g2_unpack7(unsigned char *cgrib,g2int *iofst,g2int igdsnum,g2int *igdstmpl, + g2int idrsnum,g2int *idrstmpl,g2int ndpts,g2float **fld) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_unpack7 +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-31 +// +// ABSTRACT: This subroutine unpacks Section 7 (Data Section) +// as defined in GRIB Edition 2. +// +// PROGRAM HISTORY LOG: +// 2002-10-31 Gilbert +// 2002-12-20 Gilbert - Added GDT info to arguments +// and added 5.51 processing. +// 2003-08-29 Gilbert - Added support for new templates using +// PNG and JPEG2000 algorithms/templates. +// 2004-11-29 Gilbert - JPEG2000 now allowed to use WMO Template no. 5.40 +// PNG now allowed to use WMO Template no. 5.41 +// 2004-12-16 Taylor - Added check on comunpack return code. +// +// USAGE: int g2_unpack7(unsigned char *cgrib,g2int *iofst,g2int igdsnum, +// g2int *igdstmpl, g2int idrsnum, +// g2int *idrstmpl, g2int ndpts,g2float **fld) +// INPUT ARGUMENTS: +// cgrib - char array containing Section 7 of the GRIB2 message +// iofst - Bit offset of the beginning of Section 7 in cgrib. +// igdsnum - Grid Definition Template Number ( see Code Table 3.0) +// ( Only used for DRS Template 5.51 ) +// igdstmpl - Pointer to an integer array containing the data values for +// the specified Grid Definition +// Template ( N=igdsnum ). Each element of this integer +// array contains an entry (in the order specified) of Grid +// Definition Template 3.N +// ( Only used for DRS Template 5.51 ) +// idrsnum - Data Representation Template Number ( see Code Table 5.0) +// idrstmpl - Pointer to an integer array containing the data values for +// the specified Data Representation +// Template ( N=idrsnum ). Each element of this integer +// array contains an entry (in the order specified) of Data +// Representation Template 5.N +// ndpts - Number of data points unpacked and returned. +// +// OUTPUT ARGUMENTS: +// iofst - Bit offset at the end of Section 7, returned. +// fld - Pointer to a float array containing the unpacked data field. +// +// RETURN VALUES: +// ierr - Error return code. +// 0 = no error +// 2 = Not section 7 +// 4 = Unrecognized Data Representation Template +// 5 = need one of GDT 3.50 through 3.53 to decode DRT 5.51 +// 6 = memory allocation error +// 7 = corrupt section 7. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$// +{ + g2int ierr,isecnum; + g2int ipos,lensec; + g2float *lfld; + + ierr=0; + *fld=0; //NULL + + gbit(cgrib,&lensec,*iofst,32); // Get Length of Section + *iofst=*iofst+32; + gbit(cgrib,&isecnum,*iofst,8); // Get Section Number + *iofst=*iofst+8; + + if ( isecnum != 7 ) { + ierr=2; + //fprintf(stderr,"g2_unpack7: Not Section 7 data.\n"); + return(ierr); + } + + ipos=(*iofst/8); + lfld=(g2float *)calloc(ndpts,sizeof(g2float)); + if (lfld == 0) { + ierr=6; + return(ierr); + } + else { + *fld=lfld; + } + + if (idrsnum == 0) + simunpack(cgrib+ipos,idrstmpl,ndpts,lfld); + else if (idrsnum == 2 || idrsnum == 3) { + if (comunpack(cgrib+ipos,lensec,idrsnum,idrstmpl,ndpts,lfld) != 0) { + return 7; + } + } + else if (idrsnum == 50) { // Spectral Simple + simunpack(cgrib+ipos,idrstmpl,ndpts-1,lfld+1); + rdieee(idrstmpl+4,lfld+0,1); + } + else if (idrsnum == 51) // Spectral complex + if ( igdsnum>=50 && igdsnum <=53 ) + specunpack(cgrib+ipos,idrstmpl,ndpts,igdstmpl[0],igdstmpl[2],igdstmpl[2],lfld); + else { + fprintf(stderr,"g2_unpack7: Cannot use GDT 3.%d to unpack Data Section 5.51.\n",(int)igdsnum); + ierr=5; + if ( lfld != 0 ) free(lfld); + *fld=0; //NULL + return(ierr); + } + else if (idrsnum == 40 || idrsnum == 40000) { + jpcunpack(cgrib+ipos,lensec-5,idrstmpl,ndpts,lfld); + } +#ifdef USE_PNG + else if (idrsnum == 41 || idrsnum == 40010) { + pngunpack(cgrib+ipos,lensec-5,idrstmpl,ndpts,lfld); + } +#endif /* USE_PNG */ + else { + fprintf(stderr,"g2_unpack7: Data Representation Template 5.%d not yet implemented.\n",(int)idrsnum); + ierr=4; + if ( lfld != 0 ) free(lfld); + *fld=0; //NULL + return(ierr); + } + + *iofst=*iofst+(8*lensec); + + return(ierr); // End of Section 7 processing + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/gbits.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/gbits.c new file mode 100644 index 000000000..34709dab3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/gbits.c @@ -0,0 +1,124 @@ +#include "grib2.h" + +void gbit(unsigned char *in,g2int *iout,g2int iskip,g2int nbyte) +{ + gbits(in,iout,iskip,nbyte,(g2int)0,(g2int)1); +} + +void sbit(unsigned char *out,g2int *in,g2int iskip,g2int nbyte) +{ + sbits(out,in,iskip,nbyte,(g2int)0,(g2int)1); +} + + +void gbits(unsigned char *in,g2int *iout,g2int iskip,g2int nbyte,g2int nskip, + g2int n) +/* Get bits - unpack bits: Extract arbitrary size values from a +/ packed bit string, right justifying each value in the unpacked +/ iout array. +/ *in = pointer to character array input +/ *iout = pointer to unpacked array output +/ iskip = initial number of bits to skip +/ nbyte = number of bits to take +/ nskip = additional number of bits to skip on each iteration +/ n = number of iterations +/ v1.1 +*/ +{ + g2int i,tbit,bitcnt,ibit,itmp; + g2int nbit,index; + static g2int ones[]={1,3,7,15,31,63,127,255}; + +// nbit is the start position of the field in bits + nbit = iskip; + for (i=0;i>= (8-ibit-tbit); + index++; + bitcnt = bitcnt - tbit; + +// now transfer whole bytes + while (bitcnt >= 8) { + itmp = itmp<<8 | (int)*(in+index); + bitcnt = bitcnt - 8; + index++; + } + +// get data from last byte + if (bitcnt > 0) { + itmp = ( itmp << bitcnt ) | ( ((int)*(in+index) >> (8-bitcnt)) & ones[bitcnt-1] ); + } + + *(iout+i) = itmp; + } +} + + +void sbits(unsigned char *out,g2int *in,g2int iskip,g2int nbyte,g2int nskip, + g2int n) +/*C Store bits - pack bits: Put arbitrary size values into a +/ packed bit string, taking the low order bits from each value +/ in the unpacked array. +/ *iout = pointer to packed array output +/ *in = pointer to unpacked array input +/ iskip = initial number of bits to skip +/ nbyte = number of bits to pack +/ nskip = additional number of bits to skip on each iteration +/ n = number of iterations +/ v1.1 +*/ +{ + g2int i,bitcnt,tbit,ibit,itmp,imask,itmp2,itmp3; + g2int nbit,index; + static g2int ones[]={1,3,7,15,31,63,127,255}; + +// number bits from zero to ... +// nbit is the last bit of the field to be filled + + nbit = iskip + nbyte - 1; + for (i=0;i> tbit; + index--; + } + +// now byte aligned + +// do by bytes + while (bitcnt >= 8) { + out[index] = (unsigned char)(itmp & 255); + itmp = itmp >> 8; + bitcnt = bitcnt - 8; + index--; + } + +// do last byte + + if (bitcnt > 0) { + itmp2 = itmp & ones[bitcnt-1]; + itmp3 = (int)*(out+index) & (255-ones[bitcnt-1]); + out[index] = (unsigned char)(itmp2 | itmp3); + } + } + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/getdim.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/getdim.c new file mode 100644 index 000000000..c86228fc9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/getdim.c @@ -0,0 +1,127 @@ +#include +#include +#include "grib2.h" + +g2int g2_unpack3(unsigned char *,g2int *,g2int **,g2int **, + g2int *,g2int **,g2int *); + +g2int getdim(unsigned char *csec3,g2int *width,g2int *height,g2int *iscan) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: getdim +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-12-11 +// +// ABSTRACT: This subroutine returns the dimensions and scanning mode of +// a grid definition packed in GRIB2 Grid Definition Section 3 format. +// +// PROGRAM HISTORY LOG: +// 2002-12-11 Gilbert +// +// USAGE: int getdim(unsigned char *csec3,g2int *width, +// g2int *height, g2int *iscan) +// INPUT ARGUMENT LIST: +// csec3 - Character array that contains the packed GRIB2 GDS +// +// OUTPUT ARGUMENT LIST: +// width - x (or i) dimension of the grid. +// height - y (or j) dimension of the grid. +// iscan - Scanning mode ( see Code Table 3.4 ) +// +// REMARKS: Returns width and height set to zero, if grid template +// not recognized. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: IBM SP +// +//$$$ +{ + + g2int *igdstmpl,*list_opt; + g2int *igds; + g2int iofst,igdtlen,num_opt,jerr; + + igdstmpl=0; + list_opt=0; + igds=0; + iofst=0; // set offset to beginning of section + jerr= g2_unpack3(csec3,&iofst,&igds,&igdstmpl, + &igdtlen,&list_opt,&num_opt); + if (jerr == 0) { + switch ( igds[4] ) // Template number + { + case 0: // Lat/Lon + case 1: + case 2: + case 3: + { + *width=igdstmpl[7]; + *height=igdstmpl[8]; + *iscan=igdstmpl[18]; + break; + } + case 10: // Mercator + { + *width=igdstmpl[7]; + *height=igdstmpl[8]; + *iscan=igdstmpl[15]; + break; + } + case 20: // Polar Stereographic + { + *width=igdstmpl[7]; + *height=igdstmpl[8]; + *iscan=igdstmpl[17]; + break; + } + case 30: // Lambert Conformal + { + *width=igdstmpl[7]; + *height=igdstmpl[8]; + *iscan=igdstmpl[17]; + break; + } + case 40: // Gaussian + case 41: + case 42: + case 43: + { + *width=igdstmpl[7]; + *height=igdstmpl[8]; + *iscan=igdstmpl[18]; + break; + } + case 90: // Space View/Orthographic + { + *width=igdstmpl[7]; + *height=igdstmpl[8]; + *iscan=igdstmpl[16]; + break; + } + case 110: // Equatorial Azimuthal + { + *width=igdstmpl[7]; + *height=igdstmpl[8]; + *iscan=igdstmpl[15]; + break; + } + default: + { + *width=0; + *height=0; + *iscan=0; + break; + } + } // end switch + } + else { + *width=0; + *height=0; + } + + if (igds != 0) free(igds); + if (igdstmpl != 0) free(igdstmpl); + if (list_opt != 0) free(list_opt); + + return 0; +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/getpoly.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/getpoly.c new file mode 100644 index 000000000..9e2a5a69b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/getpoly.c @@ -0,0 +1,80 @@ +#include +#include +#include "grib2.h" + +g2int g2_unpack3(unsigned char *,g2int *,g2int **,g2int **, + g2int *,g2int **,g2int *); + +g2int getpoly(unsigned char *csec3,g2int *jj,g2int *kk,g2int *mm) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: getpoly +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-12-11 +// +// ABSTRACT: This subroutine returns the J, K, and M pentagonal resolution +// parameters specified in a GRIB Grid Definition Section used +// spherical harmonic coefficients using GDT 5.50 through 5.53 +// +// PROGRAM HISTORY LOG: +// 2002-12-11 Gilbert +// +// USAGE: int getpoly(unsigned char *csec3,g2int *jj,g2int *kk,g2int *mm) +// INPUT ARGUMENTS: +// csec3 - Character array that contains the packed GRIB2 GDS +// +// OUTPUT ARGUMENTS: +// JJ = J - pentagonal resolution parameter +// KK = K - pentagonal resolution parameter +// MM = M - pentagonal resolution parameter +// +// REMARKS: Returns JJ, KK, and MM set to zero, if grid template +// not recognized. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: IBM SP +// +//$$$ +{ + + g2int *igdstmpl,*list_opt; + g2int *igds; + g2int iofst,igdtlen,num_opt,jerr; + + iofst=0; // set offset to beginning of section + jerr=g2_unpack3(csec3,&iofst,&igds,&igdstmpl, + &igdtlen,&list_opt,&num_opt); + if (jerr == 0) { + switch ( igds[4] ) // Template number + { + case 50: // Spherical harmonic coefficients + case 51: + case 52: + case 53: + { + *jj=igdstmpl[0]; + *kk=igdstmpl[1]; + *mm=igdstmpl[2]; + break; + } + default: + { + *jj=0; + *kk=0; + *mm=0; + break; + } + } // end switch + } + else { + *jj=0; + *kk=0; + *mm=0; + } + + if (igds != 0) free(igds); + if (igdstmpl != 0) free(igdstmpl); + if (list_opt != 0) free(list_opt); + + return 0; +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/grib2.h b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/grib2.h new file mode 100644 index 000000000..810e7d6f9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/grib2.h @@ -0,0 +1,241 @@ +#ifndef _grib2_H +#define _grib2_H +#include + +#define G2_VERSION "g2clib-1.0.4" +/* . . . . +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-25 +// +// Each element of structure gribfield is defined as: +// +// gribfield gfld; +// +// gfld->version = GRIB edition number ( currently 2 ) +// gfld->discipline = Message Discipline ( see Code Table 0.0 ) +// gfld->idsect = Contains the entries in the Identification +// Section ( Section 1 ) +// This element is a pointer to an array +// that holds the data. +// gfld->idsect[0] = Identification of originating Centre +// ( see Common Code Table C-1 ) +// 7 - US National Weather Service +// gfld->idsect[1] = Identification of originating Sub-centre +// gfld->idsect[2] = GRIB Master Tables Version Number +// ( see Code Table 1.0 ) +// 0 - Experimental +// 1 - Initial operational version number +// gfld->idsect[3] = GRIB Local Tables Version Number +// ( see Code Table 1.1 ) +// 0 - Local tables not used +// 1-254 - Number of local tables version used +// gfld->idsect[4] = Significance of Reference Time (Code Table 1.2) +// 0 - Analysis +// 1 - Start of forecast +// 2 - Verifying time of forecast +// 3 - Observation time +// gfld->idsect[5] = Year ( 4 digits ) +// gfld->idsect[6] = Month +// gfld->idsect[7) = Day +// gfld->idsect[8] = Hour +// gfld->idsect[9] = Minute +// gfld->idsect[10] = Second +// gfld->idsect[11] = Production status of processed data +// ( see Code Table 1.3 ) +// 0 - Operational products +// 1 - Operational test products +// 2 - Research products +// 3 - Re-analysis products +// gfld->idsect[12] = Type of processed data ( see Code Table 1.4 ) +// 0 - Analysis products +// 1 - Forecast products +// 2 - Analysis and forecast products +// 3 - Control forecast products +// 4 - Perturbed forecast products +// 5 - Control and perturbed forecast products +// 6 - Processed satellite observations +// 7 - Processed radar observations +// gfld->idsectlen = Number of elements in gfld->idsect[]. +// gfld->local = Pointer to character array containing contents +// of Local Section 2, if included +// gfld->locallen = length of array gfld->local[] +// gfld->ifldnum = field number within GRIB message +// gfld->griddef = Source of grid definition (see Code Table 3.0) +// 0 - Specified in Code table 3.1 +// 1 - Predetermined grid Defined by originating centre +// gfld->ngrdpts = Number of grid points in the defined grid. +// gfld->numoct_opt = Number of octets needed for each +// additional grid points definition. +// Used to define number of +// points in each row ( or column ) for +// non-regular grids. +// = 0, if using regular grid. +// gfld->interp_opt = Interpretation of list for optional points +// definition. (Code Table 3.11) +// gfld->igdtnum = Grid Definition Template Number (Code Table 3.1) +// gfld->igdtmpl = Contains the data values for the specified Grid +// Definition Template ( NN=gfld->igdtnum ). Each +// element of this integer array contains an entry (in +// the order specified) of Grid Defintion Template 3.NN +// This element is a pointer to an array +// that holds the data. +// gfld->igdtlen = Number of elements in gfld->igdtmpl[]. i.e. number of +// entries in Grid Defintion Template 3.NN +// ( NN=gfld->igdtnum ). +// gfld->list_opt = (Used if gfld->numoct_opt .ne. 0) This array +// contains the number of grid points contained in +// each row ( or column ). (part of Section 3) +// This element is a pointer to an array +// that holds the data. This pointer is nullified +// if gfld->numoct_opt=0. +// gfld->num_opt = (Used if gfld->numoct_opt .ne. 0) The number of entries +// in array ideflist. i.e. number of rows ( or columns ) +// for which optional grid points are defined. This value +// is set to zero, if gfld->numoct_opt=0. +// gfdl->ipdtnum = Product Definition Template Number (see Code Table 4.0) +// gfld->ipdtmpl = Contains the data values for the specified Product +// Definition Template ( N=gfdl->ipdtnum ). Each element +// of this integer array contains an entry (in the +// order specified) of Product Defintion Template 4.N. +// This element is a pointer to an array +// that holds the data. +// gfld->ipdtlen = Number of elements in gfld->ipdtmpl[]. i.e. number of +// entries in Product Defintion Template 4.N +// ( N=gfdl->ipdtnum ). +// gfld->coord_list = Real array containing floating point values +// intended to document the vertical discretisation +// associated to model data on hybrid coordinate +// vertical levels. (part of Section 4) +// This element is a pointer to an array +// that holds the data. +// gfld->num_coord = number of values in array gfld->coord_list[]. +// gfld->ndpts = Number of data points unpacked and returned. +// gfld->idrtnum = Data Representation Template Number +// ( see Code Table 5.0) +// gfld->idrtmpl = Contains the data values for the specified Data +// Representation Template ( N=gfld->idrtnum ). Each +// element of this integer array contains an entry +// (in the order specified) of Product Defintion +// Template 5.N. +// This element is a pointer to an array +// that holds the data. +// gfld->idrtlen = Number of elements in gfld->idrtmpl[]. i.e. number +// of entries in Data Representation Template 5.N +// ( N=gfld->idrtnum ). +// gfld->unpacked = logical value indicating whether the bitmap and +// data values were unpacked. If false, +// gfld->bmap and gfld->fld pointers are nullified. +// gfld->expanded = Logical value indicating whether the data field +// was expanded to the grid in the case where a +// bit-map is present. If true, the data points in +// gfld->fld match the grid points and zeros were +// inserted at grid points where data was bit-mapped +// out. If false, the data values in gfld->fld were +// not expanded to the grid and are just a consecutive +// array of data points corresponding to each value of +// "1" in gfld->bmap. +// gfld->ibmap = Bitmap indicator ( see Code Table 6.0 ) +// 0 = bitmap applies and is included in Section 6. +// 1-253 = Predefined bitmap applies +// 254 = Previously defined bitmap applies to this field +// 255 = Bit map does not apply to this product. +// gfld->bmap = integer array containing decoded bitmap, +// if gfld->ibmap=0 or gfld->ibap=254. Otherwise nullified. +// This element is a pointer to an array +// that holds the data. +// gfld->fld = Array of gfld->ndpts unpacked data points. +// This element is a pointer to an array +// that holds the data. +*/ + +typedef int g2int; +typedef unsigned int g2intu; +typedef float g2float; + +typedef struct { + g2int type; /* 3=Grid Defintion Template. */ + /* 4=Product Defintion Template. */ + /* 5=Data Representation Template. */ + g2int num; /* template number. */ + g2int maplen; /* number of entries in the static part */ + /* of the template. */ + g2int *map; /* num of octets of each entry in the */ + /* static part of the template. */ + g2int needext; /* indicates whether or not the template needs */ + /* to be extended. */ + g2int extlen; /* number of entries in the template extension. */ + g2int *ext; /* num of octets of each entry in the extension */ + /* part of the template. */ +} xxtemplate; + +//typedef struct template template; // otherwise VC6 gives "error C2447: missing function header (old-style formal list?)" + +struct gribfield { + g2int version,discipline; + g2int *idsect; + g2int idsectlen; + unsigned char *local; + g2int locallen; + g2int ifldnum; + g2int griddef,ngrdpts; + g2int numoct_opt,interp_opt,num_opt; + g2int *list_opt; + g2int igdtnum,igdtlen; + g2int *igdtmpl; + g2int ipdtnum,ipdtlen; + g2int *ipdtmpl; + g2int num_coord; + g2float *coord_list; + g2int ndpts,idrtnum,idrtlen; + g2int *idrtmpl; + g2int unpacked; + g2int expanded; + g2int ibmap; + g2int *bmap; + g2float *fld; +}; + +typedef struct gribfield gribfield; + +#define RINT(d) (floor(d+0.5)) + +/* Prototypes for unpacking API */ +void seekgb(FILE *,g2int ,g2int ,g2int *,g2int *); +g2int g2_info(unsigned char *,g2int *,g2int *,g2int *,g2int *); +g2int g2_getfld(unsigned char *,g2int ,g2int ,g2int ,gribfield **); +void g2_free(gribfield *); + +/* Prototypes for packing API */ +g2int g2_create(unsigned char *,g2int *,g2int *); +g2int g2_addlocal(unsigned char *,unsigned char *,g2int ); +g2int g2_addgrid(unsigned char *,g2int *,g2int *,g2int *,g2int ); +g2int g2_addfield(unsigned char *,g2int ,g2int *, + g2float *,g2int ,g2int ,g2int *, + g2float *,g2int ,g2int ,g2int *); +g2int g2_gribend(unsigned char *); + +/* Prototypes for supporting routines */ +extern double int_power(double, g2int ); +extern void mkieee(g2float *,g2int *,g2int); +void rdieee(g2int *,g2float *,g2int ); +extern xxtemplate *getpdstemplate(g2int); +extern xxtemplate *extpdstemplate(g2int,g2int *); +extern xxtemplate *getdrstemplate(g2int); +extern xxtemplate *extdrstemplate(g2int,g2int *); +extern xxtemplate *getgridtemplate(g2int); +extern xxtemplate *extgridtemplate(g2int,g2int *); +extern void simpack(g2float *,g2int,g2int *,unsigned char *,g2int *); +extern void compack(g2float *,g2int,g2int,g2int *,unsigned char *,g2int *); +void misspack(g2float *,g2int ,g2int ,g2int *, unsigned char *, g2int *); +void gbit(unsigned char *,g2int *,g2int ,g2int ); +void sbit(unsigned char *,g2int *,g2int ,g2int ); +void gbits(unsigned char *,g2int *,g2int ,g2int ,g2int ,g2int ); +void sbits(unsigned char *,g2int *,g2int ,g2int ,g2int ,g2int ); + +int pack_gp(g2int *, g2int *, g2int *, + g2int *, g2int *, g2int *, g2int *, g2int *, + g2int *, g2int *, g2int *, g2int *, + g2int *, g2int *, g2int *, g2int *, g2int *, + g2int *, g2int *, g2int *); + +#endif /* _grib2_H */ + diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/grib2c.doc b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/grib2c.doc new file mode 100644 index 000000000..4ad7c3411 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/grib2c.doc @@ -0,0 +1,946 @@ + GRIB2 USERS GUIDE (C) + +Contents: + +- Introduction +- GRIB2 Tables/Templates +- GRIB2 Encoding Routines +- GRIB2 Decoding Routines +- Decoding all GRIB2 Fields in a File +- Extracting Selected GRIB2 Fields from a GRIB2 file +- GRIB2 Routine Documentation + +=============================================================================== + + Introduction + +This document briefly describes the routines available for encoding/decoding +GRIB Edition 2 (GRIB2) messages. A basic familiarity with GRIB is assumed. + +A GRIB Edition 2 message is a machine independent format for storing +one or more gridded data fields. Each GRIB2 message consists of the +following sections: + +SECTION 0 - Indicator Section +SECTION 1 - Identification Section +SECTION 2 - (Local Use Section) - optional } +SECTION 3 - Grid Definition Section } } +SECTION 4 - Product Definition Section } } }(repeated) +SECTION 5 - Data Representation Section } }(repeated) } +SECTION 6 - Bit-map Section }(repeated) } } +SECTION 7 - Data Section } } } +SECTION 8 - End Section } } } + +Sequences of GRIB sections 2 to 7, 3 to 7, or sections 4 to 7 may be repeated +within a single GRIB message. All sections within such repeated sequences +must be present and shall appear in the numerical order noted above. +Unrepeated sections remain in effect until redefined. + +The above overview was taken from WMO's FM 92-XII GRIB description +of the GRIB Edition 2 form. + +=============================================================================== + + GRIB2 Tables/Templates + +WMO's GRIB2 specification "FM 92-XII GRIB - General Regularly-distributed +Information in Binary Form" contains descriptions of each template +and code table information. This document can be found at +http://www.wmo.ch/web/www/WMOCodes.html +(PDF and MSWord formats are available) + +=============================================================================== + + GRIB2 Encoding Routines + +Since a GRIB2 message can contain gridded fields for many parameters on +a number of different grids, several routines are used to encode a message. +This should give users more flexibility in how to organize data +within one or more GRIB2 messages. + +To start a new GRIB2 message, call function g2_create. G2_create +encodes Sections 0 and 1 at the beginning of the message. This routine +must be used to create each message. + +Routine g2_addlocal can be used to add a Local Use Section ( Section 2 ). +Note that this section is optional and need not appear in a GRIB2 message. + +Function g2_addgrid is used to encode a grid definition into Section 3. +This grid definition defines the geometry of the the data values in the +fields that follow it. g2_addgrid can be called again to change the grid +definition describing subsequent data fields. + +Each data field is added to the GRIB2 message using routine g2_addfield, +which adds Sections 4, 5, 6, and 7 to the message. + +After all desired data fields have been added to the GRIB2 message, a +call to function g2_gribend is needed to add the final section 8 to the +message and to update the length of the message. A call to g2_gribend +is required for each GRIB2 message. + +Please see the "GRIB2 Routine Documentation" section below for subroutine +argument usage for the routines mentioned above. + +=============================================================================== + + GRIB2 Decoding Routines + +Routine g2_info can be used to find out how many Local Use sections +and data fields are contained in a given GRIB2 message. This routine also +returns all the information stored in Sections 0 and 1 of the GRIB2 +message. + +g2_getfld can be used to get all information pertaining to the nth +data field in the message. The subroutine returns all the unpacked metadata +for each Section and Template in a gribfield structure, +which is defined in include file grib2.h. An option exists that lets the +user decide if the decoder should unpack the Bit-map ( if applicable ) +and the data values or just return the field description information. + +Note that a struct gribfield is allocated by g2_getfld, and it also +contains pointers to many arrays of data that are alloated during decoding. +Because of this, users are encouraged to free up this memory, when it is +no longer needed, by an explicit call to routine g2_free. + +Please see the "GRIB2 Routine Documentation" section below for subroutine +argument usage for the routines mentioned above. + +=============================================================================== + Decoding all GRIB2 Fields in a File + +Routines seekgb, g2_info and g2_getfld can be used to sequentially decode +all the GRIB2 fields in a file. This is illustrated by the following example: + + #include + #include + #include "grib2.h" + + unsigned char *cgrib; + g2int listsec0[3],listsec1[13],numlocal,numfields; + long lskip,n,lgrib,iseek; + int unpack,ret,ierr; + gribfield *gfld; + FILE *fptr; + size_t lengrib; + + iseek=0; + unpack=1; + expand=1; + fptr=fopen("inputgribfile","r"); + for (;;) { + seekgb(fptr,iseek,32000,&lskip,&lgrib); + if (lgrib == 0) break; // end loop at EOF or problem + cgrib=(unsigned char *)malloc(lgrib); + ret=fseek(fptr,lskip,SEEK_SET); + lengrib=fread(cgrib,sizeof(unsigned char),lgrib,fptr); + iseek=lskip+lgrib; + ierr=g2_info(cgrib,listsec0,listsec1,&numfields,&numlocal); + for (n=0;nversion = GRIB edition number ( currently 2 ) +// gfld->discipline = Message Discipline ( see Code Table 0.0 ) +// gfld->idsect = Contains the entries in the Identification +// Section ( Section 1 ) +// This element is a pointer to an array +// that holds the data. +// gfld->idsect[0] = Identification of originating Centre +// ( see Common Code Table C-1 ) +// 7 - US National Weather Service +// gfld->idsect[1] = Identification of originating Sub-centre +// gfld->idsect[2] = GRIB Master Tables Version Number +// ( see Code Table 1.0 ) +// 0 - Experimental +// 1 - Initial operational version number +// gfld->idsect[3] = GRIB Local Tables Version Number +// ( see Code Table 1.1 ) +// 0 - Local tables not used +// 1-254 - Number of local tables version used +// gfld->idsect[4] = Significance of Reference Time (Code Table 1.2) +// 0 - Analysis +// 1 - Start of forecast +// 2 - Verifying time of forecast +// 3 - Observation time +// gfld->idsect[5] = Year ( 4 digits ) +// gfld->idsect[6] = Month +// gfld->idsect[7) = Day +// gfld->idsect[8] = Hour +// gfld->idsect[9] = Minute +// gfld->idsect[10] = Second +// gfld->idsect[11] = Production status of processed data +// ( see Code Table 1.3 ) +// 0 - Operational products +// 1 - Operational test products +// 2 - Research products +// 3 - Re-analysis products +// gfld->idsect[12] = Type of processed data ( see Code Table 1.4 ) +// 0 - Analysis products +// 1 - Forecast products +// 2 - Analysis and forecast products +// 3 - Control forecast products +// 4 - Perturbed forecast products +// 5 - Control and perturbed forecast products +// 6 - Processed satellite observations +// 7 - Processed radar observations +// gfld->idsectlen = Number of elements in gfld->idsect[]. +// gfld->local = Pointer to character array containing contents +// of Local Section 2, if included +// gfld->locallen = length of array gfld->local[] +// gfld->ifldnum = field number within GRIB message +// gfld->griddef = Source of grid definition (see Code Table 3.0) +// 0 - Specified in Code table 3.1 +// 1 - Predetermined grid Defined by originating centre +// gfld->ngrdpts = Number of grid points in the defined grid. +// gfld->numoct_opt = Number of octets needed for each +// additional grid points definition. +// Used to define number of +// points in each row ( or column ) for +// non-regular grids. +// = 0, if using regular grid. +// gfld->interp_opt = Interpretation of list for optional points +// definition. (Code Table 3.11) +// gfld->igdtnum = Grid Definition Template Number (Code Table 3.1) +// gfld->igdtmpl = Contains the data values for the specified Grid +// Definition Template ( NN=gfld->igdtnum ). Each +// element of this integer array contains an entry (in +// the order specified) of Grid Defintion Template 3.NN +// This element is a pointer to an array +// that holds the data. +// gfld->igdtlen = Number of elements in gfld->igdtmpl[]. i.e. number of +// entries in Grid Defintion Template 3.NN +// ( NN=gfld->igdtnum ). +// gfld->list_opt = (Used if gfld->numoct_opt .ne. 0) This array +// contains the number of grid points contained in +// each row ( or column ). (part of Section 3) +// This element is a pointer to an array +// that holds the data. This pointer is nullified +// if gfld->numoct_opt=0. +// gfld->num_opt = (Used if gfld->numoct_opt .ne. 0) +// The number of entries +// in array ideflist. i.e. number of rows ( or columns ) +// for which optional grid points are defined. This value +// is set to zero, if gfld->numoct_opt=0. +// gfdl->ipdtnum = Product Definition Template Number(see Code Table 4.0) +// gfld->ipdtmpl = Contains the data values for the specified Product +// Definition Template ( N=gfdl->ipdtnum ). Each element +// of this integer array contains an entry (in the +// order specified) of Product Defintion Template 4.N. +// This element is a pointer to an array +// that holds the data. +// gfld->ipdtlen = Number of elements in gfld->ipdtmpl[]. i.e. number of +// entries in Product Defintion Template 4.N +// ( N=gfdl->ipdtnum ). +// gfld->coord_list = Real array containing floating point values +// intended to document the vertical discretisation +// associated to model data on hybrid coordinate +// vertical levels. (part of Section 4) +// This element is a pointer to an array +// that holds the data. +// gfld->num_coord = number of values in array gfld->coord_list[]. +// gfld->ndpts = Number of data points unpacked and returned. +// gfld->idrtnum = Data Representation Template Number +// ( see Code Table 5.0) +// gfld->idrtmpl = Contains the data values for the specified Data +// Representation Template ( N=gfld->idrtnum ). Each +// element of this integer array contains an entry +// (in the order specified) of Product Defintion +// Template 5.N. +// This element is a pointer to an array +// that holds the data. +// gfld->idrtlen = Number of elements in gfld->idrtmpl[]. i.e. number +// of entries in Data Representation Template 5.N +// ( N=gfld->idrtnum ). +// gfld->unpacked = logical value indicating whether the bitmap and +// data values were unpacked. If false, +// gfld->bmap and gfld->fld pointers are nullified. +// gfld->expanded = Logical value indicating whether the data field +// was expanded to the grid in the case where a +// bit-map is present. If true, the data points in +// gfld->fld match the grid points and zeros were +// inserted at grid points where data was bit-mapped +// out. If false, the data values in gfld->fld were +// not expanded to the grid and are just a consecutive +// array of data points corresponding to each value of +// "1" in gfld->bmap. +// gfld->ibmap = Bitmap indicator ( see Code Table 6.0 ) +// 0 = bitmap applies and is included in Section 6. +// 1-253 = Predefined bitmap applies +// 254 = Previously defined bitmap applies to this field +// 255 = Bit map does not apply to this product. +// gfld->bmap = integer array containing decoded bitmap, +// if gfld->ibmap=0 or gfld->ibap=254. +// Otherwise nullified. +// This element is a pointer to an array +// that holds the data. +// gfld->fld = Array of gfld->ndpts unpacked data points. +// This element is a pointer to an array +// that holds the data. +/////////////////////////////////////////////////////////////////////////// + + + +//$$$ SUBPROGRAM DOCUMENTATION BLOCK //////////////////////////////////// +// +// SUBPROGRAM: seekgb Searches a file for the next GRIB message. +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-28 +// +// ABSTRACT: This subprogram searches a file for the next GRIB Message. +// The search is done starting at byte offset iseek of the file referenced +// by lugb for mseek bytes at a time. +// If found, the starting position and length of the message are returned +// in lskip and lgrib, respectively. +// The search is terminated when an EOF or I/O error is encountered. +// +// PROGRAM HISTORY LOG: +// 2002-10-28 GILBERT Modified from Iredell's skgb subroutine +// +// USAGE: seekgb(FILE *lugb,long iseek,long mseek,int *lskip,int *lgrib) +// INPUT ARGUMENTS: +// lugb - FILE pointer for the file to search. File must be +// opened before this routine is called. +// iseek - number of bytes in the file to skip before search +// mseek - number of bytes to search at a time +// OUTPUT ARGUMENTS: +// lskip - number of bytes to skip from the beggining of the file +// to where the GRIB message starts +// lgrib - number of bytes in message (set to 0, if no message found) +// +// ATTRIBUTES: +// LANGUAGE: C +// +////////////////////////////////////////////////////////////////////////// + + + +//$$$ SUBPROGRAM DOCUMENTATION BLOCK /////////////////////////////////// +// . . . . +// SUBPROGRAM: g2_info +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-28 +// +// ABSTRACT: This subroutine searches through a GRIB2 message and +// returns the number of gridded fields found in the message and +// the number (and maximum size) of Local Use Sections. +// Also various checks are performed +// to see if the message is a valid GRIB2 message. +// +// PROGRAM HISTORY LOG: +// 2002-10-28 Gilbert +// +// USAGE: int g2_info(unsigned char *cgrib,g2int *listsec0,g2int *listsec1, +// g2int *numfields,g2int *numlocal) +// INPUT ARGUMENT: +// cgrib - Character pointer to the GRIB2 message +// +// OUTPUT ARGUMENTS: +// listsec0 - pointer to an array containing information decoded from +// GRIB Indicator Section 0. +// Must be allocated with >= 3 elements. +// listsec0[0]=Discipline-GRIB Master Table Number +// (see Code Table 0.0) +// listsec0[1]=GRIB Edition Number (currently 2) +// listsec0[2]=Length of GRIB message +// listsec1 - pointer to an array containing information read from GRIB +// Identification Section 1. +// Must be allocated with >= 13 elements. +// listsec1[0]=Id of orginating centre (Common Code Table C-1) +// listsec1[1]=Id of orginating sub-centre (local table) +// listsec1[2]=GRIB Master Tables Version Number (Code Table 1.0) +// listsec1[3]=GRIB Local Tables Version Number +// listsec1[4]=Significance of Reference Time (Code Table 1.1) +// listsec1[5]=Reference Time - Year (4 digits) +// listsec1[6]=Reference Time - Month +// listsec1[7]=Reference Time - Day +// listsec1[8]=Reference Time - Hour +// listsec1[9]=Reference Time - Minute +// listsec1[10]=Reference Time - Second +// listsec1[11]=Production status of data (Code Table 1.2) +// listsec1[12]=Type of processed data (Code Table 1.3) +// numfields- The number of gridded fields found in the GRIB message. +// That is, the number of occurences of Sections 4 - 7. +// numlocal - The number of Local Use Sections ( Section 2 ) found in +// the GRIB message. +// +// RETURN VALUES: +// ierr - Error return code. +// 0 = no error +// 1 = Beginning characters "GRIB" not found. +// 2 = GRIB message is not Edition 2. +// 3 = Could not find Section 1, where expected. +// 4 = End string "7777" found, but not where expected. +// 5 = End string "7777" not found at end of message. +// 6 = Invalid section number found. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +////////////////////////////////////////////////////////////////////////// + + + +//$$$ SUBPROGRAM DOCUMENTATION BLOCK /////////////////////////////////// +// . . . . +// SUBPROGRAM: g2_getfld +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-28 +// +// ABSTRACT: This subroutine returns all the metadata, template values, +// Bit-map ( if applicable ), and the unpacked data for a given data +// field. All of the information returned is stored in a gribfield +// structure, which is defined in file grib2.h. +// Users of this routine will need to include "grib2.h" in their source +// code that calls this routine. Each component of the gribfield +// struct is also described in the OUTPUT ARGUMENTS section below. +// +// Since there can be multiple data fields packed into a GRIB2 +// message, the calling routine indicates which field is being requested +// with the ifldnum argument. +// +// PROGRAM HISTORY LOG: +// 2002-10-28 Gilbert +// +// USAGE: #include "grib2.h" +// int g2_getfld(unsigned char *cgrib,g2int ifldnum,g2int unpack, +// g2int expand,gribfield **gfld) +// INPUT ARGUMENTS: +// cgrib - Character pointer to the GRIB2 message +// ifldnum - Specifies which field in the GRIB2 message to return. +// unpack - Boolean value indicating whether to unpack bitmap/data +// 1 = unpack bitmap and data values +// 0 = do not unpack bitmap and data values +// expand - Boolean value indicating whether the data points should be +// expanded to the correspond grid, if a bit-map is present. +// 1 = if possible, expand data field to grid, inserting zero +// values at gridpoints that are bitmapped out. +// (SEE REMARKS2) +// 0 = do not expand data field, leaving it an array of +// consecutive data points for each "1" in the bitmap. +// This argument is ignored if unpack == 0 OR if the +// returned field does not contain a bit-map. +// +// OUTPUT ARGUMENT: +// gribfield gfld; - pointer to structure gribfield containing +// all decoded data for the data field. +// +// gfld->version = GRIB edition number ( currently 2 ) +// gfld->discipline = Message Discipline ( see Code Table 0.0 ) +// gfld->idsect = Contains the entries in the Identification +// Section ( Section 1 ) +// This element is a pointer to an array +// that holds the data. +// gfld->idsect[0] = Identification of originating Centre +// ( see Common Code Table C-1 ) +// 7 - US National Weather Service +// gfld->idsect[1] = Identification of originating Sub-centre +// gfld->idsect[2] = GRIB Master Tables Version Number +// ( see Code Table 1.0 ) +// 0 - Experimental +// 1 - Initial operational version number +// gfld->idsect[3] = GRIB Local Tables Version Number +// ( see Code Table 1.1 ) +// 0 - Local tables not used +// 1-254 - Number of local tables version used +// gfld->idsect[4] = Significance of Reference Time (Code Table 1.2) +// 0 - Analysis +// 1 - Start of forecast +// 2 - Verifying time of forecast +// 3 - Observation time +// gfld->idsect[5] = Year ( 4 digits ) +// gfld->idsect[6] = Month +// gfld->idsect[7) = Day +// gfld->idsect[8] = Hour +// gfld->idsect[9] = Minute +// gfld->idsect[10] = Second +// gfld->idsect[11] = Production status of processed data +// ( see Code Table 1.3 ) +// 0 - Operational products +// 1 - Operational test products +// 2 - Research products +// 3 - Re-analysis products +// gfld->idsect[12] = Type of processed data ( see Code Table 1.4 ) +// 0 - Analysis products +// 1 - Forecast products +// 2 - Analysis and forecast products +// 3 - Control forecast products +// 4 - Perturbed forecast products +// 5 - Control and perturbed forecast products +// 6 - Processed satellite observations +// 7 - Processed radar observations +// gfld->idsectlen = Number of elements in gfld->idsect[]. +// gfld->local = Pointer to character array containing contents +// of Local Section 2, if included +// gfld->locallen = length of array gfld->local[] +// gfld->ifldnum = field number within GRIB message +// gfld->griddef = Source of grid definition (see Code Table 3.0) +// 0 - Specified in Code table 3.1 +// 1 - Predetermined grid Defined by originating centre +// gfld->ngrdpts = Number of grid points in the defined grid. +// gfld->numoct_opt = Number of octets needed for each +// additional grid points definition. +// Used to define number of +// points in each row ( or column ) for +// non-regular grids. +// = 0, if using regular grid. +// gfld->interp_opt = Interpretation of list for optional points +// definition. (Code Table 3.11) +// gfld->igdtnum = Grid Definition Template Number (Code Table 3.1) +// gfld->igdtmpl = Contains the data values for the specified Grid +// Definition Template ( NN=gfld->igdtnum ). Each +// element of this integer array contains an entry (in +// the order specified) of Grid Defintion Template 3.NN +// This element is a pointer to an array +// that holds the data. +// gfld->igdtlen = Number of elements in gfld->igdtmpl[]. i.e. number of +// entries in Grid Defintion Template 3.NN +// ( NN=gfld->igdtnum ). +// gfld->list_opt = (Used if gfld->numoct_opt .ne. 0) This array +// contains the number of grid points contained in +// each row ( or column ). (part of Section 3) +// This element is a pointer to an array +// that holds the data. This pointer is nullified +// if gfld->numoct_opt=0. +// gfld->num_opt = (Used if gfld->numoct_opt .ne. 0) +// The number of entries +// in array ideflist. i.e. number of rows ( or columns ) +// for which optional grid points are defined. This value +// is set to zero, if gfld->numoct_opt=0. +// gfdl->ipdtnum = Product Definition Template Number(see Code Table 4.0) +// gfld->ipdtmpl = Contains the data values for the specified Product +// Definition Template ( N=gfdl->ipdtnum ). Each element +// of this integer array contains an entry (in the +// order specified) of Product Defintion Template 4.N. +// This element is a pointer to an array +// that holds the data. +// gfld->ipdtlen = Number of elements in gfld->ipdtmpl[]. i.e. number of +// entries in Product Defintion Template 4.N +// ( N=gfdl->ipdtnum ). +// gfld->coord_list = Real array containing floating point values +// intended to document the vertical discretisation +// associated to model data on hybrid coordinate +// vertical levels. (part of Section 4) +// This element is a pointer to an array +// that holds the data. +// gfld->num_coord = number of values in array gfld->coord_list[]. +// gfld->ndpts = Number of data points unpacked and returned. +// gfld->idrtnum = Data Representation Template Number +// ( see Code Table 5.0) +// gfld->idrtmpl = Contains the data values for the specified Data +// Representation Template ( N=gfld->idrtnum ). Each +// element of this integer array contains an entry +// (in the order specified) of Product Defintion +// Template 5.N. +// This element is a pointer to an array +// that holds the data. +// gfld->idrtlen = Number of elements in gfld->idrtmpl[]. i.e. number +// of entries in Data Representation Template 5.N +// ( N=gfld->idrtnum ). +// gfld->unpacked = logical value indicating whether the bitmap and +// data values were unpacked. If false, +// gfld->bmap and gfld->fld pointers are nullified. +// gfld->expanded = Logical value indicating whether the data field +// was expanded to the grid in the case where a +// bit-map is present. If true, the data points in +// gfld->fld match the grid points and zeros were +// inserted at grid points where data was bit-mapped +// out. If false, the data values in gfld->fld were +// not expanded to the grid and are just a consecutive +// array of data points corresponding to each value of +// "1" in gfld->bmap. +// gfld->ibmap = Bitmap indicator ( see Code Table 6.0 ) +// 0 = bitmap applies and is included in Section 6. +// 1-253 = Predefined bitmap applies +// 254 = Previously defined bitmap applies to this field +// 255 = Bit map does not apply to this product. +// gfld->bmap = integer array containing decoded bitmap, +// if gfld->ibmap=0 or gfld->ibap=254. Otherwise nullified +// This element is a pointer to an array +// that holds the data. +// gfld->fld = Array of gfld->ndpts unpacked data points. +// This element is a pointer to an array +// that holds the data. +// +// +// RETURN VALUES: +// ierr - Error return code. +// 0 = no error +// 1 = Beginning characters "GRIB" not found. +// 2 = GRIB message is not Edition 2. +// 3 = The data field request number was not positive. +// 4 = End string "7777" found, but not where expected. +// 6 = GRIB message did not contain the requested number of +// data fields. +// 7 = End string "7777" not found at end of message. +// 8 = Unrecognized Section encountered. +// 9 = Data Representation Template 5.NN not yet implemented. +// 15 = Error unpacking Section 1. +// 16 = Error unpacking Section 2. +// 10 = Error unpacking Section 3. +// 11 = Error unpacking Section 4. +// 12 = Error unpacking Section 5. +// 13 = Error unpacking Section 6. +// 14 = Error unpacking Section 7. +// +// REMARKS: Note that struct gribfield is allocated by this routine and it +// also contains pointers to many arrays of data that were allocated +// during decoding. Users are encouraged to free up this memory, +// when it is no longer needed, by an explicit call to routine g2_free. +// EXAMPLE: +// #include "grib2.h" +// gribfield *gfld; +// ret=g2_getfld(cgrib,1,1,1,&gfld); +// ... +// g2_free(gfld); +// +// Routine g2_info can be used to first determine +// how many data fields exist in a given GRIB message. +// +// REMARKS2: It may not always be possible to expand a bit-mapped data field. +// If a pre-defined bit-map is used and not included in the GRIB2 +// message itself, this routine would not have the necessary +// information to expand the data. In this case, gfld->expanded would +// would be set to 0 (false), regardless of the value of input +// argument expand. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +/////////////////////////////////////////////////////////////////////////// + + + +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_create +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-31 +// +// ABSTRACT: This routine initializes a new GRIB2 message and packs +// GRIB2 sections 0 (Indicator Section) and 1 (Identification Section). +// This routine is used with routines "g2_addlocal", "g2_addgrid", +// "g2_addfield", and "g2_gribend" to create a complete GRIB2 message. +// g2_create must be called first to initialize a new GRIB2 message. +// Also, a call to g2_gribend is required to complete GRIB2 message +// after all fields have been added. +// +// PROGRAM HISTORY LOG: +// 2002-10-31 Gilbert +// +// USAGE: int g2_create(unsigned char *cgrib,long *listsec0,long *listsec1) +// INPUT ARGUMENTS: +// cgrib - Character array to contain the GRIB2 message +// listsec0 - Contains information needed for GRIB Indicator Section 0. +// Must be dimensioned >= 2. +// listsec0[0]=Discipline-GRIB Master Table Number +// (see Code Table 0.0) +// listsec0[1]=GRIB Edition Number (currently 2) +// listsec1 - Contains information needed for GRIB Identification Section 1. +// Must be dimensioned >= 13. +// listsec1[0]=Id of orginating centre (Common Code Table C-1) +// listsec1[1]=Id of orginating sub-centre (local table) +// listsec1[2]=GRIB Master Tables Version Number (Code Table 1.0) +// listsec1[3]=GRIB Local Tables Version Number (Code Table 1.1) +// listsec1[4]=Significance of Reference Time (Code Table 1.2) +// listsec1[5]=Reference Time - Year (4 digits) +// listsec1[6]=Reference Time - Month +// listsec1[7]=Reference Time - Day +// listsec1[8]=Reference Time - Hour +// listsec1[9]=Reference Time - Minute +// listsec1[10]=Reference Time - Second +// listsec1[11]=Production status of data (Code Table 1.3) +// listsec1[12]=Type of processed data (Code Table 1.4) +// +// OUTPUT ARGUMENTS: +// cgrib - Char array to contain the new GRIB2 message. +// Must be allocated large enough to store the entire +// GRIB2 message. +// +// RETURN VALUES: +// ierr - return code. +// > 0 = Current size of new GRIB2 message +// -1 = Tried to use for version other than GRIB Edition 2 +// +// REMARKS: This routine is intended for use with routines "g2_addlocal", +// "g2_addgrid", "g2_addfield", and "g2_gribend" to create a complete +// GRIB2 message. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ + + + +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_addlocal +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-11-01 +// +// ABSTRACT: This routine adds a Local Use Section (Section 2) to +// a GRIB2 message. It is used with routines "g2_create", +// "g2_addgrid", "g2_addfield", +// and "g2_gribend" to create a complete GRIB2 message. +// g2_create must be called first to initialize a new GRIB2 message. +// +// PROGRAM HISTORY LOG: +// 2002-11-01 Gilbert +// +// USAGE: int g2_addlocal(unsigned char *cgrib,unsigned char *csec2, +// long lcsec2) +// INPUT ARGUMENTS: +// cgrib - Char array that contains the GRIB2 message to which section +// 2 should be added. +// csec2 - Character array containing information to be added in +// Section 2. +// lcsec2 - Number of bytes of character array csec2 to be added to +// Section 2. +// +// OUTPUT ARGUMENT: +// cgrib - Char array to contain the updated GRIB2 message. +// Must be allocated large enough to store the entire +// GRIB2 message. +// +// RETURN VALUES: +// ierr - Return code. +// > 0 = Current size of updated GRIB2 message +// -1 = GRIB message was not initialized. Need to call +// routine gribcreate first. +// -2 = GRIB message already complete. Cannot add new section. +// -3 = Sum of Section byte counts doesn't add to total byte count +// -4 = Previous Section was not 1 or 7. +// +// REMARKS: Note that the Local Use Section ( Section 2 ) can only follow +// Section 1 or Section 7 in a GRIB2 message. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ + + + +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_addgrid +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-11-01 +// +// ABSTRACT: This routine packs up a Grid Definition Section (Section 3) +// and adds it to a GRIB2 message. It is used with routines "g2_create", +// "g2_addlocal", "g2_addfield", +// and "g2_gribend" to create a complete GRIB2 message. +// g2_create must be called first to initialize a new GRIB2 message. +// +// PROGRAM HISTORY LOG: +// 2002-11-01 Gilbert +// +// USAGE: int g2_addgrid(unsigned char *cgrib,long *igds,g2int *igdstmpl, +// long *ideflist,long idefnum) +// INPUT ARGUMENTS: +// cgrib - Char array that contains the GRIB2 message to which +// section should be added. +// igds - Contains information needed for GRIB Grid Definition Section 3 +// Must be dimensioned >= 5. +// igds[0]=Source of grid definition (see Code Table 3.0) +// igds[1]=Number of grid points in the defined grid. +// igds[2]=Number of octets needed for each +// additional grid points definition. +// Used to define number of +// points in each row ( or column ) for +// non-regular grids. +// = 0, if using regular grid. +// igds[3]=Interpretation of list for optional points +// definition. (Code Table 3.11) +// igds[4]=Grid Definition Template Number (Code Table 3.1) +// igdstmpl - Contains the data values for the specified Grid Definition +// Template ( NN=igds[4] ). Each element of this integer +// array contains an entry (in the order specified) of Grid +// Defintion Template 3.NN +// ideflist - (Used if igds[2] != 0) This array contains the +// number of grid points contained in each row ( or column ) +// idefnum - (Used if igds[2] != 0) The number of entries +// in array ideflist. i.e. number of rows ( or columns ) +// for which optional grid points are defined. +// +// OUTPUT ARGUMENTS: +// cgrib - Char array to contain the updated GRIB2 message. +// Must be allocated large enough to store the entire +// GRIB2 message. +// +// RETURN VALUES: +// ierr - Return code. +// > 0 = Current size of updated GRIB2 message +// -1 = GRIB message was not initialized. Need to call +// routine gribcreate first. +// -2 = GRIB message already complete. Cannot add new section. +// -3 = Sum of Section byte counts doesn't add to total byte count +// -4 = Previous Section was not 1, 2 or 7. +// -5 = Could not find requested Grid Definition Template. +// +// REMARKS: Note that the Grid Def Section ( Section 3 ) can only follow +// Section 1, 2 or Section 7 in a GRIB2 message. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ + + + +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_addfield +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-11-05 +// +// ABSTRACT: This routine packs up Sections 4 through 7 for a given field +// and adds them to a GRIB2 message. They are Product Definition Section, +// Data Representation Section, Bit-Map Section and Data Section, +// respectively. +// This routine is used with routines "g2_create", "g2_addlocal", +// "g2_addgrid", and "g2_gribend" to create a complete GRIB2 message. +// g2_create must be called first to initialize a new GRIB2 message. +// Also, routine g2_addgrid must be called after g2_create and +// before this routine to add the appropriate grid description to +// the GRIB2 message. Also, a call to g2_gribend is required to complete +// GRIB2 message after all fields have been added. +// +// PROGRAM HISTORY LOG: +// 2002-11-05 Gilbert +// +// USAGE: int g2_addfield(unsigned char *cgrib,g2int ipdsnum,g2int *ipdstmpl, +// g2float *coordlist,g2int numcoord,g2int idrsnum,g2int *idrstmpl, +// g2float *fld,g2int ngrdpts,g2int ibmap,g2int *bmap) +// INPUT ARGUMENT LIST: +// cgrib - Char array that contains the GRIB2 message to which sections +// 4 through 7 should be added. +// ipdsnum - Product Definition Template Number ( see Code Table 4.0) +// ipdstmpl - Contains the data values for the specified Product Definition +// Template ( N=ipdsnum ). Each element of this integer +// array contains an entry (in the order specified) of Product +// Defintion Template 4.N +// coordlist- Array containg floating point values intended to document +// the vertical discretisation associated to model data +// on hybrid coordinate vertical levels. +// numcoord - number of values in array coordlist. +// idrsnum - Data Representation Template Number ( see Code Table 5.0 ) +// idrstmpl - Contains the data values for the specified Data Representation +// Template ( N=idrsnum ). Each element of this integer +// array contains an entry (in the order specified) of Data +// Representation Template 5.N +// Note that some values in this template (eg. reference +// values, number of bits, etc...) may be changed by the +// data packing algorithms. +// Use this to specify scaling factors and order of +// spatial differencing, if desired. +// fld[] - Array of data points to pack. +// ngrdpts - Number of data points in grid. +// i.e. size of fld and bmap. +// ibmap - Bitmap indicator ( see Code Table 6.0 ) +// 0 = bitmap applies and is included in Section 6. +// 1-253 = Predefined bitmap applies +// 254 = Previously defined bitmap applies to this field +// 255 = Bit map does not apply to this product. +// bmap[] - Integer array containing bitmap to be added. ( if ibmap=0 ) +// +// OUTPUT ARGUMENT LIST: +// cgrib - Character array to contain the updated GRIB2 message. +// Must be allocated large enough to store the entire +// GRIB2 message. +// +// RETURN VALUES: +// ierr - Return code. +// > 0 = Current size of updated GRIB2 message +// -1 = GRIB message was not initialized. Need to call +// routine gribcreate first. +// -2 = GRIB message already complete. Cannot add new section. +// -3 = Sum of Section byte counts doesn't add to total byte count +// -4 = Previous Section was not 3 or 7. +// -5 = Could not find requested Product Definition Template. +// -6 = Section 3 (GDS) not previously defined in message +// -7 = Tried to use unsupported Data Representationi Template +// -8 = Specified use of a previously defined bitmap, but one +// does not exist in the GRIB message. +// +// REMARKS: Note that the Sections 4 through 7 can only follow +// Section 3 or Section 7 in a GRIB2 message. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ + + + +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: g2_gribend +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-31 +// +// ABSTRACT: This routine finalizes a GRIB2 message after all grids +// and fields have been added. It adds the End Section ( "7777" ) +// to the end of the GRIB message and calculates the length and stores +// it in the appropriate place in Section 0. +// This routine is used with routines "g2_create", "g2_addlocal", +// "g2_addgrid", and "g2_addfield" to create a complete GRIB2 message. +// g2_create must be called first to initialize a new GRIB2 message. +// +// PROGRAM HISTORY LOG: +// 2002-10-31 Gilbert +// +// USAGE: int g2_gribend(unsigned char *cgrib) +// INPUT ARGUMENT: +// cgrib - Char array containing all the data sections added +// be previous calls to g2_create, g2_addlocal, g2_addgrid, +// and g2_addfield. +// +// OUTPUT ARGUMENTS: +// cgrib - Char array containing the finalized GRIB2 message +// +// RETURN VALUES: +// ierr - Return code. +// > 0 = Length of the final GRIB2 message in bytes. +// -1 = GRIB message was not initialized. Need to call +// routine g2_create first. +// -2 = GRIB message already complete. +// -3 = Sum of Section byte counts doesn't add to total byte count +// -4 = Previous Section was not 7. +// +// REMARKS: This routine is intended for use with routines "g2_create", +// "g2_addlocal", "g2_addgrid", and "g2_addfield" to create a complete +// GRIB2 message. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/gridtemplates.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/gridtemplates.c new file mode 100644 index 000000000..9bf42615f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/gridtemplates.c @@ -0,0 +1,233 @@ +#include +#include "grib2.h" +#include "gridtemplates.h" + + +static const struct gridtemplate templatesgrid[MAXGRIDTEMP] = { + // 3.0: Lat/Lon grid + { 0, 19, 0, {1,1,4,1,4,1,4,4,4,4,4,-4,4,1,-4,4,4,4,1} }, + // 3.1: Rotated Lat/Lon grid + { 1, 22, 0, {1,1,4,1,4,1,4,4,4,4,4,-4,4,1,-4,4,4,4,1,-4,4,4} }, + // 3.2: Stretched Lat/Lon grid + { 2, 22, 0, {1,1,4,1,4,1,4,4,4,4,4,-4,4,1,-4,4,4,4,1,-4,4,-4} }, + // 3.3: Stretched & Rotated Lat/Lon grid + { 3, 25, 0, {1,1,4,1,4,1,4,4,4,4,4,-4,4,1,-4,4,4,4,1,-4,4,4,-4,4,-4} }, + // 3.10: Mercator +// {10, 19, 0, {1,1,4,1,4,1,4,4,4,-4,4,1,-4,-4,4,1,4,4,4} }, + {10, 19, 0, {1,1,4,1,4,1,4,4,4,-4,-4,1,-4,-4,-4,1,4,4,4} }, + // 3.20: Polar Stereographic Projection +// {20, 18, 0, {1,1,4,1,4,1,4,4,4,-4,4,1,-4,4,4,4,1,1} }, + {20, 18, 0, {1,1,4,1,4,1,4,4,4,-4,-4,1,-4,-4,4,4,1,1} }, + // 3.30: Lambert Conformal +// {30, 22, 0, {1,1,4,1,4,1,4,4,4,-4,4,1,-4,4,4,4,1,1,-4,-4,-4,4} }, + {30, 22, 0, {1,1,4,1,4,1,4,4,4,-4,-4,1,-4,-4,4,4,1,1,-4,-4,-4,-4} }, + // 3.31: Albers equal area + {31, 22, 0, {1,1,4,1,4,1,4,4,4,-4,4,1,-4,4,4,4,1,1,-4,-4,-4,4} }, + // 3.40: Guassian Lat/Lon + {40, 19, 0, {1,1,4,1,4,1,4,4,4,4,4,-4,4,1,-4,4,4,4,1} }, + // 3.41: Rotated Gaussian Lat/Lon + {41, 22, 0, {1,1,4,1,4,1,4,4,4,4,4,-4,4,1,-4,4,4,4,1,-4,4,4} }, + // 3.42: Stretched Gaussian Lat/Lon + {42, 22, 0, {1,1,4,1,4,1,4,4,4,4,4,-4,4,1,-4,4,4,4,1,-4,4,-4} }, + // 3.43: Stretched and Rotated Gaussian Lat/Lon + {43, 25, 0, {1,1,4,1,4,1,4,4,4,4,4,-4,4,1,-4,4,4,4,1,-4,4,4,-4,4,-4} }, + // 3.50: Spherical Harmonic Coefficients + {50, 5, 0, {4,4,4,1,1} }, + // 3.51: Rotated Spherical Harmonic Coefficients + {51, 8, 0, {4,4,4,1,1,-4,4,4} }, + // 3.52: Stretched Spherical Harmonic Coefficients + {52, 8, 0, {4,4,4,1,1,-4,4,-4} }, + // 3.53: Stretched and Rotated Spherical Harmonic Coefficients + {53, 11, 0, {4,4,4,1,1,-4,4,4,-4,4,-4} }, + // 3.90: Space View Perspective or orthographic + {90, 21, 0, {1,1,4,1,4,1,4,4,4,-4,4,1,4,4,4,4,1,4,4,4,4} }, + // 3.100: Triangular grid based on an icosahedron + {100, 11, 0, {1,1,2,1,-4,4,4,1,1,1,4} }, + // 3.110: Equatorial Azimuthal equidistant + {110, 16, 0, {1,1,4,1,4,1,4,4,4,-4,4,1,4,4,1,1} }, + // 3.120: Azimuth-range projection + {120, 7, 1, {4,4,-4,4,4,4,1} }, + // 3.1000: Cross Section Grid + {1000, 20, 1, {1,1,4,1,4,1,4,4,4,4,-4,4,1,4,4,1,2,1,1,2} }, + // 3.1100: Hovmoller Diagram Grid + {1100, 28, 0, {1,1,4,1,4,1,4,4,4,4,-4,4,1,-4,4,1,4,1,-4,1,1,-4,2,1,1,1,1,1} }, + // 3.1200: Time Section Grid + {1200, 16, 1, {4,1,-4,1,1,-4,2,1,1,1,1,1,2,1,1,2} } + + } ; + +const struct gridtemplate *get_templatesgrid() + +{ + return templatesgrid; +} + +g2int getgridindex(g2int number) +/*!$$$ SUBPROGRAM DOCUMENTATION BLOCK +! . . . . +! SUBPROGRAM: getgridindex +! PRGMMR: Gilbert ORG: W/NP11 DATE: 2001-06-28 +! +! ABSTRACT: This function returns the index of specified Grid +! Definition Template 3.NN (NN=number) in array templates. +! +! PROGRAM HISTORY LOG: +! 2001-06-28 Gilbert +! +! USAGE: index=getgridindex(number) +! INPUT ARGUMENT LIST: +! number - NN, indicating the number of the Grid Definition +! Template 3.NN that is being requested. +! +! RETURNS: Index of GDT 3.NN in array templates, if template exists. +! = -1, otherwise. +! +! REMARKS: None +! +! ATTRIBUTES: +! LANGUAGE: C +! MACHINE: IBM SP +! +!$$$*/ +{ + g2int j,getgridindex=-1; + + for (j=0;jtype=3; + new->num=templatesgrid[index].template_num; + new->maplen=templatesgrid[index].mapgridlen; + new->needext=templatesgrid[index].needext; + new->map=(g2int *)templatesgrid[index].mapgrid; + new->extlen=0; + new->ext=0; //NULL + return(new); + } + else { + printf("getgridtemplate: GDT Template 3.%d not defined.\n",(int)number); + return(0); //NULL + } + + return(0); //NULL +} + + +xxtemplate *extgridtemplate(g2int number,g2int *list) +/*!$$$ SUBPROGRAM DOCUMENTATION BLOCK +! . . . . +! SUBPROGRAM: extgridtemplate +! PRGMMR: Gilbert ORG: W/NP11 DATE: 2000-05-09 +! +! ABSTRACT: This subroutine generates the remaining octet map for a +! given Grid Definition Template, if required. Some Templates can +! vary depending on data values given in an earlier part of the +! Template, and it is necessary to know some of the earlier entry +! values to generate the full octet map of the Template. +! +! PROGRAM HISTORY LOG: +! 2000-05-09 Gilbert +! +! USAGE: CALL extgridtemplate(number,list) +! INPUT ARGUMENT LIST: +! number - NN, indicating the number of the Grid Definition +! Template 3.NN that is being requested. +! list() - The list of values for each entry in +! the Grid Definition Template. +! +! RETURN VALUE: +! - Pointer to the returned template struct. +! Returns NULL pointer, if template not found. +! +! ATTRIBUTES: +! LANGUAGE: C +! MACHINE: IBM SP +! +!$$$*/ +{ + xxtemplate *new; + g2int index,i; + + index=getgridindex(number); + if (index == -1) return(0); + + new=getgridtemplate(number); + + if ( ! new->needext ) return(new); + + if ( number == 120 ) { + new->extlen=list[1]*2; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + for (i=0;iextlen;i++) { + if ( i%2 == 0 ) { + new->ext[i]=2; + } + else { + new->ext[i]=-2; + } + } + } + else if ( number == 1000 ) { + new->extlen=list[19]; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + for (i=0;iextlen;i++) { + new->ext[i]=4; + } + } + else if ( number == 1200 ) { + new->extlen=list[15]; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + for (i=0;iextlen;i++) { + new->ext[i]=4; + } + } + + return(new); + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/gridtemplates.h b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/gridtemplates.h new file mode 100644 index 000000000..8cd8cac5a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/gridtemplates.h @@ -0,0 +1,49 @@ +#ifndef _gridtemplates_H +#define _gridtemplates_H +#include "grib2.h" + +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2001-10-26 +// +// ABSTRACT: This Fortran Module contains info on all the available +// GRIB2 Grid Definition Templates used in Section 3 (GDS). +// The information decribing each template is stored in the +// gridtemplate structure defined below. +// +// Each Template has three parts: The number of entries in the template +// (mapgridlen); A map of the template (mapgrid), which contains the +// number of octets in which to pack each of the template values; and +// a logical value (needext) that indicates whether the Template needs +// to be extended. In some cases the number of entries in a template +// can vary depending upon values specified in the "static" part of +// the template. ( See Template 3.120 as an example ) +// +// NOTE: Array mapgrid contains the number of octets in which the +// corresponding template values will be stored. A negative value in +// mapgrid is used to indicate that the corresponding template entry can +// contain negative values. This information is used later when packing +// (or unpacking) the template data values. Negative data values in GRIB +// are stored with the left most bit set to one, and a negative number +// of octets value in mapgrid[] indicates that this possibility should +// be considered. The number of octets used to store the data value +// in this case would be the absolute value of the negative value in +// mapgrid[]. +// +// +//////////////////////////////////////////////////////////////////// + + #define MAXGRIDTEMP 23 // maximum number of templates + #define MAXGRIDMAPLEN 200 // maximum template map length + + struct gridtemplate + { + g2int template_num; + g2int mapgridlen; + g2int needext; + g2int mapgrid[MAXGRIDMAPLEN]; + }; + + +const struct gridtemplate *get_templatesgrid(); +g2int getgridindex(g2int number); + +#endif /* _gridtemplates_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/int_power.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/int_power.c new file mode 100644 index 000000000..76f645abd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/int_power.c @@ -0,0 +1,30 @@ +#include "grib2.h" +/* + * w. ebisuzaki + * + * return x**y + * + * + * input: double x + * int y + */ +double int_power(double x, g2int y) { + + double value; + + if (y < 0) { + y = -y; + x = 1.0 / x; + } + value = 1.0; + + while (y) { + if (y & 1) { + value *= x; + } + x = x * x; + y >>= 1; + } + return value; +} + diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/jpcpack.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/jpcpack.c new file mode 100644 index 000000000..272358d9c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/jpcpack.c @@ -0,0 +1,175 @@ +#include +#include +#include "grib2.h" + +int enc_jpeg2000(unsigned char *,g2int ,g2int ,g2int , + g2int , g2int, g2int , char *, g2int ); + +void jpcpack(g2float *fld,g2int width,g2int height,g2int *idrstmpl, + unsigned char *cpack,g2int *lcpack) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: jpcpack +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2003-08-17 +// +// ABSTRACT: This subroutine packs up a data field into a JPEG2000 code stream. +// After the data field is scaled, and the reference value is subtracted out, +// it is treated as a grayscale image and passed to a JPEG2000 encoder. +// It also fills in GRIB2 Data Representation Template 5.40 or 5.40000 with +// the appropriate values. +// +// PROGRAM HISTORY LOG: +// 2003-08-17 Gilbert +// 2004-11-92 Gilbert - Fixed bug encountered when packing a near constant +// field. +// 2004-07-19 Gilbert - Added check on whether the jpeg2000 encoding was +// successful. If not, try again with different encoder +// options. +// 2005-05-10 Gilbert - Imposed minimum size on cpack, used to hold encoded +// bit string. +// +// USAGE: jpcpack(g2float *fld,g2int width,g2int height,g2int *idrstmpl, +// unsigned char *cpack,g2int *lcpack); +// INPUT ARGUMENT LIST: +// fld[] - Contains the data values to pack +// width - number of points in the x direction +// height - number of points in the y direction +// idrstmpl - Contains the array of values for Data Representation +// Template 5.40 or 5.40000 +// [0] = Reference value - ignored on input +// [1] = Binary Scale Factor +// [2] = Decimal Scale Factor +// [3] = number of bits for each data value - ignored on input +// [4] = Original field type - currently ignored on input +// Data values assumed to be reals. +// [5] = 0 - use lossless compression +// = 1 - use lossy compression +// [6] = Desired compression ratio, if idrstmpl[5]=1. +// Set to 255, if idrstmpl[5]=0. +// lcpack - size of array cpack[] +// +// OUTPUT ARGUMENT LIST: +// idrstmpl - Contains the array of values for Data Representation +// Template 5.0 +// [0] = Reference value - set by jpcpack routine. +// [1] = Binary Scale Factor - unchanged from input +// [2] = Decimal Scale Factor - unchanged from input +// [3] = Number of bits containing each grayscale pixel value +// [4] = Original field type - currently set = 0 on output. +// Data values assumed to be reals. +// [5] = 0 - use lossless compression +// = 1 - use lossy compression +// [6] = Desired compression ratio, if idrstmpl[5]=1 +// cpack - The packed data field +// lcpack - length of packed field in cpack. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: IBM SP +// +//$$$ +{ + g2int *ifld; + static g2float alog2=0.69314718; // ln(2.0) + g2int j,nbits,imin,imax,maxdif; + g2int ndpts,nbytes,nsize,retry; + g2float bscale,dscale,rmax,rmin,temp; + unsigned char *ctemp; + + ifld=0; + ndpts=width*height; + bscale=int_power(2.0,-idrstmpl[1]); + dscale=int_power(10.0,idrstmpl[2]); +// +// Find max and min values in the data +// + rmax=fld[0]; + rmin=fld[0]; + for (j=1;j rmax) rmax=fld[j]; + if (fld[j] < rmin) rmin=fld[j]; + } + if (idrstmpl[1] == 0) + maxdif = (g2int) (RINT(rmax*dscale) - RINT(rmin*dscale)); + else + maxdif = (g2int)RINT( (rmax-rmin)*dscale*bscale ); +// +// If max and min values are not equal, pack up field. +// If they are equal, we have a constant field, and the reference +// value (rmin) is the value for each point in the field and +// set nbits to 0. +// + if ( rmin != rmax && maxdif != 0 ) { + ifld=(g2int *)malloc(ndpts*sizeof(g2int)); + // + // Determine which algorithm to use based on user-supplied + // binary scale factor and number of bits. + // + if (idrstmpl[1] == 0) { + // + // No binary scaling and calculate minumum number of + // bits in which the data will fit. + // + imin=(g2int)RINT(rmin*dscale); + imax=(g2int)RINT(rmax*dscale); + maxdif=imax-imin; + temp=log((double)(maxdif+1))/alog2; + nbits=(g2int)ceil(temp); + rmin=(g2float)imin; + // scale data + for(j=0;j +#include +#include "grib2.h" + + int dec_jpeg2000(char *,g2int ,g2int *); + +g2int jpcunpack(unsigned char *cpack,g2int len,g2int *idrstmpl,g2int ndpts, + g2float *fld) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: jpcunpack +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2003-08-27 +// +// ABSTRACT: This subroutine unpacks a data field that was packed into a +// JPEG2000 code stream +// using info from the GRIB2 Data Representation Template 5.40 or 5.40000. +// +// PROGRAM HISTORY LOG: +// 2003-08-27 Gilbert +// +// USAGE: jpcunpack(unsigned char *cpack,g2int len,g2int *idrstmpl,g2int ndpts, +// g2float *fld) +// INPUT ARGUMENT LIST: +// cpack - The packed data field (character*1 array) +// len - length of packed field cpack(). +// idrstmpl - Pointer to array of values for Data Representation +// Template 5.40 or 5.40000 +// ndpts - The number of data values to unpack +// +// OUTPUT ARGUMENT LIST: +// fld[] - Contains the unpacked data values +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: IBM SP +// +//$$$ +{ + + g2int *ifld; + g2int j,nbits /* ,iret */; + g2float ref,bscale,dscale; + + rdieee(idrstmpl+0,&ref,1); + bscale = int_power(2.0,idrstmpl[1]); + dscale = int_power(10.0,-idrstmpl[2]); + nbits = idrstmpl[3]; +// +// if nbits equals 0, we have a constant field where the reference value +// is the data value at each gridpoint +// + if (nbits != 0) { + + ifld=(g2int *)calloc(ndpts,sizeof(g2int)); + if ( ifld == 0 ) { + fprintf(stderr,"Could not allocate space in jpcunpack.\n Data field NOT upacked.\n"); + return(1); + } + /* iret= (g2int) */ dec_jpeg2000((char *) cpack,len,ifld); + for (j=0;j +#include +#include "grib2.h" + +void misspack(g2float *fld,g2int ndpts,g2int idrsnum,g2int *idrstmpl, + unsigned char *cpack, g2int *lcpack) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: misspack +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2000-06-21 +// +// ABSTRACT: This subroutine packs up a data field using a complex +// packing algorithm as defined in the GRIB2 documention. It +// supports GRIB2 complex packing templates with or without +// spatial differences (i.e. DRTs 5.2 and 5.3). +// It also fills in GRIB2 Data Representation Template 5.2 or 5.3 +// with the appropriate values. +// This version assumes that Missing Value Management is being used and that +// 1 or 2 missing values appear in the data. +// +// PROGRAM HISTORY LOG: +// 2000-06-21 Gilbert +// +// USAGE: misspack(g2float *fld,g2int ndpts,g2int idrsnum,g2int *idrstmpl, +// unsigned char *cpack, g2int *lcpack) +// INPUT ARGUMENT LIST: +// fld[] - Contains the data values to pack +// ndpts - The number of data values in array fld[] +// idrsnum - Data Representation Template number 5.N +// Must equal 2 or 3. +// idrstmpl - Contains the array of values for Data Representation +// Template 5.2 or 5.3 +// [0] = Reference value - ignored on input +// [1] = Binary Scale Factor +// [2] = Decimal Scale Factor +// . +// . +// [6] = Missing value management +// [7] = Primary missing value +// [8] = Secondary missing value +// . +// . +// [16] = Order of Spatial Differencing ( 1 or 2 ) +// . +// . +// +// OUTPUT ARGUMENT LIST: +// idrstmpl - Contains the array of values for Data Representation +// Template 5.3 +// [0] = Reference value - set by misspack routine. +// [1] = Binary Scale Factor - unchanged from input +// [2] = Decimal Scale Factor - unchanged from input +// . +// . +// cpack - The packed data field (character*1 array) +// *lcpack - length of packed field cpack(). +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + + g2int *ifld, *ifldmiss, *jfld; + g2int *jmin, *jmax, *lbit; + static g2int zero=0; + g2int *gref, *gwidth, *glen; + g2int glength, grpwidth; + g2int i, n, iofst, imin, ival1, ival2, isd, minsd, nbitsd = 0; + g2int nbitsgref, left, iwmax, ngwidthref, nbitsgwidth, ilmax; + g2int nglenref, nglenlast, nbitsglen /* , ij */; + g2int j, missopt, nonmiss, itemp, maxorig, nbitorig, miss1, miss2; + g2int ngroups, ng, num0, num1, num2; + g2int imax, lg, mtemp, ier, igmax; + g2int kfildo, minpk, inc, maxgrps, ibit, jbit, kbit, novref, lbitref; + g2float rmissp, rmisss, bscale, dscale, rmin, temp; + static g2int simple_alg = 0; + static g2float alog2=0.69314718; // ln(2.0) + static g2int one=1; + + bscale=int_power(2.0,-idrstmpl[1]); + dscale=int_power(10.0,idrstmpl[2]); + missopt=idrstmpl[6]; + if ( missopt != 1 && missopt != 2 ) { + printf("misspack: Unrecognized option.\n"); + *lcpack=-1; + return; + } + else { // Get missing values + rdieee(idrstmpl+7,&rmissp,1); + if (missopt == 2) rdieee(idrstmpl+8,&rmisss,1); + } +// +// Find min value of non-missing values in the data, +// AND set up missing value mapping of the field. +// + ifldmiss = calloc(ndpts,sizeof(g2int)); + rmin=1E+37; + if ( missopt == 1 ) { // Primary missing value only + for ( j=0; j0; j--) + jfld[j]=jfld[j]-jfld[j-1]; + jfld[0]=0; + } + else if (idrstmpl[16] == 2) { // second order + ival1=jfld[0]; + ival2=jfld[1]; + for ( j=nonmiss-1; j>1; j--) + jfld[j]=jfld[j]-(2*jfld[j-1])+jfld[j-2]; + jfld[0]=0; + jfld[1]=0; + } + // + // subtract min value from spatial diff field + // + isd=idrstmpl[16]; + minsd=jfld[isd]; + for ( j=isd; jival1) maxorig=ival2; + temp=log((double)(maxorig+1))/alog2; + nbitorig=(g2int)ceil(temp)+1; + if (nbitorig > nbitsd) nbitsd=nbitorig; + // increase number of bits to even multiple of 8 ( octet ) + if ( (nbitsd%8) != 0) nbitsd=nbitsd+(8-(nbitsd%8)); + // + // Store extra spatial differencing info into the packed + // data section. + // + if (nbitsd != 0) { + // pack first original value + if (ival1 >= 0) { + sbit(cpack,&ival1,iofst,nbitsd); + iofst=iofst+nbitsd; + } + else { + sbit(cpack,&one,iofst,1); + iofst=iofst+1; + itemp=abs(ival1); + sbit(cpack,&itemp,iofst,nbitsd-1); + iofst=iofst+nbitsd-1; + } + if (idrstmpl[16] == 2) { + // pack second original value + if (ival2 >= 0) { + sbit(cpack,&ival2,iofst,nbitsd); + iofst=iofst+nbitsd; + } + else { + sbit(cpack,&one,iofst,1); + iofst=iofst+1; + itemp=abs(ival2); + sbit(cpack,&itemp,iofst,nbitsd-1); + iofst=iofst+nbitsd-1; + } + } + // pack overall min of spatial differences + if (minsd >= 0) { + sbit(cpack,&minsd,iofst,nbitsd); + iofst=iofst+nbitsd; + } + else { + sbit(cpack,&one,iofst,1); + iofst=iofst+1; + itemp=abs(minsd); + sbit(cpack,&itemp,iofst,nbitsd-1); + iofst=iofst+nbitsd-1; + } + } + //print *,'SDp ',ival1,ival2,minsd,nbitsd + } // end of spatial diff section + // + // Expand non-missing data values to original grid. + // + miss1=jfld[0]; + for ( j=0; j imax) imax=ifld[j]; + } + j++; + } + if (missopt == 1) imax=imax+1; + if (missopt == 2) imax=imax+2; + // calc num of bits needed to hold data + if ( gref[ng] != imax ) { + temp=log((double)(imax-gref[ng]+1))/alog2; + gwidth[ng]=(g2int)ceil(temp); + } + else { + gwidth[ng]=0; + } + } + // Subtract min from data + j=n; + mtemp=(g2int)int_power(2.,gwidth[ng]); + for ( lg=0; lg igmax) igmax=gref[j]; + if (missopt == 1) igmax=igmax+1; + if (missopt == 2) igmax=igmax+2; + if (igmax != 0) { + temp=log((double)(igmax+1))/alog2; + nbitsgref=(g2int)ceil(temp); + // reset the ref values of any "missing only" groups. + mtemp=(g2int)int_power(2.,nbitsgref); + for ( j=0; j iwmax) iwmax=gwidth[j]; + if (gwidth[j] < ngwidthref) ngwidthref=gwidth[j]; + } + if (iwmax != ngwidthref) { + temp=log((double)(iwmax-ngwidthref+1))/alog2; + nbitsgwidth=(g2int)ceil(temp); + for ( i=0; i ilmax) ilmax=glen[j]; + if (glen[j] < nglenref) nglenref=glen[j]; + } + nglenlast=glen[ngroups-1]; + if (ilmax != nglenref) { + temp=log((double)(ilmax-nglenref+1))/alog2; + nbitsglen=(g2int)ceil(temp); + for ( i=0; i +#include +#include "grib2.h" + + +void mkieee(g2float *a,g2int *rieee,g2int num) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: mkieee +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-29 +// +// ABSTRACT: This subroutine stores a list of real values in +// 32-bit IEEE floating point format. +// +// PROGRAM HISTORY LOG: +// 2002-10-29 Gilbert +// +// USAGE: mkieee(g2float *a,g2int *rieee,g2int num); +// INPUT ARGUMENT LIST: +// a - Input array of floating point values. +// num - Number of floating point values to convert. +// +// OUTPUT ARGUMENT LIST: +// rieee - Output array of data values in 32-bit IEEE format +// stored in g2int integer array. rieee must be allocated +// with at least 4*num bytes of memory before calling this +// function. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + + g2int j,n,ieee,iexp,imant; + double /* alog2, */ atemp; + + static double two23,two126; + static g2int test=0; + //g2intu msk1=0x80000000; // 10000000000000000000000000000000 binary + //g2int msk2=0x7F800000; // 01111111100000000000000000000000 binary + //g2int msk3=0x007FFFFF; // 00000000011111111111111111111111 binary + + if ( test == 0 ) { + two23=(double)int_power(2.0,23); + two126=(double)int_power(2.0,126); + test=1; + } + + // alog2=0.69314718; // ln(2.0) + + for (j=0;j= 1.0 ) { + n = 0; + while ( int_power(2.0,n+1) <= atemp ) { + n++; + } + } + else { + n = -1; + while ( int_power(2.0,n) > atemp ) { + n--; + } + } + //n=(g2int)floor(log(atemp)/alog2); + iexp=n+127; + if (n > 127) iexp=255; // overflow + if (n < -127) iexp=0; + //printf("exp %ld %ld \n",iexp,n); + // set exponent bits ( bits 30-23 ) + ieee = ieee | ( iexp << 23 ); +// +// Determine Mantissa +// + if (iexp != 255) { + if (iexp != 0) + atemp=(atemp/int_power(2.0,n))-1.0; + else + atemp=atemp*two126; + imant=(g2int)RINT(atemp*two23); + } + else { + imant=0; + } + //printf("mant %ld %x \n",imant,imant); + // set mantissa bits ( bits 22-0 ) + ieee = ieee | imant; +// +// Transfer IEEE bit string to rieee array +// + rieee[j]=ieee; + + } + + return; + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/pack_gp.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/pack_gp.c new file mode 100644 index 000000000..2eaa00ee5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/pack_gp.c @@ -0,0 +1,1447 @@ +/* pack_gp.f -- translated by f2c (version 20031025). + You must link the resulting object file with libf2c: + on Microsoft Windows system, link with libf2c.lib; + on Linux or Unix systems, link with .../path/to/libf2c.a -lm + or, if you install libf2c.a in a standard place, with -lf2c -lm + -- in that order, at the end of the command line, as in + cc *.o -lf2c -lm + Source for libf2c is in /netlib/f2c/libf2c.zip, e.g., + + http://www.netlib.org/f2c/libf2c.zip +*/ + +/*#include "f2c.h"*/ +#include +#include "grib2.h" +typedef g2int integer; +typedef g2int logical; +#define TRUE_ (1) +#define FALSE_ (0) + +/* Subroutine */ int pack_gp(integer *kfildo, integer *ic, integer *nxy, + integer *is523, integer *minpk, integer *inc, integer *missp, integer + *misss, integer *jmin, integer *jmax, integer *lbit, integer *nov, + integer *ndg, integer *lx, integer *ibit, integer *jbit, integer * + kbit, integer *novref, integer *lbitref, integer *ier) +{ + /* Initialized data */ + + const integer mallow = 1073741825; /* MALLOW=2**30+1 */ + static integer ifeed = 12; + static integer ifirst = 0; + + /* System generated locals */ + integer i__1, i__2, i__3; + + /* Local variables */ + static integer j, k, l; + static logical adda; + static integer ired, kinc, mina, maxa, minb, maxb, minc, maxc, ibxx2[31]; + static char cfeed[1]; + static integer nenda, nendb, ibita, ibitb, minak, minbk, maxak, maxbk, + minck, maxck, nouta, lmiss, itest, nount; + extern /* Subroutine */ int reduce(integer *, integer *, integer *, + integer *, integer *, integer *, integer *, integer *, integer *, + integer *, integer *, integer *, integer *); + static integer ibitbs, mislla, misllb, misllc, iersav, lminpk, ktotal, + kounta, kountb, kstart, mstart, mintst, maxtst, + kounts, mintstk, maxtstk; + integer *misslx; + + +/* FEBRUARY 1994 GLAHN TDL MOS-2000 */ +/* JUNE 1995 GLAHN MODIFIED FOR LMISS ERROR. */ +/* JULY 1996 GLAHN ADDED MISSS */ +/* FEBRUARY 1997 GLAHN REMOVED 4 REDUNDANT TESTS FOR */ +/* MISSP.EQ.0; INSERTED A TEST TO BETTER */ +/* HANDLE A STRING OF 9999'S */ +/* FEBRUARY 1997 GLAHN ADDED LOOPS TO ELIMINATE TEST FOR */ +/* MISSS WHEN MISSS = 0 */ +/* MARCH 1997 GLAHN CORRECTED FOR SECONDARY MISSING VALUE */ +/* MARCH 1997 GLAHN CORRECTED FOR USE OF LOCAL VALUE */ +/* OF MINPK */ +/* MARCH 1997 GLAHN CORRECTED FOR SECONDARY MISSING VALUE */ +/* MARCH 1997 GLAHN CHANGED CALCULATING NUMBER OF BITS */ +/* THROUGH EXPONENTS TO AN ARRAY (IMPROVED */ +/* OVERALL PACKING PERFORMANCE BY ABOUT */ +/* 35 PERCENT!). ALLOWED 0 BITS FOR */ +/* PACKING JMIN( ), LBIT( ), AND NOV( ). */ +/* MAY 1997 GLAHN A NUMBER OF CHANGES FOR EFFICIENCY. */ +/* MOD FUNCTIONS ELIMINATED AND ONE */ +/* IFTHEN ADDED. JOUNT REMOVED. */ +/* RECOMPUTATION OF BITS NOT MADE UNLESS */ +/* NECESSARY AFTER MOVING POINTS FROM */ +/* ONE GROUP TO ANOTHER. NENDB ADJUSTED */ +/* TO ELIMINATE POSSIBILITY OF VERY */ +/* SMALL GROUP AT THE END. */ +/* ABOUT 8 PERCENT IMPROVEMENT IN */ +/* OVERALL PACKING. ISKIPA REMOVED; */ +/* THERE IS ALWAYS A GROUP B THAT CAN */ +/* BECOME GROUP A. CONTROL ON SIZE */ +/* OF GROUP B (STATEMENT BELOW 150) */ +/* ADDED. ADDED ADDA, AND USE */ +/* OF GE AND LE INSTEAD OF GT AND LT */ +/* IN LOOPS BETWEEN 150 AND 160. */ +/* IBITBS ADDED TO SHORTEN TRIPS */ +/* THROUGH LOOP. */ +/* MARCH 2000 GLAHN MODIFIED FOR GRIB2; CHANGED NAME FROM */ +/* PACKGP */ +/* JANUARY 2001 GLAHN COMMENTS; IER = 706 SUBSTITUTED FOR */ +/* STOPS; ADDED RETURN1; REMOVED STATEMENT */ +/* NUMBER 110; ADDED IER AND * RETURN */ +/* NOVEMBER 2001 GLAHN CHANGED SOME DIAGNOSTIC FORMATS TO */ +/* ALLOW PRINTING LARGER NUMBERS */ +/* NOVEMBER 2001 GLAHN ADDED MISSLX( ) TO PUT MAXIMUM VALUE */ +/* INTO JMIN( ) WHEN ALL VALUES MISSING */ +/* TO AGREE WITH GRIB STANDARD. */ +/* NOVEMBER 2001 GLAHN CHANGED TWO TESTS ON MISSP AND MISSS */ +/* EQ 0 TO TESTS ON IS523. HOWEVER, */ +/* MISSP AND MISSS CANNOT IN GENERAL BE */ +/* = 0. */ +/* NOVEMBER 2001 GLAHN ADDED CALL TO REDUCE; DEFINED ITEST */ +/* BEFORE LOOPS TO REDUCE COMPUTATION; */ +/* STARTED LARGE GROUP WHEN ALL SAME */ +/* VALUE */ +/* DECEMBER 2001 GLAHN MODIFIED AND ADDED A FEW COMMENTS */ +/* JANUARY 2002 GLAHN REMOVED LOOP BEFORE 150 TO DETERMINE */ +/* A GROUP OF ALL SAME VALUE */ +/* JANUARY 2002 GLAHN CHANGED MALLOW FROM 9999999 TO 2**30+1, */ +/* AND MADE IT A PARAMETER */ +/* MARCH 2002 GLAHN ADDED NON FATAL IER = 716, 717; */ +/* REMOVED NENDB=NXY ABOVE 150; */ +/* ADDED IERSAV=0; COMMENTS */ + +/* PURPOSE */ +/* DETERMINES GROUPS OF VARIABLE SIZE, BUT AT LEAST OF */ +/* SIZE MINPK, THE ASSOCIATED MAX (JMAX( )) AND MIN (JMIN( )), */ +/* THE NUMBER OF BITS NECESSARY TO HOLD THE VALUES IN EACH */ +/* GROUP (LBIT( )), THE NUMBER OF VALUES IN EACH GROUP */ +/* (NOV( )), THE NUMBER OF BITS NECESSARY TO PACK THE JMIN( ) */ +/* VALUES (IBIT), THE NUMBER OF BITS NECESSARY TO PACK THE */ +/* LBIT( ) VALUES (JBIT), AND THE NUMBER OF BITS NECESSARY */ +/* TO PACK THE NOV( ) VALUES (KBIT). THE ROUTINE IS DESIGNED */ +/* TO DETERMINE THE GROUPS SUCH THAT A SMALL NUMBER OF BITS */ +/* IS NECESSARY TO PACK THE DATA WITHOUT EXCESSIVE */ +/* COMPUTATIONS. IF ALL VALUES IN THE GROUP ARE ZERO, THE */ +/* NUMBER OF BITS TO USE IN PACKING IS DEFINED AS ZERO WHEN */ +/* THERE CAN BE NO MISSING VALUES; WHEN THERE CAN BE MISSING */ +/* VALUES, THE NUMBER OF BITS MUST BE AT LEAST 1 TO HAVE */ +/* THE CAPABILITY TO RECOGNIZE THE MISSING VALUE. HOWEVER, */ +/* IF ALL VALUES IN A GROUP ARE MISSING, THE NUMBER OF BITS */ +/* NEEDED IS 0, AND THE UNPACKER RECOGNIZES THIS. */ +/* ALL VARIABLES ARE INTEGER. EVEN THOUGH THE GROUPS ARE */ +/* INITIALLY OF SIZE MINPK OR LARGER, AN ADJUSTMENT BETWEEN */ +/* TWO GROUPS (THE LOOKBACK PROCEDURE) MAY MAKE A GROUP */ +/* SMALLER THAN MINPK. THE CONTROL ON GROUP SIZE IS THAT */ +/* THE SUM OF THE SIZES OF THE TWO CONSECUTIVE GROUPS, EACH OF */ +/* SIZE MINPK OR LARGER, IS NOT DECREASED. WHEN DETERMINING */ +/* THE NUMBER OF BITS NECESSARY FOR PACKING, THE LARGEST */ +/* VALUE THAT CAN BE ACCOMMODATED IN, SAY, MBITS, IS */ +/* 2**MBITS-1; THIS LARGEST VALUE (AND THE NEXT SMALLEST */ +/* VALUE) IS RESERVED FOR THE MISSING VALUE INDICATOR (ONLY) */ +/* WHEN IS523 NE 0. IF THE DIMENSION NDG */ +/* IS NOT LARGE ENOUGH TO HOLD ALL THE GROUPS, THE LOCAL VALUE */ +/* OF MINPK IS INCREASED BY 50 PERCENT. THIS IS REPEATED */ +/* UNTIL NDG WILL SUFFICE. A DIAGNOSTIC IS PRINTED WHENEVER */ +/* THIS HAPPENS, WHICH SHOULD BE VERY RARELY. IF IT HAPPENS */ +/* OFTEN, NDG IN SUBROUTINE PACK SHOULD BE INCREASED AND */ +/* A CORRESPONDING INCREASE IN SUBROUTINE UNPACK MADE. */ +/* CONSIDERABLE CODE IS PROVIDED SO THAT NO MORE CHECKING */ +/* FOR MISSING VALUES WITHIN LOOPS IS DONE THAN NECESSARY; */ +/* THE ADDED EFFICIENCY OF THIS IS RELATIVELY MINOR, */ +/* BUT DOES NO HARM. FOR GRIB2, THE REFERENCE VALUE FOR */ +/* THE LENGTH OF GROUPS IN NOV( ) AND FOR THE NUMBER OF */ +/* BITS NECESSARY TO PACK GROUP VALUES ARE DETERMINED, */ +/* AND SUBTRACTED BEFORE JBIT AND KBIT ARE DETERMINED. */ + +/* WHEN 1 OR MORE GROUPS ARE LARGE COMPARED TO THE OTHERS, */ +/* THE WIDTH OF ALL GROUPS MUST BE AS LARGE AS THE LARGEST. */ +/* A SUBROUTINE REDUCE BREAKS UP LARGE GROUPS INTO 2 OR */ +/* MORE TO REDUCE TOTAL BITS REQUIRED. IF REDUCE SHOULD */ +/* ABORT, PACK_GP WILL BE EXECUTED AGAIN WITHOUT THE CALL */ +/* TO REDUCE. */ + +/* DATA SET USE */ +/* KFILDO - UNIT NUMBER FOR OUTPUT (PRINT) FILE. (OUTPUT) */ + +/* VARIABLES IN CALL SEQUENCE */ +/* KFILDO = UNIT NUMBER FOR OUTPUT (PRINT) FILE. (INPUT) */ +/* IC( ) = ARRAY TO HOLD DATA FOR PACKING. THE VALUES */ +/* DO NOT HAVE TO BE POSITIVE AT THIS POINT, BUT */ +/* MUST BE IN THE RANGE -2**30 TO +2**30 (THE */ +/* THE VALUE OF MALLOW). THESE INTEGER VALUES */ +/* WILL BE RETAINED EXACTLY THROUGH PACKING AND */ +/* UNPACKING. (INPUT) */ +/* NXY = NUMBER OF VALUES IN IC( ). ALSO TREATED */ +/* AS ITS DIMENSION. (INPUT) */ +/* IS523 = missing value management */ +/* 0=data contains no missing values */ +/* 1=data contains Primary missing values */ +/* 2=data contains Primary and secondary missing values */ +/* (INPUT) */ +/* MINPK = THE MINIMUM SIZE OF EACH GROUP, EXCEPT POSSIBLY */ +/* THE LAST ONE. (INPUT) */ +/* INC = THE NUMBER OF VALUES TO ADD TO AN ALREADY */ +/* EXISTING GROUP IN DETERMINING WHETHER OR NOT */ +/* TO START A NEW GROUP. IDEALLY, THIS WOULD BE */ +/* 1, BUT EACH TIME INC VALUES ARE ATTEMPTED, THE */ +/* MAX AND MIN OF THE NEXT MINPK VALUES MUST BE */ +/* FOUND. THIS IS "A LOOP WITHIN A LOOP," AND */ +/* A SLIGHTLY LARGER VALUE MAY GIVE ABOUT AS GOOD */ +/* RESULTS WITH SLIGHTLY LESS COMPUTATIONAL TIME. */ +/* IF INC IS LE 0, 1 IS USED, AND A DIAGNOSTIC IS */ +/* OUTPUT. NOTE: IT IS EXPECTED THAT INC WILL */ +/* EQUAL 1. THE CODE USES INC PRIMARILY IN THE */ +/* LOOPS STARTING AT STATEMENT 180. IF INC */ +/* WERE 1, THERE WOULD NOT NEED TO BE LOOPS */ +/* AS SUCH. HOWEVER, KINC (THE LOCAL VALUE OF */ +/* INC) IS SET GE 1 WHEN NEAR THE END OF THE DATA */ +/* TO FORESTALL A VERY SMALL GROUP AT THE END. */ +/* (INPUT) */ +/* MISSP = WHEN MISSING POINTS CAN BE PRESENT IN THE DATA, */ +/* THEY WILL HAVE THE VALUE MISSP OR MISSS. */ +/* MISSP IS THE PRIMARY MISSING VALUE AND MISSS */ +/* IS THE SECONDARY MISSING VALUE . THESE MUST */ +/* NOT BE VALUES THAT WOULD OCCUR WITH SUBTRACTING */ +/* THE MINIMUM (REFERENCE) VALUE OR SCALING. */ +/* FOR EXAMPLE, MISSP = 0 WOULD NOT BE ADVISABLE. */ +/* (INPUT) */ +/* MISSS = SECONDARY MISSING VALUE INDICATOR (SEE MISSP). */ +/* (INPUT) */ +/* JMIN(J) = THE MINIMUM OF EACH GROUP (J=1,LX). (OUTPUT) */ +/* JMAX(J) = THE MAXIMUM OF EACH GROUP (J=1,LX). THIS IS */ +/* NOT REALLY NEEDED, BUT SINCE THE MAX OF EACH */ +/* GROUP MUST BE FOUND, SAVING IT HERE IS CHEAP */ +/* IN CASE THE USER WANTS IT. (OUTPUT) */ +/* LBIT(J) = THE NUMBER OF BITS NECESSARY TO PACK EACH GROUP */ +/* (J=1,LX). IT IS ASSUMED THE MINIMUM OF EACH */ +/* GROUP WILL BE REMOVED BEFORE PACKING, AND THE */ +/* VALUES TO PACK WILL, THEREFORE, ALL BE POSITIVE. */ +/* HOWEVER, IC( ) DOES NOT NECESSARILY CONTAIN */ +/* ALL POSITIVE VALUES. IF THE OVERALL MINIMUM */ +/* HAS BEEN REMOVED (THE USUAL CASE), THEN IC( ) */ +/* WILL CONTAIN ONLY POSITIVE VALUES. (OUTPUT) */ +/* NOV(J) = THE NUMBER OF VALUES IN EACH GROUP (J=1,LX). */ +/* (OUTPUT) */ +/* NDG = THE DIMENSION OF JMIN( ), JMAX( ), LBIT( ), AND */ +/* NOV( ). (INPUT) */ +/* LX = THE NUMBER OF GROUPS DETERMINED. (OUTPUT) */ +/* IBIT = THE NUMBER OF BITS NECESSARY TO PACK THE JMIN(J) */ +/* VALUES, J=1,LX. (OUTPUT) */ +/* JBIT = THE NUMBER OF BITS NECESSARY TO PACK THE LBIT(J) */ +/* VALUES, J=1,LX. (OUTPUT) */ +/* KBIT = THE NUMBER OF BITS NECESSARY TO PACK THE NOV(J) */ +/* VALUES, J=1,LX. (OUTPUT) */ +/* NOVREF = REFERENCE VALUE FOR NOV( ). (OUTPUT) */ +/* LBITREF = REFERENCE VALUE FOR LBIT( ). (OUTPUT) */ +/* IER = ERROR RETURN. */ +/* 706 = VALUE WILL NOT PACK IN 30 BITS--FATAL */ +/* 714 = ERROR IN REDUCE--NON-FATAL */ +/* 715 = NGP NOT LARGE ENOUGH IN REDUCE--NON-FATAL */ +/* 716 = MINPK INCEASED--NON-FATAL */ +/* 717 = INC SET = 1--NON-FATAL */ +/* (OUTPUT) */ +/* * = ALTERNATE RETURN WHEN IER NE 0 AND FATAL ERROR. */ + +/* INTERNAL VARIABLES */ +/* CFEED = CONTAINS THE CHARACTER REPRESENTATION */ +/* OF A PRINTER FORM FEED. */ +/* IFEED = CONTAINS THE INTEGER VALUE OF A PRINTER */ +/* FORM FEED. */ +/* KINC = WORKING COPY OF INC. MAY BE MODIFIED. */ +/* MINA = MINIMUM VALUE IN GROUP A. */ +/* MAXA = MAXIMUM VALUE IN GROUP A. */ +/* NENDA = THE PLACE IN IC( ) WHERE GROUP A ENDS. */ +/* KSTART = THE PLACE IN IC( ) WHERE GROUP A STARTS. */ +/* IBITA = NUMBER OF BITS NEEDED TO HOLD VALUES IN GROUP A. */ +/* MINB = MINIMUM VALUE IN GROUP B. */ +/* MAXB = MAXIMUM VALUE IN GROUP B. */ +/* NENDB = THE PLACE IN IC( ) WHERE GROUP B ENDS. */ +/* IBITB = NUMBER OF BITS NEEDED TO HOLD VALUES IN GROUP B. */ +/* MINC = MINIMUM VALUE IN GROUP C. */ +/* MAXC = MAXIMUM VALUE IN GROUP C. */ +/* KTOTAL = COUNT OF NUMBER OF VALUES IN IC( ) PROCESSED. */ +/* NOUNT = NUMBER OF VALUES ADDED TO GROUP A. */ +/* LMISS = 0 WHEN IS523 = 0. WHEN PACKING INTO A */ +/* SPECIFIC NUMBER OF BITS, SAY MBITS, */ +/* THE MAXIMUM VALUE THAT CAN BE HANDLED IS */ +/* 2**MBITS-1. WHEN IS523 = 1, INDICATING */ +/* PRIMARY MISSING VALUES, THIS MAXIMUM VALUE */ +/* IS RESERVED TO HOLD THE PRIMARY MISSING VALUE */ +/* INDICATOR AND LMISS = 1. WHEN IS523 = 2, */ +/* THE VALUE JUST BELOW THE MAXIMUM (I.E., */ +/* 2**MBITS-2) IS RESERVED TO HOLD THE SECONDARY */ +/* MISSING VALUE INDICATOR AND LMISS = 2. */ +/* LMINPK = LOCAL VALUE OF MINPK. THIS WILL BE ADJUSTED */ +/* UPWARD WHENEVER NDG IS NOT LARGE ENOUGH TO HOLD */ +/* ALL THE GROUPS. */ +/* MALLOW = THE LARGEST ALLOWABLE VALUE FOR PACKING. */ +/* MISLLA = SET TO 1 WHEN ALL VALUES IN GROUP A ARE MISSING. */ +/* THIS IS USED TO DISTINGUISH BETWEEN A REAL */ +/* MINIMUM WHEN ALL VALUES ARE NOT MISSING */ +/* AND A MINIMUM THAT HAS BEEN SET TO ZERO WHEN */ +/* ALL VALUES ARE MISSING. 0 OTHERWISE. */ +/* NOTE THAT THIS DOES NOT DISTINGUISH BETWEEN */ +/* PRIMARY AND SECONDARY MISSINGS WHEN SECONDARY */ +/* MISSINGS ARE PRESENT. THIS MEANS THAT */ +/* LBIT( ) WILL NOT BE ZERO WITH THE RESULTING */ +/* COMPRESSION EFFICIENCY WHEN SECONDARY MISSINGS */ +/* ARE PRESENT. ALSO NOTE THAT A CHECK HAS BEEN */ +/* MADE EARLIER TO DETERMINE THAT SECONDARY */ +/* MISSINGS ARE REALLY THERE. */ +/* MISLLB = SET TO 1 WHEN ALL VALUES IN GROUP B ARE MISSING. */ +/* THIS IS USED TO DISTINGUISH BETWEEN A REAL */ +/* MINIMUM WHEN ALL VALUES ARE NOT MISSING */ +/* AND A MINIMUM THAT HAS BEEN SET TO ZERO WHEN */ +/* ALL VALUES ARE MISSING. 0 OTHERWISE. */ +/* MISLLC = PERFORMS THE SAME FUNCTION FOR GROUP C THAT */ +/* MISLLA AND MISLLB DO FOR GROUPS B AND C, */ +/* RESPECTIVELY. */ +/* IBXX2(J) = AN ARRAY THAT WHEN THIS ROUTINE IS FIRST ENTERED */ +/* IS SET TO 2**J, J=0,30. IBXX2(30) = 2**30, WHICH */ +/* IS THE LARGEST VALUE PACKABLE, BECAUSE 2**31 */ +/* IS LARGER THAN THE INTEGER WORD SIZE. */ +/* IFIRST = SET BY DATA STATEMENT TO 0. CHANGED TO 1 ON */ +/* FIRST */ +/* ENTRY WHEN IBXX2( ) IS FILLED. */ +/* MINAK = KEEPS TRACK OF THE LOCATION IN IC( ) WHERE THE */ +/* MINIMUM VALUE IN GROUP A IS LOCATED. */ +/* MAXAK = DOES THE SAME AS MINAK, EXCEPT FOR THE MAXIMUM. */ +/* MINBK = THE SAME AS MINAK FOR GROUP B. */ +/* MAXBK = THE SAME AS MAXAK FOR GROUP B. */ +/* MINCK = THE SAME AS MINAK FOR GROUP C. */ +/* MAXCK = THE SAME AS MAXAK FOR GROUP C. */ +/* ADDA = KEEPS TRACK WHETHER OR NOT AN ATTEMPT TO ADD */ +/* POINTS TO GROUP A WAS MADE. IF SO, THEN ADDA */ +/* KEEPS FROM TRYING TO PUT ONE BACK INTO B. */ +/* (LOGICAL) */ +/* IBITBS = KEEPS CURRENT VALUE IF IBITB SO THAT LOOP */ +/* ENDING AT 166 DOESN'T HAVE TO START AT */ +/* IBITB = 0 EVERY TIME. */ +/* MISSLX(J) = MALLOW EXCEPT WHEN A GROUP IS ALL ONE VALUE (AND */ +/* LBIT(J) = 0) AND THAT VALUE IS MISSING. IN */ +/* THAT CASE, MISSLX(J) IS MISSP OR MISSS. THIS */ +/* GETS INSERTED INTO JMIN(J) LATER AS THE */ +/* MISSING INDICATOR; IT CAN'T BE PUT IN UNTIL */ +/* THE END, BECAUSE JMIN( ) IS USED TO CALCULATE */ +/* THE MAXIMUM NUMBER OF BITS (IBITS) NEEDED TO */ +/* PACK JMIN( ). */ +/* 1 2 3 4 5 6 7 X */ + +/* NON SYSTEM SUBROUTINES CALLED */ +/* NONE */ + + + +/* MISSLX( ) was AN AUTOMATIC ARRAY. */ + misslx = (integer *)calloc(*ndg,sizeof(integer)); + + + /* Parameter adjustments */ + --ic; + --nov; + --lbit; + --jmax; + --jmin; + + /* Function Body */ + + *ier = 0; + iersav = 0; +/* CALL TIMPR(KFILDO,KFILDO,'START PACK_GP ') */ + *(unsigned char *)cfeed = (char) ifeed; + + ired = 0; +/* IRED IS A FLAG. WHEN ZERO, REDUCE WILL BE CALLED. */ +/* IF REDUCE ABORTS, IRED = 1 AND IS NOT CALLED. IN */ +/* THIS CASE PACK_GP EXECUTES AGAIN EXCEPT FOR REDUCE. */ + + if (*inc <= 0) { + iersav = 717; +/* WRITE(KFILDO,101)INC */ +/* 101 FORMAT(/' ****INC ='I8,' NOT CORRECT IN PACK_GP. 1 IS USED.') */ + } + +/* THERE WILL BE A RESTART OF PACK_GP IF SUBROUTINE REDUCE */ +/* ABORTS. THIS SHOULD NOT HAPPEN, BUT IF IT DOES, PACK_GP */ +/* WILL COMPLETE WITHOUT SUBROUTINE REDUCE. A NON FATAL */ +/* DIAGNOSTIC RETURN IS PROVIDED. */ + +L102: + /*kinc = max(*inc,1);*/ + kinc = (*inc > 1) ? *inc : 1; + lminpk = *minpk; + +/* CALCULATE THE POWERS OF 2 THE FIRST TIME ENTERED. */ + + if (ifirst == 0) { + ifirst = 1; + ibxx2[0] = 1; + + for (j = 1; j <= 30; ++j) { + ibxx2[j] = ibxx2[j - 1] << 1; +/* L104: */ + } + + } + +/* THERE WILL BE A RESTART AT 105 IS NDG IS NOT LARGE ENOUGH. */ +/* A NON FATAL DIAGNOSTIC RETURN IS PROVIDED. */ + +L105: + kstart = 1; + ktotal = 0; + *lx = 0; + adda = FALSE_; + lmiss = 0; + if (*is523 == 1) { + lmiss = 1; + } + if (*is523 == 2) { + lmiss = 2; + } + +/* ************************************* */ + +/* THIS SECTION COMPUTES STATISTICS FOR GROUP A. GROUP A IS */ +/* A GROUP OF SIZE LMINPK. */ + +/* ************************************* */ + + ibita = 0; + mina = mallow; + maxa = -mallow; + minak = mallow; + maxak = -mallow; + +/* FIND THE MIN AND MAX OF GROUP A. THIS WILL INITIALLY BE OF */ +/* SIZE LMINPK (IF THERE ARE STILL LMINPK VALUES IN IC( )), BUT */ +/* WILL INCREASE IN SIZE IN INCREMENTS OF INC UNTIL A NEW */ +/* GROUP IS STARTED. THE DEFINITION OF GROUP A IS DONE HERE */ +/* ONLY ONCE (UPON INITIAL ENTRY), BECAUSE A GROUP B CAN ALWAYS */ +/* BECOME A NEW GROUP A AFTER A IS PACKED, EXCEPT IF LMINPK */ +/* HAS TO BE INCREASED BECAUSE NDG IS TOO SMALL. THEREFORE, */ +/* THE SEPARATE LOOPS FOR MISSING AND NON-MISSING HERE BUYS */ +/* ALMOST NOTHING. */ + +/* Computing MIN */ + i__1 = kstart + lminpk - 1; + /*nenda = min(i__1,*nxy);*/ + nenda = (i__1 < *nxy) ? i__1 : *nxy; + if (*nxy - nenda <= lminpk / 2) { + nenda = *nxy; + } +/* ABOVE STATEMENT GUARANTEES THE LAST GROUP IS GT LMINPK/2 BY */ +/* MAKING THE ACTUAL GROUP LARGER. IF A PROVISION LIKE THIS IS */ +/* NOT INCLUDED, THERE WILL MANY TIMES BE A VERY SMALL GROUP */ +/* AT THE END. USE SEPARATE LOOPS FOR MISSING AND NO MISSING */ +/* VALUES FOR EFFICIENCY. */ + +/* DETERMINE WHETHER THERE IS A LONG STRING OF THE SAME VALUE */ +/* UNLESS NENDA = NXY. THIS MAY ALLOW A LARGE GROUP A TO */ +/* START WITH, AS WITH MISSING VALUES. SEPARATE LOOPS FOR */ +/* MISSING OPTIONS. THIS SECTION IS ONLY EXECUTED ONCE, */ +/* IN DETERMINING THE FIRST GROUP. IT HELPS FOR AN ARRAY */ +/* OF MOSTLY MISSING VALUES OR OF ONE VALUE, SUCH AS */ +/* RADAR OR PRECIP DATA. */ + + if (nenda != *nxy && ic[kstart] == ic[kstart + 1]) { +/* NO NEED TO EXECUTE IF FIRST TWO VALUES ARE NOT EQUAL. */ + + if (*is523 == 0) { +/* THIS LOOP IS FOR NO MISSING VALUES. */ + + i__1 = *nxy; + for (k = kstart + 1; k <= i__1; ++k) { + + if (ic[k] != ic[kstart]) { +/* Computing MAX */ + i__2 = nenda, i__3 = k - 1; + /*nenda = max(i__2,i__3);*/ + nenda = (i__2 > i__3) ? i__2 : i__3; + goto L114; + } + +/* L111: */ + } + + nenda = *nxy; +/* FALL THROUGH THE LOOP MEANS ALL VALUES ARE THE SAME. */ + + } else if (*is523 == 1) { +/* THIS LOOP IS FOR PRIMARY MISSING VALUES ONLY. */ + + i__1 = *nxy; + for (k = kstart + 1; k <= i__1; ++k) { + + if (ic[k] != *missp) { + + if (ic[k] != ic[kstart]) { +/* Computing MAX */ + i__2 = nenda, i__3 = k - 1; + /*nenda = max(i__2,i__3);*/ + nenda = (i__2 > i__3) ? i__2 : i__3; + goto L114; + } + + } + +/* L112: */ + } + + nenda = *nxy; +/* FALL THROUGH THE LOOP MEANS ALL VALUES ARE THE SAME. */ + + } else { +/* THIS LOOP IS FOR PRIMARY AND SECONDARY MISSING VALUES. */ + + i__1 = *nxy; + for (k = kstart + 1; k <= i__1; ++k) { + + if (ic[k] != *missp && ic[k] != *misss) { + + if (ic[k] != ic[kstart]) { +/* Computing MAX */ + i__2 = nenda, i__3 = k - 1; + /*nenda = max(i__2,i__3);*/ + nenda = (i__2 > i__3) ? i__2 : i__3; + goto L114; + } + + } + +/* L113: */ + } + + nenda = *nxy; +/* FALL THROUGH THE LOOP MEANS ALL VALUES ARE THE SAME. */ + } + + } + +L114: + if (*is523 == 0) { + + i__1 = nenda; + for (k = kstart; k <= i__1; ++k) { + if (ic[k] < mina) { + mina = ic[k]; + minak = k; + } + if (ic[k] > maxa) { + maxa = ic[k]; + maxak = k; + } +/* L115: */ + } + + } else if (*is523 == 1) { + + i__1 = nenda; + for (k = kstart; k <= i__1; ++k) { + if (ic[k] == *missp) { + goto L117; + } + if (ic[k] < mina) { + mina = ic[k]; + minak = k; + } + if (ic[k] > maxa) { + maxa = ic[k]; + maxak = k; + } +L117: + ; + } + + } else { + + i__1 = nenda; + for (k = kstart; k <= i__1; ++k) { + if (ic[k] == *missp || ic[k] == *misss) { + goto L120; + } + if (ic[k] < mina) { + mina = ic[k]; + minak = k; + } + if (ic[k] > maxa) { + maxa = ic[k]; + maxak = k; + } +L120: + ; + } + + } + + kounta = nenda - kstart + 1; + +/* INCREMENT KTOTAL AND FIND THE BITS NEEDED TO PACK THE A GROUP. */ + + ktotal += kounta; + mislla = 0; + if (mina != mallow) { + goto L125; + } +/* ALL MISSING VALUES MUST BE ACCOMMODATED. */ + mina = 0; + maxa = 0; + mislla = 1; + ibitb = 0; + if (*is523 != 2) { + goto L130; + } +/* WHEN ALL VALUES ARE MISSING AND THERE ARE NO */ +/* SECONDARY MISSING VALUES, IBITA = 0. */ +/* OTHERWISE, IBITA MUST BE CALCULATED. */ + +L125: + itest = maxa - mina + lmiss; + + for (ibita = 0; ibita <= 30; ++ibita) { + if (itest < ibxx2[ibita]) { + goto L130; + } +/* *** THIS TEST IS THE SAME AS: */ +/* *** IF(MAXA-MINA.LT.IBXX2(IBITA)-LMISS)GO TO 130 */ +/* L126: */ + } + +/* WRITE(KFILDO,127)MAXA,MINA */ +/* 127 FORMAT(' ****ERROR IN PACK_GP. VALUE WILL NOT PACK IN 30 BITS.', */ +/* 1 ' MAXA ='I13,' MINA ='I13,'. ERROR AT 127.') */ + *ier = 706; + goto L900; + +L130: + +/* ***D WRITE(KFILDO,131)KOUNTA,KTOTAL,MINA,MAXA,IBITA,MISLLA */ +/* ***D131 FORMAT(' AT 130, KOUNTA ='I8,' KTOTAL ='I8,' MINA ='I8, */ +/* ***D 1 ' MAXA ='I8,' IBITA ='I3,' MISLLA ='I3) */ + +L133: + if (ktotal >= *nxy) { + goto L200; + } + +/* ************************************* */ + +/* THIS SECTION COMPUTES STATISTICS FOR GROUP B. GROUP B IS A */ +/* GROUP OF SIZE LMINPK IMMEDIATELY FOLLOWING GROUP A. */ + +/* ************************************* */ + +L140: + minb = mallow; + maxb = -mallow; + minbk = mallow; + maxbk = -mallow; + ibitbs = 0; + mstart = ktotal + 1; + +/* DETERMINE WHETHER THERE IS A LONG STRING OF THE SAME VALUE. */ +/* THIS WORKS WHEN THERE ARE NO MISSING VALUES. */ + + nendb = 1; + + if (mstart < *nxy) { + + if (*is523 == 0) { +/* THIS LOOP IS FOR NO MISSING VALUES. */ + + i__1 = *nxy; + for (k = mstart + 1; k <= i__1; ++k) { + + if (ic[k] != ic[mstart]) { + nendb = k - 1; + goto L150; + } + +/* L145: */ + } + + nendb = *nxy; +/* FALL THROUGH THE LOOP MEANS ALL REMAINING VALUES */ +/* ARE THE SAME. */ + } + + } + +L150: +/* Computing MAX */ +/* Computing MIN */ + i__3 = ktotal + lminpk; + /*i__1 = nendb, i__2 = min(i__3,*nxy);*/ + i__1 = nendb, i__2 = (i__3 < *nxy) ? i__3 : *nxy; + /*nendb = max(i__1,i__2);*/ + nendb = (i__1 > i__2) ? i__1 : i__2; +/* **** 150 NENDB=MIN(KTOTAL+LMINPK,NXY) */ + + if (*nxy - nendb <= lminpk / 2) { + nendb = *nxy; + } +/* ABOVE STATEMENT GUARANTEES THE LAST GROUP IS GT LMINPK/2 BY */ +/* MAKING THE ACTUAL GROUP LARGER. IF A PROVISION LIKE THIS IS */ +/* NOT INCLUDED, THERE WILL MANY TIMES BE A VERY SMALL GROUP */ +/* AT THE END. USE SEPARATE LOOPS FOR MISSING AND NO MISSING */ + +/* USE SEPARATE LOOPS FOR MISSING AND NO MISSING VALUES */ +/* FOR EFFICIENCY. */ + + if (*is523 == 0) { + + i__1 = nendb; + for (k = mstart; k <= i__1; ++k) { + if (ic[k] <= minb) { + minb = ic[k]; +/* NOTE LE, NOT LT. LT COULD BE USED BUT THEN A */ +/* RECOMPUTE OVER THE WHOLE GROUP WOULD BE NEEDED */ +/* MORE OFTEN. SAME REASONING FOR GE AND OTHER */ +/* LOOPS BELOW. */ + minbk = k; + } + if (ic[k] >= maxb) { + maxb = ic[k]; + maxbk = k; + } +/* L155: */ + } + + } else if (*is523 == 1) { + + i__1 = nendb; + for (k = mstart; k <= i__1; ++k) { + if (ic[k] == *missp) { + goto L157; + } + if (ic[k] <= minb) { + minb = ic[k]; + minbk = k; + } + if (ic[k] >= maxb) { + maxb = ic[k]; + maxbk = k; + } +L157: + ; + } + + } else { + + i__1 = nendb; + for (k = mstart; k <= i__1; ++k) { + if (ic[k] == *missp || ic[k] == *misss) { + goto L160; + } + if (ic[k] <= minb) { + minb = ic[k]; + minbk = k; + } + if (ic[k] >= maxb) { + maxb = ic[k]; + maxbk = k; + } +L160: + ; + } + + } + + kountb = nendb - ktotal; + misllb = 0; + if (minb != mallow) { + goto L165; + } +/* ALL MISSING VALUES MUST BE ACCOMMODATED. */ + minb = 0; + maxb = 0; + misllb = 1; + ibitb = 0; + + if (*is523 != 2) { + goto L170; + } +/* WHEN ALL VALUES ARE MISSING AND THERE ARE NO SECONDARY */ +/* MISSING VALUES, IBITB = 0. OTHERWISE, IBITB MUST BE */ +/* CALCULATED. */ + +L165: + for (ibitb = ibitbs; ibitb <= 30; ++ibitb) { + if (maxb - minb < ibxx2[ibitb] - lmiss) { + goto L170; + } +/* L166: */ + } + +/* WRITE(KFILDO,167)MAXB,MINB */ +/* 167 FORMAT(' ****ERROR IN PACK_GP. VALUE WILL NOT PACK IN 30 BITS.', */ +/* 1 ' MAXB ='I13,' MINB ='I13,'. ERROR AT 167.') */ + *ier = 706; + goto L900; + +/* COMPARE THE BITS NEEDED TO PACK GROUP B WITH THOSE NEEDED */ +/* TO PACK GROUP A. IF IBITB GE IBITA, TRY TO ADD TO GROUP A. */ +/* IF NOT, TRY TO ADD A'S POINTS TO B, UNLESS ADDITION TO A */ +/* HAS BEEN DONE. THIS LATTER IS CONTROLLED WITH ADDA. */ + +L170: + +/* ***D WRITE(KFILDO,171)KOUNTA,KTOTAL,MINA,MAXA,IBITA,MISLLA, */ +/* ***D 1 MINB,MAXB,IBITB,MISLLB */ +/* ***D171 FORMAT(' AT 171, KOUNTA ='I8,' KTOTAL ='I8,' MINA ='I8, */ +/* ***D 1 ' MAXA ='I8,' IBITA ='I3,' MISLLA ='I3, */ +/* ***D 2 ' MINB ='I8,' MAXB ='I8,' IBITB ='I3,' MISLLB ='I3) */ + + if (ibitb >= ibita) { + goto L180; + } + if (adda) { + goto L200; + } + +/* ************************************* */ + +/* GROUP B REQUIRES LESS BITS THAN GROUP A. PUT AS MANY OF A'S */ +/* POINTS INTO B AS POSSIBLE WITHOUT EXCEEDING THE NUMBER OF */ +/* BITS NECESSARY TO PACK GROUP B. */ + +/* ************************************* */ + + kounts = kounta; +/* KOUNTA REFERS TO THE PRESENT GROUP A. */ + mintst = minb; + maxtst = maxb; + mintstk = minbk; + maxtstk = maxbk; + +/* USE SEPARATE LOOPS FOR MISSING AND NO MISSING VALUES */ +/* FOR EFFICIENCY. */ + + if (*is523 == 0) { + + i__1 = kstart; + for (k = ktotal; k >= i__1; --k) { +/* START WITH THE END OF THE GROUP AND WORK BACKWARDS. */ + if (ic[k] < minb) { + mintst = ic[k]; + mintstk = k; + } else if (ic[k] > maxb) { + maxtst = ic[k]; + maxtstk = k; + } + if (maxtst - mintst >= ibxx2[ibitb]) { + goto L174; + } +/* NOTE THAT FOR THIS LOOP, LMISS = 0. */ + minb = mintst; + maxb = maxtst; + minbk = mintstk; + maxbk = maxtstk; + --kounta; +/* THERE IS ONE LESS POINT NOW IN A. */ +/* L1715: */ + } + + } else if (*is523 == 1) { + + i__1 = kstart; + for (k = ktotal; k >= i__1; --k) { +/* START WITH THE END OF THE GROUP AND WORK BACKWARDS. */ + if (ic[k] == *missp) { + goto L1718; + } + if (ic[k] < minb) { + mintst = ic[k]; + mintstk = k; + } else if (ic[k] > maxb) { + maxtst = ic[k]; + maxtstk = k; + } + if (maxtst - mintst >= ibxx2[ibitb] - lmiss) { + goto L174; + } +/* FOR THIS LOOP, LMISS = 1. */ + minb = mintst; + maxb = maxtst; + minbk = mintstk; + maxbk = maxtstk; + misllb = 0; +/* WHEN THE POINT IS NON MISSING, MISLLB SET = 0. */ +L1718: + --kounta; +/* THERE IS ONE LESS POINT NOW IN A. */ +/* L1719: */ + } + + } else { + + i__1 = kstart; + for (k = ktotal; k >= i__1; --k) { +/* START WITH THE END OF THE GROUP AND WORK BACKWARDS. */ + if (ic[k] == *missp || ic[k] == *misss) { + goto L1729; + } + if (ic[k] < minb) { + mintst = ic[k]; + mintstk = k; + } else if (ic[k] > maxb) { + maxtst = ic[k]; + maxtstk = k; + } + if (maxtst - mintst >= ibxx2[ibitb] - lmiss) { + goto L174; + } +/* FOR THIS LOOP, LMISS = 2. */ + minb = mintst; + maxb = maxtst; + minbk = mintstk; + maxbk = maxtstk; + misllb = 0; +/* WHEN THE POINT IS NON MISSING, MISLLB SET = 0. */ +L1729: + --kounta; +/* THERE IS ONE LESS POINT NOW IN A. */ +/* L173: */ + } + + } + +/* AT THIS POINT, KOUNTA CONTAINS THE NUMBER OF POINTS TO CLOSE */ +/* OUT GROUP A WITH. GROUP B NOW STARTS WITH KSTART+KOUNTA AND */ +/* ENDS WITH NENDB. MINB AND MAXB HAVE BEEN ADJUSTED AS */ +/* NECESSARY TO REFLECT GROUP B (EVEN THOUGH THE NUMBER OF BITS */ +/* NEEDED TO PACK GROUP B HAVE NOT INCREASED, THE END POINTS */ +/* OF THE RANGE MAY HAVE). */ + +L174: + if (kounta == kounts) { + goto L200; + } +/* ON TRANSFER, GROUP A WAS NOT CHANGED. CLOSE IT OUT. */ + +/* ONE OR MORE POINTS WERE TAKEN OUT OF A. RANGE AND IBITA */ +/* MAY HAVE TO BE RECOMPUTED; IBITA COULD BE LESS THAN */ +/* ORIGINALLY COMPUTED. IN FACT, GROUP A CAN NOW CONTAIN */ +/* ONLY ONE POINT AND BE PACKED WITH ZERO BITS */ +/* (UNLESS MISSS NE 0). */ + + nouta = kounts - kounta; + ktotal -= nouta; + kountb += nouta; + if (nenda - nouta > minak && nenda - nouta > maxak) { + goto L200; + } +/* WHEN THE ABOVE TEST IS MET, THE MIN AND MAX OF THE */ +/* CURRENT GROUP A WERE WITHIN THE OLD GROUP A, SO THE */ +/* RANGE AND IBITA DO NOT NEED TO BE RECOMPUTED. */ +/* NOTE THAT MINAK AND MAXAK ARE NO LONGER NEEDED. */ + ibita = 0; + mina = mallow; + maxa = -mallow; + +/* USE SEPARATE LOOPS FOR MISSING AND NO MISSING VALUES */ +/* FOR EFFICIENCY. */ + + if (*is523 == 0) { + + i__1 = nenda - nouta; + for (k = kstart; k <= i__1; ++k) { + if (ic[k] < mina) { + mina = ic[k]; + } + if (ic[k] > maxa) { + maxa = ic[k]; + } +/* L1742: */ + } + + } else if (*is523 == 1) { + + i__1 = nenda - nouta; + for (k = kstart; k <= i__1; ++k) { + if (ic[k] == *missp) { + goto L1744; + } + if (ic[k] < mina) { + mina = ic[k]; + } + if (ic[k] > maxa) { + maxa = ic[k]; + } +L1744: + ; + } + + } else { + + i__1 = nenda - nouta; + for (k = kstart; k <= i__1; ++k) { + if (ic[k] == *missp || ic[k] == *misss) { + goto L175; + } + if (ic[k] < mina) { + mina = ic[k]; + } + if (ic[k] > maxa) { + maxa = ic[k]; + } +L175: + ; + } + + } + + mislla = 0; + if (mina != mallow) { + goto L1750; + } +/* ALL MISSING VALUES MUST BE ACCOMMODATED. */ + mina = 0; + maxa = 0; + mislla = 1; + if (*is523 != 2) { + goto L177; + } +/* WHEN ALL VALUES ARE MISSING AND THERE ARE NO SECONDARY */ +/* MISSING VALUES IBITA = 0 AS ORIGINALLY SET. OTHERWISE, */ +/* IBITA MUST BE CALCULATED. */ + +L1750: + itest = maxa - mina + lmiss; + + for (ibita = 0; ibita <= 30; ++ibita) { + if (itest < ibxx2[ibita]) { + goto L177; + } +/* *** THIS TEST IS THE SAME AS: */ +/* *** IF(MAXA-MINA.LT.IBXX2(IBITA)-LMISS)GO TO 177 */ +/* L176: */ + } + +/* WRITE(KFILDO,1760)MAXA,MINA */ +/* 1760 FORMAT(' ****ERROR IN PACK_GP. VALUE WILL NOT PACK IN 30 BITS.', */ +/* 1 ' MAXA ='I13,' MINA ='I13,'. ERROR AT 1760.') */ + *ier = 706; + goto L900; + +L177: + goto L200; + +/* ************************************* */ + +/* AT THIS POINT, GROUP B REQUIRES AS MANY BITS TO PACK AS GROUPA. */ +/* THEREFORE, TRY TO ADD INC POINTS TO GROUP A WITHOUT INCREASING */ +/* IBITA. THIS AUGMENTED GROUP IS CALLED GROUP C. */ + +/* ************************************* */ + +L180: + if (mislla == 1) { + minc = mallow; + minck = mallow; + maxc = -mallow; + maxck = -mallow; + } else { + minc = mina; + maxc = maxa; + minck = minak; + maxck = minak; + } + + nount = 0; + if (*nxy - (ktotal + kinc) <= lminpk / 2) { + kinc = *nxy - ktotal; + } +/* ABOVE STATEMENT CONSTRAINS THE LAST GROUP TO BE NOT LESS THAN */ +/* LMINPK/2 IN SIZE. IF A PROVISION LIKE THIS IS NOT INCLUDED, */ +/* THERE WILL MANY TIMES BE A VERY SMALL GROUP AT THE END. */ + +/* USE SEPARATE LOOPS FOR MISSING AND NO MISSING VALUES */ +/* FOR EFFICIENCY. SINCE KINC IS USUALLY 1, USING SEPARATE */ +/* LOOPS HERE DOESN'T BUY MUCH. A MISSING VALUE WILL ALWAYS */ +/* TRANSFER BACK TO GROUP A. */ + + if (*is523 == 0) { + +/* Computing MIN */ + i__2 = ktotal + kinc; + /*i__1 = min(i__2,*nxy);*/ + i__1 = (i__2 < *nxy) ? i__2 : *nxy; + for (k = ktotal + 1; k <= i__1; ++k) { + if (ic[k] < minc) { + minc = ic[k]; + minck = k; + } + if (ic[k] > maxc) { + maxc = ic[k]; + maxck = k; + } + ++nount; +/* L185: */ + } + + } else if (*is523 == 1) { + +/* Computing MIN */ + i__2 = ktotal + kinc; + /*i__1 = min(i__2,*nxy);*/ + i__1 = (i__2 < *nxy) ? i__2 : *nxy; + for (k = ktotal + 1; k <= i__1; ++k) { + if (ic[k] == *missp) { + goto L186; + } + if (ic[k] < minc) { + minc = ic[k]; + minck = k; + } + if (ic[k] > maxc) { + maxc = ic[k]; + maxck = k; + } +L186: + ++nount; +/* L187: */ + } + + } else { + +/* Computing MIN */ + i__2 = ktotal + kinc; + /*i__1 = min(i__2,*nxy);*/ + i__1 = (i__2 < *nxy) ? i__2 : *nxy; + for (k = ktotal + 1; k <= i__1; ++k) { + if (ic[k] == *missp || ic[k] == *misss) { + goto L189; + } + if (ic[k] < minc) { + minc = ic[k]; + minck = k; + } + if (ic[k] > maxc) { + maxc = ic[k]; + maxck = k; + } +L189: + ++nount; +/* L190: */ + } + + } + +/* ***D WRITE(KFILDO,191)KOUNTA,KTOTAL,MINA,MAXA,IBITA,MISLLA, */ +/* ***D 1 MINC,MAXC,NOUNT,IC(KTOTAL),IC(KTOTAL+1) */ +/* ***D191 FORMAT(' AT 191, KOUNTA ='I8,' KTOTAL ='I8,' MINA ='I8, */ +/* ***D 1 ' MAXA ='I8,' IBITA ='I3,' MISLLA ='I3, */ +/* ***D 2 ' MINC ='I8,' MAXC ='I8, */ +/* ***D 3 ' NOUNT ='I5,' IC(KTOTAL) ='I9,' IC(KTOTAL+1) =',I9) */ + +/* IF THE NUMBER OF BITS NEEDED FOR GROUP C IS GT IBITA, */ +/* THEN THIS GROUP A IS A GROUP TO PACK. */ + + if (minc == mallow) { + minc = mina; + maxc = maxa; + minck = minak; + maxck = maxak; + misllc = 1; + goto L195; +/* WHEN THE NEW VALUE(S) ARE MISSING, THEY CAN ALWAYS */ +/* BE ADDED. */ + + } else { + misllc = 0; + } + + if (maxc - minc >= ibxx2[ibita] - lmiss) { + goto L200; + } + +/* THE BITS NECESSARY FOR GROUP C HAS NOT INCREASED FROM THE */ +/* BITS NECESSARY FOR GROUP A. ADD THIS POINT(S) TO GROUP A. */ +/* COMPUTE THE NEXT GROUP B, ETC., UNLESS ALL POINTS HAVE BEEN */ +/* USED. */ + +L195: + ktotal += nount; + kounta += nount; + mina = minc; + maxa = maxc; + minak = minck; + maxak = maxck; + mislla = misllc; + adda = TRUE_; + if (ktotal >= *nxy) { + goto L200; + } + + if (minbk > ktotal && maxbk > ktotal) { + mstart = nendb + 1; +/* THE MAX AND MIN OF GROUP B WERE NOT FROM THE POINTS */ +/* REMOVED, SO THE WHOLE GROUP DOES NOT HAVE TO BE LOOKED */ +/* AT TO DETERMINE THE NEW MAX AND MIN. RATHER START */ +/* JUST BEYOND THE OLD NENDB. */ + ibitbs = ibitb; + nendb = 1; + goto L150; + } else { + goto L140; + } + +/* ************************************* */ + +/* GROUP A IS TO BE PACKED. STORE VALUES IN JMIN( ), JMAX( ), */ +/* LBIT( ), AND NOV( ). */ + +/* ************************************* */ + +L200: + ++(*lx); + if (*lx <= *ndg) { + goto L205; + } + lminpk += lminpk / 2; +/* WRITE(KFILDO,201)NDG,LMINPK,LX */ +/* 201 FORMAT(' ****NDG ='I5,' NOT LARGE ENOUGH.', */ +/* 1 ' LMINPK IS INCREASED TO 'I3,' FOR THIS FIELD.'/ */ +/* 2 ' LX = 'I10) */ + iersav = 716; + goto L105; + +L205: + jmin[*lx] = mina; + jmax[*lx] = maxa; + lbit[*lx] = ibita; + nov[*lx] = kounta; + kstart = ktotal + 1; + + if (mislla == 0) { + misslx[*lx - 1] = mallow; + } else { + misslx[*lx - 1] = ic[ktotal]; +/* IC(KTOTAL) WAS THE LAST VALUE PROCESSED. IF MISLLA NE 0, */ +/* THIS MUST BE THE MISSING VALUE FOR THIS GROUP. */ + } + +/* ***D WRITE(KFILDO,206)MISLLA,IC(KTOTAL),KTOTAL,LX,JMIN(LX),JMAX(LX), */ +/* ***D 1 LBIT(LX),NOV(LX),MISSLX(LX) */ +/* ***D206 FORMAT(' AT 206, MISLLA ='I2,' IC(KTOTAL) ='I5,' KTOTAL ='I8, */ +/* ***D 1 ' LX ='I6,' JMIN(LX) ='I8,' JMAX(LX) ='I8, */ +/* ***D 2 ' LBIT(LX) ='I5,' NOV(LX) ='I8,' MISSLX(LX) =',I7) */ + + if (ktotal >= *nxy) { + goto L209; + } + +/* THE NEW GROUP A WILL BE THE PREVIOUS GROUP B. SET LIMITS, ETC. */ + + ibita = ibitb; + mina = minb; + maxa = maxb; + minak = minbk; + maxak = maxbk; + mislla = misllb; + nenda = nendb; + kounta = kountb; + ktotal += kounta; + adda = FALSE_; + goto L133; + +/* ************************************* */ + +/* CALCULATE IBIT, THE NUMBER OF BITS NEEDED TO HOLD THE GROUP */ +/* MINIMUM VALUES. */ + +/* ************************************* */ + +L209: + *ibit = 0; + + i__1 = *lx; + for (l = 1; l <= i__1; ++l) { +L210: + if (jmin[l] < ibxx2[*ibit]) { + goto L220; + } + ++(*ibit); + goto L210; +L220: + ; + } + +/* INSERT THE VALUE IN JMIN( ) TO BE USED FOR ALL MISSING */ +/* VALUES WHEN LBIT( ) = 0. WHEN SECONDARY MISSING */ +/* VALUES CAN BE PRESENT, LBIT(L) WILL NOT = 0. */ + + if (*is523 == 1) { + + i__1 = *lx; + for (l = 1; l <= i__1; ++l) { + + if (lbit[l] == 0) { + + if (misslx[l - 1] == *missp) { + jmin[l] = ibxx2[*ibit] - 1; + } + + } + +/* L226: */ + } + + } + +/* ************************************* */ + +/* CALCULATE JBIT, THE NUMBER OF BITS NEEDED TO HOLD THE BITS */ +/* NEEDED TO PACK THE VALUES IN THE GROUPS. BUT FIND AND */ +/* REMOVE THE REFERENCE VALUE FIRST. */ + +/* ************************************* */ + +/* WRITE(KFILDO,228)CFEED,LX */ +/* 228 FORMAT(A1,/' *****************************************' */ +/* 1 /' THE GROUP WIDTHS LBIT( ) FOR ',I8,' GROUPS' */ +/* 2 /' *****************************************') */ +/* WRITE(KFILDO,229) (LBIT(J),J=1,MIN(LX,100)) */ +/* 229 FORMAT(/' '20I6) */ + + *lbitref = lbit[1]; + + i__1 = *lx; + for (k = 1; k <= i__1; ++k) { + if (lbit[k] < *lbitref) { + *lbitref = lbit[k]; + } +/* L230: */ + } + + if (*lbitref != 0) { + + i__1 = *lx; + for (k = 1; k <= i__1; ++k) { + lbit[k] -= *lbitref; +/* L240: */ + } + + } + +/* WRITE(KFILDO,241)CFEED,LBITREF */ +/* 241 FORMAT(A1,/' *****************************************' */ +/* 1 /' THE GROUP WIDTHS LBIT( ) AFTER REMOVING REFERENCE ', */ +/* 2 I8, */ +/* 3 /' *****************************************') */ +/* WRITE(KFILDO,242) (LBIT(J),J=1,MIN(LX,100)) */ +/* 242 FORMAT(/' '20I6) */ + + *jbit = 0; + + i__1 = *lx; + for (k = 1; k <= i__1; ++k) { +L310: + if (lbit[k] < ibxx2[*jbit]) { + goto L320; + } + ++(*jbit); + goto L310; +L320: + ; + } + +/* ************************************* */ + +/* CALCULATE KBIT, THE NUMBER OF BITS NEEDED TO HOLD THE NUMBER */ +/* OF VALUES IN THE GROUPS. BUT FIND AND REMOVE THE */ +/* REFERENCE FIRST. */ + +/* ************************************* */ + +/* WRITE(KFILDO,321)CFEED,LX */ +/* 321 FORMAT(A1,/' *****************************************' */ +/* 1 /' THE GROUP SIZES NOV( ) FOR ',I8,' GROUPS' */ +/* 2 /' *****************************************') */ +/* WRITE(KFILDO,322) (NOV(J),J=1,MIN(LX,100)) */ +/* 322 FORMAT(/' '20I6) */ + + *novref = nov[1]; + + i__1 = *lx; + for (k = 1; k <= i__1; ++k) { + if (nov[k] < *novref) { + *novref = nov[k]; + } +/* L400: */ + } + + if (*novref > 0) { + + i__1 = *lx; + for (k = 1; k <= i__1; ++k) { + nov[k] -= *novref; +/* L405: */ + } + + } + +/* WRITE(KFILDO,406)CFEED,NOVREF */ +/* 406 FORMAT(A1,/' *****************************************' */ +/* 1 /' THE GROUP SIZES NOV( ) AFTER REMOVING REFERENCE ',I8, */ +/* 2 /' *****************************************') */ +/* WRITE(KFILDO,407) (NOV(J),J=1,MIN(LX,100)) */ +/* 407 FORMAT(/' '20I6) */ +/* WRITE(KFILDO,408)CFEED */ +/* 408 FORMAT(A1,/' *****************************************' */ +/* 1 /' THE GROUP REFERENCES JMIN( )' */ +/* 2 /' *****************************************') */ +/* WRITE(KFILDO,409) (JMIN(J),J=1,MIN(LX,100)) */ +/* 409 FORMAT(/' '20I6) */ + + *kbit = 0; + + i__1 = *lx; + for (k = 1; k <= i__1; ++k) { +L410: + if (nov[k] < ibxx2[*kbit]) { + goto L420; + } + ++(*kbit); + goto L410; +L420: + ; + } + +/* DETERMINE WHETHER THE GROUP SIZES SHOULD BE REDUCED */ +/* FOR SPACE EFFICIENCY. */ + + if (ired == 0) { + reduce(kfildo, &jmin[1], &jmax[1], &lbit[1], &nov[1], lx, ndg, ibit, + jbit, kbit, novref, ibxx2, ier); + + if (*ier == 714 || *ier == 715) { +/* REDUCE HAS ABORTED. REEXECUTE PACK_GP WITHOUT REDUCE. */ +/* PROVIDE FOR A NON FATAL RETURN FROM REDUCE. */ + iersav = *ier; + ired = 1; + *ier = 0; + goto L102; + } + + } + + if ( misslx != 0 ) { + free(misslx); + misslx=0; + } +/* CALL TIMPR(KFILDO,KFILDO,'END PACK_GP ') */ + if (iersav != 0) { + *ier = iersav; + return 0; + } + +/* 900 IF(IER.NE.0)RETURN1 */ + +L900: + if ( misslx != 0 ) free(misslx); + return 0; +} /* pack_gp__ */ + diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/pdstemplates.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/pdstemplates.c new file mode 100644 index 000000000..ec9651ff3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/pdstemplates.c @@ -0,0 +1,344 @@ +#include +#include "grib2.h" +#include "pdstemplates.h" + + +static const struct pdstemplate templatespds[MAXPDSTEMP] = { + // 4.0: Analysis or Forecast at Horizontal Level/Layer + // at a point in time + {0,15,0, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4} }, + // 4.1: Individual Ensemble Forecast at Horizontal Level/Layer + // at a point in time + {1,18,0, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,1,1,1} }, + // 4.2: Derived Fcst based on whole Ensemble at Horiz Level/Layer + // at a point in time + {2,17,0, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,1,1} }, + // 4.3: Derived Fcst based on Ensemble cluster over rectangular + // area at Horiz Level/Layer at a point in time + {3,31,1, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,1,1,1,1,1,1,1,-4,-4,4,4,1,-1,4,-1,4} }, + // 4.4: Derived Fcst based on Ensemble cluster over circular + // area at Horiz Level/Layer at a point in time + {4,30,1, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,1,1,1,1,1,1,1,-4,4,4,1,-1,4,-1,4} }, + // 4.5: Probablility Forecast at Horiz Level/Layer + // at a point in time + {5,22,0, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,1,1,1,-1,-4,-1,-4} }, + // 4.6: Percentile Forecast at Horiz Level/Layer + // at a point in time + {6,16,0, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,1} }, + // 4.7: Analysis or Forecast Error at Horizontal Level/Layer + // at a point in time + {7,15,0, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4} }, + // 4.8: Ave/Accum/etc... at Horiz Level/Layer + // in a time interval + {8,29,1, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,2,1,1,1,1,1,1,4,1,1,1,4,1,4} }, + // 4.9: Probablility Forecast at Horiz Level/Layer + // in a time interval + {9,36,1, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,1,1,1,-1,-4,-1,-4,2,1,1,1,1,1,1,4,1,1,1,4,1,4} }, + // 4.10: Percentile Forecast at Horiz Level/Layer + // in a time interval + {10,30,1, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,1,2,1,1,1,1,1,1,4,1,1,1,4,1,4} }, + // 4.11: Individual Ensemble Forecast at Horizontal Level/Layer + // in a time interval + {11,32,1, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,1,1,1,2,1,1,1,1,1,1,4,1,1,1,4,1,4} }, + // 4.12: Derived Fcst based on whole Ensemble at Horiz Level/Layer + // in a time interval + {12,31,1, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,1,1,2,1,1,1,1,1,1,4,1,1,1,4,1,4} }, + // 4.13: Derived Fcst based on Ensemble cluster over rectangular + // area at Horiz Level/Layer in a time interval + {13,45,1, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,1,1,1,1,1,1,1,-4,-4,4,4,1,-1,4,-1,4,2,1,1,1,1,1,1,4,1,1,1,4,1,4} }, + // 4.14: Derived Fcst based on Ensemble cluster over circular + // area at Horiz Level/Layer in a time interval + {14,44,1, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,1,1,1,1,1,1,1,-4,4,4,1,-1,4,-1,4,2,1,1,1,1,1,1,4,1,1,1,4,1,4} }, + // 4.20: Radar Product + {20,19,0, {1,1,1,1,1,-4,4,2,4,2,1,1,1,1,1,2,1,3,2} }, + // 4.30: Satellite Product + {30,5,1, {1,1,1,1,1} }, + // 4.254: CCITT IA5 Character String + {254,3,0, {1,1,4} }, + // 4.1000: Cross section of analysis or forecast + // at a point in time + {1000,9,0, {1,1,1,1,1,2,1,1,4} }, + // 4.1001: Cross section of Ave/Accum/etc... analysis or forecast + // in a time interval + {1001,16,0, {1,1,1,1,1,2,1,1,4,4,1,1,1,4,1,4} }, + // 4.1001: Cross section of Ave/Accum/etc... analysis or forecast + // over latitude or longitude + {1002,15,0, {1,1,1,1,1,2,1,1,4,1,1,1,4,4,2} }, + // 4.1100: Hovmoller-type grid w/ no averaging or other + // statistical processing + {1100,15,0, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4} }, + // 4.1100: Hovmoller-type grid with averaging or other + // statistical processing + {1101,22,0, {1,1,1,1,1,2,1,1,4,1,-1,-4,1,-1,-4,4,1,1,1,4,1,4} } + + } ; + +const struct pdstemplate *get_templatespds() +{ + return templatespds; +} + +g2int getpdsindex(g2int number) +///$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: getpdsindex +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2001-06-28 +// +// ABSTRACT: This function returns the index of specified Product +// Definition Template 4.NN (NN=number) in array templates. +// +// PROGRAM HISTORY LOG: +// 2001-06-28 Gilbert +// +// USAGE: index=getpdsindex(number) +// INPUT ARGUMENT LIST: +// number - NN, indicating the number of the Product Definition +// Template 4.NN that is being requested. +// +// RETURNS: Index of PDT 4.NN in array templates, if template exists. +// = -1, otherwise. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: IBM SP +// +//$$$/ +{ + g2int j,getpdsindex=-1; + + for (j=0;jtype=4; + new->num=templatespds[index].template_num; + new->maplen=templatespds[index].mappdslen; + new->needext=templatespds[index].needext; + new->map=(g2int *)templatespds[index].mappds; + new->extlen=0; + new->ext=0; //NULL + return(new); + } + else { + printf("getpdstemplate: PDS Template 4.%d not defined.\n",(int)number); + return(0); //NULL + } + + return(0); //NULL +} + + +xxtemplate *extpdstemplate(g2int number,g2int *list) +///$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: extpdstemplate +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2000-05-11 +// +// ABSTRACT: This subroutine generates the remaining octet map for a +// given Product Definition Template, if required. Some Templates can +// vary depending on data values given in an earlier part of the +// Template, and it is necessary to know some of the earlier entry +// values to generate the full octet map of the Template. +// +// PROGRAM HISTORY LOG: +// 2000-05-11 Gilbert +// +// USAGE: CALL extpdstemplate(number,list) +// INPUT ARGUMENT LIST: +// number - NN, indicating the number of the Product Definition +// Template 4.NN that is being requested. +// list() - The list of values for each entry in the +// the Product Definition Template 4.NN. +// +// RETURN VALUE: +// - Pointer to the returned template struct. +// Returns NULL pointer, if template not found. +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: IBM SP +// +//$$$ +{ + xxtemplate *new; + g2int index,i,j,k,l; + + index=getpdsindex(number); + if (index == -1) return(0); + + new=getpdstemplate(number); + + if ( ! new->needext ) return(new); + + if ( number == 3 ) { + new->extlen=list[26]; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + for (i=0;iextlen;i++) { + new->ext[i]=1; + } + } + else if ( number == 4 ) { + new->extlen=list[25]; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + for (i=0;iextlen;i++) { + new->ext[i]=1; + } + } + else if ( number == 8 ) { + if ( list[21] > 1 ) { + new->extlen=(list[21]-1)*6; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + for (j=2;j<=list[21];j++) { + l=(j-2)*6; + for (k=0;k<6;k++) { + new->ext[l+k]=new->map[23+k]; + } + } + } + } + else if ( number == 9 ) { + if ( list[28] > 1 ) { + new->extlen=(list[28]-1)*6; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + for (j=2;j<=list[28];j++) { + l=(j-2)*6; + for (k=0;k<6;k++) { + new->ext[l+k]=new->map[30+k]; + } + } + } + } + else if ( number == 10 ) { + if ( list[22] > 1 ) { + new->extlen=(list[22]-1)*6; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + for (j=2;j<=list[22];j++) { + l=(j-2)*6; + for (k=0;k<6;k++) { + new->ext[l+k]=new->map[24+k]; + } + } + } + } + else if ( number == 11 ) { + if ( list[24] > 1 ) { + new->extlen=(list[24]-1)*6; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + for (j=2;j<=list[24];j++) { + l=(j-2)*6; + for (k=0;k<6;k++) { + new->ext[l+k]=new->map[26+k]; + } + } + } + } + else if ( number == 12 ) { + if ( list[23] > 1 ) { + new->extlen=(list[23]-1)*6; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + for (j=2;j<=list[23];j++) { + l=(j-2)*6; + for (k=0;k<6;k++) { + new->ext[l+k]=new->map[25+k]; + } + } + } + } + else if ( number == 13 ) { + new->extlen=((list[37]-1)*6)+list[26]; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + if ( list[37] > 1 ) { + for (j=2;j<=list[37];j++) { + l=(j-2)*6; + for (k=0;k<6;k++) { + new->ext[l+k]=new->map[39+k]; + } + } + } + l=(list[37]-1)*6; + if ( l<0 ) l=0; + for (i=0;iext[l+i]=1; + } + } + else if ( number == 14 ) { + new->extlen=((list[36]-1)*6)+list[25]; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + if ( list[36] > 1 ) { + for (j=2;j<=list[36];j++) { + l=(j-2)*6; + for (k=0;k<6;k++) { + new->ext[l+k]=new->map[38+k]; + } + } + } + l=(list[36]-1)*6; + if ( l<0 ) l=0; + for (i=0;iext[l+i]=1; + } + } + else if ( number == 30 ) { + new->extlen=list[4]*5; + new->ext=(g2int *)malloc(sizeof(g2int)*new->extlen); + for (i=0;iext[l]=2; + new->ext[l+1]=2; + new->ext[l+2]=1; + new->ext[l+3]=1; + new->ext[l+4]=4; + } + } + return(new); + +} + diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/pdstemplates.h b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/pdstemplates.h new file mode 100644 index 000000000..e26823018 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/pdstemplates.h @@ -0,0 +1,50 @@ +#ifndef _pdstemplates_H +#define _pdstemplates_H +#include "grib2.h" + +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-26 +// +// ABSTRACT: This inculde file contains info on all the available +// GRIB2 Product Definition Templates used in Section 4 (PDS). +// The information decribing each template is stored in the +// pdstemplate structure defined below. +// +// Each Template has three parts: The number of entries in the template +// (mappdslen); A map of the template (mappds), which contains the +// number of octets in which to pack each of the template values; and +// a logical value (needext) that indicates whether the Template needs +// to be extended. In some cases the number of entries in a template +// can vary depending upon values specified in the "static" part of +// the template. ( See Template 4.3 as an example ) +// +// NOTE: Array mappds contains the number of octets in which the +// corresponding template values will be stored. A negative value in +// mappds is used to indicate that the corresponding template entry can +// contain negative values. This information is used later when packing +// (or unpacking) the template data values. Negative data values in GRIB +// are stored with the left most bit set to one, and a negative number +// of octets value in mappds[] indicates that this possibility should +// be considered. The number of octets used to store the data value +// in this case would be the absolute value of the negative value in +// mappds[]. +// +// 2005-12-08 Gilbert - Allow negative scale factors and limits for +// Templates 4.5 and 4.9 +// +//$$$ + + #define MAXPDSTEMP 23 // maximum number of templates + #define MAXPDSMAPLEN 200 // maximum template map length + + struct pdstemplate + { + g2int template_num; + g2int mappdslen; + g2int needext; + g2int mappds[MAXPDSMAPLEN]; + }; + +const struct pdstemplate *get_templatespds(); +g2int getpdsindex(g2int number); + +#endif /* _pdstemplates_H */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/pngpack.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/pngpack.c new file mode 100644 index 000000000..a5a47b777 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/pngpack.c @@ -0,0 +1,162 @@ +#include +#include +#include "grib2.h" + +int enc_png(char *,g2int ,g2int ,g2int ,char *); + +void pngpack(g2float *fld,g2int width,g2int height,g2int *idrstmpl, + unsigned char *cpack,g2int *lcpack) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: pngpack +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2003-08-27 +// +// ABSTRACT: This subroutine packs up a data field into PNG image format. +// After the data field is scaled, and the reference value is subtracted out, +// it is treated as a grayscale image and passed to a PNG encoder. +// It also fills in GRIB2 Data Representation Template 5.41 or 5.40010 with +// the appropriate values. +// +// PROGRAM HISTORY LOG: +// 2003-08-27 Gilbert +// +// USAGE: pngpack(g2float *fld,g2int width,g2int height,g2int *idrstmpl, +// unsigned char *cpack,g2int *lcpack); +// INPUT ARGUMENT LIST: +// fld[] - Contains the data values to pack +// width - number of points in the x direction +// height - number of points in the y direction +// idrstmpl - Contains the array of values for Data Representation +// Template 5.41 or 5.40010 +// [0] = Reference value - ignored on input +// [1] = Binary Scale Factor +// [2] = Decimal Scale Factor +// [3] = number of bits for each data value - ignored on input +// [4] = Original field type - currently ignored on input +// Data values assumed to be reals. +// +// OUTPUT ARGUMENT LIST: +// idrstmpl - Contains the array of values for Data Representation +// Template 5.41 or 5.40010 +// [0] = Reference value - set by pngpack routine. +// [1] = Binary Scale Factor - unchanged from input +// [2] = Decimal Scale Factor - unchanged from input +// [3] = Number of bits containing each grayscale pixel value +// [4] = Original field type - currently set = 0 on output. +// Data values assumed to be reals. +// cpack - The packed data field +// lcpack - length of packed field cpack. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: IBM SP +// +//$$$ +{ + g2int *ifld; + static g2float alog2=0.69314718; // ln(2.0) + g2int j,nbits,imin,imax,maxdif; + g2int ndpts,nbytes; + g2float bscale,dscale,rmax,rmin,temp; + unsigned char *ctemp; + + ifld=0; + ndpts=width*height; + bscale=int_power(2.0,-idrstmpl[1]); + dscale=int_power(10.0,idrstmpl[2]); +// +// Find max and min values in the data +// + rmax=fld[0]; + rmin=fld[0]; + for (j=1;j rmax) rmax=fld[j]; + if (fld[j] < rmin) rmin=fld[j]; + } + maxdif = (g2int)RINT( (rmax-rmin)*dscale*bscale ); +// +// If max and min values are not equal, pack up field. +// If they are equal, we have a constant field, and the reference +// value (rmin) is the value for each point in the field and +// set nbits to 0. +// + if (rmin != rmax && maxdif != 0 ) { + ifld=(g2int *)malloc(ndpts*sizeof(g2int)); + // + // Determine which algorithm to use based on user-supplied + // binary scale factor and number of bits. + // + if (idrstmpl[1] == 0) { + // + // No binary scaling and calculate minumum number of + // bits in which the data will fit. + // + imin=(g2int)RINT(rmin*dscale); + imax=(g2int)RINT(rmax*dscale); + maxdif=imax-imin; + temp=log((double)(maxdif+1))/alog2; + nbits=(g2int)ceil(temp); + rmin=(g2float)imin; + // scale data + for(j=0;j +#include +#include "grib2.h" + +int dec_png(unsigned char *,g2int *,g2int *,char *); + +g2int pngunpack(unsigned char *cpack,g2int len,g2int *idrstmpl,g2int ndpts, + g2float *fld) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: pngunpack +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2003-08-27 +// +// ABSTRACT: This subroutine unpacks a data field that was packed into a +// PNG image format +// using info from the GRIB2 Data Representation Template 5.41 or 5.40010. +// +// PROGRAM HISTORY LOG: +// 2003-08-27 Gilbert +// +// USAGE: pngunpack(unsigned char *cpack,g2int len,g2int *idrstmpl,g2int ndpts, +// g2float *fld) +// INPUT ARGUMENT LIST: +// cpack - The packed data field (character*1 array) +// len - length of packed field cpack(). +// idrstmpl - Pointer to array of values for Data Representation +// Template 5.41 or 5.40010 +// ndpts - The number of data values to unpack +// +// OUTPUT ARGUMENT LIST: +// fld[] - Contains the unpacked data values +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: IBM SP +// +//$$$ +{ + + g2int *ifld; + g2int j,nbits,iret,width,height; + g2float ref,bscale,dscale; + unsigned char *ctemp; + + rdieee(idrstmpl+0,&ref,1); + bscale = int_power(2.0,idrstmpl[1]); + dscale = int_power(10.0,-idrstmpl[2]); + nbits = idrstmpl[3]; +// +// if nbits equals 0, we have a constant field where the reference value +// is the data value at each gridpoint +// + if (nbits != 0) { + + ifld=(g2int *)calloc(ndpts,sizeof(g2int)); + ctemp=(unsigned char *)calloc(ndpts*4,1); + if ( ifld == 0 || ctemp == 0) { + fprintf(stderr,"Could not allocate space in jpcunpack.\n Data field NOT upacked.\n"); + return(1); + } + iret=(g2int)dec_png(cpack,&width,&height,ctemp); + gbits(ctemp,ifld,0,nbits,0,ndpts); + for (j=0;j>31; + iexp=(rieee[j]&msk2)>>23; + imant=(rieee[j]&msk3); + //printf("SAGieee= %ld %ld %ld\n",isign,iexp,imant); + + sign=1.0; + if (isign == 1) sign=-1.0; + + if ( (iexp > 0) && (iexp < 255) ) { + temp=(g2float)int_power(2.0,(iexp-127)); + a[j]=sign*temp*(1.0+(two23*(g2float)imant)); + } + else if ( iexp == 0 ) { + if ( imant != 0 ) + a[j]=sign*two126*two23*(g2float)imant; + else + a[j]=sign*0.0; + + } + else if ( iexp == 255 ) + a[j]=sign*(1E+37); + + + } + +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/reduce.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/reduce.c new file mode 100644 index 000000000..044138b8e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/reduce.c @@ -0,0 +1,412 @@ +/* reduce.f -- translated by f2c (version 20031025). + You must link the resulting object file with libf2c: + on Microsoft Windows system, link with libf2c.lib; + on Linux or Unix systems, link with .../path/to/libf2c.a -lm + or, if you install libf2c.a in a standard place, with -lf2c -lm + -- in that order, at the end of the command line, as in + cc *.o -lf2c -lm + Source for libf2c is in /netlib/f2c/libf2c.zip, e.g., + + http://www.netlib.org/f2c/libf2c.zip +*/ + +/*#include "f2c.h"*/ +#include +#include "grib2.h" + +#include "cpl_port.h" + +typedef g2int integer; +typedef g2float real; + +/* Subroutine */ int reduce(CPL_UNUSED integer *kfildo, integer *jmin, integer *jmax, + integer *lbit, integer *nov, integer *lx, integer *ndg, integer *ibit, + integer *jbit, integer *kbit, integer *novref, integer *ibxx2, + integer *ier) +{ + /* Initialized data */ + + static integer ifeed = 12; + + /* System generated locals */ + integer i__1, i__2; + + /* Local variables */ + static integer newboxtp, j, l, m, jj, lxn, left; + static real pimp; + static integer move, novl; + static char cfeed[1]; + static integer /* nboxj[31], */ lxnkp, iorigb, ibxx2m1, movmin, + ntotbt[31], ntotpr, newboxt; + integer *newbox, *newboxp; + + +/* NOVEMBER 2001 GLAHN TDL GRIB2 */ +/* MARCH 2002 GLAHN COMMENT IER = 715 */ +/* MARCH 2002 GLAHN MODIFIED TO ACCOMMODATE LX=1 ON ENTRY */ + +/* PURPOSE */ +/* DETERMINES WHETHER THE NUMBER OF GROUPS SHOULD BE */ +/* INCREASED IN ORDER TO REDUCE THE SIZE OF THE LARGE */ +/* GROUPS, AND TO MAKE THAT ADJUSTMENT. BY REDUCING THE */ +/* SIZE OF THE LARGE GROUPS, LESS BITS MAY BE NECESSARY */ +/* FOR PACKING THE GROUP SIZES AND ALL THE INFORMATION */ +/* ABOUT THE GROUPS. */ + +/* THE REFERENCE FOR NOV( ) WAS REMOVED IN THE CALLING */ +/* ROUTINE SO THAT KBIT COULD BE DETERMINED. THIS */ +/* FURNISHES A STARTING POINT FOR THE ITERATIONS IN REDUCE. */ +/* HOWEVER, THE REFERENCE MUST BE CONSIDERED. */ + +/* DATA SET USE */ +/* KFILDO - UNIT NUMBER FOR OUTPUT (PRINT) FILE. (OUTPUT) */ + +/* VARIABLES IN CALL SEQUENCE */ +/* KFILDO = UNIT NUMBER FOR OUTPUT (PRINT) FILE. (INPUT) */ +/* JMIN(J) = THE MINIMUM OF EACH GROUP (J=1,LX). IT IS */ +/* POSSIBLE AFTER SPLITTING THE GROUPS, JMIN( ) */ +/* WILL NOT BE THE MINIMUM OF THE NEW GROUP. */ +/* THIS DOESN'T MATTER; JMIN( ) IS REALLY THE */ +/* GROUP REFERENCE AND DOESN'T HAVE TO BE THE */ +/* SMALLEST VALUE. (INPUT/OUTPUT) */ +/* JMAX(J) = THE MAXIMUM OF EACH GROUP (J=1,LX). */ +/* (INPUT/OUTPUT) */ +/* LBIT(J) = THE NUMBER OF BITS NECESSARY TO PACK EACH GROUP */ +/* (J=1,LX). (INPUT/OUTPUT) */ +/* NOV(J) = THE NUMBER OF VALUES IN EACH GROUP (J=1,LX). */ +/* (INPUT/OUTPUT) */ +/* LX = THE NUMBER OF GROUPS. THIS WILL BE INCREASED */ +/* IF GROUPS ARE SPLIT. (INPUT/OUTPUT) */ +/* NDG = THE DIMENSION OF JMIN( ), JMAX( ), LBIT( ), AND */ +/* NOV( ). (INPUT) */ +/* IBIT = THE NUMBER OF BITS NECESSARY TO PACK THE JMIN(J) */ +/* VALUES, J=1,LX. (INPUT) */ +/* JBIT = THE NUMBER OF BITS NECESSARY TO PACK THE LBIT(J) */ +/* VALUES, J=1,LX. (INPUT) */ +/* KBIT = THE NUMBER OF BITS NECESSARY TO PACK THE NOV(J) */ +/* VALUES, J=1,LX. IF THE GROUPS ARE SPLIT, KBIT */ +/* IS REDUCED. (INPUT/OUTPUT) */ +/* NOVREF = REFERENCE VALUE FOR NOV( ). (INPUT) */ +/* IBXX2(J) = 2**J (J=0,30). (INPUT) */ +/* IER = ERROR RETURN. (OUTPUT) */ +/* 0 = GOOD RETURN. */ +/* 714 = PROBLEM IN ALGORITHM. REDUCE ABORTED. */ +/* 715 = NGP NOT LARGE ENOUGH. REDUCE ABORTED. */ +/* NTOTBT(J) = THE TOTAL BITS USED FOR THE PACKING BITS J */ +/* (J=1,30). (INTERNAL) */ +/* NBOXJ(J) = NEW BOXES NEEDED FOR THE PACKING BITS J */ +/* (J=1,30). (INTERNAL) */ +/* NEWBOX(L) = NUMBER OF NEW BOXES (GROUPS) FOR EACH ORIGINAL */ +/* GROUP (L=1,LX) FOR THE CURRENT J. (AUTOMATIC) */ +/* (INTERNAL) */ +/* NEWBOXP(L) = SAME AS NEWBOX( ) BUT FOR THE PREVIOUS J. */ +/* THIS ELIMINATES RECOMPUTATION. (AUTOMATIC) */ +/* (INTERNAL) */ +/* CFEED = CONTAINS THE CHARACTER REPRESENTATION */ +/* OF A PRINTER FORM FEED. (CHARACTER) (INTERNAL) */ +/* IFEED = CONTAINS THE INTEGER VALUE OF A PRINTER */ +/* FORM FEED. (INTERNAL) */ +/* IORIGB = THE ORIGINAL NUMBER OF BITS NECESSARY */ +/* FOR THE GROUP VALUES. (INTERNAL) */ +/* 1 2 3 4 5 6 7 X */ + +/* NON SYSTEM SUBROUTINES CALLED */ +/* NONE */ + + +/* NEWBOX( ) AND NEWBOXP( ) were AUTOMATIC ARRAYS. */ + newbox = (integer *)calloc(*ndg,sizeof(integer)); + newboxp = (integer *)calloc(*ndg,sizeof(integer)); + + /* Parameter adjustments */ + --nov; + --lbit; + --jmax; + --jmin; + + /* Function Body */ + + *ier = 0; + if (*lx == 1) { + goto L410; + } +/* IF THERE IS ONLY ONE GROUP, RETURN. */ + + *(unsigned char *)cfeed = (char) ifeed; + +/* INITIALIZE NUMBER OF NEW BOXES PER GROUP TO ZERO. */ + + i__1 = *lx; + for (l = 1; l <= i__1; ++l) { + newbox[l - 1] = 0; +/* L110: */ + } + +/* INITIALIZE NUMBER OF TOTAL NEW BOXES PER J TO ZERO. */ + + for (j = 1; j <= 31; ++j) { + ntotbt[j - 1] = 999999999; + /* nboxj[j - 1] = 0; */ +/* L112: */ + } + + iorigb = (*ibit + *jbit + *kbit) * *lx; +/* IBIT = BITS TO PACK THE JMIN( ). */ +/* JBIT = BITS TO PACK THE LBIT( ). */ +/* KBIT = BITS TO PACK THE NOV( ). */ +/* LX = NUMBER OF GROUPS. */ + ntotbt[*kbit - 1] = iorigb; +/* THIS IS THE VALUE OF TOTAL BITS FOR THE ORIGINAL LX */ +/* GROUPS, WHICH REQUIRES KBITS TO PACK THE GROUP */ +/* LENGHTS. SETTING THIS HERE MAKES ONE LESS LOOPS */ +/* NECESSARY BELOW. */ + +/* COMPUTE BITS NOW USED FOR THE PARAMETERS DEFINED. */ + +/* DETERMINE OTHER POSSIBILITES BY INCREASING LX AND DECREASING */ +/* NOV( ) WITH VALUES GREATER THAN THRESHOLDS. ASSUME A GROUP IS */ +/* SPLIT INTO 2 OR MORE GROUPS SO THAT KBIT IS REDUCED WITHOUT */ +/* CHANGING IBIT OR JBIT. */ + + jj = 0; + +/* Computing MIN */ + i__1 = 30, i__2 = *kbit - 1; + /*for (j = min(i__1,i__2); j >= 2; --j) {*/ + for (j = (i__1 < i__2) ? i__1 : i__2; j >= 2; --j) { +/* VALUES GE KBIT WILL NOT REQUIRE SPLITS. ONCE THE TOTAL */ +/* BITS START INCREASING WITH DECREASING J, STOP. ALSO, THE */ +/* NUMBER OF BITS REQUIRED IS KNOWN FOR KBITS = NTOTBT(KBIT). */ + + newboxt = 0; + + i__1 = *lx; + for (l = 1; l <= i__1; ++l) { + + if (nov[l] < ibxx2[j]) { + newbox[l - 1] = 0; +/* NO SPLITS OR NEW BOXES. */ + goto L190; + } else { + novl = nov[l]; + + m = (nov[l] - 1) / (ibxx2[j] - 1) + 1; +/* M IS FOUND BY SOLVING THE EQUATION BELOW FOR M: */ +/* (NOV(L)+M-1)/M LT IBXX2(J) */ +/* M GT (NOV(L)-1)/(IBXX2(J)-1) */ +/* SET M = (NOV(L)-1)/(IBXX2(J)-1)+1 */ +L130: + novl = (nov[l] + m - 1) / m; +/* THE +M-1 IS NECESSARY. FOR INSTANCE, 15 WILL FIT */ +/* INTO A BOX 4 BITS WIDE, BUT WON'T DIVIDE INTO */ +/* TWO BOXES 3 BITS WIDE EACH. */ + + if (novl < ibxx2[j]) { + goto L185; + } else { + ++m; +/* *** WRITE(KFILDO,135)L,NOV(L),NOVL,M,J,IBXX2(J) */ +/* *** 135 FORMAT(/' AT 135--L,NOV(L),NOVL,M,J,IBXX2(J)',6I10) */ + goto L130; + } + +/* THE ABOVE DO LOOP WILL NEVER COMPLETE. */ + } + +L185: + newbox[l - 1] = m - 1; + newboxt = newboxt + m - 1; +L190: + ; + } + + /* nboxj[j - 1] = newboxt; */ + ntotpr = ntotbt[j]; + ntotbt[j - 1] = (*ibit + *jbit) * (*lx + newboxt) + j * (*lx + + newboxt); + + if (ntotbt[j - 1] >= ntotpr) { + jj = j + 1; +/* THE PLUS IS USED BECAUSE J DECREASES PER ITERATION. */ + goto L250; + } else { + +/* SAVE THE TOTAL NEW BOXES AND NEWBOX( ) IN CASE THIS */ +/* IS THE J TO USE. */ + + newboxtp = newboxt; + + i__1 = *lx; + for (l = 1; l <= i__1; ++l) { + newboxp[l - 1] = newbox[l - 1]; +/* L195: */ + } + +/* WRITE(KFILDO,197)NEWBOXT,IBXX2(J) */ +/* 197 FORMAT(/' *****************************************' */ +/* 1 /' THE NUMBER OF NEWBOXES PER GROUP OF THE TOTAL', */ +/* 2 I10,' FOR GROUP MAXSIZE PLUS 1 ='I10 */ +/* 3 /' *****************************************') */ +/* WRITE(KFILDO,198) (NEWBOX(L),L=1,LX) */ +/* 198 FORMAT(/' '20I6/(' '20I6)) */ + } + +/* 205 WRITE(KFILDO,209)KBIT,IORIGB */ +/* 209 FORMAT(/' ORIGINAL BITS WITH KBIT OF',I5,' =',I10) */ +/* WRITE(KFILDO,210)(N,N=2,10),(IBXX2(N),N=2,10), */ +/* 1 (NTOTBT(N),N=2,10),(NBOXJ(N),N=2,10), */ +/* 2 (N,N=11,20),(IBXX2(N),N=11,20), */ +/* 3 (NTOTBT(N),N=11,20),(NBOXJ(N),N=11,20), */ +/* 4 (N,N=21,30),(IBXX2(N),N=11,20), */ +/* 5 (NTOTBT(N),N=21,30),(NBOXJ(N),N=21,30) */ +/* 210 FORMAT(/' THE TOTAL BYTES FOR MAXIMUM GROUP LENGTHS BY ROW'// */ +/* 1 ' J = THE NUMBER OF BITS PER GROUP LENGTH'/ */ +/* 2 ' IBXX2(J) = THE MAXIMUM GROUP LENGTH PLUS 1 FOR THIS J'/ */ +/* 3 ' NTOTBT(J) = THE TOTAL BITS FOR THIS J'/ */ +/* 4 ' NBOXJ(J) = THE NEW GROUPS FOR THIS J'/ */ +/* 5 4(/10X,9I10)/4(/10I10)/4(/10I10)) */ + +/* L200: */ + } + +L250: + pimp = (iorigb - ntotbt[jj - 1]) / (real) iorigb * 100.f; +/* WRITE(KFILDO,252)PIMP,KBIT,JJ */ +/* 252 FORMAT(/' PERCENT IMPROVEMENT =',F6.1, */ +/* 1 ' BY DECREASING GROUP LENGTHS FROM',I4,' TO',I4,' BITS') */ + if (pimp >= 2.f) { + +/* WRITE(KFILDO,255)CFEED,NEWBOXTP,IBXX2(JJ) */ +/* 255 FORMAT(A1,/' *****************************************' */ +/* 1 /' THE NUMBER OF NEWBOXES PER GROUP OF THE TOTAL', */ +/* 2 I10,' FOR GROUP MAXSIZE PLUS 1 ='I10 */ +/* 2 /' *****************************************') */ +/* WRITE(KFILDO,256) (NEWBOXP(L),L=1,LX) */ +/* 256 FORMAT(/' '20I6) */ + +/* ADJUST GROUP LENGTHS FOR MAXIMUM LENGTH OF JJ BITS. */ +/* THE MIN PER GROUP AND THE NUMBER OF BITS REQUIRED */ +/* PER GROUP ARE NOT CHANGED. THIS MAY MEAN THAT A */ +/* GROUP HAS A MIN (OR REFERENCE) THAT IS NOT ZERO. */ +/* THIS SHOULD NOT MATTER TO THE UNPACKER. */ + + lxnkp = *lx + newboxtp; +/* LXNKP = THE NEW NUMBER OF BOXES */ + + if (lxnkp > *ndg) { +/* DIMENSIONS NOT LARGE ENOUGH. PROBABLY AN ERROR */ +/* OF SOME SORT. ABORT. */ +/* WRITE(KFILDO,257)NDG,LXNPK */ +/* 1 2 3 4 5 6 7 X */ +/* 257 FORMAT(/' DIMENSIONS OF JMIN, ETC. IN REDUCE =',I8, */ +/* 1 ' NOT LARGE ENOUGH FOR THE EXPANDED NUMBER OF', */ +/* 2 ' GROUPS =',I8,'. ABORT REDUCE.') */ + *ier = 715; + goto L410; +/* AN ABORT CAUSES THE CALLING PROGRAM TO REEXECUTE */ +/* WITHOUT CALLING REDUCE. */ + } + + lxn = lxnkp; +/* LXN IS THE NUMBER OF THE BOX IN THE NEW SERIES BEING */ +/* FILLED. IT DECREASES PER ITERATION. */ + ibxx2m1 = ibxx2[jj] - 1; +/* IBXX2M1 IS THE MAXIMUM NUMBER OF VALUES PER GROUP. */ + + for (l = *lx; l >= 1; --l) { + +/* THE VALUES IS NOV( ) REPRESENT THOSE VALUES + NOVREF. */ +/* WHEN VALUES ARE MOVED TO ANOTHER BOX, EACH VALUE */ +/* MOVED TO A NEW BOX REPRESENTS THAT VALUE + NOVREF. */ +/* THIS HAS TO BE CONSIDERED IN MOVING VALUES. */ + + if (newboxp[l - 1] * (ibxx2m1 + *novref) + *novref > nov[l] + * + novref) { +/* IF THE ABOVE TEST IS MET, THEN MOVING IBXX2M1 VALUES */ +/* FOR ALL NEW BOXES WILL LEAVE A NEGATIVE NUMBER FOR */ +/* THE LAST BOX. NOT A TOLERABLE SITUATION. */ + movmin = (nov[l] - newboxp[l - 1] * *novref) / newboxp[l - 1]; + left = nov[l]; +/* LEFT = THE NUMBER OF VALUES TO MOVE FROM THE ORIGINAL */ +/* BOX TO EACH NEW BOX EXCEPT THE LAST. LEFT IS THE */ +/* NUMBER LEFT TO MOVE. */ + } else { + movmin = ibxx2m1; +/* MOVMIN VALUES CAN BE MOVED FOR EACH NEW BOX. */ + left = nov[l]; +/* LEFT IS THE NUMBER OF VALUES LEFT TO MOVE. */ + } + + if (newboxp[l - 1] > 0) { + if ((movmin + *novref) * newboxp[l - 1] + *novref <= nov[l] + + *novref && (movmin + *novref) * (newboxp[l - 1] + 1) + >= nov[l] + *novref) { + goto L288; + } else { +/* ***D WRITE(KFILDO,287)L,MOVMIN,NOVREF,NEWBOXP(L),NOV(L) */ +/* ***D287 FORMAT(/' AT 287 IN REDUCE--L,MOVMIN,NOVREF,', */ +/* ***D 1 'NEWBOXP(L),NOV(L)',5I12 */ +/* ***D 2 ' REDUCE ABORTED.') */ +/* WRITE(KFILDO,2870) */ +/* 2870 FORMAT(/' AN ERROR IN REDUCE ALGORITHM. ABORT REDUCE.') */ + *ier = 714; + goto L410; +/* AN ABORT CAUSES THE CALLING PROGRAM TO REEXECUTE */ +/* WITHOUT CALLING REDUCE. */ + } + + } + +L288: + i__1 = newboxp[l - 1] + 1; + for (j = 1; j <= i__1; ++j) { + /*move = min(movmin,left);*/ + move = (movmin < left) ? movmin : left; + jmin[lxn] = jmin[l]; + jmax[lxn] = jmax[l]; + lbit[lxn] = lbit[l]; + nov[lxn] = move; + --lxn; + left -= move + *novref; +/* THE MOVE OF MOVE VALUES REALLY REPRESENTS A MOVE OF */ +/* MOVE + NOVREF VALUES. */ +/* L290: */ + } + + if (left != -(*novref)) { +/* *** WRITE(KFILDO,292)L,LXN,MOVE,LXNKP,IBXX2(JJ),LEFT,NOV(L), */ +/* *** 1 MOVMIN */ +/* *** 292 FORMAT(' AT 292 IN REDUCE--L,LXN,MOVE,LXNKP,', */ +/* *** 1 'IBXX2(JJ),LEFT,NOV(L),MOVMIN'/8I12) */ + } + +/* L300: */ + } + + *lx = lxnkp; +/* LX IS NOW THE NEW NUMBER OF GROUPS. */ + *kbit = jj; +/* KBIT IS NOW THE NEW NUMBER OF BITS REQUIRED FOR PACKING */ +/* GROUP LENGHTS. */ + } + +/* WRITE(KFILDO,406)CFEED,LX */ +/* 406 FORMAT(A1,/' *****************************************' */ +/* 1 /' THE GROUP SIZES NOV( ) AFTER REDUCTION IN SIZE', */ +/* 2 ' FOR'I10,' GROUPS', */ +/* 3 /' *****************************************') */ +/* WRITE(KFILDO,407) (NOV(J),J=1,LX) */ +/* 407 FORMAT(/' '20I6) */ +/* WRITE(KFILDO,408)CFEED,LX */ +/* 408 FORMAT(A1,/' *****************************************' */ +/* 1 /' THE GROUP MINIMA JMIN( ) AFTER REDUCTION IN SIZE', */ +/* 2 ' FOR'I10,' GROUPS', */ +/* 3 /' *****************************************') */ +/* WRITE(KFILDO,409) (JMIN(J),J=1,LX) */ +/* 409 FORMAT(/' '20I6) */ + +L410: + if ( newbox != 0 ) free(newbox); + if ( newboxp != 0 ) free(newboxp); + return 0; +} /* reduce_ */ diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/seekgb.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/seekgb.c new file mode 100644 index 000000000..8bff0c547 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/seekgb.c @@ -0,0 +1,80 @@ +#include +#include +#include "grib2.h" + +void seekgb(FILE *lugb,g2int iseek,g2int mseek,g2int *lskip,g2int *lgrib) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// +// SUBPROGRAM: seekgb Searches a file for the next GRIB message. +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-28 +// +// ABSTRACT: This subprogram searches a file for the next GRIB Message. +// The search is done starting at byte offset iseek of the file referenced +// by lugb for mseek bytes at a time. +// If found, the starting position and length of the message are returned +// in lskip and lgrib, respectively. +// The search is terminated when an EOF or I/O error is encountered. +// +// PROGRAM HISTORY LOG: +// 2002-10-28 GILBERT Modified from Iredell's skgb subroutine +// +// USAGE: seekgb(FILE *lugb,g2int iseek,g2int mseek,int *lskip,int *lgrib) +// INPUT ARGUMENTS: +// lugb - FILE pointer for the file to search. File must be +// opened before this routine is called. +// iseek - number of bytes in the file to skip before search +// mseek - number of bytes to search at a time +// OUTPUT ARGUMENTS: +// lskip - number of bytes to skip from the beggining of the file +// to where the GRIB message starts +// lgrib - number of bytes in message (set to 0, if no message found) +// +// ATTRIBUTES: +// LANGUAGE: C +// +//$$$ +{ + // g2int ret; + g2int k,k4,ipos,nread,lim,start,vers,end,lengrib; + unsigned char *cbuf; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + *lgrib=0; + cbuf=(unsigned char *)malloc(mseek); + nread=mseek; + ipos=iseek; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LOOP UNTIL GRIB MESSAGE IS FOUND + + while (*lgrib==0 && nread==mseek) { + +// READ PARTIAL SECTION + + /* ret= */ fseek(lugb,ipos,SEEK_SET); + nread=fread(cbuf,sizeof(unsigned char),mseek,lugb); + lim=nread-8; + +// LOOK FOR 'GRIB...' IN PARTIAL SECTION + + for (k=0;k +#include +#include "grib2.h" + + +void simpack(g2float *fld,g2int ndpts,g2int *idrstmpl,unsigned char *cpack,g2int *lcpack) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: simpack +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-11-06 +// +// ABSTRACT: This subroutine packs up a data field using the simple +// packing algorithm as defined in the GRIB2 documention. It +// also fills in GRIB2 Data Representation Template 5.0 with the +// appropriate values. +// +// PROGRAM HISTORY LOG: +// 2002-11-06 Gilbert +// +// USAGE: CALL simpack(fld,ndpts,idrstmpl,cpack,lcpack) +// INPUT ARGUMENT LIST: +// fld[] - Contains the data values to pack +// ndpts - The number of data values in array fld[] +// idrstmpl - Contains the array of values for Data Representation +// Template 5.0 +// [0] = Reference value - ignored on input +// [1] = Binary Scale Factor +// [2] = Decimal Scale Factor +// [3] = Number of bits used to pack data, if value is +// > 0 and <= 31. +// If this input value is 0 or outside above range +// then the num of bits is calculated based on given +// data and scale factors. +// [4] = Original field type - currently ignored on input +// Data values assumed to be reals. +// +// OUTPUT ARGUMENT LIST: +// idrstmpl - Contains the array of values for Data Representation +// Template 5.0 +// [0] = Reference value - set by simpack routine. +// [1] = Binary Scale Factor - unchanged from input +// [2] = Decimal Scale Factor - unchanged from input +// [3] = Number of bits used to pack data, unchanged from +// input if value is between 0 and 31. +// If this input value is 0 or outside above range +// then the num of bits is calculated based on given +// data and scale factors. +// [4] = Original field type - currently set = 0 on output. +// Data values assumed to be reals. +// cpack - The packed data field +// lcpack - length of packed field starting at cpack. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + + static g2int zero=0; + g2int *ifld; + g2int j,nbits,imin,imax,maxdif,nbittot,left; + g2float bscale,dscale,rmax,rmin,temp; + double maxnum; + static g2float alog2=0.69314718; // ln(2.0) + + bscale=int_power(2.0,-idrstmpl[1]); + dscale=int_power(10.0,idrstmpl[2]); + if (idrstmpl[3] <= 0 || idrstmpl[3] > 31) + nbits=0; + else + nbits=idrstmpl[3]; +// +// Find max and min values in the data +// + rmax=fld[0]; + rmin=fld[0]; + for (j=1;j rmax) rmax=fld[j]; + if (fld[j] < rmin) rmin=fld[j]; + } + + ifld=calloc(ndpts,sizeof(g2int)); +// +// If max and min values are not equal, pack up field. +// If they are equal, we have a constant field, and the reference +// value (rmin) is the value for each point in the field and +// set nbits to 0. +// + if (rmin != rmax) { + // + // Determine which algorithm to use based on user-supplied + // binary scale factor and number of bits. + // + if (nbits==0 && idrstmpl[1]==0) { + // + // No binary scaling and calculate minumum number of + // bits in which the data will fit. + // + imin=(g2int)RINT(rmin*dscale); + imax=(g2int)RINT(rmax*dscale); + maxdif=imax-imin; + temp=log((double)(maxdif+1))/alog2; + nbits=(g2int)ceil(temp); + rmin=(g2float)imin; + // scale data + for(j=0;j +#include +#include "grib2.h" + + +g2int simunpack(unsigned char *cpack,g2int *idrstmpl,g2int ndpts,g2float *fld) +////$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: simunpack +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-10-29 +// +// ABSTRACT: This subroutine unpacks a data field that was packed using a +// simple packing algorithm as defined in the GRIB2 documention, +// using info from the GRIB2 Data Representation Template 5.0. +// +// PROGRAM HISTORY LOG: +// 2002-10-29 Gilbert +// +// USAGE: int simunpack(unsigned char *cpack,g2int *idrstmpl,g2int ndpts, +// g2float *fld) +// INPUT ARGUMENT LIST: +// cpack - pointer to the packed data field. +// idrstmpl - pointer to the array of values for Data Representation +// Template 5.0 +// ndpts - The number of data values to unpack +// +// OUTPUT ARGUMENT LIST: +// fld - Contains the unpacked data values. fld must be allocated +// with at least ndpts*sizeof(g2float) bytes before +// calling this routine. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$// +{ + + g2int *ifld; + g2int j,nbits /* ,itype */; + g2float ref,bscale,dscale; + + rdieee(idrstmpl+0,&ref,1); + bscale = int_power(2.0,idrstmpl[1]); + dscale = int_power(10.0,-idrstmpl[2]); + nbits = idrstmpl[3]; + /* itype = idrstmpl[4]; */ + + ifld=(g2int *)calloc(ndpts,sizeof(g2int)); + if ( ifld == 0 ) { + fprintf(stderr,"Could not allocate space in simunpack.\n Data field NOT upacked.\n"); + return(1); + } + +// +// if nbits equals 0, we have a constant field where the reference value +// is the data value at each gridpoint +// + if (nbits != 0) { + gbits(cpack,ifld,0,nbits,0,ndpts); + for (j=0;j +#include +#include +#include "grib2.h" + + +void specpack(g2float *fld,g2int ndpts,g2int JJ,g2int KK,g2int MM, + g2int *idrstmpl,unsigned char *cpack,g2int *lcpack) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: specpack +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2002-12-19 +// +// ABSTRACT: This subroutine packs a spectral data field using the complex +// packing algorithm for spherical harmonic data as +// defined in the GRIB2 Data Representation Template 5.51. +// +// PROGRAM HISTORY LOG: +// 2002-12-19 Gilbert +// +// USAGE: void specpack(g2float *fld,g2int ndpts,g2int JJ,g2int KK,g2int MM, +// g2int *idrstmpl,insigned char *cpack,g2int *lcpack) +// INPUT ARGUMENT LIST: +// fld[] - Contains the packed data values +// ndpts - The number of data values to pack +// JJ - J - pentagonal resolution parameter +// KK - K - pentagonal resolution parameter +// MM - M - pentagonal resolution parameter +// idrstmpl - Contains the array of values for Data Representation +// Template 5.51 +// +// OUTPUT ARGUMENT LIST: +// cpack - The packed data field (character*1 array) +// lcpack - length of packed field cpack(). +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: IBM SP +// +//$$$ +{ + + g2int *ifld,tmplsim[5]; + g2float /* bscale, dscale, */ *unpk,*tfld; + g2float *pscale,tscale; + g2int Js,Ks,Ms,Ts,Ns,inc,incu,incp,n,Nm,m,ipos; + + /* bscale = int_power(2.0,-idrstmpl[1]); */ + /* dscale = int_power(10.0,idrstmpl[2]); */ + Js=idrstmpl[5]; + Ks=idrstmpl[6]; + Ms=idrstmpl[7]; + Ts=idrstmpl[8]; + +// +// Calculate Laplacian scaling factors for each possible wave number. +// + pscale=(g2float *)malloc((JJ+MM)*sizeof(g2float)); + tscale=(g2float)idrstmpl[4]*1E-6; + for (n=Js;n<=JJ+MM;n++) + pscale[n]=pow((g2float)(n*(n+1)),tscale); +// +// Separate spectral coeffs into two lists; one to contain unpacked +// values within the sub-spectrum Js, Ks, Ms, and the other with values +// outside of the sub-spectrum to be packed. +// + tfld=(g2float *)malloc(ndpts*sizeof(g2float)); + unpk=(g2float *)malloc(ndpts*sizeof(g2float)); + ifld=(g2int *)malloc(ndpts*sizeof(g2int)); + inc=0; + incu=0; + incp=0; + for (m=0;m<=MM;m++) { + Nm=JJ; // triangular or trapezoidal + if ( KK == JJ+MM ) Nm=JJ+m; // rhombodial + Ns=Js; // triangular or trapezoidal + if ( Ks == Js+Ms ) Ns=Js+m; // rhombodial + for (n=m;n<=Nm;n++) { + if (n<=Ns && m<=Ms) { // save unpacked value + unpk[incu++]=fld[inc++]; // real part + unpk[incu++]=fld[inc++]; // imaginary part + } + else { // Save value to be packed and scale + // Laplacian scale factor + tfld[incp++]=fld[inc++]*pscale[n]; // real part + tfld[incp++]=fld[inc++]*pscale[n]; // imaginary part + } + } + } + + free(pscale); + + if (incu != Ts) { + printf("specpack: Incorrect number of unpacked values %d given:\n",(int)Ts); + printf("specpack: Resetting idrstmpl[8] to %d\n",(int)incu); + Ts=incu; + } +// +// Add unpacked values to the packed data array in 32-bit IEEE format +// + mkieee(unpk,(g2int *)cpack,Ts); + ipos=4*Ts; +// +// Scale and pack the rest of the coefficients +// + tmplsim[1]=idrstmpl[1]; + tmplsim[2]=idrstmpl[2]; + tmplsim[3]=idrstmpl[3]; + simpack(tfld,ndpts-Ts,tmplsim,cpack+ipos,lcpack); + *lcpack=(*lcpack)+ipos; +// +// Fill in Template 5.51 +// + idrstmpl[0]=tmplsim[0]; + idrstmpl[1]=tmplsim[1]; + idrstmpl[2]=tmplsim[2]; + idrstmpl[3]=tmplsim[3]; + idrstmpl[8]=Ts; + idrstmpl[9]=1; // Unpacked spectral data is 32-bit IEEE + + free(tfld); + free(unpk); + free(ifld); + + return; +} diff --git a/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/specunpack.c b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/specunpack.c new file mode 100644 index 000000000..5b35459a3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/degrib18/g2clib-1.0.4/specunpack.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include "grib2.h" + + +g2int specunpack(unsigned char *cpack,g2int *idrstmpl,g2int ndpts,g2int JJ, + g2int KK, g2int MM, g2float *fld) +//$$$ SUBPROGRAM DOCUMENTATION BLOCK +// . . . . +// SUBPROGRAM: specunpack +// PRGMMR: Gilbert ORG: W/NP11 DATE: 2000-06-21 +// +// ABSTRACT: This subroutine unpacks a spectral data field that was packed +// using the complex packing algorithm for spherical harmonic data as +// defined in the GRIB2 documention, +// using info from the GRIB2 Data Representation Template 5.51. +// +// PROGRAM HISTORY LOG: +// 2000-06-21 Gilbert +// +// USAGE: int specunpack(unsigned char *cpack,g2int *idrstmpl, +// g2int ndpts,g2int JJ,g2int KK,g2int MM,g2float *fld) +// INPUT ARGUMENT LIST: +// cpack - pointer to the packed data field. +// idrstmpl - pointer to the array of values for Data Representation +// Template 5.51 +// ndpts - The number of data values to unpack (real and imaginary parts) +// JJ - J - pentagonal resolution parameter +// KK - K - pentagonal resolution parameter +// MM - M - pentagonal resolution parameter +// +// OUTPUT ARGUMENT LIST: +// fld() - Contains the unpacked data values. fld must be allocated +// with at least ndpts*sizeof(g2float) bytes before +// calling this routine. +// +// REMARKS: None +// +// ATTRIBUTES: +// LANGUAGE: C +// MACHINE: +// +//$$$ +{ + + g2int *ifld,j,iofst,nbits; + g2float ref,bscale,dscale,*unpk; + g2float *pscale,tscale; + g2int Js,Ks,Ms,Ts,Ns,Nm,n,m; + g2int inc,incu,incp; + + rdieee(idrstmpl+0,&ref,1); + bscale = int_power(2.0,idrstmpl[1]); + dscale = int_power(10.0,-idrstmpl[2]); + nbits = idrstmpl[3]; + Js=idrstmpl[5]; + Ks=idrstmpl[6]; + Ms=idrstmpl[7]; + Ts=idrstmpl[8]; + + if (idrstmpl[9] == 1) { // unpacked floats are 32-bit IEEE + + unpk=(g2float *)malloc(ndpts*sizeof(g2float)); + ifld=(g2int *)malloc(ndpts*sizeof(g2int)); + + gbits(cpack,ifld,0,32,0,Ts); + iofst=32*Ts; + rdieee(ifld,unpk,Ts); // read IEEE unpacked floats + gbits(cpack,ifld,iofst,nbits,0,ndpts-Ts); // unpack scaled data +// +// Calculate Laplacian scaling factors for each possible wave number. +// + pscale=(g2float *)malloc((JJ+MM+1)*sizeof(g2float)); + tscale=(g2float)idrstmpl[4]*1E-6; + for (n=Js;n<=JJ+MM;n++) + pscale[n]=pow((g2float)(n*(n+1)),-tscale); +// +// Assemble spectral coeffs back to original order. +// + inc=0; + incu=0; + incp=0; + for (m=0;m<=MM;m++) { + Nm=JJ; // triangular or trapezoidal + if ( KK == JJ+MM ) Nm=JJ+m; // rhombodial + Ns=Js; // triangular or trapezoidal + if ( Ks == Js+Ms ) Ns=Js+m; // rhombodial + for (n=m;n<=Nm;n++) { + if (n<=Ns && m<=Ms) { // grab unpacked value + fld[inc++]=unpk[incu++]; // real part + fld[inc++]=unpk[incu++]; // imaginary part + } + else { // Calc coeff from packed value + fld[inc++]=(((g2float)ifld[incp++]*bscale)+ref)* + dscale*pscale[n]; // real part + fld[inc++]=(((g2float)ifld[incp++]*bscale)+ref)* + dscale*pscale[n]; // imaginary part + } + } + } + + free(pscale); + free(unpk); + free(ifld); + + } + else { + printf("specunpack: Cannot handle 64 or 128-bit floats.\n"); + for (j=0;j + +GRIB -- WMO General Regularly-distributed Information in Binary form + + + + +

GRIB -- WMO General Regularly-distributed Information in Binary form

+ +GDAL supports reading of GRIB1 and GRIB2 format raster data, with some degree +of support for coordinate system, georeferencing and other metadata. GRIB +format is commonly used for distribution of Meteorological information, and +is propagated by the World Meteorological Organization.

+ +The GDAL GRIB driver is based on a modified version of the degrib application +which is written primarily by Arthur Taylor of NOAA NWS NDFD (MDL). The +degrib application (and the GDAL GRIB driver) are built on the g2clib +grib decoding library written primarily by John Huddleston of NOAA NWS NCEP. +

+ +There are several encoding schemes for raster data in GRIB format. Most +common ones should be supported including PNG encoding. JPEG2000 encoded +GRIB files will generally be supported if GDAL is also built with JPEG2000 +support via one of the GDAL JPEG2000 drivers. The JasPer library generally +provides the best jpeg2000 support for the GRIB driver.

+ +GRIB files may a be represented in GDAL as having many bands, with some sets +of bands representing a time sequence. GRIB bands are represented as Float64 +(double precision floating point) regardless of the actual values. GRIB +metadata is captured as per-band metadata and used to set band descriptions, +similar to this: +

    
+  Description = 100000[Pa] ISBL="Isobaric surface"
+    GRIB_UNIT=[gpm]
+    GRIB_COMMENT=Geopotential height [gpm]
+    GRIB_ELEMENT=HGT
+    GRIB_SHORT_NAME=100000-ISBL
+    GRIB_REF_TIME=  1201100400 sec UTC
+    GRIB_VALID_TIME=  1201104000 sec UTC
+    GRIB_FORECAST_SECONDS=3600 sec
+
+ +GRIB2 files may also include an extract of the product definition template +number (octet 8-9), and the product definition template values (octet 10+) +as metadata like this: + +
+    GRIB_PDS_PDTN=0
+    GRIB_PDS_TEMPLATE_NUMBERS=3 5 2 0 105 0 0 0 1 0 0 0 1 100 0 0 1 134 160 255 0 0 0 0 0
+
+ +

Configuration options

+ +

+ +This paragraph lists the configuration options that can be set to alter the default behaviour of the GRIB driver. + +

    +
  • GRIB_NORMALIZE_UNITS : (GDAL >= 1.9.0) Can be set to NO to avoid gdal to normalize units to metric.
  • +
+

+ +

Known issues:

+ +The library that GDAL uses to read GRIB files is known to be not thread-safe, so you should avoid +reading or writing several GRIB datasets at the same time from different threads. + +

See Also:

+ + + + + diff --git a/bazaar/plugin/gdal/frmts/grib/gribdataset.cpp b/bazaar/plugin/gdal/frmts/grib/gribdataset.cpp new file mode 100644 index 000000000..73849223a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/gribdataset.cpp @@ -0,0 +1,949 @@ +/****************************************************************************** + * $Id: gribdataset.cpp 28459 2015-02-12 13:48:21Z rouault $ + * + * Project: GRIB Driver + * Purpose: GDALDataset driver for GRIB translator for read support + * Author: Bas Retsios, retsios@itc.nl + * + ****************************************************************************** + * Copyright (c) 2007, ITC + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + */ + +#include "gdal_pam.h" +#include "cpl_multiproc.h" + +#include "degrib18/degrib/degrib2.h" +#include "degrib18/degrib/inventory.h" +#include "degrib18/degrib/myerror.h" +#include "degrib18/degrib/filedatasource.h" +#include "degrib18/degrib/memorydatasource.h" + +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: gribdataset.cpp 28459 2015-02-12 13:48:21Z rouault $"); + +CPL_C_START +void GDALRegister_GRIB(void); +CPL_C_END + +static CPLMutex *hGRIBMutex = NULL; + +/************************************************************************/ +/* ==================================================================== */ +/* GRIBDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GRIBRasterBand; + +class GRIBDataset : public GDALPamDataset +{ + friend class GRIBRasterBand; + + public: + GRIBDataset(); + ~GRIBDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef(); + + private: + void SetGribMetaData(grib_MetaData* meta); + VSILFILE *fp; + char *pszProjection; + OGRCoordinateTransformation *poTransform; + double adfGeoTransform[6]; // Calculate and store once as GetGeoTransform may be called multiple times + + GIntBig nCachedBytes; + GIntBig nCachedBytesThreshold; + int bCacheOnlyOneBand; + GRIBRasterBand* poLastUsedBand; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GRIBRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GRIBRasterBand : public GDALPamRasterBand +{ + friend class GRIBDataset; + +public: + GRIBRasterBand( GRIBDataset*, int, inventoryType* ); + virtual ~GRIBRasterBand(); + virtual CPLErr IReadBlock( int, int, void * ); + virtual const char *GetDescription() const; + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + + void FindPDSTemplate(); + + void UncacheData(); + +private: + + CPLErr LoadData(); + + static void ReadGribData( DataSource &, sInt4, int, double**, grib_MetaData**); + sInt4 start; + int subgNum; + char *longFstLevel; + + double * m_Grib_Data; + grib_MetaData* m_Grib_MetaData; + + int nGribDataXSize; + int nGribDataYSize; +}; + +/************************************************************************/ +/* ConvertUnitInText() */ +/************************************************************************/ + +static CPLString ConvertUnitInText(int bMetricUnits, const char* pszTxt) +{ + if( !bMetricUnits ) + return pszTxt; + + CPLString osRes(pszTxt); + size_t iPos = osRes.find("[K]"); + if( iPos != std::string::npos ) + osRes = osRes.substr(0, iPos) + "[C]" + osRes.substr(iPos + 3); + return osRes; +} + +/************************************************************************/ +/* GRIBRasterBand() */ +/************************************************************************/ + +GRIBRasterBand::GRIBRasterBand( GRIBDataset *poDS, int nBand, + inventoryType *psInv ) + : m_Grib_Data(NULL), m_Grib_MetaData(NULL) +{ + this->poDS = poDS; + this->nBand = nBand; + this->start = psInv->start; + this->subgNum = psInv->subgNum; + this->longFstLevel = CPLStrdup(psInv->longFstLevel); + + eDataType = GDT_Float64; // let user do -ot Float32 if needed for saving space, GRIB contains Float64 (though not fully utilized most of the time) + + nBlockXSize = poDS->nRasterXSize; + nBlockYSize = 1; + + nGribDataXSize = poDS->nRasterXSize; + nGribDataYSize = poDS->nRasterYSize; + + const char* pszGribNormalizeUnits = CPLGetConfigOption("GRIB_NORMALIZE_UNITS", "YES"); + int bMetricUnits = CSLTestBoolean(pszGribNormalizeUnits); + + SetMetadataItem( "GRIB_UNIT", ConvertUnitInText(bMetricUnits, psInv->unitName) ); + SetMetadataItem( "GRIB_COMMENT", ConvertUnitInText(bMetricUnits, psInv->comment) ); + SetMetadataItem( "GRIB_ELEMENT", psInv->element ); + SetMetadataItem( "GRIB_SHORT_NAME", psInv->shortFstLevel ); + SetMetadataItem( "GRIB_REF_TIME", + CPLString().Printf("%12.0f sec UTC", psInv->refTime ) ); + SetMetadataItem( "GRIB_VALID_TIME", + CPLString().Printf("%12.0f sec UTC", psInv->validTime ) ); + SetMetadataItem( "GRIB_FORECAST_SECONDS", + CPLString().Printf("%.0f sec", psInv->foreSec ) ); +} + +/************************************************************************/ +/* FindPDSTemplate() */ +/* */ +/* Scan the file for the PDS template info and represent it as */ +/* metadata. */ +/************************************************************************/ + +void GRIBRasterBand::FindPDSTemplate() + +{ + GRIBDataset *poGDS = (GRIBDataset *) poDS; + +/* -------------------------------------------------------------------- */ +/* Collect section 4 octet information ... we read the file */ +/* ourselves since the GRIB API does not appear to preserve all */ +/* this for us. */ +/* -------------------------------------------------------------------- */ + GIntBig nOffset = VSIFTellL( poGDS->fp ); + GByte abyHead[5]; + GUInt32 nSectSize; + + VSIFSeekL( poGDS->fp, start+16, SEEK_SET ); + VSIFReadL( abyHead, 5, 1, poGDS->fp ); + + while( abyHead[4] != 4 ) + { + memcpy( &nSectSize, abyHead, 4 ); + CPL_MSBPTR32( &nSectSize ); + + if( VSIFSeekL( poGDS->fp, nSectSize-5, SEEK_CUR ) != 0 + || VSIFReadL( abyHead, 5, 1, poGDS->fp ) != 1 ) + break; + } + + if( abyHead[4] == 4 ) + { + GUInt16 nCoordCount; + GUInt16 nPDTN; + CPLString osOctet; + int i; + GByte *pabyBody; + + memcpy( &nSectSize, abyHead, 4 ); + CPL_MSBPTR32( &nSectSize ); + + pabyBody = (GByte *) CPLMalloc(nSectSize-5); + VSIFReadL( pabyBody, 1, nSectSize-5, poGDS->fp ); + + memcpy( &nCoordCount, pabyBody + 5 - 5, 2 ); + CPL_MSBPTR16( &nCoordCount ); + + memcpy( &nPDTN, pabyBody + 7 - 5, 2 ); + CPL_MSBPTR16( &nPDTN ); + + SetMetadataItem( "GRIB_PDS_PDTN", + CPLString().Printf( "%d", nPDTN ) ); + + for( i = 9; i < (int) nSectSize; i++ ) + { + char szByte[10]; + + if( i == 9 ) + sprintf( szByte, "%d", pabyBody[i-5] ); + else + sprintf( szByte, " %d", pabyBody[i-5] ); + osOctet += szByte; + } + + SetMetadataItem( "GRIB_PDS_TEMPLATE_NUMBERS", osOctet ); + + CPLFree( pabyBody ); + } + + VSIFSeekL( poGDS->fp, nOffset, SEEK_SET ); +} + +/************************************************************************/ +/* GetDescription() */ +/************************************************************************/ + +const char * GRIBRasterBand::GetDescription() const +{ + if( longFstLevel == NULL ) + return GDALPamRasterBand::GetDescription(); + else + return longFstLevel; +} + +/************************************************************************/ +/* LoadData() */ +/************************************************************************/ + +CPLErr GRIBRasterBand::LoadData() + +{ + if( !m_Grib_Data ) + { + GRIBDataset *poGDS = (GRIBDataset *) poDS; + + if (poGDS->bCacheOnlyOneBand) + { + /* In "one-band-at-a-time" strategy, if the last recently used */ + /* band is not that one, uncache it. We could use a smarter strategy */ + /* based on a LRU, but that's a bit overkill for now. */ + poGDS->poLastUsedBand->UncacheData(); + poGDS->nCachedBytes = 0; + } + else + { + /* Once we have cached more than nCachedBytesThreshold bytes, we will switch */ + /* to "one-band-at-a-time" strategy, instead of caching all bands that have */ + /* been accessed */ + if (poGDS->nCachedBytes > poGDS->nCachedBytesThreshold) + { + CPLDebug("GRIB", "Maximum band cache size reached for this dataset. " + "Caching only one band at a time from now"); + for(int i=0;inBands;i++) + { + ((GRIBRasterBand*) poGDS->GetRasterBand(i+1))->UncacheData(); + } + poGDS->nCachedBytes = 0; + poGDS->bCacheOnlyOneBand = TRUE; + } + } + + FileDataSource grib_fp (poGDS->fp); + + // we don't seem to have any way to detect errors in this! + ReadGribData(grib_fp, start, subgNum, &m_Grib_Data, &m_Grib_MetaData); + if( !m_Grib_Data ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Out of memory." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Check that this band matches the dataset as a whole, size */ +/* wise. (#3246) */ +/* -------------------------------------------------------------------- */ + nGribDataXSize = m_Grib_MetaData->gds.Nx; + nGribDataYSize = m_Grib_MetaData->gds.Ny; + + poGDS->nCachedBytes += nGribDataXSize * nGribDataYSize * sizeof(double); + poGDS->poLastUsedBand = this; + + if( nGribDataXSize != nRasterXSize + || nGribDataYSize != nRasterYSize ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Band %d of GRIB dataset is %dx%d, while the first band and dataset is %dx%d. Georeferencing of band %d may be incorrect, and data access may be incomplete.", + nBand, + nGribDataXSize, nGribDataYSize, + nRasterXSize, nRasterYSize, + nBand ); + } + } + + return CE_None; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GRIBRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) + +{ + CPLErr eErr = LoadData(); + if (eErr != CE_None) + return eErr; + +/* -------------------------------------------------------------------- */ +/* The image as read is always upside down to our normal */ +/* orientation so we need to effectively flip it at this */ +/* point. We also need to deal with bands that are a different */ +/* size than the dataset as a whole. */ +/* -------------------------------------------------------------------- */ + if( nGribDataXSize == nRasterXSize + && nGribDataYSize == nRasterYSize ) + { + // Simple 1:1 case. + memcpy(pImage, + m_Grib_Data + nRasterXSize * (nRasterYSize - nBlockYOff - 1), + nRasterXSize * sizeof(double)); + + return CE_None; + } + else + { + memset( pImage, 0, sizeof(double)*nRasterXSize ); + + if( nBlockYOff >= nGribDataYSize ) // off image? + return CE_None; + + int nCopyWords = MIN(nRasterXSize,nGribDataXSize); + + memcpy( pImage, + m_Grib_Data + nGribDataXSize*(nGribDataYSize-nBlockYOff-1), + nCopyWords * sizeof(double) ); + + return CE_None; + } +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double GRIBRasterBand::GetNoDataValue( int *pbSuccess ) +{ + CPLErr eErr = LoadData(); + if (eErr != CE_None || + m_Grib_MetaData == NULL || + m_Grib_MetaData->gridAttrib.f_miss == 0) + { + if (pbSuccess) + *pbSuccess = FALSE; + return 0; + } + + if (m_Grib_MetaData->gridAttrib.f_miss == 2) + { + /* what TODO ? */ + CPLDebug("GRIB", "Secondary missing value also set for band %d : %f", + nBand, m_Grib_MetaData->gridAttrib.missSec); + } + + if (pbSuccess) + *pbSuccess = TRUE; + return m_Grib_MetaData->gridAttrib.missPri; +} + +/************************************************************************/ +/* ReadGribData() */ +/************************************************************************/ + +void GRIBRasterBand::ReadGribData( DataSource & fp, sInt4 start, int subgNum, double** data, grib_MetaData** metaData) +{ + /* Initialisation, for calling the ReadGrib2Record function */ + sInt4 f_endMsg = 1; /* 1 if we read the last grid in a GRIB message, or we haven't read any messages. */ + // int subgNum = 0; /* The subgrid in the message that we are interested in. */ + sChar f_unit = 2; /* None = 0, English = 1, Metric = 2 */ + double majEarth = 0; /* -radEarth if < 6000 ignore, otherwise use this to + * override the radEarth in the GRIB1 or GRIB2 + * message. Needed because NCEP uses 6371.2 but GRIB1 could only state 6367.47. */ + double minEarth = 0; /* -minEarth if < 6000 ignore, otherwise use this to + * override the minEarth in the GRIB1 or GRIB2 message. */ + sChar f_SimpleVer = 4; /* Which version of the simple NDFD Weather table to + * use. (1 is 6/2003) (2 is 1/2004) (3 is 2/2004) + * (4 is 11/2004) (default 4) */ + LatLon lwlf; /* lower left corner (cookie slicing) -lwlf */ + LatLon uprt; /* upper right corner (cookie slicing) -uprt */ + IS_dataType is; /* Un-parsed meta data for this GRIB2 message. As well as some memory used by the unpacker. */ + + lwlf.lat = -100; // lat == -100 instructs the GRIB decoder that we don't want a subgrid + + IS_Init (&is); + + const char* pszGribNormalizeUnits = CPLGetConfigOption("GRIB_NORMALIZE_UNITS", "YES"); + if ( !CSLTestBoolean(pszGribNormalizeUnits) ) + f_unit = 0; /* do not normalize units to metric */ + + /* Read GRIB message from file position "start". */ + fp.DataSourceFseek(start, SEEK_SET); + uInt4 grib_DataLen = 0; /* Size of Grib_Data. */ + *metaData = new grib_MetaData(); + MetaInit (*metaData); + ReadGrib2Record (fp, f_unit, data, &grib_DataLen, *metaData, &is, subgNum, + majEarth, minEarth, f_SimpleVer, &f_endMsg, &lwlf, &uprt); + + char * errMsg = errSprintf(NULL); // no intention to show errors, just swallow it and free the memory + if( errMsg != NULL ) + CPLDebug( "GRIB", "%s", errMsg ); + free(errMsg); + IS_Free(&is); +} + +/************************************************************************/ +/* UncacheData() */ +/************************************************************************/ + +void GRIBRasterBand::UncacheData() +{ + if (m_Grib_Data) + free (m_Grib_Data); + m_Grib_Data = NULL; + if (m_Grib_MetaData) + { + MetaFree( m_Grib_MetaData ); + delete m_Grib_MetaData; + } + m_Grib_MetaData = NULL; +} + +/************************************************************************/ +/* ~GRIBRasterBand() */ +/************************************************************************/ + +GRIBRasterBand::~GRIBRasterBand() +{ + CPLFree(longFstLevel); + UncacheData(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* GRIBDataset */ +/* ==================================================================== */ +/************************************************************************/ + +GRIBDataset::GRIBDataset() + +{ + poTransform = NULL; + pszProjection = CPLStrdup(""); + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + nCachedBytes = 0; + /* Switch caching strategy once 100 MB threshold is reached */ + /* Why 100 MB ? --> why not ! */ + nCachedBytesThreshold = ((GIntBig)atoi(CPLGetConfigOption("GRIB_CACHEMAX", "100"))) * 1024 * 1024; + bCacheOnlyOneBand = FALSE; + poLastUsedBand = NULL; +} + +/************************************************************************/ +/* ~GRIBDataset() */ +/************************************************************************/ + +GRIBDataset::~GRIBDataset() + +{ + FlushCache(); + if( fp != NULL ) + VSIFCloseL( fp ); + + CPLFree( pszProjection ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GRIBDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GRIBDataset::GetProjectionRef() + +{ + return pszProjection; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int GRIBDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + if (poOpenInfo->nHeaderBytes < 8) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Does a part of what ReadSECT0() but in a thread-safe way. */ +/* -------------------------------------------------------------------- */ + int i; + for(i=0;inHeaderBytes-3;i++) + { + if (EQUALN((const char*)poOpenInfo->pabyHeader + i, "GRIB", 4) || + EQUALN((const char*)poOpenInfo->pabyHeader + i, "TDLP", 4)) + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GRIBDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify(poOpenInfo) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* A fast "probe" on the header that is partially read in memory. */ +/* -------------------------------------------------------------------- */ + char *buff = NULL; + uInt4 buffLen = 0; + sInt4 sect0[SECT0LEN_WORD]; + uInt4 gribLen; + int version; +// grib is not thread safe, make sure not to cause problems +// for other thread safe formats + + CPLMutexHolderD(&hGRIBMutex); + MemoryDataSource mds (poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes); + if (ReadSECT0 (mds, &buff, &buffLen, -1, sect0, &gribLen, &version) < 0) { + free (buff); + char * errMsg = errSprintf(NULL); + if( errMsg != NULL && strstr(errMsg,"Ran out of file") == NULL ) + CPLDebug( "GRIB", "%s", errMsg ); + free(errMsg); + return NULL; + } + free(buff); + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GRIB driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GRIBDataset *poDS; + + poDS = new GRIBDataset(); + + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r" ); + + /* Check the return values */ + if (!poDS->fp) { + // we have no FP, so we don't have anywhere to read from + char * errMsg = errSprintf(NULL); + if( errMsg != NULL ) + CPLDebug( "GRIB", "%s", errMsg ); + free(errMsg); + + CPLError( CE_Failure, CPLE_OpenFailed, "Error (%d) opening file %s", errno, poOpenInfo->pszFilename); + CPLReleaseMutex(hGRIBMutex); // Release hGRIBMutex otherwise we'll deadlock with GDALDataset own hGRIBMutex + delete poDS; + CPLAcquireMutex(hGRIBMutex, 1000.0); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +/* Make an inventory of the GRIB file. */ +/* The inventory does not contain all the information needed for */ +/* creating the RasterBands (especially the x and y size), therefore */ +/* the first GRIB band is also read for some additional metadata. */ +/* The band-data that is read is stored into the first RasterBand, */ +/* simply so that the same portion of the file is not read twice. */ +/* -------------------------------------------------------------------- */ + + VSIFSeekL( poDS->fp, 0, SEEK_SET ); + + FileDataSource grib_fp (poDS->fp); + + inventoryType *Inv = NULL; /* Contains an GRIB2 message inventory of the file */ + uInt4 LenInv = 0; /* size of Inv (also # of GRIB2 messages) */ + int msgNum =0; /* The messageNumber during the inventory. */ + + if (GRIB2Inventory (grib_fp, &Inv, &LenInv, 0, &msgNum) <= 0 ) + { + char * errMsg = errSprintf(NULL); + if( errMsg != NULL ) + CPLDebug( "GRIB", "%s", errMsg ); + free(errMsg); + + CPLError( CE_Failure, CPLE_OpenFailed, + "%s is a grib file, but no raster dataset was successfully identified.", + poOpenInfo->pszFilename ); + CPLReleaseMutex(hGRIBMutex); // Release hGRIBMutex otherwise we'll deadlock with GDALDataset own hGRIBMutex + delete poDS; + CPLAcquireMutex(hGRIBMutex, 1000.0); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band objects. */ +/* -------------------------------------------------------------------- */ + GRIBRasterBand *gribBand; + for (uInt4 i = 0; i < LenInv; ++i) + { + uInt4 bandNr = i+1; + if (bandNr == 1) + { + // important: set DataSet extents before creating first RasterBand in it + double * data = NULL; + grib_MetaData* metaData; + GRIBRasterBand::ReadGribData(grib_fp, 0, Inv[i].subgNum, &data, &metaData); + if (data == 0 || metaData->gds.Nx < 1 || metaData->gds.Ny < 1) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s is a grib file, but no raster dataset was successfully identified.", + poOpenInfo->pszFilename ); + CPLReleaseMutex(hGRIBMutex); // Release hGRIBMutex otherwise we'll deadlock with GDALDataset own hGRIBMutex + delete poDS; + CPLAcquireMutex(hGRIBMutex, 1000.0); + return NULL; + } + + poDS->SetGribMetaData(metaData); // set the DataSet's x,y size, georeference and projection from the first GRIB band + gribBand = new GRIBRasterBand( poDS, bandNr, Inv+i); + + if( Inv->GribVersion == 2 ) + gribBand->FindPDSTemplate(); + + gribBand->m_Grib_Data = data; + gribBand->m_Grib_MetaData = metaData; + } + else + { + gribBand = new GRIBRasterBand( poDS, bandNr, Inv+i ); + if( CSLTestBoolean( CPLGetConfigOption( "GRIB_PDS_ALL_BANDS", "ON" ) ) ) + { + if( Inv->GribVersion == 2 ) + gribBand->FindPDSTemplate(); + } + } + poDS->SetBand( bandNr, gribBand); + GRIB2InventoryFree (Inv + i); + } + free (Inv); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + + CPLReleaseMutex(hGRIBMutex); // Release hGRIBMutex otherwise we'll deadlock with GDALDataset own hGRIBMutex + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + CPLAcquireMutex(hGRIBMutex, 1000.0); + + return( poDS ); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +void GRIBDataset::SetGribMetaData(grib_MetaData* meta) +{ + nRasterXSize = meta->gds.Nx; + nRasterYSize = meta->gds.Ny; + +/* -------------------------------------------------------------------- */ +/* Image projection. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + + switch(meta->gds.projType) + { + case GS3_LATLON: + case GS3_GAUSSIAN_LATLON: + // No projection, only latlon system (geographic) + break; + case GS3_MERCATOR: + oSRS.SetMercator(meta->gds.meshLat, meta->gds.orientLon, + 1.0, 0.0, 0.0); + break; + case GS3_POLAR: + oSRS.SetPS(meta->gds.meshLat, meta->gds.orientLon, + meta->gds.scaleLat1, + 0.0, 0.0); + break; + case GS3_LAMBERT: + oSRS.SetLCC(meta->gds.scaleLat1, meta->gds.scaleLat2, + meta->gds.meshLat, meta->gds.orientLon, + 0.0, 0.0); // set projection + break; + + + case GS3_ORTHOGRAPHIC: + + //oSRS.SetOrthographic(0.0, meta->gds.orientLon, + // meta->gds.lon2, meta->gds.lat2); + //oSRS.SetGEOS(meta->gds.orientLon, meta->gds.stretchFactor, meta->gds.lon2, meta->gds.lat2); + oSRS.SetGEOS( 0, 35785831, 0, 0 ); // hardcoded for now, I don't know yet how to parse the meta->gds section + break; + case GS3_EQUATOR_EQUIDIST: + break; + case GS3_AZIMUTH_RANGE: + break; + } + +/* -------------------------------------------------------------------- */ +/* Earth model */ +/* -------------------------------------------------------------------- */ + double a = meta->gds.majEarth * 1000.0; // in meters + double b = meta->gds.minEarth * 1000.0; + if( a == 0 && b == 0 ) + { + a = 6377563.396; + b = 6356256.910; + } + + if (meta->gds.f_sphere) + { + oSRS.SetGeogCS( "Coordinate System imported from GRIB file", + NULL, + "Sphere", + a, 0.0 ); + } + else + { + double fInv = a/(a-b); + oSRS.SetGeogCS( "Coordinate System imported from GRIB file", + NULL, + "Spheroid imported from GRIB file", + a, fInv ); + } + + OGRSpatialReference oLL; // construct the "geographic" part of oSRS + oLL.CopyGeogCSFrom( &oSRS ); + + double rMinX; + double rMaxY; + double rPixelSizeX; + double rPixelSizeY; + if (meta->gds.projType == GS3_ORTHOGRAPHIC) + { + //rMinX = -meta->gds.Dx * (meta->gds.Nx / 2); // This is what should work, but it doesn't .. Dx seems to have an inverse relation with pixel size + //rMaxY = meta->gds.Dy * (meta->gds.Ny / 2); + const double geosExtentInMeters = 11137496.552; // hardcoded for now, assumption: GEOS projection, full disc (like MSG) + rMinX = -(geosExtentInMeters / 2); + rMaxY = geosExtentInMeters / 2; + rPixelSizeX = geosExtentInMeters / meta->gds.Nx; + rPixelSizeY = geosExtentInMeters / meta->gds.Ny; + } + else if( oSRS.IsProjected() ) + { + rMinX = meta->gds.lon1; // longitude in degrees, to be transformed to meters (or degrees in case of latlon) + rMaxY = meta->gds.lat1; // latitude in degrees, to be transformed to meters + OGRCoordinateTransformation *poTransformLLtoSRS = OGRCreateCoordinateTransformation( &(oLL), &(oSRS) ); + if ((poTransformLLtoSRS != NULL) && poTransformLLtoSRS->Transform( 1, &rMinX, &rMaxY )) // transform it to meters + { + if (meta->gds.scan == GRIB2BIT_2) // Y is minY, GDAL wants maxY + rMaxY += (meta->gds.Ny - 1) * meta->gds.Dy; // -1 because we GDAL needs the coordinates of the centre of the pixel + rPixelSizeX = meta->gds.Dx; + rPixelSizeY = meta->gds.Dy; + } + else + { + rMinX = 0.0; + rMaxY = 0.0; + + rPixelSizeX = 1.0; + rPixelSizeY = -1.0; + + oSRS.Clear(); + + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to perform coordinate transformations, so the correct\n" + "projected geotransform could not be deduced from the lat/long\n" + "control points. Defaulting to ungeoreferenced." ); + } + delete poTransformLLtoSRS; + } + else + { + rMinX = meta->gds.lon1; // longitude in degrees, to be transformed to meters (or degrees in case of latlon) + rMaxY = meta->gds.lat1; // latitude in degrees, to be transformed to meters + + double rMinY = meta->gds.lat2; + if (meta->gds.lat2 > rMaxY) + { + rMaxY = meta->gds.lat2; + rMinY = meta->gds.lat1; + } + + if( meta->gds.Nx == 1 ) + rPixelSizeX = meta->gds.Dx; + else if (meta->gds.lon1 > meta->gds.lon2) + rPixelSizeX = (360.0 - (meta->gds.lon1 - meta->gds.lon2)) / (meta->gds.Nx - 1); + else + rPixelSizeX = (meta->gds.lon2 - meta->gds.lon1) / (meta->gds.Nx - 1); + + if( meta->gds.Ny == 1 ) + rPixelSizeY = meta->gds.Dy; + else + rPixelSizeY = (rMaxY - rMinY) / (meta->gds.Ny - 1); + + // Do some sanity checks for cases that can't be handled by the above + // pixel size corrections. GRIB1 has a minimum precision of 0.001 + // for latitudes and longitudes, so we'll allow a bit higher than that. + if (rPixelSizeX < 0 || fabs(rPixelSizeX - meta->gds.Dx) > 0.002) + rPixelSizeX = meta->gds.Dx; + + if (rPixelSizeY < 0 || fabs(rPixelSizeY - meta->gds.Dy) > 0.002) + rPixelSizeY = meta->gds.Dy; + } + + // http://gdal.org/gdal_datamodel.html : + // we need the top left corner of the top left pixel. + // At the moment we have the center of the pixel. + rMinX-=rPixelSizeX/2; + rMaxY+=rPixelSizeY/2; + + adfGeoTransform[0] = rMinX; + adfGeoTransform[3] = rMaxY; + adfGeoTransform[1] = rPixelSizeX; + adfGeoTransform[5] = -rPixelSizeY; + + CPLFree( pszProjection ); + pszProjection = NULL; + oSRS.exportToWkt( &(pszProjection) ); +} + +/************************************************************************/ +/* GDALDeregister_GRIB() */ +/************************************************************************/ + +static void GDALDeregister_GRIB(GDALDriver* ) +{ + if( hGRIBMutex != NULL ) + { + CPLDestroyMutex(hGRIBMutex); + hGRIBMutex = NULL; + } +} + +/************************************************************************/ +/* GDALRegister_GRIB() */ +/************************************************************************/ + +void GDALRegister_GRIB() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GRIB" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GRIB" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GRIdded Binary (.grb)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_grib.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "grb" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = GRIBDataset::Open; + poDriver->pfnIdentify = GRIBDataset::Identify; + poDriver->pfnUnloadDriver = GDALDeregister_GRIB; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/grib/makefile.vc b/bazaar/plugin/gdal/frmts/grib/makefile.vc new file mode 100644 index 000000000..5fe3622ea --- /dev/null +++ b/bazaar/plugin/gdal/frmts/grib/makefile.vc @@ -0,0 +1,32 @@ + +OBJ = gribdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IF DEFINED(PNG_LIB) +LINK_PNG = $(PNG_LIB) +!ELSE +LINK_PNG = ..\png\libpng\*.obj +!ENDIF + + +default: $(OBJ) + cd degrib18 && $(MAKE) /f makefile.vc && cd .. + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + -del degrib18\degrib\*.obj + -del degrib18\g2clib-1.0.4\*.obj + -del *.dll + -del *.exp + -del *.lib + +plugin: gdal_GRIB.dll + +gdal_GRIB.dll: default + link /dll $(LDEBUG) /out:gdal_GRIB.dll \ + $(OBJ) degrib18\degrib\*.obj degrib18\g2clib-1.0.4\*.obj \ + $(GDAL_ROOT)/gdal_i.lib $(LINK_PNG) ..\zlib\*.obj diff --git a/bazaar/plugin/gdal/frmts/gsg/GNUmakefile b/bazaar/plugin/gdal/frmts/gsg/GNUmakefile new file mode 100644 index 000000000..e21476c0d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gsg/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = gsagdataset.o gsbgdataset.o gs7bgdataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/gsg/gs7bgdataset.cpp b/bazaar/plugin/gdal/frmts/gsg/gs7bgdataset.cpp new file mode 100644 index 000000000..ee8d8d26f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gsg/gs7bgdataset.cpp @@ -0,0 +1,1378 @@ +/**************************************************************************** + * $Id: gs7bgdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: GDAL + * Purpose: Implements the Golden Software Surfer 7 Binary Grid Format. + * Author: Adam Guernsey, adam@ctech.com + * (Based almost entirely on gsbgdataset.cpp by Kevin Locke) + * Create functions added by Russell Jurgensen. + * + **************************************************************************** + * Copyright (c) 2007, Adam Guernsey + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include +#include +#include + +#include "gdal_pam.h" + +#ifndef DBL_MAX +# ifdef __DBL_MAX__ +# define DBL_MAX __DBL_MAX__ +# else +# define DBL_MAX 1.7976931348623157E+308 +# endif /* __DBL_MAX__ */ +#endif /* DBL_MAX */ + +#ifndef FLT_MAX +# ifdef __FLT_MAX__ +# define FLT_MAX __FLT_MAX__ +# else +# define FLT_MAX 3.40282347E+38F +# endif /* __FLT_MAX__ */ +#endif /* FLT_MAX */ + +#ifndef INT_MAX +# define INT_MAX 2147483647 +#endif /* INT_MAX */ + +#ifndef SHRT_MAX +# define SHRT_MAX 32767 +#endif /* SHRT_MAX */ + +CPL_CVSID("$Id: gs7bgdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_GS7BG(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* GS7BGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GS7BGRasterBand; + +class GS7BGDataset : public GDALPamDataset +{ + friend class GS7BGRasterBand; + + static double dfNoData_Value; + static const size_t nHEADER_SIZE; + static size_t nData_Position; + + static CPLErr WriteHeader( VSILFILE *fp, GInt32 nXSize, GInt32 nYSize, + double dfMinX, double dfMaxX, + double dfMinY, double dfMaxY, + double dfMinZ, double dfMaxZ ); + + VSILFILE *fp; + + public: + ~GS7BGDataset(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char **papszParmList ); + static GDALDataset *CreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressData ); + + CPLErr GetGeoTransform( double *padfGeoTransform ); + CPLErr SetGeoTransform( double *padfGeoTransform ); +}; + +/* NOTE: This is not mentioned in the spec, but Surfer 8 uses this value */ +/* 0x7effffee (Little Endian: eeffff7e) */ +double GS7BGDataset::dfNoData_Value = 1.701410009187828e+38f; + +const size_t GS7BGDataset::nHEADER_SIZE = 100; + +size_t GS7BGDataset::nData_Position = 0; + +const long nHEADER_TAG = 0x42525344; +const long nGRID_TAG = 0x44495247; +const long nDATA_TAG = 0x41544144; +#if 0 /* Unused */ +const long nFAULT_TAG = 0x49544c46; +#endif + +/************************************************************************/ +/* ==================================================================== */ +/* GS7BGRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GS7BGRasterBand : public GDALPamRasterBand +{ + friend class GS7BGDataset; + + double dfMinX; + double dfMaxX; + double dfMinY; + double dfMaxY; + double dfMinZ; + double dfMaxZ; + + double *pafRowMinZ; + double *pafRowMaxZ; + int nMinZRow; + int nMaxZRow; + + CPLErr ScanForMinMaxZ(); + + public: + + GS7BGRasterBand( GS7BGDataset *, int ); + ~GS7BGRasterBand(); + + CPLErr IReadBlock( int, int, void * ); + CPLErr IWriteBlock( int, int, void * ); + double GetMinimum( int *pbSuccess = NULL ); + double GetMaximum( int *pbSuccess = NULL ); + + double GetNoDataValue( int *pbSuccess = NULL ); +}; + +/************************************************************************/ +/* GS7BGRasterBand() */ +/************************************************************************/ + +GS7BGRasterBand::GS7BGRasterBand( GS7BGDataset *poDS, int nBand ) : + pafRowMinZ(NULL), + pafRowMaxZ(NULL), + nMinZRow(-1), + nMaxZRow(-1) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Float64; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* ~GSBGRasterBand() */ +/************************************************************************/ + +GS7BGRasterBand::~GS7BGRasterBand( ) + +{ + if( pafRowMinZ != NULL ) + CPLFree( pafRowMinZ ); + if( pafRowMaxZ != NULL ) + CPLFree( pafRowMaxZ ); +} + +/************************************************************************/ +/* ScanForMinMaxZ() */ +/************************************************************************/ + +CPLErr GS7BGRasterBand::ScanForMinMaxZ() + +{ + double *pafRowVals = (double *)VSIMalloc2( nRasterXSize, sizeof(double)); + + if( pafRowVals == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate row buffer to scan grid file.\n" ); + return CE_Failure; + } + + double dfNewMinZ = DBL_MAX; + double dfNewMaxZ = -DBL_MAX; + int nNewMinZRow = 0; + int nNewMaxZRow = 0; + + /* Since we have to scan, lets calc. statistics too */ + double dfSum = 0.0; + double dfSum2 = 0.0; + unsigned long nValuesRead = 0; + for( int iRow=0; iRow pafRowMinZ[iRow] ) + pafRowMaxZ[iRow] = pafRowVals[iCol]; + + dfSum += pafRowVals[iCol]; + dfSum2 += pafRowVals[iCol] * pafRowVals[iCol]; + nValuesRead++; + } + + if( pafRowMinZ[iRow] < dfNewMinZ ) + { + dfNewMinZ = pafRowMinZ[iRow]; + nNewMinZRow = iRow; + } + + if( pafRowMaxZ[iRow] > dfNewMaxZ ) + { + dfNewMaxZ = pafRowMaxZ[iRow]; + nNewMaxZRow = iRow; + } + } + + VSIFree( pafRowVals ); + + if( nValuesRead == 0 ) + { + dfMinZ = 0.0; + dfMaxZ = 0.0; + nMinZRow = 0; + nMaxZRow = 0; + return CE_None; + } + + dfMinZ = dfNewMinZ; + dfMaxZ = dfNewMaxZ; + nMinZRow = nNewMinZRow; + nMaxZRow = nNewMaxZRow; + + double dfMean = dfSum / nValuesRead; + double dfStdDev = sqrt((dfSum2 / nValuesRead) - (dfMean * dfMean)); + SetStatistics( dfMinZ, dfMaxZ, dfMean, dfStdDev ); + + return CE_None; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GS7BGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + if( nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0 ) + return CE_Failure; + + GS7BGDataset *poGDS = (GS7BGDataset *) ( poDS ); + + if( VSIFSeekL( poGDS->fp, + ( GS7BGDataset::nData_Position + + sizeof(double) * nRasterXSize * (nRasterYSize - nBlockYOff - 1) ), + SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to beginning of grid row.\n" ); + return CE_Failure; + } + + if( VSIFReadL( pImage, sizeof(double), nBlockXSize, + poGDS->fp ) != static_cast(nBlockXSize) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read block from grid file.\n" ); + return CE_Failure; + } + +#ifdef CPL_MSB + double *pfImage = (double *)pImage; + for( int iPixel=0; iPixel nRasterYSize - 1 || nBlockXOff != 0 ) + return CE_Failure; + + GS7BGDataset *poGDS = (GS7BGDataset *) ( poDS ); + + if( pafRowMinZ == NULL || pafRowMaxZ == NULL + || nMinZRow < 0 || nMaxZRow < 0 ) + { + pafRowMinZ = (double *)VSIMalloc2( nRasterYSize,sizeof(double) ); + if( pafRowMinZ == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate space for row minimums array.\n" ); + return CE_Failure; + } + + pafRowMaxZ = (double *)VSIMalloc2( nRasterYSize,sizeof(double) ); + if( pafRowMaxZ == NULL ) + { + VSIFree( pafRowMinZ ); + pafRowMinZ = NULL; + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate space for row maximums array.\n" ); + return CE_Failure; + } + + CPLErr eErr = ScanForMinMaxZ(); + if( eErr != CE_None ) + return eErr; + } + + if( VSIFSeekL( poGDS->fp, + GS7BGDataset::nHEADER_SIZE + + sizeof(double) * nRasterXSize * (nRasterYSize - nBlockYOff - 1), + SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to beginning of grid row.\n" ); + return CE_Failure; + } + + double *pdfImage = (double *)pImage; + pafRowMinZ[nBlockYOff] = DBL_MAX; + pafRowMaxZ[nBlockYOff] = -DBL_MAX; + for( int iPixel=0; iPixel pafRowMaxZ[nBlockYOff] ) + pafRowMaxZ[nBlockYOff] = pdfImage[iPixel]; + } + + CPL_LSBPTR64( pdfImage+iPixel ); + } + + if( VSIFWriteL( pImage, sizeof(double), nBlockXSize, + poGDS->fp ) != static_cast(nBlockXSize) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write block to grid file.\n" ); + return CE_Failure; + } + + /* Update min/max Z values as appropriate */ + bool bHeaderNeedsUpdate = false; + if( nMinZRow == nBlockYOff && pafRowMinZ[nBlockYOff] > dfMinZ ) + { + double dfNewMinZ = DBL_MAX; + for( int iRow=0; iRow dfNewMaxZ ) + { + dfNewMaxZ = pafRowMaxZ[iRow]; + nMaxZRow = iRow; + } + } + + if( dfNewMaxZ != dfMaxZ ) + { + dfMaxZ = dfNewMaxZ; + bHeaderNeedsUpdate = true; + } + } + + if( pafRowMinZ[nBlockYOff] < dfMinZ || pafRowMaxZ[nBlockYOff] > dfMaxZ ) + { + if( pafRowMinZ[nBlockYOff] < dfMinZ ) + { + dfMinZ = pafRowMinZ[nBlockYOff]; + nMinZRow = nBlockYOff; + } + + if( pafRowMaxZ[nBlockYOff] > dfMaxZ ) + { + dfMaxZ = pafRowMaxZ[nBlockYOff]; + nMaxZRow = nBlockYOff; + } + + bHeaderNeedsUpdate = true; + } + + if( bHeaderNeedsUpdate && dfMaxZ > dfMinZ ) + { + CPLErr eErr = poGDS->WriteHeader( poGDS->fp, + nRasterXSize, nRasterYSize, + dfMinX, dfMaxX, + dfMinY, dfMaxY, + dfMinZ, dfMaxZ ); + return eErr; + } + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double GS7BGRasterBand::GetNoDataValue( int * pbSuccess ) +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + return GS7BGDataset::dfNoData_Value; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double GS7BGRasterBand::GetMinimum( int *pbSuccess ) +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + return dfMinZ; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double GS7BGRasterBand::GetMaximum( int *pbSuccess ) +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + return dfMaxZ; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GS7BGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +GS7BGDataset::~GS7BGDataset() + +{ + FlushCache(); + if( fp != NULL ) + VSIFCloseL( fp ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int GS7BGDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + /* Check for signature - for GS7BG the signature is the */ + /* nHEADER_TAG with reverse byte order. */ + if( poOpenInfo->nHeaderBytes < 4 + || !EQUALN((const char *) poOpenInfo->pabyHeader,"DSRB",4) ) + { + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GS7BGDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify(poOpenInfo) ) + { + return NULL; + } + + /* ------------------------------------------------------------------- */ + /* Create a corresponding GDALDataset. */ + /* ------------------------------------------------------------------- */ + GS7BGDataset *poDS = new GS7BGDataset(); + + /* ------------------------------------------------------------------- */ + /* Open file with large file API. */ + /* ------------------------------------------------------------------- */ + poDS->eAccess = poOpenInfo->eAccess; + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" ); + + if( poDS->fp == NULL ) + { + delete poDS; + CPLError( CE_Failure, CPLE_OpenFailed, + "VSIFOpenL(%s) failed unexpectedly.", + poOpenInfo->pszFilename ); + return NULL; + } + + /* ------------------------------------------------------------------- */ + /* Read the header. The Header section must be the first section */ + /* in the file. */ + /* ------------------------------------------------------------------- */ + if( VSIFSeekL( poDS->fp, 0, SEEK_SET ) != 0 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to start of grid file header.\n" ); + return NULL; + } + + GInt32 nTag; + GInt32 nSize; + GInt32 nVersion; + + if( VSIFReadL( (void *)&nTag, sizeof(GInt32), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, "Unable to read Tag.\n" ); + return NULL; + } + + CPL_LSBPTR32( &nTag ); + + if(nTag != nHEADER_TAG) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, "Header tag not found.\n" ); + return NULL; + } + + if( VSIFReadL( (void *)&nSize, sizeof(GInt32), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read file section size.\n" ); + return NULL; + } + + CPL_LSBPTR32( &nSize ); + + if( VSIFReadL( (void *)&nVersion, sizeof(GInt32), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read file version.\n" ); + return NULL; + } + + CPL_LSBPTR32( &nVersion ); + + if(nVersion != 1 && nVersion != 2) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Incorrect file version (%d).", nVersion ); + return NULL; + } + + // advance until the grid tag is found + while(nTag != nGRID_TAG) + { + if( VSIFReadL( (void *)&nTag, sizeof(GInt32), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, "Unable to read Tag.\n" ); + return NULL; + } + + CPL_LSBPTR32( &nTag ); + + if( VSIFReadL( (void *)&nSize, sizeof(GInt32), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read file section size.\n" ); + return NULL; + } + + CPL_LSBPTR32( &nSize ); + + if(nTag != nGRID_TAG) + { + if( VSIFSeekL( poDS->fp, nSize, SEEK_SET ) != 0 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to end of file section.\n" ); + return NULL; + } + } + } + + /* --------------------------------------------------------------------*/ + /* Read the grid. */ + /* --------------------------------------------------------------------*/ + /* Parse number of Y axis grid rows */ + GInt32 nRows; + if( VSIFReadL( (void *)&nRows, sizeof(GInt32), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read raster Y size.\n" ); + return NULL; + } + CPL_LSBPTR32( &nRows ); + poDS->nRasterYSize = nRows; + + /* Parse number of X axis grid columns */ + GInt32 nCols; + if( VSIFReadL( (void *)&nCols, sizeof(GInt32), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read raster X size.\n" ); + return NULL; + } + CPL_LSBPTR32( &nCols ); + poDS->nRasterXSize = nCols; + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + + /* --------------------------------------------------------------------*/ + /* Create band information objects. */ + /* --------------------------------------------------------------------*/ + GS7BGRasterBand *poBand = new GS7BGRasterBand( poDS, 1 ); + + // find the min X Value of the grid + double dfTemp; + if( VSIFReadL( (void *)&dfTemp, sizeof(double), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read minimum X value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poBand->dfMinX = dfTemp; + + // find the min Y value of the grid + if( VSIFReadL( (void *)&dfTemp, sizeof(double), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read minimum X value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poBand->dfMinY = dfTemp; + + // find the spacing between adjacent nodes in the X direction + // (between columns) + if( VSIFReadL( (void *)&dfTemp, sizeof(double), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read spacing in X value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poBand->dfMaxX = poBand->dfMinX + (dfTemp * (nCols - 1)); + + // find the spacing between adjacent nodes in the Y direction + // (between rows) + if( VSIFReadL( (void *)&dfTemp, sizeof(double), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read spacing in Y value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poBand->dfMaxY = poBand->dfMinY + (dfTemp * (nRows - 1)); + + // set the z min + if( VSIFReadL( (void *)&dfTemp, sizeof(double), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read Z min value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poBand->dfMinZ = dfTemp; + + // set the z max + if( VSIFReadL( (void *)&dfTemp, sizeof(double), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read Z max value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poBand->dfMaxZ = dfTemp; + poDS->SetBand( 1, poBand ); + + // read and ignore the rotation value + //(This is not used in the current version). + if( VSIFReadL( (void *)&dfTemp, sizeof(double), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read rotation value.\n" ); + return NULL; + } + + // read and set the cell blank value + if( VSIFReadL( (void *)&dfTemp, sizeof(double), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to Blank value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poDS->dfNoData_Value = dfTemp; + + /* --------------------------------------------------------------------*/ + /* Set the current offset of the grid data. */ + /* --------------------------------------------------------------------*/ + if( VSIFReadL( (void *)&nTag, sizeof(GInt32), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, "Unable to read Tag.\n" ); + return NULL; + } + + CPL_LSBPTR32( &nTag ); + if(nTag != nDATA_TAG) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, "Data tag not found.\n" ); + return NULL; + } + + if( VSIFReadL( (void *)&nSize, sizeof(GInt32), 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to data section size.\n" ); + return NULL; + } + + poDS->nData_Position = (size_t) VSIFTellL(poDS->fp); + + /* --------------------------------------------------------------------*/ + /* Initialize any PAM information. */ + /* --------------------------------------------------------------------*/ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return poDS; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GS7BGDataset::GetGeoTransform( double *padfGeoTransform ) +{ + if( padfGeoTransform == NULL ) + return CE_Failure; + + GS7BGRasterBand *poGRB = (GS7BGRasterBand *)GetRasterBand( 1 ); + + if( poGRB == NULL ) + { + padfGeoTransform[0] = 0; + padfGeoTransform[1] = 1; + padfGeoTransform[2] = 0; + padfGeoTransform[3] = 0; + padfGeoTransform[4] = 0; + padfGeoTransform[5] = 1; + return CE_Failure; + } + + /* check if we have a PAM GeoTransform stored */ + CPLPushErrorHandler( CPLQuietErrorHandler ); + CPLErr eErr = GDALPamDataset::GetGeoTransform( padfGeoTransform ); + CPLPopErrorHandler(); + + if( eErr == CE_None ) + return CE_None; + + /* calculate pixel size first */ + padfGeoTransform[1] = (poGRB->dfMaxX - poGRB->dfMinX)/(nRasterXSize - 1); + padfGeoTransform[5] = (poGRB->dfMinY - poGRB->dfMaxY)/(nRasterYSize - 1); + + /* then calculate image origin */ + padfGeoTransform[0] = poGRB->dfMinX - padfGeoTransform[1] / 2; + padfGeoTransform[3] = poGRB->dfMaxY - padfGeoTransform[5] / 2; + + /* tilt/rotation does not supported by the GS grids */ + padfGeoTransform[4] = 0.0; + padfGeoTransform[2] = 0.0; + + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr GS7BGDataset::SetGeoTransform( double *padfGeoTransform ) +{ + if( eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Unable to set GeoTransform, dataset opened read only.\n" ); + return CE_Failure; + } + + GS7BGRasterBand *poGRB = dynamic_cast(GetRasterBand( 1 )); + + if( poGRB == NULL || padfGeoTransform == NULL) + return CE_Failure; + + /* non-zero transform 2 or 4 or negative 1 or 5 not supported natively */ + CPLErr eErr = CE_None; + /*if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0 + || padfGeoTransform[1] < 0.0 || padfGeoTransform[5] < 0.0 ) + eErr = GDALPamDataset::SetGeoTransform( padfGeoTransform ); + + if( eErr != CE_None ) + return eErr;*/ + + double dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2; + double dfMaxX = padfGeoTransform[1] * (nRasterXSize - 0.5) + padfGeoTransform[0]; + double dfMinY = padfGeoTransform[5] * (nRasterYSize - 0.5) + padfGeoTransform[3]; + double dfMaxY = padfGeoTransform[3] + padfGeoTransform[5] / 2; + + eErr = WriteHeader( fp, poGRB->nRasterXSize, poGRB->nRasterYSize, + dfMinX, dfMaxX, dfMinY, dfMaxY, + poGRB->dfMinZ, poGRB->dfMaxZ ); + + if( eErr == CE_None ) + { + poGRB->dfMinX = dfMinX; + poGRB->dfMaxX = dfMaxX; + poGRB->dfMinY = dfMinY; + poGRB->dfMaxY = dfMaxY; + } + + return eErr; +} + +/************************************************************************/ +/* WriteHeader() */ +/************************************************************************/ + +CPLErr GS7BGDataset::WriteHeader( VSILFILE *fp, GInt32 nXSize, GInt32 nYSize, + double dfMinX, double dfMaxX, + double dfMinY, double dfMaxY, + double dfMinZ, double dfMaxZ ) + +{ + if( VSIFSeekL( fp, 0, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to start of grid file.\n" ); + return CE_Failure; + } + + GInt32 nTemp = CPL_LSBWORD32(nHEADER_TAG); + if( VSIFWriteL( (void *)&nTemp, sizeof(GInt32), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write header tag to grid file.\n" ); + return CE_Failure; + } + + nTemp = CPL_LSBWORD32(sizeof(GInt32)); // Size of version section. + if( VSIFWriteL( (void *)&nTemp, sizeof(GInt32), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write size to grid file.\n" ); + return CE_Failure; + } + + nTemp = CPL_LSBWORD32(1); // Version + if( VSIFWriteL( (void *)&nTemp, sizeof(GInt32), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write size to grid file.\n" ); + return CE_Failure; + } + + nTemp = CPL_LSBWORD32(nGRID_TAG); // Mark start of grid + if( VSIFWriteL( (void *)&nTemp, sizeof(GInt32), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write size to grid file.\n" ); + return CE_Failure; + } + + nTemp = CPL_LSBWORD32(72); // Grid info size (the remainder of the header) + if( VSIFWriteL( (void *)&nTemp, sizeof(GInt32), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write size to grid file.\n" ); + return CE_Failure; + } + + nTemp = CPL_LSBWORD32(nYSize); + if( VSIFWriteL( (void *)&nTemp, sizeof(GInt32), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write Y size to grid file.\n" ); + return CE_Failure; + } + + nTemp = CPL_LSBWORD32(nXSize); + if( VSIFWriteL( (void *)&nTemp, sizeof(GInt32), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write X size to grid file.\n" ); + return CE_Failure; + } + + double dfTemp = dfMinX; + CPL_LSBPTR64(&dfTemp); + if( VSIFWriteL( (void *)&dfTemp, sizeof(double), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write minimum X value to grid file.\n" ); + return CE_Failure; + } + + dfTemp = dfMinY; + CPL_LSBPTR64( &dfTemp ); + if( VSIFWriteL( (void *)&dfTemp, sizeof(double), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write minimum Y value to grid file.\n" ); + return CE_Failure; + } + + // Write node spacing in x direction + dfTemp = (dfMaxX - dfMinX) / (nXSize - 1); + CPL_LSBPTR64(&dfTemp); + if( VSIFWriteL( (void *)&dfTemp, sizeof(double), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write spacing in X value.\n" ); + return CE_Failure; + } + + // Write node spacing in y direction + dfTemp = (dfMaxY - dfMinY) / (nYSize -1); + CPL_LSBPTR64( &dfTemp ); + if( VSIFWriteL( (void *)&dfTemp, sizeof(double), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write spacing in Y value.\n" ); + return CE_Failure; + } + + dfTemp = dfMinZ; + CPL_LSBPTR64( &dfTemp ); + if( VSIFWriteL( (void *)&dfTemp, sizeof(double), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write minimum Z value to grid file.\n" ); + return CE_Failure; + } + + dfTemp = dfMaxZ; + CPL_LSBPTR64( &dfTemp ); + if( VSIFWriteL( (void *)&dfTemp, sizeof(double), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write maximum Z value to grid file.\n" ); + return CE_Failure; + } + + dfTemp = 0; // Rotation value is zero + CPL_LSBPTR64( &dfTemp ); + if( VSIFWriteL( (void *)&dfTemp, sizeof(double), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write rotation value to grid file.\n" ); + return CE_Failure; + } + + dfTemp = dfNoData_Value; + CPL_LSBPTR64( &dfTemp ); + if( VSIFWriteL( (void *)&dfTemp, sizeof(double), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write cell blank value to grid file.\n" ); + return CE_Failure; + } + + // Only supports 1 band so go ahead and write band info here + nTemp = CPL_LSBWORD32(nDATA_TAG); // Mark start of data + if( VSIFWriteL( (void *)&nTemp, sizeof(GInt32), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to data tag to grid file.\n" ); + return CE_Failure; + } + + int nSize = nXSize * nYSize * sizeof(double); + nTemp = CPL_LSBWORD32(nSize); // Mark size of data + if( VSIFWriteL( (void *)&nTemp, sizeof(GInt32), 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write data size to grid file.\n" ); + return CE_Failure; + } + + + return CE_None; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *GS7BGDataset::Create( const char * pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + CPL_UNUSED char **papszParmList ) + +{ + if( nXSize <= 0 || nYSize <= 0 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Unable to create grid, both X and Y size must be " + "non-negative.\n" ); + + return NULL; + } + + if( eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 + && eType != GDT_Int16 && eType != GDT_Float64) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GS7BG Grid only supports Byte, Int16, " + "Uint16, Float32, and Float64 datatypes. Unable to create with " + "type %s.\n", GDALGetDataTypeName( eType ) ); + + return NULL; + } + + if (nBands > 1) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to create copy, " + "format only supports one raster band.\n" ); + return NULL; + } + + VSILFILE *fp = VSIFOpenL( pszFilename, "w+b" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file '%s' failed.\n", + pszFilename ); + return NULL; + } + + CPLErr eErr = WriteHeader( fp, nXSize, nYSize, + 0.0, nXSize, 0.0, nYSize, 0.0, 0.0 ); + if( eErr != CE_None ) + { + VSIFCloseL( fp ); + return NULL; + } + + double dfVal = dfNoData_Value; + CPL_LSBPTR64( &dfVal ); + for( int iRow = 0; iRow < nYSize; iRow++ ) + { + for( int iCol=0; iColGetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Driver does not support source dataset with zero band.\n"); + return NULL; + } + else if (nBands > 1) + { + if( bStrict ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to create copy, " + "format only supports one raster band.\n" ); + return NULL; + } + else + CPLError( CE_Warning, CPLE_NotSupported, + "Format only supports one " + "raster band, first band will be copied.\n" ); + } + + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( 1 ); + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated\n" ); + return NULL; + } + + VSILFILE *fp = VSIFOpenL( pszFilename, "w+b" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file '%s' failed.\n", + pszFilename ); + return NULL; + } + + GInt32 nXSize = poSrcBand->GetXSize(); + GInt32 nYSize = poSrcBand->GetYSize(); + double adfGeoTransform[6]; + + poSrcDS->GetGeoTransform( adfGeoTransform ); + + double dfMinX = adfGeoTransform[0] + adfGeoTransform[1] / 2; + double dfMaxX = adfGeoTransform[1] * (nXSize - 0.5) + adfGeoTransform[0]; + double dfMinY = adfGeoTransform[5] * (nYSize - 0.5) + adfGeoTransform[3]; + double dfMaxY = adfGeoTransform[3] + adfGeoTransform[5] / 2; + CPLErr eErr = WriteHeader( fp, nXSize, nYSize, + dfMinX, dfMaxX, dfMinY, dfMaxY, 0.0, 0.0 ); + + if( eErr != CE_None ) + { + VSIFCloseL( fp ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Copy band data. */ +/* -------------------------------------------------------------------- */ + double *pfData = (double *)VSIMalloc2( nXSize, sizeof( double ) ); + if( pfData == NULL ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to create copy, unable to allocate line buffer.\n" ); + return NULL; + } + + int bSrcHasNDValue; + double dfSrcNoDataValue = poSrcBand->GetNoDataValue( &bSrcHasNDValue ); + double dfMinZ = DBL_MAX; + double dfMaxZ = -DBL_MAX; + for( GInt32 iRow = nYSize - 1; iRow >= 0; iRow-- ) + { + eErr = poSrcBand->RasterIO( GF_Read, 0, iRow, + nXSize, 1, pfData, + nXSize, 1, GDT_Float64, 0, 0, NULL ); + + if( eErr != CE_None ) + { + VSIFCloseL( fp ); + VSIFree( pfData ); + return NULL; + } + + for( int iCol=0; iCol dfMaxZ ) + dfMaxZ = pfData[iCol]; + + if( pfData[iCol] < dfMinZ ) + dfMinZ = pfData[iCol]; + } + + CPL_LSBPTR32( pfData+iCol ); + } + + if( VSIFWriteL( (void *)pfData, sizeof( double ), nXSize, + fp ) != static_cast(nXSize) ) + { + VSIFCloseL( fp ); + VSIFree( pfData ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write grid row. Disk full?\n" ); + return NULL; + } + + if( !pfnProgress( static_cast(nYSize - iRow)/nYSize, + NULL, pProgressData ) ) + { + VSIFCloseL( fp ); + VSIFree( pfData ); + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return NULL; + } + } + + VSIFree( pfData ); + + /* write out the min and max values */ + eErr = WriteHeader( fp, nXSize, nYSize, + dfMinX, dfMaxX, dfMinY, dfMaxY, dfMinZ, dfMaxZ ); + + if( eErr != CE_None ) + { + VSIFCloseL( fp ); + return NULL; + } + + VSIFCloseL( fp ); + + GDALPamDataset *poDS = (GDALPamDataset *)GDALOpen( pszFilename, + GA_Update ); + if (poDS) + { + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + } + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_GS7BG() */ +/************************************************************************/ +void GDALRegister_GS7BG() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GS7BG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GS7BG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Golden Software 7 Binary Grid (.grd)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#GS7BG" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "grd" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Float32 Float64" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = GS7BGDataset::Identify; + poDriver->pfnOpen = GS7BGDataset::Open; + poDriver->pfnCreate = GS7BGDataset::Create; + poDriver->pfnCreateCopy = GS7BGDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/gsg/gsagdataset.cpp b/bazaar/plugin/gdal/frmts/gsg/gsagdataset.cpp new file mode 100644 index 000000000..1790276bb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gsg/gsagdataset.cpp @@ -0,0 +1,1774 @@ +/****************************************************************************** + * $Id: gsagdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: GDAL + * Purpose: Implements the Golden Software ASCII Grid Format. + * Author: Kevin Locke, kwl7@cornell.edu + * (Based largely on aaigriddataset.cpp by Frank Warmerdam) + * + ****************************************************************************** + * Copyright (c) 2006, Kevin Locke + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" + +#include +#include +#include +#include + +#include "gdal_pam.h" + +#ifndef DBL_MAX +# ifdef __DBL_MAX__ +# define DBL_MAX __DBL_MAX__ +# else +# define DBL_MAX 1.7976931348623157E+308 +# endif /* __DBL_MAX__ */ +#endif /* DBL_MAX */ + +#ifndef INT_MAX +# define INT_MAX 2147483647 +#endif /* INT_MAX */ + +CPL_CVSID("$Id: gsagdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_GSAG(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* GSAGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GSAGRasterBand; + +class GSAGDataset : public GDALPamDataset +{ + friend class GSAGRasterBand; + + static const double dfNODATA_VALUE; + static const int nFIELD_PRECISION; + static const size_t nMAX_HEADER_SIZE; + + static CPLErr ShiftFileContents( VSILFILE *, vsi_l_offset, int, const char * ); + + VSILFILE *fp; + size_t nMinMaxZOffset; + char szEOL[3]; + + CPLErr UpdateHeader(); + + public: + GSAGDataset( const char *pszEOL = "\x0D\x0A" ); + ~GSAGDataset(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *CreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressData ); + + CPLErr GetGeoTransform( double *padfGeoTransform ); + CPLErr SetGeoTransform( double *padfGeoTransform ); +}; + +/* NOTE: This is not mentioned in the spec, but Surfer 8 uses this value */ +const double GSAGDataset::dfNODATA_VALUE = 1.70141E+38; + +const int GSAGDataset::nFIELD_PRECISION = 14; +const size_t GSAGDataset::nMAX_HEADER_SIZE = 200; + +/************************************************************************/ +/* ==================================================================== */ +/* GSAGRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GSAGRasterBand : public GDALPamRasterBand +{ + friend class GSAGDataset; + + double dfMinX; + double dfMaxX; + double dfMinY; + double dfMaxY; + double dfMinZ; + double dfMaxZ; + + vsi_l_offset *panLineOffset; + int nLastReadLine; + + double *padfRowMinZ; + double *padfRowMaxZ; + int nMinZRow; + int nMaxZRow; + + CPLErr ScanForMinMaxZ(); + + public: + + GSAGRasterBand( GSAGDataset *, int, vsi_l_offset ); + ~GSAGRasterBand(); + + CPLErr IReadBlock( int, int, void * ); + CPLErr IWriteBlock( int, int, void * ); + + double GetNoDataValue( int *pbSuccess = NULL ); + double GetMinimum( int *pbSuccess = NULL ); + double GetMaximum( int *pbSuccess = NULL ); +}; + +/************************************************************************/ +/* AlmostEqual() */ +/* This function is needed because in release mode "1.70141E+38" is not */ +/* parsed as 1.70141E+38 in the last bit of the mantissa. */ +/* See http://gcc.gnu.org/ml/gcc/2003-08/msg01195.html for some */ +/* explanation. */ +/************************************************************************/ + +bool AlmostEqual( double dfVal1, double dfVal2 ) + +{ + const double dfTOLERANCE = 0.0000000001; + if( dfVal1 == 0.0 || dfVal2 == 0.0 ) + return fabs(dfVal1 - dfVal2) < dfTOLERANCE; + return fabs((dfVal1 - dfVal2)/dfVal1) < dfTOLERANCE; +} + +/************************************************************************/ +/* GSAGRasterBand() */ +/************************************************************************/ + +GSAGRasterBand::GSAGRasterBand( GSAGDataset *poDS, int nBand, + vsi_l_offset nDataStart ) : + padfRowMinZ(NULL), + padfRowMaxZ(NULL), + nMinZRow(-1), + nMaxZRow(-1) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Float64; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + panLineOffset = + (vsi_l_offset *)VSICalloc( poDS->nRasterYSize+1, sizeof(vsi_l_offset) ); + if( panLineOffset == NULL ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "GSAGRasterBand::GSAGRasterBand : Out of memory allocating %d * %d bytes", + (int) poDS->nRasterYSize+1, (int) sizeof(vsi_l_offset) ); + return; + } + + panLineOffset[poDS->nRasterYSize-1] = nDataStart; + nLastReadLine = poDS->nRasterYSize; +} + +/************************************************************************/ +/* ~GSAGRasterBand() */ +/************************************************************************/ + +GSAGRasterBand::~GSAGRasterBand() +{ + CPLFree( panLineOffset ); + if( padfRowMinZ != NULL ) + CPLFree( padfRowMinZ ); + if( padfRowMaxZ != NULL ) + CPLFree( padfRowMaxZ ); +} + +/************************************************************************/ +/* ScanForMinMaxZ() */ +/************************************************************************/ + +CPLErr GSAGRasterBand::ScanForMinMaxZ() + +{ + double *padfRowValues = (double *)VSIMalloc2( nBlockXSize, sizeof(double) ); + if( padfRowValues == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate memory for grid row values.\n" ); + return CE_Failure; + } + + double dfNewMinZ = DBL_MAX; + double dfNewMaxZ = -DBL_MAX; + int nNewMinZRow = 0; + int nNewMaxZRow = 0; + + /* Since we have to scan, lets calc. statistics too */ + double dfSum = 0.0; + double dfSum2 = 0.0; + unsigned long nValuesRead = 0; + for( int iRow=0; iRow padfRowMaxZ[iRow] ) + padfRowMaxZ[iRow] = padfRowValues[iCell]; + + dfSum += padfRowValues[iCell]; + dfSum2 += padfRowValues[iCell] * padfRowValues[iCell]; + nValuesRead++; + } + + if( padfRowMinZ[iRow] < dfNewMinZ ) + { + dfNewMinZ = padfRowMinZ[iRow]; + nNewMinZRow = iRow; + } + + if( padfRowMaxZ[iRow] > dfNewMaxZ ) + { + dfNewMaxZ = padfRowMaxZ[iRow]; + nNewMaxZRow = iRow; + } + } + + VSIFree( padfRowValues ); + + if( nValuesRead == 0 ) + { + dfMinZ = 0.0; + dfMaxZ = 0.0; + nMinZRow = 0; + nMaxZRow = 0; + return CE_None; + } + + dfMinZ = dfNewMinZ; + dfMaxZ = dfNewMaxZ; + nMinZRow = nNewMinZRow; + nMaxZRow = nNewMaxZRow; + + double dfMean = dfSum / nValuesRead; + double dfStdDev = sqrt((dfSum2 / nValuesRead) - (dfMean * dfMean)); + SetStatistics( dfMinZ, dfMaxZ, dfMean, dfStdDev ); + + return CE_None; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GSAGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + static size_t nMaxLineSize = 128; + double *pdfImage = (double *)pImage; + GSAGDataset *poGDS = (GSAGDataset *)poDS; + + assert( poGDS != NULL ); + + if( nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0 ) + return CE_Failure; + + if( panLineOffset[nBlockYOff] == 0 ) + { + // Discover the last read block + for ( int iFoundLine = nLastReadLine - 1; iFoundLine > nBlockYOff; iFoundLine--) + { + if( IReadBlock( nBlockXOff, iFoundLine, NULL) != CE_None ) + return CE_Failure; + } + } + + if( panLineOffset[nBlockYOff] == 0 ) + return CE_Failure; + if( VSIFSeekL( poGDS->fp, panLineOffset[nBlockYOff], SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't seek to offset %ld to read grid row %d.", + (long) panLineOffset[nBlockYOff], nBlockYOff ); + return CE_Failure; + } + + size_t nLineBufSize; + char *szLineBuf = NULL; + size_t nCharsRead; + size_t nCharsExamined = 0; + /* If we know the offsets, we can just read line directly */ + if( (nBlockYOff > 0) && ( panLineOffset[nBlockYOff-1] != 0 ) ) + { + assert(panLineOffset[nBlockYOff-1] > panLineOffset[nBlockYOff]); + nLineBufSize = (size_t) (panLineOffset[nBlockYOff-1] + - panLineOffset[nBlockYOff] + 1); + } + else + { + nLineBufSize = nMaxLineSize; + } + + szLineBuf = (char *)VSIMalloc( nLineBufSize ); + if( szLineBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to read block, unable to allocate line buffer.\n" ); + return CE_Failure; + } + + nCharsRead = VSIFReadL( szLineBuf, 1, nLineBufSize - 1, poGDS->fp ); + if( nCharsRead == 0 ) + { + VSIFree( szLineBuf ); + CPLError( CE_Failure, CPLE_FileIO, + "Can't read grid row %d at offset %ld.\n", + nBlockYOff, (long) panLineOffset[nBlockYOff] ); + return CE_Failure; + } + szLineBuf[nCharsRead] = '\0'; + + char *szStart = szLineBuf; + char *szEnd = szStart; + for( int iCell=0; iCellfp, + VSIFTellL( poGDS->fp)-1, + SEEK_SET ) != 0 ) + { + VSIFree( szLineBuf ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek in grid row %d " + "(offset %ld, seek %d).\n", + nBlockYOff, + (long) VSIFTellL(poGDS->fp), + -1 ); + + return CE_Failure; + } + } + else if( *szStart != '\0' ) + { + szEnd = szStart; + while( !isspace( (unsigned char)*szEnd ) && *szEnd != '\0' ) + szEnd++; + char cOldEnd = *szEnd; + *szEnd = '\0'; + + CPLError( CE_Warning, CPLE_FileIO, + "Unexpected value in grid row %d (expected floating " + "point value, found \"%s\").\n", + nBlockYOff, szStart ); + + *szEnd = cOldEnd; + + szEnd = szStart; + while( !isdigit( *szEnd ) && *szEnd != '.' && *szEnd != '\0' ) + szEnd++; + + continue; + } + else if( static_cast(szStart - szLineBuf) != nCharsRead ) + { + CPLError( CE_Warning, CPLE_FileIO, + "Unexpected ASCII null-character in grid row %d at " + "offset %ld.\n", + nBlockYOff, + (long) (szStart - szLineBuf) ); + + while( *szStart == '\0' && + static_cast(szStart - szLineBuf) < nCharsRead ) + szStart++; + + szEnd = szStart; + continue; + } + + nCharsExamined += szStart - szLineBuf; + nCharsRead = VSIFReadL( szLineBuf, 1, nLineBufSize - 1, poGDS->fp ); + if( nCharsRead == 0 ) + { + VSIFree( szLineBuf ); + CPLError( CE_Failure, CPLE_FileIO, + "Can't read portion of grid row %d at offset %ld.", + nBlockYOff, (long) panLineOffset[nBlockYOff] ); + return CE_Failure; + } + szLineBuf[nCharsRead] = '\0'; + szStart = szEnd = szLineBuf; + continue; + } + else if( *szEnd == '\0' + || (*szEnd == '.' && *(szEnd+1) == '\0') + || (*szEnd == '-' && *(szEnd+1) == '\0') + || (*szEnd == '+' && *(szEnd+1) == '\0') + || (*szEnd == 'E' && *(szEnd+1) == '\0') + || (*szEnd == 'E' && *(szEnd+1) == '-' && *(szEnd+2) == '\0') + || (*szEnd == 'E' && *(szEnd+1) == '+' && *(szEnd+2) == '\0') + || (*szEnd == 'e' && *(szEnd+1) == '\0') + || (*szEnd == 'e' && *(szEnd+1) == '-' && *(szEnd+2) == '\0') + || (*szEnd == 'e' && *(szEnd+1) == '+' && *(szEnd+2) == '\0')) + { + /* Number was interrupted by a nul character */ + while( *szEnd != '\0' ) + szEnd++; + + if( static_cast(szEnd - szLineBuf) != nCharsRead ) + { + CPLError( CE_Warning, CPLE_FileIO, + "Unexpected ASCII null-character in grid row %d at " + "offset %ld.\n", + nBlockYOff, + (long) (szStart - szLineBuf) ); + + while( *szEnd == '\0' && + static_cast(szStart - szLineBuf) < nCharsRead ) + szEnd++; + + continue; + } + + /* End of buffer, could be interrupting a number */ + if( VSIFSeekL( poGDS->fp, szStart - szEnd, SEEK_CUR ) != 0 ) + { + VSIFree( szLineBuf ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek in grid row %d (offset %ld, seek %d)" + ".\n", nBlockYOff, + (long) VSIFTellL(poGDS->fp), + (int) (szStart - szEnd) ); + + return CE_Failure; + } + nCharsExamined += szStart - szLineBuf; + nCharsRead = VSIFReadL( szLineBuf, 1, nLineBufSize - 1, poGDS->fp ); + szLineBuf[nCharsRead] = '\0'; + + if( nCharsRead == 0 ) + { + VSIFree( szLineBuf ); + CPLError( CE_Failure, CPLE_FileIO, + "Can't read portion of grid row %d at offset %ld.", + nBlockYOff, (long) panLineOffset[nBlockYOff] ); + return CE_Failure; + } + else if( nCharsRead > static_cast(szEnd - szStart) ) + { + /* Read new data, this was not really the end */ + szEnd = szStart = szLineBuf; + continue; + } + + /* This is really the last value and has no tailing newline */ + szStart = szLineBuf; + szEnd = szLineBuf + nCharsRead; + } + + if( pdfImage != NULL ) + { + *(pdfImage+iCell) = dfValue; + } + + iCell++; + } + + while( *szEnd == ' ' ) + szEnd++; + + if( *szEnd != '\0' && *szEnd != poGDS->szEOL[0] ) + CPLDebug( "GSAG", "Grid row %d does not end with a newline. " + "Possible skew.\n", nBlockYOff ); + + while( isspace( (unsigned char)*szEnd ) ) + szEnd++; + + nCharsExamined += szEnd - szLineBuf; + + if( nCharsExamined >= nMaxLineSize ) + nMaxLineSize = nCharsExamined + 1; + + if( nBlockYOff > 0 ) + panLineOffset[nBlockYOff - 1] = + panLineOffset[nBlockYOff] + nCharsExamined; + + nLastReadLine = nBlockYOff; + + VSIFree( szLineBuf ); + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr GSAGRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + if( eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Unable to write block, dataset opened read only.\n" ); + return CE_Failure; + } + + if( nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0 ) + return CE_Failure; + + GSAGDataset *poGDS = (GSAGDataset *)poDS; + assert( poGDS != NULL ); + + if( padfRowMinZ == NULL || padfRowMaxZ == NULL + || nMinZRow < 0 || nMaxZRow < 0 ) + { + padfRowMinZ = (double *)VSIMalloc2( nRasterYSize,sizeof(double) ); + if( padfRowMinZ == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate space for row minimums array.\n" ); + return CE_Failure; + } + + padfRowMaxZ = (double *)VSIMalloc2( nRasterYSize,sizeof(double) ); + if( padfRowMaxZ == NULL ) + { + VSIFree( padfRowMinZ ); + padfRowMinZ = NULL; + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate space for row maximums array.\n" ); + return CE_Failure; + } + + CPLErr eErr = ScanForMinMaxZ(); + if( eErr != CE_None ) + return eErr; + } + + if( panLineOffset[nBlockYOff+1] == 0 ) + IReadBlock( nBlockXOff, nBlockYOff, NULL ); + + if( panLineOffset[nBlockYOff+1] == 0 || panLineOffset[nBlockYOff] == 0 ) + return CE_Failure; + + std::ostringstream ssOutBuf; + ssOutBuf.precision( GSAGDataset::nFIELD_PRECISION ); + ssOutBuf.setf( std::ios::uppercase ); + + double *pdfImage = (double *)pImage; + padfRowMinZ[nBlockYOff] = DBL_MAX; + padfRowMaxZ[nBlockYOff] = -DBL_MAX; + for( int iCell=0; iCell padfRowMaxZ[nBlockYOff] ) + padfRowMaxZ[nBlockYOff] = pdfImage[iCell]; + } + + ssOutBuf << pdfImage[iCell] << " "; + } + ssOutBuf << poGDS->szEOL; + } + ssOutBuf << poGDS->szEOL; + + CPLString sOut = ssOutBuf.str(); + if( sOut.length() != panLineOffset[nBlockYOff+1]-panLineOffset[nBlockYOff] ) + { + int nShiftSize = (int) (sOut.length() - (panLineOffset[nBlockYOff+1] + - panLineOffset[nBlockYOff])); + if( nBlockYOff != poGDS->nRasterYSize + && GSAGDataset::ShiftFileContents( poGDS->fp, + panLineOffset[nBlockYOff+1], + nShiftSize, + poGDS->szEOL ) != CE_None ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failure writing block, " + "unable to shift file contents.\n" ); + return CE_Failure; + } + + for( size_t iLine=nBlockYOff+1; + iLine < static_cast(poGDS->nRasterYSize+1) + && panLineOffset[iLine] != 0; iLine++ ) + panLineOffset[iLine] += nShiftSize; + } + + if( VSIFSeekL( poGDS->fp, panLineOffset[nBlockYOff], SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, "Unable to seek to grid line.\n" ); + return CE_Failure; + } + + if( VSIFWriteL( sOut.c_str(), 1, sOut.length(), + poGDS->fp ) != sOut.length() ) + { + CPLError( CE_Failure, CPLE_FileIO, "Unable to write grid block.\n" ); + return CE_Failure; + } + + /* Update header as needed */ + bool bHeaderNeedsUpdate = false; + if( nMinZRow == nBlockYOff && padfRowMinZ[nBlockYOff] > dfMinZ ) + { + double dfNewMinZ = -DBL_MAX; + for( int iRow=0; iRow dfNewMaxZ ) + { + dfNewMaxZ = padfRowMaxZ[iRow]; + nMaxZRow = iRow; + } + } + + if( dfNewMaxZ != dfMaxZ ) + { + dfMaxZ = dfNewMaxZ; + bHeaderNeedsUpdate = true; + } + } + + if( padfRowMinZ[nBlockYOff] < dfMinZ || padfRowMaxZ[nBlockYOff] > dfMaxZ ) + { + if( padfRowMinZ[nBlockYOff] < dfMinZ ) + { + dfMinZ = padfRowMinZ[nBlockYOff]; + nMinZRow = nBlockYOff; + } + + if( padfRowMaxZ[nBlockYOff] > dfMaxZ ) + { + dfMaxZ = padfRowMaxZ[nBlockYOff]; + nMaxZRow = nBlockYOff; + } + + bHeaderNeedsUpdate = true; + } + + if( bHeaderNeedsUpdate && dfMaxZ > dfMinZ ) + { + CPLErr eErr = poGDS->UpdateHeader(); + return eErr; + } + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double GSAGRasterBand::GetNoDataValue( int * pbSuccess ) +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + return GSAGDataset::dfNODATA_VALUE; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double GSAGRasterBand::GetMinimum( int *pbSuccess ) +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + return dfMinZ; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double GSAGRasterBand::GetMaximum( int *pbSuccess ) +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + return dfMaxZ; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GSAGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GSAGDataset() */ +/************************************************************************/ + +GSAGDataset::GSAGDataset( const char *pszEOL ) + +{ + if( pszEOL == NULL || EQUAL(pszEOL, "") ) + { + CPLDebug( "GSAG", "GSAGDataset() created with invalid EOL string.\n" ); + this->szEOL[0] = '\x0D'; + this->szEOL[1] = '\x0A'; + this->szEOL[2] = '\0'; + } + else + { + strncpy(this->szEOL, pszEOL, sizeof(this->szEOL)); + this->szEOL[sizeof(this->szEOL) - 1] = '\0'; + } +} + +/************************************************************************/ +/* ~GSAGDataset() */ +/************************************************************************/ + +GSAGDataset::~GSAGDataset() + +{ + FlushCache(); + if( fp != NULL ) + VSIFCloseL( fp ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int GSAGDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + /* Check for signature */ + if( poOpenInfo->nHeaderBytes < 5 + || !EQUALN((const char *) poOpenInfo->pabyHeader,"DSAA",4) + || ( poOpenInfo->pabyHeader[4] != '\x0D' + && poOpenInfo->pabyHeader[4] != '\x0A' )) + { + return FALSE; + } + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GSAGDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify(poOpenInfo) ) + { + return NULL; + } + + /* Identify the end of line marker (should be \x0D\x0A, but try some others) + * (note that '\x0D' == '\r' and '\x0A' == '\n' on most systems) */ + char szEOL[3]; + szEOL[0] = poOpenInfo->pabyHeader[4]; + szEOL[1] = poOpenInfo->pabyHeader[5]; + szEOL[2] = '\0'; + if( szEOL[1] != '\x0D' && szEOL[1] != '\x0A' ) + szEOL[1] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GSAGDataset *poDS = new GSAGDataset( szEOL ); + +/* -------------------------------------------------------------------- */ +/* Open file with large file API. */ +/* -------------------------------------------------------------------- */ + + poDS->eAccess = poOpenInfo->eAccess; + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" ); + + if( poDS->fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "VSIFOpenL(%s) failed unexpectedly.", + poOpenInfo->pszFilename ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + char *pabyHeader; + bool bMustFreeHeader = false; + if( poOpenInfo->nHeaderBytes >= static_cast(nMAX_HEADER_SIZE) ) + { + pabyHeader = (char *)poOpenInfo->pabyHeader; + } + else + { + bMustFreeHeader = true; + pabyHeader = (char *)VSIMalloc( nMAX_HEADER_SIZE ); + if( pabyHeader == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to open dataset, unable to header buffer.\n" ); + return NULL; + } + + size_t nRead = VSIFReadL( pabyHeader, 1, nMAX_HEADER_SIZE-1, poDS->fp ); + pabyHeader[nRead] = '\0'; + } + + const char *szErrorMsg = NULL; + const char *szStart = pabyHeader + 5; + char *szEnd; + double dfTemp; + double dfMinX; + double dfMaxX; + double dfMinY; + double dfMaxY; + double dfMinZ; + double dfMaxZ; + + /* Parse number of X axis grid rows */ + long nTemp = strtol( szStart, &szEnd, 10 ); + if( szStart == szEnd || nTemp < 0l ) + { + szErrorMsg = "Unable to parse the number of X axis grid columns.\n"; + goto error; + } + else if( nTemp > INT_MAX ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Number of X axis grid columns not representable.\n" ); + poDS->nRasterXSize = INT_MAX; + } + else if ( nTemp == 0 ) + { + szErrorMsg = "Number of X axis grid columns is zero, which is invalid.\n"; + goto error; + } + else + { + poDS->nRasterXSize = static_cast(nTemp); + } + szStart = szEnd; + + /* Parse number of Y axis grid rows */ + nTemp = strtol( szStart, &szEnd, 10 ); + if( szStart == szEnd || nTemp < 0l ) + { + szErrorMsg = "Unable to parse the number of Y axis grid rows.\n"; + goto error; + } + else if( nTemp > INT_MAX ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Number of Y axis grid rows not representable.\n" ); + poDS->nRasterYSize = INT_MAX; + } + else if ( nTemp == 0) + { + szErrorMsg = "Number of Y axis grid rows is zero, which is invalid.\n"; + goto error; + } + else + { + poDS->nRasterYSize = static_cast(nTemp); + } + szStart = szEnd; + + /* Parse the minimum X value of the grid */ + dfTemp = CPLStrtod( szStart, &szEnd ); + if( szStart == szEnd ) + { + szErrorMsg = "Unable to parse the minimum X value.\n"; + goto error; + } + else + { + dfMinX = dfTemp; + } + szStart = szEnd; + + /* Parse the maximum X value of the grid */ + dfTemp = CPLStrtod( szStart, &szEnd ); + if( szStart == szEnd ) + { + szErrorMsg = "Unable to parse the maximum X value.\n"; + goto error; + } + else + { + dfMaxX = dfTemp; + } + szStart = szEnd; + + /* Parse the minimum Y value of the grid */ + dfTemp = CPLStrtod( szStart, &szEnd ); + if( szStart == szEnd ) + { + szErrorMsg = "Unable to parse the minimum Y value.\n"; + goto error; + } + else + { + dfMinY = dfTemp; + } + szStart = szEnd; + + /* Parse the maximum Y value of the grid */ + dfTemp = CPLStrtod( szStart, &szEnd ); + if( szStart == szEnd ) + { + szErrorMsg = "Unable to parse the maximum Y value.\n"; + goto error; + } + else + { + dfMaxY = dfTemp; + } + szStart = szEnd; + + /* Parse the minimum Z value of the grid */ + while( isspace( (unsigned char)*szStart ) ) + szStart++; + poDS->nMinMaxZOffset = szStart - pabyHeader; + + dfTemp = CPLStrtod( szStart, &szEnd ); + if( szStart == szEnd ) + { + szErrorMsg = "Unable to parse the minimum Z value.\n"; + goto error; + } + else + { + dfMinZ = dfTemp; + } + szStart = szEnd; + + /* Parse the maximum Z value of the grid */ + dfTemp = CPLStrtod( szStart, &szEnd ); + if( szStart == szEnd ) + { + szErrorMsg = "Unable to parse the maximum Z value.\n"; + goto error; + } + else + { + dfMaxZ = dfTemp; + } + + while( isspace((unsigned char)*szEnd) ) + szEnd++; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + { + GSAGRasterBand *poBand = new GSAGRasterBand( poDS, 1, szEnd-pabyHeader ); + if( poBand->panLineOffset == NULL ) + { + delete poBand; + goto error; + } + + poBand->dfMinX = dfMinX; + poBand->dfMaxX = dfMaxX; + poBand->dfMinY = dfMinY; + poBand->dfMaxY = dfMaxY; + poBand->dfMinZ = dfMinZ; + poBand->dfMaxZ = dfMaxZ; + + poDS->SetBand( 1, poBand ); + } + + if( bMustFreeHeader ) + { + CPLFree( pabyHeader ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return( poDS ); + +error: + if ( bMustFreeHeader ) + { + CPLFree( pabyHeader ); + } + + delete poDS; + + if (szErrorMsg) + CPLError( CE_Failure, CPLE_AppDefined, "%s", szErrorMsg ); + return NULL; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GSAGDataset::GetGeoTransform( double *padfGeoTransform ) +{ + if( padfGeoTransform == NULL ) + return CE_Failure; + + GSAGRasterBand *poGRB = (GSAGRasterBand *)GetRasterBand( 1 ); + + if( poGRB == NULL ) + { + padfGeoTransform[0] = 0; + padfGeoTransform[1] = 1; + padfGeoTransform[2] = 0; + padfGeoTransform[3] = 0; + padfGeoTransform[4] = 0; + padfGeoTransform[5] = 1; + return CE_Failure; + } + + /* check if we have a PAM GeoTransform stored */ + CPLPushErrorHandler( CPLQuietErrorHandler ); + CPLErr eErr = GDALPamDataset::GetGeoTransform( padfGeoTransform ); + CPLPopErrorHandler(); + + if( eErr == CE_None ) + return CE_None; + + /* calculate pixel size first */ + padfGeoTransform[1] = (poGRB->dfMaxX - poGRB->dfMinX)/(nRasterXSize - 1); + padfGeoTransform[5] = (poGRB->dfMinY - poGRB->dfMaxY)/(nRasterYSize - 1); + + /* then calculate image origin */ + padfGeoTransform[0] = poGRB->dfMinX - padfGeoTransform[1] / 2; + padfGeoTransform[3] = poGRB->dfMaxY - padfGeoTransform[5] / 2; + + /* tilt/rotation does not supported by the GS grids */ + padfGeoTransform[4] = 0.0; + padfGeoTransform[2] = 0.0; + + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr GSAGDataset::SetGeoTransform( double *padfGeoTransform ) +{ + if( eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Unable to set GeoTransform, dataset opened read only.\n" ); + return CE_Failure; + } + + GSAGRasterBand *poGRB = (GSAGRasterBand *)GetRasterBand( 1 ); + + if( poGRB == NULL || padfGeoTransform == NULL) + return CE_Failure; + + /* non-zero transform 2 or 4 or negative 1 or 5 not supported natively */ + CPLErr eErr = CE_None; + /*if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0 + || padfGeoTransform[1] < 0.0 || padfGeoTransform[5] < 0.0 ) + eErr = GDALPamDataset::SetGeoTransform( padfGeoTransform );*/ + + if( eErr != CE_None ) + return eErr; + + double dfOldMinX = poGRB->dfMinX; + double dfOldMaxX = poGRB->dfMaxX; + double dfOldMinY = poGRB->dfMinY; + double dfOldMaxY = poGRB->dfMaxY; + + poGRB->dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2; + poGRB->dfMaxX = + padfGeoTransform[1] * (nRasterXSize - 0.5) + padfGeoTransform[0]; + poGRB->dfMinY = + padfGeoTransform[5] * (nRasterYSize - 0.5) + padfGeoTransform[3]; + poGRB->dfMaxY = padfGeoTransform[3] + padfGeoTransform[5] / 2; + + eErr = UpdateHeader(); + + if( eErr != CE_None ) + { + poGRB->dfMinX = dfOldMinX; + poGRB->dfMaxX = dfOldMaxX; + poGRB->dfMinY = dfOldMinY; + poGRB->dfMaxY = dfOldMaxY; + } + + return eErr; +} + +/************************************************************************/ +/* ShiftFileContents() */ +/************************************************************************/ +CPLErr GSAGDataset::ShiftFileContents( VSILFILE *fp, vsi_l_offset nShiftStart, + int nShiftSize, const char *pszEOL ) +{ + /* nothing to do for zero-shift */ + if( nShiftSize == 0 ) + return CE_None; + + /* make sure start location is sane */ +/* Tautology is always false. nShiftStart is unsigned. */ + if( /* nShiftStart < 0 + || */ (nShiftSize < 0 + && nShiftStart < static_cast(-nShiftSize)) ) + nShiftStart = (nShiftSize > 0) ? 0 : -nShiftSize; + + /* get offset at end of file */ + if( VSIFSeekL( fp, 0, SEEK_END ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to end of grid file.\n" ); + return CE_Failure; + } + + vsi_l_offset nOldEnd = VSIFTellL( fp ); + + /* If shifting past end, just zero-pad as necessary */ + if( nShiftStart >= nOldEnd ) + { + if( nShiftSize < 0 ) + { + if( nShiftStart + nShiftSize >= nOldEnd ) + return CE_None; + + if( VSIFSeekL( fp, nShiftStart + nShiftSize, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek near end of file.\n" ); + return CE_Failure; + } + + /* ftruncate()? */ + for( vsi_l_offset nPos = nShiftStart + nShiftSize; + nPos > nOldEnd; nPos++ ) + { + if( VSIFWriteL( (void *)" ", 1, 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write padding to grid file " + "(Out of space?).\n" ); + return CE_Failure; + } + } + + return CE_None; + } + else + { + for( vsi_l_offset nPos = nOldEnd; + nPos < nShiftStart + nShiftSize; nPos++ ) + { + if( VSIFWriteL( (void *)" ", 1, 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write padding to grid file " + "(Out of space?).\n" ); + return CE_Failure; + } + } + return CE_None; + } + } + + /* prepare buffer for real shifting */ + size_t nBufferSize = (1024 >= abs(nShiftSize)*2) ? 1024 : abs(nShiftSize)*2; + char *pabyBuffer = (char *)VSIMalloc( nBufferSize ); + if( pabyBuffer == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate space for shift buffer.\n" ); + return CE_Failure; + } + + if( VSIFSeekL( fp, nShiftStart, SEEK_SET ) != 0 ) + { + VSIFree( pabyBuffer ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to start of shift in grid file.\n" ); + return CE_Failure; + } + + size_t nRead; + size_t nOverlap = (nShiftSize > 0) ? nShiftSize : 0; + /* If there is overlap, fill buffer with the overlap to start */ + if( nOverlap > 0) + { + nRead = VSIFReadL( (void *)pabyBuffer, 1, nOverlap, fp ); + if( nRead < nOverlap && !VSIFEofL( fp ) ) + { + VSIFree( pabyBuffer ); + CPLError( CE_Failure, CPLE_FileIO, + "Error reading grid file.\n" ); + return CE_Failure; + } + + /* overwrite the new space with ' ' */ + if( VSIFSeekL( fp, nShiftStart, SEEK_SET ) != 0 ) + { + VSIFree( pabyBuffer ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to start of shift in grid file.\n" ); + return CE_Failure; + } + + for( int iFill=0; iFill= nOldEnd ) + { + if( VSIFWriteL( (void *)pabyBuffer, 1, nRead, fp ) != nRead ) + { + VSIFree( pabyBuffer ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write to grid file (Out of space?).\n" ); + return CE_Failure; + } + + VSIFree( pabyBuffer ); + return CE_None; + } + } + + /* iterate over the remainder of the file and shift as requested */ + bool bEOF = false; + while( !bEOF ) + { + nRead = VSIFReadL( (void *)(pabyBuffer+nOverlap), 1, + nBufferSize - nOverlap, fp ); + + if( VSIFEofL( fp ) ) + bEOF = true; + else + bEOF = false; + + if( nRead == 0 && !bEOF ) + { + VSIFree( pabyBuffer ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read from grid file (possible corruption).\n"); + return CE_Failure; + } + + /* FIXME: Should use SEEK_CUR, review integer promotions... */ + if( VSIFSeekL( fp, VSIFTellL(fp)-nRead+nShiftSize-nOverlap, + SEEK_SET ) != 0 ) + { + VSIFree( pabyBuffer ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek in grid file (possible corruption).\n" ); + return CE_Failure; + } + + size_t nWritten = VSIFWriteL( (void *)pabyBuffer, 1, nRead, fp ); + if( nWritten != nRead ) + { + VSIFree( pabyBuffer ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write to grid file (out of space?).\n" ); + return CE_Failure; + } + + /* shift overlapped contents to the front of the buffer if necessary */ + if( nOverlap > 0) + memmove(pabyBuffer, pabyBuffer+nRead, nOverlap); + } + + /* write the remainder of the buffer or overwrite leftovers and finish */ + if( nShiftSize > 0 ) + { + size_t nTailSize = nOverlap; + while( nTailSize > 0 && isspace( (unsigned char)pabyBuffer[nTailSize-1] ) ) + nTailSize--; + + if( VSIFWriteL( (void *)pabyBuffer, 1, nTailSize, fp ) != nTailSize ) + { + VSIFree( pabyBuffer ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write to grid file (out of space?).\n" ); + return CE_Failure; + } + + if( VSIFWriteL( (void *)pszEOL, 1, strlen(pszEOL), fp ) + != strlen(pszEOL) ) + { + VSIFree( pabyBuffer ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write to grid file (out of space?).\n" ); + return CE_Failure; + } + } + else + { + /* FIXME: ftruncate()? */ + /* FIXME: Should use SEEK_CUR, review integer promotions... */ + if( VSIFSeekL( fp, VSIFTellL(fp)-strlen(pszEOL), SEEK_SET ) != 0 ) + { + VSIFree( pabyBuffer ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek in grid file.\n" ); + return CE_Failure; + } + + for( int iPadding=0; iPadding<-nShiftSize; iPadding++ ) + { + if( VSIFWriteL( (void *)" ", 1, 1, fp ) != 1 ) + { + VSIFree( pabyBuffer ); + CPLError( CE_Failure, CPLE_FileIO, + "Error writing to grid file.\n" ); + return CE_Failure; + } + } + + if( VSIFWriteL( (void *)pszEOL, 1, strlen(pszEOL), fp ) + != strlen(pszEOL) ) + { + VSIFree( pabyBuffer ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write to grid file (out of space?).\n" ); + return CE_Failure; + } + } + + VSIFree( pabyBuffer ); + return CE_None; +} + +/************************************************************************/ +/* UpdateHeader() */ +/************************************************************************/ + +CPLErr GSAGDataset::UpdateHeader() + +{ + GSAGRasterBand *poBand = (GSAGRasterBand *)GetRasterBand( 1 ); + if( poBand == NULL ) + { + CPLError( CE_Failure, CPLE_FileIO, "Unable to open raster band.\n" ); + return CE_Failure; + } + + std::ostringstream ssOutBuf; + ssOutBuf.precision( nFIELD_PRECISION ); + ssOutBuf.setf( std::ios::uppercase ); + + /* signature */ + ssOutBuf << "DSAA" << szEOL; + + /* columns rows */ + ssOutBuf << nRasterXSize << " " << nRasterYSize << szEOL; + + /* x range */ + ssOutBuf << poBand->dfMinX << " " << poBand->dfMaxX << szEOL; + + /* y range */ + ssOutBuf << poBand->dfMinY << " " << poBand->dfMaxY << szEOL; + + /* z range */ + ssOutBuf << poBand->dfMinZ << " " << poBand->dfMaxZ << szEOL; + + CPLString sOut = ssOutBuf.str(); + if( sOut.length() != poBand->panLineOffset[0] ) + { + int nShiftSize = (int) (sOut.length() - poBand->panLineOffset[0]); + if( ShiftFileContents( fp, poBand->panLineOffset[0], nShiftSize, + szEOL ) != CE_None ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to update grid header, " + "failure shifting file contents.\n" ); + return CE_Failure; + } + + for( size_t iLine=0; + iLine < static_cast(nRasterYSize+1) + && poBand->panLineOffset[iLine] != 0; + iLine++ ) + poBand->panLineOffset[iLine] += nShiftSize; + } + + if( VSIFSeekL( fp, 0, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to start of grid file.\n" ); + return CE_Failure; + } + + if( VSIFWriteL( sOut.c_str(), 1, sOut.length(), fp ) != sOut.length() ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to update file header. Disk full?\n" ); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset *GSAGDataset::CreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, + CPL_UNUSED char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressData ) +{ + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GSAG driver does not support source dataset with zero band.\n"); + return NULL; + } + else if (nBands > 1) + { + if( bStrict ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to create copy, Golden Software ASCII Grid " + "format only supports one raster band.\n" ); + return NULL; + } + else + CPLError( CE_Warning, CPLE_NotSupported, + "Golden Software ASCII Grid format only supports one " + "raster band, first band will be copied.\n" ); + } + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated\n" ); + return NULL; + } + + VSILFILE *fp = VSIFOpenL( pszFilename, "w+b" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file '%s' failed.\n", + pszFilename ); + return NULL; + } + + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + double adfGeoTransform[6]; + + poSrcDS->GetGeoTransform( adfGeoTransform ); + + std::ostringstream ssHeader; + ssHeader.precision( nFIELD_PRECISION ); + ssHeader.setf( std::ios::uppercase ); + + ssHeader << "DSAA\x0D\x0A"; + + ssHeader << nXSize << " " << nYSize << "\x0D\x0A"; + + ssHeader << adfGeoTransform[0] + adfGeoTransform[1] / 2 << " " + << adfGeoTransform[1] * (nXSize - 0.5) + adfGeoTransform[0] + << "\x0D\x0A"; + + ssHeader << adfGeoTransform[5] * (nYSize - 0.5) + adfGeoTransform[3] << " " + << adfGeoTransform[3] + adfGeoTransform[5] / 2 + << "\x0D\x0A"; + + if( VSIFWriteL( (void *)ssHeader.str().c_str(), 1, ssHeader.str().length(), + fp ) != ssHeader.str().length() ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to create copy, writing header failed.\n" ); + return NULL; + } + + /* Save the location and write placeholders for the min/max Z value */ + vsi_l_offset nRangeStart = VSIFTellL( fp ); + const char *szDummyRange = "0.0000000000001 0.0000000000001\x0D\x0A"; + size_t nDummyRangeLen = strlen( szDummyRange ); + if( VSIFWriteL( (void *)szDummyRange, 1, nDummyRangeLen, + fp ) != nDummyRangeLen ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to create copy, writing header failed.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Copy band data. */ +/* -------------------------------------------------------------------- */ + double *pdfData = (double *)VSIMalloc2( nXSize, sizeof( double ) ); + if( pdfData == NULL ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to create copy, unable to allocate line buffer.\n" ); + return NULL; + } + + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1); + int bSrcHasNDValue; + double dfSrcNoDataValue = poSrcBand->GetNoDataValue( &bSrcHasNDValue ); + double dfMin = DBL_MAX; + double dfMax = -DBL_MAX; + for( int iRow=0; iRowRasterIO( GF_Read, 0, nYSize-iRow-1, + nXSize, 1, pdfData, + nXSize, 1, GDT_Float64, 0, 0, NULL ); + + if( eErr != CE_None ) + { + VSIFCloseL( fp ); + VSIFree( pdfData ); + return NULL; + } + + for( int iCol=0; iCol dfMax ) + dfMax = dfValue; + + if( dfValue < dfMin ) + dfMin = dfValue; + } + + std::ostringstream ssOut; + ssOut.precision(nFIELD_PRECISION); + ssOut.setf( std::ios::uppercase ); + ssOut << dfValue << " "; + CPLString sOut = ssOut.str(); + + if( VSIFWriteL( sOut.c_str(), 1, sOut.length(), fp ) + != sOut.length() ) + { + VSIFCloseL( fp ); + VSIFree( pdfData ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write grid cell. Disk full?\n" ); + return NULL; + } + } + + if( VSIFWriteL( (void *)"\x0D\x0A", 1, 2, fp ) != 2 ) + { + VSIFCloseL( fp ); + VSIFree( pdfData ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to finish write of grid line. Disk full?\n" ); + return NULL; + } + } + + if( VSIFWriteL( (void *)"\x0D\x0A", 1, 2, fp ) != 2 ) + { + VSIFCloseL( fp ); + VSIFree( pdfData ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to finish write of grid row. Disk full?\n" ); + return NULL; + } + + if( !pfnProgress( static_cast(iRow + 1)/nYSize, + NULL, pProgressData ) ) + { + VSIFCloseL( fp ); + VSIFree( pdfData ); + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return NULL; + } + } + + VSIFree( pdfData ); + + /* write out the min and max values */ + std::ostringstream ssRange; + ssRange.precision( nFIELD_PRECISION ); + ssRange.setf( std::ios::uppercase ); + ssRange << dfMin << " " << dfMax << "\x0D\x0A"; + if( ssRange.str().length() != nDummyRangeLen ) + { + int nShiftSize = ssRange.str().length() - nDummyRangeLen; + if( ShiftFileContents( fp, nRangeStart + nDummyRangeLen, + nShiftSize, "\x0D\x0A" ) != CE_None ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to shift file contents.\n" ); + return NULL; + } + } + + if( VSIFSeekL( fp, nRangeStart, SEEK_SET ) != 0 ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to start of grid file copy.\n" ); + return NULL; + } + + if( VSIFWriteL( (void *)ssRange.str().c_str(), 1, ssRange.str().length(), + fp ) != ssRange.str().length() ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write range information.\n" ); + return NULL; + } + + VSIFCloseL( fp ); + + GDALPamDataset *poDS = (GDALPamDataset *)GDALOpen( pszFilename, + GA_Update ); + if (poDS) + { + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + } + return poDS; +} + +/************************************************************************/ +/* GDALRegister_GSAG() */ +/************************************************************************/ + +void GDALRegister_GSAG() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GSAG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GSAG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Golden Software ASCII Grid (.grd)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#GSAG" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "grd" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 " + "Float32 Float64" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = GSAGDataset::Identify; + poDriver->pfnOpen = GSAGDataset::Open; + poDriver->pfnCreateCopy = GSAGDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/gsg/gsbgdataset.cpp b/bazaar/plugin/gdal/frmts/gsg/gsbgdataset.cpp new file mode 100644 index 000000000..fc3054414 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gsg/gsbgdataset.cpp @@ -0,0 +1,1165 @@ +/****************************************************************************** + * $Id: gsbgdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: GDAL + * Purpose: Implements the Golden Software Binary Grid Format. + * Author: Kevin Locke, kwl7@cornell.edu + * (Based largely on aaigriddataset.cpp by Frank Warmerdam) + * + ****************************************************************************** + * Copyright (c) 2006, Kevin Locke + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" + +#include +#include +#include + +#include "gdal_pam.h" + +#ifndef DBL_MAX +# ifdef __DBL_MAX__ +# define DBL_MAX __DBL_MAX__ +# else +# define DBL_MAX 1.7976931348623157E+308 +# endif /* __DBL_MAX__ */ +#endif /* DBL_MAX */ + +#ifndef FLT_MAX +# ifdef __FLT_MAX__ +# define FLT_MAX __FLT_MAX__ +# else +# define FLT_MAX 3.40282347E+38F +# endif /* __FLT_MAX__ */ +#endif /* FLT_MAX */ + +#ifndef INT_MAX +# define INT_MAX 2147483647 +#endif /* INT_MAX */ + +#ifndef SHRT_MAX +# define SHRT_MAX 32767 +#endif /* SHRT_MAX */ + +CPL_CVSID("$Id: gsbgdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_GSBG(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* GSBGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GSBGRasterBand; + +class GSBGDataset : public GDALPamDataset +{ + friend class GSBGRasterBand; + + static const float fNODATA_VALUE; + static const size_t nHEADER_SIZE; + + static CPLErr WriteHeader( VSILFILE *fp, GInt16 nXSize, GInt16 nYSize, + double dfMinX, double dfMaxX, + double dfMinY, double dfMaxY, + double dfMinZ, double dfMaxZ ); + + VSILFILE *fp; + + public: + ~GSBGDataset(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char **papszParmList ); + static GDALDataset *CreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressData ); + + CPLErr GetGeoTransform( double *padfGeoTransform ); + CPLErr SetGeoTransform( double *padfGeoTransform ); +}; + +/* NOTE: This is not mentioned in the spec, but Surfer 8 uses this value */ +/* 0x7effffee (Little Endian: eeffff7e) */ +const float GSBGDataset::fNODATA_VALUE = 1.701410009187828e+38f; + +const size_t GSBGDataset::nHEADER_SIZE = 56; + +/************************************************************************/ +/* ==================================================================== */ +/* GSBGRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GSBGRasterBand : public GDALPamRasterBand +{ + friend class GSBGDataset; + + double dfMinX; + double dfMaxX; + double dfMinY; + double dfMaxY; + double dfMinZ; + double dfMaxZ; + + float *pafRowMinZ; + float *pafRowMaxZ; + int nMinZRow; + int nMaxZRow; + + CPLErr ScanForMinMaxZ(); + + public: + + GSBGRasterBand( GSBGDataset *, int ); + ~GSBGRasterBand(); + + CPLErr IReadBlock( int, int, void * ); + CPLErr IWriteBlock( int, int, void * ); + + double GetNoDataValue( int *pbSuccess = NULL ); + double GetMinimum( int *pbSuccess = NULL ); + double GetMaximum( int *pbSuccess = NULL ); +}; + +/************************************************************************/ +/* GSBGRasterBand() */ +/************************************************************************/ + +GSBGRasterBand::GSBGRasterBand( GSBGDataset *poDS, int nBand ) : + pafRowMinZ(NULL), + pafRowMaxZ(NULL), + nMinZRow(-1), + nMaxZRow(-1) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Float32; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* ~GSBGRasterBand() */ +/************************************************************************/ + +GSBGRasterBand::~GSBGRasterBand( ) + +{ + if( pafRowMinZ != NULL ) + CPLFree( pafRowMinZ ); + if( pafRowMaxZ != NULL ) + CPLFree( pafRowMaxZ ); +} + +/************************************************************************/ +/* ScanForMinMaxZ() */ +/************************************************************************/ + +CPLErr GSBGRasterBand::ScanForMinMaxZ() + +{ + float *pafRowVals = (float *)VSIMalloc2( nRasterXSize, 4 ); + + if( pafRowVals == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate row buffer to scan grid file.\n" ); + return CE_Failure; + } + + double dfNewMinZ = DBL_MAX; + double dfNewMaxZ = -DBL_MAX; + int nNewMinZRow = 0; + int nNewMaxZRow = 0; + + /* Since we have to scan, lets calc. statistics too */ + double dfSum = 0.0; + double dfSum2 = 0.0; + unsigned long nValuesRead = 0; + for( int iRow=0; iRow pafRowMinZ[iRow] ) + pafRowMaxZ[iRow] = pafRowVals[iCol]; + + dfSum += pafRowVals[iCol]; + dfSum2 += pafRowVals[iCol] * pafRowVals[iCol]; + nValuesRead++; + } + + if( pafRowMinZ[iRow] < dfNewMinZ ) + { + dfNewMinZ = pafRowMinZ[iRow]; + nNewMinZRow = iRow; + } + + if( pafRowMaxZ[iRow] > dfNewMaxZ ) + { + dfNewMaxZ = pafRowMaxZ[iRow]; + nNewMaxZRow = iRow; + } + } + + VSIFree( pafRowVals ); + + if( nValuesRead == 0 ) + { + dfMinZ = 0.0; + dfMaxZ = 0.0; + nMinZRow = 0; + nMaxZRow = 0; + return CE_None; + } + + dfMinZ = dfNewMinZ; + dfMaxZ = dfNewMaxZ; + nMinZRow = nNewMinZRow; + nMaxZRow = nNewMaxZRow; + + double dfMean = dfSum / nValuesRead; + double dfStdDev = sqrt((dfSum2 / nValuesRead) - (dfMean * dfMean)); + SetStatistics( dfMinZ, dfMaxZ, dfMean, dfStdDev ); + + return CE_None; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GSBGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + if( nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0 ) + return CE_Failure; + + GSBGDataset *poGDS = dynamic_cast(poDS); + if( VSIFSeekL( poGDS->fp, + GSBGDataset::nHEADER_SIZE + + 4 * nRasterXSize * (nRasterYSize - nBlockYOff - 1), + SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to beginning of grid row.\n" ); + return CE_Failure; + } + + if( VSIFReadL( pImage, sizeof(float), nBlockXSize, + poGDS->fp ) != static_cast(nBlockXSize) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read block from grid file.\n" ); + return CE_Failure; + } + +#ifdef CPL_MSB + float *pfImage = (float *)pImage; + for( int iPixel=0; iPixel nRasterYSize - 1 || nBlockXOff != 0 ) + return CE_Failure; + + GSBGDataset *poGDS = dynamic_cast(poDS); + assert( poGDS != NULL ); + + if( pafRowMinZ == NULL || pafRowMaxZ == NULL + || nMinZRow < 0 || nMaxZRow < 0 ) + { + pafRowMinZ = (float *)VSIMalloc2( nRasterYSize,sizeof(float) ); + if( pafRowMinZ == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate space for row minimums array.\n" ); + return CE_Failure; + } + + pafRowMaxZ = (float *)VSIMalloc2( nRasterYSize,sizeof(float) ); + if( pafRowMaxZ == NULL ) + { + VSIFree( pafRowMinZ ); + pafRowMinZ = NULL; + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate space for row maximums array.\n" ); + return CE_Failure; + } + + CPLErr eErr = ScanForMinMaxZ(); + if( eErr != CE_None ) + return eErr; + } + + if( VSIFSeekL( poGDS->fp, + GSBGDataset::nHEADER_SIZE + + 4 * nRasterXSize * (nRasterYSize - nBlockYOff - 1), + SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to beginning of grid row.\n" ); + return CE_Failure; + } + + float *pfImage = (float *)pImage; + pafRowMinZ[nBlockYOff] = FLT_MAX; + pafRowMaxZ[nBlockYOff] = -FLT_MAX; + for( int iPixel=0; iPixel pafRowMaxZ[nBlockYOff] ) + pafRowMaxZ[nBlockYOff] = pfImage[iPixel]; + } + + CPL_LSBPTR32( pfImage+iPixel ); + } + + if( VSIFWriteL( pImage, sizeof(float), nBlockXSize, + poGDS->fp ) != static_cast(nBlockXSize) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write block to grid file.\n" ); + return CE_Failure; + } + + /* Update min/max Z values as appropriate */ + bool bHeaderNeedsUpdate = false; + if( nMinZRow == nBlockYOff && pafRowMinZ[nBlockYOff] > dfMinZ ) + { + double dfNewMinZ = DBL_MAX; + for( int iRow=0; iRow dfNewMaxZ ) + { + dfNewMaxZ = pafRowMaxZ[iRow]; + nMaxZRow = iRow; + } + } + + if( dfNewMaxZ != dfMaxZ ) + { + dfMaxZ = dfNewMaxZ; + bHeaderNeedsUpdate = true; + } + } + + if( pafRowMinZ[nBlockYOff] < dfMinZ || pafRowMaxZ[nBlockYOff] > dfMaxZ ) + { + if( pafRowMinZ[nBlockYOff] < dfMinZ ) + { + dfMinZ = pafRowMinZ[nBlockYOff]; + nMinZRow = nBlockYOff; + } + + if( pafRowMaxZ[nBlockYOff] > dfMaxZ ) + { + dfMaxZ = pafRowMaxZ[nBlockYOff]; + nMaxZRow = nBlockYOff; + } + + bHeaderNeedsUpdate = true; + } + + if( bHeaderNeedsUpdate && dfMaxZ > dfMinZ ) + { + CPLErr eErr = poGDS->WriteHeader( poGDS->fp, + (GInt16) nRasterXSize, + (GInt16) nRasterYSize, + dfMinX, dfMaxX, + dfMinY, dfMaxY, + dfMinZ, dfMaxZ ); + return eErr; + } + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double GSBGRasterBand::GetNoDataValue( int * pbSuccess ) +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + return GSBGDataset::fNODATA_VALUE; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double GSBGRasterBand::GetMinimum( int *pbSuccess ) +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + return dfMinZ; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double GSBGRasterBand::GetMaximum( int *pbSuccess ) +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + return dfMaxZ; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GSBGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +GSBGDataset::~GSBGDataset() + +{ + FlushCache(); + if( fp != NULL ) + VSIFCloseL( fp ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int GSBGDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + /* Check for signature */ + if( poOpenInfo->nHeaderBytes < 4 + || !EQUALN((const char *) poOpenInfo->pabyHeader,"DSBB",4) ) + { + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GSBGDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify(poOpenInfo) ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GSBGDataset *poDS = new GSBGDataset(); + +/* -------------------------------------------------------------------- */ +/* Open file with large file API. */ +/* -------------------------------------------------------------------- */ + poDS->eAccess = poOpenInfo->eAccess; + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" ); + + if( poDS->fp == NULL ) + { + delete poDS; + CPLError( CE_Failure, CPLE_OpenFailed, + "VSIFOpenL(%s) failed unexpectedly.", + poOpenInfo->pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + if( VSIFSeekL( poDS->fp, 4, SEEK_SET ) != 0 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to start of grid file header.\n" ); + return NULL; + } + + /* Parse number of X axis grid rows */ + GInt16 nTemp; + if( VSIFReadL( (void *)&nTemp, 2, 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, "Unable to read raster X size.\n" ); + return NULL; + } + poDS->nRasterXSize = CPL_LSBWORD16( nTemp ); + + if( VSIFReadL( (void *)&nTemp, 2, 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, "Unable to read raster Y size.\n" ); + return NULL; + } + poDS->nRasterYSize = CPL_LSBWORD16( nTemp ); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + GSBGRasterBand *poBand = new GSBGRasterBand( poDS, 1 ); + + double dfTemp; + if( VSIFReadL( (void *)&dfTemp, 8, 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read minimum X value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poBand->dfMinX = dfTemp; + + if( VSIFReadL( (void *)&dfTemp, 8, 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read maximum X value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poBand->dfMaxX = dfTemp; + + if( VSIFReadL( (void *)&dfTemp, 8, 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read minimum Y value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poBand->dfMinY = dfTemp; + + if( VSIFReadL( (void *)&dfTemp, 8, 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read maximum Y value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poBand->dfMaxY = dfTemp; + + if( VSIFReadL( (void *)&dfTemp, 8, 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read minimum Z value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poBand->dfMinZ = dfTemp; + + if( VSIFReadL( (void *)&dfTemp, 8, 1, poDS->fp ) != 1 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read maximum Z value.\n" ); + return NULL; + } + CPL_LSBPTR64( &dfTemp ); + poBand->dfMaxZ = dfTemp; + + poDS->SetBand( 1, poBand ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return poDS; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GSBGDataset::GetGeoTransform( double *padfGeoTransform ) +{ + if( padfGeoTransform == NULL ) + return CE_Failure; + + GSBGRasterBand *poGRB = dynamic_cast(GetRasterBand( 1 )); + + if( poGRB == NULL ) + { + padfGeoTransform[0] = 0; + padfGeoTransform[1] = 1; + padfGeoTransform[2] = 0; + padfGeoTransform[3] = 0; + padfGeoTransform[4] = 0; + padfGeoTransform[5] = 1; + return CE_Failure; + } + + /* check if we have a PAM GeoTransform stored */ + CPLPushErrorHandler( CPLQuietErrorHandler ); + CPLErr eErr = GDALPamDataset::GetGeoTransform( padfGeoTransform ); + CPLPopErrorHandler(); + + if( eErr == CE_None ) + return CE_None; + + /* calculate pixel size first */ + padfGeoTransform[1] = (poGRB->dfMaxX - poGRB->dfMinX)/(nRasterXSize - 1); + padfGeoTransform[5] = (poGRB->dfMinY - poGRB->dfMaxY)/(nRasterYSize - 1); + + /* then calculate image origin */ + padfGeoTransform[0] = poGRB->dfMinX - padfGeoTransform[1] / 2; + padfGeoTransform[3] = poGRB->dfMaxY - padfGeoTransform[5] / 2; + + /* tilt/rotation does not supported by the GS grids */ + padfGeoTransform[4] = 0.0; + padfGeoTransform[2] = 0.0; + + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr GSBGDataset::SetGeoTransform( double *padfGeoTransform ) +{ + if( eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Unable to set GeoTransform, dataset opened read only.\n" ); + return CE_Failure; + } + + GSBGRasterBand *poGRB = dynamic_cast(GetRasterBand( 1 )); + + if( poGRB == NULL || padfGeoTransform == NULL) + return CE_Failure; + + /* non-zero transform 2 or 4 or negative 1 or 5 not supported natively */ + CPLErr eErr = CE_None; + /*if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0 + || padfGeoTransform[1] < 0.0 || padfGeoTransform[5] < 0.0 ) + eErr = GDALPamDataset::SetGeoTransform( padfGeoTransform ); + + if( eErr != CE_None ) + return eErr;*/ + + double dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2; + double dfMaxX = + padfGeoTransform[1] * (nRasterXSize - 0.5) + padfGeoTransform[0]; + double dfMinY = + padfGeoTransform[5] * (nRasterYSize - 0.5) + padfGeoTransform[3]; + double dfMaxY = padfGeoTransform[3] + padfGeoTransform[5] / 2; + + eErr = WriteHeader( fp, + (GInt16) poGRB->nRasterXSize, + (GInt16) poGRB->nRasterYSize, + dfMinX, dfMaxX, dfMinY, dfMaxY, + poGRB->dfMinZ, poGRB->dfMaxZ ); + + if( eErr == CE_None ) + { + poGRB->dfMinX = dfMinX; + poGRB->dfMaxX = dfMaxX; + poGRB->dfMinY = dfMinY; + poGRB->dfMaxY = dfMaxY; + } + + return eErr; +} + +/************************************************************************/ +/* WriteHeader() */ +/************************************************************************/ + +CPLErr GSBGDataset::WriteHeader( VSILFILE *fp, GInt16 nXSize, GInt16 nYSize, + double dfMinX, double dfMaxX, + double dfMinY, double dfMaxY, + double dfMinZ, double dfMaxZ ) + +{ + if( VSIFSeekL( fp, 0, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to start of grid file.\n" ); + return CE_Failure; + } + + if( VSIFWriteL( (void *)"DSBB", 1, 4, fp ) != 4 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write signature to grid file.\n" ); + return CE_Failure; + } + + GInt16 nTemp = CPL_LSBWORD16(nXSize); + if( VSIFWriteL( (void *)&nTemp, 2, 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write raster X size to grid file.\n" ); + return CE_Failure; + } + + nTemp = CPL_LSBWORD16(nYSize); + if( VSIFWriteL( (void *)&nTemp, 2, 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write raster Y size to grid file.\n" ); + return CE_Failure; + } + + double dfTemp = dfMinX; + CPL_LSBPTR64( &dfTemp ); + if( VSIFWriteL( (void *)&dfTemp, 8, 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write minimum X value to grid file.\n" ); + return CE_Failure; + } + + dfTemp = dfMaxX; + CPL_LSBPTR64( &dfTemp ); + if( VSIFWriteL( (void *)&dfTemp, 8, 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write maximum X value to grid file.\n" ); + return CE_Failure; + } + + dfTemp = dfMinY; + CPL_LSBPTR64( &dfTemp ); + if( VSIFWriteL( (void *)&dfTemp, 8, 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write minimum Y value to grid file.\n" ); + return CE_Failure; + } + + dfTemp = dfMaxY; + CPL_LSBPTR64( &dfTemp ); + if( VSIFWriteL( (void *)&dfTemp, 8, 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write maximum Y value to grid file.\n" ); + return CE_Failure; + } + + dfTemp = dfMinZ; + CPL_LSBPTR64( &dfTemp ); + if( VSIFWriteL( (void *)&dfTemp, 8, 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write minimum Z value to grid file.\n" ); + return CE_Failure; + } + + dfTemp = dfMaxZ; + CPL_LSBPTR64( &dfTemp ); + if( VSIFWriteL( (void *)&dfTemp, 8, 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write maximum Z value to grid file.\n" ); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *GSBGDataset::Create( const char * pszFilename, + int nXSize, + int nYSize, + CPL_UNUSED int nBands, + GDALDataType eType, + CPL_UNUSED char **papszParmList ) +{ + if( nXSize <= 0 || nYSize <= 0 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Unable to create grid, both X and Y size must be " + "non-negative.\n" ); + + return NULL; + } + else if( nXSize > SHRT_MAX + || nYSize > SHRT_MAX ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Unable to create grid, Golden Software Binary Grid format " + "only supports sizes up to %dx%d. %dx%d not supported.\n", + SHRT_MAX, SHRT_MAX, nXSize, nYSize ); + + return NULL; + } + + if( eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 + && eType != GDT_Int16 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Golden Software Binary Grid only supports Byte, Int16, " + "Uint16, and Float32 datatypes. Unable to create with " + "type %s.\n", GDALGetDataTypeName( eType ) ); + + return NULL; + } + + VSILFILE *fp = VSIFOpenL( pszFilename, "w+b" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file '%s' failed.\n", + pszFilename ); + return NULL; + } + + CPLErr eErr = WriteHeader( fp, (GInt16) nXSize, (GInt16) nYSize, + 0.0, nXSize, 0.0, nYSize, 0.0, 0.0 ); + if( eErr != CE_None ) + { + VSIFCloseL( fp ); + return NULL; + } + + float fVal = fNODATA_VALUE; + CPL_LSBPTR32( &fVal ); + for( int iRow = 0; iRow < nYSize; iRow++ ) + { + for( int iCol=0; iColGetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GSBG driver does not support source dataset with zero band.\n"); + return NULL; + } + else if (nBands > 1) + { + if( bStrict ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to create copy, Golden Software Binary Grid " + "format only supports one raster band.\n" ); + return NULL; + } + else + CPLError( CE_Warning, CPLE_NotSupported, + "Golden Software Binary Grid format only supports one " + "raster band, first band will be copied.\n" ); + } + + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( 1 ); + if( poSrcBand->GetXSize() > SHRT_MAX + || poSrcBand->GetYSize() > SHRT_MAX ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Unable to create grid, Golden Software Binary Grid format " + "only supports sizes up to %dx%d. %dx%d not supported.\n", + SHRT_MAX, SHRT_MAX, + poSrcBand->GetXSize(), poSrcBand->GetYSize() ); + + return NULL; + } + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated\n" ); + return NULL; + } + + VSILFILE *fp = VSIFOpenL( pszFilename, "w+b" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file '%s' failed.\n", + pszFilename ); + return NULL; + } + + GInt16 nXSize = (GInt16) poSrcBand->GetXSize(); + GInt16 nYSize = (GInt16) poSrcBand->GetYSize(); + double adfGeoTransform[6]; + + poSrcDS->GetGeoTransform( adfGeoTransform ); + + double dfMinX = adfGeoTransform[0] + adfGeoTransform[1] / 2; + double dfMaxX = adfGeoTransform[1] * (nXSize - 0.5) + adfGeoTransform[0]; + double dfMinY = adfGeoTransform[5] * (nYSize - 0.5) + adfGeoTransform[3]; + double dfMaxY = adfGeoTransform[3] + adfGeoTransform[5] / 2; + CPLErr eErr = WriteHeader( fp, nXSize, nYSize, + dfMinX, dfMaxX, dfMinY, dfMaxY, 0.0, 0.0 ); + + if( eErr != CE_None ) + { + VSIFCloseL( fp ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Copy band data. */ +/* -------------------------------------------------------------------- */ + float *pfData = (float *)VSIMalloc2( nXSize, sizeof( float ) ); + if( pfData == NULL ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to create copy, unable to allocate line buffer.\n" ); + return NULL; + } + + int bSrcHasNDValue; + float fSrcNoDataValue = (float) poSrcBand->GetNoDataValue( &bSrcHasNDValue ); + double dfMinZ = DBL_MAX; + double dfMaxZ = -DBL_MAX; + for( GInt16 iRow = nYSize - 1; iRow >= 0; iRow-- ) + { + eErr = poSrcBand->RasterIO( GF_Read, 0, iRow, + nXSize, 1, pfData, + nXSize, 1, GDT_Float32, 0, 0, NULL ); + + if( eErr != CE_None ) + { + VSIFCloseL( fp ); + VSIFree( pfData ); + return NULL; + } + + for( int iCol=0; iCol dfMaxZ ) + dfMaxZ = pfData[iCol]; + + if( pfData[iCol] < dfMinZ ) + dfMinZ = pfData[iCol]; + } + + CPL_LSBPTR32( pfData+iCol ); + } + + if( VSIFWriteL( (void *)pfData, 4, nXSize, + fp ) != static_cast(nXSize) ) + { + VSIFCloseL( fp ); + VSIFree( pfData ); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write grid row. Disk full?\n" ); + return NULL; + } + + if( !pfnProgress( static_cast(nYSize - iRow)/nYSize, + NULL, pProgressData ) ) + { + VSIFCloseL( fp ); + VSIFree( pfData ); + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return NULL; + } + } + + VSIFree( pfData ); + + /* write out the min and max values */ + eErr = WriteHeader( fp, nXSize, nYSize, + dfMinX, dfMaxX, dfMinY, dfMaxY, dfMinZ, dfMaxZ ); + + if( eErr != CE_None ) + { + VSIFCloseL( fp ); + return NULL; + } + + VSIFCloseL( fp ); + + GDALPamDataset *poDS = (GDALPamDataset *)GDALOpen( pszFilename, + GA_Update ); + if (poDS) + { + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + } + return poDS; +} + +/************************************************************************/ +/* GDALRegister_GSBG() */ +/************************************************************************/ + +void GDALRegister_GSBG() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GSBG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GSBG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Golden Software Binary Grid (.grd)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#GSBG" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "grd" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Float32" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = GSBGDataset::Identify; + poDriver->pfnOpen = GSBGDataset::Open; + poDriver->pfnCreate = GSBGDataset::Create; + poDriver->pfnCreateCopy = GSBGDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/gsg/makefile.vc b/bazaar/plugin/gdal/frmts/gsg/makefile.vc new file mode 100644 index 000000000..c38b5d935 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gsg/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = gsagdataset.obj gsbgdataset.obj gs7bgdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/gta/GNUmakefile b/bazaar/plugin/gdal/frmts/gta/GNUmakefile new file mode 100644 index 000000000..148615bc0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gta/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = gtadataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/gta/frmt_gta.html b/bazaar/plugin/gdal/frmts/gta/frmt_gta.html new file mode 100644 index 000000000..cdfae7bc0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gta/frmt_gta.html @@ -0,0 +1,31 @@ + + + + +GTA - Generic Tagged Arrays + + + + +

GTA - Generic Tagged Arrays

+ +

Starting with GDAL 1.9.0, GDAL can read and write GTA data files through the libgta library.

+ +

GTA is a file format that can store any kind of multidimensional array data, allows generic manipulations of array data, and +allows easy conversion to and from other file formats.

+ +

Creation options

+ +
    +
  • COMPRESS=method Set the GTA compression method: NONE (default) or one of BZIP2, XZ, ZLIB, +ZLIB1, ZLIB2, ZLIB3, ZLIB4, ZLIB5, ZLIB6, ZLIB7, ZLIB8, ZLIB9.

  • +
+ +

See Also:

+ + + + + diff --git a/bazaar/plugin/gdal/frmts/gta/gtadataset.cpp b/bazaar/plugin/gdal/frmts/gta/gtadataset.cpp new file mode 100644 index 000000000..f9df79546 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gta/gtadataset.cpp @@ -0,0 +1,1735 @@ +/****************************************************************************** + * $Id: gtadataset.cpp 28785 2015-03-26 20:46:45Z goatbar $ + * + * Project: GTA read/write Driver + * Purpose: GDAL bindings over GTA library. + * Author: Martin Lambers, marlam@marlam.de + * + ****************************************************************************** + * Copyright (c) 2010, 2011, Martin Lambers + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* + * This driver supports reading and writing GTAs (Generic Tagged Arrays). See + * http://www.nongnu.org/gta/ for details on this format. + * + * Supported Features: + * - CreateCopy(). + * - GTA compression can be set. + * - Raster data is updatable for uncompressed GTAs. + * - All input/output is routed through the VSIF*L functions + * (GDAL_DCAP_VIRTUALIO is set to "YES"). + * - All kinds of metadata are supported (see tag list below). + * + * Limitations: + * - Only uncompressed GTAs can be updated. + * - Only raster data updates are possible; metadata cannot be changed. + * - Color palettes are not supported. + * - CInt16 is stored as gta::cfloat32, and CInt32 as gta::cfloat64. + * - GDAL metadata is assumed to be in UTF-8 encoding, so that no conversion is + * necessary to store it in GTA tags. I'm not sure that this is correct, but + * since some metadata might not be representable in the local encoding (e.g. + * a chinese description in latin1), using UTF-8 seems reasonable. + * + * The following could be implemented, but currently is not: + * - Allow metadata updates by using a special GDAL/METADATA_BUFFER tag that + * contains a number of spaces as a placeholder for additional metadata, so + * that the header size on disk can be kept constant. + * - Implement Create(). + * - Implement AddBand() for uncompressed GTAs. But this would be inefficient: + * the old data would need to be copied to a temporary file, and then copied + * back while extending it with the new band. + * - When strict conversion is requested, store CInt16 in 2 x gta::int16 and + * CInt32 in 2 x gta::int32, and mark these components with special flags so + * that this is reverted when opening the GTA. + * - Support color palettes by storing the palette in special tags. + * + * This driver supports the following standard GTA tags: + * DESCRIPTION + * INTERPRETATION + * NO_DATA_VALUE + * MIN_VALUE + * MAX_VALUE + * UNIT + * + * Additionally, the following tags are used for GDAL-specific metadata: + * GDAL/PROJECTION (WKT) + * GDAL/GEO_TRANSFORM (6 doubles) + * GDAL/OFFSET (1 double) + * GDAL/SCALE (1 double) + * GDAL/GCP_PROJECTION (WKT) + * GDAL/GCP_COUNT (1 int > 0) + * GDAL/GCP%d (5 doubles) + * GDAL/GCP%d_INFO (String) + * GDAL/CATEGORY_COUNT (1 int > 0) + * GDAL/CATEGORY%d (String) + * GDAL/META/DEFAULT/%s (String) + * GDAL/META/RCP/%s (String) + */ + +#include +#include "cpl_port.h" // for snprintf for MSVC +#include +#include "gdal_pam.h" + +CPL_CVSID("$Id: gtadataset.cpp 28785 2015-03-26 20:46:45Z goatbar $"); + +CPL_C_START +void GDALRegister_GTA(void); +CPL_C_END + + +/************************************************************************/ +/* Helper functions */ +/************************************************************************/ + +static void ScanDoubles( const char *pszString, double *padfDoubles, int nCount ) + +{ + char *pszRemainingString = (char *)pszString; + for( int i = 0; i < nCount; i++ ) + { + padfDoubles[i] = 0.0; // fallback value + padfDoubles[i] = CPLStrtod( pszRemainingString, &pszRemainingString ); + } +} + +static CPLString PrintDoubles( const double *padfDoubles, int nCount ) + +{ + CPLString oString; + for( int i = 0; i < nCount; i++ ) + { + oString.FormatC( padfDoubles[i], "%.16g" ); + if( i < nCount - 1) + { + oString += ' '; + } + } + return oString; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GTA custom IO class using GDAL's IO abstraction layer */ +/* ==================================================================== */ +/************************************************************************/ + +class GTAIO : public gta::custom_io +{ + private: + VSILFILE *fp; + + public: + GTAIO( ) throw () + : fp( NULL ) + { + } + ~GTAIO( ) + { + close( ); + } + + int open( const char *pszFilename, const char *pszMode ) + { + fp = VSIFOpenL( pszFilename, pszMode ); + return ( fp == NULL ? -1 : 0 ); + } + + void close( ) + { + if( fp != NULL ) + { + VSIFCloseL( fp ); + fp = NULL; + } + } + + vsi_l_offset tell( ) + { + return VSIFTellL( fp ); + } + + virtual size_t read(void *buffer, size_t size, bool *error) throw () + { + size_t s; + s = VSIFReadL( buffer, 1, size, fp ); + if( s != size ) + { + errno = EIO; + *error = true; + } + return size; + } + + virtual size_t write(const void *buffer, size_t size, bool *error) throw () + { + size_t s; + s = VSIFWriteL( buffer, 1, size, fp ); + if( s != size ) + { + errno = EIO; + *error = true; + } + return size; + } + + virtual bool seekable() throw () + { + return true; + } + + virtual void seek(intmax_t offset, int whence, bool *error) throw () + { + int r; + r = VSIFSeekL( fp, offset, whence ); + if( r != 0 ) + { + errno = EIO; + *error = true; + } + } +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GTADataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GTARasterBand; + +class GTADataset : public GDALPamDataset +{ + friend class GTARasterBand; + + private: + // GTA input/output via VSIF*L functions + GTAIO oGTAIO; + // GTA information + gta::header oHeader; + vsi_l_offset DataOffset; + // Metadata + bool bHaveGeoTransform; + double adfGeoTransform[6]; + int nGCPs; + char *pszGCPProjection; + GDAL_GCP *pasGCPs; + // Cached data block for block-based input/output + int nLastBlockXOff, nLastBlockYOff; + void *pBlock; + + // Block-based input/output of all bands at once. This is used + // by the GTARasterBand input/output functions. + CPLErr ReadBlock( int, int ); + CPLErr WriteBlock( ); + + public: + GTADataset(); + ~GTADataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + + CPLErr GetGeoTransform( double * padfTransform ); + CPLErr SetGeoTransform( double * padfTransform ); + + const char *GetProjectionRef( ); + CPLErr SetProjection( const char *pszProjection ); + + int GetGCPCount( ); + const char *GetGCPProjection( ); + const GDAL_GCP *GetGCPs( ); + CPLErr SetGCPs( int, const GDAL_GCP *, const char * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GTARasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GTARasterBand : public GDALPamRasterBand +{ + friend class GTADataset; + private: + // Size of the component represented by this band + size_t sComponentSize; + // Offset of the component represented by this band inside a GTA element + size_t sComponentOffset; + // StringList for category names + char **papszCategoryNames; + // StringList for metadata + char **papszMetaData; + + public: + GTARasterBand( GTADataset *, int ); + ~GTARasterBand( ); + + CPLErr IReadBlock( int, int, void * ); + CPLErr IWriteBlock( int, int, void * ); + + char **GetCategoryNames( ); + CPLErr SetCategoryNames( char ** ); + + double GetMinimum( int * ); + double GetMaximum( int * ); + + double GetNoDataValue( int * ); + CPLErr SetNoDataValue( double ); + double GetOffset( int * ); + CPLErr SetOffset( double ); + double GetScale( int * ); + CPLErr SetScale( double ); + const char *GetUnitType( ); + CPLErr SetUnitType( const char * ); + GDALColorInterp GetColorInterpretation( ); + CPLErr SetColorInterpretation( GDALColorInterp ); +}; + +/************************************************************************/ +/* GTARasterBand() */ +/************************************************************************/ + +GTARasterBand::GTARasterBand( GTADataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + // Data type + switch( poDS->oHeader.component_type( nBand-1 ) ) + { + case gta::int8: + eDataType = GDT_Byte; + SetMetadataItem("PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE"); + break; + case gta::uint8: + eDataType = GDT_Byte; + break; + case gta::int16: + eDataType = GDT_Int16; + break; + case gta::uint16: + eDataType = GDT_UInt16; + break; + case gta::int32: + eDataType = GDT_Int32; + break; + case gta::uint32: + eDataType = GDT_UInt32; + break; + case gta::float32: + eDataType = GDT_Float32; + break; + case gta::float64: + eDataType = GDT_Float64; + break; + case gta::cfloat32: + eDataType = GDT_CFloat32; + break; + case gta::cfloat64: + eDataType = GDT_CFloat64; + break; + default: + // cannot happen because we checked this in GTADataset::Open() + break; + } + + // Block size + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + // Component information + sComponentSize = poDS->oHeader.component_size( nBand-1 ); + sComponentOffset = 0; + for( int i = 0; i < nBand-1; i++ ) + { + sComponentOffset += poDS->oHeader.component_size( i ); + } + + // Metadata + papszCategoryNames = NULL; + papszMetaData = NULL; + if( poDS->oHeader.component_taglist( nBand-1 ).get( "DESCRIPTION" ) ) + { + SetDescription( poDS->oHeader.component_taglist( nBand-1 ).get( "DESCRIPTION" ) ); + } + for( uintmax_t i = 0; i < poDS->oHeader.component_taglist( nBand-1 ).tags(); i++) + { + const char *pszTagName = poDS->oHeader.component_taglist( nBand-1 ).name( i ); + if( strncmp( pszTagName, "GDAL/META/", 10 ) == 0 ) + { + const char *pDomainEnd = strchr( pszTagName + 10, '/' ); + if( pDomainEnd && pDomainEnd - (pszTagName + 10) > 0 ) + { + char *pszDomain = (char *)VSIMalloc( pDomainEnd - (pszTagName + 10) + 1 ); + if( !pszDomain ) + { + continue; + } + int j; + for( j = 0; j < pDomainEnd - (pszTagName + 10); j++ ) + { + pszDomain[j] = pszTagName[10 + j]; + } + pszDomain[j] = '\0'; + const char *pszName = pszTagName + 10 + j + 1; + const char *pszValue = poDS->oHeader.component_taglist( nBand-1 ).value( i ); + SetMetadataItem( pszName, pszValue, + strcmp( pszDomain, "DEFAULT" ) == 0 ? NULL : pszDomain ); + VSIFree( pszDomain ); + } + } + } +} + +/************************************************************************/ +/* ~GTARasterBand() */ +/************************************************************************/ + +GTARasterBand::~GTARasterBand( ) + +{ + CSLDestroy( papszCategoryNames ); + CSLDestroy( papszMetaData ); +} + +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +char **GTARasterBand::GetCategoryNames( ) + +{ + if( !papszCategoryNames ) + { + GTADataset *poGDS = (GTADataset *) poDS; + const char *pszCatCount = poGDS->oHeader.component_taglist( nBand-1 ).get( "GDAL/CATEGORY_COUNT" ); + int nCatCount = 0; + if( pszCatCount ) + { + nCatCount = atoi( pszCatCount ); + } + if( nCatCount > 0 ) + { + for( int i = 0; i < nCatCount; i++ ) + { + const char *pszCatName = poGDS->oHeader.component_taglist( nBand-1 ).get( + CPLSPrintf( "GDAL/CATEGORY%d", i ) ); + papszCategoryNames = CSLAddString( papszCategoryNames, pszCatName ? pszCatName : "" ); + } + } + } + return papszCategoryNames; +} + +/************************************************************************/ +/* SetCategoryName() */ +/************************************************************************/ + +CPLErr GTARasterBand::SetCategoryNames( char ** ) + +{ + CPLError( CE_Warning, CPLE_NotSupported, + "The GTA driver does not support metadata updates.\n" ); + return CE_Failure; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double GTARasterBand::GetMinimum( int *pbSuccess ) + +{ + GTADataset *poGDS = (GTADataset *) poDS; + const char *pszValue = poGDS->oHeader.component_taglist( nBand-1 ).get( "MIN_VALUE" ); + if( pszValue ) + { + if( pbSuccess ) + *pbSuccess = true; + return CPLAtof( pszValue ); + } + else + { + return GDALRasterBand::GetMinimum( pbSuccess ); + } +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double GTARasterBand::GetMaximum( int *pbSuccess ) + +{ + GTADataset *poGDS = (GTADataset *) poDS; + const char *pszValue = poGDS->oHeader.component_taglist( nBand-1 ).get( "MAX_VALUE" ); + if( pszValue ) + { + if( pbSuccess ) + *pbSuccess = true; + return CPLAtof( pszValue ); + } + else + { + return GDALRasterBand::GetMaximum( pbSuccess ); + } +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double GTARasterBand::GetNoDataValue( int *pbSuccess ) + +{ + GTADataset *poGDS = (GTADataset *) poDS; + const char *pszValue = poGDS->oHeader.component_taglist( nBand-1 ).get( "NO_DATA_VALUE" ); + if( pszValue ) + { + if( pbSuccess ) + *pbSuccess = true; + return CPLAtof( pszValue ); + } + else + { + return GDALRasterBand::GetNoDataValue( pbSuccess ); + } +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr GTARasterBand::SetNoDataValue( double ) + +{ + CPLError( CE_Warning, CPLE_NotSupported, + "The GTA driver does not support metadata updates.\n" ); + return CE_Failure; +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +double GTARasterBand::GetOffset( int *pbSuccess ) + +{ + GTADataset *poGDS = (GTADataset *) poDS; + const char *pszValue = poGDS->oHeader.component_taglist( nBand-1 ).get( "GDAL/OFFSET" ); + if( pszValue ) + { + if( pbSuccess ) + *pbSuccess = true; + return CPLAtof( pszValue ); + } + else + { + return GDALRasterBand::GetOffset( pbSuccess ); + } +} + +/************************************************************************/ +/* SetOffset() */ +/************************************************************************/ + +CPLErr GTARasterBand::SetOffset( double ) + +{ + CPLError( CE_Warning, CPLE_NotSupported, + "The GTA driver does not support metadata updates.\n" ); + return CE_Failure; +} + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ + +double GTARasterBand::GetScale( int *pbSuccess ) + +{ + GTADataset *poGDS = (GTADataset *) poDS; + const char *pszValue = poGDS->oHeader.component_taglist( nBand-1 ).get( "GDAL/SCALE" ); + if( pszValue ) + { + if( pbSuccess ) + *pbSuccess = true; + return CPLAtof( pszValue ); + } + else + { + return GDALRasterBand::GetScale( pbSuccess ); + } +} + +/************************************************************************/ +/* SetScale() */ +/************************************************************************/ + +CPLErr GTARasterBand::SetScale( double ) + +{ + CPLError( CE_Warning, CPLE_NotSupported, + "The GTA driver does not support metadata updates.\n" ); + return CE_Failure; +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +const char *GTARasterBand::GetUnitType( ) + +{ + GTADataset *poGDS = (GTADataset *) poDS; + const char *pszValue = poGDS->oHeader.component_taglist( nBand-1 ).get( "UNIT" ); + return pszValue ? pszValue : ""; +} + +/************************************************************************/ +/* SetUnitType() */ +/************************************************************************/ + +CPLErr GTARasterBand::SetUnitType( const char * ) + +{ + CPLError( CE_Warning, CPLE_NotSupported, + "The GTA driver does not support metadata updates.\n" ); + return CE_Failure; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp GTARasterBand::GetColorInterpretation( ) + +{ + GTADataset *poGDS = (GTADataset *) poDS; + const char *pszColorInterpretation = + poGDS->oHeader.component_taglist( nBand-1 ).get( + "INTERPRETATION" ); + if( pszColorInterpretation ) + { + if( EQUAL( pszColorInterpretation, "GRAY" ) ) + return GCI_GrayIndex ; + else if ( EQUAL( pszColorInterpretation, "RED" ) ) + return GCI_RedBand ; + else if ( EQUAL( pszColorInterpretation, "GREEN" ) ) + return GCI_GreenBand ; + else if ( EQUAL( pszColorInterpretation, "BLUE" ) ) + return GCI_BlueBand ; + else if ( EQUAL( pszColorInterpretation, "ALPHA" ) ) + return GCI_AlphaBand ; + else if ( EQUAL( pszColorInterpretation, "HSL/H" ) ) + return GCI_HueBand ; + else if ( EQUAL( pszColorInterpretation, "HSL/S" ) ) + return GCI_SaturationBand ; + else if ( EQUAL( pszColorInterpretation, "HSL/L" ) ) + return GCI_LightnessBand ; + else if ( EQUAL( pszColorInterpretation, "CMYK/C" ) ) + return GCI_CyanBand ; + else if ( EQUAL( pszColorInterpretation, "CMYK/M" ) ) + return GCI_MagentaBand ; + else if ( EQUAL( pszColorInterpretation, "CMYK/Y" ) ) + return GCI_YellowBand ; + else if ( EQUAL( pszColorInterpretation, "CMYK/K" ) ) + return GCI_BlackBand ; + else if ( EQUAL( pszColorInterpretation, "YCBCR/Y" ) ) + return GCI_YCbCr_YBand; + else if ( EQUAL( pszColorInterpretation, "YCBCR/CB" ) ) + return GCI_YCbCr_CbBand; + else if ( EQUAL( pszColorInterpretation, "YCBCR/CR" ) ) + return GCI_YCbCr_CrBand; + } + return GCI_Undefined; +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +CPLErr GTARasterBand::SetColorInterpretation( GDALColorInterp ) + +{ + CPLError( CE_Warning, CPLE_NotSupported, + "The GTA driver does not support metadata updates.\n" ); + return CE_Failure; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GTARasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + GTADataset *poGDS = (GTADataset *) poDS; + + // Read and cache block containing all bands at once + if( poGDS->ReadBlock( nBlockXOff, nBlockYOff ) != CE_None ) + { + return CE_Failure; + } + + char *pBlock = (char *)poGDS->pBlock; + if( poGDS->oHeader.compression() != gta::none ) + { + // pBlock contains the complete data set. Add the offset into the + // requested block. This assumes that nBlockYSize == 1 and + // nBlockXSize == nRasterXSize. + pBlock += nBlockYOff * nBlockXSize * poGDS->oHeader.element_size(); + } + + // Copy the data for this band from the cached block + for( int i = 0; i < nBlockXSize; i++ ) + { + char *pSrc = pBlock + i * poGDS->oHeader.element_size() + sComponentOffset; + char *pDst = (char *) pImage + i * sComponentSize; + memcpy( (void *) pDst, (void *) pSrc, sComponentSize ); + } + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr GTARasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + GTADataset *poGDS = (GTADataset *) poDS; + + if( poGDS->oHeader.compression() != gta::none ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "The GTA driver cannot update compressed GTAs.\n" ); + return CE_Failure; + } + + // Read and cache block containing all bands at once + if( poGDS->ReadBlock( nBlockXOff, nBlockYOff ) != CE_None ) + { + return CE_Failure; + } + char *pBlock = (char *)poGDS->pBlock; + + // Copy the data for this band into the cached block + for( int i = 0; i < nBlockXSize; i++ ) + { + char *pSrc = (char *) pImage + i * sComponentSize; + char *pDst = pBlock + i * poGDS->oHeader.element_size() + sComponentOffset; + memcpy( (void *) pDst, (void *) pSrc, sComponentSize ); + } + + // Write the block that conatins all bands at once + if( poGDS->WriteBlock( ) != CE_None ) + { + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GTADataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GTADataset() */ +/************************************************************************/ + +GTADataset::GTADataset() + +{ + // Initialize Metadata + bHaveGeoTransform = false; + nGCPs = 0; + pszGCPProjection = NULL; + pasGCPs = NULL; + // Initialize block-based input/output + nLastBlockXOff = -1; + nLastBlockYOff = -1; + pBlock = NULL; +} + +/************************************************************************/ +/* ~GTADataset() */ +/************************************************************************/ + +GTADataset::~GTADataset() + +{ + FlushCache(); + VSIFree( pszGCPProjection ); + for( int i = 0; i < nGCPs; i++ ) + { + VSIFree( pasGCPs[i].pszId ); + VSIFree( pasGCPs[i].pszInfo ); + } + VSIFree( pasGCPs ); + VSIFree( pBlock ); +} + +/************************************************************************/ +/* ReadBlock() */ +/************************************************************************/ + +CPLErr GTADataset::ReadBlock( int nBlockXOff, int nBlockYOff ) + +{ + /* Compressed data sets must be read into memory completely. + * Uncompressed data sets are read block-wise. */ + + if( oHeader.compression() != gta::none ) + { + if( pBlock == NULL ) + { + if( oHeader.data_size() > (size_t)(-1) + || ( pBlock = VSIMalloc( oHeader.data_size() ) ) == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Cannot allocate buffer for the complete data set.\n" + "Try to uncompress the data set to allow block-wise " + "reading.\n" ); + return CE_Failure; + } + + try + { + oHeader.read_data( oGTAIO, pBlock ); + } + catch( gta::exception &e ) + { + CPLError( CE_Failure, CPLE_FileIO, "GTA error: %s\n", e.what() ); + return CE_Failure; + } + } + } + else + { + // This has to be the same as in the RasterBand constructor! + int nBlockXSize = GetRasterXSize(); + int nBlockYSize = 1; + + if( nLastBlockXOff == nBlockXOff && nLastBlockYOff == nBlockYOff ) + return CE_None; + + if( pBlock == NULL ) + { + pBlock = VSIMalloc2( oHeader.element_size(), nBlockXSize ); + if( pBlock == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Cannot allocate scanline buffer" ); + return CE_Failure; + } + } + + try + { + uintmax_t lo[2] = { (uintmax_t)nBlockXOff * nBlockXSize, (uintmax_t)nBlockYOff * nBlockYSize}; + uintmax_t hi[2] = { lo[0] + nBlockXSize - 1, lo[1] + nBlockYSize - 1 }; + oHeader.read_block( oGTAIO, DataOffset, lo, hi, pBlock ); + } + catch( gta::exception &e ) + { + CPLError( CE_Failure, CPLE_FileIO, "GTA error: %s\n", e.what() ); + return CE_Failure; + } + + nLastBlockXOff = nBlockXOff; + nLastBlockYOff = nBlockYOff; + } + return CE_None; +} + +/************************************************************************/ +/* WriteBlock() */ +/************************************************************************/ + +CPLErr GTADataset::WriteBlock( ) + +{ + // This has to be the same as in the RasterBand constructor! + int nBlockXSize = GetRasterXSize(); + int nBlockYSize = 1; + + // Write the block (nLastBlockXOff, nLastBlockYOff) stored in pBlock. + try + { + uintmax_t lo[2] = { (uintmax_t)nLastBlockXOff * nBlockXSize, (uintmax_t)nLastBlockYOff * nBlockYSize}; + uintmax_t hi[2] = { lo[0] + nBlockXSize - 1, lo[1] + nBlockYSize - 1 }; + oHeader.write_block( oGTAIO, DataOffset, lo, hi, pBlock ); + } + catch( gta::exception &e ) + { + CPLError( CE_Failure, CPLE_FileIO, "GTA error: %s\n", e.what() ); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GTADataset::GetGeoTransform( double * padfTransform ) + +{ + if( bHaveGeoTransform ) + { + memcpy( padfTransform, adfGeoTransform, 6*sizeof(double) ); + return CE_None; + } + else + { + return CE_Failure; + } +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr GTADataset::SetGeoTransform( double * ) + +{ + CPLError( CE_Warning, CPLE_NotSupported, + "The GTA driver does not support metadata updates.\n" ); + return CE_Failure; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GTADataset::GetProjectionRef() + +{ + const char *p = oHeader.global_taglist().get("GDAL/PROJECTION"); + return ( p ? p : "" ); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr GTADataset::SetProjection( const char * ) + +{ + CPLError( CE_Warning, CPLE_NotSupported, + "The GTA driver does not support metadata updates.\n" ); + return CE_Failure; +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int GTADataset::GetGCPCount( ) + +{ + return nGCPs; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char * GTADataset::GetGCPProjection( ) + +{ + return pszGCPProjection ? pszGCPProjection : ""; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP * GTADataset::GetGCPs( ) + +{ + return pasGCPs; +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +CPLErr GTADataset::SetGCPs( int, const GDAL_GCP *, const char * ) + +{ + CPLError( CE_Warning, CPLE_NotSupported, + "The GTA driver does not support metadata updates.\n" ); + return CE_Failure; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GTADataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 5 ) + return NULL; + + if( !EQUALN((char *)poOpenInfo->pabyHeader,"GTA",3) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GTADataset *poDS; + + poDS = new GTADataset(); + + if( poDS->oGTAIO.open( poOpenInfo->pszFilename, + poOpenInfo->eAccess == GA_Update ? "r+" : "r" ) != 0 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, "Cannot open file.\n" ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + + try + { + poDS->oHeader.read_from( poDS->oGTAIO ); + } + catch( gta::exception &e ) + { + CPLError( CE_Failure, CPLE_OpenFailed, "GTA error: %s\n", e.what() ); + delete poDS; + return NULL; + } + poDS->DataOffset = poDS->oGTAIO.tell(); + poDS->eAccess = poOpenInfo->eAccess; + + if( poDS->oHeader.compression() != gta::none + && poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GTA driver does not support update access to compressed " + "data sets.\nUncompress the data set first.\n" ); + delete poDS; + return NULL; + } + + if( poDS->oHeader.dimensions() != 2 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GTA driver does not support GTAs with %s than 2 " + "dimensions.\n", + poDS->oHeader.dimensions() < 2 ? "less" : "more" ); + delete poDS; + return NULL; + } + + // We know the dimensions are > 0 (guaranteed by libgta), but they may be + // unrepresentable in GDAL. + if( poDS->oHeader.dimension_size(0) > INT_MAX + || poDS->oHeader.dimension_size(1) > INT_MAX ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GTA driver does not support the size of this data set.\n" ); + delete poDS; + return NULL; + } + poDS->nRasterXSize = poDS->oHeader.dimension_size(0); + poDS->nRasterYSize = poDS->oHeader.dimension_size(1); + + // Check the number of bands (called components in GTA) + if( poDS->oHeader.components() > INT_MAX-1 + || poDS->oHeader.element_size() > ((size_t)-1) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GTA driver does not support the number or size of bands " + "in this data set.\n" ); + delete poDS; + return NULL; + } + poDS->nBands = poDS->oHeader.components(); + + // Check the data types (called component types in GTA) + for( int iBand = 0; iBand < poDS->nBands; iBand++ ) + { + if( poDS->oHeader.component_type(iBand) != gta::uint8 + && poDS->oHeader.component_type(iBand) != gta::int8 + && poDS->oHeader.component_type(iBand) != gta::uint16 + && poDS->oHeader.component_type(iBand) != gta::int16 + && poDS->oHeader.component_type(iBand) != gta::uint32 + && poDS->oHeader.component_type(iBand) != gta::int32 + && poDS->oHeader.component_type(iBand) != gta::float32 + && poDS->oHeader.component_type(iBand) != gta::float64 + && poDS->oHeader.component_type(iBand) != gta::cfloat32 + && poDS->oHeader.component_type(iBand) != gta::cfloat64 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GTA driver does not support some of the data types " + "used in this data set.\n" ); + delete poDS; + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Read and set meta information. */ +/* -------------------------------------------------------------------- */ + + if( poDS->oHeader.global_taglist().get("GDAL/GEO_TRANSFORM") ) + { + poDS->bHaveGeoTransform = true; + ScanDoubles( poDS->oHeader.global_taglist().get( "GDAL/GEO_TRANSFORM" ), + poDS->adfGeoTransform, 6 ); + } + else + { + poDS->bHaveGeoTransform = false; + } + + if( poDS->oHeader.global_taglist().get("GDAL/GCP_PROJECTION") ) + { + poDS->pszGCPProjection = VSIStrdup( poDS->oHeader.global_taglist().get("GDAL/GCP_PROJECTION") ); + } + if( poDS->oHeader.global_taglist().get("GDAL/GCP_COUNT") ) + { + poDS->nGCPs = atoi( poDS->oHeader.global_taglist().get("GDAL/GCP_COUNT") ); + if( poDS->nGCPs < 1 ) + { + poDS->nGCPs = 0; + } + else + { + poDS->pasGCPs = (GDAL_GCP *)VSIMalloc2( poDS->nGCPs, sizeof(GDAL_GCP) ); + if( poDS->pasGCPs == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Cannot allocate GCP list" ); + delete poDS; + return NULL; + } + for( int i = 0; i < poDS->nGCPs; i++ ) + { + poDS->pasGCPs[i].pszInfo = NULL; + poDS->pasGCPs[i].dfGCPPixel = 0.0; + poDS->pasGCPs[i].dfGCPLine = 0.0; + poDS->pasGCPs[i].dfGCPX = 0.0; + poDS->pasGCPs[i].dfGCPY = 0.0; + poDS->pasGCPs[i].dfGCPZ = 0.0; + poDS->pasGCPs[i].pszId = VSIStrdup( CPLSPrintf( "%d", i ) ); + char pszGCPTagName[64]; + char pszGCPInfoTagName[64]; + strcpy( pszGCPTagName, CPLSPrintf( "GDAL/GCP%d", i ) ); + strcpy( pszGCPInfoTagName, CPLSPrintf( "GDAL/GCP%d_INFO", i ) ); + if( poDS->oHeader.global_taglist().get(pszGCPInfoTagName) ) + { + poDS->pasGCPs[i].pszInfo = VSIStrdup( poDS->oHeader.global_taglist().get(pszGCPInfoTagName) ); + } + else + { + poDS->pasGCPs[i].pszInfo = VSIStrdup( "" ); + } + if( poDS->oHeader.global_taglist().get(pszGCPTagName) ) + { + double adfTempDoubles[5]; + ScanDoubles( poDS->oHeader.global_taglist().get(pszGCPTagName), adfTempDoubles, 5 ); + poDS->pasGCPs[i].dfGCPPixel = adfTempDoubles[0]; + poDS->pasGCPs[i].dfGCPLine = adfTempDoubles[1]; + poDS->pasGCPs[i].dfGCPX = adfTempDoubles[2]; + poDS->pasGCPs[i].dfGCPY = adfTempDoubles[3]; + poDS->pasGCPs[i].dfGCPZ = adfTempDoubles[4]; + } + } + } + } + + if( poDS->oHeader.global_taglist().get("DESCRIPTION") ) + { + poDS->SetDescription( poDS->oHeader.global_taglist().get("DESCRIPTION") ); + } + for( uintmax_t i = 0; i < poDS->oHeader.global_taglist().tags(); i++) + { + const char *pszTagName = poDS->oHeader.global_taglist().name( i ); + if( strncmp( pszTagName, "GDAL/META/", 10 ) == 0 ) + { + const char *pDomainEnd = strchr( pszTagName + 10, '/' ); + if( pDomainEnd && pDomainEnd - (pszTagName + 10) > 0 ) + { + char *pszDomain = (char *)VSIMalloc( pDomainEnd - (pszTagName + 10) + 1 ); + if( !pszDomain ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Cannot allocate metadata buffer" ); + delete poDS; + return NULL; + } + int j; + for( j = 0; j < pDomainEnd - (pszTagName + 10); j++ ) + { + pszDomain[j] = pszTagName[10 + j]; + } + pszDomain[j] = '\0'; + const char *pszName = pszTagName + 10 + j + 1; + const char *pszValue = poDS->oHeader.global_taglist().value( i ); + poDS->SetMetadataItem( pszName, pszValue, + strcmp( pszDomain, "DEFAULT" ) == 0 ? NULL : pszDomain ); + VSIFree( pszDomain ); + } + } + } + + if( poDS->nBands > 0 ) + { + poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + } + if( poDS->oHeader.compression() == gta::bzip2 ) + poDS->SetMetadataItem( "COMPRESSION", "BZIP2", "IMAGE_STRUCTURE" ); + else if( poDS->oHeader.compression() == gta::xz ) + poDS->SetMetadataItem( "COMPRESSION", "XZ", "IMAGE_STRUCTURE" ); + else if( poDS->oHeader.compression() == gta::zlib ) + poDS->SetMetadataItem( "COMPRESSION", "ZLIB", "IMAGE_STRUCTURE" ); + else if( poDS->oHeader.compression() == gta::zlib1 ) + poDS->SetMetadataItem( "COMPRESSION", "ZLIB1", "IMAGE_STRUCTURE" ); + else if( poDS->oHeader.compression() == gta::zlib2 ) + poDS->SetMetadataItem( "COMPRESSION", "ZLIB2", "IMAGE_STRUCTURE" ); + else if( poDS->oHeader.compression() == gta::zlib3 ) + poDS->SetMetadataItem( "COMPRESSION", "ZLIB3", "IMAGE_STRUCTURE" ); + else if( poDS->oHeader.compression() == gta::zlib4 ) + poDS->SetMetadataItem( "COMPRESSION", "ZLIB4", "IMAGE_STRUCTURE" ); + else if( poDS->oHeader.compression() == gta::zlib5 ) + poDS->SetMetadataItem( "COMPRESSION", "ZLIB5", "IMAGE_STRUCTURE" ); + else if( poDS->oHeader.compression() == gta::zlib6 ) + poDS->SetMetadataItem( "COMPRESSION", "ZLIB6", "IMAGE_STRUCTURE" ); + else if( poDS->oHeader.compression() == gta::zlib7 ) + poDS->SetMetadataItem( "COMPRESSION", "ZLIB7", "IMAGE_STRUCTURE" ); + else if( poDS->oHeader.compression() == gta::zlib8 ) + poDS->SetMetadataItem( "COMPRESSION", "ZLIB8", "IMAGE_STRUCTURE" ); + else if( poDS->oHeader.compression() == gta::zlib9 ) + poDS->SetMetadataItem( "COMPRESSION", "ZLIB9", "IMAGE_STRUCTURE" ); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < poDS->nBands; iBand++ ) + { + poDS->SetBand( iBand+1, new GTARasterBand( poDS, iBand+1 ) ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +static GDALDataset* +GTACreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a GTA header */ +/* -------------------------------------------------------------------- */ + + gta::compression eGTACompression = gta::none; + const char *pszCompressionValue = CSLFetchNameValue( papszOptions, + "COMPRESS" ); + if( pszCompressionValue != NULL ) + { + if( EQUAL( pszCompressionValue, "NONE" ) ) + eGTACompression = gta::none; + else if( EQUAL( pszCompressionValue, "BZIP2" ) ) + eGTACompression = gta::bzip2; + else if( EQUAL( pszCompressionValue, "XZ" ) ) + eGTACompression = gta::xz; + else if( EQUAL( pszCompressionValue, "ZLIB" )) + eGTACompression = gta::zlib; + else if( EQUAL( pszCompressionValue, "ZLIB1" )) + eGTACompression = gta::zlib1; + else if( EQUAL( pszCompressionValue, "ZLIB2" )) + eGTACompression = gta::zlib2; + else if( EQUAL( pszCompressionValue, "ZLIB3" )) + eGTACompression = gta::zlib3; + else if( EQUAL( pszCompressionValue, "ZLIB4" )) + eGTACompression = gta::zlib4; + else if( EQUAL( pszCompressionValue, "ZLIB5" )) + eGTACompression = gta::zlib5; + else if( EQUAL( pszCompressionValue, "ZLIB6" )) + eGTACompression = gta::zlib6; + else if( EQUAL( pszCompressionValue, "ZLIB7" )) + eGTACompression = gta::zlib7; + else if( EQUAL( pszCompressionValue, "ZLIB8" )) + eGTACompression = gta::zlib8; + else if( EQUAL( pszCompressionValue, "ZLIB9" )) + eGTACompression = gta::zlib9; + else + CPLError( CE_Warning, CPLE_IllegalArg, + "COMPRESS=%s value not recognised, ignoring.", + pszCompressionValue ); + } + + gta::type *peGTATypes = (gta::type *)VSIMalloc2( poSrcDS->GetRasterCount(), sizeof(gta::type) ); + if( peGTATypes == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Cannot allocate GTA type list" ); + return NULL; + } + for( int i = 0; i < poSrcDS->GetRasterCount(); i++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( i+1 ); + if( poSrcBand->GetColorInterpretation() == GCI_PaletteIndex ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GTA driver does not support color palettes.\n" ); + VSIFree( peGTATypes ); + return NULL; + } + GDALDataType eDT = poSrcBand->GetRasterDataType(); + switch( eDT ) + { + case GDT_Byte: + { + const char *pszPixelType = poSrcBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + if (pszPixelType && EQUAL(pszPixelType, "SIGNEDBYTE")) + peGTATypes[i] = gta::int8; + else + peGTATypes[i] = gta::uint8; + break; + } + case GDT_UInt16: + peGTATypes[i] = gta::uint16; + break; + case GDT_Int16: + peGTATypes[i] = gta::int16; + break; + case GDT_UInt32: + peGTATypes[i] = gta::uint32; + break; + case GDT_Int32: + peGTATypes[i] = gta::int32; + break; + case GDT_Float32: + peGTATypes[i] = gta::float32; + break; + case GDT_Float64: + peGTATypes[i] = gta::float64; + break; + case GDT_CInt16: + if( bStrict ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GTA driver does not support the CInt16 data " + "type.\n" + "(If no strict copy is required, the driver can " + "use CFloat32 instead.)\n" ); + VSIFree( peGTATypes ); + return NULL; + } + peGTATypes[i] = gta::cfloat32; + break; + case GDT_CInt32: + if( bStrict ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GTA driver does not support the CInt32 data " + "type.\n" + "(If no strict copy is required, the driver can " + "use CFloat64 instead.)\n" ); + VSIFree( peGTATypes ); + return NULL; + } + peGTATypes[i] = gta::cfloat64; + break; + case GDT_CFloat32: + peGTATypes[i] = gta::cfloat32; + break; + case GDT_CFloat64: + peGTATypes[i] = gta::cfloat64; + break; + default: + CPLError( CE_Failure, CPLE_NotSupported, + "The GTA driver does not support source data sets using " + "unknown data types.\n"); + VSIFree( peGTATypes ); + return NULL; + } + } + + gta::header oHeader; + try + { + oHeader.set_compression( eGTACompression ); + oHeader.set_dimensions( poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize() ); + oHeader.set_components( poSrcDS->GetRasterCount(), peGTATypes ); + const char *pszDescription = poSrcDS->GetDescription(); + // Metadata from GDALMajorObject + if( pszDescription && pszDescription[0] != '\0' ) + { + oHeader.global_taglist().set( "DESCRIPTION", pszDescription ); + } + const char *papszMetadataDomains[] = { NULL /* default */, "RPC" }; + size_t nMetadataDomains = sizeof( papszMetadataDomains ) / sizeof( papszMetadataDomains[0] ); + for( size_t iDomain = 0; iDomain < nMetadataDomains; iDomain++ ) + { + char **papszMetadata = poSrcDS->GetMetadata( papszMetadataDomains[iDomain] ); + if( papszMetadata ) + { + for( int i = 0; papszMetadata[i]; i++ ) + { + char *pEqualSign = strchr( papszMetadata[i], '=' ); + if( pEqualSign && pEqualSign - papszMetadata[i] > 0 ) + { + *pEqualSign = '\0'; + oHeader.global_taglist().set( + CPLSPrintf( "GDAL/META/%s/%s", + papszMetadataDomains[iDomain] ? papszMetadataDomains[iDomain] : "DEFAULT", + papszMetadata[i] ), + pEqualSign + 1 ); + *pEqualSign = '='; + } + } + } + } + // Projection and transformation + const char *pszWKT = poSrcDS->GetProjectionRef(); + if( pszWKT && pszWKT[0] != '\0' ) + { + oHeader.global_taglist().set( "GDAL/PROJECTION", pszWKT ); + } + double adfTransform[6]; + if( poSrcDS->GetGeoTransform( adfTransform ) == CE_None ) + { + oHeader.global_taglist().set( "GDAL/GEO_TRANSFORM", + PrintDoubles( adfTransform, 6 ).c_str() ); + } + // GCPs + if( poSrcDS->GetGCPCount() > 0 ) + { + oHeader.global_taglist().set( "GDAL/GCP_COUNT", CPLSPrintf( "%d", poSrcDS->GetGCPCount() ) ); + oHeader.global_taglist().set( "GDAL/GCP_PROJECTION", poSrcDS->GetGCPProjection() ); + const GDAL_GCP *pasGCPs = poSrcDS->GetGCPs(); + for( int i = 0; i < poSrcDS->GetGCPCount(); i++ ) + { + char pszGCPTagName[64]; + char pszGCPInfoTagName[64]; + strcpy( pszGCPTagName, CPLSPrintf( "GDAL/GCP%d", i ) ); + strcpy( pszGCPInfoTagName, CPLSPrintf( "GDAL/GCP%d_INFO", i ) ); + if( pasGCPs[i].pszInfo && pasGCPs[i].pszInfo[0] != '\0' ) + { + oHeader.global_taglist().set( pszGCPInfoTagName, pasGCPs[i].pszInfo ); + } + double adfTempDoubles[5]; + adfTempDoubles[0] = pasGCPs[i].dfGCPPixel; + adfTempDoubles[1] = pasGCPs[i].dfGCPLine; + adfTempDoubles[2] = pasGCPs[i].dfGCPX; + adfTempDoubles[3] = pasGCPs[i].dfGCPY; + adfTempDoubles[4] = pasGCPs[i].dfGCPZ; + oHeader.global_taglist().set( pszGCPTagName, PrintDoubles( adfTempDoubles, 5 ).c_str() ); + } + } + // Now the bands + for( int iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + // Metadata from GDALMajorObject + const char *pszBandDescription = poSrcBand->GetDescription(); + if( pszBandDescription && pszBandDescription[0] != '\0' ) + { + oHeader.component_taglist( iBand ).set( "DESCRIPTION", pszBandDescription ); + } + for( size_t iDomain = 0; iDomain < nMetadataDomains; iDomain++ ) + { + char **papszBandMetadata = poSrcBand->GetMetadata( papszMetadataDomains[iDomain] ); + if( papszBandMetadata ) + { + for( int i = 0; papszBandMetadata[i]; i++ ) + { + char *pEqualSign = strchr( papszBandMetadata[i], '=' ); + if( pEqualSign && pEqualSign - papszBandMetadata[i] > 0 ) + { + *pEqualSign = '\0'; + oHeader.component_taglist( iBand ).set( + CPLSPrintf( "GDAL/META/%s/%s", + papszMetadataDomains[iDomain] ? papszMetadataDomains[iDomain] : "DEFAULT", + papszBandMetadata[i] ), + pEqualSign + 1 ); + *pEqualSign = '='; + } + } + } + } + // Category names + char **papszCategoryNames = poSrcBand->GetCategoryNames( ); + if( papszCategoryNames ) + { + int i; + for( i = 0; papszCategoryNames[i]; i++ ) + { + oHeader.component_taglist( iBand ).set( + CPLSPrintf( "GDAL/CATEGORY%d", i ), + papszCategoryNames[i] ); + } + oHeader.component_taglist( iBand ).set( + "GDAL/CATEGORY_COUNT", CPLSPrintf( "%d", i ) ); + } + // No data value + int bHaveNoDataValue; + double dfNoDataValue; + dfNoDataValue = poSrcBand->GetNoDataValue( &bHaveNoDataValue ); + if( bHaveNoDataValue ) + oHeader.component_taglist( iBand ).set( "NO_DATA_VALUE", + PrintDoubles( &dfNoDataValue, 1 ).c_str() ); + // Min/max values + int bHaveMinValue; + double dfMinValue; + dfMinValue = poSrcBand->GetMinimum( &bHaveMinValue ); + if( bHaveMinValue ) + oHeader.component_taglist( iBand ).set( "MIN_VALUE", + PrintDoubles( &dfMinValue, 1 ).c_str() ); + int bHaveMaxValue; + double dfMaxValue; + dfMaxValue = poSrcBand->GetMaximum( &bHaveMaxValue ); + if( bHaveMaxValue ) + oHeader.component_taglist( iBand ).set( "MAX_VALUE", + PrintDoubles( &dfMaxValue, 1 ).c_str() ); + // Offset/scale values + int bHaveOffsetValue; + double dfOffsetValue; + dfOffsetValue = poSrcBand->GetOffset( &bHaveOffsetValue ); + if( bHaveOffsetValue ) + oHeader.component_taglist( iBand ).set( "GDAL/OFFSET", + PrintDoubles( &dfOffsetValue, 1 ).c_str() ); + int bHaveScaleValue; + double dfScaleValue; + dfScaleValue = poSrcBand->GetScale( &bHaveScaleValue ); + if( bHaveScaleValue ) + oHeader.component_taglist( iBand ).set( "GDAL/SCALE", + PrintDoubles( &dfScaleValue, 1 ).c_str() ); + // Unit + const char *pszUnit = poSrcBand->GetUnitType( ); + if( pszUnit != NULL && pszUnit[0] != '\0' ) + oHeader.component_taglist( iBand ).set( "UNIT", pszUnit ); + // Color interpretation + GDALColorInterp eCI = poSrcBand->GetColorInterpretation(); + if( eCI == GCI_GrayIndex ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "GRAY" ); + else if( eCI == GCI_RedBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "RED" ); + else if( eCI == GCI_GreenBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "GREEN" ); + else if( eCI == GCI_BlueBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "BLUE" ); + else if( eCI == GCI_AlphaBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "ALPHA" ); + else if( eCI == GCI_HueBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "HSL/H" ); + else if( eCI == GCI_SaturationBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "HSL/S" ); + else if( eCI == GCI_LightnessBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "HSL/L" ); + else if( eCI == GCI_CyanBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "CMYK/C" ); + else if( eCI == GCI_MagentaBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "CMYK/M" ); + else if( eCI == GCI_YellowBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "CMYK/Y" ); + else if( eCI == GCI_BlackBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "CMYK/K" ); + else if( eCI == GCI_YCbCr_YBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "YCBCR/Y" ); + else if( eCI == GCI_YCbCr_CbBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "YCBCR/CB" ); + else if( eCI == GCI_YCbCr_CrBand ) + oHeader.component_taglist( iBand ).set( "INTERPRETATION", "YCBCR/CR" ); + } + } + catch( gta::exception &e ) + { + CPLError( CE_Failure, CPLE_NotSupported, "GTA error: %s\n", e.what() ); + VSIFree( peGTATypes ); + return NULL; + } + VSIFree( peGTATypes ); + +/* -------------------------------------------------------------------- */ +/* Write header and data to the file */ +/* -------------------------------------------------------------------- */ + + GTAIO oGTAIO; + if( oGTAIO.open( pszFilename, "w" ) != 0 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Cannot create GTA file %s.\n", pszFilename ); + return NULL; + } + + void *pLine = VSIMalloc2( oHeader.element_size(), oHeader.dimension_size(0) ); + if( pLine == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Cannot allocate scanline buffer.\n" ); + VSIFree( pLine ); + return NULL; + } + + try + { + // Write header + oHeader.write_to( oGTAIO ); + // Write data line by line + gta::io_state oGTAIOState; + for( int iLine = 0; iLine < poSrcDS->GetRasterYSize(); iLine++ ) + { + size_t nComponentOffset = 0; + for( int iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + GDALDataType eDT = poSrcBand->GetRasterDataType(); + if( eDT == GDT_CInt16 ) + { + eDT = GDT_CFloat32; + } + else if( eDT == GDT_CInt32 ) + { + eDT = GDT_CFloat64; + } + char *pDst = (char *)pLine + nComponentOffset; + CPLErr eErr = poSrcBand->RasterIO( GF_Read, 0, iLine, + poSrcDS->GetRasterXSize(), 1, + pDst, poSrcDS->GetRasterXSize(), 1, eDT, + oHeader.element_size(), 0, NULL ); + if( eErr != CE_None ) + { + CPLError( CE_Failure, CPLE_FileIO, "Cannot read source data set.\n" ); + VSIFree( pLine ); + return NULL; + } + nComponentOffset += oHeader.component_size( iBand ); + } + oHeader.write_elements( oGTAIOState, oGTAIO, poSrcDS->GetRasterXSize(), pLine ); + if( !pfnProgress( (iLine+1) / (double) poSrcDS->GetRasterYSize(), + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated CreateCopy()" ); + VSIFree( pLine ); + return NULL; + } + } + } + catch( gta::exception &e ) + { + CPLError( CE_Failure, CPLE_FileIO, "GTA write error: %s\n", e.what() ); + VSIFree( pLine ); + return NULL; + } + VSIFree( pLine ); + + oGTAIO.close(); + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + + GTADataset *poDS = (GTADataset *) GDALOpen( pszFilename, + eGTACompression == gta::none ? GA_Update : GA_ReadOnly ); + + if( poDS ) + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_GTA() */ +/************************************************************************/ + +void GDALRegister_GTA() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GTA" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GTA" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Generic Tagged Arrays (.gta)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_gta.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gta" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16 Int16 UInt32 Int32 Float32 Float64 " + "CInt16 CInt32 CFloat32 CFloat64" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, + "" + " " + "" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = GTADataset::Open; + poDriver->pfnCreateCopy = GTACreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/gta/makefile.vc b/bazaar/plugin/gdal/frmts/gta/makefile.vc new file mode 100644 index 000000000..442ad121d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gta/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = gtadataset.obj + +EXTRAFLAGS = $(GTA_CFLAGS) + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/gtiff/GNUmakefile b/bazaar/plugin/gdal/frmts/gtiff/GNUmakefile new file mode 100644 index 000000000..6a8bf9c11 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/GNUmakefile @@ -0,0 +1,63 @@ + +include ../../GDALmake.opt + +OBJ = geotiff.o gt_wkt_srs.o gt_citation.o gt_overview.o \ + tif_float.o tifvsi.o gt_jpeg_copy.o + +SUBLIBS = + +ifeq ($(TIFF_SETTING),internal) +SUBLIBS := lib-tiff $(SUBLIBS) +TIFF_OPTS := -DINTERNAL_LIBTIFF -Ilibtiff $(TIFF_OPTS) +ifeq ($(RENAME_INTERNAL_LIBTIFF_SYMBOLS),yes) +TIFF_OPTS := -DRENAME_INTERNAL_LIBTIFF_SYMBOLS $(TIFF_OPTS) +endif +endif + +ifeq ($(GEOTIFF_SETTING),internal) +SUBLIBS := lib-geotiff $(SUBLIBS) +TIFF_OPTS := -DINTERNAL_LIBGEOTIFF -Ilibgeotiff $(TIFF_OPTS) +ifeq ($(RENAME_INTERNAL_LIBGEOTIFF_SYMBOLS),yes) +TIFF_OPTS := -DRENAME_INTERNAL_LIBGEOTIFF_SYMBOLS $(TIFF_OPTS) +endif +endif + +JPEG_FLAGS = + +ifneq ($(JPEG_SETTING),no) +JPEG_FLAGS := $(JPEG_FLAGS) -I../jpeg -DHAVE_LIBJPEG +endif + +ifeq ($(JPEG_SETTING),internal) +JPEG_FLAGS := $(JPEG_FLAGS) -I../jpeg/libjpeg +endif + +CPPFLAGS := -I.. $(JPEG_FLAGS) $(GEOTIFF_INCLUDE) $(TIFF_OPTS) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) $(SUBLIBS) + +clean: + rm -f *.o $(O_OBJ) + (cd libtiff; $(MAKE) clean) + (cd libgeotiff; $(MAKE) clean) + +gt_test: gt_test.$(OBJ_EXT) gt_gs.$(OBJ_EXT) cpl_csv.$(OBJ_EXT) + $(CC) gt_test.$(OBJ_EXT) gt_gs.$(OBJ_EXT) cpl_csv.$(OBJ_EXT) ../../port/*.$(OBJ_EXT) \ + libgeotiff/libgeotiff.a libtiff/libtiff.a $(LIBS) -o gt_test + +gt_write: gt_write.$(OBJ_EXT) gt_gs.$(OBJ_EXT) cpl_csv.$(OBJ_EXT) + $(CC) gt_write.$(OBJ_EXT) gt_gs.$(OBJ_EXT) cpl_csv.$(OBJ_EXT) ../../port/*.$(OBJ_EXT) \ + libgeotiff/libgeotiff.a libtiff/libtiff.a $(LIBS) -o gt_write + +epsg_to_wkt: epsg_to_wkt.$(OBJ_EXT) gt_wkt_srs.$(OBJ_EXT) + $(CXX) epsg_to_wkt.$(OBJ_EXT) gt_wkt_srs.$(OBJ_EXT) ../../port/*.$(OBJ_EXT) \ + libgeotiff/libgeotiff.a libtiff/libtiff.a \ + $(GDAL_LIB) $(LIBS) -o epsg_to_wkt + +lib-tiff: + (cd libtiff; $(MAKE) install-obj) + +lib-geotiff: + (cd libgeotiff; $(MAKE) install-obj) + +install-obj: $(SUBLIBS) $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/gtiff/frmt_gtiff.html b/bazaar/plugin/gdal/frmts/gtiff/frmt_gtiff.html new file mode 100644 index 000000000..16eb6b527 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/frmt_gtiff.html @@ -0,0 +1,423 @@ + + + + +GTiff -- GeoTIFF File Format + + + + +

GTiff -- GeoTIFF File Format

+ +

Most forms of TIFF and GeoTIFF files are supported by GDAL for reading, and +somewhat less varieties can be written.

+ +

When built with internal libtiff or with libtiff >= 4.0, GDAL also supports +reading and writing BigTIFF files (evolution of the TIFF format to support files +larger than 4 GB).

+ +

Currently band types of Byte, UInt16, Int16, UInt32, Int32, Float32, Float64, +CInt16, CInt32, CFloat32 and CFloat64 are supported for +reading and writing. +Paletted images will return palette information associated with +the band. The compression formats listed below should be supported for +reading as well.

+ +

As well, one bit files, and some other unusual formulations of GeoTIFF file, +such as YCbCr color model files, are automatically translated into RGBA +(red, green, blue, alpha) form, and treated as four eight bit bands.

+ +

Georeferencing

+ +

Most GeoTIFF projections should be supported, with the caveat that in order +to translate uncommon Projected, and Geographic coordinate systems into +OGC WKT it is necessary to have the EPSG .csv files available. They must +be found at the location pointed to by the GEOTIFF_CSV environment variable. +

+ +

Georeferencing from GeoTIFF is supported in the form of one tiepoint and +pixel size, a transformation matrix, or a list of GCPs.

+ +

If no georeferencing +information is available in the TIFF file itself, GDAL will also check for, +and use an ESRI world file with the +extension .tfw, .tifw/.tiffw or .wld, as well as a MapInfo .tab file.

+ +

GDAL can read and write the RPCCoefficientTag as described in the +RPCs in GeoTIFF proposed extension. The tag is written only for +files created with the default profile GDALGeoTIFF. For other profiles, a .RPB file +is created. In GDAL data model, the RPC coefficients are stored into the RPC metadata domain. +For more details, see the RPC Georeferencing RFC. +If .RPB or _RPC.TXT files are found, they will be used to read the RPCs, even if the RPCCoefficientTag tag is set. +

+ +

Internal nodata masks

+ +

(from GDAL 1.6.0)

+ +

TIFF files can contain internal transparency masks. The GeoTIFF driver +recognizes an internal directory as being a transparency mask when the +FILETYPE_MASK bit value is set on the TIFFTAG_SUBFILETYPE tag. +According to the TIFF specification, such internal transparency masks +contain 1 sample of 1-bit data. Although the TIFF specification allows +for higher resolutions for the internal transparency mask, the GeoTIFF +driver only supports internal transparency masks of the same dimensions +as the main image. Transparency masks of internal overviews are also +supported.

+ +

When the GDAL_TIFF_INTERNAL_MASK configuration option is set to YES and +the GeoTIFF file is opened in update mode, the CreateMaskBand() method +on a TIFF dataset or rasterband will create an internal transparency mask. +Otherwise, the default behaviour of nodata mask creation will be used, +that is to say the creation of a .msk file, as per +RFC 15

+ +

Starting with GDAL 1.8.0, 1-bit internal mask band are deflate compressed. +When reading them back, to make conversion between mask band and alpha band easier, +mask bands are exposed to the user as being promoted to full 8 bits (i.e. the +value for unmasked pixels is 255) unless the GDAL_TIFF_INTERNAL_MASK_TO_8BIT +configuration option is set to NO. This does not affect the way the mask band is +written (it is always 1-bit).

+ +

Overviews

+ +

The GeoTIFF driver supports reading, creation and update of internal overviews. +Internal overviews can be created on GeoTIFF files opened in update mode +(with gdaladdo for instance). If the GeoTIFF file is opened as read only, +the creation of overviews will be done in an external .ovr file. +Overview are only updated on request with the BuildOverviews() method.

+ +

(From GDAL 1.6.0) If a GeoTIFF file has a transparency mask and the GDAL_TIFF_INTERNAL_MASK +environment variable is set to YES and the GeoTIFF file is opened in update mode, +BuildOverviews() will automatically create overviews for the internal transparency mask. +These overviews will be refreshed by further calls to BuildOverviews() even if +GDAL_TIFF_INTERNAL_MASK is not set to YES.

+ +

(From GDAL 1.8.0) The block size (tile width and height) used for overviews +(internal or external) can be specified by setting the GDAL_TIFF_OVR_BLOCKSIZE +environment variable to a power-of-two value between 64 and 4096. The default value is 128.

+ +

Metadata

+ +

GDAL can deal with the following baseline TIFF tags as dataset-level metadata :

+
    +
  • TIFFTAG_DOCUMENTNAME
  • +
  • TIFFTAG_IMAGEDESCRIPTION
  • +
  • TIFFTAG_SOFTWARE
  • +
  • TIFFTAG_DATETIME
  • +
  • TIFFTAG_ARTIST
  • +
  • TIFFTAG_HOSTCOMPUTER
  • +
  • TIFFTAG_COPYRIGHT
  • +
  • TIFFTAG_XRESOLUTION
  • +
  • TIFFTAG_YRESOLUTION
  • +
  • TIFFTAG_RESOLUTIONUNIT
  • +
  • TIFFTAG_MINSAMPLEVALUE (read only)
  • +
  • TIFFTAG_MAXSAMPLEVALUE (read only)
  • +
+ +

The name of the metadata item to use is one of the above names ("TIFFTAG_DOCUMENTNAME", ...).

+ +

Other non standard metadata items can be stored in a TIFF file created with the profile GDALGeoTIFF +(the default, see below in the Creation issues section). Those metadata items are grouped together +into a XML string stored in the non standard TIFFTAG_GDAL_METADATA ASCII tag (code 42112). When BASELINE +or GeoTIFF profile are used, those non standard metadata items are stored into a PAM .aux.xml file.

+ +

The value of GDALMD_AREA_OR_POINT ("AREA_OR_POINT") metadata item is stored in the GeoTIFF key +RasterPixelIsPoint for GDALGeoTIFF or GeoTIFF profiles.

+ +

Starting with GDAL 1.9.0, XMP metadata can be extracted from the file, and will be +stored as XML raw content in the xml:XMP metadata domain.

+ +

Starting with GDAL 1.10, EXIF metadata can be extracted from the file, and will be +stored the EXIF metadata domain.

+ +

Color Profile Metadata

+ +

Starting with GDAL 1.11, GDAL can deal with the following color profile metadata in the COLOR_PROFILE domain:

+
    +
  • SOURCE_ICC_PROFILE (Base64 encoded ICC profile embedded in file. If available, other tags are ignored.)
  • +
  • SOURCE_PRIMARIES_RED (xyY in "x,y,1" format for red primary.)
  • +
  • SOURCE_PRIMARIES_GREEN (xyY in "x,y,1" format for green primary)
  • +
  • SOURCE_PRIMARIES_BLUE (xyY in "x,y,1" format for blue primary)
  • +
  • SOURCE_WHITEPOINT (xyY in "x,y,1" format for whitepoint)
  • +
  • TIFFTAG_TRANSFERFUNCTION_RED (Red table of TIFFTAG_TRANSFERFUNCTION)
  • +
  • TIFFTAG_TRANSFERFUNCTION_GREEN (Green table of TIFFTAG_TRANSFERFUNCTION)
  • +
  • TIFFTAG_TRANSFERFUNCTION_BLUE (Blue table of TIFFTAG_TRANSFERFUNCTION)
  • +
  • TIFFTAG_TRANSFERRANGE_BLACK (Min range of TIFFTAG_TRANSFERRANGE)
  • +
  • TIFFTAG_TRANSFERRANGE_WHITE (Max range of TIFFTAG_TRANSFERRANGE)
  • +
+ +

Note that these metadata properties can only be used on the original raw pixel data. If automatic conversion to RGB has been done, the color profile information cannot be used.

+ +

All these metadata tags can be overriden and/or used as creation options.

+ +

Nodata value

+ +

GDAL stores band nodata value in the non standard TIFFTAG_GDAL_NODATA ASCII tag (code 42113) for +files created with the default profile GDALGeoTIFF. Note that all bands must use the same nodata value. +When BASELINE or GeoTIFF profile are used, the nodata value is stored into a PAM .aux.xml file.

+ +

Creation Issues

+ +

GeoTIFF files can be created with any GDAL defined band type, including +the complex types. Created files may have any number of bands. Files +with exactly 3 bands will be +given a photometric interpretation of RGB, files with exactly four bands +will have a photometric interpretation of RGBA, while all other combinations +will have a photometric interpretation of MIN_IS_WHITE. Files with +pseudo-color tables, or GCPs can currently only be created when creating from +an existing GDAL dataset with those objects (GDALDriver::CreateCopy()).

+ +

Note that the GeoTIFF format does not support parametric description of datums, +so TOWGS84 parameters in coordinate systems are lost in GeoTIFF format.

+ +

Creation Options

+ +
    + +
  • TFW=YES: Force the generation of an associated ESRI world +file (.tfw).See a World Files section +for details.

  • + +
  • RPB=YES: Force the generation of an associated .RPB file to +describe RPC (Rational Polynomial Coefficients), if RPC information is available. +If not specified, this file is automatically generated if there's RPC information +and that the PROFILE is not the default GDALGeoTIFF.

  • + +
  • RPCTXT=YES: (GDAL >=2.0) Force the generation of an associated +_RPC.TXT file to describe RPC (Rational Polynomial Coefficients), +if RPC information is available.

  • + +
  • INTERLEAVE=[BAND,PIXEL]: By default TIFF files with pixel +interleaving (PLANARCONFIG_CONTIG in TIFF terminology) are created. These +are slightly less efficient than BAND interleaving for some purposes, but +some applications only support pixel interleaved TIFF files.

  • + +
  • TILED=YES: By default stripped TIFF files are created. This +option can be used to force creation of tiled TIFF files.

  • + +
  • BLOCKXSIZE=n: Sets tile width, defaults to 256.

  • + +
  • BLOCKYSIZE=n: Set tile or strip height. Tile height defaults to +256, strip height defaults to a value such that one strip is 8K or less.

  • + +
  • NBITS=n: Create a file with less than 8 bits per sample by passing a value from 1 to 7. The apparent pixel type should be Byte. From GDAL 1.6.0, values of n=9...15 (UInt16 type) and n=17...31 (UInt32 type) are also accepted.

  • + +
  • COMPRESS=[JPEG/LZW/PACKBITS/DEFLATE/CCITTRLE/CCITTFAX3/CCITTFAX4/NONE]: +Set the compression to use. JPEG should generally only be used with Byte data (8 bit per channel). +But starting with GDAL 1.7.0 and provided that GDAL is built with internal libtiff and libjpeg, +it is possible to read and write TIFF files with 12bit JPEG compressed TIFF files (seen as UInt16 bands with NBITS=12). +See the "8 and 12 bit JPEG in TIFF" wiki page for more details. +The CCITT compression should only be used with 1bit (NBITS=1) data. +LZW and DEFLATE compressions can be used with the PREDICTOR creation option. +None is the default.

  • + +
  • PREDICTOR=[1/2/3]: Set the predictor for LZW or DEFLATE compression. The default is 1 (no predictor), 2 is horizontal differencing and 3 is floating point prediction.

  • + +
  • DISCARD_LSB=nbits or nbits_band1,nbits_band2,...nbits_bandN: (GDAL >= 2.0) +Set the number of least-significant bits to clear, possibly different per band. +Lossy compression scheme to be best used with PREDICTOR=2 and LZW/DEFLATE compression.

  • + +
  • SPARSE_OK=TRUE/FALSE (From GDAL 1.6.0): Should newly created files be allowed to be sparse? Sparse files have 0 tile/strip offsets for blocks never written and save space; however, most non-GDAL packages cannot read such files. The default is FALSE.

  • + +
  • JPEG_QUALITY=[1-100]: Set the JPEG quality when using JPEG compression. A value of 100 is best quality (least compression), and 1 is worst quality (best compression). The default is 75.

  • + +
  • JPEGTABLESMODE=0/1/2/3: (From GDAL 2.0) Configure how and where JPEG quantization and Huffman tables are written in the TIFF JpegTables tag and strip/tile. Default to 1.

    +
      +
    • 0: JpegTables is not written. Each strip/tile contains its own quantization tables and use optimized Huffman coding.
    • +
    • 1: JpegTables is written with only the quantization tables. Each strip/tile refers to those quantized tables and use optimized Huffman coding. +This is generally the optimal choice for smallest file size, and consequently is the default.
    • +
    • 2: JpegTables is written with only the default Huffman tables. Each strip/tile refers to those Huffman tables + (thus no optimized Huffman coding) and contains its own quantization tables (identical). This option has no anticipated practical value.
    • +
    • 3: JpegTables is written with the quantization and default Huffman tables. Each strip/tile refers to those tables + (thus no optimized Huffman coding). This option could perhaps with some data be more efficient than 1, but this should only occur in rare circumstances.
    • +
    +
  • + +
  • ZLEVEL=[1-9]: Set the level of compression when using DEFLATE compression. A value of 9 is best, and 1 is least compression. The default is 6.

  • + +
  • PHOTOMETRIC=[MINISBLACK/MINISWHITE/RGB/CMYK/YCBCR/CIELAB/ICCLAB/ITULAB]: +Set the photometric interpretation tag. Default is MINISBLACK, but if the +input image has 3 or 4 bands of Byte type, then RGB will be selected. You can +override default photometric using this option.

  • + +
  • ALPHA=[YES/NON-PREMULTIPLIED/PREMULTIPLIED/UNSPECIFIED]: +The first "extrasample" is marked as being alpha if +there are any extra samples. This is necessary if you want to produce +a greyscale TIFF file with an alpha band (for instance). +For GDAL < 1.10, only the YES value is supported, and it is then assumed as being PREMULTIPLIED alpha +(ASSOCALPHA in TIFF). Starting with GDAL 1.10, YES is an alias for NON-PREMULTIPLIED alpha, and +the other values can be used.

  • + +
  • PROFILE=[GDALGeoTIFF/GeoTIFF/BASELINE]: Control what non-baseline +tags are emitted by GDAL.

    +
      +
    • With GDALGeoTIFF (the default) various GDAL custom tags may be written.
    • +
    • With GeoTIFF only GeoTIFF tags will be added to the baseline.
    • +
    • With BASELINE no GDAL or GeoTIFF tags will be written. BASELINE is occasionally useful when writing files to +be read by applications intolerant of unrecognised tags.
    • +
    +
  • + +
  • BIGTIFF=YES/NO/IF_NEEDED/IF_SAFER: Control whether the created file is a BigTIFF or a classic TIFF.

    +
      +
    • YES forces BigTIFF.
    • +
    • NO forces classic TIFF.
    • +
    • IF_NEEDED will only create a BigTIFF if it is clearly needed (uncompressed, and image larger than 4GB).
    • +
    • IF_SAFER will create BigTIFF if the resulting file *might* exceed 4GB.
    • +
    +

    BigTIFF is a TIFF variant which can contain more than 4GiB of data (size of classic TIFF is limited by that value). This option is available if GDAL is built with libtiff library version 4.0 or higher (which is the case of the internal libtiff version from GDAL >= 1.5.0). The default is IF_NEEDED. (IF_NEEDED and IF_SAFER are available from GDAL 1.6.0).

    +

    When creating a new GeoTIFF with no compression, GDAL computes in advance the +size of the resulting file. If that computed file size is over 4GiB, GDAL will automatically +decide to create a BigTIFF file. However, when compression is used, it is not possible in +advance to known the final size of the file, so classical TIFF will be chosen. In +that case, the user must explicitly require the creation of a BigTIFF with BIGTIFF=YES +if the final file is anticipated to be too big for classical TIFF format. +If BigTIFF creation is not explicitly asked or guessed and the resulting file is too big for classical TIFF, +libtiff will fail with an error message like "TIFFAppendToStrip:Maximum TIFF file size exceeded".

  • + +
  • PIXELTYPE=[DEFAULT/SIGNEDBYTE]: By setting this to SIGNEDBYTE, a +new Byte file can be forced to be written as signed byte.

  • + + + +
  • COPY_SRC_OVERVIEWS=[YES/NO]: (GDAL >= 1.8.0, CreateCopy() only) By setting this to YES (default is NO), the potential existing overviews +of the source dataset will be copied to the target dataset without being recomputed. If overviews of mask band +also exist, provided that the GDAL_TIFF_INTERNAL_MASK configuration option is set to YES, they will also be copied. +Note that this creation option will have no effect if general options +(i.e. options which are not creation options) of gdal_translate are used.

  • + +
+ +

About JPEG compression of RGB images

+ +

When translating a RGB image to JPEG-In-TIFF, using PHOTOMETRIC=YCBCR can make the size +of the image typically 2 to 3 times smaller than the default photometric value (RGB). +When using PHOTOMETRIC=YCBCR, the INTERLEAVE option must be kept to its default value (PIXEL), +otherwise libtiff will fail to compress the data.

+

+Note also that the dimensions of the tiles or strips must be a multiple of 8 for PHOTOMETRIC=RGB or 16 for PHOTOMETRIC=YCBCR

+ +

Streaming operations

+ +Starting with GDAL 2.0, the GeoTIFF driver can support reading or writing TIFF +files (with some restrictions detailed below) in a streaming compatible way.

+ +When reading a file from /vsistdin/, a named pipe (on Unix), or if forcing streamed +reading by setting the TIFF_READ_STREAMING configuration option to YES, the GeoTIFF driver +will assume that the TIFF Image File Directory (IFD) is at the beginning of the file, +i.e. at offset 8 for a classical TIFF file or at offset 16 for a BigTIFF file. +The values of the tags of array type must be contained at the beginning of file, after the +end of the IFD and before the first image strip/tile. The reader must read the +strips/tiles in the order they are written in the file. For a pixel interleaved file (PlanarConfiguration=Contig), +the recommended order for a writer, and thus for a reader, is from top to bottom +for a strip-organized file or from top to bottom, which a chunk of a block height, +and left to right for a tile-organized file. For a band organized file (PlanarConfiguration=Separate), +the above order is recommended with the content of the first band, then the +content of the second band, etc... Technically this order corresponds to increasing +offsets in the TileOffsets/StripOffsets tag. This is the order that the GDAL raster +copy routine will assume. +

+If the order is not the one described above, the UNORDERED_BLOCKS=YES dataset metadata item +will be set in the TIFF metadata domain. Each block offset can be determined +by querying the "BLOCK_OFFSET_[xblock]_[yblock]" band metadata items in the TIFF metadata +domain (where xblock, yblock is the coordinate of the block), and a reader could use +that information to determine the appropriate reading order for image blocks. +

+The files that are streamed into the GeoTIFF driver may be compressed, even if the +GeoTIFF driver cannot produce such files in streamable output mode (regular creation +of TIFF files will produce such compatible files for streamed reading). +

+When writing a file to /vsistdout/, a named pipe (on Unix), or when definiting +the STREAMABLE_OUTPUT=YES creation option, the CreateCopy() method of the GeoTIFF driver +will generate a file with the above defined constraints (related to position of IFD and +block order), and this +is only supported for a uncompressed file. The Create() method also supports creating streamable +compatible files, but the writer must be careful to set the projection, geotransform +or metadata before writting image blocks (so that the IFD is written at the beginning +of the file). And when writing image blocks, the order +of blocks must be the one of the above paragraph, otherwise errors will be reported. + +

+Some examples : +

+gdal_translate in.tif /vsistdout/ -co TILED=YES | gzip | gunzip | gdal_translate /vsistdin/ out.tif -co TILED=YES -co COMPRESS=DEFLATE
+
+or +
+mkfifo my_fifo
+gdalwarp in.tif my_fifo -t_srs EPSG:3857
+gdal_translate my_fifo out.png -of PNG
+
+ +

+Note: not all utilities are compatible with such input or output streaming operations, +and even those which may deal with such files may not manage to deal with them in +all circumstances, for example if the reading driver drived by the output file is +not compatible with the block order of the streamed input. + +

Configuration options

+ +

+ +This paragraph lists the configuration options that can be set to alter the default behaviour of the GTiff driver. + +

    + +
  • GTIFF_IGNORE_READ_ERRORS : (GDAL >= 1.9.0) Can be set to TRUE to avoid turning libtiff errors into GDAL errors. +Can help reading partially corrupted TIFF files
  • +
  • ESRI_XML_PAM: Can be set to TRUE to force metadata in the xml:ESRI domain to be written to PAM.
  • +
  • JPEG_QUALITY_OVERVIEW: Integer between 0 and 100. Default value : 75. Quality of JPEG compressed overviews, either internal or external.
  • +
  • GDAL_TIFF_INTERNAL_MASK: See Internal nodata masks section. Default value : FALSE.
  • +
  • GDAL_TIFF_INTERNAL_MASK_TO_8BIT: See Internal nodata masks section. Default value : TRUE
  • +
  • USE_RRD: Can be set to TRUE to force external overviews in the RRD format. Default value : FALSE
  • +
  • TIFF_USE_OVR: Can be set to TRUE to force external overviews in the GeoTIFF (.ovr) format. Default value : FALSE
  • +
  • GTIFF_POINT_GEO_IGNORE: Can be set to TRUE to revert back to the behaviour of GDAL < 1.8.0 +regarding how PixelIsPoint is interprated w.r.t geotransform. See +RFC 33: GTiff - Fixing PixelIsPoint Interpretation for more details. Default value : FALSE
  • +
  • GTIFF_REPORT_COMPD_CS: (GDAL >= 1.9.0). Can be set to TRUE to avoid stripping the vertical CS of compound CS. Default value : FALSE
  • +
  • GDAL_ENABLE_TIFF_SPLIT : Can be set to FALSE to avoid all-in-one-strip files being presented as having. Default value : TRUE
  • + + + +
  • GDAL_TIFF_OVR_BLOCKSIZE : See Overviews section. +
  • GTIFF_LINEAR_UNITS: Can be set to BROKEN to read GeoTIFF files that +have false easting/northing improperly set in meters when it ought to be in +coordinate system linear units. (Ticket #3901). +
  • TAB_APPROX_GEOTRANSFORM=YES/NO: (GDAL >= 2.0) To decide if an approximate geotransform is acceptable when reading a .tab file. Default value: NO +
  • GTIFF_DIRECT_IO=YES/NO: (GDAL >= 2.0) Can be set to YES to use specialized +RasterIO() implementations when reading un-tiled un-compressed TIFF files to +avoid using the block cache. Setting it to YES even when the optimized cases do +not apply should be safe (generic implementation will be used). Default value:NO +
  • GTIFF_VIRTUAL_MEM_IO=YES/NO/IF_ENOUGH_RAM: (GDAL >= 2.0) Can be set to YES +to use specialized RasterIO() implementations when reading un-compressed TIFF +files to avoid using the block cache. +This implementation relies on memory-mapped file I/O, +and is currently only supported on Linux (64-bit build strongly recommended). +Setting it to YES even when the optimized cases do not apply should be safe +(generic implementation will be used), but if the file exceeds RAM, disk swapping +might occur if the whole file is read. Setting it to IF_ENOUGH_RAM will first +check if the uncompressed file size is no bigger than the physical memory. Default value:NO. +If both GTIFF_VIRTUAL_MEM_IO and GTIFF_DIRECT_IO are enabled, the former is used +in priority, and if not possible, the later is tried. +
+

+ +
+ +

See Also:

+ + + + + diff --git a/bazaar/plugin/gdal/frmts/gtiff/geotiff.cpp b/bazaar/plugin/gdal/frmts/gtiff/geotiff.cpp new file mode 100644 index 000000000..a17c1cfd9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/geotiff.cpp @@ -0,0 +1,13698 @@ +/****************************************************************************** + * $Id: geotiff.cpp 29334 2015-06-14 17:30:54Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: GDAL GeoTIFF support. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, 2002, Frank Warmerdam + * Copyright (c) 2007-2015, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* If we use sunpro compiler on linux. Weird idea indeed ! */ +#if defined(__SUNPRO_CC) && defined(__linux__) +#define _GNU_SOURCE +#elif defined(__GNUC__) && !defined(_GNU_SOURCE) +/* Required to use RTLD_DEFAULT of dlfcn.h */ +#define _GNU_SOURCE +#endif + +#include "gdal_pam.h" +#define CPL_SERV_H_INCLUDED + +#include "xtiffio.h" +#include "geovalues.h" +#include "cpl_string.h" +#include "cpl_csv.h" +#include "cpl_minixml.h" +#include "gt_overview.h" +#include "ogr_spatialref.h" +#include "tif_float.h" +#include "gtiff.h" +#include "gdal_csv.h" +#include "gt_wkt_srs.h" +#include "gt_wkt_srs_priv.h" +#include "tifvsi.h" +#include "cpl_multiproc.h" +#include "cplkeywordparser.h" +#include "gt_jpeg_copy.h" +#include "cpl_vsi_virtual.h" +#include +#include "gdal_mdreader.h" + +#ifdef INTERNAL_LIBTIFF +#include "tiffiop.h" +#endif + +CPL_CVSID("$Id: geotiff.cpp 29334 2015-06-14 17:30:54Z rouault $"); + +#if SIZEOF_VOIDP == 4 +static int bGlobalStripIntegerOverflow = FALSE; +#endif + +typedef enum +{ + GTIFFTAGTYPE_STRING, + GTIFFTAGTYPE_SHORT, + GTIFFTAGTYPE_FLOAT +} GTIFFTagTypes; + +typedef struct +{ + const char *pszTagName; + int nTagVal; + GTIFFTagTypes eType; +} GTIFFTags; + +static const GTIFFTags asTIFFTags[] = +{ + { "TIFFTAG_DOCUMENTNAME", TIFFTAG_DOCUMENTNAME, GTIFFTAGTYPE_STRING }, + { "TIFFTAG_IMAGEDESCRIPTION", TIFFTAG_IMAGEDESCRIPTION, GTIFFTAGTYPE_STRING }, + { "TIFFTAG_SOFTWARE", TIFFTAG_SOFTWARE, GTIFFTAGTYPE_STRING }, + { "TIFFTAG_DATETIME", TIFFTAG_DATETIME, GTIFFTAGTYPE_STRING }, + { "TIFFTAG_ARTIST", TIFFTAG_ARTIST, GTIFFTAGTYPE_STRING }, + { "TIFFTAG_HOSTCOMPUTER", TIFFTAG_HOSTCOMPUTER, GTIFFTAGTYPE_STRING }, + { "TIFFTAG_COPYRIGHT", TIFFTAG_COPYRIGHT, GTIFFTAGTYPE_STRING }, + { "TIFFTAG_XRESOLUTION", TIFFTAG_XRESOLUTION, GTIFFTAGTYPE_FLOAT }, + { "TIFFTAG_YRESOLUTION", TIFFTAG_YRESOLUTION, GTIFFTAGTYPE_FLOAT }, + { "TIFFTAG_RESOLUTIONUNIT", TIFFTAG_RESOLUTIONUNIT, GTIFFTAGTYPE_SHORT }, /* dealt as special case */ + { "TIFFTAG_MINSAMPLEVALUE", TIFFTAG_MINSAMPLEVALUE, GTIFFTAGTYPE_SHORT }, + { "TIFFTAG_MAXSAMPLEVALUE", TIFFTAG_MAXSAMPLEVALUE, GTIFFTAGTYPE_SHORT }, +}; + +/************************************************************************/ +/* IsPowerOfTwo() */ +/************************************************************************/ + +static int IsPowerOfTwo(unsigned int i) +{ + int nBitSet = 0; + while(i != 0) + { + if ((i & 1)) + nBitSet ++; + i >>= 1; + } + return nBitSet == 1; +} + +/************************************************************************/ +/* GTIFFGetOverviewBlockSize() */ +/************************************************************************/ + +void GTIFFGetOverviewBlockSize(int* pnBlockXSize, int* pnBlockYSize) +{ + static int bHasWarned = FALSE; + const char* pszVal = CPLGetConfigOption("GDAL_TIFF_OVR_BLOCKSIZE", "128"); + int nOvrBlockSize = atoi(pszVal); + if (nOvrBlockSize < 64 || nOvrBlockSize > 4096 || + !IsPowerOfTwo(nOvrBlockSize)) + { + if (!bHasWarned) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Wrong value for GDAL_TIFF_OVR_BLOCKSIZE : %s. " + "Should be a power of 2 between 64 and 4096. Defaulting to 128", + pszVal); + bHasWarned = TRUE; + } + nOvrBlockSize = 128; + } + + *pnBlockXSize = nOvrBlockSize; + *pnBlockYSize = nOvrBlockSize; +} + +enum +{ + ENDIANNESS_NATIVE, + ENDIANNESS_LITTLE, + ENDIANNESS_BIG +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GTiffDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GTiffRasterBand; +class GTiffRGBABand; +class GTiffBitmapBand; +class GTiffJPEGOverviewDS; +class GTiffJPEGOverviewBand; + +typedef enum +{ + VIRTUAL_MEM_IO_NO, + VIRTUAL_MEM_IO_YES, + VIRTUAL_MEM_IO_IF_ENOUGH_RAM +} VirtualMemIOEnum; + +class GTiffDataset : public GDALPamDataset +{ + friend class GTiffRasterBand; + friend class GTiffSplitBand; + friend class GTiffRGBABand; + friend class GTiffBitmapBand; + friend class GTiffSplitBitmapBand; + friend class GTiffOddBitsBand; + friend class GTiffJPEGOverviewDS; + friend class GTiffJPEGOverviewBand; + + friend void GTIFFSetJpegQuality(GDALDatasetH hGTIFFDS, int nJpegQuality); + + TIFF *hTIFF; + VSILFILE *fpL; + int bStreamingIn; + + int bStreamingOut; + CPLString osTmpFilename; + VSILFILE* fpToWrite; + int nLastWrittenBlockId; + + GTiffDataset **ppoActiveDSRef; + GTiffDataset *poActiveDS; /* only used in actual base */ + + int bScanDeferred; + void ScanDirectories(); + + toff_t nDirOffset; + int bBase; + int bCloseTIFFHandle; /* useful for closing TIFF handle opened by GTIFF_DIR: */ + + uint16 nPlanarConfig; + uint16 nSamplesPerPixel; + uint16 nBitsPerSample; + uint32 nRowsPerStrip; + uint16 nPhotometric; + uint16 nSampleFormat; + uint16 nCompression; + + int nBlocksPerBand; + + uint32 nBlockXSize; + uint32 nBlockYSize; + + int nLoadedBlock; /* or tile */ + int bLoadedBlockDirty; + GByte *pabyBlockBuf; + + CPLErr LoadBlockBuf( int nBlockId, int bReadFromDisk = TRUE ); + CPLErr FlushBlockBuf(); + int bWriteErrorInFlushBlockBuf; + + char *pszProjection; + int bLookedForProjection; + int bLookedForMDAreaOrPoint; + + void LoadMDAreaOrPoint(); + void LookForProjection(); +#ifdef ESRI_BUILD + void AdjustLinearUnit( short UOMLength ); +#endif + + double adfGeoTransform[6]; + int bGeoTransformValid; + + int bTreatAsRGBA; + int bCrystalized; + + void Crystalize(); + + GDALColorTable *poColorTable; + + void WriteGeoTIFFInfo(); + int SetDirectory( toff_t nDirOffset = 0 ); + + int nOverviewCount; + GTiffDataset **papoOverviewDS; + + int nJPEGOverviewVisibilityFlag; /* if > 0, the implicit JPEG overviews are visible through GetOverviewCount() */ + int nJPEGOverviewCount; /* currently visible overviews. Generally == nJPEGOverviewCountOri */ + int nJPEGOverviewCountOri; /* size of papoJPEGOverviewDS */ + GTiffJPEGOverviewDS **papoJPEGOverviewDS; + int GetJPEGOverviewCount(); + + int nGCPCount; + GDAL_GCP *pasGCPList; + + int IsBlockAvailable( int nBlockId ); + + int bGeoTIFFInfoChanged; + int bForceUnsetGTOrGCPs; + int bForceUnsetProjection; + + int bNoDataChanged; + int bNoDataSet; + double dfNoDataValue; + + int bMetadataChanged; + int bColorProfileMetadataChanged; + + int bNeedsRewrite; + + void ApplyPamInfo(); + void PushMetadataToPam(); + + GDALMultiDomainMetadata oGTiffMDMD; + + CPLString osProfile; + char **papszCreationOptions; + + int bLoadingOtherBands; + + void* pabyTempWriteBuffer; + int nTempWriteBufferSize; + int WriteEncodedTile(uint32 tile, GByte* pabyData, int bPreserveDataBuffer); + int WriteEncodedStrip(uint32 strip, GByte* pabyData, int bPreserveDataBuffer); + + GTiffDataset* poMaskDS; + GTiffDataset* poBaseDS; + + CPLString osFilename; + + int bFillEmptyTiles; + void FillEmptyTiles(void); + + void FlushDirectory(); + CPLErr CleanOverviews(); + + /* Used for the all-in-on-strip case */ + int nLastLineRead; + int nLastBandRead; + int bTreatAsSplit; + int bTreatAsSplitBitmap; + + int bClipWarn; + + int bIMDRPCMetadataLoaded; + char** papszMetadataFiles; + void LoadMetadata(); + + int bEXIFMetadataLoaded; + void LoadEXIFMetadata(); + + int bICCMetadataLoaded; + void LoadICCProfile(); + + int bHasWarnedDisableAggressiveBandCaching; + + int bDontReloadFirstBlock; /* Hack for libtiff 3.X and #3633 */ + + int nZLevel; + int nLZMAPreset; + int nJpegQuality; + int nJpegTablesMode; + + int bPromoteTo8Bits; + + int bDebugDontWriteBlocks; + + CPLErr RegisterNewOverviewDataset(toff_t nOverviewOffset); + CPLErr CreateOverviewsFromSrcOverviews(GDALDataset* poSrcDS); + CPLErr CreateInternalMaskOverviews(int nOvrBlockXSize, + int nOvrBlockYSize); + + int bIsFinalized; + int Finalize(); + + int bIgnoreReadErrors; + + CPLString osGeorefFilename; + + int bDirectIO; + + VirtualMemIOEnum eVirtualMemIOUsage; + CPLVirtualMem* psVirtualMemIOMapping; + + int nSetPhotometricFromBandColorInterp; + + CPLVirtualMem *pBaseMapping; + int nRefBaseMapping; + + int bHasDiscardedLsb; + std::vector anMaskLsb, anOffsetLsb; + void DiscardLsb(GByte* pabyBuffer, int nBytes, int iBand); + void GetDiscardLsbOption(char** papszOptions); + + int GuessJPEGQuality(int& bOutHasQuantizationTable, + int& bOutHasHuffmanTable); + + CPLErr DirectIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ); + + int VirtualMemIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ); + protected: + virtual int CloseDependentDatasets(); + + public: + GTiffDataset(); + ~GTiffDataset(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + CPLErr SetGCPs( int, const GDAL_GCP *, const char * ); + + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + virtual char **GetFileList(void); + + virtual CPLErr IBuildOverviews( const char *, int, int *, int, int *, + GDALProgressFunc, void * ); + + CPLErr OpenOffset( TIFF *, GTiffDataset **ppoActiveDSRef, + toff_t nDirOffset, int bBaseIn, GDALAccess, + int bAllowRGBAInterface = TRUE, int bReadGeoTransform = FALSE, + char** papszSiblingFiles = NULL); + + static GDALDataset *OpenDir( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); + static GDALDataset *CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + virtual void FlushCache( void ); + + virtual char **GetMetadataDomainList(); + virtual CPLErr SetMetadata( char **, const char * = "" ); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char*, const char*, + const char* = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual void *GetInternalHandle( const char * ); + + virtual CPLErr CreateMaskBand( int nFlags ); + + // only needed by createcopy and close code. + static void WriteRPC( GDALDataset *, TIFF *, int, const char *, + const char *, char **, int bWriteOnlyInPAMIfNeeded = FALSE ); + static int WriteMetadata( GDALDataset *, TIFF *, int, const char *, + const char *, char **, int bExcludeRPBandIMGFileWriting = FALSE ); + static void WriteNoDataValue( TIFF *, double ); + + static TIFF * CreateLL( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + double dfExtraSpaceForOverviews, + char **papszParmList, + VSILFILE** pfpL, + CPLString& osTmpFilename); + + CPLErr WriteEncodedTileOrStrip(uint32 tile_or_strip, void* data, int bPreserveDataBuffer); + + static void SaveICCProfile(GTiffDataset *pDS, TIFF *hTIFF, char **papszParmList, uint32 nBitsPerSample); +}; + + +/************************************************************************/ +/* ==================================================================== */ +/* GTiffJPEGOverviewDS */ +/* ==================================================================== */ +/************************************************************************/ + +class GTiffJPEGOverviewDS : public GDALDataset +{ + friend class GTiffJPEGOverviewBand; + GTiffDataset* poParentDS; + int nOverviewLevel; + + int nJPEGTableSize; + GByte *pabyJPEGTable; + CPLString osTmpFilenameJPEGTable; + + CPLString osTmpFilename; + GDALDataset* poJPEGDS; + int nBlockId; /* valid block id of the parent DS that match poJPEGDS */ + + public: + GTiffJPEGOverviewDS(GTiffDataset* poParentDS, int nOverviewLevel, + const void* pJPEGTable, int nJPEGTableSize); + ~GTiffJPEGOverviewDS(); + + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); +}; + +class GTiffJPEGOverviewBand : public GDALRasterBand +{ + public: + GTiffJPEGOverviewBand(GTiffJPEGOverviewDS* poDS, int nBand); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* GTiffJPEGOverviewDS() */ +/************************************************************************/ + +GTiffJPEGOverviewDS::GTiffJPEGOverviewDS(GTiffDataset* poParentDS, int nOverviewLevel, + const void* pJPEGTable, int nJPEGTableSizeIn) +{ + this->poParentDS = poParentDS; + this->nOverviewLevel = nOverviewLevel; + poJPEGDS = NULL; + nBlockId = -1; + + osTmpFilenameJPEGTable.Printf("/vsimem/jpegtable_%p", this); + nJPEGTableSize = nJPEGTableSizeIn; + + const GByte abyAdobeAPP14RGB[] = { + 0xFF, 0xEE, 0x00, 0x0E, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00 }; + int bAddAdobe = ( poParentDS->nPlanarConfig == PLANARCONFIG_CONTIG && + poParentDS->nPhotometric != PHOTOMETRIC_YCBCR && poParentDS->nBands == 3 ); + pabyJPEGTable = (GByte*) CPLMalloc(nJPEGTableSize + ((bAddAdobe) ? sizeof(abyAdobeAPP14RGB) : 0)); + memcpy(pabyJPEGTable, pJPEGTable, nJPEGTableSize); + if( bAddAdobe ) + { + memcpy(pabyJPEGTable + nJPEGTableSize, abyAdobeAPP14RGB, sizeof(abyAdobeAPP14RGB)); + nJPEGTableSize += sizeof(abyAdobeAPP14RGB); + } + VSIFCloseL(VSIFileFromMemBuffer( osTmpFilenameJPEGTable, pabyJPEGTable, nJPEGTableSize, TRUE )); + + int nScaleFactor = 1 << nOverviewLevel; + nRasterXSize = (poParentDS->nRasterXSize + nScaleFactor - 1) / nScaleFactor; + nRasterYSize = (poParentDS->nRasterYSize + nScaleFactor - 1) / nScaleFactor; + + int i; + for(i=1;i<=poParentDS->nBands;i++) + SetBand(i, new GTiffJPEGOverviewBand(this, i)); + + SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + if ( poParentDS->nPhotometric == PHOTOMETRIC_YCBCR ) + SetMetadataItem( "COMPRESSION", "YCbCr JPEG", "IMAGE_STRUCTURE" ); + else + SetMetadataItem( "COMPRESSION", "JPEG", "IMAGE_STRUCTURE" ); +} + +/************************************************************************/ +/* ~GTiffJPEGOverviewDS() */ +/************************************************************************/ + +GTiffJPEGOverviewDS::~GTiffJPEGOverviewDS() +{ + if( poJPEGDS != NULL ) + GDALClose( (GDALDatasetH) poJPEGDS ); + VSIUnlink(osTmpFilenameJPEGTable); + if( osTmpFilename.size() ) + VSIUnlink(osTmpFilename); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr GTiffJPEGOverviewDS::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ + /* For non-single strip JPEG-IN-TIFF, the block based strategy will */ + /* be the most efficient one, to avoid decompressing the JPEG content */ + /* for each requested band */ + if( nBandCount > 1 && poParentDS->nPlanarConfig == PLANARCONFIG_CONTIG && + ((int)poParentDS->nBlockXSize < poParentDS->nRasterXSize || + poParentDS->nBlockYSize > 1) ) + { + return BlockBasedRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); + } + else + { + return GDALDataset::IRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg); + } + +} + +/************************************************************************/ +/* GTiffJPEGOverviewBand() */ +/************************************************************************/ + +GTiffJPEGOverviewBand::GTiffJPEGOverviewBand(GTiffJPEGOverviewDS* poDS, int nBand) +{ + this->poDS = poDS; + this->nBand = nBand; + eDataType = poDS->poParentDS->GetRasterBand(nBand)->GetRasterDataType(); + poDS->poParentDS->GetRasterBand(nBand)->GetBlockSize(&nBlockXSize, &nBlockYSize); + int nScaleFactor = 1 << poDS->nOverviewLevel; + nBlockXSize = (nBlockXSize + nScaleFactor - 1) / nScaleFactor; + if( nBlockYSize == 1 ) + nBlockYSize = 1; + else + nBlockYSize = (nBlockYSize + nScaleFactor - 1) / nScaleFactor; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GTiffJPEGOverviewBand::IReadBlock( int nBlockXOff, int nBlockYOff, void *pImage ) +{ + GTiffJPEGOverviewDS* poGDS = (GTiffJPEGOverviewDS*)poDS; + + /* Compute the source block ID */ + int nBlockId; + if( nBlockYSize == 1 ) + { + nBlockId = 0; + } + else + { + int nBlocksPerRow = DIV_ROUND_UP(poGDS->poParentDS->nRasterXSize, poGDS->poParentDS->nBlockXSize); + nBlockId = nBlockYOff * nBlocksPerRow + nBlockXOff; + } + if( poGDS->poParentDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) + { + nBlockId += (nBand-1) * poGDS->poParentDS->nBlocksPerBand; + } + + if( !poGDS->poParentDS->SetDirectory() ) + return CE_Failure; + + /* Make sure it is available */ + int nDataTypeSize = GDALGetDataTypeSize(eDataType)/8; + if( !poGDS->poParentDS->IsBlockAvailable(nBlockId) ) + { + memset(pImage, 0, nBlockXSize * nBlockYSize * nDataTypeSize ); + return CE_None; + } + + int nScaleFactor = 1 << poGDS->nOverviewLevel; + if( poGDS->poJPEGDS == NULL || nBlockId != poGDS->nBlockId ) + { + toff_t *panByteCounts = NULL; + toff_t *panOffsets = NULL; + vsi_l_offset nOffset = 0; + vsi_l_offset nByteCount = 0; + + /* Find offset and size of the JPEG tile/strip */ + TIFF* hTIFF = poGDS->poParentDS->hTIFF; + if( (( TIFFIsTiled( hTIFF ) + && TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts ) + && TIFFGetField( hTIFF, TIFFTAG_TILEOFFSETS, &panOffsets ) ) + || ( !TIFFIsTiled( hTIFF ) + && TIFFGetField( hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts ) + && TIFFGetField( hTIFF, TIFFTAG_STRIPOFFSETS, &panOffsets ) )) && + panByteCounts != NULL && panOffsets != NULL ) + { + if( panByteCounts[nBlockId] < 2 ) + return CE_Failure; + nOffset = panOffsets[nBlockId] + 2; /* skip leading 0xFF 0xF8 */ + nByteCount = panByteCounts[nBlockId] - 2; + } + else + { + return CE_Failure; + } + + /* Special case for last strip that might be smaller than other strips */ + /* In which case we must invalidate the dataset */ + if( !TIFFIsTiled( hTIFF ) && poGDS->poParentDS->nBlockYSize > 1 && + (nBlockYOff + 1 == (int)DIV_ROUND_UP(poGDS->poParentDS->nRasterYSize, poGDS->poParentDS->nBlockYSize) || + (poGDS->poJPEGDS != NULL && poGDS->poJPEGDS->GetRasterYSize() != nBlockYSize * nScaleFactor)) ) + { + if( poGDS->poJPEGDS != NULL ) + GDALClose( (GDALDatasetH) poGDS->poJPEGDS ); + poGDS->poJPEGDS = NULL; + } + + CPLString osFileToOpen; + poGDS->osTmpFilename.Printf("/vsimem/sparse_%p", poGDS); + VSILFILE* fp = VSIFOpenL(poGDS->osTmpFilename, "wb+"); + + /* If the size of the JPEG strip/tile is small enough, we will */ + /* read it from the TIFF file and forge a in-memory JPEG file with */ + /* the JPEG table followed by the JPEG data. */ + int bInMemoryJPEGFile = ( nByteCount < 256 * 256 ); + if( bInMemoryJPEGFile ) + { + /* If the previous file was opened as a /vsisparse/, we have to re-open */ + if( poGDS->poJPEGDS != NULL && + strncmp(poGDS->poJPEGDS->GetDescription(), "/vsisparse/", strlen("/vsisparse/")) == 0 ) + { + GDALClose( (GDALDatasetH) poGDS->poJPEGDS ); + poGDS->poJPEGDS = NULL; + } + osFileToOpen = poGDS->osTmpFilename; + + VSIFSeekL(fp, poGDS->nJPEGTableSize + nByteCount - 1, SEEK_SET); + char ch = 0; + VSIFWriteL(&ch, 1, 1, fp); + GByte* pabyBuffer = VSIGetMemFileBuffer( poGDS->osTmpFilename, NULL, FALSE); + memcpy(pabyBuffer, poGDS->pabyJPEGTable, poGDS->nJPEGTableSize); + VSILFILE* fpTIF = VSI_TIFFGetVSILFile(TIFFClientdata( hTIFF )); + VSIFSeekL(fpTIF, nOffset, SEEK_SET); + VSIFReadL(pabyBuffer + poGDS->nJPEGTableSize, 1, (size_t)nByteCount, fpTIF); + } + else + { + /* If the JPEG strip/tile is too big (e.g. a single-strip JPEG-in-TIFF) */ + /* we will use /vsisparse mechanism to make a fake JPEG file */ + + /* If the previous file was NOT opened as a /vsisparse/, we have to re-open */ + if( poGDS->poJPEGDS != NULL && + strncmp(GDALGetDescription(poGDS->poJPEGDS), "/vsisparse/", strlen("/vsisparse/")) != 0 ) + { + GDALClose( (GDALDatasetH) poGDS->poJPEGDS ); + poGDS->poJPEGDS = NULL; + } + osFileToOpen = CPLSPrintf("/vsisparse/%s", poGDS->osTmpFilename.c_str()); + + VSIFPrintfL(fp, "%s" + "0" + "0" + "%d" + "" + "" + "%s" + "%d" + "" CPL_FRMT_GUIB "" + "" CPL_FRMT_GUIB "" + "", + poGDS->osTmpFilenameJPEGTable.c_str(), + (int)poGDS->nJPEGTableSize, + poGDS->poParentDS->GetDescription(), + (int)poGDS->nJPEGTableSize, + nOffset, + nByteCount); + } + VSIFCloseL(fp); + + if( poGDS->poJPEGDS == NULL ) + { + const char* apszDrivers[] = { "JPEG", NULL }; + poGDS->poJPEGDS = (GDALDataset*) GDALOpenEx(osFileToOpen, + GDAL_OF_RASTER | GDAL_OF_INTERNAL, + apszDrivers, + NULL, NULL); + if( poGDS->poJPEGDS != NULL ) + { + /* Force all implicit overviews to be available, even for small tiles */ + CPLSetThreadLocalConfigOption("JPEG_FORCE_INTERNAL_OVERVIEWS", "YES"); + GDALGetOverviewCount(GDALGetRasterBand(poGDS->poJPEGDS, 1)); + CPLSetThreadLocalConfigOption("JPEG_FORCE_INTERNAL_OVERVIEWS", NULL); + + poGDS->nBlockId = nBlockId; + } + } + else + { + /* Trick: we invalidate the JPEG dataset to force a reload */ + /* of the new content */ + CPLErrorReset(); + poGDS->poJPEGDS->FlushCache(); + if( CPLGetLastErrorNo() != 0 ) + { + GDALClose( (GDALDatasetH) poGDS->poJPEGDS ); + poGDS->poJPEGDS = NULL; + return CE_Failure; + } + poGDS->nBlockId = nBlockId; + } + } + + CPLErr eErr = CE_Failure; + if( poGDS->poJPEGDS ) + { + GDALDataset* poDS = poGDS->poJPEGDS; + + int nReqXOff = 0, nReqYOff, nReqXSize, nReqYSize; + if( nBlockYSize == 1 ) + { + nReqYOff = nBlockYOff * nScaleFactor; + nReqXSize = poDS->GetRasterXSize(); + nReqYSize = nScaleFactor; + } + else + { + nReqYOff = 0; + nReqXSize = nBlockXSize * nScaleFactor; + nReqYSize = nBlockYSize * nScaleFactor; + } + int nBufXSize = nBlockXSize; + int nBufYSize = nBlockYSize; + if( nReqXOff + nReqXSize > poDS->GetRasterXSize() ) + { + nReqXSize = poDS->GetRasterXSize() - nReqXOff; + nBufXSize = nReqXSize / nScaleFactor; + if( nBufXSize == 0 ) nBufXSize = 1; + } + if( nReqYOff + nReqYSize > poDS->GetRasterYSize() ) + { + nReqYSize = poDS->GetRasterYSize() - nReqYOff; + nBufYSize = nReqYSize / nScaleFactor; + if( nBufYSize == 0 ) nBufYSize = 1; + } + + int nSrcBand = ( poGDS->poParentDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) ? 1 : nBand; + if( nSrcBand <= poDS->GetRasterCount() ) + { + eErr = poDS->GetRasterBand(nSrcBand)->RasterIO(GF_Read, + nReqXOff, nReqYOff, nReqXSize, nReqYSize, + pImage, + nBufXSize, nBufYSize, eDataType, + 0, nBlockXSize * nDataTypeSize, NULL ); + } + } + + return eErr; +} + +/************************************************************************/ +/* GTIFFSetJpegQuality() */ +/* Called by GTIFFBuildOverviews() to set the jpeg quality on the IFD */ +/* of the .ovr file */ +/************************************************************************/ + +void GTIFFSetJpegQuality(GDALDatasetH hGTIFFDS, int nJpegQuality) +{ + CPLAssert(EQUAL(GDALGetDriverShortName(GDALGetDatasetDriver(hGTIFFDS)), "GTIFF")); + + GTiffDataset* poDS = (GTiffDataset*)hGTIFFDS; + poDS->nJpegQuality = nJpegQuality; + + poDS->ScanDirectories(); + + int i; + for(i=0;inOverviewCount;i++) + poDS->papoOverviewDS[i]->nJpegQuality = nJpegQuality; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GTiffRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GTiffRasterBand : public GDALPamRasterBand +{ + friend class GTiffDataset; + + GDALColorInterp eBandInterp; + + int bHaveOffsetScale; + double dfOffset; + double dfScale; + CPLString osUnitType; + CPLString osDescription; + + CPLErr DirectIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + std::set aSetPSelf; + static void DropReferenceVirtualMem(void* pUserData); + CPLVirtualMem * GetVirtualMemAutoInternal( GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ); +protected: + GTiffDataset *poGDS; + GDALMultiDomainMetadata oGTiffMDMD; + + int bNoDataSet; + double dfNoDataValue; + + void NullBlock( void *pData ); + CPLErr FillCacheForOtherBands( int nBlockXOff, int nBlockYOff ); + +public: + GTiffRasterBand( GTiffDataset *, int ); + ~GTiffRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + virtual const char *GetDescription() const; + virtual void SetDescription( const char * ); + + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual CPLErr SetColorTable( GDALColorTable * ); + virtual double GetNoDataValue( int * ); + virtual CPLErr SetNoDataValue( double ); + + virtual double GetOffset( int *pbSuccess = NULL ); + virtual CPLErr SetOffset( double dfNewValue ); + virtual double GetScale( int *pbSuccess = NULL ); + virtual CPLErr SetScale( double dfNewValue ); + virtual const char* GetUnitType(); + virtual CPLErr SetUnitType( const char *pszNewValue ); + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + + virtual char **GetMetadataDomainList(); + virtual CPLErr SetMetadata( char **, const char * = "" ); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char*, const char*, + const char* = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview( int ); + + virtual GDALRasterBand *GetMaskBand(); + virtual int GetMaskFlags(); + virtual CPLErr CreateMaskBand( int nFlags ); + + virtual CPLVirtualMem *GetVirtualMemAuto( GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ); +}; + +/************************************************************************/ +/* GTiffRasterBand() */ +/************************************************************************/ + +GTiffRasterBand::GTiffRasterBand( GTiffDataset *poDS, int nBand ) + +{ + poGDS = poDS; + + this->poDS = poDS; + this->nBand = nBand; + + bHaveOffsetScale = FALSE; + dfOffset = 0.0; + dfScale = 1.0; + +/* -------------------------------------------------------------------- */ +/* Get the GDAL data type. */ +/* -------------------------------------------------------------------- */ + uint16 nSampleFormat = poDS->nSampleFormat; + + eDataType = GDT_Unknown; + + if( poDS->nBitsPerSample <= 8 ) + { + eDataType = GDT_Byte; + if( nSampleFormat == SAMPLEFORMAT_INT ) + SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" ); + + } + else if( poDS->nBitsPerSample <= 16 ) + { + if( nSampleFormat == SAMPLEFORMAT_INT ) + eDataType = GDT_Int16; + else + eDataType = GDT_UInt16; + } + else if( poDS->nBitsPerSample == 32 ) + { + if( nSampleFormat == SAMPLEFORMAT_COMPLEXINT ) + eDataType = GDT_CInt16; + else if( nSampleFormat == SAMPLEFORMAT_IEEEFP ) + eDataType = GDT_Float32; + else if( nSampleFormat == SAMPLEFORMAT_INT ) + eDataType = GDT_Int32; + else + eDataType = GDT_UInt32; + } + else if( poDS->nBitsPerSample == 64 ) + { + if( nSampleFormat == SAMPLEFORMAT_IEEEFP ) + eDataType = GDT_Float64; + else if( nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP ) + eDataType = GDT_CFloat32; + else if( nSampleFormat == SAMPLEFORMAT_COMPLEXINT ) + eDataType = GDT_CInt32; + } + else if( poDS->nBitsPerSample == 128 ) + { + if( nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP ) + eDataType = GDT_CFloat64; + } + +/* -------------------------------------------------------------------- */ +/* Try to work out band color interpretation. */ +/* -------------------------------------------------------------------- */ + int bLookForExtraSamples = FALSE; + + if( poDS->poColorTable != NULL && nBand == 1 ) + eBandInterp = GCI_PaletteIndex; + else if( poDS->nPhotometric == PHOTOMETRIC_RGB + || (poDS->nPhotometric == PHOTOMETRIC_YCBCR + && poDS->nCompression == COMPRESSION_JPEG + && CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", + "YES") )) ) + { + if( nBand == 1 ) + eBandInterp = GCI_RedBand; + else if( nBand == 2 ) + eBandInterp = GCI_GreenBand; + else if( nBand == 3 ) + eBandInterp = GCI_BlueBand; + else + bLookForExtraSamples = TRUE; + } + else if( poDS->nPhotometric == PHOTOMETRIC_YCBCR ) + { + if( nBand == 1 ) + eBandInterp = GCI_YCbCr_YBand; + else if( nBand == 2 ) + eBandInterp = GCI_YCbCr_CbBand; + else if( nBand == 3 ) + eBandInterp = GCI_YCbCr_CrBand; + else + bLookForExtraSamples = TRUE; + } + else if( poDS->nPhotometric == PHOTOMETRIC_SEPARATED ) + { + if( nBand == 1 ) + eBandInterp = GCI_CyanBand; + else if( nBand == 2 ) + eBandInterp = GCI_MagentaBand; + else if( nBand == 3 ) + eBandInterp = GCI_YellowBand; + else if( nBand == 4 ) + eBandInterp = GCI_BlackBand; + else + bLookForExtraSamples = TRUE; + } + else if( poDS->nPhotometric == PHOTOMETRIC_MINISBLACK && nBand == 1 ) + eBandInterp = GCI_GrayIndex; + else + bLookForExtraSamples = TRUE; + + if( bLookForExtraSamples ) + { + uint16 *v; + uint16 count = 0; + + if( TIFFGetField( poDS->hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v ) ) + { + int nBaseSamples; + nBaseSamples = poDS->nSamplesPerPixel - count; + + if( nBand > nBaseSamples + && (v[nBand-nBaseSamples-1] == EXTRASAMPLE_ASSOCALPHA + || v[nBand-nBaseSamples-1] == EXTRASAMPLE_UNASSALPHA) ) + eBandInterp = GCI_AlphaBand; + else + eBandInterp = GCI_Undefined; + } + else + eBandInterp = GCI_Undefined; + } + +/* -------------------------------------------------------------------- */ +/* Establish block size for strip or tiles. */ +/* -------------------------------------------------------------------- */ + nBlockXSize = poDS->nBlockXSize; + nBlockYSize = poDS->nBlockYSize; + + bNoDataSet = FALSE; + dfNoDataValue = -9999.0; +} + +/************************************************************************/ +/* ~GTiffRasterBand() */ +/************************************************************************/ + +GTiffRasterBand::~GTiffRasterBand() +{ + /* So that any future DropReferenceVirtualMem() will not try to access the */ + /* raster band object, but this wouldn't conform the advertized contract */ + if( aSetPSelf.size() != 0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Virtual memory objects still exist at GTiffRasterBand destruction"); + std::set::iterator oIter = aSetPSelf.begin(); + for(; oIter != aSetPSelf.end(); ++oIter ) + *(*oIter) = NULL; + } +} + +/************************************************************************/ +/* DirectIO() */ +/************************************************************************/ + +/* Reads directly bytes from the file using ReadMultiRange(), and by-pass */ +/* block reading. Restricted to simple TIFF configurations (un-tiled, */ +/* uncompressed data, standard data types). Particularly useful to extract */ +/* sub-windows of data on a large /vsicurl dataset). */ + +CPLErr GTiffRasterBand::DirectIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ + if( !(eRWFlag == GF_Read && + poGDS->nCompression == COMPRESSION_NONE && + (poGDS->nPhotometric == PHOTOMETRIC_MINISBLACK || + poGDS->nPhotometric == PHOTOMETRIC_RGB || + poGDS->nPhotometric == PHOTOMETRIC_PALETTE) && + (poGDS->nBitsPerSample == 8 || (poGDS->nBitsPerSample == 16) || + poGDS->nBitsPerSample == 32 || poGDS->nBitsPerSample == 64) && + poGDS->nBitsPerSample == GDALGetDataTypeSize(eDataType) && + poGDS->SetDirectory() && /* very important to make hTIFF uptodate! */ + !TIFFIsTiled( poGDS->hTIFF )) ) + { + return CE_Failure; + } + + /* we only know how to deal with nearest neighbour in this optimized routine */ + if( (nXSize != nBufXSize || nYSize != nBufYSize) && + psExtraArg != NULL && + psExtraArg->eResampleAlg != GRIORA_NearestNeighbour ) + { + return CE_Failure; + } + + /*CPLDebug("GTiff", "DirectIO(%d,%d,%d,%d -> %dx%d)", + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize);*/ + + /* Make sure that TIFFTAG_STRIPOFFSETS is up-to-date */ + if (poGDS->GetAccess() == GA_Update) + { + poGDS->FlushCache(); + VSI_TIFFFlushBufferedWrite( TIFFClientdata( poGDS->hTIFF ) ); + } + + /* Get strip offsets */ + toff_t *panTIFFOffsets = NULL; + if ( !TIFFGetField( poGDS->hTIFF, TIFFTAG_STRIPOFFSETS, &panTIFFOffsets ) || + panTIFFOffsets == NULL ) + { + return CE_Failure; + } + + int iLine; + int nReqXSize = nXSize; /* sub-sampling or over-sampling can only be done at last stage */ + int nReqYSize = MIN(nBufYSize, nYSize); /* we can do sub-sampling at the extraction stage */ + void** ppData = (void**) VSIMalloc(nReqYSize * sizeof(void*)); + vsi_l_offset* panOffsets = (vsi_l_offset*) + VSIMalloc(nReqYSize * sizeof(vsi_l_offset)); + size_t* panSizes = (size_t*) VSIMalloc(nReqYSize * sizeof(size_t)); + int nDTSize = GDALGetDataTypeSize(eDataType) / 8; + void* pTmpBuffer = NULL; + CPLErr eErr = CE_None; + int nContigBands = ((poGDS->nPlanarConfig == PLANARCONFIG_CONTIG) ? poGDS->nBands : 1); + int ePixelSize = nDTSize * nContigBands; + + if (ppData == NULL || panOffsets == NULL || panSizes == NULL) + eErr = CE_Failure; + else if (nXSize != nBufXSize || nYSize != nBufYSize || + eBufType != eDataType || + nPixelSpace != GDALGetDataTypeSize(eBufType) / 8 || + nContigBands > 1) + { + /* We need a temporary buffer for over-sampling/sub-sampling */ + /* and/or data type conversion */ + pTmpBuffer = VSIMalloc(nReqXSize * nReqYSize * ePixelSize); + if (pTmpBuffer == NULL) + eErr = CE_Failure; + } + + /* Prepare data extraction */ + for(iLine=0;eErr == CE_None && iLinenPlanarConfig == PLANARCONFIG_SEPARATE ) + { + nBlockId += (nBand-1) * poGDS->nBlocksPerBand; + } + + panOffsets[iLine] = panTIFFOffsets[nBlockId]; + if (panOffsets[iLine] == 0) /* We don't support sparse files */ + eErr = CE_Failure; + + panOffsets[iLine] += (nXOff + nYOffsetInBlock * nBlockXSize) * ePixelSize; + panSizes[iLine] = nReqXSize * ePixelSize; + } + + /* Extract data from the file */ + if (eErr == CE_None) + { + VSILFILE* fp = VSI_TIFFGetVSILFile(TIFFClientdata( poGDS->hTIFF )); + int nRet = VSIFReadMultiRangeL(nReqYSize, ppData, panOffsets, panSizes, fp); + if (nRet != 0) + eErr = CE_Failure; + } + + /* Byte-swap if necessary */ + if (eErr == CE_None && TIFFIsByteSwapped(poGDS->hTIFF)) + { + for(iLine=0;iLine 1) ? (nBand-1) : 0) * nDTSize; + GByte* pabyDstData = ((GByte*)pData) + iY * nLineSpace; + if( nBufXSize == nXSize && nDTSize == 1 && eBufType == GDT_Byte ) + { + for(int iX=0;iXpoGDS->nRefBaseMapping) == 0 ) + { + poSelf->poGDS->pBaseMapping = NULL; + } + poSelf->aSetPSelf.erase(ppoSelf); + } + CPLFree(pUserData); +} + +/************************************************************************/ +/* GetVirtualMemAutoInternal() */ +/************************************************************************/ + +CPLVirtualMem* GTiffRasterBand::GetVirtualMemAutoInternal( GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ) +{ + int nLineSize = nBlockXSize * (GDALGetDataTypeSize(eDataType) / 8); + if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG ) + nLineSize *= poGDS->nBands; + + if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG ) + { + /* In case of a pixel interleaved file, we save virtual memory space */ + /* by reusing a base mapping that embraces the whole imagery */ + if( poGDS->pBaseMapping != NULL ) + { + /* Offset between the base mapping and the requested mapping */ + vsi_l_offset nOffset = (vsi_l_offset)(nBand - 1) * GDALGetDataTypeSize(eDataType) / 8; + + GTiffRasterBand** ppoSelf = (GTiffRasterBand** )CPLCalloc(1, sizeof(GTiffRasterBand*)); + *ppoSelf = this; + + CPLVirtualMem* pVMem = CPLVirtualMemDerivedNew( + poGDS->pBaseMapping, + nOffset, + CPLVirtualMemGetSize(poGDS->pBaseMapping) - nOffset, + GTiffRasterBand::DropReferenceVirtualMem, + ppoSelf); + if( pVMem == NULL ) + { + CPLFree(ppoSelf); + return NULL; + } + + /* Mechanism used so that the memory mapping object can be */ + /* destroyed after the raster band */ + aSetPSelf.insert(ppoSelf); + poGDS->nRefBaseMapping ++; + *pnPixelSpace = GDALGetDataTypeSize(eDataType) / 8; + if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG ) + *pnPixelSpace *= poGDS->nBands; + *pnLineSpace = nLineSize; + return pVMem; + } + } + + if( !poGDS->SetDirectory() ) /* very important to make hTIFF up-to-date */ + return NULL; + VSILFILE* fp = VSI_TIFFGetVSILFile(TIFFClientdata( poGDS->hTIFF )); + + vsi_l_offset nLength = (vsi_l_offset)nRasterYSize * nLineSize; + + if( !(CPLIsVirtualMemFileMapAvailable() && + VSIFGetNativeFileDescriptorL(fp) != NULL && + nLength == (size_t)nLength && + poGDS->nCompression == COMPRESSION_NONE && + (poGDS->nPhotometric == PHOTOMETRIC_MINISBLACK || + poGDS->nPhotometric == PHOTOMETRIC_RGB || + poGDS->nPhotometric == PHOTOMETRIC_PALETTE) && + (poGDS->nBitsPerSample == 8 || poGDS->nBitsPerSample == 16 || + poGDS->nBitsPerSample == 32 || poGDS->nBitsPerSample == 64) && + poGDS->nBitsPerSample == GDALGetDataTypeSize(eDataType) && + !TIFFIsTiled( poGDS->hTIFF ) && !TIFFIsByteSwapped(poGDS->hTIFF)) ) + { + return NULL; + } + + /* Make sure that TIFFTAG_STRIPOFFSETS is up-to-date */ + if (poGDS->GetAccess() == GA_Update) + { + poGDS->FlushCache(); + VSI_TIFFFlushBufferedWrite( TIFFClientdata( poGDS->hTIFF ) ); + } + + /* Get strip offsets */ + toff_t *panTIFFOffsets = NULL; + if ( !TIFFGetField( poGDS->hTIFF, TIFFTAG_STRIPOFFSETS, &panTIFFOffsets ) || + panTIFFOffsets == NULL ) + { + return NULL; + } + + int nBlockSize = + nBlockXSize * nBlockYSize * GDALGetDataTypeSize(eDataType) / 8; + if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG ) + nBlockSize *= poGDS->nBands; + + int nBlocks = poGDS->nBlocksPerBand; + if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) + nBlocks *= poGDS->nBands; + int i; + for(i = 0; i < nBlocks; i ++) + { + if( panTIFFOffsets[i] != 0 ) + break; + } + if( i == nBlocks ) + { + /* All zeroes */ + if( poGDS->eAccess == GA_Update ) + { + /* Initialize the file with empty blocks so that the file has */ + /* the appropriate size */ + + toff_t* panByteCounts = NULL; + if( !TIFFGetField( poGDS->hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts ) || + panByteCounts == NULL ) + { + return NULL; + } + VSIFSeekL(fp, 0, SEEK_END); + vsi_l_offset nBaseOffset = VSIFTellL(fp); + + /* Just write one tile with libtiff to put it in appropriate state */ + GByte* pabyData = (GByte*)VSICalloc(1, nBlockSize); + if( pabyData == NULL ) + { + return NULL; + } + int ret = TIFFWriteEncodedStrip(poGDS->hTIFF, 0, pabyData, nBlockSize); + VSI_TIFFFlushBufferedWrite( TIFFClientdata( poGDS->hTIFF ) ); + VSIFree(pabyData); + if( ret != nBlockSize ) + { + return NULL; + } + CPLAssert(panTIFFOffsets[0] == nBaseOffset); + CPLAssert(panByteCounts[0] == (toff_t)nBlockSize); + + /* Now simulate the writing of other blocks */ + vsi_l_offset nDataSize = (vsi_l_offset)nBlockSize * nBlocks; + VSIFSeekL(fp, nBaseOffset + nDataSize - 1, SEEK_SET); + char ch = 0; + if( VSIFWriteL(&ch, 1, 1, fp) != 1 ) + { + return NULL; + } + + for(i = 1; i < nBlocks; i ++) + { + panTIFFOffsets[i] = nBaseOffset + i * (toff_t)nBlockSize; + panByteCounts[i] = nBlockSize; + } + } + else + { + CPLDebug("GTiff", "Sparse files not supported in file mapping"); + return NULL; + } + } + + GIntBig nBlockSpacing = 0; + int bCompatibleSpacing = TRUE; + toff_t nPrevOffset = 0; + for(i = 0; i < poGDS->nBlocksPerBand; i ++) + { + toff_t nCurOffset; + if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) + nCurOffset = panTIFFOffsets[poGDS->nBlocksPerBand * (nBand - 1) + i]; + else + nCurOffset = panTIFFOffsets[i]; + if( nCurOffset == 0 ) + { + bCompatibleSpacing = FALSE; + break; + } + if( i > 0 ) + { + GIntBig nCurSpacing = nCurOffset - nPrevOffset; + if( i == 1 ) + { + if( nCurSpacing != (GIntBig)nBlockYSize * nLineSize ) + { + bCompatibleSpacing = FALSE; + break; + } + nBlockSpacing = nCurSpacing; + } + else if( nBlockSpacing != nCurSpacing ) + { + bCompatibleSpacing = FALSE; + break; + } + } + nPrevOffset = nCurOffset; + } + + if( !bCompatibleSpacing ) + { + return NULL; + } + else + { + vsi_l_offset nOffset; + if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG ) + { + CPLAssert( poGDS->pBaseMapping == NULL ); + nOffset = panTIFFOffsets[0]; + } + else + nOffset = panTIFFOffsets[poGDS->nBlocksPerBand * (nBand - 1)]; + CPLVirtualMem* pVMem = CPLVirtualMemFileMapNew( + fp, nOffset, nLength, + (eRWFlag == GF_Write) ? VIRTUALMEM_READWRITE : VIRTUALMEM_READONLY, + NULL, NULL); + if( pVMem == NULL ) + { + return NULL; + } + else + { + if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG ) + { + poGDS->pBaseMapping = pVMem; + pVMem = GetVirtualMemAutoInternal( eRWFlag, + pnPixelSpace, + pnLineSpace, + papszOptions ); + /* drop ref on base mapping */ + CPLVirtualMemFree(poGDS->pBaseMapping); + if( pVMem == NULL ) + poGDS->pBaseMapping = NULL; + } + else + { + *pnPixelSpace = GDALGetDataTypeSize(eDataType) / 8; + if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG ) + *pnPixelSpace *= poGDS->nBands; + *pnLineSpace = nLineSize; + } + return pVMem; + } + } +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr GTiffDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ + CPLErr eErr; + /* Try to pass the request to the most appropriate overview dataset */ + if( nBufXSize < nXSize && nBufYSize < nYSize ) + { + int nXOffMod = nXOff, nYOffMod = nYOff, nXSizeMod = nXSize, nYSizeMod = nYSize; + GDALRasterIOExtraArg sExtraArg; + + GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg); + + nJPEGOverviewVisibilityFlag ++; + int iOvrLevel = GDALBandGetBestOverviewLevel2(papoBands[0], + nXOffMod, nYOffMod, + nXSizeMod, nYSizeMod, + nBufXSize, nBufYSize, + &sExtraArg); + nJPEGOverviewVisibilityFlag --; + + if( iOvrLevel >= 0 && papoBands[0]->GetOverview(iOvrLevel) != NULL && + papoBands[0]->GetOverview(iOvrLevel)->GetDataset() != NULL ) + { + nJPEGOverviewVisibilityFlag ++; + eErr = papoBands[0]->GetOverview(iOvrLevel)->GetDataset()->RasterIO( + eRWFlag, nXOffMod, nYOffMod, nXSizeMod, nYSizeMod, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, &sExtraArg); + nJPEGOverviewVisibilityFlag --; + return eErr; + } + } + + if( eVirtualMemIOUsage != VIRTUAL_MEM_IO_NO ) + { + int nErr = VirtualMemIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg); + if (nErr >= 0) + return (CPLErr)nErr; + } + if (bDirectIO) + { + eErr = DirectIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg); + if (eErr == CE_None) + return eErr; + } + + nJPEGOverviewVisibilityFlag ++; + eErr = GDALPamDataset::IRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg); + nJPEGOverviewVisibilityFlag --; + return eErr; +} + +/************************************************************************/ +/* VirtualMemIO() */ +/************************************************************************/ + +//#define DEBUG_REACHED_VIRTUAL_MEM_IO +#ifdef DEBUG_REACHED_VIRTUAL_MEM_IO +static int anReachedVirtualMemIO[32] = { 0 }; +#define REACHED(x) anReachedVirtualMemIO[x] = 1 +#else +#define REACHED(x) +#endif + +int GTiffDataset::VirtualMemIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ + GDALDataType eDataType = GetRasterBand(1)->GetRasterDataType(); + if( eAccess == GA_Update || eRWFlag == GF_Write || bStreamingIn ) + return -1; + + /* we only know how to deal with nearest neighbour in this optimized routine */ + if( (nXSize != nBufXSize || nYSize != nBufYSize) && + psExtraArg != NULL && + psExtraArg->eResampleAlg != GRIORA_NearestNeighbour ) + { + return -1; + } + + if( !SetDirectory() ) + return CE_Failure; + + size_t nMappingSize = 0; + GByte* pabySrcData = NULL; + if( strncmp(GetDescription(), "/vsimem/", strlen("/vsimem/")) == 0 ) + { + vsi_l_offset nDataLength = 0; + pabySrcData = VSIGetMemFileBuffer(GetDescription(), &nDataLength, FALSE); + nMappingSize = (size_t)nDataLength; + if( pabySrcData == NULL ) + return -1; + } + else if( psVirtualMemIOMapping == NULL ) + { + if( !(nCompression == COMPRESSION_NONE && + (nPhotometric == PHOTOMETRIC_MINISBLACK || + nPhotometric == PHOTOMETRIC_RGB || + nPhotometric == PHOTOMETRIC_PALETTE) && + (nBitsPerSample == 8 || (nBitsPerSample == 16) || + nBitsPerSample == 32 || nBitsPerSample == 64) && + nBitsPerSample == GDALGetDataTypeSize(eDataType) && + !TIFFIsByteSwapped(hTIFF) ) ) + { + eVirtualMemIOUsage = VIRTUAL_MEM_IO_NO; + return -1; + } + VSILFILE* fp = VSI_TIFFGetVSILFile(TIFFClientdata( hTIFF )); + if( !CPLIsVirtualMemFileMapAvailable() || + VSIFGetNativeFileDescriptorL(fp) == NULL ) + { + eVirtualMemIOUsage = VIRTUAL_MEM_IO_NO; + return -1; + } + VSIFSeekL(fp, 0, SEEK_END); + vsi_l_offset nLength = VSIFTellL(fp); + if( (size_t)nLength != nLength ) + { + eVirtualMemIOUsage = VIRTUAL_MEM_IO_NO; + return -1; + } + if( eVirtualMemIOUsage == VIRTUAL_MEM_IO_IF_ENOUGH_RAM ) + { + GIntBig nRAM = CPLGetUsablePhysicalRAM(); + if( (GIntBig)nLength > nRAM ) + { + CPLDebug("GTiff", "Not enough RAM to map whole file into memory."); + eVirtualMemIOUsage = VIRTUAL_MEM_IO_NO; + return -1; + } + } + psVirtualMemIOMapping = CPLVirtualMemFileMapNew( + fp, 0, nLength, VIRTUALMEM_READONLY, NULL, NULL); + if( psVirtualMemIOMapping == NULL ) + { + eVirtualMemIOUsage = VIRTUAL_MEM_IO_NO; + return -1; + } + eVirtualMemIOUsage = VIRTUAL_MEM_IO_YES; + } + + /* Get strip offsets */ + toff_t *panOffsets = NULL; + if ( !TIFFGetField( hTIFF, (TIFFIsTiled( hTIFF )) ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS, &panOffsets ) || + panOffsets == NULL ) + { + eVirtualMemIOUsage = VIRTUAL_MEM_IO_NO; + return CE_Failure; + } + + int nDTSize = GDALGetDataTypeSize(eDataType) / 8; + int nBufDTSize = GDALGetDataTypeSize(eBufType) / 8; + + int iBand; + int bUseContigImplementation = + ( nPlanarConfig == PLANARCONFIG_CONTIG ) && (nBandCount > 1) && (nBandSpace == nBufDTSize); + for(iBand = 0; iBand < nBandCount; iBand ++ ) + { + int nBand = panBandMap[iBand]; + if( nBand != iBand + 1 ) + { + bUseContigImplementation = FALSE; + break; + } + } + + if( psVirtualMemIOMapping ) + { + nMappingSize = CPLVirtualMemGetSize(psVirtualMemIOMapping); + pabySrcData = (GByte*)CPLVirtualMemGetAddr(psVirtualMemIOMapping); + } + const int nBandsPerBlock = ( nPlanarConfig == PLANARCONFIG_SEPARATE ) ? 1 : nBands; + const int nBandsPerBlockDTSize = nBandsPerBlock * nDTSize; + const int nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize); + const int bByteOnly = (eDataType == eBufType && nDTSize == 1 ); + const int bByteNoXResampling = ( bByteOnly && nXSize == nBufXSize ); + const int nBlockSize = nBlockXSize * nBlockYSize * nBandsPerBlockDTSize; + + int bNoDataSet; + double dfNoData = GetRasterBand(1)->GetNoDataValue( &bNoDataSet ); + GByte abyNoData = 0; + if( !bNoDataSet ) + dfNoData = 0; + else if( dfNoData >= 0 && dfNoData <= 255 ) + abyNoData = (GByte) (dfNoData + 0.5); + + if( bUseContigImplementation ) + { + if( TIFFIsTiled( hTIFF ) ) + { + GByte* pabyData = (GByte*)pData; + for(int y=0;y= nLastPixelBlock ) + { + REACHED(0); + nLastPixelBlock = (nBlockXOff + 1) * nBlockXSize; + toff_t nCurOffset = panOffsets[nBlockId]; + if( nCurOffset ) + { + if( nCurOffset + nBlockSize > nMappingSize ) + { + REACHED(24); + CPLError(CE_Failure, CPLE_FileIO, + "Missing data for block %d", nBlockId); + return CE_Failure; + } + pabyLocalSrcData = pabySrcData + nCurOffset + nByteOffsetInBlock; + } + else + pabyLocalSrcData = NULL; + nByteOffsetInBlock = nBaseByteOffsetInBlock; + nBlockXOff ++; + nBlockId ++; + } + else + { + REACHED(1); + } + + if( pabyLocalSrcData == NULL ) + { + REACHED(2); + for(int iBand=0;iBand nMappingSize ) + { + REACHED(25); + CPLError(CE_Failure, CPLE_FileIO, + "Missing data for block %d", nBlockId); + return CE_Failure; + } + REACHED(5); + GDALCopyWords(pabySrcData + nCurOffset + nByteOffsetInBlock, + eDataType, nDTSize, + pabyData + y * nLineSpace + x * nPixelSpace, + eBufType, nBandSpace, + nBandCount); + } + } + } + } + } + else + { + GByte* pabyData = (GByte*)pData; + for(int y=0;y nMappingSize ) + { + REACHED(26); + CPLError(CE_Failure, CPLE_FileIO, + "Missing data for block %d", nBlockId); + return CE_Failure; + } + int nBaseByteOffsetInBlock = (nYOffsetInBlock * nBlockXSize + nXOff) * nBandsPerBlockDTSize; + GByte* pabyLocalData = pabyData + y * nLineSpace; + GByte* pabyLocalSrcData = pabySrcData + nCurOffset + nBaseByteOffsetInBlock; + if( bByteNoXResampling ) + { + if( nPixelSpace == nBandCount && nBandsPerBlockDTSize == nBandCount ) + { + REACHED(7); + memcpy(pabyLocalData, pabyLocalSrcData, nBufXSize * nBandCount); + } + else + { + REACHED(23); + for(int x=0;x= nLastPixelBlock ) + { + REACHED(11); + nLastPixelBlock = (nBlockXOff + 1) * nBlockXSize; + nByteOffsetInBlock = nBaseByteOffsetInBlock + nXOffsetInBlock * nBandsPerBlockDTSize; + nCurOffset = panOffsets[nBlockId]; + if( nCurOffset != 0 && + nCurOffset + nBlockSize > nMappingSize ) + { + REACHED(27); + CPLError(CE_Failure, CPLE_FileIO, + "Missing data for block %d", nBlockId); + return CE_Failure; + } + nXOffsetInBlock = 0; + nBlockXOff ++; + nBlockId ++; + } + else + { + REACHED(12); + nByteOffsetInBlock += nBandsPerBlockDTSize; + } + + if( nCurOffset == 0 ) + { + REACHED(13); + *pabyLocalData = abyNoData; + } + else + { + REACHED(14); + *pabyLocalData = pabySrcData[nCurOffset + nByteOffsetInBlock]; + } + pabyLocalData += nPixelSpace; + } + } + else + { + for(int x=0;x nMappingSize) + { + REACHED(28); + CPLError(CE_Failure, CPLE_FileIO, + "Missing data for block %d", nBlockId); + return CE_Failure; + } + REACHED(16); + GDALCopyWords(pabySrcData + nCurOffset + nByteOffsetInBlock, + eDataType, nDTSize, + pabyData + y * nLineSpace + x * nPixelSpace, + eBufType, nPixelSpace, + 1); + } + } + } + } + } + } + else + { + for(iBand = 0; iBand < nBandCount; iBand ++ ) + { + int nBand = panBandMap[iBand]; + GByte* pabyData = (GByte*)pData + iBand * nBandSpace; + for(int y=0;y nMappingSize ) + { + REACHED(29); + CPLError(CE_Failure, CPLE_FileIO, + "Missing data for block %d", nBlockId); + return CE_Failure; + } + int nBaseByteOffsetInBlock = (nYOffsetInBlock * nBlockXSize + nXOff) * nBandsPerBlockDTSize; + if ( nPlanarConfig == PLANARCONFIG_CONTIG ) + nBaseByteOffsetInBlock += (nBand-1) * nDTSize; + + if( bByteNoXResampling ) + { + GByte* pabyLocalData = pabyData + y * nLineSpace; + GByte* pabyLocalSrcData = pabySrcData + nCurOffset + nBaseByteOffsetInBlock; + if( nPixelSpace == 1 && nBandsPerBlockDTSize == 1 ) + { + REACHED(20); + memcpy(pabyLocalData, pabyLocalSrcData, nBufXSize); + } + else + { + REACHED(21); + for(int x=0;xGetRasterDataType(); + if( !(eRWFlag == GF_Read && + nCompression == COMPRESSION_NONE && + (nPhotometric == PHOTOMETRIC_MINISBLACK || + nPhotometric == PHOTOMETRIC_RGB || + nPhotometric == PHOTOMETRIC_PALETTE) && + (nBitsPerSample == 8 || (nBitsPerSample == 16) || + nBitsPerSample == 32 || nBitsPerSample == 64) && + nBitsPerSample == GDALGetDataTypeSize(eDataType) && + SetDirectory() && /* very important to make hTIFF uptodate! */ + !TIFFIsTiled( hTIFF )) ) + { + return CE_Failure; + } + + /* we only know how to deal with nearest neighbour in this optimized routine */ + if( (nXSize != nBufXSize || nYSize != nBufYSize) && + psExtraArg != NULL && + psExtraArg->eResampleAlg != GRIORA_NearestNeighbour ) + { + return CE_Failure; + } + + /* if the file is band interleave or only one band is requested, then */ + /* fallback to band DirectIO */ + int bUseBandRasterIO = FALSE; + if( nPlanarConfig == PLANARCONFIG_SEPARATE || nBandCount == 1 ) + { + bUseBandRasterIO = TRUE; + } + else + { + /* For the sake of simplicity, only deals with "naturally ordered" bands */ + for(int iBand = 0; iBand < nBandCount; iBand ++ ) + { + if( panBandMap[iBand] != iBand + 1) + { + bUseBandRasterIO = TRUE; + break; + } + } + } + if( bUseBandRasterIO ) + { + CPLErr eErr = CE_None; + for(int iBand = 0; eErr == CE_None && iBand < nBandCount; iBand ++ ) + { + eErr = GetRasterBand(panBandMap[iBand])->RasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + (GByte*)pData + iBand * nBandSpace, + nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace, + psExtraArg); + } + return eErr; + } + + /*CPLDebug("GTiff", "DirectIO(%d,%d,%d,%d -> %dx%d)", + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize);*/ + + /* No need to look if overviews can satisfy the request as it has already */ + /* been done in GTiffDataset::IRasterIO() */ + + /* Make sure that TIFFTAG_STRIPOFFSETS is up-to-date */ + if (GetAccess() == GA_Update) + FlushCache(); + + /* Get strip offsets */ + toff_t *panTIFFOffsets = NULL; + if ( !TIFFGetField( hTIFF, TIFFTAG_STRIPOFFSETS, &panTIFFOffsets ) || + panTIFFOffsets == NULL ) + { + return CE_Failure; + } + + int iLine; + int nReqXSize = nXSize; /* sub-sampling or over-sampling can only be done at last stage */ + int nReqYSize = MIN(nBufYSize, nYSize); /* we can do sub-sampling at the extraction stage */ + void** ppData = (void**) VSIMalloc(nReqYSize * sizeof(void*)); + vsi_l_offset* panOffsets = (vsi_l_offset*) + VSIMalloc(nReqYSize * sizeof(vsi_l_offset)); + size_t* panSizes = (size_t*) VSIMalloc(nReqYSize * sizeof(size_t)); + int nDTSize = GDALGetDataTypeSize(eDataType) / 8; + void* pTmpBuffer = NULL; + CPLErr eErr = CE_None; + int nContigBands = nBands; + int ePixelSize = nDTSize * nContigBands; + + if (ppData == NULL || panOffsets == NULL || panSizes == NULL) + eErr = CE_Failure; + /* For now we always allocate a temp buffer as it's easier */ + else /*if (nXSize != nBufXSize || nYSize != nBufYSize || + eBufType != eDataType || + nPixelSpace != GDALGetDataTypeSize(eBufType) / 8 || + check if the user buffer is large enough)*/ + { + /* We need a temporary buffer for over-sampling/sub-sampling */ + /* and/or data type conversion */ + pTmpBuffer = VSIMalloc(nReqXSize * nReqYSize * ePixelSize); + if (pTmpBuffer == NULL) + eErr = CE_Failure; + } + + /* Prepare data extraction */ + for(iLine=0;eErr == CE_None && iLine nBandCount ) + { + GByte* pabySrcData = ((GByte*)ppData[iSrcY]); + GByte* pabyDstData = ((GByte*)pData) + iY * nLineSpace; + for(int iX=0;iXnJPEGOverviewVisibilityFlag ++; + int iOvrLevel = GDALBandGetBestOverviewLevel2(this, + nXOffMod, nYOffMod, + nXSizeMod, nYSizeMod, + nBufXSize, nBufYSize, + &sExtraArg); + poGDS->nJPEGOverviewVisibilityFlag --; + + if( iOvrLevel >= 0 && GetOverview(iOvrLevel) != NULL && + GetOverview(iOvrLevel)->GetDataset() != NULL ) + { + poGDS->nJPEGOverviewVisibilityFlag ++; + eErr = GetOverview(iOvrLevel)->RasterIO( + eRWFlag, nXOffMod, nYOffMod, nXSizeMod, nYSizeMod, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, &sExtraArg); + poGDS->nJPEGOverviewVisibilityFlag --; + return eErr; + } + } + + + if( poGDS->eVirtualMemIOUsage != VIRTUAL_MEM_IO_NO ) + { + int nErr = poGDS->VirtualMemIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + 1, &nBand, nPixelSpace, nLineSpace, 0, psExtraArg); + if (nErr >= 0) + return (CPLErr)nErr; + } + if (poGDS->bDirectIO) + { + eErr = DirectIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg); + if (eErr == CE_None) + return eErr; + } + + if (poGDS->nBands != 1 && + poGDS->nPlanarConfig == PLANARCONFIG_CONTIG && + eRWFlag == GF_Read && + nXSize == nBufXSize && nYSize == nBufYSize) + { + int nBlockX1 = nXOff / nBlockXSize; + int nBlockY1 = nYOff / nBlockYSize; + int nBlockX2 = (nXOff + nXSize - 1) / nBlockXSize; + int nBlockY2 = (nYOff + nYSize - 1) / nBlockYSize; + int nXBlocks = nBlockX2 - nBlockX1 + 1; + int nYBlocks = nBlockY2 - nBlockY1 + 1; + GIntBig nRequiredMem = (GIntBig)poGDS->nBands * nXBlocks * nYBlocks * + nBlockXSize * nBlockYSize * + (GDALGetDataTypeSize(eDataType) / 8); + if (nRequiredMem > GDALGetCacheMax64()) + { + if (!poGDS->bHasWarnedDisableAggressiveBandCaching) + { + CPLDebug("GTiff", "Disable aggressive band caching. Cache not big enough. " + "At least " CPL_FRMT_GIB " bytes necessary", nRequiredMem); + poGDS->bHasWarnedDisableAggressiveBandCaching = TRUE; + } + poGDS->bLoadingOtherBands = TRUE; + } + } + + poGDS->nJPEGOverviewVisibilityFlag ++; + eErr = GDALPamRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg); + poGDS->nJPEGOverviewVisibilityFlag --; + + poGDS->bLoadingOtherBands = FALSE; + + return eErr; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GTiffRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + int nBlockBufSize, nBlockId, nBlockIdBand0; + CPLErr eErr = CE_None; + + if (!poGDS->SetDirectory()) + return CE_Failure; + + if( TIFFIsTiled(poGDS->hTIFF) ) + nBlockBufSize = TIFFTileSize( poGDS->hTIFF ); + else + { + CPLAssert( nBlockXOff == 0 ); + nBlockBufSize = TIFFStripSize( poGDS->hTIFF ); + } + + CPLAssert(nBlocksPerRow != 0); + nBlockIdBand0 = nBlockXOff + nBlockYOff * nBlocksPerRow; + if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) + nBlockId = nBlockIdBand0 + (nBand-1) * poGDS->nBlocksPerBand; + else + nBlockId = nBlockIdBand0; + +/* -------------------------------------------------------------------- */ +/* The bottom most partial tiles and strips are sometimes only */ +/* partially encoded. This code reduces the requested data so */ +/* an error won't be reported in this case. (#1179) */ +/* -------------------------------------------------------------------- */ + int nBlockReqSize = nBlockBufSize; + + if( (nBlockYOff+1) * nBlockYSize > nRasterYSize ) + { + nBlockReqSize = (nBlockBufSize / nBlockYSize) + * (nBlockYSize - (((nBlockYOff+1) * nBlockYSize) % nRasterYSize)); + } + +/* -------------------------------------------------------------------- */ +/* Handle the case of a strip or tile that doesn't exist yet. */ +/* Just set to zeros and return. */ +/* -------------------------------------------------------------------- */ + if( nBlockId != poGDS->nLoadedBlock && !poGDS->IsBlockAvailable(nBlockId) ) + { + NullBlock( pImage ); + return CE_None; + } + + if( poGDS->bStreamingIn && + !(poGDS->nBands > 1 && poGDS->nPlanarConfig == PLANARCONFIG_CONTIG && nBlockId == poGDS->nLoadedBlock) ) + { + toff_t* panOffsets = NULL; + TIFFGetField( poGDS->hTIFF, (TIFFIsTiled( poGDS->hTIFF )) ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS , &panOffsets ); + if( panOffsets == NULL ) + return CE_Failure; + if( panOffsets[nBlockId] < VSIFTellL(poGDS->fpL) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Trying to load block %d at offset " CPL_FRMT_GUIB + " whereas current pos is " CPL_FRMT_GUIB " (backward read not supported)", + nBlockId, (GUIntBig)panOffsets[nBlockId], + (GUIntBig)VSIFTellL(poGDS->fpL) ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Handle simple case (separate, onesampleperpixel) */ +/* -------------------------------------------------------------------- */ + if( poGDS->nBands == 1 + || poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) + { + if( nBlockReqSize < nBlockBufSize ) + memset( pImage, 0, nBlockBufSize ); + + if( TIFFIsTiled( poGDS->hTIFF ) ) + { + if( TIFFReadEncodedTile( poGDS->hTIFF, nBlockId, pImage, + nBlockReqSize ) == -1 + && !poGDS->bIgnoreReadErrors ) + { + memset( pImage, 0, nBlockBufSize ); + CPLError( CE_Failure, CPLE_AppDefined, + "TIFFReadEncodedTile() failed.\n" ); + + eErr = CE_Failure; + } + } + else + { + if( TIFFReadEncodedStrip( poGDS->hTIFF, nBlockId, pImage, + nBlockReqSize ) == -1 + && !poGDS->bIgnoreReadErrors ) + { + memset( pImage, 0, nBlockBufSize ); + CPLError( CE_Failure, CPLE_AppDefined, + "TIFFReadEncodedStrip() failed.\n" ); + + eErr = CE_Failure; + } + } + + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Load desired block */ +/* -------------------------------------------------------------------- */ + eErr = poGDS->LoadBlockBuf( nBlockId ); + if( eErr != CE_None ) + { + memset( pImage, 0, + nBlockXSize * nBlockYSize + * (GDALGetDataTypeSize(eDataType) / 8) ); + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Special case for YCbCr subsampled data. */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + if( (eBandInterp == GCI_YCbCr_YBand + || eBandInterp == GCI_YCbCr_CbBand + || eBandInterp == GCI_YCbCr_CrBand) + && poGDS->nBitsPerSample == 8 ) + { + uint16 hs, vs; + int iX, iY; + + TIFFGetFieldDefaulted( poGDS->hTIFF, TIFFTAG_YCBCRSUBSAMPLING, + &hs, &vs); + + for( iY = 0; iY < nBlockYSize; iY++ ) + { + for( iX = 0; iX < nBlockXSize; iX++ ) + { + int iBlock = (iY / vs) * (nBlockXSize/hs) + (iX / hs); + GByte *pabySrcBlock = poGDS->pabyBlockBuf + + (vs * hs + 2) * iBlock; + + if( eBandInterp == GCI_YCbCr_YBand ) + ((GByte *)pImage)[iY*nBlockXSize + iX] = + pabySrcBlock[(iX % hs) + (iY % vs) * hs]; + else if( eBandInterp == GCI_YCbCr_CbBand ) + ((GByte *)pImage)[iY*nBlockXSize + iX] = + pabySrcBlock[vs * hs + 0]; + else if( eBandInterp == GCI_YCbCr_CrBand ) + ((GByte *)pImage)[iY*nBlockXSize + iX] = + pabySrcBlock[vs * hs + 1]; + } + } + + return CE_None; + } +#endif + +/* -------------------------------------------------------------------- */ +/* Handle simple case of eight bit data, and pixel interleaving. */ +/* -------------------------------------------------------------------- */ + if( poGDS->nBitsPerSample == 8 ) + { + int i, nBlockPixels; + GByte *pabyImage; + GByte *pabyImageDest = (GByte*)pImage; + int nBands = poGDS->nBands; + + pabyImage = poGDS->pabyBlockBuf + nBand - 1; + + nBlockPixels = nBlockXSize * nBlockYSize; + +/* ==================================================================== */ +/* Optimization for high number of words to transfer and some */ +/* typical band numbers : we unroll the loop. */ +/* ==================================================================== */ +#define COPY_TO_DST_BUFFER(nBands) \ + if (nBlockPixels > 100) \ + { \ + for ( i = nBlockPixels / 16; i != 0; i -- ) \ + { \ + pabyImageDest[0] = pabyImage[0*nBands]; \ + pabyImageDest[1] = pabyImage[1*nBands]; \ + pabyImageDest[2] = pabyImage[2*nBands]; \ + pabyImageDest[3] = pabyImage[3*nBands]; \ + pabyImageDest[4] = pabyImage[4*nBands]; \ + pabyImageDest[5] = pabyImage[5*nBands]; \ + pabyImageDest[6] = pabyImage[6*nBands]; \ + pabyImageDest[7] = pabyImage[7*nBands]; \ + pabyImageDest[8] = pabyImage[8*nBands]; \ + pabyImageDest[9] = pabyImage[9*nBands]; \ + pabyImageDest[10] = pabyImage[10*nBands]; \ + pabyImageDest[11] = pabyImage[11*nBands]; \ + pabyImageDest[12] = pabyImage[12*nBands]; \ + pabyImageDest[13] = pabyImage[13*nBands]; \ + pabyImageDest[14] = pabyImage[14*nBands]; \ + pabyImageDest[15] = pabyImage[15*nBands]; \ + pabyImageDest += 16; \ + pabyImage += 16*nBands; \ + } \ + nBlockPixels = nBlockPixels % 16; \ + } \ + for( i = 0; i < nBlockPixels; i++ ) \ + { \ + pabyImageDest[i] = *pabyImage; \ + pabyImage += nBands; \ + } + + switch (nBands) + { + case 3: COPY_TO_DST_BUFFER(3); break; + case 4: COPY_TO_DST_BUFFER(4); break; + default: + { + for( i = 0; i < nBlockPixels; i++ ) + { + pabyImageDest[i] = *pabyImage; + pabyImage += nBands; + } + } + } +#undef COPY_TO_DST_BUFFER + } + + else + { + int i, nBlockPixels, nWordBytes; + GByte *pabyImage; + + nWordBytes = poGDS->nBitsPerSample / 8; + pabyImage = poGDS->pabyBlockBuf + (nBand - 1) * nWordBytes; + + nBlockPixels = nBlockXSize * nBlockYSize; + for( i = 0; i < nBlockPixels; i++ ) + { + for( int j = 0; j < nWordBytes; j++ ) + { + ((GByte *) pImage)[i*nWordBytes + j] = pabyImage[j]; + } + pabyImage += poGDS->nBands * nWordBytes; + } + } + + if (eErr == CE_None) + eErr = FillCacheForOtherBands(nBlockXOff, nBlockYOff); + + return eErr; +} + + +/************************************************************************/ +/* FillCacheForOtherBands() */ +/************************************************************************/ + +CPLErr GTiffRasterBand::FillCacheForOtherBands( int nBlockXOff, int nBlockYOff ) + +{ + CPLErr eErr = CE_None; +/* -------------------------------------------------------------------- */ +/* In the fairly common case of pixel interleaved 8bit data */ +/* that is multi-band, lets push the rest of the data into the */ +/* block cache too, to avoid (hopefully) having to redecode it. */ +/* */ +/* Our following logic actually depends on the fact that the */ +/* this block is already loaded, so subsequent calls will end */ +/* up back in this method and pull from the loaded block. */ +/* */ +/* Be careful not entering this portion of code from */ +/* the other bands, otherwise we'll get very deep nested calls */ +/* and O(nBands^2) performance ! */ +/* */ +/* If there are many bands and the block cache size is not big */ +/* enough to accommodate the size of all the blocks, don't enter */ +/* -------------------------------------------------------------------- */ + if( poGDS->nBands != 1 && !poGDS->bLoadingOtherBands && + nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType) / 8) < GDALGetCacheMax64() / poGDS->nBands) + { + int iOtherBand; + + poGDS->bLoadingOtherBands = TRUE; + + for( iOtherBand = 1; iOtherBand <= poGDS->nBands; iOtherBand++ ) + { + if( iOtherBand == nBand ) + continue; + + GDALRasterBlock *poBlock; + + poBlock = poGDS->GetRasterBand(iOtherBand)-> + GetLockedBlockRef(nBlockXOff,nBlockYOff); + if (poBlock == NULL) + { + eErr = CE_Failure; + break; + } + poBlock->DropLock(); + } + + poGDS->bLoadingOtherBands = FALSE; + } + + return eErr; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr GTiffRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + int nBlockId; + CPLErr eErr = CE_None; + + if (poGDS->bDebugDontWriteBlocks) + return CE_None; + + if (poGDS->bWriteErrorInFlushBlockBuf) + { + /* Report as an error if a previously loaded block couldn't be */ + /* written correctly */ + poGDS->bWriteErrorInFlushBlockBuf = FALSE; + return CE_Failure; + } + + if (!poGDS->SetDirectory()) + return CE_Failure; + + CPLAssert( poGDS != NULL + && nBlockXOff >= 0 + && nBlockYOff >= 0 + && pImage != NULL ); + CPLAssert(nBlocksPerRow != 0); + +/* -------------------------------------------------------------------- */ +/* Handle case of "separate" images */ +/* -------------------------------------------------------------------- */ + if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE + || poGDS->nBands == 1 ) + { + nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow + + (nBand-1) * poGDS->nBlocksPerBand; + + eErr = poGDS->WriteEncodedTileOrStrip(nBlockId, pImage, TRUE); + + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Handle case of pixel interleaved (PLANARCONFIG_CONTIG) images. */ +/* -------------------------------------------------------------------- */ + nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow; + + eErr = poGDS->LoadBlockBuf( nBlockId ); + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* On write of pixel interleaved data, we might as well flush */ +/* out any other bands that are dirty in our cache. This is */ +/* especially helpful when writing compressed blocks. */ +/* -------------------------------------------------------------------- */ + int iBand; + int nWordBytes = poGDS->nBitsPerSample / 8; + int nBands = poGDS->nBands; + + for( iBand = 0; iBand < nBands; iBand++ ) + { + const GByte *pabyThisImage = NULL; + GDALRasterBlock *poBlock = NULL; + + if( iBand+1 == nBand ) + pabyThisImage = (GByte *) pImage; + else + { + poBlock = ((GTiffRasterBand *)poGDS->GetRasterBand( iBand+1 )) + ->TryGetLockedBlockRef( nBlockXOff, nBlockYOff ); + + if( poBlock == NULL ) + continue; + + if( !poBlock->GetDirty() ) + { + poBlock->DropLock(); + continue; + } + + pabyThisImage = (GByte *) poBlock->GetDataRef(); + } + + int i, nBlockPixels = nBlockXSize * nBlockYSize; + GByte *pabyOut = poGDS->pabyBlockBuf + iBand*nWordBytes; + + if (nWordBytes == 1) + { + +/* ==================================================================== */ +/* Optimization for high number of words to transfer and some */ +/* typical band numbers : we unroll the loop. */ +/* ==================================================================== */ +#define COPY_TO_DST_BUFFER(nBands) \ + if (nBlockPixels > 100) \ + { \ + for ( i = nBlockPixels / 16; i != 0; i -- ) \ + { \ + pabyOut[0*nBands] = pabyThisImage[0]; \ + pabyOut[1*nBands] = pabyThisImage[1]; \ + pabyOut[2*nBands] = pabyThisImage[2]; \ + pabyOut[3*nBands] = pabyThisImage[3]; \ + pabyOut[4*nBands] = pabyThisImage[4]; \ + pabyOut[5*nBands] = pabyThisImage[5]; \ + pabyOut[6*nBands] = pabyThisImage[6]; \ + pabyOut[7*nBands] = pabyThisImage[7]; \ + pabyOut[8*nBands] = pabyThisImage[8]; \ + pabyOut[9*nBands] = pabyThisImage[9]; \ + pabyOut[10*nBands] = pabyThisImage[10]; \ + pabyOut[11*nBands] = pabyThisImage[11]; \ + pabyOut[12*nBands] = pabyThisImage[12]; \ + pabyOut[13*nBands] = pabyThisImage[13]; \ + pabyOut[14*nBands] = pabyThisImage[14]; \ + pabyOut[15*nBands] = pabyThisImage[15]; \ + pabyThisImage += 16; \ + pabyOut += 16*nBands; \ + } \ + nBlockPixels = nBlockPixels % 16; \ + } \ + for( i = 0; i < nBlockPixels; i++ ) \ + { \ + *pabyOut = pabyThisImage[i]; \ + pabyOut += nBands; \ + } + + switch (nBands) + { + case 3: COPY_TO_DST_BUFFER(3); break; + case 4: COPY_TO_DST_BUFFER(4); break; + default: + { + for( i = 0; i < nBlockPixels; i++ ) + { + *pabyOut = pabyThisImage[i]; + pabyOut += nBands; + } + } + } +#undef COPY_TO_DST_BUFFER + } + else + { + for( i = 0; i < nBlockPixels; i++ ) + { + memcpy( pabyOut, pabyThisImage, nWordBytes ); + + pabyOut += nWordBytes * nBands; + pabyThisImage += nWordBytes; + } + } + + if( poBlock != NULL ) + { + poBlock->MarkClean(); + poBlock->DropLock(); + } + } + + poGDS->bLoadedBlockDirty = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +void GTiffRasterBand::SetDescription( const char *pszDescription ) + +{ + if( pszDescription == NULL ) + pszDescription = ""; + + osDescription = pszDescription; +} + +/************************************************************************/ +/* GetDescription() */ +/************************************************************************/ + +const char *GTiffRasterBand::GetDescription() const +{ + return osDescription; +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +double GTiffRasterBand::GetOffset( int *pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bHaveOffsetScale; + return dfOffset; +} + +/************************************************************************/ +/* SetOffset() */ +/************************************************************************/ + +CPLErr GTiffRasterBand::SetOffset( double dfNewValue ) + +{ + if( !bHaveOffsetScale || dfNewValue != dfOffset ) + poGDS->bMetadataChanged = TRUE; + + bHaveOffsetScale = TRUE; + dfOffset = dfNewValue; + return CE_None; +} + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ + +double GTiffRasterBand::GetScale( int *pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bHaveOffsetScale; + return dfScale; +} + +/************************************************************************/ +/* SetScale() */ +/************************************************************************/ + +CPLErr GTiffRasterBand::SetScale( double dfNewValue ) + +{ + if( !bHaveOffsetScale || dfNewValue != dfScale ) + poGDS->bMetadataChanged = TRUE; + + bHaveOffsetScale = TRUE; + dfScale = dfNewValue; + return CE_None; +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +const char* GTiffRasterBand::GetUnitType() + +{ + return osUnitType.c_str(); +} + +/************************************************************************/ +/* SetUnitType() */ +/************************************************************************/ + +CPLErr GTiffRasterBand::SetUnitType( const char* pszNewValue ) + +{ + CPLString osNewValue(pszNewValue ? pszNewValue : ""); + if( osNewValue.compare(osUnitType) != 0 ) + poGDS->bMetadataChanged = TRUE; + + osUnitType = osNewValue; + return CE_None; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **GTiffRasterBand::GetMetadataDomainList() +{ + return CSLDuplicate(oGTiffMDMD.GetDomainList()); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GTiffRasterBand::GetMetadata( const char * pszDomain ) + +{ + return oGTiffMDMD.GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr GTiffRasterBand::SetMetadata( char ** papszMD, const char *pszDomain ) + +{ + if( poGDS->bStreamingOut && poGDS->bCrystalized ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot modify metadata at that point in a streamed output file"); + return CE_Failure; + } + + if( pszDomain == NULL || !EQUAL(pszDomain,"_temporary_") ) + { + if( papszMD != NULL || GetMetadata(pszDomain) != NULL ) + { + poGDS->bMetadataChanged = TRUE; + // Cancel any existing metadata from PAM file + if( eAccess == GA_Update && + GDALPamRasterBand::GetMetadata(pszDomain) != NULL ) + GDALPamRasterBand::SetMetadata(papszMD, pszDomain); + } + } + + return oGTiffMDMD.SetMetadata( papszMD, pszDomain ); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *GTiffRasterBand::GetMetadataItem( const char * pszName, + const char * pszDomain ) + +{ + if( pszName != NULL && pszDomain != NULL && EQUAL(pszDomain, "TIFF") ) + { + int nBlockXOff, nBlockYOff; + + if( EQUAL(pszName, "JPEGTABLES") ) + { + if( !poGDS->SetDirectory() ) + return NULL; + + uint32 nJPEGTableSize = 0; + void* pJPEGTable = NULL; + if( TIFFGetField(poGDS->hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize, &pJPEGTable) != 1 || + pJPEGTable == NULL || (int)nJPEGTableSize <= 0 ) + { + return NULL; + } + char* pszHex = CPLBinaryToHex( nJPEGTableSize, (const GByte*)pJPEGTable ); + const char* pszReturn = CPLSPrintf("%s", pszHex); + CPLFree(pszHex); + return pszReturn; + } + else if( sscanf(pszName, "BLOCK_OFFSET_%d_%d", &nBlockXOff, &nBlockYOff) == 2 ) + { + if( !poGDS->SetDirectory() ) + return NULL; + + int nBlocksPerRow = DIV_ROUND_UP(poGDS->nRasterXSize, poGDS->nBlockXSize); + int nBlocksPerColumn = DIV_ROUND_UP(poGDS->nRasterYSize, poGDS->nBlockYSize); + if( nBlockXOff < 0 || nBlockXOff >= nBlocksPerRow || + nBlockYOff < 0 || nBlockYOff >= nBlocksPerColumn ) + return NULL; + + int nBlockId = nBlockYOff * nBlocksPerRow + nBlockXOff; + if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) + { + nBlockId += (nBand-1) * poGDS->nBlocksPerBand; + } + + if( !poGDS->IsBlockAvailable(nBlockId) ) + { + return NULL; + } + + toff_t *panOffsets = NULL; + TIFF* hTIFF = poGDS->hTIFF; + if( (( TIFFIsTiled( hTIFF ) + && TIFFGetField( hTIFF, TIFFTAG_TILEOFFSETS, &panOffsets ) ) + || ( !TIFFIsTiled( hTIFF ) + && TIFFGetField( hTIFF, TIFFTAG_STRIPOFFSETS, &panOffsets ) )) && + panOffsets != NULL ) + { + return CPLSPrintf(CPL_FRMT_GUIB, (GUIntBig)panOffsets[nBlockId]); + } + else + { + return NULL; + } + } + else if( sscanf(pszName, "BLOCK_SIZE_%d_%d", &nBlockXOff, &nBlockYOff) == 2 ) + { + if( !poGDS->SetDirectory() ) + return NULL; + + int nBlocksPerRow = DIV_ROUND_UP(poGDS->nRasterXSize, poGDS->nBlockXSize); + int nBlocksPerColumn = DIV_ROUND_UP(poGDS->nRasterYSize, poGDS->nBlockYSize); + if( nBlockXOff < 0 || nBlockXOff >= nBlocksPerRow || + nBlockYOff < 0 || nBlockYOff >= nBlocksPerColumn ) + return NULL; + + int nBlockId = nBlockYOff * nBlocksPerRow + nBlockXOff; + if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) + { + nBlockId += (nBand-1) * poGDS->nBlocksPerBand; + } + + if( !poGDS->IsBlockAvailable(nBlockId) ) + { + return NULL; + } + + toff_t *panByteCounts = NULL; + TIFF* hTIFF = poGDS->hTIFF; + if( (( TIFFIsTiled( hTIFF ) + && TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts ) ) + || ( !TIFFIsTiled( hTIFF ) + && TIFFGetField( hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts ) )) && + panByteCounts != NULL ) + { + return CPLSPrintf(CPL_FRMT_GUIB, (GUIntBig)panByteCounts[nBlockId]); + } + else + { + return NULL; + } + } + } + return oGTiffMDMD.GetMetadataItem( pszName, pszDomain ); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GTiffRasterBand::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ + if( poGDS->bStreamingOut && poGDS->bCrystalized ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot modify metadata at that point in a streamed output file"); + return CE_Failure; + } + + if( pszDomain == NULL || !EQUAL(pszDomain,"_temporary_") ) + { + poGDS->bMetadataChanged = TRUE; + // Cancel any existing metadata from PAM file + if( eAccess == GA_Update && + GDALPamRasterBand::GetMetadataItem(pszName, pszDomain) != NULL ) + GDALPamRasterBand::SetMetadataItem(pszName, NULL, pszDomain); + } + + return oGTiffMDMD.SetMetadataItem( pszName, pszValue, pszDomain ); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp GTiffRasterBand::GetColorInterpretation() + +{ + return eBandInterp; +} + +/************************************************************************/ +/* GTiffGetAlphaValue() */ +/************************************************************************/ + + /* Note: was EXTRASAMPLE_ASSOCALPHA in GDAL < 1.10 */ +#define DEFAULT_ALPHA_TYPE EXTRASAMPLE_UNASSALPHA + +static uint16 GTiffGetAlphaValue(const char* pszValue, uint16 nDefault) +{ + if (pszValue == NULL) + return nDefault; + else if (EQUAL(pszValue, "YES")) + return DEFAULT_ALPHA_TYPE; + else if (EQUAL(pszValue, "PREMULTIPLIED")) + return EXTRASAMPLE_ASSOCALPHA; + else if (EQUAL(pszValue, "NON-PREMULTIPLIED")) + return EXTRASAMPLE_UNASSALPHA; + else if (EQUAL(pszValue, "NO") || + EQUAL(pszValue, "UNSPECIFIED")) + return EXTRASAMPLE_UNSPECIFIED; + else + return nDefault; +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +CPLErr GTiffRasterBand::SetColorInterpretation( GDALColorInterp eInterp ) + +{ + if( eInterp == eBandInterp ) + return CE_None; + + eBandInterp = eInterp; + + if( poGDS->bCrystalized ) + { + CPLDebug("GTIFF", "ColorInterpretation %s for band %d goes to PAM instead of TIFF tag", + GDALGetColorInterpretationName(eInterp), nBand); + return GDALPamRasterBand::SetColorInterpretation( eInterp ); + } + + /* greyscale + alpha */ + else if( eInterp == GCI_AlphaBand + && nBand == 2 + && poGDS->nSamplesPerPixel == 2 + && poGDS->nPhotometric == PHOTOMETRIC_MINISBLACK ) + { + uint16 v[1]; + v[0] = GTiffGetAlphaValue(CPLGetConfigOption("GTIFF_ALPHA", NULL), + DEFAULT_ALPHA_TYPE); + + TIFFSetField(poGDS->hTIFF, TIFFTAG_EXTRASAMPLES, 1, v); + return CE_None; + } + + /* RGB + alpha */ + else if( eInterp == GCI_AlphaBand + && nBand == 4 + && poGDS->nSamplesPerPixel == 4 + && poGDS->nPhotometric == PHOTOMETRIC_RGB ) + { + uint16 v[1]; + v[0] = GTiffGetAlphaValue(CPLGetConfigOption("GTIFF_ALPHA", NULL), + DEFAULT_ALPHA_TYPE); + + TIFFSetField(poGDS->hTIFF, TIFFTAG_EXTRASAMPLES, 1, v); + return CE_None; + } + + else + { + /* Try to autoset TIFFTAG_PHOTOMETRIC = PHOTOMETRIC_RGB if possible */ + if( poGDS->nCompression != COMPRESSION_JPEG && + poGDS->nSetPhotometricFromBandColorInterp >= 0 && + CSLFetchNameValue( poGDS->papszCreationOptions, "PHOTOMETRIC") == NULL && + (poGDS->nBands == 3 || poGDS->nBands == 4) && + ((nBand == 1 && eInterp == GCI_RedBand) || + (nBand == 2 && eInterp == GCI_GreenBand) || + (nBand == 3 && eInterp == GCI_BlueBand) || + (nBand == 4 && eInterp == GCI_AlphaBand)) ) + { + poGDS->nSetPhotometricFromBandColorInterp ++; + if( poGDS->nSetPhotometricFromBandColorInterp == poGDS->nBands ) + { + poGDS->nPhotometric = PHOTOMETRIC_RGB; + TIFFSetField(poGDS->hTIFF, TIFFTAG_PHOTOMETRIC, poGDS->nPhotometric); + if( poGDS->nSetPhotometricFromBandColorInterp == 4 ) + { + uint16 v[1]; + v[0] = GTiffGetAlphaValue(CPLGetConfigOption("GTIFF_ALPHA", NULL), + DEFAULT_ALPHA_TYPE); + + TIFFSetField(poGDS->hTIFF, TIFFTAG_EXTRASAMPLES, 1, v); + } + } + return CE_None; + } + else + { + if( poGDS->nPhotometric != PHOTOMETRIC_MINISBLACK && + CSLFetchNameValue( poGDS->papszCreationOptions, "PHOTOMETRIC") == NULL ) + { + poGDS->nPhotometric = PHOTOMETRIC_MINISBLACK; + TIFFSetField(poGDS->hTIFF, TIFFTAG_PHOTOMETRIC, poGDS->nPhotometric); + } + if( poGDS->nSetPhotometricFromBandColorInterp > 0 ) + { + for(int i=1;i<=poGDS->nBands;i++) + { + if( i != nBand ) + { + ((GDALPamRasterBand*)poGDS->GetRasterBand(i))->GDALPamRasterBand::SetColorInterpretation( + poGDS->GetRasterBand(i)->GetColorInterpretation() ); + CPLDebug("GTIFF", "ColorInterpretation %s for band %d goes to PAM instead of TIFF tag", + GDALGetColorInterpretationName(poGDS->GetRasterBand(i)->GetColorInterpretation()), i); + } + } + } + poGDS->nSetPhotometricFromBandColorInterp = -1; + CPLDebug("GTIFF", "ColorInterpretation %s for band %d goes to PAM instead of TIFF tag", + GDALGetColorInterpretationName(eInterp), nBand); + return GDALPamRasterBand::SetColorInterpretation( eInterp ); + } + } +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *GTiffRasterBand::GetColorTable() + +{ + if( nBand == 1 ) + return poGDS->poColorTable; + else + return NULL; +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr GTiffRasterBand::SetColorTable( GDALColorTable * poCT ) + +{ +/* -------------------------------------------------------------------- */ +/* Check if this is even a candidate for applying a PCT. */ +/* -------------------------------------------------------------------- */ + if( nBand != 1) + { + CPLError( CE_Failure, CPLE_NotSupported, + "SetColorTable() can only be called on band 1." ); + return CE_Failure; + } + + if( poGDS->nSamplesPerPixel != 1 && poGDS->nSamplesPerPixel != 2) + { + CPLError( CE_Failure, CPLE_NotSupported, + "SetColorTable() not supported for multi-sample TIFF files." ); + return CE_Failure; + } + + if( eDataType != GDT_Byte && eDataType != GDT_UInt16 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "SetColorTable() only supported for Byte or UInt16 bands in TIFF format." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* We are careful about calling SetDirectory() to avoid */ +/* prematurely crystalizing the directory. (#2820) */ +/* -------------------------------------------------------------------- */ + if( poGDS->bCrystalized ) + { + if (!poGDS->SetDirectory()) + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Is this really a request to clear the color table? */ +/* -------------------------------------------------------------------- */ + if( poCT == NULL || poCT->GetColorEntryCount() == 0 ) + { + TIFFSetField( poGDS->hTIFF, TIFFTAG_PHOTOMETRIC, + PHOTOMETRIC_MINISBLACK ); + +#ifdef HAVE_UNSETFIELD + TIFFUnsetField( poGDS->hTIFF, TIFFTAG_COLORMAP ); +#else + CPLDebug( "GTiff", + "TIFFUnsetField() not supported, colormap may not be cleared." ); +#endif + + if( poGDS->poColorTable ) + { + delete poGDS->poColorTable; + poGDS->poColorTable = NULL; + } + + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Write out the colortable, and update the configuration. */ +/* -------------------------------------------------------------------- */ + int nColors; + + if( eDataType == GDT_Byte ) + nColors = 256; + else + nColors = 65536; + + unsigned short *panTRed, *panTGreen, *panTBlue; + + panTRed = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors); + panTGreen = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors); + panTBlue = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors); + + for( int iColor = 0; iColor < nColors; iColor++ ) + { + if( iColor < poCT->GetColorEntryCount() ) + { + GDALColorEntry sRGB; + + poCT->GetColorEntryAsRGB( iColor, &sRGB ); + + panTRed[iColor] = (unsigned short) (257 * sRGB.c1); + panTGreen[iColor] = (unsigned short) (257 * sRGB.c2); + panTBlue[iColor] = (unsigned short) (257 * sRGB.c3); + } + else + { + panTRed[iColor] = panTGreen[iColor] = panTBlue[iColor] = 0; + } + } + + TIFFSetField( poGDS->hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE ); + TIFFSetField( poGDS->hTIFF, TIFFTAG_COLORMAP, + panTRed, panTGreen, panTBlue ); + + CPLFree( panTRed ); + CPLFree( panTGreen ); + CPLFree( panTBlue ); + + if( poGDS->poColorTable ) + delete poGDS->poColorTable; + + /* libtiff 3.X needs setting this in all cases (creation or update) */ + /* whereas libtiff 4.X would just need it if there */ + /* was no color table before */ +#if 0 + else +#endif + poGDS->bNeedsRewrite = TRUE; + + poGDS->poColorTable = poCT->Clone(); + eBandInterp = GCI_PaletteIndex; + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double GTiffRasterBand::GetNoDataValue( int * pbSuccess ) + +{ + if( bNoDataSet ) + { + if( pbSuccess ) + *pbSuccess = TRUE; + + return dfNoDataValue; + } + + if( poGDS->bNoDataSet ) + { + if( pbSuccess ) + *pbSuccess = TRUE; + + return poGDS->dfNoDataValue; + } + + return GDALPamRasterBand::GetNoDataValue( pbSuccess ); +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr GTiffRasterBand::SetNoDataValue( double dfNoData ) + +{ + if( poGDS->bNoDataSet && poGDS->dfNoDataValue == dfNoData ) + return CE_None; + if( poGDS->bStreamingOut && poGDS->bCrystalized ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot modify nodata at that point in a streamed output file"); + return CE_Failure; + } + + poGDS->bNoDataSet = TRUE; + poGDS->dfNoDataValue = dfNoData; + + poGDS->bNoDataChanged = TRUE; + + bNoDataSet = TRUE; + dfNoDataValue = dfNoData; + return CE_None; +} + +/************************************************************************/ +/* NullBlock() */ +/* */ +/* Set the block data to the null value if it is set, or zero */ +/* if there is no null data value. */ +/************************************************************************/ + +void GTiffRasterBand::NullBlock( void *pData ) + +{ + int nWords = nBlockXSize * nBlockYSize; + int nChunkSize = MAX(1,GDALGetDataTypeSize(eDataType)/8); + + int bNoDataSet; + double dfNoData = GetNoDataValue( &bNoDataSet ); + if( !bNoDataSet ) + { +#ifdef ESRI_BUILD + if ( poGDS->nBitsPerSample >= 2 ) + memset( pData, 0, nWords*nChunkSize ); + else + memset( pData, 1, nWords*nChunkSize ); +#else + memset( pData, 0, nWords*nChunkSize ); +#endif + } + else + { + /* Will convert nodata value to the right type and copy efficiently */ + GDALCopyWords( &dfNoData, GDT_Float64, 0, + pData, eDataType, nChunkSize, nWords); + } +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int GTiffRasterBand::GetOverviewCount() + +{ + poGDS->ScanDirectories(); + + if( poGDS->nOverviewCount > 0 ) + { + return poGDS->nOverviewCount; + } + else + { + int nOverviewCount = GDALRasterBand::GetOverviewCount(); + if( nOverviewCount > 0 ) + return nOverviewCount; + + /* Implict JPEG overviews are normally hidden, except when doing */ + /* IRasterIO() operations */ + if( poGDS->nJPEGOverviewVisibilityFlag ) + return poGDS->GetJPEGOverviewCount(); + else + return 0; + } +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *GTiffRasterBand::GetOverview( int i ) + +{ + poGDS->ScanDirectories(); + + if( poGDS->nOverviewCount > 0 ) + { + /* Do we have internal overviews ? */ + if( i < 0 || i >= poGDS->nOverviewCount ) + return NULL; + else + return poGDS->papoOverviewDS[i]->GetRasterBand(nBand); + } + else + { + GDALRasterBand* poOvrBand = GDALRasterBand::GetOverview( i ); + if( poOvrBand != NULL ) + return poOvrBand; + + /* For consistency with GetOverviewCount(), we should also test */ + /* nJPEGOverviewVisibilityFlag, but it is also convenient to be able */ + /* to query them for testing purposes. */ + if( i >= 0 && i < poGDS->GetJPEGOverviewCount() ) + return poGDS->papoJPEGOverviewDS[i]->GetRasterBand(nBand); + else + return NULL; + } +} + +/************************************************************************/ +/* GetMaskFlags() */ +/************************************************************************/ + +int GTiffRasterBand::GetMaskFlags() +{ + poGDS->ScanDirectories(); + + if( poGDS->poMaskDS != NULL ) + { + if( poGDS->poMaskDS->GetRasterCount() == 1) + { + return GMF_PER_DATASET; + } + else + { + return 0; + } + } + else + return GDALPamRasterBand::GetMaskFlags(); +} + +/************************************************************************/ +/* GetMaskBand() */ +/************************************************************************/ + +GDALRasterBand *GTiffRasterBand::GetMaskBand() +{ + poGDS->ScanDirectories(); + + if( poGDS->poMaskDS != NULL ) + { + if( poGDS->poMaskDS->GetRasterCount() == 1) + return poGDS->poMaskDS->GetRasterBand(1); + else + return poGDS->poMaskDS->GetRasterBand(nBand); + } + else + return GDALPamRasterBand::GetMaskBand(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* GTiffSplitBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GTiffSplitBand : public GTiffRasterBand +{ + friend class GTiffDataset; + + public: + + GTiffSplitBand( GTiffDataset *, int ); + virtual ~GTiffSplitBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); +}; + +/************************************************************************/ +/* GTiffSplitBand() */ +/************************************************************************/ + +GTiffSplitBand::GTiffSplitBand( GTiffDataset *poDS, int nBand ) + : GTiffRasterBand( poDS, nBand ) + +{ + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* ~GTiffSplitBand() */ +/************************************************************************/ + +GTiffSplitBand::~GTiffSplitBand() +{ +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GTiffSplitBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + (void) nBlockXOff; + + /* Optimization when reading the same line in a contig multi-band TIFF */ + if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG && poGDS->nBands > 1 && + poGDS->nLastLineRead == nBlockYOff ) + { + goto extract_band_data; + } + + if (!poGDS->SetDirectory()) + return CE_Failure; + + if (poGDS->nPlanarConfig == PLANARCONFIG_CONTIG && + poGDS->nBands > 1) + { + if (poGDS->pabyBlockBuf == NULL) + { + poGDS->pabyBlockBuf = (GByte *) VSIMalloc(TIFFScanlineSize(poGDS->hTIFF)); + if( poGDS->pabyBlockBuf == NULL ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate " CPL_FRMT_GUIB " bytes.", + (GUIntBig)TIFFScanlineSize(poGDS->hTIFF)); + return CE_Failure; + } + } + } + else + { + CPLAssert(TIFFScanlineSize(poGDS->hTIFF) == nBlockXSize); + } + +/* -------------------------------------------------------------------- */ +/* Read through to target scanline. */ +/* -------------------------------------------------------------------- */ + if( poGDS->nLastLineRead >= nBlockYOff ) + poGDS->nLastLineRead = -1; + + if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE && poGDS->nBands > 1 ) + { + /* If we change of band, we must start reading the */ + /* new strip from its beginning */ + if ( poGDS->nLastBandRead != nBand ) + poGDS->nLastLineRead = -1; + poGDS->nLastBandRead = nBand; + } + + while( poGDS->nLastLineRead < nBlockYOff ) + { + if( TIFFReadScanline( poGDS->hTIFF, + poGDS->pabyBlockBuf ? poGDS->pabyBlockBuf : pImage, + ++poGDS->nLastLineRead, + (poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE) ? (uint16) (nBand-1) : 0 ) == -1 + && !poGDS->bIgnoreReadErrors ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "TIFFReadScanline() failed." ); + return CE_Failure; + } + } + +extract_band_data: +/* -------------------------------------------------------------------- */ +/* Extract band data from contig buffer. */ +/* -------------------------------------------------------------------- */ + if ( poGDS->pabyBlockBuf != NULL ) + { + int iPixel, iSrcOffset= nBand - 1, iDstOffset=0; + + for( iPixel = 0; iPixel < nBlockXSize; iPixel++, iSrcOffset+=poGDS->nBands, iDstOffset++ ) + { + ((GByte *) pImage)[iDstOffset] = poGDS->pabyBlockBuf[iSrcOffset]; + } + } + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr GTiffSplitBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + (void) nBlockXOff; + (void) nBlockYOff; + (void) pImage; + + CPLError( CE_Failure, CPLE_AppDefined, + "Split bands are read-only." ); + return CE_Failure; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GTiffRGBABand */ +/* ==================================================================== */ +/************************************************************************/ + +class GTiffRGBABand : public GTiffRasterBand +{ + friend class GTiffDataset; + + public: + + GTiffRGBABand( GTiffDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + + virtual GDALColorInterp GetColorInterpretation(); +}; + + +/************************************************************************/ +/* GTiffRGBABand() */ +/************************************************************************/ + +GTiffRGBABand::GTiffRGBABand( GTiffDataset *poDS, int nBand ) + : GTiffRasterBand( poDS, nBand ) + +{ + eDataType = GDT_Byte; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr GTiffRGBABand::IWriteBlock( int, int, void * ) + +{ + CPLError( CE_Failure, CPLE_AppDefined, + "RGBA interpreted raster bands are read-only." ); + return CE_Failure; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GTiffRGBABand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + int nBlockBufSize, nBlockId; + CPLErr eErr = CE_None; + + if (!poGDS->SetDirectory()) + return CE_Failure; + + CPLAssert(nBlocksPerRow != 0); + nBlockBufSize = 4 * nBlockXSize * nBlockYSize; + nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow; + +/* -------------------------------------------------------------------- */ +/* Allocate a temporary buffer for this strip. */ +/* -------------------------------------------------------------------- */ + if( poGDS->pabyBlockBuf == NULL ) + { + poGDS->pabyBlockBuf = (GByte *) VSIMalloc3( 4, nBlockXSize, nBlockYSize ); + if( poGDS->pabyBlockBuf == NULL ) + return( CE_Failure ); + } + +/* -------------------------------------------------------------------- */ +/* Read the strip */ +/* -------------------------------------------------------------------- */ + if( poGDS->nLoadedBlock != nBlockId ) + { + if( TIFFIsTiled( poGDS->hTIFF ) ) + { + if( TIFFReadRGBATile(poGDS->hTIFF, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + (uint32 *) poGDS->pabyBlockBuf) == -1 + && !poGDS->bIgnoreReadErrors ) + { + /* Once TIFFError() is properly hooked, this can go away */ + CPLError( CE_Failure, CPLE_AppDefined, + "TIFFReadRGBATile() failed." ); + + memset( poGDS->pabyBlockBuf, 0, nBlockBufSize ); + + eErr = CE_Failure; + } + } + else + { + if( TIFFReadRGBAStrip(poGDS->hTIFF, + nBlockId * nBlockYSize, + (uint32 *) poGDS->pabyBlockBuf) == -1 + && !poGDS->bIgnoreReadErrors ) + { + /* Once TIFFError() is properly hooked, this can go away */ + CPLError( CE_Failure, CPLE_AppDefined, + "TIFFReadRGBAStrip() failed." ); + + memset( poGDS->pabyBlockBuf, 0, nBlockBufSize ); + + eErr = CE_Failure; + } + } + } + + poGDS->nLoadedBlock = nBlockId; + +/* -------------------------------------------------------------------- */ +/* Handle simple case of eight bit data, and pixel interleaving. */ +/* -------------------------------------------------------------------- */ + int iDestLine, nBO; + int nThisBlockYSize; + + if( (nBlockYOff+1) * nBlockYSize > GetYSize() + && !TIFFIsTiled( poGDS->hTIFF ) ) + nThisBlockYSize = GetYSize() - nBlockYOff * nBlockYSize; + else + nThisBlockYSize = nBlockYSize; + +#ifdef CPL_LSB + nBO = nBand - 1; +#else + nBO = 4 - nBand; +#endif + + for( iDestLine = 0; iDestLine < nThisBlockYSize; iDestLine++ ) + { + int nSrcOffset; + + nSrcOffset = (nThisBlockYSize - iDestLine - 1) * nBlockXSize * 4; + + GDALCopyWords( poGDS->pabyBlockBuf + nBO + nSrcOffset, GDT_Byte, 4, + ((GByte *) pImage)+iDestLine*nBlockXSize, GDT_Byte, 1, + nBlockXSize ); + } + + if (eErr == CE_None) + eErr = FillCacheForOtherBands(nBlockXOff, nBlockYOff); + + return eErr; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp GTiffRGBABand::GetColorInterpretation() + +{ + if( nBand == 1 ) + return GCI_RedBand; + else if( nBand == 2 ) + return GCI_GreenBand; + else if( nBand == 3 ) + return GCI_BlueBand; + else + return GCI_AlphaBand; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GTiffOddBitsBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GTiffOddBitsBand : public GTiffRasterBand +{ + friend class GTiffDataset; + public: + + GTiffOddBitsBand( GTiffDataset *, int ); + virtual ~GTiffOddBitsBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); +}; + + +/************************************************************************/ +/* GTiffOddBitsBand() */ +/************************************************************************/ + +GTiffOddBitsBand::GTiffOddBitsBand( GTiffDataset *poGDS, int nBand ) + : GTiffRasterBand( poGDS, nBand ) + +{ + eDataType = GDT_Byte; + if( poGDS->nSampleFormat == SAMPLEFORMAT_IEEEFP ) + eDataType = GDT_Float32; + else if( poGDS->nBitsPerSample > 8 && poGDS->nBitsPerSample < 16 ) + eDataType = GDT_UInt16; + else if( poGDS->nBitsPerSample > 16 ) + eDataType = GDT_UInt32; +} + +/************************************************************************/ +/* ~GTiffOddBitsBand() */ +/************************************************************************/ + +GTiffOddBitsBand::~GTiffOddBitsBand() + +{ +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr GTiffOddBitsBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void *pImage ) + +{ + int nBlockId; + CPLErr eErr = CE_None; + + if (poGDS->bWriteErrorInFlushBlockBuf) + { + /* Report as an error if a previously loaded block couldn't be */ + /* written correctly */ + poGDS->bWriteErrorInFlushBlockBuf = FALSE; + return CE_Failure; + } + + if (!poGDS->SetDirectory()) + return CE_Failure; + + CPLAssert( poGDS != NULL + && nBlockXOff >= 0 + && nBlockYOff >= 0 + && pImage != NULL ); + + if( eDataType == GDT_Float32 && poGDS->nBitsPerSample < 32 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Writing float data with nBitsPerSample < 32 is unsupported"); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Load the block buffer. */ +/* -------------------------------------------------------------------- */ + CPLAssert(nBlocksPerRow != 0); + nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow; + + if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) + nBlockId += (nBand-1) * poGDS->nBlocksPerBand; + + /* Only read content from disk in the CONTIG case */ + eErr = poGDS->LoadBlockBuf( nBlockId, + poGDS->nPlanarConfig == PLANARCONFIG_CONTIG && poGDS->nBands > 1 ); + if( eErr != CE_None ) + return eErr; + + GUInt32 nMaxVal = (1 << poGDS->nBitsPerSample) - 1; + +/* -------------------------------------------------------------------- */ +/* Handle case of "separate" images or single band images where */ +/* no interleaving with other data is required. */ +/* -------------------------------------------------------------------- */ + if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE + || poGDS->nBands == 1 ) + { + int iBit, iPixel, iBitOffset = 0; + int iX, iY, nBitsPerLine; + + // bits per line rounds up to next byte boundary. + nBitsPerLine = nBlockXSize * poGDS->nBitsPerSample; + if( (nBitsPerLine & 7) != 0 ) + nBitsPerLine = (nBitsPerLine + 7) & (~7); + + /* Initialize to zero as we set the buffer with binary or operations */ + if (poGDS->nBitsPerSample != 24) + memset(poGDS->pabyBlockBuf, 0, (nBitsPerLine / 8) * nBlockYSize); + + iPixel = 0; + for( iY = 0; iY < nBlockYSize; iY++ ) + { + iBitOffset = iY * nBitsPerLine; + + /* Small optimization in 1 bit case */ + if (poGDS->nBitsPerSample == 1) + { + for( iX = 0; iX < nBlockXSize; iX++ ) + { + if (((GByte *) pImage)[iPixel++]) + poGDS->pabyBlockBuf[iBitOffset>>3] |= (0x80 >>(iBitOffset & 7)); + iBitOffset++; + } + + continue; + } + + for( iX = 0; iX < nBlockXSize; iX++ ) + { + GUInt32 nInWord = 0; + if( eDataType == GDT_Byte ) + nInWord = ((GByte *) pImage)[iPixel++]; + else if( eDataType == GDT_UInt16 ) + nInWord = ((GUInt16 *) pImage)[iPixel++]; + else if( eDataType == GDT_UInt32 ) + nInWord = ((GUInt32 *) pImage)[iPixel++]; + else { + CPLAssert(0); + } + + if (nInWord > nMaxVal) + { + nInWord = nMaxVal; + if( !poGDS->bClipWarn ) + { + poGDS->bClipWarn = TRUE; + CPLError( CE_Warning, CPLE_AppDefined, + "One or more pixels clipped to fit %d bit domain.", poGDS->nBitsPerSample ); + } + } + + if (poGDS->nBitsPerSample == 24) + { +/* -------------------------------------------------------------------- */ +/* Special case for 24bit data which is pre-byteswapped since */ +/* the size falls on a byte boundary ... ugg (#2361). */ +/* -------------------------------------------------------------------- */ +#ifdef CPL_MSB + poGDS->pabyBlockBuf[(iBitOffset>>3) + 0] = + (GByte) nInWord; + poGDS->pabyBlockBuf[(iBitOffset>>3) + 1] = + (GByte) (nInWord >> 8); + poGDS->pabyBlockBuf[(iBitOffset>>3) + 2] = + (GByte) (nInWord >> 16); +#else + poGDS->pabyBlockBuf[(iBitOffset>>3) + 0] = + (GByte) (nInWord >> 16); + poGDS->pabyBlockBuf[(iBitOffset>>3) + 1] = + (GByte) (nInWord >> 8); + poGDS->pabyBlockBuf[(iBitOffset>>3) + 2] = + (GByte) nInWord; +#endif + iBitOffset += 24; + } + else + { + for( iBit = 0; iBit < poGDS->nBitsPerSample; iBit++ ) + { + if (nInWord & (1 << (poGDS->nBitsPerSample - 1 - iBit))) + poGDS->pabyBlockBuf[iBitOffset>>3] |= (0x80 >>(iBitOffset & 7)); + iBitOffset++; + } + } + } + } + + poGDS->bLoadedBlockDirty = TRUE; + + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Handle case of pixel interleaved (PLANARCONFIG_CONTIG) images. */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +/* On write of pixel interleaved data, we might as well flush */ +/* out any other bands that are dirty in our cache. This is */ +/* especially helpful when writing compressed blocks. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 0; iBand < poGDS->nBands; iBand++ ) + { + const GByte *pabyThisImage = NULL; + GDALRasterBlock *poBlock = NULL; + int iBit, iPixel, iBitOffset = 0; + int iPixelBitSkip, iBandBitOffset, iX, iY, nBitsPerLine; + + if( iBand+1 == nBand ) + pabyThisImage = (GByte *) pImage; + else + { + poBlock = ((GTiffOddBitsBand *)poGDS->GetRasterBand( iBand+1 )) + ->TryGetLockedBlockRef( nBlockXOff, nBlockYOff ); + + if( poBlock == NULL ) + continue; + + if( !poBlock->GetDirty() ) + { + poBlock->DropLock(); + continue; + } + + pabyThisImage = (GByte *) poBlock->GetDataRef(); + } + + iPixelBitSkip = poGDS->nBitsPerSample * poGDS->nBands; + iBandBitOffset = iBand * poGDS->nBitsPerSample; + + // bits per line rounds up to next byte boundary. + nBitsPerLine = nBlockXSize * iPixelBitSkip; + if( (nBitsPerLine & 7) != 0 ) + nBitsPerLine = (nBitsPerLine + 7) & (~7); + + iPixel = 0; + for( iY = 0; iY < nBlockYSize; iY++ ) + { + iBitOffset = iBandBitOffset + iY * nBitsPerLine; + + for( iX = 0; iX < nBlockXSize; iX++ ) + { + GUInt32 nInWord = 0; + if( eDataType == GDT_Byte ) + nInWord = ((GByte *) pabyThisImage)[iPixel++]; + else if( eDataType == GDT_UInt16 ) + nInWord = ((GUInt16 *) pabyThisImage)[iPixel++]; + else if( eDataType == GDT_UInt32 ) + nInWord = ((GUInt32 *) pabyThisImage)[iPixel++]; + else { + CPLAssert(0); + } + + if (nInWord > nMaxVal) + { + nInWord = nMaxVal; + if( !poGDS->bClipWarn ) + { + poGDS->bClipWarn = TRUE; + CPLError( CE_Warning, CPLE_AppDefined, + "One or more pixels clipped to fit %d bit domain.", poGDS->nBitsPerSample ); + } + } + + if (poGDS->nBitsPerSample == 24) + { +/* -------------------------------------------------------------------- */ +/* Special case for 24bit data which is pre-byteswapped since */ +/* the size falls on a byte boundary ... ugg (#2361). */ +/* -------------------------------------------------------------------- */ +#ifdef CPL_MSB + poGDS->pabyBlockBuf[(iBitOffset>>3) + 0] = + (GByte) nInWord; + poGDS->pabyBlockBuf[(iBitOffset>>3) + 1] = + (GByte) (nInWord >> 8); + poGDS->pabyBlockBuf[(iBitOffset>>3) + 2] = + (GByte) (nInWord >> 16); +#else + poGDS->pabyBlockBuf[(iBitOffset>>3) + 0] = + (GByte) (nInWord >> 16); + poGDS->pabyBlockBuf[(iBitOffset>>3) + 1] = + (GByte) (nInWord >> 8); + poGDS->pabyBlockBuf[(iBitOffset>>3) + 2] = + (GByte) nInWord; +#endif + iBitOffset += 24; + } + else + { + for( iBit = 0; iBit < poGDS->nBitsPerSample; iBit++ ) + { + if (nInWord & (1 << (poGDS->nBitsPerSample - 1 - iBit))) + poGDS->pabyBlockBuf[iBitOffset>>3] |= (0x80 >>(iBitOffset & 7)); + else + { + /* We must explictly unset the bit as we may update an existing block */ + poGDS->pabyBlockBuf[iBitOffset>>3] &= ~(0x80 >>(iBitOffset & 7)); + } + + iBitOffset++; + } + } + + iBitOffset= iBitOffset + iPixelBitSkip - poGDS->nBitsPerSample; + } + } + + if( poBlock != NULL ) + { + poBlock->MarkClean(); + poBlock->DropLock(); + } + } + + poGDS->bLoadedBlockDirty = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GTiffOddBitsBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + int nBlockId; + CPLErr eErr = CE_None; + + if (!poGDS->SetDirectory()) + return CE_Failure; + + CPLAssert(nBlocksPerRow != 0); + nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow; + + if( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) + nBlockId += (nBand-1) * poGDS->nBlocksPerBand; + +/* -------------------------------------------------------------------- */ +/* Handle the case of a strip in a writable file that doesn't */ +/* exist yet, but that we want to read. Just set to zeros and */ +/* return. */ +/* -------------------------------------------------------------------- */ + if( nBlockId != poGDS->nLoadedBlock && !poGDS->IsBlockAvailable(nBlockId) ) + { + NullBlock( pImage ); + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Load the block buffer. */ +/* -------------------------------------------------------------------- */ + eErr = poGDS->LoadBlockBuf( nBlockId ); + if( eErr != CE_None ) + return eErr; + + if ( poGDS->nBitsPerSample == 1 && (poGDS->nBands == 1 || poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) ) + { +/* -------------------------------------------------------------------- */ +/* Translate 1bit data to eight bit. */ +/* -------------------------------------------------------------------- */ + int iDstOffset=0, iLine; + register GByte *pabyBlockBuf = poGDS->pabyBlockBuf; + + for( iLine = 0; iLine < nBlockYSize; iLine++ ) + { + int iSrcOffset, iPixel; + + iSrcOffset = ((nBlockXSize+7) >> 3) * 8 * iLine; + + GByte bSetVal = (poGDS->bPromoteTo8Bits) ? 255 : 1; + + for( iPixel = 0; iPixel < nBlockXSize; iPixel++, iSrcOffset++ ) + { + if( pabyBlockBuf[iSrcOffset >>3] & (0x80 >> (iSrcOffset & 0x7)) ) + ((GByte *) pImage)[iDstOffset++] = bSetVal; + else + ((GByte *) pImage)[iDstOffset++] = 0; + } + } + } +/* -------------------------------------------------------------------- */ +/* Handle the case of 16- and 24-bit floating point data as per */ +/* TIFF Technical Note 3. */ +/* -------------------------------------------------------------------- */ + else if( eDataType == GDT_Float32 && poGDS->nBitsPerSample < 32 ) + { + int i, nBlockPixels, nWordBytes, iSkipBytes; + GByte *pabyImage; + + nWordBytes = poGDS->nBitsPerSample / 8; + pabyImage = poGDS->pabyBlockBuf + (nBand - 1) * nWordBytes; + iSkipBytes = ( poGDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) ? + nWordBytes : poGDS->nBands * nWordBytes; + + nBlockPixels = nBlockXSize * nBlockYSize; + if ( poGDS->nBitsPerSample == 16 ) + { + for( i = 0; i < nBlockPixels; i++ ) + { + ((GUInt32 *) pImage)[i] = + HalfToFloat( *((GUInt16 *)pabyImage) ); + pabyImage += iSkipBytes; + } + } + else if ( poGDS->nBitsPerSample == 24 ) + { + for( i = 0; i < nBlockPixels; i++ ) + { +#ifdef CPL_MSB + ((GUInt32 *) pImage)[i] = + TripleToFloat( ((GUInt32)*(pabyImage + 0) << 16) + | ((GUInt32)*(pabyImage + 1) << 8) + | (GUInt32)*(pabyImage + 2) ); +#else + ((GUInt32 *) pImage)[i] = + TripleToFloat( ((GUInt32)*(pabyImage + 2) << 16) + | ((GUInt32)*(pabyImage + 1) << 8) + | (GUInt32)*pabyImage ); +#endif + pabyImage += iSkipBytes; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Special case for moving 12bit data somewhat more efficiently. */ +/* -------------------------------------------------------------------- */ + else if( poGDS->nBitsPerSample == 12 ) + { + int iPixel, iBitOffset = 0; + int iPixelBitSkip, iBandBitOffset, iX, iY, nBitsPerLine; + + if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG ) + { + iPixelBitSkip = poGDS->nBands * poGDS->nBitsPerSample; + iBandBitOffset = (nBand-1) * poGDS->nBitsPerSample; + } + else + { + iPixelBitSkip = poGDS->nBitsPerSample; + iBandBitOffset = 0; + } + + // bits per line rounds up to next byte boundary. + nBitsPerLine = nBlockXSize * iPixelBitSkip; + if( (nBitsPerLine & 7) != 0 ) + nBitsPerLine = (nBitsPerLine + 7) & (~7); + + iPixel = 0; + for( iY = 0; iY < nBlockYSize; iY++ ) + { + iBitOffset = iBandBitOffset + iY * nBitsPerLine; + + for( iX = 0; iX < nBlockXSize; iX++ ) + { + int iByte = iBitOffset>>3; + + if( (iBitOffset & 0x7) == 0 ) + { + /* starting on byte boundary */ + + ((GUInt16 *) pImage)[iPixel++] = + (poGDS->pabyBlockBuf[iByte] << 4) + | (poGDS->pabyBlockBuf[iByte+1] >> 4); + } + else + { + /* starting off byte boundary */ + + ((GUInt16 *) pImage)[iPixel++] = + ((poGDS->pabyBlockBuf[iByte] & 0xf) << 8) + | (poGDS->pabyBlockBuf[iByte+1]); + } + iBitOffset += iPixelBitSkip; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Special case for 24bit data which is pre-byteswapped since */ +/* the size falls on a byte boundary ... ugg (#2361). */ +/* -------------------------------------------------------------------- */ + else if( poGDS->nBitsPerSample == 24 ) + { + int iPixel; + int iPixelByteSkip, iBandByteOffset, iX, iY, nBytesPerLine; + + if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG ) + { + iPixelByteSkip = (poGDS->nBands * poGDS->nBitsPerSample) / 8; + iBandByteOffset = ((nBand-1) * poGDS->nBitsPerSample) / 8; + } + else + { + iPixelByteSkip = poGDS->nBitsPerSample / 8; + iBandByteOffset = 0; + } + + nBytesPerLine = nBlockXSize * iPixelByteSkip; + + iPixel = 0; + for( iY = 0; iY < nBlockYSize; iY++ ) + { + GByte *pabyImage = + poGDS->pabyBlockBuf + iBandByteOffset + iY * nBytesPerLine; + + for( iX = 0; iX < nBlockXSize; iX++ ) + { +#ifdef CPL_MSB + ((GUInt32 *) pImage)[iPixel++] = + ((GUInt32)*(pabyImage + 2) << 16) + | ((GUInt32)*(pabyImage + 1) << 8) + | (GUInt32)*(pabyImage + 0); +#else + ((GUInt32 *) pImage)[iPixel++] = + ((GUInt32)*(pabyImage + 0) << 16) + | ((GUInt32)*(pabyImage + 1) << 8) + | (GUInt32)*(pabyImage + 2); +#endif + pabyImage += iPixelByteSkip; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Handle 1-32 bit integer data. */ +/* -------------------------------------------------------------------- */ + else + { + int iBit, iPixel, iBitOffset = 0; + int iPixelBitSkip, iBandBitOffset, iX, iY, nBitsPerLine; + + if( poGDS->nPlanarConfig == PLANARCONFIG_CONTIG ) + { + iPixelBitSkip = poGDS->nBands * poGDS->nBitsPerSample; + iBandBitOffset = (nBand-1) * poGDS->nBitsPerSample; + } + else + { + iPixelBitSkip = poGDS->nBitsPerSample; + iBandBitOffset = 0; + } + + // bits per line rounds up to next byte boundary. + nBitsPerLine = nBlockXSize * iPixelBitSkip; + if( (nBitsPerLine & 7) != 0 ) + nBitsPerLine = (nBitsPerLine + 7) & (~7); + + register GByte *pabyBlockBuf = poGDS->pabyBlockBuf; + iPixel = 0; + + for( iY = 0; iY < nBlockYSize; iY++ ) + { + iBitOffset = iBandBitOffset + iY * nBitsPerLine; + + for( iX = 0; iX < nBlockXSize; iX++ ) + { + int nOutWord = 0; + + for( iBit = 0; iBit < poGDS->nBitsPerSample; iBit++ ) + { + if( pabyBlockBuf[iBitOffset>>3] + & (0x80 >>(iBitOffset & 7)) ) + nOutWord |= (1 << (poGDS->nBitsPerSample - 1 - iBit)); + iBitOffset++; + } + + iBitOffset= iBitOffset + iPixelBitSkip - poGDS->nBitsPerSample; + + if( eDataType == GDT_Byte ) + ((GByte *) pImage)[iPixel++] = (GByte) nOutWord; + else if( eDataType == GDT_UInt16 ) + ((GUInt16 *) pImage)[iPixel++] = (GUInt16) nOutWord; + else if( eDataType == GDT_UInt32 ) + ((GUInt32 *) pImage)[iPixel++] = nOutWord; + else { + CPLAssert(0); + } + } + } + } + + return CE_None; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* GTiffBitmapBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GTiffBitmapBand : public GTiffOddBitsBand +{ + friend class GTiffDataset; + + GDALColorTable *poColorTable; + + public: + + GTiffBitmapBand( GTiffDataset *, int ); + virtual ~GTiffBitmapBand(); + + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); +}; + + +/************************************************************************/ +/* GTiffBitmapBand() */ +/************************************************************************/ + +GTiffBitmapBand::GTiffBitmapBand( GTiffDataset *poDS, int nBand ) + : GTiffOddBitsBand( poDS, nBand ) + +{ + eDataType = GDT_Byte; + + if( poDS->poColorTable != NULL ) + poColorTable = poDS->poColorTable->Clone(); + else + { +#ifdef ESRI_BUILD + poColorTable = NULL; +#else + GDALColorEntry oWhite, oBlack; + + oWhite.c1 = 255; + oWhite.c2 = 255; + oWhite.c3 = 255; + oWhite.c4 = 255; + + oBlack.c1 = 0; + oBlack.c2 = 0; + oBlack.c3 = 0; + oBlack.c4 = 255; + + poColorTable = new GDALColorTable(); + + if( poDS->nPhotometric == PHOTOMETRIC_MINISWHITE ) + { + poColorTable->SetColorEntry( 0, &oWhite ); + poColorTable->SetColorEntry( 1, &oBlack ); + } + else + { + poColorTable->SetColorEntry( 0, &oBlack ); + poColorTable->SetColorEntry( 1, &oWhite ); + } +#endif /* not defined ESRI_BUILD */ + } +} + +/************************************************************************/ +/* ~GTiffBitmapBand() */ +/************************************************************************/ + +GTiffBitmapBand::~GTiffBitmapBand() + +{ + delete poColorTable; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp GTiffBitmapBand::GetColorInterpretation() + +{ + if (poGDS->bPromoteTo8Bits) + return GCI_Undefined; + else + return GCI_PaletteIndex; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *GTiffBitmapBand::GetColorTable() + +{ + if (poGDS->bPromoteTo8Bits) + return NULL; + else + return poColorTable; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GTiffSplitBitmapBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GTiffSplitBitmapBand : public GTiffBitmapBand +{ + friend class GTiffDataset; + + public: + + GTiffSplitBitmapBand( GTiffDataset *, int ); + virtual ~GTiffSplitBitmapBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); +}; + + +/************************************************************************/ +/* GTiffSplitBitmapBand() */ +/************************************************************************/ + +GTiffSplitBitmapBand::GTiffSplitBitmapBand( GTiffDataset *poDS, int nBand ) + : GTiffBitmapBand( poDS, nBand ) + +{ + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* ~GTiffSplitBitmapBand() */ +/************************************************************************/ + +GTiffSplitBitmapBand::~GTiffSplitBitmapBand() + +{ +} + + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GTiffSplitBitmapBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + (void) nBlockXOff; + + if (!poGDS->SetDirectory()) + return CE_Failure; + + if (poGDS->pabyBlockBuf == NULL) + { + poGDS->pabyBlockBuf = (GByte *) VSIMalloc(TIFFScanlineSize(poGDS->hTIFF)); + if( poGDS->pabyBlockBuf == NULL ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate " CPL_FRMT_GUIB " bytes.", + (GUIntBig)TIFFScanlineSize(poGDS->hTIFF)); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Read through to target scanline. */ +/* -------------------------------------------------------------------- */ + if( poGDS->nLastLineRead >= nBlockYOff ) + poGDS->nLastLineRead = -1; + + while( poGDS->nLastLineRead < nBlockYOff ) + { + if( TIFFReadScanline( poGDS->hTIFF, poGDS->pabyBlockBuf, ++poGDS->nLastLineRead, 0 ) == -1 + && !poGDS->bIgnoreReadErrors ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "TIFFReadScanline() failed." ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Translate 1bit data to eight bit. */ +/* -------------------------------------------------------------------- */ + int iPixel, iSrcOffset=0, iDstOffset=0; + + for( iPixel = 0; iPixel < nBlockXSize; iPixel++, iSrcOffset++ ) + { + if( poGDS->pabyBlockBuf[iSrcOffset >>3] & (0x80 >> (iSrcOffset & 0x7)) ) + ((GByte *) pImage)[iDstOffset++] = 1; + else + ((GByte *) pImage)[iDstOffset++] = 0; + } + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr GTiffSplitBitmapBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + (void) nBlockXOff; + (void) nBlockYOff; + (void) pImage; + + CPLError( CE_Failure, CPLE_AppDefined, + "Split bitmap bands are read-only." ); + return CE_Failure; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GTiffDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* GTiffDataset() */ +/************************************************************************/ + +GTiffDataset::GTiffDataset() + +{ + nLoadedBlock = -1; + bLoadedBlockDirty = FALSE; + pabyBlockBuf = NULL; + bWriteErrorInFlushBlockBuf = FALSE; + hTIFF = NULL; + fpL = NULL; + bStreamingIn = FALSE; + bStreamingOut = FALSE; + fpToWrite = NULL; + nLastWrittenBlockId = -1; + bNeedsRewrite = FALSE; + bMetadataChanged = FALSE; + bColorProfileMetadataChanged = FALSE; + bGeoTIFFInfoChanged = FALSE; + bForceUnsetGTOrGCPs = FALSE; + bForceUnsetProjection = FALSE; + bCrystalized = TRUE; + poColorTable = NULL; + bNoDataChanged = FALSE; + bNoDataSet = FALSE; + dfNoDataValue = -9999.0; + pszProjection = CPLStrdup(""); + bLookedForProjection = FALSE; + bLookedForMDAreaOrPoint = FALSE; + bBase = TRUE; + bCloseTIFFHandle = FALSE; + bTreatAsRGBA = FALSE; + nOverviewCount = 0; + papoOverviewDS = NULL; + + nJPEGOverviewVisibilityFlag = FALSE; + nJPEGOverviewCount = -1; + nJPEGOverviewCountOri = 0; + papoJPEGOverviewDS = NULL; + + nDirOffset = 0; + poActiveDS = NULL; + ppoActiveDSRef = NULL; + + bGeoTransformValid = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + nGCPCount = 0; + pasGCPList = NULL; + + osProfile = "GDALGeoTIFF"; + + papszCreationOptions = NULL; + + nTempWriteBufferSize = 0; + pabyTempWriteBuffer = NULL; + + poMaskDS = NULL; + poBaseDS = NULL; + + bFillEmptyTiles = FALSE; + bLoadingOtherBands = FALSE; + nLastLineRead = -1; + nLastBandRead = -1; + bTreatAsSplit = FALSE; + bTreatAsSplitBitmap = FALSE; + bClipWarn = FALSE; + bHasWarnedDisableAggressiveBandCaching = FALSE; + bDontReloadFirstBlock = FALSE; + + nZLevel = -1; + nLZMAPreset = -1; + nJpegQuality = -1; + nJpegTablesMode = -1; + + bPromoteTo8Bits = FALSE; + + bDebugDontWriteBlocks = CSLTestBoolean(CPLGetConfigOption("GTIFF_DONT_WRITE_BLOCKS", "NO")); + + bIsFinalized = FALSE; + bIgnoreReadErrors = CSLTestBoolean(CPLGetConfigOption("GTIFF_IGNORE_READ_ERRORS", "NO")); + + bEXIFMetadataLoaded = FALSE; + bICCMetadataLoaded = FALSE; + + bScanDeferred = TRUE; + + bDirectIO = CSLTestBoolean(CPLGetConfigOption("GTIFF_DIRECT_IO", "NO")); + const char* pszVirtualMemIO = CPLGetConfigOption("GTIFF_VIRTUAL_MEM_IO", "NO"); + if( EQUAL(pszVirtualMemIO, "IF_ENOUGH_RAM") ) + eVirtualMemIOUsage = VIRTUAL_MEM_IO_IF_ENOUGH_RAM; + else if( CSLTestBoolean(pszVirtualMemIO) ) + eVirtualMemIOUsage = VIRTUAL_MEM_IO_YES; + else + eVirtualMemIOUsage = VIRTUAL_MEM_IO_NO; + psVirtualMemIOMapping = NULL; + + nSetPhotometricFromBandColorInterp = 0; + + pBaseMapping = NULL; + nRefBaseMapping = 0; + + bHasDiscardedLsb = FALSE; + + bIMDRPCMetadataLoaded = FALSE; + papszMetadataFiles = NULL; +} + +/************************************************************************/ +/* ~GTiffDataset() */ +/************************************************************************/ + +GTiffDataset::~GTiffDataset() + +{ + Finalize(); +} + +/************************************************************************/ +/* Finalize() */ +/************************************************************************/ + +int GTiffDataset::Finalize() +{ + if (bIsFinalized) + return FALSE; + + int bHasDroppedRef = FALSE; + + Crystalize(); + + if ( bColorProfileMetadataChanged ) + { + SaveICCProfile(this, NULL, NULL, 0); + bColorProfileMetadataChanged = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Handle forcing xml:ESRI data to be written to PAM. */ +/* -------------------------------------------------------------------- */ + if( CSLTestBoolean(CPLGetConfigOption( "ESRI_XML_PAM", "NO" )) ) + { + char **papszESRIMD = GetMetadata("xml:ESRI"); + if( papszESRIMD ) + { + GDALPamDataset::SetMetadata( papszESRIMD, "xml:ESRI"); + } + } + + if( psVirtualMemIOMapping ) + CPLVirtualMemFree( psVirtualMemIOMapping ); + psVirtualMemIOMapping = NULL; + +/* -------------------------------------------------------------------- */ +/* Ensure any blocks write cached by GDAL gets pushed through libtiff.*/ +/* -------------------------------------------------------------------- */ + GDALPamDataset::FlushCache(); + +/* -------------------------------------------------------------------- */ +/* Fill in missing blocks with empty data. */ +/* -------------------------------------------------------------------- */ + if( bFillEmptyTiles ) + { + FillEmptyTiles(); + bFillEmptyTiles = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Force a complete flush, including either rewriting(moving) */ +/* of writing in place the current directory. */ +/* -------------------------------------------------------------------- */ + FlushCache(); + +/* -------------------------------------------------------------------- */ +/* If there is still changed metadata, then presumably we want */ +/* to push it into PAM. */ +/* -------------------------------------------------------------------- */ + if( bMetadataChanged ) + { + PushMetadataToPam(); + bMetadataChanged = FALSE; + GDALPamDataset::FlushCache(); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup overviews. */ +/* -------------------------------------------------------------------- */ + if( bBase ) + { + for( int i = 0; i < nOverviewCount; i++ ) + { + delete papoOverviewDS[i]; + bHasDroppedRef = TRUE; + } + nOverviewCount = 0; + + for( int i = 0; i < nJPEGOverviewCountOri; i++ ) + { + delete papoJPEGOverviewDS[i]; + bHasDroppedRef = TRUE; + } + nJPEGOverviewCount = 0; + nJPEGOverviewCountOri = 0; + CPLFree( papoJPEGOverviewDS ); + papoJPEGOverviewDS = NULL; + } + + /* If we are a mask dataset, we can have overviews, but we don't */ + /* own them. We can only free the array, not the overviews themselves */ + CPLFree( papoOverviewDS ); + papoOverviewDS = NULL; + + /* poMaskDS is owned by the main image and the overviews */ + /* so because of the latter case, we can delete it even if */ + /* we are not the base image */ + if (poMaskDS) + { + delete poMaskDS; + poMaskDS = NULL; + bHasDroppedRef = TRUE; + } + + if( poColorTable != NULL ) + delete poColorTable; + poColorTable = NULL; + + if( bBase || bCloseTIFFHandle ) + { + XTIFFClose( hTIFF ); + hTIFF = NULL; + if( fpL != NULL ) + { + VSIFCloseL( fpL ); + fpL = NULL; + } + } + + if( fpToWrite != NULL ) + { + VSIFCloseL( fpToWrite ); + fpToWrite = NULL; + } + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + pasGCPList = NULL; + nGCPCount = 0; + } + + CPLFree( pszProjection ); + pszProjection = NULL; + + CSLDestroy( papszCreationOptions ); + papszCreationOptions = NULL; + + CPLFree(pabyTempWriteBuffer); + pabyTempWriteBuffer = NULL; + + if( ppoActiveDSRef != NULL && *ppoActiveDSRef == this ) + *ppoActiveDSRef = NULL; + ppoActiveDSRef = NULL; + + bIMDRPCMetadataLoaded = FALSE; + CSLDestroy(papszMetadataFiles); + papszMetadataFiles = NULL; + + bIsFinalized = TRUE; + + return bHasDroppedRef; +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int GTiffDataset::CloseDependentDatasets() +{ + if (!bBase) + return FALSE; + + int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets(); + + bHasDroppedRef |= Finalize(); + + return bHasDroppedRef; +} + +/************************************************************************/ +/* GetJPEGOverviewCount() */ +/************************************************************************/ + +int GTiffDataset::GetJPEGOverviewCount() +{ + if( nJPEGOverviewCount >= 0 ) + return nJPEGOverviewCount; + + nJPEGOverviewCount = 0; + if( eAccess != GA_ReadOnly || nCompression != COMPRESSION_JPEG || + (nRasterXSize < 256 && nRasterYSize < 256) || + !CSLTestBoolean(CPLGetConfigOption("GTIFF_IMPLICIT_JPEG_OVR", "YES")) || + GDALGetDriverByName("JPEG") == NULL ) + { + return 0; + } + + /* libjpeg-6b only suppports 2, 4 and 8 scale denominators */ + /* TODO: Later versions support more */ + int i; + for(i = 2; i >= 0; i--) + { + if( nRasterXSize >= (256 << i) || nRasterYSize >= (256 << i) ) + { + nJPEGOverviewCount = i + 1; + break; + } + } + if( nJPEGOverviewCount == 0 ) + return 0; + + if( !SetDirectory() ) + return 0; + + /* Get JPEG tables */ + uint32 nJPEGTableSize = 0; + void* pJPEGTable = NULL; + GByte abyFFD8[] = { 0xFF, 0xD8 }; + if( TIFFGetField(hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize, &pJPEGTable) ) + { + if( pJPEGTable == NULL || (int)nJPEGTableSize <= 0 || + ((GByte*)pJPEGTable)[nJPEGTableSize-1] != 0xD9 ) + { + return 0; + } + nJPEGTableSize --; /* remove final 0xD9 */ + } + else + { + pJPEGTable = abyFFD8; + nJPEGTableSize = 2; + } + + papoJPEGOverviewDS = (GTiffJPEGOverviewDS**) CPLMalloc( + sizeof(GTiffJPEGOverviewDS*) * nJPEGOverviewCount ); + for(i = 0; i < nJPEGOverviewCount; i++) + { + papoJPEGOverviewDS[i] = new GTiffJPEGOverviewDS(this, i+1, + pJPEGTable, (int)nJPEGTableSize); + } + + nJPEGOverviewCountOri = nJPEGOverviewCount; + + return nJPEGOverviewCount; +} + +/************************************************************************/ +/* FillEmptyTiles() */ +/************************************************************************/ + +void GTiffDataset::FillEmptyTiles() + +{ + toff_t *panByteCounts = NULL; + int nBlockCount, iBlock; + + if (!SetDirectory()) + return; + +/* -------------------------------------------------------------------- */ +/* How many blocks are there in this file? */ +/* -------------------------------------------------------------------- */ + if( nPlanarConfig == PLANARCONFIG_SEPARATE ) + nBlockCount = nBlocksPerBand * nBands; + else + nBlockCount = nBlocksPerBand; + +/* -------------------------------------------------------------------- */ +/* Fetch block maps. */ +/* -------------------------------------------------------------------- */ + if( TIFFIsTiled( hTIFF ) ) + TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts ); + else + TIFFGetField( hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts ); + + if (panByteCounts == NULL) + { + /* Got here with libtiff 3.9.3 and tiff_write_8 test */ + CPLError(CE_Failure, CPLE_AppDefined, "FillEmptyTiles() failed because panByteCounts == NULL"); + return; + } + +/* -------------------------------------------------------------------- */ +/* Prepare a blank data buffer to write for uninitialized blocks. */ +/* -------------------------------------------------------------------- */ + int nBlockBytes; + + if( TIFFIsTiled( hTIFF ) ) + nBlockBytes = TIFFTileSize(hTIFF); + else + nBlockBytes = TIFFStripSize(hTIFF); + + GByte *pabyData = (GByte *) VSICalloc(nBlockBytes,1); + if (pabyData == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate %d bytes", nBlockBytes); + return; + } + +/* -------------------------------------------------------------------- */ +/* Check all blocks, writing out data for uninitialized blocks. */ +/* -------------------------------------------------------------------- */ + for( iBlock = 0; iBlock < nBlockCount; iBlock++ ) + { + if( panByteCounts[iBlock] == 0 ) + { + if( WriteEncodedTileOrStrip( iBlock, pabyData, FALSE ) != CE_None ) + break; + } + } + + CPLFree( pabyData ); +} + +/************************************************************************/ +/* WriteEncodedTile() */ +/************************************************************************/ + +int GTiffDataset::WriteEncodedTile(uint32 tile, GByte *pabyData, + int bPreserveDataBuffer) +{ + int cc = TIFFTileSize( hTIFF ); + int bNeedTileFill = FALSE; + int iRow=0, iColumn=0; + int nBlocksPerRow=1, nBlocksPerColumn=1; + + /* + ** Do we need to spread edge values right or down for a partial + ** JPEG encoded tile? We do this to avoid edge artifacts. + */ + if( nCompression == COMPRESSION_JPEG ) + { + nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize); + nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize); + + iColumn = (tile % nBlocksPerBand) % nBlocksPerRow; + iRow = (tile % nBlocksPerBand) / nBlocksPerRow; + + // Is this a partial right edge tile? + if( iRow == nBlocksPerRow - 1 + && nRasterXSize % nBlockXSize != 0 ) + bNeedTileFill = TRUE; + + // Is this a partial bottom edge tile? + if( iColumn == nBlocksPerColumn - 1 + && nRasterYSize % nBlockYSize != 0 ) + bNeedTileFill = TRUE; + } + + /* + ** If we need to fill out the tile, or if we want to prevent + ** TIFFWriteEncodedTile from altering the buffer as part of + ** byte swapping the data on write then we will need a temporary + ** working buffer. If not, we can just do a direct write. + */ + if (bPreserveDataBuffer + && (TIFFIsByteSwapped(hTIFF) || bNeedTileFill || bHasDiscardedLsb) ) + { + if (cc != nTempWriteBufferSize) + { + pabyTempWriteBuffer = CPLRealloc(pabyTempWriteBuffer, cc); + nTempWriteBufferSize = cc; + } + memcpy(pabyTempWriteBuffer, pabyData, cc); + + pabyData = (GByte *) pabyTempWriteBuffer; + } + + /* + ** Perform tile fill if needed. + */ + // TODO: we should also handle the case of nBitsPerSample == 12 + // but this is more involved... + if( bNeedTileFill && nBitsPerSample == 8 ) + { + int nRightPixelsToFill = 0; + int nBottomPixelsToFill = 0; + unsigned int iX, iY, iSrcX, iSrcY; + int nComponents = 1; + if( nPlanarConfig == PLANARCONFIG_CONTIG ) + nComponents = nBands; + + CPLDebug( "GTiff", "Filling out jpeg edge tile on write." ); + + if( iColumn == nBlocksPerRow - 1 ) + nRightPixelsToFill = nBlockXSize * (iColumn+1) - nRasterXSize; + if( iRow == nBlocksPerColumn - 1 ) + nBottomPixelsToFill = nBlockYSize * (iRow+1) - nRasterYSize; + + // Fill out to the right. + iSrcX = nBlockXSize - nRightPixelsToFill - 1; + + for( iX = iSrcX+1; iX < nBlockXSize; iX++ ) + { + for( iY = 0; iY < nBlockYSize; iY++ ) + { + memcpy( pabyData + (nBlockXSize * iY + iX) * nComponents, + pabyData + (nBlockXSize * iY + iSrcX) * nComponents, + nComponents ); + } + } + + // now fill out the bottom. + iSrcY = nBlockYSize - nBottomPixelsToFill - 1; + for( iY = iSrcY+1; iY < nBlockYSize; iY++ ) + { + memcpy( pabyData + nBlockXSize * nComponents * iY, + pabyData + nBlockXSize * nComponents * iSrcY, + nBlockXSize * nComponents ); + } + } + + if( bHasDiscardedLsb != 0 ) + { + int iBand = (nPlanarConfig == PLANARCONFIG_SEPARATE ) ? tile / nBlocksPerBand : -1; + DiscardLsb(pabyData, cc, iBand); + } + + if( bStreamingOut ) + { + if( tile != (uint32)(nLastWrittenBlockId + 1) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Attempt to write block %d whereas %d was expected", + tile, nLastWrittenBlockId + 1); + return -1; + } + if( (int)VSIFWriteL(pabyData, 1, cc, fpToWrite) != cc ) + { + CPLError(CE_Failure, CPLE_FileIO, "Could not write %d bytes", + cc); + return -1; + } + nLastWrittenBlockId = tile; + return 0; + } + + return TIFFWriteEncodedTile(hTIFF, tile, pabyData, cc); +} + +/************************************************************************/ +/* WriteEncodedStrip() */ +/************************************************************************/ + +int GTiffDataset::WriteEncodedStrip(uint32 strip, GByte* pabyData, + int bPreserveDataBuffer) +{ + int cc = TIFFStripSize( hTIFF ); + +/* -------------------------------------------------------------------- */ +/* If this is the last strip in the image, and is partial, then */ +/* we need to trim the number of scanlines written to the */ +/* amount of valid data we have. (#2748) */ +/* -------------------------------------------------------------------- */ + int nStripWithinBand = strip % nBlocksPerBand; + + if( (int) ((nStripWithinBand+1) * nRowsPerStrip) > GetRasterYSize() ) + { + cc = (cc / nRowsPerStrip) + * (GetRasterYSize() - nStripWithinBand * nRowsPerStrip); + CPLDebug( "GTiff", "Adjusted bytes to write from %d to %d.", + (int) TIFFStripSize(hTIFF), cc ); + } + +/* -------------------------------------------------------------------- */ +/* TIFFWriteEncodedStrip can alter the passed buffer if */ +/* byte-swapping is necessary so we use a temporary buffer */ +/* before calling it. */ +/* -------------------------------------------------------------------- */ + if (bPreserveDataBuffer && (TIFFIsByteSwapped(hTIFF) || bHasDiscardedLsb)) + { + if (cc != nTempWriteBufferSize) + { + pabyTempWriteBuffer = CPLRealloc(pabyTempWriteBuffer, cc); + nTempWriteBufferSize = cc; + } + memcpy(pabyTempWriteBuffer, pabyData, cc); + pabyData = (GByte *) pabyTempWriteBuffer; + } + + if( bHasDiscardedLsb != 0 ) + { + int iBand = (nPlanarConfig == PLANARCONFIG_SEPARATE ) ? strip / nBlocksPerBand : -1; + DiscardLsb(pabyData, cc, iBand); + } + + if( bStreamingOut ) + { + if( strip != (uint32)(nLastWrittenBlockId + 1) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Attempt to write block %d whereas %d was expected", + strip, nLastWrittenBlockId + 1); + return -1; + } + if( (int)VSIFWriteL(pabyData, 1, cc, fpToWrite) != cc ) + { + CPLError(CE_Failure, CPLE_FileIO, "Could not write %d bytes", + cc); + return -1; + } + nLastWrittenBlockId = strip; + return 0; + } + + return TIFFWriteEncodedStrip(hTIFF, strip, pabyData, cc); +} + +/************************************************************************/ +/* DiscardLsb() */ +/************************************************************************/ + +void GTiffDataset::DiscardLsb(GByte* pabyBuffer, int nBytes, int iBand) +{ + if( nBitsPerSample == 8 ) + { + if( nPlanarConfig == PLANARCONFIG_SEPARATE ) + { + int nMask = anMaskLsb[iBand]; + int nOffset = anOffsetLsb[iBand]; + for( int i = 0; i < nBytes; i ++ ) + { + if( pabyBuffer[i] != 255 ) /* we want to keep 255 in case it is alpha */ + pabyBuffer[i] = (pabyBuffer[i] & nMask) | nOffset; + } + } + else + { + for( int i = 0; i < nBytes; i += nBands ) + { + for( int j = 0; j < nBands; j ++ ) + { + if( pabyBuffer[i + j] != 255 ) /* we want to keep 255 in case it is alpha */ + pabyBuffer[i + j] = (pabyBuffer[i + j] & + anMaskLsb[j]) | anOffsetLsb[j]; + } + } + } + } + else if( nBitsPerSample == 16 ) + { + if( nPlanarConfig == PLANARCONFIG_SEPARATE ) + { + int nMask = anMaskLsb[iBand]; + int nOffset = anOffsetLsb[iBand]; + for( int i = 0; i < nBytes/2; i ++ ) + { + ((GUInt16*)pabyBuffer)[i] = (((GUInt16*)pabyBuffer)[i] & nMask) | nOffset; + } + } + else + { + for( int i = 0; i < nBytes/2; i += nBands ) + { + for( int j = 0; j < nBands; j ++ ) + { + ((GUInt16*)pabyBuffer)[i + j] = (((GUInt16*)pabyBuffer)[i + j] & + anMaskLsb[j]) | anOffsetLsb[j]; + } + } + } + } + else if( nBitsPerSample == 32 ) + { + if( nPlanarConfig == PLANARCONFIG_SEPARATE ) + { + int nMask = anMaskLsb[iBand]; + int nOffset = anOffsetLsb[iBand]; + for( int i = 0; i < nBytes/4; i ++ ) + { + ((GUInt32*)pabyBuffer)[i] = (((GUInt32*)pabyBuffer)[i] & nMask) | nOffset; + } + } + else + { + for( int i = 0; i < nBytes/4; i += nBands ) + { + for( int j = 0; j < nBands; j ++ ) + { + ((GUInt32*)pabyBuffer)[i + j] = (((GUInt32*)pabyBuffer)[i + j] & + anMaskLsb[j]) | anOffsetLsb[j]; + } + } + } + } +} + +/************************************************************************/ +/* WriteEncodedTileOrStrip() */ +/************************************************************************/ + +CPLErr GTiffDataset::WriteEncodedTileOrStrip(uint32 tile_or_strip, void* data, + int bPreserveDataBuffer) +{ + CPLErr eErr = CE_None; + + if( TIFFIsTiled( hTIFF ) ) + { + if( WriteEncodedTile(tile_or_strip, (GByte*) data, + bPreserveDataBuffer) == -1 ) + { + eErr = CE_Failure; + } + } + else + { + if( WriteEncodedStrip(tile_or_strip, (GByte *) data, + bPreserveDataBuffer) == -1 ) + { + eErr = CE_Failure; + } + } + + return eErr; +} + +/************************************************************************/ +/* FlushBlockBuf() */ +/************************************************************************/ + +CPLErr GTiffDataset::FlushBlockBuf() + +{ + CPLErr eErr = CE_None; + + if( nLoadedBlock < 0 || !bLoadedBlockDirty ) + return CE_None; + + bLoadedBlockDirty = FALSE; + + if (!SetDirectory()) + return CE_Failure; + + eErr = WriteEncodedTileOrStrip(nLoadedBlock, pabyBlockBuf, TRUE); + if (eErr != CE_None) + { + CPLError( CE_Failure, CPLE_AppDefined, + "WriteEncodedTile/Strip() failed." ); + bWriteErrorInFlushBlockBuf = TRUE; + } + + return eErr; +} + +/************************************************************************/ +/* LoadBlockBuf() */ +/* */ +/* Load working block buffer with request block (tile/strip). */ +/************************************************************************/ + +CPLErr GTiffDataset::LoadBlockBuf( int nBlockId, int bReadFromDisk ) + +{ + int nBlockBufSize; + CPLErr eErr = CE_None; + + if( nLoadedBlock == nBlockId ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* If we have a dirty loaded block, flush it out first. */ +/* -------------------------------------------------------------------- */ + if( nLoadedBlock != -1 && bLoadedBlockDirty ) + { + eErr = FlushBlockBuf(); + if( eErr != CE_None ) + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Get block size. */ +/* -------------------------------------------------------------------- */ + if( TIFFIsTiled(hTIFF) ) + nBlockBufSize = TIFFTileSize( hTIFF ); + else + nBlockBufSize = TIFFStripSize( hTIFF ); + + if ( !nBlockBufSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Bogus block size; unable to allocate a buffer."); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Allocate a temporary buffer for this strip. */ +/* -------------------------------------------------------------------- */ + if( pabyBlockBuf == NULL ) + { + pabyBlockBuf = (GByte *) VSICalloc( 1, nBlockBufSize ); + if( pabyBlockBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate %d bytes for a temporary strip " + "buffer in GTIFF driver.", + nBlockBufSize ); + + return( CE_Failure ); + } + } + +/* -------------------------------------------------------------------- */ +/* When called from ::IWriteBlock in separate cases (or in single band */ +/* geotiffs), the ::IWriteBlock will override the content of the buffer*/ +/* with pImage, so we don't need to read data from disk */ +/* -------------------------------------------------------------------- */ + if( !bReadFromDisk || bStreamingOut ) + { + nLoadedBlock = nBlockId; + return CE_None; + } + + /* libtiff 3.X doesn't like mixing read&write of JPEG compressed blocks */ + /* The below hack is necessary due to another hack that consist in */ + /* writing zero block to force creation of JPEG tables */ + if( nBlockId == 0 && bDontReloadFirstBlock ) + { + bDontReloadFirstBlock = FALSE; + memset( pabyBlockBuf, 0, nBlockBufSize ); + nLoadedBlock = nBlockId; + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* The bottom most partial tiles and strips are sometimes only */ +/* partially encoded. This code reduces the requested data so */ +/* an error won't be reported in this case. (#1179) */ +/* -------------------------------------------------------------------- */ + int nBlockReqSize = nBlockBufSize; + int nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize); + int nBlockYOff = (nBlockId % nBlocksPerBand) / nBlocksPerRow; + + if( (int)((nBlockYOff+1) * nBlockYSize) > nRasterYSize ) + { + nBlockReqSize = (nBlockBufSize / nBlockYSize) + * (nBlockYSize - (((nBlockYOff+1) * nBlockYSize) % nRasterYSize)); + memset( pabyBlockBuf, 0, nBlockBufSize ); + } + +/* -------------------------------------------------------------------- */ +/* If we don't have this block already loaded, and we know it */ +/* doesn't yet exist on disk, just zero the memory buffer and */ +/* pretend we loaded it. */ +/* -------------------------------------------------------------------- */ + if( !IsBlockAvailable( nBlockId ) ) + { + memset( pabyBlockBuf, 0, nBlockBufSize ); + nLoadedBlock = nBlockId; + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Load the block, if it isn't our current block. */ +/* -------------------------------------------------------------------- */ + if( TIFFIsTiled( hTIFF ) ) + { + if( TIFFReadEncodedTile(hTIFF, nBlockId, pabyBlockBuf, + nBlockReqSize) == -1 + && !bIgnoreReadErrors ) + { + /* Once TIFFError() is properly hooked, this can go away */ + CPLError( CE_Failure, CPLE_AppDefined, + "TIFFReadEncodedTile() failed." ); + + memset( pabyBlockBuf, 0, nBlockBufSize ); + + eErr = CE_Failure; + } + } + else + { + if( TIFFReadEncodedStrip(hTIFF, nBlockId, pabyBlockBuf, + nBlockReqSize) == -1 + && !bIgnoreReadErrors ) + { + /* Once TIFFError() is properly hooked, this can go away */ + CPLError( CE_Failure, CPLE_AppDefined, + "TIFFReadEncodedStrip() failed." ); + + memset( pabyBlockBuf, 0, nBlockBufSize ); + + eErr = CE_Failure; + } + } + + nLoadedBlock = nBlockId; + bLoadedBlockDirty = FALSE; + + return eErr; +} + +/************************************************************************/ +/* GTiffFillStreamableOffsetAndCount() */ +/************************************************************************/ + +static void GTiffFillStreamableOffsetAndCount(TIFF* hTIFF, int nSize) +{ + uint32 nXSize, nYSize; + TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize ); + TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &nYSize ); + toff_t* panOffset = NULL, *panSize = NULL; + int nBlockCount = ( TIFFIsTiled(hTIFF) ) ? TIFFNumberOfTiles(hTIFF) : TIFFNumberOfStrips(hTIFF); + TIFFGetField( hTIFF, TIFFIsTiled(hTIFF) ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS, &panOffset ); + TIFFGetField( hTIFF, TIFFIsTiled(hTIFF) ? TIFFTAG_TILEBYTECOUNTS : TIFFTAG_STRIPBYTECOUNTS, &panSize ); + toff_t nOffset = nSize; + int nBlocksPerBand = 0; + uint32 nRowsPerStrip = 0; + if( !TIFFIsTiled(hTIFF) ) + { + TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP, &nRowsPerStrip); + if( nRowsPerStrip > (uint32)nYSize ) + nRowsPerStrip = nYSize; + nBlocksPerBand = DIV_ROUND_UP(nYSize, nRowsPerStrip); + } + for(int i=0;i (int) nYSize ) + { + cc = (cc / nRowsPerStrip) + * (nYSize - nStripWithinBand * nRowsPerStrip); + } + } + panOffset[i] = nOffset; + panSize[i] = cc; + nOffset += cc; + } +} + +/************************************************************************/ +/* Crystalize() */ +/* */ +/* Make sure that the directory information is written out for */ +/* a new file, require before writing any imagery data. */ +/************************************************************************/ + +void GTiffDataset::Crystalize() + +{ + if( !bCrystalized ) + { + // FIXME? libtiff writes extended tags in the order they are specified + // and not in increasing order + WriteMetadata( this, hTIFF, TRUE, osProfile, osFilename, + papszCreationOptions ); + WriteGeoTIFFInfo(); + if( bNoDataSet ) + WriteNoDataValue( hTIFF, dfNoDataValue ); + + bMetadataChanged = FALSE; + bGeoTIFFInfoChanged = FALSE; + bNoDataChanged = FALSE; + bNeedsRewrite = FALSE; + + bCrystalized = TRUE; + + TIFFWriteCheck( hTIFF, TIFFIsTiled(hTIFF), "GTiffDataset::Crystalize"); + + // Keep zip and tiff quality, and jpegcolormode which get reset when we call + // TIFFWriteDirectory + int jquality = -1, zquality = -1, nColorMode = -1, nJpegTablesMode = -1; + TIFFGetField(hTIFF, TIFFTAG_JPEGQUALITY, &jquality); + TIFFGetField(hTIFF, TIFFTAG_ZIPQUALITY, &zquality); + TIFFGetField( hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode ); + TIFFGetField( hTIFF, TIFFTAG_JPEGTABLESMODE, &nJpegTablesMode ); + + TIFFWriteDirectory( hTIFF ); + if( bStreamingOut ) + { + /* We need to write twice the directory to be sure that custom */ + /* TIFF tags are correctly sorted and that padding bytes have been */ + /* added */ + TIFFSetDirectory( hTIFF, 0 ); + TIFFWriteDirectory( hTIFF ); + + VSIFSeekL( fpL, 0, SEEK_END ); + int nSize = (int) VSIFTellL(fpL); + + TIFFSetDirectory( hTIFF, 0 ); + GTiffFillStreamableOffsetAndCount( hTIFF, nSize ); + TIFFWriteDirectory( hTIFF ); + + vsi_l_offset nDataLength; + void* pabyBuffer = VSIGetMemFileBuffer( osTmpFilename, &nDataLength, FALSE); + if( (int)VSIFWriteL( pabyBuffer, 1, (int)nDataLength, fpToWrite ) != (int)nDataLength ) + { + CPLError(CE_Failure, CPLE_FileIO, "Could not write %d bytes", + (int)nDataLength); + } + /* In case of single strip file, there's a libtiff check that would */ + /* issue a warning since the file hasn't the required size */ + CPLPushErrorHandler(CPLQuietErrorHandler); + TIFFSetDirectory( hTIFF, 0 ); + CPLPopErrorHandler(); + } + else + TIFFSetDirectory( hTIFF, 0 ); + + + // Now, reset zip and tiff quality and jpegcolormode. + if(jquality > 0) + TIFFSetField(hTIFF, TIFFTAG_JPEGQUALITY, jquality); + if(zquality > 0) + TIFFSetField(hTIFF, TIFFTAG_ZIPQUALITY, zquality); + if (nColorMode >= 0) + TIFFSetField(hTIFF, TIFFTAG_JPEGCOLORMODE, nColorMode); + if (nJpegTablesMode >= 0 ) + TIFFSetField(hTIFF, TIFFTAG_JPEGTABLESMODE, nJpegTablesMode); + + nDirOffset = TIFFCurrentDirOffset( hTIFF ); + } +} + +#ifdef INTERNAL_LIBTIFF + +#define IO_CACHE_PAGE_SIZE 4096 + +static +void GTiffCacheOffsetOrCount(VSILFILE* fp, + vsi_l_offset nBaseOffset, + int nBlockId, + uint32 nstrips, + uint64* panVals, + size_t sizeofval) +{ + int i, iStartBefore; + vsi_l_offset nOffset, nOffsetStartPage, nOffsetEndPage; + GByte buffer[2 * IO_CACHE_PAGE_SIZE]; + + nOffset = nBaseOffset + sizeofval * nBlockId; + nOffsetStartPage = (nOffset / IO_CACHE_PAGE_SIZE) * IO_CACHE_PAGE_SIZE; + nOffsetEndPage = nOffsetStartPage + IO_CACHE_PAGE_SIZE; + + if( nOffset + sizeofval > nOffsetEndPage ) + nOffsetEndPage += IO_CACHE_PAGE_SIZE; + vsi_l_offset nLastStripOffset = nBaseOffset + nstrips * sizeofval; + if( nLastStripOffset < nOffsetEndPage ) + nOffsetEndPage = nLastStripOffset; + if( nOffsetStartPage >= nOffsetEndPage ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read offset/size for strile %d", nBlockId); + panVals[nBlockId] = 0; + return; + } + VSIFSeekL(fp, nOffsetStartPage, SEEK_SET); + size_t nToRead = (size_t)(nOffsetEndPage - nOffsetStartPage); + size_t nRead = VSIFReadL(buffer, 1, nToRead, fp); + if( nRead < nToRead ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read offset/size for strile around ~%d", nBlockId); + memset(buffer + nRead, 0, nToRead - nRead); + } + iStartBefore = - (int)((nOffset - nOffsetStartPage) / sizeofval); + if( nBlockId + iStartBefore < 0 ) + iStartBefore = -nBlockId; + for(i=iStartBefore; (uint32)(nBlockId + i) < nstrips && + (GIntBig)nOffset + (i+1) * (int)sizeofval <= (GIntBig)nOffsetEndPage; i++) + { + if( sizeofval == 4 ) + { + uint32 val; + memcpy(&val, + buffer + (nOffset - nOffsetStartPage) + i * sizeof(val), + sizeof(val)); + panVals[nBlockId + i] = val; + } + else + { + uint64 val; + memcpy(&val, + buffer + (nOffset - nOffsetStartPage) + i * sizeof(val), + sizeof(val)); + panVals[nBlockId + i] = val; + } + } +} + +#endif /* INTERNAL_LIBTIFF */ + +/************************************************************************/ +/* IsBlockAvailable() */ +/* */ +/* Return TRUE if the indicated strip/tile is available. We */ +/* establish this by testing if the stripbytecount is zero. If */ +/* zero then the block has never been committed to disk. */ +/************************************************************************/ + +int GTiffDataset::IsBlockAvailable( int nBlockId ) + +{ +#ifdef INTERNAL_LIBTIFF + + /* Optimization to avoid fetching the whole Strip/TileCounts and Strip/TileOffsets arrays */ + if( eAccess == GA_ReadOnly && + !(hTIFF->tif_flags & TIFF_SWAB) && + hTIFF->tif_dir.td_nstrips > 2 && + (hTIFF->tif_dir.td_stripoffset_entry.tdir_type == TIFF_LONG || + hTIFF->tif_dir.td_stripoffset_entry.tdir_type == TIFF_LONG8) && + (hTIFF->tif_dir.td_stripbytecount_entry.tdir_type == TIFF_LONG || + hTIFF->tif_dir.td_stripbytecount_entry.tdir_type == TIFF_LONG8) && + !bStreamingIn ) + { + if( hTIFF->tif_dir.td_stripoffset == NULL ) + { + hTIFF->tif_dir.td_stripoffset = + (uint64*) _TIFFmalloc( sizeof(uint64) * hTIFF->tif_dir.td_nstrips ); + hTIFF->tif_dir.td_stripbytecount = + (uint64*) _TIFFmalloc( sizeof(uint64) * hTIFF->tif_dir.td_nstrips ); + if( hTIFF->tif_dir.td_stripoffset && hTIFF->tif_dir.td_stripbytecount ) + { + memset(hTIFF->tif_dir.td_stripoffset, 0xFF, + sizeof(uint64) * hTIFF->tif_dir.td_nstrips ); + memset(hTIFF->tif_dir.td_stripbytecount, 0xFF, + sizeof(uint64) * hTIFF->tif_dir.td_nstrips ); + } + else + { + _TIFFfree(hTIFF->tif_dir.td_stripoffset); + hTIFF->tif_dir.td_stripoffset = NULL; + _TIFFfree(hTIFF->tif_dir.td_stripbytecount); + hTIFF->tif_dir.td_stripbytecount = NULL; + } + } + if( hTIFF->tif_dir.td_stripbytecount == NULL ) + return FALSE; + if( ~(hTIFF->tif_dir.td_stripoffset[nBlockId]) == 0 || + ~(hTIFF->tif_dir.td_stripbytecount[nBlockId]) == 0 ) + { + VSILFILE* fp = VSI_TIFFGetVSILFile(TIFFClientdata( hTIFF )); + vsi_l_offset nCurOffset = VSIFTellL(fp); + if( ~(hTIFF->tif_dir.td_stripoffset[nBlockId]) == 0 ) + { + if( hTIFF->tif_dir.td_stripoffset_entry.tdir_type == TIFF_LONG ) + { + GTiffCacheOffsetOrCount(fp, + hTIFF->tif_dir.td_stripoffset_entry.tdir_offset.toff_long, + nBlockId, + hTIFF->tif_dir.td_nstrips, + hTIFF->tif_dir.td_stripoffset, + sizeof(uint32)); + } + else + { + GTiffCacheOffsetOrCount(fp, + hTIFF->tif_dir.td_stripoffset_entry.tdir_offset.toff_long8, + nBlockId, + hTIFF->tif_dir.td_nstrips, + hTIFF->tif_dir.td_stripoffset, + sizeof(uint64)); + } + } + + if( ~(hTIFF->tif_dir.td_stripbytecount[nBlockId]) == 0 ) + { + if( hTIFF->tif_dir.td_stripbytecount_entry.tdir_type == TIFF_LONG ) + { + GTiffCacheOffsetOrCount(fp, + hTIFF->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long, + nBlockId, + hTIFF->tif_dir.td_nstrips, + hTIFF->tif_dir.td_stripbytecount, + sizeof(uint32)); + } + else + { + GTiffCacheOffsetOrCount(fp, + hTIFF->tif_dir.td_stripbytecount_entry.tdir_offset.toff_long8, + nBlockId, + hTIFF->tif_dir.td_nstrips, + hTIFF->tif_dir.td_stripbytecount, + sizeof(uint64)); + } + } + VSIFSeekL(fp, nCurOffset, SEEK_SET); + } + return hTIFF->tif_dir.td_stripbytecount[nBlockId] != 0; + } +#endif /* INTERNAL_LIBTIFF */ + toff_t *panByteCounts = NULL; + + if( ( TIFFIsTiled( hTIFF ) + && TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts ) ) + || ( !TIFFIsTiled( hTIFF ) + && TIFFGetField( hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts ) ) ) + { + if( panByteCounts == NULL ) + return FALSE; + else + return panByteCounts[nBlockId] != 0; + } + else + return FALSE; +} + +/************************************************************************/ +/* FlushCache() */ +/* */ +/* We override this so we can also flush out local tiff strip */ +/* cache if need be. */ +/************************************************************************/ + +void GTiffDataset::FlushCache() + +{ + if (bIsFinalized || ppoActiveDSRef == NULL) + return; + + GDALPamDataset::FlushCache(); + + if( bLoadedBlockDirty && nLoadedBlock != -1 ) + FlushBlockBuf(); + + CPLFree( pabyBlockBuf ); + pabyBlockBuf = NULL; + nLoadedBlock = -1; + bLoadedBlockDirty = FALSE; + + if (!SetDirectory()) + return; + FlushDirectory(); +} + +/************************************************************************/ +/* FlushDirectory() */ +/************************************************************************/ + +void GTiffDataset::FlushDirectory() + +{ + if( GetAccess() == GA_Update ) + { + if( bMetadataChanged ) + { + if (!SetDirectory()) + return; + bNeedsRewrite = + WriteMetadata( this, hTIFF, TRUE, osProfile, osFilename, + papszCreationOptions ); + bMetadataChanged = FALSE; + } + + if( bGeoTIFFInfoChanged ) + { + if (!SetDirectory()) + return; + WriteGeoTIFFInfo(); + } + + if( bNoDataChanged ) + { + if (!SetDirectory()) + return; + if( bNoDataSet ) + { + WriteNoDataValue( hTIFF, dfNoDataValue ); + bNeedsRewrite = TRUE; + bNoDataChanged = FALSE; + } + } + + if( bNeedsRewrite ) + { +#if defined(TIFFLIB_VERSION) +#if defined(HAVE_TIFFGETSIZEPROC) + if (!SetDirectory()) + return; + + TIFFSizeProc pfnSizeProc = TIFFGetSizeProc( hTIFF ); + + nDirOffset = pfnSizeProc( TIFFClientdata( hTIFF ) ); + if( (nDirOffset % 2) == 1 ) + nDirOffset++; + + TIFFRewriteDirectory( hTIFF ); + + TIFFSetSubDirectory( hTIFF, nDirOffset ); +#elif TIFFLIB_VERSION > 20010925 && TIFFLIB_VERSION != 20011807 + if (!SetDirectory()) + return; + + TIFFRewriteDirectory( hTIFF ); +#endif +#endif + bNeedsRewrite = FALSE; + } + } + + // there are some circumstances in which we can reach this point + // without having made this our directory (SetDirectory()) in which + // case we should not risk a flush. + if( GetAccess() == GA_Update && TIFFCurrentDirOffset(hTIFF) == nDirOffset ) + { +#if defined(BIGTIFF_SUPPORT) + TIFFSizeProc pfnSizeProc = TIFFGetSizeProc( hTIFF ); + + toff_t nNewDirOffset = pfnSizeProc( TIFFClientdata( hTIFF ) ); + if( (nNewDirOffset % 2) == 1 ) + nNewDirOffset++; + + TIFFFlush( hTIFF ); + + if( nDirOffset != TIFFCurrentDirOffset( hTIFF ) ) + { + nDirOffset = nNewDirOffset; + CPLDebug( "GTiff", + "directory moved during flush in FlushDirectory()" ); + } +#else + /* For libtiff 3.X, the above causes regressions and crashes in */ + /* tiff_write.py and tiff_ovr.py */ + TIFFFlush( hTIFF ); +#endif + } +} + +/************************************************************************/ +/* CleanOverviews() */ +/************************************************************************/ + +CPLErr GTiffDataset::CleanOverviews() + +{ + CPLAssert( bBase ); + + ScanDirectories(); + + FlushDirectory(); + *ppoActiveDSRef = NULL; + +/* -------------------------------------------------------------------- */ +/* Cleanup overviews objects, and get offsets to all overview */ +/* directories. */ +/* -------------------------------------------------------------------- */ + std::vector anOvDirOffsets; + int i; + + for( i = 0; i < nOverviewCount; i++ ) + { + anOvDirOffsets.push_back( papoOverviewDS[i]->nDirOffset ); + delete papoOverviewDS[i]; + } + +/* -------------------------------------------------------------------- */ +/* Loop through all the directories, translating the offsets */ +/* into indexes we can use with TIFFUnlinkDirectory(). */ +/* -------------------------------------------------------------------- */ + std::vector anOvDirIndexes; + int iThisOffset = 1; + + TIFFSetDirectory( hTIFF, 0 ); + + for( ; TRUE; ) + { + for( i = 0; i < nOverviewCount; i++ ) + { + if( anOvDirOffsets[i] == TIFFCurrentDirOffset( hTIFF ) ) + { + CPLDebug( "GTiff", "%d -> %d", + (int) anOvDirOffsets[i], iThisOffset ); + anOvDirIndexes.push_back( (uint16) iThisOffset ); + } + } + + if( TIFFLastDirectory( hTIFF ) ) + break; + + TIFFReadDirectory( hTIFF ); + iThisOffset++; + } + +/* -------------------------------------------------------------------- */ +/* Actually unlink the target directories. Note that we do */ +/* this from last to first so as to avoid renumbering any of */ +/* the earlier directories we need to remove. */ +/* -------------------------------------------------------------------- */ + while( !anOvDirIndexes.empty() ) + { + TIFFUnlinkDirectory( hTIFF, anOvDirIndexes.back() ); + anOvDirIndexes.pop_back(); + } + + CPLFree( papoOverviewDS ); + + nOverviewCount = 0; + papoOverviewDS = NULL; + + if (!SetDirectory()) + return CE_Failure; + + return CE_None; +} + + +/************************************************************************/ +/* RegisterNewOverviewDataset() */ +/************************************************************************/ + +CPLErr GTiffDataset::RegisterNewOverviewDataset(toff_t nOverviewOffset) +{ + GTiffDataset* poODS = new GTiffDataset(); + poODS->nJpegQuality = nJpegQuality; + poODS->nZLevel = nZLevel; + poODS->nLZMAPreset = nLZMAPreset; + + if( nCompression == COMPRESSION_JPEG ) + { + if ( CPLGetConfigOption( "JPEG_QUALITY_OVERVIEW", NULL ) != NULL ) + { + poODS->nJpegQuality = atoi(CPLGetConfigOption("JPEG_QUALITY_OVERVIEW","75")); + } + TIFFSetField( hTIFF, TIFFTAG_JPEGQUALITY, + poODS->nJpegQuality ); + } + + if( poODS->OpenOffset( hTIFF, ppoActiveDSRef, nOverviewOffset, FALSE, + GA_Update ) != CE_None ) + { + delete poODS; + return CE_Failure; + } + else + { + nOverviewCount++; + papoOverviewDS = (GTiffDataset **) + CPLRealloc(papoOverviewDS, + nOverviewCount * (sizeof(void*))); + papoOverviewDS[nOverviewCount-1] = poODS; + poODS->poBaseDS = this; + return CE_None; + } +} + +/************************************************************************/ +/* CreateOverviewsFromSrcOverviews() */ +/************************************************************************/ + +CPLErr GTiffDataset::CreateOverviewsFromSrcOverviews(GDALDataset* poSrcDS) +{ + CPLAssert(poSrcDS->GetRasterCount() != 0); + CPLAssert(nOverviewCount == 0); + + ScanDirectories(); + +/* -------------------------------------------------------------------- */ +/* Move to the directory for this dataset. */ +/* -------------------------------------------------------------------- */ + if (!SetDirectory()) + return CE_Failure; + FlushDirectory(); + + int nOvBitsPerSample = nBitsPerSample; + +/* -------------------------------------------------------------------- */ +/* Do we have a palette? If so, create a TIFF compatible version. */ +/* -------------------------------------------------------------------- */ + std::vector anTRed, anTGreen, anTBlue; + unsigned short *panRed=NULL, *panGreen=NULL, *panBlue=NULL; + + if( nPhotometric == PHOTOMETRIC_PALETTE && poColorTable != NULL ) + { + int nColors; + + if( nOvBitsPerSample == 8 ) + nColors = 256; + else if( nOvBitsPerSample < 8 ) + nColors = 1 << nOvBitsPerSample; + else + nColors = 65536; + + anTRed.resize(nColors,0); + anTGreen.resize(nColors,0); + anTBlue.resize(nColors,0); + + for( int iColor = 0; iColor < nColors; iColor++ ) + { + if( iColor < poColorTable->GetColorEntryCount() ) + { + GDALColorEntry sRGB; + + poColorTable->GetColorEntryAsRGB( iColor, &sRGB ); + + anTRed[iColor] = (unsigned short) (256 * sRGB.c1); + anTGreen[iColor] = (unsigned short) (256 * sRGB.c2); + anTBlue[iColor] = (unsigned short) (256 * sRGB.c3); + } + else + { + anTRed[iColor] = anTGreen[iColor] = anTBlue[iColor] = 0; + } + } + + panRed = &(anTRed[0]); + panGreen = &(anTGreen[0]); + panBlue = &(anTBlue[0]); + } + +/* -------------------------------------------------------------------- */ +/* Do we need some metadata for the overviews? */ +/* -------------------------------------------------------------------- */ + CPLString osMetadata; + + GTIFFBuildOverviewMetadata( "NONE", this, osMetadata ); + +/* -------------------------------------------------------------------- */ +/* Fetch extra sample tag */ +/* -------------------------------------------------------------------- */ + uint16 *panExtraSampleValues = NULL; + uint16 nExtraSamples = 0; + + if( TIFFGetField( hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples, &panExtraSampleValues) ) + { + uint16* panExtraSampleValuesNew = (uint16*) CPLMalloc(nExtraSamples * sizeof(uint16)); + memcpy(panExtraSampleValuesNew, panExtraSampleValues, nExtraSamples * sizeof(uint16)); + panExtraSampleValues = panExtraSampleValuesNew; + } + else + { + panExtraSampleValues = NULL; + nExtraSamples = 0; + } + +/* -------------------------------------------------------------------- */ +/* Fetch predictor tag */ +/* -------------------------------------------------------------------- */ + uint16 nPredictor = PREDICTOR_NONE; + if ( nCompression == COMPRESSION_LZW || + nCompression == COMPRESSION_ADOBE_DEFLATE ) + TIFFGetField( hTIFF, TIFFTAG_PREDICTOR, &nPredictor ); + int nOvrBlockXSize, nOvrBlockYSize; + GTIFFGetOverviewBlockSize(&nOvrBlockXSize, &nOvrBlockYSize); + + int nSrcOverviews = poSrcDS->GetRasterBand(1)->GetOverviewCount(); + int i; + CPLErr eErr = CE_None; + + for(i=0;iGetRasterBand(1)->GetOverview(i); + + int nOXSize = poOvrBand->GetXSize(), nOYSize = poOvrBand->GetYSize(); + + toff_t nOverviewOffset = + GTIFFWriteDirectory(hTIFF, FILETYPE_REDUCEDIMAGE, + nOXSize, nOYSize, + nOvBitsPerSample, nPlanarConfig, + nSamplesPerPixel, nOvrBlockXSize, nOvrBlockYSize, TRUE, + nCompression, nPhotometric, nSampleFormat, + nPredictor, + panRed, panGreen, panBlue, + nExtraSamples, panExtraSampleValues, + osMetadata ); + + if( nOverviewOffset == 0 ) + eErr = CE_Failure; + else + eErr = RegisterNewOverviewDataset(nOverviewOffset); + } + + CPLFree(panExtraSampleValues); + panExtraSampleValues = NULL; + +/* -------------------------------------------------------------------- */ +/* Create overviews for the mask. */ +/* -------------------------------------------------------------------- */ + if (eErr == CE_None) + eErr = CreateInternalMaskOverviews(nOvrBlockXSize, nOvrBlockYSize); + + return eErr; +} + + +/************************************************************************/ +/* CreateInternalMaskOverviews() */ +/************************************************************************/ + +CPLErr GTiffDataset::CreateInternalMaskOverviews(int nOvrBlockXSize, + int nOvrBlockYSize) +{ + GTiffDataset *poODS; + + ScanDirectories(); + +/* -------------------------------------------------------------------- */ +/* Create overviews for the mask. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + + const char* pszInternalMask = CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", NULL); + if (poMaskDS != NULL && + poMaskDS->GetRasterCount() == 1 && + (pszInternalMask == NULL || CSLTestBoolean(pszInternalMask))) + { + int nMaskOvrCompression; + if( strstr(GDALGetMetadataItem(GDALGetDriverByName( "GTiff" ), + GDAL_DMD_CREATIONOPTIONLIST, NULL ), + "DEFLATE") != NULL ) + nMaskOvrCompression = COMPRESSION_ADOBE_DEFLATE; + else + nMaskOvrCompression = COMPRESSION_PACKBITS; + + int i; + for( i = 0; i < nOverviewCount; i++ ) + { + if (papoOverviewDS[i]->poMaskDS == NULL) + { + toff_t nOverviewOffset; + + nOverviewOffset = + GTIFFWriteDirectory(hTIFF, FILETYPE_REDUCEDIMAGE | FILETYPE_MASK, + papoOverviewDS[i]->nRasterXSize, papoOverviewDS[i]->nRasterYSize, + 1, PLANARCONFIG_CONTIG, + 1, nOvrBlockXSize, nOvrBlockYSize, TRUE, + nMaskOvrCompression, PHOTOMETRIC_MASK, SAMPLEFORMAT_UINT, PREDICTOR_NONE, + NULL, NULL, NULL, 0, NULL, + "" ); + + if( nOverviewOffset == 0 ) + { + eErr = CE_Failure; + continue; + } + + poODS = new GTiffDataset(); + if( poODS->OpenOffset( hTIFF, ppoActiveDSRef, + nOverviewOffset, FALSE, + GA_Update ) != CE_None ) + { + delete poODS; + eErr = CE_Failure; + } + else + { + poODS->bPromoteTo8Bits = CSLTestBoolean(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES")); + poODS->poBaseDS = this; + papoOverviewDS[i]->poMaskDS = poODS; + poMaskDS->nOverviewCount++; + poMaskDS->papoOverviewDS = (GTiffDataset **) + CPLRealloc(poMaskDS->papoOverviewDS, + poMaskDS->nOverviewCount * (sizeof(void*))); + poMaskDS->papoOverviewDS[poMaskDS->nOverviewCount-1] = poODS; + } + } + } + } + + return eErr; +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr GTiffDataset::IBuildOverviews( + const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + CPLErr eErr = CE_None; + int i; + GTiffDataset *poODS; + int bUseGenericHandling = FALSE; + + ScanDirectories(); + + /* Make implicit JPEG overviews invisible, but do not destroy */ + /* them in case they are already used (not sure that the client */ + /* has the right to do that. behaviour undefined in GDAL API I think) */ + nJPEGOverviewCount = 0; + +/* -------------------------------------------------------------------- */ +/* If RRD or external OVR overviews requested, then invoke */ +/* generic handling. */ +/* -------------------------------------------------------------------- */ + if( CSLTestBoolean(CPLGetConfigOption( "USE_RRD", "NO" )) + || CSLTestBoolean(CPLGetConfigOption( "TIFF_USE_OVR", "NO" )) ) + { + bUseGenericHandling = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* If we don't have read access, then create the overviews */ +/* externally. */ +/* -------------------------------------------------------------------- */ + if( GetAccess() != GA_Update ) + { + CPLDebug( "GTiff", + "File open for read-only accessing, " + "creating overviews externally." ); + + bUseGenericHandling = TRUE; + } + + if( bUseGenericHandling ) + { + if (nOverviewCount != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot add external overviews when there are already internal overviews"); + return CE_Failure; + } + + return GDALDataset::IBuildOverviews( + pszResampling, nOverviews, panOverviewList, + nBands, panBandList, pfnProgress, pProgressData ); + } + +/* -------------------------------------------------------------------- */ +/* Our TIFF overview support currently only works safely if all */ +/* bands are handled at the same time. */ +/* -------------------------------------------------------------------- */ + if( nBands != GetRasterCount() ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Generation of overviews in TIFF currently only" + " supported when operating on all bands.\n" + "Operation failed.\n" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* If zero overviews were requested, we need to clear all */ +/* existing overviews. */ +/* -------------------------------------------------------------------- */ + if( nOverviews == 0 ) + { + if( nOverviewCount == 0 ) + return GDALDataset::IBuildOverviews( + pszResampling, nOverviews, panOverviewList, + nBands, panBandList, pfnProgress, pProgressData ); + else + return CleanOverviews(); + } + +/* -------------------------------------------------------------------- */ +/* libtiff 3.X has issues when generating interleaved overviews. */ +/* so generate them one after another one. */ +/* -------------------------------------------------------------------- */ +#ifndef BIGTIFF_SUPPORT + if( nOverviews > 1 ) + { + double* padfOvrRasterFactor = (double*) CPLMalloc(sizeof(double) * nOverviews); + double dfTotal = 0; + for( i = 0; i < nOverviews; i++ ) + { + if( panOverviewList[i] <= 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid overview factor : %d", panOverviewList[i]); + eErr = CE_Failure; + break; + } + padfOvrRasterFactor[i] = 1.0 / (panOverviewList[i] * panOverviewList[i]); + dfTotal += padfOvrRasterFactor[i]; + } + + double dfAcc = 0.0; + for( i = 0; i < nOverviews && eErr == CE_None; i++ ) + { + void *pScaledProgressData; + pScaledProgressData = + GDALCreateScaledProgress( dfAcc / dfTotal, + (dfAcc + padfOvrRasterFactor[i]) / dfTotal, + pfnProgress, pProgressData ); + dfAcc += padfOvrRasterFactor[i]; + + eErr = IBuildOverviews( + pszResampling, 1, &panOverviewList[i], + nBands, panBandList, GDALScaledProgress, pScaledProgressData ); + + GDALDestroyScaledProgress(pScaledProgressData); + } + + CPLFree(padfOvrRasterFactor); + + return eErr; + } +#endif + +/* -------------------------------------------------------------------- */ +/* Initialize progress counter. */ +/* -------------------------------------------------------------------- */ + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Move to the directory for this dataset. */ +/* -------------------------------------------------------------------- */ + if (!SetDirectory()) + return CE_Failure; + FlushDirectory(); + +/* -------------------------------------------------------------------- */ +/* If we are averaging bit data to grayscale we need to create */ +/* 8bit overviews. */ +/* -------------------------------------------------------------------- */ + int nOvBitsPerSample = nBitsPerSample; + + if( EQUALN(pszResampling,"AVERAGE_BIT2",12) ) + nOvBitsPerSample = 8; + +/* -------------------------------------------------------------------- */ +/* Do we have a palette? If so, create a TIFF compatible version. */ +/* -------------------------------------------------------------------- */ + std::vector anTRed, anTGreen, anTBlue; + unsigned short *panRed=NULL, *panGreen=NULL, *panBlue=NULL; + + if( nPhotometric == PHOTOMETRIC_PALETTE && poColorTable != NULL ) + { + int nColors; + + if( nOvBitsPerSample == 8 ) + nColors = 256; + else if( nOvBitsPerSample < 8 ) + nColors = 1 << nOvBitsPerSample; + else + nColors = 65536; + + anTRed.resize(nColors,0); + anTGreen.resize(nColors,0); + anTBlue.resize(nColors,0); + + for( int iColor = 0; iColor < nColors; iColor++ ) + { + if( iColor < poColorTable->GetColorEntryCount() ) + { + GDALColorEntry sRGB; + + poColorTable->GetColorEntryAsRGB( iColor, &sRGB ); + + anTRed[iColor] = (unsigned short) (256 * sRGB.c1); + anTGreen[iColor] = (unsigned short) (256 * sRGB.c2); + anTBlue[iColor] = (unsigned short) (256 * sRGB.c3); + } + else + { + anTRed[iColor] = anTGreen[iColor] = anTBlue[iColor] = 0; + } + } + + panRed = &(anTRed[0]); + panGreen = &(anTGreen[0]); + panBlue = &(anTBlue[0]); + } + +/* -------------------------------------------------------------------- */ +/* Do we need some metadata for the overviews? */ +/* -------------------------------------------------------------------- */ + CPLString osMetadata; + + GTIFFBuildOverviewMetadata( pszResampling, this, osMetadata ); + +/* -------------------------------------------------------------------- */ +/* Fetch extra sample tag */ +/* -------------------------------------------------------------------- */ + uint16 *panExtraSampleValues = NULL; + uint16 nExtraSamples = 0; + + if( TIFFGetField( hTIFF, TIFFTAG_EXTRASAMPLES, &nExtraSamples, &panExtraSampleValues) ) + { + uint16* panExtraSampleValuesNew = (uint16*) CPLMalloc(nExtraSamples * sizeof(uint16)); + memcpy(panExtraSampleValuesNew, panExtraSampleValues, nExtraSamples * sizeof(uint16)); + panExtraSampleValues = panExtraSampleValuesNew; + } + else + { + panExtraSampleValues = NULL; + nExtraSamples = 0; + } + +/* -------------------------------------------------------------------- */ +/* Fetch predictor tag */ +/* -------------------------------------------------------------------- */ + uint16 nPredictor = PREDICTOR_NONE; + if ( nCompression == COMPRESSION_LZW || + nCompression == COMPRESSION_ADOBE_DEFLATE ) + TIFFGetField( hTIFF, TIFFTAG_PREDICTOR, &nPredictor ); + +/* -------------------------------------------------------------------- */ +/* Establish which of the overview levels we already have, and */ +/* which are new. We assume that band 1 of the file is */ +/* representative. */ +/* -------------------------------------------------------------------- */ + int nOvrBlockXSize, nOvrBlockYSize; + GTIFFGetOverviewBlockSize(&nOvrBlockXSize, &nOvrBlockYSize); + for( i = 0; i < nOverviews && eErr == CE_None; i++ ) + { + int j; + + for( j = 0; j < nOverviewCount && eErr == CE_None; j++ ) + { + int nOvFactor; + + poODS = papoOverviewDS[j]; + + nOvFactor = GDALComputeOvFactor(poODS->GetRasterXSize(), + GetRasterXSize(), + poODS->GetRasterYSize(), + GetRasterYSize()); + + if( nOvFactor == panOverviewList[i] + || nOvFactor == GDALOvLevelAdjust2( panOverviewList[i], + GetRasterXSize(), + GetRasterYSize() ) ) + panOverviewList[i] *= -1; + } + + if( panOverviewList[i] > 0 ) + { + toff_t nOverviewOffset; + int nOXSize, nOYSize; + + nOXSize = (GetRasterXSize() + panOverviewList[i] - 1) + / panOverviewList[i]; + nOYSize = (GetRasterYSize() + panOverviewList[i] - 1) + / panOverviewList[i]; + + nOverviewOffset = + GTIFFWriteDirectory(hTIFF, FILETYPE_REDUCEDIMAGE, + nOXSize, nOYSize, + nOvBitsPerSample, nPlanarConfig, + nSamplesPerPixel, nOvrBlockXSize, nOvrBlockYSize, TRUE, + nCompression, nPhotometric, nSampleFormat, + nPredictor, + panRed, panGreen, panBlue, + nExtraSamples, panExtraSampleValues, + osMetadata ); + + + if( nOverviewOffset == 0 ) + eErr = CE_Failure; + else + eErr = RegisterNewOverviewDataset(nOverviewOffset); + } + else + panOverviewList[i] *= -1; + } + + CPLFree(panExtraSampleValues); + panExtraSampleValues = NULL; + +/* -------------------------------------------------------------------- */ +/* Create overviews for the mask. */ +/* -------------------------------------------------------------------- */ + if (eErr == CE_None) + eErr = CreateInternalMaskOverviews(nOvrBlockXSize, nOvrBlockYSize); + else + return eErr; + +/* -------------------------------------------------------------------- */ +/* Refresh overviews for the mask */ +/* -------------------------------------------------------------------- */ + if (poMaskDS != NULL && + poMaskDS->GetRasterCount() == 1) + { + GDALRasterBand **papoOverviewBands; + int nMaskOverviews = 0; + + papoOverviewBands = (GDALRasterBand **) CPLCalloc(sizeof(void*),nOverviewCount); + for( i = 0; i < nOverviewCount; i++ ) + { + if (papoOverviewDS[i]->poMaskDS != NULL) + { + papoOverviewBands[nMaskOverviews ++] = + papoOverviewDS[i]->poMaskDS->GetRasterBand(1); + } + } + eErr = GDALRegenerateOverviews( (GDALRasterBandH) + poMaskDS->GetRasterBand(1), + nMaskOverviews, + (GDALRasterBandH *) papoOverviewBands, + pszResampling, GDALDummyProgress, NULL); + CPLFree(papoOverviewBands); + } + + +/* -------------------------------------------------------------------- */ +/* Refresh old overviews that were listed. */ +/* -------------------------------------------------------------------- */ + if (nPlanarConfig == PLANARCONFIG_CONTIG && + GDALDataTypeIsComplex(GetRasterBand( panBandList[0] )->GetRasterDataType()) == FALSE && + GetRasterBand( panBandList[0] )->GetColorTable() == NULL && + (EQUALN(pszResampling, "NEAR", 4) || EQUAL(pszResampling, "AVERAGE") || + EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "CUBIC") || + EQUAL(pszResampling, "CUBICSPLINE") || EQUAL(pszResampling, "LANCZOS") || + EQUAL(pszResampling, "BILINEAR"))) + { + /* In the case of pixel interleaved compressed overviews, we want to generate */ + /* the overviews for all the bands block by block, and not band after band, */ + /* in order to write the block once and not loose space in the TIFF file */ + /* We also use that logic for uncompressed overviews, since GDALRegenerateOverviewsMultiBand() */ + /* will be able to trigger cascading overview regeneration even in the presence */ + /* of an alpha band. */ + + GDALRasterBand ***papapoOverviewBands; + GDALRasterBand **papoBandList; + + int nNewOverviews = 0; + int iBand; + + papapoOverviewBands = (GDALRasterBand ***) CPLCalloc(sizeof(void*),nBands); + papoBandList = (GDALRasterBand **) CPLCalloc(sizeof(void*),nBands); + for( iBand = 0; iBand < nBands; iBand++ ) + { + GDALRasterBand* poBand = GetRasterBand( panBandList[iBand] ); + + papoBandList[iBand] = poBand; + papapoOverviewBands[iBand] = (GDALRasterBand **) CPLCalloc(sizeof(void*), poBand->GetOverviewCount()); + + int iCurOverview = 0; + for( i = 0; i < nOverviews; i++ ) + { + int j; + + for( j = 0; j < poBand->GetOverviewCount(); j++ ) + { + int nOvFactor; + GDALRasterBand * poOverview = poBand->GetOverview( j ); + + nOvFactor = GDALComputeOvFactor(poOverview->GetXSize(), + poBand->GetXSize(), + poOverview->GetYSize(), + poBand->GetYSize()); + + int bHasNoData; + double noDataValue = poBand->GetNoDataValue(&bHasNoData); + + if (bHasNoData) + poOverview->SetNoDataValue(noDataValue); + + if( nOvFactor == panOverviewList[i] + || nOvFactor == GDALOvLevelAdjust2( panOverviewList[i], + poBand->GetXSize(), + poBand->GetYSize() ) ) + { + papapoOverviewBands[iBand][iCurOverview] = poOverview; + iCurOverview++ ; + break; + } + } + } + + if (nNewOverviews == 0) + nNewOverviews = iCurOverview; + else if (nNewOverviews != iCurOverview) + { + CPLAssert(0); + return CE_Failure; + } + } + + GDALRegenerateOverviewsMultiBand(nBands, papoBandList, + nNewOverviews, papapoOverviewBands, + pszResampling, pfnProgress, pProgressData ); + + for( iBand = 0; iBand < nBands; iBand++ ) + { + CPLFree(papapoOverviewBands[iBand]); + } + CPLFree(papapoOverviewBands); + CPLFree(papoBandList); + } + else + { + GDALRasterBand **papoOverviewBands; + + papoOverviewBands = (GDALRasterBand **) + CPLCalloc(sizeof(void*),nOverviews); + + for( int iBand = 0; iBand < nBands && eErr == CE_None; iBand++ ) + { + GDALRasterBand *poBand; + int nNewOverviews; + + poBand = GetRasterBand( panBandList[iBand] ); + + nNewOverviews = 0; + for( i = 0; i < nOverviews && poBand != NULL; i++ ) + { + int j; + + for( j = 0; j < poBand->GetOverviewCount(); j++ ) + { + int nOvFactor; + GDALRasterBand * poOverview = poBand->GetOverview( j ); + + int bHasNoData; + double noDataValue = poBand->GetNoDataValue(&bHasNoData); + + if (bHasNoData) + poOverview->SetNoDataValue(noDataValue); + + nOvFactor = GDALComputeOvFactor(poOverview->GetXSize(), + poBand->GetXSize(), + poOverview->GetYSize(), + poBand->GetYSize()); + + if( nOvFactor == panOverviewList[i] + || nOvFactor == GDALOvLevelAdjust2( panOverviewList[i], + poBand->GetXSize(), + poBand->GetYSize() ) ) + { + papoOverviewBands[nNewOverviews++] = poOverview; + break; + } + } + } + + void *pScaledProgressData; + + pScaledProgressData = + GDALCreateScaledProgress( iBand / (double) nBands, + (iBand+1) / (double) nBands, + pfnProgress, pProgressData ); + + eErr = GDALRegenerateOverviews( (GDALRasterBandH) poBand, + nNewOverviews, + (GDALRasterBandH *) papoOverviewBands, + pszResampling, + GDALScaledProgress, + pScaledProgressData); + + GDALDestroyScaledProgress( pScaledProgressData ); + } + + /* -------------------------------------------------------------------- */ + /* Cleanup */ + /* -------------------------------------------------------------------- */ + CPLFree( papoOverviewBands ); + } + + + pfnProgress( 1.0, NULL, pProgressData ); + + return eErr; +} + +/************************************************************************/ +/* GTiffWriteDummyGeokeyDirectory() */ +/************************************************************************/ + +static void GTiffWriteDummyGeokeyDirectory(TIFF* hTIFF) +{ + // If we have existing geokeys, try to wipe them + // by writing a dummy geokey directory. (#2546) + uint16 *panVI = NULL; + uint16 nKeyCount; + + if( TIFFGetField( hTIFF, TIFFTAG_GEOKEYDIRECTORY, + &nKeyCount, &panVI ) ) + { + GUInt16 anGKVersionInfo[4] = { 1, 1, 0, 0 }; + double adfDummyDoubleParams[1] = { 0.0 }; + TIFFSetField( hTIFF, TIFFTAG_GEOKEYDIRECTORY, + 4, anGKVersionInfo ); + TIFFSetField( hTIFF, TIFFTAG_GEODOUBLEPARAMS, + 1, adfDummyDoubleParams ); + TIFFSetField( hTIFF, TIFFTAG_GEOASCIIPARAMS, "" ); + } +} + +/************************************************************************/ +/* WriteGeoTIFFInfo() */ +/************************************************************************/ + +void GTiffDataset::WriteGeoTIFFInfo() + +{ + bool bPixelIsPoint = false; + int bPointGeoIgnore = FALSE; + + if( GetMetadataItem( GDALMD_AREA_OR_POINT ) + && EQUAL(GetMetadataItem(GDALMD_AREA_OR_POINT), + GDALMD_AOP_POINT) ) + { + bPixelIsPoint = true; + bPointGeoIgnore = + CSLTestBoolean( CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", + "FALSE") ); + } + + if( bForceUnsetGTOrGCPs ) + { + bNeedsRewrite = TRUE; + bForceUnsetGTOrGCPs = FALSE; + +#ifdef HAVE_UNSETFIELD + TIFFUnsetField( hTIFF, TIFFTAG_GEOPIXELSCALE ); + TIFFUnsetField( hTIFF, TIFFTAG_GEOTIEPOINTS ); + TIFFUnsetField( hTIFF, TIFFTAG_GEOTRANSMATRIX ); +#endif + } + + if( bForceUnsetProjection ) + { + bNeedsRewrite = TRUE; + bForceUnsetProjection = FALSE; + +#ifdef HAVE_UNSETFIELD + TIFFUnsetField( hTIFF, TIFFTAG_GEOKEYDIRECTORY ); + TIFFUnsetField( hTIFF, TIFFTAG_GEODOUBLEPARAMS ); + TIFFUnsetField( hTIFF, TIFFTAG_GEOASCIIPARAMS ); +#else + GTiffWriteDummyGeokeyDirectory(hTIFF); +#endif + } + +/* -------------------------------------------------------------------- */ +/* If the geotransform is the default, don't bother writing it. */ +/* -------------------------------------------------------------------- */ + if( adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 + || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 + || adfGeoTransform[4] != 0.0 || ABS(adfGeoTransform[5]) != 1.0 ) + { + bNeedsRewrite = TRUE; + +/* -------------------------------------------------------------------- */ +/* Clear old tags to ensure we don't end up with conflicting */ +/* information. (#2625) */ +/* -------------------------------------------------------------------- */ +#ifdef HAVE_UNSETFIELD + TIFFUnsetField( hTIFF, TIFFTAG_GEOPIXELSCALE ); + TIFFUnsetField( hTIFF, TIFFTAG_GEOTIEPOINTS ); + TIFFUnsetField( hTIFF, TIFFTAG_GEOTRANSMATRIX ); +#endif + +/* -------------------------------------------------------------------- */ +/* Write the transform. If we have a normal north-up image we */ +/* use the tiepoint plus pixelscale otherwise we use a matrix. */ +/* -------------------------------------------------------------------- */ + if( adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0 + && adfGeoTransform[5] < 0.0 ) + { + double adfPixelScale[3], adfTiePoints[6]; + + adfPixelScale[0] = adfGeoTransform[1]; + adfPixelScale[1] = fabs(adfGeoTransform[5]); + adfPixelScale[2] = 0.0; + + if( !EQUAL(osProfile,"BASELINE") ) + TIFFSetField( hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale ); + + adfTiePoints[0] = 0.0; + adfTiePoints[1] = 0.0; + adfTiePoints[2] = 0.0; + adfTiePoints[3] = adfGeoTransform[0]; + adfTiePoints[4] = adfGeoTransform[3]; + adfTiePoints[5] = 0.0; + + if( bPixelIsPoint && !bPointGeoIgnore ) + { + adfTiePoints[3] += adfGeoTransform[1] * 0.5 + adfGeoTransform[2] * 0.5; + adfTiePoints[4] += adfGeoTransform[4] * 0.5 + adfGeoTransform[5] * 0.5; + } + + if( !EQUAL(osProfile,"BASELINE") ) + TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints ); + } + else + { + double adfMatrix[16]; + + memset(adfMatrix,0,sizeof(double) * 16); + + adfMatrix[0] = adfGeoTransform[1]; + adfMatrix[1] = adfGeoTransform[2]; + adfMatrix[3] = adfGeoTransform[0]; + adfMatrix[4] = adfGeoTransform[4]; + adfMatrix[5] = adfGeoTransform[5]; + adfMatrix[7] = adfGeoTransform[3]; + adfMatrix[15] = 1.0; + + if( bPixelIsPoint && !bPointGeoIgnore ) + { + adfMatrix[3] += adfGeoTransform[1] * 0.5 + adfGeoTransform[2] * 0.5; + adfMatrix[7] += adfGeoTransform[4] * 0.5 + adfGeoTransform[5] * 0.5; + } + + if( !EQUAL(osProfile,"BASELINE") ) + TIFFSetField( hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix ); + } + + // Do we need a world file? + if( CSLFetchBoolean( papszCreationOptions, "TFW", FALSE ) ) + GDALWriteWorldFile( osFilename, "tfw", adfGeoTransform ); + else if( CSLFetchBoolean( papszCreationOptions, "WORLDFILE", FALSE ) ) + GDALWriteWorldFile( osFilename, "wld", adfGeoTransform ); + } + else if( GetGCPCount() > 0 ) + { + double *padfTiePoints; + int iGCP; + + bNeedsRewrite = TRUE; + + padfTiePoints = (double *) + CPLMalloc( 6 * sizeof(double) * GetGCPCount() ); + + for( iGCP = 0; iGCP < GetGCPCount(); iGCP++ ) + { + + padfTiePoints[iGCP*6+0] = pasGCPList[iGCP].dfGCPPixel; + padfTiePoints[iGCP*6+1] = pasGCPList[iGCP].dfGCPLine; + padfTiePoints[iGCP*6+2] = 0; + padfTiePoints[iGCP*6+3] = pasGCPList[iGCP].dfGCPX; + padfTiePoints[iGCP*6+4] = pasGCPList[iGCP].dfGCPY; + padfTiePoints[iGCP*6+5] = pasGCPList[iGCP].dfGCPZ; + + if( bPixelIsPoint && !bPointGeoIgnore ) + { + padfTiePoints[iGCP*6+0] += 0.5; + padfTiePoints[iGCP*6+1] += 0.5; + } + } + + if( !EQUAL(osProfile,"BASELINE") ) + TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, + 6 * GetGCPCount(), padfTiePoints ); + CPLFree( padfTiePoints ); + } + +/* -------------------------------------------------------------------- */ +/* Write out projection definition. */ +/* -------------------------------------------------------------------- */ + if( pszProjection != NULL && !EQUAL( pszProjection, "" ) + && !EQUAL(osProfile,"BASELINE") ) + { + GTIF *psGTIF; + + bNeedsRewrite = TRUE; + + // If we have existing geokeys, try to wipe them + // by writing a dummy geokey directory. (#2546) + GTiffWriteDummyGeokeyDirectory(hTIFF); + + psGTIF = GTIFNew( hTIFF ); + + // set according to coordinate system. + GTIFSetFromOGISDefn( psGTIF, pszProjection ); + + if( bPixelIsPoint ) + { + GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1, + RasterPixelIsPoint); + } + + GTIFWriteKeys( psGTIF ); + GTIFFree( psGTIF ); + } +} + +/************************************************************************/ +/* AppendMetadataItem() */ +/************************************************************************/ + +static void AppendMetadataItem( CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail, + const char *pszKey, const char *pszValue, + int nBand, const char *pszRole, + const char *pszDomain ) + +{ + char szBandId[32]; + CPLXMLNode *psItem; + +/* -------------------------------------------------------------------- */ +/* Create the Item element, and subcomponents. */ +/* -------------------------------------------------------------------- */ + psItem = CPLCreateXMLNode( NULL, CXT_Element, "Item" ); + CPLCreateXMLNode( CPLCreateXMLNode( psItem, CXT_Attribute, "name"), + CXT_Text, pszKey ); + + if( nBand > 0 ) + { + sprintf( szBandId, "%d", nBand - 1 ); + CPLCreateXMLNode( CPLCreateXMLNode( psItem,CXT_Attribute,"sample"), + CXT_Text, szBandId ); + } + + if( pszRole != NULL ) + CPLCreateXMLNode( CPLCreateXMLNode( psItem,CXT_Attribute,"role"), + CXT_Text, pszRole ); + + if( pszDomain != NULL && strlen(pszDomain) > 0 ) + CPLCreateXMLNode( CPLCreateXMLNode( psItem,CXT_Attribute,"domain"), + CXT_Text, pszDomain ); + + char *pszEscapedItemValue = CPLEscapeString(pszValue,-1,CPLES_XML); + CPLCreateXMLNode( psItem, CXT_Text, pszEscapedItemValue ); + CPLFree( pszEscapedItemValue ); + +/* -------------------------------------------------------------------- */ +/* Create root, if missing. */ +/* -------------------------------------------------------------------- */ + if( *ppsRoot == NULL ) + *ppsRoot = CPLCreateXMLNode( NULL, CXT_Element, "GDALMetadata" ); + +/* -------------------------------------------------------------------- */ +/* Append item to tail. We keep track of the tail to avoid */ +/* O(nsquared) time as the list gets longer. */ +/* -------------------------------------------------------------------- */ + if( *ppsTail == NULL ) + CPLAddXMLChild( *ppsRoot, psItem ); + else + CPLAddXMLSibling( *ppsTail, psItem ); + + *ppsTail = psItem; +} + +/************************************************************************/ +/* WriteMDMetadata() */ +/************************************************************************/ + +static void WriteMDMetadata( GDALMultiDomainMetadata *poMDMD, TIFF *hTIFF, + CPLXMLNode **ppsRoot, CPLXMLNode **ppsTail, + int nBand, const char *pszProfile ) + +{ + int iDomain; + char **papszDomainList; + + (void) pszProfile; + +/* ==================================================================== */ +/* Process each domain. */ +/* ==================================================================== */ + papszDomainList = poMDMD->GetDomainList(); + for( iDomain = 0; papszDomainList && papszDomainList[iDomain]; iDomain++ ) + { + char **papszMD = poMDMD->GetMetadata( papszDomainList[iDomain] ); + int iItem; + int bIsXML = FALSE; + + if( EQUAL(papszDomainList[iDomain], "IMAGE_STRUCTURE") ) + continue; // ignored + if( EQUAL(papszDomainList[iDomain], "COLOR_PROFILE") ) + continue; // ignored + if( EQUAL(papszDomainList[iDomain], MD_DOMAIN_RPC) ) + continue; // handled elsewhere + if( EQUAL(papszDomainList[iDomain], "xml:ESRI") + && CSLTestBoolean(CPLGetConfigOption( "ESRI_XML_PAM", "NO" )) ) + continue; // handled elsewhere + + if( EQUALN(papszDomainList[iDomain], "xml:",4 ) ) + bIsXML = TRUE; + +/* -------------------------------------------------------------------- */ +/* Process each item in this domain. */ +/* -------------------------------------------------------------------- */ + for( iItem = 0; papszMD && papszMD[iItem]; iItem++ ) + { + const char *pszItemValue; + char *pszItemName = NULL; + + if( bIsXML ) + { + pszItemName = CPLStrdup("doc"); + pszItemValue = papszMD[iItem]; + } + else + { + pszItemValue = CPLParseNameValue( papszMD[iItem], &pszItemName); + if( pszItemName == NULL ) + { + CPLDebug("GTiff", "Invalid metadata item : %s", papszMD[iItem]); + continue; + } + } + +/* -------------------------------------------------------------------- */ +/* Convert into XML item or handle as a special TIFF tag. */ +/* -------------------------------------------------------------------- */ + if( strlen(papszDomainList[iDomain]) == 0 + && nBand == 0 && EQUALN(pszItemName,"TIFFTAG_",8) ) + { + if( EQUAL(pszItemName,"TIFFTAG_RESOLUTIONUNIT") ) { + /* ResolutionUnit can't be 0, which is the default if atoi() fails. + Set to 1=Unknown */ + int v = atoi(pszItemValue); + if (!v) v = RESUNIT_NONE; + TIFFSetField( hTIFF, TIFFTAG_RESOLUTIONUNIT, v); + } + else + { + int bFoundTag = FALSE; + size_t iTag; + for(iTag=0;iTagGetMetadata(MD_DOMAIN_RPC); + if( papszRPCMD != NULL ) + { + int bRPCSerializedOtherWay = FALSE; + + if( EQUAL(pszProfile,"GDALGeoTIFF") ) + { + if( !bWriteOnlyInPAMIfNeeded ) + GTiffDatasetWriteRPCTag( hTIFF, papszRPCMD ); + bRPCSerializedOtherWay = TRUE; + } + + /* Write RPB file if explicitly asked, or if a non GDAL specific */ + /* profile is selected and RPCTXT is not asked */ + int bRPBExplicitlyAsked = CSLFetchBoolean( papszCreationOptions, "RPB", FALSE ); + int bRPBExplicitlyDenied = !CSLFetchBoolean( papszCreationOptions, "RPB", TRUE ); + if( (!EQUAL(pszProfile,"GDALGeoTIFF") && + !CSLFetchBoolean( papszCreationOptions, "RPCTXT", FALSE ) && + !bRPBExplicitlyDenied ) + || bRPBExplicitlyAsked ) + { + if( !bWriteOnlyInPAMIfNeeded ) + GDALWriteRPBFile( pszTIFFFilename, papszRPCMD ); + bRPCSerializedOtherWay = TRUE; + } + + if( CSLFetchBoolean( papszCreationOptions, "RPCTXT", FALSE ) ) + { + if( !bWriteOnlyInPAMIfNeeded ) + GDALWriteRPCTXTFile( pszTIFFFilename, papszRPCMD ); + bRPCSerializedOtherWay = TRUE; + } + + if( !bRPCSerializedOtherWay && bWriteOnlyInPAMIfNeeded && bSrcIsGeoTIFF ) + ((GTiffDataset*)poSrcDS)->GDALPamDataset::SetMetadata(papszRPCMD, MD_DOMAIN_RPC); + } +} + +/************************************************************************/ +/* WriteMetadata() */ +/************************************************************************/ + +int GTiffDataset::WriteMetadata( GDALDataset *poSrcDS, TIFF *hTIFF, + int bSrcIsGeoTIFF, + const char *pszProfile, + const char *pszTIFFFilename, + char **papszCreationOptions, + int bExcludeRPBandIMGFileWriting) + +{ +/* -------------------------------------------------------------------- */ +/* Convert all the remaining metadata into a simple XML */ +/* format. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRoot = NULL, *psTail = NULL; + + if( bSrcIsGeoTIFF ) + { + WriteMDMetadata( &(((GTiffDataset *)poSrcDS)->oGTiffMDMD), + hTIFF, &psRoot, &psTail, 0, pszProfile ); + } + else + { + char **papszMD = poSrcDS->GetMetadata(); + + if( CSLCount(papszMD) > 0 ) + { + GDALMultiDomainMetadata oMDMD; + oMDMD.SetMetadata( papszMD ); + + WriteMDMetadata( &oMDMD, hTIFF, &psRoot, &psTail, 0, pszProfile ); + } + } + + if( !bExcludeRPBandIMGFileWriting ) + { + WriteRPC(poSrcDS, hTIFF, bSrcIsGeoTIFF, + pszProfile, pszTIFFFilename, + papszCreationOptions); + +/* -------------------------------------------------------------------- */ +/* Handle metadata data written to an IMD file. */ +/* -------------------------------------------------------------------- */ + char **papszIMDMD = poSrcDS->GetMetadata(MD_DOMAIN_IMD); + if( papszIMDMD != NULL ) + { + GDALWriteIMDFile( pszTIFFFilename, papszIMDMD ); + } + } +/* -------------------------------------------------------------------- */ +/* We also need to address band specific metadata, and special */ +/* "role" metadata. */ +/* -------------------------------------------------------------------- */ + int nBand; + for( nBand = 1; nBand <= poSrcDS->GetRasterCount(); nBand++ ) + { + GDALRasterBand *poBand = poSrcDS->GetRasterBand( nBand ); + + if( bSrcIsGeoTIFF ) + { + WriteMDMetadata( &(((GTiffRasterBand *)poBand)->oGTiffMDMD), + hTIFF, &psRoot, &psTail, nBand, pszProfile ); + } + else + { + char **papszMD = poBand->GetMetadata(); + + if( CSLCount(papszMD) > 0 ) + { + GDALMultiDomainMetadata oMDMD; + oMDMD.SetMetadata( papszMD ); + + WriteMDMetadata( &oMDMD, hTIFF, &psRoot, &psTail, nBand, + pszProfile ); + } + } + + double dfOffset = poBand->GetOffset(); + double dfScale = poBand->GetScale(); + + if( dfOffset != 0.0 || dfScale != 1.0 ) + { + char szValue[128]; + + CPLsprintf( szValue, "%.18g", dfOffset ); + AppendMetadataItem( &psRoot, &psTail, "OFFSET", szValue, nBand, + "offset", "" ); + CPLsprintf( szValue, "%.18g", dfScale ); + AppendMetadataItem( &psRoot, &psTail, "SCALE", szValue, nBand, + "scale", "" ); + } + + const char* pszUnitType = poBand->GetUnitType(); + if (pszUnitType != NULL && pszUnitType[0] != '\0') + AppendMetadataItem( &psRoot, &psTail, "UNITTYPE", pszUnitType, nBand, + "unittype", "" ); + + + if (strlen(poBand->GetDescription()) > 0) + { + AppendMetadataItem( &psRoot, &psTail, "DESCRIPTION", + poBand->GetDescription(), nBand, + "description", "" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Write out the generic XML metadata if there is any. */ +/* -------------------------------------------------------------------- */ + if( psRoot != NULL ) + { + int bRet = TRUE; + + if( EQUAL(pszProfile,"GDALGeoTIFF") ) + { + char *pszXML_MD = CPLSerializeXMLTree( psRoot ); + if( strlen(pszXML_MD) > 32000 ) + { + if( bSrcIsGeoTIFF ) + ((GTiffDataset *) poSrcDS)->PushMetadataToPam(); + else + bRet = FALSE; + CPLError( CE_Warning, CPLE_AppDefined, + "Lost metadata writing to GeoTIFF ... too large to fit in tag." ); + } + else + { + TIFFSetField( hTIFF, TIFFTAG_GDAL_METADATA, pszXML_MD ); + } + CPLFree( pszXML_MD ); + } + else + { + if( bSrcIsGeoTIFF ) + ((GTiffDataset *) poSrcDS)->PushMetadataToPam(); + else + bRet = FALSE; + } + + CPLDestroyXMLNode( psRoot ); + + return bRet; + } + else + { + /* If we have no more metadata but it existed before, remove the GDAL_METADATA tag */ + if( EQUAL(pszProfile,"GDALGeoTIFF") ) + { + char* pszText = NULL; + if( TIFFGetField( hTIFF, TIFFTAG_GDAL_METADATA, &pszText ) ) + { +#ifdef HAVE_UNSETFIELD + TIFFUnsetField( hTIFF, TIFFTAG_GDAL_METADATA ); +#else + TIFFSetField( hTIFF, TIFFTAG_GDAL_METADATA, "" ); +#endif + } + } + } + + return TRUE; +} + +/************************************************************************/ +/* PushMetadataToPam() */ +/* */ +/* When producing a strict profile TIFF or if our aggregate */ +/* metadata is too big for a single tiff tag we may end up */ +/* needing to write it via the PAM mechanisms. This method */ +/* copies all the appropriate metadata into the PAM level */ +/* metadata object but with special care to avoid copying */ +/* metadata handled in other ways in TIFF format. */ +/************************************************************************/ + +void GTiffDataset::PushMetadataToPam() + +{ + int nBand; + for( nBand = 0; nBand <= GetRasterCount(); nBand++ ) + { + GDALMultiDomainMetadata *poSrcMDMD; + GTiffRasterBand *poBand = NULL; + + if( nBand == 0 ) + poSrcMDMD = &(this->oGTiffMDMD); + else + { + poBand = (GTiffRasterBand *) GetRasterBand(nBand); + poSrcMDMD = &(poBand->oGTiffMDMD); + } + +/* -------------------------------------------------------------------- */ +/* Loop over the available domains. */ +/* -------------------------------------------------------------------- */ + int iDomain, i; + char **papszDomainList; + + papszDomainList = poSrcMDMD->GetDomainList(); + for( iDomain = 0; + papszDomainList && papszDomainList[iDomain]; + iDomain++ ) + { + char **papszMD = poSrcMDMD->GetMetadata( papszDomainList[iDomain] ); + + if( EQUAL(papszDomainList[iDomain],MD_DOMAIN_RPC) + || EQUAL(papszDomainList[iDomain],MD_DOMAIN_IMD) + || EQUAL(papszDomainList[iDomain],"_temporary_") + || EQUAL(papszDomainList[iDomain],"IMAGE_STRUCTURE") + || EQUAL(papszDomainList[iDomain],"COLOR_PROFILE") ) + continue; + + papszMD = CSLDuplicate(papszMD); + + for( i = CSLCount(papszMD)-1; i >= 0; i-- ) + { + if( EQUALN(papszMD[i],"TIFFTAG_",8) + || EQUALN(papszMD[i],GDALMD_AREA_OR_POINT, + strlen(GDALMD_AREA_OR_POINT)) ) + papszMD = CSLRemoveStrings( papszMD, i, 1, NULL ); + } + + if( nBand == 0 ) + GDALPamDataset::SetMetadata( papszMD, papszDomainList[iDomain]); + else + poBand->GDALPamRasterBand::SetMetadata( papszMD, papszDomainList[iDomain]); + + CSLDestroy( papszMD ); + } + +/* -------------------------------------------------------------------- */ +/* Handle some "special domain" stuff. */ +/* -------------------------------------------------------------------- */ + if( poBand != NULL ) + { + poBand->GDALPamRasterBand::SetOffset( poBand->GetOffset() ); + poBand->GDALPamRasterBand::SetScale( poBand->GetScale() ); + poBand->GDALPamRasterBand::SetUnitType( poBand->GetUnitType() ); + poBand->GDALPamRasterBand::SetDescription( poBand->GetDescription() ); + } + } +} + +/************************************************************************/ +/* GTiffDatasetWriteRPCTag() */ +/* */ +/* Format a TAG according to: */ +/* */ +/* http://geotiff.maptools.org/rpc_prop.html */ +/************************************************************************/ + +void GTiffDatasetWriteRPCTag( TIFF *hTIFF, char **papszRPCMD ) + +{ + double adfRPCTag[92]; + GDALRPCInfo sRPC; + + if( !GDALExtractRPCInfo( papszRPCMD, &sRPC ) ) + return; + + adfRPCTag[0] = -1.0; // Error Bias + adfRPCTag[1] = -1.0; // Error Random + + adfRPCTag[2] = sRPC.dfLINE_OFF; + adfRPCTag[3] = sRPC.dfSAMP_OFF; + adfRPCTag[4] = sRPC.dfLAT_OFF; + adfRPCTag[5] = sRPC.dfLONG_OFF; + adfRPCTag[6] = sRPC.dfHEIGHT_OFF; + adfRPCTag[7] = sRPC.dfLINE_SCALE; + adfRPCTag[8] = sRPC.dfSAMP_SCALE; + adfRPCTag[9] = sRPC.dfLAT_SCALE; + adfRPCTag[10] = sRPC.dfLONG_SCALE; + adfRPCTag[11] = sRPC.dfHEIGHT_SCALE; + + memcpy( adfRPCTag + 12, sRPC.adfLINE_NUM_COEFF, sizeof(double) * 20 ); + memcpy( adfRPCTag + 32, sRPC.adfLINE_DEN_COEFF, sizeof(double) * 20 ); + memcpy( adfRPCTag + 52, sRPC.adfSAMP_NUM_COEFF, sizeof(double) * 20 ); + memcpy( adfRPCTag + 72, sRPC.adfSAMP_DEN_COEFF, sizeof(double) * 20 ); + + TIFFSetField( hTIFF, TIFFTAG_RPCCOEFFICIENT, 92, adfRPCTag ); +} + +/************************************************************************/ +/* ReadRPCTag() */ +/* */ +/* Format a TAG according to: */ +/* */ +/* http://geotiff.maptools.org/rpc_prop.html */ +/************************************************************************/ + +char** GTiffDatasetReadRPCTag(TIFF* hTIFF) + +{ + double *padfRPCTag; + CPLString osField; + CPLString osMultiField; + CPLStringList asMD; + int i; + uint16 nCount; + + if( !TIFFGetField( hTIFF, TIFFTAG_RPCCOEFFICIENT, &nCount, &padfRPCTag ) + || nCount != 92 ) + return NULL; + + asMD.SetNameValue(RPC_LINE_OFF, CPLOPrintf("%.15g", padfRPCTag[2])); + asMD.SetNameValue(RPC_SAMP_OFF, CPLOPrintf("%.15g", padfRPCTag[3])); + asMD.SetNameValue(RPC_LAT_OFF, CPLOPrintf("%.15g", padfRPCTag[4])); + asMD.SetNameValue(RPC_LONG_OFF, CPLOPrintf("%.15g", padfRPCTag[5])); + asMD.SetNameValue(RPC_HEIGHT_OFF, CPLOPrintf("%.15g", padfRPCTag[6])); + asMD.SetNameValue(RPC_LINE_SCALE, CPLOPrintf("%.15g", padfRPCTag[7])); + asMD.SetNameValue(RPC_SAMP_SCALE, CPLOPrintf("%.15g", padfRPCTag[8])); + asMD.SetNameValue(RPC_LAT_SCALE, CPLOPrintf("%.15g", padfRPCTag[9])); + asMD.SetNameValue(RPC_LONG_SCALE, CPLOPrintf("%.15g", padfRPCTag[10])); + asMD.SetNameValue(RPC_HEIGHT_SCALE, CPLOPrintf("%.15g", padfRPCTag[11])); + + for( i = 0; i < 20; i++ ) + { + osField.Printf( "%.15g", padfRPCTag[12+i] ); + if( i > 0 ) + osMultiField += " "; + else + osMultiField = ""; + osMultiField += osField; + } + asMD.SetNameValue(RPC_LINE_NUM_COEFF, osMultiField ); + + for( i = 0; i < 20; i++ ) + { + osField.Printf( "%.15g", padfRPCTag[32+i] ); + if( i > 0 ) + osMultiField += " "; + else + osMultiField = ""; + osMultiField += osField; + } + asMD.SetNameValue( RPC_LINE_DEN_COEFF, osMultiField ); + + for( i = 0; i < 20; i++ ) + { + osField.Printf( "%.15g", padfRPCTag[52+i] ); + if( i > 0 ) + osMultiField += " "; + else + osMultiField = ""; + osMultiField += osField; + } + asMD.SetNameValue( RPC_SAMP_NUM_COEFF, osMultiField ); + + for( i = 0; i < 20; i++ ) + { + osField.Printf( "%.15g", padfRPCTag[72+i] ); + if( i > 0 ) + osMultiField += " "; + else + osMultiField = ""; + osMultiField += osField; + } + asMD.SetNameValue( RPC_SAMP_DEN_COEFF, osMultiField ); + + return asMD.StealList(); +} + +/************************************************************************/ +/* WriteNoDataValue() */ +/************************************************************************/ + +void GTiffDataset::WriteNoDataValue( TIFF *hTIFF, double dfNoData ) + +{ + char szVal[400]; + if (CPLIsNan(dfNoData)) + strcpy(szVal, "nan"); + else + CPLsnprintf(szVal, sizeof(szVal), "%.18g", dfNoData); + TIFFSetField( hTIFF, TIFFTAG_GDAL_NODATA, szVal ); +} + +/************************************************************************/ +/* SetDirectory() */ +/************************************************************************/ + +int GTiffDataset::SetDirectory( toff_t nNewOffset ) + +{ + Crystalize(); + + if( nNewOffset == 0 ) + nNewOffset = nDirOffset; + + if( TIFFCurrentDirOffset(hTIFF) == nNewOffset ) + { + CPLAssert( *ppoActiveDSRef == this || *ppoActiveDSRef == NULL ); + *ppoActiveDSRef = this; + return TRUE; + } + + if( GetAccess() == GA_Update ) + { + if( *ppoActiveDSRef != NULL ) + (*ppoActiveDSRef)->FlushDirectory(); + } + + if( nNewOffset == 0) + return TRUE; + + (*ppoActiveDSRef) = this; + + int nSetDirResult = TIFFSetSubDirectory( hTIFF, nNewOffset ); + if (!nSetDirResult) + return nSetDirResult; + +/* -------------------------------------------------------------------- */ +/* YCbCr JPEG compressed images should be translated on the fly */ +/* to RGB by libtiff/libjpeg unless specifically requested */ +/* otherwise. */ +/* -------------------------------------------------------------------- */ + if( !TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &(nCompression) ) ) + nCompression = COMPRESSION_NONE; + + if( !TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric) ) ) + nPhotometric = PHOTOMETRIC_MINISBLACK; + + if( nCompression == COMPRESSION_JPEG + && nPhotometric == PHOTOMETRIC_YCBCR + && CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", + "YES") ) ) + { + int nColorMode; + + TIFFGetField( hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode ); + if( nColorMode != JPEGCOLORMODE_RGB ) + TIFFSetField(hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + } + +/* -------------------------------------------------------------------- */ +/* Propogate any quality settings. */ +/* -------------------------------------------------------------------- */ + if( GetAccess() == GA_Update ) + { + // Now, reset zip and jpeg quality. + if(nJpegQuality > 0 && nCompression == COMPRESSION_JPEG) + { + CPLDebug( "GTiff", "Propagate JPEG_QUALITY(%d) in SetDirectory()", + nJpegQuality ); + TIFFSetField(hTIFF, TIFFTAG_JPEGQUALITY, nJpegQuality); + } + if(nJpegTablesMode >= 0 && nCompression == COMPRESSION_JPEG) + TIFFSetField(hTIFF, TIFFTAG_JPEGTABLESMODE, nJpegTablesMode); + if(nZLevel > 0 && nCompression == COMPRESSION_ADOBE_DEFLATE) + TIFFSetField(hTIFF, TIFFTAG_ZIPQUALITY, nZLevel); + if(nLZMAPreset > 0 && nCompression == COMPRESSION_LZMA) + TIFFSetField(hTIFF, TIFFTAG_LZMAPRESET, nLZMAPreset); + } + + return nSetDirResult; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int GTiffDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + const char *pszFilename = poOpenInfo->pszFilename; + if( EQUALN(pszFilename,"GTIFF_RAW:", strlen("GTIFF_RAW:")) ) + { + pszFilename += strlen("GTIFF_RAW:"); + GDALOpenInfo oOpenInfo( pszFilename, poOpenInfo->eAccess ); + return Identify(&oOpenInfo); + } + +/* -------------------------------------------------------------------- */ +/* We have a special hook for handling opening a specific */ +/* directory of a TIFF file. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszFilename,"GTIFF_DIR:",strlen("GTIFF_DIR:")) ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* First we check to see if the file has the expected header */ +/* bytes. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->fpL == NULL || poOpenInfo->nHeaderBytes < 2 ) + return FALSE; + + if( (poOpenInfo->pabyHeader[0] != 'I' || poOpenInfo->pabyHeader[1] != 'I') + && (poOpenInfo->pabyHeader[0] != 'M' || poOpenInfo->pabyHeader[1] != 'M')) + return FALSE; + +#ifndef BIGTIFF_SUPPORT + if( (poOpenInfo->pabyHeader[2] == 0x2B && poOpenInfo->pabyHeader[3] == 0) || + (poOpenInfo->pabyHeader[2] == 0 && poOpenInfo->pabyHeader[3] == 0x2B) ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "This is a BigTIFF file. BigTIFF is not supported by this\n" + "version of GDAL and libtiff." ); + return FALSE; + } +#endif + + if( (poOpenInfo->pabyHeader[2] != 0x2A || poOpenInfo->pabyHeader[3] != 0) + && (poOpenInfo->pabyHeader[3] != 0x2A || poOpenInfo->pabyHeader[2] != 0) + && (poOpenInfo->pabyHeader[2] != 0x2B || poOpenInfo->pabyHeader[3] != 0) + && (poOpenInfo->pabyHeader[3] != 0x2B || poOpenInfo->pabyHeader[2] != 0)) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* GTIFFErrorHandler() */ +/************************************************************************/ + +class GTIFFErrorStruct +{ +public: + CPLErr type; + int no; + CPLString msg; + + GTIFFErrorStruct() {} + GTIFFErrorStruct(CPLErr eErr, int no, const char* msg) : + type(eErr), no(no), msg(msg) {} +}; + +static void CPL_STDCALL GTIFFErrorHandler(CPLErr eErr, int no, const char* msg) +{ + std::vector* paoErrors = + (std::vector*) CPLGetErrorHandlerUserData(); + paoErrors->push_back(GTIFFErrorStruct(eErr, no, msg)); +} + +/************************************************************************/ +/* GTIFFExtendMemoryFile() */ +/************************************************************************/ + +static int GTIFFExtendMemoryFile(const CPLString& osTmpFilename, + VSILFILE* fpTemp, + VSILFILE* fpL, + int nNewLength, + GByte*& pabyBuffer, + vsi_l_offset& nDataLength) +{ + if( nNewLength <= (int)nDataLength ) + return TRUE; + VSIFSeekL(fpTemp, nNewLength - 1, SEEK_SET); + char ch = 0; + VSIFWriteL(&ch, 1, 1, fpTemp); + int nOldDataLength = nDataLength; + pabyBuffer = (GByte*)VSIGetMemFileBuffer( osTmpFilename, &nDataLength, FALSE); + int nToRead = nNewLength - nOldDataLength; + int nRead = (int)VSIFReadL( pabyBuffer + nOldDataLength, 1, nToRead, fpL); + if( nRead != nToRead ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Needed to read %d bytes. Only %d got", nToRead, nRead); + return FALSE; + } + return TRUE; +} + +/************************************************************************/ +/* GTIFFMakeBufferedStream() */ +/************************************************************************/ + +static int GTIFFMakeBufferedStream(GDALOpenInfo* poOpenInfo) +{ + CPLString osTmpFilename; + static int nCounter = 0; + osTmpFilename.Printf("/vsimem/stream_%d.tif", ++nCounter); + VSILFILE* fpTemp = VSIFOpenL(osTmpFilename, "wb+"); + if( fpTemp == NULL ) + return FALSE; + /* The seek is needed for /vsistdin/ that has some rewind capabilities */ + VSIFSeekL(poOpenInfo->fpL, poOpenInfo->nHeaderBytes, SEEK_SET); + CPLAssert( (int)VSIFTellL(poOpenInfo->fpL) == poOpenInfo->nHeaderBytes ); + VSIFWriteL(poOpenInfo->pabyHeader, 1, poOpenInfo->nHeaderBytes, fpTemp); + vsi_l_offset nDataLength; + GByte* pabyBuffer = (GByte*)VSIGetMemFileBuffer( osTmpFilename, &nDataLength, FALSE); + int bLittleEndian = (pabyBuffer[0] == 'I'); + int bSwap = (bLittleEndian && !CPL_IS_LSB) || (!bLittleEndian && CPL_IS_LSB); + int bBigTIFF = ( pabyBuffer[2] == 43 || pabyBuffer[3] == 43 ); + vsi_l_offset nMaxOffset = 0; + if( bBigTIFF ) + { + GUIntBig nTmp; + memcpy(&nTmp, pabyBuffer + 8, 8); + if( bSwap ) CPL_SWAP64PTR(&nTmp); + if( nTmp != 16 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "IFD start should be at offset 16 for a streamed BigTIFF"); + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + return FALSE; + } + memcpy(&nTmp, pabyBuffer + 16, 8); + if( bSwap ) CPL_SWAP64PTR(&nTmp); + if( nTmp > 1024 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Too many tags : " CPL_FRMT_GIB, nTmp); + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + return FALSE; + } + int nTags = nTmp; + int nSpaceForTags = nTags * 20; + if( !GTIFFExtendMemoryFile(osTmpFilename, fpTemp, poOpenInfo->fpL, + 24 + nSpaceForTags, + pabyBuffer, nDataLength) ) + { + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + return FALSE; + } + nMaxOffset = 24 + nSpaceForTags + 8; + for(int i=0;i= 16 * 1024 * 1024 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Too many elements for tag %d : " CPL_FRMT_GUIB, nTag, nTmp); + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + return FALSE; + } + GUInt32 nCount = (GUInt32)nTmp; + GUInt32 nTagSize = TIFFDataWidth((TIFFDataType)nDataType) * nCount; + if( nTagSize > 8 ) + { + memcpy(&nTmp, pabyBuffer + 24 + i * 20 + 12, 8); + if( bSwap ) CPL_SWAP64PTR(&nTmp); + if( nTmp > (GUIntBig)((((GIntBig)INT_MAX) << 32) - nTagSize) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Overflow with tag %d", nTag); + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + return FALSE; + } + if( (vsi_l_offset)(nTmp + nTagSize) > nMaxOffset ) + nMaxOffset = nTmp + nTagSize; + } + } + } + else + { + GUInt32 nTmp; + memcpy(&nTmp, pabyBuffer + 4, 4); + if( bSwap ) CPL_SWAP32PTR(&nTmp); + if( nTmp != 8 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "IFD start should be at offset 8 for a streamed TIFF"); + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + return FALSE; + } + GUInt16 nTmp16; + memcpy(&nTmp16, pabyBuffer + 8, 2); + if( bSwap ) CPL_SWAP16PTR(&nTmp16); + if( nTmp16 > 1024 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Too many tags : %d", nTmp16); + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + return FALSE; + } + int nTags = nTmp16; + int nSpaceForTags = nTags * 12; + if( !GTIFFExtendMemoryFile(osTmpFilename, fpTemp, poOpenInfo->fpL, + 10 + nSpaceForTags, + pabyBuffer, nDataLength) ) + { + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + return FALSE; + } + nMaxOffset = 10 + nSpaceForTags + 4; + for(int i=0;i= 16 * 1024 * 1024 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Too many elements for tag %d : %u", nTag, nTmp); + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + return FALSE; + } + GUInt32 nCount = nTmp; + GUInt32 nTagSize = TIFFDataWidth((TIFFDataType)nDataType) * nCount; + if( nTagSize > 4 ) + { + memcpy(&nTmp, pabyBuffer + 10 + i * 12 + 8, 4); + if( bSwap ) CPL_SWAP32PTR(&nTmp); + if( nTmp > (GUInt32)(0xFFFFFFFFU - nTagSize) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Overflow with tag %d", nTag); + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + return FALSE; + } + if( nTmp + nTagSize > nMaxOffset ) + nMaxOffset = nTmp + nTagSize; + } + } + } + if( nMaxOffset > 10 * 1024 * 1024 ) + { + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + return FALSE; + } + if( !GTIFFExtendMemoryFile(osTmpFilename, fpTemp, poOpenInfo->fpL, + nMaxOffset, pabyBuffer, nDataLength) ) + { + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + return FALSE; + } + CPLAssert(nDataLength == VSIFTellL(poOpenInfo->fpL)); + poOpenInfo->fpL = (VSILFILE*)VSICreateBufferedReaderHandle( + (VSIVirtualHandle*)poOpenInfo->fpL, pabyBuffer, ((vsi_l_offset)INT_MAX) << 32 ); + VSIFCloseL(fpTemp); + VSIUnlink(osTmpFilename); + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GTiffDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + TIFF *hTIFF; + int bAllowRGBAInterface = TRUE; + const char *pszFilename = poOpenInfo->pszFilename; + +/* -------------------------------------------------------------------- */ +/* Check if it looks like a TIFF file. */ +/* -------------------------------------------------------------------- */ + if (!Identify(poOpenInfo)) + return NULL; + + if( EQUALN(pszFilename,"GTIFF_RAW:", strlen("GTIFF_RAW:")) ) + { + bAllowRGBAInterface = FALSE; + pszFilename += strlen("GTIFF_RAW:"); + } + +/* -------------------------------------------------------------------- */ +/* We have a special hook for handling opening a specific */ +/* directory of a TIFF file. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszFilename,"GTIFF_DIR:",strlen("GTIFF_DIR:")) ) + return OpenDir( poOpenInfo ); + + if (!GTiffOneTimeInit()) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ + + /* Disable strip chop for now */ + int bStreaming = FALSE; + const char* pszReadStreaming = CPLGetConfigOption("TIFF_READ_STREAMING", NULL); + if( poOpenInfo->fpL == NULL ) + { + poOpenInfo->fpL = VSIFOpenL( pszFilename, ( poOpenInfo->eAccess == GA_ReadOnly ) ? "rb" : "r+b" ); + if( poOpenInfo->fpL == NULL ) + return NULL; + } + else if( !(pszReadStreaming && !CSLTestBoolean(pszReadStreaming)) && + poOpenInfo->nHeaderBytes >= 24 && + ((int)VSIFTellL(poOpenInfo->fpL) == poOpenInfo->nHeaderBytes || /* A pipe has no seeking capability, so its position is 0 despite having read bytes */ + strcmp(pszFilename, "/vsistdin/") == 0 || + //strncmp(pszFilename, "/vsicurl_streaming/", strlen("/vsicurl_streaming/")) == 0 || + (pszReadStreaming && CSLTestBoolean(pszReadStreaming))) ) + { + bStreaming = TRUE; + if( !GTIFFMakeBufferedStream(poOpenInfo) ) + return NULL; + } + + /* Store errors/warnings and emit them later */ + std::vector aoErrors; + CPLPushErrorHandlerEx(GTIFFErrorHandler, &aoErrors); + hTIFF = VSI_TIFFOpen( pszFilename, ( poOpenInfo->eAccess == GA_ReadOnly ) ? "rc" : "r+c", + poOpenInfo->fpL ); + CPLPopErrorHandler(); +#if SIZEOF_VOIDP == 4 + if( hTIFF == NULL ) + { + /* Case of one-strip file where the strip size is > 2GB (#5403) */ + if( bGlobalStripIntegerOverflow ) + { + hTIFF = VSI_TIFFOpen( pszFilename, ( poOpenInfo->eAccess == GA_ReadOnly ) ? "r" : "r+", + poOpenInfo->fpL ); + bGlobalStripIntegerOverflow = FALSE; + } + } + else + { + bGlobalStripIntegerOverflow = FALSE; + } +#endif + + /* Now emit errors and change their criticality if needed */ + /* We only emit failures if we didn't manage to open the file */ + /* Otherwise it make Python bindings unhappy (#5616) */ + for(size_t iError=0;iError INT_MAX || nYSize > INT_MAX ) + { + /* GDAL only supports signed 32bit dimensions */ + XTIFFClose( hTIFF ); + return( NULL ); + } + + if( !TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &(nPlanarConfig) ) ) + nPlanarConfig = PLANARCONFIG_CONTIG; + + if( !TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &(nCompression) ) ) + nCompression = COMPRESSION_NONE; + + if( !TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP, &(nRowsPerStrip) ) ) + nRowsPerStrip = nYSize; + + if (!TIFFIsTiled( hTIFF ) && + nCompression == COMPRESSION_NONE && + nRowsPerStrip >= nYSize && + nPlanarConfig == PLANARCONFIG_CONTIG) + { + int bReopenWithStripChop = TRUE; + if ( nYSize > 128 * 1024 * 1024 ) + { + uint16 nSamplesPerPixel; + uint16 nBitsPerSample; + + if( !TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSamplesPerPixel ) ) + nSamplesPerPixel = 1; + + if( !TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &(nBitsPerSample)) ) + nBitsPerSample = 1; + + vsi_l_offset nLineSize = (nSamplesPerPixel * (vsi_l_offset)nXSize * nBitsPerSample + 7) / 8; + int nDefaultStripHeight = (int)(8192 / nLineSize); + if (nDefaultStripHeight == 0) nDefaultStripHeight = 1; + vsi_l_offset nStrips = nYSize / nDefaultStripHeight; + + /* There is a risk of DoS due to huge amount of memory allocated in ChopUpSingleUncompressedStrip() */ + /* in libtiff */ + if (nStrips > 128 * 1024 * 1024 && + !CSLTestBoolean(CPLGetConfigOption("GTIFF_FORCE_STRIP_CHOP", "NO"))) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Potential denial of service detected. Avoid using strip chop. " + "Set the GTIFF_FORCE_STRIP_CHOP configuration open to go over this test."); + bReopenWithStripChop = FALSE; + } + } + + if (bReopenWithStripChop) + { + CPLDebug("GTiff", "Reopen with strip chop enabled"); + XTIFFClose(hTIFF); + hTIFF = VSI_TIFFOpen( pszFilename, ( poOpenInfo->eAccess == GA_ReadOnly ) ? "r" : "r+", + poOpenInfo->fpL ); + if( hTIFF == NULL ) + return( NULL ); + } + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GTiffDataset *poDS; + + poDS = new GTiffDataset(); + poDS->SetDescription( pszFilename ); + poDS->osFilename = pszFilename; + poDS->poActiveDS = poDS; + poDS->fpL = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + poDS->bStreamingIn = bStreaming; + + if( poDS->OpenOffset( hTIFF, &(poDS->poActiveDS), + TIFFCurrentDirOffset(hTIFF), TRUE, + poOpenInfo->eAccess, + bAllowRGBAInterface, TRUE, + poOpenInfo->GetSiblingFiles()) != CE_None ) + { + delete poDS; + return NULL; + } + + if( nCompression == COMPRESSION_JPEG && poOpenInfo->eAccess == GA_Update ) + { + int bHasQuantizationTable = FALSE, bHasHuffmanTable = FALSE; + int nQuality = poDS->GuessJPEGQuality(bHasQuantizationTable, + bHasHuffmanTable); + if( nQuality > 0 ) + { + CPLDebug("GTiff", "Guessed JPEG quality to be %d", nQuality); + poDS->nJpegQuality = nQuality; + TIFFSetField( hTIFF, TIFFTAG_JPEGQUALITY, nQuality ); + + /* This means we will use the quantization tables from the JpegTables */ + /* tag */ + poDS->nJpegTablesMode = JPEGTABLESMODE_QUANT; + } + else + { + uint32 nJPEGTableSize = 0; + void* pJPEGTable = NULL; + if( !TIFFGetField(hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize, &pJPEGTable) ) + { + int bFoundNonEmptyBlock = FALSE; + toff_t *panByteCounts = NULL; + int nBlockCount; + if( poDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) + nBlockCount = poDS->nBlocksPerBand * poDS->nBands; + else + nBlockCount = poDS->nBlocksPerBand; + if( TIFFIsTiled( hTIFF ) ) + TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &panByteCounts ); + else + TIFFGetField( hTIFF, TIFFTAG_STRIPBYTECOUNTS, &panByteCounts ); + if( panByteCounts != NULL ) + { + for( int iBlock = 0; iBlock < nBlockCount; iBlock++ ) + { + if( panByteCounts[iBlock] != 0 ) + { + bFoundNonEmptyBlock = TRUE; + break; + } + } + } + if( bFoundNonEmptyBlock ) + { + CPLDebug("GTiff", "Could not guess JPEG quality. " + "JPEG tables are missing, so going in " + "TIFFTAG_JPEGTABLESMODE = 0/2 mode"); + /* Write quantization tables in each strile */ + poDS->nJpegTablesMode = 0; + } + } + else + { + if( bHasQuantizationTable ) + { + // FIXME in libtiff: this is likely going to cause issues since + // libtiff will reuse in each strile the number of the global + // quantization table, which is invalid. + + CPLDebug("GTiff", "Could not guess JPEG quality although JPEG " + "quantization tables are present, so going in " + "TIFFTAG_JPEGTABLESMODE = 0/2 mode"); + } + else + { + CPLDebug("GTiff", "Could not guess JPEG quality since JPEG " + "quantization tables are not present, so going in " + "TIFFTAG_JPEGTABLESMODE = 0/2 mode"); + } + + /* Write quantization tables in each strile */ + poDS->nJpegTablesMode = 0; + } + } + if( bHasHuffmanTable ) + { + /* If there are Huffman tables in header use them, otherwise */ + /* if we use optimized tables, libtiff will currently reuse */ + /* the number of the Huffman tables of the header for the */ + /* optimized version of each strile, which is illegal */ + poDS->nJpegTablesMode |= JPEGTABLESMODE_HUFF; + } + if( poDS->nJpegTablesMode >= 0 ) + TIFFSetField( hTIFF, TIFFTAG_JPEGTABLESMODE, poDS->nJpegTablesMode); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->TryLoadXML( poOpenInfo->GetSiblingFiles() ); + poDS->ApplyPamInfo(); + + int i; + for(i=1;i<=poDS->nBands;i++) + { + GTiffRasterBand* poBand = (GTiffRasterBand*) poDS->GetRasterBand(i); + + /* Load scale, offset and unittype from PAM if available */ + if (!poBand->bHaveOffsetScale) + { + poBand->dfScale = poBand->GDALPamRasterBand::GetScale(&poBand->bHaveOffsetScale); + poBand->dfOffset = poBand->GDALPamRasterBand::GetOffset(); + } + if (poBand->osUnitType.size() == 0) + { + const char* pszUnitType = poBand->GDALPamRasterBand::GetUnitType(); + if (pszUnitType) + poBand->osUnitType = pszUnitType; + } + + GDALColorInterp ePAMColorInterp = poBand->GDALPamRasterBand::GetColorInterpretation(); + if( ePAMColorInterp != GCI_Undefined ) + poBand->eBandInterp = ePAMColorInterp; + } + + poDS->bColorProfileMetadataChanged = FALSE; + poDS->bMetadataChanged = FALSE; + poDS->bGeoTIFFInfoChanged = FALSE; + poDS->bNoDataChanged = FALSE; + poDS->bForceUnsetGTOrGCPs = FALSE; + poDS->bForceUnsetProjection = FALSE; + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, pszFilename, poOpenInfo->GetSiblingFiles() ); + + return poDS; +} + +/************************************************************************/ +/* GTiffDatasetSetAreaOrPointMD() */ +/************************************************************************/ + +static void GTiffDatasetSetAreaOrPointMD(GTIF* hGTIF, + GDALMultiDomainMetadata& oGTiffMDMD) +{ + // Is this a pixel-is-point dataset? + short nRasterType; + + if( GDALGTIFKeyGetSHORT(hGTIF, GTRasterTypeGeoKey, &nRasterType, + 0, 1 ) == 1 ) + { + if( nRasterType == (short) RasterPixelIsPoint ) + oGTiffMDMD.SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT ); + else + oGTiffMDMD.SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_AREA ); + } +} + +/************************************************************************/ +/* LoadMDAreaOrPoint() */ +/************************************************************************/ + +/* This is a light version of LookForProjection(), which saves the */ +/* potential costly cost of GTIFGetOGISDefn(), since we just need to */ +/* access to a raw GeoTIFF key, and not build the full projection object. */ + +void GTiffDataset::LoadMDAreaOrPoint() +{ + if( bLookedForProjection || bLookedForMDAreaOrPoint || + oGTiffMDMD.GetMetadataItem( GDALMD_AREA_OR_POINT ) != NULL ) + return; + + bLookedForMDAreaOrPoint = TRUE; + + if (!SetDirectory()) + return; + + GTIF* hGTIF = GTIFNew(hTIFF); + + if ( !hGTIF ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "GeoTIFF tags apparently corrupt, they are being ignored." ); + } + else + { + GTiffDatasetSetAreaOrPointMD( hGTIF, oGTiffMDMD ); + + GTIFFree( hGTIF ); + } +} + +/************************************************************************/ +/* LookForProjection() */ +/************************************************************************/ + +void GTiffDataset::LookForProjection() + +{ + if( bLookedForProjection ) + return; + + bLookedForProjection = TRUE; + if (!SetDirectory()) + return; + +/* -------------------------------------------------------------------- */ +/* Capture the GeoTIFF projection, if available. */ +/* -------------------------------------------------------------------- */ + GTIF *hGTIF; + + CPLFree( pszProjection ); + pszProjection = NULL; + + hGTIF = GTIFNew(hTIFF); + + if ( !hGTIF ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "GeoTIFF tags apparently corrupt, they are being ignored." ); + } + else + { + GTIFDefn *psGTIFDefn; + +#if LIBGEOTIFF_VERSION >= 1410 + psGTIFDefn = GTIFAllocDefn(); +#else + psGTIFDefn = (GTIFDefn *) CPLCalloc(1,sizeof(GTIFDefn)); +#endif + + if( GTIFGetDefn( hGTIF, psGTIFDefn ) ) + { + pszProjection = GTIFGetOGISDefn( hGTIF, psGTIFDefn ); + + // Should we simplify away vertical CS stuff? + if( EQUALN(pszProjection,"COMPD_CS",8) + && !CSLTestBoolean( CPLGetConfigOption("GTIFF_REPORT_COMPD_CS", + "NO") ) ) + { + OGRSpatialReference oSRS; + + CPLDebug( "GTiff", "Got COMPD_CS, but stripping it." ); + char *pszWKT = pszProjection; + oSRS.importFromWkt( &pszWKT ); + CPLFree( pszProjection ); + + oSRS.StripVertical(); + oSRS.exportToWkt( &pszProjection ); + } + } + + // check the tif linear unit and the CS linear unit +#ifdef ESRI_BUILD + AdjustLinearUnit(psGTIFDefn.UOMLength); +#endif + +#if LIBGEOTIFF_VERSION >= 1410 + GTIFFreeDefn(psGTIFDefn); +#else + CPLFree(psGTIFDefn); +#endif + + GTiffDatasetSetAreaOrPointMD( hGTIF, oGTiffMDMD ); + + GTIFFree( hGTIF ); + } + + if( pszProjection == NULL ) + { + pszProjection = CPLStrdup( "" ); + } + + bGeoTIFFInfoChanged = FALSE; + bForceUnsetGTOrGCPs = FALSE; + bForceUnsetProjection = FALSE; +} + +/************************************************************************/ +/* AdjustLinearUnit() */ +/* */ +/* The following code is only used in ESRI Builds and there is */ +/* outstanding discussion on whether it is even appropriate */ +/* then. */ +/************************************************************************/ +#ifdef ESRI_BUILD + +void GTiffDataset::AdjustLinearUnit(short UOMLength) +{ + if (!pszProjection || strlen(pszProjection) == 0) + return; + if( UOMLength == 9001) + { + char* pstr = strstr(pszProjection, "PARAMETER"); + if (!pstr) + return; + pstr = strstr(pstr, "UNIT["); + if (!pstr) + return; + pstr = strchr(pstr, ',') + 1; + if (!pstr) + return; + char* pstr1 = strchr(pstr, ']'); + if (!pstr1 || pstr1 - pstr >= 128) + return; + char csUnitStr[128]; + strncpy(csUnitStr, pstr, pstr1-pstr); + csUnitStr[pstr1-pstr] = '\0'; + double csUnit = CPLAtof(csUnitStr); + if(fabs(csUnit - 1.0) > 0.000001) + { + for(long i=0; i<6; i++) + adfGeoTransform[i] /= csUnit; + } + } +} + +#endif /* def ESRI_BUILD */ + +/************************************************************************/ +/* ApplyPamInfo() */ +/* */ +/* PAM Information, if available, overrides the GeoTIFF */ +/* geotransform and projection definition. Check for them */ +/* now. */ +/************************************************************************/ + +void GTiffDataset::ApplyPamInfo() + +{ + double adfPamGeoTransform[6]; + + if( GDALPamDataset::GetGeoTransform( adfPamGeoTransform ) == CE_None + && (adfPamGeoTransform[0] != 0.0 || adfPamGeoTransform[1] != 1.0 + || adfPamGeoTransform[2] != 0.0 || adfPamGeoTransform[3] != 0.0 + || adfPamGeoTransform[4] != 0.0 || adfPamGeoTransform[5] != 1.0 )) + { + memcpy( adfGeoTransform, adfPamGeoTransform, sizeof(double)*6 ); + bGeoTransformValid = TRUE; + } + + const char *pszPamSRS = GDALPamDataset::GetProjectionRef(); + + if( pszPamSRS != NULL && strlen(pszPamSRS) > 0 ) + { + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszPamSRS ); + bLookedForProjection = TRUE; + } + + int nPamGCPCount = GDALPamDataset::GetGCPCount(); + if( nPamGCPCount > 0 ) + { + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + pasGCPList = NULL; + } + + nGCPCount = nPamGCPCount; + pasGCPList = GDALDuplicateGCPs(nGCPCount, GDALPamDataset::GetGCPs()); + + CPLFree( pszProjection ); + pszProjection = NULL; + + const char *pszPamGCPProjection = GDALPamDataset::GetGCPProjection(); + if( pszPamGCPProjection != NULL && strlen(pszPamGCPProjection) > 0 ) + pszProjection = CPLStrdup(pszPamGCPProjection); + + bLookedForProjection = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Copy any PAM metadata into our GeoTIFF context, and with */ +/* the PAM info overriding the GeoTIFF context. */ +/* -------------------------------------------------------------------- */ + char **papszPamDomains = oMDMD.GetDomainList(); + + for( int iDomain = 0; papszPamDomains && papszPamDomains[iDomain] != NULL; iDomain++ ) + { + const char *pszDomain = papszPamDomains[iDomain]; + char **papszGT_MD = CSLDuplicate(oGTiffMDMD.GetMetadata( pszDomain )); + char **papszPAM_MD = oMDMD.GetMetadata( pszDomain ); + + papszGT_MD = CSLMerge( papszGT_MD, papszPAM_MD ); + + oGTiffMDMD.SetMetadata( papszGT_MD, pszDomain ); + CSLDestroy( papszGT_MD ); + } + + for( int i = 1; i <= GetRasterCount(); i++) + { + GTiffRasterBand* poBand = (GTiffRasterBand *)GetRasterBand(i); + papszPamDomains = poBand->oMDMD.GetDomainList(); + + for( int iDomain = 0; papszPamDomains && papszPamDomains[iDomain] != NULL; iDomain++ ) + { + const char *pszDomain = papszPamDomains[iDomain]; + char **papszGT_MD = CSLDuplicate(poBand->oGTiffMDMD.GetMetadata( pszDomain )); + char **papszPAM_MD = poBand->oMDMD.GetMetadata( pszDomain ); + + papszGT_MD = CSLMerge( papszGT_MD, papszPAM_MD ); + + poBand->oGTiffMDMD.SetMetadata( papszGT_MD, pszDomain ); + CSLDestroy( papszGT_MD ); + } + } +} + +/************************************************************************/ +/* OpenDir() */ +/* */ +/* Open a specific directory as encoded into a filename. */ +/************************************************************************/ + +GDALDataset *GTiffDataset::OpenDir( GDALOpenInfo * poOpenInfo ) + +{ + int bAllowRGBAInterface = TRUE; + const char* pszFilename = poOpenInfo->pszFilename; + if( EQUALN(pszFilename,"GTIFF_RAW:", strlen("GTIFF_RAW:")) ) + { + bAllowRGBAInterface = FALSE; + pszFilename += strlen("GTIFF_RAW:"); + } + + if( !EQUALN(pszFilename,"GTIFF_DIR:",strlen("GTIFF_DIR:")) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Split out filename, and dir#/offset. */ +/* -------------------------------------------------------------------- */ + pszFilename += strlen("GTIFF_DIR:"); + int bAbsolute = FALSE; + toff_t nOffset; + + if( EQUALN(pszFilename,"off:",4) ) + { + bAbsolute = TRUE; + pszFilename += 4; + } + + nOffset = atol(pszFilename); + pszFilename += 1; + + while( *pszFilename != '\0' && pszFilename[-1] != ':' ) + pszFilename++; + + if( *pszFilename == '\0' || nOffset == 0 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to extract offset or filename, should take the form\n" + "GTIFF_DIR::filename or GTIFF_DIR:off::filename" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ + TIFF *hTIFF; + + if (!GTiffOneTimeInit()) + return NULL; + + VSILFILE* fpL = VSIFOpenL(pszFilename, "r"); + if( fpL == NULL ) + return NULL; + hTIFF = VSI_TIFFOpen( pszFilename, "r", fpL ); + if( hTIFF == NULL ) + { + VSIFCloseL(fpL); + return( NULL ); + } + +/* -------------------------------------------------------------------- */ +/* If a directory was requested by index, advance to it now. */ +/* -------------------------------------------------------------------- */ + if( !bAbsolute ) + { + toff_t nOffsetRequested = nOffset; + while( nOffset > 1 ) + { + if( TIFFReadDirectory( hTIFF ) == 0 ) + { + XTIFFClose( hTIFF ); + CPLError( CE_Failure, CPLE_OpenFailed, + "Requested directory %lu not found.", (long unsigned int)nOffsetRequested ); + VSIFCloseL(fpL); + return NULL; + } + nOffset--; + } + + nOffset = TIFFCurrentDirOffset( hTIFF ); + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GTiffDataset *poDS; + + poDS = new GTiffDataset(); + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->osFilename = poOpenInfo->pszFilename; + poDS->poActiveDS = poDS; + poDS->fpL = fpL; + + if( !EQUAL(pszFilename,poOpenInfo->pszFilename) + && !EQUALN(poOpenInfo->pszFilename,"GTIFF_RAW:",10) ) + { + poDS->SetPhysicalFilename( pszFilename ); + poDS->SetSubdatasetName( poOpenInfo->pszFilename ); + poDS->osFilename = pszFilename; + } + + if (poOpenInfo->eAccess == GA_Update) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Opening a specific TIFF directory is not supported in update mode. Switching to read-only" ); + } + + if( poDS->OpenOffset( hTIFF, &(poDS->poActiveDS), + nOffset, FALSE, GA_ReadOnly, + bAllowRGBAInterface, TRUE, + poOpenInfo->GetSiblingFiles() ) != CE_None ) + { + delete poDS; + return NULL; + } + else + { + poDS->bCloseTIFFHandle = TRUE; + return poDS; + } +} + +/************************************************************************/ +/* ConvertTransferFunctionToString() */ +/* */ +/* Convert a transfer function table into a string. */ +/* Used by LoadICCProfile(). */ +/************************************************************************/ +static CPLString ConvertTransferFunctionToString( const uint16 *pTable, uint32 nTableEntries ) +{ + CPLString sValue; + + for(uint32 i = 0; i < nTableEntries; i++) + { + if (i == 0) + sValue = sValue.Printf("%d", (uint32)pTable[i]); + else + sValue = sValue.Printf("%s, %d", (const char*)sValue, (uint32)pTable[i]); + } + + return sValue; +} + +/************************************************************************/ +/* LoadICCProfile() */ +/* */ +/* Load ICC Profile or colorimetric data into metadata */ +/************************************************************************/ + +void GTiffDataset::LoadICCProfile() +{ + uint32 nEmbedLen; + uint8* pEmbedBuffer; + float* pCHR; + float* pWP; + uint16 *pTFR, *pTFG, *pTFB; + uint16 *pTransferRange = NULL; + const int TIFFTAG_TRANSFERRANGE = 0x0156; + + if (bICCMetadataLoaded) + return; + bICCMetadataLoaded = TRUE; + + if (!SetDirectory()) + return; + + if (TIFFGetField(hTIFF, TIFFTAG_ICCPROFILE, &nEmbedLen, &pEmbedBuffer)) + { + char *pszBase64Profile = CPLBase64Encode(nEmbedLen, (const GByte*)pEmbedBuffer); + + oGTiffMDMD.SetMetadataItem( "SOURCE_ICC_PROFILE", pszBase64Profile, "COLOR_PROFILE" ); + + CPLFree(pszBase64Profile); + + return; + } + + /* Check for colorimetric tiff */ + if (TIFFGetField(hTIFF, TIFFTAG_PRIMARYCHROMATICITIES, &pCHR)) + { + if (TIFFGetField(hTIFF, TIFFTAG_WHITEPOINT, &pWP)) + { + if (!TIFFGetFieldDefaulted(hTIFF, TIFFTAG_TRANSFERFUNCTION, &pTFR, &pTFG, &pTFB)) + return; + + TIFFGetFieldDefaulted(hTIFF, TIFFTAG_TRANSFERRANGE, &pTransferRange); + + // Set all the colorimetric metadata. + oGTiffMDMD.SetMetadataItem( "SOURCE_PRIMARIES_RED", + CPLString().Printf( "%.9f, %.9f, 1.0", (double)pCHR[0], (double)pCHR[1] ) , "COLOR_PROFILE" ); + oGTiffMDMD.SetMetadataItem( "SOURCE_PRIMARIES_GREEN", + CPLString().Printf( "%.9f, %.9f, 1.0", (double)pCHR[2], (double)pCHR[3] ) , "COLOR_PROFILE" ); + oGTiffMDMD.SetMetadataItem( "SOURCE_PRIMARIES_BLUE", + CPLString().Printf( "%.9f, %.9f, 1.0", (double)pCHR[4], (double)pCHR[5] ) , "COLOR_PROFILE" ); + + oGTiffMDMD.SetMetadataItem( "SOURCE_WHITEPOINT", + CPLString().Printf( "%.9f, %.9f, 1.0", (double)pWP[0], (double)pWP[1] ) , "COLOR_PROFILE" ); + + /* Set transfer function metadata */ + + /* Get length of table. */ + const uint32 nTransferFunctionLength = 1 << nBitsPerSample; + + oGTiffMDMD.SetMetadataItem( "TIFFTAG_TRANSFERFUNCTION_RED", + ConvertTransferFunctionToString( pTFR, nTransferFunctionLength), "COLOR_PROFILE" ); + + oGTiffMDMD.SetMetadataItem( "TIFFTAG_TRANSFERFUNCTION_GREEN", + ConvertTransferFunctionToString( pTFG, nTransferFunctionLength), "COLOR_PROFILE" ); + + oGTiffMDMD.SetMetadataItem( "TIFFTAG_TRANSFERFUNCTION_BLUE", + ConvertTransferFunctionToString( pTFB, nTransferFunctionLength), "COLOR_PROFILE" ); + + /* Set transfer range */ + if (pTransferRange) + { + oGTiffMDMD.SetMetadataItem( "TIFFTAG_TRANSFERRANGE_BLACK", + CPLString().Printf( "%d, %d, %d", + (int)pTransferRange[0], (int)pTransferRange[2], (int)pTransferRange[4]), "COLOR_PROFILE" ); + oGTiffMDMD.SetMetadataItem( "TIFFTAG_TRANSFERRANGE_WHITE", + CPLString().Printf( "%d, %d, %d", + (int)pTransferRange[1], (int)pTransferRange[3], (int)pTransferRange[5]), "COLOR_PROFILE" ); + } + } + } +} + +/************************************************************************/ +/* SaveICCProfile() */ +/* */ +/* Save ICC Profile or colorimetric data into file */ +/* pDS: */ +/* Dataset that contains the metadata with the ICC or colorimetric */ +/* data. If this argument is specified, all other arguments are */ +/* ignored. Set them to NULL or 0. */ +/* hTIFF: */ +/* Pointer to TIFF handle. Only needed if pDS is NULL or */ +/* pDS->hTIFF is NULL. */ +/* papszParmList: */ +/* Options containing the ICC profile or colorimetric metadata. */ +/* Ignored if pDS is not NULL. */ +/* nBitsPerSample: */ +/* Bits per sample. Ignored if pDS is not NULL. */ +/************************************************************************/ + +void GTiffDataset::SaveICCProfile(GTiffDataset *pDS, TIFF *hTIFF, char **papszParmList, uint32 nBitsPerSample) +{ + if ((pDS != NULL) && (pDS->eAccess != GA_Update)) + return; + + if (hTIFF == NULL) + { + if (pDS == NULL) + return; + + hTIFF = pDS->hTIFF; + if (hTIFF == NULL) + return; + } + + if ((papszParmList == NULL) && (pDS == NULL)) + return; + + const char *pszValue = NULL; + if (pDS != NULL) + pszValue = pDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE"); + else + pszValue = CSLFetchNameValue(papszParmList, "SOURCE_ICC_PROFILE"); + if( pszValue != NULL ) + { + int32 nEmbedLen; + char *pEmbedBuffer = CPLStrdup(pszValue); + nEmbedLen = CPLBase64DecodeInPlace((GByte*)pEmbedBuffer); + + TIFFSetField(hTIFF, TIFFTAG_ICCPROFILE, nEmbedLen, pEmbedBuffer); + + CPLFree(pEmbedBuffer); + } + else + { + /* Output colorimetric data. */ + const int TIFFTAG_TRANSFERRANGE = 0x0156; + + float pCHR[6]; // Primaries + float pWP[2]; // Whitepoint + uint16 pTXR[6]; // Transfer range + const char* pszCHRNames[] = { + "SOURCE_PRIMARIES_RED", + "SOURCE_PRIMARIES_GREEN", + "SOURCE_PRIMARIES_BLUE" + }; + const char* pszTXRNames[] = { + "TIFFTAG_TRANSFERRANGE_BLACK", + "TIFFTAG_TRANSFERRANGE_WHITE" + }; + + /* Output chromacities */ + bool bOutputCHR = true; + for(int i = 0; ((i < 3) && bOutputCHR); i++) + { + if (pDS != NULL) + pszValue = pDS->GetMetadataItem(pszCHRNames[i], "COLOR_PROFILE"); + else + pszValue = CSLFetchNameValue(papszParmList, pszCHRNames[i]); + if (pszValue == NULL) + { + bOutputCHR = false; + break; + } + + char** papszTokens = CSLTokenizeString2( pszValue, ",", + CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + + if (CSLCount( papszTokens ) != 3) + { + bOutputCHR = false; + CSLDestroy( papszTokens ); + break; + } + + int j; + for( j = 0; j < 3; j++ ) + { + float v = (float)CPLAtof(papszTokens[j]); + + if (j == 2) + { + /* Last term of xyY color must be 1.0 */ + if (v != 1.0) + { + bOutputCHR = false; + break; + } + } + else + { + pCHR[i * 2 + j] = v; + } + } + + CSLDestroy( papszTokens ); + } + + if (bOutputCHR) + { + TIFFSetField(hTIFF, TIFFTAG_PRIMARYCHROMATICITIES, pCHR); + } + + /* Output whitepoint */ + bool bOutputWhitepoint = true; + if (pDS != NULL) + pszValue = pDS->GetMetadataItem("SOURCE_WHITEPOINT", "COLOR_PROFILE"); + else + pszValue = CSLFetchNameValue(papszParmList, "SOURCE_WHITEPOINT"); + if (pszValue != NULL) + { + char** papszTokens = CSLTokenizeString2( pszValue, ",", + CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + + if (CSLCount( papszTokens ) != 3) + { + bOutputWhitepoint = false; + } + else + { + int j; + for( j = 0; j < 3; j++ ) + { + float v = (float)CPLAtof(papszTokens[j]); + + if (j == 2) + { + /* Last term of xyY color must be 1.0 */ + if (v != 1.0) + { + bOutputWhitepoint = false; + break; + } + } + else + { + pWP[j] = v; + } + } + } + CSLDestroy( papszTokens ); + + if (bOutputWhitepoint) + { + TIFFSetField(hTIFF, TIFFTAG_WHITEPOINT, pWP); + } + } + + /* Set transfer function metadata */ + char const *pszTFRed = NULL; + char const *pszTFGreen = NULL; + char const *pszTFBlue = NULL; + if (pDS != NULL) + pszTFRed = pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_RED", "COLOR_PROFILE"); + else + pszTFRed = CSLFetchNameValue(papszParmList, "TIFFTAG_TRANSFERFUNCTION_RED"); + + if (pDS != NULL) + pszTFGreen = pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_GREEN", "COLOR_PROFILE"); + else + pszTFGreen = CSLFetchNameValue(papszParmList, "TIFFTAG_TRANSFERFUNCTION_GREEN"); + + if (pDS != NULL) + pszTFBlue = pDS->GetMetadataItem("TIFFTAG_TRANSFERFUNCTION_BLUE", "COLOR_PROFILE"); + else + pszTFBlue = CSLFetchNameValue(papszParmList, "TIFFTAG_TRANSFERFUNCTION_BLUE"); + + if ((pszTFRed != NULL) && (pszTFGreen != NULL) && (pszTFBlue != NULL)) + { + /* Get length of table. */ + const int nTransferFunctionLength = 1 << ((pDS!=NULL)?pDS->nBitsPerSample:nBitsPerSample); + + char** papszTokensRed = CSLTokenizeString2( pszTFRed, ",", + CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + char** papszTokensGreen = CSLTokenizeString2( pszTFGreen, ",", + CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + char** papszTokensBlue = CSLTokenizeString2( pszTFBlue, ",", + CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + + if ((CSLCount( papszTokensRed ) == nTransferFunctionLength) && + (CSLCount( papszTokensGreen ) == nTransferFunctionLength) && + (CSLCount( papszTokensBlue ) == nTransferFunctionLength)) + { + uint16 *pTransferFuncRed, *pTransferFuncGreen, *pTransferFuncBlue; + pTransferFuncRed = (uint16*)CPLMalloc(sizeof(uint16) * nTransferFunctionLength); + pTransferFuncGreen = (uint16*)CPLMalloc(sizeof(uint16) * nTransferFunctionLength); + pTransferFuncBlue = (uint16*)CPLMalloc(sizeof(uint16) * nTransferFunctionLength); + + /* Convert our table in string format into int16 format. */ + for(int i = 0; i < nTransferFunctionLength; i++) + { + pTransferFuncRed[i] = (uint16)atoi(papszTokensRed[i]); + pTransferFuncGreen[i] = (uint16)atoi(papszTokensGreen[i]); + pTransferFuncBlue[i] = (uint16)atoi(papszTokensBlue[i]); + } + + TIFFSetField(hTIFF, TIFFTAG_TRANSFERFUNCTION, + pTransferFuncRed, pTransferFuncGreen, pTransferFuncBlue); + + CPLFree(pTransferFuncRed); + CPLFree(pTransferFuncGreen); + CPLFree(pTransferFuncBlue); + } + + CSLDestroy( papszTokensRed ); + CSLDestroy( papszTokensGreen ); + CSLDestroy( papszTokensBlue ); + } + + /* Output transfer range */ + bool bOutputTransferRange = true; + for(int i = 0; ((i < 2) && bOutputTransferRange); i++) + { + if (pDS != NULL) + pszValue = pDS->GetMetadataItem(pszTXRNames[i], "COLOR_PROFILE"); + else + pszValue = CSLFetchNameValue(papszParmList, pszTXRNames[i]); + if (pszValue == NULL) + { + bOutputTransferRange = false; + break; + } + + char** papszTokens = CSLTokenizeString2( pszValue, ",", + CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + + if (CSLCount( papszTokens ) != 3) + { + bOutputTransferRange = false; + CSLDestroy( papszTokens ); + break; + } + + int j; + for( j = 0; j < 3; j++ ) + { + pTXR[i + j * 2] = (uint16)atoi(papszTokens[j]); + } + + CSLDestroy( papszTokens ); + } + + if (bOutputTransferRange) + { + TIFFSetField(hTIFF, TIFFTAG_TRANSFERRANGE, pTXR); + } + } +} + +/************************************************************************/ +/* OpenOffset() */ +/* */ +/* Initialize the GTiffDataset based on a passed in file */ +/* handle, and directory offset to utilize. This is called for */ +/* full res, and overview pages. */ +/************************************************************************/ + +CPLErr GTiffDataset::OpenOffset( TIFF *hTIFFIn, + GTiffDataset **ppoActiveDSRef, + toff_t nDirOffsetIn, + int bBaseIn, GDALAccess eAccess, + int bAllowRGBAInterface, + int bReadGeoTransform, + char** papszSiblingFiles ) + +{ + uint32 nXSize, nYSize; + int bTreatAsBitmap = FALSE; + int bTreatAsOdd = FALSE; + + this->eAccess = eAccess; + + hTIFF = hTIFFIn; + this->ppoActiveDSRef = ppoActiveDSRef; + + nDirOffset = nDirOffsetIn; + + if (!SetDirectory( nDirOffsetIn )) + return CE_Failure; + + bBase = bBaseIn; + + this->eAccess = eAccess; + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize ); + TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &nYSize ); + nRasterXSize = nXSize; + nRasterYSize = nYSize; + + if( !TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSamplesPerPixel ) ) + nBands = 1; + else + nBands = nSamplesPerPixel; + + if( !TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &(nBitsPerSample)) ) + nBitsPerSample = 1; + + if( !TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &(nPlanarConfig) ) ) + nPlanarConfig = PLANARCONFIG_CONTIG; + + if( !TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric) ) ) + nPhotometric = PHOTOMETRIC_MINISBLACK; + + if( !TIFFGetField( hTIFF, TIFFTAG_SAMPLEFORMAT, &(nSampleFormat) ) ) + nSampleFormat = SAMPLEFORMAT_UINT; + + if( !TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &(nCompression) ) ) + nCompression = COMPRESSION_NONE; + +#if defined(TIFFLIB_VERSION) && TIFFLIB_VERSION > 20031007 /* 3.6.0 */ + if (nCompression != COMPRESSION_NONE && + !TIFFIsCODECConfigured(nCompression)) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot open TIFF file due to missing codec." ); + return CE_Failure; + } +#endif + +/* -------------------------------------------------------------------- */ +/* YCbCr JPEG compressed images should be translated on the fly */ +/* to RGB by libtiff/libjpeg unless specifically requested */ +/* otherwise. */ +/* -------------------------------------------------------------------- */ + if( nCompression == COMPRESSION_JPEG + && nPhotometric == PHOTOMETRIC_YCBCR + && CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", + "YES") ) ) + { + int nColorMode; + + SetMetadataItem( "SOURCE_COLOR_SPACE", "YCbCr", "IMAGE_STRUCTURE" ); + if ( !TIFFGetField( hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode ) || + nColorMode != JPEGCOLORMODE_RGB ) + TIFFSetField(hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + } + +/* -------------------------------------------------------------------- */ +/* Get strip/tile layout. */ +/* -------------------------------------------------------------------- */ + if( TIFFIsTiled(hTIFF) ) + { + TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &(nBlockXSize) ); + TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &(nBlockYSize) ); + } + else + { + if( !TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP, + &(nRowsPerStrip) ) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "RowsPerStrip not defined ... assuming all one strip." ); + nRowsPerStrip = nYSize; /* dummy value */ + } + + // If the rows per strip is larger than the file we will get + // confused. libtiff internally will treat the rowsperstrip as + // the image height and it is best if we do too. (#4468) + if (nRowsPerStrip > (uint32)nRasterYSize) + nRowsPerStrip = nRasterYSize; + + nBlockXSize = nRasterXSize; + nBlockYSize = nRowsPerStrip; + } + + nBlocksPerBand = + DIV_ROUND_UP(nYSize, nBlockYSize) * DIV_ROUND_UP(nXSize, nBlockXSize); + +/* -------------------------------------------------------------------- */ +/* Should we handle this using the GTiffBitmapBand? */ +/* -------------------------------------------------------------------- */ + if( nBitsPerSample == 1 && nBands == 1 ) + { + bTreatAsBitmap = TRUE; + + // Lets treat large "one row" bitmaps using the scanline api. + if( !TIFFIsTiled(hTIFF) + && nBlockYSize == nYSize + && nYSize > 2000 ) + bTreatAsSplitBitmap = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Should we treat this via the RGBA interface? */ +/* -------------------------------------------------------------------- */ + if( bAllowRGBAInterface && + !bTreatAsBitmap && !(nBitsPerSample > 8) + && (nPhotometric == PHOTOMETRIC_CIELAB || + nPhotometric == PHOTOMETRIC_LOGL || + nPhotometric == PHOTOMETRIC_LOGLUV || + nPhotometric == PHOTOMETRIC_SEPARATED || + ( nPhotometric == PHOTOMETRIC_YCBCR + && nCompression != COMPRESSION_JPEG )) ) + { + char szMessage[1024]; + + if( TIFFRGBAImageOK( hTIFF, szMessage ) == 1 ) + { + const char* pszSourceColorSpace = NULL; + switch (nPhotometric) + { + case PHOTOMETRIC_CIELAB: + pszSourceColorSpace = "CIELAB"; + break; + case PHOTOMETRIC_LOGL: + pszSourceColorSpace = "LOGL"; + break; + case PHOTOMETRIC_LOGLUV: + pszSourceColorSpace = "LOGLUV"; + break; + case PHOTOMETRIC_SEPARATED: + pszSourceColorSpace = "CMYK"; + break; + case PHOTOMETRIC_YCBCR: + pszSourceColorSpace = "YCbCr"; + break; + } + if (pszSourceColorSpace) + SetMetadataItem( "SOURCE_COLOR_SPACE", pszSourceColorSpace, "IMAGE_STRUCTURE" ); + bTreatAsRGBA = TRUE; + nBands = 4; + } + else + { + CPLDebug( "GTiff", "TIFFRGBAImageOK says:\n%s", szMessage ); + } + } + +/* -------------------------------------------------------------------- */ +/* Should we treat this via the split interface? */ +/* -------------------------------------------------------------------- */ + if( !TIFFIsTiled(hTIFF) + && nBitsPerSample == 8 + && nBlockYSize == nYSize + && nYSize > 2000 + && !bTreatAsRGBA + && CSLTestBoolean(CPLGetConfigOption("GDAL_ENABLE_TIFF_SPLIT", "YES"))) + { + /* libtiff 3.9.2 (20091104) and older, libtiff 4.0.0beta5 (also 20091104) */ + /* and older will crash when trying to open a all-in-one-strip */ + /* YCbCr JPEG compressed TIFF (see #3259). */ +#if (TIFFLIB_VERSION <= 20091104 && !defined(BIGTIFF_SUPPORT)) || \ + (TIFFLIB_VERSION <= 20091104 && defined(BIGTIFF_SUPPORT)) + if (nPhotometric == PHOTOMETRIC_YCBCR && + nCompression == COMPRESSION_JPEG) + { + CPLDebug("GTiff", "Avoid using split band to open all-in-one-strip " + "YCbCr JPEG compressed TIFF because of older libtiff"); + } + else +#endif + bTreatAsSplit = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Should we treat this via the odd bits interface? */ +/* -------------------------------------------------------------------- */ + if ( nSampleFormat == SAMPLEFORMAT_IEEEFP ) + { + if ( nBitsPerSample == 16 || nBitsPerSample == 24 ) + bTreatAsOdd = TRUE; + } + else if ( !bTreatAsRGBA && !bTreatAsBitmap + && nBitsPerSample != 8 + && nBitsPerSample != 16 + && nBitsPerSample != 32 + && nBitsPerSample != 64 + && nBitsPerSample != 128 ) + bTreatAsOdd = TRUE; + + int bMinIsWhite = nPhotometric == PHOTOMETRIC_MINISWHITE; + +/* -------------------------------------------------------------------- */ +/* Check for NODATA */ +/* -------------------------------------------------------------------- */ + char *pszText; + if( TIFFGetField( hTIFF, TIFFTAG_GDAL_NODATA, &pszText ) && + !EQUAL(pszText, "") ) + { + bNoDataSet = TRUE; + dfNoDataValue = CPLAtofM( pszText ); + } + +/* -------------------------------------------------------------------- */ +/* Capture the color table if there is one. */ +/* -------------------------------------------------------------------- */ + unsigned short *panRed, *panGreen, *panBlue; + + if( bTreatAsRGBA + || TIFFGetField( hTIFF, TIFFTAG_COLORMAP, + &panRed, &panGreen, &panBlue) == 0 ) + { + // Build inverted palette if we have inverted photometric. + // Pixel values remains unchanged. Avoid doing this for *deep* + // data types (per #1882) + if( nBitsPerSample <= 16 && nPhotometric == PHOTOMETRIC_MINISWHITE ) + { + GDALColorEntry oEntry; + int iColor, nColorCount; + + poColorTable = new GDALColorTable(); + nColorCount = 1 << nBitsPerSample; + + for ( iColor = 0; iColor < nColorCount; iColor++ ) + { + oEntry.c1 = oEntry.c2 = oEntry.c3 = (short) + ((255 * (nColorCount - 1 - iColor)) / (nColorCount-1)); + oEntry.c4 = 255; + poColorTable->SetColorEntry( iColor, &oEntry ); + } + + nPhotometric = PHOTOMETRIC_PALETTE; + } + else + poColorTable = NULL; + } + else + { + int nColorCount, nMaxColor = 0; + GDALColorEntry oEntry; + + poColorTable = new GDALColorTable(); + + nColorCount = 1 << nBitsPerSample; + + for( int iColor = nColorCount - 1; iColor >= 0; iColor-- ) + { + oEntry.c1 = panRed[iColor] / 256; + oEntry.c2 = panGreen[iColor] / 256; + oEntry.c3 = panBlue[iColor] / 256; + oEntry.c4 = (bNoDataSet && (int)dfNoDataValue == iColor) ? 0 : 255; + + poColorTable->SetColorEntry( iColor, &oEntry ); + + nMaxColor = MAX(nMaxColor,panRed[iColor]); + nMaxColor = MAX(nMaxColor,panGreen[iColor]); + nMaxColor = MAX(nMaxColor,panBlue[iColor]); + } + + // Bug 1384 - Some TIFF files are generated with color map entry + // values in range 0-255 instead of 0-65535 - try to handle these + // gracefully. + if( nMaxColor > 0 && nMaxColor < 256 ) + { + CPLDebug( "GTiff", "TIFF ColorTable seems to be improperly scaled, fixing up." ); + + for( int iColor = nColorCount - 1; iColor >= 0; iColor-- ) + { + oEntry.c1 = panRed[iColor]; + oEntry.c2 = panGreen[iColor]; + oEntry.c3 = panBlue[iColor]; + oEntry.c4 = (bNoDataSet && (int)dfNoDataValue == iColor) ? 0 : 255; + + poColorTable->SetColorEntry( iColor, &oEntry ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < nBands; iBand++ ) + { + if( bTreatAsRGBA ) + SetBand( iBand+1, new GTiffRGBABand( this, iBand+1 ) ); + else if( bTreatAsSplitBitmap ) + SetBand( iBand+1, new GTiffSplitBitmapBand( this, iBand+1 ) ); + else if( bTreatAsSplit ) + SetBand( iBand+1, new GTiffSplitBand( this, iBand+1 ) ); + else if( bTreatAsBitmap ) + SetBand( iBand+1, new GTiffBitmapBand( this, iBand+1 ) ); + else if( bTreatAsOdd ) + SetBand( iBand+1, new GTiffOddBitsBand( this, iBand+1 ) ); + else + SetBand( iBand+1, new GTiffRasterBand( this, iBand+1 ) ); + } + + if( GetRasterBand(1)->GetRasterDataType() == GDT_Unknown ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unsupported TIFF configuration." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Get the transform or gcps from the GeoTIFF file. */ +/* -------------------------------------------------------------------- */ + if( bReadGeoTransform ) + { + char *pszTabWKT = NULL; + double *padfTiePoints, *padfScale, *padfMatrix; + uint16 nCount; + bool bPixelIsPoint = false; + short nRasterType; + GTIF *psGTIF; + int bPointGeoIgnore = FALSE; + + psGTIF = GTIFNew( hTIFF ); // I wonder how expensive this is? + + if( psGTIF ) + { + if( GDALGTIFKeyGetSHORT(psGTIF, GTRasterTypeGeoKey, &nRasterType, + 0, 1 ) == 1 + && nRasterType == (short) RasterPixelIsPoint ) + { + bPixelIsPoint = true; + bPointGeoIgnore = + CSLTestBoolean( CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", + "FALSE") ); + } + + GTIFFree( psGTIF ); + } + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + if( TIFFGetField(hTIFF,TIFFTAG_GEOPIXELSCALE,&nCount,&padfScale ) + && nCount >= 2 + && padfScale[0] != 0.0 && padfScale[1] != 0.0 ) + { + adfGeoTransform[1] = padfScale[0]; + adfGeoTransform[5] = - ABS(padfScale[1]); + + if( TIFFGetField(hTIFF,TIFFTAG_GEOTIEPOINTS,&nCount,&padfTiePoints ) + && nCount >= 6 ) + { + adfGeoTransform[0] = + padfTiePoints[3] - padfTiePoints[0] * adfGeoTransform[1]; + adfGeoTransform[3] = + padfTiePoints[4] - padfTiePoints[1] * adfGeoTransform[5]; + + if( bPixelIsPoint && !bPointGeoIgnore ) + { + adfGeoTransform[0] -= (adfGeoTransform[1] * 0.5 + adfGeoTransform[2] * 0.5); + adfGeoTransform[3] -= (adfGeoTransform[4] * 0.5 + adfGeoTransform[5] * 0.5); + } + + bGeoTransformValid = TRUE; + } + } + + else if( TIFFGetField(hTIFF,TIFFTAG_GEOTRANSMATRIX,&nCount,&padfMatrix ) + && nCount == 16 ) + { + adfGeoTransform[0] = padfMatrix[3]; + adfGeoTransform[1] = padfMatrix[0]; + adfGeoTransform[2] = padfMatrix[1]; + adfGeoTransform[3] = padfMatrix[7]; + adfGeoTransform[4] = padfMatrix[4]; + adfGeoTransform[5] = padfMatrix[5]; + + if( bPixelIsPoint && !bPointGeoIgnore ) + { + adfGeoTransform[0] -= (adfGeoTransform[1] * 0.5 + adfGeoTransform[2] * 0.5); + adfGeoTransform[3] -= (adfGeoTransform[4] * 0.5 + adfGeoTransform[5] * 0.5); + } + + bGeoTransformValid = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise try looking for a .tab, .tfw, .tifw or .wld file. */ +/* -------------------------------------------------------------------- */ + else + { + char* pszGeorefFilename = NULL; + + /* Begin with .tab since it can also have projection info */ + int bTabFileOK = + GDALReadTabFile2( osFilename, adfGeoTransform, + &pszTabWKT, &nGCPCount, &pasGCPList, + papszSiblingFiles, &pszGeorefFilename ); + + if( bTabFileOK ) + { + if( nGCPCount == 0 ) + bGeoTransformValid = TRUE; + } + else + { + if( !bGeoTransformValid ) + { + bGeoTransformValid = + GDALReadWorldFile2( osFilename, NULL, adfGeoTransform, + papszSiblingFiles, &pszGeorefFilename); + } + + if( !bGeoTransformValid ) + { + bGeoTransformValid = + GDALReadWorldFile2( osFilename, "wld", adfGeoTransform, + papszSiblingFiles, &pszGeorefFilename); + } + } + + if (pszGeorefFilename) + { + osGeorefFilename = pszGeorefFilename; + CPLFree(pszGeorefFilename); + } + } + +/* -------------------------------------------------------------------- */ +/* Check for GCPs. Note, we will allow there to be GCPs and a */ +/* transform in some circumstances. */ +/* -------------------------------------------------------------------- */ + if( TIFFGetField(hTIFF,TIFFTAG_GEOTIEPOINTS,&nCount,&padfTiePoints ) + && !bGeoTransformValid ) + { + nGCPCount = nCount / 6; + pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPCount); + + for( int iGCP = 0; iGCP < nGCPCount; iGCP++ ) + { + char szID[32]; + + sprintf( szID, "%d", iGCP+1 ); + pasGCPList[iGCP].pszId = CPLStrdup( szID ); + pasGCPList[iGCP].pszInfo = CPLStrdup(""); + pasGCPList[iGCP].dfGCPPixel = padfTiePoints[iGCP*6+0]; + pasGCPList[iGCP].dfGCPLine = padfTiePoints[iGCP*6+1]; + pasGCPList[iGCP].dfGCPX = padfTiePoints[iGCP*6+3]; + pasGCPList[iGCP].dfGCPY = padfTiePoints[iGCP*6+4]; + pasGCPList[iGCP].dfGCPZ = padfTiePoints[iGCP*6+5]; + + if( bPixelIsPoint && !bPointGeoIgnore ) + { + pasGCPList[iGCP].dfGCPPixel -= 0.5; + pasGCPList[iGCP].dfGCPLine -= 0.5; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Did we find a tab file? If so we will use it's coordinate */ +/* system and give it precidence. */ +/* -------------------------------------------------------------------- */ + if( pszTabWKT != NULL + && (pszProjection == NULL || pszProjection[0] == '\0') ) + { + CPLFree( pszProjection ); + pszProjection = pszTabWKT; + pszTabWKT = NULL; + bLookedForProjection = TRUE; + } + + CPLFree( pszTabWKT ); + bGeoTIFFInfoChanged = FALSE; + bForceUnsetGTOrGCPs = FALSE; + bForceUnsetProjection = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Capture some other potentially interesting information. */ +/* -------------------------------------------------------------------- */ + char szWorkMDI[200]; + uint16 nShort; + + size_t iTag; + for(iTag=0;iTagGetRasterDataType() == GDT_Byte && nBitsPerSample != 8 ) || + (GetRasterBand(1)->GetRasterDataType() == GDT_UInt16 && nBitsPerSample != 16) || + (GetRasterBand(1)->GetRasterDataType() == GDT_UInt32 && nBitsPerSample != 32) ) + { + for (int i = 0; i < nBands; ++i) + GetRasterBand(i+1)->SetMetadataItem( "NBITS", + CPLString().Printf( "%d", (int)nBitsPerSample ), + "IMAGE_STRUCTURE" ); + } + + if( bMinIsWhite ) + SetMetadataItem( "MINISWHITE", "YES", "IMAGE_STRUCTURE" ); + + if( TIFFGetField( hTIFF, TIFFTAG_GDAL_METADATA, &pszText ) ) + { + CPLXMLNode *psRoot = CPLParseXMLString( pszText ); + CPLXMLNode *psItem = NULL; + + if( psRoot != NULL && psRoot->eType == CXT_Element + && EQUAL(psRoot->pszValue,"GDALMetadata") ) + psItem = psRoot->psChild; + + for( ; psItem != NULL; psItem = psItem->psNext ) + { + const char *pszKey, *pszValue, *pszRole, *pszDomain; + char *pszUnescapedValue; + int nBand, bIsXML = FALSE; + + if( psItem->eType != CXT_Element + || !EQUAL(psItem->pszValue,"Item") ) + continue; + + pszKey = CPLGetXMLValue( psItem, "name", NULL ); + pszValue = CPLGetXMLValue( psItem, NULL, NULL ); + nBand = atoi(CPLGetXMLValue( psItem, "sample", "-1" )) + 1; + pszRole = CPLGetXMLValue( psItem, "role", "" ); + pszDomain = CPLGetXMLValue( psItem, "domain", "" ); + + if( pszKey == NULL || pszValue == NULL ) + continue; + + if( EQUALN(pszDomain,"xml:",4) ) + bIsXML = TRUE; + + pszUnescapedValue = CPLUnescapeString( pszValue, NULL, + CPLES_XML ); + if( nBand == 0 ) + { + if( bIsXML ) + { + char *apszMD[2] = { pszUnescapedValue, NULL }; + SetMetadata( apszMD, pszDomain ); + } + else + SetMetadataItem( pszKey, pszUnescapedValue, pszDomain ); + } + else + { + GDALRasterBand *poBand = GetRasterBand(nBand); + if( poBand != NULL ) + { + if( EQUAL(pszRole,"scale") ) + poBand->SetScale( CPLAtofM(pszUnescapedValue) ); + else if( EQUAL(pszRole,"offset") ) + poBand->SetOffset( CPLAtofM(pszUnescapedValue) ); + else if( EQUAL(pszRole,"unittype") ) + poBand->SetUnitType( pszUnescapedValue ); + else if( EQUAL(pszRole,"description") ) + poBand->SetDescription( pszUnescapedValue ); + else + { + if( bIsXML ) + { + char *apszMD[2] = { pszUnescapedValue, NULL }; + poBand->SetMetadata( apszMD, pszDomain ); + } + else + poBand->SetMetadataItem(pszKey,pszUnescapedValue, + pszDomain ); + } + } + } + CPLFree( pszUnescapedValue ); + } + + CPLDestroyXMLNode( psRoot ); + } + + if( bStreamingIn ) + { + toff_t* panOffsets = NULL; + TIFFGetField( hTIFF, (TIFFIsTiled( hTIFF )) ? TIFFTAG_TILEOFFSETS : TIFFTAG_STRIPOFFSETS , &panOffsets ); + if( panOffsets ) + { + int nBlockCount = ( TIFFIsTiled(hTIFF) ) ? TIFFNumberOfTiles(hTIFF) : TIFFNumberOfStrips(hTIFF); + for(int i=1;iOpenOffset( hTIFF, ppoActiveDSRef, nThisDir, FALSE, + eAccess ) != CE_None + || poODS->GetRasterCount() != GetRasterCount() ) + { + delete poODS; + } + else + { + CPLDebug( "GTiff", "Opened %dx%d overview.\n", + poODS->GetRasterXSize(), poODS->GetRasterYSize()); + nOverviewCount++; + papoOverviewDS = (GTiffDataset **) + CPLRealloc(papoOverviewDS, + nOverviewCount * (sizeof(void*))); + papoOverviewDS[nOverviewCount-1] = poODS; + poODS->poBaseDS = this; + } + } + + /* Embedded mask of the main image */ + else if ((nSubType & FILETYPE_MASK) != 0 && + (nSubType & FILETYPE_REDUCEDIMAGE) == 0 && + poMaskDS == NULL ) + { + poMaskDS = new GTiffDataset(); + + /* The TIFF6 specification - page 37 - only allows 1 SamplesPerPixel and 1 BitsPerSample + Here we support either 1 or 8 bit per sample + and we support either 1 sample per pixel or as many samples as in the main image + We don't check the value of the PhotometricInterpretation tag, which should be + set to "Transparency mask" (4) according to the specification (page 36) + ... But the TIFF6 specification allows image masks to have a higher resolution than + the main image, what we don't support here + */ + + if( poMaskDS->OpenOffset( hTIFF, ppoActiveDSRef, nThisDir, + FALSE, eAccess ) != CE_None + || poMaskDS->GetRasterCount() == 0 + || !(poMaskDS->GetRasterCount() == 1 || poMaskDS->GetRasterCount() == GetRasterCount()) + || poMaskDS->GetRasterXSize() != GetRasterXSize() + || poMaskDS->GetRasterYSize() != GetRasterYSize() + || poMaskDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte) + { + delete poMaskDS; + poMaskDS = NULL; + } + else + { + CPLDebug( "GTiff", "Opened band mask.\n"); + poMaskDS->poBaseDS = this; + + poMaskDS->bPromoteTo8Bits = CSLTestBoolean(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES")); + } + } + + /* Embedded mask of an overview */ + /* The TIFF6 specification allows the combination of the FILETYPE_xxxx masks */ + else if ((nSubType & FILETYPE_REDUCEDIMAGE) != 0 && + (nSubType & FILETYPE_MASK) != 0) + { + GTiffDataset* poDS = new GTiffDataset(); + if( poDS->OpenOffset( hTIFF, ppoActiveDSRef, nThisDir, FALSE, + eAccess ) != CE_None + || poDS->GetRasterCount() == 0 + || poDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte) + { + delete poDS; + } + else + { + int i; + for(i=0;ipoMaskDS == NULL && + poDS->GetRasterXSize() == papoOverviewDS[i]->GetRasterXSize() && + poDS->GetRasterYSize() == papoOverviewDS[i]->GetRasterYSize() && + (poDS->GetRasterCount() == 1 || poDS->GetRasterCount() == GetRasterCount())) + { + CPLDebug( "GTiff", "Opened band mask for %dx%d overview.\n", + poDS->GetRasterXSize(), poDS->GetRasterYSize()); + ((GTiffDataset*)papoOverviewDS[i])->poMaskDS = poDS; + poDS->bPromoteTo8Bits = CSLTestBoolean(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES")); + poDS->poBaseDS = this; + break; + } + } + if (i == nOverviewCount) + { + delete poDS; + } + } + } + else if( nSubType == 0 || nSubType == FILETYPE_PAGE ) { + CPLString osName, osDesc; + uint32 nXSize, nYSize; + uint16 nSPP; + + TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &nXSize ); + TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &nYSize ); + if( !TIFFGetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, &nSPP ) ) + nSPP = 1; + + osName.Printf( "SUBDATASET_%d_NAME=GTIFF_DIR:%d:%s", + iDirIndex, iDirIndex, osFilename.c_str() ); + osDesc.Printf( "SUBDATASET_%d_DESC=Page %d (%dP x %dL x %dB)", + iDirIndex, iDirIndex, + (int)nXSize, (int)nYSize, nSPP ); + + aosSubdatasets.AddString(osName); + aosSubdatasets.AddString(osDesc); + } + + // Make sure we are stepping from the expected directory regardless + // of churn done processing the above. + if( TIFFCurrentDirOffset(hTIFF) != nThisDir ) + TIFFSetSubDirectory( hTIFF, nThisDir ); + *ppoActiveDSRef = NULL; + } + + /* If we have a mask for the main image, loop over the overviews, and if they */ + /* have a mask, let's set this mask as an overview of the main mask... */ + if (poMaskDS != NULL) + { + int i; + for(i=0;ipoMaskDS != NULL) + { + poMaskDS->nOverviewCount++; + poMaskDS->papoOverviewDS = (GTiffDataset **) + CPLRealloc(poMaskDS->papoOverviewDS, + poMaskDS->nOverviewCount * (sizeof(void*))); + poMaskDS->papoOverviewDS[poMaskDS->nOverviewCount-1] = + ((GTiffDataset*)papoOverviewDS[i])->poMaskDS; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Only keep track of subdatasets if we have more than one */ +/* subdataset (pair). */ +/* -------------------------------------------------------------------- */ + if( aosSubdatasets.size() > 2 ) + { + oGTiffMDMD.SetMetadata( aosSubdatasets.StealList(), "SUBDATASETS" ); + } +} + + +static int GTiffGetLZMAPreset(char** papszOptions) +{ + int nLZMAPreset = -1; + const char* pszValue = CSLFetchNameValue( papszOptions, "LZMA_PRESET" ); + if( pszValue != NULL ) + { + nLZMAPreset = atoi( pszValue ); + if (!(nLZMAPreset >= 0 && nLZMAPreset <= 9)) + { + CPLError( CE_Warning, CPLE_IllegalArg, + "LZMA_PRESET=%s value not recognised, ignoring.", + pszValue ); + nLZMAPreset = -1; + } + } + return nLZMAPreset; +} + + +static int GTiffGetZLevel(char** papszOptions) +{ + int nZLevel = -1; + const char* pszValue = CSLFetchNameValue( papszOptions, "ZLEVEL" ); + if( pszValue != NULL ) + { + nZLevel = atoi( pszValue ); + if (!(nZLevel >= 1 && nZLevel <= 9)) + { + CPLError( CE_Warning, CPLE_IllegalArg, + "ZLEVEL=%s value not recognised, ignoring.", + pszValue ); + nZLevel = -1; + } + } + return nZLevel; +} + +static int GTiffGetJpegQuality(char** papszOptions) +{ + int nJpegQuality = -1; + const char* pszValue = CSLFetchNameValue( papszOptions, "JPEG_QUALITY" ); + if( pszValue != NULL ) + { + nJpegQuality = atoi( pszValue ); + if (!(nJpegQuality >= 1 && nJpegQuality <= 100)) + { + CPLError( CE_Warning, CPLE_IllegalArg, + "JPEG_QUALITY=%s value not recognised, ignoring.", + pszValue ); + nJpegQuality = -1; + } + } + return nJpegQuality; +} + +static int GTiffGetJpegTablesMode(char** papszOptions) +{ + return atoi(CSLFetchNameValueDef( papszOptions, "JPEGTABLESMODE", + "1" /* JPEGTABLESMODE_QUANT */)); +} + +/************************************************************************/ +/* GetDiscardLsbOption() */ +/************************************************************************/ + +void GTiffDataset::GetDiscardLsbOption(char** papszOptions) +{ + const char* pszBits = CSLFetchNameValue( papszOptions, "DISCARD_LSB" ); + if( pszBits == NULL) + return; + + if( nPhotometric == PHOTOMETRIC_PALETTE ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "DISCARD_LSB ignored on a paletted image"); + return; + } + if( !(nBitsPerSample == 8 || nBitsPerSample == 16 || nBitsPerSample == 32) ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "DISCARD_LSB ignored on non 8, 16 or 32 bits integer images"); + return; + } + + char** papszTokens = CSLTokenizeString2( pszBits, ",", 0 ); + if( CSLCount(papszTokens) == 1 ) + { + bHasDiscardedLsb = TRUE; + for(int i=0;i 1 ) + anOffsetLsb.push_back(1 << (nBits-1)); + else + anOffsetLsb.push_back(0); + } + } + else if( CSLCount(papszTokens) == nBands ) + { + bHasDiscardedLsb = TRUE; + for(int i=0;i 1 ) + anOffsetLsb.push_back(1 << (nBits-1)); + else + anOffsetLsb.push_back(0); + } + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "DISCARD_LSB ignored: wrong number of components"); + } + CSLDestroy(papszTokens); +} + +/************************************************************************/ +/* GTiffCreate() */ +/* */ +/* Shared functionality between GTiffDataset::Create() and */ +/* GTiffCreateCopy() for creating TIFF file based on a set of */ +/* options and a configuration. */ +/************************************************************************/ + +TIFF *GTiffDataset::CreateLL( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + double dfExtraSpaceForOverviews, + char **papszParmList, + VSILFILE** pfpL, + CPLString& osTmpFilename ) + +{ + TIFF *hTIFF; + int nBlockXSize = 0, nBlockYSize = 0; + int bTiled = FALSE; + int nCompression = COMPRESSION_NONE; + int nPredictor = PREDICTOR_NONE, nJpegQuality = -1, nZLevel = -1, + nLZMAPreset = -1, nJpegTablesMode = -1; + uint16 nSampleFormat; + int nPlanar; + const char *pszValue; + const char *pszProfile; + int bCreateBigTIFF = FALSE; + + if (!GTiffOneTimeInit()) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Blow on a few errors. */ +/* -------------------------------------------------------------------- */ + if( nXSize < 1 || nYSize < 1 || nBands < 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create %dx%dx%d TIFF file, but width, height and bands\n" + "must be positive.", + nXSize, nYSize, nBands ); + + return NULL; + } + + if (nBands > 65535) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create %dx%dx%d TIFF file, but bands\n" + "must be lesser or equal to 65535.", + nXSize, nYSize, nBands ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Setup values based on options. */ +/* -------------------------------------------------------------------- */ + pszProfile = CSLFetchNameValue(papszParmList,"PROFILE"); + if( pszProfile == NULL ) + pszProfile = "GDALGeoTIFF"; + + if( CSLFetchBoolean( papszParmList, "TILED", FALSE ) ) + bTiled = TRUE; + + pszValue = CSLFetchNameValue(papszParmList,"BLOCKXSIZE"); + if( pszValue != NULL ) + nBlockXSize = atoi( pszValue ); + + pszValue = CSLFetchNameValue(papszParmList,"BLOCKYSIZE"); + if( pszValue != NULL ) + nBlockYSize = atoi( pszValue ); + + pszValue = CSLFetchNameValue(papszParmList,"INTERLEAVE"); + if( pszValue != NULL ) + { + if( EQUAL( pszValue, "PIXEL" ) ) + nPlanar = PLANARCONFIG_CONTIG; + else if( EQUAL( pszValue, "BAND" ) ) + nPlanar = PLANARCONFIG_SEPARATE; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "INTERLEAVE=%s unsupported, value must be PIXEL or BAND.", + pszValue ); + return NULL; + } + } + else + { + nPlanar = PLANARCONFIG_CONTIG; + } + + pszValue = CSLFetchNameValue( papszParmList, "COMPRESS" ); + if( pszValue != NULL ) + { + nCompression = GTIFFGetCompressionMethod(pszValue, "COMPRESS"); + if (nCompression < 0) + return NULL; + } + + pszValue = CSLFetchNameValue( papszParmList, "PREDICTOR" ); + if( pszValue != NULL ) + nPredictor = atoi( pszValue ); + + nZLevel = GTiffGetZLevel(papszParmList); + nLZMAPreset = GTiffGetLZMAPreset(papszParmList); + nJpegQuality = GTiffGetJpegQuality(papszParmList); + nJpegTablesMode = GTiffGetJpegTablesMode(papszParmList); + +/* -------------------------------------------------------------------- */ +/* Streaming related code */ +/* -------------------------------------------------------------------- */ + int bStreaming = ( strcmp(pszFilename, "/vsistdout/") == 0 || + CSLFetchBoolean(papszParmList, "STREAMABLE_OUTPUT", FALSE) ); +#ifdef S_ISFIFO + if( !bStreaming ) + { + VSIStatBufL sStat; + if( VSIStatExL(pszFilename, &sStat, VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 && + S_ISFIFO(sStat.st_mode) ) + { + bStreaming = TRUE; + } + } +#endif + if( bStreaming && + !EQUAL("NONE", CSLFetchNameValueDef(papszParmList, "COMPRESS", "NONE")) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Streaming only supported to uncompressed TIFF"); + return NULL; + } + if( bStreaming && + CSLFetchBoolean(papszParmList, "SPARSE_OK", FALSE) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Streaming not supported with SPARSE_OK"); + return NULL; + } + if( bStreaming && + CSLFetchBoolean(papszParmList, "COPY_SRC_OVERVIEWS", FALSE) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Streaming not supported with COPY_SRC_OVERVIEWS"); + return NULL; + } + if( bStreaming ) + { + static int nCounter = 0; + osTmpFilename = CPLSPrintf("/vsimem/vsistdout_%d.tif", ++nCounter); + pszFilename = osTmpFilename.c_str(); + } + +/* -------------------------------------------------------------------- */ +/* Compute the uncompressed size. */ +/* -------------------------------------------------------------------- */ + double dfUncompressedImageSize; + + dfUncompressedImageSize = + nXSize * ((double)nYSize) * nBands * (GDALGetDataTypeSize(eType)/8); + dfUncompressedImageSize += dfExtraSpaceForOverviews; + + if( nCompression == COMPRESSION_NONE + && dfUncompressedImageSize > 4200000000.0 ) + { +#ifndef BIGTIFF_SUPPORT + CPLError( CE_Failure, CPLE_NotSupported, + "A %d pixels x %d lines x %d bands %s image would be larger than 4GB\n" + "but this is the largest size a TIFF can be, and BigTIFF is unavailable.\n" + "Creation failed.", + nXSize, nYSize, nBands, GDALGetDataTypeName(eType) ); + return NULL; +#endif + } + +/* -------------------------------------------------------------------- */ +/* Should the file be created as a bigtiff file? */ +/* -------------------------------------------------------------------- */ + const char *pszBIGTIFF = CSLFetchNameValue(papszParmList, "BIGTIFF"); + + if( pszBIGTIFF == NULL ) + pszBIGTIFF = "IF_NEEDED"; + + if( EQUAL(pszBIGTIFF,"IF_NEEDED") ) + { + if( nCompression == COMPRESSION_NONE + && dfUncompressedImageSize > 4200000000.0 ) + bCreateBigTIFF = TRUE; + } + else if( EQUAL(pszBIGTIFF,"IF_SAFER") ) + { + if( dfUncompressedImageSize > 2000000000.0 ) + bCreateBigTIFF = TRUE; + } + + else + { + bCreateBigTIFF = CSLTestBoolean( pszBIGTIFF ); + if (!bCreateBigTIFF && nCompression == COMPRESSION_NONE && + dfUncompressedImageSize > 4200000000.0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The TIFF file will be larger than 4GB, so BigTIFF is necessary.\n" + "Creation failed."); + return NULL; + } + } + +#ifndef BIGTIFF_SUPPORT + if( bCreateBigTIFF ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "BigTIFF requested, but GDAL built without BigTIFF\n" + "enabled libtiff, request ignored." ); + bCreateBigTIFF = FALSE; + } +#endif + + if( bCreateBigTIFF ) + CPLDebug( "GTiff", "File being created as a BigTIFF." ); + +/* -------------------------------------------------------------------- */ +/* Check if the user wishes a particular endianness */ +/* -------------------------------------------------------------------- */ + + int eEndianness = ENDIANNESS_NATIVE; + pszValue = CSLFetchNameValue(papszParmList, "ENDIANNESS"); + if ( pszValue == NULL ) + pszValue = CPLGetConfigOption( "GDAL_TIFF_ENDIANNESS", NULL ); + if ( pszValue != NULL ) + { + if (EQUAL(pszValue, "LITTLE")) + eEndianness = ENDIANNESS_LITTLE; + else if (EQUAL(pszValue, "BIG")) + eEndianness = ENDIANNESS_BIG; + else if (EQUAL(pszValue, "INVERTED")) + { +#ifdef CPL_LSB + eEndianness = ENDIANNESS_BIG; +#else + eEndianness = ENDIANNESS_LITTLE; +#endif + } + else if (!EQUAL(pszValue, "NATIVE")) + { + CPLError( CE_Warning, CPLE_NotSupported, + "ENDIANNESS=%s not supported. Defaulting to NATIVE", pszValue ); + } + } + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ + + char szOpeningFlag[5]; + strcpy(szOpeningFlag, "w+"); + if (bCreateBigTIFF) + strcat(szOpeningFlag, "8"); + if (eEndianness == ENDIANNESS_BIG) + strcat(szOpeningFlag, "b"); + else if (eEndianness == ENDIANNESS_LITTLE) + strcat(szOpeningFlag, "l"); + + VSILFILE* fpL = VSIFOpenL( pszFilename, "w+b" ); + if( fpL == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create new tiff file `%s' failed: %s", + pszFilename, VSIStrerror(errno) ); + return NULL; + } + hTIFF = VSI_TIFFOpen( pszFilename, szOpeningFlag, fpL ); + if( hTIFF == NULL ) + { + if( CPLGetLastErrorNo() == 0 ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create new tiff file `%s'\n" + "failed in XTIFFOpen().\n", + pszFilename ); + VSIFCloseL(fpL); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* How many bits per sample? We have a special case if NBITS */ +/* specified for GDT_Byte, GDT_UInt16, GDT_UInt32. */ +/* -------------------------------------------------------------------- */ + int nBitsPerSample = GDALGetDataTypeSize(eType); + if (CSLFetchNameValue(papszParmList, "NBITS") != NULL) + { + int nMinBits = 0, nMaxBits = 0; + nBitsPerSample = atoi(CSLFetchNameValue(papszParmList, "NBITS")); + if( eType == GDT_Byte ) + { + nMinBits = 1; + nMaxBits = 8; + } + else if( eType == GDT_UInt16 ) + { + nMinBits = 9; + nMaxBits = 16; + } + else if( eType == GDT_UInt32 ) + { + nMinBits = 17; + nMaxBits = 32; + } + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "NBITS is not supported for data type %s", + GDALGetDataTypeName(eType)); + nBitsPerSample = GDALGetDataTypeSize(eType); + } + + if (nMinBits != 0) + { + if (nBitsPerSample < nMinBits) + { + CPLError(CE_Warning, CPLE_AppDefined, + "NBITS=%d is invalid for data type %s. Using NBITS=%d", + nBitsPerSample, GDALGetDataTypeName(eType), nMinBits); + nBitsPerSample = nMinBits; + } + else if (nBitsPerSample > nMaxBits) + { + CPLError(CE_Warning, CPLE_AppDefined, + "NBITS=%d is invalid for data type %s. Using NBITS=%d", + nBitsPerSample, GDALGetDataTypeName(eType), nMaxBits); + nBitsPerSample = nMaxBits; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have a custom pixel type (just used for signed byte now). */ +/* -------------------------------------------------------------------- */ + const char *pszPixelType = CSLFetchNameValue( papszParmList, "PIXELTYPE" ); + if( pszPixelType == NULL ) + pszPixelType = ""; + +/* -------------------------------------------------------------------- */ +/* Setup some standard flags. */ +/* -------------------------------------------------------------------- */ + TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, nXSize ); + TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, nYSize ); + TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, nBitsPerSample ); + + if( (eType == GDT_Byte && EQUAL(pszPixelType,"SIGNEDBYTE")) + || eType == GDT_Int16 || eType == GDT_Int32 ) + nSampleFormat = SAMPLEFORMAT_INT; + else if( eType == GDT_CInt16 || eType == GDT_CInt32 ) + nSampleFormat = SAMPLEFORMAT_COMPLEXINT; + else if( eType == GDT_Float32 || eType == GDT_Float64 ) + nSampleFormat = SAMPLEFORMAT_IEEEFP; + else if( eType == GDT_CFloat32 || eType == GDT_CFloat64 ) + nSampleFormat = SAMPLEFORMAT_COMPLEXIEEEFP; + else + nSampleFormat = SAMPLEFORMAT_UINT; + + TIFFSetField( hTIFF, TIFFTAG_SAMPLEFORMAT, nSampleFormat ); + TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, nBands ); + TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, nPlanar ); + +/* -------------------------------------------------------------------- */ +/* Setup Photometric Interpretation. Take this value from the user */ +/* passed option or guess correct value otherwise. */ +/* -------------------------------------------------------------------- */ + int nSamplesAccountedFor = 1; + int bForceColorTable = FALSE; + + pszValue = CSLFetchNameValue(papszParmList,"PHOTOMETRIC"); + if( pszValue != NULL ) + { + if( EQUAL( pszValue, "MINISBLACK" ) ) + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK ); + else if( EQUAL( pszValue, "MINISWHITE" ) ) + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE ); + else if( EQUAL( pszValue, "PALETTE" )) + { + if( eType == GDT_Byte || eType == GDT_UInt16 ) + { + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE ); + nSamplesAccountedFor = 1; + bForceColorTable = TRUE; + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "PHOTOMETRIC=PALETTE only compatible with Byte or UInt16"); + } + } + else if( EQUAL( pszValue, "RGB" )) + { + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB ); + nSamplesAccountedFor = 3; + } + else if( EQUAL( pszValue, "CMYK" )) + { + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED ); + nSamplesAccountedFor = 4; + } + else if( EQUAL( pszValue, "YCBCR" )) + { + /* Because of subsampling, setting YCBCR without JPEG compression leads */ + /* to a crash currently. Would need to make GTiffRasterBand::IWriteBlock() */ + /* aware of subsampling so that it doesn't overrun buffer size returned */ + /* by libtiff */ + if ( nCompression != COMPRESSION_JPEG ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Currently, PHOTOMETRIC=YCBCR requires COMPRESS=JPEG"); + XTIFFClose(hTIFF); + VSIFCloseL(fpL); + return NULL; + } + + if ( nPlanar == PLANARCONFIG_SEPARATE ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "PHOTOMETRIC=YCBCR requires INTERLEAVE=PIXEL"); + XTIFFClose(hTIFF); + VSIFCloseL(fpL); + return NULL; + } + + /* YCBCR strictly requires 3 bands. Not less, not more */ + /* Issue an explicit error message as libtiff one is a bit cryptic : */ + /* TIFFVStripSize64:Invalid td_samplesperpixel value */ + if ( nBands != 3 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "PHOTOMETRIC=YCBCR requires a source raster with only 3 bands (RGB)"); + XTIFFClose(hTIFF); + VSIFCloseL(fpL); + return NULL; + } + + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR ); + nSamplesAccountedFor = 3; + } + else if( EQUAL( pszValue, "CIELAB" )) + { + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB ); + nSamplesAccountedFor = 3; + } + else if( EQUAL( pszValue, "ICCLAB" )) + { + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ICCLAB ); + nSamplesAccountedFor = 3; + } + else if( EQUAL( pszValue, "ITULAB" )) + { + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_ITULAB ); + nSamplesAccountedFor = 3; + } + else + { + CPLError( CE_Warning, CPLE_IllegalArg, + "PHOTOMETRIC=%s value not recognised, ignoring.\n" + "Set the Photometric Interpretation as MINISBLACK.", + pszValue ); + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK ); + } + + if ( nBands < nSamplesAccountedFor ) + { + CPLError( CE_Warning, CPLE_IllegalArg, + "PHOTOMETRIC=%s value does not correspond to number " + "of bands (%d), ignoring.\n" + "Set the Photometric Interpretation as MINISBLACK.", + pszValue, nBands ); + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK ); + } + } + else + { + /* + * If image contains 3 or 4 bands and datatype is Byte then we will + * assume it is RGB. In all other cases assume it is MINISBLACK. + */ + if( nBands == 3 && eType == GDT_Byte ) + { + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB ); + nSamplesAccountedFor = 3; + } + else if( nBands == 4 && eType == GDT_Byte ) + { + uint16 v[1]; + + v[0] = GTiffGetAlphaValue(CSLFetchNameValue(papszParmList,"ALPHA"), + DEFAULT_ALPHA_TYPE); + + TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, 1, v); + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB ); + nSamplesAccountedFor = 4; + } + else + { + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK ); + nSamplesAccountedFor = 1; + } + } + +/* -------------------------------------------------------------------- */ +/* If there are extra samples, we need to mark them with an */ +/* appropriate extrasamples definition here. */ +/* -------------------------------------------------------------------- */ + if( nBands > nSamplesAccountedFor ) + { + uint16 *v; + int i; + int nExtraSamples = nBands - nSamplesAccountedFor; + + v = (uint16 *) CPLMalloc( sizeof(uint16) * nExtraSamples ); + + v[0] = GTiffGetAlphaValue(CSLFetchNameValue(papszParmList, "ALPHA"), + EXTRASAMPLE_UNSPECIFIED); + + for( i = 1; i < nExtraSamples; i++ ) + v[i] = EXTRASAMPLE_UNSPECIFIED; + + TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples, v ); + + CPLFree(v); + } + + /* Set the ICC color profile. */ + if (!EQUAL(pszProfile,"BASELINE")) + { + SaveICCProfile(NULL, hTIFF, papszParmList, nBitsPerSample); + } + + /* Set the compression method before asking the default strip size */ + /* This is useful when translating to a JPEG-In-TIFF file where */ + /* the default strip size is 8 or 16 depending on the photometric value */ + TIFFSetField( hTIFF, TIFFTAG_COMPRESSION, nCompression ); + +/* -------------------------------------------------------------------- */ +/* Setup tiling/stripping flags. */ +/* -------------------------------------------------------------------- */ + if( bTiled ) + { + if( nBlockXSize == 0 ) + nBlockXSize = 256; + + if( nBlockYSize == 0 ) + nBlockYSize = 256; + + if (!TIFFSetField( hTIFF, TIFFTAG_TILEWIDTH, nBlockXSize ) || + !TIFFSetField( hTIFF, TIFFTAG_TILELENGTH, nBlockYSize )) + { + XTIFFClose(hTIFF); + VSIFCloseL(fpL); + return NULL; + } + } + else + { + uint32 nRowsPerStrip; + + if( nBlockYSize == 0 ) + nRowsPerStrip = MIN(nYSize, (int)TIFFDefaultStripSize(hTIFF,0)); + else + nRowsPerStrip = nBlockYSize; + + TIFFSetField( hTIFF, TIFFTAG_ROWSPERSTRIP, nRowsPerStrip ); + } + +/* -------------------------------------------------------------------- */ +/* Set compression related tags. */ +/* -------------------------------------------------------------------- */ + if ( nCompression == COMPRESSION_LZW || + nCompression == COMPRESSION_ADOBE_DEFLATE ) + TIFFSetField( hTIFF, TIFFTAG_PREDICTOR, nPredictor ); + if (nCompression == COMPRESSION_ADOBE_DEFLATE + && nZLevel != -1) + TIFFSetField( hTIFF, TIFFTAG_ZIPQUALITY, nZLevel ); + else if( nCompression == COMPRESSION_JPEG + && nJpegQuality != -1 ) + TIFFSetField( hTIFF, TIFFTAG_JPEGQUALITY, nJpegQuality ); + else if( nCompression == COMPRESSION_LZMA && nLZMAPreset != -1) + TIFFSetField( hTIFF, TIFFTAG_LZMAPRESET, nLZMAPreset ); + + if( nCompression == COMPRESSION_JPEG ) + TIFFSetField( hTIFF, TIFFTAG_JPEGTABLESMODE, nJpegTablesMode ); + +/* -------------------------------------------------------------------- */ +/* If we forced production of a file with photometric=palette, */ +/* we need to push out a default color table. */ +/* -------------------------------------------------------------------- */ + if( bForceColorTable ) + { + int nColors; + + if( eType == GDT_Byte ) + nColors = 256; + else + nColors = 65536; + + unsigned short *panTRed, *panTGreen, *panTBlue; + + panTRed = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors); + panTGreen = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors); + panTBlue = (unsigned short *) CPLMalloc(sizeof(unsigned short)*nColors); + + for( int iColor = 0; iColor < nColors; iColor++ ) + { + if( eType == GDT_Byte ) + { + panTRed[iColor] = (unsigned short) (257 * iColor); + panTGreen[iColor] = (unsigned short) (257 * iColor); + panTBlue[iColor] = (unsigned short) (257 * iColor); + } + else + { + panTRed[iColor] = (unsigned short) iColor; + panTGreen[iColor] = (unsigned short) iColor; + panTBlue[iColor] = (unsigned short) iColor; + } + } + + TIFFSetField( hTIFF, TIFFTAG_COLORMAP, + panTRed, panTGreen, panTBlue ); + + CPLFree( panTRed ); + CPLFree( panTGreen ); + CPLFree( panTBlue ); + } + + /* Would perhaps works with libtiff 3.X but didn't bother trying */ + /* This trick creates a temporary in-memory file and fetches its JPEG tables */ + /* so that we can directly set them, before tif_jpeg.c compute them at */ + /* the first strip/tile writing, which is too late, since we have already */ + /* crystalized the directory. This way we avoid a directory rewriting */ +#if defined(BIGTIFF_SUPPORT) + if( nCompression == COMPRESSION_JPEG && + strncmp(pszFilename, "/vsimem/gtiffdataset_jpg_tmp_", + strlen("/vsimem/gtiffdataset_jpg_tmp_")) != 0 && + CSLTestBoolean(CSLFetchNameValueDef(papszParmList, "WRITE_JPEGTABLE_TAG", "YES")) ) + { + CPLString osTmpFilename; + osTmpFilename.Printf("/vsimem/gtiffdataset_jpg_tmp_%p", hTIFF); + VSILFILE* fpTmp = NULL; + CPLString osTmp; + char** papszLocalParameters = NULL; + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "COMPRESS", "JPEG"); + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "JPEG_QUALITY", CSLFetchNameValue(papszParmList, "JPEG_QUALITY")); + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "PHOTOMETRIC", CSLFetchNameValue(papszParmList, "PHOTOMETRIC")); + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "BLOCKYSIZE", "16"); + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "NBITS", CSLFetchNameValue(papszParmList, "NBITS")); + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "JPEGTABLESMODE", CSLFetchNameValue(papszParmList, "JPEGTABLESMODE")); + TIFF* hTIFFTmp = CreateLL( osTmpFilename, 16, 16, (nBands <= 4) ? nBands : 1, + eType, 0.0, papszLocalParameters, &fpTmp, osTmp ); + CSLDestroy(papszLocalParameters); + if( hTIFFTmp ) + { + uint16 nPhotometric; + int nJpegTablesMode; + TIFFGetField( hTIFFTmp, TIFFTAG_PHOTOMETRIC, &(nPhotometric) ); + TIFFGetField( hTIFFTmp, TIFFTAG_JPEGTABLESMODE, &nJpegTablesMode ); + TIFFWriteCheck( hTIFFTmp, FALSE, "CreateLL" ); + TIFFWriteDirectory( hTIFFTmp ); + TIFFSetDirectory( hTIFFTmp, 0 ); + // Now, reset quality and jpegcolormode. + if(nJpegQuality > 0) + TIFFSetField(hTIFFTmp, TIFFTAG_JPEGQUALITY, nJpegQuality); + if( nPhotometric == PHOTOMETRIC_YCBCR + && CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", + "YES") ) ) + { + TIFFSetField(hTIFFTmp, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + } + if (nJpegTablesMode >= 0 ) + TIFFSetField(hTIFFTmp, TIFFTAG_JPEGTABLESMODE, nJpegTablesMode); + + GByte abyZeroData[(16*16*4*3)/2]; + memset(abyZeroData, 0, (16*16*4*3)/2); + int nBlockSize = 16 * 16 * ((nBands <= 4) ? nBands : 1); + if( nBitsPerSample == 12 ) + nBlockSize = (nBlockSize * 3) / 2; + TIFFWriteEncodedStrip( hTIFFTmp, 0, abyZeroData, nBlockSize); + + uint32 nJPEGTableSize = 0; + void* pJPEGTable = NULL; + if( TIFFGetField(hTIFFTmp, TIFFTAG_JPEGTABLES, &nJPEGTableSize, &pJPEGTable) ) + TIFFSetField(hTIFF, TIFFTAG_JPEGTABLES, nJPEGTableSize, pJPEGTable); + + float *ref; + if( TIFFGetField(hTIFFTmp, TIFFTAG_REFERENCEBLACKWHITE, &ref) ) + TIFFSetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE, ref); + + XTIFFClose(hTIFFTmp); + VSIFCloseL(fpTmp); + } + VSIUnlink(osTmpFilename); + } +#endif + + *pfpL = fpL; + + return( hTIFF ); +} + +/************************************************************************/ +/* GuessJPEGQuality() */ +/* */ +/* Guess JPEG quality from JPEGTABLES tag. */ +/************************************************************************/ + +static const GByte* GTIFFFindNextTable(const GByte* paby, GByte byMarker, + int nLen, int* pnLenTable) +{ + for(int i = 0; i+1 < nLen; ) + { + if( paby[i] != 0xFF ) + return NULL; + i ++; + if( paby[i] == 0xD8 ) + { + i ++; + continue; + } + if( i+2 >= nLen ) + return NULL; + int nMarkerLen = paby[i+1] * 256 + paby[i+2]; + if( i+1+nMarkerLen >= nLen ) + return NULL; + if( paby[i] == byMarker ) + { + if( pnLenTable ) *pnLenTable = nMarkerLen; + return paby + i + 1; + } + i += 1 + nMarkerLen; + } + return NULL; +} + +/* We assume that if there are several quantization tables, they are */ +/* in the same order. Which is a reasonable assumption for updating */ +/* a file generated by ourselves */ +static int GTIFFQuantizationTablesEqual(const GByte* paby1, int nLen1, + const GByte* paby2, int nLen2) +{ + int bFound = FALSE; + while(TRUE) + { + int nLenTable1 = 0; + int nLenTable2 = 0; + const GByte* paby1New = GTIFFFindNextTable(paby1, 0xDB, nLen1, &nLenTable1); + const GByte* paby2New = GTIFFFindNextTable(paby2, 0xDB, nLen2, &nLenTable2); + if( paby1New == NULL && paby2New == NULL ) + return bFound; + if( paby1New == NULL && paby2New != NULL ) + return FALSE; + if( paby1New != NULL && paby2New == NULL ) + return FALSE; + if( nLenTable1 != nLenTable2 ) + return FALSE; + if( memcmp(paby1New, paby2New, nLenTable1) != 0 ) + return FALSE; + paby1New += nLenTable1; + paby2New += nLenTable2; + nLen1 -= (paby1New - paby1); + nLen2 -= (paby2New - paby2); + paby1 = paby1New; + paby2 = paby2New; + bFound = TRUE; + } +} + +int GTiffDataset::GuessJPEGQuality(int& bOutHasQuantizationTable, + int& bOutHasHuffmanTable) +{ + CPLAssert( nCompression == COMPRESSION_JPEG ); + uint32 nJPEGTableSize = 0; + void* pJPEGTable = NULL; + bOutHasQuantizationTable = FALSE; + bOutHasHuffmanTable = FALSE; + if( !TIFFGetField(hTIFF, TIFFTAG_JPEGTABLES, &nJPEGTableSize, &pJPEGTable) ) + return -1; + + bOutHasQuantizationTable = GTIFFFindNextTable((const GByte*)pJPEGTable, 0xDB, nJPEGTableSize, NULL) != NULL; + bOutHasHuffmanTable = GTIFFFindNextTable((const GByte*)pJPEGTable, 0xC4, nJPEGTableSize, NULL) != NULL; + if( !bOutHasQuantizationTable ) + return -1; + + char** papszLocalParameters = NULL; + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "COMPRESS", "JPEG"); + if( nPhotometric == PHOTOMETRIC_YCBCR ) + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "PHOTOMETRIC", "YCBCR"); + else if( nPhotometric == PHOTOMETRIC_SEPARATED ) + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "PHOTOMETRIC", "CMYK"); + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "BLOCKYSIZE", "16"); + if( nBitsPerSample == 12 ) + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "NBITS", "12"); + + CPLString osTmpFilename; + osTmpFilename.Printf("/vsimem/gtiffdataset_guess_jpeg_quality_tmp_%p", this); + + int nRet = -1; + for(int nQuality=0;nQuality<=100 && nRet < 0;nQuality++) + { + VSILFILE* fpTmp = NULL; + if( nQuality == 0 ) + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "JPEG_QUALITY", "75"); + else + papszLocalParameters = CSLSetNameValue(papszLocalParameters, + "JPEG_QUALITY", CPLSPrintf("%d", nQuality)); + + CPLPushErrorHandler(CPLQuietErrorHandler); + CPLString osTmp; + TIFF* hTIFFTmp = CreateLL( osTmpFilename, 16, 16, (nBands <= 4) ? nBands : 1, + GetRasterBand(1)->GetRasterDataType(), 0.0, + papszLocalParameters, &fpTmp, osTmp ); + CPLPopErrorHandler(); + if( !hTIFFTmp ) + { + break; + } + + TIFFWriteCheck( hTIFFTmp, FALSE, "CreateLL" ); + TIFFWriteDirectory( hTIFFTmp ); + TIFFSetDirectory( hTIFFTmp, 0 ); + // Now reset jpegcolormode. + if( nPhotometric == PHOTOMETRIC_YCBCR + && CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", + "YES") ) ) + { + TIFFSetField(hTIFFTmp, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + } + + GByte abyZeroData[(16*16*4*3)/2]; + memset(abyZeroData, 0, (16*16*4*3)/2); + int nBlockSize = (16 * 16 * ((nBands <= 4) ? nBands : 1) * nBitsPerSample) / 8; + TIFFWriteEncodedStrip( hTIFFTmp, 0, abyZeroData, nBlockSize); + + uint32 nJPEGTableSizeTry = 0; + void* pJPEGTableTry = NULL; + if( TIFFGetField(hTIFFTmp, TIFFTAG_JPEGTABLES, + &nJPEGTableSizeTry, &pJPEGTableTry) ) + { + if( GTIFFQuantizationTablesEqual((GByte*)pJPEGTable, nJPEGTableSize, + (GByte*)pJPEGTableTry, nJPEGTableSizeTry) ) + { + nRet = (nQuality == 0 ) ? 75 : nQuality; + } + } + + XTIFFClose(hTIFFTmp); + VSIFCloseL(fpTmp); + } + + CSLDestroy(papszLocalParameters); + VSIUnlink(osTmpFilename); + + return nRet; +} + +/************************************************************************/ +/* Create() */ +/* */ +/* Create a new GeoTIFF or TIFF file. */ +/************************************************************************/ + +GDALDataset *GTiffDataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char **papszParmList ) + +{ + GTiffDataset * poDS; + TIFF *hTIFF; + VSILFILE* fpL = NULL; + CPLString osTmpFilename; + +/* -------------------------------------------------------------------- */ +/* Create the underlying TIFF file. */ +/* -------------------------------------------------------------------- */ + hTIFF = CreateLL( pszFilename, + nXSize, nYSize, nBands, + eType, 0, papszParmList, &fpL, osTmpFilename ); + int bStreaming = (osTmpFilename.size() != 0); + + if( hTIFF == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the new GTiffDataset object. */ +/* -------------------------------------------------------------------- */ + poDS = new GTiffDataset(); + poDS->hTIFF = hTIFF; + poDS->fpL = fpL; + if( bStreaming ) + { + poDS->bStreamingOut = TRUE; + poDS->osTmpFilename = osTmpFilename; + poDS->fpToWrite = VSIFOpenL( pszFilename, "wb" ); + if( poDS->fpToWrite == NULL ) + { + VSIUnlink(osTmpFilename); + delete poDS; + return NULL; + } + } + poDS->poActiveDS = poDS; + poDS->ppoActiveDSRef = &(poDS->poActiveDS); + + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->eAccess = GA_Update; + poDS->bCrystalized = FALSE; + poDS->nSamplesPerPixel = (uint16) nBands; + poDS->osFilename = pszFilename; + + /* Avoid premature crystalization that will cause directory re-writting */ + /* if GetProjectionRef() or GetGeoTransform() are called on the newly created GeoTIFF */ + poDS->bLookedForProjection = TRUE; + + TIFFGetField( hTIFF, TIFFTAG_SAMPLEFORMAT, &(poDS->nSampleFormat) ); + TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &(poDS->nPlanarConfig) ); + // Weird that we need this, but otherwise we get a Valgrind warning on tiff_write_124 + if( !TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &(poDS->nPhotometric) ) ) + poDS->nPhotometric = PHOTOMETRIC_MINISBLACK; + TIFFGetField( hTIFF, TIFFTAG_BITSPERSAMPLE, &(poDS->nBitsPerSample) ); + TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &(poDS->nCompression) ); + + if( TIFFIsTiled(hTIFF) ) + { + TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &(poDS->nBlockXSize) ); + TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &(poDS->nBlockYSize) ); + } + else + { + if( !TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP, + &(poDS->nRowsPerStrip) ) ) + poDS->nRowsPerStrip = 1; /* dummy value */ + + poDS->nBlockXSize = nXSize; + poDS->nBlockYSize = MIN((int)poDS->nRowsPerStrip,nYSize); + } + + poDS->nBlocksPerBand = + DIV_ROUND_UP(nYSize, poDS->nBlockYSize) + * DIV_ROUND_UP(nXSize, poDS->nBlockXSize); + + if( CSLFetchNameValue( papszParmList, "PROFILE" ) != NULL ) + poDS->osProfile = CSLFetchNameValue( papszParmList, "PROFILE" ); + +/* -------------------------------------------------------------------- */ +/* YCbCr JPEG compressed images should be translated on the fly */ +/* to RGB by libtiff/libjpeg unless specifically requested */ +/* otherwise. */ +/* -------------------------------------------------------------------- */ + if( poDS->nCompression == COMPRESSION_JPEG + && poDS->nPhotometric == PHOTOMETRIC_YCBCR + && CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", + "YES") ) ) + { + int nColorMode; + + poDS->SetMetadataItem( "SOURCE_COLOR_SPACE", "YCbCr", "IMAGE_STRUCTURE" ); + if ( !TIFFGetField( hTIFF, TIFFTAG_JPEGCOLORMODE, &nColorMode ) || + nColorMode != JPEGCOLORMODE_RGB ) + TIFFSetField(hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + } + +/* -------------------------------------------------------------------- */ +/* Read palette back as a color table if it has one. */ +/* -------------------------------------------------------------------- */ + unsigned short *panRed, *panGreen, *panBlue; + + if( poDS->nPhotometric == PHOTOMETRIC_PALETTE + && TIFFGetField( hTIFF, TIFFTAG_COLORMAP, + &panRed, &panGreen, &panBlue) ) + { + int nColorCount; + GDALColorEntry oEntry; + + poDS->poColorTable = new GDALColorTable(); + + nColorCount = 1 << poDS->nBitsPerSample; + + for( int iColor = nColorCount - 1; iColor >= 0; iColor-- ) + { + oEntry.c1 = panRed[iColor] / 256; + oEntry.c2 = panGreen[iColor] / 256; + oEntry.c3 = panBlue[iColor] / 256; + oEntry.c4 = 255; + + poDS->poColorTable->SetColorEntry( iColor, &oEntry ); + } + } + +/* -------------------------------------------------------------------- */ +/* Do we want to ensure all blocks get written out on close to */ +/* avoid sparse files? */ +/* -------------------------------------------------------------------- */ + if( !CSLFetchBoolean( papszParmList, "SPARSE_OK", FALSE ) ) + poDS->bFillEmptyTiles = TRUE; + +/* -------------------------------------------------------------------- */ +/* Preserve creation options for consulting later (for instance */ +/* to decide if a TFW file should be written). */ +/* -------------------------------------------------------------------- */ + poDS->papszCreationOptions = CSLDuplicate( papszParmList ); + + poDS->nZLevel = GTiffGetZLevel(papszParmList); + poDS->nLZMAPreset = GTiffGetLZMAPreset(papszParmList); + poDS->nJpegQuality = GTiffGetJpegQuality(papszParmList); + poDS->nJpegTablesMode = GTiffGetJpegTablesMode(papszParmList); + +#if !defined(BIGTIFF_SUPPORT) +/* -------------------------------------------------------------------- */ +/* If we are writing jpeg compression we need to write some */ +/* imagery to force the jpegtables to get created. This is, */ +/* likely only needed with libtiff >= 3.9.3 (#3633) */ +/* -------------------------------------------------------------------- */ + if( poDS->nCompression == COMPRESSION_JPEG + && strstr(TIFFLIB_VERSION_STR, "Version 3.9") != NULL ) + { + CPLDebug( "GDAL", + "Writing zero block to force creation of JPEG tables." ); + if( TIFFIsTiled( hTIFF ) ) + { + int cc = TIFFTileSize( hTIFF ); + unsigned char *pabyZeros = (unsigned char *) CPLCalloc(cc,1); + TIFFWriteEncodedTile(hTIFF, 0, pabyZeros, cc); + CPLFree( pabyZeros ); + } + else + { + int cc = TIFFStripSize( hTIFF ); + unsigned char *pabyZeros = (unsigned char *) CPLCalloc(cc,1); + TIFFWriteEncodedStrip(hTIFF, 0, pabyZeros, cc); + CPLFree( pabyZeros ); + } + poDS->bDontReloadFirstBlock = TRUE; + } +#endif + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 0; iBand < nBands; iBand++ ) + { + if( poDS->nBitsPerSample == 8 || + poDS->nBitsPerSample == 16 || + poDS->nBitsPerSample == 32 || + poDS->nBitsPerSample == 64 || + poDS->nBitsPerSample == 128) + poDS->SetBand( iBand+1, new GTiffRasterBand( poDS, iBand+1 ) ); + else + { + poDS->SetBand( iBand+1, new GTiffOddBitsBand( poDS, iBand+1 ) ); + poDS->GetRasterBand( iBand+1 )-> + SetMetadataItem( "NBITS", + CPLString().Printf("%d",poDS->nBitsPerSample), + "IMAGE_STRUCTURE" ); + } + } + + poDS->GetDiscardLsbOption(papszParmList); + + if( poDS->nPlanarConfig == PLANARCONFIG_CONTIG && nBands != 1 ) + poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + else + poDS->SetMetadataItem( "INTERLEAVE", "BAND", "IMAGE_STRUCTURE" ); + + poDS->oOvManager.Initialize( poDS, pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * +GTiffDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + TIFF *hTIFF; + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int nBands = poSrcDS->GetRasterCount(); + int iBand; + CPLErr eErr = CE_None; + uint16 nPlanarConfig; + uint16 nBitsPerSample; + GDALRasterBand *poPBand; + + if( poSrcDS->GetRasterCount() == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to export GeoTIFF files with zero bands." ); + return NULL; + } + + poPBand = poSrcDS->GetRasterBand(1); + GDALDataType eType = poPBand->GetRasterDataType(); + +/* -------------------------------------------------------------------- */ +/* Check, whether all bands in input dataset has the same type. */ +/* -------------------------------------------------------------------- */ + for ( iBand = 2; iBand <= nBands; iBand++ ) + { + if ( eType != poSrcDS->GetRasterBand(iBand)->GetRasterDataType() ) + { + if ( bStrict ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to export GeoTIFF file with different datatypes per\n" + "different bands. All bands should have the same types in TIFF." ); + return NULL; + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to export GeoTIFF file with different datatypes per\n" + "different bands. All bands should have the same types in TIFF." ); + } + } + } + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Capture the profile. */ +/* -------------------------------------------------------------------- */ + const char *pszProfile; + int bGeoTIFF; + + pszProfile = CSLFetchNameValue(papszOptions,"PROFILE"); + if( pszProfile == NULL ) + pszProfile = "GDALGeoTIFF"; + + if( !EQUAL(pszProfile,"BASELINE") + && !EQUAL(pszProfile,"GeoTIFF") + && !EQUAL(pszProfile,"GDALGeoTIFF") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "PROFILE=%s not supported in GTIFF driver.", + pszProfile ); + return NULL; + } + + if( EQUAL(pszProfile,"BASELINE") ) + bGeoTIFF = FALSE; + else + bGeoTIFF = TRUE; + +/* -------------------------------------------------------------------- */ +/* Special handling for NBITS. Copy from band metadata if found. */ +/* -------------------------------------------------------------------- */ + char **papszCreateOptions = CSLDuplicate( papszOptions ); + + if( poPBand->GetMetadataItem( "NBITS", "IMAGE_STRUCTURE" ) != NULL + && atoi(poPBand->GetMetadataItem( "NBITS", "IMAGE_STRUCTURE" )) > 0 + && CSLFetchNameValue( papszCreateOptions, "NBITS") == NULL ) + { + papszCreateOptions = + CSLSetNameValue( papszCreateOptions, "NBITS", + poPBand->GetMetadataItem( "NBITS", + "IMAGE_STRUCTURE" ) ); + } + + if( CSLFetchNameValue( papszOptions, "PIXELTYPE" ) == NULL + && eType == GDT_Byte + && poPBand->GetMetadataItem( "PIXELTYPE", "IMAGE_STRUCTURE" ) ) + { + papszCreateOptions = + CSLSetNameValue( papszCreateOptions, "PIXELTYPE", + poPBand->GetMetadataItem( + "PIXELTYPE", "IMAGE_STRUCTURE" ) ); + } + +/* -------------------------------------------------------------------- */ +/* Color profile. Copy from band metadata if found. */ +/* -------------------------------------------------------------------- */ + if (bGeoTIFF) + { + const char* pszOptionsMD[] = { + "SOURCE_ICC_PROFILE", + "SOURCE_PRIMARIES_RED", + "SOURCE_PRIMARIES_GREEN", + "SOURCE_PRIMARIES_BLUE", + "SOURCE_WHITEPOINT", + "TIFFTAG_TRANSFERFUNCTION_RED", + "TIFFTAG_TRANSFERFUNCTION_GREEN", + "TIFFTAG_TRANSFERFUNCTION_BLUE", + "TIFFTAG_TRANSFERRANGE_BLACK", + "TIFFTAG_TRANSFERRANGE_WHITE", + NULL + }; + + /* Copy all the tags. Options will override tags in the source */ + int i = 0; + while(pszOptionsMD[i] != NULL) + { + char const *pszMD = CSLFetchNameValue(papszOptions, pszOptionsMD[i]); + if (pszMD == NULL) + pszMD = poSrcDS->GetMetadataItem( pszOptionsMD[i], "COLOR_PROFILE" ); + + if ((pszMD != NULL) && !EQUAL(pszMD, "") ) + { + papszCreateOptions = + CSLSetNameValue( papszCreateOptions, pszOptionsMD[i], pszMD ); + + /* If an ICC profile exists, other tags are not needed */ + if (EQUAL(pszOptionsMD[i], "SOURCE_ICC_PROFILE")) + break; + } + + i++; + } + } + + int nSrcOverviews = poSrcDS->GetRasterBand(1)->GetOverviewCount(); + double dfExtraSpaceForOverviews = 0; + if (nSrcOverviews != 0 && + CSLFetchBoolean(papszOptions, "COPY_SRC_OVERVIEWS", FALSE)) + { + for(int j=1;j<=nBands;j++) + { + if( poSrcDS->GetRasterBand(j)->GetOverviewCount() != nSrcOverviews ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "COPY_SRC_OVERVIEWS cannot be used when the bands have not the same number of overview levels." ); + CSLDestroy(papszCreateOptions); + return NULL; + } + for(int i=0;iGetRasterBand(j)->GetOverview(i); + if( poOvrBand == NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "COPY_SRC_OVERVIEWS cannot be used when one overview band is NULL." ); + CSLDestroy(papszCreateOptions); + return NULL; + } + GDALRasterBand* poOvrFirstBand = poSrcDS->GetRasterBand(1)->GetOverview(i); + if( poOvrBand->GetXSize() != poOvrFirstBand->GetXSize() || + poOvrBand->GetYSize() != poOvrFirstBand->GetYSize() ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "COPY_SRC_OVERVIEWS cannot be used when the overview bands have not the same dimensions among bands." ); + CSLDestroy(papszCreateOptions); + return NULL; + } + } + } + + for(int i=0;iGetRasterBand(1)->GetOverview(i)->GetXSize()) * + poSrcDS->GetRasterBand(1)->GetOverview(i)->GetYSize(); + } + dfExtraSpaceForOverviews *= nBands * (GDALGetDataTypeSize(eType) / 8); + } + +/* -------------------------------------------------------------------- */ +/* Should we use optimized way of copying from an input JPEG */ +/* dataset ? */ +/* -------------------------------------------------------------------- */ +#if defined(HAVE_LIBJPEG) + int bCopyFromJPEG = FALSE; +#endif +#if defined(HAVE_LIBJPEG) || defined(JPEG_DIRECT_COPY) + int bDirectCopyFromJPEG = FALSE; +#endif + + /* Note: JPEG_DIRECT_COPY is not defined by default, because it is mainly */ + /* useful for debugging purposes */ +#ifdef JPEG_DIRECT_COPY + if (CSLFetchBoolean(papszCreateOptions, "JPEG_DIRECT_COPY", FALSE) && + GTIFF_CanDirectCopyFromJPEG(poSrcDS, papszCreateOptions)) + { + CPLDebug("GTiff", "Using special direct copy mode from a JPEG dataset"); + + bDirectCopyFromJPEG = TRUE; + } +#endif + +#ifdef HAVE_LIBJPEG + /* when CreateCopy'ing() from a JPEG dataset, and asking for COMPRESS=JPEG, */ + /* use DCT coefficients (unless other options are incompatible, like strip/tile dimensions, */ + /* specifying JPEG_QUALITY option, incompatible PHOTOMETRIC with the source colorspace, etc...) */ + /* to avoid the lossy steps involved by uncompression/recompression */ + if (!bDirectCopyFromJPEG && GTIFF_CanCopyFromJPEG(poSrcDS, papszCreateOptions)) + { + CPLDebug("GTiff", "Using special copy mode from a JPEG dataset"); + + bCopyFromJPEG = TRUE; + } +#endif + +/* -------------------------------------------------------------------- */ +/* Create the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE* fpL = NULL; + CPLString osTmpFilename; + + hTIFF = CreateLL( pszFilename, nXSize, nYSize, nBands, + eType, dfExtraSpaceForOverviews, papszCreateOptions, &fpL, osTmpFilename ); + int bStreaming = (osTmpFilename.size() != 0); + + CSLDestroy( papszCreateOptions ); + papszCreateOptions = NULL; + + if( hTIFF == NULL ) + { + if( bStreaming ) VSIUnlink(osTmpFilename); + return NULL; + } + + TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &nPlanarConfig ); + TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &nBitsPerSample ); + + uint16 nCompression; + + if( !TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &(nCompression) ) ) + nCompression = COMPRESSION_NONE; + + int bForcePhotometric = + CSLFetchNameValue(papszOptions,"PHOTOMETRIC") != NULL; + +/* -------------------------------------------------------------------- */ +/* If the source is RGB, then set the PHOTOMETRIC_RGB value */ +/* -------------------------------------------------------------------- */ + if( nBands == 3 && !bForcePhotometric && + nCompression != COMPRESSION_JPEG && + poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_RedBand && + poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_GreenBand && + poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_BlueBand ) + { + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB ); + } + +/* -------------------------------------------------------------------- */ +/* Are we really producing a Grey+Alpha image? If so, set the */ +/* associated alpha information. */ +/* -------------------------------------------------------------------- */ + else if( nBands == 2 && !bForcePhotometric && + nCompression != COMPRESSION_JPEG && + poSrcDS->GetRasterBand(1)->GetColorInterpretation()==GCI_GrayIndex && + poSrcDS->GetRasterBand(2)->GetColorInterpretation()==GCI_AlphaBand) + { + uint16 v[1]; + + v[0] = GTiffGetAlphaValue(CSLFetchNameValue(papszOptions, "ALPHA"), + DEFAULT_ALPHA_TYPE); + + TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, 1, v); + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK ); + } +/* -------------------------------------------------------------------- */ +/* Are we really producing an RGBA image? If so, set the */ +/* associated alpha information. */ +/* -------------------------------------------------------------------- */ + else if( nBands == 4 && !bForcePhotometric && + nCompression != COMPRESSION_JPEG && + poSrcDS->GetRasterBand(4)->GetColorInterpretation()==GCI_AlphaBand) + { + uint16 v[1]; + + v[0] = GTiffGetAlphaValue(CSLFetchNameValue(papszOptions, "ALPHA"), + DEFAULT_ALPHA_TYPE); + + TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, 1, v); + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB ); + } + + else if( !bForcePhotometric && nBands == 3 && + nCompression != COMPRESSION_JPEG && + (poSrcDS->GetRasterBand(1)->GetColorInterpretation() != GCI_Undefined || + poSrcDS->GetRasterBand(2)->GetColorInterpretation() != GCI_Undefined || + poSrcDS->GetRasterBand(3)->GetColorInterpretation() != GCI_Undefined) ) + { + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK ); + } + +/* -------------------------------------------------------------------- */ +/* If the output is jpeg compressed, and the input is RGB make */ +/* sure we note that. */ +/* -------------------------------------------------------------------- */ + + if( nCompression == COMPRESSION_JPEG ) + { + if( nBands >= 3 + && (poSrcDS->GetRasterBand(1)->GetColorInterpretation() + == GCI_YCbCr_YBand) + && (poSrcDS->GetRasterBand(2)->GetColorInterpretation() + == GCI_YCbCr_CbBand) + && (poSrcDS->GetRasterBand(3)->GetColorInterpretation() + == GCI_YCbCr_CrBand) ) + { + /* do nothing ... */ + } + else + { + /* we assume RGB if it isn't explicitly YCbCr */ + CPLDebug( "GTiff", "Setting JPEGCOLORMODE_RGB" ); + TIFFSetField( hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB ); + } + } + +/* -------------------------------------------------------------------- */ +/* Does the source image consist of one band, with a palette? */ +/* If so, copy over. */ +/* -------------------------------------------------------------------- */ + if( (nBands == 1 || nBands == 2) && poSrcDS->GetRasterBand(1)->GetColorTable() != NULL + && eType == GDT_Byte ) + { + unsigned short anTRed[256], anTGreen[256], anTBlue[256]; + GDALColorTable *poCT; + + poCT = poSrcDS->GetRasterBand(1)->GetColorTable(); + + for( int iColor = 0; iColor < 256; iColor++ ) + { + if( iColor < poCT->GetColorEntryCount() ) + { + GDALColorEntry sRGB; + + poCT->GetColorEntryAsRGB( iColor, &sRGB ); + + anTRed[iColor] = (unsigned short) (257 * sRGB.c1); + anTGreen[iColor] = (unsigned short) (257 * sRGB.c2); + anTBlue[iColor] = (unsigned short) (257 * sRGB.c3); + } + else + { + anTRed[iColor] = anTGreen[iColor] = anTBlue[iColor] = 0; + } + } + + if( !bForcePhotometric ) + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE ); + TIFFSetField( hTIFF, TIFFTAG_COLORMAP, anTRed, anTGreen, anTBlue ); + } + else if( (nBands == 1 || nBands == 2) + && poSrcDS->GetRasterBand(1)->GetColorTable() != NULL + && eType == GDT_UInt16 ) + { + unsigned short *panTRed, *panTGreen, *panTBlue; + GDALColorTable *poCT; + + panTRed = (unsigned short *) CPLMalloc(65536*sizeof(unsigned short)); + panTGreen = (unsigned short *) CPLMalloc(65536*sizeof(unsigned short)); + panTBlue = (unsigned short *) CPLMalloc(65536*sizeof(unsigned short)); + + poCT = poSrcDS->GetRasterBand(1)->GetColorTable(); + + for( int iColor = 0; iColor < 65536; iColor++ ) + { + if( iColor < poCT->GetColorEntryCount() ) + { + GDALColorEntry sRGB; + + poCT->GetColorEntryAsRGB( iColor, &sRGB ); + + panTRed[iColor] = (unsigned short) (256 * sRGB.c1); + panTGreen[iColor] = (unsigned short) (256 * sRGB.c2); + panTBlue[iColor] = (unsigned short) (256 * sRGB.c3); + } + else + { + panTRed[iColor] = panTGreen[iColor] = panTBlue[iColor] = 0; + } + } + + if( !bForcePhotometric ) + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE ); + TIFFSetField( hTIFF, TIFFTAG_COLORMAP, panTRed, panTGreen, panTBlue ); + + CPLFree( panTRed ); + CPLFree( panTGreen ); + CPLFree( panTBlue ); + } + else if( poSrcDS->GetRasterBand(1)->GetColorTable() != NULL ) + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to export color table to GeoTIFF file. Color tables\n" + "can only be written to 1 band or 2 bands Byte or UInt16 GeoTIFF files." ); + + if( nBands == 2 + && poSrcDS->GetRasterBand(1)->GetColorTable() != NULL + && (eType == GDT_Byte || eType == GDT_UInt16) ) + { + uint16 v[1] = { EXTRASAMPLE_UNASSALPHA }; + + TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, 1, v ); + } + + // FIXME? libtiff writes extended tags in the order they are specified + // and not in increasing order + +/* -------------------------------------------------------------------- */ +/* Transfer some TIFF specific metadata, if available. */ +/* The return value will tell us if we need to try again later with*/ +/* PAM because the profile doesn't allow to write some metadata */ +/* as TIFF tag */ +/* -------------------------------------------------------------------- */ + int bHasWrittenMDInGeotiffTAG = + GTiffDataset::WriteMetadata( poSrcDS, hTIFF, FALSE, pszProfile, + pszFilename, papszOptions ); + +/* -------------------------------------------------------------------- */ +/* Write NoData value, if exist. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszProfile,"GDALGeoTIFF") ) + { + int bSuccess; + double dfNoData; + + dfNoData = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bSuccess ); + if ( bSuccess ) + GTiffDataset::WriteNoDataValue( hTIFF, dfNoData ); + } + +/* -------------------------------------------------------------------- */ +/* Are we addressing PixelIsPoint mode? */ +/* -------------------------------------------------------------------- */ + bool bPixelIsPoint = false; + int bPointGeoIgnore = FALSE; + + if( poSrcDS->GetMetadataItem( GDALMD_AREA_OR_POINT ) + && EQUAL(poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT), + GDALMD_AOP_POINT) ) + { + bPixelIsPoint = true; + bPointGeoIgnore = + CSLTestBoolean( CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", + "FALSE") ); + } + +/* -------------------------------------------------------------------- */ +/* Write affine transform if it is meaningful. */ +/* -------------------------------------------------------------------- */ + const char *pszProjection = NULL; + double adfGeoTransform[6]; + + if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None + && (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 + || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 + || adfGeoTransform[4] != 0.0 || adfGeoTransform[5] != 1.0 )) + { + if( bGeoTIFF ) + { + if( adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0 + && adfGeoTransform[5] < 0.0 ) + { + + double adfPixelScale[3], adfTiePoints[6]; + + adfPixelScale[0] = adfGeoTransform[1]; + adfPixelScale[1] = fabs(adfGeoTransform[5]); + adfPixelScale[2] = 0.0; + + TIFFSetField( hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale ); + + adfTiePoints[0] = 0.0; + adfTiePoints[1] = 0.0; + adfTiePoints[2] = 0.0; + adfTiePoints[3] = adfGeoTransform[0]; + adfTiePoints[4] = adfGeoTransform[3]; + adfTiePoints[5] = 0.0; + + if( bPixelIsPoint && !bPointGeoIgnore ) + { + adfTiePoints[3] += adfGeoTransform[1] * 0.5 + adfGeoTransform[2] * 0.5; + adfTiePoints[4] += adfGeoTransform[4] * 0.5 + adfGeoTransform[5] * 0.5; + } + + TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints ); + } + else + { + double adfMatrix[16]; + + memset(adfMatrix,0,sizeof(double) * 16); + + adfMatrix[0] = adfGeoTransform[1]; + adfMatrix[1] = adfGeoTransform[2]; + adfMatrix[3] = adfGeoTransform[0]; + adfMatrix[4] = adfGeoTransform[4]; + adfMatrix[5] = adfGeoTransform[5]; + adfMatrix[7] = adfGeoTransform[3]; + adfMatrix[15] = 1.0; + + if( bPixelIsPoint && !bPointGeoIgnore ) + { + adfMatrix[3] += adfGeoTransform[1] * 0.5 + adfGeoTransform[2] * 0.5; + adfMatrix[7] += adfGeoTransform[4] * 0.5 + adfGeoTransform[5] * 0.5; + } + + TIFFSetField( hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix ); + } + + pszProjection = poSrcDS->GetProjectionRef(); + } + +/* -------------------------------------------------------------------- */ +/* Do we need a TFW file? */ +/* -------------------------------------------------------------------- */ + if( CSLFetchBoolean( papszOptions, "TFW", FALSE ) ) + GDALWriteWorldFile( pszFilename, "tfw", adfGeoTransform ); + else if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) ) + GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise write tiepoints if they are available. */ +/* -------------------------------------------------------------------- */ + else if( poSrcDS->GetGCPCount() > 0 && bGeoTIFF ) + { + const GDAL_GCP *pasGCPs = poSrcDS->GetGCPs(); + double *padfTiePoints; + + padfTiePoints = (double *) + CPLMalloc(6*sizeof(double)*poSrcDS->GetGCPCount()); + + for( int iGCP = 0; iGCP < poSrcDS->GetGCPCount(); iGCP++ ) + { + + padfTiePoints[iGCP*6+0] = pasGCPs[iGCP].dfGCPPixel; + padfTiePoints[iGCP*6+1] = pasGCPs[iGCP].dfGCPLine; + padfTiePoints[iGCP*6+2] = 0; + padfTiePoints[iGCP*6+3] = pasGCPs[iGCP].dfGCPX; + padfTiePoints[iGCP*6+4] = pasGCPs[iGCP].dfGCPY; + padfTiePoints[iGCP*6+5] = pasGCPs[iGCP].dfGCPZ; + + if( bPixelIsPoint && !bPointGeoIgnore ) + { + padfTiePoints[iGCP*6+0] += 0.5; + padfTiePoints[iGCP*6+1] += 0.5; + } + } + + TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, + 6*poSrcDS->GetGCPCount(), padfTiePoints ); + CPLFree( padfTiePoints ); + + pszProjection = poSrcDS->GetGCPProjection(); + + if( CSLFetchBoolean( papszOptions, "TFW", FALSE ) + || CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "TFW=ON or WORLDFILE=ON creation options are ignored when GCPs are available"); + } + } + + else + pszProjection = poSrcDS->GetProjectionRef(); + +/* -------------------------------------------------------------------- */ +/* Write the projection information, if possible. */ +/* -------------------------------------------------------------------- */ + if( pszProjection != NULL && strlen(pszProjection) > 0 && bGeoTIFF ) + { + GTIF *psGTIF; + + psGTIF = GTIFNew( hTIFF ); + GTIFSetFromOGISDefn( psGTIF, pszProjection ); + + if( poSrcDS->GetMetadataItem( GDALMD_AREA_OR_POINT ) + && EQUAL(poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT), + GDALMD_AOP_POINT) ) + { + GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1, + RasterPixelIsPoint); + } + + GTIFWriteKeys( psGTIF ); + GTIFFree( psGTIF ); + } + + int bDontReloadFirstBlock = FALSE; + +#ifdef HAVE_LIBJPEG + if (bCopyFromJPEG) + { + GTIFF_CopyFromJPEG_WriteAdditionalTags(hTIFF, + poSrcDS); + } +#else + if (0) + { + } +#endif + +#if !defined(BIGTIFF_SUPPORT) + /* -------------------------------------------------------------------- */ + /* If we are writing jpeg compression we need to write some */ + /* imagery to force the jpegtables to get created. This is, */ + /* likely only needed with libtiff >= 3.9.3 (#3633) */ + /* -------------------------------------------------------------------- */ + else if( nCompression == COMPRESSION_JPEG + && strstr(TIFFLIB_VERSION_STR, "Version 3.9") != NULL ) + { + CPLDebug( "GDAL", + "Writing zero block to force creation of JPEG tables." ); + if( TIFFIsTiled( hTIFF ) ) + { + int cc = TIFFTileSize( hTIFF ); + unsigned char *pabyZeros = (unsigned char *) CPLCalloc(cc,1); + TIFFWriteEncodedTile(hTIFF, 0, pabyZeros, cc); + CPLFree( pabyZeros ); + } + else + { + int cc = TIFFStripSize( hTIFF ); + unsigned char *pabyZeros = (unsigned char *) CPLCalloc(cc,1); + TIFFWriteEncodedStrip(hTIFF, 0, pabyZeros, cc); + CPLFree( pabyZeros ); + } + bDontReloadFirstBlock = TRUE; + } +#endif + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + + TIFFWriteCheck( hTIFF, TIFFIsTiled(hTIFF), "GTiffCreateCopy()"); + TIFFWriteDirectory( hTIFF ); + if( bStreaming ) + { + /* We need to write twice the directory to be sure that custom */ + /* TIFF tags are correctly sorted and that padding bytes have been */ + /* added */ + TIFFSetDirectory( hTIFF, 0 ); + TIFFWriteDirectory( hTIFF ); + + VSIFSeekL( fpL, 0, SEEK_END ); + int nSize = (int) VSIFTellL(fpL); + + vsi_l_offset nDataLength; + VSIGetMemFileBuffer( osTmpFilename, &nDataLength, FALSE); + TIFFSetDirectory( hTIFF, 0 ); + GTiffFillStreamableOffsetAndCount( hTIFF, nSize ); + TIFFWriteDirectory( hTIFF ); + } + TIFFFlush( hTIFF ); + XTIFFClose( hTIFF ); + hTIFF = NULL; + VSIFCloseL(fpL); + fpL = NULL; + + if( eErr != CE_None ) + { + VSIUnlink( bStreaming ? osTmpFilename.c_str() : pszFilename ); + return NULL; + } + + if( bStreaming ) + { + vsi_l_offset nDataLength; + void* pabyBuffer = VSIGetMemFileBuffer( osTmpFilename, &nDataLength, FALSE); + fpL = VSIFOpenL( pszFilename, "wb" ); + if( fpL == NULL ) + { + VSIUnlink(osTmpFilename); + return NULL; + } + if( (int)VSIFWriteL( pabyBuffer, 1, (int)nDataLength, fpL ) != (int)nDataLength ) + { + CPLError(CE_Failure, CPLE_FileIO, "Could not write %d bytes", + (int)nDataLength); + VSIFCloseL( fpL ); + VSIUnlink(osTmpFilename); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Re-open as a dataset and copy over missing metadata using */ +/* PAM facilities. */ +/* -------------------------------------------------------------------- */ + GTiffDataset *poDS; + CPLString osFileName("GTIFF_RAW:"); + + osFileName += bStreaming ? osTmpFilename.c_str() : pszFilename; + + GDALOpenInfo oOpenInfo( osFileName, GA_Update ); + if( bStreaming ) + { + /* In case of single strip file, there's a libtiff check that would */ + /* issue a warning since the file hasn't the required size */ + CPLPushErrorHandler(CPLQuietErrorHandler); + } + poDS = (GTiffDataset *) Open(&oOpenInfo); + if( bStreaming ) + CPLPopErrorHandler(); + if( poDS == NULL ) + { + oOpenInfo.eAccess = GA_ReadOnly; + poDS = (GTiffDataset *) Open(&oOpenInfo); + } + + if ( poDS == NULL ) + { + VSIUnlink( bStreaming ? osTmpFilename.c_str() : pszFilename ); + return NULL; + } + + if( bStreaming ) + { + VSIUnlink(osTmpFilename); + poDS->fpToWrite = fpL; + } + poDS->osProfile = pszProfile; + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT & ~GCIF_MASK ); + poDS->papszCreationOptions = CSLDuplicate( papszOptions ); + poDS->bDontReloadFirstBlock = bDontReloadFirstBlock; + +/* -------------------------------------------------------------------- */ +/* CloneInfo() doesn't merge metadata, it just replaces it totally */ +/* So we have to merge it */ +/* -------------------------------------------------------------------- */ + + char **papszSRC_MD = poSrcDS->GetMetadata(); + char **papszDST_MD = CSLDuplicate(poDS->GetMetadata()); + + papszDST_MD = CSLMerge( papszDST_MD, papszSRC_MD ); + + poDS->SetMetadata( papszDST_MD ); + CSLDestroy( papszDST_MD ); + + /* Depending on the PHOTOMETRIC tag, the TIFF file may not have */ + /* the same band count as the source. Will fail later in GDALDatasetCopyWholeRaster anyway... */ + for( int nBand = 1; + nBand <= MIN(poDS->GetRasterCount(), poSrcDS->GetRasterCount()) ; + nBand++ ) + { + GDALRasterBand* poSrcBand = poSrcDS->GetRasterBand(nBand); + GDALRasterBand* poDstBand = poDS->GetRasterBand(nBand); + char **papszSRC_MD = poSrcBand->GetMetadata(); + char **papszDST_MD = CSLDuplicate(poDstBand->GetMetadata()); + + papszDST_MD = CSLMerge( papszDST_MD, papszSRC_MD ); + + poDstBand->SetMetadata( papszDST_MD ); + CSLDestroy( papszDST_MD ); + + char** papszCatNames; + papszCatNames = poSrcBand->GetCategoryNames(); + if (NULL != papszCatNames) + poDstBand->SetCategoryNames( papszCatNames ); + } + + hTIFF = (TIFF*) poDS->GetInternalHandle(NULL); + +/* -------------------------------------------------------------------- */ +/* Handle forcing xml:ESRI data to be written to PAM. */ +/* -------------------------------------------------------------------- */ + if( CSLTestBoolean(CPLGetConfigOption( "ESRI_XML_PAM", "NO" )) ) + { + char **papszESRIMD = poSrcDS->GetMetadata("xml:ESRI"); + if( papszESRIMD ) + { + poDS->SetMetadata( papszESRIMD, "xml:ESRI"); + } + } + +/* -------------------------------------------------------------------- */ +/* Second chance : now that we have a PAM dataset, it is possible */ +/* to write metadata that we couldn't be writen as TIFF tag */ +/* -------------------------------------------------------------------- */ + if (!bHasWrittenMDInGeotiffTAG && !bStreaming) + GTiffDataset::WriteMetadata( poDS, hTIFF, TRUE, pszProfile, + pszFilename, papszOptions, TRUE /* don't write RPC and IMD file again */); + + if( !bStreaming ) + GTiffDataset::WriteRPC( poDS, hTIFF, TRUE, pszProfile, + pszFilename, papszOptions, TRUE /* write only in PAM AND if needed */ ); + + /* To avoid unnecessary directory rewriting */ + poDS->bMetadataChanged = FALSE; + poDS->bGeoTIFFInfoChanged = FALSE; + poDS->bNoDataChanged = FALSE; + poDS->bForceUnsetGTOrGCPs = FALSE; + poDS->bForceUnsetProjection = FALSE; + poDS->bStreamingOut = bStreaming; + + /* We must re-set the compression level at this point, since it has */ + /* been lost a few lines above when closing the newly create TIFF file */ + /* The TIFFTAG_ZIPQUALITY & TIFFTAG_JPEGQUALITY are not store in the TIFF file. */ + /* They are just TIFF session parameters */ + + poDS->nZLevel = GTiffGetZLevel(papszOptions); + poDS->nLZMAPreset = GTiffGetLZMAPreset(papszOptions); + poDS->nJpegQuality = GTiffGetJpegQuality(papszOptions); + poDS->nJpegTablesMode = GTiffGetJpegTablesMode(papszOptions); + poDS->GetDiscardLsbOption(papszOptions); + + if (nCompression == COMPRESSION_ADOBE_DEFLATE) + { + if (poDS->nZLevel != -1) + { + TIFFSetField( hTIFF, TIFFTAG_ZIPQUALITY, poDS->nZLevel ); + } + } + else if( nCompression == COMPRESSION_JPEG) + { + if (poDS->nJpegQuality != -1) + { + TIFFSetField( hTIFF, TIFFTAG_JPEGQUALITY, poDS->nJpegQuality ); + } + TIFFSetField( hTIFF, TIFFTAG_JPEGTABLESMODE, poDS->nJpegTablesMode ); + } + else if( nCompression == COMPRESSION_LZMA) + { + if (poDS->nLZMAPreset != -1) + { + TIFFSetField( hTIFF, TIFFTAG_LZMAPRESET, poDS->nLZMAPreset ); + } + } + + /* Precreate (internal) mask, so that the IBuildOverviews() below */ + /* has a chance to create also the overviews of the mask */ + int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags(); + if( eErr == CE_None + && !(nMaskFlags & (GMF_ALL_VALID|GMF_ALPHA|GMF_NODATA) ) + && (nMaskFlags & GMF_PER_DATASET) ) + { + eErr = poDS->CreateMaskBand( nMaskFlags ); + } + +/* -------------------------------------------------------------------- */ +/* Create and then copy existing overviews if requested */ +/* We do it such that all the IFDs are at the beginning of the file, */ +/* and that the imagery data for the smallest overview is written */ +/* first, that way the file is more usable when embedded in a */ +/* compressed stream. */ +/* -------------------------------------------------------------------- */ + + /* For scaled progress due to overview copying */ + double dfTotalPixels = ((double)nXSize) * nYSize; + double dfCurPixels = 0; + + if (eErr == CE_None && + nSrcOverviews != 0 && + CSLFetchBoolean(papszOptions, "COPY_SRC_OVERVIEWS", FALSE)) + { + eErr = poDS->CreateOverviewsFromSrcOverviews(poSrcDS); + + if (poDS->nOverviewCount != nSrcOverviews) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Did only manage to instanciate %d overview levels, whereas source contains %d", + poDS->nOverviewCount, nSrcOverviews); + eErr = CE_Failure; + } + + int i; + for(i=0;iGetRasterBand(1)->GetOverview(i); + dfTotalPixels += ((double)poOvrBand->GetXSize()) * + poOvrBand->GetYSize(); + } + + char* papszCopyWholeRasterOptions[2] = { NULL, NULL }; + if (nCompression != COMPRESSION_NONE) + papszCopyWholeRasterOptions[0] = (char*) "COMPRESSED=YES"; + /* Now copy the imagery */ + for(i=0;eErr == CE_None && iGetRasterBand(1)->GetOverview(iOvrLevel); + double dfNextCurPixels = dfCurPixels + + ((double)poOvrBand->GetXSize()) * poOvrBand->GetYSize(); + + void* pScaledData = GDALCreateScaledProgress( dfCurPixels / dfTotalPixels, + dfNextCurPixels / dfTotalPixels, + pfnProgress, pProgressData); + + eErr = GDALDatasetCopyWholeRaster( (GDALDatasetH) poSrcOvrDS, + (GDALDatasetH) poDS->papoOverviewDS[iOvrLevel], + papszCopyWholeRasterOptions, + GDALScaledProgress, pScaledData ); + + dfCurPixels = dfNextCurPixels; + GDALDestroyScaledProgress(pScaledData); + + delete poSrcOvrDS; + poDS->papoOverviewDS[iOvrLevel]->FlushCache(); + + /* Copy mask of the overview */ + if (eErr == CE_None && poDS->poMaskDS != NULL) + { + eErr = GDALRasterBandCopyWholeRaster( poOvrBand->GetMaskBand(), + poDS->papoOverviewDS[iOvrLevel]->poMaskDS->GetRasterBand(1), + papszCopyWholeRasterOptions, + GDALDummyProgress, NULL); + poDS->papoOverviewDS[iOvrLevel]->poMaskDS->FlushCache(); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Copy actual imagery. */ +/* -------------------------------------------------------------------- */ + void* pScaledData = GDALCreateScaledProgress( dfCurPixels / dfTotalPixels, + 1.0, + pfnProgress, pProgressData); + + int bTryCopy = TRUE; + +#ifdef HAVE_LIBJPEG + if (bCopyFromJPEG) + { + eErr = GTIFF_CopyFromJPEG(poDS, poSrcDS, + pfnProgress, pProgressData, + bTryCopy); + + /* In case of failure in the decompression step, try normal copy */ + if (bTryCopy) + eErr = CE_None; + } +#endif + +#ifdef JPEG_DIRECT_COPY + if (bDirectCopyFromJPEG) + { + eErr = GTIFF_DirectCopyFromJPEG(poDS, poSrcDS, + pfnProgress, pProgressData, + bTryCopy); + + /* In case of failure in the reading step, try normal copy */ + if (bTryCopy) + eErr = CE_None; + } +#endif + + if (bTryCopy && (poDS->bTreatAsSplit || poDS->bTreatAsSplitBitmap)) + { + /* For split bands, we use TIFFWriteScanline() interface */ + CPLAssert(poDS->nBitsPerSample == 8 || poDS->nBitsPerSample == 1); + + if (poDS->nPlanarConfig == PLANARCONFIG_CONTIG && poDS->nBands > 1) + { + int j; + GByte* pabyScanline = (GByte *) CPLMalloc(TIFFScanlineSize(hTIFF)); + for(j=0;jRasterIO(GF_Read, 0, j, nXSize, 1, + pabyScanline, nXSize, 1, + GDT_Byte, nBands, NULL, poDS->nBands, 0, 1, + NULL); + if (eErr == CE_None && + TIFFWriteScanline( hTIFF, pabyScanline, j, 0) == -1) + { + CPLError( CE_Failure, CPLE_AppDefined, + "TIFFWriteScanline() failed." ); + eErr = CE_Failure; + } + if( !GDALScaledProgress( (j+1) * 1.0 / nYSize, NULL, pScaledData ) ) + eErr = CE_Failure; + } + CPLFree(pabyScanline); + } + else + { + int iBand, j; + GByte* pabyScanline = (GByte *) CPLMalloc(nXSize); + eErr = CE_None; + for(iBand=1;iBand<=nBands && eErr == CE_None;iBand++) + { + for(j=0;jGetRasterBand(iBand)->RasterIO( + GF_Read, 0, j, nXSize, 1, + pabyScanline, nXSize, 1, + GDT_Byte, 0, 0, NULL); + if (poDS->bTreatAsSplitBitmap) + { + for(int i=0;i> 3] = 0; + if (byVal) + pabyScanline[i >> 3] |= (0x80 >> (i & 0x7)); + } + } + if (eErr == CE_None && + TIFFWriteScanline( hTIFF, pabyScanline, j, (uint16) (iBand-1)) == -1) + { + CPLError( CE_Failure, CPLE_AppDefined, + "TIFFWriteScanline() failed." ); + eErr = CE_Failure; + } + if( !GDALScaledProgress( (j+1 + (iBand - 1) * nYSize) * 1.0 / + (nBands * nYSize), NULL, pScaledData ) ) + eErr = CE_Failure; + } + } + CPLFree(pabyScanline); + } + + /* Necessary to be able to read the file without re-opening */ +#if defined(HAVE_TIFFGETSIZEPROC) + TIFFSizeProc pfnSizeProc = TIFFGetSizeProc( hTIFF ); + + TIFFFlushData( hTIFF ); + + toff_t nNewDirOffset = pfnSizeProc( TIFFClientdata( hTIFF ) ); + if( (nNewDirOffset % 2) == 1 ) + nNewDirOffset++; +#endif + + TIFFFlush( hTIFF ); + +#if defined(HAVE_TIFFGETSIZEPROC) + if( poDS->nDirOffset != TIFFCurrentDirOffset( hTIFF ) ) + { + poDS->nDirOffset = nNewDirOffset; + CPLDebug( "GTiff", "directory moved during flush." ); + } +#endif + } + else if (bTryCopy && eErr == CE_None) + { + char* papszCopyWholeRasterOptions[2] = { NULL, NULL }; + if (nCompression != COMPRESSION_NONE) + papszCopyWholeRasterOptions[0] = (char*) "COMPRESSED=YES"; + /* For streaming with separate, we really want that bands are written */ + /* after each other, even if the source is pixel interleaved */ + else if( bStreaming && poDS->nPlanarConfig == PLANARCONFIG_SEPARATE ) + papszCopyWholeRasterOptions[0] = (char*) "INTERLEAVE=BAND"; + eErr = GDALDatasetCopyWholeRaster( (GDALDatasetH) poSrcDS, + (GDALDatasetH) poDS, + papszCopyWholeRasterOptions, + GDALScaledProgress, pScaledData ); + } + + GDALDestroyScaledProgress(pScaledData); + + if (eErr == CE_None && !bStreaming) + { + if (poDS->poMaskDS) + { + const char* papszOptions[2] = { "COMPRESSED=YES", NULL }; + eErr = GDALRasterBandCopyWholeRaster( + poSrcDS->GetRasterBand(1)->GetMaskBand(), + poDS->GetRasterBand(1)->GetMaskBand(), + (char**)papszOptions, + GDALDummyProgress, NULL); + } + else + eErr = GDALDriver::DefaultCopyMasks( poSrcDS, poDS, bStrict ); + } + + if( eErr == CE_Failure ) + { + delete poDS; + poDS = NULL; + + if (CSLTestBoolean(CPLGetConfigOption("GTIFF_DELETE_ON_ERROR", "YES"))) + { + if( !bStreaming ) + VSIUnlink( pszFilename ); // should really delete more carefully. + } + } + + return poDS; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GTiffDataset::GetProjectionRef() + +{ + if( nGCPCount == 0 ) + { + LookForProjection(); + + if( EQUAL(pszProjection,"") ) + return GDALPamDataset::GetProjectionRef(); + else + return( pszProjection ); + } + else + return ""; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr GTiffDataset::SetProjection( const char * pszNewProjection ) + +{ + if( bStreamingOut && bCrystalized ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot modify projection at that point in a streamed output file"); + return CE_Failure; + } + + LookForProjection(); + + if( !EQUALN(pszNewProjection,"GEOGCS",6) + && !EQUALN(pszNewProjection,"PROJCS",6) + && !EQUALN(pszNewProjection,"LOCAL_CS",8) + && !EQUALN(pszNewProjection,"COMPD_CS",8) + && !EQUALN(pszNewProjection,"GEOCCS",6) + && !EQUAL(pszNewProjection,"") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Only OGC WKT Projections supported for writing to GeoTIFF.\n" + "%s not supported.", + pszNewProjection ); + + return CE_Failure; + } + + if ( EQUAL(pszNewProjection, "") && + pszProjection != NULL && + !EQUAL(pszProjection, "") ) + { + bForceUnsetProjection = TRUE; + } + + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszNewProjection ); + + bGeoTIFFInfoChanged = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GTiffDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + + if( !bGeoTransformValid ) + return CE_Failure; + else + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr GTiffDataset::SetGeoTransform( double * padfTransform ) + +{ + if( bStreamingOut && bCrystalized ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot modify geotransform at that point in a streamed output file"); + return CE_Failure; + } + + if( GetAccess() == GA_Update ) + { + if ( + padfTransform[0] == 0.0 && + padfTransform[1] == 1.0 && + padfTransform[2] == 0.0 && + padfTransform[3] == 0.0 && + padfTransform[4] == 0.0 && + padfTransform[5] == 1.0 && + !(adfGeoTransform[0] == 0.0 && + adfGeoTransform[1] == 1.0 && + adfGeoTransform[2] == 0.0 && + adfGeoTransform[3] == 0.0 && + adfGeoTransform[4] == 0.0 && + adfGeoTransform[5] == 1.0) ) + { + bForceUnsetGTOrGCPs = TRUE; + } + + memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 ); + bGeoTransformValid = TRUE; + bGeoTIFFInfoChanged = TRUE; + + return( CE_None ); + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Attempt to call SetGeoTransform() on a read-only GeoTIFF file." ); + return CE_Failure; + } +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int GTiffDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *GTiffDataset::GetGCPProjection() + +{ + if( nGCPCount > 0 ) + { + LookForProjection(); + } + if (pszProjection != NULL) + return pszProjection; + else + return ""; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *GTiffDataset::GetGCPs() + +{ + return pasGCPList; +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +CPLErr GTiffDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ) +{ + if( GetAccess() == GA_Update ) + { + LookForProjection(); + + if (this->nGCPCount > 0 && nGCPCount == 0) + bForceUnsetGTOrGCPs = TRUE; + if( !EQUAL(this->pszProjection, "") && + (pszGCPProjection == NULL || + pszGCPProjection[0] == '\0') ) + bForceUnsetProjection = TRUE; + + if( this->nGCPCount > 0 ) + { + GDALDeinitGCPs( this->nGCPCount, this->pasGCPList ); + CPLFree( this->pasGCPList ); + } + + this->nGCPCount = nGCPCount; + this->pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPList); + + CPLFree( this->pszProjection ); + this->pszProjection = CPLStrdup( pszGCPProjection ); + bGeoTIFFInfoChanged = TRUE; + + return CE_None; + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "SetGCPs() is only supported on newly created GeoTIFF files." ); + return CE_Failure; + } +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **GTiffDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(CSLDuplicate(oGTiffMDMD.GetDomainList()), + TRUE, + "", "ProxyOverviewRequest", MD_DOMAIN_RPC, MD_DOMAIN_IMD, "SUBDATASETS", "EXIF", + "xml:XMP", "COLOR_PROFILE", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GTiffDataset::GetMetadata( const char * pszDomain ) + +{ + if( pszDomain != NULL && EQUAL(pszDomain,"ProxyOverviewRequest") ) + return GDALPamDataset::GetMetadata( pszDomain ); + + else if( pszDomain != NULL && (EQUAL(pszDomain, MD_DOMAIN_RPC) || + EQUAL(pszDomain, MD_DOMAIN_IMD) || + EQUAL(pszDomain, MD_DOMAIN_IMAGERY)) ) + LoadMetadata(); + + else if( pszDomain != NULL && EQUAL(pszDomain,"SUBDATASETS") ) + ScanDirectories(); + + else if( pszDomain != NULL && EQUAL(pszDomain,"EXIF") ) + LoadEXIFMetadata(); + + else if( pszDomain != NULL && EQUAL(pszDomain,"COLOR_PROFILE") ) + LoadICCProfile(); + + else if( pszDomain == NULL || EQUAL(pszDomain, "") ) + LoadMDAreaOrPoint(); /* to set GDALMD_AREA_OR_POINT */ + + return oGTiffMDMD.GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ +CPLErr GTiffDataset::SetMetadata( char ** papszMD, const char *pszDomain ) + +{ + if( bStreamingOut && bCrystalized ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot modify metadata at that point in a streamed output file"); + return CE_Failure; + } + + if ((papszMD != NULL) && (pszDomain != NULL) && EQUAL(pszDomain, "COLOR_PROFILE")) + bColorProfileMetadataChanged = TRUE; + else if( pszDomain == NULL || !EQUAL(pszDomain,"_temporary_") ) + { + bMetadataChanged = TRUE; + // Cancel any existing metadata from PAM file + if( eAccess == GA_Update && + GDALPamDataset::GetMetadata(pszDomain) != NULL ) + GDALPamDataset::SetMetadata(papszMD, pszDomain); + } + + if( (pszDomain == NULL || EQUAL(pszDomain, "")) && + CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT) != NULL ) + { + const char* pszPrevValue = + GetMetadataItem(GDALMD_AREA_OR_POINT); + const char* pszNewValue = + CSLFetchNameValue(papszMD, GDALMD_AREA_OR_POINT); + if (pszPrevValue == NULL || pszNewValue == NULL || + !EQUAL(pszPrevValue, pszNewValue)) + { + LookForProjection(); + bGeoTIFFInfoChanged = TRUE; + } + } + + return oGTiffMDMD.SetMetadata( papszMD, pszDomain ); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *GTiffDataset::GetMetadataItem( const char * pszName, + const char * pszDomain ) + +{ + if( pszDomain != NULL && EQUAL(pszDomain,"ProxyOverviewRequest") ) + return GDALPamDataset::GetMetadataItem( pszName, pszDomain ); + + else if( pszDomain != NULL && (EQUAL(pszDomain, MD_DOMAIN_RPC) || + EQUAL(pszDomain, MD_DOMAIN_IMD) || + EQUAL(pszDomain, MD_DOMAIN_IMAGERY)) ) + LoadMetadata(); + + else if( pszDomain != NULL && EQUAL(pszDomain,"SUBDATASETS") ) + ScanDirectories(); + + else if( pszDomain != NULL && EQUAL(pszDomain,"EXIF") ) + LoadEXIFMetadata(); + + else if( pszDomain != NULL && EQUAL(pszDomain,"COLOR_PROFILE") ) + LoadICCProfile(); + + else if( (pszDomain == NULL || EQUAL(pszDomain, "")) && + pszName != NULL && EQUAL(pszName, GDALMD_AREA_OR_POINT) ) + { + LoadMDAreaOrPoint(); /* to set GDALMD_AREA_OR_POINT */ + } + +#ifdef DEBUG_REACHED_VIRTUAL_MEM_IO + else if( pszDomain != NULL && EQUAL(pszDomain, "_DEBUG_") && + pszName != NULL && EQUAL(pszName, "UNREACHED_VIRTUALMEMIO_CODE_PATH") ) + { + CPLString osMissing; + for(int i=0;i<(int)(sizeof(anReachedVirtualMemIO)/sizeof(anReachedVirtualMemIO[0]));i++) + { + if( !anReachedVirtualMemIO[i] ) + { + if( osMissing.size() ) osMissing += ","; + osMissing += CPLSPrintf("%d", i); + } + } + return (osMissing.size()) ? CPLSPrintf("%s", osMissing.c_str()) : NULL; + } +#endif + + return oGTiffMDMD.GetMetadataItem( pszName, pszDomain ); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GTiffDataset::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ + if( bStreamingOut && bCrystalized ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot modify metadata at that point in a streamed output file"); + return CE_Failure; + } + + if ((pszDomain != NULL) && EQUAL(pszDomain, "COLOR_PROFILE")) + bColorProfileMetadataChanged = TRUE; + else if( pszDomain == NULL || !EQUAL(pszDomain,"_temporary_") ) + { + bMetadataChanged = TRUE; + // Cancel any existing metadata from PAM file + if( eAccess == GA_Update && + GDALPamDataset::GetMetadataItem(pszName, pszDomain) != NULL ) + GDALPamDataset::SetMetadataItem(pszName, NULL, pszDomain); + } + + if( (pszDomain == NULL || EQUAL(pszDomain, "")) && + pszName != NULL && EQUAL(pszName, GDALMD_AREA_OR_POINT) ) + { + LookForProjection(); + bGeoTIFFInfoChanged = TRUE; + } + + return oGTiffMDMD.SetMetadataItem( pszName, pszValue, pszDomain ); +} + +/************************************************************************/ +/* GetInternalHandle() */ +/************************************************************************/ + +void *GTiffDataset::GetInternalHandle( const char * /* pszHandleName */ ) + +{ + return hTIFF; +} + +/************************************************************************/ +/* LoadEXIFMetadata() */ +/************************************************************************/ + +void GTiffDataset::LoadEXIFMetadata() +{ + if (bEXIFMetadataLoaded) + return; + bEXIFMetadataLoaded = TRUE; + + if (!SetDirectory()) + return; + + VSILFILE* fp = VSI_TIFFGetVSILFile(TIFFClientdata( hTIFF )); + + GByte abyHeader[2]; + VSIFSeekL(fp, 0, SEEK_SET); + VSIFReadL(abyHeader, 1, 2, fp); + + int bLittleEndian = abyHeader[0] == 'I' && abyHeader[1] == 'I'; + int bSwabflag = bLittleEndian ^ CPL_IS_LSB; + + char** papszMetadata = NULL; + toff_t nOffset; + + if (TIFFGetField(hTIFF, TIFFTAG_EXIFIFD, &nOffset)) + { + int nExifOffset = (int)nOffset, nInterOffset = 0, nGPSOffset = 0; + EXIFExtractMetadata(papszMetadata, + fp, (int)nOffset, + bSwabflag, 0, + nExifOffset, nInterOffset, nGPSOffset); + } + + if (TIFFGetField(hTIFF, TIFFTAG_GPSIFD, &nOffset)) + { + int nExifOffset = 0, nInterOffset = 0, nGPSOffset = (int)nOffset; + EXIFExtractMetadata(papszMetadata, + fp, (int)nOffset, + bSwabflag, 0, + nExifOffset, nInterOffset, nGPSOffset); + } + + oGTiffMDMD.SetMetadata( papszMetadata, "EXIF" ); + CSLDestroy( papszMetadata ); +} + +/************************************************************************/ +/* LoadMetadata() */ +/************************************************************************/ +void GTiffDataset::LoadMetadata() +{ + if(TRUE == bIMDRPCMetadataLoaded) + return; + bIMDRPCMetadataLoaded = TRUE; + + GDALMDReaderManager mdreadermanager; + GDALMDReaderBase* mdreader = mdreadermanager.GetReader(osFilename, + oOvManager.GetSiblingFiles(), MDR_ANY); + + if(NULL != mdreader) + { + mdreader->FillMetadata(&oGTiffMDMD); + + if(mdreader->GetMetadataDomain(MD_DOMAIN_RPC) == NULL) + { + char** papszRPCMD = GTiffDatasetReadRPCTag(hTIFF); + if( papszRPCMD ) + { + oGTiffMDMD.SetMetadata( papszRPCMD, MD_DOMAIN_RPC ); + CSLDestroy( papszRPCMD ); + } + } + + papszMetadataFiles = mdreader->GetMetadataFiles(); + } + else + { + char** papszRPCMD = GTiffDatasetReadRPCTag(hTIFF); + if( papszRPCMD ) + { + oGTiffMDMD.SetMetadata( papszRPCMD, MD_DOMAIN_RPC ); + CSLDestroy( papszRPCMD ); + } + } +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **GTiffDataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + + LoadMetadata(); + if(NULL != papszMetadataFiles) + { + for( int i = 0; papszMetadataFiles[i] != NULL; i++ ) + { + papszFileList = CSLAddString( papszFileList, papszMetadataFiles[i] ); + } + } + + if (osGeorefFilename.size() != 0 && + CSLFindString(papszFileList, osGeorefFilename) == -1) + { + papszFileList = CSLAddString( papszFileList, osGeorefFilename ); + } + + return papszFileList; +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +CPLErr GTiffDataset::CreateMaskBand(int nFlags) +{ + ScanDirectories(); + + if (poMaskDS != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "This TIFF dataset has already an internal mask band"); + return CE_Failure; + } + else if (CSLTestBoolean(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", "NO"))) + { + toff_t nOffset; + int bIsTiled; + int bIsOverview = FALSE; + uint32 nSubType; + int nCompression; + + if (nFlags != GMF_PER_DATASET) + { + CPLError(CE_Failure, CPLE_AppDefined, + "The only flag value supported for internal mask is GMF_PER_DATASET"); + return CE_Failure; + } + + if( strstr(GDALGetMetadataItem(GDALGetDriverByName( "GTiff" ), + GDAL_DMD_CREATIONOPTIONLIST, NULL ), + "DEFLATE") != NULL ) + nCompression = COMPRESSION_ADOBE_DEFLATE; + else + nCompression = COMPRESSION_PACKBITS; + + /* -------------------------------------------------------------------- */ + /* If we don't have read access, then create the mask externally. */ + /* -------------------------------------------------------------------- */ + if( GetAccess() != GA_Update ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "File open for read-only accessing, " + "creating mask externally." ); + + return GDALPamDataset::CreateMaskBand(nFlags); + } + + if (poBaseDS) + { + if (!poBaseDS->SetDirectory()) + return CE_Failure; + } + if (!SetDirectory()) + return CE_Failure; + + if( TIFFGetField(hTIFF, TIFFTAG_SUBFILETYPE, &nSubType)) + { + bIsOverview = (nSubType & FILETYPE_REDUCEDIMAGE) != 0; + + if ((nSubType & FILETYPE_MASK) != 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot create a mask on a TIFF mask IFD !" ); + return CE_Failure; + } + } + + bIsTiled = TIFFIsTiled(hTIFF); + + FlushDirectory(); + + nOffset = GTIFFWriteDirectory(hTIFF, + (bIsOverview) ? FILETYPE_REDUCEDIMAGE | FILETYPE_MASK : FILETYPE_MASK, + nRasterXSize, nRasterYSize, + 1, PLANARCONFIG_CONTIG, 1, + nBlockXSize, nBlockYSize, + bIsTiled, nCompression, + PHOTOMETRIC_MASK, PREDICTOR_NONE, + SAMPLEFORMAT_UINT, NULL, NULL, NULL, 0, NULL, ""); + if (nOffset == 0) + return CE_Failure; + + poMaskDS = new GTiffDataset(); + poMaskDS->bPromoteTo8Bits = CSLTestBoolean(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK_TO_8BIT", "YES")); + if( poMaskDS->OpenOffset( hTIFF, ppoActiveDSRef, nOffset, + FALSE, GA_Update ) != CE_None) + { + delete poMaskDS; + poMaskDS = NULL; + return CE_Failure; + } + + return CE_None; + } + else + { + return GDALPamDataset::CreateMaskBand(nFlags); + } +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +CPLErr GTiffRasterBand::CreateMaskBand(int nFlags) +{ + poGDS->ScanDirectories(); + + if (poGDS->poMaskDS != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "This TIFF dataset has already an internal mask band"); + return CE_Failure; + } + else if (CSLTestBoolean(CPLGetConfigOption("GDAL_TIFF_INTERNAL_MASK", "NO"))) + { + return poGDS->CreateMaskBand(nFlags); + } + else + { + return GDALPamRasterBand::CreateMaskBand(nFlags); + } +} + +/************************************************************************/ +/* PrepareTIFFErrorFormat() */ +/* */ +/* sometimes the "module" has stuff in it that has special */ +/* meaning in a printf() style format, so we try to escape it. */ +/* For now we hope the only thing we have to escape is %'s. */ +/************************************************************************/ + +static char *PrepareTIFFErrorFormat( const char *module, const char *fmt ) + +{ + char *pszModFmt; + int iIn, iOut; + + pszModFmt = (char *) CPLMalloc( strlen(module)*2 + strlen(fmt) + 2 ); + for( iOut = 0, iIn = 0; module[iIn] != '\0'; iIn++ ) + { + if( module[iIn] == '%' ) + { + pszModFmt[iOut++] = '%'; + pszModFmt[iOut++] = '%'; + } + else + pszModFmt[iOut++] = module[iIn]; + } + pszModFmt[iOut] = '\0'; + strcat( pszModFmt, ":" ); + strcat( pszModFmt, fmt ); + + return pszModFmt; +} + +/************************************************************************/ +/* GTiffWarningHandler() */ +/************************************************************************/ +void +GTiffWarningHandler(const char* module, const char* fmt, va_list ap ) +{ + char *pszModFmt; + + if( strstr(fmt,"nknown field") != NULL ) + return; + + pszModFmt = PrepareTIFFErrorFormat( module, fmt ); + if( strstr(fmt, "does not end in null byte") != NULL ) + { + CPLString osMsg; + osMsg.vPrintf(pszModFmt, ap); + CPLDebug( "GTiff", "%s", osMsg.c_str() ); + } + else + CPLErrorV( CE_Warning, CPLE_AppDefined, pszModFmt, ap ); + CPLFree( pszModFmt ); +} + +/************************************************************************/ +/* GTiffErrorHandler() */ +/************************************************************************/ +void +GTiffErrorHandler(const char* module, const char* fmt, va_list ap ) +{ + char *pszModFmt; + +#if SIZEOF_VOIDP == 4 + /* Case of one-strip file where the strip size is > 2GB (#5403) */ + if( strcmp(module, "TIFFStripSize") == 0 && + strstr(fmt, "Integer overflow") != NULL ) + { + bGlobalStripIntegerOverflow = TRUE; + return; + } + if( bGlobalStripIntegerOverflow && + strstr(fmt, "Cannot handle zero strip size") != NULL ) + { + return; + } +#endif + +#ifdef BIGTIFF_SUPPORT + if( strcmp(fmt, "Maximum TIFF file size exceeded") == 0 ) + { + fmt = "Maximum TIFF file size exceeded. Use BIGTIFF=YES creation option."; + } +#endif + + pszModFmt = PrepareTIFFErrorFormat( module, fmt ); + CPLErrorV( CE_Failure, CPLE_AppDefined, pszModFmt, ap ); + CPLFree( pszModFmt ); +} + +/************************************************************************/ +/* GTiffTagExtender() */ +/* */ +/* Install tags specially known to GDAL. */ +/************************************************************************/ + +static TIFFExtendProc _ParentExtender = NULL; + +static void GTiffTagExtender(TIFF *tif) + +{ + static const TIFFFieldInfo xtiffFieldInfo[] = { + { TIFFTAG_GDAL_METADATA, -1,-1, TIFF_ASCII, FIELD_CUSTOM, + TRUE, FALSE, (char*) "GDALMetadata" }, + { TIFFTAG_GDAL_NODATA, -1,-1, TIFF_ASCII, FIELD_CUSTOM, + TRUE, FALSE, (char*) "GDALNoDataValue" }, + { TIFFTAG_RPCCOEFFICIENT, -1,-1, TIFF_DOUBLE, FIELD_CUSTOM, + TRUE, TRUE, (char*) "RPCCoefficient" } + }; + + if (_ParentExtender) + (*_ParentExtender)(tif); + + TIFFMergeFieldInfo( tif, xtiffFieldInfo, + sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]) ); +} + +/************************************************************************/ +/* GTiffOneTimeInit() */ +/* */ +/* This is stuff that is initialized for the TIFF library just */ +/* once. We deliberately defer the initialization till the */ +/* first time we are likely to call into libtiff to avoid */ +/* unnecessary paging in of the library for GDAL apps that */ +/* don't use it. */ +/************************************************************************/ +#if defined(HAVE_DLFCN_H) && !defined(WIN32) +#include +#endif + +static CPLMutex* hGTiffOneTimeInitMutex = NULL; + +int GTiffOneTimeInit() + +{ + static int bInitIsOk = TRUE; + static int bOneTimeInitDone = FALSE; + CPLMutexHolder oHolder( &hGTiffOneTimeInitMutex); + if( bOneTimeInitDone ) + return bInitIsOk; + + bOneTimeInitDone = TRUE; + + /* This is a frequent configuration error that is difficult to track down */ + /* for people unaware of the issue : GDAL built against internal libtiff (4.X) */ + /* but used by an application that links with external libtiff (3.X) */ + /* Note: on my conf, the order that cause GDAL to crash - and that is detected */ + /* by the following code - is "-ltiff -lgdal". "-lgdal -ltiff" works for the */ + /* GTiff driver but probably breaks the application that believes it uses libtiff 3.X */ + /* but we cannot detect that... */ +#if defined(BIGTIFF_SUPPORT) && !defined(RENAME_INTERNAL_LIBTIFF_SYMBOLS) +#if defined(HAVE_DLFCN_H) && !defined(WIN32) + const char* (*pfnVersion)(void); + pfnVersion = (const char* (*)(void)) dlsym(RTLD_DEFAULT, "TIFFGetVersion"); + if (pfnVersion) + { + const char* pszVersion = pfnVersion(); + if (pszVersion && strstr(pszVersion, "Version 3.") != NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, + "libtiff version mismatch : You're linking against libtiff 3.X, but GDAL has been compiled against libtiff >= 4.0.0"); + } + } +#endif +#endif + + _ParentExtender = TIFFSetTagExtender(GTiffTagExtender); + + TIFFSetWarningHandler( GTiffWarningHandler ); + TIFFSetErrorHandler( GTiffErrorHandler ); + + // This only really needed if we are linked to an external libgeotiff + // with its own (lame) file searching logic. + LibgeotiffOneTimeInit(); + + return TRUE; +} + +/************************************************************************/ +/* GDALDeregister_GTiff() */ +/************************************************************************/ + +static +void GDALDeregister_GTiff( GDALDriver * ) + +{ + CSVDeaccess( NULL ); + +#if defined(LIBGEOTIFF_VERSION) && LIBGEOTIFF_VERSION > 1150 + GTIFDeaccessCSV(); +#endif + + if( hGTiffOneTimeInitMutex != NULL ) + { + CPLDestroyMutex(hGTiffOneTimeInitMutex); + hGTiffOneTimeInitMutex = NULL; + } + + LibgeotiffOneTimeCleanupMutex(); +} + +/************************************************************************/ +/* GTIFFGetCompressionMethod() */ +/************************************************************************/ + +int GTIFFGetCompressionMethod(const char* pszValue, const char* pszVariableName) +{ + int nCompression = COMPRESSION_NONE; + if( EQUAL( pszValue, "NONE" ) ) + nCompression = COMPRESSION_NONE; + else if( EQUAL( pszValue, "JPEG" ) ) + nCompression = COMPRESSION_JPEG; + else if( EQUAL( pszValue, "LZW" ) ) + nCompression = COMPRESSION_LZW; + else if( EQUAL( pszValue, "PACKBITS" )) + nCompression = COMPRESSION_PACKBITS; + else if( EQUAL( pszValue, "DEFLATE" ) || EQUAL( pszValue, "ZIP" )) + nCompression = COMPRESSION_ADOBE_DEFLATE; + else if( EQUAL( pszValue, "FAX3" ) + || EQUAL( pszValue, "CCITTFAX3" )) + nCompression = COMPRESSION_CCITTFAX3; + else if( EQUAL( pszValue, "FAX4" ) + || EQUAL( pszValue, "CCITTFAX4" )) + nCompression = COMPRESSION_CCITTFAX4; + else if( EQUAL( pszValue, "CCITTRLE" ) ) + nCompression = COMPRESSION_CCITTRLE; + else if( EQUAL( pszValue, "LZMA" ) ) + nCompression = COMPRESSION_LZMA; + else + CPLError( CE_Warning, CPLE_IllegalArg, + "%s=%s value not recognised, ignoring.", + pszVariableName,pszValue ); + +#if defined(TIFFLIB_VERSION) && TIFFLIB_VERSION > 20031007 /* 3.6.0 */ + if (nCompression != COMPRESSION_NONE && + !TIFFIsCODECConfigured((uint16) nCompression)) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot create TIFF file due to missing codec for %s.", pszValue ); + return -1; + } +#endif + + return nCompression; +} +/************************************************************************/ +/* GDALRegister_GTiff() */ +/************************************************************************/ + +void GDALRegister_GTiff() + +{ + if( GDALGetDriverByName( "GTiff" ) == NULL ) + { + GDALDriver *poDriver; + char szCreateOptions[5000]; + char szOptionalCompressItems[500]; + int bHasJPEG = FALSE, bHasLZW = FALSE, bHasDEFLATE = FALSE, bHasLZMA = FALSE; + + poDriver = new GDALDriver(); + +/* -------------------------------------------------------------------- */ +/* Determine which compression codecs are available that we */ +/* want to advertise. If we are using an old libtiff we won't */ +/* be able to find out so we just assume all are available. */ +/* -------------------------------------------------------------------- */ + strcpy( szOptionalCompressItems, + " NONE" ); + +#if TIFFLIB_VERSION <= 20040919 + strcat( szOptionalCompressItems, + " PACKBITS" + " JPEG" + " LZW" + " DEFLATE" ); + bHasLZW = bHasDEFLATE = TRUE; +#else + TIFFCodec *c, *codecs = TIFFGetConfiguredCODECs(); + + for( c = codecs; c->name; c++ ) + { + if( c->scheme == COMPRESSION_PACKBITS ) + strcat( szOptionalCompressItems, + " PACKBITS" ); + else if( c->scheme == COMPRESSION_JPEG ) + { + bHasJPEG = TRUE; + strcat( szOptionalCompressItems, + " JPEG" ); + } + else if( c->scheme == COMPRESSION_LZW ) + { + bHasLZW = TRUE; + strcat( szOptionalCompressItems, + " LZW" ); + } + else if( c->scheme == COMPRESSION_ADOBE_DEFLATE ) + { + bHasDEFLATE = TRUE; + strcat( szOptionalCompressItems, + " DEFLATE" ); + } + else if( c->scheme == COMPRESSION_CCITTRLE ) + strcat( szOptionalCompressItems, + " CCITTRLE" ); + else if( c->scheme == COMPRESSION_CCITTFAX3 ) + strcat( szOptionalCompressItems, + " CCITTFAX3" ); + else if( c->scheme == COMPRESSION_CCITTFAX4 ) + strcat( szOptionalCompressItems, + " CCITTFAX4" ); + else if( c->scheme == COMPRESSION_LZMA ) + { + bHasLZMA = TRUE; + strcat( szOptionalCompressItems, + " LZMA" ); + } + } + _TIFFfree( codecs ); +#endif + +/* -------------------------------------------------------------------- */ +/* Build full creation option list. */ +/* -------------------------------------------------------------------- */ + sprintf( szCreateOptions, "%s%s%s", +"" +" "); + if (bHasLZW || bHasDEFLATE) + strcat( szCreateOptions, "" +" " +" " +" " +" " +" " +#ifdef BIGTIFF_SUPPORT +" " +#endif +" " +" " ); + +/* -------------------------------------------------------------------- */ +/* Set the driver details. */ +/* -------------------------------------------------------------------- */ + poDriver->SetDescription( "GTiff" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "GeoTIFF" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_gtiff.html" ); + poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/tiff" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "tif" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "tif tiff" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16 Int16 UInt32 Int32 Float32 " + "Float64 CInt16 CInt32 CFloat32 CFloat64" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, + szCreateOptions ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + +#ifdef INTERNAL_LIBTIFF + poDriver->SetMetadataItem( "LIBTIFF", "INTERNAL" ); +#else + poDriver->SetMetadataItem( "LIBTIFF", TIFFLIB_VERSION_STR ); +#endif + + poDriver->pfnOpen = GTiffDataset::Open; + poDriver->pfnCreate = GTiffDataset::Create; + poDriver->pfnCreateCopy = GTiffDataset::CreateCopy; + poDriver->pfnUnloadDriver = GDALDeregister_GTiff; + poDriver->pfnIdentify = GTiffDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/gtiff/gt_citation.cpp b/bazaar/plugin/gdal/frmts/gtiff/gt_citation.cpp new file mode 100644 index 000000000..80470cf07 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/gt_citation.cpp @@ -0,0 +1,770 @@ +/****************************************************************************** + * $Id: gt_citation.cpp 28263 2014-12-29 22:00:08Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Implements special parsing of Imagine citation strings, and + * to encode PE String info in citation fields as needed. + * Author: Xiuguang Zhou (ESRI) + * + ****************************************************************************** + * Copyright (c) 2008, Xiuguang Zhou (ESRI) + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_port.h" +#include "cpl_string.h" + +#include "geovalues.h" +#include "gt_citation.h" +#include "gt_wkt_srs_priv.h" + +CPL_CVSID("$Id: gt_citation.cpp 28263 2014-12-29 22:00:08Z rouault $"); + +static const char *apszUnitMap[] = { + "meters", "1.0", + "meter", "1.0", + "m", "1.0", + "centimeters", "0.01", + "centimeter", "0.01", + "cm", "0.01", + "millimeters", "0.001", + "millimeter", "0.001", + "mm", "0.001", + "kilometers", "1000.0", + "kilometer", "1000.0", + "km", "1000.0", + "us_survey_feet", "0.3048006096012192", + "us_survey_foot", "0.3048006096012192", + "feet", "0.3048006096012192", + "foot", "0.3048006096012192", + "ft", "0.3048006096012192", + "international_feet", "0.3048", + "international_foot", "0.3048", + "inches", "0.0254000508001", + "inch", "0.0254000508001", + "in", "0.0254000508001", + "yards", "0.9144", + "yard", "0.9144", + "yd", "0.9144", + "miles", "1304.544", + "mile", "1304.544", + "mi", "1304.544", + "modified_american_feet", "0.3048122530", + "modified_american_foot", "0.3048122530", + "clarke_feet", "0.3047972651", + "clarke_foot", "0.3047972651", + "indian_feet", "0.3047995142", + "indian_foot", "0.3047995142", + "Yard_Indian", "0.9143985307444408", + "Foot_Clarke", "0.30479726540", + "Foot_Gold_Coast", "0.3047997101815088", + "Link_Clarke", "0.2011661951640", + "Yard_Sears", "0.9143984146160287", + "50_Kilometers", "50000.0", + "150_Kilometers", "150000.0", + NULL, NULL +}; + +/************************************************************************/ +/* ImagineCitationTranslation() */ +/* */ +/* Translate ERDAS Imagine GeoTif citation */ +/************************************************************************/ +char* ImagineCitationTranslation(char* psCitation, geokey_t keyID) +{ + static const char *keyNames[] = { + "NAD = ", "Datum = ", "Ellipsoid = ", "Units = ", NULL + }; + + char* ret = NULL; + int i; + if(!psCitation) + return ret; + if(EQUALN(psCitation, "IMAGINE GeoTIFF Support", strlen("IMAGINE GeoTIFF Support"))) + { + // this is a handle IMAGING style citation + char name[256]; + name[0] = '\0'; + char* p = NULL; + char* p1 = NULL; + + p = strchr(psCitation, '$'); + if( p && strchr(p, '\n') ) + p = strchr(p, '\n') + 1; + if(p) + { + p1 = p + strlen(p); + char *p2 = strchr(p, '\n'); + if(p2) + p1 = MIN(p1, p2); + p2 = strchr(p, '\0'); + if(p2) + p1 = MIN(p1, p2); + for(i=0; keyNames[i]!=NULL; i++) + { + p2 = strstr(p, keyNames[i]); + if(p2) + p1 = MIN(p1, p2); + } + } + + // PCS name, GCS name and PRJ name + if(p && p1) + { + switch (keyID) + { + case PCSCitationGeoKey: + if(strstr(psCitation, "Projection = ")) + strcpy(name, "PRJ Name = "); + else + strcpy(name, "PCS Name = "); + break; + case GTCitationGeoKey: + strcpy(name, "PCS Name = "); + break; + case GeogCitationGeoKey: + if(!strstr(p, "Unable to")) + strcpy(name, "GCS Name = "); + break; + default: + break; + } + if(strlen(name)>0) + { + char* p2; + if((p2 = strstr(psCitation, "Projection Name = ")) != 0) + p = p2 + strlen("Projection Name = "); + if((p2 = strstr(psCitation, "Projection = ")) != 0) + p = p2 + strlen("Projection = "); + if(p1[0] == '\0' || p1[0] == '\n' || p1[0] == ' ') + p1 --; + p2 = p1 - 1; + while( p2 != 0 && (p2[0] == ' ' || p2[0] == '\0' || p2[0] == '\n') ) + p2--; + if(p2 != p1 - 1) + p1 = p2; + if(p1 >= p) + { + strncat(name, p, p1-p+1); + strcat(name, "|"); + name[strlen(name)] = '\0'; + } + } + } + + // All other parameters + for(i=0; keyNames[i]!=NULL; i++) + { + p = strstr(psCitation, keyNames[i]); + if(p) + { + p += strlen(keyNames[i]); + p1 = p + strlen(p); + char *p2 = strchr(p, '\n'); + if(p2) + p1 = MIN(p1, p2); + p2 = strchr(p, '\0'); + if(p2) + p1 = MIN(p1, p2); + for(int j=0; keyNames[j]!=NULL; j++) + { + p2 = strstr(p, keyNames[j]); + if(p2) + p1 = MIN(p1, p2); + } + } + if(p && p1 && p1>p) + { + if(EQUAL(keyNames[i], "Units = ")) + strcat(name, "LUnits = "); + else + strcat(name, keyNames[i]); + if(p1[0] == '\0' || p1[0] == '\n' || p1[0] == ' ') + p1 --; + char* p2 = p1 - 1; + while( p2 != 0 && (p2[0] == ' ' || p2[0] == '\0' || p2[0] == '\n') ) + p2--; + if(p2 != p1 - 1) + p1 = p2; + if(p1 >= p) + { + strncat(name, p, p1-p+1); + strcat(name, "|"); + name[strlen(name)] = '\0'; + } + } + } + if(strlen(name) > 0) + ret = CPLStrdup(name); + } + return ret; + +} + +/************************************************************************/ +/* CitationStringParse() */ +/* */ +/* Parse a Citation string */ +/************************************************************************/ + +char** CitationStringParse(char* psCitation, geokey_t keyID) +{ + char ** ret = NULL; + if(!psCitation) + return ret; + + ret = (char **) CPLCalloc(sizeof(char*), nCitationNameTypes); + char* pDelimit = NULL; + char* pStr = psCitation; + char name[512]; + int nameSet = FALSE; + int nameLen = strlen(psCitation); + OGRBoolean nameFound = FALSE; + while((pStr-psCitation+1)< nameLen) + { + if( (pDelimit = strstr(pStr, "|")) != NULL ) + { + strncpy( name, pStr, pDelimit-pStr ); + name[pDelimit-pStr] = '\0'; + pStr = pDelimit+1; + nameSet = TRUE; + } + else + { + strcpy (name, pStr); + pStr += strlen(pStr); + nameSet = TRUE; + } + if( strstr(name, "PCS Name = ") ) + { + ret[CitPcsName] = CPLStrdup(name+strlen("PCS Name = ")); + nameFound = TRUE; + } + if(strstr(name, "PRJ Name = ")) + { + ret[CitProjectionName] = CPLStrdup(name+strlen("PRJ Name = ")); + nameFound = TRUE; + } + if(strstr(name, "LUnits = ")) + { + ret[CitLUnitsName] = CPLStrdup(name+strlen("LUnits = ")); + nameFound = TRUE; + } + if(strstr(name, "GCS Name = ")) + { + ret[CitGcsName] = CPLStrdup(name+strlen("GCS Name = ")); + nameFound = TRUE; + } + if(strstr(name, "Datum = ")) + { + ret[CitDatumName] = CPLStrdup(name+strlen("Datum = ")); + nameFound = TRUE; + } + if(strstr(name, "Ellipsoid = ")) + { + ret[CitEllipsoidName] = CPLStrdup(name+strlen("Ellipsoid = ")); + nameFound = TRUE; + } + if(strstr(name, "Primem = ")) + { + ret[CitPrimemName] = CPLStrdup(name+strlen("Primem = ")); + nameFound = TRUE; + } + if(strstr(name, "AUnits = ")) + { + ret[CitAUnitsName] = CPLStrdup(name+strlen("AUnits = ")); + nameFound = TRUE; + } + } + if( !nameFound && keyID == GeogCitationGeoKey && nameSet ) + { + ret[CitGcsName] = CPLStrdup(name); + nameFound = TRUE; + } + if(!nameFound) + { + CPLFree( ret ); + ret = (char**)NULL; + } + return ret; +} + + +/************************************************************************/ +/* SetLinearUnitCitation() */ +/* */ +/* Set linear unit Citation string */ +/************************************************************************/ +void SetLinearUnitCitation(GTIF* psGTIF, char* pszLinearUOMName) +{ + char szName[512]; + CPLString osCitation; + int n = 0; + if( GDALGTIFKeyGetASCII( psGTIF, PCSCitationGeoKey, szName, 0, sizeof(szName) ) ) + n = strlen(szName); + if(n>0) + { + osCitation = szName; + if(osCitation[n-1] != '|') + osCitation += "|"; + osCitation += "LUnits = "; + osCitation += pszLinearUOMName; + osCitation += "|"; + } + else + { + osCitation = "LUnits = "; + osCitation += pszLinearUOMName; + } + GTIFKeySet( psGTIF, PCSCitationGeoKey, TYPE_ASCII, 0, osCitation.c_str() ); + return; +} + +/************************************************************************/ +/* SetGeogCSCitation() */ +/* */ +/* Set geogcs Citation string */ +/************************************************************************/ +void SetGeogCSCitation(GTIF * psGTIF, OGRSpatialReference *poSRS, char* angUnitName, int nDatum, short nSpheroid) +{ + int bRewriteGeogCitation = FALSE; + char szName[256]; + CPLString osCitation; + size_t n = 0; + if( GDALGTIFKeyGetASCII( psGTIF, GeogCitationGeoKey, szName, 0, sizeof(szName) ) ) + n = strlen(szName); + if (n == 0) + return; + + if(!EQUALN(szName, "GCS Name = ", strlen("GCS Name = "))) + { + osCitation = "GCS Name = "; + osCitation += szName; + } + else + { + osCitation = szName; + } + + if(nDatum == KvUserDefined ) + { + const char* datumName = poSRS->GetAttrValue( "DATUM" ); + if(datumName && strlen(datumName) > 0) + { + osCitation += "|Datum = "; + osCitation += datumName; + bRewriteGeogCitation = TRUE; + } + } + if(nSpheroid == KvUserDefined ) + { + const char* spheroidName = poSRS->GetAttrValue( "SPHEROID" ); + if(spheroidName && strlen(spheroidName) > 0) + { + osCitation += "|Ellipsoid = "; + osCitation += spheroidName; + bRewriteGeogCitation = TRUE; + } + } + + const char* primemName = poSRS->GetAttrValue( "PRIMEM" ); + if(primemName && strlen(primemName) > 0) + { + osCitation += "|Primem = "; + osCitation += primemName; + bRewriteGeogCitation = TRUE; + + double primemValue = poSRS->GetPrimeMeridian(NULL); + if(angUnitName && !EQUAL(angUnitName, "Degree")) + { + double aUnit = poSRS->GetAngularUnits(NULL); + primemValue *= aUnit; + } + GTIFKeySet( psGTIF, GeogPrimeMeridianLongGeoKey, TYPE_DOUBLE, 1, + primemValue ); + } + if(angUnitName && strlen(angUnitName) > 0 && !EQUAL(angUnitName, "Degree")) + { + osCitation += "|AUnits = "; + osCitation += angUnitName; + bRewriteGeogCitation = TRUE; + } + + if (osCitation[strlen(osCitation) - 1] != '|') + osCitation += "|"; + + if (bRewriteGeogCitation) + GTIFKeySet( psGTIF, GeogCitationGeoKey, TYPE_ASCII, 0, osCitation.c_str() ); + + return; +} + +/************************************************************************/ +/* SetCitationToSRS() */ +/* */ +/* Parse and set Citation string to SRS */ +/************************************************************************/ +OGRBoolean SetCitationToSRS(GTIF* hGTIF, char* szCTString, int nCTStringLen, + geokey_t geoKey, OGRSpatialReference* poSRS, OGRBoolean* linearUnitIsSet) +{ + OGRBoolean ret = FALSE; + char* lUnitName = NULL; + + poSRS->GetLinearUnits( &lUnitName ); + if(!lUnitName || strlen(lUnitName) == 0 || EQUAL(lUnitName, "unknown")) + *linearUnitIsSet = FALSE; + else + *linearUnitIsSet = TRUE; + + char* imgCTName = ImagineCitationTranslation(szCTString, geoKey); + if(imgCTName) + { + strncpy(szCTString, imgCTName, nCTStringLen); + szCTString[nCTStringLen-1] = '\0'; + CPLFree( imgCTName ); + } + char** ctNames = CitationStringParse(szCTString, geoKey); + if(ctNames) + { + if( poSRS->GetRoot() == NULL) + poSRS->SetNode( "PROJCS", "unnamed" ); + if(ctNames[CitPcsName]) + { + poSRS->SetNode( "PROJCS", ctNames[CitPcsName] ); + ret = TRUE; + } + if(ctNames[CitProjectionName]) + poSRS->SetProjection( ctNames[CitProjectionName] ); + + if(ctNames[CitLUnitsName]) + { + double unitSize = 0.0; + int size = strlen(ctNames[CitLUnitsName]); + if(strchr(ctNames[CitLUnitsName], '\0')) + size -= 1; + for( int i = 0; apszUnitMap[i] != NULL; i += 2 ) + { + if( EQUALN(apszUnitMap[i], ctNames[CitLUnitsName], size) ) + { + unitSize = CPLAtof(apszUnitMap[i+1]); + break; + } + } + if( unitSize == 0.0 ) + GDALGTIFKeyGetDOUBLE(hGTIF, ProjLinearUnitSizeGeoKey, &unitSize, 0, 1 ); + poSRS->SetLinearUnits( ctNames[CitLUnitsName], unitSize); + *linearUnitIsSet = TRUE; + } + for(int i= 0; i 0) && !strstr(szCTString, "Projected Coordinates")) + ||(pszProjCS && strstr(pszProjCS, "unnamed"))) + poSRS->SetNode( "PROJCS", szCTString ); + ret = TRUE; + } + } + + return ret; +} + +/************************************************************************/ +/* GetGeogCSFromCitation() */ +/* */ +/* Parse and get geogcs names from a Citation string */ +/************************************************************************/ +void GetGeogCSFromCitation(char* szGCSName, int nGCSName, + geokey_t geoKey, + char **ppszGeogName, + char **ppszDatumName, + char **ppszPMName, + char **ppszSpheroidName, + char **ppszAngularUnits) +{ + *ppszGeogName = *ppszDatumName = *ppszPMName = + *ppszSpheroidName = *ppszAngularUnits = NULL; + + char* imgCTName = ImagineCitationTranslation(szGCSName, geoKey); + if(imgCTName) + { + strncpy(szGCSName, imgCTName, nGCSName); + szGCSName[nGCSName-1] = '\0'; + CPLFree( imgCTName ); + } + char** ctNames = CitationStringParse(szGCSName, geoKey); + if(ctNames) + { + if(ctNames[CitGcsName]) + *ppszGeogName = CPLStrdup( ctNames[CitGcsName] ); + + if(ctNames[CitDatumName]) + *ppszDatumName = CPLStrdup( ctNames[CitDatumName] ); + + if(ctNames[CitEllipsoidName]) + *ppszSpheroidName = CPLStrdup( ctNames[CitEllipsoidName] ); + + if(ctNames[CitPrimemName]) + *ppszPMName = CPLStrdup( ctNames[CitPrimemName] ); + + if(ctNames[CitAUnitsName]) + *ppszAngularUnits = CPLStrdup( ctNames[CitAUnitsName] ); + + for(int i= 0; iPCS != KvUserDefined ) + return FALSE; +#endif + + char szCTString[512]; + szCTString[0] = '\0'; + + /* Check units */ + char units[32]; + units[0] = '\0'; + + OGRBoolean hasUnits = FALSE; + if( GDALGTIFKeyGetASCII( hGTIF, GTCitationGeoKey, szCTString, 0, sizeof(szCTString) ) ) + { + CPLString osLCCT = szCTString; + + osLCCT.tolower(); + + if( strstr(osLCCT,"us") && strstr(osLCCT,"survey") + && (strstr(osLCCT,"feet") || strstr(osLCCT,"foot")) ) + strcpy(units, "us_survey_feet"); + else if(strstr(osLCCT, "linear_feet") + || strstr(osLCCT, "linear_foot") + || strstr(osLCCT, "international")) + strcpy(units, "international_feet"); + else if( strstr(osLCCT,"meter") ) + strcpy(units, "meters"); + + if (strlen(units) > 0) + hasUnits = TRUE; + + if( strstr( szCTString, "Projection Name = ") && strstr( szCTString, "_StatePlane_")) + { + const char *pStr = strstr( szCTString, "Projection Name = ") + strlen("Projection Name = "); + const char* pReturn = strchr( pStr, '\n'); + char CSName[128]; + strncpy(CSName, pStr, pReturn-pStr); + CSName[pReturn-pStr] = '\0'; + if( poSRS->ImportFromESRIStatePlaneWKT(0, NULL, NULL, 32767, CSName) == OGRERR_NONE ) + { + // for some erdas citation keys, the state plane CS name is incomplete, the unit check is necessary. + OGRBoolean done = FALSE; + if (hasUnits) + { + OGR_SRSNode *poUnit = poSRS->GetAttrNode( "PROJCS|UNIT" ); + + if( poUnit != NULL && poUnit->GetChildCount() >= 2 ) + { + CPLString unitName = poUnit->GetChild(0)->GetValue(); + unitName.tolower(); + + if (strstr(units, "us_survey_feet")) + { + if (strstr(unitName, "us_survey_feet") || strstr(unitName, "foot_us") ) + done = TRUE; + } + else if (strstr(units, "international_feet")) + { + if (strstr(unitName, "feet") || strstr(unitName, "foot")) + done = TRUE; + } + else if (strstr(units, "meters")) + { + if (strstr(unitName, "meter") ) + done = TRUE; + } + } + } + if (done) + return TRUE; + } + } + } + if( !hasUnits ) + { + char *pszUnitsName = NULL; + GTIFGetUOMLengthInfo( psDefn->UOMLength, &pszUnitsName, NULL ); + if( pszUnitsName && strlen(pszUnitsName) > 0 ) + { + CPLString osLCCT = pszUnitsName; + GTIFFreeMemory( pszUnitsName ); + osLCCT.tolower(); + + if( strstr(osLCCT, "us") && strstr(osLCCT, "survey") + && (strstr(osLCCT, "feet") || strstr(osLCCT, "foot"))) + strcpy(units, "us_survey_feet"); + else if(strstr(osLCCT, "feet") || strstr(osLCCT, "foot")) + strcpy(units, "international_feet"); + else if(strstr(osLCCT, "meter")) + strcpy(units, "meters"); + hasUnits = TRUE; + } + } + + if (strlen(units) == 0) + strcpy(units, "meters"); + + /* check PCSCitationGeoKey if it exists */ + szCTString[0] = '\0'; + if( hGTIF && GDALGTIFKeyGetASCII( hGTIF, PCSCitationGeoKey, szCTString, 0, sizeof(szCTString)) ) + { + /* For tif created by LEICA(ERDAS), ESRI state plane pe string was used and */ + /* the state plane zone is given in PCSCitation. Therefore try Esri pe string first. */ + SetCitationToSRS(hGTIF, szCTString, strlen(szCTString), PCSCitationGeoKey, poSRS, pLinearUnitIsSet); + const char *pcsName = poSRS->GetAttrValue("PROJCS"); + const char *pStr = NULL; + if( (pcsName && (pStr = strstr(pcsName, "State Plane Zone ")) != NULL) + || (pStr = strstr(szCTString, "State Plane Zone ")) != NULL ) + { + pStr += strlen("State Plane Zone "); + int statePlaneZone = abs(atoi(pStr)); + char nad[32]; + strcpy(nad, "HARN"); + if( strstr(szCTString, "NAD83") || strstr(szCTString, "NAD = 83") ) + strcpy(nad, "NAD83"); + else if( strstr(szCTString, "NAD27") || strstr(szCTString, "NAD = 27") ) + strcpy(nad, "NAD27"); + if( poSRS->ImportFromESRIStatePlaneWKT(statePlaneZone, (const char*)nad, (const char*)units, psDefn->PCS) == OGRERR_NONE ) + return TRUE; + } + else if( pcsName && (pStr = strstr(pcsName, "UTM Zone ")) != NULL ) + CheckUTM( psDefn, szCTString ); + } + + /* check state plane again to see if a pe string is available */ + if( psDefn->PCS != KvUserDefined ) + { + if( poSRS->ImportFromESRIStatePlaneWKT(0, NULL, (const char*)units, psDefn->PCS) == OGRERR_NONE ) + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* CheckUTM() */ +/* */ +/* Check utm proj code by its name. */ +/************************************************************************/ +void CheckUTM( GTIFDefn * psDefn, const char * pszCtString ) +{ + if(!psDefn || !pszCtString) + return; + + static const char *apszUtmProjCode[] = { + "PSAD56", "17N", "16017", + "PSAD56", "18N", "16018", + "PSAD56", "19N", "16019", + "PSAD56", "20N", "16020", + "PSAD56", "21N", "16021", + "PSAD56", "17S", "16117", + "PSAD56", "18S", "16118", + "PSAD56", "19S", "16119", + "PSAD56", "20S", "16120", + "PSAD56", "21S", "16121", + "PSAD56", "22S", "16122", + NULL, NULL, NULL}; + + const char* p = strstr(pszCtString, "Datum = "); + char datumName[128]; + if(p) + { + p += strlen("Datum = "); + const char* p1 = strchr(p, '|'); + if(p1 && p1-p < (int)sizeof(datumName)) + { + strncpy(datumName, p, (p1-p)); + datumName[p1-p] = '\0'; + } + else + CPLStrlcpy(datumName, p, sizeof(datumName)); + } + else + { + datumName[0] = '\0'; + } + + char utmName[64]; + p = strstr(pszCtString, "UTM Zone "); + if(p) + { + p += strlen("UTM Zone "); + const char* p1 = strchr(p, '|'); + if(p1 && p1-p < (int)sizeof(utmName)) + { + strncpy(utmName, p, (p1-p)); + utmName[p1-p] = '\0'; + } + else + CPLStrlcpy(utmName, p, sizeof(utmName)); + + for(int i=0; apszUtmProjCode[i]!=NULL; i += 3) + { + if(EQUALN(utmName, apszUtmProjCode[i+1], strlen(apszUtmProjCode[i+1])) && + EQUAL(datumName, apszUtmProjCode[i]) ) + { + if(psDefn->ProjCode != atoi(apszUtmProjCode[i+2])) + { + psDefn->ProjCode = (short) atoi(apszUtmProjCode[i+2]); + GTIFGetProjTRFInfo( psDefn->ProjCode, NULL, &(psDefn->Projection), + psDefn->ProjParm ); + break; + } + } + } + } + + return; +} diff --git a/bazaar/plugin/gdal/frmts/gtiff/gt_citation.h b/bazaar/plugin/gdal/frmts/gtiff/gt_citation.h new file mode 100644 index 000000000..8a43c9a80 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/gt_citation.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * $Id: gt_citation.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Implements special parsing of Imagine citation strings, and + * to encode PE String info in citation fields as needed. + * Author: Xiuguang Zhou (ESRI) + * + ****************************************************************************** + * Copyright (c) 2008, Xiuguang Zhou (ESRI) + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GT_CITATION_H_INCLUDED +#define GT_CITATION_H_INCLUDED + +#include "cpl_port.h" +#include "geo_normalize.h" +#include "ogr_spatialref.h" + +char* ImagineCitationTranslation(char* psCitation, geokey_t keyID); +char** CitationStringParse(char* psCitation, geokey_t keyID); + +#define nCitationNameTypes 9 +typedef enum +{ + CitCsName = 0, + CitPcsName = 1, + CitProjectionName = 2, + CitLUnitsName = 3, + CitGcsName = 4, + CitDatumName = 5, + CitEllipsoidName = 6, + CitPrimemName = 7, + CitAUnitsName = 8 +} CitationNameType; + +OGRBoolean CheckCitationKeyForStatePlaneUTM(GTIF* hGTIF, GTIFDefn* psDefn, OGRSpatialReference* poSRS, OGRBoolean* pLinearUnitIsSet); +//char* ImagineCitationTranslation(char* psCitation, geokey_t keyID); +//char** CitationStringParse(char* psCitation, geokey_t keyID); +void SetLinearUnitCitation(GTIF* psGTIF, char* pszLinearUOMName); +void SetGeogCSCitation(GTIF * psGTIF, OGRSpatialReference *poSRS, char* angUnitName, int nDatum, short nSpheroid); +OGRBoolean SetCitationToSRS(GTIF* hGTIF, char* szCTString, int nCTStringLen, + geokey_t geoKey, OGRSpatialReference* poSRS, OGRBoolean* linearUnitIsSet); +void GetGeogCSFromCitation(char* szGCSName, int nGCSName, + geokey_t geoKey, + char **ppszGeogName, + char **ppszDatumName, + char **ppszPMName, + char **ppszSpheroidName, + char **ppszAngularUnits); +void CheckUTM( GTIFDefn * psDefn, const char * pszCtString ); + + +#endif // GT_CITATION_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/gtiff/gt_jpeg_copy.cpp b/bazaar/plugin/gdal/frmts/gtiff/gt_jpeg_copy.cpp new file mode 100644 index 000000000..97b98dc37 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/gt_jpeg_copy.cpp @@ -0,0 +1,875 @@ +/****************************************************************************** + * $Id: gt_jpeg_copy.cpp 28213 2014-12-25 00:42:13Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Specialized copy of JPEG content into TIFF. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi.h" +#include "gt_jpeg_copy.h" + +/* Note: JPEG_DIRECT_COPY is not defined by default, because it is mainly */ +/* useful for debugging purposes */ + +CPL_CVSID("$Id: gt_jpeg_copy.cpp 28213 2014-12-25 00:42:13Z rouault $"); + +#if defined(JPEG_DIRECT_COPY) || defined(HAVE_LIBJPEG) + +#include "vrt/vrtdataset.h" + +/************************************************************************/ +/* GetUnderlyingDataset() */ +/************************************************************************/ + +static GDALDataset* GetUnderlyingDataset(GDALDataset* poSrcDS) +{ + /* Test if we can directly copy original JPEG content */ + /* if available */ + if (poSrcDS->GetDriver() != NULL && + poSrcDS->GetDriver() == GDALGetDriverByName("VRT")) + { + VRTDataset* poVRTDS = (VRTDataset* )poSrcDS; + poSrcDS = poVRTDS->GetSingleSimpleSource(); + } + + return poSrcDS; +} + +#endif // defined(JPEG_DIRECT_COPY) || defined(HAVE_LIBJPEG) + + +#ifdef JPEG_DIRECT_COPY + +/************************************************************************/ +/* IsBaselineDCTJPEG() */ +/************************************************************************/ + +static int IsBaselineDCTJPEG(VSILFILE* fp) +{ + GByte abyBuf[4]; + + if (VSIFReadL(abyBuf, 1, 2, fp) != 2 || + abyBuf[0] != 0xff || abyBuf[1] != 0xd8 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Not a valid JPEG file"); + return FALSE; + } + + int nOffset = 2; + while(TRUE) + { + VSIFSeekL(fp, nOffset, SEEK_SET); + if (VSIFReadL(abyBuf, 1, 4, fp) != 4 || + abyBuf[0] != 0xFF) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Not a valid JPEG file"); + return FALSE; + } + + int nMarker = abyBuf[1]; + + if (nMarker == 0xC0 /* Start of Frame 0 = Baseline DCT */) + return TRUE; + + if (nMarker == 0xD9) + return FALSE; + + if (nMarker == 0xF7 /* JPEG Extension 7, JPEG-LS */ || + nMarker == 0xF8 /* JPEG Extension 8, JPEG-LS Extension */ || + (nMarker >= 0xC1 && nMarker <= 0xCF) /* Other Start of Frames that we don't want to support */) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unsupported type of JPEG file for JPEG_DIRECT_COPY mode"); + return FALSE; + } + + nOffset += 2 + abyBuf[2] * 256 + abyBuf[3]; + } +} + +/************************************************************************/ +/* GTIFF_CanDirectCopyFromJPEG() */ +/************************************************************************/ + +int GTIFF_CanDirectCopyFromJPEG(GDALDataset* poSrcDS, char** &papszCreateOptions) +{ + poSrcDS = GetUnderlyingDataset(poSrcDS); + if (poSrcDS == NULL) + return FALSE; + if (poSrcDS->GetDriver() == NULL) + return FALSE; + if (!EQUAL(GDALGetDriverShortName(poSrcDS->GetDriver()), "JPEG")) + return FALSE; + + const char* pszCompress = CSLFetchNameValue(papszCreateOptions, "COMPRESS"); + if (pszCompress != NULL && !EQUAL(pszCompress, "JPEG")) + return FALSE; + + const char* pszSrcColorSpace = poSrcDS->GetMetadataItem("SOURCE_COLOR_SPACE", "IMAGE_STRUCTURE"); + if (pszSrcColorSpace != NULL && + (EQUAL(pszSrcColorSpace, "CMYK") || EQUAL(pszSrcColorSpace, "YCbCrK"))) + return FALSE; + + int bJPEGDirectCopy = FALSE; + + VSILFILE* fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb"); + if (fpJPEG && IsBaselineDCTJPEG(fpJPEG)) + { + bJPEGDirectCopy = TRUE; + + if (pszCompress == NULL) + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "COMPRESS", "JPEG"); + + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "BLOCKXSIZE", NULL); + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "BLOCKYSIZE", + CPLSPrintf("%d", poSrcDS->GetRasterYSize())); + + if (pszSrcColorSpace != NULL && EQUAL(pszSrcColorSpace, "YCbCr")) + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "YCBCR"); + else + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", NULL); + + if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte) + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "NBITS", "12"); + else + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "NBITS", NULL); + + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "TILED", NULL); + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "JPEG_QUALITY", NULL); + } + if (fpJPEG) + VSIFCloseL(fpJPEG); + + return bJPEGDirectCopy; +} + +/************************************************************************/ +/* GTIFF_DirectCopyFromJPEG() */ +/************************************************************************/ + +CPLErr GTIFF_DirectCopyFromJPEG(GDALDataset* poDS, GDALDataset* poSrcDS, + GDALProgressFunc pfnProgress, void * pProgressData, + int& bShouldFallbackToNormalCopyIfFail) +{ + bShouldFallbackToNormalCopyIfFail = TRUE; + + poSrcDS = GetUnderlyingDataset(poSrcDS); + if (poSrcDS == NULL) + return CE_Failure; + + VSILFILE* fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb"); + if (fpJPEG == NULL) + return CE_Failure; + + CPLErr eErr = CE_None; + + VSIFSeekL(fpJPEG, 0, SEEK_END); + tmsize_t nSize = (tmsize_t) VSIFTellL(fpJPEG); + VSIFSeekL(fpJPEG, 0, SEEK_SET); + + void* pabyJPEGData = VSIMalloc(nSize); + if (pabyJPEGData == NULL) + { + VSIFCloseL(fpJPEG); + return CE_Failure; + } + + if (pabyJPEGData != NULL && + (tmsize_t)VSIFReadL(pabyJPEGData, 1, nSize, fpJPEG) == nSize) + { + bShouldFallbackToNormalCopyIfFail = FALSE; + + TIFF* hTIFF = (TIFF*) poDS->GetInternalHandle(NULL); + if (TIFFWriteRawStrip(hTIFF, 0, pabyJPEGData, nSize) != nSize) + eErr = CE_Failure; + + if( !pfnProgress( 1.0, NULL, pProgressData ) ) + eErr = CE_Failure; + } + else + { + eErr = CE_Failure; + } + + VSIFree(pabyJPEGData); + VSIFCloseL(fpJPEG); + + return eErr; +} + +#endif // JPEG_DIRECT_COPY + +#ifdef HAVE_LIBJPEG + +#include "vsidataio.h" + +#include + +/* + * We are using width_in_blocks which is supposed to be private to + * libjpeg. Unfortunately, the libjpeg delivered with Cygwin has + * renamed this member to width_in_data_units. Since the header has + * also renamed a define, use that unique define name in order to + * detect the problem header and adjust to suit. + */ +#if defined(D_MAX_DATA_UNITS_IN_MCU) +#define width_in_blocks width_in_data_units +#endif + +/************************************************************************/ +/* GTIFF_CanCopyFromJPEG() */ +/************************************************************************/ + +int GTIFF_CanCopyFromJPEG(GDALDataset* poSrcDS, char** &papszCreateOptions) +{ + poSrcDS = GetUnderlyingDataset(poSrcDS); + if (poSrcDS == NULL) + return FALSE; + if (poSrcDS->GetDriver() == NULL) + return FALSE; + if (!EQUAL(GDALGetDriverShortName(poSrcDS->GetDriver()), "JPEG")) + return FALSE; + + const char* pszCompress = CSLFetchNameValue(papszCreateOptions, "COMPRESS"); + if (pszCompress == NULL || !EQUAL(pszCompress, "JPEG")) + return FALSE; + + int nBlockXSize = atoi(CSLFetchNameValueDef(papszCreateOptions, "BLOCKXSIZE", "0")); + int nBlockYSize = atoi(CSLFetchNameValueDef(papszCreateOptions, "BLOCKYSIZE", "0")); + int nMCUSize = 8; + const char* pszSrcColorSpace = + poSrcDS->GetMetadataItem("SOURCE_COLOR_SPACE", "IMAGE_STRUCTURE"); + if (pszSrcColorSpace != NULL && EQUAL(pszSrcColorSpace, "YCbCr")) + nMCUSize = 16; + + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int nBands = poSrcDS->GetRasterCount(); + + const char* pszPhotometric = CSLFetchNameValue(papszCreateOptions, "PHOTOMETRIC"); + int bCompatiblePhotometric = ( + pszPhotometric == NULL || + (nMCUSize == 16 && EQUAL(pszPhotometric, "YCbCr")) || + (nMCUSize == 8 && nBands == 4 && + poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_CyanBand && + poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_MagentaBand && + poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_YellowBand && + poSrcDS->GetRasterBand(4)->GetColorInterpretation() == GCI_BlackBand) || + (nMCUSize == 8 && EQUAL(pszPhotometric, "RGB") && nBands == 3) || + (nMCUSize == 8 && EQUAL(pszPhotometric, "MINISBLACK") && nBands == 1) ); + if (!bCompatiblePhotometric) + return FALSE; + + if ( nBands == 4 && pszPhotometric == NULL && + poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_CyanBand && + poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_MagentaBand && + poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_YellowBand && + poSrcDS->GetRasterBand(4)->GetColorInterpretation() == GCI_BlackBand ) + { + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "CMYK"); + } + + const char* pszInterleave = CSLFetchNameValue(papszCreateOptions, "INTERLEAVE"); + int bCompatibleInterleave = ( pszInterleave == NULL || + (nBands > 1 && EQUAL(pszInterleave, "PIXEL")) || + nBands == 1 ); + if( !bCompatibleInterleave ) + return FALSE; + + if ( (nBlockXSize == nXSize || (nBlockXSize % nMCUSize) == 0) && + (nBlockYSize == nYSize || (nBlockYSize % nMCUSize) == 0) && + poSrcDS->GetRasterBand(1)->GetRasterDataType() == GDT_Byte && + CSLFetchNameValue(papszCreateOptions, "NBITS") == NULL && + CSLFetchNameValue(papszCreateOptions, "JPEG_QUALITY") == NULL ) + { + if (nMCUSize == 16 && pszPhotometric == NULL) + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "YCBCR"); + return TRUE; + } + else + { + return FALSE; + } +} + +/************************************************************************/ +/* GTIFF_ErrorExitJPEG() */ +/************************************************************************/ + +static void GTIFF_ErrorExitJPEG(j_common_ptr cinfo) +{ + jmp_buf *setjmp_buffer = (jmp_buf *) cinfo->client_data; + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + CPLError( CE_Failure, CPLE_AppDefined, + "libjpeg: %s", buffer ); + + /* Return control to the setjmp point */ + longjmp(*setjmp_buffer, 1); +} + +/************************************************************************/ +/* GTIFF_Set_TIFFTAG_JPEGTABLES() */ +/************************************************************************/ + +static +void GTIFF_Set_TIFFTAG_JPEGTABLES(TIFF* hTIFF, + jpeg_decompress_struct& sDInfo, + jpeg_compress_struct& sCInfo) +{ + char szTmpFilename[128]; + sprintf(szTmpFilename, "/vsimem/tables_%p", &sDInfo); + VSILFILE* fpTABLES = VSIFOpenL(szTmpFilename, "wb+"); + + uint16 nPhotometric; + TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &nPhotometric ); + + jpeg_vsiio_dest( &sCInfo, fpTABLES ); + + // Avoid unnecessary tables to be emitted + if( nPhotometric != PHOTOMETRIC_YCBCR ) + { + JQUANT_TBL* qtbl; + JHUFF_TBL* htbl; + qtbl = sCInfo.quant_tbl_ptrs[1]; + if (qtbl != NULL) + qtbl->sent_table = TRUE; + htbl = sCInfo.dc_huff_tbl_ptrs[1]; + if (htbl != NULL) + htbl->sent_table = TRUE; + htbl = sCInfo.ac_huff_tbl_ptrs[1]; + if (htbl != NULL) + htbl->sent_table = TRUE; + } + jpeg_write_tables( &sCInfo ); + + VSIFCloseL(fpTABLES); + + vsi_l_offset nSizeTables = 0; + GByte* pabyJPEGTablesData = VSIGetMemFileBuffer(szTmpFilename, &nSizeTables, FALSE); + TIFFSetField(hTIFF, TIFFTAG_JPEGTABLES, (int)nSizeTables, pabyJPEGTablesData); + + VSIUnlink(szTmpFilename); +} + +/************************************************************************/ +/* GTIFF_CopyFromJPEG_WriteAdditionalTags() */ +/************************************************************************/ + +CPLErr GTIFF_CopyFromJPEG_WriteAdditionalTags(TIFF* hTIFF, + GDALDataset* poSrcDS) +{ + poSrcDS = GetUnderlyingDataset(poSrcDS); + if (poSrcDS == NULL) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Write TIFFTAG_JPEGTABLES */ +/* -------------------------------------------------------------------- */ + + VSILFILE* fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb"); + if (fpJPEG == NULL) + return CE_Failure; + + struct jpeg_error_mgr sJErr; + struct jpeg_decompress_struct sDInfo; + jmp_buf setjmp_buffer; + if (setjmp(setjmp_buffer)) + { + VSIFCloseL(fpJPEG); + return CE_Failure; + } + + sDInfo.err = jpeg_std_error( &sJErr ); + sJErr.error_exit = GTIFF_ErrorExitJPEG; + sDInfo.client_data = (void *) &setjmp_buffer; + + jpeg_create_decompress(&sDInfo); + + jpeg_vsiio_src( &sDInfo, fpJPEG ); + jpeg_read_header( &sDInfo, TRUE ); + + struct jpeg_compress_struct sCInfo; + + sCInfo.err = jpeg_std_error( &sJErr ); + sJErr.error_exit = GTIFF_ErrorExitJPEG; + sCInfo.client_data = (void *) &setjmp_buffer; + + jpeg_create_compress(&sCInfo); + jpeg_copy_critical_parameters(&sDInfo, &sCInfo); + GTIFF_Set_TIFFTAG_JPEGTABLES(hTIFF, sDInfo, sCInfo); + jpeg_abort_compress(&sCInfo); + jpeg_destroy_compress(&sCInfo); + +/* -------------------------------------------------------------------- */ +/* Write TIFFTAG_REFERENCEBLACKWHITE if needed. */ +/* -------------------------------------------------------------------- */ + + uint16 nPhotometric; + if( !TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric) ) ) + nPhotometric = PHOTOMETRIC_MINISBLACK; + + uint16 nBitsPerSample; + if( !TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &(nBitsPerSample)) ) + nBitsPerSample = 1; + + if ( nPhotometric == PHOTOMETRIC_YCBCR ) + { + /* + * A ReferenceBlackWhite field *must* be present since the + * default value is inappropriate for YCbCr. Fill in the + * proper value if application didn't set it. + */ + float *ref; + if (!TIFFGetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE, + &ref)) + { + float refbw[6]; + long top = 1L << nBitsPerSample; + refbw[0] = 0; + refbw[1] = (float)(top-1L); + refbw[2] = (float)(top>>1); + refbw[3] = refbw[1]; + refbw[4] = refbw[2]; + refbw[5] = refbw[1]; + TIFFSetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE, + refbw); + } + } + +/* -------------------------------------------------------------------- */ +/* Write TIFFTAG_YCBCRSUBSAMPLING if needed. */ +/* -------------------------------------------------------------------- */ + + if ( nPhotometric == PHOTOMETRIC_YCBCR && sDInfo.num_components == 3 ) + { + if ((sDInfo.comp_info[0].h_samp_factor == 1 || sDInfo.comp_info[0].h_samp_factor == 2) && + (sDInfo.comp_info[0].v_samp_factor == 1 || sDInfo.comp_info[0].v_samp_factor == 2) && + sDInfo.comp_info[1].h_samp_factor == 1 && + sDInfo.comp_info[1].v_samp_factor == 1 && + sDInfo.comp_info[2].h_samp_factor == 1 && + sDInfo.comp_info[2].v_samp_factor == 1) + { + TIFFSetField(hTIFF, TIFFTAG_YCBCRSUBSAMPLING, + sDInfo.comp_info[0].h_samp_factor, + sDInfo.comp_info[0].v_samp_factor); + } + else + { + CPLDebug("GTiff", "Unusual sampling factors. TIFFTAG_YCBCRSUBSAMPLING not written."); + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + + jpeg_abort_decompress( &sDInfo ); + jpeg_destroy_decompress( &sDInfo ); + + VSIFCloseL(fpJPEG); + + return CE_None; +} + +/************************************************************************/ +/* GTIFF_CopyBlockFromJPEG() */ +/************************************************************************/ + +static CPLErr GTIFF_CopyBlockFromJPEG(TIFF* hTIFF, + jpeg_decompress_struct& sDInfo, + int iX, int iY, + int nXBlocks, + CPL_UNUSED int nYBlocks, + int nXSize, int nYSize, + int nBlockXSize, int nBlockYSize, + int iMCU_sample_width, int iMCU_sample_height, + jvirt_barray_ptr *pSrcCoeffs) +{ + CPLString osTmpFilename(CPLSPrintf("/vsimem/%p", &sDInfo)); + VSILFILE* fpMEM = VSIFOpenL(osTmpFilename, "wb+"); + +/* -------------------------------------------------------------------- */ +/* Initialization of the compressor */ +/* -------------------------------------------------------------------- */ + struct jpeg_error_mgr sJErr; + struct jpeg_compress_struct sCInfo; + jmp_buf setjmp_buffer; + if (setjmp(setjmp_buffer)) + { + VSIFCloseL(fpMEM); + VSIUnlink(osTmpFilename); + return CE_Failure; + } + + sCInfo.err = jpeg_std_error( &sJErr ); + sJErr.error_exit = GTIFF_ErrorExitJPEG; + sCInfo.client_data = (void *) &setjmp_buffer; + + /* Initialize destination compression parameters from source values */ + jpeg_create_compress(&sCInfo); + jpeg_copy_critical_parameters(&sDInfo, &sCInfo); + + /* ensure libjpeg won't write any extraneous markers */ + sCInfo.write_JFIF_header = FALSE; + sCInfo.write_Adobe_marker = FALSE; + +/* -------------------------------------------------------------------- */ +/* Allocated destination coefficient array */ +/* -------------------------------------------------------------------- */ + int bIsTiled = TIFFIsTiled(hTIFF); + + int nJPEGWidth, nJPEGHeight; + if (bIsTiled) + { + nJPEGWidth = nBlockXSize; + nJPEGHeight = nBlockYSize; + } + else + { + nJPEGWidth = MIN(nBlockXSize, nXSize - iX * nBlockXSize); + nJPEGHeight = MIN(nBlockYSize, nYSize - iY * nBlockYSize); + } + + /* Code partially derived from libjpeg transupp.c */ + + /* Correct the destination's image dimensions as necessary */ + #if JPEG_LIB_VERSION >= 70 + sCInfo.jpeg_width = nJPEGWidth; + sCInfo.jpeg_height = nJPEGHeight; + #else + sCInfo.image_width = nJPEGWidth; + sCInfo.image_height = nJPEGHeight; + #endif + + /* Save x/y offsets measured in iMCUs */ + int x_crop_offset = (iX * nBlockXSize) / iMCU_sample_width; + int y_crop_offset = (iY * nBlockYSize) / iMCU_sample_height; + + jvirt_barray_ptr* pDstCoeffs = (jvirt_barray_ptr *) + (*sCInfo.mem->alloc_small) ((j_common_ptr) &sCInfo, JPOOL_IMAGE, + sizeof(jvirt_barray_ptr) * sCInfo.num_components); + int ci; + + for (ci = 0; ci < sCInfo.num_components; ci++) + { + jpeg_component_info *compptr = sCInfo.comp_info + ci; + int h_samp_factor, v_samp_factor; + if (sCInfo.num_components == 1) + { + /* we're going to force samp factors to 1x1 in this case */ + h_samp_factor = v_samp_factor = 1; + } + else + { + h_samp_factor = compptr->h_samp_factor; + v_samp_factor = compptr->v_samp_factor; + } + int width_in_iMCUs = + (nJPEGWidth + iMCU_sample_width - 1) / iMCU_sample_width; + int height_in_iMCUs = + (nJPEGHeight + iMCU_sample_height - 1) / iMCU_sample_height; + int nWidth_in_blocks = width_in_iMCUs * h_samp_factor; + int nHeight_in_blocks = height_in_iMCUs * v_samp_factor; + pDstCoeffs[ci] = (*sCInfo.mem->request_virt_barray) + ((j_common_ptr) &sCInfo, JPOOL_IMAGE, FALSE, + nWidth_in_blocks, nHeight_in_blocks, (JDIMENSION) v_samp_factor); + } + + jpeg_vsiio_dest( &sCInfo, fpMEM ); + + /* Start compressor (note no image data is actually written here) */ + jpeg_write_coefficients(&sCInfo, pDstCoeffs); + + jpeg_suppress_tables( &sCInfo, TRUE ); + + /* We simply have to copy the right amount of data (the destination's + * image size) starting at the given X and Y offsets in the source. + */ + for (ci = 0; ci < sCInfo.num_components; ci++) + { + jpeg_component_info *compptr = sCInfo.comp_info + ci; + int x_crop_blocks = x_crop_offset * compptr->h_samp_factor; + int y_crop_blocks = y_crop_offset * compptr->v_samp_factor; + JDIMENSION nSrcWidthInBlocks = sDInfo.comp_info[ci].width_in_blocks; + JDIMENSION nSrcHeightInBlocks = sDInfo.comp_info[ci].height_in_blocks; + + JDIMENSION nXBlocksToCopy = compptr->width_in_blocks; + if (x_crop_blocks + compptr->width_in_blocks > nSrcWidthInBlocks) + nXBlocksToCopy = nSrcWidthInBlocks - x_crop_blocks; + + for (JDIMENSION dst_blk_y = 0; + dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) + { + JBLOCKARRAY dst_buffer = (*sDInfo.mem->access_virt_barray) + ((j_common_ptr) &sDInfo, pDstCoeffs[ci], + dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + + int offset_y = 0; + int nYBlocks = compptr->v_samp_factor; + if( bIsTiled && + dst_blk_y + y_crop_blocks + compptr->v_samp_factor > + nSrcHeightInBlocks) + { + nYBlocks = nSrcHeightInBlocks - (dst_blk_y + y_crop_blocks); + if (nYBlocks > 0) + { + JBLOCKARRAY src_buffer = (*sDInfo.mem->access_virt_barray) + ((j_common_ptr) &sDInfo, pSrcCoeffs[ci], + dst_blk_y + y_crop_blocks, + (JDIMENSION) 1, FALSE); + for (; offset_y < nYBlocks; offset_y++) + { + memcpy(dst_buffer[offset_y], + src_buffer[offset_y] + x_crop_blocks, + nXBlocksToCopy * (DCTSIZE2 * sizeof(JCOEF))); + if (nXBlocksToCopy < compptr->width_in_blocks) + { + memset(dst_buffer[offset_y] + nXBlocksToCopy, 0, + (compptr->width_in_blocks - nXBlocksToCopy) * + (DCTSIZE2 * sizeof(JCOEF))); + } + } + } + + for (; offset_y < compptr->v_samp_factor; offset_y++) + { + memset(dst_buffer[offset_y], 0, + compptr->width_in_blocks * (DCTSIZE2 * sizeof(JCOEF))); + } + } + else + { + JBLOCKARRAY src_buffer = (*sDInfo.mem->access_virt_barray) + ((j_common_ptr) &sDInfo, pSrcCoeffs[ci], + dst_blk_y + y_crop_blocks, + (JDIMENSION) compptr->v_samp_factor, FALSE); + for (; offset_y < compptr->v_samp_factor; offset_y++) + { + memcpy(dst_buffer[offset_y], + src_buffer[offset_y] + x_crop_blocks, + nXBlocksToCopy * (DCTSIZE2 * sizeof(JCOEF))); + if (nXBlocksToCopy < compptr->width_in_blocks) + { + memset(dst_buffer[offset_y] + nXBlocksToCopy, 0, + (compptr->width_in_blocks - nXBlocksToCopy) * + (DCTSIZE2 * sizeof(JCOEF))); + } + } + } + } + } + + jpeg_finish_compress(&sCInfo); + jpeg_destroy_compress(&sCInfo); + + VSIFCloseL(fpMEM); + +/* -------------------------------------------------------------------- */ +/* Write the JPEG content with libtiff raw API */ +/* -------------------------------------------------------------------- */ + vsi_l_offset nSize = 0; + GByte* pabyJPEGData = VSIGetMemFileBuffer(osTmpFilename, &nSize, FALSE); + + CPLErr eErr = CE_None; + + if ( bIsTiled ) + { + if ((vsi_l_offset)TIFFWriteRawTile(hTIFF, iX + iY * nXBlocks, + pabyJPEGData, nSize) != nSize) + eErr = CE_Failure; + } + else + { + if ((vsi_l_offset)TIFFWriteRawStrip(hTIFF, iX + iY * nXBlocks, + pabyJPEGData, nSize) != nSize) + eErr = CE_Failure; + } + + VSIUnlink(osTmpFilename); + + return eErr; +} + +/************************************************************************/ +/* GTIFF_CopyFromJPEG() */ +/************************************************************************/ + +CPLErr GTIFF_CopyFromJPEG(GDALDataset* poDS, GDALDataset* poSrcDS, + GDALProgressFunc pfnProgress, void * pProgressData, + int& bShouldFallbackToNormalCopyIfFail) +{ + bShouldFallbackToNormalCopyIfFail = TRUE; + + poSrcDS = GetUnderlyingDataset(poSrcDS); + if (poSrcDS == NULL) + return CE_Failure; + + VSILFILE* fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb"); + if (fpJPEG == NULL) + return CE_Failure; + + CPLErr eErr = CE_None; + +/* -------------------------------------------------------------------- */ +/* Initialization of the decompressor */ +/* -------------------------------------------------------------------- */ + struct jpeg_error_mgr sJErr; + struct jpeg_decompress_struct sDInfo; + memset(&sDInfo, 0, sizeof(sDInfo)); + jmp_buf setjmp_buffer; + if (setjmp(setjmp_buffer)) + { + VSIFCloseL(fpJPEG); + jpeg_destroy_decompress(&sDInfo); + return CE_Failure; + } + + sDInfo.err = jpeg_std_error( &sJErr ); + sJErr.error_exit = GTIFF_ErrorExitJPEG; + sDInfo.client_data = (void *) &setjmp_buffer; + + jpeg_create_decompress(&sDInfo); + + /* This is to address bug related in ticket #1795 */ + if (CPLGetConfigOption("JPEGMEM", NULL) == NULL) + { + /* If the user doesn't provide a value for JPEGMEM, we want to be sure */ + /* that at least 500 MB will be used before creating the temporary file */ + sDInfo.mem->max_memory_to_use = + MAX(sDInfo.mem->max_memory_to_use, 500 * 1024 * 1024); + } + + jpeg_vsiio_src( &sDInfo, fpJPEG ); + jpeg_read_header( &sDInfo, TRUE ); + + jvirt_barray_ptr* pSrcCoeffs = jpeg_read_coefficients(&sDInfo); + +/* -------------------------------------------------------------------- */ +/* Compute MCU dimensions */ +/* -------------------------------------------------------------------- */ + int iMCU_sample_width, iMCU_sample_height; + if (sDInfo.num_components == 1) + { + iMCU_sample_width = 8; + iMCU_sample_height = 8; + } + else + { + iMCU_sample_width = sDInfo.max_h_samp_factor * 8; + iMCU_sample_height = sDInfo.max_v_samp_factor * 8; + } + +/* -------------------------------------------------------------------- */ +/* Get raster and block dimensions */ +/* -------------------------------------------------------------------- */ + int nXSize, nYSize /* , nBands */; + int nBlockXSize, nBlockYSize; + + nXSize = poDS->GetRasterXSize(); + nYSize = poDS->GetRasterYSize(); + /* nBands = poDS->GetRasterCount(); */ + + /* We don't use the GDAL block dimensions because of the split-band */ + /* mechanism that can expose a pseudo one-line-strip whereas the */ + /* real layout is a single big strip */ + + TIFF* hTIFF = (TIFF*) poDS->GetInternalHandle(NULL); + if( TIFFIsTiled(hTIFF) ) + { + TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &(nBlockXSize) ); + TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &(nBlockYSize) ); + } + else + { + uint32 nRowsPerStrip; + if( !TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP, + &(nRowsPerStrip) ) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "RowsPerStrip not defined ... assuming all one strip." ); + nRowsPerStrip = nYSize; /* dummy value */ + } + + // If the rows per strip is larger than the file we will get + // confused. libtiff internally will treat the rowsperstrip as + // the image height and it is best if we do too. (#4468) + if (nRowsPerStrip > (uint32)nYSize) + nRowsPerStrip = nYSize; + + nBlockXSize = nXSize; + nBlockYSize = nRowsPerStrip; + } + + int nXBlocks = (nXSize + nBlockXSize - 1) / nBlockXSize; + int nYBlocks = (nYSize + nBlockYSize - 1) / nBlockYSize; + +/* -------------------------------------------------------------------- */ +/* Copy blocks. */ +/* -------------------------------------------------------------------- */ + + bShouldFallbackToNormalCopyIfFail = FALSE; + + for(int iY=0;iY + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GT_JPEG_COPY_H_INCLUDED +#define GT_JPEG_COPY_H_INCLUDED + +#ifdef JPEG_DIRECT_COPY + +#include "gdal_priv.h" +#include "cpl_vsi.h" + +int GTIFF_CanDirectCopyFromJPEG(GDALDataset* poSrcDS, char** &papszCreateOptions); + +CPLErr GTIFF_DirectCopyFromJPEG(GDALDataset* poDS, GDALDataset* poSrcDS, + GDALProgressFunc pfnProgress, void * pProgressData, + int& bShouldFallbackToNormalCopyIfFail); + +#endif // JPEG_DIRECT_COPY + +#ifdef HAVE_LIBJPEG + +#include "gdal_priv.h" +#include "cpl_error.h" +#include "tiffio.h" + +int GTIFF_CanCopyFromJPEG(GDALDataset* poSrcDS, char** &papszCreateOptions); + +CPLErr GTIFF_CopyFromJPEG_WriteAdditionalTags(TIFF* hTIFF, + GDALDataset* poSrcDS); + +CPLErr GTIFF_CopyFromJPEG(GDALDataset* poDS, GDALDataset* poSrcDS, + GDALProgressFunc pfnProgress, void * pProgressData, + int& bShouldFallbackToNormalCopyIfFail); + +#endif // HAVE_LIBJPEG + +#endif // GT_JPEG_COPY_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/gtiff/gt_overview.cpp b/bazaar/plugin/gdal/frmts/gtiff/gt_overview.cpp new file mode 100644 index 000000000..fc845bf33 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/gt_overview.cpp @@ -0,0 +1,834 @@ +/****************************************************************************** + * $Id: gt_overview.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Code to build overviews of external databases as a TIFF file. + * Only used by the GDALDefaultOverviews::BuildOverviews() method. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#define CPL_SERV_H_INCLUDED + +#include "tifvsi.h" +#include "xtiffio.h" +#include "gt_overview.h" +#include "gtiff.h" + +CPL_CVSID("$Id: gt_overview.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +/************************************************************************/ +/* GTIFFWriteDirectory() */ +/* */ +/* Create a new directory, without any image data for an overview */ +/* or a mask */ +/* Returns offset of newly created directory, but the */ +/* current directory is reset to be the one in used when this */ +/* function is called. */ +/************************************************************************/ + +toff_t GTIFFWriteDirectory(TIFF *hTIFF, int nSubfileType, int nXSize, int nYSize, + int nBitsPerPixel, int nPlanarConfig, int nSamples, + int nBlockXSize, int nBlockYSize, + int bTiled, int nCompressFlag, int nPhotometric, + int nSampleFormat, + int nPredictor, + unsigned short *panRed, + unsigned short *panGreen, + unsigned short *panBlue, + int nExtraSamples, + unsigned short *panExtraSampleValues, + const char *pszMetadata ) + +{ + toff_t nBaseDirOffset; + toff_t nOffset; + + nBaseDirOffset = TIFFCurrentDirOffset( hTIFF ); + +#if defined(TIFFLIB_VERSION) && TIFFLIB_VERSION >= 20051201 /* 3.8.0 */ + TIFFFreeDirectory( hTIFF ); +#endif + + TIFFCreateDirectory( hTIFF ); + +/* -------------------------------------------------------------------- */ +/* Setup TIFF fields. */ +/* -------------------------------------------------------------------- */ + TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, nXSize ); + TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, nYSize ); + if( nSamples == 1 ) + TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ); + else + TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, nPlanarConfig ); + + TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, nBitsPerPixel ); + TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, nSamples ); + TIFFSetField( hTIFF, TIFFTAG_COMPRESSION, nCompressFlag ); + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, nPhotometric ); + TIFFSetField( hTIFF, TIFFTAG_SAMPLEFORMAT, nSampleFormat ); + + if( bTiled ) + { + TIFFSetField( hTIFF, TIFFTAG_TILEWIDTH, nBlockXSize ); + TIFFSetField( hTIFF, TIFFTAG_TILELENGTH, nBlockYSize ); + } + else + TIFFSetField( hTIFF, TIFFTAG_ROWSPERSTRIP, nBlockYSize ); + + TIFFSetField( hTIFF, TIFFTAG_SUBFILETYPE, nSubfileType ); + + if (panExtraSampleValues != NULL) + { + TIFFSetField(hTIFF, TIFFTAG_EXTRASAMPLES, nExtraSamples, panExtraSampleValues ); + } + + if ( nCompressFlag == COMPRESSION_LZW || + nCompressFlag == COMPRESSION_ADOBE_DEFLATE ) + TIFFSetField( hTIFF, TIFFTAG_PREDICTOR, nPredictor ); + +/* -------------------------------------------------------------------- */ +/* Write color table if one is present. */ +/* -------------------------------------------------------------------- */ + if( panRed != NULL ) + { + TIFFSetField( hTIFF, TIFFTAG_COLORMAP, panRed, panGreen, panBlue ); + } + +/* -------------------------------------------------------------------- */ +/* Write metadata if we have some. */ +/* -------------------------------------------------------------------- */ + if( pszMetadata && strlen(pszMetadata) > 0 ) + TIFFSetField( hTIFF, TIFFTAG_GDAL_METADATA, pszMetadata ); + +/* -------------------------------------------------------------------- */ +/* Write directory, and return byte offset. */ +/* -------------------------------------------------------------------- */ + if( TIFFWriteCheck( hTIFF, bTiled, "GTIFFWriteDirectory" ) == 0 ) + { + TIFFSetSubDirectory( hTIFF, nBaseDirOffset ); + return 0; + } + + TIFFWriteDirectory( hTIFF ); + TIFFSetDirectory( hTIFF, (tdir_t) (TIFFNumberOfDirectories(hTIFF)-1) ); + + nOffset = TIFFCurrentDirOffset( hTIFF ); + + TIFFSetSubDirectory( hTIFF, nBaseDirOffset ); + + return nOffset; +} + +/************************************************************************/ +/* GTIFFBuildOverviewMetadata() */ +/************************************************************************/ + +void GTIFFBuildOverviewMetadata( const char *pszResampling, + GDALDataset *poBaseDS, + CPLString &osMetadata ) + +{ + osMetadata = ""; + + if( pszResampling && EQUALN(pszResampling,"AVERAGE_BIT2",12) ) + osMetadata += "AVERAGE_BIT2GRAYSCALE"; + + if( poBaseDS->GetMetadataItem( "INTERNAL_MASK_FLAGS_1" ) ) + { + int iBand; + + for( iBand = 0; iBand < 200; iBand++ ) + { + CPLString osItem; + CPLString osName; + + osName.Printf( "INTERNAL_MASK_FLAGS_%d", iBand+1 ); + if( poBaseDS->GetMetadataItem( osName ) ) + { + osItem.Printf( "%s", + osName.c_str(), + poBaseDS->GetMetadataItem( osName ) ); + osMetadata += osItem; + } + } + } + + const char* pszNoDataValues = poBaseDS->GetMetadataItem("NODATA_VALUES"); + if (pszNoDataValues) + { + CPLString osItem; + osItem.Printf( "%s", pszNoDataValues ); + osMetadata += osItem; + } + + if( !EQUAL(osMetadata,"") ) + osMetadata += ""; + else + osMetadata = ""; +} + +/************************************************************************/ +/* GTIFFBuildOverviews() */ +/************************************************************************/ + +CPLErr +GTIFFBuildOverviews( const char * pszFilename, + int nBands, GDALRasterBand **papoBandList, + int nOverviews, int * panOverviewList, + const char * pszResampling, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + TIFF *hOTIFF; + int nBitsPerPixel=0, nCompression=COMPRESSION_NONE, nPhotometric=0; + int nSampleFormat=0, nPlanarConfig, iOverview, iBand; + int nXSize=0, nYSize=0; + + if( nBands == 0 || nOverviews == 0 ) + return CE_None; + + if (!GTiffOneTimeInit()) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Verify that the list of bands is suitable for emitting in */ +/* TIFF file. */ +/* -------------------------------------------------------------------- */ + for( iBand = 0; iBand < nBands; iBand++ ) + { + int nBandBits, nBandFormat; + GDALRasterBand *hBand = papoBandList[iBand]; + + switch( hBand->GetRasterDataType() ) + { + case GDT_Byte: + nBandBits = 8; + nBandFormat = SAMPLEFORMAT_UINT; + break; + + case GDT_UInt16: + nBandBits = 16; + nBandFormat = SAMPLEFORMAT_UINT; + break; + + case GDT_Int16: + nBandBits = 16; + nBandFormat = SAMPLEFORMAT_INT; + break; + + case GDT_UInt32: + nBandBits = 32; + nBandFormat = SAMPLEFORMAT_UINT; + break; + + case GDT_Int32: + nBandBits = 32; + nBandFormat = SAMPLEFORMAT_INT; + break; + + case GDT_Float32: + nBandBits = 32; + nBandFormat = SAMPLEFORMAT_IEEEFP; + break; + + case GDT_Float64: + nBandBits = 64; + nBandFormat = SAMPLEFORMAT_IEEEFP; + break; + + case GDT_CInt16: + nBandBits = 32; + nBandFormat = SAMPLEFORMAT_COMPLEXINT; + break; + + case GDT_CInt32: + nBandBits = 64; + nBandFormat = SAMPLEFORMAT_COMPLEXINT; + break; + + case GDT_CFloat32: + nBandBits = 64; + nBandFormat = SAMPLEFORMAT_COMPLEXIEEEFP; + break; + + case GDT_CFloat64: + nBandBits = 128; + nBandFormat = SAMPLEFORMAT_COMPLEXIEEEFP; + break; + + default: + CPLAssert( FALSE ); + return CE_Failure; + } + + if( hBand->GetMetadataItem( "NBITS", "IMAGE_STRUCTURE" ) ) + { + nBandBits = + atoi(hBand->GetMetadataItem("NBITS","IMAGE_STRUCTURE")); + + if( nBandBits == 1 + && EQUALN(pszResampling,"AVERAGE_BIT2",12) ) + nBandBits = 8; + } + + if( iBand == 0 ) + { + nBitsPerPixel = nBandBits; + nSampleFormat = nBandFormat; + nXSize = hBand->GetXSize(); + nYSize = hBand->GetYSize(); + } + else if( nBitsPerPixel != nBandBits || nSampleFormat != nBandFormat ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GTIFFBuildOverviews() doesn't support a mixture of band" + " data types." ); + return CE_Failure; + } + else if( hBand->GetColorTable() != NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GTIFFBuildOverviews() doesn't support building" + " overviews of multiple colormapped bands." ); + return CE_Failure; + } + else if( hBand->GetXSize() != nXSize + || hBand->GetYSize() != nYSize ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GTIFFBuildOverviews() doesn't support building" + " overviews of different sized bands." ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Use specified compression method. */ +/* -------------------------------------------------------------------- */ + const char *pszCompress = CPLGetConfigOption( "COMPRESS_OVERVIEW", NULL ); + + if( pszCompress != NULL && pszCompress[0] != '\0' ) + { + nCompression = GTIFFGetCompressionMethod(pszCompress, "COMPRESS_OVERVIEW"); + if (nCompression < 0) + return CE_Failure; + } + + if( nCompression == COMPRESSION_JPEG && nBitsPerPixel > 8 ) + { + if( nBitsPerPixel > 16 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GTIFFBuildOverviews() doesn't support building" + " JPEG compressed overviews of nBitsPerPixel > 16." ); + return CE_Failure; + } + + nBitsPerPixel = 12; + } + +/* -------------------------------------------------------------------- */ +/* Figure out the planar configuration to use. */ +/* -------------------------------------------------------------------- */ + if( nBands == 1 ) + nPlanarConfig = PLANARCONFIG_CONTIG; + else + nPlanarConfig = PLANARCONFIG_SEPARATE; + + const char* pszInterleave = CPLGetConfigOption( "INTERLEAVE_OVERVIEW", NULL ); + if (pszInterleave != NULL && pszInterleave[0] != '\0') + { + if( EQUAL( pszInterleave, "PIXEL" ) ) + nPlanarConfig = PLANARCONFIG_CONTIG; + else if( EQUAL( pszInterleave, "BAND" ) ) + nPlanarConfig = PLANARCONFIG_SEPARATE; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "INTERLEAVE_OVERVIEW=%s unsupported, value must be PIXEL or BAND. ignoring", + pszInterleave ); + } + } + +/* -------------------------------------------------------------------- */ +/* Figure out the photometric interpretation to use. */ +/* -------------------------------------------------------------------- */ + if( nBands == 3 ) + nPhotometric = PHOTOMETRIC_RGB; + else if( papoBandList[0]->GetColorTable() != NULL + && !EQUALN(pszResampling,"AVERAGE_BIT2",12) ) + { + nPhotometric = PHOTOMETRIC_PALETTE; + /* should set the colormap up at this point too! */ + } + else + nPhotometric = PHOTOMETRIC_MINISBLACK; + + const char* pszPhotometric = CPLGetConfigOption( "PHOTOMETRIC_OVERVIEW", NULL ); + if (pszPhotometric != NULL && pszPhotometric[0] != '\0') + { + if( EQUAL( pszPhotometric, "MINISBLACK" ) ) + nPhotometric = PHOTOMETRIC_MINISBLACK; + else if( EQUAL( pszPhotometric, "MINISWHITE" ) ) + nPhotometric = PHOTOMETRIC_MINISWHITE; + else if( EQUAL( pszPhotometric, "RGB" )) + { + nPhotometric = PHOTOMETRIC_RGB; + } + else if( EQUAL( pszPhotometric, "CMYK" )) + { + nPhotometric = PHOTOMETRIC_SEPARATED; + } + else if( EQUAL( pszPhotometric, "YCBCR" )) + { + nPhotometric = PHOTOMETRIC_YCBCR; + + /* Because of subsampling, setting YCBCR without JPEG compression leads */ + /* to a crash currently. Would need to make GTiffRasterBand::IWriteBlock() */ + /* aware of subsampling so that it doesn't overrun buffer size returned */ + /* by libtiff */ + if ( nCompression != COMPRESSION_JPEG ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Currently, PHOTOMETRIC_OVERVIEW=YCBCR requires COMPRESS_OVERVIEW=JPEG"); + return CE_Failure; + } + + if (pszInterleave != NULL && pszInterleave[0] != '\0' && nPlanarConfig == PLANARCONFIG_SEPARATE) + { + CPLError(CE_Failure, CPLE_NotSupported, + "PHOTOMETRIC_OVERVIEW=YCBCR requires INTERLEAVE_OVERVIEW=PIXEL"); + return CE_Failure; + } + else + { + nPlanarConfig = PLANARCONFIG_CONTIG; + } + + /* YCBCR strictly requires 3 bands. Not less, not more */ + /* Issue an explicit error message as libtiff one is a bit cryptic : */ + /* JPEGLib:Bogus input colorspace */ + if ( nBands != 3 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "PHOTOMETRIC_OVERVIEW=YCBCR requires a source raster with only 3 bands (RGB)"); + return CE_Failure; + } + } + else if( EQUAL( pszPhotometric, "CIELAB" )) + { + nPhotometric = PHOTOMETRIC_CIELAB; + } + else if( EQUAL( pszPhotometric, "ICCLAB" )) + { + nPhotometric = PHOTOMETRIC_ICCLAB; + } + else if( EQUAL( pszPhotometric, "ITULAB" )) + { + nPhotometric = PHOTOMETRIC_ITULAB; + } + else + { + CPLError( CE_Warning, CPLE_IllegalArg, + "PHOTOMETRIC_OVERVIEW=%s value not recognised, ignoring.\n", + pszPhotometric ); + } + } + +/* -------------------------------------------------------------------- */ +/* Figure out the predictor value to use. */ +/* -------------------------------------------------------------------- */ + int nPredictor = PREDICTOR_NONE; + if ( nCompression == COMPRESSION_LZW || + nCompression == COMPRESSION_ADOBE_DEFLATE ) + { + const char* pszPredictor = CPLGetConfigOption( "PREDICTOR_OVERVIEW", NULL ); + if( pszPredictor != NULL ) + { + nPredictor = atoi( pszPredictor ); + } + } + +/* -------------------------------------------------------------------- */ +/* Create the file, if it does not already exist. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + VSILFILE* fpL = NULL; + + if( VSIStatExL( pszFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG ) != 0 ) + { + /* -------------------------------------------------------------------- */ + /* Compute the uncompressed size. */ + /* -------------------------------------------------------------------- */ + double dfUncompressedOverviewSize = 0; + int nDataTypeSize = GDALGetDataTypeSize(papoBandList[0]->GetRasterDataType())/8; + + for( iOverview = 0; iOverview < nOverviews; iOverview++ ) + { + int nOXSize, nOYSize; + + nOXSize = (nXSize + panOverviewList[iOverview] - 1) + / panOverviewList[iOverview]; + nOYSize = (nYSize + panOverviewList[iOverview] - 1) + / panOverviewList[iOverview]; + + dfUncompressedOverviewSize += + nOXSize * ((double)nOYSize) * nBands * nDataTypeSize; + } + + if( nCompression == COMPRESSION_NONE + && dfUncompressedOverviewSize > 4200000000.0 ) + { + #ifndef BIGTIFF_SUPPORT + CPLError( CE_Failure, CPLE_NotSupported, + "The overview file would be larger than 4GB\n" + "but this is the largest size a TIFF can be, and BigTIFF is unavailable.\n" + "Creation failed." ); + return CE_Failure; + #endif + } + /* -------------------------------------------------------------------- */ + /* Should the file be created as a bigtiff file? */ + /* -------------------------------------------------------------------- */ + const char *pszBIGTIFF = CPLGetConfigOption( "BIGTIFF_OVERVIEW", NULL ); + + if( pszBIGTIFF == NULL ) + pszBIGTIFF = "IF_NEEDED"; + + int bCreateBigTIFF = FALSE; + if( EQUAL(pszBIGTIFF,"IF_NEEDED") ) + { + if( nCompression == COMPRESSION_NONE + && dfUncompressedOverviewSize > 4200000000.0 ) + bCreateBigTIFF = TRUE; + } + else if( EQUAL(pszBIGTIFF,"IF_SAFER") ) + { + /* Look at the size of the base image and suppose that */ + /* the added overview levels won't be more than 1/2 of */ + /* the size of the base image. The theory says 1/3 of the */ + /* base image size if the overview levels are 2, 4, 8, 16... */ + /* Thus take 1/2 as the security margin for 1/3 */ + double dfUncompressedImageSize = + nXSize * ((double)nYSize) * nBands * nDataTypeSize; + if( dfUncompressedImageSize * .5 > 4200000000.0 ) + bCreateBigTIFF = TRUE; + } + else + { + bCreateBigTIFF = CSLTestBoolean( pszBIGTIFF ); + if (!bCreateBigTIFF && nCompression == COMPRESSION_NONE + && dfUncompressedOverviewSize > 4200000000.0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The overview file will be larger than 4GB, so BigTIFF is necessary.\n" + "Creation failed."); + return CE_Failure; + } + } + + #ifndef BIGTIFF_SUPPORT + if( bCreateBigTIFF ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "BigTIFF requested, but GDAL built without BigTIFF\n" + "enabled libtiff, request ignored." ); + bCreateBigTIFF = FALSE; + } + #endif + + if( bCreateBigTIFF ) + CPLDebug( "GTiff", "File being created as a BigTIFF." ); + + fpL = VSIFOpenL( pszFilename, "w+" ); + if( fpL == NULL ) + hOTIFF = NULL; + else + hOTIFF = VSI_TIFFOpen( pszFilename, (bCreateBigTIFF) ? "w+8" : "w+", fpL ); + if( hOTIFF == NULL ) + { + if( CPLGetLastErrorNo() == 0 ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create new tiff file `%s'\n" + "failed in VSI_TIFFOpen().\n", + pszFilename ); + if( fpL != NULL ) + VSIFCloseL(fpL); + return CE_Failure; + } + } +/* -------------------------------------------------------------------- */ +/* Otherwise just open it for update access. */ +/* -------------------------------------------------------------------- */ + else + { + fpL = VSIFOpenL( pszFilename, "r+" ); + if( fpL == NULL ) + hOTIFF = NULL; + else + hOTIFF = VSI_TIFFOpen( pszFilename, "r+", fpL ); + if( hOTIFF == NULL ) + { + if( CPLGetLastErrorNo() == 0 ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create new tiff file `%s'\n" + "failed in VSI_TIFFOpen().\n", + pszFilename ); + if( fpL != NULL ) + VSIFCloseL(fpL); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have a palette? If so, create a TIFF compatible version. */ +/* -------------------------------------------------------------------- */ + unsigned short *panRed=NULL, *panGreen=NULL, *panBlue=NULL; + + if( nPhotometric == PHOTOMETRIC_PALETTE ) + { + GDALColorTable *poCT = papoBandList[0]->GetColorTable(); + int nColorCount; + + if( nBitsPerPixel <= 8 ) + nColorCount = 256; + else + nColorCount = 65536; + + panRed = (unsigned short *) + CPLCalloc(nColorCount,sizeof(unsigned short)); + panGreen = (unsigned short *) + CPLCalloc(nColorCount,sizeof(unsigned short)); + panBlue = (unsigned short *) + CPLCalloc(nColorCount,sizeof(unsigned short)); + + for( int iColor = 0; iColor < nColorCount; iColor++ ) + { + GDALColorEntry sRGB; + + if( poCT->GetColorEntryAsRGB( iColor, &sRGB ) ) + { + panRed[iColor] = (unsigned short) (257 * sRGB.c1); + panGreen[iColor] = (unsigned short) (257 * sRGB.c2); + panBlue[iColor] = (unsigned short) (257 * sRGB.c3); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Do we need some metadata for the overviews? */ +/* -------------------------------------------------------------------- */ + CPLString osMetadata; + GDALDataset *poBaseDS = papoBandList[0]->GetDataset(); + + GTIFFBuildOverviewMetadata( pszResampling, poBaseDS, osMetadata ); + +/* -------------------------------------------------------------------- */ +/* Loop, creating overviews. */ +/* -------------------------------------------------------------------- */ + int nOvrBlockXSize, nOvrBlockYSize; + GTIFFGetOverviewBlockSize(&nOvrBlockXSize, &nOvrBlockYSize); + for( iOverview = 0; iOverview < nOverviews; iOverview++ ) + { + int nOXSize, nOYSize; + + nOXSize = (nXSize + panOverviewList[iOverview] - 1) + / panOverviewList[iOverview]; + nOYSize = (nYSize + panOverviewList[iOverview] - 1) + / panOverviewList[iOverview]; + + GTIFFWriteDirectory(hOTIFF, FILETYPE_REDUCEDIMAGE, + nOXSize, nOYSize, nBitsPerPixel, + nPlanarConfig, nBands, + nOvrBlockXSize, nOvrBlockYSize, TRUE, nCompression, + nPhotometric, nSampleFormat, nPredictor, + panRed, panGreen, panBlue, + 0, NULL, /* FIXME? how can we fetch extrasamples */ + osMetadata ); + } + + if (panRed) + { + CPLFree(panRed); + CPLFree(panGreen); + CPLFree(panBlue); + panRed = panGreen = panBlue = NULL; + } + + XTIFFClose( hOTIFF ); + VSIFCloseL(fpL); + fpL = NULL; + +/* -------------------------------------------------------------------- */ +/* Open the overview dataset so that we can get at the overview */ +/* bands. */ +/* -------------------------------------------------------------------- */ + GDALDataset *hODS; + CPLErr eErr = CE_None; + + hODS = (GDALDataset *) GDALOpen( pszFilename, GA_Update ); + if( hODS == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Do we need to set the jpeg quality? */ +/* -------------------------------------------------------------------- */ + TIFF *hTIFF = (TIFF*) hODS->GetInternalHandle(NULL); + + if( nCompression == COMPRESSION_JPEG + && CPLGetConfigOption( "JPEG_QUALITY_OVERVIEW", NULL ) != NULL ) + { + int nJpegQuality = atoi(CPLGetConfigOption("JPEG_QUALITY_OVERVIEW","75")); + TIFFSetField( hTIFF, TIFFTAG_JPEGQUALITY, + nJpegQuality ); + GTIFFSetJpegQuality((GDALDatasetH)hODS, nJpegQuality); + } + +/* -------------------------------------------------------------------- */ +/* Loop writing overview data. */ +/* -------------------------------------------------------------------- */ + + if (nCompression != COMPRESSION_NONE && + nPlanarConfig == PLANARCONFIG_CONTIG && + GDALDataTypeIsComplex(papoBandList[0]->GetRasterDataType()) == FALSE && + papoBandList[0]->GetColorTable() == NULL && + (EQUALN(pszResampling, "NEAR", 4) || EQUAL(pszResampling, "AVERAGE") || + EQUAL(pszResampling, "GAUSS") || EQUAL(pszResampling, "CUBIC") || + EQUAL(pszResampling, "CUBICSPLINE") || EQUAL(pszResampling, "LANCZOS") || + EQUAL(pszResampling, "BILINEAR"))) + { + /* In the case of pixel interleaved compressed overviews, we want to generate */ + /* the overviews for all the bands block by block, and not band after band, */ + /* in order to write the block once and not loose space in the TIFF file */ + GDALRasterBand ***papapoOverviewBands; + + papapoOverviewBands = (GDALRasterBand ***) CPLCalloc(sizeof(void*),nBands); + for( iBand = 0; iBand < nBands && eErr == CE_None; iBand++ ) + { + GDALRasterBand *hSrcBand = papoBandList[iBand]; + GDALRasterBand *hDstBand = hODS->GetRasterBand( iBand+1 ); + papapoOverviewBands[iBand] = (GDALRasterBand **) CPLCalloc(sizeof(void*),nOverviews); + papapoOverviewBands[iBand][0] = hDstBand; + + int bHasNoData; + double noDataValue = hSrcBand->GetNoDataValue(&bHasNoData); + if (bHasNoData) + hDstBand->SetNoDataValue(noDataValue); + + for( int i = 0; i < nOverviews-1 && eErr == CE_None; i++ ) + { + papapoOverviewBands[iBand][i+1] = hDstBand->GetOverview(i); + if (papapoOverviewBands[iBand][i+1] == NULL) + eErr = CE_Failure; + else + { + if (bHasNoData) + papapoOverviewBands[iBand][i+1]->SetNoDataValue(noDataValue); + } + } + } + + if (eErr == CE_None) + eErr = GDALRegenerateOverviewsMultiBand(nBands, papoBandList, + nOverviews, papapoOverviewBands, + pszResampling, pfnProgress, pProgressData ); + + for( iBand = 0; iBand < nBands; iBand++ ) + { + CPLFree(papapoOverviewBands[iBand]); + } + CPLFree(papapoOverviewBands); + } + else + { + GDALRasterBand **papoOverviews; + + papoOverviews = (GDALRasterBand **) CPLCalloc(sizeof(void*),128); + + for( iBand = 0; iBand < nBands && eErr == CE_None; iBand++ ) + { + GDALRasterBand *hSrcBand = papoBandList[iBand]; + GDALRasterBand *hDstBand; + int nDstOverviews; + + hDstBand = hODS->GetRasterBand( iBand+1 ); + + int bHasNoData; + double noDataValue = hSrcBand->GetNoDataValue(&bHasNoData); + if (bHasNoData) + hDstBand->SetNoDataValue(noDataValue); + + papoOverviews[0] = hDstBand; + nDstOverviews = hDstBand->GetOverviewCount() + 1; + CPLAssert( nDstOverviews < 128 ); + nDstOverviews = MIN(128,nDstOverviews); + + for( int i = 0; i < nDstOverviews-1 && eErr == CE_None; i++ ) + { + papoOverviews[i+1] = hDstBand->GetOverview(i); + if (papoOverviews[i+1] == NULL) + eErr = CE_Failure; + else + { + if (bHasNoData) + papoOverviews[i+1]->SetNoDataValue(noDataValue); + } + } + + void *pScaledProgressData; + + pScaledProgressData = + GDALCreateScaledProgress( iBand / (double) nBands, + (iBand+1) / (double) nBands, + pfnProgress, pProgressData ); + + if (eErr == CE_None) + eErr = + GDALRegenerateOverviews( (GDALRasterBandH) hSrcBand, + nDstOverviews, + (GDALRasterBandH *) papoOverviews, + pszResampling, + GDALScaledProgress, + pScaledProgressData); + + GDALDestroyScaledProgress( pScaledProgressData ); + } + + CPLFree( papoOverviews ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + if (eErr == CE_None) + hODS->FlushCache(); + delete hODS; + + pfnProgress( 1.0, NULL, pProgressData ); + + return eErr; +} + diff --git a/bazaar/plugin/gdal/frmts/gtiff/gt_overview.h b/bazaar/plugin/gdal/frmts/gtiff/gt_overview.h new file mode 100644 index 000000000..184316238 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/gt_overview.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * $Id: gt_overview.h 13297 2007-12-09 19:03:50Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Code to build overviews of external databases as a TIFF file. + * Only used by the GDALDefaultOverviews::BuildOverviews() method. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GT_OVERVIEW_H_INCLUDED +#define GT_OVERVIEW_H_INCLUDED + +#include "gdal_priv.h" +#include "tiffio.h" + +toff_t GTIFFWriteDirectory(TIFF *hTIFF, int nSubfileType, int nXSize, int nYSize, + int nBitsPerPixel, int nPlanarConfig, int nSamples, + int nBlockXSize, int nBlockYSize, + int bTiled, int nCompressFlag, int nPhotometric, + int nSampleFormat, + int nPredictor, + unsigned short *panRed, + unsigned short *panGreen, + unsigned short *panBlue, + int nExtraSamples, + unsigned short *panExtraSampleValues, + const char *pszMetadata ); + +void GTIFFBuildOverviewMetadata( const char *pszResampling, + GDALDataset *poBaseDS, + CPLString &osMetadata ); + +#endif diff --git a/bazaar/plugin/gdal/frmts/gtiff/gt_wkt_srs.cpp b/bazaar/plugin/gdal/frmts/gtiff/gt_wkt_srs.cpp new file mode 100644 index 000000000..04ff9f9c3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/gt_wkt_srs.cpp @@ -0,0 +1,2820 @@ +/****************************************************************************** + * $Id: gt_wkt_srs.cpp 29049 2015-04-29 15:54:12Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Implements translation between GeoTIFF normalized projection + * definitions and OpenGIS WKT SRS format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_error.h" +#include "cpl_conv.h" +#include "cpl_csv.h" +#include "gdal_csv.h" + +#include "geovalues.h" +#include "ogr_spatialref.h" +#include "gdal.h" +#include "xtiffio.h" +#include "cpl_multiproc.h" +#include "tifvsi.h" +#include "gt_wkt_srs.h" +#include "gt_wkt_srs_for_gdal.h" +#include "gt_citation.h" +#include "gt_wkt_srs_priv.h" +#include "gtiff.h" + +CPL_CVSID("$Id: gt_wkt_srs.cpp 29049 2015-04-29 15:54:12Z rouault $") + +#define ProjLinearUnitsInterpCorrectGeoKey 3059 + +#ifndef CT_HotineObliqueMercatorAzimuthCenter +# define CT_HotineObliqueMercatorAzimuthCenter 9815 +#endif + +#if !defined(GTIFAtof) +# define GTIFAtof CPLAtof +#endif + +CPL_C_START +#ifndef INTERNAL_LIBGEOTIFF +void CPL_DLL gtSetCSVFilenameHook( const char *(*)(const char *) ); +#define SetCSVFilenameHook gtSetCSVFilenameHook +#endif +CPL_C_END + +// To remind myself not to use CPLString in this file! +#define CPLString Please_do_not_use_CPLString_in_this_file + +static const char *papszDatumEquiv[] = +{ + "Militar_Geographische_Institut", + "Militar_Geographische_Institute", + "World_Geodetic_System_1984", + "WGS_1984", + "WGS_72_Transit_Broadcast_Ephemeris", + "WGS_1972_Transit_Broadcast_Ephemeris", + "World_Geodetic_System_1972", + "WGS_1972", + "European_Terrestrial_Reference_System_89", + "European_Reference_System_1989", + NULL +}; + +// older libgeotiff's won't list this. +#ifndef CT_CylindricalEqualArea +# define CT_CylindricalEqualArea 28 +#endif + +/************************************************************************/ +/* LibgeotiffOneTimeInit() */ +/************************************************************************/ + +static CPLMutex* hMutex = NULL; + +void LibgeotiffOneTimeInit() +{ + static int bOneTimeInitDone = FALSE; + CPLMutexHolder oHolder( &hMutex); + + if (bOneTimeInitDone) + return; + + bOneTimeInitDone = TRUE; + + // If linking with an external libgeotiff we hope this will call the + // SetCSVFilenameHook() in libgeotiff, not the one in gdal/port! +// SetCSVFilenameHook( GDALDefaultCSVFilename ); +} + +/************************************************************************/ +/* LibgeotiffOneTimeCleanupMutex() */ +/************************************************************************/ + +void LibgeotiffOneTimeCleanupMutex() +{ + if( hMutex != NULL ) + { + CPLDestroyMutex(hMutex); + hMutex = NULL; + } +} + +/************************************************************************/ +/* GTIFToCPLRecyleString() */ +/* */ +/* This changes a string from the libgeotiff heap to the GDAL */ +/* heap. */ +/************************************************************************/ + +static void GTIFToCPLRecycleString( char **ppszTarget ) + +{ + if( *ppszTarget == NULL ) + return; + + char *pszTempString = CPLStrdup(*ppszTarget); + GTIFFreeMemory( *ppszTarget ); + *ppszTarget = pszTempString; +} + +/************************************************************************/ +/* WKTMassageDatum() */ +/* */ +/* Massage an EPSG datum name into WMT format. Also transform */ +/* specific exception cases into WKT versions. */ +/************************************************************************/ + +static void WKTMassageDatum( char ** ppszDatum ) + +{ + int i, j; + char *pszDatum; + + pszDatum = *ppszDatum; + if (pszDatum[0] == '\0') + return; + +/* -------------------------------------------------------------------- */ +/* Translate non-alphanumeric values to underscores. */ +/* -------------------------------------------------------------------- */ + for( i = 0; pszDatum[i] != '\0'; i++ ) + { + if( pszDatum[i] != '+' + && !(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z') + && !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z') + && !(pszDatum[i] >= '0' && pszDatum[i] <= '9') ) + { + pszDatum[i] = '_'; + } + } + +/* -------------------------------------------------------------------- */ +/* Remove repeated and trailing underscores. */ +/* -------------------------------------------------------------------- */ + for( i = 1, j = 0; pszDatum[i] != '\0'; i++ ) + { + if( pszDatum[j] == '_' && pszDatum[i] == '_' ) + continue; + + pszDatum[++j] = pszDatum[i]; + } + if( pszDatum[j] == '_' ) + pszDatum[j] = '\0'; + else + pszDatum[j+1] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Search for datum equivelences. Specific massaged names get */ +/* mapped to OpenGIS specified names. */ +/* -------------------------------------------------------------------- */ + for( i = 0; papszDatumEquiv[i] != NULL; i += 2 ) + { + if( EQUAL(*ppszDatum,papszDatumEquiv[i]) ) + { + CPLFree( *ppszDatum ); + *ppszDatum = CPLStrdup( papszDatumEquiv[i+1] ); + return; + } + } +} + +/************************************************************************/ +/* GTIFCleanupImageineNames() */ +/* */ +/* Erdas Imagine sometimes emits big copyright messages, and */ +/* other stuff into citations. These can be pretty messy when */ +/* turned into WKT, so we try to trim and clean the strings */ +/* somewhat. */ +/************************************************************************/ + +/* For example: + GTCitationGeoKey (Ascii,215): "IMAGINE GeoTIFF Support\nCopyright 1991 - 2001 by ERDAS, Inc. All Rights Reserved\n@(#)$RCSfile$ $Revision: 29049 $ $Date: 2015-04-29 08:54:12 -0700 (Wed, 29 Apr 2015) $\nProjection Name = UTM\nUnits = meters\nGeoTIFF Units = meters" + + GeogCitationGeoKey (Ascii,267): "IMAGINE GeoTIFF Support\nCopyright 1991 - 2001 by ERDAS, Inc. All Rights Reserved\n@(#)$RCSfile$ $Revision: 29049 $ $Date: 2015-04-29 08:54:12 -0700 (Wed, 29 Apr 2015) $\nUnable to match Ellipsoid (Datum) to a GeographicTypeGeoKey value\nEllipsoid = Clarke 1866\nDatum = NAD27 (CONUS)" + + PCSCitationGeoKey (Ascii,214): "IMAGINE GeoTIFF Support\nCopyright 1991 - 2001 by ERDAS, Inc. All Rights Reserved\n@(#)$RCSfile$ $Revision: 29049 $ $Date: 2015-04-29 08:54:12 -0700 (Wed, 29 Apr 2015) $\nUTM Zone 10N\nEllipsoid = Clarke 1866\nDatum = NAD27 (CONUS)" + +*/ + +static void GTIFCleanupImagineNames( char *pszCitation ) + +{ + if( strstr(pszCitation,"IMAGINE GeoTIFF") == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* First, we skip past all the copyright, and RCS stuff. We */ +/* assume that this will have a "$" at the end of it all. */ +/* -------------------------------------------------------------------- */ + char *pszSkip; + + for( pszSkip = pszCitation + strlen(pszCitation) - 1; + pszSkip != pszCitation && *pszSkip != '$'; + pszSkip-- ) {} + + if( *pszSkip == '$' ) + pszSkip++; + + memmove( pszCitation, pszSkip, strlen(pszSkip)+1 ); + +/* -------------------------------------------------------------------- */ +/* Convert any newlines into spaces, they really gum up the */ +/* WKT. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; pszCitation[i] != '\0'; i++ ) + { + if( pszCitation[i] == '\n' ) + pszCitation[i] = ' '; + } +} + +/************************************************************************/ +/* GDALGTIFKeyGet() */ +/************************************************************************/ + +static int GDALGTIFKeyGet( GTIF *hGTIF, geokey_t key, + void* pData, + int nIndex, + int nCount, + tagtype_t expected_tagtype ) +{ + tagtype_t tagtype; + if( !GTIFKeyInfo(hGTIF, key, NULL, &tagtype) ) + return 0; + if( tagtype != expected_tagtype ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Expected key %s to be of type %s. Got %s", + GTIFKeyName(key), GTIFTypeName(expected_tagtype), GTIFTypeName(tagtype)); + return 0; + } + return GTIFKeyGet( hGTIF, key, pData, nIndex, nCount ); +} + +/************************************************************************/ +/* GDALGTIFKeyGetASCII() */ +/************************************************************************/ + +int GDALGTIFKeyGetASCII( GTIF *hGTIF, geokey_t key, + char* szStr, + int nIndex, + int szStrMaxLen ) +{ + CPLAssert(nIndex == 0); + return GDALGTIFKeyGet(hGTIF, key, szStr, nIndex, szStrMaxLen, TYPE_ASCII); +} + +/************************************************************************/ +/* GDALGTIFKeyGetSHORT() */ +/************************************************************************/ + +int GDALGTIFKeyGetSHORT( GTIF *hGTIF, geokey_t key, + short* pnVal, + int nIndex, + int nCount ) +{ + return GDALGTIFKeyGet(hGTIF, key, pnVal, nIndex, nCount, TYPE_SHORT); +} + +/************************************************************************/ +/* GDALGTIFKeyGetDOUBLE() */ +/************************************************************************/ + +int GDALGTIFKeyGetDOUBLE( GTIF *hGTIF, geokey_t key, + double* pdfVal, + int nIndex, + int nCount ) +{ + return GDALGTIFKeyGet(hGTIF, key, pdfVal, nIndex, nCount, TYPE_DOUBLE); +} + +/************************************************************************/ +/* GTIFGetOGISDefn() */ +/************************************************************************/ + +char *GTIFGetOGISDefn( GTIF *hGTIF, GTIFDefn * psDefn ) + +{ + OGRSpatialReference oSRS; + +/* -------------------------------------------------------------------- */ +/* Make sure we have hooked CSVFilename(). */ +/* -------------------------------------------------------------------- */ + LibgeotiffOneTimeInit(); + +/* -------------------------------------------------------------------- */ +/* Handle non-standard coordinate systems where GTModelTypeGeoKey */ +/* is not defined, but ProjectedCSTypeGeoKey is defined (ticket #3019) */ +/* -------------------------------------------------------------------- */ + if( psDefn->Model == KvUserDefined && psDefn->PCS != KvUserDefined) + { + psDefn->Model = ModelTypeProjected; + } + +/* -------------------------------------------------------------------- */ +/* Handle non-standard coordinate systems as LOCAL_CS. */ +/* -------------------------------------------------------------------- */ + if( psDefn->Model != ModelTypeProjected + && psDefn->Model != ModelTypeGeographic + && psDefn->Model != ModelTypeGeocentric ) + { + char *pszWKT; + char szPeStr[2400]; + + /** check if there is a pe string citation key **/ + if( GDALGTIFKeyGetASCII( hGTIF, PCSCitationGeoKey, szPeStr, 0, sizeof(szPeStr) ) && + strstr(szPeStr, "ESRI PE String = " ) ) + { + pszWKT = CPLStrdup( szPeStr + strlen("ESRI PE String = ") ); + + if( strstr(pszWKT, "PROJCS[\"WGS_1984_Web_Mercator_Auxiliary_Sphere\"") ) + { + oSRS.SetFromUserInput(pszWKT); + oSRS.SetExtension( "PROJCS", "PROJ4", + "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs" ); + oSRS.FixupOrdering(); + CPLFree(pszWKT); + pszWKT = NULL; + oSRS.exportToWkt(&pszWKT); + } + + return pszWKT; + } + else + { + char *pszUnitsName = NULL; + char szPCSName[300]; + int nKeyCount = 0; + int anVersion[3]; + + if( hGTIF != NULL ) + GTIFDirectoryInfo( hGTIF, anVersion, &nKeyCount ); + + if( nKeyCount > 0 ) // Use LOCAL_CS if we have any geokeys at all. + { + // Handle citation. + strcpy( szPCSName, "unnamed" ); + if( !GDALGTIFKeyGetASCII( hGTIF, GTCitationGeoKey, szPCSName, + 0, sizeof(szPCSName) ) ) + GDALGTIFKeyGetASCII( hGTIF, GeogCitationGeoKey, szPCSName, + 0, sizeof(szPCSName) ); + + GTIFCleanupImagineNames( szPCSName ); + oSRS.SetLocalCS( szPCSName ); + + // Handle units + GTIFGetUOMLengthInfo( psDefn->UOMLength, &pszUnitsName, NULL ); + + if( pszUnitsName != NULL && psDefn->UOMLength != KvUserDefined ) + { + oSRS.SetLinearUnits( pszUnitsName, psDefn->UOMLengthInMeters ); + oSRS.SetAuthority( "LOCAL_CS|UNIT", "EPSG", psDefn->UOMLength); + } + else + oSRS.SetLinearUnits( "unknown", psDefn->UOMLengthInMeters ); + + GTIFFreeMemory( pszUnitsName ); + } + oSRS.exportToWkt( &pszWKT ); + + return pszWKT; + } + } + +/* -------------------------------------------------------------------- */ +/* Handle Geocentric coordinate systems. */ +/* -------------------------------------------------------------------- */ + if( psDefn->Model == ModelTypeGeocentric ) + { + char szName[300]; + + strcpy( szName, "unnamed" ); + if( !GDALGTIFKeyGetASCII( hGTIF, GTCitationGeoKey, szName, + 0, sizeof(szName) ) ) + GDALGTIFKeyGetASCII( hGTIF, GeogCitationGeoKey, szName, + 0, sizeof(szName) ); + + oSRS.SetGeocCS( szName ); + + char *pszUnitsName = NULL; + + GTIFGetUOMLengthInfo( psDefn->UOMLength, &pszUnitsName, NULL ); + + if( pszUnitsName != NULL && psDefn->UOMLength != KvUserDefined ) + { + oSRS.SetLinearUnits( pszUnitsName, psDefn->UOMLengthInMeters ); + oSRS.SetAuthority( "GEOCCS|UNIT", "EPSG", psDefn->UOMLength ); + } + else + oSRS.SetLinearUnits( "unknown", psDefn->UOMLengthInMeters ); + + GTIFFreeMemory( pszUnitsName ); + } + +/* -------------------------------------------------------------------- */ +/* #3901: In libgeotiff 1.3.0 and earlier we incorrectly */ +/* interpreted linear projection parameter geokeys (false */ +/* easting/northing) as being in meters instead of the */ +/* coordinate system of the file. The following code attempts */ +/* to provide mechanisms for fixing the issue if we are linked */ +/* with an older version of libgeotiff. */ +/* -------------------------------------------------------------------- */ + int iParm; + const char *pszLinearUnits = + CPLGetConfigOption( "GTIFF_LINEAR_UNITS", "DEFAULT" ); + +#if LIBGEOTIFF_VERSION <= 1300 + if( EQUAL(pszLinearUnits,"DEFAULT") && psDefn->Projection == KvUserDefined ) + { + for( iParm = 0; iParm < psDefn->nParms; iParm++ ) + { + switch( psDefn->ProjParmId[iParm] ) + { + case ProjFalseEastingGeoKey: + case ProjFalseNorthingGeoKey: + case ProjFalseOriginEastingGeoKey: + case ProjFalseOriginNorthingGeoKey: + case ProjCenterEastingGeoKey: + case ProjCenterNorthingGeoKey: + if( psDefn->UOMLengthInMeters != 0 + && psDefn->UOMLengthInMeters != 1.0 ) + { + psDefn->ProjParm[iParm] *= psDefn->UOMLengthInMeters; + CPLDebug( "GTIFF", "converting geokey to meters to fix bug in old libgeotiff" ); + } + break; + + default: + break; + } + } + } +#endif /* LIBGEOTIFF_VERSION <= 1300 */ + +/* -------------------------------------------------------------------- */ +/* #3901: If folks have broken GeoTIFF files generated with */ +/* older versions of GDAL+libgeotiff, then they may need a */ +/* hack to allow them to be read properly. This is that */ +/* hack. We basically try to undue the conversion applied by */ +/* libgeotiff to meters (or above) to simulate the old */ +/* behavior. */ +/* -------------------------------------------------------------------- */ + short bLinearUnitsMarkedCorrect = FALSE; + + GDALGTIFKeyGetSHORT(hGTIF, (geokey_t) ProjLinearUnitsInterpCorrectGeoKey, + &bLinearUnitsMarkedCorrect, 0, 1); + + if( EQUAL(pszLinearUnits,"BROKEN") + && psDefn->Projection == KvUserDefined + && !bLinearUnitsMarkedCorrect ) + { + for( iParm = 0; iParm < psDefn->nParms; iParm++ ) + { + switch( psDefn->ProjParmId[iParm] ) + { + case ProjFalseEastingGeoKey: + case ProjFalseNorthingGeoKey: + case ProjFalseOriginEastingGeoKey: + case ProjFalseOriginNorthingGeoKey: + case ProjCenterEastingGeoKey: + case ProjCenterNorthingGeoKey: + if( psDefn->UOMLengthInMeters != 0 + && psDefn->UOMLengthInMeters != 1.0 ) + { + psDefn->ProjParm[iParm] /= psDefn->UOMLengthInMeters; + CPLDebug( "GTIFF", "converting geokey to accommodate old broken file due to GTIFF_LINEAR_UNITS=BROKEN setting." ); + } + break; + + default: + break; + } + } + } + +/* -------------------------------------------------------------------- */ +/* If this is a projected SRS we set the PROJCS keyword first */ +/* to ensure that the GEOGCS will be a child. */ +/* -------------------------------------------------------------------- */ + OGRBoolean linearUnitIsSet = FALSE; + if( psDefn->Model == ModelTypeProjected ) + { + char szCTString[512]; + strcpy( szCTString, "unnamed" ); + if( psDefn->PCS != KvUserDefined ) + { + char *pszPCSName = NULL; + + GTIFGetPCSInfo( psDefn->PCS, &pszPCSName, NULL, NULL, NULL ); + + oSRS.SetNode( "PROJCS", pszPCSName ? pszPCSName : "unnamed" ); + if ( pszPCSName ) + GTIFFreeMemory( pszPCSName ); + + oSRS.SetAuthority( "PROJCS", "EPSG", psDefn->PCS ); + } + else if(hGTIF && GDALGTIFKeyGetASCII( hGTIF, PCSCitationGeoKey, szCTString, 0, + sizeof(szCTString)) ) + { + if (!SetCitationToSRS(hGTIF, szCTString, sizeof(szCTString), + PCSCitationGeoKey, &oSRS, &linearUnitIsSet)) + oSRS.SetNode("PROJCS",szCTString); + } + else + { + if( hGTIF ) + { + GDALGTIFKeyGetASCII( hGTIF, GTCitationGeoKey, szCTString, 0, sizeof(szCTString) ); + if(!SetCitationToSRS(hGTIF, szCTString, sizeof(szCTString), + GTCitationGeoKey, &oSRS, &linearUnitIsSet)) + oSRS.SetNode( "PROJCS", szCTString ); + } + else + oSRS.SetNode( "PROJCS", szCTString ); + } + + /* Handle ESRI/Erdas style state plane and UTM in citation key */ + if( CheckCitationKeyForStatePlaneUTM(hGTIF, psDefn, &oSRS, &linearUnitIsSet) ) + { + char *pszWKT; + oSRS.morphFromESRI(); + oSRS.FixupOrdering(); + if( oSRS.exportToWkt( &pszWKT ) == OGRERR_NONE ) + return pszWKT; + } + + /* Handle ESRI PE string in citation */ + szCTString[0] = '\0'; + if( hGTIF && GDALGTIFKeyGetASCII( hGTIF, GTCitationGeoKey, szCTString, 0, sizeof(szCTString) ) ) + SetCitationToSRS(hGTIF, szCTString, sizeof(szCTString), GTCitationGeoKey, &oSRS, &linearUnitIsSet); + } + +/* ==================================================================== */ +/* Setup the GeogCS */ +/* ==================================================================== */ + char *pszGeogName = NULL; + char *pszDatumName = NULL; + char *pszPMName = NULL; + char *pszSpheroidName = NULL; + char *pszAngularUnits = NULL; + double dfInvFlattening=0.0, dfSemiMajor=0.0; + char szGCSName[512]; + OGRBoolean aUnitGot = FALSE; + + if( !GTIFGetGCSInfo( psDefn->GCS, &pszGeogName, NULL, NULL, NULL ) + && hGTIF != NULL + && GDALGTIFKeyGetASCII( hGTIF, GeogCitationGeoKey, szGCSName, 0, + sizeof(szGCSName)) ) + { + GetGeogCSFromCitation(szGCSName, sizeof(szGCSName), + GeogCitationGeoKey, + &pszGeogName, &pszDatumName, + &pszPMName, &pszSpheroidName, + &pszAngularUnits); + } + else + GTIFToCPLRecycleString( &pszGeogName ); + + if( !pszDatumName ) + { + GTIFGetDatumInfo( psDefn->Datum, &pszDatumName, NULL ); + GTIFToCPLRecycleString( &pszDatumName ); + } + if( !pszSpheroidName ) + { + GTIFGetEllipsoidInfo( psDefn->Ellipsoid, &pszSpheroidName, NULL, NULL ); + GTIFToCPLRecycleString( &pszSpheroidName ); + } + else + { + GDALGTIFKeyGetDOUBLE(hGTIF, GeogSemiMajorAxisGeoKey, &(psDefn->SemiMajor), 0, 1 ); + GDALGTIFKeyGetDOUBLE(hGTIF, GeogInvFlatteningGeoKey, &dfInvFlattening, 0, 1 ); + } + if( !pszPMName ) + { + GTIFGetPMInfo( psDefn->PM, &pszPMName, NULL ); + GTIFToCPLRecycleString( &pszPMName ); + } + else + GDALGTIFKeyGetDOUBLE(hGTIF, GeogPrimeMeridianLongGeoKey, &(psDefn->PMLongToGreenwich), 0, 1 ); + + if( !pszAngularUnits ) + { + GTIFGetUOMAngleInfo( psDefn->UOMAngle, &pszAngularUnits, NULL ); + if( pszAngularUnits == NULL ) + pszAngularUnits = CPLStrdup("unknown"); + else + GTIFToCPLRecycleString( &pszAngularUnits ); + } + else + { + GDALGTIFKeyGetDOUBLE(hGTIF, GeogAngularUnitSizeGeoKey, &(psDefn->UOMAngleInDegrees), 0, 1 ); + aUnitGot = TRUE; + } + + if( pszDatumName != NULL ) + WKTMassageDatum( &pszDatumName ); + + dfSemiMajor = psDefn->SemiMajor; + if( dfSemiMajor == 0.0 ) + { + CPLFree(pszSpheroidName); + pszSpheroidName = CPLStrdup("unretrievable - using WGS84"); + dfSemiMajor = SRS_WGS84_SEMIMAJOR; + dfInvFlattening = SRS_WGS84_INVFLATTENING; + } + else if( dfInvFlattening == 0.0 && ((psDefn->SemiMinor / psDefn->SemiMajor) < 0.99999999999999999 + || (psDefn->SemiMinor / psDefn->SemiMajor) > 1.00000000000000001 ) ) + { + dfInvFlattening = OSRCalcInvFlattening(psDefn->SemiMajor,psDefn->SemiMinor); + + /* Take official inverse flattening definition in the WGS84 case */ + if (fabs(dfSemiMajor-SRS_WGS84_SEMIMAJOR) < 1e-10 && + fabs(dfInvFlattening - SRS_WGS84_INVFLATTENING) < 1e-10) + dfInvFlattening = SRS_WGS84_INVFLATTENING; + } + if(!pszGeogName || strlen(pszGeogName) == 0) + { + CPLFree(pszGeogName); + pszGeogName = CPLStrdup( pszDatumName ? pszDatumName : "unknown" ); + } + + if(aUnitGot) + oSRS.SetGeogCS( pszGeogName, pszDatumName, + pszSpheroidName, dfSemiMajor, dfInvFlattening, + pszPMName, + psDefn->PMLongToGreenwich / psDefn->UOMAngleInDegrees, + pszAngularUnits, + psDefn->UOMAngleInDegrees ); + else + oSRS.SetGeogCS( pszGeogName, pszDatumName, + pszSpheroidName, dfSemiMajor, dfInvFlattening, + pszPMName, + psDefn->PMLongToGreenwich / psDefn->UOMAngleInDegrees, + pszAngularUnits, + psDefn->UOMAngleInDegrees * 0.0174532925199433 ); + + if( psDefn->GCS != KvUserDefined && psDefn->GCS > 0 ) + oSRS.SetAuthority( "GEOGCS", "EPSG", psDefn->GCS ); + + if( psDefn->Datum != KvUserDefined ) + oSRS.SetAuthority( "DATUM", "EPSG", psDefn->Datum ); + + if( psDefn->Ellipsoid != KvUserDefined ) + oSRS.SetAuthority( "SPHEROID", "EPSG", psDefn->Ellipsoid ); + + CPLFree( pszGeogName ); + CPLFree( pszDatumName ); + CPLFree( pszSpheroidName ); + CPLFree( pszPMName ); + CPLFree( pszAngularUnits ); + +#if LIBGEOTIFF_VERSION >= 1310 && !defined(GEO_NORMALIZE_DISABLE_TOWGS84) + if( psDefn->TOWGS84Count > 0 ) + oSRS.SetTOWGS84( psDefn->TOWGS84[0], + psDefn->TOWGS84[1], + psDefn->TOWGS84[2], + psDefn->TOWGS84[3], + psDefn->TOWGS84[4], + psDefn->TOWGS84[5], + psDefn->TOWGS84[6] ); +#endif + +/* ==================================================================== */ +/* Try to import PROJCS from ProjectedCSTypeGeoKey if we */ +/* have essentially only it. We could relax a bit the constraints */ +/* but that should do for now. This may mask shortcomings in the */ +/* libgeotiff GTIFGetDefn() function. */ +/* ==================================================================== */ + short tmp; + int bGotFromEPSG = FALSE; + if( psDefn->Model == ModelTypeProjected && + psDefn->PCS != KvUserDefined && + GDALGTIFKeyGetSHORT(hGTIF, ProjectionGeoKey, &tmp, 0, 1 ) == 0 && + GDALGTIFKeyGetSHORT(hGTIF, ProjCoordTransGeoKey, &tmp, 0, 1 ) == 0 && + GDALGTIFKeyGetSHORT(hGTIF, GeographicTypeGeoKey, &tmp, 0, 1 ) == 0 && + GDALGTIFKeyGetSHORT(hGTIF, GeogGeodeticDatumGeoKey, &tmp, 0, 1 ) == 0 && + GDALGTIFKeyGetSHORT(hGTIF, GeogEllipsoidGeoKey, &tmp, 0, 1 ) == 0 && + CSLTestBoolean(CPLGetConfigOption("GTIFF_IMPORT_FROM_EPSG", "YES")) ) + { + // Save error state as importFromEPSGA() will call CPLReset() + int errNo = CPLGetLastErrorNo(); + CPLErr eErr = CPLGetLastErrorType(); + const char* pszTmp = CPLGetLastErrorMsg(); + char* pszLastErrorMsg = CPLStrdup(pszTmp ? pszTmp : ""); + CPLPushErrorHandler(CPLQuietErrorHandler); + OGRErr eImportErr = oSRS.importFromEPSG(psDefn->PCS); + CPLPopErrorHandler(); + // Restore error state + CPLErrorSetState( eErr, errNo, pszLastErrorMsg); + CPLFree(pszLastErrorMsg); + bGotFromEPSG = (eImportErr == OGRERR_NONE); + } + +/* ==================================================================== */ +/* Handle projection parameters. */ +/* ==================================================================== */ + if( psDefn->Model == ModelTypeProjected && !bGotFromEPSG ) + { +/* -------------------------------------------------------------------- */ +/* Make a local copy of parms, and convert back into the */ +/* angular units of the GEOGCS and the linear units of the */ +/* projection. */ +/* -------------------------------------------------------------------- */ + double adfParm[10]; + int i; + + for( i = 0; i < MIN(10,psDefn->nParms); i++ ) + adfParm[i] = psDefn->ProjParm[i]; + + for( ; i < 10; i++ ) + adfParm[i] = 0.0; + + if(!aUnitGot) + { + adfParm[0] *= psDefn->UOMAngleInDegrees; + adfParm[1] *= psDefn->UOMAngleInDegrees; + adfParm[2] *= psDefn->UOMAngleInDegrees; + adfParm[3] *= psDefn->UOMAngleInDegrees; + } + short unitCode = 0; + GDALGTIFKeyGetSHORT(hGTIF, ProjLinearUnitsGeoKey, &unitCode, 0, 1 ); + if(unitCode != KvUserDefined) + { + adfParm[5] /= psDefn->UOMLengthInMeters; + adfParm[6] /= psDefn->UOMLengthInMeters; + } + +/* -------------------------------------------------------------------- */ +/* Translation the fundamental projection. */ +/* -------------------------------------------------------------------- */ + switch( psDefn->CTProjection ) + { + case CT_TransverseMercator: + oSRS.SetTM( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_TransvMercator_SouthOriented: + oSRS.SetTMSO( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_Mercator: + /* If a lat_ts was specified use 2SP, otherwise use 1SP */ + if (psDefn->ProjParmId[2] == ProjStdParallel1GeoKey) + { + if (psDefn->ProjParmId[4] == ProjScaleAtNatOriginGeoKey) + CPLError( CE_Warning, CPLE_AppDefined, + "Mercator projection should not define both StdParallel1 and ScaleAtNatOrigin.\n" + "Using StdParallel1 and ignoring ScaleAtNatOrigin.\n" ); + oSRS.SetMercator2SP( adfParm[2], + adfParm[0], adfParm[1], + adfParm[5], adfParm[6]); + } + else + oSRS.SetMercator( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + + if (psDefn->Projection == 1024 || psDefn->Projection == 9841) // override hack for google mercator. + { + oSRS.SetExtension( "PROJCS", "PROJ4", + "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs" ); + } + break; + + case CT_ObliqueStereographic: + oSRS.SetOS( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_Stereographic: + oSRS.SetStereographic( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_ObliqueMercator: /* hotine */ + oSRS.SetHOM( adfParm[0], adfParm[1], + adfParm[2], adfParm[3], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_HotineObliqueMercatorAzimuthCenter: + oSRS.SetHOMAC( adfParm[0], adfParm[1], + adfParm[2], adfParm[3], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_EquidistantConic: + oSRS.SetEC( adfParm[0], adfParm[1], + adfParm[2], adfParm[3], + adfParm[5], adfParm[6] ); + break; + + case CT_CassiniSoldner: + oSRS.SetCS( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_Polyconic: + oSRS.SetPolyconic( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_AzimuthalEquidistant: + oSRS.SetAE( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_MillerCylindrical: + oSRS.SetMC( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_Equirectangular: + oSRS.SetEquirectangular2( adfParm[0], adfParm[1], + adfParm[2], + adfParm[5], adfParm[6] ); + break; + + case CT_Gnomonic: + oSRS.SetGnomonic( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_LambertAzimEqualArea: + oSRS.SetLAEA( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_Orthographic: + oSRS.SetOrthographic( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_Robinson: + oSRS.SetRobinson( adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_Sinusoidal: + oSRS.SetSinusoidal( adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_VanDerGrinten: + oSRS.SetVDG( adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_PolarStereographic: + oSRS.SetPS( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_LambertConfConic_2SP: + oSRS.SetLCC( adfParm[2], adfParm[3], + adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_LambertConfConic_1SP: + oSRS.SetLCC1SP( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_AlbersEqualArea: + oSRS.SetACEA( adfParm[0], adfParm[1], + adfParm[2], adfParm[3], + adfParm[5], adfParm[6] ); + break; + + case CT_NewZealandMapGrid: + oSRS.SetNZMG( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_CylindricalEqualArea: + oSRS.SetCEA( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + default: + if( oSRS.IsProjected() ) + oSRS.GetRoot()->SetValue( "LOCAL_CS" ); + break; + } + +/* -------------------------------------------------------------------- */ +/* Set projection units. */ +/* -------------------------------------------------------------------- */ + if(!linearUnitIsSet) + { + char *pszUnitsName = NULL; + + GTIFGetUOMLengthInfo( psDefn->UOMLength, &pszUnitsName, NULL ); + + if( pszUnitsName != NULL && psDefn->UOMLength != KvUserDefined ) + { + oSRS.SetLinearUnits( pszUnitsName, psDefn->UOMLengthInMeters ); + oSRS.SetAuthority( "PROJCS|UNIT", "EPSG", psDefn->UOMLength ); + } + else + oSRS.SetLinearUnits( "unknown", psDefn->UOMLengthInMeters ); + + GTIFFreeMemory( pszUnitsName ); + } + } + + if( oSRS.IsProjected()) + { + // Hack to be able to read properly what we have written for EPSG:102113 (ESRI ancient WebMercator) + if( EQUAL(oSRS.GetAttrValue("PROJCS"), "WGS_1984_Web_Mercator") ) + oSRS.importFromEPSG(102113); + // And for EPSG:900913 + else if( EQUAL(oSRS.GetAttrValue("PROJCS"), "Google Maps Global Mercator") ) + oSRS.importFromEPSG(900913); + } + +/* ==================================================================== */ +/* Handle vertical coordinate system information if we have it. */ +/* ==================================================================== */ + short verticalCSType = -1; + short verticalDatum = -1; + short verticalUnits = -1; + const char *pszFilename = NULL; + const char *pszValue; + char szSearchKey[128]; + bool bNeedManualVertCS = false; + char citation[2048]; + + // Don't do anything if there is no apparent vertical information. + GDALGTIFKeyGetSHORT( hGTIF, VerticalCSTypeGeoKey, &verticalCSType, 0, 1 ); + GDALGTIFKeyGetSHORT( hGTIF, VerticalDatumGeoKey, &verticalDatum, 0, 1 ); + GDALGTIFKeyGetSHORT( hGTIF, VerticalUnitsGeoKey, &verticalUnits, 0, 1 ); + + if( (verticalCSType != -1 || verticalDatum != -1 || verticalUnits != -1) + && (oSRS.IsGeographic() || oSRS.IsProjected() || oSRS.IsLocal()) ) + { + if( !GDALGTIFKeyGetASCII( hGTIF, VerticalCitationGeoKey, citation, + 0, sizeof(citation) ) ) + strcpy( citation, "unknown" ); + +/* -------------------------------------------------------------------- */ +/* The original geotiff specification appears to have */ +/* misconstrued the EPSG codes 5101 to 5106 to be vertical */ +/* coordinate system codes, when in fact they are vertical */ +/* datum codes. So if these are found in the */ +/* VerticalCSTypeGeoKey move them to the VerticalDatumGeoKey */ +/* and insert the "normal" corresponding VerticalCSTypeGeoKey */ +/* value. */ +/* -------------------------------------------------------------------- */ + if( (verticalCSType >= 5101 && verticalCSType <= 5112) + && verticalDatum == -1 ) + { + verticalDatum = verticalCSType; + verticalCSType = verticalDatum + 600; + } + +/* -------------------------------------------------------------------- */ +/* This addresses another case where the EGM96 Vertical Datum code */ +/* is mis-used as a Vertical CS code (#4922) */ +/* -------------------------------------------------------------------- */ + if( verticalCSType == 5171 ) + { + verticalDatum = 5171; + verticalCSType = 5773; + } + +/* -------------------------------------------------------------------- */ +/* Somewhat similarly, codes 5001 to 5033 were treated as */ +/* vertical coordinate systems based on ellipsoidal heights. */ +/* We use the corresponding 2d geodetic datum as the vertical */ +/* datum and clear the vertical coordinate system code since */ +/* there isn't one in epsg. */ +/* -------------------------------------------------------------------- */ + if( (verticalCSType >= 5001 && verticalCSType <= 5033) + && verticalDatum == -1 ) + { + verticalDatum = verticalCSType+1000; + verticalCSType = -1; + } + +/* -------------------------------------------------------------------- */ +/* Promote to being a compound coordinate system. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poOldRoot = oSRS.GetRoot()->Clone(); + + oSRS.Clear(); + oSRS.SetNode( "COMPD_CS", "unknown" ); + oSRS.GetRoot()->AddChild( poOldRoot ); + +/* -------------------------------------------------------------------- */ +/* If we have the vertical cs, try to look it up using the */ +/* vertcs.csv file, and use the definition provided by that. */ +/* -------------------------------------------------------------------- */ + bNeedManualVertCS = true; + + if( verticalCSType != KvUserDefined && verticalCSType > 0 ) + { + OGRSpatialReference oVertSRS; + if( oVertSRS.importFromEPSG( verticalCSType ) == OGRERR_NONE ) + { + oSRS.GetRoot()->AddChild( oVertSRS.GetRoot()->Clone() ); + bNeedManualVertCS = false; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Collect some information from the VerticalCS if not provided */ +/* via geokeys. */ +/* -------------------------------------------------------------------- */ + if( bNeedManualVertCS ) + { + if( verticalCSType > 0 && verticalCSType != KvUserDefined ) + { + pszFilename = CSVFilename( "coordinate_reference_system.csv" ); + sprintf( szSearchKey, "%d", verticalCSType ); + + if( verticalDatum < 1 || verticalDatum == KvUserDefined ) + { + pszValue = CSVGetField( pszFilename, + "coord_ref_sys_code", + szSearchKey, CC_Integer, + "datum_code" ); + if( pszValue != NULL ) + verticalDatum = (short) atoi(pszValue); + } + + if( EQUAL(citation,"unknown") ) + { + pszValue = CSVGetField( pszFilename, + "coord_ref_sys_code", + szSearchKey, CC_Integer, + "coord_ref_sys_name" ); + if( pszValue != NULL && *pszValue != '\0' ) + strncpy( citation, pszValue, sizeof(citation) ); + } + + if( verticalUnits < 1 || verticalUnits == KvUserDefined ) + { + pszValue = CSVGetField( pszFilename, + "coord_ref_sys_code", + szSearchKey, CC_Integer, + "coord_sys_code" ); + if( pszValue != NULL ) + { + pszFilename = CSVFilename( "coordinate_axis.csv" ); + pszValue = CSVGetField( pszFilename, + "coord_sys_code", + pszValue, CC_Integer, + "uom_code" ); + if( pszValue != NULL ) + verticalUnits = (short) atoi(pszValue); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Setup VERT_CS with citation if present. */ +/* -------------------------------------------------------------------- */ + oSRS.SetNode( "COMPD_CS|VERT_CS", citation ); + +/* -------------------------------------------------------------------- */ +/* Setup the vertical datum. */ +/* -------------------------------------------------------------------- */ + const char *pszVDatumName = "unknown"; + const char *pszVDatumType = "2005"; // CS_VD_GeoidModelDerived + + if( verticalDatum > 0 && verticalDatum != KvUserDefined ) + { + pszFilename = CSVFilename( "datum.csv" ); + if( EQUAL(pszFilename,"datum.csv") ) + pszFilename = CSVFilename( "gdal_datum.csv" ); + + sprintf( szSearchKey, "%d", verticalDatum ); + + pszValue = CSVGetField( pszFilename, + "DATUM_CODE", szSearchKey, CC_Integer, + "DATUM_NAME" ); + if( pszValue != NULL && *pszValue != '\0' ) + pszVDatumName = pszValue; + + pszValue = CSVGetField( pszFilename, + "DATUM_CODE", szSearchKey, CC_Integer, + "DATUM_TYPE" ); + if( pszValue != NULL && EQUALN(pszValue,"geodetic",8) ) + pszVDatumType = "2002"; // CS_VD_Ellipsoidal + + // We unfortunately don't know how to identify other + // vertical datum types, particularly orthometric (2001). + } + + oSRS.SetNode( "COMPD_CS|VERT_CS|VERT_DATUM", pszVDatumName ); + oSRS.GetAttrNode( "COMPD_CS|VERT_CS|VERT_DATUM" ) + ->AddChild( new OGR_SRSNode( pszVDatumType ) ); + if( verticalDatum > 0 && verticalDatum != KvUserDefined ) + oSRS.SetAuthority( "COMPD_CS|VERT_CS|VERT_DATUM", "EPSG", + verticalDatum ); + +/* -------------------------------------------------------------------- */ +/* Set the vertical units. */ +/* -------------------------------------------------------------------- */ + if( verticalUnits > 0 && verticalUnits != KvUserDefined + && verticalUnits != 9001 ) + { + char szInMeters[128]; + + pszFilename = CSVFilename("unit_of_measure.csv"); + + // Name + sprintf( szSearchKey, "%d", verticalUnits ); + pszValue = CSVGetField( pszFilename, + "uom_code", szSearchKey, CC_Integer, + "unit_of_meas_name" ); + if( pszValue == NULL ) + pszValue = "unknown"; + + oSRS.SetNode( "COMPD_CS|VERT_CS|UNIT", pszValue ); + + // Value + double dfFactorB, dfFactorC; + dfFactorB = GTIFAtof( + CSVGetField( pszFilename, + "uom_code", szSearchKey, CC_Integer, + "factor_b" )); + dfFactorC = GTIFAtof( + CSVGetField( pszFilename, + "uom_code", szSearchKey, CC_Integer, + "factor_c" )); + if( dfFactorB != 0.0 && dfFactorC != 0.0 ) + CPLsprintf( szInMeters, "%.16g", dfFactorB / dfFactorC ); + else + strcpy( szInMeters, "1" ); + + + oSRS.GetAttrNode( "COMPD_CS|VERT_CS|UNIT" ) + ->AddChild( new OGR_SRSNode( szInMeters ) ); + + oSRS.SetAuthority( "COMPD_CS|VERT_CS|UNIT", "EPSG", verticalUnits); + } + else + { + oSRS.SetNode( "COMPD_CS|VERT_CS|UNIT", "metre" ); + oSRS.GetAttrNode( "COMPD_CS|VERT_CS|UNIT" ) + ->AddChild( new OGR_SRSNode( "1.0" ) ); + oSRS.SetAuthority( "COMPD_CS|VERT_CS|UNIT", "EPSG", 9001 ); + } + +/* -------------------------------------------------------------------- */ +/* Set the axis and VERT_CS authority. */ +/* -------------------------------------------------------------------- */ + oSRS.SetNode( "COMPD_CS|VERT_CS|AXIS", "Up" ); + oSRS.GetAttrNode( "COMPD_CS|VERT_CS|AXIS" ) + ->AddChild( new OGR_SRSNode( "UP" ) ); + + if( verticalCSType > 0 && verticalCSType != KvUserDefined ) + oSRS.SetAuthority( "COMPD_CS|VERT_CS", "EPSG", verticalCSType ); + } + +/* ==================================================================== */ +/* Return the WKT serialization of the object. */ +/* ==================================================================== */ + char *pszWKT; + + oSRS.FixupOrdering(); + + if( oSRS.exportToWkt( &pszWKT ) == OGRERR_NONE ) + return pszWKT; + else + return NULL; +} + +/************************************************************************/ +/* OGCDatumName2EPSGDatumCode() */ +/************************************************************************/ + +static int OGCDatumName2EPSGDatumCode( const char * pszOGCName ) + +{ + FILE *fp; + char **papszTokens; + int nReturn = KvUserDefined; + + +/* -------------------------------------------------------------------- */ +/* Do we know it as a built in? */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszOGCName,"NAD27") + || EQUAL(pszOGCName,"North_American_Datum_1927") ) + return Datum_North_American_Datum_1927; + else if( EQUAL(pszOGCName,"NAD83") + || EQUAL(pszOGCName,"North_American_Datum_1983") ) + return Datum_North_American_Datum_1983; + else if( EQUAL(pszOGCName,"WGS84") || EQUAL(pszOGCName,"WGS_1984") + || EQUAL(pszOGCName,"WGS 84")) + return Datum_WGS84; + else if( EQUAL(pszOGCName,"WGS72") || EQUAL(pszOGCName,"WGS_1972") ) + return Datum_WGS72; + +/* -------------------------------------------------------------------- */ +/* Open the table if possible. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpen( CSVFilename("gdal_datum.csv"), "r" ); + if( fp == NULL ) + fp = VSIFOpen( CSVFilename("datum.csv"), "r" ); + + if( fp == NULL ) + return nReturn; + +/* -------------------------------------------------------------------- */ +/* Discard the first line with field names. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( CSVReadParseLine( fp ) ); + +/* -------------------------------------------------------------------- */ +/* Read lines looking for our datum. */ +/* -------------------------------------------------------------------- */ + for( papszTokens = CSVReadParseLine( fp ); + CSLCount(papszTokens) > 2 && nReturn == KvUserDefined; + papszTokens = CSVReadParseLine( fp ) ) + { + WKTMassageDatum( papszTokens + 1 ); + + if( EQUAL(papszTokens[1], pszOGCName) ) + nReturn = atoi(papszTokens[0]); + + CSLDestroy( papszTokens ); + } + + CSLDestroy( papszTokens ); + VSIFClose( fp ); + + return nReturn; +} + +/************************************************************************/ +/* GTIFSetFromOGISDefn() */ +/* */ +/* Write GeoTIFF projection tags from an OGC WKT definition. */ +/************************************************************************/ + +int GTIFSetFromOGISDefn( GTIF * psGTIF, const char *pszOGCWKT ) + +{ + OGRSpatialReference *poSRS; + int nPCS = KvUserDefined; + OGRErr eErr; + OGRBoolean peStrStored = FALSE; + + GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1, + RasterPixelIsArea); + +/* -------------------------------------------------------------------- */ +/* Create an OGRSpatialReference object corresponding to the */ +/* string. */ +/* -------------------------------------------------------------------- */ + poSRS = new OGRSpatialReference(); + if( poSRS->importFromWkt((char **) &pszOGCWKT) != OGRERR_NONE ) + { + delete poSRS; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Get the ellipsoid definition. */ +/* -------------------------------------------------------------------- */ + short nSpheroid = KvUserDefined; + double dfSemiMajor, dfInvFlattening; + + if( poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM|SPHEROID") != NULL + && EQUAL(poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM|SPHEROID"), + "EPSG")) + { + nSpheroid = (short) + atoi(poSRS->GetAuthorityCode("PROJCS|GEOGCS|DATUM|SPHEROID")); + } + else if( poSRS->GetAuthorityName("GEOGCS|DATUM|SPHEROID") != NULL + && EQUAL(poSRS->GetAuthorityName("GEOGCS|DATUM|SPHEROID"),"EPSG")) + { + nSpheroid = (short) + atoi(poSRS->GetAuthorityCode("GEOGCS|DATUM|SPHEROID")); + } + + dfSemiMajor = poSRS->GetSemiMajor( &eErr ); + dfInvFlattening = poSRS->GetInvFlattening( &eErr ); + if( eErr != OGRERR_NONE ) + { + dfSemiMajor = 0.0; + dfInvFlattening = 0.0; + } + +/* -------------------------------------------------------------------- */ +/* Get the Datum so we can special case a few PCS codes. */ +/* -------------------------------------------------------------------- */ + int nDatum = KvUserDefined; + + if( poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM") != NULL + && EQUAL(poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM"),"EPSG") ) + nDatum = atoi(poSRS->GetAuthorityCode("PROJCS|GEOGCS|DATUM")); + else if( poSRS->GetAuthorityName("GEOGCS|DATUM") != NULL + && EQUAL(poSRS->GetAuthorityName("GEOGCS|DATUM"),"EPSG") ) + nDatum = atoi(poSRS->GetAuthorityCode("GEOGCS|DATUM")); + else if( poSRS->GetAttrValue("DATUM") != NULL ) + nDatum = OGCDatumName2EPSGDatumCode( poSRS->GetAttrValue("DATUM") ); + +/* -------------------------------------------------------------------- */ +/* Get the GCS if possible. */ +/* -------------------------------------------------------------------- */ + int nGCS = KvUserDefined; + + if( poSRS->GetAuthorityName("PROJCS|GEOGCS") != NULL + && EQUAL(poSRS->GetAuthorityName("PROJCS|GEOGCS"),"EPSG") ) + nGCS = atoi(poSRS->GetAuthorityCode("PROJCS|GEOGCS")); + else if( poSRS->GetAuthorityName("GEOGCS") != NULL + && EQUAL(poSRS->GetAuthorityName("GEOGCS"),"EPSG") ) + nGCS = atoi(poSRS->GetAuthorityCode("GEOGCS")); + + if( nGCS > 32767 ) + nGCS = KvUserDefined; + +/* -------------------------------------------------------------------- */ +/* Get the linear units. */ +/* -------------------------------------------------------------------- */ + char *pszLinearUOMName = NULL; + double dfLinearUOM = poSRS->GetLinearUnits( &pszLinearUOMName ); + int nUOMLengthCode = 9001; /* meters */ + + if( poSRS->GetAuthorityName("PROJCS|UNIT") != NULL + && EQUAL(poSRS->GetAuthorityName("PROJCS|UNIT"),"EPSG") + && poSRS->GetAttrNode( "PROJCS|UNIT" ) != poSRS->GetAttrNode("GEOGCS|UNIT") ) + nUOMLengthCode = atoi(poSRS->GetAuthorityCode("PROJCS|UNIT")); + else if( (pszLinearUOMName != NULL + && EQUAL(pszLinearUOMName,SRS_UL_FOOT)) + || fabs(dfLinearUOM-GTIFAtof(SRS_UL_FOOT_CONV)) < 0.0000001 ) + nUOMLengthCode = 9002; /* international foot */ + else if( (pszLinearUOMName != NULL + && EQUAL(pszLinearUOMName,SRS_UL_US_FOOT)) + || ABS(dfLinearUOM-GTIFAtof(SRS_UL_US_FOOT_CONV)) < 0.0000001 ) + nUOMLengthCode = 9003; /* us survey foot */ + else if( fabs(dfLinearUOM-1.0) > 0.00000001 ) + nUOMLengthCode = KvUserDefined; + +/* -------------------------------------------------------------------- */ +/* Get some authority values. */ +/* -------------------------------------------------------------------- */ + if( poSRS->GetAuthorityName("PROJCS") != NULL + && EQUAL(poSRS->GetAuthorityName("PROJCS"),"EPSG") ) + { + nPCS = atoi(poSRS->GetAuthorityCode("PROJCS")); + if( nPCS > 32767 ) + nPCS = KvUserDefined; + } + +/* -------------------------------------------------------------------- */ +/* Handle the projection transformation. */ +/* -------------------------------------------------------------------- */ + const char *pszProjection = poSRS->GetAttrValue( "PROJECTION" ); + int bWritePEString = FALSE; + + if( nPCS != KvUserDefined ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS ); + } + else if( poSRS->IsGeocentric() ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeGeocentric ); + } + else if( pszProjection == NULL ) + { + if( poSRS->IsGeographic() ) + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeGeographic); + // otherwise, presumably something like LOCAL_CS. + } + else if( EQUAL(pszProjection,SRS_PT_ALBERS_CONIC_EQUAL_AREA) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_AlbersEqualArea ); + + GTIFKeySet(psGTIF, ProjStdParallelGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( poSRS->GetUTMZone() != 0 ) + { + int bNorth, nZone, nProjection; + + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + + nZone = poSRS->GetUTMZone( &bNorth ); + + if( nDatum == Datum_North_American_Datum_1983 && nZone >= 3 + && nZone <= 22 && bNorth && nUOMLengthCode == 9001 ) + { + nPCS = 26900 + nZone; + + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS ); + } + else if( nDatum == Datum_North_American_Datum_1927 && nZone >= 3 + && nZone <= 22 && bNorth && nUOMLengthCode == 9001 ) + { + nPCS = 26700 + nZone; + + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS ); + } + else if( nDatum == Datum_WGS84 && nUOMLengthCode == 9001 ) + { + if( bNorth ) + nPCS = 32600 + nZone; + else + nPCS = 32700 + nZone; + + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS ); + } + else + { + if( bNorth ) + nProjection = 16000 + nZone; + else + nProjection = 16100 + nZone; + + + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, nProjection ); + } + } + + else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_TransverseMercator ); + + GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_TransvMercator_SouthOriented ); + + GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_MERCATOR_2SP) + || EQUAL(pszProjection,SRS_PT_MERCATOR_1SP) ) + + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_Mercator ); + + GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + if( EQUAL(pszProjection,SRS_PT_MERCATOR_2SP) ) + GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) ); + else + GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_OBLIQUE_STEREOGRAPHIC) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_ObliqueStereographic ); + + GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_STEREOGRAPHIC) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_Stereographic ); + + GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_POLAR_STEREOGRAPHIC) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_PolarStereographic ); + + GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjStraightVertPoleLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_HOTINE_OBLIQUE_MERCATOR) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_ObliqueMercator ); + + GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjAzimuthAngleGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_AZIMUTH, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjRectifiedGridAngleGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_RECTIFIED_GRID_ANGLE, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjScaleAtCenterGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_HotineObliqueMercatorAzimuthCenter ); + + GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjAzimuthAngleGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_AZIMUTH, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjRectifiedGridAngleGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_RECTIFIED_GRID_ANGLE, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjScaleAtCenterGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_CASSINI_SOLDNER) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_CassiniSoldner ); + + GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_EQUIDISTANT_CONIC) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_EquidistantConic ); + + GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_POLYCONIC) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_Polyconic ); + + GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_AZIMUTHAL_EQUIDISTANT) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_AzimuthalEquidistant ); + + GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_MILLER_CYLINDRICAL) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_MillerCylindrical ); + + GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_EQUIRECTANGULAR) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_Equirectangular ); + + GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_GNOMONIC) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_Gnomonic ); + + GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_LambertAzimEqualArea ); + + GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_ORTHOGRAPHIC) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_Orthographic ); + + GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_NEW_ZEALAND_MAP_GRID) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_NewZealandMapGrid ); + + GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_ROBINSON) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_Robinson ); + + GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_SINUSOIDAL) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_Sinusoidal ); + + GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_VANDERGRINTEN) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_VanDerGrinten ); + + GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_LambertConfConic_2SP ); + + GTIFKeySet(psGTIF, ProjFalseOriginLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseOriginLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseOriginEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseOriginNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_LambertConfConic_1SP ); + + GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else if( EQUAL(pszProjection,SRS_PT_CYLINDRICAL_EQUAL_AREA) ) + { + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_CylindricalEqualArea ); + + GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1, + poSRS->GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_EASTING, 0.0 ) ); + + GTIFKeySet(psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + poSRS->GetProjParm( SRS_PP_FALSE_NORTHING, 0.0 ) ); + } + + else + { + bWritePEString = TRUE; + } + + // Note that VERTCS is an ESRI "spelling" of VERT_CS so we assume if + // we find it that we should try to treat this as a PE string. + bWritePEString |= (poSRS->GetAttrValue("VERTCS") != NULL); + + if( bWritePEString + && CSLTestBoolean( CPLGetConfigOption("GTIFF_ESRI_CITATION", + "YES") ) ) + { + /* Anyhing we can't map, we store as an ESRI PE string with a citation key */ + char *pszPEString = NULL; + poSRS->morphToESRI(); + poSRS->exportToWkt( &pszPEString ); + int peStrLen = strlen(pszPEString); + if(peStrLen > 0) + { + char *outPeStr = (char *) CPLMalloc( peStrLen + strlen("ESRI PE String = ")+1 ); + strcpy(outPeStr, "ESRI PE String = "); + strcat(outPeStr, pszPEString); + GTIFKeySet( psGTIF, PCSCitationGeoKey, TYPE_ASCII, 0, outPeStr ); + peStrStored = TRUE; + CPLFree( outPeStr ); + } + if(pszPEString) + CPLFree( pszPEString ); + GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + } + +/* -------------------------------------------------------------------- */ +/* Is there a false easting/northing set? If so, write out a */ +/* special geokey tag to indicate that GDAL has written these */ +/* with the proper interpretation of the linear units. */ +/* -------------------------------------------------------------------- */ + double dfFE = 0.0, dfFN = 0.0; + + if( (GDALGTIFKeyGetDOUBLE(psGTIF, ProjFalseEastingGeoKey, &dfFE, 0, 1) + || GDALGTIFKeyGetDOUBLE(psGTIF, ProjFalseNorthingGeoKey, &dfFN, 0, 1) + || GDALGTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginEastingGeoKey, &dfFE, 0, 1) + || GDALGTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginNorthingGeoKey, &dfFN, 0, 1)) + && (dfFE != 0.0 || dfFN != 0.0) + && nUOMLengthCode != 9001 ) + { + GTIFKeySet(psGTIF, (geokey_t) ProjLinearUnitsInterpCorrectGeoKey, + TYPE_SHORT, 1, (short) 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Write linear units information. */ +/* -------------------------------------------------------------------- */ + if( poSRS->IsGeocentric() ) + { + GTIFKeySet(psGTIF, GeogLinearUnitsGeoKey, TYPE_SHORT, 1, + nUOMLengthCode ); + if( nUOMLengthCode == KvUserDefined ) + GTIFKeySet( psGTIF, GeogLinearUnitSizeGeoKey, TYPE_DOUBLE, 1, + dfLinearUOM); + } + else if( !poSRS->IsGeographic() ) + { + GTIFKeySet(psGTIF, ProjLinearUnitsGeoKey, TYPE_SHORT, 1, + nUOMLengthCode ); + if( nUOMLengthCode == KvUserDefined ) + GTIFKeySet( psGTIF, ProjLinearUnitSizeGeoKey, TYPE_DOUBLE, 1, + dfLinearUOM); + + /* if linear units name is available and user defined, store it as citation */ + if(!peStrStored + && nUOMLengthCode == KvUserDefined + && pszLinearUOMName + && strlen(pszLinearUOMName)>0 + && CSLTestBoolean( CPLGetConfigOption("GTIFF_ESRI_CITATION", + "YES") ) ) + { + SetLinearUnitCitation(psGTIF, pszLinearUOMName); + } + } + +/* -------------------------------------------------------------------- */ +/* Write angular units. Always Degrees for now. */ +/* Changed to support different angular units */ +/* -------------------------------------------------------------------- */ + + char* angUnitName = NULL; + double angUnitValue = poSRS->GetAngularUnits(&angUnitName); + if(EQUAL(angUnitName, "Degree")) + GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1, + Angular_Degree ); + else if(angUnitName) + { + GTIFKeySet(psGTIF, GeogCitationGeoKey, TYPE_ASCII, 0, + angUnitName ); // it may be rewritten if the gcs is userdefined + GTIFKeySet(psGTIF, GeogAngularUnitSizeGeoKey, TYPE_DOUBLE, 1, + angUnitValue ); + } + +/* -------------------------------------------------------------------- */ +/* Try to write a citation from the main coordinate system */ +/* name. */ +/* -------------------------------------------------------------------- */ + if( poSRS->GetRoot() != NULL + && poSRS->GetRoot()->GetChild(0) != NULL + && (poSRS->IsProjected() || poSRS->IsLocal() || poSRS->IsGeocentric()) ) + { + GTIFKeySet( psGTIF, GTCitationGeoKey, TYPE_ASCII, 0, + poSRS->GetRoot()->GetChild(0)->GetValue() ); + } + +/* -------------------------------------------------------------------- */ +/* Try to write a GCS citation. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poGCS = poSRS->GetAttrNode( "GEOGCS" ); + + if( poGCS != NULL && poGCS->GetChild(0) != NULL ) + { + GTIFKeySet( psGTIF, GeogCitationGeoKey, TYPE_ASCII, 0, + poGCS->GetChild(0)->GetValue() ); + } + +/* -------------------------------------------------------------------- */ +/* Try to identify the GCS/datum, scanning the EPSG datum file for */ +/* a match. */ +/* -------------------------------------------------------------------- */ + if( nPCS == KvUserDefined ) + { + if( nGCS == KvUserDefined ) + { + if( nDatum == Datum_North_American_Datum_1927 ) + nGCS = GCS_NAD27; + else if( nDatum == Datum_North_American_Datum_1983 ) + nGCS = GCS_NAD83; + else if( nDatum == Datum_WGS84 || nDatum == DatumE_WGS84 ) + nGCS = GCS_WGS_84; + } + + if( nGCS != KvUserDefined ) + { + GTIFKeySet( psGTIF, GeographicTypeGeoKey, TYPE_SHORT, + 1, nGCS ); + } + else if( nDatum != KvUserDefined ) + { + GTIFKeySet( psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet( psGTIF, GeogGeodeticDatumGeoKey, TYPE_SHORT, + 1, nDatum ); + } + else if( nSpheroid != KvUserDefined ) + { + GTIFKeySet( psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet( psGTIF, GeogGeodeticDatumGeoKey, TYPE_SHORT, + 1, KvUserDefined ); + GTIFKeySet( psGTIF, GeogEllipsoidGeoKey, TYPE_SHORT, 1, + nSpheroid ); + } + else if( dfSemiMajor != 0.0 ) + { + GTIFKeySet( psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet( psGTIF, GeogGeodeticDatumGeoKey, TYPE_SHORT, + 1, KvUserDefined ); + GTIFKeySet( psGTIF, GeogEllipsoidGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet( psGTIF, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1, + dfSemiMajor ); + if( dfInvFlattening == 0.0 ) + GTIFKeySet( psGTIF, GeogSemiMinorAxisGeoKey, TYPE_DOUBLE, 1, + dfSemiMajor ); + else + GTIFKeySet( psGTIF, GeogInvFlatteningGeoKey, TYPE_DOUBLE, 1, + dfInvFlattening ); + } + else if( poSRS->GetAttrValue("DATUM") != NULL + && strstr(poSRS->GetAttrValue("DATUM"),"unknown") == NULL + && strstr(poSRS->GetAttrValue("DATUM"),"unnamed") == NULL ) + + { + CPLError( CE_Warning, CPLE_AppDefined, + "Couldn't translate `%s' to a GeoTIFF datum.\n", + poSRS->GetAttrValue("DATUM") ); + } + + /* Always set InvFlattening if it is avaliable. */ + /* So that it doesn'tneed to calculate from SemiMinor */ + if( dfInvFlattening != 0.0 ) + GTIFKeySet( psGTIF, GeogInvFlatteningGeoKey, TYPE_DOUBLE, 1, + dfInvFlattening ); + /* Always set SemiMajor to keep the precision and in case of editing */ + if( dfSemiMajor != 0.0 ) + GTIFKeySet( psGTIF, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1, + dfSemiMajor ); + + if( nGCS == KvUserDefined + && CSLTestBoolean( CPLGetConfigOption("GTIFF_ESRI_CITATION", + "YES") ) ) + SetGeogCSCitation(psGTIF, poSRS, angUnitName, nDatum, nSpheroid); + } + +/* -------------------------------------------------------------------- */ +/* Do we have TOWGS84 parameters? */ +/* -------------------------------------------------------------------- */ + +#if LIBGEOTIFF_VERSION >= 1310 && !defined(GEO_NORMALIZE_DISABLE_TOWGS84) + double adfTOWGS84[7]; + + if( poSRS->GetTOWGS84( adfTOWGS84 ) == OGRERR_NONE ) + { + if( adfTOWGS84[3] == 0.0 && adfTOWGS84[4] == 0.0 + && adfTOWGS84[5] == 0.0 && adfTOWGS84[6] == 0.0 ) + { + if( nGCS == GCS_WGS_84 && adfTOWGS84[0] == 0.0 + && adfTOWGS84[1] == 0.0 && adfTOWGS84[2] == 0.0 ) + { + ; /* do nothing */ + } + else + GTIFKeySet( psGTIF, GeogTOWGS84GeoKey, TYPE_DOUBLE, 3, + adfTOWGS84 ); + } + else + GTIFKeySet( psGTIF, GeogTOWGS84GeoKey, TYPE_DOUBLE, 7, + adfTOWGS84 ); + } +#endif + +/* -------------------------------------------------------------------- */ +/* Do we have vertical datum information to set? */ +/* -------------------------------------------------------------------- */ + if( poSRS->GetAttrValue( "COMPD_CS|VERT_CS" ) != NULL ) + { + const char *pszValue; + + GTIFKeySet( psGTIF, VerticalCitationGeoKey, TYPE_ASCII, 0, + poSRS->GetAttrValue( "COMPD_CS|VERT_CS" ) ); + + pszValue = poSRS->GetAuthorityCode( "COMPD_CS|VERT_CS" ); + if( pszValue && atoi(pszValue) ) + GTIFKeySet( psGTIF, VerticalCSTypeGeoKey, TYPE_SHORT, 1, + atoi(pszValue) ); + + pszValue = poSRS->GetAuthorityCode( "COMPD_CS|VERT_CS|VERT_DATUM" ); + if( pszValue && atoi(pszValue) ) + GTIFKeySet( psGTIF, VerticalDatumGeoKey, TYPE_SHORT, 1, + atoi(pszValue) ); + + pszValue = poSRS->GetAuthorityCode( "COMPD_CS|VERT_CS|UNIT" ); + if( pszValue && atoi(pszValue) ) + GTIFKeySet( psGTIF, VerticalUnitsGeoKey, TYPE_SHORT, 1, + atoi(pszValue) ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + delete poSRS; + return TRUE; +} + +/************************************************************************/ +/* GTIFWktFromMemBuf() */ +/************************************************************************/ + +CPLErr GTIFWktFromMemBuf( int nSize, unsigned char *pabyBuffer, + char **ppszWKT, double *padfGeoTransform, + int *pnGCPCount, GDAL_GCP **ppasGCPList ) +{ + return GTIFWktFromMemBufEx(nSize, pabyBuffer, ppszWKT, padfGeoTransform, + pnGCPCount, ppasGCPList, NULL, NULL); +} + +CPLErr GTIFWktFromMemBufEx( int nSize, unsigned char *pabyBuffer, + char **ppszWKT, double *padfGeoTransform, + int *pnGCPCount, GDAL_GCP **ppasGCPList, + int *pbPixelIsPoint, char*** ppapszRPCMD ) + +{ + bool bPixelIsPoint = false; + int bPointGeoIgnore = FALSE; + short nRasterType; + char szFilename[100]; + + sprintf( szFilename, "/vsimem/wkt_from_mem_buf_%ld.tif", + (long) CPLGetPID() ); + +/* -------------------------------------------------------------------- */ +/* Make sure we have hooked CSVFilename(). */ +/* -------------------------------------------------------------------- */ + GTiffOneTimeInit(); /* for RPC tag */ + LibgeotiffOneTimeInit(); + +/* -------------------------------------------------------------------- */ +/* Create a memory file from the buffer. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFileFromMemBuffer( szFilename, pabyBuffer, nSize, FALSE ); + if( fp == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Initialize access to the memory geotiff structure. */ +/* -------------------------------------------------------------------- */ + TIFF *hTIFF; + + hTIFF = VSI_TIFFOpen( szFilename, "rc", fp ); + + if( hTIFF == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "TIFF/GeoTIFF structure is corrupt." ); + VSIUnlink( szFilename ); + VSIFCloseL( fp ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Get the projection definition. */ +/* -------------------------------------------------------------------- */ + GTIF *hGTIF; + GTIFDefn *psGTIFDefn; + + hGTIF = GTIFNew(hTIFF); + + if( hGTIF != NULL && GDALGTIFKeyGetSHORT(hGTIF, GTRasterTypeGeoKey, &nRasterType, + 0, 1 ) == 1 + && nRasterType == (short) RasterPixelIsPoint ) + { + bPixelIsPoint = true; + bPointGeoIgnore = + CSLTestBoolean( CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", + "FALSE") ); + } + if( pbPixelIsPoint ) + *pbPixelIsPoint = bPixelIsPoint; + if( ppapszRPCMD ) + *ppapszRPCMD = NULL; + +#if LIBGEOTIFF_VERSION >= 1410 + psGTIFDefn = GTIFAllocDefn(); +#else + psGTIFDefn = (GTIFDefn *) CPLCalloc(1,sizeof(GTIFDefn)); +#endif + + + if( hGTIF != NULL && GTIFGetDefn( hGTIF, psGTIFDefn ) ) + *ppszWKT = GTIFGetOGISDefn( hGTIF, psGTIFDefn ); + else + *ppszWKT = NULL; + + if( hGTIF ) + GTIFFree( hGTIF ); + +#if LIBGEOTIFF_VERSION >= 1410 + GTIFFreeDefn(psGTIFDefn); +#else + CPLFree(psGTIFDefn); +#endif + +/* -------------------------------------------------------------------- */ +/* Get geotransform or tiepoints. */ +/* -------------------------------------------------------------------- */ + double *padfTiePoints, *padfScale, *padfMatrix; + int16 nCount; + + padfGeoTransform[0] = 0.0; + padfGeoTransform[1] = 1.0; + padfGeoTransform[2] = 0.0; + padfGeoTransform[3] = 0.0; + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = 1.0; + + *pnGCPCount = 0; + *ppasGCPList = NULL; + + if( TIFFGetField(hTIFF,TIFFTAG_GEOPIXELSCALE,&nCount,&padfScale ) + && nCount >= 2 ) + { + padfGeoTransform[1] = padfScale[0]; + padfGeoTransform[5] = - ABS(padfScale[1]); + + if( TIFFGetField(hTIFF,TIFFTAG_GEOTIEPOINTS,&nCount,&padfTiePoints ) + && nCount >= 6 ) + { + padfGeoTransform[0] = + padfTiePoints[3] - padfTiePoints[0] * padfGeoTransform[1]; + padfGeoTransform[3] = + padfTiePoints[4] - padfTiePoints[1] * padfGeoTransform[5]; + + // adjust for pixel is point in transform + if( bPixelIsPoint && !bPointGeoIgnore ) + { + padfGeoTransform[0] -= (padfGeoTransform[1] * 0.5 + padfGeoTransform[2] * 0.5); + padfGeoTransform[3] -= (padfGeoTransform[4] * 0.5 + padfGeoTransform[5] * 0.5); + } + } + } + + else if( TIFFGetField(hTIFF,TIFFTAG_GEOTIEPOINTS,&nCount,&padfTiePoints ) + && nCount >= 6 ) + { + *pnGCPCount = nCount / 6; + *ppasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),*pnGCPCount); + + for( int iGCP = 0; iGCP < *pnGCPCount; iGCP++ ) + { + char szID[32]; + GDAL_GCP *psGCP = *ppasGCPList + iGCP; + + sprintf( szID, "%d", iGCP+1 ); + psGCP->pszId = CPLStrdup( szID ); + psGCP->pszInfo = CPLStrdup(""); + psGCP->dfGCPPixel = padfTiePoints[iGCP*6+0]; + psGCP->dfGCPLine = padfTiePoints[iGCP*6+1]; + psGCP->dfGCPX = padfTiePoints[iGCP*6+3]; + psGCP->dfGCPY = padfTiePoints[iGCP*6+4]; + psGCP->dfGCPZ = padfTiePoints[iGCP*6+5]; + } + } + + else if( TIFFGetField(hTIFF,TIFFTAG_GEOTRANSMATRIX,&nCount,&padfMatrix ) + && nCount == 16 ) + { + padfGeoTransform[0] = padfMatrix[3]; + padfGeoTransform[1] = padfMatrix[0]; + padfGeoTransform[2] = padfMatrix[1]; + padfGeoTransform[3] = padfMatrix[7]; + padfGeoTransform[4] = padfMatrix[4]; + padfGeoTransform[5] = padfMatrix[5]; + } + +/* -------------------------------------------------------------------- */ +/* Read RPC */ +/* -------------------------------------------------------------------- */ + if( ppapszRPCMD != NULL ) + { + *ppapszRPCMD = GTiffDatasetReadRPCTag( hTIFF ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + XTIFFClose( hTIFF ); + VSIFCloseL( fp ); + + VSIUnlink( szFilename ); + + if( *ppszWKT == NULL ) + return CE_Failure; + else + return CE_None; +} + +/************************************************************************/ +/* GTIFMemBufFromWkt() */ +/************************************************************************/ + +CPLErr GTIFMemBufFromWkt( const char *pszWKT, const double *padfGeoTransform, + int nGCPCount, const GDAL_GCP *pasGCPList, + int *pnSize, unsigned char **ppabyBuffer ) +{ + return GTIFMemBufFromWktEx(pszWKT, padfGeoTransform, + nGCPCount,pasGCPList, + pnSize, ppabyBuffer, FALSE, NULL); +} + +CPLErr GTIFMemBufFromWktEx( const char *pszWKT, const double *padfGeoTransform, + int nGCPCount, const GDAL_GCP *pasGCPList, + int *pnSize, unsigned char **ppabyBuffer, + int bPixelIsPoint, char** papszRPCMD ) + +{ + TIFF *hTIFF; + GTIF *hGTIF; + char szFilename[100]; + + sprintf( szFilename, "/vsimem/wkt_from_mem_buf_%ld.tif", + (long) CPLGetPID() ); + +/* -------------------------------------------------------------------- */ +/* Make sure we have hooked CSVFilename(). */ +/* -------------------------------------------------------------------- */ + GTiffOneTimeInit(); /* for RPC tag */ + LibgeotiffOneTimeInit(); + +/* -------------------------------------------------------------------- */ +/* Initialize access to the memory geotiff structure. */ +/* -------------------------------------------------------------------- */ + VSILFILE* fpL = VSIFOpenL( szFilename, "w" ); + if( fpL == NULL ) + return CE_Failure; + + hTIFF = VSI_TIFFOpen( szFilename, "w", fpL ); + + if( hTIFF == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "TIFF/GeoTIFF structure is corrupt." ); + VSIFCloseL(fpL); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Write some minimal set of image parameters. */ +/* -------------------------------------------------------------------- */ + TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, 1 ); + TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, 1 ); + TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, 8 ); + TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, 1 ); + TIFFSetField( hTIFF, TIFFTAG_ROWSPERSTRIP, 1 ); + TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ); + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK ); + +/* -------------------------------------------------------------------- */ +/* Get the projection definition. */ +/* -------------------------------------------------------------------- */ + + int bPointGeoIgnore = FALSE; + if( bPixelIsPoint ) + { + bPointGeoIgnore = + CSLTestBoolean( CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", + "FALSE") ); + } + + if( pszWKT != NULL || bPixelIsPoint ) + { + hGTIF = GTIFNew(hTIFF); + if( pszWKT != NULL ) + GTIFSetFromOGISDefn( hGTIF, pszWKT ); + + if( bPixelIsPoint ) + { + GTIFKeySet(hGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1, + RasterPixelIsPoint); + } + + GTIFWriteKeys( hGTIF ); + GTIFFree( hGTIF ); + } + +/* -------------------------------------------------------------------- */ +/* Set the geotransform, or GCPs. */ +/* -------------------------------------------------------------------- */ + + if( padfGeoTransform[0] != 0.0 || padfGeoTransform[1] != 1.0 + || padfGeoTransform[2] != 0.0 || padfGeoTransform[3] != 0.0 + || padfGeoTransform[4] != 0.0 || ABS(padfGeoTransform[5]) != 1.0 ) + { + + if( padfGeoTransform[2] == 0.0 && padfGeoTransform[4] == 0.0 ) + { + double adfPixelScale[3], adfTiePoints[6]; + + adfPixelScale[0] = padfGeoTransform[1]; + adfPixelScale[1] = fabs(padfGeoTransform[5]); + adfPixelScale[2] = 0.0; + + TIFFSetField( hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale ); + + adfTiePoints[0] = 0.0; + adfTiePoints[1] = 0.0; + adfTiePoints[2] = 0.0; + adfTiePoints[3] = padfGeoTransform[0]; + adfTiePoints[4] = padfGeoTransform[3]; + adfTiePoints[5] = 0.0; + + if( bPixelIsPoint && !bPointGeoIgnore ) + { + adfTiePoints[3] += padfGeoTransform[1] * 0.5 + padfGeoTransform[2] * 0.5; + adfTiePoints[4] += padfGeoTransform[4] * 0.5 + padfGeoTransform[5] * 0.5; + } + + TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints ); + } + else + { + double adfMatrix[16]; + + memset(adfMatrix,0,sizeof(double) * 16); + + adfMatrix[0] = padfGeoTransform[1]; + adfMatrix[1] = padfGeoTransform[2]; + adfMatrix[3] = padfGeoTransform[0]; + adfMatrix[4] = padfGeoTransform[4]; + adfMatrix[5] = padfGeoTransform[5]; + adfMatrix[7] = padfGeoTransform[3]; + adfMatrix[15] = 1.0; + + if( bPixelIsPoint && !bPointGeoIgnore ) + { + adfMatrix[3] += padfGeoTransform[1] * 0.5 + padfGeoTransform[2] * 0.5; + adfMatrix[7] += padfGeoTransform[4] * 0.5 + padfGeoTransform[5] * 0.5; + } + + TIFFSetField( hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix ); + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise write tiepoints if they are available. */ +/* -------------------------------------------------------------------- */ + else if( nGCPCount > 0 ) + { + double *padfTiePoints; + + padfTiePoints = (double *) CPLMalloc(6*sizeof(double)*nGCPCount); + + for( int iGCP = 0; iGCP < nGCPCount; iGCP++ ) + { + + padfTiePoints[iGCP*6+0] = pasGCPList[iGCP].dfGCPPixel; + padfTiePoints[iGCP*6+1] = pasGCPList[iGCP].dfGCPLine; + padfTiePoints[iGCP*6+2] = 0; + padfTiePoints[iGCP*6+3] = pasGCPList[iGCP].dfGCPX; + padfTiePoints[iGCP*6+4] = pasGCPList[iGCP].dfGCPY; + padfTiePoints[iGCP*6+5] = pasGCPList[iGCP].dfGCPZ; + } + + TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6*nGCPCount, padfTiePoints); + CPLFree( padfTiePoints ); + } + +/* -------------------------------------------------------------------- */ +/* Write RPC */ +/* -------------------------------------------------------------------- */ + if( papszRPCMD != NULL ) + { + GTiffDatasetWriteRPCTag( hTIFF, papszRPCMD ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup and return the created memory buffer. */ +/* -------------------------------------------------------------------- */ + GByte bySmallImage = 0; + + TIFFWriteEncodedStrip( hTIFF, 0, (char *) &bySmallImage, 1 ); + TIFFWriteCheck( hTIFF, TIFFIsTiled(hTIFF), "GTIFMemBufFromWkt"); + TIFFWriteDirectory( hTIFF ); + + XTIFFClose( hTIFF ); + VSIFCloseL(fpL); + +/* -------------------------------------------------------------------- */ +/* Read back from the memory buffer. It would be preferrable */ +/* to be able to "steal" the memory buffer, but there isn't */ +/* currently any support for this. */ +/* -------------------------------------------------------------------- */ + GUIntBig nBigLength; + + *ppabyBuffer = VSIGetMemFileBuffer( szFilename, &nBigLength, TRUE ); + *pnSize = (int) nBigLength; + + return CE_None; +} diff --git a/bazaar/plugin/gdal/frmts/gtiff/gt_wkt_srs.h b/bazaar/plugin/gdal/frmts/gtiff/gt_wkt_srs.h new file mode 100644 index 000000000..e1791c2b6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/gt_wkt_srs.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * $Id: gt_wkt_srs.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Implements translation between GeoTIFF normalized projection + * definitions and OpenGIS WKT SRS format. This code is + * deliberately GDAL free, and it is intended to be moved into + * libgeotiff someday if possible. + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GT_WKT_SRS_H_INCLUDED +#define GT_WKT_SRS_H_INCLUDED + +#include "cpl_port.h" + +#include "geotiff.h" +#include "geo_normalize.h" + +CPL_C_START +char CPL_DLL * GTIFGetOGISDefn( GTIF *, GTIFDefn * ); +int CPL_DLL GTIFSetFromOGISDefn( GTIF *, const char * ); +CPL_C_END + +#endif // GT_WKT_SRS_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/gtiff/gt_wkt_srs_for_gdal.h b/bazaar/plugin/gdal/frmts/gtiff/gt_wkt_srs_for_gdal.h new file mode 100644 index 000000000..8f6910357 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/gt_wkt_srs_for_gdal.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * $Id: gt_wkt_srs_for_gdal.h 29049 2015-04-29 15:54:12Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Read/Write in-memory GeoTIFF file + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GT_WKT_SRS_FOR_GDAL_H_INCLUDED +#define GT_WKT_SRS_FOR_GDAL_H_INCLUDED + +#include "cpl_port.h" +#include "gdal.h" + +CPL_C_START + +CPLErr CPL_DLL GTIFMemBufFromWkt( const char *pszWKT, + const double *padfGeoTransform, + int nGCPCount, const GDAL_GCP *pasGCPList, + int *pnSize, unsigned char **ppabyBuffer ); + +CPLErr GTIFMemBufFromWktEx( const char *pszWKT, + const double *padfGeoTransform, + int nGCPCount, const GDAL_GCP *pasGCPList, + int *pnSize, unsigned char **ppabyBuffer, + int bPixelIsPoint, char** papszRPCMD ); + +CPLErr CPL_DLL GTIFWktFromMemBuf( int nSize, unsigned char *pabyBuffer, + char **ppszWKT, double *padfGeoTransform, + int *pnGCPCount, GDAL_GCP **ppasGCPList ); + +CPLErr GTIFWktFromMemBufEx( int nSize, unsigned char *pabyBuffer, + char **ppszWKT, double *padfGeoTransform, + int *pnGCPCount, GDAL_GCP **ppasGCPList, + int *pbPixelIsPoint, char*** ppapszRPCMD ); + +CPL_C_END; + +#endif // GT_WKT_SRS_FOR_GDAL_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/gtiff/gt_wkt_srs_priv.h b/bazaar/plugin/gdal/frmts/gtiff/gt_wkt_srs_priv.h new file mode 100644 index 000000000..8660715ea --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/gt_wkt_srs_priv.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * $Id: gt_wkt_srs_priv.h 28263 2014-12-29 22:00:08Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Internal methods of gt_wkt_srs.cpp shared with gt_citation.cpp + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GT_WKT_SRS_PRIV_H_INCLUDED +#define GT_WKT_SRS_PRIV_H_INCLUDED + +#include "geotiff.h" + +int GDALGTIFKeyGetASCII( GTIF *hGTIF, geokey_t key, + char* szStr, + int nIndex, + int szStrMaxLen ); + +int GDALGTIFKeyGetSHORT( GTIF *hGTIF, geokey_t key, + short* pnVal, + int nIndex, + int nCount ); + +int GDALGTIFKeyGetDOUBLE( GTIF *hGTIF, geokey_t key, + double* pdfVal, + int nIndex, + int nCount ); + +#endif // GT_WKT_SRS_PRIV_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/gtiff/gtiff.h b/bazaar/plugin/gdal/frmts/gtiff/gtiff.h new file mode 100644 index 000000000..4347bc446 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/gtiff.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * $Id: gtiff.h 29049 2015-04-29 15:54:12Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: GDAL GeoTIFF support. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, 2002, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GTIFF_H_INCLUDED +#define GTIFF_H_INCLUDED + +#include "cpl_port.h" + +#include "tiffio.h" +#include "gdal.h" + +CPL_C_START +int GTiffOneTimeInit(); +void CPL_DLL LibgeotiffOneTimeInit(); +void LibgeotiffOneTimeCleanupMutex(); +CPL_C_END + +void GTIFFGetOverviewBlockSize(int* pnBlockXSize, int* pnBlockYSize); +void GTIFFSetJpegQuality(GDALDatasetH hGTIFFDS, int nJpegQuality); +int GTIFFGetCompressionMethod(const char* pszValue, const char* pszVariableName); + +void GTiffDatasetWriteRPCTag( TIFF *hTIFF, char **papszRPCMD ); +char** GTiffDatasetReadRPCTag( TIFF *hTIFF ); + +#define TIFFTAG_GDAL_METADATA 42112 +#define TIFFTAG_GDAL_NODATA 42113 +#define TIFFTAG_RPCCOEFFICIENT 50844 + +#if defined(TIFFLIB_VERSION) && TIFFLIB_VERSION >= 20081217 && defined(BIGTIFF_SUPPORT) +# define HAVE_UNSETFIELD +#endif + +#if defined(TIFFLIB_VERSION) && TIFFLIB_VERSION > 20041016 +/* We need at least TIFF 3.7.0 for TIFFGetSizeProc and TIFFClientdata */ +# define HAVE_TIFFGETSIZEPROC +#endif + +#if !defined(PREDICTOR_NONE) +#define PREDICTOR_NONE 1 +#endif + +#if !defined(COMPRESSION_LZMA) +#define COMPRESSION_LZMA 34925 /* LZMA2 */ +#endif + +#if !defined(TIFFTAG_LZMAPRESET) +#define TIFFTAG_LZMAPRESET 65562 /* LZMA2 preset (compression level) */ +#endif + +#endif // GTIFF_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/GNUmakefile b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/GNUmakefile new file mode 100644 index 000000000..d10dbb951 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/GNUmakefile @@ -0,0 +1,67 @@ + +include ../../../GDALmake.opt + +OBJ = \ + xtiff.o \ + geo_free.o \ + geo_get.o \ + geo_names.o \ + geo_new.o \ + geo_print.o \ + geo_set.o \ + geo_tiffp.o \ + geo_write.o \ + geo_normalize.o \ + geotiff_proj4.o \ + geo_extra.o \ + geo_trans.o \ + geo_simpletags.o + +O_OBJ = $(foreach file,$(OBJ),../../o/$(file)) + +ALL_C_FLAGS = $(CPPFLAGS) $(CFLAGS) + +ifeq ($(TIFF_SETTING),internal) +ALL_C_FLAGS := -I../libtiff $(ALL_C_FLAGS) +ifeq ($(RENAME_INTERNAL_LIBTIFF_SYMBOLS),yes) +ALL_C_FLAGS := -DRENAME_INTERNAL_LIBTIFF_SYMBOLS $(ALL_C_FLAGS) +endif +endif +ifeq ($(RENAME_INTERNAL_LIBGEOTIFF_SYMBOLS),yes) +ALL_C_FLAGS := -DRENAME_INTERNAL_LIBGEOTIFF_SYMBOLS $(ALL_C_FLAGS) +endif + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f $(O_OBJ) *.o *.a + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +../../o/%.$(OBJ_EXT): %.c + $(CC) -c -I../../port $(ALL_C_FLAGS) $< -o $@ + +# +# Updating to the latest libgeotiff involves copying all matching source +# except for a few files that hook to GDALs own CPL services. +# +import: + @if test ! -d ~/libgeotiff ; then \ + echo reimport requires libgeotiff checked out ~/libgeotiff ; \ + exit 1; \ + fi + + rm -rf safe + mkdir safe + mv cpl_serv.h geo_config.h safe + + copymatch.sh ~/libgeotiff *.cpp *.c *.h *.inc + copymatch.sh ~/libgeotiff/libxtiff xtiff*.c xtiffio.h + + mv safe/* . + rm -rf safe + + @echo + @echo 'Now do something like:' + @echo '% cvs commit -m "updated to libgeotiff 1.1.x"' + @echo diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/cpl_serv.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/cpl_serv.h new file mode 100644 index 000000000..3fe8dd5c4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/cpl_serv.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * cpl_serv.h + * + * This include file derived and simplified from the GDAL Common Portability + * Library. + */ + +#ifndef CPL_SERV_H_INCLUDED +#define CPL_SERV_H_INCLUDED + +/* ==================================================================== */ +/* Standard include files. */ +/* ==================================================================== */ + +#include "geo_config.h" + +#include "cpl_port.h" +#include "cpl_string.h" +#include "cpl_csv.h" + +#define GTIFAtof CPLAtof +#define GTIFStrtod CPLStrtod +/* + * Define an auxiliary symbol to help us to find when the internal cpl_serv.h + * is used instead of the external one from the geotiff package. + */ +#define CPL_SERV_H_INTERNAL 1 + +#endif /* ndef CPL_SERV_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/dump_symbols.sh b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/dump_symbols.sh new file mode 100644 index 000000000..a717a7c68 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/dump_symbols.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# GDAL specific script to extract exported libtiff symbols that can be renamed +# to keep them internal to GDAL as much as possible + +gcc *.c -fPIC -shared -o libgeotiff.so -I. -I../../../port + +OUT_FILE=gdal_libgeotiff_symbol_rename.h + +rm $OUT_FILE 2>/dev/null + +echo "/* This is a generated file by dump_symbols.h. *DO NOT EDIT MANUALLY !* */" >> $OUT_FILE + +symbol_list=$(objdump -t libgeotiff.so | grep .text | awk '{print $6}' | grep -v .text | grep -v __do_global | grep -v __bss_start | grep -v _edata | grep -v _end | grep -v _fini | grep -v _init | sort) +for symbol in $symbol_list +do + echo "#define $symbol gdal_$symbol" >> $OUT_FILE +done + +rodata_symbol_list=$(objdump -t libgeotiff.so | grep "\.rodata" | awk '{print $6}' | grep -v "\.") +for symbol in $data_symbol_list +do + echo "#define $symbol gdal_$symbol" >> $OUT_FILE +done + +data_symbol_list=$(objdump -t libgeotiff.so | grep "\.data" | awk '{print $6}' | grep -v "\.") +for symbol in $data_symbol_list +do + echo "#define $symbol gdal_$symbol" >> $OUT_FILE +done + +bss_symbol_list=$(objdump -t libgeotiff.so | grep "\.bss" | awk '{print $6}' | grep -v "\.") +for symbol in $bss_symbol_list +do + echo "#define $symbol gdal_$symbol" >> $OUT_FILE +done + +rm libgeotiff.so diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_datum.inc b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_datum.inc new file mode 100644 index 000000000..198e78ff2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_datum.inc @@ -0,0 +1,174 @@ +/* + * EPSG/POSC Datum database -- GeoTIFF Rev. 0.2 + */ + +/* C database for Geotiff include files. */ +/* the macro ValuePair() must be defined */ +/* by the enclosing include file */ + +#ifdef INCLUDE_OLD_CODES +#include old_datum.inc +#endif /* OLD Codes */ + +/* New datums */ +ValuePair(Datum_Dealul_Piscului_1970,6317) + +/* Datums for which only the ellipsoid is known */ +ValuePair(DatumE_Airy1830, 6001) +ValuePair(DatumE_AiryModified1849, 6002) +ValuePair(DatumE_AustralianNationalSpheroid, 6003) +ValuePair(DatumE_Bessel1841, 6004) +ValuePair(DatumE_BesselModified, 6005) +ValuePair(DatumE_BesselNamibia, 6006) +ValuePair(DatumE_Clarke1858, 6007) +ValuePair(DatumE_Clarke1866, 6008) +ValuePair(DatumE_Clarke1866Michigan, 6009) +ValuePair(DatumE_Clarke1880_Benoit, 6010) +ValuePair(DatumE_Clarke1880_IGN, 6011) +ValuePair(DatumE_Clarke1880_RGS, 6012) +ValuePair(DatumE_Clarke1880_Arc, 6013) +ValuePair(DatumE_Clarke1880_SGA1922, 6014) +ValuePair(DatumE_Everest1830_1937Adjustment, 6015) +ValuePair(DatumE_Everest1830_1967Definition, 6016) +ValuePair(DatumE_Everest1830_1975Definition, 6017) +ValuePair(DatumE_Everest1830Modified, 6018) +ValuePair(DatumE_GRS1980, 6019) +ValuePair(DatumE_Helmert1906, 6020) +ValuePair(DatumE_IndonesianNationalSpheroid, 6021) +ValuePair(DatumE_International1924, 6022) +ValuePair(DatumE_International1967, 6023) +ValuePair(DatumE_Krassowsky1960, 6024) +ValuePair(DatumE_NWL9D, 6025) +ValuePair(DatumE_NWL10D, 6026) +ValuePair(DatumE_Plessis1817, 6027) +ValuePair(DatumE_Struve1860, 6028) +ValuePair(DatumE_WarOffice, 6029) +ValuePair(DatumE_WGS84, 6030) +ValuePair(DatumE_GEM10C, 6031) +ValuePair(DatumE_OSU86F, 6032) +ValuePair(DatumE_OSU91A, 6033) +ValuePair(DatumE_Clarke1880, 6034) +ValuePair(DatumE_Sphere, 6035) + +/* standard datums */ +ValuePair(Datum_Adindan, 6201) +ValuePair(Datum_Australian_Geodetic_Datum_1966, 6202) +ValuePair(Datum_Australian_Geodetic_Datum_1984, 6203) +ValuePair(Datum_Ain_el_Abd_1970, 6204) +ValuePair(Datum_Afgooye, 6205) +ValuePair(Datum_Agadez, 6206) +ValuePair(Datum_Lisbon, 6207) +ValuePair(Datum_Aratu, 6208) +ValuePair(Datum_Arc_1950, 6209) +ValuePair(Datum_Arc_1960, 6210) +ValuePair(Datum_Batavia, 6211) +ValuePair(Datum_Barbados, 6212) +ValuePair(Datum_Beduaram, 6213) +ValuePair(Datum_Beijing_1954, 6214) +ValuePair(Datum_Reseau_National_Belge_1950, 6215) +ValuePair(Datum_Bermuda_1957, 6216) +ValuePair(Datum_Bern_1898, 6217) +ValuePair(Datum_Bogota, 6218) +ValuePair(Datum_Bukit_Rimpah, 6219) +ValuePair(Datum_Camacupa, 6220) +ValuePair(Datum_Campo_Inchauspe, 6221) +ValuePair(Datum_Cape, 6222) +ValuePair(Datum_Carthage, 6223) +ValuePair(Datum_Chua, 6224) +ValuePair(Datum_Corrego_Alegre, 6225) +ValuePair(Datum_Cote_d_Ivoire, 6226) +ValuePair(Datum_Deir_ez_Zor, 6227) +ValuePair(Datum_Douala, 6228) +ValuePair(Datum_Egypt_1907, 6229) +ValuePair(Datum_European_Datum_1950, 6230) +ValuePair(Datum_European_Datum_1987, 6231) +ValuePair(Datum_Fahud, 6232) +ValuePair(Datum_Gandajika_1970, 6233) +ValuePair(Datum_Garoua, 6234) +ValuePair(Datum_Guyane_Francaise, 6235) +ValuePair(Datum_Hu_Tzu_Shan, 6236) +ValuePair(Datum_Hungarian_Datum_1972, 6237) +ValuePair(Datum_Indonesian_Datum_1974, 6238) +ValuePair(Datum_Indian_1954, 6239) +ValuePair(Datum_Indian_1975, 6240) +ValuePair(Datum_Jamaica_1875, 6241) +ValuePair(Datum_Jamaica_1969, 6242) +ValuePair(Datum_Kalianpur, 6243) +ValuePair(Datum_Kandawala, 6244) +ValuePair(Datum_Kertau, 6245) +ValuePair(Datum_Kuwait_Oil_Company, 6246) +ValuePair(Datum_La_Canoa, 6247) +ValuePair(Datum_Provisional_S_American_Datum_1956, 6248) +ValuePair(Datum_Lake, 6249) +ValuePair(Datum_Leigon, 6250) +ValuePair(Datum_Liberia_1964, 6251) +ValuePair(Datum_Lome, 6252) +ValuePair(Datum_Luzon_1911, 6253) +ValuePair(Datum_Hito_XVIII_1963, 6254) +ValuePair(Datum_Herat_North, 6255) +ValuePair(Datum_Mahe_1971, 6256) +ValuePair(Datum_Makassar, 6257) +ValuePair(Datum_European_Reference_System_1989, 6258) +ValuePair(Datum_Malongo_1987, 6259) +ValuePair(Datum_Manoca, 6260) +ValuePair(Datum_Merchich, 6261) +ValuePair(Datum_Massawa, 6262) +ValuePair(Datum_Minna, 6263) +ValuePair(Datum_Mhast, 6264) +ValuePair(Datum_Monte_Mario, 6265) +ValuePair(Datum_M_poraloko, 6266) +ValuePair(Datum_North_American_Datum_1927, 6267) +ValuePair(Datum_NAD_Michigan, 6268) +ValuePair(Datum_North_American_Datum_1983, 6269) +ValuePair(Datum_Nahrwan_1967, 6270) +ValuePair(Datum_Naparima_1972, 6271) +ValuePair(Datum_New_Zealand_Geodetic_Datum_1949, 6272) +ValuePair(Datum_NGO_1948, 6273) +ValuePair(Datum_Datum_73, 6274) +ValuePair(Datum_Nouvelle_Triangulation_Francaise, 6275) +ValuePair(Datum_NSWC_9Z_2, 6276) +ValuePair(Datum_OSGB_1936, 6277) +ValuePair(Datum_OSGB_1970_SN, 6278) +ValuePair(Datum_OS_SN_1980, 6279) +ValuePair(Datum_Padang_1884, 6280) +ValuePair(Datum_Palestine_1923, 6281) +ValuePair(Datum_Pointe_Noire, 6282) +ValuePair(Datum_Geocentric_Datum_of_Australia_1994, 6283) +ValuePair(Datum_Pulkovo_1942, 6284) +ValuePair(Datum_Qatar, 6285) +ValuePair(Datum_Qatar_1948, 6286) +ValuePair(Datum_Qornoq, 6287) +ValuePair(Datum_Loma_Quintana, 6288) +ValuePair(Datum_Amersfoort, 6289) +ValuePair(Datum_RT38, 6290) +ValuePair(Datum_South_American_Datum_1969, 6291) +ValuePair(Datum_Sapper_Hill_1943, 6292) +ValuePair(Datum_Schwarzeck, 6293) +ValuePair(Datum_Segora, 6294) +ValuePair(Datum_Serindung, 6295) +ValuePair(Datum_Sudan, 6296) +ValuePair(Datum_Tananarive_1925, 6297) +ValuePair(Datum_Timbalai_1948, 6298) +ValuePair(Datum_TM65, 6299) +ValuePair(Datum_TM75, 6300) +ValuePair(Datum_Tokyo, 6301) +ValuePair(Datum_Trinidad_1903, 6302) +ValuePair(Datum_Trucial_Coast_1948, 6303) +ValuePair(Datum_Voirol_1875, 6304) +ValuePair(Datum_Voirol_Unifie_1960, 6305) +ValuePair(Datum_Bern_1938, 6306) +ValuePair(Datum_Nord_Sahara_1959, 6307) +ValuePair(Datum_Stockholm_1938, 6308) +ValuePair(Datum_Yacare, 6309) +ValuePair(Datum_Yoff, 6310) +ValuePair(Datum_Zanderij, 6311) +ValuePair(Datum_Militar_Geographische_Institut, 6312) +ValuePair(Datum_Reseau_National_Belge_1972, 6313) +ValuePair(Datum_Deutsche_Hauptdreiecksnetz, 6314) +ValuePair(Datum_Conakry_1905, 6315) +ValuePair(Datum_WGS72, 6322) +ValuePair(Datum_WGS72_Transit_Broadcast_Ephemeris, 6324) +ValuePair(Datum_WGS84, 6326) +ValuePair(Datum_Ancienne_Triangulation_Francaise, 6901) +ValuePair(Datum_Nord_de_Guerre, 6902) +/* end of list */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_ellipse.inc b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_ellipse.inc new file mode 100644 index 000000000..95c14321f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_ellipse.inc @@ -0,0 +1,48 @@ +/* + * GeoTIFF Rev. 0.2 Ellipsoids + */ + +/* C database for Geotiff include files. */ +/* the macro ValuePair() must be defined */ +/* by the enclosing include file */ + +#ifdef INCLUDE_OLD_CODES +#include old_ellipse.inc +#endif /* OLD Codes */ + +ValuePair(Ellipse_Airy_1830, 7001) +ValuePair(Ellipse_Airy_Modified_1849, 7002) +ValuePair(Ellipse_Australian_National_Spheroid, 7003) +ValuePair(Ellipse_Bessel_1841, 7004) +ValuePair(Ellipse_Bessel_Modified, 7005) +ValuePair(Ellipse_Bessel_Namibia, 7006) +ValuePair(Ellipse_Clarke_1858, 7007) +ValuePair(Ellipse_Clarke_1866, 7008) +ValuePair(Ellipse_Clarke_1866_Michigan, 7009) +ValuePair(Ellipse_Clarke_1880_Benoit, 7010) +ValuePair(Ellipse_Clarke_1880_IGN, 7011) +ValuePair(Ellipse_Clarke_1880_RGS, 7012) +ValuePair(Ellipse_Clarke_1880_Arc, 7013) +ValuePair(Ellipse_Clarke_1880_SGA_1922, 7014) +ValuePair(Ellipse_Everest_1830_1937_Adjustment, 7015) +ValuePair(Ellipse_Everest_1830_1967_Definition, 7016) +ValuePair(Ellipse_Everest_1830_1975_Definition, 7017) +ValuePair(Ellipse_Everest_1830_Modified, 7018) +ValuePair(Ellipse_GRS_1980, 7019) +ValuePair(Ellipse_Helmert_1906, 7020) +ValuePair(Ellipse_Indonesian_National_Spheroid, 7021) +ValuePair(Ellipse_International_1924, 7022) +ValuePair(Ellipse_International_1967, 7023) +ValuePair(Ellipse_Krassowsky_1940, 7024) +ValuePair(Ellipse_NWL_9D, 7025) +ValuePair(Ellipse_NWL_10D, 7026) +ValuePair(Ellipse_Plessis_1817, 7027) +ValuePair(Ellipse_Struve_1860, 7028) +ValuePair(Ellipse_War_Office, 7029) +ValuePair(Ellipse_WGS_84, 7030) +ValuePair(Ellipse_GEM_10C, 7031) +ValuePair(Ellipse_OSU86F, 7032) +ValuePair(Ellipse_OSU91A, 7033) +ValuePair(Ellipse_Clarke_1880, 7034) +ValuePair(Ellipse_Sphere, 7035) +/* end of list */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_gcs.inc b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_gcs.inc new file mode 100644 index 000000000..4917964d8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_gcs.inc @@ -0,0 +1,193 @@ +/* + * EPSG/POSC GCS Codes -- GeoTIFF Rev. 0.2 + */ + +/* C database for Geotiff include files. */ +/* the macro ValuePair() must be defined */ +/* by the enclosing include file */ + +#ifdef INCLUDE_OLD_CODES +#include old_gcs.inc +#endif /* OLD Codes */ + +/* Unspecified GCS based on ellipsoid */ +ValuePair(GCSE_Airy1830, 4001) +ValuePair(GCSE_AiryModified1849, 4002) +ValuePair(GCSE_AustralianNationalSpheroid, 4003) +ValuePair(GCSE_Bessel1841, 4004) +ValuePair(GCSE_BesselModified, 4005) +ValuePair(GCSE_BesselNamibia, 4006) +ValuePair(GCSE_Clarke1858, 4007) +ValuePair(GCSE_Clarke1866, 4008) +ValuePair(GCSE_Clarke1866Michigan, 4009) +ValuePair(GCSE_Clarke1880_Benoit, 4010) +ValuePair(GCSE_Clarke1880_IGN, 4011) +ValuePair(GCSE_Clarke1880_RGS, 4012) +ValuePair(GCSE_Clarke1880_Arc, 4013) +ValuePair(GCSE_Clarke1880_SGA1922, 4014) +ValuePair(GCSE_Everest1830_1937Adjustment, 4015) +ValuePair(GCSE_Everest1830_1967Definition, 4016) +ValuePair(GCSE_Everest1830_1975Definition, 4017) +ValuePair(GCSE_Everest1830Modified, 4018) +ValuePair(GCSE_GRS1980, 4019) +ValuePair(GCSE_Helmert1906, 4020) +ValuePair(GCSE_IndonesianNationalSpheroid, 4021) +ValuePair(GCSE_International1924, 4022) +ValuePair(GCSE_International1967, 4023) +ValuePair(GCSE_Krassowsky1940, 4024) +ValuePair(GCSE_NWL9D, 4025) +ValuePair(GCSE_NWL10D, 4026) +ValuePair(GCSE_Plessis1817, 4027) +ValuePair(GCSE_Struve1860, 4028) +ValuePair(GCSE_WarOffice, 4029) +ValuePair(GCSE_WGS84, 4030) +ValuePair(GCSE_GEM10C, 4031) +ValuePair(GCSE_OSU86F, 4032) +ValuePair(GCSE_OSU91A, 4033) +ValuePair(GCSE_Clarke1880, 4034) +ValuePair(GCSE_Sphere, 4035) + +/* New GCS */ +ValuePair(GCS_Greek,4120) +ValuePair(GCS_GGRS87,4121) +ValuePair(GCS_KKJ,4123) +ValuePair(GCS_RT90,4124) +ValuePair(GCS_EST92,4133) +ValuePair(GCS_Dealul_Piscului_1970,4317) +ValuePair(GCS_Greek_Athens,4815) + +/* Standard GCS */ +ValuePair(GCS_Adindan, 4201) +ValuePair(GCS_AGD66, 4202) +ValuePair(GCS_AGD84, 4203) +ValuePair(GCS_Ain_el_Abd, 4204) +ValuePair(GCS_Afgooye, 4205) +ValuePair(GCS_Agadez, 4206) +ValuePair(GCS_Lisbon, 4207) +ValuePair(GCS_Aratu, 4208) +ValuePair(GCS_Arc_1950, 4209) +ValuePair(GCS_Arc_1960, 4210) +ValuePair(GCS_Batavia, 4211) +ValuePair(GCS_Barbados, 4212) +ValuePair(GCS_Beduaram, 4213) +ValuePair(GCS_Beijing_1954, 4214) +ValuePair(GCS_Belge_1950, 4215) +ValuePair(GCS_Bermuda_1957, 4216) +ValuePair(GCS_Bern_1898, 4217) +ValuePair(GCS_Bogota, 4218) +ValuePair(GCS_Bukit_Rimpah, 4219) +ValuePair(GCS_Camacupa, 4220) +ValuePair(GCS_Campo_Inchauspe, 4221) +ValuePair(GCS_Cape, 4222) +ValuePair(GCS_Carthage, 4223) +ValuePair(GCS_Chua, 4224) +ValuePair(GCS_Corrego_Alegre, 4225) +ValuePair(GCS_Cote_d_Ivoire, 4226) +ValuePair(GCS_Deir_ez_Zor, 4227) +ValuePair(GCS_Douala, 4228) +ValuePair(GCS_Egypt_1907, 4229) +ValuePair(GCS_ED50, 4230) +ValuePair(GCS_ED87, 4231) +ValuePair(GCS_Fahud, 4232) +ValuePair(GCS_Gandajika_1970, 4233) +ValuePair(GCS_Garoua, 4234) +ValuePair(GCS_Guyane_Francaise, 4235) +ValuePair(GCS_Hu_Tzu_Shan, 4236) +ValuePair(GCS_HD72, 4237) +ValuePair(GCS_ID74, 4238) +ValuePair(GCS_Indian_1954, 4239) +ValuePair(GCS_Indian_1975, 4240) +ValuePair(GCS_Jamaica_1875, 4241) +ValuePair(GCS_JAD69, 4242) +ValuePair(GCS_Kalianpur, 4243) +ValuePair(GCS_Kandawala, 4244) +ValuePair(GCS_Kertau, 4245) +ValuePair(GCS_KOC, 4246) +ValuePair(GCS_La_Canoa, 4247) +ValuePair(GCS_PSAD56, 4248) +ValuePair(GCS_Lake, 4249) +ValuePair(GCS_Leigon, 4250) +ValuePair(GCS_Liberia_1964, 4251) +ValuePair(GCS_Lome, 4252) +ValuePair(GCS_Luzon_1911, 4253) +ValuePair(GCS_Hito_XVIII_1963, 4254) +ValuePair(GCS_Herat_North, 4255) +ValuePair(GCS_Mahe_1971, 4256) +ValuePair(GCS_Makassar, 4257) +ValuePair(GCS_EUREF89, 4258) +ValuePair(GCS_Malongo_1987, 4259) +ValuePair(GCS_Manoca, 4260) +ValuePair(GCS_Merchich, 4261) +ValuePair(GCS_Massawa, 4262) +ValuePair(GCS_Minna, 4263) +ValuePair(GCS_Mhast, 4264) +ValuePair(GCS_Monte_Mario, 4265) +ValuePair(GCS_M_poraloko, 4266) +ValuePair(GCS_NAD27, 4267) +ValuePair(GCS_NAD_Michigan, 4268) +ValuePair(GCS_NAD83, 4269) +ValuePair(GCS_Nahrwan_1967, 4270) +ValuePair(GCS_Naparima_1972, 4271) +ValuePair(GCS_GD49, 4272) +ValuePair(GCS_NGO_1948, 4273) +ValuePair(GCS_Datum_73, 4274) +ValuePair(GCS_NTF, 4275) +ValuePair(GCS_NSWC_9Z_2, 4276) +ValuePair(GCS_OSGB_1936, 4277) +ValuePair(GCS_OSGB70, 4278) +ValuePair(GCS_OS_SN80, 4279) +ValuePair(GCS_Padang, 4280) +ValuePair(GCS_Palestine_1923, 4281) +ValuePair(GCS_Pointe_Noire, 4282) +ValuePair(GCS_GDA94, 4283) +ValuePair(GCS_Pulkovo_1942, 4284) +ValuePair(GCS_Qatar, 4285) +ValuePair(GCS_Qatar_1948, 4286) +ValuePair(GCS_Qornoq, 4287) +ValuePair(GCS_Loma_Quintana, 4288) +ValuePair(GCS_Amersfoort, 4289) +ValuePair(GCS_RT38, 4290) +ValuePair(GCS_SAD69, 4291) +ValuePair(GCS_Sapper_Hill_1943, 4292) +ValuePair(GCS_Schwarzeck, 4293) +ValuePair(GCS_Segora, 4294) +ValuePair(GCS_Serindung, 4295) +ValuePair(GCS_Sudan, 4296) +ValuePair(GCS_Tananarive, 4297) +ValuePair(GCS_Timbalai_1948, 4298) +ValuePair(GCS_TM65, 4299) +ValuePair(GCS_TM75, 4300) +ValuePair(GCS_Tokyo, 4301) +ValuePair(GCS_Trinidad_1903, 4302) +ValuePair(GCS_TC_1948, 4303) +ValuePair(GCS_Voirol_1875, 4304) +ValuePair(GCS_Voirol_Unifie, 4305) +ValuePair(GCS_Bern_1938, 4306) +ValuePair(GCS_Nord_Sahara_1959, 4307) +ValuePair(GCS_Stockholm_1938, 4308) +ValuePair(GCS_Yacare, 4309) +ValuePair(GCS_Yoff, 4310) +ValuePair(GCS_Zanderij, 4311) +ValuePair(GCS_MGI, 4312) +ValuePair(GCS_Belge_1972, 4313) +ValuePair(GCS_DHDN, 4314) +ValuePair(GCS_Conakry_1905, 4315) +ValuePair(GCS_WGS_72, 4322) +ValuePair(GCS_WGS_72BE, 4324) +ValuePair(GCS_WGS_84, 4326) +ValuePair(GCS_Bern_1898_Bern, 4801) +ValuePair(GCS_Bogota_Bogota, 4802) +ValuePair(GCS_Lisbon_Lisbon, 4803) +ValuePair(GCS_Makassar_Jakarta, 4804) +ValuePair(GCS_MGI_Ferro, 4805) +ValuePair(GCS_Monte_Mario_Rome, 4806) +ValuePair(GCS_NTF_Paris, 4807) +ValuePair(GCS_Padang_Jakarta, 4808) +ValuePair(GCS_Belge_1950_Brussels, 4809) +ValuePair(GCS_Tananarive_Paris, 4810) +ValuePair(GCS_Voirol_1875_Paris, 4811) +ValuePair(GCS_Voirol_Unifie_Paris, 4812) +ValuePair(GCS_Batavia_Jakarta, 4813) +ValuePair(GCS_ATF_Paris, 4901) +ValuePair(GCS_NDG_Paris, 4902) +/* End of list */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_pcs.inc b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_pcs.inc new file mode 100644 index 000000000..e8a5ef50a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_pcs.inc @@ -0,0 +1,1012 @@ +/* + * EPSG PCS Codes - GeoTIFF Rev 0.2 + */ + +/* C database for Geotiff include files. */ +/* the macro ValuePair() must be defined */ +/* by the enclosing include file */ + +#ifdef INCLUDE_OLD_CODES +#include old_pcs.inc +#endif /* OLD Codes */ + +/* Newer PCS */ +ValuePair(PCS_Hjorsey_1955_Lambert, 3053) +ValuePair(PCS_ISN93_Lambert_1993, 3057) +ValuePair(PCS_ETRS89_Poland_CS2000_zone_5,2176) +ValuePair(PCS_ETRS89_Poland_CS2000_zone_6,2177) +ValuePair(PCS_ETRS89_Poland_CS2000_zone_7,2177) +ValuePair(PCS_ETRS89_Poland_CS2000_zone_8,2178) +ValuePair(PCS_ETRS89_Poland_CS92,2180) + +/* New PCS */ +ValuePair(PCS_GGRS87_Greek_Grid,2100) +ValuePair(PCS_KKJ_Finland_zone_1,2391) +ValuePair(PCS_KKJ_Finland_zone_2,2392) +ValuePair(PCS_KKJ_Finland_zone_3,2393) +ValuePair(PCS_KKJ_Finland_zone_4,2394) +ValuePair(PCS_RT90_2_5_gon_W,2400) +ValuePair(PCS_Lietuvos_Koordinoei_Sistema_1994,2600) +ValuePair(PCS_Estonian_Coordinate_System_of_1992,3300) +ValuePair(PCS_HD72_EOV,23700) +ValuePair(PCS_Dealul_Piscului_1970_Stereo_70,31700) + +ValuePair(PCS_Adindan_UTM_zone_37N, 20137) +ValuePair(PCS_Adindan_UTM_zone_38N, 20138) +ValuePair(PCS_AGD66_AMG_zone_48, 20248) +ValuePair(PCS_AGD66_AMG_zone_49, 20249) +ValuePair(PCS_AGD66_AMG_zone_50, 20250) +ValuePair(PCS_AGD66_AMG_zone_51, 20251) +ValuePair(PCS_AGD66_AMG_zone_52, 20252) +ValuePair(PCS_AGD66_AMG_zone_53, 20253) +ValuePair(PCS_AGD66_AMG_zone_54, 20254) +ValuePair(PCS_AGD66_AMG_zone_55, 20255) +ValuePair(PCS_AGD66_AMG_zone_56, 20256) +ValuePair(PCS_AGD66_AMG_zone_57, 20257) +ValuePair(PCS_AGD66_AMG_zone_58, 20258) +ValuePair(PCS_AGD84_AMG_zone_48, 20348) +ValuePair(PCS_AGD84_AMG_zone_49, 20349) +ValuePair(PCS_AGD84_AMG_zone_50, 20350) +ValuePair(PCS_AGD84_AMG_zone_51, 20351) +ValuePair(PCS_AGD84_AMG_zone_52, 20352) +ValuePair(PCS_AGD84_AMG_zone_53, 20353) +ValuePair(PCS_AGD84_AMG_zone_54, 20354) +ValuePair(PCS_AGD84_AMG_zone_55, 20355) +ValuePair(PCS_AGD84_AMG_zone_56, 20356) +ValuePair(PCS_AGD84_AMG_zone_57, 20357) +ValuePair(PCS_AGD84_AMG_zone_58, 20358) +ValuePair(PCS_Ain_el_Abd_UTM_zone_37N, 20437) +ValuePair(PCS_Ain_el_Abd_UTM_zone_38N, 20438) +ValuePair(PCS_Ain_el_Abd_UTM_zone_39N, 20439) +ValuePair(PCS_Ain_el_Abd_Bahrain_Grid, 20499) +ValuePair(PCS_Afgooye_UTM_zone_38N, 20538) +ValuePair(PCS_Afgooye_UTM_zone_39N, 20539) +ValuePair(PCS_Lisbon_Portugese_Grid, 20700) +ValuePair(PCS_Aratu_UTM_zone_22S, 20822) +ValuePair(PCS_Aratu_UTM_zone_23S, 20823) +ValuePair(PCS_Aratu_UTM_zone_24S, 20824) +ValuePair(PCS_Arc_1950_Lo13, 20973) +ValuePair(PCS_Arc_1950_Lo15, 20975) +ValuePair(PCS_Arc_1950_Lo17, 20977) +ValuePair(PCS_Arc_1950_Lo19, 20979) +ValuePair(PCS_Arc_1950_Lo21, 20981) +ValuePair(PCS_Arc_1950_Lo23, 20983) +ValuePair(PCS_Arc_1950_Lo25, 20985) +ValuePair(PCS_Arc_1950_Lo27, 20987) +ValuePair(PCS_Arc_1950_Lo29, 20989) +ValuePair(PCS_Arc_1950_Lo31, 20991) +ValuePair(PCS_Arc_1950_Lo33, 20993) +ValuePair(PCS_Arc_1950_Lo35, 20995) +ValuePair(PCS_Batavia_NEIEZ, 21100) +ValuePair(PCS_Batavia_UTM_zone_48S, 21148) +ValuePair(PCS_Batavia_UTM_zone_49S, 21149) +ValuePair(PCS_Batavia_UTM_zone_50S, 21150) +ValuePair(PCS_Beijing_Gauss_zone_13, 21413) +ValuePair(PCS_Beijing_Gauss_zone_14, 21414) +ValuePair(PCS_Beijing_Gauss_zone_15, 21415) +ValuePair(PCS_Beijing_Gauss_zone_16, 21416) +ValuePair(PCS_Beijing_Gauss_zone_17, 21417) +ValuePair(PCS_Beijing_Gauss_zone_18, 21418) +ValuePair(PCS_Beijing_Gauss_zone_19, 21419) +ValuePair(PCS_Beijing_Gauss_zone_20, 21420) +ValuePair(PCS_Beijing_Gauss_zone_21, 21421) +ValuePair(PCS_Beijing_Gauss_zone_22, 21422) +ValuePair(PCS_Beijing_Gauss_zone_23, 21423) +ValuePair(PCS_Beijing_Gauss_13N, 21473) +ValuePair(PCS_Beijing_Gauss_14N, 21474) +ValuePair(PCS_Beijing_Gauss_15N, 21475) +ValuePair(PCS_Beijing_Gauss_16N, 21476) +ValuePair(PCS_Beijing_Gauss_17N, 21477) +ValuePair(PCS_Beijing_Gauss_18N, 21478) +ValuePair(PCS_Beijing_Gauss_19N, 21479) +ValuePair(PCS_Beijing_Gauss_20N, 21480) +ValuePair(PCS_Beijing_Gauss_21N, 21481) +ValuePair(PCS_Beijing_Gauss_22N, 21482) +ValuePair(PCS_Beijing_Gauss_23N, 21483) +ValuePair(PCS_Belge_Lambert_50, 21500) +ValuePair(PCS_Bern_1898_Swiss_Old, 21790) +ValuePair(PCS_Bogota_UTM_zone_17N, 21817) +ValuePair(PCS_Bogota_UTM_zone_18N, 21818) +ValuePair(PCS_Bogota_Colombia_3W, 21891) +ValuePair(PCS_Bogota_Colombia_Bogota, 21892) +ValuePair(PCS_Bogota_Colombia_3E, 21893) +ValuePair(PCS_Bogota_Colombia_6E, 21894) +ValuePair(PCS_Camacupa_UTM_32S, 22032) +ValuePair(PCS_Camacupa_UTM_33S, 22033) +ValuePair(PCS_C_Inchauspe_Argentina_1, 22191) +ValuePair(PCS_C_Inchauspe_Argentina_2, 22192) +ValuePair(PCS_C_Inchauspe_Argentina_3, 22193) +ValuePair(PCS_C_Inchauspe_Argentina_4, 22194) +ValuePair(PCS_C_Inchauspe_Argentina_5, 22195) +ValuePair(PCS_C_Inchauspe_Argentina_6, 22196) +ValuePair(PCS_C_Inchauspe_Argentina_7, 22197) +ValuePair(PCS_Carthage_UTM_zone_32N, 22332) +ValuePair(PCS_Carthage_Nord_Tunisie, 22391) +ValuePair(PCS_Carthage_Sud_Tunisie, 22392) +ValuePair(PCS_Corrego_Alegre_UTM_23S, 22523) +ValuePair(PCS_Corrego_Alegre_UTM_24S, 22524) +ValuePair(PCS_Douala_UTM_zone_32N, 22832) +ValuePair(PCS_Egypt_1907_Red_Belt, 22992) +ValuePair(PCS_Egypt_1907_Purple_Belt, 22993) +ValuePair(PCS_Egypt_1907_Ext_Purple, 22994) +ValuePair(PCS_ED50_UTM_zone_28N, 23028) +ValuePair(PCS_ED50_UTM_zone_29N, 23029) +ValuePair(PCS_ED50_UTM_zone_30N, 23030) +ValuePair(PCS_ED50_UTM_zone_31N, 23031) +ValuePair(PCS_ED50_UTM_zone_32N, 23032) +ValuePair(PCS_ED50_UTM_zone_33N, 23033) +ValuePair(PCS_ED50_UTM_zone_34N, 23034) +ValuePair(PCS_ED50_UTM_zone_35N, 23035) +ValuePair(PCS_ED50_UTM_zone_36N, 23036) +ValuePair(PCS_ED50_UTM_zone_37N, 23037) +ValuePair(PCS_ED50_UTM_zone_38N, 23038) +ValuePair(PCS_Fahud_UTM_zone_39N, 23239) +ValuePair(PCS_Fahud_UTM_zone_40N, 23240) +ValuePair(PCS_Garoua_UTM_zone_33N, 23433) +ValuePair(PCS_ID74_UTM_zone_46N, 23846) +ValuePair(PCS_ID74_UTM_zone_47N, 23847) +ValuePair(PCS_ID74_UTM_zone_48N, 23848) +ValuePair(PCS_ID74_UTM_zone_49N, 23849) +ValuePair(PCS_ID74_UTM_zone_50N, 23850) +ValuePair(PCS_ID74_UTM_zone_51N, 23851) +ValuePair(PCS_ID74_UTM_zone_52N, 23852) +ValuePair(PCS_ID74_UTM_zone_53N, 23853) +ValuePair(PCS_ID74_UTM_zone_46S, 23886) +ValuePair(PCS_ID74_UTM_zone_47S, 23887) +ValuePair(PCS_ID74_UTM_zone_48S, 23888) +ValuePair(PCS_ID74_UTM_zone_49S, 23889) +ValuePair(PCS_ID74_UTM_zone_50S, 23890) +ValuePair(PCS_ID74_UTM_zone_51S, 23891) +ValuePair(PCS_ID74_UTM_zone_52S, 23892) +ValuePair(PCS_ID74_UTM_zone_53S, 23893) +ValuePair(PCS_ID74_UTM_zone_54S, 23894) +ValuePair(PCS_Indian_1954_UTM_47N, 23947) +ValuePair(PCS_Indian_1954_UTM_48N, 23948) +ValuePair(PCS_Indian_1975_UTM_47N, 24047) +ValuePair(PCS_Indian_1975_UTM_48N, 24048) +ValuePair(PCS_Jamaica_1875_Old_Grid, 24100) +ValuePair(PCS_JAD69_Jamaica_Grid, 24200) +ValuePair(PCS_Kalianpur_India_0, 24370) +ValuePair(PCS_Kalianpur_India_I, 24371) +ValuePair(PCS_Kalianpur_India_IIa, 24372) +ValuePair(PCS_Kalianpur_India_IIIa, 24373) +ValuePair(PCS_Kalianpur_India_IVa, 24374) +ValuePair(PCS_Kalianpur_India_IIb, 24382) +ValuePair(PCS_Kalianpur_India_IIIb, 24383) +ValuePair(PCS_Kalianpur_India_IVb, 24384) +ValuePair(PCS_Kertau_Singapore_Grid, 24500) +ValuePair(PCS_Kertau_UTM_zone_47N, 24547) +ValuePair(PCS_Kertau_UTM_zone_48N, 24548) +ValuePair(PCS_La_Canoa_UTM_zone_20N, 24720) +ValuePair(PCS_La_Canoa_UTM_zone_21N, 24721) +ValuePair(PCS_PSAD56_UTM_zone_18N, 24818) +ValuePair(PCS_PSAD56_UTM_zone_19N, 24819) +ValuePair(PCS_PSAD56_UTM_zone_20N, 24820) +ValuePair(PCS_PSAD56_UTM_zone_21N, 24821) +ValuePair(PCS_PSAD56_UTM_zone_17S, 24877) +ValuePair(PCS_PSAD56_UTM_zone_18S, 24878) +ValuePair(PCS_PSAD56_UTM_zone_19S, 24879) +ValuePair(PCS_PSAD56_UTM_zone_20S, 24880) +ValuePair(PCS_PSAD56_Peru_west_zone, 24891) +ValuePair(PCS_PSAD56_Peru_central, 24892) +ValuePair(PCS_PSAD56_Peru_east_zone, 24893) +ValuePair(PCS_Leigon_Ghana_Grid, 25000) +ValuePair(PCS_Lome_UTM_zone_31N, 25231) +ValuePair(PCS_Luzon_Philippines_I, 25391) +ValuePair(PCS_Luzon_Philippines_II, 25392) +ValuePair(PCS_Luzon_Philippines_III, 25393) +ValuePair(PCS_Luzon_Philippines_IV, 25394) +ValuePair(PCS_Luzon_Philippines_V, 25395) +ValuePair(PCS_Makassar_NEIEZ, 25700) +ValuePair(PCS_Malongo_1987_UTM_32S, 25932) +ValuePair(PCS_Merchich_Nord_Maroc, 26191) +ValuePair(PCS_Merchich_Sud_Maroc, 26192) +ValuePair(PCS_Merchich_Sahara, 26193) +ValuePair(PCS_Massawa_UTM_zone_37N, 26237) +ValuePair(PCS_Minna_UTM_zone_31N, 26331) +ValuePair(PCS_Minna_UTM_zone_32N, 26332) +ValuePair(PCS_Minna_Nigeria_West, 26391) +ValuePair(PCS_Minna_Nigeria_Mid_Belt, 26392) +ValuePair(PCS_Minna_Nigeria_East, 26393) +ValuePair(PCS_Mhast_UTM_zone_32S, 26432) +ValuePair(PCS_Monte_Mario_Italy_1, 26591) +ValuePair(PCS_Monte_Mario_Italy_2, 26592) +ValuePair(PCS_M_poraloko_UTM_32N, 26632) +ValuePair(PCS_M_poraloko_UTM_32S, 26692) +ValuePair(PCS_NAD27_UTM_zone_3N, 26703) +ValuePair(PCS_NAD27_UTM_zone_4N, 26704) +ValuePair(PCS_NAD27_UTM_zone_5N, 26705) +ValuePair(PCS_NAD27_UTM_zone_6N, 26706) +ValuePair(PCS_NAD27_UTM_zone_7N, 26707) +ValuePair(PCS_NAD27_UTM_zone_8N, 26708) +ValuePair(PCS_NAD27_UTM_zone_9N, 26709) +ValuePair(PCS_NAD27_UTM_zone_10N, 26710) +ValuePair(PCS_NAD27_UTM_zone_11N, 26711) +ValuePair(PCS_NAD27_UTM_zone_12N, 26712) +ValuePair(PCS_NAD27_UTM_zone_13N, 26713) +ValuePair(PCS_NAD27_UTM_zone_14N, 26714) +ValuePair(PCS_NAD27_UTM_zone_15N, 26715) +ValuePair(PCS_NAD27_UTM_zone_16N, 26716) +ValuePair(PCS_NAD27_UTM_zone_17N, 26717) +ValuePair(PCS_NAD27_UTM_zone_18N, 26718) +ValuePair(PCS_NAD27_UTM_zone_19N, 26719) +ValuePair(PCS_NAD27_UTM_zone_20N, 26720) +ValuePair(PCS_NAD27_UTM_zone_21N, 26721) +ValuePair(PCS_NAD27_UTM_zone_22N, 26722) +ValuePair(PCS_NAD27_Alabama_East, 26729) +ValuePair(PCS_NAD27_Alabama_West, 26730) +ValuePair(PCS_NAD27_Alaska_zone_1, 26731) +ValuePair(PCS_NAD27_Alaska_zone_2, 26732) +ValuePair(PCS_NAD27_Alaska_zone_3, 26733) +ValuePair(PCS_NAD27_Alaska_zone_4, 26734) +ValuePair(PCS_NAD27_Alaska_zone_5, 26735) +ValuePair(PCS_NAD27_Alaska_zone_6, 26736) +ValuePair(PCS_NAD27_Alaska_zone_7, 26737) +ValuePair(PCS_NAD27_Alaska_zone_8, 26738) +ValuePair(PCS_NAD27_Alaska_zone_9, 26739) +ValuePair(PCS_NAD27_Alaska_zone_10, 26740) +ValuePair(PCS_NAD27_California_I, 26741) +ValuePair(PCS_NAD27_California_II, 26742) +ValuePair(PCS_NAD27_California_III, 26743) +ValuePair(PCS_NAD27_California_IV, 26744) +ValuePair(PCS_NAD27_California_V, 26745) +ValuePair(PCS_NAD27_California_VI, 26746) +ValuePair(PCS_NAD27_California_VII, 26747) +ValuePair(PCS_NAD27_Arizona_East, 26748) +ValuePair(PCS_NAD27_Arizona_Central, 26749) +ValuePair(PCS_NAD27_Arizona_West, 26750) +ValuePair(PCS_NAD27_Arkansas_North, 26751) +ValuePair(PCS_NAD27_Arkansas_South, 26752) +ValuePair(PCS_NAD27_Colorado_North, 26753) +ValuePair(PCS_NAD27_Colorado_Central, 26754) +ValuePair(PCS_NAD27_Colorado_South, 26755) +ValuePair(PCS_NAD27_Connecticut, 26756) +ValuePair(PCS_NAD27_Delaware, 26757) +ValuePair(PCS_NAD27_Florida_East, 26758) +ValuePair(PCS_NAD27_Florida_West, 26759) +ValuePair(PCS_NAD27_Florida_North, 26760) +ValuePair(PCS_NAD27_Hawaii_zone_1, 26761) +ValuePair(PCS_NAD27_Hawaii_zone_2, 26762) +ValuePair(PCS_NAD27_Hawaii_zone_3, 26763) +ValuePair(PCS_NAD27_Hawaii_zone_4, 26764) +ValuePair(PCS_NAD27_Hawaii_zone_5, 26765) +ValuePair(PCS_NAD27_Georgia_East, 26766) +ValuePair(PCS_NAD27_Georgia_West, 26767) +ValuePair(PCS_NAD27_Idaho_East, 26768) +ValuePair(PCS_NAD27_Idaho_Central, 26769) +ValuePair(PCS_NAD27_Idaho_West, 26770) +ValuePair(PCS_NAD27_Illinois_East, 26771) +ValuePair(PCS_NAD27_Illinois_West, 26772) +ValuePair(PCS_NAD27_Indiana_East, 26773) +ValuePair(PCS_NAD27_BLM_14N_feet, 26774) +ValuePair(PCS_NAD27_Indiana_West, 26774) +ValuePair(PCS_NAD27_BLM_15N_feet, 26775) +ValuePair(PCS_NAD27_Iowa_North, 26775) +ValuePair(PCS_NAD27_BLM_16N_feet, 26776) +ValuePair(PCS_NAD27_Iowa_South, 26776) +ValuePair(PCS_NAD27_BLM_17N_feet, 26777) +ValuePair(PCS_NAD27_Kansas_North, 26777) +ValuePair(PCS_NAD27_Kansas_South, 26778) +ValuePair(PCS_NAD27_Kentucky_North, 26779) +ValuePair(PCS_NAD27_Kentucky_South, 26780) +ValuePair(PCS_NAD27_Louisiana_North, 26781) +ValuePair(PCS_NAD27_Louisiana_South, 26782) +ValuePair(PCS_NAD27_Maine_East, 26783) +ValuePair(PCS_NAD27_Maine_West, 26784) +ValuePair(PCS_NAD27_Maryland, 26785) +ValuePair(PCS_NAD27_Massachusetts, 26786) +ValuePair(PCS_NAD27_Massachusetts_Is, 26787) +ValuePair(PCS_NAD27_Michigan_North, 26788) +ValuePair(PCS_NAD27_Michigan_Central, 26789) +ValuePair(PCS_NAD27_Michigan_South, 26790) +ValuePair(PCS_NAD27_Minnesota_North, 26791) +ValuePair(PCS_NAD27_Minnesota_Cent, 26792) +ValuePair(PCS_NAD27_Minnesota_South, 26793) +ValuePair(PCS_NAD27_Mississippi_East, 26794) +ValuePair(PCS_NAD27_Mississippi_West, 26795) +ValuePair(PCS_NAD27_Missouri_East, 26796) +ValuePair(PCS_NAD27_Missouri_Central, 26797) +ValuePair(PCS_NAD27_Missouri_West, 26798) +ValuePair(PCS_NAD_Michigan_Michigan_East, 26801) +ValuePair(PCS_NAD_Michigan_Michigan_Old_Central, 26802) +ValuePair(PCS_NAD_Michigan_Michigan_West, 26803) +ValuePair(PCS_NAD83_UTM_zone_3N, 26903) +ValuePair(PCS_NAD83_UTM_zone_4N, 26904) +ValuePair(PCS_NAD83_UTM_zone_5N, 26905) +ValuePair(PCS_NAD83_UTM_zone_6N, 26906) +ValuePair(PCS_NAD83_UTM_zone_7N, 26907) +ValuePair(PCS_NAD83_UTM_zone_8N, 26908) +ValuePair(PCS_NAD83_UTM_zone_9N, 26909) +ValuePair(PCS_NAD83_UTM_zone_10N, 26910) +ValuePair(PCS_NAD83_UTM_zone_11N, 26911) +ValuePair(PCS_NAD83_UTM_zone_12N, 26912) +ValuePair(PCS_NAD83_UTM_zone_13N, 26913) +ValuePair(PCS_NAD83_UTM_zone_14N, 26914) +ValuePair(PCS_NAD83_UTM_zone_15N, 26915) +ValuePair(PCS_NAD83_UTM_zone_16N, 26916) +ValuePair(PCS_NAD83_UTM_zone_17N, 26917) +ValuePair(PCS_NAD83_UTM_zone_18N, 26918) +ValuePair(PCS_NAD83_UTM_zone_19N, 26919) +ValuePair(PCS_NAD83_UTM_zone_20N, 26920) +ValuePair(PCS_NAD83_UTM_zone_21N, 26921) +ValuePair(PCS_NAD83_UTM_zone_22N, 26922) +ValuePair(PCS_NAD83_UTM_zone_23N, 26923) +ValuePair(PCS_NAD83_Alabama_East, 26929) +ValuePair(PCS_NAD83_Alabama_West, 26930) +ValuePair(PCS_NAD83_Alaska_zone_1, 26931) +ValuePair(PCS_NAD83_Alaska_zone_2, 26932) +ValuePair(PCS_NAD83_Alaska_zone_3, 26933) +ValuePair(PCS_NAD83_Alaska_zone_4, 26934) +ValuePair(PCS_NAD83_Alaska_zone_5, 26935) +ValuePair(PCS_NAD83_Alaska_zone_6, 26936) +ValuePair(PCS_NAD83_Alaska_zone_7, 26937) +ValuePair(PCS_NAD83_Alaska_zone_8, 26938) +ValuePair(PCS_NAD83_Alaska_zone_9, 26939) +ValuePair(PCS_NAD83_Alaska_zone_10, 26940) +ValuePair(PCS_NAD83_California_1, 26941) +ValuePair(PCS_NAD83_California_2, 26942) +ValuePair(PCS_NAD83_California_3, 26943) +ValuePair(PCS_NAD83_California_4, 26944) +ValuePair(PCS_NAD83_California_5, 26945) +ValuePair(PCS_NAD83_California_6, 26946) +ValuePair(PCS_NAD83_Arizona_East, 26948) +ValuePair(PCS_NAD83_Arizona_Central, 26949) +ValuePair(PCS_NAD83_Arizona_West, 26950) +ValuePair(PCS_NAD83_Arkansas_North, 26951) +ValuePair(PCS_NAD83_Arkansas_South, 26952) +ValuePair(PCS_NAD83_Colorado_North, 26953) +ValuePair(PCS_NAD83_Colorado_Central, 26954) +ValuePair(PCS_NAD83_Colorado_South, 26955) +ValuePair(PCS_NAD83_Connecticut, 26956) +ValuePair(PCS_NAD83_Delaware, 26957) +ValuePair(PCS_NAD83_Florida_East, 26958) +ValuePair(PCS_NAD83_Florida_West, 26959) +ValuePair(PCS_NAD83_Florida_North, 26960) +ValuePair(PCS_NAD83_Hawaii_zone_1, 26961) +ValuePair(PCS_NAD83_Hawaii_zone_2, 26962) +ValuePair(PCS_NAD83_Hawaii_zone_3, 26963) +ValuePair(PCS_NAD83_Hawaii_zone_4, 26964) +ValuePair(PCS_NAD83_Hawaii_zone_5, 26965) +ValuePair(PCS_NAD83_Georgia_East, 26966) +ValuePair(PCS_NAD83_Georgia_West, 26967) +ValuePair(PCS_NAD83_Idaho_East, 26968) +ValuePair(PCS_NAD83_Idaho_Central, 26969) +ValuePair(PCS_NAD83_Idaho_West, 26970) +ValuePair(PCS_NAD83_Illinois_East, 26971) +ValuePair(PCS_NAD83_Illinois_West, 26972) +ValuePair(PCS_NAD83_Indiana_East, 26973) +ValuePair(PCS_NAD83_Indiana_West, 26974) +ValuePair(PCS_NAD83_Iowa_North, 26975) +ValuePair(PCS_NAD83_Iowa_South, 26976) +ValuePair(PCS_NAD83_Kansas_North, 26977) +ValuePair(PCS_NAD83_Kansas_South, 26978) +ValuePair(PCS_NAD83_Kentucky_North, 2205) +ValuePair(PCS_NAD83_Kentucky_South, 26980) +ValuePair(PCS_NAD83_Louisiana_North, 26981) +ValuePair(PCS_NAD83_Louisiana_South, 26982) +ValuePair(PCS_NAD83_Maine_East, 26983) +ValuePair(PCS_NAD83_Maine_West, 26984) +ValuePair(PCS_NAD83_Maryland, 26985) +ValuePair(PCS_NAD83_Massachusetts, 26986) +ValuePair(PCS_NAD83_Massachusetts_Is, 26987) +ValuePair(PCS_NAD83_Michigan_North, 26988) +ValuePair(PCS_NAD83_Michigan_Central, 26989) +ValuePair(PCS_NAD83_Michigan_South, 26990) +ValuePair(PCS_NAD83_Minnesota_North, 26991) +ValuePair(PCS_NAD83_Minnesota_Cent, 26992) +ValuePair(PCS_NAD83_Minnesota_South, 26993) +ValuePair(PCS_NAD83_Mississippi_East, 26994) +ValuePair(PCS_NAD83_Mississippi_West, 26995) +ValuePair(PCS_NAD83_Missouri_East, 26996) +ValuePair(PCS_NAD83_Missouri_Central, 26997) +ValuePair(PCS_NAD83_Missouri_West, 26998) +ValuePair(PCS_Nahrwan_1967_UTM_38N, 27038) +ValuePair(PCS_Nahrwan_1967_UTM_39N, 27039) +ValuePair(PCS_Nahrwan_1967_UTM_40N, 27040) +ValuePair(PCS_Naparima_UTM_20N, 27120) +ValuePair(PCS_GD49_NZ_Map_Grid, 27200) +ValuePair(PCS_GD49_North_Island_Grid, 27291) +ValuePair(PCS_GD49_South_Island_Grid, 27292) +ValuePair(PCS_Datum_73_UTM_zone_29N, 27429) +ValuePair(PCS_ATF_Nord_de_Guerre, 27500) +ValuePair(PCS_NTF_France_I, 27581) +ValuePair(PCS_NTF_France_II, 27582) +ValuePair(PCS_NTF_France_III, 27583) +ValuePair(PCS_NTF_Nord_France, 27591) +ValuePair(PCS_NTF_Centre_France, 27592) +ValuePair(PCS_NTF_Sud_France, 27593) +ValuePair(PCS_British_National_Grid, 27700) +ValuePair(PCS_Point_Noire_UTM_32S, 28232) +ValuePair(PCS_GDA94_MGA_zone_48, 28348) +ValuePair(PCS_GDA94_MGA_zone_49, 28349) +ValuePair(PCS_GDA94_MGA_zone_50, 28350) +ValuePair(PCS_GDA94_MGA_zone_51, 28351) +ValuePair(PCS_GDA94_MGA_zone_52, 28352) +ValuePair(PCS_GDA94_MGA_zone_53, 28353) +ValuePair(PCS_GDA94_MGA_zone_54, 28354) +ValuePair(PCS_GDA94_MGA_zone_55, 28355) +ValuePair(PCS_GDA94_MGA_zone_56, 28356) +ValuePair(PCS_GDA94_MGA_zone_57, 28357) +ValuePair(PCS_GDA94_MGA_zone_58, 28358) +ValuePair(PCS_Pulkovo_Gauss_zone_4, 28404) +ValuePair(PCS_Pulkovo_Gauss_zone_5, 28405) +ValuePair(PCS_Pulkovo_Gauss_zone_6, 28406) +ValuePair(PCS_Pulkovo_Gauss_zone_7, 28407) +ValuePair(PCS_Pulkovo_Gauss_zone_8, 28408) +ValuePair(PCS_Pulkovo_Gauss_zone_9, 28409) +ValuePair(PCS_Pulkovo_Gauss_zone_10, 28410) +ValuePair(PCS_Pulkovo_Gauss_zone_11, 28411) +ValuePair(PCS_Pulkovo_Gauss_zone_12, 28412) +ValuePair(PCS_Pulkovo_Gauss_zone_13, 28413) +ValuePair(PCS_Pulkovo_Gauss_zone_14, 28414) +ValuePair(PCS_Pulkovo_Gauss_zone_15, 28415) +ValuePair(PCS_Pulkovo_Gauss_zone_16, 28416) +ValuePair(PCS_Pulkovo_Gauss_zone_17, 28417) +ValuePair(PCS_Pulkovo_Gauss_zone_18, 28418) +ValuePair(PCS_Pulkovo_Gauss_zone_19, 28419) +ValuePair(PCS_Pulkovo_Gauss_zone_20, 28420) +ValuePair(PCS_Pulkovo_Gauss_zone_21, 28421) +ValuePair(PCS_Pulkovo_Gauss_zone_22, 28422) +ValuePair(PCS_Pulkovo_Gauss_zone_23, 28423) +ValuePair(PCS_Pulkovo_Gauss_zone_24, 28424) +ValuePair(PCS_Pulkovo_Gauss_zone_25, 28425) +ValuePair(PCS_Pulkovo_Gauss_zone_26, 28426) +ValuePair(PCS_Pulkovo_Gauss_zone_27, 28427) +ValuePair(PCS_Pulkovo_Gauss_zone_28, 28428) +ValuePair(PCS_Pulkovo_Gauss_zone_29, 28429) +ValuePair(PCS_Pulkovo_Gauss_zone_30, 28430) +ValuePair(PCS_Pulkovo_Gauss_zone_31, 28431) +ValuePair(PCS_Pulkovo_Gauss_zone_32, 28432) +ValuePair(PCS_Pulkovo_Gauss_4N, 28464) +ValuePair(PCS_Pulkovo_Gauss_5N, 28465) +ValuePair(PCS_Pulkovo_Gauss_6N, 28466) +ValuePair(PCS_Pulkovo_Gauss_7N, 28467) +ValuePair(PCS_Pulkovo_Gauss_8N, 28468) +ValuePair(PCS_Pulkovo_Gauss_9N, 28469) +ValuePair(PCS_Pulkovo_Gauss_10N, 28470) +ValuePair(PCS_Pulkovo_Gauss_11N, 28471) +ValuePair(PCS_Pulkovo_Gauss_12N, 28472) +ValuePair(PCS_Pulkovo_Gauss_13N, 28473) +ValuePair(PCS_Pulkovo_Gauss_14N, 28474) +ValuePair(PCS_Pulkovo_Gauss_15N, 28475) +ValuePair(PCS_Pulkovo_Gauss_16N, 28476) +ValuePair(PCS_Pulkovo_Gauss_17N, 28477) +ValuePair(PCS_Pulkovo_Gauss_18N, 28478) +ValuePair(PCS_Pulkovo_Gauss_19N, 28479) +ValuePair(PCS_Pulkovo_Gauss_20N, 28480) +ValuePair(PCS_Pulkovo_Gauss_21N, 28481) +ValuePair(PCS_Pulkovo_Gauss_22N, 28482) +ValuePair(PCS_Pulkovo_Gauss_23N, 28483) +ValuePair(PCS_Pulkovo_Gauss_24N, 28484) +ValuePair(PCS_Pulkovo_Gauss_25N, 28485) +ValuePair(PCS_Pulkovo_Gauss_26N, 28486) +ValuePair(PCS_Pulkovo_Gauss_27N, 28487) +ValuePair(PCS_Pulkovo_Gauss_28N, 28488) +ValuePair(PCS_Pulkovo_Gauss_29N, 28489) +ValuePair(PCS_Pulkovo_Gauss_30N, 28490) +ValuePair(PCS_Pulkovo_Gauss_31N, 28491) +ValuePair(PCS_Pulkovo_Gauss_32N, 28492) +ValuePair(PCS_Qatar_National_Grid, 28600) +ValuePair(PCS_RD_Netherlands_Old, 28991) +ValuePair(PCS_RD_Netherlands_New, 28992) +ValuePair(PCS_SAD69_UTM_zone_18N, 29118) +ValuePair(PCS_SAD69_UTM_zone_19N, 29119) +ValuePair(PCS_SAD69_UTM_zone_20N, 29120) +ValuePair(PCS_SAD69_UTM_zone_21N, 29121) +ValuePair(PCS_SAD69_UTM_zone_22N, 29122) +ValuePair(PCS_SAD69_UTM_zone_17S, 29177) +ValuePair(PCS_SAD69_UTM_zone_18S, 29178) +ValuePair(PCS_SAD69_UTM_zone_19S, 29179) +ValuePair(PCS_SAD69_UTM_zone_20S, 29180) +ValuePair(PCS_SAD69_UTM_zone_21S, 29181) +ValuePair(PCS_SAD69_UTM_zone_22S, 29182) +ValuePair(PCS_SAD69_UTM_zone_23S, 29183) +ValuePair(PCS_SAD69_UTM_zone_24S, 29184) +ValuePair(PCS_SAD69_UTM_zone_25S, 29185) +ValuePair(PCS_Sapper_Hill_UTM_20S, 29220) +ValuePair(PCS_Sapper_Hill_UTM_21S, 29221) +ValuePair(PCS_Schwarzeck_UTM_33S, 29333) +ValuePair(PCS_Sudan_UTM_zone_35N, 29635) +ValuePair(PCS_Sudan_UTM_zone_36N, 29636) +ValuePair(PCS_Tananarive_Laborde, 29700) +ValuePair(PCS_Tananarive_UTM_38S, 29738) +ValuePair(PCS_Tananarive_UTM_39S, 29739) +ValuePair(PCS_Timbalai_1948_Borneo, 29800) +ValuePair(PCS_Timbalai_1948_UTM_49N, 29849) +ValuePair(PCS_Timbalai_1948_UTM_50N, 29850) +ValuePair(PCS_TM65_Irish_Nat_Grid, 29900) +ValuePair(PCS_Trinidad_1903_Trinidad, 30200) +ValuePair(PCS_TC_1948_UTM_zone_39N, 30339) +ValuePair(PCS_TC_1948_UTM_zone_40N, 30340) +ValuePair(PCS_Voirol_N_Algerie_ancien, 30491) +ValuePair(PCS_Voirol_S_Algerie_ancien, 30492) +ValuePair(PCS_Voirol_Unifie_N_Algerie, 30591) +ValuePair(PCS_Voirol_Unifie_S_Algerie, 30592) +ValuePair(PCS_Bern_1938_Swiss_New, 30600) +ValuePair(PCS_Nord_Sahara_UTM_29N, 30729) +ValuePair(PCS_Nord_Sahara_UTM_30N, 30730) +ValuePair(PCS_Nord_Sahara_UTM_31N, 30731) +ValuePair(PCS_Nord_Sahara_UTM_32N, 30732) +ValuePair(PCS_Yoff_UTM_zone_28N, 31028) +ValuePair(PCS_Zanderij_UTM_zone_21N, 31121) +ValuePair(PCS_MGI_Austria_West, 31291) +ValuePair(PCS_MGI_Austria_Central, 31292) +ValuePair(PCS_MGI_Austria_East, 31293) +ValuePair(PCS_Belge_Lambert_72, 31300) +ValuePair(PCS_DHDN_Germany_zone_1, 31491) +ValuePair(PCS_DHDN_Germany_zone_2, 31492) +ValuePair(PCS_DHDN_Germany_zone_3, 31493) +ValuePair(PCS_DHDN_Germany_zone_4, 31494) +ValuePair(PCS_DHDN_Germany_zone_5, 31495) +ValuePair(PCS_NAD27_Montana_North, 32001) +ValuePair(PCS_NAD27_Montana_Central, 32002) +ValuePair(PCS_NAD27_Montana_South, 32003) +ValuePair(PCS_NAD27_Nebraska_North, 32005) +ValuePair(PCS_NAD27_Nebraska_South, 32006) +ValuePair(PCS_NAD27_Nevada_East, 32007) +ValuePair(PCS_NAD27_Nevada_Central, 32008) +ValuePair(PCS_NAD27_Nevada_West, 32009) +ValuePair(PCS_NAD27_New_Hampshire, 32010) +ValuePair(PCS_NAD27_New_Jersey, 32011) +ValuePair(PCS_NAD27_New_Mexico_East, 32012) +ValuePair(PCS_NAD27_New_Mexico_Cent, 32013) +ValuePair(PCS_NAD27_New_Mexico_West, 32014) +ValuePair(PCS_NAD27_New_York_East, 32015) +ValuePair(PCS_NAD27_New_York_Central, 32016) +ValuePair(PCS_NAD27_New_York_West, 32017) +ValuePair(PCS_NAD27_New_York_Long_Is, 32018) +ValuePair(PCS_NAD27_North_Carolina, 32019) +ValuePair(PCS_NAD27_North_Dakota_N, 32020) +ValuePair(PCS_NAD27_North_Dakota_S, 32021) +ValuePair(PCS_NAD27_Ohio_North, 32022) +ValuePair(PCS_NAD27_Ohio_South, 32023) +ValuePair(PCS_NAD27_Oklahoma_North, 32024) +ValuePair(PCS_NAD27_Oklahoma_South, 32025) +ValuePair(PCS_NAD27_Oregon_North, 32026) +ValuePair(PCS_NAD27_Oregon_South, 32027) +ValuePair(PCS_NAD27_Pennsylvania_N, 32028) +ValuePair(PCS_NAD27_Pennsylvania_S, 32029) +ValuePair(PCS_NAD27_Rhode_Island, 32030) +ValuePair(PCS_NAD27_South_Carolina_N, 32031) +ValuePair(PCS_NAD27_South_Carolina_S, 32033) +ValuePair(PCS_NAD27_South_Dakota_N, 32034) +ValuePair(PCS_NAD27_South_Dakota_S, 32035) +ValuePair(PCS_NAD27_Tennessee, 2204) +ValuePair(PCS_NAD27_Texas_North, 32037) +ValuePair(PCS_NAD27_Texas_North_Cen, 32038) +ValuePair(PCS_NAD27_Texas_Central, 32039) +ValuePair(PCS_NAD27_Texas_South_Cen, 32040) +ValuePair(PCS_NAD27_Texas_South, 32041) +ValuePair(PCS_NAD27_Utah_North, 32042) +ValuePair(PCS_NAD27_Utah_Central, 32043) +ValuePair(PCS_NAD27_Utah_South, 32044) +ValuePair(PCS_NAD27_Vermont, 32045) +ValuePair(PCS_NAD27_Virginia_North, 32046) +ValuePair(PCS_NAD27_Virginia_South, 32047) +ValuePair(PCS_NAD27_Washington_North, 32048) +ValuePair(PCS_NAD27_Washington_South, 32049) +ValuePair(PCS_NAD27_West_Virginia_N, 32050) +ValuePair(PCS_NAD27_West_Virginia_S, 32051) +ValuePair(PCS_NAD27_Wisconsin_North, 32052) +ValuePair(PCS_NAD27_Wisconsin_Cen, 32053) +ValuePair(PCS_NAD27_Wisconsin_South, 32054) +ValuePair(PCS_NAD27_Wyoming_East, 32055) +ValuePair(PCS_NAD27_Wyoming_E_Cen, 32056) +ValuePair(PCS_NAD27_Wyoming_W_Cen, 32057) +ValuePair(PCS_NAD27_Wyoming_West, 32058) +ValuePair(PCS_NAD27_Puerto_Rico, 32059) +ValuePair(PCS_NAD27_St_Croix, 32060) +ValuePair(PCS_NAD83_Montana, 32100) +ValuePair(PCS_NAD83_Nebraska, 32104) +ValuePair(PCS_NAD83_Nevada_East, 32107) +ValuePair(PCS_NAD83_Nevada_Central, 32108) +ValuePair(PCS_NAD83_Nevada_West, 32109) +ValuePair(PCS_NAD83_New_Hampshire, 32110) +ValuePair(PCS_NAD83_New_Jersey, 32111) +ValuePair(PCS_NAD83_New_Mexico_East, 32112) +ValuePair(PCS_NAD83_New_Mexico_Cent, 32113) +ValuePair(PCS_NAD83_New_Mexico_West, 32114) +ValuePair(PCS_NAD83_New_York_East, 32115) +ValuePair(PCS_NAD83_New_York_Central, 32116) +ValuePair(PCS_NAD83_New_York_West, 32117) +ValuePair(PCS_NAD83_New_York_Long_Is, 32118) +ValuePair(PCS_NAD83_North_Carolina, 32119) +ValuePair(PCS_NAD83_North_Dakota_N, 32120) +ValuePair(PCS_NAD83_North_Dakota_S, 32121) +ValuePair(PCS_NAD83_Ohio_North, 32122) +ValuePair(PCS_NAD83_Ohio_South, 32123) +ValuePair(PCS_NAD83_Oklahoma_North, 32124) +ValuePair(PCS_NAD83_Oklahoma_South, 32125) +ValuePair(PCS_NAD83_Oregon_North, 32126) +ValuePair(PCS_NAD83_Oregon_South, 32127) +ValuePair(PCS_NAD83_Pennsylvania_N, 32128) +ValuePair(PCS_NAD83_Pennsylvania_S, 32129) +ValuePair(PCS_NAD83_Rhode_Island, 32130) +ValuePair(PCS_NAD83_South_Carolina, 32133) +ValuePair(PCS_NAD83_South_Dakota_N, 32134) +ValuePair(PCS_NAD83_South_Dakota_S, 32135) +ValuePair(PCS_NAD83_Tennessee, 32136) +ValuePair(PCS_NAD83_Texas_North, 32137) +ValuePair(PCS_NAD83_Texas_North_Cen, 32138) +ValuePair(PCS_NAD83_Texas_Central, 32139) +ValuePair(PCS_NAD83_Texas_South_Cen, 32140) +ValuePair(PCS_NAD83_Texas_South, 32141) +ValuePair(PCS_NAD83_Utah_North, 32142) +ValuePair(PCS_NAD83_Utah_Central, 32143) +ValuePair(PCS_NAD83_Utah_South, 32144) +ValuePair(PCS_NAD83_Vermont, 32145) +ValuePair(PCS_NAD83_Virginia_North, 32146) +ValuePair(PCS_NAD83_Virginia_South, 32147) +ValuePair(PCS_NAD83_Washington_North, 32148) +ValuePair(PCS_NAD83_Washington_South, 32149) +ValuePair(PCS_NAD83_West_Virginia_N, 32150) +ValuePair(PCS_NAD83_West_Virginia_S, 32151) +ValuePair(PCS_NAD83_Wisconsin_North, 32152) +ValuePair(PCS_NAD83_Wisconsin_Cen, 32153) +ValuePair(PCS_NAD83_Wisconsin_South, 32154) +ValuePair(PCS_NAD83_Wyoming_East, 32155) +ValuePair(PCS_NAD83_Wyoming_E_Cen, 32156) +ValuePair(PCS_NAD83_Wyoming_W_Cen, 32157) +ValuePair(PCS_NAD83_Wyoming_West, 32158) +ValuePair(PCS_NAD83_Puerto_Rico_Virgin_Is, 32161) +ValuePair(PCS_WGS72_UTM_zone_1N, 32201) +ValuePair(PCS_WGS72_UTM_zone_2N, 32202) +ValuePair(PCS_WGS72_UTM_zone_3N, 32203) +ValuePair(PCS_WGS72_UTM_zone_4N, 32204) +ValuePair(PCS_WGS72_UTM_zone_5N, 32205) +ValuePair(PCS_WGS72_UTM_zone_6N, 32206) +ValuePair(PCS_WGS72_UTM_zone_7N, 32207) +ValuePair(PCS_WGS72_UTM_zone_8N, 32208) +ValuePair(PCS_WGS72_UTM_zone_9N, 32209) +ValuePair(PCS_WGS72_UTM_zone_10N, 32210) +ValuePair(PCS_WGS72_UTM_zone_11N, 32211) +ValuePair(PCS_WGS72_UTM_zone_12N, 32212) +ValuePair(PCS_WGS72_UTM_zone_13N, 32213) +ValuePair(PCS_WGS72_UTM_zone_14N, 32214) +ValuePair(PCS_WGS72_UTM_zone_15N, 32215) +ValuePair(PCS_WGS72_UTM_zone_16N, 32216) +ValuePair(PCS_WGS72_UTM_zone_17N, 32217) +ValuePair(PCS_WGS72_UTM_zone_18N, 32218) +ValuePair(PCS_WGS72_UTM_zone_19N, 32219) +ValuePair(PCS_WGS72_UTM_zone_20N, 32220) +ValuePair(PCS_WGS72_UTM_zone_21N, 32221) +ValuePair(PCS_WGS72_UTM_zone_22N, 32222) +ValuePair(PCS_WGS72_UTM_zone_23N, 32223) +ValuePair(PCS_WGS72_UTM_zone_24N, 32224) +ValuePair(PCS_WGS72_UTM_zone_25N, 32225) +ValuePair(PCS_WGS72_UTM_zone_26N, 32226) +ValuePair(PCS_WGS72_UTM_zone_27N, 32227) +ValuePair(PCS_WGS72_UTM_zone_28N, 32228) +ValuePair(PCS_WGS72_UTM_zone_29N, 32229) +ValuePair(PCS_WGS72_UTM_zone_30N, 32230) +ValuePair(PCS_WGS72_UTM_zone_31N, 32231) +ValuePair(PCS_WGS72_UTM_zone_32N, 32232) +ValuePair(PCS_WGS72_UTM_zone_33N, 32233) +ValuePair(PCS_WGS72_UTM_zone_34N, 32234) +ValuePair(PCS_WGS72_UTM_zone_35N, 32235) +ValuePair(PCS_WGS72_UTM_zone_36N, 32236) +ValuePair(PCS_WGS72_UTM_zone_37N, 32237) +ValuePair(PCS_WGS72_UTM_zone_38N, 32238) +ValuePair(PCS_WGS72_UTM_zone_39N, 32239) +ValuePair(PCS_WGS72_UTM_zone_40N, 32240) +ValuePair(PCS_WGS72_UTM_zone_41N, 32241) +ValuePair(PCS_WGS72_UTM_zone_42N, 32242) +ValuePair(PCS_WGS72_UTM_zone_43N, 32243) +ValuePair(PCS_WGS72_UTM_zone_44N, 32244) +ValuePair(PCS_WGS72_UTM_zone_45N, 32245) +ValuePair(PCS_WGS72_UTM_zone_46N, 32246) +ValuePair(PCS_WGS72_UTM_zone_47N, 32247) +ValuePair(PCS_WGS72_UTM_zone_48N, 32248) +ValuePair(PCS_WGS72_UTM_zone_49N, 32249) +ValuePair(PCS_WGS72_UTM_zone_50N, 32250) +ValuePair(PCS_WGS72_UTM_zone_51N, 32251) +ValuePair(PCS_WGS72_UTM_zone_52N, 32252) +ValuePair(PCS_WGS72_UTM_zone_53N, 32253) +ValuePair(PCS_WGS72_UTM_zone_54N, 32254) +ValuePair(PCS_WGS72_UTM_zone_55N, 32255) +ValuePair(PCS_WGS72_UTM_zone_56N, 32256) +ValuePair(PCS_WGS72_UTM_zone_57N, 32257) +ValuePair(PCS_WGS72_UTM_zone_58N, 32258) +ValuePair(PCS_WGS72_UTM_zone_59N, 32259) +ValuePair(PCS_WGS72_UTM_zone_60N, 32260) +ValuePair(PCS_WGS72_UTM_zone_1S, 32301) +ValuePair(PCS_WGS72_UTM_zone_2S, 32302) +ValuePair(PCS_WGS72_UTM_zone_3S, 32303) +ValuePair(PCS_WGS72_UTM_zone_4S, 32304) +ValuePair(PCS_WGS72_UTM_zone_5S, 32305) +ValuePair(PCS_WGS72_UTM_zone_6S, 32306) +ValuePair(PCS_WGS72_UTM_zone_7S, 32307) +ValuePair(PCS_WGS72_UTM_zone_8S, 32308) +ValuePair(PCS_WGS72_UTM_zone_9S, 32309) +ValuePair(PCS_WGS72_UTM_zone_10S, 32310) +ValuePair(PCS_WGS72_UTM_zone_11S, 32311) +ValuePair(PCS_WGS72_UTM_zone_12S, 32312) +ValuePair(PCS_WGS72_UTM_zone_13S, 32313) +ValuePair(PCS_WGS72_UTM_zone_14S, 32314) +ValuePair(PCS_WGS72_UTM_zone_15S, 32315) +ValuePair(PCS_WGS72_UTM_zone_16S, 32316) +ValuePair(PCS_WGS72_UTM_zone_17S, 32317) +ValuePair(PCS_WGS72_UTM_zone_18S, 32318) +ValuePair(PCS_WGS72_UTM_zone_19S, 32319) +ValuePair(PCS_WGS72_UTM_zone_20S, 32320) +ValuePair(PCS_WGS72_UTM_zone_21S, 32321) +ValuePair(PCS_WGS72_UTM_zone_22S, 32322) +ValuePair(PCS_WGS72_UTM_zone_23S, 32323) +ValuePair(PCS_WGS72_UTM_zone_24S, 32324) +ValuePair(PCS_WGS72_UTM_zone_25S, 32325) +ValuePair(PCS_WGS72_UTM_zone_26S, 32326) +ValuePair(PCS_WGS72_UTM_zone_27S, 32327) +ValuePair(PCS_WGS72_UTM_zone_28S, 32328) +ValuePair(PCS_WGS72_UTM_zone_29S, 32329) +ValuePair(PCS_WGS72_UTM_zone_30S, 32330) +ValuePair(PCS_WGS72_UTM_zone_31S, 32331) +ValuePair(PCS_WGS72_UTM_zone_32S, 32332) +ValuePair(PCS_WGS72_UTM_zone_33S, 32333) +ValuePair(PCS_WGS72_UTM_zone_34S, 32334) +ValuePair(PCS_WGS72_UTM_zone_35S, 32335) +ValuePair(PCS_WGS72_UTM_zone_36S, 32336) +ValuePair(PCS_WGS72_UTM_zone_37S, 32337) +ValuePair(PCS_WGS72_UTM_zone_38S, 32338) +ValuePair(PCS_WGS72_UTM_zone_39S, 32339) +ValuePair(PCS_WGS72_UTM_zone_40S, 32340) +ValuePair(PCS_WGS72_UTM_zone_41S, 32341) +ValuePair(PCS_WGS72_UTM_zone_42S, 32342) +ValuePair(PCS_WGS72_UTM_zone_43S, 32343) +ValuePair(PCS_WGS72_UTM_zone_44S, 32344) +ValuePair(PCS_WGS72_UTM_zone_45S, 32345) +ValuePair(PCS_WGS72_UTM_zone_46S, 32346) +ValuePair(PCS_WGS72_UTM_zone_47S, 32347) +ValuePair(PCS_WGS72_UTM_zone_48S, 32348) +ValuePair(PCS_WGS72_UTM_zone_49S, 32349) +ValuePair(PCS_WGS72_UTM_zone_50S, 32350) +ValuePair(PCS_WGS72_UTM_zone_51S, 32351) +ValuePair(PCS_WGS72_UTM_zone_52S, 32352) +ValuePair(PCS_WGS72_UTM_zone_53S, 32353) +ValuePair(PCS_WGS72_UTM_zone_54S, 32354) +ValuePair(PCS_WGS72_UTM_zone_55S, 32355) +ValuePair(PCS_WGS72_UTM_zone_56S, 32356) +ValuePair(PCS_WGS72_UTM_zone_57S, 32357) +ValuePair(PCS_WGS72_UTM_zone_58S, 32358) +ValuePair(PCS_WGS72_UTM_zone_59S, 32359) +ValuePair(PCS_WGS72_UTM_zone_60S, 32360) +ValuePair(PCS_WGS72BE_UTM_zone_1N, 32401) +ValuePair(PCS_WGS72BE_UTM_zone_2N, 32402) +ValuePair(PCS_WGS72BE_UTM_zone_3N, 32403) +ValuePair(PCS_WGS72BE_UTM_zone_4N, 32404) +ValuePair(PCS_WGS72BE_UTM_zone_5N, 32405) +ValuePair(PCS_WGS72BE_UTM_zone_6N, 32406) +ValuePair(PCS_WGS72BE_UTM_zone_7N, 32407) +ValuePair(PCS_WGS72BE_UTM_zone_8N, 32408) +ValuePair(PCS_WGS72BE_UTM_zone_9N, 32409) +ValuePair(PCS_WGS72BE_UTM_zone_10N, 32410) +ValuePair(PCS_WGS72BE_UTM_zone_11N, 32411) +ValuePair(PCS_WGS72BE_UTM_zone_12N, 32412) +ValuePair(PCS_WGS72BE_UTM_zone_13N, 32413) +ValuePair(PCS_WGS72BE_UTM_zone_14N, 32414) +ValuePair(PCS_WGS72BE_UTM_zone_15N, 32415) +ValuePair(PCS_WGS72BE_UTM_zone_16N, 32416) +ValuePair(PCS_WGS72BE_UTM_zone_17N, 32417) +ValuePair(PCS_WGS72BE_UTM_zone_18N, 32418) +ValuePair(PCS_WGS72BE_UTM_zone_19N, 32419) +ValuePair(PCS_WGS72BE_UTM_zone_20N, 32420) +ValuePair(PCS_WGS72BE_UTM_zone_21N, 32421) +ValuePair(PCS_WGS72BE_UTM_zone_22N, 32422) +ValuePair(PCS_WGS72BE_UTM_zone_23N, 32423) +ValuePair(PCS_WGS72BE_UTM_zone_24N, 32424) +ValuePair(PCS_WGS72BE_UTM_zone_25N, 32425) +ValuePair(PCS_WGS72BE_UTM_zone_26N, 32426) +ValuePair(PCS_WGS72BE_UTM_zone_27N, 32427) +ValuePair(PCS_WGS72BE_UTM_zone_28N, 32428) +ValuePair(PCS_WGS72BE_UTM_zone_29N, 32429) +ValuePair(PCS_WGS72BE_UTM_zone_30N, 32430) +ValuePair(PCS_WGS72BE_UTM_zone_31N, 32431) +ValuePair(PCS_WGS72BE_UTM_zone_32N, 32432) +ValuePair(PCS_WGS72BE_UTM_zone_33N, 32433) +ValuePair(PCS_WGS72BE_UTM_zone_34N, 32434) +ValuePair(PCS_WGS72BE_UTM_zone_35N, 32435) +ValuePair(PCS_WGS72BE_UTM_zone_36N, 32436) +ValuePair(PCS_WGS72BE_UTM_zone_37N, 32437) +ValuePair(PCS_WGS72BE_UTM_zone_38N, 32438) +ValuePair(PCS_WGS72BE_UTM_zone_39N, 32439) +ValuePair(PCS_WGS72BE_UTM_zone_40N, 32440) +ValuePair(PCS_WGS72BE_UTM_zone_41N, 32441) +ValuePair(PCS_WGS72BE_UTM_zone_42N, 32442) +ValuePair(PCS_WGS72BE_UTM_zone_43N, 32443) +ValuePair(PCS_WGS72BE_UTM_zone_44N, 32444) +ValuePair(PCS_WGS72BE_UTM_zone_45N, 32445) +ValuePair(PCS_WGS72BE_UTM_zone_46N, 32446) +ValuePair(PCS_WGS72BE_UTM_zone_47N, 32447) +ValuePair(PCS_WGS72BE_UTM_zone_48N, 32448) +ValuePair(PCS_WGS72BE_UTM_zone_49N, 32449) +ValuePair(PCS_WGS72BE_UTM_zone_50N, 32450) +ValuePair(PCS_WGS72BE_UTM_zone_51N, 32451) +ValuePair(PCS_WGS72BE_UTM_zone_52N, 32452) +ValuePair(PCS_WGS72BE_UTM_zone_53N, 32453) +ValuePair(PCS_WGS72BE_UTM_zone_54N, 32454) +ValuePair(PCS_WGS72BE_UTM_zone_55N, 32455) +ValuePair(PCS_WGS72BE_UTM_zone_56N, 32456) +ValuePair(PCS_WGS72BE_UTM_zone_57N, 32457) +ValuePair(PCS_WGS72BE_UTM_zone_58N, 32458) +ValuePair(PCS_WGS72BE_UTM_zone_59N, 32459) +ValuePair(PCS_WGS72BE_UTM_zone_60N, 32460) +ValuePair(PCS_WGS72BE_UTM_zone_1S, 32501) +ValuePair(PCS_WGS72BE_UTM_zone_2S, 32502) +ValuePair(PCS_WGS72BE_UTM_zone_3S, 32503) +ValuePair(PCS_WGS72BE_UTM_zone_4S, 32504) +ValuePair(PCS_WGS72BE_UTM_zone_5S, 32505) +ValuePair(PCS_WGS72BE_UTM_zone_6S, 32506) +ValuePair(PCS_WGS72BE_UTM_zone_7S, 32507) +ValuePair(PCS_WGS72BE_UTM_zone_8S, 32508) +ValuePair(PCS_WGS72BE_UTM_zone_9S, 32509) +ValuePair(PCS_WGS72BE_UTM_zone_10S, 32510) +ValuePair(PCS_WGS72BE_UTM_zone_11S, 32511) +ValuePair(PCS_WGS72BE_UTM_zone_12S, 32512) +ValuePair(PCS_WGS72BE_UTM_zone_13S, 32513) +ValuePair(PCS_WGS72BE_UTM_zone_14S, 32514) +ValuePair(PCS_WGS72BE_UTM_zone_15S, 32515) +ValuePair(PCS_WGS72BE_UTM_zone_16S, 32516) +ValuePair(PCS_WGS72BE_UTM_zone_17S, 32517) +ValuePair(PCS_WGS72BE_UTM_zone_18S, 32518) +ValuePair(PCS_WGS72BE_UTM_zone_19S, 32519) +ValuePair(PCS_WGS72BE_UTM_zone_20S, 32520) +ValuePair(PCS_WGS72BE_UTM_zone_21S, 32521) +ValuePair(PCS_WGS72BE_UTM_zone_22S, 32522) +ValuePair(PCS_WGS72BE_UTM_zone_23S, 32523) +ValuePair(PCS_WGS72BE_UTM_zone_24S, 32524) +ValuePair(PCS_WGS72BE_UTM_zone_25S, 32525) +ValuePair(PCS_WGS72BE_UTM_zone_26S, 32526) +ValuePair(PCS_WGS72BE_UTM_zone_27S, 32527) +ValuePair(PCS_WGS72BE_UTM_zone_28S, 32528) +ValuePair(PCS_WGS72BE_UTM_zone_29S, 32529) +ValuePair(PCS_WGS72BE_UTM_zone_30S, 32530) +ValuePair(PCS_WGS72BE_UTM_zone_31S, 32531) +ValuePair(PCS_WGS72BE_UTM_zone_32S, 32532) +ValuePair(PCS_WGS72BE_UTM_zone_33S, 32533) +ValuePair(PCS_WGS72BE_UTM_zone_34S, 32534) +ValuePair(PCS_WGS72BE_UTM_zone_35S, 32535) +ValuePair(PCS_WGS72BE_UTM_zone_36S, 32536) +ValuePair(PCS_WGS72BE_UTM_zone_37S, 32537) +ValuePair(PCS_WGS72BE_UTM_zone_38S, 32538) +ValuePair(PCS_WGS72BE_UTM_zone_39S, 32539) +ValuePair(PCS_WGS72BE_UTM_zone_40S, 32540) +ValuePair(PCS_WGS72BE_UTM_zone_41S, 32541) +ValuePair(PCS_WGS72BE_UTM_zone_42S, 32542) +ValuePair(PCS_WGS72BE_UTM_zone_43S, 32543) +ValuePair(PCS_WGS72BE_UTM_zone_44S, 32544) +ValuePair(PCS_WGS72BE_UTM_zone_45S, 32545) +ValuePair(PCS_WGS72BE_UTM_zone_46S, 32546) +ValuePair(PCS_WGS72BE_UTM_zone_47S, 32547) +ValuePair(PCS_WGS72BE_UTM_zone_48S, 32548) +ValuePair(PCS_WGS72BE_UTM_zone_49S, 32549) +ValuePair(PCS_WGS72BE_UTM_zone_50S, 32550) +ValuePair(PCS_WGS72BE_UTM_zone_51S, 32551) +ValuePair(PCS_WGS72BE_UTM_zone_52S, 32552) +ValuePair(PCS_WGS72BE_UTM_zone_53S, 32553) +ValuePair(PCS_WGS72BE_UTM_zone_54S, 32554) +ValuePair(PCS_WGS72BE_UTM_zone_55S, 32555) +ValuePair(PCS_WGS72BE_UTM_zone_56S, 32556) +ValuePair(PCS_WGS72BE_UTM_zone_57S, 32557) +ValuePair(PCS_WGS72BE_UTM_zone_58S, 32558) +ValuePair(PCS_WGS72BE_UTM_zone_59S, 32559) +ValuePair(PCS_WGS72BE_UTM_zone_60S, 32560) +ValuePair(PCS_WGS84_UTM_zone_1N, 32601) +ValuePair(PCS_WGS84_UTM_zone_2N, 32602) +ValuePair(PCS_WGS84_UTM_zone_3N, 32603) +ValuePair(PCS_WGS84_UTM_zone_4N, 32604) +ValuePair(PCS_WGS84_UTM_zone_5N, 32605) +ValuePair(PCS_WGS84_UTM_zone_6N, 32606) +ValuePair(PCS_WGS84_UTM_zone_7N, 32607) +ValuePair(PCS_WGS84_UTM_zone_8N, 32608) +ValuePair(PCS_WGS84_UTM_zone_9N, 32609) +ValuePair(PCS_WGS84_UTM_zone_10N, 32610) +ValuePair(PCS_WGS84_UTM_zone_11N, 32611) +ValuePair(PCS_WGS84_UTM_zone_12N, 32612) +ValuePair(PCS_WGS84_UTM_zone_13N, 32613) +ValuePair(PCS_WGS84_UTM_zone_14N, 32614) +ValuePair(PCS_WGS84_UTM_zone_15N, 32615) +ValuePair(PCS_WGS84_UTM_zone_16N, 32616) +ValuePair(PCS_WGS84_UTM_zone_17N, 32617) +ValuePair(PCS_WGS84_UTM_zone_18N, 32618) +ValuePair(PCS_WGS84_UTM_zone_19N, 32619) +ValuePair(PCS_WGS84_UTM_zone_20N, 32620) +ValuePair(PCS_WGS84_UTM_zone_21N, 32621) +ValuePair(PCS_WGS84_UTM_zone_22N, 32622) +ValuePair(PCS_WGS84_UTM_zone_23N, 32623) +ValuePair(PCS_WGS84_UTM_zone_24N, 32624) +ValuePair(PCS_WGS84_UTM_zone_25N, 32625) +ValuePair(PCS_WGS84_UTM_zone_26N, 32626) +ValuePair(PCS_WGS84_UTM_zone_27N, 32627) +ValuePair(PCS_WGS84_UTM_zone_28N, 32628) +ValuePair(PCS_WGS84_UTM_zone_29N, 32629) +ValuePair(PCS_WGS84_UTM_zone_30N, 32630) +ValuePair(PCS_WGS84_UTM_zone_31N, 32631) +ValuePair(PCS_WGS84_UTM_zone_32N, 32632) +ValuePair(PCS_WGS84_UTM_zone_33N, 32633) +ValuePair(PCS_WGS84_UTM_zone_34N, 32634) +ValuePair(PCS_WGS84_UTM_zone_35N, 32635) +ValuePair(PCS_WGS84_UTM_zone_36N, 32636) +ValuePair(PCS_WGS84_UTM_zone_37N, 32637) +ValuePair(PCS_WGS84_UTM_zone_38N, 32638) +ValuePair(PCS_WGS84_UTM_zone_39N, 32639) +ValuePair(PCS_WGS84_UTM_zone_40N, 32640) +ValuePair(PCS_WGS84_UTM_zone_41N, 32641) +ValuePair(PCS_WGS84_UTM_zone_42N, 32642) +ValuePair(PCS_WGS84_UTM_zone_43N, 32643) +ValuePair(PCS_WGS84_UTM_zone_44N, 32644) +ValuePair(PCS_WGS84_UTM_zone_45N, 32645) +ValuePair(PCS_WGS84_UTM_zone_46N, 32646) +ValuePair(PCS_WGS84_UTM_zone_47N, 32647) +ValuePair(PCS_WGS84_UTM_zone_48N, 32648) +ValuePair(PCS_WGS84_UTM_zone_49N, 32649) +ValuePair(PCS_WGS84_UTM_zone_50N, 32650) +ValuePair(PCS_WGS84_UTM_zone_51N, 32651) +ValuePair(PCS_WGS84_UTM_zone_52N, 32652) +ValuePair(PCS_WGS84_UTM_zone_53N, 32653) +ValuePair(PCS_WGS84_UTM_zone_54N, 32654) +ValuePair(PCS_WGS84_UTM_zone_55N, 32655) +ValuePair(PCS_WGS84_UTM_zone_56N, 32656) +ValuePair(PCS_WGS84_UTM_zone_57N, 32657) +ValuePair(PCS_WGS84_UTM_zone_58N, 32658) +ValuePair(PCS_WGS84_UTM_zone_59N, 32659) +ValuePair(PCS_WGS84_UTM_zone_60N, 32660) +ValuePair(PCS_WGS84_UTM_zone_1S, 32701) +ValuePair(PCS_WGS84_UTM_zone_2S, 32702) +ValuePair(PCS_WGS84_UTM_zone_3S, 32703) +ValuePair(PCS_WGS84_UTM_zone_4S, 32704) +ValuePair(PCS_WGS84_UTM_zone_5S, 32705) +ValuePair(PCS_WGS84_UTM_zone_6S, 32706) +ValuePair(PCS_WGS84_UTM_zone_7S, 32707) +ValuePair(PCS_WGS84_UTM_zone_8S, 32708) +ValuePair(PCS_WGS84_UTM_zone_9S, 32709) +ValuePair(PCS_WGS84_UTM_zone_10S, 32710) +ValuePair(PCS_WGS84_UTM_zone_11S, 32711) +ValuePair(PCS_WGS84_UTM_zone_12S, 32712) +ValuePair(PCS_WGS84_UTM_zone_13S, 32713) +ValuePair(PCS_WGS84_UTM_zone_14S, 32714) +ValuePair(PCS_WGS84_UTM_zone_15S, 32715) +ValuePair(PCS_WGS84_UTM_zone_16S, 32716) +ValuePair(PCS_WGS84_UTM_zone_17S, 32717) +ValuePair(PCS_WGS84_UTM_zone_18S, 32718) +ValuePair(PCS_WGS84_UTM_zone_19S, 32719) +ValuePair(PCS_WGS84_UTM_zone_20S, 32720) +ValuePair(PCS_WGS84_UTM_zone_21S, 32721) +ValuePair(PCS_WGS84_UTM_zone_22S, 32722) +ValuePair(PCS_WGS84_UTM_zone_23S, 32723) +ValuePair(PCS_WGS84_UTM_zone_24S, 32724) +ValuePair(PCS_WGS84_UTM_zone_25S, 32725) +ValuePair(PCS_WGS84_UTM_zone_26S, 32726) +ValuePair(PCS_WGS84_UTM_zone_27S, 32727) +ValuePair(PCS_WGS84_UTM_zone_28S, 32728) +ValuePair(PCS_WGS84_UTM_zone_29S, 32729) +ValuePair(PCS_WGS84_UTM_zone_30S, 32730) +ValuePair(PCS_WGS84_UTM_zone_31S, 32731) +ValuePair(PCS_WGS84_UTM_zone_32S, 32732) +ValuePair(PCS_WGS84_UTM_zone_33S, 32733) +ValuePair(PCS_WGS84_UTM_zone_34S, 32734) +ValuePair(PCS_WGS84_UTM_zone_35S, 32735) +ValuePair(PCS_WGS84_UTM_zone_36S, 32736) +ValuePair(PCS_WGS84_UTM_zone_37S, 32737) +ValuePair(PCS_WGS84_UTM_zone_38S, 32738) +ValuePair(PCS_WGS84_UTM_zone_39S, 32739) +ValuePair(PCS_WGS84_UTM_zone_40S, 32740) +ValuePair(PCS_WGS84_UTM_zone_41S, 32741) +ValuePair(PCS_WGS84_UTM_zone_42S, 32742) +ValuePair(PCS_WGS84_UTM_zone_43S, 32743) +ValuePair(PCS_WGS84_UTM_zone_44S, 32744) +ValuePair(PCS_WGS84_UTM_zone_45S, 32745) +ValuePair(PCS_WGS84_UTM_zone_46S, 32746) +ValuePair(PCS_WGS84_UTM_zone_47S, 32747) +ValuePair(PCS_WGS84_UTM_zone_48S, 32748) +ValuePair(PCS_WGS84_UTM_zone_49S, 32749) +ValuePair(PCS_WGS84_UTM_zone_50S, 32750) +ValuePair(PCS_WGS84_UTM_zone_51S, 32751) +ValuePair(PCS_WGS84_UTM_zone_52S, 32752) +ValuePair(PCS_WGS84_UTM_zone_53S, 32753) +ValuePair(PCS_WGS84_UTM_zone_54S, 32754) +ValuePair(PCS_WGS84_UTM_zone_55S, 32755) +ValuePair(PCS_WGS84_UTM_zone_56S, 32756) +ValuePair(PCS_WGS84_UTM_zone_57S, 32757) +ValuePair(PCS_WGS84_UTM_zone_58S, 32758) +ValuePair(PCS_WGS84_UTM_zone_59S, 32759) +ValuePair(PCS_WGS84_UTM_zone_60S, 32760) +/* end of list */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_pm.inc b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_pm.inc new file mode 100644 index 000000000..1e4159881 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_pm.inc @@ -0,0 +1,22 @@ +/* EPSG/GeoTIFF Rev 0.2 Prime Meridian Database */ + +/* C database for Geotiff include files. */ +/* the macro ValuePair() must be defined */ +/* by the enclosing include file */ + +#ifdef INCLUDE_OLD_CODES +#include old_pm.inc +#endif /* OLD Codes */ + +ValuePair(PM_Greenwich, 8901) +ValuePair(PM_Lisbon, 8902) +ValuePair(PM_Paris, 8903) +ValuePair(PM_Bogota, 8904) +ValuePair(PM_Madrid, 8905) +ValuePair(PM_Rome, 8906) +ValuePair(PM_Bern, 8907) +ValuePair(PM_Jakarta, 8908) +ValuePair(PM_Ferro, 8909) +ValuePair(PM_Brussels, 8910) +ValuePair(PM_Stockholm, 8911) +/* end of list */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_proj.inc b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_proj.inc new file mode 100644 index 000000000..d6c879155 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_proj.inc @@ -0,0 +1,443 @@ +/* + * EPSG/POSC Projection Codes - GeoTIFF Rev 0.2 + */ + +/* C database for Geotiff include files. */ +/* the macro ValuePair() must be defined */ +/* by the enclosing include file */ + +#ifdef INCLUDE_OLD_CODES +#include old_proj.inc +#endif /* OLD Codes */ + +/* New codes */ + +ValuePair(Proj_Stereo_70,19926) + +/* old codes */ + +ValuePair(Proj_Alabama_CS27_East, 10101) +ValuePair(Proj_Alabama_CS27_West, 10102) +ValuePair(Proj_Alabama_CS83_East, 10131) +ValuePair(Proj_Alabama_CS83_West, 10132) +ValuePair(Proj_Arizona_Coordinate_System_east, 10201) +ValuePair(Proj_Arizona_Coordinate_System_Central, 10202) +ValuePair(Proj_Arizona_Coordinate_System_west, 10203) +ValuePair(Proj_Arizona_CS83_east, 10231) +ValuePair(Proj_Arizona_CS83_Central, 10232) +ValuePair(Proj_Arizona_CS83_west, 10233) +ValuePair(Proj_Arkansas_CS27_North, 10301) +ValuePair(Proj_Arkansas_CS27_South, 10302) +ValuePair(Proj_Arkansas_CS83_North, 10331) +ValuePair(Proj_Arkansas_CS83_South, 10332) +ValuePair(Proj_California_CS27_I, 10401) +ValuePair(Proj_California_CS27_II, 10402) +ValuePair(Proj_California_CS27_III, 10403) +ValuePair(Proj_California_CS27_IV, 10404) +ValuePair(Proj_California_CS27_V, 10405) +ValuePair(Proj_California_CS27_VI, 10406) +ValuePair(Proj_California_CS27_VII, 10407) +ValuePair(Proj_California_CS83_1, 10431) +ValuePair(Proj_California_CS83_2, 10432) +ValuePair(Proj_California_CS83_3, 10433) +ValuePair(Proj_California_CS83_4, 10434) +ValuePair(Proj_California_CS83_5, 10435) +ValuePair(Proj_California_CS83_6, 10436) +ValuePair(Proj_Colorado_CS27_North, 10501) +ValuePair(Proj_Colorado_CS27_Central, 10502) +ValuePair(Proj_Colorado_CS27_South, 10503) +ValuePair(Proj_Colorado_CS83_North, 10531) +ValuePair(Proj_Colorado_CS83_Central, 10532) +ValuePair(Proj_Colorado_CS83_South, 10533) +ValuePair(Proj_Connecticut_CS27, 10600) +ValuePair(Proj_Connecticut_CS83, 10630) +ValuePair(Proj_Delaware_CS27, 10700) +ValuePair(Proj_Delaware_CS83, 10730) +ValuePair(Proj_Florida_CS27_East, 10901) +ValuePair(Proj_Florida_CS27_West, 10902) +ValuePair(Proj_Florida_CS27_North, 10903) +ValuePair(Proj_Florida_CS83_East, 10931) +ValuePair(Proj_Florida_CS83_West, 10932) +ValuePair(Proj_Florida_CS83_North, 10933) +ValuePair(Proj_Georgia_CS27_East, 11001) +ValuePair(Proj_Georgia_CS27_West, 11002) +ValuePair(Proj_Georgia_CS83_East, 11031) +ValuePair(Proj_Georgia_CS83_West, 11032) +ValuePair(Proj_Idaho_CS27_East, 11101) +ValuePair(Proj_Idaho_CS27_Central, 11102) +ValuePair(Proj_Idaho_CS27_West, 11103) +ValuePair(Proj_Idaho_CS83_East, 11131) +ValuePair(Proj_Idaho_CS83_Central, 11132) +ValuePair(Proj_Idaho_CS83_West, 11133) +ValuePair(Proj_Illinois_CS27_East, 11201) +ValuePair(Proj_Illinois_CS27_West, 11202) +ValuePair(Proj_Illinois_CS83_East, 11231) +ValuePair(Proj_Illinois_CS83_West, 11232) +ValuePair(Proj_Indiana_CS27_East, 11301) +ValuePair(Proj_Indiana_CS27_West, 11302) +ValuePair(Proj_Indiana_CS83_East, 11331) +ValuePair(Proj_Indiana_CS83_West, 11332) +ValuePair(Proj_Iowa_CS27_North, 11401) +ValuePair(Proj_Iowa_CS27_South, 11402) +ValuePair(Proj_Iowa_CS83_North, 11431) +ValuePair(Proj_Iowa_CS83_South, 11432) +ValuePair(Proj_Kansas_CS27_North, 11501) +ValuePair(Proj_Kansas_CS27_South, 11502) +ValuePair(Proj_Kansas_CS83_North, 11531) +ValuePair(Proj_Kansas_CS83_South, 11532) +ValuePair(Proj_Kentucky_CS27_North, 11601) +ValuePair(Proj_Kentucky_CS27_South, 11602) +ValuePair(Proj_Kentucky_CS83_North, 15303) +ValuePair(Proj_Kentucky_CS83_South, 11632) +ValuePair(Proj_Louisiana_CS27_North, 11701) +ValuePair(Proj_Louisiana_CS27_South, 11702) +ValuePair(Proj_Louisiana_CS83_North, 11731) +ValuePair(Proj_Louisiana_CS83_South, 11732) +ValuePair(Proj_Maine_CS27_East, 11801) +ValuePair(Proj_Maine_CS27_West, 11802) +ValuePair(Proj_Maine_CS83_East, 11831) +ValuePair(Proj_Maine_CS83_West, 11832) +ValuePair(Proj_Maryland_CS27, 11900) +ValuePair(Proj_Maryland_CS83, 11930) +ValuePair(Proj_Massachusetts_CS27_Mainland, 12001) +ValuePair(Proj_Massachusetts_CS27_Island, 12002) +ValuePair(Proj_Massachusetts_CS83_Mainland, 12031) +ValuePair(Proj_Massachusetts_CS83_Island, 12032) +ValuePair(Proj_Michigan_State_Plane_East, 12101) +ValuePair(Proj_Michigan_State_Plane_Old_Central, 12102) +ValuePair(Proj_Michigan_State_Plane_West, 12103) +ValuePair(Proj_Michigan_CS27_North, 12111) +ValuePair(Proj_Michigan_CS27_Central, 12112) +ValuePair(Proj_Michigan_CS27_South, 12113) +ValuePair(Proj_Michigan_CS83_North, 12141) +ValuePair(Proj_Michigan_CS83_Central, 12142) +ValuePair(Proj_Michigan_CS83_South, 12143) +ValuePair(Proj_Minnesota_CS27_North, 12201) +ValuePair(Proj_Minnesota_CS27_Central, 12202) +ValuePair(Proj_Minnesota_CS27_South, 12203) +ValuePair(Proj_Minnesota_CS83_North, 12231) +ValuePair(Proj_Minnesota_CS83_Central, 12232) +ValuePair(Proj_Minnesota_CS83_South, 12233) +ValuePair(Proj_Mississippi_CS27_East, 12301) +ValuePair(Proj_Mississippi_CS27_West, 12302) +ValuePair(Proj_Mississippi_CS83_East, 12331) +ValuePair(Proj_Mississippi_CS83_West, 12332) +ValuePair(Proj_Missouri_CS27_East, 12401) +ValuePair(Proj_Missouri_CS27_Central, 12402) +ValuePair(Proj_Missouri_CS27_West, 12403) +ValuePair(Proj_Missouri_CS83_East, 12431) +ValuePair(Proj_Missouri_CS83_Central, 12432) +ValuePair(Proj_Missouri_CS83_West, 12433) +ValuePair(Proj_Montana_CS27_North, 12501) +ValuePair(Proj_Montana_CS27_Central, 12502) +ValuePair(Proj_Montana_CS27_South, 12503) +ValuePair(Proj_Montana_CS83, 12530) +ValuePair(Proj_Nebraska_CS27_North, 12601) +ValuePair(Proj_Nebraska_CS27_South, 12602) +ValuePair(Proj_Nebraska_CS83, 12630) +ValuePair(Proj_Nevada_CS27_East, 12701) +ValuePair(Proj_Nevada_CS27_Central, 12702) +ValuePair(Proj_Nevada_CS27_West, 12703) +ValuePair(Proj_Nevada_CS83_East, 12731) +ValuePair(Proj_Nevada_CS83_Central, 12732) +ValuePair(Proj_Nevada_CS83_West, 12733) +ValuePair(Proj_New_Hampshire_CS27, 12800) +ValuePair(Proj_New_Hampshire_CS83, 12830) +ValuePair(Proj_New_Jersey_CS27, 12900) +ValuePair(Proj_New_Jersey_CS83, 12930) +ValuePair(Proj_New_Mexico_CS27_East, 13001) +ValuePair(Proj_New_Mexico_CS27_Central, 13002) +ValuePair(Proj_New_Mexico_CS27_West, 13003) +ValuePair(Proj_New_Mexico_CS83_East, 13031) +ValuePair(Proj_New_Mexico_CS83_Central, 13032) +ValuePair(Proj_New_Mexico_CS83_West, 13033) +ValuePair(Proj_New_York_CS27_East, 13101) +ValuePair(Proj_New_York_CS27_Central, 13102) +ValuePair(Proj_New_York_CS27_West, 13103) +ValuePair(Proj_New_York_CS27_Long_Island, 13104) +ValuePair(Proj_New_York_CS83_East, 13131) +ValuePair(Proj_New_York_CS83_Central, 13132) +ValuePair(Proj_New_York_CS83_West, 13133) +ValuePair(Proj_New_York_CS83_Long_Island, 13134) +ValuePair(Proj_North_Carolina_CS27, 13200) +ValuePair(Proj_North_Carolina_CS83, 13230) +ValuePair(Proj_North_Dakota_CS27_North, 13301) +ValuePair(Proj_North_Dakota_CS27_South, 13302) +ValuePair(Proj_North_Dakota_CS83_North, 13331) +ValuePair(Proj_North_Dakota_CS83_South, 13332) +ValuePair(Proj_Ohio_CS27_North, 13401) +ValuePair(Proj_Ohio_CS27_South, 13402) +ValuePair(Proj_Ohio_CS83_North, 13431) +ValuePair(Proj_Ohio_CS83_South, 13432) +ValuePair(Proj_Oklahoma_CS27_North, 13501) +ValuePair(Proj_Oklahoma_CS27_South, 13502) +ValuePair(Proj_Oklahoma_CS83_North, 13531) +ValuePair(Proj_Oklahoma_CS83_South, 13532) +ValuePair(Proj_Oregon_CS27_North, 13601) +ValuePair(Proj_Oregon_CS27_South, 13602) +ValuePair(Proj_Oregon_CS83_North, 13631) +ValuePair(Proj_Oregon_CS83_South, 13632) +ValuePair(Proj_Pennsylvania_CS27_North, 13701) +ValuePair(Proj_Pennsylvania_CS27_South, 13702) +ValuePair(Proj_Pennsylvania_CS83_North, 13731) +ValuePair(Proj_Pennsylvania_CS83_South, 13732) +ValuePair(Proj_Rhode_Island_CS27, 13800) +ValuePair(Proj_Rhode_Island_CS83, 13830) +ValuePair(Proj_South_Carolina_CS27_North, 13901) +ValuePair(Proj_South_Carolina_CS27_South, 13902) +ValuePair(Proj_South_Carolina_CS83, 13930) +ValuePair(Proj_South_Dakota_CS27_North, 14001) +ValuePair(Proj_South_Dakota_CS27_South, 14002) +ValuePair(Proj_South_Dakota_CS83_North, 14031) +ValuePair(Proj_South_Dakota_CS83_South, 14032) +ValuePair(Proj_Tennessee_CS27, 15302) +ValuePair(Proj_Tennessee_CS83, 14130) +ValuePair(Proj_Texas_CS27_North, 14201) +ValuePair(Proj_Texas_CS27_North_Central, 14202) +ValuePair(Proj_Texas_CS27_Central, 14203) +ValuePair(Proj_Texas_CS27_South_Central, 14204) +ValuePair(Proj_Texas_CS27_South, 14205) +ValuePair(Proj_Texas_CS83_North, 14231) +ValuePair(Proj_Texas_CS83_North_Central, 14232) +ValuePair(Proj_Texas_CS83_Central, 14233) +ValuePair(Proj_Texas_CS83_South_Central, 14234) +ValuePair(Proj_Texas_CS83_South, 14235) +ValuePair(Proj_Utah_CS27_North, 14301) +ValuePair(Proj_Utah_CS27_Central, 14302) +ValuePair(Proj_Utah_CS27_South, 14303) +ValuePair(Proj_Utah_CS83_North, 14331) +ValuePair(Proj_Utah_CS83_Central, 14332) +ValuePair(Proj_Utah_CS83_South, 14333) +ValuePair(Proj_Vermont_CS27, 14400) +ValuePair(Proj_Vermont_CS83, 14430) +ValuePair(Proj_Virginia_CS27_North, 14501) +ValuePair(Proj_Virginia_CS27_South, 14502) +ValuePair(Proj_Virginia_CS83_North, 14531) +ValuePair(Proj_Virginia_CS83_South, 14532) +ValuePair(Proj_Washington_CS27_North, 14601) +ValuePair(Proj_Washington_CS27_South, 14602) +ValuePair(Proj_Washington_CS83_North, 14631) +ValuePair(Proj_Washington_CS83_South, 14632) +ValuePair(Proj_West_Virginia_CS27_North, 14701) +ValuePair(Proj_West_Virginia_CS27_South, 14702) +ValuePair(Proj_West_Virginia_CS83_North, 14731) +ValuePair(Proj_West_Virginia_CS83_South, 14732) +ValuePair(Proj_Wisconsin_CS27_North, 14801) +ValuePair(Proj_Wisconsin_CS27_Central, 14802) +ValuePair(Proj_Wisconsin_CS27_South, 14803) +ValuePair(Proj_Wisconsin_CS83_North, 14831) +ValuePair(Proj_Wisconsin_CS83_Central, 14832) +ValuePair(Proj_Wisconsin_CS83_South, 14833) +ValuePair(Proj_Wyoming_CS27_East, 14901) +ValuePair(Proj_Wyoming_CS27_East_Central, 14902) +ValuePair(Proj_Wyoming_CS27_West_Central, 14903) +ValuePair(Proj_Wyoming_CS27_West, 14904) +ValuePair(Proj_Wyoming_CS83_East, 14931) +ValuePair(Proj_Wyoming_CS83_East_Central, 14932) +ValuePair(Proj_Wyoming_CS83_West_Central, 14933) +ValuePair(Proj_Wyoming_CS83_West, 14934) +ValuePair(Proj_Alaska_CS27_1, 15001) +ValuePair(Proj_Alaska_CS27_2, 15002) +ValuePair(Proj_Alaska_CS27_3, 15003) +ValuePair(Proj_Alaska_CS27_4, 15004) +ValuePair(Proj_Alaska_CS27_5, 15005) +ValuePair(Proj_Alaska_CS27_6, 15006) +ValuePair(Proj_Alaska_CS27_7, 15007) +ValuePair(Proj_Alaska_CS27_8, 15008) +ValuePair(Proj_Alaska_CS27_9, 15009) +ValuePair(Proj_Alaska_CS27_10, 15010) +ValuePair(Proj_Alaska_CS83_1, 15031) +ValuePair(Proj_Alaska_CS83_2, 15032) +ValuePair(Proj_Alaska_CS83_3, 15033) +ValuePair(Proj_Alaska_CS83_4, 15034) +ValuePair(Proj_Alaska_CS83_5, 15035) +ValuePair(Proj_Alaska_CS83_6, 15036) +ValuePair(Proj_Alaska_CS83_7, 15037) +ValuePair(Proj_Alaska_CS83_8, 15038) +ValuePair(Proj_Alaska_CS83_9, 15039) +ValuePair(Proj_Alaska_CS83_10, 15040) +ValuePair(Proj_Hawaii_CS27_1, 15101) +ValuePair(Proj_Hawaii_CS27_2, 15102) +ValuePair(Proj_Hawaii_CS27_3, 15103) +ValuePair(Proj_Hawaii_CS27_4, 15104) +ValuePair(Proj_Hawaii_CS27_5, 15105) +ValuePair(Proj_Hawaii_CS83_1, 15131) +ValuePair(Proj_Hawaii_CS83_2, 15132) +ValuePair(Proj_Hawaii_CS83_3, 15133) +ValuePair(Proj_Hawaii_CS83_4, 15134) +ValuePair(Proj_Hawaii_CS83_5, 15135) +ValuePair(Proj_Puerto_Rico_CS27, 15201) +ValuePair(Proj_St_Croix, 15202) +ValuePair(Proj_Puerto_Rico_Virgin_Is, 15230) +ValuePair(Proj_BLM_14N_feet, 15914) +ValuePair(Proj_BLM_15N_feet, 15915) +ValuePair(Proj_BLM_16N_feet, 15916) +ValuePair(Proj_BLM_17N_feet, 15917) +ValuePair(Proj_UTM_zone_1N, 16001) +ValuePair(Proj_UTM_zone_2N, 16002) +ValuePair(Proj_UTM_zone_3N, 16003) +ValuePair(Proj_UTM_zone_4N, 16004) +ValuePair(Proj_UTM_zone_5N, 16005) +ValuePair(Proj_UTM_zone_6N, 16006) +ValuePair(Proj_UTM_zone_7N, 16007) +ValuePair(Proj_UTM_zone_8N, 16008) +ValuePair(Proj_UTM_zone_9N, 16009) +ValuePair(Proj_UTM_zone_10N, 16010) +ValuePair(Proj_UTM_zone_11N, 16011) +ValuePair(Proj_UTM_zone_12N, 16012) +ValuePair(Proj_UTM_zone_13N, 16013) +ValuePair(Proj_UTM_zone_14N, 16014) +ValuePair(Proj_UTM_zone_15N, 16015) +ValuePair(Proj_UTM_zone_16N, 16016) +ValuePair(Proj_UTM_zone_17N, 16017) +ValuePair(Proj_UTM_zone_18N, 16018) +ValuePair(Proj_UTM_zone_19N, 16019) +ValuePair(Proj_UTM_zone_20N, 16020) +ValuePair(Proj_UTM_zone_21N, 16021) +ValuePair(Proj_UTM_zone_22N, 16022) +ValuePair(Proj_UTM_zone_23N, 16023) +ValuePair(Proj_UTM_zone_24N, 16024) +ValuePair(Proj_UTM_zone_25N, 16025) +ValuePair(Proj_UTM_zone_26N, 16026) +ValuePair(Proj_UTM_zone_27N, 16027) +ValuePair(Proj_UTM_zone_28N, 16028) +ValuePair(Proj_UTM_zone_29N, 16029) +ValuePair(Proj_UTM_zone_30N, 16030) +ValuePair(Proj_UTM_zone_31N, 16031) +ValuePair(Proj_UTM_zone_32N, 16032) +ValuePair(Proj_UTM_zone_33N, 16033) +ValuePair(Proj_UTM_zone_34N, 16034) +ValuePair(Proj_UTM_zone_35N, 16035) +ValuePair(Proj_UTM_zone_36N, 16036) +ValuePair(Proj_UTM_zone_37N, 16037) +ValuePair(Proj_UTM_zone_38N, 16038) +ValuePair(Proj_UTM_zone_39N, 16039) +ValuePair(Proj_UTM_zone_40N, 16040) +ValuePair(Proj_UTM_zone_41N, 16041) +ValuePair(Proj_UTM_zone_42N, 16042) +ValuePair(Proj_UTM_zone_43N, 16043) +ValuePair(Proj_UTM_zone_44N, 16044) +ValuePair(Proj_UTM_zone_45N, 16045) +ValuePair(Proj_UTM_zone_46N, 16046) +ValuePair(Proj_UTM_zone_47N, 16047) +ValuePair(Proj_UTM_zone_48N, 16048) +ValuePair(Proj_UTM_zone_49N, 16049) +ValuePair(Proj_UTM_zone_50N, 16050) +ValuePair(Proj_UTM_zone_51N, 16051) +ValuePair(Proj_UTM_zone_52N, 16052) +ValuePair(Proj_UTM_zone_53N, 16053) +ValuePair(Proj_UTM_zone_54N, 16054) +ValuePair(Proj_UTM_zone_55N, 16055) +ValuePair(Proj_UTM_zone_56N, 16056) +ValuePair(Proj_UTM_zone_57N, 16057) +ValuePair(Proj_UTM_zone_58N, 16058) +ValuePair(Proj_UTM_zone_59N, 16059) +ValuePair(Proj_UTM_zone_60N, 16060) +ValuePair(Proj_UTM_zone_1S, 16101) +ValuePair(Proj_UTM_zone_2S, 16102) +ValuePair(Proj_UTM_zone_3S, 16103) +ValuePair(Proj_UTM_zone_4S, 16104) +ValuePair(Proj_UTM_zone_5S, 16105) +ValuePair(Proj_UTM_zone_6S, 16106) +ValuePair(Proj_UTM_zone_7S, 16107) +ValuePair(Proj_UTM_zone_8S, 16108) +ValuePair(Proj_UTM_zone_9S, 16109) +ValuePair(Proj_UTM_zone_10S, 16110) +ValuePair(Proj_UTM_zone_11S, 16111) +ValuePair(Proj_UTM_zone_12S, 16112) +ValuePair(Proj_UTM_zone_13S, 16113) +ValuePair(Proj_UTM_zone_14S, 16114) +ValuePair(Proj_UTM_zone_15S, 16115) +ValuePair(Proj_UTM_zone_16S, 16116) +ValuePair(Proj_UTM_zone_17S, 16117) +ValuePair(Proj_UTM_zone_18S, 16118) +ValuePair(Proj_UTM_zone_19S, 16119) +ValuePair(Proj_UTM_zone_20S, 16120) +ValuePair(Proj_UTM_zone_21S, 16121) +ValuePair(Proj_UTM_zone_22S, 16122) +ValuePair(Proj_UTM_zone_23S, 16123) +ValuePair(Proj_UTM_zone_24S, 16124) +ValuePair(Proj_UTM_zone_25S, 16125) +ValuePair(Proj_UTM_zone_26S, 16126) +ValuePair(Proj_UTM_zone_27S, 16127) +ValuePair(Proj_UTM_zone_28S, 16128) +ValuePair(Proj_UTM_zone_29S, 16129) +ValuePair(Proj_UTM_zone_30S, 16130) +ValuePair(Proj_UTM_zone_31S, 16131) +ValuePair(Proj_UTM_zone_32S, 16132) +ValuePair(Proj_UTM_zone_33S, 16133) +ValuePair(Proj_UTM_zone_34S, 16134) +ValuePair(Proj_UTM_zone_35S, 16135) +ValuePair(Proj_UTM_zone_36S, 16136) +ValuePair(Proj_UTM_zone_37S, 16137) +ValuePair(Proj_UTM_zone_38S, 16138) +ValuePair(Proj_UTM_zone_39S, 16139) +ValuePair(Proj_UTM_zone_40S, 16140) +ValuePair(Proj_UTM_zone_41S, 16141) +ValuePair(Proj_UTM_zone_42S, 16142) +ValuePair(Proj_UTM_zone_43S, 16143) +ValuePair(Proj_UTM_zone_44S, 16144) +ValuePair(Proj_UTM_zone_45S, 16145) +ValuePair(Proj_UTM_zone_46S, 16146) +ValuePair(Proj_UTM_zone_47S, 16147) +ValuePair(Proj_UTM_zone_48S, 16148) +ValuePair(Proj_UTM_zone_49S, 16149) +ValuePair(Proj_UTM_zone_50S, 16150) +ValuePair(Proj_UTM_zone_51S, 16151) +ValuePair(Proj_UTM_zone_52S, 16152) +ValuePair(Proj_UTM_zone_53S, 16153) +ValuePair(Proj_UTM_zone_54S, 16154) +ValuePair(Proj_UTM_zone_55S, 16155) +ValuePair(Proj_UTM_zone_56S, 16156) +ValuePair(Proj_UTM_zone_57S, 16157) +ValuePair(Proj_UTM_zone_58S, 16158) +ValuePair(Proj_UTM_zone_59S, 16159) +ValuePair(Proj_UTM_zone_60S, 16160) +ValuePair(Proj_Gauss_Kruger_zone_0, 16200) +ValuePair(Proj_Gauss_Kruger_zone_1, 16201) +ValuePair(Proj_Gauss_Kruger_zone_2, 16202) +ValuePair(Proj_Gauss_Kruger_zone_3, 16203) +ValuePair(Proj_Gauss_Kruger_zone_4, 16204) +ValuePair(Proj_Gauss_Kruger_zone_5, 16205) +ValuePair(Proj_Map_Grid_of_Australia_48, 17348) +ValuePair(Proj_Map_Grid_of_Australia_49, 17349) +ValuePair(Proj_Map_Grid_of_Australia_50, 17350) +ValuePair(Proj_Map_Grid_of_Australia_51, 17351) +ValuePair(Proj_Map_Grid_of_Australia_52, 17352) +ValuePair(Proj_Map_Grid_of_Australia_53, 17353) +ValuePair(Proj_Map_Grid_of_Australia_54, 17354) +ValuePair(Proj_Map_Grid_of_Australia_55, 17355) +ValuePair(Proj_Map_Grid_of_Australia_56, 17356) +ValuePair(Proj_Map_Grid_of_Australia_57, 17357) +ValuePair(Proj_Map_Grid_of_Australia_58, 17358) +ValuePair(Proj_Australian_Map_Grid_48, 17448) +ValuePair(Proj_Australian_Map_Grid_49, 17449) +ValuePair(Proj_Australian_Map_Grid_50, 17450) +ValuePair(Proj_Australian_Map_Grid_51, 17451) +ValuePair(Proj_Australian_Map_Grid_52, 17452) +ValuePair(Proj_Australian_Map_Grid_53, 17453) +ValuePair(Proj_Australian_Map_Grid_54, 17454) +ValuePair(Proj_Australian_Map_Grid_55, 17455) +ValuePair(Proj_Australian_Map_Grid_56, 17456) +ValuePair(Proj_Australian_Map_Grid_57, 17457) +ValuePair(Proj_Australian_Map_Grid_58, 17458) +ValuePair(Proj_Argentina_1, 18031) +ValuePair(Proj_Argentina_2, 18032) +ValuePair(Proj_Argentina_3, 18033) +ValuePair(Proj_Argentina_4, 18034) +ValuePair(Proj_Argentina_5, 18035) +ValuePair(Proj_Argentina_6, 18036) +ValuePair(Proj_Argentina_7, 18037) +ValuePair(Proj_Colombia_3W, 18051) +ValuePair(Proj_Colombia_Bogota, 18052) +ValuePair(Proj_Colombia_3E, 18053) +ValuePair(Proj_Colombia_6E, 18054) +ValuePair(Proj_Egypt_Red_Belt, 18072) +ValuePair(Proj_Egypt_Purple_Belt, 18073) +ValuePair(Proj_Extended_Purple_Belt, 18074) +ValuePair(Proj_New_Zealand_North_Island_Nat_Grid, 18141) +ValuePair(Proj_New_Zealand_South_Island_Nat_Grid, 18142) +ValuePair(Proj_Bahrain_Grid, 19900) +ValuePair(Proj_Netherlands_E_Indies_Equatorial, 19905) +ValuePair(Proj_RSO_Borneo, 19912) +/* end of list */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_units.inc b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_units.inc new file mode 100644 index 000000000..fe1b5db7b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_units.inc @@ -0,0 +1,35 @@ +/* + * Rev. 0.2 EPSG/POSC Units Database. + */ + +#ifdef INCLUDE_OLD_CODES +#include geo_units.inc +#endif /* OLD Codes */ + +ValuePair(Linear_Meter, 9001) +ValuePair(Linear_Foot, 9002) +ValuePair(Linear_Foot_US_Survey, 9003) +ValuePair(Linear_Foot_Modified_American, 9004) +ValuePair(Linear_Foot_Clarke, 9005) +ValuePair(Linear_Foot_Indian, 9006) +ValuePair(Linear_Link, 9007) +ValuePair(Linear_Link_Benoit, 9008) +ValuePair(Linear_Link_Sears, 9009) +ValuePair(Linear_Chain_Benoit, 9010) +ValuePair(Linear_Chain_Sears, 9011) +ValuePair(Linear_Yard_Sears, 9012) +ValuePair(Linear_Yard_Indian, 9013) +ValuePair(Linear_Fathom, 9014) +ValuePair(Linear_Mile_International_Nautical, 9015) +/* + * Angular Units + */ +ValuePair(Angular_Radian, 9101) +ValuePair(Angular_Degree, 9102) +ValuePair(Angular_Arc_Minute, 9103) +ValuePair(Angular_Arc_Second, 9104) +ValuePair(Angular_Grad, 9105) +ValuePair(Angular_Gon, 9106) +ValuePair(Angular_DMS, 9107) +ValuePair(Angular_DMS_Hemisphere, 9108) +/* end of list */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_vertcs.inc b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_vertcs.inc new file mode 100644 index 000000000..d6b6791cb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/epsg_vertcs.inc @@ -0,0 +1,46 @@ +/* + * EPSG/POSC Ellipsoid-referenced Vertical CS + * Note: these should correspond exactly with the Ellipsoid database. + */ +ValuePair(VertCS_Airy_1830_ellipsoid, 5001) +ValuePair(VertCS_Airy_Modified_1849_ellipsoid, 5002) +ValuePair(VertCS_ANS_ellipsoid, 5003) +ValuePair(VertCS_Bessel_1841_ellipsoid, 5004) +ValuePair(VertCS_Bessel_Modified_ellipsoid, 5005) +ValuePair(VertCS_Bessel_Namibia_ellipsoid, 5006) +ValuePair(VertCS_Clarke_1858_ellipsoid, 5007) +ValuePair(VertCS_Clarke_1866_ellipsoid, 5008) +ValuePair(VertCS_Clarke_1880_Benoit_ellipsoid, 5010) +ValuePair(VertCS_Clarke_1880_IGN_ellipsoid, 5011) +ValuePair(VertCS_Clarke_1880_RGS_ellipsoid, 5012) +ValuePair(VertCS_Clarke_1880_Arc_ellipsoid, 5013) +ValuePair(VertCS_Clarke_1880_SGA_1922_ellipsoid, 5014) +ValuePair(VertCS_Everest_1830_1937_Adjustment_ellipsoid, 5015) +ValuePair(VertCS_Everest_1830_1967_Definition_ellipsoid, 5016) +ValuePair(VertCS_Everest_1830_1975_Definition_ellipsoid, 5017) +ValuePair(VertCS_Everest_1830_Modified_ellipsoid, 5018) +ValuePair(VertCS_GRS_1980_ellipsoid, 5019) +ValuePair(VertCS_Helmert_1906_ellipsoid, 5020) +ValuePair(VertCS_INS_ellipsoid, 5021) +ValuePair(VertCS_International_1924_ellipsoid, 5022) +ValuePair(VertCS_International_1967_ellipsoid, 5023) +ValuePair(VertCS_Krassowsky_1940_ellipsoid, 5024) +ValuePair(VertCS_NWL_9D_ellipsoid, 5025) +ValuePair(VertCS_NWL_10D_ellipsoid, 5026) +ValuePair(VertCS_Plessis_1817_ellipsoid, 5027) +ValuePair(VertCS_Struve_1860_ellipsoid, 5028) +ValuePair(VertCS_War_Office_ellipsoid, 5029) +ValuePair(VertCS_WGS_84_ellipsoid, 5030) +ValuePair(VertCS_GEM_10C_ellipsoid, 5031) +ValuePair(VertCS_OSU86F_ellipsoid, 5032) +ValuePair(VertCS_OSU91A_ellipsoid, 5033) +/* + * Other established Vertical CS + */ +ValuePair(VertCS_Newlyn, 5101) +ValuePair(VertCS_North_American_Vertical_Datum_1929, 5102) +ValuePair(VertCS_North_American_Vertical_Datum_1988, 5103) +ValuePair(VertCS_Yellow_Sea_1956, 5104) +ValuePair(VertCS_Baltic_Sea, 5105) +ValuePair(VertCS_Caspian_Sea, 5106) +/* end of list */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/gdal_libgeotiff_symbol_rename.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/gdal_libgeotiff_symbol_rename.h new file mode 100644 index 000000000..1f63ab3db --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/gdal_libgeotiff_symbol_rename.h @@ -0,0 +1,116 @@ +/* This is a generated file by dump_symbols.h. *DO NOT EDIT MANUALLY !* */ +#define call_gmon_start gdal_call_gmon_start +#define DefaultPrint gdal_DefaultPrint +#define DefaultRead gdal_DefaultRead +#define EPSGProjMethodToCTProjMethod gdal_EPSGProjMethodToCTProjMethod +#define FindCode gdal_FindCode +#define FindName gdal_FindName +#define frame_dummy gdal_frame_dummy +#define GTIFAllocDefn gdal_GTIFAllocDefn +#define GTIFAngleStringToDD gdal_GTIFAngleStringToDD +#define GTIFAngleToDD gdal_GTIFAngleToDD +#define _GTIFcalloc gdal__GTIFcalloc +#define GTIFDeaccessCSV gdal_GTIFDeaccessCSV +#define GTIFDecToDMS gdal_GTIFDecToDMS +#define GTIFDirectoryInfo gdal_GTIFDirectoryInfo +#define GTIFFetchProjParms gdal_GTIFFetchProjParms +#define GTIFFree gdal_GTIFFree +#define _GTIFFree gdal__GTIFFree +#define GTIFFreeDefn gdal_GTIFFreeDefn +#define GTIFFreeMemory gdal_GTIFFreeMemory +#define GTIFGetDatumInfo gdal_GTIFGetDatumInfo +#define GTIFGetDefn gdal_GTIFGetDefn +#define GTIFGetEllipsoidInfo gdal_GTIFGetEllipsoidInfo +#define _GTIFGetField gdal__GTIFGetField +#define _GTIFGetField gdal__GTIFGetField +#define GTIFGetGCSInfo gdal_GTIFGetGCSInfo +#define GTIFGetPCSInfo gdal_GTIFGetPCSInfo +#define GTIFGetPMInfo gdal_GTIFGetPMInfo +#define GTIFGetProj4Defn gdal_GTIFGetProj4Defn +#define GTIFGetProjTRFInfo gdal_GTIFGetProjTRFInfo +#define GTIFGetUOMAngleInfo gdal_GTIFGetUOMAngleInfo +#define GTIFGetUOMLengthInfo gdal_GTIFGetUOMLengthInfo +#define GTIFImageToPCS gdal_GTIFImageToPCS +#define GTIFImport gdal_GTIFImport +#define GTIFKeyCode gdal_GTIFKeyCode +#define GTIFKeyGet gdal_GTIFKeyGet +#define GTIFKeyInfo gdal_GTIFKeyInfo +#define GTIFKeyName gdal_GTIFKeyName +#define GTIFKeySet gdal_GTIFKeySet +#define GTIFMapSysToPCS gdal_GTIFMapSysToPCS +#define GTIFMapSysToProj gdal_GTIFMapSysToProj +#define _GTIFmemcpy gdal__GTIFmemcpy +#define GTIFNew gdal_GTIFNew +#define GTIFNewSimpleTags gdal_GTIFNewSimpleTags +#define GTIFNewWithMethods gdal_GTIFNewWithMethods +#define GTIFPCSToImage gdal_GTIFPCSToImage +#define GTIFPCSToMapSys gdal_GTIFPCSToMapSys +#define GTIFPrint gdal_GTIFPrint +#define GTIFPrintDefn gdal_GTIFPrintDefn +#define GTIFProj4FromLatLong gdal_GTIFProj4FromLatLong +#define GTIFProj4ToLatLong gdal_GTIFProj4ToLatLong +#define GTIFProjToMapSys gdal_GTIFProjToMapSys +#define _GTIFrealloc gdal__GTIFrealloc +#define _GTIFSetDefaultTIFF gdal__GTIFSetDefaultTIFF +#define _GTIFSetField gdal__GTIFSetField +#define _GTIFSetField gdal__GTIFSetField +#define GTIFSetFromProj4 gdal_GTIFSetFromProj4 +#define GTIFSetSimpleTagsMethods gdal_GTIFSetSimpleTagsMethods +#define GTIFTagCode gdal_GTIFTagCode +#define GTIFTagName gdal_GTIFTagName +#define _GTIFTagType gdal__GTIFTagType +#define _GTIFTagType gdal__GTIFTagType +#define GTIFTiepointTranslate gdal_GTIFTiepointTranslate +#define GTIFTypeCode gdal_GTIFTypeCode +#define GTIFTypeName gdal_GTIFTypeName +#define GTIFValueCode gdal_GTIFValueCode +#define GTIFValueName gdal_GTIFValueName +#define GTIFWriteKeys gdal_GTIFWriteKeys +#define inv_geotransform gdal_inv_geotransform +#define OSRFreeStringList gdal_OSRFreeStringList +#define OSR_GDV gdal_OSR_GDV +#define OSR_GSV gdal_OSR_GSV +#define OSRProj4Tokenize gdal_OSRProj4Tokenize +#define PrintGeoTags gdal_PrintGeoTags +#define PrintKey gdal_PrintKey +#define PrintTag gdal_PrintTag +#define ReadKey gdal_ReadKey +#define ReadKey gdal_ReadKey +#define ReadTag gdal_ReadTag +#define SetGTParmIds gdal_SetGTParmIds +#define SortKeys gdal_SortKeys +#define ST_Create gdal_ST_Create +#define ST_Destroy gdal_ST_Destroy +#define ST_GetKey gdal_ST_GetKey +#define StringError gdal_StringError +#define ST_SetKey gdal_ST_SetKey +#define ST_TagType gdal_ST_TagType +#define ST_TypeSize gdal_ST_TypeSize +#define WriteKey gdal_WriteKey +#define XTIFFClientOpen gdal_XTIFFClientOpen +#define XTIFFClose gdal_XTIFFClose +#define _XTIFFDefaultDirectory gdal__XTIFFDefaultDirectory +#define XTIFFFdOpen gdal_XTIFFFdOpen +#define XTIFFInitialize gdal_XTIFFInitialize +#define _XTIFFLocalDefaultDirectory gdal__XTIFFLocalDefaultDirectory +#define XTIFFOpen gdal_XTIFFOpen +#define StatePlaneTable gdal_StatePlaneTable +#define _keyInfo gdal__keyInfo +#define _csdefaultValue gdal__csdefaultValue +#define _modeltypeValue gdal__modeltypeValue +#define _rastertypeValue gdal__rastertypeValue +#define _geounitsValue gdal__geounitsValue +#define _geographicValue gdal__geographicValue +#define _geodeticdatumValue gdal__geodeticdatumValue +#define _ellipsoidValue gdal__ellipsoidValue +#define _primemeridianValue gdal__primemeridianValue +#define _pcstypeValue gdal__pcstypeValue +#define _projectionValue gdal__projectionValue +#define _coordtransValue gdal__coordtransValue +#define _vertcstypeValue gdal__vertcstypeValue +#define _vdatumValue gdal__vdatumValue +#define _formatInfo gdal__formatInfo +#define _tagInfo gdal__tagInfo +#define xtiffFieldInfo gdal_xtiffFieldInfo +#define _gtiff_size gdal__gtiff_size +#define _ParentExtender gdal__ParentExtender diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_config.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_config.h new file mode 100644 index 000000000..d773b7179 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_config.h @@ -0,0 +1,20 @@ +/* geo_config.h. Generated automatically by configure. */ +#ifndef GEO_CONFIG_H +#define GEO_CONFIG_H + +#include "cpl_config.h" +#include "cpl_string.h" +#ifdef sprintf +#undef sprintf +#endif +#define sprintf CPLsprintf + +#ifdef RENAME_INTERNAL_LIBTIFF_SYMBOLS +#include "gdal_libtiff_symbol_rename.h" +#endif + +#ifdef RENAME_INTERNAL_LIBGEOTIFF_SYMBOLS +#include "gdal_libgeotiff_symbol_rename.h" +#endif + +#endif /* ndef GEO_CONFIG_H */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_ctrans.inc b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_ctrans.inc new file mode 100644 index 000000000..9bad7c149 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_ctrans.inc @@ -0,0 +1,94 @@ +/****************************************************************************** + * $Id: geo_ctrans.inc 2209 2012-05-09 01:34:58Z warmerdam $ + * + * Project: libgeotiff + * Purpose: GeoTIFF Projection Method codes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log$ + * Revision 1.3 2005/03/04 03:59:11 fwarmerdam + * Added header. + * + */ + +/* C database for Geotiff include files. */ +/* the macro ValuePair() must be defined */ +/* by the enclosing include file */ + +/* + * Revised 12 Jul 1995 NDR -- changed South Oriented to a code + * Revised 28 Sep 1995 NDR -- Added Rev. 1.0 aliases. + */ + +ValuePair(CT_TransverseMercator, 1) +ValuePair(CT_TransvMercator_Modified_Alaska, 2) +ValuePair(CT_ObliqueMercator, 3) +ValuePair(CT_ObliqueMercator_Laborde, 4) +ValuePair(CT_ObliqueMercator_Rosenmund, 5) +ValuePair(CT_ObliqueMercator_Spherical, 6) /* not advisable */ +ValuePair(CT_Mercator, 7) +ValuePair(CT_LambertConfConic_2SP, 8) +ValuePair(CT_LambertConfConic,CT_LambertConfConic_2SP) /* Alias */ +ValuePair(CT_LambertConfConic_1SP, 9) +ValuePair(CT_LambertConfConic_Helmert,CT_LambertConfConic_1SP) /* alias */ +ValuePair(CT_LambertAzimEqualArea, 10) +ValuePair(CT_AlbersEqualArea, 11) +ValuePair(CT_AzimuthalEquidistant, 12) +ValuePair(CT_EquidistantConic, 13) +ValuePair(CT_Stereographic, 14) +ValuePair(CT_PolarStereographic, 15) +ValuePair(CT_ObliqueStereographic, 16) /* not advisable */ +ValuePair(CT_Equirectangular, 17) +ValuePair(CT_CassiniSoldner, 18) +ValuePair(CT_Gnomonic, 19) +ValuePair(CT_MillerCylindrical, 20) +ValuePair(CT_Orthographic, 21) +ValuePair(CT_Polyconic, 22) +ValuePair(CT_Robinson, 23) +ValuePair(CT_Sinusoidal, 24) +ValuePair(CT_VanDerGrinten, 25) +ValuePair(CT_NewZealandMapGrid, 26) +/* Added for 1.0 */ +ValuePair(CT_TransvMercator_SouthOrientated, 27) + +/* Added Feb 2005 */ +ValuePair(CT_CylindricalEqualArea, 28) + +/* Added May 2012 - from now on we use the EPSG */ +ValuePair(CT_HotineObliqueMercatorAzimuthCenter, 9815) + + +/* Aliases */ + +ValuePair(CT_SouthOrientedGaussConformal,CT_TransvMercator_SouthOrientated) +ValuePair(CT_AlaskaConformal, CT_TransvMercator_Modified_Alaska) +ValuePair(CT_TransvEquidistCylindrical, CT_CassiniSoldner) +ValuePair(CT_ObliqueMercator_Hotine, CT_ObliqueMercator) +ValuePair(CT_SwissObliqueCylindrical, CT_ObliqueMercator_Rosenmund) +ValuePair(CT_GaussBoaga, CT_TransverseMercator) +ValuePair(CT_GaussKruger, CT_TransverseMercator) +ValuePair(CT_TransvMercator_SouthOriented, CT_TransvMercator_SouthOrientated) + + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_extra.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_extra.c new file mode 100644 index 000000000..b31aa7d13 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_extra.c @@ -0,0 +1,732 @@ +/****************************************************************************** + * $Id: geo_extra.c 1568 2009-04-22 21:10:55Z warmerdam $ + * + * Project: libgeotiff + * Purpose: Code to normalize a few common PCS values without use of CSV + * files. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +/* +#include "geotiff.h" +#include "geo_tiffp.h" +#include "geo_keyp.h" +*/ + +#include "geo_normalize.h" +#include "geovalues.h" + +static int StatePlaneTable[] = +{ + PCS_NAD83_Alabama_East, Proj_Alabama_CS83_East, + PCS_NAD83_Alabama_West, Proj_Alabama_CS83_West, + + PCS_NAD83_Alaska_zone_1, Proj_Alaska_CS83_1, + PCS_NAD83_Alaska_zone_2, Proj_Alaska_CS83_2, + PCS_NAD83_Alaska_zone_3, Proj_Alaska_CS83_3, + PCS_NAD83_Alaska_zone_4, Proj_Alaska_CS83_4, + PCS_NAD83_Alaska_zone_5, Proj_Alaska_CS83_5, + PCS_NAD83_Alaska_zone_6, Proj_Alaska_CS83_6, + PCS_NAD83_Alaska_zone_7, Proj_Alaska_CS83_7, + PCS_NAD83_Alaska_zone_8, Proj_Alaska_CS83_8, + PCS_NAD83_Alaska_zone_9, Proj_Alaska_CS83_9, + PCS_NAD83_Alaska_zone_10, Proj_Alaska_CS83_10, + + PCS_NAD83_California_1, Proj_California_CS83_1, + PCS_NAD83_California_2, Proj_California_CS83_2, + PCS_NAD83_California_3, Proj_California_CS83_3, + PCS_NAD83_California_4, Proj_California_CS83_4, + PCS_NAD83_California_5, Proj_California_CS83_5, + PCS_NAD83_California_6, Proj_California_CS83_6, + + PCS_NAD83_Arizona_East, Proj_Arizona_CS83_east, + PCS_NAD83_Arizona_Central, Proj_Arizona_CS83_Central, + PCS_NAD83_Arizona_West, Proj_Arizona_CS83_west, + + PCS_NAD83_Arkansas_North, Proj_Arkansas_CS83_North, + PCS_NAD83_Arkansas_South, Proj_Arkansas_CS83_South, + + PCS_NAD83_Colorado_North, Proj_Colorado_CS83_North, + PCS_NAD83_Colorado_Central, Proj_Colorado_CS83_Central, + PCS_NAD83_Colorado_South, Proj_Colorado_CS83_South, + + PCS_NAD83_Connecticut, Proj_Connecticut_CS83, + + PCS_NAD83_Delaware, Proj_Delaware_CS83, + + PCS_NAD83_Florida_East, Proj_Florida_CS83_East, + PCS_NAD83_Florida_North, Proj_Florida_CS83_North, + PCS_NAD83_Florida_West, Proj_Florida_CS83_West, + + PCS_NAD83_Hawaii_zone_1, Proj_Hawaii_CS83_1, + PCS_NAD83_Hawaii_zone_2, Proj_Hawaii_CS83_2, + PCS_NAD83_Hawaii_zone_3, Proj_Hawaii_CS83_3, + PCS_NAD83_Hawaii_zone_4, Proj_Hawaii_CS83_4, + PCS_NAD83_Hawaii_zone_5, Proj_Hawaii_CS83_5, + + PCS_NAD83_Georgia_East, Proj_Georgia_CS83_East, + PCS_NAD83_Georgia_West, Proj_Georgia_CS83_West, + + PCS_NAD83_Idaho_East, Proj_Idaho_CS83_East, + PCS_NAD83_Idaho_Central, Proj_Idaho_CS83_Central, + PCS_NAD83_Idaho_West, Proj_Idaho_CS83_West, + + PCS_NAD83_Illinois_East, Proj_Illinois_CS83_East, + PCS_NAD83_Illinois_West, Proj_Illinois_CS83_West, + + PCS_NAD83_Indiana_East, Proj_Indiana_CS83_East, + PCS_NAD83_Indiana_West, Proj_Indiana_CS83_West, + + PCS_NAD83_Iowa_North, Proj_Iowa_CS83_North, + PCS_NAD83_Iowa_South, Proj_Iowa_CS83_South, + + PCS_NAD83_Kansas_North, Proj_Kansas_CS83_North, + PCS_NAD83_Kansas_South, Proj_Kansas_CS83_South, + + PCS_NAD83_Kentucky_North, Proj_Kentucky_CS83_North, + PCS_NAD83_Kentucky_South, Proj_Kentucky_CS83_South, + + PCS_NAD83_Louisiana_North, Proj_Louisiana_CS83_North, + PCS_NAD83_Louisiana_South, Proj_Louisiana_CS83_South, + + PCS_NAD83_Maine_East, Proj_Maine_CS83_East, + PCS_NAD83_Maine_West, Proj_Maine_CS83_West, + + PCS_NAD83_Maryland, Proj_Maryland_CS83, + + PCS_NAD83_Massachusetts, Proj_Massachusetts_CS83_Mainland, + PCS_NAD83_Massachusetts_Is, Proj_Massachusetts_CS83_Island, + + PCS_NAD83_Michigan_North, Proj_Michigan_CS83_North, + PCS_NAD83_Michigan_Central, Proj_Michigan_CS83_Central, + PCS_NAD83_Michigan_South, Proj_Michigan_CS83_South, + + PCS_NAD83_Minnesota_North, Proj_Minnesota_CS83_North, + PCS_NAD83_Minnesota_Cent, Proj_Minnesota_CS83_Central, + PCS_NAD83_Minnesota_South, Proj_Minnesota_CS83_South, + + PCS_NAD83_Mississippi_East, Proj_Mississippi_CS83_East, + PCS_NAD83_Mississippi_West, Proj_Mississippi_CS83_West, + + PCS_NAD83_Missouri_East, Proj_Missouri_CS83_East, + PCS_NAD83_Missouri_Central, Proj_Missouri_CS83_Central, + PCS_NAD83_Missouri_West, Proj_Missouri_CS83_West, + + PCS_NAD83_Montana, Proj_Montana_CS83, + + PCS_NAD83_Nebraska, Proj_Nebraska_CS83, + + PCS_NAD83_Nevada_East, Proj_Nevada_CS83_East, + PCS_NAD83_Nevada_Central, Proj_Nevada_CS83_Central, + PCS_NAD83_Nevada_West, Proj_Nevada_CS83_West, + + PCS_NAD83_New_Hampshire, Proj_New_Hampshire_CS83, + + PCS_NAD83_New_Jersey, Proj_New_Jersey_CS83, + + PCS_NAD83_New_Mexico_East, Proj_New_Mexico_CS83_East, + PCS_NAD83_New_Mexico_Cent, Proj_New_Mexico_CS83_Central, + PCS_NAD83_New_Mexico_West, Proj_New_Mexico_CS83_West, + + PCS_NAD83_New_York_East, Proj_New_York_CS83_East, + PCS_NAD83_New_York_Central, Proj_New_York_CS83_Central, + PCS_NAD83_New_York_West, Proj_New_York_CS83_West, + PCS_NAD83_New_York_Long_Is, Proj_New_York_CS83_Long_Island, + + PCS_NAD83_North_Carolina, Proj_North_Carolina_CS83, + + PCS_NAD83_North_Dakota_N, Proj_North_Dakota_CS83_North, + PCS_NAD83_North_Dakota_S, Proj_North_Dakota_CS83_South, + + PCS_NAD83_Ohio_North, Proj_Ohio_CS83_North, + PCS_NAD83_Ohio_South, Proj_Ohio_CS83_South, + + PCS_NAD83_Oklahoma_North, Proj_Oklahoma_CS83_North, + PCS_NAD83_Oklahoma_South, Proj_Oklahoma_CS83_South, + + PCS_NAD83_Oregon_North, Proj_Oregon_CS83_North, + PCS_NAD83_Oregon_South, Proj_Oregon_CS83_South, + + PCS_NAD83_Pennsylvania_N, Proj_Pennsylvania_CS83_North, + PCS_NAD83_Pennsylvania_S, Proj_Pennsylvania_CS83_South, + + PCS_NAD83_Rhode_Island, Proj_Rhode_Island_CS83, + + PCS_NAD83_South_Carolina, Proj_South_Carolina_CS83, + + PCS_NAD83_South_Dakota_N, Proj_South_Dakota_CS83_North, + PCS_NAD83_South_Dakota_S, Proj_South_Dakota_CS83_South, + + PCS_NAD83_Tennessee, Proj_Tennessee_CS83, + + PCS_NAD83_Texas_North, Proj_Texas_CS83_North, + PCS_NAD83_Texas_North_Cen, Proj_Texas_CS83_North_Central, + PCS_NAD83_Texas_Central, Proj_Texas_CS83_Central, + PCS_NAD83_Texas_South_Cen, Proj_Texas_CS83_South_Central, + PCS_NAD83_Texas_South, Proj_Texas_CS83_South, + + PCS_NAD83_Utah_North, Proj_Utah_CS83_North, + PCS_NAD83_Utah_Central, Proj_Utah_CS83_Central, + PCS_NAD83_Utah_South, Proj_Utah_CS83_South, + + PCS_NAD83_Vermont, Proj_Vermont_CS83, + + PCS_NAD83_Virginia_North, Proj_Virginia_CS83_North, + PCS_NAD83_Virginia_South, Proj_Virginia_CS83_South, + + PCS_NAD83_Washington_North, Proj_Washington_CS83_North, + PCS_NAD83_Washington_South, Proj_Washington_CS83_South, + + PCS_NAD83_West_Virginia_N, Proj_West_Virginia_CS83_North, + PCS_NAD83_West_Virginia_S, Proj_West_Virginia_CS83_South, + + PCS_NAD83_Wisconsin_North, Proj_Wisconsin_CS83_North, + PCS_NAD83_Wisconsin_Cen, Proj_Wisconsin_CS83_Central, + PCS_NAD83_Wisconsin_South, Proj_Wisconsin_CS83_South, + + PCS_NAD83_Wyoming_East, Proj_Wyoming_CS83_East, + PCS_NAD83_Wyoming_E_Cen, Proj_Wyoming_CS83_East_Central, + PCS_NAD83_Wyoming_W_Cen, Proj_Wyoming_CS83_West_Central, + PCS_NAD83_Wyoming_West, Proj_Wyoming_CS83_West, + + PCS_NAD83_Puerto_Rico_Virgin_Is, Proj_Puerto_Rico_Virgin_Is, + + PCS_NAD27_Alabama_East, Proj_Alabama_CS27_East, + PCS_NAD27_Alabama_West, Proj_Alabama_CS27_West, + + PCS_NAD27_Alaska_zone_1, Proj_Alaska_CS27_1, + PCS_NAD27_Alaska_zone_2, Proj_Alaska_CS27_2, + PCS_NAD27_Alaska_zone_3, Proj_Alaska_CS27_3, + PCS_NAD27_Alaska_zone_4, Proj_Alaska_CS27_4, + PCS_NAD27_Alaska_zone_5, Proj_Alaska_CS27_5, + PCS_NAD27_Alaska_zone_6, Proj_Alaska_CS27_6, + PCS_NAD27_Alaska_zone_7, Proj_Alaska_CS27_7, + PCS_NAD27_Alaska_zone_8, Proj_Alaska_CS27_8, + PCS_NAD27_Alaska_zone_9, Proj_Alaska_CS27_9, + PCS_NAD27_Alaska_zone_10, Proj_Alaska_CS27_10, + + PCS_NAD27_California_I, Proj_California_CS27_I, + PCS_NAD27_California_II, Proj_California_CS27_II, + PCS_NAD27_California_III, Proj_California_CS27_III, + PCS_NAD27_California_IV, Proj_California_CS27_IV, + PCS_NAD27_California_V, Proj_California_CS27_V, + PCS_NAD27_California_VI, Proj_California_CS27_VI, + PCS_NAD27_California_VII, Proj_California_CS27_VII, + + PCS_NAD27_Arizona_East, Proj_Arizona_Coordinate_System_east, + PCS_NAD27_Arizona_Central, Proj_Arizona_Coordinate_System_Central, + PCS_NAD27_Arizona_West, Proj_Arizona_Coordinate_System_west, + + PCS_NAD27_Arkansas_North, Proj_Arkansas_CS27_North, + PCS_NAD27_Arkansas_South, Proj_Arkansas_CS27_South, + + PCS_NAD27_Colorado_North, Proj_Colorado_CS27_North, + PCS_NAD27_Colorado_Central, Proj_Colorado_CS27_Central, + PCS_NAD27_Colorado_South, Proj_Colorado_CS27_South, + + PCS_NAD27_Connecticut, Proj_Connecticut_CS27, + + PCS_NAD27_Delaware, Proj_Delaware_CS27, + + PCS_NAD27_Florida_East, Proj_Florida_CS27_East, + PCS_NAD27_Florida_North, Proj_Florida_CS27_North, + PCS_NAD27_Florida_West, Proj_Florida_CS27_West, + + PCS_NAD27_Hawaii_zone_1, Proj_Hawaii_CS27_1, + PCS_NAD27_Hawaii_zone_2, Proj_Hawaii_CS27_2, + PCS_NAD27_Hawaii_zone_3, Proj_Hawaii_CS27_3, + PCS_NAD27_Hawaii_zone_4, Proj_Hawaii_CS27_4, + PCS_NAD27_Hawaii_zone_5, Proj_Hawaii_CS27_5, + + PCS_NAD27_Georgia_East, Proj_Georgia_CS27_East, + PCS_NAD27_Georgia_West, Proj_Georgia_CS27_West, + + PCS_NAD27_Idaho_East, Proj_Idaho_CS27_East, + PCS_NAD27_Idaho_Central, Proj_Idaho_CS27_Central, + PCS_NAD27_Idaho_West, Proj_Idaho_CS27_West, + + PCS_NAD27_Illinois_East, Proj_Illinois_CS27_East, + PCS_NAD27_Illinois_West, Proj_Illinois_CS27_West, + + PCS_NAD27_Indiana_East, Proj_Indiana_CS27_East, + PCS_NAD27_Indiana_West, Proj_Indiana_CS27_West, + + PCS_NAD27_Iowa_North, Proj_Iowa_CS27_North, + PCS_NAD27_Iowa_South, Proj_Iowa_CS27_South, + + PCS_NAD27_Kansas_North, Proj_Kansas_CS27_North, + PCS_NAD27_Kansas_South, Proj_Kansas_CS27_South, + + PCS_NAD27_Kentucky_North, Proj_Kentucky_CS27_North, + PCS_NAD27_Kentucky_South, Proj_Kentucky_CS27_South, + + PCS_NAD27_Louisiana_North, Proj_Louisiana_CS27_North, + PCS_NAD27_Louisiana_South, Proj_Louisiana_CS27_South, + + PCS_NAD27_Maine_East, Proj_Maine_CS27_East, + PCS_NAD27_Maine_West, Proj_Maine_CS27_West, + + PCS_NAD27_Maryland, Proj_Maryland_CS27, + + PCS_NAD27_Massachusetts, Proj_Massachusetts_CS27_Mainland, + PCS_NAD27_Massachusetts_Is, Proj_Massachusetts_CS27_Island, + + PCS_NAD27_Michigan_North, Proj_Michigan_CS27_North, + PCS_NAD27_Michigan_Central, Proj_Michigan_CS27_Central, + PCS_NAD27_Michigan_South, Proj_Michigan_CS27_South, + + PCS_NAD27_Minnesota_North, Proj_Minnesota_CS27_North, + PCS_NAD27_Minnesota_Cent, Proj_Minnesota_CS27_Central, + PCS_NAD27_Minnesota_South, Proj_Minnesota_CS27_South, + + PCS_NAD27_Mississippi_East, Proj_Mississippi_CS27_East, + PCS_NAD27_Mississippi_West, Proj_Mississippi_CS27_West, + + PCS_NAD27_Missouri_East, Proj_Missouri_CS27_East, + PCS_NAD27_Missouri_Central, Proj_Missouri_CS27_Central, + PCS_NAD27_Missouri_West, Proj_Missouri_CS27_West, + + PCS_NAD27_Montana_North, Proj_Montana_CS27_North, + PCS_NAD27_Montana_Central, Proj_Montana_CS27_Central, + PCS_NAD27_Montana_South, Proj_Montana_CS27_South, + + PCS_NAD27_Nebraska_North, Proj_Nebraska_CS27_North, + PCS_NAD27_Nebraska_South, Proj_Nebraska_CS27_South, + + PCS_NAD27_Nevada_East, Proj_Nevada_CS27_East, + PCS_NAD27_Nevada_Central, Proj_Nevada_CS27_Central, + PCS_NAD27_Nevada_West, Proj_Nevada_CS27_West, + + PCS_NAD27_New_Hampshire, Proj_New_Hampshire_CS27, + + PCS_NAD27_New_Jersey, Proj_New_Jersey_CS27, + + PCS_NAD27_New_Mexico_East, Proj_New_Mexico_CS27_East, + PCS_NAD27_New_Mexico_Cent, Proj_New_Mexico_CS27_Central, + PCS_NAD27_New_Mexico_West, Proj_New_Mexico_CS27_West, + + PCS_NAD27_New_York_East, Proj_New_York_CS27_East, + PCS_NAD27_New_York_Central, Proj_New_York_CS27_Central, + PCS_NAD27_New_York_West, Proj_New_York_CS27_West, + PCS_NAD27_New_York_Long_Is, Proj_New_York_CS27_Long_Island, + + PCS_NAD27_North_Carolina, Proj_North_Carolina_CS27, + + PCS_NAD27_North_Dakota_N, Proj_North_Dakota_CS27_North, + PCS_NAD27_North_Dakota_S, Proj_North_Dakota_CS27_South, + + PCS_NAD27_Ohio_North, Proj_Ohio_CS27_North, + PCS_NAD27_Ohio_South, Proj_Ohio_CS27_South, + + PCS_NAD27_Oklahoma_North, Proj_Oklahoma_CS27_North, + PCS_NAD27_Oklahoma_South, Proj_Oklahoma_CS27_South, + + PCS_NAD27_Oregon_North, Proj_Oregon_CS27_North, + PCS_NAD27_Oregon_South, Proj_Oregon_CS27_South, + + PCS_NAD27_Pennsylvania_N, Proj_Pennsylvania_CS27_North, + PCS_NAD27_Pennsylvania_S, Proj_Pennsylvania_CS27_South, + + PCS_NAD27_Rhode_Island, Proj_Rhode_Island_CS27, + + PCS_NAD27_South_Carolina_N, Proj_South_Carolina_CS27_North, + PCS_NAD27_South_Carolina_S, Proj_South_Carolina_CS27_South, + + PCS_NAD27_South_Dakota_N, Proj_South_Dakota_CS27_North, + PCS_NAD27_South_Dakota_S, Proj_South_Dakota_CS27_South, + + PCS_NAD27_Tennessee, Proj_Tennessee_CS27, + + PCS_NAD27_Texas_North, Proj_Texas_CS27_North, + PCS_NAD27_Texas_North_Cen, Proj_Texas_CS27_North_Central, + PCS_NAD27_Texas_Central, Proj_Texas_CS27_Central, + PCS_NAD27_Texas_South_Cen, Proj_Texas_CS27_South_Central, + PCS_NAD27_Texas_South, Proj_Texas_CS27_South, + + PCS_NAD27_Utah_North, Proj_Utah_CS27_North, + PCS_NAD27_Utah_Central, Proj_Utah_CS27_Central, + PCS_NAD27_Utah_South, Proj_Utah_CS27_South, + + PCS_NAD27_Vermont, Proj_Vermont_CS27, + + PCS_NAD27_Virginia_North, Proj_Virginia_CS27_North, + PCS_NAD27_Virginia_South, Proj_Virginia_CS27_South, + + PCS_NAD27_Washington_North, Proj_Washington_CS27_North, + PCS_NAD27_Washington_South, Proj_Washington_CS27_South, + + PCS_NAD27_West_Virginia_N, Proj_West_Virginia_CS27_North, + PCS_NAD27_West_Virginia_S, Proj_West_Virginia_CS27_South, + + PCS_NAD27_Wisconsin_North, Proj_Wisconsin_CS27_North, + PCS_NAD27_Wisconsin_Cen, Proj_Wisconsin_CS27_Central, + PCS_NAD27_Wisconsin_South, Proj_Wisconsin_CS27_South, + + PCS_NAD27_Wyoming_East, Proj_Wyoming_CS27_East, + PCS_NAD27_Wyoming_E_Cen, Proj_Wyoming_CS27_East_Central, + PCS_NAD27_Wyoming_W_Cen, Proj_Wyoming_CS27_West_Central, + PCS_NAD27_Wyoming_West, Proj_Wyoming_CS27_West, + + PCS_NAD27_Puerto_Rico, Proj_Puerto_Rico_CS27, + + KvUserDefined +}; + +/************************************************************************/ +/* GTIFMapSysToPCS() */ +/* */ +/* Given a Datum, MapSys and zone value generate the best PCS */ +/* code possible. */ +/************************************************************************/ + +int GTIFMapSysToPCS( int MapSys, int Datum, int nZone ) + +{ + int PCSCode = KvUserDefined; + + if( MapSys == MapSys_UTM_North ) + { + if( Datum == GCS_NAD27 ) + PCSCode = PCS_NAD27_UTM_zone_3N + nZone - 3; + else if( Datum == GCS_NAD83 ) + PCSCode = PCS_NAD83_UTM_zone_3N + nZone - 3; + else if( Datum == GCS_WGS_72 ) + PCSCode = PCS_WGS72_UTM_zone_1N + nZone - 1; + else if( Datum == GCS_WGS_72BE ) + PCSCode = PCS_WGS72BE_UTM_zone_1N + nZone - 1; + else if( Datum == GCS_WGS_84 ) + PCSCode = PCS_WGS84_UTM_zone_1N + nZone - 1; + } + else if( MapSys == MapSys_UTM_South ) + { + if( Datum == GCS_WGS_72 ) + PCSCode = PCS_WGS72_UTM_zone_1S + nZone - 1; + else if( Datum == GCS_WGS_72BE ) + PCSCode = PCS_WGS72BE_UTM_zone_1S + nZone - 1; + else if( Datum == GCS_WGS_84 ) + PCSCode = PCS_WGS84_UTM_zone_1S + nZone - 1; + } + else if( MapSys == MapSys_State_Plane_27 ) + { + int i; + + PCSCode = 10000 + nZone; + for( i = 0; StatePlaneTable[i] != KvUserDefined; i += 2 ) + { + if( StatePlaneTable[i+1] == PCSCode ) + PCSCode = StatePlaneTable[i]; + } + + /* Old EPSG code was in error for Tennesse CS27, override */ + if( nZone == 4100 ) + PCSCode = 2204; + } + else if( MapSys == MapSys_State_Plane_83 ) + { + int i; + + PCSCode = 10000 + nZone + 30; + + for( i = 0; StatePlaneTable[i] != KvUserDefined; i += 2 ) + { + if( StatePlaneTable[i+1] == PCSCode ) + PCSCode = StatePlaneTable[i]; + } + + /* Old EPSG code was in error for Kentucky North CS83, override */ + if( nZone == 1601 ) + PCSCode = 2205; + } + + return( PCSCode ); +} + +/************************************************************************/ +/* GTIFMapSysToProj() */ +/* */ +/* Given a MapSys and zone value generate the best Proj_ */ +/* code possible. */ +/************************************************************************/ + +int GTIFMapSysToProj( int MapSys, int nZone ) + +{ + int ProjCode = KvUserDefined; + + if( MapSys == MapSys_UTM_North ) + { + ProjCode = Proj_UTM_zone_1N + nZone - 1; + } + else if( MapSys == MapSys_UTM_South ) + { + ProjCode = Proj_UTM_zone_1S + nZone - 1; + } + else if( MapSys == MapSys_State_Plane_27 ) + { + ProjCode = 10000 + nZone; + + /* Tennesse override */ + if( nZone == 4100 ) + ProjCode = 15302; + } + else if( MapSys == MapSys_State_Plane_83 ) + { + ProjCode = 10000 + nZone + 30; + + /* Kentucky North override */ + if( nZone == 1601 ) + ProjCode = 15303; + } + + return( ProjCode ); +} + +/************************************************************************/ +/* GTIFPCSToMapSys() */ +/************************************************************************/ + +/** + * Translate a PCS_ code into a UTM or State Plane map system, a datum, + * and a zone if possible. + * + * @param PCSCode The projection code (PCS_*) as would be stored in the + * ProjectedCSTypeGeoKey of a GeoTIFF file. + * + * @param pDatum Pointer to an integer into which the datum code (GCS_*) + * is put if the function succeeds. + * + * @param pZone Pointer to an integer into which the zone will be placed + * if the function is successful. + * + * @return Returns either MapSys_UTM_North, MapSys_UTM_South, + * MapSys_State_Plane_83, MapSys_State_Plane_27 or KvUserDefined. + * KvUserDefined indicates that the + * function failed to recognise the projection as UTM or State Plane. + * + * The zone value is only set if the return code is other than KvUserDefined. + * For utm map system the returned zone will be between 1 and 60. For + * State Plane, the USGS state plane zone number is returned. For instance, + * Alabama East is zone 101. + * + * The datum (really this is the GCS) is set to a GCS_ value such as GCS_NAD27. + * + * This function is useful to recognise (most) UTM and State Plane coordinate + * systems, even if CSV files aren't available to translate them automatically. + * It is used as a fallback mechanism by GTIFGetDefn() for normalization when + * CSV files aren't found. + */ + +int GTIFPCSToMapSys( int PCSCode, int * pDatum, int * pZone ) + +{ + int Datum = KvUserDefined, Proj = KvUserDefined; + int nZone = KvUserDefined, i; + +/* -------------------------------------------------------------------- */ +/* UTM with various datums. Note there are lots of PCS UTM */ +/* codes not done yet which use strange datums. */ +/* -------------------------------------------------------------------- */ + if( PCSCode >= PCS_NAD27_UTM_zone_3N && PCSCode <= PCS_NAD27_UTM_zone_22N ) + { + Datum = GCS_NAD27; + Proj = MapSys_UTM_North; + nZone = PCSCode - PCS_NAD27_UTM_zone_3N + 3; + } + else if( PCSCode >= PCS_NAD83_UTM_zone_3N + && PCSCode <= PCS_NAD83_UTM_zone_23N ) + { + Datum = GCS_NAD83; + Proj = MapSys_UTM_North; + nZone = PCSCode - PCS_NAD83_UTM_zone_3N + 3; + } + + else if( PCSCode >= PCS_WGS72_UTM_zone_1N + && PCSCode <= PCS_WGS72_UTM_zone_60N ) + { + Datum = GCS_WGS_72; + Proj = MapSys_UTM_North; + nZone = PCSCode - PCS_WGS72_UTM_zone_1N + 1; + } + else if( PCSCode >= PCS_WGS72_UTM_zone_1S + && PCSCode <= PCS_WGS72_UTM_zone_60S ) + { + Datum = GCS_WGS_72; + Proj = MapSys_UTM_South; + nZone = PCSCode - PCS_WGS72_UTM_zone_1S + 1; + } + + else if( PCSCode >= PCS_WGS72BE_UTM_zone_1N + && PCSCode <= PCS_WGS72BE_UTM_zone_60N ) + { + Datum = GCS_WGS_72BE; + Proj = MapSys_UTM_North; + nZone = PCSCode - PCS_WGS72BE_UTM_zone_1N + 1; + } + else if( PCSCode >= PCS_WGS72BE_UTM_zone_1S + && PCSCode <= PCS_WGS72BE_UTM_zone_60S ) + { + Datum = GCS_WGS_72BE; + Proj = MapSys_UTM_South; + nZone = PCSCode - PCS_WGS72BE_UTM_zone_1S + 1; + } + + else if( PCSCode >= PCS_WGS84_UTM_zone_1N + && PCSCode <= PCS_WGS84_UTM_zone_60N ) + { + Datum = GCS_WGS_84; + Proj = MapSys_UTM_North; + nZone = PCSCode - PCS_WGS84_UTM_zone_1N + 1; + } + else if( PCSCode >= PCS_WGS84_UTM_zone_1S + && PCSCode <= PCS_WGS84_UTM_zone_60S ) + { + Datum = GCS_WGS_84; + Proj = MapSys_UTM_South; + nZone = PCSCode - PCS_WGS84_UTM_zone_1S + 1; + } + else if( PCSCode >= PCS_SAD69_UTM_zone_18N + && PCSCode <= PCS_SAD69_UTM_zone_22N ) + { + Datum = KvUserDefined; + Proj = MapSys_UTM_North; + nZone = PCSCode - PCS_SAD69_UTM_zone_18N + 18; + } + else if( PCSCode >= PCS_SAD69_UTM_zone_17S + && PCSCode <= PCS_SAD69_UTM_zone_25S ) + { + Datum = KvUserDefined; + Proj = MapSys_UTM_South; + nZone = PCSCode - PCS_SAD69_UTM_zone_17S + 17; + } + +/* -------------------------------------------------------------------- */ +/* State Plane zones, first we translate any PCS_ codes to */ +/* a Proj_ code that we can get a handle on. */ +/* -------------------------------------------------------------------- */ + for( i = 0; StatePlaneTable[i] != KvUserDefined; i += 2 ) + { + if( StatePlaneTable[i] == PCSCode ) + PCSCode = StatePlaneTable[i+1]; + } + + if( PCSCode <= 15900 && PCSCode >= 10000 ) + { + if( (PCSCode % 100) >= 30 ) + { + Proj = MapSys_State_Plane_83; + Datum = GCS_NAD83; + } + else + { + Proj = MapSys_State_Plane_27; + Datum = GCS_NAD27; + } + + nZone = PCSCode - 10000; + if( Datum == GCS_NAD83 ) + nZone -= 30; + } + + if( pDatum != NULL ) + *pDatum = Datum; + + if( pZone != NULL ) + *pZone = nZone; + + return( Proj ); +} + +/************************************************************************/ +/* GTIFProjToMapSys() */ +/************************************************************************/ + +/** + * Translate a Proj_ code into a UTM or State Plane map system, and a zone + * if possible. + * + * @param ProjCode The projection code (Proj_*) as would be stored in the + * ProjectionGeoKey of a GeoTIFF file. + * @param pZone Pointer to an integer into which the zone will be placed + * if the function is successful. + * + * @return Returns either MapSys_UTM_North, MapSys_UTM_South, + * MapSys_State_Plane_27, MapSys_State_Plane_83 or KvUserDefined. + * KvUserDefined indicates that the + * function failed to recognise the projection as UTM or State Plane. + * + * The zone value is only set if the return code is other than KvUserDefined. + * For utm map system the returned zone will be between 1 and 60. For + * State Plane, the USGS state plane zone number is returned. For instance, + * Alabama East is zone 101. + * + * This function is useful to recognise UTM and State Plane coordinate + * systems, and to extract zone numbers so the projections can be + * represented as UTM rather than as the underlying projection method such + * Transverse Mercator for instance. + */ + +int GTIFProjToMapSys( int ProjCode, int * pZone ) + +{ + int nZone = KvUserDefined; + int MapSys = KvUserDefined; + +/* -------------------------------------------------------------------- */ +/* Handle UTM. */ +/* -------------------------------------------------------------------- */ + if( ProjCode >= Proj_UTM_zone_1N && ProjCode <= Proj_UTM_zone_60N ) + { + MapSys = MapSys_UTM_North; + nZone = ProjCode - Proj_UTM_zone_1N + 1; + } + else if( ProjCode >= Proj_UTM_zone_1S && ProjCode <= Proj_UTM_zone_60S ) + { + MapSys = MapSys_UTM_South; + nZone = ProjCode - Proj_UTM_zone_1S + 1; + } + +/* -------------------------------------------------------------------- */ +/* Handle State Plane. I think there are some anomolies in */ +/* here, so this is a bit risky. */ +/* -------------------------------------------------------------------- */ + else if( ProjCode >= 10101 && ProjCode <= 15299 ) + { + if( ProjCode % 100 >= 30 ) + { + MapSys = MapSys_State_Plane_83; + nZone = ProjCode - 10000 - 30; + } + else + { + MapSys = MapSys_State_Plane_27; + nZone = ProjCode - 10000; + } + } + + if( pZone != NULL ) + *pZone = nZone; + + return( MapSys ); +} + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_free.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_free.c new file mode 100644 index 000000000..a43fcad35 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_free.c @@ -0,0 +1,62 @@ +/********************************************************************** + * + * geo_free.c -- Public routines for GEOTIFF GeoKey access. + * + * Written By: Niles D. Ritter. + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + * + **********************************************************************/ + +#include "geotiff.h" /* public interface */ +#include "geo_tiffp.h" /* external TIFF interface */ +#include "geo_keyp.h" /* private interface */ + + +/********************************************************************** + * + * Public Routines + * + **********************************************************************/ + +/** + +This function deallocates an existing GeoTIFF access handle previously +created with GTIFNew(). If the handle was +used to write GeoTIFF keys to the TIFF file, the +GTIFWriteKeys() function should be used +to flush results to the file before calling GTIFFree(). GTIFFree() +should be called before XTIFFClose() is +called on the corresponding TIFF file handle.

+ +*/ + +void GTIFFree(GTIF* gtif) +{ + int i; + + if (!gtif) return; + + /* Free parameter arrays */ + if (gtif->gt_double) _GTIFFree (gtif->gt_double); + if (gtif->gt_short) _GTIFFree (gtif->gt_short); + + /* Free GeoKey arrays */ + if (gtif->gt_keys) + { + for (i = 0; i < MAX_KEYS; i++) + { + if (gtif->gt_keys[i].gk_type == TYPE_ASCII) + { + _GTIFFree (gtif->gt_keys[i].gk_data); + } + } + _GTIFFree (gtif->gt_keys); + } + if (gtif->gt_keyindex) _GTIFFree (gtif->gt_keyindex); + + _GTIFFree (gtif); +} diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_get.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_get.c new file mode 100644 index 000000000..f027bd3aa --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_get.c @@ -0,0 +1,176 @@ +/********************************************************************** + * + * geo_get.c -- Public routines for GEOTIFF GeoKey access. + * + * Written By: Niles D. Ritter. + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + * + * Revision History; + * + * 20 June, 1995 Niles D. Ritter New + * 3 July, 1995 Greg Martin Fix strings and index + * 6 July, 1995 Niles D. Ritter Unfix indexing. + * + **********************************************************************/ + +#include "geotiff.h" /* public interface */ +#include "geo_tiffp.h" /* external TIFF interface */ +#include "geo_keyp.h" /* private interface */ + +/* return the Header info of this geotiff file */ + +void GTIFDirectoryInfo(GTIF *gtif, int version[3], int *keycount) +{ + if (version) + { + version[0] = gtif->gt_version; + version[1] = gtif->gt_rev_major; + version[2] = gtif->gt_rev_minor; + } + if (keycount) *keycount = gtif->gt_num_keys; +} + + +int GTIFKeyInfo(GTIF *gtif, geokey_t key, int *size, tagtype_t* type) +{ + int index = gtif->gt_keyindex[ key ]; + GeoKey *keyptr; + + if (!index) return 0; + + keyptr = gtif->gt_keys + index; + if (size) *size = (int) keyptr->gk_size; + if (type) *type = keyptr->gk_type; + + return keyptr->gk_count; +} + +/** + +This function reads the value of a single GeoKey from a GeoTIFF file. + +@param gtif The geotiff information handle from GTIFNew(). + +@param thekey The geokey_t name (such as ProjectedCSTypeGeoKey). +This must come from the list of legal geokey_t values +(an enumeration) listed below. + +@param val The val argument is a pointer to the +variable into which the value should be read. The type of the variable +varies depending on the geokey_t given. While there is no ready mapping +of geokey_t values onto types, in general code values are of type short, +citations are strings, and everything else is of type double. Note +that pointer's to int should never be passed to GTIFKeyGet() for +integer values as they will be shorts, and the int's may not be properly +initialized (and will be grossly wrong on MSB systems). + +@param index Indicates how far into the list of values +for this geokey to offset. Should normally be zero. + +@param count Indicates how many values +to read. At this time all keys except for strings have only one value, +so index should be zero, and count should be one. + +@return The GTIFKeyGet() function returns the number of values read. Normally +this would be one if successful or zero if the key doesn't exist for this +file. + +From geokeys.inc we see the following geokey_t values are possible:

+ +

+-- 6.2.1 GeoTIFF Configuration Keys --
+
+ValuePair(  GTModelTypeGeoKey,	1024) -- Section 6.3.1.1 Codes       --
+ValuePair(  GTRasterTypeGeoKey,	1025) -- Section 6.3.1.2 Codes       --
+ValuePair(  GTCitationGeoKey,	1026) -- documentation --
+
+-- 6.2.2 Geographic CS Parameter Keys --
+
+ValuePair(  GeographicTypeGeoKey,	2048) -- Section 6.3.2.1 Codes     --
+ValuePair(  GeogCitationGeoKey,	2049) -- documentation             --
+ValuePair(  GeogGeodeticDatumGeoKey,	2050) -- Section 6.3.2.2 Codes     --
+ValuePair(  GeogPrimeMeridianGeoKey,	2051) -- Section 6.3.2.4 codes     --
+ValuePair(  GeogLinearUnitsGeoKey,	2052) -- Section 6.3.1.3 Codes     --
+ValuePair(  GeogLinearUnitSizeGeoKey,	2053) -- meters                    --
+ValuePair(  GeogAngularUnitsGeoKey,	2054) -- Section 6.3.1.4 Codes     --
+ValuePair(  GeogAngularUnitSizeGeoKey,	2055) -- radians                   --
+ValuePair(  GeogEllipsoidGeoKey,	2056) -- Section 6.3.2.3 Codes     --
+ValuePair(  GeogSemiMajorAxisGeoKey,	2057) -- GeogLinearUnits           --
+ValuePair(  GeogSemiMinorAxisGeoKey,	2058) -- GeogLinearUnits           --
+ValuePair(  GeogInvFlatteningGeoKey,	2059) -- ratio                     --
+ValuePair(  GeogAzimuthUnitsGeoKey,	2060) -- Section 6.3.1.4 Codes     --
+ValuePair(  GeogPrimeMeridianLongGeoKey,	2061) -- GeoAngularUnit            --
+
+-- 6.2.3 Projected CS Parameter Keys --
+--    Several keys have been renamed,--
+--    and the deprecated names aliased for backward compatibility --
+
+ValuePair(  ProjectedCSTypeGeoKey,	3072)     -- Section 6.3.3.1 codes   --
+ValuePair(  PCSCitationGeoKey,	3073)     -- documentation           --
+ValuePair(  ProjectionGeoKey,	3074)     -- Section 6.3.3.2 codes   --
+ValuePair(  ProjCoordTransGeoKey,	3075)     -- Section 6.3.3.3 codes   --
+ValuePair(  ProjLinearUnitsGeoKey,	3076)     -- Section 6.3.1.3 codes   --
+ValuePair(  ProjLinearUnitSizeGeoKey,	3077)     -- meters                  --
+ValuePair(  ProjStdParallel1GeoKey,	3078)     -- GeogAngularUnit --
+ValuePair(  ProjStdParallelGeoKey,ProjStdParallel1GeoKey) -- ** alias **   --
+ValuePair(  ProjStdParallel2GeoKey,	3079)     -- GeogAngularUnit --
+ValuePair(  ProjNatOriginLongGeoKey,	3080)     -- GeogAngularUnit --
+ValuePair(  ProjOriginLongGeoKey,ProjNatOriginLongGeoKey) -- ** alias **     --
+ValuePair(  ProjNatOriginLatGeoKey,	3081)     -- GeogAngularUnit --
+ValuePair(  ProjOriginLatGeoKey,ProjNatOriginLatGeoKey)   -- ** alias **     --
+ValuePair(  ProjFalseEastingGeoKey,	3082)     -- ProjLinearUnits --
+ValuePair(  ProjFalseNorthingGeoKey,	3083)     -- ProjLinearUnits --
+ValuePair(  ProjFalseOriginLongGeoKey,	3084)     -- GeogAngularUnit --
+ValuePair(  ProjFalseOriginLatGeoKey,	3085)     -- GeogAngularUnit --
+ValuePair(  ProjFalseOriginEastingGeoKey,	3086)     -- ProjLinearUnits --
+ValuePair(  ProjFalseOriginNorthingGeoKey,	3087)     -- ProjLinearUnits --
+ValuePair(  ProjCenterLongGeoKey,	3088)     -- GeogAngularUnit --
+ValuePair(  ProjCenterLatGeoKey,	3089)     -- GeogAngularUnit --
+ValuePair(  ProjCenterEastingGeoKey,	3090)     -- ProjLinearUnits --
+ValuePair(  ProjCenterNorthingGeoKey,	3091)     -- ProjLinearUnits --
+ValuePair(  ProjScaleAtNatOriginGeoKey,	3092)     -- ratio   --
+ValuePair(  ProjScaleAtOriginGeoKey,ProjScaleAtNatOriginGeoKey)  -- ** alias **   --
+ValuePair(  ProjScaleAtCenterGeoKey,	3093)     -- ratio   --
+ValuePair(  ProjAzimuthAngleGeoKey,	3094)     -- GeogAzimuthUnit --
+ValuePair(  ProjStraightVertPoleLongGeoKey,	3095)     -- GeogAngularUnit --
+
+ 6.2.4 Vertical CS Keys 
+   
+ValuePair(  VerticalCSTypeGeoKey,	4096)  -- Section 6.3.4.1 codes   --
+ValuePair(  VerticalCitationGeoKey,	4097)  -- documentation --
+ValuePair(  VerticalDatumGeoKey,	4098)  -- Section 6.3.4.2 codes   --
+ValuePair(  VerticalUnitsGeoKey,	4099)  -- Section 6.3.1 (.x) codes   --
+
+*/ + +int GTIFKeyGet(GTIF *gtif, geokey_t thekey, void *val, int index, int count) +{ + int kindex = gtif->gt_keyindex[ thekey ]; + GeoKey *key; + gsize_t size; + char *data; + tagtype_t type; + + if (!kindex) return 0; + + key = gtif->gt_keys+kindex; + if (!count) count = key->gk_count - index; + if (count <=0) return 0; + if (count > key->gk_count) count = key->gk_count; + size = key->gk_size; + type = key->gk_type; + + if (count==1 && type==TYPE_SHORT) data = (char *)&key->gk_data; + else data = key->gk_data; + + _GTIFmemcpy( val, data + index*size, count*size ); + + if (type==TYPE_ASCII) + ((char *)val)[count-1] = '\0'; /* replace last char with NULL */ + + return count; +} diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_keyp.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_keyp.h new file mode 100644 index 000000000..01091de8d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_keyp.h @@ -0,0 +1,102 @@ +/********************************************************************** + * + * geo_keyp.h - private interface for GeoTIFF geokey tag parsing + * + * Written by: Niles D. Ritter + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + **********************************************************************/ + +#ifndef __geo_keyp_h_ +#define __geo_keyp_h_ + +#include /* for size_t */ + +/* + * This structure contains the internal program + * representation of the key entry. + */ +struct GeoKey { + int gk_key; /* GeoKey ID */ + size_t gk_size; /* data byte size */ + tagtype_t gk_type; /* TIFF data type */ + long gk_count; /* number of values */ + char* gk_data; /* pointer to data, or value */ +}; +typedef struct GeoKey GeoKey; + +/* + * This structure represents the file-organization of + * the key entry. Note that it assumes that short entries + * are aligned along 2-byte boundaries. + */ +struct KeyEntry { + pinfo_t ent_key; /* GeoKey ID */ + pinfo_t ent_location; /* TIFF Tag ID or 0 */ + pinfo_t ent_count; /* GeoKey value count */ + pinfo_t ent_val_offset; /* value or tag offset */ +}; +typedef struct KeyEntry KeyEntry; + +/* + * This is the header of the CoordSystemInfoTag. The 'Version' + * will only change if the CoorSystemInfoTag structure changes; + * The Major Revision will be incremented whenever a new set of + * Keys is added or changed, while the Minor revision will be + * incremented when only the set of Key-values is increased. + */ +struct KeyHeader{ + pinfo_t hdr_version; /* GeoTIFF Version */ + pinfo_t hdr_rev_major; /* GeoKey Major Revision # */ + pinfo_t hdr_rev_minor; /* GeoKey Minor Revision # */ + pinfo_t hdr_num_keys; /* Number of GeoKeys */ +}; +typedef struct KeyHeader KeyHeader; + +/* + * This structure holds temporary data while reading or writing + * the tags. + */ +struct TempKeyData { + char *tk_asciiParams; + int tk_asciiParamsLength; + int tk_asciiParamsOffset; +}; +typedef struct TempKeyData TempKeyData; + + +struct gtiff { + tiff_t* gt_tif; /* TIFF file descriptor */ + struct _TIFFMethod gt_methods; /* TIFF i/o methods */ + int gt_flags; /* file flags */ + + pinfo_t gt_version; /* GeoTIFF Version */ + pinfo_t gt_rev_major;/* GeoKey Key Revision */ + pinfo_t gt_rev_minor;/* GeoKey Code Revision */ + + int gt_num_keys; /* number of keys */ + GeoKey* gt_keys; /* array of keys */ + int* gt_keyindex; /* index of a key, if set*/ + int gt_keymin; /* smallest key set */ + int gt_keymax; /* largest key set */ + + pinfo_t* gt_short; /* array of SHORT vals */ + double* gt_double; /* array of DOUBLE vals */ + int gt_nshorts; /* number of SHORT vals */ + int gt_ndoubles; /* number of DOUBLE vals */ +}; + +typedef enum { + FLAG_FILE_OPEN=1, + FLAG_FILE_MODIFIED=2 +} gtiff_flags; + +#define MAX_KEYINDEX 65535 /* largest possible key */ +#define MAX_KEYS 100 /* maximum keys in a file */ +#define MAX_VALUES 1000 /* maximum values in a tag */ + +#endif /* __geo_keyp_h_ */ + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_names.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_names.c new file mode 100644 index 000000000..4c73f493f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_names.c @@ -0,0 +1,183 @@ +/* + * geo_names.c + * + * This encapsulates all of the value-naming mechanism of + * libgeotiff. + * + * Written By: Niles Ritter + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + * + */ + +#include "geotiffio.h" +#include "geonames.h" +#include "geo_tiffp.h" /* for tag names */ + +static KeyInfo _formatInfo[] = { + {TYPE_BYTE, "Byte"}, + {TYPE_SHORT, "Short"}, + {TYPE_LONG, "Long"}, + {TYPE_RATIONAL,"Rational"}, + {TYPE_ASCII, "Ascii"}, + {TYPE_FLOAT, "Float"}, + {TYPE_DOUBLE, "Double"}, + {TYPE_SBYTE, "SignedByte"}, + {TYPE_SSHORT, "SignedShort"}, + {TYPE_SLONG, "SignedLong"}, + {TYPE_UNKNOWN, "Unknown"}, + END_LIST +}; + +static KeyInfo _tagInfo[] = { + {GTIFF_PIXELSCALE, "ModelPixelScaleTag"}, + {GTIFF_TRANSMATRIX, "ModelTransformationTag"}, + {GTIFF_TIEPOINTS, "ModelTiepointTag"}, + /* This alias maps the Intergraph symbol to the current tag */ + {GTIFF_TRANSMATRIX, "IntergraphMatrixTag"}, + END_LIST +}; + +static char *FindName(KeyInfo *info,int key) +{ + static char errmsg[80]; + + while (info->ki_key>=0 && info->ki_key != key) info++; + + if (info->ki_key<0) + { + sprintf(errmsg,"Unknown-%d", key ); + return errmsg; + } + return info->ki_name; +} + +char *GTIFKeyName(geokey_t key) +{ + return FindName( &_keyInfo[0],key); +} + +char *GTIFTypeName(tagtype_t type) +{ + return FindName( &_formatInfo[0],type); +} + +char *GTIFTagName(int tag) +{ + return FindName( &_tagInfo[0],tag); +} + +char *GTIFValueName(geokey_t key, int value) +{ + KeyInfo *info; + + switch (key) + { + /* All codes using linear/angular/whatever units */ + case GeogLinearUnitsGeoKey: + case ProjLinearUnitsGeoKey: + case GeogAngularUnitsGeoKey: + case GeogAzimuthUnitsGeoKey: + case VerticalUnitsGeoKey: + info=_geounitsValue; break; + + /* put other key-dependent lists here */ + case GTModelTypeGeoKey: info=_modeltypeValue; break; + case GTRasterTypeGeoKey: info=_rastertypeValue; break; + case GeographicTypeGeoKey: info=_geographicValue; break; + case GeogGeodeticDatumGeoKey: info=_geodeticdatumValue; break; + case GeogEllipsoidGeoKey: info=_ellipsoidValue; break; + case GeogPrimeMeridianGeoKey: info=_primemeridianValue; break; + case ProjectedCSTypeGeoKey: info=_pcstypeValue; break; + case ProjectionGeoKey: info=_projectionValue; break; + case ProjCoordTransGeoKey: info=_coordtransValue; break; + case VerticalCSTypeGeoKey: info=_vertcstypeValue; break; + case VerticalDatumGeoKey: info=_vdatumValue; break; + + /* And if all else fails... */ + default: info = _csdefaultValue;break; + } + + return FindName( info,value); +} + +/* + * Inverse Utilities (name->code) + */ + + +static int FindCode(KeyInfo *info,char *key) +{ + while (info->ki_key>=0 && strcmp(info->ki_name,key) ) info++; + + if (info->ki_key<0) + { + /* not a registered key; might be generic code */ + if (!strncmp(key,"Unknown-",8)) + { + int code=-1; + sscanf(key,"Unknown-%d",&code); + return code; + } + else return -1; + } + return info->ki_key; +} + +int GTIFKeyCode(char *key) +{ + return FindCode( &_keyInfo[0],key); +} + +int GTIFTypeCode(char *type) +{ + return FindCode( &_formatInfo[0],type); +} + +int GTIFTagCode(char *tag) +{ + return FindCode( &_tagInfo[0],tag); +} + + +/* + * The key must be determined with GTIFKeyCode() before + * the name can be encoded. + */ +int GTIFValueCode(geokey_t key, char *name) +{ + KeyInfo *info; + + switch (key) + { + /* All codes using linear/angular/whatever units */ + case GeogLinearUnitsGeoKey: + case ProjLinearUnitsGeoKey: + case GeogAngularUnitsGeoKey: + case GeogAzimuthUnitsGeoKey: + case VerticalUnitsGeoKey: + info=_geounitsValue; break; + + /* put other key-dependent lists here */ + case GTModelTypeGeoKey: info=_modeltypeValue; break; + case GTRasterTypeGeoKey: info=_rastertypeValue; break; + case GeographicTypeGeoKey: info=_geographicValue; break; + case GeogGeodeticDatumGeoKey: info=_geodeticdatumValue; break; + case GeogEllipsoidGeoKey: info=_ellipsoidValue; break; + case GeogPrimeMeridianGeoKey: info=_primemeridianValue; break; + case ProjectedCSTypeGeoKey: info=_pcstypeValue; break; + case ProjectionGeoKey: info=_projectionValue; break; + case ProjCoordTransGeoKey: info=_coordtransValue; break; + case VerticalCSTypeGeoKey: info=_vertcstypeValue; break; + case VerticalDatumGeoKey: info=_vdatumValue; break; + + /* And if all else fails... */ + default: info = _csdefaultValue;break; + } + + return FindCode( info,name); +} + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_new.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_new.c new file mode 100644 index 000000000..af02bee36 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_new.c @@ -0,0 +1,285 @@ +/********************************************************************** + * + * geo_new.c -- Public routines for GEOTIFF GeoKey access. + * + * Written By: Niles D. Ritter. + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + * + * 20 June, 1995 Niles D. Ritter New + * 7 July, 1995 Greg Martin Fix index + * + **********************************************************************/ + +#include "geotiffio.h" /* public interface */ +#include "geo_tiffp.h" /* external TIFF interface */ +#include "geo_keyp.h" /* private interface */ +#include "geo_simpletags.h" + +/* private local routines */ +static int ReadKey(GTIF* gt, TempKeyData* tempData, + KeyEntry* entptr, GeoKey* keyptr); + + +/********************************************************************** + * + * Public Routines + * + **********************************************************************/ + + +/** + * Given an open TIFF file, look for GTIF keys and + * values and return GTIF structure. + +This function creates a GeoTIFF information interpretation handle +(GTIF *) based on a passed in TIFF handle originally from +XTIFFOpen(). Even though the argument +(tif) is shown as type void *, it is really normally +of type TIFF *.

+ +The returned GTIF handle can be used to read or write GeoTIFF tags +using the various GTIF functions. The handle should be destroyed using +GTIFFree() before the file is closed with TIFFClose().

+ +If the file accessed has no GeoTIFF keys, an valid (but empty) GTIF is +still returned. GTIFNew() is used both for existing files being read, and +for new TIFF files that will have GeoTIFF tags written to them.

+ + */ + +GTIF* GTIFNew(void *tif) + +{ + TIFFMethod default_methods; + _GTIFSetDefaultTIFF( &default_methods ); + + return GTIFNewWithMethods( tif, &default_methods ); +} + +GTIF *GTIFNewSimpleTags( void *tif ) + +{ + TIFFMethod default_methods; + GTIFSetSimpleTagsMethods( &default_methods ); + + return GTIFNewWithMethods( tif, &default_methods ); +} + +/************************************************************************/ +/* GTIFNewWithMethods() */ +/* */ +/* Create a new geotiff, passing in the methods structure to */ +/* support not libtiff implementations without replacing the */ +/* default methods. */ +/************************************************************************/ + +GTIF* GTIFNewWithMethods(void *tif, TIFFMethod* methods) +{ + GTIF* gt=(GTIF*)0; + int count,bufcount,index; + GeoKey *keyptr; + pinfo_t *data; + KeyEntry *entptr; + KeyHeader *header; + TempKeyData tempData; + + memset( &tempData, 0, sizeof(tempData) ); + gt = (GTIF*)_GTIFcalloc( sizeof(GTIF)); + if (!gt) goto failure; + + /* install TIFF file and I/O methods */ + gt->gt_tif = (tiff_t *)tif; + memcpy( >->gt_methods, methods, sizeof(TIFFMethod) ); + + /* since this is an array, GTIF will allocate the memory */ + if ( tif == NULL + || !(gt->gt_methods.get)(tif, GTIFF_GEOKEYDIRECTORY, >->gt_nshorts, &data )) + { + /* No ProjectionInfo, create a blank one */ + data=(pinfo_t*)_GTIFcalloc((4+MAX_VALUES)*sizeof(pinfo_t)); + if (!data) goto failure; + header = (KeyHeader *)data; + header->hdr_version = GvCurrentVersion; + header->hdr_rev_major = GvCurrentRevision; + header->hdr_rev_minor = GvCurrentMinorRev; + gt->gt_nshorts=sizeof(KeyHeader)/sizeof(pinfo_t); + } + else + { + /* resize data array so it can be extended if needed */ + data = (pinfo_t*) _GTIFrealloc(data,(4+MAX_VALUES)*sizeof(pinfo_t)); + } + gt->gt_short = data; + header = (KeyHeader *)data; + + if (header->hdr_version > GvCurrentVersion) goto failure; + if (header->hdr_rev_major > GvCurrentRevision) + { + /* issue warning */ + } + + /* If we got here, then the geokey can be parsed */ + count = header->hdr_num_keys; + + if (count * sizeof(KeyEntry) >= (4 + MAX_VALUES) * sizeof(pinfo_t)) + goto failure; + + gt->gt_num_keys = count; + gt->gt_version = header->hdr_version; + gt->gt_rev_major = header->hdr_rev_major; + gt->gt_rev_minor = header->hdr_rev_minor; + + bufcount = count+MAX_KEYS; /* allow for expansion */ + + /* Get the PARAMS Tags, if any */ + if (tif == NULL + || !(gt->gt_methods.get)(tif, GTIFF_DOUBLEPARAMS, + >->gt_ndoubles, >->gt_double )) + { + gt->gt_double=(double*)_GTIFcalloc(MAX_VALUES*sizeof(double)); + if (!gt->gt_double) goto failure; + } + else + { + /* resize data array so it can be extended if needed */ + gt->gt_double = (double*) _GTIFrealloc(gt->gt_double, + (MAX_VALUES)*sizeof(double)); + } + if ( tif == NULL + || !(gt->gt_methods.get)(tif, GTIFF_ASCIIPARAMS, + &tempData.tk_asciiParamsLength, + &tempData.tk_asciiParams )) + { + tempData.tk_asciiParams = 0; + tempData.tk_asciiParamsLength = 0; + } + else + { + /* last NULL doesn't count; "|" used for delimiter */ + if( tempData.tk_asciiParamsLength > 0 + && tempData.tk_asciiParams[tempData.tk_asciiParamsLength-1] == '\0') + { + --tempData.tk_asciiParamsLength; + } + } + + /* allocate space for GeoKey array and its index */ + gt->gt_keys = (GeoKey *)_GTIFcalloc( sizeof(GeoKey)*bufcount); + if (!gt->gt_keys) goto failure; + gt->gt_keyindex = (int *)_GTIFcalloc( sizeof(int)*(MAX_KEYINDEX+1)); + if (!gt->gt_keyindex) goto failure; + + /* Loop to get all GeoKeys */ + entptr = ((KeyEntry *)data) + 1; + keyptr = gt->gt_keys; + gt->gt_keymin = MAX_KEYINDEX; + gt->gt_keymax = 0; + for (index=1; index<=count; index++,entptr++) + { + if (!ReadKey(gt, &tempData, entptr, ++keyptr)) + goto failure; + + /* Set up the index (start at 1, since 0=unset) */ + gt->gt_keyindex[entptr->ent_key] = index; + } + + if( tempData.tk_asciiParams != NULL ) + _GTIFFree( tempData.tk_asciiParams ); + + return gt; + + failure: + /* Notify of error */ + if( tempData.tk_asciiParams != NULL ) + _GTIFFree( tempData.tk_asciiParams ); + GTIFFree (gt); + return (GTIF *)0; +} + +/********************************************************************** + * + * Private Routines + * + **********************************************************************/ + +/* + * Given KeyEntry, read in the GeoKey value location and set up + * the Key structure, returning 0 if failure. + */ + +static int ReadKey(GTIF* gt, TempKeyData* tempData, + KeyEntry* entptr, GeoKey* keyptr) +{ + int offset,count; + + keyptr->gk_key = entptr->ent_key; + keyptr->gk_count = entptr->ent_count; + count = entptr->ent_count; + offset = entptr->ent_val_offset; + if (gt->gt_keymin > keyptr->gk_key) gt->gt_keymin=keyptr->gk_key; + if (gt->gt_keymax < keyptr->gk_key) gt->gt_keymax=keyptr->gk_key; + + if (entptr->ent_location) + keyptr->gk_type = (gt->gt_methods.type)(gt->gt_tif,entptr->ent_location); + else + keyptr->gk_type = (gt->gt_methods.type)(gt->gt_tif,GTIFF_GEOKEYDIRECTORY); + + switch (entptr->ent_location) + { + case GTIFF_LOCAL: + /* store value into data value */ + memcpy(&keyptr->gk_data, &(entptr->ent_val_offset), sizeof(pinfo_t)); + break; + case GTIFF_GEOKEYDIRECTORY: + keyptr->gk_data = (char *)(gt->gt_short+offset); + if (gt->gt_nshorts < offset+count) + gt->gt_nshorts = offset+count; + break; + case GTIFF_DOUBLEPARAMS: + keyptr->gk_data = (char *)(gt->gt_double+offset); + if (gt->gt_ndoubles < offset+count) + gt->gt_ndoubles = offset+count; + break; + case GTIFF_ASCIIPARAMS: + if( offset + count == tempData->tk_asciiParamsLength + 1 + && count > 0 ) + { + /* some vendors seem to feel they should not use the + terminating '|' char, but do include a terminating '\0' + which we lose in the low level reading code. + If this is the case, drop the extra character */ + count--; + } + else if (offset < tempData->tk_asciiParamsLength + && offset + count > tempData->tk_asciiParamsLength ) + { + count = tempData->tk_asciiParamsLength - offset; + /* issue warning... if we could */ + } + else if (offset + count > tempData->tk_asciiParamsLength) + return (0); + + keyptr->gk_count = MAX(1,count+1); + keyptr->gk_data = (char *) _GTIFcalloc (keyptr->gk_count); + + _GTIFmemcpy (keyptr->gk_data, + tempData->tk_asciiParams + offset, count); + if( keyptr->gk_data[MAX(0,count-1)] == '|' ) + { + keyptr->gk_data[MAX(0,count-1)] = '\0'; + keyptr->gk_count = count; + } + else + keyptr->gk_data[MAX(0,count)] = '\0'; + break; + default: + return 0; /* failure */ + } + keyptr->gk_size = _gtiff_size[keyptr->gk_type]; + + return 1; /* success */ +} diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_normalize.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_normalize.c new file mode 100644 index 000000000..f88b5af4d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_normalize.c @@ -0,0 +1,2849 @@ +/****************************************************************************** + * $Id: geo_normalize.c 2595 2014-12-27 22:59:32Z rouault $ + * + * Project: libgeotiff + * Purpose: Code to normalize PCS and other composite codes in a GeoTIFF file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "cpl_serv.h" +#include "geo_tiffp.h" +#include "geovalues.h" +#include "geo_normalize.h" + +#ifndef KvUserDefined +# define KvUserDefined 32767 +#endif + +#ifndef PI +# define PI 3.14159265358979323846 +#endif + +/* EPSG Codes for projection parameters. Unfortunately, these bear no + relationship to the GeoTIFF codes even though the names are so similar. */ + +#define EPSGNatOriginLat 8801 +#define EPSGNatOriginLong 8802 +#define EPSGNatOriginScaleFactor 8805 +#define EPSGFalseEasting 8806 +#define EPSGFalseNorthing 8807 +#define EPSGProjCenterLat 8811 +#define EPSGProjCenterLong 8812 +#define EPSGAzimuth 8813 +#define EPSGAngleRectifiedToSkewedGrid 8814 +#define EPSGInitialLineScaleFactor 8815 +#define EPSGProjCenterEasting 8816 +#define EPSGProjCenterNorthing 8817 +#define EPSGPseudoStdParallelLat 8818 +#define EPSGPseudoStdParallelScaleFactor 8819 +#define EPSGFalseOriginLat 8821 +#define EPSGFalseOriginLong 8822 +#define EPSGStdParallel1Lat 8823 +#define EPSGStdParallel2Lat 8824 +#define EPSGFalseOriginEasting 8826 +#define EPSGFalseOriginNorthing 8827 +#define EPSGSphericalOriginLat 8828 +#define EPSGSphericalOriginLong 8829 +#define EPSGInitialLongitude 8830 +#define EPSGZoneWidth 8831 +#define EPSGLatOfStdParallel 8832 +#define EPSGOriginLong 8833 +#define EPSGTopocentricOriginLat 8834 +#define EPSGTopocentricOriginLong 8835 +#define EPSGTopocentricOriginHeight 8836 + +#define CT_Ext_Mercator_2SP -CT_Mercator + +/************************************************************************/ +/* GTIFGetPCSInfo() */ +/************************************************************************/ + +int GTIFGetPCSInfo( int nPCSCode, char **ppszEPSGName, + short *pnProjOp, short *pnUOMLengthCode, + short *pnGeogCS ) + +{ + char **papszRecord; + char szSearchKey[24]; + const char *pszFilename; + int nDatum; + int nZone; + + int Proj = GTIFPCSToMapSys( nPCSCode, &nDatum, &nZone ); + if ((Proj == MapSys_UTM_North || Proj == MapSys_UTM_South) && + nDatum != KvUserDefined) + { + const char* pszDatumName = NULL; + switch (nDatum) + { + case GCS_NAD27: pszDatumName = "NAD27"; break; + case GCS_NAD83: pszDatumName = "NAD83"; break; + case GCS_WGS_72: pszDatumName = "WGS 72"; break; + case GCS_WGS_72BE: pszDatumName = "WGS 72BE"; break; + case GCS_WGS_84: pszDatumName = "WGS 84"; break; + default: break; + } + + if (pszDatumName) + { + if (ppszEPSGName) + { + char szEPSGName[64]; + sprintf(szEPSGName, "%s / UTM zone %d%c", + pszDatumName, nZone, (Proj == MapSys_UTM_North) ? 'N' : 'S'); + *ppszEPSGName = CPLStrdup(szEPSGName); + } + + if (pnProjOp) + *pnProjOp = (short) (((Proj == MapSys_UTM_North) ? Proj_UTM_zone_1N - 1 : Proj_UTM_zone_1S - 1) + nZone); + + if (pnUOMLengthCode) + *pnUOMLengthCode = 9001; /* Linear_Meter */ + + if (pnGeogCS) + *pnGeogCS = (short) nDatum; + + return TRUE; + } + } + +/* -------------------------------------------------------------------- */ +/* Search the pcs.override table for this PCS. */ +/* -------------------------------------------------------------------- */ + pszFilename = CSVFilename( "pcs.override.csv" ); + sprintf( szSearchKey, "%d", nPCSCode ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer ); + +/* -------------------------------------------------------------------- */ +/* If not found, search the EPSG PCS database. */ +/* -------------------------------------------------------------------- */ + if( papszRecord == NULL ) + { + pszFilename = CSVFilename( "pcs.csv" ); + + sprintf( szSearchKey, "%d", nPCSCode ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer ); + + if( papszRecord == NULL ) + { + static int bWarnedOrTried = FALSE; + if( !bWarnedOrTried ) + { + FILE* f = VSIFOpen(CSVFilename( "pcs.csv" ), "rb"); + if( f == NULL ) + CPLError(CE_Warning, CPLE_AppDefined, "Cannot find pcs.csv"); + else + VSIFClose(f); + bWarnedOrTried = TRUE; + } + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Get the name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszEPSGName != NULL ) + { + *ppszEPSGName = + CPLStrdup( CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "COORD_REF_SYS_NAME") )); + } + +/* -------------------------------------------------------------------- */ +/* Get the UOM Length code, if requested. */ +/* -------------------------------------------------------------------- */ + if( pnUOMLengthCode != NULL ) + { + const char *pszValue; + + pszValue = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"UOM_CODE")); + if( atoi(pszValue) > 0 ) + *pnUOMLengthCode = (short) atoi(pszValue); + else + *pnUOMLengthCode = KvUserDefined; + } + +/* -------------------------------------------------------------------- */ +/* Get the UOM Length code, if requested. */ +/* -------------------------------------------------------------------- */ + if( pnProjOp != NULL ) + { + const char *pszValue; + + pszValue = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"COORD_OP_CODE")); + if( atoi(pszValue) > 0 ) + *pnProjOp = (short) atoi(pszValue); + else + *pnUOMLengthCode = KvUserDefined; + } + +/* -------------------------------------------------------------------- */ +/* Get the GeogCS (Datum with PM) code, if requested. */ +/* -------------------------------------------------------------------- */ + if( pnGeogCS != NULL ) + { + const char *pszValue; + + pszValue = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"SOURCE_GEOGCRS_CODE")); + if( atoi(pszValue) > 0 ) + *pnGeogCS = (short) atoi(pszValue); + else + *pnGeogCS = KvUserDefined; + } + + return TRUE; +} + +/************************************************************************/ +/* GTIFAngleToDD() */ +/* */ +/* Convert a numeric angle to decimal degress. */ +/************************************************************************/ + +double GTIFAngleToDD( double dfAngle, int nUOMAngle ) + +{ + if( nUOMAngle == 9110 ) /* DDD.MMSSsss */ + { + char szAngleString[32]; + + sprintf( szAngleString, "%12.7f", dfAngle ); + dfAngle = GTIFAngleStringToDD( szAngleString, nUOMAngle ); + } + else if ( nUOMAngle != KvUserDefined ) + { + double dfInDegrees = 1.0; + + GTIFGetUOMAngleInfo( nUOMAngle, NULL, &dfInDegrees ); + dfAngle = dfAngle * dfInDegrees; + } + + return( dfAngle ); +} + +/************************************************************************/ +/* GTIFAngleStringToDD() */ +/* */ +/* Convert an angle in the specified units to decimal degrees. */ +/************************************************************************/ + +double GTIFAngleStringToDD( const char * pszAngle, int nUOMAngle ) + +{ + double dfAngle; + + if( nUOMAngle == 9110 ) /* DDD.MMSSsss */ + { + char *pszDecimal; + + dfAngle = ABS(atoi(pszAngle)); + pszDecimal = strchr(pszAngle,'.'); + if( pszDecimal != NULL && strlen(pszDecimal) > 1 ) + { + char szMinutes[3]; + char szSeconds[64]; + + szMinutes[0] = pszDecimal[1]; + if( pszDecimal[2] >= '0' && pszDecimal[2] <= '9' ) + szMinutes[1] = pszDecimal[2]; + else + szMinutes[1] = '0'; + + szMinutes[2] = '\0'; + dfAngle += atoi(szMinutes) / 60.0; + + if( strlen(pszDecimal) > 3 ) + { + szSeconds[0] = pszDecimal[3]; + if( pszDecimal[4] >= '0' && pszDecimal[4] <= '9' ) + { + szSeconds[1] = pszDecimal[4]; + szSeconds[2] = '.'; + strncpy( szSeconds+3, pszDecimal + 5, sizeof(szSeconds) - 3 ); + szSeconds[sizeof(szSeconds) - 1] = 0; + } + else + { + szSeconds[1] = '0'; + szSeconds[2] = '\0'; + } + dfAngle += GTIFAtof(szSeconds) / 3600.0; + } + } + + if( pszAngle[0] == '-' ) + dfAngle *= -1; + } + else if( nUOMAngle == 9105 || nUOMAngle == 9106 ) /* grad */ + { + dfAngle = 180 * (GTIFAtof(pszAngle ) / 200); + } + else if( nUOMAngle == 9101 ) /* radians */ + { + dfAngle = 180 * (GTIFAtof(pszAngle ) / PI); + } + else if( nUOMAngle == 9103 ) /* arc-minute */ + { + dfAngle = GTIFAtof(pszAngle) / 60; + } + else if( nUOMAngle == 9104 ) /* arc-second */ + { + dfAngle = GTIFAtof(pszAngle) / 3600; + } + else /* decimal degrees ... some cases missing but seeminly never used */ + { + CPLAssert( nUOMAngle == 9102 || nUOMAngle == KvUserDefined + || nUOMAngle == 0 ); + + dfAngle = GTIFAtof(pszAngle ); + } + + return( dfAngle ); +} + +/************************************************************************/ +/* GTIFGetGCSInfo() */ +/* */ +/* Fetch the datum, and prime meridian related to a particular */ +/* GCS. */ +/************************************************************************/ + +int GTIFGetGCSInfo( int nGCSCode, char ** ppszName, + short * pnDatum, short * pnPM, short *pnUOMAngle ) + +{ + char szSearchKey[24]; + int nDatum=0, nPM, nUOMAngle; + const char *pszFilename; + +/* -------------------------------------------------------------------- */ +/* Handle some "well known" GCS codes directly */ +/* -------------------------------------------------------------------- */ + const char * pszName = NULL; + nPM = PM_Greenwich; + nUOMAngle = Angular_DMS_Hemisphere; + if( nGCSCode == GCS_NAD27 ) + { + nDatum = Datum_North_American_Datum_1927; + pszName = "NAD27"; + } + else if( nGCSCode == GCS_NAD83 ) + { + nDatum = Datum_North_American_Datum_1983; + pszName = "NAD83"; + } + else if( nGCSCode == GCS_WGS_84 ) + { + nDatum = Datum_WGS84; + pszName = "WGS 84"; + } + else if( nGCSCode == GCS_WGS_72 ) + { + nDatum = Datum_WGS72; + pszName = "WGS 72"; + } + else if ( nGCSCode == KvUserDefined ) + { + return FALSE; + } + + if (pszName != NULL) + { + if( ppszName != NULL ) + *ppszName = CPLStrdup( pszName ); + if( pnDatum != NULL ) + *pnDatum = (short) nDatum; + if( pnPM != NULL ) + *pnPM = (short) nPM; + if( pnUOMAngle != NULL ) + *pnUOMAngle = (short) nUOMAngle; + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Search the database for the corresponding datum code. */ +/* -------------------------------------------------------------------- */ + pszFilename = CSVFilename("gcs.override.csv"); + sprintf( szSearchKey, "%d", nGCSCode ); + nDatum = atoi(CSVGetField( pszFilename, + "COORD_REF_SYS_CODE", szSearchKey, + CC_Integer, "DATUM_CODE" ) ); + + if( nDatum < 1 ) + { + pszFilename = CSVFilename("gcs.csv"); + sprintf( szSearchKey, "%d", nGCSCode ); + nDatum = atoi(CSVGetField( pszFilename, + "COORD_REF_SYS_CODE", szSearchKey, + CC_Integer, "DATUM_CODE" ) ); + } + + if( nDatum < 1 ) + { + static int bWarnedOrTried = FALSE; + if( !bWarnedOrTried ) + { + FILE* f = VSIFOpen(CSVFilename( "gcs.csv" ), "rb"); + if( f == NULL ) + CPLError(CE_Warning, CPLE_AppDefined, "Cannot find gcs.csv"); + else + VSIFClose(f); + bWarnedOrTried = TRUE; + } + return FALSE; + } + + if( pnDatum != NULL ) + *pnDatum = (short) nDatum; + +/* -------------------------------------------------------------------- */ +/* Get the PM. */ +/* -------------------------------------------------------------------- */ + if( pnPM != NULL ) + { + nPM = atoi(CSVGetField( pszFilename, + "COORD_REF_SYS_CODE", szSearchKey, CC_Integer, + "PRIME_MERIDIAN_CODE" ) ); + + if( nPM < 1 ) + return FALSE; + + *pnPM = (short) nPM; + } + +/* -------------------------------------------------------------------- */ +/* Get the angular units. */ +/* -------------------------------------------------------------------- */ + nUOMAngle = atoi(CSVGetField( pszFilename, + "COORD_REF_SYS_CODE",szSearchKey, CC_Integer, + "UOM_CODE" ) ); + + if( nUOMAngle < 1 ) + return FALSE; + + if( pnUOMAngle != NULL ) + *pnUOMAngle = (short) nUOMAngle; + +/* -------------------------------------------------------------------- */ +/* Get the name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszName != NULL ) + *ppszName = + CPLStrdup(CSVGetField( pszFilename, + "COORD_REF_SYS_CODE",szSearchKey,CC_Integer, + "COORD_REF_SYS_NAME" )); + + return( TRUE ); +} + +/************************************************************************/ +/* GTIFGetEllipsoidInfo() */ +/* */ +/* Fetch info about an ellipsoid. Axes are always returned in */ +/* meters. SemiMajor computed based on inverse flattening */ +/* where that is provided. */ +/************************************************************************/ + +int GTIFGetEllipsoidInfo( int nEllipseCode, char ** ppszName, + double * pdfSemiMajor, double * pdfSemiMinor ) + +{ + char szSearchKey[24]; + double dfSemiMajor=0.0, dfToMeters = 1.0; + int nUOMLength; + +/* -------------------------------------------------------------------- */ +/* Try some well known ellipsoids. */ +/* -------------------------------------------------------------------- */ + double dfInvFlattening=0.0, dfSemiMinor=0.0; + const char *pszName = NULL; + + if( nEllipseCode == Ellipse_Clarke_1866 ) + { + pszName = "Clarke 1866"; + dfSemiMajor = 6378206.4; + dfSemiMinor = 6356583.8; + dfInvFlattening = 0.0; + } + else if( nEllipseCode == Ellipse_GRS_1980 ) + { + pszName = "GRS 1980"; + dfSemiMajor = 6378137.0; + dfSemiMinor = 0.0; + dfInvFlattening = 298.257222101; + } + else if( nEllipseCode == Ellipse_WGS_84 ) + { + pszName = "WGS 84"; + dfSemiMajor = 6378137.0; + dfSemiMinor = 0.0; + dfInvFlattening = 298.257223563; + } + else if( nEllipseCode == 7043 ) + { + pszName = "WGS 72"; + dfSemiMajor = 6378135.0; + dfSemiMinor = 0.0; + dfInvFlattening = 298.26; + } + + if (pszName != NULL) + { + if( dfSemiMinor == 0.0 ) + dfSemiMinor = dfSemiMajor * (1 - 1.0/dfInvFlattening); + + if( pdfSemiMinor != NULL ) + *pdfSemiMinor = dfSemiMinor; + if( pdfSemiMajor != NULL ) + *pdfSemiMajor = dfSemiMajor; + if( ppszName != NULL ) + *ppszName = CPLStrdup( pszName ); + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Get the semi major axis. */ +/* -------------------------------------------------------------------- */ + sprintf( szSearchKey, "%d", nEllipseCode ); + dfSemiMajor = + GTIFAtof(CSVGetField( CSVFilename("ellipsoid.csv"), + "ELLIPSOID_CODE", szSearchKey, CC_Integer, + "SEMI_MAJOR_AXIS" ) ); + + if( dfSemiMajor == 0.0 ) + { + static int bWarnedOrTried = FALSE; + if( !bWarnedOrTried ) + { + FILE* f = VSIFOpen(CSVFilename( "ellipsoid.csv" ), "rb"); + if( f == NULL ) + CPLError(CE_Warning, CPLE_AppDefined, "Cannot find ellipsoid.csv"); + else + VSIFClose(f); + bWarnedOrTried = TRUE; + } + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Get the translation factor into meters. */ +/* -------------------------------------------------------------------- */ + nUOMLength = atoi(CSVGetField( CSVFilename("ellipsoid.csv"), + "ELLIPSOID_CODE", szSearchKey, CC_Integer, + "UOM_CODE" )); + GTIFGetUOMLengthInfo( nUOMLength, NULL, &dfToMeters ); + + dfSemiMajor *= dfToMeters; + + if( pdfSemiMajor != NULL ) + *pdfSemiMajor = dfSemiMajor; + +/* -------------------------------------------------------------------- */ +/* Get the semi-minor if requested. If the Semi-minor axis */ +/* isn't available, compute it based on the inverse flattening. */ +/* -------------------------------------------------------------------- */ + if( pdfSemiMinor != NULL ) + { + *pdfSemiMinor = + GTIFAtof(CSVGetField( CSVFilename("ellipsoid.csv"), + "ELLIPSOID_CODE", szSearchKey, CC_Integer, + "SEMI_MINOR_AXIS" )) * dfToMeters; + + if( *pdfSemiMinor == 0.0 ) + { + double dfInvFlattening; + + dfInvFlattening = + GTIFAtof(CSVGetField( CSVFilename("ellipsoid.csv"), + "ELLIPSOID_CODE", szSearchKey, CC_Integer, + "INV_FLATTENING" )); + *pdfSemiMinor = dfSemiMajor * (1 - 1.0/dfInvFlattening); + } + } + +/* -------------------------------------------------------------------- */ +/* Get the name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszName != NULL ) + *ppszName = + CPLStrdup(CSVGetField( CSVFilename("ellipsoid.csv"), + "ELLIPSOID_CODE", szSearchKey, CC_Integer, + "ELLIPSOID_NAME" )); + + return( TRUE ); +} + +/************************************************************************/ +/* GTIFGetPMInfo() */ +/* */ +/* Get the offset between a given prime meridian and Greenwich */ +/* in degrees. */ +/************************************************************************/ + +int GTIFGetPMInfo( int nPMCode, char ** ppszName, double *pdfOffset ) + +{ + char szSearchKey[24]; + int nUOMAngle; + const char *pszFilename; + +/* -------------------------------------------------------------------- */ +/* Use a special short cut for Greenwich, since it is so common. */ +/* -------------------------------------------------------------------- */ + if( nPMCode == PM_Greenwich ) + { + if( pdfOffset != NULL ) + *pdfOffset = 0.0; + if( ppszName != NULL ) + *ppszName = CPLStrdup( "Greenwich" ); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Search the database for the corresponding datum code. */ +/* -------------------------------------------------------------------- */ + pszFilename = CSVFilename("prime_meridian.csv"); + sprintf( szSearchKey, "%d", nPMCode ); + + nUOMAngle = + atoi(CSVGetField( pszFilename, + "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer, + "UOM_CODE" ) ); + if( nUOMAngle < 1 ) + { + static int bWarnedOrTried = FALSE; + if( !bWarnedOrTried ) + { + FILE* f = VSIFOpen(CSVFilename( "prime_meridian.csv" ), "rb"); + if( f == NULL ) + CPLError(CE_Warning, CPLE_AppDefined, "Cannot find prime_meridian.csv"); + else + VSIFClose(f); + bWarnedOrTried = TRUE; + } + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Get the PM offset. */ +/* -------------------------------------------------------------------- */ + if( pdfOffset != NULL ) + { + *pdfOffset = + GTIFAngleStringToDD( + CSVGetField( pszFilename, + "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer, + "GREENWICH_LONGITUDE" ), + nUOMAngle ); + } + +/* -------------------------------------------------------------------- */ +/* Get the name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszName != NULL ) + *ppszName = + CPLStrdup( + CSVGetField( pszFilename, + "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer, + "PRIME_MERIDIAN_NAME" )); + + return( TRUE ); +} + +/************************************************************************/ +/* GTIFGetDatumInfo() */ +/* */ +/* Fetch the ellipsoid, and name for a datum. */ +/************************************************************************/ + +int GTIFGetDatumInfo( int nDatumCode, char ** ppszName, short * pnEllipsoid ) + +{ + char szSearchKey[24]; + int nEllipsoid = 0; + const char *pszFilename; + FILE *fp; + const char *pszName = NULL; + +/* -------------------------------------------------------------------- */ +/* Handle a few built-in datums. */ +/* -------------------------------------------------------------------- */ + if( nDatumCode == Datum_North_American_Datum_1927 ) + { + nEllipsoid = Ellipse_Clarke_1866; + pszName = "North American Datum 1927"; + } + else if( nDatumCode == Datum_North_American_Datum_1983 ) + { + nEllipsoid = Ellipse_GRS_1980; + pszName = "North American Datum 1983"; + } + else if( nDatumCode == Datum_WGS84 ) + { + nEllipsoid = Ellipse_WGS_84; + pszName = "World Geodetic System 1984"; + } + else if( nDatumCode == Datum_WGS72 ) + { + nEllipsoid = 7043; /* WGS72 */ + pszName = "World Geodetic System 1972"; + } + + if (pszName != NULL) + { + if( pnEllipsoid != NULL ) + *pnEllipsoid = (short) nEllipsoid; + + if( ppszName != NULL ) + *ppszName = CPLStrdup( pszName ); + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* If we can't find datum.csv then gdal_datum.csv is an */ +/* acceptable fallback. Mostly this is for GDAL. */ +/* -------------------------------------------------------------------- */ + pszFilename = CSVFilename( "datum.csv" ); + if( (fp = VSIFOpen(pszFilename,"r")) == NULL ) + { + if( (fp = VSIFOpen(CSVFilename("gdal_datum.csv"), "r")) != NULL ) + { + pszFilename = CSVFilename( "gdal_datum.csv" ); + VSIFClose( fp ); + } + } + else + VSIFClose( fp ); + +/* -------------------------------------------------------------------- */ +/* Search the database for the corresponding datum code. */ +/* -------------------------------------------------------------------- */ + sprintf( szSearchKey, "%d", nDatumCode ); + + nEllipsoid = atoi(CSVGetField( pszFilename, + "DATUM_CODE", szSearchKey, CC_Integer, + "ELLIPSOID_CODE" ) ); + + if( pnEllipsoid != NULL ) + *pnEllipsoid = (short) nEllipsoid; + + if( nEllipsoid < 1 ) + { + static int bWarnedOrTried = FALSE; + if( !bWarnedOrTried ) + { + FILE* f = VSIFOpen(CSVFilename( "datum.csv" ), "rb"); + if( f == NULL ) + f = VSIFOpen(CSVFilename( "gdal_datum.csv" ), "rb"); + if( f == NULL ) + CPLError(CE_Warning, CPLE_AppDefined, "Cannot find datum.csv or gdal_datum.csv"); + else + VSIFClose(f); + bWarnedOrTried = TRUE; + } + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Get the name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszName != NULL ) + *ppszName = + CPLStrdup(CSVGetField( pszFilename, + "DATUM_CODE", szSearchKey, CC_Integer, + "DATUM_NAME" )); + + return( TRUE ); +} + + +/************************************************************************/ +/* GTIFGetUOMLengthInfo() */ +/* */ +/* Note: This function should eventually also know how to */ +/* lookup length aliases in the UOM_LE_ALIAS table. */ +/************************************************************************/ + +int GTIFGetUOMLengthInfo( int nUOMLengthCode, + char **ppszUOMName, + double * pdfInMeters ) + +{ + char **papszUnitsRecord; + char szSearchKey[24]; + int iNameField; + const char *pszFilename; + +/* -------------------------------------------------------------------- */ +/* We short cut meter to save work and avoid failure for missing */ +/* in the most common cases. */ +/* -------------------------------------------------------------------- */ + if( nUOMLengthCode == 9001 ) + { + if( ppszUOMName != NULL ) + *ppszUOMName = CPLStrdup( "metre" ); + if( pdfInMeters != NULL ) + *pdfInMeters = 1.0; + + return TRUE; + } + + if( nUOMLengthCode == 9002 ) + { + if( ppszUOMName != NULL ) + *ppszUOMName = CPLStrdup( "foot" ); + if( pdfInMeters != NULL ) + *pdfInMeters = 0.3048; + + return TRUE; + } + + if( nUOMLengthCode == 9003 ) + { + if( ppszUOMName != NULL ) + *ppszUOMName = CPLStrdup( "US survey foot" ); + if( pdfInMeters != NULL ) + *pdfInMeters = 12.0 / 39.37; + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Search the units database for this unit. If we don't find */ +/* it return failure. */ +/* -------------------------------------------------------------------- */ + pszFilename = CSVFilename( "unit_of_measure.csv" ); + + sprintf( szSearchKey, "%d", nUOMLengthCode ); + papszUnitsRecord = + CSVScanFileByName( pszFilename, + "UOM_CODE", szSearchKey, CC_Integer ); + + if( papszUnitsRecord == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get the name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszUOMName != NULL ) + { + iNameField = CSVGetFileFieldId( pszFilename, + "UNIT_OF_MEAS_NAME" ); + *ppszUOMName = CPLStrdup( CSLGetField(papszUnitsRecord, iNameField) ); + } + +/* -------------------------------------------------------------------- */ +/* Get the A and B factor fields, and create the multiplicative */ +/* factor. */ +/* -------------------------------------------------------------------- */ + if( pdfInMeters != NULL ) + { + int iBFactorField, iCFactorField; + + iBFactorField = CSVGetFileFieldId( pszFilename, "FACTOR_B" ); + iCFactorField = CSVGetFileFieldId( pszFilename, "FACTOR_C" ); + + if( GTIFAtof(CSLGetField(papszUnitsRecord, iCFactorField)) > 0.0 ) + *pdfInMeters = GTIFAtof(CSLGetField(papszUnitsRecord, iBFactorField)) + / GTIFAtof(CSLGetField(papszUnitsRecord, iCFactorField)); + else + *pdfInMeters = 0.0; + } + + return( TRUE ); +} + +/************************************************************************/ +/* GTIFGetUOMAngleInfo() */ +/************************************************************************/ + +int GTIFGetUOMAngleInfo( int nUOMAngleCode, + char **ppszUOMName, + double * pdfInDegrees ) + +{ + const char *pszUOMName = NULL; + double dfInDegrees = 1.0; + const char *pszFilename; + char szSearchKey[24]; + + switch( nUOMAngleCode ) + { + case 9101: + pszUOMName = "radian"; + dfInDegrees = 180.0 / PI; + break; + + case 9102: + case 9107: + case 9108: + case 9110: + case 9122: + pszUOMName = "degree"; + dfInDegrees = 1.0; + break; + + case 9103: + pszUOMName = "arc-minute"; + dfInDegrees = 1 / 60.0; + break; + + case 9104: + pszUOMName = "arc-second"; + dfInDegrees = 1 / 3600.0; + break; + + case 9105: + pszUOMName = "grad"; + dfInDegrees = 180.0 / 200.0; + break; + + case 9106: + pszUOMName = "gon"; + dfInDegrees = 180.0 / 200.0; + break; + + case 9109: + pszUOMName = "microradian"; + dfInDegrees = 180.0 / (PI * 1000000.0); + break; + + default: + break; + } + + if (pszUOMName) + { + if( ppszUOMName != NULL ) + { + if( pszUOMName != NULL ) + *ppszUOMName = CPLStrdup( pszUOMName ); + else + *ppszUOMName = NULL; + } + + if( pdfInDegrees != NULL ) + *pdfInDegrees = dfInDegrees; + + return TRUE; + } + + pszFilename = CSVFilename( "unit_of_measure.csv" ); + sprintf( szSearchKey, "%d", nUOMAngleCode ); + pszUOMName = CSVGetField( pszFilename, + "UOM_CODE", szSearchKey, CC_Integer, + "UNIT_OF_MEAS_NAME" ); + +/* -------------------------------------------------------------------- */ +/* If the file is found, read from there. Note that FactorC is */ +/* an empty field for any of the DMS style formats, and in this */ +/* case we really want to return the default InDegrees value */ +/* (1.0) from above. */ +/* -------------------------------------------------------------------- */ + if( pszUOMName != NULL ) + { + double dfFactorB, dfFactorC, dfInRadians; + + dfFactorB = + GTIFAtof(CSVGetField( pszFilename, + "UOM_CODE", szSearchKey, CC_Integer, + "FACTOR_B" )); + + dfFactorC = + GTIFAtof(CSVGetField( pszFilename, + "UOM_CODE", szSearchKey, CC_Integer, + "FACTOR_C" )); + + if( dfFactorC != 0.0 ) + { + dfInRadians = (dfFactorB / dfFactorC); + dfInDegrees = dfInRadians * 180.0 / PI; + } + } + else + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Return to caller. */ +/* -------------------------------------------------------------------- */ + if( ppszUOMName != NULL ) + { + if( pszUOMName != NULL ) + *ppszUOMName = CPLStrdup( pszUOMName ); + else + *ppszUOMName = NULL; + } + + if( pdfInDegrees != NULL ) + *pdfInDegrees = dfInDegrees; + + return( TRUE ); +} + +/************************************************************************/ +/* EPSGProjMethodToCTProjMethod() */ +/* */ +/* Convert between the EPSG enumeration for projection methods, */ +/* and the GeoTIFF CT codes. */ +/************************************************************************/ + +static int EPSGProjMethodToCTProjMethod( int nEPSG, int bReturnExtendedCTCode ) + +{ + /* see trf_method.csv for list of EPSG codes */ + + switch( nEPSG ) + { + case 9801: + return( CT_LambertConfConic_1SP ); + + case 9802: + return( CT_LambertConfConic_2SP ); + + case 9803: + return( CT_LambertConfConic_2SP ); /* Belgian variant not supported */ + + case 9804: + return( CT_Mercator ); /* 1SP and 2SP not differentiated */ + + case 9805: + if( bReturnExtendedCTCode ) + return( CT_Ext_Mercator_2SP ); + else + return( CT_Mercator ); /* 1SP and 2SP not differentiated */ + + /* Mercator 1SP (Spherical) For EPSG:3785 */ + case 9841: + return( CT_Mercator ); /* 1SP and 2SP not differentiated */ + + /* Google Mercator For EPSG:3857 */ + case 1024: + return( CT_Mercator ); /* 1SP and 2SP not differentiated */ + + case 9806: + return( CT_CassiniSoldner ); + + case 9807: + return( CT_TransverseMercator ); + + case 9808: + return( CT_TransvMercator_SouthOriented ); + + case 9809: + return( CT_ObliqueStereographic ); + + case 9810: + case 9829: /* variant B not quite the same - not sure how to handle */ + return( CT_PolarStereographic ); + + case 9811: + return( CT_NewZealandMapGrid ); + + case 9812: + return( CT_ObliqueMercator ); /* is hotine actually different? */ + + case 9813: + return( CT_ObliqueMercator_Laborde ); + + case 9814: + return( CT_ObliqueMercator_Rosenmund ); /* swiss */ + + case 9815: + return( CT_HotineObliqueMercatorAzimuthCenter ); + + case 9816: /* tunesia mining grid has no counterpart */ + return( KvUserDefined ); + + case 9818: + return( CT_Polyconic ); + + case 9820: + case 1027: + return( CT_LambertAzimEqualArea ); + + case 9822: + return( CT_AlbersEqualArea ); + + case 9834: + return( CT_CylindricalEqualArea ); + + case 1028: + case 1029: + case 9823: /* spherical */ + case 9842: /* elliptical */ + return( CT_Equirectangular ); + + default: /* use the EPSG code for other methods */ + return nEPSG; + } + + return( KvUserDefined ); +} + +/************************************************************************/ +/* SetGTParmIds() */ +/* */ +/* This is hardcoded logic to set the GeoTIFF parmaeter */ +/* identifiers for all the EPSG supported projections. As the */ +/* trf_method.csv table grows with new projections, this code */ +/* will need to be updated. */ +/************************************************************************/ + +static int SetGTParmIds( int nCTProjection, + int *panProjParmId, + int *panEPSGCodes ) + +{ + int anWorkingDummy[7]; + + if( panEPSGCodes == NULL ) + panEPSGCodes = anWorkingDummy; + if( panProjParmId == NULL ) + panProjParmId = anWorkingDummy; + + memset( panEPSGCodes, 0, sizeof(int) * 7 ); + + /* psDefn->nParms = 7; */ + + switch( nCTProjection ) + { + case CT_CassiniSoldner: + case CT_NewZealandMapGrid: + case CT_Polyconic: + panProjParmId[0] = ProjNatOriginLatGeoKey; + panProjParmId[1] = ProjNatOriginLongGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGNatOriginLat; + panEPSGCodes[1] = EPSGNatOriginLong; + panEPSGCodes[5] = EPSGFalseEasting; + panEPSGCodes[6] = EPSGFalseNorthing; + return TRUE; + + case CT_ObliqueMercator: + case CT_HotineObliqueMercatorAzimuthCenter: + panProjParmId[0] = ProjCenterLatGeoKey; + panProjParmId[1] = ProjCenterLongGeoKey; + panProjParmId[2] = ProjAzimuthAngleGeoKey; + panProjParmId[3] = ProjRectifiedGridAngleGeoKey; + panProjParmId[4] = ProjScaleAtCenterGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGProjCenterLat; + panEPSGCodes[1] = EPSGProjCenterLong; + panEPSGCodes[2] = EPSGAzimuth; + panEPSGCodes[3] = EPSGAngleRectifiedToSkewedGrid; + panEPSGCodes[4] = EPSGInitialLineScaleFactor; + panEPSGCodes[5] = EPSGProjCenterEasting; /* EPSG proj method 9812 uses EPSGFalseEasting, but 9815 uses EPSGProjCenterEasting */ + panEPSGCodes[6] = EPSGProjCenterNorthing; /* EPSG proj method 9812 uses EPSGFalseNorthing, but 9815 uses EPSGProjCenterNorthing */ + return TRUE; + + case CT_ObliqueMercator_Laborde: + panProjParmId[0] = ProjCenterLatGeoKey; + panProjParmId[1] = ProjCenterLongGeoKey; + panProjParmId[2] = ProjAzimuthAngleGeoKey; + panProjParmId[4] = ProjScaleAtCenterGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGProjCenterLat; + panEPSGCodes[1] = EPSGProjCenterLong; + panEPSGCodes[2] = EPSGAzimuth; + panEPSGCodes[4] = EPSGInitialLineScaleFactor; + panEPSGCodes[5] = EPSGProjCenterEasting; + panEPSGCodes[6] = EPSGProjCenterNorthing; + return TRUE; + + case CT_LambertConfConic_1SP: + case CT_Mercator: + case CT_ObliqueStereographic: + case CT_PolarStereographic: + case CT_TransverseMercator: + case CT_TransvMercator_SouthOriented: + panProjParmId[0] = ProjNatOriginLatGeoKey; + panProjParmId[1] = ProjNatOriginLongGeoKey; + panProjParmId[4] = ProjScaleAtNatOriginGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGNatOriginLat; + panEPSGCodes[1] = EPSGNatOriginLong; + panEPSGCodes[4] = EPSGNatOriginScaleFactor; + panEPSGCodes[5] = EPSGFalseEasting; + panEPSGCodes[6] = EPSGFalseNorthing; + return TRUE; + + case CT_LambertConfConic_2SP: + panProjParmId[0] = ProjFalseOriginLatGeoKey; + panProjParmId[1] = ProjFalseOriginLongGeoKey; + panProjParmId[2] = ProjStdParallel1GeoKey; + panProjParmId[3] = ProjStdParallel2GeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGFalseOriginLat; + panEPSGCodes[1] = EPSGFalseOriginLong; + panEPSGCodes[2] = EPSGStdParallel1Lat; + panEPSGCodes[3] = EPSGStdParallel2Lat; + panEPSGCodes[5] = EPSGFalseOriginEasting; + panEPSGCodes[6] = EPSGFalseOriginNorthing; + return TRUE; + + case CT_AlbersEqualArea: + panProjParmId[0] = ProjStdParallel1GeoKey; + panProjParmId[1] = ProjStdParallel2GeoKey; + panProjParmId[2] = ProjNatOriginLatGeoKey; + panProjParmId[3] = ProjNatOriginLongGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGStdParallel1Lat; + panEPSGCodes[1] = EPSGStdParallel2Lat; + panEPSGCodes[2] = EPSGFalseOriginLat; + panEPSGCodes[3] = EPSGFalseOriginLong; + panEPSGCodes[5] = EPSGFalseOriginEasting; + panEPSGCodes[6] = EPSGFalseOriginNorthing; + return TRUE; + + case CT_SwissObliqueCylindrical: + panProjParmId[0] = ProjCenterLatGeoKey; + panProjParmId[1] = ProjCenterLongGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + /* EPSG codes? */ + return TRUE; + + case CT_LambertAzimEqualArea: + panProjParmId[0] = ProjCenterLatGeoKey; + panProjParmId[1] = ProjCenterLongGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGNatOriginLat; + panEPSGCodes[1] = EPSGNatOriginLong; + panEPSGCodes[5] = EPSGFalseEasting; + panEPSGCodes[6] = EPSGFalseNorthing; + return TRUE; + + case CT_CylindricalEqualArea: + panProjParmId[0] = ProjStdParallel1GeoKey; + panProjParmId[1] = ProjNatOriginLongGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGStdParallel1Lat; + panEPSGCodes[1] = EPSGFalseOriginLong; + panEPSGCodes[5] = EPSGFalseOriginEasting; + panEPSGCodes[6] = EPSGFalseOriginNorthing; + return TRUE; + + case CT_Equirectangular: + panProjParmId[0] = ProjCenterLatGeoKey; + panProjParmId[1] = ProjCenterLongGeoKey; + panProjParmId[2] = ProjStdParallel1GeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGNatOriginLat; + panEPSGCodes[1] = EPSGNatOriginLong; + panEPSGCodes[2] = EPSGStdParallel1Lat; + panEPSGCodes[5] = EPSGFalseEasting; + panEPSGCodes[6] = EPSGFalseNorthing; + return TRUE; + + case CT_Ext_Mercator_2SP: + panProjParmId[0] = ProjNatOriginLatGeoKey; + panProjParmId[1] = ProjNatOriginLongGeoKey; + panProjParmId[2] = ProjStdParallel1GeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGNatOriginLat; + panEPSGCodes[1] = EPSGNatOriginLong; + panEPSGCodes[2] = EPSGStdParallel1Lat; + panEPSGCodes[5] = EPSGFalseEasting; + panEPSGCodes[6] = EPSGFalseNorthing; + return TRUE; + + default: + return( FALSE ); + } +} + +/************************************************************************/ +/* GTIFGetProjTRFInfo() */ +/* */ +/* Transform a PROJECTION_TRF_CODE into a projection method, */ +/* and a set of parameters. The parameters identify will */ +/* depend on the returned method, but they will all have been */ +/* normalized into degrees and meters. */ +/************************************************************************/ + +int GTIFGetProjTRFInfo( /* COORD_OP_CODE from coordinate_operation.csv */ + int nProjTRFCode, + char **ppszProjTRFName, + short * pnProjMethod, + double * padfProjParms ) + +{ + int nProjMethod, i, anEPSGCodes[7]; + double adfProjParms[7]; + char szTRFCode[16]; + int nCTProjMethod; + char *pszFilename; + + if ((nProjTRFCode >= Proj_UTM_zone_1N && nProjTRFCode <= Proj_UTM_zone_60N) || + (nProjTRFCode >= Proj_UTM_zone_1S && nProjTRFCode <= Proj_UTM_zone_60S)) + { + int bNorth; + int nZone; + if (nProjTRFCode <= Proj_UTM_zone_60N) + { + bNorth = TRUE; + nZone = nProjTRFCode - Proj_UTM_zone_1N + 1; + } + else + { + bNorth = FALSE; + nZone = nProjTRFCode - Proj_UTM_zone_1S + 1; + } + + if (ppszProjTRFName) + { + char szProjTRFName[64]; + sprintf(szProjTRFName, "UTM zone %d%c", + nZone, (bNorth) ? 'N' : 'S'); + *ppszProjTRFName = CPLStrdup(szProjTRFName); + } + + if (pnProjMethod) + *pnProjMethod = 9807; + + if (padfProjParms) + { + padfProjParms[0] = 0; + padfProjParms[1] = -183 + 6 * nZone; + padfProjParms[2] = 0; + padfProjParms[3] = 0; + padfProjParms[4] = 0.9996; + padfProjParms[5] = 500000; + padfProjParms[6] = (bNorth) ? 0 : 10000000; + } + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Get the proj method. If this fails to return a meaningful */ +/* number, then the whole function fails. */ +/* -------------------------------------------------------------------- */ + pszFilename = CPLStrdup(CSVFilename("projop_wparm.csv")); + sprintf( szTRFCode, "%d", nProjTRFCode ); + nProjMethod = + atoi( CSVGetField( pszFilename, + "COORD_OP_CODE", szTRFCode, CC_Integer, + "COORD_OP_METHOD_CODE" ) ); + if( nProjMethod == 0 ) + { + CPLFree( pszFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Initialize a definition of what EPSG codes need to be loaded */ +/* into what fields in adfProjParms. */ +/* -------------------------------------------------------------------- */ + nCTProjMethod = EPSGProjMethodToCTProjMethod( nProjMethod, TRUE ); + SetGTParmIds( nCTProjMethod, NULL, anEPSGCodes ); + +/* -------------------------------------------------------------------- */ +/* Get the parameters for this projection. For the time being */ +/* I am assuming the first four parameters are angles, the */ +/* fifth is unitless (normally scale), and the remainder are */ +/* linear measures. This works fine for the existing */ +/* projections, but is a pretty fragile approach. */ +/* -------------------------------------------------------------------- */ + + for( i = 0; i < 7; i++ ) + { + char szParamUOMID[32], szParamValueID[32], szParamCodeID[32]; + const char *pszValue; + int nUOM; + int nEPSGCode = anEPSGCodes[i]; + int iEPSG; + + /* Establish default */ + if( nEPSGCode == EPSGAngleRectifiedToSkewedGrid ) + adfProjParms[i] = 90.0; + else if( nEPSGCode == EPSGNatOriginScaleFactor + || nEPSGCode == EPSGInitialLineScaleFactor + || nEPSGCode == EPSGPseudoStdParallelScaleFactor ) + adfProjParms[i] = 1.0; + else + adfProjParms[i] = 0.0; + + /* If there is no parameter, skip */ + if( nEPSGCode == 0 ) + continue; + + /* Find the matching parameter */ + for( iEPSG = 0; iEPSG < 7; iEPSG++ ) + { + sprintf( szParamCodeID, "PARAMETER_CODE_%d", iEPSG+1 ); + + if( atoi(CSVGetField( pszFilename, + "COORD_OP_CODE", szTRFCode, CC_Integer, + szParamCodeID )) == nEPSGCode ) + break; + } + + /* not found, accept the default */ + if( iEPSG == 7 ) + { + /* for CT_ObliqueMercator try alternate parameter codes first */ + /* because EPSG proj method 9812 uses EPSGFalseXXXXX, but 9815 uses EPSGProjCenterXXXXX */ + if ( nCTProjMethod == CT_ObliqueMercator && nEPSGCode == EPSGProjCenterEasting ) + nEPSGCode = EPSGFalseEasting; + else if ( nCTProjMethod == CT_ObliqueMercator && nEPSGCode == EPSGProjCenterNorthing ) + nEPSGCode = EPSGFalseNorthing; + /* for CT_PolarStereographic try alternate parameter codes first */ + /* because EPSG proj method 9829 uses EPSGLatOfStdParallel instead of EPSGNatOriginLat */ + /* and EPSGOriginLong instead of EPSGNatOriginLong */ + else if( nCTProjMethod == CT_PolarStereographic && nEPSGCode == EPSGNatOriginLat ) + nEPSGCode = EPSGLatOfStdParallel; + else if( nCTProjMethod == CT_PolarStereographic && nEPSGCode == EPSGNatOriginLong ) + nEPSGCode = EPSGOriginLong; + else + continue; + + for( iEPSG = 0; iEPSG < 7; iEPSG++ ) + { + sprintf( szParamCodeID, "PARAMETER_CODE_%d", iEPSG+1 ); + + if( atoi(CSVGetField( pszFilename, + "COORD_OP_CODE", szTRFCode, CC_Integer, + szParamCodeID )) == nEPSGCode ) + break; + } + + if( iEPSG == 7 ) + continue; + } + + /* Get the value, and UOM */ + sprintf( szParamUOMID, "PARAMETER_UOM_%d", iEPSG+1 ); + sprintf( szParamValueID, "PARAMETER_VALUE_%d", iEPSG+1 ); + + nUOM = atoi(CSVGetField( pszFilename, + "COORD_OP_CODE", szTRFCode, CC_Integer, + szParamUOMID )); + pszValue = CSVGetField( pszFilename, + "COORD_OP_CODE", szTRFCode, CC_Integer, + szParamValueID ); + + /* Transform according to the UOM */ + if( nUOM >= 9100 && nUOM < 9200 ) + adfProjParms[i] = GTIFAngleStringToDD( pszValue, nUOM ); + else if( nUOM > 9000 && nUOM < 9100 ) + { + double dfInMeters; + + if( !GTIFGetUOMLengthInfo( nUOM, NULL, &dfInMeters ) ) + dfInMeters = 1.0; + adfProjParms[i] = GTIFAtof(pszValue) * dfInMeters; + } + else + adfProjParms[i] = GTIFAtof(pszValue); + } + +/* -------------------------------------------------------------------- */ +/* Get the name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszProjTRFName != NULL ) + { + *ppszProjTRFName = + CPLStrdup(CSVGetField( pszFilename, + "COORD_OP_CODE", szTRFCode, CC_Integer, + "COORD_OP_NAME" )); + } + +/* -------------------------------------------------------------------- */ +/* Transfer requested data into passed variables. */ +/* -------------------------------------------------------------------- */ + if( pnProjMethod != NULL ) + *pnProjMethod = (short) nProjMethod; + + if( padfProjParms != NULL ) + { + for( i = 0; i < 7; i++ ) + padfProjParms[i] = adfProjParms[i]; + } + + CPLFree( pszFilename ); + + return TRUE; +} + +/************************************************************************/ +/* GTIFKeyGetInternal() */ +/************************************************************************/ + +static int GTIFKeyGetInternal( GTIF *psGTIF, geokey_t key, + void* pData, + int nIndex, + int nCount, + tagtype_t expected_tagtype ) +{ + tagtype_t tagtype; + if( !GTIFKeyInfo(psGTIF, key, NULL, &tagtype) ) + return 0; + if( tagtype != expected_tagtype ) + { + static int nErrorCount = 0; + if( ++nErrorCount < 100 ) + { + fprintf(stderr, + "Expected key %s to be of type %s. Got %s\n", + GTIFKeyName(key), GTIFTypeName(expected_tagtype), + GTIFTypeName(tagtype)); + } + return 0; + } + return GTIFKeyGet( psGTIF, key, pData, nIndex, nCount ); +} + +/************************************************************************/ +/* GTIFKeyGetSHORT() */ +/************************************************************************/ + +static int GTIFKeyGetSHORT( GTIF *psGTIF, geokey_t key, + short* pnVal, + int nIndex, + int nCount ) +{ + return GTIFKeyGetInternal(psGTIF, key, pnVal, nIndex, nCount, TYPE_SHORT); +} + +/************************************************************************/ +/* GDALGTIFKeyGetDOUBLE() */ +/************************************************************************/ + +static int GTIFKeyGetDOUBLE( GTIF *psGTIF, geokey_t key, + double* pdfVal, + int nIndex, + int nCount ) +{ + return GTIFKeyGetInternal(psGTIF, key, pdfVal, nIndex, nCount, TYPE_DOUBLE); +} + +/************************************************************************/ +/* GTIFFetchProjParms() */ +/* */ +/* Fetch the projection parameters for a particular projection */ +/* from a GeoTIFF file, and fill the GTIFDefn structure out */ +/* with them. */ +/************************************************************************/ + +static void GTIFFetchProjParms( GTIF * psGTIF, GTIFDefn * psDefn ) + +{ + double dfNatOriginLong = 0.0, dfNatOriginLat = 0.0, dfRectGridAngle = 0.0; + double dfFalseEasting = 0.0, dfFalseNorthing = 0.0, dfNatOriginScale = 1.0; + double dfStdParallel1 = 0.0, dfStdParallel2 = 0.0, dfAzimuth = 0.0; + int iParm; + int bHaveSP1, bHaveNOS; + +/* -------------------------------------------------------------------- */ +/* Get the false easting, and northing if available. */ +/* -------------------------------------------------------------------- */ + if( !GTIFKeyGetDOUBLE(psGTIF, ProjFalseEastingGeoKey, &dfFalseEasting, 0, 1) + && !GTIFKeyGetDOUBLE(psGTIF, ProjCenterEastingGeoKey, + &dfFalseEasting, 0, 1) + && !GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginEastingGeoKey, + &dfFalseEasting, 0, 1) ) + dfFalseEasting = 0.0; + + if( !GTIFKeyGetDOUBLE(psGTIF, ProjFalseNorthingGeoKey, &dfFalseNorthing,0,1) + && !GTIFKeyGetDOUBLE(psGTIF, ProjCenterNorthingGeoKey, + &dfFalseNorthing, 0, 1) + && !GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginNorthingGeoKey, + &dfFalseNorthing, 0, 1) ) + dfFalseNorthing = 0.0; + + switch( psDefn->CTProjection ) + { +/* -------------------------------------------------------------------- */ + case CT_Stereographic: +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 ) + dfNatOriginLong = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 ) + dfNatOriginLat = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey, + &dfNatOriginScale, 0, 1 ) == 0 ) + dfNatOriginScale = 1.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjCenterLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjCenterLongGeoKey; + psDefn->ProjParm[4] = dfNatOriginScale; + psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_Mercator: +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 ) + dfNatOriginLong = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 ) + dfNatOriginLat = 0.0; + + + bHaveSP1 = GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey, + &dfStdParallel1, 0, 1 ); + + bHaveNOS = GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey, + &dfNatOriginScale, 0, 1 ); + + /* Default scale only if dfStdParallel1 isn't defined either */ + if( !bHaveNOS && !bHaveSP1) + { + bHaveNOS = TRUE; + dfNatOriginScale = 1.0; + } + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; + if( bHaveSP1 ) + { + psDefn->ProjParm[2] = dfStdParallel1; + psDefn->ProjParmId[2] = ProjStdParallel1GeoKey; + } + if( bHaveNOS ) + { + psDefn->ProjParm[4] = dfNatOriginScale; + psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; + } + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_LambertConfConic_1SP: + case CT_ObliqueStereographic: + case CT_TransverseMercator: + case CT_TransvMercator_SouthOriented: +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 ) + dfNatOriginLong = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 ) + dfNatOriginLat = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey, + &dfNatOriginScale, 0, 1 ) == 0 ) + dfNatOriginScale = 1.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; + psDefn->ProjParm[4] = dfNatOriginScale; + psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_ObliqueMercator: /* hotine */ + case CT_HotineObliqueMercatorAzimuthCenter: +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 ) + dfNatOriginLong = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 ) + dfNatOriginLat = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjAzimuthAngleGeoKey, + &dfAzimuth, 0, 1 ) == 0 ) + dfAzimuth = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjRectifiedGridAngleGeoKey, + &dfRectGridAngle, 0, 1 ) == 0 ) + dfRectGridAngle = 90.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey, + &dfNatOriginScale, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey, + &dfNatOriginScale, 0, 1 ) == 0 ) + dfNatOriginScale = 1.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjCenterLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjCenterLongGeoKey; + psDefn->ProjParm[2] = dfAzimuth; + psDefn->ProjParmId[2] = ProjAzimuthAngleGeoKey; + psDefn->ProjParm[3] = dfRectGridAngle; + psDefn->ProjParmId[3] = ProjRectifiedGridAngleGeoKey; + psDefn->ProjParm[4] = dfNatOriginScale; + psDefn->ProjParmId[4] = ProjScaleAtCenterGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_CassiniSoldner: + case CT_Polyconic: +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 ) + dfNatOriginLong = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 ) + dfNatOriginLat = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey, + &dfNatOriginScale, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey, + &dfNatOriginScale, 0, 1 ) == 0 ) + dfNatOriginScale = 1.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; + psDefn->ProjParm[4] = dfNatOriginScale; + psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_AzimuthalEquidistant: + case CT_MillerCylindrical: + case CT_Gnomonic: + case CT_LambertAzimEqualArea: + case CT_Orthographic: + case CT_NewZealandMapGrid: +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 ) + dfNatOriginLong = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 ) + dfNatOriginLat = 0.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjCenterLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjCenterLongGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_Equirectangular: +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 ) + dfNatOriginLong = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 ) + dfNatOriginLat = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey, + &dfStdParallel1, 0, 1 ) == 0 ) + dfStdParallel1 = 0.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjCenterLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjCenterLongGeoKey; + psDefn->ProjParm[2] = dfStdParallel1; + psDefn->ProjParmId[2] = ProjStdParallel1GeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_Robinson: + case CT_Sinusoidal: + case CT_VanDerGrinten: +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 ) + dfNatOriginLong = 0.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjCenterLongGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_PolarStereographic: +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetDOUBLE(psGTIF, ProjStraightVertPoleLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 ) + dfNatOriginLong = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 ) + dfNatOriginLat = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey, + &dfNatOriginScale, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey, + &dfNatOriginScale, 0, 1 ) == 0 ) + dfNatOriginScale = 1.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjStraightVertPoleLongGeoKey; + psDefn->ProjParm[4] = dfNatOriginScale; + psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_LambertConfConic_2SP: +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey, + &dfStdParallel1, 0, 1 ) == 0 ) + dfStdParallel1 = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel2GeoKey, + &dfStdParallel2, 0, 1 ) == 0 ) + dfStdParallel1 = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 ) + dfNatOriginLong = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 ) + dfNatOriginLat = 0.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjFalseOriginLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjFalseOriginLongGeoKey; + psDefn->ProjParm[2] = dfStdParallel1; + psDefn->ProjParmId[2] = ProjStdParallel1GeoKey; + psDefn->ProjParm[3] = dfStdParallel2; + psDefn->ProjParmId[3] = ProjStdParallel2GeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_AlbersEqualArea: + case CT_EquidistantConic: +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey, + &dfStdParallel1, 0, 1 ) == 0 ) + dfStdParallel1 = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel2GeoKey, + &dfStdParallel2, 0, 1 ) == 0 ) + dfStdParallel2 = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 ) + dfNatOriginLong = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey, + &dfNatOriginLat, 0, 1 ) == 0 ) + dfNatOriginLat = 0.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfStdParallel1; + psDefn->ProjParmId[0] = ProjStdParallel1GeoKey; + psDefn->ProjParm[1] = dfStdParallel2; + psDefn->ProjParmId[1] = ProjStdParallel2GeoKey; + psDefn->ProjParm[2] = dfNatOriginLat; + psDefn->ProjParmId[2] = ProjNatOriginLatGeoKey; + psDefn->ProjParm[3] = dfNatOriginLong; + psDefn->ProjParmId[3] = ProjNatOriginLongGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_CylindricalEqualArea: +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey, + &dfStdParallel1, 0, 1 ) == 0 ) + dfStdParallel1 = 0.0; + + if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 + && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey, + &dfNatOriginLong, 0, 1 ) == 0 ) + dfNatOriginLong = 0.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfStdParallel1; + psDefn->ProjParmId[0] = ProjStdParallel1GeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + } + +/* -------------------------------------------------------------------- */ +/* Normalize any linear parameters into meters. In GeoTIFF */ +/* the linear projection parameter tags are normally in the */ +/* units of the coordinate system described. */ +/* -------------------------------------------------------------------- */ + for( iParm = 0; iParm < psDefn->nParms; iParm++ ) + { + switch( psDefn->ProjParmId[iParm] ) + { + case ProjFalseEastingGeoKey: + case ProjFalseNorthingGeoKey: + case ProjFalseOriginEastingGeoKey: + case ProjFalseOriginNorthingGeoKey: + case ProjCenterEastingGeoKey: + case ProjCenterNorthingGeoKey: + if( psDefn->UOMLengthInMeters != 0 + && psDefn->UOMLengthInMeters != 1.0 ) + { + psDefn->ProjParm[iParm] *= psDefn->UOMLengthInMeters; + } + break; + + default: + break; + } + } +} + +/************************************************************************/ +/* GTIFGetDefn() */ +/************************************************************************/ + +/** +@param psGTIF GeoTIFF information handle as returned by GTIFNew. +@param psDefn Pointer to an existing GTIFDefn structure allocated by GTIFAllocDefn(). + +@return TRUE if the function has been successful, otherwise FALSE. + +This function reads the coordinate system definition from a GeoTIFF file, +and normalizes it into a set of component information using +definitions from CSV (Comma Seperated Value ASCII) files derived from +EPSG tables. This function is intended to simplify correct support for +reading files with defined PCS (Projected Coordinate System) codes that +wouldn't otherwise be directly known by application software by reducing +it to the underlying projection method, parameters, datum, ellipsoid, +prime meridian and units.

+ +The application should pass a pointer to an existing uninitialized +GTIFDefn structure, and GTIFGetDefn() will fill it in. The fuction +currently always returns TRUE but in the future will return FALSE if +CSV files are not found. In any event, all geokeys actually found in the +file will be copied into the GTIFDefn. However, if the CSV files aren't +found codes implied by other codes will not be set properly.

+ +GTIFGetDefn() will not generally work if the EPSG derived CSV files cannot +be found. By default a modest attempt will be made to find them, but +in general it is necessary for the calling application to override the +logic to find them. This can be done by calling the +SetCSVFilenameHook() function to +override the search method based on application knowledge of where they are +found.

+ +The normalization methodology operates by fetching tags from the GeoTIFF +file, and then setting all other tags implied by them in the structure. The +implied relationships are worked out by reading definitions from the +various EPSG derived CSV tables.

+ +For instance, if a PCS (ProjectedCSTypeGeoKey) is found in the GeoTIFF file +this code is used to lookup a record in the horiz_cs.csv CSV +file. For example given the PCS 26746 we can find the name +(NAD27 / California zone VI), the GCS 4257 (NAD27), and the ProjectionCode +10406 (California CS27 zone VI). The GCS, and ProjectionCode can in turn +be looked up in other tables until all the details of units, ellipsoid, +prime meridian, datum, projection (LambertConfConic_2SP) and projection +parameters are established. A full listgeo dump of a file +for this result might look like the following, all based on a single PCS +value:

+ +

+% listgeo -norm ~/data/geotiff/pci_eg/spaf27.tif
+Geotiff_Information:
+   Version: 1
+   Key_Revision: 1.0
+   Tagged_Information:
+      ModelTiepointTag (2,3):
+         0                0                0                
+         1577139.71       634349.176       0                
+      ModelPixelScaleTag (1,3):
+         195.509321       198.32184        0                
+      End_Of_Tags.
+   Keyed_Information:
+      GTModelTypeGeoKey (Short,1): ModelTypeProjected
+      GTRasterTypeGeoKey (Short,1): RasterPixelIsArea
+      ProjectedCSTypeGeoKey (Short,1): PCS_NAD27_California_VI
+      End_Of_Keys.
+   End_Of_Geotiff.
+
+PCS = 26746 (NAD27 / California zone VI)
+Projection = 10406 (California CS27 zone VI)
+Projection Method: CT_LambertConfConic_2SP
+   ProjStdParallel1GeoKey: 33.883333
+   ProjStdParallel2GeoKey: 32.766667
+   ProjFalseOriginLatGeoKey: 32.166667
+   ProjFalseOriginLongGeoKey: -116.233333
+   ProjFalseEastingGeoKey: 609601.219202
+   ProjFalseNorthingGeoKey: 0.000000
+GCS: 4267/NAD27
+Datum: 6267/North American Datum 1927
+Ellipsoid: 7008/Clarke 1866 (6378206.40,6356583.80)
+Prime Meridian: 8901/Greenwich (0.000000)
+Projection Linear Units: 9003/US survey foot (0.304801m)
+
+ +Note that GTIFGetDefn() does not inspect or return the tiepoints and scale. +This must be handled seperately as it normally would. It is intended to +simplify capture and normalization of the coordinate system definition. +Note that GTIFGetDefn() also does the following things: + +
    +
  1. Convert all angular values to decimal degrees. +
  2. Convert all linear values to meters. +
  3. Return the linear units and conversion to meters for the tiepoints and +scale (though the tiepoints and scale remain in their native units). +
  4. When reading projection parameters a variety of differences between +different GeoTIFF generators are handled, and a normalized set of parameters +for each projection are always returned. +
+ +Code fields in the GTIFDefn are filled with KvUserDefined if there is not +value to assign. The parameter lists for each of the underlying projection +transform methods can be found at the +Projections +page. Note that nParms will be set based on the maximum parameter used. +Some of the parameters may not be used in which case the +GTIFDefn::ProjParmId[] will +be zero. This is done to retain correspondence to the EPSG parameter +numbering scheme.

+ +The +geotiff_proj4.c module distributed with libgeotiff can +be used as an example of code that converts a GTIFDefn into another projection +system.

+ +@see GTIFKeySet(), SetCSVFilenameHook() + +*/ + +int GTIFGetDefn( GTIF * psGTIF, GTIFDefn * psDefn ) + +{ + int i; + short nGeogUOMLinear; + double dfInvFlattening; + +/* -------------------------------------------------------------------- */ +/* Initially we default all the information we can. */ +/* -------------------------------------------------------------------- */ + psDefn->DefnSet = 1; + psDefn->Model = KvUserDefined; + psDefn->PCS = KvUserDefined; + psDefn->GCS = KvUserDefined; + psDefn->UOMLength = KvUserDefined; + psDefn->UOMLengthInMeters = 1.0; + psDefn->UOMAngle = KvUserDefined; + psDefn->UOMAngleInDegrees = 1.0; + psDefn->Datum = KvUserDefined; + psDefn->Ellipsoid = KvUserDefined; + psDefn->SemiMajor = 0.0; + psDefn->SemiMinor = 0.0; + psDefn->PM = KvUserDefined; + psDefn->PMLongToGreenwich = 0.0; +#if !defined(GEO_NORMALIZE_DISABLE_TOWGS84) + psDefn->TOWGS84Count = 0; + memset( psDefn->TOWGS84, 0, sizeof(psDefn->TOWGS84) ); +#endif + + psDefn->ProjCode = KvUserDefined; + psDefn->Projection = KvUserDefined; + psDefn->CTProjection = KvUserDefined; + + psDefn->nParms = 0; + for( i = 0; i < MAX_GTIF_PROJPARMS; i++ ) + { + psDefn->ProjParm[i] = 0.0; + psDefn->ProjParmId[i] = 0; + } + + psDefn->MapSys = KvUserDefined; + psDefn->Zone = 0; + +/* -------------------------------------------------------------------- */ +/* Do we have any geokeys? */ +/* -------------------------------------------------------------------- */ + { + int nKeyCount = 0; + int anVersion[3]; + GTIFDirectoryInfo( psGTIF, anVersion, &nKeyCount ); + + if( nKeyCount == 0 ) + { + psDefn->DefnSet = 0; + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Try to get the overall model type. */ +/* -------------------------------------------------------------------- */ + GTIFKeyGetSHORT(psGTIF,GTModelTypeGeoKey,&(psDefn->Model),0,1); + +/* -------------------------------------------------------------------- */ +/* Extract the Geog units. */ +/* -------------------------------------------------------------------- */ + nGeogUOMLinear = 9001; /* Linear_Meter */ + GTIFKeyGetSHORT(psGTIF, GeogLinearUnitsGeoKey, &nGeogUOMLinear, 0, 1 ); + +/* -------------------------------------------------------------------- */ +/* Try to get a PCS. */ +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetSHORT(psGTIF,ProjectedCSTypeGeoKey, &(psDefn->PCS),0,1) == 1 + && psDefn->PCS != KvUserDefined ) + { + /* + * Translate this into useful information. + */ + GTIFGetPCSInfo( psDefn->PCS, NULL, &(psDefn->ProjCode), + &(psDefn->UOMLength), &(psDefn->GCS) ); + } + +/* -------------------------------------------------------------------- */ +/* If we have the PCS code, but didn't find it in the CSV files */ +/* (likely because we can't find them) we will try some ``jiffy */ +/* rules'' for UTM and state plane. */ +/* -------------------------------------------------------------------- */ + if( psDefn->PCS != KvUserDefined && psDefn->ProjCode == KvUserDefined ) + { + int nMapSys, nZone; + int nGCS = psDefn->GCS; + + nMapSys = GTIFPCSToMapSys( psDefn->PCS, &nGCS, &nZone ); + if( nMapSys != KvUserDefined ) + { + psDefn->ProjCode = (short) GTIFMapSysToProj( nMapSys, nZone ); + psDefn->GCS = (short) nGCS; + } + } + +/* -------------------------------------------------------------------- */ +/* If the Proj_ code is specified directly, use that. */ +/* -------------------------------------------------------------------- */ + if( psDefn->ProjCode == KvUserDefined ) + GTIFKeyGetSHORT(psGTIF, ProjectionGeoKey, &(psDefn->ProjCode), 0, 1 ); + + if( psDefn->ProjCode != KvUserDefined ) + { + /* + * We have an underlying projection transformation value. Look + * this up. For a PCS of ``WGS 84 / UTM 11'' the transformation + * would be Transverse Mercator, with a particular set of options. + * The nProjTRFCode itself would correspond to the name + * ``UTM zone 11N'', and doesn't include datum info. + */ + GTIFGetProjTRFInfo( psDefn->ProjCode, NULL, &(psDefn->Projection), + psDefn->ProjParm ); + + /* + * Set the GeoTIFF identity of the parameters. + */ + psDefn->CTProjection = (short) + EPSGProjMethodToCTProjMethod( psDefn->Projection, FALSE ); + + SetGTParmIds( EPSGProjMethodToCTProjMethod(psDefn->Projection, TRUE), + psDefn->ProjParmId, NULL); + psDefn->nParms = 7; + } + +/* -------------------------------------------------------------------- */ +/* Try to get a GCS. If found, it will override any implied by */ +/* the PCS. */ +/* -------------------------------------------------------------------- */ + GTIFKeyGetSHORT(psGTIF, GeographicTypeGeoKey, &(psDefn->GCS), 0, 1 ); + if( psDefn->GCS < 1 || psDefn->GCS >= KvUserDefined ) + psDefn->GCS = KvUserDefined; + +/* -------------------------------------------------------------------- */ +/* Derive the datum, and prime meridian from the GCS. */ +/* -------------------------------------------------------------------- */ + if( psDefn->GCS != KvUserDefined ) + { + GTIFGetGCSInfo( psDefn->GCS, NULL, &(psDefn->Datum), &(psDefn->PM), + &(psDefn->UOMAngle) ); + } + +/* -------------------------------------------------------------------- */ +/* Handle the GCS angular units. GeogAngularUnitsGeoKey */ +/* overrides the GCS or PCS setting. */ +/* -------------------------------------------------------------------- */ + GTIFKeyGetSHORT(psGTIF, GeogAngularUnitsGeoKey, &(psDefn->UOMAngle), 0, 1 ); + if( psDefn->UOMAngle != KvUserDefined ) + { + GTIFGetUOMAngleInfo( psDefn->UOMAngle, NULL, + &(psDefn->UOMAngleInDegrees) ); + } + +/* -------------------------------------------------------------------- */ +/* Check for a datum setting, and then use the datum to derive */ +/* an ellipsoid. */ +/* -------------------------------------------------------------------- */ + GTIFKeyGetSHORT(psGTIF, GeogGeodeticDatumGeoKey, &(psDefn->Datum), 0, 1 ); + + if( psDefn->Datum != KvUserDefined ) + { + GTIFGetDatumInfo( psDefn->Datum, NULL, &(psDefn->Ellipsoid) ); + } + +/* -------------------------------------------------------------------- */ +/* Check for an explicit ellipsoid. Use the ellipsoid to */ +/* derive the ellipsoid characteristics, if possible. */ +/* -------------------------------------------------------------------- */ + GTIFKeyGetSHORT(psGTIF, GeogEllipsoidGeoKey, &(psDefn->Ellipsoid), 0, 1 ); + + if( psDefn->Ellipsoid != KvUserDefined ) + { + GTIFGetEllipsoidInfo( psDefn->Ellipsoid, NULL, + &(psDefn->SemiMajor), &(psDefn->SemiMinor) ); + } + +/* -------------------------------------------------------------------- */ +/* Check for overridden ellipsoid parameters. It would be nice */ +/* to warn if they conflict with provided information, but for */ +/* now we just override. */ +/* -------------------------------------------------------------------- */ + GTIFKeyGetDOUBLE(psGTIF, GeogSemiMajorAxisGeoKey, &(psDefn->SemiMajor), 0, 1 ); + GTIFKeyGetDOUBLE(psGTIF, GeogSemiMinorAxisGeoKey, &(psDefn->SemiMinor), 0, 1 ); + + if( GTIFKeyGetDOUBLE(psGTIF, GeogInvFlatteningGeoKey, &dfInvFlattening, + 0, 1 ) == 1 ) + { + if( dfInvFlattening != 0.0 ) + psDefn->SemiMinor = + psDefn->SemiMajor * (1 - 1.0/dfInvFlattening); + else + psDefn->SemiMinor = psDefn->SemiMajor; + } + +/* -------------------------------------------------------------------- */ +/* Get the prime meridian info. */ +/* -------------------------------------------------------------------- */ + GTIFKeyGetSHORT(psGTIF, GeogPrimeMeridianGeoKey, &(psDefn->PM), 0, 1 ); + + if( psDefn->PM != KvUserDefined ) + { + GTIFGetPMInfo( psDefn->PM, NULL, &(psDefn->PMLongToGreenwich) ); + } + else + { + GTIFKeyGetDOUBLE(psGTIF, GeogPrimeMeridianLongGeoKey, + &(psDefn->PMLongToGreenwich), 0, 1 ); + + psDefn->PMLongToGreenwich = + GTIFAngleToDD( psDefn->PMLongToGreenwich, + psDefn->UOMAngle ); + } + +/* -------------------------------------------------------------------- */ +/* Get the TOWGS84 parameters. */ +/* -------------------------------------------------------------------- */ +#if !defined(GEO_NORMALIZE_DISABLE_TOWGS84) + psDefn->TOWGS84Count = + GTIFKeyGetDOUBLE(psGTIF, GeogTOWGS84GeoKey, psDefn->TOWGS84, 0, 7 ); +#endif + +/* -------------------------------------------------------------------- */ +/* Have the projection units of measure been overridden? We */ +/* should likely be doing something about angular units too, */ +/* but these are very rarely not decimal degrees for actual */ +/* file coordinates. */ +/* -------------------------------------------------------------------- */ + GTIFKeyGetSHORT(psGTIF,ProjLinearUnitsGeoKey,&(psDefn->UOMLength),0,1); + + if( psDefn->UOMLength != KvUserDefined ) + { + GTIFGetUOMLengthInfo( psDefn->UOMLength, NULL, + &(psDefn->UOMLengthInMeters) ); + } + else + { + GTIFKeyGetDOUBLE(psGTIF,ProjLinearUnitSizeGeoKey,&(psDefn->UOMLengthInMeters),0,1); + } + +/* -------------------------------------------------------------------- */ +/* Handle a variety of user defined transform types. */ +/* -------------------------------------------------------------------- */ + if( GTIFKeyGetSHORT(psGTIF,ProjCoordTransGeoKey, + &(psDefn->CTProjection),0,1) == 1) + { + GTIFFetchProjParms( psGTIF, psDefn ); + } + +/* -------------------------------------------------------------------- */ +/* Try to set the zoned map system information. */ +/* -------------------------------------------------------------------- */ + psDefn->MapSys = GTIFProjToMapSys( psDefn->ProjCode, &(psDefn->Zone) ); + +/* -------------------------------------------------------------------- */ +/* If this is UTM, and we were unable to extract the projection */ +/* parameters from the CSV file, just set them directly now, */ +/* since it's pretty easy, and a common case. */ +/* -------------------------------------------------------------------- */ + if( (psDefn->MapSys == MapSys_UTM_North + || psDefn->MapSys == MapSys_UTM_South) + && psDefn->CTProjection == KvUserDefined ) + { + psDefn->CTProjection = CT_TransverseMercator; + psDefn->nParms = 7; + psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey; + psDefn->ProjParm[0] = 0.0; + + psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; + psDefn->ProjParm[1] = psDefn->Zone*6 - 183.0; + + psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; + psDefn->ProjParm[4] = 0.9996; + + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[5] = 500000.0; + + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + if( psDefn->MapSys == MapSys_UTM_North ) + psDefn->ProjParm[6] = 0.0; + else + psDefn->ProjParm[6] = 10000000.0; + } + + return TRUE; +} + +/************************************************************************/ +/* GTIFDecToDMS() */ +/* */ +/* Convenient function to translate decimal degrees to DMS */ +/* format for reporting to a user. */ +/************************************************************************/ + +const char *GTIFDecToDMS( double dfAngle, const char * pszAxis, + int nPrecision ) + +{ + int nDegrees, nMinutes; + double dfSeconds; + char szFormat[30]; + static char szBuffer[50]; + const char *pszHemisphere = NULL; + double dfRound; + int i; + + dfRound = 0.5/60; + for( i = 0; i < nPrecision; i++ ) + dfRound = dfRound * 0.1; + + nDegrees = (int) ABS(dfAngle); + nMinutes = (int) ((ABS(dfAngle) - nDegrees) * 60 + dfRound); + dfSeconds = ABS((ABS(dfAngle) * 3600 - nDegrees*3600 - nMinutes*60)); + + if( EQUAL(pszAxis,"Long") && dfAngle < 0.0 ) + pszHemisphere = "W"; + else if( EQUAL(pszAxis,"Long") ) + pszHemisphere = "E"; + else if( dfAngle < 0.0 ) + pszHemisphere = "S"; + else + pszHemisphere = "N"; + + sprintf( szFormat, "%%3dd%%2d\'%%%d.%df\"%s", + nPrecision+3, nPrecision, pszHemisphere ); + sprintf( szBuffer, szFormat, nDegrees, nMinutes, dfSeconds ); + + return( szBuffer ); +} + +/************************************************************************/ +/* GTIFPrintDefn() */ +/* */ +/* Report the contents of a GTIFDefn structure ... mostly for */ +/* debugging. */ +/************************************************************************/ + +void GTIFPrintDefn( GTIFDefn * psDefn, FILE * fp ) + +{ +/* -------------------------------------------------------------------- */ +/* Do we have anything to report? */ +/* -------------------------------------------------------------------- */ + if( !psDefn->DefnSet ) + { + fprintf( fp, "No GeoKeys found.\n" ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Get the PCS name if possible. */ +/* -------------------------------------------------------------------- */ + if( psDefn->PCS != KvUserDefined ) + { + char *pszPCSName = NULL; + + GTIFGetPCSInfo( psDefn->PCS, &pszPCSName, NULL, NULL, NULL ); + if( pszPCSName == NULL ) + pszPCSName = CPLStrdup("name unknown"); + + fprintf( fp, "PCS = %d (%s)\n", psDefn->PCS, pszPCSName ); + CPLFree( pszPCSName ); + } + +/* -------------------------------------------------------------------- */ +/* Dump the projection code if possible. */ +/* -------------------------------------------------------------------- */ + if( psDefn->ProjCode != KvUserDefined ) + { + char *pszTRFName = NULL; + + GTIFGetProjTRFInfo( psDefn->ProjCode, &pszTRFName, NULL, NULL ); + if( pszTRFName == NULL ) + pszTRFName = CPLStrdup(""); + + fprintf( fp, "Projection = %d (%s)\n", + psDefn->ProjCode, pszTRFName ); + + CPLFree( pszTRFName ); + } + +/* -------------------------------------------------------------------- */ +/* Try to dump the projection method name, and parameters if possible.*/ +/* -------------------------------------------------------------------- */ + if( psDefn->CTProjection != KvUserDefined ) + { + char *pszName = GTIFValueName(ProjCoordTransGeoKey, + psDefn->CTProjection); + int i; + + if( pszName == NULL ) + pszName = "(unknown)"; + + fprintf( fp, "Projection Method: %s\n", pszName ); + + for( i = 0; i < psDefn->nParms; i++ ) + { + if( psDefn->ProjParmId[i] == 0 ) + continue; + + pszName = GTIFKeyName((geokey_t) psDefn->ProjParmId[i]); + if( pszName == NULL ) + pszName = "(unknown)"; + + if( i < 4 ) + { + char *pszAxisName; + + if( strstr(pszName,"Long") != NULL ) + pszAxisName = "Long"; + else if( strstr(pszName,"Lat") != NULL ) + pszAxisName = "Lat"; + else + pszAxisName = "?"; + + fprintf( fp, " %s: %f (%s)\n", + pszName, psDefn->ProjParm[i], + GTIFDecToDMS( psDefn->ProjParm[i], pszAxisName, 2 ) ); + } + else if( i == 4 ) + fprintf( fp, " %s: %f\n", pszName, psDefn->ProjParm[i] ); + else + fprintf( fp, " %s: %f m\n", pszName, psDefn->ProjParm[i] ); + } + } + +/* -------------------------------------------------------------------- */ +/* Report the GCS name, and number. */ +/* -------------------------------------------------------------------- */ + if( psDefn->GCS != KvUserDefined ) + { + char *pszName = NULL; + + GTIFGetGCSInfo( psDefn->GCS, &pszName, NULL, NULL, NULL ); + if( pszName == NULL ) + pszName = CPLStrdup("(unknown)"); + + fprintf( fp, "GCS: %d/%s\n", psDefn->GCS, pszName ); + CPLFree( pszName ); + } + +/* -------------------------------------------------------------------- */ +/* Report the datum name. */ +/* -------------------------------------------------------------------- */ + if( psDefn->Datum != KvUserDefined ) + { + char *pszName = NULL; + + GTIFGetDatumInfo( psDefn->Datum, &pszName, NULL ); + if( pszName == NULL ) + pszName = CPLStrdup("(unknown)"); + + fprintf( fp, "Datum: %d/%s\n", psDefn->Datum, pszName ); + CPLFree( pszName ); + } + +/* -------------------------------------------------------------------- */ +/* Report the ellipsoid. */ +/* -------------------------------------------------------------------- */ + if( psDefn->Ellipsoid != KvUserDefined ) + { + char *pszName = NULL; + + GTIFGetEllipsoidInfo( psDefn->Ellipsoid, &pszName, NULL, NULL ); + if( pszName == NULL ) + pszName = CPLStrdup("(unknown)"); + + fprintf( fp, "Ellipsoid: %d/%s (%.2f,%.2f)\n", + psDefn->Ellipsoid, pszName, + psDefn->SemiMajor, psDefn->SemiMinor ); + CPLFree( pszName ); + } + +/* -------------------------------------------------------------------- */ +/* Report the prime meridian. */ +/* -------------------------------------------------------------------- */ + if( psDefn->PM != KvUserDefined ) + { + char *pszName = NULL; + + GTIFGetPMInfo( psDefn->PM, &pszName, NULL ); + + if( pszName == NULL ) + pszName = CPLStrdup("(unknown)"); + + fprintf( fp, "Prime Meridian: %d/%s (%f/%s)\n", + psDefn->PM, pszName, + psDefn->PMLongToGreenwich, + GTIFDecToDMS( psDefn->PMLongToGreenwich, "Long", 2 ) ); + CPLFree( pszName ); + } + +/* -------------------------------------------------------------------- */ +/* Report TOWGS84 parameters. */ +/* -------------------------------------------------------------------- */ +#if !defined(GEO_NORMALIZE_DISABLE_TOWGS84) + if( psDefn->TOWGS84Count > 0 ) + { + int i; + + fprintf( fp, "TOWGS84: " ); + + for( i = 0; i < psDefn->TOWGS84Count; i++ ) + { + if( i > 0 ) + fprintf( fp, "," ); + fprintf( fp, "%g", psDefn->TOWGS84[i] ); + } + + fprintf( fp, "\n" ); + } +#endif + +/* -------------------------------------------------------------------- */ +/* Report the projection units of measure (currently just */ +/* linear). */ +/* -------------------------------------------------------------------- */ + if( psDefn->UOMLength != KvUserDefined ) + { + char *pszName = NULL; + + GTIFGetUOMLengthInfo( psDefn->UOMLength, &pszName, NULL ); + if( pszName == NULL ) + pszName = CPLStrdup( "(unknown)" ); + + fprintf( fp, "Projection Linear Units: %d/%s (%fm)\n", + psDefn->UOMLength, pszName, psDefn->UOMLengthInMeters ); + CPLFree( pszName ); + } + else + { + fprintf( fp, "Projection Linear Units: User-Defined (%fm)\n", + psDefn->UOMLengthInMeters ); + } +} + +/************************************************************************/ +/* GTIFFreeMemory() */ +/* */ +/* Externally visible function to free memory allocated within */ +/* geo_normalize.c. */ +/************************************************************************/ + +void GTIFFreeMemory( char * pMemory ) + +{ + if( pMemory != NULL ) + VSIFree( pMemory ); +} + +/************************************************************************/ +/* GTIFDeaccessCSV() */ +/* */ +/* Free all cached CSV info. */ +/************************************************************************/ + +void GTIFDeaccessCSV() + +{ + CSVDeaccess( NULL ); +} + +/************************************************************************/ +/* GTIFAllocDefn() */ +/* */ +/* This allocates a GTIF structure in such a way that the */ +/* calling application doesn't need to know the size and */ +/* initializes it appropriately. */ +/************************************************************************/ + +GTIFDefn *GTIFAllocDefn() +{ + return (GTIFDefn *) CPLCalloc(sizeof(GTIFDefn),1); +} + +/************************************************************************/ +/* GTIFFreeDefn() */ +/* */ +/* Free a GTIF structure allocated by GTIFAllocDefn(). */ +/************************************************************************/ + +void GTIFFreeDefn( GTIFDefn *defn ) +{ + VSIFree( defn ); +} diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_normalize.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_normalize.h new file mode 100644 index 000000000..60a4095e1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_normalize.h @@ -0,0 +1,219 @@ +/****************************************************************************** + * $Id: geo_normalize.h 2233 2012-10-09 01:33:11Z warmerdam $ + * + * Project: libgeotiff + * Purpose: Include file related to geo_normalize.c containing Code to + * normalize PCS and other composite codes in a GeoTIFF file. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#ifndef GEO_NORMALIZE_H_INCLUDED +#define GEO_NORMALIZE_H_INCLUDED + +#include +#include "geotiff.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file geo_normalize.h + * + * Include file for extended projection definition normalization api. + */ + +#define MAX_GTIF_PROJPARMS 10 + +/** + * Holds a definition of a coordinate system in normalized form. + */ + +typedef struct { + /** From GTModelTypeGeoKey tag. Can have the values ModelTypeGeographic + or ModelTypeProjected. */ + short Model; + + /** From ProjectedCSTypeGeoKey tag. For example PCS_NAD27_UTM_zone_3N.*/ + short PCS; + + /** From GeographicTypeGeoKey tag. For example GCS_WGS_84 or + GCS_Voirol_1875_Paris. Includes datum and prime meridian value. */ + short GCS; + + /** From ProjLinearUnitsGeoKey. For example Linear_Meter. */ + short UOMLength; + + /** One UOMLength = UOMLengthInMeters meters. */ + double UOMLengthInMeters; + + /** The angular units of the GCS. */ + short UOMAngle; + + /** One UOMAngle = UOMLengthInDegrees degrees. */ + double UOMAngleInDegrees; + + /** Datum from GeogGeodeticDatumGeoKey tag. For example Datum_WGS84 */ + short Datum; + + /** Prime meridian from GeogPrimeMeridianGeoKey. For example PM_Greenwich + or PM_Paris. */ + short PM; + + /** Decimal degrees of longitude between this prime meridian and + Greenwich. Prime meridians to the west of Greenwich are negative. */ + double PMLongToGreenwich; + + /** Ellipsoid identifier from GeogELlipsoidGeoKey. For example + Ellipse_Clarke_1866. */ + short Ellipsoid; + + /** The length of the semi major ellipse axis in meters. */ + double SemiMajor; + + /** The length of the semi minor ellipse axis in meters. */ + double SemiMinor; + + /* this #if is primary intended to maintain binary compatability with older + versions of libgeotiff for MrSID binaries (for example) */ +#if !defined(GEO_NORMALIZE_DISABLE_TOWGS84) + /** TOWGS84 transformation values (0/3/7) */ + short TOWGS84Count; + + /** TOWGS84 transformation values */ + double TOWGS84[7]; +#endif /* !defined(GEO_NORMALIZE_DISABLE_TOWGS84) */ + + /** Projection id from ProjectionGeoKey. For example Proj_UTM_11S. */ + short ProjCode; + + /** EPSG identifier for underlying projection method. From the EPSG + TRF_METHOD table. */ + short Projection; + + /** GeoTIFF identifier for underlying projection method. While some of + these values have corresponding vlaues in EPSG (Projection field), + others do not. For example CT_TransverseMercator. */ + short CTProjection; + + /** Number of projection parameters in ProjParm and ProjParmId. */ + int nParms; + + /** Projection parameter value. The identify of this parameter + is established from the corresponding entry in ProjParmId. The + value will be measured in meters, or decimal degrees if it is a + linear or angular measure. */ + double ProjParm[MAX_GTIF_PROJPARMS]; + + /** Projection parameter identifier. For example ProjFalseEastingGeoKey. + The value will be 0 for unused table entries. */ + int ProjParmId[MAX_GTIF_PROJPARMS]; /* geokey identifier, + eg. ProjFalseEastingGeoKey*/ + + /** Special zone map system code (MapSys_UTM_South, MapSys_UTM_North, + MapSys_State_Plane or KvUserDefined if none apply. */ + int MapSys; + + /** UTM, or State Plane Zone number, zero if not known. */ + int Zone; + + /** Do we have any definition at all? 0 if no geokeys found */ + int DefnSet; + +} GTIFDefn; + +int CPL_DLL GTIFGetPCSInfo( int nPCSCode, char **ppszEPSGName, + short *pnProjOp, + short *pnUOMLengthCode, short *pnGeogCS ); +int CPL_DLL GTIFGetProjTRFInfo( int nProjTRFCode, + char ** ppszProjTRFName, + short * pnProjMethod, + double * padfProjParms ); +int CPL_DLL GTIFGetGCSInfo( int nGCSCode, char **ppszName, + short *pnDatum, short *pnPM, short *pnUOMAngle ); +int CPL_DLL GTIFGetDatumInfo( int nDatumCode, char **ppszName, + short * pnEllipsoid ); +int CPL_DLL GTIFGetEllipsoidInfo( int nEllipsoid, char ** ppszName, + double * pdfSemiMajor, + double * pdfSemiMinor ); +int CPL_DLL GTIFGetPMInfo( int nPM, char **ppszName, + double * pdfLongToGreenwich ); + +double CPL_DLL GTIFAngleStringToDD( const char *pszAngle, int nUOMAngle ); +int CPL_DLL GTIFGetUOMLengthInfo( int nUOMLengthCode, + char **ppszUOMName, + double * pdfInMeters ); +int CPL_DLL GTIFGetUOMAngleInfo( int nUOMAngleCode, + char **ppszUOMName, + double * pdfInDegrees ); +double CPL_DLL GTIFAngleToDD( double dfAngle, int nUOMAngle ); + + +/* this should be used to free strings returned by GTIFGet... funcs */ +void CPL_DLL GTIFFreeMemory( char * ); +void CPL_DLL GTIFDeaccessCSV( void ); + +int CPL_DLL GTIFGetDefn( GTIF *psGTIF, GTIFDefn * psDefn ); +void CPL_DLL GTIFPrintDefn( GTIFDefn *, FILE * ); +GTIFDefn CPL_DLL *GTIFAllocDefn( void ); +void CPL_DLL GTIFFreeDefn( GTIFDefn * ); + +void CPL_DLL SetCSVFilenameHook( const char *(*CSVFileOverride)(const char *) ); + +const char CPL_DLL *GTIFDecToDMS( double, const char *, int ); + +/* + * These are useful for recognising UTM and State Plane, with or without + * CSV files being found. + */ + +#define MapSys_UTM_North -9001 +#define MapSys_UTM_South -9002 +#define MapSys_State_Plane_27 -9003 +#define MapSys_State_Plane_83 -9004 + +int CPL_DLL GTIFMapSysToPCS( int MapSys, int Datum, int nZone ); +int CPL_DLL GTIFMapSysToProj( int MapSys, int nZone ); +int CPL_DLL GTIFPCSToMapSys( int PCSCode, int * pDatum, int * pZone ); +int CPL_DLL GTIFProjToMapSys( int ProjCode, int * pZone ); + +/* + * These are only useful if using libgeotiff with libproj (PROJ.4+). + */ +char CPL_DLL *GTIFGetProj4Defn( GTIFDefn * ); + +int CPL_DLL GTIFProj4ToLatLong( GTIFDefn *, int, double *, double * ); +int CPL_DLL GTIFProj4FromLatLong( GTIFDefn *, int, double *, double * ); + +int CPL_DLL GTIFSetFromProj4( GTIF *gtif, const char *proj4 ); + +#if defined(HAVE_LIBPROJ) && defined(HAVE_PROJECTS_H) +# define HAVE_GTIFPROJ4 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ndef GEO_NORMALIZE_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_print.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_print.c new file mode 100644 index 000000000..bc9d16455 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_print.c @@ -0,0 +1,503 @@ +/********************************************************************** + * + * geo_print.c -- Key-dumping routines for GEOTIFF files. + * + * Written By: Niles D. Ritter. + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + * + * Revision History; + * + * 20 June, 1995 Niles D. Ritter New + * 7 July, 1995 NDR Fix indexing + * 27 July, 1995 NDR Added Import utils + * 28 July, 1995 NDR Made parser more strict. + * 29 Sep, 1995 NDR Fixed matrix printing. + * + **********************************************************************/ + +#include "geotiff.h" /* public interface */ +#include "geo_tiffp.h" /* external TIFF interface */ +#include "geo_keyp.h" /* private interface */ +#include "geokeys.h" + +#include /* for sprintf */ + +#define FMT_GEOTIFF "Geotiff_Information:" +#define FMT_VERSION "Version: %hd" +#define FMT_REV "Key_Revision: %1hd.%hd" +#define FMT_TAGS "Tagged_Information:" +#define FMT_TAGEND "End_Of_Tags." +#define FMT_KEYS "Keyed_Information:" +#define FMT_KEYEND "End_Of_Keys." +#define FMT_GEOEND "End_Of_Geotiff." +#define FMT_DOUBLE "%-17.15g" +#define FMT_SHORT "%-11hd" + +static void DefaultPrint(char *string, void *aux); +static void PrintKey(GeoKey *key, GTIFPrintMethod print,void *aux); +static void PrintGeoTags(GTIF *gtif,GTIFReadMethod scan,void *aux); +static void PrintTag(int tag, int nrows, double *data, int ncols, + GTIFPrintMethod print,void *aux); +static void DefaultRead(char *string, void *aux); +static int ReadKey(GTIF *gt, GTIFReadMethod scan, void *aux); +static int ReadTag(GTIF *gt,GTIFReadMethod scan,void *aux); + +/* + * Print off the directory info, using whatever method is specified + * (defaults to fprintf if null). The "aux" parameter is provided for user + * defined method for passing parameters or whatever. + * + * The output format is a "GeoTIFF meta-data" file, which may be + * used to import information with the GTIFFImport() routine. + */ + +void GTIFPrint(GTIF *gtif, GTIFPrintMethod print,void *aux) +{ + int i; + int numkeys = gtif->gt_num_keys; + GeoKey *key = gtif->gt_keys; + char message[1024]; + + if (!print) print = (GTIFPrintMethod) &DefaultPrint; + if (!aux) aux=stdout; + + sprintf(message,FMT_GEOTIFF "\n"); + print(message,aux); + sprintf(message, "Version: %hd" ,gtif->gt_version); + sprintf(message, FMT_VERSION,gtif->gt_version); + print(" ",aux); print(message,aux); print("\n",aux); + sprintf(message, FMT_REV,gtif->gt_rev_major, + gtif->gt_rev_minor); + print(" ",aux); print(message,aux); print("\n",aux); + + sprintf(message," %s\n",FMT_TAGS); print(message,aux); + PrintGeoTags(gtif,print,aux); + sprintf(message," %s\n",FMT_TAGEND); print(message,aux); + + sprintf(message," %s\n",FMT_KEYS); print(message,aux); + for (i=0; igt_tif; + + if( tif == NULL ) + return; + + if ((gt->gt_methods.get)(tif, GTIFF_TIEPOINTS, &count, &data )) + PrintTag(GTIFF_TIEPOINTS,count/3, data, 3, print, aux); + if ((gt->gt_methods.get)(tif, GTIFF_PIXELSCALE, &count, &data )) + PrintTag(GTIFF_PIXELSCALE,count/3, data, 3, print, aux); + if ((gt->gt_methods.get)(tif, GTIFF_TRANSMATRIX, &count, &data )) + PrintTag(GTIFF_TRANSMATRIX,count/4, data, 4, print, aux); +} + +static void PrintTag(int tag, int nrows, double *dptr, int ncols, + GTIFPrintMethod print,void *aux) +{ + int i,j; + double *data=dptr; + char message[1024]; + + print(" ",aux); + print(GTIFTagName(tag),aux); + sprintf(message," (%d,%d):\n",nrows,ncols); + print(message,aux); + for (i=0;igk_key; + int count = key->gk_count; + int vals_now,i; + pinfo_t *sptr; + double *dptr; + char message[40]; + + print(" ",aux); + print(GTIFKeyName(keyid),aux); + + sprintf(message," (%s,%d): ",GTIFTypeName(key->gk_type),count); + print(message,aux); + + if (key->gk_type==TYPE_SHORT && count==1) + data = (char *)&key->gk_data; + else + data = key->gk_data; + + switch (key->gk_type) + { + case TYPE_ASCII: + { + int in_char, out_char; + + print("\"",aux); + + in_char = 0; + out_char = 0; + while( in_char < count-1 ) + { + char ch = ((char *) data)[in_char++]; + + if( ch == '\n' ) + { + message[out_char++] = '\\'; + message[out_char++] = 'n'; + } + else if( ch == '\\' ) + { + message[out_char++] = '\\'; + message[out_char++] = '\\'; + } + else + message[out_char++] = ch; + + /* flush message if buffer full */ + if( (size_t)out_char >= sizeof(message)-3 ) + { + message[out_char] = '\0'; + print(message,aux); + out_char = 0; + } + } + + message[out_char]='\0'; + print(message,aux); + + print("\"\n",aux); + } + break; + + case TYPE_DOUBLE: + for (dptr = (double *)data; count > 0; count-= vals_now) + { + vals_now = count > 3? 3: count; + for (i=0; i 0 ) + print( "****Corrupted data****\n", aux ); + else + { + for (; count > 0; count-= vals_now) + { + vals_now = count > 3? 3: count; + for (i=0; igk_type); + print(message,aux); + break; + } +} + +static void DefaultPrint(char *string, void *aux) +{ + /* Pretty boring */ + fprintf((FILE *)aux,"%s",string); +} + + +/* + * Importing metadata file + */ + +/* + * Import the directory info, using whatever method is specified + * (defaults to fscanf if null). The "aux" parameter is provided for user + * defined method for passing file or whatever. + * + * The input format is a "GeoTIFF meta-data" file, which may be + * generated by the GTIFFPrint() routine. + */ + +int GTIFImport(GTIF *gtif, GTIFReadMethod scan,void *aux) +{ + int status; + char message[1024]; + + if (!scan) scan = (GTIFReadMethod) &DefaultRead; + if (!aux) aux=stdin; + + scan(message,aux); + if (strncmp(message,FMT_GEOTIFF,8)) return 0; + scan(message,aux); + if (!sscanf(message,FMT_VERSION,(short int*)>if->gt_version)) return 0; + scan(message,aux); + if (sscanf(message,FMT_REV,(short int*)>if->gt_rev_major, + (short int*)>if->gt_rev_minor) !=2) return 0; + + scan(message,aux); + if (strncmp(message,FMT_TAGS,8)) return 0; + while ((status=ReadTag(gtif,scan,aux))>0); + if (status < 0) return 0; + + scan(message,aux); + if (strncmp(message,FMT_KEYS,8)) return 0; + while ((status=ReadKey(gtif,scan,aux))>0); + + return (status==0); /* success */ +} + +static int StringError(char *string) +{ + fprintf(stderr,"Parsing Error at \'%s\'\n",string); + return -1; +} + +#define SKIPWHITE(vptr) \ + while (*vptr && (*vptr==' '||*vptr=='\t')) vptr++ +#define FINDCHAR(vptr,c) \ + while (*vptr && *vptr!=(c)) vptr++ + +static int ReadTag(GTIF *gt,GTIFReadMethod scan,void *aux) +{ + int i,j,tag; + char *vptr; + char tagname[100]; + double *data,*dptr; + int count,nrows,ncols,num; + char message[1024]; + + scan(message,aux); + if (!strncmp(message,FMT_TAGEND,8)) return 0; + + num=sscanf(message,"%[^( ] (%d,%d):\n",tagname,&nrows,&ncols); + if (num!=3) return StringError(message); + + tag = GTIFTagCode(tagname); + if (tag < 0) return StringError(tagname); + + count = nrows*ncols; + + data = (double *) _GTIFcalloc(count * sizeof(double)); + dptr = data; + + for (i=0;igt_methods.set)(gt->gt_tif, (pinfo_t) tag, count, data ); + + _GTIFFree( data ); + + return 1; +} + + +static int ReadKey(GTIF *gt, GTIFReadMethod scan, void *aux) +{ + tagtype_t ktype; + int count,outcount; + int vals_now,i; + geokey_t key; + int icode; + pinfo_t code; + short *sptr; + char name[1000]; + char type[20]; + double data[100]; + double *dptr; + char *vptr; + int num; + char message[2048]; + + scan(message,aux); + if (!strncmp(message,FMT_KEYEND,8)) return 0; + + num=sscanf(message,"%[^( ] (%[^,],%d):\n",name,type,&count); + if (num!=3) return StringError(message); + + vptr = message; + FINDCHAR(vptr,':'); + if (!*vptr) return StringError(message); + vptr+=2; + + if( GTIFKeyCode(name) < 0 ) + return StringError(name); + else + key = (geokey_t) GTIFKeyCode(name); + + if( GTIFTypeCode(type) < 0 ) + return StringError(type); + else + ktype = (tagtype_t) GTIFTypeCode(type); + + /* skip white space */ + SKIPWHITE(vptr); + if (!*vptr) return StringError(message); + + switch (ktype) + { + case TYPE_ASCII: + { + char *cdata; + int out_char = 0; + + FINDCHAR(vptr,'"'); + if (!*vptr) return StringError(message); + + cdata = (char *) _GTIFcalloc( count+1 ); + + vptr++; + while( out_char < count-1 ) + { + if( *vptr == '\0' ) + break; + + else if( vptr[0] == '\\' && vptr[1] == 'n' ) + { + cdata[out_char++] = '\n'; + vptr += 2; + } + else if( vptr[0] == '\\' && vptr[1] == '\\' ) + { + cdata[out_char++] = '\\'; + vptr += 2; + } + else + cdata[out_char++] = *(vptr++); + } + + if( out_char < count-1 ) return StringError(message); + if( *vptr != '"' ) return StringError(message); + + cdata[count-1] = '\0'; + GTIFKeySet(gt,key,ktype,count,cdata); + + _GTIFFree( cdata ); + } + break; + + case TYPE_DOUBLE: + outcount = count; + for (dptr = data; count > 0; count-= vals_now) + { + vals_now = count > 3? 3: count; + for (i=0; i 0; count-= vals_now) + { + vals_now = count > 3? 3: count; + for (i=0; i + +/** +This function writes a geokey_t value to a GeoTIFF file. + +@param gtif The geotiff information handle from GTIFNew(). + +@param keyID The geokey_t name (such as ProjectedCSTypeGeoKey). +This must come from the list of legal geokey_t values +(an enumeration) listed below. + +@param val The val argument is a pointer to the +variable into which the value should be read. The type of the variable +varies depending on the geokey_t given. While there is no ready mapping +of geokey_t values onto types, in general code values are of type short, +citations are strings, and everything else is of type double. Note +that pointer's to int should never be passed to GTIFKeyGet() for +integer values as they will be shorts, and the int's may not be properly +initialized (and will be grossly wrong on MSB systems). + +@param index Indicates how far into the list of values +for this geokey to offset. Should normally be zero. + +@param count Indicates how many values +to read. At this time all keys except for strings have only one value, +so index should be zero, and count should be one.

+ +The key indicates the key name to be written to the +file and should from the geokey_t enumeration +(eg. ProjectedCSTypeGeoKey). The full list of possible geokey_t +values can be found in geokeys.inc, or in the online documentation for +GTIFKeyGet().

+ +The type should be one of TYPE_SHORT, TYPE_ASCII, or TYPE_DOUBLE and +will indicate the type of value being passed at the end of the argument +list (the key value). The count should be one except for strings +when it should be the length of the string (or zero to for this to be +computed internally). As a special case a count of -1 can be +used to request an existing key be deleted, in which no value is passed.

+ +The actual value is passed at the end of the argument list, and should be +a short, a double, or a char * value. Note that short and double values +are passed by value rather than as pointers when count is 1, but as pointers +if count is larger than 1.

+ +Note that key values aren't actually flushed to the file until +GTIFWriteKeys() is called. Till then +the new values are just kept with the GTIF structure.

+ +Example:

+ +

+    GTIFKeySet(gtif, GTRasterTypeGeoKey, TYPE_SHORT, 1, 
+               RasterPixelIsArea);
+    GTIFKeySet(gtif, GTCitationGeoKey, TYPE_ASCII, 0, 
+               "UTM 11 North / NAD27" );
+
+ + */ + +int GTIFKeySet(GTIF *gtif, geokey_t keyID, tagtype_t type, int count,...) +{ + va_list ap; + int index = gtif->gt_keyindex[ keyID ]; + int newvalues = 0; + GeoKey *key; + char *data = NULL; + char *val = NULL; + pinfo_t sval; + double dval; + + va_start(ap, count); + /* pass singleton keys by value */ + if (count>1 && type!=TYPE_ASCII) + { + val = va_arg(ap, char*); + } + else if( count == -1 ) + { + /* delete the indicated tag */ + va_end(ap); + + if( index < 1 ) + return 0; + + if (gtif->gt_keys[index].gk_type == TYPE_ASCII) + { + _GTIFFree (gtif->gt_keys[index].gk_data); + } + + while( index < gtif->gt_num_keys ) + { + _GTIFmemcpy( gtif->gt_keys + index, + gtif->gt_keys + index + 1, + sizeof(GeoKey) ); + gtif->gt_keyindex[gtif->gt_keys[index].gk_key] = index; + index++; + } + + gtif->gt_num_keys--; + gtif->gt_nshorts -= sizeof(KeyEntry)/sizeof(pinfo_t); + gtif->gt_keyindex[keyID] = 0; + gtif->gt_flags |= FLAG_FILE_MODIFIED; + + return 1; + } + else switch (type) + { + case TYPE_SHORT: sval=(pinfo_t) va_arg(ap, int); val=(char *)&sval; break; + case TYPE_DOUBLE: dval=va_arg(ap, dblparam_t); val=(char *)&dval; break; + case TYPE_ASCII: + val=va_arg(ap, char*); + count = strlen(val) + 1; /* force = string length */ + break; + default: + assert( FALSE ); + break; + } + va_end(ap); + + /* We assume here that there are no multi-valued SHORTS ! */ + if (index) + { + /* Key already exists */ + key = gtif->gt_keys+index; + if (type!=key->gk_type || count > key->gk_count) + { + /* need to reset data pointer */ + key->gk_type = type; + key->gk_count = count; + key->gk_size = _gtiff_size[ type ]; + newvalues = 1; + } + } + else + { + /* We need to create the key */ + if (gtif->gt_num_keys == MAX_KEYS) return 0; + key = gtif->gt_keys + ++gtif->gt_num_keys; + index = gtif->gt_num_keys; + gtif->gt_keyindex[ keyID ] = index; + key->gk_key = keyID; + key->gk_type = type; + key->gk_count = count; + key->gk_size = _gtiff_size[ type ]; + if ((geokey_t)gtif->gt_keymin > keyID) gtif->gt_keymin=keyID; + if ((geokey_t)gtif->gt_keymax < keyID) gtif->gt_keymax=keyID; + newvalues = 1; + } + + if (newvalues) + { + switch (type) + { + case TYPE_SHORT: + if (count > 1) return 0; + data = (char *)&key->gk_data; /* store value *in* data */ + break; + case TYPE_DOUBLE: + key->gk_data = (char *)(gtif->gt_double + gtif->gt_ndoubles); + data = key->gk_data; + gtif->gt_ndoubles += count; + break; + case TYPE_ASCII: + break; + default: + va_end(ap); + return 0; + } + gtif->gt_nshorts += sizeof(KeyEntry)/sizeof(pinfo_t); + } + + /* this fixes a bug where if a request is made to write a duplicate + key, we must initialize the data to a valid value. + Bryan Wells (bryan@athena.bangor.autometric.com) */ + + else /* no new values, but still have something to write */ + { + switch (type) + { + case TYPE_SHORT: + if (count > 1) return 0; + data = (char *)&key->gk_data; /* store value *in* data */ + break; + case TYPE_DOUBLE: + data = key->gk_data; + break; + case TYPE_ASCII: + break; + default: + return 0; + } + } + + switch (type) + { + case TYPE_ASCII: + /* throw away existing data and allocate room for new data */ + if (key->gk_data != 0) + { + _GTIFFree(key->gk_data); + } + key->gk_data = (char *)_GTIFcalloc(count); + key->gk_count = count; + data = key->gk_data; + break; + default: + break; + } + + _GTIFmemcpy(data, val, count*key->gk_size); + + gtif->gt_flags |= FLAG_FILE_MODIFIED; + return 1; +} diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_simpletags.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_simpletags.c new file mode 100644 index 000000000..e001782b0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_simpletags.c @@ -0,0 +1,262 @@ +/****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * geo_simpletags.c TIFF Interface module that just keeps track of the + * tags in memory, without depending on libtiff. + * + *****************************************************************************/ + +#include "geotiff.h" /* public GTIFF interface */ +#include "geo_simpletags.h" + +#include "geo_tiffp.h" /* Private TIFF interface */ +#include "geo_keyp.h" /* Private GTIFF interface */ + +static int ST_TypeSize( int st_type ); + +static int _GTIFGetField (tiff_t *tif, pinfo_t tag, int *count, void *value ); +static int _GTIFSetField (tiff_t *tif, pinfo_t tag, int count, void *value ); +static tagtype_t _GTIFTagType (tiff_t *tif, pinfo_t tag); + +/* + * Set up default TIFF handlers. + */ +void GTIFSetSimpleTagsMethods(TIFFMethod *method) +{ + if (!method) return; + + method->get = _GTIFGetField; + method->set = _GTIFSetField; + method->type = _GTIFTagType; +} + +/* returns the value of TIFF tag , or if + * the value is an array, returns an allocated buffer + * containing the values. Allocate a copy of the actual + * buffer, sized up for updating. + */ +static int _GTIFGetField (tiff_t *tif, pinfo_t tag, int *count, void *val ) +{ + int item_size, data_type; + void *internal_value, *ret_value; + + if( !ST_GetKey( (ST_TIFF*) tif, (int) tag, count, &data_type, + &internal_value ) ) + return 0; + + if( data_type != ST_TagType( tag ) ) + return 0; + + item_size = ST_TypeSize( data_type ); + + ret_value = (char *)_GTIFcalloc( *count * item_size ); + if (!ret_value) return 0; + + _TIFFmemcpy( ret_value, internal_value, item_size * *count ); + + *(void **)val = ret_value; + return 1; +} + +/* + * Set a GeoTIFF TIFF field. + */ +static int _GTIFSetField (tiff_t *tif, pinfo_t tag, int count, void *value ) +{ + int st_type = ST_TagType( tag ); + + return ST_SetKey( (ST_TIFF *) tif, (int) tag, count, st_type, value ); +} + +/* + * This routine is supposed to return the TagType of the + * TIFF tag. Unfortunately, "libtiff" does not provide this + * service by default, so we just have to "know" what type of tags + * we've got, and how many. We only define the ones Geotiff + * uses here, and others return UNKNOWN. The "tif" parameter + * is provided for those TIFF implementations that provide + * for tag-type queries. + */ +static tagtype_t _GTIFTagType (tiff_t *tif, pinfo_t tag) +{ + tagtype_t ttype; + + (void) tif; /* dummy reference */ + + switch (tag) + { + case GTIFF_ASCIIPARAMS: ttype=TYPE_ASCII; break; + case GTIFF_PIXELSCALE: + case GTIFF_TRANSMATRIX: + case GTIFF_TIEPOINTS: + case GTIFF_DOUBLEPARAMS: ttype=TYPE_DOUBLE; break; + case GTIFF_GEOKEYDIRECTORY: ttype=TYPE_SHORT; break; + default: ttype = TYPE_UNKNOWN; + } + + return ttype; +} + +/************************************************************************/ +/* ST_TagType() */ +/************************************************************************/ + +int ST_TagType( int tag ) +{ + switch (tag) + { + case GTIFF_ASCIIPARAMS: + return STT_ASCII; + + case GTIFF_PIXELSCALE: + case GTIFF_TRANSMATRIX: + case GTIFF_TIEPOINTS: + case GTIFF_DOUBLEPARAMS: + return STT_DOUBLE; + + case GTIFF_GEOKEYDIRECTORY: + return STT_SHORT; + } + + return -1; +} + + +/************************************************************************/ +/* ST_TypeSize() */ +/************************************************************************/ + +static int ST_TypeSize( int st_type ) + +{ + if( st_type == STT_ASCII ) + return 1; + else if( st_type == STT_SHORT ) + return 2; + else if( st_type == STT_DOUBLE ) + return 8; + else + return 8; +} + +/************************************************************************/ +/* ST_Create() */ +/************************************************************************/ + +ST_TIFF *ST_Create() + +{ + return (ST_TIFF *) calloc(1,sizeof(ST_TIFF)); +} + +/************************************************************************/ +/* ST_Destroy() */ +/************************************************************************/ + +void ST_Destroy( ST_TIFF *st ) + +{ + int i; + + for( i = 0; i < st->key_count; i++ ) + free( st->key_list[i].data ); + + if( st->key_list ) + free( st->key_list ); + free( st ); +} + +/************************************************************************/ +/* ST_SetKey() */ +/************************************************************************/ + +int ST_SetKey( ST_TIFF *st, int tag, int count, int st_type, void *data ) + +{ + int i, item_size = ST_TypeSize( st_type ); + +/* -------------------------------------------------------------------- */ +/* We should compute the length if we were not given a count */ +/* -------------------------------------------------------------------- */ + if (count == 0 && st_type == STT_ASCII ) + { + count = strlen((char*)data)+1; + } + +/* -------------------------------------------------------------------- */ +/* If we already have a value for this tag, replace it. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < st->key_count; i++ ) + { + if( st->key_list[i].tag == tag ) + { + free( st->key_list[i].data ); + st->key_list[i].count = count; + st->key_list[i].type = st_type; + st->key_list[i].data = malloc(item_size*count); + memcpy( st->key_list[i].data, data, count * item_size ); + return 1; + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise, add a new entry. */ +/* -------------------------------------------------------------------- */ + st->key_count++; + st->key_list = (ST_KEY *) realloc(st->key_list, + sizeof(ST_KEY) * st->key_count); + st->key_list[st->key_count-1].tag = tag; + st->key_list[st->key_count-1].count = count; + st->key_list[st->key_count-1].type = st_type; + st->key_list[st->key_count-1].data = malloc(item_size * count); + memcpy( st->key_list[st->key_count-1].data, data, item_size * count ); + + return 1; +} + +/************************************************************************/ +/* ST_GetKey() */ +/************************************************************************/ + +int ST_GetKey( ST_TIFF *st, int tag, int *count, + int *st_type, void **data_ptr ) + +{ + int i; + + for( i = 0; i < st->key_count; i++ ) + { + if( st->key_list[i].tag == tag ) + { + if( count ) + *count = st->key_list[i].count; + if( st_type ) + *st_type = st->key_list[i].type; + if( data_ptr ) + *data_ptr = st->key_list[i].data; + return 1; + } + } + + return 0; +} + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_simpletags.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_simpletags.h new file mode 100644 index 000000000..666578ea4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_simpletags.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ***************************************************************************** + * + * geo_simpletags.h + * + * Provides interface for a "simple tags io in memory" mechanism + * as an alternative to accessing a real tiff file using libtiff. + * + ****************************************************************************/ + +#ifndef __geo_simpletags_h_ +#define __geo_simpletags_h_ + +#include "geotiff.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define STT_SHORT 1 +#define STT_DOUBLE 2 +#define STT_ASCII 3 + +typedef struct { + int tag; + int count; + int type; + void *data; +} ST_KEY; + +typedef struct { + int key_count; + ST_KEY *key_list; +} ST_TIFF; + +typedef void *STIFF; + +void CPL_DLL GTIFSetSimpleTagsMethods(TIFFMethod *method); + +int CPL_DLL ST_SetKey( ST_TIFF *, int tag, int count, + int st_type, void *data ); +int CPL_DLL ST_GetKey( ST_TIFF *, int tag, int *count, + int *st_type, void **data_ptr ); + +ST_TIFF CPL_DLL *ST_Create(); +void CPL_DLL ST_Destroy( ST_TIFF * ); + +int CPL_DLL ST_TagType( int tag ); + +#if defined(__cplusplus) +} +#endif + +#endif /* __geo_simpletags_h_ */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_tiffp.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_tiffp.c new file mode 100644 index 000000000..63ba284b4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_tiffp.c @@ -0,0 +1,145 @@ +/********************************************************************** + * + * geo_tiffp.c Private TIFF interface module for GEOTIFF + * + * This module implements the interface between the GEOTIFF + * tag parser and the TIFF i/o module. The current setup + * relies on the "libtiff" code, but if you use your own + * TIFF reader software, you may replace the module implementations + * here with your own calls. No "libtiff" dependencies occur + * anywhere else in this code. + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + * + **********************************************************************/ + +#include "geotiff.h" /* public GTIFF interface */ + +#include "geo_tiffp.h" /* Private TIFF interface */ +#include "geo_keyp.h" /* Private GTIFF interface */ + +/* tiff size array global */ +gsize_t _gtiff_size[] = { 0, 1, 2, 4, 8, 1, 4, 8, 1, 2, 4, 1 }; + +static int _GTIFGetField (tiff_t *tif, pinfo_t tag, int *count, void *value ); +static int _GTIFSetField (tiff_t *tif, pinfo_t tag, int count, void *value ); +static tagtype_t _GTIFTagType (tiff_t *tif, pinfo_t tag); + +/* + * Set up default TIFF handlers. + */ +void _GTIFSetDefaultTIFF(TIFFMethod *method) +{ + if (!method) return; + + method->get = _GTIFGetField; + method->set = _GTIFSetField; + method->type = _GTIFTagType; +} + +gdata_t _GTIFcalloc(gsize_t size) +{ + gdata_t data=(gdata_t)_TIFFmalloc((tsize_t)size); + if (data) _TIFFmemset((tdata_t)data,0,(tsize_t)size); + return data; +} + +gdata_t _GTIFrealloc(gdata_t ptr, gsize_t size) +{ + return( _TIFFrealloc((tdata_t)ptr, (tsize_t) size) ); +} + +void _GTIFmemcpy(gdata_t out,gdata_t in,gsize_t size) +{ + _TIFFmemcpy((tdata_t)out,(tdata_t)in,(tsize_t)size); +} + +void _GTIFFree(gdata_t data) +{ + if (data) _TIFFfree((tdata_t)data); +} + + + +/* returns the value of TIFF tag , or if + * the value is an array, returns an allocated buffer + * containing the values. Allocate a copy of the actual + * buffer, sized up for updating. + */ +static int _GTIFGetField (tiff_t *tif, pinfo_t tag, int *count, void *val ) +{ + int status; + unsigned short scount=0; + char *tmp; + char *value; + gsize_t size = _gtiff_size[_GTIFTagType (tif,tag)]; + + if (_GTIFTagType(tif, tag) == TYPE_ASCII) + { + status = TIFFGetField((TIFF *)tif,tag,&tmp); + if (!status) return status; + scount = (unsigned short) (strlen(tmp)+1); + } + else status = TIFFGetField((TIFF *)tif,tag,&scount,&tmp); + if (!status) return status; + + *count = scount; + + value = (char *)_GTIFcalloc( (scount+MAX_VALUES)*size); + if (!value) return 0; + + _TIFFmemcpy( value, tmp, size * scount); + + *(char **)val = value; + return status; +} + +/* + * Set a GeoTIFF TIFF field. + */ +static int _GTIFSetField (tiff_t *tif, pinfo_t tag, int count, void *value ) +{ + int status; + unsigned short scount = (unsigned short) count; + + /* libtiff ASCII uses null-delimiter */ + if (_GTIFTagType(tif, tag) == TYPE_ASCII) + status = TIFFSetField((TIFF *)tif,tag,value); + else + status = TIFFSetField((TIFF *)tif,tag,scount,value); + return status; +} + + +/* + * This routine is supposed to return the TagType of the + * TIFF tag. Unfortunately, "libtiff" does not provide this + * service by default, so we just have to "know" what type of tags + * we've got, and how many. We only define the ones Geotiff + * uses here, and others return UNKNOWN. The "tif" parameter + * is provided for those TIFF implementations that provide + * for tag-type queries. + */ +static tagtype_t _GTIFTagType (tiff_t *tif, pinfo_t tag) +{ + tagtype_t ttype; + + (void) tif; /* dummy reference */ + + switch (tag) + { + case GTIFF_ASCIIPARAMS: ttype=TYPE_ASCII; break; + case GTIFF_PIXELSCALE: + case GTIFF_TRANSMATRIX: + case GTIFF_TIEPOINTS: + case GTIFF_DOUBLEPARAMS: ttype=TYPE_DOUBLE; break; + case GTIFF_GEOKEYDIRECTORY: ttype=TYPE_SHORT; break; + default: ttype = TYPE_UNKNOWN; + } + + return ttype; +} + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_tiffp.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_tiffp.h new file mode 100644 index 000000000..0e56cde0d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_tiffp.h @@ -0,0 +1,113 @@ +/********************************************************************** + * + * geo_tiffp.h - Private interface for TIFF tag parsing. + * + * Written by: Niles D. Ritter + * + * This interface file encapsulates the interface to external TIFF + * file-io routines and definitions. The current configuration + * assumes that the "libtiff" module is used, but if you have your + * own TIFF reader, you may replace the definitions with your own + * here, and replace the implementations in geo_tiffp.c. No other + * modules have any explicit dependence on external TIFF modules. + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + **********************************************************************/ + +#ifndef __geo_tiffp_h_ +#define __geo_tiffp_h_ + +/********************************************************************** + * + * Private includes + * + * If you are not using libtiff and XTIFF, replace this include file + * with the appropriate one for your own TIFF parsing routines. + * + * Revision History + * + * 19 September 1995 ndr Demoted Intergraph trans matrix. + * + **********************************************************************/ + +#include "geotiff.h" +#include "xtiffio.h" +#include "cpl_serv.h" + +/* + * dblparam_t is the type that a double precision + * floating point value will have on the parameter + * stack (when coerced by the compiler). You shouldn't + * have to change this. + */ +#ifdef applec +typedef extended dblparam_t; +#else +typedef double dblparam_t; +#endif + + +/********************************************************************** + * + * Private defines + * + * If you are not using "libtiff"/LIBXTIFF, replace these definitions + * with the appropriate definitions to access the geo-tags + * + **********************************************************************/ + +typedef unsigned short pinfo_t; /* SHORT ProjectionInfo tag type */ +typedef TIFF tiff_t; /* TIFF file descriptor */ +typedef tdata_t gdata_t; /* pointer to data */ +typedef tsize_t gsize_t; /* data allocation size */ + +#define GTIFF_GEOKEYDIRECTORY TIFFTAG_GEOKEYDIRECTORY /* from xtiffio.h */ +#define GTIFF_DOUBLEPARAMS TIFFTAG_GEODOUBLEPARAMS +#define GTIFF_ASCIIPARAMS TIFFTAG_GEOASCIIPARAMS +#define GTIFF_PIXELSCALE TIFFTAG_GEOPIXELSCALE +#define GTIFF_TRANSMATRIX TIFFTAG_GEOTRANSMATRIX +#define GTIFF_INTERGRAPH_MATRIX TIFFTAG_INTERGRAPH_MATRIX +#define GTIFF_TIEPOINTS TIFFTAG_GEOTIEPOINTS +#define GTIFF_LOCAL 0 + +#if defined(__cplusplus) +extern "C" { +#endif + +/* + * Method function pointer types + */ +typedef int (*GTGetFunction) (tiff_t *tif, pinfo_t tag, int *count, void *value ); +typedef int (*GTSetFunction) (tiff_t *tif, pinfo_t tag, int count, void *value ); +typedef tagtype_t (*GTTypeFunction) (tiff_t *tif, pinfo_t tag); +typedef struct _TIFFMethod { + GTGetFunction get; + GTSetFunction set; + GTTypeFunction type; +} TIFFMethod_t; + +/********************************************************************** + * + * Protected Function Declarations + * + * These routines are exposed implementations, and should not + * be used by external GEOTIFF client programs. + * + **********************************************************************/ + +extern gsize_t _gtiff_size[]; /* TIFF data sizes */ +extern void CPL_DLL _GTIFSetDefaultTIFF(TIFFMethod *method); +extern gdata_t CPL_DLL _GTIFcalloc(gsize_t); +extern gdata_t CPL_DLL _GTIFrealloc(gdata_t,gsize_t); +extern void CPL_DLL _GTIFFree(gdata_t data); +extern void CPL_DLL _GTIFmemcpy(gdata_t out,gdata_t in,gsize_t size); + +#if defined(__cplusplus) +} +#endif + + +#endif /* __geo_tiffp_h_ */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_trans.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_trans.c new file mode 100644 index 000000000..0ea910730 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_trans.c @@ -0,0 +1,299 @@ +/****************************************************************************** + * $Id: geo_trans.c 1568 2009-04-22 21:10:55Z warmerdam $ + * + * Project: libgeotiff + * Purpose: Code to abstract translation between pixel/line and PCS + * coordinates. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "geotiff.h" +#include "geo_tiffp.h" /* external TIFF interface */ +#include "geo_keyp.h" /* private interface */ +#include "geokeys.h" + +/************************************************************************/ +/* inv_geotransform() */ +/* */ +/* Invert a 6 term geotransform style matrix. */ +/************************************************************************/ + +static int inv_geotransform( double *gt_in, double *gt_out ) + +{ + double det, inv_det; + + /* we assume a 3rd row that is [0 0 1] */ + + /* Compute determinate */ + + det = gt_in[0] * gt_in[4] - gt_in[1] * gt_in[3]; + + if( fabs(det) < 0.000000000000001 ) + return 0; + + inv_det = 1.0 / det; + + /* compute adjoint, and devide by determinate */ + + gt_out[0] = gt_in[4] * inv_det; + gt_out[3] = -gt_in[3] * inv_det; + + gt_out[1] = -gt_in[1] * inv_det; + gt_out[4] = gt_in[0] * inv_det; + + gt_out[2] = ( gt_in[1] * gt_in[5] - gt_in[2] * gt_in[4]) * inv_det; + gt_out[5] = (-gt_in[0] * gt_in[5] + gt_in[2] * gt_in[3]) * inv_det; + + return 1; +} + +/************************************************************************/ +/* GTIFTiepointTranslate() */ +/************************************************************************/ + +int GTIFTiepointTranslate( int gcp_count, double * gcps_in, double * gcps_out, + double x_in, double y_in, + double *x_out, double *y_out ) + +{ + (void) gcp_count; + (void) gcps_in; + (void) gcps_out; + (void) x_in; + (void) y_in; + (void) x_out; + (void) y_out; + + /* I would appreciate a _brief_ block of code for doing second order + polynomial regression here! */ + return FALSE; +} + + +/************************************************************************/ +/* GTIFImageToPCS() */ +/************************************************************************/ + +/** + * Translate a pixel/line coordinate to projection coordinates. + * + * At this time this function does not support image to PCS translations for + * tiepoints-only definitions, only pixelscale and transformation matrix + * formulations. + * + * @param gtif The handle from GTIFNew() indicating the target file. + * @param x A pointer to the double containing the pixel offset on input, + * and into which the easting/longitude will be put on completion. + * @param y A pointer to the double containing the line offset on input, + * and into which the northing/latitude will be put on completion. + * + * @return TRUE if the transformation succeeds, or FALSE if it fails. It may + * fail if the file doesn't have properly setup transformation information, + * or it is in a form unsupported by this function. + */ + +int GTIFImageToPCS( GTIF *gtif, double *x, double *y ) + +{ + int res = FALSE; + int tiepoint_count, count, transform_count; + tiff_t *tif=gtif->gt_tif; + double *tiepoints = 0; + double *pixel_scale = 0; + double *transform = 0; + + + if (!(gtif->gt_methods.get)(tif, GTIFF_TIEPOINTS, + &tiepoint_count, &tiepoints )) + tiepoint_count = 0; + + if (!(gtif->gt_methods.get)(tif, GTIFF_PIXELSCALE, &count, &pixel_scale )) + count = 0; + + if (!(gtif->gt_methods.get)(tif, GTIFF_TRANSMATRIX, + &transform_count, &transform )) + transform_count = 0; + +/* -------------------------------------------------------------------- */ +/* If the pixelscale count is zero, but we have tiepoints use */ +/* the tiepoint based approach. */ +/* -------------------------------------------------------------------- */ + if( tiepoint_count > 6 && count == 0 ) + { + res = GTIFTiepointTranslate( tiepoint_count / 6, + tiepoints, tiepoints + 3, + *x, *y, x, y ); + } + +/* -------------------------------------------------------------------- */ +/* If we have a transformation matrix, use it. */ +/* -------------------------------------------------------------------- */ + else if( transform_count == 16 ) + { + double x_in = *x, y_in = *y; + + *x = x_in * transform[0] + y_in * transform[1] + transform[3]; + *y = x_in * transform[4] + y_in * transform[5] + transform[7]; + + res = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* For now we require one tie point, and a valid pixel scale. */ +/* -------------------------------------------------------------------- */ + else if( count < 3 || tiepoint_count < 6 ) + { + res = FALSE; + } + + else + { + *x = (*x - tiepoints[0]) * pixel_scale[0] + tiepoints[3]; + *y = (*y - tiepoints[1]) * (-1 * pixel_scale[1]) + tiepoints[4]; + + res = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + if(tiepoints) + _GTIFFree(tiepoints); + if(pixel_scale) + _GTIFFree(pixel_scale); + if(transform) + _GTIFFree(transform); + + return res; +} + +/************************************************************************/ +/* GTIFPCSToImage() */ +/************************************************************************/ + +/** + * Translate a projection coordinate to pixel/line coordinates. + * + * At this time this function does not support PCS to image translations for + * tiepoints-only based definitions, only matrix and pixelscale/tiepoints + * formulations are supposed. + * + * @param gtif The handle from GTIFNew() indicating the target file. + * @param x A pointer to the double containing the pixel offset on input, + * and into which the easting/longitude will be put on completion. + * @param y A pointer to the double containing the line offset on input, + * and into which the northing/latitude will be put on completion. + * + * @return TRUE if the transformation succeeds, or FALSE if it fails. It may + * fail if the file doesn't have properly setup transformation information, + * or it is in a form unsupported by this function. + */ + +int GTIFPCSToImage( GTIF *gtif, double *x, double *y ) + +{ + double *tiepoints = NULL; + int tiepoint_count, count, transform_count = 0; + double *pixel_scale = NULL; + double *transform = NULL; + tiff_t *tif=gtif->gt_tif; + int result = FALSE; + +/* -------------------------------------------------------------------- */ +/* Fetch tiepoints and pixel scale. */ +/* -------------------------------------------------------------------- */ + if (!(gtif->gt_methods.get)(tif, GTIFF_TIEPOINTS, + &tiepoint_count, &tiepoints )) + tiepoint_count = 0; + + if (!(gtif->gt_methods.get)(tif, GTIFF_PIXELSCALE, &count, &pixel_scale )) + count = 0; + + if (!(gtif->gt_methods.get)(tif, GTIFF_TRANSMATRIX, + &transform_count, &transform )) + transform_count = 0; + +/* -------------------------------------------------------------------- */ +/* If the pixelscale count is zero, but we have tiepoints use */ +/* the tiepoint based approach. */ +/* -------------------------------------------------------------------- */ + if( tiepoint_count > 6 && count == 0 ) + { + result = GTIFTiepointTranslate( tiepoint_count / 6, + tiepoints + 3, tiepoints, + *x, *y, x, y ); + } + +/* -------------------------------------------------------------------- */ +/* Handle matrix - convert to "geotransform" format, invert and */ +/* apply. */ +/* -------------------------------------------------------------------- */ + else if( transform_count == 16 ) + { + double x_in = *x, y_in = *y; + double gt_in[6], gt_out[6]; + + gt_in[0] = transform[0]; + gt_in[1] = transform[1]; + gt_in[2] = transform[3]; + gt_in[3] = transform[4]; + gt_in[4] = transform[5]; + gt_in[5] = transform[7]; + + if( !inv_geotransform( gt_in, gt_out ) ) + result = FALSE; + else + { + *x = x_in * gt_out[0] + y_in * gt_out[1] + gt_out[2]; + *y = x_in * gt_out[3] + y_in * gt_out[4] + gt_out[5]; + + result = TRUE; + } + } + +/* -------------------------------------------------------------------- */ +/* For now we require one tie point, and a valid pixel scale. */ +/* -------------------------------------------------------------------- */ + else if( count >= 3 && tiepoint_count >= 6 ) + { + *x = (*x - tiepoints[3]) / pixel_scale[0] + tiepoints[0]; + *y = (*y - tiepoints[4]) / (-1 * pixel_scale[1]) + tiepoints[1]; + + result = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + if(tiepoints) + _GTIFFree(tiepoints); + if(pixel_scale) + _GTIFFree(pixel_scale); + if(transform) + _GTIFFree(transform); + + return result; +} + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_write.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_write.c new file mode 100644 index 000000000..344d4bdec --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geo_write.c @@ -0,0 +1,199 @@ +/********************************************************************** + * + * geo_write.c -- Public routines for GEOTIFF GeoKey access. + * + * Written By: Niles D. Ritter. + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any source code derived therefrom. + * + **********************************************************************/ + +#include "geotiffio.h" /* public interface */ +#include "geo_tiffp.h" /* external TIFF interface */ +#include "geo_keyp.h" /* private interface */ + +static int WriteKey(GTIF* gt, TempKeyData* tempData, + KeyEntry* entptr, GeoKey* keyptr); +static int SortKeys(GTIF* gt,int *sortkeys); + + +/** +This function flushes all the GeoTIFF keys that have been set with the +GTIFKeySet() function into the associated +TIFF file. + +@param gt The GeoTIFF handle returned by GTIFNew. + +GTIFWriteKeys() should be called before +GTIFFree() is used to deallocate a GeoTIFF access handle. + */ + +int GTIFWriteKeys(GTIF *gt) +{ + int i; + GeoKey *keyptr; + KeyEntry *entptr; + KeyHeader *header; + TempKeyData tempData; + int sortkeys[MAX_KEYS]; + + if (!(gt->gt_flags & FLAG_FILE_MODIFIED)) return 1; + + if( gt->gt_tif == NULL ) + return 0; + + tempData.tk_asciiParams = 0; + tempData.tk_asciiParamsLength = 0; + tempData.tk_asciiParamsOffset = 0; + + /* Sort the Keys into numerical order */ + if (!SortKeys(gt,sortkeys)) + { + /* XXX error: a key was not recognized */ + } + + /* Set up header of ProjectionInfo tag */ + header = (KeyHeader *)gt->gt_short; + header->hdr_num_keys = (pinfo_t) gt->gt_num_keys; + header->hdr_version = GvCurrentVersion; + header->hdr_rev_major = GvCurrentRevision; + header->hdr_rev_minor = GvCurrentMinorRev; + + /* Sum up the ASCII tag lengths */ + for (i = 0; i < gt->gt_num_keys; i++) + { + keyptr = gt->gt_keys + sortkeys[i]; + if (keyptr->gk_type == TYPE_ASCII) + { + tempData.tk_asciiParamsLength += keyptr->gk_count; + } + } + if (tempData.tk_asciiParamsLength > 0) + { + tempData.tk_asciiParams = + (char *)_GTIFcalloc(tempData.tk_asciiParamsLength + 1); + tempData.tk_asciiParams[tempData.tk_asciiParamsLength] = '\0'; + } + + /* Set up the rest of SHORT array properly */ + keyptr = gt->gt_keys; + entptr = (KeyEntry*)(gt->gt_short + 4); + for (i=0; i< gt->gt_num_keys; i++,entptr++) + { + if (!WriteKey(gt,&tempData,entptr,keyptr+sortkeys[i])) return 0; + } + + /* Write out the Key Directory */ + (gt->gt_methods.set)(gt->gt_tif, GTIFF_GEOKEYDIRECTORY, gt->gt_nshorts, gt->gt_short ); + + /* Write out the params directories */ + if (gt->gt_ndoubles) + (gt->gt_methods.set)(gt->gt_tif, GTIFF_DOUBLEPARAMS, gt->gt_ndoubles, gt->gt_double ); + if (tempData.tk_asciiParamsLength > 0) + { + /* just to be safe */ + tempData.tk_asciiParams[tempData.tk_asciiParamsLength] = '\0'; + (gt->gt_methods.set)(gt->gt_tif, + GTIFF_ASCIIPARAMS, 0, tempData.tk_asciiParams); + } + + gt->gt_flags &= ~FLAG_FILE_MODIFIED; + + if (tempData.tk_asciiParamsLength > 0) + { + _GTIFFree (tempData.tk_asciiParams); + } + return 1; +} + +/********************************************************************** + * + * Private Routines + * + **********************************************************************/ + +/* + * Given GeoKey, write out the KeyEntry entries, returning 0 if failure. + * This is the exact complement of ReadKey(). + */ + +static int WriteKey(GTIF* gt, TempKeyData* tempData, + KeyEntry* entptr, GeoKey* keyptr) +{ + int count; + + entptr->ent_key = (pinfo_t) keyptr->gk_key; + entptr->ent_count = (pinfo_t) keyptr->gk_count; + count = entptr->ent_count; + + if (count==1 && keyptr->gk_type==TYPE_SHORT) + { + entptr->ent_location = GTIFF_LOCAL; + memcpy(&(entptr->ent_val_offset), &keyptr->gk_data, sizeof(pinfo_t)); + return 1; + } + + switch (keyptr->gk_type) + { + case TYPE_SHORT: + entptr->ent_location = GTIFF_GEOKEYDIRECTORY; + entptr->ent_val_offset = (pinfo_t) + ((pinfo_t*)keyptr->gk_data - gt->gt_short); + break; + case TYPE_DOUBLE: + entptr->ent_location = GTIFF_DOUBLEPARAMS; + entptr->ent_val_offset = (pinfo_t) + ((double*)keyptr->gk_data - gt->gt_double); + break; + case TYPE_ASCII: + entptr->ent_location = GTIFF_ASCIIPARAMS; + entptr->ent_val_offset = (pinfo_t) tempData->tk_asciiParamsOffset; + _GTIFmemcpy (tempData->tk_asciiParams + tempData->tk_asciiParamsOffset + , keyptr->gk_data, keyptr->gk_count); + tempData->tk_asciiParams[tempData->tk_asciiParamsOffset+keyptr->gk_count-1] = '|'; + tempData->tk_asciiParamsOffset += keyptr->gk_count; + break; + default: + return 0; /* failure */ + } + + return 1; /* success */ +} + + +/* + * Numerically sort the GeoKeys. + * We just do a linear search through + * the list and pull out the keys that were set. + */ + +static int SortKeys(GTIF* gt,int *sortkeys) +{ + int i, did_work; + + for( i = 0; i < gt->gt_num_keys; i++ ) + sortkeys[i] = i+1; + + do { /* simple bubble sort */ + did_work = 0; + for( i = 0; i < gt->gt_num_keys-1; i++ ) + { + if( gt->gt_keys[sortkeys[i]].gk_key + > gt->gt_keys[sortkeys[i+1]].gk_key ) + { + /* swap keys in sort list */ + int j = sortkeys[i]; + sortkeys[i] = sortkeys[i+1]; + sortkeys[i+1] = j; + + did_work = 1; + } + } + } while( did_work ); + + return 1; +} + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geokeys.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geokeys.h new file mode 100644 index 000000000..b3a14c882 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geokeys.h @@ -0,0 +1,51 @@ +/********************************************************************** + * + * geokeys.h - Public registry for valid GEOTIFF GeoKeys. + * + * Written By: Niles D. Ritter + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + **********************************************************************/ + +#ifndef __geokeys_h_ +#define __geokeys_h_ + +/* The GvCurrentRevision number should be incremented whenever a + * new set of Keys are defined or modified in "geokeys.inc", and comments + * added to the "Revision History" section above. If only code + * _values_ are augmented, the "GvCurrentMinorRev" number should + * be incremented instead (see "geovalues.h"). Whenever the + * GvCurrentRevision is incremented, the GvCurrentMinorRev should + * be reset to zero. + * + * + * The Section Numbers below refer to the GeoTIFF Spec sections + * in which these values are documented. + * + */ +#define GvCurrentRevision 1 /* Final 1.0 Release */ + +#ifdef ValuePair +# undef ValuePair +#endif +#define ValuePair(name,value) name = value, + +typedef enum { + BaseGeoKey = 1024, /* First valid code */ + +# include "geokeys.inc" /* geokey database */ + + ReservedEndGeoKey = 32767, + + /* Key space available for Private or internal use */ + PrivateBaseGeoKey = 32768, /* Consistent with TIFF Private tags */ + PrivateEndGeoKey = 65535, + + EndGeoKey = 65535 /* Largest Possible GeoKey ID */ +} geokey_t; + + +#endif /* __geokeys_h_ */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geokeys.inc b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geokeys.inc new file mode 100644 index 000000000..03be6e0a7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geokeys.inc @@ -0,0 +1,77 @@ +/* GeoTIFF GeoKey Database */ + +/* Note: Any changes/additions to this database require */ +/* a change in the revision value in geokeys.h */ + +/* C database for Geotiff include files. */ +/* the macro ValuePair() must be defined */ +/* by the enclosing include file */ + +/* Revised 28 Sep 1995 NDR -- Added Rev. 1.0 aliases. */ + +/* 6.2.1 GeoTIFF Configuration Keys */ + +ValuePair( GTModelTypeGeoKey, 1024) /* Section 6.3.1.1 Codes */ +ValuePair( GTRasterTypeGeoKey, 1025) /* Section 6.3.1.2 Codes */ +ValuePair( GTCitationGeoKey, 1026) /* documentation */ + +/* 6.2.2 Geographic CS Parameter Keys */ + +ValuePair( GeographicTypeGeoKey, 2048) /* Section 6.3.2.1 Codes */ +ValuePair( GeogCitationGeoKey, 2049) /* documentation */ +ValuePair( GeogGeodeticDatumGeoKey, 2050) /* Section 6.3.2.2 Codes */ +ValuePair( GeogPrimeMeridianGeoKey, 2051) /* Section 6.3.2.4 codes */ +ValuePair( GeogLinearUnitsGeoKey, 2052) /* Section 6.3.1.3 Codes */ +ValuePair( GeogLinearUnitSizeGeoKey, 2053) /* meters */ +ValuePair( GeogAngularUnitsGeoKey, 2054) /* Section 6.3.1.4 Codes */ +ValuePair( GeogAngularUnitSizeGeoKey, 2055) /* radians */ +ValuePair( GeogEllipsoidGeoKey, 2056) /* Section 6.3.2.3 Codes */ +ValuePair( GeogSemiMajorAxisGeoKey, 2057) /* GeogLinearUnits */ +ValuePair( GeogSemiMinorAxisGeoKey, 2058) /* GeogLinearUnits */ +ValuePair( GeogInvFlatteningGeoKey, 2059) /* ratio */ +ValuePair( GeogAzimuthUnitsGeoKey, 2060) /* Section 6.3.1.4 Codes */ +ValuePair( GeogPrimeMeridianLongGeoKey, 2061) /* GeoAngularUnit */ +ValuePair( GeogTOWGS84GeoKey, 2062) /* 2011 - proposed addition */ + +/* 6.2.3 Projected CS Parameter Keys */ +/* Several keys have been renamed,*/ +/* and the deprecated names aliased for backward compatibility */ + +ValuePair( ProjectedCSTypeGeoKey, 3072) /* Section 6.3.3.1 codes */ +ValuePair( PCSCitationGeoKey, 3073) /* documentation */ +ValuePair( ProjectionGeoKey, 3074) /* Section 6.3.3.2 codes */ +ValuePair( ProjCoordTransGeoKey, 3075) /* Section 6.3.3.3 codes */ +ValuePair( ProjLinearUnitsGeoKey, 3076) /* Section 6.3.1.3 codes */ +ValuePair( ProjLinearUnitSizeGeoKey, 3077) /* meters */ +ValuePair( ProjStdParallel1GeoKey, 3078) /* GeogAngularUnit */ +ValuePair( ProjStdParallelGeoKey,ProjStdParallel1GeoKey) /* ** alias ** */ +ValuePair( ProjStdParallel2GeoKey, 3079) /* GeogAngularUnit */ +ValuePair( ProjNatOriginLongGeoKey, 3080) /* GeogAngularUnit */ +ValuePair( ProjOriginLongGeoKey,ProjNatOriginLongGeoKey) /* ** alias ** */ +ValuePair( ProjNatOriginLatGeoKey, 3081) /* GeogAngularUnit */ +ValuePair( ProjOriginLatGeoKey,ProjNatOriginLatGeoKey) /* ** alias ** */ +ValuePair( ProjFalseEastingGeoKey, 3082) /* ProjLinearUnits */ +ValuePair( ProjFalseNorthingGeoKey, 3083) /* ProjLinearUnits */ +ValuePair( ProjFalseOriginLongGeoKey, 3084) /* GeogAngularUnit */ +ValuePair( ProjFalseOriginLatGeoKey, 3085) /* GeogAngularUnit */ +ValuePair( ProjFalseOriginEastingGeoKey, 3086) /* ProjLinearUnits */ +ValuePair( ProjFalseOriginNorthingGeoKey, 3087) /* ProjLinearUnits */ +ValuePair( ProjCenterLongGeoKey, 3088) /* GeogAngularUnit */ +ValuePair( ProjCenterLatGeoKey, 3089) /* GeogAngularUnit */ +ValuePair( ProjCenterEastingGeoKey, 3090) /* ProjLinearUnits */ +ValuePair( ProjCenterNorthingGeoKey, 3091) /* ProjLinearUnits */ +ValuePair( ProjScaleAtNatOriginGeoKey, 3092) /* ratio */ +ValuePair( ProjScaleAtOriginGeoKey,ProjScaleAtNatOriginGeoKey) /* ** alias ** */ +ValuePair( ProjScaleAtCenterGeoKey, 3093) /* ratio */ +ValuePair( ProjAzimuthAngleGeoKey, 3094) /* GeogAzimuthUnit */ +ValuePair( ProjStraightVertPoleLongGeoKey, 3095) /* GeogAngularUnit */ +ValuePair( ProjRectifiedGridAngleGeoKey, 3096) /* GeogAngularUnit */ + +/* 6.2.4 Vertical CS Keys */ + +ValuePair( VerticalCSTypeGeoKey, 4096) /* Section 6.3.4.1 codes */ +ValuePair( VerticalCitationGeoKey, 4097) /* documentation */ +ValuePair( VerticalDatumGeoKey, 4098) /* Section 6.3.4.2 codes */ +ValuePair( VerticalUnitsGeoKey, 4099) /* Section 6.3.1 (.x) codes */ + +/* End of Data base */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geonames.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geonames.h new file mode 100644 index 000000000..37670c437 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geonames.h @@ -0,0 +1,144 @@ +/* + * geonames.h + * + * This encapsulates all of the value-naming mechanism of + * libgeotiff. + * + * Written By: Niles Ritter + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + */ + +#ifndef __geonames_h +#define __geonames_h + +struct _KeyInfo { + int ki_key; + char *ki_name; +}; +typedef struct _KeyInfo KeyInfo; + +/* If memory is a premium, then omitting the + * long name lists may save some space; simply + * #define OMIT_GEOTIFF_NAMES in the compile statement + * to remove all key->string translation. + */ +#ifdef ValuePair +# undef ValuePair +#endif + +#ifndef OMIT_GEOTIFF_NAMES +#define ValuePair(token,value) {token,#token}, +#else +#define ValuePair(token,value) +#endif + +#define END_LIST { -1, (char *)0} + +/************************************************************ + * 6.2.x GeoTIFF Keys + ************************************************************/ + +static KeyInfo _keyInfo[] = { +# include "geokeys.inc" /* geokey database */ + END_LIST +}; + +#define COMMON_VALUES \ + {KvUndefined, "Undefined"}, \ + {KvUserDefined,"User-Defined"}, \ + ValuePair(KvUndefined,KvUndefined) \ + ValuePair(KvUserDefined,KvUserDefined) + +static KeyInfo _csdefaultValue[] = { + COMMON_VALUES + END_LIST +}; + +/************************************************************ + * 6.3.x GeoTIFF Key Values + ************************************************************/ + +static KeyInfo _modeltypeValue[] = { + COMMON_VALUES + ValuePair(ModelTypeProjected,1) + ValuePair(ModelTypeGeographic,2) + ValuePair(ModelTypeGeocentric,3) + ValuePair(ModelProjected,1) /* aliases */ + ValuePair(ModelGeographic,2) /* aliases */ + ValuePair(ModelGeocentric,3) /* aliases */ + END_LIST +}; + +static KeyInfo _rastertypeValue[] = { + COMMON_VALUES + ValuePair(RasterPixelIsArea,1) + ValuePair(RasterPixelIsPoint,2) + END_LIST +}; + +static KeyInfo _geounitsValue[] = { + COMMON_VALUES +# include "epsg_units.inc" + END_LIST +}; + +static KeyInfo _geographicValue[] = { + COMMON_VALUES +# include "epsg_gcs.inc" + END_LIST +}; + +static KeyInfo _geodeticdatumValue[] = { + COMMON_VALUES +# include "epsg_datum.inc" + END_LIST +}; + +static KeyInfo _ellipsoidValue[] = { + COMMON_VALUES +# include "epsg_ellipse.inc" + END_LIST +}; + +static KeyInfo _primemeridianValue[] = { + COMMON_VALUES +# include "epsg_pm.inc" + END_LIST +}; + +static KeyInfo _pcstypeValue[] = { + COMMON_VALUES +# include "epsg_pcs.inc" + END_LIST +}; + +static KeyInfo _projectionValue[] = { + COMMON_VALUES +# include "epsg_proj.inc" + END_LIST +}; + +static KeyInfo _coordtransValue[] = { + COMMON_VALUES +# include "geo_ctrans.inc" + END_LIST +}; + +static KeyInfo _vertcstypeValue[] = { + COMMON_VALUES +# include "epsg_vertcs.inc" + END_LIST +}; + +static KeyInfo _vdatumValue[] = { + COMMON_VALUES + ValuePair(VDatumBase,1) + END_LIST +}; + +#endif /* __geonames_h */ + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geotiff.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geotiff.h new file mode 100644 index 000000000..6bf9ba27f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geotiff.h @@ -0,0 +1,123 @@ +/********************************************************************** + * + * geotiff.h - Public interface for Geotiff tag parsing. + * + * Written By: Niles D. Ritter + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + **********************************************************************/ + +#ifndef __geotiff_h_ +#define __geotiff_h_ + +/** + * \file geotiff.h + * + * Primary libgeotiff include file. + * + * This is the defacto registry for valid GEOTIFF GeoKeys + * and their associated symbolic values. This is also the only file + * of the GeoTIFF library which needs to be included in client source + * code. + */ + +/* This Version code should only change if a drastic + * alteration is made to the GeoTIFF key structure. Readers + * encountering a larger value should give up gracefully. + */ +#define GvCurrentVersion 1 + +#define LIBGEOTIFF_VERSION 1410 + +#include "geo_config.h" +#include "geokeys.h" + +/********************************************************************** + * Do we want to build as a DLL on windows? + **********************************************************************/ +#if !defined(CPL_DLL) +# if defined(_WIN32) && defined(BUILD_AS_DLL) +# define CPL_DLL __declspec(dllexport) +# else +# define CPL_DLL +# endif +#endif + +/********************************************************************** + * + * Public Structures & Definitions + * + **********************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct gtiff GTIF; /* struct gtiff is private */ +typedef struct _TIFFMethod TIFFMethod; +typedef unsigned short tifftag_t; +typedef unsigned short geocode_t; +typedef int (*GTIFPrintMethod)(char *string, void *aux); +typedef int (*GTIFReadMethod)(char *string, void *aux); // string 1024+ in size + +typedef enum { + TYPE_BYTE=1, + TYPE_SHORT=2, + TYPE_LONG=3, + TYPE_RATIONAL=4, + TYPE_ASCII=5, + TYPE_FLOAT=6, + TYPE_DOUBLE=7, + TYPE_SBYTE=8, + TYPE_SSHORT=9, + TYPE_SLONG=10, + TYPE_UNKNOWN=11 +} tagtype_t; + + +/********************************************************************** + * + * Public Function Declarations + * + **********************************************************************/ + +/* TIFF-level interface */ +GTIF CPL_DLL *GTIFNew(void *tif); +GTIF CPL_DLL *GTIFNewSimpleTags(void *tif); +GTIF CPL_DLL *GTIFNewWithMethods(void *tif, TIFFMethod*); +void CPL_DLL GTIFFree(GTIF *gtif); +int CPL_DLL GTIFWriteKeys(GTIF *gtif); +void CPL_DLL GTIFDirectoryInfo(GTIF *gtif, int *versions, int *keycount); + +/* GeoKey Access */ +int CPL_DLL GTIFKeyInfo(GTIF *gtif, geokey_t key, int *size, tagtype_t* type); +int CPL_DLL GTIFKeyGet(GTIF *gtif, geokey_t key, void *val, int index, + int count); +int CPL_DLL GTIFKeySet(GTIF *gtif, geokey_t keyID, tagtype_t type, + int count,...); + +/* Metadata Import-Export utilities */ +void CPL_DLL GTIFPrint(GTIF *gtif, GTIFPrintMethod print, void *aux); +int CPL_DLL GTIFImport(GTIF *gtif, GTIFReadMethod scan, void *aux); +char CPL_DLL *GTIFKeyName(geokey_t key); +char CPL_DLL *GTIFValueName(geokey_t key,int value); +char CPL_DLL *GTIFTypeName(tagtype_t type); +char CPL_DLL *GTIFTagName(int tag); +int CPL_DLL GTIFKeyCode(char * key); +int CPL_DLL GTIFValueCode(geokey_t key,char *value); +int CPL_DLL GTIFTypeCode(char *type); +int CPL_DLL GTIFTagCode(char *tag); + +/* Translation between image/PCS space */ + +int CPL_DLL GTIFImageToPCS( GTIF *gtif, double *x, double *y ); +int CPL_DLL GTIFPCSToImage( GTIF *gtif, double *x, double *y ); + +#if defined(__cplusplus) +} +#endif + +#endif /* __geotiff_h_ */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geotiff_proj4.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geotiff_proj4.c new file mode 100644 index 000000000..129f68796 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geotiff_proj4.c @@ -0,0 +1,1464 @@ +/****************************************************************************** + * $Id: geotiff_proj4.c 2594 2014-12-27 16:46:35Z rouault $ + * + * Project: libgeotiff + * Purpose: Code to convert a normalized GeoTIFF definition into a PROJ.4 + * (OGDI) compatible projection string. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + */ + +#include "cpl_serv.h" +#include "geotiff.h" +#include "geo_normalize.h" +#include "geovalues.h" + +/************************************************************************/ +/* OSRProj4Tokenize() */ +/* */ +/* Custom tokenizing function for PROJ.4 strings. The main */ +/* reason we can't just use CSLTokenizeString is to handle */ +/* strings with a + sign in the exponents of parameter values. */ +/************************************************************************/ + +static char **OSRProj4Tokenize( const char *pszFull ) + +{ + char *pszStart = NULL; + char *pszFullWrk; + char **papszTokens; + int i; + int nTokens = 0; + static const int nMaxTokens = 200; + + if( pszFull == NULL ) + return NULL; + + papszTokens = (char **) calloc(sizeof(char*),nMaxTokens); + + pszFullWrk = CPLStrdup(pszFull); + + for( i=0; pszFullWrk[i] != '\0' && nTokens != nMaxTokens-1; i++ ) + { + switch( pszFullWrk[i] ) + { + case '+': + if( i == 0 || pszFullWrk[i-1] == '\0' ) + { + if( pszStart != NULL ) + { + if( strstr(pszStart,"=") != NULL ) + { + papszTokens[nTokens++] = CPLStrdup(pszStart); + } + else + { + char szAsBoolean[100]; + strncpy( szAsBoolean,pszStart, sizeof(szAsBoolean)-1-4); + szAsBoolean[sizeof(szAsBoolean)-1-4] = '\0'; + strcat( szAsBoolean,"=yes" ); + papszTokens[nTokens++] = CPLStrdup(szAsBoolean); + } + } + pszStart = pszFullWrk + i + 1; + } + break; + + case ' ': + case '\t': + case '\n': + pszFullWrk[i] = '\0'; + break; + + default: + break; + } + } + + if( pszStart != NULL && strlen(pszStart) > 0 ) + { + if (nTokens != 199) + papszTokens[nTokens++] = CPLStrdup(pszStart); + } + + CPLFree( pszFullWrk ); + + return papszTokens; +} + + +/************************************************************************/ +/* OSR_GSV() */ +/************************************************************************/ + +static const char *OSR_GSV( char **papszNV, const char * pszField ) + +{ + size_t field_len = strlen(pszField); + int i; + + if( !papszNV ) + return NULL; + + for( i = 0; papszNV[i] != NULL; i++ ) + { + if( EQUALN(papszNV[i],pszField,field_len) ) + { + if( papszNV[i][field_len] == '=' ) + return papszNV[i] + field_len + 1; + + if( strlen(papszNV[i]) == field_len ) + return ""; + } + } + + return NULL; +} + +/************************************************************************/ +/* OSR_GDV() */ +/* */ +/* Fetch a particular parameter out of the parameter list, or */ +/* the indicated default if it isn't available. This is a */ +/* helper function for importFromProj4(). */ +/************************************************************************/ + +static double OSR_GDV( char **papszNV, const char * pszField, + double dfDefaultValue ) + +{ + const char *pszValue = OSR_GSV( papszNV, pszField ); + + /* special hack to use k_0 if available. */ + if( pszValue == NULL && EQUAL(pszField,"k") ) + return OSR_GDV( papszNV, "k_0", dfDefaultValue ); + + if( pszValue == NULL ) + return dfDefaultValue; + else + return GTIFAtof(pszValue); +} + +/************************************************************************/ +/* OSRFreeStringList() */ +/************************************************************************/ + +static void OSRFreeStringList( char ** list ) + +{ + int i; + + for( i = 0; list != NULL && list[i] != NULL; i++ ) + free( list[i] ); + free(list); +} + + +/************************************************************************/ +/* GTIFSetFromProj4() */ +/************************************************************************/ + +int GTIFSetFromProj4( GTIF *gtif, const char *proj4 ) + +{ + char **papszNV = OSRProj4Tokenize( proj4 ); + short nSpheroid = KvUserDefined; + double dfSemiMajor=0.0, dfSemiMinor=0.0, dfInvFlattening=0.0; + int nDatum = KvUserDefined; + int nGCS = KvUserDefined; + const char *value; + +/* -------------------------------------------------------------------- */ +/* Get the ellipsoid definition. */ +/* -------------------------------------------------------------------- */ + value = OSR_GSV( papszNV, "ellps" ); + + if( value == NULL ) + { + /* nothing */; + } + else if( EQUAL(value,"WGS84") ) + nSpheroid = Ellipse_WGS_84; + else if( EQUAL(value,"clrk66") ) + nSpheroid = Ellipse_Clarke_1866; + else if( EQUAL(value,"clrk80") ) + nSpheroid = Ellipse_Clarke_1880; + else if( EQUAL(value,"GRS80") ) + nSpheroid = Ellipse_GRS_1980; + + if( nSpheroid == KvUserDefined ) + { + dfSemiMajor = OSR_GDV(papszNV,"a",0.0); + dfSemiMinor = OSR_GDV(papszNV,"b",0.0); + dfInvFlattening = OSR_GDV(papszNV,"rf",0.0); + if( dfSemiMinor != 0.0 && dfInvFlattening == 0.0 ) + dfInvFlattening = -1.0 / (dfSemiMinor/dfSemiMajor - 1.0); + } + +/* -------------------------------------------------------------------- */ +/* Get the GCS/Datum code. */ +/* -------------------------------------------------------------------- */ + value = OSR_GSV( papszNV, "datum" ); + + if( value == NULL ) + { + } + else if( EQUAL(value,"WGS84") ) + { + nGCS = GCS_WGS_84; + nDatum = Datum_WGS84; + } + else if( EQUAL(value,"NAD83") ) + { + nGCS = GCS_NAD83; + nDatum = Datum_North_American_Datum_1983; + } + else if( EQUAL(value,"NAD27") ) + { + nGCS = GCS_NAD27; + nDatum = Datum_North_American_Datum_1927; + } + +/* -------------------------------------------------------------------- */ +/* Operate on the basis of the projection name. */ +/* -------------------------------------------------------------------- */ + value = OSR_GSV(papszNV,"proj"); + + if( value == NULL ) + { + OSRFreeStringList( papszNV ); + return FALSE; + } + + else if( EQUAL(value,"longlat") || EQUAL(value,"latlong") ) + { + } + + else if( EQUAL(value,"tmerc") ) + { + GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(gtif, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(gtif, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(gtif, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_TransverseMercator ); + + GTIFKeySet(gtif, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "lat_0", 0.0 ) ); + + GTIFKeySet(gtif, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "lon_0", 0.0 ) ); + + GTIFKeySet(gtif, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "k", 1.0 ) ); + + GTIFKeySet(gtif, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "x_0", 0.0 ) ); + + GTIFKeySet(gtif, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"utm") ) + { + int nZone = (int) OSR_GDV(papszNV,"zone",0); + const char *south = OSR_GSV(papszNV,"south"); + + GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(gtif, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(gtif, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(gtif, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_TransverseMercator ); + + GTIFKeySet(gtif, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + 0.0 ); + + GTIFKeySet(gtif, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + nZone * 6 - 183.0 ); + + GTIFKeySet(gtif, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, + 0.9996 ); + + GTIFKeySet(gtif, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + 500000.0 ); + + if( south != NULL ) + GTIFKeySet(gtif, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + 10000000.0 ); + else + GTIFKeySet(gtif, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + 0.0 ); + } + + else if( EQUAL(value,"lcc") + && OSR_GDV(papszNV, "lat_0", 0.0 ) + == OSR_GDV(papszNV, "lat_1", 0.0 ) ) + { + GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(gtif, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(gtif, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(gtif, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_LambertConfConic_1SP ); + + GTIFKeySet(gtif, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "lat_0", 0.0 ) ); + + GTIFKeySet(gtif, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "lon_0", 0.0 ) ); + + GTIFKeySet(gtif, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "k", 1.0 ) ); + + GTIFKeySet(gtif, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "x_0", 0.0 ) ); + + GTIFKeySet(gtif, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"lcc") ) + { + GTIFKeySet(gtif, GTModelTypeGeoKey, TYPE_SHORT, 1, + ModelTypeProjected); + GTIFKeySet(gtif, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet(gtif, ProjectionGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + + GTIFKeySet(gtif, ProjCoordTransGeoKey, TYPE_SHORT, 1, + CT_LambertConfConic_2SP ); + + GTIFKeySet(gtif, ProjFalseOriginLatGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "lat_0", 0.0 ) ); + + GTIFKeySet(gtif, ProjFalseOriginLongGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "lon_0", 0.0 ) ); + + GTIFKeySet(gtif, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "lat_1", 0.0 ) ); + + GTIFKeySet(gtif, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "lat_2", 0.0 ) ); + + GTIFKeySet(gtif, ProjFalseOriginEastingGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "x_0", 0.0 ) ); + + GTIFKeySet(gtif, ProjFalseOriginNorthingGeoKey, TYPE_DOUBLE, 1, + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + +#ifdef notdef + else if( EQUAL(value,"bonne") ) + { + SetBonne( OSR_GDV( papszNV, "lat_1", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"cass") ) + { + SetCS( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"nzmg") ) + { + SetNZMG( OSR_GDV( papszNV, "lat_0", -41.0 ), + OSR_GDV( papszNV, "lon_0", 173.0 ), + OSR_GDV( papszNV, "x_0", 2510000.0 ), + OSR_GDV( papszNV, "y_0", 6023150.0 ) ); + } + + else if( EQUAL(value,"cea") ) + { + SetCEA( OSR_GDV( papszNV, "lat_ts", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"merc") /* 2SP form */ + && OSR_GDV(papszNV, "lat_ts", 1000.0) < 999.0 ) + { + SetMercator2SP( OSR_GDV( papszNV, "lat_ts", 0.0 ), + 0.0, + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"merc") ) /* 1SP form */ + { + SetMercator( 0.0, + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"stere") + && ABS(OSR_GDV( papszNV, "lat_0", 0.0 ) - 90) < 0.001 ) + { + SetPS( OSR_GDV( papszNV, "lat_ts", 90.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"stere") + && ABS(OSR_GDV( papszNV, "lat_0", 0.0 ) + 90) < 0.001 ) + { + SetPS( OSR_GDV( papszNV, "lat_ts", -90.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUALN(value,"stere",5) /* mostly sterea */ + && CSLFetchNameValue(papszNV,"k") != NULL ) + { + SetOS( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"stere") ) + { + SetStereographic( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + 1.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"eqc") ) + { + if( OSR_GDV( papszNV, "lat_0", 0.0 ) != OSR_GDV( papszNV, "lat_ts", 0.0 ) ) + SetEquirectangular2( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 )+dfFromGreenwich, + OSR_GDV( papszNV, "lat_ts", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + else + SetEquirectangular( OSR_GDV( papszNV, "lat_ts", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 )+dfFromGreenwich, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"glabsgm") ) + { + SetGaussLabordeReunion( OSR_GDV( papszNV, "lat_0", -21.116666667 ), + OSR_GDV( papszNV, "lon_0", 55.53333333309)+dfFromGreenwich, + OSR_GDV( papszNV, "k_0", 1.0 ), + OSR_GDV( papszNV, "x_0", 160000.000 ), + OSR_GDV( papszNV, "y_0", 50000.000 ) ); + } + + else if( EQUAL(value,"gnom") ) + { + SetGnomonic( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"ortho") ) + { + SetOrthographic( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"laea") ) + { + SetLAEA( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"aeqd") ) + { + SetAE( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"eqdc") ) + { + SetEC( OSR_GDV( papszNV, "lat_1", 0.0 ), + OSR_GDV( papszNV, "lat_2", 0.0 ), + OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"mill") ) + { + SetMC( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"moll") ) + { + SetMollweide( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"eck4") ) + { + SetEckertIV( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"eck6") ) + { + SetEckertVI( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"poly") ) + { + SetPolyconic( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"aea") ) + { + SetACEA( OSR_GDV( papszNV, "lat_1", 0.0 ), + OSR_GDV( papszNV, "lat_2", 0.0 ), + OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"robin") ) + { + SetRobinson( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"vandg") ) + { + SetVDG( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"sinu") ) + { + SetSinusoidal( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"gall") ) + { + SetGS( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"goode") ) + { + SetGH( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"geos") ) + { + SetGEOS( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "h", 35785831.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"lcc") ) + { + if( OSR_GDV(papszNV, "lat_0", 0.0 ) + == OSR_GDV(papszNV, "lat_1", 0.0 ) ) + { + /* 1SP form */ + SetLCC1SP( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k_0", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + else + { + /* 2SP form */ + SetLCC( OSR_GDV( papszNV, "lat_1", 0.0 ), + OSR_GDV( papszNV, "lat_2", 0.0 ), + OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + } + + else if( EQUAL(value,"omerc") ) + { + SetHOM( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lonc", 0.0 ), + OSR_GDV( papszNV, "alpha", 0.0 ), + 0.0, /* ??? */ + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"somerc") ) + { + SetHOM( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + 90.0, 90.0, + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"krovak") ) + { + SetKrovak( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "alpha", 0.0 ), + 0.0, /* pseudo_standard_parallel_1 */ + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value, "iwm_p") ) + { + SetIWMPolyconic( OSR_GDV( papszNV, "lat_1", 0.0 ), + OSR_GDV( papszNV, "lat_2", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value, "wag1") ) + { + SetWagner( 1, 0.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value, "wag2") ) + { + SetWagner( 2, 0.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value, "wag3") ) + { + SetWagner( 3, + OSR_GDV( papszNV, "lat_ts", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value, "wag1") ) + { + SetWagner( 4, 0.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value, "wag1") ) + { + SetWagner( 5, 0.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value, "wag1") ) + { + SetWagner( 6, 0.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value, "wag1") ) + { + SetWagner( 7, 0.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(value,"tpeqd") ) + { + SetTPED( OSR_GDV( papszNV, "lat_1", 0.0 ), + OSR_GDV( papszNV, "lon_1", 0.0 ), + OSR_GDV( papszNV, "lat_2", 0.0 ), + OSR_GDV( papszNV, "lon_2", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } +#endif + else + { + /* unsupported coordinate system */ + OSRFreeStringList( papszNV ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Write the GCS if we have it, otherwise write the datum. */ +/* -------------------------------------------------------------------- */ + if( nGCS != KvUserDefined ) + { + GTIFKeySet( gtif, GeographicTypeGeoKey, TYPE_SHORT, + 1, nGCS ); + } + else + { + GTIFKeySet( gtif, GeographicTypeGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet( gtif, GeogGeodeticDatumGeoKey, TYPE_SHORT, + 1, nDatum ); + } + +/* -------------------------------------------------------------------- */ +/* Write the ellipsoid if we don't know the GCS. */ +/* -------------------------------------------------------------------- */ + if( nGCS == KvUserDefined ) + { + if( nSpheroid != KvUserDefined ) + GTIFKeySet( gtif, GeogEllipsoidGeoKey, TYPE_SHORT, 1, + nSpheroid ); + else + { + GTIFKeySet( gtif, GeogEllipsoidGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet( gtif, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1, + dfSemiMajor ); + if( dfInvFlattening == 0.0 ) + GTIFKeySet( gtif, GeogSemiMinorAxisGeoKey, TYPE_DOUBLE, 1, + dfSemiMajor ); + else + GTIFKeySet( gtif, GeogInvFlatteningGeoKey, TYPE_DOUBLE, 1, + dfInvFlattening ); + } + + } + +/* -------------------------------------------------------------------- */ +/* Linear units translation */ +/* -------------------------------------------------------------------- */ + value = OSR_GSV( papszNV, "units" ); + + if( value == NULL ) + { + value = OSR_GSV( papszNV, "to_meter" ); + if( value ) + { + GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1, + KvUserDefined ); + GTIFKeySet( gtif, ProjLinearUnitSizeGeoKey, TYPE_DOUBLE, 1, + GTIFAtof(value) ); + } + } + else if( EQUAL(value,"meter") || EQUAL(value,"m") ) + { + GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1, + Linear_Meter ); + } + else if( EQUAL(value,"us-ft") ) + { + GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1, + Linear_Foot_US_Survey ); + } + else if( EQUAL(value,"ft") ) + { + GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1, + Linear_Foot ); + } + + + OSRFreeStringList( papszNV ); + + return TRUE; +} + +/************************************************************************/ +/* GTIFGetProj4Defn() */ +/************************************************************************/ + +char * GTIFGetProj4Defn( GTIFDefn * psDefn ) + +{ + char szProjection[512]; + char szUnits[64]; + double dfFalseEasting, dfFalseNorthing; + + if( psDefn == NULL || !psDefn->DefnSet ) + return CPLStrdup(""); + + szProjection[0] = '\0'; + +/* ==================================================================== */ +/* Translate the units of measure. */ +/* */ +/* Note that even with a +units, or +to_meter in effect, it is */ +/* still assumed that all the projection parameters are in */ +/* meters. */ +/* ==================================================================== */ + if( psDefn->UOMLength == Linear_Meter ) + { + strcpy( szUnits, "+units=m " ); + } + else if( psDefn->UOMLength == Linear_Foot ) + { + strcpy( szUnits, "+units=ft " ); + } + else if( psDefn->UOMLength == Linear_Foot_US_Survey ) + { + strcpy( szUnits, "+units=us-ft " ); + } + else if( psDefn->UOMLength == Linear_Foot_Indian ) + { + strcpy( szUnits, "+units=ind-ft " ); + } + else if( psDefn->UOMLength == Linear_Link ) + { + strcpy( szUnits, "+units=link " ); + } + else if( psDefn->UOMLength == Linear_Yard_Indian) + { + strcpy( szUnits, "+units=ind-yd " ); + } + else if( psDefn->UOMLength == Linear_Fathom ) + { + strcpy( szUnits, "+units=fath " ); + } + else if( psDefn->UOMLength == Linear_Mile_International_Nautical ) + { + strcpy( szUnits, "+units=kmi " ); + } + else + { + sprintf( szUnits, "+to_meter=%.10f", psDefn->UOMLengthInMeters ); + } + +/* -------------------------------------------------------------------- */ +/* false easting and northing are in meters and that is what */ +/* PROJ.4 wants regardless of the linear units. */ +/* -------------------------------------------------------------------- */ + dfFalseEasting = psDefn->ProjParm[5]; + dfFalseNorthing = psDefn->ProjParm[6]; + +/* ==================================================================== */ +/* Handle general projection methods. */ +/* ==================================================================== */ + +/* -------------------------------------------------------------------- */ +/* Geographic. */ +/* -------------------------------------------------------------------- */ + if(psDefn->Model==ModelTypeGeographic) + { + sprintf(szProjection+strlen(szProjection),"+proj=latlong "); + + } + +/* -------------------------------------------------------------------- */ +/* UTM - special case override on transverse mercator so things */ +/* will be more meaningful to the user. */ +/* -------------------------------------------------------------------- */ + else if( psDefn->MapSys == MapSys_UTM_North ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=utm +zone=%d ", + psDefn->Zone ); + } + +/* -------------------------------------------------------------------- */ +/* Transverse Mercator */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_TransverseMercator ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=tmerc +lat_0=%.9f +lon_0=%.9f +k=%f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[4], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Mercator */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_Mercator ) + { + if( psDefn->ProjParm[2] != 0.0 ) /* Mercator 2SP: FIXME we need a better way of detecting it */ + sprintf( szProjection+strlen(szProjection), + "+proj=merc +lat_ts=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[2], + psDefn->ProjParm[1], + dfFalseEasting, + dfFalseNorthing ); + else + sprintf( szProjection+strlen(szProjection), + "+proj=merc +lat_ts=%.9f +lon_0=%.9f +k=%f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[4], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Cassini/Soldner */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_CassiniSoldner ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=cass +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Oblique Stereographic - Should this really map onto */ +/* Stereographic? */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_ObliqueStereographic ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=stere +lat_0=%.9f +lon_0=%.9f +k=%f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[4], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Stereographic */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_Stereographic ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=stere +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Polar Stereographic */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_PolarStereographic ) + { + if( psDefn->ProjParm[0] > 0.0 ) + sprintf( szProjection+strlen(szProjection), + "+proj=stere +lat_0=90 +lat_ts=%.9f +lon_0=%.9f " + "+k=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[4], + dfFalseEasting, + dfFalseNorthing ); + else + sprintf( szProjection+strlen(szProjection), + "+proj=stere +lat_0=-90 +lat_ts=%.9f +lon_0=%.9f " + "+k=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[4], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Equirectangular */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_Equirectangular ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=eqc +lat_0=%.9f +lon_0=%.9f +lat_ts=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[2], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Gnomonic */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_Gnomonic ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=gnom +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Orthographic */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_Orthographic ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=ortho +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Lambert Azimuthal Equal Area */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_LambertAzimEqualArea ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=laea +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Azimuthal Equidistant */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_AzimuthalEquidistant ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=aeqd +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Miller Cylindrical */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_MillerCylindrical ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=mill +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f +R_A ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Polyconic */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_Polyconic ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=poly +lat_0=%.9f +lon_0=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* AlbersEqualArea */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_AlbersEqualArea ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=aea +lat_1=%.9f +lat_2=%.9f +lat_0=%.9f +lon_0=%.9f" + " +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[2], + psDefn->ProjParm[3], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* EquidistantConic */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_EquidistantConic ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=eqdc +lat_1=%.9f +lat_2=%.9f +lat_0=%.9f +lon_0=%.9f" + " +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[2], + psDefn->ProjParm[3], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Robinson */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_Robinson ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=robin +lon_0=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[1], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* VanDerGrinten */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_VanDerGrinten ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=vandg +lon_0=%.9f +x_0=%.3f +y_0=%.3f +R_A ", + psDefn->ProjParm[1], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* Sinusoidal */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_Sinusoidal ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=sinu +lon_0=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[1], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* LambertConfConic_2SP */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_LambertConfConic_2SP ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=lcc +lat_0=%.9f +lon_0=%.9f +lat_1=%.9f +lat_2=%.9f " + " +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[2], + psDefn->ProjParm[3], + dfFalseEasting, + dfFalseNorthing ); + } + +/* -------------------------------------------------------------------- */ +/* LambertConfConic_1SP */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_LambertConfConic_1SP ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=lcc +lat_0=%.9f +lat_1=%.9f +lon_0=%.9f" + " +k_0=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[4], + psDefn->ProjParm[5], + psDefn->ProjParm[6] ); + } + +/* -------------------------------------------------------------------- */ +/* CT_CylindricalEqualArea */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_CylindricalEqualArea ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=cea +lat_ts=%.9f +lon_0=%.9f " + " +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[5], + psDefn->ProjParm[6] ); + } + +/* -------------------------------------------------------------------- */ +/* NewZealandMapGrid */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_NewZealandMapGrid ) + { + sprintf( szProjection+strlen(szProjection), + "+proj=nzmg +lat_0=%.9f +lon_0=%.9f" + " +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[5], + psDefn->ProjParm[6] ); + } + +/* -------------------------------------------------------------------- */ +/* Transverse Mercator - south oriented. */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_TransvMercator_SouthOriented ) + { + /* this appears to be an unsupported formulation with PROJ.4 */ + } + +/* -------------------------------------------------------------------- */ +/* ObliqueMercator (Hotine) */ +/* -------------------------------------------------------------------- */ + else if( psDefn->CTProjection == CT_ObliqueMercator ) + { + /* not clear how ProjParm[3] - angle from rectified to skewed grid - + should be applied ... see the +not_rot flag for PROJ.4. + Just ignoring for now. */ + + sprintf( szProjection+strlen(szProjection), + "+proj=omerc +lat_0=%.9f +lonc=%.9f +alpha=%.9f" + " +k=%.9f +x_0=%.3f +y_0=%.3f ", + psDefn->ProjParm[0], + psDefn->ProjParm[1], + psDefn->ProjParm[2], + psDefn->ProjParm[4], + psDefn->ProjParm[5], + psDefn->ProjParm[6] ); + } + +/* ==================================================================== */ +/* Handle ellipsoid information. */ +/* ==================================================================== */ + if( psDefn->Ellipsoid == Ellipse_WGS_84 ) + strcat( szProjection, "+ellps=WGS84 " ); + else if( psDefn->Ellipsoid == Ellipse_Clarke_1866 ) + strcat( szProjection, "+ellps=clrk66 " ); + else if( psDefn->Ellipsoid == Ellipse_Clarke_1880 ) + strcat( szProjection, "+ellps=clrk80 " ); + else if( psDefn->Ellipsoid == Ellipse_GRS_1980 ) + strcat( szProjection, "+ellps=GRS80 " ); + else + { + if( psDefn->SemiMajor != 0.0 && psDefn->SemiMinor != 0.0 ) + { + sprintf( szProjection+strlen(szProjection), + "+a=%.3f +b=%.3f ", + psDefn->SemiMajor, + psDefn->SemiMinor ); + } + } + + strcat( szProjection, szUnits ); + + /* If we don't have anything, reset */ + if (strstr(szProjection, "+proj=") == NULL) { return CPLStrdup(""); } + + return( CPLStrdup( szProjection ) ); +} + +#if !defined(HAVE_LIBPROJ) + +int GTIFProj4ToLatLong( GTIFDefn * psDefn, int nPoints, + double *padfX, double *padfY ) +{ + (void) psDefn; + (void) nPoints; + (void) padfX; + (void) padfY; +#ifdef DEBUG + fprintf( stderr, + "GTIFProj4ToLatLong() - PROJ.4 support not compiled in.\n" ); +#endif + return FALSE; +} + +int GTIFProj4FromLatLong( GTIFDefn * psDefn, int nPoints, + double *padfX, double *padfY ) +{ + (void) psDefn; + (void) nPoints; + (void) padfX; + (void) padfY; +#ifdef DEBUG + fprintf( stderr, + "GTIFProj4FromLatLong() - PROJ.4 support not compiled in.\n" ); +#endif + return FALSE; +} +#else + +#include "proj_api.h" + +/************************************************************************/ +/* GTIFProj4FromLatLong() */ +/* */ +/* Convert lat/long values to projected coordinate for a */ +/* particular definition. */ +/************************************************************************/ + +int GTIFProj4FromLatLong( GTIFDefn * psDefn, int nPoints, + double *padfX, double *padfY ) + +{ + char *pszProjection, **papszArgs; + projPJ *psPJ; + int i; + +/* -------------------------------------------------------------------- */ +/* Get a projection definition. */ +/* -------------------------------------------------------------------- */ + pszProjection = GTIFGetProj4Defn( psDefn ); + + if( pszProjection == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Parse into tokens for pj_init(), and initialize the projection. */ +/* -------------------------------------------------------------------- */ + + papszArgs = CSLTokenizeStringComplex( pszProjection, " +", TRUE, FALSE ); + free( pszProjection ); + + psPJ = pj_init( CSLCount(papszArgs), papszArgs ); + + CSLDestroy( papszArgs ); + + if( psPJ == NULL ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Process each of the points. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nPoints; i++ ) + { + projUV sUV; + + sUV.u = padfX[i] * DEG_TO_RAD; + sUV.v = padfY[i] * DEG_TO_RAD; + + sUV = pj_fwd( sUV, psPJ ); + + padfX[i] = sUV.u; + padfY[i] = sUV.v; + } + + pj_free( psPJ ); + + return TRUE; +} + +/************************************************************************/ +/* GTIFProj4ToLatLong() */ +/* */ +/* Convert projection coordinates to lat/long for a particular */ +/* definition. */ +/************************************************************************/ + +int GTIFProj4ToLatLong( GTIFDefn * psDefn, int nPoints, + double *padfX, double *padfY ) + +{ + char *pszProjection, **papszArgs; + projPJ *psPJ; + int i; + +/* -------------------------------------------------------------------- */ +/* Get a projection definition. */ +/* -------------------------------------------------------------------- */ + pszProjection = GTIFGetProj4Defn( psDefn ); + + if( pszProjection == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Parse into tokens for pj_init(), and initialize the projection. */ +/* -------------------------------------------------------------------- */ + + papszArgs = CSLTokenizeStringComplex( pszProjection, " +", TRUE, FALSE ); + free( pszProjection ); + + psPJ = pj_init( CSLCount(papszArgs), papszArgs ); + + CSLDestroy( papszArgs ); + + if( psPJ == NULL ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Process each of the points. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nPoints; i++ ) + { + projUV sUV; + + sUV.u = padfX[i]; + sUV.v = padfY[i]; + + sUV = pj_inv( sUV, psPJ ); + + padfX[i] = sUV.u * RAD_TO_DEG; + padfY[i] = sUV.v * RAD_TO_DEG; + } + + pj_free( psPJ ); + + return TRUE; +} + +#endif /* has proj_api.h and -lproj */ + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geotiffio.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geotiffio.h new file mode 100644 index 000000000..32ec19ed2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geotiffio.h @@ -0,0 +1,21 @@ +/* + * geotiffio.h + * + * Standard include file for geotiff, including all + * key and code definitions. + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + */ + + +#ifndef __geotiffio_h +#define __geotiffio_h + +#include "geotiff.h" /* public key interface */ +#include "geovalues.h" /* key code definitions */ + +#endif /* __geotiffio_h */ + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geovalues.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geovalues.h new file mode 100644 index 000000000..1cd8444c5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/geovalues.h @@ -0,0 +1,116 @@ +/********************************************************************** + * + * geovalues.h - Public registry for valid GEOTIFF key-values. + * + * Written By: Niles D. Ritter + * + * copyright (c) 1995 Niles D. Ritter + * + * Permission granted to use this software, so long as this copyright + * notice accompanies any products derived therefrom. + * + **********************************************************************/ + +#ifndef __geovalues_h_ +#define __geovalues_h_ + +/* If code values are added or modified, the "GvCurrentMinorRev" + * number should be incremented here. If new Keys are added, then the + * GvCurrentRevision number should be incremented instead, and the + * GvCurrentMinorRev should be reset to zero (see "geokeys.h"). + * + * In addition, any changes here should be reflected in "geo_names.c" + * + */ + +#define GvCurrentMinorRev 0 /* First Major Rev EPSG Code Release */ + + +/* + * Universal key values -- defined for consistency + */ +#define KvUndefined 0 +#define KvUserDefined 32767 + +#ifdef ValuePair +# undef ValuePair +#endif +#define ValuePair(name,value) name = value, + +/* + * The section numbers refer to the GeoTIFF Specification section + * in which the code values are documented. + */ + +/************************************************************ + * 6.3.1 GeoTIFF General Codes + ************************************************************/ + +/* 6.3.1.1 Model Type Codes */ +typedef enum { + ModelTypeProjected = 1, /* Projection Coordinate System */ + ModelTypeGeographic = 2, /* Geographic latitude-longitude System */ + ModelTypeGeocentric = 3, /* Geocentric (X,Y,Z) Coordinate System */ + ModelProjected = ModelTypeProjected, /* alias */ + ModelGeographic = ModelTypeGeographic, /* alias */ + ModelGeocentric = ModelTypeGeocentric /* alias */ +} modeltype_t; + +/* 6.3.1.2 Raster Type Codes */ +typedef enum { + RasterPixelIsArea = 1, /* Standard pixel-fills-grid-cell */ + RasterPixelIsPoint = 2 /* Pixel-at-grid-vertex */ +} rastertype_t; + +typedef enum { +# include "epsg_gcs.inc" + geographic_end +} geographic_t; + +typedef enum { +# include "epsg_datum.inc" + geodeticdatum_end +} geodeticdatum_t; + +typedef enum { +# include "epsg_units.inc" + Unit_End +} geounits_t; + +typedef enum { +# include "epsg_ellipse.inc" + ellipsoid_end +} ellipsoid_t; + +typedef enum { +# include "epsg_pm.inc" + primemeridian_end +} primemeridian_t; + +typedef enum { +# include "epsg_pcs.inc" + pcstype_end +} pcstype_t; + +typedef enum { +# include "epsg_proj.inc" + projection_end +} projection_t; + +typedef enum { +# include "geo_ctrans.inc" + coordtrans_end +} coordtrans_t; + +typedef enum { +# include "epsg_vertcs.inc" + vertcs_end +} vertcstype_t; + + +typedef enum { + VDatumBase = 1 +} vdatum_t; + +#endif /* __geovalues_h_ */ + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/makefile.vc b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/makefile.vc new file mode 100644 index 000000000..7b2b64130 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/makefile.vc @@ -0,0 +1,28 @@ + +EXTRAFLAGS = -I..\libtiff -DBUILD_AS_DLL + +OBJ = \ + xtiff.obj \ + geo_free.obj \ + geo_get.obj \ + geo_names.obj \ + geo_new.obj \ + geo_print.obj \ + geo_set.obj \ + geo_tiffp.obj \ + geo_write.obj \ + geo_normalize.obj \ + geo_extra.obj \ + geotiff_proj4.obj \ + geo_simpletags.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\..\o + +clean: + del *.obj + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/xtiff.c b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/xtiff.c new file mode 100644 index 000000000..5e679dcc4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/xtiff.c @@ -0,0 +1,205 @@ +/* + * xtiff.c + * + * Extended TIFF Directory GEO Tag Support. + * + * You may use this file as a template to add your own + * extended tags to the library. Only the parts of the code + * marked with "XXX" require modification. + * + * Author: Niles D. Ritter + * + * Revisions: + * 18 Sep 1995 -- Deprecated Integraph Matrix tag with new one. + * Backward compatible support provided. --NDR. + */ + +#include "xtiffio.h" +#include +#include "cpl_serv.h" + +/* Tiff info structure. + * + * Entry format: + * { TAGNUMBER, ReadCount, WriteCount, DataType, FIELDNUM, + * OkToChange, PassDirCountOnSet, AsciiName } + * + * For ReadCount, WriteCount, -1 = unknown. + */ + +static const TIFFFieldInfo xtiffFieldInfo[] = { + + /* XXX Insert Your tags here */ + { TIFFTAG_GEOPIXELSCALE, -1,-1, TIFF_DOUBLE, FIELD_CUSTOM, + TRUE, TRUE, "GeoPixelScale" }, + { TIFFTAG_INTERGRAPH_MATRIX,-1,-1, TIFF_DOUBLE, FIELD_CUSTOM, + TRUE, TRUE, "Intergraph TransformationMatrix" }, + { TIFFTAG_GEOTRANSMATRIX, -1,-1, TIFF_DOUBLE, FIELD_CUSTOM, + TRUE, TRUE, "GeoTransformationMatrix" }, + { TIFFTAG_GEOTIEPOINTS, -1,-1, TIFF_DOUBLE, FIELD_CUSTOM, + TRUE, TRUE, "GeoTiePoints" }, + { TIFFTAG_GEOKEYDIRECTORY,-1,-1, TIFF_SHORT, FIELD_CUSTOM, + TRUE, TRUE, "GeoKeyDirectory" }, + { TIFFTAG_GEODOUBLEPARAMS, -1,-1, TIFF_DOUBLE, FIELD_CUSTOM, + TRUE, TRUE, "GeoDoubleParams" }, + { TIFFTAG_GEOASCIIPARAMS, -1,-1, TIFF_ASCII, FIELD_CUSTOM, + TRUE, FALSE, "GeoASCIIParams" }, +#ifdef JPL_TAG_SUPPORT + { TIFFTAG_JPL_CARTO_IFD, 1, 1, TIFF_LONG, FIELD_CUSTOM, + TRUE, TRUE, "JPL Carto IFD offset" }, /** Don't use this! **/ +#endif +}; + +#define N(a) (sizeof (a) / sizeof (a[0])) +static void _XTIFFLocalDefaultDirectory(TIFF *tif) +{ + /* Install the extended Tag field info */ + TIFFMergeFieldInfo(tif, xtiffFieldInfo, N(xtiffFieldInfo)); +} + + +/********************************************************************** + * Nothing below this line should need to be changed. + **********************************************************************/ + +static TIFFExtendProc _ParentExtender; + +/* + * This is the callback procedure, and is + * called by the DefaultDirectory method + * every time a new TIFF directory is opened. + */ + +static void +_XTIFFDefaultDirectory(TIFF *tif) +{ + /* set up our own defaults */ + _XTIFFLocalDefaultDirectory(tif); + + /* Since an XTIFF client module may have overridden + * the default directory method, we call it now to + * allow it to set up the rest of its own methods. + */ + + if (_ParentExtender) + (*_ParentExtender)(tif); +} + + +/** +Registers an extension with libtiff for adding GeoTIFF tags. +After this one-time intialization, any TIFF open function may be called in +the usual manner to create a TIFF file that compatible with libgeotiff. +The XTIFF open functions are simply for convenience: they call this +and then pass their parameters on to the appropriate TIFF open function. + +

This function may be called any number of times safely, since it will +only register the extension the first time it is called. +**/ + +void XTIFFInitialize(void) +{ + static int first_time=1; + + if (! first_time) return; /* Been there. Done that. */ + first_time = 0; + + /* Grab the inherited method and install */ + _ParentExtender = TIFFSetTagExtender(_XTIFFDefaultDirectory); +} + + +/** + * GeoTIFF compatible TIFF file open function. + * + * @param name The filename of a TIFF file to open. + * @param mode The open mode ("r", "w" or "a"). + * + * @return a TIFF * for the file, or NULL if the open failed. + * +This function is used to open GeoTIFF files instead of TIFFOpen() from +libtiff. Internally it calls TIFFOpen(), but sets up some extra hooks +so that GeoTIFF tags can be extracted from the file. If XTIFFOpen() isn't +used, GTIFNew() won't work properly. Files opened +with XTIFFOpen() should be closed with XTIFFClose(). + +The name of the file to be opened should be passed as name, and an +opening mode ("r", "w" or "a") acceptable to TIFFOpen() should be passed as the +mode.

+ +If XTIFFOpen() fails it will return NULL. Otherwise, normal TIFFOpen() +error reporting steps will have already taken place.

+ */ + +TIFF* +XTIFFOpen(const char* name, const char* mode) +{ + TIFF *tif; + + /* Set up the callback */ + XTIFFInitialize(); + + /* Open the file; the callback will set everything up + */ + tif = TIFFOpen(name, mode); + if (!tif) return tif; + + return tif; +} + +TIFF* +XTIFFFdOpen(int fd, const char* name, const char* mode) +{ + TIFF *tif; + + /* Set up the callback */ + XTIFFInitialize(); + + /* Open the file; the callback will set everything up + */ + tif = TIFFFdOpen(fd, name, mode); + if (!tif) return tif; + + return tif; +} + +TIFF* +XTIFFClientOpen(const char* name, const char* mode, thandle_t thehandle, + TIFFReadWriteProc RWProc, TIFFReadWriteProc RWProc2, + TIFFSeekProc SProc, TIFFCloseProc CProc, + TIFFSizeProc SzProc, + TIFFMapFileProc MFProvc, TIFFUnmapFileProc UMFProc ) +{ + TIFF *tif; + + /* Set up the callback */ + XTIFFInitialize(); + + /* Open the file; the callback will set everything up + */ + tif = TIFFClientOpen(name, mode, thehandle, + RWProc, RWProc2, + SProc, CProc, + SzProc, + MFProvc, UMFProc); + + if (!tif) return tif; + + return tif; +} + +/** + * Close a file opened with XTIFFOpen(). + * + * @param tif The file handle returned by XTIFFOpen(). + * + * If a GTIF structure was created with GTIFNew() + * for this file, it should be freed with GTIFFree() + * before calling XTIFFClose(). +*/ + +void +XTIFFClose(TIFF *tif) +{ + TIFFClose(tif); +} diff --git a/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/xtiffio.h b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/xtiffio.h new file mode 100644 index 000000000..a26708742 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libgeotiff/xtiffio.h @@ -0,0 +1,84 @@ +/* + * xtiffio.h -- Public interface to Extended GEO TIFF tags + * + * written by: Niles D. Ritter + */ + +#ifndef __xtiffio_h +#define __xtiffio_h + +#include "tiffio.h" +#include "geo_config.h" + +/** + * \file xtiffio.h + * + * Definitions relating GeoTIFF functions from geotiff.h to the TIFF + * library (usually libtiff). + */ + +/* + * Define public Tag names and values here + */ + +/* tags 33550 is a private tag registered to SoftDesk, Inc */ +#define TIFFTAG_GEOPIXELSCALE 33550 +/* tags 33920-33921 are private tags registered to Intergraph, Inc */ +#define TIFFTAG_INTERGRAPH_MATRIX 33920 /* $use TIFFTAG_GEOTRANSMATRIX ! */ +#define TIFFTAG_GEOTIEPOINTS 33922 +/* tags 34263-34264 are private tags registered to NASA-JPL Carto Group */ +#ifdef JPL_TAG_SUPPORT +#define TIFFTAG_JPL_CARTO_IFD 34263 /* $use GeoProjectionInfo ! */ +#endif +#define TIFFTAG_GEOTRANSMATRIX 34264 /* New Matrix Tag replaces 33920 */ +/* tags 34735-3438 are private tags registered to SPOT Image, Inc */ +#define TIFFTAG_GEOKEYDIRECTORY 34735 +#define TIFFTAG_GEODOUBLEPARAMS 34736 +#define TIFFTAG_GEOASCIIPARAMS 34737 + +/* + * Define Printing method flags. These + * flags may be passed in to TIFFPrintDirectory() to + * indicate that those particular field values should + * be printed out in full, rather than just an indicator + * of whether they are present or not. + */ +#define TIFFPRINT_GEOKEYDIRECTORY 0x80000000 +#define TIFFPRINT_GEOKEYPARAMS 0x40000000 + +/********************************************************************** + * Nothing below this line should need to be changed by the user. + **********************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +/********************************************************************** + * Do we want to build as a DLL on windows? + **********************************************************************/ +#if !defined(CPL_DLL) +# if defined(_WIN32) && defined(BUILD_AS_DLL) +# define CPL_DLL __declspec(dllexport) +# else +# define CPL_DLL +# endif +#endif + +extern void CPL_DLL XTIFFInitialize(); +extern TIFF CPL_DLL * XTIFFOpen(const char* name, const char* mode); +extern TIFF CPL_DLL * XTIFFFdOpen(int fd, const char* name, const char* mode); +extern void CPL_DLL XTIFFClose(TIFF *tif); + +extern TIFF CPL_DLL * XTIFFClientOpen(const char* name, const char* mode, + thandle_t thehandle, + TIFFReadWriteProc, TIFFReadWriteProc, + TIFFSeekProc, TIFFCloseProc, + TIFFSizeProc, + TIFFMapFileProc, TIFFUnmapFileProc); +#if defined(__cplusplus) +} +#endif + +#endif /* __xtiffio_h */ + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/GNUmakefile b/bazaar/plugin/gdal/frmts/gtiff/libtiff/GNUmakefile new file mode 100644 index 000000000..db77c07d4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/GNUmakefile @@ -0,0 +1,120 @@ + +include ../../../GDALmake.opt + +OBJ = \ + tif_aux.o \ + tif_close.o \ + tif_codec.o \ + tif_color.o \ + tif_compress.o \ + tif_dir.o \ + tif_dirinfo.o \ + tif_dirread.o \ + tif_dirwrite.o \ + tif_dumpmode.o \ + tif_error.o \ + tif_extension.o \ + tif_fax3.o \ + tif_fax3sm.o \ + tif_getimage.o \ + tif_jpeg.o \ + tif_jpeg_12.o \ + tif_flush.o \ + tif_luv.o \ + tif_lzw.o \ + tif_next.o \ + tif_ojpeg.o \ + tif_open.o \ + tif_packbits.o \ + tif_pixarlog.o \ + tif_predict.o \ + tif_print.o \ + tif_read.o \ + tif_swab.o \ + tif_strip.o \ + tif_thunder.o \ + tif_tile.o \ + tif_vsi.o \ + tif_version.o \ + tif_warning.o \ + tif_write.o \ + tif_zip.o \ + tif_lzma.o + +O_OBJ = $(foreach file,$(OBJ),../../o/$(file)) + +ALL_C_FLAGS = $(CFLAGS) $(CPPFLAGS) + +ifeq ($(LIBZ_SETTING),internal) +XTRA_OPT = -I../../zlib +else +XTRA_OPT = +endif + +ifeq ($(RENAME_INTERNAL_LIBTIFF_SYMBOLS),yes) +ALL_C_FLAGS := $(ALL_C_FLAGS) -DRENAME_INTERNAL_LIBTIFF_SYMBOLS +endif + +ifneq ($(LIBZ_SETTING),no) +ALL_C_FLAGS := $(ALL_C_FLAGS) -DPIXARLOG_SUPPORT -DZIP_SUPPORT $(XTRA_OPT) +endif + +ifneq ($(JPEG_SETTING),no) +ALL_C_FLAGS := $(ALL_C_FLAGS) -DJPEG_SUPPORT -DOJPEG_SUPPORT +# jpeg9: -DHAVE_BOOLEAN -Dboolean=int +endif + +ifeq ($(JPEG_SETTING),internal) +ALL_C_FLAGS := $(ALL_C_FLAGS) -I../../jpeg/libjpeg +endif + +ifeq ($(TIFF_JPEG12_ENABLED),yes) +ALL_C_FLAGS := $(ALL_C_FLAGS) -DJPEG_DUAL_MODE_8_12 +EXTRA_DEP = libjpeg12src +endif + +ifeq ($(LIBLZMA_SETTING),yes) +ALL_C_FLAGS := $(ALL_C_FLAGS) -DLZMA_SUPPORT +endif + +default: $(EXTRA_DEP) $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f $(O_OBJ) *.o *.a + +import: + @if test ! -d ~/libtiff ; then \ + echo reimport requires libtiff checked out ~/libtiff ; \ + exit 1; \ + fi + + mv tif_config.h tif_config_safe.h + mv tiffconf.h tiffconf_safe.h + copymatch.sh ~/libtiff/libtiff *.c *.h + mv tif_config_safe.h tif_config.h + mv tiffconf_safe.h tiffconf.h + + @echo + @echo 'Now do something like:' + @echo '% svn commit -m "updated to libtiff 3.6.0"' + @echo + +install-obj: $(EXTRA_DEP) $(O_OBJ:.o=.$(OBJ_EXT)) + +ifeq ($(RENAME_INTERNAL_LIBTIFF_SYMBOLS),yes) +../../o/tif_jpeg.$(OBJ_EXT) : tif_jpeg.c t4.h tif_config.h tif_dir.h tif_fax3.h tif_predict.h tiff.h tiffconf.h tiffio.h tiffiop.h tiffvers.h uvcode.h + sed "s/defined(TIFFInitJPEG)/0/" < tif_jpeg.c > tmp_tif_jpeg.c + $(CC) -c -I../../port $(ALL_C_FLAGS) tmp_tif_jpeg.c -o $@ + rm tmp_tif_jpeg.c + +../../o/tif_jpeg_12.$(OBJ_EXT) : tif_jpeg_12.c t4.h tif_config.h tif_dir.h tif_fax3.h tif_predict.h tiff.h tiffconf.h tiffio.h tiffiop.h tiffvers.h uvcode.h + sed "s/# define TIFFInitJPEG/#undef TIFFInitJPEG\n# define TIFFInitJPEG/" < tif_jpeg_12.c > tmp_tif_jpeg_12.c + $(CC) -c -I../../port $(ALL_C_FLAGS) tmp_tif_jpeg_12.c -o $@ + rm tmp_tif_jpeg_12.c +endif + +../../o/%.$(OBJ_EXT): %.c t4.h tif_config.h tif_dir.h tif_fax3.h tif_predict.h tiff.h tiffconf.h tiffio.h tiffiop.h tiffvers.h uvcode.h + $(CC) -c -I../../port $(ALL_C_FLAGS) $< -o $@ + +libjpeg12src: + (cd ../../jpeg; $(MAKE) libjpeg12/jcapimin12.c) diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/dump_symbols.sh b/bazaar/plugin/gdal/frmts/gtiff/libtiff/dump_symbols.sh new file mode 100644 index 000000000..36b07958c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/dump_symbols.sh @@ -0,0 +1,85 @@ +#!/bin/sh +# GDAL specific script to extract exported libtiff symbols that can be renamed +# to keep them internal to GDAL as much as possible + +gcc *.c -fPIC -shared -o libtiff.so -I. -I../../../port -DPIXARLOG_SUPPORT -DZIP_SUPPORT -DOJPEG_SUPPORT -DLZMA_SUPPORT + +OUT_FILE=gdal_libtiff_symbol_rename.h + +rm $OUT_FILE 2>/dev/null + +echo "/* This is a generated file by dump_symbols.h. *DO NOT EDIT MANUALLY !* */" >> $OUT_FILE + +# We exclude the TIFFSwab functions for renaming since tif_swab.c uses ifdef to determine if the symbols must be defined +symbol_list=$(objdump -t libtiff.so | grep .text | awk '{print $6}' | grep -v .text | grep -v TIFFInit | grep -v TIFFSwab | grep -v __do_global | grep -v __bss_start | grep -v _edata | grep -v _end | grep -v _fini | grep -v _init | sort) +for symbol in $symbol_list +do + echo "#define $symbol gdal_$symbol" >> $OUT_FILE +done + +rodata_symbol_list=$(objdump -t libtiff.so | grep "\.rodata" | awk '{print $6}' | grep -v "\.") +for symbol in $rodata_symbol_list +do + echo "#define $symbol gdal_$symbol" >> $OUT_FILE +done + +data_symbol_list=$(objdump -t libtiff.so | grep "\.data" | awk '{print $6}' | grep -v "\.") +for symbol in $data_symbol_list +do + echo "#define $symbol gdal_$symbol" >> $OUT_FILE +done + +bss_symbol_list=$(objdump -t libtiff.so | grep "\.bss" | awk '{print $6}' | grep -v "\.") +for symbol in $bss_symbol_list +do + echo "#define $symbol gdal_$symbol" >> $OUT_FILE +done + +rm libtiff.so + +# Was excluded by grep -v TIFFInit +echo "#define TIFFInitDumpMode gdal_TIFFInitDumpMode" >> $OUT_FILE + +# Pasted and adapter from tif_codec.c +echo "#define TIFFReInitJPEG_12 gdal_TIFFReInitJPEG_12" >> $OUT_FILE +echo "#ifdef LZW_SUPPORT" >> $OUT_FILE +echo "#define TIFFInitLZW gdal_TIFFInitLZW" >> $OUT_FILE +echo "#endif" >> $OUT_FILE +echo "#ifdef PACKBITS_SUPPORT" >> $OUT_FILE +echo "#define TIFFInitPackBits gdal_TIFFInitPackBits" >> $OUT_FILE +echo "#endif" >> $OUT_FILE +echo "#ifdef THUNDER_SUPPORT" >> $OUT_FILE +echo "#define TIFFInitThunderScan gdal_TIFFInitThunderScan" >> $OUT_FILE +echo "#endif" >> $OUT_FILE +echo "#ifdef NEXT_SUPPORT" >> $OUT_FILE +echo "#define TIFFInitNeXT gdal_TIFFInitNeXT" >> $OUT_FILE +echo "#endif" >> $OUT_FILE +echo "#ifdef JPEG_SUPPORT" >> $OUT_FILE +echo "#define TIFFInitJPEG gdal_TIFFInitJPEG" >> $OUT_FILE +# Manually added +echo "#define TIFFInitJPEG_12 gdal_TIFFInitJPEG_12" >> $OUT_FILE +echo "#endif" >> $OUT_FILE +echo "#ifdef OJPEG_SUPPORT" >> $OUT_FILE +echo "#define TIFFInitOJPEG gdal_TIFFInitOJPEG" >> $OUT_FILE +echo "#endif" >> $OUT_FILE +echo "#ifdef CCITT_SUPPORT" >> $OUT_FILE +echo "#define TIFFInitCCITTRLE gdal_TIFFInitCCITTRLE" >> $OUT_FILE +echo "#define TIFFInitCCITTRLEW gdal_TIFFInitCCITTRLEW" >> $OUT_FILE +echo "#define TIFFInitCCITTFax3 gdal_TIFFInitCCITTFax3" >> $OUT_FILE +echo "#define TIFFInitCCITTFax4 gdal_TIFFInitCCITTFax4" >> $OUT_FILE +echo "#endif" >> $OUT_FILE +echo "#ifdef JBIG_SUPPORT" >> $OUT_FILE +echo "#define TIFFInitJBIG gdal_TIFFInitJBIG" >> $OUT_FILE +echo "#endif" >> $OUT_FILE +echo "#ifdef ZIP_SUPPORT" >> $OUT_FILE +echo "#define TIFFInitZIP gdal_TIFFInitZIP" >> $OUT_FILE +echo "#endif" >> $OUT_FILE +echo "#ifdef PIXARLOG_SUPPORT" >> $OUT_FILE +echo "#define TIFFInitPixarLog gdal_TIFFInitPixarLog" >> $OUT_FILE +echo "#endif" >> $OUT_FILE +echo "#ifdef LOGLUV_SUPPORT" >> $OUT_FILE +echo "#define TIFFInitSGILog gdal_TIFFInitSGILog" >> $OUT_FILE +echo "#endif" >> $OUT_FILE +echo "#ifdef LZMA_SUPPORT" >> $OUT_FILE +echo "#define TIFFInitLZMA gdal_TIFFInitLZMA" >> $OUT_FILE +echo "#endif" >> $OUT_FILE diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/gdal_libtiff_symbol_rename.h b/bazaar/plugin/gdal/frmts/gtiff/libtiff/gdal_libtiff_symbol_rename.h new file mode 100644 index 000000000..96abde95e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/gdal_libtiff_symbol_rename.h @@ -0,0 +1,747 @@ +/* This is a generated file by dump_symbols.h. *DO NOT EDIT MANUALLY !* */ +#define add_ms gdal_add_ms +#define buildMap gdal_buildMap +#define BuildMapBitdepth16To8 gdal_BuildMapBitdepth16To8 +#define BuildMapUaToAa gdal_BuildMapUaToAa +#define call_gmon_start gdal_call_gmon_start +#define checkcmap gdal_checkcmap +#define CheckDirCount gdal_CheckDirCount +#define checkInkNamesString gdal_checkInkNamesString +#define ChopUpSingleUncompressedStrip gdal_ChopUpSingleUncompressedStrip +#define cl_hash gdal_cl_hash +#define codeLoop gdal_codeLoop +#define cvtcmap gdal_cvtcmap +#define DumpFixupTags gdal_DumpFixupTags +#define DumpModeDecode gdal_DumpModeDecode +#define DumpModeEncode gdal_DumpModeEncode +#define DumpModeSeek gdal_DumpModeSeek +#define EstimateStripByteCounts gdal_EstimateStripByteCounts +#define Fax3BadLength gdal_Fax3BadLength +#define Fax3Cleanup gdal_Fax3Cleanup +#define Fax3Close gdal_Fax3Close +#define Fax3Decode1D gdal_Fax3Decode1D +#define Fax3Decode2D gdal_Fax3Decode2D +#define Fax3DecodeRLE gdal_Fax3DecodeRLE +#define Fax3Encode gdal_Fax3Encode +#define Fax3Encode1DRow gdal_Fax3Encode1DRow +#define Fax3Encode2DRow gdal_Fax3Encode2DRow +#define Fax3Extension gdal_Fax3Extension +#define Fax3FixupTags gdal_Fax3FixupTags +#define Fax3PostEncode gdal_Fax3PostEncode +#define Fax3PreDecode gdal_Fax3PreDecode +#define Fax3PreEncode gdal_Fax3PreEncode +#define Fax3PrematureEOF gdal_Fax3PrematureEOF +#define Fax3PrintDir gdal_Fax3PrintDir +#define Fax3PutBits gdal_Fax3PutBits +#define Fax3PutEOL gdal_Fax3PutEOL +#define Fax3SetupState gdal_Fax3SetupState +#define Fax3Unexpected gdal_Fax3Unexpected +#define Fax3VGetField gdal_Fax3VGetField +#define Fax3VSetField gdal_Fax3VSetField +#define Fax4Decode gdal_Fax4Decode +#define Fax4Encode gdal_Fax4Encode +#define Fax4PostEncode gdal_Fax4PostEncode +#define find0span gdal_find0span +#define find1span gdal_find1span +#define fpAcc gdal_fpAcc +#define fpDiff gdal_fpDiff +#define frame_dummy gdal_frame_dummy +#define gtStripContig gdal_gtStripContig +#define gtStripSeparate gdal_gtStripSeparate +#define gtTileContig gdal_gtTileContig +#define gtTileSeparate gdal_gtTileSeparate +#define horAcc16 gdal_horAcc16 +#define horAcc32 gdal_horAcc32 +#define horAcc8 gdal_horAcc8 +#define horDiff16 gdal_horDiff16 +#define horDiff32 gdal_horDiff32 +#define horDiff8 gdal_horDiff8 +#define horizontalAccumulate11 gdal_horizontalAccumulate11 +#define horizontalAccumulate12 gdal_horizontalAccumulate12 +#define horizontalAccumulate16 gdal_horizontalAccumulate16 +#define horizontalAccumulate8 gdal_horizontalAccumulate8 +#define horizontalAccumulate8abgr gdal_horizontalAccumulate8abgr +#define horizontalAccumulateF gdal_horizontalAccumulateF +#define horizontalDifference16 gdal_horizontalDifference16 +#define horizontalDifference8 gdal_horizontalDifference8 +#define horizontalDifferenceF gdal_horizontalDifferenceF +#define InitCCITTFax3 gdal_InitCCITTFax3 +#define initCIELabConversion gdal_initCIELabConversion +#define initYCbCrConversion gdal_initYCbCrConversion +#define isCCITTCompression gdal_isCCITTCompression +#define jpeg_create_decompress_encap gdal_jpeg_create_decompress_encap +#define jpeg_encap_unwind gdal_jpeg_encap_unwind +#define jpeg_read_header_encap gdal_jpeg_read_header_encap +#define jpeg_read_raw_data_encap gdal_jpeg_read_raw_data_encap +#define jpeg_read_scanlines_encap gdal_jpeg_read_scanlines_encap +#define jpeg_start_decompress_encap gdal_jpeg_start_decompress_encap +#define L16fromY gdal_L16fromY +#define L16toGry gdal_L16toGry +#define L16toY gdal_L16toY +#define LogL10fromY gdal_LogL10fromY +#define LogL10toY gdal_LogL10toY +#define LogL16Decode gdal_LogL16Decode +#define LogL16Encode gdal_LogL16Encode +#define LogL16fromY gdal_LogL16fromY +#define LogL16GuessDataFmt gdal_LogL16GuessDataFmt +#define LogL16InitState gdal_LogL16InitState +#define LogL16toY gdal_LogL16toY +#define LogLuv24fromXYZ gdal_LogLuv24fromXYZ +#define LogLuv24toXYZ gdal_LogLuv24toXYZ +#define LogLuv32fromXYZ gdal_LogLuv32fromXYZ +#define LogLuv32toXYZ gdal_LogLuv32toXYZ +#define LogLuvCleanup gdal_LogLuvCleanup +#define LogLuvClose gdal_LogLuvClose +#define LogLuvDecode24 gdal_LogLuvDecode24 +#define LogLuvDecode32 gdal_LogLuvDecode32 +#define LogLuvDecodeStrip gdal_LogLuvDecodeStrip +#define LogLuvDecodeTile gdal_LogLuvDecodeTile +#define LogLuvEncode24 gdal_LogLuvEncode24 +#define LogLuvEncode32 gdal_LogLuvEncode32 +#define LogLuvEncodeStrip gdal_LogLuvEncodeStrip +#define LogLuvEncodeTile gdal_LogLuvEncodeTile +#define LogLuvFixupTags gdal_LogLuvFixupTags +#define LogLuvGuessDataFmt gdal_LogLuvGuessDataFmt +#define LogLuvInitState gdal_LogLuvInitState +#define _logLuvNop gdal__logLuvNop +#define LogLuvSetupDecode gdal_LogLuvSetupDecode +#define LogLuvSetupEncode gdal_LogLuvSetupEncode +#define LogLuvVGetField gdal_LogLuvVGetField +#define LogLuvVSetField gdal_LogLuvVSetField +#define Luv24fromLuv48 gdal_Luv24fromLuv48 +#define Luv24fromXYZ gdal_Luv24fromXYZ +#define Luv24toLuv48 gdal_Luv24toLuv48 +#define Luv24toRGB gdal_Luv24toRGB +#define Luv24toXYZ gdal_Luv24toXYZ +#define Luv32fromLuv48 gdal_Luv32fromLuv48 +#define Luv32fromXYZ gdal_Luv32fromXYZ +#define Luv32toLuv48 gdal_Luv32toLuv48 +#define Luv32toRGB gdal_Luv32toRGB +#define Luv32toXYZ gdal_Luv32toXYZ +#define LZMACleanup gdal_LZMACleanup +#define LZMADecode gdal_LZMADecode +#define LZMAEncode gdal_LZMAEncode +#define LZMAFixupTags gdal_LZMAFixupTags +#define LZMAPostEncode gdal_LZMAPostEncode +#define LZMAPreDecode gdal_LZMAPreDecode +#define LZMAPreEncode gdal_LZMAPreEncode +#define LZMASetupDecode gdal_LZMASetupDecode +#define LZMASetupEncode gdal_LZMASetupEncode +#define LZMAStrerror gdal_LZMAStrerror +#define LZMAVGetField gdal_LZMAVGetField +#define LZMAVSetField gdal_LZMAVSetField +#define LZWCleanup gdal_LZWCleanup +#define LZWDecode gdal_LZWDecode +#define LZWDecodeCompat gdal_LZWDecodeCompat +#define LZWEncode gdal_LZWEncode +#define LZWFixupTags gdal_LZWFixupTags +#define LZWPostEncode gdal_LZWPostEncode +#define LZWPreDecode gdal_LZWPreDecode +#define LZWPreEncode gdal_LZWPreEncode +#define LZWSetupDecode gdal_LZWSetupDecode +#define LZWSetupEncode gdal_LZWSetupEncode +#define makebwmap gdal_makebwmap +#define makecmap gdal_makecmap +#define MissingRequired gdal_MissingRequired +#define multiply_ms gdal_multiply_ms +#define multiply_ms gdal_multiply_ms +#define NeXTDecode gdal_NeXTDecode +#define _notConfigured gdal__notConfigured +#define NotConfigured gdal_NotConfigured +#define OJPEGCleanup gdal_OJPEGCleanup +#define OJPEGDecode gdal_OJPEGDecode +#define OJPEGDecodeRaw gdal_OJPEGDecodeRaw +#define OJPEGDecodeScanlines gdal_OJPEGDecodeScanlines +#define OJPEGEncode gdal_OJPEGEncode +#define OJPEGFixupTags gdal_OJPEGFixupTags +#define OJPEGLibjpegJpegErrorMgrErrorExit gdal_OJPEGLibjpegJpegErrorMgrErrorExit +#define OJPEGLibjpegJpegErrorMgrOutputMessage gdal_OJPEGLibjpegJpegErrorMgrOutputMessage +#define OJPEGLibjpegJpegSourceMgrFillInputBuffer gdal_OJPEGLibjpegJpegSourceMgrFillInputBuffer +#define OJPEGLibjpegJpegSourceMgrInitSource gdal_OJPEGLibjpegJpegSourceMgrInitSource +#define OJPEGLibjpegJpegSourceMgrResyncToRestart gdal_OJPEGLibjpegJpegSourceMgrResyncToRestart +#define OJPEGLibjpegJpegSourceMgrSkipInputData gdal_OJPEGLibjpegJpegSourceMgrSkipInputData +#define OJPEGLibjpegJpegSourceMgrTermSource gdal_OJPEGLibjpegJpegSourceMgrTermSource +#define OJPEGLibjpegSessionAbort gdal_OJPEGLibjpegSessionAbort +#define OJPEGPostDecode gdal_OJPEGPostDecode +#define OJPEGPostEncode gdal_OJPEGPostEncode +#define OJPEGPreDecode gdal_OJPEGPreDecode +#define OJPEGPreDecodeSkipRaw gdal_OJPEGPreDecodeSkipRaw +#define OJPEGPreDecodeSkipScanlines gdal_OJPEGPreDecodeSkipScanlines +#define OJPEGPreEncode gdal_OJPEGPreEncode +#define OJPEGPrintDir gdal_OJPEGPrintDir +#define OJPEGReadBlock gdal_OJPEGReadBlock +#define OJPEGReadBufferFill gdal_OJPEGReadBufferFill +#define OJPEGReadByte gdal_OJPEGReadByte +#define OJPEGReadByteAdvance gdal_OJPEGReadByteAdvance +#define OJPEGReadBytePeek gdal_OJPEGReadBytePeek +#define OJPEGReadHeaderInfo gdal_OJPEGReadHeaderInfo +#define OJPEGReadHeaderInfoSec gdal_OJPEGReadHeaderInfoSec +#define OJPEGReadHeaderInfoSecStreamDht gdal_OJPEGReadHeaderInfoSecStreamDht +#define OJPEGReadHeaderInfoSecStreamDqt gdal_OJPEGReadHeaderInfoSecStreamDqt +#define OJPEGReadHeaderInfoSecStreamDri gdal_OJPEGReadHeaderInfoSecStreamDri +#define OJPEGReadHeaderInfoSecStreamSof gdal_OJPEGReadHeaderInfoSecStreamSof +#define OJPEGReadHeaderInfoSecStreamSos gdal_OJPEGReadHeaderInfoSecStreamSos +#define OJPEGReadHeaderInfoSecTablesAcTable gdal_OJPEGReadHeaderInfoSecTablesAcTable +#define OJPEGReadHeaderInfoSecTablesDcTable gdal_OJPEGReadHeaderInfoSecTablesDcTable +#define OJPEGReadHeaderInfoSecTablesQTable gdal_OJPEGReadHeaderInfoSecTablesQTable +#define OJPEGReadSecondarySos gdal_OJPEGReadSecondarySos +#define OJPEGReadSkip gdal_OJPEGReadSkip +#define OJPEGReadWord gdal_OJPEGReadWord +#define OJPEGSetupDecode gdal_OJPEGSetupDecode +#define OJPEGSetupEncode gdal_OJPEGSetupEncode +#define OJPEGSubsamplingCorrect gdal_OJPEGSubsamplingCorrect +#define OJPEGVGetField gdal_OJPEGVGetField +#define OJPEGVSetField gdal_OJPEGVSetField +#define OJPEGWriteHeaderInfo gdal_OJPEGWriteHeaderInfo +#define OJPEGWriteStream gdal_OJPEGWriteStream +#define OJPEGWriteStreamAcTable gdal_OJPEGWriteStreamAcTable +#define OJPEGWriteStreamCompressed gdal_OJPEGWriteStreamCompressed +#define OJPEGWriteStreamDcTable gdal_OJPEGWriteStreamDcTable +#define OJPEGWriteStreamDri gdal_OJPEGWriteStreamDri +#define OJPEGWriteStreamEoi gdal_OJPEGWriteStreamEoi +#define OJPEGWriteStreamQTable gdal_OJPEGWriteStreamQTable +#define OJPEGWriteStreamRst gdal_OJPEGWriteStreamRst +#define OJPEGWriteStreamSof gdal_OJPEGWriteStreamSof +#define OJPEGWriteStreamSoi gdal_OJPEGWriteStreamSoi +#define OJPEGWriteStreamSos gdal_OJPEGWriteStreamSos +#define OkToChangeTag gdal_OkToChangeTag +#define oog_encode gdal_oog_encode +#define PackBitsDecode gdal_PackBitsDecode +#define PackBitsEncode gdal_PackBitsEncode +#define PackBitsEncodeChunk gdal_PackBitsEncodeChunk +#define PackBitsPostEncode gdal_PackBitsPostEncode +#define PackBitsPreEncode gdal_PackBitsPreEncode +#define PickContigCase gdal_PickContigCase +#define PickSeparateCase gdal_PickSeparateCase +#define PixarLogCleanup gdal_PixarLogCleanup +#define PixarLogClose gdal_PixarLogClose +#define PixarLogDecode gdal_PixarLogDecode +#define PixarLogEncode gdal_PixarLogEncode +#define PixarLogFixupTags gdal_PixarLogFixupTags +#define PixarLogGuessDataFmt gdal_PixarLogGuessDataFmt +#define PixarLogMakeTables gdal_PixarLogMakeTables +#define PixarLogPostEncode gdal_PixarLogPostEncode +#define PixarLogPreDecode gdal_PixarLogPreDecode +#define PixarLogPreEncode gdal_PixarLogPreEncode +#define PixarLogSetupDecode gdal_PixarLogSetupDecode +#define PixarLogSetupEncode gdal_PixarLogSetupEncode +#define PixarLogVGetField gdal_PixarLogVGetField +#define PixarLogVSetField gdal_PixarLogVSetField +#define PredictorDecodeRow gdal_PredictorDecodeRow +#define PredictorDecodeTile gdal_PredictorDecodeTile +#define PredictorEncodeRow gdal_PredictorEncodeRow +#define PredictorEncodeTile gdal_PredictorEncodeTile +#define PredictorPrintDir gdal_PredictorPrintDir +#define PredictorSetup gdal_PredictorSetup +#define PredictorSetupDecode gdal_PredictorSetupDecode +#define PredictorSetupEncode gdal_PredictorSetupEncode +#define PredictorVGetField gdal_PredictorVGetField +#define PredictorVSetField gdal_PredictorVSetField +#define put16bitbwtile gdal_put16bitbwtile +#define put1bitbwtile gdal_put1bitbwtile +#define put1bitcmaptile gdal_put1bitcmaptile +#define put2bitbwtile gdal_put2bitbwtile +#define put2bitcmaptile gdal_put2bitcmaptile +#define put4bitbwtile gdal_put4bitbwtile +#define put4bitcmaptile gdal_put4bitcmaptile +#define put8bitcmaptile gdal_put8bitcmaptile +#define putagreytile gdal_putagreytile +#define putCMYKseparate8bittile gdal_putCMYKseparate8bittile +#define putcontig8bitCIELab gdal_putcontig8bitCIELab +#define putcontig8bitYCbCr11tile gdal_putcontig8bitYCbCr11tile +#define putcontig8bitYCbCr12tile gdal_putcontig8bitYCbCr12tile +#define putcontig8bitYCbCr21tile gdal_putcontig8bitYCbCr21tile +#define putcontig8bitYCbCr22tile gdal_putcontig8bitYCbCr22tile +#define putcontig8bitYCbCr41tile gdal_putcontig8bitYCbCr41tile +#define putcontig8bitYCbCr42tile gdal_putcontig8bitYCbCr42tile +#define putcontig8bitYCbCr44tile gdal_putcontig8bitYCbCr44tile +#define putgreytile gdal_putgreytile +#define putRGBAAcontig16bittile gdal_putRGBAAcontig16bittile +#define putRGBAAcontig8bittile gdal_putRGBAAcontig8bittile +#define putRGBAAseparate16bittile gdal_putRGBAAseparate16bittile +#define putRGBAAseparate8bittile gdal_putRGBAAseparate8bittile +#define putRGBcontig16bittile gdal_putRGBcontig16bittile +#define putRGBcontig8bitCMYKMaptile gdal_putRGBcontig8bitCMYKMaptile +#define putRGBcontig8bitCMYKtile gdal_putRGBcontig8bitCMYKtile +#define putRGBcontig8bittile gdal_putRGBcontig8bittile +#define putRGBseparate16bittile gdal_putRGBseparate16bittile +#define putRGBseparate8bittile gdal_putRGBseparate8bittile +#define putRGBUAcontig16bittile gdal_putRGBUAcontig16bittile +#define putRGBUAcontig8bittile gdal_putRGBUAcontig8bittile +#define putRGBUAseparate16bittile gdal_putRGBUAseparate16bittile +#define putRGBUAseparate8bittile gdal_putRGBUAseparate8bittile +#define putseparate8bitYCbCr11tile gdal_putseparate8bitYCbCr11tile +#define putspan gdal_putspan +#define setByteArray gdal_setByteArray +#define setDoubleArrayOneValue gdal_setDoubleArrayOneValue +#define setExtraSamples gdal_setExtraSamples +#define setorientation gdal_setorientation +#define setupMap gdal_setupMap +#define swabHorAcc16 gdal_swabHorAcc16 +#define swabHorAcc32 gdal_swabHorAcc32 +#define tagCompare gdal_tagCompare +#define tagNameCompare gdal_tagNameCompare +#define td_lfind gdal_td_lfind +#define ThunderDecode gdal_ThunderDecode +#define ThunderDecodeRow gdal_ThunderDecodeRow +#define ThunderSetupDecode gdal_ThunderSetupDecode +#define TIFFAccessTagMethods gdal_TIFFAccessTagMethods +#define TIFFAdvanceDirectory gdal_TIFFAdvanceDirectory +#define TIFFAppendToStrip gdal_TIFFAppendToStrip +#define TIFFCheckDirOffset gdal_TIFFCheckDirOffset +#define _TIFFCheckMalloc gdal__TIFFCheckMalloc +#define TIFFCheckpointDirectory gdal_TIFFCheckpointDirectory +#define TIFFCheckRead gdal_TIFFCheckRead +#define _TIFFCheckRealloc gdal__TIFFCheckRealloc +#define TIFFCheckTile gdal_TIFFCheckTile +#define TIFFCIELabToRGBInit gdal_TIFFCIELabToRGBInit +#define TIFFCIELabToXYZ gdal_TIFFCIELabToXYZ +#define TIFFCleanup gdal_TIFFCleanup +#define TIFFClientdata gdal_TIFFClientdata +#define TIFFClientOpen gdal_TIFFClientOpen +#define TIFFClose gdal_TIFFClose +#define _tiffCloseProc gdal__tiffCloseProc +#define TIFFComputeStrip gdal_TIFFComputeStrip +#define TIFFComputeTile gdal_TIFFComputeTile +#define _TIFFCreateAnonField gdal__TIFFCreateAnonField +#define TIFFCreateCustomDirectory gdal_TIFFCreateCustomDirectory +#define TIFFCreateDirectory gdal_TIFFCreateDirectory +#define TIFFCreateEXIFDirectory gdal_TIFFCreateEXIFDirectory +#define TIFFCurrentDirectory gdal_TIFFCurrentDirectory +#define TIFFCurrentDirOffset gdal_TIFFCurrentDirOffset +#define TIFFCurrentRow gdal_TIFFCurrentRow +#define TIFFCurrentStrip gdal_TIFFCurrentStrip +#define TIFFCurrentTile gdal_TIFFCurrentTile +#define _TIFFDataSize gdal__TIFFDataSize +#define TIFFDataWidth gdal_TIFFDataWidth +#define TIFFDefaultDirectory gdal_TIFFDefaultDirectory +#define TIFFDefaultRefBlackWhite gdal_TIFFDefaultRefBlackWhite +#define TIFFDefaultStripSize gdal_TIFFDefaultStripSize +#define _TIFFDefaultStripSize gdal__TIFFDefaultStripSize +#define TIFFDefaultTileSize gdal_TIFFDefaultTileSize +#define _TIFFDefaultTileSize gdal__TIFFDefaultTileSize +#define TIFFDefaultTransferFunction gdal_TIFFDefaultTransferFunction +#define _tiffDummyMapProc gdal__tiffDummyMapProc +#define _tiffDummyUnmapProc gdal__tiffDummyUnmapProc +#define TIFFError gdal_TIFFError +#define TIFFErrorExt gdal_TIFFErrorExt +#define _TIFFFax3fillruns gdal__TIFFFax3fillruns +#define TIFFFdOpen gdal_TIFFFdOpen +#define TIFFFetchDirectory gdal_TIFFFetchDirectory +#define TIFFFetchNormalTag gdal_TIFFFetchNormalTag +#define TIFFFetchStripThing gdal_TIFFFetchStripThing +#define TIFFFetchSubjectDistance gdal_TIFFFetchSubjectDistance +#define TIFFFieldDataType gdal_TIFFFieldDataType +#define TIFFFieldName gdal_TIFFFieldName +#define TIFFFieldPassCount gdal_TIFFFieldPassCount +#define TIFFFieldReadCount gdal_TIFFFieldReadCount +#define TIFFFieldTag gdal_TIFFFieldTag +#define TIFFFieldWithName gdal_TIFFFieldWithName +#define TIFFFieldWithTag gdal_TIFFFieldWithTag +#define TIFFFieldWriteCount gdal_TIFFFieldWriteCount +#define TIFFFileName gdal_TIFFFileName +#define TIFFFileno gdal_TIFFFileno +#define _TIFFFillStriles gdal__TIFFFillStriles +#define TIFFFillStrip gdal_TIFFFillStrip +#define TIFFFillStripPartial gdal_TIFFFillStripPartial +#define TIFFFillTile gdal_TIFFFillTile +#define TIFFFindCODEC gdal_TIFFFindCODEC +#define TIFFFindField gdal_TIFFFindField +#define _TIFFFindFieldByName gdal__TIFFFindFieldByName +#define _TIFFFindOrRegisterField gdal__TIFFFindOrRegisterField +#define TIFFFlush gdal_TIFFFlush +#define TIFFFlushData gdal_TIFFFlushData +#define TIFFFlushData1 gdal_TIFFFlushData1 +#define _TIFFfree gdal__TIFFfree +#define TIFFFreeDirectory gdal_TIFFFreeDirectory +#define TIFFGetBitRevTable gdal_TIFFGetBitRevTable +#define TIFFGetClientInfo gdal_TIFFGetClientInfo +#define TIFFGetCloseProc gdal_TIFFGetCloseProc +#define TIFFGetConfiguredCODECs gdal_TIFFGetConfiguredCODECs +#define _TIFFGetExifFields gdal__TIFFGetExifFields +#define TIFFGetField gdal_TIFFGetField +#define TIFFGetFieldDefaulted gdal_TIFFGetFieldDefaulted +#define _TIFFGetFields gdal__TIFFGetFields +#define TIFFGetMapFileProc gdal_TIFFGetMapFileProc +#define _TIFFgetMode gdal__TIFFgetMode +#define TIFFGetMode gdal_TIFFGetMode +#define TIFFGetReadProc gdal_TIFFGetReadProc +#define TIFFGetSeekProc gdal_TIFFGetSeekProc +#define TIFFGetSizeProc gdal_TIFFGetSizeProc +#define TIFFGetTagListCount gdal_TIFFGetTagListCount +#define TIFFGetTagListEntry gdal_TIFFGetTagListEntry +#define TIFFGetUnmapFileProc gdal_TIFFGetUnmapFileProc +#define TIFFGetVersion gdal_TIFFGetVersion +#define TIFFGetWriteProc gdal_TIFFGetWriteProc +#define TIFFGrowStrips gdal_TIFFGrowStrips +#define TIFFIsBigEndian gdal_TIFFIsBigEndian +#define TIFFIsByteSwapped gdal_TIFFIsByteSwapped +#define TIFFIsCODECConfigured gdal_TIFFIsCODECConfigured +#define TIFFIsMSB2LSB gdal_TIFFIsMSB2LSB +#define TIFFIsTiled gdal_TIFFIsTiled +#define TIFFIsUpSampled gdal_TIFFIsUpSampled +#define TIFFLastDirectory gdal_TIFFLastDirectory +#define TIFFLinkDirectory gdal_TIFFLinkDirectory +#define _TIFFmalloc gdal__TIFFmalloc +#define _tiffMapProc gdal__tiffMapProc +#define _TIFFmemcmp gdal__TIFFmemcmp +#define _TIFFmemcpy gdal__TIFFmemcpy +#define _TIFFmemset gdal__TIFFmemset +#define TIFFMergeFieldInfo gdal_TIFFMergeFieldInfo +#define _TIFFMergeFields gdal__TIFFMergeFields +#define _TIFFMultiply32 gdal__TIFFMultiply32 +#define _TIFFMultiply64 gdal__TIFFMultiply64 +#define TIFFNoDecode gdal_TIFFNoDecode +#define TIFFNoEncode gdal_TIFFNoEncode +#define _TIFFNoFixupTags gdal__TIFFNoFixupTags +#define _TIFFNoPostDecode gdal__TIFFNoPostDecode +#define _TIFFNoPreCode gdal__TIFFNoPreCode +#define _TIFFNoRowDecode gdal__TIFFNoRowDecode +#define _TIFFNoRowEncode gdal__TIFFNoRowEncode +#define _TIFFNoSeek gdal__TIFFNoSeek +#define _TIFFNoStripDecode gdal__TIFFNoStripDecode +#define _TIFFNoStripEncode gdal__TIFFNoStripEncode +#define _TIFFNoTileDecode gdal__TIFFNoTileDecode +#define _TIFFNoTileEncode gdal__TIFFNoTileEncode +#define TIFFNumberOfDirectories gdal_TIFFNumberOfDirectories +#define TIFFNumberOfStrips gdal_TIFFNumberOfStrips +#define TIFFNumberOfTiles gdal_TIFFNumberOfTiles +#define TIFFOpen gdal_TIFFOpen +#define TIFFPredictorCleanup gdal_TIFFPredictorCleanup +#define TIFFPredictorInit gdal_TIFFPredictorInit +#define _TIFFPrettyPrintField gdal__TIFFPrettyPrintField +#define _TIFFprintAscii gdal__TIFFprintAscii +#define _TIFFprintAsciiBounded gdal__TIFFprintAsciiBounded +#define _TIFFprintAsciiTag gdal__TIFFprintAsciiTag +#define TIFFPrintDirectory gdal_TIFFPrintDirectory +#define _TIFFPrintField gdal__TIFFPrintField +#define _TIFFPrintFieldInfo gdal__TIFFPrintFieldInfo +#define TIFFRasterScanlineSize gdal_TIFFRasterScanlineSize +#define TIFFRasterScanlineSize64 gdal_TIFFRasterScanlineSize64 +#define TIFFRawStripSize gdal_TIFFRawStripSize +#define TIFFRawStripSize64 gdal_TIFFRawStripSize64 +#define TIFFReadBufferSetup gdal_TIFFReadBufferSetup +#define TIFFReadCustomDirectory gdal_TIFFReadCustomDirectory +#define TIFFReadDirectory gdal_TIFFReadDirectory +#define TIFFReadDirectoryCheckOrder gdal_TIFFReadDirectoryCheckOrder +#define TIFFReadDirectoryFindEntry gdal_TIFFReadDirectoryFindEntry +#define TIFFReadDirectoryFindFieldInfo gdal_TIFFReadDirectoryFindFieldInfo +#define TIFFReadDirEntryArray gdal_TIFFReadDirEntryArray +#define TIFFReadDirEntryByte gdal_TIFFReadDirEntryByte +#define TIFFReadDirEntryByteArray gdal_TIFFReadDirEntryByteArray +#define TIFFReadDirEntryCheckedByte gdal_TIFFReadDirEntryCheckedByte +#define TIFFReadDirEntryCheckedDouble gdal_TIFFReadDirEntryCheckedDouble +#define TIFFReadDirEntryCheckedFloat gdal_TIFFReadDirEntryCheckedFloat +#define TIFFReadDirEntryCheckedLong gdal_TIFFReadDirEntryCheckedLong +#define TIFFReadDirEntryCheckedLong8 gdal_TIFFReadDirEntryCheckedLong8 +#define TIFFReadDirEntryCheckedRational gdal_TIFFReadDirEntryCheckedRational +#define TIFFReadDirEntryCheckedSbyte gdal_TIFFReadDirEntryCheckedSbyte +#define TIFFReadDirEntryCheckedShort gdal_TIFFReadDirEntryCheckedShort +#define TIFFReadDirEntryCheckedSlong gdal_TIFFReadDirEntryCheckedSlong +#define TIFFReadDirEntryCheckedSlong8 gdal_TIFFReadDirEntryCheckedSlong8 +#define TIFFReadDirEntryCheckedSrational gdal_TIFFReadDirEntryCheckedSrational +#define TIFFReadDirEntryCheckedSshort gdal_TIFFReadDirEntryCheckedSshort +#define TIFFReadDirEntryCheckRangeByteLong gdal_TIFFReadDirEntryCheckRangeByteLong +#define TIFFReadDirEntryCheckRangeByteLong8 gdal_TIFFReadDirEntryCheckRangeByteLong8 +#define TIFFReadDirEntryCheckRangeByteSbyte gdal_TIFFReadDirEntryCheckRangeByteSbyte +#define TIFFReadDirEntryCheckRangeByteShort gdal_TIFFReadDirEntryCheckRangeByteShort +#define TIFFReadDirEntryCheckRangeByteSlong gdal_TIFFReadDirEntryCheckRangeByteSlong +#define TIFFReadDirEntryCheckRangeByteSlong8 gdal_TIFFReadDirEntryCheckRangeByteSlong8 +#define TIFFReadDirEntryCheckRangeByteSshort gdal_TIFFReadDirEntryCheckRangeByteSshort +#define TIFFReadDirEntryCheckRangeLong8Sbyte gdal_TIFFReadDirEntryCheckRangeLong8Sbyte +#define TIFFReadDirEntryCheckRangeLong8Slong gdal_TIFFReadDirEntryCheckRangeLong8Slong +#define TIFFReadDirEntryCheckRangeLong8Slong8 gdal_TIFFReadDirEntryCheckRangeLong8Slong8 +#define TIFFReadDirEntryCheckRangeLong8Sshort gdal_TIFFReadDirEntryCheckRangeLong8Sshort +#define TIFFReadDirEntryCheckRangeLongLong8 gdal_TIFFReadDirEntryCheckRangeLongLong8 +#define TIFFReadDirEntryCheckRangeLongSbyte gdal_TIFFReadDirEntryCheckRangeLongSbyte +#define TIFFReadDirEntryCheckRangeLongSlong gdal_TIFFReadDirEntryCheckRangeLongSlong +#define TIFFReadDirEntryCheckRangeLongSlong8 gdal_TIFFReadDirEntryCheckRangeLongSlong8 +#define TIFFReadDirEntryCheckRangeLongSshort gdal_TIFFReadDirEntryCheckRangeLongSshort +#define TIFFReadDirEntryCheckRangeSbyteByte gdal_TIFFReadDirEntryCheckRangeSbyteByte +#define TIFFReadDirEntryCheckRangeSbyteLong gdal_TIFFReadDirEntryCheckRangeSbyteLong +#define TIFFReadDirEntryCheckRangeSbyteLong8 gdal_TIFFReadDirEntryCheckRangeSbyteLong8 +#define TIFFReadDirEntryCheckRangeSbyteShort gdal_TIFFReadDirEntryCheckRangeSbyteShort +#define TIFFReadDirEntryCheckRangeSbyteSlong gdal_TIFFReadDirEntryCheckRangeSbyteSlong +#define TIFFReadDirEntryCheckRangeSbyteSlong8 gdal_TIFFReadDirEntryCheckRangeSbyteSlong8 +#define TIFFReadDirEntryCheckRangeSbyteSshort gdal_TIFFReadDirEntryCheckRangeSbyteSshort +#define TIFFReadDirEntryCheckRangeShortLong gdal_TIFFReadDirEntryCheckRangeShortLong +#define TIFFReadDirEntryCheckRangeShortLong8 gdal_TIFFReadDirEntryCheckRangeShortLong8 +#define TIFFReadDirEntryCheckRangeShortSbyte gdal_TIFFReadDirEntryCheckRangeShortSbyte +#define TIFFReadDirEntryCheckRangeShortSlong gdal_TIFFReadDirEntryCheckRangeShortSlong +#define TIFFReadDirEntryCheckRangeShortSlong8 gdal_TIFFReadDirEntryCheckRangeShortSlong8 +#define TIFFReadDirEntryCheckRangeShortSshort gdal_TIFFReadDirEntryCheckRangeShortSshort +#define TIFFReadDirEntryCheckRangeSlong8Long8 gdal_TIFFReadDirEntryCheckRangeSlong8Long8 +#define TIFFReadDirEntryCheckRangeSlongLong gdal_TIFFReadDirEntryCheckRangeSlongLong +#define TIFFReadDirEntryCheckRangeSlongLong8 gdal_TIFFReadDirEntryCheckRangeSlongLong8 +#define TIFFReadDirEntryCheckRangeSlongSlong8 gdal_TIFFReadDirEntryCheckRangeSlongSlong8 +#define TIFFReadDirEntryCheckRangeSshortLong gdal_TIFFReadDirEntryCheckRangeSshortLong +#define TIFFReadDirEntryCheckRangeSshortLong8 gdal_TIFFReadDirEntryCheckRangeSshortLong8 +#define TIFFReadDirEntryCheckRangeSshortShort gdal_TIFFReadDirEntryCheckRangeSshortShort +#define TIFFReadDirEntryCheckRangeSshortSlong gdal_TIFFReadDirEntryCheckRangeSshortSlong +#define TIFFReadDirEntryCheckRangeSshortSlong8 gdal_TIFFReadDirEntryCheckRangeSshortSlong8 +#define TIFFReadDirEntryData gdal_TIFFReadDirEntryData +#define TIFFReadDirEntryDouble gdal_TIFFReadDirEntryDouble +#define TIFFReadDirEntryDoubleArray gdal_TIFFReadDirEntryDoubleArray +#define TIFFReadDirEntryFloat gdal_TIFFReadDirEntryFloat +#define TIFFReadDirEntryFloatArray gdal_TIFFReadDirEntryFloatArray +#define TIFFReadDirEntryIfd8 gdal_TIFFReadDirEntryIfd8 +#define TIFFReadDirEntryIfd8Array gdal_TIFFReadDirEntryIfd8Array +#define TIFFReadDirEntryLong gdal_TIFFReadDirEntryLong +#define TIFFReadDirEntryLong8 gdal_TIFFReadDirEntryLong8 +#define TIFFReadDirEntryLong8Array gdal_TIFFReadDirEntryLong8Array +#define TIFFReadDirEntryLongArray gdal_TIFFReadDirEntryLongArray +#define TIFFReadDirEntryOutputErr gdal_TIFFReadDirEntryOutputErr +#define TIFFReadDirEntryPersampleShort gdal_TIFFReadDirEntryPersampleShort +#define TIFFReadDirEntrySbyteArray gdal_TIFFReadDirEntrySbyteArray +#define TIFFReadDirEntryShort gdal_TIFFReadDirEntryShort +#define TIFFReadDirEntryShortArray gdal_TIFFReadDirEntryShortArray +#define TIFFReadDirEntrySlong8Array gdal_TIFFReadDirEntrySlong8Array +#define TIFFReadDirEntrySlongArray gdal_TIFFReadDirEntrySlongArray +#define TIFFReadDirEntrySshortArray gdal_TIFFReadDirEntrySshortArray +#define TIFFReadEncodedStrip gdal_TIFFReadEncodedStrip +#define TIFFReadEncodedTile gdal_TIFFReadEncodedTile +#define TIFFReadEXIFDirectory gdal_TIFFReadEXIFDirectory +#define _tiffReadProc gdal__tiffReadProc +#define TIFFReadRawStrip gdal_TIFFReadRawStrip +#define TIFFReadRawStrip1 gdal_TIFFReadRawStrip1 +#define TIFFReadRawTile gdal_TIFFReadRawTile +#define TIFFReadRawTile1 gdal_TIFFReadRawTile1 +#define TIFFReadRGBAImage gdal_TIFFReadRGBAImage +#define TIFFReadRGBAImageOriented gdal_TIFFReadRGBAImageOriented +#define TIFFReadRGBAStrip gdal_TIFFReadRGBAStrip +#define TIFFReadRGBATile gdal_TIFFReadRGBATile +#define TIFFReadScanline gdal_TIFFReadScanline +#define TIFFReadTile gdal_TIFFReadTile +#define TIFFReadUInt64 gdal_TIFFReadUInt64 +#define _TIFFrealloc gdal__TIFFrealloc +#define TIFFRegisterCODEC gdal_TIFFRegisterCODEC +#define TIFFReverseBits gdal_TIFFReverseBits +#define TIFFRewriteDirectory gdal_TIFFRewriteDirectory +#define _TIFFRewriteField gdal__TIFFRewriteField +#define TIFFRGBAImageBegin gdal_TIFFRGBAImageBegin +#define TIFFRGBAImageEnd gdal_TIFFRGBAImageEnd +#define TIFFRGBAImageGet gdal_TIFFRGBAImageGet +#define TIFFRGBAImageOK gdal_TIFFRGBAImageOK +#define TIFFScanlineSize gdal_TIFFScanlineSize +#define TIFFScanlineSize64 gdal_TIFFScanlineSize64 +#define TIFFSeek gdal_TIFFSeek +#define _tiffSeekProc gdal__tiffSeekProc +#define _TIFFsetByteArray gdal__TIFFsetByteArray +#define TIFFSetClientdata gdal_TIFFSetClientdata +#define TIFFSetClientInfo gdal_TIFFSetClientInfo +#define TIFFSetCompressionScheme gdal_TIFFSetCompressionScheme +#define _TIFFSetDefaultCompressionState gdal__TIFFSetDefaultCompressionState +#define TIFFSetDirectory gdal_TIFFSetDirectory +#define _TIFFsetDoubleArray gdal__TIFFsetDoubleArray +#define TIFFSetErrorHandler gdal_TIFFSetErrorHandler +#define TIFFSetErrorHandlerExt gdal_TIFFSetErrorHandlerExt +#define TIFFSetField gdal_TIFFSetField +#define TIFFSetFileName gdal_TIFFSetFileName +#define TIFFSetFileno gdal_TIFFSetFileno +#define _TIFFsetFloatArray gdal__TIFFsetFloatArray +#define _TIFFSetGetType gdal__TIFFSetGetType +#define _TIFFsetLong8Array gdal__TIFFsetLong8Array +#define _TIFFsetLongArray gdal__TIFFsetLongArray +#define TIFFSetMode gdal_TIFFSetMode +#define _TIFFsetNString gdal__TIFFsetNString +#define _TIFFsetShortArray gdal__TIFFsetShortArray +#define _TIFFsetString gdal__TIFFsetString +#define TIFFSetSubDirectory gdal_TIFFSetSubDirectory +#define TIFFSetTagExtender gdal_TIFFSetTagExtender +#define _TIFFSetupFields gdal__TIFFSetupFields +#define TIFFSetupStrips gdal_TIFFSetupStrips +#define TIFFSetWarningHandler gdal_TIFFSetWarningHandler +#define TIFFSetWarningHandlerExt gdal_TIFFSetWarningHandlerExt +#define TIFFSetWriteOffset gdal_TIFFSetWriteOffset +#define _tiffSizeProc gdal__tiffSizeProc +#define TIFFStartStrip gdal_TIFFStartStrip +#define TIFFStartTile gdal_TIFFStartTile +#define TIFFStripSize gdal_TIFFStripSize +#define TIFFStripSize64 gdal_TIFFStripSize64 +#define TIFFTileRowSize gdal_TIFFTileRowSize +#define TIFFTileRowSize64 gdal_TIFFTileRowSize64 +#define TIFFTileSize gdal_TIFFTileSize +#define TIFFTileSize64 gdal_TIFFTileSize64 +#define _TIFFtrue gdal__TIFFtrue +#define _TIFFUInt64ToDouble gdal__TIFFUInt64ToDouble +#define _TIFFUInt64ToFloat gdal__TIFFUInt64ToFloat +#define TIFFUnlinkDirectory gdal_TIFFUnlinkDirectory +#define _tiffUnmapProc gdal__tiffUnmapProc +#define TIFFUnRegisterCODEC gdal_TIFFUnRegisterCODEC +#define TIFFUnsetField gdal_TIFFUnsetField +#define TIFFVGetField gdal_TIFFVGetField +#define _TIFFVGetField gdal__TIFFVGetField +#define TIFFVGetFieldDefaulted gdal_TIFFVGetFieldDefaulted +#define _TIFFvoid gdal__TIFFvoid +#define TIFFVSetField gdal_TIFFVSetField +#define _TIFFVSetField gdal__TIFFVSetField +#define TIFFVStripSize gdal_TIFFVStripSize +#define TIFFVStripSize64 gdal_TIFFVStripSize64 +#define TIFFVTileSize gdal_TIFFVTileSize +#define TIFFVTileSize64 gdal_TIFFVTileSize64 +#define TIFFWarning gdal_TIFFWarning +#define TIFFWarningExt gdal_TIFFWarningExt +#define TIFFWriteBufferSetup gdal_TIFFWriteBufferSetup +#define TIFFWriteCheck gdal_TIFFWriteCheck +#define TIFFWriteCustomDirectory gdal_TIFFWriteCustomDirectory +#define TIFFWriteDirectory gdal_TIFFWriteDirectory +#define TIFFWriteDirectorySec gdal_TIFFWriteDirectorySec +#define TIFFWriteDirectoryTagAscii gdal_TIFFWriteDirectoryTagAscii +#define TIFFWriteDirectoryTagByteArray gdal_TIFFWriteDirectoryTagByteArray +#define TIFFWriteDirectoryTagCheckedAscii gdal_TIFFWriteDirectoryTagCheckedAscii +#define TIFFWriteDirectoryTagCheckedByteArray gdal_TIFFWriteDirectoryTagCheckedByteArray +#define TIFFWriteDirectoryTagCheckedDoubleArray gdal_TIFFWriteDirectoryTagCheckedDoubleArray +#define TIFFWriteDirectoryTagCheckedFloatArray gdal_TIFFWriteDirectoryTagCheckedFloatArray +#define TIFFWriteDirectoryTagCheckedIfd8Array gdal_TIFFWriteDirectoryTagCheckedIfd8Array +#define TIFFWriteDirectoryTagCheckedIfdArray gdal_TIFFWriteDirectoryTagCheckedIfdArray +#define TIFFWriteDirectoryTagCheckedLong gdal_TIFFWriteDirectoryTagCheckedLong +#define TIFFWriteDirectoryTagCheckedLong8Array gdal_TIFFWriteDirectoryTagCheckedLong8Array +#define TIFFWriteDirectoryTagCheckedLongArray gdal_TIFFWriteDirectoryTagCheckedLongArray +#define TIFFWriteDirectoryTagCheckedRational gdal_TIFFWriteDirectoryTagCheckedRational +#define TIFFWriteDirectoryTagCheckedRationalArray gdal_TIFFWriteDirectoryTagCheckedRationalArray +#define TIFFWriteDirectoryTagCheckedSbyteArray gdal_TIFFWriteDirectoryTagCheckedSbyteArray +#define TIFFWriteDirectoryTagCheckedShort gdal_TIFFWriteDirectoryTagCheckedShort +#define TIFFWriteDirectoryTagCheckedShortArray gdal_TIFFWriteDirectoryTagCheckedShortArray +#define TIFFWriteDirectoryTagCheckedSlong8Array gdal_TIFFWriteDirectoryTagCheckedSlong8Array +#define TIFFWriteDirectoryTagCheckedSlongArray gdal_TIFFWriteDirectoryTagCheckedSlongArray +#define TIFFWriteDirectoryTagCheckedSrationalArray gdal_TIFFWriteDirectoryTagCheckedSrationalArray +#define TIFFWriteDirectoryTagCheckedSshortArray gdal_TIFFWriteDirectoryTagCheckedSshortArray +#define TIFFWriteDirectoryTagCheckedUndefinedArray gdal_TIFFWriteDirectoryTagCheckedUndefinedArray +#define TIFFWriteDirectoryTagColormap gdal_TIFFWriteDirectoryTagColormap +#define TIFFWriteDirectoryTagData gdal_TIFFWriteDirectoryTagData +#define TIFFWriteDirectoryTagDoubleArray gdal_TIFFWriteDirectoryTagDoubleArray +#define TIFFWriteDirectoryTagFloatArray gdal_TIFFWriteDirectoryTagFloatArray +#define TIFFWriteDirectoryTagIfdArray gdal_TIFFWriteDirectoryTagIfdArray +#define TIFFWriteDirectoryTagIfdIfd8Array gdal_TIFFWriteDirectoryTagIfdIfd8Array +#define TIFFWriteDirectoryTagLong gdal_TIFFWriteDirectoryTagLong +#define TIFFWriteDirectoryTagLong8Array gdal_TIFFWriteDirectoryTagLong8Array +#define TIFFWriteDirectoryTagLongArray gdal_TIFFWriteDirectoryTagLongArray +#define TIFFWriteDirectoryTagLongLong8Array gdal_TIFFWriteDirectoryTagLongLong8Array +#define TIFFWriteDirectoryTagRational gdal_TIFFWriteDirectoryTagRational +#define TIFFWriteDirectoryTagRationalArray gdal_TIFFWriteDirectoryTagRationalArray +#define TIFFWriteDirectoryTagSampleformatArray gdal_TIFFWriteDirectoryTagSampleformatArray +#define TIFFWriteDirectoryTagSbyteArray gdal_TIFFWriteDirectoryTagSbyteArray +#define TIFFWriteDirectoryTagShort gdal_TIFFWriteDirectoryTagShort +#define TIFFWriteDirectoryTagShortArray gdal_TIFFWriteDirectoryTagShortArray +#define TIFFWriteDirectoryTagShortLong gdal_TIFFWriteDirectoryTagShortLong +#define TIFFWriteDirectoryTagShortPerSample gdal_TIFFWriteDirectoryTagShortPerSample +#define TIFFWriteDirectoryTagSlong8Array gdal_TIFFWriteDirectoryTagSlong8Array +#define TIFFWriteDirectoryTagSlongArray gdal_TIFFWriteDirectoryTagSlongArray +#define TIFFWriteDirectoryTagSrationalArray gdal_TIFFWriteDirectoryTagSrationalArray +#define TIFFWriteDirectoryTagSshortArray gdal_TIFFWriteDirectoryTagSshortArray +#define TIFFWriteDirectoryTagSubifd gdal_TIFFWriteDirectoryTagSubifd +#define TIFFWriteDirectoryTagTransferfunction gdal_TIFFWriteDirectoryTagTransferfunction +#define TIFFWriteDirectoryTagUndefinedArray gdal_TIFFWriteDirectoryTagUndefinedArray +#define TIFFWriteEncodedStrip gdal_TIFFWriteEncodedStrip +#define TIFFWriteEncodedTile gdal_TIFFWriteEncodedTile +#define _tiffWriteProc gdal__tiffWriteProc +#define TIFFWriteRawStrip gdal_TIFFWriteRawStrip +#define TIFFWriteRawTile gdal_TIFFWriteRawTile +#define TIFFWriteScanline gdal_TIFFWriteScanline +#define TIFFWriteTile gdal_TIFFWriteTile +#define TIFFXYZToRGB gdal_TIFFXYZToRGB +#define TIFFYCbCrtoRGB gdal_TIFFYCbCrtoRGB +#define TIFFYCbCrToRGBInit gdal_TIFFYCbCrToRGBInit +#define unixErrorHandler gdal_unixErrorHandler +#define unixWarningHandler gdal_unixWarningHandler +#define uv_decode gdal_uv_decode +#define uv_encode gdal_uv_encode +#define XYZtoRGB24 gdal_XYZtoRGB24 +#define ZIPCleanup gdal_ZIPCleanup +#define ZIPDecode gdal_ZIPDecode +#define ZIPEncode gdal_ZIPEncode +#define ZIPFixupTags gdal_ZIPFixupTags +#define ZIPPostEncode gdal_ZIPPostEncode +#define ZIPPreDecode gdal_ZIPPreDecode +#define ZIPPreEncode gdal_ZIPPreEncode +#define ZIPSetupDecode gdal_ZIPSetupDecode +#define ZIPSetupEncode gdal_ZIPSetupEncode +#define ZIPVGetField gdal_ZIPVGetField +#define ZIPVSetField gdal_ZIPVSetField +#define _msbmask gdal__msbmask +#define zeroruns gdal_zeroruns +#define oneruns gdal_oneruns +#define horizcode gdal_horizcode +#define passcode gdal_passcode +#define vcodes gdal_vcodes +#define photoTag gdal_photoTag +#define display_sRGB gdal_display_sRGB +#define TIFFBitRevTable gdal_TIFFBitRevTable +#define TIFFNoBitRevTable gdal_TIFFNoBitRevTable +#define twobitdeltas gdal_twobitdeltas +#define threebitdeltas gdal_threebitdeltas +#define TIFFVersion gdal_TIFFVersion +#define TIFFFaxWhiteCodes gdal_TIFFFaxWhiteCodes +#define TIFFFaxWhiteTable gdal_TIFFFaxWhiteTable +#define TIFFFaxBlackCodes gdal_TIFFFaxBlackCodes +#define TIFFFaxMainTable gdal_TIFFFaxMainTable +#define TIFFFaxBlackTable gdal_TIFFFaxBlackTable +#define tiffFieldArray gdal_tiffFieldArray +#define tiffFields gdal_tiffFields +#define exifFieldArray gdal_exifFieldArray +#define exifFields gdal_exifFields +#define faxFields gdal_faxFields +#define fax3Fields gdal_fax3Fields +#define fax4Fields gdal_fax4Fields +#define uv_row gdal_uv_row +#define LogLuvFields gdal_LogLuvFields +#define lzmaFields gdal_lzmaFields +#define ojpegFields gdal_ojpegFields +#define pixarlogFields gdal_pixarlogFields +#define predictFields gdal_predictFields +#define photoNames gdal_photoNames +#define orientNames gdal_orientNames +#define zipFields gdal_zipFields +#define _TIFFerrorHandler gdal__TIFFerrorHandler +#define _TIFFBuiltinCODECS gdal__TIFFBuiltinCODECS +#define _TIFFwarningHandler gdal__TIFFwarningHandler +#define registeredCODECS gdal_registeredCODECS +#define _TIFFextender gdal__TIFFextender +#define Fltsize gdal_Fltsize +#define LogK1 gdal_LogK1 +#define LogK2 gdal_LogK2 +#define _TIFFwarningHandlerExt gdal__TIFFwarningHandlerExt +#define _TIFFerrorHandlerExt gdal__TIFFerrorHandlerExt +#define TIFFInitDumpMode gdal_TIFFInitDumpMode +#define TIFFReInitJPEG_12 gdal_TIFFReInitJPEG_12 + +#ifdef LZW_SUPPORT +#define TIFFInitLZW gdal_TIFFInitLZW +#endif +#ifdef PACKBITS_SUPPORT +#define TIFFInitPackBits gdal_TIFFInitPackBits +#endif +#ifdef THUNDER_SUPPORT +#define TIFFInitThunderScan gdal_TIFFInitThunderScan +#endif +#ifdef NEXT_SUPPORT +#define TIFFInitNeXT gdal_TIFFInitNeXT +#endif +#ifdef JPEG_SUPPORT +#define TIFFInitJPEG gdal_TIFFInitJPEG +#define TIFFInitJPEG_12 gdal_TIFFInitJPEG_12 +#endif +#ifdef OJPEG_SUPPORT +#define TIFFInitOJPEG gdal_TIFFInitOJPEG +#endif +#ifdef CCITT_SUPPORT +#define TIFFInitCCITTRLE gdal_TIFFInitCCITTRLE +#define TIFFInitCCITTRLEW gdal_TIFFInitCCITTRLEW +#define TIFFInitCCITTFax3 gdal_TIFFInitCCITTFax3 +#define TIFFInitCCITTFax4 gdal_TIFFInitCCITTFax4 +#endif +#ifdef JBIG_SUPPORT +#define TIFFInitJBIG gdal_TIFFInitJBIG +#endif +#ifdef ZIP_SUPPORT +#define TIFFInitZIP gdal_TIFFInitZIP +#endif +#ifdef PIXARLOG_SUPPORT +#define TIFFInitPixarLog gdal_TIFFInitPixarLog +#endif +#ifdef LOGLUV_SUPPORT +#define TIFFInitSGILog gdal_TIFFInitSGILog +#endif +#ifdef LZMA_SUPPORT +#define TIFFInitLZMA gdal_TIFFInitLZMA +#endif diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/makefile.vc b/bazaar/plugin/gdal/frmts/gtiff/libtiff/makefile.vc new file mode 100644 index 000000000..23375366c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/makefile.vc @@ -0,0 +1,79 @@ + +OBJ = \ + tif_aux.obj \ + tif_close.obj \ + tif_codec.obj \ + tif_color.obj \ + tif_compress.obj \ + tif_dir.obj \ + tif_dirinfo.obj \ + tif_dirread.obj \ + tif_dirwrite.obj \ + tif_dumpmode.obj \ + tif_error.obj \ + tif_extension.obj \ + tif_fax3.obj \ + tif_fax3sm.obj \ + tif_getimage.obj \ + tif_jpeg.obj \ + tif_jpeg_12.obj \ + tif_flush.obj \ + tif_luv.obj \ + tif_lzw.obj \ + tif_next.obj \ + tif_ojpeg.obj \ + tif_open.obj \ + tif_packbits.obj \ + tif_pixarlog.obj \ + tif_predict.obj \ + tif_print.obj \ + tif_read.obj \ + tif_swab.obj \ + tif_strip.obj \ + tif_thunder.obj \ + tif_tile.obj \ + tif_vsi.obj \ + tif_version.obj \ + tif_warning.obj \ + tif_write.obj \ + tif_zip.obj \ + tif_lzma.obj + +GDAL_ROOT = ..\..\.. + +EXTRAFLAGS = -I..\..\zlib -DZIP_SUPPORT -DPIXARLOG_SUPPORT \ + $(JPEG_FLAGS) $(JPEG12_FLAGS) $(LZMA_FLAGS) \ + $(SOFTWARNFLAGS) + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF JPEG_SUPPORTED +!IFDEF JPEG_EXTERNAL_LIB +JPEG_FLAGS = -I$(JPEGDIR) -DJPEG_SUPPORT -DOJPEG_SUPPORT +!ELSE +JPEG_FLAGS = -I..\..\jpeg\libjpeg -DJPEG_SUPPORT -DOJPEG_SUPPORT +!ENDIF +!ENDIF + +!IFDEF JPEG12_SUPPORTED +JPEG12_FLAGS = -DJPEG_DUAL_MODE_8_12 +EXTRA_DEP = libjpeg12src +!ENDIF + +!IFDEF LZMA_CFLAGS +LZMA_FLAGS = $(LZMA_CFLAGS) -DLZMA_SUPPORT +!ENDIF + + + +default: $(EXTRA_DEP) $(OBJ) + xcopy /D /Y *.obj ..\..\o + +clean: + del *.obj + +libjpeg12src: + cd ../../jpeg/libjpeg12 + $(MAKE) /f makefile.vc jcapimin12.c + cd ../../gtiff/libtiff + diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/t4.h b/bazaar/plugin/gdal/frmts/gtiff/libtiff/t4.h new file mode 100644 index 000000000..b908f54f0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/t4.h @@ -0,0 +1,292 @@ +/* $Id: t4.h,v 1.3 2010-03-10 18:56:48 bfriesen Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _T4_ +#define _T4_ +/* + * CCITT T.4 1D Huffman runlength codes and + * related definitions. Given the small sizes + * of these tables it does not seem + * worthwhile to make code & length 8 bits. + */ +typedef struct tableentry { + unsigned short length; /* bit length of g3 code */ + unsigned short code; /* g3 code */ + short runlen; /* run length in bits */ +} tableentry; + +#define EOL 0x001 /* EOL code value - 0000 0000 0000 1 */ + +/* status values returned instead of a run length */ +#define G3CODE_EOL -1 /* NB: ACT_EOL - ACT_WRUNT */ +#define G3CODE_INVALID -2 /* NB: ACT_INVALID - ACT_WRUNT */ +#define G3CODE_EOF -3 /* end of input data */ +#define G3CODE_INCOMP -4 /* incomplete run code */ + +/* + * Note that these tables are ordered such that the + * index into the table is known to be either the + * run length, or (run length / 64) + a fixed offset. + * + * NB: The G3CODE_INVALID entries are only used + * during state generation (see mkg3states.c). + */ +#ifdef G3CODES +const tableentry TIFFFaxWhiteCodes[] = { + { 8, 0x35, 0 }, /* 0011 0101 */ + { 6, 0x7, 1 }, /* 0001 11 */ + { 4, 0x7, 2 }, /* 0111 */ + { 4, 0x8, 3 }, /* 1000 */ + { 4, 0xB, 4 }, /* 1011 */ + { 4, 0xC, 5 }, /* 1100 */ + { 4, 0xE, 6 }, /* 1110 */ + { 4, 0xF, 7 }, /* 1111 */ + { 5, 0x13, 8 }, /* 1001 1 */ + { 5, 0x14, 9 }, /* 1010 0 */ + { 5, 0x7, 10 }, /* 0011 1 */ + { 5, 0x8, 11 }, /* 0100 0 */ + { 6, 0x8, 12 }, /* 0010 00 */ + { 6, 0x3, 13 }, /* 0000 11 */ + { 6, 0x34, 14 }, /* 1101 00 */ + { 6, 0x35, 15 }, /* 1101 01 */ + { 6, 0x2A, 16 }, /* 1010 10 */ + { 6, 0x2B, 17 }, /* 1010 11 */ + { 7, 0x27, 18 }, /* 0100 111 */ + { 7, 0xC, 19 }, /* 0001 100 */ + { 7, 0x8, 20 }, /* 0001 000 */ + { 7, 0x17, 21 }, /* 0010 111 */ + { 7, 0x3, 22 }, /* 0000 011 */ + { 7, 0x4, 23 }, /* 0000 100 */ + { 7, 0x28, 24 }, /* 0101 000 */ + { 7, 0x2B, 25 }, /* 0101 011 */ + { 7, 0x13, 26 }, /* 0010 011 */ + { 7, 0x24, 27 }, /* 0100 100 */ + { 7, 0x18, 28 }, /* 0011 000 */ + { 8, 0x2, 29 }, /* 0000 0010 */ + { 8, 0x3, 30 }, /* 0000 0011 */ + { 8, 0x1A, 31 }, /* 0001 1010 */ + { 8, 0x1B, 32 }, /* 0001 1011 */ + { 8, 0x12, 33 }, /* 0001 0010 */ + { 8, 0x13, 34 }, /* 0001 0011 */ + { 8, 0x14, 35 }, /* 0001 0100 */ + { 8, 0x15, 36 }, /* 0001 0101 */ + { 8, 0x16, 37 }, /* 0001 0110 */ + { 8, 0x17, 38 }, /* 0001 0111 */ + { 8, 0x28, 39 }, /* 0010 1000 */ + { 8, 0x29, 40 }, /* 0010 1001 */ + { 8, 0x2A, 41 }, /* 0010 1010 */ + { 8, 0x2B, 42 }, /* 0010 1011 */ + { 8, 0x2C, 43 }, /* 0010 1100 */ + { 8, 0x2D, 44 }, /* 0010 1101 */ + { 8, 0x4, 45 }, /* 0000 0100 */ + { 8, 0x5, 46 }, /* 0000 0101 */ + { 8, 0xA, 47 }, /* 0000 1010 */ + { 8, 0xB, 48 }, /* 0000 1011 */ + { 8, 0x52, 49 }, /* 0101 0010 */ + { 8, 0x53, 50 }, /* 0101 0011 */ + { 8, 0x54, 51 }, /* 0101 0100 */ + { 8, 0x55, 52 }, /* 0101 0101 */ + { 8, 0x24, 53 }, /* 0010 0100 */ + { 8, 0x25, 54 }, /* 0010 0101 */ + { 8, 0x58, 55 }, /* 0101 1000 */ + { 8, 0x59, 56 }, /* 0101 1001 */ + { 8, 0x5A, 57 }, /* 0101 1010 */ + { 8, 0x5B, 58 }, /* 0101 1011 */ + { 8, 0x4A, 59 }, /* 0100 1010 */ + { 8, 0x4B, 60 }, /* 0100 1011 */ + { 8, 0x32, 61 }, /* 0011 0010 */ + { 8, 0x33, 62 }, /* 0011 0011 */ + { 8, 0x34, 63 }, /* 0011 0100 */ + { 5, 0x1B, 64 }, /* 1101 1 */ + { 5, 0x12, 128 }, /* 1001 0 */ + { 6, 0x17, 192 }, /* 0101 11 */ + { 7, 0x37, 256 }, /* 0110 111 */ + { 8, 0x36, 320 }, /* 0011 0110 */ + { 8, 0x37, 384 }, /* 0011 0111 */ + { 8, 0x64, 448 }, /* 0110 0100 */ + { 8, 0x65, 512 }, /* 0110 0101 */ + { 8, 0x68, 576 }, /* 0110 1000 */ + { 8, 0x67, 640 }, /* 0110 0111 */ + { 9, 0xCC, 704 }, /* 0110 0110 0 */ + { 9, 0xCD, 768 }, /* 0110 0110 1 */ + { 9, 0xD2, 832 }, /* 0110 1001 0 */ + { 9, 0xD3, 896 }, /* 0110 1001 1 */ + { 9, 0xD4, 960 }, /* 0110 1010 0 */ + { 9, 0xD5, 1024 }, /* 0110 1010 1 */ + { 9, 0xD6, 1088 }, /* 0110 1011 0 */ + { 9, 0xD7, 1152 }, /* 0110 1011 1 */ + { 9, 0xD8, 1216 }, /* 0110 1100 0 */ + { 9, 0xD9, 1280 }, /* 0110 1100 1 */ + { 9, 0xDA, 1344 }, /* 0110 1101 0 */ + { 9, 0xDB, 1408 }, /* 0110 1101 1 */ + { 9, 0x98, 1472 }, /* 0100 1100 0 */ + { 9, 0x99, 1536 }, /* 0100 1100 1 */ + { 9, 0x9A, 1600 }, /* 0100 1101 0 */ + { 6, 0x18, 1664 }, /* 0110 00 */ + { 9, 0x9B, 1728 }, /* 0100 1101 1 */ + { 11, 0x8, 1792 }, /* 0000 0001 000 */ + { 11, 0xC, 1856 }, /* 0000 0001 100 */ + { 11, 0xD, 1920 }, /* 0000 0001 101 */ + { 12, 0x12, 1984 }, /* 0000 0001 0010 */ + { 12, 0x13, 2048 }, /* 0000 0001 0011 */ + { 12, 0x14, 2112 }, /* 0000 0001 0100 */ + { 12, 0x15, 2176 }, /* 0000 0001 0101 */ + { 12, 0x16, 2240 }, /* 0000 0001 0110 */ + { 12, 0x17, 2304 }, /* 0000 0001 0111 */ + { 12, 0x1C, 2368 }, /* 0000 0001 1100 */ + { 12, 0x1D, 2432 }, /* 0000 0001 1101 */ + { 12, 0x1E, 2496 }, /* 0000 0001 1110 */ + { 12, 0x1F, 2560 }, /* 0000 0001 1111 */ + { 12, 0x1, G3CODE_EOL }, /* 0000 0000 0001 */ + { 9, 0x1, G3CODE_INVALID }, /* 0000 0000 1 */ + { 10, 0x1, G3CODE_INVALID }, /* 0000 0000 01 */ + { 11, 0x1, G3CODE_INVALID }, /* 0000 0000 001 */ + { 12, 0x0, G3CODE_INVALID }, /* 0000 0000 0000 */ +}; + +const tableentry TIFFFaxBlackCodes[] = { + { 10, 0x37, 0 }, /* 0000 1101 11 */ + { 3, 0x2, 1 }, /* 010 */ + { 2, 0x3, 2 }, /* 11 */ + { 2, 0x2, 3 }, /* 10 */ + { 3, 0x3, 4 }, /* 011 */ + { 4, 0x3, 5 }, /* 0011 */ + { 4, 0x2, 6 }, /* 0010 */ + { 5, 0x3, 7 }, /* 0001 1 */ + { 6, 0x5, 8 }, /* 0001 01 */ + { 6, 0x4, 9 }, /* 0001 00 */ + { 7, 0x4, 10 }, /* 0000 100 */ + { 7, 0x5, 11 }, /* 0000 101 */ + { 7, 0x7, 12 }, /* 0000 111 */ + { 8, 0x4, 13 }, /* 0000 0100 */ + { 8, 0x7, 14 }, /* 0000 0111 */ + { 9, 0x18, 15 }, /* 0000 1100 0 */ + { 10, 0x17, 16 }, /* 0000 0101 11 */ + { 10, 0x18, 17 }, /* 0000 0110 00 */ + { 10, 0x8, 18 }, /* 0000 0010 00 */ + { 11, 0x67, 19 }, /* 0000 1100 111 */ + { 11, 0x68, 20 }, /* 0000 1101 000 */ + { 11, 0x6C, 21 }, /* 0000 1101 100 */ + { 11, 0x37, 22 }, /* 0000 0110 111 */ + { 11, 0x28, 23 }, /* 0000 0101 000 */ + { 11, 0x17, 24 }, /* 0000 0010 111 */ + { 11, 0x18, 25 }, /* 0000 0011 000 */ + { 12, 0xCA, 26 }, /* 0000 1100 1010 */ + { 12, 0xCB, 27 }, /* 0000 1100 1011 */ + { 12, 0xCC, 28 }, /* 0000 1100 1100 */ + { 12, 0xCD, 29 }, /* 0000 1100 1101 */ + { 12, 0x68, 30 }, /* 0000 0110 1000 */ + { 12, 0x69, 31 }, /* 0000 0110 1001 */ + { 12, 0x6A, 32 }, /* 0000 0110 1010 */ + { 12, 0x6B, 33 }, /* 0000 0110 1011 */ + { 12, 0xD2, 34 }, /* 0000 1101 0010 */ + { 12, 0xD3, 35 }, /* 0000 1101 0011 */ + { 12, 0xD4, 36 }, /* 0000 1101 0100 */ + { 12, 0xD5, 37 }, /* 0000 1101 0101 */ + { 12, 0xD6, 38 }, /* 0000 1101 0110 */ + { 12, 0xD7, 39 }, /* 0000 1101 0111 */ + { 12, 0x6C, 40 }, /* 0000 0110 1100 */ + { 12, 0x6D, 41 }, /* 0000 0110 1101 */ + { 12, 0xDA, 42 }, /* 0000 1101 1010 */ + { 12, 0xDB, 43 }, /* 0000 1101 1011 */ + { 12, 0x54, 44 }, /* 0000 0101 0100 */ + { 12, 0x55, 45 }, /* 0000 0101 0101 */ + { 12, 0x56, 46 }, /* 0000 0101 0110 */ + { 12, 0x57, 47 }, /* 0000 0101 0111 */ + { 12, 0x64, 48 }, /* 0000 0110 0100 */ + { 12, 0x65, 49 }, /* 0000 0110 0101 */ + { 12, 0x52, 50 }, /* 0000 0101 0010 */ + { 12, 0x53, 51 }, /* 0000 0101 0011 */ + { 12, 0x24, 52 }, /* 0000 0010 0100 */ + { 12, 0x37, 53 }, /* 0000 0011 0111 */ + { 12, 0x38, 54 }, /* 0000 0011 1000 */ + { 12, 0x27, 55 }, /* 0000 0010 0111 */ + { 12, 0x28, 56 }, /* 0000 0010 1000 */ + { 12, 0x58, 57 }, /* 0000 0101 1000 */ + { 12, 0x59, 58 }, /* 0000 0101 1001 */ + { 12, 0x2B, 59 }, /* 0000 0010 1011 */ + { 12, 0x2C, 60 }, /* 0000 0010 1100 */ + { 12, 0x5A, 61 }, /* 0000 0101 1010 */ + { 12, 0x66, 62 }, /* 0000 0110 0110 */ + { 12, 0x67, 63 }, /* 0000 0110 0111 */ + { 10, 0xF, 64 }, /* 0000 0011 11 */ + { 12, 0xC8, 128 }, /* 0000 1100 1000 */ + { 12, 0xC9, 192 }, /* 0000 1100 1001 */ + { 12, 0x5B, 256 }, /* 0000 0101 1011 */ + { 12, 0x33, 320 }, /* 0000 0011 0011 */ + { 12, 0x34, 384 }, /* 0000 0011 0100 */ + { 12, 0x35, 448 }, /* 0000 0011 0101 */ + { 13, 0x6C, 512 }, /* 0000 0011 0110 0 */ + { 13, 0x6D, 576 }, /* 0000 0011 0110 1 */ + { 13, 0x4A, 640 }, /* 0000 0010 0101 0 */ + { 13, 0x4B, 704 }, /* 0000 0010 0101 1 */ + { 13, 0x4C, 768 }, /* 0000 0010 0110 0 */ + { 13, 0x4D, 832 }, /* 0000 0010 0110 1 */ + { 13, 0x72, 896 }, /* 0000 0011 1001 0 */ + { 13, 0x73, 960 }, /* 0000 0011 1001 1 */ + { 13, 0x74, 1024 }, /* 0000 0011 1010 0 */ + { 13, 0x75, 1088 }, /* 0000 0011 1010 1 */ + { 13, 0x76, 1152 }, /* 0000 0011 1011 0 */ + { 13, 0x77, 1216 }, /* 0000 0011 1011 1 */ + { 13, 0x52, 1280 }, /* 0000 0010 1001 0 */ + { 13, 0x53, 1344 }, /* 0000 0010 1001 1 */ + { 13, 0x54, 1408 }, /* 0000 0010 1010 0 */ + { 13, 0x55, 1472 }, /* 0000 0010 1010 1 */ + { 13, 0x5A, 1536 }, /* 0000 0010 1101 0 */ + { 13, 0x5B, 1600 }, /* 0000 0010 1101 1 */ + { 13, 0x64, 1664 }, /* 0000 0011 0010 0 */ + { 13, 0x65, 1728 }, /* 0000 0011 0010 1 */ + { 11, 0x8, 1792 }, /* 0000 0001 000 */ + { 11, 0xC, 1856 }, /* 0000 0001 100 */ + { 11, 0xD, 1920 }, /* 0000 0001 101 */ + { 12, 0x12, 1984 }, /* 0000 0001 0010 */ + { 12, 0x13, 2048 }, /* 0000 0001 0011 */ + { 12, 0x14, 2112 }, /* 0000 0001 0100 */ + { 12, 0x15, 2176 }, /* 0000 0001 0101 */ + { 12, 0x16, 2240 }, /* 0000 0001 0110 */ + { 12, 0x17, 2304 }, /* 0000 0001 0111 */ + { 12, 0x1C, 2368 }, /* 0000 0001 1100 */ + { 12, 0x1D, 2432 }, /* 0000 0001 1101 */ + { 12, 0x1E, 2496 }, /* 0000 0001 1110 */ + { 12, 0x1F, 2560 }, /* 0000 0001 1111 */ + { 12, 0x1, G3CODE_EOL }, /* 0000 0000 0001 */ + { 9, 0x1, G3CODE_INVALID }, /* 0000 0000 1 */ + { 10, 0x1, G3CODE_INVALID }, /* 0000 0000 01 */ + { 11, 0x1, G3CODE_INVALID }, /* 0000 0000 001 */ + { 12, 0x0, G3CODE_INVALID }, /* 0000 0000 0000 */ +}; +#else +extern const tableentry TIFFFaxWhiteCodes[]; +extern const tableentry TIFFFaxBlackCodes[]; +#endif +#endif /* _T4_ */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_aux.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_aux.c new file mode 100644 index 000000000..927150a49 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_aux.c @@ -0,0 +1,358 @@ +/* $Id: tif_aux.c,v 1.26 2010-07-01 15:33:28 dron Exp $ */ + +/* + * Copyright (c) 1991-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Auxiliary Support Routines. + */ +#include "tiffiop.h" +#include "tif_predict.h" +#include + +uint32 +_TIFFMultiply32(TIFF* tif, uint32 first, uint32 second, const char* where) +{ + uint32 bytes = first * second; + + if (second && bytes / second != first) { + TIFFErrorExt(tif->tif_clientdata, where, "Integer overflow in %s", where); + bytes = 0; + } + + return bytes; +} + +uint64 +_TIFFMultiply64(TIFF* tif, uint64 first, uint64 second, const char* where) +{ + uint64 bytes = first * second; + + if (second && bytes / second != first) { + TIFFErrorExt(tif->tif_clientdata, where, "Integer overflow in %s", where); + bytes = 0; + } + + return bytes; +} + +void* +_TIFFCheckRealloc(TIFF* tif, void* buffer, + tmsize_t nmemb, tmsize_t elem_size, const char* what) +{ + void* cp = NULL; + tmsize_t bytes = nmemb * elem_size; + + /* + * XXX: Check for integer overflow. + */ + if (nmemb && elem_size && bytes / elem_size == nmemb) + cp = _TIFFrealloc(buffer, bytes); + + if (cp == NULL) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Failed to allocate memory for %s " + "(%ld elements of %ld bytes each)", + what,(long) nmemb, (long) elem_size); + } + + return cp; +} + +void* +_TIFFCheckMalloc(TIFF* tif, tmsize_t nmemb, tmsize_t elem_size, const char* what) +{ + return _TIFFCheckRealloc(tif, NULL, nmemb, elem_size, what); +} + +static int +TIFFDefaultTransferFunction(TIFFDirectory* td) +{ + uint16 **tf = td->td_transferfunction; + tmsize_t i, n, nbytes; + + tf[0] = tf[1] = tf[2] = 0; + if (td->td_bitspersample >= sizeof(tmsize_t) * 8 - 2) + return 0; + + n = ((tmsize_t)1)<td_bitspersample; + nbytes = n * sizeof (uint16); + if (!(tf[0] = (uint16 *)_TIFFmalloc(nbytes))) + return 0; + tf[0][0] = 0; + for (i = 1; i < n; i++) { + double t = (double)i/((double) n-1.); + tf[0][i] = (uint16)floor(65535.*pow(t, 2.2) + .5); + } + + if (td->td_samplesperpixel - td->td_extrasamples > 1) { + if (!(tf[1] = (uint16 *)_TIFFmalloc(nbytes))) + goto bad; + _TIFFmemcpy(tf[1], tf[0], nbytes); + if (!(tf[2] = (uint16 *)_TIFFmalloc(nbytes))) + goto bad; + _TIFFmemcpy(tf[2], tf[0], nbytes); + } + return 1; + +bad: + if (tf[0]) + _TIFFfree(tf[0]); + if (tf[1]) + _TIFFfree(tf[1]); + if (tf[2]) + _TIFFfree(tf[2]); + tf[0] = tf[1] = tf[2] = 0; + return 0; +} + +static int +TIFFDefaultRefBlackWhite(TIFFDirectory* td) +{ + int i; + + if (!(td->td_refblackwhite = (float *)_TIFFmalloc(6*sizeof (float)))) + return 0; + if (td->td_photometric == PHOTOMETRIC_YCBCR) { + /* + * YCbCr (Class Y) images must have the ReferenceBlackWhite + * tag set. Fix the broken images, which lacks that tag. + */ + td->td_refblackwhite[0] = 0.0F; + td->td_refblackwhite[1] = td->td_refblackwhite[3] = + td->td_refblackwhite[5] = 255.0F; + td->td_refblackwhite[2] = td->td_refblackwhite[4] = 128.0F; + } else { + /* + * Assume RGB (Class R) + */ + for (i = 0; i < 3; i++) { + td->td_refblackwhite[2*i+0] = 0; + td->td_refblackwhite[2*i+1] = + (float)((1L<td_bitspersample)-1L); + } + } + return 1; +} + +/* + * Like TIFFGetField, but return any default + * value if the tag is not present in the directory. + * + * NB: We use the value in the directory, rather than + * explcit values so that defaults exist only one + * place in the library -- in TIFFDefaultDirectory. + */ +int +TIFFVGetFieldDefaulted(TIFF* tif, uint32 tag, va_list ap) +{ + TIFFDirectory *td = &tif->tif_dir; + + if (TIFFVGetField(tif, tag, ap)) + return (1); + switch (tag) { + case TIFFTAG_SUBFILETYPE: + *va_arg(ap, uint32 *) = td->td_subfiletype; + return (1); + case TIFFTAG_BITSPERSAMPLE: + *va_arg(ap, uint16 *) = td->td_bitspersample; + return (1); + case TIFFTAG_THRESHHOLDING: + *va_arg(ap, uint16 *) = td->td_threshholding; + return (1); + case TIFFTAG_FILLORDER: + *va_arg(ap, uint16 *) = td->td_fillorder; + return (1); + case TIFFTAG_ORIENTATION: + *va_arg(ap, uint16 *) = td->td_orientation; + return (1); + case TIFFTAG_SAMPLESPERPIXEL: + *va_arg(ap, uint16 *) = td->td_samplesperpixel; + return (1); + case TIFFTAG_ROWSPERSTRIP: + *va_arg(ap, uint32 *) = td->td_rowsperstrip; + return (1); + case TIFFTAG_MINSAMPLEVALUE: + *va_arg(ap, uint16 *) = td->td_minsamplevalue; + return (1); + case TIFFTAG_MAXSAMPLEVALUE: + *va_arg(ap, uint16 *) = td->td_maxsamplevalue; + return (1); + case TIFFTAG_PLANARCONFIG: + *va_arg(ap, uint16 *) = td->td_planarconfig; + return (1); + case TIFFTAG_RESOLUTIONUNIT: + *va_arg(ap, uint16 *) = td->td_resolutionunit; + return (1); + case TIFFTAG_PREDICTOR: + { + TIFFPredictorState* sp = (TIFFPredictorState*) tif->tif_data; + *va_arg(ap, uint16*) = (uint16) sp->predictor; + return 1; + } + case TIFFTAG_DOTRANGE: + *va_arg(ap, uint16 *) = 0; + *va_arg(ap, uint16 *) = (1<td_bitspersample)-1; + return (1); + case TIFFTAG_INKSET: + *va_arg(ap, uint16 *) = INKSET_CMYK; + return 1; + case TIFFTAG_NUMBEROFINKS: + *va_arg(ap, uint16 *) = 4; + return (1); + case TIFFTAG_EXTRASAMPLES: + *va_arg(ap, uint16 *) = td->td_extrasamples; + *va_arg(ap, uint16 **) = td->td_sampleinfo; + return (1); + case TIFFTAG_MATTEING: + *va_arg(ap, uint16 *) = + (td->td_extrasamples == 1 && + td->td_sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA); + return (1); + case TIFFTAG_TILEDEPTH: + *va_arg(ap, uint32 *) = td->td_tiledepth; + return (1); + case TIFFTAG_DATATYPE: + *va_arg(ap, uint16 *) = td->td_sampleformat-1; + return (1); + case TIFFTAG_SAMPLEFORMAT: + *va_arg(ap, uint16 *) = td->td_sampleformat; + return(1); + case TIFFTAG_IMAGEDEPTH: + *va_arg(ap, uint32 *) = td->td_imagedepth; + return (1); + case TIFFTAG_YCBCRCOEFFICIENTS: + { + /* defaults are from CCIR Recommendation 601-1 */ + static float ycbcrcoeffs[] = { 0.299f, 0.587f, 0.114f }; + *va_arg(ap, float **) = ycbcrcoeffs; + return 1; + } + case TIFFTAG_YCBCRSUBSAMPLING: + *va_arg(ap, uint16 *) = td->td_ycbcrsubsampling[0]; + *va_arg(ap, uint16 *) = td->td_ycbcrsubsampling[1]; + return (1); + case TIFFTAG_YCBCRPOSITIONING: + *va_arg(ap, uint16 *) = td->td_ycbcrpositioning; + return (1); + case TIFFTAG_WHITEPOINT: + { + static float whitepoint[2]; + + /* TIFF 6.0 specification tells that it is no default + value for the WhitePoint, but AdobePhotoshop TIFF + Technical Note tells that it should be CIE D50. */ + whitepoint[0] = D50_X0 / (D50_X0 + D50_Y0 + D50_Z0); + whitepoint[1] = D50_Y0 / (D50_X0 + D50_Y0 + D50_Z0); + *va_arg(ap, float **) = whitepoint; + return 1; + } + case TIFFTAG_TRANSFERFUNCTION: + if (!td->td_transferfunction[0] && + !TIFFDefaultTransferFunction(td)) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "No space for \"TransferFunction\" tag"); + return (0); + } + *va_arg(ap, uint16 **) = td->td_transferfunction[0]; + if (td->td_samplesperpixel - td->td_extrasamples > 1) { + *va_arg(ap, uint16 **) = td->td_transferfunction[1]; + *va_arg(ap, uint16 **) = td->td_transferfunction[2]; + } + return (1); + case TIFFTAG_REFERENCEBLACKWHITE: + if (!td->td_refblackwhite && !TIFFDefaultRefBlackWhite(td)) + return (0); + *va_arg(ap, float **) = td->td_refblackwhite; + return (1); + } + return 0; +} + +/* + * Like TIFFGetField, but return any default + * value if the tag is not present in the directory. + */ +int +TIFFGetFieldDefaulted(TIFF* tif, uint32 tag, ...) +{ + int ok; + va_list ap; + + va_start(ap, tag); + ok = TIFFVGetFieldDefaulted(tif, tag, ap); + va_end(ap); + return (ok); +} + +struct _Int64Parts { + int32 low, high; +}; + +typedef union { + struct _Int64Parts part; + int64 value; +} _Int64; + +float +_TIFFUInt64ToFloat(uint64 ui64) +{ + _Int64 i; + + i.value = ui64; + if (i.part.high >= 0) { + return (float)i.value; + } else { + long double df; + df = (long double)i.value; + df += 18446744073709551616.0; /* adding 2**64 */ + return (float)df; + } +} + +double +_TIFFUInt64ToDouble(uint64 ui64) +{ + _Int64 i; + + i.value = ui64; + if (i.part.high >= 0) { + return (double)i.value; + } else { + long double df; + df = (long double)i.value; + df += 18446744073709551616.0; /* adding 2**64 */ + return (double)df; + } +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_close.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_close.c new file mode 100644 index 000000000..13d2bab5c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_close.c @@ -0,0 +1,140 @@ +/* $Id: tif_close.c,v 1.19 2010-03-10 18:56:48 bfriesen Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + */ +#include "tiffiop.h" +#include + +/************************************************************************/ +/* TIFFCleanup() */ +/************************************************************************/ + +/** + * Auxiliary function to free the TIFF structure. Given structure will be + * completetly freed, so you should save opened file handle and pointer + * to the close procedure in external variables before calling + * _TIFFCleanup(), if you will need these ones to close the file. + * + * @param tif A TIFF pointer. + */ + +void +TIFFCleanup(TIFF* tif) +{ + /* + * Flush buffered data and directory (if dirty). + */ + if (tif->tif_mode != O_RDONLY) + TIFFFlush(tif); + (*tif->tif_cleanup)(tif); + TIFFFreeDirectory(tif); + + if (tif->tif_dirlist) + _TIFFfree(tif->tif_dirlist); + + /* + * Clean up client info links. + */ + while( tif->tif_clientinfo ) + { + TIFFClientInfoLink *link = tif->tif_clientinfo; + + tif->tif_clientinfo = link->next; + _TIFFfree( link->name ); + _TIFFfree( link ); + } + + if (tif->tif_rawdata && (tif->tif_flags&TIFF_MYBUFFER)) + _TIFFfree(tif->tif_rawdata); + if (isMapped(tif)) + TIFFUnmapFileContents(tif, tif->tif_base, (toff_t)tif->tif_size); + + /* + * Clean up custom fields. + */ + if (tif->tif_fields && tif->tif_nfields > 0) { + uint32 i; + + for (i = 0; i < tif->tif_nfields; i++) { + TIFFField *fld = tif->tif_fields[i]; + if (fld->field_bit == FIELD_CUSTOM && + strncmp("Tag ", fld->field_name, 4) == 0) { + _TIFFfree(fld->field_name); + _TIFFfree(fld); + } + } + + _TIFFfree(tif->tif_fields); + } + + if (tif->tif_nfieldscompat > 0) { + uint32 i; + + for (i = 0; i < tif->tif_nfieldscompat; i++) { + if (tif->tif_fieldscompat[i].allocated_size) + _TIFFfree(tif->tif_fieldscompat[i].fields); + } + _TIFFfree(tif->tif_fieldscompat); + } + + _TIFFfree(tif); +} + +/************************************************************************/ +/* TIFFClose() */ +/************************************************************************/ + +/** + * Close a previously opened TIFF file. + * + * TIFFClose closes a file that was previously opened with TIFFOpen(). + * Any buffered data are flushed to the file, including the contents of + * the current directory (if modified); and all resources are reclaimed. + * + * @param tif A TIFF pointer. + */ + +void +TIFFClose(TIFF* tif) +{ + TIFFCloseProc closeproc = tif->tif_closeproc; + thandle_t fd = tif->tif_clientdata; + + TIFFCleanup(tif); + (void) (*closeproc)(fd); +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_codec.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_codec.c new file mode 100644 index 000000000..703e87d56 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_codec.c @@ -0,0 +1,166 @@ +/* $Id: tif_codec.c,v 1.16 2013-05-02 14:44:29 tgl Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library + * + * Builtin Compression Scheme Configuration Support. + */ +#include "tiffiop.h" + +static int NotConfigured(TIFF*, int); + +#ifndef LZW_SUPPORT +#define TIFFInitLZW NotConfigured +#endif +#ifndef PACKBITS_SUPPORT +#define TIFFInitPackBits NotConfigured +#endif +#ifndef THUNDER_SUPPORT +#define TIFFInitThunderScan NotConfigured +#endif +#ifndef NEXT_SUPPORT +#define TIFFInitNeXT NotConfigured +#endif +#ifndef JPEG_SUPPORT +#define TIFFInitJPEG NotConfigured +#endif +#ifndef OJPEG_SUPPORT +#define TIFFInitOJPEG NotConfigured +#endif +#ifndef CCITT_SUPPORT +#define TIFFInitCCITTRLE NotConfigured +#define TIFFInitCCITTRLEW NotConfigured +#define TIFFInitCCITTFax3 NotConfigured +#define TIFFInitCCITTFax4 NotConfigured +#endif +#ifndef JBIG_SUPPORT +#define TIFFInitJBIG NotConfigured +#endif +#ifndef ZIP_SUPPORT +#define TIFFInitZIP NotConfigured +#endif +#ifndef PIXARLOG_SUPPORT +#define TIFFInitPixarLog NotConfigured +#endif +#ifndef LOGLUV_SUPPORT +#define TIFFInitSGILog NotConfigured +#endif +#ifndef LZMA_SUPPORT +#define TIFFInitLZMA NotConfigured +#endif + +/* + * Compression schemes statically built into the library. + */ +#ifdef VMS +const TIFFCodec _TIFFBuiltinCODECS[] = { +#else +TIFFCodec _TIFFBuiltinCODECS[] = { +#endif + { "None", COMPRESSION_NONE, TIFFInitDumpMode }, + { "LZW", COMPRESSION_LZW, TIFFInitLZW }, + { "PackBits", COMPRESSION_PACKBITS, TIFFInitPackBits }, + { "ThunderScan", COMPRESSION_THUNDERSCAN,TIFFInitThunderScan }, + { "NeXT", COMPRESSION_NEXT, TIFFInitNeXT }, + { "JPEG", COMPRESSION_JPEG, TIFFInitJPEG }, + { "Old-style JPEG", COMPRESSION_OJPEG, TIFFInitOJPEG }, + { "CCITT RLE", COMPRESSION_CCITTRLE, TIFFInitCCITTRLE }, + { "CCITT RLE/W", COMPRESSION_CCITTRLEW, TIFFInitCCITTRLEW }, + { "CCITT Group 3", COMPRESSION_CCITTFAX3, TIFFInitCCITTFax3 }, + { "CCITT Group 4", COMPRESSION_CCITTFAX4, TIFFInitCCITTFax4 }, + { "ISO JBIG", COMPRESSION_JBIG, TIFFInitJBIG }, + { "Deflate", COMPRESSION_DEFLATE, TIFFInitZIP }, + { "AdobeDeflate", COMPRESSION_ADOBE_DEFLATE , TIFFInitZIP }, + { "PixarLog", COMPRESSION_PIXARLOG, TIFFInitPixarLog }, + { "SGILog", COMPRESSION_SGILOG, TIFFInitSGILog }, + { "SGILog24", COMPRESSION_SGILOG24, TIFFInitSGILog }, + { "LZMA", COMPRESSION_LZMA, TIFFInitLZMA }, + { NULL, 0, NULL } +}; + +static int +_notConfigured(TIFF* tif) +{ + const TIFFCodec* c = TIFFFindCODEC(tif->tif_dir.td_compression); + char compression_code[20]; + + snprintf(compression_code, sizeof(compression_code), "%d", + tif->tif_dir.td_compression ); + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "%s compression support is not configured", + c ? c->name : compression_code ); + return (0); +} + +static int +NotConfigured(TIFF* tif, int scheme) +{ + (void) scheme; + + tif->tif_fixuptags = _notConfigured; + tif->tif_decodestatus = FALSE; + tif->tif_setupdecode = _notConfigured; + tif->tif_encodestatus = FALSE; + tif->tif_setupencode = _notConfigured; + return (1); +} + +/************************************************************************/ +/* TIFFIsCODECConfigured() */ +/************************************************************************/ + +/** + * Check whether we have working codec for the specific coding scheme. + * + * @return returns 1 if the codec is configured and working. Otherwise + * 0 will be returned. + */ + +int +TIFFIsCODECConfigured(uint16 scheme) +{ + const TIFFCodec* codec = TIFFFindCODEC(scheme); + + if(codec == NULL) { + return 0; + } + if(codec->init == NULL) { + return 0; + } + if(codec->init != NotConfigured){ + return 1; + } + return 0; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_color.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_color.c new file mode 100644 index 000000000..be4850ce6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_color.c @@ -0,0 +1,287 @@ +/* $Id: tif_color.c,v 1.19 2010-12-14 02:22:42 faxguy Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * CIE L*a*b* to CIE XYZ and CIE XYZ to RGB conversion routines are taken + * from the VIPS library (http://www.vips.ecs.soton.ac.uk) with + * the permission of John Cupitt, the VIPS author. + */ + +/* + * TIFF Library. + * + * Color space conversion routines. + */ + +#include "tiffiop.h" +#include + +/* + * Convert color value from the CIE L*a*b* 1976 space to CIE XYZ. + */ +void +TIFFCIELabToXYZ(TIFFCIELabToRGB *cielab, uint32 l, int32 a, int32 b, + float *X, float *Y, float *Z) +{ + float L = (float)l * 100.0F / 255.0F; + float cby, tmp; + + if( L < 8.856F ) { + *Y = (L * cielab->Y0) / 903.292F; + cby = 7.787F * (*Y / cielab->Y0) + 16.0F / 116.0F; + } else { + cby = (L + 16.0F) / 116.0F; + *Y = cielab->Y0 * cby * cby * cby; + } + + tmp = (float)a / 500.0F + cby; + if( tmp < 0.2069F ) + *X = cielab->X0 * (tmp - 0.13793F) / 7.787F; + else + *X = cielab->X0 * tmp * tmp * tmp; + + tmp = cby - (float)b / 200.0F; + if( tmp < 0.2069F ) + *Z = cielab->Z0 * (tmp - 0.13793F) / 7.787F; + else + *Z = cielab->Z0 * tmp * tmp * tmp; +} + +#define RINT(R) ((uint32)((R)>0?((R)+0.5):((R)-0.5))) +/* + * Convert color value from the XYZ space to RGB. + */ +void +TIFFXYZToRGB(TIFFCIELabToRGB *cielab, float X, float Y, float Z, + uint32 *r, uint32 *g, uint32 *b) +{ + int i; + float Yr, Yg, Yb; + float *matrix = &cielab->display.d_mat[0][0]; + + /* Multiply through the matrix to get luminosity values. */ + Yr = matrix[0] * X + matrix[1] * Y + matrix[2] * Z; + Yg = matrix[3] * X + matrix[4] * Y + matrix[5] * Z; + Yb = matrix[6] * X + matrix[7] * Y + matrix[8] * Z; + + /* Clip input */ + Yr = TIFFmax(Yr, cielab->display.d_Y0R); + Yg = TIFFmax(Yg, cielab->display.d_Y0G); + Yb = TIFFmax(Yb, cielab->display.d_Y0B); + + /* Avoid overflow in case of wrong input values */ + Yr = TIFFmin(Yr, cielab->display.d_YCR); + Yg = TIFFmin(Yg, cielab->display.d_YCG); + Yb = TIFFmin(Yb, cielab->display.d_YCB); + + /* Turn luminosity to colour value. */ + i = (int)((Yr - cielab->display.d_Y0R) / cielab->rstep); + i = TIFFmin(cielab->range, i); + *r = RINT(cielab->Yr2r[i]); + + i = (int)((Yg - cielab->display.d_Y0G) / cielab->gstep); + i = TIFFmin(cielab->range, i); + *g = RINT(cielab->Yg2g[i]); + + i = (int)((Yb - cielab->display.d_Y0B) / cielab->bstep); + i = TIFFmin(cielab->range, i); + *b = RINT(cielab->Yb2b[i]); + + /* Clip output. */ + *r = TIFFmin(*r, cielab->display.d_Vrwr); + *g = TIFFmin(*g, cielab->display.d_Vrwg); + *b = TIFFmin(*b, cielab->display.d_Vrwb); +} +#undef RINT + +/* + * Allocate conversion state structures and make look_up tables for + * the Yr,Yb,Yg <=> r,g,b conversions. + */ +int +TIFFCIELabToRGBInit(TIFFCIELabToRGB* cielab, + const TIFFDisplay *display, float *refWhite) +{ + int i; + double gamma; + + cielab->range = CIELABTORGB_TABLE_RANGE; + + _TIFFmemcpy(&cielab->display, display, sizeof(TIFFDisplay)); + + /* Red */ + gamma = 1.0 / cielab->display.d_gammaR ; + cielab->rstep = + (cielab->display.d_YCR - cielab->display.d_Y0R) / cielab->range; + for(i = 0; i <= cielab->range; i++) { + cielab->Yr2r[i] = cielab->display.d_Vrwr + * ((float)pow((double)i / cielab->range, gamma)); + } + + /* Green */ + gamma = 1.0 / cielab->display.d_gammaG ; + cielab->gstep = + (cielab->display.d_YCR - cielab->display.d_Y0R) / cielab->range; + for(i = 0; i <= cielab->range; i++) { + cielab->Yg2g[i] = cielab->display.d_Vrwg + * ((float)pow((double)i / cielab->range, gamma)); + } + + /* Blue */ + gamma = 1.0 / cielab->display.d_gammaB ; + cielab->bstep = + (cielab->display.d_YCR - cielab->display.d_Y0R) / cielab->range; + for(i = 0; i <= cielab->range; i++) { + cielab->Yb2b[i] = cielab->display.d_Vrwb + * ((float)pow((double)i / cielab->range, gamma)); + } + + /* Init reference white point */ + cielab->X0 = refWhite[0]; + cielab->Y0 = refWhite[1]; + cielab->Z0 = refWhite[2]; + + return 0; +} + +/* + * Convert color value from the YCbCr space to CIE XYZ. + * The colorspace conversion algorithm comes from the IJG v5a code; + * see below for more information on how it works. + */ +#define SHIFT 16 +#define FIX(x) ((int32)((x) * (1L<(max)?(max):(f)) +#define HICLAMP(f,max) ((f)>(max)?(max):(f)) + +void +TIFFYCbCrtoRGB(TIFFYCbCrToRGB *ycbcr, uint32 Y, int32 Cb, int32 Cr, + uint32 *r, uint32 *g, uint32 *b) +{ + int32 i; + + /* XXX: Only 8-bit YCbCr input supported for now */ + Y = HICLAMP(Y, 255), Cb = CLAMP(Cb, 0, 255), Cr = CLAMP(Cr, 0, 255); + + i = ycbcr->Y_tab[Y] + ycbcr->Cr_r_tab[Cr]; + *r = CLAMP(i, 0, 255); + i = ycbcr->Y_tab[Y] + + (int)((ycbcr->Cb_g_tab[Cb] + ycbcr->Cr_g_tab[Cr]) >> SHIFT); + *g = CLAMP(i, 0, 255); + i = ycbcr->Y_tab[Y] + ycbcr->Cb_b_tab[Cb]; + *b = CLAMP(i, 0, 255); +} + +/* + * Initialize the YCbCr->RGB conversion tables. The conversion + * is done according to the 6.0 spec: + * + * R = Y + Cr*(2 - 2*LumaRed) + * B = Y + Cb*(2 - 2*LumaBlue) + * G = Y + * - LumaBlue*Cb*(2-2*LumaBlue)/LumaGreen + * - LumaRed*Cr*(2-2*LumaRed)/LumaGreen + * + * To avoid floating point arithmetic the fractional constants that + * come out of the equations are represented as fixed point values + * in the range 0...2^16. We also eliminate multiplications by + * pre-calculating possible values indexed by Cb and Cr (this code + * assumes conversion is being done for 8-bit samples). + */ +int +TIFFYCbCrToRGBInit(TIFFYCbCrToRGB* ycbcr, float *luma, float *refBlackWhite) +{ + TIFFRGBValue* clamptab; + int i; + +#define LumaRed luma[0] +#define LumaGreen luma[1] +#define LumaBlue luma[2] + + clamptab = (TIFFRGBValue*)( + (uint8*) ycbcr+TIFFroundup_32(sizeof (TIFFYCbCrToRGB), sizeof (long))); + _TIFFmemset(clamptab, 0, 256); /* v < 0 => 0 */ + ycbcr->clamptab = (clamptab += 256); + for (i = 0; i < 256; i++) + clamptab[i] = (TIFFRGBValue) i; + _TIFFmemset(clamptab+256, 255, 2*256); /* v > 255 => 255 */ + ycbcr->Cr_r_tab = (int*) (clamptab + 3*256); + ycbcr->Cb_b_tab = ycbcr->Cr_r_tab + 256; + ycbcr->Cr_g_tab = (int32*) (ycbcr->Cb_b_tab + 256); + ycbcr->Cb_g_tab = ycbcr->Cr_g_tab + 256; + ycbcr->Y_tab = ycbcr->Cb_g_tab + 256; + + { float f1 = 2-2*LumaRed; int32 D1 = FIX(f1); + float f2 = LumaRed*f1/LumaGreen; int32 D2 = -FIX(f2); + float f3 = 2-2*LumaBlue; int32 D3 = FIX(f3); + float f4 = LumaBlue*f3/LumaGreen; int32 D4 = -FIX(f4); + int x; + +#undef LumaBlue +#undef LumaGreen +#undef LumaRed + + /* + * i is the actual input pixel value in the range 0..255 + * Cb and Cr values are in the range -128..127 (actually + * they are in a range defined by the ReferenceBlackWhite + * tag) so there is some range shifting to do here when + * constructing tables indexed by the raw pixel data. + */ + for (i = 0, x = -128; i < 256; i++, x++) { + int32 Cr = (int32)Code2V(x, refBlackWhite[4] - 128.0F, + refBlackWhite[5] - 128.0F, 127); + int32 Cb = (int32)Code2V(x, refBlackWhite[2] - 128.0F, + refBlackWhite[3] - 128.0F, 127); + + ycbcr->Cr_r_tab[i] = (int32)((D1*Cr + ONE_HALF)>>SHIFT); + ycbcr->Cb_b_tab[i] = (int32)((D3*Cb + ONE_HALF)>>SHIFT); + ycbcr->Cr_g_tab[i] = D2*Cr; + ycbcr->Cb_g_tab[i] = D4*Cb + ONE_HALF; + ycbcr->Y_tab[i] = + (int32)Code2V(x + 128, refBlackWhite[0], refBlackWhite[1], 255); + } + } + + return 0; +} +#undef HICLAMP +#undef CLAMP +#undef Code2V +#undef SHIFT +#undef ONE_HALF +#undef FIX + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_compress.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_compress.c new file mode 100644 index 000000000..20e72fd07 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_compress.c @@ -0,0 +1,304 @@ +/* $Id: tif_compress.c,v 1.22 2010-03-10 18:56:48 bfriesen Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library + * + * Compression Scheme Configuration Support. + */ +#include "tiffiop.h" + +static int +TIFFNoEncode(TIFF* tif, const char* method) +{ + const TIFFCodec* c = TIFFFindCODEC(tif->tif_dir.td_compression); + + if (c) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "%s %s encoding is not implemented", + c->name, method); + } else { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Compression scheme %u %s encoding is not implemented", + tif->tif_dir.td_compression, method); + } + return (-1); +} + +int +_TIFFNoRowEncode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s) +{ + (void) pp; (void) cc; (void) s; + return (TIFFNoEncode(tif, "scanline")); +} + +int +_TIFFNoStripEncode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s) +{ + (void) pp; (void) cc; (void) s; + return (TIFFNoEncode(tif, "strip")); +} + +int +_TIFFNoTileEncode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s) +{ + (void) pp; (void) cc; (void) s; + return (TIFFNoEncode(tif, "tile")); +} + +static int +TIFFNoDecode(TIFF* tif, const char* method) +{ + const TIFFCodec* c = TIFFFindCODEC(tif->tif_dir.td_compression); + + if (c) + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "%s %s decoding is not implemented", + c->name, method); + else + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Compression scheme %u %s decoding is not implemented", + tif->tif_dir.td_compression, method); + return (-1); +} + +int +_TIFFNoFixupTags(TIFF* tif) +{ + (void) tif; + return (1); +} + +int +_TIFFNoRowDecode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s) +{ + (void) pp; (void) cc; (void) s; + return (TIFFNoDecode(tif, "scanline")); +} + +int +_TIFFNoStripDecode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s) +{ + (void) pp; (void) cc; (void) s; + return (TIFFNoDecode(tif, "strip")); +} + +int +_TIFFNoTileDecode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s) +{ + (void) pp; (void) cc; (void) s; + return (TIFFNoDecode(tif, "tile")); +} + +int +_TIFFNoSeek(TIFF* tif, uint32 off) +{ + (void) off; + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Compression algorithm does not support random access"); + return (0); +} + +int +_TIFFNoPreCode(TIFF* tif, uint16 s) +{ + (void) tif; (void) s; + return (1); +} + +static int _TIFFtrue(TIFF* tif) { (void) tif; return (1); } +static void _TIFFvoid(TIFF* tif) { (void) tif; } + +void +_TIFFSetDefaultCompressionState(TIFF* tif) +{ + tif->tif_fixuptags = _TIFFNoFixupTags; + tif->tif_decodestatus = TRUE; + tif->tif_setupdecode = _TIFFtrue; + tif->tif_predecode = _TIFFNoPreCode; + tif->tif_decoderow = _TIFFNoRowDecode; + tif->tif_decodestrip = _TIFFNoStripDecode; + tif->tif_decodetile = _TIFFNoTileDecode; + tif->tif_encodestatus = TRUE; + tif->tif_setupencode = _TIFFtrue; + tif->tif_preencode = _TIFFNoPreCode; + tif->tif_postencode = _TIFFtrue; + tif->tif_encoderow = _TIFFNoRowEncode; + tif->tif_encodestrip = _TIFFNoStripEncode; + tif->tif_encodetile = _TIFFNoTileEncode; + tif->tif_close = _TIFFvoid; + tif->tif_seek = _TIFFNoSeek; + tif->tif_cleanup = _TIFFvoid; + tif->tif_defstripsize = _TIFFDefaultStripSize; + tif->tif_deftilesize = _TIFFDefaultTileSize; + tif->tif_flags &= ~(TIFF_NOBITREV|TIFF_NOREADRAW); +} + +int +TIFFSetCompressionScheme(TIFF* tif, int scheme) +{ + const TIFFCodec *c = TIFFFindCODEC((uint16) scheme); + + _TIFFSetDefaultCompressionState(tif); + /* + * Don't treat an unknown compression scheme as an error. + * This permits applications to open files with data that + * the library does not have builtin support for, but which + * may still be meaningful. + */ + return (c ? (*c->init)(tif, scheme) : 1); +} + +/* + * Other compression schemes may be registered. Registered + * schemes can also override the builtin versions provided + * by this library. + */ +typedef struct _codec { + struct _codec* next; + TIFFCodec* info; +} codec_t; +static codec_t* registeredCODECS = NULL; + +const TIFFCodec* +TIFFFindCODEC(uint16 scheme) +{ + const TIFFCodec* c; + codec_t* cd; + + for (cd = registeredCODECS; cd; cd = cd->next) + if (cd->info->scheme == scheme) + return ((const TIFFCodec*) cd->info); + for (c = _TIFFBuiltinCODECS; c->name; c++) + if (c->scheme == scheme) + return (c); + return ((const TIFFCodec*) 0); +} + +TIFFCodec* +TIFFRegisterCODEC(uint16 scheme, const char* name, TIFFInitMethod init) +{ + codec_t* cd = (codec_t*) + _TIFFmalloc((tmsize_t)(sizeof (codec_t) + sizeof (TIFFCodec) + strlen(name)+1)); + + if (cd != NULL) { + cd->info = (TIFFCodec*) ((uint8*) cd + sizeof (codec_t)); + cd->info->name = (char*) + ((uint8*) cd->info + sizeof (TIFFCodec)); + strcpy(cd->info->name, name); + cd->info->scheme = scheme; + cd->info->init = init; + cd->next = registeredCODECS; + registeredCODECS = cd; + } else { + TIFFErrorExt(0, "TIFFRegisterCODEC", + "No space to register compression scheme %s", name); + return NULL; + } + return (cd->info); +} + +void +TIFFUnRegisterCODEC(TIFFCodec* c) +{ + codec_t* cd; + codec_t** pcd; + + for (pcd = ®isteredCODECS; (cd = *pcd); pcd = &cd->next) + if (cd->info == c) { + *pcd = cd->next; + _TIFFfree(cd); + return; + } + TIFFErrorExt(0, "TIFFUnRegisterCODEC", + "Cannot remove compression scheme %s; not registered", c->name); +} + +/************************************************************************/ +/* TIFFGetConfisuredCODECs() */ +/************************************************************************/ + +/** + * Get list of configured codecs, both built-in and registered by user. + * Caller is responsible to free this structure. + * + * @return returns array of TIFFCodec records (the last record should be NULL) + * or NULL if function failed. + */ + +TIFFCodec* +TIFFGetConfiguredCODECs() +{ + int i = 1; + codec_t *cd; + const TIFFCodec* c; + TIFFCodec* codecs = NULL; + TIFFCodec* new_codecs; + + for (cd = registeredCODECS; cd; cd = cd->next) { + new_codecs = (TIFFCodec *) + _TIFFrealloc(codecs, i * sizeof(TIFFCodec)); + if (!new_codecs) { + _TIFFfree (codecs); + return NULL; + } + codecs = new_codecs; + _TIFFmemcpy(codecs + i - 1, cd, sizeof(TIFFCodec)); + i++; + } + for (c = _TIFFBuiltinCODECS; c->name; c++) { + if (TIFFIsCODECConfigured(c->scheme)) { + new_codecs = (TIFFCodec *) + _TIFFrealloc(codecs, i * sizeof(TIFFCodec)); + if (!new_codecs) { + _TIFFfree (codecs); + return NULL; + } + codecs = new_codecs; + _TIFFmemcpy(codecs + i - 1, (const void*)c, sizeof(TIFFCodec)); + i++; + } + } + + new_codecs = (TIFFCodec *) _TIFFrealloc(codecs, i * sizeof(TIFFCodec)); + if (!new_codecs) { + _TIFFfree (codecs); + return NULL; + } + codecs = new_codecs; + _TIFFmemset(codecs + i - 1, 0, sizeof(TIFFCodec)); + + return codecs; +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_config.h b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_config.h new file mode 100644 index 000000000..39473994c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_config.h @@ -0,0 +1,91 @@ +/* Get the common system configuration switches from the main file. */ +#include "cpl_port.h" + +/* Libtiff specific switches. */ + +/* Support CCITT Group 3 & 4 algorithms */ +#define CCITT_SUPPORT 1 + +/* Support LogLuv high dynamic range encoding */ +#define LOGLUV_SUPPORT 1 + +/* Support LZW algorithm */ +#define LZW_SUPPORT 1 + +/* Support NeXT 2-bit RLE algorithm */ +#define NEXT_SUPPORT 1 + +/* Support Macintosh PackBits algorithm */ +#define PACKBITS_SUPPORT 1 + +/* Support ThunderScan 4-bit RLE algorithm */ +#define THUNDER_SUPPORT 1 + +/* Pick up YCbCr subsampling info from the JPEG data stream to support files + lacking the tag (default enabled). */ +#define CHECK_JPEG_YCBCR_SUBSAMPLING 1 + +/* Treat extra sample as alpha (default enabled). The RGBA interface will + treat a fourth sample with no EXTRASAMPLE_ value as being ASSOCALPHA. Many + packages produce RGBA files but don't mark the alpha properly. */ +#define DEFAULT_EXTRASAMPLE_AS_ALPHA 1 + +/* Support strip chopping (whether or not to convert single-strip uncompressed + images to mutiple strips of ~8Kb to reduce memory usage) */ +#define STRIPCHOP_DEFAULT TIFF_STRIPCHOP + +#define CHUNKY_STRIP_READ_SUPPORT 1 +#define DEFER_STRILE_LOAD 1 + +/* Default size of the strip in bytes (when strip chopping enabled) */ +#define STRIP_SIZE_DEFAULT 8192 + +/* Enable SubIFD tag (330) support */ +#define SUBIFD_SUPPORT 1 + +/* Signed 16-bit type */ +#define TIFF_INT16_T GInt16 + +/* Signed 32-bit type */ +#define TIFF_INT32_T GInt32 + +/* Signed 64-bit type */ +#define TIFF_INT64_T GIntBig + +/* Signed 8-bit type */ +#define TIFF_INT8_T signed char + +/* Pointer difference type */ +#define TIFF_PTRDIFF_T ptrdiff_t + +/* Signed size type */ +#ifdef _WIN64 +# define TIFF_SSIZE_T GIntBig +# define TIFF_SSIZE_FORMAT CPL_FRMT_GIB +#else +# define TIFF_SSIZE_T signed long +# define TIFF_SSIZE_FORMAT "%ld" +#endif + +/* Unsigned 16-bit type */ +#define TIFF_UINT16_T GUInt16 + +/* Unsigned 32-bit type */ +#define TIFF_UINT32_T GUInt32 + +/* Unsigned 64-bit type */ +#define TIFF_UINT64_T GUIntBig + +/* Unsigned 8-bit type */ +#define TIFF_UINT8_T unsigned char + +#define TIFF_UINT64_FORMAT CPL_FRMT_GUIB +#define TIFF_INT64_FORMAT CPL_FRMT_GIB + +#ifdef JPEG_DUAL_MODE_8_12 +# define LIBJPEG_12_PATH "../../jpeg/libjpeg12/jpeglib.h" +#endif + +#ifdef RENAME_INTERNAL_LIBTIFF_SYMBOLS +#include "gdal_libtiff_symbol_rename.h" +#endif diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_config.h.wince b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_config.h.wince new file mode 100644 index 000000000..baf229895 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_config.h.wince @@ -0,0 +1,101 @@ +/* $Id: tif_config.h.wince 19417 2010-04-15 16:28:40Z mloskot $ */ + +/* + * Windows CE platform config. + */ +#ifndef _WIN32_WCE +# error This version of tif_config.h header is dedicated for Windows CE platform! +#endif + +/* Get the common system configuration switches from the main file. */ +#include "cpl_port.h" + +/* Compatibility stuff. */ + +/* Define as 0 or 1 according to the floating point format suported by the + machine */ +#define HAVE_IEEEFP 1 + +/* Set the native cpu bit order (FILLORDER_LSB2MSB or FILLORDER_MSB2LSB) */ +#define HOST_FILLORDER FILLORDER_LSB2MSB + +/* Native cpu byte order: 1 if big-endian (Motorola) or 0 if little-endian + (Intel) */ +#define HOST_BIGENDIAN 0 + +/* Libtiff specific switches. */ + +/* Support CCITT Group 3 & 4 algorithms */ +#undef CCITT_SUPPORT + +/* Support LogLuv high dynamic range encoding */ +#define LOGLUV_SUPPORT 1 + +/* Support LZW algorithm */ +#define LZW_SUPPORT 1 + +/* Support NeXT 2-bit RLE algorithm */ +#define NEXT_SUPPORT 1 + +/* Support Macintosh PackBits algorithm */ +#define PACKBITS_SUPPORT 1 + +/* Support ThunderScan 4-bit RLE algorithm */ +#define THUNDER_SUPPORT 1 + +/* Pick up YCbCr subsampling info from the JPEG data stream to support files + lacking the tag (default enabled). */ +//#define CHECK_JPEG_YCBCR_SUBSAMPLING 1 + +/* Treat extra sample as alpha (default enabled). The RGBA interface will + treat a fourth sample with no EXTRASAMPLE_ value as being ASSOCALPHA. Many + packages produce RGBA files but don't mark the alpha properly. */ +#define DEFAULT_EXTRASAMPLE_AS_ALPHA 1 + +/* Support strip chopping (whether or not to convert single-strip uncompressed + images to mutiple strips of ~8Kb to reduce memory usage) */ +#define STRIPCHOP_DEFAULT TIFF_STRIPCHOP + +/* Default size of the strip in bytes (when strip chopping enabled) */ +#define STRIP_SIZE_DEFAULT 8192 + +/* Enable SubIFD tag (330) support */ +#define SUBIFD_SUPPORT 1 + +/* Signed 16-bit type */ +#define TIFF_INT16_T short + +/* Signed 32-bit type */ +#define TIFF_INT32_T int + +/* Signed 64-bit type */ +#define TIFF_INT64_T long + +/* Signed 8-bit type */ +#define TIFF_INT8_T signed char + +/* Pointer difference type */ +#define TIFF_PTRDIFF_T ptrdiff_t + +/* Signed size type */ +#ifdef _WIN64 +# define TIFF_SSIZE_T long +# define TIFF_SSIZE_FORMAT CPL_FRMT_GIB +#else +# define TIFF_SSIZE_T signed long +# define TIFF_SSIZE_FORMAT "%ld" +#endif + +/* Unsigned 16-bit type */ +#define TIFF_UINT16_T unsigned short + +/* Unsigned 32-bit type */ +#define TIFF_UINT32_T unsigned int + +/* Unsigned 64-bit type */ +#define TIFF_UINT64_T unsigned long + +/* Unsigned 8-bit type */ +#define TIFF_UINT8_T unsigned char + +#define TIFF_UINT64_FORMAT CPL_FRMT_GUIB \ No newline at end of file diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dir.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dir.c new file mode 100644 index 000000000..17bf87828 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dir.c @@ -0,0 +1,1700 @@ +/* $Id: tif_dir.c,v 1.119 2014-12-27 15:20:42 erouault Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Directory Tag Get & Set Routines. + * (and also some miscellaneous stuff) + */ +#include "tiffiop.h" + +/* + * These are used in the backwards compatibility code... + */ +#define DATATYPE_VOID 0 /* !untyped data */ +#define DATATYPE_INT 1 /* !signed integer data */ +#define DATATYPE_UINT 2 /* !unsigned integer data */ +#define DATATYPE_IEEEFP 3 /* !IEEE floating point data */ + +static void +setByteArray(void** vpp, void* vp, size_t nmemb, size_t elem_size) +{ + if (*vpp) + _TIFFfree(*vpp), *vpp = 0; + if (vp) { + tmsize_t bytes = (tmsize_t)(nmemb * elem_size); + if (elem_size && bytes / elem_size == nmemb) + *vpp = (void*) _TIFFmalloc(bytes); + if (*vpp) + _TIFFmemcpy(*vpp, vp, bytes); + } +} +void _TIFFsetByteArray(void** vpp, void* vp, uint32 n) + { setByteArray(vpp, vp, n, 1); } +void _TIFFsetString(char** cpp, char* cp) + { setByteArray((void**) cpp, (void*) cp, strlen(cp)+1, 1); } +void _TIFFsetNString(char** cpp, char* cp, uint32 n) + { setByteArray((void**) cpp, (void*) cp, n, 1); } +void _TIFFsetShortArray(uint16** wpp, uint16* wp, uint32 n) + { setByteArray((void**) wpp, (void*) wp, n, sizeof (uint16)); } +void _TIFFsetLongArray(uint32** lpp, uint32* lp, uint32 n) + { setByteArray((void**) lpp, (void*) lp, n, sizeof (uint32)); } +void _TIFFsetLong8Array(uint64** lpp, uint64* lp, uint32 n) + { setByteArray((void**) lpp, (void*) lp, n, sizeof (uint64)); } +void _TIFFsetFloatArray(float** fpp, float* fp, uint32 n) + { setByteArray((void**) fpp, (void*) fp, n, sizeof (float)); } +void _TIFFsetDoubleArray(double** dpp, double* dp, uint32 n) + { setByteArray((void**) dpp, (void*) dp, n, sizeof (double)); } + +static void +setDoubleArrayOneValue(double** vpp, double value, size_t nmemb) +{ + if (*vpp) + _TIFFfree(*vpp); + *vpp = _TIFFmalloc(nmemb*sizeof(double)); + if (*vpp) + { + while (nmemb--) + ((double*)*vpp)[nmemb] = value; + } +} + +/* + * Install extra samples information. + */ +static int +setExtraSamples(TIFFDirectory* td, va_list ap, uint32* v) +{ +/* XXX: Unassociated alpha data == 999 is a known Corel Draw bug, see below */ +#define EXTRASAMPLE_COREL_UNASSALPHA 999 + + uint16* va; + uint32 i; + + *v = (uint16) va_arg(ap, uint16_vap); + if ((uint16) *v > td->td_samplesperpixel) + return 0; + va = va_arg(ap, uint16*); + if (*v > 0 && va == NULL) /* typically missing param */ + return 0; + for (i = 0; i < *v; i++) { + if (va[i] > EXTRASAMPLE_UNASSALPHA) { + /* + * XXX: Corel Draw is known to produce incorrect + * ExtraSamples tags which must be patched here if we + * want to be able to open some of the damaged TIFF + * files: + */ + if (va[i] == EXTRASAMPLE_COREL_UNASSALPHA) + va[i] = EXTRASAMPLE_UNASSALPHA; + else + return 0; + } + } + td->td_extrasamples = (uint16) *v; + _TIFFsetShortArray(&td->td_sampleinfo, va, td->td_extrasamples); + return 1; + +#undef EXTRASAMPLE_COREL_UNASSALPHA +} + +/* + * Confirm we have "samplesperpixel" ink names separated by \0. Returns + * zero if the ink names are not as expected. + */ +static uint32 +checkInkNamesString(TIFF* tif, uint32 slen, const char* s) +{ + TIFFDirectory* td = &tif->tif_dir; + uint16 i = td->td_samplesperpixel; + + if (slen > 0) { + const char* ep = s+slen; + const char* cp = s; + for (; i > 0; i--) { + for (; cp < ep && *cp != '\0'; cp++) {} + if (cp >= ep) + goto bad; + cp++; /* skip \0 */ + } + return ((uint32)(cp-s)); + } +bad: + TIFFErrorExt(tif->tif_clientdata, "TIFFSetField", + "%s: Invalid InkNames value; expecting %d names, found %d", + tif->tif_name, + td->td_samplesperpixel, + td->td_samplesperpixel-i); + return (0); +} + +static int +_TIFFVSetField(TIFF* tif, uint32 tag, va_list ap) +{ + static const char module[] = "_TIFFVSetField"; + + TIFFDirectory* td = &tif->tif_dir; + int status = 1; + uint32 v32, i, v; + double dblval; + char* s; + const TIFFField *fip = TIFFFindField(tif, tag, TIFF_ANY); + uint32 standard_tag = tag; + if( fip == NULL ) /* cannot happen since OkToChangeTag() already checks it */ + return 0; + /* + * We want to force the custom code to be used for custom + * fields even if the tag happens to match a well known + * one - important for reinterpreted handling of standard + * tag values in custom directories (ie. EXIF) + */ + if (fip->field_bit == FIELD_CUSTOM) { + standard_tag = 0; + } + + switch (standard_tag) { + case TIFFTAG_SUBFILETYPE: + td->td_subfiletype = (uint32) va_arg(ap, uint32); + break; + case TIFFTAG_IMAGEWIDTH: + td->td_imagewidth = (uint32) va_arg(ap, uint32); + break; + case TIFFTAG_IMAGELENGTH: + td->td_imagelength = (uint32) va_arg(ap, uint32); + break; + case TIFFTAG_BITSPERSAMPLE: + td->td_bitspersample = (uint16) va_arg(ap, uint16_vap); + /* + * If the data require post-decoding processing to byte-swap + * samples, set it up here. Note that since tags are required + * to be ordered, compression code can override this behaviour + * in the setup method if it wants to roll the post decoding + * work in with its normal work. + */ + if (tif->tif_flags & TIFF_SWAB) { + if (td->td_bitspersample == 8) + tif->tif_postdecode = _TIFFNoPostDecode; + else if (td->td_bitspersample == 16) + tif->tif_postdecode = _TIFFSwab16BitData; + else if (td->td_bitspersample == 24) + tif->tif_postdecode = _TIFFSwab24BitData; + else if (td->td_bitspersample == 32) + tif->tif_postdecode = _TIFFSwab32BitData; + else if (td->td_bitspersample == 64) + tif->tif_postdecode = _TIFFSwab64BitData; + else if (td->td_bitspersample == 128) /* two 64's */ + tif->tif_postdecode = _TIFFSwab64BitData; + } + break; + case TIFFTAG_COMPRESSION: + v = (uint16) va_arg(ap, uint16_vap); + /* + * If we're changing the compression scheme, the notify the + * previous module so that it can cleanup any state it's + * setup. + */ + if (TIFFFieldSet(tif, FIELD_COMPRESSION)) { + if ((uint32)td->td_compression == v) + break; + (*tif->tif_cleanup)(tif); + tif->tif_flags &= ~TIFF_CODERSETUP; + } + /* + * Setup new compression routine state. + */ + if( (status = TIFFSetCompressionScheme(tif, v)) != 0 ) + td->td_compression = (uint16) v; + else + status = 0; + break; + case TIFFTAG_PHOTOMETRIC: + td->td_photometric = (uint16) va_arg(ap, uint16_vap); + break; + case TIFFTAG_THRESHHOLDING: + td->td_threshholding = (uint16) va_arg(ap, uint16_vap); + break; + case TIFFTAG_FILLORDER: + v = (uint16) va_arg(ap, uint16_vap); + if (v != FILLORDER_LSB2MSB && v != FILLORDER_MSB2LSB) + goto badvalue; + td->td_fillorder = (uint16) v; + break; + case TIFFTAG_ORIENTATION: + v = (uint16) va_arg(ap, uint16_vap); + if (v < ORIENTATION_TOPLEFT || ORIENTATION_LEFTBOT < v) + goto badvalue; + else + td->td_orientation = (uint16) v; + break; + case TIFFTAG_SAMPLESPERPIXEL: + v = (uint16) va_arg(ap, uint16_vap); + if (v == 0) + goto badvalue; + td->td_samplesperpixel = (uint16) v; + break; + case TIFFTAG_ROWSPERSTRIP: + v32 = (uint32) va_arg(ap, uint32); + if (v32 == 0) + goto badvalue32; + td->td_rowsperstrip = v32; + if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) { + td->td_tilelength = v32; + td->td_tilewidth = td->td_imagewidth; + } + break; + case TIFFTAG_MINSAMPLEVALUE: + td->td_minsamplevalue = (uint16) va_arg(ap, uint16_vap); + break; + case TIFFTAG_MAXSAMPLEVALUE: + td->td_maxsamplevalue = (uint16) va_arg(ap, uint16_vap); + break; + case TIFFTAG_SMINSAMPLEVALUE: + if (tif->tif_flags & TIFF_PERSAMPLE) + _TIFFsetDoubleArray(&td->td_sminsamplevalue, va_arg(ap, double*), td->td_samplesperpixel); + else + setDoubleArrayOneValue(&td->td_sminsamplevalue, va_arg(ap, double), td->td_samplesperpixel); + break; + case TIFFTAG_SMAXSAMPLEVALUE: + if (tif->tif_flags & TIFF_PERSAMPLE) + _TIFFsetDoubleArray(&td->td_smaxsamplevalue, va_arg(ap, double*), td->td_samplesperpixel); + else + setDoubleArrayOneValue(&td->td_smaxsamplevalue, va_arg(ap, double), td->td_samplesperpixel); + break; + case TIFFTAG_XRESOLUTION: + dblval = va_arg(ap, double); + if( dblval < 0 ) + goto badvaluedouble; + td->td_xresolution = (float) dblval; + break; + case TIFFTAG_YRESOLUTION: + dblval = va_arg(ap, double); + if( dblval < 0 ) + goto badvaluedouble; + td->td_yresolution = (float) dblval; + break; + case TIFFTAG_PLANARCONFIG: + v = (uint16) va_arg(ap, uint16_vap); + if (v != PLANARCONFIG_CONTIG && v != PLANARCONFIG_SEPARATE) + goto badvalue; + td->td_planarconfig = (uint16) v; + break; + case TIFFTAG_XPOSITION: + td->td_xposition = (float) va_arg(ap, double); + break; + case TIFFTAG_YPOSITION: + td->td_yposition = (float) va_arg(ap, double); + break; + case TIFFTAG_RESOLUTIONUNIT: + v = (uint16) va_arg(ap, uint16_vap); + if (v < RESUNIT_NONE || RESUNIT_CENTIMETER < v) + goto badvalue; + td->td_resolutionunit = (uint16) v; + break; + case TIFFTAG_PAGENUMBER: + td->td_pagenumber[0] = (uint16) va_arg(ap, uint16_vap); + td->td_pagenumber[1] = (uint16) va_arg(ap, uint16_vap); + break; + case TIFFTAG_HALFTONEHINTS: + td->td_halftonehints[0] = (uint16) va_arg(ap, uint16_vap); + td->td_halftonehints[1] = (uint16) va_arg(ap, uint16_vap); + break; + case TIFFTAG_COLORMAP: + v32 = (uint32)(1L<td_bitspersample); + _TIFFsetShortArray(&td->td_colormap[0], va_arg(ap, uint16*), v32); + _TIFFsetShortArray(&td->td_colormap[1], va_arg(ap, uint16*), v32); + _TIFFsetShortArray(&td->td_colormap[2], va_arg(ap, uint16*), v32); + break; + case TIFFTAG_EXTRASAMPLES: + if (!setExtraSamples(td, ap, &v)) + goto badvalue; + break; + case TIFFTAG_MATTEING: + td->td_extrasamples = (((uint16) va_arg(ap, uint16_vap)) != 0); + if (td->td_extrasamples) { + uint16 sv = EXTRASAMPLE_ASSOCALPHA; + _TIFFsetShortArray(&td->td_sampleinfo, &sv, 1); + } + break; + case TIFFTAG_TILEWIDTH: + v32 = (uint32) va_arg(ap, uint32); + if (v32 % 16) { + if (tif->tif_mode != O_RDONLY) + goto badvalue32; + TIFFWarningExt(tif->tif_clientdata, tif->tif_name, + "Nonstandard tile width %d, convert file", v32); + } + td->td_tilewidth = v32; + tif->tif_flags |= TIFF_ISTILED; + break; + case TIFFTAG_TILELENGTH: + v32 = (uint32) va_arg(ap, uint32); + if (v32 % 16) { + if (tif->tif_mode != O_RDONLY) + goto badvalue32; + TIFFWarningExt(tif->tif_clientdata, tif->tif_name, + "Nonstandard tile length %d, convert file", v32); + } + td->td_tilelength = v32; + tif->tif_flags |= TIFF_ISTILED; + break; + case TIFFTAG_TILEDEPTH: + v32 = (uint32) va_arg(ap, uint32); + if (v32 == 0) + goto badvalue32; + td->td_tiledepth = v32; + break; + case TIFFTAG_DATATYPE: + v = (uint16) va_arg(ap, uint16_vap); + switch (v) { + case DATATYPE_VOID: v = SAMPLEFORMAT_VOID; break; + case DATATYPE_INT: v = SAMPLEFORMAT_INT; break; + case DATATYPE_UINT: v = SAMPLEFORMAT_UINT; break; + case DATATYPE_IEEEFP: v = SAMPLEFORMAT_IEEEFP;break; + default: goto badvalue; + } + td->td_sampleformat = (uint16) v; + break; + case TIFFTAG_SAMPLEFORMAT: + v = (uint16) va_arg(ap, uint16_vap); + if (v < SAMPLEFORMAT_UINT || SAMPLEFORMAT_COMPLEXIEEEFP < v) + goto badvalue; + td->td_sampleformat = (uint16) v; + + /* Try to fix up the SWAB function for complex data. */ + if( td->td_sampleformat == SAMPLEFORMAT_COMPLEXINT + && td->td_bitspersample == 32 + && tif->tif_postdecode == _TIFFSwab32BitData ) + tif->tif_postdecode = _TIFFSwab16BitData; + else if( (td->td_sampleformat == SAMPLEFORMAT_COMPLEXINT + || td->td_sampleformat == SAMPLEFORMAT_COMPLEXIEEEFP) + && td->td_bitspersample == 64 + && tif->tif_postdecode == _TIFFSwab64BitData ) + tif->tif_postdecode = _TIFFSwab32BitData; + break; + case TIFFTAG_IMAGEDEPTH: + td->td_imagedepth = (uint32) va_arg(ap, uint32); + break; + case TIFFTAG_SUBIFD: + if ((tif->tif_flags & TIFF_INSUBIFD) == 0) { + td->td_nsubifd = (uint16) va_arg(ap, uint16_vap); + _TIFFsetLong8Array(&td->td_subifd, (uint64*) va_arg(ap, uint64*), + (long) td->td_nsubifd); + } else { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Sorry, cannot nest SubIFDs", + tif->tif_name); + status = 0; + } + break; + case TIFFTAG_YCBCRPOSITIONING: + td->td_ycbcrpositioning = (uint16) va_arg(ap, uint16_vap); + break; + case TIFFTAG_YCBCRSUBSAMPLING: + td->td_ycbcrsubsampling[0] = (uint16) va_arg(ap, uint16_vap); + td->td_ycbcrsubsampling[1] = (uint16) va_arg(ap, uint16_vap); + break; + case TIFFTAG_TRANSFERFUNCTION: + v = (td->td_samplesperpixel - td->td_extrasamples) > 1 ? 3 : 1; + for (i = 0; i < v; i++) + _TIFFsetShortArray(&td->td_transferfunction[i], + va_arg(ap, uint16*), 1L<td_bitspersample); + break; + case TIFFTAG_REFERENCEBLACKWHITE: + /* XXX should check for null range */ + _TIFFsetFloatArray(&td->td_refblackwhite, va_arg(ap, float*), 6); + break; + case TIFFTAG_INKNAMES: + v = (uint16) va_arg(ap, uint16_vap); + s = va_arg(ap, char*); + v = checkInkNamesString(tif, v, s); + status = v > 0; + if( v > 0 ) { + _TIFFsetNString(&td->td_inknames, s, v); + td->td_inknameslen = v; + } + break; + case TIFFTAG_PERSAMPLE: + v = (uint16) va_arg(ap, uint16_vap); + if( v == PERSAMPLE_MULTI ) + tif->tif_flags |= TIFF_PERSAMPLE; + else + tif->tif_flags &= ~TIFF_PERSAMPLE; + break; + default: { + TIFFTagValue *tv; + int tv_size, iCustom; + + /* + * This can happen if multiple images are open with different + * codecs which have private tags. The global tag information + * table may then have tags that are valid for one file but not + * the other. If the client tries to set a tag that is not valid + * for the image's codec then we'll arrive here. This + * happens, for example, when tiffcp is used to convert between + * compression schemes and codec-specific tags are blindly copied. + */ + if(fip->field_bit != FIELD_CUSTOM) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Invalid %stag \"%s\" (not supported by codec)", + tif->tif_name, isPseudoTag(tag) ? "pseudo-" : "", + fip->field_name); + status = 0; + break; + } + + /* + * Find the existing entry for this custom value. + */ + tv = NULL; + for (iCustom = 0; iCustom < td->td_customValueCount; iCustom++) { + if (td->td_customValues[iCustom].info->field_tag == tag) { + tv = td->td_customValues + iCustom; + if (tv->value != NULL) { + _TIFFfree(tv->value); + tv->value = NULL; + } + break; + } + } + + /* + * Grow the custom list if the entry was not found. + */ + if(tv == NULL) { + TIFFTagValue *new_customValues; + + td->td_customValueCount++; + new_customValues = (TIFFTagValue *) + _TIFFrealloc(td->td_customValues, + sizeof(TIFFTagValue) * td->td_customValueCount); + if (!new_customValues) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Failed to allocate space for list of custom values", + tif->tif_name); + status = 0; + goto end; + } + + td->td_customValues = new_customValues; + + tv = td->td_customValues + (td->td_customValueCount - 1); + tv->info = fip; + tv->value = NULL; + tv->count = 0; + } + + /* + * Set custom value ... save a copy of the custom tag value. + */ + tv_size = _TIFFDataSize(fip->field_type); + if (tv_size == 0) { + status = 0; + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Bad field type %d for \"%s\"", + tif->tif_name, fip->field_type, + fip->field_name); + goto end; + } + + if (fip->field_type == TIFF_ASCII) + { + uint32 ma; + char* mb; + if (fip->field_passcount) + { + assert(fip->field_writecount==TIFF_VARIABLE2); + ma=(uint32)va_arg(ap,uint32); + mb=(char*)va_arg(ap,char*); + } + else + { + mb=(char*)va_arg(ap,char*); + ma=(uint32)(strlen(mb)+1); + } + tv->count=ma; + setByteArray(&tv->value,mb,ma,1); + } + else + { + if (fip->field_passcount) { + if (fip->field_writecount == TIFF_VARIABLE2) + tv->count = (uint32) va_arg(ap, uint32); + else + tv->count = (int) va_arg(ap, int); + } else if (fip->field_writecount == TIFF_VARIABLE + || fip->field_writecount == TIFF_VARIABLE2) + tv->count = 1; + else if (fip->field_writecount == TIFF_SPP) + tv->count = td->td_samplesperpixel; + else + tv->count = fip->field_writecount; + + if (tv->count == 0) { + status = 0; + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Null count for \"%s\" (type " + "%d, writecount %d, passcount %d)", + tif->tif_name, + fip->field_name, + fip->field_type, + fip->field_writecount, + fip->field_passcount); + goto end; + } + + tv->value = _TIFFCheckMalloc(tif, tv->count, tv_size, + "custom tag binary object"); + if (!tv->value) { + status = 0; + goto end; + } + + if (fip->field_tag == TIFFTAG_DOTRANGE + && strcmp(fip->field_name,"DotRange") == 0) { + /* TODO: This is an evil exception and should not have been + handled this way ... likely best if we move it into + the directory structure with an explicit field in + libtiff 4.1 and assign it a FIELD_ value */ + uint16 v[2]; + v[0] = (uint16)va_arg(ap, int); + v[1] = (uint16)va_arg(ap, int); + _TIFFmemcpy(tv->value, &v, 4); + } + + else if (fip->field_passcount + || fip->field_writecount == TIFF_VARIABLE + || fip->field_writecount == TIFF_VARIABLE2 + || fip->field_writecount == TIFF_SPP + || tv->count > 1) { + _TIFFmemcpy(tv->value, va_arg(ap, void *), + tv->count * tv_size); + } else { + char *val = (char *)tv->value; + assert( tv->count == 1 ); + + switch (fip->field_type) { + case TIFF_BYTE: + case TIFF_UNDEFINED: + { + uint8 v = (uint8)va_arg(ap, int); + _TIFFmemcpy(val, &v, tv_size); + } + break; + case TIFF_SBYTE: + { + int8 v = (int8)va_arg(ap, int); + _TIFFmemcpy(val, &v, tv_size); + } + break; + case TIFF_SHORT: + { + uint16 v = (uint16)va_arg(ap, int); + _TIFFmemcpy(val, &v, tv_size); + } + break; + case TIFF_SSHORT: + { + int16 v = (int16)va_arg(ap, int); + _TIFFmemcpy(val, &v, tv_size); + } + break; + case TIFF_LONG: + case TIFF_IFD: + { + uint32 v = va_arg(ap, uint32); + _TIFFmemcpy(val, &v, tv_size); + } + break; + case TIFF_SLONG: + { + int32 v = va_arg(ap, int32); + _TIFFmemcpy(val, &v, tv_size); + } + break; + case TIFF_LONG8: + case TIFF_IFD8: + { + uint64 v = va_arg(ap, uint64); + _TIFFmemcpy(val, &v, tv_size); + } + break; + case TIFF_SLONG8: + { + int64 v = va_arg(ap, int64); + _TIFFmemcpy(val, &v, tv_size); + } + break; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_FLOAT: + { + float v = (float)va_arg(ap, double); + _TIFFmemcpy(val, &v, tv_size); + } + break; + case TIFF_DOUBLE: + { + double v = va_arg(ap, double); + _TIFFmemcpy(val, &v, tv_size); + } + break; + default: + _TIFFmemset(val, 0, tv_size); + status = 0; + break; + } + } + } + } + } + if (status) { + const TIFFField* fip=TIFFFieldWithTag(tif,tag); + if (fip) + TIFFSetFieldBit(tif, fip->field_bit); + tif->tif_flags |= TIFF_DIRTYDIRECT; + } + +end: + va_end(ap); + return (status); +badvalue: + { + const TIFFField* fip=TIFFFieldWithTag(tif,tag); + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Bad value %u for \"%s\" tag", + tif->tif_name, v, + fip ? fip->field_name : "Unknown"); + va_end(ap); + } + return (0); +badvalue32: + { + const TIFFField* fip=TIFFFieldWithTag(tif,tag); + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Bad value %u for \"%s\" tag", + tif->tif_name, v32, + fip ? fip->field_name : "Unknown"); + va_end(ap); + } + return (0); +badvaluedouble: + { + const TIFFField* fip=TIFFFieldWithTag(tif,tag); + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Bad value %f for \"%s\" tag", + tif->tif_name, dblval, + fip ? fip->field_name : "Unknown"); + va_end(ap); + } + return (0); +} + +/* + * Return 1/0 according to whether or not + * it is permissible to set the tag's value. + * Note that we allow ImageLength to be changed + * so that we can append and extend to images. + * Any other tag may not be altered once writing + * has commenced, unless its value has no effect + * on the format of the data that is written. + */ +static int +OkToChangeTag(TIFF* tif, uint32 tag) +{ + const TIFFField* fip = TIFFFindField(tif, tag, TIFF_ANY); + if (!fip) { /* unknown tag */ + TIFFErrorExt(tif->tif_clientdata, "TIFFSetField", "%s: Unknown %stag %u", + tif->tif_name, isPseudoTag(tag) ? "pseudo-" : "", tag); + return (0); + } + if (tag != TIFFTAG_IMAGELENGTH && (tif->tif_flags & TIFF_BEENWRITING) && + !fip->field_oktochange) { + /* + * Consult info table to see if tag can be changed + * after we've started writing. We only allow changes + * to those tags that don't/shouldn't affect the + * compression and/or format of the data. + */ + TIFFErrorExt(tif->tif_clientdata, "TIFFSetField", + "%s: Cannot modify tag \"%s\" while writing", + tif->tif_name, fip->field_name); + return (0); + } + return (1); +} + +/* + * Record the value of a field in the + * internal directory structure. The + * field will be written to the file + * when/if the directory structure is + * updated. + */ +int +TIFFSetField(TIFF* tif, uint32 tag, ...) +{ + va_list ap; + int status; + + va_start(ap, tag); + status = TIFFVSetField(tif, tag, ap); + va_end(ap); + return (status); +} + +/* + * Clear the contents of the field in the internal structure. + */ +int +TIFFUnsetField(TIFF* tif, uint32 tag) +{ + const TIFFField *fip = TIFFFieldWithTag(tif, tag); + TIFFDirectory* td = &tif->tif_dir; + + if( !fip ) + return 0; + + if( fip->field_bit != FIELD_CUSTOM ) + TIFFClrFieldBit(tif, fip->field_bit); + else + { + TIFFTagValue *tv = NULL; + int i; + + for (i = 0; i < td->td_customValueCount; i++) { + + tv = td->td_customValues + i; + if( tv->info->field_tag == tag ) + break; + } + + if( i < td->td_customValueCount ) + { + _TIFFfree(tv->value); + for( ; i < td->td_customValueCount-1; i++) { + td->td_customValues[i] = td->td_customValues[i+1]; + } + td->td_customValueCount--; + } + } + + tif->tif_flags |= TIFF_DIRTYDIRECT; + + return (1); +} + +/* + * Like TIFFSetField, but taking a varargs + * parameter list. This routine is useful + * for building higher-level interfaces on + * top of the library. + */ +int +TIFFVSetField(TIFF* tif, uint32 tag, va_list ap) +{ + return OkToChangeTag(tif, tag) ? + (*tif->tif_tagmethods.vsetfield)(tif, tag, ap) : 0; +} + +static int +_TIFFVGetField(TIFF* tif, uint32 tag, va_list ap) +{ + TIFFDirectory* td = &tif->tif_dir; + int ret_val = 1; + uint32 standard_tag = tag; + const TIFFField* fip = TIFFFindField(tif, tag, TIFF_ANY); + if( fip == NULL ) /* cannot happen since TIFFGetField() already checks it */ + return 0; + + /* + * We want to force the custom code to be used for custom + * fields even if the tag happens to match a well known + * one - important for reinterpreted handling of standard + * tag values in custom directories (ie. EXIF) + */ + if (fip->field_bit == FIELD_CUSTOM) { + standard_tag = 0; + } + + switch (standard_tag) { + case TIFFTAG_SUBFILETYPE: + *va_arg(ap, uint32*) = td->td_subfiletype; + break; + case TIFFTAG_IMAGEWIDTH: + *va_arg(ap, uint32*) = td->td_imagewidth; + break; + case TIFFTAG_IMAGELENGTH: + *va_arg(ap, uint32*) = td->td_imagelength; + break; + case TIFFTAG_BITSPERSAMPLE: + *va_arg(ap, uint16*) = td->td_bitspersample; + break; + case TIFFTAG_COMPRESSION: + *va_arg(ap, uint16*) = td->td_compression; + break; + case TIFFTAG_PHOTOMETRIC: + *va_arg(ap, uint16*) = td->td_photometric; + break; + case TIFFTAG_THRESHHOLDING: + *va_arg(ap, uint16*) = td->td_threshholding; + break; + case TIFFTAG_FILLORDER: + *va_arg(ap, uint16*) = td->td_fillorder; + break; + case TIFFTAG_ORIENTATION: + *va_arg(ap, uint16*) = td->td_orientation; + break; + case TIFFTAG_SAMPLESPERPIXEL: + *va_arg(ap, uint16*) = td->td_samplesperpixel; + break; + case TIFFTAG_ROWSPERSTRIP: + *va_arg(ap, uint32*) = td->td_rowsperstrip; + break; + case TIFFTAG_MINSAMPLEVALUE: + *va_arg(ap, uint16*) = td->td_minsamplevalue; + break; + case TIFFTAG_MAXSAMPLEVALUE: + *va_arg(ap, uint16*) = td->td_maxsamplevalue; + break; + case TIFFTAG_SMINSAMPLEVALUE: + if (tif->tif_flags & TIFF_PERSAMPLE) + *va_arg(ap, double**) = td->td_sminsamplevalue; + else + { + /* libtiff historially treats this as a single value. */ + uint16 i; + double v = td->td_sminsamplevalue[0]; + for (i=1; i < td->td_samplesperpixel; ++i) + if( td->td_sminsamplevalue[i] < v ) + v = td->td_sminsamplevalue[i]; + *va_arg(ap, double*) = v; + } + break; + case TIFFTAG_SMAXSAMPLEVALUE: + if (tif->tif_flags & TIFF_PERSAMPLE) + *va_arg(ap, double**) = td->td_smaxsamplevalue; + else + { + /* libtiff historially treats this as a single value. */ + uint16 i; + double v = td->td_smaxsamplevalue[0]; + for (i=1; i < td->td_samplesperpixel; ++i) + if( td->td_smaxsamplevalue[i] > v ) + v = td->td_smaxsamplevalue[i]; + *va_arg(ap, double*) = v; + } + break; + case TIFFTAG_XRESOLUTION: + *va_arg(ap, float*) = td->td_xresolution; + break; + case TIFFTAG_YRESOLUTION: + *va_arg(ap, float*) = td->td_yresolution; + break; + case TIFFTAG_PLANARCONFIG: + *va_arg(ap, uint16*) = td->td_planarconfig; + break; + case TIFFTAG_XPOSITION: + *va_arg(ap, float*) = td->td_xposition; + break; + case TIFFTAG_YPOSITION: + *va_arg(ap, float*) = td->td_yposition; + break; + case TIFFTAG_RESOLUTIONUNIT: + *va_arg(ap, uint16*) = td->td_resolutionunit; + break; + case TIFFTAG_PAGENUMBER: + *va_arg(ap, uint16*) = td->td_pagenumber[0]; + *va_arg(ap, uint16*) = td->td_pagenumber[1]; + break; + case TIFFTAG_HALFTONEHINTS: + *va_arg(ap, uint16*) = td->td_halftonehints[0]; + *va_arg(ap, uint16*) = td->td_halftonehints[1]; + break; + case TIFFTAG_COLORMAP: + *va_arg(ap, uint16**) = td->td_colormap[0]; + *va_arg(ap, uint16**) = td->td_colormap[1]; + *va_arg(ap, uint16**) = td->td_colormap[2]; + break; + case TIFFTAG_STRIPOFFSETS: + case TIFFTAG_TILEOFFSETS: + _TIFFFillStriles( tif ); + *va_arg(ap, uint64**) = td->td_stripoffset; + break; + case TIFFTAG_STRIPBYTECOUNTS: + case TIFFTAG_TILEBYTECOUNTS: + _TIFFFillStriles( tif ); + *va_arg(ap, uint64**) = td->td_stripbytecount; + break; + case TIFFTAG_MATTEING: + *va_arg(ap, uint16*) = + (td->td_extrasamples == 1 && + td->td_sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA); + break; + case TIFFTAG_EXTRASAMPLES: + *va_arg(ap, uint16*) = td->td_extrasamples; + *va_arg(ap, uint16**) = td->td_sampleinfo; + break; + case TIFFTAG_TILEWIDTH: + *va_arg(ap, uint32*) = td->td_tilewidth; + break; + case TIFFTAG_TILELENGTH: + *va_arg(ap, uint32*) = td->td_tilelength; + break; + case TIFFTAG_TILEDEPTH: + *va_arg(ap, uint32*) = td->td_tiledepth; + break; + case TIFFTAG_DATATYPE: + switch (td->td_sampleformat) { + case SAMPLEFORMAT_UINT: + *va_arg(ap, uint16*) = DATATYPE_UINT; + break; + case SAMPLEFORMAT_INT: + *va_arg(ap, uint16*) = DATATYPE_INT; + break; + case SAMPLEFORMAT_IEEEFP: + *va_arg(ap, uint16*) = DATATYPE_IEEEFP; + break; + case SAMPLEFORMAT_VOID: + *va_arg(ap, uint16*) = DATATYPE_VOID; + break; + } + break; + case TIFFTAG_SAMPLEFORMAT: + *va_arg(ap, uint16*) = td->td_sampleformat; + break; + case TIFFTAG_IMAGEDEPTH: + *va_arg(ap, uint32*) = td->td_imagedepth; + break; + case TIFFTAG_SUBIFD: + *va_arg(ap, uint16*) = td->td_nsubifd; + *va_arg(ap, uint64**) = td->td_subifd; + break; + case TIFFTAG_YCBCRPOSITIONING: + *va_arg(ap, uint16*) = td->td_ycbcrpositioning; + break; + case TIFFTAG_YCBCRSUBSAMPLING: + *va_arg(ap, uint16*) = td->td_ycbcrsubsampling[0]; + *va_arg(ap, uint16*) = td->td_ycbcrsubsampling[1]; + break; + case TIFFTAG_TRANSFERFUNCTION: + *va_arg(ap, uint16**) = td->td_transferfunction[0]; + if (td->td_samplesperpixel - td->td_extrasamples > 1) { + *va_arg(ap, uint16**) = td->td_transferfunction[1]; + *va_arg(ap, uint16**) = td->td_transferfunction[2]; + } + break; + case TIFFTAG_REFERENCEBLACKWHITE: + *va_arg(ap, float**) = td->td_refblackwhite; + break; + case TIFFTAG_INKNAMES: + *va_arg(ap, char**) = td->td_inknames; + break; + default: + { + int i; + + /* + * This can happen if multiple images are open + * with different codecs which have private + * tags. The global tag information table may + * then have tags that are valid for one file + * but not the other. If the client tries to + * get a tag that is not valid for the image's + * codec then we'll arrive here. + */ + if( fip->field_bit != FIELD_CUSTOM ) + { + TIFFErrorExt(tif->tif_clientdata, "_TIFFVGetField", + "%s: Invalid %stag \"%s\" " + "(not supported by codec)", + tif->tif_name, + isPseudoTag(tag) ? "pseudo-" : "", + fip->field_name); + ret_val = 0; + break; + } + + /* + * Do we have a custom value? + */ + ret_val = 0; + for (i = 0; i < td->td_customValueCount; i++) { + TIFFTagValue *tv = td->td_customValues + i; + + if (tv->info->field_tag != tag) + continue; + + if (fip->field_passcount) { + if (fip->field_readcount == TIFF_VARIABLE2) + *va_arg(ap, uint32*) = (uint32)tv->count; + else /* Assume TIFF_VARIABLE */ + *va_arg(ap, uint16*) = (uint16)tv->count; + *va_arg(ap, void **) = tv->value; + ret_val = 1; + } else if (fip->field_tag == TIFFTAG_DOTRANGE + && strcmp(fip->field_name,"DotRange") == 0) { + /* TODO: This is an evil exception and should not have been + handled this way ... likely best if we move it into + the directory structure with an explicit field in + libtiff 4.1 and assign it a FIELD_ value */ + *va_arg(ap, uint16*) = ((uint16 *)tv->value)[0]; + *va_arg(ap, uint16*) = ((uint16 *)tv->value)[1]; + ret_val = 1; + } else { + if (fip->field_type == TIFF_ASCII + || fip->field_readcount == TIFF_VARIABLE + || fip->field_readcount == TIFF_VARIABLE2 + || fip->field_readcount == TIFF_SPP + || tv->count > 1) { + *va_arg(ap, void **) = tv->value; + ret_val = 1; + } else { + char *val = (char *)tv->value; + assert( tv->count == 1 ); + switch (fip->field_type) { + case TIFF_BYTE: + case TIFF_UNDEFINED: + *va_arg(ap, uint8*) = + *(uint8 *)val; + ret_val = 1; + break; + case TIFF_SBYTE: + *va_arg(ap, int8*) = + *(int8 *)val; + ret_val = 1; + break; + case TIFF_SHORT: + *va_arg(ap, uint16*) = + *(uint16 *)val; + ret_val = 1; + break; + case TIFF_SSHORT: + *va_arg(ap, int16*) = + *(int16 *)val; + ret_val = 1; + break; + case TIFF_LONG: + case TIFF_IFD: + *va_arg(ap, uint32*) = + *(uint32 *)val; + ret_val = 1; + break; + case TIFF_SLONG: + *va_arg(ap, int32*) = + *(int32 *)val; + ret_val = 1; + break; + case TIFF_LONG8: + case TIFF_IFD8: + *va_arg(ap, uint64*) = + *(uint64 *)val; + ret_val = 1; + break; + case TIFF_SLONG8: + *va_arg(ap, int64*) = + *(int64 *)val; + ret_val = 1; + break; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_FLOAT: + *va_arg(ap, float*) = + *(float *)val; + ret_val = 1; + break; + case TIFF_DOUBLE: + *va_arg(ap, double*) = + *(double *)val; + ret_val = 1; + break; + default: + ret_val = 0; + break; + } + } + } + break; + } + } + } + return(ret_val); +} + +/* + * Return the value of a field in the + * internal directory structure. + */ +int +TIFFGetField(TIFF* tif, uint32 tag, ...) +{ + int status; + va_list ap; + + va_start(ap, tag); + status = TIFFVGetField(tif, tag, ap); + va_end(ap); + return (status); +} + +/* + * Like TIFFGetField, but taking a varargs + * parameter list. This routine is useful + * for building higher-level interfaces on + * top of the library. + */ +int +TIFFVGetField(TIFF* tif, uint32 tag, va_list ap) +{ + const TIFFField* fip = TIFFFindField(tif, tag, TIFF_ANY); + return (fip && (isPseudoTag(tag) || TIFFFieldSet(tif, fip->field_bit)) ? + (*tif->tif_tagmethods.vgetfield)(tif, tag, ap) : 0); +} + +#define CleanupField(member) { \ + if (td->member) { \ + _TIFFfree(td->member); \ + td->member = 0; \ + } \ +} + +/* + * Release storage associated with a directory. + */ +void +TIFFFreeDirectory(TIFF* tif) +{ + TIFFDirectory *td = &tif->tif_dir; + int i; + + _TIFFmemset(td->td_fieldsset, 0, FIELD_SETLONGS); + CleanupField(td_sminsamplevalue); + CleanupField(td_smaxsamplevalue); + CleanupField(td_colormap[0]); + CleanupField(td_colormap[1]); + CleanupField(td_colormap[2]); + CleanupField(td_sampleinfo); + CleanupField(td_subifd); + CleanupField(td_inknames); + CleanupField(td_refblackwhite); + CleanupField(td_transferfunction[0]); + CleanupField(td_transferfunction[1]); + CleanupField(td_transferfunction[2]); + CleanupField(td_stripoffset); + CleanupField(td_stripbytecount); + TIFFClrFieldBit(tif, FIELD_YCBCRSUBSAMPLING); + TIFFClrFieldBit(tif, FIELD_YCBCRPOSITIONING); + + /* Cleanup custom tag values */ + for( i = 0; i < td->td_customValueCount; i++ ) { + if (td->td_customValues[i].value) + _TIFFfree(td->td_customValues[i].value); + } + + td->td_customValueCount = 0; + CleanupField(td_customValues); + +#if defined(DEFER_STRILE_LOAD) + _TIFFmemset( &(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry)); + _TIFFmemset( &(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry)); +#endif +} +#undef CleanupField + +/* + * Client Tag extension support (from Niles Ritter). + */ +static TIFFExtendProc _TIFFextender = (TIFFExtendProc) NULL; + +TIFFExtendProc +TIFFSetTagExtender(TIFFExtendProc extender) +{ + TIFFExtendProc prev = _TIFFextender; + _TIFFextender = extender; + return (prev); +} + +/* + * Setup for a new directory. Should we automatically call + * TIFFWriteDirectory() if the current one is dirty? + * + * The newly created directory will not exist on the file till + * TIFFWriteDirectory(), TIFFFlush() or TIFFClose() is called. + */ +int +TIFFCreateDirectory(TIFF* tif) +{ + TIFFDefaultDirectory(tif); + tif->tif_diroff = 0; + tif->tif_nextdiroff = 0; + tif->tif_curoff = 0; + tif->tif_row = (uint32) -1; + tif->tif_curstrip = (uint32) -1; + + return 0; +} + +int +TIFFCreateCustomDirectory(TIFF* tif, const TIFFFieldArray* infoarray) +{ + TIFFDefaultDirectory(tif); + + /* + * Reset the field definitions to match the application provided list. + * Hopefully TIFFDefaultDirectory() won't have done anything irreversable + * based on it's assumption this is an image directory. + */ + _TIFFSetupFields(tif, infoarray); + + tif->tif_diroff = 0; + tif->tif_nextdiroff = 0; + tif->tif_curoff = 0; + tif->tif_row = (uint32) -1; + tif->tif_curstrip = (uint32) -1; + + return 0; +} + +int +TIFFCreateEXIFDirectory(TIFF* tif) +{ + const TIFFFieldArray* exifFieldArray; + exifFieldArray = _TIFFGetExifFields(); + return TIFFCreateCustomDirectory(tif, exifFieldArray); +} + +/* + * Setup a default directory structure. + */ +int +TIFFDefaultDirectory(TIFF* tif) +{ + register TIFFDirectory* td = &tif->tif_dir; + const TIFFFieldArray* tiffFieldArray; + + tiffFieldArray = _TIFFGetFields(); + _TIFFSetupFields(tif, tiffFieldArray); + + _TIFFmemset(td, 0, sizeof (*td)); + td->td_fillorder = FILLORDER_MSB2LSB; + td->td_bitspersample = 1; + td->td_threshholding = THRESHHOLD_BILEVEL; + td->td_orientation = ORIENTATION_TOPLEFT; + td->td_samplesperpixel = 1; + td->td_rowsperstrip = (uint32) -1; + td->td_tilewidth = 0; + td->td_tilelength = 0; + td->td_tiledepth = 1; + td->td_stripbytecountsorted = 1; /* Our own arrays always sorted. */ + td->td_resolutionunit = RESUNIT_INCH; + td->td_sampleformat = SAMPLEFORMAT_UINT; + td->td_imagedepth = 1; + td->td_ycbcrsubsampling[0] = 2; + td->td_ycbcrsubsampling[1] = 2; + td->td_ycbcrpositioning = YCBCRPOSITION_CENTERED; + tif->tif_postdecode = _TIFFNoPostDecode; + tif->tif_foundfield = NULL; + tif->tif_tagmethods.vsetfield = _TIFFVSetField; + tif->tif_tagmethods.vgetfield = _TIFFVGetField; + tif->tif_tagmethods.printdir = NULL; + /* + * Give client code a chance to install their own + * tag extensions & methods, prior to compression overloads, + * but do some prior cleanup first. (http://trac.osgeo.org/gdal/ticket/5054) + */ + if (tif->tif_nfieldscompat > 0) { + uint32 i; + + for (i = 0; i < tif->tif_nfieldscompat; i++) { + if (tif->tif_fieldscompat[i].allocated_size) + _TIFFfree(tif->tif_fieldscompat[i].fields); + } + _TIFFfree(tif->tif_fieldscompat); + tif->tif_nfieldscompat = 0; + tif->tif_fieldscompat = NULL; + } + if (_TIFFextender) + (*_TIFFextender)(tif); + (void) TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); + /* + * NB: The directory is marked dirty as a result of setting + * up the default compression scheme. However, this really + * isn't correct -- we want TIFF_DIRTYDIRECT to be set only + * if the user does something. We could just do the setup + * by hand, but it seems better to use the normal mechanism + * (i.e. TIFFSetField). + */ + tif->tif_flags &= ~TIFF_DIRTYDIRECT; + + /* + * As per http://bugzilla.remotesensing.org/show_bug.cgi?id=19 + * we clear the ISTILED flag when setting up a new directory. + * Should we also be clearing stuff like INSUBIFD? + */ + tif->tif_flags &= ~TIFF_ISTILED; + + return (1); +} + +static int +TIFFAdvanceDirectory(TIFF* tif, uint64* nextdir, uint64* off) +{ + static const char module[] = "TIFFAdvanceDirectory"; + if (isMapped(tif)) + { + uint64 poff=*nextdir; + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + tmsize_t poffa,poffb,poffc,poffd; + uint16 dircount; + uint32 nextdir32; + poffa=(tmsize_t)poff; + poffb=poffa+sizeof(uint16); + if (((uint64)poffa!=poff)||(poffbtif->tif_size)) + { + TIFFErrorExt(tif->tif_clientdata,module,"Error fetching directory count"); + *nextdir=0; + return(0); + } + _TIFFmemcpy(&dircount,tif->tif_base+poffa,sizeof(uint16)); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort(&dircount); + poffc=poffb+dircount*12; + poffd=poffc+sizeof(uint32); + if ((poffctif->tif_size)) + { + TIFFErrorExt(tif->tif_clientdata,module,"Error fetching directory link"); + return(0); + } + if (off!=NULL) + *off=(uint64)poffc; + _TIFFmemcpy(&nextdir32,tif->tif_base+poffc,sizeof(uint32)); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&nextdir32); + *nextdir=nextdir32; + } + else + { + tmsize_t poffa,poffb,poffc,poffd; + uint64 dircount64; + uint16 dircount16; + poffa=(tmsize_t)poff; + poffb=poffa+sizeof(uint64); + if (((uint64)poffa!=poff)||(poffbtif->tif_size)) + { + TIFFErrorExt(tif->tif_clientdata,module,"Error fetching directory count"); + return(0); + } + _TIFFmemcpy(&dircount64,tif->tif_base+poffa,sizeof(uint64)); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8(&dircount64); + if (dircount64>0xFFFF) + { + TIFFErrorExt(tif->tif_clientdata,module,"Sanity check on directory count failed"); + return(0); + } + dircount16=(uint16)dircount64; + poffc=poffb+dircount16*20; + poffd=poffc+sizeof(uint64); + if ((poffctif->tif_size)) + { + TIFFErrorExt(tif->tif_clientdata,module,"Error fetching directory link"); + return(0); + } + if (off!=NULL) + *off=(uint64)poffc; + _TIFFmemcpy(nextdir,tif->tif_base+poffc,sizeof(uint64)); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8(nextdir); + } + return(1); + } + else + { + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + uint16 dircount; + uint32 nextdir32; + if (!SeekOK(tif, *nextdir) || + !ReadOK(tif, &dircount, sizeof (uint16))) { + TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory count", + tif->tif_name); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&dircount); + if (off != NULL) + *off = TIFFSeekFile(tif, + dircount*12, SEEK_CUR); + else + (void) TIFFSeekFile(tif, + dircount*12, SEEK_CUR); + if (!ReadOK(tif, &nextdir32, sizeof (uint32))) { + TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory link", + tif->tif_name); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong(&nextdir32); + *nextdir=nextdir32; + } + else + { + uint64 dircount64; + uint16 dircount16; + if (!SeekOK(tif, *nextdir) || + !ReadOK(tif, &dircount64, sizeof (uint64))) { + TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory count", + tif->tif_name); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong8(&dircount64); + if (dircount64>0xFFFF) + { + TIFFErrorExt(tif->tif_clientdata, module, "Error fetching directory count"); + return(0); + } + dircount16 = (uint16)dircount64; + if (off != NULL) + *off = TIFFSeekFile(tif, + dircount16*20, SEEK_CUR); + else + (void) TIFFSeekFile(tif, + dircount16*20, SEEK_CUR); + if (!ReadOK(tif, nextdir, sizeof (uint64))) { + TIFFErrorExt(tif->tif_clientdata, module, "%s: Error fetching directory link", + tif->tif_name); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong8(nextdir); + } + return (1); + } +} + +/* + * Count the number of directories in a file. + */ +uint16 +TIFFNumberOfDirectories(TIFF* tif) +{ + static const char module[] = "TIFFNumberOfDirectories"; + uint64 nextdir; + uint16 n; + if (!(tif->tif_flags&TIFF_BIGTIFF)) + nextdir = tif->tif_header.classic.tiff_diroff; + else + nextdir = tif->tif_header.big.tiff_diroff; + n = 0; + while (nextdir != 0 && TIFFAdvanceDirectory(tif, &nextdir, NULL)) + { + if(++n == 0) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Directory count exceeded 65535 limit, giving up on counting."); + return (65535); + } + } + return (n); +} + +/* + * Set the n-th directory as the current directory. + * NB: Directories are numbered starting at 0. + */ +int +TIFFSetDirectory(TIFF* tif, uint16 dirn) +{ + uint64 nextdir; + uint16 n; + + if (!(tif->tif_flags&TIFF_BIGTIFF)) + nextdir = tif->tif_header.classic.tiff_diroff; + else + nextdir = tif->tif_header.big.tiff_diroff; + for (n = dirn; n > 0 && nextdir != 0; n--) + if (!TIFFAdvanceDirectory(tif, &nextdir, NULL)) + return (0); + tif->tif_nextdiroff = nextdir; + /* + * Set curdir to the actual directory index. The + * -1 is because TIFFReadDirectory will increment + * tif_curdir after successfully reading the directory. + */ + tif->tif_curdir = (dirn - n) - 1; + /* + * Reset tif_dirnumber counter and start new list of seen directories. + * We need this to prevent IFD loops. + */ + tif->tif_dirnumber = 0; + return (TIFFReadDirectory(tif)); +} + +/* + * Set the current directory to be the directory + * located at the specified file offset. This interface + * is used mainly to access directories linked with + * the SubIFD tag (e.g. thumbnail images). + */ +int +TIFFSetSubDirectory(TIFF* tif, uint64 diroff) +{ + tif->tif_nextdiroff = diroff; + /* + * Reset tif_dirnumber counter and start new list of seen directories. + * We need this to prevent IFD loops. + */ + tif->tif_dirnumber = 0; + return (TIFFReadDirectory(tif)); +} + +/* + * Return file offset of the current directory. + */ +uint64 +TIFFCurrentDirOffset(TIFF* tif) +{ + return (tif->tif_diroff); +} + +/* + * Return an indication of whether or not we are + * at the last directory in the file. + */ +int +TIFFLastDirectory(TIFF* tif) +{ + return (tif->tif_nextdiroff == 0); +} + +/* + * Unlink the specified directory from the directory chain. + */ +int +TIFFUnlinkDirectory(TIFF* tif, uint16 dirn) +{ + static const char module[] = "TIFFUnlinkDirectory"; + uint64 nextdir; + uint64 off; + uint16 n; + + if (tif->tif_mode == O_RDONLY) { + TIFFErrorExt(tif->tif_clientdata, module, + "Can not unlink directory in read-only file"); + return (0); + } + /* + * Go to the directory before the one we want + * to unlink and nab the offset of the link + * field we'll need to patch. + */ + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + nextdir = tif->tif_header.classic.tiff_diroff; + off = 4; + } + else + { + nextdir = tif->tif_header.big.tiff_diroff; + off = 8; + } + for (n = dirn-1; n > 0; n--) { + if (nextdir == 0) { + TIFFErrorExt(tif->tif_clientdata, module, "Directory %d does not exist", dirn); + return (0); + } + if (!TIFFAdvanceDirectory(tif, &nextdir, &off)) + return (0); + } + /* + * Advance to the directory to be unlinked and fetch + * the offset of the directory that follows. + */ + if (!TIFFAdvanceDirectory(tif, &nextdir, NULL)) + return (0); + /* + * Go back and patch the link field of the preceding + * directory to point to the offset of the directory + * that follows. + */ + (void) TIFFSeekFile(tif, off, SEEK_SET); + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + uint32 nextdir32; + nextdir32=(uint32)nextdir; + assert((uint64)nextdir32==nextdir); + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong(&nextdir32); + if (!WriteOK(tif, &nextdir32, sizeof (uint32))) { + TIFFErrorExt(tif->tif_clientdata, module, "Error writing directory link"); + return (0); + } + } + else + { + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong8(&nextdir); + if (!WriteOK(tif, &nextdir, sizeof (uint64))) { + TIFFErrorExt(tif->tif_clientdata, module, "Error writing directory link"); + return (0); + } + } + /* + * Leave directory state setup safely. We don't have + * facilities for doing inserting and removing directories, + * so it's safest to just invalidate everything. This + * means that the caller can only append to the directory + * chain. + */ + (*tif->tif_cleanup)(tif); + if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) { + _TIFFfree(tif->tif_rawdata); + tif->tif_rawdata = NULL; + tif->tif_rawcc = 0; + tif->tif_rawdataoff = 0; + tif->tif_rawdataloaded = 0; + } + tif->tif_flags &= ~(TIFF_BEENWRITING|TIFF_BUFFERSETUP|TIFF_POSTENCODE|TIFF_BUF4WRITE); + TIFFFreeDirectory(tif); + TIFFDefaultDirectory(tif); + tif->tif_diroff = 0; /* force link on next write */ + tif->tif_nextdiroff = 0; /* next write must be at end */ + tif->tif_curoff = 0; + tif->tif_row = (uint32) -1; + tif->tif_curstrip = (uint32) -1; + return (1); +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dir.h b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dir.h new file mode 100644 index 000000000..6af5f3dc3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dir.h @@ -0,0 +1,308 @@ +/* $Id: tif_dir.h,v 1.54 2011-02-18 20:53:05 fwarmerdam Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _TIFFDIR_ +#define _TIFFDIR_ +/* + * ``Library-private'' Directory-related Definitions. + */ + +typedef struct { + const TIFFField *info; + int count; + void *value; +} TIFFTagValue; + +/* + * TIFF Image File Directories are comprised of a table of field + * descriptors of the form shown below. The table is sorted in + * ascending order by tag. The values associated with each entry are + * disjoint and may appear anywhere in the file (so long as they are + * placed on a word boundary). + * + * If the value is 4 bytes or less, in ClassicTIFF, or 8 bytes or less in + * BigTIFF, then it is placed in the offset field to save space. If so, + * it is left-justified in the offset field. + */ +typedef struct { + uint16 tdir_tag; /* see below */ + uint16 tdir_type; /* data type; see below */ + uint64 tdir_count; /* number of items; length in spec */ + union { + uint16 toff_short; + uint32 toff_long; + uint64 toff_long8; + } tdir_offset; /* either offset or the data itself if fits */ +} TIFFDirEntry; + +/* + * Internal format of a TIFF directory entry. + */ +typedef struct { +#define FIELD_SETLONGS 4 + /* bit vector of fields that are set */ + unsigned long td_fieldsset[FIELD_SETLONGS]; + + uint32 td_imagewidth, td_imagelength, td_imagedepth; + uint32 td_tilewidth, td_tilelength, td_tiledepth; + uint32 td_subfiletype; + uint16 td_bitspersample; + uint16 td_sampleformat; + uint16 td_compression; + uint16 td_photometric; + uint16 td_threshholding; + uint16 td_fillorder; + uint16 td_orientation; + uint16 td_samplesperpixel; + uint32 td_rowsperstrip; + uint16 td_minsamplevalue, td_maxsamplevalue; + double* td_sminsamplevalue; + double* td_smaxsamplevalue; + float td_xresolution, td_yresolution; + uint16 td_resolutionunit; + uint16 td_planarconfig; + float td_xposition, td_yposition; + uint16 td_pagenumber[2]; + uint16* td_colormap[3]; + uint16 td_halftonehints[2]; + uint16 td_extrasamples; + uint16* td_sampleinfo; + /* even though the name is misleading, td_stripsperimage is the number + * of striles (=strips or tiles) per plane, and td_nstrips the total + * number of striles */ + uint32 td_stripsperimage; + uint32 td_nstrips; /* size of offset & bytecount arrays */ + uint64* td_stripoffset; + uint64* td_stripbytecount; + int td_stripbytecountsorted; /* is the bytecount array sorted ascending? */ +#if defined(DEFER_STRILE_LOAD) + TIFFDirEntry td_stripoffset_entry; /* for deferred loading */ + TIFFDirEntry td_stripbytecount_entry; /* for deferred loading */ +#endif + uint16 td_nsubifd; + uint64* td_subifd; + /* YCbCr parameters */ + uint16 td_ycbcrsubsampling[2]; + uint16 td_ycbcrpositioning; + /* Colorimetry parameters */ + uint16* td_transferfunction[3]; + float* td_refblackwhite; + /* CMYK parameters */ + int td_inknameslen; + char* td_inknames; + + int td_customValueCount; + TIFFTagValue *td_customValues; +} TIFFDirectory; + +/* + * Field flags used to indicate fields that have been set in a directory, and + * to reference fields when manipulating a directory. + */ + +/* + * FIELD_IGNORE is used to signify tags that are to be processed but otherwise + * ignored. This permits antiquated tags to be quietly read and discarded. + * Note that a bit *is* allocated for ignored tags; this is understood by the + * directory reading logic which uses this fact to avoid special-case handling + */ +#define FIELD_IGNORE 0 + +/* multi-item fields */ +#define FIELD_IMAGEDIMENSIONS 1 +#define FIELD_TILEDIMENSIONS 2 +#define FIELD_RESOLUTION 3 +#define FIELD_POSITION 4 + +/* single-item fields */ +#define FIELD_SUBFILETYPE 5 +#define FIELD_BITSPERSAMPLE 6 +#define FIELD_COMPRESSION 7 +#define FIELD_PHOTOMETRIC 8 +#define FIELD_THRESHHOLDING 9 +#define FIELD_FILLORDER 10 +#define FIELD_ORIENTATION 15 +#define FIELD_SAMPLESPERPIXEL 16 +#define FIELD_ROWSPERSTRIP 17 +#define FIELD_MINSAMPLEVALUE 18 +#define FIELD_MAXSAMPLEVALUE 19 +#define FIELD_PLANARCONFIG 20 +#define FIELD_RESOLUTIONUNIT 22 +#define FIELD_PAGENUMBER 23 +#define FIELD_STRIPBYTECOUNTS 24 +#define FIELD_STRIPOFFSETS 25 +#define FIELD_COLORMAP 26 +#define FIELD_EXTRASAMPLES 31 +#define FIELD_SAMPLEFORMAT 32 +#define FIELD_SMINSAMPLEVALUE 33 +#define FIELD_SMAXSAMPLEVALUE 34 +#define FIELD_IMAGEDEPTH 35 +#define FIELD_TILEDEPTH 36 +#define FIELD_HALFTONEHINTS 37 +#define FIELD_YCBCRSUBSAMPLING 39 +#define FIELD_YCBCRPOSITIONING 40 +#define FIELD_REFBLACKWHITE 41 +#define FIELD_TRANSFERFUNCTION 44 +#define FIELD_INKNAMES 46 +#define FIELD_SUBIFD 49 +/* FIELD_CUSTOM (see tiffio.h) 65 */ +/* end of support for well-known tags; codec-private tags follow */ +#define FIELD_CODEC 66 /* base of codec-private tags */ + + +/* + * Pseudo-tags don't normally need field bits since they are not written to an + * output file (by definition). The library also has express logic to always + * query a codec for a pseudo-tag so allocating a field bit for one is a + * waste. If codec wants to promote the notion of a pseudo-tag being ``set'' + * or ``unset'' then it can do using internal state flags without polluting + * the field bit space defined for real tags. + */ +#define FIELD_PSEUDO 0 + +#define FIELD_LAST (32*FIELD_SETLONGS-1) + +#define BITn(n) (((unsigned long)1L)<<((n)&0x1f)) +#define BITFIELDn(tif, n) ((tif)->tif_dir.td_fieldsset[(n)/32]) +#define TIFFFieldSet(tif, field) (BITFIELDn(tif, field) & BITn(field)) +#define TIFFSetFieldBit(tif, field) (BITFIELDn(tif, field) |= BITn(field)) +#define TIFFClrFieldBit(tif, field) (BITFIELDn(tif, field) &= ~BITn(field)) + +#define FieldSet(fields, f) (fields[(f)/32] & BITn(f)) +#define ResetFieldBit(fields, f) (fields[(f)/32] &= ~BITn(f)) + +typedef enum { + TIFF_SETGET_UNDEFINED = 0, + TIFF_SETGET_ASCII = 1, + TIFF_SETGET_UINT8 = 2, + TIFF_SETGET_SINT8 = 3, + TIFF_SETGET_UINT16 = 4, + TIFF_SETGET_SINT16 = 5, + TIFF_SETGET_UINT32 = 6, + TIFF_SETGET_SINT32 = 7, + TIFF_SETGET_UINT64 = 8, + TIFF_SETGET_SINT64 = 9, + TIFF_SETGET_FLOAT = 10, + TIFF_SETGET_DOUBLE = 11, + TIFF_SETGET_IFD8 = 12, + TIFF_SETGET_INT = 13, + TIFF_SETGET_UINT16_PAIR = 14, + TIFF_SETGET_C0_ASCII = 15, + TIFF_SETGET_C0_UINT8 = 16, + TIFF_SETGET_C0_SINT8 = 17, + TIFF_SETGET_C0_UINT16 = 18, + TIFF_SETGET_C0_SINT16 = 19, + TIFF_SETGET_C0_UINT32 = 20, + TIFF_SETGET_C0_SINT32 = 21, + TIFF_SETGET_C0_UINT64 = 22, + TIFF_SETGET_C0_SINT64 = 23, + TIFF_SETGET_C0_FLOAT = 24, + TIFF_SETGET_C0_DOUBLE = 25, + TIFF_SETGET_C0_IFD8 = 26, + TIFF_SETGET_C16_ASCII = 27, + TIFF_SETGET_C16_UINT8 = 28, + TIFF_SETGET_C16_SINT8 = 29, + TIFF_SETGET_C16_UINT16 = 30, + TIFF_SETGET_C16_SINT16 = 31, + TIFF_SETGET_C16_UINT32 = 32, + TIFF_SETGET_C16_SINT32 = 33, + TIFF_SETGET_C16_UINT64 = 34, + TIFF_SETGET_C16_SINT64 = 35, + TIFF_SETGET_C16_FLOAT = 36, + TIFF_SETGET_C16_DOUBLE = 37, + TIFF_SETGET_C16_IFD8 = 38, + TIFF_SETGET_C32_ASCII = 39, + TIFF_SETGET_C32_UINT8 = 40, + TIFF_SETGET_C32_SINT8 = 41, + TIFF_SETGET_C32_UINT16 = 42, + TIFF_SETGET_C32_SINT16 = 43, + TIFF_SETGET_C32_UINT32 = 44, + TIFF_SETGET_C32_SINT32 = 45, + TIFF_SETGET_C32_UINT64 = 46, + TIFF_SETGET_C32_SINT64 = 47, + TIFF_SETGET_C32_FLOAT = 48, + TIFF_SETGET_C32_DOUBLE = 49, + TIFF_SETGET_C32_IFD8 = 50, + TIFF_SETGET_OTHER = 51 +} TIFFSetGetFieldType; + +#if defined(__cplusplus) +extern "C" { +#endif + +extern const TIFFFieldArray* _TIFFGetFields(void); +extern const TIFFFieldArray* _TIFFGetExifFields(void); +extern void _TIFFSetupFields(TIFF* tif, const TIFFFieldArray* infoarray); +extern void _TIFFPrintFieldInfo(TIFF*, FILE*); + +extern int _TIFFFillStriles(TIFF*); + +typedef enum { + tfiatImage, + tfiatExif, + tfiatOther +} TIFFFieldArrayType; + +struct _TIFFFieldArray { + TIFFFieldArrayType type; /* array type, will be used to determine if IFD is image and such */ + uint32 allocated_size; /* 0 if array is constant, other if modified by future definition extension support */ + uint32 count; /* number of elements in fields array */ + TIFFField* fields; /* actual field info */ +}; + +struct _TIFFField { + uint32 field_tag; /* field's tag */ + short field_readcount; /* read count/TIFF_VARIABLE/TIFF_SPP */ + short field_writecount; /* write count/TIFF_VARIABLE */ + TIFFDataType field_type; /* type of associated data */ + uint32 reserved; /* reserved for future extension */ + TIFFSetGetFieldType set_field_type; /* type to be passed to TIFFSetField */ + TIFFSetGetFieldType get_field_type; /* type to be passed to TIFFGetField */ + unsigned short field_bit; /* bit in fieldsset bit vector */ + unsigned char field_oktochange; /* if true, can change while writing */ + unsigned char field_passcount; /* if true, pass dir count on set */ + char* field_name; /* ASCII name */ + TIFFFieldArray* field_subfields; /* if field points to child ifds, child ifd field definition array */ +}; + +extern int _TIFFMergeFields(TIFF*, const TIFFField[], uint32); +extern const TIFFField* _TIFFFindOrRegisterField(TIFF *, uint32, TIFFDataType); +extern TIFFField* _TIFFCreateAnonField(TIFF *, uint32, TIFFDataType); + +#if defined(__cplusplus) +} +#endif +#endif /* _TIFFDIR_ */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dirinfo.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dirinfo.c new file mode 100644 index 000000000..7db4bdb95 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dirinfo.c @@ -0,0 +1,959 @@ +/* $Id: tif_dirinfo.c,v 1.121 2014-05-07 01:58:46 bfriesen Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Core Directory Tag Support. + */ +#include "tiffiop.h" +#include + +/* + * NOTE: THIS ARRAY IS ASSUMED TO BE SORTED BY TAG. + * + * NOTE: The second field (field_readcount) and third field (field_writecount) + * sometimes use the values TIFF_VARIABLE (-1), TIFF_VARIABLE2 (-3) + * and TIFF_SPP (-2). The macros should be used but would throw off + * the formatting of the code, so please interprete the -1, -2 and -3 + * values accordingly. + */ + +static TIFFFieldArray tiffFieldArray; +static TIFFFieldArray exifFieldArray; + +static TIFFField +tiffFields[] = { + { TIFFTAG_SUBFILETYPE, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_SUBFILETYPE, 1, 0, "SubfileType", NULL }, + { TIFFTAG_OSUBFILETYPE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_SUBFILETYPE, 1, 0, "OldSubfileType", NULL }, + { TIFFTAG_IMAGEWIDTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_IMAGEDIMENSIONS, 0, 0, "ImageWidth", NULL }, + { TIFFTAG_IMAGELENGTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_IMAGEDIMENSIONS, 1, 0, "ImageLength", NULL }, + { TIFFTAG_BITSPERSAMPLE, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_BITSPERSAMPLE, 0, 0, "BitsPerSample", NULL }, + { TIFFTAG_COMPRESSION, -1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_COMPRESSION, 0, 0, "Compression", NULL }, + { TIFFTAG_PHOTOMETRIC, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_PHOTOMETRIC, 0, 0, "PhotometricInterpretation", NULL }, + { TIFFTAG_THRESHHOLDING, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_THRESHHOLDING, 1, 0, "Threshholding", NULL }, + { TIFFTAG_CELLWIDTH, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "CellWidth", NULL }, + { TIFFTAG_CELLLENGTH, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "CellLength", NULL }, + { TIFFTAG_FILLORDER, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_FILLORDER, 0, 0, "FillOrder", NULL }, + { TIFFTAG_DOCUMENTNAME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DocumentName", NULL }, + { TIFFTAG_IMAGEDESCRIPTION, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageDescription", NULL }, + { TIFFTAG_MAKE, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Make", NULL }, + { TIFFTAG_MODEL, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Model", NULL }, + { TIFFTAG_STRIPOFFSETS, -1, -1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_STRIPOFFSETS, 0, 0, "StripOffsets", NULL }, + { TIFFTAG_ORIENTATION, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_ORIENTATION, 0, 0, "Orientation", NULL }, + { TIFFTAG_SAMPLESPERPIXEL, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_SAMPLESPERPIXEL, 0, 0, "SamplesPerPixel", NULL }, + { TIFFTAG_ROWSPERSTRIP, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_ROWSPERSTRIP, 0, 0, "RowsPerStrip", NULL }, + { TIFFTAG_STRIPBYTECOUNTS, -1, -1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_STRIPBYTECOUNTS, 0, 0, "StripByteCounts", NULL }, + { TIFFTAG_MINSAMPLEVALUE, -2, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_MINSAMPLEVALUE, 1, 0, "MinSampleValue", NULL }, + { TIFFTAG_MAXSAMPLEVALUE, -2, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_MAXSAMPLEVALUE, 1, 0, "MaxSampleValue", NULL }, + { TIFFTAG_XRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_RESOLUTION, 1, 0, "XResolution", NULL }, + { TIFFTAG_YRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_RESOLUTION, 1, 0, "YResolution", NULL }, + { TIFFTAG_PLANARCONFIG, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_PLANARCONFIG, 0, 0, "PlanarConfiguration", NULL }, + { TIFFTAG_PAGENAME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PageName", NULL }, + { TIFFTAG_XPOSITION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_POSITION, 1, 0, "XPosition", NULL }, + { TIFFTAG_YPOSITION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_POSITION, 1, 0, "YPosition", NULL }, + { TIFFTAG_FREEOFFSETS, -1, -1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 0, 0, "FreeOffsets", NULL }, + { TIFFTAG_FREEBYTECOUNTS, -1, -1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 0, 0, "FreeByteCounts", NULL }, + { TIFFTAG_GRAYRESPONSEUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "GrayResponseUnit", NULL }, + { TIFFTAG_GRAYRESPONSECURVE, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "GrayResponseCurve", NULL }, + { TIFFTAG_RESOLUTIONUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_RESOLUTIONUNIT, 1, 0, "ResolutionUnit", NULL }, + { TIFFTAG_PAGENUMBER, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_UINT16_PAIR, TIFF_SETGET_UNDEFINED, FIELD_PAGENUMBER, 1, 0, "PageNumber", NULL }, + { TIFFTAG_COLORRESPONSEUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_IGNORE, 1, 0, "ColorResponseUnit", NULL }, + { TIFFTAG_TRANSFERFUNCTION, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_OTHER, TIFF_SETGET_UNDEFINED, FIELD_TRANSFERFUNCTION, 1, 0, "TransferFunction", NULL }, + { TIFFTAG_SOFTWARE, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Software", NULL }, + { TIFFTAG_DATETIME, 20, 20, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DateTime", NULL }, + { TIFFTAG_ARTIST, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Artist", NULL }, + { TIFFTAG_HOSTCOMPUTER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "HostComputer", NULL }, + { TIFFTAG_WHITEPOINT, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "WhitePoint", NULL }, + { TIFFTAG_PRIMARYCHROMATICITIES, 6, 6, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PrimaryChromaticities", NULL }, + { TIFFTAG_COLORMAP, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_OTHER, TIFF_SETGET_UNDEFINED, FIELD_COLORMAP, 1, 0, "ColorMap", NULL }, + { TIFFTAG_HALFTONEHINTS, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_UINT16_PAIR, TIFF_SETGET_UNDEFINED, FIELD_HALFTONEHINTS, 1, 0, "HalftoneHints", NULL }, + { TIFFTAG_TILEWIDTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_TILEDIMENSIONS, 0, 0, "TileWidth", NULL }, + { TIFFTAG_TILELENGTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_TILEDIMENSIONS, 0, 0, "TileLength", NULL }, + { TIFFTAG_TILEOFFSETS, -1, 1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_STRIPOFFSETS, 0, 0, "TileOffsets", NULL }, + { TIFFTAG_TILEBYTECOUNTS, -1, 1, TIFF_LONG8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_STRIPBYTECOUNTS, 0, 0, "TileByteCounts", NULL }, + { TIFFTAG_SUBIFD, -1, -1, TIFF_IFD8, 0, TIFF_SETGET_C16_IFD8, TIFF_SETGET_UNDEFINED, FIELD_SUBIFD, 1, 1, "SubIFD", &tiffFieldArray }, + { TIFFTAG_INKSET, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "InkSet", NULL }, + { TIFFTAG_INKNAMES, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_C16_ASCII, TIFF_SETGET_UNDEFINED, FIELD_INKNAMES, 1, 1, "InkNames", NULL }, + { TIFFTAG_NUMBEROFINKS, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "NumberOfInks", NULL }, + { TIFFTAG_DOTRANGE, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_UINT16_PAIR, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DotRange", NULL }, + { TIFFTAG_TARGETPRINTER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TargetPrinter", NULL }, + { TIFFTAG_EXTRASAMPLES, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_EXTRASAMPLES, 0, 1, "ExtraSamples", NULL }, + { TIFFTAG_SAMPLEFORMAT, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_SAMPLEFORMAT, 0, 0, "SampleFormat", NULL }, + { TIFFTAG_SMINSAMPLEVALUE, -2, -1, TIFF_ANY, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_SMINSAMPLEVALUE, 1, 0, "SMinSampleValue", NULL }, + { TIFFTAG_SMAXSAMPLEVALUE, -2, -1, TIFF_ANY, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_SMAXSAMPLEVALUE, 1, 0, "SMaxSampleValue", NULL }, + { TIFFTAG_CLIPPATH, -1, -3, TIFF_BYTE, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ClipPath", NULL }, + { TIFFTAG_XCLIPPATHUNITS, 1, 1, TIFF_SLONG, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "XClipPathUnits", NULL }, + { TIFFTAG_XCLIPPATHUNITS, 1, 1, TIFF_SBYTE, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "XClipPathUnits", NULL }, + { TIFFTAG_YCLIPPATHUNITS, 1, 1, TIFF_SLONG, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "YClipPathUnits", NULL }, + { TIFFTAG_YCBCRCOEFFICIENTS, 3, 3, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "YCbCrCoefficients", NULL }, + { TIFFTAG_YCBCRSUBSAMPLING, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_UINT16_PAIR, TIFF_SETGET_UNDEFINED, FIELD_YCBCRSUBSAMPLING, 0, 0, "YCbCrSubsampling", NULL }, + { TIFFTAG_YCBCRPOSITIONING, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_YCBCRPOSITIONING, 0, 0, "YCbCrPositioning", NULL }, + { TIFFTAG_REFERENCEBLACKWHITE, 6, 6, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_REFBLACKWHITE, 1, 0, "ReferenceBlackWhite", NULL }, + { TIFFTAG_XMLPACKET, -3, -3, TIFF_BYTE, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "XMLPacket", NULL }, + /* begin SGI tags */ + { TIFFTAG_MATTEING, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_EXTRASAMPLES, 0, 0, "Matteing", NULL }, + { TIFFTAG_DATATYPE, -2, -1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_SAMPLEFORMAT, 0, 0, "DataType", NULL }, + { TIFFTAG_IMAGEDEPTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_IMAGEDEPTH, 0, 0, "ImageDepth", NULL }, + { TIFFTAG_TILEDEPTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_TILEDEPTH, 0, 0, "TileDepth", NULL }, + /* end SGI tags */ + /* begin Pixar tags */ + { TIFFTAG_PIXAR_IMAGEFULLWIDTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageFullWidth", NULL }, + { TIFFTAG_PIXAR_IMAGEFULLLENGTH, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageFullLength", NULL }, + { TIFFTAG_PIXAR_TEXTUREFORMAT, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TextureFormat", NULL }, + { TIFFTAG_PIXAR_WRAPMODES, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "TextureWrapModes", NULL }, + { TIFFTAG_PIXAR_FOVCOT, 1, 1, TIFF_FLOAT, 0, TIFF_SETGET_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FieldOfViewCotangent", NULL }, + { TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN, 16, 16, TIFF_FLOAT, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MatrixWorldToScreen", NULL }, + { TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA, 16, 16, TIFF_FLOAT, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MatrixWorldToCamera", NULL }, + { TIFFTAG_CFAREPEATPATTERNDIM, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_C0_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "CFARepeatPatternDim", NULL }, + { TIFFTAG_CFAPATTERN, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "CFAPattern" , NULL}, + { TIFFTAG_COPYRIGHT, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Copyright", NULL }, + /* end Pixar tags */ + { TIFFTAG_RICHTIFFIPTC, -3, -3, TIFF_LONG, 0, TIFF_SETGET_C32_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "RichTIFFIPTC", NULL }, + { TIFFTAG_PHOTOSHOP, -3, -3, TIFF_BYTE, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "Photoshop", NULL }, + { TIFFTAG_EXIFIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "EXIFIFDOffset", &exifFieldArray }, + { TIFFTAG_ICCPROFILE, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ICC Profile", NULL }, + { TIFFTAG_GPSIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "GPSIFDOffset", NULL }, + { TIFFTAG_FAXRECVPARAMS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_CUSTOM, TRUE, FALSE, "FaxRecvParams", NULL }, + { TIFFTAG_FAXSUBADDRESS, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_ASCII, FIELD_CUSTOM, TRUE, FALSE, "FaxSubAddress", NULL }, + { TIFFTAG_FAXRECVTIME, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_CUSTOM, TRUE, FALSE, "FaxRecvTime", NULL }, + { TIFFTAG_FAXDCS, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_ASCII, FIELD_CUSTOM, TRUE, FALSE, "FaxDcs", NULL }, + { TIFFTAG_STONITS, 1, 1, TIFF_DOUBLE, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "StoNits", NULL }, + { TIFFTAG_INTEROPERABILITYIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "InteroperabilityIFDOffset", NULL }, + /* begin DNG tags */ + { TIFFTAG_DNGVERSION, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DNGVersion", NULL }, + { TIFFTAG_DNGBACKWARDVERSION, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DNGBackwardVersion", NULL }, + { TIFFTAG_UNIQUECAMERAMODEL, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "UniqueCameraModel", NULL }, + { TIFFTAG_LOCALIZEDCAMERAMODEL, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "LocalizedCameraModel", NULL }, + { TIFFTAG_CFAPLANECOLOR, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "CFAPlaneColor", NULL }, + { TIFFTAG_CFALAYOUT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "CFALayout", NULL }, + { TIFFTAG_LINEARIZATIONTABLE, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "LinearizationTable", NULL }, + { TIFFTAG_BLACKLEVELREPEATDIM, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_C0_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BlackLevelRepeatDim", NULL }, + { TIFFTAG_BLACKLEVEL, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "BlackLevel", NULL }, + { TIFFTAG_BLACKLEVELDELTAH, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "BlackLevelDeltaH", NULL }, + { TIFFTAG_BLACKLEVELDELTAV, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "BlackLevelDeltaV", NULL }, + { TIFFTAG_WHITELEVEL, -1, -1, TIFF_LONG, 0, TIFF_SETGET_C16_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "WhiteLevel", NULL }, + { TIFFTAG_DEFAULTSCALE, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DefaultScale", NULL }, + { TIFFTAG_BESTQUALITYSCALE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BestQualityScale", NULL }, + { TIFFTAG_DEFAULTCROPORIGIN, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DefaultCropOrigin", NULL }, + { TIFFTAG_DEFAULTCROPSIZE, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "DefaultCropSize", NULL }, + { TIFFTAG_COLORMATRIX1, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ColorMatrix1", NULL }, + { TIFFTAG_COLORMATRIX2, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ColorMatrix2", NULL }, + { TIFFTAG_CAMERACALIBRATION1, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "CameraCalibration1", NULL }, + { TIFFTAG_CAMERACALIBRATION2, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "CameraCalibration2", NULL }, + { TIFFTAG_REDUCTIONMATRIX1, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ReductionMatrix1", NULL }, + { TIFFTAG_REDUCTIONMATRIX2, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ReductionMatrix2", NULL }, + { TIFFTAG_ANALOGBALANCE, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "AnalogBalance", NULL }, + { TIFFTAG_ASSHOTNEUTRAL, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "AsShotNeutral", NULL }, + { TIFFTAG_ASSHOTWHITEXY, 2, 2, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "AsShotWhiteXY", NULL }, + { TIFFTAG_BASELINEEXPOSURE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BaselineExposure", NULL }, + { TIFFTAG_BASELINENOISE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BaselineNoise", NULL }, + { TIFFTAG_BASELINESHARPNESS, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BaselineSharpness", NULL }, + { TIFFTAG_BAYERGREENSPLIT, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "BayerGreenSplit", NULL }, + { TIFFTAG_LINEARRESPONSELIMIT, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "LinearResponseLimit", NULL }, + { TIFFTAG_CAMERASERIALNUMBER, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CameraSerialNumber", NULL }, + { TIFFTAG_LENSINFO, 4, 4, TIFF_RATIONAL, 0, TIFF_SETGET_C0_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "LensInfo", NULL }, + { TIFFTAG_CHROMABLURRADIUS, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ChromaBlurRadius", NULL }, + { TIFFTAG_ANTIALIASSTRENGTH, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "AntiAliasStrength", NULL }, + { TIFFTAG_SHADOWSCALE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ShadowScale", NULL }, + { TIFFTAG_DNGPRIVATEDATA, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "DNGPrivateData", NULL }, + { TIFFTAG_MAKERNOTESAFETY, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "MakerNoteSafety", NULL }, + { TIFFTAG_CALIBRATIONILLUMINANT1, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "CalibrationIlluminant1", NULL }, + { TIFFTAG_CALIBRATIONILLUMINANT2, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "CalibrationIlluminant2", NULL }, + { TIFFTAG_RAWDATAUNIQUEID, 16, 16, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "RawDataUniqueID", NULL }, + { TIFFTAG_ORIGINALRAWFILENAME, -1, -1, TIFF_BYTE, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "OriginalRawFileName", NULL }, + { TIFFTAG_ORIGINALRAWFILEDATA, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "OriginalRawFileData", NULL }, + { TIFFTAG_ACTIVEAREA, 4, 4, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ActiveArea", NULL }, + { TIFFTAG_MASKEDAREAS, -1, -1, TIFF_LONG, 0, TIFF_SETGET_C16_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "MaskedAreas", NULL }, + { TIFFTAG_ASSHOTICCPROFILE, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "AsShotICCProfile", NULL }, + { TIFFTAG_ASSHOTPREPROFILEMATRIX, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "AsShotPreProfileMatrix", NULL }, + { TIFFTAG_CURRENTICCPROFILE, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "CurrentICCProfile", NULL }, + { TIFFTAG_CURRENTPREPROFILEMATRIX, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "CurrentPreProfileMatrix", NULL }, + { TIFFTAG_PERSAMPLE, 0, 0, TIFF_SHORT, 0, TIFF_SETGET_UNDEFINED, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "PerSample", NULL}, + /* end DNG tags */ + /* begin TIFF/FX tags */ + { TIFFTAG_INDEXED, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "Indexed", NULL }, + { TIFFTAG_GLOBALPARAMETERSIFD, 1, 1, TIFF_IFD8, 0, TIFF_SETGET_IFD8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "GlobalParametersIFD", NULL }, + { TIFFTAG_PROFILETYPE, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ProfileType", NULL }, + { TIFFTAG_FAXPROFILE, 1, 1, TIFF_BYTE, 0, TIFF_SETGET_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "FaxProfile", NULL }, + { TIFFTAG_CODINGMETHODS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "CodingMethods", NULL }, + { TIFFTAG_VERSIONYEAR, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "VersionYear", NULL }, + { TIFFTAG_MODENUMBER, 1, 1, TIFF_BYTE, 0, TIFF_SETGET_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ModeNumber", NULL }, + { TIFFTAG_DECODE, -1, -1, TIFF_SRATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "Decode", NULL }, + { TIFFTAG_IMAGEBASECOLOR, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "ImageBaseColor", NULL }, + { TIFFTAG_T82OPTIONS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "T82Options", NULL }, + { TIFFTAG_STRIPROWCOUNTS, -1, -1, TIFF_LONG, 0, TIFF_SETGET_C16_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 1, "StripRowCounts", NULL }, + { TIFFTAG_IMAGELAYER, 2, 2, TIFF_LONG, 0, TIFF_SETGET_C0_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 0, 0, "ImageLayer", NULL }, + /* end TIFF/FX tags */ + /* begin pseudo tags */ +}; + +static TIFFField +exifFields[] = { + { EXIFTAG_EXPOSURETIME, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureTime", NULL }, + { EXIFTAG_FNUMBER, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FNumber", NULL }, + { EXIFTAG_EXPOSUREPROGRAM, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureProgram", NULL }, + { EXIFTAG_SPECTRALSENSITIVITY, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SpectralSensitivity", NULL }, + { EXIFTAG_ISOSPEEDRATINGS, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "ISOSpeedRatings", NULL }, + { EXIFTAG_OECF, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "OptoelectricConversionFactor", NULL }, + { EXIFTAG_EXIFVERSION, 4, 4, TIFF_UNDEFINED, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExifVersion", NULL }, + { EXIFTAG_DATETIMEORIGINAL, 20, 20, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DateTimeOriginal", NULL }, + { EXIFTAG_DATETIMEDIGITIZED, 20, 20, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DateTimeDigitized", NULL }, + { EXIFTAG_COMPONENTSCONFIGURATION, 4, 4, TIFF_UNDEFINED, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ComponentsConfiguration", NULL }, + { EXIFTAG_COMPRESSEDBITSPERPIXEL, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CompressedBitsPerPixel", NULL }, + { EXIFTAG_SHUTTERSPEEDVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ShutterSpeedValue", NULL }, + { EXIFTAG_APERTUREVALUE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ApertureValue", NULL }, + { EXIFTAG_BRIGHTNESSVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "BrightnessValue", NULL }, + { EXIFTAG_EXPOSUREBIASVALUE, 1, 1, TIFF_SRATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureBiasValue", NULL }, + { EXIFTAG_MAXAPERTUREVALUE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MaxApertureValue", NULL }, + { EXIFTAG_SUBJECTDISTANCE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubjectDistance", NULL }, + { EXIFTAG_METERINGMODE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "MeteringMode", NULL }, + { EXIFTAG_LIGHTSOURCE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "LightSource", NULL }, + { EXIFTAG_FLASH, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Flash", NULL }, + { EXIFTAG_FOCALLENGTH, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalLength", NULL }, + { EXIFTAG_SUBJECTAREA, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "SubjectArea", NULL }, + { EXIFTAG_MAKERNOTE, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "MakerNote", NULL }, + { EXIFTAG_USERCOMMENT, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "UserComment", NULL }, + { EXIFTAG_SUBSECTIME, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubSecTime", NULL }, + { EXIFTAG_SUBSECTIMEORIGINAL, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubSecTimeOriginal", NULL }, + { EXIFTAG_SUBSECTIMEDIGITIZED, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubSecTimeDigitized", NULL }, + { EXIFTAG_FLASHPIXVERSION, 4, 4, TIFF_UNDEFINED, 0, TIFF_SETGET_C0_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FlashpixVersion", NULL }, + { EXIFTAG_COLORSPACE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ColorSpace", NULL }, + { EXIFTAG_PIXELXDIMENSION, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PixelXDimension", NULL }, + { EXIFTAG_PIXELYDIMENSION, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "PixelYDimension", NULL }, + { EXIFTAG_RELATEDSOUNDFILE, 13, 13, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "RelatedSoundFile", NULL }, + { EXIFTAG_FLASHENERGY, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FlashEnergy", NULL }, + { EXIFTAG_SPATIALFREQUENCYRESPONSE, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "SpatialFrequencyResponse", NULL }, + { EXIFTAG_FOCALPLANEXRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalPlaneXResolution", NULL }, + { EXIFTAG_FOCALPLANEYRESOLUTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalPlaneYResolution", NULL }, + { EXIFTAG_FOCALPLANERESOLUTIONUNIT, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalPlaneResolutionUnit", NULL }, + { EXIFTAG_SUBJECTLOCATION, 2, 2, TIFF_SHORT, 0, TIFF_SETGET_C0_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubjectLocation", NULL }, + { EXIFTAG_EXPOSUREINDEX, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureIndex", NULL }, + { EXIFTAG_SENSINGMETHOD, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SensingMethod", NULL }, + { EXIFTAG_FILESOURCE, 1, 1, TIFF_UNDEFINED, 0, TIFF_SETGET_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FileSource", NULL }, + { EXIFTAG_SCENETYPE, 1, 1, TIFF_UNDEFINED, 0, TIFF_SETGET_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SceneType", NULL }, + { EXIFTAG_CFAPATTERN, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "CFAPattern", NULL }, + { EXIFTAG_CUSTOMRENDERED, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "CustomRendered", NULL }, + { EXIFTAG_EXPOSUREMODE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ExposureMode", NULL }, + { EXIFTAG_WHITEBALANCE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "WhiteBalance", NULL }, + { EXIFTAG_DIGITALZOOMRATIO, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "DigitalZoomRatio", NULL }, + { EXIFTAG_FOCALLENGTHIN35MMFILM, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "FocalLengthIn35mmFilm", NULL }, + { EXIFTAG_SCENECAPTURETYPE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SceneCaptureType", NULL }, + { EXIFTAG_GAINCONTROL, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GainControl", NULL }, + { EXIFTAG_CONTRAST, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Contrast", NULL }, + { EXIFTAG_SATURATION, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Saturation", NULL }, + { EXIFTAG_SHARPNESS, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Sharpness", NULL }, + { EXIFTAG_DEVICESETTINGDESCRIPTION, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "DeviceSettingDescription", NULL }, + { EXIFTAG_SUBJECTDISTANCERANGE, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "SubjectDistanceRange", NULL }, + { EXIFTAG_IMAGEUNIQUEID, 33, 33, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "ImageUniqueID", NULL } +}; + +static TIFFFieldArray +tiffFieldArray = { tfiatImage, 0, TIFFArrayCount(tiffFields), tiffFields }; +static TIFFFieldArray +exifFieldArray = { tfiatExif, 0, TIFFArrayCount(exifFields), exifFields }; + +/* + * We have our own local lfind() equivelent to avoid subtle differences + * in types passed to lfind() on different systems. + */ + +static void * +td_lfind(const void *key, const void *base, size_t *nmemb, size_t size, + int(*compar)(const void *, const void *)) +{ + char *element, *end; + + end = (char *)base + *nmemb * size; + for (element = (char *)base; element < end; element += size) + if (!compar(key, element)) /* key found */ + return element; + + return NULL; +} + +const TIFFFieldArray* +_TIFFGetFields(void) +{ + return(&tiffFieldArray); +} + +const TIFFFieldArray* +_TIFFGetExifFields(void) +{ + return(&exifFieldArray); +} + +void +_TIFFSetupFields(TIFF* tif, const TIFFFieldArray* fieldarray) +{ + if (tif->tif_fields && tif->tif_nfields > 0) { + uint32 i; + + for (i = 0; i < tif->tif_nfields; i++) { + TIFFField *fld = tif->tif_fields[i]; + if (fld->field_bit == FIELD_CUSTOM && + strncmp("Tag ", fld->field_name, 4) == 0) { + _TIFFfree(fld->field_name); + _TIFFfree(fld); + } + } + + _TIFFfree(tif->tif_fields); + tif->tif_fields = NULL; + tif->tif_nfields = 0; + } + if (!_TIFFMergeFields(tif, fieldarray->fields, fieldarray->count)) { + TIFFErrorExt(tif->tif_clientdata, "_TIFFSetupFields", + "Setting up field info failed"); + } +} + +static int +tagCompare(const void* a, const void* b) +{ + const TIFFField* ta = *(const TIFFField**) a; + const TIFFField* tb = *(const TIFFField**) b; + /* NB: be careful of return values for 16-bit platforms */ + if (ta->field_tag != tb->field_tag) + return (int)ta->field_tag - (int)tb->field_tag; + else + return (ta->field_type == TIFF_ANY) ? + 0 : ((int)tb->field_type - (int)ta->field_type); +} + +static int +tagNameCompare(const void* a, const void* b) +{ + const TIFFField* ta = *(const TIFFField**) a; + const TIFFField* tb = *(const TIFFField**) b; + int ret = strcmp(ta->field_name, tb->field_name); + + if (ret) + return ret; + else + return (ta->field_type == TIFF_ANY) ? + 0 : ((int)tb->field_type - (int)ta->field_type); +} + +int +_TIFFMergeFields(TIFF* tif, const TIFFField info[], uint32 n) +{ + static const char module[] = "_TIFFMergeFields"; + static const char reason[] = "for fields array"; + /* TIFFField** tp; */ + uint32 i; + + tif->tif_foundfield = NULL; + + if (tif->tif_fields && tif->tif_nfields > 0) { + tif->tif_fields = (TIFFField**) + _TIFFCheckRealloc(tif, tif->tif_fields, + (tif->tif_nfields + n), + sizeof(TIFFField *), reason); + } else { + tif->tif_fields = (TIFFField **) + _TIFFCheckMalloc(tif, n, sizeof(TIFFField *), + reason); + } + if (!tif->tif_fields) { + TIFFErrorExt(tif->tif_clientdata, module, + "Failed to allocate fields array"); + return 0; + } + + /* tp = tif->tif_fields + tif->tif_nfields; */ + for (i = 0; i < n; i++) { + const TIFFField *fip = + TIFFFindField(tif, info[i].field_tag, TIFF_ANY); + + /* only add definitions that aren't already present */ + if (!fip) { + tif->tif_fields[tif->tif_nfields] = (TIFFField *) (info+i); + tif->tif_nfields++; + } + } + + /* Sort the field info by tag number */ + qsort(tif->tif_fields, tif->tif_nfields, + sizeof(TIFFField *), tagCompare); + + return n; +} + +void +_TIFFPrintFieldInfo(TIFF* tif, FILE* fd) +{ + uint32 i; + + fprintf(fd, "%s: \n", tif->tif_name); + for (i = 0; i < tif->tif_nfields; i++) { + const TIFFField* fip = tif->tif_fields[i]; + fprintf(fd, "field[%2d] %5lu, %2d, %2d, %d, %2d, %5s, %5s, %s\n" + , (int)i + , (unsigned long) fip->field_tag + , fip->field_readcount, fip->field_writecount + , fip->field_type + , fip->field_bit + , fip->field_oktochange ? "TRUE" : "FALSE" + , fip->field_passcount ? "TRUE" : "FALSE" + , fip->field_name + ); + } +} + +/* + * Return size of TIFFDataType in bytes + */ +int +TIFFDataWidth(TIFFDataType type) +{ + switch(type) + { + case 0: /* nothing */ + case TIFF_BYTE: + case TIFF_ASCII: + case TIFF_SBYTE: + case TIFF_UNDEFINED: + return 1; + case TIFF_SHORT: + case TIFF_SSHORT: + return 2; + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_FLOAT: + case TIFF_IFD: + return 4; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_DOUBLE: + case TIFF_LONG8: + case TIFF_SLONG8: + case TIFF_IFD8: + return 8; + default: + return 0; /* will return 0 for unknown types */ + } +} + +/* + * Return size of TIFFDataType in bytes. + * + * XXX: We need a separate function to determine the space needed + * to store the value. For TIFF_RATIONAL values TIFFDataWidth() returns 8, + * but we use 4-byte float to represent rationals. + */ +int +_TIFFDataSize(TIFFDataType type) +{ + switch (type) + { + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_ASCII: + case TIFF_UNDEFINED: + return 1; + case TIFF_SHORT: + case TIFF_SSHORT: + return 2; + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_FLOAT: + case TIFF_IFD: + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + return 4; + case TIFF_DOUBLE: + case TIFF_LONG8: + case TIFF_SLONG8: + case TIFF_IFD8: + return 8; + default: + return 0; + } +} + +const TIFFField* +TIFFFindField(TIFF* tif, uint32 tag, TIFFDataType dt) +{ + TIFFField key = {0, 0, 0, TIFF_NOTYPE, 0, 0, 0, 0, 0, 0, NULL, NULL}; + TIFFField* pkey = &key; + const TIFFField **ret; + if (tif->tif_foundfield && tif->tif_foundfield->field_tag == tag && + (dt == TIFF_ANY || dt == tif->tif_foundfield->field_type)) + return tif->tif_foundfield; + + /* If we are invoked with no field information, then just return. */ + if (!tif->tif_fields) + return NULL; + + /* NB: use sorted search (e.g. binary search) */ + + key.field_tag = tag; + key.field_type = dt; + + ret = (const TIFFField **) bsearch(&pkey, tif->tif_fields, + tif->tif_nfields, + sizeof(TIFFField *), tagCompare); + return tif->tif_foundfield = (ret ? *ret : NULL); +} + +const TIFFField* +_TIFFFindFieldByName(TIFF* tif, const char *field_name, TIFFDataType dt) +{ + TIFFField key = {0, 0, 0, TIFF_NOTYPE, 0, 0, 0, 0, 0, 0, NULL, NULL}; + TIFFField* pkey = &key; + const TIFFField **ret; + if (tif->tif_foundfield + && streq(tif->tif_foundfield->field_name, field_name) + && (dt == TIFF_ANY || dt == tif->tif_foundfield->field_type)) + return (tif->tif_foundfield); + + /* If we are invoked with no field information, then just return. */ + if (!tif->tif_fields) + return NULL; + + /* NB: use linear search since list is sorted by key#, not name */ + + key.field_name = (char *)field_name; + key.field_type = dt; + + ret = (const TIFFField **) + td_lfind(&pkey, tif->tif_fields, &tif->tif_nfields, + sizeof(TIFFField *), tagNameCompare); + + return tif->tif_foundfield = (ret ? *ret : NULL); +} + +const TIFFField* +TIFFFieldWithTag(TIFF* tif, uint32 tag) +{ + const TIFFField* fip = TIFFFindField(tif, tag, TIFF_ANY); + if (!fip) { + TIFFErrorExt(tif->tif_clientdata, "TIFFFieldWithTag", + "Internal error, unknown tag 0x%x", + (unsigned int) tag); + } + return (fip); +} + +const TIFFField* +TIFFFieldWithName(TIFF* tif, const char *field_name) +{ + const TIFFField* fip = + _TIFFFindFieldByName(tif, field_name, TIFF_ANY); + if (!fip) { + TIFFErrorExt(tif->tif_clientdata, "TIFFFieldWithName", + "Internal error, unknown tag %s", field_name); + } + return (fip); +} + +uint32 +TIFFFieldTag(const TIFFField* fip) +{ + return fip->field_tag; +} + +const char * +TIFFFieldName(const TIFFField* fip) +{ + return fip->field_name; +} + +TIFFDataType +TIFFFieldDataType(const TIFFField* fip) +{ + return fip->field_type; +} + +int +TIFFFieldPassCount(const TIFFField* fip) +{ + return fip->field_passcount; +} + +int +TIFFFieldReadCount(const TIFFField* fip) +{ + return fip->field_readcount; +} + +int +TIFFFieldWriteCount(const TIFFField* fip) +{ + return fip->field_writecount; +} + +const TIFFField* +_TIFFFindOrRegisterField(TIFF *tif, uint32 tag, TIFFDataType dt) + +{ + const TIFFField *fld; + + fld = TIFFFindField(tif, tag, dt); + if (fld == NULL) { + fld = _TIFFCreateAnonField(tif, tag, dt); + if (!_TIFFMergeFields(tif, fld, 1)) + return NULL; + } + + return fld; +} + +TIFFField* +_TIFFCreateAnonField(TIFF *tif, uint32 tag, TIFFDataType field_type) +{ + TIFFField *fld; + (void) tif; + + fld = (TIFFField *) _TIFFmalloc(sizeof (TIFFField)); + if (fld == NULL) + return NULL; + _TIFFmemset(fld, 0, sizeof(TIFFField)); + + fld->field_tag = tag; + fld->field_readcount = TIFF_VARIABLE2; + fld->field_writecount = TIFF_VARIABLE2; + fld->field_type = field_type; + fld->reserved = 0; + switch (field_type) + { + case TIFF_BYTE: + case TIFF_UNDEFINED: + fld->set_field_type = TIFF_SETGET_C32_UINT8; + fld->get_field_type = TIFF_SETGET_C32_UINT8; + break; + case TIFF_ASCII: + fld->set_field_type = TIFF_SETGET_C32_ASCII; + fld->get_field_type = TIFF_SETGET_C32_ASCII; + break; + case TIFF_SHORT: + fld->set_field_type = TIFF_SETGET_C32_UINT16; + fld->get_field_type = TIFF_SETGET_C32_UINT16; + break; + case TIFF_LONG: + fld->set_field_type = TIFF_SETGET_C32_UINT32; + fld->get_field_type = TIFF_SETGET_C32_UINT32; + break; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_FLOAT: + fld->set_field_type = TIFF_SETGET_C32_FLOAT; + fld->get_field_type = TIFF_SETGET_C32_FLOAT; + break; + case TIFF_SBYTE: + fld->set_field_type = TIFF_SETGET_C32_SINT8; + fld->get_field_type = TIFF_SETGET_C32_SINT8; + break; + case TIFF_SSHORT: + fld->set_field_type = TIFF_SETGET_C32_SINT16; + fld->get_field_type = TIFF_SETGET_C32_SINT16; + break; + case TIFF_SLONG: + fld->set_field_type = TIFF_SETGET_C32_SINT32; + fld->get_field_type = TIFF_SETGET_C32_SINT32; + break; + case TIFF_DOUBLE: + fld->set_field_type = TIFF_SETGET_C32_DOUBLE; + fld->get_field_type = TIFF_SETGET_C32_DOUBLE; + break; + case TIFF_IFD: + case TIFF_IFD8: + fld->set_field_type = TIFF_SETGET_C32_IFD8; + fld->get_field_type = TIFF_SETGET_C32_IFD8; + break; + case TIFF_LONG8: + fld->set_field_type = TIFF_SETGET_C32_UINT64; + fld->get_field_type = TIFF_SETGET_C32_UINT64; + break; + case TIFF_SLONG8: + fld->set_field_type = TIFF_SETGET_C32_SINT64; + fld->get_field_type = TIFF_SETGET_C32_SINT64; + break; + default: + fld->set_field_type = TIFF_SETGET_UNDEFINED; + fld->get_field_type = TIFF_SETGET_UNDEFINED; + break; + } + fld->field_bit = FIELD_CUSTOM; + fld->field_oktochange = TRUE; + fld->field_passcount = TRUE; + fld->field_name = (char *) _TIFFmalloc(32); + if (fld->field_name == NULL) { + _TIFFfree(fld); + return NULL; + } + fld->field_subfields = NULL; + + /* + * note that this name is a special sign to TIFFClose() and + * _TIFFSetupFields() to free the field + */ + snprintf(fld->field_name, 32, "Tag %d", (int) tag); + + return fld; +} + +/**************************************************************************** + * O B S O L E T E D I N T E R F A C E S + * + * Don't use this stuff in your applications, it may be removed in the future + * libtiff versions. + ****************************************************************************/ + +static TIFFSetGetFieldType +_TIFFSetGetType(TIFFDataType type, short count, unsigned char passcount) +{ + if (type == TIFF_ASCII && count == TIFF_VARIABLE && passcount == 0) + return TIFF_SETGET_ASCII; + + else if (count == 1 && passcount == 0) { + switch (type) + { + case TIFF_BYTE: + case TIFF_UNDEFINED: + return TIFF_SETGET_UINT8; + case TIFF_ASCII: + return TIFF_SETGET_ASCII; + case TIFF_SHORT: + return TIFF_SETGET_UINT16; + case TIFF_LONG: + return TIFF_SETGET_UINT32; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_FLOAT: + return TIFF_SETGET_FLOAT; + case TIFF_SBYTE: + return TIFF_SETGET_SINT8; + case TIFF_SSHORT: + return TIFF_SETGET_SINT16; + case TIFF_SLONG: + return TIFF_SETGET_SINT32; + case TIFF_DOUBLE: + return TIFF_SETGET_DOUBLE; + case TIFF_IFD: + case TIFF_IFD8: + return TIFF_SETGET_IFD8; + case TIFF_LONG8: + return TIFF_SETGET_UINT64; + case TIFF_SLONG8: + return TIFF_SETGET_SINT64; + default: + return TIFF_SETGET_UNDEFINED; + } + } + + else if (count >= 1 && passcount == 0) { + switch (type) + { + case TIFF_BYTE: + case TIFF_UNDEFINED: + return TIFF_SETGET_C0_UINT8; + case TIFF_ASCII: + return TIFF_SETGET_C0_ASCII; + case TIFF_SHORT: + return TIFF_SETGET_C0_UINT16; + case TIFF_LONG: + return TIFF_SETGET_C0_UINT32; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_FLOAT: + return TIFF_SETGET_C0_FLOAT; + case TIFF_SBYTE: + return TIFF_SETGET_C0_SINT8; + case TIFF_SSHORT: + return TIFF_SETGET_C0_SINT16; + case TIFF_SLONG: + return TIFF_SETGET_C0_SINT32; + case TIFF_DOUBLE: + return TIFF_SETGET_C0_DOUBLE; + case TIFF_IFD: + case TIFF_IFD8: + return TIFF_SETGET_C0_IFD8; + case TIFF_LONG8: + return TIFF_SETGET_C0_UINT64; + case TIFF_SLONG8: + return TIFF_SETGET_C0_SINT64; + default: + return TIFF_SETGET_UNDEFINED; + } + } + + else if (count == TIFF_VARIABLE && passcount == 1) { + switch (type) + { + case TIFF_BYTE: + case TIFF_UNDEFINED: + return TIFF_SETGET_C16_UINT8; + case TIFF_ASCII: + return TIFF_SETGET_C16_ASCII; + case TIFF_SHORT: + return TIFF_SETGET_C16_UINT16; + case TIFF_LONG: + return TIFF_SETGET_C16_UINT32; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_FLOAT: + return TIFF_SETGET_C16_FLOAT; + case TIFF_SBYTE: + return TIFF_SETGET_C16_SINT8; + case TIFF_SSHORT: + return TIFF_SETGET_C16_SINT16; + case TIFF_SLONG: + return TIFF_SETGET_C16_SINT32; + case TIFF_DOUBLE: + return TIFF_SETGET_C16_DOUBLE; + case TIFF_IFD: + case TIFF_IFD8: + return TIFF_SETGET_C16_IFD8; + case TIFF_LONG8: + return TIFF_SETGET_C16_UINT64; + case TIFF_SLONG8: + return TIFF_SETGET_C16_SINT64; + default: + return TIFF_SETGET_UNDEFINED; + } + } + + else if (count == TIFF_VARIABLE2 && passcount == 1) { + switch (type) + { + case TIFF_BYTE: + case TIFF_UNDEFINED: + return TIFF_SETGET_C32_UINT8; + case TIFF_ASCII: + return TIFF_SETGET_C32_ASCII; + case TIFF_SHORT: + return TIFF_SETGET_C32_UINT16; + case TIFF_LONG: + return TIFF_SETGET_C32_UINT32; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_FLOAT: + return TIFF_SETGET_C32_FLOAT; + case TIFF_SBYTE: + return TIFF_SETGET_C32_SINT8; + case TIFF_SSHORT: + return TIFF_SETGET_C32_SINT16; + case TIFF_SLONG: + return TIFF_SETGET_C32_SINT32; + case TIFF_DOUBLE: + return TIFF_SETGET_C32_DOUBLE; + case TIFF_IFD: + case TIFF_IFD8: + return TIFF_SETGET_C32_IFD8; + case TIFF_LONG8: + return TIFF_SETGET_C32_UINT64; + case TIFF_SLONG8: + return TIFF_SETGET_C32_SINT64; + default: + return TIFF_SETGET_UNDEFINED; + } + } + + return TIFF_SETGET_UNDEFINED; +} + +int +TIFFMergeFieldInfo(TIFF* tif, const TIFFFieldInfo info[], uint32 n) +{ + static const char module[] = "TIFFMergeFieldInfo"; + static const char reason[] = "for fields array"; + TIFFField *tp; + size_t nfields; + uint32 i; + + if (tif->tif_nfieldscompat > 0) { + tif->tif_fieldscompat = (TIFFFieldArray *) + _TIFFCheckRealloc(tif, tif->tif_fieldscompat, + tif->tif_nfieldscompat + 1, + sizeof(TIFFFieldArray), reason); + } else { + tif->tif_fieldscompat = (TIFFFieldArray *) + _TIFFCheckMalloc(tif, 1, sizeof(TIFFFieldArray), + reason); + } + if (!tif->tif_fieldscompat) { + TIFFErrorExt(tif->tif_clientdata, module, + "Failed to allocate fields array"); + return -1; + } + nfields = tif->tif_nfieldscompat++; + + tif->tif_fieldscompat[nfields].type = tfiatOther; + tif->tif_fieldscompat[nfields].allocated_size = n; + tif->tif_fieldscompat[nfields].count = n; + tif->tif_fieldscompat[nfields].fields = + (TIFFField *)_TIFFCheckMalloc(tif, n, sizeof(TIFFField), + reason); + if (!tif->tif_fieldscompat[nfields].fields) { + TIFFErrorExt(tif->tif_clientdata, module, + "Failed to allocate fields array"); + return -1; + } + + tp = tif->tif_fieldscompat[nfields].fields; + for (i = 0; i < n; i++) { + tp->field_tag = info[i].field_tag; + tp->field_readcount = info[i].field_readcount; + tp->field_writecount = info[i].field_writecount; + tp->field_type = info[i].field_type; + tp->reserved = 0; + tp->set_field_type = + _TIFFSetGetType(info[i].field_type, + info[i].field_readcount, + info[i].field_passcount); + tp->get_field_type = + _TIFFSetGetType(info[i].field_type, + info[i].field_readcount, + info[i].field_passcount); + tp->field_bit = info[i].field_bit; + tp->field_oktochange = info[i].field_oktochange; + tp->field_passcount = info[i].field_passcount; + tp->field_name = info[i].field_name; + tp->field_subfields = NULL; + tp++; + } + + if (!_TIFFMergeFields(tif, tif->tif_fieldscompat[nfields].fields, n)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Setting up field info failed"); + return -1; + } + + return 0; +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dirread.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dirread.c new file mode 100644 index 000000000..7835a7c3b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dirread.c @@ -0,0 +1,5640 @@ +/* $Id: tif_dirread.c,v 1.183 2015-01-03 18:03:40 erouault Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Directory Read Support Routines. + */ + +/* Suggested pending improvements: + * - add a field 'ignore' to the TIFFDirEntry structure, to flag status, + * eliminating current use of the IGNORE value, and therefore eliminating + * current irrational behaviour on tags with tag id code 0 + * - add a field 'field_info' to the TIFFDirEntry structure, and set that with + * the pointer to the appropriate TIFFField structure early on in + * TIFFReadDirectory, so as to eliminate current possibly repetitive lookup. + */ + +#include "tiffiop.h" + +#define IGNORE 0 /* tag placeholder used below */ +#define FAILED_FII ((uint32) -1) + +#ifdef HAVE_IEEEFP +# define TIFFCvtIEEEFloatToNative(tif, n, fp) +# define TIFFCvtIEEEDoubleToNative(tif, n, dp) +#else +extern void TIFFCvtIEEEFloatToNative(TIFF*, uint32, float*); +extern void TIFFCvtIEEEDoubleToNative(TIFF*, uint32, double*); +#endif + +enum TIFFReadDirEntryErr { + TIFFReadDirEntryErrOk = 0, + TIFFReadDirEntryErrCount = 1, + TIFFReadDirEntryErrType = 2, + TIFFReadDirEntryErrIo = 3, + TIFFReadDirEntryErrRange = 4, + TIFFReadDirEntryErrPsdif = 5, + TIFFReadDirEntryErrSizesan = 6, + TIFFReadDirEntryErrAlloc = 7, +}; + +static enum TIFFReadDirEntryErr TIFFReadDirEntryByte(TIFF* tif, TIFFDirEntry* direntry, uint8* value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryShort(TIFF* tif, TIFFDirEntry* direntry, uint16* value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryLong(TIFF* tif, TIFFDirEntry* direntry, uint32* value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryLong8(TIFF* tif, TIFFDirEntry* direntry, uint64* value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryFloat(TIFF* tif, TIFFDirEntry* direntry, float* value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryDouble(TIFF* tif, TIFFDirEntry* direntry, double* value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryIfd8(TIFF* tif, TIFFDirEntry* direntry, uint64* value); + +static enum TIFFReadDirEntryErr TIFFReadDirEntryArray(TIFF* tif, TIFFDirEntry* direntry, uint32* count, uint32 desttypesize, void** value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryByteArray(TIFF* tif, TIFFDirEntry* direntry, uint8** value); +static enum TIFFReadDirEntryErr TIFFReadDirEntrySbyteArray(TIFF* tif, TIFFDirEntry* direntry, int8** value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryShortArray(TIFF* tif, TIFFDirEntry* direntry, uint16** value); +static enum TIFFReadDirEntryErr TIFFReadDirEntrySshortArray(TIFF* tif, TIFFDirEntry* direntry, int16** value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryLongArray(TIFF* tif, TIFFDirEntry* direntry, uint32** value); +static enum TIFFReadDirEntryErr TIFFReadDirEntrySlongArray(TIFF* tif, TIFFDirEntry* direntry, int32** value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryLong8Array(TIFF* tif, TIFFDirEntry* direntry, uint64** value); +static enum TIFFReadDirEntryErr TIFFReadDirEntrySlong8Array(TIFF* tif, TIFFDirEntry* direntry, int64** value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryFloatArray(TIFF* tif, TIFFDirEntry* direntry, float** value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryDoubleArray(TIFF* tif, TIFFDirEntry* direntry, double** value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryIfd8Array(TIFF* tif, TIFFDirEntry* direntry, uint64** value); + +static enum TIFFReadDirEntryErr TIFFReadDirEntryPersampleShort(TIFF* tif, TIFFDirEntry* direntry, uint16* value); +#if 0 +static enum TIFFReadDirEntryErr TIFFReadDirEntryPersampleDouble(TIFF* tif, TIFFDirEntry* direntry, double* value); +#endif + +static void TIFFReadDirEntryCheckedByte(TIFF* tif, TIFFDirEntry* direntry, uint8* value); +static void TIFFReadDirEntryCheckedSbyte(TIFF* tif, TIFFDirEntry* direntry, int8* value); +static void TIFFReadDirEntryCheckedShort(TIFF* tif, TIFFDirEntry* direntry, uint16* value); +static void TIFFReadDirEntryCheckedSshort(TIFF* tif, TIFFDirEntry* direntry, int16* value); +static void TIFFReadDirEntryCheckedLong(TIFF* tif, TIFFDirEntry* direntry, uint32* value); +static void TIFFReadDirEntryCheckedSlong(TIFF* tif, TIFFDirEntry* direntry, int32* value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedLong8(TIFF* tif, TIFFDirEntry* direntry, uint64* value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedSlong8(TIFF* tif, TIFFDirEntry* direntry, int64* value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedRational(TIFF* tif, TIFFDirEntry* direntry, double* value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedSrational(TIFF* tif, TIFFDirEntry* direntry, double* value); +static void TIFFReadDirEntryCheckedFloat(TIFF* tif, TIFFDirEntry* direntry, float* value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedDouble(TIFF* tif, TIFFDirEntry* direntry, double* value); + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSbyte(int8 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteShort(uint16 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSshort(int16 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteLong(uint32 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSlong(int32 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteLong8(uint64 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSlong8(int64 value); + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteByte(uint8 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteShort(uint16 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteSshort(int16 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteLong(uint32 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteSlong(int32 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteLong8(uint64 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteSlong8(int64 value); + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSbyte(int8 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSshort(int16 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortLong(uint32 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSlong(int32 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortLong8(uint64 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSlong8(int64 value); + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortShort(uint16 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortLong(uint32 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortSlong(int32 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortLong8(uint64 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortSlong8(int64 value); + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSbyte(int8 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSshort(int16 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSlong(int32 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongLong8(uint64 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSlong8(int64 value); + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSlongLong(uint32 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSlongLong8(uint64 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSlongSlong8(int64 value); + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLong8Sbyte(int8 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLong8Sshort(int16 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLong8Slong(int32 value); +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLong8Slong8(int64 value); + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSlong8Long8(uint64 value); + +static enum TIFFReadDirEntryErr TIFFReadDirEntryData(TIFF* tif, uint64 offset, tmsize_t size, void* dest); +static void TIFFReadDirEntryOutputErr(TIFF* tif, enum TIFFReadDirEntryErr err, const char* module, const char* tagname, int recover); + +static void TIFFReadDirectoryCheckOrder(TIFF* tif, TIFFDirEntry* dir, uint16 dircount); +static TIFFDirEntry* TIFFReadDirectoryFindEntry(TIFF* tif, TIFFDirEntry* dir, uint16 dircount, uint16 tagid); +static void TIFFReadDirectoryFindFieldInfo(TIFF* tif, uint16 tagid, uint32* fii); + +static int EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16 dircount); +static void MissingRequired(TIFF*, const char*); +static int TIFFCheckDirOffset(TIFF* tif, uint64 diroff); +static int CheckDirCount(TIFF*, TIFFDirEntry*, uint32); +static uint16 TIFFFetchDirectory(TIFF* tif, uint64 diroff, TIFFDirEntry** pdir, uint64* nextdiroff); +static int TIFFFetchNormalTag(TIFF*, TIFFDirEntry*, int recover); +static int TIFFFetchStripThing(TIFF* tif, TIFFDirEntry* dir, uint32 nstrips, uint64** lpp); +static int TIFFFetchSubjectDistance(TIFF*, TIFFDirEntry*); +static void ChopUpSingleUncompressedStrip(TIFF*); +static uint64 TIFFReadUInt64(const uint8 *value); + +typedef union _UInt64Aligned_t +{ + double d; + uint64 l; + uint32 i[2]; + uint16 s[4]; + uint8 c[8]; +} UInt64Aligned_t; + +/* + Unaligned safe copy of a uint64 value from an octet array. +*/ +static uint64 TIFFReadUInt64(const uint8 *value) +{ + UInt64Aligned_t result; + + result.c[0]=value[0]; + result.c[1]=value[1]; + result.c[2]=value[2]; + result.c[3]=value[3]; + result.c[4]=value[4]; + result.c[5]=value[5]; + result.c[6]=value[6]; + result.c[7]=value[7]; + + return result.l; +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryByte(TIFF* tif, TIFFDirEntry* direntry, uint8* value) +{ + enum TIFFReadDirEntryErr err; + if (direntry->tdir_count!=1) + return(TIFFReadDirEntryErrCount); + switch (direntry->tdir_type) + { + case TIFF_BYTE: + TIFFReadDirEntryCheckedByte(tif,direntry,value); + return(TIFFReadDirEntryErrOk); + case TIFF_SBYTE: + { + int8 m; + TIFFReadDirEntryCheckedSbyte(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeByteSbyte(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint8)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SHORT: + { + uint16 m; + TIFFReadDirEntryCheckedShort(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeByteShort(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint8)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SSHORT: + { + int16 m; + TIFFReadDirEntryCheckedSshort(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeByteSshort(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint8)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG: + { + uint32 m; + TIFFReadDirEntryCheckedLong(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeByteLong(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint8)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SLONG: + { + int32 m; + TIFFReadDirEntryCheckedSlong(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeByteSlong(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint8)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG8: + { + uint64 m; + err=TIFFReadDirEntryCheckedLong8(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + err=TIFFReadDirEntryCheckRangeByteLong8(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint8)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SLONG8: + { + int64 m; + err=TIFFReadDirEntryCheckedSlong8(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + err=TIFFReadDirEntryCheckRangeByteSlong8(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint8)m; + return(TIFFReadDirEntryErrOk); + } + default: + return(TIFFReadDirEntryErrType); + } +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryShort(TIFF* tif, TIFFDirEntry* direntry, uint16* value) +{ + enum TIFFReadDirEntryErr err; + if (direntry->tdir_count!=1) + return(TIFFReadDirEntryErrCount); + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8 m; + TIFFReadDirEntryCheckedByte(tif,direntry,&m); + *value=(uint16)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SBYTE: + { + int8 m; + TIFFReadDirEntryCheckedSbyte(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeShortSbyte(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint16)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SHORT: + TIFFReadDirEntryCheckedShort(tif,direntry,value); + return(TIFFReadDirEntryErrOk); + case TIFF_SSHORT: + { + int16 m; + TIFFReadDirEntryCheckedSshort(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeShortSshort(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint16)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG: + { + uint32 m; + TIFFReadDirEntryCheckedLong(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeShortLong(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint16)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SLONG: + { + int32 m; + TIFFReadDirEntryCheckedSlong(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeShortSlong(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint16)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG8: + { + uint64 m; + err=TIFFReadDirEntryCheckedLong8(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + err=TIFFReadDirEntryCheckRangeShortLong8(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint16)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SLONG8: + { + int64 m; + err=TIFFReadDirEntryCheckedSlong8(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + err=TIFFReadDirEntryCheckRangeShortSlong8(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint16)m; + return(TIFFReadDirEntryErrOk); + } + default: + return(TIFFReadDirEntryErrType); + } +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryLong(TIFF* tif, TIFFDirEntry* direntry, uint32* value) +{ + enum TIFFReadDirEntryErr err; + if (direntry->tdir_count!=1) + return(TIFFReadDirEntryErrCount); + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8 m; + TIFFReadDirEntryCheckedByte(tif,direntry,&m); + *value=(uint32)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SBYTE: + { + int8 m; + TIFFReadDirEntryCheckedSbyte(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeLongSbyte(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint32)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SHORT: + { + uint16 m; + TIFFReadDirEntryCheckedShort(tif,direntry,&m); + *value=(uint32)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SSHORT: + { + int16 m; + TIFFReadDirEntryCheckedSshort(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeLongSshort(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint32)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG: + TIFFReadDirEntryCheckedLong(tif,direntry,value); + return(TIFFReadDirEntryErrOk); + case TIFF_SLONG: + { + int32 m; + TIFFReadDirEntryCheckedSlong(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeLongSlong(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint32)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG8: + { + uint64 m; + err=TIFFReadDirEntryCheckedLong8(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + err=TIFFReadDirEntryCheckRangeLongLong8(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint32)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SLONG8: + { + int64 m; + err=TIFFReadDirEntryCheckedSlong8(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + err=TIFFReadDirEntryCheckRangeLongSlong8(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint32)m; + return(TIFFReadDirEntryErrOk); + } + default: + return(TIFFReadDirEntryErrType); + } +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryLong8(TIFF* tif, TIFFDirEntry* direntry, uint64* value) +{ + enum TIFFReadDirEntryErr err; + if (direntry->tdir_count!=1) + return(TIFFReadDirEntryErrCount); + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8 m; + TIFFReadDirEntryCheckedByte(tif,direntry,&m); + *value=(uint64)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SBYTE: + { + int8 m; + TIFFReadDirEntryCheckedSbyte(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeLong8Sbyte(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint64)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SHORT: + { + uint16 m; + TIFFReadDirEntryCheckedShort(tif,direntry,&m); + *value=(uint64)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SSHORT: + { + int16 m; + TIFFReadDirEntryCheckedSshort(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeLong8Sshort(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint64)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG: + { + uint32 m; + TIFFReadDirEntryCheckedLong(tif,direntry,&m); + *value=(uint64)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SLONG: + { + int32 m; + TIFFReadDirEntryCheckedSlong(tif,direntry,&m); + err=TIFFReadDirEntryCheckRangeLong8Slong(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint64)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG8: + err=TIFFReadDirEntryCheckedLong8(tif,direntry,value); + return(err); + case TIFF_SLONG8: + { + int64 m; + err=TIFFReadDirEntryCheckedSlong8(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + err=TIFFReadDirEntryCheckRangeLong8Slong8(m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(uint64)m; + return(TIFFReadDirEntryErrOk); + } + default: + return(TIFFReadDirEntryErrType); + } +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryFloat(TIFF* tif, TIFFDirEntry* direntry, float* value) +{ + enum TIFFReadDirEntryErr err; + if (direntry->tdir_count!=1) + return(TIFFReadDirEntryErrCount); + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8 m; + TIFFReadDirEntryCheckedByte(tif,direntry,&m); + *value=(float)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SBYTE: + { + int8 m; + TIFFReadDirEntryCheckedSbyte(tif,direntry,&m); + *value=(float)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SHORT: + { + uint16 m; + TIFFReadDirEntryCheckedShort(tif,direntry,&m); + *value=(float)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SSHORT: + { + int16 m; + TIFFReadDirEntryCheckedSshort(tif,direntry,&m); + *value=(float)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG: + { + uint32 m; + TIFFReadDirEntryCheckedLong(tif,direntry,&m); + *value=(float)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SLONG: + { + int32 m; + TIFFReadDirEntryCheckedSlong(tif,direntry,&m); + *value=(float)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG8: + { + uint64 m; + err=TIFFReadDirEntryCheckedLong8(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); +#if defined(__WIN32__) && (_MSC_VER < 1500) + /* + * XXX: MSVC 6.0 does not support conversion + * of 64-bit integers into floating point + * values. + */ + *value = _TIFFUInt64ToFloat(m); +#else + *value=(float)m; +#endif + return(TIFFReadDirEntryErrOk); + } + case TIFF_SLONG8: + { + int64 m; + err=TIFFReadDirEntryCheckedSlong8(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(float)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_RATIONAL: + { + double m; + err=TIFFReadDirEntryCheckedRational(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(float)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SRATIONAL: + { + double m; + err=TIFFReadDirEntryCheckedSrational(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(float)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_FLOAT: + TIFFReadDirEntryCheckedFloat(tif,direntry,value); + return(TIFFReadDirEntryErrOk); + case TIFF_DOUBLE: + { + double m; + err=TIFFReadDirEntryCheckedDouble(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(float)m; + return(TIFFReadDirEntryErrOk); + } + default: + return(TIFFReadDirEntryErrType); + } +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryDouble(TIFF* tif, TIFFDirEntry* direntry, double* value) +{ + enum TIFFReadDirEntryErr err; + if (direntry->tdir_count!=1) + return(TIFFReadDirEntryErrCount); + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8 m; + TIFFReadDirEntryCheckedByte(tif,direntry,&m); + *value=(double)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SBYTE: + { + int8 m; + TIFFReadDirEntryCheckedSbyte(tif,direntry,&m); + *value=(double)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SHORT: + { + uint16 m; + TIFFReadDirEntryCheckedShort(tif,direntry,&m); + *value=(double)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SSHORT: + { + int16 m; + TIFFReadDirEntryCheckedSshort(tif,direntry,&m); + *value=(double)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG: + { + uint32 m; + TIFFReadDirEntryCheckedLong(tif,direntry,&m); + *value=(double)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SLONG: + { + int32 m; + TIFFReadDirEntryCheckedSlong(tif,direntry,&m); + *value=(double)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG8: + { + uint64 m; + err=TIFFReadDirEntryCheckedLong8(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); +#if defined(__WIN32__) && (_MSC_VER < 1500) + /* + * XXX: MSVC 6.0 does not support conversion + * of 64-bit integers into floating point + * values. + */ + *value = _TIFFUInt64ToDouble(m); +#else + *value = (double)m; +#endif + return(TIFFReadDirEntryErrOk); + } + case TIFF_SLONG8: + { + int64 m; + err=TIFFReadDirEntryCheckedSlong8(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + *value=(double)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_RATIONAL: + err=TIFFReadDirEntryCheckedRational(tif,direntry,value); + return(err); + case TIFF_SRATIONAL: + err=TIFFReadDirEntryCheckedSrational(tif,direntry,value); + return(err); + case TIFF_FLOAT: + { + float m; + TIFFReadDirEntryCheckedFloat(tif,direntry,&m); + *value=(double)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_DOUBLE: + err=TIFFReadDirEntryCheckedDouble(tif,direntry,value); + return(err); + default: + return(TIFFReadDirEntryErrType); + } +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryIfd8(TIFF* tif, TIFFDirEntry* direntry, uint64* value) +{ + enum TIFFReadDirEntryErr err; + if (direntry->tdir_count!=1) + return(TIFFReadDirEntryErrCount); + switch (direntry->tdir_type) + { + case TIFF_LONG: + case TIFF_IFD: + { + uint32 m; + TIFFReadDirEntryCheckedLong(tif,direntry,&m); + *value=(uint64)m; + return(TIFFReadDirEntryErrOk); + } + case TIFF_LONG8: + case TIFF_IFD8: + err=TIFFReadDirEntryCheckedLong8(tif,direntry,value); + return(err); + default: + return(TIFFReadDirEntryErrType); + } +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryArray(TIFF* tif, TIFFDirEntry* direntry, uint32* count, uint32 desttypesize, void** value) +{ + int typesize; + uint32 datasize; + void* data; + typesize=TIFFDataWidth(direntry->tdir_type); + if ((direntry->tdir_count==0)||(typesize==0)) + { + *value=0; + return(TIFFReadDirEntryErrOk); + } + (void) desttypesize; + + /* + * As a sanity check, make sure we have no more than a 2GB tag array + * in either the current data type or the dest data type. This also + * avoids problems with overflow of tmsize_t on 32bit systems. + */ + if ((uint64)(2147483647/typesize)tdir_count) + return(TIFFReadDirEntryErrSizesan); + if ((uint64)(2147483647/desttypesize)tdir_count) + return(TIFFReadDirEntryErrSizesan); + + *count=(uint32)direntry->tdir_count; + datasize=(*count)*typesize; + assert((tmsize_t)datasize>0); + data=_TIFFCheckMalloc(tif, *count, typesize, "ReadDirEntryArray"); + if (data==0) + return(TIFFReadDirEntryErrAlloc); + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + if (datasize<=4) + _TIFFmemcpy(data,&direntry->tdir_offset,datasize); + else + { + enum TIFFReadDirEntryErr err; + uint32 offset = direntry->tdir_offset.toff_long; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&offset); + err=TIFFReadDirEntryData(tif,(uint64)offset,(tmsize_t)datasize,data); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(data); + return(err); + } + } + } + else + { + if (datasize<=8) + _TIFFmemcpy(data,&direntry->tdir_offset,datasize); + else + { + enum TIFFReadDirEntryErr err; + uint64 offset = direntry->tdir_offset.toff_long8; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8(&offset); + err=TIFFReadDirEntryData(tif,offset,(tmsize_t)datasize,data); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(data); + return(err); + } + } + } + *value=data; + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryByteArray(TIFF* tif, TIFFDirEntry* direntry, uint8** value) +{ + enum TIFFReadDirEntryErr err; + uint32 count; + void* origdata; + uint8* data; + switch (direntry->tdir_type) + { + case TIFF_ASCII: + case TIFF_UNDEFINED: + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_SHORT: + case TIFF_SSHORT: + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_LONG8: + case TIFF_SLONG8: + break; + default: + return(TIFFReadDirEntryErrType); + } + err=TIFFReadDirEntryArray(tif,direntry,&count,1,&origdata); + if ((err!=TIFFReadDirEntryErrOk)||(origdata==0)) + { + *value=0; + return(err); + } + switch (direntry->tdir_type) + { + case TIFF_ASCII: + case TIFF_UNDEFINED: + case TIFF_BYTE: + *value=(uint8*)origdata; + return(TIFFReadDirEntryErrOk); + case TIFF_SBYTE: + { + int8* m; + uint32 n; + m=(int8*)origdata; + for (n=0; ntdir_type) + { + case TIFF_SHORT: + { + uint16* ma; + uint8* mb; + uint32 n; + ma=(uint16*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort(ma); + err=TIFFReadDirEntryCheckRangeByteShort(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint8)(*ma++); + } + } + break; + case TIFF_SSHORT: + { + int16* ma; + uint8* mb; + uint32 n; + ma=(int16*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)ma); + err=TIFFReadDirEntryCheckRangeByteSshort(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint8)(*ma++); + } + } + break; + case TIFF_LONG: + { + uint32* ma; + uint8* mb; + uint32 n; + ma=(uint32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + err=TIFFReadDirEntryCheckRangeByteLong(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint8)(*ma++); + } + } + break; + case TIFF_SLONG: + { + int32* ma; + uint8* mb; + uint32 n; + ma=(int32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)ma); + err=TIFFReadDirEntryCheckRangeByteSlong(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint8)(*ma++); + } + } + break; + case TIFF_LONG8: + { + uint64* ma; + uint8* mb; + uint32 n; + ma=(uint64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8(ma); + err=TIFFReadDirEntryCheckRangeByteLong8(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint8)(*ma++); + } + } + break; + case TIFF_SLONG8: + { + int64* ma; + uint8* mb; + uint32 n; + ma=(int64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)ma); + err=TIFFReadDirEntryCheckRangeByteSlong8(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint8)(*ma++); + } + } + break; + } + _TIFFfree(origdata); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(data); + return(err); + } + *value=data; + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntrySbyteArray(TIFF* tif, TIFFDirEntry* direntry, int8** value) +{ + enum TIFFReadDirEntryErr err; + uint32 count; + void* origdata; + int8* data; + switch (direntry->tdir_type) + { + case TIFF_UNDEFINED: + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_SHORT: + case TIFF_SSHORT: + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_LONG8: + case TIFF_SLONG8: + break; + default: + return(TIFFReadDirEntryErrType); + } + err=TIFFReadDirEntryArray(tif,direntry,&count,1,&origdata); + if ((err!=TIFFReadDirEntryErrOk)||(origdata==0)) + { + *value=0; + return(err); + } + switch (direntry->tdir_type) + { + case TIFF_UNDEFINED: + case TIFF_BYTE: + { + uint8* m; + uint32 n; + m=(uint8*)origdata; + for (n=0; ntdir_type) + { + case TIFF_SHORT: + { + uint16* ma; + int8* mb; + uint32 n; + ma=(uint16*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort(ma); + err=TIFFReadDirEntryCheckRangeSbyteShort(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(int8)(*ma++); + } + } + break; + case TIFF_SSHORT: + { + int16* ma; + int8* mb; + uint32 n; + ma=(int16*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)ma); + err=TIFFReadDirEntryCheckRangeSbyteSshort(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(int8)(*ma++); + } + } + break; + case TIFF_LONG: + { + uint32* ma; + int8* mb; + uint32 n; + ma=(uint32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + err=TIFFReadDirEntryCheckRangeSbyteLong(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(int8)(*ma++); + } + } + break; + case TIFF_SLONG: + { + int32* ma; + int8* mb; + uint32 n; + ma=(int32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)ma); + err=TIFFReadDirEntryCheckRangeSbyteSlong(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(int8)(*ma++); + } + } + break; + case TIFF_LONG8: + { + uint64* ma; + int8* mb; + uint32 n; + ma=(uint64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8(ma); + err=TIFFReadDirEntryCheckRangeSbyteLong8(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(int8)(*ma++); + } + } + break; + case TIFF_SLONG8: + { + int64* ma; + int8* mb; + uint32 n; + ma=(int64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)ma); + err=TIFFReadDirEntryCheckRangeSbyteSlong8(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(int8)(*ma++); + } + } + break; + } + _TIFFfree(origdata); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(data); + return(err); + } + *value=data; + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryShortArray(TIFF* tif, TIFFDirEntry* direntry, uint16** value) +{ + enum TIFFReadDirEntryErr err; + uint32 count; + void* origdata; + uint16* data; + switch (direntry->tdir_type) + { + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_SHORT: + case TIFF_SSHORT: + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_LONG8: + case TIFF_SLONG8: + break; + default: + return(TIFFReadDirEntryErrType); + } + err=TIFFReadDirEntryArray(tif,direntry,&count,2,&origdata); + if ((err!=TIFFReadDirEntryErrOk)||(origdata==0)) + { + *value=0; + return(err); + } + switch (direntry->tdir_type) + { + case TIFF_SHORT: + *value=(uint16*)origdata; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfShort(*value,count); + return(TIFFReadDirEntryErrOk); + case TIFF_SSHORT: + { + int16* m; + uint32 n; + m=(int16*)origdata; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)m); + err=TIFFReadDirEntryCheckRangeShortSshort(*m); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(origdata); + return(err); + } + m++; + } + *value=(uint16*)origdata; + return(TIFFReadDirEntryErrOk); + } + } + data=(uint16*)_TIFFmalloc(count*2); + if (data==0) + { + _TIFFfree(origdata); + return(TIFFReadDirEntryErrAlloc); + } + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8* ma; + uint16* mb; + uint32 n; + ma=(uint8*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + err=TIFFReadDirEntryCheckRangeShortLong(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint16)(*ma++); + } + } + break; + case TIFF_SLONG: + { + int32* ma; + uint16* mb; + uint32 n; + ma=(int32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)ma); + err=TIFFReadDirEntryCheckRangeShortSlong(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint16)(*ma++); + } + } + break; + case TIFF_LONG8: + { + uint64* ma; + uint16* mb; + uint32 n; + ma=(uint64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8(ma); + err=TIFFReadDirEntryCheckRangeShortLong8(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint16)(*ma++); + } + } + break; + case TIFF_SLONG8: + { + int64* ma; + uint16* mb; + uint32 n; + ma=(int64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)ma); + err=TIFFReadDirEntryCheckRangeShortSlong8(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint16)(*ma++); + } + } + break; + } + _TIFFfree(origdata); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(data); + return(err); + } + *value=data; + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntrySshortArray(TIFF* tif, TIFFDirEntry* direntry, int16** value) +{ + enum TIFFReadDirEntryErr err; + uint32 count; + void* origdata; + int16* data; + switch (direntry->tdir_type) + { + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_SHORT: + case TIFF_SSHORT: + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_LONG8: + case TIFF_SLONG8: + break; + default: + return(TIFFReadDirEntryErrType); + } + err=TIFFReadDirEntryArray(tif,direntry,&count,2,&origdata); + if ((err!=TIFFReadDirEntryErrOk)||(origdata==0)) + { + *value=0; + return(err); + } + switch (direntry->tdir_type) + { + case TIFF_SHORT: + { + uint16* m; + uint32 n; + m=(uint16*)origdata; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort(m); + err=TIFFReadDirEntryCheckRangeSshortShort(*m); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(origdata); + return(err); + } + m++; + } + *value=(int16*)origdata; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SSHORT: + *value=(int16*)origdata; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfShort((uint16*)(*value),count); + return(TIFFReadDirEntryErrOk); + } + data=(int16*)_TIFFmalloc(count*2); + if (data==0) + { + _TIFFfree(origdata); + return(TIFFReadDirEntryErrAlloc); + } + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8* ma; + int16* mb; + uint32 n; + ma=(uint8*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + err=TIFFReadDirEntryCheckRangeSshortLong(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(int16)(*ma++); + } + } + break; + case TIFF_SLONG: + { + int32* ma; + int16* mb; + uint32 n; + ma=(int32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)ma); + err=TIFFReadDirEntryCheckRangeSshortSlong(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(int16)(*ma++); + } + } + break; + case TIFF_LONG8: + { + uint64* ma; + int16* mb; + uint32 n; + ma=(uint64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8(ma); + err=TIFFReadDirEntryCheckRangeSshortLong8(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(int16)(*ma++); + } + } + break; + case TIFF_SLONG8: + { + int64* ma; + int16* mb; + uint32 n; + ma=(int64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)ma); + err=TIFFReadDirEntryCheckRangeSshortSlong8(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(int16)(*ma++); + } + } + break; + } + _TIFFfree(origdata); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(data); + return(err); + } + *value=data; + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryLongArray(TIFF* tif, TIFFDirEntry* direntry, uint32** value) +{ + enum TIFFReadDirEntryErr err; + uint32 count; + void* origdata; + uint32* data; + switch (direntry->tdir_type) + { + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_SHORT: + case TIFF_SSHORT: + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_LONG8: + case TIFF_SLONG8: + break; + default: + return(TIFFReadDirEntryErrType); + } + err=TIFFReadDirEntryArray(tif,direntry,&count,4,&origdata); + if ((err!=TIFFReadDirEntryErrOk)||(origdata==0)) + { + *value=0; + return(err); + } + switch (direntry->tdir_type) + { + case TIFF_LONG: + *value=(uint32*)origdata; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong(*value,count); + return(TIFFReadDirEntryErrOk); + case TIFF_SLONG: + { + int32* m; + uint32 n; + m=(int32*)origdata; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)m); + err=TIFFReadDirEntryCheckRangeLongSlong(*m); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(origdata); + return(err); + } + m++; + } + *value=(uint32*)origdata; + return(TIFFReadDirEntryErrOk); + } + } + data=(uint32*)_TIFFmalloc(count*4); + if (data==0) + { + _TIFFfree(origdata); + return(TIFFReadDirEntryErrAlloc); + } + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8* ma; + uint32* mb; + uint32 n; + ma=(uint8*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort(ma); + *mb++=(uint32)(*ma++); + } + } + break; + case TIFF_SSHORT: + { + int16* ma; + uint32* mb; + uint32 n; + ma=(int16*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)ma); + err=TIFFReadDirEntryCheckRangeLongSshort(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint32)(*ma++); + } + } + break; + case TIFF_LONG8: + { + uint64* ma; + uint32* mb; + uint32 n; + ma=(uint64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8(ma); + err=TIFFReadDirEntryCheckRangeLongLong8(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint32)(*ma++); + } + } + break; + case TIFF_SLONG8: + { + int64* ma; + uint32* mb; + uint32 n; + ma=(int64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)ma); + err=TIFFReadDirEntryCheckRangeLongSlong8(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint32)(*ma++); + } + } + break; + } + _TIFFfree(origdata); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(data); + return(err); + } + *value=data; + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntrySlongArray(TIFF* tif, TIFFDirEntry* direntry, int32** value) +{ + enum TIFFReadDirEntryErr err; + uint32 count; + void* origdata; + int32* data; + switch (direntry->tdir_type) + { + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_SHORT: + case TIFF_SSHORT: + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_LONG8: + case TIFF_SLONG8: + break; + default: + return(TIFFReadDirEntryErrType); + } + err=TIFFReadDirEntryArray(tif,direntry,&count,4,&origdata); + if ((err!=TIFFReadDirEntryErrOk)||(origdata==0)) + { + *value=0; + return(err); + } + switch (direntry->tdir_type) + { + case TIFF_LONG: + { + uint32* m; + uint32 n; + m=(uint32*)origdata; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)m); + err=TIFFReadDirEntryCheckRangeSlongLong(*m); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(origdata); + return(err); + } + m++; + } + *value=(int32*)origdata; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SLONG: + *value=(int32*)origdata; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong((uint32*)(*value),count); + return(TIFFReadDirEntryErrOk); + } + data=(int32*)_TIFFmalloc(count*4); + if (data==0) + { + _TIFFfree(origdata); + return(TIFFReadDirEntryErrAlloc); + } + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8* ma; + int32* mb; + uint32 n; + ma=(uint8*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort(ma); + *mb++=(int32)(*ma++); + } + } + break; + case TIFF_SSHORT: + { + int16* ma; + int32* mb; + uint32 n; + ma=(int16*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)ma); + *mb++=(int32)(*ma++); + } + } + break; + case TIFF_LONG8: + { + uint64* ma; + int32* mb; + uint32 n; + ma=(uint64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8(ma); + err=TIFFReadDirEntryCheckRangeSlongLong8(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(int32)(*ma++); + } + } + break; + case TIFF_SLONG8: + { + int64* ma; + int32* mb; + uint32 n; + ma=(int64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)ma); + err=TIFFReadDirEntryCheckRangeSlongSlong8(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(int32)(*ma++); + } + } + break; + } + _TIFFfree(origdata); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(data); + return(err); + } + *value=data; + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryLong8Array(TIFF* tif, TIFFDirEntry* direntry, uint64** value) +{ + enum TIFFReadDirEntryErr err; + uint32 count; + void* origdata; + uint64* data; + switch (direntry->tdir_type) + { + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_SHORT: + case TIFF_SSHORT: + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_LONG8: + case TIFF_SLONG8: + break; + default: + return(TIFFReadDirEntryErrType); + } + err=TIFFReadDirEntryArray(tif,direntry,&count,8,&origdata); + if ((err!=TIFFReadDirEntryErrOk)||(origdata==0)) + { + *value=0; + return(err); + } + switch (direntry->tdir_type) + { + case TIFF_LONG8: + *value=(uint64*)origdata; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong8(*value,count); + return(TIFFReadDirEntryErrOk); + case TIFF_SLONG8: + { + int64* m; + uint32 n; + m=(int64*)origdata; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)m); + err=TIFFReadDirEntryCheckRangeLong8Slong8(*m); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(origdata); + return(err); + } + m++; + } + *value=(uint64*)origdata; + return(TIFFReadDirEntryErrOk); + } + } + data=(uint64*)_TIFFmalloc(count*8); + if (data==0) + { + _TIFFfree(origdata); + return(TIFFReadDirEntryErrAlloc); + } + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8* ma; + uint64* mb; + uint32 n; + ma=(uint8*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort(ma); + *mb++=(uint64)(*ma++); + } + } + break; + case TIFF_SSHORT: + { + int16* ma; + uint64* mb; + uint32 n; + ma=(int16*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)ma); + err=TIFFReadDirEntryCheckRangeLong8Sshort(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint64)(*ma++); + } + } + break; + case TIFF_LONG: + { + uint32* ma; + uint64* mb; + uint32 n; + ma=(uint32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + *mb++=(uint64)(*ma++); + } + } + break; + case TIFF_SLONG: + { + int32* ma; + uint64* mb; + uint32 n; + ma=(int32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)ma); + err=TIFFReadDirEntryCheckRangeLong8Slong(*ma); + if (err!=TIFFReadDirEntryErrOk) + break; + *mb++=(uint64)(*ma++); + } + } + break; + } + _TIFFfree(origdata); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(data); + return(err); + } + *value=data; + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntrySlong8Array(TIFF* tif, TIFFDirEntry* direntry, int64** value) +{ + enum TIFFReadDirEntryErr err; + uint32 count; + void* origdata; + int64* data; + switch (direntry->tdir_type) + { + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_SHORT: + case TIFF_SSHORT: + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_LONG8: + case TIFF_SLONG8: + break; + default: + return(TIFFReadDirEntryErrType); + } + err=TIFFReadDirEntryArray(tif,direntry,&count,8,&origdata); + if ((err!=TIFFReadDirEntryErrOk)||(origdata==0)) + { + *value=0; + return(err); + } + switch (direntry->tdir_type) + { + case TIFF_LONG8: + { + uint64* m; + uint32 n; + m=(uint64*)origdata; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8(m); + err=TIFFReadDirEntryCheckRangeSlong8Long8(*m); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(origdata); + return(err); + } + m++; + } + *value=(int64*)origdata; + return(TIFFReadDirEntryErrOk); + } + case TIFF_SLONG8: + *value=(int64*)origdata; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong8((uint64*)(*value),count); + return(TIFFReadDirEntryErrOk); + } + data=(int64*)_TIFFmalloc(count*8); + if (data==0) + { + _TIFFfree(origdata); + return(TIFFReadDirEntryErrAlloc); + } + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8* ma; + int64* mb; + uint32 n; + ma=(uint8*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort(ma); + *mb++=(int64)(*ma++); + } + } + break; + case TIFF_SSHORT: + { + int16* ma; + int64* mb; + uint32 n; + ma=(int16*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)ma); + *mb++=(int64)(*ma++); + } + } + break; + case TIFF_LONG: + { + uint32* ma; + int64* mb; + uint32 n; + ma=(uint32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + *mb++=(int64)(*ma++); + } + } + break; + case TIFF_SLONG: + { + int32* ma; + int64* mb; + uint32 n; + ma=(int32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)ma); + *mb++=(int64)(*ma++); + } + } + break; + } + _TIFFfree(origdata); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(data); + return(err); + } + *value=data; + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryFloatArray(TIFF* tif, TIFFDirEntry* direntry, float** value) +{ + enum TIFFReadDirEntryErr err; + uint32 count; + void* origdata; + float* data; + switch (direntry->tdir_type) + { + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_SHORT: + case TIFF_SSHORT: + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_LONG8: + case TIFF_SLONG8: + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_FLOAT: + case TIFF_DOUBLE: + break; + default: + return(TIFFReadDirEntryErrType); + } + err=TIFFReadDirEntryArray(tif,direntry,&count,4,&origdata); + if ((err!=TIFFReadDirEntryErrOk)||(origdata==0)) + { + *value=0; + return(err); + } + switch (direntry->tdir_type) + { + case TIFF_FLOAT: + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong((uint32*)origdata,count); + TIFFCvtIEEEDoubleToNative(tif,count,(float*)origdata); + *value=(float*)origdata; + return(TIFFReadDirEntryErrOk); + } + data=(float*)_TIFFmalloc(count*sizeof(float)); + if (data==0) + { + _TIFFfree(origdata); + return(TIFFReadDirEntryErrAlloc); + } + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8* ma; + float* mb; + uint32 n; + ma=(uint8*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort(ma); + *mb++=(float)(*ma++); + } + } + break; + case TIFF_SSHORT: + { + int16* ma; + float* mb; + uint32 n; + ma=(int16*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)ma); + *mb++=(float)(*ma++); + } + } + break; + case TIFF_LONG: + { + uint32* ma; + float* mb; + uint32 n; + ma=(uint32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + *mb++=(float)(*ma++); + } + } + break; + case TIFF_SLONG: + { + int32* ma; + float* mb; + uint32 n; + ma=(int32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)ma); + *mb++=(float)(*ma++); + } + } + break; + case TIFF_LONG8: + { + uint64* ma; + float* mb; + uint32 n; + ma=(uint64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8(ma); +#if defined(__WIN32__) && (_MSC_VER < 1500) + /* + * XXX: MSVC 6.0 does not support + * conversion of 64-bit integers into + * floating point values. + */ + *mb++ = _TIFFUInt64ToFloat(*ma++); +#else + *mb++ = (float)(*ma++); +#endif + } + } + break; + case TIFF_SLONG8: + { + int64* ma; + float* mb; + uint32 n; + ma=(int64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)ma); + *mb++=(float)(*ma++); + } + } + break; + case TIFF_RATIONAL: + { + uint32* ma; + uint32 maa; + uint32 mab; + float* mb; + uint32 n; + ma=(uint32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + maa=*ma++; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + mab=*ma++; + if (mab==0) + *mb++=0.0; + else + *mb++=(float)maa/(float)mab; + } + } + break; + case TIFF_SRATIONAL: + { + uint32* ma; + int32 maa; + uint32 mab; + float* mb; + uint32 n; + ma=(uint32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + maa=*(int32*)ma; + ma++; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + mab=*ma++; + if (mab==0) + *mb++=0.0; + else + *mb++=(float)maa/(float)mab; + } + } + break; + case TIFF_DOUBLE: + { + double* ma; + float* mb; + uint32 n; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong8((uint64*)origdata,count); + TIFFCvtIEEEDoubleToNative(tif,count,(double*)origdata); + ma=(double*)origdata; + mb=data; + for (n=0; ntdir_type) + { + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_SHORT: + case TIFF_SSHORT: + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_LONG8: + case TIFF_SLONG8: + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_FLOAT: + case TIFF_DOUBLE: + break; + default: + return(TIFFReadDirEntryErrType); + } + err=TIFFReadDirEntryArray(tif,direntry,&count,8,&origdata); + if ((err!=TIFFReadDirEntryErrOk)||(origdata==0)) + { + *value=0; + return(err); + } + switch (direntry->tdir_type) + { + case TIFF_DOUBLE: + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong8((uint64*)origdata,count); + TIFFCvtIEEEDoubleToNative(tif,count,(double*)origdata); + *value=(double*)origdata; + return(TIFFReadDirEntryErrOk); + } + data=(double*)_TIFFmalloc(count*sizeof(double)); + if (data==0) + { + _TIFFfree(origdata); + return(TIFFReadDirEntryErrAlloc); + } + switch (direntry->tdir_type) + { + case TIFF_BYTE: + { + uint8* ma; + double* mb; + uint32 n; + ma=(uint8*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort(ma); + *mb++=(double)(*ma++); + } + } + break; + case TIFF_SSHORT: + { + int16* ma; + double* mb; + uint32 n; + ma=(int16*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)ma); + *mb++=(double)(*ma++); + } + } + break; + case TIFF_LONG: + { + uint32* ma; + double* mb; + uint32 n; + ma=(uint32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + *mb++=(double)(*ma++); + } + } + break; + case TIFF_SLONG: + { + int32* ma; + double* mb; + uint32 n; + ma=(int32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)ma); + *mb++=(double)(*ma++); + } + } + break; + case TIFF_LONG8: + { + uint64* ma; + double* mb; + uint32 n; + ma=(uint64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8(ma); +#if defined(__WIN32__) && (_MSC_VER < 1500) + /* + * XXX: MSVC 6.0 does not support + * conversion of 64-bit integers into + * floating point values. + */ + *mb++ = _TIFFUInt64ToDouble(*ma++); +#else + *mb++ = (double)(*ma++); +#endif + } + } + break; + case TIFF_SLONG8: + { + int64* ma; + double* mb; + uint32 n; + ma=(int64*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)ma); + *mb++=(double)(*ma++); + } + } + break; + case TIFF_RATIONAL: + { + uint32* ma; + uint32 maa; + uint32 mab; + double* mb; + uint32 n; + ma=(uint32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + maa=*ma++; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + mab=*ma++; + if (mab==0) + *mb++=0.0; + else + *mb++=(double)maa/(double)mab; + } + } + break; + case TIFF_SRATIONAL: + { + uint32* ma; + int32 maa; + uint32 mab; + double* mb; + uint32 n; + ma=(uint32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + maa=*(int32*)ma; + ma++; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + mab=*ma++; + if (mab==0) + *mb++=0.0; + else + *mb++=(double)maa/(double)mab; + } + } + break; + case TIFF_FLOAT: + { + float* ma; + double* mb; + uint32 n; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong((uint32*)origdata,count); + TIFFCvtIEEEFloatToNative(tif,count,(float*)origdata); + ma=(float*)origdata; + mb=data; + for (n=0; ntdir_type) + { + case TIFF_LONG: + case TIFF_LONG8: + case TIFF_IFD: + case TIFF_IFD8: + break; + default: + return(TIFFReadDirEntryErrType); + } + err=TIFFReadDirEntryArray(tif,direntry,&count,8,&origdata); + if ((err!=TIFFReadDirEntryErrOk)||(origdata==0)) + { + *value=0; + return(err); + } + switch (direntry->tdir_type) + { + case TIFF_LONG8: + case TIFF_IFD8: + *value=(uint64*)origdata; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong8(*value,count); + return(TIFFReadDirEntryErrOk); + } + data=(uint64*)_TIFFmalloc(count*8); + if (data==0) + { + _TIFFfree(origdata); + return(TIFFReadDirEntryErrAlloc); + } + switch (direntry->tdir_type) + { + case TIFF_LONG: + case TIFF_IFD: + { + uint32* ma; + uint64* mb; + uint32 n; + ma=(uint32*)origdata; + mb=data; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabLong(ma); + *mb++=(uint64)(*ma++); + } + } + break; + } + _TIFFfree(origdata); + if (err!=TIFFReadDirEntryErrOk) + { + _TIFFfree(data); + return(err); + } + *value=data; + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryPersampleShort(TIFF* tif, TIFFDirEntry* direntry, uint16* value) +{ + enum TIFFReadDirEntryErr err; + uint16* m; + uint16* na; + uint16 nb; + if (direntry->tdir_count<(uint64)tif->tif_dir.td_samplesperpixel) + return(TIFFReadDirEntryErrCount); + err=TIFFReadDirEntryShortArray(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + na=m; + nb=tif->tif_dir.td_samplesperpixel; + *value=*na++; + nb--; + while (nb>0) + { + if (*na++!=*value) + { + err=TIFFReadDirEntryErrPsdif; + break; + } + nb--; + } + _TIFFfree(m); + return(err); +} + +#if 0 +static enum TIFFReadDirEntryErr TIFFReadDirEntryPersampleDouble(TIFF* tif, TIFFDirEntry* direntry, double* value) +{ + enum TIFFReadDirEntryErr err; + double* m; + double* na; + uint16 nb; + if (direntry->tdir_count<(uint64)tif->tif_dir.td_samplesperpixel) + return(TIFFReadDirEntryErrCount); + err=TIFFReadDirEntryDoubleArray(tif,direntry,&m); + if (err!=TIFFReadDirEntryErrOk) + return(err); + na=m; + nb=tif->tif_dir.td_samplesperpixel; + *value=*na++; + nb--; + while (nb>0) + { + if (*na++!=*value) + { + err=TIFFReadDirEntryErrPsdif; + break; + } + nb--; + } + _TIFFfree(m); + return(err); +} +#endif + +static void TIFFReadDirEntryCheckedByte(TIFF* tif, TIFFDirEntry* direntry, uint8* value) +{ + (void) tif; + *value=*(uint8*)(&direntry->tdir_offset); +} + +static void TIFFReadDirEntryCheckedSbyte(TIFF* tif, TIFFDirEntry* direntry, int8* value) +{ + (void) tif; + *value=*(int8*)(&direntry->tdir_offset); +} + +static void TIFFReadDirEntryCheckedShort(TIFF* tif, TIFFDirEntry* direntry, uint16* value) +{ + *value = direntry->tdir_offset.toff_short; + /* *value=*(uint16*)(&direntry->tdir_offset); */ + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort(value); +} + +static void TIFFReadDirEntryCheckedSshort(TIFF* tif, TIFFDirEntry* direntry, int16* value) +{ + *value=*(int16*)(&direntry->tdir_offset); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)value); +} + +static void TIFFReadDirEntryCheckedLong(TIFF* tif, TIFFDirEntry* direntry, uint32* value) +{ + *value=*(uint32*)(&direntry->tdir_offset); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(value); +} + +static void TIFFReadDirEntryCheckedSlong(TIFF* tif, TIFFDirEntry* direntry, int32* value) +{ + *value=*(int32*)(&direntry->tdir_offset); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)value); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedLong8(TIFF* tif, TIFFDirEntry* direntry, uint64* value) +{ + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + enum TIFFReadDirEntryErr err; + uint32 offset = direntry->tdir_offset.toff_long; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&offset); + err=TIFFReadDirEntryData(tif,offset,8,value); + if (err!=TIFFReadDirEntryErrOk) + return(err); + } + else + *value = direntry->tdir_offset.toff_long8; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8(value); + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedSlong8(TIFF* tif, TIFFDirEntry* direntry, int64* value) +{ + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + enum TIFFReadDirEntryErr err; + uint32 offset = direntry->tdir_offset.toff_long; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&offset); + err=TIFFReadDirEntryData(tif,offset,8,value); + if (err!=TIFFReadDirEntryErrOk) + return(err); + } + else + *value=*(int64*)(&direntry->tdir_offset); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)value); + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedRational(TIFF* tif, TIFFDirEntry* direntry, double* value) +{ + UInt64Aligned_t m; + + assert(sizeof(double)==8); + assert(sizeof(uint64)==8); + assert(sizeof(uint32)==4); + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + enum TIFFReadDirEntryErr err; + uint32 offset = direntry->tdir_offset.toff_long; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&offset); + err=TIFFReadDirEntryData(tif,offset,8,m.i); + if (err!=TIFFReadDirEntryErrOk) + return(err); + } + else + m.l = direntry->tdir_offset.toff_long8; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong(m.i,2); + if (m.i[0]==0) + *value=0.0; + else + *value=(double)m.i[0]/(double)m.i[1]; + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedSrational(TIFF* tif, TIFFDirEntry* direntry, double* value) +{ + UInt64Aligned_t m; + assert(sizeof(double)==8); + assert(sizeof(uint64)==8); + assert(sizeof(int32)==4); + assert(sizeof(uint32)==4); + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + enum TIFFReadDirEntryErr err; + uint32 offset = direntry->tdir_offset.toff_long; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&offset); + err=TIFFReadDirEntryData(tif,offset,8,m.i); + if (err!=TIFFReadDirEntryErrOk) + return(err); + } + else + m.l=direntry->tdir_offset.toff_long8; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong(m.i,2); + if ((int32)m.i[0]==0) + *value=0.0; + else + *value=(double)((int32)m.i[0])/(double)m.i[1]; + return(TIFFReadDirEntryErrOk); +} + +static void TIFFReadDirEntryCheckedFloat(TIFF* tif, TIFFDirEntry* direntry, float* value) +{ + union + { + float f; + uint32 i; + } float_union; + assert(sizeof(float)==4); + assert(sizeof(uint32)==4); + assert(sizeof(float_union)==4); + float_union.i=*(uint32*)(&direntry->tdir_offset); + *value=float_union.f; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)value); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckedDouble(TIFF* tif, TIFFDirEntry* direntry, double* value) +{ + assert(sizeof(double)==8); + assert(sizeof(uint64)==8); + assert(sizeof(UInt64Aligned_t)==8); + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + enum TIFFReadDirEntryErr err; + uint32 offset = direntry->tdir_offset.toff_long; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&offset); + err=TIFFReadDirEntryData(tif,offset,8,value); + if (err!=TIFFReadDirEntryErrOk) + return(err); + } + else + { + UInt64Aligned_t uint64_union; + uint64_union.l=direntry->tdir_offset.toff_long8; + *value=uint64_union.d; + } + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)value); + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSbyte(int8 value) +{ + if (value<0) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteShort(uint16 value) +{ + if (value>0xFF) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSshort(int16 value) +{ + if ((value<0)||(value>0xFF)) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteLong(uint32 value) +{ + if (value>0xFF) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSlong(int32 value) +{ + if ((value<0)||(value>0xFF)) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteLong8(uint64 value) +{ + if (value>0xFF) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeByteSlong8(int64 value) +{ + if ((value<0)||(value>0xFF)) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteByte(uint8 value) +{ + if (value>0x7F) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteShort(uint16 value) +{ + if (value>0x7F) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteSshort(int16 value) +{ + if ((value<-0x80)||(value>0x7F)) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteLong(uint32 value) +{ + if (value>0x7F) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteSlong(int32 value) +{ + if ((value<-0x80)||(value>0x7F)) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteLong8(uint64 value) +{ + if (value>0x7F) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSbyteSlong8(int64 value) +{ + if ((value<-0x80)||(value>0x7F)) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSbyte(int8 value) +{ + if (value<0) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSshort(int16 value) +{ + if (value<0) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortLong(uint32 value) +{ + if (value>0xFFFF) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSlong(int32 value) +{ + if ((value<0)||(value>0xFFFF)) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortLong8(uint64 value) +{ + if (value>0xFFFF) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeShortSlong8(int64 value) +{ + if ((value<0)||(value>0xFFFF)) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortShort(uint16 value) +{ + if (value>0x7FFF) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortLong(uint32 value) +{ + if (value>0x7FFF) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortSlong(int32 value) +{ + if ((value<-0x8000)||(value>0x7FFF)) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortLong8(uint64 value) +{ + if (value>0x7FFF) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeSshortSlong8(int64 value) +{ + if ((value<-0x8000)||(value>0x7FFF)) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSbyte(int8 value) +{ + if (value<0) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSshort(int16 value) +{ + if (value<0) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr TIFFReadDirEntryCheckRangeLongSlong(int32 value) +{ + if (value<0) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +/* + * Largest 32-bit unsigned integer value. + */ +#if defined(__WIN32__) && defined(_MSC_VER) +# define TIFF_UINT32_MAX 0xFFFFFFFFI64 +#else +# define TIFF_UINT32_MAX 0xFFFFFFFFLL +#endif + +static enum TIFFReadDirEntryErr +TIFFReadDirEntryCheckRangeLongLong8(uint64 value) +{ + if (value > TIFF_UINT32_MAX) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr +TIFFReadDirEntryCheckRangeLongSlong8(int64 value) +{ + if ((value<0) || (value > TIFF_UINT32_MAX)) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +#undef TIFF_UINT32_MAX + +static enum TIFFReadDirEntryErr +TIFFReadDirEntryCheckRangeSlongLong(uint32 value) +{ + if (value > 0x7FFFFFFFUL) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr +TIFFReadDirEntryCheckRangeSlongLong8(uint64 value) +{ + if (value > 0x7FFFFFFFUL) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr +TIFFReadDirEntryCheckRangeSlongSlong8(int64 value) +{ + if ((value < 0L-0x80000000L) || (value > 0x7FFFFFFFL)) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr +TIFFReadDirEntryCheckRangeLong8Sbyte(int8 value) +{ + if (value < 0) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr +TIFFReadDirEntryCheckRangeLong8Sshort(int16 value) +{ + if (value < 0) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr +TIFFReadDirEntryCheckRangeLong8Slong(int32 value) +{ + if (value < 0) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +static enum TIFFReadDirEntryErr +TIFFReadDirEntryCheckRangeLong8Slong8(int64 value) +{ + if (value < 0) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +/* + * Largest 64-bit signed integer value. + */ +#if defined(__WIN32__) && defined(_MSC_VER) +# define TIFF_INT64_MAX 0x7FFFFFFFFFFFFFFFI64 +#else +# define TIFF_INT64_MAX 0x7FFFFFFFFFFFFFFFLL +#endif + +static enum TIFFReadDirEntryErr +TIFFReadDirEntryCheckRangeSlong8Long8(uint64 value) +{ + if (value > TIFF_INT64_MAX) + return(TIFFReadDirEntryErrRange); + else + return(TIFFReadDirEntryErrOk); +} + +#undef TIFF_INT64_MAX + +static enum TIFFReadDirEntryErr +TIFFReadDirEntryData(TIFF* tif, uint64 offset, tmsize_t size, void* dest) +{ + assert(size>0); + if (!isMapped(tif)) { + if (!SeekOK(tif,offset)) + return(TIFFReadDirEntryErrIo); + if (!ReadOK(tif,dest,size)) + return(TIFFReadDirEntryErrIo); + } else { + size_t ma,mb; + ma=(size_t)offset; + mb=ma+size; + if (((uint64)ma!=offset) + || (mb < ma) + || (mb - ma != (size_t) size) + || (mb < (size_t)size) + || (mb > (size_t)tif->tif_size) + ) + return(TIFFReadDirEntryErrIo); + _TIFFmemcpy(dest,tif->tif_base+ma,size); + } + return(TIFFReadDirEntryErrOk); +} + +static void TIFFReadDirEntryOutputErr(TIFF* tif, enum TIFFReadDirEntryErr err, const char* module, const char* tagname, int recover) +{ + if (!recover) { + switch (err) { + case TIFFReadDirEntryErrCount: + TIFFErrorExt(tif->tif_clientdata, module, + "Incorrect count for \"%s\"", + tagname); + break; + case TIFFReadDirEntryErrType: + TIFFErrorExt(tif->tif_clientdata, module, + "Incompatible type for \"%s\"", + tagname); + break; + case TIFFReadDirEntryErrIo: + TIFFErrorExt(tif->tif_clientdata, module, + "IO error during reading of \"%s\"", + tagname); + break; + case TIFFReadDirEntryErrRange: + TIFFErrorExt(tif->tif_clientdata, module, + "Incorrect value for \"%s\"", + tagname); + break; + case TIFFReadDirEntryErrPsdif: + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot handle different values per sample for \"%s\"", + tagname); + break; + case TIFFReadDirEntryErrSizesan: + TIFFErrorExt(tif->tif_clientdata, module, + "Sanity check on size of \"%s\" value failed", + tagname); + break; + case TIFFReadDirEntryErrAlloc: + TIFFErrorExt(tif->tif_clientdata, module, + "Out of memory reading of \"%s\"", + tagname); + break; + default: + assert(0); /* we should never get here */ + break; + } + } else { + switch (err) { + case TIFFReadDirEntryErrCount: + TIFFWarningExt(tif->tif_clientdata, module, + "Incorrect count for \"%s\"; tag ignored", + tagname); + break; + case TIFFReadDirEntryErrType: + TIFFWarningExt(tif->tif_clientdata, module, + "Incompatible type for \"%s\"; tag ignored", + tagname); + break; + case TIFFReadDirEntryErrIo: + TIFFWarningExt(tif->tif_clientdata, module, + "IO error during reading of \"%s\"; tag ignored", + tagname); + break; + case TIFFReadDirEntryErrRange: + TIFFWarningExt(tif->tif_clientdata, module, + "Incorrect value for \"%s\"; tag ignored", + tagname); + break; + case TIFFReadDirEntryErrPsdif: + TIFFWarningExt(tif->tif_clientdata, module, + "Cannot handle different values per sample for \"%s\"; tag ignored", + tagname); + break; + case TIFFReadDirEntryErrSizesan: + TIFFWarningExt(tif->tif_clientdata, module, + "Sanity check on size of \"%s\" value failed; tag ignored", + tagname); + break; + case TIFFReadDirEntryErrAlloc: + TIFFWarningExt(tif->tif_clientdata, module, + "Out of memory reading of \"%s\"; tag ignored", + tagname); + break; + default: + assert(0); /* we should never get here */ + break; + } + } +} + +/* + * Read the next TIFF directory from a file and convert it to the internal + * format. We read directories sequentially. + */ +int +TIFFReadDirectory(TIFF* tif) +{ + static const char module[] = "TIFFReadDirectory"; + TIFFDirEntry* dir; + uint16 dircount; + TIFFDirEntry* dp; + uint16 di; + const TIFFField* fip; + uint32 fii=FAILED_FII; + toff_t nextdiroff; + int bitspersample_read = FALSE; + + tif->tif_diroff=tif->tif_nextdiroff; + if (!TIFFCheckDirOffset(tif,tif->tif_nextdiroff)) + return 0; /* last offset or bad offset (IFD looping) */ + (*tif->tif_cleanup)(tif); /* cleanup any previous compression state */ + tif->tif_curdir++; + nextdiroff = tif->tif_nextdiroff; + dircount=TIFFFetchDirectory(tif,nextdiroff,&dir,&tif->tif_nextdiroff); + if (!dircount) + { + TIFFErrorExt(tif->tif_clientdata,module, + "Failed to read directory at offset " TIFF_UINT64_FORMAT,nextdiroff); + return 0; + } + TIFFReadDirectoryCheckOrder(tif,dir,dircount); + + /* + * Mark duplicates of any tag to be ignored (bugzilla 1994) + * to avoid certain pathological problems. + */ + { + TIFFDirEntry* ma; + uint16 mb; + for (ma=dir, mb=0; mbtdir_tag==na->tdir_tag) + na->tdir_tag=IGNORE; + } + } + } + + tif->tif_flags &= ~TIFF_BEENWRITING; /* reset before new dir */ + tif->tif_flags &= ~TIFF_BUF4WRITE; /* reset before new dir */ + /* free any old stuff and reinit */ + TIFFFreeDirectory(tif); + TIFFDefaultDirectory(tif); + /* + * Electronic Arts writes gray-scale TIFF files + * without a PlanarConfiguration directory entry. + * Thus we setup a default value here, even though + * the TIFF spec says there is no default value. + */ + TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG); + /* + * Setup default value and then make a pass over + * the fields to check type and tag information, + * and to extract info required to size data + * structures. A second pass is made afterwards + * to read in everthing not taken in the first pass. + * But we must process the Compression tag first + * in order to merge in codec-private tag definitions (otherwise + * we may get complaints about unknown tags). However, the + * Compression tag may be dependent on the SamplesPerPixel + * tag value because older TIFF specs permited Compression + * to be written as a SamplesPerPixel-count tag entry. + * Thus if we don't first figure out the correct SamplesPerPixel + * tag value then we may end up ignoring the Compression tag + * value because it has an incorrect count value (if the + * true value of SamplesPerPixel is not 1). + */ + dp=TIFFReadDirectoryFindEntry(tif,dir,dircount,TIFFTAG_SAMPLESPERPIXEL); + if (dp) + { + if (!TIFFFetchNormalTag(tif,dp,0)) + goto bad; + dp->tdir_tag=IGNORE; + } + dp=TIFFReadDirectoryFindEntry(tif,dir,dircount,TIFFTAG_COMPRESSION); + if (dp) + { + /* + * The 5.0 spec says the Compression tag has one value, while + * earlier specs say it has one value per sample. Because of + * this, we accept the tag if one value is supplied with either + * count. + */ + uint16 value; + enum TIFFReadDirEntryErr err; + err=TIFFReadDirEntryShort(tif,dp,&value); + if (err==TIFFReadDirEntryErrCount) + err=TIFFReadDirEntryPersampleShort(tif,dp,&value); + if (err!=TIFFReadDirEntryErrOk) + { + TIFFReadDirEntryOutputErr(tif,err,module,"Compression",0); + goto bad; + } + if (!TIFFSetField(tif,TIFFTAG_COMPRESSION,value)) + goto bad; + dp->tdir_tag=IGNORE; + } + else + { + if (!TIFFSetField(tif,TIFFTAG_COMPRESSION,COMPRESSION_NONE)) + goto bad; + } + /* + * First real pass over the directory. + */ + for (di=0, dp=dir; ditdir_tag!=IGNORE) + { + TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii); + if (fii == FAILED_FII) + { + TIFFWarningExt(tif->tif_clientdata, module, + "Unknown field with tag %d (0x%x) encountered", + dp->tdir_tag,dp->tdir_tag); + /* the following knowingly leaks the + anonymous field structure */ + if (!_TIFFMergeFields(tif, + _TIFFCreateAnonField(tif, + dp->tdir_tag, + (TIFFDataType) dp->tdir_type), + 1)) { + TIFFWarningExt(tif->tif_clientdata, + module, + "Registering anonymous field with tag %d (0x%x) failed", + dp->tdir_tag, + dp->tdir_tag); + dp->tdir_tag=IGNORE; + } else { + TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii); + assert(fii != FAILED_FII); + } + } + } + if (dp->tdir_tag!=IGNORE) + { + fip=tif->tif_fields[fii]; + if (fip->field_bit==FIELD_IGNORE) + dp->tdir_tag=IGNORE; + else + { + switch (dp->tdir_tag) + { + case TIFFTAG_STRIPOFFSETS: + case TIFFTAG_STRIPBYTECOUNTS: + case TIFFTAG_TILEOFFSETS: + case TIFFTAG_TILEBYTECOUNTS: + TIFFSetFieldBit(tif,fip->field_bit); + break; + case TIFFTAG_IMAGEWIDTH: + case TIFFTAG_IMAGELENGTH: + case TIFFTAG_IMAGEDEPTH: + case TIFFTAG_TILELENGTH: + case TIFFTAG_TILEWIDTH: + case TIFFTAG_TILEDEPTH: + case TIFFTAG_PLANARCONFIG: + case TIFFTAG_ROWSPERSTRIP: + case TIFFTAG_EXTRASAMPLES: + if (!TIFFFetchNormalTag(tif,dp,0)) + goto bad; + dp->tdir_tag=IGNORE; + break; + } + } + } + } + /* + * XXX: OJPEG hack. + * If a) compression is OJPEG, b) planarconfig tag says it's separate, + * c) strip offsets/bytecounts tag are both present and + * d) both contain exactly one value, then we consistently find + * that the buggy implementation of the buggy compression scheme + * matches contig planarconfig best. So we 'fix-up' the tag here + */ + if ((tif->tif_dir.td_compression==COMPRESSION_OJPEG)&& + (tif->tif_dir.td_planarconfig==PLANARCONFIG_SEPARATE)) + { + if (!_TIFFFillStriles(tif)) + goto bad; + dp=TIFFReadDirectoryFindEntry(tif,dir,dircount,TIFFTAG_STRIPOFFSETS); + if ((dp!=0)&&(dp->tdir_count==1)) + { + dp=TIFFReadDirectoryFindEntry(tif,dir,dircount, + TIFFTAG_STRIPBYTECOUNTS); + if ((dp!=0)&&(dp->tdir_count==1)) + { + tif->tif_dir.td_planarconfig=PLANARCONFIG_CONTIG; + TIFFWarningExt(tif->tif_clientdata,module, + "Planarconfig tag value assumed incorrect, " + "assuming data is contig instead of chunky"); + } + } + } + /* + * Allocate directory structure and setup defaults. + */ + if (!TIFFFieldSet(tif,FIELD_IMAGEDIMENSIONS)) + { + MissingRequired(tif,"ImageLength"); + goto bad; + } + /* + * Setup appropriate structures (by strip or by tile) + */ + if (!TIFFFieldSet(tif, FIELD_TILEDIMENSIONS)) { + tif->tif_dir.td_nstrips = TIFFNumberOfStrips(tif); + tif->tif_dir.td_tilewidth = tif->tif_dir.td_imagewidth; + tif->tif_dir.td_tilelength = tif->tif_dir.td_rowsperstrip; + tif->tif_dir.td_tiledepth = tif->tif_dir.td_imagedepth; + tif->tif_flags &= ~TIFF_ISTILED; + } else { + tif->tif_dir.td_nstrips = TIFFNumberOfTiles(tif); + tif->tif_flags |= TIFF_ISTILED; + } + if (!tif->tif_dir.td_nstrips) { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot handle zero number of %s", + isTiled(tif) ? "tiles" : "strips"); + goto bad; + } + tif->tif_dir.td_stripsperimage = tif->tif_dir.td_nstrips; + if (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE) + tif->tif_dir.td_stripsperimage /= tif->tif_dir.td_samplesperpixel; + if (!TIFFFieldSet(tif, FIELD_STRIPOFFSETS)) { + if ((tif->tif_dir.td_compression==COMPRESSION_OJPEG) && + (isTiled(tif)==0) && + (tif->tif_dir.td_nstrips==1)) { + /* + * XXX: OJPEG hack. + * If a) compression is OJPEG, b) it's not a tiled TIFF, + * and c) the number of strips is 1, + * then we tolerate the absence of stripoffsets tag, + * because, presumably, all required data is in the + * JpegInterchangeFormat stream. + */ + TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS); + } else { + MissingRequired(tif, + isTiled(tif) ? "TileOffsets" : "StripOffsets"); + goto bad; + } + } + /* + * Second pass: extract other information. + */ + for (di=0, dp=dir; ditdir_tag) + { + case IGNORE: + break; + case TIFFTAG_MINSAMPLEVALUE: + case TIFFTAG_MAXSAMPLEVALUE: + case TIFFTAG_BITSPERSAMPLE: + case TIFFTAG_DATATYPE: + case TIFFTAG_SAMPLEFORMAT: + /* + * The MinSampleValue, MaxSampleValue, BitsPerSample + * DataType and SampleFormat tags are supposed to be + * written as one value/sample, but some vendors + * incorrectly write one value only -- so we accept + * that as well (yech). Other vendors write correct + * value for NumberOfSamples, but incorrect one for + * BitsPerSample and friends, and we will read this + * too. + */ + { + uint16 value; + enum TIFFReadDirEntryErr err; + err=TIFFReadDirEntryShort(tif,dp,&value); + if (err==TIFFReadDirEntryErrCount) + err=TIFFReadDirEntryPersampleShort(tif,dp,&value); + if (err!=TIFFReadDirEntryErrOk) + { + fip = TIFFFieldWithTag(tif,dp->tdir_tag); + TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",0); + goto bad; + } + if (!TIFFSetField(tif,dp->tdir_tag,value)) + goto bad; + if( dp->tdir_tag == TIFFTAG_BITSPERSAMPLE ) + bitspersample_read = TRUE; + } + break; + case TIFFTAG_SMINSAMPLEVALUE: + case TIFFTAG_SMAXSAMPLEVALUE: + { + + double *data; + enum TIFFReadDirEntryErr err; + uint32 saved_flags; + int m; + if (dp->tdir_count != (uint64)tif->tif_dir.td_samplesperpixel) + err = TIFFReadDirEntryErrCount; + else + err = TIFFReadDirEntryDoubleArray(tif, dp, &data); + if (err!=TIFFReadDirEntryErrOk) + { + fip = TIFFFieldWithTag(tif,dp->tdir_tag); + TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",0); + goto bad; + } + saved_flags = tif->tif_flags; + tif->tif_flags |= TIFF_PERSAMPLE; + m = TIFFSetField(tif,dp->tdir_tag,data); + tif->tif_flags = saved_flags; + _TIFFfree(data); + if (!m) + goto bad; + } + break; + case TIFFTAG_STRIPOFFSETS: + case TIFFTAG_TILEOFFSETS: +#if defined(DEFER_STRILE_LOAD) + _TIFFmemcpy( &(tif->tif_dir.td_stripoffset_entry), + dp, sizeof(TIFFDirEntry) ); +#else + if (!TIFFFetchStripThing(tif,dp,tif->tif_dir.td_nstrips,&tif->tif_dir.td_stripoffset)) + goto bad; +#endif + break; + case TIFFTAG_STRIPBYTECOUNTS: + case TIFFTAG_TILEBYTECOUNTS: +#if defined(DEFER_STRILE_LOAD) + _TIFFmemcpy( &(tif->tif_dir.td_stripbytecount_entry), + dp, sizeof(TIFFDirEntry) ); +#else + if (!TIFFFetchStripThing(tif,dp,tif->tif_dir.td_nstrips,&tif->tif_dir.td_stripbytecount)) + goto bad; +#endif + break; + case TIFFTAG_COLORMAP: + case TIFFTAG_TRANSFERFUNCTION: + { + enum TIFFReadDirEntryErr err; + uint32 countpersample; + uint32 countrequired; + uint32 incrementpersample; + uint16* value=NULL; + /* It would be dangerous to instanciate those tag values */ + /* since if td_bitspersample has not yet been read (due to */ + /* unordered tags), it could be read afterwards with a */ + /* values greater than the default one (1), which may cause */ + /* crashes in user code */ + if( !bitspersample_read ) + { + fip = TIFFFieldWithTag(tif,dp->tdir_tag); + TIFFWarningExt(tif->tif_clientdata,module, + "Ignoring %s since BitsPerSample tag not found", + fip ? fip->field_name : "unknown tagname"); + continue; + } + countpersample=(1L<tif_dir.td_bitspersample); + if ((dp->tdir_tag==TIFFTAG_TRANSFERFUNCTION)&&(dp->tdir_count==(uint64)countpersample)) + { + countrequired=countpersample; + incrementpersample=0; + } + else + { + countrequired=3*countpersample; + incrementpersample=countpersample; + } + if (dp->tdir_count!=(uint64)countrequired) + err=TIFFReadDirEntryErrCount; + else + err=TIFFReadDirEntryShortArray(tif,dp,&value); + if (err!=TIFFReadDirEntryErrOk) + { + fip = TIFFFieldWithTag(tif,dp->tdir_tag); + TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",1); + } + else + { + TIFFSetField(tif,dp->tdir_tag,value,value+incrementpersample,value+2*incrementpersample); + _TIFFfree(value); + } + } + break; +/* BEGIN REV 4.0 COMPATIBILITY */ + case TIFFTAG_OSUBFILETYPE: + { + uint16 valueo; + uint32 value; + if (TIFFReadDirEntryShort(tif,dp,&valueo)==TIFFReadDirEntryErrOk) + { + switch (valueo) + { + case OFILETYPE_REDUCEDIMAGE: value=FILETYPE_REDUCEDIMAGE; break; + case OFILETYPE_PAGE: value=FILETYPE_PAGE; break; + default: value=0; break; + } + if (value!=0) + TIFFSetField(tif,TIFFTAG_SUBFILETYPE,value); + } + } + break; +/* END REV 4.0 COMPATIBILITY */ + default: + (void) TIFFFetchNormalTag(tif, dp, TRUE); + break; + } + } + /* + * OJPEG hack: + * - If a) compression is OJPEG, and b) photometric tag is missing, + * then we consistently find that photometric should be YCbCr + * - If a) compression is OJPEG, and b) photometric tag says it's RGB, + * then we consistently find that the buggy implementation of the + * buggy compression scheme matches photometric YCbCr instead. + * - If a) compression is OJPEG, and b) bitspersample tag is missing, + * then we consistently find bitspersample should be 8. + * - If a) compression is OJPEG, b) samplesperpixel tag is missing, + * and c) photometric is RGB or YCbCr, then we consistently find + * samplesperpixel should be 3 + * - If a) compression is OJPEG, b) samplesperpixel tag is missing, + * and c) photometric is MINISWHITE or MINISBLACK, then we consistently + * find samplesperpixel should be 3 + */ + if (tif->tif_dir.td_compression==COMPRESSION_OJPEG) + { + if (!TIFFFieldSet(tif,FIELD_PHOTOMETRIC)) + { + TIFFWarningExt(tif->tif_clientdata, module, + "Photometric tag is missing, assuming data is YCbCr"); + if (!TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,PHOTOMETRIC_YCBCR)) + goto bad; + } + else if (tif->tif_dir.td_photometric==PHOTOMETRIC_RGB) + { + tif->tif_dir.td_photometric=PHOTOMETRIC_YCBCR; + TIFFWarningExt(tif->tif_clientdata, module, + "Photometric tag value assumed incorrect, " + "assuming data is YCbCr instead of RGB"); + } + if (!TIFFFieldSet(tif,FIELD_BITSPERSAMPLE)) + { + TIFFWarningExt(tif->tif_clientdata,module, + "BitsPerSample tag is missing, assuming 8 bits per sample"); + if (!TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,8)) + goto bad; + } + if (!TIFFFieldSet(tif,FIELD_SAMPLESPERPIXEL)) + { + if (tif->tif_dir.td_photometric==PHOTOMETRIC_RGB) + { + TIFFWarningExt(tif->tif_clientdata,module, + "SamplesPerPixel tag is missing, " + "assuming correct SamplesPerPixel value is 3"); + if (!TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,3)) + goto bad; + } + if (tif->tif_dir.td_photometric==PHOTOMETRIC_YCBCR) + { + TIFFWarningExt(tif->tif_clientdata,module, + "SamplesPerPixel tag is missing, " + "applying correct SamplesPerPixel value of 3"); + if (!TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,3)) + goto bad; + } + else if ((tif->tif_dir.td_photometric==PHOTOMETRIC_MINISWHITE) + || (tif->tif_dir.td_photometric==PHOTOMETRIC_MINISBLACK)) + { + /* + * SamplesPerPixel tag is missing, but is not required + * by spec. Assume correct SamplesPerPixel value of 1. + */ + if (!TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,1)) + goto bad; + } + } + } + /* + * Verify Palette image has a Colormap. + */ + if (tif->tif_dir.td_photometric == PHOTOMETRIC_PALETTE && + !TIFFFieldSet(tif, FIELD_COLORMAP)) { + if ( tif->tif_dir.td_bitspersample>=8 && tif->tif_dir.td_samplesperpixel==3) + tif->tif_dir.td_photometric = PHOTOMETRIC_RGB; + else if (tif->tif_dir.td_bitspersample>=8) + tif->tif_dir.td_photometric = PHOTOMETRIC_MINISBLACK; + else { + MissingRequired(tif, "Colormap"); + goto bad; + } + } + /* + * OJPEG hack: + * We do no further messing with strip/tile offsets/bytecounts in OJPEG + * TIFFs + */ + if (tif->tif_dir.td_compression!=COMPRESSION_OJPEG) + { + /* + * Attempt to deal with a missing StripByteCounts tag. + */ + if (!TIFFFieldSet(tif, FIELD_STRIPBYTECOUNTS)) { + /* + * Some manufacturers violate the spec by not giving + * the size of the strips. In this case, assume there + * is one uncompressed strip of data. + */ + if ((tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG && + tif->tif_dir.td_nstrips > 1) || + (tif->tif_dir.td_planarconfig == PLANARCONFIG_SEPARATE && + tif->tif_dir.td_nstrips != (uint32)tif->tif_dir.td_samplesperpixel)) { + MissingRequired(tif, "StripByteCounts"); + goto bad; + } + TIFFWarningExt(tif->tif_clientdata, module, + "TIFF directory is missing required " + "\"StripByteCounts\" field, calculating from imagelength"); + if (EstimateStripByteCounts(tif, dir, dircount) < 0) + goto bad; + /* + * Assume we have wrong StripByteCount value (in case + * of single strip) in following cases: + * - it is equal to zero along with StripOffset; + * - it is larger than file itself (in case of uncompressed + * image); + * - it is smaller than the size of the bytes per row + * multiplied on the number of rows. The last case should + * not be checked in the case of writing new image, + * because we may do not know the exact strip size + * until the whole image will be written and directory + * dumped out. + */ + #define BYTECOUNTLOOKSBAD \ + ( (tif->tif_dir.td_stripbytecount[0] == 0 && tif->tif_dir.td_stripoffset[0] != 0) || \ + (tif->tif_dir.td_compression == COMPRESSION_NONE && \ + tif->tif_dir.td_stripbytecount[0] > TIFFGetFileSize(tif) - tif->tif_dir.td_stripoffset[0]) || \ + (tif->tif_mode == O_RDONLY && \ + tif->tif_dir.td_compression == COMPRESSION_NONE && \ + tif->tif_dir.td_stripbytecount[0] < TIFFScanlineSize64(tif) * tif->tif_dir.td_imagelength) ) + + } else if (tif->tif_dir.td_nstrips == 1 + && _TIFFFillStriles(tif) + && tif->tif_dir.td_stripoffset[0] != 0 + && BYTECOUNTLOOKSBAD) { + /* + * XXX: Plexus (and others) sometimes give a value of + * zero for a tag when they don't know what the + * correct value is! Try and handle the simple case + * of estimating the size of a one strip image. + */ + TIFFWarningExt(tif->tif_clientdata, module, + "Bogus \"StripByteCounts\" field, ignoring and calculating from imagelength"); + if(EstimateStripByteCounts(tif, dir, dircount) < 0) + goto bad; + +#if !defined(DEFER_STRILE_LOAD) + } else if (tif->tif_dir.td_planarconfig == PLANARCONFIG_CONTIG + && tif->tif_dir.td_nstrips > 2 + && tif->tif_dir.td_compression == COMPRESSION_NONE + && tif->tif_dir.td_stripbytecount[0] != tif->tif_dir.td_stripbytecount[1] + && tif->tif_dir.td_stripbytecount[0] != 0 + && tif->tif_dir.td_stripbytecount[1] != 0 ) { + /* + * XXX: Some vendors fill StripByteCount array with + * absolutely wrong values (it can be equal to + * StripOffset array, for example). Catch this case + * here. + * + * We avoid this check if deferring strile loading + * as it would always force us to load the strip/tile + * information. + */ + TIFFWarningExt(tif->tif_clientdata, module, + "Wrong \"StripByteCounts\" field, ignoring and calculating from imagelength"); + if (EstimateStripByteCounts(tif, dir, dircount) < 0) + goto bad; +#endif /* !defined(DEFER_STRILE_LOAD) */ + } + } + if (dir) + { + _TIFFfree(dir); + dir=NULL; + } + if (!TIFFFieldSet(tif, FIELD_MAXSAMPLEVALUE)) + { + if (tif->tif_dir.td_bitspersample>=16) + tif->tif_dir.td_maxsamplevalue=0xFFFF; + else + tif->tif_dir.td_maxsamplevalue = (uint16)((1L<tif_dir.td_bitspersample)-1); + } + /* + * XXX: We can optimize checking for the strip bounds using the sorted + * bytecounts array. See also comments for TIFFAppendToStrip() + * function in tif_write.c. + */ +#if !defined(DEFER_STRILE_LOAD) + if (tif->tif_dir.td_nstrips > 1) { + uint32 strip; + + tif->tif_dir.td_stripbytecountsorted = 1; + for (strip = 1; strip < tif->tif_dir.td_nstrips; strip++) { + if (tif->tif_dir.td_stripoffset[strip - 1] > + tif->tif_dir.td_stripoffset[strip]) { + tif->tif_dir.td_stripbytecountsorted = 0; + break; + } + } + } +#endif /* !defined(DEFER_STRILE_LOAD) */ + + /* + * An opportunity for compression mode dependent tag fixup + */ + (*tif->tif_fixuptags)(tif); + + /* + * Some manufacturers make life difficult by writing + * large amounts of uncompressed data as a single strip. + * This is contrary to the recommendations of the spec. + * The following makes an attempt at breaking such images + * into strips closer to the recommended 8k bytes. A + * side effect, however, is that the RowsPerStrip tag + * value may be changed. + */ + if ((tif->tif_dir.td_planarconfig==PLANARCONFIG_CONTIG)&& + (tif->tif_dir.td_nstrips==1)&& + (tif->tif_dir.td_compression==COMPRESSION_NONE)&& + ((tif->tif_flags&(TIFF_STRIPCHOP|TIFF_ISTILED))==TIFF_STRIPCHOP)) + { + if ( !_TIFFFillStriles(tif) || !tif->tif_dir.td_stripbytecount ) + return 0; + ChopUpSingleUncompressedStrip(tif); + } + + /* + * Clear the dirty directory flag. + */ + tif->tif_flags &= ~TIFF_DIRTYDIRECT; + tif->tif_flags &= ~TIFF_DIRTYSTRIP; + + /* + * Reinitialize i/o since we are starting on a new directory. + */ + tif->tif_row = (uint32) -1; + tif->tif_curstrip = (uint32) -1; + tif->tif_col = (uint32) -1; + tif->tif_curtile = (uint32) -1; + tif->tif_tilesize = (tmsize_t) -1; + + tif->tif_scanlinesize = TIFFScanlineSize(tif); + if (!tif->tif_scanlinesize) { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot handle zero scanline size"); + return (0); + } + + if (isTiled(tif)) { + tif->tif_tilesize = TIFFTileSize(tif); + if (!tif->tif_tilesize) { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot handle zero tile size"); + return (0); + } + } else { + if (!TIFFStripSize(tif)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot handle zero strip size"); + return (0); + } + } + return (1); +bad: + if (dir) + _TIFFfree(dir); + return (0); +} + +static void +TIFFReadDirectoryCheckOrder(TIFF* tif, TIFFDirEntry* dir, uint16 dircount) +{ + static const char module[] = "TIFFReadDirectoryCheckOrder"; + uint16 m; + uint16 n; + TIFFDirEntry* o; + m=0; + for (n=0, o=dir; ntdir_tagtif_clientdata,module, + "Invalid TIFF directory; tags are not sorted in ascending order"); + break; + } + m=o->tdir_tag+1; + } +} + +static TIFFDirEntry* +TIFFReadDirectoryFindEntry(TIFF* tif, TIFFDirEntry* dir, uint16 dircount, uint16 tagid) +{ + TIFFDirEntry* m; + uint16 n; + (void) tif; + for (m=dir, n=0; ntdir_tag==tagid) + return(m); + } + return(0); +} + +static void +TIFFReadDirectoryFindFieldInfo(TIFF* tif, uint16 tagid, uint32* fii) +{ + int32 ma,mb,mc; + ma=-1; + mc=(int32)tif->tif_nfields; + while (1) + { + if (ma+1==mc) + { + *fii = FAILED_FII; + return; + } + mb=(ma+mc)/2; + if (tif->tif_fields[mb]->field_tag==(uint32)tagid) + break; + if (tif->tif_fields[mb]->field_tag<(uint32)tagid) + ma=mb; + else + mc=mb; + } + while (1) + { + if (mb==0) + break; + if (tif->tif_fields[mb-1]->field_tag!=(uint32)tagid) + break; + mb--; + } + *fii=mb; +} + +/* + * Read custom directory from the arbitarry offset. + * The code is very similar to TIFFReadDirectory(). + */ +int +TIFFReadCustomDirectory(TIFF* tif, toff_t diroff, + const TIFFFieldArray* infoarray) +{ + static const char module[] = "TIFFReadCustomDirectory"; + TIFFDirEntry* dir; + uint16 dircount; + TIFFDirEntry* dp; + uint16 di; + const TIFFField* fip; + uint32 fii; + _TIFFSetupFields(tif, infoarray); + dircount=TIFFFetchDirectory(tif,diroff,&dir,NULL); + if (!dircount) + { + TIFFErrorExt(tif->tif_clientdata,module, + "Failed to read custom directory at offset " TIFF_UINT64_FORMAT,diroff); + return 0; + } + TIFFFreeDirectory(tif); + _TIFFmemset(&tif->tif_dir, 0, sizeof(TIFFDirectory)); + TIFFReadDirectoryCheckOrder(tif,dir,dircount); + for (di=0, dp=dir; ditdir_tag,&fii); + if (fii == FAILED_FII) + { + TIFFWarningExt(tif->tif_clientdata, module, + "Unknown field with tag %d (0x%x) encountered", + dp->tdir_tag, dp->tdir_tag); + if (!_TIFFMergeFields(tif, _TIFFCreateAnonField(tif, + dp->tdir_tag, + (TIFFDataType) dp->tdir_type), + 1)) { + TIFFWarningExt(tif->tif_clientdata, module, + "Registering anonymous field with tag %d (0x%x) failed", + dp->tdir_tag, dp->tdir_tag); + dp->tdir_tag=IGNORE; + } else { + TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii); + assert( fii != FAILED_FII ); + } + } + if (dp->tdir_tag!=IGNORE) + { + fip=tif->tif_fields[fii]; + if (fip->field_bit==FIELD_IGNORE) + dp->tdir_tag=IGNORE; + else + { + /* check data type */ + while ((fip->field_type!=TIFF_ANY)&&(fip->field_type!=dp->tdir_type)) + { + fii++; + if ((fii==tif->tif_nfields)|| + (tif->tif_fields[fii]->field_tag!=(uint32)dp->tdir_tag)) + { + fii=0xFFFF; + break; + } + fip=tif->tif_fields[fii]; + } + if (fii==0xFFFF) + { + TIFFWarningExt(tif->tif_clientdata, module, + "Wrong data type %d for \"%s\"; tag ignored", + dp->tdir_type,fip->field_name); + dp->tdir_tag=IGNORE; + } + else + { + /* check count if known in advance */ + if ((fip->field_readcount!=TIFF_VARIABLE)&& + (fip->field_readcount!=TIFF_VARIABLE2)) + { + uint32 expected; + if (fip->field_readcount==TIFF_SPP) + expected=(uint32)tif->tif_dir.td_samplesperpixel; + else + expected=(uint32)fip->field_readcount; + if (!CheckDirCount(tif,dp,expected)) + dp->tdir_tag=IGNORE; + } + } + } + switch (dp->tdir_tag) + { + case IGNORE: + break; + case EXIFTAG_SUBJECTDISTANCE: + (void) TIFFFetchSubjectDistance(tif,dp); + break; + default: + (void) TIFFFetchNormalTag(tif, dp, TRUE); + break; + } + } + } + if (dir) + _TIFFfree(dir); + return 1; +} + +/* + * EXIF is important special case of custom IFD, so we have a special + * function to read it. + */ +int +TIFFReadEXIFDirectory(TIFF* tif, toff_t diroff) +{ + const TIFFFieldArray* exifFieldArray; + exifFieldArray = _TIFFGetExifFields(); + return TIFFReadCustomDirectory(tif, diroff, exifFieldArray); +} + +static int +EstimateStripByteCounts(TIFF* tif, TIFFDirEntry* dir, uint16 dircount) +{ + static const char module[] = "EstimateStripByteCounts"; + + TIFFDirEntry *dp; + TIFFDirectory *td = &tif->tif_dir; + uint32 strip; + + if( !_TIFFFillStriles( tif ) ) + return -1; + + if (td->td_stripbytecount) + _TIFFfree(td->td_stripbytecount); + td->td_stripbytecount = (uint64*) + _TIFFCheckMalloc(tif, td->td_nstrips, sizeof (uint64), + "for \"StripByteCounts\" array"); + if( td->td_stripbytecount == NULL ) + return -1; + + if (td->td_compression != COMPRESSION_NONE) { + uint64 space; + uint64 filesize; + uint16 n; + filesize = TIFFGetFileSize(tif); + if (!(tif->tif_flags&TIFF_BIGTIFF)) + space=sizeof(TIFFHeaderClassic)+2+dircount*12+4; + else + space=sizeof(TIFFHeaderBig)+8+dircount*20+8; + /* calculate amount of space used by indirect values */ + for (dp = dir, n = dircount; n > 0; n--, dp++) + { + uint32 typewidth = TIFFDataWidth((TIFFDataType) dp->tdir_type); + uint64 datasize; + typewidth = TIFFDataWidth((TIFFDataType) dp->tdir_type); + if (typewidth == 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "Cannot determine size of unknown tag type %d", + dp->tdir_type); + return -1; + } + datasize=(uint64)typewidth*dp->tdir_count; + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + if (datasize<=4) + datasize=0; + } + else + { + if (datasize<=8) + datasize=0; + } + space+=datasize; + } + space = filesize - space; + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) + space /= td->td_samplesperpixel; + for (strip = 0; strip < td->td_nstrips; strip++) + td->td_stripbytecount[strip] = space; + /* + * This gross hack handles the case were the offset to + * the last strip is past the place where we think the strip + * should begin. Since a strip of data must be contiguous, + * it's safe to assume that we've overestimated the amount + * of data in the strip and trim this number back accordingly. + */ + strip--; + if (td->td_stripoffset[strip]+td->td_stripbytecount[strip] > filesize) + td->td_stripbytecount[strip] = filesize - td->td_stripoffset[strip]; + } else if (isTiled(tif)) { + uint64 bytespertile = TIFFTileSize64(tif); + + for (strip = 0; strip < td->td_nstrips; strip++) + td->td_stripbytecount[strip] = bytespertile; + } else { + uint64 rowbytes = TIFFScanlineSize64(tif); + uint32 rowsperstrip = td->td_imagelength/td->td_stripsperimage; + for (strip = 0; strip < td->td_nstrips; strip++) + td->td_stripbytecount[strip] = rowbytes * rowsperstrip; + } + TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS); + if (!TIFFFieldSet(tif, FIELD_ROWSPERSTRIP)) + td->td_rowsperstrip = td->td_imagelength; + return 1; +} + +static void +MissingRequired(TIFF* tif, const char* tagname) +{ + static const char module[] = "MissingRequired"; + + TIFFErrorExt(tif->tif_clientdata, module, + "TIFF directory is missing required \"%s\" field", + tagname); +} + +/* + * Check the directory offset against the list of already seen directory + * offsets. This is a trick to prevent IFD looping. The one can create TIFF + * file with looped directory pointers. We will maintain a list of already + * seen directories and check every IFD offset against that list. + */ +static int +TIFFCheckDirOffset(TIFF* tif, uint64 diroff) +{ + uint16 n; + + if (diroff == 0) /* no more directories */ + return 0; + if (tif->tif_dirnumber == 65535) { + TIFFErrorExt(tif->tif_clientdata, "TIFFCheckDirOffset", + "Cannot handle more than 65535 TIFF directories"); + return 0; + } + + for (n = 0; n < tif->tif_dirnumber && tif->tif_dirlist; n++) { + if (tif->tif_dirlist[n] == diroff) + return 0; + } + + tif->tif_dirnumber++; + + if (tif->tif_dirnumber > tif->tif_dirlistsize) { + uint64* new_dirlist; + + /* + * XXX: Reduce memory allocation granularity of the dirlist + * array. + */ + new_dirlist = (uint64*)_TIFFCheckRealloc(tif, tif->tif_dirlist, + tif->tif_dirnumber, 2 * sizeof(uint64), "for IFD list"); + if (!new_dirlist) + return 0; + if( tif->tif_dirnumber >= 32768 ) + tif->tif_dirlistsize = 65535; + else + tif->tif_dirlistsize = 2 * tif->tif_dirnumber; + tif->tif_dirlist = new_dirlist; + } + + tif->tif_dirlist[tif->tif_dirnumber - 1] = diroff; + + return 1; +} + +/* + * Check the count field of a directory entry against a known value. The + * caller is expected to skip/ignore the tag if there is a mismatch. + */ +static int +CheckDirCount(TIFF* tif, TIFFDirEntry* dir, uint32 count) +{ + if ((uint64)count > dir->tdir_count) { + const TIFFField* fip = TIFFFieldWithTag(tif, dir->tdir_tag); + TIFFWarningExt(tif->tif_clientdata, tif->tif_name, + "incorrect count for field \"%s\" (" TIFF_UINT64_FORMAT ", expecting %u); tag ignored", + fip ? fip->field_name : "unknown tagname", + dir->tdir_count, count); + return (0); + } else if ((uint64)count < dir->tdir_count) { + const TIFFField* fip = TIFFFieldWithTag(tif, dir->tdir_tag); + TIFFWarningExt(tif->tif_clientdata, tif->tif_name, + "incorrect count for field \"%s\" (" TIFF_UINT64_FORMAT ", expecting %u); tag trimmed", + fip ? fip->field_name : "unknown tagname", + dir->tdir_count, count); + dir->tdir_count = count; + return (1); + } + return (1); +} + +/* + * Read IFD structure from the specified offset. If the pointer to + * nextdiroff variable has been specified, read it too. Function returns a + * number of fields in the directory or 0 if failed. + */ +static uint16 +TIFFFetchDirectory(TIFF* tif, uint64 diroff, TIFFDirEntry** pdir, + uint64 *nextdiroff) +{ + static const char module[] = "TIFFFetchDirectory"; + + void* origdir; + uint16 dircount16; + uint32 dirsize; + TIFFDirEntry* dir; + uint8* ma; + TIFFDirEntry* mb; + uint16 n; + + assert(pdir); + + tif->tif_diroff = diroff; + if (nextdiroff) + *nextdiroff = 0; + if (!isMapped(tif)) { + if (!SeekOK(tif, tif->tif_diroff)) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Seek error accessing TIFF directory", + tif->tif_name); + return 0; + } + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + if (!ReadOK(tif, &dircount16, sizeof (uint16))) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Can not read TIFF directory count", + tif->tif_name); + return 0; + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&dircount16); + if (dircount16>4096) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Sanity check on directory count failed, this is probably not a valid IFD offset"); + return 0; + } + dirsize = 12; + } else { + uint64 dircount64; + if (!ReadOK(tif, &dircount64, sizeof (uint64))) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Can not read TIFF directory count", + tif->tif_name); + return 0; + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong8(&dircount64); + if (dircount64>4096) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Sanity check on directory count failed, this is probably not a valid IFD offset"); + return 0; + } + dircount16 = (uint16)dircount64; + dirsize = 20; + } + origdir = _TIFFCheckMalloc(tif, dircount16, + dirsize, "to read TIFF directory"); + if (origdir == NULL) + return 0; + if (!ReadOK(tif, origdir, (tmsize_t)(dircount16*dirsize))) { + TIFFErrorExt(tif->tif_clientdata, module, + "%.100s: Can not read TIFF directory", + tif->tif_name); + _TIFFfree(origdir); + return 0; + } + /* + * Read offset to next directory for sequential scans if + * needed. + */ + if (nextdiroff) + { + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + uint32 nextdiroff32; + if (!ReadOK(tif, &nextdiroff32, sizeof(uint32))) + nextdiroff32 = 0; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&nextdiroff32); + *nextdiroff=nextdiroff32; + } else { + if (!ReadOK(tif, nextdiroff, sizeof(uint64))) + *nextdiroff = 0; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8(nextdiroff); + } + } + } else { + tmsize_t m; + tmsize_t off = (tmsize_t) tif->tif_diroff; + if ((uint64)off!=tif->tif_diroff) + { + TIFFErrorExt(tif->tif_clientdata,module,"Can not read TIFF directory count"); + return(0); + } + + /* + * Check for integer overflow when validating the dir_off, + * otherwise a very high offset may cause an OOB read and + * crash the client. Make two comparisons instead of + * + * off + sizeof(uint16) > tif->tif_size + * + * to avoid overflow. + */ + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + m=off+sizeof(uint16); + if ((mtif->tif_size)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Can not read TIFF directory count"); + return 0; + } else { + _TIFFmemcpy(&dircount16, tif->tif_base + off, + sizeof(uint16)); + } + off += sizeof (uint16); + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&dircount16); + if (dircount16>4096) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Sanity check on directory count failed, this is probably not a valid IFD offset"); + return 0; + } + dirsize = 12; + } + else + { + tmsize_t m; + uint64 dircount64; + m=off+sizeof(uint64); + if ((mtif->tif_size)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Can not read TIFF directory count"); + return 0; + } else { + _TIFFmemcpy(&dircount64, tif->tif_base + off, + sizeof(uint64)); + } + off += sizeof (uint64); + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong8(&dircount64); + if (dircount64>4096) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Sanity check on directory count failed, this is probably not a valid IFD offset"); + return 0; + } + dircount16 = (uint16)dircount64; + dirsize = 20; + } + if (dircount16 == 0 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Sanity check on directory count failed, zero tag directories not supported"); + return 0; + } + origdir = _TIFFCheckMalloc(tif, dircount16, + dirsize, + "to read TIFF directory"); + if (origdir == NULL) + return 0; + m=off+dircount16*dirsize; + if ((mtif->tif_size)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Can not read TIFF directory"); + _TIFFfree(origdir); + return 0; + } else { + _TIFFmemcpy(origdir, tif->tif_base + off, + dircount16 * dirsize); + } + if (nextdiroff) { + off += dircount16 * dirsize; + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + uint32 nextdiroff32; + m=off+sizeof(uint32); + if ((mtif->tif_size)) + nextdiroff32 = 0; + else + _TIFFmemcpy(&nextdiroff32, tif->tif_base + off, + sizeof (uint32)); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&nextdiroff32); + *nextdiroff = nextdiroff32; + } + else + { + m=off+sizeof(uint64); + if ((mtif->tif_size)) + *nextdiroff = 0; + else + _TIFFmemcpy(nextdiroff, tif->tif_base + off, + sizeof (uint64)); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8(nextdiroff); + } + } + } + dir = (TIFFDirEntry*)_TIFFCheckMalloc(tif, dircount16, + sizeof(TIFFDirEntry), + "to read TIFF directory"); + if (dir==0) + { + _TIFFfree(origdir); + return 0; + } + ma=(uint8*)origdir; + mb=dir; + for (n=0; ntif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)ma); + mb->tdir_tag=*(uint16*)ma; + ma+=sizeof(uint16); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)ma); + mb->tdir_type=*(uint16*)ma; + ma+=sizeof(uint16); + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)ma); + mb->tdir_count=(uint64)(*(uint32*)ma); + ma+=sizeof(uint32); + *(uint32*)(&mb->tdir_offset)=*(uint32*)ma; + ma+=sizeof(uint32); + } + else + { + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)ma); + mb->tdir_count=TIFFReadUInt64(ma); + ma+=sizeof(uint64); + mb->tdir_offset.toff_long8=TIFFReadUInt64(ma); + ma+=sizeof(uint64); + } + mb++; + } + _TIFFfree(origdir); + *pdir = dir; + return dircount16; +} + +/* + * Fetch a tag that is not handled by special case code. + */ +static int +TIFFFetchNormalTag(TIFF* tif, TIFFDirEntry* dp, int recover) +{ + static const char module[] = "TIFFFetchNormalTag"; + enum TIFFReadDirEntryErr err; + uint32 fii; + const TIFFField* fip = NULL; + TIFFReadDirectoryFindFieldInfo(tif,dp->tdir_tag,&fii); + if( fii == FAILED_FII ) + { + TIFFErrorExt(tif->tif_clientdata, "TIFFFetchNormalTag", + "No definition found for tag %d", + dp->tdir_tag); + return 0; + } + fip=tif->tif_fields[fii]; + assert(fip != NULL); /* should not happen */ + assert(fip->set_field_type!=TIFF_SETGET_OTHER); /* if so, we shouldn't arrive here but deal with this in specialized code */ + assert(fip->set_field_type!=TIFF_SETGET_INT); /* if so, we shouldn't arrive here as this is only the case for pseudo-tags */ + err=TIFFReadDirEntryErrOk; + switch (fip->set_field_type) + { + case TIFF_SETGET_UNDEFINED: + break; + case TIFF_SETGET_ASCII: + { + uint8* data; + assert(fip->field_passcount==0); + err=TIFFReadDirEntryByteArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + uint8* ma; + uint32 mb; + int n; + ma=data; + mb=0; + while (mb<(uint32)dp->tdir_count) + { + if (*ma==0) + break; + ma++; + mb++; + } + if (mb+1<(uint32)dp->tdir_count) + TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" contains null byte in value; value incorrectly truncated during reading due to implementation limitations",fip->field_name); + else if (mb+1>(uint32)dp->tdir_count) + { + uint8* o; + TIFFWarningExt(tif->tif_clientdata,module,"ASCII value for tag \"%s\" does not end in null byte",fip->field_name); + if ((uint32)dp->tdir_count+1!=dp->tdir_count+1) + o=NULL; + else + o=_TIFFmalloc((uint32)dp->tdir_count+1); + if (o==NULL) + { + if (data!=NULL) + _TIFFfree(data); + return(0); + } + _TIFFmemcpy(o,data,(uint32)dp->tdir_count); + o[(uint32)dp->tdir_count]=0; + if (data!=0) + _TIFFfree(data); + data=o; + } + n=TIFFSetField(tif,dp->tdir_tag,data); + if (data!=0) + _TIFFfree(data); + if (!n) + return(0); + } + } + break; + case TIFF_SETGET_UINT8: + { + uint8 data=0; + assert(fip->field_readcount==1); + assert(fip->field_passcount==0); + err=TIFFReadDirEntryByte(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + if (!TIFFSetField(tif,dp->tdir_tag,data)) + return(0); + } + } + break; + case TIFF_SETGET_UINT16: + { + uint16 data; + assert(fip->field_readcount==1); + assert(fip->field_passcount==0); + err=TIFFReadDirEntryShort(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + if (!TIFFSetField(tif,dp->tdir_tag,data)) + return(0); + } + } + break; + case TIFF_SETGET_UINT32: + { + uint32 data; + assert(fip->field_readcount==1); + assert(fip->field_passcount==0); + err=TIFFReadDirEntryLong(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + if (!TIFFSetField(tif,dp->tdir_tag,data)) + return(0); + } + } + break; + case TIFF_SETGET_UINT64: + { + uint64 data; + assert(fip->field_readcount==1); + assert(fip->field_passcount==0); + err=TIFFReadDirEntryLong8(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + if (!TIFFSetField(tif,dp->tdir_tag,data)) + return(0); + } + } + break; + case TIFF_SETGET_FLOAT: + { + float data; + assert(fip->field_readcount==1); + assert(fip->field_passcount==0); + err=TIFFReadDirEntryFloat(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + if (!TIFFSetField(tif,dp->tdir_tag,data)) + return(0); + } + } + break; + case TIFF_SETGET_DOUBLE: + { + double data; + assert(fip->field_readcount==1); + assert(fip->field_passcount==0); + err=TIFFReadDirEntryDouble(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + if (!TIFFSetField(tif,dp->tdir_tag,data)) + return(0); + } + } + break; + case TIFF_SETGET_IFD8: + { + uint64 data; + assert(fip->field_readcount==1); + assert(fip->field_passcount==0); + err=TIFFReadDirEntryIfd8(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + if (!TIFFSetField(tif,dp->tdir_tag,data)) + return(0); + } + } + break; + case TIFF_SETGET_UINT16_PAIR: + { + uint16* data; + assert(fip->field_readcount==2); + assert(fip->field_passcount==0); + if (dp->tdir_count!=2) { + TIFFWarningExt(tif->tif_clientdata,module, + "incorrect count for field \"%s\", expected 2, got %d", + fip->field_name,(int)dp->tdir_count); + return(0); + } + err=TIFFReadDirEntryShortArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,data[0],data[1]); + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + case TIFF_SETGET_C0_UINT8: + { + uint8* data; + assert(fip->field_readcount>=1); + assert(fip->field_passcount==0); + if (dp->tdir_count!=(uint64)fip->field_readcount) { + TIFFWarningExt(tif->tif_clientdata,module, + "incorrect count for field \"%s\", expected %d, got %d", + fip->field_name,(int) fip->field_readcount, (int)dp->tdir_count); + return 0; + } + else + { + err=TIFFReadDirEntryByteArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + } + break; + case TIFF_SETGET_C0_UINT16: + { + uint16* data; + assert(fip->field_readcount>=1); + assert(fip->field_passcount==0); + if (dp->tdir_count!=(uint64)fip->field_readcount) + /* corrupt file */; + else + { + err=TIFFReadDirEntryShortArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + } + break; + case TIFF_SETGET_C0_UINT32: + { + uint32* data; + assert(fip->field_readcount>=1); + assert(fip->field_passcount==0); + if (dp->tdir_count!=(uint64)fip->field_readcount) + /* corrupt file */; + else + { + err=TIFFReadDirEntryLongArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + } + break; + case TIFF_SETGET_C0_FLOAT: + { + float* data; + assert(fip->field_readcount>=1); + assert(fip->field_passcount==0); + if (dp->tdir_count!=(uint64)fip->field_readcount) + /* corrupt file */; + else + { + err=TIFFReadDirEntryFloatArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + } + break; + case TIFF_SETGET_C16_ASCII: + { + uint8* data; + assert(fip->field_readcount==TIFF_VARIABLE); + assert(fip->field_passcount==1); + if (dp->tdir_count>0xFFFF) + err=TIFFReadDirEntryErrCount; + else + { + err=TIFFReadDirEntryByteArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + } + break; + case TIFF_SETGET_C16_UINT8: + { + uint8* data; + assert(fip->field_readcount==TIFF_VARIABLE); + assert(fip->field_passcount==1); + if (dp->tdir_count>0xFFFF) + err=TIFFReadDirEntryErrCount; + else + { + err=TIFFReadDirEntryByteArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + } + break; + case TIFF_SETGET_C16_UINT16: + { + uint16* data; + assert(fip->field_readcount==TIFF_VARIABLE); + assert(fip->field_passcount==1); + if (dp->tdir_count>0xFFFF) + err=TIFFReadDirEntryErrCount; + else + { + err=TIFFReadDirEntryShortArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + } + break; + case TIFF_SETGET_C16_UINT32: + { + uint32* data; + assert(fip->field_readcount==TIFF_VARIABLE); + assert(fip->field_passcount==1); + if (dp->tdir_count>0xFFFF) + err=TIFFReadDirEntryErrCount; + else + { + err=TIFFReadDirEntryLongArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + } + break; + case TIFF_SETGET_C16_UINT64: + { + uint64* data; + assert(fip->field_readcount==TIFF_VARIABLE); + assert(fip->field_passcount==1); + if (dp->tdir_count>0xFFFF) + err=TIFFReadDirEntryErrCount; + else + { + err=TIFFReadDirEntryLong8Array(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + } + break; + case TIFF_SETGET_C16_FLOAT: + { + float* data; + assert(fip->field_readcount==TIFF_VARIABLE); + assert(fip->field_passcount==1); + if (dp->tdir_count>0xFFFF) + err=TIFFReadDirEntryErrCount; + else + { + err=TIFFReadDirEntryFloatArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + } + break; + case TIFF_SETGET_C16_DOUBLE: + { + double* data; + assert(fip->field_readcount==TIFF_VARIABLE); + assert(fip->field_passcount==1); + if (dp->tdir_count>0xFFFF) + err=TIFFReadDirEntryErrCount; + else + { + err=TIFFReadDirEntryDoubleArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + } + break; + case TIFF_SETGET_C16_IFD8: + { + uint64* data; + assert(fip->field_readcount==TIFF_VARIABLE); + assert(fip->field_passcount==1); + if (dp->tdir_count>0xFFFF) + err=TIFFReadDirEntryErrCount; + else + { + err=TIFFReadDirEntryIfd8Array(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint16)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + } + break; + case TIFF_SETGET_C32_ASCII: + { + uint8* data; + assert(fip->field_readcount==TIFF_VARIABLE2); + assert(fip->field_passcount==1); + err=TIFFReadDirEntryByteArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + case TIFF_SETGET_C32_UINT8: + { + uint8* data; + assert(fip->field_readcount==TIFF_VARIABLE2); + assert(fip->field_passcount==1); + err=TIFFReadDirEntryByteArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + case TIFF_SETGET_C32_SINT8: + { + int8* data = NULL; + assert(fip->field_readcount==TIFF_VARIABLE2); + assert(fip->field_passcount==1); + err=TIFFReadDirEntrySbyteArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + case TIFF_SETGET_C32_UINT16: + { + uint16* data; + assert(fip->field_readcount==TIFF_VARIABLE2); + assert(fip->field_passcount==1); + err=TIFFReadDirEntryShortArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + case TIFF_SETGET_C32_SINT16: + { + int16* data = NULL; + assert(fip->field_readcount==TIFF_VARIABLE2); + assert(fip->field_passcount==1); + err=TIFFReadDirEntrySshortArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + case TIFF_SETGET_C32_UINT32: + { + uint32* data; + assert(fip->field_readcount==TIFF_VARIABLE2); + assert(fip->field_passcount==1); + err=TIFFReadDirEntryLongArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + case TIFF_SETGET_C32_SINT32: + { + int32* data = NULL; + assert(fip->field_readcount==TIFF_VARIABLE2); + assert(fip->field_passcount==1); + err=TIFFReadDirEntrySlongArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + case TIFF_SETGET_C32_UINT64: + { + uint64* data; + assert(fip->field_readcount==TIFF_VARIABLE2); + assert(fip->field_passcount==1); + err=TIFFReadDirEntryLong8Array(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + case TIFF_SETGET_C32_SINT64: + { + int64* data = NULL; + assert(fip->field_readcount==TIFF_VARIABLE2); + assert(fip->field_passcount==1); + err=TIFFReadDirEntrySlong8Array(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + case TIFF_SETGET_C32_FLOAT: + { + float* data; + assert(fip->field_readcount==TIFF_VARIABLE2); + assert(fip->field_passcount==1); + err=TIFFReadDirEntryFloatArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + case TIFF_SETGET_C32_DOUBLE: + { + double* data; + assert(fip->field_readcount==TIFF_VARIABLE2); + assert(fip->field_passcount==1); + err=TIFFReadDirEntryDoubleArray(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + case TIFF_SETGET_C32_IFD8: + { + uint64* data; + assert(fip->field_readcount==TIFF_VARIABLE2); + assert(fip->field_passcount==1); + err=TIFFReadDirEntryIfd8Array(tif,dp,&data); + if (err==TIFFReadDirEntryErrOk) + { + int m; + m=TIFFSetField(tif,dp->tdir_tag,(uint32)(dp->tdir_count),data); + if (data!=0) + _TIFFfree(data); + if (!m) + return(0); + } + } + break; + default: + assert(0); /* we should never get here */ + break; + } + if (err!=TIFFReadDirEntryErrOk) + { + TIFFReadDirEntryOutputErr(tif,err,module,fip->field_name,recover); + return(0); + } + return(1); +} + +/* + * Fetch a set of offsets or lengths. + * While this routine says "strips", in fact it's also used for tiles. + */ +static int +TIFFFetchStripThing(TIFF* tif, TIFFDirEntry* dir, uint32 nstrips, uint64** lpp) +{ + static const char module[] = "TIFFFetchStripThing"; + enum TIFFReadDirEntryErr err; + uint64* data; + err=TIFFReadDirEntryLong8Array(tif,dir,&data); + if (err!=TIFFReadDirEntryErrOk) + { + const TIFFField* fip = TIFFFieldWithTag(tif,dir->tdir_tag); + TIFFReadDirEntryOutputErr(tif,err,module,fip ? fip->field_name : "unknown tagname",0); + return(0); + } + if (dir->tdir_count!=(uint64)nstrips) + { + uint64* resizeddata; + resizeddata=(uint64*)_TIFFCheckMalloc(tif,nstrips,sizeof(uint64),"for strip array"); + if (resizeddata==0) { + _TIFFfree(data); + return(0); + } + if (dir->tdir_count<(uint64)nstrips) + { + _TIFFmemcpy(resizeddata,data,(uint32)dir->tdir_count*sizeof(uint64)); + _TIFFmemset(resizeddata+(uint32)dir->tdir_count,0,(nstrips-(uint32)dir->tdir_count)*sizeof(uint64)); + } + else + _TIFFmemcpy(resizeddata,data,nstrips*sizeof(uint64)); + _TIFFfree(data); + data=resizeddata; + } + *lpp=data; + return(1); +} + +/* + * Fetch and set the SubjectDistance EXIF tag. + */ +static int +TIFFFetchSubjectDistance(TIFF* tif, TIFFDirEntry* dir) +{ + static const char module[] = "TIFFFetchSubjectDistance"; + enum TIFFReadDirEntryErr err; + UInt64Aligned_t m; + m.l=0; + assert(sizeof(double)==8); + assert(sizeof(uint64)==8); + assert(sizeof(uint32)==4); + if (dir->tdir_count!=1) + err=TIFFReadDirEntryErrCount; + else if (dir->tdir_type!=TIFF_RATIONAL) + err=TIFFReadDirEntryErrType; + else + { + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + uint32 offset; + offset=*(uint32*)(&dir->tdir_offset); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&offset); + err=TIFFReadDirEntryData(tif,offset,8,m.i); + } + else + { + m.l=dir->tdir_offset.toff_long8; + err=TIFFReadDirEntryErrOk; + } + } + if (err==TIFFReadDirEntryErrOk) + { + double n; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong(m.i,2); + if (m.i[0]==0) + n=0.0; + else if (m.i[0]==0xFFFFFFFF) + /* + * XXX: Numerator 0xFFFFFFFF means that we have infinite + * distance. Indicate that with a negative floating point + * SubjectDistance value. + */ + n=-1.0; + else + n=(double)m.i[0]/(double)m.i[1]; + return(TIFFSetField(tif,dir->tdir_tag,n)); + } + else + { + TIFFReadDirEntryOutputErr(tif,err,module,"SubjectDistance",TRUE); + return(0); + } +} + +/* + * Replace a single strip (tile) of uncompressed data by multiple strips + * (tiles), each approximately STRIP_SIZE_DEFAULT bytes. This is useful for + * dealing with large images or for dealing with machines with a limited + * amount memory. + */ +static void +ChopUpSingleUncompressedStrip(TIFF* tif) +{ + register TIFFDirectory *td = &tif->tif_dir; + uint64 bytecount; + uint64 offset; + uint32 rowblock; + uint64 rowblockbytes; + uint64 stripbytes; + uint32 strip; + uint64 nstrips64; + uint32 nstrips32; + uint32 rowsperstrip; + uint64* newcounts; + uint64* newoffsets; + + bytecount = td->td_stripbytecount[0]; + offset = td->td_stripoffset[0]; + assert(td->td_planarconfig == PLANARCONFIG_CONTIG); + if ((td->td_photometric == PHOTOMETRIC_YCBCR)&& + (!isUpSampled(tif))) + rowblock = td->td_ycbcrsubsampling[1]; + else + rowblock = 1; + rowblockbytes = TIFFVTileSize64(tif, rowblock); + /* + * Make the rows hold at least one scanline, but fill specified amount + * of data if possible. + */ + if (rowblockbytes > STRIP_SIZE_DEFAULT) { + stripbytes = rowblockbytes; + rowsperstrip = rowblock; + } else if (rowblockbytes > 0 ) { + uint32 rowblocksperstrip; + rowblocksperstrip = (uint32) (STRIP_SIZE_DEFAULT / rowblockbytes); + rowsperstrip = rowblocksperstrip * rowblock; + stripbytes = rowblocksperstrip * rowblockbytes; + } + else + return; + + /* + * never increase the number of strips in an image + */ + if (rowsperstrip >= td->td_rowsperstrip) + return; + nstrips64 = TIFFhowmany_64(bytecount, stripbytes); + if ((nstrips64==0)||(nstrips64>0xFFFFFFFF)) /* something is wonky, do nothing. */ + return; + nstrips32 = (uint32)nstrips64; + + newcounts = (uint64*) _TIFFCheckMalloc(tif, nstrips32, sizeof (uint64), + "for chopped \"StripByteCounts\" array"); + newoffsets = (uint64*) _TIFFCheckMalloc(tif, nstrips32, sizeof (uint64), + "for chopped \"StripOffsets\" array"); + if (newcounts == NULL || newoffsets == NULL) { + /* + * Unable to allocate new strip information, give up and use + * the original one strip information. + */ + if (newcounts != NULL) + _TIFFfree(newcounts); + if (newoffsets != NULL) + _TIFFfree(newoffsets); + return; + } + /* + * Fill the strip information arrays with new bytecounts and offsets + * that reflect the broken-up format. + */ + for (strip = 0; strip < nstrips32; strip++) { + if (stripbytes > bytecount) + stripbytes = bytecount; + newcounts[strip] = stripbytes; + newoffsets[strip] = offset; + offset += stripbytes; + bytecount -= stripbytes; + } + /* + * Replace old single strip info with multi-strip info. + */ + td->td_stripsperimage = td->td_nstrips = nstrips32; + TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip); + + _TIFFfree(td->td_stripbytecount); + _TIFFfree(td->td_stripoffset); + td->td_stripbytecount = newcounts; + td->td_stripoffset = newoffsets; + td->td_stripbytecountsorted = 1; +} + +int _TIFFFillStriles( TIFF *tif ) +{ +#if defined(DEFER_STRILE_LOAD) + register TIFFDirectory *td = &tif->tif_dir; + int return_value = 1; + + if( td->td_stripoffset != NULL ) + return 1; + + if( td->td_stripoffset_entry.tdir_count == 0 ) + return 0; + + if (!TIFFFetchStripThing(tif,&(td->td_stripoffset_entry), + td->td_nstrips,&td->td_stripoffset)) + { + return_value = 0; + } + + if (!TIFFFetchStripThing(tif,&(td->td_stripbytecount_entry), + td->td_nstrips,&td->td_stripbytecount)) + { + return_value = 0; + } + + _TIFFmemset( &(td->td_stripoffset_entry), 0, sizeof(TIFFDirEntry)); + _TIFFmemset( &(td->td_stripbytecount_entry), 0, sizeof(TIFFDirEntry)); + + if (tif->tif_dir.td_nstrips > 1 && return_value == 1 ) { + uint32 strip; + + tif->tif_dir.td_stripbytecountsorted = 1; + for (strip = 1; strip < tif->tif_dir.td_nstrips; strip++) { + if (tif->tif_dir.td_stripoffset[strip - 1] > + tif->tif_dir.td_stripoffset[strip]) { + tif->tif_dir.td_stripbytecountsorted = 0; + break; + } + } + } + + return return_value; +#else /* !defined(DEFER_STRILE_LOAD) */ + (void) tif; + return 1; +#endif +} + + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dirwrite.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dirwrite.c new file mode 100644 index 000000000..fa20609e2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dirwrite.c @@ -0,0 +1,2910 @@ +/* $Id: tif_dirwrite.c,v 1.77 2012-07-06 19:18:31 bfriesen Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Directory Write Support Routines. + */ +#include "tiffiop.h" + +#ifdef HAVE_IEEEFP +#define TIFFCvtNativeToIEEEFloat(tif, n, fp) +#define TIFFCvtNativeToIEEEDouble(tif, n, dp) +#else +extern void TIFFCvtNativeToIEEEFloat(TIFF* tif, uint32 n, float* fp); +extern void TIFFCvtNativeToIEEEDouble(TIFF* tif, uint32 n, double* dp); +#endif + +static int TIFFWriteDirectorySec(TIFF* tif, int isimage, int imagedone, uint64* pdiroff); + +static int TIFFWriteDirectoryTagSampleformatArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, double* value); +#if 0 +static int TIFFWriteDirectoryTagSampleformatPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value); +#endif + +static int TIFFWriteDirectoryTagAscii(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, char* value); +static int TIFFWriteDirectoryTagUndefinedArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value); +#ifdef notdef +static int TIFFWriteDirectoryTagByte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint8 value); +#endif +static int TIFFWriteDirectoryTagByteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value); +#if 0 +static int TIFFWriteDirectoryTagBytePerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint8 value); +#endif +#ifdef notdef +static int TIFFWriteDirectoryTagSbyte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int8 value); +#endif +static int TIFFWriteDirectoryTagSbyteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int8* value); +#if 0 +static int TIFFWriteDirectoryTagSbytePerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int8 value); +#endif +static int TIFFWriteDirectoryTagShort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 value); +static int TIFFWriteDirectoryTagShortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint16* value); +static int TIFFWriteDirectoryTagShortPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 value); +#ifdef notdef +static int TIFFWriteDirectoryTagSshort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int16 value); +#endif +static int TIFFWriteDirectoryTagSshortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int16* value); +#if 0 +static int TIFFWriteDirectoryTagSshortPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int16 value); +#endif +static int TIFFWriteDirectoryTagLong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value); +static int TIFFWriteDirectoryTagLongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value); +#if 0 +static int TIFFWriteDirectoryTagLongPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value); +#endif +#ifdef notdef +static int TIFFWriteDirectoryTagSlong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int32 value); +#endif +static int TIFFWriteDirectoryTagSlongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int32* value); +#if 0 +static int TIFFWriteDirectoryTagSlongPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int32 value); +#endif +#ifdef notdef +static int TIFFWriteDirectoryTagLong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint64 value); +#endif +static int TIFFWriteDirectoryTagLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value); +#ifdef notdef +static int TIFFWriteDirectoryTagSlong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int64 value); +#endif +static int TIFFWriteDirectoryTagSlong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int64* value); +static int TIFFWriteDirectoryTagRational(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value); +static int TIFFWriteDirectoryTagRationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value); +static int TIFFWriteDirectoryTagSrationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value); +#ifdef notdef +static int TIFFWriteDirectoryTagFloat(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, float value); +#endif +static int TIFFWriteDirectoryTagFloatArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value); +#if 0 +static int TIFFWriteDirectoryTagFloatPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, float value); +#endif +#ifdef notdef +static int TIFFWriteDirectoryTagDouble(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value); +#endif +static int TIFFWriteDirectoryTagDoubleArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, double* value); +#if 0 +static int TIFFWriteDirectoryTagDoublePerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value); +#endif +static int TIFFWriteDirectoryTagIfdArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value); +#ifdef notdef +static int TIFFWriteDirectoryTagIfd8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value); +#endif +static int TIFFWriteDirectoryTagShortLong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value); +static int TIFFWriteDirectoryTagLongLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value); +static int TIFFWriteDirectoryTagIfdIfd8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value); +#ifdef notdef +static int TIFFWriteDirectoryTagShortLongLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value); +#endif +static int TIFFWriteDirectoryTagColormap(TIFF* tif, uint32* ndir, TIFFDirEntry* dir); +static int TIFFWriteDirectoryTagTransferfunction(TIFF* tif, uint32* ndir, TIFFDirEntry* dir); +static int TIFFWriteDirectoryTagSubifd(TIFF* tif, uint32* ndir, TIFFDirEntry* dir); + +static int TIFFWriteDirectoryTagCheckedAscii(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, char* value); +static int TIFFWriteDirectoryTagCheckedUndefinedArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value); +#ifdef notdef +static int TIFFWriteDirectoryTagCheckedByte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint8 value); +#endif +static int TIFFWriteDirectoryTagCheckedByteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value); +#ifdef notdef +static int TIFFWriteDirectoryTagCheckedSbyte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int8 value); +#endif +static int TIFFWriteDirectoryTagCheckedSbyteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int8* value); +static int TIFFWriteDirectoryTagCheckedShort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 value); +static int TIFFWriteDirectoryTagCheckedShortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint16* value); +#ifdef notdef +static int TIFFWriteDirectoryTagCheckedSshort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int16 value); +#endif +static int TIFFWriteDirectoryTagCheckedSshortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int16* value); +static int TIFFWriteDirectoryTagCheckedLong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value); +static int TIFFWriteDirectoryTagCheckedLongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value); +#ifdef notdef +static int TIFFWriteDirectoryTagCheckedSlong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int32 value); +#endif +static int TIFFWriteDirectoryTagCheckedSlongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int32* value); +#ifdef notdef +static int TIFFWriteDirectoryTagCheckedLong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint64 value); +#endif +static int TIFFWriteDirectoryTagCheckedLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value); +#ifdef notdef +static int TIFFWriteDirectoryTagCheckedSlong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int64 value); +#endif +static int TIFFWriteDirectoryTagCheckedSlong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int64* value); +static int TIFFWriteDirectoryTagCheckedRational(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value); +static int TIFFWriteDirectoryTagCheckedRationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value); +static int TIFFWriteDirectoryTagCheckedSrationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value); +#ifdef notdef +static int TIFFWriteDirectoryTagCheckedFloat(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, float value); +#endif +static int TIFFWriteDirectoryTagCheckedFloatArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value); +#ifdef notdef +static int TIFFWriteDirectoryTagCheckedDouble(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value); +#endif +static int TIFFWriteDirectoryTagCheckedDoubleArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, double* value); +static int TIFFWriteDirectoryTagCheckedIfdArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value); +static int TIFFWriteDirectoryTagCheckedIfd8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value); + +static int TIFFWriteDirectoryTagData(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 datatype, uint32 count, uint32 datalength, void* data); + +static int TIFFLinkDirectory(TIFF*); + +/* + * Write the contents of the current directory + * to the specified file. This routine doesn't + * handle overwriting a directory with auxiliary + * storage that's been changed. + */ +int +TIFFWriteDirectory(TIFF* tif) +{ + return TIFFWriteDirectorySec(tif,TRUE,TRUE,NULL); +} + +/* + * Similar to TIFFWriteDirectory(), writes the directory out + * but leaves all data structures in memory so that it can be + * written again. This will make a partially written TIFF file + * readable before it is successfully completed/closed. + */ +int +TIFFCheckpointDirectory(TIFF* tif) +{ + int rc; + /* Setup the strips arrays, if they haven't already been. */ + if (tif->tif_dir.td_stripoffset == NULL) + (void) TIFFSetupStrips(tif); + rc = TIFFWriteDirectorySec(tif,TRUE,FALSE,NULL); + (void) TIFFSetWriteOffset(tif, TIFFSeekFile(tif, 0, SEEK_END)); + return rc; +} + +int +TIFFWriteCustomDirectory(TIFF* tif, uint64* pdiroff) +{ + return TIFFWriteDirectorySec(tif,FALSE,FALSE,pdiroff); +} + +/* + * Similar to TIFFWriteDirectory(), but if the directory has already + * been written once, it is relocated to the end of the file, in case it + * has changed in size. Note that this will result in the loss of the + * previously used directory space. + */ +int +TIFFRewriteDirectory( TIFF *tif ) +{ + static const char module[] = "TIFFRewriteDirectory"; + + /* We don't need to do anything special if it hasn't been written. */ + if( tif->tif_diroff == 0 ) + return TIFFWriteDirectory( tif ); + + /* + * Find and zero the pointer to this directory, so that TIFFLinkDirectory + * will cause it to be added after this directories current pre-link. + */ + + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + if (tif->tif_header.classic.tiff_diroff == tif->tif_diroff) + { + tif->tif_header.classic.tiff_diroff = 0; + tif->tif_diroff = 0; + + TIFFSeekFile(tif,4,SEEK_SET); + if (!WriteOK(tif, &(tif->tif_header.classic.tiff_diroff),4)) + { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Error updating TIFF header"); + return (0); + } + } + else + { + uint32 nextdir; + nextdir = tif->tif_header.classic.tiff_diroff; + while(1) { + uint16 dircount; + uint32 nextnextdir; + + if (!SeekOK(tif, nextdir) || + !ReadOK(tif, &dircount, 2)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error fetching directory count"); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&dircount); + (void) TIFFSeekFile(tif, + nextdir+2+dircount*12, SEEK_SET); + if (!ReadOK(tif, &nextnextdir, 4)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error fetching directory link"); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong(&nextnextdir); + if (nextnextdir==tif->tif_diroff) + { + uint32 m; + m=0; + (void) TIFFSeekFile(tif, + nextdir+2+dircount*12, SEEK_SET); + if (!WriteOK(tif, &m, 4)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error writing directory link"); + return (0); + } + tif->tif_diroff=0; + break; + } + nextdir=nextnextdir; + } + } + } + else + { + if (tif->tif_header.big.tiff_diroff == tif->tif_diroff) + { + tif->tif_header.big.tiff_diroff = 0; + tif->tif_diroff = 0; + + TIFFSeekFile(tif,8,SEEK_SET); + if (!WriteOK(tif, &(tif->tif_header.big.tiff_diroff),8)) + { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Error updating TIFF header"); + return (0); + } + } + else + { + uint64 nextdir; + nextdir = tif->tif_header.big.tiff_diroff; + while(1) { + uint64 dircount64; + uint16 dircount; + uint64 nextnextdir; + + if (!SeekOK(tif, nextdir) || + !ReadOK(tif, &dircount64, 8)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error fetching directory count"); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong8(&dircount64); + if (dircount64>0xFFFF) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Sanity check on tag count failed, likely corrupt TIFF"); + return (0); + } + dircount=(uint16)dircount64; + (void) TIFFSeekFile(tif, + nextdir+8+dircount*20, SEEK_SET); + if (!ReadOK(tif, &nextnextdir, 8)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error fetching directory link"); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong8(&nextnextdir); + if (nextnextdir==tif->tif_diroff) + { + uint64 m; + m=0; + (void) TIFFSeekFile(tif, + nextdir+8+dircount*20, SEEK_SET); + if (!WriteOK(tif, &m, 8)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error writing directory link"); + return (0); + } + tif->tif_diroff=0; + break; + } + nextdir=nextnextdir; + } + } + } + + /* + * Now use TIFFWriteDirectory() normally. + */ + + return TIFFWriteDirectory( tif ); +} + +static int +TIFFWriteDirectorySec(TIFF* tif, int isimage, int imagedone, uint64* pdiroff) +{ + static const char module[] = "TIFFWriteDirectorySec"; + uint32 ndir; + TIFFDirEntry* dir; + uint32 dirsize; + void* dirmem; + uint32 m; + if (tif->tif_mode == O_RDONLY) + return (1); + + _TIFFFillStriles( tif ); + + /* + * Clear write state so that subsequent images with + * different characteristics get the right buffers + * setup for them. + */ + if (imagedone) + { + if (tif->tif_flags & TIFF_POSTENCODE) + { + tif->tif_flags &= ~TIFF_POSTENCODE; + if (!(*tif->tif_postencode)(tif)) + { + TIFFErrorExt(tif->tif_clientdata,module, + "Error post-encoding before directory write"); + return (0); + } + } + (*tif->tif_close)(tif); /* shutdown encoder */ + /* + * Flush any data that might have been written + * by the compression close+cleanup routines. But + * be careful not to write stuff if we didn't add data + * in the previous steps as the "rawcc" data may well be + * a previously read tile/strip in mixed read/write mode. + */ + if (tif->tif_rawcc > 0 + && (tif->tif_flags & TIFF_BEENWRITING) != 0 ) + { + if( !TIFFFlushData1(tif) ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Error flushing data before directory write"); + return (0); + } + } + if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) + { + _TIFFfree(tif->tif_rawdata); + tif->tif_rawdata = NULL; + tif->tif_rawcc = 0; + tif->tif_rawdatasize = 0; + tif->tif_rawdataoff = 0; + tif->tif_rawdataloaded = 0; + } + tif->tif_flags &= ~(TIFF_BEENWRITING|TIFF_BUFFERSETUP); + } + dir=NULL; + dirmem=NULL; + dirsize=0; + while (1) + { + ndir=0; + if (isimage) + { + if (TIFFFieldSet(tif,FIELD_IMAGEDIMENSIONS)) + { + if (!TIFFWriteDirectoryTagShortLong(tif,&ndir,dir,TIFFTAG_IMAGEWIDTH,tif->tif_dir.td_imagewidth)) + goto bad; + if (!TIFFWriteDirectoryTagShortLong(tif,&ndir,dir,TIFFTAG_IMAGELENGTH,tif->tif_dir.td_imagelength)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_TILEDIMENSIONS)) + { + if (!TIFFWriteDirectoryTagShortLong(tif,&ndir,dir,TIFFTAG_TILEWIDTH,tif->tif_dir.td_tilewidth)) + goto bad; + if (!TIFFWriteDirectoryTagShortLong(tif,&ndir,dir,TIFFTAG_TILELENGTH,tif->tif_dir.td_tilelength)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_RESOLUTION)) + { + if (!TIFFWriteDirectoryTagRational(tif,&ndir,dir,TIFFTAG_XRESOLUTION,tif->tif_dir.td_xresolution)) + goto bad; + if (!TIFFWriteDirectoryTagRational(tif,&ndir,dir,TIFFTAG_YRESOLUTION,tif->tif_dir.td_yresolution)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_POSITION)) + { + if (!TIFFWriteDirectoryTagRational(tif,&ndir,dir,TIFFTAG_XPOSITION,tif->tif_dir.td_xposition)) + goto bad; + if (!TIFFWriteDirectoryTagRational(tif,&ndir,dir,TIFFTAG_YPOSITION,tif->tif_dir.td_yposition)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_SUBFILETYPE)) + { + if (!TIFFWriteDirectoryTagLong(tif,&ndir,dir,TIFFTAG_SUBFILETYPE,tif->tif_dir.td_subfiletype)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_BITSPERSAMPLE)) + { + if (!TIFFWriteDirectoryTagShortPerSample(tif,&ndir,dir,TIFFTAG_BITSPERSAMPLE,tif->tif_dir.td_bitspersample)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_COMPRESSION)) + { + if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_COMPRESSION,tif->tif_dir.td_compression)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_PHOTOMETRIC)) + { + if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_PHOTOMETRIC,tif->tif_dir.td_photometric)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_THRESHHOLDING)) + { + if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_THRESHHOLDING,tif->tif_dir.td_threshholding)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_FILLORDER)) + { + if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_FILLORDER,tif->tif_dir.td_fillorder)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_ORIENTATION)) + { + if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_ORIENTATION,tif->tif_dir.td_orientation)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_SAMPLESPERPIXEL)) + { + if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_SAMPLESPERPIXEL,tif->tif_dir.td_samplesperpixel)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_ROWSPERSTRIP)) + { + if (!TIFFWriteDirectoryTagShortLong(tif,&ndir,dir,TIFFTAG_ROWSPERSTRIP,tif->tif_dir.td_rowsperstrip)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_MINSAMPLEVALUE)) + { + if (!TIFFWriteDirectoryTagShortPerSample(tif,&ndir,dir,TIFFTAG_MINSAMPLEVALUE,tif->tif_dir.td_minsamplevalue)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_MAXSAMPLEVALUE)) + { + if (!TIFFWriteDirectoryTagShortPerSample(tif,&ndir,dir,TIFFTAG_MAXSAMPLEVALUE,tif->tif_dir.td_maxsamplevalue)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_PLANARCONFIG)) + { + if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_PLANARCONFIG,tif->tif_dir.td_planarconfig)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_RESOLUTIONUNIT)) + { + if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_RESOLUTIONUNIT,tif->tif_dir.td_resolutionunit)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_PAGENUMBER)) + { + if (!TIFFWriteDirectoryTagShortArray(tif,&ndir,dir,TIFFTAG_PAGENUMBER,2,&tif->tif_dir.td_pagenumber[0])) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_STRIPBYTECOUNTS)) + { + if (!isTiled(tif)) + { + if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_STRIPBYTECOUNTS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripbytecount)) + goto bad; + } + else + { + if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_TILEBYTECOUNTS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripbytecount)) + goto bad; + } + } + if (TIFFFieldSet(tif,FIELD_STRIPOFFSETS)) + { + if (!isTiled(tif)) + { + if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_STRIPOFFSETS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripoffset)) + goto bad; + } + else + { + if (!TIFFWriteDirectoryTagLongLong8Array(tif,&ndir,dir,TIFFTAG_TILEOFFSETS,tif->tif_dir.td_nstrips,tif->tif_dir.td_stripoffset)) + goto bad; + } + } + if (TIFFFieldSet(tif,FIELD_COLORMAP)) + { + if (!TIFFWriteDirectoryTagColormap(tif,&ndir,dir)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_EXTRASAMPLES)) + { + if (tif->tif_dir.td_extrasamples) + { + uint16 na; + uint16* nb; + TIFFGetFieldDefaulted(tif,TIFFTAG_EXTRASAMPLES,&na,&nb); + if (!TIFFWriteDirectoryTagShortArray(tif,&ndir,dir,TIFFTAG_EXTRASAMPLES,na,nb)) + goto bad; + } + } + if (TIFFFieldSet(tif,FIELD_SAMPLEFORMAT)) + { + if (!TIFFWriteDirectoryTagShortPerSample(tif,&ndir,dir,TIFFTAG_SAMPLEFORMAT,tif->tif_dir.td_sampleformat)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_SMINSAMPLEVALUE)) + { + if (!TIFFWriteDirectoryTagSampleformatArray(tif,&ndir,dir,TIFFTAG_SMINSAMPLEVALUE,tif->tif_dir.td_samplesperpixel,tif->tif_dir.td_sminsamplevalue)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_SMAXSAMPLEVALUE)) + { + if (!TIFFWriteDirectoryTagSampleformatArray(tif,&ndir,dir,TIFFTAG_SMAXSAMPLEVALUE,tif->tif_dir.td_samplesperpixel,tif->tif_dir.td_smaxsamplevalue)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_IMAGEDEPTH)) + { + if (!TIFFWriteDirectoryTagLong(tif,&ndir,dir,TIFFTAG_IMAGEDEPTH,tif->tif_dir.td_imagedepth)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_TILEDEPTH)) + { + if (!TIFFWriteDirectoryTagLong(tif,&ndir,dir,TIFFTAG_TILEDEPTH,tif->tif_dir.td_tiledepth)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_HALFTONEHINTS)) + { + if (!TIFFWriteDirectoryTagShortArray(tif,&ndir,dir,TIFFTAG_HALFTONEHINTS,2,&tif->tif_dir.td_halftonehints[0])) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_YCBCRSUBSAMPLING)) + { + if (!TIFFWriteDirectoryTagShortArray(tif,&ndir,dir,TIFFTAG_YCBCRSUBSAMPLING,2,&tif->tif_dir.td_ycbcrsubsampling[0])) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_YCBCRPOSITIONING)) + { + if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,TIFFTAG_YCBCRPOSITIONING,tif->tif_dir.td_ycbcrpositioning)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_REFBLACKWHITE)) + { + if (!TIFFWriteDirectoryTagRationalArray(tif,&ndir,dir,TIFFTAG_REFERENCEBLACKWHITE,6,tif->tif_dir.td_refblackwhite)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_TRANSFERFUNCTION)) + { + if (!TIFFWriteDirectoryTagTransferfunction(tif,&ndir,dir)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_INKNAMES)) + { + if (!TIFFWriteDirectoryTagAscii(tif,&ndir,dir,TIFFTAG_INKNAMES,tif->tif_dir.td_inknameslen,tif->tif_dir.td_inknames)) + goto bad; + } + if (TIFFFieldSet(tif,FIELD_SUBIFD)) + { + if (!TIFFWriteDirectoryTagSubifd(tif,&ndir,dir)) + goto bad; + } + { + uint32 n; + for (n=0; ntif_nfields; n++) { + const TIFFField* o; + o = tif->tif_fields[n]; + if ((o->field_bit>=FIELD_CODEC)&&(TIFFFieldSet(tif,o->field_bit))) + { + switch (o->get_field_type) + { + case TIFF_SETGET_ASCII: + { + uint32 pa; + char* pb; + assert(o->field_type==TIFF_ASCII); + assert(o->field_readcount==TIFF_VARIABLE); + assert(o->field_passcount==0); + TIFFGetField(tif,o->field_tag,&pb); + pa=(uint32)(strlen(pb)); + if (!TIFFWriteDirectoryTagAscii(tif,&ndir,dir,o->field_tag,pa,pb)) + goto bad; + } + break; + case TIFF_SETGET_UINT16: + { + uint16 p; + assert(o->field_type==TIFF_SHORT); + assert(o->field_readcount==1); + assert(o->field_passcount==0); + TIFFGetField(tif,o->field_tag,&p); + if (!TIFFWriteDirectoryTagShort(tif,&ndir,dir,o->field_tag,p)) + goto bad; + } + break; + case TIFF_SETGET_UINT32: + { + uint32 p; + assert(o->field_type==TIFF_LONG); + assert(o->field_readcount==1); + assert(o->field_passcount==0); + TIFFGetField(tif,o->field_tag,&p); + if (!TIFFWriteDirectoryTagLong(tif,&ndir,dir,o->field_tag,p)) + goto bad; + } + break; + case TIFF_SETGET_C32_UINT8: + { + uint32 pa; + void* pb; + assert(o->field_type==TIFF_UNDEFINED); + assert(o->field_readcount==TIFF_VARIABLE2); + assert(o->field_passcount==1); + TIFFGetField(tif,o->field_tag,&pa,&pb); + if (!TIFFWriteDirectoryTagUndefinedArray(tif,&ndir,dir,o->field_tag,pa,pb)) + goto bad; + } + break; + default: + assert(0); /* we should never get here */ + break; + } + } + } + } + } + for (m=0; m<(uint32)(tif->tif_dir.td_customValueCount); m++) + { + switch (tif->tif_dir.td_customValues[m].info->field_type) + { + case TIFF_ASCII: + if (!TIFFWriteDirectoryTagAscii(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_UNDEFINED: + if (!TIFFWriteDirectoryTagUndefinedArray(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_BYTE: + if (!TIFFWriteDirectoryTagByteArray(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_SBYTE: + if (!TIFFWriteDirectoryTagSbyteArray(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_SHORT: + if (!TIFFWriteDirectoryTagShortArray(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_SSHORT: + if (!TIFFWriteDirectoryTagSshortArray(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_LONG: + if (!TIFFWriteDirectoryTagLongArray(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_SLONG: + if (!TIFFWriteDirectoryTagSlongArray(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_LONG8: + if (!TIFFWriteDirectoryTagLong8Array(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_SLONG8: + if (!TIFFWriteDirectoryTagSlong8Array(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_RATIONAL: + if (!TIFFWriteDirectoryTagRationalArray(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_SRATIONAL: + if (!TIFFWriteDirectoryTagSrationalArray(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_FLOAT: + if (!TIFFWriteDirectoryTagFloatArray(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_DOUBLE: + if (!TIFFWriteDirectoryTagDoubleArray(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_IFD: + if (!TIFFWriteDirectoryTagIfdArray(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + case TIFF_IFD8: + if (!TIFFWriteDirectoryTagIfdIfd8Array(tif,&ndir,dir,tif->tif_dir.td_customValues[m].info->field_tag,tif->tif_dir.td_customValues[m].count,tif->tif_dir.td_customValues[m].value)) + goto bad; + break; + default: + assert(0); /* we should never get here */ + break; + } + } + if (dir!=NULL) + break; + dir=_TIFFmalloc(ndir*sizeof(TIFFDirEntry)); + if (dir==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + goto bad; + } + if (isimage) + { + if ((tif->tif_diroff==0)&&(!TIFFLinkDirectory(tif))) + goto bad; + } + else + tif->tif_diroff=(TIFFSeekFile(tif,0,SEEK_END)+1)&(~1); + if (pdiroff!=NULL) + *pdiroff=tif->tif_diroff; + if (!(tif->tif_flags&TIFF_BIGTIFF)) + dirsize=2+ndir*12+4; + else + dirsize=8+ndir*20+8; + tif->tif_dataoff=tif->tif_diroff+dirsize; + if (!(tif->tif_flags&TIFF_BIGTIFF)) + tif->tif_dataoff=(uint32)tif->tif_dataoff; + if ((tif->tif_dataofftif_diroff)||(tif->tif_dataoff<(uint64)dirsize)) + { + TIFFErrorExt(tif->tif_clientdata,module,"Maximum TIFF file size exceeded"); + goto bad; + } + if (tif->tif_dataoff&1) + tif->tif_dataoff++; + if (isimage) + tif->tif_curdir++; + } + if (isimage) + { + if (TIFFFieldSet(tif,FIELD_SUBIFD)&&(tif->tif_subifdoff==0)) + { + uint32 na; + TIFFDirEntry* nb; + for (na=0, nb=dir; ; na++, nb++) + { + assert(natdir_tag==TIFFTAG_SUBIFD) + break; + } + if (!(tif->tif_flags&TIFF_BIGTIFF)) + tif->tif_subifdoff=tif->tif_diroff+2+na*12+8; + else + tif->tif_subifdoff=tif->tif_diroff+8+na*20+12; + } + } + dirmem=_TIFFmalloc(dirsize); + if (dirmem==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + goto bad; + } + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + uint8* n; + uint32 nTmp; + TIFFDirEntry* o; + n=dirmem; + *(uint16*)n=ndir; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)n); + n+=2; + o=dir; + for (m=0; mtdir_tag; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)n); + n+=2; + *(uint16*)n=o->tdir_type; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)n); + n+=2; + nTmp = (uint32)o->tdir_count; + _TIFFmemcpy(n,&nTmp,4); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)n); + n+=4; + /* This is correct. The data has been */ + /* swabbed previously in TIFFWriteDirectoryTagData */ + _TIFFmemcpy(n,&o->tdir_offset,4); + n+=4; + o++; + } + nTmp = (uint32)tif->tif_nextdiroff; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&nTmp); + _TIFFmemcpy(n,&nTmp,4); + } + else + { + uint8* n; + TIFFDirEntry* o; + n=dirmem; + *(uint64*)n=ndir; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)n); + n+=8; + o=dir; + for (m=0; mtdir_tag; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)n); + n+=2; + *(uint16*)n=o->tdir_type; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)n); + n+=2; + _TIFFmemcpy(n,&o->tdir_count,8); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)n); + n+=8; + _TIFFmemcpy(n,&o->tdir_offset,8); + n+=8; + o++; + } + _TIFFmemcpy(n,&tif->tif_nextdiroff,8); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)n); + } + _TIFFfree(dir); + dir=NULL; + if (!SeekOK(tif,tif->tif_diroff)) + { + TIFFErrorExt(tif->tif_clientdata,module,"IO error writing directory"); + goto bad; + } + if (!WriteOK(tif,dirmem,(tmsize_t)dirsize)) + { + TIFFErrorExt(tif->tif_clientdata,module,"IO error writing directory"); + goto bad; + } + _TIFFfree(dirmem); + if (imagedone) + { + TIFFFreeDirectory(tif); + tif->tif_flags &= ~TIFF_DIRTYDIRECT; + tif->tif_flags &= ~TIFF_DIRTYSTRIP; + (*tif->tif_cleanup)(tif); + /* + * Reset directory-related state for subsequent + * directories. + */ + TIFFCreateDirectory(tif); + } + return(1); +bad: + if (dir!=NULL) + _TIFFfree(dir); + if (dirmem!=NULL) + _TIFFfree(dirmem); + return(0); +} + +static int +TIFFWriteDirectoryTagSampleformatArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, double* value) +{ + static const char module[] = "TIFFWriteDirectoryTagSampleformatArray"; + void* conv; + uint32 i; + int ok; + conv = _TIFFmalloc(count*sizeof(double)); + if (conv == NULL) + { + TIFFErrorExt(tif->tif_clientdata, module, "Out of memory"); + return (0); + } + + switch (tif->tif_dir.td_sampleformat) + { + case SAMPLEFORMAT_IEEEFP: + if (tif->tif_dir.td_bitspersample<=32) + { + for (i = 0; i < count; ++i) + ((float*)conv)[i] = (float)value[i]; + ok = TIFFWriteDirectoryTagFloatArray(tif,ndir,dir,tag,count,(float*)conv); + } + else + { + ok = TIFFWriteDirectoryTagDoubleArray(tif,ndir,dir,tag,count,value); + } + break; + case SAMPLEFORMAT_INT: + if (tif->tif_dir.td_bitspersample<=8) + { + for (i = 0; i < count; ++i) + ((int8*)conv)[i] = (int8)value[i]; + ok = TIFFWriteDirectoryTagSbyteArray(tif,ndir,dir,tag,count,(int8*)conv); + } + else if (tif->tif_dir.td_bitspersample<=16) + { + for (i = 0; i < count; ++i) + ((int16*)conv)[i] = (int16)value[i]; + ok = TIFFWriteDirectoryTagSshortArray(tif,ndir,dir,tag,count,(int16*)conv); + } + else + { + for (i = 0; i < count; ++i) + ((int32*)conv)[i] = (int32)value[i]; + ok = TIFFWriteDirectoryTagSlongArray(tif,ndir,dir,tag,count,(int32*)conv); + } + break; + case SAMPLEFORMAT_UINT: + if (tif->tif_dir.td_bitspersample<=8) + { + for (i = 0; i < count; ++i) + ((uint8*)conv)[i] = (uint8)value[i]; + ok = TIFFWriteDirectoryTagByteArray(tif,ndir,dir,tag,count,(uint8*)conv); + } + else if (tif->tif_dir.td_bitspersample<=16) + { + for (i = 0; i < count; ++i) + ((uint16*)conv)[i] = (uint16)value[i]; + ok = TIFFWriteDirectoryTagShortArray(tif,ndir,dir,tag,count,(uint16*)conv); + } + else + { + for (i = 0; i < count; ++i) + ((uint32*)conv)[i] = (uint32)value[i]; + ok = TIFFWriteDirectoryTagLongArray(tif,ndir,dir,tag,count,(uint32*)conv); + } + break; + default: + ok = 0; + } + + _TIFFfree(conv); + return (ok); +} + +#if 0 +static int +TIFFWriteDirectoryTagSampleformatPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value) +{ + switch (tif->tif_dir.td_sampleformat) + { + case SAMPLEFORMAT_IEEEFP: + if (tif->tif_dir.td_bitspersample<=32) + return(TIFFWriteDirectoryTagFloatPerSample(tif,ndir,dir,tag,(float)value)); + else + return(TIFFWriteDirectoryTagDoublePerSample(tif,ndir,dir,tag,value)); + case SAMPLEFORMAT_INT: + if (tif->tif_dir.td_bitspersample<=8) + return(TIFFWriteDirectoryTagSbytePerSample(tif,ndir,dir,tag,(int8)value)); + else if (tif->tif_dir.td_bitspersample<=16) + return(TIFFWriteDirectoryTagSshortPerSample(tif,ndir,dir,tag,(int16)value)); + else + return(TIFFWriteDirectoryTagSlongPerSample(tif,ndir,dir,tag,(int32)value)); + case SAMPLEFORMAT_UINT: + if (tif->tif_dir.td_bitspersample<=8) + return(TIFFWriteDirectoryTagBytePerSample(tif,ndir,dir,tag,(uint8)value)); + else if (tif->tif_dir.td_bitspersample<=16) + return(TIFFWriteDirectoryTagShortPerSample(tif,ndir,dir,tag,(uint16)value)); + else + return(TIFFWriteDirectoryTagLongPerSample(tif,ndir,dir,tag,(uint32)value)); + default: + return(1); + } +} +#endif + +static int +TIFFWriteDirectoryTagAscii(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, char* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedAscii(tif,ndir,dir,tag,count,value)); +} + +static int +TIFFWriteDirectoryTagUndefinedArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedUndefinedArray(tif,ndir,dir,tag,count,value)); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagByte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint8 value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedByte(tif,ndir,dir,tag,value)); +} +#endif + +static int +TIFFWriteDirectoryTagByteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedByteArray(tif,ndir,dir,tag,count,value)); +} + +#if 0 +static int +TIFFWriteDirectoryTagBytePerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint8 value) +{ + static const char module[] = "TIFFWriteDirectoryTagBytePerSample"; + uint8* m; + uint8* na; + uint16 nb; + int o; + if (dir==NULL) + { + (*ndir)++; + return(1); + } + m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(uint8)); + if (m==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + for (na=m, nb=0; nbtif_dir.td_samplesperpixel; na++, nb++) + *na=value; + o=TIFFWriteDirectoryTagCheckedByteArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m); + _TIFFfree(m); + return(o); +} +#endif + +#ifdef notdef +static int +TIFFWriteDirectoryTagSbyte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int8 value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedSbyte(tif,ndir,dir,tag,value)); +} +#endif + +static int +TIFFWriteDirectoryTagSbyteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int8* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedSbyteArray(tif,ndir,dir,tag,count,value)); +} + +#if 0 +static int +TIFFWriteDirectoryTagSbytePerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int8 value) +{ + static const char module[] = "TIFFWriteDirectoryTagSbytePerSample"; + int8* m; + int8* na; + uint16 nb; + int o; + if (dir==NULL) + { + (*ndir)++; + return(1); + } + m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(int8)); + if (m==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + for (na=m, nb=0; nbtif_dir.td_samplesperpixel; na++, nb++) + *na=value; + o=TIFFWriteDirectoryTagCheckedSbyteArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m); + _TIFFfree(m); + return(o); +} +#endif + +static int +TIFFWriteDirectoryTagShort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedShort(tif,ndir,dir,tag,value)); +} + +static int +TIFFWriteDirectoryTagShortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint16* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedShortArray(tif,ndir,dir,tag,count,value)); +} + +static int +TIFFWriteDirectoryTagShortPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 value) +{ + static const char module[] = "TIFFWriteDirectoryTagShortPerSample"; + uint16* m; + uint16* na; + uint16 nb; + int o; + if (dir==NULL) + { + (*ndir)++; + return(1); + } + m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(uint16)); + if (m==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + for (na=m, nb=0; nbtif_dir.td_samplesperpixel; na++, nb++) + *na=value; + o=TIFFWriteDirectoryTagCheckedShortArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m); + _TIFFfree(m); + return(o); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagSshort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int16 value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedSshort(tif,ndir,dir,tag,value)); +} +#endif + +static int +TIFFWriteDirectoryTagSshortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int16* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedSshortArray(tif,ndir,dir,tag,count,value)); +} + +#if 0 +static int +TIFFWriteDirectoryTagSshortPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int16 value) +{ + static const char module[] = "TIFFWriteDirectoryTagSshortPerSample"; + int16* m; + int16* na; + uint16 nb; + int o; + if (dir==NULL) + { + (*ndir)++; + return(1); + } + m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(int16)); + if (m==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + for (na=m, nb=0; nbtif_dir.td_samplesperpixel; na++, nb++) + *na=value; + o=TIFFWriteDirectoryTagCheckedSshortArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m); + _TIFFfree(m); + return(o); +} +#endif + +static int +TIFFWriteDirectoryTagLong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedLong(tif,ndir,dir,tag,value)); +} + +static int +TIFFWriteDirectoryTagLongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedLongArray(tif,ndir,dir,tag,count,value)); +} + +#if 0 +static int +TIFFWriteDirectoryTagLongPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value) +{ + static const char module[] = "TIFFWriteDirectoryTagLongPerSample"; + uint32* m; + uint32* na; + uint16 nb; + int o; + if (dir==NULL) + { + (*ndir)++; + return(1); + } + m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(uint32)); + if (m==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + for (na=m, nb=0; nbtif_dir.td_samplesperpixel; na++, nb++) + *na=value; + o=TIFFWriteDirectoryTagCheckedLongArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m); + _TIFFfree(m); + return(o); +} +#endif + +#ifdef notdef +static int +TIFFWriteDirectoryTagSlong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int32 value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedSlong(tif,ndir,dir,tag,value)); +} +#endif + +static int +TIFFWriteDirectoryTagSlongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int32* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedSlongArray(tif,ndir,dir,tag,count,value)); +} + +#if 0 +static int +TIFFWriteDirectoryTagSlongPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int32 value) +{ + static const char module[] = "TIFFWriteDirectoryTagSlongPerSample"; + int32* m; + int32* na; + uint16 nb; + int o; + if (dir==NULL) + { + (*ndir)++; + return(1); + } + m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(int32)); + if (m==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + for (na=m, nb=0; nbtif_dir.td_samplesperpixel; na++, nb++) + *na=value; + o=TIFFWriteDirectoryTagCheckedSlongArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m); + _TIFFfree(m); + return(o); +} +#endif + +#ifdef notdef +static int +TIFFWriteDirectoryTagLong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint64 value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedLong8(tif,ndir,dir,tag,value)); +} +#endif + +static int +TIFFWriteDirectoryTagLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedLong8Array(tif,ndir,dir,tag,count,value)); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagSlong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int64 value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedSlong8(tif,ndir,dir,tag,value)); +} +#endif + +static int +TIFFWriteDirectoryTagSlong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int64* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedSlong8Array(tif,ndir,dir,tag,count,value)); +} + +static int +TIFFWriteDirectoryTagRational(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedRational(tif,ndir,dir,tag,value)); +} + +static int +TIFFWriteDirectoryTagRationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedRationalArray(tif,ndir,dir,tag,count,value)); +} + +static int +TIFFWriteDirectoryTagSrationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedSrationalArray(tif,ndir,dir,tag,count,value)); +} + +#ifdef notdef +static int TIFFWriteDirectoryTagFloat(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, float value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedFloat(tif,ndir,dir,tag,value)); +} +#endif + +static int TIFFWriteDirectoryTagFloatArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedFloatArray(tif,ndir,dir,tag,count,value)); +} + +#if 0 +static int TIFFWriteDirectoryTagFloatPerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, float value) +{ + static const char module[] = "TIFFWriteDirectoryTagFloatPerSample"; + float* m; + float* na; + uint16 nb; + int o; + if (dir==NULL) + { + (*ndir)++; + return(1); + } + m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(float)); + if (m==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + for (na=m, nb=0; nbtif_dir.td_samplesperpixel; na++, nb++) + *na=value; + o=TIFFWriteDirectoryTagCheckedFloatArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m); + _TIFFfree(m); + return(o); +} +#endif + +#ifdef notdef +static int TIFFWriteDirectoryTagDouble(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedDouble(tif,ndir,dir,tag,value)); +} +#endif + +static int TIFFWriteDirectoryTagDoubleArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, double* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedDoubleArray(tif,ndir,dir,tag,count,value)); +} + +#if 0 +static int TIFFWriteDirectoryTagDoublePerSample(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value) +{ + static const char module[] = "TIFFWriteDirectoryTagDoublePerSample"; + double* m; + double* na; + uint16 nb; + int o; + if (dir==NULL) + { + (*ndir)++; + return(1); + } + m=_TIFFmalloc(tif->tif_dir.td_samplesperpixel*sizeof(double)); + if (m==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + for (na=m, nb=0; nbtif_dir.td_samplesperpixel; na++, nb++) + *na=value; + o=TIFFWriteDirectoryTagCheckedDoubleArray(tif,ndir,dir,tag,tif->tif_dir.td_samplesperpixel,m); + _TIFFfree(m); + return(o); +} +#endif + +static int +TIFFWriteDirectoryTagIfdArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedIfdArray(tif,ndir,dir,tag,count,value)); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagIfd8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + return(TIFFWriteDirectoryTagCheckedIfd8Array(tif,ndir,dir,tag,count,value)); +} +#endif + +static int +TIFFWriteDirectoryTagShortLong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value) +{ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + if (value<=0xFFFF) + return(TIFFWriteDirectoryTagCheckedShort(tif,ndir,dir,tag,(uint16)value)); + else + return(TIFFWriteDirectoryTagCheckedLong(tif,ndir,dir,tag,value)); +} + +/************************************************************************/ +/* TIFFWriteDirectoryTagLongLong8Array() */ +/* */ +/* Write out LONG8 array as LONG8 for BigTIFF or LONG for */ +/* Classic TIFF with some checking. */ +/************************************************************************/ + +static int +TIFFWriteDirectoryTagLongLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value) +{ + static const char module[] = "TIFFWriteDirectoryTagLongLong8Array"; + uint64* ma; + uint32 mb; + uint32* p; + uint32* q; + int o; + + /* is this just a counting pass? */ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + + /* We always write LONG8 for BigTIFF, no checking needed. */ + if( tif->tif_flags&TIFF_BIGTIFF ) + return TIFFWriteDirectoryTagCheckedLong8Array(tif,ndir,dir, + tag,count,value); + + /* + ** For classic tiff we want to verify everything is in range for LONG + ** and convert to long format. + */ + + p = _TIFFmalloc(count*sizeof(uint32)); + if (p==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + + for (q=p, ma=value, mb=0; mb0xFFFFFFFF) + { + TIFFErrorExt(tif->tif_clientdata,module, + "Attempt to write value larger than 0xFFFFFFFF in Classic TIFF file."); + _TIFFfree(p); + return(0); + } + *q= (uint32)(*ma); + } + + o=TIFFWriteDirectoryTagCheckedLongArray(tif,ndir,dir,tag,count,p); + _TIFFfree(p); + + return(o); +} + +/************************************************************************/ +/* TIFFWriteDirectoryTagIfdIfd8Array() */ +/* */ +/* Write either IFD8 or IFD array depending on file type. */ +/************************************************************************/ + +static int +TIFFWriteDirectoryTagIfdIfd8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value) +{ + static const char module[] = "TIFFWriteDirectoryTagIfdIfd8Array"; + uint64* ma; + uint32 mb; + uint32* p; + uint32* q; + int o; + + /* is this just a counting pass? */ + if (dir==NULL) + { + (*ndir)++; + return(1); + } + + /* We always write IFD8 for BigTIFF, no checking needed. */ + if( tif->tif_flags&TIFF_BIGTIFF ) + return TIFFWriteDirectoryTagCheckedIfd8Array(tif,ndir,dir, + tag,count,value); + + /* + ** For classic tiff we want to verify everything is in range for IFD + ** and convert to long format. + */ + + p = _TIFFmalloc(count*sizeof(uint32)); + if (p==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + + for (q=p, ma=value, mb=0; mb0xFFFFFFFF) + { + TIFFErrorExt(tif->tif_clientdata,module, + "Attempt to write value larger than 0xFFFFFFFF in Classic TIFF file."); + _TIFFfree(p); + return(0); + } + *q= (uint32)(*ma); + } + + o=TIFFWriteDirectoryTagCheckedIfdArray(tif,ndir,dir,tag,count,p); + _TIFFfree(p); + + return(o); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagShortLongLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value) +{ + static const char module[] = "TIFFWriteDirectoryTagShortLongLong8Array"; + uint64* ma; + uint32 mb; + uint8 n; + int o; + if (dir==NULL) + { + (*ndir)++; + return(1); + } + n=0; + for (ma=value, mb=0; mb0xFFFF)) + n=1; + if ((n==1)&&(*ma>0xFFFFFFFF)) + { + n=2; + break; + } + } + if (n==0) + { + uint16* p; + uint16* q; + p=_TIFFmalloc(count*sizeof(uint16)); + if (p==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + for (ma=value, mb=0, q=p; mbtif_clientdata,module,"Out of memory"); + return(0); + } + for (ma=value, mb=0, q=p; mbtif_dir.td_bitspersample); + n=_TIFFmalloc(3*m*sizeof(uint16)); + if (n==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + _TIFFmemcpy(&n[0],tif->tif_dir.td_colormap[0],m*sizeof(uint16)); + _TIFFmemcpy(&n[m],tif->tif_dir.td_colormap[1],m*sizeof(uint16)); + _TIFFmemcpy(&n[2*m],tif->tif_dir.td_colormap[2],m*sizeof(uint16)); + o=TIFFWriteDirectoryTagCheckedShortArray(tif,ndir,dir,TIFFTAG_COLORMAP,3*m,n); + _TIFFfree(n); + return(o); +} + +static int +TIFFWriteDirectoryTagTransferfunction(TIFF* tif, uint32* ndir, TIFFDirEntry* dir) +{ + static const char module[] = "TIFFWriteDirectoryTagTransferfunction"; + uint32 m; + uint16 n; + uint16* o; + int p; + if (dir==NULL) + { + (*ndir)++; + return(1); + } + m=(1<tif_dir.td_bitspersample); + n=tif->tif_dir.td_samplesperpixel-tif->tif_dir.td_extrasamples; + /* + * Check if the table can be written as a single column, + * or if it must be written as 3 columns. Note that we + * write a 3-column tag if there are 2 samples/pixel and + * a single column of data won't suffice--hmm. + */ + if (n>3) + n=3; + if (n==3) + { + if (!_TIFFmemcmp(tif->tif_dir.td_transferfunction[0],tif->tif_dir.td_transferfunction[2],m*sizeof(uint16))) + n=2; + } + if (n==2) + { + if (!_TIFFmemcmp(tif->tif_dir.td_transferfunction[0],tif->tif_dir.td_transferfunction[1],m*sizeof(uint16))) + n=1; + } + if (n==0) + n=1; + o=_TIFFmalloc(n*m*sizeof(uint16)); + if (o==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + _TIFFmemcpy(&o[0],tif->tif_dir.td_transferfunction[0],m*sizeof(uint16)); + if (n>1) + _TIFFmemcpy(&o[m],tif->tif_dir.td_transferfunction[1],m*sizeof(uint16)); + if (n>2) + _TIFFmemcpy(&o[2*m],tif->tif_dir.td_transferfunction[2],m*sizeof(uint16)); + p=TIFFWriteDirectoryTagCheckedShortArray(tif,ndir,dir,TIFFTAG_TRANSFERFUNCTION,n*m,o); + _TIFFfree(o); + return(p); +} + +static int +TIFFWriteDirectoryTagSubifd(TIFF* tif, uint32* ndir, TIFFDirEntry* dir) +{ + static const char module[] = "TIFFWriteDirectoryTagSubifd"; + uint64 m; + int n; + if (tif->tif_dir.td_nsubifd==0) + return(1); + if (dir==NULL) + { + (*ndir)++; + return(1); + } + m=tif->tif_dataoff; + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + uint32* o; + uint64* pa; + uint32* pb; + uint16 p; + o=_TIFFmalloc(tif->tif_dir.td_nsubifd*sizeof(uint32)); + if (o==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + pa=tif->tif_dir.td_subifd; + pb=o; + for (p=0; p < tif->tif_dir.td_nsubifd; p++) + { + assert(pa != 0); + assert(*pa <= 0xFFFFFFFFUL); + *pb++=(uint32)(*pa++); + } + n=TIFFWriteDirectoryTagCheckedIfdArray(tif,ndir,dir,TIFFTAG_SUBIFD,tif->tif_dir.td_nsubifd,o); + _TIFFfree(o); + } + else + n=TIFFWriteDirectoryTagCheckedIfd8Array(tif,ndir,dir,TIFFTAG_SUBIFD,tif->tif_dir.td_nsubifd,tif->tif_dir.td_subifd); + if (!n) + return(0); + /* + * Total hack: if this directory includes a SubIFD + * tag then force the next directories to be + * written as ``sub directories'' of this one. This + * is used to write things like thumbnails and + * image masks that one wants to keep out of the + * normal directory linkage access mechanism. + */ + tif->tif_flags|=TIFF_INSUBIFD; + tif->tif_nsubifd=tif->tif_dir.td_nsubifd; + if (tif->tif_dir.td_nsubifd==1) + tif->tif_subifdoff=0; + else + tif->tif_subifdoff=m; + return(1); +} + +static int +TIFFWriteDirectoryTagCheckedAscii(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, char* value) +{ + assert(sizeof(char)==1); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_ASCII,count,count,value)); +} + +static int +TIFFWriteDirectoryTagCheckedUndefinedArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value) +{ + assert(sizeof(uint8)==1); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_UNDEFINED,count,count,value)); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagCheckedByte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint8 value) +{ + assert(sizeof(uint8)==1); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_BYTE,1,1,&value)); +} +#endif + +static int +TIFFWriteDirectoryTagCheckedByteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint8* value) +{ + assert(sizeof(uint8)==1); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_BYTE,count,count,value)); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagCheckedSbyte(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int8 value) +{ + assert(sizeof(int8)==1); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SBYTE,1,1,&value)); +} +#endif + +static int +TIFFWriteDirectoryTagCheckedSbyteArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int8* value) +{ + assert(sizeof(int8)==1); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SBYTE,count,count,value)); +} + +static int +TIFFWriteDirectoryTagCheckedShort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 value) +{ + uint16 m; + assert(sizeof(uint16)==2); + m=value; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort(&m); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SHORT,1,2,&m)); +} + +static int +TIFFWriteDirectoryTagCheckedShortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint16* value) +{ + assert(count<0x80000000); + assert(sizeof(uint16)==2); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfShort(value,count); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SHORT,count,count*2,value)); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagCheckedSshort(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int16 value) +{ + int16 m; + assert(sizeof(int16)==2); + m=value; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort((uint16*)(&m)); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SSHORT,1,2,&m)); +} +#endif + +static int +TIFFWriteDirectoryTagCheckedSshortArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int16* value) +{ + assert(count<0x80000000); + assert(sizeof(int16)==2); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfShort((uint16*)value,count); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SSHORT,count,count*2,value)); +} + +static int +TIFFWriteDirectoryTagCheckedLong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 value) +{ + uint32 m; + assert(sizeof(uint32)==4); + m=value; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&m); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_LONG,1,4,&m)); +} + +static int +TIFFWriteDirectoryTagCheckedLongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value) +{ + assert(count<0x40000000); + assert(sizeof(uint32)==4); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong(value,count); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_LONG,count,count*4,value)); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagCheckedSlong(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int32 value) +{ + int32 m; + assert(sizeof(int32)==4); + m=value; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong((uint32*)(&m)); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SLONG,1,4,&m)); +} +#endif + +static int +TIFFWriteDirectoryTagCheckedSlongArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int32* value) +{ + assert(count<0x40000000); + assert(sizeof(int32)==4); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong((uint32*)value,count); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SLONG,count,count*4,value)); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagCheckedLong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint64 value) +{ + uint64 m; + assert(sizeof(uint64)==8); + assert(tif->tif_flags&TIFF_BIGTIFF); + m=value; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8(&m); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_LONG8,1,8,&m)); +} +#endif + +static int +TIFFWriteDirectoryTagCheckedLong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value) +{ + assert(count<0x20000000); + assert(sizeof(uint64)==8); + assert(tif->tif_flags&TIFF_BIGTIFF); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong8(value,count); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_LONG8,count,count*8,value)); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagCheckedSlong8(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, int64 value) +{ + int64 m; + assert(sizeof(int64)==8); + assert(tif->tif_flags&TIFF_BIGTIFF); + m=value; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8((uint64*)(&m)); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SLONG8,1,8,&m)); +} +#endif + +static int +TIFFWriteDirectoryTagCheckedSlong8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, int64* value) +{ + assert(count<0x20000000); + assert(sizeof(int64)==8); + assert(tif->tif_flags&TIFF_BIGTIFF); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong8((uint64*)value,count); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SLONG8,count,count*8,value)); +} + +static int +TIFFWriteDirectoryTagCheckedRational(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value) +{ + uint32 m[2]; + assert(value>=0.0); + assert(sizeof(uint32)==4); + if (value<=0.0) + { + m[0]=0; + m[1]=1; + } + else if (value==(double)(uint32)value) + { + m[0]=(uint32)value; + m[1]=1; + } + else if (value<1.0) + { + m[0]=(uint32)(value*0xFFFFFFFF); + m[1]=0xFFFFFFFF; + } + else + { + m[0]=0xFFFFFFFF; + m[1]=(uint32)(0xFFFFFFFF/value); + } + if (tif->tif_flags&TIFF_SWAB) + { + TIFFSwabLong(&m[0]); + TIFFSwabLong(&m[1]); + } + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_RATIONAL,1,8,&m[0])); +} + +static int +TIFFWriteDirectoryTagCheckedRationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value) +{ + static const char module[] = "TIFFWriteDirectoryTagCheckedRationalArray"; + uint32* m; + float* na; + uint32* nb; + uint32 nc; + int o; + assert(sizeof(uint32)==4); + m=_TIFFmalloc(count*2*sizeof(uint32)); + if (m==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + for (na=value, nb=m, nc=0; nctif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong(m,count*2); + o=TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_RATIONAL,count,count*8,&m[0]); + _TIFFfree(m); + return(o); +} + +static int +TIFFWriteDirectoryTagCheckedSrationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value) +{ + static const char module[] = "TIFFWriteDirectoryTagCheckedSrationalArray"; + int32* m; + float* na; + int32* nb; + uint32 nc; + int o; + assert(sizeof(int32)==4); + m=_TIFFmalloc(count*2*sizeof(int32)); + if (m==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + for (na=value, nb=m, nc=0; nc-1.0) + { + nb[0]=-(int32)((-*na)*0x7FFFFFFF); + nb[1]=0x7FFFFFFF; + } + else + { + nb[0]=-0x7FFFFFFF; + nb[1]=(int32)(0x7FFFFFFF/(-*na)); + } + } + else + { + if (*na==(int32)(*na)) + { + nb[0]=(int32)(*na); + nb[1]=1; + } + else if (*na<1.0) + { + nb[0]=(int32)((*na)*0x7FFFFFFF); + nb[1]=0x7FFFFFFF; + } + else + { + nb[0]=0x7FFFFFFF; + nb[1]=(int32)(0x7FFFFFFF/(*na)); + } + } + } + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong((uint32*)m,count*2); + o=TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_SRATIONAL,count,count*8,&m[0]); + _TIFFfree(m); + return(o); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagCheckedFloat(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, float value) +{ + float m; + assert(sizeof(float)==4); + m=value; + TIFFCvtNativeToIEEEFloat(tif,1,&m); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabFloat(&m); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_FLOAT,1,4,&m)); +} +#endif + +static int +TIFFWriteDirectoryTagCheckedFloatArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value) +{ + assert(count<0x40000000); + assert(sizeof(float)==4); + TIFFCvtNativeToIEEEFloat(tif,count,&value); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfFloat(value,count); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_FLOAT,count,count*4,value)); +} + +#ifdef notdef +static int +TIFFWriteDirectoryTagCheckedDouble(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, double value) +{ + double m; + assert(sizeof(double)==8); + m=value; + TIFFCvtNativeToIEEEDouble(tif,1,&m); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabDouble(&m); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_DOUBLE,1,8,&m)); +} +#endif + +static int +TIFFWriteDirectoryTagCheckedDoubleArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, double* value) +{ + assert(count<0x20000000); + assert(sizeof(double)==8); + TIFFCvtNativeToIEEEDouble(tif,count,&value); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfDouble(value,count); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_DOUBLE,count,count*8,value)); +} + +static int +TIFFWriteDirectoryTagCheckedIfdArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint32* value) +{ + assert(count<0x40000000); + assert(sizeof(uint32)==4); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong(value,count); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_IFD,count,count*4,value)); +} + +static int +TIFFWriteDirectoryTagCheckedIfd8Array(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, uint64* value) +{ + assert(count<0x20000000); + assert(sizeof(uint64)==8); + assert(tif->tif_flags&TIFF_BIGTIFF); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabArrayOfLong8(value,count); + return(TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_IFD8,count,count*8,value)); +} + +static int +TIFFWriteDirectoryTagData(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint16 datatype, uint32 count, uint32 datalength, void* data) +{ + static const char module[] = "TIFFWriteDirectoryTagData"; + uint32 m; + m=0; + while (m<(*ndir)) + { + assert(dir[m].tdir_tag!=tag); + if (dir[m].tdir_tag>tag) + break; + m++; + } + if (m<(*ndir)) + { + uint32 n; + for (n=*ndir; n>m; n--) + dir[n]=dir[n-1]; + } + dir[m].tdir_tag=tag; + dir[m].tdir_type=datatype; + dir[m].tdir_count=count; + dir[m].tdir_offset.toff_long8 = 0; + if (datalength<=((tif->tif_flags&TIFF_BIGTIFF)?0x8U:0x4U)) + _TIFFmemcpy(&dir[m].tdir_offset,data,datalength); + else + { + uint64 na,nb; + na=tif->tif_dataoff; + nb=na+datalength; + if (!(tif->tif_flags&TIFF_BIGTIFF)) + nb=(uint32)nb; + if ((nbtif_clientdata,module,"Maximum TIFF file size exceeded"); + return(0); + } + if (!SeekOK(tif,na)) + { + TIFFErrorExt(tif->tif_clientdata,module,"IO error writing tag data"); + return(0); + } + assert(datalength<0x80000000UL); + if (!WriteOK(tif,data,(tmsize_t)datalength)) + { + TIFFErrorExt(tif->tif_clientdata,module,"IO error writing tag data"); + return(0); + } + tif->tif_dataoff=nb; + if (tif->tif_dataoff&1) + tif->tif_dataoff++; + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + uint32 o; + o=(uint32)na; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong(&o); + _TIFFmemcpy(&dir[m].tdir_offset,&o,4); + } + else + { + dir[m].tdir_offset.toff_long8 = na; + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8(&dir[m].tdir_offset.toff_long8); + } + } + (*ndir)++; + return(1); +} + +/* + * Link the current directory into the directory chain for the file. + */ +static int +TIFFLinkDirectory(TIFF* tif) +{ + static const char module[] = "TIFFLinkDirectory"; + + tif->tif_diroff = (TIFFSeekFile(tif,0,SEEK_END)+1) &~ 1; + + /* + * Handle SubIFDs + */ + if (tif->tif_flags & TIFF_INSUBIFD) + { + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + uint32 m; + m = (uint32)tif->tif_diroff; + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong(&m); + (void) TIFFSeekFile(tif, tif->tif_subifdoff, SEEK_SET); + if (!WriteOK(tif, &m, 4)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error writing SubIFD directory link"); + return (0); + } + /* + * Advance to the next SubIFD or, if this is + * the last one configured, revert back to the + * normal directory linkage. + */ + if (--tif->tif_nsubifd) + tif->tif_subifdoff += 4; + else + tif->tif_flags &= ~TIFF_INSUBIFD; + return (1); + } + else + { + uint64 m; + m = tif->tif_diroff; + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong8(&m); + (void) TIFFSeekFile(tif, tif->tif_subifdoff, SEEK_SET); + if (!WriteOK(tif, &m, 8)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error writing SubIFD directory link"); + return (0); + } + /* + * Advance to the next SubIFD or, if this is + * the last one configured, revert back to the + * normal directory linkage. + */ + if (--tif->tif_nsubifd) + tif->tif_subifdoff += 8; + else + tif->tif_flags &= ~TIFF_INSUBIFD; + return (1); + } + } + + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + uint32 m; + uint32 nextdir; + m = (uint32)(tif->tif_diroff); + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong(&m); + if (tif->tif_header.classic.tiff_diroff == 0) { + /* + * First directory, overwrite offset in header. + */ + tif->tif_header.classic.tiff_diroff = (uint32) tif->tif_diroff; + (void) TIFFSeekFile(tif,4, SEEK_SET); + if (!WriteOK(tif, &m, 4)) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Error writing TIFF header"); + return (0); + } + return (1); + } + /* + * Not the first directory, search to the last and append. + */ + nextdir = tif->tif_header.classic.tiff_diroff; + while(1) { + uint16 dircount; + uint32 nextnextdir; + + if (!SeekOK(tif, nextdir) || + !ReadOK(tif, &dircount, 2)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error fetching directory count"); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&dircount); + (void) TIFFSeekFile(tif, + nextdir+2+dircount*12, SEEK_SET); + if (!ReadOK(tif, &nextnextdir, 4)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error fetching directory link"); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong(&nextnextdir); + if (nextnextdir==0) + { + (void) TIFFSeekFile(tif, + nextdir+2+dircount*12, SEEK_SET); + if (!WriteOK(tif, &m, 4)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error writing directory link"); + return (0); + } + break; + } + nextdir=nextnextdir; + } + } + else + { + uint64 m; + uint64 nextdir; + m = tif->tif_diroff; + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong8(&m); + if (tif->tif_header.big.tiff_diroff == 0) { + /* + * First directory, overwrite offset in header. + */ + tif->tif_header.big.tiff_diroff = tif->tif_diroff; + (void) TIFFSeekFile(tif,8, SEEK_SET); + if (!WriteOK(tif, &m, 8)) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Error writing TIFF header"); + return (0); + } + return (1); + } + /* + * Not the first directory, search to the last and append. + */ + nextdir = tif->tif_header.big.tiff_diroff; + while(1) { + uint64 dircount64; + uint16 dircount; + uint64 nextnextdir; + + if (!SeekOK(tif, nextdir) || + !ReadOK(tif, &dircount64, 8)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error fetching directory count"); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong8(&dircount64); + if (dircount64>0xFFFF) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Sanity check on tag count failed, likely corrupt TIFF"); + return (0); + } + dircount=(uint16)dircount64; + (void) TIFFSeekFile(tif, + nextdir+8+dircount*20, SEEK_SET); + if (!ReadOK(tif, &nextnextdir, 8)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error fetching directory link"); + return (0); + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong8(&nextnextdir); + if (nextnextdir==0) + { + (void) TIFFSeekFile(tif, + nextdir+8+dircount*20, SEEK_SET); + if (!WriteOK(tif, &m, 8)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error writing directory link"); + return (0); + } + break; + } + nextdir=nextnextdir; + } + } + return (1); +} + +/************************************************************************/ +/* TIFFRewriteField() */ +/* */ +/* Rewrite a field in the directory on disk without regard to */ +/* updating the TIFF directory structure in memory. Currently */ +/* only supported for field that already exist in the on-disk */ +/* directory. Mainly used for updating stripoffset / */ +/* stripbytecount values after the directory is already on */ +/* disk. */ +/* */ +/* Returns zero on failure, and one on success. */ +/************************************************************************/ + +int +_TIFFRewriteField(TIFF* tif, uint16 tag, TIFFDataType in_datatype, + tmsize_t count, void* data) +{ + static const char module[] = "TIFFResetField"; + /* const TIFFField* fip = NULL; */ + uint16 dircount; + tmsize_t dirsize; + uint8 direntry_raw[20]; + uint16 entry_tag = 0; + uint16 entry_type = 0; + uint64 entry_count = 0; + uint64 entry_offset = 0; + int value_in_entry = 0; + uint64 read_offset; + uint8 *buf_to_write = NULL; + TIFFDataType datatype; + +/* -------------------------------------------------------------------- */ +/* Find field definition. */ +/* -------------------------------------------------------------------- */ + /*fip =*/ TIFFFindField(tif, tag, TIFF_ANY); + +/* -------------------------------------------------------------------- */ +/* Do some checking this is a straight forward case. */ +/* -------------------------------------------------------------------- */ + if( isMapped(tif) ) + { + TIFFErrorExt( tif->tif_clientdata, module, + "Memory mapped files not currently supported for this operation." ); + return 0; + } + + if( tif->tif_diroff == 0 ) + { + TIFFErrorExt( tif->tif_clientdata, module, + "Attempt to reset field on directory not already on disk." ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* Read the directory entry count. */ +/* -------------------------------------------------------------------- */ + if (!SeekOK(tif, tif->tif_diroff)) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Seek error accessing TIFF directory", + tif->tif_name); + return 0; + } + + read_offset = tif->tif_diroff; + + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + if (!ReadOK(tif, &dircount, sizeof (uint16))) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Can not read TIFF directory count", + tif->tif_name); + return 0; + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&dircount); + dirsize = 12; + read_offset += 2; + } else { + uint64 dircount64; + if (!ReadOK(tif, &dircount64, sizeof (uint64))) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Can not read TIFF directory count", + tif->tif_name); + return 0; + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong8(&dircount64); + dircount = (uint16)dircount64; + dirsize = 20; + read_offset += 8; + } + +/* -------------------------------------------------------------------- */ +/* Read through directory to find target tag. */ +/* -------------------------------------------------------------------- */ + while( dircount > 0 ) + { + if (!ReadOK(tif, direntry_raw, dirsize)) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Can not read TIFF directory entry.", + tif->tif_name); + return 0; + } + + memcpy( &entry_tag, direntry_raw + 0, sizeof(uint16) ); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort( &entry_tag ); + + if( entry_tag == tag ) + break; + + read_offset += dirsize; + } + + if( entry_tag != tag ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Could not find tag %d.", + tif->tif_name, tag ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* Extract the type, count and offset for this entry. */ +/* -------------------------------------------------------------------- */ + memcpy( &entry_type, direntry_raw + 2, sizeof(uint16) ); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort( &entry_type ); + + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + uint32 value; + + memcpy( &value, direntry_raw + 4, sizeof(uint32) ); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong( &value ); + entry_count = value; + + memcpy( &value, direntry_raw + 8, sizeof(uint32) ); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong( &value ); + entry_offset = value; + } + else + { + memcpy( &entry_count, direntry_raw + 4, sizeof(uint64) ); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8( &entry_count ); + + memcpy( &entry_offset, direntry_raw + 12, sizeof(uint64) ); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8( &entry_offset ); + } + +/* -------------------------------------------------------------------- */ +/* What data type do we want to write this as? */ +/* -------------------------------------------------------------------- */ + if( TIFFDataWidth(in_datatype) == 8 && !(tif->tif_flags&TIFF_BIGTIFF) ) + { + if( in_datatype == TIFF_LONG8 ) + datatype = TIFF_LONG; + else if( in_datatype == TIFF_SLONG8 ) + datatype = TIFF_SLONG; + else if( in_datatype == TIFF_IFD8 ) + datatype = TIFF_IFD; + else + datatype = in_datatype; + } + else + datatype = in_datatype; + +/* -------------------------------------------------------------------- */ +/* Prepare buffer of actual data to write. This includes */ +/* swabbing as needed. */ +/* -------------------------------------------------------------------- */ + buf_to_write = + (uint8 *)_TIFFCheckMalloc(tif, count, TIFFDataWidth(datatype), + "for field buffer."); + if (!buf_to_write) + return 0; + + if( datatype == in_datatype ) + memcpy( buf_to_write, data, count * TIFFDataWidth(datatype) ); + else if( datatype == TIFF_SLONG && in_datatype == TIFF_SLONG8 ) + { + tmsize_t i; + + for( i = 0; i < count; i++ ) + { + ((int32 *) buf_to_write)[i] = + (int32) ((int64 *) data)[i]; + if( (int64) ((int32 *) buf_to_write)[i] != ((int64 *) data)[i] ) + { + _TIFFfree( buf_to_write ); + TIFFErrorExt( tif->tif_clientdata, module, + "Value exceeds 32bit range of output type." ); + return 0; + } + } + } + else if( (datatype == TIFF_LONG && in_datatype == TIFF_LONG8) + || (datatype == TIFF_IFD && in_datatype == TIFF_IFD8) ) + { + tmsize_t i; + + for( i = 0; i < count; i++ ) + { + ((uint32 *) buf_to_write)[i] = + (uint32) ((uint64 *) data)[i]; + if( (uint64) ((uint32 *) buf_to_write)[i] != ((uint64 *) data)[i] ) + { + _TIFFfree( buf_to_write ); + TIFFErrorExt( tif->tif_clientdata, module, + "Value exceeds 32bit range of output type." ); + return 0; + } + } + } + + if( TIFFDataWidth(datatype) > 1 && (tif->tif_flags&TIFF_SWAB) ) + { + if( TIFFDataWidth(datatype) == 2 ) + TIFFSwabArrayOfShort( (uint16 *) buf_to_write, count ); + else if( TIFFDataWidth(datatype) == 4 ) + TIFFSwabArrayOfLong( (uint32 *) buf_to_write, count ); + else if( TIFFDataWidth(datatype) == 8 ) + TIFFSwabArrayOfLong8( (uint64 *) buf_to_write, count ); + } + +/* -------------------------------------------------------------------- */ +/* Is this a value that fits into the directory entry? */ +/* -------------------------------------------------------------------- */ + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + if( TIFFDataWidth(datatype) * count <= 4 ) + { + entry_offset = read_offset + 8; + value_in_entry = 1; + } + } + else + { + if( TIFFDataWidth(datatype) * count <= 8 ) + { + entry_offset = read_offset + 12; + value_in_entry = 1; + } + } + +/* -------------------------------------------------------------------- */ +/* If the tag type, and count match, then we just write it out */ +/* over the old values without altering the directory entry at */ +/* all. */ +/* -------------------------------------------------------------------- */ + if( entry_count == (uint64)count && entry_type == (uint16) datatype ) + { + if (!SeekOK(tif, entry_offset)) { + _TIFFfree( buf_to_write ); + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Seek error accessing TIFF directory", + tif->tif_name); + return 0; + } + if (!WriteOK(tif, buf_to_write, count*TIFFDataWidth(datatype))) { + _TIFFfree( buf_to_write ); + TIFFErrorExt(tif->tif_clientdata, module, + "Error writing directory link"); + return (0); + } + + _TIFFfree( buf_to_write ); + return 1; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise, we write the new tag data at the end of the file. */ +/* -------------------------------------------------------------------- */ + if( !value_in_entry ) + { + entry_offset = TIFFSeekFile(tif,0,SEEK_END); + + if (!WriteOK(tif, buf_to_write, count*TIFFDataWidth(datatype))) { + _TIFFfree( buf_to_write ); + TIFFErrorExt(tif->tif_clientdata, module, + "Error writing directory link"); + return (0); + } + + _TIFFfree( buf_to_write ); + } + else + { + memcpy( &entry_offset, buf_to_write, count*TIFFDataWidth(datatype)); + } + +/* -------------------------------------------------------------------- */ +/* Adjust the directory entry. */ +/* -------------------------------------------------------------------- */ + entry_type = datatype; + memcpy( direntry_raw + 2, &entry_type, sizeof(uint16) ); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabShort( (uint16 *) (direntry_raw + 2) ); + + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + uint32 value; + + value = (uint32) entry_count; + memcpy( direntry_raw + 4, &value, sizeof(uint32) ); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong( (uint32 *) (direntry_raw + 4) ); + + value = (uint32) entry_offset; + memcpy( direntry_raw + 8, &value, sizeof(uint32) ); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong( (uint32 *) (direntry_raw + 8) ); + } + else + { + memcpy( direntry_raw + 4, &entry_count, sizeof(uint64) ); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8( (uint64 *) (direntry_raw + 4) ); + + memcpy( direntry_raw + 12, &entry_offset, sizeof(uint64) ); + if (tif->tif_flags&TIFF_SWAB) + TIFFSwabLong8( (uint64 *) (direntry_raw + 12) ); + } + +/* -------------------------------------------------------------------- */ +/* Write the directory entry out to disk. */ +/* -------------------------------------------------------------------- */ + if (!SeekOK(tif, read_offset )) { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Seek error accessing TIFF directory", + tif->tif_name); + return 0; + } + + if (!WriteOK(tif, direntry_raw,dirsize)) + { + TIFFErrorExt(tif->tif_clientdata, module, + "%s: Can not write TIFF directory entry.", + tif->tif_name); + return 0; + } + + return 1; +} +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dumpmode.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dumpmode.c new file mode 100644 index 000000000..a94cf0b34 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_dumpmode.c @@ -0,0 +1,143 @@ +/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_dumpmode.c,v 1.14 2011-04-02 20:54:09 bfriesen Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * "Null" Compression Algorithm Support. + */ +#include "tiffiop.h" + +static int +DumpFixupTags(TIFF* tif) +{ + (void) tif; + return (1); +} + +/* + * Encode a hunk of pixels. + */ +static int +DumpModeEncode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s) +{ + (void) s; + while (cc > 0) { + tmsize_t n; + + n = cc; + if (tif->tif_rawcc + n > tif->tif_rawdatasize) + n = tif->tif_rawdatasize - tif->tif_rawcc; + + assert( n > 0 ); + + /* + * Avoid copy if client has setup raw + * data buffer to avoid extra copy. + */ + if (tif->tif_rawcp != pp) + _TIFFmemcpy(tif->tif_rawcp, pp, n); + tif->tif_rawcp += n; + tif->tif_rawcc += n; + pp += n; + cc -= n; + if (tif->tif_rawcc >= tif->tif_rawdatasize && + !TIFFFlushData1(tif)) + return (-1); + } + return (1); +} + +/* + * Decode a hunk of pixels. + */ +static int +DumpModeDecode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s) +{ + static const char module[] = "DumpModeDecode"; + (void) s; + if (tif->tif_rawcc < cc) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, +"Not enough data for scanline %lu, expected a request for at most %I64d bytes, got a request for %I64d bytes", + (unsigned long) tif->tif_row, + (signed __int64) tif->tif_rawcc, + (signed __int64) cc); +#else + TIFFErrorExt(tif->tif_clientdata, module, +"Not enough data for scanline %lu, expected a request for at most %lld bytes, got a request for %lld bytes", + (unsigned long) tif->tif_row, + (signed long long) tif->tif_rawcc, + (signed long long) cc); +#endif + return (0); + } + /* + * Avoid copy if client has setup raw + * data buffer to avoid extra copy. + */ + if (tif->tif_rawcp != buf) + _TIFFmemcpy(buf, tif->tif_rawcp, cc); + tif->tif_rawcp += cc; + tif->tif_rawcc -= cc; + return (1); +} + +/* + * Seek forwards nrows in the current strip. + */ +static int +DumpModeSeek(TIFF* tif, uint32 nrows) +{ + tif->tif_rawcp += nrows * tif->tif_scanlinesize; + tif->tif_rawcc -= nrows * tif->tif_scanlinesize; + return (1); +} + +/* + * Initialize dump mode. + */ +int +TIFFInitDumpMode(TIFF* tif, int scheme) +{ + (void) scheme; + tif->tif_fixuptags = DumpFixupTags; + tif->tif_decoderow = DumpModeDecode; + tif->tif_decodestrip = DumpModeDecode; + tif->tif_decodetile = DumpModeDecode; + tif->tif_encoderow = DumpModeEncode; + tif->tif_encodestrip = DumpModeEncode; + tif->tif_encodetile = DumpModeEncode; + tif->tif_seek = DumpModeSeek; + return (1); +} +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_error.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_error.c new file mode 100644 index 000000000..0bc8b878b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_error.c @@ -0,0 +1,80 @@ +/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_error.c,v 1.5 2010-03-10 18:56:48 bfriesen Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + */ +#include "tiffiop.h" + +TIFFErrorHandlerExt _TIFFerrorHandlerExt = NULL; + +TIFFErrorHandler +TIFFSetErrorHandler(TIFFErrorHandler handler) +{ + TIFFErrorHandler prev = _TIFFerrorHandler; + _TIFFerrorHandler = handler; + return (prev); +} + +TIFFErrorHandlerExt +TIFFSetErrorHandlerExt(TIFFErrorHandlerExt handler) +{ + TIFFErrorHandlerExt prev = _TIFFerrorHandlerExt; + _TIFFerrorHandlerExt = handler; + return (prev); +} + +void +TIFFError(const char* module, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (_TIFFerrorHandler) + (*_TIFFerrorHandler)(module, fmt, ap); + if (_TIFFerrorHandlerExt) + (*_TIFFerrorHandlerExt)(0, module, fmt, ap); + va_end(ap); +} + +void +TIFFErrorExt(thandle_t fd, const char* module, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (_TIFFerrorHandler) + (*_TIFFerrorHandler)(module, fmt, ap); + if (_TIFFerrorHandlerExt) + (*_TIFFerrorHandlerExt)(fd, module, fmt, ap); + va_end(ap); +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_extension.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_extension.c new file mode 100644 index 000000000..10afd4162 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_extension.c @@ -0,0 +1,118 @@ +/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_extension.c,v 1.7 2010-03-10 18:56:48 bfriesen Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Various routines support external extension of the tag set, and other + * application extension capabilities. + */ + +#include "tiffiop.h" + +int TIFFGetTagListCount( TIFF *tif ) + +{ + TIFFDirectory* td = &tif->tif_dir; + + return td->td_customValueCount; +} + +uint32 TIFFGetTagListEntry( TIFF *tif, int tag_index ) + +{ + TIFFDirectory* td = &tif->tif_dir; + + if( tag_index < 0 || tag_index >= td->td_customValueCount ) + return (uint32)(-1); + else + return td->td_customValues[tag_index].info->field_tag; +} + +/* +** This provides read/write access to the TIFFTagMethods within the TIFF +** structure to application code without giving access to the private +** TIFF structure. +*/ +TIFFTagMethods *TIFFAccessTagMethods( TIFF *tif ) + +{ + return &(tif->tif_tagmethods); +} + +void *TIFFGetClientInfo( TIFF *tif, const char *name ) + +{ + TIFFClientInfoLink *link = tif->tif_clientinfo; + + while( link != NULL && strcmp(link->name,name) != 0 ) + link = link->next; + + if( link != NULL ) + return link->data; + else + return NULL; +} + +void TIFFSetClientInfo( TIFF *tif, void *data, const char *name ) + +{ + TIFFClientInfoLink *link = tif->tif_clientinfo; + + /* + ** Do we have an existing link with this name? If so, just + ** set it. + */ + while( link != NULL && strcmp(link->name,name) != 0 ) + link = link->next; + + if( link != NULL ) + { + link->data = data; + return; + } + + /* + ** Create a new link. + */ + + link = (TIFFClientInfoLink *) _TIFFmalloc(sizeof(TIFFClientInfoLink)); + assert (link != NULL); + link->next = tif->tif_clientinfo; + link->name = (char *) _TIFFmalloc((tmsize_t)(strlen(name)+1)); + assert (link->name != NULL); + strcpy(link->name, name); + link->data = data; + + tif->tif_clientinfo = link; +} +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_fax3.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_fax3.c new file mode 100644 index 000000000..2b2dccd08 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_fax3.c @@ -0,0 +1,1595 @@ +/* $Id: tif_fax3.c,v 1.74 2012-06-21 02:01:31 fwarmerdam Exp $ */ + +/* + * Copyright (c) 1990-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "tiffiop.h" +#ifdef CCITT_SUPPORT +/* + * TIFF Library. + * + * CCITT Group 3 (T.4) and Group 4 (T.6) Compression Support. + * + * This file contains support for decoding and encoding TIFF + * compression algorithms 2, 3, 4, and 32771. + * + * Decoder support is derived, with permission, from the code + * in Frank Cringle's viewfax program; + * Copyright (C) 1990, 1995 Frank D. Cringle. + */ +#include "tif_fax3.h" +#define G3CODES +#include "t4.h" +#include + +/* + * Compression+decompression state blocks are + * derived from this ``base state'' block. + */ +typedef struct { + int rw_mode; /* O_RDONLY for decode, else encode */ + int mode; /* operating mode */ + tmsize_t rowbytes; /* bytes in a decoded scanline */ + uint32 rowpixels; /* pixels in a scanline */ + + uint16 cleanfaxdata; /* CleanFaxData tag */ + uint32 badfaxrun; /* BadFaxRun tag */ + uint32 badfaxlines; /* BadFaxLines tag */ + uint32 groupoptions; /* Group 3/4 options tag */ + + TIFFVGetMethod vgetparent; /* super-class method */ + TIFFVSetMethod vsetparent; /* super-class method */ + TIFFPrintMethod printdir; /* super-class method */ +} Fax3BaseState; +#define Fax3State(tif) ((Fax3BaseState*) (tif)->tif_data) + +typedef enum { G3_1D, G3_2D } Ttag; +typedef struct { + Fax3BaseState b; + + /* Decoder state info */ + const unsigned char* bitmap; /* bit reversal table */ + uint32 data; /* current i/o byte/word */ + int bit; /* current i/o bit in byte */ + int EOLcnt; /* count of EOL codes recognized */ + TIFFFaxFillFunc fill; /* fill routine */ + uint32* runs; /* b&w runs for current/previous row */ + uint32* refruns; /* runs for reference line */ + uint32* curruns; /* runs for current line */ + + /* Encoder state info */ + Ttag tag; /* encoding state */ + unsigned char* refline; /* reference line for 2d decoding */ + int k; /* #rows left that can be 2d encoded */ + int maxk; /* max #rows that can be 2d encoded */ + + int line; +} Fax3CodecState; +#define DecoderState(tif) ((Fax3CodecState*) Fax3State(tif)) +#define EncoderState(tif) ((Fax3CodecState*) Fax3State(tif)) + +#define is2DEncoding(sp) (sp->b.groupoptions & GROUP3OPT_2DENCODING) +#define isAligned(p,t) ((((size_t)(p)) & (sizeof (t)-1)) == 0) + +/* + * Group 3 and Group 4 Decoding. + */ + +/* + * These macros glue the TIFF library state to + * the state expected by Frank's decoder. + */ +#define DECLARE_STATE(tif, sp, mod) \ + static const char module[] = mod; \ + Fax3CodecState* sp = DecoderState(tif); \ + int a0; /* reference element */ \ + int lastx = sp->b.rowpixels; /* last element in row */ \ + uint32 BitAcc; /* bit accumulator */ \ + int BitsAvail; /* # valid bits in BitAcc */ \ + int RunLength; /* length of current run */ \ + unsigned char* cp; /* next byte of input data */ \ + unsigned char* ep; /* end of input data */ \ + uint32* pa; /* place to stuff next run */ \ + uint32* thisrun; /* current row's run array */ \ + int EOLcnt; /* # EOL codes recognized */ \ + const unsigned char* bitmap = sp->bitmap; /* input data bit reverser */ \ + const TIFFFaxTabEnt* TabEnt +#define DECLARE_STATE_2D(tif, sp, mod) \ + DECLARE_STATE(tif, sp, mod); \ + int b1; /* next change on prev line */ \ + uint32* pb /* next run in reference line */\ +/* + * Load any state that may be changed during decoding. + */ +#define CACHE_STATE(tif, sp) do { \ + BitAcc = sp->data; \ + BitsAvail = sp->bit; \ + EOLcnt = sp->EOLcnt; \ + cp = (unsigned char*) tif->tif_rawcp; \ + ep = cp + tif->tif_rawcc; \ +} while (0) +/* + * Save state possibly changed during decoding. + */ +#define UNCACHE_STATE(tif, sp) do { \ + sp->bit = BitsAvail; \ + sp->data = BitAcc; \ + sp->EOLcnt = EOLcnt; \ + tif->tif_rawcc -= (tmsize_t)((uint8*) cp - tif->tif_rawcp); \ + tif->tif_rawcp = (uint8*) cp; \ +} while (0) + +/* + * Setup state for decoding a strip. + */ +static int +Fax3PreDecode(TIFF* tif, uint16 s) +{ + Fax3CodecState* sp = DecoderState(tif); + + (void) s; + assert(sp != NULL); + sp->bit = 0; /* force initial read */ + sp->data = 0; + sp->EOLcnt = 0; /* force initial scan for EOL */ + /* + * Decoder assumes lsb-to-msb bit order. Note that we select + * this here rather than in Fax3SetupState so that viewers can + * hold the image open, fiddle with the FillOrder tag value, + * and then re-decode the image. Otherwise they'd need to close + * and open the image to get the state reset. + */ + sp->bitmap = + TIFFGetBitRevTable(tif->tif_dir.td_fillorder != FILLORDER_LSB2MSB); + if (sp->refruns) { /* init reference line to white */ + sp->refruns[0] = (uint32) sp->b.rowpixels; + sp->refruns[1] = 0; + } + sp->line = 0; + return (1); +} + +/* + * Routine for handling various errors/conditions. + * Note how they are "glued into the decoder" by + * overriding the definitions used by the decoder. + */ + +static void +Fax3Unexpected(const char* module, TIFF* tif, uint32 line, uint32 a0) +{ + TIFFErrorExt(tif->tif_clientdata, module, "Bad code word at line %u of %s %u (x %u)", + line, isTiled(tif) ? "tile" : "strip", + (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip), + a0); +} +#define unexpected(table, a0) Fax3Unexpected(module, tif, sp->line, a0) + +static void +Fax3Extension(const char* module, TIFF* tif, uint32 line, uint32 a0) +{ + TIFFErrorExt(tif->tif_clientdata, module, + "Uncompressed data (not supported) at line %u of %s %u (x %u)", + line, isTiled(tif) ? "tile" : "strip", + (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip), + a0); +} +#define extension(a0) Fax3Extension(module, tif, sp->line, a0) + +static void +Fax3BadLength(const char* module, TIFF* tif, uint32 line, uint32 a0, uint32 lastx) +{ + TIFFWarningExt(tif->tif_clientdata, module, "%s at line %u of %s %u (got %u, expected %u)", + a0 < lastx ? "Premature EOL" : "Line length mismatch", + line, isTiled(tif) ? "tile" : "strip", + (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip), + a0, lastx); +} +#define badlength(a0,lastx) Fax3BadLength(module, tif, sp->line, a0, lastx) + +static void +Fax3PrematureEOF(const char* module, TIFF* tif, uint32 line, uint32 a0) +{ + TIFFWarningExt(tif->tif_clientdata, module, "Premature EOF at line %u of %s %u (x %u)", + line, isTiled(tif) ? "tile" : "strip", + (isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip), + a0); +} +#define prematureEOF(a0) Fax3PrematureEOF(module, tif, sp->line, a0) + +#define Nop + +/* + * Decode the requested amount of G3 1D-encoded data. + */ +static int +Fax3Decode1D(TIFF* tif, uint8* buf, tmsize_t occ, uint16 s) +{ + DECLARE_STATE(tif, sp, "Fax3Decode1D"); + (void) s; + if (occ % sp->b.rowbytes) + { + TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be read"); + return (-1); + } + CACHE_STATE(tif, sp); + thisrun = sp->curruns; + while (occ > 0) { + a0 = 0; + RunLength = 0; + pa = thisrun; +#ifdef FAX3_DEBUG + printf("\nBitAcc=%08X, BitsAvail = %d\n", BitAcc, BitsAvail); + printf("-------------------- %d\n", tif->tif_row); + fflush(stdout); +#endif + SYNC_EOL(EOF1D); + EXPAND1D(EOF1Da); + (*sp->fill)(buf, thisrun, pa, lastx); + buf += sp->b.rowbytes; + occ -= sp->b.rowbytes; + sp->line++; + continue; + EOF1D: /* premature EOF */ + CLEANUP_RUNS(); + EOF1Da: /* premature EOF */ + (*sp->fill)(buf, thisrun, pa, lastx); + UNCACHE_STATE(tif, sp); + return (-1); + } + UNCACHE_STATE(tif, sp); + return (1); +} + +#define SWAP(t,a,b) { t x; x = (a); (a) = (b); (b) = x; } +/* + * Decode the requested amount of G3 2D-encoded data. + */ +static int +Fax3Decode2D(TIFF* tif, uint8* buf, tmsize_t occ, uint16 s) +{ + DECLARE_STATE_2D(tif, sp, "Fax3Decode2D"); + int is1D; /* current line is 1d/2d-encoded */ + (void) s; + if (occ % sp->b.rowbytes) + { + TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be read"); + return (-1); + } + CACHE_STATE(tif, sp); + while (occ > 0) { + a0 = 0; + RunLength = 0; + pa = thisrun = sp->curruns; +#ifdef FAX3_DEBUG + printf("\nBitAcc=%08X, BitsAvail = %d EOLcnt = %d", + BitAcc, BitsAvail, EOLcnt); +#endif + SYNC_EOL(EOF2D); + NeedBits8(1, EOF2D); + is1D = GetBits(1); /* 1D/2D-encoding tag bit */ + ClrBits(1); +#ifdef FAX3_DEBUG + printf(" %s\n-------------------- %d\n", + is1D ? "1D" : "2D", tif->tif_row); + fflush(stdout); +#endif + pb = sp->refruns; + b1 = *pb++; + if (is1D) + EXPAND1D(EOF2Da); + else + EXPAND2D(EOF2Da); + (*sp->fill)(buf, thisrun, pa, lastx); + SETVALUE(0); /* imaginary change for reference */ + SWAP(uint32*, sp->curruns, sp->refruns); + buf += sp->b.rowbytes; + occ -= sp->b.rowbytes; + sp->line++; + continue; + EOF2D: /* premature EOF */ + CLEANUP_RUNS(); + EOF2Da: /* premature EOF */ + (*sp->fill)(buf, thisrun, pa, lastx); + UNCACHE_STATE(tif, sp); + return (-1); + } + UNCACHE_STATE(tif, sp); + return (1); +} +#undef SWAP + +/* + * The ZERO & FILL macros must handle spans < 2*sizeof(long) bytes. + * For machines with 64-bit longs this is <16 bytes; otherwise + * this is <8 bytes. We optimize the code here to reflect the + * machine characteristics. + */ +#if SIZEOF_UNSIGNED_LONG == 8 +# define FILL(n, cp) \ + switch (n) { \ + case 15:(cp)[14] = 0xff; case 14:(cp)[13] = 0xff; case 13: (cp)[12] = 0xff;\ + case 12:(cp)[11] = 0xff; case 11:(cp)[10] = 0xff; case 10: (cp)[9] = 0xff;\ + case 9: (cp)[8] = 0xff; case 8: (cp)[7] = 0xff; case 7: (cp)[6] = 0xff;\ + case 6: (cp)[5] = 0xff; case 5: (cp)[4] = 0xff; case 4: (cp)[3] = 0xff;\ + case 3: (cp)[2] = 0xff; case 2: (cp)[1] = 0xff; \ + case 1: (cp)[0] = 0xff; (cp) += (n); case 0: ; \ + } +# define ZERO(n, cp) \ + switch (n) { \ + case 15:(cp)[14] = 0; case 14:(cp)[13] = 0; case 13: (cp)[12] = 0; \ + case 12:(cp)[11] = 0; case 11:(cp)[10] = 0; case 10: (cp)[9] = 0; \ + case 9: (cp)[8] = 0; case 8: (cp)[7] = 0; case 7: (cp)[6] = 0; \ + case 6: (cp)[5] = 0; case 5: (cp)[4] = 0; case 4: (cp)[3] = 0; \ + case 3: (cp)[2] = 0; case 2: (cp)[1] = 0; \ + case 1: (cp)[0] = 0; (cp) += (n); case 0: ; \ + } +#else +# define FILL(n, cp) \ + switch (n) { \ + case 7: (cp)[6] = 0xff; case 6: (cp)[5] = 0xff; case 5: (cp)[4] = 0xff; \ + case 4: (cp)[3] = 0xff; case 3: (cp)[2] = 0xff; case 2: (cp)[1] = 0xff; \ + case 1: (cp)[0] = 0xff; (cp) += (n); case 0: ; \ + } +# define ZERO(n, cp) \ + switch (n) { \ + case 7: (cp)[6] = 0; case 6: (cp)[5] = 0; case 5: (cp)[4] = 0; \ + case 4: (cp)[3] = 0; case 3: (cp)[2] = 0; case 2: (cp)[1] = 0; \ + case 1: (cp)[0] = 0; (cp) += (n); case 0: ; \ + } +#endif + +/* + * Bit-fill a row according to the white/black + * runs generated during G3/G4 decoding. + */ +void +_TIFFFax3fillruns(unsigned char* buf, uint32* runs, uint32* erun, uint32 lastx) +{ + static const unsigned char _fillmasks[] = + { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; + unsigned char* cp; + uint32 x, bx, run; + int32 n, nw; + long* lp; + + if ((erun-runs)&1) + *erun++ = 0; + x = 0; + for (; runs < erun; runs += 2) { + run = runs[0]; + if (x+run > lastx || run > lastx ) + run = runs[0] = (uint32) (lastx - x); + if (run) { + cp = buf + (x>>3); + bx = x&7; + if (run > 8-bx) { + if (bx) { /* align to byte boundary */ + *cp++ &= 0xff << (8-bx); + run -= 8-bx; + } + if( (n = run >> 3) != 0 ) { /* multiple bytes to fill */ + if ((n/sizeof (long)) > 1) { + /* + * Align to longword boundary and fill. + */ + for (; n && !isAligned(cp, long); n--) + *cp++ = 0x00; + lp = (long*) cp; + nw = (int32)(n / sizeof (long)); + n -= nw * sizeof (long); + do { + *lp++ = 0L; + } while (--nw); + cp = (unsigned char*) lp; + } + ZERO(n, cp); + run &= 7; + } + if (run) + cp[0] &= 0xff >> run; + } else + cp[0] &= ~(_fillmasks[run]>>bx); + x += runs[0]; + } + run = runs[1]; + if (x+run > lastx || run > lastx ) + run = runs[1] = lastx - x; + if (run) { + cp = buf + (x>>3); + bx = x&7; + if (run > 8-bx) { + if (bx) { /* align to byte boundary */ + *cp++ |= 0xff >> bx; + run -= 8-bx; + } + if( (n = run>>3) != 0 ) { /* multiple bytes to fill */ + if ((n/sizeof (long)) > 1) { + /* + * Align to longword boundary and fill. + */ + for (; n && !isAligned(cp, long); n--) + *cp++ = 0xff; + lp = (long*) cp; + nw = (int32)(n / sizeof (long)); + n -= nw * sizeof (long); + do { + *lp++ = -1L; + } while (--nw); + cp = (unsigned char*) lp; + } + FILL(n, cp); + run &= 7; + } + if (run) + cp[0] |= 0xff00 >> run; + } else + cp[0] |= _fillmasks[run]>>bx; + x += runs[1]; + } + } + assert(x == lastx); +} +#undef ZERO +#undef FILL + +static int +Fax3FixupTags(TIFF* tif) +{ + (void) tif; + return (1); +} + +/* + * Setup G3/G4-related compression/decompression state + * before data is processed. This routine is called once + * per image -- it sets up different state based on whether + * or not decoding or encoding is being done and whether + * 1D- or 2D-encoded data is involved. + */ +static int +Fax3SetupState(TIFF* tif) +{ + static const char module[] = "Fax3SetupState"; + TIFFDirectory* td = &tif->tif_dir; + Fax3BaseState* sp = Fax3State(tif); + int needsRefLine; + Fax3CodecState* dsp = (Fax3CodecState*) Fax3State(tif); + tmsize_t rowbytes; + uint32 rowpixels, nruns; + + if (td->td_bitspersample != 1) { + TIFFErrorExt(tif->tif_clientdata, module, + "Bits/sample must be 1 for Group 3/4 encoding/decoding"); + return (0); + } + /* + * Calculate the scanline/tile widths. + */ + if (isTiled(tif)) { + rowbytes = TIFFTileRowSize(tif); + rowpixels = td->td_tilewidth; + } else { + rowbytes = TIFFScanlineSize(tif); + rowpixels = td->td_imagewidth; + } + sp->rowbytes = rowbytes; + sp->rowpixels = rowpixels; + /* + * Allocate any additional space required for decoding/encoding. + */ + needsRefLine = ( + (sp->groupoptions & GROUP3OPT_2DENCODING) || + td->td_compression == COMPRESSION_CCITTFAX4 + ); + + /* + Assure that allocation computations do not overflow. + + TIFFroundup and TIFFSafeMultiply return zero on integer overflow + */ + dsp->runs=(uint32*) NULL; + nruns = TIFFroundup_32(rowpixels,32); + if (needsRefLine) { + nruns = TIFFSafeMultiply(uint32,nruns,2); + } + if ((nruns == 0) || (TIFFSafeMultiply(uint32,nruns,2) == 0)) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Row pixels integer overflow (rowpixels %u)", + rowpixels); + return (0); + } + dsp->runs = (uint32*) _TIFFCheckMalloc(tif, + TIFFSafeMultiply(uint32,nruns,2), + sizeof (uint32), + "for Group 3/4 run arrays"); + if (dsp->runs == NULL) + return (0); + memset( dsp->runs, 0, TIFFSafeMultiply(uint32,nruns,2)*sizeof(uint32)); + dsp->curruns = dsp->runs; + if (needsRefLine) + dsp->refruns = dsp->runs + nruns; + else + dsp->refruns = NULL; + if (td->td_compression == COMPRESSION_CCITTFAX3 + && is2DEncoding(dsp)) { /* NB: default is 1D routine */ + tif->tif_decoderow = Fax3Decode2D; + tif->tif_decodestrip = Fax3Decode2D; + tif->tif_decodetile = Fax3Decode2D; + } + + if (needsRefLine) { /* 2d encoding */ + Fax3CodecState* esp = EncoderState(tif); + /* + * 2d encoding requires a scanline + * buffer for the ``reference line''; the + * scanline against which delta encoding + * is referenced. The reference line must + * be initialized to be ``white'' (done elsewhere). + */ + esp->refline = (unsigned char*) _TIFFmalloc(rowbytes); + if (esp->refline == NULL) { + TIFFErrorExt(tif->tif_clientdata, module, + "No space for Group 3/4 reference line"); + return (0); + } + } else /* 1d encoding */ + EncoderState(tif)->refline = NULL; + + return (1); +} + +/* + * CCITT Group 3 FAX Encoding. + */ + +#define Fax3FlushBits(tif, sp) { \ + if ((tif)->tif_rawcc >= (tif)->tif_rawdatasize) \ + (void) TIFFFlushData1(tif); \ + *(tif)->tif_rawcp++ = (uint8) (sp)->data; \ + (tif)->tif_rawcc++; \ + (sp)->data = 0, (sp)->bit = 8; \ +} +#define _FlushBits(tif) { \ + if ((tif)->tif_rawcc >= (tif)->tif_rawdatasize) \ + (void) TIFFFlushData1(tif); \ + *(tif)->tif_rawcp++ = (uint8) data; \ + (tif)->tif_rawcc++; \ + data = 0, bit = 8; \ +} +static const int _msbmask[9] = + { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; +#define _PutBits(tif, bits, length) { \ + while (length > bit) { \ + data |= bits >> (length - bit); \ + length -= bit; \ + _FlushBits(tif); \ + } \ + assert( length < 9 ); \ + data |= (bits & _msbmask[length]) << (bit - length); \ + bit -= length; \ + if (bit == 0) \ + _FlushBits(tif); \ +} + +/* + * Write a variable-length bit-value to + * the output stream. Values are + * assumed to be at most 16 bits. + */ +static void +Fax3PutBits(TIFF* tif, unsigned int bits, unsigned int length) +{ + Fax3CodecState* sp = EncoderState(tif); + unsigned int bit = sp->bit; + int data = sp->data; + + _PutBits(tif, bits, length); + + sp->data = data; + sp->bit = bit; +} + +/* + * Write a code to the output stream. + */ +#define putcode(tif, te) Fax3PutBits(tif, (te)->code, (te)->length) + +#ifdef FAX3_DEBUG +#define DEBUG_COLOR(w) (tab == TIFFFaxWhiteCodes ? w "W" : w "B") +#define DEBUG_PRINT(what,len) { \ + int t; \ + printf("%08X/%-2d: %s%5d\t", data, bit, DEBUG_COLOR(what), len); \ + for (t = length-1; t >= 0; t--) \ + putchar(code & (1<bit; + int data = sp->data; + unsigned int code, length; + + while (span >= 2624) { + const tableentry* te = &tab[63 + (2560>>6)]; + code = te->code, length = te->length; +#ifdef FAX3_DEBUG + DEBUG_PRINT("MakeUp", te->runlen); +#endif + _PutBits(tif, code, length); + span -= te->runlen; + } + if (span >= 64) { + const tableentry* te = &tab[63 + (span>>6)]; + assert(te->runlen == 64*(span>>6)); + code = te->code, length = te->length; +#ifdef FAX3_DEBUG + DEBUG_PRINT("MakeUp", te->runlen); +#endif + _PutBits(tif, code, length); + span -= te->runlen; + } + code = tab[span].code, length = tab[span].length; +#ifdef FAX3_DEBUG + DEBUG_PRINT(" Term", tab[span].runlen); +#endif + _PutBits(tif, code, length); + + sp->data = data; + sp->bit = bit; +} + +/* + * Write an EOL code to the output stream. The zero-fill + * logic for byte-aligning encoded scanlines is handled + * here. We also handle writing the tag bit for the next + * scanline when doing 2d encoding. + */ +static void +Fax3PutEOL(TIFF* tif) +{ + Fax3CodecState* sp = EncoderState(tif); + unsigned int bit = sp->bit; + int data = sp->data; + unsigned int code, length, tparm; + + if (sp->b.groupoptions & GROUP3OPT_FILLBITS) { + /* + * Force bit alignment so EOL will terminate on + * a byte boundary. That is, force the bit alignment + * to 16-12 = 4 before putting out the EOL code. + */ + int align = 8 - 4; + if (align != sp->bit) { + if (align > sp->bit) + align = sp->bit + (8 - align); + else + align = sp->bit - align; + code = 0; + tparm=align; + _PutBits(tif, 0, tparm); + } + } + code = EOL, length = 12; + if (is2DEncoding(sp)) + code = (code<<1) | (sp->tag == G3_1D), length++; + _PutBits(tif, code, length); + + sp->data = data; + sp->bit = bit; +} + +/* + * Reset encoding state at the start of a strip. + */ +static int +Fax3PreEncode(TIFF* tif, uint16 s) +{ + Fax3CodecState* sp = EncoderState(tif); + + (void) s; + assert(sp != NULL); + sp->bit = 8; + sp->data = 0; + sp->tag = G3_1D; + /* + * This is necessary for Group 4; otherwise it isn't + * needed because the first scanline of each strip ends + * up being copied into the refline. + */ + if (sp->refline) + _TIFFmemset(sp->refline, 0x00, sp->b.rowbytes); + if (is2DEncoding(sp)) { + float res = tif->tif_dir.td_yresolution; + /* + * The CCITT spec says that when doing 2d encoding, you + * should only do it on K consecutive scanlines, where K + * depends on the resolution of the image being encoded + * (2 for <= 200 lpi, 4 for > 200 lpi). Since the directory + * code initializes td_yresolution to 0, this code will + * select a K of 2 unless the YResolution tag is set + * appropriately. (Note also that we fudge a little here + * and use 150 lpi to avoid problems with units conversion.) + */ + if (tif->tif_dir.td_resolutionunit == RESUNIT_CENTIMETER) + res *= 2.54f; /* convert to inches */ + sp->maxk = (res > 150 ? 4 : 2); + sp->k = sp->maxk-1; + } else + sp->k = sp->maxk = 0; + sp->line = 0; + return (1); +} + +static const unsigned char zeroruns[256] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */ +}; +static const unsigned char oneruns[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */ + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */ +}; + +/* + * On certain systems it pays to inline + * the routines that find pixel spans. + */ +#ifdef VAXC +static int32 find0span(unsigned char*, int32, int32); +static int32 find1span(unsigned char*, int32, int32); +#pragma inline(find0span,find1span) +#endif + +/* + * Find a span of ones or zeros using the supplied + * table. The ``base'' of the bit string is supplied + * along with the start+end bit indices. + */ +inline static int32 +find0span(unsigned char* bp, int32 bs, int32 be) +{ + int32 bits = be - bs; + int32 n, span; + + bp += bs>>3; + /* + * Check partial byte on lhs. + */ + if (bits > 0 && (n = (bs & 7))) { + span = zeroruns[(*bp << n) & 0xff]; + if (span > 8-n) /* table value too generous */ + span = 8-n; + if (span > bits) /* constrain span to bit range */ + span = bits; + if (n+span < 8) /* doesn't extend to edge of byte */ + return (span); + bits -= span; + bp++; + } else + span = 0; + if (bits >= (int32)(2 * 8 * sizeof(long))) { + long* lp; + /* + * Align to longword boundary and check longwords. + */ + while (!isAligned(bp, long)) { + if (*bp != 0x00) + return (span + zeroruns[*bp]); + span += 8, bits -= 8; + bp++; + } + lp = (long*) bp; + while ((bits >= (int32)(8 * sizeof(long))) && (0 == *lp)) { + span += 8*sizeof (long), bits -= 8*sizeof (long); + lp++; + } + bp = (unsigned char*) lp; + } + /* + * Scan full bytes for all 0's. + */ + while (bits >= 8) { + if (*bp != 0x00) /* end of run */ + return (span + zeroruns[*bp]); + span += 8, bits -= 8; + bp++; + } + /* + * Check partial byte on rhs. + */ + if (bits > 0) { + n = zeroruns[*bp]; + span += (n > bits ? bits : n); + } + return (span); +} + +inline static int32 +find1span(unsigned char* bp, int32 bs, int32 be) +{ + int32 bits = be - bs; + int32 n, span; + + bp += bs>>3; + /* + * Check partial byte on lhs. + */ + if (bits > 0 && (n = (bs & 7))) { + span = oneruns[(*bp << n) & 0xff]; + if (span > 8-n) /* table value too generous */ + span = 8-n; + if (span > bits) /* constrain span to bit range */ + span = bits; + if (n+span < 8) /* doesn't extend to edge of byte */ + return (span); + bits -= span; + bp++; + } else + span = 0; + if (bits >= (int32)(2 * 8 * sizeof(long))) { + long* lp; + /* + * Align to longword boundary and check longwords. + */ + while (!isAligned(bp, long)) { + if (*bp != 0xff) + return (span + oneruns[*bp]); + span += 8, bits -= 8; + bp++; + } + lp = (long*) bp; + while ((bits >= (int32)(8 * sizeof(long))) && (~0 == *lp)) { + span += 8*sizeof (long), bits -= 8*sizeof (long); + lp++; + } + bp = (unsigned char*) lp; + } + /* + * Scan full bytes for all 1's. + */ + while (bits >= 8) { + if (*bp != 0xff) /* end of run */ + return (span + oneruns[*bp]); + span += 8, bits -= 8; + bp++; + } + /* + * Check partial byte on rhs. + */ + if (bits > 0) { + n = oneruns[*bp]; + span += (n > bits ? bits : n); + } + return (span); +} + +/* + * Return the offset of the next bit in the range + * [bs..be] that is different from the specified + * color. The end, be, is returned if no such bit + * exists. + */ +#define finddiff(_cp, _bs, _be, _color) \ + (_bs + (_color ? find1span(_cp,_bs,_be) : find0span(_cp,_bs,_be))) +/* + * Like finddiff, but also check the starting bit + * against the end in case start > end. + */ +#define finddiff2(_cp, _bs, _be, _color) \ + (_bs < _be ? finddiff(_cp,_bs,_be,_color) : _be) + +/* + * 1d-encode a row of pixels. The encoding is + * a sequence of all-white or all-black spans + * of pixels encoded with Huffman codes. + */ +static int +Fax3Encode1DRow(TIFF* tif, unsigned char* bp, uint32 bits) +{ + Fax3CodecState* sp = EncoderState(tif); + int32 span; + uint32 bs = 0; + + for (;;) { + span = find0span(bp, bs, bits); /* white span */ + putspan(tif, span, TIFFFaxWhiteCodes); + bs += span; + if (bs >= bits) + break; + span = find1span(bp, bs, bits); /* black span */ + putspan(tif, span, TIFFFaxBlackCodes); + bs += span; + if (bs >= bits) + break; + } + if (sp->b.mode & (FAXMODE_BYTEALIGN|FAXMODE_WORDALIGN)) { + if (sp->bit != 8) /* byte-align */ + Fax3FlushBits(tif, sp); + if ((sp->b.mode&FAXMODE_WORDALIGN) && + !isAligned(tif->tif_rawcp, uint16)) + Fax3FlushBits(tif, sp); + } + return (1); +} + +static const tableentry horizcode = + { 3, 0x1, 0 }; /* 001 */ +static const tableentry passcode = + { 4, 0x1, 0 }; /* 0001 */ +static const tableentry vcodes[7] = { + { 7, 0x03, 0 }, /* 0000 011 */ + { 6, 0x03, 0 }, /* 0000 11 */ + { 3, 0x03, 0 }, /* 011 */ + { 1, 0x1, 0 }, /* 1 */ + { 3, 0x2, 0 }, /* 010 */ + { 6, 0x02, 0 }, /* 0000 10 */ + { 7, 0x02, 0 } /* 0000 010 */ +}; + +/* + * 2d-encode a row of pixels. Consult the CCITT + * documentation for the algorithm. + */ +static int +Fax3Encode2DRow(TIFF* tif, unsigned char* bp, unsigned char* rp, uint32 bits) +{ +#define PIXEL(buf,ix) ((((buf)[(ix)>>3]) >> (7-((ix)&7))) & 1) + uint32 a0 = 0; + uint32 a1 = (PIXEL(bp, 0) != 0 ? 0 : finddiff(bp, 0, bits, 0)); + uint32 b1 = (PIXEL(rp, 0) != 0 ? 0 : finddiff(rp, 0, bits, 0)); + uint32 a2, b2; + + for (;;) { + b2 = finddiff2(rp, b1, bits, PIXEL(rp,b1)); + if (b2 >= a1) { + int32 d = b1 - a1; + if (!(-3 <= d && d <= 3)) { /* horizontal mode */ + a2 = finddiff2(bp, a1, bits, PIXEL(bp,a1)); + putcode(tif, &horizcode); + if (a0+a1 == 0 || PIXEL(bp, a0) == 0) { + putspan(tif, a1-a0, TIFFFaxWhiteCodes); + putspan(tif, a2-a1, TIFFFaxBlackCodes); + } else { + putspan(tif, a1-a0, TIFFFaxBlackCodes); + putspan(tif, a2-a1, TIFFFaxWhiteCodes); + } + a0 = a2; + } else { /* vertical mode */ + putcode(tif, &vcodes[d+3]); + a0 = a1; + } + } else { /* pass mode */ + putcode(tif, &passcode); + a0 = b2; + } + if (a0 >= bits) + break; + a1 = finddiff(bp, a0, bits, PIXEL(bp,a0)); + b1 = finddiff(rp, a0, bits, !PIXEL(bp,a0)); + b1 = finddiff(rp, b1, bits, PIXEL(bp,a0)); + } + return (1); +#undef PIXEL +} + +/* + * Encode a buffer of pixels. + */ +static int +Fax3Encode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + static const char module[] = "Fax3Encode"; + Fax3CodecState* sp = EncoderState(tif); + (void) s; + if (cc % sp->b.rowbytes) + { + TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be written"); + return (0); + } + while (cc > 0) { + if ((sp->b.mode & FAXMODE_NOEOL) == 0) + Fax3PutEOL(tif); + if (is2DEncoding(sp)) { + if (sp->tag == G3_1D) { + if (!Fax3Encode1DRow(tif, bp, sp->b.rowpixels)) + return (0); + sp->tag = G3_2D; + } else { + if (!Fax3Encode2DRow(tif, bp, sp->refline, + sp->b.rowpixels)) + return (0); + sp->k--; + } + if (sp->k == 0) { + sp->tag = G3_1D; + sp->k = sp->maxk-1; + } else + _TIFFmemcpy(sp->refline, bp, sp->b.rowbytes); + } else { + if (!Fax3Encode1DRow(tif, bp, sp->b.rowpixels)) + return (0); + } + bp += sp->b.rowbytes; + cc -= sp->b.rowbytes; + } + return (1); +} + +static int +Fax3PostEncode(TIFF* tif) +{ + Fax3CodecState* sp = EncoderState(tif); + + if (sp->bit != 8) + Fax3FlushBits(tif, sp); + return (1); +} + +static void +Fax3Close(TIFF* tif) +{ + if ((Fax3State(tif)->mode & FAXMODE_NORTC) == 0) { + Fax3CodecState* sp = EncoderState(tif); + unsigned int code = EOL; + unsigned int length = 12; + int i; + + if (is2DEncoding(sp)) + code = (code<<1) | (sp->tag == G3_1D), length++; + for (i = 0; i < 6; i++) + Fax3PutBits(tif, code, length); + Fax3FlushBits(tif, sp); + } +} + +static void +Fax3Cleanup(TIFF* tif) +{ + Fax3CodecState* sp = DecoderState(tif); + + assert(sp != 0); + + tif->tif_tagmethods.vgetfield = sp->b.vgetparent; + tif->tif_tagmethods.vsetfield = sp->b.vsetparent; + tif->tif_tagmethods.printdir = sp->b.printdir; + + if (sp->runs) + _TIFFfree(sp->runs); + if (sp->refline) + _TIFFfree(sp->refline); + + _TIFFfree(tif->tif_data); + tif->tif_data = NULL; + + _TIFFSetDefaultCompressionState(tif); +} + +#define FIELD_BADFAXLINES (FIELD_CODEC+0) +#define FIELD_CLEANFAXDATA (FIELD_CODEC+1) +#define FIELD_BADFAXRUN (FIELD_CODEC+2) + +#define FIELD_OPTIONS (FIELD_CODEC+7) + +static const TIFFField faxFields[] = { + { TIFFTAG_FAXMODE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "FaxMode", NULL }, + { TIFFTAG_FAXFILLFUNC, 0, 0, TIFF_ANY, 0, TIFF_SETGET_OTHER, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "FaxFillFunc", NULL }, + { TIFFTAG_BADFAXLINES, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_BADFAXLINES, TRUE, FALSE, "BadFaxLines", NULL }, + { TIFFTAG_CLEANFAXDATA, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UINT16, FIELD_CLEANFAXDATA, TRUE, FALSE, "CleanFaxData", NULL }, + { TIFFTAG_CONSECUTIVEBADFAXLINES, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_BADFAXRUN, TRUE, FALSE, "ConsecutiveBadFaxLines", NULL }}; +static const TIFFField fax3Fields[] = { + { TIFFTAG_GROUP3OPTIONS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_OPTIONS, FALSE, FALSE, "Group3Options", NULL }, +}; +static const TIFFField fax4Fields[] = { + { TIFFTAG_GROUP4OPTIONS, 1, 1, TIFF_LONG, 0, TIFF_SETGET_UINT32, TIFF_SETGET_UINT32, FIELD_OPTIONS, FALSE, FALSE, "Group4Options", NULL }, +}; + +static int +Fax3VSetField(TIFF* tif, uint32 tag, va_list ap) +{ + Fax3BaseState* sp = Fax3State(tif); + const TIFFField* fip; + + assert(sp != 0); + assert(sp->vsetparent != 0); + + switch (tag) { + case TIFFTAG_FAXMODE: + sp->mode = (int) va_arg(ap, int); + return 1; /* NB: pseudo tag */ + case TIFFTAG_FAXFILLFUNC: + DecoderState(tif)->fill = va_arg(ap, TIFFFaxFillFunc); + return 1; /* NB: pseudo tag */ + case TIFFTAG_GROUP3OPTIONS: + /* XXX: avoid reading options if compression mismatches. */ + if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX3) + sp->groupoptions = (uint32) va_arg(ap, uint32); + break; + case TIFFTAG_GROUP4OPTIONS: + /* XXX: avoid reading options if compression mismatches. */ + if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX4) + sp->groupoptions = (uint32) va_arg(ap, uint32); + break; + case TIFFTAG_BADFAXLINES: + sp->badfaxlines = (uint32) va_arg(ap, uint32); + break; + case TIFFTAG_CLEANFAXDATA: + sp->cleanfaxdata = (uint16) va_arg(ap, uint16_vap); + break; + case TIFFTAG_CONSECUTIVEBADFAXLINES: + sp->badfaxrun = (uint32) va_arg(ap, uint32); + break; + default: + return (*sp->vsetparent)(tif, tag, ap); + } + + if ((fip = TIFFFieldWithTag(tif, tag))) + TIFFSetFieldBit(tif, fip->field_bit); + else + return 0; + + tif->tif_flags |= TIFF_DIRTYDIRECT; + return 1; +} + +static int +Fax3VGetField(TIFF* tif, uint32 tag, va_list ap) +{ + Fax3BaseState* sp = Fax3State(tif); + + assert(sp != 0); + + switch (tag) { + case TIFFTAG_FAXMODE: + *va_arg(ap, int*) = sp->mode; + break; + case TIFFTAG_FAXFILLFUNC: + *va_arg(ap, TIFFFaxFillFunc*) = DecoderState(tif)->fill; + break; + case TIFFTAG_GROUP3OPTIONS: + case TIFFTAG_GROUP4OPTIONS: + *va_arg(ap, uint32*) = sp->groupoptions; + break; + case TIFFTAG_BADFAXLINES: + *va_arg(ap, uint32*) = sp->badfaxlines; + break; + case TIFFTAG_CLEANFAXDATA: + *va_arg(ap, uint16*) = sp->cleanfaxdata; + break; + case TIFFTAG_CONSECUTIVEBADFAXLINES: + *va_arg(ap, uint32*) = sp->badfaxrun; + break; + default: + return (*sp->vgetparent)(tif, tag, ap); + } + return (1); +} + +static void +Fax3PrintDir(TIFF* tif, FILE* fd, long flags) +{ + Fax3BaseState* sp = Fax3State(tif); + + assert(sp != 0); + + (void) flags; + if (TIFFFieldSet(tif,FIELD_OPTIONS)) { + const char* sep = " "; + if (tif->tif_dir.td_compression == COMPRESSION_CCITTFAX4) { + fprintf(fd, " Group 4 Options:"); + if (sp->groupoptions & GROUP4OPT_UNCOMPRESSED) + fprintf(fd, "%suncompressed data", sep); + } else { + + fprintf(fd, " Group 3 Options:"); + if (sp->groupoptions & GROUP3OPT_2DENCODING) + fprintf(fd, "%s2-d encoding", sep), sep = "+"; + if (sp->groupoptions & GROUP3OPT_FILLBITS) + fprintf(fd, "%sEOL padding", sep), sep = "+"; + if (sp->groupoptions & GROUP3OPT_UNCOMPRESSED) + fprintf(fd, "%suncompressed data", sep); + } + fprintf(fd, " (%lu = 0x%lx)\n", + (unsigned long) sp->groupoptions, + (unsigned long) sp->groupoptions); + } + if (TIFFFieldSet(tif,FIELD_CLEANFAXDATA)) { + fprintf(fd, " Fax Data:"); + switch (sp->cleanfaxdata) { + case CLEANFAXDATA_CLEAN: + fprintf(fd, " clean"); + break; + case CLEANFAXDATA_REGENERATED: + fprintf(fd, " receiver regenerated"); + break; + case CLEANFAXDATA_UNCLEAN: + fprintf(fd, " uncorrected errors"); + break; + } + fprintf(fd, " (%u = 0x%x)\n", + sp->cleanfaxdata, sp->cleanfaxdata); + } + if (TIFFFieldSet(tif,FIELD_BADFAXLINES)) + fprintf(fd, " Bad Fax Lines: %lu\n", + (unsigned long) sp->badfaxlines); + if (TIFFFieldSet(tif,FIELD_BADFAXRUN)) + fprintf(fd, " Consecutive Bad Fax Lines: %lu\n", + (unsigned long) sp->badfaxrun); + if (sp->printdir) + (*sp->printdir)(tif, fd, flags); +} + +static int +InitCCITTFax3(TIFF* tif) +{ + static const char module[] = "InitCCITTFax3"; + Fax3BaseState* sp; + + /* + * Merge codec-specific tag information. + */ + if (!_TIFFMergeFields(tif, faxFields, TIFFArrayCount(faxFields))) { + TIFFErrorExt(tif->tif_clientdata, "InitCCITTFax3", + "Merging common CCITT Fax codec-specific tags failed"); + return 0; + } + + /* + * Allocate state block so tag methods have storage to record values. + */ + tif->tif_data = (uint8*) + _TIFFmalloc(sizeof (Fax3CodecState)); + + if (tif->tif_data == NULL) { + TIFFErrorExt(tif->tif_clientdata, module, + "No space for state block"); + return (0); + } + + sp = Fax3State(tif); + sp->rw_mode = tif->tif_mode; + + /* + * Override parent get/set field methods. + */ + sp->vgetparent = tif->tif_tagmethods.vgetfield; + tif->tif_tagmethods.vgetfield = Fax3VGetField; /* hook for codec tags */ + sp->vsetparent = tif->tif_tagmethods.vsetfield; + tif->tif_tagmethods.vsetfield = Fax3VSetField; /* hook for codec tags */ + sp->printdir = tif->tif_tagmethods.printdir; + tif->tif_tagmethods.printdir = Fax3PrintDir; /* hook for codec tags */ + sp->groupoptions = 0; + + if (sp->rw_mode == O_RDONLY) /* FIXME: improve for in place update */ + tif->tif_flags |= TIFF_NOBITREV; /* decoder does bit reversal */ + DecoderState(tif)->runs = NULL; + TIFFSetField(tif, TIFFTAG_FAXFILLFUNC, _TIFFFax3fillruns); + EncoderState(tif)->refline = NULL; + + /* + * Install codec methods. + */ + tif->tif_fixuptags = Fax3FixupTags; + tif->tif_setupdecode = Fax3SetupState; + tif->tif_predecode = Fax3PreDecode; + tif->tif_decoderow = Fax3Decode1D; + tif->tif_decodestrip = Fax3Decode1D; + tif->tif_decodetile = Fax3Decode1D; + tif->tif_setupencode = Fax3SetupState; + tif->tif_preencode = Fax3PreEncode; + tif->tif_postencode = Fax3PostEncode; + tif->tif_encoderow = Fax3Encode; + tif->tif_encodestrip = Fax3Encode; + tif->tif_encodetile = Fax3Encode; + tif->tif_close = Fax3Close; + tif->tif_cleanup = Fax3Cleanup; + + return (1); +} + +int +TIFFInitCCITTFax3(TIFF* tif, int scheme) +{ + (void) scheme; + if (InitCCITTFax3(tif)) { + /* + * Merge codec-specific tag information. + */ + if (!_TIFFMergeFields(tif, fax3Fields, + TIFFArrayCount(fax3Fields))) { + TIFFErrorExt(tif->tif_clientdata, "TIFFInitCCITTFax3", + "Merging CCITT Fax 3 codec-specific tags failed"); + return 0; + } + + /* + * The default format is Class/F-style w/o RTC. + */ + return TIFFSetField(tif, TIFFTAG_FAXMODE, FAXMODE_CLASSF); + } else + return 01; +} + +/* + * CCITT Group 4 (T.6) Facsimile-compatible + * Compression Scheme Support. + */ + +#define SWAP(t,a,b) { t x; x = (a); (a) = (b); (b) = x; } +/* + * Decode the requested amount of G4-encoded data. + */ +static int +Fax4Decode(TIFF* tif, uint8* buf, tmsize_t occ, uint16 s) +{ + DECLARE_STATE_2D(tif, sp, "Fax4Decode"); + (void) s; + if (occ % sp->b.rowbytes) + { + TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be read"); + return (-1); + } + CACHE_STATE(tif, sp); + while (occ > 0) { + a0 = 0; + RunLength = 0; + pa = thisrun = sp->curruns; + pb = sp->refruns; + b1 = *pb++; +#ifdef FAX3_DEBUG + printf("\nBitAcc=%08X, BitsAvail = %d\n", BitAcc, BitsAvail); + printf("-------------------- %d\n", tif->tif_row); + fflush(stdout); +#endif + EXPAND2D(EOFG4); + if (EOLcnt) + goto EOFG4; + (*sp->fill)(buf, thisrun, pa, lastx); + SETVALUE(0); /* imaginary change for reference */ + SWAP(uint32*, sp->curruns, sp->refruns); + buf += sp->b.rowbytes; + occ -= sp->b.rowbytes; + sp->line++; + continue; + EOFG4: + NeedBits16( 13, BADG4 ); + BADG4: +#ifdef FAX3_DEBUG + if( GetBits(13) != 0x1001 ) + fputs( "Bad EOFB\n", stderr ); +#endif + ClrBits( 13 ); + (*sp->fill)(buf, thisrun, pa, lastx); + UNCACHE_STATE(tif, sp); + return ( sp->line ? 1 : -1); /* don't error on badly-terminated strips */ + } + UNCACHE_STATE(tif, sp); + return (1); +} +#undef SWAP + +/* + * Encode the requested amount of data. + */ +static int +Fax4Encode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + static const char module[] = "Fax4Encode"; + Fax3CodecState *sp = EncoderState(tif); + (void) s; + if (cc % sp->b.rowbytes) + { + TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be written"); + return (0); + } + while (cc > 0) { + if (!Fax3Encode2DRow(tif, bp, sp->refline, sp->b.rowpixels)) + return (0); + _TIFFmemcpy(sp->refline, bp, sp->b.rowbytes); + bp += sp->b.rowbytes; + cc -= sp->b.rowbytes; + } + return (1); +} + +static int +Fax4PostEncode(TIFF* tif) +{ + Fax3CodecState *sp = EncoderState(tif); + + /* terminate strip w/ EOFB */ + Fax3PutBits(tif, EOL, 12); + Fax3PutBits(tif, EOL, 12); + if (sp->bit != 8) + Fax3FlushBits(tif, sp); + return (1); +} + +int +TIFFInitCCITTFax4(TIFF* tif, int scheme) +{ + (void) scheme; + if (InitCCITTFax3(tif)) { /* reuse G3 support */ + /* + * Merge codec-specific tag information. + */ + if (!_TIFFMergeFields(tif, fax4Fields, + TIFFArrayCount(fax4Fields))) { + TIFFErrorExt(tif->tif_clientdata, "TIFFInitCCITTFax4", + "Merging CCITT Fax 4 codec-specific tags failed"); + return 0; + } + + tif->tif_decoderow = Fax4Decode; + tif->tif_decodestrip = Fax4Decode; + tif->tif_decodetile = Fax4Decode; + tif->tif_encoderow = Fax4Encode; + tif->tif_encodestrip = Fax4Encode; + tif->tif_encodetile = Fax4Encode; + tif->tif_postencode = Fax4PostEncode; + /* + * Suppress RTC at the end of each strip. + */ + return TIFFSetField(tif, TIFFTAG_FAXMODE, FAXMODE_NORTC); + } else + return (0); +} + +/* + * CCITT Group 3 1-D Modified Huffman RLE Compression Support. + * (Compression algorithms 2 and 32771) + */ + +/* + * Decode the requested amount of RLE-encoded data. + */ +static int +Fax3DecodeRLE(TIFF* tif, uint8* buf, tmsize_t occ, uint16 s) +{ + DECLARE_STATE(tif, sp, "Fax3DecodeRLE"); + int mode = sp->b.mode; + (void) s; + if (occ % sp->b.rowbytes) + { + TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be read"); + return (-1); + } + CACHE_STATE(tif, sp); + thisrun = sp->curruns; + while (occ > 0) { + a0 = 0; + RunLength = 0; + pa = thisrun; +#ifdef FAX3_DEBUG + printf("\nBitAcc=%08X, BitsAvail = %d\n", BitAcc, BitsAvail); + printf("-------------------- %d\n", tif->tif_row); + fflush(stdout); +#endif + EXPAND1D(EOFRLE); + (*sp->fill)(buf, thisrun, pa, lastx); + /* + * Cleanup at the end of the row. + */ + if (mode & FAXMODE_BYTEALIGN) { + int n = BitsAvail - (BitsAvail &~ 7); + ClrBits(n); + } else if (mode & FAXMODE_WORDALIGN) { + int n = BitsAvail - (BitsAvail &~ 15); + ClrBits(n); + if (BitsAvail == 0 && !isAligned(cp, uint16)) + cp++; + } + buf += sp->b.rowbytes; + occ -= sp->b.rowbytes; + sp->line++; + continue; + EOFRLE: /* premature EOF */ + (*sp->fill)(buf, thisrun, pa, lastx); + UNCACHE_STATE(tif, sp); + return (-1); + } + UNCACHE_STATE(tif, sp); + return (1); +} + +int +TIFFInitCCITTRLE(TIFF* tif, int scheme) +{ + (void) scheme; + if (InitCCITTFax3(tif)) { /* reuse G3 support */ + tif->tif_decoderow = Fax3DecodeRLE; + tif->tif_decodestrip = Fax3DecodeRLE; + tif->tif_decodetile = Fax3DecodeRLE; + /* + * Suppress RTC+EOLs when encoding and byte-align data. + */ + return TIFFSetField(tif, TIFFTAG_FAXMODE, + FAXMODE_NORTC|FAXMODE_NOEOL|FAXMODE_BYTEALIGN); + } else + return (0); +} + +int +TIFFInitCCITTRLEW(TIFF* tif, int scheme) +{ + (void) scheme; + if (InitCCITTFax3(tif)) { /* reuse G3 support */ + tif->tif_decoderow = Fax3DecodeRLE; + tif->tif_decodestrip = Fax3DecodeRLE; + tif->tif_decodetile = Fax3DecodeRLE; + /* + * Suppress RTC+EOLs when encoding and word-align data. + */ + return TIFFSetField(tif, TIFFTAG_FAXMODE, + FAXMODE_NORTC|FAXMODE_NOEOL|FAXMODE_WORDALIGN); + } else + return (0); +} +#endif /* CCITT_SUPPORT */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_fax3.h b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_fax3.h new file mode 100644 index 000000000..b0f46c9a4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_fax3.h @@ -0,0 +1,538 @@ +/* $Id: tif_fax3.h,v 1.9 2011-03-10 20:23:07 fwarmerdam Exp $ */ + +/* + * Copyright (c) 1990-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _FAX3_ +#define _FAX3_ +/* + * TIFF Library. + * + * CCITT Group 3 (T.4) and Group 4 (T.6) Decompression Support. + * + * Decoder support is derived, with permission, from the code + * in Frank Cringle's viewfax program; + * Copyright (C) 1990, 1995 Frank D. Cringle. + */ +#include "tiff.h" + +/* + * To override the default routine used to image decoded + * spans one can use the pseduo tag TIFFTAG_FAXFILLFUNC. + * The routine must have the type signature given below; + * for example: + * + * fillruns(unsigned char* buf, uint32* runs, uint32* erun, uint32 lastx) + * + * where buf is place to set the bits, runs is the array of b&w run + * lengths (white then black), erun is the last run in the array, and + * lastx is the width of the row in pixels. Fill routines can assume + * the run array has room for at least lastx runs and can overwrite + * data in the run array as needed (e.g. to append zero runs to bring + * the count up to a nice multiple). + */ +typedef void (*TIFFFaxFillFunc)(unsigned char*, uint32*, uint32*, uint32); + +/* + * The default run filler; made external for other decoders. + */ +#if defined(__cplusplus) +extern "C" { +#endif +extern void _TIFFFax3fillruns(unsigned char*, uint32*, uint32*, uint32); +#if defined(__cplusplus) +} +#endif + + +/* finite state machine codes */ +#define S_Null 0 +#define S_Pass 1 +#define S_Horiz 2 +#define S_V0 3 +#define S_VR 4 +#define S_VL 5 +#define S_Ext 6 +#define S_TermW 7 +#define S_TermB 8 +#define S_MakeUpW 9 +#define S_MakeUpB 10 +#define S_MakeUp 11 +#define S_EOL 12 + +typedef struct { /* state table entry */ + unsigned char State; /* see above */ + unsigned char Width; /* width of code in bits */ + uint32 Param; /* unsigned 32-bit run length in bits */ +} TIFFFaxTabEnt; + +extern const TIFFFaxTabEnt TIFFFaxMainTable[]; +extern const TIFFFaxTabEnt TIFFFaxWhiteTable[]; +extern const TIFFFaxTabEnt TIFFFaxBlackTable[]; + +/* + * The following macros define the majority of the G3/G4 decoder + * algorithm using the state tables defined elsewhere. To build + * a decoder you need some setup code and some glue code. Note + * that you may also need/want to change the way the NeedBits* + * macros get input data if, for example, you know the data to be + * decoded is properly aligned and oriented (doing so before running + * the decoder can be a big performance win). + * + * Consult the decoder in the TIFF library for an idea of what you + * need to define and setup to make use of these definitions. + * + * NB: to enable a debugging version of these macros define FAX3_DEBUG + * before including this file. Trace output goes to stdout. + */ + +#ifndef EndOfData +#define EndOfData() (cp >= ep) +#endif +/* + * Need <=8 or <=16 bits of input data. Unlike viewfax we + * cannot use/assume a word-aligned, properly bit swizzled + * input data set because data may come from an arbitrarily + * aligned, read-only source such as a memory-mapped file. + * Note also that the viewfax decoder does not check for + * running off the end of the input data buffer. This is + * possible for G3-encoded data because it prescans the input + * data to count EOL markers, but can cause problems for G4 + * data. In any event, we don't prescan and must watch for + * running out of data since we can't permit the library to + * scan past the end of the input data buffer. + * + * Finally, note that we must handle remaindered data at the end + * of a strip specially. The coder asks for a fixed number of + * bits when scanning for the next code. This may be more bits + * than are actually present in the data stream. If we appear + * to run out of data but still have some number of valid bits + * remaining then we makeup the requested amount with zeros and + * return successfully. If the returned data is incorrect then + * we should be called again and get a premature EOF error; + * otherwise we should get the right answer. + */ +#ifndef NeedBits8 +#define NeedBits8(n,eoflab) do { \ + if (BitsAvail < (n)) { \ + if (EndOfData()) { \ + if (BitsAvail == 0) /* no valid bits */ \ + goto eoflab; \ + BitsAvail = (n); /* pad with zeros */ \ + } else { \ + BitAcc |= ((uint32) bitmap[*cp++])<>= (n); \ +} while (0) + +#ifdef FAX3_DEBUG +static const char* StateNames[] = { + "Null ", + "Pass ", + "Horiz ", + "V0 ", + "VR ", + "VL ", + "Ext ", + "TermW ", + "TermB ", + "MakeUpW", + "MakeUpB", + "MakeUp ", + "EOL ", +}; +#define DEBUG_SHOW putchar(BitAcc & (1 << t) ? '1' : '0') +#define LOOKUP8(wid,tab,eoflab) do { \ + int t; \ + NeedBits8(wid,eoflab); \ + TabEnt = tab + GetBits(wid); \ + printf("%08lX/%d: %s%5d\t", (long) BitAcc, BitsAvail, \ + StateNames[TabEnt->State], TabEnt->Param); \ + for (t = 0; t < TabEnt->Width; t++) \ + DEBUG_SHOW; \ + putchar('\n'); \ + fflush(stdout); \ + ClrBits(TabEnt->Width); \ +} while (0) +#define LOOKUP16(wid,tab,eoflab) do { \ + int t; \ + NeedBits16(wid,eoflab); \ + TabEnt = tab + GetBits(wid); \ + printf("%08lX/%d: %s%5d\t", (long) BitAcc, BitsAvail, \ + StateNames[TabEnt->State], TabEnt->Param); \ + for (t = 0; t < TabEnt->Width; t++) \ + DEBUG_SHOW; \ + putchar('\n'); \ + fflush(stdout); \ + ClrBits(TabEnt->Width); \ +} while (0) + +#define SETVALUE(x) do { \ + *pa++ = RunLength + (x); \ + printf("SETVALUE: %d\t%d\n", RunLength + (x), a0); \ + a0 += x; \ + RunLength = 0; \ +} while (0) +#else +#define LOOKUP8(wid,tab,eoflab) do { \ + NeedBits8(wid,eoflab); \ + TabEnt = tab + GetBits(wid); \ + ClrBits(TabEnt->Width); \ +} while (0) +#define LOOKUP16(wid,tab,eoflab) do { \ + NeedBits16(wid,eoflab); \ + TabEnt = tab + GetBits(wid); \ + ClrBits(TabEnt->Width); \ +} while (0) + +/* + * Append a run to the run length array for the + * current row and reset decoding state. + */ +#define SETVALUE(x) do { \ + *pa++ = RunLength + (x); \ + a0 += (x); \ + RunLength = 0; \ +} while (0) +#endif + +/* + * Synchronize input decoding at the start of each + * row by scanning for an EOL (if appropriate) and + * skipping any trash data that might be present + * after a decoding error. Note that the decoding + * done elsewhere that recognizes an EOL only consumes + * 11 consecutive zero bits. This means that if EOLcnt + * is non-zero then we still need to scan for the final flag + * bit that is part of the EOL code. + */ +#define SYNC_EOL(eoflab) do { \ + if (EOLcnt == 0) { \ + for (;;) { \ + NeedBits16(11,eoflab); \ + if (GetBits(11) == 0) \ + break; \ + ClrBits(1); \ + } \ + } \ + for (;;) { \ + NeedBits8(8,eoflab); \ + if (GetBits(8)) \ + break; \ + ClrBits(8); \ + } \ + while (GetBits(1) == 0) \ + ClrBits(1); \ + ClrBits(1); /* EOL bit */ \ + EOLcnt = 0; /* reset EOL counter/flag */ \ +} while (0) + +/* + * Cleanup the array of runs after decoding a row. + * We adjust final runs to insure the user buffer is not + * overwritten and/or undecoded area is white filled. + */ +#define CLEANUP_RUNS() do { \ + if (RunLength) \ + SETVALUE(0); \ + if (a0 != lastx) { \ + badlength(a0, lastx); \ + while (a0 > lastx && pa > thisrun) \ + a0 -= *--pa; \ + if (a0 < lastx) { \ + if (a0 < 0) \ + a0 = 0; \ + if ((pa-thisrun)&1) \ + SETVALUE(0); \ + SETVALUE(lastx - a0); \ + } else if (a0 > lastx) { \ + SETVALUE(lastx); \ + SETVALUE(0); \ + } \ + } \ +} while (0) + +/* + * Decode a line of 1D-encoded data. + * + * The line expanders are written as macros so that they can be reused + * but still have direct access to the local variables of the "calling" + * function. + * + * Note that unlike the original version we have to explicitly test for + * a0 >= lastx after each black/white run is decoded. This is because + * the original code depended on the input data being zero-padded to + * insure the decoder recognized an EOL before running out of data. + */ +#define EXPAND1D(eoflab) do { \ + for (;;) { \ + for (;;) { \ + LOOKUP16(12, TIFFFaxWhiteTable, eof1d); \ + switch (TabEnt->State) { \ + case S_EOL: \ + EOLcnt = 1; \ + goto done1d; \ + case S_TermW: \ + SETVALUE(TabEnt->Param); \ + goto doneWhite1d; \ + case S_MakeUpW: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + unexpected("WhiteTable", a0); \ + goto done1d; \ + } \ + } \ + doneWhite1d: \ + if (a0 >= lastx) \ + goto done1d; \ + for (;;) { \ + LOOKUP16(13, TIFFFaxBlackTable, eof1d); \ + switch (TabEnt->State) { \ + case S_EOL: \ + EOLcnt = 1; \ + goto done1d; \ + case S_TermB: \ + SETVALUE(TabEnt->Param); \ + goto doneBlack1d; \ + case S_MakeUpB: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + unexpected("BlackTable", a0); \ + goto done1d; \ + } \ + } \ + doneBlack1d: \ + if (a0 >= lastx) \ + goto done1d; \ + if( *(pa-1) == 0 && *(pa-2) == 0 ) \ + pa -= 2; \ + } \ +eof1d: \ + prematureEOF(a0); \ + CLEANUP_RUNS(); \ + goto eoflab; \ +done1d: \ + CLEANUP_RUNS(); \ +} while (0) + +/* + * Update the value of b1 using the array + * of runs for the reference line. + */ +#define CHECK_b1 do { \ + if (pa != thisrun) while (b1 <= a0 && b1 < lastx) { \ + b1 += pb[0] + pb[1]; \ + pb += 2; \ + } \ +} while (0) + +/* + * Expand a row of 2D-encoded data. + */ +#define EXPAND2D(eoflab) do { \ + while (a0 < lastx) { \ + LOOKUP8(7, TIFFFaxMainTable, eof2d); \ + switch (TabEnt->State) { \ + case S_Pass: \ + CHECK_b1; \ + b1 += *pb++; \ + RunLength += b1 - a0; \ + a0 = b1; \ + b1 += *pb++; \ + break; \ + case S_Horiz: \ + if ((pa-thisrun)&1) { \ + for (;;) { /* black first */ \ + LOOKUP16(13, TIFFFaxBlackTable, eof2d); \ + switch (TabEnt->State) { \ + case S_TermB: \ + SETVALUE(TabEnt->Param); \ + goto doneWhite2da; \ + case S_MakeUpB: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + goto badBlack2d; \ + } \ + } \ + doneWhite2da:; \ + for (;;) { /* then white */ \ + LOOKUP16(12, TIFFFaxWhiteTable, eof2d); \ + switch (TabEnt->State) { \ + case S_TermW: \ + SETVALUE(TabEnt->Param); \ + goto doneBlack2da; \ + case S_MakeUpW: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + goto badWhite2d; \ + } \ + } \ + doneBlack2da:; \ + } else { \ + for (;;) { /* white first */ \ + LOOKUP16(12, TIFFFaxWhiteTable, eof2d); \ + switch (TabEnt->State) { \ + case S_TermW: \ + SETVALUE(TabEnt->Param); \ + goto doneWhite2db; \ + case S_MakeUpW: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + goto badWhite2d; \ + } \ + } \ + doneWhite2db:; \ + for (;;) { /* then black */ \ + LOOKUP16(13, TIFFFaxBlackTable, eof2d); \ + switch (TabEnt->State) { \ + case S_TermB: \ + SETVALUE(TabEnt->Param); \ + goto doneBlack2db; \ + case S_MakeUpB: \ + case S_MakeUp: \ + a0 += TabEnt->Param; \ + RunLength += TabEnt->Param; \ + break; \ + default: \ + goto badBlack2d; \ + } \ + } \ + doneBlack2db:; \ + } \ + CHECK_b1; \ + break; \ + case S_V0: \ + CHECK_b1; \ + SETVALUE(b1 - a0); \ + b1 += *pb++; \ + break; \ + case S_VR: \ + CHECK_b1; \ + SETVALUE(b1 - a0 + TabEnt->Param); \ + b1 += *pb++; \ + break; \ + case S_VL: \ + CHECK_b1; \ + if (b1 <= (int) (a0 + TabEnt->Param)) { \ + if (b1 < (int) (a0 + TabEnt->Param) || pa != thisrun) { \ + unexpected("VL", a0); \ + goto eol2d; \ + } \ + } \ + SETVALUE(b1 - a0 - TabEnt->Param); \ + b1 -= *--pb; \ + break; \ + case S_Ext: \ + *pa++ = lastx - a0; \ + extension(a0); \ + goto eol2d; \ + case S_EOL: \ + *pa++ = lastx - a0; \ + NeedBits8(4,eof2d); \ + if (GetBits(4)) \ + unexpected("EOL", a0); \ + ClrBits(4); \ + EOLcnt = 1; \ + goto eol2d; \ + default: \ + badMain2d: \ + unexpected("MainTable", a0); \ + goto eol2d; \ + badBlack2d: \ + unexpected("BlackTable", a0); \ + goto eol2d; \ + badWhite2d: \ + unexpected("WhiteTable", a0); \ + goto eol2d; \ + eof2d: \ + prematureEOF(a0); \ + CLEANUP_RUNS(); \ + goto eoflab; \ + } \ + } \ + if (RunLength) { \ + if (RunLength + a0 < lastx) { \ + /* expect a final V0 */ \ + NeedBits8(1,eof2d); \ + if (!GetBits(1)) \ + goto badMain2d; \ + ClrBits(1); \ + } \ + SETVALUE(0); \ + } \ +eol2d: \ + CLEANUP_RUNS(); \ +} while (0) +#endif /* _FAX3_ */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_fax3sm.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_fax3sm.c new file mode 100644 index 000000000..822191ecf --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_fax3sm.c @@ -0,0 +1,1260 @@ +/* WARNING, this file was automatically generated by the + mkg3states program */ +#include "tiff.h" +#include "tif_fax3.h" + const TIFFFaxTabEnt TIFFFaxMainTable[128] = { +{12,7,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0}, +{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{5,6,2},{3,1,0},{5,3,1},{3,1,0}, +{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0}, +{4,3,1},{3,1,0},{5,7,3},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0}, +{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{4,6,2},{3,1,0}, +{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0}, +{2,3,0},{3,1,0},{4,3,1},{3,1,0},{6,7,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0}, +{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0}, +{5,6,2},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0}, +{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0},{4,7,3},{3,1,0},{5,3,1},{3,1,0}, +{2,3,0},{3,1,0},{4,3,1},{3,1,0},{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0}, +{4,3,1},{3,1,0},{4,6,2},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0}, +{1,4,0},{3,1,0},{5,3,1},{3,1,0},{2,3,0},{3,1,0},{4,3,1},{3,1,0} +}; + const TIFFFaxTabEnt TIFFFaxWhiteTable[4096] = { +{12,11,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128}, +{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6}, +{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7}, +{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8}, +{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1792},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16}, +{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128}, +{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5}, +{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3}, +{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9}, +{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4}, +{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6}, +{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3}, +{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15}, +{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17}, +{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128}, +{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5}, +{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6}, +{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1856},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8}, +{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5}, +{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14}, +{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16}, +{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128}, +{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3}, +{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9}, +{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4}, +{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6}, +{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3}, +{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15}, +{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17}, +{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{11,12,2112},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128}, +{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6}, +{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7}, +{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8}, +{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16}, +{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128}, +{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5}, +{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3}, +{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9}, +{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2368},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4}, +{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6}, +{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3}, +{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15}, +{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17}, +{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128}, +{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5}, +{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6}, +{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8}, +{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5}, +{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14}, +{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16}, +{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128}, +{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{11,12,1984},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3}, +{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9}, +{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4}, +{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6}, +{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3}, +{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15}, +{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17}, +{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128}, +{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6}, +{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7}, +{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8}, +{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1920},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16}, +{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128}, +{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5}, +{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3}, +{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9}, +{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4}, +{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6}, +{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3}, +{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15}, +{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17}, +{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128}, +{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5}, +{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6}, +{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2240},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8}, +{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5}, +{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14}, +{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16}, +{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128}, +{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3}, +{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9}, +{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4}, +{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6}, +{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3}, +{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15}, +{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17}, +{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{11,12,2496},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128}, +{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6}, +{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7}, +{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8}, +{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{12,11,0},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16}, +{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128}, +{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5}, +{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3}, +{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9}, +{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1792},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4}, +{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6}, +{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3}, +{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15}, +{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17}, +{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128}, +{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5}, +{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6}, +{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8}, +{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5}, +{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14}, +{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16}, +{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128}, +{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{11,11,1856},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3}, +{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9}, +{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4}, +{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6}, +{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3}, +{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15}, +{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17}, +{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128}, +{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6}, +{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7}, +{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6},{7,8,31},{7,5,8}, +{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2176},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16}, +{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128}, +{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1600},{7,4,5}, +{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3}, +{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9}, +{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4}, +{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6}, +{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3}, +{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15}, +{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5},{7,8,43},{7,6,17}, +{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128}, +{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5}, +{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,768},{7,4,6}, +{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2432},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6},{7,7,19},{7,5,8}, +{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5}, +{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17},{9,9,1408},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14}, +{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16}, +{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128}, +{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3}, +{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9}, +{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4}, +{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,960},{7,4,6}, +{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3}, +{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15}, +{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17}, +{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{11,12,2048},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128}, +{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6}, +{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7}, +{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6},{7,8,32},{7,5,8}, +{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16}, +{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128}, +{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1536},{7,4,5}, +{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3}, +{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9}, +{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,11,1920},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4}, +{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,896},{7,4,6}, +{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3}, +{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15}, +{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5},{7,8,44},{7,6,17}, +{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128}, +{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5}, +{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6}, +{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7}, +{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8}, +{7,8,55},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5}, +{7,8,53},{7,5,9},{9,8,448},{7,4,6},{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1472},{7,4,5},{7,8,43},{7,6,17},{9,9,1216},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14}, +{7,8,61},{7,4,4},{7,4,2},{7,4,7},{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16}, +{9,9,960},{7,4,6},{7,8,31},{7,5,8},{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,9,704},{7,4,6},{7,8,37},{9,5,128}, +{7,7,25},{7,6,15},{9,8,320},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5}, +{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{11,12,2304},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,7,20},{9,5,128},{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3}, +{7,7,27},{7,4,5},{7,8,40},{7,6,16},{9,9,832},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9}, +{9,8,512},{7,4,6},{7,8,36},{9,5,128},{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{9,9,1600},{7,4,5},{7,8,44},{7,6,17},{9,9,1344},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5}, +{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4}, +{7,4,2},{7,4,7},{7,8,48},{7,4,3},{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1088},{7,4,6}, +{7,8,32},{7,5,8},{7,8,58},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3}, +{7,5,11},{7,4,5},{7,7,26},{7,5,9},{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15}, +{9,8,384},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17}, +{9,7,256},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{0,0,0},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128}, +{7,7,24},{7,6,14},{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5}, +{7,8,39},{7,6,16},{9,8,576},{7,4,6},{7,7,19},{7,5,8},{7,8,55},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,45},{7,4,3},{7,5,11},{7,4,5},{7,8,53},{7,5,9},{9,8,448},{7,4,6}, +{7,8,35},{9,5,128},{7,8,51},{7,6,15},{7,8,63},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3}, +{9,9,1536},{7,4,5},{7,8,43},{7,6,17},{9,9,1280},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,8,29},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9}, +{9,6,1664},{7,4,6},{7,8,33},{9,5,128},{7,8,49},{7,6,14},{7,8,61},{7,4,4},{7,4,2},{7,4,7}, +{7,8,47},{7,4,3},{7,8,59},{7,4,5},{7,8,41},{7,6,16},{9,9,1024},{7,4,6},{7,8,31},{7,5,8}, +{7,8,57},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5}, +{7,7,26},{7,5,9},{9,9,768},{7,4,6},{7,8,37},{9,5,128},{7,7,25},{7,6,15},{9,8,320},{7,4,4}, +{7,4,2},{7,4,7},{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6}, +{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7},{11,12,2560},{7,4,3}, +{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6},{7,7,20},{9,5,128},{7,7,24},{7,6,14}, +{7,7,28},{7,4,4},{7,4,2},{7,4,7},{7,7,23},{7,4,3},{7,7,27},{7,4,5},{7,8,40},{7,6,16}, +{9,9,896},{7,4,6},{7,7,19},{7,5,8},{7,8,56},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7}, +{7,8,46},{7,4,3},{7,5,11},{7,4,5},{7,8,54},{7,5,9},{9,8,512},{7,4,6},{7,8,36},{9,5,128}, +{7,8,52},{7,6,15},{7,8,0},{7,4,4},{7,4,2},{7,4,7},{7,6,13},{7,4,3},{9,9,1728},{7,4,5}, +{7,8,44},{7,6,17},{9,9,1408},{7,4,6},{7,6,1},{7,5,8},{9,6,192},{9,5,64},{7,5,10},{7,4,4}, +{7,4,2},{7,4,7},{7,8,30},{7,4,3},{7,5,11},{7,4,5},{7,6,12},{7,5,9},{9,6,1664},{7,4,6}, +{7,8,34},{9,5,128},{7,8,50},{7,6,14},{7,8,62},{7,4,4},{7,4,2},{7,4,7},{7,8,48},{7,4,3}, +{7,8,60},{7,4,5},{7,8,42},{7,6,16},{9,9,1152},{7,4,6},{7,8,32},{7,5,8},{7,8,58},{9,5,64}, +{7,5,10},{7,4,4},{7,4,2},{7,4,7},{7,7,22},{7,4,3},{7,5,11},{7,4,5},{7,7,26},{7,5,9}, +{9,8,640},{7,4,6},{7,8,38},{9,5,128},{7,7,25},{7,6,15},{9,8,384},{7,4,4},{7,4,2},{7,4,7}, +{7,6,13},{7,4,3},{7,7,18},{7,4,5},{7,7,21},{7,6,17},{9,7,256},{7,4,6},{7,6,1},{7,5,8}, +{9,6,192},{9,5,64},{7,5,10},{7,4,4},{7,4,2},{7,4,7} +}; + const TIFFFaxTabEnt TIFFFaxBlackTable[8192] = { +{12,11,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1792},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,23},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,20},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,11,25},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,128},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,56},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,30},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1856},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,57},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,11,21},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,54},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,52},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,48},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,12,2112},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,44},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,36},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,384},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,28},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,60},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,40},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2368},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{11,12,1984},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,50},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,34},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1664},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,26},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1408},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,32},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1920},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,61},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,42},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{10,13,1024},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,13,768},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,62},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2240},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,46},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,38},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,512},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,19},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,24},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,22},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,12,2496},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{12,11,0},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1792},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,23},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,20},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,25},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{10,12,192},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1280},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,31},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{11,11,1856},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,58},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,21},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,896},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,640},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,49},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2176},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,45},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,37},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{10,12,448},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,29},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,13,1536},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,41},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2432},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,12,2048},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,51},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,35},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,320},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,27},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,59},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,33},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1920},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,256},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,43},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,13,1152},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,55},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,63},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{11,12,2304},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,47},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,39},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,53},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,19},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,24},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,22},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2560},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{12,11,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1792},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,23},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,11,20},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,25},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,12,128},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,56},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,30},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,11,1856},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,57},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,21},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,54},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,52},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,48},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2112},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,44},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,36},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,12,384},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,28},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,60},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,40},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{11,12,2368},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,1984},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,50},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,34},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{10,13,1728},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,26},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,13,1472},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,32},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1920},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,61},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,42},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1088},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,832},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,62},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,12,2240},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,46},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,38},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,576},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,19},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,11,24},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,22},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2496},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{12,11,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,18},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{11,11,1792},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,23},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,20},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,25},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,192},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1344},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,31},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,11,1856},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,58},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,21},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{10,13,960},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,13,704},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,49},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2176},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,45},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,37},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,448},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,29},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1600},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,41},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{11,12,2432},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,0},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,10,18},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,17},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2048},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,51},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,35},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{10,12,320},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,27},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,59},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,33},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{11,11,1920},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,12,256},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,43},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,13,1216},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{0,0,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,8,13},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,9,15},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,55},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,63},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2304},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,12,47},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,12,39},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,12,53},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,12},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{0,0,0},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,8,13},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,11,19},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,11,24},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,11,22},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{11,12,2560},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,7,10},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,10,16},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2},{8,10,0},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2}, +{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{10,10,64},{8,2,3}, +{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,9},{8,2,3},{8,3,1},{8,2,2}, +{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,11},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3}, +{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2}, +{8,8,14},{8,2,3},{8,3,1},{8,2,2},{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,6,8},{8,2,3}, +{8,3,1},{8,2,2},{8,4,5},{8,2,3},{8,3,4},{8,2,2},{8,7,12},{8,2,3},{8,3,1},{8,2,2}, +{8,4,6},{8,2,3},{8,3,4},{8,2,2},{8,5,7},{8,2,3},{8,3,1},{8,2,2},{8,4,5},{8,2,3}, +{8,3,4},{8,2,2} +}; +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_flush.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_flush.c new file mode 100644 index 000000000..fd14e4cda --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_flush.c @@ -0,0 +1,118 @@ +/* $Id: tif_flush.c,v 1.9 2010-03-31 06:40:10 fwarmerdam Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + */ +#include "tiffiop.h" + +int +TIFFFlush(TIFF* tif) +{ + if( tif->tif_mode == O_RDONLY ) + return 1; + + if (!TIFFFlushData(tif)) + return (0); + + /* In update (r+) mode we try to detect the case where + only the strip/tile map has been altered, and we try to + rewrite only that portion of the directory without + making any other changes */ + + if( (tif->tif_flags & TIFF_DIRTYSTRIP) + && !(tif->tif_flags & TIFF_DIRTYDIRECT) + && tif->tif_mode == O_RDWR ) + { + uint64 *offsets=NULL, *sizes=NULL; + + if( TIFFIsTiled(tif) ) + { + if( TIFFGetField( tif, TIFFTAG_TILEOFFSETS, &offsets ) + && TIFFGetField( tif, TIFFTAG_TILEBYTECOUNTS, &sizes ) + && _TIFFRewriteField( tif, TIFFTAG_TILEOFFSETS, TIFF_LONG8, + tif->tif_dir.td_nstrips, offsets ) + && _TIFFRewriteField( tif, TIFFTAG_TILEBYTECOUNTS, TIFF_LONG8, + tif->tif_dir.td_nstrips, sizes ) ) + { + tif->tif_flags &= ~TIFF_DIRTYSTRIP; + tif->tif_flags &= ~TIFF_BEENWRITING; + return 1; + } + } + else + { + if( TIFFGetField( tif, TIFFTAG_STRIPOFFSETS, &offsets ) + && TIFFGetField( tif, TIFFTAG_STRIPBYTECOUNTS, &sizes ) + && _TIFFRewriteField( tif, TIFFTAG_STRIPOFFSETS, TIFF_LONG8, + tif->tif_dir.td_nstrips, offsets ) + && _TIFFRewriteField( tif, TIFFTAG_STRIPBYTECOUNTS, TIFF_LONG8, + tif->tif_dir.td_nstrips, sizes ) ) + { + tif->tif_flags &= ~TIFF_DIRTYSTRIP; + tif->tif_flags &= ~TIFF_BEENWRITING; + return 1; + } + } + } + + if ((tif->tif_flags & (TIFF_DIRTYDIRECT|TIFF_DIRTYSTRIP)) + && !TIFFRewriteDirectory(tif)) + return (0); + + return (1); +} + +/* + * Flush buffered data to the file. + * + * Frank Warmerdam'2000: I modified this to return 1 if TIFF_BEENWRITING + * is not set, so that TIFFFlush() will proceed to write out the directory. + * The documentation says returning 1 is an error indicator, but not having + * been writing isn't exactly a an error. Hopefully this doesn't cause + * problems for other people. + */ +int +TIFFFlushData(TIFF* tif) +{ + if ((tif->tif_flags & TIFF_BEENWRITING) == 0) + return (1); + if (tif->tif_flags & TIFF_POSTENCODE) { + tif->tif_flags &= ~TIFF_POSTENCODE; + if (!(*tif->tif_postencode)(tif)) + return (0); + } + return (TIFFFlushData1(tif)); +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_getimage.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_getimage.c new file mode 100644 index 000000000..a856cc50e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_getimage.c @@ -0,0 +1,2890 @@ +/* $Id: tif_getimage.c,v 1.87 2014-12-29 18:28:46 erouault Exp $ */ + +/* + * Copyright (c) 1991-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library + * + * Read and return a packed RGBA image. + */ +#include "tiffiop.h" +#include + +static int gtTileContig(TIFFRGBAImage*, uint32*, uint32, uint32); +static int gtTileSeparate(TIFFRGBAImage*, uint32*, uint32, uint32); +static int gtStripContig(TIFFRGBAImage*, uint32*, uint32, uint32); +static int gtStripSeparate(TIFFRGBAImage*, uint32*, uint32, uint32); +static int PickContigCase(TIFFRGBAImage*); +static int PickSeparateCase(TIFFRGBAImage*); + +static int BuildMapUaToAa(TIFFRGBAImage* img); +static int BuildMapBitdepth16To8(TIFFRGBAImage* img); + +static const char photoTag[] = "PhotometricInterpretation"; + +/* + * Helper constants used in Orientation tag handling + */ +#define FLIP_VERTICALLY 0x01 +#define FLIP_HORIZONTALLY 0x02 + +/* + * Color conversion constants. We will define display types here. + */ + +static const TIFFDisplay display_sRGB = { + { /* XYZ -> luminance matrix */ + { 3.2410F, -1.5374F, -0.4986F }, + { -0.9692F, 1.8760F, 0.0416F }, + { 0.0556F, -0.2040F, 1.0570F } + }, + 100.0F, 100.0F, 100.0F, /* Light o/p for reference white */ + 255, 255, 255, /* Pixel values for ref. white */ + 1.0F, 1.0F, 1.0F, /* Residual light o/p for black pixel */ + 2.4F, 2.4F, 2.4F, /* Gamma values for the three guns */ +}; + +/* + * Check the image to see if TIFFReadRGBAImage can deal with it. + * 1/0 is returned according to whether or not the image can + * be handled. If 0 is returned, emsg contains the reason + * why it is being rejected. + */ +int +TIFFRGBAImageOK(TIFF* tif, char emsg[1024]) +{ + TIFFDirectory* td = &tif->tif_dir; + uint16 photometric; + int colorchannels; + + if (!tif->tif_decodestatus) { + sprintf(emsg, "Sorry, requested compression method is not configured"); + return (0); + } + switch (td->td_bitspersample) { + case 1: + case 2: + case 4: + case 8: + case 16: + break; + default: + sprintf(emsg, "Sorry, can not handle images with %d-bit samples", + td->td_bitspersample); + return (0); + } + colorchannels = td->td_samplesperpixel - td->td_extrasamples; + if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric)) { + switch (colorchannels) { + case 1: + photometric = PHOTOMETRIC_MINISBLACK; + break; + case 3: + photometric = PHOTOMETRIC_RGB; + break; + default: + sprintf(emsg, "Missing needed %s tag", photoTag); + return (0); + } + } + switch (photometric) { + case PHOTOMETRIC_MINISWHITE: + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_PALETTE: + if (td->td_planarconfig == PLANARCONFIG_CONTIG + && td->td_samplesperpixel != 1 + && td->td_bitspersample < 8 ) { + sprintf(emsg, + "Sorry, can not handle contiguous data with %s=%d, " + "and %s=%d and Bits/Sample=%d", + photoTag, photometric, + "Samples/pixel", td->td_samplesperpixel, + td->td_bitspersample); + return (0); + } + /* + * We should likely validate that any extra samples are either + * to be ignored, or are alpha, and if alpha we should try to use + * them. But for now we won't bother with this. + */ + break; + case PHOTOMETRIC_YCBCR: + /* + * TODO: if at all meaningful and useful, make more complete + * support check here, or better still, refactor to let supporting + * code decide whether there is support and what meaningfull + * error to return + */ + break; + case PHOTOMETRIC_RGB: + if (colorchannels < 3) { + sprintf(emsg, "Sorry, can not handle RGB image with %s=%d", + "Color channels", colorchannels); + return (0); + } + break; + case PHOTOMETRIC_SEPARATED: + { + uint16 inkset; + TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset); + if (inkset != INKSET_CMYK) { + sprintf(emsg, + "Sorry, can not handle separated image with %s=%d", + "InkSet", inkset); + return 0; + } + if (td->td_samplesperpixel < 4) { + sprintf(emsg, + "Sorry, can not handle separated image with %s=%d", + "Samples/pixel", td->td_samplesperpixel); + return 0; + } + break; + } + case PHOTOMETRIC_LOGL: + if (td->td_compression != COMPRESSION_SGILOG) { + sprintf(emsg, "Sorry, LogL data must have %s=%d", + "Compression", COMPRESSION_SGILOG); + return (0); + } + break; + case PHOTOMETRIC_LOGLUV: + if (td->td_compression != COMPRESSION_SGILOG && + td->td_compression != COMPRESSION_SGILOG24) { + sprintf(emsg, "Sorry, LogLuv data must have %s=%d or %d", + "Compression", COMPRESSION_SGILOG, COMPRESSION_SGILOG24); + return (0); + } + if (td->td_planarconfig != PLANARCONFIG_CONTIG) { + sprintf(emsg, "Sorry, can not handle LogLuv images with %s=%d", + "Planarconfiguration", td->td_planarconfig); + return (0); + } + if( td->td_samplesperpixel != 3 ) + { + sprintf(emsg, + "Sorry, can not handle image with %s=%d", + "Samples/pixel", td->td_samplesperpixel); + return 0; + } + break; + case PHOTOMETRIC_CIELAB: + if( td->td_samplesperpixel != 3 || td->td_bitspersample != 8 ) + { + sprintf(emsg, + "Sorry, can not handle image with %s=%d and %s=%d", + "Samples/pixel", td->td_samplesperpixel, + "Bits/sample", td->td_bitspersample); + return 0; + } + break; + default: + sprintf(emsg, "Sorry, can not handle image with %s=%d", + photoTag, photometric); + return (0); + } + return (1); +} + +void +TIFFRGBAImageEnd(TIFFRGBAImage* img) +{ + if (img->Map) + _TIFFfree(img->Map), img->Map = NULL; + if (img->BWmap) + _TIFFfree(img->BWmap), img->BWmap = NULL; + if (img->PALmap) + _TIFFfree(img->PALmap), img->PALmap = NULL; + if (img->ycbcr) + _TIFFfree(img->ycbcr), img->ycbcr = NULL; + if (img->cielab) + _TIFFfree(img->cielab), img->cielab = NULL; + if (img->UaToAa) + _TIFFfree(img->UaToAa), img->UaToAa = NULL; + if (img->Bitdepth16To8) + _TIFFfree(img->Bitdepth16To8), img->Bitdepth16To8 = NULL; + + if( img->redcmap ) { + _TIFFfree( img->redcmap ); + _TIFFfree( img->greencmap ); + _TIFFfree( img->bluecmap ); + img->redcmap = img->greencmap = img->bluecmap = NULL; + } +} + +static int +isCCITTCompression(TIFF* tif) +{ + uint16 compress; + TIFFGetField(tif, TIFFTAG_COMPRESSION, &compress); + return (compress == COMPRESSION_CCITTFAX3 || + compress == COMPRESSION_CCITTFAX4 || + compress == COMPRESSION_CCITTRLE || + compress == COMPRESSION_CCITTRLEW); +} + +int +TIFFRGBAImageBegin(TIFFRGBAImage* img, TIFF* tif, int stop, char emsg[1024]) +{ + uint16* sampleinfo; + uint16 extrasamples; + uint16 planarconfig; + uint16 compress; + int colorchannels; + uint16 *red_orig, *green_orig, *blue_orig; + int n_color; + + /* Initialize to normal values */ + img->row_offset = 0; + img->col_offset = 0; + img->redcmap = NULL; + img->greencmap = NULL; + img->bluecmap = NULL; + img->req_orientation = ORIENTATION_BOTLEFT; /* It is the default */ + + img->tif = tif; + img->stoponerr = stop; + TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &img->bitspersample); + switch (img->bitspersample) { + case 1: + case 2: + case 4: + case 8: + case 16: + break; + default: + sprintf(emsg, "Sorry, can not handle images with %d-bit samples", + img->bitspersample); + goto fail_return; + } + img->alpha = 0; + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &img->samplesperpixel); + TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES, + &extrasamples, &sampleinfo); + if (extrasamples >= 1) + { + switch (sampleinfo[0]) { + case EXTRASAMPLE_UNSPECIFIED: /* Workaround for some images without */ + if (img->samplesperpixel > 3) /* correct info about alpha channel */ + img->alpha = EXTRASAMPLE_ASSOCALPHA; + break; + case EXTRASAMPLE_ASSOCALPHA: /* data is pre-multiplied */ + case EXTRASAMPLE_UNASSALPHA: /* data is not pre-multiplied */ + img->alpha = sampleinfo[0]; + break; + } + } + +#ifdef DEFAULT_EXTRASAMPLE_AS_ALPHA + if( !TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &img->photometric)) + img->photometric = PHOTOMETRIC_MINISWHITE; + + if( extrasamples == 0 + && img->samplesperpixel == 4 + && img->photometric == PHOTOMETRIC_RGB ) + { + img->alpha = EXTRASAMPLE_ASSOCALPHA; + extrasamples = 1; + } +#endif + + colorchannels = img->samplesperpixel - extrasamples; + TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compress); + TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planarconfig); + if (!TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &img->photometric)) { + switch (colorchannels) { + case 1: + if (isCCITTCompression(tif)) + img->photometric = PHOTOMETRIC_MINISWHITE; + else + img->photometric = PHOTOMETRIC_MINISBLACK; + break; + case 3: + img->photometric = PHOTOMETRIC_RGB; + break; + default: + sprintf(emsg, "Missing needed %s tag", photoTag); + goto fail_return; + } + } + switch (img->photometric) { + case PHOTOMETRIC_PALETTE: + if (!TIFFGetField(tif, TIFFTAG_COLORMAP, + &red_orig, &green_orig, &blue_orig)) { + sprintf(emsg, "Missing required \"Colormap\" tag"); + goto fail_return; + } + + /* copy the colormaps so we can modify them */ + n_color = (1L << img->bitspersample); + img->redcmap = (uint16 *) _TIFFmalloc(sizeof(uint16)*n_color); + img->greencmap = (uint16 *) _TIFFmalloc(sizeof(uint16)*n_color); + img->bluecmap = (uint16 *) _TIFFmalloc(sizeof(uint16)*n_color); + if( !img->redcmap || !img->greencmap || !img->bluecmap ) { + sprintf(emsg, "Out of memory for colormap copy"); + goto fail_return; + } + + _TIFFmemcpy( img->redcmap, red_orig, n_color * 2 ); + _TIFFmemcpy( img->greencmap, green_orig, n_color * 2 ); + _TIFFmemcpy( img->bluecmap, blue_orig, n_color * 2 ); + + /* fall thru... */ + case PHOTOMETRIC_MINISWHITE: + case PHOTOMETRIC_MINISBLACK: + if (planarconfig == PLANARCONFIG_CONTIG + && img->samplesperpixel != 1 + && img->bitspersample < 8 ) { + sprintf(emsg, + "Sorry, can not handle contiguous data with %s=%d, " + "and %s=%d and Bits/Sample=%d", + photoTag, img->photometric, + "Samples/pixel", img->samplesperpixel, + img->bitspersample); + goto fail_return; + } + break; + case PHOTOMETRIC_YCBCR: + /* It would probably be nice to have a reality check here. */ + if (planarconfig == PLANARCONFIG_CONTIG) + /* can rely on libjpeg to convert to RGB */ + /* XXX should restore current state on exit */ + switch (compress) { + case COMPRESSION_JPEG: + /* + * TODO: when complete tests verify complete desubsampling + * and YCbCr handling, remove use of TIFFTAG_JPEGCOLORMODE in + * favor of tif_getimage.c native handling + */ + TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + img->photometric = PHOTOMETRIC_RGB; + break; + default: + /* do nothing */; + break; + } + /* + * TODO: if at all meaningful and useful, make more complete + * support check here, or better still, refactor to let supporting + * code decide whether there is support and what meaningfull + * error to return + */ + break; + case PHOTOMETRIC_RGB: + if (colorchannels < 3) { + sprintf(emsg, "Sorry, can not handle RGB image with %s=%d", + "Color channels", colorchannels); + goto fail_return; + } + break; + case PHOTOMETRIC_SEPARATED: + { + uint16 inkset; + TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset); + if (inkset != INKSET_CMYK) { + sprintf(emsg, "Sorry, can not handle separated image with %s=%d", + "InkSet", inkset); + goto fail_return; + } + if (img->samplesperpixel < 4) { + sprintf(emsg, "Sorry, can not handle separated image with %s=%d", + "Samples/pixel", img->samplesperpixel); + goto fail_return; + } + } + break; + case PHOTOMETRIC_LOGL: + if (compress != COMPRESSION_SGILOG) { + sprintf(emsg, "Sorry, LogL data must have %s=%d", + "Compression", COMPRESSION_SGILOG); + goto fail_return; + } + TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_8BIT); + img->photometric = PHOTOMETRIC_MINISBLACK; /* little white lie */ + img->bitspersample = 8; + break; + case PHOTOMETRIC_LOGLUV: + if (compress != COMPRESSION_SGILOG && compress != COMPRESSION_SGILOG24) { + sprintf(emsg, "Sorry, LogLuv data must have %s=%d or %d", + "Compression", COMPRESSION_SGILOG, COMPRESSION_SGILOG24); + goto fail_return; + } + if (planarconfig != PLANARCONFIG_CONTIG) { + sprintf(emsg, "Sorry, can not handle LogLuv images with %s=%d", + "Planarconfiguration", planarconfig); + return (0); + } + TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_8BIT); + img->photometric = PHOTOMETRIC_RGB; /* little white lie */ + img->bitspersample = 8; + break; + case PHOTOMETRIC_CIELAB: + break; + default: + sprintf(emsg, "Sorry, can not handle image with %s=%d", + photoTag, img->photometric); + goto fail_return; + } + img->Map = NULL; + img->BWmap = NULL; + img->PALmap = NULL; + img->ycbcr = NULL; + img->cielab = NULL; + img->UaToAa = NULL; + img->Bitdepth16To8 = NULL; + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &img->width); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &img->height); + TIFFGetFieldDefaulted(tif, TIFFTAG_ORIENTATION, &img->orientation); + img->isContig = + !(planarconfig == PLANARCONFIG_SEPARATE && img->samplesperpixel > 1); + if (img->isContig) { + if (!PickContigCase(img)) { + sprintf(emsg, "Sorry, can not handle image"); + goto fail_return; + } + } else { + if (!PickSeparateCase(img)) { + sprintf(emsg, "Sorry, can not handle image"); + goto fail_return; + } + } + return 1; + + fail_return: + _TIFFfree( img->redcmap ); + _TIFFfree( img->greencmap ); + _TIFFfree( img->bluecmap ); + img->redcmap = img->greencmap = img->bluecmap = NULL; + return 0; +} + +int +TIFFRGBAImageGet(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h) +{ + if (img->get == NULL) { + TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif), "No \"get\" routine setup"); + return (0); + } + if (img->put.any == NULL) { + TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif), + "No \"put\" routine setupl; probably can not handle image format"); + return (0); + } + return (*img->get)(img, raster, w, h); +} + +/* + * Read the specified image into an ABGR-format rastertaking in account + * specified orientation. + */ +int +TIFFReadRGBAImageOriented(TIFF* tif, + uint32 rwidth, uint32 rheight, uint32* raster, + int orientation, int stop) +{ + char emsg[1024] = ""; + TIFFRGBAImage img; + int ok; + + if (TIFFRGBAImageOK(tif, emsg) && TIFFRGBAImageBegin(&img, tif, stop, emsg)) { + img.req_orientation = orientation; + /* XXX verify rwidth and rheight against width and height */ + ok = TIFFRGBAImageGet(&img, raster+(rheight-img.height)*rwidth, + rwidth, img.height); + TIFFRGBAImageEnd(&img); + } else { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", emsg); + ok = 0; + } + return (ok); +} + +/* + * Read the specified image into an ABGR-format raster. Use bottom left + * origin for raster by default. + */ +int +TIFFReadRGBAImage(TIFF* tif, + uint32 rwidth, uint32 rheight, uint32* raster, int stop) +{ + return TIFFReadRGBAImageOriented(tif, rwidth, rheight, raster, + ORIENTATION_BOTLEFT, stop); +} + +static int +setorientation(TIFFRGBAImage* img) +{ + switch (img->orientation) { + case ORIENTATION_TOPLEFT: + case ORIENTATION_LEFTTOP: + if (img->req_orientation == ORIENTATION_TOPRIGHT || + img->req_orientation == ORIENTATION_RIGHTTOP) + return FLIP_HORIZONTALLY; + else if (img->req_orientation == ORIENTATION_BOTRIGHT || + img->req_orientation == ORIENTATION_RIGHTBOT) + return FLIP_HORIZONTALLY | FLIP_VERTICALLY; + else if (img->req_orientation == ORIENTATION_BOTLEFT || + img->req_orientation == ORIENTATION_LEFTBOT) + return FLIP_VERTICALLY; + else + return 0; + case ORIENTATION_TOPRIGHT: + case ORIENTATION_RIGHTTOP: + if (img->req_orientation == ORIENTATION_TOPLEFT || + img->req_orientation == ORIENTATION_LEFTTOP) + return FLIP_HORIZONTALLY; + else if (img->req_orientation == ORIENTATION_BOTRIGHT || + img->req_orientation == ORIENTATION_RIGHTBOT) + return FLIP_VERTICALLY; + else if (img->req_orientation == ORIENTATION_BOTLEFT || + img->req_orientation == ORIENTATION_LEFTBOT) + return FLIP_HORIZONTALLY | FLIP_VERTICALLY; + else + return 0; + case ORIENTATION_BOTRIGHT: + case ORIENTATION_RIGHTBOT: + if (img->req_orientation == ORIENTATION_TOPLEFT || + img->req_orientation == ORIENTATION_LEFTTOP) + return FLIP_HORIZONTALLY | FLIP_VERTICALLY; + else if (img->req_orientation == ORIENTATION_TOPRIGHT || + img->req_orientation == ORIENTATION_RIGHTTOP) + return FLIP_VERTICALLY; + else if (img->req_orientation == ORIENTATION_BOTLEFT || + img->req_orientation == ORIENTATION_LEFTBOT) + return FLIP_HORIZONTALLY; + else + return 0; + case ORIENTATION_BOTLEFT: + case ORIENTATION_LEFTBOT: + if (img->req_orientation == ORIENTATION_TOPLEFT || + img->req_orientation == ORIENTATION_LEFTTOP) + return FLIP_VERTICALLY; + else if (img->req_orientation == ORIENTATION_TOPRIGHT || + img->req_orientation == ORIENTATION_RIGHTTOP) + return FLIP_HORIZONTALLY | FLIP_VERTICALLY; + else if (img->req_orientation == ORIENTATION_BOTRIGHT || + img->req_orientation == ORIENTATION_RIGHTBOT) + return FLIP_HORIZONTALLY; + else + return 0; + default: /* NOTREACHED */ + return 0; + } +} + +/* + * Get an tile-organized image that has + * PlanarConfiguration contiguous if SamplesPerPixel > 1 + * or + * SamplesPerPixel == 1 + */ +static int +gtTileContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h) +{ + TIFF* tif = img->tif; + tileContigRoutine put = img->put.contig; + uint32 col, row, y, rowstoread; + tmsize_t pos; + uint32 tw, th; + unsigned char* buf; + int32 fromskew, toskew; + uint32 nrow; + int ret = 1, flip; + + buf = (unsigned char*) _TIFFmalloc(TIFFTileSize(tif)); + if (buf == 0) { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "No space for tile buffer"); + return (0); + } + _TIFFmemset(buf, 0, TIFFTileSize(tif)); + TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw); + TIFFGetField(tif, TIFFTAG_TILELENGTH, &th); + + flip = setorientation(img); + if (flip & FLIP_VERTICALLY) { + y = h - 1; + toskew = -(int32)(tw + w); + } + else { + y = 0; + toskew = -(int32)(tw - w); + } + + for (row = 0; row < h; row += nrow) + { + rowstoread = th - (row + img->row_offset) % th; + nrow = (row + rowstoread > h ? h - row : rowstoread); + for (col = 0; col < w; col += tw) + { + if (TIFFReadTile(tif, buf, col+img->col_offset, + row+img->row_offset, 0, 0)==(tmsize_t)(-1) && img->stoponerr) + { + ret = 0; + break; + } + + pos = ((row+img->row_offset) % th) * TIFFTileRowSize(tif); + + if (col + tw > w) + { + /* + * Tile is clipped horizontally. Calculate + * visible portion and skewing factors. + */ + uint32 npix = w - col; + fromskew = tw - npix; + (*put)(img, raster+y*w+col, col, y, + npix, nrow, fromskew, toskew + fromskew, buf + pos); + } + else + { + (*put)(img, raster+y*w+col, col, y, tw, nrow, 0, toskew, buf + pos); + } + } + + y += (flip & FLIP_VERTICALLY ? -(int32) nrow : (int32) nrow); + } + _TIFFfree(buf); + + if (flip & FLIP_HORIZONTALLY) { + uint32 line; + + for (line = 0; line < h; line++) { + uint32 *left = raster + (line * w); + uint32 *right = left + w - 1; + + while ( left < right ) { + uint32 temp = *left; + *left = *right; + *right = temp; + left++, right--; + } + } + } + + return (ret); +} + +/* + * Get an tile-organized image that has + * SamplesPerPixel > 1 + * PlanarConfiguration separated + * We assume that all such images are RGB. + */ +static int +gtTileSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h) +{ + TIFF* tif = img->tif; + tileSeparateRoutine put = img->put.separate; + uint32 col, row, y, rowstoread; + tmsize_t pos; + uint32 tw, th; + unsigned char* buf; + unsigned char* p0; + unsigned char* p1; + unsigned char* p2; + unsigned char* pa; + tmsize_t tilesize; + tmsize_t bufsize; + int32 fromskew, toskew; + int alpha = img->alpha; + uint32 nrow; + int ret = 1, flip; + int colorchannels; + + tilesize = TIFFTileSize(tif); + bufsize = TIFFSafeMultiply(tmsize_t,alpha?4:3,tilesize); + if (bufsize == 0) { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Integer overflow in %s", "gtTileSeparate"); + return (0); + } + buf = (unsigned char*) _TIFFmalloc(bufsize); + if (buf == 0) { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", "No space for tile buffer"); + return (0); + } + _TIFFmemset(buf, 0, bufsize); + p0 = buf; + p1 = p0 + tilesize; + p2 = p1 + tilesize; + pa = (alpha?(p2+tilesize):NULL); + TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw); + TIFFGetField(tif, TIFFTAG_TILELENGTH, &th); + + flip = setorientation(img); + if (flip & FLIP_VERTICALLY) { + y = h - 1; + toskew = -(int32)(tw + w); + } + else { + y = 0; + toskew = -(int32)(tw - w); + } + + switch( img->photometric ) + { + case PHOTOMETRIC_MINISWHITE: + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_PALETTE: + colorchannels = 1; + p2 = p1 = p0; + break; + + default: + colorchannels = 3; + break; + } + + for (row = 0; row < h; row += nrow) + { + rowstoread = th - (row + img->row_offset) % th; + nrow = (row + rowstoread > h ? h - row : rowstoread); + for (col = 0; col < w; col += tw) + { + if (TIFFReadTile(tif, p0, col+img->col_offset, + row+img->row_offset,0,0)==(tmsize_t)(-1) && img->stoponerr) + { + ret = 0; + break; + } + if (colorchannels > 1 + && TIFFReadTile(tif, p1, col+img->col_offset, + row+img->row_offset,0,1) == (tmsize_t)(-1) + && img->stoponerr) + { + ret = 0; + break; + } + if (colorchannels > 1 + && TIFFReadTile(tif, p2, col+img->col_offset, + row+img->row_offset,0,2) == (tmsize_t)(-1) + && img->stoponerr) + { + ret = 0; + break; + } + if (alpha + && TIFFReadTile(tif,pa,col+img->col_offset, + row+img->row_offset,0,colorchannels) == (tmsize_t)(-1) + && img->stoponerr) + { + ret = 0; + break; + } + + pos = ((row+img->row_offset) % th) * TIFFTileRowSize(tif); + + if (col + tw > w) + { + /* + * Tile is clipped horizontally. Calculate + * visible portion and skewing factors. + */ + uint32 npix = w - col; + fromskew = tw - npix; + (*put)(img, raster+y*w+col, col, y, + npix, nrow, fromskew, toskew + fromskew, + p0 + pos, p1 + pos, p2 + pos, (alpha?(pa+pos):NULL)); + } else { + (*put)(img, raster+y*w+col, col, y, + tw, nrow, 0, toskew, p0 + pos, p1 + pos, p2 + pos, (alpha?(pa+pos):NULL)); + } + } + + y += (flip & FLIP_VERTICALLY ?-(int32) nrow : (int32) nrow); + } + + if (flip & FLIP_HORIZONTALLY) { + uint32 line; + + for (line = 0; line < h; line++) { + uint32 *left = raster + (line * w); + uint32 *right = left + w - 1; + + while ( left < right ) { + uint32 temp = *left; + *left = *right; + *right = temp; + left++, right--; + } + } + } + + _TIFFfree(buf); + return (ret); +} + +/* + * Get a strip-organized image that has + * PlanarConfiguration contiguous if SamplesPerPixel > 1 + * or + * SamplesPerPixel == 1 + */ +static int +gtStripContig(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h) +{ + TIFF* tif = img->tif; + tileContigRoutine put = img->put.contig; + uint32 row, y, nrow, nrowsub, rowstoread; + tmsize_t pos; + unsigned char* buf; + uint32 rowsperstrip; + uint16 subsamplinghor,subsamplingver; + uint32 imagewidth = img->width; + tmsize_t scanline; + int32 fromskew, toskew; + int ret = 1, flip; + + TIFFGetFieldDefaulted(tif, TIFFTAG_YCBCRSUBSAMPLING, &subsamplinghor, &subsamplingver); + if( subsamplingver == 0 ) { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Invalid vertical YCbCr subsampling"); + return (0); + } + + buf = (unsigned char*) _TIFFmalloc(TIFFStripSize(tif)); + if (buf == 0) { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "No space for strip buffer"); + return (0); + } + _TIFFmemset(buf, 0, TIFFStripSize(tif)); + + flip = setorientation(img); + if (flip & FLIP_VERTICALLY) { + y = h - 1; + toskew = -(int32)(w + w); + } else { + y = 0; + toskew = -(int32)(w - w); + } + + TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); + + scanline = TIFFScanlineSize(tif); + fromskew = (w < imagewidth ? imagewidth - w : 0); + for (row = 0; row < h; row += nrow) + { + rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip; + nrow = (row + rowstoread > h ? h - row : rowstoread); + nrowsub = nrow; + if ((nrowsub%subsamplingver)!=0) + nrowsub+=subsamplingver-nrowsub%subsamplingver; + if (TIFFReadEncodedStrip(tif, + TIFFComputeStrip(tif,row+img->row_offset, 0), + buf, + ((row + img->row_offset)%rowsperstrip + nrowsub) * scanline)==(tmsize_t)(-1) + && img->stoponerr) + { + ret = 0; + break; + } + + pos = ((row + img->row_offset) % rowsperstrip) * scanline; + (*put)(img, raster+y*w, 0, y, w, nrow, fromskew, toskew, buf + pos); + y += (flip & FLIP_VERTICALLY ? -(int32) nrow : (int32) nrow); + } + + if (flip & FLIP_HORIZONTALLY) { + uint32 line; + + for (line = 0; line < h; line++) { + uint32 *left = raster + (line * w); + uint32 *right = left + w - 1; + + while ( left < right ) { + uint32 temp = *left; + *left = *right; + *right = temp; + left++, right--; + } + } + } + + _TIFFfree(buf); + return (ret); +} + +/* + * Get a strip-organized image with + * SamplesPerPixel > 1 + * PlanarConfiguration separated + * We assume that all such images are RGB. + */ +static int +gtStripSeparate(TIFFRGBAImage* img, uint32* raster, uint32 w, uint32 h) +{ + TIFF* tif = img->tif; + tileSeparateRoutine put = img->put.separate; + unsigned char *buf; + unsigned char *p0, *p1, *p2, *pa; + uint32 row, y, nrow, rowstoread; + tmsize_t pos; + tmsize_t scanline; + uint32 rowsperstrip, offset_row; + uint32 imagewidth = img->width; + tmsize_t stripsize; + tmsize_t bufsize; + int32 fromskew, toskew; + int alpha = img->alpha; + int ret = 1, flip, colorchannels; + + stripsize = TIFFStripSize(tif); + bufsize = TIFFSafeMultiply(tmsize_t,alpha?4:3,stripsize); + if (bufsize == 0) { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "Integer overflow in %s", "gtStripSeparate"); + return (0); + } + p0 = buf = (unsigned char *)_TIFFmalloc(bufsize); + if (buf == 0) { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "No space for tile buffer"); + return (0); + } + _TIFFmemset(buf, 0, bufsize); + p1 = p0 + stripsize; + p2 = p1 + stripsize; + pa = (alpha?(p2+stripsize):NULL); + + flip = setorientation(img); + if (flip & FLIP_VERTICALLY) { + y = h - 1; + toskew = -(int32)(w + w); + } + else { + y = 0; + toskew = -(int32)(w - w); + } + + switch( img->photometric ) + { + case PHOTOMETRIC_MINISWHITE: + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_PALETTE: + colorchannels = 1; + p2 = p1 = p0; + break; + + default: + colorchannels = 3; + break; + } + + TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); + scanline = TIFFScanlineSize(tif); + fromskew = (w < imagewidth ? imagewidth - w : 0); + for (row = 0; row < h; row += nrow) + { + rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip; + nrow = (row + rowstoread > h ? h - row : rowstoread); + offset_row = row + img->row_offset; + if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 0), + p0, ((row + img->row_offset)%rowsperstrip + nrow) * scanline)==(tmsize_t)(-1) + && img->stoponerr) + { + ret = 0; + break; + } + if (colorchannels > 1 + && TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 1), + p1, ((row + img->row_offset)%rowsperstrip + nrow) * scanline) == (tmsize_t)(-1) + && img->stoponerr) + { + ret = 0; + break; + } + if (colorchannels > 1 + && TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, 2), + p2, ((row + img->row_offset)%rowsperstrip + nrow) * scanline) == (tmsize_t)(-1) + && img->stoponerr) + { + ret = 0; + break; + } + if (alpha) + { + if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, offset_row, colorchannels), + pa, ((row + img->row_offset)%rowsperstrip + nrow) * scanline)==(tmsize_t)(-1) + && img->stoponerr) + { + ret = 0; + break; + } + } + + pos = ((row + img->row_offset) % rowsperstrip) * scanline; + (*put)(img, raster+y*w, 0, y, w, nrow, fromskew, toskew, p0 + pos, p1 + pos, + p2 + pos, (alpha?(pa+pos):NULL)); + y += (flip & FLIP_VERTICALLY ? -(int32) nrow : (int32) nrow); + } + + if (flip & FLIP_HORIZONTALLY) { + uint32 line; + + for (line = 0; line < h; line++) { + uint32 *left = raster + (line * w); + uint32 *right = left + w - 1; + + while ( left < right ) { + uint32 temp = *left; + *left = *right; + *right = temp; + left++, right--; + } + } + } + + _TIFFfree(buf); + return (ret); +} + +/* + * The following routines move decoded data returned + * from the TIFF library into rasters filled with packed + * ABGR pixels (i.e. suitable for passing to lrecwrite.) + * + * The routines have been created according to the most + * important cases and optimized. PickContigCase and + * PickSeparateCase analyze the parameters and select + * the appropriate "get" and "put" routine to use. + */ +#define REPEAT8(op) REPEAT4(op); REPEAT4(op) +#define REPEAT4(op) REPEAT2(op); REPEAT2(op) +#define REPEAT2(op) op; op +#define CASE8(x,op) \ + switch (x) { \ + case 7: op; case 6: op; case 5: op; \ + case 4: op; case 3: op; case 2: op; \ + case 1: op; \ + } +#define CASE4(x,op) switch (x) { case 3: op; case 2: op; case 1: op; } +#define NOP + +#define UNROLL8(w, op1, op2) { \ + uint32 _x; \ + for (_x = w; _x >= 8; _x -= 8) { \ + op1; \ + REPEAT8(op2); \ + } \ + if (_x > 0) { \ + op1; \ + CASE8(_x,op2); \ + } \ +} +#define UNROLL4(w, op1, op2) { \ + uint32 _x; \ + for (_x = w; _x >= 4; _x -= 4) { \ + op1; \ + REPEAT4(op2); \ + } \ + if (_x > 0) { \ + op1; \ + CASE4(_x,op2); \ + } \ +} +#define UNROLL2(w, op1, op2) { \ + uint32 _x; \ + for (_x = w; _x >= 2; _x -= 2) { \ + op1; \ + REPEAT2(op2); \ + } \ + if (_x) { \ + op1; \ + op2; \ + } \ +} + +#define SKEW(r,g,b,skew) { r += skew; g += skew; b += skew; } +#define SKEW4(r,g,b,a,skew) { r += skew; g += skew; b += skew; a+= skew; } + +#define A1 (((uint32)0xffL)<<24) +#define PACK(r,g,b) \ + ((uint32)(r)|((uint32)(g)<<8)|((uint32)(b)<<16)|A1) +#define PACK4(r,g,b,a) \ + ((uint32)(r)|((uint32)(g)<<8)|((uint32)(b)<<16)|((uint32)(a)<<24)) +#define W2B(v) (((v)>>8)&0xff) +/* TODO: PACKW should have be made redundant in favor of Bitdepth16To8 LUT */ +#define PACKW(r,g,b) \ + ((uint32)W2B(r)|((uint32)W2B(g)<<8)|((uint32)W2B(b)<<16)|A1) +#define PACKW4(r,g,b,a) \ + ((uint32)W2B(r)|((uint32)W2B(g)<<8)|((uint32)W2B(b)<<16)|((uint32)W2B(a)<<24)) + +#define DECLAREContigPutFunc(name) \ +static void name(\ + TIFFRGBAImage* img, \ + uint32* cp, \ + uint32 x, uint32 y, \ + uint32 w, uint32 h, \ + int32 fromskew, int32 toskew, \ + unsigned char* pp \ +) + +/* + * 8-bit palette => colormap/RGB + */ +DECLAREContigPutFunc(put8bitcmaptile) +{ + uint32** PALmap = img->PALmap; + int samplesperpixel = img->samplesperpixel; + + (void) y; + while (h-- > 0) { + for (x = w; x-- > 0;) + { + *cp++ = PALmap[*pp][0]; + pp += samplesperpixel; + } + cp += toskew; + pp += fromskew; + } +} + +/* + * 4-bit palette => colormap/RGB + */ +DECLAREContigPutFunc(put4bitcmaptile) +{ + uint32** PALmap = img->PALmap; + + (void) x; (void) y; + fromskew /= 2; + while (h-- > 0) { + uint32* bw; + UNROLL2(w, bw = PALmap[*pp++], *cp++ = *bw++); + cp += toskew; + pp += fromskew; + } +} + +/* + * 2-bit palette => colormap/RGB + */ +DECLAREContigPutFunc(put2bitcmaptile) +{ + uint32** PALmap = img->PALmap; + + (void) x; (void) y; + fromskew /= 4; + while (h-- > 0) { + uint32* bw; + UNROLL4(w, bw = PALmap[*pp++], *cp++ = *bw++); + cp += toskew; + pp += fromskew; + } +} + +/* + * 1-bit palette => colormap/RGB + */ +DECLAREContigPutFunc(put1bitcmaptile) +{ + uint32** PALmap = img->PALmap; + + (void) x; (void) y; + fromskew /= 8; + while (h-- > 0) { + uint32* bw; + UNROLL8(w, bw = PALmap[*pp++], *cp++ = *bw++); + cp += toskew; + pp += fromskew; + } +} + +/* + * 8-bit greyscale => colormap/RGB + */ +DECLAREContigPutFunc(putgreytile) +{ + int samplesperpixel = img->samplesperpixel; + uint32** BWmap = img->BWmap; + + (void) y; + while (h-- > 0) { + for (x = w; x-- > 0;) + { + *cp++ = BWmap[*pp][0]; + pp += samplesperpixel; + } + cp += toskew; + pp += fromskew; + } +} + +/* + * 8-bit greyscale with associated alpha => colormap/RGBA + */ +DECLAREContigPutFunc(putagreytile) +{ + int samplesperpixel = img->samplesperpixel; + uint32** BWmap = img->BWmap; + + (void) y; + while (h-- > 0) { + for (x = w; x-- > 0;) + { + *cp++ = BWmap[*pp][0] & (*(pp+1) << 24 | ~A1); + pp += samplesperpixel; + } + cp += toskew; + pp += fromskew; + } +} + +/* + * 16-bit greyscale => colormap/RGB + */ +DECLAREContigPutFunc(put16bitbwtile) +{ + int samplesperpixel = img->samplesperpixel; + uint32** BWmap = img->BWmap; + + (void) y; + while (h-- > 0) { + uint16 *wp = (uint16 *) pp; + + for (x = w; x-- > 0;) + { + /* use high order byte of 16bit value */ + + *cp++ = BWmap[*wp >> 8][0]; + pp += 2 * samplesperpixel; + wp += samplesperpixel; + } + cp += toskew; + pp += fromskew; + } +} + +/* + * 1-bit bilevel => colormap/RGB + */ +DECLAREContigPutFunc(put1bitbwtile) +{ + uint32** BWmap = img->BWmap; + + (void) x; (void) y; + fromskew /= 8; + while (h-- > 0) { + uint32* bw; + UNROLL8(w, bw = BWmap[*pp++], *cp++ = *bw++); + cp += toskew; + pp += fromskew; + } +} + +/* + * 2-bit greyscale => colormap/RGB + */ +DECLAREContigPutFunc(put2bitbwtile) +{ + uint32** BWmap = img->BWmap; + + (void) x; (void) y; + fromskew /= 4; + while (h-- > 0) { + uint32* bw; + UNROLL4(w, bw = BWmap[*pp++], *cp++ = *bw++); + cp += toskew; + pp += fromskew; + } +} + +/* + * 4-bit greyscale => colormap/RGB + */ +DECLAREContigPutFunc(put4bitbwtile) +{ + uint32** BWmap = img->BWmap; + + (void) x; (void) y; + fromskew /= 2; + while (h-- > 0) { + uint32* bw; + UNROLL2(w, bw = BWmap[*pp++], *cp++ = *bw++); + cp += toskew; + pp += fromskew; + } +} + +/* + * 8-bit packed samples, no Map => RGB + */ +DECLAREContigPutFunc(putRGBcontig8bittile) +{ + int samplesperpixel = img->samplesperpixel; + + (void) x; (void) y; + fromskew *= samplesperpixel; + while (h-- > 0) { + UNROLL8(w, NOP, + *cp++ = PACK(pp[0], pp[1], pp[2]); + pp += samplesperpixel); + cp += toskew; + pp += fromskew; + } +} + +/* + * 8-bit packed samples => RGBA w/ associated alpha + * (known to have Map == NULL) + */ +DECLAREContigPutFunc(putRGBAAcontig8bittile) +{ + int samplesperpixel = img->samplesperpixel; + + (void) x; (void) y; + fromskew *= samplesperpixel; + while (h-- > 0) { + UNROLL8(w, NOP, + *cp++ = PACK4(pp[0], pp[1], pp[2], pp[3]); + pp += samplesperpixel); + cp += toskew; + pp += fromskew; + } +} + +/* + * 8-bit packed samples => RGBA w/ unassociated alpha + * (known to have Map == NULL) + */ +DECLAREContigPutFunc(putRGBUAcontig8bittile) +{ + int samplesperpixel = img->samplesperpixel; + (void) y; + fromskew *= samplesperpixel; + while (h-- > 0) { + uint32 r, g, b, a; + uint8* m; + for (x = w; x-- > 0;) { + a = pp[3]; + m = img->UaToAa+(a<<8); + r = m[pp[0]]; + g = m[pp[1]]; + b = m[pp[2]]; + *cp++ = PACK4(r,g,b,a); + pp += samplesperpixel; + } + cp += toskew; + pp += fromskew; + } +} + +/* + * 16-bit packed samples => RGB + */ +DECLAREContigPutFunc(putRGBcontig16bittile) +{ + int samplesperpixel = img->samplesperpixel; + uint16 *wp = (uint16 *)pp; + (void) y; + fromskew *= samplesperpixel; + while (h-- > 0) { + for (x = w; x-- > 0;) { + *cp++ = PACK(img->Bitdepth16To8[wp[0]], + img->Bitdepth16To8[wp[1]], + img->Bitdepth16To8[wp[2]]); + wp += samplesperpixel; + } + cp += toskew; + wp += fromskew; + } +} + +/* + * 16-bit packed samples => RGBA w/ associated alpha + * (known to have Map == NULL) + */ +DECLAREContigPutFunc(putRGBAAcontig16bittile) +{ + int samplesperpixel = img->samplesperpixel; + uint16 *wp = (uint16 *)pp; + (void) y; + fromskew *= samplesperpixel; + while (h-- > 0) { + for (x = w; x-- > 0;) { + *cp++ = PACK4(img->Bitdepth16To8[wp[0]], + img->Bitdepth16To8[wp[1]], + img->Bitdepth16To8[wp[2]], + img->Bitdepth16To8[wp[3]]); + wp += samplesperpixel; + } + cp += toskew; + wp += fromskew; + } +} + +/* + * 16-bit packed samples => RGBA w/ unassociated alpha + * (known to have Map == NULL) + */ +DECLAREContigPutFunc(putRGBUAcontig16bittile) +{ + int samplesperpixel = img->samplesperpixel; + uint16 *wp = (uint16 *)pp; + (void) y; + fromskew *= samplesperpixel; + while (h-- > 0) { + uint32 r,g,b,a; + uint8* m; + for (x = w; x-- > 0;) { + a = img->Bitdepth16To8[wp[3]]; + m = img->UaToAa+(a<<8); + r = m[img->Bitdepth16To8[wp[0]]]; + g = m[img->Bitdepth16To8[wp[1]]]; + b = m[img->Bitdepth16To8[wp[2]]]; + *cp++ = PACK4(r,g,b,a); + wp += samplesperpixel; + } + cp += toskew; + wp += fromskew; + } +} + +/* + * 8-bit packed CMYK samples w/o Map => RGB + * + * NB: The conversion of CMYK->RGB is *very* crude. + */ +DECLAREContigPutFunc(putRGBcontig8bitCMYKtile) +{ + int samplesperpixel = img->samplesperpixel; + uint16 r, g, b, k; + + (void) x; (void) y; + fromskew *= samplesperpixel; + while (h-- > 0) { + UNROLL8(w, NOP, + k = 255 - pp[3]; + r = (k*(255-pp[0]))/255; + g = (k*(255-pp[1]))/255; + b = (k*(255-pp[2]))/255; + *cp++ = PACK(r, g, b); + pp += samplesperpixel); + cp += toskew; + pp += fromskew; + } +} + +/* + * 8-bit packed CMYK samples w/Map => RGB + * + * NB: The conversion of CMYK->RGB is *very* crude. + */ +DECLAREContigPutFunc(putRGBcontig8bitCMYKMaptile) +{ + int samplesperpixel = img->samplesperpixel; + TIFFRGBValue* Map = img->Map; + uint16 r, g, b, k; + + (void) y; + fromskew *= samplesperpixel; + while (h-- > 0) { + for (x = w; x-- > 0;) { + k = 255 - pp[3]; + r = (k*(255-pp[0]))/255; + g = (k*(255-pp[1]))/255; + b = (k*(255-pp[2]))/255; + *cp++ = PACK(Map[r], Map[g], Map[b]); + pp += samplesperpixel; + } + pp += fromskew; + cp += toskew; + } +} + +#define DECLARESepPutFunc(name) \ +static void name(\ + TIFFRGBAImage* img,\ + uint32* cp,\ + uint32 x, uint32 y, \ + uint32 w, uint32 h,\ + int32 fromskew, int32 toskew,\ + unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a\ +) + +/* + * 8-bit unpacked samples => RGB + */ +DECLARESepPutFunc(putRGBseparate8bittile) +{ + (void) img; (void) x; (void) y; (void) a; + while (h-- > 0) { + UNROLL8(w, NOP, *cp++ = PACK(*r++, *g++, *b++)); + SKEW(r, g, b, fromskew); + cp += toskew; + } +} + +/* + * 8-bit unpacked samples => RGBA w/ associated alpha + */ +DECLARESepPutFunc(putRGBAAseparate8bittile) +{ + (void) img; (void) x; (void) y; + while (h-- > 0) { + UNROLL8(w, NOP, *cp++ = PACK4(*r++, *g++, *b++, *a++)); + SKEW4(r, g, b, a, fromskew); + cp += toskew; + } +} + +/* + * 8-bit unpacked CMYK samples => RGBA + */ +DECLARESepPutFunc(putCMYKseparate8bittile) +{ + (void) img; (void) y; + while (h-- > 0) { + uint32 rv, gv, bv, kv; + for (x = w; x-- > 0;) { + kv = 255 - *a++; + rv = (kv*(255-*r++))/255; + gv = (kv*(255-*g++))/255; + bv = (kv*(255-*b++))/255; + *cp++ = PACK4(rv,gv,bv,255); + } + SKEW4(r, g, b, a, fromskew); + cp += toskew; + } +} + +/* + * 8-bit unpacked samples => RGBA w/ unassociated alpha + */ +DECLARESepPutFunc(putRGBUAseparate8bittile) +{ + (void) img; (void) y; + while (h-- > 0) { + uint32 rv, gv, bv, av; + uint8* m; + for (x = w; x-- > 0;) { + av = *a++; + m = img->UaToAa+(av<<8); + rv = m[*r++]; + gv = m[*g++]; + bv = m[*b++]; + *cp++ = PACK4(rv,gv,bv,av); + } + SKEW4(r, g, b, a, fromskew); + cp += toskew; + } +} + +/* + * 16-bit unpacked samples => RGB + */ +DECLARESepPutFunc(putRGBseparate16bittile) +{ + uint16 *wr = (uint16*) r; + uint16 *wg = (uint16*) g; + uint16 *wb = (uint16*) b; + (void) img; (void) y; (void) a; + while (h-- > 0) { + for (x = 0; x < w; x++) + *cp++ = PACK(img->Bitdepth16To8[*wr++], + img->Bitdepth16To8[*wg++], + img->Bitdepth16To8[*wb++]); + SKEW(wr, wg, wb, fromskew); + cp += toskew; + } +} + +/* + * 16-bit unpacked samples => RGBA w/ associated alpha + */ +DECLARESepPutFunc(putRGBAAseparate16bittile) +{ + uint16 *wr = (uint16*) r; + uint16 *wg = (uint16*) g; + uint16 *wb = (uint16*) b; + uint16 *wa = (uint16*) a; + (void) img; (void) y; + while (h-- > 0) { + for (x = 0; x < w; x++) + *cp++ = PACK4(img->Bitdepth16To8[*wr++], + img->Bitdepth16To8[*wg++], + img->Bitdepth16To8[*wb++], + img->Bitdepth16To8[*wa++]); + SKEW4(wr, wg, wb, wa, fromskew); + cp += toskew; + } +} + +/* + * 16-bit unpacked samples => RGBA w/ unassociated alpha + */ +DECLARESepPutFunc(putRGBUAseparate16bittile) +{ + uint16 *wr = (uint16*) r; + uint16 *wg = (uint16*) g; + uint16 *wb = (uint16*) b; + uint16 *wa = (uint16*) a; + (void) img; (void) y; + while (h-- > 0) { + uint32 r,g,b,a; + uint8* m; + for (x = w; x-- > 0;) { + a = img->Bitdepth16To8[*wa++]; + m = img->UaToAa+(a<<8); + r = m[img->Bitdepth16To8[*wr++]]; + g = m[img->Bitdepth16To8[*wg++]]; + b = m[img->Bitdepth16To8[*wb++]]; + *cp++ = PACK4(r,g,b,a); + } + SKEW4(wr, wg, wb, wa, fromskew); + cp += toskew; + } +} + +/* + * 8-bit packed CIE L*a*b 1976 samples => RGB + */ +DECLAREContigPutFunc(putcontig8bitCIELab) +{ + float X, Y, Z; + uint32 r, g, b; + (void) y; + fromskew *= 3; + while (h-- > 0) { + for (x = w; x-- > 0;) { + TIFFCIELabToXYZ(img->cielab, + (unsigned char)pp[0], + (signed char)pp[1], + (signed char)pp[2], + &X, &Y, &Z); + TIFFXYZToRGB(img->cielab, X, Y, Z, &r, &g, &b); + *cp++ = PACK(r, g, b); + pp += 3; + } + cp += toskew; + pp += fromskew; + } +} + +/* + * YCbCr -> RGB conversion and packing routines. + */ + +#define YCbCrtoRGB(dst, Y) { \ + uint32 r, g, b; \ + TIFFYCbCrtoRGB(img->ycbcr, (Y), Cb, Cr, &r, &g, &b); \ + dst = PACK(r, g, b); \ +} + +/* + * 8-bit packed YCbCr samples => RGB + * This function is generic for different sampling sizes, + * and can handle blocks sizes that aren't multiples of the + * sampling size. However, it is substantially less optimized + * than the specific sampling cases. It is used as a fallback + * for difficult blocks. + */ +#ifdef notdef +static void putcontig8bitYCbCrGenericTile( + TIFFRGBAImage* img, + uint32* cp, + uint32 x, uint32 y, + uint32 w, uint32 h, + int32 fromskew, int32 toskew, + unsigned char* pp, + int h_group, + int v_group ) + +{ + uint32* cp1 = cp+w+toskew; + uint32* cp2 = cp1+w+toskew; + uint32* cp3 = cp2+w+toskew; + int32 incr = 3*w+4*toskew; + int32 Cb, Cr; + int group_size = v_group * h_group + 2; + + (void) y; + fromskew = (fromskew * group_size) / h_group; + + for( yy = 0; yy < h; yy++ ) + { + unsigned char *pp_line; + int y_line_group = yy / v_group; + int y_remainder = yy - y_line_group * v_group; + + pp_line = pp + v_line_group * + + + for( xx = 0; xx < w; xx++ ) + { + Cb = pp + } + } + for (; h >= 4; h -= 4) { + x = w>>2; + do { + Cb = pp[16]; + Cr = pp[17]; + + YCbCrtoRGB(cp [0], pp[ 0]); + YCbCrtoRGB(cp [1], pp[ 1]); + YCbCrtoRGB(cp [2], pp[ 2]); + YCbCrtoRGB(cp [3], pp[ 3]); + YCbCrtoRGB(cp1[0], pp[ 4]); + YCbCrtoRGB(cp1[1], pp[ 5]); + YCbCrtoRGB(cp1[2], pp[ 6]); + YCbCrtoRGB(cp1[3], pp[ 7]); + YCbCrtoRGB(cp2[0], pp[ 8]); + YCbCrtoRGB(cp2[1], pp[ 9]); + YCbCrtoRGB(cp2[2], pp[10]); + YCbCrtoRGB(cp2[3], pp[11]); + YCbCrtoRGB(cp3[0], pp[12]); + YCbCrtoRGB(cp3[1], pp[13]); + YCbCrtoRGB(cp3[2], pp[14]); + YCbCrtoRGB(cp3[3], pp[15]); + + cp += 4, cp1 += 4, cp2 += 4, cp3 += 4; + pp += 18; + } while (--x); + cp += incr, cp1 += incr, cp2 += incr, cp3 += incr; + pp += fromskew; + } +} +#endif + +/* + * 8-bit packed YCbCr samples w/ 4,4 subsampling => RGB + */ +DECLAREContigPutFunc(putcontig8bitYCbCr44tile) +{ + uint32* cp1 = cp+w+toskew; + uint32* cp2 = cp1+w+toskew; + uint32* cp3 = cp2+w+toskew; + int32 incr = 3*w+4*toskew; + + (void) y; + /* adjust fromskew */ + fromskew = (fromskew * 18) / 4; + if ((h & 3) == 0 && (w & 3) == 0) { + for (; h >= 4; h -= 4) { + x = w>>2; + do { + int32 Cb = pp[16]; + int32 Cr = pp[17]; + + YCbCrtoRGB(cp [0], pp[ 0]); + YCbCrtoRGB(cp [1], pp[ 1]); + YCbCrtoRGB(cp [2], pp[ 2]); + YCbCrtoRGB(cp [3], pp[ 3]); + YCbCrtoRGB(cp1[0], pp[ 4]); + YCbCrtoRGB(cp1[1], pp[ 5]); + YCbCrtoRGB(cp1[2], pp[ 6]); + YCbCrtoRGB(cp1[3], pp[ 7]); + YCbCrtoRGB(cp2[0], pp[ 8]); + YCbCrtoRGB(cp2[1], pp[ 9]); + YCbCrtoRGB(cp2[2], pp[10]); + YCbCrtoRGB(cp2[3], pp[11]); + YCbCrtoRGB(cp3[0], pp[12]); + YCbCrtoRGB(cp3[1], pp[13]); + YCbCrtoRGB(cp3[2], pp[14]); + YCbCrtoRGB(cp3[3], pp[15]); + + cp += 4, cp1 += 4, cp2 += 4, cp3 += 4; + pp += 18; + } while (--x); + cp += incr, cp1 += incr, cp2 += incr, cp3 += incr; + pp += fromskew; + } + } else { + while (h > 0) { + for (x = w; x > 0;) { + int32 Cb = pp[16]; + int32 Cr = pp[17]; + switch (x) { + default: + switch (h) { + default: YCbCrtoRGB(cp3[3], pp[15]); /* FALLTHROUGH */ + case 3: YCbCrtoRGB(cp2[3], pp[11]); /* FALLTHROUGH */ + case 2: YCbCrtoRGB(cp1[3], pp[ 7]); /* FALLTHROUGH */ + case 1: YCbCrtoRGB(cp [3], pp[ 3]); /* FALLTHROUGH */ + } /* FALLTHROUGH */ + case 3: + switch (h) { + default: YCbCrtoRGB(cp3[2], pp[14]); /* FALLTHROUGH */ + case 3: YCbCrtoRGB(cp2[2], pp[10]); /* FALLTHROUGH */ + case 2: YCbCrtoRGB(cp1[2], pp[ 6]); /* FALLTHROUGH */ + case 1: YCbCrtoRGB(cp [2], pp[ 2]); /* FALLTHROUGH */ + } /* FALLTHROUGH */ + case 2: + switch (h) { + default: YCbCrtoRGB(cp3[1], pp[13]); /* FALLTHROUGH */ + case 3: YCbCrtoRGB(cp2[1], pp[ 9]); /* FALLTHROUGH */ + case 2: YCbCrtoRGB(cp1[1], pp[ 5]); /* FALLTHROUGH */ + case 1: YCbCrtoRGB(cp [1], pp[ 1]); /* FALLTHROUGH */ + } /* FALLTHROUGH */ + case 1: + switch (h) { + default: YCbCrtoRGB(cp3[0], pp[12]); /* FALLTHROUGH */ + case 3: YCbCrtoRGB(cp2[0], pp[ 8]); /* FALLTHROUGH */ + case 2: YCbCrtoRGB(cp1[0], pp[ 4]); /* FALLTHROUGH */ + case 1: YCbCrtoRGB(cp [0], pp[ 0]); /* FALLTHROUGH */ + } /* FALLTHROUGH */ + } + if (x < 4) { + cp += x; cp1 += x; cp2 += x; cp3 += x; + x = 0; + } + else { + cp += 4; cp1 += 4; cp2 += 4; cp3 += 4; + x -= 4; + } + pp += 18; + } + if (h <= 4) + break; + h -= 4; + cp += incr, cp1 += incr, cp2 += incr, cp3 += incr; + pp += fromskew; + } + } +} + +/* + * 8-bit packed YCbCr samples w/ 4,2 subsampling => RGB + */ +DECLAREContigPutFunc(putcontig8bitYCbCr42tile) +{ + uint32* cp1 = cp+w+toskew; + int32 incr = 2*toskew+w; + + (void) y; + fromskew = (fromskew * 10) / 4; + if ((w & 3) == 0 && (h & 1) == 0) { + for (; h >= 2; h -= 2) { + x = w>>2; + do { + int32 Cb = pp[8]; + int32 Cr = pp[9]; + + YCbCrtoRGB(cp [0], pp[0]); + YCbCrtoRGB(cp [1], pp[1]); + YCbCrtoRGB(cp [2], pp[2]); + YCbCrtoRGB(cp [3], pp[3]); + YCbCrtoRGB(cp1[0], pp[4]); + YCbCrtoRGB(cp1[1], pp[5]); + YCbCrtoRGB(cp1[2], pp[6]); + YCbCrtoRGB(cp1[3], pp[7]); + + cp += 4, cp1 += 4; + pp += 10; + } while (--x); + cp += incr, cp1 += incr; + pp += fromskew; + } + } else { + while (h > 0) { + for (x = w; x > 0;) { + int32 Cb = pp[8]; + int32 Cr = pp[9]; + switch (x) { + default: + switch (h) { + default: YCbCrtoRGB(cp1[3], pp[ 7]); /* FALLTHROUGH */ + case 1: YCbCrtoRGB(cp [3], pp[ 3]); /* FALLTHROUGH */ + } /* FALLTHROUGH */ + case 3: + switch (h) { + default: YCbCrtoRGB(cp1[2], pp[ 6]); /* FALLTHROUGH */ + case 1: YCbCrtoRGB(cp [2], pp[ 2]); /* FALLTHROUGH */ + } /* FALLTHROUGH */ + case 2: + switch (h) { + default: YCbCrtoRGB(cp1[1], pp[ 5]); /* FALLTHROUGH */ + case 1: YCbCrtoRGB(cp [1], pp[ 1]); /* FALLTHROUGH */ + } /* FALLTHROUGH */ + case 1: + switch (h) { + default: YCbCrtoRGB(cp1[0], pp[ 4]); /* FALLTHROUGH */ + case 1: YCbCrtoRGB(cp [0], pp[ 0]); /* FALLTHROUGH */ + } /* FALLTHROUGH */ + } + if (x < 4) { + cp += x; cp1 += x; + x = 0; + } + else { + cp += 4; cp1 += 4; + x -= 4; + } + pp += 10; + } + if (h <= 2) + break; + h -= 2; + cp += incr, cp1 += incr; + pp += fromskew; + } + } +} + +/* + * 8-bit packed YCbCr samples w/ 4,1 subsampling => RGB + */ +DECLAREContigPutFunc(putcontig8bitYCbCr41tile) +{ + (void) y; + /* XXX adjust fromskew */ + do { + x = w>>2; + while(x>0) { + int32 Cb = pp[4]; + int32 Cr = pp[5]; + + YCbCrtoRGB(cp [0], pp[0]); + YCbCrtoRGB(cp [1], pp[1]); + YCbCrtoRGB(cp [2], pp[2]); + YCbCrtoRGB(cp [3], pp[3]); + + cp += 4; + pp += 6; + x--; + } + + if( (w&3) != 0 ) + { + int32 Cb = pp[4]; + int32 Cr = pp[5]; + + switch( (w&3) ) { + case 3: YCbCrtoRGB(cp [2], pp[2]); + case 2: YCbCrtoRGB(cp [1], pp[1]); + case 1: YCbCrtoRGB(cp [0], pp[0]); + case 0: break; + } + + cp += (w&3); + pp += 6; + } + + cp += toskew; + pp += fromskew; + } while (--h); + +} + +/* + * 8-bit packed YCbCr samples w/ 2,2 subsampling => RGB + */ +DECLAREContigPutFunc(putcontig8bitYCbCr22tile) +{ + uint32* cp2; + int32 incr = 2*toskew+w; + (void) y; + fromskew = (fromskew / 2) * 6; + cp2 = cp+w+toskew; + while (h>=2) { + x = w; + while (x>=2) { + uint32 Cb = pp[4]; + uint32 Cr = pp[5]; + YCbCrtoRGB(cp[0], pp[0]); + YCbCrtoRGB(cp[1], pp[1]); + YCbCrtoRGB(cp2[0], pp[2]); + YCbCrtoRGB(cp2[1], pp[3]); + cp += 2; + cp2 += 2; + pp += 6; + x -= 2; + } + if (x==1) { + uint32 Cb = pp[4]; + uint32 Cr = pp[5]; + YCbCrtoRGB(cp[0], pp[0]); + YCbCrtoRGB(cp2[0], pp[2]); + cp ++ ; + cp2 ++ ; + pp += 6; + } + cp += incr; + cp2 += incr; + pp += fromskew; + h-=2; + } + if (h==1) { + x = w; + while (x>=2) { + uint32 Cb = pp[4]; + uint32 Cr = pp[5]; + YCbCrtoRGB(cp[0], pp[0]); + YCbCrtoRGB(cp[1], pp[1]); + cp += 2; + cp2 += 2; + pp += 6; + x -= 2; + } + if (x==1) { + uint32 Cb = pp[4]; + uint32 Cr = pp[5]; + YCbCrtoRGB(cp[0], pp[0]); + } + } +} + +/* + * 8-bit packed YCbCr samples w/ 2,1 subsampling => RGB + */ +DECLAREContigPutFunc(putcontig8bitYCbCr21tile) +{ + (void) y; + fromskew = (fromskew * 4) / 2; + do { + x = w>>1; + while(x>0) { + int32 Cb = pp[2]; + int32 Cr = pp[3]; + + YCbCrtoRGB(cp[0], pp[0]); + YCbCrtoRGB(cp[1], pp[1]); + + cp += 2; + pp += 4; + x --; + } + + if( (w&1) != 0 ) + { + int32 Cb = pp[2]; + int32 Cr = pp[3]; + + YCbCrtoRGB(cp[0], pp[0]); + + cp += 1; + pp += 4; + } + + cp += toskew; + pp += fromskew; + } while (--h); +} + +/* + * 8-bit packed YCbCr samples w/ 1,2 subsampling => RGB + */ +DECLAREContigPutFunc(putcontig8bitYCbCr12tile) +{ + uint32* cp2; + int32 incr = 2*toskew+w; + (void) y; + fromskew = (fromskew / 2) * 4; + cp2 = cp+w+toskew; + while (h>=2) { + x = w; + do { + uint32 Cb = pp[2]; + uint32 Cr = pp[3]; + YCbCrtoRGB(cp[0], pp[0]); + YCbCrtoRGB(cp2[0], pp[1]); + cp ++; + cp2 ++; + pp += 4; + } while (--x); + cp += incr; + cp2 += incr; + pp += fromskew; + h-=2; + } + if (h==1) { + x = w; + do { + uint32 Cb = pp[2]; + uint32 Cr = pp[3]; + YCbCrtoRGB(cp[0], pp[0]); + cp ++; + pp += 4; + } while (--x); + } +} + +/* + * 8-bit packed YCbCr samples w/ no subsampling => RGB + */ +DECLAREContigPutFunc(putcontig8bitYCbCr11tile) +{ + (void) y; + fromskew *= 3; + do { + x = w; /* was x = w>>1; patched 2000/09/25 warmerda@home.com */ + do { + int32 Cb = pp[1]; + int32 Cr = pp[2]; + + YCbCrtoRGB(*cp++, pp[0]); + + pp += 3; + } while (--x); + cp += toskew; + pp += fromskew; + } while (--h); +} + +/* + * 8-bit packed YCbCr samples w/ no subsampling => RGB + */ +DECLARESepPutFunc(putseparate8bitYCbCr11tile) +{ + (void) y; + (void) a; + /* TODO: naming of input vars is still off, change obfuscating declaration inside define, or resolve obfuscation */ + while (h-- > 0) { + x = w; + do { + uint32 dr, dg, db; + TIFFYCbCrtoRGB(img->ycbcr,*r++,*g++,*b++,&dr,&dg,&db); + *cp++ = PACK(dr,dg,db); + } while (--x); + SKEW(r, g, b, fromskew); + cp += toskew; + } +} +#undef YCbCrtoRGB + +static int +initYCbCrConversion(TIFFRGBAImage* img) +{ + static const char module[] = "initYCbCrConversion"; + + float *luma, *refBlackWhite; + + if (img->ycbcr == NULL) { + img->ycbcr = (TIFFYCbCrToRGB*) _TIFFmalloc( + TIFFroundup_32(sizeof (TIFFYCbCrToRGB), sizeof (long)) + + 4*256*sizeof (TIFFRGBValue) + + 2*256*sizeof (int) + + 3*256*sizeof (int32) + ); + if (img->ycbcr == NULL) { + TIFFErrorExt(img->tif->tif_clientdata, module, + "No space for YCbCr->RGB conversion state"); + return (0); + } + } + + TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRCOEFFICIENTS, &luma); + TIFFGetFieldDefaulted(img->tif, TIFFTAG_REFERENCEBLACKWHITE, + &refBlackWhite); + if (TIFFYCbCrToRGBInit(img->ycbcr, luma, refBlackWhite) < 0) + return(0); + return (1); +} + +static tileContigRoutine +initCIELabConversion(TIFFRGBAImage* img) +{ + static const char module[] = "initCIELabConversion"; + + float *whitePoint; + float refWhite[3]; + + if (!img->cielab) { + img->cielab = (TIFFCIELabToRGB *) + _TIFFmalloc(sizeof(TIFFCIELabToRGB)); + if (!img->cielab) { + TIFFErrorExt(img->tif->tif_clientdata, module, + "No space for CIE L*a*b*->RGB conversion state."); + return NULL; + } + } + + TIFFGetFieldDefaulted(img->tif, TIFFTAG_WHITEPOINT, &whitePoint); + refWhite[1] = 100.0F; + refWhite[0] = whitePoint[0] / whitePoint[1] * refWhite[1]; + refWhite[2] = (1.0F - whitePoint[0] - whitePoint[1]) + / whitePoint[1] * refWhite[1]; + if (TIFFCIELabToRGBInit(img->cielab, &display_sRGB, refWhite) < 0) { + TIFFErrorExt(img->tif->tif_clientdata, module, + "Failed to initialize CIE L*a*b*->RGB conversion state."); + _TIFFfree(img->cielab); + return NULL; + } + + return putcontig8bitCIELab; +} + +/* + * Greyscale images with less than 8 bits/sample are handled + * with a table to avoid lots of shifts and masks. The table + * is setup so that put*bwtile (below) can retrieve 8/bitspersample + * pixel values simply by indexing into the table with one + * number. + */ +static int +makebwmap(TIFFRGBAImage* img) +{ + TIFFRGBValue* Map = img->Map; + int bitspersample = img->bitspersample; + int nsamples = 8 / bitspersample; + int i; + uint32* p; + + if( nsamples == 0 ) + nsamples = 1; + + img->BWmap = (uint32**) _TIFFmalloc( + 256*sizeof (uint32 *)+(256*nsamples*sizeof(uint32))); + if (img->BWmap == NULL) { + TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif), "No space for B&W mapping table"); + return (0); + } + p = (uint32*)(img->BWmap + 256); + for (i = 0; i < 256; i++) { + TIFFRGBValue c; + img->BWmap[i] = p; + switch (bitspersample) { +#define GREY(x) c = Map[x]; *p++ = PACK(c,c,c); + case 1: + GREY(i>>7); + GREY((i>>6)&1); + GREY((i>>5)&1); + GREY((i>>4)&1); + GREY((i>>3)&1); + GREY((i>>2)&1); + GREY((i>>1)&1); + GREY(i&1); + break; + case 2: + GREY(i>>6); + GREY((i>>4)&3); + GREY((i>>2)&3); + GREY(i&3); + break; + case 4: + GREY(i>>4); + GREY(i&0xf); + break; + case 8: + case 16: + GREY(i); + break; + } +#undef GREY + } + return (1); +} + +/* + * Construct a mapping table to convert from the range + * of the data samples to [0,255] --for display. This + * process also handles inverting B&W images when needed. + */ +static int +setupMap(TIFFRGBAImage* img) +{ + int32 x, range; + + range = (int32)((1L<bitspersample)-1); + + /* treat 16 bit the same as eight bit */ + if( img->bitspersample == 16 ) + range = (int32) 255; + + img->Map = (TIFFRGBValue*) _TIFFmalloc((range+1) * sizeof (TIFFRGBValue)); + if (img->Map == NULL) { + TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif), + "No space for photometric conversion table"); + return (0); + } + if (img->photometric == PHOTOMETRIC_MINISWHITE) { + for (x = 0; x <= range; x++) + img->Map[x] = (TIFFRGBValue) (((range - x) * 255) / range); + } else { + for (x = 0; x <= range; x++) + img->Map[x] = (TIFFRGBValue) ((x * 255) / range); + } + if (img->bitspersample <= 16 && + (img->photometric == PHOTOMETRIC_MINISBLACK || + img->photometric == PHOTOMETRIC_MINISWHITE)) { + /* + * Use photometric mapping table to construct + * unpacking tables for samples <= 8 bits. + */ + if (!makebwmap(img)) + return (0); + /* no longer need Map, free it */ + _TIFFfree(img->Map), img->Map = NULL; + } + return (1); +} + +static int +checkcmap(TIFFRGBAImage* img) +{ + uint16* r = img->redcmap; + uint16* g = img->greencmap; + uint16* b = img->bluecmap; + long n = 1L<bitspersample; + + while (n-- > 0) + if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256) + return (16); + return (8); +} + +static void +cvtcmap(TIFFRGBAImage* img) +{ + uint16* r = img->redcmap; + uint16* g = img->greencmap; + uint16* b = img->bluecmap; + long i; + + for (i = (1L<bitspersample)-1; i >= 0; i--) { +#define CVT(x) ((uint16)((x)>>8)) + r[i] = CVT(r[i]); + g[i] = CVT(g[i]); + b[i] = CVT(b[i]); +#undef CVT + } +} + +/* + * Palette images with <= 8 bits/sample are handled + * with a table to avoid lots of shifts and masks. The table + * is setup so that put*cmaptile (below) can retrieve 8/bitspersample + * pixel values simply by indexing into the table with one + * number. + */ +static int +makecmap(TIFFRGBAImage* img) +{ + int bitspersample = img->bitspersample; + int nsamples = 8 / bitspersample; + uint16* r = img->redcmap; + uint16* g = img->greencmap; + uint16* b = img->bluecmap; + uint32 *p; + int i; + + img->PALmap = (uint32**) _TIFFmalloc( + 256*sizeof (uint32 *)+(256*nsamples*sizeof(uint32))); + if (img->PALmap == NULL) { + TIFFErrorExt(img->tif->tif_clientdata, TIFFFileName(img->tif), "No space for Palette mapping table"); + return (0); + } + p = (uint32*)(img->PALmap + 256); + for (i = 0; i < 256; i++) { + TIFFRGBValue c; + img->PALmap[i] = p; +#define CMAP(x) c = (TIFFRGBValue) x; *p++ = PACK(r[c]&0xff, g[c]&0xff, b[c]&0xff); + switch (bitspersample) { + case 1: + CMAP(i>>7); + CMAP((i>>6)&1); + CMAP((i>>5)&1); + CMAP((i>>4)&1); + CMAP((i>>3)&1); + CMAP((i>>2)&1); + CMAP((i>>1)&1); + CMAP(i&1); + break; + case 2: + CMAP(i>>6); + CMAP((i>>4)&3); + CMAP((i>>2)&3); + CMAP(i&3); + break; + case 4: + CMAP(i>>4); + CMAP(i&0xf); + break; + case 8: + CMAP(i); + break; + } +#undef CMAP + } + return (1); +} + +/* + * Construct any mapping table used + * by the associated put routine. + */ +static int +buildMap(TIFFRGBAImage* img) +{ + switch (img->photometric) { + case PHOTOMETRIC_RGB: + case PHOTOMETRIC_YCBCR: + case PHOTOMETRIC_SEPARATED: + if (img->bitspersample == 8) + break; + /* fall thru... */ + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_MINISWHITE: + if (!setupMap(img)) + return (0); + break; + case PHOTOMETRIC_PALETTE: + /* + * Convert 16-bit colormap to 8-bit (unless it looks + * like an old-style 8-bit colormap). + */ + if (checkcmap(img) == 16) + cvtcmap(img); + else + TIFFWarningExt(img->tif->tif_clientdata, TIFFFileName(img->tif), "Assuming 8-bit colormap"); + /* + * Use mapping table and colormap to construct + * unpacking tables for samples < 8 bits. + */ + if (img->bitspersample <= 8 && !makecmap(img)) + return (0); + break; + } + return (1); +} + +/* + * Select the appropriate conversion routine for packed data. + */ +static int +PickContigCase(TIFFRGBAImage* img) +{ + img->get = TIFFIsTiled(img->tif) ? gtTileContig : gtStripContig; + img->put.contig = NULL; + switch (img->photometric) { + case PHOTOMETRIC_RGB: + switch (img->bitspersample) { + case 8: + if (img->alpha == EXTRASAMPLE_ASSOCALPHA) + img->put.contig = putRGBAAcontig8bittile; + else if (img->alpha == EXTRASAMPLE_UNASSALPHA) + { + if (BuildMapUaToAa(img)) + img->put.contig = putRGBUAcontig8bittile; + } + else + img->put.contig = putRGBcontig8bittile; + break; + case 16: + if (img->alpha == EXTRASAMPLE_ASSOCALPHA) + { + if (BuildMapBitdepth16To8(img)) + img->put.contig = putRGBAAcontig16bittile; + } + else if (img->alpha == EXTRASAMPLE_UNASSALPHA) + { + if (BuildMapBitdepth16To8(img) && + BuildMapUaToAa(img)) + img->put.contig = putRGBUAcontig16bittile; + } + else + { + if (BuildMapBitdepth16To8(img)) + img->put.contig = putRGBcontig16bittile; + } + break; + } + break; + case PHOTOMETRIC_SEPARATED: + if (buildMap(img)) { + if (img->bitspersample == 8) { + if (!img->Map) + img->put.contig = putRGBcontig8bitCMYKtile; + else + img->put.contig = putRGBcontig8bitCMYKMaptile; + } + } + break; + case PHOTOMETRIC_PALETTE: + if (buildMap(img)) { + switch (img->bitspersample) { + case 8: + img->put.contig = put8bitcmaptile; + break; + case 4: + img->put.contig = put4bitcmaptile; + break; + case 2: + img->put.contig = put2bitcmaptile; + break; + case 1: + img->put.contig = put1bitcmaptile; + break; + } + } + break; + case PHOTOMETRIC_MINISWHITE: + case PHOTOMETRIC_MINISBLACK: + if (buildMap(img)) { + switch (img->bitspersample) { + case 16: + img->put.contig = put16bitbwtile; + break; + case 8: + if (img->alpha && img->samplesperpixel == 2) + img->put.contig = putagreytile; + else + img->put.contig = putgreytile; + break; + case 4: + img->put.contig = put4bitbwtile; + break; + case 2: + img->put.contig = put2bitbwtile; + break; + case 1: + img->put.contig = put1bitbwtile; + break; + } + } + break; + case PHOTOMETRIC_YCBCR: + if ((img->bitspersample==8) && (img->samplesperpixel==3)) + { + if (initYCbCrConversion(img)!=0) + { + /* + * The 6.0 spec says that subsampling must be + * one of 1, 2, or 4, and that vertical subsampling + * must always be <= horizontal subsampling; so + * there are only a few possibilities and we just + * enumerate the cases. + * Joris: added support for the [1,2] case, nonetheless, to accommodate + * some OJPEG files + */ + uint16 SubsamplingHor; + uint16 SubsamplingVer; + TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRSUBSAMPLING, &SubsamplingHor, &SubsamplingVer); + switch ((SubsamplingHor<<4)|SubsamplingVer) { + case 0x44: + img->put.contig = putcontig8bitYCbCr44tile; + break; + case 0x42: + img->put.contig = putcontig8bitYCbCr42tile; + break; + case 0x41: + img->put.contig = putcontig8bitYCbCr41tile; + break; + case 0x22: + img->put.contig = putcontig8bitYCbCr22tile; + break; + case 0x21: + img->put.contig = putcontig8bitYCbCr21tile; + break; + case 0x12: + img->put.contig = putcontig8bitYCbCr12tile; + break; + case 0x11: + img->put.contig = putcontig8bitYCbCr11tile; + break; + } + } + } + break; + case PHOTOMETRIC_CIELAB: + if (buildMap(img)) { + if (img->bitspersample == 8) + img->put.contig = initCIELabConversion(img); + break; + } + } + return ((img->get!=NULL) && (img->put.contig!=NULL)); +} + +/* + * Select the appropriate conversion routine for unpacked data. + * + * NB: we assume that unpacked single channel data is directed + * to the "packed routines. + */ +static int +PickSeparateCase(TIFFRGBAImage* img) +{ + img->get = TIFFIsTiled(img->tif) ? gtTileSeparate : gtStripSeparate; + img->put.separate = NULL; + switch (img->photometric) { + case PHOTOMETRIC_MINISWHITE: + case PHOTOMETRIC_MINISBLACK: + /* greyscale images processed pretty much as RGB by gtTileSeparate */ + case PHOTOMETRIC_RGB: + switch (img->bitspersample) { + case 8: + if (img->alpha == EXTRASAMPLE_ASSOCALPHA) + img->put.separate = putRGBAAseparate8bittile; + else if (img->alpha == EXTRASAMPLE_UNASSALPHA) + { + if (BuildMapUaToAa(img)) + img->put.separate = putRGBUAseparate8bittile; + } + else + img->put.separate = putRGBseparate8bittile; + break; + case 16: + if (img->alpha == EXTRASAMPLE_ASSOCALPHA) + { + if (BuildMapBitdepth16To8(img)) + img->put.separate = putRGBAAseparate16bittile; + } + else if (img->alpha == EXTRASAMPLE_UNASSALPHA) + { + if (BuildMapBitdepth16To8(img) && + BuildMapUaToAa(img)) + img->put.separate = putRGBUAseparate16bittile; + } + else + { + if (BuildMapBitdepth16To8(img)) + img->put.separate = putRGBseparate16bittile; + } + break; + } + break; + case PHOTOMETRIC_SEPARATED: + if (img->bitspersample == 8 && img->samplesperpixel == 4) + { + img->alpha = 1; // Not alpha, but seems like the only way to get 4th band + img->put.separate = putCMYKseparate8bittile; + } + break; + case PHOTOMETRIC_YCBCR: + if ((img->bitspersample==8) && (img->samplesperpixel==3)) + { + if (initYCbCrConversion(img)!=0) + { + uint16 hs, vs; + TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRSUBSAMPLING, &hs, &vs); + switch ((hs<<4)|vs) { + case 0x11: + img->put.separate = putseparate8bitYCbCr11tile; + break; + /* TODO: add other cases here */ + } + } + } + break; + } + return ((img->get!=NULL) && (img->put.separate!=NULL)); +} + +static int +BuildMapUaToAa(TIFFRGBAImage* img) +{ + static const char module[]="BuildMapUaToAa"; + uint8* m; + uint16 na,nv; + assert(img->UaToAa==NULL); + img->UaToAa=_TIFFmalloc(65536); + if (img->UaToAa==NULL) + { + TIFFErrorExt(img->tif->tif_clientdata,module,"Out of memory"); + return(0); + } + m=img->UaToAa; + for (na=0; na<256; na++) + { + for (nv=0; nv<256; nv++) + *m++=(nv*na+127)/255; + } + return(1); +} + +static int +BuildMapBitdepth16To8(TIFFRGBAImage* img) +{ + static const char module[]="BuildMapBitdepth16To8"; + uint8* m; + uint32 n; + assert(img->Bitdepth16To8==NULL); + img->Bitdepth16To8=_TIFFmalloc(65536); + if (img->Bitdepth16To8==NULL) + { + TIFFErrorExt(img->tif->tif_clientdata,module,"Out of memory"); + return(0); + } + m=img->Bitdepth16To8; + for (n=0; n<65536; n++) + *m++=(n+128)/257; + return(1); +} + + +/* + * Read a whole strip off data from the file, and convert to RGBA form. + * If this is the last strip, then it will only contain the portion of + * the strip that is actually within the image space. The result is + * organized in bottom to top form. + */ + + +int +TIFFReadRGBAStrip(TIFF* tif, uint32 row, uint32 * raster ) + +{ + char emsg[1024] = ""; + TIFFRGBAImage img; + int ok; + uint32 rowsperstrip, rows_to_read; + + if( TIFFIsTiled( tif ) ) + { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), + "Can't use TIFFReadRGBAStrip() with tiled file."); + return (0); + } + + TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip); + if( (row % rowsperstrip) != 0 ) + { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), + "Row passed to TIFFReadRGBAStrip() must be first in a strip."); + return (0); + } + + if (TIFFRGBAImageOK(tif, emsg) && TIFFRGBAImageBegin(&img, tif, 0, emsg)) { + + img.row_offset = row; + img.col_offset = 0; + + if( row + rowsperstrip > img.height ) + rows_to_read = img.height - row; + else + rows_to_read = rowsperstrip; + + ok = TIFFRGBAImageGet(&img, raster, img.width, rows_to_read ); + + TIFFRGBAImageEnd(&img); + } else { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", emsg); + ok = 0; + } + + return (ok); +} + +/* + * Read a whole tile off data from the file, and convert to RGBA form. + * The returned RGBA data is organized from bottom to top of tile, + * and may include zeroed areas if the tile extends off the image. + */ + +int +TIFFReadRGBATile(TIFF* tif, uint32 col, uint32 row, uint32 * raster) + +{ + char emsg[1024] = ""; + TIFFRGBAImage img; + int ok; + uint32 tile_xsize, tile_ysize; + uint32 read_xsize, read_ysize; + uint32 i_row; + + /* + * Verify that our request is legal - on a tile file, and on a + * tile boundary. + */ + + if( !TIFFIsTiled( tif ) ) + { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), + "Can't use TIFFReadRGBATile() with stripped file."); + return (0); + } + + TIFFGetFieldDefaulted(tif, TIFFTAG_TILEWIDTH, &tile_xsize); + TIFFGetFieldDefaulted(tif, TIFFTAG_TILELENGTH, &tile_ysize); + if( (col % tile_xsize) != 0 || (row % tile_ysize) != 0 ) + { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), + "Row/col passed to TIFFReadRGBATile() must be top" + "left corner of a tile."); + return (0); + } + + /* + * Setup the RGBA reader. + */ + + if (!TIFFRGBAImageOK(tif, emsg) + || !TIFFRGBAImageBegin(&img, tif, 0, emsg)) { + TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "%s", emsg); + return( 0 ); + } + + /* + * The TIFFRGBAImageGet() function doesn't allow us to get off the + * edge of the image, even to fill an otherwise valid tile. So we + * figure out how much we can read, and fix up the tile buffer to + * a full tile configuration afterwards. + */ + + if( row + tile_ysize > img.height ) + read_ysize = img.height - row; + else + read_ysize = tile_ysize; + + if( col + tile_xsize > img.width ) + read_xsize = img.width - col; + else + read_xsize = tile_xsize; + + /* + * Read the chunk of imagery. + */ + + img.row_offset = row; + img.col_offset = col; + + ok = TIFFRGBAImageGet(&img, raster, read_xsize, read_ysize ); + + TIFFRGBAImageEnd(&img); + + /* + * If our read was incomplete we will need to fix up the tile by + * shifting the data around as if a full tile of data is being returned. + * + * This is all the more complicated because the image is organized in + * bottom to top format. + */ + + if( read_xsize == tile_xsize && read_ysize == tile_ysize ) + return( ok ); + + for( i_row = 0; i_row < read_ysize; i_row++ ) { + memmove( raster + (tile_ysize - i_row - 1) * tile_xsize, + raster + (read_ysize - i_row - 1) * read_xsize, + read_xsize * sizeof(uint32) ); + _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize+read_xsize, + 0, sizeof(uint32) * (tile_xsize - read_xsize) ); + } + + for( i_row = read_ysize; i_row < tile_ysize; i_row++ ) { + _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize, + 0, sizeof(uint32) * tile_xsize ); + } + + return (ok); +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_jpeg.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_jpeg.c new file mode 100644 index 000000000..dc77f5593 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_jpeg.c @@ -0,0 +1,2354 @@ +/* $Id: tif_jpeg.c,v 1.114 2014-12-30 16:37:22 erouault Exp $ */ + +/* + * Copyright (c) 1994-1997 Sam Leffler + * Copyright (c) 1994-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#define WIN32_LEAN_AND_MEAN +#define VC_EXTRALEAN + +#include "tiffiop.h" +#ifdef JPEG_SUPPORT + +/* + * TIFF Library + * + * JPEG Compression support per TIFF Technical Note #2 + * (*not* per the original TIFF 6.0 spec). + * + * This file is simply an interface to the libjpeg library written by + * the Independent JPEG Group. You need release 5 or later of the IJG + * code, which you can find on the Internet at ftp.uu.net:/graphics/jpeg/. + * + * Contributed by Tom Lane . + */ +#include + +int TIFFFillStrip(TIFF* tif, uint32 strip); +int TIFFFillTile(TIFF* tif, uint32 tile); +int TIFFReInitJPEG_12( TIFF *tif, int scheme, int is_encode ); + +/* We undefine FAR to avoid conflict with JPEG definition */ + +#ifdef FAR +#undef FAR +#endif + +/* + Libjpeg's jmorecfg.h defines INT16 and INT32, but only if XMD_H is + not defined. Unfortunately, the MinGW and Borland compilers include + a typedef for INT32, which causes a conflict. MSVC does not include + a conficting typedef given the headers which are included. +*/ +#if defined(__BORLANDC__) || defined(__MINGW32__) +# define XMD_H 1 +#endif + +/* + The windows RPCNDR.H file defines boolean, but defines it with the + unsigned char size. You should compile JPEG library using appropriate + definitions in jconfig.h header, but many users compile library in wrong + way. That causes errors of the following type: + + "JPEGLib: JPEG parameter struct mismatch: library thinks size is 432, + caller expects 464" + + For such users we wil fix the problem here. See install.doc file from + the JPEG library distribution for details. +*/ + +/* Define "boolean" as unsigned char, not int, per Windows custom. */ +#if defined(__WIN32__) && !defined(__MINGW32__) +# ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ + typedef unsigned char boolean; +# endif +# define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ +#endif + +#include "jpeglib.h" +#include "jerror.h" + +/* + * Do we want to do special processing suitable for when JSAMPLE is a + * 16bit value? + */ + +#if defined(JPEG_LIB_MK1) +# define JPEG_LIB_MK1_OR_12BIT 1 +#elif BITS_IN_JSAMPLE == 12 +# define JPEG_LIB_MK1_OR_12BIT 1 +#endif + +/* + * We are using width_in_blocks which is supposed to be private to + * libjpeg. Unfortunately, the libjpeg delivered with Cygwin has + * renamed this member to width_in_data_units. Since the header has + * also renamed a define, use that unique define name in order to + * detect the problem header and adjust to suit. + */ +#if defined(D_MAX_DATA_UNITS_IN_MCU) +#define width_in_blocks width_in_data_units +#endif + +/* + * On some machines it may be worthwhile to use _setjmp or sigsetjmp + * in place of plain setjmp. These macros will make it easier. + */ +#define SETJMP(jbuf) setjmp(jbuf) +#define LONGJMP(jbuf,code) longjmp(jbuf,code) +#define JMP_BUF jmp_buf + +typedef struct jpeg_destination_mgr jpeg_destination_mgr; +typedef struct jpeg_source_mgr jpeg_source_mgr; +typedef struct jpeg_error_mgr jpeg_error_mgr; + +/* + * State block for each open TIFF file using + * libjpeg to do JPEG compression/decompression. + * + * libjpeg's visible state is either a jpeg_compress_struct + * or jpeg_decompress_struct depending on which way we + * are going. comm can be used to refer to the fields + * which are common to both. + * + * NB: cinfo is required to be the first member of JPEGState, + * so we can safely cast JPEGState* -> jpeg_xxx_struct* + * and vice versa! + */ +typedef struct { + union { + struct jpeg_compress_struct c; + struct jpeg_decompress_struct d; + struct jpeg_common_struct comm; + } cinfo; /* NB: must be first */ + int cinfo_initialized; + + jpeg_error_mgr err; /* libjpeg error manager */ + JMP_BUF exit_jmpbuf; /* for catching libjpeg failures */ + /* + * The following two members could be a union, but + * they're small enough that it's not worth the effort. + */ + jpeg_destination_mgr dest; /* data dest for compression */ + jpeg_source_mgr src; /* data source for decompression */ + /* private state */ + TIFF* tif; /* back link needed by some code */ + uint16 photometric; /* copy of PhotometricInterpretation */ + uint16 h_sampling; /* luminance sampling factors */ + uint16 v_sampling; + tmsize_t bytesperline; /* decompressed bytes per scanline */ + /* pointers to intermediate buffers when processing downsampled data */ + JSAMPARRAY ds_buffer[MAX_COMPONENTS]; + int scancount; /* number of "scanlines" accumulated */ + int samplesperclump; + + TIFFVGetMethod vgetparent; /* super-class method */ + TIFFVSetMethod vsetparent; /* super-class method */ + TIFFPrintMethod printdir; /* super-class method */ + TIFFStripMethod defsparent; /* super-class method */ + TIFFTileMethod deftparent; /* super-class method */ + /* pseudo-tag fields */ + void* jpegtables; /* JPEGTables tag value, or NULL */ + uint32 jpegtables_length; /* number of bytes in same */ + int jpegquality; /* Compression quality level */ + int jpegcolormode; /* Auto RGB<=>YCbCr convert? */ + int jpegtablesmode; /* What to put in JPEGTables */ + + int ycbcrsampling_fetched; +} JPEGState; + +#define JState(tif) ((JPEGState*)(tif)->tif_data) + +static int JPEGDecode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s); +static int JPEGDecodeRaw(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s); +static int JPEGEncode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s); +static int JPEGEncodeRaw(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s); +static int JPEGInitializeLibJPEG(TIFF * tif, int decode ); +static int DecodeRowError(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s); + +#define FIELD_JPEGTABLES (FIELD_CODEC+0) + +static const TIFFField jpegFields[] = { + { TIFFTAG_JPEGTABLES, -3, -3, TIFF_UNDEFINED, 0, TIFF_SETGET_C32_UINT8, TIFF_SETGET_C32_UINT8, FIELD_JPEGTABLES, FALSE, TRUE, "JPEGTables", NULL }, + { TIFFTAG_JPEGQUALITY, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "", NULL }, + { TIFFTAG_JPEGCOLORMODE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "", NULL }, + { TIFFTAG_JPEGTABLESMODE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "", NULL } +}; + +/* + * libjpeg interface layer. + * + * We use setjmp/longjmp to return control to libtiff + * when a fatal error is encountered within the JPEG + * library. We also direct libjpeg error and warning + * messages through the appropriate libtiff handlers. + */ + +/* + * Error handling routines (these replace corresponding + * IJG routines from jerror.c). These are used for both + * compression and decompression. + */ +static void +TIFFjpeg_error_exit(j_common_ptr cinfo) +{ + JPEGState *sp = (JPEGState *) cinfo; /* NB: cinfo assumed first */ + char buffer[JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message) (cinfo, buffer); + TIFFErrorExt(sp->tif->tif_clientdata, "JPEGLib", "%s", buffer); /* display the error message */ + jpeg_abort(cinfo); /* clean up libjpeg state */ + LONGJMP(sp->exit_jmpbuf, 1); /* return to libtiff caller */ +} + +/* + * This routine is invoked only for warning messages, + * since error_exit does its own thing and trace_level + * is never set > 0. + */ +static void +TIFFjpeg_output_message(j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message) (cinfo, buffer); + TIFFWarningExt(((JPEGState *) cinfo)->tif->tif_clientdata, "JPEGLib", "%s", buffer); +} + +/* + * Interface routines. This layer of routines exists + * primarily to limit side-effects from using setjmp. + * Also, normal/error returns are converted into return + * values per libtiff practice. + */ +#define CALLJPEG(sp, fail, op) (SETJMP((sp)->exit_jmpbuf) ? (fail) : (op)) +#define CALLVJPEG(sp, op) CALLJPEG(sp, 0, ((op),1)) + +static int +TIFFjpeg_create_compress(JPEGState* sp) +{ + /* initialize JPEG error handling */ + sp->cinfo.c.err = jpeg_std_error(&sp->err); + sp->err.error_exit = TIFFjpeg_error_exit; + sp->err.output_message = TIFFjpeg_output_message; + + return CALLVJPEG(sp, jpeg_create_compress(&sp->cinfo.c)); +} + +static int +TIFFjpeg_create_decompress(JPEGState* sp) +{ + /* initialize JPEG error handling */ + sp->cinfo.d.err = jpeg_std_error(&sp->err); + sp->err.error_exit = TIFFjpeg_error_exit; + sp->err.output_message = TIFFjpeg_output_message; + + return CALLVJPEG(sp, jpeg_create_decompress(&sp->cinfo.d)); +} + +static int +TIFFjpeg_set_defaults(JPEGState* sp) +{ + return CALLVJPEG(sp, jpeg_set_defaults(&sp->cinfo.c)); +} + +static int +TIFFjpeg_set_colorspace(JPEGState* sp, J_COLOR_SPACE colorspace) +{ + return CALLVJPEG(sp, jpeg_set_colorspace(&sp->cinfo.c, colorspace)); +} + +static int +TIFFjpeg_set_quality(JPEGState* sp, int quality, boolean force_baseline) +{ + return CALLVJPEG(sp, + jpeg_set_quality(&sp->cinfo.c, quality, force_baseline)); +} + +static int +TIFFjpeg_suppress_tables(JPEGState* sp, boolean suppress) +{ + return CALLVJPEG(sp, jpeg_suppress_tables(&sp->cinfo.c, suppress)); +} + +static int +TIFFjpeg_start_compress(JPEGState* sp, boolean write_all_tables) +{ + return CALLVJPEG(sp, + jpeg_start_compress(&sp->cinfo.c, write_all_tables)); +} + +static int +TIFFjpeg_write_scanlines(JPEGState* sp, JSAMPARRAY scanlines, int num_lines) +{ + return CALLJPEG(sp, -1, (int) jpeg_write_scanlines(&sp->cinfo.c, + scanlines, (JDIMENSION) num_lines)); +} + +static int +TIFFjpeg_write_raw_data(JPEGState* sp, JSAMPIMAGE data, int num_lines) +{ + return CALLJPEG(sp, -1, (int) jpeg_write_raw_data(&sp->cinfo.c, + data, (JDIMENSION) num_lines)); +} + +static int +TIFFjpeg_finish_compress(JPEGState* sp) +{ + return CALLVJPEG(sp, jpeg_finish_compress(&sp->cinfo.c)); +} + +static int +TIFFjpeg_write_tables(JPEGState* sp) +{ + return CALLVJPEG(sp, jpeg_write_tables(&sp->cinfo.c)); +} + +static int +TIFFjpeg_read_header(JPEGState* sp, boolean require_image) +{ + return CALLJPEG(sp, -1, jpeg_read_header(&sp->cinfo.d, require_image)); +} + +static int +TIFFjpeg_start_decompress(JPEGState* sp) +{ + return CALLVJPEG(sp, jpeg_start_decompress(&sp->cinfo.d)); +} + +static int +TIFFjpeg_read_scanlines(JPEGState* sp, JSAMPARRAY scanlines, int max_lines) +{ + return CALLJPEG(sp, -1, (int) jpeg_read_scanlines(&sp->cinfo.d, + scanlines, (JDIMENSION) max_lines)); +} + +static int +TIFFjpeg_read_raw_data(JPEGState* sp, JSAMPIMAGE data, int max_lines) +{ + return CALLJPEG(sp, -1, (int) jpeg_read_raw_data(&sp->cinfo.d, + data, (JDIMENSION) max_lines)); +} + +static int +TIFFjpeg_finish_decompress(JPEGState* sp) +{ + return CALLJPEG(sp, -1, (int) jpeg_finish_decompress(&sp->cinfo.d)); +} + +static int +TIFFjpeg_abort(JPEGState* sp) +{ + return CALLVJPEG(sp, jpeg_abort(&sp->cinfo.comm)); +} + +static int +TIFFjpeg_destroy(JPEGState* sp) +{ + return CALLVJPEG(sp, jpeg_destroy(&sp->cinfo.comm)); +} + +static JSAMPARRAY +TIFFjpeg_alloc_sarray(JPEGState* sp, int pool_id, + JDIMENSION samplesperrow, JDIMENSION numrows) +{ + return CALLJPEG(sp, (JSAMPARRAY) NULL, + (*sp->cinfo.comm.mem->alloc_sarray) + (&sp->cinfo.comm, pool_id, samplesperrow, numrows)); +} + +/* + * JPEG library destination data manager. + * These routines direct compressed data from libjpeg into the + * libtiff output buffer. + */ + +static void +std_init_destination(j_compress_ptr cinfo) +{ + JPEGState* sp = (JPEGState*) cinfo; + TIFF* tif = sp->tif; + + sp->dest.next_output_byte = (JOCTET*) tif->tif_rawdata; + sp->dest.free_in_buffer = (size_t) tif->tif_rawdatasize; +} + +static boolean +std_empty_output_buffer(j_compress_ptr cinfo) +{ + JPEGState* sp = (JPEGState*) cinfo; + TIFF* tif = sp->tif; + + /* the entire buffer has been filled */ + tif->tif_rawcc = tif->tif_rawdatasize; + +#ifdef IPPJ_HUFF + /* + * The Intel IPP performance library does not necessarily fill up + * the whole output buffer on each pass, so only dump out the parts + * that have been filled. + * http://trac.osgeo.org/gdal/wiki/JpegIPP + */ + if ( sp->dest.free_in_buffer >= 0 ) { + tif->tif_rawcc = tif->tif_rawdatasize - sp->dest.free_in_buffer; + } +#endif + + TIFFFlushData1(tif); + sp->dest.next_output_byte = (JOCTET*) tif->tif_rawdata; + sp->dest.free_in_buffer = (size_t) tif->tif_rawdatasize; + + return (TRUE); +} + +static void +std_term_destination(j_compress_ptr cinfo) +{ + JPEGState* sp = (JPEGState*) cinfo; + TIFF* tif = sp->tif; + + tif->tif_rawcp = (uint8*) sp->dest.next_output_byte; + tif->tif_rawcc = + tif->tif_rawdatasize - (tmsize_t) sp->dest.free_in_buffer; + /* NB: libtiff does the final buffer flush */ +} + +static void +TIFFjpeg_data_dest(JPEGState* sp, TIFF* tif) +{ + (void) tif; + sp->cinfo.c.dest = &sp->dest; + sp->dest.init_destination = std_init_destination; + sp->dest.empty_output_buffer = std_empty_output_buffer; + sp->dest.term_destination = std_term_destination; +} + +/* + * Alternate destination manager for outputting to JPEGTables field. + */ + +static void +tables_init_destination(j_compress_ptr cinfo) +{ + JPEGState* sp = (JPEGState*) cinfo; + + /* while building, jpegtables_length is allocated buffer size */ + sp->dest.next_output_byte = (JOCTET*) sp->jpegtables; + sp->dest.free_in_buffer = (size_t) sp->jpegtables_length; +} + +static boolean +tables_empty_output_buffer(j_compress_ptr cinfo) +{ + JPEGState* sp = (JPEGState*) cinfo; + void* newbuf; + + /* the entire buffer has been filled; enlarge it by 1000 bytes */ + newbuf = _TIFFrealloc((void*) sp->jpegtables, + (tmsize_t) (sp->jpegtables_length + 1000)); + if (newbuf == NULL) + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 100); + sp->dest.next_output_byte = (JOCTET*) newbuf + sp->jpegtables_length; + sp->dest.free_in_buffer = (size_t) 1000; + sp->jpegtables = newbuf; + sp->jpegtables_length += 1000; + return (TRUE); +} + +static void +tables_term_destination(j_compress_ptr cinfo) +{ + JPEGState* sp = (JPEGState*) cinfo; + + /* set tables length to number of bytes actually emitted */ + sp->jpegtables_length -= (uint32) sp->dest.free_in_buffer; +} + +static int +TIFFjpeg_tables_dest(JPEGState* sp, TIFF* tif) +{ + (void) tif; + /* + * Allocate a working buffer for building tables. + * Initial size is 1000 bytes, which is usually adequate. + */ + if (sp->jpegtables) + _TIFFfree(sp->jpegtables); + sp->jpegtables_length = 1000; + sp->jpegtables = (void*) _TIFFmalloc((tmsize_t) sp->jpegtables_length); + if (sp->jpegtables == NULL) { + sp->jpegtables_length = 0; + TIFFErrorExt(sp->tif->tif_clientdata, "TIFFjpeg_tables_dest", "No space for JPEGTables"); + return (0); + } + sp->cinfo.c.dest = &sp->dest; + sp->dest.init_destination = tables_init_destination; + sp->dest.empty_output_buffer = tables_empty_output_buffer; + sp->dest.term_destination = tables_term_destination; + return (1); +} + +/* + * JPEG library source data manager. + * These routines supply compressed data to libjpeg. + */ + +static void +std_init_source(j_decompress_ptr cinfo) +{ + JPEGState* sp = (JPEGState*) cinfo; + TIFF* tif = sp->tif; + + sp->src.next_input_byte = (const JOCTET*) tif->tif_rawdata; + sp->src.bytes_in_buffer = (size_t) tif->tif_rawcc; +} + +static boolean +std_fill_input_buffer(j_decompress_ptr cinfo) +{ + JPEGState* sp = (JPEGState* ) cinfo; + static const JOCTET dummy_EOI[2] = { 0xFF, JPEG_EOI }; + +#ifdef IPPJ_HUFF + /* + * The Intel IPP performance library does not necessarily read the whole + * input buffer in one pass, so it is possible to get here with data + * yet to read. + * + * We just return without doing anything, until the entire buffer has + * been read. + * http://trac.osgeo.org/gdal/wiki/JpegIPP + */ + if( sp->src.bytes_in_buffer > 0 ) { + return (TRUE); + } +#endif + + /* + * Normally the whole strip/tile is read and so we don't need to do + * a fill. In the case of CHUNKY_STRIP_READ_SUPPORT we might not have + * all the data, but the rawdata is refreshed between scanlines and + * we push this into the io machinery in JPEGDecode(). + * http://trac.osgeo.org/gdal/ticket/3894 + */ + + WARNMS(cinfo, JWRN_JPEG_EOF); + /* insert a fake EOI marker */ + sp->src.next_input_byte = dummy_EOI; + sp->src.bytes_in_buffer = 2; + return (TRUE); +} + +static void +std_skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + JPEGState* sp = (JPEGState*) cinfo; + + if (num_bytes > 0) { + if ((size_t)num_bytes > sp->src.bytes_in_buffer) { + /* oops, buffer overrun */ + (void) std_fill_input_buffer(cinfo); + } else { + sp->src.next_input_byte += (size_t) num_bytes; + sp->src.bytes_in_buffer -= (size_t) num_bytes; + } + } +} + +static void +std_term_source(j_decompress_ptr cinfo) +{ + /* No work necessary here */ + (void) cinfo; +} + +static void +TIFFjpeg_data_src(JPEGState* sp, TIFF* tif) +{ + (void) tif; + sp->cinfo.d.src = &sp->src; + sp->src.init_source = std_init_source; + sp->src.fill_input_buffer = std_fill_input_buffer; + sp->src.skip_input_data = std_skip_input_data; + sp->src.resync_to_restart = jpeg_resync_to_restart; + sp->src.term_source = std_term_source; + sp->src.bytes_in_buffer = 0; /* for safety */ + sp->src.next_input_byte = NULL; +} + +/* + * Alternate source manager for reading from JPEGTables. + * We can share all the code except for the init routine. + */ + +static void +tables_init_source(j_decompress_ptr cinfo) +{ + JPEGState* sp = (JPEGState*) cinfo; + + sp->src.next_input_byte = (const JOCTET*) sp->jpegtables; + sp->src.bytes_in_buffer = (size_t) sp->jpegtables_length; +} + +static void +TIFFjpeg_tables_src(JPEGState* sp, TIFF* tif) +{ + TIFFjpeg_data_src(sp, tif); + sp->src.init_source = tables_init_source; +} + +/* + * Allocate downsampled-data buffers needed for downsampled I/O. + * We use values computed in jpeg_start_compress or jpeg_start_decompress. + * We use libjpeg's allocator so that buffers will be released automatically + * when done with strip/tile. + * This is also a handy place to compute samplesperclump, bytesperline. + */ +static int +alloc_downsampled_buffers(TIFF* tif, jpeg_component_info* comp_info, + int num_components) +{ + JPEGState* sp = JState(tif); + int ci; + jpeg_component_info* compptr; + JSAMPARRAY buf; + int samples_per_clump = 0; + + for (ci = 0, compptr = comp_info; ci < num_components; + ci++, compptr++) { + samples_per_clump += compptr->h_samp_factor * + compptr->v_samp_factor; + buf = TIFFjpeg_alloc_sarray(sp, JPOOL_IMAGE, + compptr->width_in_blocks * DCTSIZE, + (JDIMENSION) (compptr->v_samp_factor*DCTSIZE)); + if (buf == NULL) + return (0); + sp->ds_buffer[ci] = buf; + } + sp->samplesperclump = samples_per_clump; + return (1); +} + + +/* + * JPEG Decoding. + */ + +#ifdef CHECK_JPEG_YCBCR_SUBSAMPLING + +#define JPEG_MARKER_SOF0 0xC0 +#define JPEG_MARKER_SOF1 0xC1 +#define JPEG_MARKER_SOF2 0xC2 +#define JPEG_MARKER_SOF9 0xC9 +#define JPEG_MARKER_SOF10 0xCA +#define JPEG_MARKER_DHT 0xC4 +#define JPEG_MARKER_SOI 0xD8 +#define JPEG_MARKER_SOS 0xDA +#define JPEG_MARKER_DQT 0xDB +#define JPEG_MARKER_DRI 0xDD +#define JPEG_MARKER_APP0 0xE0 +#define JPEG_MARKER_COM 0xFE +struct JPEGFixupTagsSubsamplingData +{ + TIFF* tif; + void* buffer; + uint32 buffersize; + uint8* buffercurrentbyte; + uint32 bufferbytesleft; + uint64 fileoffset; + uint64 filebytesleft; + uint8 filepositioned; +}; +static void JPEGFixupTagsSubsampling(TIFF* tif); +static int JPEGFixupTagsSubsamplingSec(struct JPEGFixupTagsSubsamplingData* data); +static int JPEGFixupTagsSubsamplingReadByte(struct JPEGFixupTagsSubsamplingData* data, uint8* result); +static int JPEGFixupTagsSubsamplingReadWord(struct JPEGFixupTagsSubsamplingData* data, uint16* result); +static void JPEGFixupTagsSubsamplingSkip(struct JPEGFixupTagsSubsamplingData* data, uint16 skiplength); + +#endif + +static int +JPEGFixupTags(TIFF* tif) +{ +#ifdef CHECK_JPEG_YCBCR_SUBSAMPLING + if ((tif->tif_dir.td_photometric==PHOTOMETRIC_YCBCR)&& + (tif->tif_dir.td_planarconfig==PLANARCONFIG_CONTIG)&& + (tif->tif_dir.td_samplesperpixel==3)) + JPEGFixupTagsSubsampling(tif); +#endif + + return(1); +} + +#ifdef CHECK_JPEG_YCBCR_SUBSAMPLING + +static void +JPEGFixupTagsSubsampling(TIFF* tif) +{ + /* + * Some JPEG-in-TIFF produces do not emit the YCBCRSUBSAMPLING values in + * the TIFF tags, but still use non-default (2,2) values within the jpeg + * data stream itself. In order for TIFF applications to work properly + * - for instance to get the strip buffer size right - it is imperative + * that the subsampling be available before we start reading the image + * data normally. This function will attempt to analyze the first strip in + * order to get the sampling values from the jpeg data stream. + * + * Note that JPEGPreDeocode() will produce a fairly loud warning when the + * discovered sampling does not match the default sampling (2,2) or whatever + * was actually in the tiff tags. + * + * See the bug in bugzilla for details: + * + * http://bugzilla.remotesensing.org/show_bug.cgi?id=168 + * + * Frank Warmerdam, July 2002 + * Joris Van Damme, May 2007 + */ + static const char module[] = "JPEGFixupTagsSubsampling"; + struct JPEGFixupTagsSubsamplingData m; + + _TIFFFillStriles( tif ); + + if( tif->tif_dir.td_stripbytecount == NULL + || tif->tif_dir.td_stripoffset == NULL + || tif->tif_dir.td_stripbytecount[0] == 0 ) + { + /* Do not even try to check if the first strip/tile does not + yet exist, as occurs when GDAL has created a new NULL file + for instance. */ + return; + } + + m.tif=tif; + m.buffersize=2048; + m.buffer=_TIFFmalloc(m.buffersize); + if (m.buffer==NULL) + { + TIFFWarningExt(tif->tif_clientdata,module, + "Unable to allocate memory for auto-correcting of subsampling values; auto-correcting skipped"); + return; + } + m.buffercurrentbyte=NULL; + m.bufferbytesleft=0; + m.fileoffset=tif->tif_dir.td_stripoffset[0]; + m.filepositioned=0; + m.filebytesleft=tif->tif_dir.td_stripbytecount[0]; + if (!JPEGFixupTagsSubsamplingSec(&m)) + TIFFWarningExt(tif->tif_clientdata,module, + "Unable to auto-correct subsampling values, likely corrupt JPEG compressed data in first strip/tile; auto-correcting skipped"); + _TIFFfree(m.buffer); +} + +static int +JPEGFixupTagsSubsamplingSec(struct JPEGFixupTagsSubsamplingData* data) +{ + static const char module[] = "JPEGFixupTagsSubsamplingSec"; + uint8 m; + while (1) + { + while (1) + { + if (!JPEGFixupTagsSubsamplingReadByte(data,&m)) + return(0); + if (m==255) + break; + } + while (1) + { + if (!JPEGFixupTagsSubsamplingReadByte(data,&m)) + return(0); + if (m!=255) + break; + } + switch (m) + { + case JPEG_MARKER_SOI: + /* this type of marker has no data and should be skipped */ + break; + case JPEG_MARKER_COM: + case JPEG_MARKER_APP0: + case JPEG_MARKER_APP0+1: + case JPEG_MARKER_APP0+2: + case JPEG_MARKER_APP0+3: + case JPEG_MARKER_APP0+4: + case JPEG_MARKER_APP0+5: + case JPEG_MARKER_APP0+6: + case JPEG_MARKER_APP0+7: + case JPEG_MARKER_APP0+8: + case JPEG_MARKER_APP0+9: + case JPEG_MARKER_APP0+10: + case JPEG_MARKER_APP0+11: + case JPEG_MARKER_APP0+12: + case JPEG_MARKER_APP0+13: + case JPEG_MARKER_APP0+14: + case JPEG_MARKER_APP0+15: + case JPEG_MARKER_DQT: + case JPEG_MARKER_SOS: + case JPEG_MARKER_DHT: + case JPEG_MARKER_DRI: + /* this type of marker has data, but it has no use to us and should be skipped */ + { + uint16 n; + if (!JPEGFixupTagsSubsamplingReadWord(data,&n)) + return(0); + if (n<2) + return(0); + n-=2; + if (n>0) + JPEGFixupTagsSubsamplingSkip(data,n); + } + break; + case JPEG_MARKER_SOF0: /* Baseline sequential Huffman */ + case JPEG_MARKER_SOF1: /* Extended sequential Huffman */ + case JPEG_MARKER_SOF2: /* Progressive Huffman: normally not allowed by TechNote, but that doesn't hurt supporting it */ + case JPEG_MARKER_SOF9: /* Extended sequential arithmetic */ + case JPEG_MARKER_SOF10: /* Progressive arithmetic: normally not allowed by TechNote, but that doesn't hurt supporting it */ + /* this marker contains the subsampling factors we're scanning for */ + { + uint16 n; + uint16 o; + uint8 p; + uint8 ph,pv; + if (!JPEGFixupTagsSubsamplingReadWord(data,&n)) + return(0); + if (n!=8+data->tif->tif_dir.td_samplesperpixel*3) + return(0); + JPEGFixupTagsSubsamplingSkip(data,7); + if (!JPEGFixupTagsSubsamplingReadByte(data,&p)) + return(0); + ph=(p>>4); + pv=(p&15); + JPEGFixupTagsSubsamplingSkip(data,1); + for (o=1; otif->tif_dir.td_samplesperpixel; o++) + { + JPEGFixupTagsSubsamplingSkip(data,1); + if (!JPEGFixupTagsSubsamplingReadByte(data,&p)) + return(0); + if (p!=0x11) + { + TIFFWarningExt(data->tif->tif_clientdata,module, + "Subsampling values inside JPEG compressed data have no TIFF equivalent, auto-correction of TIFF subsampling values failed"); + return(1); + } + JPEGFixupTagsSubsamplingSkip(data,1); + } + if (((ph!=1)&&(ph!=2)&&(ph!=4))||((pv!=1)&&(pv!=2)&&(pv!=4))) + { + TIFFWarningExt(data->tif->tif_clientdata,module, + "Subsampling values inside JPEG compressed data have no TIFF equivalent, auto-correction of TIFF subsampling values failed"); + return(1); + } + if ((ph!=data->tif->tif_dir.td_ycbcrsubsampling[0])||(pv!=data->tif->tif_dir.td_ycbcrsubsampling[1])) + { + TIFFWarningExt(data->tif->tif_clientdata,module, + "Auto-corrected former TIFF subsampling values [%d,%d] to match subsampling values inside JPEG compressed data [%d,%d]", + (int)data->tif->tif_dir.td_ycbcrsubsampling[0], + (int)data->tif->tif_dir.td_ycbcrsubsampling[1], + (int)ph,(int)pv); + data->tif->tif_dir.td_ycbcrsubsampling[0]=ph; + data->tif->tif_dir.td_ycbcrsubsampling[1]=pv; + } + } + return(1); + default: + return(0); + } + } +} + +static int +JPEGFixupTagsSubsamplingReadByte(struct JPEGFixupTagsSubsamplingData* data, uint8* result) +{ + if (data->bufferbytesleft==0) + { + uint32 m; + if (data->filebytesleft==0) + return(0); + if (!data->filepositioned) + { + TIFFSeekFile(data->tif,data->fileoffset,SEEK_SET); + data->filepositioned=1; + } + m=data->buffersize; + if ((uint64)m>data->filebytesleft) + m=(uint32)data->filebytesleft; + assert(m<0x80000000UL); + if (TIFFReadFile(data->tif,data->buffer,(tmsize_t)m)!=(tmsize_t)m) + return(0); + data->buffercurrentbyte=data->buffer; + data->bufferbytesleft=m; + data->fileoffset+=m; + data->filebytesleft-=m; + } + *result=*data->buffercurrentbyte; + data->buffercurrentbyte++; + data->bufferbytesleft--; + return(1); +} + +static int +JPEGFixupTagsSubsamplingReadWord(struct JPEGFixupTagsSubsamplingData* data, uint16* result) +{ + uint8 ma; + uint8 mb; + if (!JPEGFixupTagsSubsamplingReadByte(data,&ma)) + return(0); + if (!JPEGFixupTagsSubsamplingReadByte(data,&mb)) + return(0); + *result=(ma<<8)|mb; + return(1); +} + +static void +JPEGFixupTagsSubsamplingSkip(struct JPEGFixupTagsSubsamplingData* data, uint16 skiplength) +{ + if ((uint32)skiplength<=data->bufferbytesleft) + { + data->buffercurrentbyte+=skiplength; + data->bufferbytesleft-=skiplength; + } + else + { + uint16 m; + m=skiplength-data->bufferbytesleft; + if (m<=data->filebytesleft) + { + data->bufferbytesleft=0; + data->fileoffset+=m; + data->filebytesleft-=m; + data->filepositioned=0; + } + else + { + data->bufferbytesleft=0; + data->filebytesleft=0; + } + } +} + +#endif + + +static int +JPEGSetupDecode(TIFF* tif) +{ + JPEGState* sp = JState(tif); + TIFFDirectory *td = &tif->tif_dir; + +#if defined(JPEG_DUAL_MODE_8_12) && !defined(TIFFInitJPEG) + if( tif->tif_dir.td_bitspersample == 12 ) + return TIFFReInitJPEG_12( tif, COMPRESSION_JPEG, 0 ); +#endif + + JPEGInitializeLibJPEG( tif, TRUE ); + + assert(sp != NULL); + assert(sp->cinfo.comm.is_decompressor); + + /* Read JPEGTables if it is present */ + if (TIFFFieldSet(tif,FIELD_JPEGTABLES)) { + TIFFjpeg_tables_src(sp, tif); + if(TIFFjpeg_read_header(sp,FALSE) != JPEG_HEADER_TABLES_ONLY) { + TIFFErrorExt(tif->tif_clientdata, "JPEGSetupDecode", "Bogus JPEGTables field"); + return (0); + } + } + + /* Grab parameters that are same for all strips/tiles */ + sp->photometric = td->td_photometric; + switch (sp->photometric) { + case PHOTOMETRIC_YCBCR: + sp->h_sampling = td->td_ycbcrsubsampling[0]; + sp->v_sampling = td->td_ycbcrsubsampling[1]; + break; + default: + /* TIFF 6.0 forbids subsampling of all other color spaces */ + sp->h_sampling = 1; + sp->v_sampling = 1; + break; + } + + /* Set up for reading normal data */ + TIFFjpeg_data_src(sp, tif); + tif->tif_postdecode = _TIFFNoPostDecode; /* override byte swapping */ + return (1); +} + +/* + * Set up for decoding a strip or tile. + */ +static int +JPEGPreDecode(TIFF* tif, uint16 s) +{ + JPEGState *sp = JState(tif); + TIFFDirectory *td = &tif->tif_dir; + static const char module[] = "JPEGPreDecode"; + uint32 segment_width, segment_height; + int downsampled_output; + int ci; + + assert(sp != NULL); + + if (sp->cinfo.comm.is_decompressor == 0) + { + tif->tif_setupdecode( tif ); + } + + assert(sp->cinfo.comm.is_decompressor); + /* + * Reset decoder state from any previous strip/tile, + * in case application didn't read the whole strip. + */ + if (!TIFFjpeg_abort(sp)) + return (0); + /* + * Read the header for this strip/tile. + */ + + if (TIFFjpeg_read_header(sp, TRUE) != JPEG_HEADER_OK) + return (0); + + tif->tif_rawcp = (uint8*) sp->src.next_input_byte; + tif->tif_rawcc = sp->src.bytes_in_buffer; + + /* + * Check image parameters and set decompression parameters. + */ + segment_width = td->td_imagewidth; + segment_height = td->td_imagelength - tif->tif_row; + if (isTiled(tif)) { + segment_width = td->td_tilewidth; + segment_height = td->td_tilelength; + sp->bytesperline = TIFFTileRowSize(tif); + } else { + if (segment_height > td->td_rowsperstrip) + segment_height = td->td_rowsperstrip; + sp->bytesperline = TIFFScanlineSize(tif); + } + if (td->td_planarconfig == PLANARCONFIG_SEPARATE && s > 0) { + /* + * For PC 2, scale down the expected strip/tile size + * to match a downsampled component + */ + segment_width = TIFFhowmany_32(segment_width, sp->h_sampling); + segment_height = TIFFhowmany_32(segment_height, sp->v_sampling); + } + if (sp->cinfo.d.image_width < segment_width || + sp->cinfo.d.image_height < segment_height) { + TIFFWarningExt(tif->tif_clientdata, module, + "Improper JPEG strip/tile size, " + "expected %dx%d, got %dx%d", + segment_width, segment_height, + sp->cinfo.d.image_width, + sp->cinfo.d.image_height); + } + if (sp->cinfo.d.image_width > segment_width || + sp->cinfo.d.image_height > segment_height) { + /* + * This case could be dangerous, if the strip or tile size has + * been reported as less than the amount of data jpeg will + * return, some potential security issues arise. Catch this + * case and error out. + */ + TIFFErrorExt(tif->tif_clientdata, module, + "JPEG strip/tile size exceeds expected dimensions," + " expected %dx%d, got %dx%d", + segment_width, segment_height, + sp->cinfo.d.image_width, sp->cinfo.d.image_height); + return (0); + } + if (sp->cinfo.d.num_components != + (td->td_planarconfig == PLANARCONFIG_CONTIG ? + td->td_samplesperpixel : 1)) { + TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG component count"); + return (0); + } +#ifdef JPEG_LIB_MK1 + if (12 != td->td_bitspersample && 8 != td->td_bitspersample) { + TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG data precision"); + return (0); + } + sp->cinfo.d.data_precision = td->td_bitspersample; + sp->cinfo.d.bits_in_jsample = td->td_bitspersample; +#else + if (sp->cinfo.d.data_precision != td->td_bitspersample) { + TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG data precision"); + return (0); + } +#endif + if (td->td_planarconfig == PLANARCONFIG_CONTIG) { + /* Component 0 should have expected sampling factors */ + if (sp->cinfo.d.comp_info[0].h_samp_factor != sp->h_sampling || + sp->cinfo.d.comp_info[0].v_samp_factor != sp->v_sampling) { + TIFFErrorExt(tif->tif_clientdata, module, + "Improper JPEG sampling factors %d,%d\n" + "Apparently should be %d,%d.", + sp->cinfo.d.comp_info[0].h_samp_factor, + sp->cinfo.d.comp_info[0].v_samp_factor, + sp->h_sampling, sp->v_sampling); + return (0); + } + /* Rest should have sampling factors 1,1 */ + for (ci = 1; ci < sp->cinfo.d.num_components; ci++) { + if (sp->cinfo.d.comp_info[ci].h_samp_factor != 1 || + sp->cinfo.d.comp_info[ci].v_samp_factor != 1) { + TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG sampling factors"); + return (0); + } + } + } else { + /* PC 2's single component should have sampling factors 1,1 */ + if (sp->cinfo.d.comp_info[0].h_samp_factor != 1 || + sp->cinfo.d.comp_info[0].v_samp_factor != 1) { + TIFFErrorExt(tif->tif_clientdata, module, "Improper JPEG sampling factors"); + return (0); + } + } + downsampled_output = FALSE; + if (td->td_planarconfig == PLANARCONFIG_CONTIG && + sp->photometric == PHOTOMETRIC_YCBCR && + sp->jpegcolormode == JPEGCOLORMODE_RGB) { + /* Convert YCbCr to RGB */ + sp->cinfo.d.jpeg_color_space = JCS_YCbCr; + sp->cinfo.d.out_color_space = JCS_RGB; + } else { + /* Suppress colorspace handling */ + sp->cinfo.d.jpeg_color_space = JCS_UNKNOWN; + sp->cinfo.d.out_color_space = JCS_UNKNOWN; + if (td->td_planarconfig == PLANARCONFIG_CONTIG && + (sp->h_sampling != 1 || sp->v_sampling != 1)) + downsampled_output = TRUE; + /* XXX what about up-sampling? */ + } + if (downsampled_output) { + /* Need to use raw-data interface to libjpeg */ + sp->cinfo.d.raw_data_out = TRUE; +#if JPEG_LIB_VERSION >= 70 + sp->cinfo.d.do_fancy_upsampling = FALSE; +#endif /* JPEG_LIB_VERSION >= 70 */ + tif->tif_decoderow = DecodeRowError; + tif->tif_decodestrip = JPEGDecodeRaw; + tif->tif_decodetile = JPEGDecodeRaw; + } else { + /* Use normal interface to libjpeg */ + sp->cinfo.d.raw_data_out = FALSE; + tif->tif_decoderow = JPEGDecode; + tif->tif_decodestrip = JPEGDecode; + tif->tif_decodetile = JPEGDecode; + } + /* Start JPEG decompressor */ + if (!TIFFjpeg_start_decompress(sp)) + return (0); + /* Allocate downsampled-data buffers if needed */ + if (downsampled_output) { + if (!alloc_downsampled_buffers(tif, sp->cinfo.d.comp_info, + sp->cinfo.d.num_components)) + return (0); + sp->scancount = DCTSIZE; /* mark buffer empty */ + } + return (1); +} + +/* + * Decode a chunk of pixels. + * "Standard" case: returned data is not downsampled. + */ +/*ARGSUSED*/ static int +JPEGDecode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s) +{ + JPEGState *sp = JState(tif); + tmsize_t nrows; + (void) s; + + /* + ** Update available information, buffer may have been refilled + ** between decode requests + */ + sp->src.next_input_byte = (const JOCTET*) tif->tif_rawcp; + sp->src.bytes_in_buffer = (size_t) tif->tif_rawcc; + + if( sp->bytesperline == 0 ) + return 0; + + nrows = cc / sp->bytesperline; + if (cc % sp->bytesperline) + TIFFWarningExt(tif->tif_clientdata, tif->tif_name, "fractional scanline not read"); + + if( nrows > (tmsize_t) sp->cinfo.d.image_height ) + nrows = sp->cinfo.d.image_height; + + /* data is expected to be read in multiples of a scanline */ + if (nrows) + { + JSAMPROW line_work_buf = NULL; + + /* + * For 6B, only use temporary buffer for 12 bit imagery. + * For Mk1 always use it. + */ +#if !defined(JPEG_LIB_MK1) + if( sp->cinfo.d.data_precision == 12 ) +#endif + { + line_work_buf = (JSAMPROW) + _TIFFmalloc(sizeof(short) * sp->cinfo.d.output_width + * sp->cinfo.d.num_components ); + } + + do { + if( line_work_buf != NULL ) + { + /* + * In the MK1 case, we aways read into a 16bit buffer, and then + * pack down to 12bit or 8bit. In 6B case we only read into 16 + * bit buffer for 12bit data, which we need to repack. + */ + if (TIFFjpeg_read_scanlines(sp, &line_work_buf, 1) != 1) + return (0); + + if( sp->cinfo.d.data_precision == 12 ) + { + int value_pairs = (sp->cinfo.d.output_width + * sp->cinfo.d.num_components) / 2; + int iPair; + + for( iPair = 0; iPair < value_pairs; iPair++ ) + { + unsigned char *out_ptr = + ((unsigned char *) buf) + iPair * 3; + JSAMPLE *in_ptr = line_work_buf + iPair * 2; + + out_ptr[0] = (in_ptr[0] & 0xff0) >> 4; + out_ptr[1] = ((in_ptr[0] & 0xf) << 4) + | ((in_ptr[1] & 0xf00) >> 8); + out_ptr[2] = ((in_ptr[1] & 0xff) >> 0); + } + } + else if( sp->cinfo.d.data_precision == 8 ) + { + int value_count = (sp->cinfo.d.output_width + * sp->cinfo.d.num_components); + int iValue; + + for( iValue = 0; iValue < value_count; iValue++ ) + { + ((unsigned char *) buf)[iValue] = + line_work_buf[iValue] & 0xff; + } + } + } + else + { + /* + * In the libjpeg6b 8bit case. We read directly into the + * TIFF buffer. + */ + JSAMPROW bufptr = (JSAMPROW)buf; + + if (TIFFjpeg_read_scanlines(sp, &bufptr, 1) != 1) + return (0); + } + + ++tif->tif_row; + buf += sp->bytesperline; + cc -= sp->bytesperline; + } while (--nrows > 0); + + if( line_work_buf != NULL ) + _TIFFfree( line_work_buf ); + } + + /* Update information on consumed data */ + tif->tif_rawcp = (uint8*) sp->src.next_input_byte; + tif->tif_rawcc = sp->src.bytes_in_buffer; + + /* Close down the decompressor if we've finished the strip or tile. */ + return sp->cinfo.d.output_scanline < sp->cinfo.d.output_height + || TIFFjpeg_finish_decompress(sp); +} + +/*ARGSUSED*/ static int +DecodeRowError(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s) + +{ + (void) buf; + (void) cc; + (void) s; + + TIFFErrorExt(tif->tif_clientdata, "TIFFReadScanline", + "scanline oriented access is not supported for downsampled JPEG compressed images, consider enabling TIFF_JPEGCOLORMODE as JPEGCOLORMODE_RGB." ); + return 0; +} + +/* + * Decode a chunk of pixels. + * Returned data is downsampled per sampling factors. + */ +/*ARGSUSED*/ static int +JPEGDecodeRaw(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s) +{ + JPEGState *sp = JState(tif); + tmsize_t nrows; + (void) s; + + /* data is expected to be read in multiples of a scanline */ + if ( (nrows = sp->cinfo.d.image_height) ) { + + /* Cb,Cr both have sampling factors 1, so this is correct */ + JDIMENSION clumps_per_line = sp->cinfo.d.comp_info[1].downsampled_width; + int samples_per_clump = sp->samplesperclump; + +#if defined(JPEG_LIB_MK1_OR_12BIT) + unsigned short* tmpbuf = _TIFFmalloc(sizeof(unsigned short) * + sp->cinfo.d.output_width * + sp->cinfo.d.num_components); + if(tmpbuf==NULL) { + TIFFErrorExt(tif->tif_clientdata, "JPEGDecodeRaw", + "Out of memory"); + return 0; + } +#endif + + do { + jpeg_component_info *compptr; + int ci, clumpoffset; + + if( cc < sp->bytesperline ) { + TIFFErrorExt(tif->tif_clientdata, "JPEGDecodeRaw", + "application buffer not large enough for all data."); + return 0; + } + + /* Reload downsampled-data buffer if needed */ + if (sp->scancount >= DCTSIZE) { + int n = sp->cinfo.d.max_v_samp_factor * DCTSIZE; + if (TIFFjpeg_read_raw_data(sp, sp->ds_buffer, n) != n) + return (0); + sp->scancount = 0; + } + /* + * Fastest way to unseparate data is to make one pass + * over the scanline for each row of each component. + */ + clumpoffset = 0; /* first sample in clump */ + for (ci = 0, compptr = sp->cinfo.d.comp_info; + ci < sp->cinfo.d.num_components; + ci++, compptr++) { + int hsamp = compptr->h_samp_factor; + int vsamp = compptr->v_samp_factor; + int ypos; + + for (ypos = 0; ypos < vsamp; ypos++) { + JSAMPLE *inptr = sp->ds_buffer[ci][sp->scancount*vsamp + ypos]; + JDIMENSION nclump; +#if defined(JPEG_LIB_MK1_OR_12BIT) + JSAMPLE *outptr = (JSAMPLE*)tmpbuf + clumpoffset; +#else + JSAMPLE *outptr = (JSAMPLE*)buf + clumpoffset; + if (cc < (tmsize_t) (clumpoffset + samples_per_clump*(clumps_per_line-1) + hsamp)) { + TIFFErrorExt(tif->tif_clientdata, "JPEGDecodeRaw", + "application buffer not large enough for all data, possible subsampling issue"); + return 0; + } +#endif + + if (hsamp == 1) { + /* fast path for at least Cb and Cr */ + for (nclump = clumps_per_line; nclump-- > 0; ) { + outptr[0] = *inptr++; + outptr += samples_per_clump; + } + } else { + int xpos; + + /* general case */ + for (nclump = clumps_per_line; nclump-- > 0; ) { + for (xpos = 0; xpos < hsamp; xpos++) + outptr[xpos] = *inptr++; + outptr += samples_per_clump; + } + } + clumpoffset += hsamp; + } + } + +#if defined(JPEG_LIB_MK1_OR_12BIT) + { + if (sp->cinfo.d.data_precision == 8) + { + int i=0; + int len = sp->cinfo.d.output_width * sp->cinfo.d.num_components; + for (i=0; icinfo.d.output_width + * sp->cinfo.d.num_components) / 2; + int iPair; + for( iPair = 0; iPair < value_pairs; iPair++ ) + { + unsigned char *out_ptr = ((unsigned char *) buf) + iPair * 3; + JSAMPLE *in_ptr = (JSAMPLE *) (tmpbuf + iPair * 2); + out_ptr[0] = (in_ptr[0] & 0xff0) >> 4; + out_ptr[1] = ((in_ptr[0] & 0xf) << 4) + | ((in_ptr[1] & 0xf00) >> 8); + out_ptr[2] = ((in_ptr[1] & 0xff) >> 0); + } + } + } +#endif + + sp->scancount ++; + tif->tif_row += sp->v_sampling; + + buf += sp->bytesperline; + cc -= sp->bytesperline; + + nrows -= sp->v_sampling; + } while (nrows > 0); + +#if defined(JPEG_LIB_MK1_OR_12BIT) + _TIFFfree(tmpbuf); +#endif + + } + + /* Close down the decompressor if done. */ + return sp->cinfo.d.output_scanline < sp->cinfo.d.output_height + || TIFFjpeg_finish_decompress(sp); +} + + +/* + * JPEG Encoding. + */ + +static void +unsuppress_quant_table (JPEGState* sp, int tblno) +{ + JQUANT_TBL* qtbl; + + if ((qtbl = sp->cinfo.c.quant_tbl_ptrs[tblno]) != NULL) + qtbl->sent_table = FALSE; +} + +static void +suppress_quant_table (JPEGState* sp, int tblno) +{ + JQUANT_TBL* qtbl; + + if ((qtbl = sp->cinfo.c.quant_tbl_ptrs[tblno]) != NULL) + qtbl->sent_table = TRUE; +} + +static void +unsuppress_huff_table (JPEGState* sp, int tblno) +{ + JHUFF_TBL* htbl; + + if ((htbl = sp->cinfo.c.dc_huff_tbl_ptrs[tblno]) != NULL) + htbl->sent_table = FALSE; + if ((htbl = sp->cinfo.c.ac_huff_tbl_ptrs[tblno]) != NULL) + htbl->sent_table = FALSE; +} + +static void +suppress_huff_table (JPEGState* sp, int tblno) +{ + JHUFF_TBL* htbl; + + if ((htbl = sp->cinfo.c.dc_huff_tbl_ptrs[tblno]) != NULL) + htbl->sent_table = TRUE; + if ((htbl = sp->cinfo.c.ac_huff_tbl_ptrs[tblno]) != NULL) + htbl->sent_table = TRUE; +} + +static int +prepare_JPEGTables(TIFF* tif) +{ + JPEGState* sp = JState(tif); + + /* Initialize quant tables for current quality setting */ + if (!TIFFjpeg_set_quality(sp, sp->jpegquality, FALSE)) + return (0); + /* Mark only the tables we want for output */ + /* NB: chrominance tables are currently used only with YCbCr */ + if (!TIFFjpeg_suppress_tables(sp, TRUE)) + return (0); + if (sp->jpegtablesmode & JPEGTABLESMODE_QUANT) { + unsuppress_quant_table(sp, 0); + if (sp->photometric == PHOTOMETRIC_YCBCR) + unsuppress_quant_table(sp, 1); + } + if (sp->jpegtablesmode & JPEGTABLESMODE_HUFF) { + unsuppress_huff_table(sp, 0); + if (sp->photometric == PHOTOMETRIC_YCBCR) + unsuppress_huff_table(sp, 1); + } + /* Direct libjpeg output into jpegtables */ + if (!TIFFjpeg_tables_dest(sp, tif)) + return (0); + /* Emit tables-only datastream */ + if (!TIFFjpeg_write_tables(sp)) + return (0); + + return (1); +} + +static int +JPEGSetupEncode(TIFF* tif) +{ + JPEGState* sp = JState(tif); + TIFFDirectory *td = &tif->tif_dir; + static const char module[] = "JPEGSetupEncode"; + +#if defined(JPEG_DUAL_MODE_8_12) && !defined(TIFFInitJPEG) + if( tif->tif_dir.td_bitspersample == 12 ) + return TIFFReInitJPEG_12( tif, COMPRESSION_JPEG, 1 ); +#endif + + JPEGInitializeLibJPEG( tif, FALSE ); + + assert(sp != NULL); + assert(!sp->cinfo.comm.is_decompressor); + + sp->photometric = td->td_photometric; + + /* + * Initialize all JPEG parameters to default values. + * Note that jpeg_set_defaults needs legal values for + * in_color_space and input_components. + */ + if (td->td_planarconfig == PLANARCONFIG_CONTIG) { + sp->cinfo.c.input_components = td->td_samplesperpixel; + if (sp->photometric == PHOTOMETRIC_YCBCR) { + if (sp->jpegcolormode == JPEGCOLORMODE_RGB) { + sp->cinfo.c.in_color_space = JCS_RGB; + } else { + sp->cinfo.c.in_color_space = JCS_YCbCr; + } + } else { + if ((td->td_photometric == PHOTOMETRIC_MINISWHITE || td->td_photometric == PHOTOMETRIC_MINISBLACK) && td->td_samplesperpixel == 1) + sp->cinfo.c.in_color_space = JCS_GRAYSCALE; + else if (td->td_photometric == PHOTOMETRIC_RGB && td->td_samplesperpixel == 3) + sp->cinfo.c.in_color_space = JCS_RGB; + else if (td->td_photometric == PHOTOMETRIC_SEPARATED && td->td_samplesperpixel == 4) + sp->cinfo.c.in_color_space = JCS_CMYK; + else + sp->cinfo.c.in_color_space = JCS_UNKNOWN; + } + } else { + sp->cinfo.c.input_components = 1; + sp->cinfo.c.in_color_space = JCS_UNKNOWN; + } + if (!TIFFjpeg_set_defaults(sp)) + return (0); + /* Set per-file parameters */ + switch (sp->photometric) { + case PHOTOMETRIC_YCBCR: + sp->h_sampling = td->td_ycbcrsubsampling[0]; + sp->v_sampling = td->td_ycbcrsubsampling[1]; + /* + * A ReferenceBlackWhite field *must* be present since the + * default value is inappropriate for YCbCr. Fill in the + * proper value if application didn't set it. + */ + { + float *ref; + if (!TIFFGetField(tif, TIFFTAG_REFERENCEBLACKWHITE, + &ref)) { + float refbw[6]; + long top = 1L << td->td_bitspersample; + refbw[0] = 0; + refbw[1] = (float)(top-1L); + refbw[2] = (float)(top>>1); + refbw[3] = refbw[1]; + refbw[4] = refbw[2]; + refbw[5] = refbw[1]; + TIFFSetField(tif, TIFFTAG_REFERENCEBLACKWHITE, + refbw); + } + } + break; + case PHOTOMETRIC_PALETTE: /* disallowed by Tech Note */ + case PHOTOMETRIC_MASK: + TIFFErrorExt(tif->tif_clientdata, module, + "PhotometricInterpretation %d not allowed for JPEG", + (int) sp->photometric); + return (0); + default: + /* TIFF 6.0 forbids subsampling of all other color spaces */ + sp->h_sampling = 1; + sp->v_sampling = 1; + break; + } + + /* Verify miscellaneous parameters */ + + /* + * This would need work if libtiff ever supports different + * depths for different components, or if libjpeg ever supports + * run-time selection of depth. Neither is imminent. + */ +#ifdef JPEG_LIB_MK1 + /* BITS_IN_JSAMPLE now permits 8 and 12 --- dgilbert */ + if (td->td_bitspersample != 8 && td->td_bitspersample != 12) +#else + if (td->td_bitspersample != BITS_IN_JSAMPLE ) +#endif + { + TIFFErrorExt(tif->tif_clientdata, module, "BitsPerSample %d not allowed for JPEG", + (int) td->td_bitspersample); + return (0); + } + sp->cinfo.c.data_precision = td->td_bitspersample; +#ifdef JPEG_LIB_MK1 + sp->cinfo.c.bits_in_jsample = td->td_bitspersample; +#endif + if (isTiled(tif)) { + if ((td->td_tilelength % (sp->v_sampling * DCTSIZE)) != 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "JPEG tile height must be multiple of %d", + sp->v_sampling * DCTSIZE); + return (0); + } + if ((td->td_tilewidth % (sp->h_sampling * DCTSIZE)) != 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "JPEG tile width must be multiple of %d", + sp->h_sampling * DCTSIZE); + return (0); + } + } else { + if (td->td_rowsperstrip < td->td_imagelength && + (td->td_rowsperstrip % (sp->v_sampling * DCTSIZE)) != 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "RowsPerStrip must be multiple of %d for JPEG", + sp->v_sampling * DCTSIZE); + return (0); + } + } + + /* Create a JPEGTables field if appropriate */ + if (sp->jpegtablesmode & (JPEGTABLESMODE_QUANT|JPEGTABLESMODE_HUFF)) { + if( sp->jpegtables == NULL + || memcmp(sp->jpegtables,"\0\0\0\0\0\0\0\0\0",8) == 0 ) + { + if (!prepare_JPEGTables(tif)) + return (0); + /* Mark the field present */ + /* Can't use TIFFSetField since BEENWRITING is already set! */ + tif->tif_flags |= TIFF_DIRTYDIRECT; + TIFFSetFieldBit(tif, FIELD_JPEGTABLES); + } + } else { + /* We do not support application-supplied JPEGTables, */ + /* so mark the field not present */ + TIFFClrFieldBit(tif, FIELD_JPEGTABLES); + } + + /* Direct libjpeg output to libtiff's output buffer */ + TIFFjpeg_data_dest(sp, tif); + + return (1); +} + +/* + * Set encoding state at the start of a strip or tile. + */ +static int +JPEGPreEncode(TIFF* tif, uint16 s) +{ + JPEGState *sp = JState(tif); + TIFFDirectory *td = &tif->tif_dir; + static const char module[] = "JPEGPreEncode"; + uint32 segment_width, segment_height; + int downsampled_input; + + assert(sp != NULL); + + if (sp->cinfo.comm.is_decompressor == 1) + { + tif->tif_setupencode( tif ); + } + + assert(!sp->cinfo.comm.is_decompressor); + /* + * Set encoding parameters for this strip/tile. + */ + if (isTiled(tif)) { + segment_width = td->td_tilewidth; + segment_height = td->td_tilelength; + sp->bytesperline = TIFFTileRowSize(tif); + } else { + segment_width = td->td_imagewidth; + segment_height = td->td_imagelength - tif->tif_row; + if (segment_height > td->td_rowsperstrip) + segment_height = td->td_rowsperstrip; + sp->bytesperline = TIFFScanlineSize(tif); + } + if (td->td_planarconfig == PLANARCONFIG_SEPARATE && s > 0) { + /* for PC 2, scale down the strip/tile size + * to match a downsampled component + */ + segment_width = TIFFhowmany_32(segment_width, sp->h_sampling); + segment_height = TIFFhowmany_32(segment_height, sp->v_sampling); + } + if (segment_width > 65535 || segment_height > 65535) { + TIFFErrorExt(tif->tif_clientdata, module, "Strip/tile too large for JPEG"); + return (0); + } + sp->cinfo.c.image_width = segment_width; + sp->cinfo.c.image_height = segment_height; + downsampled_input = FALSE; + if (td->td_planarconfig == PLANARCONFIG_CONTIG) { + sp->cinfo.c.input_components = td->td_samplesperpixel; + if (sp->photometric == PHOTOMETRIC_YCBCR) { + if (sp->jpegcolormode != JPEGCOLORMODE_RGB) { + if (sp->h_sampling != 1 || sp->v_sampling != 1) + downsampled_input = TRUE; + } + if (!TIFFjpeg_set_colorspace(sp, JCS_YCbCr)) + return (0); + /* + * Set Y sampling factors; + * we assume jpeg_set_colorspace() set the rest to 1 + */ + sp->cinfo.c.comp_info[0].h_samp_factor = sp->h_sampling; + sp->cinfo.c.comp_info[0].v_samp_factor = sp->v_sampling; + } else { + if (!TIFFjpeg_set_colorspace(sp, sp->cinfo.c.in_color_space)) + return (0); + /* jpeg_set_colorspace set all sampling factors to 1 */ + } + } else { + if (!TIFFjpeg_set_colorspace(sp, JCS_UNKNOWN)) + return (0); + sp->cinfo.c.comp_info[0].component_id = s; + /* jpeg_set_colorspace() set sampling factors to 1 */ + if (sp->photometric == PHOTOMETRIC_YCBCR && s > 0) { + sp->cinfo.c.comp_info[0].quant_tbl_no = 1; + sp->cinfo.c.comp_info[0].dc_tbl_no = 1; + sp->cinfo.c.comp_info[0].ac_tbl_no = 1; + } + } + /* ensure libjpeg won't write any extraneous markers */ + sp->cinfo.c.write_JFIF_header = FALSE; + sp->cinfo.c.write_Adobe_marker = FALSE; + /* set up table handling correctly */ + /* calling TIFFjpeg_set_quality() causes quantization tables to be flagged */ + /* as being to be emitted, which we don't want in the JPEGTABLESMODE_QUANT */ + /* mode, so we must manually suppress them. However TIFFjpeg_set_quality() */ + /* should really be called when dealing with files with directories with */ + /* mixed qualities. see http://trac.osgeo.org/gdal/ticket/3539 */ + if (!TIFFjpeg_set_quality(sp, sp->jpegquality, FALSE)) + return (0); + if (sp->jpegtablesmode & JPEGTABLESMODE_QUANT) { + suppress_quant_table(sp, 0); + suppress_quant_table(sp, 1); + } + else { + unsuppress_quant_table(sp, 0); + unsuppress_quant_table(sp, 1); + } + if (sp->jpegtablesmode & JPEGTABLESMODE_HUFF) + { + /* Explicit suppression is only needed if we did not go through the */ + /* prepare_JPEGTables() code path, which may be the case if updating */ + /* an existing file */ + suppress_huff_table(sp, 0); + suppress_huff_table(sp, 1); + sp->cinfo.c.optimize_coding = FALSE; + } + else + sp->cinfo.c.optimize_coding = TRUE; + if (downsampled_input) { + /* Need to use raw-data interface to libjpeg */ + sp->cinfo.c.raw_data_in = TRUE; + tif->tif_encoderow = JPEGEncodeRaw; + tif->tif_encodestrip = JPEGEncodeRaw; + tif->tif_encodetile = JPEGEncodeRaw; + } else { + /* Use normal interface to libjpeg */ + sp->cinfo.c.raw_data_in = FALSE; + tif->tif_encoderow = JPEGEncode; + tif->tif_encodestrip = JPEGEncode; + tif->tif_encodetile = JPEGEncode; + } + /* Start JPEG compressor */ + if (!TIFFjpeg_start_compress(sp, FALSE)) + return (0); + /* Allocate downsampled-data buffers if needed */ + if (downsampled_input) { + if (!alloc_downsampled_buffers(tif, sp->cinfo.c.comp_info, + sp->cinfo.c.num_components)) + return (0); + } + sp->scancount = 0; + + return (1); +} + +/* + * Encode a chunk of pixels. + * "Standard" case: incoming data is not downsampled. + */ +static int +JPEGEncode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s) +{ + JPEGState *sp = JState(tif); + tmsize_t nrows; + JSAMPROW bufptr[1]; + short *line16 = NULL; + int line16_count = 0; + + (void) s; + assert(sp != NULL); + /* data is expected to be supplied in multiples of a scanline */ + nrows = cc / sp->bytesperline; + if (cc % sp->bytesperline) + TIFFWarningExt(tif->tif_clientdata, tif->tif_name, + "fractional scanline discarded"); + + /* The last strip will be limited to image size */ + if( !isTiled(tif) && tif->tif_row+nrows > tif->tif_dir.td_imagelength ) + nrows = tif->tif_dir.td_imagelength - tif->tif_row; + + if( sp->cinfo.c.data_precision == 12 ) + { + line16_count = (sp->bytesperline * 2) / 3; + line16 = (short *) _TIFFmalloc(sizeof(short) * line16_count); + // FIXME: undiagnosed malloc failure + } + + while (nrows-- > 0) { + + if( sp->cinfo.c.data_precision == 12 ) + { + + int value_pairs = line16_count / 2; + int iPair; + + bufptr[0] = (JSAMPROW) line16; + + for( iPair = 0; iPair < value_pairs; iPair++ ) + { + unsigned char *in_ptr = + ((unsigned char *) buf) + iPair * 3; + JSAMPLE *out_ptr = (JSAMPLE *) (line16 + iPair * 2); + + out_ptr[0] = (in_ptr[0] << 4) | ((in_ptr[1] & 0xf0) >> 4); + out_ptr[1] = ((in_ptr[1] & 0x0f) << 8) | in_ptr[2]; + } + } + else + { + bufptr[0] = (JSAMPROW) buf; + } + if (TIFFjpeg_write_scanlines(sp, bufptr, 1) != 1) + return (0); + if (nrows > 0) + tif->tif_row++; + buf += sp->bytesperline; + } + + if( sp->cinfo.c.data_precision == 12 ) + { + _TIFFfree( line16 ); + } + + return (1); +} + +/* + * Encode a chunk of pixels. + * Incoming data is expected to be downsampled per sampling factors. + */ +static int +JPEGEncodeRaw(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s) +{ + JPEGState *sp = JState(tif); + JSAMPLE* inptr; + JSAMPLE* outptr; + tmsize_t nrows; + JDIMENSION clumps_per_line, nclump; + int clumpoffset, ci, xpos, ypos; + jpeg_component_info* compptr; + int samples_per_clump = sp->samplesperclump; + tmsize_t bytesperclumpline; + + (void) s; + assert(sp != NULL); + /* data is expected to be supplied in multiples of a clumpline */ + /* a clumpline is equivalent to v_sampling desubsampled scanlines */ + /* TODO: the following calculation of bytesperclumpline, should substitute calculation of sp->bytesperline, except that it is per v_sampling lines */ + bytesperclumpline = (((sp->cinfo.c.image_width+sp->h_sampling-1)/sp->h_sampling) + *(sp->h_sampling*sp->v_sampling+2)*sp->cinfo.c.data_precision+7) + /8; + + nrows = ( cc / bytesperclumpline ) * sp->v_sampling; + if (cc % bytesperclumpline) + TIFFWarningExt(tif->tif_clientdata, tif->tif_name, "fractional scanline discarded"); + + /* Cb,Cr both have sampling factors 1, so this is correct */ + clumps_per_line = sp->cinfo.c.comp_info[1].downsampled_width; + + while (nrows > 0) { + /* + * Fastest way to separate the data is to make one pass + * over the scanline for each row of each component. + */ + clumpoffset = 0; /* first sample in clump */ + for (ci = 0, compptr = sp->cinfo.c.comp_info; + ci < sp->cinfo.c.num_components; + ci++, compptr++) { + int hsamp = compptr->h_samp_factor; + int vsamp = compptr->v_samp_factor; + int padding = (int) (compptr->width_in_blocks * DCTSIZE - + clumps_per_line * hsamp); + for (ypos = 0; ypos < vsamp; ypos++) { + inptr = ((JSAMPLE*) buf) + clumpoffset; + outptr = sp->ds_buffer[ci][sp->scancount*vsamp + ypos]; + if (hsamp == 1) { + /* fast path for at least Cb and Cr */ + for (nclump = clumps_per_line; nclump-- > 0; ) { + *outptr++ = inptr[0]; + inptr += samples_per_clump; + } + } else { + /* general case */ + for (nclump = clumps_per_line; nclump-- > 0; ) { + for (xpos = 0; xpos < hsamp; xpos++) + *outptr++ = inptr[xpos]; + inptr += samples_per_clump; + } + } + /* pad each scanline as needed */ + for (xpos = 0; xpos < padding; xpos++) { + *outptr = outptr[-1]; + outptr++; + } + clumpoffset += hsamp; + } + } + sp->scancount++; + if (sp->scancount >= DCTSIZE) { + int n = sp->cinfo.c.max_v_samp_factor * DCTSIZE; + if (TIFFjpeg_write_raw_data(sp, sp->ds_buffer, n) != n) + return (0); + sp->scancount = 0; + } + tif->tif_row += sp->v_sampling; + buf += bytesperclumpline; + nrows -= sp->v_sampling; + } + return (1); +} + +/* + * Finish up at the end of a strip or tile. + */ +static int +JPEGPostEncode(TIFF* tif) +{ + JPEGState *sp = JState(tif); + + if (sp->scancount > 0) { + /* + * Need to emit a partial bufferload of downsampled data. + * Pad the data vertically. + */ + int ci, ypos, n; + jpeg_component_info* compptr; + + for (ci = 0, compptr = sp->cinfo.c.comp_info; + ci < sp->cinfo.c.num_components; + ci++, compptr++) { + int vsamp = compptr->v_samp_factor; + tmsize_t row_width = compptr->width_in_blocks * DCTSIZE + * sizeof(JSAMPLE); + for (ypos = sp->scancount * vsamp; + ypos < DCTSIZE * vsamp; ypos++) { + _TIFFmemcpy((void*)sp->ds_buffer[ci][ypos], + (void*)sp->ds_buffer[ci][ypos-1], + row_width); + + } + } + n = sp->cinfo.c.max_v_samp_factor * DCTSIZE; + if (TIFFjpeg_write_raw_data(sp, sp->ds_buffer, n) != n) + return (0); + } + + return (TIFFjpeg_finish_compress(JState(tif))); +} + +static void +JPEGCleanup(TIFF* tif) +{ + JPEGState *sp = JState(tif); + + assert(sp != 0); + + tif->tif_tagmethods.vgetfield = sp->vgetparent; + tif->tif_tagmethods.vsetfield = sp->vsetparent; + tif->tif_tagmethods.printdir = sp->printdir; + + if( sp != NULL ) { + if( sp->cinfo_initialized ) + TIFFjpeg_destroy(sp); /* release libjpeg resources */ + if (sp->jpegtables) /* tag value */ + _TIFFfree(sp->jpegtables); + } + _TIFFfree(tif->tif_data); /* release local state */ + tif->tif_data = NULL; + + _TIFFSetDefaultCompressionState(tif); +} + +static void +JPEGResetUpsampled( TIFF* tif ) +{ + JPEGState* sp = JState(tif); + TIFFDirectory* td = &tif->tif_dir; + + /* + * Mark whether returned data is up-sampled or not so TIFFStripSize + * and TIFFTileSize return values that reflect the true amount of + * data. + */ + tif->tif_flags &= ~TIFF_UPSAMPLED; + if (td->td_planarconfig == PLANARCONFIG_CONTIG) { + if (td->td_photometric == PHOTOMETRIC_YCBCR && + sp->jpegcolormode == JPEGCOLORMODE_RGB) { + tif->tif_flags |= TIFF_UPSAMPLED; + } else { +#ifdef notdef + if (td->td_ycbcrsubsampling[0] != 1 || + td->td_ycbcrsubsampling[1] != 1) + ; /* XXX what about up-sampling? */ +#endif + } + } + + /* + * Must recalculate cached tile size in case sampling state changed. + * Should we really be doing this now if image size isn't set? + */ + if( tif->tif_tilesize > 0 ) + tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tmsize_t)(-1); + if( tif->tif_scanlinesize > 0 ) + tif->tif_scanlinesize = TIFFScanlineSize(tif); +} + +static int +JPEGVSetField(TIFF* tif, uint32 tag, va_list ap) +{ + JPEGState* sp = JState(tif); + const TIFFField* fip; + uint32 v32; + + assert(sp != NULL); + + switch (tag) { + case TIFFTAG_JPEGTABLES: + v32 = (uint32) va_arg(ap, uint32); + if (v32 == 0) { + /* XXX */ + return (0); + } + _TIFFsetByteArray(&sp->jpegtables, va_arg(ap, void*), + (long) v32); + sp->jpegtables_length = v32; + TIFFSetFieldBit(tif, FIELD_JPEGTABLES); + break; + case TIFFTAG_JPEGQUALITY: + sp->jpegquality = (int) va_arg(ap, int); + return (1); /* pseudo tag */ + case TIFFTAG_JPEGCOLORMODE: + sp->jpegcolormode = (int) va_arg(ap, int); + JPEGResetUpsampled( tif ); + return (1); /* pseudo tag */ + case TIFFTAG_PHOTOMETRIC: + { + int ret_value = (*sp->vsetparent)(tif, tag, ap); + JPEGResetUpsampled( tif ); + return ret_value; + } + case TIFFTAG_JPEGTABLESMODE: + sp->jpegtablesmode = (int) va_arg(ap, int); + return (1); /* pseudo tag */ + case TIFFTAG_YCBCRSUBSAMPLING: + /* mark the fact that we have a real ycbcrsubsampling! */ + sp->ycbcrsampling_fetched = 1; + /* should we be recomputing upsampling info here? */ + return (*sp->vsetparent)(tif, tag, ap); + default: + return (*sp->vsetparent)(tif, tag, ap); + } + + if ((fip = TIFFFieldWithTag(tif, tag))) { + TIFFSetFieldBit(tif, fip->field_bit); + } else { + return (0); + } + + tif->tif_flags |= TIFF_DIRTYDIRECT; + return (1); +} + +static int +JPEGVGetField(TIFF* tif, uint32 tag, va_list ap) +{ + JPEGState* sp = JState(tif); + + assert(sp != NULL); + + switch (tag) { + case TIFFTAG_JPEGTABLES: + *va_arg(ap, uint32*) = sp->jpegtables_length; + *va_arg(ap, void**) = sp->jpegtables; + break; + case TIFFTAG_JPEGQUALITY: + *va_arg(ap, int*) = sp->jpegquality; + break; + case TIFFTAG_JPEGCOLORMODE: + *va_arg(ap, int*) = sp->jpegcolormode; + break; + case TIFFTAG_JPEGTABLESMODE: + *va_arg(ap, int*) = sp->jpegtablesmode; + break; + default: + return (*sp->vgetparent)(tif, tag, ap); + } + return (1); +} + +static void +JPEGPrintDir(TIFF* tif, FILE* fd, long flags) +{ + JPEGState* sp = JState(tif); + + assert(sp != NULL); + (void) flags; + + if( sp != NULL ) { + if (TIFFFieldSet(tif,FIELD_JPEGTABLES)) + fprintf(fd, " JPEG Tables: (%lu bytes)\n", + (unsigned long) sp->jpegtables_length); + if (sp->printdir) + (*sp->printdir)(tif, fd, flags); + } +} + +static uint32 +JPEGDefaultStripSize(TIFF* tif, uint32 s) +{ + JPEGState* sp = JState(tif); + TIFFDirectory *td = &tif->tif_dir; + + s = (*sp->defsparent)(tif, s); + if (s < td->td_imagelength) + s = TIFFroundup_32(s, td->td_ycbcrsubsampling[1] * DCTSIZE); + return (s); +} + +static void +JPEGDefaultTileSize(TIFF* tif, uint32* tw, uint32* th) +{ + JPEGState* sp = JState(tif); + TIFFDirectory *td = &tif->tif_dir; + + (*sp->deftparent)(tif, tw, th); + *tw = TIFFroundup_32(*tw, td->td_ycbcrsubsampling[0] * DCTSIZE); + *th = TIFFroundup_32(*th, td->td_ycbcrsubsampling[1] * DCTSIZE); +} + +/* + * The JPEG library initialized used to be done in TIFFInitJPEG(), but + * now that we allow a TIFF file to be opened in update mode it is necessary + * to have some way of deciding whether compression or decompression is + * desired other than looking at tif->tif_mode. We accomplish this by + * examining {TILE/STRIP}BYTECOUNTS to see if there is a non-zero entry. + * If so, we assume decompression is desired. + * + * This is tricky, because TIFFInitJPEG() is called while the directory is + * being read, and generally speaking the BYTECOUNTS tag won't have been read + * at that point. So we try to defer jpeg library initialization till we + * do have that tag ... basically any access that might require the compressor + * or decompressor that occurs after the reading of the directory. + * + * In an ideal world compressors or decompressors would be setup + * at the point where a single tile or strip was accessed (for read or write) + * so that stuff like update of missing tiles, or replacement of tiles could + * be done. However, we aren't trying to crack that nut just yet ... + * + * NFW, Feb 3rd, 2003. + */ + +static int JPEGInitializeLibJPEG( TIFF * tif, int decompress ) +{ + JPEGState* sp = JState(tif); + + if(sp->cinfo_initialized) + { + if( !decompress && sp->cinfo.comm.is_decompressor ) + TIFFjpeg_destroy( sp ); + else if( decompress && !sp->cinfo.comm.is_decompressor ) + TIFFjpeg_destroy( sp ); + else + return 1; + + sp->cinfo_initialized = 0; + } + + /* + * Initialize libjpeg. + */ + if ( decompress ) { + if (!TIFFjpeg_create_decompress(sp)) + return (0); + } else { + if (!TIFFjpeg_create_compress(sp)) + return (0); + } + + sp->cinfo_initialized = TRUE; + + return 1; +} + +int +TIFFInitJPEG(TIFF* tif, int scheme) +{ + JPEGState* sp; + + assert(scheme == COMPRESSION_JPEG); + + /* + * Merge codec-specific tag information. + */ + if (!_TIFFMergeFields(tif, jpegFields, TIFFArrayCount(jpegFields))) { + TIFFErrorExt(tif->tif_clientdata, + "TIFFInitJPEG", + "Merging JPEG codec-specific tags failed"); + return 0; + } + + /* + * Allocate state block so tag methods have storage to record values. + */ + tif->tif_data = (uint8*) _TIFFmalloc(sizeof (JPEGState)); + + if (tif->tif_data == NULL) { + TIFFErrorExt(tif->tif_clientdata, + "TIFFInitJPEG", "No space for JPEG state block"); + return 0; + } + _TIFFmemset(tif->tif_data, 0, sizeof(JPEGState)); + + sp = JState(tif); + sp->tif = tif; /* back link */ + + /* + * Override parent get/set field methods. + */ + sp->vgetparent = tif->tif_tagmethods.vgetfield; + tif->tif_tagmethods.vgetfield = JPEGVGetField; /* hook for codec tags */ + sp->vsetparent = tif->tif_tagmethods.vsetfield; + tif->tif_tagmethods.vsetfield = JPEGVSetField; /* hook for codec tags */ + sp->printdir = tif->tif_tagmethods.printdir; + tif->tif_tagmethods.printdir = JPEGPrintDir; /* hook for codec tags */ + + /* Default values for codec-specific fields */ + sp->jpegtables = NULL; + sp->jpegtables_length = 0; + sp->jpegquality = 75; /* Default IJG quality */ + sp->jpegcolormode = JPEGCOLORMODE_RAW; + sp->jpegtablesmode = JPEGTABLESMODE_QUANT | JPEGTABLESMODE_HUFF; + sp->ycbcrsampling_fetched = 0; + + /* + * Install codec methods. + */ + tif->tif_fixuptags = JPEGFixupTags; + tif->tif_setupdecode = JPEGSetupDecode; + tif->tif_predecode = JPEGPreDecode; + tif->tif_decoderow = JPEGDecode; + tif->tif_decodestrip = JPEGDecode; + tif->tif_decodetile = JPEGDecode; + tif->tif_setupencode = JPEGSetupEncode; + tif->tif_preencode = JPEGPreEncode; + tif->tif_postencode = JPEGPostEncode; + tif->tif_encoderow = JPEGEncode; + tif->tif_encodestrip = JPEGEncode; + tif->tif_encodetile = JPEGEncode; + tif->tif_cleanup = JPEGCleanup; + sp->defsparent = tif->tif_defstripsize; + tif->tif_defstripsize = JPEGDefaultStripSize; + sp->deftparent = tif->tif_deftilesize; + tif->tif_deftilesize = JPEGDefaultTileSize; + tif->tif_flags |= TIFF_NOBITREV; /* no bit reversal, please */ + + sp->cinfo_initialized = FALSE; + + /* + ** Create a JPEGTables field if no directory has yet been created. + ** We do this just to ensure that sufficient space is reserved for + ** the JPEGTables field. It will be properly created the right + ** size later. + */ + if( tif->tif_diroff == 0 ) + { +#define SIZE_OF_JPEGTABLES 2000 +/* +The following line assumes incorrectly that all JPEG-in-TIFF files will have +a JPEGTABLES tag generated and causes null-filled JPEGTABLES tags to be written +when the JPEG data is placed with TIFFWriteRawStrip. The field bit should be +set, anyway, later when actual JPEGTABLES header is generated, so removing it +here hopefully is harmless. + TIFFSetFieldBit(tif, FIELD_JPEGTABLES); +*/ + sp->jpegtables_length = SIZE_OF_JPEGTABLES; + sp->jpegtables = (void *) _TIFFmalloc(sp->jpegtables_length); + // FIXME: NULL-deref after malloc failure + _TIFFmemset(sp->jpegtables, 0, SIZE_OF_JPEGTABLES); +#undef SIZE_OF_JPEGTABLES + } + + return 1; +} +#endif /* JPEG_SUPPORT */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_jpeg_12.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_jpeg_12.c new file mode 100644 index 000000000..87aaa19e0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_jpeg_12.c @@ -0,0 +1,65 @@ + +#include "tiffiop.h" + +#if defined(JPEG_DUAL_MODE_8_12) + +# define TIFFInitJPEG TIFFInitJPEG_12 + +# include LIBJPEG_12_PATH + +# include "tif_jpeg.c" + +int TIFFReInitJPEG_12( TIFF *tif, int scheme, int is_encode ) + +{ + JPEGState* sp; + + assert(scheme == COMPRESSION_JPEG); + + sp = JState(tif); + sp->tif = tif; /* back link */ + + /* + * Override parent get/set field methods. + */ + tif->tif_tagmethods.vgetfield = JPEGVGetField; /* hook for codec tags */ + tif->tif_tagmethods.vsetfield = JPEGVSetField; /* hook for codec tags */ + tif->tif_tagmethods.printdir = JPEGPrintDir; /* hook for codec tags */ + + /* + * Install codec methods. + */ + tif->tif_fixuptags = JPEGFixupTags; + tif->tif_setupdecode = JPEGSetupDecode; + tif->tif_predecode = JPEGPreDecode; + tif->tif_decoderow = JPEGDecode; + tif->tif_decodestrip = JPEGDecode; + tif->tif_decodetile = JPEGDecode; + tif->tif_setupencode = JPEGSetupEncode; + tif->tif_preencode = JPEGPreEncode; + tif->tif_postencode = JPEGPostEncode; + tif->tif_encoderow = JPEGEncode; + tif->tif_encodestrip = JPEGEncode; + tif->tif_encodetile = JPEGEncode; + tif->tif_cleanup = JPEGCleanup; + tif->tif_defstripsize = JPEGDefaultStripSize; + tif->tif_deftilesize = JPEGDefaultTileSize; + tif->tif_flags |= TIFF_NOBITREV; /* no bit reversal, please */ + + sp->cinfo_initialized = FALSE; + + if( is_encode ) + return JPEGSetupEncode(tif); + else + return JPEGSetupDecode(tif); +} + +#endif /* defined(JPEG_DUAL_MODE_8_12) */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_luv.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_luv.c new file mode 100644 index 000000000..eba6c08eb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_luv.c @@ -0,0 +1,1683 @@ +/* $Id: tif_luv.c,v 1.35 2011-04-02 20:54:09 bfriesen Exp $ */ + +/* + * Copyright (c) 1997 Greg Ward Larson + * Copyright (c) 1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler, Greg Larson and Silicon Graphics may not be used in any + * advertising or publicity relating to the software without the specific, + * prior written permission of Sam Leffler, Greg Larson and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER, GREG LARSON OR SILICON GRAPHICS BE LIABLE + * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "tiffiop.h" +#ifdef LOGLUV_SUPPORT + +/* + * TIFF Library. + * LogLuv compression support for high dynamic range images. + * + * Contributed by Greg Larson. + * + * LogLuv image support uses the TIFF library to store 16 or 10-bit + * log luminance values with 8 bits each of u and v or a 14-bit index. + * + * The codec can take as input and produce as output 32-bit IEEE float values + * as well as 16-bit integer values. A 16-bit luminance is interpreted + * as a sign bit followed by a 15-bit integer that is converted + * to and from a linear magnitude using the transformation: + * + * L = 2^( (Le+.5)/256 - 64 ) # real from 15-bit + * + * Le = floor( 256*(log2(L) + 64) ) # 15-bit from real + * + * The actual conversion to world luminance units in candelas per sq. meter + * requires an additional multiplier, which is stored in the TIFFTAG_STONITS. + * This value is usually set such that a reasonable exposure comes from + * clamping decoded luminances above 1 to 1 in the displayed image. + * + * The 16-bit values for u and v may be converted to real values by dividing + * each by 32768. (This allows for negative values, which aren't useful as + * far as we know, but are left in case of future improvements in human + * color vision.) + * + * Conversion from (u,v), which is actually the CIE (u',v') system for + * you color scientists, is accomplished by the following transformation: + * + * u = 4*x / (-2*x + 12*y + 3) + * v = 9*y / (-2*x + 12*y + 3) + * + * x = 9*u / (6*u - 16*v + 12) + * y = 4*v / (6*u - 16*v + 12) + * + * This process is greatly simplified by passing 32-bit IEEE floats + * for each of three CIE XYZ coordinates. The codec then takes care + * of conversion to and from LogLuv, though the application is still + * responsible for interpreting the TIFFTAG_STONITS calibration factor. + * + * By definition, a CIE XYZ vector of [1 1 1] corresponds to a neutral white + * point of (x,y)=(1/3,1/3). However, most color systems assume some other + * white point, such as D65, and an absolute color conversion to XYZ then + * to another color space with a different white point may introduce an + * unwanted color cast to the image. It is often desirable, therefore, to + * perform a white point conversion that maps the input white to [1 1 1] + * in XYZ, then record the original white point using the TIFFTAG_WHITEPOINT + * tag value. A decoder that demands absolute color calibration may use + * this white point tag to get back the original colors, but usually it + * will be ignored and the new white point will be used instead that + * matches the output color space. + * + * Pixel information is compressed into one of two basic encodings, depending + * on the setting of the compression tag, which is one of COMPRESSION_SGILOG + * or COMPRESSION_SGILOG24. For COMPRESSION_SGILOG, greyscale data is + * stored as: + * + * 1 15 + * |-+---------------| + * + * COMPRESSION_SGILOG color data is stored as: + * + * 1 15 8 8 + * |-+---------------|--------+--------| + * S Le ue ve + * + * For the 24-bit COMPRESSION_SGILOG24 color format, the data is stored as: + * + * 10 14 + * |----------|--------------| + * Le' Ce + * + * There is no sign bit in the 24-bit case, and the (u,v) chromaticity is + * encoded as an index for optimal color resolution. The 10 log bits are + * defined by the following conversions: + * + * L = 2^((Le'+.5)/64 - 12) # real from 10-bit + * + * Le' = floor( 64*(log2(L) + 12) ) # 10-bit from real + * + * The 10 bits of the smaller format may be converted into the 15 bits of + * the larger format by multiplying by 4 and adding 13314. Obviously, + * a smaller range of magnitudes is covered (about 5 orders of magnitude + * instead of 38), and the lack of a sign bit means that negative luminances + * are not allowed. (Well, they aren't allowed in the real world, either, + * but they are useful for certain types of image processing.) + * + * The desired user format is controlled by the setting the internal + * pseudo tag TIFFTAG_SGILOGDATAFMT to one of: + * SGILOGDATAFMT_FLOAT = IEEE 32-bit float XYZ values + * SGILOGDATAFMT_16BIT = 16-bit integer encodings of logL, u and v + * Raw data i/o is also possible using: + * SGILOGDATAFMT_RAW = 32-bit unsigned integer with encoded pixel + * In addition, the following decoding is provided for ease of display: + * SGILOGDATAFMT_8BIT = 8-bit default RGB gamma-corrected values + * + * For grayscale images, we provide the following data formats: + * SGILOGDATAFMT_FLOAT = IEEE 32-bit float Y values + * SGILOGDATAFMT_16BIT = 16-bit integer w/ encoded luminance + * SGILOGDATAFMT_8BIT = 8-bit gray monitor values + * + * Note that the COMPRESSION_SGILOG applies a simple run-length encoding + * scheme by separating the logL, u and v bytes for each row and applying + * a PackBits type of compression. Since the 24-bit encoding is not + * adaptive, the 32-bit color format takes less space in many cases. + * + * Further control is provided over the conversion from higher-resolution + * formats to final encoded values through the pseudo tag + * TIFFTAG_SGILOGENCODE: + * SGILOGENCODE_NODITHER = do not dither encoded values + * SGILOGENCODE_RANDITHER = apply random dithering during encoding + * + * The default value of this tag is SGILOGENCODE_NODITHER for + * COMPRESSION_SGILOG to maximize run-length encoding and + * SGILOGENCODE_RANDITHER for COMPRESSION_SGILOG24 to turn + * quantization errors into noise. + */ + +#include +#include +#include + +/* + * State block for each open TIFF + * file using LogLuv compression/decompression. + */ +typedef struct logLuvState LogLuvState; + +struct logLuvState { + int user_datafmt; /* user data format */ + int encode_meth; /* encoding method */ + int pixel_size; /* bytes per pixel */ + + uint8* tbuf; /* translation buffer */ + tmsize_t tbuflen; /* buffer length */ + void (*tfunc)(LogLuvState*, uint8*, tmsize_t); + + TIFFVSetMethod vgetparent; /* super-class method */ + TIFFVSetMethod vsetparent; /* super-class method */ +}; + +#define DecoderState(tif) ((LogLuvState*) (tif)->tif_data) +#define EncoderState(tif) ((LogLuvState*) (tif)->tif_data) + +#define SGILOGDATAFMT_UNKNOWN -1 + +#define MINRUN 4 /* minimum run length */ + +/* + * Decode a string of 16-bit gray pixels. + */ +static int +LogL16Decode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s) +{ + static const char module[] = "LogL16Decode"; + LogLuvState* sp = DecoderState(tif); + int shft; + tmsize_t i; + tmsize_t npixels; + unsigned char* bp; + int16* tp; + int16 b; + tmsize_t cc; + int rc; + + assert(s == 0); + assert(sp != NULL); + + npixels = occ / sp->pixel_size; + + if (sp->user_datafmt == SGILOGDATAFMT_16BIT) + tp = (int16*) op; + else { + assert(sp->tbuflen >= npixels); + tp = (int16*) sp->tbuf; + } + _TIFFmemset((void*) tp, 0, npixels*sizeof (tp[0])); + + bp = (unsigned char*) tif->tif_rawcp; + cc = tif->tif_rawcc; + /* get each byte string */ + for (shft = 2*8; (shft -= 8) >= 0; ) { + for (i = 0; i < npixels && cc > 0; ) + if (*bp >= 128) { /* run */ + rc = *bp++ + (2-128); /* TODO: potential input buffer overrun when decoding corrupt or truncated data */ + b = (int16)(*bp++ << shft); + cc -= 2; + while (rc-- && i < npixels) + tp[i++] |= b; + } else { /* non-run */ + rc = *bp++; /* nul is noop */ + while (--cc && rc-- && i < npixels) + tp[i++] |= (int16)*bp++ << shft; + } + if (i != npixels) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at row %lu (short %I64d pixels)", + (unsigned long) tif->tif_row, + (unsigned __int64) (npixels - i)); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at row %lu (short %llu pixels)", + (unsigned long) tif->tif_row, + (unsigned long long) (npixels - i)); +#endif + tif->tif_rawcp = (uint8*) bp; + tif->tif_rawcc = cc; + return (0); + } + } + (*sp->tfunc)(sp, op, npixels); + tif->tif_rawcp = (uint8*) bp; + tif->tif_rawcc = cc; + return (1); +} + +/* + * Decode a string of 24-bit pixels. + */ +static int +LogLuvDecode24(TIFF* tif, uint8* op, tmsize_t occ, uint16 s) +{ + static const char module[] = "LogLuvDecode24"; + LogLuvState* sp = DecoderState(tif); + tmsize_t cc; + tmsize_t i; + tmsize_t npixels; + unsigned char* bp; + uint32* tp; + + assert(s == 0); + assert(sp != NULL); + + npixels = occ / sp->pixel_size; + + if (sp->user_datafmt == SGILOGDATAFMT_RAW) + tp = (uint32 *)op; + else { + assert(sp->tbuflen >= npixels); + tp = (uint32 *) sp->tbuf; + } + /* copy to array of uint32 */ + bp = (unsigned char*) tif->tif_rawcp; + cc = tif->tif_rawcc; + for (i = 0; i < npixels && cc > 0; i++) { + tp[i] = bp[0] << 16 | bp[1] << 8 | bp[2]; + bp += 3; + cc -= 3; + } + tif->tif_rawcp = (uint8*) bp; + tif->tif_rawcc = cc; + if (i != npixels) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at row %lu (short %I64d pixels)", + (unsigned long) tif->tif_row, + (unsigned __int64) (npixels - i)); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at row %lu (short %llu pixels)", + (unsigned long) tif->tif_row, + (unsigned long long) (npixels - i)); +#endif + return (0); + } + (*sp->tfunc)(sp, op, npixels); + return (1); +} + +/* + * Decode a string of 32-bit pixels. + */ +static int +LogLuvDecode32(TIFF* tif, uint8* op, tmsize_t occ, uint16 s) +{ + static const char module[] = "LogLuvDecode32"; + LogLuvState* sp; + int shft; + tmsize_t i; + tmsize_t npixels; + unsigned char* bp; + uint32* tp; + uint32 b; + tmsize_t cc; + int rc; + + assert(s == 0); + sp = DecoderState(tif); + assert(sp != NULL); + + npixels = occ / sp->pixel_size; + + if (sp->user_datafmt == SGILOGDATAFMT_RAW) + tp = (uint32*) op; + else { + assert(sp->tbuflen >= npixels); + tp = (uint32*) sp->tbuf; + } + _TIFFmemset((void*) tp, 0, npixels*sizeof (tp[0])); + + bp = (unsigned char*) tif->tif_rawcp; + cc = tif->tif_rawcc; + /* get each byte string */ + for (shft = 4*8; (shft -= 8) >= 0; ) { + for (i = 0; i < npixels && cc > 0; ) + if (*bp >= 128) { /* run */ + rc = *bp++ + (2-128); + b = (uint32)*bp++ << shft; + cc -= 2; /* TODO: potential input buffer overrun when decoding corrupt or truncated data */ + while (rc-- && i < npixels) + tp[i++] |= b; + } else { /* non-run */ + rc = *bp++; /* nul is noop */ + while (--cc && rc-- && i < npixels) + tp[i++] |= (uint32)*bp++ << shft; + } + if (i != npixels) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at row %lu (short %I64d pixels)", + (unsigned long) tif->tif_row, + (unsigned __int64) (npixels - i)); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at row %lu (short %llu pixels)", + (unsigned long) tif->tif_row, + (unsigned long long) (npixels - i)); +#endif + tif->tif_rawcp = (uint8*) bp; + tif->tif_rawcc = cc; + return (0); + } + } + (*sp->tfunc)(sp, op, npixels); + tif->tif_rawcp = (uint8*) bp; + tif->tif_rawcc = cc; + return (1); +} + +/* + * Decode a strip of pixels. We break it into rows to + * maintain synchrony with the encode algorithm, which + * is row by row. + */ +static int +LogLuvDecodeStrip(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + tmsize_t rowlen = TIFFScanlineSize(tif); + + assert(cc%rowlen == 0); + while (cc && (*tif->tif_decoderow)(tif, bp, rowlen, s)) + bp += rowlen, cc -= rowlen; + return (cc == 0); +} + +/* + * Decode a tile of pixels. We break it into rows to + * maintain synchrony with the encode algorithm, which + * is row by row. + */ +static int +LogLuvDecodeTile(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + tmsize_t rowlen = TIFFTileRowSize(tif); + + assert(cc%rowlen == 0); + while (cc && (*tif->tif_decoderow)(tif, bp, rowlen, s)) + bp += rowlen, cc -= rowlen; + return (cc == 0); +} + +/* + * Encode a row of 16-bit pixels. + */ +static int +LogL16Encode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + LogLuvState* sp = EncoderState(tif); + int shft; + tmsize_t i; + tmsize_t j; + tmsize_t npixels; + uint8* op; + int16* tp; + int16 b; + tmsize_t occ; + int rc=0, mask; + tmsize_t beg; + + assert(s == 0); + assert(sp != NULL); + npixels = cc / sp->pixel_size; + + if (sp->user_datafmt == SGILOGDATAFMT_16BIT) + tp = (int16*) bp; + else { + tp = (int16*) sp->tbuf; + assert(sp->tbuflen >= npixels); + (*sp->tfunc)(sp, bp, npixels); + } + /* compress each byte string */ + op = tif->tif_rawcp; + occ = tif->tif_rawdatasize - tif->tif_rawcc; + for (shft = 2*8; (shft -= 8) >= 0; ) + for (i = 0; i < npixels; i += rc) { + if (occ < 4) { + tif->tif_rawcp = op; + tif->tif_rawcc = tif->tif_rawdatasize - occ; + if (!TIFFFlushData1(tif)) + return (-1); + op = tif->tif_rawcp; + occ = tif->tif_rawdatasize - tif->tif_rawcc; + } + mask = 0xff << shft; /* find next run */ + for (beg = i; beg < npixels; beg += rc) { + b = (int16) (tp[beg] & mask); + rc = 1; + while (rc < 127+2 && beg+rc < npixels && + (tp[beg+rc] & mask) == b) + rc++; + if (rc >= MINRUN) + break; /* long enough */ + } + if (beg-i > 1 && beg-i < MINRUN) { + b = (int16) (tp[i] & mask);/*check short run */ + j = i+1; + while ((tp[j++] & mask) == b) + if (j == beg) { + *op++ = (uint8)(128-2+j-i); + *op++ = (uint8)(b >> shft); + occ -= 2; + i = beg; + break; + } + } + while (i < beg) { /* write out non-run */ + if ((j = beg-i) > 127) j = 127; + if (occ < j+3) { + tif->tif_rawcp = op; + tif->tif_rawcc = tif->tif_rawdatasize - occ; + if (!TIFFFlushData1(tif)) + return (-1); + op = tif->tif_rawcp; + occ = tif->tif_rawdatasize - tif->tif_rawcc; + } + *op++ = (uint8) j; occ--; + while (j--) { + *op++ = (uint8) (tp[i++] >> shft & 0xff); + occ--; + } + } + if (rc >= MINRUN) { /* write out run */ + *op++ = (uint8) (128-2+rc); + *op++ = (uint8) (tp[beg] >> shft & 0xff); + occ -= 2; + } else + rc = 0; + } + tif->tif_rawcp = op; + tif->tif_rawcc = tif->tif_rawdatasize - occ; + + return (1); +} + +/* + * Encode a row of 24-bit pixels. + */ +static int +LogLuvEncode24(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + LogLuvState* sp = EncoderState(tif); + tmsize_t i; + tmsize_t npixels; + tmsize_t occ; + uint8* op; + uint32* tp; + + assert(s == 0); + assert(sp != NULL); + npixels = cc / sp->pixel_size; + + if (sp->user_datafmt == SGILOGDATAFMT_RAW) + tp = (uint32*) bp; + else { + tp = (uint32*) sp->tbuf; + assert(sp->tbuflen >= npixels); + (*sp->tfunc)(sp, bp, npixels); + } + /* write out encoded pixels */ + op = tif->tif_rawcp; + occ = tif->tif_rawdatasize - tif->tif_rawcc; + for (i = npixels; i--; ) { + if (occ < 3) { + tif->tif_rawcp = op; + tif->tif_rawcc = tif->tif_rawdatasize - occ; + if (!TIFFFlushData1(tif)) + return (-1); + op = tif->tif_rawcp; + occ = tif->tif_rawdatasize - tif->tif_rawcc; + } + *op++ = (uint8)(*tp >> 16); + *op++ = (uint8)(*tp >> 8 & 0xff); + *op++ = (uint8)(*tp++ & 0xff); + occ -= 3; + } + tif->tif_rawcp = op; + tif->tif_rawcc = tif->tif_rawdatasize - occ; + + return (1); +} + +/* + * Encode a row of 32-bit pixels. + */ +static int +LogLuvEncode32(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + LogLuvState* sp = EncoderState(tif); + int shft; + tmsize_t i; + tmsize_t j; + tmsize_t npixels; + uint8* op; + uint32* tp; + uint32 b; + tmsize_t occ; + int rc=0, mask; + tmsize_t beg; + + assert(s == 0); + assert(sp != NULL); + + npixels = cc / sp->pixel_size; + + if (sp->user_datafmt == SGILOGDATAFMT_RAW) + tp = (uint32*) bp; + else { + tp = (uint32*) sp->tbuf; + assert(sp->tbuflen >= npixels); + (*sp->tfunc)(sp, bp, npixels); + } + /* compress each byte string */ + op = tif->tif_rawcp; + occ = tif->tif_rawdatasize - tif->tif_rawcc; + for (shft = 4*8; (shft -= 8) >= 0; ) + for (i = 0; i < npixels; i += rc) { + if (occ < 4) { + tif->tif_rawcp = op; + tif->tif_rawcc = tif->tif_rawdatasize - occ; + if (!TIFFFlushData1(tif)) + return (-1); + op = tif->tif_rawcp; + occ = tif->tif_rawdatasize - tif->tif_rawcc; + } + mask = 0xff << shft; /* find next run */ + for (beg = i; beg < npixels; beg += rc) { + b = tp[beg] & mask; + rc = 1; + while (rc < 127+2 && beg+rc < npixels && + (tp[beg+rc] & mask) == b) + rc++; + if (rc >= MINRUN) + break; /* long enough */ + } + if (beg-i > 1 && beg-i < MINRUN) { + b = tp[i] & mask; /* check short run */ + j = i+1; + while ((tp[j++] & mask) == b) + if (j == beg) { + *op++ = (uint8)(128-2+j-i); + *op++ = (uint8)(b >> shft); + occ -= 2; + i = beg; + break; + } + } + while (i < beg) { /* write out non-run */ + if ((j = beg-i) > 127) j = 127; + if (occ < j+3) { + tif->tif_rawcp = op; + tif->tif_rawcc = tif->tif_rawdatasize - occ; + if (!TIFFFlushData1(tif)) + return (-1); + op = tif->tif_rawcp; + occ = tif->tif_rawdatasize - tif->tif_rawcc; + } + *op++ = (uint8) j; occ--; + while (j--) { + *op++ = (uint8)(tp[i++] >> shft & 0xff); + occ--; + } + } + if (rc >= MINRUN) { /* write out run */ + *op++ = (uint8) (128-2+rc); + *op++ = (uint8)(tp[beg] >> shft & 0xff); + occ -= 2; + } else + rc = 0; + } + tif->tif_rawcp = op; + tif->tif_rawcc = tif->tif_rawdatasize - occ; + + return (1); +} + +/* + * Encode a strip of pixels. We break it into rows to + * avoid encoding runs across row boundaries. + */ +static int +LogLuvEncodeStrip(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + tmsize_t rowlen = TIFFScanlineSize(tif); + + assert(cc%rowlen == 0); + while (cc && (*tif->tif_encoderow)(tif, bp, rowlen, s) == 1) + bp += rowlen, cc -= rowlen; + return (cc == 0); +} + +/* + * Encode a tile of pixels. We break it into rows to + * avoid encoding runs across row boundaries. + */ +static int +LogLuvEncodeTile(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + tmsize_t rowlen = TIFFTileRowSize(tif); + + assert(cc%rowlen == 0); + while (cc && (*tif->tif_encoderow)(tif, bp, rowlen, s) == 1) + bp += rowlen, cc -= rowlen; + return (cc == 0); +} + +/* + * Encode/Decode functions for converting to and from user formats. + */ + +#include "uvcode.h" + +#ifndef UVSCALE +#define U_NEU 0.210526316 +#define V_NEU 0.473684211 +#define UVSCALE 410. +#endif + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#define log2(x) ((1./M_LN2)*log(x)) +#define exp2(x) exp(M_LN2*(x)) + +#define itrunc(x,m) ((m)==SGILOGENCODE_NODITHER ? \ + (int)(x) : \ + (int)((x) + rand()*(1./RAND_MAX) - .5)) + +#if !LOGLUV_PUBLIC +static +#endif +double +LogL16toY(int p16) /* compute luminance from 16-bit LogL */ +{ + int Le = p16 & 0x7fff; + double Y; + + if (!Le) + return (0.); + Y = exp(M_LN2/256.*(Le+.5) - M_LN2*64.); + return (!(p16 & 0x8000) ? Y : -Y); +} + +#if !LOGLUV_PUBLIC +static +#endif +int +LogL16fromY(double Y, int em) /* get 16-bit LogL from Y */ +{ + if (Y >= 1.8371976e19) + return (0x7fff); + if (Y <= -1.8371976e19) + return (0xffff); + if (Y > 5.4136769e-20) + return itrunc(256.*(log2(Y) + 64.), em); + if (Y < -5.4136769e-20) + return (~0x7fff | itrunc(256.*(log2(-Y) + 64.), em)); + return (0); +} + +static void +L16toY(LogLuvState* sp, uint8* op, tmsize_t n) +{ + int16* l16 = (int16*) sp->tbuf; + float* yp = (float*) op; + + while (n-- > 0) + *yp++ = (float)LogL16toY(*l16++); +} + +static void +L16toGry(LogLuvState* sp, uint8* op, tmsize_t n) +{ + int16* l16 = (int16*) sp->tbuf; + uint8* gp = (uint8*) op; + + while (n-- > 0) { + double Y = LogL16toY(*l16++); + *gp++ = (uint8) ((Y <= 0.) ? 0 : (Y >= 1.) ? 255 : (int)(256.*sqrt(Y))); + } +} + +static void +L16fromY(LogLuvState* sp, uint8* op, tmsize_t n) +{ + int16* l16 = (int16*) sp->tbuf; + float* yp = (float*) op; + + while (n-- > 0) + *l16++ = (int16) (LogL16fromY(*yp++, sp->encode_meth)); +} + +#if !LOGLUV_PUBLIC +static +#endif +void +XYZtoRGB24(float xyz[3], uint8 rgb[3]) +{ + double r, g, b; + /* assume CCIR-709 primaries */ + r = 2.690*xyz[0] + -1.276*xyz[1] + -0.414*xyz[2]; + g = -1.022*xyz[0] + 1.978*xyz[1] + 0.044*xyz[2]; + b = 0.061*xyz[0] + -0.224*xyz[1] + 1.163*xyz[2]; + /* assume 2.0 gamma for speed */ + /* could use integer sqrt approx., but this is probably faster */ + rgb[0] = (uint8)((r<=0.) ? 0 : (r >= 1.) ? 255 : (int)(256.*sqrt(r))); + rgb[1] = (uint8)((g<=0.) ? 0 : (g >= 1.) ? 255 : (int)(256.*sqrt(g))); + rgb[2] = (uint8)((b<=0.) ? 0 : (b >= 1.) ? 255 : (int)(256.*sqrt(b))); +} + +#if !LOGLUV_PUBLIC +static +#endif +double +LogL10toY(int p10) /* compute luminance from 10-bit LogL */ +{ + if (p10 == 0) + return (0.); + return (exp(M_LN2/64.*(p10+.5) - M_LN2*12.)); +} + +#if !LOGLUV_PUBLIC +static +#endif +int +LogL10fromY(double Y, int em) /* get 10-bit LogL from Y */ +{ + if (Y >= 15.742) + return (0x3ff); + else if (Y <= .00024283) + return (0); + else + return itrunc(64.*(log2(Y) + 12.), em); +} + +#define NANGLES 100 +#define uv2ang(u, v) ( (NANGLES*.499999999/M_PI) \ + * atan2((v)-V_NEU,(u)-U_NEU) + .5*NANGLES ) + +static int +oog_encode(double u, double v) /* encode out-of-gamut chroma */ +{ + static int oog_table[NANGLES]; + static int initialized = 0; + register int i; + + if (!initialized) { /* set up perimeter table */ + double eps[NANGLES], ua, va, ang, epsa; + int ui, vi, ustep; + for (i = NANGLES; i--; ) + eps[i] = 2.; + for (vi = UV_NVS; vi--; ) { + va = UV_VSTART + (vi+.5)*UV_SQSIZ; + ustep = uv_row[vi].nus-1; + if (vi == UV_NVS-1 || vi == 0 || ustep <= 0) + ustep = 1; + for (ui = uv_row[vi].nus-1; ui >= 0; ui -= ustep) { + ua = uv_row[vi].ustart + (ui+.5)*UV_SQSIZ; + ang = uv2ang(ua, va); + i = (int) ang; + epsa = fabs(ang - (i+.5)); + if (epsa < eps[i]) { + oog_table[i] = uv_row[vi].ncum + ui; + eps[i] = epsa; + } + } + } + for (i = NANGLES; i--; ) /* fill any holes */ + if (eps[i] > 1.5) { + int i1, i2; + for (i1 = 1; i1 < NANGLES/2; i1++) + if (eps[(i+i1)%NANGLES] < 1.5) + break; + for (i2 = 1; i2 < NANGLES/2; i2++) + if (eps[(i+NANGLES-i2)%NANGLES] < 1.5) + break; + if (i1 < i2) + oog_table[i] = + oog_table[(i+i1)%NANGLES]; + else + oog_table[i] = + oog_table[(i+NANGLES-i2)%NANGLES]; + } + initialized = 1; + } + i = (int) uv2ang(u, v); /* look up hue angle */ + return (oog_table[i]); +} + +#undef uv2ang +#undef NANGLES + +#if !LOGLUV_PUBLIC +static +#endif +int +uv_encode(double u, double v, int em) /* encode (u',v') coordinates */ +{ + register int vi, ui; + + if (v < UV_VSTART) + return oog_encode(u, v); + vi = itrunc((v - UV_VSTART)*(1./UV_SQSIZ), em); + if (vi >= UV_NVS) + return oog_encode(u, v); + if (u < uv_row[vi].ustart) + return oog_encode(u, v); + ui = itrunc((u - uv_row[vi].ustart)*(1./UV_SQSIZ), em); + if (ui >= uv_row[vi].nus) + return oog_encode(u, v); + + return (uv_row[vi].ncum + ui); +} + +#if !LOGLUV_PUBLIC +static +#endif +int +uv_decode(double *up, double *vp, int c) /* decode (u',v') index */ +{ + int upper, lower; + register int ui, vi; + + if (c < 0 || c >= UV_NDIVS) + return (-1); + lower = 0; /* binary search */ + upper = UV_NVS; + while (upper - lower > 1) { + vi = (lower + upper) >> 1; + ui = c - uv_row[vi].ncum; + if (ui > 0) + lower = vi; + else if (ui < 0) + upper = vi; + else { + lower = vi; + break; + } + } + vi = lower; + ui = c - uv_row[vi].ncum; + *up = uv_row[vi].ustart + (ui+.5)*UV_SQSIZ; + *vp = UV_VSTART + (vi+.5)*UV_SQSIZ; + return (0); +} + +#if !LOGLUV_PUBLIC +static +#endif +void +LogLuv24toXYZ(uint32 p, float XYZ[3]) +{ + int Ce; + double L, u, v, s, x, y; + /* decode luminance */ + L = LogL10toY(p>>14 & 0x3ff); + if (L <= 0.) { + XYZ[0] = XYZ[1] = XYZ[2] = 0.; + return; + } + /* decode color */ + Ce = p & 0x3fff; + if (uv_decode(&u, &v, Ce) < 0) { + u = U_NEU; v = V_NEU; + } + s = 1./(6.*u - 16.*v + 12.); + x = 9.*u * s; + y = 4.*v * s; + /* convert to XYZ */ + XYZ[0] = (float)(x/y * L); + XYZ[1] = (float)L; + XYZ[2] = (float)((1.-x-y)/y * L); +} + +#if !LOGLUV_PUBLIC +static +#endif +uint32 +LogLuv24fromXYZ(float XYZ[3], int em) +{ + int Le, Ce; + double u, v, s; + /* encode luminance */ + Le = LogL10fromY(XYZ[1], em); + /* encode color */ + s = XYZ[0] + 15.*XYZ[1] + 3.*XYZ[2]; + if (!Le || s <= 0.) { + u = U_NEU; + v = V_NEU; + } else { + u = 4.*XYZ[0] / s; + v = 9.*XYZ[1] / s; + } + Ce = uv_encode(u, v, em); + if (Ce < 0) /* never happens */ + Ce = uv_encode(U_NEU, V_NEU, SGILOGENCODE_NODITHER); + /* combine encodings */ + return (Le << 14 | Ce); +} + +static void +Luv24toXYZ(LogLuvState* sp, uint8* op, tmsize_t n) +{ + uint32* luv = (uint32*) sp->tbuf; + float* xyz = (float*) op; + + while (n-- > 0) { + LogLuv24toXYZ(*luv, xyz); + xyz += 3; + luv++; + } +} + +static void +Luv24toLuv48(LogLuvState* sp, uint8* op, tmsize_t n) +{ + uint32* luv = (uint32*) sp->tbuf; + int16* luv3 = (int16*) op; + + while (n-- > 0) { + double u, v; + + *luv3++ = (int16)((*luv >> 12 & 0xffd) + 13314); + if (uv_decode(&u, &v, *luv&0x3fff) < 0) { + u = U_NEU; + v = V_NEU; + } + *luv3++ = (int16)(u * (1L<<15)); + *luv3++ = (int16)(v * (1L<<15)); + luv++; + } +} + +static void +Luv24toRGB(LogLuvState* sp, uint8* op, tmsize_t n) +{ + uint32* luv = (uint32*) sp->tbuf; + uint8* rgb = (uint8*) op; + + while (n-- > 0) { + float xyz[3]; + + LogLuv24toXYZ(*luv++, xyz); + XYZtoRGB24(xyz, rgb); + rgb += 3; + } +} + +static void +Luv24fromXYZ(LogLuvState* sp, uint8* op, tmsize_t n) +{ + uint32* luv = (uint32*) sp->tbuf; + float* xyz = (float*) op; + + while (n-- > 0) { + *luv++ = LogLuv24fromXYZ(xyz, sp->encode_meth); + xyz += 3; + } +} + +static void +Luv24fromLuv48(LogLuvState* sp, uint8* op, tmsize_t n) +{ + uint32* luv = (uint32*) sp->tbuf; + int16* luv3 = (int16*) op; + + while (n-- > 0) { + int Le, Ce; + + if (luv3[0] <= 0) + Le = 0; + else if (luv3[0] >= (1<<12)+3314) + Le = (1<<10) - 1; + else if (sp->encode_meth == SGILOGENCODE_NODITHER) + Le = (luv3[0]-3314) >> 2; + else + Le = itrunc(.25*(luv3[0]-3314.), sp->encode_meth); + + Ce = uv_encode((luv3[1]+.5)/(1<<15), (luv3[2]+.5)/(1<<15), + sp->encode_meth); + if (Ce < 0) /* never happens */ + Ce = uv_encode(U_NEU, V_NEU, SGILOGENCODE_NODITHER); + *luv++ = (uint32)Le << 14 | Ce; + luv3 += 3; + } +} + +#if !LOGLUV_PUBLIC +static +#endif +void +LogLuv32toXYZ(uint32 p, float XYZ[3]) +{ + double L, u, v, s, x, y; + /* decode luminance */ + L = LogL16toY((int)p >> 16); + if (L <= 0.) { + XYZ[0] = XYZ[1] = XYZ[2] = 0.; + return; + } + /* decode color */ + u = 1./UVSCALE * ((p>>8 & 0xff) + .5); + v = 1./UVSCALE * ((p & 0xff) + .5); + s = 1./(6.*u - 16.*v + 12.); + x = 9.*u * s; + y = 4.*v * s; + /* convert to XYZ */ + XYZ[0] = (float)(x/y * L); + XYZ[1] = (float)L; + XYZ[2] = (float)((1.-x-y)/y * L); +} + +#if !LOGLUV_PUBLIC +static +#endif +uint32 +LogLuv32fromXYZ(float XYZ[3], int em) +{ + unsigned int Le, ue, ve; + double u, v, s; + /* encode luminance */ + Le = (unsigned int)LogL16fromY(XYZ[1], em); + /* encode color */ + s = XYZ[0] + 15.*XYZ[1] + 3.*XYZ[2]; + if (!Le || s <= 0.) { + u = U_NEU; + v = V_NEU; + } else { + u = 4.*XYZ[0] / s; + v = 9.*XYZ[1] / s; + } + if (u <= 0.) ue = 0; + else ue = itrunc(UVSCALE*u, em); + if (ue > 255) ue = 255; + if (v <= 0.) ve = 0; + else ve = itrunc(UVSCALE*v, em); + if (ve > 255) ve = 255; + /* combine encodings */ + return (Le << 16 | ue << 8 | ve); +} + +static void +Luv32toXYZ(LogLuvState* sp, uint8* op, tmsize_t n) +{ + uint32* luv = (uint32*) sp->tbuf; + float* xyz = (float*) op; + + while (n-- > 0) { + LogLuv32toXYZ(*luv++, xyz); + xyz += 3; + } +} + +static void +Luv32toLuv48(LogLuvState* sp, uint8* op, tmsize_t n) +{ + uint32* luv = (uint32*) sp->tbuf; + int16* luv3 = (int16*) op; + + while (n-- > 0) { + double u, v; + + *luv3++ = (int16)(*luv >> 16); + u = 1./UVSCALE * ((*luv>>8 & 0xff) + .5); + v = 1./UVSCALE * ((*luv & 0xff) + .5); + *luv3++ = (int16)(u * (1L<<15)); + *luv3++ = (int16)(v * (1L<<15)); + luv++; + } +} + +static void +Luv32toRGB(LogLuvState* sp, uint8* op, tmsize_t n) +{ + uint32* luv = (uint32*) sp->tbuf; + uint8* rgb = (uint8*) op; + + while (n-- > 0) { + float xyz[3]; + + LogLuv32toXYZ(*luv++, xyz); + XYZtoRGB24(xyz, rgb); + rgb += 3; + } +} + +static void +Luv32fromXYZ(LogLuvState* sp, uint8* op, tmsize_t n) +{ + uint32* luv = (uint32*) sp->tbuf; + float* xyz = (float*) op; + + while (n-- > 0) { + *luv++ = LogLuv32fromXYZ(xyz, sp->encode_meth); + xyz += 3; + } +} + +static void +Luv32fromLuv48(LogLuvState* sp, uint8* op, tmsize_t n) +{ + uint32* luv = (uint32*) sp->tbuf; + int16* luv3 = (int16*) op; + + if (sp->encode_meth == SGILOGENCODE_NODITHER) { + while (n-- > 0) { + *luv++ = (uint32)luv3[0] << 16 | + (luv3[1]*(uint32)(UVSCALE+.5) >> 7 & 0xff00) | + (luv3[2]*(uint32)(UVSCALE+.5) >> 15 & 0xff); + luv3 += 3; + } + return; + } + while (n-- > 0) { + *luv++ = (uint32)luv3[0] << 16 | + (itrunc(luv3[1]*(UVSCALE/(1<<15)), sp->encode_meth) << 8 & 0xff00) | + (itrunc(luv3[2]*(UVSCALE/(1<<15)), sp->encode_meth) & 0xff); + luv3 += 3; + } +} + +static void +_logLuvNop(LogLuvState* sp, uint8* op, tmsize_t n) +{ + (void) sp; (void) op; (void) n; +} + +static int +LogL16GuessDataFmt(TIFFDirectory *td) +{ +#define PACK(s,b,f) (((b)<<6)|((s)<<3)|(f)) + switch (PACK(td->td_samplesperpixel, td->td_bitspersample, td->td_sampleformat)) { + case PACK(1, 32, SAMPLEFORMAT_IEEEFP): + return (SGILOGDATAFMT_FLOAT); + case PACK(1, 16, SAMPLEFORMAT_VOID): + case PACK(1, 16, SAMPLEFORMAT_INT): + case PACK(1, 16, SAMPLEFORMAT_UINT): + return (SGILOGDATAFMT_16BIT); + case PACK(1, 8, SAMPLEFORMAT_VOID): + case PACK(1, 8, SAMPLEFORMAT_UINT): + return (SGILOGDATAFMT_8BIT); + } +#undef PACK + return (SGILOGDATAFMT_UNKNOWN); +} + +static tmsize_t +multiply_ms(tmsize_t m1, tmsize_t m2) +{ + tmsize_t bytes = m1 * m2; + + if (m1 && bytes / m1 != m2) + bytes = 0; + + return bytes; +} + +static int +LogL16InitState(TIFF* tif) +{ + static const char module[] = "LogL16InitState"; + TIFFDirectory *td = &tif->tif_dir; + LogLuvState* sp = DecoderState(tif); + + assert(sp != NULL); + assert(td->td_photometric == PHOTOMETRIC_LOGL); + + /* for some reason, we can't do this in TIFFInitLogL16 */ + if (sp->user_datafmt == SGILOGDATAFMT_UNKNOWN) + sp->user_datafmt = LogL16GuessDataFmt(td); + switch (sp->user_datafmt) { + case SGILOGDATAFMT_FLOAT: + sp->pixel_size = sizeof (float); + break; + case SGILOGDATAFMT_16BIT: + sp->pixel_size = sizeof (int16); + break; + case SGILOGDATAFMT_8BIT: + sp->pixel_size = sizeof (uint8); + break; + default: + TIFFErrorExt(tif->tif_clientdata, module, + "No support for converting user data format to LogL"); + return (0); + } + if( isTiled(tif) ) + sp->tbuflen = multiply_ms(td->td_tilewidth, td->td_tilelength); + else + sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_rowsperstrip); + if (multiply_ms(sp->tbuflen, sizeof (int16)) == 0 || + (sp->tbuf = (uint8*) _TIFFmalloc(sp->tbuflen * sizeof (int16))) == NULL) { + TIFFErrorExt(tif->tif_clientdata, module, "No space for SGILog translation buffer"); + return (0); + } + return (1); +} + +static int +LogLuvGuessDataFmt(TIFFDirectory *td) +{ + int guess; + + /* + * If the user didn't tell us their datafmt, + * take our best guess from the bitspersample. + */ +#define PACK(a,b) (((a)<<3)|(b)) + switch (PACK(td->td_bitspersample, td->td_sampleformat)) { + case PACK(32, SAMPLEFORMAT_IEEEFP): + guess = SGILOGDATAFMT_FLOAT; + break; + case PACK(32, SAMPLEFORMAT_VOID): + case PACK(32, SAMPLEFORMAT_UINT): + case PACK(32, SAMPLEFORMAT_INT): + guess = SGILOGDATAFMT_RAW; + break; + case PACK(16, SAMPLEFORMAT_VOID): + case PACK(16, SAMPLEFORMAT_INT): + case PACK(16, SAMPLEFORMAT_UINT): + guess = SGILOGDATAFMT_16BIT; + break; + case PACK( 8, SAMPLEFORMAT_VOID): + case PACK( 8, SAMPLEFORMAT_UINT): + guess = SGILOGDATAFMT_8BIT; + break; + default: + guess = SGILOGDATAFMT_UNKNOWN; + break; +#undef PACK + } + /* + * Double-check samples per pixel. + */ + switch (td->td_samplesperpixel) { + case 1: + if (guess != SGILOGDATAFMT_RAW) + guess = SGILOGDATAFMT_UNKNOWN; + break; + case 3: + if (guess == SGILOGDATAFMT_RAW) + guess = SGILOGDATAFMT_UNKNOWN; + break; + default: + guess = SGILOGDATAFMT_UNKNOWN; + break; + } + return (guess); +} + +static int +LogLuvInitState(TIFF* tif) +{ + static const char module[] = "LogLuvInitState"; + TIFFDirectory* td = &tif->tif_dir; + LogLuvState* sp = DecoderState(tif); + + assert(sp != NULL); + assert(td->td_photometric == PHOTOMETRIC_LOGLUV); + + /* for some reason, we can't do this in TIFFInitLogLuv */ + if (td->td_planarconfig != PLANARCONFIG_CONTIG) { + TIFFErrorExt(tif->tif_clientdata, module, + "SGILog compression cannot handle non-contiguous data"); + return (0); + } + if (sp->user_datafmt == SGILOGDATAFMT_UNKNOWN) + sp->user_datafmt = LogLuvGuessDataFmt(td); + switch (sp->user_datafmt) { + case SGILOGDATAFMT_FLOAT: + sp->pixel_size = 3*sizeof (float); + break; + case SGILOGDATAFMT_16BIT: + sp->pixel_size = 3*sizeof (int16); + break; + case SGILOGDATAFMT_RAW: + sp->pixel_size = sizeof (uint32); + break; + case SGILOGDATAFMT_8BIT: + sp->pixel_size = 3*sizeof (uint8); + break; + default: + TIFFErrorExt(tif->tif_clientdata, module, + "No support for converting user data format to LogLuv"); + return (0); + } + if( isTiled(tif) ) + sp->tbuflen = multiply_ms(td->td_tilewidth, td->td_tilelength); + else + sp->tbuflen = multiply_ms(td->td_imagewidth, td->td_rowsperstrip); + if (multiply_ms(sp->tbuflen, sizeof (uint32)) == 0 || + (sp->tbuf = (uint8*) _TIFFmalloc(sp->tbuflen * sizeof (uint32))) == NULL) { + TIFFErrorExt(tif->tif_clientdata, module, "No space for SGILog translation buffer"); + return (0); + } + return (1); +} + +static int +LogLuvFixupTags(TIFF* tif) +{ + (void) tif; + return (1); +} + +static int +LogLuvSetupDecode(TIFF* tif) +{ + static const char module[] = "LogLuvSetupDecode"; + LogLuvState* sp = DecoderState(tif); + TIFFDirectory* td = &tif->tif_dir; + + tif->tif_postdecode = _TIFFNoPostDecode; + switch (td->td_photometric) { + case PHOTOMETRIC_LOGLUV: + if (!LogLuvInitState(tif)) + break; + if (td->td_compression == COMPRESSION_SGILOG24) { + tif->tif_decoderow = LogLuvDecode24; + switch (sp->user_datafmt) { + case SGILOGDATAFMT_FLOAT: + sp->tfunc = Luv24toXYZ; + break; + case SGILOGDATAFMT_16BIT: + sp->tfunc = Luv24toLuv48; + break; + case SGILOGDATAFMT_8BIT: + sp->tfunc = Luv24toRGB; + break; + } + } else { + tif->tif_decoderow = LogLuvDecode32; + switch (sp->user_datafmt) { + case SGILOGDATAFMT_FLOAT: + sp->tfunc = Luv32toXYZ; + break; + case SGILOGDATAFMT_16BIT: + sp->tfunc = Luv32toLuv48; + break; + case SGILOGDATAFMT_8BIT: + sp->tfunc = Luv32toRGB; + break; + } + } + return (1); + case PHOTOMETRIC_LOGL: + if (!LogL16InitState(tif)) + break; + tif->tif_decoderow = LogL16Decode; + switch (sp->user_datafmt) { + case SGILOGDATAFMT_FLOAT: + sp->tfunc = L16toY; + break; + case SGILOGDATAFMT_8BIT: + sp->tfunc = L16toGry; + break; + } + return (1); + default: + TIFFErrorExt(tif->tif_clientdata, module, + "Inappropriate photometric interpretation %d for SGILog compression; %s", + td->td_photometric, "must be either LogLUV or LogL"); + break; + } + return (0); +} + +static int +LogLuvSetupEncode(TIFF* tif) +{ + static const char module[] = "LogLuvSetupEncode"; + LogLuvState* sp = EncoderState(tif); + TIFFDirectory* td = &tif->tif_dir; + + switch (td->td_photometric) { + case PHOTOMETRIC_LOGLUV: + if (!LogLuvInitState(tif)) + break; + if (td->td_compression == COMPRESSION_SGILOG24) { + tif->tif_encoderow = LogLuvEncode24; + switch (sp->user_datafmt) { + case SGILOGDATAFMT_FLOAT: + sp->tfunc = Luv24fromXYZ; + break; + case SGILOGDATAFMT_16BIT: + sp->tfunc = Luv24fromLuv48; + break; + case SGILOGDATAFMT_RAW: + break; + default: + goto notsupported; + } + } else { + tif->tif_encoderow = LogLuvEncode32; + switch (sp->user_datafmt) { + case SGILOGDATAFMT_FLOAT: + sp->tfunc = Luv32fromXYZ; + break; + case SGILOGDATAFMT_16BIT: + sp->tfunc = Luv32fromLuv48; + break; + case SGILOGDATAFMT_RAW: + break; + default: + goto notsupported; + } + } + break; + case PHOTOMETRIC_LOGL: + if (!LogL16InitState(tif)) + break; + tif->tif_encoderow = LogL16Encode; + switch (sp->user_datafmt) { + case SGILOGDATAFMT_FLOAT: + sp->tfunc = L16fromY; + break; + case SGILOGDATAFMT_16BIT: + break; + default: + goto notsupported; + } + break; + default: + TIFFErrorExt(tif->tif_clientdata, module, + "Inappropriate photometric interpretation %d for SGILog compression; %s", + td->td_photometric, "must be either LogLUV or LogL"); + break; + } + return (1); +notsupported: + TIFFErrorExt(tif->tif_clientdata, module, + "SGILog compression supported only for %s, or raw data", + td->td_photometric == PHOTOMETRIC_LOGL ? "Y, L" : "XYZ, Luv"); + return (0); +} + +static void +LogLuvClose(TIFF* tif) +{ + TIFFDirectory *td = &tif->tif_dir; + + /* + * For consistency, we always want to write out the same + * bitspersample and sampleformat for our TIFF file, + * regardless of the data format being used by the application. + * Since this routine is called after tags have been set but + * before they have been recorded in the file, we reset them here. + */ + td->td_samplesperpixel = + (td->td_photometric == PHOTOMETRIC_LOGL) ? 1 : 3; + td->td_bitspersample = 16; + td->td_sampleformat = SAMPLEFORMAT_INT; +} + +static void +LogLuvCleanup(TIFF* tif) +{ + LogLuvState* sp = (LogLuvState *)tif->tif_data; + + assert(sp != 0); + + tif->tif_tagmethods.vgetfield = sp->vgetparent; + tif->tif_tagmethods.vsetfield = sp->vsetparent; + + if (sp->tbuf) + _TIFFfree(sp->tbuf); + _TIFFfree(sp); + tif->tif_data = NULL; + + _TIFFSetDefaultCompressionState(tif); +} + +static int +LogLuvVSetField(TIFF* tif, uint32 tag, va_list ap) +{ + static const char module[] = "LogLuvVSetField"; + LogLuvState* sp = DecoderState(tif); + int bps, fmt; + + switch (tag) { + case TIFFTAG_SGILOGDATAFMT: + sp->user_datafmt = (int) va_arg(ap, int); + /* + * Tweak the TIFF header so that the rest of libtiff knows what + * size of data will be passed between app and library, and + * assume that the app knows what it is doing and is not + * confused by these header manipulations... + */ + switch (sp->user_datafmt) { + case SGILOGDATAFMT_FLOAT: + bps = 32, fmt = SAMPLEFORMAT_IEEEFP; + break; + case SGILOGDATAFMT_16BIT: + bps = 16, fmt = SAMPLEFORMAT_INT; + break; + case SGILOGDATAFMT_RAW: + bps = 32, fmt = SAMPLEFORMAT_UINT; + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); + break; + case SGILOGDATAFMT_8BIT: + bps = 8, fmt = SAMPLEFORMAT_UINT; + break; + default: + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "Unknown data format %d for LogLuv compression", + sp->user_datafmt); + return (0); + } + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, fmt); + /* + * Must recalculate sizes should bits/sample change. + */ + tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tmsize_t) -1; + tif->tif_scanlinesize = TIFFScanlineSize(tif); + return (1); + case TIFFTAG_SGILOGENCODE: + sp->encode_meth = (int) va_arg(ap, int); + if (sp->encode_meth != SGILOGENCODE_NODITHER && + sp->encode_meth != SGILOGENCODE_RANDITHER) { + TIFFErrorExt(tif->tif_clientdata, module, + "Unknown encoding %d for LogLuv compression", + sp->encode_meth); + return (0); + } + return (1); + default: + return (*sp->vsetparent)(tif, tag, ap); + } +} + +static int +LogLuvVGetField(TIFF* tif, uint32 tag, va_list ap) +{ + LogLuvState *sp = (LogLuvState *)tif->tif_data; + + switch (tag) { + case TIFFTAG_SGILOGDATAFMT: + *va_arg(ap, int*) = sp->user_datafmt; + return (1); + default: + return (*sp->vgetparent)(tif, tag, ap); + } +} + +static const TIFFField LogLuvFields[] = { + { TIFFTAG_SGILOGDATAFMT, 0, 0, TIFF_SHORT, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "SGILogDataFmt", NULL}, + { TIFFTAG_SGILOGENCODE, 0, 0, TIFF_SHORT, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "SGILogEncode", NULL} +}; + +int +TIFFInitSGILog(TIFF* tif, int scheme) +{ + static const char module[] = "TIFFInitSGILog"; + LogLuvState* sp; + + assert(scheme == COMPRESSION_SGILOG24 || scheme == COMPRESSION_SGILOG); + + /* + * Merge codec-specific tag information. + */ + if (!_TIFFMergeFields(tif, LogLuvFields, + TIFFArrayCount(LogLuvFields))) { + TIFFErrorExt(tif->tif_clientdata, module, + "Merging SGILog codec-specific tags failed"); + return 0; + } + + /* + * Allocate state block so tag methods have storage to record values. + */ + tif->tif_data = (uint8*) _TIFFmalloc(sizeof (LogLuvState)); + if (tif->tif_data == NULL) + goto bad; + sp = (LogLuvState*) tif->tif_data; + _TIFFmemset((void*)sp, 0, sizeof (*sp)); + sp->user_datafmt = SGILOGDATAFMT_UNKNOWN; + sp->encode_meth = (scheme == COMPRESSION_SGILOG24) ? + SGILOGENCODE_RANDITHER : SGILOGENCODE_NODITHER; + sp->tfunc = _logLuvNop; + + /* + * Install codec methods. + * NB: tif_decoderow & tif_encoderow are filled + * in at setup time. + */ + tif->tif_fixuptags = LogLuvFixupTags; + tif->tif_setupdecode = LogLuvSetupDecode; + tif->tif_decodestrip = LogLuvDecodeStrip; + tif->tif_decodetile = LogLuvDecodeTile; + tif->tif_setupencode = LogLuvSetupEncode; + tif->tif_encodestrip = LogLuvEncodeStrip; + tif->tif_encodetile = LogLuvEncodeTile; + tif->tif_close = LogLuvClose; + tif->tif_cleanup = LogLuvCleanup; + + /* + * Override parent get/set field methods. + */ + sp->vgetparent = tif->tif_tagmethods.vgetfield; + tif->tif_tagmethods.vgetfield = LogLuvVGetField; /* hook for codec tags */ + sp->vsetparent = tif->tif_tagmethods.vsetfield; + tif->tif_tagmethods.vsetfield = LogLuvVSetField; /* hook for codec tags */ + + return (1); +bad: + TIFFErrorExt(tif->tif_clientdata, module, + "%s: No space for LogLuv state block", tif->tif_name); + return (0); +} +#endif /* LOGLUV_SUPPORT */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_lzma.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_lzma.c new file mode 100644 index 000000000..dedf1d948 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_lzma.c @@ -0,0 +1,495 @@ +/* $Id: tif_lzma.c,v 1.4 2011-12-22 00:29:29 bfriesen Exp $ */ + +/* + * Copyright (c) 2010, Andrey Kiselev + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "tiffiop.h" +#ifdef LZMA_SUPPORT +/* + * TIFF Library. + * + * LZMA2 Compression Support + * + * You need an LZMA2 SDK to link with. See http://tukaani.org/xz/ for details. + * + * The codec is derived from ZLIB codec (tif_zip.c). + */ + +#include "tif_predict.h" +#include "lzma.h" + +#include + +/* + * State block for each open TIFF file using LZMA2 compression/decompression. + */ +typedef struct { + TIFFPredictorState predict; + lzma_stream stream; + lzma_filter filters[LZMA_FILTERS_MAX + 1]; + lzma_options_delta opt_delta; /* delta filter options */ + lzma_options_lzma opt_lzma; /* LZMA2 filter options */ + int preset; /* compression level */ + lzma_check check; /* type of the integrity check */ + int state; /* state flags */ +#define LSTATE_INIT_DECODE 0x01 +#define LSTATE_INIT_ENCODE 0x02 + + TIFFVGetMethod vgetparent; /* super-class method */ + TIFFVSetMethod vsetparent; /* super-class method */ +} LZMAState; + +#define LState(tif) ((LZMAState*) (tif)->tif_data) +#define DecoderState(tif) LState(tif) +#define EncoderState(tif) LState(tif) + +static int LZMAEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s); +static int LZMADecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s); + +static const char * +LZMAStrerror(lzma_ret ret) +{ + switch (ret) { + case LZMA_OK: + return "operation completed successfully"; + case LZMA_STREAM_END: + return "end of stream was reached"; + case LZMA_NO_CHECK: + return "input stream has no integrity check"; + case LZMA_UNSUPPORTED_CHECK: + return "cannot calculate the integrity check"; + case LZMA_GET_CHECK: + return "integrity check type is now available"; + case LZMA_MEM_ERROR: + return "cannot allocate memory"; + case LZMA_MEMLIMIT_ERROR: + return "memory usage limit was reached"; + case LZMA_FORMAT_ERROR: + return "file format not recognized"; + case LZMA_OPTIONS_ERROR: + return "invalid or unsupported options"; + case LZMA_DATA_ERROR: + return "data is corrupt"; + case LZMA_BUF_ERROR: + return "no progress is possible (stream is truncated or corrupt)"; + case LZMA_PROG_ERROR: + return "programming error"; + default: + return "unindentified liblzma error"; + } +} + +static int +LZMAFixupTags(TIFF* tif) +{ + (void) tif; + return 1; +} + +static int +LZMASetupDecode(TIFF* tif) +{ + LZMAState* sp = DecoderState(tif); + + assert(sp != NULL); + + /* if we were last encoding, terminate this mode */ + if (sp->state & LSTATE_INIT_ENCODE) { + lzma_end(&sp->stream); + sp->state = 0; + } + + sp->state |= LSTATE_INIT_DECODE; + return 1; +} + +/* + * Setup state for decoding a strip. + */ +static int +LZMAPreDecode(TIFF* tif, uint16 s) +{ + static const char module[] = "LZMAPreDecode"; + LZMAState* sp = DecoderState(tif); + lzma_ret ret; + + (void) s; + assert(sp != NULL); + + if( (sp->state & LSTATE_INIT_DECODE) == 0 ) + tif->tif_setupdecode(tif); + + sp->stream.next_in = tif->tif_rawdata; + sp->stream.avail_in = (size_t) tif->tif_rawcc; + if ((tmsize_t)sp->stream.avail_in != tif->tif_rawcc) { + TIFFErrorExt(tif->tif_clientdata, module, + "Liblzma cannot deal with buffers this size"); + return 0; + } + + /* + * Disable memory limit when decoding. UINT64_MAX is a flag to disable + * the limit, we are passing (uint64_t)-1 which should be the same. + */ + ret = lzma_stream_decoder(&sp->stream, (uint64_t)-1, 0); + if (ret != LZMA_OK) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error initializing the stream decoder, %s", + LZMAStrerror(ret)); + return 0; + } + return 1; +} + +static int +LZMADecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s) +{ + static const char module[] = "LZMADecode"; + LZMAState* sp = DecoderState(tif); + + (void) s; + assert(sp != NULL); + assert(sp->state == LSTATE_INIT_DECODE); + + sp->stream.next_in = tif->tif_rawcp; + sp->stream.avail_in = (size_t) tif->tif_rawcc; + + sp->stream.next_out = op; + sp->stream.avail_out = (size_t) occ; + if ((tmsize_t)sp->stream.avail_out != occ) { + TIFFErrorExt(tif->tif_clientdata, module, + "Liblzma cannot deal with buffers this size"); + return 0; + } + + do { + /* + * Save the current stream state to properly recover from the + * decoding errors later. + */ + const uint8_t *next_in = sp->stream.next_in; + size_t avail_in = sp->stream.avail_in; + + lzma_ret ret = lzma_code(&sp->stream, LZMA_RUN); + if (ret == LZMA_STREAM_END) + break; + if (ret == LZMA_MEMLIMIT_ERROR) { + lzma_ret r = lzma_stream_decoder(&sp->stream, + lzma_memusage(&sp->stream), 0); + if (r != LZMA_OK) { + TIFFErrorExt(tif->tif_clientdata, module, + "Error initializing the stream decoder, %s", + LZMAStrerror(r)); + break; + } + sp->stream.next_in = next_in; + sp->stream.avail_in = avail_in; + continue; + } + if (ret != LZMA_OK) { + TIFFErrorExt(tif->tif_clientdata, module, + "Decoding error at scanline %lu, %s", + (unsigned long) tif->tif_row, LZMAStrerror(ret)); + break; + } + } while (sp->stream.avail_out > 0); + if (sp->stream.avail_out != 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at scanline %lu (short %lu bytes)", + (unsigned long) tif->tif_row, (unsigned long) sp->stream.avail_out); + return 0; + } + + tif->tif_rawcp = (uint8 *)sp->stream.next_in; /* cast away const */ + tif->tif_rawcc = sp->stream.avail_in; + + return 1; +} + +static int +LZMASetupEncode(TIFF* tif) +{ + LZMAState* sp = EncoderState(tif); + + assert(sp != NULL); + if (sp->state & LSTATE_INIT_DECODE) { + lzma_end(&sp->stream); + sp->state = 0; + } + + sp->state |= LSTATE_INIT_ENCODE; + return 1; +} + +/* + * Reset encoding state at the start of a strip. + */ +static int +LZMAPreEncode(TIFF* tif, uint16 s) +{ + static const char module[] = "LZMAPreEncode"; + LZMAState *sp = EncoderState(tif); + + (void) s; + assert(sp != NULL); + if( sp->state != LSTATE_INIT_ENCODE ) + tif->tif_setupencode(tif); + + sp->stream.next_out = tif->tif_rawdata; + sp->stream.avail_out = (size_t)tif->tif_rawdatasize; + if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize) { + TIFFErrorExt(tif->tif_clientdata, module, + "Liblzma cannot deal with buffers this size"); + return 0; + } + return (lzma_stream_encoder(&sp->stream, sp->filters, sp->check) == LZMA_OK); +} + +/* + * Encode a chunk of pixels. + */ +static int +LZMAEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + static const char module[] = "LZMAEncode"; + LZMAState *sp = EncoderState(tif); + + assert(sp != NULL); + assert(sp->state == LSTATE_INIT_ENCODE); + + (void) s; + sp->stream.next_in = bp; + sp->stream.avail_in = (size_t) cc; + if ((tmsize_t)sp->stream.avail_in != cc) { + TIFFErrorExt(tif->tif_clientdata, module, + "Liblzma cannot deal with buffers this size"); + return 0; + } + do { + lzma_ret ret = lzma_code(&sp->stream, LZMA_RUN); + if (ret != LZMA_OK) { + TIFFErrorExt(tif->tif_clientdata, module, + "Encoding error at scanline %lu, %s", + (unsigned long) tif->tif_row, LZMAStrerror(ret)); + return 0; + } + if (sp->stream.avail_out == 0) { + tif->tif_rawcc = tif->tif_rawdatasize; + TIFFFlushData1(tif); + sp->stream.next_out = tif->tif_rawdata; + sp->stream.avail_out = (size_t)tif->tif_rawdatasize; /* this is a safe typecast, as check is made already in LZMAPreEncode */ + } + } while (sp->stream.avail_in > 0); + return 1; +} + +/* + * Finish off an encoded strip by flushing the last + * string and tacking on an End Of Information code. + */ +static int +LZMAPostEncode(TIFF* tif) +{ + static const char module[] = "LZMAPostEncode"; + LZMAState *sp = EncoderState(tif); + lzma_ret ret; + + sp->stream.avail_in = 0; + do { + ret = lzma_code(&sp->stream, LZMA_FINISH); + switch (ret) { + case LZMA_STREAM_END: + case LZMA_OK: + if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize) { + tif->tif_rawcc = + tif->tif_rawdatasize - sp->stream.avail_out; + TIFFFlushData1(tif); + sp->stream.next_out = tif->tif_rawdata; + sp->stream.avail_out = (size_t)tif->tif_rawdatasize; /* this is a safe typecast, as check is made already in ZIPPreEncode */ + } + break; + default: + TIFFErrorExt(tif->tif_clientdata, module, "Liblzma error: %s", + LZMAStrerror(ret)); + return 0; + } + } while (ret != LZMA_STREAM_END); + return 1; +} + +static void +LZMACleanup(TIFF* tif) +{ + LZMAState* sp = LState(tif); + + assert(sp != 0); + + (void)TIFFPredictorCleanup(tif); + + tif->tif_tagmethods.vgetfield = sp->vgetparent; + tif->tif_tagmethods.vsetfield = sp->vsetparent; + + if (sp->state) { + lzma_end(&sp->stream); + sp->state = 0; + } + _TIFFfree(sp); + tif->tif_data = NULL; + + _TIFFSetDefaultCompressionState(tif); +} + +static int +LZMAVSetField(TIFF* tif, uint32 tag, va_list ap) +{ + static const char module[] = "LZMAVSetField"; + LZMAState* sp = LState(tif); + + switch (tag) { + case TIFFTAG_LZMAPRESET: + sp->preset = (int) va_arg(ap, int); + lzma_lzma_preset(&sp->opt_lzma, sp->preset); + if (sp->state & LSTATE_INIT_ENCODE) { + lzma_ret ret = lzma_stream_encoder(&sp->stream, + sp->filters, + sp->check); + if (ret != LZMA_OK) { + TIFFErrorExt(tif->tif_clientdata, module, + "Liblzma error: %s", + LZMAStrerror(ret)); + } + } + return 1; + default: + return (*sp->vsetparent)(tif, tag, ap); + } + /*NOTREACHED*/ +} + +static int +LZMAVGetField(TIFF* tif, uint32 tag, va_list ap) +{ + LZMAState* sp = LState(tif); + + switch (tag) { + case TIFFTAG_LZMAPRESET: + *va_arg(ap, int*) = sp->preset; + break; + default: + return (*sp->vgetparent)(tif, tag, ap); + } + return 1; +} + +static const TIFFField lzmaFields[] = { + { TIFFTAG_LZMAPRESET, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, + FIELD_PSEUDO, TRUE, FALSE, "LZMA2 Compression Preset", NULL }, +}; + +int +TIFFInitLZMA(TIFF* tif, int scheme) +{ + static const char module[] = "TIFFInitLZMA"; + LZMAState* sp; + lzma_stream tmp_stream = LZMA_STREAM_INIT; + + assert( scheme == COMPRESSION_LZMA ); + + /* + * Merge codec-specific tag information. + */ + if (!_TIFFMergeFields(tif, lzmaFields, TIFFArrayCount(lzmaFields))) { + TIFFErrorExt(tif->tif_clientdata, module, + "Merging LZMA2 codec-specific tags failed"); + return 0; + } + + /* + * Allocate state block so tag methods have storage to record values. + */ + tif->tif_data = (uint8*) _TIFFmalloc(sizeof(LZMAState)); + if (tif->tif_data == NULL) + goto bad; + sp = LState(tif); + memcpy(&sp->stream, &tmp_stream, sizeof(lzma_stream)); + + /* + * Override parent get/set field methods. + */ + sp->vgetparent = tif->tif_tagmethods.vgetfield; + tif->tif_tagmethods.vgetfield = LZMAVGetField; /* hook for codec tags */ + sp->vsetparent = tif->tif_tagmethods.vsetfield; + tif->tif_tagmethods.vsetfield = LZMAVSetField; /* hook for codec tags */ + + /* Default values for codec-specific fields */ + sp->preset = LZMA_PRESET_DEFAULT; /* default comp. level */ + sp->check = LZMA_CHECK_NONE; + sp->state = 0; + + /* Data filters. So far we are using delta and LZMA2 filters only. */ + sp->opt_delta.type = LZMA_DELTA_TYPE_BYTE; + /* + * The sample size in bytes seems to be reasonable distance for delta + * filter. + */ + sp->opt_delta.dist = (tif->tif_dir.td_bitspersample % 8) ? + 1 : tif->tif_dir.td_bitspersample / 8; + sp->filters[0].id = LZMA_FILTER_DELTA; + sp->filters[0].options = &sp->opt_delta; + + lzma_lzma_preset(&sp->opt_lzma, sp->preset); + sp->filters[1].id = LZMA_FILTER_LZMA2; + sp->filters[1].options = &sp->opt_lzma; + + sp->filters[2].id = LZMA_VLI_UNKNOWN; + sp->filters[2].options = NULL; + + /* + * Install codec methods. + */ + tif->tif_fixuptags = LZMAFixupTags; + tif->tif_setupdecode = LZMASetupDecode; + tif->tif_predecode = LZMAPreDecode; + tif->tif_decoderow = LZMADecode; + tif->tif_decodestrip = LZMADecode; + tif->tif_decodetile = LZMADecode; + tif->tif_setupencode = LZMASetupEncode; + tif->tif_preencode = LZMAPreEncode; + tif->tif_postencode = LZMAPostEncode; + tif->tif_encoderow = LZMAEncode; + tif->tif_encodestrip = LZMAEncode; + tif->tif_encodetile = LZMAEncode; + tif->tif_cleanup = LZMACleanup; + /* + * Setup predictor setup. + */ + (void) TIFFPredictorInit(tif); + return 1; +bad: + TIFFErrorExt(tif->tif_clientdata, module, + "No space for LZMA2 state block"); + return 0; +} +#endif /* LZMA_SUPORT */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_lzw.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_lzw.c new file mode 100644 index 000000000..b81bc7109 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_lzw.c @@ -0,0 +1,1169 @@ +/* $Id: tif_lzw.c,v 1.46 2014-11-20 16:47:21 erouault Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "tiffiop.h" +#ifdef LZW_SUPPORT +/* + * TIFF Library. + * Rev 5.0 Lempel-Ziv & Welch Compression Support + * + * This code is derived from the compress program whose code is + * derived from software contributed to Berkeley by James A. Woods, + * derived from original work by Spencer Thomas and Joseph Orost. + * + * The original Berkeley copyright notice appears below in its entirety. + */ +#include "tif_predict.h" + +#include + +/* + * NB: The 5.0 spec describes a different algorithm than Aldus + * implements. Specifically, Aldus does code length transitions + * one code earlier than should be done (for real LZW). + * Earlier versions of this library implemented the correct + * LZW algorithm, but emitted codes in a bit order opposite + * to the TIFF spec. Thus, to maintain compatibility w/ Aldus + * we interpret MSB-LSB ordered codes to be images written w/ + * old versions of this library, but otherwise adhere to the + * Aldus "off by one" algorithm. + * + * Future revisions to the TIFF spec are expected to "clarify this issue". + */ +#define LZW_COMPAT /* include backwards compatibility code */ +/* + * Each strip of data is supposed to be terminated by a CODE_EOI. + * If the following #define is included, the decoder will also + * check for end-of-strip w/o seeing this code. This makes the + * library more robust, but also slower. + */ +#define LZW_CHECKEOS /* include checks for strips w/o EOI code */ + +#define MAXCODE(n) ((1L<<(n))-1) +/* + * The TIFF spec specifies that encoded bit + * strings range from 9 to 12 bits. + */ +#define BITS_MIN 9 /* start with 9 bits */ +#define BITS_MAX 12 /* max of 12 bit strings */ +/* predefined codes */ +#define CODE_CLEAR 256 /* code to clear string table */ +#define CODE_EOI 257 /* end-of-information code */ +#define CODE_FIRST 258 /* first free code entry */ +#define CODE_MAX MAXCODE(BITS_MAX) +#define HSIZE 9001L /* 91% occupancy */ +#define HSHIFT (13-8) +#ifdef LZW_COMPAT +/* NB: +1024 is for compatibility with old files */ +#define CSIZE (MAXCODE(BITS_MAX)+1024L) +#else +#define CSIZE (MAXCODE(BITS_MAX)+1L) +#endif + +/* + * State block for each open TIFF file using LZW + * compression/decompression. Note that the predictor + * state block must be first in this data structure. + */ +typedef struct { + TIFFPredictorState predict; /* predictor super class */ + + unsigned short nbits; /* # of bits/code */ + unsigned short maxcode; /* maximum code for lzw_nbits */ + unsigned short free_ent; /* next free entry in hash table */ + long nextdata; /* next bits of i/o */ + long nextbits; /* # of valid bits in lzw_nextdata */ + + int rw_mode; /* preserve rw_mode from init */ +} LZWBaseState; + +#define lzw_nbits base.nbits +#define lzw_maxcode base.maxcode +#define lzw_free_ent base.free_ent +#define lzw_nextdata base.nextdata +#define lzw_nextbits base.nextbits + +/* + * Encoding-specific state. + */ +typedef uint16 hcode_t; /* codes fit in 16 bits */ +typedef struct { + long hash; + hcode_t code; +} hash_t; + +/* + * Decoding-specific state. + */ +typedef struct code_ent { + struct code_ent *next; + unsigned short length; /* string len, including this token */ + unsigned char value; /* data value */ + unsigned char firstchar; /* first token of string */ +} code_t; + +typedef int (*decodeFunc)(TIFF*, uint8*, tmsize_t, uint16); + +typedef struct { + LZWBaseState base; + + /* Decoding specific data */ + long dec_nbitsmask; /* lzw_nbits 1 bits, right adjusted */ + long dec_restart; /* restart count */ +#ifdef LZW_CHECKEOS + uint64 dec_bitsleft; /* available bits in raw data */ +#endif + decodeFunc dec_decode; /* regular or backwards compatible */ + code_t* dec_codep; /* current recognized code */ + code_t* dec_oldcodep; /* previously recognized code */ + code_t* dec_free_entp; /* next free entry */ + code_t* dec_maxcodep; /* max available entry */ + code_t* dec_codetab; /* kept separate for small machines */ + + /* Encoding specific data */ + int enc_oldcode; /* last code encountered */ + long enc_checkpoint; /* point at which to clear table */ +#define CHECK_GAP 10000 /* enc_ratio check interval */ + long enc_ratio; /* current compression ratio */ + long enc_incount; /* (input) data bytes encoded */ + long enc_outcount; /* encoded (output) bytes */ + uint8* enc_rawlimit; /* bound on tif_rawdata buffer */ + hash_t* enc_hashtab; /* kept separate for small machines */ +} LZWCodecState; + +#define LZWState(tif) ((LZWBaseState*) (tif)->tif_data) +#define DecoderState(tif) ((LZWCodecState*) LZWState(tif)) +#define EncoderState(tif) ((LZWCodecState*) LZWState(tif)) + +static int LZWDecode(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s); +#ifdef LZW_COMPAT +static int LZWDecodeCompat(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s); +#endif +static void cl_hash(LZWCodecState*); + +/* + * LZW Decoder. + */ + +#ifdef LZW_CHECKEOS +/* + * This check shouldn't be necessary because each + * strip is suppose to be terminated with CODE_EOI. + */ +#define NextCode(_tif, _sp, _bp, _code, _get) { \ + if ((_sp)->dec_bitsleft < (uint64)nbits) { \ + TIFFWarningExt(_tif->tif_clientdata, module, \ + "LZWDecode: Strip %d not terminated with EOI code", \ + _tif->tif_curstrip); \ + _code = CODE_EOI; \ + } else { \ + _get(_sp,_bp,_code); \ + (_sp)->dec_bitsleft -= nbits; \ + } \ +} +#else +#define NextCode(tif, sp, bp, code, get) get(sp, bp, code) +#endif + +static int +LZWFixupTags(TIFF* tif) +{ + (void) tif; + return (1); +} + +static int +LZWSetupDecode(TIFF* tif) +{ + static const char module[] = "LZWSetupDecode"; + LZWCodecState* sp = DecoderState(tif); + int code; + + if( sp == NULL ) + { + /* + * Allocate state block so tag methods have storage to record + * values. + */ + tif->tif_data = (uint8*) _TIFFmalloc(sizeof(LZWCodecState)); + if (tif->tif_data == NULL) + { + TIFFErrorExt(tif->tif_clientdata, module, "No space for LZW state block"); + return (0); + } + + DecoderState(tif)->dec_codetab = NULL; + DecoderState(tif)->dec_decode = NULL; + + /* + * Setup predictor setup. + */ + (void) TIFFPredictorInit(tif); + + sp = DecoderState(tif); + } + + assert(sp != NULL); + + if (sp->dec_codetab == NULL) { + sp->dec_codetab = (code_t*)_TIFFmalloc(CSIZE*sizeof (code_t)); + if (sp->dec_codetab == NULL) { + TIFFErrorExt(tif->tif_clientdata, module, + "No space for LZW code table"); + return (0); + } + /* + * Pre-load the table. + */ + code = 255; + do { + sp->dec_codetab[code].value = code; + sp->dec_codetab[code].firstchar = code; + sp->dec_codetab[code].length = 1; + sp->dec_codetab[code].next = NULL; + } while (code--); + /* + * Zero-out the unused entries + */ + _TIFFmemset(&sp->dec_codetab[CODE_CLEAR], 0, + (CODE_FIRST - CODE_CLEAR) * sizeof (code_t)); + } + return (1); +} + +/* + * Setup state for decoding a strip. + */ +static int +LZWPreDecode(TIFF* tif, uint16 s) +{ + static const char module[] = "LZWPreDecode"; + LZWCodecState *sp = DecoderState(tif); + + (void) s; + assert(sp != NULL); + if( sp->dec_codetab == NULL ) + { + tif->tif_setupdecode( tif ); + if( sp->dec_codetab == NULL ) + return (0); + } + + /* + * Check for old bit-reversed codes. + */ + if (tif->tif_rawdata[0] == 0 && (tif->tif_rawdata[1] & 0x1)) { +#ifdef LZW_COMPAT + if (!sp->dec_decode) { + TIFFWarningExt(tif->tif_clientdata, module, + "Old-style LZW codes, convert file"); + /* + * Override default decoding methods with + * ones that deal with the old coding. + * Otherwise the predictor versions set + * above will call the compatibility routines + * through the dec_decode method. + */ + tif->tif_decoderow = LZWDecodeCompat; + tif->tif_decodestrip = LZWDecodeCompat; + tif->tif_decodetile = LZWDecodeCompat; + /* + * If doing horizontal differencing, must + * re-setup the predictor logic since we + * switched the basic decoder methods... + */ + (*tif->tif_setupdecode)(tif); + sp->dec_decode = LZWDecodeCompat; + } + sp->lzw_maxcode = MAXCODE(BITS_MIN); +#else /* !LZW_COMPAT */ + if (!sp->dec_decode) { + TIFFErrorExt(tif->tif_clientdata, module, + "Old-style LZW codes not supported"); + sp->dec_decode = LZWDecode; + } + return (0); +#endif/* !LZW_COMPAT */ + } else { + sp->lzw_maxcode = MAXCODE(BITS_MIN)-1; + sp->dec_decode = LZWDecode; + } + sp->lzw_nbits = BITS_MIN; + sp->lzw_nextbits = 0; + sp->lzw_nextdata = 0; + + sp->dec_restart = 0; + sp->dec_nbitsmask = MAXCODE(BITS_MIN); +#ifdef LZW_CHECKEOS + sp->dec_bitsleft = ((uint64)tif->tif_rawcc) << 3; +#endif + sp->dec_free_entp = sp->dec_codetab + CODE_FIRST; + /* + * Zero entries that are not yet filled in. We do + * this to guard against bogus input data that causes + * us to index into undefined entries. If you can + * come up with a way to safely bounds-check input codes + * while decoding then you can remove this operation. + */ + _TIFFmemset(sp->dec_free_entp, 0, (CSIZE-CODE_FIRST)*sizeof (code_t)); + sp->dec_oldcodep = &sp->dec_codetab[-1]; + sp->dec_maxcodep = &sp->dec_codetab[sp->dec_nbitsmask-1]; + return (1); +} + +/* + * Decode a "hunk of data". + */ +#define GetNextCode(sp, bp, code) { \ + nextdata = (nextdata<<8) | *(bp)++; \ + nextbits += 8; \ + if (nextbits < nbits) { \ + nextdata = (nextdata<<8) | *(bp)++; \ + nextbits += 8; \ + } \ + code = (hcode_t)((nextdata >> (nextbits-nbits)) & nbitsmask); \ + nextbits -= nbits; \ +} + +static void +codeLoop(TIFF* tif, const char* module) +{ + TIFFErrorExt(tif->tif_clientdata, module, + "Bogus encoding, loop in the code table; scanline %d", + tif->tif_row); +} + +static int +LZWDecode(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s) +{ + static const char module[] = "LZWDecode"; + LZWCodecState *sp = DecoderState(tif); + char *op = (char*) op0; + long occ = (long) occ0; + char *tp; + unsigned char *bp; + hcode_t code; + int len; + long nbits, nextbits, nextdata, nbitsmask; + code_t *codep, *free_entp, *maxcodep, *oldcodep; + + (void) s; + assert(sp != NULL); + assert(sp->dec_codetab != NULL); + + /* + Fail if value does not fit in long. + */ + if ((tmsize_t) occ != occ0) + return (0); + /* + * Restart interrupted output operation. + */ + if (sp->dec_restart) { + long residue; + + codep = sp->dec_codep; + residue = codep->length - sp->dec_restart; + if (residue > occ) { + /* + * Residue from previous decode is sufficient + * to satisfy decode request. Skip to the + * start of the decoded string, place decoded + * values in the output buffer, and return. + */ + sp->dec_restart += occ; + do { + codep = codep->next; + } while (--residue > occ && codep); + if (codep) { + tp = op + occ; + do { + *--tp = codep->value; + codep = codep->next; + } while (--occ && codep); + } + return (1); + } + /* + * Residue satisfies only part of the decode request. + */ + op += residue, occ -= residue; + tp = op; + do { + int t; + --tp; + t = codep->value; + codep = codep->next; + *tp = t; + } while (--residue && codep); + sp->dec_restart = 0; + } + + bp = (unsigned char *)tif->tif_rawcp; + nbits = sp->lzw_nbits; + nextdata = sp->lzw_nextdata; + nextbits = sp->lzw_nextbits; + nbitsmask = sp->dec_nbitsmask; + oldcodep = sp->dec_oldcodep; + free_entp = sp->dec_free_entp; + maxcodep = sp->dec_maxcodep; + + while (occ > 0) { + NextCode(tif, sp, bp, code, GetNextCode); + if (code == CODE_EOI) + break; + if (code == CODE_CLEAR) { + free_entp = sp->dec_codetab + CODE_FIRST; + _TIFFmemset(free_entp, 0, + (CSIZE - CODE_FIRST) * sizeof (code_t)); + nbits = BITS_MIN; + nbitsmask = MAXCODE(BITS_MIN); + maxcodep = sp->dec_codetab + nbitsmask-1; + NextCode(tif, sp, bp, code, GetNextCode); + if (code == CODE_EOI) + break; + if (code >= CODE_CLEAR) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "LZWDecode: Corrupted LZW table at scanline %d", + tif->tif_row); + return (0); + } + *op++ = (char)code, occ--; + oldcodep = sp->dec_codetab + code; + continue; + } + codep = sp->dec_codetab + code; + + /* + * Add the new entry to the code table. + */ + if (free_entp < &sp->dec_codetab[0] || + free_entp >= &sp->dec_codetab[CSIZE]) { + TIFFErrorExt(tif->tif_clientdata, module, + "Corrupted LZW table at scanline %d", + tif->tif_row); + return (0); + } + + free_entp->next = oldcodep; + if (free_entp->next < &sp->dec_codetab[0] || + free_entp->next >= &sp->dec_codetab[CSIZE]) { + TIFFErrorExt(tif->tif_clientdata, module, + "Corrupted LZW table at scanline %d", + tif->tif_row); + return (0); + } + free_entp->firstchar = free_entp->next->firstchar; + free_entp->length = free_entp->next->length+1; + free_entp->value = (codep < free_entp) ? + codep->firstchar : free_entp->firstchar; + if (++free_entp > maxcodep) { + if (++nbits > BITS_MAX) /* should not happen */ + nbits = BITS_MAX; + nbitsmask = MAXCODE(nbits); + maxcodep = sp->dec_codetab + nbitsmask-1; + } + oldcodep = codep; + if (code >= 256) { + /* + * Code maps to a string, copy string + * value to output (written in reverse). + */ + if(codep->length == 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "Wrong length of decoded string: " + "data probably corrupted at scanline %d", + tif->tif_row); + return (0); + } + if (codep->length > occ) { + /* + * String is too long for decode buffer, + * locate portion that will fit, copy to + * the decode buffer, and setup restart + * logic for the next decoding call. + */ + sp->dec_codep = codep; + do { + codep = codep->next; + } while (codep && codep->length > occ); + if (codep) { + sp->dec_restart = (long)occ; + tp = op + occ; + do { + *--tp = codep->value; + codep = codep->next; + } while (--occ && codep); + if (codep) + codeLoop(tif, module); + } + break; + } + len = codep->length; + tp = op + len; + do { + int t; + --tp; + t = codep->value; + codep = codep->next; + *tp = t; + } while (codep && tp > op); + if (codep) { + codeLoop(tif, module); + break; + } + assert(occ >= len); + op += len, occ -= len; + } else + *op++ = (char)code, occ--; + } + + tif->tif_rawcp = (uint8*) bp; + sp->lzw_nbits = (unsigned short) nbits; + sp->lzw_nextdata = nextdata; + sp->lzw_nextbits = nextbits; + sp->dec_nbitsmask = nbitsmask; + sp->dec_oldcodep = oldcodep; + sp->dec_free_entp = free_entp; + sp->dec_maxcodep = maxcodep; + + if (occ > 0) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at scanline %d (short %I64d bytes)", + tif->tif_row, (unsigned __int64) occ); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at scanline %d (short %llu bytes)", + tif->tif_row, (unsigned long long) occ); +#endif + return (0); + } + return (1); +} + +#ifdef LZW_COMPAT +/* + * Decode a "hunk of data" for old images. + */ +#define GetNextCodeCompat(sp, bp, code) { \ + nextdata |= (unsigned long) *(bp)++ << nextbits; \ + nextbits += 8; \ + if (nextbits < nbits) { \ + nextdata |= (unsigned long) *(bp)++ << nextbits;\ + nextbits += 8; \ + } \ + code = (hcode_t)(nextdata & nbitsmask); \ + nextdata >>= nbits; \ + nextbits -= nbits; \ +} + +static int +LZWDecodeCompat(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s) +{ + static const char module[] = "LZWDecodeCompat"; + LZWCodecState *sp = DecoderState(tif); + char *op = (char*) op0; + long occ = (long) occ0; + char *tp; + unsigned char *bp; + int code, nbits; + long nextbits, nextdata, nbitsmask; + code_t *codep, *free_entp, *maxcodep, *oldcodep; + + (void) s; + assert(sp != NULL); + + /* + Fail if value does not fit in long. + */ + if ((tmsize_t) occ != occ0) + return (0); + + /* + * Restart interrupted output operation. + */ + if (sp->dec_restart) { + long residue; + + codep = sp->dec_codep; + residue = codep->length - sp->dec_restart; + if (residue > occ) { + /* + * Residue from previous decode is sufficient + * to satisfy decode request. Skip to the + * start of the decoded string, place decoded + * values in the output buffer, and return. + */ + sp->dec_restart += occ; + do { + codep = codep->next; + } while (--residue > occ); + tp = op + occ; + do { + *--tp = codep->value; + codep = codep->next; + } while (--occ); + return (1); + } + /* + * Residue satisfies only part of the decode request. + */ + op += residue, occ -= residue; + tp = op; + do { + *--tp = codep->value; + codep = codep->next; + } while (--residue); + sp->dec_restart = 0; + } + + bp = (unsigned char *)tif->tif_rawcp; + nbits = sp->lzw_nbits; + nextdata = sp->lzw_nextdata; + nextbits = sp->lzw_nextbits; + nbitsmask = sp->dec_nbitsmask; + oldcodep = sp->dec_oldcodep; + free_entp = sp->dec_free_entp; + maxcodep = sp->dec_maxcodep; + + while (occ > 0) { + NextCode(tif, sp, bp, code, GetNextCodeCompat); + if (code == CODE_EOI) + break; + if (code == CODE_CLEAR) { + free_entp = sp->dec_codetab + CODE_FIRST; + _TIFFmemset(free_entp, 0, + (CSIZE - CODE_FIRST) * sizeof (code_t)); + nbits = BITS_MIN; + nbitsmask = MAXCODE(BITS_MIN); + maxcodep = sp->dec_codetab + nbitsmask; + NextCode(tif, sp, bp, code, GetNextCodeCompat); + if (code == CODE_EOI) + break; + if (code >= CODE_CLEAR) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "LZWDecode: Corrupted LZW table at scanline %d", + tif->tif_row); + return (0); + } + *op++ = code, occ--; + oldcodep = sp->dec_codetab + code; + continue; + } + codep = sp->dec_codetab + code; + + /* + * Add the new entry to the code table. + */ + if (free_entp < &sp->dec_codetab[0] || + free_entp >= &sp->dec_codetab[CSIZE]) { + TIFFErrorExt(tif->tif_clientdata, module, + "Corrupted LZW table at scanline %d", tif->tif_row); + return (0); + } + + free_entp->next = oldcodep; + if (free_entp->next < &sp->dec_codetab[0] || + free_entp->next >= &sp->dec_codetab[CSIZE]) { + TIFFErrorExt(tif->tif_clientdata, module, + "Corrupted LZW table at scanline %d", tif->tif_row); + return (0); + } + free_entp->firstchar = free_entp->next->firstchar; + free_entp->length = free_entp->next->length+1; + free_entp->value = (codep < free_entp) ? + codep->firstchar : free_entp->firstchar; + if (++free_entp > maxcodep) { + if (++nbits > BITS_MAX) /* should not happen */ + nbits = BITS_MAX; + nbitsmask = MAXCODE(nbits); + maxcodep = sp->dec_codetab + nbitsmask; + } + oldcodep = codep; + if (code >= 256) { + /* + * Code maps to a string, copy string + * value to output (written in reverse). + */ + if(codep->length == 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "Wrong length of decoded " + "string: data probably corrupted at scanline %d", + tif->tif_row); + return (0); + } + if (codep->length > occ) { + /* + * String is too long for decode buffer, + * locate portion that will fit, copy to + * the decode buffer, and setup restart + * logic for the next decoding call. + */ + sp->dec_codep = codep; + do { + codep = codep->next; + } while (codep->length > occ); + sp->dec_restart = occ; + tp = op + occ; + do { + *--tp = codep->value; + codep = codep->next; + } while (--occ); + break; + } + assert(occ >= codep->length); + op += codep->length, occ -= codep->length; + tp = op; + do { + *--tp = codep->value; + } while( (codep = codep->next) != NULL ); + } else + *op++ = code, occ--; + } + + tif->tif_rawcp = (uint8*) bp; + sp->lzw_nbits = nbits; + sp->lzw_nextdata = nextdata; + sp->lzw_nextbits = nextbits; + sp->dec_nbitsmask = nbitsmask; + sp->dec_oldcodep = oldcodep; + sp->dec_free_entp = free_entp; + sp->dec_maxcodep = maxcodep; + + if (occ > 0) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at scanline %d (short %I64d bytes)", + tif->tif_row, (unsigned __int64) occ); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at scanline %d (short %llu bytes)", + tif->tif_row, (unsigned long long) occ); +#endif + return (0); + } + return (1); +} +#endif /* LZW_COMPAT */ + +/* + * LZW Encoding. + */ + +static int +LZWSetupEncode(TIFF* tif) +{ + static const char module[] = "LZWSetupEncode"; + LZWCodecState* sp = EncoderState(tif); + + assert(sp != NULL); + sp->enc_hashtab = (hash_t*) _TIFFmalloc(HSIZE*sizeof (hash_t)); + if (sp->enc_hashtab == NULL) { + TIFFErrorExt(tif->tif_clientdata, module, + "No space for LZW hash table"); + return (0); + } + return (1); +} + +/* + * Reset encoding state at the start of a strip. + */ +static int +LZWPreEncode(TIFF* tif, uint16 s) +{ + LZWCodecState *sp = EncoderState(tif); + + (void) s; + assert(sp != NULL); + + if( sp->enc_hashtab == NULL ) + { + tif->tif_setupencode( tif ); + } + + sp->lzw_nbits = BITS_MIN; + sp->lzw_maxcode = MAXCODE(BITS_MIN); + sp->lzw_free_ent = CODE_FIRST; + sp->lzw_nextbits = 0; + sp->lzw_nextdata = 0; + sp->enc_checkpoint = CHECK_GAP; + sp->enc_ratio = 0; + sp->enc_incount = 0; + sp->enc_outcount = 0; + /* + * The 4 here insures there is space for 2 max-sized + * codes in LZWEncode and LZWPostDecode. + */ + sp->enc_rawlimit = tif->tif_rawdata + tif->tif_rawdatasize-1 - 4; + cl_hash(sp); /* clear hash table */ + sp->enc_oldcode = (hcode_t) -1; /* generates CODE_CLEAR in LZWEncode */ + return (1); +} + +#define CALCRATIO(sp, rat) { \ + if (incount > 0x007fffff) { /* NB: shift will overflow */\ + rat = outcount >> 8; \ + rat = (rat == 0 ? 0x7fffffff : incount/rat); \ + } else \ + rat = (incount<<8) / outcount; \ +} +#define PutNextCode(op, c) { \ + nextdata = (nextdata << nbits) | c; \ + nextbits += nbits; \ + *op++ = (unsigned char)(nextdata >> (nextbits-8)); \ + nextbits -= 8; \ + if (nextbits >= 8) { \ + *op++ = (unsigned char)(nextdata >> (nextbits-8)); \ + nextbits -= 8; \ + } \ + outcount += nbits; \ +} + +/* + * Encode a chunk of pixels. + * + * Uses an open addressing double hashing (no chaining) on the + * prefix code/next character combination. We do a variant of + * Knuth's algorithm D (vol. 3, sec. 6.4) along with G. Knott's + * relatively-prime secondary probe. Here, the modular division + * first probe is gives way to a faster exclusive-or manipulation. + * Also do block compression with an adaptive reset, whereby the + * code table is cleared when the compression ratio decreases, + * but after the table fills. The variable-length output codes + * are re-sized at this point, and a CODE_CLEAR is generated + * for the decoder. + */ +static int +LZWEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + register LZWCodecState *sp = EncoderState(tif); + register long fcode; + register hash_t *hp; + register int h, c; + hcode_t ent; + long disp; + long incount, outcount, checkpoint; + long nextdata, nextbits; + int free_ent, maxcode, nbits; + uint8* op; + uint8* limit; + + (void) s; + if (sp == NULL) + return (0); + + assert(sp->enc_hashtab != NULL); + + /* + * Load local state. + */ + incount = sp->enc_incount; + outcount = sp->enc_outcount; + checkpoint = sp->enc_checkpoint; + nextdata = sp->lzw_nextdata; + nextbits = sp->lzw_nextbits; + free_ent = sp->lzw_free_ent; + maxcode = sp->lzw_maxcode; + nbits = sp->lzw_nbits; + op = tif->tif_rawcp; + limit = sp->enc_rawlimit; + ent = sp->enc_oldcode; + + if (ent == (hcode_t) -1 && cc > 0) { + /* + * NB: This is safe because it can only happen + * at the start of a strip where we know there + * is space in the data buffer. + */ + PutNextCode(op, CODE_CLEAR); + ent = *bp++; cc--; incount++; + } + while (cc > 0) { + c = *bp++; cc--; incount++; + fcode = ((long)c << BITS_MAX) + ent; + h = (c << HSHIFT) ^ ent; /* xor hashing */ +#ifdef _WINDOWS + /* + * Check hash index for an overflow. + */ + if (h >= HSIZE) + h -= HSIZE; +#endif + hp = &sp->enc_hashtab[h]; + if (hp->hash == fcode) { + ent = hp->code; + continue; + } + if (hp->hash >= 0) { + /* + * Primary hash failed, check secondary hash. + */ + disp = HSIZE - h; + if (h == 0) + disp = 1; + do { + /* + * Avoid pointer arithmetic 'cuz of + * wraparound problems with segments. + */ + if ((h -= disp) < 0) + h += HSIZE; + hp = &sp->enc_hashtab[h]; + if (hp->hash == fcode) { + ent = hp->code; + goto hit; + } + } while (hp->hash >= 0); + } + /* + * New entry, emit code and add to table. + */ + /* + * Verify there is space in the buffer for the code + * and any potential Clear code that might be emitted + * below. The value of limit is setup so that there + * are at least 4 bytes free--room for 2 codes. + */ + if (op > limit) { + tif->tif_rawcc = (tmsize_t)(op - tif->tif_rawdata); + TIFFFlushData1(tif); + op = tif->tif_rawdata; + } + PutNextCode(op, ent); + ent = c; + hp->code = free_ent++; + hp->hash = fcode; + if (free_ent == CODE_MAX-1) { + /* table is full, emit clear code and reset */ + cl_hash(sp); + sp->enc_ratio = 0; + incount = 0; + outcount = 0; + free_ent = CODE_FIRST; + PutNextCode(op, CODE_CLEAR); + nbits = BITS_MIN; + maxcode = MAXCODE(BITS_MIN); + } else { + /* + * If the next entry is going to be too big for + * the code size, then increase it, if possible. + */ + if (free_ent > maxcode) { + nbits++; + assert(nbits <= BITS_MAX); + maxcode = (int) MAXCODE(nbits); + } else if (incount >= checkpoint) { + long rat; + /* + * Check compression ratio and, if things seem + * to be slipping, clear the hash table and + * reset state. The compression ratio is a + * 24+8-bit fractional number. + */ + checkpoint = incount+CHECK_GAP; + CALCRATIO(sp, rat); + if (rat <= sp->enc_ratio) { + cl_hash(sp); + sp->enc_ratio = 0; + incount = 0; + outcount = 0; + free_ent = CODE_FIRST; + PutNextCode(op, CODE_CLEAR); + nbits = BITS_MIN; + maxcode = MAXCODE(BITS_MIN); + } else + sp->enc_ratio = rat; + } + } + hit: + ; + } + + /* + * Restore global state. + */ + sp->enc_incount = incount; + sp->enc_outcount = outcount; + sp->enc_checkpoint = checkpoint; + sp->enc_oldcode = ent; + sp->lzw_nextdata = nextdata; + sp->lzw_nextbits = nextbits; + sp->lzw_free_ent = free_ent; + sp->lzw_maxcode = maxcode; + sp->lzw_nbits = nbits; + tif->tif_rawcp = op; + return (1); +} + +/* + * Finish off an encoded strip by flushing the last + * string and tacking on an End Of Information code. + */ +static int +LZWPostEncode(TIFF* tif) +{ + register LZWCodecState *sp = EncoderState(tif); + uint8* op = tif->tif_rawcp; + long nextbits = sp->lzw_nextbits; + long nextdata = sp->lzw_nextdata; + long outcount = sp->enc_outcount; + int nbits = sp->lzw_nbits; + + if (op > sp->enc_rawlimit) { + tif->tif_rawcc = (tmsize_t)(op - tif->tif_rawdata); + TIFFFlushData1(tif); + op = tif->tif_rawdata; + } + if (sp->enc_oldcode != (hcode_t) -1) { + PutNextCode(op, sp->enc_oldcode); + sp->enc_oldcode = (hcode_t) -1; + } + PutNextCode(op, CODE_EOI); + if (nextbits > 0) + *op++ = (unsigned char)(nextdata << (8-nextbits)); + tif->tif_rawcc = (tmsize_t)(op - tif->tif_rawdata); + return (1); +} + +/* + * Reset encoding hash table. + */ +static void +cl_hash(LZWCodecState* sp) +{ + register hash_t *hp = &sp->enc_hashtab[HSIZE-1]; + register long i = HSIZE-8; + + do { + i -= 8; + hp[-7].hash = -1; + hp[-6].hash = -1; + hp[-5].hash = -1; + hp[-4].hash = -1; + hp[-3].hash = -1; + hp[-2].hash = -1; + hp[-1].hash = -1; + hp[ 0].hash = -1; + hp -= 8; + } while (i >= 0); + for (i += 8; i > 0; i--, hp--) + hp->hash = -1; +} + +static void +LZWCleanup(TIFF* tif) +{ + (void)TIFFPredictorCleanup(tif); + + assert(tif->tif_data != 0); + + if (DecoderState(tif)->dec_codetab) + _TIFFfree(DecoderState(tif)->dec_codetab); + + if (EncoderState(tif)->enc_hashtab) + _TIFFfree(EncoderState(tif)->enc_hashtab); + + _TIFFfree(tif->tif_data); + tif->tif_data = NULL; + + _TIFFSetDefaultCompressionState(tif); +} + +int +TIFFInitLZW(TIFF* tif, int scheme) +{ + static const char module[] = "TIFFInitLZW"; + assert(scheme == COMPRESSION_LZW); + /* + * Allocate state block so tag methods have storage to record values. + */ + tif->tif_data = (uint8*) _TIFFmalloc(sizeof (LZWCodecState)); + if (tif->tif_data == NULL) + goto bad; + DecoderState(tif)->dec_codetab = NULL; + DecoderState(tif)->dec_decode = NULL; + EncoderState(tif)->enc_hashtab = NULL; + LZWState(tif)->rw_mode = tif->tif_mode; + + /* + * Install codec methods. + */ + tif->tif_fixuptags = LZWFixupTags; + tif->tif_setupdecode = LZWSetupDecode; + tif->tif_predecode = LZWPreDecode; + tif->tif_decoderow = LZWDecode; + tif->tif_decodestrip = LZWDecode; + tif->tif_decodetile = LZWDecode; + tif->tif_setupencode = LZWSetupEncode; + tif->tif_preencode = LZWPreEncode; + tif->tif_postencode = LZWPostEncode; + tif->tif_encoderow = LZWEncode; + tif->tif_encodestrip = LZWEncode; + tif->tif_encodetile = LZWEncode; + tif->tif_cleanup = LZWCleanup; + /* + * Setup predictor setup. + */ + (void) TIFFPredictorInit(tif); + return (1); +bad: + TIFFErrorExt(tif->tif_clientdata, module, + "No space for LZW state block"); + return (0); +} + +/* + * Copyright (c) 1985, 1986 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James A. Woods, derived from original work by Spencer Thomas + * and Joseph Orost. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#endif /* LZW_SUPPORT */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_next.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_next.c new file mode 100644 index 000000000..17e031111 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_next.c @@ -0,0 +1,181 @@ +/* $Id: tif_next.c,v 1.16 2014-12-29 12:09:11 erouault Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "tiffiop.h" +#ifdef NEXT_SUPPORT +/* + * TIFF Library. + * + * NeXT 2-bit Grey Scale Compression Algorithm Support + */ + +#define SETPIXEL(op, v) { \ + switch (npixels++ & 3) { \ + case 0: op[0] = (unsigned char) ((v) << 6); break; \ + case 1: op[0] |= (v) << 4; break; \ + case 2: op[0] |= (v) << 2; break; \ + case 3: *op++ |= (v); break; \ + } \ +} + +#define LITERALROW 0x00 +#define LITERALSPAN 0x40 +#define WHITE ((1<<2)-1) + +static int +NeXTDecode(TIFF* tif, uint8* buf, tmsize_t occ, uint16 s) +{ + static const char module[] = "NeXTDecode"; + unsigned char *bp, *op; + tmsize_t cc; + uint8* row; + tmsize_t scanline, n; + + (void) s; + /* + * Each scanline is assumed to start off as all + * white (we assume a PhotometricInterpretation + * of ``min-is-black''). + */ + for (op = (unsigned char*) buf, cc = occ; cc-- > 0;) + *op++ = 0xff; + + bp = (unsigned char *)tif->tif_rawcp; + cc = tif->tif_rawcc; + scanline = tif->tif_scanlinesize; + if (occ % scanline) + { + TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be read"); + return (0); + } + for (row = buf; cc > 0 && occ > 0; occ -= scanline, row += scanline) { + n = *bp++, cc--; + switch (n) { + case LITERALROW: + /* + * The entire scanline is given as literal values. + */ + if (cc < scanline) + goto bad; + _TIFFmemcpy(row, bp, scanline); + bp += scanline; + cc -= scanline; + break; + case LITERALSPAN: { + tmsize_t off; + /* + * The scanline has a literal span that begins at some + * offset. + */ + if( cc < 4 ) + goto bad; + off = (bp[0] * 256) + bp[1]; + n = (bp[2] * 256) + bp[3]; + if (cc < 4+n || off+n > scanline) + goto bad; + _TIFFmemcpy(row+off, bp+4, n); + bp += 4+n; + cc -= 4+n; + break; + } + default: { + uint32 npixels = 0, grey; + uint32 imagewidth = tif->tif_dir.td_imagewidth; + if( isTiled(tif) ) + imagewidth = tif->tif_dir.td_tilewidth; + + /* + * The scanline is composed of a sequence of constant + * color ``runs''. We shift into ``run mode'' and + * interpret bytes as codes of the form + * until we've filled the scanline. + */ + op = row; + for (;;) { + grey = (uint32)((n>>6) & 0x3); + n &= 0x3f; + /* + * Ensure the run does not exceed the scanline + * bounds, potentially resulting in a security + * issue. + */ + while (n-- > 0 && npixels < imagewidth) + SETPIXEL(op, grey); + if (npixels >= imagewidth) + break; + if (cc == 0) + goto bad; + n = *bp++, cc--; + } + break; + } + } + } + tif->tif_rawcp = (uint8*) bp; + tif->tif_rawcc = cc; + return (1); +bad: + TIFFErrorExt(tif->tif_clientdata, module, "Not enough data for scanline %ld", + (long) tif->tif_row); + return (0); +} + +static int +NeXTPreDecode(TIFF* tif, uint16 s) +{ + static const char module[] = "NeXTPreDecode"; + TIFFDirectory *td = &tif->tif_dir; + (void)s; + + if( td->td_bitspersample != 2 ) + { + TIFFErrorExt(tif->tif_clientdata, module, "Unsupported BitsPerSample = %d", + td->td_bitspersample); + return (0); + } + return (1); +} + +int +TIFFInitNeXT(TIFF* tif, int scheme) +{ + (void) scheme; + tif->tif_predecode = NeXTPreDecode; + tif->tif_decoderow = NeXTDecode; + tif->tif_decodestrip = NeXTDecode; + tif->tif_decodetile = NeXTDecode; + return (1); +} +#endif /* NEXT_SUPPORT */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_ojpeg.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_ojpeg.c new file mode 100644 index 000000000..61f422101 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_ojpeg.c @@ -0,0 +1,2501 @@ +/* $Id: tif_ojpeg.c,v 1.58 2014-12-25 18:29:11 erouault Exp $ */ + +/* WARNING: The type of JPEG encapsulation defined by the TIFF Version 6.0 + specification is now totally obsolete and deprecated for new applications and + images. This file was was created solely in order to read unconverted images + still present on some users' computer systems. It will never be extended + to write such files. Writing new-style JPEG compressed TIFFs is implemented + in tif_jpeg.c. + + The code is carefully crafted to robustly read all gathered JPEG-in-TIFF + testfiles, and anticipate as much as possible all other... But still, it may + fail on some. If you encounter problems, please report them on the TIFF + mailing list and/or to Joris Van Damme . + + Please read the file called "TIFF Technical Note #2" if you need to be + convinced this compression scheme is bad and breaks TIFF. That document + is linked to from the LibTiff site + and from AWare Systems' TIFF section + . It is also absorbed + in Adobe's specification supplements, marked "draft" up to this day, but + supported by the TIFF community. + + This file interfaces with Release 6B of the JPEG Library written by the + Independent JPEG Group. Previous versions of this file required a hack inside + the LibJpeg library. This version no longer requires that. Remember to + remove the hack if you update from the old version. + + Copyright (c) Joris Van Damme + Copyright (c) AWare Systems + + The licence agreement for this file is the same as the rest of the LibTiff + library. + + IN NO EVENT SHALL JORIS VAN DAMME OR AWARE SYSTEMS BE LIABLE FOR + ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + OF THIS SOFTWARE. + + Joris Van Damme and/or AWare Systems may be available for custom + development. If you like what you see, and need anything similar or related, + contact . +*/ + +/* What is what, and what is not? + + This decoder starts with an input stream, that is essentially the JpegInterchangeFormat + stream, if any, followed by the strile data, if any. This stream is read in + OJPEGReadByte and related functions. + + It analyzes the start of this stream, until it encounters non-marker data, i.e. + compressed image data. Some of the header markers it sees have no actual content, + like the SOI marker, and APP/COM markers that really shouldn't even be there. Some + other markers do have content, and the valuable bits and pieces of information + in these markers are saved, checking all to verify that the stream is more or + less within expected bounds. This happens inside the OJPEGReadHeaderInfoSecStreamXxx + functions. + + Some OJPEG imagery contains no valid JPEG header markers. This situation is picked + up on if we've seen no SOF marker when we're at the start of the compressed image + data. In this case, the tables are read from JpegXxxTables tags, and the other + bits and pieces of information is initialized to its most basic value. This is + implemented in the OJPEGReadHeaderInfoSecTablesXxx functions. + + When this is complete, a good and valid JPEG header can be assembled, and this is + passed through to LibJpeg. When that's done, the remainder of the input stream, i.e. + the compressed image data, can be passed through unchanged. This is done in + OJPEGWriteStream functions. + + LibTiff rightly expects to know the subsampling values before decompression. Just like + in new-style JPEG-in-TIFF, though, or even more so, actually, the YCbCrsubsampling + tag is notoriously unreliable. To correct these tag values with the ones inside + the JPEG stream, the first part of the input stream is pre-scanned in + OJPEGSubsamplingCorrect, making no note of any other data, reporting no warnings + or errors, up to the point where either these values are read, or it's clear they + aren't there. This means that some of the data is read twice, but we feel speed + in correcting these values is important enough to warrant this sacrifice. Allthough + there is currently no define or other configuration mechanism to disable this behaviour, + the actual header scanning is build to robustly respond with error report if it + should encounter an uncorrected mismatch of subsampling values. See + OJPEGReadHeaderInfoSecStreamSof. + + The restart interval and restart markers are the most tricky part... The restart + interval can be specified in a tag. It can also be set inside the input JPEG stream. + It can be used inside the input JPEG stream. If reading from strile data, we've + consistenly discovered the need to insert restart markers in between the different + striles, as is also probably the most likely interpretation of the original TIFF 6.0 + specification. With all this setting of interval, and actual use of markers that is not + predictable at the time of valid JPEG header assembly, the restart thing may turn + out the Achilles heel of this implementation. Fortunately, most OJPEG writer vendors + succeed in reading back what they write, which may be the reason why we've been able + to discover ways that seem to work. + + Some special provision is made for planarconfig separate OJPEG files. These seem + to consistently contain header info, a SOS marker, a plane, SOS marker, plane, SOS, + and plane. This may or may not be a valid JPEG configuration, we don't know and don't + care. We want LibTiff to be able to access the planes individually, without huge + buffering inside LibJpeg, anyway. So we compose headers to feed to LibJpeg, in this + case, that allow us to pass a single plane such that LibJpeg sees a valid + single-channel JPEG stream. Locating subsequent SOS markers, and thus subsequent + planes, is done inside OJPEGReadSecondarySos. + + The benefit of the scheme is... that it works, basically. We know of no other that + does. It works without checking software tag, or otherwise going about things in an + OJPEG flavor specific manner. Instead, it is a single scheme, that covers the cases + with and without JpegInterchangeFormat, with and without striles, with part of + the header in JpegInterchangeFormat and remainder in first strile, etc. It is forgiving + and robust, may likely work with OJPEG flavors we've not seen yet, and makes most out + of the data. + + Another nice side-effect is that a complete JPEG single valid stream is build if + planarconfig is not separate (vast majority). We may one day use that to build + converters to JPEG, and/or to new-style JPEG compression inside TIFF. + + A dissadvantage is the lack of random access to the individual striles. This is the + reason for much of the complicated restart-and-position stuff inside OJPEGPreDecode. + Applications would do well accessing all striles in order, as this will result in + a single sequential scan of the input stream, and no restarting of LibJpeg decoding + session. +*/ + +#define WIN32_LEAN_AND_MEAN +#define VC_EXTRALEAN + +#include "tiffiop.h" +#ifdef OJPEG_SUPPORT + +/* Configuration defines here are: + * JPEG_ENCAP_EXTERNAL: The normal way to call libjpeg, uses longjump. In some environments, + * like eg LibTiffDelphi, this is not possible. For this reason, the actual calls to + * libjpeg, with longjump stuff, are encapsulated in dedicated functions. When + * JPEG_ENCAP_EXTERNAL is defined, these encapsulating functions are declared external + * to this unit, and can be defined elsewhere to use stuff other then longjump. + * The default mode, without JPEG_ENCAP_EXTERNAL, implements the call encapsulators + * here, internally, with normal longjump. + * SETJMP, LONGJMP, JMP_BUF: On some machines/environments a longjump equivalent is + * conviniently available, but still it may be worthwhile to use _setjmp or sigsetjmp + * in place of plain setjmp. These macros will make it easier. It is useless + * to fiddle with these if you define JPEG_ENCAP_EXTERNAL. + * OJPEG_BUFFER: Define the size of the desired buffer here. Should be small enough so as to guarantee + * instant processing, optimal streaming and optimal use of processor cache, but also big + * enough so as to not result in significant call overhead. It should be at least a few + * bytes to accommodate some structures (this is verified in asserts), but it would not be + * sensible to make it this small anyway, and it should be at most 64K since it is indexed + * with uint16. We recommend 2K. + * EGYPTIANWALK: You could also define EGYPTIANWALK here, but it is not used anywhere and has + * absolutely no effect. That is why most people insist the EGYPTIANWALK is a bit silly. + */ + +/* define LIBJPEG_ENCAP_EXTERNAL */ +#define SETJMP(jbuf) setjmp(jbuf) +#define LONGJMP(jbuf,code) longjmp(jbuf,code) +#define JMP_BUF jmp_buf +#define OJPEG_BUFFER 2048 +/* define EGYPTIANWALK */ + +#define JPEG_MARKER_SOF0 0xC0 +#define JPEG_MARKER_SOF1 0xC1 +#define JPEG_MARKER_SOF3 0xC3 +#define JPEG_MARKER_DHT 0xC4 +#define JPEG_MARKER_RST0 0XD0 +#define JPEG_MARKER_SOI 0xD8 +#define JPEG_MARKER_EOI 0xD9 +#define JPEG_MARKER_SOS 0xDA +#define JPEG_MARKER_DQT 0xDB +#define JPEG_MARKER_DRI 0xDD +#define JPEG_MARKER_APP0 0xE0 +#define JPEG_MARKER_COM 0xFE + +#define FIELD_OJPEG_JPEGINTERCHANGEFORMAT (FIELD_CODEC+0) +#define FIELD_OJPEG_JPEGINTERCHANGEFORMATLENGTH (FIELD_CODEC+1) +#define FIELD_OJPEG_JPEGQTABLES (FIELD_CODEC+2) +#define FIELD_OJPEG_JPEGDCTABLES (FIELD_CODEC+3) +#define FIELD_OJPEG_JPEGACTABLES (FIELD_CODEC+4) +#define FIELD_OJPEG_JPEGPROC (FIELD_CODEC+5) +#define FIELD_OJPEG_JPEGRESTARTINTERVAL (FIELD_CODEC+6) + +static const TIFFField ojpegFields[] = { + {TIFFTAG_JPEGIFOFFSET,1,1,TIFF_LONG8,0,TIFF_SETGET_UINT64,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGINTERCHANGEFORMAT,TRUE,FALSE,"JpegInterchangeFormat",NULL}, + {TIFFTAG_JPEGIFBYTECOUNT,1,1,TIFF_LONG8,0,TIFF_SETGET_UINT64,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGINTERCHANGEFORMATLENGTH,TRUE,FALSE,"JpegInterchangeFormatLength",NULL}, + {TIFFTAG_JPEGQTABLES,TIFF_VARIABLE2,TIFF_VARIABLE2,TIFF_LONG8,0,TIFF_SETGET_C32_UINT64,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGQTABLES,FALSE,TRUE,"JpegQTables",NULL}, + {TIFFTAG_JPEGDCTABLES,TIFF_VARIABLE2,TIFF_VARIABLE2,TIFF_LONG8,0,TIFF_SETGET_C32_UINT64,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGDCTABLES,FALSE,TRUE,"JpegDcTables",NULL}, + {TIFFTAG_JPEGACTABLES,TIFF_VARIABLE2,TIFF_VARIABLE2,TIFF_LONG8,0,TIFF_SETGET_C32_UINT64,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGACTABLES,FALSE,TRUE,"JpegAcTables",NULL}, + {TIFFTAG_JPEGPROC,1,1,TIFF_SHORT,0,TIFF_SETGET_UINT16,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGPROC,FALSE,FALSE,"JpegProc",NULL}, + {TIFFTAG_JPEGRESTARTINTERVAL,1,1,TIFF_SHORT,0,TIFF_SETGET_UINT16,TIFF_SETGET_UNDEFINED,FIELD_OJPEG_JPEGRESTARTINTERVAL,FALSE,FALSE,"JpegRestartInterval",NULL}, +}; + +#ifndef LIBJPEG_ENCAP_EXTERNAL +#include +#endif + +/* We undefine FAR to avoid conflict with JPEG definition */ + +#ifdef FAR +#undef FAR +#endif + +/* + Libjpeg's jmorecfg.h defines INT16 and INT32, but only if XMD_H is + not defined. Unfortunately, the MinGW and Borland compilers include + a typedef for INT32, which causes a conflict. MSVC does not include + a conficting typedef given the headers which are included. +*/ +#if defined(__BORLANDC__) || defined(__MINGW32__) +# define XMD_H 1 +#endif + +/* Define "boolean" as unsigned char, not int, per Windows custom. */ +#if defined(__WIN32__) && !defined(__MINGW32__) +# ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ + typedef unsigned char boolean; +# endif +# define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ +#endif + +#include "jpeglib.h" +#include "jerror.h" + +typedef struct jpeg_error_mgr jpeg_error_mgr; +typedef struct jpeg_common_struct jpeg_common_struct; +typedef struct jpeg_decompress_struct jpeg_decompress_struct; +typedef struct jpeg_source_mgr jpeg_source_mgr; + +typedef enum { + osibsNotSetYet, + osibsJpegInterchangeFormat, + osibsStrile, + osibsEof +} OJPEGStateInBufferSource; + +typedef enum { + ososSoi, + ososQTable0,ososQTable1,ososQTable2,ososQTable3, + ososDcTable0,ososDcTable1,ososDcTable2,ososDcTable3, + ososAcTable0,ososAcTable1,ososAcTable2,ososAcTable3, + ososDri, + ososSof, + ososSos, + ososCompressed, + ososRst, + ososEoi +} OJPEGStateOutState; + +typedef struct { + TIFF* tif; + #ifndef LIBJPEG_ENCAP_EXTERNAL + JMP_BUF exit_jmpbuf; + #endif + TIFFVGetMethod vgetparent; + TIFFVSetMethod vsetparent; + TIFFPrintMethod printdir; + uint64 file_size; + uint32 image_width; + uint32 image_length; + uint32 strile_width; + uint32 strile_length; + uint32 strile_length_total; + uint8 samples_per_pixel; + uint8 plane_sample_offset; + uint8 samples_per_pixel_per_plane; + uint64 jpeg_interchange_format; + uint64 jpeg_interchange_format_length; + uint8 jpeg_proc; + uint8 subsamplingcorrect; + uint8 subsamplingcorrect_done; + uint8 subsampling_tag; + uint8 subsampling_hor; + uint8 subsampling_ver; + uint8 subsampling_force_desubsampling_inside_decompression; + uint8 qtable_offset_count; + uint8 dctable_offset_count; + uint8 actable_offset_count; + uint64 qtable_offset[3]; + uint64 dctable_offset[3]; + uint64 actable_offset[3]; + uint8* qtable[4]; + uint8* dctable[4]; + uint8* actable[4]; + uint16 restart_interval; + uint8 restart_index; + uint8 sof_log; + uint8 sof_marker_id; + uint32 sof_x; + uint32 sof_y; + uint8 sof_c[3]; + uint8 sof_hv[3]; + uint8 sof_tq[3]; + uint8 sos_cs[3]; + uint8 sos_tda[3]; + struct { + uint8 log; + OJPEGStateInBufferSource in_buffer_source; + uint32 in_buffer_next_strile; + uint64 in_buffer_file_pos; + uint64 in_buffer_file_togo; + } sos_end[3]; + uint8 readheader_done; + uint8 writeheader_done; + uint16 write_cursample; + uint32 write_curstrile; + uint8 libjpeg_session_active; + uint8 libjpeg_jpeg_query_style; + jpeg_error_mgr libjpeg_jpeg_error_mgr; + jpeg_decompress_struct libjpeg_jpeg_decompress_struct; + jpeg_source_mgr libjpeg_jpeg_source_mgr; + uint8 subsampling_convert_log; + uint32 subsampling_convert_ylinelen; + uint32 subsampling_convert_ylines; + uint32 subsampling_convert_clinelen; + uint32 subsampling_convert_clines; + uint32 subsampling_convert_ybuflen; + uint32 subsampling_convert_cbuflen; + uint32 subsampling_convert_ycbcrbuflen; + uint8* subsampling_convert_ycbcrbuf; + uint8* subsampling_convert_ybuf; + uint8* subsampling_convert_cbbuf; + uint8* subsampling_convert_crbuf; + uint32 subsampling_convert_ycbcrimagelen; + uint8** subsampling_convert_ycbcrimage; + uint32 subsampling_convert_clinelenout; + uint32 subsampling_convert_state; + uint32 bytes_per_line; /* if the codec outputs subsampled data, a 'line' in bytes_per_line */ + uint32 lines_per_strile; /* and lines_per_strile means subsampling_ver desubsampled rows */ + OJPEGStateInBufferSource in_buffer_source; + uint32 in_buffer_next_strile; + uint32 in_buffer_strile_count; + uint64 in_buffer_file_pos; + uint8 in_buffer_file_pos_log; + uint64 in_buffer_file_togo; + uint16 in_buffer_togo; + uint8* in_buffer_cur; + uint8 in_buffer[OJPEG_BUFFER]; + OJPEGStateOutState out_state; + uint8 out_buffer[OJPEG_BUFFER]; + uint8* skip_buffer; +} OJPEGState; + +static int OJPEGVGetField(TIFF* tif, uint32 tag, va_list ap); +static int OJPEGVSetField(TIFF* tif, uint32 tag, va_list ap); +static void OJPEGPrintDir(TIFF* tif, FILE* fd, long flags); + +static int OJPEGFixupTags(TIFF* tif); +static int OJPEGSetupDecode(TIFF* tif); +static int OJPEGPreDecode(TIFF* tif, uint16 s); +static int OJPEGPreDecodeSkipRaw(TIFF* tif); +static int OJPEGPreDecodeSkipScanlines(TIFF* tif); +static int OJPEGDecode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s); +static int OJPEGDecodeRaw(TIFF* tif, uint8* buf, tmsize_t cc); +static int OJPEGDecodeScanlines(TIFF* tif, uint8* buf, tmsize_t cc); +static void OJPEGPostDecode(TIFF* tif, uint8* buf, tmsize_t cc); +static int OJPEGSetupEncode(TIFF* tif); +static int OJPEGPreEncode(TIFF* tif, uint16 s); +static int OJPEGEncode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s); +static int OJPEGPostEncode(TIFF* tif); +static void OJPEGCleanup(TIFF* tif); + +static void OJPEGSubsamplingCorrect(TIFF* tif); +static int OJPEGReadHeaderInfo(TIFF* tif); +static int OJPEGReadSecondarySos(TIFF* tif, uint16 s); +static int OJPEGWriteHeaderInfo(TIFF* tif); +static void OJPEGLibjpegSessionAbort(TIFF* tif); + +static int OJPEGReadHeaderInfoSec(TIFF* tif); +static int OJPEGReadHeaderInfoSecStreamDri(TIFF* tif); +static int OJPEGReadHeaderInfoSecStreamDqt(TIFF* tif); +static int OJPEGReadHeaderInfoSecStreamDht(TIFF* tif); +static int OJPEGReadHeaderInfoSecStreamSof(TIFF* tif, uint8 marker_id); +static int OJPEGReadHeaderInfoSecStreamSos(TIFF* tif); +static int OJPEGReadHeaderInfoSecTablesQTable(TIFF* tif); +static int OJPEGReadHeaderInfoSecTablesDcTable(TIFF* tif); +static int OJPEGReadHeaderInfoSecTablesAcTable(TIFF* tif); + +static int OJPEGReadBufferFill(OJPEGState* sp); +static int OJPEGReadByte(OJPEGState* sp, uint8* byte); +static int OJPEGReadBytePeek(OJPEGState* sp, uint8* byte); +static void OJPEGReadByteAdvance(OJPEGState* sp); +static int OJPEGReadWord(OJPEGState* sp, uint16* word); +static int OJPEGReadBlock(OJPEGState* sp, uint16 len, void* mem); +static void OJPEGReadSkip(OJPEGState* sp, uint16 len); + +static int OJPEGWriteStream(TIFF* tif, void** mem, uint32* len); +static void OJPEGWriteStreamSoi(TIFF* tif, void** mem, uint32* len); +static void OJPEGWriteStreamQTable(TIFF* tif, uint8 table_index, void** mem, uint32* len); +static void OJPEGWriteStreamDcTable(TIFF* tif, uint8 table_index, void** mem, uint32* len); +static void OJPEGWriteStreamAcTable(TIFF* tif, uint8 table_index, void** mem, uint32* len); +static void OJPEGWriteStreamDri(TIFF* tif, void** mem, uint32* len); +static void OJPEGWriteStreamSof(TIFF* tif, void** mem, uint32* len); +static void OJPEGWriteStreamSos(TIFF* tif, void** mem, uint32* len); +static int OJPEGWriteStreamCompressed(TIFF* tif, void** mem, uint32* len); +static void OJPEGWriteStreamRst(TIFF* tif, void** mem, uint32* len); +static void OJPEGWriteStreamEoi(TIFF* tif, void** mem, uint32* len); + +#ifdef LIBJPEG_ENCAP_EXTERNAL +extern int jpeg_create_decompress_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo); +extern int jpeg_read_header_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, uint8 require_image); +extern int jpeg_start_decompress_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo); +extern int jpeg_read_scanlines_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, void* scanlines, uint32 max_lines); +extern int jpeg_read_raw_data_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, void* data, uint32 max_lines); +extern void jpeg_encap_unwind(TIFF* tif); +#else +static int jpeg_create_decompress_encap(OJPEGState* sp, jpeg_decompress_struct* j); +static int jpeg_read_header_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, uint8 require_image); +static int jpeg_start_decompress_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo); +static int jpeg_read_scanlines_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, void* scanlines, uint32 max_lines); +static int jpeg_read_raw_data_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, void* data, uint32 max_lines); +static void jpeg_encap_unwind(TIFF* tif); +#endif + +static void OJPEGLibjpegJpegErrorMgrOutputMessage(jpeg_common_struct* cinfo); +static void OJPEGLibjpegJpegErrorMgrErrorExit(jpeg_common_struct* cinfo); +static void OJPEGLibjpegJpegSourceMgrInitSource(jpeg_decompress_struct* cinfo); +static boolean OJPEGLibjpegJpegSourceMgrFillInputBuffer(jpeg_decompress_struct* cinfo); +static void OJPEGLibjpegJpegSourceMgrSkipInputData(jpeg_decompress_struct* cinfo, long num_bytes); +static boolean OJPEGLibjpegJpegSourceMgrResyncToRestart(jpeg_decompress_struct* cinfo, int desired); +static void OJPEGLibjpegJpegSourceMgrTermSource(jpeg_decompress_struct* cinfo); + +int +TIFFInitOJPEG(TIFF* tif, int scheme) +{ + static const char module[]="TIFFInitOJPEG"; + OJPEGState* sp; + + assert(scheme==COMPRESSION_OJPEG); + + /* + * Merge codec-specific tag information. + */ + if (!_TIFFMergeFields(tif, ojpegFields, TIFFArrayCount(ojpegFields))) { + TIFFErrorExt(tif->tif_clientdata, module, + "Merging Old JPEG codec-specific tags failed"); + return 0; + } + + /* state block */ + sp=_TIFFmalloc(sizeof(OJPEGState)); + if (sp==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"No space for OJPEG state block"); + return(0); + } + _TIFFmemset(sp,0,sizeof(OJPEGState)); + sp->tif=tif; + sp->jpeg_proc=1; + sp->subsampling_hor=2; + sp->subsampling_ver=2; + TIFFSetField(tif,TIFFTAG_YCBCRSUBSAMPLING,2,2); + /* tif codec methods */ + tif->tif_fixuptags=OJPEGFixupTags; + tif->tif_setupdecode=OJPEGSetupDecode; + tif->tif_predecode=OJPEGPreDecode; + tif->tif_postdecode=OJPEGPostDecode; + tif->tif_decoderow=OJPEGDecode; + tif->tif_decodestrip=OJPEGDecode; + tif->tif_decodetile=OJPEGDecode; + tif->tif_setupencode=OJPEGSetupEncode; + tif->tif_preencode=OJPEGPreEncode; + tif->tif_postencode=OJPEGPostEncode; + tif->tif_encoderow=OJPEGEncode; + tif->tif_encodestrip=OJPEGEncode; + tif->tif_encodetile=OJPEGEncode; + tif->tif_cleanup=OJPEGCleanup; + tif->tif_data=(uint8*)sp; + /* tif tag methods */ + sp->vgetparent=tif->tif_tagmethods.vgetfield; + tif->tif_tagmethods.vgetfield=OJPEGVGetField; + sp->vsetparent=tif->tif_tagmethods.vsetfield; + tif->tif_tagmethods.vsetfield=OJPEGVSetField; + sp->printdir=tif->tif_tagmethods.printdir; + tif->tif_tagmethods.printdir=OJPEGPrintDir; + /* Some OJPEG files don't have strip or tile offsets or bytecounts tags. + Some others do, but have totally meaningless or corrupt values + in these tags. In these cases, the JpegInterchangeFormat stream is + reliable. In any case, this decoder reads the compressed data itself, + from the most reliable locations, and we need to notify encapsulating + LibTiff not to read raw strips or tiles for us. */ + tif->tif_flags|=TIFF_NOREADRAW; + return(1); +} + +static int +OJPEGVGetField(TIFF* tif, uint32 tag, va_list ap) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + switch(tag) + { + case TIFFTAG_JPEGIFOFFSET: + *va_arg(ap,uint64*)=(uint64)sp->jpeg_interchange_format; + break; + case TIFFTAG_JPEGIFBYTECOUNT: + *va_arg(ap,uint64*)=(uint64)sp->jpeg_interchange_format_length; + break; + case TIFFTAG_YCBCRSUBSAMPLING: + if (sp->subsamplingcorrect_done==0) + OJPEGSubsamplingCorrect(tif); + *va_arg(ap,uint16*)=(uint16)sp->subsampling_hor; + *va_arg(ap,uint16*)=(uint16)sp->subsampling_ver; + break; + case TIFFTAG_JPEGQTABLES: + *va_arg(ap,uint32*)=(uint32)sp->qtable_offset_count; + *va_arg(ap,void**)=(void*)sp->qtable_offset; + break; + case TIFFTAG_JPEGDCTABLES: + *va_arg(ap,uint32*)=(uint32)sp->dctable_offset_count; + *va_arg(ap,void**)=(void*)sp->dctable_offset; + break; + case TIFFTAG_JPEGACTABLES: + *va_arg(ap,uint32*)=(uint32)sp->actable_offset_count; + *va_arg(ap,void**)=(void*)sp->actable_offset; + break; + case TIFFTAG_JPEGPROC: + *va_arg(ap,uint16*)=(uint16)sp->jpeg_proc; + break; + case TIFFTAG_JPEGRESTARTINTERVAL: + *va_arg(ap,uint16*)=sp->restart_interval; + break; + default: + return (*sp->vgetparent)(tif,tag,ap); + } + return (1); +} + +static int +OJPEGVSetField(TIFF* tif, uint32 tag, va_list ap) +{ + static const char module[]="OJPEGVSetField"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint32 ma; + uint64* mb; + uint32 n; + const TIFFField* fip; + + switch(tag) + { + case TIFFTAG_JPEGIFOFFSET: + sp->jpeg_interchange_format=(uint64)va_arg(ap,uint64); + break; + case TIFFTAG_JPEGIFBYTECOUNT: + sp->jpeg_interchange_format_length=(uint64)va_arg(ap,uint64); + break; + case TIFFTAG_YCBCRSUBSAMPLING: + sp->subsampling_tag=1; + sp->subsampling_hor=(uint8)va_arg(ap,uint16_vap); + sp->subsampling_ver=(uint8)va_arg(ap,uint16_vap); + tif->tif_dir.td_ycbcrsubsampling[0]=sp->subsampling_hor; + tif->tif_dir.td_ycbcrsubsampling[1]=sp->subsampling_ver; + break; + case TIFFTAG_JPEGQTABLES: + ma=(uint32)va_arg(ap,uint32); + if (ma!=0) + { + if (ma>3) + { + TIFFErrorExt(tif->tif_clientdata,module,"JpegQTables tag has incorrect count"); + return(0); + } + sp->qtable_offset_count=(uint8)ma; + mb=(uint64*)va_arg(ap,uint64*); + for (n=0; nqtable_offset[n]=mb[n]; + } + break; + case TIFFTAG_JPEGDCTABLES: + ma=(uint32)va_arg(ap,uint32); + if (ma!=0) + { + if (ma>3) + { + TIFFErrorExt(tif->tif_clientdata,module,"JpegDcTables tag has incorrect count"); + return(0); + } + sp->dctable_offset_count=(uint8)ma; + mb=(uint64*)va_arg(ap,uint64*); + for (n=0; ndctable_offset[n]=mb[n]; + } + break; + case TIFFTAG_JPEGACTABLES: + ma=(uint32)va_arg(ap,uint32); + if (ma!=0) + { + if (ma>3) + { + TIFFErrorExt(tif->tif_clientdata,module,"JpegAcTables tag has incorrect count"); + return(0); + } + sp->actable_offset_count=(uint8)ma; + mb=(uint64*)va_arg(ap,uint64*); + for (n=0; nactable_offset[n]=mb[n]; + } + break; + case TIFFTAG_JPEGPROC: + sp->jpeg_proc=(uint8)va_arg(ap,uint16_vap); + break; + case TIFFTAG_JPEGRESTARTINTERVAL: + sp->restart_interval=(uint16)va_arg(ap,uint16_vap); + break; + default: + return (*sp->vsetparent)(tif,tag,ap); + } + fip = TIFFFieldWithTag(tif,tag); + if( fip == NULL ) /* shouldn't happen */ + return(0); + TIFFSetFieldBit(tif,fip->field_bit); + tif->tif_flags|=TIFF_DIRTYDIRECT; + return(1); +} + +static void +OJPEGPrintDir(TIFF* tif, FILE* fd, long flags) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint8 m; + (void)flags; + assert(sp!=NULL); + if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGINTERCHANGEFORMAT)) + fprintf(fd," JpegInterchangeFormat: " TIFF_UINT64_FORMAT "\n",(TIFF_UINT64_T)sp->jpeg_interchange_format); + if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGINTERCHANGEFORMATLENGTH)) + fprintf(fd," JpegInterchangeFormatLength: " TIFF_UINT64_FORMAT "\n",(TIFF_UINT64_T)sp->jpeg_interchange_format_length); + if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGQTABLES)) + { + fprintf(fd," JpegQTables:"); + for (m=0; mqtable_offset_count; m++) + fprintf(fd," " TIFF_UINT64_FORMAT,(TIFF_UINT64_T)sp->qtable_offset[m]); + fprintf(fd,"\n"); + } + if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGDCTABLES)) + { + fprintf(fd," JpegDcTables:"); + for (m=0; mdctable_offset_count; m++) + fprintf(fd," " TIFF_UINT64_FORMAT,(TIFF_UINT64_T)sp->dctable_offset[m]); + fprintf(fd,"\n"); + } + if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGACTABLES)) + { + fprintf(fd," JpegAcTables:"); + for (m=0; mactable_offset_count; m++) + fprintf(fd," " TIFF_UINT64_FORMAT,(TIFF_UINT64_T)sp->actable_offset[m]); + fprintf(fd,"\n"); + } + if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGPROC)) + fprintf(fd," JpegProc: %u\n",(unsigned int)sp->jpeg_proc); + if (TIFFFieldSet(tif,FIELD_OJPEG_JPEGRESTARTINTERVAL)) + fprintf(fd," JpegRestartInterval: %u\n",(unsigned int)sp->restart_interval); + if (sp->printdir) + (*sp->printdir)(tif, fd, flags); +} + +static int +OJPEGFixupTags(TIFF* tif) +{ + (void) tif; + return(1); +} + +static int +OJPEGSetupDecode(TIFF* tif) +{ + static const char module[]="OJPEGSetupDecode"; + TIFFWarningExt(tif->tif_clientdata,module,"Depreciated and troublesome old-style JPEG compression mode, please convert to new-style JPEG compression and notify vendor of writing software"); + return(1); +} + +static int +OJPEGPreDecode(TIFF* tif, uint16 s) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint32 m; + if (sp->subsamplingcorrect_done==0) + OJPEGSubsamplingCorrect(tif); + if (sp->readheader_done==0) + { + if (OJPEGReadHeaderInfo(tif)==0) + return(0); + } + if (sp->sos_end[s].log==0) + { + if (OJPEGReadSecondarySos(tif,s)==0) + return(0); + } + if isTiled(tif) + m=tif->tif_curtile; + else + m=tif->tif_curstrip; + if ((sp->writeheader_done!=0) && ((sp->write_cursample!=s) || (sp->write_curstrile>m))) + { + if (sp->libjpeg_session_active!=0) + OJPEGLibjpegSessionAbort(tif); + sp->writeheader_done=0; + } + if (sp->writeheader_done==0) + { + sp->plane_sample_offset=(uint8)s; + sp->write_cursample=s; + sp->write_curstrile=s*tif->tif_dir.td_stripsperimage; + if ((sp->in_buffer_file_pos_log==0) || + (sp->in_buffer_file_pos-sp->in_buffer_togo!=sp->sos_end[s].in_buffer_file_pos)) + { + sp->in_buffer_source=sp->sos_end[s].in_buffer_source; + sp->in_buffer_next_strile=sp->sos_end[s].in_buffer_next_strile; + sp->in_buffer_file_pos=sp->sos_end[s].in_buffer_file_pos; + sp->in_buffer_file_pos_log=0; + sp->in_buffer_file_togo=sp->sos_end[s].in_buffer_file_togo; + sp->in_buffer_togo=0; + sp->in_buffer_cur=0; + } + if (OJPEGWriteHeaderInfo(tif)==0) + return(0); + } + while (sp->write_curstrilelibjpeg_jpeg_query_style==0) + { + if (OJPEGPreDecodeSkipRaw(tif)==0) + return(0); + } + else + { + if (OJPEGPreDecodeSkipScanlines(tif)==0) + return(0); + } + sp->write_curstrile++; + } + return(1); +} + +static int +OJPEGPreDecodeSkipRaw(TIFF* tif) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint32 m; + m=sp->lines_per_strile; + if (sp->subsampling_convert_state!=0) + { + if (sp->subsampling_convert_clines-sp->subsampling_convert_state>=m) + { + sp->subsampling_convert_state+=m; + if (sp->subsampling_convert_state==sp->subsampling_convert_clines) + sp->subsampling_convert_state=0; + return(1); + } + m-=sp->subsampling_convert_clines-sp->subsampling_convert_state; + sp->subsampling_convert_state=0; + } + while (m>=sp->subsampling_convert_clines) + { + if (jpeg_read_raw_data_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),sp->subsampling_convert_ycbcrimage,sp->subsampling_ver*8)==0) + return(0); + m-=sp->subsampling_convert_clines; + } + if (m>0) + { + if (jpeg_read_raw_data_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),sp->subsampling_convert_ycbcrimage,sp->subsampling_ver*8)==0) + return(0); + sp->subsampling_convert_state=m; + } + return(1); +} + +static int +OJPEGPreDecodeSkipScanlines(TIFF* tif) +{ + static const char module[]="OJPEGPreDecodeSkipScanlines"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint32 m; + if (sp->skip_buffer==NULL) + { + sp->skip_buffer=_TIFFmalloc(sp->bytes_per_line); + if (sp->skip_buffer==NULL) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + } + for (m=0; mlines_per_strile; m++) + { + if (jpeg_read_scanlines_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),&sp->skip_buffer,1)==0) + return(0); + } + return(1); +} + +static int +OJPEGDecode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + (void)s; + if (sp->libjpeg_jpeg_query_style==0) + { + if (OJPEGDecodeRaw(tif,buf,cc)==0) + return(0); + } + else + { + if (OJPEGDecodeScanlines(tif,buf,cc)==0) + return(0); + } + return(1); +} + +static int +OJPEGDecodeRaw(TIFF* tif, uint8* buf, tmsize_t cc) +{ + static const char module[]="OJPEGDecodeRaw"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint8* m; + tmsize_t n; + uint8* oy; + uint8* ocb; + uint8* ocr; + uint8* p; + uint32 q; + uint8* r; + uint8 sx,sy; + if (cc%sp->bytes_per_line!=0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Fractional scanline not read"); + return(0); + } + assert(cc>0); + m=buf; + n=cc; + do + { + if (sp->subsampling_convert_state==0) + { + if (jpeg_read_raw_data_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),sp->subsampling_convert_ycbcrimage,sp->subsampling_ver*8)==0) + return(0); + } + oy=sp->subsampling_convert_ybuf+sp->subsampling_convert_state*sp->subsampling_ver*sp->subsampling_convert_ylinelen; + ocb=sp->subsampling_convert_cbbuf+sp->subsampling_convert_state*sp->subsampling_convert_clinelen; + ocr=sp->subsampling_convert_crbuf+sp->subsampling_convert_state*sp->subsampling_convert_clinelen; + p=m; + for (q=0; qsubsampling_convert_clinelenout; q++) + { + r=oy; + for (sy=0; sysubsampling_ver; sy++) + { + for (sx=0; sxsubsampling_hor; sx++) + *p++=*r++; + r+=sp->subsampling_convert_ylinelen-sp->subsampling_hor; + } + oy+=sp->subsampling_hor; + *p++=*ocb++; + *p++=*ocr++; + } + sp->subsampling_convert_state++; + if (sp->subsampling_convert_state==sp->subsampling_convert_clines) + sp->subsampling_convert_state=0; + m+=sp->bytes_per_line; + n-=sp->bytes_per_line; + } while(n>0); + return(1); +} + +static int +OJPEGDecodeScanlines(TIFF* tif, uint8* buf, tmsize_t cc) +{ + static const char module[]="OJPEGDecodeScanlines"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint8* m; + tmsize_t n; + if (cc%sp->bytes_per_line!=0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Fractional scanline not read"); + return(0); + } + assert(cc>0); + m=buf; + n=cc; + do + { + if (jpeg_read_scanlines_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),&m,1)==0) + return(0); + m+=sp->bytes_per_line; + n-=sp->bytes_per_line; + } while(n>0); + return(1); +} + +static void +OJPEGPostDecode(TIFF* tif, uint8* buf, tmsize_t cc) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + (void)buf; + (void)cc; + sp->write_curstrile++; + if (sp->write_curstrile%tif->tif_dir.td_stripsperimage==0) + { + assert(sp->libjpeg_session_active!=0); + OJPEGLibjpegSessionAbort(tif); + sp->writeheader_done=0; + } +} + +static int +OJPEGSetupEncode(TIFF* tif) +{ + static const char module[]="OJPEGSetupEncode"; + TIFFErrorExt(tif->tif_clientdata,module,"OJPEG encoding not supported; use new-style JPEG compression instead"); + return(0); +} + +static int +OJPEGPreEncode(TIFF* tif, uint16 s) +{ + static const char module[]="OJPEGPreEncode"; + (void)s; + TIFFErrorExt(tif->tif_clientdata,module,"OJPEG encoding not supported; use new-style JPEG compression instead"); + return(0); +} + +static int +OJPEGEncode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s) +{ + static const char module[]="OJPEGEncode"; + (void)buf; + (void)cc; + (void)s; + TIFFErrorExt(tif->tif_clientdata,module,"OJPEG encoding not supported; use new-style JPEG compression instead"); + return(0); +} + +static int +OJPEGPostEncode(TIFF* tif) +{ + static const char module[]="OJPEGPostEncode"; + TIFFErrorExt(tif->tif_clientdata,module,"OJPEG encoding not supported; use new-style JPEG compression instead"); + return(0); +} + +static void +OJPEGCleanup(TIFF* tif) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + if (sp!=0) + { + tif->tif_tagmethods.vgetfield=sp->vgetparent; + tif->tif_tagmethods.vsetfield=sp->vsetparent; + tif->tif_tagmethods.printdir=sp->printdir; + if (sp->qtable[0]!=0) + _TIFFfree(sp->qtable[0]); + if (sp->qtable[1]!=0) + _TIFFfree(sp->qtable[1]); + if (sp->qtable[2]!=0) + _TIFFfree(sp->qtable[2]); + if (sp->qtable[3]!=0) + _TIFFfree(sp->qtable[3]); + if (sp->dctable[0]!=0) + _TIFFfree(sp->dctable[0]); + if (sp->dctable[1]!=0) + _TIFFfree(sp->dctable[1]); + if (sp->dctable[2]!=0) + _TIFFfree(sp->dctable[2]); + if (sp->dctable[3]!=0) + _TIFFfree(sp->dctable[3]); + if (sp->actable[0]!=0) + _TIFFfree(sp->actable[0]); + if (sp->actable[1]!=0) + _TIFFfree(sp->actable[1]); + if (sp->actable[2]!=0) + _TIFFfree(sp->actable[2]); + if (sp->actable[3]!=0) + _TIFFfree(sp->actable[3]); + if (sp->libjpeg_session_active!=0) + OJPEGLibjpegSessionAbort(tif); + if (sp->subsampling_convert_ycbcrbuf!=0) + _TIFFfree(sp->subsampling_convert_ycbcrbuf); + if (sp->subsampling_convert_ycbcrimage!=0) + _TIFFfree(sp->subsampling_convert_ycbcrimage); + if (sp->skip_buffer!=0) + _TIFFfree(sp->skip_buffer); + _TIFFfree(sp); + tif->tif_data=NULL; + _TIFFSetDefaultCompressionState(tif); + } +} + +static void +OJPEGSubsamplingCorrect(TIFF* tif) +{ + static const char module[]="OJPEGSubsamplingCorrect"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint8 mh; + uint8 mv; + _TIFFFillStriles( tif ); + + assert(sp->subsamplingcorrect_done==0); + if ((tif->tif_dir.td_samplesperpixel!=3) || ((tif->tif_dir.td_photometric!=PHOTOMETRIC_YCBCR) && + (tif->tif_dir.td_photometric!=PHOTOMETRIC_ITULAB))) + { + if (sp->subsampling_tag!=0) + TIFFWarningExt(tif->tif_clientdata,module,"Subsampling tag not appropriate for this Photometric and/or SamplesPerPixel"); + sp->subsampling_hor=1; + sp->subsampling_ver=1; + sp->subsampling_force_desubsampling_inside_decompression=0; + } + else + { + sp->subsamplingcorrect_done=1; + mh=sp->subsampling_hor; + mv=sp->subsampling_ver; + sp->subsamplingcorrect=1; + OJPEGReadHeaderInfoSec(tif); + if (sp->subsampling_force_desubsampling_inside_decompression!=0) + { + sp->subsampling_hor=1; + sp->subsampling_ver=1; + } + sp->subsamplingcorrect=0; + if (((sp->subsampling_hor!=mh) || (sp->subsampling_ver!=mv)) && (sp->subsampling_force_desubsampling_inside_decompression==0)) + { + if (sp->subsampling_tag==0) + TIFFWarningExt(tif->tif_clientdata,module,"Subsampling tag is not set, yet subsampling inside JPEG data [%d,%d] does not match default values [2,2]; assuming subsampling inside JPEG data is correct",sp->subsampling_hor,sp->subsampling_ver); + else + TIFFWarningExt(tif->tif_clientdata,module,"Subsampling inside JPEG data [%d,%d] does not match subsampling tag values [%d,%d]; assuming subsampling inside JPEG data is correct",sp->subsampling_hor,sp->subsampling_ver,mh,mv); + } + if (sp->subsampling_force_desubsampling_inside_decompression!=0) + { + if (sp->subsampling_tag==0) + TIFFWarningExt(tif->tif_clientdata,module,"Subsampling tag is not set, yet subsampling inside JPEG data does not match default values [2,2] (nor any other values allowed in TIFF); assuming subsampling inside JPEG data is correct and desubsampling inside JPEG decompression"); + else + TIFFWarningExt(tif->tif_clientdata,module,"Subsampling inside JPEG data does not match subsampling tag values [%d,%d] (nor any other values allowed in TIFF); assuming subsampling inside JPEG data is correct and desubsampling inside JPEG decompression",mh,mv); + } + if (sp->subsampling_force_desubsampling_inside_decompression==0) + { + if (sp->subsampling_horsubsampling_ver) + TIFFWarningExt(tif->tif_clientdata,module,"Subsampling values [%d,%d] are not allowed in TIFF",sp->subsampling_hor,sp->subsampling_ver); + } + } + sp->subsamplingcorrect_done=1; +} + +static int +OJPEGReadHeaderInfo(TIFF* tif) +{ + static const char module[]="OJPEGReadHeaderInfo"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + assert(sp->readheader_done==0); + sp->image_width=tif->tif_dir.td_imagewidth; + sp->image_length=tif->tif_dir.td_imagelength; + if isTiled(tif) + { + sp->strile_width=tif->tif_dir.td_tilewidth; + sp->strile_length=tif->tif_dir.td_tilelength; + sp->strile_length_total=((sp->image_length+sp->strile_length-1)/sp->strile_length)*sp->strile_length; + } + else + { + sp->strile_width=sp->image_width; + sp->strile_length=tif->tif_dir.td_rowsperstrip; + sp->strile_length_total=sp->image_length; + } + if (tif->tif_dir.td_samplesperpixel==1) + { + sp->samples_per_pixel=1; + sp->plane_sample_offset=0; + sp->samples_per_pixel_per_plane=sp->samples_per_pixel; + sp->subsampling_hor=1; + sp->subsampling_ver=1; + } + else + { + if (tif->tif_dir.td_samplesperpixel!=3) + { + TIFFErrorExt(tif->tif_clientdata,module,"SamplesPerPixel %d not supported for this compression scheme",sp->samples_per_pixel); + return(0); + } + sp->samples_per_pixel=3; + sp->plane_sample_offset=0; + if (tif->tif_dir.td_planarconfig==PLANARCONFIG_CONTIG) + sp->samples_per_pixel_per_plane=3; + else + sp->samples_per_pixel_per_plane=1; + } + if (sp->strile_lengthimage_length) + { + if (sp->strile_length%(sp->subsampling_ver*8)!=0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Incompatible vertical subsampling and image strip/tile length"); + return(0); + } + sp->restart_interval=((sp->strile_width+sp->subsampling_hor*8-1)/(sp->subsampling_hor*8))*(sp->strile_length/(sp->subsampling_ver*8)); + } + if (OJPEGReadHeaderInfoSec(tif)==0) + return(0); + sp->sos_end[0].log=1; + sp->sos_end[0].in_buffer_source=sp->in_buffer_source; + sp->sos_end[0].in_buffer_next_strile=sp->in_buffer_next_strile; + sp->sos_end[0].in_buffer_file_pos=sp->in_buffer_file_pos-sp->in_buffer_togo; + sp->sos_end[0].in_buffer_file_togo=sp->in_buffer_file_togo+sp->in_buffer_togo; + sp->readheader_done=1; + return(1); +} + +static int +OJPEGReadSecondarySos(TIFF* tif, uint16 s) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint8 m; + assert(s>0); + assert(s<3); + assert(sp->sos_end[0].log!=0); + assert(sp->sos_end[s].log==0); + sp->plane_sample_offset=s-1; + while(sp->sos_end[sp->plane_sample_offset].log==0) + sp->plane_sample_offset--; + sp->in_buffer_source=sp->sos_end[sp->plane_sample_offset].in_buffer_source; + sp->in_buffer_next_strile=sp->sos_end[sp->plane_sample_offset].in_buffer_next_strile; + sp->in_buffer_file_pos=sp->sos_end[sp->plane_sample_offset].in_buffer_file_pos; + sp->in_buffer_file_pos_log=0; + sp->in_buffer_file_togo=sp->sos_end[sp->plane_sample_offset].in_buffer_file_togo; + sp->in_buffer_togo=0; + sp->in_buffer_cur=0; + while(sp->plane_sample_offsetplane_sample_offset++; + if (OJPEGReadHeaderInfoSecStreamSos(tif)==0) + return(0); + sp->sos_end[sp->plane_sample_offset].log=1; + sp->sos_end[sp->plane_sample_offset].in_buffer_source=sp->in_buffer_source; + sp->sos_end[sp->plane_sample_offset].in_buffer_next_strile=sp->in_buffer_next_strile; + sp->sos_end[sp->plane_sample_offset].in_buffer_file_pos=sp->in_buffer_file_pos-sp->in_buffer_togo; + sp->sos_end[sp->plane_sample_offset].in_buffer_file_togo=sp->in_buffer_file_togo+sp->in_buffer_togo; + } + return(1); +} + +static int +OJPEGWriteHeaderInfo(TIFF* tif) +{ + static const char module[]="OJPEGWriteHeaderInfo"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint8** m; + uint32 n; + /* if a previous attempt failed, don't try again */ + if (sp->libjpeg_session_active != 0) + return 0; + sp->out_state=ososSoi; + sp->restart_index=0; + jpeg_std_error(&(sp->libjpeg_jpeg_error_mgr)); + sp->libjpeg_jpeg_error_mgr.output_message=OJPEGLibjpegJpegErrorMgrOutputMessage; + sp->libjpeg_jpeg_error_mgr.error_exit=OJPEGLibjpegJpegErrorMgrErrorExit; + sp->libjpeg_jpeg_decompress_struct.err=&(sp->libjpeg_jpeg_error_mgr); + sp->libjpeg_jpeg_decompress_struct.client_data=(void*)tif; + if (jpeg_create_decompress_encap(sp,&(sp->libjpeg_jpeg_decompress_struct))==0) + return(0); + sp->libjpeg_session_active=1; + sp->libjpeg_jpeg_source_mgr.bytes_in_buffer=0; + sp->libjpeg_jpeg_source_mgr.init_source=OJPEGLibjpegJpegSourceMgrInitSource; + sp->libjpeg_jpeg_source_mgr.fill_input_buffer=OJPEGLibjpegJpegSourceMgrFillInputBuffer; + sp->libjpeg_jpeg_source_mgr.skip_input_data=OJPEGLibjpegJpegSourceMgrSkipInputData; + sp->libjpeg_jpeg_source_mgr.resync_to_restart=OJPEGLibjpegJpegSourceMgrResyncToRestart; + sp->libjpeg_jpeg_source_mgr.term_source=OJPEGLibjpegJpegSourceMgrTermSource; + sp->libjpeg_jpeg_decompress_struct.src=&(sp->libjpeg_jpeg_source_mgr); + if (jpeg_read_header_encap(sp,&(sp->libjpeg_jpeg_decompress_struct),1)==0) + return(0); + if ((sp->subsampling_force_desubsampling_inside_decompression==0) && (sp->samples_per_pixel_per_plane>1)) + { + sp->libjpeg_jpeg_decompress_struct.raw_data_out=1; +#if JPEG_LIB_VERSION >= 70 + sp->libjpeg_jpeg_decompress_struct.do_fancy_upsampling=FALSE; +#endif + sp->libjpeg_jpeg_query_style=0; + if (sp->subsampling_convert_log==0) + { + assert(sp->subsampling_convert_ycbcrbuf==0); + assert(sp->subsampling_convert_ycbcrimage==0); + sp->subsampling_convert_ylinelen=((sp->strile_width+sp->subsampling_hor*8-1)/(sp->subsampling_hor*8)*sp->subsampling_hor*8); + sp->subsampling_convert_ylines=sp->subsampling_ver*8; + sp->subsampling_convert_clinelen=sp->subsampling_convert_ylinelen/sp->subsampling_hor; + sp->subsampling_convert_clines=8; + sp->subsampling_convert_ybuflen=sp->subsampling_convert_ylinelen*sp->subsampling_convert_ylines; + sp->subsampling_convert_cbuflen=sp->subsampling_convert_clinelen*sp->subsampling_convert_clines; + sp->subsampling_convert_ycbcrbuflen=sp->subsampling_convert_ybuflen+2*sp->subsampling_convert_cbuflen; + sp->subsampling_convert_ycbcrbuf=_TIFFmalloc(sp->subsampling_convert_ycbcrbuflen); + if (sp->subsampling_convert_ycbcrbuf==0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + sp->subsampling_convert_ybuf=sp->subsampling_convert_ycbcrbuf; + sp->subsampling_convert_cbbuf=sp->subsampling_convert_ybuf+sp->subsampling_convert_ybuflen; + sp->subsampling_convert_crbuf=sp->subsampling_convert_cbbuf+sp->subsampling_convert_cbuflen; + sp->subsampling_convert_ycbcrimagelen=3+sp->subsampling_convert_ylines+2*sp->subsampling_convert_clines; + sp->subsampling_convert_ycbcrimage=_TIFFmalloc(sp->subsampling_convert_ycbcrimagelen*sizeof(uint8*)); + if (sp->subsampling_convert_ycbcrimage==0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + m=sp->subsampling_convert_ycbcrimage; + *m++=(uint8*)(sp->subsampling_convert_ycbcrimage+3); + *m++=(uint8*)(sp->subsampling_convert_ycbcrimage+3+sp->subsampling_convert_ylines); + *m++=(uint8*)(sp->subsampling_convert_ycbcrimage+3+sp->subsampling_convert_ylines+sp->subsampling_convert_clines); + for (n=0; nsubsampling_convert_ylines; n++) + *m++=sp->subsampling_convert_ybuf+n*sp->subsampling_convert_ylinelen; + for (n=0; nsubsampling_convert_clines; n++) + *m++=sp->subsampling_convert_cbbuf+n*sp->subsampling_convert_clinelen; + for (n=0; nsubsampling_convert_clines; n++) + *m++=sp->subsampling_convert_crbuf+n*sp->subsampling_convert_clinelen; + sp->subsampling_convert_clinelenout=((sp->strile_width+sp->subsampling_hor-1)/sp->subsampling_hor); + sp->subsampling_convert_state=0; + sp->bytes_per_line=sp->subsampling_convert_clinelenout*(sp->subsampling_ver*sp->subsampling_hor+2); + sp->lines_per_strile=((sp->strile_length+sp->subsampling_ver-1)/sp->subsampling_ver); + sp->subsampling_convert_log=1; + } + } + else + { + sp->libjpeg_jpeg_decompress_struct.jpeg_color_space=JCS_UNKNOWN; + sp->libjpeg_jpeg_decompress_struct.out_color_space=JCS_UNKNOWN; + sp->libjpeg_jpeg_query_style=1; + sp->bytes_per_line=sp->samples_per_pixel_per_plane*sp->strile_width; + sp->lines_per_strile=sp->strile_length; + } + if (jpeg_start_decompress_encap(sp,&(sp->libjpeg_jpeg_decompress_struct))==0) + return(0); + sp->writeheader_done=1; + return(1); +} + +static void +OJPEGLibjpegSessionAbort(TIFF* tif) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + assert(sp->libjpeg_session_active!=0); + jpeg_destroy((jpeg_common_struct*)(&(sp->libjpeg_jpeg_decompress_struct))); + sp->libjpeg_session_active=0; +} + +static int +OJPEGReadHeaderInfoSec(TIFF* tif) +{ + static const char module[]="OJPEGReadHeaderInfoSec"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint8 m; + uint16 n; + uint8 o; + if (sp->file_size==0) + sp->file_size=TIFFGetFileSize(tif); + if (sp->jpeg_interchange_format!=0) + { + if (sp->jpeg_interchange_format>=sp->file_size) + { + sp->jpeg_interchange_format=0; + sp->jpeg_interchange_format_length=0; + } + else + { + if ((sp->jpeg_interchange_format_length==0) || (sp->jpeg_interchange_format+sp->jpeg_interchange_format_length>sp->file_size)) + sp->jpeg_interchange_format_length=sp->file_size-sp->jpeg_interchange_format; + } + } + sp->in_buffer_source=osibsNotSetYet; + sp->in_buffer_next_strile=0; + sp->in_buffer_strile_count=tif->tif_dir.td_nstrips; + sp->in_buffer_file_togo=0; + sp->in_buffer_togo=0; + do + { + if (OJPEGReadBytePeek(sp,&m)==0) + return(0); + if (m!=255) + break; + OJPEGReadByteAdvance(sp); + do + { + if (OJPEGReadByte(sp,&m)==0) + return(0); + } while(m==255); + switch(m) + { + case JPEG_MARKER_SOI: + /* this type of marker has no data, and should be skipped */ + break; + case JPEG_MARKER_COM: + case JPEG_MARKER_APP0: + case JPEG_MARKER_APP0+1: + case JPEG_MARKER_APP0+2: + case JPEG_MARKER_APP0+3: + case JPEG_MARKER_APP0+4: + case JPEG_MARKER_APP0+5: + case JPEG_MARKER_APP0+6: + case JPEG_MARKER_APP0+7: + case JPEG_MARKER_APP0+8: + case JPEG_MARKER_APP0+9: + case JPEG_MARKER_APP0+10: + case JPEG_MARKER_APP0+11: + case JPEG_MARKER_APP0+12: + case JPEG_MARKER_APP0+13: + case JPEG_MARKER_APP0+14: + case JPEG_MARKER_APP0+15: + /* this type of marker has data, but it has no use to us (and no place here) and should be skipped */ + if (OJPEGReadWord(sp,&n)==0) + return(0); + if (n<2) + { + if (sp->subsamplingcorrect==0) + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt JPEG data"); + return(0); + } + if (n>2) + OJPEGReadSkip(sp,n-2); + break; + case JPEG_MARKER_DRI: + if (OJPEGReadHeaderInfoSecStreamDri(tif)==0) + return(0); + break; + case JPEG_MARKER_DQT: + if (OJPEGReadHeaderInfoSecStreamDqt(tif)==0) + return(0); + break; + case JPEG_MARKER_DHT: + if (OJPEGReadHeaderInfoSecStreamDht(tif)==0) + return(0); + break; + case JPEG_MARKER_SOF0: + case JPEG_MARKER_SOF1: + case JPEG_MARKER_SOF3: + if (OJPEGReadHeaderInfoSecStreamSof(tif,m)==0) + return(0); + if (sp->subsamplingcorrect!=0) + return(1); + break; + case JPEG_MARKER_SOS: + if (sp->subsamplingcorrect!=0) + return(1); + assert(sp->plane_sample_offset==0); + if (OJPEGReadHeaderInfoSecStreamSos(tif)==0) + return(0); + break; + default: + TIFFErrorExt(tif->tif_clientdata,module,"Unknown marker type %d in JPEG data",m); + return(0); + } + } while(m!=JPEG_MARKER_SOS); + if (sp->subsamplingcorrect) + return(1); + if (sp->sof_log==0) + { + if (OJPEGReadHeaderInfoSecTablesQTable(tif)==0) + return(0); + sp->sof_marker_id=JPEG_MARKER_SOF0; + for (o=0; osamples_per_pixel; o++) + sp->sof_c[o]=o; + sp->sof_hv[0]=((sp->subsampling_hor<<4)|sp->subsampling_ver); + for (o=1; osamples_per_pixel; o++) + sp->sof_hv[o]=17; + sp->sof_x=sp->strile_width; + sp->sof_y=sp->strile_length_total; + sp->sof_log=1; + if (OJPEGReadHeaderInfoSecTablesDcTable(tif)==0) + return(0); + if (OJPEGReadHeaderInfoSecTablesAcTable(tif)==0) + return(0); + for (o=1; osamples_per_pixel; o++) + sp->sos_cs[o]=o; + } + return(1); +} + +static int +OJPEGReadHeaderInfoSecStreamDri(TIFF* tif) +{ + /* this could easilly cause trouble in some cases... but no such cases have occured sofar */ + static const char module[]="OJPEGReadHeaderInfoSecStreamDri"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint16 m; + if (OJPEGReadWord(sp,&m)==0) + return(0); + if (m!=4) + { + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DRI marker in JPEG data"); + return(0); + } + if (OJPEGReadWord(sp,&m)==0) + return(0); + sp->restart_interval=m; + return(1); +} + +static int +OJPEGReadHeaderInfoSecStreamDqt(TIFF* tif) +{ + /* this is a table marker, and it is to be saved as a whole for exact pushing on the jpeg stream later on */ + static const char module[]="OJPEGReadHeaderInfoSecStreamDqt"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint16 m; + uint32 na; + uint8* nb; + uint8 o; + if (OJPEGReadWord(sp,&m)==0) + return(0); + if (m<=2) + { + if (sp->subsamplingcorrect==0) + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DQT marker in JPEG data"); + return(0); + } + if (sp->subsamplingcorrect!=0) + OJPEGReadSkip(sp,m-2); + else + { + m-=2; + do + { + if (m<65) + { + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DQT marker in JPEG data"); + return(0); + } + na=sizeof(uint32)+69; + nb=_TIFFmalloc(na); + if (nb==0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + *(uint32*)nb=na; + nb[sizeof(uint32)]=255; + nb[sizeof(uint32)+1]=JPEG_MARKER_DQT; + nb[sizeof(uint32)+2]=0; + nb[sizeof(uint32)+3]=67; + if (OJPEGReadBlock(sp,65,&nb[sizeof(uint32)+4])==0) { + _TIFFfree(nb); + return(0); + } + o=nb[sizeof(uint32)+4]&15; + if (3tif_clientdata,module,"Corrupt DQT marker in JPEG data"); + _TIFFfree(nb); + return(0); + } + if (sp->qtable[o]!=0) + _TIFFfree(sp->qtable[o]); + sp->qtable[o]=nb; + m-=65; + } while(m>0); + } + return(1); +} + +static int +OJPEGReadHeaderInfoSecStreamDht(TIFF* tif) +{ + /* this is a table marker, and it is to be saved as a whole for exact pushing on the jpeg stream later on */ + /* TODO: the following assumes there is only one table in this marker... but i'm not quite sure that assumption is guaranteed correct */ + static const char module[]="OJPEGReadHeaderInfoSecStreamDht"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint16 m; + uint32 na; + uint8* nb; + uint8 o; + if (OJPEGReadWord(sp,&m)==0) + return(0); + if (m<=2) + { + if (sp->subsamplingcorrect==0) + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DHT marker in JPEG data"); + return(0); + } + if (sp->subsamplingcorrect!=0) + { + OJPEGReadSkip(sp,m-2); + } + else + { + na=sizeof(uint32)+2+m; + nb=_TIFFmalloc(na); + if (nb==0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + *(uint32*)nb=na; + nb[sizeof(uint32)]=255; + nb[sizeof(uint32)+1]=JPEG_MARKER_DHT; + nb[sizeof(uint32)+2]=(m>>8); + nb[sizeof(uint32)+3]=(m&255); + if (OJPEGReadBlock(sp,m-2,&nb[sizeof(uint32)+4])==0) + return(0); + o=nb[sizeof(uint32)+4]; + if ((o&240)==0) + { + if (3tif_clientdata,module,"Corrupt DHT marker in JPEG data"); + return(0); + } + if (sp->dctable[o]!=0) + _TIFFfree(sp->dctable[o]); + sp->dctable[o]=nb; + } + else + { + if ((o&240)!=16) + { + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt DHT marker in JPEG data"); + return(0); + } + o&=15; + if (3tif_clientdata,module,"Corrupt DHT marker in JPEG data"); + return(0); + } + if (sp->actable[o]!=0) + _TIFFfree(sp->actable[o]); + sp->actable[o]=nb; + } + } + return(1); +} + +static int +OJPEGReadHeaderInfoSecStreamSof(TIFF* tif, uint8 marker_id) +{ + /* this marker needs to be checked, and part of its data needs to be saved for regeneration later on */ + static const char module[]="OJPEGReadHeaderInfoSecStreamSof"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint16 m; + uint16 n; + uint8 o; + uint16 p; + uint16 q; + if (sp->sof_log!=0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt JPEG data"); + return(0); + } + if (sp->subsamplingcorrect==0) + sp->sof_marker_id=marker_id; + /* Lf: data length */ + if (OJPEGReadWord(sp,&m)==0) + return(0); + if (m<11) + { + if (sp->subsamplingcorrect==0) + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOF marker in JPEG data"); + return(0); + } + m-=8; + if (m%3!=0) + { + if (sp->subsamplingcorrect==0) + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOF marker in JPEG data"); + return(0); + } + n=m/3; + if (sp->subsamplingcorrect==0) + { + if (n!=sp->samples_per_pixel) + { + TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates unexpected number of samples"); + return(0); + } + } + /* P: Sample precision */ + if (OJPEGReadByte(sp,&o)==0) + return(0); + if (o!=8) + { + if (sp->subsamplingcorrect==0) + TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates unexpected number of bits per sample"); + return(0); + } + /* Y: Number of lines, X: Number of samples per line */ + if (sp->subsamplingcorrect) + OJPEGReadSkip(sp,4); + else + { + /* Y: Number of lines */ + if (OJPEGReadWord(sp,&p)==0) + return(0); + if (((uint32)pimage_length) && ((uint32)pstrile_length_total)) + { + TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates unexpected height"); + return(0); + } + sp->sof_y=p; + /* X: Number of samples per line */ + if (OJPEGReadWord(sp,&p)==0) + return(0); + if (((uint32)pimage_width) && ((uint32)pstrile_width)) + { + TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates unexpected width"); + return(0); + } + if ((uint32)p>sp->strile_width) + { + TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data image width exceeds expected image width"); + return(0); + } + sp->sof_x=p; + } + /* Nf: Number of image components in frame */ + if (OJPEGReadByte(sp,&o)==0) + return(0); + if (o!=n) + { + if (sp->subsamplingcorrect==0) + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOF marker in JPEG data"); + return(0); + } + /* per component stuff */ + /* TODO: double-check that flow implies that n cannot be as big as to make us overflow sof_c, sof_hv and sof_tq arrays */ + for (q=0; qsubsamplingcorrect==0) + sp->sof_c[q]=o; + /* H: Horizontal sampling factor, and V: Vertical sampling factor */ + if (OJPEGReadByte(sp,&o)==0) + return(0); + if (sp->subsamplingcorrect!=0) + { + if (q==0) + { + sp->subsampling_hor=(o>>4); + sp->subsampling_ver=(o&15); + if (((sp->subsampling_hor!=1) && (sp->subsampling_hor!=2) && (sp->subsampling_hor!=4)) || + ((sp->subsampling_ver!=1) && (sp->subsampling_ver!=2) && (sp->subsampling_ver!=4))) + sp->subsampling_force_desubsampling_inside_decompression=1; + } + else + { + if (o!=17) + sp->subsampling_force_desubsampling_inside_decompression=1; + } + } + else + { + sp->sof_hv[q]=o; + if (sp->subsampling_force_desubsampling_inside_decompression==0) + { + if (q==0) + { + if (o!=((sp->subsampling_hor<<4)|sp->subsampling_ver)) + { + TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates unexpected subsampling values"); + return(0); + } + } + else + { + if (o!=17) + { + TIFFErrorExt(tif->tif_clientdata,module,"JPEG compressed data indicates unexpected subsampling values"); + return(0); + } + } + } + } + /* Tq: Quantization table destination selector */ + if (OJPEGReadByte(sp,&o)==0) + return(0); + if (sp->subsamplingcorrect==0) + sp->sof_tq[q]=o; + } + if (sp->subsamplingcorrect==0) + sp->sof_log=1; + return(1); +} + +static int +OJPEGReadHeaderInfoSecStreamSos(TIFF* tif) +{ + /* this marker needs to be checked, and part of its data needs to be saved for regeneration later on */ + static const char module[]="OJPEGReadHeaderInfoSecStreamSos"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint16 m; + uint8 n; + uint8 o; + assert(sp->subsamplingcorrect==0); + if (sp->sof_log==0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOS marker in JPEG data"); + return(0); + } + /* Ls */ + if (OJPEGReadWord(sp,&m)==0) + return(0); + if (m!=6+sp->samples_per_pixel_per_plane*2) + { + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOS marker in JPEG data"); + return(0); + } + /* Ns */ + if (OJPEGReadByte(sp,&n)==0) + return(0); + if (n!=sp->samples_per_pixel_per_plane) + { + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt SOS marker in JPEG data"); + return(0); + } + /* Cs, Td, and Ta */ + for (o=0; osamples_per_pixel_per_plane; o++) + { + /* Cs */ + if (OJPEGReadByte(sp,&n)==0) + return(0); + sp->sos_cs[sp->plane_sample_offset+o]=n; + /* Td and Ta */ + if (OJPEGReadByte(sp,&n)==0) + return(0); + sp->sos_tda[sp->plane_sample_offset+o]=n; + } + /* skip Ss, Se, Ah, en Al -> no check, as per Tom Lane recommendation, as per LibJpeg source */ + OJPEGReadSkip(sp,3); + return(1); +} + +static int +OJPEGReadHeaderInfoSecTablesQTable(TIFF* tif) +{ + static const char module[]="OJPEGReadHeaderInfoSecTablesQTable"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint8 m; + uint8 n; + uint32 oa; + uint8* ob; + uint32 p; + if (sp->qtable_offset[0]==0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Missing JPEG tables"); + return(0); + } + sp->in_buffer_file_pos_log=0; + for (m=0; msamples_per_pixel; m++) + { + if ((sp->qtable_offset[m]!=0) && ((m==0) || (sp->qtable_offset[m]!=sp->qtable_offset[m-1]))) + { + for (n=0; nqtable_offset[m]==sp->qtable_offset[n]) + { + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt JpegQTables tag value"); + return(0); + } + } + oa=sizeof(uint32)+69; + ob=_TIFFmalloc(oa); + if (ob==0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + *(uint32*)ob=oa; + ob[sizeof(uint32)]=255; + ob[sizeof(uint32)+1]=JPEG_MARKER_DQT; + ob[sizeof(uint32)+2]=0; + ob[sizeof(uint32)+3]=67; + ob[sizeof(uint32)+4]=m; + TIFFSeekFile(tif,sp->qtable_offset[m],SEEK_SET); + p=TIFFReadFile(tif,&ob[sizeof(uint32)+5],64); + if (p!=64) + return(0); + sp->qtable[m]=ob; + sp->sof_tq[m]=m; + } + else + sp->sof_tq[m]=sp->sof_tq[m-1]; + } + return(1); +} + +static int +OJPEGReadHeaderInfoSecTablesDcTable(TIFF* tif) +{ + static const char module[]="OJPEGReadHeaderInfoSecTablesDcTable"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint8 m; + uint8 n; + uint8 o[16]; + uint32 p; + uint32 q; + uint32 ra; + uint8* rb; + if (sp->dctable_offset[0]==0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Missing JPEG tables"); + return(0); + } + sp->in_buffer_file_pos_log=0; + for (m=0; msamples_per_pixel; m++) + { + if ((sp->dctable_offset[m]!=0) && ((m==0) || (sp->dctable_offset[m]!=sp->dctable_offset[m-1]))) + { + for (n=0; ndctable_offset[m]==sp->dctable_offset[n]) + { + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt JpegDcTables tag value"); + return(0); + } + } + TIFFSeekFile(tif,sp->dctable_offset[m],SEEK_SET); + p=TIFFReadFile(tif,o,16); + if (p!=16) + return(0); + q=0; + for (n=0; n<16; n++) + q+=o[n]; + ra=sizeof(uint32)+21+q; + rb=_TIFFmalloc(ra); + if (rb==0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + *(uint32*)rb=ra; + rb[sizeof(uint32)]=255; + rb[sizeof(uint32)+1]=JPEG_MARKER_DHT; + rb[sizeof(uint32)+2]=((19+q)>>8); + rb[sizeof(uint32)+3]=((19+q)&255); + rb[sizeof(uint32)+4]=m; + for (n=0; n<16; n++) + rb[sizeof(uint32)+5+n]=o[n]; + p=TIFFReadFile(tif,&(rb[sizeof(uint32)+21]),q); + if (p!=q) + return(0); + sp->dctable[m]=rb; + sp->sos_tda[m]=(m<<4); + } + else + sp->sos_tda[m]=sp->sos_tda[m-1]; + } + return(1); +} + +static int +OJPEGReadHeaderInfoSecTablesAcTable(TIFF* tif) +{ + static const char module[]="OJPEGReadHeaderInfoSecTablesAcTable"; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint8 m; + uint8 n; + uint8 o[16]; + uint32 p; + uint32 q; + uint32 ra; + uint8* rb; + if (sp->actable_offset[0]==0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Missing JPEG tables"); + return(0); + } + sp->in_buffer_file_pos_log=0; + for (m=0; msamples_per_pixel; m++) + { + if ((sp->actable_offset[m]!=0) && ((m==0) || (sp->actable_offset[m]!=sp->actable_offset[m-1]))) + { + for (n=0; nactable_offset[m]==sp->actable_offset[n]) + { + TIFFErrorExt(tif->tif_clientdata,module,"Corrupt JpegAcTables tag value"); + return(0); + } + } + TIFFSeekFile(tif,sp->actable_offset[m],SEEK_SET); + p=TIFFReadFile(tif,o,16); + if (p!=16) + return(0); + q=0; + for (n=0; n<16; n++) + q+=o[n]; + ra=sizeof(uint32)+21+q; + rb=_TIFFmalloc(ra); + if (rb==0) + { + TIFFErrorExt(tif->tif_clientdata,module,"Out of memory"); + return(0); + } + *(uint32*)rb=ra; + rb[sizeof(uint32)]=255; + rb[sizeof(uint32)+1]=JPEG_MARKER_DHT; + rb[sizeof(uint32)+2]=((19+q)>>8); + rb[sizeof(uint32)+3]=((19+q)&255); + rb[sizeof(uint32)+4]=(16|m); + for (n=0; n<16; n++) + rb[sizeof(uint32)+5+n]=o[n]; + p=TIFFReadFile(tif,&(rb[sizeof(uint32)+21]),q); + if (p!=q) + return(0); + sp->actable[m]=rb; + sp->sos_tda[m]=(sp->sos_tda[m]|m); + } + else + sp->sos_tda[m]=(sp->sos_tda[m]|(sp->sos_tda[m-1]&15)); + } + return(1); +} + +static int +OJPEGReadBufferFill(OJPEGState* sp) +{ + uint16 m; + tmsize_t n; + /* TODO: double-check: when subsamplingcorrect is set, no call to TIFFErrorExt or TIFFWarningExt should be made + * in any other case, seek or read errors should be passed through */ + do + { + if (sp->in_buffer_file_togo!=0) + { + if (sp->in_buffer_file_pos_log==0) + { + TIFFSeekFile(sp->tif,sp->in_buffer_file_pos,SEEK_SET); + sp->in_buffer_file_pos_log=1; + } + m=OJPEG_BUFFER; + if ((uint64)m>sp->in_buffer_file_togo) + m=(uint16)sp->in_buffer_file_togo; + n=TIFFReadFile(sp->tif,sp->in_buffer,(tmsize_t)m); + if (n==0) + return(0); + assert(n>0); + assert(n<=OJPEG_BUFFER); + assert(n<65536); + assert((uint64)n<=sp->in_buffer_file_togo); + m=(uint16)n; + sp->in_buffer_togo=m; + sp->in_buffer_cur=sp->in_buffer; + sp->in_buffer_file_togo-=m; + sp->in_buffer_file_pos+=m; + break; + } + sp->in_buffer_file_pos_log=0; + switch(sp->in_buffer_source) + { + case osibsNotSetYet: + if (sp->jpeg_interchange_format!=0) + { + sp->in_buffer_file_pos=sp->jpeg_interchange_format; + sp->in_buffer_file_togo=sp->jpeg_interchange_format_length; + } + sp->in_buffer_source=osibsJpegInterchangeFormat; + break; + case osibsJpegInterchangeFormat: + sp->in_buffer_source=osibsStrile; + case osibsStrile: + if (!_TIFFFillStriles( sp->tif ) + || sp->tif->tif_dir.td_stripoffset == NULL + || sp->tif->tif_dir.td_stripbytecount == NULL) + return 0; + + if (sp->in_buffer_next_strile==sp->in_buffer_strile_count) + sp->in_buffer_source=osibsEof; + else + { + sp->in_buffer_file_pos=sp->tif->tif_dir.td_stripoffset[sp->in_buffer_next_strile]; + if (sp->in_buffer_file_pos!=0) + { + if (sp->in_buffer_file_pos>=sp->file_size) + sp->in_buffer_file_pos=0; + else if (sp->tif->tif_dir.td_stripbytecount==NULL) + sp->in_buffer_file_togo=sp->file_size-sp->in_buffer_file_pos; + else + { + if (sp->tif->tif_dir.td_stripbytecount == 0) { + TIFFErrorExt(sp->tif->tif_clientdata,sp->tif->tif_name,"Strip byte counts are missing"); + return(0); + } + sp->in_buffer_file_togo=sp->tif->tif_dir.td_stripbytecount[sp->in_buffer_next_strile]; + if (sp->in_buffer_file_togo==0) + sp->in_buffer_file_pos=0; + else if (sp->in_buffer_file_pos+sp->in_buffer_file_togo>sp->file_size) + sp->in_buffer_file_togo=sp->file_size-sp->in_buffer_file_pos; + } + } + sp->in_buffer_next_strile++; + } + break; + default: + return(0); + } + } while (1); + return(1); +} + +static int +OJPEGReadByte(OJPEGState* sp, uint8* byte) +{ + if (sp->in_buffer_togo==0) + { + if (OJPEGReadBufferFill(sp)==0) + return(0); + assert(sp->in_buffer_togo>0); + } + *byte=*(sp->in_buffer_cur); + sp->in_buffer_cur++; + sp->in_buffer_togo--; + return(1); +} + +static int +OJPEGReadBytePeek(OJPEGState* sp, uint8* byte) +{ + if (sp->in_buffer_togo==0) + { + if (OJPEGReadBufferFill(sp)==0) + return(0); + assert(sp->in_buffer_togo>0); + } + *byte=*(sp->in_buffer_cur); + return(1); +} + +static void +OJPEGReadByteAdvance(OJPEGState* sp) +{ + assert(sp->in_buffer_togo>0); + sp->in_buffer_cur++; + sp->in_buffer_togo--; +} + +static int +OJPEGReadWord(OJPEGState* sp, uint16* word) +{ + uint8 m; + if (OJPEGReadByte(sp,&m)==0) + return(0); + *word=(m<<8); + if (OJPEGReadByte(sp,&m)==0) + return(0); + *word|=m; + return(1); +} + +static int +OJPEGReadBlock(OJPEGState* sp, uint16 len, void* mem) +{ + uint16 mlen; + uint8* mmem; + uint16 n; + assert(len>0); + mlen=len; + mmem=mem; + do + { + if (sp->in_buffer_togo==0) + { + if (OJPEGReadBufferFill(sp)==0) + return(0); + assert(sp->in_buffer_togo>0); + } + n=mlen; + if (n>sp->in_buffer_togo) + n=sp->in_buffer_togo; + _TIFFmemcpy(mmem,sp->in_buffer_cur,n); + sp->in_buffer_cur+=n; + sp->in_buffer_togo-=n; + mlen-=n; + mmem+=n; + } while(mlen>0); + return(1); +} + +static void +OJPEGReadSkip(OJPEGState* sp, uint16 len) +{ + uint16 m; + uint16 n; + m=len; + n=m; + if (n>sp->in_buffer_togo) + n=sp->in_buffer_togo; + sp->in_buffer_cur+=n; + sp->in_buffer_togo-=n; + m-=n; + if (m>0) + { + assert(sp->in_buffer_togo==0); + n=m; + if ((uint64)n>sp->in_buffer_file_togo) + n=(uint16)sp->in_buffer_file_togo; + sp->in_buffer_file_pos+=n; + sp->in_buffer_file_togo-=n; + sp->in_buffer_file_pos_log=0; + /* we don't skip past jpeginterchangeformat/strile block... + * if that is asked from us, we're dealing with totally bazurk + * data anyway, and we've not seen this happening on any + * testfile, so we might as well likely cause some other + * meaningless error to be passed at some later time + */ + } +} + +static int +OJPEGWriteStream(TIFF* tif, void** mem, uint32* len) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + *len=0; + do + { + assert(sp->out_state<=ososEoi); + switch(sp->out_state) + { + case ososSoi: + OJPEGWriteStreamSoi(tif,mem,len); + break; + case ososQTable0: + OJPEGWriteStreamQTable(tif,0,mem,len); + break; + case ososQTable1: + OJPEGWriteStreamQTable(tif,1,mem,len); + break; + case ososQTable2: + OJPEGWriteStreamQTable(tif,2,mem,len); + break; + case ososQTable3: + OJPEGWriteStreamQTable(tif,3,mem,len); + break; + case ososDcTable0: + OJPEGWriteStreamDcTable(tif,0,mem,len); + break; + case ososDcTable1: + OJPEGWriteStreamDcTable(tif,1,mem,len); + break; + case ososDcTable2: + OJPEGWriteStreamDcTable(tif,2,mem,len); + break; + case ososDcTable3: + OJPEGWriteStreamDcTable(tif,3,mem,len); + break; + case ososAcTable0: + OJPEGWriteStreamAcTable(tif,0,mem,len); + break; + case ososAcTable1: + OJPEGWriteStreamAcTable(tif,1,mem,len); + break; + case ososAcTable2: + OJPEGWriteStreamAcTable(tif,2,mem,len); + break; + case ososAcTable3: + OJPEGWriteStreamAcTable(tif,3,mem,len); + break; + case ososDri: + OJPEGWriteStreamDri(tif,mem,len); + break; + case ososSof: + OJPEGWriteStreamSof(tif,mem,len); + break; + case ososSos: + OJPEGWriteStreamSos(tif,mem,len); + break; + case ososCompressed: + if (OJPEGWriteStreamCompressed(tif,mem,len)==0) + return(0); + break; + case ososRst: + OJPEGWriteStreamRst(tif,mem,len); + break; + case ososEoi: + OJPEGWriteStreamEoi(tif,mem,len); + break; + } + } while (*len==0); + return(1); +} + +static void +OJPEGWriteStreamSoi(TIFF* tif, void** mem, uint32* len) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + assert(OJPEG_BUFFER>=2); + sp->out_buffer[0]=255; + sp->out_buffer[1]=JPEG_MARKER_SOI; + *len=2; + *mem=(void*)sp->out_buffer; + sp->out_state++; +} + +static void +OJPEGWriteStreamQTable(TIFF* tif, uint8 table_index, void** mem, uint32* len) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + if (sp->qtable[table_index]!=0) + { + *mem=(void*)(sp->qtable[table_index]+sizeof(uint32)); + *len=*((uint32*)sp->qtable[table_index])-sizeof(uint32); + } + sp->out_state++; +} + +static void +OJPEGWriteStreamDcTable(TIFF* tif, uint8 table_index, void** mem, uint32* len) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + if (sp->dctable[table_index]!=0) + { + *mem=(void*)(sp->dctable[table_index]+sizeof(uint32)); + *len=*((uint32*)sp->dctable[table_index])-sizeof(uint32); + } + sp->out_state++; +} + +static void +OJPEGWriteStreamAcTable(TIFF* tif, uint8 table_index, void** mem, uint32* len) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + if (sp->actable[table_index]!=0) + { + *mem=(void*)(sp->actable[table_index]+sizeof(uint32)); + *len=*((uint32*)sp->actable[table_index])-sizeof(uint32); + } + sp->out_state++; +} + +static void +OJPEGWriteStreamDri(TIFF* tif, void** mem, uint32* len) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + assert(OJPEG_BUFFER>=6); + if (sp->restart_interval!=0) + { + sp->out_buffer[0]=255; + sp->out_buffer[1]=JPEG_MARKER_DRI; + sp->out_buffer[2]=0; + sp->out_buffer[3]=4; + sp->out_buffer[4]=(sp->restart_interval>>8); + sp->out_buffer[5]=(sp->restart_interval&255); + *len=6; + *mem=(void*)sp->out_buffer; + } + sp->out_state++; +} + +static void +OJPEGWriteStreamSof(TIFF* tif, void** mem, uint32* len) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint8 m; + assert(OJPEG_BUFFER>=2+8+sp->samples_per_pixel_per_plane*3); + assert(255>=8+sp->samples_per_pixel_per_plane*3); + sp->out_buffer[0]=255; + sp->out_buffer[1]=sp->sof_marker_id; + /* Lf */ + sp->out_buffer[2]=0; + sp->out_buffer[3]=8+sp->samples_per_pixel_per_plane*3; + /* P */ + sp->out_buffer[4]=8; + /* Y */ + sp->out_buffer[5]=(sp->sof_y>>8); + sp->out_buffer[6]=(sp->sof_y&255); + /* X */ + sp->out_buffer[7]=(sp->sof_x>>8); + sp->out_buffer[8]=(sp->sof_x&255); + /* Nf */ + sp->out_buffer[9]=sp->samples_per_pixel_per_plane; + for (m=0; msamples_per_pixel_per_plane; m++) + { + /* C */ + sp->out_buffer[10+m*3]=sp->sof_c[sp->plane_sample_offset+m]; + /* H and V */ + sp->out_buffer[10+m*3+1]=sp->sof_hv[sp->plane_sample_offset+m]; + /* Tq */ + sp->out_buffer[10+m*3+2]=sp->sof_tq[sp->plane_sample_offset+m]; + } + *len=10+sp->samples_per_pixel_per_plane*3; + *mem=(void*)sp->out_buffer; + sp->out_state++; +} + +static void +OJPEGWriteStreamSos(TIFF* tif, void** mem, uint32* len) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + uint8 m; + assert(OJPEG_BUFFER>=2+6+sp->samples_per_pixel_per_plane*2); + assert(255>=6+sp->samples_per_pixel_per_plane*2); + sp->out_buffer[0]=255; + sp->out_buffer[1]=JPEG_MARKER_SOS; + /* Ls */ + sp->out_buffer[2]=0; + sp->out_buffer[3]=6+sp->samples_per_pixel_per_plane*2; + /* Ns */ + sp->out_buffer[4]=sp->samples_per_pixel_per_plane; + for (m=0; msamples_per_pixel_per_plane; m++) + { + /* Cs */ + sp->out_buffer[5+m*2]=sp->sos_cs[sp->plane_sample_offset+m]; + /* Td and Ta */ + sp->out_buffer[5+m*2+1]=sp->sos_tda[sp->plane_sample_offset+m]; + } + /* Ss */ + sp->out_buffer[5+sp->samples_per_pixel_per_plane*2]=0; + /* Se */ + sp->out_buffer[5+sp->samples_per_pixel_per_plane*2+1]=63; + /* Ah and Al */ + sp->out_buffer[5+sp->samples_per_pixel_per_plane*2+2]=0; + *len=8+sp->samples_per_pixel_per_plane*2; + *mem=(void*)sp->out_buffer; + sp->out_state++; +} + +static int +OJPEGWriteStreamCompressed(TIFF* tif, void** mem, uint32* len) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + if (sp->in_buffer_togo==0) + { + if (OJPEGReadBufferFill(sp)==0) + return(0); + assert(sp->in_buffer_togo>0); + } + *len=sp->in_buffer_togo; + *mem=(void*)sp->in_buffer_cur; + sp->in_buffer_togo=0; + if (sp->in_buffer_file_togo==0) + { + switch(sp->in_buffer_source) + { + case osibsStrile: + if (sp->in_buffer_next_strilein_buffer_strile_count) + sp->out_state=ososRst; + else + sp->out_state=ososEoi; + break; + case osibsEof: + sp->out_state=ososEoi; + break; + default: + break; + } + } + return(1); +} + +static void +OJPEGWriteStreamRst(TIFF* tif, void** mem, uint32* len) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + assert(OJPEG_BUFFER>=2); + sp->out_buffer[0]=255; + sp->out_buffer[1]=JPEG_MARKER_RST0+sp->restart_index; + sp->restart_index++; + if (sp->restart_index==8) + sp->restart_index=0; + *len=2; + *mem=(void*)sp->out_buffer; + sp->out_state=ososCompressed; +} + +static void +OJPEGWriteStreamEoi(TIFF* tif, void** mem, uint32* len) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + assert(OJPEG_BUFFER>=2); + sp->out_buffer[0]=255; + sp->out_buffer[1]=JPEG_MARKER_EOI; + *len=2; + *mem=(void*)sp->out_buffer; +} + +#ifndef LIBJPEG_ENCAP_EXTERNAL +static int +jpeg_create_decompress_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo) +{ + return(SETJMP(sp->exit_jmpbuf)?0:(jpeg_create_decompress(cinfo),1)); +} +#endif + +#ifndef LIBJPEG_ENCAP_EXTERNAL +static int +jpeg_read_header_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, uint8 require_image) +{ + return(SETJMP(sp->exit_jmpbuf)?0:(jpeg_read_header(cinfo,require_image),1)); +} +#endif + +#ifndef LIBJPEG_ENCAP_EXTERNAL +static int +jpeg_start_decompress_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo) +{ + return(SETJMP(sp->exit_jmpbuf)?0:(jpeg_start_decompress(cinfo),1)); +} +#endif + +#ifndef LIBJPEG_ENCAP_EXTERNAL +static int +jpeg_read_scanlines_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, void* scanlines, uint32 max_lines) +{ + return(SETJMP(sp->exit_jmpbuf)?0:(jpeg_read_scanlines(cinfo,scanlines,max_lines),1)); +} +#endif + +#ifndef LIBJPEG_ENCAP_EXTERNAL +static int +jpeg_read_raw_data_encap(OJPEGState* sp, jpeg_decompress_struct* cinfo, void* data, uint32 max_lines) +{ + return(SETJMP(sp->exit_jmpbuf)?0:(jpeg_read_raw_data(cinfo,data,max_lines),1)); +} +#endif + +#ifndef LIBJPEG_ENCAP_EXTERNAL +static void +jpeg_encap_unwind(TIFF* tif) +{ + OJPEGState* sp=(OJPEGState*)tif->tif_data; + LONGJMP(sp->exit_jmpbuf,1); +} +#endif + +static void +OJPEGLibjpegJpegErrorMgrOutputMessage(jpeg_common_struct* cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo,buffer); + TIFFWarningExt(((TIFF*)(cinfo->client_data))->tif_clientdata,"LibJpeg","%s",buffer); +} + +static void +OJPEGLibjpegJpegErrorMgrErrorExit(jpeg_common_struct* cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message)(cinfo,buffer); + TIFFErrorExt(((TIFF*)(cinfo->client_data))->tif_clientdata,"LibJpeg","%s",buffer); + jpeg_encap_unwind((TIFF*)(cinfo->client_data)); +} + +static void +OJPEGLibjpegJpegSourceMgrInitSource(jpeg_decompress_struct* cinfo) +{ + (void)cinfo; +} + +static boolean +OJPEGLibjpegJpegSourceMgrFillInputBuffer(jpeg_decompress_struct* cinfo) +{ + TIFF* tif=(TIFF*)cinfo->client_data; + OJPEGState* sp=(OJPEGState*)tif->tif_data; + void* mem=0; + uint32 len=0U; + if (OJPEGWriteStream(tif,&mem,&len)==0) + { + TIFFErrorExt(tif->tif_clientdata,"LibJpeg","Premature end of JPEG data"); + jpeg_encap_unwind(tif); + } + sp->libjpeg_jpeg_source_mgr.bytes_in_buffer=len; + sp->libjpeg_jpeg_source_mgr.next_input_byte=mem; + return(1); +} + +static void +OJPEGLibjpegJpegSourceMgrSkipInputData(jpeg_decompress_struct* cinfo, long num_bytes) +{ + TIFF* tif=(TIFF*)cinfo->client_data; + (void)num_bytes; + TIFFErrorExt(tif->tif_clientdata,"LibJpeg","Unexpected error"); + jpeg_encap_unwind(tif); +} + +static boolean +OJPEGLibjpegJpegSourceMgrResyncToRestart(jpeg_decompress_struct* cinfo, int desired) +{ + TIFF* tif=(TIFF*)cinfo->client_data; + (void)desired; + TIFFErrorExt(tif->tif_clientdata,"LibJpeg","Unexpected error"); + jpeg_encap_unwind(tif); + return(0); +} + +static void +OJPEGLibjpegJpegSourceMgrTermSource(jpeg_decompress_struct* cinfo) +{ + (void)cinfo; +} + +#endif + + +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_open.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_open.c new file mode 100644 index 000000000..8c88328cf --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_open.c @@ -0,0 +1,725 @@ +/* $Id: tif_open.c,v 1.46 2010-12-06 16:54:54 faxguy Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + */ +#include "tiffiop.h" + +/* + * Dummy functions to fill the omitted client procedures. + */ +static int +_tiffDummyMapProc(thandle_t fd, void** pbase, toff_t* psize) +{ + (void) fd; (void) pbase; (void) psize; + return (0); +} + +static void +_tiffDummyUnmapProc(thandle_t fd, void* base, toff_t size) +{ + (void) fd; (void) base; (void) size; +} + +int +_TIFFgetMode(const char* mode, const char* module) +{ + int m = -1; + + switch (mode[0]) { + case 'r': + m = O_RDONLY; + if (mode[1] == '+') + m = O_RDWR; + break; + case 'w': + case 'a': + m = O_RDWR|O_CREAT; + if (mode[0] == 'w') + m |= O_TRUNC; + break; + default: + TIFFErrorExt(0, module, "\"%s\": Bad mode", mode); + break; + } + return (m); +} + +TIFF* +TIFFClientOpen( + const char* name, const char* mode, + thandle_t clientdata, + TIFFReadWriteProc readproc, + TIFFReadWriteProc writeproc, + TIFFSeekProc seekproc, + TIFFCloseProc closeproc, + TIFFSizeProc sizeproc, + TIFFMapFileProc mapproc, + TIFFUnmapFileProc unmapproc +) +{ + static const char module[] = "TIFFClientOpen"; + TIFF *tif; + int m; + const char* cp; + + /* The following are configuration checks. They should be redundant, but should not + * compile to any actual code in an optimised release build anyway. If any of them + * fail, (makefile-based or other) configuration is not correct */ + assert(sizeof(uint8)==1); + assert(sizeof(int8)==1); + assert(sizeof(uint16)==2); + assert(sizeof(int16)==2); + assert(sizeof(uint32)==4); + assert(sizeof(int32)==4); + assert(sizeof(uint64)==8); + assert(sizeof(int64)==8); + assert(sizeof(tmsize_t)==sizeof(void*)); + { + union{ + uint8 a8[2]; + uint16 a16; + } n; + n.a8[0]=1; + n.a8[1]=0; + #ifdef WORDS_BIGENDIAN + assert(n.a16==256); + #else + assert(n.a16==1); + #endif + } + + m = _TIFFgetMode(mode, module); + if (m == -1) + goto bad2; + tif = (TIFF *)_TIFFmalloc((tmsize_t)(sizeof (TIFF) + strlen(name) + 1)); + if (tif == NULL) { + TIFFErrorExt(clientdata, module, "%s: Out of memory (TIFF structure)", name); + goto bad2; + } + _TIFFmemset(tif, 0, sizeof (*tif)); + tif->tif_name = (char *)tif + sizeof (TIFF); + strcpy(tif->tif_name, name); + tif->tif_mode = m &~ (O_CREAT|O_TRUNC); + tif->tif_curdir = (uint16) -1; /* non-existent directory */ + tif->tif_curoff = 0; + tif->tif_curstrip = (uint32) -1; /* invalid strip */ + tif->tif_row = (uint32) -1; /* read/write pre-increment */ + tif->tif_clientdata = clientdata; + if (!readproc || !writeproc || !seekproc || !closeproc || !sizeproc) { + TIFFErrorExt(clientdata, module, + "One of the client procedures is NULL pointer."); + goto bad2; + } + tif->tif_readproc = readproc; + tif->tif_writeproc = writeproc; + tif->tif_seekproc = seekproc; + tif->tif_closeproc = closeproc; + tif->tif_sizeproc = sizeproc; + if (mapproc) + tif->tif_mapproc = mapproc; + else + tif->tif_mapproc = _tiffDummyMapProc; + if (unmapproc) + tif->tif_unmapproc = unmapproc; + else + tif->tif_unmapproc = _tiffDummyUnmapProc; + _TIFFSetDefaultCompressionState(tif); /* setup default state */ + /* + * Default is to return data MSB2LSB and enable the + * use of memory-mapped files and strip chopping when + * a file is opened read-only. + */ + tif->tif_flags = FILLORDER_MSB2LSB; + if (m == O_RDONLY ) + tif->tif_flags |= TIFF_MAPPED; + + #ifdef STRIPCHOP_DEFAULT + if (m == O_RDONLY || m == O_RDWR) + tif->tif_flags |= STRIPCHOP_DEFAULT; + #endif + + /* + * Process library-specific flags in the open mode string. + * The following flags may be used to control intrinsic library + * behaviour that may or may not be desirable (usually for + * compatibility with some application that claims to support + * TIFF but only supports some braindead idea of what the + * vendor thinks TIFF is): + * + * 'l' use little-endian byte order for creating a file + * 'b' use big-endian byte order for creating a file + * 'L' read/write information using LSB2MSB bit order + * 'B' read/write information using MSB2LSB bit order + * 'H' read/write information using host bit order + * 'M' enable use of memory-mapped files when supported + * 'm' disable use of memory-mapped files + * 'C' enable strip chopping support when reading + * 'c' disable strip chopping support + * 'h' read TIFF header only, do not load the first IFD + * '4' ClassicTIFF for creating a file (default) + * '8' BigTIFF for creating a file + * + * The use of the 'l' and 'b' flags is strongly discouraged. + * These flags are provided solely because numerous vendors, + * typically on the PC, do not correctly support TIFF; they + * only support the Intel little-endian byte order. This + * support is not configured by default because it supports + * the violation of the TIFF spec that says that readers *MUST* + * support both byte orders. It is strongly recommended that + * you not use this feature except to deal with busted apps + * that write invalid TIFF. And even in those cases you should + * bang on the vendors to fix their software. + * + * The 'L', 'B', and 'H' flags are intended for applications + * that can optimize operations on data by using a particular + * bit order. By default the library returns data in MSB2LSB + * bit order for compatibiltiy with older versions of this + * library. Returning data in the bit order of the native cpu + * makes the most sense but also requires applications to check + * the value of the FillOrder tag; something they probably do + * not do right now. + * + * The 'M' and 'm' flags are provided because some virtual memory + * systems exhibit poor behaviour when large images are mapped. + * These options permit clients to control the use of memory-mapped + * files on a per-file basis. + * + * The 'C' and 'c' flags are provided because the library support + * for chopping up large strips into multiple smaller strips is not + * application-transparent and as such can cause problems. The 'c' + * option permits applications that only want to look at the tags, + * for example, to get the unadulterated TIFF tag information. + */ + for (cp = mode; *cp; cp++) + switch (*cp) { + case 'b': + #ifndef WORDS_BIGENDIAN + if (m&O_CREAT) + tif->tif_flags |= TIFF_SWAB; + #endif + break; + case 'l': + #ifdef WORDS_BIGENDIAN + if ((m&O_CREAT)) + tif->tif_flags |= TIFF_SWAB; + #endif + break; + case 'B': + tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) | + FILLORDER_MSB2LSB; + break; + case 'L': + tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) | + FILLORDER_LSB2MSB; + break; + case 'H': + tif->tif_flags = (tif->tif_flags &~ TIFF_FILLORDER) | + HOST_FILLORDER; + break; + case 'M': + if (m == O_RDONLY) + tif->tif_flags |= TIFF_MAPPED; + break; + case 'm': + if (m == O_RDONLY) + tif->tif_flags &= ~TIFF_MAPPED; + break; + case 'C': + if (m == O_RDONLY) + tif->tif_flags |= TIFF_STRIPCHOP; + break; + case 'c': + if (m == O_RDONLY) + tif->tif_flags &= ~TIFF_STRIPCHOP; + break; + case 'h': + tif->tif_flags |= TIFF_HEADERONLY; + break; + case '8': + if (m&O_CREAT) + tif->tif_flags |= TIFF_BIGTIFF; + break; + } + /* + * Read in TIFF header. + */ + if ((m & O_TRUNC) || + !ReadOK(tif, &tif->tif_header, sizeof (TIFFHeaderClassic))) { + if (tif->tif_mode == O_RDONLY) { + TIFFErrorExt(tif->tif_clientdata, name, + "Cannot read TIFF header"); + goto bad; + } + /* + * Setup header and write. + */ + #ifdef WORDS_BIGENDIAN + tif->tif_header.common.tiff_magic = tif->tif_flags & TIFF_SWAB + ? TIFF_LITTLEENDIAN : TIFF_BIGENDIAN; + #else + tif->tif_header.common.tiff_magic = tif->tif_flags & TIFF_SWAB + ? TIFF_BIGENDIAN : TIFF_LITTLEENDIAN; + #endif + if (!(tif->tif_flags&TIFF_BIGTIFF)) + { + tif->tif_header.common.tiff_version = TIFF_VERSION_CLASSIC; + tif->tif_header.classic.tiff_diroff = 0; + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&tif->tif_header.common.tiff_version); + tif->tif_header_size = sizeof(TIFFHeaderClassic); + } + else + { + tif->tif_header.common.tiff_version = TIFF_VERSION_BIG; + tif->tif_header.big.tiff_offsetsize = 8; + tif->tif_header.big.tiff_unused = 0; + tif->tif_header.big.tiff_diroff = 0; + if (tif->tif_flags & TIFF_SWAB) + { + TIFFSwabShort(&tif->tif_header.common.tiff_version); + TIFFSwabShort(&tif->tif_header.big.tiff_offsetsize); + } + tif->tif_header_size = sizeof (TIFFHeaderBig); + } + /* + * The doc for "fopen" for some STD_C_LIBs says that if you + * open a file for modify ("+"), then you must fseek (or + * fflush?) between any freads and fwrites. This is not + * necessary on most systems, but has been shown to be needed + * on Solaris. + */ + TIFFSeekFile( tif, 0, SEEK_SET ); + if (!WriteOK(tif, &tif->tif_header, (tmsize_t)(tif->tif_header_size))) { + TIFFErrorExt(tif->tif_clientdata, name, + "Error writing TIFF header"); + goto bad; + } + /* + * Setup the byte order handling. + */ + if (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN) { + #ifndef WORDS_BIGENDIAN + tif->tif_flags |= TIFF_SWAB; + #endif + } else { + #ifdef WORDS_BIGENDIAN + tif->tif_flags |= TIFF_SWAB; + #endif + } + /* + * Setup default directory. + */ + if (!TIFFDefaultDirectory(tif)) + goto bad; + tif->tif_diroff = 0; + tif->tif_dirlist = NULL; + tif->tif_dirlistsize = 0; + tif->tif_dirnumber = 0; + return (tif); + } + /* + * Setup the byte order handling. + */ + if (tif->tif_header.common.tiff_magic != TIFF_BIGENDIAN && + tif->tif_header.common.tiff_magic != TIFF_LITTLEENDIAN + #if MDI_SUPPORT + && + #if HOST_BIGENDIAN + tif->tif_header.common.tiff_magic != MDI_BIGENDIAN + #else + tif->tif_header.common.tiff_magic != MDI_LITTLEENDIAN + #endif + ) { + TIFFErrorExt(tif->tif_clientdata, name, + "Not a TIFF or MDI file, bad magic number %d (0x%x)", + #else + ) { + TIFFErrorExt(tif->tif_clientdata, name, + "Not a TIFF file, bad magic number %d (0x%x)", + #endif + tif->tif_header.common.tiff_magic, + tif->tif_header.common.tiff_magic); + goto bad; + } + if (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN) { + #ifndef WORDS_BIGENDIAN + tif->tif_flags |= TIFF_SWAB; + #endif + } else { + #ifdef WORDS_BIGENDIAN + tif->tif_flags |= TIFF_SWAB; + #endif + } + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabShort(&tif->tif_header.common.tiff_version); + if ((tif->tif_header.common.tiff_version != TIFF_VERSION_CLASSIC)&& + (tif->tif_header.common.tiff_version != TIFF_VERSION_BIG)) { + TIFFErrorExt(tif->tif_clientdata, name, + "Not a TIFF file, bad version number %d (0x%x)", + tif->tif_header.common.tiff_version, + tif->tif_header.common.tiff_version); + goto bad; + } + if (tif->tif_header.common.tiff_version == TIFF_VERSION_CLASSIC) + { + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabLong(&tif->tif_header.classic.tiff_diroff); + tif->tif_header_size = sizeof(TIFFHeaderClassic); + } + else + { + if (!ReadOK(tif, ((uint8*)(&tif->tif_header) + sizeof(TIFFHeaderClassic)), (sizeof(TIFFHeaderBig)-sizeof(TIFFHeaderClassic)))) + { + TIFFErrorExt(tif->tif_clientdata, name, + "Cannot read TIFF header"); + goto bad; + } + if (tif->tif_flags & TIFF_SWAB) + { + TIFFSwabShort(&tif->tif_header.big.tiff_offsetsize); + TIFFSwabLong8(&tif->tif_header.big.tiff_diroff); + } + if (tif->tif_header.big.tiff_offsetsize != 8) + { + TIFFErrorExt(tif->tif_clientdata, name, + "Not a TIFF file, bad BigTIFF offsetsize %d (0x%x)", + tif->tif_header.big.tiff_offsetsize, + tif->tif_header.big.tiff_offsetsize); + goto bad; + } + if (tif->tif_header.big.tiff_unused != 0) + { + TIFFErrorExt(tif->tif_clientdata, name, + "Not a TIFF file, bad BigTIFF unused %d (0x%x)", + tif->tif_header.big.tiff_unused, + tif->tif_header.big.tiff_unused); + goto bad; + } + tif->tif_header_size = sizeof(TIFFHeaderBig); + tif->tif_flags |= TIFF_BIGTIFF; + } + tif->tif_flags |= TIFF_MYBUFFER; + tif->tif_rawcp = tif->tif_rawdata = 0; + tif->tif_rawdatasize = 0; + tif->tif_rawdataoff = 0; + tif->tif_rawdataloaded = 0; + + switch (mode[0]) { + case 'r': + if (!(tif->tif_flags&TIFF_BIGTIFF)) + tif->tif_nextdiroff = tif->tif_header.classic.tiff_diroff; + else + tif->tif_nextdiroff = tif->tif_header.big.tiff_diroff; + /* + * Try to use a memory-mapped file if the client + * has not explicitly suppressed usage with the + * 'm' flag in the open mode (see above). + */ + if (tif->tif_flags & TIFF_MAPPED) + { + toff_t n; + if (TIFFMapFileContents(tif,(void**)(&tif->tif_base),&n)) + { + tif->tif_size=(tmsize_t)n; + assert((toff_t)tif->tif_size==n); + } + else + tif->tif_flags &= ~TIFF_MAPPED; + } + /* + * Sometimes we do not want to read the first directory (for example, + * it may be broken) and want to proceed to other directories. I this + * case we use the TIFF_HEADERONLY flag to open file and return + * immediately after reading TIFF header. + */ + if (tif->tif_flags & TIFF_HEADERONLY) + return (tif); + + /* + * Setup initial directory. + */ + if (TIFFReadDirectory(tif)) { + tif->tif_rawcc = (tmsize_t)-1; + tif->tif_flags |= TIFF_BUFFERSETUP; + return (tif); + } + break; + case 'a': + /* + * New directories are automatically append + * to the end of the directory chain when they + * are written out (see TIFFWriteDirectory). + */ + if (!TIFFDefaultDirectory(tif)) + goto bad; + return (tif); + } +bad: + tif->tif_mode = O_RDONLY; /* XXX avoid flush */ + TIFFCleanup(tif); +bad2: + return ((TIFF*)0); +} + +/* + * Query functions to access private data. + */ + +/* + * Return open file's name. + */ +const char * +TIFFFileName(TIFF* tif) +{ + return (tif->tif_name); +} + +/* + * Set the file name. + */ +const char * +TIFFSetFileName(TIFF* tif, const char *name) +{ + const char* old_name = tif->tif_name; + tif->tif_name = (char *)name; + return (old_name); +} + +/* + * Return open file's I/O descriptor. + */ +int +TIFFFileno(TIFF* tif) +{ + return (tif->tif_fd); +} + +/* + * Set open file's I/O descriptor, and return previous value. + */ +int +TIFFSetFileno(TIFF* tif, int fd) +{ + int old_fd = tif->tif_fd; + tif->tif_fd = fd; + return old_fd; +} + +/* + * Return open file's clientdata. + */ +thandle_t +TIFFClientdata(TIFF* tif) +{ + return (tif->tif_clientdata); +} + +/* + * Set open file's clientdata, and return previous value. + */ +thandle_t +TIFFSetClientdata(TIFF* tif, thandle_t newvalue) +{ + thandle_t m = tif->tif_clientdata; + tif->tif_clientdata = newvalue; + return m; +} + +/* + * Return read/write mode. + */ +int +TIFFGetMode(TIFF* tif) +{ + return (tif->tif_mode); +} + +/* + * Return read/write mode. + */ +int +TIFFSetMode(TIFF* tif, int mode) +{ + int old_mode = tif->tif_mode; + tif->tif_mode = mode; + return (old_mode); +} + +/* + * Return nonzero if file is organized in + * tiles; zero if organized as strips. + */ +int +TIFFIsTiled(TIFF* tif) +{ + return (isTiled(tif)); +} + +/* + * Return current row being read/written. + */ +uint32 +TIFFCurrentRow(TIFF* tif) +{ + return (tif->tif_row); +} + +/* + * Return index of the current directory. + */ +uint16 +TIFFCurrentDirectory(TIFF* tif) +{ + return (tif->tif_curdir); +} + +/* + * Return current strip. + */ +uint32 +TIFFCurrentStrip(TIFF* tif) +{ + return (tif->tif_curstrip); +} + +/* + * Return current tile. + */ +uint32 +TIFFCurrentTile(TIFF* tif) +{ + return (tif->tif_curtile); +} + +/* + * Return nonzero if the file has byte-swapped data. + */ +int +TIFFIsByteSwapped(TIFF* tif) +{ + return ((tif->tif_flags & TIFF_SWAB) != 0); +} + +/* + * Return nonzero if the data is returned up-sampled. + */ +int +TIFFIsUpSampled(TIFF* tif) +{ + return (isUpSampled(tif)); +} + +/* + * Return nonzero if the data is returned in MSB-to-LSB bit order. + */ +int +TIFFIsMSB2LSB(TIFF* tif) +{ + return (isFillOrder(tif, FILLORDER_MSB2LSB)); +} + +/* + * Return nonzero if given file was written in big-endian order. + */ +int +TIFFIsBigEndian(TIFF* tif) +{ + return (tif->tif_header.common.tiff_magic == TIFF_BIGENDIAN); +} + +/* + * Return pointer to file read method. + */ +TIFFReadWriteProc +TIFFGetReadProc(TIFF* tif) +{ + return (tif->tif_readproc); +} + +/* + * Return pointer to file write method. + */ +TIFFReadWriteProc +TIFFGetWriteProc(TIFF* tif) +{ + return (tif->tif_writeproc); +} + +/* + * Return pointer to file seek method. + */ +TIFFSeekProc +TIFFGetSeekProc(TIFF* tif) +{ + return (tif->tif_seekproc); +} + +/* + * Return pointer to file close method. + */ +TIFFCloseProc +TIFFGetCloseProc(TIFF* tif) +{ + return (tif->tif_closeproc); +} + +/* + * Return pointer to file size requesting method. + */ +TIFFSizeProc +TIFFGetSizeProc(TIFF* tif) +{ + return (tif->tif_sizeproc); +} + +/* + * Return pointer to memory mapping method. + */ +TIFFMapFileProc +TIFFGetMapFileProc(TIFF* tif) +{ + return (tif->tif_mapproc); +} + +/* + * Return pointer to memory unmapping method. + */ +TIFFUnmapFileProc +TIFFGetUnmapFileProc(TIFF* tif) +{ + return (tif->tif_unmapproc); +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_packbits.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_packbits.c new file mode 100644 index 000000000..9e7719013 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_packbits.c @@ -0,0 +1,300 @@ +/* $Id: tif_packbits.c,v 1.22 2012-06-20 05:25:33 fwarmerdam Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "tiffiop.h" +#ifdef PACKBITS_SUPPORT +/* + * TIFF Library. + * + * PackBits Compression Algorithm Support + */ +#include + +static int +PackBitsPreEncode(TIFF* tif, uint16 s) +{ + (void) s; + + if (!(tif->tif_data = (uint8*)_TIFFmalloc(sizeof(tmsize_t)))) + return (0); + /* + * Calculate the scanline/tile-width size in bytes. + */ + if (isTiled(tif)) + *(tmsize_t*)tif->tif_data = TIFFTileRowSize(tif); + else + *(tmsize_t*)tif->tif_data = TIFFScanlineSize(tif); + return (1); +} + +static int +PackBitsPostEncode(TIFF* tif) +{ + if (tif->tif_data) + _TIFFfree(tif->tif_data); + return (1); +} + +/* + * Encode a run of pixels. + */ +static int +PackBitsEncode(TIFF* tif, uint8* buf, tmsize_t cc, uint16 s) +{ + unsigned char* bp = (unsigned char*) buf; + uint8* op; + uint8* ep; + uint8* lastliteral; + long n, slop; + int b; + enum { BASE, LITERAL, RUN, LITERAL_RUN } state; + + (void) s; + op = tif->tif_rawcp; + ep = tif->tif_rawdata + tif->tif_rawdatasize; + state = BASE; + lastliteral = 0; + while (cc > 0) { + /* + * Find the longest string of identical bytes. + */ + b = *bp++, cc--, n = 1; + for (; cc > 0 && b == *bp; cc--, bp++) + n++; + again: + if (op + 2 >= ep) { /* insure space for new data */ + /* + * Be careful about writing the last + * literal. Must write up to that point + * and then copy the remainder to the + * front of the buffer. + */ + if (state == LITERAL || state == LITERAL_RUN) { + slop = (long)(op - lastliteral); + tif->tif_rawcc += (tmsize_t)(lastliteral - tif->tif_rawcp); + if (!TIFFFlushData1(tif)) + return (-1); + op = tif->tif_rawcp; + while (slop-- > 0) + *op++ = *lastliteral++; + lastliteral = tif->tif_rawcp; + } else { + tif->tif_rawcc += (tmsize_t)(op - tif->tif_rawcp); + if (!TIFFFlushData1(tif)) + return (-1); + op = tif->tif_rawcp; + } + } + switch (state) { + case BASE: /* initial state, set run/literal */ + if (n > 1) { + state = RUN; + if (n > 128) { + *op++ = (uint8) -127; + *op++ = (uint8) b; + n -= 128; + goto again; + } + *op++ = (uint8)(-(n-1)); + *op++ = (uint8) b; + } else { + lastliteral = op; + *op++ = 0; + *op++ = (uint8) b; + state = LITERAL; + } + break; + case LITERAL: /* last object was literal string */ + if (n > 1) { + state = LITERAL_RUN; + if (n > 128) { + *op++ = (uint8) -127; + *op++ = (uint8) b; + n -= 128; + goto again; + } + *op++ = (uint8)(-(n-1)); /* encode run */ + *op++ = (uint8) b; + } else { /* extend literal */ + if (++(*lastliteral) == 127) + state = BASE; + *op++ = (uint8) b; + } + break; + case RUN: /* last object was run */ + if (n > 1) { + if (n > 128) { + *op++ = (uint8) -127; + *op++ = (uint8) b; + n -= 128; + goto again; + } + *op++ = (uint8)(-(n-1)); + *op++ = (uint8) b; + } else { + lastliteral = op; + *op++ = 0; + *op++ = (uint8) b; + state = LITERAL; + } + break; + case LITERAL_RUN: /* literal followed by a run */ + /* + * Check to see if previous run should + * be converted to a literal, in which + * case we convert literal-run-literal + * to a single literal. + */ + if (n == 1 && op[-2] == (uint8) -1 && + *lastliteral < 126) { + state = (((*lastliteral) += 2) == 127 ? + BASE : LITERAL); + op[-2] = op[-1]; /* replicate */ + } else + state = RUN; + goto again; + } + } + tif->tif_rawcc += (tmsize_t)(op - tif->tif_rawcp); + tif->tif_rawcp = op; + return (1); +} + +/* + * Encode a rectangular chunk of pixels. We break it up + * into row-sized pieces to insure that encoded runs do + * not span rows. Otherwise, there can be problems with + * the decoder if data is read, for example, by scanlines + * when it was encoded by strips. + */ +static int +PackBitsEncodeChunk(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + tmsize_t rowsize = *(tmsize_t*)tif->tif_data; + + while (cc > 0) { + tmsize_t chunk = rowsize; + + if( cc < chunk ) + chunk = cc; + + if (PackBitsEncode(tif, bp, chunk, s) < 0) + return (-1); + bp += chunk; + cc -= chunk; + } + return (1); +} + +static int +PackBitsDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s) +{ + static const char module[] = "PackBitsDecode"; + char *bp; + tmsize_t cc; + long n; + int b; + + (void) s; + bp = (char*) tif->tif_rawcp; + cc = tif->tif_rawcc; + while (cc > 0 && occ > 0) { + n = (long) *bp++, cc--; + /* + * Watch out for compilers that + * don't sign extend chars... + */ + if (n >= 128) + n -= 256; + if (n < 0) { /* replicate next byte -n+1 times */ + if (n == -128) /* nop */ + continue; + n = -n + 1; + if( occ < (tmsize_t)n ) + { + TIFFWarningExt(tif->tif_clientdata, module, + "Discarding %lu bytes to avoid buffer overrun", + (unsigned long) ((tmsize_t)n - occ)); + n = (long)occ; + } + occ -= n; + b = *bp++, cc--; + while (n-- > 0) + *op++ = (uint8) b; + } else { /* copy next n+1 bytes literally */ + if (occ < (tmsize_t)(n + 1)) + { + TIFFWarningExt(tif->tif_clientdata, module, + "Discarding %lu bytes to avoid buffer overrun", + (unsigned long) ((tmsize_t)n - occ + 1)); + n = (long)occ - 1; + } + if (cc < (tmsize_t) (n+1)) + { + TIFFWarningExt(tif->tif_clientdata, module, + "Terminating PackBitsDecode due to lack of data."); + break; + } + _TIFFmemcpy(op, bp, ++n); + op += n; occ -= n; + bp += n; cc -= n; + } + } + tif->tif_rawcp = (uint8*) bp; + tif->tif_rawcc = cc; + if (occ > 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data for scanline %lu", + (unsigned long) tif->tif_row); + return (0); + } + return (1); +} + +int +TIFFInitPackBits(TIFF* tif, int scheme) +{ + (void) scheme; + tif->tif_decoderow = PackBitsDecode; + tif->tif_decodestrip = PackBitsDecode; + tif->tif_decodetile = PackBitsDecode; + tif->tif_preencode = PackBitsPreEncode; + tif->tif_postencode = PackBitsPostEncode; + tif->tif_encoderow = PackBitsEncode; + tif->tif_encodestrip = PackBitsEncodeChunk; + tif->tif_encodetile = PackBitsEncodeChunk; + return (1); +} +#endif /* PACKBITS_SUPPORT */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_pixarlog.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_pixarlog.c new file mode 100644 index 000000000..044c4115c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_pixarlog.c @@ -0,0 +1,1442 @@ +/* $Id: tif_pixarlog.c,v 1.39 2012-12-10 17:27:13 tgl Exp $ */ + +/* + * Copyright (c) 1996-1997 Sam Leffler + * Copyright (c) 1996 Pixar + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Pixar, Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Pixar, Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL PIXAR, SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "tiffiop.h" +#ifdef PIXARLOG_SUPPORT + +/* + * TIFF Library. + * PixarLog Compression Support + * + * Contributed by Dan McCoy. + * + * PixarLog film support uses the TIFF library to store companded + * 11 bit values into a tiff file, which are compressed using the + * zip compressor. + * + * The codec can take as input and produce as output 32-bit IEEE float values + * as well as 16-bit or 8-bit unsigned integer values. + * + * On writing any of the above are converted into the internal + * 11-bit log format. In the case of 8 and 16 bit values, the + * input is assumed to be unsigned linear color values that represent + * the range 0-1. In the case of IEEE values, the 0-1 range is assumed to + * be the normal linear color range, in addition over 1 values are + * accepted up to a value of about 25.0 to encode "hot" hightlights and such. + * The encoding is lossless for 8-bit values, slightly lossy for the + * other bit depths. The actual color precision should be better + * than the human eye can perceive with extra room to allow for + * error introduced by further image computation. As with any quantized + * color format, it is possible to perform image calculations which + * expose the quantization error. This format should certainly be less + * susceptable to such errors than standard 8-bit encodings, but more + * susceptable than straight 16-bit or 32-bit encodings. + * + * On reading the internal format is converted to the desired output format. + * The program can request which format it desires by setting the internal + * pseudo tag TIFFTAG_PIXARLOGDATAFMT to one of these possible values: + * PIXARLOGDATAFMT_FLOAT = provide IEEE float values. + * PIXARLOGDATAFMT_16BIT = provide unsigned 16-bit integer values + * PIXARLOGDATAFMT_8BIT = provide unsigned 8-bit integer values + * + * alternately PIXARLOGDATAFMT_8BITABGR provides unsigned 8-bit integer + * values with the difference that if there are exactly three or four channels + * (rgb or rgba) it swaps the channel order (bgr or abgr). + * + * PIXARLOGDATAFMT_11BITLOG provides the internal encoding directly + * packed in 16-bit values. However no tools are supplied for interpreting + * these values. + * + * "hot" (over 1.0) areas written in floating point get clamped to + * 1.0 in the integer data types. + * + * When the file is closed after writing, the bit depth and sample format + * are set always to appear as if 8-bit data has been written into it. + * That way a naive program unaware of the particulars of the encoding + * gets the format it is most likely able to handle. + * + * The codec does it's own horizontal differencing step on the coded + * values so the libraries predictor stuff should be turned off. + * The codec also handle byte swapping the encoded values as necessary + * since the library does not have the information necessary + * to know the bit depth of the raw unencoded buffer. + * + * NOTE: This decoder does not appear to update tif_rawcp, and tif_rawcc. + * This can cause problems with the implementation of CHUNKY_STRIP_READ_SUPPORT + * as noted in http://trac.osgeo.org/gdal/ticket/3894. FrankW - Jan'11 + */ + +#include "tif_predict.h" +#include "zlib.h" + +#include +#include +#include + +/* Tables for converting to/from 11 bit coded values */ + +#define TSIZE 2048 /* decode table size (11-bit tokens) */ +#define TSIZEP1 2049 /* Plus one for slop */ +#define ONE 1250 /* token value of 1.0 exactly */ +#define RATIO 1.004 /* nominal ratio for log part */ + +#define CODE_MASK 0x7ff /* 11 bits. */ + +static float Fltsize; +static float LogK1, LogK2; + +#define REPEAT(n, op) { int i; i=n; do { i--; op; } while (i>0); } + +static void +horizontalAccumulateF(uint16 *wp, int n, int stride, float *op, + float *ToLinearF) +{ + register unsigned int cr, cg, cb, ca, mask; + register float t0, t1, t2, t3; + + if (n >= stride) { + mask = CODE_MASK; + if (stride == 3) { + t0 = ToLinearF[cr = (wp[0] & mask)]; + t1 = ToLinearF[cg = (wp[1] & mask)]; + t2 = ToLinearF[cb = (wp[2] & mask)]; + op[0] = t0; + op[1] = t1; + op[2] = t2; + n -= 3; + while (n > 0) { + wp += 3; + op += 3; + n -= 3; + t0 = ToLinearF[(cr += wp[0]) & mask]; + t1 = ToLinearF[(cg += wp[1]) & mask]; + t2 = ToLinearF[(cb += wp[2]) & mask]; + op[0] = t0; + op[1] = t1; + op[2] = t2; + } + } else if (stride == 4) { + t0 = ToLinearF[cr = (wp[0] & mask)]; + t1 = ToLinearF[cg = (wp[1] & mask)]; + t2 = ToLinearF[cb = (wp[2] & mask)]; + t3 = ToLinearF[ca = (wp[3] & mask)]; + op[0] = t0; + op[1] = t1; + op[2] = t2; + op[3] = t3; + n -= 4; + while (n > 0) { + wp += 4; + op += 4; + n -= 4; + t0 = ToLinearF[(cr += wp[0]) & mask]; + t1 = ToLinearF[(cg += wp[1]) & mask]; + t2 = ToLinearF[(cb += wp[2]) & mask]; + t3 = ToLinearF[(ca += wp[3]) & mask]; + op[0] = t0; + op[1] = t1; + op[2] = t2; + op[3] = t3; + } + } else { + REPEAT(stride, *op = ToLinearF[*wp&mask]; wp++; op++) + n -= stride; + while (n > 0) { + REPEAT(stride, + wp[stride] += *wp; *op = ToLinearF[*wp&mask]; wp++; op++) + n -= stride; + } + } + } +} + +static void +horizontalAccumulate12(uint16 *wp, int n, int stride, int16 *op, + float *ToLinearF) +{ + register unsigned int cr, cg, cb, ca, mask; + register float t0, t1, t2, t3; + +#define SCALE12 2048.0F +#define CLAMP12(t) (((t) < 3071) ? (uint16) (t) : 3071) + + if (n >= stride) { + mask = CODE_MASK; + if (stride == 3) { + t0 = ToLinearF[cr = (wp[0] & mask)] * SCALE12; + t1 = ToLinearF[cg = (wp[1] & mask)] * SCALE12; + t2 = ToLinearF[cb = (wp[2] & mask)] * SCALE12; + op[0] = CLAMP12(t0); + op[1] = CLAMP12(t1); + op[2] = CLAMP12(t2); + n -= 3; + while (n > 0) { + wp += 3; + op += 3; + n -= 3; + t0 = ToLinearF[(cr += wp[0]) & mask] * SCALE12; + t1 = ToLinearF[(cg += wp[1]) & mask] * SCALE12; + t2 = ToLinearF[(cb += wp[2]) & mask] * SCALE12; + op[0] = CLAMP12(t0); + op[1] = CLAMP12(t1); + op[2] = CLAMP12(t2); + } + } else if (stride == 4) { + t0 = ToLinearF[cr = (wp[0] & mask)] * SCALE12; + t1 = ToLinearF[cg = (wp[1] & mask)] * SCALE12; + t2 = ToLinearF[cb = (wp[2] & mask)] * SCALE12; + t3 = ToLinearF[ca = (wp[3] & mask)] * SCALE12; + op[0] = CLAMP12(t0); + op[1] = CLAMP12(t1); + op[2] = CLAMP12(t2); + op[3] = CLAMP12(t3); + n -= 4; + while (n > 0) { + wp += 4; + op += 4; + n -= 4; + t0 = ToLinearF[(cr += wp[0]) & mask] * SCALE12; + t1 = ToLinearF[(cg += wp[1]) & mask] * SCALE12; + t2 = ToLinearF[(cb += wp[2]) & mask] * SCALE12; + t3 = ToLinearF[(ca += wp[3]) & mask] * SCALE12; + op[0] = CLAMP12(t0); + op[1] = CLAMP12(t1); + op[2] = CLAMP12(t2); + op[3] = CLAMP12(t3); + } + } else { + REPEAT(stride, t0 = ToLinearF[*wp&mask] * SCALE12; + *op = CLAMP12(t0); wp++; op++) + n -= stride; + while (n > 0) { + REPEAT(stride, + wp[stride] += *wp; t0 = ToLinearF[wp[stride]&mask]*SCALE12; + *op = CLAMP12(t0); wp++; op++) + n -= stride; + } + } + } +} + +static void +horizontalAccumulate16(uint16 *wp, int n, int stride, uint16 *op, + uint16 *ToLinear16) +{ + register unsigned int cr, cg, cb, ca, mask; + + if (n >= stride) { + mask = CODE_MASK; + if (stride == 3) { + op[0] = ToLinear16[cr = (wp[0] & mask)]; + op[1] = ToLinear16[cg = (wp[1] & mask)]; + op[2] = ToLinear16[cb = (wp[2] & mask)]; + n -= 3; + while (n > 0) { + wp += 3; + op += 3; + n -= 3; + op[0] = ToLinear16[(cr += wp[0]) & mask]; + op[1] = ToLinear16[(cg += wp[1]) & mask]; + op[2] = ToLinear16[(cb += wp[2]) & mask]; + } + } else if (stride == 4) { + op[0] = ToLinear16[cr = (wp[0] & mask)]; + op[1] = ToLinear16[cg = (wp[1] & mask)]; + op[2] = ToLinear16[cb = (wp[2] & mask)]; + op[3] = ToLinear16[ca = (wp[3] & mask)]; + n -= 4; + while (n > 0) { + wp += 4; + op += 4; + n -= 4; + op[0] = ToLinear16[(cr += wp[0]) & mask]; + op[1] = ToLinear16[(cg += wp[1]) & mask]; + op[2] = ToLinear16[(cb += wp[2]) & mask]; + op[3] = ToLinear16[(ca += wp[3]) & mask]; + } + } else { + REPEAT(stride, *op = ToLinear16[*wp&mask]; wp++; op++) + n -= stride; + while (n > 0) { + REPEAT(stride, + wp[stride] += *wp; *op = ToLinear16[*wp&mask]; wp++; op++) + n -= stride; + } + } + } +} + +/* + * Returns the log encoded 11-bit values with the horizontal + * differencing undone. + */ +static void +horizontalAccumulate11(uint16 *wp, int n, int stride, uint16 *op) +{ + register unsigned int cr, cg, cb, ca, mask; + + if (n >= stride) { + mask = CODE_MASK; + if (stride == 3) { + op[0] = cr = wp[0]; op[1] = cg = wp[1]; op[2] = cb = wp[2]; + n -= 3; + while (n > 0) { + wp += 3; + op += 3; + n -= 3; + op[0] = (cr += wp[0]) & mask; + op[1] = (cg += wp[1]) & mask; + op[2] = (cb += wp[2]) & mask; + } + } else if (stride == 4) { + op[0] = cr = wp[0]; op[1] = cg = wp[1]; + op[2] = cb = wp[2]; op[3] = ca = wp[3]; + n -= 4; + while (n > 0) { + wp += 4; + op += 4; + n -= 4; + op[0] = (cr += wp[0]) & mask; + op[1] = (cg += wp[1]) & mask; + op[2] = (cb += wp[2]) & mask; + op[3] = (ca += wp[3]) & mask; + } + } else { + REPEAT(stride, *op = *wp&mask; wp++; op++) + n -= stride; + while (n > 0) { + REPEAT(stride, + wp[stride] += *wp; *op = *wp&mask; wp++; op++) + n -= stride; + } + } + } +} + +static void +horizontalAccumulate8(uint16 *wp, int n, int stride, unsigned char *op, + unsigned char *ToLinear8) +{ + register unsigned int cr, cg, cb, ca, mask; + + if (n >= stride) { + mask = CODE_MASK; + if (stride == 3) { + op[0] = ToLinear8[cr = (wp[0] & mask)]; + op[1] = ToLinear8[cg = (wp[1] & mask)]; + op[2] = ToLinear8[cb = (wp[2] & mask)]; + n -= 3; + while (n > 0) { + n -= 3; + wp += 3; + op += 3; + op[0] = ToLinear8[(cr += wp[0]) & mask]; + op[1] = ToLinear8[(cg += wp[1]) & mask]; + op[2] = ToLinear8[(cb += wp[2]) & mask]; + } + } else if (stride == 4) { + op[0] = ToLinear8[cr = (wp[0] & mask)]; + op[1] = ToLinear8[cg = (wp[1] & mask)]; + op[2] = ToLinear8[cb = (wp[2] & mask)]; + op[3] = ToLinear8[ca = (wp[3] & mask)]; + n -= 4; + while (n > 0) { + n -= 4; + wp += 4; + op += 4; + op[0] = ToLinear8[(cr += wp[0]) & mask]; + op[1] = ToLinear8[(cg += wp[1]) & mask]; + op[2] = ToLinear8[(cb += wp[2]) & mask]; + op[3] = ToLinear8[(ca += wp[3]) & mask]; + } + } else { + REPEAT(stride, *op = ToLinear8[*wp&mask]; wp++; op++) + n -= stride; + while (n > 0) { + REPEAT(stride, + wp[stride] += *wp; *op = ToLinear8[*wp&mask]; wp++; op++) + n -= stride; + } + } + } +} + + +static void +horizontalAccumulate8abgr(uint16 *wp, int n, int stride, unsigned char *op, + unsigned char *ToLinear8) +{ + register unsigned int cr, cg, cb, ca, mask; + register unsigned char t0, t1, t2, t3; + + if (n >= stride) { + mask = CODE_MASK; + if (stride == 3) { + op[0] = 0; + t1 = ToLinear8[cb = (wp[2] & mask)]; + t2 = ToLinear8[cg = (wp[1] & mask)]; + t3 = ToLinear8[cr = (wp[0] & mask)]; + op[1] = t1; + op[2] = t2; + op[3] = t3; + n -= 3; + while (n > 0) { + n -= 3; + wp += 3; + op += 4; + op[0] = 0; + t1 = ToLinear8[(cb += wp[2]) & mask]; + t2 = ToLinear8[(cg += wp[1]) & mask]; + t3 = ToLinear8[(cr += wp[0]) & mask]; + op[1] = t1; + op[2] = t2; + op[3] = t3; + } + } else if (stride == 4) { + t0 = ToLinear8[ca = (wp[3] & mask)]; + t1 = ToLinear8[cb = (wp[2] & mask)]; + t2 = ToLinear8[cg = (wp[1] & mask)]; + t3 = ToLinear8[cr = (wp[0] & mask)]; + op[0] = t0; + op[1] = t1; + op[2] = t2; + op[3] = t3; + n -= 4; + while (n > 0) { + n -= 4; + wp += 4; + op += 4; + t0 = ToLinear8[(ca += wp[3]) & mask]; + t1 = ToLinear8[(cb += wp[2]) & mask]; + t2 = ToLinear8[(cg += wp[1]) & mask]; + t3 = ToLinear8[(cr += wp[0]) & mask]; + op[0] = t0; + op[1] = t1; + op[2] = t2; + op[3] = t3; + } + } else { + REPEAT(stride, *op = ToLinear8[*wp&mask]; wp++; op++) + n -= stride; + while (n > 0) { + REPEAT(stride, + wp[stride] += *wp; *op = ToLinear8[*wp&mask]; wp++; op++) + n -= stride; + } + } + } +} + +/* + * State block for each open TIFF + * file using PixarLog compression/decompression. + */ +typedef struct { + TIFFPredictorState predict; + z_stream stream; + uint16 *tbuf; + uint16 stride; + int state; + int user_datafmt; + int quality; +#define PLSTATE_INIT 1 + + TIFFVSetMethod vgetparent; /* super-class method */ + TIFFVSetMethod vsetparent; /* super-class method */ + + float *ToLinearF; + uint16 *ToLinear16; + unsigned char *ToLinear8; + uint16 *FromLT2; + uint16 *From14; /* Really for 16-bit data, but we shift down 2 */ + uint16 *From8; + +} PixarLogState; + +static int +PixarLogMakeTables(PixarLogState *sp) +{ + +/* + * We make several tables here to convert between various external + * representations (float, 16-bit, and 8-bit) and the internal + * 11-bit companded representation. The 11-bit representation has two + * distinct regions. A linear bottom end up through .018316 in steps + * of about .000073, and a region of constant ratio up to about 25. + * These floating point numbers are stored in the main table ToLinearF. + * All other tables are derived from this one. The tables (and the + * ratios) are continuous at the internal seam. + */ + + int nlin, lt2size; + int i, j; + double b, c, linstep, v; + float *ToLinearF; + uint16 *ToLinear16; + unsigned char *ToLinear8; + uint16 *FromLT2; + uint16 *From14; /* Really for 16-bit data, but we shift down 2 */ + uint16 *From8; + + c = log(RATIO); + nlin = (int)(1./c); /* nlin must be an integer */ + c = 1./nlin; + b = exp(-c*ONE); /* multiplicative scale factor [b*exp(c*ONE) = 1] */ + linstep = b*c*exp(1.); + + LogK1 = (float)(1./c); /* if (v >= 2) token = k1*log(v*k2) */ + LogK2 = (float)(1./b); + lt2size = (int)(2./linstep) + 1; + FromLT2 = (uint16 *)_TIFFmalloc(lt2size*sizeof(uint16)); + From14 = (uint16 *)_TIFFmalloc(16384*sizeof(uint16)); + From8 = (uint16 *)_TIFFmalloc(256*sizeof(uint16)); + ToLinearF = (float *)_TIFFmalloc(TSIZEP1 * sizeof(float)); + ToLinear16 = (uint16 *)_TIFFmalloc(TSIZEP1 * sizeof(uint16)); + ToLinear8 = (unsigned char *)_TIFFmalloc(TSIZEP1 * sizeof(unsigned char)); + if (FromLT2 == NULL || From14 == NULL || From8 == NULL || + ToLinearF == NULL || ToLinear16 == NULL || ToLinear8 == NULL) { + if (FromLT2) _TIFFfree(FromLT2); + if (From14) _TIFFfree(From14); + if (From8) _TIFFfree(From8); + if (ToLinearF) _TIFFfree(ToLinearF); + if (ToLinear16) _TIFFfree(ToLinear16); + if (ToLinear8) _TIFFfree(ToLinear8); + sp->FromLT2 = NULL; + sp->From14 = NULL; + sp->From8 = NULL; + sp->ToLinearF = NULL; + sp->ToLinear16 = NULL; + sp->ToLinear8 = NULL; + return 0; + } + + j = 0; + + for (i = 0; i < nlin; i++) { + v = i * linstep; + ToLinearF[j++] = (float)v; + } + + for (i = nlin; i < TSIZE; i++) + ToLinearF[j++] = (float)(b*exp(c*i)); + + ToLinearF[2048] = ToLinearF[2047]; + + for (i = 0; i < TSIZEP1; i++) { + v = ToLinearF[i]*65535.0 + 0.5; + ToLinear16[i] = (v > 65535.0) ? 65535 : (uint16)v; + v = ToLinearF[i]*255.0 + 0.5; + ToLinear8[i] = (v > 255.0) ? 255 : (unsigned char)v; + } + + j = 0; + for (i = 0; i < lt2size; i++) { + if ((i*linstep)*(i*linstep) > ToLinearF[j]*ToLinearF[j+1]) + j++; + FromLT2[i] = j; + } + + /* + * Since we lose info anyway on 16-bit data, we set up a 14-bit + * table and shift 16-bit values down two bits on input. + * saves a little table space. + */ + j = 0; + for (i = 0; i < 16384; i++) { + while ((i/16383.)*(i/16383.) > ToLinearF[j]*ToLinearF[j+1]) + j++; + From14[i] = j; + } + + j = 0; + for (i = 0; i < 256; i++) { + while ((i/255.)*(i/255.) > ToLinearF[j]*ToLinearF[j+1]) + j++; + From8[i] = j; + } + + Fltsize = (float)(lt2size/2); + + sp->ToLinearF = ToLinearF; + sp->ToLinear16 = ToLinear16; + sp->ToLinear8 = ToLinear8; + sp->FromLT2 = FromLT2; + sp->From14 = From14; + sp->From8 = From8; + + return 1; +} + +#define DecoderState(tif) ((PixarLogState*) (tif)->tif_data) +#define EncoderState(tif) ((PixarLogState*) (tif)->tif_data) + +static int PixarLogEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s); +static int PixarLogDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s); + +#define PIXARLOGDATAFMT_UNKNOWN -1 + +static int +PixarLogGuessDataFmt(TIFFDirectory *td) +{ + int guess = PIXARLOGDATAFMT_UNKNOWN; + int format = td->td_sampleformat; + + /* If the user didn't tell us his datafmt, + * take our best guess from the bitspersample. + */ + switch (td->td_bitspersample) { + case 32: + if (format == SAMPLEFORMAT_IEEEFP) + guess = PIXARLOGDATAFMT_FLOAT; + break; + case 16: + if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_UINT) + guess = PIXARLOGDATAFMT_16BIT; + break; + case 12: + if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_INT) + guess = PIXARLOGDATAFMT_12BITPICIO; + break; + case 11: + if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_UINT) + guess = PIXARLOGDATAFMT_11BITLOG; + break; + case 8: + if (format == SAMPLEFORMAT_VOID || format == SAMPLEFORMAT_UINT) + guess = PIXARLOGDATAFMT_8BIT; + break; + } + + return guess; +} + +static tmsize_t +multiply_ms(tmsize_t m1, tmsize_t m2) +{ + tmsize_t bytes = m1 * m2; + + if (m1 && bytes / m1 != m2) + bytes = 0; + + return bytes; +} + +static tmsize_t +add_ms(tmsize_t m1, tmsize_t m2) +{ + tmsize_t bytes = m1 + m2; + + /* if either input is zero, assume overflow already occurred */ + if (m1 == 0 || m2 == 0) + bytes = 0; + else if (bytes <= m1 || bytes <= m2) + bytes = 0; + + return bytes; +} + +static int +PixarLogFixupTags(TIFF* tif) +{ + (void) tif; + return (1); +} + +static int +PixarLogSetupDecode(TIFF* tif) +{ + static const char module[] = "PixarLogSetupDecode"; + TIFFDirectory *td = &tif->tif_dir; + PixarLogState* sp = DecoderState(tif); + tmsize_t tbuf_size; + + assert(sp != NULL); + + /* Make sure no byte swapping happens on the data + * after decompression. */ + tif->tif_postdecode = _TIFFNoPostDecode; + + /* for some reason, we can't do this in TIFFInitPixarLog */ + + sp->stride = (td->td_planarconfig == PLANARCONFIG_CONTIG ? + td->td_samplesperpixel : 1); + tbuf_size = multiply_ms(multiply_ms(multiply_ms(sp->stride, td->td_imagewidth), + td->td_rowsperstrip), sizeof(uint16)); + /* add one more stride in case input ends mid-stride */ + tbuf_size = add_ms(tbuf_size, sizeof(uint16) * sp->stride); + if (tbuf_size == 0) + return (0); /* TODO: this is an error return without error report through TIFFErrorExt */ + sp->tbuf = (uint16 *) _TIFFmalloc(tbuf_size); + if (sp->tbuf == NULL) + return (0); + if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN) + sp->user_datafmt = PixarLogGuessDataFmt(td); + if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN) { + TIFFErrorExt(tif->tif_clientdata, module, + "PixarLog compression can't handle bits depth/data format combination (depth: %d)", + td->td_bitspersample); + return (0); + } + + if (inflateInit(&sp->stream) != Z_OK) { + TIFFErrorExt(tif->tif_clientdata, module, "%s", sp->stream.msg); + return (0); + } else { + sp->state |= PLSTATE_INIT; + return (1); + } +} + +/* + * Setup state for decoding a strip. + */ +static int +PixarLogPreDecode(TIFF* tif, uint16 s) +{ + static const char module[] = "PixarLogPreDecode"; + PixarLogState* sp = DecoderState(tif); + + (void) s; + assert(sp != NULL); + sp->stream.next_in = tif->tif_rawdata; + assert(sizeof(sp->stream.avail_in)==4); /* if this assert gets raised, + we need to simplify this code to reflect a ZLib that is likely updated + to deal with 8byte memory sizes, though this code will respond + apropriately even before we simplify it */ + sp->stream.avail_in = (uInt) tif->tif_rawcc; + if ((tmsize_t)sp->stream.avail_in != tif->tif_rawcc) + { + TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size"); + return (0); + } + return (inflateReset(&sp->stream) == Z_OK); +} + +static int +PixarLogDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s) +{ + static const char module[] = "PixarLogDecode"; + TIFFDirectory *td = &tif->tif_dir; + PixarLogState* sp = DecoderState(tif); + tmsize_t i; + tmsize_t nsamples; + int llen; + uint16 *up; + + switch (sp->user_datafmt) { + case PIXARLOGDATAFMT_FLOAT: + nsamples = occ / sizeof(float); /* XXX float == 32 bits */ + break; + case PIXARLOGDATAFMT_16BIT: + case PIXARLOGDATAFMT_12BITPICIO: + case PIXARLOGDATAFMT_11BITLOG: + nsamples = occ / sizeof(uint16); /* XXX uint16 == 16 bits */ + break; + case PIXARLOGDATAFMT_8BIT: + case PIXARLOGDATAFMT_8BITABGR: + nsamples = occ; + break; + default: + TIFFErrorExt(tif->tif_clientdata, module, + "%d bit input not supported in PixarLog", + td->td_bitspersample); + return 0; + } + + llen = sp->stride * td->td_imagewidth; + + (void) s; + assert(sp != NULL); + sp->stream.next_out = (unsigned char *) sp->tbuf; + assert(sizeof(sp->stream.avail_out)==4); /* if this assert gets raised, + we need to simplify this code to reflect a ZLib that is likely updated + to deal with 8byte memory sizes, though this code will respond + apropriately even before we simplify it */ + sp->stream.avail_out = (uInt) (nsamples * sizeof(uint16)); + if (sp->stream.avail_out != nsamples * sizeof(uint16)) + { + TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size"); + return (0); + } + do { + int state = inflate(&sp->stream, Z_PARTIAL_FLUSH); + if (state == Z_STREAM_END) { + break; /* XXX */ + } + if (state == Z_DATA_ERROR) { + TIFFErrorExt(tif->tif_clientdata, module, + "Decoding error at scanline %lu, %s", + (unsigned long) tif->tif_row, sp->stream.msg); + if (inflateSync(&sp->stream) != Z_OK) + return (0); + continue; + } + if (state != Z_OK) { + TIFFErrorExt(tif->tif_clientdata, module, "ZLib error: %s", + sp->stream.msg); + return (0); + } + } while (sp->stream.avail_out > 0); + + /* hopefully, we got all the bytes we needed */ + if (sp->stream.avail_out != 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at scanline %lu (short " TIFF_UINT64_FORMAT " bytes)", + (unsigned long) tif->tif_row, (TIFF_UINT64_T) sp->stream.avail_out); + return (0); + } + + up = sp->tbuf; + /* Swap bytes in the data if from a different endian machine. */ + if (tif->tif_flags & TIFF_SWAB) + TIFFSwabArrayOfShort(up, nsamples); + + /* + * if llen is not an exact multiple of nsamples, the decode operation + * may overflow the output buffer, so truncate it enough to prevent + * that but still salvage as much data as possible. + */ + if (nsamples % llen) { + TIFFWarningExt(tif->tif_clientdata, module, + "stride %lu is not a multiple of sample count, " + "%lu, data truncated.", (unsigned long) llen, (unsigned long) nsamples); + nsamples -= nsamples % llen; + } + + for (i = 0; i < nsamples; i += llen, up += llen) { + switch (sp->user_datafmt) { + case PIXARLOGDATAFMT_FLOAT: + horizontalAccumulateF(up, llen, sp->stride, + (float *)op, sp->ToLinearF); + op += llen * sizeof(float); + break; + case PIXARLOGDATAFMT_16BIT: + horizontalAccumulate16(up, llen, sp->stride, + (uint16 *)op, sp->ToLinear16); + op += llen * sizeof(uint16); + break; + case PIXARLOGDATAFMT_12BITPICIO: + horizontalAccumulate12(up, llen, sp->stride, + (int16 *)op, sp->ToLinearF); + op += llen * sizeof(int16); + break; + case PIXARLOGDATAFMT_11BITLOG: + horizontalAccumulate11(up, llen, sp->stride, + (uint16 *)op); + op += llen * sizeof(uint16); + break; + case PIXARLOGDATAFMT_8BIT: + horizontalAccumulate8(up, llen, sp->stride, + (unsigned char *)op, sp->ToLinear8); + op += llen * sizeof(unsigned char); + break; + case PIXARLOGDATAFMT_8BITABGR: + horizontalAccumulate8abgr(up, llen, sp->stride, + (unsigned char *)op, sp->ToLinear8); + op += llen * sizeof(unsigned char); + break; + default: + TIFFErrorExt(tif->tif_clientdata, module, + "Unsupported bits/sample: %d", + td->td_bitspersample); + return (0); + } + } + + return (1); +} + +static int +PixarLogSetupEncode(TIFF* tif) +{ + static const char module[] = "PixarLogSetupEncode"; + TIFFDirectory *td = &tif->tif_dir; + PixarLogState* sp = EncoderState(tif); + tmsize_t tbuf_size; + + assert(sp != NULL); + + /* for some reason, we can't do this in TIFFInitPixarLog */ + + sp->stride = (td->td_planarconfig == PLANARCONFIG_CONTIG ? + td->td_samplesperpixel : 1); + tbuf_size = multiply_ms(multiply_ms(multiply_ms(sp->stride, td->td_imagewidth), + td->td_rowsperstrip), sizeof(uint16)); + if (tbuf_size == 0) + return (0); /* TODO: this is an error return without error report through TIFFErrorExt */ + sp->tbuf = (uint16 *) _TIFFmalloc(tbuf_size); + if (sp->tbuf == NULL) + return (0); + if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN) + sp->user_datafmt = PixarLogGuessDataFmt(td); + if (sp->user_datafmt == PIXARLOGDATAFMT_UNKNOWN) { + TIFFErrorExt(tif->tif_clientdata, module, "PixarLog compression can't handle %d bit linear encodings", td->td_bitspersample); + return (0); + } + + if (deflateInit(&sp->stream, sp->quality) != Z_OK) { + TIFFErrorExt(tif->tif_clientdata, module, "%s", sp->stream.msg); + return (0); + } else { + sp->state |= PLSTATE_INIT; + return (1); + } +} + +/* + * Reset encoding state at the start of a strip. + */ +static int +PixarLogPreEncode(TIFF* tif, uint16 s) +{ + static const char module[] = "PixarLogPreEncode"; + PixarLogState *sp = EncoderState(tif); + + (void) s; + assert(sp != NULL); + sp->stream.next_out = tif->tif_rawdata; + assert(sizeof(sp->stream.avail_out)==4); /* if this assert gets raised, + we need to simplify this code to reflect a ZLib that is likely updated + to deal with 8byte memory sizes, though this code will respond + apropriately even before we simplify it */ + sp->stream.avail_out = tif->tif_rawdatasize; + if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize) + { + TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size"); + return (0); + } + return (deflateReset(&sp->stream) == Z_OK); +} + +static void +horizontalDifferenceF(float *ip, int n, int stride, uint16 *wp, uint16 *FromLT2) +{ + int32 r1, g1, b1, a1, r2, g2, b2, a2, mask; + float fltsize = Fltsize; + +#define CLAMP(v) ( (v<(float)0.) ? 0 \ + : (v<(float)2.) ? FromLT2[(int)(v*fltsize)] \ + : (v>(float)24.2) ? 2047 \ + : LogK1*log(v*LogK2) + 0.5 ) + + mask = CODE_MASK; + if (n >= stride) { + if (stride == 3) { + r2 = wp[0] = (uint16) CLAMP(ip[0]); + g2 = wp[1] = (uint16) CLAMP(ip[1]); + b2 = wp[2] = (uint16) CLAMP(ip[2]); + n -= 3; + while (n > 0) { + n -= 3; + wp += 3; + ip += 3; + r1 = (int32) CLAMP(ip[0]); wp[0] = (r1-r2) & mask; r2 = r1; + g1 = (int32) CLAMP(ip[1]); wp[1] = (g1-g2) & mask; g2 = g1; + b1 = (int32) CLAMP(ip[2]); wp[2] = (b1-b2) & mask; b2 = b1; + } + } else if (stride == 4) { + r2 = wp[0] = (uint16) CLAMP(ip[0]); + g2 = wp[1] = (uint16) CLAMP(ip[1]); + b2 = wp[2] = (uint16) CLAMP(ip[2]); + a2 = wp[3] = (uint16) CLAMP(ip[3]); + n -= 4; + while (n > 0) { + n -= 4; + wp += 4; + ip += 4; + r1 = (int32) CLAMP(ip[0]); wp[0] = (r1-r2) & mask; r2 = r1; + g1 = (int32) CLAMP(ip[1]); wp[1] = (g1-g2) & mask; g2 = g1; + b1 = (int32) CLAMP(ip[2]); wp[2] = (b1-b2) & mask; b2 = b1; + a1 = (int32) CLAMP(ip[3]); wp[3] = (a1-a2) & mask; a2 = a1; + } + } else { + ip += n - 1; /* point to last one */ + wp += n - 1; /* point to last one */ + n -= stride; + while (n > 0) { + REPEAT(stride, wp[0] = (uint16) CLAMP(ip[0]); + wp[stride] -= wp[0]; + wp[stride] &= mask; + wp--; ip--) + n -= stride; + } + REPEAT(stride, wp[0] = (uint16) CLAMP(ip[0]); wp--; ip--) + } + } +} + +static void +horizontalDifference16(unsigned short *ip, int n, int stride, + unsigned short *wp, uint16 *From14) +{ + register int r1, g1, b1, a1, r2, g2, b2, a2, mask; + +/* assumption is unsigned pixel values */ +#undef CLAMP +#define CLAMP(v) From14[(v) >> 2] + + mask = CODE_MASK; + if (n >= stride) { + if (stride == 3) { + r2 = wp[0] = CLAMP(ip[0]); g2 = wp[1] = CLAMP(ip[1]); + b2 = wp[2] = CLAMP(ip[2]); + n -= 3; + while (n > 0) { + n -= 3; + wp += 3; + ip += 3; + r1 = CLAMP(ip[0]); wp[0] = (r1-r2) & mask; r2 = r1; + g1 = CLAMP(ip[1]); wp[1] = (g1-g2) & mask; g2 = g1; + b1 = CLAMP(ip[2]); wp[2] = (b1-b2) & mask; b2 = b1; + } + } else if (stride == 4) { + r2 = wp[0] = CLAMP(ip[0]); g2 = wp[1] = CLAMP(ip[1]); + b2 = wp[2] = CLAMP(ip[2]); a2 = wp[3] = CLAMP(ip[3]); + n -= 4; + while (n > 0) { + n -= 4; + wp += 4; + ip += 4; + r1 = CLAMP(ip[0]); wp[0] = (r1-r2) & mask; r2 = r1; + g1 = CLAMP(ip[1]); wp[1] = (g1-g2) & mask; g2 = g1; + b1 = CLAMP(ip[2]); wp[2] = (b1-b2) & mask; b2 = b1; + a1 = CLAMP(ip[3]); wp[3] = (a1-a2) & mask; a2 = a1; + } + } else { + ip += n - 1; /* point to last one */ + wp += n - 1; /* point to last one */ + n -= stride; + while (n > 0) { + REPEAT(stride, wp[0] = CLAMP(ip[0]); + wp[stride] -= wp[0]; + wp[stride] &= mask; + wp--; ip--) + n -= stride; + } + REPEAT(stride, wp[0] = CLAMP(ip[0]); wp--; ip--) + } + } +} + + +static void +horizontalDifference8(unsigned char *ip, int n, int stride, + unsigned short *wp, uint16 *From8) +{ + register int r1, g1, b1, a1, r2, g2, b2, a2, mask; + +#undef CLAMP +#define CLAMP(v) (From8[(v)]) + + mask = CODE_MASK; + if (n >= stride) { + if (stride == 3) { + r2 = wp[0] = CLAMP(ip[0]); g2 = wp[1] = CLAMP(ip[1]); + b2 = wp[2] = CLAMP(ip[2]); + n -= 3; + while (n > 0) { + n -= 3; + r1 = CLAMP(ip[3]); wp[3] = (r1-r2) & mask; r2 = r1; + g1 = CLAMP(ip[4]); wp[4] = (g1-g2) & mask; g2 = g1; + b1 = CLAMP(ip[5]); wp[5] = (b1-b2) & mask; b2 = b1; + wp += 3; + ip += 3; + } + } else if (stride == 4) { + r2 = wp[0] = CLAMP(ip[0]); g2 = wp[1] = CLAMP(ip[1]); + b2 = wp[2] = CLAMP(ip[2]); a2 = wp[3] = CLAMP(ip[3]); + n -= 4; + while (n > 0) { + n -= 4; + r1 = CLAMP(ip[4]); wp[4] = (r1-r2) & mask; r2 = r1; + g1 = CLAMP(ip[5]); wp[5] = (g1-g2) & mask; g2 = g1; + b1 = CLAMP(ip[6]); wp[6] = (b1-b2) & mask; b2 = b1; + a1 = CLAMP(ip[7]); wp[7] = (a1-a2) & mask; a2 = a1; + wp += 4; + ip += 4; + } + } else { + wp += n + stride - 1; /* point to last one */ + ip += n + stride - 1; /* point to last one */ + n -= stride; + while (n > 0) { + REPEAT(stride, wp[0] = CLAMP(ip[0]); + wp[stride] -= wp[0]; + wp[stride] &= mask; + wp--; ip--) + n -= stride; + } + REPEAT(stride, wp[0] = CLAMP(ip[0]); wp--; ip--) + } + } +} + +/* + * Encode a chunk of pixels. + */ +static int +PixarLogEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + static const char module[] = "PixarLogEncode"; + TIFFDirectory *td = &tif->tif_dir; + PixarLogState *sp = EncoderState(tif); + tmsize_t i; + tmsize_t n; + int llen; + unsigned short * up; + + (void) s; + + switch (sp->user_datafmt) { + case PIXARLOGDATAFMT_FLOAT: + n = cc / sizeof(float); /* XXX float == 32 bits */ + break; + case PIXARLOGDATAFMT_16BIT: + case PIXARLOGDATAFMT_12BITPICIO: + case PIXARLOGDATAFMT_11BITLOG: + n = cc / sizeof(uint16); /* XXX uint16 == 16 bits */ + break; + case PIXARLOGDATAFMT_8BIT: + case PIXARLOGDATAFMT_8BITABGR: + n = cc; + break; + default: + TIFFErrorExt(tif->tif_clientdata, module, + "%d bit input not supported in PixarLog", + td->td_bitspersample); + return 0; + } + + llen = sp->stride * td->td_imagewidth; + + for (i = 0, up = sp->tbuf; i < n; i += llen, up += llen) { + switch (sp->user_datafmt) { + case PIXARLOGDATAFMT_FLOAT: + horizontalDifferenceF((float *)bp, llen, + sp->stride, up, sp->FromLT2); + bp += llen * sizeof(float); + break; + case PIXARLOGDATAFMT_16BIT: + horizontalDifference16((uint16 *)bp, llen, + sp->stride, up, sp->From14); + bp += llen * sizeof(uint16); + break; + case PIXARLOGDATAFMT_8BIT: + horizontalDifference8((unsigned char *)bp, llen, + sp->stride, up, sp->From8); + bp += llen * sizeof(unsigned char); + break; + default: + TIFFErrorExt(tif->tif_clientdata, module, + "%d bit input not supported in PixarLog", + td->td_bitspersample); + return 0; + } + } + + sp->stream.next_in = (unsigned char *) sp->tbuf; + assert(sizeof(sp->stream.avail_in)==4); /* if this assert gets raised, + we need to simplify this code to reflect a ZLib that is likely updated + to deal with 8byte memory sizes, though this code will respond + apropriately even before we simplify it */ + sp->stream.avail_in = (uInt) (n * sizeof(uint16)); + if ((sp->stream.avail_in / sizeof(uint16)) != (uInt) n) + { + TIFFErrorExt(tif->tif_clientdata, module, + "ZLib cannot deal with buffers this size"); + return (0); + } + + do { + if (deflate(&sp->stream, Z_NO_FLUSH) != Z_OK) { + TIFFErrorExt(tif->tif_clientdata, module, "Encoder error: %s", + sp->stream.msg); + return (0); + } + if (sp->stream.avail_out == 0) { + tif->tif_rawcc = tif->tif_rawdatasize; + TIFFFlushData1(tif); + sp->stream.next_out = tif->tif_rawdata; + sp->stream.avail_out = (uInt) tif->tif_rawdatasize; /* this is a safe typecast, as check is made already in PixarLogPreEncode */ + } + } while (sp->stream.avail_in > 0); + return (1); +} + +/* + * Finish off an encoded strip by flushing the last + * string and tacking on an End Of Information code. + */ + +static int +PixarLogPostEncode(TIFF* tif) +{ + static const char module[] = "PixarLogPostEncode"; + PixarLogState *sp = EncoderState(tif); + int state; + + sp->stream.avail_in = 0; + + do { + state = deflate(&sp->stream, Z_FINISH); + switch (state) { + case Z_STREAM_END: + case Z_OK: + if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize) { + tif->tif_rawcc = + tif->tif_rawdatasize - sp->stream.avail_out; + TIFFFlushData1(tif); + sp->stream.next_out = tif->tif_rawdata; + sp->stream.avail_out = (uInt) tif->tif_rawdatasize; /* this is a safe typecast, as check is made already in PixarLogPreEncode */ + } + break; + default: + TIFFErrorExt(tif->tif_clientdata, module, "ZLib error: %s", + sp->stream.msg); + return (0); + } + } while (state != Z_STREAM_END); + return (1); +} + +static void +PixarLogClose(TIFF* tif) +{ + TIFFDirectory *td = &tif->tif_dir; + + /* In a really sneaky (and really incorrect, and untruthfull, and + * troublesome, and error-prone) maneuver that completely goes against + * the spirit of TIFF, and breaks TIFF, on close, we covertly + * modify both bitspersample and sampleformat in the directory to + * indicate 8-bit linear. This way, the decode "just works" even for + * readers that don't know about PixarLog, or how to set + * the PIXARLOGDATFMT pseudo-tag. + */ + td->td_bitspersample = 8; + td->td_sampleformat = SAMPLEFORMAT_UINT; +} + +static void +PixarLogCleanup(TIFF* tif) +{ + PixarLogState* sp = (PixarLogState*) tif->tif_data; + + assert(sp != 0); + + (void)TIFFPredictorCleanup(tif); + + tif->tif_tagmethods.vgetfield = sp->vgetparent; + tif->tif_tagmethods.vsetfield = sp->vsetparent; + + if (sp->FromLT2) _TIFFfree(sp->FromLT2); + if (sp->From14) _TIFFfree(sp->From14); + if (sp->From8) _TIFFfree(sp->From8); + if (sp->ToLinearF) _TIFFfree(sp->ToLinearF); + if (sp->ToLinear16) _TIFFfree(sp->ToLinear16); + if (sp->ToLinear8) _TIFFfree(sp->ToLinear8); + if (sp->state&PLSTATE_INIT) { + if (tif->tif_mode == O_RDONLY) + inflateEnd(&sp->stream); + else + deflateEnd(&sp->stream); + } + if (sp->tbuf) + _TIFFfree(sp->tbuf); + _TIFFfree(sp); + tif->tif_data = NULL; + + _TIFFSetDefaultCompressionState(tif); +} + +static int +PixarLogVSetField(TIFF* tif, uint32 tag, va_list ap) +{ + static const char module[] = "PixarLogVSetField"; + PixarLogState *sp = (PixarLogState *)tif->tif_data; + int result; + + switch (tag) { + case TIFFTAG_PIXARLOGQUALITY: + sp->quality = (int) va_arg(ap, int); + if (tif->tif_mode != O_RDONLY && (sp->state&PLSTATE_INIT)) { + if (deflateParams(&sp->stream, + sp->quality, Z_DEFAULT_STRATEGY) != Z_OK) { + TIFFErrorExt(tif->tif_clientdata, module, "ZLib error: %s", + sp->stream.msg); + return (0); + } + } + return (1); + case TIFFTAG_PIXARLOGDATAFMT: + sp->user_datafmt = (int) va_arg(ap, int); + /* Tweak the TIFF header so that the rest of libtiff knows what + * size of data will be passed between app and library, and + * assume that the app knows what it is doing and is not + * confused by these header manipulations... + */ + switch (sp->user_datafmt) { + case PIXARLOGDATAFMT_8BIT: + case PIXARLOGDATAFMT_8BITABGR: + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + break; + case PIXARLOGDATAFMT_11BITLOG: + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + break; + case PIXARLOGDATAFMT_12BITPICIO: + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT); + break; + case PIXARLOGDATAFMT_16BIT: + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 16); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + break; + case PIXARLOGDATAFMT_FLOAT: + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP); + break; + } + /* + * Must recalculate sizes should bits/sample change. + */ + tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tmsize_t)(-1); + tif->tif_scanlinesize = TIFFScanlineSize(tif); + result = 1; /* NB: pseudo tag */ + break; + default: + result = (*sp->vsetparent)(tif, tag, ap); + } + return (result); +} + +static int +PixarLogVGetField(TIFF* tif, uint32 tag, va_list ap) +{ + PixarLogState *sp = (PixarLogState *)tif->tif_data; + + switch (tag) { + case TIFFTAG_PIXARLOGQUALITY: + *va_arg(ap, int*) = sp->quality; + break; + case TIFFTAG_PIXARLOGDATAFMT: + *va_arg(ap, int*) = sp->user_datafmt; + break; + default: + return (*sp->vgetparent)(tif, tag, ap); + } + return (1); +} + +static const TIFFField pixarlogFields[] = { + {TIFFTAG_PIXARLOGDATAFMT, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "", NULL}, + {TIFFTAG_PIXARLOGQUALITY, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "", NULL} +}; + +int +TIFFInitPixarLog(TIFF* tif, int scheme) +{ + static const char module[] = "TIFFInitPixarLog"; + + PixarLogState* sp; + + assert(scheme == COMPRESSION_PIXARLOG); + + /* + * Merge codec-specific tag information. + */ + if (!_TIFFMergeFields(tif, pixarlogFields, + TIFFArrayCount(pixarlogFields))) { + TIFFErrorExt(tif->tif_clientdata, module, + "Merging PixarLog codec-specific tags failed"); + return 0; + } + + /* + * Allocate state block so tag methods have storage to record values. + */ + tif->tif_data = (uint8*) _TIFFmalloc(sizeof (PixarLogState)); + if (tif->tif_data == NULL) + goto bad; + sp = (PixarLogState*) tif->tif_data; + _TIFFmemset(sp, 0, sizeof (*sp)); + sp->stream.data_type = Z_BINARY; + sp->user_datafmt = PIXARLOGDATAFMT_UNKNOWN; + + /* + * Install codec methods. + */ + tif->tif_fixuptags = PixarLogFixupTags; + tif->tif_setupdecode = PixarLogSetupDecode; + tif->tif_predecode = PixarLogPreDecode; + tif->tif_decoderow = PixarLogDecode; + tif->tif_decodestrip = PixarLogDecode; + tif->tif_decodetile = PixarLogDecode; + tif->tif_setupencode = PixarLogSetupEncode; + tif->tif_preencode = PixarLogPreEncode; + tif->tif_postencode = PixarLogPostEncode; + tif->tif_encoderow = PixarLogEncode; + tif->tif_encodestrip = PixarLogEncode; + tif->tif_encodetile = PixarLogEncode; + tif->tif_close = PixarLogClose; + tif->tif_cleanup = PixarLogCleanup; + + /* Override SetField so we can handle our private pseudo-tag */ + sp->vgetparent = tif->tif_tagmethods.vgetfield; + tif->tif_tagmethods.vgetfield = PixarLogVGetField; /* hook for codec tags */ + sp->vsetparent = tif->tif_tagmethods.vsetfield; + tif->tif_tagmethods.vsetfield = PixarLogVSetField; /* hook for codec tags */ + + /* Default values for codec-specific fields */ + sp->quality = Z_DEFAULT_COMPRESSION; /* default comp. level */ + sp->state = 0; + + /* we don't wish to use the predictor, + * the default is none, which predictor value 1 + */ + (void) TIFFPredictorInit(tif); + + /* + * build the companding tables + */ + PixarLogMakeTables(sp); + + return (1); +bad: + TIFFErrorExt(tif->tif_clientdata, module, + "No space for PixarLog state block"); + return (0); +} +#endif /* PIXARLOG_SUPPORT */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_predict.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_predict.c new file mode 100644 index 000000000..f93c6645f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_predict.c @@ -0,0 +1,764 @@ +/* $Id: tif_predict.c,v 1.32 2010-03-10 18:56:49 bfriesen Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Predictor Tag Support (used by multiple codecs). + */ +#include "tiffiop.h" +#include "tif_predict.h" + +#define PredictorState(tif) ((TIFFPredictorState*) (tif)->tif_data) + +static void horAcc8(TIFF* tif, uint8* cp0, tmsize_t cc); +static void horAcc16(TIFF* tif, uint8* cp0, tmsize_t cc); +static void horAcc32(TIFF* tif, uint8* cp0, tmsize_t cc); +static void swabHorAcc16(TIFF* tif, uint8* cp0, tmsize_t cc); +static void swabHorAcc32(TIFF* tif, uint8* cp0, tmsize_t cc); +static void horDiff8(TIFF* tif, uint8* cp0, tmsize_t cc); +static void horDiff16(TIFF* tif, uint8* cp0, tmsize_t cc); +static void horDiff32(TIFF* tif, uint8* cp0, tmsize_t cc); +static void fpAcc(TIFF* tif, uint8* cp0, tmsize_t cc); +static void fpDiff(TIFF* tif, uint8* cp0, tmsize_t cc); +static int PredictorDecodeRow(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s); +static int PredictorDecodeTile(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s); +static int PredictorEncodeRow(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s); +static int PredictorEncodeTile(TIFF* tif, uint8* bp0, tmsize_t cc0, uint16 s); + +static int +PredictorSetup(TIFF* tif) +{ + static const char module[] = "PredictorSetup"; + + TIFFPredictorState* sp = PredictorState(tif); + TIFFDirectory* td = &tif->tif_dir; + + switch (sp->predictor) /* no differencing */ + { + case PREDICTOR_NONE: + return 1; + case PREDICTOR_HORIZONTAL: + if (td->td_bitspersample != 8 + && td->td_bitspersample != 16 + && td->td_bitspersample != 32) { + TIFFErrorExt(tif->tif_clientdata, module, + "Horizontal differencing \"Predictor\" not supported with %d-bit samples", + td->td_bitspersample); + return 0; + } + break; + case PREDICTOR_FLOATINGPOINT: + if (td->td_sampleformat != SAMPLEFORMAT_IEEEFP) { + TIFFErrorExt(tif->tif_clientdata, module, + "Floating point \"Predictor\" not supported with %d data format", + td->td_sampleformat); + return 0; + } + break; + default: + TIFFErrorExt(tif->tif_clientdata, module, + "\"Predictor\" value %d not supported", + sp->predictor); + return 0; + } + sp->stride = (td->td_planarconfig == PLANARCONFIG_CONTIG ? + td->td_samplesperpixel : 1); + /* + * Calculate the scanline/tile-width size in bytes. + */ + if (isTiled(tif)) + sp->rowsize = TIFFTileRowSize(tif); + else + sp->rowsize = TIFFScanlineSize(tif); + if (sp->rowsize == 0) + return 0; + + return 1; +} + +static int +PredictorSetupDecode(TIFF* tif) +{ + TIFFPredictorState* sp = PredictorState(tif); + TIFFDirectory* td = &tif->tif_dir; + + if (!(*sp->setupdecode)(tif) || !PredictorSetup(tif)) + return 0; + + if (sp->predictor == 2) { + switch (td->td_bitspersample) { + case 8: sp->decodepfunc = horAcc8; break; + case 16: sp->decodepfunc = horAcc16; break; + case 32: sp->decodepfunc = horAcc32; break; + } + /* + * Override default decoding method with one that does the + * predictor stuff. + */ + if( tif->tif_decoderow != PredictorDecodeRow ) + { + sp->decoderow = tif->tif_decoderow; + tif->tif_decoderow = PredictorDecodeRow; + sp->decodestrip = tif->tif_decodestrip; + tif->tif_decodestrip = PredictorDecodeTile; + sp->decodetile = tif->tif_decodetile; + tif->tif_decodetile = PredictorDecodeTile; + } + + /* + * If the data is horizontally differenced 16-bit data that + * requires byte-swapping, then it must be byte swapped before + * the accumulation step. We do this with a special-purpose + * routine and override the normal post decoding logic that + * the library setup when the directory was read. + */ + if (tif->tif_flags & TIFF_SWAB) { + if (sp->decodepfunc == horAcc16) { + sp->decodepfunc = swabHorAcc16; + tif->tif_postdecode = _TIFFNoPostDecode; + } else if (sp->decodepfunc == horAcc32) { + sp->decodepfunc = swabHorAcc32; + tif->tif_postdecode = _TIFFNoPostDecode; + } + } + } + + else if (sp->predictor == 3) { + sp->decodepfunc = fpAcc; + /* + * Override default decoding method with one that does the + * predictor stuff. + */ + if( tif->tif_decoderow != PredictorDecodeRow ) + { + sp->decoderow = tif->tif_decoderow; + tif->tif_decoderow = PredictorDecodeRow; + sp->decodestrip = tif->tif_decodestrip; + tif->tif_decodestrip = PredictorDecodeTile; + sp->decodetile = tif->tif_decodetile; + tif->tif_decodetile = PredictorDecodeTile; + } + /* + * The data should not be swapped outside of the floating + * point predictor, the accumulation routine should return + * byres in the native order. + */ + if (tif->tif_flags & TIFF_SWAB) { + tif->tif_postdecode = _TIFFNoPostDecode; + } + /* + * Allocate buffer to keep the decoded bytes before + * rearranging in the ight order + */ + } + + return 1; +} + +static int +PredictorSetupEncode(TIFF* tif) +{ + TIFFPredictorState* sp = PredictorState(tif); + TIFFDirectory* td = &tif->tif_dir; + + if (!(*sp->setupencode)(tif) || !PredictorSetup(tif)) + return 0; + + if (sp->predictor == 2) { + switch (td->td_bitspersample) { + case 8: sp->encodepfunc = horDiff8; break; + case 16: sp->encodepfunc = horDiff16; break; + case 32: sp->encodepfunc = horDiff32; break; + } + /* + * Override default encoding method with one that does the + * predictor stuff. + */ + if( tif->tif_encoderow != PredictorEncodeRow ) + { + sp->encoderow = tif->tif_encoderow; + tif->tif_encoderow = PredictorEncodeRow; + sp->encodestrip = tif->tif_encodestrip; + tif->tif_encodestrip = PredictorEncodeTile; + sp->encodetile = tif->tif_encodetile; + tif->tif_encodetile = PredictorEncodeTile; + } + } + + else if (sp->predictor == 3) { + sp->encodepfunc = fpDiff; + /* + * Override default encoding method with one that does the + * predictor stuff. + */ + if( tif->tif_encoderow != PredictorEncodeRow ) + { + sp->encoderow = tif->tif_encoderow; + tif->tif_encoderow = PredictorEncodeRow; + sp->encodestrip = tif->tif_encodestrip; + tif->tif_encodestrip = PredictorEncodeTile; + sp->encodetile = tif->tif_encodetile; + tif->tif_encodetile = PredictorEncodeTile; + } + } + + return 1; +} + +#define REPEAT4(n, op) \ + switch (n) { \ + default: { tmsize_t i; for (i = n-4; i > 0; i--) { op; } } \ + case 4: op; \ + case 3: op; \ + case 2: op; \ + case 1: op; \ + case 0: ; \ + } + +static void +horAcc8(TIFF* tif, uint8* cp0, tmsize_t cc) +{ + tmsize_t stride = PredictorState(tif)->stride; + + char* cp = (char*) cp0; + assert((cc%stride)==0); + if (cc > stride) { + /* + * Pipeline the most common cases. + */ + if (stride == 3) { + unsigned int cr = cp[0]; + unsigned int cg = cp[1]; + unsigned int cb = cp[2]; + cc -= 3; + cp += 3; + while (cc>0) { + cp[0] = (char) (cr += cp[0]); + cp[1] = (char) (cg += cp[1]); + cp[2] = (char) (cb += cp[2]); + cc -= 3; + cp += 3; + } + } else if (stride == 4) { + unsigned int cr = cp[0]; + unsigned int cg = cp[1]; + unsigned int cb = cp[2]; + unsigned int ca = cp[3]; + cc -= 4; + cp += 4; + while (cc>0) { + cp[0] = (char) (cr += cp[0]); + cp[1] = (char) (cg += cp[1]); + cp[2] = (char) (cb += cp[2]); + cp[3] = (char) (ca += cp[3]); + cc -= 4; + cp += 4; + } + } else { + cc -= stride; + do { + REPEAT4(stride, cp[stride] = + (char) (cp[stride] + *cp); cp++) + cc -= stride; + } while (cc>0); + } + } +} + +static void +swabHorAcc16(TIFF* tif, uint8* cp0, tmsize_t cc) +{ + tmsize_t stride = PredictorState(tif)->stride; + uint16* wp = (uint16*) cp0; + tmsize_t wc = cc / 2; + + assert((cc%(2*stride))==0); + + if (wc > stride) { + TIFFSwabArrayOfShort(wp, wc); + wc -= stride; + do { + REPEAT4(stride, wp[stride] += wp[0]; wp++) + wc -= stride; + } while (wc > 0); + } +} + +static void +horAcc16(TIFF* tif, uint8* cp0, tmsize_t cc) +{ + tmsize_t stride = PredictorState(tif)->stride; + uint16* wp = (uint16*) cp0; + tmsize_t wc = cc / 2; + + assert((cc%(2*stride))==0); + + if (wc > stride) { + wc -= stride; + do { + REPEAT4(stride, wp[stride] += wp[0]; wp++) + wc -= stride; + } while (wc > 0); + } +} + +static void +swabHorAcc32(TIFF* tif, uint8* cp0, tmsize_t cc) +{ + tmsize_t stride = PredictorState(tif)->stride; + uint32* wp = (uint32*) cp0; + tmsize_t wc = cc / 4; + + assert((cc%(4*stride))==0); + + if (wc > stride) { + TIFFSwabArrayOfLong(wp, wc); + wc -= stride; + do { + REPEAT4(stride, wp[stride] += wp[0]; wp++) + wc -= stride; + } while (wc > 0); + } +} + +static void +horAcc32(TIFF* tif, uint8* cp0, tmsize_t cc) +{ + tmsize_t stride = PredictorState(tif)->stride; + uint32* wp = (uint32*) cp0; + tmsize_t wc = cc / 4; + + assert((cc%(4*stride))==0); + + if (wc > stride) { + wc -= stride; + do { + REPEAT4(stride, wp[stride] += wp[0]; wp++) + wc -= stride; + } while (wc > 0); + } +} + +/* + * Floating point predictor accumulation routine. + */ +static void +fpAcc(TIFF* tif, uint8* cp0, tmsize_t cc) +{ + tmsize_t stride = PredictorState(tif)->stride; + uint32 bps = tif->tif_dir.td_bitspersample / 8; + tmsize_t wc = cc / bps; + tmsize_t count = cc; + uint8 *cp = (uint8 *) cp0; + uint8 *tmp = (uint8 *)_TIFFmalloc(cc); + + assert((cc%(bps*stride))==0); + + if (!tmp) + return; + + while (count > stride) { + REPEAT4(stride, cp[stride] += cp[0]; cp++) + count -= stride; + } + + _TIFFmemcpy(tmp, cp0, cc); + cp = (uint8 *) cp0; + for (count = 0; count < wc; count++) { + uint32 byte; + for (byte = 0; byte < bps; byte++) { + #if WORDS_BIGENDIAN + cp[bps * count + byte] = tmp[byte * wc + count]; + #else + cp[bps * count + byte] = + tmp[(bps - byte - 1) * wc + count]; + #endif + } + } + _TIFFfree(tmp); +} + +/* + * Decode a scanline and apply the predictor routine. + */ +static int +PredictorDecodeRow(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s) +{ + TIFFPredictorState *sp = PredictorState(tif); + + assert(sp != NULL); + assert(sp->decoderow != NULL); + assert(sp->decodepfunc != NULL); + + if ((*sp->decoderow)(tif, op0, occ0, s)) { + (*sp->decodepfunc)(tif, op0, occ0); + return 1; + } else + return 0; +} + +/* + * Decode a tile/strip and apply the predictor routine. + * Note that horizontal differencing must be done on a + * row-by-row basis. The width of a "row" has already + * been calculated at pre-decode time according to the + * strip/tile dimensions. + */ +static int +PredictorDecodeTile(TIFF* tif, uint8* op0, tmsize_t occ0, uint16 s) +{ + TIFFPredictorState *sp = PredictorState(tif); + + assert(sp != NULL); + assert(sp->decodetile != NULL); + + if ((*sp->decodetile)(tif, op0, occ0, s)) { + tmsize_t rowsize = sp->rowsize; + assert(rowsize > 0); + assert((occ0%rowsize)==0); + assert(sp->decodepfunc != NULL); + while (occ0 > 0) { + (*sp->decodepfunc)(tif, op0, rowsize); + occ0 -= rowsize; + op0 += rowsize; + } + return 1; + } else + return 0; +} + +static void +horDiff8(TIFF* tif, uint8* cp0, tmsize_t cc) +{ + TIFFPredictorState* sp = PredictorState(tif); + tmsize_t stride = sp->stride; + char* cp = (char*) cp0; + + assert((cc%stride)==0); + + if (cc > stride) { + cc -= stride; + /* + * Pipeline the most common cases. + */ + if (stride == 3) { + int r1, g1, b1; + int r2 = cp[0]; + int g2 = cp[1]; + int b2 = cp[2]; + do { + r1 = cp[3]; cp[3] = r1-r2; r2 = r1; + g1 = cp[4]; cp[4] = g1-g2; g2 = g1; + b1 = cp[5]; cp[5] = b1-b2; b2 = b1; + cp += 3; + } while ((cc -= 3) > 0); + } else if (stride == 4) { + int r1, g1, b1, a1; + int r2 = cp[0]; + int g2 = cp[1]; + int b2 = cp[2]; + int a2 = cp[3]; + do { + r1 = cp[4]; cp[4] = r1-r2; r2 = r1; + g1 = cp[5]; cp[5] = g1-g2; g2 = g1; + b1 = cp[6]; cp[6] = b1-b2; b2 = b1; + a1 = cp[7]; cp[7] = a1-a2; a2 = a1; + cp += 4; + } while ((cc -= 4) > 0); + } else { + cp += cc - 1; + do { + REPEAT4(stride, cp[stride] -= cp[0]; cp--) + } while ((cc -= stride) > 0); + } + } +} + +static void +horDiff16(TIFF* tif, uint8* cp0, tmsize_t cc) +{ + TIFFPredictorState* sp = PredictorState(tif); + tmsize_t stride = sp->stride; + int16 *wp = (int16*) cp0; + tmsize_t wc = cc/2; + + assert((cc%(2*stride))==0); + + if (wc > stride) { + wc -= stride; + wp += wc - 1; + do { + REPEAT4(stride, wp[stride] -= wp[0]; wp--) + wc -= stride; + } while (wc > 0); + } +} + +static void +horDiff32(TIFF* tif, uint8* cp0, tmsize_t cc) +{ + TIFFPredictorState* sp = PredictorState(tif); + tmsize_t stride = sp->stride; + int32 *wp = (int32*) cp0; + tmsize_t wc = cc/4; + + assert((cc%(4*stride))==0); + + if (wc > stride) { + wc -= stride; + wp += wc - 1; + do { + REPEAT4(stride, wp[stride] -= wp[0]; wp--) + wc -= stride; + } while (wc > 0); + } +} + +/* + * Floating point predictor differencing routine. + */ +static void +fpDiff(TIFF* tif, uint8* cp0, tmsize_t cc) +{ + tmsize_t stride = PredictorState(tif)->stride; + uint32 bps = tif->tif_dir.td_bitspersample / 8; + tmsize_t wc = cc / bps; + tmsize_t count; + uint8 *cp = (uint8 *) cp0; + uint8 *tmp = (uint8 *)_TIFFmalloc(cc); + + assert((cc%(bps*stride))==0); + + if (!tmp) + return; + + _TIFFmemcpy(tmp, cp0, cc); + for (count = 0; count < wc; count++) { + uint32 byte; + for (byte = 0; byte < bps; byte++) { + #if WORDS_BIGENDIAN + cp[byte * wc + count] = tmp[bps * count + byte]; + #else + cp[(bps - byte - 1) * wc + count] = + tmp[bps * count + byte]; + #endif + } + } + _TIFFfree(tmp); + + cp = (uint8 *) cp0; + cp += cc - stride - 1; + for (count = cc; count > stride; count -= stride) + REPEAT4(stride, cp[stride] -= cp[0]; cp--) +} + +static int +PredictorEncodeRow(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + TIFFPredictorState *sp = PredictorState(tif); + + assert(sp != NULL); + assert(sp->encodepfunc != NULL); + assert(sp->encoderow != NULL); + + /* XXX horizontal differencing alters user's data XXX */ + (*sp->encodepfunc)(tif, bp, cc); + return (*sp->encoderow)(tif, bp, cc, s); +} + +static int +PredictorEncodeTile(TIFF* tif, uint8* bp0, tmsize_t cc0, uint16 s) +{ + static const char module[] = "PredictorEncodeTile"; + TIFFPredictorState *sp = PredictorState(tif); + uint8 *working_copy; + tmsize_t cc = cc0, rowsize; + unsigned char* bp; + int result_code; + + assert(sp != NULL); + assert(sp->encodepfunc != NULL); + assert(sp->encodetile != NULL); + + /* + * Do predictor manipulation in a working buffer to avoid altering + * the callers buffer. http://trac.osgeo.org/gdal/ticket/1965 + */ + working_copy = (uint8*) _TIFFmalloc(cc0); + if( working_copy == NULL ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Out of memory allocating " TIFF_SSIZE_FORMAT " byte temp buffer.", + cc0 ); + return 0; + } + memcpy( working_copy, bp0, cc0 ); + bp = working_copy; + + rowsize = sp->rowsize; + assert(rowsize > 0); + assert((cc0%rowsize)==0); + while (cc > 0) { + (*sp->encodepfunc)(tif, bp, rowsize); + cc -= rowsize; + bp += rowsize; + } + result_code = (*sp->encodetile)(tif, working_copy, cc0, s); + + _TIFFfree( working_copy ); + + return result_code; +} + +#define FIELD_PREDICTOR (FIELD_CODEC+0) /* XXX */ + +static const TIFFField predictFields[] = { + { TIFFTAG_PREDICTOR, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UINT16, FIELD_PREDICTOR, FALSE, FALSE, "Predictor", NULL }, +}; + +static int +PredictorVSetField(TIFF* tif, uint32 tag, va_list ap) +{ + TIFFPredictorState *sp = PredictorState(tif); + + assert(sp != NULL); + assert(sp->vsetparent != NULL); + + switch (tag) { + case TIFFTAG_PREDICTOR: + sp->predictor = (uint16) va_arg(ap, uint16_vap); + TIFFSetFieldBit(tif, FIELD_PREDICTOR); + break; + default: + return (*sp->vsetparent)(tif, tag, ap); + } + tif->tif_flags |= TIFF_DIRTYDIRECT; + return 1; +} + +static int +PredictorVGetField(TIFF* tif, uint32 tag, va_list ap) +{ + TIFFPredictorState *sp = PredictorState(tif); + + assert(sp != NULL); + assert(sp->vgetparent != NULL); + + switch (tag) { + case TIFFTAG_PREDICTOR: + *va_arg(ap, uint16*) = sp->predictor; + break; + default: + return (*sp->vgetparent)(tif, tag, ap); + } + return 1; +} + +static void +PredictorPrintDir(TIFF* tif, FILE* fd, long flags) +{ + TIFFPredictorState* sp = PredictorState(tif); + + (void) flags; + if (TIFFFieldSet(tif,FIELD_PREDICTOR)) { + fprintf(fd, " Predictor: "); + switch (sp->predictor) { + case 1: fprintf(fd, "none "); break; + case 2: fprintf(fd, "horizontal differencing "); break; + case 3: fprintf(fd, "floating point predictor "); break; + } + fprintf(fd, "%u (0x%x)\n", sp->predictor, sp->predictor); + } + if (sp->printdir) + (*sp->printdir)(tif, fd, flags); +} + +int +TIFFPredictorInit(TIFF* tif) +{ + TIFFPredictorState* sp = PredictorState(tif); + + assert(sp != 0); + + /* + * Merge codec-specific tag information. + */ + if (!_TIFFMergeFields(tif, predictFields, + TIFFArrayCount(predictFields))) { + TIFFErrorExt(tif->tif_clientdata, "TIFFPredictorInit", + "Merging Predictor codec-specific tags failed"); + return 0; + } + + /* + * Override parent get/set field methods. + */ + sp->vgetparent = tif->tif_tagmethods.vgetfield; + tif->tif_tagmethods.vgetfield = + PredictorVGetField;/* hook for predictor tag */ + sp->vsetparent = tif->tif_tagmethods.vsetfield; + tif->tif_tagmethods.vsetfield = + PredictorVSetField;/* hook for predictor tag */ + sp->printdir = tif->tif_tagmethods.printdir; + tif->tif_tagmethods.printdir = + PredictorPrintDir; /* hook for predictor tag */ + + sp->setupdecode = tif->tif_setupdecode; + tif->tif_setupdecode = PredictorSetupDecode; + sp->setupencode = tif->tif_setupencode; + tif->tif_setupencode = PredictorSetupEncode; + + sp->predictor = 1; /* default value */ + sp->encodepfunc = NULL; /* no predictor routine */ + sp->decodepfunc = NULL; /* no predictor routine */ + return 1; +} + +int +TIFFPredictorCleanup(TIFF* tif) +{ + TIFFPredictorState* sp = PredictorState(tif); + + assert(sp != 0); + + tif->tif_tagmethods.vgetfield = sp->vgetparent; + tif->tif_tagmethods.vsetfield = sp->vsetparent; + tif->tif_tagmethods.printdir = sp->printdir; + tif->tif_setupdecode = sp->setupdecode; + tif->tif_setupencode = sp->setupencode; + + return 1; +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_predict.h b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_predict.h new file mode 100644 index 000000000..dc7144c69 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_predict.h @@ -0,0 +1,77 @@ +/* $Id: tif_predict.h,v 1.8 2010-03-10 18:56:49 bfriesen Exp $ */ + +/* + * Copyright (c) 1995-1997 Sam Leffler + * Copyright (c) 1995-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _TIFFPREDICT_ +#define _TIFFPREDICT_ +/* + * ``Library-private'' Support for the Predictor Tag + */ + +/* + * Codecs that want to support the Predictor tag must place + * this structure first in their private state block so that + * the predictor code can cast tif_data to find its state. + */ +typedef struct { + int predictor; /* predictor tag value */ + tmsize_t stride; /* sample stride over data */ + tmsize_t rowsize; /* tile/strip row size */ + + TIFFCodeMethod encoderow; /* parent codec encode/decode row */ + TIFFCodeMethod encodestrip; /* parent codec encode/decode strip */ + TIFFCodeMethod encodetile; /* parent codec encode/decode tile */ + TIFFPostMethod encodepfunc; /* horizontal differencer */ + + TIFFCodeMethod decoderow; /* parent codec encode/decode row */ + TIFFCodeMethod decodestrip; /* parent codec encode/decode strip */ + TIFFCodeMethod decodetile; /* parent codec encode/decode tile */ + TIFFPostMethod decodepfunc; /* horizontal accumulator */ + + TIFFVGetMethod vgetparent; /* super-class method */ + TIFFVSetMethod vsetparent; /* super-class method */ + TIFFPrintMethod printdir; /* super-class method */ + TIFFBoolMethod setupdecode; /* super-class method */ + TIFFBoolMethod setupencode; /* super-class method */ +} TIFFPredictorState; + +#if defined(__cplusplus) +extern "C" { +#endif +extern int TIFFPredictorInit(TIFF*); +extern int TIFFPredictorCleanup(TIFF*); +#if defined(__cplusplus) +} +#endif +#endif /* _TIFFPREDICT_ */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_print.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_print.c new file mode 100644 index 000000000..9e27ae259 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_print.c @@ -0,0 +1,716 @@ +/* $Id: tif_print.c,v 1.61 2012-12-12 22:50:18 tgl Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Directory Printing Support + */ +#include "tiffiop.h" +#include + +#include + +static void +_TIFFprintAsciiBounded(FILE* fd, const char* cp, int max_chars); + +static const char *photoNames[] = { + "min-is-white", /* PHOTOMETRIC_MINISWHITE */ + "min-is-black", /* PHOTOMETRIC_MINISBLACK */ + "RGB color", /* PHOTOMETRIC_RGB */ + "palette color (RGB from colormap)", /* PHOTOMETRIC_PALETTE */ + "transparency mask", /* PHOTOMETRIC_MASK */ + "separated", /* PHOTOMETRIC_SEPARATED */ + "YCbCr", /* PHOTOMETRIC_YCBCR */ + "7 (0x7)", + "CIE L*a*b*", /* PHOTOMETRIC_CIELAB */ + "ICC L*a*b*", /* PHOTOMETRIC_ICCLAB */ + "ITU L*a*b*" /* PHOTOMETRIC_ITULAB */ +}; +#define NPHOTONAMES (sizeof (photoNames) / sizeof (photoNames[0])) + +static const char *orientNames[] = { + "0 (0x0)", + "row 0 top, col 0 lhs", /* ORIENTATION_TOPLEFT */ + "row 0 top, col 0 rhs", /* ORIENTATION_TOPRIGHT */ + "row 0 bottom, col 0 rhs", /* ORIENTATION_BOTRIGHT */ + "row 0 bottom, col 0 lhs", /* ORIENTATION_BOTLEFT */ + "row 0 lhs, col 0 top", /* ORIENTATION_LEFTTOP */ + "row 0 rhs, col 0 top", /* ORIENTATION_RIGHTTOP */ + "row 0 rhs, col 0 bottom", /* ORIENTATION_RIGHTBOT */ + "row 0 lhs, col 0 bottom", /* ORIENTATION_LEFTBOT */ +}; +#define NORIENTNAMES (sizeof (orientNames) / sizeof (orientNames[0])) + +static void +_TIFFPrintField(FILE* fd, const TIFFField *fip, + uint32 value_count, void *raw_data) +{ + uint32 j; + + fprintf(fd, " %s: ", fip->field_name); + + for(j = 0; j < value_count; j++) { + if(fip->field_type == TIFF_BYTE) + fprintf(fd, "%u", ((uint8 *) raw_data)[j]); + else if(fip->field_type == TIFF_UNDEFINED) + fprintf(fd, "0x%x", + (unsigned int) ((unsigned char *) raw_data)[j]); + else if(fip->field_type == TIFF_SBYTE) + fprintf(fd, "%d", ((int8 *) raw_data)[j]); + else if(fip->field_type == TIFF_SHORT) + fprintf(fd, "%u", ((uint16 *) raw_data)[j]); + else if(fip->field_type == TIFF_SSHORT) + fprintf(fd, "%d", ((int16 *) raw_data)[j]); + else if(fip->field_type == TIFF_LONG) + fprintf(fd, "%lu", + (unsigned long)((uint32 *) raw_data)[j]); + else if(fip->field_type == TIFF_SLONG) + fprintf(fd, "%ld", (long)((int32 *) raw_data)[j]); + else if(fip->field_type == TIFF_IFD) + fprintf(fd, "0x%lx", + (unsigned long)((uint32 *) raw_data)[j]); + else if(fip->field_type == TIFF_RATIONAL + || fip->field_type == TIFF_SRATIONAL + || fip->field_type == TIFF_FLOAT) + fprintf(fd, "%f", ((float *) raw_data)[j]); + else if(fip->field_type == TIFF_LONG8) +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + fprintf(fd, "%I64u", + (unsigned __int64)((uint64 *) raw_data)[j]); +#else + fprintf(fd, "%llu", + (unsigned long long)((uint64 *) raw_data)[j]); +#endif + else if(fip->field_type == TIFF_SLONG8) +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + fprintf(fd, "%I64d", (__int64)((int64 *) raw_data)[j]); +#else + fprintf(fd, "%lld", (long long)((int64 *) raw_data)[j]); +#endif + else if(fip->field_type == TIFF_IFD8) +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + fprintf(fd, "0x%I64x", + (unsigned __int64)((uint64 *) raw_data)[j]); +#else + fprintf(fd, "0x%llx", + (unsigned long long)((uint64 *) raw_data)[j]); +#endif + else if(fip->field_type == TIFF_FLOAT) + fprintf(fd, "%f", ((float *)raw_data)[j]); + else if(fip->field_type == TIFF_DOUBLE) + fprintf(fd, "%f", ((double *) raw_data)[j]); + else if(fip->field_type == TIFF_ASCII) { + fprintf(fd, "%s", (char *) raw_data); + break; + } + else { + fprintf(fd, ""); + break; + } + + if(j < value_count - 1) + fprintf(fd, ","); + } + + fprintf(fd, "\n"); +} + +static int +_TIFFPrettyPrintField(TIFF* tif, const TIFFField *fip, FILE* fd, uint32 tag, + uint32 value_count, void *raw_data) +{ + (void) tif; + + /* do not try to pretty print auto-defined fields */ + if (strncmp(fip->field_name,"Tag ", 4) == 0) { + return 0; + } + + switch (tag) + { + case TIFFTAG_INKSET: + if (value_count == 2 && fip->field_type == TIFF_SHORT) { + fprintf(fd, " Ink Set: "); + switch (*((uint16*)raw_data)) { + case INKSET_CMYK: + fprintf(fd, "CMYK\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + *((uint16*)raw_data), + *((uint16*)raw_data)); + break; + } + return 1; + } + return 0; + + case TIFFTAG_DOTRANGE: + if (value_count == 2 && fip->field_type == TIFF_SHORT) { + fprintf(fd, " Dot Range: %u-%u\n", + ((uint16*)raw_data)[0], ((uint16*)raw_data)[1]); + return 1; + } + return 0; + + case TIFFTAG_WHITEPOINT: + if (value_count == 2 && fip->field_type == TIFF_RATIONAL) { + fprintf(fd, " White Point: %g-%g\n", + ((float *)raw_data)[0], ((float *)raw_data)[1]); + return 1; + } + return 0; + + case TIFFTAG_XMLPACKET: + { + uint32 i; + + fprintf(fd, " XMLPacket (XMP Metadata):\n" ); + for(i = 0; i < value_count; i++) + fputc(((char *)raw_data)[i], fd); + fprintf( fd, "\n" ); + return 1; + } + case TIFFTAG_RICHTIFFIPTC: + /* + * XXX: for some weird reason RichTIFFIPTC tag + * defined as array of LONG values. + */ + fprintf(fd, + " RichTIFFIPTC Data: , %lu bytes\n", + (unsigned long) value_count * 4); + return 1; + + case TIFFTAG_PHOTOSHOP: + fprintf(fd, " Photoshop Data: , %lu bytes\n", + (unsigned long) value_count); + return 1; + + case TIFFTAG_ICCPROFILE: + fprintf(fd, " ICC Profile: , %lu bytes\n", + (unsigned long) value_count); + return 1; + + case TIFFTAG_STONITS: + if (value_count == 1 && fip->field_type == TIFF_DOUBLE) { + fprintf(fd, + " Sample to Nits conversion factor: %.4e\n", + *((double*)raw_data)); + return 1; + } + return 0; + } + + return 0; +} + +/* + * Print the contents of the current directory + * to the specified stdio file stream. + */ +void +TIFFPrintDirectory(TIFF* tif, FILE* fd, long flags) +{ + TIFFDirectory *td = &tif->tif_dir; + char *sep; + uint16 i; + long l, n; + +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + fprintf(fd, "TIFF Directory at offset 0x%I64x (%I64u)\n", + (unsigned __int64) tif->tif_diroff, + (unsigned __int64) tif->tif_diroff); +#else + fprintf(fd, "TIFF Directory at offset 0x%llx (%llu)\n", + (unsigned long long) tif->tif_diroff, + (unsigned long long) tif->tif_diroff); +#endif + if (TIFFFieldSet(tif,FIELD_SUBFILETYPE)) { + fprintf(fd, " Subfile Type:"); + sep = " "; + if (td->td_subfiletype & FILETYPE_REDUCEDIMAGE) { + fprintf(fd, "%sreduced-resolution image", sep); + sep = "/"; + } + if (td->td_subfiletype & FILETYPE_PAGE) { + fprintf(fd, "%smulti-page document", sep); + sep = "/"; + } + if (td->td_subfiletype & FILETYPE_MASK) + fprintf(fd, "%stransparency mask", sep); + fprintf(fd, " (%lu = 0x%lx)\n", + (long) td->td_subfiletype, (long) td->td_subfiletype); + } + if (TIFFFieldSet(tif,FIELD_IMAGEDIMENSIONS)) { + fprintf(fd, " Image Width: %lu Image Length: %lu", + (unsigned long) td->td_imagewidth, (unsigned long) td->td_imagelength); + if (TIFFFieldSet(tif,FIELD_IMAGEDEPTH)) + fprintf(fd, " Image Depth: %lu", + (unsigned long) td->td_imagedepth); + fprintf(fd, "\n"); + } + if (TIFFFieldSet(tif,FIELD_TILEDIMENSIONS)) { + fprintf(fd, " Tile Width: %lu Tile Length: %lu", + (unsigned long) td->td_tilewidth, (unsigned long) td->td_tilelength); + if (TIFFFieldSet(tif,FIELD_TILEDEPTH)) + fprintf(fd, " Tile Depth: %lu", + (unsigned long) td->td_tiledepth); + fprintf(fd, "\n"); + } + if (TIFFFieldSet(tif,FIELD_RESOLUTION)) { + fprintf(fd, " Resolution: %g, %g", + td->td_xresolution, td->td_yresolution); + if (TIFFFieldSet(tif,FIELD_RESOLUTIONUNIT)) { + switch (td->td_resolutionunit) { + case RESUNIT_NONE: + fprintf(fd, " (unitless)"); + break; + case RESUNIT_INCH: + fprintf(fd, " pixels/inch"); + break; + case RESUNIT_CENTIMETER: + fprintf(fd, " pixels/cm"); + break; + default: + fprintf(fd, " (unit %u = 0x%x)", + td->td_resolutionunit, + td->td_resolutionunit); + break; + } + } + fprintf(fd, "\n"); + } + if (TIFFFieldSet(tif,FIELD_POSITION)) + fprintf(fd, " Position: %g, %g\n", + td->td_xposition, td->td_yposition); + if (TIFFFieldSet(tif,FIELD_BITSPERSAMPLE)) + fprintf(fd, " Bits/Sample: %u\n", td->td_bitspersample); + if (TIFFFieldSet(tif,FIELD_SAMPLEFORMAT)) { + fprintf(fd, " Sample Format: "); + switch (td->td_sampleformat) { + case SAMPLEFORMAT_VOID: + fprintf(fd, "void\n"); + break; + case SAMPLEFORMAT_INT: + fprintf(fd, "signed integer\n"); + break; + case SAMPLEFORMAT_UINT: + fprintf(fd, "unsigned integer\n"); + break; + case SAMPLEFORMAT_IEEEFP: + fprintf(fd, "IEEE floating point\n"); + break; + case SAMPLEFORMAT_COMPLEXINT: + fprintf(fd, "complex signed integer\n"); + break; + case SAMPLEFORMAT_COMPLEXIEEEFP: + fprintf(fd, "complex IEEE floating point\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_sampleformat, td->td_sampleformat); + break; + } + } + if (TIFFFieldSet(tif,FIELD_COMPRESSION)) { + const TIFFCodec* c = TIFFFindCODEC(td->td_compression); + fprintf(fd, " Compression Scheme: "); + if (c) + fprintf(fd, "%s\n", c->name); + else + fprintf(fd, "%u (0x%x)\n", + td->td_compression, td->td_compression); + } + if (TIFFFieldSet(tif,FIELD_PHOTOMETRIC)) { + fprintf(fd, " Photometric Interpretation: "); + if (td->td_photometric < NPHOTONAMES) + fprintf(fd, "%s\n", photoNames[td->td_photometric]); + else { + switch (td->td_photometric) { + case PHOTOMETRIC_LOGL: + fprintf(fd, "CIE Log2(L)\n"); + break; + case PHOTOMETRIC_LOGLUV: + fprintf(fd, "CIE Log2(L) (u',v')\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_photometric, td->td_photometric); + break; + } + } + } + if (TIFFFieldSet(tif,FIELD_EXTRASAMPLES) && td->td_extrasamples) { + fprintf(fd, " Extra Samples: %u<", td->td_extrasamples); + sep = ""; + for (i = 0; i < td->td_extrasamples; i++) { + switch (td->td_sampleinfo[i]) { + case EXTRASAMPLE_UNSPECIFIED: + fprintf(fd, "%sunspecified", sep); + break; + case EXTRASAMPLE_ASSOCALPHA: + fprintf(fd, "%sassoc-alpha", sep); + break; + case EXTRASAMPLE_UNASSALPHA: + fprintf(fd, "%sunassoc-alpha", sep); + break; + default: + fprintf(fd, "%s%u (0x%x)", sep, + td->td_sampleinfo[i], td->td_sampleinfo[i]); + break; + } + sep = ", "; + } + fprintf(fd, ">\n"); + } + if (TIFFFieldSet(tif,FIELD_INKNAMES)) { + char* cp; + fprintf(fd, " Ink Names: "); + i = td->td_samplesperpixel; + sep = ""; + for (cp = td->td_inknames; + i > 0 && cp < td->td_inknames + td->td_inknameslen; + cp = strchr(cp,'\0')+1, i--) { + int max_chars = + td->td_inknameslen - (cp - td->td_inknames); + fputs(sep, fd); + _TIFFprintAsciiBounded(fd, cp, max_chars); + sep = ", "; + } + fputs("\n", fd); + } + if (TIFFFieldSet(tif,FIELD_THRESHHOLDING)) { + fprintf(fd, " Thresholding: "); + switch (td->td_threshholding) { + case THRESHHOLD_BILEVEL: + fprintf(fd, "bilevel art scan\n"); + break; + case THRESHHOLD_HALFTONE: + fprintf(fd, "halftone or dithered scan\n"); + break; + case THRESHHOLD_ERRORDIFFUSE: + fprintf(fd, "error diffused\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_threshholding, td->td_threshholding); + break; + } + } + if (TIFFFieldSet(tif,FIELD_FILLORDER)) { + fprintf(fd, " FillOrder: "); + switch (td->td_fillorder) { + case FILLORDER_MSB2LSB: + fprintf(fd, "msb-to-lsb\n"); + break; + case FILLORDER_LSB2MSB: + fprintf(fd, "lsb-to-msb\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_fillorder, td->td_fillorder); + break; + } + } + if (TIFFFieldSet(tif,FIELD_YCBCRSUBSAMPLING)) + { + fprintf(fd, " YCbCr Subsampling: %u, %u\n", + td->td_ycbcrsubsampling[0], td->td_ycbcrsubsampling[1] ); + } + if (TIFFFieldSet(tif,FIELD_YCBCRPOSITIONING)) { + fprintf(fd, " YCbCr Positioning: "); + switch (td->td_ycbcrpositioning) { + case YCBCRPOSITION_CENTERED: + fprintf(fd, "centered\n"); + break; + case YCBCRPOSITION_COSITED: + fprintf(fd, "cosited\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_ycbcrpositioning, td->td_ycbcrpositioning); + break; + } + } + if (TIFFFieldSet(tif,FIELD_HALFTONEHINTS)) + fprintf(fd, " Halftone Hints: light %u dark %u\n", + td->td_halftonehints[0], td->td_halftonehints[1]); + if (TIFFFieldSet(tif,FIELD_ORIENTATION)) { + fprintf(fd, " Orientation: "); + if (td->td_orientation < NORIENTNAMES) + fprintf(fd, "%s\n", orientNames[td->td_orientation]); + else + fprintf(fd, "%u (0x%x)\n", + td->td_orientation, td->td_orientation); + } + if (TIFFFieldSet(tif,FIELD_SAMPLESPERPIXEL)) + fprintf(fd, " Samples/Pixel: %u\n", td->td_samplesperpixel); + if (TIFFFieldSet(tif,FIELD_ROWSPERSTRIP)) { + fprintf(fd, " Rows/Strip: "); + if (td->td_rowsperstrip == (uint32) -1) + fprintf(fd, "(infinite)\n"); + else + fprintf(fd, "%lu\n", (unsigned long) td->td_rowsperstrip); + } + if (TIFFFieldSet(tif,FIELD_MINSAMPLEVALUE)) + fprintf(fd, " Min Sample Value: %u\n", td->td_minsamplevalue); + if (TIFFFieldSet(tif,FIELD_MAXSAMPLEVALUE)) + fprintf(fd, " Max Sample Value: %u\n", td->td_maxsamplevalue); + if (TIFFFieldSet(tif,FIELD_SMINSAMPLEVALUE)) { + int count = (tif->tif_flags & TIFF_PERSAMPLE) ? td->td_samplesperpixel : 1; + fprintf(fd, " SMin Sample Value:"); + for (i = 0; i < count; ++i) + fprintf(fd, " %g", td->td_sminsamplevalue[i]); + fprintf(fd, "\n"); + } + if (TIFFFieldSet(tif,FIELD_SMAXSAMPLEVALUE)) { + int count = (tif->tif_flags & TIFF_PERSAMPLE) ? td->td_samplesperpixel : 1; + fprintf(fd, " SMax Sample Value:"); + for (i = 0; i < count; ++i) + fprintf(fd, " %g", td->td_smaxsamplevalue[i]); + fprintf(fd, "\n"); + } + if (TIFFFieldSet(tif,FIELD_PLANARCONFIG)) { + fprintf(fd, " Planar Configuration: "); + switch (td->td_planarconfig) { + case PLANARCONFIG_CONTIG: + fprintf(fd, "single image plane\n"); + break; + case PLANARCONFIG_SEPARATE: + fprintf(fd, "separate image planes\n"); + break; + default: + fprintf(fd, "%u (0x%x)\n", + td->td_planarconfig, td->td_planarconfig); + break; + } + } + if (TIFFFieldSet(tif,FIELD_PAGENUMBER)) + fprintf(fd, " Page Number: %u-%u\n", + td->td_pagenumber[0], td->td_pagenumber[1]); + if (TIFFFieldSet(tif,FIELD_COLORMAP)) { + fprintf(fd, " Color Map: "); + if (flags & TIFFPRINT_COLORMAP) { + fprintf(fd, "\n"); + n = 1L<td_bitspersample; + for (l = 0; l < n; l++) + fprintf(fd, " %5lu: %5u %5u %5u\n", + l, + td->td_colormap[0][l], + td->td_colormap[1][l], + td->td_colormap[2][l]); + } else + fprintf(fd, "(present)\n"); + } + if (TIFFFieldSet(tif,FIELD_REFBLACKWHITE)) { + fprintf(fd, " Reference Black/White:\n"); + for (i = 0; i < 3; i++) + fprintf(fd, " %2d: %5g %5g\n", i, + td->td_refblackwhite[2*i+0], + td->td_refblackwhite[2*i+1]); + } + if (TIFFFieldSet(tif,FIELD_TRANSFERFUNCTION)) { + fprintf(fd, " Transfer Function: "); + if (flags & TIFFPRINT_CURVES) { + fprintf(fd, "\n"); + n = 1L<td_bitspersample; + for (l = 0; l < n; l++) { + fprintf(fd, " %2lu: %5u", + l, td->td_transferfunction[0][l]); + for (i = 1; i < td->td_samplesperpixel; i++) + fprintf(fd, " %5u", + td->td_transferfunction[i][l]); + fputc('\n', fd); + } + } else + fprintf(fd, "(present)\n"); + } + if (TIFFFieldSet(tif, FIELD_SUBIFD) && (td->td_subifd)) { + fprintf(fd, " SubIFD Offsets:"); + for (i = 0; i < td->td_nsubifd; i++) +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + fprintf(fd, " %5I64u", + (unsigned __int64) td->td_subifd[i]); +#else + fprintf(fd, " %5llu", + (unsigned long long) td->td_subifd[i]); +#endif + fputc('\n', fd); + } + + /* + ** Custom tag support. + */ + { + int i; + short count; + + count = (short) TIFFGetTagListCount(tif); + for(i = 0; i < count; i++) { + uint32 tag = TIFFGetTagListEntry(tif, i); + const TIFFField *fip; + uint32 value_count; + int mem_alloc = 0; + void *raw_data; + + fip = TIFFFieldWithTag(tif, tag); + if(fip == NULL) + continue; + + if(fip->field_passcount) { + if (fip->field_readcount == TIFF_VARIABLE2 ) { + if(TIFFGetField(tif, tag, &value_count, &raw_data) != 1) + continue; + } else if (fip->field_readcount == TIFF_VARIABLE ) { + uint16 small_value_count; + if(TIFFGetField(tif, tag, &small_value_count, &raw_data) != 1) + continue; + value_count = small_value_count; + } else { + assert (fip->field_readcount == TIFF_VARIABLE + || fip->field_readcount == TIFF_VARIABLE2); + continue; + } + } else { + if (fip->field_readcount == TIFF_VARIABLE + || fip->field_readcount == TIFF_VARIABLE2) + value_count = 1; + else if (fip->field_readcount == TIFF_SPP) + value_count = td->td_samplesperpixel; + else + value_count = fip->field_readcount; + if (fip->field_tag == TIFFTAG_DOTRANGE + && strcmp(fip->field_name,"DotRange") == 0) { + /* TODO: This is an evil exception and should not have been + handled this way ... likely best if we move it into + the directory structure with an explicit field in + libtiff 4.1 and assign it a FIELD_ value */ + static uint16 dotrange[2]; + raw_data = dotrange; + TIFFGetField(tif, tag, dotrange+0, dotrange+1); + } else if (fip->field_type == TIFF_ASCII + || fip->field_readcount == TIFF_VARIABLE + || fip->field_readcount == TIFF_VARIABLE2 + || fip->field_readcount == TIFF_SPP + || value_count > 1) { + if(TIFFGetField(tif, tag, &raw_data) != 1) + continue; + } else { + raw_data = _TIFFmalloc( + _TIFFDataSize(fip->field_type) + * value_count); + mem_alloc = 1; + if(TIFFGetField(tif, tag, raw_data) != 1) { + _TIFFfree(raw_data); + continue; + } + } + } + + /* + * Catch the tags which needs to be specially handled + * and pretty print them. If tag not handled in + * _TIFFPrettyPrintField() fall down and print it as + * any other tag. + */ + if (!_TIFFPrettyPrintField(tif, fip, fd, tag, value_count, raw_data)) + _TIFFPrintField(fd, fip, value_count, raw_data); + + if(mem_alloc) + _TIFFfree(raw_data); + } + } + + if (tif->tif_tagmethods.printdir) + (*tif->tif_tagmethods.printdir)(tif, fd, flags); + + _TIFFFillStriles( tif ); + + if ((flags & TIFFPRINT_STRIPS) && + TIFFFieldSet(tif,FIELD_STRIPOFFSETS)) { + uint32 s; + + fprintf(fd, " %lu %s:\n", + (long) td->td_nstrips, + isTiled(tif) ? "Tiles" : "Strips"); + for (s = 0; s < td->td_nstrips; s++) +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + fprintf(fd, " %3lu: [%8I64u, %8I64u]\n", + (unsigned long) s, + (unsigned __int64) td->td_stripoffset[s], + (unsigned __int64) td->td_stripbytecount[s]); +#else + fprintf(fd, " %3lu: [%8llu, %8llu]\n", + (unsigned long) s, + (unsigned long long) td->td_stripoffset[s], + (unsigned long long) td->td_stripbytecount[s]); +#endif + } +} + +void +_TIFFprintAscii(FILE* fd, const char* cp) +{ + _TIFFprintAsciiBounded( fd, cp, strlen(cp)); +} + +static void +_TIFFprintAsciiBounded(FILE* fd, const char* cp, int max_chars) +{ + for (; max_chars > 0 && *cp != '\0'; cp++, max_chars--) { + const char* tp; + + if (isprint((int)*cp)) { + fputc(*cp, fd); + continue; + } + for (tp = "\tt\bb\rr\nn\vv"; *tp; tp++) + if (*tp++ == *cp) + break; + if (*tp) + fprintf(fd, "\\%c", *tp); + else + fprintf(fd, "\\%03o", *cp & 0xff); + } +} + +void +_TIFFprintAsciiTag(FILE* fd, const char* name, const char* value) +{ + fprintf(fd, " %s: \"", name); + _TIFFprintAscii(fd, value); + fprintf(fd, "\"\n"); +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_read.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_read.c new file mode 100644 index 000000000..b0095192f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_read.c @@ -0,0 +1,1086 @@ +/* $Id: tif_read.c,v 1.44 2014-12-23 10:15:35 erouault Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * Scanline-oriented Read Support + */ +#include "tiffiop.h" +#include + +int TIFFFillStrip(TIFF* tif, uint32 strip); +int TIFFFillTile(TIFF* tif, uint32 tile); +static int TIFFStartStrip(TIFF* tif, uint32 strip); +static int TIFFStartTile(TIFF* tif, uint32 tile); +static int TIFFCheckRead(TIFF*, int); +static tmsize_t +TIFFReadRawStrip1(TIFF* tif, uint32 strip, void* buf, tmsize_t size,const char* module); + +#define NOSTRIP ((uint32)(-1)) /* undefined state */ +#define NOTILE ((uint32)(-1)) /* undefined state */ + +static int +TIFFFillStripPartial( TIFF *tif, int strip, tmsize_t read_ahead, int restart ) +{ + static const char module[] = "TIFFFillStripPartial"; + register TIFFDirectory *td = &tif->tif_dir; + tmsize_t unused_data; + uint64 read_offset; + tmsize_t cc, to_read; + /* tmsize_t bytecountm; */ + + if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount) + return 0; + + /* + * Expand raw data buffer, if needed, to hold data + * strip coming from file (perhaps should set upper + * bound on the size of a buffer we'll use?). + */ + + /* bytecountm=(tmsize_t) td->td_stripbytecount[strip]; */ + if (read_ahead*2 > tif->tif_rawdatasize) { + assert( restart ); + + tif->tif_curstrip = NOSTRIP; + if ((tif->tif_flags & TIFF_MYBUFFER) == 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "Data buffer too small to hold part of strip %lu", + (unsigned long) strip); + return (0); + } + if (!TIFFReadBufferSetup(tif, 0, read_ahead*2)) + return (0); + } + + if( restart ) + { + tif->tif_rawdataloaded = 0; + tif->tif_rawdataoff = 0; + } + + /* + ** If we are reading more data, move any unused data to the + ** start of the buffer. + */ + if( tif->tif_rawdataloaded > 0 ) + unused_data = tif->tif_rawdataloaded - (tif->tif_rawcp - tif->tif_rawdata); + else + unused_data = 0; + + if( unused_data > 0 ) + { + assert((tif->tif_flags&TIFF_BUFFERMMAP)==0); + memmove( tif->tif_rawdata, tif->tif_rawcp, unused_data ); + } + + /* + ** Seek to the point in the file where more data should be read. + */ + read_offset = td->td_stripoffset[strip] + + tif->tif_rawdataoff + tif->tif_rawdataloaded; + + if (!SeekOK(tif, read_offset)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Seek error at scanline %lu, strip %lu", + (unsigned long) tif->tif_row, (unsigned long) strip); + return 0; + } + + /* + ** How much do we want to read? + */ + to_read = tif->tif_rawdatasize - unused_data; + if( (uint64) to_read > td->td_stripbytecount[strip] + - tif->tif_rawdataoff - tif->tif_rawdataloaded ) + { + to_read = (tmsize_t) td->td_stripbytecount[strip] + - tif->tif_rawdataoff - tif->tif_rawdataloaded; + } + + assert((tif->tif_flags&TIFF_BUFFERMMAP)==0); + cc = TIFFReadFile(tif, tif->tif_rawdata + unused_data, to_read); + + if (cc != to_read) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "Read error at scanline %lu; got %I64u bytes, expected %I64u", + (unsigned long) tif->tif_row, + (unsigned __int64) cc, + (unsigned __int64) to_read); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "Read error at scanline %lu; got %llu bytes, expected %llu", + (unsigned long) tif->tif_row, + (unsigned long long) cc, + (unsigned long long) to_read); +#endif + return 0; + } + + tif->tif_rawdataoff = tif->tif_rawdataoff + tif->tif_rawdataloaded - unused_data ; + tif->tif_rawdataloaded = unused_data + to_read; + + tif->tif_rawcp = tif->tif_rawdata; + + if (!isFillOrder(tif, td->td_fillorder) && + (tif->tif_flags & TIFF_NOBITREV) == 0) { + assert((tif->tif_flags&TIFF_BUFFERMMAP)==0); + TIFFReverseBits(tif->tif_rawdata + unused_data, to_read ); + } + + /* + ** When starting a strip from the beginning we need to + ** restart the decoder. + */ + if( restart ) + return TIFFStartStrip(tif, strip); + else + return 1; +} + +/* + * Seek to a random row+sample in a file. + * + * Only used by TIFFReadScanline, and is only used on + * strip organized files. We do some tricky stuff to try + * and avoid reading the whole compressed raw data for big + * strips. + */ +static int +TIFFSeek(TIFF* tif, uint32 row, uint16 sample ) +{ + register TIFFDirectory *td = &tif->tif_dir; + uint32 strip; + int whole_strip; + tmsize_t read_ahead = 0; + + /* + ** Establish what strip we are working from. + */ + if (row >= td->td_imagelength) { /* out of range */ + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "%lu: Row out of range, max %lu", + (unsigned long) row, + (unsigned long) td->td_imagelength); + return (0); + } + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { + if (sample >= td->td_samplesperpixel) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "%lu: Sample out of range, max %lu", + (unsigned long) sample, (unsigned long) td->td_samplesperpixel); + return (0); + } + strip = (uint32)sample*td->td_stripsperimage + row/td->td_rowsperstrip; + } else + strip = row / td->td_rowsperstrip; + + /* + * Do we want to treat this strip as one whole chunk or + * read it a few lines at a time? + */ +#if defined(CHUNKY_STRIP_READ_SUPPORT) + if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount) + return 0; + whole_strip = tif->tif_dir.td_stripbytecount[strip] < 10 + || isMapped(tif); +#else + whole_strip = 1; +#endif + + if( !whole_strip ) + { + read_ahead = tif->tif_scanlinesize * 16 + 5000; + } + + /* + * If we haven't loaded this strip, do so now, possibly + * only reading the first part. + */ + if (strip != tif->tif_curstrip) { /* different strip, refill */ + + if( whole_strip ) + { + if (!TIFFFillStrip(tif, strip)) + return (0); + } + else + { + if( !TIFFFillStripPartial(tif,strip,read_ahead,1) ) + return 0; + } + } + + /* + ** If we already have some data loaded, do we need to read some more? + */ + else if( !whole_strip ) + { + if( ((tif->tif_rawdata + tif->tif_rawdataloaded) - tif->tif_rawcp) < read_ahead + && (uint64) tif->tif_rawdataoff+tif->tif_rawdataloaded < td->td_stripbytecount[strip] ) + { + if( !TIFFFillStripPartial(tif,strip,read_ahead,0) ) + return 0; + } + } + + if (row < tif->tif_row) { + /* + * Moving backwards within the same strip: backup + * to the start and then decode forward (below). + * + * NB: If you're planning on lots of random access within a + * strip, it's better to just read and decode the entire + * strip, and then access the decoded data in a random fashion. + */ + + if( tif->tif_rawdataoff != 0 ) + { + if( !TIFFFillStripPartial(tif,strip,read_ahead,1) ) + return 0; + } + else + { + if (!TIFFStartStrip(tif, strip)) + return (0); + } + } + + if (row != tif->tif_row) { + /* + * Seek forward to the desired row. + */ + + /* TODO: Will this really work with partial buffers? */ + + if (!(*tif->tif_seek)(tif, row - tif->tif_row)) + return (0); + tif->tif_row = row; + } + + return (1); +} + +int +TIFFReadScanline(TIFF* tif, void* buf, uint32 row, uint16 sample) +{ + int e; + + if (!TIFFCheckRead(tif, 0)) + return (-1); + if( (e = TIFFSeek(tif, row, sample)) != 0) { + /* + * Decompress desired row into user buffer. + */ + e = (*tif->tif_decoderow) + (tif, (uint8*) buf, tif->tif_scanlinesize, sample); + + /* we are now poised at the beginning of the next row */ + tif->tif_row = row + 1; + + if (e) + (*tif->tif_postdecode)(tif, (uint8*) buf, + tif->tif_scanlinesize); + } + return (e > 0 ? 1 : -1); +} + +/* + * Read a strip of data and decompress the specified + * amount into the user-supplied buffer. + */ +tmsize_t +TIFFReadEncodedStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size) +{ + static const char module[] = "TIFFReadEncodedStrip"; + TIFFDirectory *td = &tif->tif_dir; + uint32 rowsperstrip; + uint32 stripsperplane; + uint32 stripinplane; + uint16 plane; + uint32 rows; + tmsize_t stripsize; + if (!TIFFCheckRead(tif,0)) + return((tmsize_t)(-1)); + if (strip>=td->td_nstrips) + { + TIFFErrorExt(tif->tif_clientdata,module, + "%lu: Strip out of range, max %lu",(unsigned long)strip, + (unsigned long)td->td_nstrips); + return((tmsize_t)(-1)); + } + /* + * Calculate the strip size according to the number of + * rows in the strip (check for truncated last strip on any + * of the separations). + */ + rowsperstrip=td->td_rowsperstrip; + if (rowsperstrip>td->td_imagelength) + rowsperstrip=td->td_imagelength; + stripsperplane=((td->td_imagelength+rowsperstrip-1)/rowsperstrip); + stripinplane=(strip%stripsperplane); + plane=(strip/stripsperplane); + rows=td->td_imagelength-stripinplane*rowsperstrip; + if (rows>rowsperstrip) + rows=rowsperstrip; + stripsize=TIFFVStripSize(tif,rows); + if (stripsize==0) + return((tmsize_t)(-1)); + if ((size!=(tmsize_t)(-1))&&(sizetif_decodestrip)(tif,buf,stripsize,plane)<=0) + return((tmsize_t)(-1)); + (*tif->tif_postdecode)(tif,buf,stripsize); + return(stripsize); +} + +static tmsize_t +TIFFReadRawStrip1(TIFF* tif, uint32 strip, void* buf, tmsize_t size, + const char* module) +{ + TIFFDirectory *td = &tif->tif_dir; + + if (!_TIFFFillStriles( tif )) + return ((tmsize_t)(-1)); + + assert((tif->tif_flags&TIFF_NOREADRAW)==0); + if (!isMapped(tif)) { + tmsize_t cc; + + if (!SeekOK(tif, td->td_stripoffset[strip])) { + TIFFErrorExt(tif->tif_clientdata, module, + "Seek error at scanline %lu, strip %lu", + (unsigned long) tif->tif_row, (unsigned long) strip); + return ((tmsize_t)(-1)); + } + cc = TIFFReadFile(tif, buf, size); + if (cc != size) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "Read error at scanline %lu; got %I64u bytes, expected %I64u", + (unsigned long) tif->tif_row, + (unsigned __int64) cc, + (unsigned __int64) size); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "Read error at scanline %lu; got %llu bytes, expected %llu", + (unsigned long) tif->tif_row, + (unsigned long long) cc, + (unsigned long long) size); +#endif + return ((tmsize_t)(-1)); + } + } else { + tmsize_t ma,mb; + tmsize_t n; + ma=(tmsize_t)td->td_stripoffset[strip]; + mb=ma+size; + if (((uint64)ma!=td->td_stripoffset[strip])||(ma>tif->tif_size)) + n=0; + else if ((mbtif->tif_size)) + n=tif->tif_size-ma; + else + n=size; + if (n!=size) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "Read error at scanline %lu, strip %lu; got %I64u bytes, expected %I64u", + (unsigned long) tif->tif_row, + (unsigned long) strip, + (unsigned __int64) n, + (unsigned __int64) size); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "Read error at scanline %lu, strip %lu; got %llu bytes, expected %llu", + (unsigned long) tif->tif_row, + (unsigned long) strip, + (unsigned long long) n, + (unsigned long long) size); +#endif + return ((tmsize_t)(-1)); + } + _TIFFmemcpy(buf, tif->tif_base + ma, + size); + } + return (size); +} + +/* + * Read a strip of data from the file. + */ +tmsize_t +TIFFReadRawStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size) +{ + static const char module[] = "TIFFReadRawStrip"; + TIFFDirectory *td = &tif->tif_dir; + uint64 bytecount; + tmsize_t bytecountm; + + if (!TIFFCheckRead(tif, 0)) + return ((tmsize_t)(-1)); + if (strip >= td->td_nstrips) { + TIFFErrorExt(tif->tif_clientdata, module, + "%lu: Strip out of range, max %lu", + (unsigned long) strip, + (unsigned long) td->td_nstrips); + return ((tmsize_t)(-1)); + } + if (tif->tif_flags&TIFF_NOREADRAW) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Compression scheme does not support access to raw uncompressed data"); + return ((tmsize_t)(-1)); + } + bytecount = td->td_stripbytecount[strip]; + if ((int64)bytecount <= 0) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "%I64u: Invalid strip byte count, strip %lu", + (unsigned __int64) bytecount, + (unsigned long) strip); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "%llu: Invalid strip byte count, strip %lu", + (unsigned long long) bytecount, + (unsigned long) strip); +#endif + return ((tmsize_t)(-1)); + } + bytecountm = (tmsize_t)bytecount; + if ((uint64)bytecountm!=bytecount) { + TIFFErrorExt(tif->tif_clientdata, module, "Integer overflow"); + return ((tmsize_t)(-1)); + } + if (size != (tmsize_t)(-1) && size < bytecountm) + bytecountm = size; + return (TIFFReadRawStrip1(tif, strip, buf, bytecountm, module)); +} + +/* + * Read the specified strip and setup for decoding. The data buffer is + * expanded, as necessary, to hold the strip's data. + */ +int +TIFFFillStrip(TIFF* tif, uint32 strip) +{ + static const char module[] = "TIFFFillStrip"; + TIFFDirectory *td = &tif->tif_dir; + + if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount) + return 0; + + if ((tif->tif_flags&TIFF_NOREADRAW)==0) + { + uint64 bytecount = td->td_stripbytecount[strip]; + if ((int64)bytecount <= 0) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "Invalid strip byte count %I64u, strip %lu", + (unsigned __int64) bytecount, + (unsigned long) strip); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "Invalid strip byte count %llu, strip %lu", + (unsigned long long) bytecount, + (unsigned long) strip); +#endif + return (0); + } + if (isMapped(tif) && + (isFillOrder(tif, td->td_fillorder) + || (tif->tif_flags & TIFF_NOBITREV))) { + /* + * The image is mapped into memory and we either don't + * need to flip bits or the compression routine is + * going to handle this operation itself. In this + * case, avoid copying the raw data and instead just + * reference the data from the memory mapped file + * image. This assumes that the decompression + * routines do not modify the contents of the raw data + * buffer (if they try to, the application will get a + * fault since the file is mapped read-only). + */ + if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) { + _TIFFfree(tif->tif_rawdata); + tif->tif_rawdata = NULL; + tif->tif_rawdatasize = 0; + } + tif->tif_flags &= ~TIFF_MYBUFFER; + /* + * We must check for overflow, potentially causing + * an OOB read. Instead of simple + * + * td->td_stripoffset[strip]+bytecount > tif->tif_size + * + * comparison (which can overflow) we do the following + * two comparisons: + */ + if (bytecount > (uint64)tif->tif_size || + td->td_stripoffset[strip] > (uint64)tif->tif_size - bytecount) { + /* + * This error message might seem strange, but + * it's what would happen if a read were done + * instead. + */ +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + + "Read error on strip %lu; " + "got %I64u bytes, expected %I64u", + (unsigned long) strip, + (unsigned __int64) tif->tif_size - td->td_stripoffset[strip], + (unsigned __int64) bytecount); +#else + TIFFErrorExt(tif->tif_clientdata, module, + + "Read error on strip %lu; " + "got %llu bytes, expected %llu", + (unsigned long) strip, + (unsigned long long) tif->tif_size - td->td_stripoffset[strip], + (unsigned long long) bytecount); +#endif + tif->tif_curstrip = NOSTRIP; + return (0); + } + tif->tif_rawdatasize = (tmsize_t)bytecount; + tif->tif_rawdata = tif->tif_base + (tmsize_t)td->td_stripoffset[strip]; + tif->tif_rawdataoff = 0; + tif->tif_rawdataloaded = (tmsize_t) bytecount; + + /* + * When we have tif_rawdata reference directly into the memory mapped file + * we need to be pretty careful about how we use the rawdata. It is not + * a general purpose working buffer as it normally otherwise is. So we + * keep track of this fact to avoid using it improperly. + */ + tif->tif_flags |= TIFF_BUFFERMMAP; + } else { + /* + * Expand raw data buffer, if needed, to hold data + * strip coming from file (perhaps should set upper + * bound on the size of a buffer we'll use?). + */ + tmsize_t bytecountm; + bytecountm=(tmsize_t)bytecount; + if ((uint64)bytecountm!=bytecount) + { + TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow"); + return(0); + } + if (bytecountm > tif->tif_rawdatasize) { + tif->tif_curstrip = NOSTRIP; + if ((tif->tif_flags & TIFF_MYBUFFER) == 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "Data buffer too small to hold strip %lu", + (unsigned long) strip); + return (0); + } + if (!TIFFReadBufferSetup(tif, 0, bytecountm)) + return (0); + } + if (tif->tif_flags&TIFF_BUFFERMMAP) { + tif->tif_curstrip = NOSTRIP; + if (!TIFFReadBufferSetup(tif, 0, bytecountm)) + return (0); + } + if (TIFFReadRawStrip1(tif, strip, tif->tif_rawdata, + bytecountm, module) != bytecountm) + return (0); + + tif->tif_rawdataoff = 0; + tif->tif_rawdataloaded = bytecountm; + + if (!isFillOrder(tif, td->td_fillorder) && + (tif->tif_flags & TIFF_NOBITREV) == 0) + TIFFReverseBits(tif->tif_rawdata, bytecountm); + } + } + return (TIFFStartStrip(tif, strip)); +} + +/* + * Tile-oriented Read Support + * Contributed by Nancy Cam (Silicon Graphics). + */ + +/* + * Read and decompress a tile of data. The + * tile is selected by the (x,y,z,s) coordinates. + */ +tmsize_t +TIFFReadTile(TIFF* tif, void* buf, uint32 x, uint32 y, uint32 z, uint16 s) +{ + if (!TIFFCheckRead(tif, 1) || !TIFFCheckTile(tif, x, y, z, s)) + return ((tmsize_t)(-1)); + return (TIFFReadEncodedTile(tif, + TIFFComputeTile(tif, x, y, z, s), buf, (tmsize_t)(-1))); +} + +/* + * Read a tile of data and decompress the specified + * amount into the user-supplied buffer. + */ +tmsize_t +TIFFReadEncodedTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size) +{ + static const char module[] = "TIFFReadEncodedTile"; + TIFFDirectory *td = &tif->tif_dir; + tmsize_t tilesize = tif->tif_tilesize; + + if (!TIFFCheckRead(tif, 1)) + return ((tmsize_t)(-1)); + if (tile >= td->td_nstrips) { + TIFFErrorExt(tif->tif_clientdata, module, + "%lu: Tile out of range, max %lu", + (unsigned long) tile, (unsigned long) td->td_nstrips); + return ((tmsize_t)(-1)); + } + if (size == (tmsize_t)(-1)) + size = tilesize; + else if (size > tilesize) + size = tilesize; + if (TIFFFillTile(tif, tile) && (*tif->tif_decodetile)(tif, + (uint8*) buf, size, (uint16)(tile/td->td_stripsperimage))) { + (*tif->tif_postdecode)(tif, (uint8*) buf, size); + return (size); + } else + return ((tmsize_t)(-1)); +} + +static tmsize_t +TIFFReadRawTile1(TIFF* tif, uint32 tile, void* buf, tmsize_t size, const char* module) +{ + TIFFDirectory *td = &tif->tif_dir; + + if (!_TIFFFillStriles( tif )) + return ((tmsize_t)(-1)); + + assert((tif->tif_flags&TIFF_NOREADRAW)==0); + if (!isMapped(tif)) { + tmsize_t cc; + + if (!SeekOK(tif, td->td_stripoffset[tile])) { + TIFFErrorExt(tif->tif_clientdata, module, + "Seek error at row %lu, col %lu, tile %lu", + (unsigned long) tif->tif_row, + (unsigned long) tif->tif_col, + (unsigned long) tile); + return ((tmsize_t)(-1)); + } + cc = TIFFReadFile(tif, buf, size); + if (cc != size) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "Read error at row %lu, col %lu; got %I64u bytes, expected %I64u", + (unsigned long) tif->tif_row, + (unsigned long) tif->tif_col, + (unsigned __int64) cc, + (unsigned __int64) size); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "Read error at row %lu, col %lu; got %llu bytes, expected %llu", + (unsigned long) tif->tif_row, + (unsigned long) tif->tif_col, + (unsigned long long) cc, + (unsigned long long) size); +#endif + return ((tmsize_t)(-1)); + } + } else { + tmsize_t ma,mb; + tmsize_t n; + ma=(tmsize_t)td->td_stripoffset[tile]; + mb=ma+size; + if (((uint64)ma!=td->td_stripoffset[tile])||(ma>tif->tif_size)) + n=0; + else if ((mbtif->tif_size)) + n=tif->tif_size-ma; + else + n=size; + if (n!=size) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, +"Read error at row %lu, col %lu, tile %lu; got %I64u bytes, expected %I64u", + (unsigned long) tif->tif_row, + (unsigned long) tif->tif_col, + (unsigned long) tile, + (unsigned __int64) n, + (unsigned __int64) size); +#else + TIFFErrorExt(tif->tif_clientdata, module, +"Read error at row %lu, col %lu, tile %lu; got %llu bytes, expected %llu", + (unsigned long) tif->tif_row, + (unsigned long) tif->tif_col, + (unsigned long) tile, + (unsigned long long) n, + (unsigned long long) size); +#endif + return ((tmsize_t)(-1)); + } + _TIFFmemcpy(buf, tif->tif_base + ma, size); + } + return (size); +} + +/* + * Read a tile of data from the file. + */ +tmsize_t +TIFFReadRawTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size) +{ + static const char module[] = "TIFFReadRawTile"; + TIFFDirectory *td = &tif->tif_dir; + uint64 bytecount64; + tmsize_t bytecountm; + + if (!TIFFCheckRead(tif, 1)) + return ((tmsize_t)(-1)); + if (tile >= td->td_nstrips) { + TIFFErrorExt(tif->tif_clientdata, module, + "%lu: Tile out of range, max %lu", + (unsigned long) tile, (unsigned long) td->td_nstrips); + return ((tmsize_t)(-1)); + } + if (tif->tif_flags&TIFF_NOREADRAW) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Compression scheme does not support access to raw uncompressed data"); + return ((tmsize_t)(-1)); + } + bytecount64 = td->td_stripbytecount[tile]; + if (size != (tmsize_t)(-1) && (uint64)size < bytecount64) + bytecount64 = (uint64)size; + bytecountm = (tmsize_t)bytecount64; + if ((uint64)bytecountm!=bytecount64) + { + TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow"); + return ((tmsize_t)(-1)); + } + return (TIFFReadRawTile1(tif, tile, buf, bytecountm, module)); +} + +/* + * Read the specified tile and setup for decoding. The data buffer is + * expanded, as necessary, to hold the tile's data. + */ +int +TIFFFillTile(TIFF* tif, uint32 tile) +{ + static const char module[] = "TIFFFillTile"; + TIFFDirectory *td = &tif->tif_dir; + + if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount) + return 0; + + if ((tif->tif_flags&TIFF_NOREADRAW)==0) + { + uint64 bytecount = td->td_stripbytecount[tile]; + if ((int64)bytecount <= 0) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "%I64u: Invalid tile byte count, tile %lu", + (unsigned __int64) bytecount, + (unsigned long) tile); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "%llu: Invalid tile byte count, tile %lu", + (unsigned long long) bytecount, + (unsigned long) tile); +#endif + return (0); + } + if (isMapped(tif) && + (isFillOrder(tif, td->td_fillorder) + || (tif->tif_flags & TIFF_NOBITREV))) { + /* + * The image is mapped into memory and we either don't + * need to flip bits or the compression routine is + * going to handle this operation itself. In this + * case, avoid copying the raw data and instead just + * reference the data from the memory mapped file + * image. This assumes that the decompression + * routines do not modify the contents of the raw data + * buffer (if they try to, the application will get a + * fault since the file is mapped read-only). + */ + if ((tif->tif_flags & TIFF_MYBUFFER) && tif->tif_rawdata) { + _TIFFfree(tif->tif_rawdata); + tif->tif_rawdata = NULL; + tif->tif_rawdatasize = 0; + } + tif->tif_flags &= ~TIFF_MYBUFFER; + /* + * We must check for overflow, potentially causing + * an OOB read. Instead of simple + * + * td->td_stripoffset[tile]+bytecount > tif->tif_size + * + * comparison (which can overflow) we do the following + * two comparisons: + */ + if (bytecount > (uint64)tif->tif_size || + td->td_stripoffset[tile] > (uint64)tif->tif_size - bytecount) { + tif->tif_curtile = NOTILE; + return (0); + } + tif->tif_rawdatasize = (tmsize_t)bytecount; + tif->tif_rawdata = + tif->tif_base + (tmsize_t)td->td_stripoffset[tile]; + tif->tif_rawdataoff = 0; + tif->tif_rawdataloaded = (tmsize_t) bytecount; + tif->tif_flags |= TIFF_BUFFERMMAP; + } else { + /* + * Expand raw data buffer, if needed, to hold data + * tile coming from file (perhaps should set upper + * bound on the size of a buffer we'll use?). + */ + tmsize_t bytecountm; + bytecountm=(tmsize_t)bytecount; + if ((uint64)bytecountm!=bytecount) + { + TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow"); + return(0); + } + if (bytecountm > tif->tif_rawdatasize) { + tif->tif_curtile = NOTILE; + if ((tif->tif_flags & TIFF_MYBUFFER) == 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "Data buffer too small to hold tile %lu", + (unsigned long) tile); + return (0); + } + if (!TIFFReadBufferSetup(tif, 0, bytecountm)) + return (0); + } + if (tif->tif_flags&TIFF_BUFFERMMAP) { + tif->tif_curtile = NOTILE; + if (!TIFFReadBufferSetup(tif, 0, bytecountm)) + return (0); + } + + if (TIFFReadRawTile1(tif, tile, tif->tif_rawdata, + bytecountm, module) != bytecountm) + return (0); + + tif->tif_rawdataoff = 0; + tif->tif_rawdataloaded = bytecountm; + + if (!isFillOrder(tif, td->td_fillorder) && + (tif->tif_flags & TIFF_NOBITREV) == 0) + TIFFReverseBits(tif->tif_rawdata, + tif->tif_rawdataloaded); + } + } + return (TIFFStartTile(tif, tile)); +} + +/* + * Setup the raw data buffer in preparation for + * reading a strip of raw data. If the buffer + * is specified as zero, then a buffer of appropriate + * size is allocated by the library. Otherwise, + * the client must guarantee that the buffer is + * large enough to hold any individual strip of + * raw data. + */ +int +TIFFReadBufferSetup(TIFF* tif, void* bp, tmsize_t size) +{ + static const char module[] = "TIFFReadBufferSetup"; + + assert((tif->tif_flags&TIFF_NOREADRAW)==0); + tif->tif_flags &= ~TIFF_BUFFERMMAP; + + if (tif->tif_rawdata) { + if (tif->tif_flags & TIFF_MYBUFFER) + _TIFFfree(tif->tif_rawdata); + tif->tif_rawdata = NULL; + tif->tif_rawdatasize = 0; + } + if (bp) { + tif->tif_rawdatasize = size; + tif->tif_rawdata = (uint8*) bp; + tif->tif_flags &= ~TIFF_MYBUFFER; + } else { + tif->tif_rawdatasize = (tmsize_t)TIFFroundup_64((uint64)size, 1024); + if (tif->tif_rawdatasize==0) { + TIFFErrorExt(tif->tif_clientdata, module, + "Invalid buffer size"); + return (0); + } + tif->tif_rawdata = (uint8*) _TIFFmalloc(tif->tif_rawdatasize); + tif->tif_flags |= TIFF_MYBUFFER; + } + if (tif->tif_rawdata == NULL) { + TIFFErrorExt(tif->tif_clientdata, module, + "No space for data buffer at scanline %lu", + (unsigned long) tif->tif_row); + tif->tif_rawdatasize = 0; + return (0); + } + return (1); +} + +/* + * Set state to appear as if a + * strip has just been read in. + */ +static int +TIFFStartStrip(TIFF* tif, uint32 strip) +{ + TIFFDirectory *td = &tif->tif_dir; + + if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount) + return 0; + + if ((tif->tif_flags & TIFF_CODERSETUP) == 0) { + if (!(*tif->tif_setupdecode)(tif)) + return (0); + tif->tif_flags |= TIFF_CODERSETUP; + } + tif->tif_curstrip = strip; + tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip; + tif->tif_flags &= ~TIFF_BUF4WRITE; + + if (tif->tif_flags&TIFF_NOREADRAW) + { + tif->tif_rawcp = NULL; + tif->tif_rawcc = 0; + } + else + { + tif->tif_rawcp = tif->tif_rawdata; + tif->tif_rawcc = (tmsize_t)td->td_stripbytecount[strip]; + } + return ((*tif->tif_predecode)(tif, + (uint16)(strip / td->td_stripsperimage))); +} + +/* + * Set state to appear as if a + * tile has just been read in. + */ +static int +TIFFStartTile(TIFF* tif, uint32 tile) +{ + TIFFDirectory *td = &tif->tif_dir; + + if (!_TIFFFillStriles( tif ) || !tif->tif_dir.td_stripbytecount) + return 0; + + if ((tif->tif_flags & TIFF_CODERSETUP) == 0) { + if (!(*tif->tif_setupdecode)(tif)) + return (0); + tif->tif_flags |= TIFF_CODERSETUP; + } + tif->tif_curtile = tile; + tif->tif_row = + (tile % TIFFhowmany_32(td->td_imagewidth, td->td_tilewidth)) * + td->td_tilelength; + tif->tif_col = + (tile % TIFFhowmany_32(td->td_imagelength, td->td_tilelength)) * + td->td_tilewidth; + tif->tif_flags &= ~TIFF_BUF4WRITE; + if (tif->tif_flags&TIFF_NOREADRAW) + { + tif->tif_rawcp = NULL; + tif->tif_rawcc = 0; + } + else + { + tif->tif_rawcp = tif->tif_rawdata; + tif->tif_rawcc = (tmsize_t)td->td_stripbytecount[tile]; + } + return ((*tif->tif_predecode)(tif, + (uint16)(tile/td->td_stripsperimage))); +} + +static int +TIFFCheckRead(TIFF* tif, int tiles) +{ + if (tif->tif_mode == O_WRONLY) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, "File not open for reading"); + return (0); + } + if (tiles ^ isTiled(tif)) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, tiles ? + "Can not read tiles from a stripped image" : + "Can not read scanlines from a tiled image"); + return (0); + } + return (1); +} + +void +_TIFFNoPostDecode(TIFF* tif, uint8* buf, tmsize_t cc) +{ + (void) tif; (void) buf; (void) cc; +} + +void +_TIFFSwab16BitData(TIFF* tif, uint8* buf, tmsize_t cc) +{ + (void) tif; + assert((cc & 1) == 0); + TIFFSwabArrayOfShort((uint16*) buf, cc/2); +} + +void +_TIFFSwab24BitData(TIFF* tif, uint8* buf, tmsize_t cc) +{ + (void) tif; + assert((cc % 3) == 0); + TIFFSwabArrayOfTriples((uint8*) buf, cc/3); +} + +void +_TIFFSwab32BitData(TIFF* tif, uint8* buf, tmsize_t cc) +{ + (void) tif; + assert((cc & 3) == 0); + TIFFSwabArrayOfLong((uint32*) buf, cc/4); +} + +void +_TIFFSwab64BitData(TIFF* tif, uint8* buf, tmsize_t cc) +{ + (void) tif; + assert((cc & 7) == 0); + TIFFSwabArrayOfDouble((double*) buf, cc/8); +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_strip.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_strip.c new file mode 100644 index 000000000..568e4898d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_strip.c @@ -0,0 +1,383 @@ +/* $Id: tif_strip.c,v 1.35 2012-06-06 05:33:55 fwarmerdam Exp $ */ + +/* + * Copyright (c) 1991-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Strip-organized Image Support Routines. + */ +#include "tiffiop.h" + +/* + * Compute which strip a (row,sample) value is in. + */ +uint32 +TIFFComputeStrip(TIFF* tif, uint32 row, uint16 sample) +{ + static const char module[] = "TIFFComputeStrip"; + TIFFDirectory *td = &tif->tif_dir; + uint32 strip; + + strip = row / td->td_rowsperstrip; + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { + if (sample >= td->td_samplesperpixel) { + TIFFErrorExt(tif->tif_clientdata, module, + "%lu: Sample out of range, max %lu", + (unsigned long) sample, (unsigned long) td->td_samplesperpixel); + return (0); + } + strip += (uint32)sample*td->td_stripsperimage; + } + return (strip); +} + +/* + * Compute how many strips are in an image. + */ +uint32 +TIFFNumberOfStrips(TIFF* tif) +{ + TIFFDirectory *td = &tif->tif_dir; + uint32 nstrips; + + nstrips = (td->td_rowsperstrip == (uint32) -1 ? 1 : + TIFFhowmany_32(td->td_imagelength, td->td_rowsperstrip)); + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) + nstrips = _TIFFMultiply32(tif, nstrips, (uint32)td->td_samplesperpixel, + "TIFFNumberOfStrips"); + return (nstrips); +} + +/* + * Compute the # bytes in a variable height, row-aligned strip. + */ +uint64 +TIFFVStripSize64(TIFF* tif, uint32 nrows) +{ + static const char module[] = "TIFFVStripSize64"; + TIFFDirectory *td = &tif->tif_dir; + if (nrows==(uint32)(-1)) + nrows=td->td_imagelength; + if ((td->td_planarconfig==PLANARCONFIG_CONTIG)&& + (td->td_photometric == PHOTOMETRIC_YCBCR)&& + (!isUpSampled(tif))) + { + /* + * Packed YCbCr data contain one Cb+Cr for every + * HorizontalSampling*VerticalSampling Y values. + * Must also roundup width and height when calculating + * since images that are not a multiple of the + * horizontal/vertical subsampling area include + * YCbCr data for the extended image. + */ + uint16 ycbcrsubsampling[2]; + uint16 samplingblock_samples; + uint32 samplingblocks_hor; + uint32 samplingblocks_ver; + uint64 samplingrow_samples; + uint64 samplingrow_size; + if(td->td_samplesperpixel!=3) + { + TIFFErrorExt(tif->tif_clientdata,module, + "Invalid td_samplesperpixel value"); + return 0; + } + TIFFGetFieldDefaulted(tif,TIFFTAG_YCBCRSUBSAMPLING,ycbcrsubsampling+0, + ycbcrsubsampling+1); + if ((ycbcrsubsampling[0] != 1 && ycbcrsubsampling[0] != 2 && ycbcrsubsampling[0] != 4) + ||(ycbcrsubsampling[1] != 1 && ycbcrsubsampling[1] != 2 && ycbcrsubsampling[1] != 4)) + { + TIFFErrorExt(tif->tif_clientdata,module, + "Invalid YCbCr subsampling (%dx%d)", + ycbcrsubsampling[0], + ycbcrsubsampling[1] ); + return 0; + } + samplingblock_samples=ycbcrsubsampling[0]*ycbcrsubsampling[1]+2; + samplingblocks_hor=TIFFhowmany_32(td->td_imagewidth,ycbcrsubsampling[0]); + samplingblocks_ver=TIFFhowmany_32(nrows,ycbcrsubsampling[1]); + samplingrow_samples=_TIFFMultiply64(tif,samplingblocks_hor,samplingblock_samples,module); + samplingrow_size=TIFFhowmany8_64(_TIFFMultiply64(tif,samplingrow_samples,td->td_bitspersample,module)); + return(_TIFFMultiply64(tif,samplingrow_size,samplingblocks_ver,module)); + } + else + return(_TIFFMultiply64(tif,nrows,TIFFScanlineSize64(tif),module)); +} +tmsize_t +TIFFVStripSize(TIFF* tif, uint32 nrows) +{ + static const char module[] = "TIFFVStripSize"; + uint64 m; + tmsize_t n; + m=TIFFVStripSize64(tif,nrows); + n=(tmsize_t)m; + if ((uint64)n!=m) + { + TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow"); + n=0; + } + return(n); +} + +/* + * Compute the # bytes in a raw strip. + */ +uint64 +TIFFRawStripSize64(TIFF* tif, uint32 strip) +{ + static const char module[] = "TIFFRawStripSize64"; + TIFFDirectory* td = &tif->tif_dir; + uint64 bytecount = td->td_stripbytecount[strip]; + + if (bytecount == 0) + { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "%I64u: Invalid strip byte count, strip %lu", + (unsigned __int64) bytecount, + (unsigned long) strip); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "%llu: Invalid strip byte count, strip %lu", + (unsigned long long) bytecount, + (unsigned long) strip); +#endif + bytecount = (uint64) -1; + } + + return bytecount; +} +tmsize_t +TIFFRawStripSize(TIFF* tif, uint32 strip) +{ + static const char module[] = "TIFFRawStripSize"; + uint64 m; + tmsize_t n; + m=TIFFRawStripSize64(tif,strip); + if (m==(uint64)(-1)) + n=(tmsize_t)(-1); + else + { + n=(tmsize_t)m; + if ((uint64)n!=m) + { + TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow"); + n=0; + } + } + return(n); +} + +/* + * Compute the # bytes in a (row-aligned) strip. + * + * Note that if RowsPerStrip is larger than the + * recorded ImageLength, then the strip size is + * truncated to reflect the actual space required + * to hold the strip. + */ +uint64 +TIFFStripSize64(TIFF* tif) +{ + TIFFDirectory* td = &tif->tif_dir; + uint32 rps = td->td_rowsperstrip; + if (rps > td->td_imagelength) + rps = td->td_imagelength; + return (TIFFVStripSize64(tif, rps)); +} +tmsize_t +TIFFStripSize(TIFF* tif) +{ + static const char module[] = "TIFFStripSize"; + uint64 m; + tmsize_t n; + m=TIFFStripSize64(tif); + n=(tmsize_t)m; + if ((uint64)n!=m) + { + TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow"); + n=0; + } + return(n); +} + +/* + * Compute a default strip size based on the image + * characteristics and a requested value. If the + * request is <1 then we choose a strip size according + * to certain heuristics. + */ +uint32 +TIFFDefaultStripSize(TIFF* tif, uint32 request) +{ + return (*tif->tif_defstripsize)(tif, request); +} + +uint32 +_TIFFDefaultStripSize(TIFF* tif, uint32 s) +{ + if ((int32) s < 1) { + /* + * If RowsPerStrip is unspecified, try to break the + * image up into strips that are approximately + * STRIP_SIZE_DEFAULT bytes long. + */ + uint64 scanlinesize; + uint64 rows; + scanlinesize=TIFFScanlineSize64(tif); + if (scanlinesize==0) + scanlinesize=1; + rows=(uint64)STRIP_SIZE_DEFAULT/scanlinesize; + if (rows==0) + rows=1; + else if (rows>0xFFFFFFFF) + rows=0xFFFFFFFF; + s=(uint32)rows; + } + return (s); +} + +/* + * Return the number of bytes to read/write in a call to + * one of the scanline-oriented i/o routines. Note that + * this number may be 1/samples-per-pixel if data is + * stored as separate planes. + * The ScanlineSize in case of YCbCrSubsampling is defined as the + * strip size divided by the strip height, i.e. the size of a pack of vertical + * subsampling lines divided by vertical subsampling. It should thus make + * sense when multiplied by a multiple of vertical subsampling. + */ +uint64 +TIFFScanlineSize64(TIFF* tif) +{ + static const char module[] = "TIFFScanlineSize64"; + TIFFDirectory *td = &tif->tif_dir; + uint64 scanline_size; + if (td->td_planarconfig==PLANARCONFIG_CONTIG) + { + if ((td->td_photometric==PHOTOMETRIC_YCBCR)&& + (td->td_samplesperpixel==3)&& + (!isUpSampled(tif))) + { + uint16 ycbcrsubsampling[2]; + uint16 samplingblock_samples; + uint32 samplingblocks_hor; + uint64 samplingrow_samples; + uint64 samplingrow_size; + if(td->td_samplesperpixel!=3) + { + TIFFErrorExt(tif->tif_clientdata,module, + "Invalid td_samplesperpixel value"); + return 0; + } + TIFFGetFieldDefaulted(tif,TIFFTAG_YCBCRSUBSAMPLING, + ycbcrsubsampling+0, + ycbcrsubsampling+1); + if (((ycbcrsubsampling[0]!=1)&&(ycbcrsubsampling[0]!=2)&&(ycbcrsubsampling[0]!=4)) || + ((ycbcrsubsampling[1]!=1)&&(ycbcrsubsampling[1]!=2)&&(ycbcrsubsampling[1]!=4))) + { + TIFFErrorExt(tif->tif_clientdata,module, + "Invalid YCbCr subsampling"); + return 0; + } + samplingblock_samples = ycbcrsubsampling[0]*ycbcrsubsampling[1]+2; + samplingblocks_hor = TIFFhowmany_32(td->td_imagewidth,ycbcrsubsampling[0]); + samplingrow_samples = _TIFFMultiply64(tif,samplingblocks_hor,samplingblock_samples,module); + samplingrow_size = TIFFhowmany_64(_TIFFMultiply64(tif,samplingrow_samples,td->td_bitspersample,module),8); + scanline_size = (samplingrow_size/ycbcrsubsampling[1]); + } + else + { + uint64 scanline_samples; + scanline_samples=_TIFFMultiply64(tif,td->td_imagewidth,td->td_samplesperpixel,module); + scanline_size=TIFFhowmany_64(_TIFFMultiply64(tif,scanline_samples,td->td_bitspersample,module),8); + } + } + else + scanline_size=TIFFhowmany_64(_TIFFMultiply64(tif,td->td_imagewidth,td->td_bitspersample,module),8); + return(scanline_size); +} +tmsize_t +TIFFScanlineSize(TIFF* tif) +{ + static const char module[] = "TIFFScanlineSize"; + uint64 m; + tmsize_t n; + m=TIFFScanlineSize64(tif); + n=(tmsize_t)m; + if ((uint64)n!=m) + { + TIFFErrorExt(tif->tif_clientdata,module,"Integer arithmetic overflow"); + n=0; + } + return(n); +} + +/* + * Return the number of bytes required to store a complete + * decoded and packed raster scanline (as opposed to the + * I/O size returned by TIFFScanlineSize which may be less + * if data is store as separate planes). + */ +uint64 +TIFFRasterScanlineSize64(TIFF* tif) +{ + static const char module[] = "TIFFRasterScanlineSize64"; + TIFFDirectory *td = &tif->tif_dir; + uint64 scanline; + + scanline = _TIFFMultiply64(tif, td->td_bitspersample, td->td_imagewidth, module); + if (td->td_planarconfig == PLANARCONFIG_CONTIG) { + scanline = _TIFFMultiply64(tif, scanline, td->td_samplesperpixel, module); + return (TIFFhowmany8_64(scanline)); + } else + return (_TIFFMultiply64(tif, TIFFhowmany8_64(scanline), + td->td_samplesperpixel, module)); +} +tmsize_t +TIFFRasterScanlineSize(TIFF* tif) +{ + static const char module[] = "TIFFRasterScanlineSize"; + uint64 m; + tmsize_t n; + m=TIFFRasterScanlineSize64(tif); + n=(tmsize_t)m; + if ((uint64)n!=m) + { + TIFFErrorExt(tif->tif_clientdata,module,"Integer arithmetic overflow"); + n=0; + } + return(n); +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_swab.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_swab.c new file mode 100644 index 000000000..dcc8a9eae --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_swab.c @@ -0,0 +1,288 @@ +/* $Id: tif_swab.c,v 1.13 2010-03-10 18:56:49 bfriesen Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library Bit & Byte Swapping Support. + * + * XXX We assume short = 16-bits and long = 32-bits XXX + */ +#include "tiffiop.h" + +void +TIFFSwabShort(uint16* wp) +{ + register unsigned char* cp = (unsigned char*) wp; + unsigned char t; + assert(sizeof(uint16)==2); + t = cp[1]; cp[1] = cp[0]; cp[0] = t; +} + +void +TIFFSwabLong(uint32* lp) +{ + register unsigned char* cp = (unsigned char*) lp; + unsigned char t; + assert(sizeof(uint32)==4); + t = cp[3]; cp[3] = cp[0]; cp[0] = t; + t = cp[2]; cp[2] = cp[1]; cp[1] = t; +} + +void +TIFFSwabLong8(uint64* lp) +{ + register unsigned char* cp = (unsigned char*) lp; + unsigned char t; + assert(sizeof(uint64)==8); + t = cp[7]; cp[7] = cp[0]; cp[0] = t; + t = cp[6]; cp[6] = cp[1]; cp[1] = t; + t = cp[5]; cp[5] = cp[2]; cp[2] = t; + t = cp[4]; cp[4] = cp[3]; cp[3] = t; +} + +void +TIFFSwabArrayOfShort(register uint16* wp, tmsize_t n) +{ + register unsigned char* cp; + register unsigned char t; + assert(sizeof(uint16)==2); + /* XXX unroll loop some */ + while (n-- > 0) { + cp = (unsigned char*) wp; + t = cp[1]; cp[1] = cp[0]; cp[0] = t; + wp++; + } +} + +void +TIFFSwabArrayOfTriples(register uint8* tp, tmsize_t n) +{ + unsigned char* cp; + unsigned char t; + + /* XXX unroll loop some */ + while (n-- > 0) { + cp = (unsigned char*) tp; + t = cp[2]; cp[2] = cp[0]; cp[0] = t; + tp += 3; + } +} + +void +TIFFSwabArrayOfLong(register uint32* lp, tmsize_t n) +{ + register unsigned char *cp; + register unsigned char t; + assert(sizeof(uint32)==4); + /* XXX unroll loop some */ + while (n-- > 0) { + cp = (unsigned char *)lp; + t = cp[3]; cp[3] = cp[0]; cp[0] = t; + t = cp[2]; cp[2] = cp[1]; cp[1] = t; + lp++; + } +} + +void +TIFFSwabArrayOfLong8(register uint64* lp, tmsize_t n) +{ + register unsigned char *cp; + register unsigned char t; + assert(sizeof(uint64)==8); + /* XXX unroll loop some */ + while (n-- > 0) { + cp = (unsigned char *)lp; + t = cp[7]; cp[7] = cp[0]; cp[0] = t; + t = cp[6]; cp[6] = cp[1]; cp[1] = t; + t = cp[5]; cp[5] = cp[2]; cp[2] = t; + t = cp[4]; cp[4] = cp[3]; cp[3] = t; + lp++; + } +} + +void +TIFFSwabFloat(float* fp) +{ + register unsigned char* cp = (unsigned char*) fp; + unsigned char t; + assert(sizeof(float)==4); + t = cp[3]; cp[3] = cp[0]; cp[0] = t; + t = cp[2]; cp[2] = cp[1]; cp[1] = t; +} + +void +TIFFSwabArrayOfFloat(register float* fp, tmsize_t n) +{ + register unsigned char *cp; + register unsigned char t; + assert(sizeof(float)==4); + /* XXX unroll loop some */ + while (n-- > 0) { + cp = (unsigned char *)fp; + t = cp[3]; cp[3] = cp[0]; cp[0] = t; + t = cp[2]; cp[2] = cp[1]; cp[1] = t; + fp++; + } +} + +void +TIFFSwabDouble(double *dp) +{ + register unsigned char* cp = (unsigned char*) dp; + unsigned char t; + assert(sizeof(double)==8); + t = cp[7]; cp[7] = cp[0]; cp[0] = t; + t = cp[6]; cp[6] = cp[1]; cp[1] = t; + t = cp[5]; cp[5] = cp[2]; cp[2] = t; + t = cp[4]; cp[4] = cp[3]; cp[3] = t; +} + +void +TIFFSwabArrayOfDouble(double* dp, tmsize_t n) +{ + register unsigned char *cp; + register unsigned char t; + assert(sizeof(double)==8); + /* XXX unroll loop some */ + while (n-- > 0) { + cp = (unsigned char *)dp; + t = cp[7]; cp[7] = cp[0]; cp[0] = t; + t = cp[6]; cp[6] = cp[1]; cp[1] = t; + t = cp[5]; cp[5] = cp[2]; cp[2] = t; + t = cp[4]; cp[4] = cp[3]; cp[3] = t; + dp++; + } +} + +/* + * Bit reversal tables. TIFFBitRevTable[] gives + * the bit reversed value of . Used in various + * places in the library when the FillOrder requires + * bit reversal of byte values (e.g. CCITT Fax 3 + * encoding/decoding). TIFFNoBitRevTable is provided + * for algorithms that want an equivalent table that + * do not reverse bit values. + */ +static const unsigned char TIFFBitRevTable[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; +static const unsigned char TIFFNoBitRevTable[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, +}; + +const unsigned char* +TIFFGetBitRevTable(int reversed) +{ + return (reversed ? TIFFBitRevTable : TIFFNoBitRevTable); +} + +void +TIFFReverseBits(uint8* cp, tmsize_t n) +{ + for (; n > 8; n -= 8) { + cp[0] = TIFFBitRevTable[cp[0]]; + cp[1] = TIFFBitRevTable[cp[1]]; + cp[2] = TIFFBitRevTable[cp[2]]; + cp[3] = TIFFBitRevTable[cp[3]]; + cp[4] = TIFFBitRevTable[cp[4]]; + cp[5] = TIFFBitRevTable[cp[5]]; + cp[6] = TIFFBitRevTable[cp[6]]; + cp[7] = TIFFBitRevTable[cp[7]]; + cp += 8; + } + while (n-- > 0) + *cp = TIFFBitRevTable[*cp], cp++; +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_thunder.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_thunder.c new file mode 100644 index 000000000..390891c98 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_thunder.c @@ -0,0 +1,207 @@ +/* $Id: tif_thunder.c,v 1.12 2011-04-02 20:54:09 bfriesen Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "tiffiop.h" +#include +#ifdef THUNDER_SUPPORT +/* + * TIFF Library. + * + * ThunderScan 4-bit Compression Algorithm Support + */ + +/* + * ThunderScan uses an encoding scheme designed for + * 4-bit pixel values. Data is encoded in bytes, with + * each byte split into a 2-bit code word and a 6-bit + * data value. The encoding gives raw data, runs of + * pixels, or pixel values encoded as a delta from the + * previous pixel value. For the latter, either 2-bit + * or 3-bit delta values are used, with the deltas packed + * into a single byte. + */ +#define THUNDER_DATA 0x3f /* mask for 6-bit data */ +#define THUNDER_CODE 0xc0 /* mask for 2-bit code word */ +/* code values */ +#define THUNDER_RUN 0x00 /* run of pixels w/ encoded count */ +#define THUNDER_2BITDELTAS 0x40 /* 3 pixels w/ encoded 2-bit deltas */ +#define DELTA2_SKIP 2 /* skip code for 2-bit deltas */ +#define THUNDER_3BITDELTAS 0x80 /* 2 pixels w/ encoded 3-bit deltas */ +#define DELTA3_SKIP 4 /* skip code for 3-bit deltas */ +#define THUNDER_RAW 0xc0 /* raw data encoded */ + +static const int twobitdeltas[4] = { 0, 1, 0, -1 }; +static const int threebitdeltas[8] = { 0, 1, 2, 3, 0, -3, -2, -1 }; + +#define SETPIXEL(op, v) { \ + lastpixel = (v) & 0xf; \ + if ( npixels < maxpixels ) \ + { \ + if (npixels++ & 1) \ + *op++ |= lastpixel; \ + else \ + op[0] = (uint8) (lastpixel << 4); \ + } \ +} + +static int +ThunderSetupDecode(TIFF* tif) +{ + static const char module[] = "ThunderSetupDecode"; + + if( tif->tif_dir.td_bitspersample != 4 ) + { + TIFFErrorExt(tif->tif_clientdata, module, + "Wrong bitspersample value (%d), Thunder decoder only supports 4bits per sample.", + (int) tif->tif_dir.td_bitspersample ); + return 0; + } + + + return (1); +} + +static int +ThunderDecode(TIFF* tif, uint8* op, tmsize_t maxpixels) +{ + static const char module[] = "ThunderDecode"; + register unsigned char *bp; + register tmsize_t cc; + unsigned int lastpixel; + tmsize_t npixels; + + bp = (unsigned char *)tif->tif_rawcp; + cc = tif->tif_rawcc; + lastpixel = 0; + npixels = 0; + while (cc > 0 && npixels < maxpixels) { + int n, delta; + + n = *bp++, cc--; + switch (n & THUNDER_CODE) { + case THUNDER_RUN: /* pixel run */ + /* + * Replicate the last pixel n times, + * where n is the lower-order 6 bits. + */ + if (npixels & 1) { + op[0] |= lastpixel; + lastpixel = *op++; npixels++; n--; + } else + lastpixel |= lastpixel << 4; + npixels += n; + if (npixels < maxpixels) { + for (; n > 0; n -= 2) + *op++ = (uint8) lastpixel; + } + if (n == -1) + *--op &= 0xf0; + lastpixel &= 0xf; + break; + case THUNDER_2BITDELTAS: /* 2-bit deltas */ + if ((delta = ((n >> 4) & 3)) != DELTA2_SKIP) + SETPIXEL(op, lastpixel + twobitdeltas[delta]); + if ((delta = ((n >> 2) & 3)) != DELTA2_SKIP) + SETPIXEL(op, lastpixel + twobitdeltas[delta]); + if ((delta = (n & 3)) != DELTA2_SKIP) + SETPIXEL(op, lastpixel + twobitdeltas[delta]); + break; + case THUNDER_3BITDELTAS: /* 3-bit deltas */ + if ((delta = ((n >> 3) & 7)) != DELTA3_SKIP) + SETPIXEL(op, lastpixel + threebitdeltas[delta]); + if ((delta = (n & 7)) != DELTA3_SKIP) + SETPIXEL(op, lastpixel + threebitdeltas[delta]); + break; + case THUNDER_RAW: /* raw data */ + SETPIXEL(op, n); + break; + } + } + tif->tif_rawcp = (uint8*) bp; + tif->tif_rawcc = cc; + if (npixels != maxpixels) { +#if defined(__WIN32__) && (defined(_MSC_VER) || defined(__MINGW32__)) + TIFFErrorExt(tif->tif_clientdata, module, + "%s data at scanline %lu (%I64u != %I64u)", + npixels < maxpixels ? "Not enough" : "Too much", + (unsigned long) tif->tif_row, + (unsigned __int64) npixels, + (unsigned __int64) maxpixels); +#else + TIFFErrorExt(tif->tif_clientdata, module, + "%s data at scanline %lu (%llu != %llu)", + npixels < maxpixels ? "Not enough" : "Too much", + (unsigned long) tif->tif_row, + (unsigned long long) npixels, + (unsigned long long) maxpixels); +#endif + return (0); + } + + return (1); +} + +static int +ThunderDecodeRow(TIFF* tif, uint8* buf, tmsize_t occ, uint16 s) +{ + static const char module[] = "ThunderDecodeRow"; + uint8* row = buf; + + (void) s; + if (occ % tif->tif_scanlinesize) + { + TIFFErrorExt(tif->tif_clientdata, module, "Fractional scanlines cannot be read"); + return (0); + } + while (occ > 0) { + if (!ThunderDecode(tif, row, tif->tif_dir.td_imagewidth)) + return (0); + occ -= tif->tif_scanlinesize; + row += tif->tif_scanlinesize; + } + return (1); +} + +int +TIFFInitThunderScan(TIFF* tif, int scheme) +{ + (void) scheme; + + tif->tif_setupdecode = ThunderSetupDecode; + tif->tif_decoderow = ThunderDecodeRow; + tif->tif_decodestrip = ThunderDecodeRow; + return (1); +} +#endif /* THUNDER_SUPPORT */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_tile.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_tile.c new file mode 100644 index 000000000..0ff7e8535 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_tile.c @@ -0,0 +1,299 @@ +/* $Id: tif_tile.c,v 1.23 2012-06-06 05:33:55 fwarmerdam Exp $ */ + +/* + * Copyright (c) 1991-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Tiled Image Support Routines. + */ +#include "tiffiop.h" + +/* + * Compute which tile an (x,y,z,s) value is in. + */ +uint32 +TIFFComputeTile(TIFF* tif, uint32 x, uint32 y, uint32 z, uint16 s) +{ + TIFFDirectory *td = &tif->tif_dir; + uint32 dx = td->td_tilewidth; + uint32 dy = td->td_tilelength; + uint32 dz = td->td_tiledepth; + uint32 tile = 1; + + if (td->td_imagedepth == 1) + z = 0; + if (dx == (uint32) -1) + dx = td->td_imagewidth; + if (dy == (uint32) -1) + dy = td->td_imagelength; + if (dz == (uint32) -1) + dz = td->td_imagedepth; + if (dx != 0 && dy != 0 && dz != 0) { + uint32 xpt = TIFFhowmany_32(td->td_imagewidth, dx); + uint32 ypt = TIFFhowmany_32(td->td_imagelength, dy); + uint32 zpt = TIFFhowmany_32(td->td_imagedepth, dz); + + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) + tile = (xpt*ypt*zpt)*s + + (xpt*ypt)*(z/dz) + + xpt*(y/dy) + + x/dx; + else + tile = (xpt*ypt)*(z/dz) + xpt*(y/dy) + x/dx; + } + return (tile); +} + +/* + * Check an (x,y,z,s) coordinate + * against the image bounds. + */ +int +TIFFCheckTile(TIFF* tif, uint32 x, uint32 y, uint32 z, uint16 s) +{ + TIFFDirectory *td = &tif->tif_dir; + + if (x >= td->td_imagewidth) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "%lu: Col out of range, max %lu", + (unsigned long) x, + (unsigned long) (td->td_imagewidth - 1)); + return (0); + } + if (y >= td->td_imagelength) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "%lu: Row out of range, max %lu", + (unsigned long) y, + (unsigned long) (td->td_imagelength - 1)); + return (0); + } + if (z >= td->td_imagedepth) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "%lu: Depth out of range, max %lu", + (unsigned long) z, + (unsigned long) (td->td_imagedepth - 1)); + return (0); + } + if (td->td_planarconfig == PLANARCONFIG_SEPARATE && + s >= td->td_samplesperpixel) { + TIFFErrorExt(tif->tif_clientdata, tif->tif_name, + "%lu: Sample out of range, max %lu", + (unsigned long) s, + (unsigned long) (td->td_samplesperpixel - 1)); + return (0); + } + return (1); +} + +/* + * Compute how many tiles are in an image. + */ +uint32 +TIFFNumberOfTiles(TIFF* tif) +{ + TIFFDirectory *td = &tif->tif_dir; + uint32 dx = td->td_tilewidth; + uint32 dy = td->td_tilelength; + uint32 dz = td->td_tiledepth; + uint32 ntiles; + + if (dx == (uint32) -1) + dx = td->td_imagewidth; + if (dy == (uint32) -1) + dy = td->td_imagelength; + if (dz == (uint32) -1) + dz = td->td_imagedepth; + ntiles = (dx == 0 || dy == 0 || dz == 0) ? 0 : + _TIFFMultiply32(tif, _TIFFMultiply32(tif, TIFFhowmany_32(td->td_imagewidth, dx), + TIFFhowmany_32(td->td_imagelength, dy), + "TIFFNumberOfTiles"), + TIFFhowmany_32(td->td_imagedepth, dz), "TIFFNumberOfTiles"); + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) + ntiles = _TIFFMultiply32(tif, ntiles, td->td_samplesperpixel, + "TIFFNumberOfTiles"); + return (ntiles); +} + +/* + * Compute the # bytes in each row of a tile. + */ +uint64 +TIFFTileRowSize64(TIFF* tif) +{ + TIFFDirectory *td = &tif->tif_dir; + uint64 rowsize; + + if (td->td_tilelength == 0 || td->td_tilewidth == 0) + return (0); + rowsize = _TIFFMultiply64(tif, td->td_bitspersample, td->td_tilewidth, + "TIFFTileRowSize"); + if (td->td_planarconfig == PLANARCONFIG_CONTIG) + rowsize = _TIFFMultiply64(tif, rowsize, td->td_samplesperpixel, + "TIFFTileRowSize"); + return (TIFFhowmany8_64(rowsize)); +} +tmsize_t +TIFFTileRowSize(TIFF* tif) +{ + static const char module[] = "TIFFTileRowSize"; + uint64 m; + tmsize_t n; + m=TIFFTileRowSize64(tif); + n=(tmsize_t)m; + if ((uint64)n!=m) + { + TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow"); + n=0; + } + return(n); +} + +/* + * Compute the # bytes in a variable length, row-aligned tile. + */ +uint64 +TIFFVTileSize64(TIFF* tif, uint32 nrows) +{ + static const char module[] = "TIFFVTileSize64"; + TIFFDirectory *td = &tif->tif_dir; + if (td->td_tilelength == 0 || td->td_tilewidth == 0 || + td->td_tiledepth == 0) + return (0); + if ((td->td_planarconfig==PLANARCONFIG_CONTIG)&& + (td->td_photometric==PHOTOMETRIC_YCBCR)&& + (td->td_samplesperpixel==3)&& + (!isUpSampled(tif))) + { + /* + * Packed YCbCr data contain one Cb+Cr for every + * HorizontalSampling*VerticalSampling Y values. + * Must also roundup width and height when calculating + * since images that are not a multiple of the + * horizontal/vertical subsampling area include + * YCbCr data for the extended image. + */ + uint16 ycbcrsubsampling[2]; + uint16 samplingblock_samples; + uint32 samplingblocks_hor; + uint32 samplingblocks_ver; + uint64 samplingrow_samples; + uint64 samplingrow_size; + TIFFGetFieldDefaulted(tif,TIFFTAG_YCBCRSUBSAMPLING,ycbcrsubsampling+0, + ycbcrsubsampling+1); + if ((ycbcrsubsampling[0] != 1 && ycbcrsubsampling[0] != 2 && ycbcrsubsampling[0] != 4) + ||(ycbcrsubsampling[1] != 1 && ycbcrsubsampling[1] != 2 && ycbcrsubsampling[1] != 4)) + { + TIFFErrorExt(tif->tif_clientdata,module, + "Invalid YCbCr subsampling (%dx%d)", + ycbcrsubsampling[0], + ycbcrsubsampling[1] ); + return 0; + } + samplingblock_samples=ycbcrsubsampling[0]*ycbcrsubsampling[1]+2; + samplingblocks_hor=TIFFhowmany_32(td->td_tilewidth,ycbcrsubsampling[0]); + samplingblocks_ver=TIFFhowmany_32(nrows,ycbcrsubsampling[1]); + samplingrow_samples=_TIFFMultiply64(tif,samplingblocks_hor,samplingblock_samples,module); + samplingrow_size=TIFFhowmany8_64(_TIFFMultiply64(tif,samplingrow_samples,td->td_bitspersample,module)); + return(_TIFFMultiply64(tif,samplingrow_size,samplingblocks_ver,module)); + } + else + return(_TIFFMultiply64(tif,nrows,TIFFTileRowSize64(tif),module)); +} +tmsize_t +TIFFVTileSize(TIFF* tif, uint32 nrows) +{ + static const char module[] = "TIFFVTileSize"; + uint64 m; + tmsize_t n; + m=TIFFVTileSize64(tif,nrows); + n=(tmsize_t)m; + if ((uint64)n!=m) + { + TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow"); + n=0; + } + return(n); +} + +/* + * Compute the # bytes in a row-aligned tile. + */ +uint64 +TIFFTileSize64(TIFF* tif) +{ + return (TIFFVTileSize64(tif, tif->tif_dir.td_tilelength)); +} +tmsize_t +TIFFTileSize(TIFF* tif) +{ + static const char module[] = "TIFFTileSize"; + uint64 m; + tmsize_t n; + m=TIFFTileSize64(tif); + n=(tmsize_t)m; + if ((uint64)n!=m) + { + TIFFErrorExt(tif->tif_clientdata,module,"Integer overflow"); + n=0; + } + return(n); +} + +/* + * Compute a default tile size based on the image + * characteristics and a requested value. If a + * request is <1 then we choose a size according + * to certain heuristics. + */ +void +TIFFDefaultTileSize(TIFF* tif, uint32* tw, uint32* th) +{ + (*tif->tif_deftilesize)(tif, tw, th); +} + +void +_TIFFDefaultTileSize(TIFF* tif, uint32* tw, uint32* th) +{ + (void) tif; + if (*(int32*) tw < 1) + *tw = 256; + if (*(int32*) th < 1) + *th = 256; + /* roundup to a multiple of 16 per the spec */ + if (*tw & 0xf) + *tw = TIFFroundup_32(*tw, 16); + if (*th & 0xf) + *th = TIFFroundup_32(*th, 16); +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_version.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_version.c new file mode 100644 index 000000000..f92c843d8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_version.c @@ -0,0 +1,40 @@ +/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_version.c,v 1.3 2010-03-10 18:56:49 bfriesen Exp $ */ +/* + * Copyright (c) 1992-1997 Sam Leffler + * Copyright (c) 1992-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ +#include "tiffiop.h" + +static const char TIFFVersion[] = TIFFLIB_VERSION_STR; + +const char* +TIFFGetVersion(void) +{ + return (TIFFVersion); +} +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_vsi.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_vsi.c new file mode 100644 index 000000000..459502e0c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_vsi.c @@ -0,0 +1,208 @@ +/****************************************************************************** + * $Id: tif_vsi.c 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: GeoTIFF Driver + * Purpose: Implement system hook functions for libtiff on top of CPL/VSI, + * including > 2GB support. Based on tif_unix.c from libtiff + * distribution. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam, warmerdam@pobox.com + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* + * TIFF Library UNIX-specific Routines. + */ +#include "tiffiop.h" +#include "cpl_vsi.h" + +static tsize_t +_tiffReadProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return VSIFReadL( buf, 1, size, (VSILFILE *) fd ); +} + +static tsize_t +_tiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size) +{ + return VSIFWriteL( buf, 1, size, (VSILFILE *) fd ); +} + +static toff_t +_tiffSeekProc(thandle_t fd, toff_t off, int whence) +{ + if( VSIFSeekL( (VSILFILE *) fd, off, whence ) == 0 ) + return (toff_t) VSIFTellL( (VSILFILE *) fd ); + else + return (toff_t) -1; +} + +static int +_tiffCloseProc(thandle_t fd) +{ + return VSIFCloseL( (VSILFILE *) fd ); +} + +static toff_t +_tiffSizeProc(thandle_t fd) +{ + vsi_l_offset old_off; + toff_t file_size; + + old_off = VSIFTellL( (VSILFILE *) fd ); + VSIFSeekL( (VSILFILE *) fd, 0, SEEK_END ); + + file_size = (toff_t) VSIFTellL( (VSILFILE *) fd ); + VSIFSeekL( (VSILFILE *) fd, old_off, SEEK_SET ); + + return file_size; +} + +static int +_tiffMapProc(thandle_t fd, tdata_t* pbase, toff_t* psize) +{ + (void) fd; (void) pbase; (void) psize; + return (0); +} + +static void +_tiffUnmapProc(thandle_t fd, tdata_t base, toff_t size) +{ + (void) fd; (void) base; (void) size; +} + +/* + * Open a TIFF file descriptor for read/writing. + */ +TIFF* +TIFFFdOpen(CPL_UNUSED int fd, CPL_UNUSED const char* name, CPL_UNUSED const char* mode) +{ + return NULL; +} + +/* + * Open a TIFF file for read/writing. + */ +TIFF* +TIFFOpen(const char* name, const char* mode) +{ + static const char module[] = "TIFFOpen"; + int i, a_out; + char access[32]; + VSILFILE *fp; + TIFF *tif; + + a_out = 0; + access[0] = '\0'; + for( i = 0; mode[i] != '\0'; i++ ) + { + if( mode[i] == 'r' + || mode[i] == 'w' + || mode[i] == '+' + || mode[i] == 'a' ) + { + access[a_out++] = mode[i]; + access[a_out] = '\0'; + } + } + + strcat( access, "b" ); + + fp = VSIFOpenL( name, access ); + if (fp == NULL) { + if( errno >= 0 ) + TIFFError(module,"%s: %s", name, VSIStrerror( errno ) ); + else + TIFFError(module, "%s: Cannot open", name); + return ((TIFF *)0); + } + + tif = TIFFClientOpen(name, mode, + (thandle_t) fp, + _tiffReadProc, _tiffWriteProc, + _tiffSeekProc, _tiffCloseProc, _tiffSizeProc, + _tiffMapProc, _tiffUnmapProc); + + if( tif != NULL ) + tif->tif_fd = 0; + else + VSIFCloseL( fp ); + + return tif; +} + +void* +_TIFFmalloc(tsize_t s) +{ + return VSIMalloc((size_t) s); +} + +void +_TIFFfree(tdata_t p) +{ + VSIFree( p ); +} + +void* +_TIFFrealloc(tdata_t p, tsize_t s) +{ + return VSIRealloc( p, s ); +} + +void +_TIFFmemset(void* p, int v, tmsize_t c) +{ + memset(p, v, (size_t) c); +} + +void +_TIFFmemcpy(void* d, const void* s, tmsize_t c) +{ + memcpy(d, s, (size_t) c); +} + +int +_TIFFmemcmp(const void* p1, const void* p2, tmsize_t c) +{ + return (memcmp(p1, p2, (size_t) c)); +} + +static void +unixWarningHandler(const char* module, const char* fmt, va_list ap) +{ + if (module != NULL) + fprintf(stderr, "%s: ", module); + fprintf(stderr, "Warning, "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, ".\n"); +} +TIFFErrorHandler _TIFFwarningHandler = unixWarningHandler; + +static void +unixErrorHandler(const char* module, const char* fmt, va_list ap) +{ + if (module != NULL) + fprintf(stderr, "%s: ", module); + vfprintf(stderr, fmt, ap); + fprintf(stderr, ".\n"); +} +TIFFErrorHandler _TIFFerrorHandler = unixErrorHandler; diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_warning.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_warning.c new file mode 100644 index 000000000..423b636e6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_warning.c @@ -0,0 +1,81 @@ +/* $Header: /cvs/maptools/cvsroot/libtiff/libtiff/tif_warning.c,v 1.3 2010-03-10 18:56:49 bfriesen Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + */ +#include "tiffiop.h" + +TIFFErrorHandlerExt _TIFFwarningHandlerExt = NULL; + +TIFFErrorHandler +TIFFSetWarningHandler(TIFFErrorHandler handler) +{ + TIFFErrorHandler prev = _TIFFwarningHandler; + _TIFFwarningHandler = handler; + return (prev); +} + +TIFFErrorHandlerExt +TIFFSetWarningHandlerExt(TIFFErrorHandlerExt handler) +{ + TIFFErrorHandlerExt prev = _TIFFwarningHandlerExt; + _TIFFwarningHandlerExt = handler; + return (prev); +} + +void +TIFFWarning(const char* module, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (_TIFFwarningHandler) + (*_TIFFwarningHandler)(module, fmt, ap); + if (_TIFFwarningHandlerExt) + (*_TIFFwarningHandlerExt)(0, module, fmt, ap); + va_end(ap); +} + +void +TIFFWarningExt(thandle_t fd, const char* module, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (_TIFFwarningHandler) + (*_TIFFwarningHandler)(module, fmt, ap); + if (_TIFFwarningHandlerExt) + (*_TIFFwarningHandlerExt)(fd, module, fmt, ap); + va_end(ap); +} + + +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_write.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_write.c new file mode 100644 index 000000000..cb610fcbb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_write.c @@ -0,0 +1,771 @@ +/* $Id: tif_write.c,v 1.38 2013-01-18 21:57:12 fwarmerdam Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * TIFF Library. + * + * Scanline-oriented Write Support + */ +#include "tiffiop.h" +#include + +#define STRIPINCR 20 /* expansion factor on strip array */ + +#define WRITECHECKSTRIPS(tif, module) \ + (((tif)->tif_flags&TIFF_BEENWRITING) || TIFFWriteCheck((tif),0,module)) +#define WRITECHECKTILES(tif, module) \ + (((tif)->tif_flags&TIFF_BEENWRITING) || TIFFWriteCheck((tif),1,module)) +#define BUFFERCHECK(tif) \ + ((((tif)->tif_flags & TIFF_BUFFERSETUP) && tif->tif_rawdata) || \ + TIFFWriteBufferSetup((tif), NULL, (tmsize_t) -1)) + +static int TIFFGrowStrips(TIFF* tif, uint32 delta, const char* module); +static int TIFFAppendToStrip(TIFF* tif, uint32 strip, uint8* data, tmsize_t cc); + +int +TIFFWriteScanline(TIFF* tif, void* buf, uint32 row, uint16 sample) +{ + static const char module[] = "TIFFWriteScanline"; + register TIFFDirectory *td; + int status, imagegrew = 0; + uint32 strip; + + if (!WRITECHECKSTRIPS(tif, module)) + return (-1); + /* + * Handle delayed allocation of data buffer. This + * permits it to be sized more intelligently (using + * directory information). + */ + if (!BUFFERCHECK(tif)) + return (-1); + tif->tif_flags |= TIFF_BUF4WRITE; /* not strictly sure this is right*/ + + td = &tif->tif_dir; + /* + * Extend image length if needed + * (but only for PlanarConfig=1). + */ + if (row >= td->td_imagelength) { /* extend image */ + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { + TIFFErrorExt(tif->tif_clientdata, module, + "Can not change \"ImageLength\" when using separate planes"); + return (-1); + } + td->td_imagelength = row+1; + imagegrew = 1; + } + /* + * Calculate strip and check for crossings. + */ + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { + if (sample >= td->td_samplesperpixel) { + TIFFErrorExt(tif->tif_clientdata, module, + "%lu: Sample out of range, max %lu", + (unsigned long) sample, (unsigned long) td->td_samplesperpixel); + return (-1); + } + strip = sample*td->td_stripsperimage + row/td->td_rowsperstrip; + } else + strip = row / td->td_rowsperstrip; + /* + * Check strip array to make sure there's space. We don't support + * dynamically growing files that have data organized in separate + * bitplanes because it's too painful. In that case we require that + * the imagelength be set properly before the first write (so that the + * strips array will be fully allocated above). + */ + if (strip >= td->td_nstrips && !TIFFGrowStrips(tif, 1, module)) + return (-1); + if (strip != tif->tif_curstrip) { + /* + * Changing strips -- flush any data present. + */ + if (!TIFFFlushData(tif)) + return (-1); + tif->tif_curstrip = strip; + /* + * Watch out for a growing image. The value of strips/image + * will initially be 1 (since it can't be deduced until the + * imagelength is known). + */ + if (strip >= td->td_stripsperimage && imagegrew) + td->td_stripsperimage = + TIFFhowmany_32(td->td_imagelength,td->td_rowsperstrip); + tif->tif_row = + (strip % td->td_stripsperimage) * td->td_rowsperstrip; + if ((tif->tif_flags & TIFF_CODERSETUP) == 0) { + if (!(*tif->tif_setupencode)(tif)) + return (-1); + tif->tif_flags |= TIFF_CODERSETUP; + } + + tif->tif_rawcc = 0; + tif->tif_rawcp = tif->tif_rawdata; + + if( td->td_stripbytecount[strip] > 0 ) + { + /* if we are writing over existing tiles, zero length */ + td->td_stripbytecount[strip] = 0; + + /* this forces TIFFAppendToStrip() to do a seek */ + tif->tif_curoff = 0; + } + + if (!(*tif->tif_preencode)(tif, sample)) + return (-1); + tif->tif_flags |= TIFF_POSTENCODE; + } + /* + * Ensure the write is either sequential or at the + * beginning of a strip (or that we can randomly + * access the data -- i.e. no encoding). + */ + if (row != tif->tif_row) { + if (row < tif->tif_row) { + /* + * Moving backwards within the same strip: + * backup to the start and then decode + * forward (below). + */ + tif->tif_row = (strip % td->td_stripsperimage) * + td->td_rowsperstrip; + tif->tif_rawcp = tif->tif_rawdata; + } + /* + * Seek forward to the desired row. + */ + if (!(*tif->tif_seek)(tif, row - tif->tif_row)) + return (-1); + tif->tif_row = row; + } + + /* swab if needed - note that source buffer will be altered */ + tif->tif_postdecode( tif, (uint8*) buf, tif->tif_scanlinesize ); + + status = (*tif->tif_encoderow)(tif, (uint8*) buf, + tif->tif_scanlinesize, sample); + + /* we are now poised at the beginning of the next row */ + tif->tif_row = row + 1; + return (status); +} + +/* + * Encode the supplied data and write it to the + * specified strip. + * + * NB: Image length must be setup before writing. + */ +tmsize_t +TIFFWriteEncodedStrip(TIFF* tif, uint32 strip, void* data, tmsize_t cc) +{ + static const char module[] = "TIFFWriteEncodedStrip"; + TIFFDirectory *td = &tif->tif_dir; + uint16 sample; + + if (!WRITECHECKSTRIPS(tif, module)) + return ((tmsize_t) -1); + /* + * Check strip array to make sure there's space. + * We don't support dynamically growing files that + * have data organized in separate bitplanes because + * it's too painful. In that case we require that + * the imagelength be set properly before the first + * write (so that the strips array will be fully + * allocated above). + */ + if (strip >= td->td_nstrips) { + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { + TIFFErrorExt(tif->tif_clientdata, module, + "Can not grow image by strips when using separate planes"); + return ((tmsize_t) -1); + } + if (!TIFFGrowStrips(tif, 1, module)) + return ((tmsize_t) -1); + td->td_stripsperimage = + TIFFhowmany_32(td->td_imagelength, td->td_rowsperstrip); + } + /* + * Handle delayed allocation of data buffer. This + * permits it to be sized according to the directory + * info. + */ + if (!BUFFERCHECK(tif)) + return ((tmsize_t) -1); + + tif->tif_flags |= TIFF_BUF4WRITE; + tif->tif_curstrip = strip; + + tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip; + if ((tif->tif_flags & TIFF_CODERSETUP) == 0) { + if (!(*tif->tif_setupencode)(tif)) + return ((tmsize_t) -1); + tif->tif_flags |= TIFF_CODERSETUP; + } + + if( td->td_stripbytecount[strip] > 0 ) + { + /* Make sure that at the first attempt of rewriting the tile, we will have */ + /* more bytes available in the output buffer than the previous byte count, */ + /* so that TIFFAppendToStrip() will detect the overflow when it is called the first */ + /* time if the new compressed tile is bigger than the older one. (GDAL #4771) */ + if( tif->tif_rawdatasize <= (tmsize_t)td->td_stripbytecount[strip] ) + { + if( !(TIFFWriteBufferSetup(tif, NULL, + (tmsize_t)TIFFroundup_64((uint64)(td->td_stripbytecount[strip] + 1), 1024))) ) + return ((tmsize_t)(-1)); + } + + /* Force TIFFAppendToStrip() to consider placing data at end + of file. */ + tif->tif_curoff = 0; + } + + tif->tif_rawcc = 0; + tif->tif_rawcp = tif->tif_rawdata; + + tif->tif_flags &= ~TIFF_POSTENCODE; + sample = (uint16)(strip / td->td_stripsperimage); + if (!(*tif->tif_preencode)(tif, sample)) + return ((tmsize_t) -1); + + /* swab if needed - note that source buffer will be altered */ + tif->tif_postdecode( tif, (uint8*) data, cc ); + + if (!(*tif->tif_encodestrip)(tif, (uint8*) data, cc, sample)) + return (0); + if (!(*tif->tif_postencode)(tif)) + return ((tmsize_t) -1); + if (!isFillOrder(tif, td->td_fillorder) && + (tif->tif_flags & TIFF_NOBITREV) == 0) + TIFFReverseBits(tif->tif_rawdata, tif->tif_rawcc); + if (tif->tif_rawcc > 0 && + !TIFFAppendToStrip(tif, strip, tif->tif_rawdata, tif->tif_rawcc)) + return ((tmsize_t) -1); + tif->tif_rawcc = 0; + tif->tif_rawcp = tif->tif_rawdata; + return (cc); +} + +/* + * Write the supplied data to the specified strip. + * + * NB: Image length must be setup before writing. + */ +tmsize_t +TIFFWriteRawStrip(TIFF* tif, uint32 strip, void* data, tmsize_t cc) +{ + static const char module[] = "TIFFWriteRawStrip"; + TIFFDirectory *td = &tif->tif_dir; + + if (!WRITECHECKSTRIPS(tif, module)) + return ((tmsize_t) -1); + /* + * Check strip array to make sure there's space. + * We don't support dynamically growing files that + * have data organized in separate bitplanes because + * it's too painful. In that case we require that + * the imagelength be set properly before the first + * write (so that the strips array will be fully + * allocated above). + */ + if (strip >= td->td_nstrips) { + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) { + TIFFErrorExt(tif->tif_clientdata, module, + "Can not grow image by strips when using separate planes"); + return ((tmsize_t) -1); + } + /* + * Watch out for a growing image. The value of + * strips/image will initially be 1 (since it + * can't be deduced until the imagelength is known). + */ + if (strip >= td->td_stripsperimage) + td->td_stripsperimage = + TIFFhowmany_32(td->td_imagelength,td->td_rowsperstrip); + if (!TIFFGrowStrips(tif, 1, module)) + return ((tmsize_t) -1); + } + tif->tif_curstrip = strip; + tif->tif_row = (strip % td->td_stripsperimage) * td->td_rowsperstrip; + return (TIFFAppendToStrip(tif, strip, (uint8*) data, cc) ? + cc : (tmsize_t) -1); +} + +/* + * Write and compress a tile of data. The + * tile is selected by the (x,y,z,s) coordinates. + */ +tmsize_t +TIFFWriteTile(TIFF* tif, void* buf, uint32 x, uint32 y, uint32 z, uint16 s) +{ + if (!TIFFCheckTile(tif, x, y, z, s)) + return ((tmsize_t)(-1)); + /* + * NB: A tile size of -1 is used instead of tif_tilesize knowing + * that TIFFWriteEncodedTile will clamp this to the tile size. + * This is done because the tile size may not be defined until + * after the output buffer is setup in TIFFWriteBufferSetup. + */ + return (TIFFWriteEncodedTile(tif, + TIFFComputeTile(tif, x, y, z, s), buf, (tmsize_t)(-1))); +} + +/* + * Encode the supplied data and write it to the + * specified tile. There must be space for the + * data. The function clamps individual writes + * to a tile to the tile size, but does not (and + * can not) check that multiple writes to the same + * tile do not write more than tile size data. + * + * NB: Image length must be setup before writing; this + * interface does not support automatically growing + * the image on each write (as TIFFWriteScanline does). + */ +tmsize_t +TIFFWriteEncodedTile(TIFF* tif, uint32 tile, void* data, tmsize_t cc) +{ + static const char module[] = "TIFFWriteEncodedTile"; + TIFFDirectory *td; + uint16 sample; + + if (!WRITECHECKTILES(tif, module)) + return ((tmsize_t)(-1)); + td = &tif->tif_dir; + if (tile >= td->td_nstrips) { + TIFFErrorExt(tif->tif_clientdata, module, "Tile %lu out of range, max %lu", + (unsigned long) tile, (unsigned long) td->td_nstrips); + return ((tmsize_t)(-1)); + } + /* + * Handle delayed allocation of data buffer. This + * permits it to be sized more intelligently (using + * directory information). + */ + if (!BUFFERCHECK(tif)) + return ((tmsize_t)(-1)); + + tif->tif_flags |= TIFF_BUF4WRITE; + tif->tif_curtile = tile; + + if( td->td_stripbytecount[tile] > 0 ) + { + /* Make sure that at the first attempt of rewriting the tile, we will have */ + /* more bytes available in the output buffer than the previous byte count, */ + /* so that TIFFAppendToStrip() will detect the overflow when it is called the first */ + /* time if the new compressed tile is bigger than the older one. (GDAL #4771) */ + if( tif->tif_rawdatasize <= (tmsize_t) td->td_stripbytecount[tile] ) + { + if( !(TIFFWriteBufferSetup(tif, NULL, + (tmsize_t)TIFFroundup_64((uint64)(td->td_stripbytecount[tile] + 1), 1024))) ) + return ((tmsize_t)(-1)); + } + + /* Force TIFFAppendToStrip() to consider placing data at end + of file. */ + tif->tif_curoff = 0; + } + + tif->tif_rawcc = 0; + tif->tif_rawcp = tif->tif_rawdata; + + /* + * Compute tiles per row & per column to compute + * current row and column + */ + tif->tif_row = (tile % TIFFhowmany_32(td->td_imagelength, td->td_tilelength)) + * td->td_tilelength; + tif->tif_col = (tile % TIFFhowmany_32(td->td_imagewidth, td->td_tilewidth)) + * td->td_tilewidth; + + if ((tif->tif_flags & TIFF_CODERSETUP) == 0) { + if (!(*tif->tif_setupencode)(tif)) + return ((tmsize_t)(-1)); + tif->tif_flags |= TIFF_CODERSETUP; + } + tif->tif_flags &= ~TIFF_POSTENCODE; + sample = (uint16)(tile/td->td_stripsperimage); + if (!(*tif->tif_preencode)(tif, sample)) + return ((tmsize_t)(-1)); + /* + * Clamp write amount to the tile size. This is mostly + * done so that callers can pass in some large number + * (e.g. -1) and have the tile size used instead. + */ + if ( cc < 1 || cc > tif->tif_tilesize) + cc = tif->tif_tilesize; + + /* swab if needed - note that source buffer will be altered */ + tif->tif_postdecode( tif, (uint8*) data, cc ); + + if (!(*tif->tif_encodetile)(tif, (uint8*) data, cc, sample)) + return (0); + if (!(*tif->tif_postencode)(tif)) + return ((tmsize_t)(-1)); + if (!isFillOrder(tif, td->td_fillorder) && + (tif->tif_flags & TIFF_NOBITREV) == 0) + TIFFReverseBits((uint8*)tif->tif_rawdata, tif->tif_rawcc); + if (tif->tif_rawcc > 0 && !TIFFAppendToStrip(tif, tile, + tif->tif_rawdata, tif->tif_rawcc)) + return ((tmsize_t)(-1)); + tif->tif_rawcc = 0; + tif->tif_rawcp = tif->tif_rawdata; + return (cc); +} + +/* + * Write the supplied data to the specified strip. + * There must be space for the data; we don't check + * if strips overlap! + * + * NB: Image length must be setup before writing; this + * interface does not support automatically growing + * the image on each write (as TIFFWriteScanline does). + */ +tmsize_t +TIFFWriteRawTile(TIFF* tif, uint32 tile, void* data, tmsize_t cc) +{ + static const char module[] = "TIFFWriteRawTile"; + + if (!WRITECHECKTILES(tif, module)) + return ((tmsize_t)(-1)); + if (tile >= tif->tif_dir.td_nstrips) { + TIFFErrorExt(tif->tif_clientdata, module, "Tile %lu out of range, max %lu", + (unsigned long) tile, + (unsigned long) tif->tif_dir.td_nstrips); + return ((tmsize_t)(-1)); + } + return (TIFFAppendToStrip(tif, tile, (uint8*) data, cc) ? + cc : (tmsize_t)(-1)); +} + +#define isUnspecified(tif, f) \ + (TIFFFieldSet(tif,f) && (tif)->tif_dir.td_imagelength == 0) + +int +TIFFSetupStrips(TIFF* tif) +{ + TIFFDirectory* td = &tif->tif_dir; + + if (isTiled(tif)) + td->td_stripsperimage = + isUnspecified(tif, FIELD_TILEDIMENSIONS) ? + td->td_samplesperpixel : TIFFNumberOfTiles(tif); + else + td->td_stripsperimage = + isUnspecified(tif, FIELD_ROWSPERSTRIP) ? + td->td_samplesperpixel : TIFFNumberOfStrips(tif); + td->td_nstrips = td->td_stripsperimage; + if (td->td_planarconfig == PLANARCONFIG_SEPARATE) + td->td_stripsperimage /= td->td_samplesperpixel; + td->td_stripoffset = (uint64 *) + _TIFFmalloc(td->td_nstrips * sizeof (uint64)); + td->td_stripbytecount = (uint64 *) + _TIFFmalloc(td->td_nstrips * sizeof (uint64)); + if (td->td_stripoffset == NULL || td->td_stripbytecount == NULL) + return (0); + /* + * Place data at the end-of-file + * (by setting offsets to zero). + */ + _TIFFmemset(td->td_stripoffset, 0, td->td_nstrips*sizeof (uint64)); + _TIFFmemset(td->td_stripbytecount, 0, td->td_nstrips*sizeof (uint64)); + TIFFSetFieldBit(tif, FIELD_STRIPOFFSETS); + TIFFSetFieldBit(tif, FIELD_STRIPBYTECOUNTS); + return (1); +} +#undef isUnspecified + +/* + * Verify file is writable and that the directory + * information is setup properly. In doing the latter + * we also "freeze" the state of the directory so + * that important information is not changed. + */ +int +TIFFWriteCheck(TIFF* tif, int tiles, const char* module) +{ + if (tif->tif_mode == O_RDONLY) { + TIFFErrorExt(tif->tif_clientdata, module, "File not open for writing"); + return (0); + } + if (tiles ^ isTiled(tif)) { + TIFFErrorExt(tif->tif_clientdata, module, tiles ? + "Can not write tiles to a stripped image" : + "Can not write scanlines to a tiled image"); + return (0); + } + + _TIFFFillStriles( tif ); + + /* + * On the first write verify all the required information + * has been setup and initialize any data structures that + * had to wait until directory information was set. + * Note that a lot of our work is assumed to remain valid + * because we disallow any of the important parameters + * from changing after we start writing (i.e. once + * TIFF_BEENWRITING is set, TIFFSetField will only allow + * the image's length to be changed). + */ + if (!TIFFFieldSet(tif, FIELD_IMAGEDIMENSIONS)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Must set \"ImageWidth\" before writing data"); + return (0); + } + if (tif->tif_dir.td_samplesperpixel == 1) { + /* + * Planarconfiguration is irrelevant in case of single band + * images and need not be included. We will set it anyway, + * because this field is used in other parts of library even + * in the single band case. + */ + if (!TIFFFieldSet(tif, FIELD_PLANARCONFIG)) + tif->tif_dir.td_planarconfig = PLANARCONFIG_CONTIG; + } else { + if (!TIFFFieldSet(tif, FIELD_PLANARCONFIG)) { + TIFFErrorExt(tif->tif_clientdata, module, + "Must set \"PlanarConfiguration\" before writing data"); + return (0); + } + } + if (tif->tif_dir.td_stripoffset == NULL && !TIFFSetupStrips(tif)) { + tif->tif_dir.td_nstrips = 0; + TIFFErrorExt(tif->tif_clientdata, module, "No space for %s arrays", + isTiled(tif) ? "tile" : "strip"); + return (0); + } + if (isTiled(tif)) + { + tif->tif_tilesize = TIFFTileSize(tif); + if (tif->tif_tilesize == 0) + return (0); + } + else + tif->tif_tilesize = (tmsize_t)(-1); + tif->tif_scanlinesize = TIFFScanlineSize(tif); + if (tif->tif_scanlinesize == 0) + return (0); + tif->tif_flags |= TIFF_BEENWRITING; + return (1); +} + +/* + * Setup the raw data buffer used for encoding. + */ +int +TIFFWriteBufferSetup(TIFF* tif, void* bp, tmsize_t size) +{ + static const char module[] = "TIFFWriteBufferSetup"; + + if (tif->tif_rawdata) { + if (tif->tif_flags & TIFF_MYBUFFER) { + _TIFFfree(tif->tif_rawdata); + tif->tif_flags &= ~TIFF_MYBUFFER; + } + tif->tif_rawdata = NULL; + } + if (size == (tmsize_t)(-1)) { + size = (isTiled(tif) ? + tif->tif_tilesize : TIFFStripSize(tif)); + /* + * Make raw data buffer at least 8K + */ + if (size < 8*1024) + size = 8*1024; + bp = NULL; /* NB: force malloc */ + } + if (bp == NULL) { + bp = _TIFFmalloc(size); + if (bp == NULL) { + TIFFErrorExt(tif->tif_clientdata, module, "No space for output buffer"); + return (0); + } + tif->tif_flags |= TIFF_MYBUFFER; + } else + tif->tif_flags &= ~TIFF_MYBUFFER; + tif->tif_rawdata = (uint8*) bp; + tif->tif_rawdatasize = size; + tif->tif_rawcc = 0; + tif->tif_rawcp = tif->tif_rawdata; + tif->tif_flags |= TIFF_BUFFERSETUP; + return (1); +} + +/* + * Grow the strip data structures by delta strips. + */ +static int +TIFFGrowStrips(TIFF* tif, uint32 delta, const char* module) +{ + TIFFDirectory *td = &tif->tif_dir; + uint64* new_stripoffset; + uint64* new_stripbytecount; + + assert(td->td_planarconfig == PLANARCONFIG_CONTIG); + new_stripoffset = (uint64*)_TIFFrealloc(td->td_stripoffset, + (td->td_nstrips + delta) * sizeof (uint64)); + new_stripbytecount = (uint64*)_TIFFrealloc(td->td_stripbytecount, + (td->td_nstrips + delta) * sizeof (uint64)); + if (new_stripoffset == NULL || new_stripbytecount == NULL) { + if (new_stripoffset) + _TIFFfree(new_stripoffset); + if (new_stripbytecount) + _TIFFfree(new_stripbytecount); + td->td_nstrips = 0; + TIFFErrorExt(tif->tif_clientdata, module, "No space to expand strip arrays"); + return (0); + } + td->td_stripoffset = new_stripoffset; + td->td_stripbytecount = new_stripbytecount; + _TIFFmemset(td->td_stripoffset + td->td_nstrips, + 0, delta*sizeof (uint64)); + _TIFFmemset(td->td_stripbytecount + td->td_nstrips, + 0, delta*sizeof (uint64)); + td->td_nstrips += delta; + tif->tif_flags |= TIFF_DIRTYDIRECT; + + return (1); +} + +/* + * Append the data to the specified strip. + */ +static int +TIFFAppendToStrip(TIFF* tif, uint32 strip, uint8* data, tmsize_t cc) +{ + static const char module[] = "TIFFAppendToStrip"; + TIFFDirectory *td = &tif->tif_dir; + uint64 m; + int64 old_byte_count = -1; + + if (td->td_stripoffset[strip] == 0 || tif->tif_curoff == 0) { + assert(td->td_nstrips > 0); + + if( td->td_stripbytecount[strip] != 0 + && td->td_stripoffset[strip] != 0 + && td->td_stripbytecount[strip] >= (uint64) cc ) + { + /* + * There is already tile data on disk, and the new tile + * data we have will fit in the same space. The only + * aspect of this that is risky is that there could be + * more data to append to this strip before we are done + * depending on how we are getting called. + */ + if (!SeekOK(tif, td->td_stripoffset[strip])) { + TIFFErrorExt(tif->tif_clientdata, module, + "Seek error at scanline %lu", + (unsigned long)tif->tif_row); + return (0); + } + } + else + { + /* + * Seek to end of file, and set that as our location to + * write this strip. + */ + td->td_stripoffset[strip] = TIFFSeekFile(tif, 0, SEEK_END); + tif->tif_flags |= TIFF_DIRTYSTRIP; + } + + tif->tif_curoff = td->td_stripoffset[strip]; + + /* + * We are starting a fresh strip/tile, so set the size to zero. + */ + old_byte_count = td->td_stripbytecount[strip]; + td->td_stripbytecount[strip] = 0; + } + + m = tif->tif_curoff+cc; + if (!(tif->tif_flags&TIFF_BIGTIFF)) + m = (uint32)m; + if ((mtif_curoff)||(m<(uint64)cc)) + { + TIFFErrorExt(tif->tif_clientdata, module, "Maximum TIFF file size exceeded"); + return (0); + } + if (!WriteOK(tif, data, cc)) { + TIFFErrorExt(tif->tif_clientdata, module, "Write error at scanline %lu", + (unsigned long) tif->tif_row); + return (0); + } + tif->tif_curoff = m; + td->td_stripbytecount[strip] += cc; + + if( (int64) td->td_stripbytecount[strip] != old_byte_count ) + tif->tif_flags |= TIFF_DIRTYSTRIP; + + return (1); +} + +/* + * Internal version of TIFFFlushData that can be + * called by ``encodestrip routines'' w/o concern + * for infinite recursion. + */ +int +TIFFFlushData1(TIFF* tif) +{ + if (tif->tif_rawcc > 0 && tif->tif_flags & TIFF_BUF4WRITE ) { + if (!isFillOrder(tif, tif->tif_dir.td_fillorder) && + (tif->tif_flags & TIFF_NOBITREV) == 0) + TIFFReverseBits((uint8*)tif->tif_rawdata, + tif->tif_rawcc); + if (!TIFFAppendToStrip(tif, + isTiled(tif) ? tif->tif_curtile : tif->tif_curstrip, + tif->tif_rawdata, tif->tif_rawcc)) + return (0); + tif->tif_rawcc = 0; + tif->tif_rawcp = tif->tif_rawdata; + } + return (1); +} + +/* + * Set the current write offset. This should only be + * used to set the offset to a known previous location + * (very carefully), or to 0 so that the next write gets + * appended to the end of the file. + */ +void +TIFFSetWriteOffset(TIFF* tif, toff_t off) +{ + tif->tif_curoff = off; +} + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_zip.c b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_zip.c new file mode 100644 index 000000000..22e9f35bc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tif_zip.c @@ -0,0 +1,472 @@ +/* $Id: tif_zip.c,v 1.33 2014-12-25 18:29:11 erouault Exp $ */ + +/* + * Copyright (c) 1995-1997 Sam Leffler + * Copyright (c) 1995-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "tiffiop.h" +#ifdef ZIP_SUPPORT +/* + * TIFF Library. + * + * ZIP (aka Deflate) Compression Support + * + * This file is simply an interface to the zlib library written by + * Jean-loup Gailly and Mark Adler. You must use version 1.0 or later + * of the library: this code assumes the 1.0 API and also depends on + * the ability to write the zlib header multiple times (one per strip) + * which was not possible with versions prior to 0.95. Note also that + * older versions of this codec avoided this bug by suppressing the header + * entirely. This means that files written with the old library cannot + * be read; they should be converted to a different compression scheme + * and then reconverted. + * + * The data format used by the zlib library is described in the files + * zlib-3.1.doc, deflate-1.1.doc and gzip-4.1.doc, available in the + * directory ftp://ftp.uu.net/pub/archiving/zip/doc. The library was + * last found at ftp://ftp.uu.net/pub/archiving/zip/zlib/zlib-0.99.tar.gz. + */ +#include "tif_predict.h" +#include "zlib.h" + +#include + +/* + * Sigh, ZLIB_VERSION is defined as a string so there's no + * way to do a proper check here. Instead we guess based + * on the presence of #defines that were added between the + * 0.95 and 1.0 distributions. + */ +#if !defined(Z_NO_COMPRESSION) || !defined(Z_DEFLATED) +#error "Antiquated ZLIB software; you must use version 1.0 or later" +#endif + +#define SAFE_MSG(sp) ((sp)->stream.msg == NULL ? "" : (sp)->stream.msg) + +/* + * State block for each open TIFF + * file using ZIP compression/decompression. + */ +typedef struct { + TIFFPredictorState predict; + z_stream stream; + int zipquality; /* compression level */ + int state; /* state flags */ +#define ZSTATE_INIT_DECODE 0x01 +#define ZSTATE_INIT_ENCODE 0x02 + + TIFFVGetMethod vgetparent; /* super-class method */ + TIFFVSetMethod vsetparent; /* super-class method */ +} ZIPState; + +#define ZState(tif) ((ZIPState*) (tif)->tif_data) +#define DecoderState(tif) ZState(tif) +#define EncoderState(tif) ZState(tif) + +static int ZIPEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s); +static int ZIPDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s); + +static int +ZIPFixupTags(TIFF* tif) +{ + (void) tif; + return (1); +} + +static int +ZIPSetupDecode(TIFF* tif) +{ + static const char module[] = "ZIPSetupDecode"; + ZIPState* sp = DecoderState(tif); + + assert(sp != NULL); + + /* if we were last encoding, terminate this mode */ + if (sp->state & ZSTATE_INIT_ENCODE) { + deflateEnd(&sp->stream); + sp->state = 0; + } + + if (inflateInit(&sp->stream) != Z_OK) { + TIFFErrorExt(tif->tif_clientdata, module, "%s", SAFE_MSG(sp)); + return (0); + } else { + sp->state |= ZSTATE_INIT_DECODE; + return (1); + } +} + +/* + * Setup state for decoding a strip. + */ +static int +ZIPPreDecode(TIFF* tif, uint16 s) +{ + static const char module[] = "ZIPPreDecode"; + ZIPState* sp = DecoderState(tif); + + (void) s; + assert(sp != NULL); + + if( (sp->state & ZSTATE_INIT_DECODE) == 0 ) + tif->tif_setupdecode( tif ); + + sp->stream.next_in = tif->tif_rawdata; + assert(sizeof(sp->stream.avail_in)==4); /* if this assert gets raised, + we need to simplify this code to reflect a ZLib that is likely updated + to deal with 8byte memory sizes, though this code will respond + apropriately even before we simplify it */ + sp->stream.avail_in = (uInt) tif->tif_rawcc; + if ((tmsize_t)sp->stream.avail_in != tif->tif_rawcc) + { + TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size"); + return (0); + } + return (inflateReset(&sp->stream) == Z_OK); +} + +static int +ZIPDecode(TIFF* tif, uint8* op, tmsize_t occ, uint16 s) +{ + static const char module[] = "ZIPDecode"; + ZIPState* sp = DecoderState(tif); + + (void) s; + assert(sp != NULL); + assert(sp->state == ZSTATE_INIT_DECODE); + + sp->stream.next_in = tif->tif_rawcp; + sp->stream.avail_in = (uInt) tif->tif_rawcc; + + sp->stream.next_out = op; + assert(sizeof(sp->stream.avail_out)==4); /* if this assert gets raised, + we need to simplify this code to reflect a ZLib that is likely updated + to deal with 8byte memory sizes, though this code will respond + apropriately even before we simplify it */ + sp->stream.avail_out = (uInt) occ; + if ((tmsize_t)sp->stream.avail_out != occ) + { + TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size"); + return (0); + } + do { + int state = inflate(&sp->stream, Z_PARTIAL_FLUSH); + if (state == Z_STREAM_END) + break; + if (state == Z_DATA_ERROR) { + TIFFErrorExt(tif->tif_clientdata, module, + "Decoding error at scanline %lu, %s", + (unsigned long) tif->tif_row, SAFE_MSG(sp)); + if (inflateSync(&sp->stream) != Z_OK) + return (0); + continue; + } + if (state != Z_OK) { + TIFFErrorExt(tif->tif_clientdata, module, + "ZLib error: %s", SAFE_MSG(sp)); + return (0); + } + } while (sp->stream.avail_out > 0); + if (sp->stream.avail_out != 0) { + TIFFErrorExt(tif->tif_clientdata, module, + "Not enough data at scanline %lu (short " TIFF_UINT64_FORMAT " bytes)", + (unsigned long) tif->tif_row, (TIFF_UINT64_T) sp->stream.avail_out); + return (0); + } + + tif->tif_rawcp = sp->stream.next_in; + tif->tif_rawcc = sp->stream.avail_in; + + return (1); +} + +static int +ZIPSetupEncode(TIFF* tif) +{ + static const char module[] = "ZIPSetupEncode"; + ZIPState* sp = EncoderState(tif); + + assert(sp != NULL); + if (sp->state & ZSTATE_INIT_DECODE) { + inflateEnd(&sp->stream); + sp->state = 0; + } + + if (deflateInit(&sp->stream, sp->zipquality) != Z_OK) { + TIFFErrorExt(tif->tif_clientdata, module, "%s", SAFE_MSG(sp)); + return (0); + } else { + sp->state |= ZSTATE_INIT_ENCODE; + return (1); + } +} + +/* + * Reset encoding state at the start of a strip. + */ +static int +ZIPPreEncode(TIFF* tif, uint16 s) +{ + static const char module[] = "ZIPPreEncode"; + ZIPState *sp = EncoderState(tif); + + (void) s; + assert(sp != NULL); + if( sp->state != ZSTATE_INIT_ENCODE ) + tif->tif_setupencode( tif ); + + sp->stream.next_out = tif->tif_rawdata; + assert(sizeof(sp->stream.avail_out)==4); /* if this assert gets raised, + we need to simplify this code to reflect a ZLib that is likely updated + to deal with 8byte memory sizes, though this code will respond + apropriately even before we simplify it */ + sp->stream.avail_out = tif->tif_rawdatasize; + if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize) + { + TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size"); + return (0); + } + return (deflateReset(&sp->stream) == Z_OK); +} + +/* + * Encode a chunk of pixels. + */ +static int +ZIPEncode(TIFF* tif, uint8* bp, tmsize_t cc, uint16 s) +{ + static const char module[] = "ZIPEncode"; + ZIPState *sp = EncoderState(tif); + + assert(sp != NULL); + assert(sp->state == ZSTATE_INIT_ENCODE); + + (void) s; + sp->stream.next_in = bp; + assert(sizeof(sp->stream.avail_in)==4); /* if this assert gets raised, + we need to simplify this code to reflect a ZLib that is likely updated + to deal with 8byte memory sizes, though this code will respond + apropriately even before we simplify it */ + sp->stream.avail_in = (uInt) cc; + if ((tmsize_t)sp->stream.avail_in != cc) + { + TIFFErrorExt(tif->tif_clientdata, module, "ZLib cannot deal with buffers this size"); + return (0); + } + do { + if (deflate(&sp->stream, Z_NO_FLUSH) != Z_OK) { + TIFFErrorExt(tif->tif_clientdata, module, + "Encoder error: %s", + SAFE_MSG(sp)); + return (0); + } + if (sp->stream.avail_out == 0) { + tif->tif_rawcc = tif->tif_rawdatasize; + TIFFFlushData1(tif); + sp->stream.next_out = tif->tif_rawdata; + sp->stream.avail_out = (uInt) tif->tif_rawdatasize; /* this is a safe typecast, as check is made already in ZIPPreEncode */ + } + } while (sp->stream.avail_in > 0); + return (1); +} + +/* + * Finish off an encoded strip by flushing the last + * string and tacking on an End Of Information code. + */ +static int +ZIPPostEncode(TIFF* tif) +{ + static const char module[] = "ZIPPostEncode"; + ZIPState *sp = EncoderState(tif); + int state; + + sp->stream.avail_in = 0; + do { + state = deflate(&sp->stream, Z_FINISH); + switch (state) { + case Z_STREAM_END: + case Z_OK: + if ((tmsize_t)sp->stream.avail_out != tif->tif_rawdatasize) + { + tif->tif_rawcc = tif->tif_rawdatasize - sp->stream.avail_out; + TIFFFlushData1(tif); + sp->stream.next_out = tif->tif_rawdata; + sp->stream.avail_out = (uInt) tif->tif_rawdatasize; /* this is a safe typecast, as check is made already in ZIPPreEncode */ + } + break; + default: + TIFFErrorExt(tif->tif_clientdata, module, + "ZLib error: %s", SAFE_MSG(sp)); + return (0); + } + } while (state != Z_STREAM_END); + return (1); +} + +static void +ZIPCleanup(TIFF* tif) +{ + ZIPState* sp = ZState(tif); + + assert(sp != 0); + + (void)TIFFPredictorCleanup(tif); + + tif->tif_tagmethods.vgetfield = sp->vgetparent; + tif->tif_tagmethods.vsetfield = sp->vsetparent; + + if (sp->state & ZSTATE_INIT_ENCODE) { + deflateEnd(&sp->stream); + sp->state = 0; + } else if( sp->state & ZSTATE_INIT_DECODE) { + inflateEnd(&sp->stream); + sp->state = 0; + } + _TIFFfree(sp); + tif->tif_data = NULL; + + _TIFFSetDefaultCompressionState(tif); +} + +static int +ZIPVSetField(TIFF* tif, uint32 tag, va_list ap) +{ + static const char module[] = "ZIPVSetField"; + ZIPState* sp = ZState(tif); + + switch (tag) { + case TIFFTAG_ZIPQUALITY: + sp->zipquality = (int) va_arg(ap, int); + if ( sp->state&ZSTATE_INIT_ENCODE ) { + if (deflateParams(&sp->stream, + sp->zipquality, Z_DEFAULT_STRATEGY) != Z_OK) { + TIFFErrorExt(tif->tif_clientdata, module, "ZLib error: %s", + SAFE_MSG(sp)); + return (0); + } + } + return (1); + default: + return (*sp->vsetparent)(tif, tag, ap); + } + /*NOTREACHED*/ +} + +static int +ZIPVGetField(TIFF* tif, uint32 tag, va_list ap) +{ + ZIPState* sp = ZState(tif); + + switch (tag) { + case TIFFTAG_ZIPQUALITY: + *va_arg(ap, int*) = sp->zipquality; + break; + default: + return (*sp->vgetparent)(tif, tag, ap); + } + return (1); +} + +static const TIFFField zipFields[] = { + { TIFFTAG_ZIPQUALITY, 0, 0, TIFF_ANY, 0, TIFF_SETGET_INT, TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, TRUE, FALSE, "", NULL }, +}; + +int +TIFFInitZIP(TIFF* tif, int scheme) +{ + static const char module[] = "TIFFInitZIP"; + ZIPState* sp; + + assert( (scheme == COMPRESSION_DEFLATE) + || (scheme == COMPRESSION_ADOBE_DEFLATE)); + + /* + * Merge codec-specific tag information. + */ + if (!_TIFFMergeFields(tif, zipFields, TIFFArrayCount(zipFields))) { + TIFFErrorExt(tif->tif_clientdata, module, + "Merging Deflate codec-specific tags failed"); + return 0; + } + + /* + * Allocate state block so tag methods have storage to record values. + */ + tif->tif_data = (uint8*) _TIFFmalloc(sizeof (ZIPState)); + if (tif->tif_data == NULL) + goto bad; + sp = ZState(tif); + sp->stream.zalloc = NULL; + sp->stream.zfree = NULL; + sp->stream.opaque = NULL; + sp->stream.data_type = Z_BINARY; + + /* + * Override parent get/set field methods. + */ + sp->vgetparent = tif->tif_tagmethods.vgetfield; + tif->tif_tagmethods.vgetfield = ZIPVGetField; /* hook for codec tags */ + sp->vsetparent = tif->tif_tagmethods.vsetfield; + tif->tif_tagmethods.vsetfield = ZIPVSetField; /* hook for codec tags */ + + /* Default values for codec-specific fields */ + sp->zipquality = Z_DEFAULT_COMPRESSION; /* default comp. level */ + sp->state = 0; + + /* + * Install codec methods. + */ + tif->tif_fixuptags = ZIPFixupTags; + tif->tif_setupdecode = ZIPSetupDecode; + tif->tif_predecode = ZIPPreDecode; + tif->tif_decoderow = ZIPDecode; + tif->tif_decodestrip = ZIPDecode; + tif->tif_decodetile = ZIPDecode; + tif->tif_setupencode = ZIPSetupEncode; + tif->tif_preencode = ZIPPreEncode; + tif->tif_postencode = ZIPPostEncode; + tif->tif_encoderow = ZIPEncode; + tif->tif_encodestrip = ZIPEncode; + tif->tif_encodetile = ZIPEncode; + tif->tif_cleanup = ZIPCleanup; + /* + * Setup predictor setup. + */ + (void) TIFFPredictorInit(tif); + return (1); +bad: + TIFFErrorExt(tif->tif_clientdata, module, + "No space for ZIP state block"); + return (0); +} +#endif /* ZIP_SUPORT */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiff.h b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiff.h new file mode 100644 index 000000000..bc46acd02 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiff.h @@ -0,0 +1,681 @@ +/* $Id: tiff.h,v 1.69 2014-04-02 17:23:06 fwarmerdam Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _TIFF_ +#define _TIFF_ + +#include "tiffconf.h" + +/* + * Tag Image File Format (TIFF) + * + * Based on Rev 6.0 from: + * Developer's Desk + * Aldus Corporation + * 411 First Ave. South + * Suite 200 + * Seattle, WA 98104 + * 206-622-5500 + * + * (http://partners.adobe.com/asn/developer/PDFS/TN/TIFF6.pdf) + * + * For BigTIFF design notes see the following links + * http://www.remotesensing.org/libtiff/bigtiffdesign.html + * http://www.awaresystems.be/imaging/tiff/bigtiff.html + */ + +#define TIFF_VERSION_CLASSIC 42 +#define TIFF_VERSION_BIG 43 + +#define TIFF_BIGENDIAN 0x4d4d +#define TIFF_LITTLEENDIAN 0x4949 +#define MDI_LITTLEENDIAN 0x5045 +#define MDI_BIGENDIAN 0x4550 + +/* + * Intrinsic data types required by the file format: + * + * 8-bit quantities int8/uint8 + * 16-bit quantities int16/uint16 + * 32-bit quantities int32/uint32 + * 64-bit quantities int64/uint64 + * strings unsigned char* + */ + +typedef TIFF_INT8_T int8; +typedef TIFF_UINT8_T uint8; + +typedef TIFF_INT16_T int16; +typedef TIFF_UINT16_T uint16; + +typedef TIFF_INT32_T int32; +typedef TIFF_UINT32_T uint32; + +typedef TIFF_INT64_T int64; +typedef TIFF_UINT64_T uint64; + +/* + * Some types as promoted in a variable argument list + * We use uint16_vap rather then directly using int, because this way + * we document the type we actually want to pass through, conceptually, + * rather then confusing the issue by merely stating the type it gets + * promoted to + */ + +typedef int uint16_vap; + +/* + * TIFF header. + */ +typedef struct { + uint16 tiff_magic; /* magic number (defines byte order) */ + uint16 tiff_version; /* TIFF version number */ +} TIFFHeaderCommon; +typedef struct { + uint16 tiff_magic; /* magic number (defines byte order) */ + uint16 tiff_version; /* TIFF version number */ + uint32 tiff_diroff; /* byte offset to first directory */ +} TIFFHeaderClassic; +typedef struct { + uint16 tiff_magic; /* magic number (defines byte order) */ + uint16 tiff_version; /* TIFF version number */ + uint16 tiff_offsetsize; /* size of offsets, should be 8 */ + uint16 tiff_unused; /* unused word, should be 0 */ + uint64 tiff_diroff; /* byte offset to first directory */ +} TIFFHeaderBig; + + +/* + * NB: In the comments below, + * - items marked with a + are obsoleted by revision 5.0, + * - items marked with a ! are introduced in revision 6.0. + * - items marked with a % are introduced post revision 6.0. + * - items marked with a $ are obsoleted by revision 6.0. + * - items marked with a & are introduced by Adobe DNG specification. + */ + +/* + * Tag data type information. + * + * Note: RATIONALs are the ratio of two 32-bit integer values. + */ +typedef enum { + TIFF_NOTYPE = 0, /* placeholder */ + TIFF_BYTE = 1, /* 8-bit unsigned integer */ + TIFF_ASCII = 2, /* 8-bit bytes w/ last byte null */ + TIFF_SHORT = 3, /* 16-bit unsigned integer */ + TIFF_LONG = 4, /* 32-bit unsigned integer */ + TIFF_RATIONAL = 5, /* 64-bit unsigned fraction */ + TIFF_SBYTE = 6, /* !8-bit signed integer */ + TIFF_UNDEFINED = 7, /* !8-bit untyped data */ + TIFF_SSHORT = 8, /* !16-bit signed integer */ + TIFF_SLONG = 9, /* !32-bit signed integer */ + TIFF_SRATIONAL = 10, /* !64-bit signed fraction */ + TIFF_FLOAT = 11, /* !32-bit IEEE floating point */ + TIFF_DOUBLE = 12, /* !64-bit IEEE floating point */ + TIFF_IFD = 13, /* %32-bit unsigned integer (offset) */ + TIFF_LONG8 = 16, /* BigTIFF 64-bit unsigned integer */ + TIFF_SLONG8 = 17, /* BigTIFF 64-bit signed integer */ + TIFF_IFD8 = 18 /* BigTIFF 64-bit unsigned integer (offset) */ +} TIFFDataType; + +/* + * TIFF Tag Definitions. + */ +#define TIFFTAG_SUBFILETYPE 254 /* subfile data descriptor */ +#define FILETYPE_REDUCEDIMAGE 0x1 /* reduced resolution version */ +#define FILETYPE_PAGE 0x2 /* one page of many */ +#define FILETYPE_MASK 0x4 /* transparency mask */ +#define TIFFTAG_OSUBFILETYPE 255 /* +kind of data in subfile */ +#define OFILETYPE_IMAGE 1 /* full resolution image data */ +#define OFILETYPE_REDUCEDIMAGE 2 /* reduced size image data */ +#define OFILETYPE_PAGE 3 /* one page of many */ +#define TIFFTAG_IMAGEWIDTH 256 /* image width in pixels */ +#define TIFFTAG_IMAGELENGTH 257 /* image height in pixels */ +#define TIFFTAG_BITSPERSAMPLE 258 /* bits per channel (sample) */ +#define TIFFTAG_COMPRESSION 259 /* data compression technique */ +#define COMPRESSION_NONE 1 /* dump mode */ +#define COMPRESSION_CCITTRLE 2 /* CCITT modified Huffman RLE */ +#define COMPRESSION_CCITTFAX3 3 /* CCITT Group 3 fax encoding */ +#define COMPRESSION_CCITT_T4 3 /* CCITT T.4 (TIFF 6 name) */ +#define COMPRESSION_CCITTFAX4 4 /* CCITT Group 4 fax encoding */ +#define COMPRESSION_CCITT_T6 4 /* CCITT T.6 (TIFF 6 name) */ +#define COMPRESSION_LZW 5 /* Lempel-Ziv & Welch */ +#define COMPRESSION_OJPEG 6 /* !6.0 JPEG */ +#define COMPRESSION_JPEG 7 /* %JPEG DCT compression */ +#define COMPRESSION_T85 9 /* !TIFF/FX T.85 JBIG compression */ +#define COMPRESSION_T43 10 /* !TIFF/FX T.43 colour by layered JBIG compression */ +#define COMPRESSION_NEXT 32766 /* NeXT 2-bit RLE */ +#define COMPRESSION_CCITTRLEW 32771 /* #1 w/ word alignment */ +#define COMPRESSION_PACKBITS 32773 /* Macintosh RLE */ +#define COMPRESSION_THUNDERSCAN 32809 /* ThunderScan RLE */ +/* codes 32895-32898 are reserved for ANSI IT8 TIFF/IT */ +#define COMPRESSION_DCS 32947 /* Kodak DCS encoding */ +#define COMPRESSION_JBIG 34661 /* ISO JBIG */ +#define COMPRESSION_SGILOG 34676 /* SGI Log Luminance RLE */ +#define COMPRESSION_SGILOG24 34677 /* SGI Log 24-bit packed */ +#define COMPRESSION_JP2000 34712 /* Leadtools JPEG2000 */ +#define COMPRESSION_LZMA 34925 /* LZMA2 */ +#define TIFFTAG_PHOTOMETRIC 262 /* photometric interpretation */ +#define PHOTOMETRIC_MINISWHITE 0 /* min value is white */ +#define PHOTOMETRIC_MINISBLACK 1 /* min value is black */ +#define PHOTOMETRIC_RGB 2 /* RGB color model */ +#define PHOTOMETRIC_PALETTE 3 /* color map indexed */ +#define PHOTOMETRIC_MASK 4 /* $holdout mask */ +#define PHOTOMETRIC_SEPARATED 5 /* !color separations */ +#define PHOTOMETRIC_YCBCR 6 /* !CCIR 601 */ +#define PHOTOMETRIC_CIELAB 8 /* !1976 CIE L*a*b* */ +#define PHOTOMETRIC_ICCLAB 9 /* ICC L*a*b* [Adobe TIFF Technote 4] */ +#define PHOTOMETRIC_ITULAB 10 /* ITU L*a*b* */ +#define PHOTOMETRIC_CFA 32803 /* color filter array */ +#define PHOTOMETRIC_LOGL 32844 /* CIE Log2(L) */ +#define PHOTOMETRIC_LOGLUV 32845 /* CIE Log2(L) (u',v') */ +#define TIFFTAG_THRESHHOLDING 263 /* +thresholding used on data */ +#define THRESHHOLD_BILEVEL 1 /* b&w art scan */ +#define THRESHHOLD_HALFTONE 2 /* or dithered scan */ +#define THRESHHOLD_ERRORDIFFUSE 3 /* usually floyd-steinberg */ +#define TIFFTAG_CELLWIDTH 264 /* +dithering matrix width */ +#define TIFFTAG_CELLLENGTH 265 /* +dithering matrix height */ +#define TIFFTAG_FILLORDER 266 /* data order within a byte */ +#define FILLORDER_MSB2LSB 1 /* most significant -> least */ +#define FILLORDER_LSB2MSB 2 /* least significant -> most */ +#define TIFFTAG_DOCUMENTNAME 269 /* name of doc. image is from */ +#define TIFFTAG_IMAGEDESCRIPTION 270 /* info about image */ +#define TIFFTAG_MAKE 271 /* scanner manufacturer name */ +#define TIFFTAG_MODEL 272 /* scanner model name/number */ +#define TIFFTAG_STRIPOFFSETS 273 /* offsets to data strips */ +#define TIFFTAG_ORIENTATION 274 /* +image orientation */ +#define ORIENTATION_TOPLEFT 1 /* row 0 top, col 0 lhs */ +#define ORIENTATION_TOPRIGHT 2 /* row 0 top, col 0 rhs */ +#define ORIENTATION_BOTRIGHT 3 /* row 0 bottom, col 0 rhs */ +#define ORIENTATION_BOTLEFT 4 /* row 0 bottom, col 0 lhs */ +#define ORIENTATION_LEFTTOP 5 /* row 0 lhs, col 0 top */ +#define ORIENTATION_RIGHTTOP 6 /* row 0 rhs, col 0 top */ +#define ORIENTATION_RIGHTBOT 7 /* row 0 rhs, col 0 bottom */ +#define ORIENTATION_LEFTBOT 8 /* row 0 lhs, col 0 bottom */ +#define TIFFTAG_SAMPLESPERPIXEL 277 /* samples per pixel */ +#define TIFFTAG_ROWSPERSTRIP 278 /* rows per strip of data */ +#define TIFFTAG_STRIPBYTECOUNTS 279 /* bytes counts for strips */ +#define TIFFTAG_MINSAMPLEVALUE 280 /* +minimum sample value */ +#define TIFFTAG_MAXSAMPLEVALUE 281 /* +maximum sample value */ +#define TIFFTAG_XRESOLUTION 282 /* pixels/resolution in x */ +#define TIFFTAG_YRESOLUTION 283 /* pixels/resolution in y */ +#define TIFFTAG_PLANARCONFIG 284 /* storage organization */ +#define PLANARCONFIG_CONTIG 1 /* single image plane */ +#define PLANARCONFIG_SEPARATE 2 /* separate planes of data */ +#define TIFFTAG_PAGENAME 285 /* page name image is from */ +#define TIFFTAG_XPOSITION 286 /* x page offset of image lhs */ +#define TIFFTAG_YPOSITION 287 /* y page offset of image lhs */ +#define TIFFTAG_FREEOFFSETS 288 /* +byte offset to free block */ +#define TIFFTAG_FREEBYTECOUNTS 289 /* +sizes of free blocks */ +#define TIFFTAG_GRAYRESPONSEUNIT 290 /* $gray scale curve accuracy */ +#define GRAYRESPONSEUNIT_10S 1 /* tenths of a unit */ +#define GRAYRESPONSEUNIT_100S 2 /* hundredths of a unit */ +#define GRAYRESPONSEUNIT_1000S 3 /* thousandths of a unit */ +#define GRAYRESPONSEUNIT_10000S 4 /* ten-thousandths of a unit */ +#define GRAYRESPONSEUNIT_100000S 5 /* hundred-thousandths */ +#define TIFFTAG_GRAYRESPONSECURVE 291 /* $gray scale response curve */ +#define TIFFTAG_GROUP3OPTIONS 292 /* 32 flag bits */ +#define TIFFTAG_T4OPTIONS 292 /* TIFF 6.0 proper name alias */ +#define GROUP3OPT_2DENCODING 0x1 /* 2-dimensional coding */ +#define GROUP3OPT_UNCOMPRESSED 0x2 /* data not compressed */ +#define GROUP3OPT_FILLBITS 0x4 /* fill to byte boundary */ +#define TIFFTAG_GROUP4OPTIONS 293 /* 32 flag bits */ +#define TIFFTAG_T6OPTIONS 293 /* TIFF 6.0 proper name */ +#define GROUP4OPT_UNCOMPRESSED 0x2 /* data not compressed */ +#define TIFFTAG_RESOLUTIONUNIT 296 /* units of resolutions */ +#define RESUNIT_NONE 1 /* no meaningful units */ +#define RESUNIT_INCH 2 /* english */ +#define RESUNIT_CENTIMETER 3 /* metric */ +#define TIFFTAG_PAGENUMBER 297 /* page numbers of multi-page */ +#define TIFFTAG_COLORRESPONSEUNIT 300 /* $color curve accuracy */ +#define COLORRESPONSEUNIT_10S 1 /* tenths of a unit */ +#define COLORRESPONSEUNIT_100S 2 /* hundredths of a unit */ +#define COLORRESPONSEUNIT_1000S 3 /* thousandths of a unit */ +#define COLORRESPONSEUNIT_10000S 4 /* ten-thousandths of a unit */ +#define COLORRESPONSEUNIT_100000S 5 /* hundred-thousandths */ +#define TIFFTAG_TRANSFERFUNCTION 301 /* !colorimetry info */ +#define TIFFTAG_SOFTWARE 305 /* name & release */ +#define TIFFTAG_DATETIME 306 /* creation date and time */ +#define TIFFTAG_ARTIST 315 /* creator of image */ +#define TIFFTAG_HOSTCOMPUTER 316 /* machine where created */ +#define TIFFTAG_PREDICTOR 317 /* prediction scheme w/ LZW */ +#define PREDICTOR_NONE 1 /* no prediction scheme used */ +#define PREDICTOR_HORIZONTAL 2 /* horizontal differencing */ +#define PREDICTOR_FLOATINGPOINT 3 /* floating point predictor */ +#define TIFFTAG_WHITEPOINT 318 /* image white point */ +#define TIFFTAG_PRIMARYCHROMATICITIES 319 /* !primary chromaticities */ +#define TIFFTAG_COLORMAP 320 /* RGB map for pallette image */ +#define TIFFTAG_HALFTONEHINTS 321 /* !highlight+shadow info */ +#define TIFFTAG_TILEWIDTH 322 /* !tile width in pixels */ +#define TIFFTAG_TILELENGTH 323 /* !tile height in pixels */ +#define TIFFTAG_TILEOFFSETS 324 /* !offsets to data tiles */ +#define TIFFTAG_TILEBYTECOUNTS 325 /* !byte counts for tiles */ +#define TIFFTAG_BADFAXLINES 326 /* lines w/ wrong pixel count */ +#define TIFFTAG_CLEANFAXDATA 327 /* regenerated line info */ +#define CLEANFAXDATA_CLEAN 0 /* no errors detected */ +#define CLEANFAXDATA_REGENERATED 1 /* receiver regenerated lines */ +#define CLEANFAXDATA_UNCLEAN 2 /* uncorrected errors exist */ +#define TIFFTAG_CONSECUTIVEBADFAXLINES 328 /* max consecutive bad lines */ +#define TIFFTAG_SUBIFD 330 /* subimage descriptors */ +#define TIFFTAG_INKSET 332 /* !inks in separated image */ +#define INKSET_CMYK 1 /* !cyan-magenta-yellow-black color */ +#define INKSET_MULTIINK 2 /* !multi-ink or hi-fi color */ +#define TIFFTAG_INKNAMES 333 /* !ascii names of inks */ +#define TIFFTAG_NUMBEROFINKS 334 /* !number of inks */ +#define TIFFTAG_DOTRANGE 336 /* !0% and 100% dot codes */ +#define TIFFTAG_TARGETPRINTER 337 /* !separation target */ +#define TIFFTAG_EXTRASAMPLES 338 /* !info about extra samples */ +#define EXTRASAMPLE_UNSPECIFIED 0 /* !unspecified data */ +#define EXTRASAMPLE_ASSOCALPHA 1 /* !associated alpha data */ +#define EXTRASAMPLE_UNASSALPHA 2 /* !unassociated alpha data */ +#define TIFFTAG_SAMPLEFORMAT 339 /* !data sample format */ +#define SAMPLEFORMAT_UINT 1 /* !unsigned integer data */ +#define SAMPLEFORMAT_INT 2 /* !signed integer data */ +#define SAMPLEFORMAT_IEEEFP 3 /* !IEEE floating point data */ +#define SAMPLEFORMAT_VOID 4 /* !untyped data */ +#define SAMPLEFORMAT_COMPLEXINT 5 /* !complex signed int */ +#define SAMPLEFORMAT_COMPLEXIEEEFP 6 /* !complex ieee floating */ +#define TIFFTAG_SMINSAMPLEVALUE 340 /* !variable MinSampleValue */ +#define TIFFTAG_SMAXSAMPLEVALUE 341 /* !variable MaxSampleValue */ +#define TIFFTAG_CLIPPATH 343 /* %ClipPath + [Adobe TIFF technote 2] */ +#define TIFFTAG_XCLIPPATHUNITS 344 /* %XClipPathUnits + [Adobe TIFF technote 2] */ +#define TIFFTAG_YCLIPPATHUNITS 345 /* %YClipPathUnits + [Adobe TIFF technote 2] */ +#define TIFFTAG_INDEXED 346 /* %Indexed + [Adobe TIFF Technote 3] */ +#define TIFFTAG_JPEGTABLES 347 /* %JPEG table stream */ +#define TIFFTAG_OPIPROXY 351 /* %OPI Proxy [Adobe TIFF technote] */ +/* Tags 400-435 are from the TIFF/FX spec */ +#define TIFFTAG_GLOBALPARAMETERSIFD 400 /* ! */ +#define TIFFTAG_PROFILETYPE 401 /* ! */ +#define PROFILETYPE_UNSPECIFIED 0 /* ! */ +#define PROFILETYPE_G3_FAX 1 /* ! */ +#define TIFFTAG_FAXPROFILE 402 /* ! */ +#define FAXPROFILE_S 1 /* !TIFF/FX FAX profile S */ +#define FAXPROFILE_F 2 /* !TIFF/FX FAX profile F */ +#define FAXPROFILE_J 3 /* !TIFF/FX FAX profile J */ +#define FAXPROFILE_C 4 /* !TIFF/FX FAX profile C */ +#define FAXPROFILE_L 5 /* !TIFF/FX FAX profile L */ +#define FAXPROFILE_M 6 /* !TIFF/FX FAX profile LM */ +#define TIFFTAG_CODINGMETHODS 403 /* !TIFF/FX coding methods */ +#define CODINGMETHODS_T4_1D (1 << 1) /* !T.4 1D */ +#define CODINGMETHODS_T4_2D (1 << 2) /* !T.4 2D */ +#define CODINGMETHODS_T6 (1 << 3) /* !T.6 */ +#define CODINGMETHODS_T85 (1 << 4) /* !T.85 JBIG */ +#define CODINGMETHODS_T42 (1 << 5) /* !T.42 JPEG */ +#define CODINGMETHODS_T43 (1 << 6) /* !T.43 colour by layered JBIG */ +#define TIFFTAG_VERSIONYEAR 404 /* !TIFF/FX version year */ +#define TIFFTAG_MODENUMBER 405 /* !TIFF/FX mode number */ +#define TIFFTAG_DECODE 433 /* !TIFF/FX decode */ +#define TIFFTAG_IMAGEBASECOLOR 434 /* !TIFF/FX image base colour */ +#define TIFFTAG_T82OPTIONS 435 /* !TIFF/FX T.82 options */ +/* + * Tags 512-521 are obsoleted by Technical Note #2 which specifies a + * revised JPEG-in-TIFF scheme. + */ +#define TIFFTAG_JPEGPROC 512 /* !JPEG processing algorithm */ +#define JPEGPROC_BASELINE 1 /* !baseline sequential */ +#define JPEGPROC_LOSSLESS 14 /* !Huffman coded lossless */ +#define TIFFTAG_JPEGIFOFFSET 513 /* !pointer to SOI marker */ +#define TIFFTAG_JPEGIFBYTECOUNT 514 /* !JFIF stream length */ +#define TIFFTAG_JPEGRESTARTINTERVAL 515 /* !restart interval length */ +#define TIFFTAG_JPEGLOSSLESSPREDICTORS 517 /* !lossless proc predictor */ +#define TIFFTAG_JPEGPOINTTRANSFORM 518 /* !lossless point transform */ +#define TIFFTAG_JPEGQTABLES 519 /* !Q matrice offsets */ +#define TIFFTAG_JPEGDCTABLES 520 /* !DCT table offsets */ +#define TIFFTAG_JPEGACTABLES 521 /* !AC coefficient offsets */ +#define TIFFTAG_YCBCRCOEFFICIENTS 529 /* !RGB -> YCbCr transform */ +#define TIFFTAG_YCBCRSUBSAMPLING 530 /* !YCbCr subsampling factors */ +#define TIFFTAG_YCBCRPOSITIONING 531 /* !subsample positioning */ +#define YCBCRPOSITION_CENTERED 1 /* !as in PostScript Level 2 */ +#define YCBCRPOSITION_COSITED 2 /* !as in CCIR 601-1 */ +#define TIFFTAG_REFERENCEBLACKWHITE 532 /* !colorimetry info */ +#define TIFFTAG_STRIPROWCOUNTS 559 /* !TIFF/FX strip row counts */ +#define TIFFTAG_XMLPACKET 700 /* %XML packet + [Adobe XMP Specification, + January 2004 */ +#define TIFFTAG_OPIIMAGEID 32781 /* %OPI ImageID + [Adobe TIFF technote] */ +/* tags 32952-32956 are private tags registered to Island Graphics */ +#define TIFFTAG_REFPTS 32953 /* image reference points */ +#define TIFFTAG_REGIONTACKPOINT 32954 /* region-xform tack point */ +#define TIFFTAG_REGIONWARPCORNERS 32955 /* warp quadrilateral */ +#define TIFFTAG_REGIONAFFINE 32956 /* affine transformation mat */ +/* tags 32995-32999 are private tags registered to SGI */ +#define TIFFTAG_MATTEING 32995 /* $use ExtraSamples */ +#define TIFFTAG_DATATYPE 32996 /* $use SampleFormat */ +#define TIFFTAG_IMAGEDEPTH 32997 /* z depth of image */ +#define TIFFTAG_TILEDEPTH 32998 /* z depth/data tile */ +/* tags 33300-33309 are private tags registered to Pixar */ +/* + * TIFFTAG_PIXAR_IMAGEFULLWIDTH and TIFFTAG_PIXAR_IMAGEFULLLENGTH + * are set when an image has been cropped out of a larger image. + * They reflect the size of the original uncropped image. + * The TIFFTAG_XPOSITION and TIFFTAG_YPOSITION can be used + * to determine the position of the smaller image in the larger one. + */ +#define TIFFTAG_PIXAR_IMAGEFULLWIDTH 33300 /* full image size in x */ +#define TIFFTAG_PIXAR_IMAGEFULLLENGTH 33301 /* full image size in y */ + /* Tags 33302-33306 are used to identify special image modes and data + * used by Pixar's texture formats. + */ +#define TIFFTAG_PIXAR_TEXTUREFORMAT 33302 /* texture map format */ +#define TIFFTAG_PIXAR_WRAPMODES 33303 /* s & t wrap modes */ +#define TIFFTAG_PIXAR_FOVCOT 33304 /* cotan(fov) for env. maps */ +#define TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN 33305 +#define TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA 33306 +/* tag 33405 is a private tag registered to Eastman Kodak */ +#define TIFFTAG_WRITERSERIALNUMBER 33405 /* device serial number */ +#define TIFFTAG_CFAREPEATPATTERNDIM 33421 /* dimensions of CFA pattern */ +#define TIFFTAG_CFAPATTERN 33422 /* color filter array pattern */ +/* tag 33432 is listed in the 6.0 spec w/ unknown ownership */ +#define TIFFTAG_COPYRIGHT 33432 /* copyright string */ +/* IPTC TAG from RichTIFF specifications */ +#define TIFFTAG_RICHTIFFIPTC 33723 +/* 34016-34029 are reserved for ANSI IT8 TIFF/IT */ +#define TIFFTAG_STONITS 37439 /* Sample value to Nits */ +/* tag 34929 is a private tag registered to FedEx */ +#define TIFFTAG_FEDEX_EDR 34929 /* unknown use */ +#define TIFFTAG_INTEROPERABILITYIFD 40965 /* Pointer to Interoperability private directory */ +/* Adobe Digital Negative (DNG) format tags */ +#define TIFFTAG_DNGVERSION 50706 /* &DNG version number */ +#define TIFFTAG_DNGBACKWARDVERSION 50707 /* &DNG compatibility version */ +#define TIFFTAG_UNIQUECAMERAMODEL 50708 /* &name for the camera model */ +#define TIFFTAG_LOCALIZEDCAMERAMODEL 50709 /* &localized camera model + name */ +#define TIFFTAG_CFAPLANECOLOR 50710 /* &CFAPattern->LinearRaw space + mapping */ +#define TIFFTAG_CFALAYOUT 50711 /* &spatial layout of the CFA */ +#define TIFFTAG_LINEARIZATIONTABLE 50712 /* &lookup table description */ +#define TIFFTAG_BLACKLEVELREPEATDIM 50713 /* &repeat pattern size for + the BlackLevel tag */ +#define TIFFTAG_BLACKLEVEL 50714 /* &zero light encoding level */ +#define TIFFTAG_BLACKLEVELDELTAH 50715 /* &zero light encoding level + differences (columns) */ +#define TIFFTAG_BLACKLEVELDELTAV 50716 /* &zero light encoding level + differences (rows) */ +#define TIFFTAG_WHITELEVEL 50717 /* &fully saturated encoding + level */ +#define TIFFTAG_DEFAULTSCALE 50718 /* &default scale factors */ +#define TIFFTAG_DEFAULTCROPORIGIN 50719 /* &origin of the final image + area */ +#define TIFFTAG_DEFAULTCROPSIZE 50720 /* &size of the final image + area */ +#define TIFFTAG_COLORMATRIX1 50721 /* &XYZ->reference color space + transformation matrix 1 */ +#define TIFFTAG_COLORMATRIX2 50722 /* &XYZ->reference color space + transformation matrix 2 */ +#define TIFFTAG_CAMERACALIBRATION1 50723 /* &calibration matrix 1 */ +#define TIFFTAG_CAMERACALIBRATION2 50724 /* &calibration matrix 2 */ +#define TIFFTAG_REDUCTIONMATRIX1 50725 /* &dimensionality reduction + matrix 1 */ +#define TIFFTAG_REDUCTIONMATRIX2 50726 /* &dimensionality reduction + matrix 2 */ +#define TIFFTAG_ANALOGBALANCE 50727 /* &gain applied the stored raw + values*/ +#define TIFFTAG_ASSHOTNEUTRAL 50728 /* &selected white balance in + linear reference space */ +#define TIFFTAG_ASSHOTWHITEXY 50729 /* &selected white balance in + x-y chromaticity + coordinates */ +#define TIFFTAG_BASELINEEXPOSURE 50730 /* &how much to move the zero + point */ +#define TIFFTAG_BASELINENOISE 50731 /* &relative noise level */ +#define TIFFTAG_BASELINESHARPNESS 50732 /* &relative amount of + sharpening */ +#define TIFFTAG_BAYERGREENSPLIT 50733 /* &how closely the values of + the green pixels in the + blue/green rows track the + values of the green pixels + in the red/green rows */ +#define TIFFTAG_LINEARRESPONSELIMIT 50734 /* &non-linear encoding range */ +#define TIFFTAG_CAMERASERIALNUMBER 50735 /* &camera's serial number */ +#define TIFFTAG_LENSINFO 50736 /* info about the lens */ +#define TIFFTAG_CHROMABLURRADIUS 50737 /* &chroma blur radius */ +#define TIFFTAG_ANTIALIASSTRENGTH 50738 /* &relative strength of the + camera's anti-alias filter */ +#define TIFFTAG_SHADOWSCALE 50739 /* &used by Adobe Camera Raw */ +#define TIFFTAG_DNGPRIVATEDATA 50740 /* &manufacturer's private data */ +#define TIFFTAG_MAKERNOTESAFETY 50741 /* &whether the EXIF MakerNote + tag is safe to preserve + along with the rest of the + EXIF data */ +#define TIFFTAG_CALIBRATIONILLUMINANT1 50778 /* &illuminant 1 */ +#define TIFFTAG_CALIBRATIONILLUMINANT2 50779 /* &illuminant 2 */ +#define TIFFTAG_BESTQUALITYSCALE 50780 /* &best quality multiplier */ +#define TIFFTAG_RAWDATAUNIQUEID 50781 /* &unique identifier for + the raw image data */ +#define TIFFTAG_ORIGINALRAWFILENAME 50827 /* &file name of the original + raw file */ +#define TIFFTAG_ORIGINALRAWFILEDATA 50828 /* &contents of the original + raw file */ +#define TIFFTAG_ACTIVEAREA 50829 /* &active (non-masked) pixels + of the sensor */ +#define TIFFTAG_MASKEDAREAS 50830 /* &list of coordinates + of fully masked pixels */ +#define TIFFTAG_ASSHOTICCPROFILE 50831 /* &these two tags used to */ +#define TIFFTAG_ASSHOTPREPROFILEMATRIX 50832 /* map cameras's color space + into ICC profile space */ +#define TIFFTAG_CURRENTICCPROFILE 50833 /* & */ +#define TIFFTAG_CURRENTPREPROFILEMATRIX 50834 /* & */ +/* tag 65535 is an undefined tag used by Eastman Kodak */ +#define TIFFTAG_DCSHUESHIFTVALUES 65535 /* hue shift correction data */ + +/* + * The following are ``pseudo tags'' that can be used to control + * codec-specific functionality. These tags are not written to file. + * Note that these values start at 0xffff+1 so that they'll never + * collide with Aldus-assigned tags. + * + * If you want your private pseudo tags ``registered'' (i.e. added to + * this file), please post a bug report via the tracking system at + * http://www.remotesensing.org/libtiff/bugs.html with the appropriate + * C definitions to add. + */ +#define TIFFTAG_FAXMODE 65536 /* Group 3/4 format control */ +#define FAXMODE_CLASSIC 0x0000 /* default, include RTC */ +#define FAXMODE_NORTC 0x0001 /* no RTC at end of data */ +#define FAXMODE_NOEOL 0x0002 /* no EOL code at end of row */ +#define FAXMODE_BYTEALIGN 0x0004 /* byte align row */ +#define FAXMODE_WORDALIGN 0x0008 /* word align row */ +#define FAXMODE_CLASSF FAXMODE_NORTC /* TIFF Class F */ +#define TIFFTAG_JPEGQUALITY 65537 /* Compression quality level */ +/* Note: quality level is on the IJG 0-100 scale. Default value is 75 */ +#define TIFFTAG_JPEGCOLORMODE 65538 /* Auto RGB<=>YCbCr convert? */ +#define JPEGCOLORMODE_RAW 0x0000 /* no conversion (default) */ +#define JPEGCOLORMODE_RGB 0x0001 /* do auto conversion */ +#define TIFFTAG_JPEGTABLESMODE 65539 /* What to put in JPEGTables */ +#define JPEGTABLESMODE_QUANT 0x0001 /* include quantization tbls */ +#define JPEGTABLESMODE_HUFF 0x0002 /* include Huffman tbls */ +/* Note: default is JPEGTABLESMODE_QUANT | JPEGTABLESMODE_HUFF */ +#define TIFFTAG_FAXFILLFUNC 65540 /* G3/G4 fill function */ +#define TIFFTAG_PIXARLOGDATAFMT 65549 /* PixarLogCodec I/O data sz */ +#define PIXARLOGDATAFMT_8BIT 0 /* regular u_char samples */ +#define PIXARLOGDATAFMT_8BITABGR 1 /* ABGR-order u_chars */ +#define PIXARLOGDATAFMT_11BITLOG 2 /* 11-bit log-encoded (raw) */ +#define PIXARLOGDATAFMT_12BITPICIO 3 /* as per PICIO (1.0==2048) */ +#define PIXARLOGDATAFMT_16BIT 4 /* signed short samples */ +#define PIXARLOGDATAFMT_FLOAT 5 /* IEEE float samples */ +/* 65550-65556 are allocated to Oceana Matrix */ +#define TIFFTAG_DCSIMAGERTYPE 65550 /* imager model & filter */ +#define DCSIMAGERMODEL_M3 0 /* M3 chip (1280 x 1024) */ +#define DCSIMAGERMODEL_M5 1 /* M5 chip (1536 x 1024) */ +#define DCSIMAGERMODEL_M6 2 /* M6 chip (3072 x 2048) */ +#define DCSIMAGERFILTER_IR 0 /* infrared filter */ +#define DCSIMAGERFILTER_MONO 1 /* monochrome filter */ +#define DCSIMAGERFILTER_CFA 2 /* color filter array */ +#define DCSIMAGERFILTER_OTHER 3 /* other filter */ +#define TIFFTAG_DCSINTERPMODE 65551 /* interpolation mode */ +#define DCSINTERPMODE_NORMAL 0x0 /* whole image, default */ +#define DCSINTERPMODE_PREVIEW 0x1 /* preview of image (384x256) */ +#define TIFFTAG_DCSBALANCEARRAY 65552 /* color balance values */ +#define TIFFTAG_DCSCORRECTMATRIX 65553 /* color correction values */ +#define TIFFTAG_DCSGAMMA 65554 /* gamma value */ +#define TIFFTAG_DCSTOESHOULDERPTS 65555 /* toe & shoulder points */ +#define TIFFTAG_DCSCALIBRATIONFD 65556 /* calibration file desc */ +/* Note: quality level is on the ZLIB 1-9 scale. Default value is -1 */ +#define TIFFTAG_ZIPQUALITY 65557 /* compression quality level */ +#define TIFFTAG_PIXARLOGQUALITY 65558 /* PixarLog uses same scale */ +/* 65559 is allocated to Oceana Matrix */ +#define TIFFTAG_DCSCLIPRECTANGLE 65559 /* area of image to acquire */ +#define TIFFTAG_SGILOGDATAFMT 65560 /* SGILog user data format */ +#define SGILOGDATAFMT_FLOAT 0 /* IEEE float samples */ +#define SGILOGDATAFMT_16BIT 1 /* 16-bit samples */ +#define SGILOGDATAFMT_RAW 2 /* uninterpreted data */ +#define SGILOGDATAFMT_8BIT 3 /* 8-bit RGB monitor values */ +#define TIFFTAG_SGILOGENCODE 65561 /* SGILog data encoding control*/ +#define SGILOGENCODE_NODITHER 0 /* do not dither encoded values*/ +#define SGILOGENCODE_RANDITHER 1 /* randomly dither encd values */ +#define TIFFTAG_LZMAPRESET 65562 /* LZMA2 preset (compression level) */ +#define TIFFTAG_PERSAMPLE 65563 /* interface for per sample tags */ +#define PERSAMPLE_MERGED 0 /* present as a single value */ +#define PERSAMPLE_MULTI 1 /* present as multiple values */ + +/* + * EXIF tags + */ +#define EXIFTAG_EXPOSURETIME 33434 /* Exposure time */ +#define EXIFTAG_FNUMBER 33437 /* F number */ +#define EXIFTAG_EXPOSUREPROGRAM 34850 /* Exposure program */ +#define EXIFTAG_SPECTRALSENSITIVITY 34852 /* Spectral sensitivity */ +#define EXIFTAG_ISOSPEEDRATINGS 34855 /* ISO speed rating */ +#define EXIFTAG_OECF 34856 /* Optoelectric conversion + factor */ +#define EXIFTAG_EXIFVERSION 36864 /* Exif version */ +#define EXIFTAG_DATETIMEORIGINAL 36867 /* Date and time of original + data generation */ +#define EXIFTAG_DATETIMEDIGITIZED 36868 /* Date and time of digital + data generation */ +#define EXIFTAG_COMPONENTSCONFIGURATION 37121 /* Meaning of each component */ +#define EXIFTAG_COMPRESSEDBITSPERPIXEL 37122 /* Image compression mode */ +#define EXIFTAG_SHUTTERSPEEDVALUE 37377 /* Shutter speed */ +#define EXIFTAG_APERTUREVALUE 37378 /* Aperture */ +#define EXIFTAG_BRIGHTNESSVALUE 37379 /* Brightness */ +#define EXIFTAG_EXPOSUREBIASVALUE 37380 /* Exposure bias */ +#define EXIFTAG_MAXAPERTUREVALUE 37381 /* Maximum lens aperture */ +#define EXIFTAG_SUBJECTDISTANCE 37382 /* Subject distance */ +#define EXIFTAG_METERINGMODE 37383 /* Metering mode */ +#define EXIFTAG_LIGHTSOURCE 37384 /* Light source */ +#define EXIFTAG_FLASH 37385 /* Flash */ +#define EXIFTAG_FOCALLENGTH 37386 /* Lens focal length */ +#define EXIFTAG_SUBJECTAREA 37396 /* Subject area */ +#define EXIFTAG_MAKERNOTE 37500 /* Manufacturer notes */ +#define EXIFTAG_USERCOMMENT 37510 /* User comments */ +#define EXIFTAG_SUBSECTIME 37520 /* DateTime subseconds */ +#define EXIFTAG_SUBSECTIMEORIGINAL 37521 /* DateTimeOriginal subseconds */ +#define EXIFTAG_SUBSECTIMEDIGITIZED 37522 /* DateTimeDigitized subseconds */ +#define EXIFTAG_FLASHPIXVERSION 40960 /* Supported Flashpix version */ +#define EXIFTAG_COLORSPACE 40961 /* Color space information */ +#define EXIFTAG_PIXELXDIMENSION 40962 /* Valid image width */ +#define EXIFTAG_PIXELYDIMENSION 40963 /* Valid image height */ +#define EXIFTAG_RELATEDSOUNDFILE 40964 /* Related audio file */ +#define EXIFTAG_FLASHENERGY 41483 /* Flash energy */ +#define EXIFTAG_SPATIALFREQUENCYRESPONSE 41484 /* Spatial frequency response */ +#define EXIFTAG_FOCALPLANEXRESOLUTION 41486 /* Focal plane X resolution */ +#define EXIFTAG_FOCALPLANEYRESOLUTION 41487 /* Focal plane Y resolution */ +#define EXIFTAG_FOCALPLANERESOLUTIONUNIT 41488 /* Focal plane resolution unit */ +#define EXIFTAG_SUBJECTLOCATION 41492 /* Subject location */ +#define EXIFTAG_EXPOSUREINDEX 41493 /* Exposure index */ +#define EXIFTAG_SENSINGMETHOD 41495 /* Sensing method */ +#define EXIFTAG_FILESOURCE 41728 /* File source */ +#define EXIFTAG_SCENETYPE 41729 /* Scene type */ +#define EXIFTAG_CFAPATTERN 41730 /* CFA pattern */ +#define EXIFTAG_CUSTOMRENDERED 41985 /* Custom image processing */ +#define EXIFTAG_EXPOSUREMODE 41986 /* Exposure mode */ +#define EXIFTAG_WHITEBALANCE 41987 /* White balance */ +#define EXIFTAG_DIGITALZOOMRATIO 41988 /* Digital zoom ratio */ +#define EXIFTAG_FOCALLENGTHIN35MMFILM 41989 /* Focal length in 35 mm film */ +#define EXIFTAG_SCENECAPTURETYPE 41990 /* Scene capture type */ +#define EXIFTAG_GAINCONTROL 41991 /* Gain control */ +#define EXIFTAG_CONTRAST 41992 /* Contrast */ +#define EXIFTAG_SATURATION 41993 /* Saturation */ +#define EXIFTAG_SHARPNESS 41994 /* Sharpness */ +#define EXIFTAG_DEVICESETTINGDESCRIPTION 41995 /* Device settings description */ +#define EXIFTAG_SUBJECTDISTANCERANGE 41996 /* Subject distance range */ +#define EXIFTAG_GAINCONTROL 41991 /* Gain control */ +#define EXIFTAG_GAINCONTROL 41991 /* Gain control */ +#define EXIFTAG_IMAGEUNIQUEID 42016 /* Unique image ID */ + +#endif /* _TIFF_ */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiffconf.h b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiffconf.h new file mode 100644 index 000000000..848c44058 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiffconf.h @@ -0,0 +1,3 @@ +/* I'm not sure why tiff.h includes this instead of tif_config.h */ + +#include "tif_config.h" diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiffio.h b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiffio.h new file mode 100644 index 000000000..038b67013 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiffio.h @@ -0,0 +1,557 @@ +/* $Id: tiffio.h,v 1.91 2012-07-29 15:45:29 tgl Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _TIFFIO_ +#define _TIFFIO_ + +/* + * TIFF I/O Library Definitions. + */ +#include "tiff.h" +#include "tiffvers.h" + +/* + * TIFF is defined as an incomplete type to hide the + * library's internal data structures from clients. + */ +typedef struct tiff TIFF; + +/* + * The following typedefs define the intrinsic size of + * data types used in the *exported* interfaces. These + * definitions depend on the proper definition of types + * in tiff.h. Note also that the varargs interface used + * to pass tag types and values uses the types defined in + * tiff.h directly. + * + * NB: ttag_t is unsigned int and not unsigned short because + * ANSI C requires that the type before the ellipsis be a + * promoted type (i.e. one of int, unsigned int, pointer, + * or double) and because we defined pseudo-tags that are + * outside the range of legal Aldus-assigned tags. + * NB: tsize_t is int32 and not uint32 because some functions + * return -1. + * NB: toff_t is not off_t for many reasons; TIFFs max out at + * 32-bit file offsets, and BigTIFF maxes out at 64-bit + * offsets being the most important, and to ensure use of + * a consistently unsigned type across architectures. + * Prior to libtiff 4.0, this was an unsigned 32 bit type. + */ +/* + * this is the machine addressing size type, only it's signed, so make it + * int32 on 32bit machines, int64 on 64bit machines + */ +typedef TIFF_SSIZE_T tmsize_t; +typedef uint64 toff_t; /* file offset */ +/* the following are deprecated and should be replaced by their defining + counterparts */ +typedef uint32 ttag_t; /* directory tag */ +typedef uint16 tdir_t; /* directory index */ +typedef uint16 tsample_t; /* sample number */ +typedef uint32 tstrile_t; /* strip or tile number */ +typedef tstrile_t tstrip_t; /* strip number */ +typedef tstrile_t ttile_t; /* tile number */ +typedef tmsize_t tsize_t; /* i/o size in bytes */ +typedef void* tdata_t; /* image data ref */ + +#if !defined(__WIN32__) && (defined(_WIN32) || defined(WIN32)) +#define __WIN32__ +#endif + +/* + * On windows you should define USE_WIN32_FILEIO if you are using tif_win32.c + * or AVOID_WIN32_FILEIO if you are using something else (like tif_unix.c). + * + * By default tif_unix.c is assumed. + */ + +#if defined(_WINDOWS) || defined(__WIN32__) || defined(_Windows) +# if !defined(__CYGWIN) && !defined(AVOID_WIN32_FILEIO) && !defined(USE_WIN32_FILEIO) +# define AVOID_WIN32_FILEIO +# endif +#endif + +#if defined(USE_WIN32_FILEIO) +# define VC_EXTRALEAN +# include +# ifdef __WIN32__ +DECLARE_HANDLE(thandle_t); /* Win32 file handle */ +# else +typedef HFILE thandle_t; /* client data handle */ +# endif /* __WIN32__ */ +#else +typedef void* thandle_t; /* client data handle */ +#endif /* USE_WIN32_FILEIO */ + +/* + * Flags to pass to TIFFPrintDirectory to control + * printing of data structures that are potentially + * very large. Bit-or these flags to enable printing + * multiple items. + */ +#define TIFFPRINT_NONE 0x0 /* no extra info */ +#define TIFFPRINT_STRIPS 0x1 /* strips/tiles info */ +#define TIFFPRINT_CURVES 0x2 /* color/gray response curves */ +#define TIFFPRINT_COLORMAP 0x4 /* colormap */ +#define TIFFPRINT_JPEGQTABLES 0x100 /* JPEG Q matrices */ +#define TIFFPRINT_JPEGACTABLES 0x200 /* JPEG AC tables */ +#define TIFFPRINT_JPEGDCTABLES 0x200 /* JPEG DC tables */ + +/* + * Colour conversion stuff + */ + +/* reference white */ +#define D65_X0 (95.0470F) +#define D65_Y0 (100.0F) +#define D65_Z0 (108.8827F) + +#define D50_X0 (96.4250F) +#define D50_Y0 (100.0F) +#define D50_Z0 (82.4680F) + +/* Structure for holding information about a display device. */ + +typedef unsigned char TIFFRGBValue; /* 8-bit samples */ + +typedef struct { + float d_mat[3][3]; /* XYZ -> luminance matrix */ + float d_YCR; /* Light o/p for reference white */ + float d_YCG; + float d_YCB; + uint32 d_Vrwr; /* Pixel values for ref. white */ + uint32 d_Vrwg; + uint32 d_Vrwb; + float d_Y0R; /* Residual light for black pixel */ + float d_Y0G; + float d_Y0B; + float d_gammaR; /* Gamma values for the three guns */ + float d_gammaG; + float d_gammaB; +} TIFFDisplay; + +typedef struct { /* YCbCr->RGB support */ + TIFFRGBValue* clamptab; /* range clamping table */ + int* Cr_r_tab; + int* Cb_b_tab; + int32* Cr_g_tab; + int32* Cb_g_tab; + int32* Y_tab; +} TIFFYCbCrToRGB; + +typedef struct { /* CIE Lab 1976->RGB support */ + int range; /* Size of conversion table */ +#define CIELABTORGB_TABLE_RANGE 1500 + float rstep, gstep, bstep; + float X0, Y0, Z0; /* Reference white point */ + TIFFDisplay display; + float Yr2r[CIELABTORGB_TABLE_RANGE + 1]; /* Conversion of Yr to r */ + float Yg2g[CIELABTORGB_TABLE_RANGE + 1]; /* Conversion of Yg to g */ + float Yb2b[CIELABTORGB_TABLE_RANGE + 1]; /* Conversion of Yb to b */ +} TIFFCIELabToRGB; + +/* + * RGBA-style image support. + */ +typedef struct _TIFFRGBAImage TIFFRGBAImage; +/* + * The image reading and conversion routines invoke + * ``put routines'' to copy/image/whatever tiles of + * raw image data. A default set of routines are + * provided to convert/copy raw image data to 8-bit + * packed ABGR format rasters. Applications can supply + * alternate routines that unpack the data into a + * different format or, for example, unpack the data + * and draw the unpacked raster on the display. + */ +typedef void (*tileContigRoutine) + (TIFFRGBAImage*, uint32*, uint32, uint32, uint32, uint32, int32, int32, + unsigned char*); +typedef void (*tileSeparateRoutine) + (TIFFRGBAImage*, uint32*, uint32, uint32, uint32, uint32, int32, int32, + unsigned char*, unsigned char*, unsigned char*, unsigned char*); +/* + * RGBA-reader state. + */ +struct _TIFFRGBAImage { + TIFF* tif; /* image handle */ + int stoponerr; /* stop on read error */ + int isContig; /* data is packed/separate */ + int alpha; /* type of alpha data present */ + uint32 width; /* image width */ + uint32 height; /* image height */ + uint16 bitspersample; /* image bits/sample */ + uint16 samplesperpixel; /* image samples/pixel */ + uint16 orientation; /* image orientation */ + uint16 req_orientation; /* requested orientation */ + uint16 photometric; /* image photometric interp */ + uint16* redcmap; /* colormap pallete */ + uint16* greencmap; + uint16* bluecmap; + /* get image data routine */ + int (*get)(TIFFRGBAImage*, uint32*, uint32, uint32); + /* put decoded strip/tile */ + union { + void (*any)(TIFFRGBAImage*); + tileContigRoutine contig; + tileSeparateRoutine separate; + } put; + TIFFRGBValue* Map; /* sample mapping array */ + uint32** BWmap; /* black&white map */ + uint32** PALmap; /* palette image map */ + TIFFYCbCrToRGB* ycbcr; /* YCbCr conversion state */ + TIFFCIELabToRGB* cielab; /* CIE L*a*b conversion state */ + + uint8* UaToAa; /* Unassociated alpha to associated alpha convertion LUT */ + uint8* Bitdepth16To8; /* LUT for conversion from 16bit to 8bit values */ + + int row_offset; + int col_offset; +}; + +/* + * Macros for extracting components from the + * packed ABGR form returned by TIFFReadRGBAImage. + */ +#define TIFFGetR(abgr) ((abgr) & 0xff) +#define TIFFGetG(abgr) (((abgr) >> 8) & 0xff) +#define TIFFGetB(abgr) (((abgr) >> 16) & 0xff) +#define TIFFGetA(abgr) (((abgr) >> 24) & 0xff) + +/* + * A CODEC is a software package that implements decoding, + * encoding, or decoding+encoding of a compression algorithm. + * The library provides a collection of builtin codecs. + * More codecs may be registered through calls to the library + * and/or the builtin implementations may be overridden. + */ +typedef int (*TIFFInitMethod)(TIFF*, int); +typedef struct { + char* name; + uint16 scheme; + TIFFInitMethod init; +} TIFFCodec; + +#include +#include + +/* share internal LogLuv conversion routines? */ +#ifndef LOGLUV_PUBLIC +#define LOGLUV_PUBLIC 1 +#endif + +#if !defined(__GNUC__) && !defined(__attribute__) +# define __attribute__(x) /*nothing*/ +#endif + +#if defined(c_plusplus) || defined(__cplusplus) +extern "C" { +#endif +typedef void (*TIFFErrorHandler)(const char*, const char*, va_list); +typedef void (*TIFFErrorHandlerExt)(thandle_t, const char*, const char*, va_list); +typedef tmsize_t (*TIFFReadWriteProc)(thandle_t, void*, tmsize_t); +typedef toff_t (*TIFFSeekProc)(thandle_t, toff_t, int); +typedef int (*TIFFCloseProc)(thandle_t); +typedef toff_t (*TIFFSizeProc)(thandle_t); +typedef int (*TIFFMapFileProc)(thandle_t, void** base, toff_t* size); +typedef void (*TIFFUnmapFileProc)(thandle_t, void* base, toff_t size); +typedef void (*TIFFExtendProc)(TIFF*); + +extern const char* TIFFGetVersion(void); + +extern const TIFFCodec* TIFFFindCODEC(uint16); +extern TIFFCodec* TIFFRegisterCODEC(uint16, const char*, TIFFInitMethod); +extern void TIFFUnRegisterCODEC(TIFFCodec*); +extern int TIFFIsCODECConfigured(uint16); +extern TIFFCodec* TIFFGetConfiguredCODECs(void); + +/* + * Auxiliary functions. + */ + +extern void* _TIFFmalloc(tmsize_t s); +extern void* _TIFFrealloc(void* p, tmsize_t s); +extern void _TIFFmemset(void* p, int v, tmsize_t c); +extern void _TIFFmemcpy(void* d, const void* s, tmsize_t c); +extern int _TIFFmemcmp(const void* p1, const void* p2, tmsize_t c); +extern void _TIFFfree(void* p); + +/* +** Stuff, related to tag handling and creating custom tags. +*/ +extern int TIFFGetTagListCount( TIFF * ); +extern uint32 TIFFGetTagListEntry( TIFF *, int tag_index ); + +#define TIFF_ANY TIFF_NOTYPE /* for field descriptor searching */ +#define TIFF_VARIABLE -1 /* marker for variable length tags */ +#define TIFF_SPP -2 /* marker for SamplesPerPixel tags */ +#define TIFF_VARIABLE2 -3 /* marker for uint32 var-length tags */ + +#define FIELD_CUSTOM 65 + +typedef struct _TIFFField TIFFField; +typedef struct _TIFFFieldArray TIFFFieldArray; + +extern const TIFFField* TIFFFindField(TIFF *, uint32, TIFFDataType); +extern const TIFFField* TIFFFieldWithTag(TIFF*, uint32); +extern const TIFFField* TIFFFieldWithName(TIFF*, const char *); + +extern uint32 TIFFFieldTag(const TIFFField*); +extern const char* TIFFFieldName(const TIFFField*); +extern TIFFDataType TIFFFieldDataType(const TIFFField*); +extern int TIFFFieldPassCount(const TIFFField*); +extern int TIFFFieldReadCount(const TIFFField*); +extern int TIFFFieldWriteCount(const TIFFField*); + +typedef int (*TIFFVSetMethod)(TIFF*, uint32, va_list); +typedef int (*TIFFVGetMethod)(TIFF*, uint32, va_list); +typedef void (*TIFFPrintMethod)(TIFF*, FILE*, long); + +typedef struct { + TIFFVSetMethod vsetfield; /* tag set routine */ + TIFFVGetMethod vgetfield; /* tag get routine */ + TIFFPrintMethod printdir; /* directory print routine */ +} TIFFTagMethods; + +extern TIFFTagMethods *TIFFAccessTagMethods(TIFF *); +extern void *TIFFGetClientInfo(TIFF *, const char *); +extern void TIFFSetClientInfo(TIFF *, void *, const char *); + +extern void TIFFCleanup(TIFF* tif); +extern void TIFFClose(TIFF* tif); +extern int TIFFFlush(TIFF* tif); +extern int TIFFFlushData(TIFF* tif); +extern int TIFFGetField(TIFF* tif, uint32 tag, ...); +extern int TIFFVGetField(TIFF* tif, uint32 tag, va_list ap); +extern int TIFFGetFieldDefaulted(TIFF* tif, uint32 tag, ...); +extern int TIFFVGetFieldDefaulted(TIFF* tif, uint32 tag, va_list ap); +extern int TIFFReadDirectory(TIFF* tif); +extern int TIFFReadCustomDirectory(TIFF* tif, toff_t diroff, const TIFFFieldArray* infoarray); +extern int TIFFReadEXIFDirectory(TIFF* tif, toff_t diroff); +extern uint64 TIFFScanlineSize64(TIFF* tif); +extern tmsize_t TIFFScanlineSize(TIFF* tif); +extern uint64 TIFFRasterScanlineSize64(TIFF* tif); +extern tmsize_t TIFFRasterScanlineSize(TIFF* tif); +extern uint64 TIFFStripSize64(TIFF* tif); +extern tmsize_t TIFFStripSize(TIFF* tif); +extern uint64 TIFFRawStripSize64(TIFF* tif, uint32 strip); +extern tmsize_t TIFFRawStripSize(TIFF* tif, uint32 strip); +extern uint64 TIFFVStripSize64(TIFF* tif, uint32 nrows); +extern tmsize_t TIFFVStripSize(TIFF* tif, uint32 nrows); +extern uint64 TIFFTileRowSize64(TIFF* tif); +extern tmsize_t TIFFTileRowSize(TIFF* tif); +extern uint64 TIFFTileSize64(TIFF* tif); +extern tmsize_t TIFFTileSize(TIFF* tif); +extern uint64 TIFFVTileSize64(TIFF* tif, uint32 nrows); +extern tmsize_t TIFFVTileSize(TIFF* tif, uint32 nrows); +extern uint32 TIFFDefaultStripSize(TIFF* tif, uint32 request); +extern void TIFFDefaultTileSize(TIFF*, uint32*, uint32*); +extern int TIFFFileno(TIFF*); +extern int TIFFSetFileno(TIFF*, int); +extern thandle_t TIFFClientdata(TIFF*); +extern thandle_t TIFFSetClientdata(TIFF*, thandle_t); +extern int TIFFGetMode(TIFF*); +extern int TIFFSetMode(TIFF*, int); +extern int TIFFIsTiled(TIFF*); +extern int TIFFIsByteSwapped(TIFF*); +extern int TIFFIsUpSampled(TIFF*); +extern int TIFFIsMSB2LSB(TIFF*); +extern int TIFFIsBigEndian(TIFF*); +extern TIFFReadWriteProc TIFFGetReadProc(TIFF*); +extern TIFFReadWriteProc TIFFGetWriteProc(TIFF*); +extern TIFFSeekProc TIFFGetSeekProc(TIFF*); +extern TIFFCloseProc TIFFGetCloseProc(TIFF*); +extern TIFFSizeProc TIFFGetSizeProc(TIFF*); +extern TIFFMapFileProc TIFFGetMapFileProc(TIFF*); +extern TIFFUnmapFileProc TIFFGetUnmapFileProc(TIFF*); +extern uint32 TIFFCurrentRow(TIFF*); +extern uint16 TIFFCurrentDirectory(TIFF*); +extern uint16 TIFFNumberOfDirectories(TIFF*); +extern uint64 TIFFCurrentDirOffset(TIFF*); +extern uint32 TIFFCurrentStrip(TIFF*); +extern uint32 TIFFCurrentTile(TIFF* tif); +extern int TIFFReadBufferSetup(TIFF* tif, void* bp, tmsize_t size); +extern int TIFFWriteBufferSetup(TIFF* tif, void* bp, tmsize_t size); +extern int TIFFSetupStrips(TIFF *); +extern int TIFFWriteCheck(TIFF*, int, const char *); +extern void TIFFFreeDirectory(TIFF*); +extern int TIFFCreateDirectory(TIFF*); +extern int TIFFCreateCustomDirectory(TIFF*,const TIFFFieldArray*); +extern int TIFFCreateEXIFDirectory(TIFF*); +extern int TIFFLastDirectory(TIFF*); +extern int TIFFSetDirectory(TIFF*, uint16); +extern int TIFFSetSubDirectory(TIFF*, uint64); +extern int TIFFUnlinkDirectory(TIFF*, uint16); +extern int TIFFSetField(TIFF*, uint32, ...); +extern int TIFFVSetField(TIFF*, uint32, va_list); +extern int TIFFUnsetField(TIFF*, uint32); +extern int TIFFWriteDirectory(TIFF *); +extern int TIFFWriteCustomDirectory(TIFF *, uint64 *); +extern int TIFFCheckpointDirectory(TIFF *); +extern int TIFFRewriteDirectory(TIFF *); + +#if defined(c_plusplus) || defined(__cplusplus) +extern void TIFFPrintDirectory(TIFF*, FILE*, long = 0); +extern int TIFFReadScanline(TIFF* tif, void* buf, uint32 row, uint16 sample = 0); +extern int TIFFWriteScanline(TIFF* tif, void* buf, uint32 row, uint16 sample = 0); +extern int TIFFReadRGBAImage(TIFF*, uint32, uint32, uint32*, int = 0); +extern int TIFFReadRGBAImageOriented(TIFF*, uint32, uint32, uint32*, + int = ORIENTATION_BOTLEFT, int = 0); +#else +extern void TIFFPrintDirectory(TIFF*, FILE*, long); +extern int TIFFReadScanline(TIFF* tif, void* buf, uint32 row, uint16 sample); +extern int TIFFWriteScanline(TIFF* tif, void* buf, uint32 row, uint16 sample); +extern int TIFFReadRGBAImage(TIFF*, uint32, uint32, uint32*, int); +extern int TIFFReadRGBAImageOriented(TIFF*, uint32, uint32, uint32*, int, int); +#endif + +extern int TIFFReadRGBAStrip(TIFF*, uint32, uint32 * ); +extern int TIFFReadRGBATile(TIFF*, uint32, uint32, uint32 * ); +extern int TIFFRGBAImageOK(TIFF*, char [1024]); +extern int TIFFRGBAImageBegin(TIFFRGBAImage*, TIFF*, int, char [1024]); +extern int TIFFRGBAImageGet(TIFFRGBAImage*, uint32*, uint32, uint32); +extern void TIFFRGBAImageEnd(TIFFRGBAImage*); +extern TIFF* TIFFOpen(const char*, const char*); +# ifdef __WIN32__ +extern TIFF* TIFFOpenW(const wchar_t*, const char*); +# endif /* __WIN32__ */ +extern TIFF* TIFFFdOpen(int, const char*, const char*); +extern TIFF* TIFFClientOpen(const char*, const char*, + thandle_t, + TIFFReadWriteProc, TIFFReadWriteProc, + TIFFSeekProc, TIFFCloseProc, + TIFFSizeProc, + TIFFMapFileProc, TIFFUnmapFileProc); +extern const char* TIFFFileName(TIFF*); +extern const char* TIFFSetFileName(TIFF*, const char *); +extern void TIFFError(const char*, const char*, ...) __attribute__((__format__ (__printf__,2,3))); +extern void TIFFErrorExt(thandle_t, const char*, const char*, ...) __attribute__((__format__ (__printf__,3,4))); +extern void TIFFWarning(const char*, const char*, ...) __attribute__((__format__ (__printf__,2,3))); +extern void TIFFWarningExt(thandle_t, const char*, const char*, ...) __attribute__((__format__ (__printf__,3,4))); +extern TIFFErrorHandler TIFFSetErrorHandler(TIFFErrorHandler); +extern TIFFErrorHandlerExt TIFFSetErrorHandlerExt(TIFFErrorHandlerExt); +extern TIFFErrorHandler TIFFSetWarningHandler(TIFFErrorHandler); +extern TIFFErrorHandlerExt TIFFSetWarningHandlerExt(TIFFErrorHandlerExt); +extern TIFFExtendProc TIFFSetTagExtender(TIFFExtendProc); +extern uint32 TIFFComputeTile(TIFF* tif, uint32 x, uint32 y, uint32 z, uint16 s); +extern int TIFFCheckTile(TIFF* tif, uint32 x, uint32 y, uint32 z, uint16 s); +extern uint32 TIFFNumberOfTiles(TIFF*); +extern tmsize_t TIFFReadTile(TIFF* tif, void* buf, uint32 x, uint32 y, uint32 z, uint16 s); +extern tmsize_t TIFFWriteTile(TIFF* tif, void* buf, uint32 x, uint32 y, uint32 z, uint16 s); +extern uint32 TIFFComputeStrip(TIFF*, uint32, uint16); +extern uint32 TIFFNumberOfStrips(TIFF*); +extern tmsize_t TIFFReadEncodedStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size); +extern tmsize_t TIFFReadRawStrip(TIFF* tif, uint32 strip, void* buf, tmsize_t size); +extern tmsize_t TIFFReadEncodedTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size); +extern tmsize_t TIFFReadRawTile(TIFF* tif, uint32 tile, void* buf, tmsize_t size); +extern tmsize_t TIFFWriteEncodedStrip(TIFF* tif, uint32 strip, void* data, tmsize_t cc); +extern tmsize_t TIFFWriteRawStrip(TIFF* tif, uint32 strip, void* data, tmsize_t cc); +extern tmsize_t TIFFWriteEncodedTile(TIFF* tif, uint32 tile, void* data, tmsize_t cc); +extern tmsize_t TIFFWriteRawTile(TIFF* tif, uint32 tile, void* data, tmsize_t cc); +extern int TIFFDataWidth(TIFFDataType); /* table of tag datatype widths */ +extern void TIFFSetWriteOffset(TIFF* tif, toff_t off); +extern void TIFFSwabShort(uint16*); +extern void TIFFSwabLong(uint32*); +extern void TIFFSwabLong8(uint64*); +extern void TIFFSwabFloat(float*); +extern void TIFFSwabDouble(double*); +extern void TIFFSwabArrayOfShort(uint16* wp, tmsize_t n); +extern void TIFFSwabArrayOfTriples(uint8* tp, tmsize_t n); +extern void TIFFSwabArrayOfLong(uint32* lp, tmsize_t n); +extern void TIFFSwabArrayOfLong8(uint64* lp, tmsize_t n); +extern void TIFFSwabArrayOfFloat(float* fp, tmsize_t n); +extern void TIFFSwabArrayOfDouble(double* dp, tmsize_t n); +extern void TIFFReverseBits(uint8* cp, tmsize_t n); +extern const unsigned char* TIFFGetBitRevTable(int); + +#ifdef LOGLUV_PUBLIC +#define U_NEU 0.210526316 +#define V_NEU 0.473684211 +#define UVSCALE 410. +extern double LogL16toY(int); +extern double LogL10toY(int); +extern void XYZtoRGB24(float*, uint8*); +extern int uv_decode(double*, double*, int); +extern void LogLuv24toXYZ(uint32, float*); +extern void LogLuv32toXYZ(uint32, float*); +#if defined(c_plusplus) || defined(__cplusplus) +extern int LogL16fromY(double, int = SGILOGENCODE_NODITHER); +extern int LogL10fromY(double, int = SGILOGENCODE_NODITHER); +extern int uv_encode(double, double, int = SGILOGENCODE_NODITHER); +extern uint32 LogLuv24fromXYZ(float*, int = SGILOGENCODE_NODITHER); +extern uint32 LogLuv32fromXYZ(float*, int = SGILOGENCODE_NODITHER); +#else +extern int LogL16fromY(double, int); +extern int LogL10fromY(double, int); +extern int uv_encode(double, double, int); +extern uint32 LogLuv24fromXYZ(float*, int); +extern uint32 LogLuv32fromXYZ(float*, int); +#endif +#endif /* LOGLUV_PUBLIC */ + +extern int TIFFCIELabToRGBInit(TIFFCIELabToRGB*, const TIFFDisplay *, float*); +extern void TIFFCIELabToXYZ(TIFFCIELabToRGB *, uint32, int32, int32, + float *, float *, float *); +extern void TIFFXYZToRGB(TIFFCIELabToRGB *, float, float, float, + uint32 *, uint32 *, uint32 *); + +extern int TIFFYCbCrToRGBInit(TIFFYCbCrToRGB*, float*, float*); +extern void TIFFYCbCrtoRGB(TIFFYCbCrToRGB *, uint32, int32, int32, + uint32 *, uint32 *, uint32 *); + +/**************************************************************************** + * O B S O L E T E D I N T E R F A C E S + * + * Don't use this stuff in your applications, it may be removed in the future + * libtiff versions. + ****************************************************************************/ +typedef struct { + ttag_t field_tag; /* field's tag */ + short field_readcount; /* read count/TIFF_VARIABLE/TIFF_SPP */ + short field_writecount; /* write count/TIFF_VARIABLE */ + TIFFDataType field_type; /* type of associated data */ + unsigned short field_bit; /* bit in fieldsset bit vector */ + unsigned char field_oktochange; /* if true, can change while writing */ + unsigned char field_passcount; /* if true, pass dir count on set */ + char *field_name; /* ASCII name */ +} TIFFFieldInfo; + +extern int TIFFMergeFieldInfo(TIFF*, const TIFFFieldInfo[], uint32); + +#if defined(c_plusplus) || defined(__cplusplus) +} +#endif + +#endif /* _TIFFIO_ */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiffiop.h b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiffiop.h new file mode 100644 index 000000000..53357d852 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiffiop.h @@ -0,0 +1,367 @@ +/* $Id: tiffiop.h,v 1.84 2012-05-30 01:50:17 fwarmerdam Exp $ */ + +/* + * Copyright (c) 1988-1997 Sam Leffler + * Copyright (c) 1991-1997 Silicon Graphics, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that (i) the above copyright notices and this permission notice appear in + * all copies of the software and related documentation, and (ii) the names of + * Sam Leffler and Silicon Graphics may not be used in any advertising or + * publicity relating to the software without the specific, prior written + * permission of Sam Leffler and Silicon Graphics. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR + * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, + * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF + * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _TIFFIOP_ +#define _TIFFIOP_ +/* + * ``Library-private'' definitions. + */ + +#include "tif_config.h" + +#ifdef HAVE_FCNTL_H +# include +#endif + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#ifdef HAVE_STRING_H +# include +#endif + +#ifdef HAVE_ASSERT_H +# include +#else +# define assert(x) +#endif + +#ifdef HAVE_SEARCH_H +# include +#else +extern void *lfind(const void *, const void *, size_t *, size_t, + int (*)(const void *, const void *)); +#endif + +#include "tiffio.h" + +#include "tif_dir.h" + +#ifndef STRIP_SIZE_DEFAULT +# define STRIP_SIZE_DEFAULT 8192 +#endif + +#define streq(a,b) (strcmp(a,b) == 0) + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef struct client_info { + struct client_info *next; + void *data; + char *name; +} TIFFClientInfoLink; + +/* + * Typedefs for ``method pointers'' used internally. + * these are depriciated and provided only for backwards compatibility + */ +typedef unsigned char tidataval_t; /* internal image data value type */ +typedef tidataval_t* tidata_t; /* reference to internal image data */ + +typedef void (*TIFFVoidMethod)(TIFF*); +typedef int (*TIFFBoolMethod)(TIFF*); +typedef int (*TIFFPreMethod)(TIFF*, uint16); +typedef int (*TIFFCodeMethod)(TIFF* tif, uint8* buf, tmsize_t size, uint16 sample); +typedef int (*TIFFSeekMethod)(TIFF*, uint32); +typedef void (*TIFFPostMethod)(TIFF* tif, uint8* buf, tmsize_t size); +typedef uint32 (*TIFFStripMethod)(TIFF*, uint32); +typedef void (*TIFFTileMethod)(TIFF*, uint32*, uint32*); + +struct tiff { + char* tif_name; /* name of open file */ + int tif_fd; /* open file descriptor */ + int tif_mode; /* open mode (O_*) */ + uint32 tif_flags; + #define TIFF_FILLORDER 0x00003 /* natural bit fill order for machine */ + #define TIFF_DIRTYHEADER 0x00004 /* header must be written on close */ + #define TIFF_DIRTYDIRECT 0x00008 /* current directory must be written */ + #define TIFF_BUFFERSETUP 0x00010 /* data buffers setup */ + #define TIFF_CODERSETUP 0x00020 /* encoder/decoder setup done */ + #define TIFF_BEENWRITING 0x00040 /* written 1+ scanlines to file */ + #define TIFF_SWAB 0x00080 /* byte swap file information */ + #define TIFF_NOBITREV 0x00100 /* inhibit bit reversal logic */ + #define TIFF_MYBUFFER 0x00200 /* my raw data buffer; free on close */ + #define TIFF_ISTILED 0x00400 /* file is tile, not strip- based */ + #define TIFF_MAPPED 0x00800 /* file is mapped into memory */ + #define TIFF_POSTENCODE 0x01000 /* need call to postencode routine */ + #define TIFF_INSUBIFD 0x02000 /* currently writing a subifd */ + #define TIFF_UPSAMPLED 0x04000 /* library is doing data up-sampling */ + #define TIFF_STRIPCHOP 0x08000 /* enable strip chopping support */ + #define TIFF_HEADERONLY 0x10000 /* read header only, do not process the first directory */ + #define TIFF_NOREADRAW 0x20000 /* skip reading of raw uncompressed image data */ + #define TIFF_INCUSTOMIFD 0x40000 /* currently writing a custom IFD */ + #define TIFF_BIGTIFF 0x80000 /* read/write bigtiff */ + #define TIFF_BUF4WRITE 0x100000 /* rawcc bytes are for writing */ + #define TIFF_DIRTYSTRIP 0x200000 /* stripoffsets/stripbytecount dirty*/ + #define TIFF_PERSAMPLE 0x400000 /* get/set per sample tags as arrays */ + #define TIFF_BUFFERMMAP 0x800000 /* read buffer (tif_rawdata) points into mmap() memory */ + uint64 tif_diroff; /* file offset of current directory */ + uint64 tif_nextdiroff; /* file offset of following directory */ + uint64* tif_dirlist; /* list of offsets to already seen directories to prevent IFD looping */ + uint16 tif_dirlistsize; /* number of entires in offset list */ + uint16 tif_dirnumber; /* number of already seen directories */ + TIFFDirectory tif_dir; /* internal rep of current directory */ + TIFFDirectory tif_customdir; /* custom IFDs are separated from the main ones */ + union { + TIFFHeaderCommon common; + TIFFHeaderClassic classic; + TIFFHeaderBig big; + } tif_header; + uint16 tif_header_size; /* file's header block and its length */ + uint32 tif_row; /* current scanline */ + uint16 tif_curdir; /* current directory (index) */ + uint32 tif_curstrip; /* current strip for read/write */ + uint64 tif_curoff; /* current offset for read/write */ + uint64 tif_dataoff; /* current offset for writing dir */ + /* SubIFD support */ + uint16 tif_nsubifd; /* remaining subifds to write */ + uint64 tif_subifdoff; /* offset for patching SubIFD link */ + /* tiling support */ + uint32 tif_col; /* current column (offset by row too) */ + uint32 tif_curtile; /* current tile for read/write */ + tmsize_t tif_tilesize; /* # of bytes in a tile */ + /* compression scheme hooks */ + int tif_decodestatus; + TIFFBoolMethod tif_fixuptags; /* called in TIFFReadDirectory */ + TIFFBoolMethod tif_setupdecode; /* called once before predecode */ + TIFFPreMethod tif_predecode; /* pre- row/strip/tile decoding */ + TIFFBoolMethod tif_setupencode; /* called once before preencode */ + int tif_encodestatus; + TIFFPreMethod tif_preencode; /* pre- row/strip/tile encoding */ + TIFFBoolMethod tif_postencode; /* post- row/strip/tile encoding */ + TIFFCodeMethod tif_decoderow; /* scanline decoding routine */ + TIFFCodeMethod tif_encoderow; /* scanline encoding routine */ + TIFFCodeMethod tif_decodestrip; /* strip decoding routine */ + TIFFCodeMethod tif_encodestrip; /* strip encoding routine */ + TIFFCodeMethod tif_decodetile; /* tile decoding routine */ + TIFFCodeMethod tif_encodetile; /* tile encoding routine */ + TIFFVoidMethod tif_close; /* cleanup-on-close routine */ + TIFFSeekMethod tif_seek; /* position within a strip routine */ + TIFFVoidMethod tif_cleanup; /* cleanup state routine */ + TIFFStripMethod tif_defstripsize; /* calculate/constrain strip size */ + TIFFTileMethod tif_deftilesize; /* calculate/constrain tile size */ + uint8* tif_data; /* compression scheme private data */ + /* input/output buffering */ + tmsize_t tif_scanlinesize; /* # of bytes in a scanline */ + tmsize_t tif_scanlineskew; /* scanline skew for reading strips */ + uint8* tif_rawdata; /* raw data buffer */ + tmsize_t tif_rawdatasize; /* # of bytes in raw data buffer */ + tmsize_t tif_rawdataoff; /* rawdata offset within strip */ + tmsize_t tif_rawdataloaded;/* amount of data in rawdata */ + uint8* tif_rawcp; /* current spot in raw buffer */ + tmsize_t tif_rawcc; /* bytes unread from raw buffer */ + /* memory-mapped file support */ + uint8* tif_base; /* base of mapped file */ + tmsize_t tif_size; /* size of mapped file region (bytes, thus tmsize_t) */ + TIFFMapFileProc tif_mapproc; /* map file method */ + TIFFUnmapFileProc tif_unmapproc; /* unmap file method */ + /* input/output callback methods */ + thandle_t tif_clientdata; /* callback parameter */ + TIFFReadWriteProc tif_readproc; /* read method */ + TIFFReadWriteProc tif_writeproc; /* write method */ + TIFFSeekProc tif_seekproc; /* lseek method */ + TIFFCloseProc tif_closeproc; /* close method */ + TIFFSizeProc tif_sizeproc; /* filesize method */ + /* post-decoding support */ + TIFFPostMethod tif_postdecode; /* post decoding routine */ + /* tag support */ + TIFFField** tif_fields; /* sorted table of registered tags */ + size_t tif_nfields; /* # entries in registered tag table */ + const TIFFField* tif_foundfield; /* cached pointer to already found tag */ + TIFFTagMethods tif_tagmethods; /* tag get/set/print routines */ + TIFFClientInfoLink* tif_clientinfo; /* extra client information. */ + /* Backward compatibility stuff. We need these two fields for + * setting up an old tag extension scheme. */ + TIFFFieldArray* tif_fieldscompat; + size_t tif_nfieldscompat; +}; + +#define isPseudoTag(t) (t > 0xffff) /* is tag value normal or pseudo */ + +#define isTiled(tif) (((tif)->tif_flags & TIFF_ISTILED) != 0) +#define isMapped(tif) (((tif)->tif_flags & TIFF_MAPPED) != 0) +#define isFillOrder(tif, o) (((tif)->tif_flags & (o)) != 0) +#define isUpSampled(tif) (((tif)->tif_flags & TIFF_UPSAMPLED) != 0) +#define TIFFReadFile(tif, buf, size) \ + ((*(tif)->tif_readproc)((tif)->tif_clientdata,(buf),(size))) +#define TIFFWriteFile(tif, buf, size) \ + ((*(tif)->tif_writeproc)((tif)->tif_clientdata,(buf),(size))) +#define TIFFSeekFile(tif, off, whence) \ + ((*(tif)->tif_seekproc)((tif)->tif_clientdata,(off),(whence))) +#define TIFFCloseFile(tif) \ + ((*(tif)->tif_closeproc)((tif)->tif_clientdata)) +#define TIFFGetFileSize(tif) \ + ((*(tif)->tif_sizeproc)((tif)->tif_clientdata)) +#define TIFFMapFileContents(tif, paddr, psize) \ + ((*(tif)->tif_mapproc)((tif)->tif_clientdata,(paddr),(psize))) +#define TIFFUnmapFileContents(tif, addr, size) \ + ((*(tif)->tif_unmapproc)((tif)->tif_clientdata,(addr),(size))) + +/* + * Default Read/Seek/Write definitions. + */ +#ifndef ReadOK +#define ReadOK(tif, buf, size) \ + (TIFFReadFile((tif),(buf),(size))==(size)) +#endif +#ifndef SeekOK +#define SeekOK(tif, off) \ + (TIFFSeekFile((tif),(off),SEEK_SET)==(off)) +#endif +#ifndef WriteOK +#define WriteOK(tif, buf, size) \ + (TIFFWriteFile((tif),(buf),(size))==(size)) +#endif + +/* NB: the uint32 casts are to silence certain ANSI-C compilers */ +#define TIFFhowmany_32(x, y) (((uint32)x < (0xffffffff - (uint32)(y-1))) ? \ + ((((uint32)(x))+(((uint32)(y))-1))/((uint32)(y))) : \ + 0U) +#define TIFFhowmany8_32(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3) +#define TIFFroundup_32(x, y) (TIFFhowmany_32(x,y)*(y)) +#define TIFFhowmany_64(x, y) ((((uint64)(x))+(((uint64)(y))-1))/((uint64)(y))) +#define TIFFhowmany8_64(x) (((x)&0x07)?((uint64)(x)>>3)+1:(uint64)(x)>>3) +#define TIFFroundup_64(x, y) (TIFFhowmany_64(x,y)*(y)) + +/* Safe multiply which returns zero if there is an integer overflow */ +#define TIFFSafeMultiply(t,v,m) ((((t)(m) != (t)0) && (((t)(((v)*(m))/(m))) == (t)(v))) ? (t)((v)*(m)) : (t)0) + +#define TIFFmax(A,B) ((A)>(B)?(A):(B)) +#define TIFFmin(A,B) ((A)<(B)?(A):(B)) + +#define TIFFArrayCount(a) (sizeof (a) / sizeof ((a)[0])) + +#if defined(__cplusplus) +extern "C" { +#endif +extern int _TIFFgetMode(const char* mode, const char* module); +extern int _TIFFNoRowEncode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s); +extern int _TIFFNoStripEncode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s); +extern int _TIFFNoTileEncode(TIFF*, uint8* pp, tmsize_t cc, uint16 s); +extern int _TIFFNoRowDecode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s); +extern int _TIFFNoStripDecode(TIFF* tif, uint8* pp, tmsize_t cc, uint16 s); +extern int _TIFFNoTileDecode(TIFF*, uint8* pp, tmsize_t cc, uint16 s); +extern void _TIFFNoPostDecode(TIFF* tif, uint8* buf, tmsize_t cc); +extern int _TIFFNoPreCode(TIFF* tif, uint16 s); +extern int _TIFFNoSeek(TIFF* tif, uint32 off); +extern void _TIFFSwab16BitData(TIFF* tif, uint8* buf, tmsize_t cc); +extern void _TIFFSwab24BitData(TIFF* tif, uint8* buf, tmsize_t cc); +extern void _TIFFSwab32BitData(TIFF* tif, uint8* buf, tmsize_t cc); +extern void _TIFFSwab64BitData(TIFF* tif, uint8* buf, tmsize_t cc); +extern int TIFFFlushData1(TIFF* tif); +extern int TIFFDefaultDirectory(TIFF* tif); +extern void _TIFFSetDefaultCompressionState(TIFF* tif); +extern int _TIFFRewriteField(TIFF *, uint16, TIFFDataType, tmsize_t, void *); +extern int TIFFSetCompressionScheme(TIFF* tif, int scheme); +extern int TIFFSetDefaultCompressionState(TIFF* tif); +extern uint32 _TIFFDefaultStripSize(TIFF* tif, uint32 s); +extern void _TIFFDefaultTileSize(TIFF* tif, uint32* tw, uint32* th); +extern int _TIFFDataSize(TIFFDataType type); + +extern void _TIFFsetByteArray(void**, void*, uint32); +extern void _TIFFsetString(char**, char*); +extern void _TIFFsetShortArray(uint16**, uint16*, uint32); +extern void _TIFFsetLongArray(uint32**, uint32*, uint32); +extern void _TIFFsetFloatArray(float**, float*, uint32); +extern void _TIFFsetDoubleArray(double**, double*, uint32); + +extern void _TIFFprintAscii(FILE*, const char*); +extern void _TIFFprintAsciiTag(FILE*, const char*, const char*); + +extern TIFFErrorHandler _TIFFwarningHandler; +extern TIFFErrorHandler _TIFFerrorHandler; +extern TIFFErrorHandlerExt _TIFFwarningHandlerExt; +extern TIFFErrorHandlerExt _TIFFerrorHandlerExt; + +extern uint32 _TIFFMultiply32(TIFF*, uint32, uint32, const char*); +extern uint64 _TIFFMultiply64(TIFF*, uint64, uint64, const char*); +extern void* _TIFFCheckMalloc(TIFF*, tmsize_t, tmsize_t, const char*); +extern void* _TIFFCheckRealloc(TIFF*, void*, tmsize_t, tmsize_t, const char*); + +extern double _TIFFUInt64ToDouble(uint64); +extern float _TIFFUInt64ToFloat(uint64); + +extern int TIFFInitDumpMode(TIFF*, int); +#ifdef PACKBITS_SUPPORT +extern int TIFFInitPackBits(TIFF*, int); +#endif +#ifdef CCITT_SUPPORT +extern int TIFFInitCCITTRLE(TIFF*, int), TIFFInitCCITTRLEW(TIFF*, int); +extern int TIFFInitCCITTFax3(TIFF*, int), TIFFInitCCITTFax4(TIFF*, int); +#endif +#ifdef THUNDER_SUPPORT +extern int TIFFInitThunderScan(TIFF*, int); +#endif +#ifdef NEXT_SUPPORT +extern int TIFFInitNeXT(TIFF*, int); +#endif +#ifdef LZW_SUPPORT +extern int TIFFInitLZW(TIFF*, int); +#endif +#ifdef OJPEG_SUPPORT +extern int TIFFInitOJPEG(TIFF*, int); +#endif +#ifdef JPEG_SUPPORT +extern int TIFFInitJPEG(TIFF*, int); +#endif +#ifdef JBIG_SUPPORT +extern int TIFFInitJBIG(TIFF*, int); +#endif +#ifdef ZIP_SUPPORT +extern int TIFFInitZIP(TIFF*, int); +#endif +#ifdef PIXARLOG_SUPPORT +extern int TIFFInitPixarLog(TIFF*, int); +#endif +#ifdef LOGLUV_SUPPORT +extern int TIFFInitSGILog(TIFF*, int); +#endif +#ifdef LZMA_SUPPORT +extern int TIFFInitLZMA(TIFF*, int); +#endif +#ifdef VMS +extern const TIFFCodec _TIFFBuiltinCODECS[]; +#else +extern TIFFCodec _TIFFBuiltinCODECS[]; +#endif + +#if defined(__cplusplus) +} +#endif +#endif /* _TIFFIOP_ */ + +/* vim: set ts=8 sts=8 sw=8 noet: */ +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiffvers.h b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiffvers.h new file mode 100644 index 000000000..74f769c45 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/tiffvers.h @@ -0,0 +1,9 @@ +#define TIFFLIB_VERSION_STR "LIBTIFF, Version 4.0.4beta\nCopyright (c) 1988-1996 Sam Leffler\nCopyright (c) 1991-1996 Silicon Graphics, Inc." +/* + * This define can be used in code that requires + * compilation-related definitions specific to a + * version or versions of the library. Runtime + * version checking should be done based on the + * string returned by TIFFGetVersion. + */ +#define TIFFLIB_VERSION 20150126 diff --git a/bazaar/plugin/gdal/frmts/gtiff/libtiff/uvcode.h b/bazaar/plugin/gdal/frmts/gtiff/libtiff/uvcode.h new file mode 100644 index 000000000..50f11d7e0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/libtiff/uvcode.h @@ -0,0 +1,180 @@ +/* Version 1.0 generated April 7, 1997 by Greg Ward Larson, SGI */ +#define UV_SQSIZ (float)0.003500 +#define UV_NDIVS 16289 +#define UV_VSTART (float)0.016940 +#define UV_NVS 163 +static struct { + float ustart; + short nus, ncum; +} uv_row[UV_NVS] = { + { (float)0.247663, 4, 0 }, + { (float)0.243779, 6, 4 }, + { (float)0.241684, 7, 10 }, + { (float)0.237874, 9, 17 }, + { (float)0.235906, 10, 26 }, + { (float)0.232153, 12, 36 }, + { (float)0.228352, 14, 48 }, + { (float)0.226259, 15, 62 }, + { (float)0.222371, 17, 77 }, + { (float)0.220410, 18, 94 }, + { (float)0.214710, 21, 112 }, + { (float)0.212714, 22, 133 }, + { (float)0.210721, 23, 155 }, + { (float)0.204976, 26, 178 }, + { (float)0.202986, 27, 204 }, + { (float)0.199245, 29, 231 }, + { (float)0.195525, 31, 260 }, + { (float)0.193560, 32, 291 }, + { (float)0.189878, 34, 323 }, + { (float)0.186216, 36, 357 }, + { (float)0.186216, 36, 393 }, + { (float)0.182592, 38, 429 }, + { (float)0.179003, 40, 467 }, + { (float)0.175466, 42, 507 }, + { (float)0.172001, 44, 549 }, + { (float)0.172001, 44, 593 }, + { (float)0.168612, 46, 637 }, + { (float)0.168612, 46, 683 }, + { (float)0.163575, 49, 729 }, + { (float)0.158642, 52, 778 }, + { (float)0.158642, 52, 830 }, + { (float)0.158642, 52, 882 }, + { (float)0.153815, 55, 934 }, + { (float)0.153815, 55, 989 }, + { (float)0.149097, 58, 1044 }, + { (float)0.149097, 58, 1102 }, + { (float)0.142746, 62, 1160 }, + { (float)0.142746, 62, 1222 }, + { (float)0.142746, 62, 1284 }, + { (float)0.138270, 65, 1346 }, + { (float)0.138270, 65, 1411 }, + { (float)0.138270, 65, 1476 }, + { (float)0.132166, 69, 1541 }, + { (float)0.132166, 69, 1610 }, + { (float)0.126204, 73, 1679 }, + { (float)0.126204, 73, 1752 }, + { (float)0.126204, 73, 1825 }, + { (float)0.120381, 77, 1898 }, + { (float)0.120381, 77, 1975 }, + { (float)0.120381, 77, 2052 }, + { (float)0.120381, 77, 2129 }, + { (float)0.112962, 82, 2206 }, + { (float)0.112962, 82, 2288 }, + { (float)0.112962, 82, 2370 }, + { (float)0.107450, 86, 2452 }, + { (float)0.107450, 86, 2538 }, + { (float)0.107450, 86, 2624 }, + { (float)0.107450, 86, 2710 }, + { (float)0.100343, 91, 2796 }, + { (float)0.100343, 91, 2887 }, + { (float)0.100343, 91, 2978 }, + { (float)0.095126, 95, 3069 }, + { (float)0.095126, 95, 3164 }, + { (float)0.095126, 95, 3259 }, + { (float)0.095126, 95, 3354 }, + { (float)0.088276, 100, 3449 }, + { (float)0.088276, 100, 3549 }, + { (float)0.088276, 100, 3649 }, + { (float)0.088276, 100, 3749 }, + { (float)0.081523, 105, 3849 }, + { (float)0.081523, 105, 3954 }, + { (float)0.081523, 105, 4059 }, + { (float)0.081523, 105, 4164 }, + { (float)0.074861, 110, 4269 }, + { (float)0.074861, 110, 4379 }, + { (float)0.074861, 110, 4489 }, + { (float)0.074861, 110, 4599 }, + { (float)0.068290, 115, 4709 }, + { (float)0.068290, 115, 4824 }, + { (float)0.068290, 115, 4939 }, + { (float)0.068290, 115, 5054 }, + { (float)0.063573, 119, 5169 }, + { (float)0.063573, 119, 5288 }, + { (float)0.063573, 119, 5407 }, + { (float)0.063573, 119, 5526 }, + { (float)0.057219, 124, 5645 }, + { (float)0.057219, 124, 5769 }, + { (float)0.057219, 124, 5893 }, + { (float)0.057219, 124, 6017 }, + { (float)0.050985, 129, 6141 }, + { (float)0.050985, 129, 6270 }, + { (float)0.050985, 129, 6399 }, + { (float)0.050985, 129, 6528 }, + { (float)0.050985, 129, 6657 }, + { (float)0.044859, 134, 6786 }, + { (float)0.044859, 134, 6920 }, + { (float)0.044859, 134, 7054 }, + { (float)0.044859, 134, 7188 }, + { (float)0.040571, 138, 7322 }, + { (float)0.040571, 138, 7460 }, + { (float)0.040571, 138, 7598 }, + { (float)0.040571, 138, 7736 }, + { (float)0.036339, 142, 7874 }, + { (float)0.036339, 142, 8016 }, + { (float)0.036339, 142, 8158 }, + { (float)0.036339, 142, 8300 }, + { (float)0.032139, 146, 8442 }, + { (float)0.032139, 146, 8588 }, + { (float)0.032139, 146, 8734 }, + { (float)0.032139, 146, 8880 }, + { (float)0.027947, 150, 9026 }, + { (float)0.027947, 150, 9176 }, + { (float)0.027947, 150, 9326 }, + { (float)0.023739, 154, 9476 }, + { (float)0.023739, 154, 9630 }, + { (float)0.023739, 154, 9784 }, + { (float)0.023739, 154, 9938 }, + { (float)0.019504, 158, 10092 }, + { (float)0.019504, 158, 10250 }, + { (float)0.019504, 158, 10408 }, + { (float)0.016976, 161, 10566 }, + { (float)0.016976, 161, 10727 }, + { (float)0.016976, 161, 10888 }, + { (float)0.016976, 161, 11049 }, + { (float)0.012639, 165, 11210 }, + { (float)0.012639, 165, 11375 }, + { (float)0.012639, 165, 11540 }, + { (float)0.009991, 168, 11705 }, + { (float)0.009991, 168, 11873 }, + { (float)0.009991, 168, 12041 }, + { (float)0.009016, 170, 12209 }, + { (float)0.009016, 170, 12379 }, + { (float)0.009016, 170, 12549 }, + { (float)0.006217, 173, 12719 }, + { (float)0.006217, 173, 12892 }, + { (float)0.005097, 175, 13065 }, + { (float)0.005097, 175, 13240 }, + { (float)0.005097, 175, 13415 }, + { (float)0.003909, 177, 13590 }, + { (float)0.003909, 177, 13767 }, + { (float)0.002340, 177, 13944 }, + { (float)0.002389, 170, 14121 }, + { (float)0.001068, 164, 14291 }, + { (float)0.001653, 157, 14455 }, + { (float)0.000717, 150, 14612 }, + { (float)0.001614, 143, 14762 }, + { (float)0.000270, 136, 14905 }, + { (float)0.000484, 129, 15041 }, + { (float)0.001103, 123, 15170 }, + { (float)0.001242, 115, 15293 }, + { (float)0.001188, 109, 15408 }, + { (float)0.001011, 103, 15517 }, + { (float)0.000709, 97, 15620 }, + { (float)0.000301, 89, 15717 }, + { (float)0.002416, 82, 15806 }, + { (float)0.003251, 76, 15888 }, + { (float)0.003246, 69, 15964 }, + { (float)0.004141, 62, 16033 }, + { (float)0.005963, 55, 16095 }, + { (float)0.008839, 47, 16150 }, + { (float)0.010490, 40, 16197 }, + { (float)0.016994, 31, 16237 }, + { (float)0.023659, 21, 16268 }, +}; +/* + * Local Variables: + * mode: c + * c-basic-offset: 8 + * fill-column: 78 + * End: + */ diff --git a/bazaar/plugin/gdal/frmts/gtiff/makefile.vc b/bazaar/plugin/gdal/frmts/gtiff/makefile.vc new file mode 100644 index 000000000..a5223ecfd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/makefile.vc @@ -0,0 +1,50 @@ + +OBJ = geotiff.obj gt_wkt_srs.obj gt_overview.obj \ + tifvsi.obj tif_float.obj gt_citation.obj gt_jpeg_copy.obj + +EXTRAFLAGS = -I.. $(JPEG_FLAGS) $(TIFF_OPTS) $(TIFF_INC) $(GEOTIFF_INC) + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF TIFF_INC +SUB_TIFF_TARGET = +!ELSE +TIFF_INC = -Ilibtiff -DINTERNAL_LIBTIFF -DBIGTIFF_SUPPORT +SUB_TIFF_TARGET = tiff +!ENDIF + +!IFDEF GEOTIFF_INC +SUB_GEOTIFF_TARGET = +!ELSE +GEOTIFF_INC = -Ilibgeotiff -DINTERNAL_LIBGEOTIFF +SUB_GEOTIFF_TARGET = geotiff +!ENDIF + +!IFDEF JPEG_SUPPORTED +!IFDEF JPEG_EXTERNAL_LIB +JPEG_FLAGS = -I..\jpeg -I$(JPEGDIR) -DHAVE_LIBJPEG +!ELSE +JPEG_FLAGS = -I..\jpeg -I..\jpeg\libjpeg -DHAVE_LIBJPEG +!ENDIF +!ENDIF + +default: $(OBJ) $(SUB_TIFF_TARGET) $(SUB_GEOTIFF_TARGET) + xcopy /D /Y *.obj ..\o + +tiff: + cd libtiff + $(MAKE) /f makefile.vc + cd .. + +geotiff: + cd libgeotiff + $(MAKE) /f makefile.vc + cd .. + +clean: + -del *.obj libtiff\*.obj libgeotiff\*.obj + + + diff --git a/bazaar/plugin/gdal/frmts/gtiff/tif_float.c b/bazaar/plugin/gdal/frmts/gtiff/tif_float.c new file mode 100644 index 000000000..4bfe8de25 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/tif_float.c @@ -0,0 +1,193 @@ +/****************************************************************************** + * $Id: tif_float.c 21102 2010-11-08 20:47:38Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Floating point conversion functions. Convert 16- and 24-bit + * floating point numbers into the 32-bit IEEE 754 compliant ones. + * Author: Andrey Kiselev, dron@remotesensing.org + * + ****************************************************************************** + * Copyright (c) 2005, Andrey Kiselev + * + * This code is based on the code from OpenEXR project with the following + * copyright: + * + * Copyright (c) 2002, Industrial Light & Magic, a division of Lucas + * Digital Ltd. LLC + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Industrial Light & Magic nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#include "tif_float.h" + +/************************************************************************/ +/* HalfToFloat() */ +/* */ +/* 16-bit floating point number to 32-bit one. */ +/************************************************************************/ + +GUInt32 HalfToFloat( GUInt16 iHalf ) +{ + + GUInt32 iSign = (iHalf >> 15) & 0x00000001; + GUInt32 iExponent = (iHalf >> 10) & 0x0000001f; + GUInt32 iMantissa = iHalf & 0x000003ff; + + if (iExponent == 0) + { + if (iMantissa == 0) + { +/* -------------------------------------------------------------------- */ +/* Plus or minus zero. */ +/* -------------------------------------------------------------------- */ + + return iSign << 31; + } + else + { +/* -------------------------------------------------------------------- */ +/* Denormalized number -- renormalize it. */ +/* -------------------------------------------------------------------- */ + + while (!(iMantissa & 0x00000400)) + { + iMantissa <<= 1; + iExponent -= 1; + } + + iExponent += 1; + iMantissa &= ~0x00000400; + } + } + else if (iExponent == 31) + { + if (iMantissa == 0) + { +/* -------------------------------------------------------------------- */ +/* Positive or negative infinity. */ +/* -------------------------------------------------------------------- */ + + return (iSign << 31) | 0x7f800000; + } + else + { +/* -------------------------------------------------------------------- */ +/* NaN -- preserve sign and significand bits. */ +/* -------------------------------------------------------------------- */ + + return (iSign << 31) | 0x7f800000 | (iMantissa << 13); + } + } + +/* -------------------------------------------------------------------- */ +/* Normalized number. */ +/* -------------------------------------------------------------------- */ + + iExponent = iExponent + (127 - 15); + iMantissa = iMantissa << 13; + +/* -------------------------------------------------------------------- */ +/* Assemble sign, exponent and mantissa. */ +/* -------------------------------------------------------------------- */ + + return (iSign << 31) | (iExponent << 23) | iMantissa; +} + +/************************************************************************/ +/* TripleToFloat() */ +/* */ +/* 24-bit floating point number to 32-bit one. */ +/************************************************************************/ + +GUInt32 TripleToFloat( GUInt32 iTriple ) +{ + + GUInt32 iSign = (iTriple >> 23) & 0x00000001; + GUInt32 iExponent = (iTriple >> 16) & 0x0000007f; + GUInt32 iMantissa = iTriple & 0x0000ffff; + + if (iExponent == 0) + { + if (iMantissa == 0) + { +/* -------------------------------------------------------------------- */ +/* Plus or minus zero. */ +/* -------------------------------------------------------------------- */ + + return iSign << 31; + } + else + { +/* -------------------------------------------------------------------- */ +/* Denormalized number -- renormalize it. */ +/* -------------------------------------------------------------------- */ + + while (!(iMantissa & 0x00002000)) + { + iMantissa <<= 1; + iExponent -= 1; + } + + iExponent += 1; + iMantissa &= ~0x00002000; + } + } + else if (iExponent == 127) + { + if (iMantissa == 0) + { +/* -------------------------------------------------------------------- */ +/* Positive or negative infinity. */ +/* -------------------------------------------------------------------- */ + + return (iSign << 31) | 0x7f800000; + } + else + { +/* -------------------------------------------------------------------- */ +/* NaN -- preserve sign and significand bits. */ +/* -------------------------------------------------------------------- */ + + return (iSign << 31) | 0x7f800000 | (iMantissa << 7); + } + } + +/* -------------------------------------------------------------------- */ +/* Normalized number. */ +/* -------------------------------------------------------------------- */ + + iExponent = iExponent + (127 - 63); + iMantissa = iMantissa << 7; + +/* -------------------------------------------------------------------- */ +/* Assemble sign, exponent and mantissa. */ +/* -------------------------------------------------------------------- */ + + return (iSign << 31) | (iExponent << 23) | iMantissa; +} diff --git a/bazaar/plugin/gdal/frmts/gtiff/tif_float.h b/bazaar/plugin/gdal/frmts/gtiff/tif_float.h new file mode 100644 index 000000000..09162838b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/tif_float.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * $Id: tif_float.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Floating point conversion functions. Convert 16- and 24-bit + * floating point numbers into the 32-bit IEEE 754 compliant ones. + * Author: Andrey Kiselev, dron@remotesensing.org + * + ****************************************************************************** + * Copyright (c) 2005, Andrey Kiselev + * Copyright (c) 2010, Even Rouault + * + * This code is based on the code from OpenEXR project with the following + * copyright: + * + * Copyright (c) 2002, Industrial Light & Magic, a division of Lucas + * Digital Ltd. LLC + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Industrial Light & Magic nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef TIF_FLOAT_H_INCLUDED +#define TIF_FLOAT_H_INCLUDED + +#include "cpl_port.h" + +CPL_C_START +GUInt32 HalfToFloat( GUInt16 iHalf ); +GUInt32 TripleToFloat( GUInt32 iTriple ); +CPL_C_END + +#endif // TIF_FLOAT_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/gtiff/tifvsi.cpp b/bazaar/plugin/gdal/frmts/gtiff/tifvsi.cpp new file mode 100644 index 000000000..70b645476 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/tifvsi.cpp @@ -0,0 +1,292 @@ +/****************************************************************************** + * $Id: tifvsi.cpp 28873 2015-04-08 14:32:00Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Implement system hook functions for libtiff on top of CPL/VSI, + * including > 2GB support. Based on tif_unix.c from libtiff + * distribution. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam, warmerdam@pobox.com + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* + * TIFF Library UNIX-specific Routines. + */ +#include "cpl_vsi.h" +#include "cpl_conv.h" +#include "tifvsi.h" + +#include + +// We avoid including xtiffio.h since it drags in the libgeotiff version +// of the VSI functions. + +#ifdef RENAME_INTERNAL_LIBGEOTIFF_SYMBOLS +#include "gdal_libgeotiff_symbol_rename.h" +#endif + +CPL_C_START +extern TIFF CPL_DLL * XTIFFClientOpen(const char* name, const char* mode, + thandle_t thehandle, + TIFFReadWriteProc, TIFFReadWriteProc, + TIFFSeekProc, TIFFCloseProc, + TIFFSizeProc, + TIFFMapFileProc, TIFFUnmapFileProc); +CPL_C_END + +#define BUFFER_SIZE (65536) + +typedef struct +{ + VSILFILE* fpL; + int bAtEndOfFile; + vsi_l_offset nExpectedPos; + GByte *abyWriteBuffer; + int nWriteBufferSize; +} GDALTiffHandle; + +static tsize_t +_tiffReadProc(thandle_t th, tdata_t buf, tsize_t size) +{ + GDALTiffHandle* psGTH = (GDALTiffHandle*) th; + return VSIFReadL( buf, 1, size, psGTH->fpL ); +} + +static int GTHFlushBuffer(thandle_t th) +{ + GDALTiffHandle* psGTH = (GDALTiffHandle*) th; + int bRet = TRUE; + if( psGTH->abyWriteBuffer && psGTH->nWriteBufferSize ) + { + tsize_t nRet = VSIFWriteL( psGTH->abyWriteBuffer, 1, psGTH->nWriteBufferSize, psGTH->fpL ); + bRet = (nRet == psGTH->nWriteBufferSize); + if( !bRet ) + { + TIFFErrorExt( th, "_tiffWriteProc", "%s", VSIStrerror( errno ) ); + } + psGTH->nWriteBufferSize = 0; + } + return bRet; +} + + +static tsize_t +_tiffWriteProc(thandle_t th, tdata_t buf, tsize_t size) +{ + GDALTiffHandle* psGTH = (GDALTiffHandle*) th; + + // If we have a write buffer and are at end of file, then accumulate + // the bytes until the buffer is full + if( psGTH->bAtEndOfFile && psGTH->abyWriteBuffer ) + { + const GByte* pabyData = (const GByte*) buf; + tsize_t nRemainingBytes = size; + while( TRUE ) + { + if( psGTH->nWriteBufferSize + nRemainingBytes <= BUFFER_SIZE ) + { + memcpy( psGTH->abyWriteBuffer + psGTH->nWriteBufferSize, + pabyData, nRemainingBytes ); + psGTH->nWriteBufferSize += nRemainingBytes; + psGTH->nExpectedPos += size; + return size; + } + + int nAppendable = BUFFER_SIZE - psGTH->nWriteBufferSize; + memcpy( psGTH->abyWriteBuffer + psGTH->nWriteBufferSize, pabyData, + nAppendable ); + tsize_t nRet = VSIFWriteL( psGTH->abyWriteBuffer, 1, BUFFER_SIZE, psGTH->fpL ); + psGTH->nWriteBufferSize = 0; + if( nRet != BUFFER_SIZE ) + { + TIFFErrorExt( th, "_tiffWriteProc", "%s", VSIStrerror( errno ) ); + return 0; + } + + pabyData += nAppendable; + nRemainingBytes -= nAppendable; + } + } + + tsize_t nRet = VSIFWriteL( buf, 1, size, psGTH->fpL ); + if (nRet < size) + { + TIFFErrorExt( th, "_tiffWriteProc", "%s", VSIStrerror( errno ) ); + } + if( psGTH->bAtEndOfFile ) + { + psGTH->nExpectedPos += nRet; + } + return nRet; +} + +static toff_t +_tiffSeekProc(thandle_t th, toff_t off, int whence) +{ + GDALTiffHandle* psGTH = (GDALTiffHandle*) th; + + /* Optimization: if we are already at end, then no need to */ + /* issue a VSIFSeekL() */ + if( whence == SEEK_END ) + { + if( psGTH->bAtEndOfFile ) + { + return (toff_t) psGTH->nExpectedPos; + } + + if( VSIFSeekL( psGTH->fpL, off, whence ) != 0 ) + { + TIFFErrorExt( th, "_tiffSeekProc", "%s", VSIStrerror( errno ) ); + return (toff_t) -1; + } + psGTH->bAtEndOfFile = TRUE; + psGTH->nExpectedPos = VSIFTellL( psGTH->fpL ); + return (toff_t) (psGTH->nExpectedPos); + } + + GTHFlushBuffer(th); + psGTH->bAtEndOfFile = FALSE; + psGTH->nExpectedPos = 0; + + if( VSIFSeekL( psGTH->fpL, off, whence ) == 0 ) + return (toff_t) VSIFTellL( psGTH->fpL ); + else + { + TIFFErrorExt( th, "_tiffSeekProc", "%s", VSIStrerror( errno ) ); + return (toff_t) -1; + } +} + +static int +_tiffCloseProc(thandle_t th) +{ + GDALTiffHandle* psGTH = (GDALTiffHandle*) th; + GTHFlushBuffer(th); + CPLFree(psGTH->abyWriteBuffer); + CPLFree(psGTH); + return 0; +} + +static toff_t +_tiffSizeProc(thandle_t th) +{ + GDALTiffHandle* psGTH = (GDALTiffHandle*) th; + vsi_l_offset old_off; + toff_t file_size; + + if( psGTH->bAtEndOfFile ) + { + return (toff_t) psGTH->nExpectedPos; + } + + old_off = VSIFTellL( psGTH->fpL ); + VSIFSeekL( psGTH->fpL, 0, SEEK_END ); + + file_size = (toff_t) VSIFTellL( psGTH->fpL ); + VSIFSeekL( psGTH->fpL, old_off, SEEK_SET ); + + return file_size; +} + +static int +_tiffMapProc(thandle_t th, tdata_t* pbase, toff_t* psize) +{ + (void) th; (void) pbase; (void) psize; + return (0); +} + +static void +_tiffUnmapProc(thandle_t th, tdata_t base, toff_t size) +{ + (void) th; (void) base; (void) size; +} + +VSILFILE* VSI_TIFFGetVSILFile(thandle_t th) +{ + GDALTiffHandle* psGTH = (GDALTiffHandle*) th; + VSI_TIFFFlushBufferedWrite(th); + return psGTH->fpL; +} + +int VSI_TIFFFlushBufferedWrite(thandle_t th) +{ + GDALTiffHandle* psGTH = (GDALTiffHandle*) th; + psGTH->bAtEndOfFile = FALSE; + return GTHFlushBuffer(th); +} + +/* + * Open a TIFF file for read/writing. + */ +TIFF* VSI_TIFFOpen(const char* name, const char* mode, + VSILFILE* fpL) +{ + int i, a_out; + char access[32]; + TIFF *tif; + int bAllocBuffer = FALSE; + + a_out = 0; + access[0] = '\0'; + for( i = 0; mode[i] != '\0'; i++ ) + { + if( mode[i] == 'r' + || mode[i] == 'w' + || mode[i] == '+' + || mode[i] == 'a' ) + { + access[a_out++] = mode[i]; + access[a_out] = '\0'; + } + if( mode[i] == 'w' + || mode[i] == '+' + || mode[i] == 'a' ) + bAllocBuffer = TRUE; + } + + // No need to buffer on /vsimem/ + if( strncmp(name, "/vsimem/", strlen("/vsimem/")) == 0 ) + bAllocBuffer = FALSE; + + strcat( access, "b" ); + + VSIFSeekL(fpL, 0, SEEK_SET); + + GDALTiffHandle* psGTH = (GDALTiffHandle*) CPLMalloc(sizeof(GDALTiffHandle)); + psGTH->fpL = fpL; + psGTH->nExpectedPos = 0; + psGTH->bAtEndOfFile = FALSE; + psGTH->abyWriteBuffer = (bAllocBuffer) ? (GByte*)VSIMalloc(BUFFER_SIZE) : NULL; + psGTH->nWriteBufferSize = 0; + + tif = XTIFFClientOpen(name, mode, + (thandle_t) psGTH, + _tiffReadProc, _tiffWriteProc, + _tiffSeekProc, _tiffCloseProc, _tiffSizeProc, + _tiffMapProc, _tiffUnmapProc); + if( tif == NULL ) + CPLFree(psGTH); + + return tif; +} diff --git a/bazaar/plugin/gdal/frmts/gtiff/tifvsi.h b/bazaar/plugin/gdal/frmts/gtiff/tifvsi.h new file mode 100644 index 000000000..de7befcb4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gtiff/tifvsi.h @@ -0,0 +1,42 @@ +/****************************************************************************** + * $Id: tifvsi.h 28872 2015-04-08 14:25:20Z rouault $ + * + * Project: GeoTIFF Driver + * Purpose: Implement system hook functions for libtiff on top of CPL/VSI, + * including > 2GB support. Based on tif_unix.c from libtiff + * distribution. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam, warmerdam@pobox.com + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef TIFVSI_H_INCLUDED +#define TIFVSI_H_INCLUDED + +#include "tiffio.h" + +TIFF* VSI_TIFFOpen(const char* name, const char* mode, VSILFILE* fp); +VSILFILE* VSI_TIFFGetVSILFile(thandle_t th); +int VSI_TIFFFlushBufferedWrite(thandle_t th); + +#endif // TIFVSI_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/gxf/Doxyfile b/bazaar/plugin/gdal/frmts/gxf/Doxyfile new file mode 100644 index 000000000..751deb81c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/Doxyfile @@ -0,0 +1,255 @@ +# This file describes the settings to be used by doxygen for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of word surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = GXF + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each page. A value of NO (the default) enables the index and the +# value YES disables it. + +DISABLE_INDEX = NO + +# If the EXTRACT_ALL tag is set to YES all classes and functions will be +# included in the documentation, even if no documentation was available. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members inside documented classes or files. + +HIDE_UNDOC_MEMBERS = YES + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output + +GENERATE_HTML = YES + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the FULL_PATH_NAMES tag is set to YES Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used + +FULL_PATH_NAMES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = gxfopen.c gxfopen.h gxf_proj4.c gxf.dox gxf_ogcwkt.c + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +FILE_PATTERNS = *.* + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = . + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. + +MACRO_EXPANSION = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). In the former case 1 is used as the +# definition. + +PREDEFINED = + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED tag. + +EXPAND_ONLY_PREDEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/local/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = /usr/local/bin/ + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/bazaar/plugin/gdal/frmts/gxf/GNUmakefile b/bazaar/plugin/gdal/frmts/gxf/GNUmakefile new file mode 100644 index 000000000..4bdf883d9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/GNUmakefile @@ -0,0 +1,64 @@ +VERSION = 1_0 +DISTDIR = gxf3_$(VERSION) +WEB_DIR = /u/www/projects/gxf +SHARED_LIB = gdal_GXF.so + + +include ../../GDALmake.opt + +GXFOBJ = gxfopen.o gxf_proj4.o gxf_ogcwkt.o +OBJ = gxfdataset.o $(GXFOBJ) + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +gxftest$(EXE): gxftest.$(OBJ_EXT) + $(LD) $(LDFLAGS) gxftest.$(OBJ_EXT) $(GXFOBJ) ../../port/*.$(OBJ_EXT) $(LIBS) -o gxftest$(EXE) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +$(SHARED_LIB): $(OBJ) + $(LD_SHARED) $(OBJ) $(GDAL_SLIB_LINK) $(LIBS) -o $(SHARED_LIB) + +install-lib: $(SHARED_LIB) + cp $(SHARED_LIB) ../../lib + +docs: + rm -rf html + mkdir html + doxygen + +update-web: dist + cp html/* $(WEB_DIR) + cp $(DISTDIR).tar.gz /u/ftp/pub/outgoing + cp $(DISTDIR).zip /u/ftp/pub/outgoing + + +dist: docs + rm -rf $(DISTDIR) + mkdir $(DISTDIR) + mkdir $(DISTDIR)/html + cp html/* $(DISTDIR)/html + autoconf + cp *.c *.h configure Makefile.in $(DISTDIR) + cp makefile.vc.dist $(DISTDIR)/makefile.vc + rm configure + cp ../../port/cpl_conv.cpp $(DISTDIR)/cpl_conv.c + cp ../../port/cpl_conv.h $(DISTDIR) + cp ../../port/cpl_string.cpp $(DISTDIR)/cpl_string.c + cp ../../port/cpl_string.h $(DISTDIR) + cp ../../port/cpl_vsisimple.cpp $(DISTDIR)/cpl_vsisimple.c + cp ../../port/cpl_vsi.h $(DISTDIR) + cp ../../port/cpl_error.cpp $(DISTDIR)/cpl_error.c + cp ../../port/cpl_error.h $(DISTDIR) + cp ../../port/cpl_port.h $(DISTDIR) + cp ../../port/cpl_config.h $(DISTDIR) + cp ../../port/cpl_config.h.in $(DISTDIR) + rm -f $(DISTDIR)/*.o + tar czf $(DISTDIR).tar.gz $(DISTDIR) + zip -r $(DISTDIR).zip $(DISTDIR) + diff --git a/bazaar/plugin/gdal/frmts/gxf/Makefile.in b/bazaar/plugin/gdal/frmts/gxf/Makefile.in new file mode 100644 index 000000000..b6f3dea13 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/Makefile.in @@ -0,0 +1,34 @@ + +OBJ = gxfopen.o gxf_proj4.o gxf_ogcwkt.o \ + \ + cpl_error.o cpl_vsisimple.o cpl_string.o cpl_conv.o + +CFLAGS = @CFLAGS@ +LIBS = @LIBS@ -lm +CC = @CC@ + + +default: 8211view + +libgxf3.a: $(OBJ) + ar r libgxf3.a $(OBJ) + +cpl_error.o: cpl_error.c + $(CC) -c $(CFLAGS) cpl_error.c + +cpl_string.o: cpl_string.c + $(CC) -c $(CFLAGS) cpl_string.c + +cpl_conv.o: cpl_conv.c + $(CC) -c $(CFLAGS) cpl_conv.c + +cpl_vsisimple.o: cpl_vsisimple.c + $(CC) -c $(CFLAGS) cpl_vsisimple.c + +# +# Mainlines +# + +gxftest: gxftest.c libgxf3.a + $(CC) $(CFLAGS) gxftest.c libgxf3.a $(LIBS) -o gxftest + diff --git a/bazaar/plugin/gdal/frmts/gxf/README b/bazaar/plugin/gdal/frmts/gxf/README new file mode 100644 index 000000000..60f816652 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/README @@ -0,0 +1,65 @@ +Notes +----- + +Gilles Clement of Global Geomatics approved this support library +for general OpenSource release six months after it was released +as part of OGDI. This should be approximately September of 1999. + + +GXF Irregularities +------------------ + + o The #TRANSFORM is defined to have commas between fields, but in the + example files it has spaces. Treating both the same. + + o The document doesn't memtion it, but it seems that the first five + characters have to be taken as sufficient for a field name. For + instance the old sample file I have has only #TITL for the title, + while new ones have #TITLE. + + o It seems according to the documentation that Geographic projected + files should list "Geographic" as their projection method, but + samples seem to just skip the third line of the map projection + definition. + + o Apparently geosoft files don't have to have all the map projection + arguments listed in the document. For instance, eltoro.gxf's LCC + projection doesn't contain the false easting and northing. + + o I reprojected gxf_text.gxf to lat/long producing latlong.gxf. This + file contains a #MAP_PROJECTION of "NAD83 / UTM zone 19N" even though + everything is in degrees. Why? Shouldn't it read "Geographic"? + + + +GG GeoTIFF Irregularities +------------------------- + + o LCC_1SP seems to ignore the scale factor at natural origin parameter. + (ProjScaleAtNatOriginGeoKey) (should be +k= in PROJ.4). + + o 1SP form of LCC doesn't seem to have an analog in PROJ.4 -- + I have transformed the Longitude of Natural Origin as +lon_0, and the + scale as +k, but it isn't clear that the scale parameter is really + used for +proj=lcc, at least according to the documentation. + This _isn't_ done by the GG Geotiff translator. + + o the PROJ.4 Oblique Mercator support doesn't (apparently) include + support for an ``Angle from Rectified to Skew Grid'' though via the + the +no_rot flag it seems possible to choose between having this + angle be zero or -azimuth. Currently I am just using the angle + to decide if +no_rot should be set. + + o The Polar Stereographic projection in GeoTIFF is translated to + +proj=ups, ignoring any other polar stereographic parameters. This + would appear to be an error. For the time being I am translating as + Stereographic. + + o It appears that PROJ.4's polyconic projection doesn't include support + for the scale at the origin (presumably 1.0). + + + + + + diff --git a/bazaar/plugin/gdal/frmts/gxf/configure.in b/bazaar/plugin/gdal/frmts/gxf/configure.in new file mode 100644 index 000000000..a70e84413 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/configure.in @@ -0,0 +1,16 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(Makefile.in) +AC_CONFIG_HEADER(cpl_config.h) + +dnl Checks for programs. +AC_PROG_CC + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(fcntl.h unistd.h) + +dnl Checks for library functions. +AC_C_BIGENDIAN +AC_FUNC_VPRINTF + +AC_OUTPUT(Makefile) diff --git a/bazaar/plugin/gdal/frmts/gxf/gxf.dox b/bazaar/plugin/gdal/frmts/gxf/gxf.dox new file mode 100644 index 000000000..0e525f536 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/gxf.dox @@ -0,0 +1,91 @@ +/*! \page index + +

+GXF-3 +
+ +

Introduction

+ +The GXF-3 library is intended to make correct implementation of GXF-3 file +format readers easy. It consists of free (OpenSource) C source code for +functions to read GXF-3 raster files, and an example program using them to +convert GXF data to GeoTIFF format.

+ +GXF (Grid eXchange File) is a standard ASCII file format for exchanging +gridded data among different software systems. Software that supports the +GXF standard will be able to import properly formatted GXF files and export +grids in GXF format. GXF-3 is an adopted standard format of the +Gravity/Magnetics Committee of the Society of Exploration Geophysicists (SEG). +GXF-3 is the primary gridded data interchange format for +Geosoft.

+ +

Resources

+ + + +

Licensing

+ +This library is offered as Open Source. +In particular, it is offered under the X Consortium license which doesn't +attempt to impose any copyleft, or credit requirements on users of the code.

+ +The precise license text is:

+ + + Copyright (c) 1999, Frank Warmerdam +

+ Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: +

+ The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. +

+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +

+ + +

Building the Source

+ +Unix developers should be able to unpack the .tar.gz file, run configure, +and type make to build the library (libgxf3.a), and a simple test program +(gxftest.c).

+ +Windows developers should unpack the .zip file, and type +nmake /f makefile.vc to build with VC++.

+ +

Author and Acknowledgements

+ +The primary author of the GXF3 library is +Frank Warmerdam, +and I can be reached at +warmerdam@pobox.com. I am open to +bug reports, and suggestions.

+ +I would like to thank:

+ +

    +
  • Global Geomatics +who funded development the majority of the work on this library, +and agreed for it to be Open Source.

    + +

  • Ian Macleod of Geosoft who answered a number of questions I had, +and provided sample files.

    + +

+ +*/ diff --git a/bazaar/plugin/gdal/frmts/gxf/gxf_ogcwkt.c b/bazaar/plugin/gdal/frmts/gxf/gxf_ogcwkt.c new file mode 100644 index 000000000..8ab0f4ad8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/gxf_ogcwkt.c @@ -0,0 +1,647 @@ +/****************************************************************************** + * $Id: gxf_ogcwkt.c 28565 2015-02-27 10:26:21Z rouault $ + * + * Project: GXF Reader + * Purpose: Handle GXF to OGC WKT projection transformation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gxfopen.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: gxf_ogcwkt.c 28565 2015-02-27 10:26:21Z rouault $"); + +/* -------------------------------------------------------------------- */ +/* the following #defines come from ogr_spatialref.h in the GDAL/OGR */ +/* distribution (see http://gdal.velocet.ca/projects/opengis) and */ +/* should be kept in sync with that file. */ +/* -------------------------------------------------------------------- */ + +#define SRS_PT_ALBERS_CONIC_EQUAL_AREA \ + "Albers_Conic_Equal_Area" +#define SRS_PT_AZIMUTHAL_EQUIDISTANT "Azimuthal_Equidistant" +#define SRS_PT_CASSINI_SOLDNER "Cassini_Soldner" +#define SRS_PT_CYLINDRICAL_EQUAL_AREA "Cylindrical_Equal_Area" +#define SRS_PT_ECKERT_IV "Eckert_IV" +#define SRS_PT_ECKERT_VI "Eckert_VI" +#define SRS_PT_EQUIDISTANT_CONIC "Equidistant_Conic" +#define SRS_PT_EQUIRECTANGULAR "Equirectangular" +#define SRS_PT_GALL_STEREOGRAPHIC "Gall_Stereographic" +#define SRS_PT_GNOMONIC "Gnomonic" +#define SRS_PT_HOTINE_OBLIQUE_MERCATOR \ + "Hotine_Oblique_Mercator" +#define SRS_PT_LABORDE_OBLIQUE_MERCATOR \ + "Laborde_Oblique_Mercator" +#define SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP \ + "Lambert_Conformal_Conic_1SP" +#define SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP \ + "Lambert_Conformal_Conic_2SP" +#define SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM \ + "Lambert_Conformal_Conic_2SP_Belgium" +#define SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA \ + "Lambert_Azimuthal_Equal_Area" +#define SRS_PT_MERCATOR_1SP "Mercator_1SP" +#define SRS_PT_MERCATOR_2SP "Mercator_2SP" +#define SRS_PT_MILLER_CYLINDRICAL "Miller_Cylindrical" +#define SRS_PT_MOLLWEIDE "Mollweide" +#define SRS_PT_NEW_ZEALAND_MAP_GRID \ + "New_Zealand_Map_Grid" +#define SRS_PT_OBLIQUE_STEREOGRAPHIC \ + "Oblique_Stereographic" +#define SRS_PT_ORTHOGRAPHIC "Orthographic" +#define SRS_PT_POLAR_STEREOGRAPHIC \ + "Polar_Stereographic" +#define SRS_PT_POLYCONIC "Polyconic" +#define SRS_PT_ROBINSON "Robinson" +#define SRS_PT_SINUSOIDAL "Sinusoidal" +#define SRS_PT_STEREOGRAPHIC "Stereographic" +#define SRS_PT_SWISS_OBLIQUE_CYLINDRICAL \ + "Swiss_Oblique_Cylindrical" +#define SRS_PT_TRANSVERSE_MERCATOR \ + "Transverse_Mercator" +#define SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED \ + "Transverse_Mercator_South_Orientated" +#define SRS_PT_TUNISIA_MINING_GRID \ + "Tunisia_Mining_Grid" +#define SRS_PT_VANDERGRINTEN "VanDerGrinten" + +#define SRS_PP_CENTRAL_MERIDIAN "central_meridian" +#define SRS_PP_SCALE_FACTOR "scale_factor" +#define SRS_PP_STANDARD_PARALLEL_1 "standard_parallel_1" +#define SRS_PP_STANDARD_PARALLEL_2 "standard_parallel_2" +#define SRS_PP_LONGITUDE_OF_CENTER "longitude_of_center" +#define SRS_PP_LATITUDE_OF_CENTER "latitude_of_center" +#define SRS_PP_LONGITUDE_OF_ORIGIN "longitude_of_origin" +#define SRS_PP_LATITUDE_OF_ORIGIN "latitude_of_origin" +#define SRS_PP_FALSE_EASTING "false_easting" +#define SRS_PP_FALSE_NORTHING "false_northing" +#define SRS_PP_AZIMUTH "azimuth" +#define SRS_PP_LONGITUDE_OF_POINT_1 "longitude_of_point_1" +#define SRS_PP_LATITUDE_OF_POINT_1 "latitude_of_point_1" +#define SRS_PP_LONGITUDE_OF_POINT_2 "longitude_of_point_2" +#define SRS_PP_LATITUDE_OF_POINT_2 "latitude_of_point_2" +#define SRS_PP_LONGITUDE_OF_POINT_3 "longitude_of_point_3" +#define SRS_PP_LATITUDE_OF_POINT_3 "latitude_of_point_3" +#define SRS_PP_RECTIFIED_GRID_ANGLE "rectified_grid_angle" + +/* -------------------------------------------------------------------- */ +/* This table was copied from gt_wkt_srs.cpp in the libgeotiff */ +/* distribution. Please keep changes in sync. */ +/* -------------------------------------------------------------------- */ +static char *papszDatumEquiv[] = +{ + "Militar_Geographische_Institut", + "Militar_Geographische_Institute", + "World_Geodetic_System_1984", + "WGS_1984", + "WGS_72_Transit_Broadcast_Ephemeris", + "WGS_1972_Transit_Broadcast_Ephemeris", + "World_Geodetic_System_1972", + "WGS_1972", + "European_Terrestrial_Reference_System_89", + "European_Reference_System_1989", + NULL +}; + +/************************************************************************/ +/* WKTMassageDatum() */ +/* */ +/* Massage an EPSG datum name into WMT format. Also transform */ +/* specific exception cases into WKT versions. */ +/* */ +/* This function was copied from the gt_wkt_srs.cpp file in the */ +/* libgeotiff distribution. Please keep changes in sync. */ +/************************************************************************/ + +static void WKTMassageDatum( char ** ppszDatum ) + +{ + int i, j; + char *pszDatum; + + pszDatum = *ppszDatum; + if (pszDatum[0] == '\0') + return; + +/* -------------------------------------------------------------------- */ +/* Translate non-alphanumeric values to underscores. */ +/* -------------------------------------------------------------------- */ + for( i = 0; pszDatum[i] != '\0'; i++ ) + { + if( pszDatum[i] != '+' + && !(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z') + && !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z') + && !(pszDatum[i] >= '0' && pszDatum[i] <= '9') ) + { + pszDatum[i] = '_'; + } + } + +/* -------------------------------------------------------------------- */ +/* Remove repeated and trailing underscores. */ +/* -------------------------------------------------------------------- */ + for( i = 1, j = 0; pszDatum[i] != '\0'; i++ ) + { + if( pszDatum[j] == '_' && pszDatum[i] == '_' ) + continue; + + pszDatum[++j] = pszDatum[i]; + } + if( pszDatum[j] == '_' ) + pszDatum[j] = '\0'; + else + pszDatum[j+1] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Search for datum equivelences. Specific massaged names get */ +/* mapped to OpenGIS specified names. */ +/* -------------------------------------------------------------------- */ + for( i = 0; papszDatumEquiv[i] != NULL; i += 2 ) + { + if( EQUAL(*ppszDatum,papszDatumEquiv[i]) ) + { + CPLFree( *ppszDatum ); + *ppszDatum = CPLStrdup( papszDatumEquiv[i+1] ); + return; + } + } +} + +/************************************************************************/ +/* OGCWKTSetProj() */ +/************************************************************************/ + +static void OGCWKTSetProj( char * pszProjection, char ** papszMethods, + const char * pszTransformName, + const char * pszParm1, + const char * pszParm2, + const char * pszParm3, + const char * pszParm4, + const char * pszParm5, + const char * pszParm6, + const char * pszParm7 ) + +{ + int iParm, nCount = CSLCount(papszMethods); + const char *apszParmNames[8]; + + apszParmNames[0] = pszParm1; + apszParmNames[1] = pszParm2; + apszParmNames[2] = pszParm3; + apszParmNames[3] = pszParm4; + apszParmNames[4] = pszParm5; + apszParmNames[5] = pszParm6; + apszParmNames[6] = pszParm7; + apszParmNames[7] = NULL; + + sprintf( pszProjection, + "PROJECTION[\"%s\"]", + pszTransformName ); + + for( iParm = 0; iParm < nCount-1 && apszParmNames[iParm] != NULL; iParm++ ) + { + sprintf( pszProjection + strlen(pszProjection), + ",PARAMETER[\"%s\",%s]", + apszParmNames[iParm], + papszMethods[iParm+1] ); + } +} + + +/************************************************************************/ +/* GXFGetMapProjectionAsOGCWKT() */ +/************************************************************************/ + +/** + * Return the GXF Projection in OpenGIS Well Known Text format. + * + * The returned string becomes owned by the caller, and should be freed + * with CPLFree() or VSIFree(). The return value will be "" if + * no projection information is passed. + * + * The mapping of GXF projections to OGC WKT format is not complete. Please + * see the gxf_ogcwkt.c code to better understand limitations of this + * translation. More information about OGC WKT format can be found in + * the OpenGIS Simple Features specification for OLEDB/COM found on the + * OpenGIS web site at www.opengis.org. + * The translation uses some code cribbed from the OGR library, about which + * more can be learned from + * http://gdal.velocet.ca/projects/opengis/. + * + * For example, the following GXF definitions: + *
+ * #UNIT_LENGTH                        
+ * m,1
+ * #MAP_PROJECTION
+ * "NAD83 / UTM zone 19N"
+ * "GRS 1980",6378137,0.081819191,0
+ * "Transverse Mercator",0,-69,0.9996,500000,0
+ * 
+ * + * Would translate to (without the nice formatting): + *
+   PROJCS["NAD83 / UTM zone 19N",
+          GEOGCS["GRS 1980",
+                 DATUM["GRS_1980",
+                     SPHEROID["GRS 1980",6378137,298.257222413684]],
+                 PRIMEM["unnamed",0],
+                 UNIT["degree",0.0174532925199433]],
+          PROJECTION["Transverse_Mercator"],
+          PARAMETER["latitude_of_origin",0],
+          PARAMETER["central_meridian",-69],
+          PARAMETER["scale_factor",0.9996],
+          PARAMETER["false_easting",500000],
+          PARAMETER["false_northing",0],
+          UNIT["m",1]]
+ * 
+ * + * @param hGXF handle to GXF file, as returned by GXFOpen(). + * + * @return string containing OGC WKT projection. + */ + +char *GXFGetMapProjectionAsOGCWKT( GXFHandle hGXF ) + +{ + GXFInfo_t *psGXF = (GXFInfo_t *) hGXF; + char **papszMethods = NULL; + char szWKT[1024]; + char szGCS[512]; + char szProjection[512]; + +/* -------------------------------------------------------------------- */ +/* If there was nothing in the file return "unknown". */ +/* -------------------------------------------------------------------- */ + if( CSLCount(psGXF->papszMapProjection) < 2 ) + return( CPLStrdup( "" ) ); + + strcpy( szWKT, "" ); + strcpy( szGCS, "" ); + strcpy( szProjection, "" ); + +/* -------------------------------------------------------------------- */ +/* Parse the third line, looking for known projection methods. */ +/* -------------------------------------------------------------------- */ + if( psGXF->papszMapProjection[2] != NULL ) + { + /* We allow more than 80 characters if the projection parameters */ + /* are on 2 lines as allowed by GXF 3 */ + if( strlen(psGXF->papszMapProjection[2]) > 120 ) + return( CPLStrdup( "" ) ); + papszMethods = CSLTokenizeStringComplex(psGXF->papszMapProjection[2], + ",", TRUE, TRUE ); + } + +#ifdef DBMALLOC + malloc_chain_check(1); +#endif + +/* -------------------------------------------------------------------- */ +/* Create the PROJCS. */ +/* -------------------------------------------------------------------- */ + if( papszMethods == NULL + || papszMethods[0] == NULL + || EQUAL(papszMethods[0],"Geographic") ) + { + /* do nothing */ + } + + else if( EQUAL(papszMethods[0],"Lambert Conic Conformal (1SP)") ) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + NULL ); + } + + else if( EQUAL(papszMethods[0],"Lambert Conic Conformal (2SP)") ) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL ); + } + + else if( EQUAL(papszMethods[0],"Lambert Conformal (2SP Belgium)") ) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL ); + } + + else if( EQUAL(papszMethods[0],"Mercator (1SP)")) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_MERCATOR_1SP, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + NULL ); + } + + else if( EQUAL(papszMethods[0],"Mercator (2SP)")) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_MERCATOR_2SP, + SRS_PP_LATITUDE_OF_ORIGIN,/* should it be StdParalle1?*/ + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + NULL, + NULL ); + } + + else if( EQUAL(papszMethods[0],"Laborde Oblique Mercator") ) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_LABORDE_OBLIQUE_MERCATOR, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_AZIMUTH, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL ); + + } + + else if( EQUAL(papszMethods[0],"Hotine Oblique Mercator") ) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_HOTINE_OBLIQUE_MERCATOR, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_AZIMUTH, + SRS_PP_RECTIFIED_GRID_ANGLE, + SRS_PP_SCALE_FACTOR, /* not in normal formulation */ + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING ); + } + + else if( EQUAL(papszMethods[0],"New Zealand Map Grid") ) + + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_NEW_ZEALAND_MAP_GRID, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + NULL, + NULL ); + } + + else if( EQUAL(papszMethods[0],"Oblique Stereographic") ) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_OBLIQUE_STEREOGRAPHIC, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + NULL ); + } + + else if( EQUAL(papszMethods[0],"Polar Stereographic") ) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_POLAR_STEREOGRAPHIC, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + NULL ); + } + + else if( EQUAL(papszMethods[0],"Swiss Oblique Cylindrical") ) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_SWISS_OBLIQUE_CYLINDRICAL, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + NULL, + NULL ); + } + + else if( EQUAL(papszMethods[0],"Transverse Mercator") ) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_TRANSVERSE_MERCATOR, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + NULL ); + } + + else if( EQUAL(papszMethods[0],"Transverse Mercator (South Oriented)") + || EQUAL(papszMethods[0],"Transverse Mercator (South Orientated)")) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + NULL ); + } + + else if( EQUAL(papszMethods[0],"*Albers Conic") ) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_ALBERS_CONIC_EQUAL_AREA, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL ); + } + + else if( EQUAL(papszMethods[0],"*Equidistant Conic") ) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_EQUIDISTANT_CONIC, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL ); + } + + else if( EQUAL(papszMethods[0],"*Polyconic") ) + { + OGCWKTSetProj( szProjection, papszMethods, + SRS_PT_POLYCONIC, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, /* not normally expected */ + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + NULL ); + } + + CSLDestroy( papszMethods ); + + +/* -------------------------------------------------------------------- */ +/* Extract the linear Units specification. */ +/* -------------------------------------------------------------------- */ + if( psGXF->pszUnitName != NULL && strlen(szProjection) > 0 ) + { + if( strlen(psGXF->pszUnitName) > 80 ) + return CPLStrdup(""); + + CPLsprintf( szProjection+strlen(szProjection), + ",UNIT[\"%s\",%.15g]", + psGXF->pszUnitName, psGXF->dfUnitToMeter ); + } + +/* -------------------------------------------------------------------- */ +/* Build GEOGCS. There are still "issues" with the generation */ +/* of the GEOGCS/Datum and Spheroid names. Of these, only the */ +/* datum name is really significant. */ +/* -------------------------------------------------------------------- */ + if( CSLCount(psGXF->papszMapProjection) > 1 ) + { + char **papszTokens; + + if( strlen(psGXF->papszMapProjection[1]) > 80 ) + return CPLStrdup(""); + + papszTokens = CSLTokenizeStringComplex(psGXF->papszMapProjection[1], + ",", TRUE, TRUE ); + + + if( CSLCount(papszTokens) > 2 ) + { + double dfMajor = CPLAtof(papszTokens[1]); + double dfEccentricity = CPLAtof(papszTokens[2]); + double dfInvFlattening, dfMinor; + char *pszOGCDatum; + + /* translate eccentricity to inv flattening. */ + if( dfEccentricity == 0.0 ) + dfInvFlattening = 0.0; + else + { + dfMinor = dfMajor * pow(1.0-dfEccentricity*dfEccentricity,0.5); + dfInvFlattening = OSRCalcInvFlattening(dfMajor, dfMinor); + } + + pszOGCDatum = CPLStrdup(papszTokens[0]); + WKTMassageDatum( &pszOGCDatum ); + + CPLsprintf( szGCS, + "GEOGCS[\"%s\"," + "DATUM[\"%s\"," + "SPHEROID[\"%s\",%s,%.15g]],", + papszTokens[0], + pszOGCDatum, + papszTokens[0], /* this is datum, but should be ellipse*/ + papszTokens[1], + dfInvFlattening ); + CPLFree( pszOGCDatum ); + } + + if( CSLCount(papszTokens) > 3 ) + sprintf( szGCS + strlen(szGCS), + "PRIMEM[\"unnamed\",%s],", + papszTokens[3] ); + + strcat( szGCS, "UNIT[\"degree\",0.0174532925199433]]" ); + + CSLDestroy( papszTokens ); + } + + CPLAssert(strlen(szProjection) < sizeof(szProjection)); + CPLAssert(strlen(szGCS) < sizeof(szGCS)); + +/* -------------------------------------------------------------------- */ +/* Put this all together into a full projection. */ +/* -------------------------------------------------------------------- */ + if( strlen(szProjection) > 0 ) + { + if( strlen(psGXF->papszMapProjection[0]) > 80 ) + return CPLStrdup(""); + + if( psGXF->papszMapProjection[0][0] == '"' ) + sprintf( szWKT, + "PROJCS[%s,%s,%s]", + psGXF->papszMapProjection[0], + szGCS, + szProjection ); + else + sprintf( szWKT, + "PROJCS[\"%s\",%s,%s]", + psGXF->papszMapProjection[0], + szGCS, + szProjection ); + + } + else + { + strcpy( szWKT, szGCS ); + } + + return( CPLStrdup( szWKT ) ); +} + diff --git a/bazaar/plugin/gdal/frmts/gxf/gxf_proj4.c b/bazaar/plugin/gdal/frmts/gxf/gxf_proj4.c new file mode 100644 index 000000000..8e356c937 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/gxf_proj4.c @@ -0,0 +1,624 @@ +/****************************************************************************** + * $Id: gxf_proj4.c 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: GXF Reader + * Purpose: Handle GXF to PROJ.4 projection transformation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Global Geomatics + * Copyright (c) 1998, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gxfopen.h" + +CPL_CVSID("$Id: gxf_proj4.c 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* GXFGetMapProjectionAsPROJ4() */ +/************************************************************************/ + +/** + * Return the GXF Projection in PROJ.4 format. + * + * The returned string becomes owned by the caller, and should be freed + * with CPLFree() or VSIFree(). The return value will be "unknown" if + * no projection information is passed. + * + * The mapping of GXF projections to PROJ.4 format is not complete. Please + * see the gxf_proj4.c code to better understand limitations of this + * translation. Noteable PROJ.4 knows little about datums. + * + * For example, the following GXF definitions: + *
+ * #UNIT_LENGTH                        
+ * m,1
+ * #MAP_PROJECTION
+ * "NAD83 / UTM zone 19N"
+ * "GRS 1980",6378137,0.081819191,0
+ * "Transverse Mercator",0,-69,0.9996,500000,0
+ * 
+ * + * Would translate to: + *
+ * +proj=tmerc +lat_0=0 +lon_0=-69 +k=0.9996 +x_0=500000 +y_0=0 +ellps=GRS80
+ * 
+ * + * @param hGXF handle to GXF file, as returned by GXFOpen(). + * + * @return string containing PROJ.4 projection. + */ + +char *GXFGetMapProjectionAsPROJ4( GXFHandle hGXF ) + +{ + GXFInfo_t *psGXF = (GXFInfo_t *) hGXF; + char **papszMethods = NULL; + char szPROJ4[512]; + +/* -------------------------------------------------------------------- */ +/* If there was nothing in the file return "unknown". */ +/* -------------------------------------------------------------------- */ + if( CSLCount(psGXF->papszMapProjection) < 2 ) + return( CPLStrdup( "unknown" ) ); + + szPROJ4[0] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Parse the third line, looking for known projection methods. */ +/* -------------------------------------------------------------------- */ + if( psGXF->papszMapProjection[2] != NULL ) + { + if( strlen(psGXF->papszMapProjection[2]) > 80 ) + return( CPLStrdup( "" ) ); + papszMethods = CSLTokenizeStringComplex(psGXF->papszMapProjection[2], + ",", TRUE, TRUE ); + } + +#ifdef DBMALLOC + malloc_chain_check(1); +#endif + + if( papszMethods == NULL + || papszMethods[0] == NULL + || EQUAL(papszMethods[0],"Geographic") ) + { + strcat( szPROJ4, "+proj=longlat" ); + } + +#ifdef notdef + else if( EQUAL(papszMethods[0],"Lambert Conic Conformal (1SP)") + && CSLCount(papszMethods) > 5 ) + { + /* notdef: It isn't clear that this 1SP + scale method is even + supported by PROJ.4 + Later note: It is not. */ + + strcat( szPROJ4, "+proj=lcc" ); + + strcat( szPROJ4, " +lat_0=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +k=" ); + strcat( szPROJ4, papszMethods[3] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[4] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[5] ); + } +#endif + else if( EQUAL(papszMethods[0],"Lambert Conic Conformal (2SP)") + || EQUAL(papszMethods[0],"Lambert Conformal (2SP Belgium)") ) + { + /* notdef: Note we are apparently losing whatever makes the + Belgium variant different than normal LCC, but hopefully + they are close! */ + + strcat( szPROJ4, "+proj=lcc" ); + + if( CSLCount(papszMethods) > 1 ) + { + strcat( szPROJ4, " +lat_1=" ); + strcat( szPROJ4, papszMethods[1] ); + } + + if( CSLCount(papszMethods) > 2 ) + { + strcat( szPROJ4, " +lat_2=" ); + strcat( szPROJ4, papszMethods[2] ); + } + + if( CSLCount(papszMethods) > 3 ) + { + strcat( szPROJ4, " +lat_0=" ); + strcat( szPROJ4, papszMethods[3] ); + } + + if( CSLCount(papszMethods) > 4 ) + { + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[4] ); + } + + if( CSLCount(papszMethods) > 5 ) + { + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[5] ); + } + + if( CSLCount(papszMethods) > 6 ) + { + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[6] ); + } + } + + else if( EQUAL(papszMethods[0],"Mercator (1SP)") + && CSLCount(papszMethods) > 5 ) + { + /* notdef: it isn't clear that +proj=merc support a scale of other + than 1.0 in PROJ.4 */ + + strcat( szPROJ4, "+proj=merc" ); + + strcat( szPROJ4, " +lat_ts=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +k=" ); + strcat( szPROJ4, papszMethods[3] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[4] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[5] ); + } + + else if( EQUAL(papszMethods[0],"Mercator (2SP)") + && CSLCount(papszMethods) > 4 ) + { + /* notdef: it isn't clear that +proj=merc support a scale of other + than 1.0 in PROJ.4 */ + + strcat( szPROJ4, "+proj=merc" ); + + strcat( szPROJ4, " +lat_ts=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[3] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[4] ); + } + + else if( EQUAL(papszMethods[0],"Hotine Oblique Mercator") + && CSLCount(papszMethods) > 7 ) + { + /* Note that only the second means of specifying omerc is supported + by this code in GXF. */ + strcat( szPROJ4, "+proj=omerc" ); + + strcat( szPROJ4, " +lat_0=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lonc=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +alpha=" ); + strcat( szPROJ4, papszMethods[3] ); + + if( CPLAtof(papszMethods[4]) < 0.00001 ) + { + strcat( szPROJ4, " +not_rot" ); + } + else + { +#ifdef notdef + if( CPLAtof(papszMethods[4]) + CPLAtof(papszMethods[3]) < 0.00001 ) + /* ok */; + else + /* notdef: no way to specify arbitrary angles! */; +#endif + } + + strcat( szPROJ4, " +k=" ); + strcat( szPROJ4, papszMethods[5] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[6] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[7] ); + } + + else if( EQUAL(papszMethods[0],"Laborde Oblique Mercator") + && CSLCount(papszMethods) > 6 ) + { + strcat( szPROJ4, "+proj=labrd" ); + + strcat( szPROJ4, " +lat_0=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +azi=" ); + strcat( szPROJ4, papszMethods[3] ); + + strcat( szPROJ4, " +k=" ); + strcat( szPROJ4, papszMethods[4] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[5] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[6] ); + } + + else if( EQUAL(papszMethods[0],"New Zealand Map Grid") + && CSLCount(papszMethods) > 4 ) + { + strcat( szPROJ4, "+proj=nzmg" ); + + strcat( szPROJ4, " +lat_0=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[3] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[4] ); + } + + else if( EQUAL(papszMethods[0],"New Zealand Map Grid") + && CSLCount(papszMethods) > 4 ) + { + strcat( szPROJ4, "+proj=nzmg" ); + + strcat( szPROJ4, " +lat_0=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[3] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[4] ); + } + + else if( EQUAL(papszMethods[0],"Oblique Stereographic") + && CSLCount(papszMethods) > 5 ) + { + /* there is an option to produce +lat_ts, which we ignore */ + + strcat( szPROJ4, "+proj=stere" ); + + strcat( szPROJ4, " +lat_0=45" ); + + strcat( szPROJ4, " +lat_ts=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +k=" ); + strcat( szPROJ4, papszMethods[3] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[4] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[5] ); + } + + else if( EQUAL(papszMethods[0],"Polar Stereographic") + && CSLCount(papszMethods) > 5 ) + { + /* there is an option to produce +lat_ts, which we ignore */ + + strcat( szPROJ4, "+proj=stere" ); + + strcat( szPROJ4, " +lat_0=90" ); + + strcat( szPROJ4, " +lat_ts=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +k=" ); + strcat( szPROJ4, papszMethods[3] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[4] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[5] ); + } + + else if( EQUAL(papszMethods[0],"Swiss Oblique Cylindrical") + && CSLCount(papszMethods) > 4 ) + { + /* notdef: geotiff's geo_ctrans.inc says this is the same as + ObliqueMercator_Rosenmund, which GG's geotiff support just + maps directly to +proj=omerc, though I find that questionable. */ + + strcat( szPROJ4, "+proj=omerc" ); + + strcat( szPROJ4, " +lat_0=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lonc=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[3] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[4] ); + } + + else if( EQUAL(papszMethods[0],"Transverse Mercator") + && CSLCount(papszMethods) > 5 ) + { + /* notdef: geotiff's geo_ctrans.inc says this is the same as + ObliqueMercator_Rosenmund, which GG's geotiff support just + maps directly to +proj=omerc, though I find that questionable. */ + + strcat( szPROJ4, "+proj=tmerc" ); + + strcat( szPROJ4, " +lat_0=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +k=" ); + strcat( szPROJ4, papszMethods[3] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[4] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[5] ); + } + + else if( EQUAL(papszMethods[0],"Transverse Mercator (South Oriented)") + && CSLCount(papszMethods) > 5 ) + { + /* notdef: I don't know how south oriented is different from + normal, and I don't find any mention of it in Geotiff;s geo_ctrans. + Translating as tmerc, but that is presumably wrong. */ + + strcat( szPROJ4, "+proj=tmerc" ); + + strcat( szPROJ4, " +lat_0=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +k=" ); + strcat( szPROJ4, papszMethods[3] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[4] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[5] ); + } + + else if( EQUAL(papszMethods[0],"*Equidistant Conic") + && CSLCount(papszMethods) > 6 ) + { + strcat( szPROJ4, "+proj=eqdc" ); + + strcat( szPROJ4, " +lat_1=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lat_2=" ); + strcat( szPROJ4, papszMethods[2] ); + + strcat( szPROJ4, " +lat_0=" ); + strcat( szPROJ4, papszMethods[3] ); + + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[4] ); + + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[5] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[6] ); + } + + else if( EQUAL(papszMethods[0],"*Polyconic") + && CSLCount(papszMethods) > 5 ) + { + strcat( szPROJ4, "+proj=poly" ); + + strcat( szPROJ4, " +lat_0=" ); + strcat( szPROJ4, papszMethods[1] ); + + strcat( szPROJ4, " +lon_0=" ); + strcat( szPROJ4, papszMethods[2] ); + +#ifdef notdef + /*not supported by PROJ.4 */ + strcat( szPROJ4, " +k=" ); + strcat( szPROJ4, papszMethods[3] ); +#endif + strcat( szPROJ4, " +x_0=" ); + strcat( szPROJ4, papszMethods[4] ); + + strcat( szPROJ4, " +y_0=" ); + strcat( szPROJ4, papszMethods[5] ); + } + + else + { + strcat( szPROJ4, "unknown" ); + } + + CSLDestroy( papszMethods ); + +/* -------------------------------------------------------------------- */ +/* Now get the ellipsoid parameters. For a bunch of common */ +/* ones we preserve the name. For the rest we just carry over */ +/* the parameters. */ +/* -------------------------------------------------------------------- */ + if( CSLCount(psGXF->papszMapProjection) > 1 ) + { + char **papszTokens; + + if( strlen(psGXF->papszMapProjection[1]) > 80 ) + return CPLStrdup(""); + + papszTokens = CSLTokenizeStringComplex(psGXF->papszMapProjection[1], + ",", TRUE, TRUE ); + + + if( EQUAL(papszTokens[0],"WGS 84") ) + strcat( szPROJ4, " +ellps=WGS84" ); + else if( EQUAL(papszTokens[0],"*WGS 72") ) + strcat( szPROJ4, " +ellps=WGS72" ); + else if( EQUAL(papszTokens[0],"*WGS 66") ) + strcat( szPROJ4, " +ellps=WGS66" ); + else if( EQUAL(papszTokens[0],"*WGS 60") ) + strcat( szPROJ4, " +ellps=WGS60" ); + else if( EQUAL(papszTokens[0],"Clarke 1866") ) + strcat( szPROJ4, " +ellps=clrk66" ); + else if( EQUAL(papszTokens[0],"Clarke 1880") ) + strcat( szPROJ4, " +ellps=clrk80" ); + else if( EQUAL(papszTokens[0],"GRS 1980") ) + strcat( szPROJ4, " +ellps=GRS80" ); + else if( CSLCount( papszTokens ) > 2 ) + { + sprintf( szPROJ4+strlen(szPROJ4), + " +a=%s +e=%s", + papszTokens[1], papszTokens[2] ); + } + + CSLDestroy(papszTokens); + } + +/* -------------------------------------------------------------------- */ +/* Extract the units specification. */ +/* -------------------------------------------------------------------- */ + if( psGXF->pszUnitName != NULL ) + { + if( EQUAL(psGXF->pszUnitName,"ft") ) + { + strcat( szPROJ4, " +units=ft" ); + } + else if( EQUAL(psGXF->pszUnitName,"ftUS") ) + { + strcat( szPROJ4, " +units=us-ft" ); + } + else if( EQUAL(psGXF->pszUnitName,"km") ) + { + strcat( szPROJ4, " +units=km" ); + } + else if( EQUAL(psGXF->pszUnitName,"mm") ) + { + strcat( szPROJ4, " +units=mm" ); + } + else if( EQUAL(psGXF->pszUnitName,"in") ) + { + strcat( szPROJ4, " +units=in" ); + } + else if( EQUAL(psGXF->pszUnitName,"ftInd") ) + { + strcat( szPROJ4, " +units=ind-ft" ); + } + else if( EQUAL(psGXF->pszUnitName,"lk") ) + { + strcat( szPROJ4, " +units=link" ); + } + } + + return( CPLStrdup( szPROJ4 ) ); +} + + +/************************************************************************/ +/* GXFGetPROJ4Position() */ +/* */ +/* Get the same information as GXFGetPosition(), but adjust */ +/* to units to meters if we don't ``know'' the indicated */ +/* units. */ +/************************************************************************/ + +CPLErr GXFGetPROJ4Position( GXFHandle hGXF, + double * pdfXOrigin, double * pdfYOrigin, + double * pdfXPixelSize, double * pdfYPixelSize, + double * pdfRotation ) + +{ + GXFInfo_t *psGXF = (GXFInfo_t *) hGXF; + char *pszProj; + +/* -------------------------------------------------------------------- */ +/* Get the raw position. */ +/* -------------------------------------------------------------------- */ + if( GXFGetPosition( hGXF, + pdfXOrigin, pdfYOrigin, + pdfXPixelSize, pdfYPixelSize, + pdfRotation ) == CE_Failure ) + return( CE_Failure ); + +/* -------------------------------------------------------------------- */ +/* Do we know the units in PROJ.4? Get the PROJ.4 string, and */ +/* check for a +units definition. */ +/* -------------------------------------------------------------------- */ + pszProj = GXFGetMapProjectionAsPROJ4( hGXF ); + if( strstr(pszProj,"+unit") == NULL && psGXF->pszUnitName != NULL ) + { + if( pdfXOrigin != NULL ) + *pdfXOrigin *= psGXF->dfUnitToMeter; + if( pdfYOrigin != NULL ) + *pdfYOrigin *= psGXF->dfUnitToMeter; + if( pdfXPixelSize != NULL ) + *pdfXPixelSize *= psGXF->dfUnitToMeter; + if( pdfYPixelSize != NULL ) + *pdfYPixelSize *= psGXF->dfUnitToMeter; + } + CPLFree( pszProj ); + + return( CE_None ); +} diff --git a/bazaar/plugin/gdal/frmts/gxf/gxfdataset.cpp b/bazaar/plugin/gdal/frmts/gxf/gxfdataset.cpp new file mode 100644 index 000000000..47e420b74 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/gxfdataset.cpp @@ -0,0 +1,411 @@ +/****************************************************************************** + * $Id: gxfdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: GXF Reader + * Purpose: GDAL binding for GXF reader. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gxfopen.h" +#include "gdal_pam.h" + +CPL_CVSID("$Id: gxfdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#ifndef PI +# define PI 3.14159265358979323846 +#endif + +CPL_C_START +void GDALRegister_GXF(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* GXFDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GXFRasterBand; + +class GXFDataset : public GDALPamDataset +{ + friend class GXFRasterBand; + + GXFHandle hGXF; + + char *pszProjection; + double dfNoDataValue; + GDALDataType eDataType; + + public: + GXFDataset(); + ~GXFDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GXFRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GXFRasterBand : public GDALPamRasterBand +{ + friend class GXFDataset; + + public: + + GXFRasterBand( GXFDataset *, int ); + double GetNoDataValue(int* bGotNoDataValue); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + + +/************************************************************************/ +/* GXFRasterBand() */ +/************************************************************************/ + +GXFRasterBand::GXFRasterBand( GXFDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = poDS->eDataType; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double GXFRasterBand::GetNoDataValue(int* bGotNoDataValue) + +{ + GXFDataset *poGXF_DS = (GXFDataset *) poDS; + if (bGotNoDataValue) + *bGotNoDataValue = (fabs(poGXF_DS->dfNoDataValue - -1e12) > .1); + if (eDataType == GDT_Float32) + return (double)(float)poGXF_DS->dfNoDataValue; + else + return poGXF_DS->dfNoDataValue; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GXFRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + GXFDataset *poGXF_DS = (GXFDataset *) poDS; + double *padfBuffer; + float *pafBuffer = (float *) pImage; + int i; + CPLErr eErr; + + CPLAssert( nBlockXOff == 0 ); + + if (eDataType == GDT_Float32) + { + padfBuffer = (double *) VSIMalloc2(sizeof(double), nBlockXSize); + if( padfBuffer == NULL ) + return CE_Failure; + eErr = GXFGetScanline( poGXF_DS->hGXF, nBlockYOff, padfBuffer ); + + for( i = 0; i < nBlockXSize; i++ ) + pafBuffer[i] = (float) padfBuffer[i]; + + CPLFree( padfBuffer ); + } + else if (eDataType == GDT_Float64) + eErr = GXFGetScanline( poGXF_DS->hGXF, nBlockYOff, (double*)pImage ); + else + eErr = CE_Failure; + + return eErr; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GXFDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GXFDataset() */ +/************************************************************************/ + +GXFDataset::GXFDataset() + +{ + pszProjection = NULL; + hGXF = NULL; + dfNoDataValue = 0; + eDataType = GDT_Float32; +} + +/************************************************************************/ +/* ~GXFDataset() */ +/************************************************************************/ + +GXFDataset::~GXFDataset() + +{ + FlushCache(); + if( hGXF != NULL ) + GXFClose( hGXF ); + CPLFree( pszProjection ); +} + + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GXFDataset::GetGeoTransform( double * padfTransform ) + +{ + CPLErr eErr; + double dfXOrigin, dfYOrigin, dfXSize, dfYSize, dfRotation; + + eErr = GXFGetPosition( hGXF, &dfXOrigin, &dfYOrigin, + &dfXSize, &dfYSize, &dfRotation ); + + if( eErr != CE_None ) + return eErr; + + // Transform to radians. + dfRotation = (dfRotation / 360.0) * 2 * PI; + + padfTransform[1] = dfXSize * cos(dfRotation); + padfTransform[2] = dfYSize * sin(dfRotation); + padfTransform[4] = dfXSize * sin(dfRotation); + padfTransform[5] = -1 * dfYSize * cos(dfRotation); + + // take into account that GXF is point or center of pixel oriented. + padfTransform[0] = dfXOrigin - 0.5*padfTransform[1] - 0.5*padfTransform[2]; + padfTransform[3] = dfYOrigin - 0.5*padfTransform[4] - 0.5*padfTransform[5]; + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GXFDataset::GetProjectionRef() + +{ + return( pszProjection ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GXFDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + GXFHandle hGXF; + int i, bFoundKeyword, bFoundIllegal; + +/* -------------------------------------------------------------------- */ +/* Before trying GXFOpen() we first verify that there is at */ +/* least one "\n#keyword" type signature in the first chunk of */ +/* the file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 50 ) + return NULL; + + bFoundKeyword = FALSE; + bFoundIllegal = FALSE; + for( i = 0; i < poOpenInfo->nHeaderBytes-1; i++ ) + { + if( (poOpenInfo->pabyHeader[i] == 10 + || poOpenInfo->pabyHeader[i] == 13) + && poOpenInfo->pabyHeader[i+1] == '#' ) + { + if( strncmp((const char*)poOpenInfo->pabyHeader + i + 2, "include", strlen("include")) == 0 ) + return NULL; + if( strncmp((const char*)poOpenInfo->pabyHeader + i + 2, "define", strlen("define")) == 0 ) + return NULL; + if( strncmp((const char*)poOpenInfo->pabyHeader + i + 2, "ifdef", strlen("ifdef")) == 0 ) + return NULL; + bFoundKeyword = TRUE; + } + if( poOpenInfo->pabyHeader[i] == 0 ) + { + bFoundIllegal = TRUE; + break; + } + } + + if( !bFoundKeyword || bFoundIllegal ) + return NULL; + + +/* -------------------------------------------------------------------- */ +/* At this point it is plausible that this is a GXF file, but */ +/* we also now verify that there is a #GRID keyword before */ +/* passing it off to GXFOpen(). We check in the first 50K. */ +/* -------------------------------------------------------------------- */ +#define BIGBUFSIZE 50000 + int nBytesRead, bGotGrid = FALSE; + FILE *fp; + + fp = VSIFOpen( poOpenInfo->pszFilename, "rb" ); + if( fp == NULL ) + return NULL; + + char *pszBigBuf = (char *) CPLMalloc(BIGBUFSIZE); + nBytesRead = VSIFRead( pszBigBuf, 1, BIGBUFSIZE, fp ); + VSIFClose( fp ); + + for( i = 0; i < nBytesRead - 5 && !bGotGrid; i++ ) + { + if( pszBigBuf[i] == '#' && EQUALN(pszBigBuf+i+1,"GRID",4) ) + bGotGrid = TRUE; + } + + CPLFree( pszBigBuf ); + + if( !bGotGrid ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ + + hGXF = GXFOpen( poOpenInfo->pszFilename ); + + if( hGXF == NULL ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + GXFClose(hGXF); + CPLError( CE_Failure, CPLE_NotSupported, + "The GXF driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GXFDataset *poDS; + + poDS = new GXFDataset(); + + const char* pszGXFDataType = CPLGetConfigOption("GXF_DATATYPE", "Float32"); + GDALDataType eDT = GDALGetDataTypeByName(pszGXFDataType); + if (!(eDT == GDT_Float32 || eDT == GDT_Float64)) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unsupported value for GXF_DATATYPE : %s", pszGXFDataType); + eDT = GDT_Float32; + } + + poDS->hGXF = hGXF; + poDS->eDataType = eDT; + +/* -------------------------------------------------------------------- */ +/* Establish the projection. */ +/* -------------------------------------------------------------------- */ + poDS->pszProjection = GXFGetMapProjectionAsOGCWKT( hGXF ); + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + GXFGetRawInfo( hGXF, &(poDS->nRasterXSize), &(poDS->nRasterYSize), NULL, + NULL, NULL, &(poDS->dfNoDataValue) ); + + if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid dimensions : %d x %d", + poDS->nRasterXSize, poDS->nRasterYSize); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = 1; + poDS->SetBand( 1, new GXFRasterBand( poDS, 1 )); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_GXF() */ +/************************************************************************/ + +void GDALRegister_GXF() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GXF" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GXF" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GeoSoft Grid Exchange Format" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#GXF" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gxf" ); + + poDriver->pfnOpen = GXFDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/gxf/gxfopen.c b/bazaar/plugin/gdal/frmts/gxf/gxfopen.c new file mode 100644 index 000000000..40d46abb6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/gxfopen.c @@ -0,0 +1,1032 @@ +/****************************************************************************** + * $Id: gxfopen.c 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GXF Reader + * Purpose: Majority of Geosoft GXF reading code. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Global Geomatics + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "gxfopen.h" + +CPL_CVSID("$Id: gxfopen.c 27044 2014-03-16 23:41:27Z rouault $"); + + +/* this is also defined in gdal.h which we avoid in this separable component */ +#define CPLE_WrongFormat 200 + +#define MAX_LINE_COUNT_PER_HEADER 1000 +#define MAX_HEADER_COUNT 1000 + +/************************************************************************/ +/* GXFReadHeaderValue() */ +/* */ +/* Read one entry from the file header, and return it and it's */ +/* value in clean form. */ +/************************************************************************/ + +static char **GXFReadHeaderValue( FILE * fp, char * pszHTitle ) + +{ + const char *pszLine; + char **papszReturn = NULL; + int i; + int nLineCount = 0, nReturnLineCount = 0; + int bContinuedLine = FALSE; + +/* -------------------------------------------------------------------- */ +/* Try to read a line. If we fail or if this isn't a proper */ +/* header value then return the failure. */ +/* -------------------------------------------------------------------- */ + pszLine = CPLReadLine( fp ); + if( pszLine == NULL ) + { + strcpy( pszHTitle, "#EOF" ); + return( NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Extract the title. It should be terminated by some sort of */ +/* white space. */ +/* -------------------------------------------------------------------- */ + for( i = 0; !isspace((unsigned char)pszLine[i]) && pszLine[i] != '\0' && i < 70; i++ ) {} + + strncpy( pszHTitle, pszLine, i ); + pszHTitle[i] = '\0'; + +/* -------------------------------------------------------------------- */ +/* If this is #GRID, then return ... we are at the end of the */ +/* header. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszHTitle,"#GRID") ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Skip white space. */ +/* -------------------------------------------------------------------- */ + while( isspace((unsigned char)pszLine[i]) ) + i++; + +/* -------------------------------------------------------------------- */ +/* If we have reached the end of the line, try to read another line. */ +/* -------------------------------------------------------------------- */ + if( pszLine[i] == '\0' ) + { + pszLine = CPLReadLine( fp ); + if( pszLine == NULL ) + { + strcpy( pszHTitle, "#EOF" ); + return( NULL ); + } + i = 0; + } + +/* -------------------------------------------------------------------- */ +/* Keeping adding the value stuff as new lines till we reach a */ +/* `#' mark at the beginning of a new line. */ +/* -------------------------------------------------------------------- */ + do { + int nNextChar; + char *pszTrimmedLine; + + /* Lines are supposed to be limited to 80 characters */ + if( strlen(pszLine) > 1024 ) + { + CSLDestroy(papszReturn); + return NULL; + } + + pszTrimmedLine = CPLStrdup( pszLine ); + + for( i = strlen(pszLine)-1; i >= 0 && pszLine[i] == ' '; i-- ) + pszTrimmedLine[i] = '\0'; + + if( bContinuedLine ) + { + char* pszTmp = (char*) VSIMalloc((strlen(papszReturn[nReturnLineCount-1]) - 1) + strlen(pszTrimmedLine) + 1); + if( pszTmp == NULL ) + { + CSLDestroy(papszReturn); + return NULL; + } + strcpy(pszTmp, papszReturn[nReturnLineCount-1]); + strcpy(pszTmp + (strlen(papszReturn[nReturnLineCount-1]) - 1), pszTrimmedLine); + CPLFree(papszReturn[nReturnLineCount-1]); + papszReturn[nReturnLineCount-1] = pszTmp; + } + else + { + papszReturn = CSLAddString( papszReturn, pszTrimmedLine ); + nReturnLineCount ++; + } + + /* Is it a continued line ? */ + bContinuedLine = ( i >= 0 && pszTrimmedLine[i] == '\\' ); + + CPLFree( pszTrimmedLine ); + + nNextChar = VSIFGetc( fp ); + VSIUngetc( nNextChar, fp ); + + if( nNextChar == '#' ) + pszLine = NULL; + else + { + pszLine = CPLReadLine( fp ); + nLineCount ++; + } + } while( pszLine != NULL && nLineCount < MAX_LINE_COUNT_PER_HEADER ); + + return( papszReturn ); +} + +/************************************************************************/ +/* GXFOpen() */ +/************************************************************************/ + +/** + * Open a GXF file, and collect contents of the header. + * + * @param pszFilename the name of the file to open. + * + * @return a handle for use with other GXF functions to access the file. This + * will be NULL if the access fails. + */ + +GXFHandle GXFOpen( const char * pszFilename ) + +{ + FILE *fp; + GXFInfo_t *psGXF; + char szTitle[71]; + char **papszList; + int nHeaderCount = 0; + +/* -------------------------------------------------------------------- */ +/* We open in binary to ensure that we can efficiently seek() */ +/* to any location when reading scanlines randomly. If we */ +/* opened as text we might still be able to seek(), but I */ +/* believe that on Windows, the C library has to read through */ +/* all the data to find the right spot taking into account DOS */ +/* CRs. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpen( pszFilename, "rb" ); + + if( fp == NULL ) + { + /* how to effectively communicate this error out? */ + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open file: %s\n", pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the GXF Information object. */ +/* -------------------------------------------------------------------- */ + psGXF = (GXFInfo_t *) VSICalloc( sizeof(GXFInfo_t), 1 ); + psGXF->fp = fp; + psGXF->dfTransformScale = 1.0; + psGXF->nSense = GXFS_LL_RIGHT; + psGXF->dfXPixelSize = 1.0; + psGXF->dfYPixelSize = 1.0; + psGXF->dfSetDummyTo = -1e12; + + psGXF->dfUnitToMeter = 1.0; + psGXF->pszTitle = VSIStrdup(""); + +/* -------------------------------------------------------------------- */ +/* Read the header, one line at a time. */ +/* -------------------------------------------------------------------- */ + while( (papszList = GXFReadHeaderValue( fp, szTitle)) != NULL && nHeaderCount < MAX_HEADER_COUNT ) + { + if( EQUALN(szTitle,"#TITL",5) ) + { + CPLFree( psGXF->pszTitle ); + psGXF->pszTitle = CPLStrdup( papszList[0] ); + } + else if( EQUALN(szTitle,"#POIN",5) ) + { + psGXF->nRawXSize = atoi(papszList[0]); + } + else if( EQUALN(szTitle,"#ROWS",5) ) + { + psGXF->nRawYSize = atoi(papszList[0]); + } + else if( EQUALN(szTitle,"#PTSE",5) ) + { + psGXF->dfXPixelSize = CPLAtof(papszList[0]); + } + else if( EQUALN(szTitle,"#RWSE",5) ) + { + psGXF->dfYPixelSize = CPLAtof(papszList[0]); + } + else if( EQUALN(szTitle,"#DUMM",5) ) + { + memset( psGXF->szDummy, 0, sizeof(psGXF->szDummy)); + strncpy( psGXF->szDummy, papszList[0], sizeof(psGXF->szDummy) - 1); + psGXF->dfSetDummyTo = CPLAtof(papszList[0]); + } + else if( EQUALN(szTitle,"#XORI",5) ) + { + psGXF->dfXOrigin = CPLAtof(papszList[0]); + } + else if( EQUALN(szTitle,"#YORI",5) ) + { + psGXF->dfYOrigin = CPLAtof(papszList[0]); + } + else if( EQUALN(szTitle,"#ZMIN",5) ) + { + psGXF->dfZMinimum = CPLAtof(papszList[0]); + } + else if( EQUALN(szTitle,"#ZMAX",5) ) + { + psGXF->dfZMaximum = CPLAtof(papszList[0]); + } + else if( EQUALN(szTitle,"#SENS",5) ) + { + psGXF->nSense = atoi(papszList[0]); + } + else if( EQUALN(szTitle,"#MAP_PROJECTION",8) ) + { + psGXF->papszMapProjection = papszList; + papszList = NULL; + } + else if( EQUALN(szTitle,"#MAP_D",5) ) + { + psGXF->papszMapDatumTransform = papszList; + papszList = NULL; + } + else if( EQUALN(szTitle,"#UNIT",5) ) + { + char **papszFields; + + papszFields = CSLTokenizeStringComplex( papszList[0], ", ", + TRUE, TRUE ); + + if( CSLCount(papszFields) > 1 ) + { + psGXF->pszUnitName = VSIStrdup( papszFields[0] ); + psGXF->dfUnitToMeter = CPLAtof( papszFields[1] ); + if( psGXF->dfUnitToMeter == 0.0 ) + psGXF->dfUnitToMeter = 1.0; + } + + CSLDestroy( papszFields ); + } + else if( EQUALN(szTitle,"#TRAN",5) ) + { + char **papszFields; + + papszFields = CSLTokenizeStringComplex( papszList[0], ", ", + TRUE, TRUE ); + + if( CSLCount(papszFields) > 1 ) + { + psGXF->dfTransformScale = CPLAtof(papszFields[0]); + psGXF->dfTransformOffset = CPLAtof(papszFields[1]); + } + + if( CSLCount(papszFields) > 2 ) + psGXF->pszTransformName = CPLStrdup( papszFields[2] ); + + CSLDestroy( papszFields ); + } + else if( EQUALN(szTitle,"#GTYPE",5) ) + { + psGXF->nGType = atoi(papszList[0]); + } + + CSLDestroy( papszList ); + nHeaderCount ++; + } + +/* -------------------------------------------------------------------- */ +/* Did we find the #GRID? */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(szTitle,"#GRID",5) ) + { + GXFClose( psGXF ); + CPLError( CE_Failure, CPLE_WrongFormat, + "Didn't parse through to #GRID successfully in.\n" + "file `%s'.\n", + pszFilename ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Allocate, and initialize the raw scanline offset array. */ +/* -------------------------------------------------------------------- */ + if( psGXF->nRawYSize <= 0 ) + { + GXFClose( psGXF ); + return NULL; + } + + psGXF->panRawLineOffset = (long *) + VSICalloc( sizeof(long), psGXF->nRawYSize+1 ); + if( psGXF->panRawLineOffset == NULL ) + { + GXFClose( psGXF ); + return NULL; + } + + psGXF->panRawLineOffset[0] = VSIFTell( psGXF->fp ); + +/* -------------------------------------------------------------------- */ +/* Update the zmin/zmax values to take into account #TRANSFORM */ +/* information. */ +/* -------------------------------------------------------------------- */ + if( psGXF->dfZMinimum != 0.0 || psGXF->dfZMaximum != 0.0 ) + { + psGXF->dfZMinimum = (psGXF->dfZMinimum * psGXF->dfTransformScale) + + psGXF->dfTransformOffset; + psGXF->dfZMaximum = (psGXF->dfZMaximum * psGXF->dfTransformScale) + + psGXF->dfTransformOffset; + } + + return( (GXFHandle) psGXF ); +} + +/************************************************************************/ +/* GXFClose() */ +/************************************************************************/ + +/** + * Close GXF file opened with GXFOpen(). + * + * @param hGXF handle to GXF file. + */ + +void GXFClose( GXFHandle hGXF ) + +{ + GXFInfo_t *psGXF = (GXFInfo_t *) hGXF; + + CPLFree( psGXF->panRawLineOffset ); + CPLFree( psGXF->pszUnitName ); + CSLDestroy( psGXF->papszMapDatumTransform ); + CSLDestroy( psGXF->papszMapProjection ); + CPLFree( psGXF->pszTitle ); + CPLFree( psGXF->pszTransformName ); + + VSIFClose( psGXF->fp ); + + CPLReadLine( NULL ); + + CPLFree( psGXF ); +} + +/************************************************************************/ +/* GXFParseBase90() */ +/* */ +/* Parse a base 90 number ... exceptions (repeat, and dummy) */ +/* values have to be recognised outside this function. */ +/************************************************************************/ + +double GXFParseBase90( GXFInfo_t * psGXF, const char * pszText, + int bScale ) + +{ + int i = 0, nValue = 0; + + while( i < psGXF->nGType ) + { + nValue = nValue*90 + (pszText[i] - 37); + i++; + } + + if( bScale ) + return( (nValue * psGXF->dfTransformScale) + psGXF->dfTransformOffset); + else + return( nValue ); +} + + +/************************************************************************/ +/* GXFReadRawScanlineFrom() */ +/************************************************************************/ + +static int GXFReadRawScanlineFrom( GXFInfo_t * psGXF, long iOffset, + long * pnNewOffset, double * padfLineBuf ) + +{ + const char *pszLine; + int nValuesRead = 0, nValuesSought = psGXF->nRawXSize; + + VSIFSeek( psGXF->fp, iOffset, SEEK_SET ); + + while( nValuesRead < nValuesSought ) + { + pszLine = CPLReadLine( psGXF->fp ); + if( pszLine == NULL ) + break; + +/* -------------------------------------------------------------------- */ +/* Uncompressed case. */ +/* -------------------------------------------------------------------- */ + if( psGXF->nGType == 0 ) + { + /* we could just tokenize the line, but that's pretty expensive. + Instead I will parse on white space ``by hand''. */ + while( *pszLine != '\0' && nValuesRead < nValuesSought ) + { + int i; + + /* skip leading white space */ + for( ; isspace((unsigned char)*pszLine); pszLine++ ) {} + + /* Skip the data value (non white space) */ + for( i = 0; pszLine[i] != '\0' && !isspace((unsigned char)pszLine[i]); i++) {} + + if( strncmp(pszLine,psGXF->szDummy,i) == 0 ) + { + padfLineBuf[nValuesRead++] = psGXF->dfSetDummyTo; + } + else + { + padfLineBuf[nValuesRead++] = CPLAtof(pszLine); + } + + /* skip further whitespace */ + for( pszLine += i; isspace((unsigned char)*pszLine); pszLine++ ) {} + } + } + +/* -------------------------------------------------------------------- */ +/* Compressed case. */ +/* -------------------------------------------------------------------- */ + else + { + int nLineLen = (int)strlen(pszLine); + + while( *pszLine != '\0' && nValuesRead < nValuesSought ) + { + if( nLineLen < psGXF->nGType ) + return CE_Failure; + + if( pszLine[0] == '!' ) + { + padfLineBuf[nValuesRead++] = psGXF->dfSetDummyTo; + } + else if( pszLine[0] == '"' ) + { + int nCount, i; + double dfValue; + + pszLine += psGXF->nGType; + nLineLen -= psGXF->nGType; + if( nLineLen < psGXF->nGType ) + { + pszLine = CPLReadLine( psGXF->fp ); + if( pszLine == NULL ) + return CE_Failure; + nLineLen = (int)strlen(pszLine); + if( nLineLen < psGXF->nGType ) + return CE_Failure; + } + + nCount = (int) GXFParseBase90( psGXF, pszLine, FALSE); + pszLine += psGXF->nGType; + nLineLen -= psGXF->nGType; + + if( nLineLen < psGXF->nGType ) + { + pszLine = CPLReadLine( psGXF->fp ); + if( pszLine == NULL ) + return CE_Failure; + nLineLen = (int)strlen(pszLine); + if( nLineLen < psGXF->nGType ) + return CE_Failure; + } + + if( *pszLine == '!' ) + dfValue = psGXF->dfSetDummyTo; + else + dfValue = GXFParseBase90( psGXF, pszLine, TRUE ); + + if( nValuesRead + nCount > nValuesSought ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong count value"); + return CE_Failure; + } + + for( i=0; i < nCount && nValuesRead < nValuesSought; i++ ) + padfLineBuf[nValuesRead++] = dfValue; + } + else + { + padfLineBuf[nValuesRead++] = + GXFParseBase90( psGXF, pszLine, TRUE ); + } + + pszLine += psGXF->nGType; + nLineLen -= psGXF->nGType; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Return the new offset, if requested. */ +/* -------------------------------------------------------------------- */ + if( pnNewOffset != NULL ) + { + *pnNewOffset = VSIFTell( psGXF->fp ); + } + + return CE_None; +} + +/************************************************************************/ +/* GXFGetScanline() */ +/************************************************************************/ + +/** + * Read a scanline of raster data from GXF file. + * + * This function operates similarly to GXFGetRawScanline(), but it + * attempts to mirror data horizontally or vertically based on the #SENSE + * flag to return data in a top to bottom, and left to right organization. + * If the file is organized in columns (#SENSE is GXFS_UR_DOWN, GXFS_UL_DOWN, + * GXFS_LR_UP, or GXFS_LL_UP) then this function will fail, returning + * CE_Failure, and reporting a sense error. + * + * See GXFGetRawScanline() for other notes. + * + * @param hGXF the GXF file handle, as returned from GXFOpen(). + * @param iScanline the scanline to read, zero is the top scanline. + * @param padfLineBuf a buffer of doubles into which the scanline pixel + * values are read. This must be at least as long as a scanline. + * + * @return CE_None if access succeeds or CE_Failure if something goes wrong. + */ + +CPLErr GXFGetScanline( GXFHandle hGXF, int iScanline, double * padfLineBuf ) + +{ + GXFInfo_t *psGXF = (GXFInfo_t *) hGXF; + CPLErr nErr; + int iRawScanline; + + if( psGXF->nSense == GXFS_LL_RIGHT + || psGXF->nSense == GXFS_LR_LEFT ) + { + iRawScanline = psGXF->nRawYSize - iScanline - 1; + } + + else if( psGXF->nSense == GXFS_UL_RIGHT + || psGXF->nSense == GXFS_UR_LEFT ) + { + iRawScanline = iScanline; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to support vertically oriented images." ); + return( CE_Failure ); + } + + nErr = GXFGetRawScanline( hGXF, iRawScanline, padfLineBuf ); + + if( nErr == CE_None + && (psGXF->nSense == GXFS_LR_LEFT || psGXF->nSense == GXFS_UR_LEFT) ) + { + int i; + double dfTemp; + + for( i = psGXF->nRawXSize / 2 - 1; i >= 0; i-- ) + { + dfTemp = padfLineBuf[i]; + padfLineBuf[i] = padfLineBuf[psGXF->nRawXSize-i-1]; + padfLineBuf[psGXF->nRawXSize-i-1] = dfTemp; + } + } + + return( nErr ); +} + +/************************************************************************/ +/* GXFGetRawScanline() */ +/************************************************************************/ + +/** + * Read a scanline of raster data from GXF file. + * + * This function will read a row of data from the GXF file. It is "Raw" + * in the sense that it doesn't attempt to account for the #SENSE flag as + * the GXFGetScanline() function does. Unlike GXFGetScanline(), this function + * supports column organized files. + * + * Any dummy pixels are assigned the dummy value indicated by GXFGetRawInfo(). + * + * @param hGXF the GXF file handle, as returned from GXFOpen(). + * @param iScanline the scanline to read, zero is the first scanline in the + * file. + * @param padfLineBuf a buffer of doubles into which the scanline pixel + * values are read. This must be at least as long as a scanline. + * + * @return CE_None if access succeeds or CE_Failure if something goes wrong. + */ + +CPLErr GXFGetRawScanline( GXFHandle hGXF, int iScanline, double * padfLineBuf ) + +{ + GXFInfo_t *psGXF = (GXFInfo_t *) hGXF; + CPLErr nErr; + +/* -------------------------------------------------------------------- */ +/* Validate scanline. */ +/* -------------------------------------------------------------------- */ + if( iScanline < 0 || iScanline >= psGXF->nRawYSize ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "GXFGetRawScanline(): Scanline `%d' does not exist.\n", + iScanline ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* If we don't have the requested scanline, fetch preceeding */ +/* scanlines to find the pointer to this scanline. */ +/* -------------------------------------------------------------------- */ + if( psGXF->panRawLineOffset[iScanline] == 0 ) + { + int i; + + CPLAssert( iScanline > 0 ); + + for( i = 0; i < iScanline; i++ ) + { + if( psGXF->panRawLineOffset[i+1] == 0 ) + { + nErr = GXFGetRawScanline( hGXF, i, padfLineBuf ); + if( nErr != CE_None ) + return( nErr ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Get this scanline, and update the offset for the next line. */ +/* -------------------------------------------------------------------- */ + nErr = (CPLErr) + GXFReadRawScanlineFrom( psGXF, psGXF->panRawLineOffset[iScanline], + psGXF->panRawLineOffset+iScanline+1, + padfLineBuf ); + + return nErr; +} + +/************************************************************************/ +/* GXFScanForZMinMax() */ +/* */ +/* The header doesn't contain the ZMin/ZMax values, but the */ +/* application has requested it ... scan the entire image for */ +/* it. */ +/************************************************************************/ + +static void GXFScanForZMinMax( GXFHandle hGXF ) + +{ + GXFInfo_t *psGXF = (GXFInfo_t *) hGXF; + int iLine, iPixel; + double *padfScanline; + + + padfScanline = (double *) VSICalloc(sizeof(double),psGXF->nRawXSize); + if( padfScanline == NULL ) + return; + + psGXF->dfZMinimum = 1e50; + psGXF->dfZMaximum = -1e50; + + for( iLine = 0; iLine < psGXF->nRawYSize; iLine++ ) + { + if( GXFGetRawScanline( hGXF, iLine, padfScanline ) != CE_None ) + break; + + for( iPixel = 0; iPixel < psGXF->nRawXSize; iPixel++ ) + { + if( padfScanline[iPixel] != psGXF->dfSetDummyTo ) + { + psGXF->dfZMinimum = + MIN(psGXF->dfZMinimum,padfScanline[iPixel]); + psGXF->dfZMaximum = + MAX(psGXF->dfZMaximum,padfScanline[iPixel]); + } + } + } + + VSIFree( padfScanline ); + +/* -------------------------------------------------------------------- */ +/* Did we get any real data points? */ +/* -------------------------------------------------------------------- */ + if( psGXF->dfZMinimum > psGXF->dfZMaximum ) + { + psGXF->dfZMinimum = 0.0; + psGXF->dfZMaximum = 0.0; + } +} + +/************************************************************************/ +/* GXFGetRawInfo() */ +/************************************************************************/ + +/** + * Fetch header information about a GXF file. + * + * Note that the X and Y sizes are of the raw raster and don't take into + * account the #SENSE flag. If the file is column oriented (rows in the + * files are actually columns in the raster) these values would need to be + * transposed for the actual raster. + * + * The legal pnSense values are: + *
    + *
  • GXFS_LL_UP(-1): lower left origin, scanning up. + *
  • GXFS_LL_RIGHT(1): lower left origin, scanning right. + *
  • GXFS_UL_RIGHT(-2): upper left origin, scanning right. + *
  • GXFS_UL_DOWN(2): upper left origin, scanning down. + *
  • GXFS_UR_DOWN(-3): upper right origin, scanning down. + *
  • GXFS_UR_LEFT(3): upper right origin, scanning left. + *
  • GXFS_LR_LEFT(-4): lower right origin, scanning left. + *
  • GXFS_LR_UP(4): lower right origin, scanning up. + *
+ * + * Note that the GXFGetScanline() function attempts to provide a GXFS_UL_RIGHT + * view onto files, but doesn't handle the *_DOWN and *_UP oriented files. + * + * The Z min and max values may not occur in the GXF header. If they are + * requested, and aren't available in the header the entire file is scanned + * in order to establish them. This can be expensive. + * + * If no #DUMMY value was specified in the file, a default of -1e12 is used. + * + * @param hGXF handle to GXF file returned by GXFOpen(). + * @param pnXSize int to be set with the width of the raw raster. May be NULL. + * @param pnYSize int to be set with the height of the raw raster. May be NULL. + * @param pnSense int to set with #SENSE flag, may be NULL. + * @param pdfZMin double to set with minimum raster value, may be NULL. + * @param pdfZMax double to set with minimum raster value, may be NULL. + * @param pdfDummy double to set with dummy (nodata / invalid data) pixel + * value. + */ + +CPLErr GXFGetRawInfo( GXFHandle hGXF, int *pnXSize, int *pnYSize, + int * pnSense, double * pdfZMin, double * pdfZMax, + double * pdfDummy ) + +{ + GXFInfo_t *psGXF = (GXFInfo_t *) hGXF; + + if( pnXSize != NULL ) + *pnXSize = psGXF->nRawXSize; + + if( pnYSize != NULL ) + *pnYSize = psGXF->nRawYSize; + + if( pnSense != NULL ) + *pnSense = psGXF->nSense; + + if( (pdfZMin != NULL || pdfZMax != NULL) + && psGXF->dfZMinimum == 0.0 && psGXF->dfZMaximum == 0.0 ) + { + GXFScanForZMinMax( hGXF ); + } + + if( pdfZMin != NULL ) + *pdfZMin = psGXF->dfZMinimum; + + if( pdfZMax != NULL ) + *pdfZMax = psGXF->dfZMaximum; + + if( pdfDummy != NULL ) + *pdfDummy = psGXF->dfSetDummyTo; + + return( CE_None ); +} + +/************************************************************************/ +/* GXFGetMapProjection() */ +/************************************************************************/ + +/** + * Return the lines related to the map projection. It is up to + * the caller to parse them and interprete. The return result + * will be NULL if no #MAP_PROJECTION line was found in the header. + * + * @param hGXF the GXF file handle. + * + * @return a NULL terminated array of string pointers containing the + * projection, or NULL. The strings remained owned by the GXF API, and + * should not be modified or freed by the caller. + */ + +char **GXFGetMapProjection( GXFHandle hGXF ) + +{ + return( ((GXFInfo_t *) hGXF)->papszMapProjection ); +} + +/************************************************************************/ +/* GXFGetMapDatumTransform() */ +/************************************************************************/ + +/** + * Return the lines related to the datum transformation. It is up to + * the caller to parse them and interpret. The return result + * will be NULL if no #MAP_DATUM_TRANSFORM line was found in the header. + * + * @param hGXF the GXF file handle. + * + * @return a NULL terminated array of string pointers containing the + * datum, or NULL. The strings remained owned by the GXF API, and + * should not be modified or freed by the caller. + */ + +char **GXFGetMapDatumTransform( GXFHandle hGXF ) + +{ + return( ((GXFInfo_t *) hGXF)->papszMapDatumTransform ); +} + +/************************************************************************/ +/* GXFGetRawPosition() */ +/************************************************************************/ + +/** + * Get the raw grid positioning information. + * + * Note that these coordinates refer to the raw grid, and are in the units + * specified by the #UNITS field. See GXFGetPosition() for a similar + * function that takes into account the #SENSE values similarly to + * GXFGetScanline(). + * + * Note that the pixel values are considered to be point values in GXF, + * and thus the origin is for the first point. If you consider the pixels + * to be areas, then the origin is for the center of the origin pixel, not + * the outer corner. + * + * @param hGXF the GXF file handle. + * @param pdfXOrigin X position of the origin in the base coordinate system. + * @param pdfYOrigin Y position of the origin in the base coordinate system. + * @param pdfXPixelSize X pixel size in base coordinates. + * @param pdfYPixelSize Y pixel size in base coordinates. + * @param pdfRotation rotation in degrees counter-clockwise from the + * base coordinate system. + * + * @return Returns CE_None if successful, or CE_Failure if no posiitioning + * information was found in the file. + */ + + +CPLErr GXFGetRawPosition( GXFHandle hGXF, + double * pdfXOrigin, double * pdfYOrigin, + double * pdfXPixelSize, double * pdfYPixelSize, + double * pdfRotation ) + +{ + GXFInfo_t *psGXF = (GXFInfo_t *) hGXF; + + if( pdfXOrigin != NULL ) + *pdfXOrigin = psGXF->dfXOrigin; + if( pdfYOrigin != NULL ) + *pdfYOrigin = psGXF->dfYOrigin; + if( pdfXPixelSize != NULL ) + *pdfXPixelSize = psGXF->dfXPixelSize; + if( pdfYPixelSize != NULL ) + *pdfYPixelSize = psGXF->dfYPixelSize; + if( pdfRotation != NULL ) + *pdfRotation = psGXF->dfRotation; + + if( psGXF->dfXOrigin == 0.0 && psGXF->dfYOrigin == 0.0 + && psGXF->dfXPixelSize == 0.0 && psGXF->dfYPixelSize == 0.0 ) + return( CE_Failure ); + else + return( CE_None ); +} + + +/************************************************************************/ +/* GXFGetPosition() */ +/************************************************************************/ + +/** + * Get the grid positioning information. + * + * Note that these coordinates refer to the grid positioning after taking + * into account the #SENSE flag (as is done by the GXFGetScanline()) function. + * + * Note that the pixel values are considered to be point values in GXF, + * and thus the origin is for the first point. If you consider the pixels + * to be areas, then the origin is for the center of the origin pixel, not + * the outer corner. + * + * This function does not support vertically oriented images, nor does it + * properly transform rotation for images with a SENSE other than + * GXFS_UL_RIGHT. + * + * @param hGXF the GXF file handle. + * @param pdfXOrigin X position of the origin in the base coordinate system. + * @param pdfYOrigin Y position of the origin in the base coordinate system. + * @param pdfXPixelSize X pixel size in base coordinates. + * @param pdfYPixelSize Y pixel size in base coordinates. + * @param pdfRotation rotation in degrees counter-clockwise from the + * base coordinate system. + * + * @return Returns CE_None if successful, or CE_Failure if no posiitioning + * information was found in the file. + */ + + +CPLErr GXFGetPosition( GXFHandle hGXF, + double * pdfXOrigin, double * pdfYOrigin, + double * pdfXPixelSize, double * pdfYPixelSize, + double * pdfRotation ) + +{ + GXFInfo_t *psGXF = (GXFInfo_t *) hGXF; + double dfCXOrigin, dfCYOrigin, dfCXPixelSize, dfCYPixelSize; + + switch( psGXF->nSense ) + { + case GXFS_UL_RIGHT: + dfCXOrigin = psGXF->dfXOrigin; + dfCYOrigin = psGXF->dfYOrigin; + dfCXPixelSize = psGXF->dfXPixelSize; + dfCYPixelSize = psGXF->dfYPixelSize; + break; + + case GXFS_UR_LEFT: + dfCXOrigin = psGXF->dfXOrigin + - (psGXF->nRawXSize-1) * psGXF->dfXPixelSize; + dfCYOrigin = psGXF->dfYOrigin; + dfCXPixelSize = psGXF->dfXPixelSize; + dfCYPixelSize = psGXF->dfYPixelSize; + break; + + case GXFS_LL_RIGHT: + dfCXOrigin = psGXF->dfXOrigin; + dfCYOrigin = psGXF->dfYOrigin + + (psGXF->nRawYSize-1) * psGXF->dfYPixelSize; + dfCXPixelSize = psGXF->dfXPixelSize; + dfCYPixelSize = psGXF->dfYPixelSize; + break; + + case GXFS_LR_LEFT: + dfCXOrigin = psGXF->dfXOrigin + - (psGXF->nRawXSize-1) * psGXF->dfXPixelSize; + dfCYOrigin = psGXF->dfYOrigin + + (psGXF->nRawYSize-1) * psGXF->dfYPixelSize; + dfCXPixelSize = psGXF->dfXPixelSize; + dfCYPixelSize = psGXF->dfYPixelSize; + break; + + default: + CPLError( CE_Failure, CPLE_AppDefined, + "GXFGetPosition() doesn't support vertically organized images." ); + return CE_Failure; + } + + if( pdfXOrigin != NULL ) + *pdfXOrigin = dfCXOrigin; + if( pdfYOrigin != NULL ) + *pdfYOrigin = dfCYOrigin; + if( pdfXPixelSize != NULL ) + *pdfXPixelSize = dfCXPixelSize; + if( pdfYPixelSize != NULL ) + *pdfYPixelSize = dfCYPixelSize; + if( pdfRotation != NULL ) + *pdfRotation = psGXF->dfRotation; + + if( psGXF->dfXOrigin == 0.0 && psGXF->dfYOrigin == 0.0 + && psGXF->dfXPixelSize == 0.0 && psGXF->dfYPixelSize == 0.0 ) + return( CE_Failure ); + else + return( CE_None ); +} + + diff --git a/bazaar/plugin/gdal/frmts/gxf/gxfopen.h b/bazaar/plugin/gdal/frmts/gxf/gxfopen.h new file mode 100644 index 000000000..ac915c8d9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/gxfopen.h @@ -0,0 +1,126 @@ +/****************************************************************************** + * $Id: gxfopen.h 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: GXF Reader + * Purpose: GXF-3 access function declarations. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Global Geomatics + * Copyright (c) 1998, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _GXFOPEN_H_INCLUDED +#define _GXFOPEN_H_INCLUDED + +/** + * \file gxfopen.h + * + * Public GXF-3 function definitions. + */ + +/* -------------------------------------------------------------------- */ +/* Include standard portability stuff. */ +/* -------------------------------------------------------------------- */ +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_C_START + +typedef void *GXFHandle; + +GXFHandle GXFOpen( const char * pszFilename ); + +CPLErr GXFGetRawInfo( GXFHandle hGXF, int *pnXSize, int *pnYSize, + int *pnSense, double * pdfZMin, double * pdfZMax, + double * pdfDummy ); +CPLErr GXFGetInfo( GXFHandle hGXF, int *pnXSize, int *pnYSize ); + +CPLErr GXFGetRawScanline( GXFHandle, int iScanline, double * padfLineBuf ); +CPLErr GXFGetScanline( GXFHandle, int iScanline, double * padfLineBuf ); + +char **GXFGetMapProjection( GXFHandle ); +char **GXFGetMapDatumTransform( GXFHandle ); +char *GXFGetMapProjectionAsPROJ4( GXFHandle ); +char *GXFGetMapProjectionAsOGCWKT( GXFHandle ); + +CPLErr GXFGetRawPosition( GXFHandle, double *, double *, double *, double *, + double * ); +CPLErr GXFGetPosition( GXFHandle, double *, double *, double *, double *, + double * ); + +CPLErr GXFGetPROJ4Position( GXFHandle, double *, double *, double *, double *, + double * ); + +void GXFClose( GXFHandle hGXF ); + +#define GXFS_LL_UP -1 +#define GXFS_LL_RIGHT 1 +#define GXFS_UL_RIGHT -2 +#define GXFS_UL_DOWN 2 +#define GXFS_UR_DOWN -3 +#define GXFS_UR_LEFT 3 +#define GXFS_LR_LEFT -4 +#define GXFS_LR_UP 4 + +CPL_C_END + +/* -------------------------------------------------------------------- */ +/* This is consider to be a private structure. */ +/* -------------------------------------------------------------------- */ +typedef struct { + FILE *fp; + + int nRawXSize; + int nRawYSize; + int nSense; /* GXFS_ codes */ + int nGType; /* 0 is uncompressed */ + + double dfXPixelSize; + double dfYPixelSize; + double dfRotation; + double dfXOrigin; /* lower left corner */ + double dfYOrigin; /* lower left corner */ + + char szDummy[64]; + double dfSetDummyTo; + + char *pszTitle; + + double dfTransformScale; + double dfTransformOffset; + char *pszTransformName; + + char **papszMapProjection; + char **papszMapDatumTransform; + + char *pszUnitName; + double dfUnitToMeter; + + + double dfZMaximum; + double dfZMinimum; + + long *panRawLineOffset; + +} GXFInfo_t; + +#endif /* ndef _GXFOPEN_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/gxf/makefile.vc b/bazaar/plugin/gdal/frmts/gxf/makefile.vc new file mode 100644 index 000000000..9610488cc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = gxfopen.obj gxfdataset.obj gxf_ogcwkt.obj + +EXTRAFLAGS = -I..\iso8211 + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/gxf/makefile.vc.dist b/bazaar/plugin/gdal/frmts/gxf/makefile.vc.dist new file mode 100644 index 000000000..6bdc0e0de --- /dev/null +++ b/bazaar/plugin/gdal/frmts/gxf/makefile.vc.dist @@ -0,0 +1,14 @@ + +OBJ = gxfopen.obj gxf_proj4.obj gxf_ogcwkt.obj \ + \ + cpl_error.obj cpl_vsisimple.obj cpl_string.obj cpl_conv.obj + +default: gxftest.exe + +gxf3.lib: $(OBJ) + lib /out:gxf3.lib $(OBJ) + +gxftest.exe: gxftest.c gxf3.lib + $(CC) $(CFLAGS) gxftest.c gxf3.lib + + diff --git a/bazaar/plugin/gdal/frmts/hdf4/GNUmakefile b/bazaar/plugin/gdal/frmts/hdf4/GNUmakefile new file mode 100644 index 000000000..409e0028a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/GNUmakefile @@ -0,0 +1,27 @@ + +include ../../GDALmake.opt + +OBJ = hdf4dataset.o hdf4imagedataset.o + +SUBLIBS = lib-hdfeos + +CPPFLAGS := -I../pds -Ihdf-eos $(HDF4_INCLUDE) $(HDF4_FLAGS) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) $(SUBLIBS) + +clean: + rm -f *.o $(O_OBJ) + (cd hdf-eos; $(MAKE) clean) + +lib-hdfeos: + (cd hdf-eos; $(MAKE) install-obj) + +install-obj: $(SUBLIBS) $(O_OBJ:.o=.$(OBJ_EXT)) + +PLUGIN_SO = gdal_HDF4.so + +plugin: $(PLUGIN_SO) + +$(PLUGIN_SO): $(OBJ) + $(LD_SHARED) $(LNK_FLAGS) $(OBJ) $(CONFIG_LIBS_INS) $(LIBS) \ + -o $(PLUGIN_SO) diff --git a/bazaar/plugin/gdal/frmts/hdf4/frmt_hdf4.html b/bazaar/plugin/gdal/frmts/hdf4/frmt_hdf4.html new file mode 100644 index 000000000..34fa36b8c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/frmt_hdf4.html @@ -0,0 +1,271 @@ + + + + +HDF4 --- Hierarchical Data Format Release 4 (HDF4) + + + + +

HDF4 --- Hierarchical Data Format Release 4 (HDF4)

+ +There are two HDF formats, HDF4 (4.x and previous releases) and HDF5. +These formats are completely different and NOT compatible. This driver +intended for HDF4 file formats importing. NASA's Earth Observing System (EOS) +maintains its own HDF modification called HDF-EOS. This modification is +suited for use with remote sensing data and fully compatible with +underlying HDF. This driver can import HDF4-EOS files. Currently EOS use +HDF4-EOS for data storing (telemetry form `Terra' and `Aqua' satellites). In +the future they will switch to HDF5-EOS format, which will be used for +telemetry from `Aura' satellite.

+ +

Multiple Image Handling (Subdatasets)

+ +Hierarchical Data Format is a container for several different datasets. +For data storing Scientific Datasets (SDS) used most often. SDS is a +multidimensional array filled by data. One HDF file may contain several +different SDS arrays. They may differ in size, number of dimensions and may +represent data for different regions.

+ +If the file contains only one SDS that appears to be an image, it may be +accessed normally, but if it contains multiple images it may be necessary to +import the file via a two step process. The first step is to get a report +of the components images (SDS arrays) in the file using gdalinfo, and +then to import the desired images using gdal_translate. + +The gdalinfo utility lists all multidimensional subdatasets from the +input HDF file. The name of individual images (subdatasets) are assigned to +the SUBDATASET_n_NAME metadata item. The description for each image is +found in the SUBDATASET_n_DESC metadata item. For HDF4 images the +subdataset names will be formatted like this:

+ +HDF4_SDS:subdataset_type:file_name:subdataset_index

+ +where subdataset_type shows predefined names for some of the well +known HDF datasets, file_name is the name of the input file, and +subdataset_index is the index of the image to use (for internal use in +GDAL).

+ +On the second step you should provide this name for gdalinfo or +gdal_translate for actual reading of the data.

+ +For example, we want to read data from the MODIS Level 1B dataset:

+

+$ gdalinfo GSUB1.A2001124.0855.003.200219309451.hdf
+Driver: HDF4/Hierarchical Data Format Release 4
+Size is 512, 512
+Coordinate System is `'
+Metadata:
+  HDFEOSVersion=HDFEOS_V2.7
+  Number of Scans=204
+  Number of Day mode scans=204
+  Number of Night mode scans=0
+  Incomplete Scans=0
+
+...a lot of metadata output skipped...

+

+Subdatasets:
+  SUBDATASET_1_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:0
+  SUBDATASET_1_DESC=[408x271] Latitude (32-bit floating-point)
+  SUBDATASET_2_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:1
+  SUBDATASET_2_DESC=[408x271] Longitude (32-bit floating-point)
+  SUBDATASET_3_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:2
+  SUBDATASET_3_DESC=[12x2040x1354] EV_1KM_RefSB (16-bit unsigned integer)
+  SUBDATASET_4_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:3
+  SUBDATASET_4_DESC=[12x2040x1354] EV_1KM_RefSB_Uncert_Indexes (8-bit unsigned integer)
+  SUBDATASET_5_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:4
+  SUBDATASET_5_DESC=[408x271] Height (16-bit integer)
+  SUBDATASET_6_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:5
+  SUBDATASET_6_DESC=[408x271] SensorZenith (16-bit integer)
+  SUBDATASET_7_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:6
+  SUBDATASET_7_DESC=[408x271] SensorAzimuth (16-bit integer)
+  SUBDATASET_8_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:7
+  SUBDATASET_8_DESC=[408x271] Range (16-bit unsigned integer)
+  SUBDATASET_9_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:8
+  SUBDATASET_9_DESC=[408x271] SolarZenith (16-bit integer)
+  SUBDATASET_10_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:9
+  SUBDATASET_10_DESC=[408x271] SolarAzimuth (16-bit integer)
+  SUBDATASET_11_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:10
+  SUBDATASET_11_DESC=[408x271] gflags (8-bit unsigned integer)
+  SUBDATASET_12_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:12
+  SUBDATASET_12_DESC=[16x10] Noise in Thermal Detectors (8-bit unsigned integer)
+  SUBDATASET_13_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:13
+  SUBDATASET_13_DESC=[16x10] Change in relative responses of thermal detectors (8-bit unsigned integer)
+  SUBDATASET_14_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:14
+  SUBDATASET_14_DESC=[204x16x10] DC Restore Change for Thermal Bands (8-bit integer)
+  SUBDATASET_15_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:15
+  SUBDATASET_15_DESC=[204x2x40] DC Restore Change for Reflective 250m Bands (8-bit integer)
+  SUBDATASET_16_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:16
+  SUBDATASET_16_DESC=[204x5x20] DC Restore Change for Reflective 500m Bands (8-bit integer)
+  SUBDATASET_17_NAME=HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:17
+  SUBDATASET_17_DESC=[204x15x10] DC Restore Change for Reflective 1km Bands (8-bit integer)
+Corner Coordinates:
+Upper Left  (    0.0,    0.0)
+Lower Left  (    0.0,  512.0)
+Upper Right (  512.0,    0.0)
+Lower Right (  512.0,  512.0)
+Center      (  256.0,  256.0)
+
+ +Now select one of the subdatasets, described as +[12x2040x1354] EV_1KM_RefSB (16-bit unsigned integer):

+

+$ gdalinfo HDF4_SDS:MODIS_L1B:GSUB1.A2001124.0855.003.200219309451.hdf:2
+Driver: HDF4Image/HDF4 Internal Dataset
+Size is 1354, 2040
+Coordinate System is `'
+Metadata:
+  long_name=Earth View 1KM Reflective Solar Bands Scaled Integers
+
+...metadata skipped...

+

+Corner Coordinates:
+Upper Left  (    0.0,    0.0)
+Lower Left  (    0.0, 2040.0)
+Upper Right ( 1354.0,    0.0)
+Lower Right ( 1354.0, 2040.0)
+Center      (  677.0, 1020.0)
+Band 1 Block=1354x2040 Type=UInt16, ColorInterp=Undefined
+Band 2 Block=1354x2040 Type=UInt16, ColorInterp=Undefined
+Band 3 Block=1354x2040 Type=UInt16, ColorInterp=Undefined
+Band 4 Block=1354x2040 Type=UInt16, ColorInterp=Undefined
+Band 5 Block=1354x2040 Type=UInt16, ColorInterp=Undefined
+Band 6 Block=1354x2040 Type=UInt16, ColorInterp=Undefined
+Band 7 Block=1354x2040 Type=UInt16, ColorInterp=Undefined
+Band 8 Block=1354x2040 Type=UInt16, ColorInterp=Undefined
+Band 9 Block=1354x2040 Type=UInt16, ColorInterp=Undefined
+Band 10 Block=1354x2040 Type=UInt16, ColorInterp=Undefined
+Band 11 Block=1354x2040 Type=UInt16, ColorInterp=Undefined
+Band 12 Block=1354x2040 Type=UInt16, ColorInterp=Undefined
+
+ +Or you may use gdal_translate for reading image bands from this +dataset.

+ +Note that you should provide exactly the contents of the line marked +SUBDATASET_n_NAME to GDAL, including the HDF4_SDS: prefix.

+ +This driver is intended only for importing remote sensing and geospatial +datasets in form of raster images. If you want explore all data contained in +HDF file you should use another tools (you can find information about +different HDF tools using links at end of this page). + +

Georeference

+ +There is no universal way of storing georeferencing in HDF files. However, +some product types have mechanisms for saving georeferencing, and some of +these are supported by GDAL. Currently supported are (subdataset_type +shown in parenthesis):

+ +

    +
  • HDF4 files created by GDAL (GDAL_HDF4) + +
  • ASTER Level 1A (ASTER_L1A) + +
  • ASTER Level 1B (ASTER_L1B) + +
  • ASTER Level 2 (ASTER_L2) + +
  • ASTER DEM (AST14DEM) + +
  • MODIS Level 1B Earth View products (MODIS_L1B) + +
  • MODIS Level 3 products (MODIS_L3) + +
  • SeaWiFS Level 3 Standard Mapped Image Products (SEAWIFS_L3) +
+ +

+By default the hdf4 driver only reads the gcps from every 10th row and column +from EOS_SWATH datasets. You can change this behaviour by setting the +GEOL_AS_GCPS environment variable to PARTIAL (default), NONE, or FULL. +

+ + +

Creation Issues

+ +This driver supports creation of the HDF4 Scientific Datasets. You may create +set of 2D datasets (one per each input band) or single 3D dataset where the third +dimension represents band numbers. All metadata and band descriptions from +the input dataset are stored as HDF4 attributes. Projection information (if it +exists) and affine transformation coefficients also stored in form of +attributes. +Files, created by GDAL have the special attribute:

+ +"Signature=Created with GDAL (http://www.remotesensing.org/gdal/)"

+ +and are automatically recognised when read, so the projection info and +transformation matrix restored back.

+ +Creation Options:

+ +

    +
  • RANK=n: Create n-dimensional SDS. Currently only 2D + and 3D datasets supported. By default a 3-dimensional dataset will be + created.

    + + + +

+ +

Metadata

+ +All HDF4 attributes are transparently translated as GDAL metadata. In the HDF +file attributes may be assigned assigned to the whole file as well as to +particular subdatasets. + +

Driver building

+ +This driver builded on top of NCSA HDF library, so you need one to compile +GDAL with HDF4 support. You may search your operating system distribution for +the precompiled binaries or download source code or binaries from the NCSA HDF +Home Page (see links below).

+ +Please note, that NCSA HDF library compiled with several defaults which is +defined in hlimits.h file. For example, hlimits.h defines +the maximum number of opened files: + +

+#   define MAX_FILE   32
+
+ +If you need open more HDF4 files simultaneously you should change this value +and rebuild HDF4 library (and relink GDAL if using static HDF libraries).

+ +

See Also:

+ + + +Documentation to individual products, supported by this driver:

+ +

+ + + diff --git a/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/EHapi.c b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/EHapi.c new file mode 100644 index 000000000..4dc1e8819 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/EHapi.c @@ -0,0 +1,3777 @@ +/***************************************************************************** + * $Id: EHapi.c 25847 2013-04-03 09:45:20Z dron $ + * + * This module has a number of additions and improvements over the original + * implementation to be suitable for usage in GDAL HDF driver. + * + * Andrey Kiselev is responsible for all the changes. + ****************************************************************************/ + +/* +Copyright (C) 1996 Hughes and Applied Research Corporation + +Permission to use, modify, and distribute this software and its documentation +for any purpose without fee is hereby granted, provided that the above +copyright notice appear in all copies and that both that copyright notice and +this permission notice appear in supporting documentation. +*/ + +#include +#include "mfhdf.h" +#include "HdfEosDef.h" + +/* Set maximum number of HDF-EOS files to HDF limit (MAX_FILE) */ +#define NEOSHDF MAX_FILE +static intn EHXmaxfilecount = 0; +static uint8 *EHXtypeTable = NULL; +static uint8 *EHXacsTable = NULL; +static int32 *EHXfidTable = NULL; +static int32 *EHXsdTable = NULL; + +/* define a macro for the string size of the utility strings and some dimension + list strings. The value in previous versions of this code may not be + enough in some cases. The length now is 512 which seems to be more than + enough to hold larger strings. */ + +#define UTLSTR_MAX_SIZE 512 +#define UTLSTRSIZE 32000 + +#define EHIDOFFSET 524288 + +#define HDFEOSVERSION 2.12 +#define HDFEOSVERSION1 "2.12" +#include + +#define MAX_RETRIES 10 + +/* Function Prototypes */ +static intn EHmetalist(char *, char *); +static intn EHreset_maxopenfiles(intn); +static intn EHget_maxopenfiles(intn *, intn *); +static intn EHget_numfiles(); + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHopen | +| | +| DESCRIPTION: Opens HDF-EOS file and returns file handle | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| fid int32 HDF-EOS file ID | +| | +| INPUTS: | +| filename char Filename | +| access intn HDF access code | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Jul 96 Joel Gales Add file id offset EHIDOFFSET | +| Aug 96 Joel Gales Add "END" statment to structural metadata | +| Sep 96 Joel Gales Reverse order of Hopen ane SDstart statements | +| for RDWR and READ access | +| Oct 96 Joel Gales Trap CREATE & RDWR (no write permission) | +| access errors | +| Apr 97 Joel Gales Fix problem with RDWR open when file previously | +| open for READONLY access | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +EHopen(char *filename, intn access) + +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + intn dum; /* Dummy variable */ + intn curr_max = 0; /* maximum # of HDF files to open */ + intn sys_limit = 0; /* OS limit for maximum # of opened files */ + + int32 HDFfid; /* HDF file ID */ + int32 fid = -1; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 attrIndex; /* Structural Metadata attribute index */ + + uint8 acs; /* Read (0) / Write (1) access code */ + + char *testname; /* Test filename */ + char errbuf[256];/* Error report buffer */ + char *metabuf; /* Pointer to structural metadata buffer */ + char hdfeosVersion[32]; /* HDFEOS version string */ + + intn retryCount; + + /* Request the system allowed number of opened files */ + /* and increase HDFEOS file tables to the same size */ + /* ------------------------------------------------- */ + if (EHget_maxopenfiles(&curr_max, &sys_limit) >= 0 + && curr_max < sys_limit) + { + if (EHreset_maxopenfiles(sys_limit) < 0) + { + HEpush(DFE_ALROPEN, "EHopen", __FILE__, __LINE__); + HEreport("Can't set maximum opened files number to \"%d\".\n", curr_max); + return -1; + } + } + + /* Setup file interface */ + /* -------------------- */ + if (EHget_numfiles() < EHXmaxfilecount) + { + + /* + * Check that file has not been previously opened for write access if + * current open request is not READONLY + */ + if (access != DFACC_READ) + { + /* Loop through all files */ + /* ---------------------- */ + for (i = 0; i < EHXmaxfilecount; i++) + { + /* if entry is active file opened for write access ... */ + /* --------------------------------------------------- */ + if (EHXtypeTable[i] != 0 && EHXacsTable[i] == 1) + { + /* Get filename (testname) */ + /* ----------------------- */ + Hfidinquire(EHXfidTable[i], &testname, &dum, &dum); + + + /* if same as filename then report error */ + /* ------------------------------------- */ + if (strcmp(testname, filename) == 0) + { + status = -1; + fid = -1; + HEpush(DFE_ALROPEN, "EHopen", __FILE__, __LINE__); + HEreport("\"%s\" already open.\n", filename); + break; + } + } + } + } + if (status == 0) + { + /* Create HDF-EOS file */ + /* ------------------- */ + switch (access) + { + case DFACC_CREATE: + + /* Get SDS interface ID */ + /* -------------------- */ + sdInterfaceID = SDstart(filename, DFACC_CREATE); + + /* If SDstart successful ... */ + /* ------------------------- */ + if (sdInterfaceID != -1) + { + /* Set HDFEOS version number in file */ + /* --------------------------------- */ + sprintf(hdfeosVersion, "%s%s", "HDFEOS_V", + HDFEOSVERSION1); + SDsetattr(sdInterfaceID, "HDFEOSVersion", DFNT_CHAR8, + strlen(hdfeosVersion), hdfeosVersion); + + + /* Get HDF file ID */ + /* --------------- */ + HDFfid = Hopen(filename, DFACC_RDWR, 0); + + /* Set open access to write */ + /* ------------------------ */ + acs = 1; + + /* Setup structural metadata */ + /* ------------------------- */ + metabuf = (char *) calloc(32000, 1); + if(metabuf == NULL) + { + HEpush(DFE_NOSPACE,"EHopen", __FILE__, __LINE__); + return(-1); + } + + strcpy(metabuf, "GROUP=SwathStructure\n"); + strcat(metabuf, "END_GROUP=SwathStructure\n"); + strcat(metabuf, "GROUP=GridStructure\n"); + strcat(metabuf, "END_GROUP=GridStructure\n"); + strcat(metabuf, "GROUP=PointStructure\n"); + strcat(metabuf, "END_GROUP=PointStructure\n"); + strcat(metabuf, "END\n"); + + /* Write Structural metadata */ + /* ------------------------- */ + SDsetattr(sdInterfaceID, "StructMetadata.0", + DFNT_CHAR8, 32000, metabuf); + free(metabuf); + } else + { + /* If error in SDstart then report */ + /* ------------------------------- */ + fid = -1; + status = -1; + HEpush(DFE_FNF, "EHopen", __FILE__, __LINE__); + sprintf(errbuf, "%s%s%s", "\"", filename, + "\" cannot be created."); + HEreport("%s\n", errbuf); + } + + break; + + /* Open existing HDF-EOS file for read/write access */ + /* ------------------------------------------------ */ + case DFACC_RDWR: + + /* Get HDF file ID */ + /* --------------- */ +#ifndef _PGS_OLDNFS +/* The following loop around the function Hopen is intended to deal with the NFS cache + problem when opening file fails with errno = 150 or 151. When NFS cache is updated, + this part of change is no longer neccessary. 10/18/1999 */ + retryCount = 0; + HDFfid = -1; + while ((HDFfid == -1) && (retryCount < MAX_RETRIES)) + { + HDFfid = Hopen(filename, DFACC_RDWR, 0); + if((HDFfid == -1) && (errno == 150 || errno == 151)) + { + HEpush(DFE_FNF, "EHopen", __FILE__, __LINE__); + sprintf(errbuf, "\"%s\" cannot be opened for READ/WRITE access, will retry %d times.", filename, (MAX_RETRIES - retryCount - 1)); + HEreport("%s\n", errbuf); + } + retryCount++; + } +#else + HDFfid = Hopen(filename, DFACC_RDWR, 0); +#endif + + /* If Hopen successful ... */ + /* ----------------------- */ + if (HDFfid != -1) + { + /* Get SDS interface ID */ + /* -------------------- */ + sdInterfaceID = SDstart(filename, DFACC_RDWR); + + /* If SDstart successful ... */ + /* ------------------------- */ + if (sdInterfaceID != -1) + { + /* Set HDFEOS version number in file */ + /* --------------------------------- */ + + attrIndex = SDfindattr(sdInterfaceID, "HDFEOSVersion"); + if (attrIndex == -1) + { + sprintf(hdfeosVersion, "%s%s", "HDFEOS_V", + HDFEOSVERSION1); + SDsetattr(sdInterfaceID, "HDFEOSVersion", DFNT_CHAR8, + strlen(hdfeosVersion), hdfeosVersion); + } + /* Set open access to write */ + /* ------------------------ */ + acs = 1; + + /* Get structural metadata attribute ID */ + /* ------------------------------------ */ + attrIndex = SDfindattr(sdInterfaceID, "StructMetadata.0"); + + /* Write structural metadata if it doesn't exist */ + /* --------------------------------------------- */ + if (attrIndex == -1) + { + metabuf = (char *) calloc(32000, 1); + if(metabuf == NULL) + { + HEpush(DFE_NOSPACE,"EHopen", __FILE__, __LINE__); + return(-1); + } + + strcpy(metabuf, "GROUP=SwathStructure\n"); + strcat(metabuf, "END_GROUP=SwathStructure\n"); + strcat(metabuf, "GROUP=GridStructure\n"); + strcat(metabuf, "END_GROUP=GridStructure\n"); + strcat(metabuf, "GROUP=PointStructure\n"); + strcat(metabuf, "END_GROUP=PointStructure\n"); + strcat(metabuf, "END\n"); + + SDsetattr(sdInterfaceID, "StructMetadata.0", + DFNT_CHAR8, 32000, metabuf); + free(metabuf); + } + } else + { + /* If error in SDstart then report */ + /* ------------------------------- */ + fid = -1; + status = -1; + HEpush(DFE_FNF, "EHopen", __FILE__, __LINE__); + sprintf(errbuf, "%s%s%s", "\"", filename, + "\" cannot be opened for read/write access."); + HEreport("%s\n", errbuf); + } + } else + { + /* If error in Hopen then report */ + /* ----------------------------- */ + fid = -1; + status = -1; + HEpush(DFE_FNF, "EHopen", __FILE__, __LINE__); + sprintf(errbuf, "%s%s%s", "\"", filename, + "\" cannot be opened for RDWR access."); + HEreport("%s\n", errbuf); + } + + break; + + + /* Open existing HDF-EOS file for read-only access */ + /* ----------------------------------------------- */ + case DFACC_READ: + + /* Get HDF file ID */ + /* --------------- */ +#ifndef _PGS_OLDNFS +/* The following loop around the function Hopen is intended to deal with the NFS cache + problem when opening file fails with errno = 150 or 151. When NFS cache is updated, + this part of change is no longer neccessary. 10/18/1999 */ + retryCount = 0; + HDFfid = -1; + while ((HDFfid == -1) && (retryCount < MAX_RETRIES)) + { + HDFfid = Hopen(filename, DFACC_READ, 0); + if((HDFfid == -1) && (errno == 150 || errno == 151)) + { + HEpush(DFE_FNF, "EHopen", __FILE__, __LINE__); + sprintf(errbuf, "\"%s\" cannot be opened for READONLY access, will retry %d times.", filename, (MAX_RETRIES - retryCount - 1)); + HEreport("%s\n", errbuf); + } + retryCount++; + } +#else + HDFfid = Hopen(filename, DFACC_READ, 0); +#endif + + /* If file does not exist report error */ + /* ----------------------------------- */ + if (HDFfid == -1) + { + fid = -1; + status = -1; + HEpush(DFE_FNF, "EHopen", __FILE__, __LINE__); + strcpy(errbuf, "\""); + strcat(errbuf, filename); + strcat(errbuf, "\" (opened for READONLY access)"); + strcat(errbuf, " does not exist."); + HEreport("%s\n", errbuf); + } else + { + /* If file exists then get SD interface ID */ + /* --------------------------------------- */ + sdInterfaceID = SDstart(filename, DFACC_RDONLY); + + /* If SDstart successful ... */ + /* ------------------------- */ + if (sdInterfaceID != -1) + { + + /* Set open access to read-only */ + /* ---------------------------- */ + acs = 0; + } else + { + /* If error in SDstart then report */ + /* ------------------------------- */ + fid = -1; + status = -1; + HEpush(DFE_FNF, "EHopen", __FILE__, __LINE__); + sprintf(errbuf, "%s%s%s", "\"", filename, + "\" cannot be opened for read access."); + HEreport("%s\n", errbuf); + } + } + + break; + + default: + /* Invalid Access Code */ + /* ------------------- */ + fid = -1; + status = -1; + HEpush(DFE_BADACC, "EHopen", __FILE__, __LINE__); + HEreport("Access Code: %d (%s).\n", access, filename); + } + + } + } else + { + /* Too many files opened */ + /* --------------------- */ + status = -1; + fid = -1; + HEpush(DFE_TOOMANY, "EHopen", __FILE__, __LINE__); + HEreport("No more than %d files may be open simultaneously (%s).\n", + EHXmaxfilecount, filename); + } + + + + + if (status == 0) + { + /* Initialize Vgroup Access */ + /* ------------------------ */ + Vstart(HDFfid); + + + /* Assign HDFEOS fid # & Load HDF fid and sdInterfaceID tables */ + /* ----------------------------------------------------------- */ + for (i = 0; i < EHXmaxfilecount; i++) + { + if (EHXtypeTable[i] == 0) + { + fid = i + EHIDOFFSET; + EHXacsTable[i] = acs; + EHXtypeTable[i] = 1; + EHXfidTable[i] = HDFfid; + EHXsdTable[i] = sdInterfaceID; + break; + } + } + + } + return (fid); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHchkfid | +| | +| DESCRIPTION: Checks for valid file id and returns HDF file ID and | +| SD interface ID | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| fid int32 HDF-EOS file ID | +| name char Structure name | +| | +| OUTPUTS: | +| HDFfid int32 HDF File ID | +| sdInterfaceID int32 SDS interface ID | +| access uint8 access code | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Jul 96 Joel Gales set status=-1 if failure | +| Jul 96 Joel Gales Add file id offset EHIDOFFSET | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHchkfid(int32 fid, char *name, int32 * HDFfid, int32 * sdInterfaceID, + uint8 * access) + +{ + intn status = 0; /* routine return status variable */ + intn fid0; /* HDFEOS file ID - Offset */ + + + /* Check for valid HDFEOS file ID range */ + /* ------------------------------------ */ + if (fid < EHIDOFFSET || fid > EHXmaxfilecount + EHIDOFFSET) + { + status = -1; + HEpush(DFE_RANGE, "EHchkfid", __FILE__, __LINE__); + HEreport("Invalid file id: %d. ID must be >= %d and < %d (%s).\n", + fid, EHIDOFFSET, EHXmaxfilecount + EHIDOFFSET, name); + } else + { + /* Compute "reduced" file ID */ + /* ------------------------- */ + fid0 = fid % EHIDOFFSET; + + + /* Check that HDFEOS file ID is active */ + /* ----------------------------------- */ + if (EHXtypeTable[fid0] == 0) + { + status = -1; + HEpush(DFE_GENAPP, "EHchkfid", __FILE__, __LINE__); + HEreport("File id %d not active (%s).\n", fid, name); + } else + { + /* + * Get HDF file ID, SD interface ID and file access from external + * arrays + */ + *HDFfid = EHXfidTable[fid0]; + *sdInterfaceID = EHXsdTable[fid0]; + *access = EHXacsTable[fid0]; + } + } + + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHidinfo | +| | +| DESCRIPTION: Gets Hopen and SD intereface IDs from HDF-EOS id | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| fid int32 HDF-EOS file ID | +| | +| OUTPUTS: | +| HDFfid int32 HDF File ID | +| sdInterfaceID int32 SDS interface ID | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jul 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHidinfo(int32 fid, int32 * HDFfid, int32 * sdInterfaceID) + +{ + intn status = 0; /* routine return status variable */ + uint8 dum; /* Dummy variable */ + + /* Call EHchkfid to get HDF and SD interface IDs */ + /* --------------------------------------------- */ + status = EHchkfid(fid, "EHidinfo", HDFfid, sdInterfaceID, &dum); + + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHfilename | +| | +| DESCRIPTION: Returns HDF filename | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| fid int32 HDF-EOS file id | +| | +| OUTPUTS: | +| filename char HDF-EOS file name | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Sep 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHfilename(int32 fid, char *filename) +{ + intn status = 0; /* routine return status variable */ + intn dum; /* Dummy variable */ + + char *fname; /* Pointer to filename */ + + /* Get point to filename from Hfidinquire */ + /* -------------------------------------- */ + Hfidinquire(EHXfidTable[fid % EHIDOFFSET], &fname, &dum, &dum); + strcpy(filename, fname); + + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHgetversion | +| | +| DESCRIPTION: Returns HDF-EOS version string | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| fid int32 HDF-EOS file id | +| | +| OUTPUTS: | +| version char HDF-EOS version string | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Mar 97 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHgetversion(int32 fid, char *version) +{ + intn status = 0; /* routine return status variable */ + + uint8 access; /* Access code */ + int32 dum; /* Dummy variable */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 attrIndex; /* HDFEOS version attribute index */ + int32 count; /* Version string size */ + + char attrname[16]; /* Attribute name */ + + + /* Get SDS interface ID */ + /* -------------------- */ + status = EHchkfid(fid, "EHgetversion", &dum, &sdInterfaceID, &access); + + + /* Get attribute index number */ + /* -------------------------- */ + attrIndex = SDfindattr(sdInterfaceID, "HDFEOSVersion"); + + /* No such attribute */ + /* ----------------- */ + if (attrIndex < 0) + return (-1); + + /* Get attribute size */ + /* ------------------ */ + status = SDattrinfo(sdInterfaceID, attrIndex, attrname, &dum, &count); + + /* Check return status */ + /* ------------------- */ + if (status < 0) + return (-1); + + /* Read version attribute */ + /* ---------------------- */ + status = SDreadattr(sdInterfaceID, attrIndex, (VOIDP) version); + + + /* Place string terminator on version string */ + /* ----------------------------------------- */ + version[count] = 0; + + + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHconvAng | +| | +| DESCRIPTION: Angle conversion Utility | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| outAngle float64 Output Angle value | +| | +| INPUTS: | +| inAngle float64 Input Angle value | +| code intn Conversion code | +! HDFE_RAD_DEG (0) | +| HDFE_DEG_RAD (1) | +| HDFE_DMS_DEG (2) | +| HDFE_DEG_DMS (3) | +| HDFE_RAD_DMS (4) | +| HDFE_DMS_RAD (5) | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Feb 97 Joel Gales Correct "60" min & "60" sec in _DMS conversion | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +float64 +EHconvAng(float64 inAngle, intn code) +{ +#define RADIANS_TO_DEGREES 180. / 3.14159265358979324 +#define DEGREES_TO_RADIANS 3.14159265358979324 / 180. + + int32 min; /* Truncated Minutes */ + int32 deg; /* Truncated Degrees */ + + float64 sec; /* Seconds */ + float64 outAngle = 0.0; /* Angle in desired units */ + + switch (code) + { + + /* Convert radians to degrees */ + /* -------------------------- */ + case HDFE_RAD_DEG: + outAngle = inAngle * RADIANS_TO_DEGREES; + break; + + + /* Convert degrees to radians */ + /* -------------------------- */ + case HDFE_DEG_RAD: + outAngle = inAngle * DEGREES_TO_RADIANS; + break; + + + /* Convert packed degrees to degrees */ + /* --------------------------------- */ + case HDFE_DMS_DEG: + deg = inAngle / 1000000; + min = (inAngle - deg * 1000000) / 1000; + sec = (inAngle - deg * 1000000 - min * 1000); + outAngle = deg + min / 60.0 + sec / 3600.0; + break; + + + /* Convert degrees to packed degrees */ + /* --------------------------------- */ + case HDFE_DEG_DMS: + deg = inAngle; + min = (inAngle - deg) * 60; + sec = (inAngle - deg - min / 60.0) * 3600; + + if ((intn) sec == 60) + { + sec = sec - 60; + min = min + 1; + } + if (min == 60) + { + min = min - 60; + deg = deg + 1; + } + outAngle = deg * 1000000 + min * 1000 + sec; + break; + + + /* Convert radians to packed degrees */ + /* --------------------------------- */ + case HDFE_RAD_DMS: + inAngle = inAngle * RADIANS_TO_DEGREES; + deg = inAngle; + min = (inAngle - deg) * 60; + sec = (inAngle - deg - min / 60.0) * 3600; + + if ((intn) sec == 60) + { + sec = sec - 60; + min = min + 1; + } + if (min == 60) + { + min = min - 60; + deg = deg + 1; + } + outAngle = deg * 1000000 + min * 1000 + sec; + break; + + + /* Convert packed degrees to radians */ + /* --------------------------------- */ + case HDFE_DMS_RAD: + deg = inAngle / 1000000; + min = (inAngle - deg * 1000000) / 1000; + sec = (inAngle - deg * 1000000 - min * 1000); + outAngle = deg + min / 60.0 + sec / 3600.0; + outAngle = outAngle * DEGREES_TO_RADIANS; + break; + } + return (outAngle); +} + +#undef TO_DEGREES +#undef TO_RADIANS + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHparsestr | +| | +| DESCRIPTION: String Parser Utility | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| count int32 Number of string entries | +| | +| INPUTS: | +| instring const char Input string | +| delim const char string delimitor | +| | +| OUTPUTS: | +| pntr char * Pointer array to beginning of each | +| string entry | +| len int32 Array of string entry lengths | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales NULL pointer array returns count only | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +EHparsestr(const char *instring, const char delim, char *pntr[], int32 len[]) +{ + int32 i; /* Loop index */ + int32 prevDelimPos = 0; /* Previous delimitor position */ + int32 count; /* Number of elements in string list */ + int32 slen; /* String length */ + + char *delimitor; /* Pointer to delimitor */ + + + /* Get length of input string list & Point to first delimitor */ + /* ---------------------------------------------------------- */ + slen = strlen(instring); + delimitor = strchr(instring, delim); + + /* If NULL string set count to zero otherwise set to 1 */ + /* --------------------------------------------------- */ + count = (slen == 0) ? 0 : 1; + + + /* if string pointers are requested set first one to beginning of string */ + /* --------------------------------------------------------------------- */ + if (&pntr[0] != NULL) + { + pntr[0] = (char *)instring; + } + /* If delimitor not found ... */ + /* -------------------------- */ + if (delimitor == NULL) + { + /* if string length requested then set to input string length */ + /* ---------------------------------------------------------- */ + if (len != NULL) + { + len[0] = slen; + } + } else + /* Delimitors Found */ + /* ---------------- */ + { + /* Loop through all characters in string */ + /* ------------------------------------- */ + for (i = 1; i < slen; i++) + { + /* If character is a delimitor ... */ + /* ------------------------------- */ + if (instring[i] == delim) + { + + /* If string pointer requested */ + /* --------------------------- */ + if (&pntr[0] != NULL) + { + /* if requested then compute string length of entry */ + /* ------------------------------------------------ */ + if (len != NULL) + { + len[count - 1] = i - prevDelimPos; + } + /* Point to beginning of string entry */ + /* ---------------------------------- */ + pntr[count] = (char *)instring + i + 1; + } + /* Reset previous delimitor position and increment counter */ + /* ------------------------------------------------------- */ + prevDelimPos = i + 1; + count++; + } + } + + /* Compute string length of last entry */ + /* ----------------------------------- */ + if (&pntr[0] != NULL && len != NULL) + { + len[count - 1] = i - prevDelimPos; + } + } + + return (count); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHstrwithin | +| | +| DESCRIPTION: Searchs for string within target string | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| indx int32 Element index (0 - based) | +| | +| INPUTS: | +| target const char Target string | +| search const char Search string | +| delim const char Delimitor | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Jan 97 Joel Gales Change ptr & slen to dynamic arrays | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +EHstrwithin(const char *target, const char *search, const char delim) +{ + intn found = 0; /* Target string found flag */ + + int32 indx; /* Loop index */ + int32 nentries; /* Number of entries in search string */ + int32 *slen; /* Pointer to string length array */ + + char **ptr; /* Pointer to string pointer array */ + char buffer[128];/* Buffer to hold "test" string entry */ + + + /* Count number of entries in search string list */ + /* --------------------------------------------- */ + nentries = EHparsestr(search, delim, NULL, NULL); + + + /* Allocate string pointer and length arrays */ + /* ----------------------------------------- */ + ptr = (char **) calloc(nentries, sizeof(char *)); + if(ptr == NULL) + { + HEpush(DFE_NOSPACE,"EHstrwithin", __FILE__, __LINE__); + return(-1); + } + slen = (int32 *) calloc(nentries, sizeof(int32)); + if(slen == NULL) + { + HEpush(DFE_NOSPACE,"EHstrwithin", __FILE__, __LINE__); + free(ptr); + return(-1); + } + + + /* Parse search string */ + /* ------------------- */ + nentries = EHparsestr(search, delim, ptr, slen); + + + /* Loop through all elements in search string list */ + /* ----------------------------------------------- */ + for (indx = 0; indx < nentries; indx++) + { + /* Copy string entry into buffer */ + /* ----------------------------- */ + memcpy(buffer, ptr[indx], slen[indx]); + buffer[slen[indx]] = 0; + + + /* Compare target string with string entry */ + /* --------------------------------------- */ + if (strcmp(target, buffer) == 0) + { + found = 1; + break; + } + } + + /* If not found set return to -1 */ + /* ----------------------------- */ + if (found == 0) + { + indx = -1; + } + free(slen); + free(ptr); + + return (indx); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHloadliststr | +| | +| DESCRIPTION: Builds list string from string array | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| ptr char String pointer array | +| nentries int32 Number of string array elements | +| delim char Delimitor | +| | +| OUTPUTS: | +| liststr char Output list string | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHloadliststr(char *ptr[], int32 nentries, char *liststr, char delim) +{ + intn status = 0; /* routine return status variable */ + + int32 i; /* Loop index */ + int32 slen; /* String entry length */ + int32 off = 0; /* Position of next entry along string list */ + char dstr[2]; /* string version of input variable "delim" */ + + dstr[0] = delim; + dstr[1] = '\0'; + + + /* Loop through all entries in string array */ + /* ---------------------------------------- */ + for (i = 0; i < nentries; i++) + { + /* Get string length of string array entry */ + /* --------------------------------------- */ + slen = strlen(ptr[i]); + + + /* Copy string entry to string list */ + /* -------------------------------- */ + memcpy(liststr + off, ptr[i], slen + 1); + + + /* Concatenate with delimitor */ + /* -------------------------- */ + if (i != nentries - 1) + { + strcat(liststr, dstr); + } + /* Get position of next entry for string list */ + /* ------------------------------------------ */ + off += slen + 1; + } + + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHgetid | +| | +| DESCRIPTION: Get Vgroup/Vdata ID from name | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| outID int32 Output ID | +| | +| INPUTS: | +| fid int32 HDF-EOS file ID | +| vgid int32 Vgroup ID | +| objectname const char object name | +| code intn object code (0 - Vgroup, 1 - Vdata) | +| access const char access ("w/r") | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +EHgetid(int32 fid, int32 vgid, const char *objectname, intn code, + const char *access) +{ + intn i; /* Loop index */ + + int32 nObjects; /* # of objects in Vgroup */ + int32 *tags; /* Pnt to Vgroup object tags array */ + int32 *refs; /* Pnt to Vgroup object refs array */ + int32 id; /* Object ID */ + int32 outID = -1; /* Desired object ID */ + + char name[128]; /* Object name */ + + + /* Get Number of objects */ + /* --------------------- */ + nObjects = Vntagrefs(vgid); + + /* If objects exist ... */ + /* -------------------- */ + if (nObjects != 0) + { + + /* Get tags and references of objects */ + /* ---------------------------------- */ + tags = (int32 *) malloc(sizeof(int32) * nObjects); + if(tags == NULL) + { + HEpush(DFE_NOSPACE,"EHgetid", __FILE__, __LINE__); + return(-1); + } + refs = (int32 *) malloc(sizeof(int32) * nObjects); + if(refs == NULL) + { + HEpush(DFE_NOSPACE,"EHgetid", __FILE__, __LINE__); + free(tags); + return(-1); + } + + Vgettagrefs(vgid, tags, refs, nObjects); + + + /* Vgroup ID Section */ + /* ----------------- */ + if (code == 0) + { + /* Loop through objects */ + /* -------------------- */ + for (i = 0; i < nObjects; i++) + { + + /* If object is Vgroup ... */ + /* ----------------------- */ + if (*(tags + i) == DFTAG_VG) + { + + /* Get ID and name */ + /* --------------- */ + id = Vattach(fid, *(refs + i), access); + Vgetname(id, name); + + /* If name equals desired object name get ID */ + /* ----------------------------------------- */ + if (strcmp(name, objectname) == 0) + { + outID = id; + break; + } + /* If not desired object then detach */ + /* --------------------------------- */ + Vdetach(id); + } + } + } else if (code == 1) + { + + /* Loop through objects */ + /* -------------------- */ + for (i = 0; i < nObjects; i++) + { + + /* If object is Vdata ... */ + /* ---------------------- */ + if (*(tags + i) == DFTAG_VH) + { + + /* Get ID and name */ + /* --------------- */ + id = VSattach(fid, *(refs + i), access); + VSgetname(id, name); + + /* If name equals desired object name get ID */ + /* ----------------------------------------- */ + if (EHstrwithin(objectname, name, ',') != -1) + { + outID = id; + break; + } + /* If not desired object then detach */ + /* --------------------------------- */ + VSdetach(id); + } + } + } + free(tags); + free(refs); + } + return (outID); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHrevflds | +| | +| DESCRIPTION: Reverses elements in a string list | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| dimlist char Original dimension list | +| | +| OUTPUTS: | +| revdimlist char Reversed dimension list | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHrevflds(char *dimlist, char *revdimlist) +{ + intn status = 0; /* routine return status variable */ + + int32 indx; /* Loop index */ + int32 nentries; /* Number of entries in search string */ + int32 *slen; /* Pointer to string length array */ + + char **ptr; /* Pointer to string pointer array */ + char *tempPtr; /* Temporary string pointer */ + char *tempdimlist;/* Temporary dimension list */ + + + /* Copy dimlist into temp dimlist */ + /* ------------------------------ */ + tempdimlist = (char *) malloc(strlen(dimlist) + 1); + if(tempdimlist == NULL) + { + HEpush(DFE_NOSPACE,"EHrevflds", __FILE__, __LINE__); + return(-1); + } + strcpy(tempdimlist, dimlist); + + + /* Count number of entries in search string list */ + /* --------------------------------------------- */ + nentries = EHparsestr(tempdimlist, ',', NULL, NULL); + + + /* Allocate string pointer and length arrays */ + /* ----------------------------------------- */ + ptr = (char **) calloc(nentries, sizeof(char *)); + if(ptr == NULL) + { + HEpush(DFE_NOSPACE,"EHrevflds", __FILE__, __LINE__); + free(tempdimlist); + return(-1); + } + slen = (int32 *) calloc(nentries, sizeof(int32)); + if(slen == NULL) + { + HEpush(DFE_NOSPACE,"EHrevflds", __FILE__, __LINE__); + free(ptr); + free(tempdimlist); + return(-1); + } + + + /* Parse search string */ + /* ------------------- */ + nentries = EHparsestr(tempdimlist, ',', ptr, slen); + + + /* Reverse entries in string pointer array */ + /* --------------------------------------- */ + for (indx = 0; indx < nentries / 2; indx++) + { + tempPtr = ptr[indx]; + ptr[indx] = ptr[nentries - 1 - indx]; + ptr[nentries - 1 - indx] = tempPtr; + } + + + /* Replace comma delimitors by nulls */ + /* --------------------------------- */ + for (indx = 0; indx < nentries - 1; indx++) + { + *(ptr[indx] - 1) = 0; + } + + + /* Build new string list */ + /* --------------------- */ + status = EHloadliststr(ptr, nentries, revdimlist, ','); + + + free(slen); + free(ptr); + free(tempdimlist); + + return (status); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHcntOBJECT | +| | +| DESCRIPTION: Determines number of OBJECTs in metadata GROUP | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| count int32 Number of OBJECTs in GROUP | +| | +| INPUTS: | +| metabur char Begin & end metadata pointer array | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Sep 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +EHcntOBJECT(char *metabuf[]) +{ + int32 count = 0; /* Counter */ + + char *metaptr; /* Beginning of metadata section */ + char *endptr; /* End of metadata section */ + char *tempptr; /* Pointer within metadata section */ + + + /* Get Pointers to beginning and ending of metadata section */ + /* -------------------------------------------------------- */ + metaptr = metabuf[0]; + endptr = metabuf[1]; + + + /* Find number of "END_OBJECT" strings within section */ + /* -------------------------------------------------- */ + tempptr = metaptr; + while (tempptr < endptr && tempptr != NULL) + { + tempptr = strstr(tempptr + 1, "END_OBJECT"); + count++; + } + count--; + + return (count); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHcntGROUP | +| | +| DESCRIPTION: Determines number of GROUPs in metadata GROUP | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| count int32 Number of GROUPs in GROUP | +| | +| INPUTS: | +| metabur char Begin & end metadata pointer array | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Sep 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +EHcntGROUP(char *metabuf[]) +{ + int32 count = 0; /* Counter */ + + char *metaptr; /* Beginning of metadata section */ + char *endptr; /* End of metadata section */ + char *tempptr; /* Pointer within metadata section */ + + + /* Get Pointers to beginning and ending of metadata section */ + /* -------------------------------------------------------- */ + metaptr = metabuf[0]; + endptr = metabuf[1]; + + + /* Find number of "END_GROUP" strings within section */ + /* ------------------------------------------------- */ + tempptr = metaptr; + while (tempptr < endptr && tempptr != NULL) + { + tempptr = strstr(tempptr + 1, "END_GROUP"); + count++; + } + count--; + + return (count); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHmetalist | +| | +| DESCRIPTION: Converts string list to metadata list | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| instring char Input string list | +| | +| OUTPUTS: | +| outstring char Output metadata string | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHmetalist(char *instring, char *outstring) +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 nentries; /* Number of entries in search string */ + int32 listlen = 1;/* String list length */ + int32 *slen; /* Pointer to string length array */ + + char **ptr; /* Pointer to string pointer array */ + + + /* Count number of entries in search string list */ + /* --------------------------------------------- */ + nentries = EHparsestr(instring, ',', NULL, NULL); + + + /* Allocate string pointer and length arrays */ + /* ----------------------------------------- */ + ptr = (char **) calloc(nentries, sizeof(char *)); + if(ptr == NULL) + { + HEpush(DFE_NOSPACE,"EHmetalist", __FILE__, __LINE__); + return(-1); + } + slen = (int32 *) calloc(nentries, sizeof(int32)); + if(slen == NULL) + { + HEpush(DFE_NOSPACE,"EHmetalist", __FILE__, __LINE__); + free(ptr); + return(-1); + } + + + /* Parse input string */ + /* ------------------ */ + nentries = EHparsestr(instring, ',', ptr, slen); + + + /* Start output string with leading "(" */ + /* ------------------------------------ */ + strcpy(outstring, "("); + + + /* Loop through all entries */ + /* ------------------------ */ + for (i = 0; i < nentries; i++) + { + /* Add double quote (") to output string */ + /* ------------------------------------- */ + strcat(outstring, "\""); + listlen++; + + /* Add input string entry to output string */ + /* --------------------------------------- */ + memcpy(outstring + listlen, ptr[i], slen[i]); + listlen += slen[i]; + outstring[listlen] = 0; + + + /* Add closing double quote (") to output string */ + /* --------------------------------------------- */ + strcat(outstring, "\""); + listlen++; + outstring[listlen] = 0; + + + /* Add comma delimitor to output string */ + /* ------------------------------------ */ + if (i != (nentries - 1)) + { + strcat(outstring, ","); + listlen++; + } + /* Place null terminator in output string */ + /* -------------------------------------- */ + outstring[listlen] = 0; + } + + + /* End output string with trailing ")" */ + /* ----------------------------------- */ + strcat(outstring, ")"); + + free(ptr); + free(slen); + + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHinsertmeta | +| | +| DESCRIPTION: Writes metadata | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| sdInterfaceID int32 SDS interface ID | +| structname char HDF-EOS structure name | +| structcode char Structure code ("s/g/p") | +| metacode int32 Metadata code type | +| metastr char Metadata input string | +| metadata int32 Metadata utility array | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Sep 96 Joel Gales Allow new metadata object to be written in | +| old metadata. | +| Dec 96 Joel Gales Fix Point metadata problem | +| Oct 98 David Wynne Change utlstr/utlstr2 to dynamic allocation from | +| static | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHinsertmeta(int32 sdInterfaceID, char *structname, char *structcode, + int32 metacode, char *metastr, int32 metadata[]) +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 attrIndex; /* Structural metadata attribute index */ + int32 slen[8]; /* String length array (for dim map parsing) */ + int32 nmeta; /* Number of 32000 byte metadata sections */ + int32 metalen; /* Length of structural metadata */ + int32 seglen; /* Length of metadata string to insert */ + int32 count; /* Objects/Groups counter */ + int32 offset; /* Offset insertion position of new metadata + * section within existing metadata */ + + char *metabuf; /* Pointer (handle) to structural metadata */ + char *begptr; /* Pointer to beginning of metadata section */ + char *metaptr; /* Metadata pointer */ + char *prevmetaptr;/* Previous position of metadata pointer */ + char *ptr[8]; /* String pointer array (for dim map parsing) */ + char type[32]; /* Number type descriptor string */ + char *metaArr[2]; /* Array of metadata positions */ + char *colon; /* Colon position */ + char *colon2; /* 2nd colon position */ + char *slash; /* Slash postion */ + char *utlstr; /* Utility string */ + char *utlstr2; /* Utility string 2 */ + + + /* Allocate space for utility strings */ + /* ---------------------------------- */ + utlstr = (char *) calloc(UTLSTRSIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"EHinsertmeta", __FILE__, __LINE__); + return(-1); + } + + utlstr2 = (char *) calloc(UTLSTRSIZE, sizeof(char)); + if(utlstr2 == NULL) + { + HEpush(DFE_NOSPACE,"EHinsertmeta", __FILE__, __LINE__); + free(utlstr); + return(-1); + } + + /* Determine number of structural metadata "sections" */ + /* -------------------------------------------------- */ + nmeta = 0; + while (1) + { + /* Search for "StructMetadata.x" attribute */ + /* --------------------------------------- */ + sprintf(utlstr, "%s%d", "StructMetadata.", (int)nmeta); + attrIndex = SDfindattr(sdInterfaceID, utlstr); + + + /* If found then increment metadata section counter else exit loop */ + /* --------------------------------------------------------------- */ + if (attrIndex != -1) + { + nmeta++; + } else + { + break; + } + } + + + /* Allocate space for metadata (in units of 32000 bytes) */ + /* ----------------------------------------------------- */ + metabuf = (char *) calloc(32000 * nmeta, 1); + if(metabuf == NULL) + { + HEpush(DFE_NOSPACE,"EHinsertmeta", __FILE__, __LINE__); + free(utlstr); + free(utlstr2); + return(-1); + } + + + /* Read structural metadata */ + /* ------------------------ */ + for (i = 0; i < nmeta; i++) + { + sprintf(utlstr, "%s%d", "StructMetadata.", i); + attrIndex = SDfindattr(sdInterfaceID, utlstr); + metalen = strlen(metabuf); + SDreadattr(sdInterfaceID, attrIndex, metabuf + metalen); + } + + /* Determine length (# of characters) of metadata */ + /* ---------------------------------------------- */ + metalen = strlen(metabuf); + + + + /* Find HDF-EOS structure "root" group in metadata */ + /* ----------------------------------------------- */ + + /* Setup proper search string */ + /* -------------------------- */ + if (strcmp(structcode, "s") == 0) + { + strcpy(utlstr, "GROUP=SwathStructure"); + } else if (strcmp(structcode, "g") == 0) + { + strcpy(utlstr, "GROUP=GridStructure"); + } else if (strcmp(structcode, "p") == 0) + { + strcpy(utlstr, "GROUP=PointStructure"); + } + /* Use string search routine (strstr) to move through metadata */ + /* ----------------------------------------------------------- */ + metaptr = strstr(metabuf, utlstr); + + + + /* Find specific (named) structure */ + /* ------------------------------- */ + if (metacode < 1000) + { + /* Save current metadata pointer */ + /* ----------------------------- */ + prevmetaptr = metaptr; + + + /* First loop for "old-style" (non-ODL) metadata string */ + /* ---------------------------------------------------- */ + if (strcmp(structcode, "s") == 0) + { + sprintf(utlstr, "%s%s", "SwathName=\"", structname); + } else if (strcmp(structcode, "g") == 0) + { + sprintf(utlstr, "%s%s", "GridName=\"", structname); + } else if (strcmp(structcode, "p") == 0) + { + sprintf(utlstr, "%s%s", "PointName=\"", structname); + } + /* Do string search */ + /* ---------------- */ + metaptr = strstr(metaptr, utlstr); + + + /* + * If not found then return to previous position in metadata and look + * for "new-style" (ODL) metadata string + */ + if (metaptr == NULL) + { + sprintf(utlstr, "%s%s", "GROUP=\"", structname); + metaptr = strstr(prevmetaptr, utlstr); + } + } + /* + * If searching for geo fields (3), data fields (4), or point fields (11) + * convert type code to string designator. + */ + if (metacode == 3 || metacode == 4 || metacode == 11) + { + switch (metadata[0]) + { + case 3: + strcpy(type, "DFNT_UCHAR8"); + break; + case 4: + strcpy(type, "DFNT_CHAR8"); + break; + case 5: + strcpy(type, "DFNT_FLOAT32"); + break; + case 6: + strcpy(type, "DFNT_FLOAT64"); + break; + case 20: + strcpy(type, "DFNT_INT8"); + break; + case 21: + strcpy(type, "DFNT_UINT8"); + break; + case 22: + strcpy(type, "DFNT_INT16"); + break; + case 23: + strcpy(type, "DFNT_UINT16"); + break; + case 24: + strcpy(type, "DFNT_INT32"); + break; + case 25: + strcpy(type, "DFNT_UINT32"); + break; + } + } + /* Metadata Section Switch */ + /* ----------------------- */ + switch (abs(metacode)) + { + + case 0: + /* Dimension Section */ + /* ----------------- */ + + /* Find beginning and ending of metadata section */ + /* --------------------------------------------- */ + strcpy(utlstr, "\t\tGROUP=Dimension"); + begptr = strstr(metaptr, utlstr); + + strcpy(utlstr, "\t\tEND_GROUP=Dimension"); + metaptr = strstr(metaptr, utlstr); + + + /* Count number of existing entries and increment */ + /* ---------------------------------------------- */ + metaArr[0] = begptr; + metaArr[1] = metaptr; + count = EHcntOBJECT(metaArr) + 1; + + + /* Build metadata entry string */ + /* --------------------------- */ + sprintf(utlstr, "%s%d%s%s%s%d%s%d%s", + "\t\t\tOBJECT=Dimension_", (int)count, + "\n\t\t\t\tDimensionName=\"", &metastr[0], + "\"\n\t\t\t\tSize=", (int)metadata[0], + "\n\t\t\tEND_OBJECT=Dimension_", (int)count, "\n"); + break; + + + case 1: + /* Dimension Map Section */ + /* --------------------- */ + + /* Find beginning and ending of metadata section */ + /* --------------------------------------------- */ + strcpy(utlstr, "\t\tGROUP=DimensionMap"); + begptr = strstr(metaptr, utlstr); + + strcpy(utlstr, "\t\tEND_GROUP=DimensionMap"); + metaptr = strstr(metaptr, utlstr); + + + /* Count number of existing entries and increment */ + /* ---------------------------------------------- */ + metaArr[0] = begptr; + metaArr[1] = metaptr; + count = EHcntOBJECT(metaArr) + 1; + + + /* Find slash within input mapping string and replace with NULL */ + /* ------------------------------------------------------------ */ + EHparsestr(metastr, '/', ptr, slen); + metastr[slen[0]] = 0; + + + /* Build metadata entry string */ + /* --------------------------- */ + sprintf(utlstr, "%s%d%s%s%s%s%s%d%s%d%s%d%s", + "\t\t\tOBJECT=DimensionMap_", (int)count, + "\n\t\t\t\tGeoDimension=\"", &metastr[0], + "\"\n\t\t\t\tDataDimension=\"", &metastr[slen[0] + 1], + "\"\n\t\t\t\tOffset=", (int)metadata[0], + "\n\t\t\t\tIncrement=", (int)metadata[1], + "\n\t\t\tEND_OBJECT=DimensionMap_", (int)count, "\n"); + break; + + + case 2: + /* Index Dimension Map Section */ + /* --------------------------- */ + + /* Find beginning and ending of metadata section */ + /* --------------------------------------------- */ + strcpy(utlstr, "\t\tGROUP=IndexDimensionMap"); + begptr = strstr(metaptr, utlstr); + + strcpy(utlstr, "\t\tEND_GROUP=IndexDimensionMap"); + metaptr = strstr(metaptr, utlstr); + + + /* Count number of existing entries and increment */ + /* ---------------------------------------------- */ + metaArr[0] = begptr; + metaArr[1] = metaptr; + count = EHcntOBJECT(metaArr) + 1; + + + /* Find slash within input mapping string and replace with NULL */ + /* ------------------------------------------------------------ */ + EHparsestr(metastr, '/', ptr, slen); + metastr[slen[0]] = 0; + + + /* Build metadata entry string */ + /* --------------------------- */ + sprintf(utlstr, "%s%d%s%s%s%s%s%d%s", + "\t\t\tOBJECT=IndexDimensionMap_", (int)count, + "\n\t\t\t\tGeoDimension=\"", &metastr[0], + "\"\n\t\t\t\tDataDimension=\"", &metastr[slen[0] + 1], + "\"\n\t\t\tEND_OBJECT=IndexDimensionMap_", (int)count, "\n"); + break; + + + case 3: + /* Geolocation Field Section */ + /* ------------------------- */ + + /* Find beginning and ending of metadata section */ + /* --------------------------------------------- */ + strcpy(utlstr, "\t\tGROUP=GeoField"); + begptr = strstr(metaptr, utlstr); + + strcpy(utlstr, "\t\tEND_GROUP=GeoField"); + metaptr = strstr(metaptr, utlstr); + + + /* Count number of existing entries and increment */ + /* ---------------------------------------------- */ + metaArr[0] = begptr; + metaArr[1] = metaptr; + count = EHcntOBJECT(metaArr) + 1; + + + /* Find colon (parse off field name) */ + /* --------------------------------- */ + colon = strchr(metastr, ':'); + *colon = 0; + + + /* Search for next colon (compression and/or tiling parameters) */ + /* ------------------------------------------------------------ */ + colon2 = strchr(colon + 1, ':'); + if (colon2 != NULL) + { + *colon2 = 0; + } + /* Make metadata string list for dimension list */ + /* -------------------------------------------- */ + EHmetalist(colon + 1, utlstr2); + + + /* Build metadata entry string */ + /* --------------------------- */ + sprintf(utlstr, "%s%d%s%s%s%s%s%s", + "\t\t\tOBJECT=GeoField_", (int)count, + "\n\t\t\t\tGeoFieldName=\"", metastr, + "\"\n\t\t\t\tDataType=", type, + "\n\t\t\t\tDimList=", utlstr2); + + + /* If compression and/or tiling parameters add to string */ + /* ----------------------------------------------------- */ + if (colon2 != NULL) + { + strcat(utlstr, colon2 + 1); + } + /* Add END_OBJECT terminator to metadata string */ + /* -------------------------------------------- */ + sprintf(utlstr2, "%s%d%s", + "\n\t\t\tEND_OBJECT=GeoField_", (int)count, "\n"); + strcat(utlstr, utlstr2); + + break; + + + case 4: + /* Data Field Section */ + /* ------------------ */ + + /* Find beginning and ending of metadata section */ + /* --------------------------------------------- */ + strcpy(utlstr, "\t\tGROUP=DataField"); + begptr = strstr(metaptr, utlstr); + + strcpy(utlstr, "\t\tEND_GROUP=DataField"); + metaptr = strstr(metaptr, utlstr); + + + /* Count number of existing entries and increment */ + /* ---------------------------------------------- */ + metaArr[0] = begptr; + metaArr[1] = metaptr; + count = EHcntOBJECT(metaArr) + 1; + + + /* Find colon (parse off field name) */ + /* --------------------------------- */ + colon = strchr(metastr, ':'); + *colon = 0; + + + /* Search for next colon (compression and/or tiling parameters) */ + /* ------------------------------------------------------------ */ + colon2 = strchr(colon + 1, ':'); + if (colon2 != NULL) + { + *colon2 = 0; + } + /* Make metadata string list from dimension list */ + /* --------------------------------------------- */ + EHmetalist(colon + 1, utlstr2); + + + /* Build metadata entry string */ + /* --------------------------- */ + sprintf(utlstr, "%s%d%s%s%s%s%s%s", + "\t\t\tOBJECT=DataField_", (int)count, + "\n\t\t\t\tDataFieldName=\"", metastr, + "\"\n\t\t\t\tDataType=", type, + "\n\t\t\t\tDimList=", utlstr2); + + + /* If compression and/or tiling parameters add to string */ + /* ----------------------------------------------------- */ + if (colon2 != NULL) + { + strcat(utlstr, colon2 + 1); + } + /* Add END_OBJECT terminator to metadata string */ + /* -------------------------------------------- */ + sprintf(utlstr2, "%s%d%s", + "\n\t\t\tEND_OBJECT=DataField_", (int)count, "\n"); + strcat(utlstr, utlstr2); + + break; + + + case 6: + /* Merged Field Section */ + /* -------------------- */ + + /* Find beginning and ending of metadata section */ + /* --------------------------------------------- */ + strcpy(utlstr, "\t\tGROUP=MergedFields"); + begptr = strstr(metaptr, utlstr); + + strcpy(utlstr, "\t\tEND_GROUP=MergedFields"); + metaptr = strstr(metaptr, utlstr); + + + /* Count number of existing entries and increment */ + /* ---------------------------------------------- */ + metaArr[0] = begptr; + metaArr[1] = metaptr; + count = EHcntOBJECT(metaArr) + 1; + + + /* Find colon (parse off merged fieldname) */ + /* --------------------------------------- */ + colon = strchr(metastr, ':'); + + + /* Make metadata string list from field list */ + /* ----------------------------------------- */ + EHmetalist(colon + 1, utlstr2); + *colon = 0; + + + /* Build metadata entry string */ + /* --------------------------- */ + sprintf(utlstr, "%s%d%s%s%s%s%s%s%d%s", + "\t\t\tOBJECT=MergedFields_", (int)count, + "\n\t\t\t\tMergedFieldName=\"", metastr, "\"", + "\n\t\t\t\tFieldList=", utlstr2, + "\n\t\t\tEND_OBJECT=MergedFields_", (int)count, "\n"); + break; + + + case 10: + /* Point Level Section */ + /* ------------------- */ + + /* Find beginning and ending of metadata section */ + /* --------------------------------------------- */ + strcpy(utlstr, "\t\tGROUP=Level"); + begptr = strstr(metaptr, utlstr); + + strcpy(utlstr, "\n\t\tEND_GROUP=Level"); + metaptr = strstr(metaptr, utlstr) + 1; + + + /* Count number of existing entries and increment */ + /* ---------------------------------------------- */ + metaArr[0] = begptr; + metaArr[1] = metaptr; + count = EHcntGROUP(metaArr); + + + /* Build metadata entry string */ + /* --------------------------- */ + sprintf(utlstr, "%s%d%s%s%s%d%s", + "\t\t\tGROUP=Level_", (int)count, + "\n\t\t\t\tLevelName=\"", metastr, + "\"\n\t\t\tEND_GROUP=Level_", (int)count, "\n"); + break; + + + case 11: + /* Point Field Section */ + /* ------------------- */ + + /* Find colon (parse off point field name) */ + /* --------------------------------------- */ + colon = strchr(metastr, ':'); + *colon = 0; + + + /* Find beginning and ending of metadata section */ + /* --------------------------------------------- */ + strcpy(utlstr, "\t\t\t\tLevelName=\""); + strcat(utlstr, colon + 1); + begptr = strstr(metaptr, utlstr); + + strcpy(utlstr, "\t\t\tEND_GROUP=Level_"); + metaptr = strstr(begptr, utlstr); + + + /* Count number of existing entries and increment */ + /* ---------------------------------------------- */ + metaArr[0] = begptr; + metaArr[1] = metaptr; + count = EHcntOBJECT(metaArr) + 1; + + + /* Build metadata entry string */ + /* --------------------------- */ + sprintf(utlstr, "%s%d%s%s%s%s%s%d%s%d%s", + "\t\t\t\tOBJECT=PointField_", (int)count, + "\n\t\t\t\t\tPointFieldName=\"", metastr, + "\"\n\t\t\t\t\tDataType=", type, + "\n\t\t\t\t\tOrder=", (int)metadata[1], + "\n\t\t\t\tEND_OBJECT=PointField_", (int)count, "\n"); + break; + + + + case 12: + /* Level Link Section */ + /* ------------------ */ + + /* Find beginning and ending of metadata section */ + /* --------------------------------------------- */ + strcpy(utlstr, "\t\tGROUP=LevelLink"); + begptr = strstr(metaptr, utlstr); + + strcpy(utlstr, "\t\tEND_GROUP=LevelLink"); + metaptr = strstr(metaptr, utlstr); + + + /* Count number of existing entries and increment */ + /* ---------------------------------------------- */ + metaArr[0] = begptr; + metaArr[1] = metaptr; + count = EHcntOBJECT(metaArr) + 1; + + + /* Find colon (parse off parent/child level names from link field) */ + /* --------------------------------------------------------------- */ + colon = strchr(metastr, ':'); + *colon = 0; + + + /* Find slash (divide parent and child levels) */ + /* ------------------------------------------- */ + slash = strchr(metastr, '/'); + *slash = 0; + + + /* Build metadata entry string */ + /* --------------------------- */ + sprintf(utlstr, "%s%d%s%s%s%s%s%s%s%d%s", + "\t\t\tOBJECT=LevelLink_", (int)count, + "\n\t\t\t\tParent=\"", metastr, + "\"\n\t\t\t\tChild=\"", slash + 1, + "\"\n\t\t\t\tLinkField=\"", colon + 1, + "\"\n\t\t\tEND_OBJECT=LevelLink_", (int)count, "\n"); + + break; + + + case 101: + /* Position metadata pointer for Grid proj parms, pix reg, origin */ + /* -------------------------------------------------------------- */ + strcpy(utlstr, "\t\tGROUP=Dimension"); + metaptr = strstr(metaptr, utlstr); + strcpy(utlstr, metastr); + + break; + + + case 1001: + /* Position metadata pointer for new swath structure (SWcreate) */ + /* ------------------------------------------------------------ */ + strcpy(utlstr, "END_GROUP=SwathStructure"); + metaptr = strstr(metaptr, utlstr); + strcpy(utlstr, metastr); + break; + + + case 1002: + /* Position metadata pointer for new grid structure (GDcreate) */ + /* ----------------------------------------------------------- */ + strcpy(utlstr, "END_GROUP=GridStructure"); + metaptr = strstr(metaptr, utlstr); + strcpy(utlstr, metastr); + break; + + + case 1003: + /* Position metadata pointer for new point structure (PTcreate) */ + /* ------------------------------------------------------------ */ + strcpy(utlstr, "END_GROUP=PointStructure"); + metaptr = strstr(metaptr, utlstr); + strcpy(utlstr, metastr); + break; + } + + + + /* Get length of metadata string to insert */ + /* --------------------------------------- */ + seglen = strlen(utlstr); + + /* Get offset of entry postion within existing metadata */ + /* ---------------------------------------------------- */ + offset = metaptr - metabuf; + + + /* If end of new metadata string outside of current metadata buffer ... */ + /* -------------------------------------------------------------------- */ + if (metalen + seglen > 32000 * nmeta - 1) + { + /* Reallocate metadata buffer with additional 32000 bytes */ + /* ------------------------------------------------------ */ + metabuf = (char *) realloc((void *) metabuf, 32000 * (nmeta + 1)); + if(metabuf == NULL) + { + HEpush(DFE_NOSPACE,"EHinsertmeta", __FILE__, __LINE__); + free(utlstr); + free(utlstr2); + return(-1); + } + + /* Increment metadata section counter */ + /* ---------------------------------- */ + nmeta++; + + /* Reposition metadata pointer (entry position) */ + /* -------------------------------------------- */ + metaptr = metabuf + offset; + } + /* Move metadata following entry point to its new position */ + /* ------------------------------------------------------- */ + for (i = metalen - 1; i > offset - 1; i--) + { + *(metabuf + seglen + i) = *(metabuf + i); + } + + /* Copy new metadat string (utlstr) into metadata */ + /* ---------------------------------------------- */ + memcpy(metaptr, utlstr, seglen); + + /* set to null character remaining of the metabuf */ + + memset((metabuf + metalen + seglen), '\0', (nmeta*32000 -1 - (metalen + + seglen))); + /* Add new null string terminator */ + /* ------------------------------ */ + metabuf[metalen + seglen] = 0; + + + /* Write Back to Global Attribute(s) */ + /* --------------------------------- */ + for (i = 0; i < nmeta; i++) + { + sprintf(utlstr, "%s%d", "StructMetadata.", i); + SDsetattr(sdInterfaceID, utlstr, DFNT_CHAR8, + 32000, metabuf + i * 32000); + } + + + + free(metabuf); + free(utlstr); + free(utlstr2); + + return (status); + +} + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHgetmetavalue | +| | +| DESCRIPTION: Returns metadata value | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| metaptrs char Begin and end of metadata section | +| parameter char parameter to access | +| | +| OUTPUTS: | +| metaptr char Ptr to (updated) beginning of metadata | +| retstr char return string containing value | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Jan 97 Joel Gales Check string pointer against end of meta section | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHgetmetavalue(char *metaptrs[], char *parameter, char *retstr) +{ + intn status = 0; /* routine return status variable */ + + int32 slen; /* String length */ + char *newline; /* Position of new line character */ + char *sptr; /* string pointer within metadata */ + + + /* Get string length of parameter string + 1 */ + /* ----------------------------------------- */ + slen = strlen(parameter) + 1; + + + /* Build search string (parameter string + "=") */ + /* -------------------------------------------- */ + strcpy(retstr, parameter); + strcat(retstr, "="); + + + /* Search for string within metadata (beginning at metaptrs[0]) */ + /* ------------------------------------------------------------ */ + sptr = strstr(metaptrs[0], retstr); + + + /* If string found within desired section ... */ + /* ------------------------------------------ */ + if (sptr != NULL && sptr < metaptrs[1]) + { + /* Store position of string within metadata */ + /* ---------------------------------------- */ + metaptrs[0] = sptr; + + /* Find newline "\n" character */ + /* --------------------------- */ + newline = strchr(metaptrs[0], '\n'); + + /* Copy from "=" to "\n" (exclusive) into return string */ + /* ---------------------------------------------------- */ + memcpy(retstr, metaptrs[0] + slen, newline - metaptrs[0] - slen); + + /* Terminate return string with null */ + /* --------------------------------- */ + retstr[newline - metaptrs[0] - slen] = 0; + } else + { + /* + * if parameter string not found within section, null return string + * and set status to -1. + */ + retstr[0] = 0; + status = -1; + } + + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHmetagroup | +| | +| DESCRIPTION: Returns pointers to beginning and end of metadata group | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| metabuf char Pointer to HDF-EOS object in metadata | +| | +| INPUTS: | +| sdInterfaceID int32 SDS interface ID | +| structname char HDF-EOS structure name | +| structcode char Structure code ("s/g/p") | +| groupname char Metadata group name | +| | +| OUTPUTS: | +| metaptrs char pointers to begin and end of metadata | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +char * +EHmetagroup(int32 sdInterfaceID, char *structname, char *structcode, + char *groupname, char *metaptrs[]) +{ + intn i; /* Loop index */ + + int32 attrIndex; /* Structural metadata attribute index */ + int32 nmeta; /* Number of 32000 byte metadata sections */ + int32 metalen; /* Length of structural metadata */ + + char *metabuf; /* Pointer (handle) to structural metadata */ + char *endptr; /* Pointer to end of metadata section */ + char *metaptr; /* Metadata pointer */ + char *prevmetaptr;/* Previous position of metadata pointer */ + char *utlstr; /* Utility string */ + + + + /* Allocate memory for utility string */ + /* ---------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE,sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"EHEHmetagroup", __FILE__, __LINE__); + + return( NULL); + } + /* Determine number of structural metadata "sections" */ + /* -------------------------------------------------- */ + nmeta = 0; + while (1) + { + /* Search for "StructMetadata.x" attribute */ + /* --------------------------------------- */ + sprintf(utlstr, "%s%d", "StructMetadata.", (int)nmeta); + attrIndex = SDfindattr(sdInterfaceID, utlstr); + + + /* If found then increment metadata section counter else exit loop */ + /* --------------------------------------------------------------- */ + if (attrIndex != -1) + { + nmeta++; + } else + { + break; + } + } + + + /* Allocate space for metadata (in units of 32000 bytes) */ + /* ----------------------------------------------------- */ + metabuf = (char *) calloc(32000 * nmeta, 1); + + if(metabuf == NULL) + { + HEpush(DFE_NOSPACE,"EHmetagroup", __FILE__, __LINE__); + free(utlstr); + return(metabuf); + } + + + /* Read structural metadata */ + /* ------------------------ */ + for (i = 0; i < nmeta; i++) + { + sprintf(utlstr, "%s%d", "StructMetadata.", i); + attrIndex = SDfindattr(sdInterfaceID, utlstr); + metalen = strlen(metabuf); + SDreadattr(sdInterfaceID, attrIndex, metabuf + metalen); + } + + /* Determine length (# of characters) of metadata */ + /* ---------------------------------------------- */ + metalen = strlen(metabuf); + + + + /* Find HDF-EOS structure "root" group in metadata */ + /* ----------------------------------------------- */ + + /* Setup proper search string */ + /* -------------------------- */ + if (strcmp(structcode, "s") == 0) + { + strcpy(utlstr, "GROUP=SwathStructure"); + } else if (strcmp(structcode, "g") == 0) + { + strcpy(utlstr, "GROUP=GridStructure"); + } else if (strcmp(structcode, "p") == 0) + { + strcpy(utlstr, "GROUP=PointStructure"); + } + /* Use string search routine (strstr) to move through metadata */ + /* ----------------------------------------------------------- */ + metaptr = strstr(metabuf, utlstr); + + + + /* Save current metadata pointer */ + /* ----------------------------- */ + prevmetaptr = metaptr; + + + /* First loop for "old-style" (non-ODL) metadata string */ + /* ---------------------------------------------------- */ + if (strcmp(structcode, "s") == 0) + { + sprintf(utlstr, "%s%s", "SwathName=\"", structname); + } else if (strcmp(structcode, "g") == 0) + { + sprintf(utlstr, "%s%s", "GridName=\"", structname); + } else if (strcmp(structcode, "p") == 0) + { + sprintf(utlstr, "%s%s", "PointName=\"", structname); + } + /* Do string search */ + /* ---------------- */ + metaptr = strstr(metaptr, utlstr); + + + /* + * If not found then return to previous position in metadata and look for + * "new-style" (ODL) metadata string + */ + if (metaptr == NULL) + { + sprintf(utlstr, "%s%s", "GROUP=\"", structname); + metaptr = strstr(prevmetaptr, utlstr); + } + /* Find group within structure */ + /* --------------------------- */ + if (groupname != NULL) + { + sprintf(utlstr, "%s%s", "GROUP=", groupname); + metaptr = strstr(metaptr, utlstr); + + sprintf(utlstr, "%s%s", "\t\tEND_GROUP=", groupname); + endptr = strstr(metaptr, utlstr); + } else + { + /* If groupname == NULL then find end of structure in metadata */ + /* ----------------------------------------------------------- */ + sprintf(utlstr, "%s", "\n\tEND_GROUP="); + endptr = strstr(metaptr, utlstr); + } + + + /* Return beginning and ending pointers */ + /* ------------------------------------ */ + metaptrs[0] = metaptr; + metaptrs[1] = endptr; + + free(utlstr); + + return (metabuf); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHfillfld | +| | +| DESCRIPTION: Fills field with fill value | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| sdid int32 SD element ID | +| rank int32 Rank of field | +| truerank int32 True rank of field (merging) | +| size int32 size of fill element | +| off int32 Offset of field within merged field | +| dims int32 Dimensions of field | +| fillval void fill value | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHfillfld(int32 sdid, int32 rank, int32 truerank, int32 size, int32 off, + int32 dims[], VOIDP fillval) +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 n; /* Max number of planes or rows in fill + * buffer */ + int32 start[3] = {0, 0, 0}; /* Start array (SDwritedata) */ + int32 edge[3]; /* Edge (count) array (SDwritedata) */ + int32 totN; /* Total number of elements in field */ + int32 planeN; /* Number of elements in plane */ + + char *fillbuf; /* Fill buffer */ + + + /* Get total number of elements in field */ + /* ------------------------------------- */ + totN = dims[0]; + for (i = 1; i < rank; i++) + { + totN *= dims[i]; + } + + + /* Get number of elements in a plane of the field */ + /* ---------------------------------------------- */ + planeN = dims[1] * dims[2]; + + + + /* Allocate & Write Fill buffer */ + /* ---------------------------- */ + if (totN * size < HDFE_MAXMEMBUF) + { + /* Entire field size (in bytes) smaller than max fill buffer */ + /* --------------------------------------------------------- */ + + + /* Allocate fill buffer */ + /* -------------------- */ + fillbuf = (char *) malloc(totN * size); + if(fillbuf == NULL) + { + HEpush(DFE_NOSPACE,"EHfillfld", __FILE__, __LINE__); + return(-1); + } + + + /* Fill buffer with fill value */ + /* --------------------------- */ + for (i = 0; i < totN; i++) + { + memcpy(fillbuf + i * size, fillval, size); + } + + + /* Write fill buffer to field */ + /* -------------------------- */ + start[0] = off; + edge[0] = dims[0]; + edge[1] = dims[1]; + edge[2] = dims[2]; + status = SDwritedata(sdid, start, NULL, edge, + (VOIDP) fillbuf); + + free(fillbuf); + + } else if (planeN * size < HDFE_MAXMEMBUF) + { + /* Single plane size (in bytes) smaller than max fill buffer */ + /* --------------------------------------------------------- */ + + + /* Compute number of planes that can be written at one time */ + /* -------------------------------------------------------- */ + n = HDFE_MAXMEMBUF / (planeN * size); + + + /* Allocate fill buffer */ + /* -------------------- */ + fillbuf = (char *) malloc(planeN * size * n); + if(fillbuf == NULL) + { + HEpush(DFE_NOSPACE,"EHfillfld", __FILE__, __LINE__); + return(-1); + } + + + /* Fill buffer with fill value */ + /* --------------------------- */ + for (i = 0; i < planeN * n; i++) + { + memcpy(fillbuf + i * size, fillval, size); + } + + + /* Write (full) fill buffer to field */ + /* --------------------------------- */ + for (i = 0; i < (dims[0] / n); i++) + { + start[0] = off + i * n; + edge[0] = n; + edge[1] = dims[1]; + edge[2] = dims[2]; + status = SDwritedata(sdid, start, NULL, edge, + (VOIDP) fillbuf); + } + + + /* Write (partial) last fill buffer to field (if necessary) */ + /* -------------------------------------------------------- */ + if (i * n != dims[0]) + { + start[0] = off + i * n; + edge[0] = dims[0] - i * n; + edge[1] = dims[1]; + edge[2] = dims[2]; + status = SDwritedata(sdid, start, NULL, edge, + (VOIDP) fillbuf); + } + free(fillbuf); + + } else + { + /* Single plane size (in bytes) greater than max fill buffer */ + /* --------------------------------------------------------- */ + + + /* Compute number of "rows" than can be written at one time */ + /* -------------------------------------------------------- */ + n = HDFE_MAXMEMBUF / (dims[rank - 1] * size); + + + /* Allocate fill buffer */ + /* -------------------- */ + fillbuf = (char *) malloc(dims[rank - 1] * size * n); + if(fillbuf == NULL) + { + HEpush(DFE_NOSPACE,"EHfillfld", __FILE__, __LINE__); + return(-1); + } + + + /* Fill buffer with fill value */ + /* --------------------------- */ + for (i = 0; i < dims[rank - 1] * n; i++) + { + memcpy(fillbuf + i * size, fillval, size); + } + + + /* For every plane in field ... */ + /* ---------------------------- */ + for (j = 0; j < dims[0]; j++) + { + + /* Write (full) fill buffer to field */ + /* --------------------------------- */ + for (i = 0; i < (dims[1] / n); i++) + { + start[0] = off + j; + start[1] = i * n; + edge[0] = 1; + edge[1] = n; + edge[2] = dims[2]; + status = SDwritedata(sdid, start, NULL, edge, + (VOIDP) fillbuf); + } + + + /* Write (partial) last fill buffer to field (if necessary) */ + /* -------------------------------------------------------- */ + if (i * n != dims[1]) + { + start[0] = off + j; + start[1] = i * n; + edge[0] = 1; + edge[1] = dims[1] - i * n; + edge[2] = dims[2]; + status = SDwritedata(sdid, start, NULL, edge, + (VOIDP) fillbuf); + } + } + + free(fillbuf); + + } + + return (status); +} + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHbisect | +| | +| DESCRIPTION: Finds root of function using bisection | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| func() float64 Function to bisect | +| funcParms float64 Function parameters (fixed) | +| nParms int32 Number of function parameters | +| limLft float64 Lower limit of function arguement | +| limRgt float64 Upper limit of function arguement | +| convCrit float64 Convergence criterion | +| | +| OUTPUTS: | +| root float64 Function root | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Nov 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHbisect(float64(*func) (float64[]), float64 funcParms[], int32 nParms, + float64 limLft, float64 limRgt, float64 convCrit, float64 * root) +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + + float64 midPnt; /* Mid-point value */ + float64 newmidPnt; /* New mid-point value */ + float64 funcLft; /* Function value at left-hand limit */ + float64 funcMid; /* Function value at mid-point */ + float64 funcRgt; /* Function value at right-hand limit */ + float64 *parms; /* Function parameters */ + + + /* Allocate space for function parameters */ + /* -------------------------------------- */ + parms = (float64 *) calloc(nParms + 1, sizeof(float64)); + if(parms == NULL) + { + HEpush(DFE_NOSPACE, "EHbisect", __FILE__, __LINE__); + return(-1); + } + + + /* Copy (fixed) function parameters */ + /* -------------------------------- */ + for (i = 0; i < nParms; i++) + { + parms[i + 1] = funcParms[i]; + } + + + /* Copy left-hand limit to "floating" parameter */ + /* -------------------------------------------- */ + parms[0] = limLft; + + + /* Determine function value */ + /* ------------------------ */ + funcLft = (*func) (parms); + + + /* Copy right-hand limit to "floating" parameter */ + /* --------------------------------------------- */ + parms[0] = limRgt; + + + /* Determine function value */ + /* ------------------------ */ + funcRgt = (*func) (parms); + + + /* If left and right limits function values of same sign then no root */ + /* ------------------------------------------------------------------ */ + if (funcLft * funcRgt > 0) + { + free(parms); + return (-1); + } + /* Compute (initial) mid-point */ + /* --------------------------- */ + newmidPnt = 0.5 * (limLft + limRgt); + + + /* Bisection Loop */ + /* -------------- */ + while (1) + { + /* Compute function at new mid-point */ + /* --------------------------------- */ + midPnt = newmidPnt; + parms[0] = midPnt; + funcMid = (*func) (parms); + + + /* If left limit same sign as mid-point move it to mid-point */ + /* --------------------------------------------------------- */ + if (funcLft * funcMid > 0.0) + { + limLft = midPnt; + } else + { + /* Otherwise move over right-hand limit */ + /* ------------------------------------ */ + limRgt = midPnt; + } + + + /* Compute new mid-point */ + /* --------------------- */ + newmidPnt = 0.5 * (limLft + limRgt); + + + /* If relative change in midpoint < convergence crit then exit loop */ + /* ---------------------------------------------------------------- */ + if (fabs((newmidPnt - midPnt) / midPnt) < convCrit) + { + break; + } + } + + /* Save root */ + /* --------- */ + *root = newmidPnt; + + + free(parms); + + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHattr | +| | +| DESCRIPTION: Reads/Writes attributes for HDF-EOS structures | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| fid int32 HDF-EOS file ID | +| attrVgrpID int32 Attribute Vgroup ID | +| attrname char attribute name | +| numbertype int32 attribute HDF numbertype | +| count int32 Number of attribute elements | +| wrcode char Read/Write Code "w/r" | +| datbuf void I/O buffer | +| | +| | +| OUTPUTS: | +| datbuf void I/O buffer | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Pass Vgroup id as routine parameter | +| Oct 96 Joel Gales Remove Vdetach call | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHattr(int32 fid, int32 attrVgrpID, char *attrname, int32 numbertype, + int32 count, char *wrcode, VOIDP datbuf) + +{ + intn status = 0; /* routine return status variable */ + int32 vdataID; /* Attribute Vdata ID */ + + /* + * Attributes are stored as Vdatas with name given by the user, class: + * "Attr0.0" and fieldname: "AttrValues" + */ + + + /* Get Attribute Vdata ID and "open" with approriate I/O code */ + /* ---------------------------------------------------------- */ + vdataID = EHgetid(fid, attrVgrpID, attrname, 1, wrcode); + + /* Write Attribute Section */ + /* ----------------------- */ + if (strcmp(wrcode, "w") == 0) + { + /* Create Attribute Vdata (if it doesn't exist) */ + /* -------------------------------------------- */ + if (vdataID == -1) + { + vdataID = VSattach(fid, -1, "w"); + VSsetname(vdataID, attrname); + VSsetclass(vdataID, "Attr0.0"); + + VSfdefine(vdataID, "AttrValues", numbertype, count); + Vinsert(attrVgrpID, vdataID); + } + /* Write Attribute */ + /* --------------- */ + VSsetfields(vdataID, "AttrValues"); + (void) VSsizeof(vdataID, "AttrValues"); + VSwrite(vdataID, datbuf, 1, FULL_INTERLACE); + + VSdetach(vdataID); + } + /* Read Attribute Section */ + /* ---------------------- */ + if (strcmp(wrcode, "r") == 0) + { + /* If attribute doesn't exist report error */ + /* --------------------------------------- */ + if (vdataID == -1) + { + status = -1; + HEpush(DFE_GENAPP, "EHattr", __FILE__, __LINE__); + HEreport("Attribute %s not defined.\n", attrname); + } else + { + VSsetfields(vdataID, "AttrValues"); + (void) VSsizeof(vdataID, "AttrValues"); + VSread(vdataID, datbuf, 1, FULL_INTERLACE); + VSdetach(vdataID); + } + } + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHattrinfo | +| | +| DESCRIPTION: Returns numbertype and count of given HDF-EOS attribute | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| fid int32 HDF-EOS file ID | +| attrVgrpID int32 Attribute Vgroup ID | +| attrname char attribute name | +| | +| OUTPUTS: | +| numbertype int32 attribute HDF numbertype | +| count int32 Number of attribute elements | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Pass Vgroup id as routine parameter | +| Oct 96 Joel Gales Remove Vdetach call | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHattrinfo(int32 fid, int32 attrVgrpID, char *attrname, int32 * numbertype, + int32 * count) + +{ + intn status = 0; /* routine return status variable */ + int32 vdataID; /* Attribute Vdata ID */ + + /* Get Attribute Vdata ID */ + /* ---------------------- */ + vdataID = EHgetid(fid, attrVgrpID, attrname, 1, "r"); + + /* If attribute not defined then report error */ + /* ------------------------------------------ */ + if (vdataID == -1) + { + status = -1; + HEpush(DFE_GENAPP, "EHattr", __FILE__, __LINE__); + HEreport("Attribute %s not defined.\n", attrname); + } else + { + /* Get attribute info */ + /* ------------------ */ + VSsetfields(vdataID, "AttrValues"); + *count = VSsizeof(vdataID, "AttrValues"); + *numbertype = VFfieldtype(vdataID, 0); + VSdetach(vdataID); + } + + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHattrcat | +| | +| DESCRIPTION: Returns a listing of attributes within an HDF-EOS structure | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nattr int32 Number of attributes in swath struct | +| | +| INPUTS: | +| fid int32 HDF-EOS file ID | +| attrVgrpID int32 Attribute Vgroup ID | +| structcode char Structure Code ("s/g/p") | +| | +| OUTPUTS: | +| attrnames char Attribute names in swath struct | +| (Comma-separated list) | +| strbufsize int32 Attributes name list string length | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Pass Vgroup id as routine parameter | +| Oct 96 Joel Gales Remove Vdetach call | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +EHattrcat(int32 fid, int32 attrVgrpID, char *attrnames, int32 * strbufsize) +{ + intn i; /* Loop index */ + + int32 nObjects; /* # of objects in Vgroup */ + int32 *tags; /* Pnt to Vgroup object tags array */ + int32 *refs; /* Pnt to Vgroup object refs array */ + int32 vdataID; /* Attribute Vdata ID */ + + int32 nattr = 0; /* Number of attributes */ + int32 slen; /* String length */ + + char name[80]; /* Attribute name */ + static const char indxstr[] = "INDXMAP:"; /* Index Mapping reserved + string */ + static const char fvstr[] = "_FV_"; /* Flag Value reserved string */ + static const char bsom[] = "_BLKSOM:";/* Block SOM Offset reserved string */ + + + /* Set string buffer size to 0 */ + /* --------------------------- */ + *strbufsize = 0; + + + /* Get number of attributes within Attribute Vgroup */ + /* ------------------------------------------------ */ + nObjects = Vntagrefs(attrVgrpID); + + + /* If attributes exist ... */ + /* ----------------------- */ + if (nObjects > 0) + { + /* Get tags and references of attribute Vdatas */ + /* ------------------------------------------- */ + tags = (int32 *) malloc(sizeof(int32) * nObjects); + if(tags == NULL) + { + HEpush(DFE_NOSPACE,"EHattrcat", __FILE__, __LINE__); + return(-1); + } + refs = (int32 *) malloc(sizeof(int32) * nObjects); + if(refs == NULL) + { + HEpush(DFE_NOSPACE,"EHattrcat", __FILE__, __LINE__); + free(tags); + return(-1); + } + + Vgettagrefs(attrVgrpID, tags, refs, nObjects); + + /* Get attribute vdata IDs and names */ + /* --------------------------------- */ + for (i = 0; i < nObjects; i++) + { + vdataID = VSattach(fid, *(refs + i), "r"); + VSgetname(vdataID, name); + + /* + * Don't return fill value, index mapping & block SOM attributes + */ + if (memcmp(name, indxstr, strlen(indxstr)) != 0 && + memcmp(name, fvstr, strlen(fvstr)) != 0 && + memcmp(name, bsom, strlen(bsom)) != 0) + { + /* Increment attribute counter and add name to list */ + /* ------------------------------------------------ */ + nattr++; + if (attrnames != NULL) + { + if (nattr == 1) + { + strcpy(attrnames, name); + } else + { + strcat(attrnames, ","); + strcat(attrnames, name); + } + } + /* Increment attribute names string length */ + /* --------------------------------------- */ + slen = (nattr == 1) ? strlen(name) : strlen(name) + 1; + *strbufsize += slen; + } + VSdetach(vdataID); + } + free(tags); + free(refs); + } + return (nattr); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHinquire | +| | +| DESCRIPTION: Returns number and names of HDF-EOS structures in file | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nobj int32 Number of HDF-EOS structures in file | +| | +| INPUTS: | +| filename char HDF-EOS filename | +| type char Object Type ("SWATH/GRID/POINT") | +| | +| OUTPUTS: | +| objectlist char List of object names (comma-separated) | +| strbufsize int32 Length of objectlist | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +EHinquire(char *filename, char *type, char *objectlist, int32 * strbufsize) +{ + int32 HDFfid; /* HDF file ID */ + int32 vgRef; /* Vgroup reference number */ + int32 vGrpID; /* Vgroup ID */ + int32 nobj = 0; /* Number of HDFEOS objects in file */ + int32 slen; /* String length */ + + char name[512]; /* Object name */ + char class[80]; /* Object class */ + + + /* Open HDFEOS file of read-only access */ + /* ------------------------------------ */ + HDFfid = Hopen(filename, DFACC_READ, 0); + + + /* Start Vgroup Interface */ + /* ---------------------- */ + Vstart(HDFfid); + + + /* If string buffer size is requested then zero out counter */ + /* -------------------------------------------------------- */ + if (strbufsize != NULL) + { + *strbufsize = 0; + } + /* Search for objects from begining of HDF file */ + /* -------------------------------------------- */ + vgRef = -1; + + /* Loop through all objects */ + /* ------------------------ */ + while (1) + { + /* Get Vgroup reference number */ + /* --------------------------- */ + vgRef = Vgetid(HDFfid, vgRef); + + /* If no more then exist search loop */ + /* --------------------------------- */ + if (vgRef == -1) + { + break; + } + /* Get Vgroup ID, name, and class */ + /* ------------------------------ */ + vGrpID = Vattach(HDFfid, vgRef, "r"); + Vgetname(vGrpID, name); + Vgetclass(vGrpID, class); + + + /* If object of desired type (SWATH, POINT, GRID) ... */ + /* -------------------------------------------------- */ + if (strcmp(class, type) == 0) + { + + /* Increment counter */ + /* ----------------- */ + nobj++; + + + /* If object list requested add name to list */ + /* ----------------------------------------- */ + if (objectlist != NULL) + { + if (nobj == 1) + { + strcpy(objectlist, name); + } else + { + strcat(objectlist, ","); + strcat(objectlist, name); + } + } + /* Compute string length of object entry */ + /* ------------------------------------- */ + slen = (nobj == 1) ? strlen(name) : strlen(name) + 1; + + + /* If string buffer size is requested then increment buffer size */ + /* ------------------------------------------------------------- */ + if (strbufsize != NULL) + { + *strbufsize += slen; + } + } + /* Detach Vgroup */ + /* ------------- */ + Vdetach(vGrpID); + } + + /* "Close" Vgroup interface and HDFEOS file */ + /* ---------------------------------------- */ + Vend(HDFfid); + Hclose(HDFfid); + + return (nobj); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHclose | +| | +| DESCRIPTION: Closes HDF-EOS file | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| fid int32 HDF-EOS File ID | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Jul 96 Joel Gales Add file id offset EHIDOFFSET | +| Aug 96 Joel Gales Add HE error report if file id out of bounds | +| Nov 96 Joel Gales Add EHXacsTable array to "garbage collection" | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +EHclose(int32 fid) +{ + intn status = 0; /* routine return status variable */ + + int32 HDFfid; /* HDF file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 fid0; /* HDF EOS file id - offset */ + + + /* Check for valid HDFEOS file ID range */ + /* ------------------------------------ */ + if (fid >= EHIDOFFSET && fid < EHXmaxfilecount + EHIDOFFSET) + { + /* Compute "reduced" file ID */ + /* ------------------------- */ + fid0 = fid % EHIDOFFSET; + + + /* Get HDF file ID and SD interface ID */ + /* ----------------------------------- */ + HDFfid = EHXfidTable[fid0]; + sdInterfaceID = EHXsdTable[fid0]; + + /* "Close" SD interface, Vgroup interface, and HDF file */ + /* ---------------------------------------------------- */ + status = SDend(sdInterfaceID); + status = Vend(HDFfid); + status = Hclose(HDFfid); + + /* Clear out external array entries */ + /* -------------------------------- */ + EHXtypeTable[fid0] = 0; + EHXacsTable[fid0] = 0; + EHXfidTable[fid0] = 0; + EHXsdTable[fid0] = 0; + if (EHget_numfiles() == 0) + { + free(EHXtypeTable); + EHXtypeTable = NULL; + free(EHXacsTable); + EHXacsTable = NULL; + free(EHXfidTable); + EHXfidTable = NULL; + free(EHXsdTable); + EHXsdTable = NULL; + EHXmaxfilecount = 0; + } + } else + { + status = -1; + HEpush(DFE_RANGE, "EHclose", __FILE__, __LINE__); + HEreport("Invalid file id: %d. ID must be >= %d and < %d.\n", + fid, EHIDOFFSET, EHXmaxfilecount + EHIDOFFSET); + } + + return (status); +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHnumstr | +| | +| DESCRIPTION: Returns numerical type code of the given string | +| representation. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| numbertype int32 numerical type code | +| | +| INPUTS: | +| strcode const char string representation of the type code | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Nov 07 Andrey Kiselev Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +EHnumstr(const char *strcode) +{ + if (strcmp(strcode, "DFNT_UCHAR8") == 0) + return DFNT_UCHAR8; + else if (strcmp(strcode, "DFNT_CHAR8") == 0) + return DFNT_CHAR8; + else if (strcmp(strcode, "DFNT_FLOAT32") == 0) + return DFNT_FLOAT32; + else if (strcmp(strcode, "DFNT_FLOAT64") == 0) + return DFNT_FLOAT64; + else if (strcmp(strcode, "DFNT_INT8") == 0) + return DFNT_INT8; + else if (strcmp(strcode, "DFNT_UINT8") == 0) + return DFNT_UINT8; + else if (strcmp(strcode, "DFNT_INT16") == 0) + return DFNT_INT16; + else if (strcmp(strcode, "DFNT_UINT16") == 0) + return DFNT_UINT16; + else if (strcmp(strcode, "DFNT_INT32") == 0) + return DFNT_INT32; + else if (strcmp(strcode, "DFNT_UINT32") == 0) + return DFNT_UINT32; + else + return DFNT_NONE; +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHreset_maxopenfiles | +| | +| DESCRIPTION: Change the allowed number of opened HDFEOS files. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| numbertype intn The current maximum number of opened | +| files allowed, or -1, if unable | +| to reset it. | +| | +| INPUTS: | +| strcode intn Requested number of opened files. | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ========== ============ ============================================== | +| 2013.04.03 Andrey Kiselev Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +EHreset_maxopenfiles(intn req_max) +{ + intn ret_value; + + if (req_max <= EHXmaxfilecount) + return EHXmaxfilecount; + + /* Falback to built-in NEOSHDF constant if */ + /* SDreset_maxopenfiles() interface is not available */ + /* ------------------------------------------------- */ +#ifdef HDF4_HAS_MAXOPENFILES + ret_value = SDreset_maxopenfiles(req_max); +#else + ret_value = NEOSHDF; +#endif /* HDF4_HAS_MAXOPENFILES */ + + if (ret_value > 0) + { + EHXtypeTable = realloc(EHXtypeTable, ret_value * sizeof(*EHXtypeTable)); + memset(EHXtypeTable + EHXmaxfilecount, 0, + (ret_value - EHXmaxfilecount) * sizeof(*EHXtypeTable)); + EHXacsTable = realloc(EHXacsTable, ret_value * sizeof(*EHXacsTable)); + memset(EHXacsTable + EHXmaxfilecount, 0, + (ret_value - EHXmaxfilecount) * sizeof(*EHXacsTable)); + EHXfidTable = realloc(EHXfidTable, ret_value * sizeof(*EHXfidTable)); + memset(EHXfidTable + EHXmaxfilecount, 0, + (ret_value - EHXmaxfilecount) * sizeof(*EHXfidTable)); + EHXsdTable = realloc(EHXsdTable, ret_value * sizeof(*EHXsdTable)); + memset(EHXsdTable + EHXmaxfilecount, 0, + (ret_value - EHXmaxfilecount) * sizeof(*EHXsdTable)); + EHXmaxfilecount = ret_value; + } + + return ret_value; +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHget_maxopenfiles | +| | +| DESCRIPTION: Request the allowed number of opened HDFEOS files and maximum | +| number of opened files allowed in the system. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| None | +| | +| | +| OUTPUTS: | +| curr_max intn Current number of open files allowed. | +| sys_limit intn Maximum number of open files allowed | +| in the system. | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ========== ============ ============================================== | +| 2013.04.03 Andrey Kiselev Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +EHget_maxopenfiles(intn *curr_max, + intn *sys_limit) +{ + intn ret_value = 0; + +#ifdef HDF4_HAS_MAXOPENFILES + ret_value = SDget_maxopenfiles(curr_max, sys_limit); +#else + *sys_limit = NEOSHDF; +#endif /* HDF4_HAS_MAXOPENFILES */ + + *curr_max = EHXmaxfilecount; + + return ret_value; +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: EHget_numfiles | +| | +| DESCRIPTION: Request the number of HDFEOS files currently opened. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nfileopen intn Number of HDFEOS files already opened. | +| | +| INPUTS: | +| None | +| | +| | +| OUTPUTS: | +| None | +| in the system. | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ========== ============ ============================================== | +| 2013.04.03 Andrey Kiselev Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +EHget_numfiles() +{ + intn i; /* Loop index */ + intn nfileopen = 0; /* # of HDF files open */ + + if (EHXtypeTable) + { + /* Determine number of files currently opened */ + /* ------------------------------------------ */ + for (i = 0; i < EHXmaxfilecount; i++) + { + nfileopen += EHXtypeTable[i]; + } + } + + return nfileopen; +} diff --git a/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/GDapi.c b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/GDapi.c new file mode 100644 index 000000000..3fd0f822d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/GDapi.c @@ -0,0 +1,10869 @@ +/***************************************************************************** + * $Id: GDapi.c 29336 2015-06-14 17:37:21Z rouault $ + * + * This module has a number of additions and improvements over the original + * implementation to be suitable for usage in GDAL HDF driver. + * + * Andrey Kiselev is responsible for all the changes. + ****************************************************************************/ + +/* +Copyright (C) 1996 Hughes and Applied Research Corporation + +Permission to use, modify, and distribute this software and its documentation +for any purpose without fee is hereby granted, provided that the above +copyright notice appear in all copies and that both that copyright notice and +this permission notice appear in supporting documentation. +*/ +/***************************************************************************** +REVISIONS: + +Aug 31, 1999 Abe Taaheri Changed memory allocation for utility strings to + the size of UTLSTR_MAX_SIZE. + Added error check for memory unavailibilty in + several functions. + Added check for NULL metabuf returned from + EHmeta... functions. NULL pointer retruned from + EHmeta... functions indicate that memory could not + be allocated for metabuf. +Jun 27, 2000 Abe Taaheri Added support for EASE grid that uses + Behrmann Cylinderical Equal Area (BCEA) projection +Oct 23, 2000 Abe Taaheri Updated for ISINUS projection, so that both codes + 31 and 99 can be used for this projection. +Jan 15, 2003 Abe Taaheri Modified for generalization of EASE Grid. + +Jun 05, 2003 Bruce Beaumont / Abe Taaheri + + Fixed SQUARE definition. + Added static projection number/name translation + Added projection table lookup in GDdefproj. + Removed projection table from GDdefproj + Added projection table lookup in GDprojinfo + Removed projection table from GDprojinfo + Added cast for compcode in call to SDsetcompress + in GDdeffield to avoid compiler errors + Removed declaration for unused variable endptr + in GDSDfldsrch + Removed initialization code for unused variables + in GDSDfldsrch + Removed declarations for unused variables + BCEA_scale, r0, s0, xMtr0, xMtr1, yMtr0, + and yMtr1 in GDll2ij + Removed initialization code for unused variables + in GDll2ij + Added code in GEO projection handling to allow + map to span dateline in GDll2ij + Changed "for each point" loop in GDll2ij to + return -2147483648.0 for xVal and yVal if + for_trans returned an error instead of + returning an error to the caller + (Note: MAXLONG is defined as 2147483647.0 in + function cproj.c of GCTP) + Added code in GDij2ll to use for_trans to + translate the BCEA corner points from packed + degrees to meters + Removed declarations for unused variables + BCEA_scale, r0, s0, xMtr, yMtr, epsilon, + beta, qp_cea, kz_cea, eccen, eccen_sq, + phi1, sinphi1, cosphi1, lon, lat, xcor, + ycor, and nlatlon from GDij2ll + Removed initialization code for unused variables + in GDij2ll + Added declarations for xMtr0, yMtr0, xMtr1, and + yMtr1 in GDij2ll + Added special-case code for BCEA + Changed "for each point" loop in GDij2ll to + return PGSd_GCT_IN_ERROR (1.0e51) for + longitude and latitude values if inv_trans + returned an error instead of return an error + to the caller + Removed declaration for unused variable ii in + GDgetpixvalues + Removed declaration for unused variable + numTileDims in GDtileinfo + Added error message and error return at the + end of GDll2mm_cea + Added return statement to GDll2mm_cea +******************************************************************************/ +#include "cpl_string.h" +#include "stdio.h" +#include "mfhdf.h" +#include "hcomp.h" +#include +#include "HdfEosDef.h" + +#include "hdf4compat.h" + +extern void for_init(int32, int32, float64 *, int32, char *, char *, int32 *, + int32 (*for_trans[])()); +extern void inv_init(int32, int32, float64 *, int32, char *, char *, int32 *, + int32 (*inv_trans[])()); + +#define GDIDOFFSET 4194304 +#define SQUARE(x) ((x) * (x)) /* x**2 */ +#define M_PI1 3.14159265358979323846 + +static int32 GDXSDcomb[512*5]; +static char GDXSDname[HDFE_NAMBUFSIZE]; +static char GDXSDdims[HDFE_DIMBUFSIZE]; + + +#define NGRID 200 +/* Grid Structure External Arrays */ +struct gridStructure +{ + int32 active; + int32 IDTable; + int32 VIDTable[2]; + int32 fid; + int32 nSDS; + int32 *sdsID; + int32 compcode; + intn compparm[5]; + int32 tilecode; + int32 tilerank; + int32 tiledims[8]; +}; +static struct gridStructure GDXGrid[NGRID]; + + + +#define NGRIDREGN 256 +struct gridRegion +{ + int32 fid; + int32 gridID; + int32 xStart; + int32 xCount; + int32 yStart; + int32 yCount; + int32 somStart; + int32 somCount; + float64 upleftpt[2]; + float64 lowrightpt[2]; + int32 StartVertical[8]; + int32 StopVertical[8]; + char *DimNamePtr[8]; +}; +static struct gridRegion *GDXRegion[NGRIDREGN]; + +/* define a macro for the string size of the utility strings and some dimension + list strings. The value of 80 in the previous version of this code + may not be enough in some cases. The length now is 512 which seems to + be more than enough to hold larger strings. */ + +#define UTLSTR_MAX_SIZE 512 + +/* Static projection table */ +static const struct { + int32 projcode; + char *projname; +} Projections[] = { + {GCTP_GEO, "GCTP_GEO"}, + {GCTP_UTM, "GCTP_UTM"}, + {GCTP_SPCS, "GCTP_SPCS"}, + {GCTP_ALBERS, "GCTP_ALBERS"}, + {GCTP_LAMCC, "GCTP_LAMCC"}, + {GCTP_MERCAT, "GCTP_MERCAT"}, + {GCTP_PS, "GCTP_PS"}, + {GCTP_POLYC, "GCTP_POLYC"}, + {GCTP_EQUIDC, "GCTP_EQUIDC"}, + {GCTP_TM, "GCTP_TM"}, + {GCTP_STEREO, "GCTP_STEREO"}, + {GCTP_LAMAZ, "GCTP_LAMAZ"}, + {GCTP_AZMEQD, "GCTP_AZMEQD"}, + {GCTP_GNOMON, "GCTP_GNOMON"}, + {GCTP_ORTHO, "GCTP_ORTHO"}, + {GCTP_GVNSP, "GCTP_GVNSP"}, + {GCTP_SNSOID, "GCTP_SNSOID"}, + {GCTP_EQRECT, "GCTP_EQRECT"}, + {GCTP_MILLER, "GCTP_MILLER"}, + {GCTP_VGRINT, "GCTP_VGRINT"}, + {GCTP_HOM, "GCTP_HOM"}, + {GCTP_ROBIN, "GCTP_ROBIN"}, + {GCTP_SOM, "GCTP_SOM"}, + {GCTP_ALASKA, "GCTP_ALASKA"}, + {GCTP_GOOD, "GCTP_GOOD"}, + {GCTP_MOLL, "GCTP_MOLL"}, + {GCTP_IMOLL, "GCTP_IMOLL"}, + {GCTP_HAMMER, "GCTP_HAMMER"}, + {GCTP_WAGIV, "GCTP_WAGIV"}, + {GCTP_WAGVII, "GCTP_WAGVII"}, + {GCTP_OBLEQA, "GCTP_OBLEQA"}, + {GCTP_ISINUS1, "GCTP_ISINUS1"}, + {GCTP_CEA, "GCTP_CEA"}, + {GCTP_BCEA, "GCTP_BCEA"}, + {GCTP_ISINUS, "GCTP_ISINUS"}, + {-1, NULL} +}; + +/* Compression Codes */ +static const char *HDFcomp[] = { + "HDFE_COMP_NONE", + "HDFE_COMP_RLE", + "HDFE_COMP_NBIT", + "HDFE_COMP_SKPHUFF", + "HDFE_COMP_DEFLATE" +}; + +/* Origin Codes */ +static const char *originNames[] = { + "HDFE_GD_UL", + "HDFE_GD_UR", + "HDFE_GD_LL", + "HDFE_GD_LR" +}; + +/* Pixel Registration Codes */ +static const char *pixregNames[] = { + "HDFE_CENTER", + "HDFE_CORNER" +}; + +/* Grid Function Prototypes (internal routines) */ +static intn GDchkgdid(int32, char *, int32 *, int32 *, int32 *); +static intn GDSDfldsrch(int32, int32, const char *, int32 *, int32 *, + int32 *, int32 *, int32 [], int32 *); +static intn GDwrrdfield(int32, char *, char *, + int32 [], int32 [], int32 [], VOIDP datbuf); +static intn GDwrrdattr(int32, char *, int32, int32, char *, VOIDP); +static intn GDll2ij(int32, int32, float64 [], int32, int32, int32, float64[], + float64[], int32, float64[], float64[], int32[], int32[], + float64[], float64[]); +static intn GDgetdefaults(int32, int32, float64[], int32, + float64[], float64[]); +static intn GDtangentpnts(int32, float64[], float64[], float64[], float64[], + float64 [], int32 *); +static intn GDwrrdtile(int32, char *, char *, int32 [], VOIDP); +static intn GDll2mm_cea(int32,int32, int32, float64[], int32, int32, + float64[], float64[], int32, float64[],float64[], + float64[], float64[], float64 *, float64 *); + +static intn GDmm2ll_cea(int32, int32, int32, float64[], int32, int32, + float64[], float64[], int32, float64[], float64[], + float64[], float64[]); + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDopen | +| | +| DESCRIPTION: Opens or creates HDF file in order to create, read, or write | +| a grid. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| fid int32 HDF-EOS file ID | +| | +| INPUTS: | +| filename char Filename | +| access intn HDF access code | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDopen(char *filename, intn access) + +{ + int32 fid /* HDF-EOS file ID */ ; + + /* Call EHopen to perform file access */ + /* ---------------------------------- */ + fid = EHopen(filename, access); + + return (fid); + +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDcreate | +| | +| DESCRIPTION: Creates a grid within the file. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| gridID int32 Grid structure ID | +| | +| INPUTS: | +| fid int32 File ID | +| gridname char Grid structure name | +| xdimsize int32 Number of columns in grid | +| ydimsize int32 Number of rows in grid | +| upleftpt float64 Location (m/deg) of upper left corner | +| lowrightpt float64 Location (m/deg) of lower right corner | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Aug 96 Joel Gales Check grid name for ODL compliance | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDcreate(int32 fid, char *gridname, int32 xdimsize, int32 ydimsize, + float64 upleftpt[], float64 lowrightpt[]) +{ + intn i; /* Loop index */ + intn ngridopen = 0; /* # of grid structures open */ + intn status = 0; /* routine return status variable */ + + uint8 access; /* Read/Write file access code */ + + int32 HDFfid; /* HDF file id */ + int32 vgRef; /* Vgroup reference number */ + int32 vgid[3]; /* Vgroup ID array */ + int32 gridID = -1;/* HDF-EOS grid ID */ + + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + int32 nGrid = 0; /* Grid counter */ + + char name[80]; /* Vgroup name */ + char class[80]; /* Vgroup class */ + char errbuf[256];/* Buffer for error message */ + char utlbuf[1024]; /* Utility buffer */ + char header[128];/* Structural metadata header string */ + char footer[256];/* Structural metadata footer string */ + char refstr1[128]; /* Upper left ref string (metadata) */ + char refstr2[128]; /* Lower right ref string (metadata) */ + + + /* + * Check HDF-EOS file ID, get back HDF file ID, SD interface ID and + * access code + */ + status = EHchkfid(fid, gridname, &HDFfid, &sdInterfaceID, &access); + + + /* Check gridname for length */ + /* ------------------------- */ + if ((intn) strlen(gridname) > VGNAMELENMAX) + { + status = -1; + HEpush(DFE_GENAPP, "GDcreate", __FILE__, __LINE__); + HEreport("Gridname \"%s\" must be less than %d characters.\n", + gridname, VGNAMELENMAX); + } + + + + if (status == 0) + { + /* Determine number of grids currently opened */ + /* ------------------------------------------- */ + for (i = 0; i < NGRID; i++) + { + ngridopen += GDXGrid[i].active; + } + + + /* Setup file interface */ + /* -------------------- */ + if (ngridopen < NGRID) + { + + /* Check that grid has not been previously opened */ + /* ----------------------------------------------- */ + vgRef = -1; + + while (1) + { + vgRef = Vgetid(HDFfid, vgRef); + + /* If no more Vgroups then exist while loop */ + /* ---------------------------------------- */ + if (vgRef == -1) + { + break; + } + + /* Get name and class of Vgroup */ + /* ---------------------------- */ + vgid[0] = Vattach(HDFfid, vgRef, "r"); + Vgetname(vgid[0], name); + Vgetclass(vgid[0], class); + Vdetach(vgid[0]); + + + /* If GRID then increment # grid counter */ + /* ------------------------------------- */ + if (strcmp(class, "GRID") == 0) + { + nGrid++; + } + + + /* If grid already exist, return error */ + /* ------------------------------------ */ + if (strcmp(name, gridname) == 0 && + strcmp(class, "GRID") == 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDcreate", __FILE__, __LINE__); + HEreport("\"%s\" already exists.\n", gridname); + break; + } + } + + + if (status == 0) + { + /* Create Root Vgroup for Grid */ + /* ---------------------------- */ + vgid[0] = Vattach(HDFfid, -1, "w"); + + + /* Set Name and Class (GRID) */ + /* -------------------------- */ + Vsetname(vgid[0], gridname); + Vsetclass(vgid[0], "GRID"); + + + + /* Create Data Fields Vgroup */ + /* ------------------------- */ + vgid[1] = Vattach(HDFfid, -1, "w"); + Vsetname(vgid[1], "Data Fields"); + Vsetclass(vgid[1], "GRID Vgroup"); + Vinsert(vgid[0], vgid[1]); + + + + /* Create Attributes Vgroup */ + /* ------------------------ */ + vgid[2] = Vattach(HDFfid, -1, "w"); + Vsetname(vgid[2], "Grid Attributes"); + Vsetclass(vgid[2], "GRID Vgroup"); + Vinsert(vgid[0], vgid[2]); + + + + /* Establish Grid in Structural MetaData Block */ + /* -------------------------------------------- */ + sprintf(header, "%s%d%s%s%s%s%d%s%s%d%s", + "\tGROUP=GRID_", (int)(nGrid + 1), + "\n\t\tGridName=\"", gridname, "\"\n", + "\t\tXDim=", (int)xdimsize, "\n", + "\t\tYDim=", (int)ydimsize, "\n"); + + + sprintf(footer, + "%s%s%s%s%s%s%s%d%s", + "\t\tGROUP=Dimension\n", + "\t\tEND_GROUP=Dimension\n", + "\t\tGROUP=DataField\n", + "\t\tEND_GROUP=DataField\n", + "\t\tGROUP=MergedFields\n", + "\t\tEND_GROUP=MergedFields\n", + "\tEND_GROUP=GRID_", (int)(nGrid + 1), "\n"); + + + + /* Build Ref point Col-Row strings */ + /* ------------------------------- */ + if (upleftpt == NULL || + (upleftpt[0] == 0 && upleftpt[1] == 0 && + lowrightpt[0] == 0 && lowrightpt[1] == 0)) + { + strcpy(refstr1, "DEFAULT"); + strcpy(refstr2, "DEFAULT"); + } + else + { + CPLsprintf(refstr1, "%s%f%s%f%s", + "(", upleftpt[0], ",", upleftpt[1], ")"); + + CPLsprintf(refstr2, "%s%f%s%f%s", + "(", lowrightpt[0], ",", lowrightpt[1], ")"); + } + + sprintf(utlbuf, + "%s%s%s%s%s%s%s%s", + header, + "\t\tUpperLeftPointMtrs=", refstr1, "\n", + "\t\tLowerRightMtrs=", refstr2, "\n", + footer); + + status = EHinsertmeta(sdInterfaceID, "", "g", 1002L, + utlbuf, NULL); + + } + } + else + { + /* Too many files opened */ + /* --------------------- */ + status = -1; + strcpy(errbuf, + "No more than %d grids may be open simutaneously"); + strcat(errbuf, " (%s)"); + HEpush(DFE_DENIED, "GDcreate", __FILE__, __LINE__); + HEreport(errbuf, NGRID, gridname); + } + + + /* Assign gridID # & Load grid and GDXGrid.fid table entries */ + /* --------------------------------------------------------- */ + if (status == 0) + { + + for (i = 0; i < NGRID; i++) + { + if (GDXGrid[i].active == 0) + { + gridID = i + idOffset; + GDXGrid[i].active = 1; + GDXGrid[i].IDTable = vgid[0]; + GDXGrid[i].VIDTable[0] = vgid[1]; + GDXGrid[i].VIDTable[1] = vgid[2]; + GDXGrid[i].fid = fid; + status = 0; + break; + } + } + + } + } + return (gridID); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDattach | +| | +| DESCRIPTION: Attaches to an existing grid within the file. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| gridID int32 grid structure ID | +| | +| INPUTS: | +| fid int32 HDF-EOS file id | +| gridname char grid sructure name | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Sep 99 Abe Taaheri Modified test for memory allocation check when no | +| SDSs are in the grid, NCR24147 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDattach(int32 fid, char *gridname) + +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn ngridopen = 0; /* # of grid structures open */ + intn status; /* routine return status variable */ + + uint8 acs; /* Read/Write file access code */ + + int32 HDFfid; /* HDF file id */ + int32 vgRef; /* Vgroup reference number */ + int32 vgid[3]; /* Vgroup ID array */ + int32 gridID = -1;/* HDF-EOS grid ID */ + int32 *tags; /* Pnt to Vgroup object tags array */ + int32 *refs; /* Pnt to Vgroup object refs array */ + int32 dum; /* dummy varible */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 nObjects; /* # of objects in Vgroup */ + int32 nSDS; /* SDS counter */ + int32 index; /* SDS index */ + int32 sdid; /* SDS object ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + char name[80]; /* Vgroup name */ + char class[80]; /* Vgroup class */ + char errbuf[256];/* Buffer for error message */ + char acsCode[1]; /* Read/Write access char: "r/w" */ + + + /* Check HDF-EOS file ID, get back HDF file ID and access code */ + /* ----------------------------------------------------------- */ + status = EHchkfid(fid, gridname, &HDFfid, &dum, &acs); + + + if (status == 0) + { + /* Convert numeric access code to character */ + /* ---------------------------------------- */ + + acsCode[0] = (acs == 1) ? 'w' : 'r'; + + /* Determine number of grids currently opened */ + /* ------------------------------------------- */ + for (i = 0; i < NGRID; i++) + { + ngridopen += GDXGrid[i].active; + } + + + /* If room for more ... */ + /* -------------------- */ + if (ngridopen < NGRID) + { + + /* Search Vgroups for Grid */ + /* ------------------------ */ + vgRef = -1; + + while (1) + { + vgRef = Vgetid(HDFfid, vgRef); + + /* If no more Vgroups then exist while loop */ + /* ---------------------------------------- */ + if (vgRef == -1) + { + break; + } + + /* Get name and class of Vgroup */ + /* ---------------------------- */ + vgid[0] = Vattach(HDFfid, vgRef, "r"); + Vgetname(vgid[0], name); + Vgetclass(vgid[0], class); + + + /* + * If Vgroup with gridname and class GRID found, load tables + */ + + if (strcmp(name, gridname) == 0 && + strcmp(class, "GRID") == 0) + { + /* Attach to "Data Fields" and "Grid Attributes" Vgroups */ + /* ----------------------------------------------------- */ + tags = (int32 *) malloc(sizeof(int32) * 2); + if(tags == NULL) + { + HEpush(DFE_NOSPACE,"GDattach", __FILE__, __LINE__); + return(-1); + } + refs = (int32 *) malloc(sizeof(int32) * 2); + if(refs == NULL) + { + HEpush(DFE_NOSPACE,"GDattach", __FILE__, __LINE__); + free(tags); + return(-1); + } + Vgettagrefs(vgid[0], tags, refs, 2); + vgid[1] = Vattach(HDFfid, refs[0], acsCode); + vgid[2] = Vattach(HDFfid, refs[1], acsCode); + free(tags); + free(refs); + + + /* Setup External Arrays */ + /* --------------------- */ + for (i = 0; i < NGRID; i++) + { + /* Find empty entry in array */ + /* ------------------------- */ + if (GDXGrid[i].active == 0) + { + /* + * Set gridID, Set grid entry active, Store root + * Vgroup ID, Store sub Vgroup IDs, Store HDF-EOS + * file ID + */ + gridID = i + idOffset; + GDXGrid[i].active = 1; + GDXGrid[i].IDTable = vgid[0]; + GDXGrid[i].VIDTable[0] = vgid[1]; + GDXGrid[i].VIDTable[1] = vgid[2]; + GDXGrid[i].fid = fid; + break; + } + } + + /* Get SDS interface ID */ + /* -------------------- */ + status = GDchkgdid(gridID, "GDattach", &dum, + &sdInterfaceID, &dum); + + + /* Get # of entries within Data Vgroup & search for SDS */ + /* ---------------------------------------------------- */ + nObjects = Vntagrefs(vgid[1]); + + if (nObjects > 0) + { + /* Get tag and ref # for Data Vgroup objects */ + /* ----------------------------------------- */ + tags = (int32 *) malloc(sizeof(int32) * nObjects); + if(tags == NULL) + { + HEpush(DFE_NOSPACE,"GDattach", __FILE__, __LINE__); + return(-1); + } + refs = (int32 *) malloc(sizeof(int32) * nObjects); + if(refs == NULL) + { + HEpush(DFE_NOSPACE,"GDattach", __FILE__, __LINE__); + free(tags); + return(-1); + } + Vgettagrefs(vgid[1], tags, refs, nObjects); + + /* Count number of SDS & allocate SDS ID array */ + /* ------------------------------------------- */ + nSDS = 0; + for (j = 0; j < nObjects; j++) + { + if (tags[j] == DFTAG_NDG) + { + nSDS++; + } + } + GDXGrid[i].sdsID = (int32 *) calloc(nSDS, 4); + if(GDXGrid[i].sdsID == NULL && nSDS != 0) + { + HEpush(DFE_NOSPACE,"GDattach", __FILE__, __LINE__); + free(tags); + free(refs); + return(-1); + } + nSDS = 0; + + + + /* Fill SDS ID array */ + /* ----------------- */ + for (j = 0; j < nObjects; j++) + { + /* If object is SDS then get id */ + /* ---------------------------- */ + if (tags[j] == DFTAG_NDG) + { + index = SDreftoindex(sdInterfaceID, refs[j]); + sdid = SDselect(sdInterfaceID, index); + GDXGrid[i].sdsID[nSDS] = sdid; + nSDS++; + GDXGrid[i].nSDS++; + } + } + free(tags); + free(refs); + } + break; + } + + /* Detach Vgroup if not desired Grid */ + /* --------------------------------- */ + Vdetach(vgid[0]); + } + + /* If Grid not found then set up error message */ + /* ------------------------------------------- */ + if (gridID == -1) + { + HEpush(DFE_RANGE, "GDattach", __FILE__, __LINE__); + HEreport("Grid: \"%s\" does not exist within HDF file.\n", + gridname); + } + } + else + { + /* Too many files opened */ + /* --------------------- */ + gridID = -1; + strcpy(errbuf, + "No more than %d grids may be open simutaneously"); + strcat(errbuf, " (%s)"); + HEpush(DFE_DENIED, "GDattach", __FILE__, __LINE__); + HEreport(errbuf, NGRID, gridname); + } + + } + return (gridID); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDchkgdid | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| routname char Name of routine calling GDchkgdid | +| | +| OUTPUTS: | +| fid int32 File ID | +| sdInterfaceID int32 SDS interface ID | +| gdVgrpID int32 grid Vgroup ID | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +GDchkgdid(int32 gridID, char *routname, + int32 * fid, int32 * sdInterfaceID, int32 * gdVgrpID) +{ + intn status = 0; /* routine return status variable */ + uint8 access; /* Read/Write access code */ + int32 gID; /* Grid ID - offset */ + + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + static const char message1[] = + "Invalid grid id: %d in routine \"%s\". ID must be >= %d and < %d.\n"; + static const char message2[] = + "Grid id %d in routine \"%s\" not active.\n"; + + + + /* Check for valid grid id */ + + if (gridID < idOffset || gridID >= NGRID + idOffset) + { + status = -1; + HEpush(DFE_RANGE, "GDchkgdid", __FILE__, __LINE__); + HEreport(message1, gridID, routname, idOffset, NGRID + idOffset); + } + else + { + + /* Compute "reduced" ID */ + /* -------------------- */ + gID = gridID % idOffset; + + + /* Check for active grid ID */ + /* ------------------------ */ + if (GDXGrid[gID].active == 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDchkgdid", __FILE__, __LINE__); + HEreport(message2, gridID, routname); + } + else + { + + /* Get file & SDS ids and Grid key */ + /* -------------------------------- */ + status = EHchkfid(GDXGrid[gID].fid, " ", + fid, sdInterfaceID, &access); + *gdVgrpID = GDXGrid[gID].IDTable; + } + } + return (status); + +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdefdim | +| | +| DESCRIPTION: Defines a new dimension within the grid. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| dimname char Dimension name to define | +| dim int32 Dimemsion value | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDdefdim(int32 gridID, char *dimname, int32 dim) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file id */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + char gridname[80] /* Grid name */ ; + + + /* Check for valid grid id */ + status = GDchkgdid(gridID, "GDdefinedim", + &fid, &sdInterfaceID, &gdVgrpID); + + + /* Make sure dimension >= 0 */ + /* ------------------------ */ + if (dim < 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDdefdim", __FILE__, __LINE__); + HEreport("Dimension value for \"%s\" less than zero: %d.\n", + dimname, dim); + } + + + /* Write Dimension to Structural MetaData */ + /* -------------------------------------- */ + if (status == 0) + { + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + status = EHinsertmeta(sdInterfaceID, gridname, "g", 0L, + dimname, &dim); + } + return (status); + +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdefproj | +| | +| DESCRIPTION: Defines projection of grid. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| projcode int32 GCTP projection code | +| zonecode int32 UTM zone code | +| spherecode int32 GCTP spheriod code | +| projparm float64 Projection parameters | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Jun 00 Abe Taaheri Added support for EASE grid | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDdefproj(int32 gridID, int32 projcode, int32 zonecode, int32 spherecode, + float64 projparm[]) +{ + intn i; /* Loop index */ + intn projx; /* Projection table index */ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + int32 slen; /* String length */ + float64 EHconvAng(); + char utlbuf[1024]; /* Utility Buffer */ + char projparmbuf[512]; /* Projection parameter metadata + * string */ + char gridname[80]; /* Grid Name */ + + + /* Check for valid grid id */ + /* ----------------------- */ + status = GDchkgdid(gridID, "GDdefproj", &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + /* + * If projection not GEO, UTM, or State Code build projection + * parameter string + */ + if (projcode != GCTP_GEO && + projcode != GCTP_UTM && + projcode != GCTP_SPCS) + { + + /* Begin projection parameter list with "(" */ + strcpy(projparmbuf, "("); + + for (i = 0; i < 13; i++) + { + /* If projparm[i] = 0 ... */ + if (projparm[i] == 0.0) + { + strcpy(utlbuf, "0,"); + } + else + { + /* if projparm[i] is integer ... */ + if ((int32) projparm[i] == projparm[i]) + { + sprintf(utlbuf, "%d%s", + (int) projparm[i], ","); + } + /* else projparm[i] is non-zero floating point ... */ + else + { + CPLsprintf(utlbuf, "%f%s", projparm[i], ","); + } + } + strcat(projparmbuf, utlbuf); + } + slen = strlen(projparmbuf); + + /* Add trailing ")" */ + projparmbuf[slen - 1] = ')'; + } + + for (projx = 0; Projections[projx].projcode != -1; projx++) + { + if (projcode == Projections[projx].projcode) + { + break; + } + } + + + /* Build metadata string */ + /* --------------------- */ + if ((projcode == GCTP_GEO)) + { + sprintf(utlbuf, + "%s%s%s", + "\t\tProjection=", Projections[projx].projname, "\n"); + } + else if (projcode == GCTP_UTM || projcode == GCTP_SPCS) + { + sprintf(utlbuf, + "%s%s%s%s%d%s%s%d%s", + "\t\tProjection=", Projections[projx].projname, "\n", + "\t\tZoneCode=", (int)zonecode, "\n", + "\t\tSphereCode=", (int)spherecode, "\n"); + } + else + { + sprintf(utlbuf, + "%s%s%s%s%s%s%s%d%s", + "\t\tProjection=", Projections[projx].projname, "\n", + "\t\tProjParams=", projparmbuf, "\n", + "\t\tSphereCode=", (int)spherecode, "\n"); + } + + + /* Insert in structural metadata */ + /* ----------------------------- */ + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + status = EHinsertmeta(sdInterfaceID, gridname, "g", 101L, + utlbuf, NULL); + } + + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDblkSOMoffset | +| | +| DESCRIPTION: Writes Block SOM offset values | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| offset float32 Offset values | +| count int32 Number of offset values | +| code char w/r code (w/r) | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Sep 96 Joel Gales Original Programmer | +| Mar 99 David Wynne Changed data type of offset array from int32 to | +| float32, NCR 21197 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDblkSOMoffset(int32 gridID, float32 offset[], int32 count, char *code) +{ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + int32 projcode; /* GCTP projection code */ + + float64 projparm[13]; /* Projection parameters */ + + char utlbuf[128];/* Utility Buffer */ + char gridname[80]; /* Grid Name */ + + /* Check for valid grid id */ + status = GDchkgdid(gridID, "GDblkSOMoffset", + &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + /* Get projection parameters */ + status = GDprojinfo(gridID, &projcode, NULL, NULL, projparm); + + /* If SOM projection with projparm[11] non-zero ... */ + if (projcode == GCTP_SOM && projparm[11] != 0) + { + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + sprintf(utlbuf, "%s%s", "_BLKSOM:", gridname); + + /* Write offset values as attribute */ + if (strcmp(code, "w") == 0) + { + status = GDwriteattr(gridID, utlbuf, DFNT_FLOAT32, + count, offset); + } + /* Read offset values from attribute */ + else if (strcmp(code, "r") == 0) + { + status = GDreadattr(gridID, utlbuf, offset); + } + } + } + return (status); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdefcomp | +| | +| DESCRIPTION: Defines compression type and parameters | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| compcode int32 compression code | +| compparm intn compression parameters | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Sep 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDdefcomp(int32 gridID, int32 compcode, intn compparm[]) +{ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file id */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + int32 gID; /* gridID - offset */ + + /* Check for valid grid id */ + status = GDchkgdid(gridID, "GDdefcomp", &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + gID = gridID % idOffset; + + /* Set compression code in compression exteral array */ + GDXGrid[gID].compcode = compcode; + + switch (compcode) + { + /* Set NBIT compression parameters in compression external array */ + case HDFE_COMP_NBIT: + + GDXGrid[gID].compparm[0] = compparm[0]; + GDXGrid[gID].compparm[1] = compparm[1]; + GDXGrid[gID].compparm[2] = compparm[2]; + GDXGrid[gID].compparm[3] = compparm[3]; + + break; + + /* Set GZIP compression parameter in compression external array */ + case HDFE_COMP_DEFLATE: + + GDXGrid[gID].compparm[0] = compparm[0]; + + break; + + } + } + + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdeftile | +| | +| DESCRIPTION: Defines tiling parameters | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| tilecode int32 tile code | +| tilerank int32 number of tiling dimensions | +| tiledims int32 tiling dimensions | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jan 97 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDdeftile(int32 gridID, int32 tilecode, int32 tilerank, int32 tiledims[]) +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file id */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + int32 gID; /* gridID - offset */ + + /* Check for valid grid id */ + /* ----------------------- */ + status = GDchkgdid(gridID, "GDdeftile", &fid, &sdInterfaceID, &gdVgrpID); + + + if (status == 0) + { + gID = gridID % idOffset; + + for (i = 0; i < 8; i++) + { + GDXGrid[gID].tiledims[i] = 0; + } + + GDXGrid[gID].tilecode = tilecode; + + switch (tilecode) + { + case HDFE_NOTILE: + + GDXGrid[gID].tilerank = 0; + + break; + + + case HDFE_TILE: + + GDXGrid[gID].tilerank = tilerank; + + for (i = 0; i < tilerank; i++) + { + GDXGrid[gID].tiledims[i] = tiledims[i]; + + if (GDXGrid[gID].tiledims[i] == 0) + { + GDXGrid[gID].tiledims[i] = 1; + } + } + + break; + + } + } + + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdeforigin | +| | +| DESCRIPTION: Defines the origin of the grid data. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| origincode int32 origin code | +| HDFE_GD_UL (0) | +| HDFE_GD_UR (1) | +| HDFE_GD_LL (2) | +| HDFE_GD_LR (3) | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDdeforigin(int32 gridID, int32 origincode) +{ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + char utlbuf[64]; /* Utility buffer */ + char gridname[80]; /* Grid name */ + + + /* Check for valid grid id */ + status = GDchkgdid(gridID, "GDdeforigin", + &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + /* If proper origin code then write to structural metadata */ + /* ------------------------------------------------------- */ + if (origincode >= 0 && origincode < sizeof(originNames)) + { + sprintf(utlbuf, "%s%s%s", + "\t\tGridOrigin=", originNames[origincode], "\n"); + + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + status = EHinsertmeta(sdInterfaceID, gridname, "g", 101L, + utlbuf, NULL); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDdeforigin", __FILE__, __LINE__); + HEreport("Improper Grid Origin code: %d\n", origincode); + } + } + + return (status); +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdefpixreg | +| | +| DESCRIPTION: Defines pixel registration within grid cell. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| pixregcode int32 Pixel registration code | +| HDFE_CENTER (0) | +| HDFE_CORNER (1) | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDdefpixreg(int32 gridID, int32 pixregcode) +{ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + char utlbuf[64]; /* Utility buffer */ + char gridname[80]; /* Grid name */ + + /* Check for valid grid id */ + status = GDchkgdid(gridID, "GDdefpixreg", + &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + /* If proper pix reg code then write to structural metadata */ + /* -------------------------------------------------------- */ + if (pixregcode >= 0 && pixregcode < sizeof(pixregNames)) + { + sprintf(utlbuf, "%s%s%s", + "\t\tPixelRegistration=", pixregNames[pixregcode], "\n"); + + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + status = EHinsertmeta(sdInterfaceID, gridname, "g", 101L, + utlbuf, NULL); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDdefpixreg", __FILE__, __LINE__); + HEreport("Improper Pixel Registration code: %d\n", pixregcode); + } + } + + return (status); + +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdiminfo | +| | +| DESCRIPTION: Retrieve size of specified dimension. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| size int32 Size of dimension | +| | +| INPUTS: | +| gridID int32 grid structure id | +| dimname char Dimension name | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Jan 97 Joel Gales Check for metadata error status from EHgetmetavalue | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDdiminfo(int32 gridID, char *dimname) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 size; /* Dimension size */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char gridname[80]; /* Grid Name */ + char *utlstr; /* Utility string */ + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"GDdiminfo", __FILE__, __LINE__); + return(-1); + } + /* Initialize return value */ + /* ----------------------- */ + size = -1; + + + /* Check Grid ID */ + /* ------------- */ + status = GDchkgdid(gridID, "GDdiminfo", &fid, &sdInterfaceID, &gdVgrpID); + + + if (status == 0) + { + /* Get grid name */ + /* ------------- */ + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + + + /* Get pointers to "Dimension" section within SM */ + /* --------------------------------------------- */ + metabuf = (char *) EHmetagroup(sdInterfaceID, gridname, "g", + "Dimension", metaptrs); + + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + /* Search for dimension name (surrounded by quotes) */ + /* ------------------------------------------------ */ + sprintf(utlstr, "%s%s%s", "\"", dimname, "\"\n"); + metaptrs[0] = strstr(metaptrs[0], utlstr); + + /* + * If dimension found within grid structure then get dimension value + */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + /* Set endptr at end of dimension definition entry */ + /* ----------------------------------------------- */ + metaptrs[1] = strstr(metaptrs[0], "\t\t\tEND_OBJECT"); + + status = EHgetmetavalue(metaptrs, "Size", utlstr); + + if (status == 0) + { + size = atol(utlstr); + } + else + { + HEpush(DFE_GENAPP, "GDdiminfo", __FILE__, __LINE__); + HEreport("\"Size\" string not found in metadata.\n"); + } + } + else + { + HEpush(DFE_GENAPP, "GDdiminfo", __FILE__, __LINE__); + HEreport("Dimension \"%s\" not found.\n", dimname); + } + + free(metabuf); + } + free(utlstr); + return (size); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDgridinfo | +| | +| DESCRIPTION: Returns xdim, ydim and location of upper left and lower | +| right corners, in meters. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| fid int32 File ID | +| gridname char Grid structure name | +| | +| OUTPUTS: | +| xdimsize int32 Number of columns in grid | +| ydimsize int32 Number of rows in grid | +| upleftpt float64 Location (m/deg) of upper left corner | +| lowrightpt float64 Location (m/deg) of lower right corner | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Jan 97 Joel Gales Check for metadata error status from EHgetmetavalue | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDgridinfo(int32 gridID, int32 * xdimsize, int32 * ydimsize, + float64 upleftpt[], float64 lowrightpt[]) + +{ + intn status = 0; /* routine return status variable */ + intn statmeta = 0; /* EHgetmetavalue return status */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char gridname[80]; /* Grid Name */ + char *utlstr; /* Utility string */ + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"GDgridinfo", __FILE__, __LINE__); + return(-1); + } + /* Check Grid ID */ + /* ------------- */ + status = GDchkgdid(gridID, "GDgridinfo", &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + /* Get grid name */ + /* ------------- */ + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + + + /* Get pointers to grid structure section within SM */ + /* ------------------------------------------------ */ + metabuf = (char *) EHmetagroup(sdInterfaceID, gridname, "g", + NULL, metaptrs); + + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + + /* Get xdimsize if requested */ + /* ------------------------- */ + if (xdimsize != NULL) + { + statmeta = EHgetmetavalue(metaptrs, "XDim", utlstr); + if (statmeta == 0) + { + *xdimsize = atol(utlstr); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDgridinfo", __FILE__, __LINE__); + HEreport("\"XDim\" string not found in metadata.\n"); + } + } + + + /* Get ydimsize if requested */ + /* ------------------------- */ + if (ydimsize != NULL) + { + statmeta = EHgetmetavalue(metaptrs, "YDim", utlstr); + if (statmeta == 0) + { + *ydimsize = atol(utlstr); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDgridinfo", __FILE__, __LINE__); + HEreport("\"YDim\" string not found in metadata.\n"); + } + } + + + /* Get upleftpt if requested */ + /* ------------------------- */ + if (upleftpt != NULL) + { + statmeta = EHgetmetavalue(metaptrs, "UpperLeftPointMtrs", utlstr); + if (statmeta == 0) + { + /* If value is "DEFAULT" then return zeros */ + /* --------------------------------------- */ + if (strcmp(utlstr, "DEFAULT") == 0) + { + upleftpt[0] = 0; + upleftpt[1] = 0; + } + else + { + sscanf(utlstr, "(%lf,%lf)", + &upleftpt[0], &upleftpt[1]); + } + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDgridinfo", __FILE__, __LINE__); + HEreport( + "\"UpperLeftPointMtrs\" string not found in metadata.\n"); + } + + } + + /* Get lowrightpt if requested */ + /* --------------------------- */ + if (lowrightpt != NULL) + { + statmeta = EHgetmetavalue(metaptrs, "LowerRightMtrs", utlstr); + if (statmeta == 0) + { + /* If value is "DEFAULT" then return zeros */ + if (strcmp(utlstr, "DEFAULT") == 0) + { + lowrightpt[0] = 0; + lowrightpt[1] = 0; + } + else + { + sscanf(utlstr, "(%lf,%lf)", + &lowrightpt[0], &lowrightpt[1]); + } + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDgridinfo", __FILE__, __LINE__); + HEreport( + "\"LowerRightMtrs\" string not found in metadata.\n"); + } + } + + free(metabuf); + } + free(utlstr); + return (status); +} + + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDprojinfo | +| | +| DESCRIPTION: Returns GCTP projection code, zone code, spheroid code | +| and projection parameters. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| | +| OUTPUTS: | +| projcode int32 GCTP projection code | +| zonecode int32 UTM zone code | +| spherecode int32 GCTP spheriod code | +| projparm float64 Projection parameters | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Add check for no projection code | +| Jan 97 Joel Gales Check for metadata error status from EHgetmetavalue | +| Jun 00 Abe Taaheri Added support for EASE grid | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDprojinfo(int32 gridID, int32 * projcode, int32 * zonecode, + int32 * spherecode, float64 projparm[]) + +{ + intn i; /* Loop index */ + intn projx; /* Loop index */ + intn status = 0; /* routine return status variable */ + intn statmeta = 0; /* EHgetmetavalue return status */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char gridname[80]; /* Grid Name */ + char *utlstr; /* Utility string */ + char fmt[96]; /* Format String */ + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"GDprojinfo", __FILE__, __LINE__); + return(-1); + } + + /* Check Grid ID */ + /* ------------- */ + status = GDchkgdid(gridID, "GDprojinfo", &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + /* Get grid name */ + /* ------------- */ + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + + + /* Get pointers to grid structure section within SM */ + /* ------------------------------------------------ */ + metabuf = (char *) EHmetagroup(sdInterfaceID, gridname, "g", + NULL, metaptrs); + + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + + /* Get projcode if requested */ + /* ------------------------- */ + if (projcode != NULL) + { + *projcode = -1; + + statmeta = EHgetmetavalue(metaptrs, "Projection", utlstr); + if (statmeta == 0) + { + /* Loop through projection codes until found */ + /* ----------------------------------------- */ + for (projx = 0; Projections[projx].projcode != -1; projx++) + if (strcmp(utlstr, Projections[projx].projname) == 0) + break; + if (Projections[projx].projname != NULL) + *projcode = Projections[projx].projcode; + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDprojinfo", __FILE__, __LINE__); + HEreport("Projection Code not defined for \"%s\".\n", + gridname); + + if (projparm != NULL) + { + for (i = 0; i < 13; i++) + { + projparm[i] = -1; + } + } + } + } + + + /* Get zonecode if requested */ + /* ------------------------- */ + if (zonecode != NULL) + { + *zonecode = -1; + + + /* Zone code only relevant for UTM and State Code projections */ + /* ---------------------------------------------------------- */ + if (*projcode == GCTP_UTM || *projcode == GCTP_SPCS) + { + statmeta = EHgetmetavalue(metaptrs, "ZoneCode", utlstr); + if (statmeta == 0) + { + *zonecode = atol(utlstr); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDprojinfo", __FILE__, __LINE__); + HEreport("Zone Code not defined for \"%s\".\n", + gridname); + } + } + } + + + /* Get projection parameters if requested */ + /* -------------------------------------- */ + if (projparm != NULL) + { + + /* + * Note: No projection parameters for GEO, UTM, and State Code + * projections + */ + if (*projcode == GCTP_GEO || *projcode == GCTP_UTM || + *projcode == GCTP_SPCS) + { + for (i = 0; i < 13; i++) + { + projparm[i] = 0.0; + } + + } + else + { + statmeta = EHgetmetavalue(metaptrs, "ProjParams", utlstr); + + if (statmeta == 0) + { + + /* Build format string to read projection parameters */ + /* ------------------------------------------------- */ + strcpy(fmt, "%lf,"); + for (i = 1; i <= 11; i++) + strcat(fmt, "%lf,"); + strcat(fmt, "%lf"); + + + /* Read parameters from numeric list */ + /* --------------------------------- */ + sscanf(&utlstr[1], fmt, + &projparm[0], &projparm[1], + &projparm[2], &projparm[3], + &projparm[4], &projparm[5], + &projparm[6], &projparm[7], + &projparm[8], &projparm[9], + &projparm[10], &projparm[11], + &projparm[12]); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDprojinfo", __FILE__, __LINE__); + HEreport("Projection parameters not defined for \"%s\".\n", + gridname); + + } + } + } + + + /* Get spherecode if requested */ + /* --------------------------- */ + if (spherecode != NULL) + { + *spherecode = 0; + + /* Note: Spherecode not defined for GEO projection */ + /* ----------------------------------------------- */ + if ((*projcode != GCTP_GEO)) + { + EHgetmetavalue(metaptrs, "SphereCode", utlstr); + if (statmeta == 0) + { + *spherecode = atol(utlstr); + } + } + } + free(metabuf); + + } + free(utlstr); + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDorigininfo | +| | +| DESCRIPTION: Returns origin code | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| | +| | +| OUTPUTS: | +| origincode int32 grid origin code | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Jan 97 Joel Gales Check for metadata error status from EHgetmetavalue | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDorigininfo(int32 gridID, int32 * origincode) +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + intn statmeta = 0; /* EHgetmetavalue return status */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char gridname[80]; /* Grid Name */ + char *utlstr; /* Utility string */ + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"GDorigininfo", __FILE__, __LINE__); + return(-1); + } + /* Check Grid ID */ + /* ------------- */ + status = GDchkgdid(gridID, "GDorigininfo", + &fid, &sdInterfaceID, &gdVgrpID); + + + /* Initialize pixreg code to -1 (in case of error) */ + /* ----------------------------------------------- */ + *origincode = -1; + + if (status == 0) + { + /* Set default origin code */ + /* ----------------------- */ + *origincode = 0; + + + /* Get grid name */ + /* ------------- */ + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + + + /* Get pointers to grid structure section within SM */ + /* ------------------------------------------------ */ + metabuf = (char *) EHmetagroup(sdInterfaceID, gridname, "g", + NULL, metaptrs); + + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + + statmeta = EHgetmetavalue(metaptrs, "GridOrigin", utlstr); + + if (statmeta == 0) + { + /* + * If "GridOrigin" string found in metadata then convert to + * numeric origin code (fixed added: Jan 97) + */ + for (i = 0; i < sizeof(originNames); i++) + { + if (strcmp(utlstr, originNames[i]) == 0) + { + *origincode = i; + break; + } + } + } + + free(metabuf); + } + free(utlstr); + return (status); +} + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDpixreginfo | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| | +| | +| OUTPUTS: | +| pixregcode int32 Pixel registration code | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Jan 97 Joel Gales Check for metadata error status from EHgetmetavalue | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDpixreginfo(int32 gridID, int32 * pixregcode) +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + intn statmeta = 0; /* EHgetmetavalue return status */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char gridname[80]; /* Grid Name */ + char *utlstr; /* Utility string */ + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"GDpixreginfo", __FILE__, __LINE__); + return(-1); + } + /* Check Grid ID */ + status = GDchkgdid(gridID, "GDpixreginfo", + &fid, &sdInterfaceID, &gdVgrpID); + + /* Initialize pixreg code to -1 (in case of error) */ + *pixregcode = -1; + + if (status == 0) + { + /* Set default pixreg code */ + *pixregcode = 0; + + /* Get grid name */ + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + + /* Get pointers to grid structure section within SM */ + metabuf = (char *) EHmetagroup(sdInterfaceID, gridname, "g", + NULL, metaptrs); + + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + + statmeta = EHgetmetavalue(metaptrs, "PixelRegistration", utlstr); + + if (statmeta == 0) + { + /* + * If "PixelRegistration" string found in metadata then convert + * to numeric origin code (fixed added: Jan 97) + */ + + for (i = 0; i < sizeof(pixregNames); i++) + { + if (strcmp(utlstr, pixregNames[i]) == 0) + { + *pixregcode = i; + break; + } + } + } + free(metabuf); + } + free(utlstr); + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDcompinfo | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn | +| | +| INPUTS: | +| gridID int32 | +| compcode int32 | +| compparm intn | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Oct 96 Joel Gales Original Programmer | +| Jan 97 Joel Gales Check for metadata error status from EHgetmetavalue | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDcompinfo(int32 gridID, char *fieldname, int32 * compcode, intn compparm[]) +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + intn statmeta = 0; /* EHgetmetavalue return status */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char gridname[80]; /* Grid Name */ + char *utlstr;/* Utility string */ + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"GDcompinfo", __FILE__, __LINE__); + return(-1); + } + /* Check Grid ID */ + status = GDchkgdid(gridID, "GDcompinfo", &fid, &sdInterfaceID, &gdVgrpID); + + + if (status == 0) + { + /* Get grid name */ + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + + /* Get pointers to "DataField" section within SM */ + metabuf = (char *) EHmetagroup(sdInterfaceID, gridname, "g", + "DataField", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + + /* Search for field */ + sprintf(utlstr, "%s%s%s", "\"", fieldname, "\"\n"); + metaptrs[0] = strstr(metaptrs[0], utlstr); + + + /* If field found and user wants compression code ... */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + if (compcode != NULL) + { + /* Set endptr at end of field's definition entry */ + metaptrs[1] = strstr(metaptrs[0], "\t\t\tEND_OBJECT"); + + /* Get compression type */ + statmeta = EHgetmetavalue(metaptrs, "CompressionType", utlstr); + + /* + * Default is no compression if "CompressionType" string not + * in metadata + */ + *compcode = HDFE_COMP_NONE; + + /* If compression code is found ... */ + if (statmeta == 0) + { + /* Loop through compression types until match */ + for (i = 0; i < sizeof(HDFcomp); i++) + { + if (strcmp(utlstr, HDFcomp[i]) == 0) + { + *compcode = i; + break; + } + } + } + } + + /* If user wants compression parameters ... */ + if (compparm != NULL && compcode != NULL) + { + /* Initialize to zero */ + for (i = 0; i < 4; i++) + { + compparm[i] = 0.0; + } + + /* + * Get compression parameters if NBIT or DEFLATE compression + */ + if (*compcode == HDFE_COMP_NBIT) + { + statmeta = + EHgetmetavalue(metaptrs, "CompressionParams", utlstr); + if (statmeta == 0) + { + sscanf(utlstr, "(%d,%d,%d,%d)", + &compparm[0], &compparm[1], + &compparm[2], &compparm[3]); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDcompinfo", __FILE__, __LINE__); + HEreport( + "\"CompressionParams\" string not found in metadata.\n"); + } + } + else if (*compcode == HDFE_COMP_DEFLATE) + { + statmeta = + EHgetmetavalue(metaptrs, "DeflateLevel", utlstr); + if (statmeta == 0) + { + sscanf(utlstr, "%d", &compparm[0]); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDcompinfo", __FILE__, __LINE__); + HEreport( + "\"DeflateLevel\" string not found in metadata.\n"); + } + } + } + } + else + { + HEpush(DFE_GENAPP, "GDcompinfo", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" not found.\n", fieldname); + } + + free(metabuf); + + } + free(utlstr); + return (status); +} + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDfieldinfo | +| | +| DESCRIPTION: Retrieve information about a specific geolocation or data | +| field in the grid. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure id | +| fieldname char name of field | +| | +| | +| OUTPUTS: | +| rank int32 rank of field (# of dims) | +| dims int32 field dimensions | +| numbertype int32 field number type | +| dimlist char field dimension list | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Jan 97 Joel Gales Check for metadata error status from EHgetmetavalue | +| Feb 99 Abe Taaheri Changed memcpy to memmove to avoid overlapping | +| problem when copying strings | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDfieldinfo(int32 gridID, char *fieldname, int32 * rank, int32 dims[], + int32 * numbertype, char *dimlist) + +{ + intn i; /* Loop index */ + intn status; /* routine return status variable */ + intn statmeta = 0; /* EHgetmetavalue return status */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + int32 ndims; /* Number of dimensions */ + int32 slen[8]; /* Length of each entry in parsed string */ + int32 dum; /* Dummy variable */ + int32 xdim; /* X dim size */ + int32 ydim; /* Y dim size */ + int32 sdid; /* SDS id */ + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2]; /* Pointers to begin and end of SM section */ + char gridname[80]; /* Grid Name */ + char *utlstr; /* Utility string */ + char *ptr[8]; /* String pointers for parsed string */ + char dimstr[64]; /* Individual dimension entry string */ + + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"GDfieldinfo", __FILE__, __LINE__); + return(-1); + } + *rank = -1; + *numbertype = -1; + + status = GDchkgdid(gridID, "GDfieldinfo", &fid, &sdInterfaceID, &dum); + + if (status == 0) + { + + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + + metabuf = (char *) EHmetagroup(sdInterfaceID, gridname, "g", + "DataField", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + + /* Search for field */ + sprintf(utlstr, "%s%s%s", "\"", fieldname, "\"\n"); + metaptrs[0] = strstr(metaptrs[0], utlstr); + + /* If field found ... */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + + /* Set endptr at end of dimension definition entry */ + metaptrs[1] = strstr(metaptrs[0], "\t\t\tEND_OBJECT"); + + /* Get DataType string */ + statmeta = EHgetmetavalue(metaptrs, "DataType", utlstr); + + /* Convert to numbertype code */ + if (statmeta == 0) + *numbertype = EHnumstr(utlstr); + else + { + status = -1; + HEpush(DFE_GENAPP, "GDfieldinfo", __FILE__, __LINE__); + HEreport("\"DataType\" string not found in metadata.\n"); + } + + /* + * Get DimList string and trim off leading and trailing parens + * "()" + */ + statmeta = EHgetmetavalue(metaptrs, "DimList", utlstr); + + if (statmeta == 0) + { + memmove(utlstr, utlstr + 1, strlen(utlstr) - 2); + utlstr[strlen(utlstr) - 2] = 0; + + /* Parse trimmed DimList string and get rank */ + ndims = EHparsestr(utlstr, ',', ptr, slen); + *rank = ndims; + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDfieldinfo", __FILE__, __LINE__); + HEreport("\"DimList\" string not found in metadata.\n"); + } + + + if (status == 0) + { + status = GDgridinfo(gridID, &xdim, &ydim, NULL, NULL); + + for (i = 0; i < ndims; i++) + { + memcpy(dimstr, ptr[i] + 1, slen[i] - 2); + dimstr[slen[i] - 2] = 0; + + if (strcmp(dimstr, "XDim") == 0) + { + dims[i] = xdim; + } + else if (strcmp(dimstr, "YDim") == 0) + { + dims[i] = ydim; + } + else + { + dims[i] = GDdiminfo(gridID, dimstr); + } + + + if (dimlist != NULL) + { + if (i == 0) + { + dimlist[0] = 0; + } + + if (i > 0) + { + strcat(dimlist, ","); + } + strcat(dimlist, dimstr); + } + } + + + if (dims[0] == 0) + { + status = GDSDfldsrch(gridID, sdInterfaceID, fieldname, + &sdid, &dum, &dum, &dum, dims, + &dum); + } + } + } + + free(metabuf); + } + + if (*rank == -1) + { + status = -1; + + HEpush(DFE_GENAPP, "GDfieldinfo", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" not found.\n", fieldname); + } + free(utlstr); + return (status); +} + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdeffield | +| | +| DESCRIPTION: Defines a new data field within the grid. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| fieldname char fieldname | +| dimlist char Dimension list (comma-separated list) | +| numbertype int32 field type | +| merge int32 merge code | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Check name for ODL compliance | +| Sep 96 Joel Gales Make string array "dimbuf" dynamic | +| Sep 96 Joel Gales Add support for Block SOM (MISR) | +| Jan 97 Joel Gales Add support for tiling | +| Feb 99 Abe Taaheri Changed strcpy to memmove to avoid overlapping | +| problem when copying strings | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDdeffield(int32 gridID, char *fieldname, char *dimlist, + int32 numbertype, int32 merge) + +{ + intn i; /* Loop index */ + intn status; /* routine return status variable */ + intn found; /* utility found flag */ + intn foundNT = 0;/* found number type flag */ + intn foundAllDim = 1; /* found all dimensions flag */ + intn first = 1; /* first entry flag */ + + int32 fid; /* HDF-EOS file ID */ + int32 vgid; /* Geo/Data field Vgroup ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 sdid; /* SDS object ID */ + int32 dimid; /* SDS dimension ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 dims[8]; /* Dimension size array */ + int32 dimsize; /* Dimension size */ + int32 rank = 0; /* Field rank */ + int32 slen[32]; /* String length array */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + int32 compcode; /* Compression code */ + int32 tilecode; /* Tiling code */ + int32 chunkFlag; /* Chunking (Tiling) flag */ + int32 gID; /* GridID - offset */ + int32 xdim; /* Grid X dimension */ + int32 ydim; /* Grid Y dimension */ + int32 projcode; /* Projection Code */ + + float64 projparm[13]; /* Projection Parameters */ + + char *dimbuf; /* Dimension buffer */ + char *dimlist0; /* Auxilliary dimension list */ + char *comma; /* Pointer to comma */ + char *dimcheck; /* Dimension check buffer */ + char utlbuf[512];/* Utility buffer */ + char utlbuf2[256]; /* Utility buffer 1 */ + char *ptr[32]; /* String pointer array */ + char gridname[80]; /* Grid name */ + char parmbuf[128]; /* Parameter string buffer */ + char errbuf1[128]; /* Error buffer 1 */ + char errbuf2[128]; /* Error buffer 2 */ + static const char errmsg1[] = "Dimension: %d (size: %d) not divisible by "; + /* Tiling error message part 1 */ + static const char errmsg2[] = "tile dimension (size: %d).\n"; + /* Tiling error message part 2 */ + char errmsg[128];/* Tiling error message */ + + /* Valid number types */ + static const uint16 good_number[10] = { + 3, 4, 5, 6, 20, 21, 22, 23, 24, 25 + }; + + comp_info c_info; /* Compression parameter structure */ + + HDF_CHUNK_DEF chunkDef; /* Tiling structure */ + + + + /* Setup error message strings */ + /* --------------------------- */ + strcpy(errbuf1, "GDXSDname array too small.\nPlease increase "); + strcat(errbuf1, "size of HDFE_NAMBUFSIZE in \"HdfEosDef.h\".\n"); + strcpy(errbuf2, "GDXSDdims array too small.\nPlease increase "); + strcat(errbuf2, "size of HDFE_DIMBUFSIZE in \"HdfEosDef.h\".\n"); + + + /* Build tiling dimension error message */ + /* ------------------------------------ */ + strcpy(errmsg, errmsg1); + strcat(errmsg, errmsg2); + + /* + * Check for proper grid ID and return HDF-EOS file ID, SDinterface ID, + * and grid root Vgroup ID + */ + status = GDchkgdid(gridID, "GDdefinefield", + &fid, &sdInterfaceID, &gdVgrpID); + + + if (status == 0) + { + /* Remove offset from grid ID & get gridname */ + gID = gridID % idOffset; + Vgetname(GDXGrid[gID].IDTable, gridname); + + + /* Allocate space for dimension buffer and auxilliary dimension list */ + /* ----------------------------------------------------------------- */ + dimbuf = (char *) calloc(strlen(dimlist) + 64, 1); + if(dimbuf == NULL) + { + HEpush(DFE_NOSPACE,"GDdeffield", __FILE__, __LINE__); + return(-1); + } + dimlist0 = (char *) calloc(strlen(dimlist) + 64, 1); + if(dimlist0 == NULL) + { + HEpush(DFE_NOSPACE,"GDdeffield", __FILE__, __LINE__); + free(dimbuf); + return(-1); + } + + + /* Get Grid and Projection info */ + /* ---------------------------- */ + status = GDgridinfo(gridID, &xdim, &ydim, NULL, NULL); + status = GDprojinfo(gridID, &projcode, NULL, NULL, projparm); + + + /* Setup Block Dimension if "Blocked" SOM projection */ + /* ------------------------------------------------- */ + if (projcode == GCTP_SOM && (int32) projparm[11] != 0) + { + dimsize = GDdiminfo(gridID, "SOMBlockDim"); + + /* If "SOMBlockDim" not yet defined then do it */ + if (dimsize == -1) + { + GDdefdim(gridID, "SOMBlockDim", (int32) projparm[11]); + } + + /* If not 1D field then prepend to dimension list */ + if (strchr(dimlist, ',') != NULL) + { + strcpy(dimbuf, "SOMBlockDim,"); + strcat(dimbuf, dimlist); + } + else + { + strcpy(dimbuf, dimlist); + } + } + else + { + /* If not "Blocked" SOM then just copy dim list to dim buffer */ + strcpy(dimbuf, dimlist); + } + + /* + * Copy dimension buffer to auxilliary dimlist and Append comma to + * end of dimension list + */ + strcpy(dimlist0, dimbuf); + strcat(dimbuf, ","); + + + /* Find comma */ + /* ---------- */ + comma = strchr(dimbuf, ','); + + + /* + * Loop through entries in dimension list to make sure they are + * defined in grid + */ + while (comma != NULL) + { + /* Copy dimension list entry to dimcheck */ + /* ------------------------------------- */ + dimcheck = (char *) calloc(comma - dimbuf + 1, 1); + if(dimcheck == NULL) + { + HEpush(DFE_NOSPACE,"GDdeffield", __FILE__, __LINE__); + free(dimbuf); + free(dimlist0); + return(-1); + } + memcpy(dimcheck, dimbuf, comma - dimbuf); + + + /* Get Dimension Size */ + /* ------------------ */ + if (strcmp(dimcheck, "XDim") == 0) + { + /* If "XDim" then use xdim value for grid definition */ + /* ------------------------------------------------- */ + dimsize = xdim; + found = 1; + dims[rank] = dimsize; + rank++; + } + else if (strcmp(dimcheck, "YDim") == 0) + { + /* If "YDim" then use ydim value for grid definition */ + /* ------------------------------------------------- */ + dimsize = ydim; + found = 1; + dims[rank] = dimsize; + rank++; + } + else + { + /* "Regular" Dimension */ + /* ------------------- */ + dimsize = GDdiminfo(gridID, dimcheck); + if (dimsize != -1) + { + found = 1; + dims[rank] = dimsize; + rank++; + } + else + { + found = 0; + } + } + + + /* + * If dimension list entry not found - set error return status, + * append name to utility buffer for error report + */ + if (found == 0) + { + status = -1; + foundAllDim = 0; + if (first == 1) + { + strcpy(utlbuf, dimcheck); + } + else + { + strcat(utlbuf, ","); + strcat(utlbuf, dimcheck); + } + first = 0; + } + + /* + * Go to next dimension entry, find next comma, & free up + * dimcheck buffer + */ + memmove(dimbuf, comma + 1, strlen(comma)-1); + dimbuf[strlen(comma)-1]= 0; + comma = strchr(dimbuf, ','); + free(dimcheck); + } + free(dimbuf); + + + + /* Check fieldname length */ + /* ---------------------- */ + if (status == 0) + { +/* if ((intn) strlen(fieldname) > MAX_NC_NAME - 7) +** this was changed because HDF4.1r3 made a change in the +** hlimits.h file. We have notidfied NCSA and asked to have +** it made the same as in previous versions of HDF +** see ncr 26314. DaW Apr 2000 +*/ + if((intn) strlen(fieldname) > (256 - 7)) + { + status = -1; + HEpush(DFE_GENAPP, "GDdefinefield", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" too long.\n", fieldname); + } + } + + + + /* Check for valid numbertype */ + /* -------------------------- */ + if (status == 0) + { + for (i = 0; i < 10; i++) + { + if (numbertype == good_number[i]) + { + foundNT = 1; + break; + } + } + + if (foundNT == 0) + { + HEpush(DFE_BADNUMTYPE, "GDdeffield", __FILE__, __LINE__); + HEreport("Invalid number type: %d (%s).\n", + numbertype, fieldname); + status = -1; + } + } + + + /* Define Field */ + /* ------------ */ + if (status == 0) + { + /* Get Field Vgroup id, compresion code, & tiling code */ + /* -------------------------------------------------- */ + vgid = GDXGrid[gID].VIDTable[0]; + compcode = GDXGrid[gID].compcode; + tilecode = GDXGrid[gID].tilecode; + + + /* SDS Interface (Multi-dim fields) */ + /* -------------------------------- */ + + + /* + * If rank is less than or equal to 3 (and greater than 1) and + * AUTOMERGE is set and the first dimension is not appendable and + * the compression code is set to none then ... + */ + if (rank >= 2 && rank <= 3 && merge == HDFE_AUTOMERGE && + dims[0] != 0 && compcode == HDFE_COMP_NONE && + tilecode == HDFE_NOTILE) + { + + /* Find first empty slot in external combination array */ + /* --------------------------------------------------- */ + i = 0; + while (GDXSDcomb[5 * i] != 0) + { + i++; + } + + /* + * Store dimensions, grid root Vgroup ID, and number type in + * external combination array "GDXSDcomb" + */ + if (rank == 2) + { + /* If 2-dim field then set lowest dimension to 1 */ + /* --------------------------------------------- */ + GDXSDcomb[5 * i] = 1; + GDXSDcomb[5 * i + 1] = dims[0]; + GDXSDcomb[5 * i + 2] = dims[1]; + } + else + { + GDXSDcomb[5 * i] = dims[0]; + GDXSDcomb[5 * i + 1] = dims[1]; + GDXSDcomb[5 * i + 2] = dims[2]; + } + + GDXSDcomb[5 * i + 3] = gdVgrpID; + GDXSDcomb[5 * i + 4] = numbertype; + + + /* Concatanate fieldname with combined name string */ + /* ----------------------------------------------- */ + if ((intn) strlen(GDXSDname) + + (intn) strlen(fieldname) + 2 < HDFE_NAMBUFSIZE) + { + strcat(GDXSDname, fieldname); + strcat(GDXSDname, ","); + } + else + { + /* GDXSDname array too small! */ + /* -------------------------- */ + HEpush(DFE_GENAPP, "GDdefinefield", + __FILE__, __LINE__); + HEreport(errbuf1); + status = -1; + free(dimlist0); + return (status); + } + + + /* + * If 2-dim field then set lowest dimension (in 3-dim array) + * to "ONE" + */ + if (rank == 2) + { + if ((intn) strlen(GDXSDdims) + 5 < HDFE_DIMBUFSIZE) + { + strcat(GDXSDdims, "ONE,"); + } + else + { + /* GDXSDdims array too small! */ + /* -------------------------- */ + HEpush(DFE_GENAPP, "GDdefinefield", + __FILE__, __LINE__); + HEreport(errbuf2); + status = -1; + free(dimlist0); + return (status); + } + } + + + /* + * Concatanate field dimlist to merged dimlist and separate + * fields with semi-colon + */ + if ((intn) strlen(GDXSDdims) + + (intn) strlen(dimlist0) + 2 < HDFE_DIMBUFSIZE) + { + strcat(GDXSDdims, dimlist0); + strcat(GDXSDdims, ";"); + } + else + { + /* GDXSDdims array too small! */ + /* -------------------------- */ + HEpush(DFE_GENAPP, "GDdefinefield", + __FILE__, __LINE__); + HEreport(errbuf2); + status = -1; + free(dimlist0); + return (status); + } + + } /* End Multi-Dim Merge Section */ + else + { + + /* Multi-Dim No Merge Section */ + /* ========================== */ + + + /* Check that field dims are divisible by tile dims */ + /* ------------------------------------------------ */ + if (tilecode == HDFE_TILE) + { + for (i = 0; i < GDXGrid[gID].tilerank; i++) + { + if ((dims[i] % GDXGrid[gID].tiledims[i]) != 0) + { + HEpush(DFE_GENAPP, "GDdeffield", + __FILE__, __LINE__); + HEreport(errmsg, + i, dims[i], GDXGrid[gID].tiledims[0]); + status = -1; + } + } + + if (status == -1) + { + free(dimlist0); + return (status); + } + } + + + /* Create SDS dataset */ + /* ------------------ */ + sdid = SDcreate(sdInterfaceID, fieldname, + numbertype, rank, dims); + + + /* Store Dimension Names in SDS */ + /* ---------------------------- */ + rank = EHparsestr(dimlist0, ',', ptr, slen); + for (i = 0; i < rank; i++) + { + /* Dimension name = Swathname:Dimname */ + /* ---------------------------------- */ + memcpy(utlbuf, ptr[i], slen[i]); + utlbuf[slen[i]] = 0; + strcat(utlbuf, ":"); + strcat(utlbuf, gridname); + + dimid = SDgetdimid(sdid, i); + SDsetdimname(dimid, utlbuf); + } + + + /* Setup Compression */ + /* ----------------- */ + if (compcode == HDFE_COMP_NBIT) + { + c_info.nbit.nt = numbertype; + c_info.nbit.sign_ext = GDXGrid[gID].compparm[0]; + c_info.nbit.fill_one = GDXGrid[gID].compparm[1]; + c_info.nbit.start_bit = GDXGrid[gID].compparm[2]; + c_info.nbit.bit_len = GDXGrid[gID].compparm[3]; + } + else if (compcode == HDFE_COMP_SKPHUFF) + { + c_info.skphuff.skp_size = (intn) DFKNTsize(numbertype); + } + else if (compcode == HDFE_COMP_DEFLATE) + { + c_info.deflate.level = GDXGrid[gID].compparm[0]; + } + + + /* If field is compressed w/o tiling then call SDsetcompress */ + /* --------------------------------------------------------- */ + if (compcode != HDFE_COMP_NONE && tilecode == HDFE_NOTILE) + { + status = SDsetcompress(sdid, (comp_coder_t) compcode, &c_info); + } + + + /* Setup Tiling */ + /* ------------ */ + if (tilecode == HDFE_TILE) + { + /* Tiling without Compression */ + /* -------------------------- */ + if (compcode == HDFE_COMP_NONE) + { + + /* Setup chunk lengths */ + /* ------------------- */ + for (i = 0; i < GDXGrid[gID].tilerank; i++) + { + chunkDef.chunk_lengths[i] = + GDXGrid[gID].tiledims[i]; + } + + chunkFlag = HDF_CHUNK; + } + + /* Tiling with Compression */ + /* ----------------------- */ + else + { + /* Setup chunk lengths */ + /* ------------------- */ + for (i = 0; i < GDXGrid[gID].tilerank; i++) + { + chunkDef.comp.chunk_lengths[i] = + GDXGrid[gID].tiledims[i]; + } + + + /* Setup chunk flag & chunk compression type */ + /* ----------------------------------------- */ + chunkFlag = HDF_CHUNK | HDF_COMP; + chunkDef.comp.comp_type = compcode; + + + /* Setup chunk compression parameters */ + /* ---------------------------------- */ + if (compcode == HDFE_COMP_SKPHUFF) + { + chunkDef.comp.cinfo.skphuff.skp_size = + c_info.skphuff.skp_size; + } + else if (compcode == HDFE_COMP_DEFLATE) + { + chunkDef.comp.cinfo.deflate.level = + c_info.deflate.level; + } + } + + /* Call SDsetchunk routine */ + /* ----------------------- */ + status = SDsetchunk(sdid, chunkDef, chunkFlag); + } + + + /* Attach to Vgroup */ + /* ---------------- */ + Vaddtagref(vgid, DFTAG_NDG, SDidtoref(sdid)); + + + /* Store SDS dataset IDs */ + /* --------------------- */ + + /* Allocate space for the SDS ID array */ + /* ----------------------------------- */ + if (GDXGrid[gID].nSDS > 0) + { + /* Array already exists therefore reallocate */ + /* ----------------------------------------- */ + GDXGrid[gID].sdsID = (int32 *) + realloc((void *) GDXGrid[gID].sdsID, + (GDXGrid[gID].nSDS + 1) * 4); + if(GDXGrid[gID].sdsID == NULL) + { + HEpush(DFE_NOSPACE,"GDdeffield", __FILE__, __LINE__); + free(dimlist0); + return(-1); + } + } + else + { + /* Array does not exist */ + /* -------------------- */ + GDXGrid[gID].sdsID = (int32 *) calloc(1, 4); + if(GDXGrid[gID].sdsID == NULL) + { + HEpush(DFE_NOSPACE,"GDdeffield", __FILE__, __LINE__); + free(dimlist0); + return(-1); + } + } + + /* Store SDS ID in array & increment count */ + /* --------------------------------------- */ + GDXGrid[gID].sdsID[GDXGrid[gID].nSDS] = sdid; + GDXGrid[gID].nSDS++; + + } + + + /* Setup metadata string */ + /* --------------------- */ + sprintf(utlbuf, "%s%s%s", fieldname, ":", dimlist0); + + + /* Setup compression metadata */ + /* -------------------------- */ + if (compcode != HDFE_COMP_NONE) + { + sprintf(utlbuf2, + "%s%s", + ":\n\t\t\t\tCompressionType=", HDFcomp[compcode]); + + switch (compcode) + { + case HDFE_COMP_NBIT: + + sprintf(parmbuf, + "%s%d,%d,%d,%d%s", + "\n\t\t\t\tCompressionParams=(", + GDXGrid[gID].compparm[0], + GDXGrid[gID].compparm[1], + GDXGrid[gID].compparm[2], + GDXGrid[gID].compparm[3], ")"); + strcat(utlbuf2, parmbuf); + break; + + + case HDFE_COMP_DEFLATE: + + sprintf(parmbuf, + "%s%d", + "\n\t\t\t\tDeflateLevel=", + GDXGrid[gID].compparm[0]); + strcat(utlbuf2, parmbuf); + break; + } + strcat(utlbuf, utlbuf2); + } + + + + + /* Setup tiling metadata */ + /* --------------------- */ + if (tilecode == HDFE_TILE) + { + if (compcode == HDFE_COMP_NONE) + { + sprintf(utlbuf2, "%s%d", + ":\n\t\t\t\tTilingDimensions=(", + (int)GDXGrid[gID].tiledims[0]); + } + else + { + sprintf(utlbuf2, "%s%d", + "\n\t\t\t\tTilingDimensions=(", + (int)GDXGrid[gID].tiledims[0]); + } + + for (i = 1; i < GDXGrid[gID].tilerank; i++) + { + sprintf(parmbuf, ",%d", (int)GDXGrid[gID].tiledims[i]); + strcat(utlbuf2, parmbuf); + } + strcat(utlbuf2, ")"); + strcat(utlbuf, utlbuf2); + } + + + /* Insert field metadata within File Structural Metadata */ + /* ----------------------------------------------------- */ + status = EHinsertmeta(sdInterfaceID, gridname, "g", 4L, + utlbuf, &numbertype); + + } + free(dimlist0); + + } + + /* If all dimensions not found then report error */ + /* --------------------------------------------- */ + if (foundAllDim == 0) + { + HEpush(DFE_GENAPP, "GDdeffield", __FILE__, __LINE__); + HEreport("Dimension(s): \"%s\" not found (%s).\n", + utlbuf, fieldname); + status = -1; + } + + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDwritefieldmeta | +| | +| DESCRIPTION: Writes field meta data for an existing grid field not | +| defined within the grid API routine "GDdeffield". | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| fieldname char fieldname | +| dimlist char Dimension list (comma-separated list) | +| numbertype int32 field type | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDwritefieldmeta(int32 gridID, char *fieldname, char *dimlist, + int32 numbertype) +{ + intn status = 0; /* routine return status variable */ + + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 dum; /* dummy variable */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + char utlbuf[256];/* Utility buffer */ + char gridname[80]; /* Grid name */ + + + status = GDchkgdid(gridID, "GDwritefieldmeta", &dum, &sdInterfaceID, + &dum); + + if (status == 0) + { + sprintf(utlbuf, "%s%s%s", fieldname, ":", dimlist); + + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + status = EHinsertmeta(sdInterfaceID, gridname, "g", 4L, + utlbuf, &numbertype); + } + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDSDfldsrch | +| | +| DESCRIPTION: Retrieves information from SDS fields | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| sdInterfaceID int32 SD interface ID | +| fieldname char field name | +| | +| | +| OUTPUTS: | +| sdid int32 SD element ID | +| rankSDS int32 Rank of SDS | +| rankFld int32 True rank of field (merging) | +| offset int32 Offset of field within merged field | +| dims int32 Dimensions of field | +| solo int32 Solo field flag | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +GDSDfldsrch(int32 gridID, int32 sdInterfaceID, const char *fieldname, + int32 * sdid, int32 * rankSDS, int32 * rankFld, int32 * offset, + int32 dims[], int32 * solo) +{ + intn i; /* Loop index */ + intn status = -1;/* routine return status variable */ + + int32 gID; /* GridID - offset */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + int32 dum; /* Dummy variable */ + int32 dums[128]; /* Dummy array */ + int32 attrIndex; /* Attribute index */ + + char name[2048]; /* Merged-Field Names */ + char gridname[80]; /* Grid Name */ + char *utlstr;/* Utility string */ + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char *oldmetaptr; /* Pointer within SM section */ + char *metaptr; /* Pointer within SM section */ + + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"GDSDfldsrch", __FILE__, __LINE__); + return(-1); + } + /* Set solo flag to 0 (no) */ + /* ----------------------- */ + *solo = 0; + + + /* Compute "reduced" grid ID */ + /* ------------------------- */ + gID = gridID % idOffset; + + + /* Loop through all SDSs in grid */ + /* ----------------------------- */ + for (i = 0; i < GDXGrid[gID].nSDS; i++) + { + /* If active SDS ... */ + /* ----------------- */ + if (GDXGrid[gID].sdsID[i] != 0) + { + /* Get SDS ID, name, rankSDS, and dimensions */ + /* ----------------------------------------- */ + *sdid = GDXGrid[gID].sdsID[i]; + SDgetinfo(*sdid, name, rankSDS, dims, &dum, &dum); + *rankFld = *rankSDS; + + + /* If merged field ... */ + /* ------------------- */ + if (strstr(name, "MRGFLD_") == &name[0]) + { + /* Get grid name */ + /* ------------- */ + Vgetname(GDXGrid[gID].IDTable, gridname); + + + /* Get pointers to "MergedFields" section within SM */ + /* ------------------------------------------------ */ + metabuf = (char *) EHmetagroup(sdInterfaceID, gridname, "g", + "MergedFields", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + + /* Initialize metaptr to beg. of section */ + /* ------------------------------------- */ + metaptr = metaptrs[0]; + + + /* Store metaptr in order to recover */ + /* --------------------------------- */ + oldmetaptr = metaptr; + + + /* Search for Merged field name */ + /* ---------------------------- */ + sprintf(utlstr, "%s%s%s", "MergedFieldName=\"", + name, "\"\n"); + metaptr = strstr(metaptr, utlstr); + + + /* If not found check for old metadata */ + /* ----------------------------------- */ + if (metaptr == NULL) + { + sprintf(utlstr, "%s%s%s", "OBJECT=\"", name, "\"\n"); + metaptr = strstr(oldmetaptr, utlstr); + } + + + /* Get field list and strip off leading and trailing quotes */ + /* -------------------------------------------------------- */ + EHgetmetavalue(metaptrs, "FieldList", name); + memmove(name, name + 1, strlen(name) - 2); + name[strlen(name) - 2] = 0; + + + /* Search for desired field within merged field list */ + /* ------------------------------------------------- */ + sprintf(utlstr, "%s%s%s", "\"", fieldname, "\""); + dum = EHstrwithin(utlstr, name, ','); + + free(metabuf); + } + else + { + /* If solo (unmerged) check if SDS name matches fieldname */ + /* ------------------------------------------------------ */ + dum = EHstrwithin(fieldname, name, ','); + if (dum != -1) + { + *solo = 1; + *offset = 0; + } + } + + + + /* If field found ... */ + /* ------------------ */ + if (dum != -1) + { + status = 0; + + /* If merged field ... */ + /* ------------------- */ + if (*solo == 0) + { + /* Get "Field Offsets" SDS attribute index */ + /* --------------------------------------- */ + attrIndex = SDfindattr(*sdid, "Field Offsets"); + + /* + * If attribute exists then get offset of desired field + * within merged field + */ + if (attrIndex != -1) + { + SDreadattr(*sdid, attrIndex, (VOIDP) dums); + *offset = dums[dum]; + } + + + /* Get "Field Dims" SDS attribute index */ + /* ------------------------------------ */ + attrIndex = SDfindattr(*sdid, "Field Dims"); + + /* + * If attribute exists then get 0th dimension of desired + * field within merged field + */ + if (attrIndex != -1) + { + SDreadattr(*sdid, attrIndex, (VOIDP) dums); + dims[0] = dums[dum]; + + /* If this dimension = 1 then field is really 2 dim */ + /* ------------------------------------------------ */ + if (dums[dum] == 1) + { + *rankFld = 2; + } + } + } + + + /* Break out of SDS loop */ + /* --------------------- */ + break; + } /* End of found field section */ + } + else + { + /* First non-active SDS signifies no more, break out of SDS loop */ + /* ------------------------------------------------------------- */ + break; + } + } + free(utlstr); + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDwrrdfield | +| | +| DESCRIPTION: Writes/Reads fields | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| fieldname char fieldname | +| code char Write/Read code (w/r) | +| start int32 start array | +| stride int32 stride array | +| edge int32 edge array | +| datbuf void data buffer for read | +| | +| | +| OUTPUTS: | +| datbuf void data buffer for write | +| | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Feb 97 Joel Gales Stride = 1 HDF compression workaround | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +GDwrrdfield(int32 gridID, char *fieldname, char *code, + int32 start[], int32 stride[], int32 edge[], VOIDP datbuf) + +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 sdid; /* SDS ID */ + int32 dum; /* Dummy variable */ + int32 rankSDS; /* Rank of SDS */ + int32 rankFld; /* Rank of field */ + + int32 offset[8]; /* I/O offset (start) */ + int32 incr[8]; /* I/O increment (stride) */ + int32 count[8]; /* I/O count (edge) */ + int32 dims[8]; /* Field/SDS dimensions */ + int32 mrgOffset; /* Merged field offset */ + int32 strideOne; /* Strides = 1 flag */ + + + /* Check for valid grid ID */ + /* ----------------------- */ + status = GDchkgdid(gridID, "GDwrrdfield", &fid, &sdInterfaceID, &dum); + + + if (status == 0) + { + /* Check that field exists */ + /* ----------------------- */ + status = GDfieldinfo(gridID, fieldname, &rankSDS, dims, &dum, NULL); + + + if (status != 0) + { + HEpush(DFE_GENAPP, "GDwrrdfield", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" does not exist.\n", fieldname); + status = -1; + + } + + + if (status == 0) + { + status = GDSDfldsrch(gridID, sdInterfaceID, fieldname, &sdid, + &rankSDS, &rankFld, &mrgOffset, dims, &dum); + + + /* Set I/O offset Section */ + /* ---------------------- */ + + /* + * If start == NULL (default) set I/O offset of 0th field to + * offset within merged field (if any) and the rest to 0 + */ + if (start == NULL) + { + for (i = 0; i < rankSDS; i++) + { + offset[i] = 0; + } + offset[0] = mrgOffset; + } + else + { + /* + * ... otherwise set I/O offset to user values, adjusting the + * 0th field with the merged field offset (if any) + */ + if (rankFld == rankSDS) + { + for (i = 0; i < rankSDS; i++) + { + offset[i] = start[i]; + } + offset[0] += mrgOffset; + } + else + { + /* + * If field really 2-dim merged in 3-dim field then set + * 0th field offset to merge offset and then next two to + * the user values + */ + for (i = 0; i < rankFld; i++) + { + offset[i + 1] = start[i]; + } + offset[0] = mrgOffset; + } + } + + + + /* Set I/O stride Section */ + /* ---------------------- */ + + /* + * If stride == NULL (default) set I/O stride to 1 + */ + if (stride == NULL) + { + for (i = 0; i < rankSDS; i++) + { + incr[i] = 1; + } + } + else + { + /* + * ... otherwise set I/O stride to user values + */ + if (rankFld == rankSDS) + { + for (i = 0; i < rankSDS; i++) + { + incr[i] = stride[i]; + } + } + else + { + /* + * If field really 2-dim merged in 3-dim field then set + * 0th field stride to 1 and then next two to the user + * values. + */ + for (i = 0; i < rankFld; i++) + { + incr[i + 1] = stride[i]; + } + incr[0] = 1; + } + } + + + + /* Set I/O count Section */ + /* --------------------- */ + + /* + * If edge == NULL (default) set I/O count to number of remaining + * entries (dims - start) / increment. Note that 0th field + * offset corrected for merged field offset (if any). + */ + if (edge == NULL) + { + for (i = 1; i < rankSDS; i++) + { + count[i] = (dims[i] - offset[i]) / incr[i]; + } + count[0] = (dims[0] - (offset[0] - mrgOffset)) / incr[0]; + } + else + { + /* + * ... otherwise set I/O count to user values + */ + if (rankFld == rankSDS) + { + for (i = 0; i < rankSDS; i++) + { + count[i] = edge[i]; + } + } + else + { + /* + * If field really 2-dim merged in 3-dim field then set + * 0th field count to 1 and then next two to the user + * values. + */ + for (i = 0; i < rankFld; i++) + { + count[i + 1] = edge[i]; + } + count[0] = 1; + } + } + + + /* Perform I/O with relevant HDF I/O routine */ + /* ----------------------------------------- */ + if (strcmp(code, "w") == 0) + { + /* Set strideOne to true (1) */ + /* ------------------------- */ + strideOne = 1; + + + /* If incr[i] != 1 set strideOne to false (0) */ + /* ------------------------------------------ */ + for (i = 0; i < rankSDS; i++) + { + if (incr[i] != 1) + { + strideOne = 0; + break; + } + } + + + /* + * If strideOne is true use NULL parameter for stride. This + * is a work-around to HDF compression problem + */ + if (strideOne == 1) + { + status = SDwritedata(sdid, offset, NULL, count, + (VOIDP) datbuf); + } + else + { + status = SDwritedata(sdid, offset, incr, count, + (VOIDP) datbuf); + } + } + else + { + status = SDreaddata(sdid, offset, incr, count, + (VOIDP) datbuf); + } + } + } + + return (status); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDwritefield | +| | +| DESCRIPTION: Writes data to a grid field. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| fieldname char fieldname | +| start int32 start array | +| stride int32 stride array | +| edge int32 edge array | +| | +| | +| OUTPUTS: | +| data void data buffer for write | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDwritefield(int32 gridID, char *fieldname, + int32 start[], int32 stride[], int32 edge[], VOIDP data) + +{ + intn status = 0; /* routine return status variable */ + + status = GDwrrdfield(gridID, fieldname, "w", start, stride, edge, + data); + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDreadfield | +| | +| DESCRIPTION: Reads data from a grid field. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| fieldname char fieldname | +| start int32 start array | +| stride int32 stride array | +| edge int32 edge array | +| buffer void data buffer for read | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDreadfield(int32 gridID, char *fieldname, + int32 start[], int32 stride[], int32 edge[], VOIDP buffer) + +{ + intn status = 0; /* routine return status variable */ + + status = GDwrrdfield(gridID, fieldname, "r", start, stride, edge, + buffer); + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDwrrdattr | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| attrname char attribute name | +| numbertype int32 attribute HDF numbertype | +| count int32 Number of attribute elements | +| wrcode char Read/Write Code "w/r" | +| datbuf void I/O buffer | +| | +| OUTPUTS: | +| datbuf | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Get Attribute Vgroup ID from external array | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +GDwrrdattr(int32 gridID, char *attrname, int32 numbertype, int32 count, + char *wrcode, VOIDP datbuf) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 attrVgrpID; /* Grid attribute ID */ + int32 dum; /* dummy variable */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + + /* Check Grid id */ + status = GDchkgdid(gridID, "GDwrrdattr", &fid, &dum, &dum); + + if (status == 0) + { + /* Perform Attribute I/O */ + /* --------------------- */ + attrVgrpID = GDXGrid[gridID % idOffset].VIDTable[1]; + status = EHattr(fid, attrVgrpID, attrname, numbertype, count, + wrcode, datbuf); + } + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDwriteattr | +| | +| DESCRIPTION: Writes/updates attribute in a grid. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| attrname char attribute name | +| numbertype int32 attribute HDF numbertype | +| count int32 Number of attribute elements | +| datbuf void I/O buffer | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDwriteattr(int32 gridID, char *attrname, int32 numbertype, int32 count, + VOIDP datbuf) +{ + intn status = 0; /* routine return status variable */ + + /* Call GDwrrdattr routine to write attribute */ + /* ------------------------------------------ */ + status = GDwrrdattr(gridID, attrname, numbertype, count, "w", datbuf); + + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDreadattr | +| | +| DESCRIPTION: Reads attribute from a grid. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| attrname char attribute name | +| | +| OUTPUTS: | +| datbuf void I/O buffer | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDreadattr(int32 gridID, char *attrname, VOIDP datbuf) +{ + intn status = 0; /* routine return status variable */ + int32 dum = 0; /* dummy variable */ + + /* Call GDwrrdattr routine to read attribute */ + /* ----------------------------------------- */ + status = GDwrrdattr(gridID, attrname, dum, dum, "r", datbuf); + + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDattrinfo | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| attrname char attribute name | +| | +| OUTPUTS: | +| numbertype int32 attribute HDF numbertype | +| count int32 Number of attribute elements | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Get Attribute Vgroup ID from external array | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDattrinfo(int32 gridID, char *attrname, int32 * numbertype, int32 * count) +{ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 attrVgrpID; /* Grid attribute ID */ + int32 dum; /* dummy variable */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + status = GDchkgdid(gridID, "GDattrinfo", &fid, &dum, &dum); + + attrVgrpID = GDXGrid[gridID % idOffset].VIDTable[1]; + + status = EHattrinfo(fid, attrVgrpID, attrname, numbertype, + count); + + return (status); +} + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDinqattrs | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nattr int32 Number of attributes in swath struct | +| | +| INPUTS: | +| grid ID int32 grid structure ID | +| | +| OUTPUTS: | +| attrnames char Attribute names in swath struct | +| (Comma-separated list) | +| strbufsize int32 Attributes name list string length | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Initialize nattr | +| Oct 96 Joel Gales Get Attribute Vgroup ID from external array | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDinqattrs(int32 gridID, char *attrnames, int32 * strbufsize) +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 attrVgrpID; /* Grid attribute ID */ + int32 dum; /* dummy variable */ + int32 nattr = 0; /* Number of attributes */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + + + /* Check Grid id */ + status = GDchkgdid(gridID, "GDinqattrs", &fid, &dum, &dum); + + if (status == 0) + { + attrVgrpID = GDXGrid[gridID % idOffset].VIDTable[1]; + nattr = EHattrcat(fid, attrVgrpID, attrnames, strbufsize); + } + + return (nattr); +} + + + + + + +#define REMQUOTE \ +\ +memmove(utlstr, utlstr + 1, strlen(utlstr) - 2); \ +utlstr[strlen(utlstr) - 2] = 0; + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDinqdims | +| | +| DESCRIPTION: Retrieve information about all dimensions defined in a grid. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nDim int32 Number of defined dimensions | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| | +| OUTPUTS: | +| dimnames char Dimension names (comma-separated) | +| dims int32 Dimension values | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Feb 97 Joel Gales Set nDim to -1 if status = -1 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDinqdims(int32 gridID, char *dimnames, int32 dims[]) +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 size; /* Dimension size */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + int32 nDim = 0; /* Number of dimensions */ + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char gridname[80]; /* Grid Name */ + char *utlstr;/* Utility string */ + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"GDinqdims", __FILE__, __LINE__); + return(-1); + } + /* Check for valid grid id */ + /* ----------------------- */ + status = GDchkgdid(gridID, "GDinqdims", &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + /* If dimension names or sizes are requested */ + /* ----------------------------------------- */ + if (dimnames != NULL || dims != NULL) + { + /* Get grid name */ + /* ------------- */ + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + + + /* Get pointers to "Dimension" section within SM */ + /* --------------------------------------------- */ + metabuf = (char *) EHmetagroup(sdInterfaceID, gridname, "g", + "Dimension", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + + /* If dimension names are requested then "clear" name buffer */ + /* --------------------------------------------------------- */ + if (dimnames != NULL) + { + dimnames[0] = 0; + } + + while (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + strcpy(utlstr, "\t\tOBJECT="); + metaptrs[0] = strstr(metaptrs[0], utlstr); + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + /* Get Dimension Name */ + /* ------------------ */ + if (dimnames != NULL) + { + /* Check 1st for old meta data then new */ + /* ------------------------------------ */ + EHgetmetavalue(metaptrs, "OBJECT", utlstr); + if (utlstr[0] != '"') + { + metaptrs[0] = + strstr(metaptrs[0], "\t\t\t\tDimensionName="); + EHgetmetavalue(metaptrs, "DimensionName", utlstr); + } + + /* Strip off double quotes */ + /* ----------------------- */ + memmove(utlstr, utlstr + 1, strlen(utlstr) - 2); + utlstr[strlen(utlstr) - 2] = 0; + + if (nDim > 0) + { + strcat(dimnames, ","); + } + strcat(dimnames, utlstr); + } + + /* Get Dimension Size */ + /* ------------------ */ + if (dims != NULL) + { + EHgetmetavalue(metaptrs, "Size", utlstr); + size = atol(utlstr); + dims[nDim] = size; + } + nDim++; + } + } + free(metabuf); + + } + } + + + /* Set nDim to -1 if error status exists */ + /* ------------------------------------- */ + if (status == -1) + { + nDim = -1; + } + free(utlstr); + return (nDim); +} + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDinqfields | +| | +| DESCRIPTION: Retrieve information about all data fields defined in a grid. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nFld int32 Number of fields in swath | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| | +| | +| OUTPUTS: | +| fieldlist char Field names (comma-separated) | +| rank int32 Array of ranks | +| numbertype int32 Array of HDF number types | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Feb 97 Joel Gales Set nFld to -1 if status = -1 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDinqfields(int32 gridID, char *fieldlist, int32 rank[], + int32 numbertype[]) +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + int32 nFld = 0; /* Number of mappings */ + int32 slen[8]; /* String length array */ + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char gridname[80]; /* Grid Name */ + char *utlstr;/* Utility string */ + char *ptr[8]; /* String pointer array */ + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"GDinqfields", __FILE__, __LINE__); + return(-1); + } + /* Check for valid grid id */ + /* ----------------------- */ + status = GDchkgdid(gridID, "GDinqfields", &fid, &sdInterfaceID, &gdVgrpID); + if (status == 0) + { + + /* If field names, ranks, or number types desired ... */ + /* --------------------------------------------------- */ + if (fieldlist != NULL || rank != NULL || numbertype != NULL) + { + /* Get grid name */ + /* ------------- */ + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + + + /* Get pointers to "DataField" section within SM */ + /* --------------------------------------------- */ + metabuf = (char *) EHmetagroup(sdInterfaceID, gridname, "g", + "DataField", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + + /* If field names are desired then "clear" name buffer */ + /* --------------------------------------------------- */ + if (fieldlist != NULL) + { + fieldlist[0] = 0; + } + + + /* Begin loop through mapping entries in metadata */ + /* ---------------------------------------------- */ + while (1) + { + /* Search for OBJECT string */ + /* ------------------------ */ + metaptrs[0] = strstr(metaptrs[0], "\t\tOBJECT="); + + + /* If found within "Data" Field metadata section .. */ + /* ------------------------------------------------ */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + /* Get Fieldnames (if desired) */ + /* --------------------------- */ + if (fieldlist != NULL) + { + /* Check 1st for old meta data then new */ + /* ------------------------------------ */ + EHgetmetavalue(metaptrs, "OBJECT", utlstr); + + /* + * If OBJECT value begins with double quote then old + * metadata, field name is OBJECT value. Otherwise + * search for "DataFieldName" string + */ + + if (utlstr[0] != '"') + { + strcpy(utlstr, "\t\t\t\t"); + strcat(utlstr, "DataFieldName"); + strcat(utlstr, "="); + metaptrs[0] = strstr(metaptrs[0], utlstr); + EHgetmetavalue(metaptrs, "DataFieldName", utlstr); + } + + /* Strip off double quotes */ + /* ----------------------- */ + REMQUOTE + + + /* Add to fieldlist */ + /* ---------------- */ + if (nFld > 0) + { + strcat(fieldlist, ","); + } + strcat(fieldlist, utlstr); + + } + /* Get Numbertype */ + if (numbertype != NULL) + { + EHgetmetavalue(metaptrs, "DataType", utlstr); + numbertype[nFld] = EHnumstr(utlstr); + } + /* + * Get Rank (if desired) by counting # of dimensions in + * "DimList" string + */ + if (rank != NULL) + { + EHgetmetavalue(metaptrs, "DimList", utlstr); + rank[nFld] = EHparsestr(utlstr, ',', ptr, slen); + } + /* Increment number of fields */ + nFld++; + } + else + /* No more fields found */ + { + break; + } + } + free(metabuf); + } + } + + /* Set nFld to -1 if error status exists */ + /* ------------------------------------- */ + if (status == -1) + { + nFld = -1; + } + free(utlstr); + return (nFld); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDnentries | +| | +| DESCRIPTION: Returns number of entries and descriptive string buffer | +| size for a specified entity. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nEntries int32 Number of entries | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| entrycode int32 Entry code | +| HDFE_NENTDIM (0) | +| HDFE_NENTDFLD (4) | +| | +| | +| OUTPUTS: | +| strbufsize int32 Length of comma-separated list | +| (Does not include null-terminator | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Feb 97 Joel Gales Set nEntries to -1 if status = -1 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDnentries(int32 gridID, int32 entrycode, int32 * strbufsize) + +{ + intn status; /* routine return status variable */ + intn i; /* Loop index */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + int32 nEntries = 0; /* Number of entries */ + int32 metaflag; /* Old (0), New (1) metadata flag) */ + int32 nVal = 0; /* Number of strings to search for */ + + char *metabuf = NULL; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char gridname[80]; /* Grid Name */ + char *utlstr;/* Utility string */ + char valName[2][32]; /* Strings to search for */ + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"GDnentries", __FILE__, __LINE__); + return(-1); + } + status = GDchkgdid(gridID, "GDnentries", &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + /* Get grid name */ + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + + /* Zero out string buffer size */ + *strbufsize = 0; + + + /* + * Get pointer to relevant section within SM and Get names of + * metadata strings to inquire about + */ + switch (entrycode) + { + case HDFE_NENTDIM: + { + metabuf = (char *) EHmetagroup(sdInterfaceID, gridname, "g", + "Dimension", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + nVal = 1; + strcpy(&valName[0][0], "DimensionName"); + } + break; + + case HDFE_NENTDFLD: + { + metabuf = (char *) EHmetagroup(sdInterfaceID, gridname, "g", + "DataField", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + nVal = 1; + strcpy(&valName[0][0], "DataFieldName"); + } + break; + } + + + /* + * Check for presence of 'GROUP="' string If found then old metadata, + * search on OBJECT string + */ + metaflag = (strstr(metabuf, "GROUP=\"") == NULL) ? 1 : 0; + if (metaflag == 0) + { + nVal = 1; + strcpy(&valName[0][0], "\t\tOBJECT"); + } + + + /* Begin loop through entries in metadata */ + /* -------------------------------------- */ + while (1) + { + /* Search for first string */ + strcpy(utlstr, &valName[0][0]); + strcat(utlstr, "="); + metaptrs[0] = strstr(metaptrs[0], utlstr); + + /* If found within relevant metadata section ... */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + for (i = 0; i < nVal; i++) + { + /* + * Get all string values Don't count quotes + */ + EHgetmetavalue(metaptrs, &valName[i][0], utlstr); + *strbufsize += strlen(utlstr) - 2; + } + /* Increment number of entries */ + nEntries++; + + /* Go to end of OBJECT */ + metaptrs[0] = strstr(metaptrs[0], "END_OBJECT"); + } + else + /* No more entries found */ + { + break; + } + } + free(metabuf); + + + /* Count comma separators & slashes (if mappings) */ + /* ---------------------------------------------- */ + if (nEntries > 0) + { + *strbufsize += nEntries - 1; + *strbufsize += (nVal - 1) * nEntries; + } + } + + + /* Set nEntries to -1 if error status exists */ + /* ----------------------------------------- */ + if (status == -1) + { + nEntries = -1; + } + + free(utlstr); + return (nEntries); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDinqgrid | +| | +| DESCRIPTION: Returns number and names of grid structures in file | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nGrid int32 Number of grid structures in file | +| | +| INPUTS: | +| filename char HDF-EOS filename | +| | +| OUTPUTS: | +| gridlist char List of grid names (comma-separated) | +| strbufsize int32 Length of gridlist | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDinqgrid(char *filename, char *gridlist, int32 * strbufsize) +{ + int32 nGrid; /* Number of grid structures in file */ + + /* Call "EHinquire" routine */ + /* ------------------------ */ + nGrid = EHinquire(filename, "GRID", gridlist, strbufsize); + + return (nGrid); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDsetfillvalue | +| | +| DESCRIPTION: Sets fill value for the specified field. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| fieldname char field name | +| fillval void fill value | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDsetfillvalue(int32 gridID, char *fieldname, VOIDP fillval) +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 sdid; /* SDS id */ + int32 nt; /* Number type */ + int32 dims[8]; /* Dimensions array */ + int32 dum; /* Dummy variable */ + int32 solo; /* "Solo" (non-merged) field flag */ + + char name[80]; /* Fill value "attribute" name */ + + /* Check for valid grid ID and get SDS interface ID */ + status = GDchkgdid(gridID, "GDsetfillvalue", + &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + /* Get field info */ + status = GDfieldinfo(gridID, fieldname, &dum, dims, &nt, NULL); + + if (status == 0) + { + /* Get SDS ID and solo flag */ + status = GDSDfldsrch(gridID, sdInterfaceID, fieldname, + &sdid, &dum, &dum, &dum, + dims, &solo); + + /* If unmerged field then call HDF set field routine */ + if (solo == 1) + { + status = SDsetfillvalue(sdid, fillval); + } + + /* + * Store fill value in attribute. Name is given by fieldname + * prepended with "_FV_" + */ + strcpy(name, "_FV_"); + strcat(name, fieldname); + status = GDwriteattr(gridID, name, nt, 1, fillval); + + + } + else + { + HEpush(DFE_GENAPP, "GDsetfillvalue", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" does not exist.\n", fieldname); + } + } + return (status); +} + + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDgetfillvalue | +| | +| DESCRIPTION: Retrieves fill value for a specified field. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| fieldname char field name | +| | +| OUTPUTS: | +| fillval void fill value | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDgetfillvalue(int32 gridID, char *fieldname, VOIDP fillval) +{ + intn status; /* routine return status variable */ + + int32 nt; /* Number type */ + int32 dims[8]; /* Dimensions array */ + int32 dum; /* Dummy variable */ + + char name[80]; /* Fill value "attribute" name */ + + status = GDchkgdid(gridID, "GDgetfillvalue", &dum, &dum, &dum); + + /* Check for valid grid ID */ + if (status == 0) + { + /* Get field info */ + status = GDfieldinfo(gridID, fieldname, &dum, dims, &nt, NULL); + + if (status == 0) + { + /* Read fill value attribute */ + strcpy(name, "_FV_"); + strcat(name, fieldname); + status = GDreadattr(gridID, name, fillval); + } + else + { + HEpush(DFE_GENAPP, "GDgetfillvalue", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" does not exist.\n", fieldname); + } + + } + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdetach | +| | +| DESCRIPTION: Detaches from grid interface and performs file housekeeping. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Sep 96 Joel Gales Setup dim names for SDsetdimname in dimbuf1 rather | +| that utlstr | +| Oct 96 Joel Gales Detach Grid Vgroups | +| Oct 96 Joel Gales "Detach" from SDS | +| Nov 96 Joel Gales Call GDchkgdid to check for proper grid ID | +| Dec 96 Joel Gales Add multiple vertical subsetting garbage collection | +| Oct 98 Abe Taaheri Added GDXRegion[k]->DimNamePtr[i] =0; after freeing | +| memory | +| Sep 99 Abe Taaheri Changed memcpy to memmove because of overlapping | +| source and destination for GDXSDcomb, nameptr, and | +| dimptr. memcpy may cause unexpected results. | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDdetach(int32 gridID) + +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn k; /* Loop index */ + intn status = 0; /* routine return status variable */ + intn statusFill = 0; /* return status from GDgetfillvalue */ + + int32 *namelen; /* Pointer to name string length array */ + int32 *dimlen; /* Pointer to dim string length array */ + int32 slen1[3]; /* String length array 1 */ + int32 slen2[3]; /* String length array 2 */ + int32 nflds; /* Number of fields */ + int32 match[5]; /* Merged field match array */ + int32 cmbfldcnt; /* Number of fields combined */ + int32 sdid; /* SDS ID */ + int32 vgid; /* Vgroup ID */ + int32 dims[3]; /* Dimension array */ + int32 *offset; /* Pointer to merged field offset array */ + int32 *indvdims; /* Pointer to merged field size array */ + int32 sdInterfaceID; /* SDS interface ID */ + int32 gID; /* Grid ID - offset */ + int32 nflds0; /* Number of fields */ + int32 *namelen0; /* Pointer to name string length array */ + int32 rank; /* Rank of merged field */ + int32 truerank; /* True rank of merged field */ + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + int32 dum; /* Dummy variable */ + + char *nambuf; /* Pointer to name buffer */ + char **nameptr; /* Pointer to name string pointer array */ + char **dimptr; /* Pointer to dim string pointer array */ + char **nameptr0; /* Pointer to name string pointer array */ + char *ptr1[3]; /* String pointer array */ + char *ptr2[3]; /* String pointer array */ + char dimbuf1[128]; /* Dimension buffer 1 */ + char dimbuf2[128]; /* Dimension buffer 2 */ + char gridname[VGNAMELENMAX + 1]; /* Grid name */ + char *utlbuf; /* Utility buffer */ + char fillval[32];/* Fill value buffer */ + + + + status = GDchkgdid(gridID, "GDdetach", &dum, &sdInterfaceID, &dum); + + if (status == 0) + { + gID = gridID % idOffset; + Vgetname(GDXGrid[gID].IDTable, gridname); + + /* SDS combined fields */ + /* ------------------- */ + if (strlen(GDXSDname) == 0) + { + nflds = 0; + + /* Allocate "dummy" arrays so free() doesn't bomb later */ + /* ---------------------------------------------------- */ + nameptr = (char **) calloc(1, sizeof(char *)); + if(nameptr == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + return(-1); + } + namelen = (int32 *) calloc(1, sizeof(int32)); + if(namelen == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + return(-1); + } + nameptr0 = (char **) calloc(1, sizeof(char *)); + if(nameptr0 == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + return(-1); + } + namelen0 = (int32 *) calloc(1, sizeof(int32)); + if(namelen0 == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + return(-1); + } + dimptr = (char **) calloc(1, sizeof(char *)); + if(dimptr == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + return(-1); + } + dimlen = (int32 *) calloc(1, sizeof(int32)); + if(dimlen == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + return(-1); + } + offset = (int32 *) calloc(1, sizeof(int32)); + if(offset == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + free(dimlen); + return(-1); + } + indvdims = (int32 *) calloc(1, sizeof(int32)); + if(indvdims == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + free(dimlen); + free(offset); + return(-1); + } + } + else + { + /* + * "Trim Off" trailing "," and ";" in GDXSDname & GDXSDdims + * respectively + */ + GDXSDname[strlen(GDXSDname) - 1] = 0; + GDXSDdims[strlen(GDXSDdims) - 1] = 0; + + + /* Get number of fields from GDXSDname string */ + /* ------------------------------------------ */ + nflds = EHparsestr(GDXSDname, ',', NULL, NULL); + + /* Allocate space for various dynamic arrays */ + /* ----------------------------------------- */ + nameptr = (char **) calloc(nflds, sizeof(char *)); + if(nameptr == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + return(-1); + } + namelen = (int32 *) calloc(nflds, sizeof(int32)); + if(namelen == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + return(-1); + } + nameptr0 = (char **) calloc(nflds, sizeof(char *)); + if(nameptr0 == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + return(-1); + } + namelen0 = (int32 *) calloc(nflds, sizeof(int32)); + if(namelen0 == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + return(-1); + } + dimptr = (char **) calloc(nflds, sizeof(char *)); + if(dimptr == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + return(-1); + } + dimlen = (int32 *) calloc(nflds, sizeof(int32)); + if(dimlen == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + return(-1); + } + offset = (int32 *) calloc(nflds, sizeof(int32)); + if(offset == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + free(dimlen); + return(-1); + } + indvdims = (int32 *) calloc(nflds, sizeof(int32)); + if(indvdims == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + free(dimlen); + free(offset); + return(-1); + } + + /* Parse GDXSDname and GDXSDdims strings */ + /* ------------------------------------- */ + nflds = EHparsestr(GDXSDname, ',', nameptr, namelen); + nflds = EHparsestr(GDXSDdims, ';', dimptr, dimlen); + } + + + for (i = 0; i < nflds; i++) + { + if (GDXSDcomb[5 * i] != 0 && + GDXSDcomb[5 * i + 3] == GDXGrid[gID].IDTable) + { + nambuf = (char *) calloc(strlen(GDXSDname) + 1, 1); + if(nambuf == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + free(dimlen); + free(offset); + return(-1); + } + utlbuf = (char *) calloc(strlen(GDXSDname) * 2 + 7, 1); + if(utlbuf == NULL) + { + HEpush(DFE_NOSPACE,"GDdetach", __FILE__, __LINE__); + free(nambuf); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + free(dimlen); + free(offset); + return(-1); + } + + for (k = 0; k < sizeof(dimbuf1); k++) + dimbuf1[k] = 0; + + + /* Load array to match, name & parse dims */ + /* -------------------------------------- */ + memcpy(match, &GDXSDcomb[5 * i], 20); + memcpy(nambuf, nameptr[i], namelen[i]); + + memcpy(dimbuf1, dimptr[i], dimlen[i]); + dum = EHparsestr(dimbuf1, ',', ptr1, slen1); + + + /* Separate combined dimension from others */ + /* --------------------------------------- */ + dimbuf1[slen1[0]] = 0; + + offset[0] = 0; + indvdims[0] = abs(match[0]); + + for (j = i + 1, cmbfldcnt = 0; j < nflds; j++) + { + for (k = 0; k < sizeof(dimbuf2); k++) + dimbuf2[k] = 0; + memcpy(dimbuf2, dimptr[j], dimlen[j]); + dum = EHparsestr(dimbuf2, ',', ptr2, slen2); + dimbuf2[slen2[0]] = 0; + + + if (GDXSDcomb[5 * j] != 0 && + strcmp(dimbuf1 + slen1[0], + dimbuf2 + slen2[0]) == 0 && + match[1] == GDXSDcomb[5 * j + 1] && + match[2] == GDXSDcomb[5 * j + 2] && + match[3] == GDXSDcomb[5 * j + 3] && + match[4] == GDXSDcomb[5 * j + 4]) + { + /* Add to combined dimension size */ + match[0] += GDXSDcomb[5 * j]; + + /* Concatanate name */ + strcat(nambuf, ","); + memcpy(nambuf + strlen(nambuf), + nameptr[j], namelen[j]); + + /* Store individual dims and dim offsets */ + cmbfldcnt++; + indvdims[cmbfldcnt] = abs(GDXSDcomb[5 * j]); + offset[cmbfldcnt] = + offset[cmbfldcnt - 1] + indvdims[cmbfldcnt - 1]; + + GDXSDcomb[5 * j] = 0; + } + } + + + /* Create SDS */ + /* ---------- */ + nflds0 = EHparsestr(nambuf, ',', nameptr0, namelen0); + + if (abs(match[0]) == 1) + { + for (k = 0; k < 2; k++) + dims[k] = abs(match[k + 1]); + + rank = 2; + + sdid = SDcreate(sdInterfaceID, nambuf, + GDXSDcomb[5 * i + 4], 2, dims); + } + else + { + for (k = 0; k < 3; k++) + dims[k] = abs(match[k]); + + rank = 3; + + if (cmbfldcnt > 0) + { + strcpy(utlbuf, "MRGFLD_"); + memcpy(utlbuf + 7, nameptr0[0], namelen0[0]); + utlbuf[7 + namelen0[0]] = 0; + strcat(utlbuf, ":"); + strcat(utlbuf, nambuf); + + status = EHinsertmeta(sdInterfaceID, gridname, "g", + 6L, utlbuf, NULL); + } + else + { + strcpy(utlbuf, nambuf); + } + + sdid = SDcreate(sdInterfaceID, utlbuf, + GDXSDcomb[5 * i + 4], 3, dims); + + + if (cmbfldcnt > 0) + { + SDsetattr(sdid, "Field Dims", DFNT_INT32, + cmbfldcnt + 1, (VOIDP) indvdims); + + SDsetattr(sdid, "Field Offsets", DFNT_INT32, + cmbfldcnt + 1, (VOIDP) offset); + } + + } + + + + /* Register Dimensions in SDS */ + /* -------------------------- */ + for (k = 0; k < rank; k++) + { + if (rank == 2) + { + memcpy(dimbuf2, ptr1[k + 1], slen1[k + 1]); + dimbuf2[slen1[k + 1]] = 0; + } + else + { + memcpy(dimbuf2, ptr1[k], slen1[k]); + dimbuf2[slen1[k]] = 0; + } + + + if (k == 0 && rank > 2 && cmbfldcnt > 0) + { + sprintf(dimbuf2, "%s%s_%d", "MRGDIM:", + gridname, (int)dims[0]); + } + else + { + strcat(dimbuf2, ":"); + strcat(dimbuf2, gridname); + } + SDsetdimname(SDgetdimid(sdid, k), (char *) dimbuf2); + } + + + + /* Write Fill Value */ + /* ---------------- */ + for (k = 0; k < nflds0; k++) + { + memcpy(utlbuf, nameptr0[k], namelen0[k]); + utlbuf[namelen[k]] = 0; + statusFill = GDgetfillvalue(gridID, utlbuf, fillval); + + if (statusFill == 0) + { + if (cmbfldcnt > 0) + { + dims[0] = indvdims[k]; + truerank = (dims[0] == 1) ? 2 : 3; + EHfillfld(sdid, rank, truerank, + DFKNTsize(match[4]), offset[k], + dims, fillval); + } + else + { + status = SDsetfillvalue(sdid, fillval); + } + } + } + + + vgid = GDXGrid[gID].VIDTable[0]; + Vaddtagref(vgid, DFTAG_NDG, SDidtoref(sdid)); + SDendaccess(sdid); + + free(nambuf); + free(utlbuf); + + } + } + + + for (i = 0; i < nflds; i++) + { + if (GDXSDcomb[5 * i + 3] == GDXGrid[gID].IDTable) + { + if (i == (nflds - 1)) + { + GDXSDcomb[5 * i] = 0; + *(nameptr[i] - (nflds != 1)) = 0; + *(dimptr[i] - (nflds != 1)) = 0; + } + else + { + /* memcpy(&GDXSDcomb[5 * i], + &GDXSDcomb[5 * (i + 1)], + (512 - i - 1) * 5 * 4);*/ + memmove(&GDXSDcomb[5 * i], + &GDXSDcomb[5 * (i + 1)], + (512 - i - 1) * 5 * 4); + /* memcpy(nameptr[i], + nameptr[i + 1], + nameptr[0] + 2048 - nameptr[i + 1] - 1);*/ + memmove(nameptr[i], + nameptr[i + 1], + nameptr[0] + 2048 - nameptr[i + 1] - 1); + /* memcpy(dimptr[i], + dimptr[i + 1], + dimptr[0] + 2048 * 2 - dimptr[i + 1] - 1);*/ + memmove(dimptr[i], + dimptr[i + 1], + dimptr[0] + 2048 * 2 - dimptr[i + 1] - 1); + } + + i--; + nflds = EHparsestr(GDXSDname, ',', nameptr, namelen); + nflds = EHparsestr(GDXSDdims, ';', dimptr, dimlen); + } + } + + if (nflds != 0) + { + strcat(GDXSDname, ","); + strcat(GDXSDdims, ";"); + } + + + + /* Free up a bunch of dynamically allocated arrays */ + /* ----------------------------------------------- */ + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + free(dimlen); + free(offset); + free(indvdims); + + + + /* "Detach" from previously attached SDSs */ + /* -------------------------------------- */ + for (k = 0; k < GDXGrid[gID].nSDS; k++) + { + SDendaccess(GDXGrid[gID].sdsID[k]); + } + free(GDXGrid[gID].sdsID); + GDXGrid[gID].sdsID = 0; + GDXGrid[gID].nSDS = 0; + + + + /* Detach Grid Vgroups */ + /* ------------------- */ + Vdetach(GDXGrid[gID].VIDTable[0]); + Vdetach(GDXGrid[gID].VIDTable[1]); + Vdetach(GDXGrid[gID].IDTable); + + GDXGrid[gID].active = 0; + GDXGrid[gID].VIDTable[0] = 0; + GDXGrid[gID].VIDTable[1] = 0; + GDXGrid[gID].IDTable = 0; + GDXGrid[gID].fid = 0; + + + + + /* Free Region Pointers */ + /* -------------------- */ + for (k = 0; k < NGRIDREGN; k++) + { + if (GDXRegion[k] != 0 && + GDXRegion[k]->gridID == gridID) + { + for (i = 0; i < 8; i++) + { + if (GDXRegion[k]->DimNamePtr[i] != 0) + { + free(GDXRegion[k]->DimNamePtr[i]); + GDXRegion[k]->DimNamePtr[i] = 0; + } + } + + free(GDXRegion[k]); + GDXRegion[k] = 0; + } + } + } + return (status); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDclose | +| | +| DESCRIPTION: Closes file. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| fid int32 File ID | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDclose(int32 fid) + +{ + intn status = 0; /* routine return status variable */ + + /* Call EHclose to perform file close */ + /* ---------------------------------- */ + status = EHclose(fid); + + return (status); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDgetdefaults | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| projcode int32 GCTP projection code | +| zonecode int32 UTM zone code | +| projparm float64 Projection parameters | +| spherecode int32 GCTP spheriod code | +| upleftpt float64 upper left corner coordinates | +| lowrightpt float64 lower right corner coordinates | +| | +| | +| OUTPUTS: | +| upleftpt float64 upper left corner coordinates | +| lowrightpt float64 lower right corner coordinates | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Aug 96 Joel Gales Original Programmer | +| Sep 96 Raj Gejjaga Fixed bugs in Polar Stereographic and Goode | | Homolosine default calculations. | +| Sep 96 Raj Gejjaga Added code to compute default boundary points | +| for Lambert Azimuthal Polar and Equitorial | +| projections. | +| Feb 97 Raj Gejjaga Added code to compute default boundary points | +| for Integerized Sinusoidal Grid. Added error | +| handling code. | +| Jun 00 Abe Taaheri Added support for EASE grid | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +GDgetdefaults(int32 projcode, int32 zonecode, float64 projparm[], + int32 spherecode, float64 upleftpt[], float64 lowrightpt[]) +{ + int32 errorcode = 0, status = 0; + int32(*for_trans[100]) (); + + float64 lon, lat, plat, x, y; + float64 plon, tlon, llon, rlon, pplon, LLon, LLat, RLon, RLat; + + + /* invoke GCTP initialization routine */ + /* ---------------------------------- */ + for_init(projcode, zonecode, projparm, spherecode, NULL, NULL, + &errorcode, for_trans); + + /* Report error if any */ + /* ------------------- */ + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + /* Compute Default Boundary Points for EASE Grid */ + /* Use Global coverage */ + /* ------------------------------------------------------ */ + if (projcode == GCTP_BCEA && + upleftpt[0] == 0 && upleftpt[1] == 0 && + lowrightpt[0] == 0 && lowrightpt[1] == 0) + { + upleftpt[0] = EHconvAng(EASE_GRID_DEFAULT_UPLEFT_LON, HDFE_DEG_DMS); + upleftpt[1] = EHconvAng(EASE_GRID_DEFAULT_UPLEFT_LAT, HDFE_DEG_DMS); + lowrightpt[0] = EHconvAng(EASE_GRID_DEFAULT_LOWRGT_LON, HDFE_DEG_DMS); + lowrightpt[1] = EHconvAng(EASE_GRID_DEFAULT_LOWRGT_LAT, HDFE_DEG_DMS); + } + +/* Compute Default Boundary Points for CEA */ + /* --------------------------------------------*/ + if (projcode == GCTP_CEA && + upleftpt[0] == 0 && upleftpt[1] == 0 && + lowrightpt[0] == 0 && lowrightpt[1] == 0) + { + LLon = EHconvAng(EASE_GRID_DEFAULT_UPLEFT_LON, HDFE_DEG_RAD); + LLat = EHconvAng(EASE_GRID_DEFAULT_UPLEFT_LAT, HDFE_DEG_RAD); + RLon = EHconvAng(EASE_GRID_DEFAULT_LOWRGT_LON, HDFE_DEG_RAD); + RLat = EHconvAng(EASE_GRID_DEFAULT_LOWRGT_LAT, HDFE_DEG_RAD); + + errorcode = for_trans[projcode] (LLon, LLat, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + upleftpt[0] = x; + upleftpt[1] = y; + + errorcode = for_trans[projcode] (RLon, RLat, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + lowrightpt[0] = x; + lowrightpt[1] = y; + + } + + + /* Compute Default Boundary Points for Polar Sterographic */ + /* ------------------------------------------------------ */ + if (projcode == GCTP_PS && + upleftpt[0] == 0 && upleftpt[1] == 0 && + lowrightpt[0] == 0 && lowrightpt[1] == 0) + { + /* + * Convert the longitude and latitude from the DMS to decimal degree + * format. + */ + plon = EHconvAng(projparm[4], HDFE_DMS_DEG); + plat = EHconvAng(projparm[5], HDFE_DMS_DEG); + + /* + * Compute the longitudes at 90, 180 and 270 degrees from the central + * longitude. + */ + + if (plon <= 0.0) + { + tlon = 180.0 + plon; + pplon = plon + 360.0; + } + else + { + tlon = plon - 180.0; + pplon = plon; + } + + rlon = pplon + 90.0; + if (rlon > 360.0) + rlon = rlon - 360; + + if (rlon > 180.0) + rlon = rlon - 360.0; + + if (rlon <= 0.0) + llon = 180.0 + rlon; + else + llon = rlon - 180.0; + + + /* Convert all four longitudes from decimal degrees to radians */ + plon = EHconvAng(plon, HDFE_DEG_RAD); + tlon = EHconvAng(tlon, HDFE_DEG_RAD); + llon = EHconvAng(llon, HDFE_DEG_RAD); + rlon = EHconvAng(rlon, HDFE_DEG_RAD); + + errorcode = for_trans[projcode] (llon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + upleftpt[0] = x; + + errorcode = for_trans[projcode] (rlon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + lowrightpt[0] = x; + + /* + * Compute the upperleft and lowright y values based on the south or + * north polar projection + */ + + if (plat < 0.0) + { + errorcode = for_trans[projcode] (plon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + upleftpt[1] = y; + + errorcode = for_trans[projcode] (tlon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + lowrightpt[1] = y; + + } + else + { + errorcode = for_trans[projcode] (tlon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + upleftpt[1] = y; + + errorcode = for_trans[projcode] (plon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + lowrightpt[1] = y; + + } + } + + + /* Compute Default Boundary Points for Goode Homolosine */ + /* ---------------------------------------------------- */ + if (projcode == GCTP_GOOD && + upleftpt[0] == 0 && upleftpt[1] == 0 && + lowrightpt[0] == 0 && lowrightpt[1] == 0) + { + lon = EHconvAng(-180, HDFE_DEG_RAD); + lat = 0.0; + + errorcode = for_trans[projcode] (lon, lat, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + upleftpt[0] = -fabs(x); + lowrightpt[0] = +fabs(x); + + lat = EHconvAng(90, HDFE_DEG_RAD); + + errorcode = for_trans[projcode] (lon, lat, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + upleftpt[1] = +fabs(y); + lowrightpt[1] = -fabs(y); + } + + /* Compute Default Boundary Points for Lambert Azimuthal */ + /* ----------------------------------------------------- */ + if (projcode == GCTP_LAMAZ && + upleftpt[0] == 0 && upleftpt[1] == 0 && + lowrightpt[0] == 0 && lowrightpt[1] == 0) + { + /* + * Convert the longitude and latitude from the DMS to decimal degree + * format. + */ + plon = EHconvAng(projparm[4], HDFE_DMS_DEG); + plat = EHconvAng(projparm[5], HDFE_DMS_DEG); + + /* + * Compute the longitudes at 90, 180 and 270 degrees from the central + * longitude. + */ + + if (plon <= 0.0) + { + tlon = 180.0 + plon; + pplon = plon + 360.0; + } + else + { + tlon = plon - 180.0; + pplon = plon; + } + + rlon = pplon + 90.0; + if (rlon > 360.0) + rlon = rlon - 360; + + if (rlon > 180.0) + rlon = rlon - 360.0; + + if (rlon <= 0.0) + llon = 180.0 + rlon; + else + llon = rlon - 180.0; + + /* Convert all four longitudes from decimal degrees to radians */ + plon = EHconvAng(plon, HDFE_DEG_RAD); + tlon = EHconvAng(tlon, HDFE_DEG_RAD); + llon = EHconvAng(llon, HDFE_DEG_RAD); + rlon = EHconvAng(rlon, HDFE_DEG_RAD); + + errorcode = for_trans[projcode] (llon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + upleftpt[0] = x; + + errorcode = for_trans[projcode] (rlon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + lowrightpt[0] = x; + + /* + * Compute upperleft and lowerright values based on whether the + * projection is south polar, north polar or equitorial + */ + + if (plat == -90.0) + { + errorcode = for_trans[projcode] (plon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + upleftpt[1] = y; + + errorcode = for_trans[projcode] (tlon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + lowrightpt[1] = y; + } + else if (plat == 90.0) + { + errorcode = for_trans[projcode] (tlon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + upleftpt[1] = y; + + errorcode = for_trans[projcode] (plon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + lowrightpt[1] = y; + } + else + { + lat = EHconvAng(90, HDFE_DEG_RAD); + errorcode = for_trans[projcode] (plon, lat, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + upleftpt[1] = y; + + lat = EHconvAng(-90, HDFE_DEG_RAD); + errorcode = for_trans[projcode] (plon, lat, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + lowrightpt[1] = y; + } + } + + /* Compute Default Boundary Points for Integerized Sinusoidal Grid */ + /* --------------------------------------------------------------- */ + if (((projcode == GCTP_ISINUS) || (projcode == GCTP_ISINUS1)) && + upleftpt[0] == 0 && upleftpt[1] == 0 && + lowrightpt[0] == 0 && lowrightpt[1] == 0) + { + /* + * Convert the longitude and latitude from the DMS to decimal degree + * format. + */ + plon = EHconvAng(projparm[4], HDFE_DMS_DEG); + plat = EHconvAng(projparm[5], HDFE_DMS_DEG); + + /* + * Compute the longitudes at 90, 180 and 270 degrees from the central + * longitude. + */ + + if (plon <= 0.0) + { + tlon = 180.0 + plon; + pplon = plon + 360.0; + } + else + { + tlon = plon - 180.0; + pplon = plon; + } + + rlon = pplon + 90.0; + if (rlon > 360.0) + rlon = rlon - 360; + + if (rlon > 180.0) + rlon = rlon - 360.0; + + if (rlon <= 0.0) + llon = 180.0 + rlon; + else + llon = rlon - 180.0; + + /* Convert all four longitudes from decimal degrees to radians */ + plon = EHconvAng(plon, HDFE_DEG_RAD); + tlon = EHconvAng(tlon, HDFE_DEG_RAD); + llon = EHconvAng(llon, HDFE_DEG_RAD); + rlon = EHconvAng(rlon, HDFE_DEG_RAD); + + errorcode = for_trans[projcode] (llon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + upleftpt[0] = x; + + errorcode = for_trans[projcode] (rlon, 0.0, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + lowrightpt[0] = x; + + lat = EHconvAng(90, HDFE_DEG_RAD); + errorcode = for_trans[projcode] (plon, lat, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + upleftpt[1] = y; + + lat = EHconvAng(-90, HDFE_DEG_RAD); + errorcode = for_trans[projcode] (plon, lat, &x, &y); + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetdefaults", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + lowrightpt[1] = y; + } + return (errorcode); +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDll2ij | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| projcode int32 GCTP projection code | +| zonecode int32 UTM zone code | +| projparm float64 Projection parameters | +| spherecode int32 GCTP spheriod code | +| xdimsize int32 xdimsize from GDcreate | +| ydimsize int32 ydimsize from GDcreate | +| upleftpt float64 upper left corner coordinates | +| lowrightpt float64 lower right corner coordinates | +| npnts int32 number of lon-lat points | +| longitude float64 longitude array (radians) | +| latitude float64 latitude array (radians) | +| | +| OUTPUTS: | +| row int32 Row array | +| col int32 Column array | +| xval float64 X value array | +| yval float64 Y value array | +| | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Return x and y values if requested | +| Jun 00 Abe Taaheri Added support for EASE grid | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +GDll2ij(int32 projcode, int32 zonecode, float64 projparm[], + int32 spherecode, int32 xdimsize, int32 ydimsize, + float64 upleftpt[], float64 lowrightpt[], + int32 npnts, float64 longitude[], float64 latitude[], + int32 row[], int32 col[], float64 xval[], float64 yval[]) + + +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 errorcode = 0; /* GCTP error code */ + int32(*for_trans[100]) (); /* GCTP function pointer */ + + float64 xVal; /* Scaled x distance */ + float64 yVal; /* Scaled y distance */ + float64 xMtr; /* X value in meters from GCTP */ + float64 yMtr; /* Y value in meters from GCTP */ + float64 lonrad0; /* Longitude in radians of upleft point */ + float64 latrad0 = 0; /* Latitude in radians of upleft point */ + float64 lonrad; /* Longitude in radians of point */ + float64 latrad; /* Latitude in radians of point */ + float64 scaleX; /* X scale factor */ + float64 scaleY; /* Y scale factor */ + float64 EHconvAng();/* Angle conversion routine */ + float64 xMtr0, xMtr1, yMtr0, yMtr1; + float64 lonrad1; /* Longitude in radians of lowright point */ + + /* If projection not GEO call GCTP initialization routine */ + /* ------------------------------------------------------ */ + if (projcode != GCTP_GEO) + { + for_init(projcode, zonecode, projparm, spherecode, NULL, NULL, + &errorcode, for_trans); + + /* Report error if any */ + /* ------------------- */ + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDll2ij", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + } + } + + + if (status == 0) + { + /* GEO projection */ + /* -------------- */ + if (projcode == GCTP_GEO) + { + /* Convert upleft and lowright X coords from DMS to radians */ + /* -------------------------------------------------------- */ + lonrad0 = EHconvAng(upleftpt[0], HDFE_DMS_RAD); + lonrad = EHconvAng(lowrightpt[0], HDFE_DMS_RAD); + + /* Compute x scale factor */ + /* ---------------------- */ + scaleX = (lonrad - lonrad0) / xdimsize; + + + /* Convert upleft and lowright Y coords from DMS to radians */ + /* -------------------------------------------------------- */ + latrad0 = EHconvAng(upleftpt[1], HDFE_DMS_RAD); + latrad = EHconvAng(lowrightpt[1], HDFE_DMS_RAD); + + + /* Compute y scale factor */ + /* ---------------------- */ + scaleY = (latrad - latrad0) / ydimsize; + } + + /* BCEA projection */ + /* -------------- */ + else if ( projcode == GCTP_BCEA) + { + /* Convert upleft and lowright X coords from DMS to radians */ + /* -------------------------------------------------------- */ + + lonrad0 = EHconvAng(upleftpt[0], HDFE_DMS_RAD); + lonrad = EHconvAng(lowrightpt[0], HDFE_DMS_RAD); + + /* Convert upleft and lowright Y coords from DMS to radians */ + /* -------------------------------------------------------- */ + latrad0 = EHconvAng(upleftpt[1], HDFE_DMS_RAD); + latrad = EHconvAng(lowrightpt[1], HDFE_DMS_RAD); + + /* Convert from lon/lat to meters(or whatever unit is, i.e unit + of r_major and r_minor) using GCTP */ + /* ----------------------------------------- */ + errorcode = for_trans[projcode] (lonrad0, latrad0, &xMtr0, &yMtr0); + + + /* Report error if any */ + /* ------------------- */ + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDll2ij", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + /* Convert from lon/lat to meters(or whatever unit is, i.e unit + of r_major and r_minor) using GCTP */ + /* ----------------------------------------- */ + errorcode = for_trans[projcode] (lonrad, latrad, &xMtr1, &yMtr1); + + + /* Report error if any */ + /* ------------------- */ + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDll2ij", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + /* Compute x scale factor */ + /* ---------------------- */ + scaleX = (xMtr1 - xMtr0) / xdimsize; + + /* Compute y scale factor */ + /* ---------------------- */ + scaleY = (yMtr1 - yMtr0) / ydimsize; + } + else + { + /* Non-GEO, Non_BCEA projections */ + /* ---------------------------- */ + + /* Compute x & y scale factors */ + /* --------------------------- */ + scaleX = (lowrightpt[0] - upleftpt[0]) / xdimsize; + scaleY = (lowrightpt[1] - upleftpt[1]) / ydimsize; + } + + + + /* Loop through all points */ + /* ----------------------- */ + for (i = 0; i < npnts; i++) + { + /* Convert lon & lat from decimal degrees to radians */ + /* ------------------------------------------------- */ + lonrad = EHconvAng(longitude[i], HDFE_DEG_RAD); + latrad = EHconvAng(latitude[i], HDFE_DEG_RAD); + + + /* GEO projection */ + /* -------------- */ + if (projcode == GCTP_GEO) + { + /*allow map to span dateline */ + lonrad0 = EHconvAng(upleftpt[0], HDFE_DMS_RAD); + lonrad1 = EHconvAng(lowrightpt[0], HDFE_DMS_RAD); + /* if time-line is paased */ + if(lonrad < lonrad1) + { + if (lonrad < lonrad0) lonrad += 2.0 * M_PI1; + if (lonrad > lonrad1) lonrad -= 2.0 * M_PI1; + } + + /* Compute scaled distance to point from origin */ + /* -------------------------------------------- */ + xVal = (lonrad - lonrad0) / scaleX; + yVal = (latrad - latrad0) / scaleY; + } + else + { + /* Convert from lon/lat to meters using GCTP */ + /* ----------------------------------------- */ + errorcode = for_trans[projcode] (lonrad, latrad, &xMtr, &yMtr); + + + /* Report error if any */ + /* ------------------- */ + if (errorcode != 0) + { + /*status = -1; + HEpush(DFE_GENAPP, "GDll2ij", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); */ /* Bruce Beaumont */ + xVal = -2147483648.0; /* Bruce Beaumont */ + yVal = -2147483648.0; /* Bruce Beaumont */ + }/* (Note: MAXLONG is defined as 2147483647.0 in + function cproj.c of GCTP) */ + else { + /* if projection is BCEA normalize x and y by cell size and + measure it from the uperleft corner of the grid */ + + /* Compute scaled distance to point from origin */ + /* -------------------------------------------- */ + if( projcode == GCTP_BCEA) + { + xVal = (xMtr - xMtr0) / scaleX; + yVal = (yMtr - yMtr0) / scaleY; + } + else + { + xVal = (xMtr - upleftpt[0]) / scaleX; + yVal = (yMtr - upleftpt[1]) / scaleY; + } + } + } + + + /* Compute row and col from scaled distance */ + /* ---------------------------------------- */ + col[i] = (int32) xVal; + row[i] = (int32) yVal; + + /* Store scaled distances if requested */ + /* ----------------------------------- */ + if (xval != NULL) + { + xval[i] = xVal; + } + + if (yval != NULL) + { + yval[i] = yVal; + } + } + } + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDrs2ll | +| | +| DESCRIPTION: Converts EASE grid's (r,s) coordinates to longitude and | +| latritude (in decimal degrees). | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| projcode int32 GCTP projection code | +| projparm float64 Projection parameters | +| xdimsize int32 xdimsize from GDcreate | +| ydimsize int32 ydimsize from GDcreate | +| pixcen int32 pixel center code | +| npnts int32 number of lon-lat points | +| s int32 s coordinate | +| r int32 r coordinate | +| pixcen int32 Code from GDpixreginfo | +| pixcnr int32 Code from GDorigininfo | +| upleft float64 upper left corner coordinates (DMS) | +| lowright float64 lower right corner coordinates (DMS) | +| | +| | +| OUTPUTS: | +| longitude float64 longitude array (decimal degrees) | +| latitude float64 latitude array (decimal degrees) | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jul 00 Abe Taaheri Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDrs2ll(int32 projcode, float64 projparm[], + int32 xdimsize, int32 ydimsize, + float64 upleft[], float64 lowright[], + int32 npnts, float64 r[], float64 s[], + float64 longitude[], float64 latitude[], int32 pixcen, int32 pixcnr) +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 errorcode = 0; /* GCTP error code */ + int32(*inv_trans[100]) (); /* GCTP function pointer */ + + float64 pixadjX = 0.0; /* Pixel adjustment (x) */ + float64 pixadjY = 0.0; /* Pixel adjustment (y) */ + float64 lonrad; /* Longitude in radians of point */ + float64 latrad; /* Latitude in radians of point */ + float64 EHconvAng(); /* Angle conversion routine */ + float64 xMtr; /* X value in meters from GCTP */ + float64 yMtr; /* Y value in meters from GCTP */ + float64 epsilon; + float64 beta; + float64 qp_cea = 0; + float64 kz_cea = 0; + float64 eccen, eccen_sq; + float64 phi1, sinphi1, cosphi1; + float64 scaleX, scaleY; + + int32 zonecode=0; + + int32 spherecode=0; + float64 lon[2],lat[2]; + float64 xcor[2], ycor[2]; + int32 nlatlon; + + /* If projection is BCEA define scale, r0 and s0 */ + if (projcode == GCTP_BCEA) + { + eccen_sq = 1.0 - SQUARE(projparm[1]/projparm[0]); + eccen = sqrt(eccen_sq); + if(eccen < 0.00001) + { + qp_cea = 2.0; + } + else + { + qp_cea = + (1.0 - eccen_sq)*((1.0/(1.0 - eccen_sq))-(1.0/(2.0*eccen))* + log((1.0 - eccen)/(1.0 + eccen))); + } + phi1 = EHconvAng(projparm[5],HDFE_DMS_RAD); + cosphi1 = cos(phi1); + sinphi1 = sin(phi1); + kz_cea = cosphi1/(sqrt(1.0 - (eccen_sq*sinphi1*sinphi1))); + } + + + + + /* Compute adjustment of position within pixel */ + /* ------------------------------------------- */ + if (pixcen == HDFE_CENTER) + { + /* Pixel defined at center */ + /* ----------------------- */ + pixadjX = 0.5; + pixadjY = 0.5; + } + else + { + switch (pixcnr) + { + case HDFE_GD_UL: + { + /* Pixel defined at upper left corner */ + /* ---------------------------------- */ + pixadjX = 0.0; + pixadjY = 0.0; + break; + } + + case HDFE_GD_UR: + { + /* Pixel defined at upper right corner */ + /* ----------------------------------- */ + pixadjX = 1.0; + pixadjY = 0.0; + break; + } + + case HDFE_GD_LL: + { + /* Pixel defined at lower left corner */ + /* ---------------------------------- */ + pixadjX = 0.0; + pixadjY = 1.0; + break; + } + + case HDFE_GD_LR: + { + /* Pixel defined at lower right corner */ + /* ----------------------------------- */ + pixadjX = 1.0; + pixadjY = 1.0; + break; + } + + } + } + + /* If projection is BCEA call GCTP initialization routine */ + /* ------------------------------------------------------ */ + if (projcode == GCTP_BCEA) + { + + inv_init(projcode, 0, projparm, 0, NULL, NULL, + &errorcode, inv_trans); + + /* Report error if any */ + /* ------------------- */ + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDrs2ll", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + } + else + { + /* For each point ... */ + /* ------------------ */ + for (i = 0; i < npnts; i++) + { + /* Convert from EASE grid's (r,s) to lon/lat (radians) + using GCTP */ + /* --------------------------------------------------- */ + nlatlon = 2; + lon[0] = upleft[0]; + lon[1] = lowright[0]; + lat[0] = upleft[1]; + lat[1] = lowright[1]; + status = + GDll2mm_cea(projcode,zonecode,spherecode,projparm, + xdimsize, ydimsize, + upleft, lowright, nlatlon, + lon, lat, + xcor, ycor, &scaleX, &scaleY); + + if (status == -1) + { + HEpush(DFE_GENAPP, "GDrs2ll", __FILE__, __LINE__); + return (status); + } + + xMtr = (r[i]/ scaleX + pixadjX - 0.5)* scaleX; + yMtr = - (s[i]/fabs(scaleY) + pixadjY - 0.5)* fabs(scaleY); + + + /* allow .5 cell tolerance in arcsin function + (used in bceainv function) so that grid + coordinates which are less than .5 cells + above 90.00N or below 90.00S are given lat of 90.00 + */ + + epsilon = 1 + 0.5 * (fabs(scaleY)/projparm[0]); + beta = 2.0 * (yMtr - projparm[7]) * kz_cea/(projparm[0] * qp_cea); + + if( fabs (beta) > epsilon) + { + status = -1; + HEpush(DFE_GENAPP, "GDrs2ll", __FILE__, __LINE__); + HEreport("GCTP Error: %s %s %s\n", "grid coordinates", + "are more than .5 cells", + "above 90.00N or below 90.00S. "); + return (status); + } + else if( beta <= -1) + { + errorcode = inv_trans[projcode] (xMtr, 0.0, + &lonrad, &latrad); + latrad = - M_PI1/2; + } + else if( beta >= 1) + { + errorcode = inv_trans[projcode] (xMtr, 0.0, + &lonrad, &latrad); + latrad = M_PI1/2; + } + else + { + errorcode = inv_trans[projcode] (xMtr, yMtr, + &lonrad, &latrad); + } + + /* Report error if any */ + /* ------------------- */ + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDrs2ll", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + /* Convert from radians to decimal degrees */ + /* --------------------------------------- */ + longitude[i] = EHconvAng(lonrad, HDFE_RAD_DEG); + latitude[i] = EHconvAng(latrad, HDFE_RAD_DEG); + } + } + } + + + + + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: lamaxDxDtheta | +| | +| DESCRIPTION: Partial derivative along longitude line for Lambert Azimuthal | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| float64 Dx/D(theta) for LAMAZ projection | +| | +| INPUTS: | +| parms float64 Parameters defining partial derivative | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Nov 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +float64 +lamazDxDtheta(float64 parms[]) +{ + float64 snTheta, sn2Theta, snTheta1, csTheta1, csLamda; + + snTheta = sin(EHconvAng(parms[0], HDFE_DEG_RAD)); + sn2Theta = sin(2 * EHconvAng(parms[0], HDFE_DEG_RAD)); + snTheta1 = sin(EHconvAng(parms[1], HDFE_DEG_RAD)); + csTheta1 = cos(EHconvAng(parms[1], HDFE_DEG_RAD)); + csLamda = cos(EHconvAng(parms[2], HDFE_DEG_RAD) - + EHconvAng(parms[3], HDFE_DEG_RAD)); + + return (4 * snTheta + + (csTheta1 * csLamda * sn2Theta) + + (2 * snTheta1 * (1 + (snTheta * snTheta)))); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: lamaxDxDlamda | +| | +| DESCRIPTION: Partial derivative along latitude line for Lambert Azimuthal | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| float64 Dx/D(lamda) for LAMAZ projection | +| | +| INPUTS: | +| parms float64 Parameters defining partial derivative | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Nov 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +float64 +lamazDxDlamda(float64 parms[]) +{ + float64 snTheta, csTheta, snTheta1, csTheta1, csLamda; + float64 cs, sn; + + snTheta = sin(EHconvAng(parms[2], HDFE_DEG_RAD)); + csTheta = cos(EHconvAng(parms[2], HDFE_DEG_RAD)); + snTheta1 = sin(EHconvAng(parms[1], HDFE_DEG_RAD)); + csTheta1 = cos(EHconvAng(parms[1], HDFE_DEG_RAD)); + csLamda = cos(EHconvAng(parms[0], HDFE_DEG_RAD) - + EHconvAng(parms[3], HDFE_DEG_RAD)); + + cs = csTheta * csTheta1; + sn = snTheta * snTheta1; + + return (cs + (2 * (1 + sn) + (cs * csLamda)) * csLamda); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: lamaxDyDtheta | +| | +| DESCRIPTION: Partial derivative along longitude line for Lambert Azimuthal | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| float64 Dy/D(theta) for LAMAZ projection | +| | +| INPUTS: | +| parms float64 Parameters defining partial derivative | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Nov 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +float64 +lamazDyDtheta(float64 parms[]) +{ + float64 snTheta, csTheta, snTheta1, csTheta1, csLamda; + float64 sn2, cs2, sndiff; + + snTheta = sin(EHconvAng(parms[0], HDFE_DEG_RAD)); + csTheta = cos(EHconvAng(parms[0], HDFE_DEG_RAD)); + snTheta1 = sin(EHconvAng(parms[1], HDFE_DEG_RAD)); + csTheta1 = cos(EHconvAng(parms[1], HDFE_DEG_RAD)); + csLamda = cos(EHconvAng(parms[2], HDFE_DEG_RAD) - + EHconvAng(parms[3], HDFE_DEG_RAD)); + + sn2 = snTheta1 * snTheta; + cs2 = csTheta1 * csTheta; + sndiff = snTheta1 - snTheta; + + return (cs2 * (sn2 * (1 + (csLamda * csLamda)) + 2) + + csLamda * (2 * (1 + sn2 * sn2) - (sndiff * sndiff))); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: homDyDtheta | +| | +| DESCRIPTION: Partial derivative along longitude line for Oblique Mercator | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| float64 Dx/D(theta) for HOM projection | +| | +| INPUTS: | +| parms float64 Parameters defining partial derivative | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Mar 97 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +float64 +homDyDtheta(float64 parms[]) +{ + float64 tnTheta, tnTheta1, snLamda; + + tnTheta = tan(EHconvAng(parms[0], HDFE_DEG_RAD)); + tnTheta1 = tan(EHconvAng(parms[1], HDFE_DEG_RAD)); + snLamda = cos(EHconvAng(parms[2], HDFE_DEG_RAD) - + EHconvAng(parms[3], HDFE_DEG_RAD)); + + return (tnTheta * snLamda + tnTheta1); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDtangentpnts | +| | +| DESCRIPTION: Finds tangent points along lon/lat lines | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| projcode int32 Projection code | +| projparm float64 Projection parameters | +| cornerlon float64 dec deg Longitude of opposite corners of box | +| cornerlat float64 dec deg Latitude of opposite corners of box | +| longitude float64 dec deg Longitude of points to check | +| latitude float64 dec deg Latitude of points to check | +| | +| | +| OUTPUTS: | +| npnts int32 Number of points to check in subset | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Nov 96 Joel Gales Original Programmer | +| Mar 97 Joel Gales Add support for LAMCC, POLYC, TM | +| Aug 99 Abe Taaheri Add support for ALBERS, and MERCAT projections. | +| Also changed misstyped bisectParm[2] to | +| bisectParm[3] for HOM projection. | +| Jun 00 Abe Taaheri Added support for EASE grid | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +GDtangentpnts(int32 projcode, float64 projparm[], float64 cornerlon[], + float64 cornerlat[], float64 longitude[], float64 latitude[], + int32 * npnts) +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + + float64 lonrad; /* Longitude (radians) */ + float64 latrad; /* Latitude (radians) */ + float64 cs[4]; /* Cosine array */ + float64 sn[4]; /* Sine array */ + float64 csTest; /* Cosine test value */ + float64 snTest; /* Sine test value */ + float64 crs01; /* Cross product */ + float64 crsTest[2]; /* Cross product array */ + float64 longPol; /* Longitude beneath pole */ + float64 minLat; /* Minimum latitude */ + float64 bisectParm[4]; /* Bisection parameters */ + float64 tanLat; /* Tangent latitude */ + float64 tanLon; /* Tangent lontitude */ + float64 dotPrd; /* Dot product */ + float64 centMerd; /* Central Meridian */ + float64 orgLat; /* Latitude of origin */ + float64 dpi; /* Double precision pi */ + + float64 lamazDxDtheta(); /* Lambert Azimuthal Dx/Dtheta */ + float64 lamazDxDlamda(); /* Lambert Azimuthal Dx/Dlamda */ + float64 lamazDyDtheta(); /* Lambert Azimuthal Dy/Dtheta */ + float64 homDyDtheta(); /* Oblique Mercator Dy/Dtheta */ + + + /* Conpute pi (double precsion) */ + /* ---------------------------- */ + dpi = atan(1.0) * 4; + + + switch (projcode) + { + case GCTP_MERCAT: + { + /* No need for tangent points, since MERCAT projection + is rectangular */ + } + break; + case GCTP_BCEA: + { + /* No need for tangent points, since BCEA projection + is rectangular */ + } + break; + case GCTP_CEA: + { + /* No need for tangent points, since CEA projection + is rectangular */ + } + break; + + case GCTP_PS: + { + /* Add "xy axis" points for Polar Stereographic if necessary */ + /* --------------------------------------------------------- */ + + + /* Get minimum of corner latitudes */ + /* ------------------------------- */ + minLat = (fabs(cornerlat[0]) <= fabs(cornerlat[1])) + ? cornerlat[0] : cornerlat[1]; + + + /* Compute sine and cosine of corner longitudes */ + /* -------------------------------------------- */ + for (i = 0; i < 2; i++) + { + lonrad = EHconvAng(cornerlon[i], HDFE_DEG_RAD); + cs[i] = cos(lonrad); + sn[i] = sin(lonrad); + } + + + /* Compute cross product */ + /* --------------------- */ + crs01 = cs[0] * sn[1] - cs[1] * sn[0]; + + + /* Convert longitude beneath pole from DMS to DEG */ + /* ---------------------------------------------- */ + longPol = EHconvAng(projparm[4], HDFE_DMS_RAD); + + + + for (i = 0; i < 4; i++) + { + csTest = cos(longPol); + snTest = sin(longPol); + + crsTest[0] = cs[0] * snTest - csTest * sn[0]; + crsTest[1] = cs[1] * snTest - csTest * sn[1]; + + if ((crs01 > 0 && crsTest[0] > 0 && crsTest[1] < 0) || + (crs01 < 0 && crsTest[0] < 0 && crsTest[1] < 0) || + (crs01 < 0 && crsTest[0] > 0 && crsTest[1] < 0) || + (crs01 < 0 && crsTest[0] > 0 && crsTest[1] > 0)) + { + longitude[*npnts] = EHconvAng(longPol, HDFE_RAD_DEG); + latitude[*npnts] = minLat; + (*npnts)++; + } + longPol += 0.5 * dpi; + } + } + break; + + + case GCTP_LAMAZ: + { + if ((int32) projparm[5] == +90000000 || + (int32) projparm[5] == -90000000) + { + /* Add "xy axis" points for Polar Lambert Azimuthal */ + /* ------------------------------------------------ */ + minLat = (fabs(cornerlat[0]) <= fabs(cornerlat[1])) + ? cornerlat[0] : cornerlat[1]; + + for (i = 0; i < 2; i++) + { + lonrad = EHconvAng(cornerlon[i], HDFE_DEG_RAD); + cs[i] = cos(lonrad); + sn[i] = sin(lonrad); + } + crs01 = cs[0] * sn[1] - cs[1] * sn[0]; + + longPol = EHconvAng(projparm[4], HDFE_DMS_RAD); + for (i = 0; i < 4; i++) + { + csTest = cos(longPol); + snTest = sin(longPol); + + crsTest[0] = cs[0] * snTest - csTest * sn[0]; + crsTest[1] = cs[1] * snTest - csTest * sn[1]; + + if ((crs01 > 0 && crsTest[0] > 0 && crsTest[1] < 0) || + (crs01 < 0 && crsTest[0] < 0 && crsTest[1] < 0) || + (crs01 < 0 && crsTest[0] > 0 && crsTest[1] < 0) || + (crs01 < 0 && crsTest[0] > 0 && crsTest[1] > 0)) + { + longitude[*npnts] = EHconvAng(longPol, HDFE_RAD_DEG); + latitude[*npnts] = minLat; + (*npnts)++; + } + longPol += 0.5 * dpi; + } + } + else if ((int32) projparm[5] == 0) + { + /* Add "Equator" points for Equatorial Lambert Azimuthal */ + /* ----------------------------------------------------- */ + if (cornerlat[0] * cornerlat[1] < 0) + { + longitude[4] = cornerlon[0]; + latitude[4] = 0; + + longitude[5] = cornerlon[1]; + latitude[5] = 0; + + *npnts = 6; + } + } + else + { + /* Add tangent points for Oblique Lambert Azimuthal */ + /* ------------------------------------------------ */ + bisectParm[0] = EHconvAng(projparm[5], HDFE_DMS_DEG); + bisectParm[2] = EHconvAng(projparm[4], HDFE_DMS_DEG); + + + /* Tangent to y-axis along longitude */ + /* --------------------------------- */ + for (i = 0; i < 2; i++) + { + bisectParm[1] = cornerlon[i]; + + if (EHbisect(lamazDxDtheta, bisectParm, 3, + cornerlat[0], cornerlat[1], + 0.0001, &tanLat) == 0) + { + longitude[*npnts] = cornerlon[i]; + latitude[*npnts] = tanLat; + (*npnts)++; + } + } + + /* Tangent to y-axis along latitude */ + /* -------------------------------- */ + for (i = 0; i < 2; i++) + { + bisectParm[1] = cornerlat[i]; + + if (EHbisect(lamazDxDlamda, bisectParm, 3, + cornerlon[0], cornerlon[1], + 0.0001, &tanLon) == 0) + { + longitude[*npnts] = tanLon; + latitude[*npnts] = cornerlat[i]; + (*npnts)++; + } + } + + + /* Tangent to x-axis along longitude */ + /* --------------------------------- */ + for (i = 0; i < 2; i++) + { + bisectParm[1] = cornerlon[i]; + + if (EHbisect(lamazDyDtheta, bisectParm, 3, + cornerlat[0], cornerlat[1], + 0.0001, &tanLat) == 0) + { + longitude[*npnts] = cornerlon[i]; + latitude[*npnts] = tanLat; + (*npnts)++; + } + } + + /* Tangent to x-axis along latitude */ + /* -------------------------------- */ + for (i = 0; i < 2; i++) + { + lonrad = EHconvAng(cornerlon[i], HDFE_DEG_RAD); + cs[i] = cos(lonrad); + sn[i] = sin(lonrad); + } + crs01 = cs[0] * sn[1] - cs[1] * sn[0]; + + longPol = EHconvAng(projparm[4], HDFE_DMS_RAD); + for (i = 0; i < 2; i++) + { + csTest = cos(longPol); + snTest = sin(longPol); + + crsTest[0] = cs[0] * snTest - csTest * sn[0]; + crsTest[1] = cs[1] * snTest - csTest * sn[1]; + + if ((crs01 > 0 && crsTest[0] > 0 && crsTest[1] < 0) || + (crs01 < 0 && crsTest[0] < 0 && crsTest[1] < 0) || + (crs01 < 0 && crsTest[0] > 0 && crsTest[1] < 0) || + (crs01 < 0 && crsTest[0] > 0 && crsTest[1] > 0)) + { + longitude[*npnts] = EHconvAng(longPol, HDFE_RAD_DEG); + latitude[*npnts] = cornerlat[0]; + (*npnts)++; + longitude[*npnts] = EHconvAng(longPol, HDFE_RAD_DEG); + latitude[*npnts] = cornerlat[1]; + (*npnts)++; + } + longPol += dpi; + } + } + } + break; + + + case GCTP_GOOD: + { + /* Add "Equator" points for Goode Homolosine if necessary */ + /* ------------------------------------------------------ */ + if (cornerlat[0] * cornerlat[1] < 0) + { + longitude[4] = cornerlon[0]; + latitude[4] = 0; + + longitude[5] = cornerlon[1]; + latitude[5] = 0; + + *npnts = 6; + } + } + break; + + + case GCTP_LAMCC: + { + /* Compute sine and cosine of corner longitudes */ + /* -------------------------------------------- */ + for (i = 0; i < 2; i++) + { + lonrad = EHconvAng(cornerlon[i], HDFE_DEG_RAD); + cs[i] = cos(lonrad); + sn[i] = sin(lonrad); + } + + + /* Compute dot product */ + /* ------------------- */ + dotPrd = cs[0] * cs[1] + sn[0] * sn[1]; + + + /* Convert central meridian (DMS to DEG) & compute sin & cos */ + /* --------------------------------------------------------- */ + centMerd = EHconvAng(projparm[4], HDFE_DMS_DEG); + lonrad = EHconvAng(centMerd, HDFE_DEG_RAD); + cs[1] = cos(lonrad); + sn[1] = sin(lonrad); + + + /* If box brackets central meridian ... */ + /* ------------------------------------ */ + if (cs[0] * cs[1] + sn[0] * sn[1] > dotPrd) + { + latitude[4] = cornerlat[0]; + longitude[4] = centMerd; + + latitude[5] = cornerlat[1]; + longitude[5] = centMerd; + + *npnts = 6; + } + } + break; + + + case GCTP_ALBERS: + { + /* Compute sine and cosine of corner longitudes */ + /* -------------------------------------------- */ + for (i = 0; i < 2; i++) + { + lonrad = EHconvAng(cornerlon[i], HDFE_DEG_RAD); + cs[i] = cos(lonrad); + sn[i] = sin(lonrad); + } + + + /* Compute dot product */ + /* ------------------- */ + dotPrd = cs[0] * cs[1] + sn[0] * sn[1]; + + + /* Convert central meridian (DMS to DEG) & compute sin & cos */ + /* --------------------------------------------------------- */ + centMerd = EHconvAng(projparm[4], HDFE_DMS_DEG); + lonrad = EHconvAng(centMerd, HDFE_DEG_RAD); + cs[1] = cos(lonrad); + sn[1] = sin(lonrad); + + + /* If box brackets central meridian ... */ + /* ------------------------------------ */ + if (cs[0] * cs[1] + sn[0] * sn[1] > dotPrd) + { + latitude[4] = cornerlat[0]; + longitude[4] = centMerd; + + latitude[5] = cornerlat[1]; + longitude[5] = centMerd; + + *npnts = 6; + } + } + break; + + + case GCTP_POLYC: + { + /* Compute sine and cosine of corner longitudes */ + /* -------------------------------------------- */ + for (i = 0; i < 2; i++) + { + lonrad = EHconvAng(cornerlon[i], HDFE_DEG_RAD); + cs[i] = cos(lonrad); + sn[i] = sin(lonrad); + } + + + /* Compute dot product */ + /* ------------------- */ + dotPrd = cs[0] * cs[1] + sn[0] * sn[1]; + + + /* Convert central meridian (DMS to DEG) & compute sin & cos */ + /* --------------------------------------------------------- */ + centMerd = EHconvAng(projparm[4], HDFE_DMS_DEG); + lonrad = EHconvAng(centMerd, HDFE_DEG_RAD); + cs[1] = cos(lonrad); + sn[1] = sin(lonrad); + + + /* If box brackets central meridian ... */ + /* ------------------------------------ */ + if (cs[0] * cs[1] + sn[0] * sn[1] > dotPrd) + { + latitude[4] = cornerlat[0]; + longitude[4] = centMerd; + + latitude[5] = cornerlat[1]; + longitude[5] = centMerd; + + *npnts = 6; + } + } + break; + + + case GCTP_TM: + { + /* Compute sine and cosine of corner longitudes */ + /* -------------------------------------------- */ + for (i = 0; i < 2; i++) + { + lonrad = EHconvAng(cornerlon[i], HDFE_DEG_RAD); + cs[i] = cos(lonrad); + sn[i] = sin(lonrad); + } + + + /* Compute dot product */ + /* ------------------- */ + dotPrd = cs[0] * cs[1] + sn[0] * sn[1]; + + + for (i = -1; i <= 1; i++) + { + centMerd = EHconvAng(projparm[4], HDFE_DMS_DEG); + lonrad = EHconvAng(centMerd + 90 * i, HDFE_DEG_RAD); + csTest = cos(lonrad); + snTest = sin(lonrad); + + + /* If box brackets meridian ... */ + /* ---------------------------- */ + if (csTest * cs[1] + snTest * sn[1] > dotPrd) + { + latitude[*npnts] = cornerlat[0]; + longitude[*npnts] = centMerd; + (*npnts)++; + + latitude[*npnts] = cornerlat[1]; + longitude[*npnts] = centMerd; + (*npnts)++; + } + } + + + + /* Compute sine and cosine of corner latitudes */ + /* ------------------------------------------- */ + for (i = 0; i < 2; i++) + { + latrad = EHconvAng(cornerlat[i], HDFE_DEG_RAD); + cs[i] = cos(latrad); + sn[i] = sin(latrad); + } + + + /* Compute dot product */ + /* ------------------- */ + dotPrd = cs[0] * cs[1] + sn[0] * sn[1]; + + + /* Convert origin latitude (DMS to DEG) & compute sin & cos */ + /* -------------------------------------------------------- */ + orgLat = EHconvAng(projparm[5], HDFE_DMS_DEG); + latrad = EHconvAng(orgLat, HDFE_DEG_RAD); + cs[1] = cos(latrad); + sn[1] = sin(latrad); + + + /* If box brackets origin latitude ... */ + /* ----------------------------------- */ + if (cs[0] * cs[1] + sn[0] * sn[1] > dotPrd) + { + latitude[*npnts] = orgLat; + longitude[*npnts] = cornerlon[0]; + (*npnts)++; + + latitude[*npnts] = orgLat; + longitude[*npnts] = cornerlon[1]; + (*npnts)++; + } + } + break; + + + case GCTP_HOM: + { + /* Tangent to y-axis along longitude */ + /* --------------------------------- */ + if (projparm[12] == 0) + { + cs[0] = cos(EHconvAng(projparm[8], HDFE_DMS_RAD)); + sn[0] = sin(EHconvAng(projparm[8], HDFE_DMS_RAD)); + cs[1] = cos(EHconvAng(projparm[9], HDFE_DMS_RAD)); + sn[1] = sin(EHconvAng(projparm[9], HDFE_DMS_RAD)); + cs[2] = cos(EHconvAng(projparm[10], HDFE_DMS_RAD)); + sn[2] = sin(EHconvAng(projparm[10], HDFE_DMS_RAD)); + cs[3] = cos(EHconvAng(projparm[11], HDFE_DMS_RAD)); + sn[3] = sin(EHconvAng(projparm[11], HDFE_DMS_RAD)); + + bisectParm[3] = atan2( + (cs[1] * sn[3] * cs[0] - sn[1] * cs[3] * cs[2]), + (sn[1] * cs[3] * sn[2] - cs[1] * sn[3] * sn[0])); + bisectParm[0] = atan( + (sin(bisectParm[3]) * sn[0] - cos(bisectParm[3]) * cs[0]) / + (sn[1] / cs[1])); + bisectParm[2] = bisectParm[3] + 0.5 * dpi; + } + else + { + cs[0] = cos(EHconvAng(projparm[3], HDFE_DMS_RAD)); + sn[0] = sin(EHconvAng(projparm[3], HDFE_DMS_RAD)); + cs[1] = cos(EHconvAng(projparm[4], HDFE_DMS_RAD)); + sn[1] = sin(EHconvAng(projparm[4], HDFE_DMS_RAD)); + + bisectParm[0] = asin(cs[1] * sn[0]); + bisectParm[2] = atan2(-cs[0], (-sn[1] * sn[0])) + 0.5 * dpi; + } + + for (i = 0; i < 2; i++) + { + bisectParm[1] = cornerlon[i]; + + if (EHbisect(homDyDtheta, bisectParm, 3, + cornerlat[0], cornerlat[1], + 0.0001, &tanLat) == 0) + { + longitude[*npnts] = cornerlon[i]; + latitude[*npnts] = tanLat; + (*npnts)++; + } + } + + } + break; + } + + + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdefboxregion | +| | +| DESCRIPTION: Defines region for subsetting in a grid. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| regionID int32 Region ID | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| cornerlon float64 dec deg Longitude of opposite corners of box | +| cornerlat float64 dec deg Latitude of opposite corners of box | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales "Clamp" subset region around grid | +| Oct 96 Joel Gales Fix "outside region" check | +| Nov 96 Joel Gales Add check for "tangent" points (GDtangentpnts) | +| Dec 96 Joel Gales Trap if no projection code defined | +| Dec 96 Joel Gales Add multiple vertical subsetting capability | +| Mar 99 David Wynne Fix for NCR 21195, allow subsetting of MISR SOM | +| data sets | +| Jun 00 Abe Taaheri Added support for EASE grid | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDdefboxregion(int32 gridID, float64 cornerlon[], float64 cornerlat[]) +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn k; /* Loop index */ + intn n; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 regionID = -1; /* Region ID */ + int32 xdimsize; /* XDim size */ + int32 ydimsize; /* YDim size */ + int32 projcode; /* Projection code */ + int32 zonecode; /* Zone code */ + int32 spherecode; /* Sphere code */ + int32 row[32]; /* Row array */ + int32 col[32]; /* Column array */ + int32 minCol = 0; /* Minimun column value */ + int32 minRow = 0; /* Minimun row value */ + int32 maxCol = 0; /* Maximun column value */ + int32 maxRow = 0; /* Maximun row value */ + int32 npnts; /* Number of boundary + (edge & tangent) pnts */ + + float64 longitude[32]; /* Longitude array */ + float64 latitude[32]; /* Latitude array */ + float64 upleftpt[2]; /* Upper left pt coordinates */ + float64 lowrightpt[2]; /* Lower right pt coordinates */ + float64 somupleftpt[2]; /* temporary Upper left pt coordinates + for SOM projection */ + float64 somlowrightpt[2]; /* temporary Lower right pt + coordinates for SOM projection */ + float64 projparm[16]; /* Projection parameters */ + float64 xscale; /* X scale */ + float64 yscale; /* Y scale */ + float64 lonrad0; /* Longitude of upper left point + (radians) */ + float64 latrad0; /* Latitude of upper left point (radians) */ + float64 lonrad2; /* Longitude of point (radians) */ + float64 latrad2; /* Latitude of point (radians) */ + + /* Used for SOM projection */ + char *utlbuf; + char *gridname; + int32 blockindexstart = -1; + int32 blockindexstop = -1; + float32 offset[180]; + float64 templeftpt[2]; + float64 temprightpt[2]; + int32 idOffset = GDIDOFFSET; /* Grid ID offset */ + float64 xmtr[2], ymtr[2]; + float64 lon[2],lat[2]; + float64 xcor[2], ycor[2]; + int32 nlatlon; + float64 upleftpt_m[2]; + + + utlbuf = (char *)calloc(128, sizeof(char)); + if(utlbuf == NULL) + { + HEpush(DFE_NOSPACE,"GDdefboxregion", __FILE__, __LINE__); + return(-1); + } + gridname = (char *)calloc(128, sizeof(char)); + if(gridname == NULL) + { + HEpush(DFE_NOSPACE,"GDdefboxregion", __FILE__, __LINE__); + free(utlbuf); + return(-1); + } + + /* Check for valid grid ID */ + /* ----------------------- */ + status = GDchkgdid(gridID, "GDdefboxregion", + &fid, &sdInterfaceID, &gdVgrpID); + + + if (status == 0) + { + /* Get grid info */ + /* ------------- */ + status = GDgridinfo(gridID, &xdimsize, &ydimsize, + upleftpt, lowrightpt); + + + /* If error then bail */ + /* ------------------ */ + if (status != 0) + { + regionID = -1; + free(utlbuf); + free(gridname); + return (regionID); + } + + + /* Get proj info */ + /* ------------- */ + status = GDprojinfo(gridID, &projcode, &zonecode, + &spherecode, projparm); + + + /* If no projection code defined then bail */ + /* --------------------------------------- */ + if (projcode == -1) + { + regionID = -1; + free(utlbuf); + free(gridname); + return (regionID); + } + + + /* Get default values for upleft and lowright if necessary */ + /* ------------------------------------------------------- */ + if (upleftpt[0] == 0 && upleftpt[1] == 0 && + lowrightpt[0] == 0 && lowrightpt[1] == 0) + { + status = GDgetdefaults(projcode, zonecode, projparm, spherecode, + upleftpt, lowrightpt); + + /* If error then bail */ + /* ------------------ */ + if (status != 0) + { + regionID = -1; + free(utlbuf); + free(gridname); + return (regionID); + } + } + + + + /* Fill-up longitude and latitude arrays */ + /* ------------------------------------- */ + longitude[0] = cornerlon[0]; + latitude[0] = cornerlat[0]; + + longitude[1] = cornerlon[0]; + latitude[1] = cornerlat[1]; + + longitude[2] = cornerlon[1]; + latitude[2] = cornerlat[0]; + + longitude[3] = cornerlon[1]; + latitude[3] = cornerlat[1]; + + npnts = 4; + + + /* Find additional tangent points from GDtangentpnts */ + /* ------------------------------------------------- */ + status = GDtangentpnts(projcode, projparm, cornerlon, cornerlat, + longitude, latitude, &npnts); + + /* If SOM projection with projparm[11] non-zero ... */ + if (projcode == GCTP_SOM && projparm[11] != 0) + { + Vgetname(GDXGrid[gridID % idOffset].IDTable, gridname); + sprintf(utlbuf, "%s%s", "_BLKSOM:", gridname); + status = GDreadattr(gridID, utlbuf, offset); + + somupleftpt[0] = upleftpt[0]; + somupleftpt[1] = upleftpt[1]; + somlowrightpt[0]= lowrightpt[0]; + somlowrightpt[1] = lowrightpt[1]; + + k = 0; + n = 2; + + for (j = 0; j <= projparm[11] - 1; j++) + { + + /* Convert from lon/lat to row/col */ + /* ------------------------------- */ + status = GDll2ij(projcode, zonecode, projparm, spherecode, + xdimsize, ydimsize, somupleftpt, somlowrightpt, + npnts, longitude, latitude, row, col, NULL, NULL); + + + /* Find min/max values for row & col */ + /* --------------------------------- */ + minCol = col[0]; + minRow = row[0]; + maxCol = col[0]; + maxRow = row[0]; + for (i = 1; i < npnts; i++) + { + if (col[i] < minCol) + { + minCol = col[i]; + } + + if (col[i] > maxCol) + { + maxCol = col[i]; + } + + if (row[i] < minRow) + { + minRow = row[i]; + } + + if (row[i] > maxRow) + { + maxRow = row[i]; + } + } + + + + /* "Clamp" if outside Grid */ + /* ----------------------- */ + minCol = (minCol < 0) ? 0 : minCol; + minRow = (minRow < 0) ? 0 : minRow; + + maxCol = (maxCol >= xdimsize) ? xdimsize - 1 : maxCol; + maxRow = (maxRow >= ydimsize) ? ydimsize - 1 : maxRow; + + + /* Check whether subset region is outside grid region */ + /* -------------------------------------------------- */ + if (minCol >= xdimsize || minRow >= ydimsize || + maxCol < 0 || maxRow < 0) + { + if ( blockindexstart == -1 && (projparm[11]) == j) + { + status = -1; + HEpush(DFE_GENAPP, "GDdefboxregion", __FILE__, __LINE__); + HEreport("Subset Region outside of Grid Region\n"); + regionID = -1; + } + } + else + { + if (k == 0) + { + blockindexstart = j; + blockindexstop = j; + k = 1; + } + else + { + blockindexstop = j; + } + } + templeftpt[0] = upleftpt[0] + ((offset[j]/xdimsize)*abs(upleftpt[0] - lowrightpt[0])) + abs((upleftpt[0] - lowrightpt[0]))*(n-1); + templeftpt[1] = upleftpt[1] + ((lowrightpt[1] - upleftpt[1]))*(n-1); + + temprightpt[0] = lowrightpt[0] + ((offset[j]/xdimsize)*abs(lowrightpt[0] - upleftpt[0])) + abs((lowrightpt[0] - upleftpt[0]))*(n-1); + temprightpt[1] = lowrightpt[1] + ((upleftpt[1] - lowrightpt[1]))*(n-1); + + somupleftpt[0] = templeftpt[0]; + somupleftpt[1] = templeftpt[1]; + + somlowrightpt[0] = temprightpt[0]; + somlowrightpt[1] = temprightpt[1]; + n++; + } + } + else + { + + /* Convert from lon/lat to row/col */ + /* ------------------------------- */ + + status = GDll2ij(projcode, zonecode, projparm, spherecode, + xdimsize, ydimsize, upleftpt, lowrightpt, + npnts, longitude, latitude, row, col, NULL, NULL); + + /* Find min/max values for row & col */ + /* --------------------------------- */ + minCol = col[0]; + minRow = row[0]; + maxCol = col[0]; + maxRow = row[0]; + for (i = 1; i < npnts; i++) + { + if (col[i] < minCol) + { + minCol = col[i]; + } + + if (col[i] > maxCol) + { + maxCol = col[i]; + } + + if (row[i] < minRow) + { + minRow = row[i]; + } + + if (row[i] > maxRow) + { + maxRow = row[i]; + } + } + + + + /* "Clamp" if outside Grid */ + /* ----------------------- */ + minCol = (minCol < 0) ? 0 : minCol; + minRow = (minRow < 0) ? 0 : minRow; + + maxCol = (maxCol >= xdimsize) ? xdimsize - 1 : maxCol; + maxRow = (maxRow >= ydimsize) ? ydimsize - 1 : maxRow; + + + /* Check whether subset region is outside grid region */ + /* -------------------------------------------------- */ + if (minCol >= xdimsize || minRow >= ydimsize || maxCol < 0 || maxRow < 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDdefboxregion", __FILE__, __LINE__); + HEreport("Subset Region outside of Grid Region\n"); + regionID = -1; + + } + } + if (status == 0) + { + /* Store grid region info */ + /* ---------------------- */ + for (i = 0; i < NGRIDREGN; i++) + { + /* Find first empty grid region */ + /* ---------------------------- */ + if (GDXRegion[i] == 0) + { + /* Allocate space for grid region entry */ + /* ------------------------------------ */ + GDXRegion[i] = (struct gridRegion *) + calloc(1, sizeof(struct gridRegion)); + if(GDXRegion[i] == NULL) + { + HEpush(DFE_NOSPACE,"GDdefboxregion", __FILE__, __LINE__); + free(utlbuf); + free(gridname); + return(-1); + } + + + /* Store file and grid ID */ + /* ---------------------- */ + GDXRegion[i]->fid = fid; + GDXRegion[i]->gridID = gridID; + + + /* Initialize vertical subset entries to -1 */ + /* ---------------------------------------- */ + for (j = 0; j < 8; j++) + { + GDXRegion[i]->StartVertical[j] = -1; + GDXRegion[i]->StopVertical[j] = -1; + } + + + /* Store start & count along x & y */ + /* ------------------------------- */ + GDXRegion[i]->xStart = minCol; + GDXRegion[i]->xCount = maxCol - minCol + 1; + GDXRegion[i]->yStart = minRow; + GDXRegion[i]->yCount = maxRow - minRow + 1; + + + /* Store upleft and lowright points of subset region */ + /* ------------------------------------------------- */ + if (projcode == GCTP_GEO ) + { + /* GEO projection */ + /* ------------------------ */ + + /* Convert upleft & lowright lon from DMS to radians */ + /* ------------------------------------------------- */ + lonrad0 = EHconvAng(upleftpt[0], HDFE_DMS_RAD); + lonrad2 = EHconvAng(lowrightpt[0], HDFE_DMS_RAD); + + /* Compute X scale */ + /* --------------- */ + xscale = (lonrad2 - lonrad0) / xdimsize; + + /* Convert upleft & lowright lat from DMS to radians */ + /* ------------------------------------------------- */ + latrad0 = EHconvAng(upleftpt[1], HDFE_DMS_RAD); + latrad2 = EHconvAng(lowrightpt[1], HDFE_DMS_RAD); + + /* Compute Y scale */ + /* --------------- */ + yscale = (latrad2 - latrad0) / ydimsize; + + + /* MinCol -> radians -> DMS -> upleftpt[0] */ + /* --------------------------------------- */ + GDXRegion[i]->upleftpt[0] = + EHconvAng(lonrad0 + xscale * minCol, + HDFE_RAD_DMS); + + + /* MinRow -> radians -> DMS -> upleftpt[1] */ + /* --------------------------------------- */ + GDXRegion[i]->upleftpt[1] = + EHconvAng(latrad0 + yscale * minRow, + HDFE_RAD_DMS); + + + /* MinCol + 1 -> radians -> DMS -> lowrightpt[0] */ + /* --------------------------------------------- */ + GDXRegion[i]->lowrightpt[0] = + EHconvAng(lonrad0 + xscale * (maxCol + 1), + HDFE_RAD_DMS); + + + /* MinRow + 1 -> radians -> DMS -> lowrightpt[1] */ + /* --------------------------------------------- */ + GDXRegion[i]->lowrightpt[1] = + EHconvAng(latrad0 + yscale * (maxRow + 1), + HDFE_RAD_DMS); + } + else if (projcode == GCTP_BCEA) + { + /* BCEA projection */ + /* -------------- */ + nlatlon = 2; + lon[0] = upleftpt[0]; + lon[1] = lowrightpt[0]; + lat[0] = upleftpt[1]; + lat[1] = lowrightpt[1]; + status = + GDll2mm_cea(projcode,zonecode,spherecode,projparm, + xdimsize, ydimsize, + upleftpt, lowrightpt,nlatlon, + lon, lat, + xcor, ycor, &xscale, &yscale); + upleftpt_m[0] = xcor[0]; + upleftpt_m[1] = ycor[0]; + + + if (status == -1) + { + HEpush(DFE_GENAPP, "GDdefboxregion", __FILE__, __LINE__); + free(utlbuf); + free(gridname); + return (status); + } + + /* MinCol -> meters -> upleftpt[0] */ + /* ------------------------------- */ + xmtr[0] = upleftpt_m[0] + xscale * minCol; + + /* MinRow -> meters -> upleftpt[1] */ + /* ------------------------------- */ + ymtr[0] = upleftpt_m[1] + yscale * minRow; + + /* MinCol + 1 -> meters -> lowrightpt[0] */ + /* ------------------------------------- */ + xmtr[1] = upleftpt_m[0] + xscale * (maxCol + 1); + + /* MinRow + 1 -> meters -> lowrightpt[1] */ + /* ------------------------------------- */ + ymtr[1] = upleftpt_m[1] + yscale * (maxRow + 1); + + /* Convert upleft & lowright lon from DMS to radians */ + /* ------------------------------------------------- */ + npnts = 2; + status = GDmm2ll_cea(projcode, zonecode, spherecode, + projparm, xdimsize, ydimsize, + upleftpt, lowrightpt, npnts, + xmtr, ymtr, + longitude, latitude); + if (status == -1) + { + HEpush(DFE_GENAPP, "GDdefboxregion", __FILE__, __LINE__); + free(utlbuf); + free(gridname); + return (status); + } + GDXRegion[i]->upleftpt[0] = longitude[0]; + + GDXRegion[i]->upleftpt[1] = latitude[0]; + + GDXRegion[i]->lowrightpt[0] = longitude[1]; + + GDXRegion[i]->lowrightpt[1] = latitude[1]; + } + else if (projcode == GCTP_SOM) + { + /* Store start & count along x & y */ + /* ------------------------------- */ + GDXRegion[i]->xStart = 0; + GDXRegion[i]->xCount = xdimsize; + GDXRegion[i]->yStart = 0; + GDXRegion[i]->yCount = ydimsize; + + GDXRegion[i]->somStart = blockindexstart; + GDXRegion[i]->somCount = blockindexstop - blockindexstart + 1; + + /* Store upleft and lowright points of subset region */ + /* ------------------------------------------------- */ + if (blockindexstart == 0) + { + GDXRegion[i]->upleftpt[0] = upleftpt[0]; + GDXRegion[i]->upleftpt[1] = upleftpt[1]; + GDXRegion[i]->lowrightpt[0] = lowrightpt[0]; + GDXRegion[i]->lowrightpt[1] = lowrightpt[1]; + } + else + { + GDXRegion[i]->upleftpt[0] = + (lowrightpt[0] - upleftpt[0])* + (offset[blockindexstart-1]/xdimsize) + upleftpt[0]; + GDXRegion[i]->upleftpt[1] = + (lowrightpt[1] - upleftpt[1])* + (blockindexstart+1-1) + upleftpt[1]; + + GDXRegion[i]->lowrightpt[0] = + (lowrightpt[0] - upleftpt[0])* + (offset[blockindexstart-1]/xdimsize) + lowrightpt[0]; + GDXRegion[i]->lowrightpt[1] = + (lowrightpt[1] - upleftpt[1])* + (blockindexstart+1-1) + lowrightpt[1]; + + } + } + else + { + /* Non-GEO, Non-BCEA projections */ + /* ---------------------------- */ + + /* Compute X & Y scale */ + /* ------------------- */ + xscale = (lowrightpt[0] - upleftpt[0]) / xdimsize; + yscale = (lowrightpt[1] - upleftpt[1]) / ydimsize; + + + /* MinCol -> meters -> upleftpt[0] */ + /* ------------------------------- */ + GDXRegion[i]->upleftpt[0] = upleftpt[0] + + xscale * minCol; + + + /* MinRow -> meters -> upleftpt[1] */ + /* ------------------------------- */ + GDXRegion[i]->upleftpt[1] = upleftpt[1] + + yscale * minRow; + + + /* MinCol + 1 -> meters -> lowrightpt[0] */ + /* ------------------------------------- */ + GDXRegion[i]->lowrightpt[0] = upleftpt[0] + + xscale * (maxCol + 1); + + + /* MinRow + 1 -> meters -> lowrightpt[1] */ + /* ------------------------------------- */ + GDXRegion[i]->lowrightpt[1] = upleftpt[1] + + yscale * (maxRow + 1); + } + + /* Store region ID */ + /* --------------- */ + regionID = i; + break; + } + + } + } + + } + free(utlbuf); + free(gridname); + return (regionID); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDregioninfo | +| | +| DESCRIPTION: Retrieves size of region in bytes. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| regionID int32 Region ID | +| fieldname char Fieldname | +| | +| | +| OUTPUTS: | +| ntype int32 field number type | +| rank int32 field rank | +| dims int32 dimensions of field region | +| size int32 size in bytes of field region | +| upleftpt float64 Upper left corner coord for region | +| lowrightpt float64 Lower right corner coord for region | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Add vertical subsetting | +| Dec 96 Joel Gales Add multiple vertical subsetting capability | +| Apr 99 David Wynne Added support for MISR SOM projection, NCR 21195 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDregioninfo(int32 gridID, int32 regionID, char *fieldname, + int32 * ntype, int32 * rank, int32 dims[], int32 * size, + float64 upleftpt[], float64 lowrightpt[]) +{ + intn j; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 index; /* Dimension index */ + + char dimlist[256]; /* Dimension list */ + char *errMesg = "Vertical Dimension Not Found: \"%s\".\n"; + char *errM1 = "Both \"XDim\" and \"YDim\" must be present "; + char *errM2 = "in the dimension list for \"%s\".\n"; + char errbuf[256];/* Error buffer */ + + + /* Check for valid grid ID */ + /* ----------------------- */ + status = GDchkgdid(gridID, "GDregioninfo", &fid, &sdInterfaceID, + &gdVgrpID); + + + /* Check for valid region ID */ + /* ------------------------- */ + if (status == 0) + { + if (regionID < 0 || regionID >= NGRIDREGN) + { + status = -1; + HEpush(DFE_RANGE, "GDregioninfo", __FILE__, __LINE__); + HEreport("Invalid Region id: %d.\n", regionID); + } + } + + + /* Check for active region ID */ + /* -------------------------- */ + if (status == 0) + { + if (GDXRegion[regionID] == 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDregioninfo", __FILE__, __LINE__); + HEreport("Inactive Region ID: %d.\n", regionID); + } + } + + + + /* Check that region defined for this file */ + /* --------------------------------------- */ + if (status == 0) + { + if (GDXRegion[regionID]->fid != fid) + { + status = -1; + HEpush(DFE_GENAPP, "GDregioninfo", __FILE__, __LINE__); + HEreport("Region is not defined for this file.\n"); + } + } + + + /* Check that region defined for this grid */ + /* --------------------------------------- */ + if (status == 0) + { + if (GDXRegion[regionID]->gridID != gridID) + { + status = -1; + HEpush(DFE_GENAPP, "GDregioninfo", __FILE__, __LINE__); + HEreport("Region is not defined for this Grid.\n"); + } + } + + + + /* Check for valid fieldname */ + /* ------------------------- */ + if (status == 0) + { + status = GDfieldinfo(gridID, fieldname, rank, dims, ntype, dimlist); + + if (status != 0) + { + /* Fieldname not found in grid */ + /* --------------------------- */ + status = -1; + HEpush(DFE_GENAPP, "GDregioninfo", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" not found.\n", + fieldname); + } + else if (*rank == 1) + { + /* Field is 1 dimensional */ + /* ---------------------- */ + status = -1; + HEpush(DFE_GENAPP, "GDregioninfo", __FILE__, __LINE__); + HEreport( + "One-Dimesional fields \"%s\" may not be subsetted.\n", + fieldname); + } + else + { + /* "XDim" and/or "YDim" not found */ + /* ------------------------------ */ + if (EHstrwithin("XDim", dimlist, ',') == -1 || + EHstrwithin("YDim", dimlist, ',') == -1) + { + status = -1; + HEpush(DFE_GENAPP, "GDregioninfo", __FILE__, __LINE__); + sprintf(errbuf, "%s%s", errM1, errM2); + HEreport(errbuf, fieldname); + } + } + } + + + + /* If no problems ... */ + /* ------------------ */ + if (status == 0) + { + /* Check if SOM projection */ + /* ----------------------- */ + if (EHstrwithin("SOMBlockDim", dimlist, ',') == 0) + { + dims[EHstrwithin("SOMBlockDim", dimlist, ',')] = + GDXRegion[regionID]->somCount; + } + + /* Load XDim dimension from region entry */ + /* ------------------------------------- */ + if (GDXRegion[regionID]->xCount != 0) + { + dims[EHstrwithin("XDim", dimlist, ',')] = + GDXRegion[regionID]->xCount; + } + + /* Load YDim dimension from region entry */ + /* ------------------------------------- */ + if (GDXRegion[regionID]->yCount != 0) + { + dims[EHstrwithin("YDim", dimlist, ',')] = + GDXRegion[regionID]->yCount; + } + + + /* Vertical Subset */ + /* --------------- */ + for (j = 0; j < 8; j++) + { + + /* If active vertical subset ... */ + /* ----------------------------- */ + if (GDXRegion[regionID]->StartVertical[j] != -1) + { + /* Find vertical dimension within dimlist */ + /* -------------------------------------- */ + index = EHstrwithin(GDXRegion[regionID]->DimNamePtr[j], + dimlist, ','); + + /* If dimension found ... */ + /* ---------------------- */ + if (index != -1) + { + /* Compute dimension size */ + /* ---------------------- */ + dims[index] = + GDXRegion[regionID]->StopVertical[j] - + GDXRegion[regionID]->StartVertical[j] + 1; + } + else + { + /* Vertical dimension not found */ + /* ---------------------------- */ + status = -1; + *size = -1; + HEpush(DFE_GENAPP, "GDregioninfo", + __FILE__, __LINE__); + HEreport(errMesg, + GDXRegion[regionID]->DimNamePtr[j]); + } + } + } + + + if (status == 0) + { + /* Compute number of total elements */ + /* -------------------------------- */ + *size = dims[0]; + for (j = 1; j < *rank; j++) + { + *size *= dims[j]; + } + + /* Multiply by size in bytes of numbertype */ + /* --------------------------------------- */ + *size *= DFKNTsize(*ntype); + + + /* Return upper left and lower right subset values */ + /* ----------------------------------------------- */ + upleftpt[0] = GDXRegion[regionID]->upleftpt[0]; + upleftpt[1] = GDXRegion[regionID]->upleftpt[1]; + lowrightpt[0] = GDXRegion[regionID]->lowrightpt[0]; + lowrightpt[1] = GDXRegion[regionID]->lowrightpt[1]; + } + } + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDextractregion | +| | +| DESCRIPTION: Retrieves data from specified region. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| regionID int32 Region ID | +| fieldname char Fieldname | +| | +| OUTPUTS: | +| buffer void Data buffer containing subsetted region | +| | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Add vertical subsetting | +| Dec 96 Joel Gales Add multiple vertical subsetting capability | +| Apr 99 David Wynne Add support for MISR SOM projection, NCR 21195 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDextractregion(int32 gridID, int32 regionID, char *fieldname, + VOIDP buffer) +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 index; /* Dimension index */ + int32 start[8]; /* Start array for data read */ + int32 edge[8]; /* Edge array for data read */ + int32 dims[8]; /* Dimensions */ + int32 rank; /* Field rank */ + int32 ntype; /* Field number type */ + int32 origincode; /* Pixel origin code */ + + char dimlist[256]; /* Dimension list */ + char *errMesg = "Vertical Dimension Not Found: \"%s\".\n"; + char *errM1 = "Both \"XDim\" and \"YDim\" must be present "; + char *errM2 = "in the dimension list for \"%s\".\n"; + char errbuf[256];/* Error buffer */ + + + /* Check for valid grid ID */ + /* ----------------------- */ + status = GDchkgdid(gridID, "GDextractregion", &fid, &sdInterfaceID, + &gdVgrpID); + + + /* Check for valid region ID */ + /* ------------------------- */ + if (status == 0) + { + if (regionID < 0 || regionID >= NGRIDREGN) + { + status = -1; + HEpush(DFE_RANGE, "GDextractregion", __FILE__, __LINE__); + HEreport("Invalid Region id: %d.\n", regionID); + } + } + + + /* Check for active region ID */ + /* -------------------------- */ + if (status == 0) + { + if (GDXRegion[regionID] == 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDextractregion", __FILE__, __LINE__); + HEreport("Inactive Region ID: %d.\n", regionID); + } + } + + + + /* Check that region defined for this file */ + /* --------------------------------------- */ + if (status == 0) + { + if (GDXRegion[regionID]->fid != fid) + { + status = -1; + HEpush(DFE_GENAPP, "GDextractregion", __FILE__, __LINE__); + HEreport("Region is not defined for this file.\n"); + } + } + + + /* Check that region defined for this grid */ + /* --------------------------------------- */ + if (status == 0) + { + if (GDXRegion[regionID]->gridID != gridID) + { + status = -1; + HEpush(DFE_GENAPP, "GDextractregion", __FILE__, __LINE__); + HEreport("Region is not defined for this Grid.\n"); + } + } + + + + /* Check for valid fieldname */ + /* ------------------------- */ + if (status == 0) + { + status = GDfieldinfo(gridID, fieldname, &rank, dims, &ntype, dimlist); + + if (status != 0) + { + /* Fieldname not found in grid */ + /* --------------------------- */ + status = -1; + HEpush(DFE_GENAPP, "GDextractregion", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" not found.\n", + fieldname); + } + else if (rank == 1) + { + /* Field is 1 dimensional */ + /* ---------------------- */ + status = -1; + HEpush(DFE_GENAPP, "GDextractregion", __FILE__, __LINE__); + HEreport( + "One-Dimesional fields \"%s\" may not be subsetted.\n", + fieldname); + } + else + { + /* "XDim" and/or "YDim" not found */ + /* ------------------------------ */ + if (EHstrwithin("XDim", dimlist, ',') == -1 || + EHstrwithin("YDim", dimlist, ',') == -1) + { + status = -1; + HEpush(DFE_GENAPP, "GDextractregion", __FILE__, __LINE__); + sprintf(errbuf, "%s%s", errM1, errM2); + HEreport(errbuf, fieldname); + } + } + } + + + + if (status == 0) + { + + /* Get origin order info */ + /* --------------------- */ + status = GDorigininfo(gridID, &origincode); + + + /* Initialize start & edge arrays */ + /* ------------------------------ */ + for (i = 0; i < rank; i++) + { + start[i] = 0; + edge[i] = dims[i]; + } + + + /* if MISR SOM projection, set start */ + /* & edge arrays for SOMBlockDim */ + /* --------------------------------- */ + if (EHstrwithin("SOMBlockDim", dimlist, ',') == 0) + { + index = EHstrwithin("SOMBlockDim", dimlist, ','); + edge[index] = GDXRegion[regionID]->somCount; + start[index] = GDXRegion[regionID]->somStart; + } + + + /* Set start & edge arrays for XDim */ + /* -------------------------------- */ + index = EHstrwithin("XDim", dimlist, ','); + if (GDXRegion[regionID]->xCount != 0) + { + edge[index] = GDXRegion[regionID]->xCount; + start[index] = GDXRegion[regionID]->xStart; + } + + /* Adjust X-dim start if origin on right edge */ + /* ------------------------------------------ */ + if ((origincode & 1) == 1) + { + start[index] = dims[index] - (start[index] + edge[index]); + } + + + /* Set start & edge arrays for YDim */ + /* -------------------------------- */ + index = EHstrwithin("YDim", dimlist, ','); + if (GDXRegion[regionID]->yCount != 0) + { + start[index] = GDXRegion[regionID]->yStart; + edge[index] = GDXRegion[regionID]->yCount; + } + + /* Adjust Y-dim start if origin on lower edge */ + /* ------------------------------------------ */ + if ((origincode & 2) == 2) + { + start[index] = dims[index] - (start[index] + edge[index]); + } + + + + /* Vertical Subset */ + /* --------------- */ + for (j = 0; j < 8; j++) + { + /* If active vertical subset ... */ + /* ----------------------------- */ + if (GDXRegion[regionID]->StartVertical[j] != -1) + { + + /* Find vertical dimension within dimlist */ + /* -------------------------------------- */ + index = EHstrwithin(GDXRegion[regionID]->DimNamePtr[j], + dimlist, ','); + + /* If dimension found ... */ + /* ---------------------- */ + if (index != -1) + { + /* Compute start and edge for vertical dimension */ + /* --------------------------------------------- */ + start[index] = GDXRegion[regionID]->StartVertical[j]; + edge[index] = GDXRegion[regionID]->StopVertical[j] - + GDXRegion[regionID]->StartVertical[j] + 1; + } + else + { + /* Vertical dimension not found */ + /* ---------------------------- */ + status = -1; + HEpush(DFE_GENAPP, "GDextractregion", __FILE__, __LINE__); + HEreport(errMesg, + GDXRegion[regionID]->DimNamePtr[j]); + } + } + } + + + /* Read into data buffer */ + /* --------------------- */ + if (status == 0) + { + status = GDreadfield(gridID, fieldname, start, NULL, edge, buffer); + } + } + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdupregion | +| | +| DESCRIPTION: Duplicates a region | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| newregionID int32 New region ID | +| | +| INPUTS: | +| oldregionID int32 Old region ID | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jan 97 Joel Gales Original Programmer | +| Oct 98 Abe Taaheri changed *GDXRegion[i] = *GDXRegion[oldregionID]; | +| to copy elements of structure one by one to avoid | +| copying pointer for DimNamePtr to another place that| +| causes "Freeing Unallocated Memory" in purify when | +| using GDdetach | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDdupregion(int32 oldregionID) +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + int32 slendupregion; + int32 newregionID = -1; /* New region ID */ + + + /* Find first empty (inactive) region */ + /* ---------------------------------- */ + for (i = 0; i < NGRIDREGN; i++) + { + if (GDXRegion[i] == 0) + { + /* Allocate space for new grid region entry */ + /* ---------------------------------------- */ + GDXRegion[i] = (struct gridRegion *) + calloc(1, sizeof(struct gridRegion)); + if(GDXRegion[i] == NULL) + { + HEpush(DFE_NOSPACE,"GDdupregion", __FILE__, __LINE__); + return(-1); + } + + + /* Copy old region structure data to new region */ + /* -------------------------------------------- */ + + GDXRegion[i]->fid = GDXRegion[oldregionID]->fid; + GDXRegion[i]->gridID = GDXRegion[oldregionID]->gridID; + GDXRegion[i]->xStart = GDXRegion[oldregionID]->xStart; + GDXRegion[i]->xCount = GDXRegion[oldregionID]->xCount; + GDXRegion[i]->yStart = GDXRegion[oldregionID]->yStart; + GDXRegion[i]->yCount = GDXRegion[oldregionID]->yCount; + GDXRegion[i]->upleftpt[0] = GDXRegion[oldregionID]->upleftpt[0]; + GDXRegion[i]->upleftpt[1] = GDXRegion[oldregionID]->upleftpt[1]; + GDXRegion[i]->lowrightpt[0] = GDXRegion[oldregionID]->lowrightpt[0]; + GDXRegion[i]->lowrightpt[1] = GDXRegion[oldregionID]->lowrightpt[1]; + for (j = 0; j < 8; j++) + { + GDXRegion[i]->StartVertical[j] = GDXRegion[oldregionID]->StartVertical[j]; + GDXRegion[i]->StopVertical[j] = GDXRegion[oldregionID]->StopVertical[j]; + } + + for (j=0; j<8; j++) + { + if(GDXRegion[oldregionID]->DimNamePtr[j] != NULL) + { + slendupregion = strlen(GDXRegion[oldregionID]->DimNamePtr[j]); + GDXRegion[i]->DimNamePtr[j] = (char *) malloc(slendupregion + 1); + strcpy(GDXRegion[i]->DimNamePtr[j],GDXRegion[oldregionID]->DimNamePtr[j]); + } + } + + + /* Define new region ID */ + /* -------------------- */ + newregionID = i; + + break; + } + } + return (newregionID); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdefvrtregion | +| | +| DESCRIPTION: Finds elements of a monotonic field within a vertical subset | +| region. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| regionID int32 Region ID | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| regionID int32 Region ID | +| vertObj char Vertical object to subset | +| range float64 Vertical subsetting range | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Aug 96 Joel Gales Original Programmer | +| Dec 96 Joel Gales Add multiple vertical subsetting capability | +| Feb 97 Joel Gales Store XDim, YDim, upleftpt, lowrightpt in GDXRegion | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +#define SETGRIDREG \ +\ +status = GDgridinfo(gridID, &xdimsize, &ydimsize, upleftpt, lowrightpt); \ +for (k = 0; k < NGRIDREGN; k++) \ +{ \ + if (GDXRegion[k] == 0) \ + { \ + GDXRegion[k] = (struct gridRegion *) \ + calloc(1, sizeof(struct gridRegion)); \ + GDXRegion[k]->fid = fid; \ + GDXRegion[k]->gridID = gridID; \ + GDXRegion[k]->xStart = 0; \ + GDXRegion[k]->xCount = xdimsize; \ + GDXRegion[k]->yStart = 0; \ + GDXRegion[k]->yCount = ydimsize; \ + GDXRegion[k]->upleftpt[0] = upleftpt[0]; \ + GDXRegion[k]->upleftpt[1] = upleftpt[1]; \ + GDXRegion[k]->lowrightpt[0] = lowrightpt[0]; \ + GDXRegion[k]->lowrightpt[1] = lowrightpt[1]; \ + regionID = k; \ + for (j=0; j<8; j++) \ + { \ + GDXRegion[k]->StartVertical[j] = -1; \ + GDXRegion[k]->StopVertical[j] = -1; \ + } \ + break; \ + } \ +} + +#define FILLVERTREG \ +for (j=0; j<8; j++) \ +{ \ + if (GDXRegion[regionID]->StartVertical[j] == -1) \ + { \ + GDXRegion[regionID]->StartVertical[j] = i; \ + GDXRegion[regionID]->DimNamePtr[j] = \ + (char *) malloc(slen + 1); \ + memcpy(GDXRegion[regionID]->DimNamePtr[j], \ + dimlist, slen + 1); \ + break; \ + } \ +} \ + + + +int32 +GDdefvrtregion(int32 gridID, int32 regionID, char *vertObj, float64 range[]) +{ + intn i, j, k, status; + uint8 found = 0; + + int16 vertINT16; + + int32 fid, sdInterfaceID, slen; + int32 gdVgrpID, rank, nt, dims[8], size; + int32 vertINT32; + int32 xdimsize; + int32 ydimsize; + + float32 vertFLT32; + float64 vertFLT64; + float64 upleftpt[2]; + float64 lowrightpt[2]; + + char *vertArr; + char *dimlist; + + /* Allocate space for dimlist */ + /* --------------------------------- */ + dimlist = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(dimlist == NULL) + { + HEpush(DFE_NOSPACE,"GDdefvrtregion", __FILE__, __LINE__); + return(-1); + } + /* Check for valid grid ID */ + /* ----------------------- */ + status = GDchkgdid(gridID, "GDdefvrtregion", + &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + memcpy(dimlist, vertObj, 4); + dimlist[4] = 0; + + if (strcmp(dimlist, "DIM:") == 0) + { + slen = strlen(vertObj) - 4; + if (regionID == -1) + { + SETGRIDREG; + } + for (j = 0; j < 8; j++) + { + if (GDXRegion[regionID]->StartVertical[j] == -1) + { + GDXRegion[regionID]->StartVertical[j] = (int32) range[0]; + GDXRegion[regionID]->StopVertical[j] = (int32) range[1]; + GDXRegion[regionID]->DimNamePtr[j] = + (char *) malloc(slen + 1); + if(GDXRegion[regionID]->DimNamePtr[j] == NULL) + { + HEpush(DFE_NOSPACE,"GDdefvrtregion", __FILE__, __LINE__); + free(dimlist); + return(-1); + } + memcpy(GDXRegion[regionID]->DimNamePtr[j], + vertObj + 4, slen + 1); + break; + } + } + } + else + { + status = GDfieldinfo(gridID, vertObj, &rank, dims, &nt, dimlist); + if (status != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDdefvrtregion", __FILE__, __LINE__); + HEreport("Vertical Field: \"%s\" not found.\n", vertObj); + } + else + { + if (rank != 1) + { + status = -1; + HEpush(DFE_GENAPP, "GDdefvrtregion", __FILE__, __LINE__); + HEreport("Vertical Field: \"%s\" must be 1-dim.\n", + vertObj); + } + else + { + slen = strlen(dimlist); + size = DFKNTsize(nt); + vertArr = (char *) calloc(dims[0], size); + if(vertArr == NULL) + { + HEpush(DFE_NOSPACE,"GDdefvrtregion", __FILE__, __LINE__); + free(dimlist); + return(-1); + } + + status = GDreadfield(gridID, vertObj, + NULL, NULL, NULL, vertArr); + + switch (nt) + { + case DFNT_INT16: + + for (i = 0; i < dims[0]; i++) + { + memcpy(&vertINT16, vertArr + i * size, size); + + if (vertINT16 >= range[0] && + vertINT16 <= range[1]) + { + found = 1; + if (regionID == -1) + { + SETGRIDREG; + } + FILLVERTREG; + + break; + } + } + + if (found == 1) + { + for (i = dims[0] - 1; i >= 0; i--) + { + memcpy(&vertINT16, vertArr + i * size, size); + + if (vertINT16 >= range[0] && + vertINT16 <= range[1]) + { + GDXRegion[regionID]->StopVertical[j] = i; + break; + } + } + } + else + { + status = -1; + } + break; + + + case DFNT_INT32: + + for (i = 0; i < dims[0]; i++) + { + memcpy(&vertINT32, vertArr + i * size, size); + + if (vertINT32 >= range[0] && + vertINT32 <= range[1]) + { + found = 1; + if (regionID == -1) + { + SETGRIDREG; + } + FILLVERTREG; + + break; + } + } + + if (found == 1) + { + for (i = dims[0] - 1; i >= 0; i--) + { + memcpy(&vertINT32, vertArr + i * size, size); + + if (vertINT32 >= range[0] && + vertINT32 <= range[1]) + { + GDXRegion[regionID]->StopVertical[j] = i; + break; + } + } + } + else + { + status = -1; + } + break; + + + case DFNT_FLOAT32: + + for (i = 0; i < dims[0]; i++) + { + memcpy(&vertFLT32, vertArr + i * size, size); + + if (vertFLT32 >= range[0] && + vertFLT32 <= range[1]) + { + found = 1; + if (regionID == -1) + { + SETGRIDREG; + } + FILLVERTREG; + + break; + } + } + + if (found == 1) + { + for (i = dims[0] - 1; i >= 0; i--) + { + memcpy(&vertFLT32, vertArr + i * size, size); + + if (vertFLT32 >= range[0] && + vertFLT32 <= range[1]) + { + GDXRegion[regionID]->StopVertical[j] = i; + break; + } + } + } + else + { + status = -1; + } + break; + + + case DFNT_FLOAT64: + + for (i = 0; i < dims[0]; i++) + { + memcpy(&vertFLT64, vertArr + i * size, size); + + if (vertFLT64 >= range[0] && + vertFLT64 <= range[1]) + { + found = 1; + if (regionID == -1) + { + SETGRIDREG; + } + FILLVERTREG; + + break; + } + } + + if (found == 1) + { + for (i = dims[0] - 1; i >= 0; i--) + { + memcpy(&vertFLT64, vertArr + i * size, size); + + if (vertFLT64 >= range[0] && + vertFLT64 <= range[1]) + { + GDXRegion[regionID]->StopVertical[j] = i; + break; + } + } + } + else + { + status = -1; + } + break; + + } + free(vertArr); + } + } + } + } + if (status == -1) + { + regionID = -1; + } + free(dimlist); + return (regionID); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDdeftimeperiod | +| | +| DESCRIPTION: Finds elements of the "Time" field within a given time | +| period. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| periodID int32 Period ID | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| periodID int32 Period ID | +| starttime float64 TAI sec Start of time period | +| stoptime float64 TAI sec Stop of time period | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Aug 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDdeftimeperiod(int32 gridID, int32 periodID, float64 starttime, + float64 stoptime) +{ + float64 timerange[2]; + + timerange[0] = starttime; + timerange[1] = stoptime; + + periodID = GDdefvrtregion(gridID, periodID, "Time", timerange); + + return (periodID); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDgetpixels | +| | +| DESCRIPTION: Finds row and columns for specified lon/lat values | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| nLonLat int32 Number of lonlat values | +| lonVal float64 dec deg Longitude values | +| latVal float64 dec deg Latitude values | +| | +| | +| OUTPUTS: | +| pixRow int32 Pixel rows | +| pixCol int32 Pixel columns | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Aug 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Set row/col to -1 if outside boundary | +| Mar 97 Joel Gales Adjust row/col for CORNER pixel registration | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDgetpixels(int32 gridID, int32 nLonLat, float64 lonVal[], float64 latVal[], + int32 pixRow[], int32 pixCol[]) +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + + int32 xdimsize; /* Size of "XDim" */ + int32 ydimsize; /* Size of "YDim" */ + int32 projcode; /* GCTP projection code */ + int32 zonecode; /* Zone code */ + int32 spherecode; /* Sphere code */ + int32 origincode; /* Origin code */ + int32 pixregcode; /* Pixel registration code */ + + float64 upleftpt[2];/* Upper left point */ + float64 lowrightpt[2]; /* Lower right point */ + float64 projparm[16]; /* Projection parameters */ + float64 *xVal; /* Pointer to point x location values */ + float64 *yVal; /* Pointer to point y location values */ + + + /* Check for valid grid ID */ + /* ----------------------- */ + status = GDchkgdid(gridID, "GDgetpixels", &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + /* Get grid info */ + /* ------------- */ + status = GDgridinfo(gridID, &xdimsize, &ydimsize, + upleftpt, lowrightpt); + + + /* Get projection info */ + /* ------------------- */ + status = GDprojinfo(gridID, &projcode, &zonecode, + &spherecode, projparm); + + + /* Get explicit upleftpt & lowrightpt if defaults are used */ + /* ------------------------------------------------------- */ + status = GDgetdefaults(projcode, zonecode, projparm, spherecode, + upleftpt, lowrightpt); + + + /* Get pixel registration and origin info */ + /* -------------------------------------- */ + status = GDorigininfo(gridID, &origincode); + status = GDpixreginfo(gridID, &pixregcode); + + + /* Allocate space for x & y locations */ + /* ---------------------------------- */ + xVal = (float64 *) calloc(nLonLat, sizeof(float64)); + if(xVal == NULL) + { + HEpush(DFE_NOSPACE,"GDgetpixels", __FILE__, __LINE__); + return(-1); + } + yVal = (float64 *) calloc(nLonLat, sizeof(float64)); + if(yVal == NULL) + { + HEpush(DFE_NOSPACE,"GDgetpixels", __FILE__, __LINE__); + free(xVal); + return(-1); + } + + + /* Get pixRow, pixCol, xVal, & yVal */ + /* -------------------------------- */ + status = GDll2ij(projcode, zonecode, projparm, spherecode, + xdimsize, ydimsize, upleftpt, lowrightpt, + nLonLat, lonVal, latVal, pixRow, pixCol, + xVal, yVal); + + + + /* Loop through all lon/lat values */ + /* ------------------------------- */ + for (i = 0; i < nLonLat; i++) + { + /* Adjust columns & rows for "corner" registered grids */ + /* --------------------------------------------------- */ + if (pixregcode == HDFE_CORNER) + { + if (origincode == HDFE_GD_UL) + { + if (xVal[i] - pixCol[i] > 0.5) + { + ++pixCol[i]; + } + + if (yVal[i] - pixRow[i] > 0.5) + { + ++pixRow[i]; + } + } + else if (origincode == HDFE_GD_UR) + { + if (xVal[i] - pixCol[i] <= 0.5) + { + --pixCol[i]; + } + + if (yVal[i] - pixRow[i] > 0.5) + { + ++pixRow[i]; + } + } + else if (origincode == HDFE_GD_LL) + { + if (xVal[i] - pixCol[i] > 0.5) + { + ++pixCol[i]; + } + + if (yVal[i] - pixRow[i] <= 0.5) + { + --pixRow[i]; + } + } + else if (origincode == HDFE_GD_LR) + { + if (xVal[i] - pixCol[i] <= 0.5) + { + --pixCol[i]; + } + + if (yVal[i] - pixRow[i] <= 0.5) + { + --pixRow[i]; + } + } + } + + + /* If outside grid boundaries then set to -1 */ + /* ----------------------------------------- */ + if (pixCol[i] < 0 || pixCol[i] >= xdimsize || + pixRow[i] < 0 || pixRow[i] >= ydimsize) + { + pixCol[i] = -1; + pixRow[i] = -1; + } + } + free(xVal); + free(yVal); + } + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDgetpixvalues | +| | +| DESCRIPTION: Retrieves data from specified pixels. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| size*nPixels int32 Size of data buffer | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| nPixels int32 Number of pixels | +| pixRow int32 Pixel row numbers | +| pixCol int32 Pixel column numbers | +| fieldname char Fieldname | +| | +| OUTPUTS: | +| buffer void Data buffer | +| | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Aug 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Check for pixels outside boundaries (-1) | +| Mar 98 Abe Taaheri revised to reduce overhead for rechecking | +| for gridid, fieldname, etc in GDreadfield. | +| June 98 AT fixed bug with 2-dim field merged in 3-dim field | +| (for offset and count) | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDgetpixvalues(int32 gridID, int32 nPixels, int32 pixRow[], int32 pixCol[], + char *fieldname, VOIDP buffer) +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + + int32 start[8]; /* GDreadfield start array */ + int32 edge[8]; /* GDreadfield edge array */ + int32 dims[8]; /* Field dimensions */ + int32 rank; /* Field rank */ + int32 xdum; /* Location of "XDim" within field list */ + int32 ydum; /* Location of "YDim" within field list */ + int32 ntype; /* Field number type */ + int32 origincode; /* Origin code */ + int32 bufOffset; /* Data buffer offset */ + int32 size; /* Size of returned data buffer for each + * value in bytes */ + int32 offset[8]; /* I/O offset (start) */ + int32 incr[8]; /* I/O increment (stride) */ + int32 count[8]; /* I/O count (edge) */ + int32 sdid; /* SDS ID */ + int32 rankSDS; /* Rank of SDS */ + int32 rankFld; /* Rank of field */ + int32 dum; /* Dummy variable */ + int32 mrgOffset; /* Merged field offset */ + + char *dimlist; /* Dimension list */ + + + + /* Allocate space for dimlist */ + /* --------------------------------- */ + dimlist = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(dimlist == NULL) + { + HEpush(DFE_NOSPACE,"GDgetpixvalues", __FILE__, __LINE__); + return(-1); + } + /* Check for valid grid ID */ + /* ----------------------- */ + status = GDchkgdid(gridID, "GDgetpixvalues", + &fid, &sdInterfaceID, &gdVgrpID); + + + if (status == 0) + { + /* Get field list */ + /* -------------- */ + status = GDfieldinfo(gridID, fieldname, &rank, dims, &ntype, dimlist); + + + /* Check for "XDim" & "YDim" in dimension list */ + /* ------------------------------------------- */ + if (status == 0) + { + xdum = EHstrwithin("XDim", dimlist, ','); + ydum = EHstrwithin("YDim", dimlist, ','); + + if (xdum == -1) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetpixvalues", __FILE__, __LINE__); + HEreport( + "\"XDim\" not present in dimlist for field: \"%s\".\n", + fieldname); + } + + + if (ydum == -1) + { + status = -1; + HEpush(DFE_GENAPP, "GDgetpixvalues", __FILE__, __LINE__); + HEreport( + "\"YDim\" not present in dimlist for field: \"%s\".\n", + fieldname); + } + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDgetpixvalues", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" not found.\n", fieldname); + } + + + if (status == 0) + { + + /* Get origin order info */ + /* --------------------- */ + status = GDorigininfo(gridID, &origincode); + + + /* Initialize start & edge arrays */ + /* ------------------------------ */ + for (i = 0; i < rank; i++) + { + start[i] = 0; + edge[i] = dims[i]; + } + + + /* Compute size of data buffer for each pixel */ + /* ------------------------------------------ */ + edge[xdum] = 1; + edge[ydum] = 1; + size = edge[0]; + for (j = 1; j < rank; j++) + { + size *= edge[j]; + } + size *= DFKNTsize(ntype); + + + + /* If data values are requested ... */ + /* -------------------------------- */ + if (buffer != NULL) + { + /* get sdid */ + status = GDSDfldsrch(gridID, sdInterfaceID, fieldname, &sdid, + &rankSDS, &rankFld, &mrgOffset, dims, &dum); + + /* Loop through all pixels */ + /* ----------------------- */ + for (i = 0; i < nPixels; i++) + { + /* Conmpute offset within returned data buffer */ + /* ------------------------------------------- */ + bufOffset = size * i; + + + /* If pixel row & column OK ... */ + /* ---------------------------- */ + if (pixCol[i] != -1 && pixRow[i] != -1) + { + start[xdum] = pixCol[i]; + start[ydum] = pixRow[i]; + + + /* Adjust X-dim start if origin on right edge */ + /* ------------------------------------------ */ + if ((origincode & 1) == 1) + { + start[xdum] = dims[xdum] - (start[xdum] + 1); + } + + + /* Adjust Y-dim start if origin on lower edge */ + /* ------------------------------------------ */ + if ((origincode & 2) == 2) + { + start[ydum] = dims[ydum] - (start[ydum] + 1); + } + + /* Set I/O offset and count Section */ + /* ---------------------- */ + + /* + * start and edge != NULL, set I/O offset and count to + * user values, adjusting the + * 0th field with the merged field offset (if any) + */ + if (rankFld == rankSDS) + { + for (j = 0; j < rankSDS; j++) + { + offset[j] = start[j]; + count[j] = edge[j]; + } + offset[0] += mrgOffset; + } + else + { + /* + * If field really 2-dim merged in 3-dim field then set + * 0th field offset to merge offset and then next two to + * the user values + */ + for (j = 0; j < rankFld; j++) + { + offset[j + 1] = start[j]; + count[j + 1] = edge[j]; + } + offset[0] = mrgOffset; + count[0] = 1; + } + + + + /* Set I/O stride Section */ + /* ---------------------- */ + + /* In original code stride enetred as NULL. + Abe Taaheri June 12, 1998 */ + /* + * If stride == NULL (default) set I/O stride to 1 + */ + for (j = 0; j < rankSDS; j++) + { + incr[j] = 1; + } + + + /* Read into data buffer */ + /* --------------------- */ + status = SDreaddata(sdid, + offset, incr, count, + (VOIDP) ((uint8 *) buffer + bufOffset)); + } + } + } + } + } + + + /* If successful return size of returned data in bytes */ + /* --------------------------------------------------- */ + if (status == 0) + { + free(dimlist); + return (size * nPixels); + } + else + { + free(dimlist); + return ((int32) status); + } +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDinterpolate | +| | +| DESCRIPTION: Performs bilinear interpolate on a set of xy values | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nRetn*nValues* int32 Size of data buffer | +| sizeof(float64) | +| | +| INPUTS: | +| gridID int32 Grid structure ID | +| nValues int32 Number of lon/lat points to interpolate | +| xyValues float64 XY values of points to interpolate | +| fieldname char Fieldname | +| | +| OUTPUTS: | +| interpVal float64 Interpolated Data Values | +| | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Aug 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Fix array index problem with interpVal write | +| Apr 97 Joel Gales Trap interpolation boundary out of bounds error | +| Jun 98 Abe Taaheri changed the return value so that the Return Value | +| is size in bytes for the data buffer which is | +| float64. +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +GDinterpolate(int32 gridID, int32 nValues, float64 lonVal[], float64 latVal[], + char *fieldname, float64 interpVal[]) +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn k; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int32 xdimsize; /* XDim size */ + int32 ydimsize; /* YDim size */ + int32 projcode; /* Projection code */ + int32 zonecode; /* Zone code */ + int32 spherecode; /* Sphere code */ + int32 pixregcode; /* Pixel registration code */ + int32 origincode; /* Origin code */ + int32 dims[8]; /* Field dimensions */ + int32 numsize; /* Size in bytes of number type */ + int32 rank; /* Field rank */ + int32 xdum; /* Location of "XDim" within field list */ + int32 ydum; /* Location of "YDim" within field list */ + int32 ntype; /* Number type */ + int32 dum; /* Dummy variable */ + int32 size; /* Size of returned data buffer for each + * value in bytes */ + int32 pixCol[4]; /* Pixel columns for 4 nearest neighbors */ + int32 pixRow[4]; /* Pixel rows for 4 nearest neighbors */ + int32 tDen; /* Interpolation denominator value 1 */ + int32 uDen; /* Interpolation denominator value 2 */ + int32 nRetn; /* Number of data values returned */ + + float64 upleftpt[2];/* Upper left pt coordinates */ + float64 lowrightpt[2]; /* Lower right pt coordinates */ + float64 projparm[16]; /* Projection parameters */ + float64 xVal; /* "Exact" x location of interpolated point */ + float64 yVal; /* "Exact" y location of interpolated point */ + float64 tNum = 0.0; /* Interpolation numerator value 1 */ + float64 uNum = 0.0; /* Interpolation numerator value 2 */ + + int16 i16[4]; /* Working buffer (int16) */ + int32 i32[4]; /* Working buffer (int132) */ + float32 f32[4]; /* Working buffer (float32) */ + float64 f64[4]; /* Working buffer (float64) */ + + char *pixVal; /* Nearest neighbor values */ + char *dimlist; /* Dimension list */ + + /* Allocate space for dimlist */ + /* --------------------------------- */ + dimlist = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(dimlist == NULL) + { + HEpush(DFE_NOSPACE,"GDinterpolate", __FILE__, __LINE__); + return(-1); + } + /* Check for valid grid ID */ + /* ----------------------- */ + status = GDchkgdid(gridID, "GDinterpolate", + &fid, &sdInterfaceID, &gdVgrpID); + + + /* If no problems ... */ + /* ------------------ */ + if (status == 0) + { + /* Get field information */ + /* --------------------- */ + status = GDfieldinfo(gridID, fieldname, &rank, dims, &ntype, dimlist); + + + /* Check for "XDim" & "YDim" in dimension list */ + /* ------------------------------------------- */ + if (status == 0) + { + xdum = EHstrwithin("XDim", dimlist, ','); + ydum = EHstrwithin("YDim", dimlist, ','); + + if (xdum == -1) + { + status = -1; + HEpush(DFE_GENAPP, "GDinterpolate", __FILE__, __LINE__); + HEreport( + "\"XDim\" not present in dimlist for field: \"%s\".\n", + fieldname); + } + + + if (ydum == -1) + { + status = -1; + HEpush(DFE_GENAPP, "GDinterpolate", __FILE__, __LINE__); + HEreport( + "\"YDim\" not present in dimlist for field: \"%s\".\n", + fieldname); + } + } + else + { + /* Fieldname not found in grid */ + /* --------------------------- */ + status = -1; + HEpush(DFE_GENAPP, "GDinterpolate", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" not found.\n", fieldname); + } + + + /* If no problems ... */ + /* ------------------ */ + if (status == 0) + { + /* Compute size of data buffer for each interpolated value */ + /* ------------------------------------------------------- */ + dims[xdum] = 1; + dims[ydum] = 1; + size = dims[0]; + for (i = 1; i < rank; i++) + { + size *= dims[i]; + } + numsize = DFKNTsize(ntype); + size *= numsize; + + nRetn = size / numsize; + + + + /* If interpolated values are requested ... */ + /* ---------------------------------------- */ + if (interpVal != NULL) + { + /* Get grid info */ + /* ------------- */ + status = GDgridinfo(gridID, &xdimsize, &ydimsize, + upleftpt, lowrightpt); + + + /* Get projection info */ + /* ------------------- */ + status = GDprojinfo(gridID, &projcode, &zonecode, + &spherecode, projparm); + + + /* Get explicit upleftpt & lowrightpt if defaults are used */ + /* ------------------------------------------------------- */ + status = GDgetdefaults(projcode, zonecode, projparm, + spherecode, upleftpt, lowrightpt); + + + /* Get pixel registration and origin info */ + /* -------------------------------------- */ + status = GDpixreginfo(gridID, &pixregcode); + status = GDorigininfo(gridID, &origincode); + + + + /* Loop through all interpolated points */ + /* ------------------------------------ */ + for (i = 0; i < nValues; i++) + { + /* Get row & column of point pixel */ + /* ------------------------------- */ + status = GDll2ij(projcode, zonecode, projparm, spherecode, + xdimsize, ydimsize, upleftpt, lowrightpt, + 1, &lonVal[i], &latVal[i], + pixRow, pixCol, &xVal, &yVal); + + + /* Get diff of interp. point from pixel location */ + /* --------------------------------------------- */ + if (pixregcode == HDFE_CENTER) + { + tNum = xVal - (pixCol[0] + 0.5); + uNum = yVal - (pixRow[0] + 0.5); + } + else if (origincode == HDFE_GD_UL) + { + tNum = xVal - pixCol[0]; + uNum = yVal - pixRow[0]; + } + else if (origincode == HDFE_GD_UR) + { + tNum = xVal - (pixCol[0] + 1); + uNum = yVal - pixRow[0]; + } + else if (origincode == HDFE_GD_LL) + { + tNum = xVal - pixCol[0]; + uNum = yVal - (pixRow[0] + 1); + } + else if (origincode == HDFE_GD_LR) + { + tNum = xVal - (pixCol[0] + 1); + uNum = yVal - (pixRow[0] + 1); + } + + + /* Get rows and columns of other nearest neighbor pixels */ + /* ----------------------------------------------------- */ + pixCol[1] = pixCol[0]; + pixRow[3] = pixRow[0]; + + if (tNum >= 0) + { + pixCol[2] = pixCol[0] + 1; + pixCol[3] = pixCol[0] + 1; + } + + if (tNum < 0) + { + pixCol[2] = pixCol[0] - 1; + pixCol[3] = pixCol[0] - 1; + } + + if (uNum >= 0) + { + pixRow[2] = pixRow[0] + 1; + pixRow[1] = pixRow[0] + 1; + } + + if (uNum < 0) + { + pixRow[2] = pixRow[0] - 1; + pixRow[1] = pixRow[0] - 1; + } + + + /* Get values of nearest neighbors */ + /* -------------------------------- */ + pixVal = (char *) malloc(4 * size); + if(pixVal == NULL) + { + HEpush(DFE_NOSPACE,"GDinterpolate", __FILE__, __LINE__); + free(dimlist); + return(-1); + } + dum = GDgetpixvalues(gridID, 4, pixRow, pixCol, + fieldname, pixVal); + + + /* Trap interpolation boundary out of range error */ + /* ---------------------------------------------- */ + if (dum == -1) + { + status = -1; + HEpush(DFE_GENAPP, "GDinterpolate", __FILE__, __LINE__); + HEreport("Interpolation boundary outside of grid.\n"); + } + else + { + + /* + * Algorithm taken for Numerical Recipies in C, 2nd + * edition, Section 3.6 + */ + + /* Perform bilinear interpolation */ + /* ------------------------------ */ + tDen = pixCol[3] - pixCol[0]; + uDen = pixRow[1] - pixRow[0]; + + switch (ntype) + { + case DFNT_INT16: + + + /* Loop through all returned data values */ + /* ------------------------------------- */ + for (j = 0; j < nRetn; j++) + { + /* Copy 4 NN values into working array */ + /* ----------------------------------- */ + for (k = 0; k < 4; k++) + { + memcpy(&i16[k], + pixVal + j * numsize + k * size, + sizeof(int16)); + } + + /* Compute interpolated value */ + /* -------------------------- */ + interpVal[i * nRetn + j] = + (1 - tNum / tDen) * (1 - uNum / uDen) * + i16[0] + + (tNum / tDen) * (1 - uNum / uDen) * + i16[3] + + (tNum / tDen) * (uNum / uDen) * + i16[2] + + (1 - tNum / tDen) * (uNum / uDen) * + i16[1]; + } + break; + + + case DFNT_INT32: + + for (j = 0; j < nRetn; j++) + { + for (k = 0; k < 4; k++) + { + memcpy(&i32[k], + pixVal + j * numsize + k * size, + sizeof(int32)); + } + + interpVal[i * nRetn + j] = + (1 - tNum / tDen) * (1 - uNum / uDen) * + i32[0] + + (tNum / tDen) * (1 - uNum / uDen) * + i32[3] + + (tNum / tDen) * (uNum / uDen) * + i32[2] + + (1 - tNum / tDen) * (uNum / uDen) * + i32[1]; + } + break; + + + case DFNT_FLOAT32: + + for (j = 0; j < nRetn; j++) + { + for (k = 0; k < 4; k++) + { + memcpy(&f32[k], + pixVal + j * numsize + k * size, + sizeof(float32)); + } + + interpVal[i * nRetn + j] = + (1 - tNum / tDen) * (1 - uNum / uDen) * + f32[0] + + (tNum / tDen) * (1 - uNum / uDen) * + f32[3] + + (tNum / tDen) * (uNum / uDen) * + f32[2] + + (1 - tNum / tDen) * (uNum / uDen) * + f32[1]; + } + break; + + + case DFNT_FLOAT64: + + for (j = 0; j < nRetn; j++) + { + for (k = 0; k < 4; k++) + { + memcpy(&f64[k], + pixVal + j * numsize + k * size, + sizeof(float64)); + } + + interpVal[i * nRetn + j] = + (1 - tNum / tDen) * (1 - uNum / uDen) * + f64[0] + + (tNum / tDen) * (1 - uNum / uDen) * + f64[3] + + (tNum / tDen) * (uNum / uDen) * + f64[2] + + (1 - tNum / tDen) * (uNum / uDen) * + f64[1]; + } + break; + } + } + free(pixVal); + } + } + } + } + + + /* If successful return size of returned data in bytes */ + /* --------------------------------------------------- */ + if (status == 0) + { + /*always return size of float64 buffer */ + free(dimlist); + return (nRetn * nValues * sizeof(float64)); + } + else + { + free(dimlist); + return ((int32) status); + } + +} +/*********************************************** +GDwrrdtile -- + This function is the underlying function below GDwritetile and + GDreadtile. + + +Author-- +Alexis Zubrow + +********************************************************/ + +static intn +GDwrrdtile(int32 gridID, char *fieldname, char *code, int32 start[], + VOIDP datbuf) +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 sdid; /* SDS ID */ + + int32 dum; /* Dummy variable */ + int32 rankSDS; /* Rank of SDS/Field */ + + int32 dims[8]; /* Field/SDS dimensions */ + int32 tileFlags; /* flag to determine if field is tiled */ + int32 numTileDims;/* number of tiles spanning a dimension */ + HDF_CHUNK_DEF tileDef; /* union holding tiling info. */ + + + /* Get gridID */ + status = GDchkgdid(gridID, "GDwrrdtile", &fid, &sdInterfaceID, &dum); + if (status == 0) + { + + /* Get field info */ + status = GDfieldinfo(gridID, fieldname, &rankSDS, dims, &dum, NULL); + + if (status == 0) + { + + /* Check whether fieldname is in SDS (multi-dim field) */ + /* --------------------------------------------------- */ + status = GDSDfldsrch(gridID, sdInterfaceID, fieldname, &sdid, + &rankSDS, &dum, &dum, dims, &dum); + + + + /* + * Check for errors in parameters passed to GDwritetile or + * GDreadtile + */ + + /* Check if untiled field */ + status = SDgetchunkinfo(sdid, &tileDef, &tileFlags); + if (tileFlags == HDF_NONE) + { + HEpush(DFE_GENAPP, "GDwrrdtile", __FILE__, __LINE__); + HEreport("Field \"%s\" is not tiled.\n", fieldname); + status = -1; + return (status); + + } + + /* + * Check if rd/wr tilecoords are within the extent of the field + */ + for (i = 0; i < rankSDS; i++) + { + /* + * Calculate the number of tiles which span a dimension of + * the field + */ + numTileDims = dims[i] / tileDef.chunk_lengths[i]; + if ((start[i] >= numTileDims) || (start[i] < 0)) + { + /* + * ERROR INDICATING BEYOND EXTENT OF THAT DIMENSION OR + * NEGATIVE TILECOORDS + */ + HEpush(DFE_GENAPP, "GDwrrdtile", __FILE__, __LINE__); + HEreport("Tilecoords for dimension \"%d\" ...\n", i); + HEreport("is beyond the extent of dimension length\n"); + status = -1; + + } + } + + if (status == -1) + { + return (status); + } + + + /* Actually write/read to the field */ + + if (strcmp(code, "w") == 0) /* write tile */ + { + status = SDwritechunk(sdid, start, (VOIDP) datbuf); + } + else if (strcmp(code, "r") == 0) /* read tile */ + { + status = SDreadchunk(sdid, start, (VOIDP) datbuf); + } + + + } + + /* Non-existent fieldname */ + else + { + HEpush(DFE_GENAPP, "GDwrrdtile", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" does not exist.\n", fieldname); + status = -1; + } + + } + + return (status); +} + + +/*********************************************** +GDtileinfo -- + This function queries the field to determine if it is tiled. If it is + tile, one can retrieve some of the characteristics of the tiles. + +Author-- Alexis Zubrow + +********************************************************/ + + +intn +GDtileinfo(int32 gridID, char *fieldname, int32 * tilecode, int32 * tilerank, + int32 tiledims[]) + +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 sdid; /* SDS ID */ + + int32 dum; /* Dummy variable */ + int32 rankSDS; /* Rank of SDS/Field/tile */ + + int32 dims[8]; /* Field/SDS dimensions */ + int32 tileFlags; /* flag to determine if field is tiled */ + HDF_CHUNK_DEF tileDef; /* union holding tiling info. */ + + + /* Check if improper gridID */ + status = GDchkgdid(gridID, "GDtileinfo", &fid, &sdInterfaceID, &dum); + if (status == 0) + { + + /* Get field info */ + status = GDfieldinfo(gridID, fieldname, &rankSDS, dims, &dum, NULL); + + if (status == 0) + { + + /* Check whether fieldname is in SDS (multi-dim field) */ + /* --------------------------------------------------- */ + status = GDSDfldsrch(gridID, sdInterfaceID, fieldname, &sdid, + &rankSDS, &dum, &dum, dims, &dum); + + + + /* Query field for tiling information */ + status = SDgetchunkinfo(sdid, &tileDef, &tileFlags); + + /* If field is untiled, return untiled flag */ + if (tileFlags == HDF_NONE) + { + *tilecode = HDFE_NOTILE; + return (status); + } + + /* IF field is tiled or tiled with compression */ + else if ((tileFlags == HDF_CHUNK) || + (tileFlags == (HDF_CHUNK | HDF_COMP))) + { + if (tilecode != NULL) + { + *tilecode = HDFE_TILE; + } + if (tilerank != NULL) + { + *tilerank = rankSDS; + } + if (tiledims != NULL) + { + /* Assign size of tile dimensions */ + for (i = 0; i < rankSDS; i++) + { + tiledims[i] = tileDef.chunk_lengths[i]; + } + } + } + } + + /* Non-existent fieldname */ + else + { + HEpush(DFE_GENAPP, "GDtileinfo", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" does not exist.\n", fieldname); + status = -1; + } + + } + return (status); +} +/*********************************************** +GDwritetile -- + This function writes one tile to a particular field. + + +Author-- +Alexis Zubrow + +********************************************************/ + +intn +GDwritetile(int32 gridID, char *fieldname, int32 tilecoords[], + VOIDP tileData) +{ + char code[] = "w"; /* write tile code */ + intn status = 0; /* routine return status variable */ + + status = GDwrrdtile(gridID, fieldname, code, tilecoords, tileData); + + return (status); +} +/*********************************************** +GDreadtile -- + This function reads one tile from a particular field. + + +Author-- +Alexis Zubrow + +********************************************************/ + +intn +GDreadtile(int32 gridID, char *fieldname, int32 tilecoords[], + VOIDP tileData) +{ + char code[] = "r"; /* read tile code */ + intn status = 0; /* routine return status variable */ + + status = GDwrrdtile(gridID, fieldname, code, tilecoords, tileData); + + return (status); +} +/*********************************************** +GDsettilecache -- + This function sets the cache size for a tiled field. + + +Author-- +Alexis Zubrow + +********************************************************/ + +intn +GDsettilecache(int32 gridID, char *fieldname, int32 maxcache, int32 cachecode) +{ + + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 sdid; /* SDS ID */ + + int32 dum; /* Dummy variable */ + + int32 dims[8]; /* Field/SDS dimensions */ + + + /* Check gridID */ + status = GDchkgdid(gridID, "GDwrrdtile", &fid, &sdInterfaceID, &dum); + if (status == 0) + { + + /* Get field info */ + status = GDfieldinfo(gridID, fieldname, &dum, dims, &dum, NULL); + + if (status == 0) + { + + /* Check whether fieldname is in SDS (multi-dim field) */ + /* --------------------------------------------------- */ + status = GDSDfldsrch(gridID, sdInterfaceID, fieldname, &sdid, + &dum, &dum, &dum, dims, &dum); + + + /* Check if maxcache is less than or equal to zero */ + if (maxcache <= 0) + { + HEpush(DFE_GENAPP, "GDsettilecache", __FILE__, __LINE__); + HEreport("Improper maxcache \"%d\"... \n", maxcache); + HEreport("maxcache must be greater than zero.\n"); + status = -1; + return (status); + } + + + /* Set the number of tiles to cache */ + /* Presently, the only cache flag allowed is 0 */ + status = SDsetchunkcache(sdid, maxcache, 0); + + + } + + /* Non-existent fieldname */ + else + { + HEpush(DFE_GENAPP, "GDwrrdtile", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" does not exist.\n", fieldname); + status = -1; + } + + } + + return (status); +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDsettilecomp | +| | +| DESCRIPTION: Sets the tiling/compression parameters for the specified | +| field. This can be called after GDsetfillvalue and assumes | +| that the field was defined with no compression/tiling set | +| by GDdeftile or GDdefcomp. | +| | +| This function replaces the following sequence: | +| GDdefcomp | +| GDdeftile | +| GDdeffield | +| GDsetfillvalue | +| with: | +| GDdeffield | +| GDsetfillvalue | +| GDsettilecomp | +| so that fill values will work correctly. | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| gridID int32 grid structure ID | +| fieldname char field name | +| tilerank int32 number of tiling dimensions | +| tiledims int32 tiling dimensions | +| compcode int32 compression code | +| compparm intn compression parameters | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 98 MISR Used GDsetfillvalue as a template and copied | +| tiling/comp portions of GDdeffield.(NCR15866). | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +GDsettilecomp(int32 gridID, char *fieldname, int32 tilerank, int32* + tiledims, int32 compcode, intn* compparm) +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 gdVgrpID; /* Grid root Vgroup ID */ + int i; /* Looping variable. */ + int32 sdid; /* SDS id */ + int32 nt; /* Number type */ + int32 dims[8]; /* Dimensions array */ + int32 dum; /* Dummy variable */ + int32 solo; /* "Solo" (non-merged) field flag */ + comp_info c_info; /* Compression parameter structure */ + HDF_CHUNK_DEF chunkDef; /* Tiling structure */ + int32 chunkFlag; /* Chunking (Tiling) flag */ + + /* Check for valid grid ID and get SDS interface ID */ + status = GDchkgdid(gridID, "GDsetfillvalue", + &fid, &sdInterfaceID, &gdVgrpID); + + if (status == 0) + { + /* Get field info */ + status = GDfieldinfo(gridID, fieldname, &dum, dims, &nt, NULL); + + if (status == 0) + { + /* Get SDS ID and solo flag */ + status = GDSDfldsrch(gridID, sdInterfaceID, fieldname, + &sdid, &dum, &dum, &dum, + dims, &solo); + if (status !=0) { + HEpush(DFE_GENAPP, "GDsettilecomp", __FILE__, __LINE__); + HEreport("GDSDfldsrch failed\n", fieldname); + return FAIL; + } + /* Tiling with Compression */ + /* ----------------------- */ + + + /* Setup Compression */ + /* ----------------- */ + if (compcode == HDFE_COMP_NBIT) + { + c_info.nbit.nt = nt; + c_info.nbit.sign_ext = compparm[0]; + c_info.nbit.fill_one = compparm[1]; + c_info.nbit.start_bit = compparm[2]; + c_info.nbit.bit_len = compparm[3]; + } + else if (compcode == HDFE_COMP_SKPHUFF) + { + c_info.skphuff.skp_size = (intn) DFKNTsize(nt); + } + else if (compcode == HDFE_COMP_DEFLATE) + { + c_info.deflate.level = compparm[0]; + } + + /* Setup chunk lengths */ + /* ------------------- */ + for (i = 0; i < tilerank; i++) + { + chunkDef.comp.chunk_lengths[i] = tiledims[i]; + } + + /* Setup chunk flag & chunk compression type */ + /* ----------------------------------------- */ + chunkFlag = HDF_CHUNK | HDF_COMP; + chunkDef.comp.comp_type = compcode; + + /* Setup chunk compression parameters */ + /* ---------------------------------- */ + if (compcode == HDFE_COMP_SKPHUFF) + { + chunkDef.comp.cinfo.skphuff.skp_size = + c_info.skphuff.skp_size; + } + else if (compcode == HDFE_COMP_DEFLATE) + { + chunkDef.comp.cinfo.deflate.level = + c_info.deflate.level; + } + /* Call SDsetchunk routine */ + /* ----------------------- */ + status = SDsetchunk(sdid, chunkDef, chunkFlag); + if (status ==FAIL) { + HEpush(DFE_GENAPP, "GDsettilecomp", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" does not exist.\n", + fieldname); + return status; + } + } + else + { + HEpush(DFE_GENAPP, "GDsettilecomp", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" does not exist.\n", fieldname); + } + } + return (status); +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDll2mm_cea | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| projcode int32 GCTP projection code | +| zonecode int32 UTM zone code | +| projparm float64 Projection parameters | +| spherecode int32 GCTP spheriod code | +| xdimsize int32 xdimsize from GDcreate | +| ydimsize int32 ydimsize from GDcreate | +| upleftpt float64 upper left corner coordinates (DMS) | +| lowrightpt float64 lower right corner coordinates (DMS) | +| longitude float64 longitude array (DMS) | +| latitude float64 latitude array (DMS) | +| npnts int32 number of lon-lat points | +| | +| OUTPUTS: | +| x float64 X value array | +| y float64 Y value array | +| scaleX float64 X grid size | +| scaley float64 Y grid size | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Oct 02 Abe Taaheri Added support for EASE grid | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn GDll2mm_cea(int32 projcode,int32 zonecode, int32 spherecode, + float64 projparm[], + int32 xdimsize, int32 ydimsize, + float64 upleftpt[], float64 lowrightpt[], int32 npnts, + float64 lon[],float64 lat[], + float64 x[],float64 y[], float64 *scaleX,float64 *scaleY) +{ + intn status = 0; /* routine return status variable */ + int32 errorcode = 0; /* GCTP error code */ + float64 xMtr0, xMtr1, yMtr0, yMtr1; + float64 lonrad0; /* Longitude in radians of upleft point */ + float64 latrad0; /* Latitude in radians of upleft point */ + float64 lonrad; /* Longitude in radians of point */ + float64 latrad; /* Latitude in radians of point */ + int32(*for_trans[100]) (); /* GCTP function pointer */ + + if(npnts <= 0) + { + HEpush(DFE_GENAPP, " GDll2mm_cea", __FILE__, __LINE__); + HEreport("Improper npnts value\"%d\"... \n", npnts); + HEreport("npnts must be greater than zero.\n"); + status = -1; + return (status); + } + if ( projcode == GCTP_BCEA) + { + for_init(projcode, zonecode, projparm, spherecode, NULL, NULL, + &errorcode, for_trans); + /* Convert upleft and lowright X coords from DMS to radians */ + /* -------------------------------------------------------- */ + + lonrad0 = EHconvAng(upleftpt[0], HDFE_DMS_RAD); + lonrad = EHconvAng(lowrightpt[0], HDFE_DMS_RAD); + + /* Convert upleft and lowright Y coords from DMS to radians */ + /* -------------------------------------------------------- */ + latrad0 = EHconvAng(upleftpt[1], HDFE_DMS_RAD); + latrad = EHconvAng(lowrightpt[1], HDFE_DMS_RAD); + + /* Convert from lon/lat to meters(or whatever unit is, i.e unit + of r_major and r_minor) using GCTP */ + /* ----------------------------------------- */ + errorcode = for_trans[projcode] (lonrad0, latrad0, &xMtr0, &yMtr0); + x[0] = xMtr0; + y[0] = yMtr0; + + /* Report error if any */ + /* ------------------- */ + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDll2mm_cea", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + /* Convert from lon/lat to meters(or whatever unit is, i.e unit + of r_major and r_minor) using GCTP */ + /* ----------------------------------------- */ + errorcode = for_trans[projcode] (lonrad, latrad, &xMtr1, &yMtr1); + x[1] = xMtr1; + y[1] = yMtr1; + + /* Report error if any */ + /* ------------------- */ + if (errorcode != 0) + { + status = -1; + HEpush(DFE_GENAPP, "GDll2mm_cea", __FILE__, __LINE__); + HEreport("GCTP Error: %d\n", errorcode); + return (status); + } + + /* Compute x scale factor */ + /* ---------------------- */ + *scaleX = (xMtr1 - xMtr0) / xdimsize; + + /* Compute y scale factor */ + /* ---------------------- */ + *scaleY = (yMtr1 - yMtr0) / ydimsize; + } + else + { + status = -1; + HEpush(DFE_GENAPP, "GDll2mm_cea", __FILE__, __LINE__); + HEreport("Wrong projection code; this function is only for EASE grid"); + return (status); + } + return (0); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: GDmm2ll_cea | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| projcode int32 GCTP projection code | +| zonecode int32 UTM zone code | +| projparm float64 Projection parameters | +| spherecode int32 GCTP spheriod code | +| xdimsize int32 xdimsize from GDcreate | +| ydimsize int32 ydimsize from GDcreate | +| upleftpt float64 upper left corner coordinates (DMS) | +| lowrightpt float64 lower right corner coordinates (DMS) | +| x float64 X value array | +| y float64 Y value array | +| npnts int32 number of x-y points | +| | +| OUTPUTS: | +| longitude float64 longitude array (DMS) | +| latitude float64 latitude array (DMS) | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Oct 02 Abe Taaheri Added support for EASE grid | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn GDmm2ll_cea(int32 projcode,int32 zonecode, int32 spherecode, + float64 projparm[], + int32 xdimsize, int32 ydimsize, + float64 upleftpt[], float64 lowrightpt[], int32 npnts, + float64 x[], float64 y[], + float64 longitude[], float64 latitude[]) +{ + intn status = 0; /* routine return status variable */ + int32 errorcode = 0; /* GCTP error code */ + int32(*inv_trans[100]) (); /* GCTP function pointer */ + int32 i; + + if(npnts <= 0) + { + HEpush(DFE_GENAPP, " GDmm2ll_cea", __FILE__, __LINE__); + HEreport("Improper npnts value\"%d\"... \n", npnts); + HEreport("npnts must be greater than zero.\n"); + status = -1; + return (status); + } + if ( projcode == GCTP_BCEA) + { + inv_init(projcode, zonecode, projparm, spherecode, NULL, NULL, + &errorcode, inv_trans); + + /* Convert from meters(or whatever unit is, i.e unit + of r_major and r_minor) to lat/lon using GCTP */ + /* ----------------------------------------- */ + for(i=0; i + +/* define strings that will be displayed by the using the UNIX "what" command + on a file containing these strings */ + +#define HDFEOSd_BANNER "@(#)## ================= HDFEOS ================" +#ifdef __GNUC__ +#define HDFEOSd_HDFEOS_VER "@(#)## HDFEOS Version: "HDFEOSVERSION1 +#define HDFEOSd_DATE "@(#)## Build date: "__DATE__" @ "__TIME__ +#else +#define HDFEOSd_HDFEOS_VER "@(#)## HDFEOS Version: "##HDFEOSVERSION1 +#define HDFEOSd_DATE "@(#)## Build date: "##__DATE__##" @ "##__TIME__ +#endif + +const char *hdfeosg_LibraryVersionString01 = HDFEOSd_BANNER; +const char *hdfeosg_LibraryVersionString02 = HDFEOSd_HDFEOS_VER; +const char *hdfeosg_LibraryVersionString03 = HDFEOSd_DATE; +const char *hdfeosg_LibraryVersionString04 = HDFEOSd_BANNER; diff --git a/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/HdfEosDef.h b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/HdfEosDef.h new file mode 100644 index 000000000..2c6be3314 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/HdfEosDef.h @@ -0,0 +1,322 @@ +/***************************************************************************** + * $Id: HdfEosDef.h 12769 2007-11-14 15:12:27Z dron $ + * + * This module has a number of additions and improvements over the original + * implementation to be suitable for usage in GDAL HDF driver. + * + * Andrey Kiselev is responsible for all the changes. + ****************************************************************************/ + +/* +Copyright (C) 1996 Hughes and Applied Research Corporation + +Permission to use, modify, and distribute this software and its documentation +for any purpose without fee is hereby granted, provided that the above +copyright notice appear in all copies and that both that copyright notice and +this permission notice appear in supporting documentation. +*/ + +#ifndef HDFEOSDEF_H_ +#define HDFEOSDEF_H_ + +/* include header file for EASE grid */ +#include + +/* Working Buffer Sizes */ +#define HDFE_MAXMEMBUF 256*256*16 +#define HDFE_NAMBUFSIZE 32000 +#define HDFE_DIMBUFSIZE 64000 + + +/* Field Merge */ +#define HDFE_NOMERGE 0 +#define HDFE_AUTOMERGE 1 + + +/* XXentries Modes */ +#define HDFE_NENTDIM 0 +#define HDFE_NENTMAP 1 +#define HDFE_NENTIMAP 2 +#define HDFE_NENTGFLD 3 +#define HDFE_NENTDFLD 4 + + +/* GCTP projection codes */ +#define GCTP_GEO 0 +#define GCTP_UTM 1 +#define GCTP_SPCS 2 +#define GCTP_ALBERS 3 +#define GCTP_LAMCC 4 +#define GCTP_MERCAT 5 +#define GCTP_PS 6 +#define GCTP_POLYC 7 +#define GCTP_EQUIDC 8 +#define GCTP_TM 9 +#define GCTP_STEREO 10 +#define GCTP_LAMAZ 11 +#define GCTP_AZMEQD 12 +#define GCTP_GNOMON 13 +#define GCTP_ORTHO 14 +#define GCTP_GVNSP 15 +#define GCTP_SNSOID 16 +#define GCTP_EQRECT 17 +#define GCTP_MILLER 18 +#define GCTP_VGRINT 19 +#define GCTP_HOM 20 +#define GCTP_ROBIN 21 +#define GCTP_SOM 22 +#define GCTP_ALASKA 23 +#define GCTP_GOOD 24 +#define GCTP_MOLL 25 +#define GCTP_IMOLL 26 +#define GCTP_HAMMER 27 +#define GCTP_WAGIV 28 +#define GCTP_WAGVII 29 +#define GCTP_OBLEQA 30 +#define GCTP_ISINUS1 31 +#define GCTP_CEA 97 +#define GCTP_BCEA 98 +#define GCTP_ISINUS 99 + + +/* Compression Modes */ +#define HDFE_COMP_NONE 0 +#define HDFE_COMP_RLE 1 +#define HDFE_COMP_NBIT 2 +#define HDFE_COMP_SKPHUFF 3 +#define HDFE_COMP_DEFLATE 4 + + +/* Tiling Codes */ +#define HDFE_NOTILE 0 +#define HDFE_TILE 1 + + +/* Swath Subset Modes */ +#define HDFE_MIDPOINT 0 +#define HDFE_ENDPOINT 1 +#define HDFE_ANYPOINT 2 +#define HDFE_INTERNAL 0 +#define HDFE_EXTERNAL 1 +#define HDFE_NOPREVSUB -1 + + + +/* Grid Origin */ +#define HDFE_GD_UL 0 +#define HDFE_GD_UR 1 +#define HDFE_GD_LL 2 +#define HDFE_GD_LR 3 + + + +/* Grid Pixel Registration */ +#define HDFE_CENTER 0 +#define HDFE_CORNER 1 + + +/* Angle Conversion Codes */ +#define HDFE_RAD_DEG 0 +#define HDFE_DEG_RAD 1 +#define HDFE_DMS_DEG 2 +#define HDFE_DEG_DMS 3 +#define HDFE_RAD_DMS 4 +#define HDFE_DMS_RAD 5 + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Swath Prototype */ +int32 SWopen(char *, intn); +int32 SWcreate(int32, char *); +int32 SWattach(int32, char *); +intn SWdefdim(int32, char *, int32); +intn SWdefcomp(int32, int32, intn []); +int32 SWdiminfo(int32, char *); +intn SWmapinfo(int32, char *, char *, int32 *, int32 *); +int32 SWidxmapinfo(int32, char *, char *, int32 []); +intn SWfieldinfo(int32, const char *, int32 *, int32 [], int32 *, char *); +intn SWcompinfo(int32, char *, int32 *, intn []); +intn SWdefdimmap(int32, char *, char *, int32, int32); +intn SWdefidxmap(int32, char *, char *, int32 []); +intn SWdefgeofield(int32, char *, char *, int32, int32); +intn SWdefdatafield(int32, char *, char *, int32, int32); +intn SWwritegeometa(int32, char *, char *, int32); +intn SWwritedatameta(int32, char *, char *, int32); +intn SWwriteattr(int32, char *, int32, int32, VOIDP); +intn SWreadattr(int32, char *, VOIDP); +intn SWattrinfo(int32, char *, int32 *, int32 *); +int32 SWinqdims(int32, char *, int32 []); +int32 SWinqmaps(int32, char *, int32 [], int32 []); +int32 SWinqidxmaps(int32, char *, int32 []); +int32 SWinqgeofields(int32, char *, int32 [], int32 []); +int32 SWinqdatafields(int32, char *, int32 [],int32 []); +int32 SWinqattrs(int32, char *, int32 *); +int32 SWnentries(int32, int32, int32 *); +int32 SWinqswath(char *, char *, int32 *); +intn SWwritefield(int32, char *, int32 [], int32 [], int32 [], VOIDP); +intn SWreadfield(int32, const char *, int32 [], int32 [], int32 [], VOIDP); +int32 SWdefboxregion(int32, float64 [], float64 [], int32); +int32 SWdefscanregion(int32, char *, float64 [], int32); +int32 SWregionindex(int32, float64 [], float64 [], int32, char *, int32 []); +intn SWextractregion(int32, int32, char *, int32, VOIDP); +intn SWregioninfo(int32, int32, char *, int32 *, int32 *, int32 [], int32 *); +int32 SWdupregion(int32); +int32 SWdeftimeperiod(int32, float64, float64, int32); +intn SWextractperiod(int32, int32, char *, int32, VOIDP); +intn SWperiodinfo(int32, int32, char *, int32 *, int32 *, int32 [], int32 *); +int32 SWdefvrtregion(int32, int32, char *, float64 []); +intn SWsetfillvalue(int32, char *, VOIDP); +intn SWgetfillvalue(int32, char *, VOIDP); +intn SWdetach(int32); +intn SWclose(int32); +int32 SWupdateidxmap(int32, int32, int32 [], int32 [], int32 []); +intn SWgeomapinfo(int32, char *); +intn SWupdatescene(int32, int32); +intn SWsdid(int32, const char *, int32 *); + +/* Grid Prototypes */ +int32 GDopen(char *, intn); +int32 GDcreate(int32, char *, int32, int32, float64 [], float64 []); +int32 GDattach(int32, char *); +intn GDdefdim(int32, char *, int32); +intn GDdefproj(int32, int32, int32, int32, float64 []); +intn GDblkSOMoffset(int32, float32 [], int32, char *); +intn GDdefcomp(int32, int32, intn []); +intn GDdeftile(int32, int32, int32, int32 []); +intn GDsettilecomp(int32, char *, int32, int32 *, int32, intn *); +intn GDdeforigin(int32, int32); +intn GDdefpixreg(int32, int32); +int32 GDdiminfo(int32, char *); +intn GDgridinfo(int32, int32 *, int32 *, float64 [], float64 []); +intn GDprojinfo(int32, int32 *, int32 *, int32 *, float64 []); +intn GDorigininfo(int32, int32 *); +intn GDpixreginfo(int32, int32 *); +intn GDcompinfo(int32, char *, int32 *, intn []); +intn GDfieldinfo(int32, char *, int32 *, int32 [], int32 *, char *); +intn GDtileinfo(int32, char *, int32 *, int32 *, int32 []); +intn GDsettilecache(int32, char *, int32, int32); +intn GDwritetile(int32, char *, int32 [], VOIDP); +intn GDreadtile(int32, char *, int32 [], VOIDP); +intn GDdeffield(int32, char *, char *, int32, int32); +intn GDwritefieldmeta(int32, char *, char *, int32); +intn GDwritefield(int32, char *, + int32 [], int32 [], int32 [], VOIDP); +intn GDreadfield(int32, char *, int32 [], int32 [], int32 [], VOIDP); +intn GDwriteattr(int32, char *, int32 , int32, VOIDP); +intn GDreadattr(int32, char *, VOIDP); +intn GDattrinfo(int32, char *, int32 *, int32 *); +int32 GDinqdims(int32, char *, int32 []); +int32 GDinqfields(int32, char *, int32 [], int32 []); +int32 GDinqattrs(int32, char *, int32 *); +int32 GDnentries(int32, int32, int32 *); +int32 GDinqgrid(char *, char *, int32 *); +int32 GDdefboxregion(int32, float64 [], float64 []); +int32 GDdefvrtregion(int32, int32, char *, float64 []); +int32 GDdeftimeperiod(int32, int32, float64, float64); +intn GDextractregion(int32, int32, char *, VOIDP); +intn GDregioninfo(int32, int32, char *, int32 *, int32 *, int32 [], int32 *, + float64 [], float64 []); +int32 GDdupregion(int32); +intn GDgetpixels(int32, int32, float64 [], float64 [], int32 [], int32 []); +int32 GDgetpixvalues(int32, int32, int32 [], int32 [], char *, VOIDP); +int32 GDinterpolate(int32, int32, float64 [], float64 [], char *, float64 []); +intn GDsetfillvalue(int32, char *, VOIDP); +intn GDgetfillvalue(int32, char *, VOIDP); +intn GDdetach(int32); +intn GDclose(int32); +intn GDrs2ll(int32, float64 [], int32, int32, + float64 [], float64 [], + int32, float64 [], float64 [], + float64 [], float64 [], int32, int32); +intn GDsdid(int32, const char *, int32 *); + +/* Point Prototypes */ +int32 PTopen(char *, intn); +int32 PTcreate(int32, char *); +int32 PTnrecs(int32, int32); +int32 PTnlevels(int32); +int32 PTsizeof(int32, char *, int32 []); +int32 PTnfields(int32, int32, int32 *); +int32 PTlevelindx(int32, char *); +int32 PTattach(int32, char *); +intn PTdeflevel(int32, char *, char *, int32 [], int32 []); +intn PTdeflinkage(int32, char *, char *, char *); +intn PTbcklinkinfo(int32, int32, char *); +intn PTfwdlinkinfo(int32, int32, char *); +int32 PTlevelinfo(int32, int32, char *, int32 [], int32 []); +intn PTgetlevelname(int32, int32, char *, int32 *); +intn PTwritelevel(int32, int32, int32, VOIDP); +intn PTupdatelevel(int32, int32, char *, int32, int32 [], VOIDP); +intn PTreadlevel(int32, int32, char *, int32, int32 [], VOIDP); +intn PTgetrecnums(int32, int32, int32, int32, int32 [], int32 *, int32 []); +intn PTwriteattr(int32, char *, int32, int32, VOIDP); +intn PTreadattr(int32, char *, VOIDP); +intn PTattrinfo(int32, char *, int32 *, int32 *); +int32 PTinqattrs(int32, char *, int32 *); +int32 PTinqpoint(char *, char *, int32 *); +int32 PTdefboxregion(int32, float64 [], float64 []); +int32 PTdeftimeperiod(int32, float64, float64); +int32 PTdefvrtregion(int32, int32, char *, float64 []); +intn PTregioninfo(int32, int32 regionID, int32, char *, int32 *); +intn PTregionrecs(int32, int32, int32, int32 *, int32 []); +intn PTperiodrecs(int32, int32, int32, int32 *, int32 []); +intn PTperiodinfo(int32, int32 periodID, int32, char *, int32 *); +intn PTextractregion(int32, int32 regionID, int32, char *, VOIDP); +intn PTextractperiod(int32, int32 periodID, int32, char *, VOIDP); +intn PTdetach(int32); +intn PTclose(int32); + + + +/* EH Utility Prototypes */ +int32 EHnumstr(const char *); +float64 EHconvAng(float64, intn); +int32 EHparsestr(const char *, const char, char *[], int32 []); +int32 EHstrwithin(const char *, const char *, const char); +intn EHchkODL(char *); +intn EHloadliststr(char *[], int32, char *, char); +intn EHgetversion(int32, char *); +int32 EHopen(char *, intn); +intn EHchkfid(int32, char *, int32 *, int32 *, uint8 *); +intn EHidinfo(int32, int32 *, int32 *); +int32 EHgetid(int32, int32, const char *, intn, const char *); +intn EHrevflds(char *, char *); +intn EHinsertmeta(int32, char *, char *, int32, char *, int32 []); +intn EHgetmetavalue(char *[], char *, char *); +char * EHmetagroup(int32, char *, char *, char *, char *[]); +intn EHfillfld(int32, int32, int32, int32, int32, int32 [], VOIDP); +intn EHattr(int32, int32, char *, int32, int32, char *, VOIDP); +intn EHattrinfo(int32, int32, char *, int32 *, int32 *); +int32 EHattrcat(int32, int32, char *, int32 *); +intn EHfilename(int32, char *); +int32 EHcntOBJECT(char *[]); +int32 EHcntGROUP(char *[]); +/* 9/3/97 Abe changed the first argument from + float64 (float64 []) to float64 (*) (float64 []) for SunOS + float64 () (float64 []) to float64 (*) (float64 []) for all other OSs */ +intn EHbisect(float64 (*) (float64 []), float64 [], int32, float64, float64, + float64, float64 *); +int32 EHinquire(char *, char *, char *, int32 *); +intn EHclose(int32); + + + +#ifdef __cplusplus +} +#endif + + +#endif /* #ifndef HDFEOSDEF_H_ */ + + + + + + + + + + diff --git a/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/README b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/README new file mode 100644 index 000000000..bfce974b6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/README @@ -0,0 +1,19 @@ +The following source files are taken from the HDF-EOS package + +EHapi.c, +GDapi.c, +SWapi.c, +HDFEOSVersion.h, +HdfEosDef.h, +ease.h + +and have the following copyright notice: + +Copyright (C) 1996 Hughes and Applied Research Corporation + +Permission to use, modify, and distribute this software and its documentation +for any purpose without fee is hereby granted, provided that the above +copyright notice appear in all copies and that both that copyright notice and +this permission notice appear in supporting documentation. + + diff --git a/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/SWapi.c b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/SWapi.c new file mode 100644 index 000000000..85c8ec9d4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/SWapi.c @@ -0,0 +1,11428 @@ +/***************************************************************************** + * $Id: SWapi.c 29336 2015-06-14 17:37:21Z rouault $ + * + * This module has a number of additions and improvements over the original + * implementation to be suitable for usage in GDAL HDF driver. + * + * Andrey Kiselev is responsible for all the changes. + ****************************************************************************/ + +/* +Copyright (C) 1996 Hughes and Applied Research Corporation + +Permission to use, modify, and distribute this software and its documentation +for any purpose without fee is hereby granted, provided that the above +copyright notice appear in all copies and that both that copyright notice and +this permission notice appear in supporting documentation. +*/ +/***************************************************************************** +REVISIONS: + +Aug 31, 1999 Abe Taaheri Changed memory allocation for utility strings to + the size of UTLSTR_MAX_SIZE. + Added error check for memory unavailibilty in + several functions. + Added check for NULL metabuf returned from + EHmeta... functions. NULL pointer retruned from + EHmeta... functions indicate that memory could not + be allocated for metabuf. + +June 05, 2003 Abe Taaheri / Bruce Beaumont + + Changed MAXNREGIONS to 1024 to support MOPITT data + Supplied cast for compcode in call to + SDsetcompress to avoid compiler error + Removed declaration for unused variable rstatus + in SWwrrdfield + Removed initialization code for unused variables + in SWwrrdfield + Removed declaration for unused variable tmpVal + in SWdefboxregion + Added code in SWdefboxregion to check for index k + exceeding NSWATHREGN to avoid overwriting + memory + Removed declaration for unused variable retchar + in SWregionindex + Removed initialization code for unused variables + in SWregionindex + Removed declarations for unused variables tstatus, + nfields, nflgs, and swathname in SWextractregion + Removed initialization code for unused variables + in SWextractregion + Removed declaration for unused variable + land_status in SWscan2longlat + Removed initialization code for unused variables + in SWscan2longlat + Added clear (0) of timeflag in SWextractperiod if + return status from SWextractregion is non-zero + Removed declarations for unused variables tstatus, + scandim, ndfields, ndflds, and swathname in + SWregioninfo + Removed initialization code for unused variables + in SWregioninfo + Added clear (0) of timeflag in SWperiodinfo if + return status from SWregioninfo is non-zero + Removed declarations for unused variables size, + nfields, nflds, nswath, idxsz, cornerlon, and + cornerlat in SWdefscanregion + Removed initialization code for unused variables + in SWdefscanregion + Removed declarations for unused variables dims2, + rank, nt, swathname, dimlist, and buffer in + SWupdateidxmap + Removed declaration for unused variable statmeta + in SWgeomapinfo +******************************************************************************/ + +#include "mfhdf.h" +#include "hcomp.h" +#include "HdfEosDef.h" +#include + +#include "hdf4compat.h" + +#define SWIDOFFSET 1048576 + + +static int32 SWX1dcomb[512*3]; +static int32 SWXSDcomb[512*5]; +static char SWXSDname[HDFE_NAMBUFSIZE]; +static char SWXSDdims[HDFE_DIMBUFSIZE]; + +/* This flag was added to allow the Time field to have different Dimensions +** than Longitude and Latitude and still be used for subsetting +** 23 June,1997 DaW +*/ +static intn timeflag = 0; + + +/* Added for routine that converts scanline to Lat/long +** for floating scene subsetting +** Jul 1999 DaW +*/ +#define PI 3.141592653589793238 +#define RADOE 6371.0 /* Radius of Earth in Km */ + +#define NSWATH 200 +/* Swath Structure External Arrays */ +struct swathStructure +{ + int32 active; + int32 IDTable; + int32 VIDTable[3]; + int32 fid; + int32 nSDS; + int32 *sdsID; + int32 compcode; + intn compparm[5]; + int32 tilecode; + int32 tilerank; + int32 tiledims[8]; +}; +static struct swathStructure SWXSwath[NSWATH]; + + + +#define NSWATHREGN 256 +#define MAXNREGIONS 1024 +struct swathRegion +{ + int32 fid; + int32 swathID; + int32 nRegions; + int32 StartRegion[MAXNREGIONS]; + int32 StopRegion[MAXNREGIONS]; + int32 StartVertical[8]; + int32 StopVertical[8]; + int32 StartScan[8]; + int32 StopScan[8]; + char *DimNamePtr[8]; + intn band8flag; + intn scanflag; +}; +static struct swathRegion *SWXRegion[NSWATHREGN]; + +/* define a macro for the string size of the utility strings. The value + of 80 in previous version of this code was resulting in core dump (Array + Bounds Write and Array Bounds Read problem in SWfinfo function and the + functions called from there) for 7-8 dimensional fields where the + string length for "DimList" can exceed 80 characters, including " and + commas in the string. The length now is 512 which seems to be more + than enough to avoid the problem mentioned above. */ + +#define UTLSTR_MAX_SIZE 512 + +/* Swath Prototypes (internal routines) */ +static intn SWchkswid(int32, char *, int32 *, int32 *, int32 *); +static int32 SWfinfo(int32, const char *, const char *, int32 *, + int32 [], int32 *, char *); +static intn SWdefinefield(int32, char *, char *, char *, int32, int32); +static intn SWwrrdattr(int32, char *, int32, int32, char *, VOIDP); +static intn SW1dfldsrch(int32, int32, const char *, const char *, int32 *, + int32 *, int32 *); +static intn SWSDfldsrch(int32, int32, const char *, int32 *, int32 *, + int32 *, int32 *, int32 [], int32 *); +static intn SWwrrdfield(int32, const char *, const char *, + int32 [], int32 [], int32 [], VOIDP); +static int32 SWinqfields(int32, char *, char *, int32 [], int32 []); +static intn SWscan2longlat(int32, char *, VOIDP, int32 [], int32 [], + int32 *, int32, int32); + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWopen | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| fid int32 HDF-EOS file ID | +| | +| INPUTS: | +| filename char Filename | +| access intn HDF access code | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWopen(char *filename, intn access) + +{ + int32 fid /* HDF-EOS file ID */ ; + + /* Call EHopen to perform file access */ + /* ---------------------------------- */ + fid = EHopen(filename, access); + + return (fid); +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWcreate | +| | +| DESCRIPTION: Creates a new swath structure and returns swath ID | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| swathID int32 Swath structure ID | +| | +| INPUTS: | +| fid int32 File ID | +| swathname char Swath structure name | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Sep 96 Joel Gales Check swath name for length | +| Mar 97 Joel Gales Enlarge utlbuf to 512 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWcreate(int32 fid, char *swathname) +{ + intn i; /* Loop index */ + intn nswathopen = 0; /* # of swath structures open */ + intn status = 0; /* routine return status variable */ + + uint8 access; /* Read/Write file access code */ + + int32 HDFfid; /* HDF file id */ + int32 vgRef; /* Vgroup reference number */ + int32 vgid[4]; /* Vgroup ID array */ + int32 swathID = -1; /* HDF-EOS swath ID */ + + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 nSwath = 0; /* Swath counter */ + + char name[80]; /* Vgroup name */ + char class[80]; /* Vgroup class */ + char errbuf[256];/* Buffer for error message */ + char utlbuf[512];/* Utility buffer */ + char utlbuf2[32];/* Utility buffer 2 */ + + /* + * Check HDF-EOS file ID, get back HDF file ID, SD interface ID and + * access code + */ + status = EHchkfid(fid, swathname, &HDFfid, &sdInterfaceID, &access); + + + /* Check swathname for length */ + /* -------------------------- */ + if ((intn) strlen(swathname) > VGNAMELENMAX) + { + status = -1; + HEpush(DFE_GENAPP, "SWcreate", __FILE__, __LINE__); + HEreport("Swathname \"%s\" must be less than %d characters.\n", + swathname, VGNAMELENMAX); + } + + + if (status == 0) + { + + /* Determine number of swaths currently opened */ + /* ------------------------------------------- */ + for (i = 0; i < NSWATH; i++) + { + nswathopen += SWXSwath[i].active; + } + + + /* Setup file interface */ + /* -------------------- */ + if (nswathopen < NSWATH) + { + + /* Check that swath has not been previously opened */ + /* ----------------------------------------------- */ + vgRef = -1; + + while (1) + { + vgRef = Vgetid(HDFfid, vgRef); + + /* If no more Vgroups then exist while loop */ + /* ---------------------------------------- */ + if (vgRef == -1) + { + break; + } + + /* Get name and class of Vgroup */ + /* ---------------------------- */ + vgid[0] = Vattach(HDFfid, vgRef, "r"); + Vgetname(vgid[0], name); + Vgetclass(vgid[0], class); + Vdetach(vgid[0]); + + /* If SWATH then increment # swath counter */ + /* --------------------------------------- */ + if (strcmp(class, "SWATH") == 0) + { + nSwath++; + } + + /* If swath already exist, return error */ + /* ------------------------------------ */ + if (strcmp(name, swathname) == 0 && + strcmp(class, "SWATH") == 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWcreate", __FILE__, __LINE__); + HEreport("\"%s\" already exists.\n", swathname); + break; + } + } + + + if (status == 0) + { + + /* Create Root Vgroup for Swath */ + /* ---------------------------- */ + vgid[0] = Vattach(HDFfid, -1, "w"); + + + /* Set Name and Class (SWATH) */ + /* -------------------------- */ + Vsetname(vgid[0], swathname); + Vsetclass(vgid[0], "SWATH"); + + + + /* Create Geolocation Fields Vgroup */ + /* -------------------------------- */ + vgid[1] = Vattach(HDFfid, -1, "w"); + Vsetname(vgid[1], "Geolocation Fields"); + Vsetclass(vgid[1], "SWATH Vgroup"); + Vinsert(vgid[0], vgid[1]); + + + + /* Create Data Fields Vgroup */ + /* ------------------------- */ + vgid[2] = Vattach(HDFfid, -1, "w"); + Vsetname(vgid[2], "Data Fields"); + Vsetclass(vgid[2], "SWATH Vgroup"); + Vinsert(vgid[0], vgid[2]); + + + + /* Create Attributes Vgroup */ + /* ------------------------ */ + vgid[3] = Vattach(HDFfid, -1, "w"); + Vsetname(vgid[3], "Swath Attributes"); + Vsetclass(vgid[3], "SWATH Vgroup"); + Vinsert(vgid[0], vgid[3]); + + + + /* Establish Swath in Structural MetaData Block */ + /* -------------------------------------------- */ + sprintf(utlbuf, "%s%ld%s%s%s", + "\tGROUP=SWATH_", (long)nSwath + 1, + "\n\t\tSwathName=\"", swathname, "\"\n"); + + strcat(utlbuf, "\t\tGROUP=Dimension\n"); + strcat(utlbuf, "\t\tEND_GROUP=Dimension\n"); + strcat(utlbuf, "\t\tGROUP=DimensionMap\n"); + strcat(utlbuf, "\t\tEND_GROUP=DimensionMap\n"); + strcat(utlbuf, "\t\tGROUP=IndexDimensionMap\n"); + strcat(utlbuf, "\t\tEND_GROUP=IndexDimensionMap\n"); + strcat(utlbuf, "\t\tGROUP=GeoField\n"); + strcat(utlbuf, "\t\tEND_GROUP=GeoField\n"); + strcat(utlbuf, "\t\tGROUP=DataField\n"); + strcat(utlbuf, "\t\tEND_GROUP=DataField\n"); + strcat(utlbuf, "\t\tGROUP=MergedFields\n"); + strcat(utlbuf, "\t\tEND_GROUP=MergedFields\n"); + sprintf(utlbuf2, "%s%ld%s", + "\tEND_GROUP=SWATH_", (long)nSwath + 1, "\n"); + strcat(utlbuf, utlbuf2); + + + status = EHinsertmeta(sdInterfaceID, "", "s", 1001L, + utlbuf, NULL); + } + } + else + { + /* Too many files opened */ + /* --------------------- */ + status = -1; + strcpy(errbuf, + "No more than %d swaths may be open simutaneously"); + strcat(errbuf, " (%s)"); + HEpush(DFE_DENIED, "SWcreate", __FILE__, __LINE__); + HEreport(errbuf, NSWATH, swathname); + } + + + /* Assign swathID # & Load swath and SWXSwath table entries */ + /* -------------------------------------------------------- */ + if (status == 0) + { + + for (i = 0; i < NSWATH; i++) + { + if (SWXSwath[i].active == 0) + { + /* + * Set swathID, Set swath entry active, Store root Vgroup + * ID, Store sub Vgroup IDs, Store HDF-EOS file ID + */ + swathID = i + idOffset; + SWXSwath[i].active = 1; + SWXSwath[i].IDTable = vgid[0]; + SWXSwath[i].VIDTable[0] = vgid[1]; + SWXSwath[i].VIDTable[1] = vgid[2]; + SWXSwath[i].VIDTable[2] = vgid[3]; + SWXSwath[i].fid = fid; + status = 0; + break; + } + } + + } + } + return (swathID); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWattach | +| | +| DESCRIPTION: Attaches to an existing swath within the file. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| swathID int32 swath structure ID | +| | +| INPUTS: | +| fid int32 HDF-EOS file ID | +| swathname char swath structure name | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Apr 99 David Wynne Modified test for memory allocation check when no | +| SDSs are in the Swath, NCR22513 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWattach(int32 fid, char *swathname) + +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn nswathopen = 0; /* # of swath structures open */ + intn status; /* routine return status variable */ + + uint8 acs; /* Read/Write file access code */ + + int32 HDFfid; /* HDF file id */ + int32 vgRef; /* Vgroup reference number */ + int32 vgid[4]; /* Vgroup ID array */ + int32 swathID = -1; /* HDF-EOS swath ID */ + int32 *tags; /* Pnt to Vgroup object tags array */ + int32 *refs; /* Pnt to Vgroup object refs array */ + int32 dum; /* dummy varible */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 nObjects; /* # of objects in Vgroup */ + int32 nSDS; /* SDS counter */ + int32 index; /* SDS index */ + int32 sdid; /* SDS object ID */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + + char name[80]; /* Vgroup name */ + char class[80]; /* Vgroup class */ + char errbuf[256];/* Buffer for error message */ + char acsCode[1]; /* Read/Write access char: "r/w" */ + + + /* Check HDF-EOS file ID, get back HDF file ID and access code */ + /* ----------------------------------------------------------- */ + status = EHchkfid(fid, swathname, &HDFfid, &dum, &acs); + + + if (status == 0) + { + /* Convert numeric access code to character */ + /* ---------------------------------------- */ + acsCode[0] = (acs == 1) ? 'w' : 'r'; + + /* Determine number of swaths currently opened */ + /* ------------------------------------------- */ + for (i = 0; i < NSWATH; i++) + { + nswathopen += SWXSwath[i].active; + } + + /* If room for more ... */ + /* -------------------- */ + if (nswathopen < NSWATH) + { + + /* Search Vgroups for Swath */ + /* ------------------------ */ + vgRef = -1; + + while (1) + { + vgRef = Vgetid(HDFfid, vgRef); + + + /* If no more Vgroups then exist while loop */ + /* ---------------------------------------- */ + if (vgRef == -1) + { + break; + } + + /* Get name and class of Vgroup */ + /* ---------------------------- */ + vgid[0] = Vattach(HDFfid, vgRef, "r"); + Vgetname(vgid[0], name); + Vgetclass(vgid[0], class); + + + /* + * If Vgroup with swathname and class SWATH found, load + * tables + */ + + if (strcmp(name, swathname) == 0 && + strcmp(class, "SWATH") == 0) + { + /* Attach to "Fields" and "Swath Attributes" Vgroups */ + /* ------------------------------------------------- */ + tags = (int32 *) malloc(sizeof(int32) * 3); + if(tags == NULL) + { + HEpush(DFE_NOSPACE,"SWattach", __FILE__, __LINE__); + return(-1); + } + refs = (int32 *) malloc(sizeof(int32) * 3); + if(refs == NULL) + { + HEpush(DFE_NOSPACE,"SWattach", __FILE__, __LINE__); + free(tags); + return(-1); + } + Vgettagrefs(vgid[0], tags, refs, 3); + vgid[1] = Vattach(HDFfid, refs[0], acsCode); + vgid[2] = Vattach(HDFfid, refs[1], acsCode); + vgid[3] = Vattach(HDFfid, refs[2], acsCode); + free(tags); + free(refs); + + /* Setup External Arrays */ + /* --------------------- */ + for (i = 0; i < NSWATH; i++) + { + /* Find empty entry in array */ + /* ------------------------- */ + if (SWXSwath[i].active == 0) + { + /* + * Set swathID, Set swath entry active, Store + * root Vgroup ID, Store sub Vgroup IDs, Store + * HDF-EOS file ID + */ + swathID = i + idOffset; + SWXSwath[i].active = 1; + SWXSwath[i].IDTable = vgid[0]; + SWXSwath[i].VIDTable[0] = vgid[1]; + SWXSwath[i].VIDTable[1] = vgid[2]; + SWXSwath[i].VIDTable[2] = vgid[3]; + SWXSwath[i].fid = fid; + break; + } + } + + /* Get SDS interface ID */ + /* -------------------- */ + status = SWchkswid(swathID, "SWattach", &dum, + &sdInterfaceID, &dum); + + + /* Access swath "Geolocation" SDS */ + /* ------------------------------ */ + + /* Get # of entries within this Vgroup & search for SDS */ + /* ---------------------------------------------------- */ + nObjects = Vntagrefs(vgid[1]); + + if (nObjects > 0) + { + /* Get tag and ref # for Geolocation Vgroup objects */ + /* ------------------------------------------------ */ + tags = (int32 *) malloc(sizeof(int32) * nObjects); + if(tags == NULL) + { + HEpush(DFE_NOSPACE,"SWattach", __FILE__, __LINE__); + return(-1); + } + refs = (int32 *) malloc(sizeof(int32) * nObjects); + if(refs == NULL) + { + HEpush(DFE_NOSPACE,"SWattach", __FILE__, __LINE__); + free(tags); + return(-1); + } + Vgettagrefs(vgid[1], tags, refs, nObjects); + + /* Count number of SDS & allocate SDS ID array */ + /* ------------------------------------------- */ + nSDS = 0; + for (j = 0; j < nObjects; j++) + { + if (tags[j] == DFTAG_NDG) + { + nSDS++; + } + } + SWXSwath[i].sdsID = (int32 *) calloc(nSDS, 4); + if(SWXSwath[i].sdsID == NULL && nSDS != 0) + { + HEpush(DFE_NOSPACE,"SWattach", __FILE__, __LINE__); + free(tags); + free(refs); + return(-1); + } + nSDS = 0; + + + /* Fill SDS ID array */ + /* ----------------- */ + for (j = 0; j < nObjects; j++) + { + /* If object is SDS then get id */ + /* ---------------------------- */ + if (tags[j] == DFTAG_NDG) + { + index = SDreftoindex(sdInterfaceID, refs[j]); + sdid = SDselect(sdInterfaceID, index); + SWXSwath[i].sdsID[nSDS] = sdid; + nSDS++; + SWXSwath[i].nSDS++; + } + } + free(tags); + free(refs); + } + + /* Access swath "Data" SDS */ + /* ----------------------- */ + + /* Get # of entries within this Vgroup & search for SDS */ + /* ---------------------------------------------------- */ + nObjects = Vntagrefs(vgid[2]); + + if (nObjects > 0) + { + /* Get tag and ref # for Data Vgroup objects */ + /* ----------------------------------------- */ + tags = (int32 *) malloc(sizeof(int32) * nObjects); + if(tags == NULL) + { + HEpush(DFE_NOSPACE,"SWattach", __FILE__, __LINE__); + return(-1); + } + refs = (int32 *) malloc(sizeof(int32) * nObjects); + if(refs == NULL) + { + HEpush(DFE_NOSPACE,"SWattach", __FILE__, __LINE__); + free(tags); + return(-1); + } + Vgettagrefs(vgid[2], tags, refs, nObjects); + + + /* Count number of SDS & allocate SDS ID array */ + /* ------------------------------------------- */ + nSDS = 0; + for (j = 0; j < nObjects; j++) + { + if (tags[j] == DFTAG_NDG) + { + nSDS++; + } + } + SWXSwath[i].sdsID = (int32 *) + realloc((void *) SWXSwath[i].sdsID, + (SWXSwath[i].nSDS + nSDS) * 4); + if(SWXSwath[i].sdsID == NULL && nSDS != 0) + { + HEpush(DFE_NOSPACE,"SWattach", __FILE__, __LINE__); + return(-1); + } + + /* Fill SDS ID array */ + /* ----------------- */ + for (j = 0; j < nObjects; j++) + { + /* If object is SDS then get id */ + /* ---------------------------- */ + if (tags[j] == DFTAG_NDG) + { + index = SDreftoindex(sdInterfaceID, refs[j]); + sdid = SDselect(sdInterfaceID, index); + SWXSwath[i].sdsID[SWXSwath[i].nSDS] = sdid; + SWXSwath[i].nSDS++; + } + } + free(tags); + free(refs); + } + break; + } + + /* Detach Vgroup if not desired Swath */ + /* ---------------------------------- */ + Vdetach(vgid[0]); + } + + /* If Swath not found then set up error message */ + /* -------------------------------------------- */ + if (swathID == -1) + { + HEpush(DFE_RANGE, "SWattach", __FILE__, __LINE__); + HEreport("Swath: \"%s\" does not exist within HDF file.\n", + swathname); + } + } + else + { + /* Too many files opened */ + /* --------------------- */ + swathID = -1; + strcpy(errbuf, + "No more than %d swaths may be open simutaneously"); + strcat(errbuf, " (%s)"); + HEpush(DFE_DENIED, "SWattach", __FILE__, __LINE__); + HEreport(errbuf, NSWATH, swathname); + } + + } + return (swathID); +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWchkswid | +| | +| DESCRIPTION: Checks for valid swathID and returns file ID, SDS ID, and | +| swath Vgroup ID | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| routname char Name of routine calling SWchkswid | +| | +| OUTPUTS: | +| fid int32 File ID | +| sdInterfaceID int32 SDS interface ID | +| swVgrpID int32 swath Vgroup ID | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +SWchkswid(int32 swathID, char *routname, + int32 * fid, int32 * sdInterfaceID, int32 * swVgrpID) + +{ + intn status = 0; /* routine return status variable */ + uint8 access; /* Read/Write access code */ + + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + + char message1[] = + "Invalid swath id: %d in routine \"%s\". ID must be >= %d and < %d.\n"; + char message2[] = + "Swath id %d in routine \"%s\" not active.\n"; + + + /* Check for valid swath id */ + /* ------------------------ */ + if (swathID < idOffset || swathID >= NSWATH + idOffset) + { + status = -1; + HEpush(DFE_RANGE, "SWchkswid", __FILE__, __LINE__); + HEreport(message1, swathID, routname, idOffset, NSWATH + idOffset); + } + else + { + /* Check for active swath ID */ + /* ------------------------- */ + if (SWXSwath[swathID % idOffset].active == 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWchkswid", __FILE__, __LINE__); + HEreport(message2, swathID, routname); + } + else + { + + /* Get file & SDS ids and Swath Vgroup */ + /* ----------------------------------- */ + status = EHchkfid(SWXSwath[swathID % idOffset].fid, " ", fid, + sdInterfaceID, &access); + *swVgrpID = SWXSwath[swathID % idOffset].IDTable; + } + } + return (status); +} + + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdefdim | +| | +| DESCRIPTION: Defines numerical value of dimension | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| dimname char Dimension name to define | +| dim int32 Dimemsion value | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Dec 96 Joel Gales Check that dim value >= 0 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWdefdim(int32 swathID, char *dimname, int32 dim) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file id */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + + char swathname[80] /* Swath name */ ; + + + /* Check for valid swath id */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWdefdim", &fid, &sdInterfaceID, &swVgrpID); + + + /* Make sure dimension >= 0 */ + /* ------------------------ */ + if (dim < 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWdefdim", __FILE__, __LINE__); + HEreport("Dimension value for \"%s\" less than zero: %d.\n", + dimname, dim); + } + + + /* Write Dimension to Structural MetaData */ + /* -------------------------------------- */ + if (status == 0) + { + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + status = EHinsertmeta(sdInterfaceID, swathname, "s", 0L, + dimname, &dim); + } + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdiminfo | +| | +| DESCRIPTION: Returns size in bytes of named dimension | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| size int32 Size of dimension | +| | +| INPUTS: | +| swathID int32 swath structure id | +| dimname char Dimension name | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Jan 97 Joel Gales Check for metadata error status from EHgetmetavalue | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWdiminfo(int32 swathID, char *dimname) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 size; /* Dimension size */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char swathname[80]; /* Swath Name */ + char *utlstr; /* Utility string */ + + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"SWdiminfo", __FILE__, __LINE__); + return(-1); + } + /* Initialize return value */ + size = -1; + + /* Check Swath ID */ + status = SWchkswid(swathID, "SWdiminfo", &fid, &sdInterfaceID, &swVgrpID); + + if (status == 0) + { + /* Get swath name */ + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + + /* Get pointers to "Dimension" section within SM */ + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "Dimension", metaptrs); + + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + /* Search for dimension name (surrounded by quotes) */ + sprintf(utlstr, "%s%s%s", "\"", dimname, "\"\n"); + metaptrs[0] = strstr(metaptrs[0], utlstr); + + /* + * If dimension found within swath structure then get dimension value + */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + /* Set endptr at end of dimension definition entry */ + metaptrs[1] = strstr(metaptrs[0], "\t\t\tEND_OBJECT"); + + status = EHgetmetavalue(metaptrs, "Size", utlstr); + + if (status == 0) + { + size = atol(utlstr); + } + else + { + HEpush(DFE_GENAPP, "SWdiminfo", __FILE__, __LINE__); + HEreport("\"Size\" string not found in metadata.\n"); + } + } + else + { + HEpush(DFE_GENAPP, "SWdiminfo", __FILE__, __LINE__); + HEreport("Dimension \"%s\" not found.\n", dimname); + } + + free(metabuf); + } + free(utlstr); + + return (size); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWmapinfo | +| | +| DESCRIPTION: Returns dimension mapping information | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure id | +| geodim char geolocation dimension name | +| datadim char data dimension name | +| | +| OUTPUTS: | +| offset int32 mapping offset | +| increment int32 mapping increment | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Jan 97 Joel Gales Check for metadata error status from EHgetmetavalue | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWmapinfo(int32 swathID, char *geodim, char *datadim, int32 * offset, + int32 * increment) + +{ + intn status; /* routine return status variable */ + intn statmeta = 0; /* EHgetmetavalue return status */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char swathname[80]; /* Swath Name */ + char *utlstr; /* Utility string */ + + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"SWmapinfo", __FILE__, __LINE__); + return(-1); + } + /* Initialize return values */ + *offset = -1; + *increment = -1; + + /* Check Swath ID */ + status = SWchkswid(swathID, "SWmapinfo", &fid, &sdInterfaceID, &swVgrpID); + + if (status == 0) + { + /* Get swath name */ + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + + /* Get pointers to "DimensionMap" section within SM */ + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "DimensionMap", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + /* Search for mapping - GeoDim/DataDim (surrounded by quotes) */ + sprintf(utlstr, "%s%s%s%s%s", "\t\t\t\tGeoDimension=\"", geodim, + "\"\n\t\t\t\tDataDimension=\"", datadim, "\"\n"); + metaptrs[0] = strstr(metaptrs[0], utlstr); + + /* + * If mapping found within swath structure then get offset and + * increment value + */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + /* Get Offset */ + statmeta = EHgetmetavalue(metaptrs, "Offset", utlstr); + if (statmeta == 0) + { + *offset = atol(utlstr); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "SWmapinfo", __FILE__, __LINE__); + HEreport("\"Offset\" string not found in metadata.\n"); + } + + + /* Get Increment */ + statmeta = EHgetmetavalue(metaptrs, "Increment", utlstr); + if (statmeta == 0) + { + *increment = atol(utlstr); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "SWmapinfo", __FILE__, __LINE__); + HEreport("\"Increment\" string not found in metadata.\n"); + } + } + else + { + status = -1; + HEpush(DFE_GENAPP, "SWmapinfo", __FILE__, __LINE__); + HEreport("Mapping \"%s/%s\" not found.\n", geodim, datadim); + } + + free(metabuf); + } + free(utlstr); + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWidxmapinfo | +| | +| DESCRIPTION: Returns indexed mapping information | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| gsize int32 Number of index values (sz of geo dim) | +| | +| INPUTS: | +| swathID int32 swath structure id | +| geodim char geolocation dimension name | +| datadim char data dimension name | +| | +| | +| OUTPUTS: | +| index int32 array of index values | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWidxmapinfo(int32 swathID, char *geodim, char *datadim, int32 index[]) +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 vgid; /* Swath Attributes Vgroup ID */ + int32 vdataID; /* Index Mapping Vdata ID */ + int32 gsize = -1; /* Size of geo dim */ + + char utlbuf[256];/* Utility buffer */ + + + /* Check Swath ID */ + status = SWchkswid(swathID, "SWidxmapinfo", + &fid, &sdInterfaceID, &swVgrpID); + + if (status == 0) + { + /* Find Index Mapping Vdata with Swath Attributes Vgroup */ + sprintf(utlbuf, "%s%s%s%s", "INDXMAP:", geodim, "/", datadim); + vgid = SWXSwath[swathID % idOffset].VIDTable[2]; + vdataID = EHgetid(fid, vgid, utlbuf, 1, "r"); + + /* If found then get geodim size & read index mapping values */ + if (vdataID != -1) + { + gsize = SWdiminfo(swathID, geodim); + + VSsetfields(vdataID, "Index"); + VSread(vdataID, (uint8 *) index, 1, FULL_INTERLACE); + VSdetach(vdataID); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "SWidxmapinfo", __FILE__, __LINE__); + HEreport("Index Mapping \"%s\" not found.\n", utlbuf); + } + } + return (gsize); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWcompinfo | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn | +| | +| INPUTS: | +| swathID int32 | +| compcode int32 | +| compparm intn | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Oct 96 Joel Gales Original Programmer | +| Jan 97 Joel Gales Check for metadata error status from EHgetmetavalue | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWcompinfo(int32 swathID, char *fieldname, int32 * compcode, intn compparm[]) +{ + intn i; /* Loop Index */ + intn status; /* routine return status variable */ + intn statmeta = 0; /* EHgetmetavalue return status */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char swathname[80]; /* Swath Name */ + char *utlstr; /* Utility string */ + + char *HDFcomp[5] = {"HDFE_COMP_NONE", "HDFE_COMP_RLE", + "HDFE_COMP_NBIT", "HDFE_COMP_SKPHUFF", + "HDFE_COMP_DEFLATE"}; /* Compression Codes */ + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"SWcompinfo", __FILE__, __LINE__); + return(-1); + } + + /* Check Swath ID */ + status = SWchkswid(swathID, "SWcompinfo", + &fid, &sdInterfaceID, &swVgrpID); + + if (status == 0) + { + /* Get swath name */ + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + + /* Get pointers to "DataField" section within SM */ + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "DataField", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + /* Search for field */ + sprintf(utlstr, "%s%s%s", "\"", fieldname, "\"\n"); + metaptrs[0] = strstr(metaptrs[0], utlstr); + + /* If not found then search in "GeoField" section */ + if (metaptrs[0] > metaptrs[1] || metaptrs[0] == NULL) + { + free(metabuf); + + /* Get pointers to "GeoField" section within SM */ + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "GeoField", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + /* Search for field */ + sprintf(utlstr, "%s%s%s", "\"", fieldname, "\"\n"); + metaptrs[0] = strstr(metaptrs[0], utlstr); + } + + + /* If field found and user wants compression code ... */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + if (compcode != NULL) + { + /* Set endptr at end of field's definition entry */ + metaptrs[1] = strstr(metaptrs[0], "\t\t\tEND_OBJECT"); + + /* Get compression type */ + statmeta = EHgetmetavalue(metaptrs, "CompressionType", utlstr); + + /* + * Default is no compression if "CompressionType" string not + * in metadata + */ + *compcode = HDFE_COMP_NONE; + + /* If compression code is found ... */ + if (statmeta == 0) + { + /* Loop through compression types until match */ + for (i = 0; i < 5; i++) + { + if (strcmp(utlstr, HDFcomp[i]) == 0) + { + *compcode = i; + break; + } + } + } + } + + /* If user wants compression parameters ... */ + if (compparm != NULL && compcode != NULL) + { + /* Initialize to zero */ + for (i = 0; i < 4; i++) + { + compparm[i] = 0.0; + } + + /* + * Get compression parameters if NBIT or DEFLATE compression + */ + if (*compcode == HDFE_COMP_NBIT) + { + statmeta = + EHgetmetavalue(metaptrs, "CompressionParams", utlstr); + if (statmeta == 0) + { + sscanf(utlstr, "(%d,%d,%d,%d)", + &compparm[0], &compparm[1], + &compparm[2], &compparm[3]); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "SWcompinfo", __FILE__, __LINE__); + HEreport( + "\"CompressionParams\" string not found in metadata.\n"); + } + } + else if (*compcode == HDFE_COMP_DEFLATE) + { + statmeta = + EHgetmetavalue(metaptrs, "DeflateLevel", utlstr); + if (statmeta == 0) + { + sscanf(utlstr, "%d", &compparm[0]); + } + else + { + status = -1; + HEpush(DFE_GENAPP, "SWcompinfo", __FILE__, __LINE__); + HEreport( + "\"DeflateLevel\" string not found in metadata.\n"); + } + } + } + } + else + { + HEpush(DFE_GENAPP, "SWcompinfo", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" not found.\n", fieldname); + } + + free(metabuf); + } + free(utlstr); + + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWfinfo | +| | +| DESCRIPTION: Returns field info | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure id | +| fieldtype const char fieldtype (geo or data) | +| fieldname const char name of field | +| | +| | +| OUTPUTS: | +| rank int32 rank of field (# of dims) | +| dims int32 field dimensions | +| numbertype int32 field number type | +| dimlist char field dimension list | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Jan 97 Joel Gales Check for metadata error status from EHgetmetavalue | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static int32 +SWfinfo(int32 swathID, const char *fieldtype, const char *fieldname, + int32 *rank, int32 dims[], int32 *numbertype, char *dimlist) + +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn status; /* routine return status variable */ + intn statmeta = 0; /* EHgetmetavalue return status */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 fsize; /* field size in bytes */ + int32 ndims = 0; /* Number of dimensions */ + int32 slen[8]; /* Length of each entry in parsed string */ + int32 dum; /* Dummy variable */ + int32 vdataID; /* 1d field vdata ID */ + + uint8 *buf; /* One-Dim field buffer */ + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char swathname[80]; /* Swath Name */ + char *utlstr; /* Utility string */ + char *ptr[8]; /* String pointers for parsed string */ + char dimstr[64]; /* Individual dimension entry string */ + + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"SWfinfo", __FILE__, __LINE__); + return(-1); + } + + /* Initialize rank and numbertype to -1 (error) */ + /* -------------------------------------------- */ + *rank = -1; + *numbertype = -1; + + /* Get HDF-EOS file ID and SDS interface ID */ + status = SWchkswid(swathID, "SWfinfo", &fid, &sdInterfaceID, &dum); + + /* Get swath name */ + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + + /* Get pointers to appropriate "Field" section within SM */ + if (strcmp(fieldtype, "Geolocation Fields") == 0) + { + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "GeoField", metaptrs); + + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + } + else + { + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "DataField", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + } + + + /* Search for field */ + sprintf(utlstr, "%s%s%s", "\"", fieldname, "\"\n"); + metaptrs[0] = strstr(metaptrs[0], utlstr); + + /* If field found ... */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + /* Get DataType string */ + statmeta = EHgetmetavalue(metaptrs, "DataType", utlstr); + + /* Convert to numbertype code */ + if (statmeta == 0) + *numbertype = EHnumstr(utlstr); + else + { + status = -1; + HEpush(DFE_GENAPP, "SWfinfo", __FILE__, __LINE__); + HEreport("\"DataType\" string not found in metadata.\n"); + } + + + /* + * Get DimList string and trim off leading and trailing parens "()" + */ + statmeta = EHgetmetavalue(metaptrs, "DimList", utlstr); + + if (statmeta == 0) + { + memmove(utlstr, utlstr + 1, strlen(utlstr) - 2); + utlstr[strlen(utlstr) - 2] = 0; + + /* Parse trimmed DimList string and get rank */ + ndims = EHparsestr(utlstr, ',', ptr, slen); + *rank = ndims; + } + else + { + status = -1; + HEpush(DFE_GENAPP, "SWfinfo", __FILE__, __LINE__); + HEreport("\"DimList\" string not found in metadata.\n"); + } + + /* If dimension list is desired by user then initialize length to 0 */ + if (dimlist != NULL) + { + dimlist[0] = 0; + } + + /* + * Copy each entry in DimList and remove leading and trailing quotes, + * Get dimension sizes and concatanate dimension names to dimension + * list + */ + for (i = 0; i < ndims; i++) + { + memcpy(dimstr, ptr[i] + 1, slen[i] - 2); + dimstr[slen[i] - 2] = 0; + dims[i] = SWdiminfo(swathID, dimstr); + if (dimlist != NULL) + { + if (i > 0) + { + strcat(dimlist, ","); + } + strcat(dimlist, dimstr); + } + + } + + + /* Appendable Field Section */ + /* ------------------------ */ + if (dims[0] == 0) + { + /* One-Dimensional Field */ + if (*rank == 1) + { + /* Get vdata ID */ + status = SW1dfldsrch(fid, swathID, fieldname, "r", + &dum, &vdataID, &dum); + + /* Get actual size of field */ + dims[0] = VSelts(vdataID); + + /* + * If size=1 then check where actual record of + * "initialization" record + */ + if (dims[0] == 1) + { + /* Get record size and read 1st record */ + fsize = VSsizeof(vdataID, (char *)fieldname); + buf = (uint8 *) calloc(fsize, 1); + if(buf == NULL) + { + HEpush(DFE_NOSPACE,"SWfinfo", __FILE__, __LINE__); + free(utlstr); + return(-1); + } + VSsetfields(vdataID, fieldname); + VSseek(vdataID, 0); + VSread(vdataID, (uint8 *) buf, 1, FULL_INTERLACE); + + /* Sum up "bytes" in record */ + for (i = 0, j = 0; i < fsize; i++) + { + j += buf[i]; + } + + /* + * If filled with 255 then "initialization" record, + * actual number of records = 0 + */ + if (j == 255 * fsize) + { + dims[0] = 0; + } + + free(buf); + } + /* Detach from 1d field */ + VSdetach(vdataID); + } + else + { + /* Get actual size of Multi-Dimensional Field */ + status = SWSDfldsrch(swathID, sdInterfaceID, fieldname, + &dum, &dum, &dum, &dum, dims, + &dum); + } + } + } + free(metabuf); + + if (*rank == -1) + { + status = -1; + } + free(utlstr); + + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWfieldinfo | +| | +| DESCRIPTION: Wrapper arount SWfinfo | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure id | +| fieldname const char name of field | +| | +| | +| OUTPUTS: | +| rank int32 rank of field (# of dims) | +| dims int32 field dimensions | +| numbertype int32 field number type | +| dimlist char field dimension list | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWfieldinfo(int32 swathID, const char *fieldname, int32 * rank, int32 dims[], + int32 * numbertype, char *dimlist) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + + + /* Check for valid swath id */ + status = SWchkswid(swathID, "SWfieldinfo", &fid, + &sdInterfaceID, &swVgrpID); + if (status == 0) + { + /* Check for field within Geolocatation Fields */ + status = SWfinfo(swathID, "Geolocation Fields", fieldname, + rank, dims, numbertype, dimlist); + + /* If not there then check within Data Fields */ + if (status == -1) + { + status = SWfinfo(swathID, "Data Fields", fieldname, + rank, dims, numbertype, dimlist); + } + + /* If not there either then can't be found */ + if (status == -1) + { + HEpush(DFE_GENAPP, "SWfieldinfo", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" not found.\n", fieldname); + } + } + return (status); +} + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdefdimmap | +| | +| DESCRIPTION: Defines mapping between geolocation and data dimensions | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| geodim char Geolocation dimension | +| datadim char Data dimension | +| offset int32 Mapping offset | +| increment int32 Mapping increment | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWdefdimmap(int32 swathID, char *geodim, char *datadim, int32 offset, + int32 increment) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 size; /* Size of geo dim */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 dum; /* Dummy variable */ + int32 metadata[2];/* Offset & Increment (passed to metadata) */ + + char mapname[80];/* Mapping name (geodim/datadim) */ + char swathname[80]; /* Swath name */ + + /* Check Swath ID */ + status = SWchkswid(swathID, "SWdefdimmap", &fid, &sdInterfaceID, &dum); + + if (status == 0) + { + + /* Search Dimension Vdata for dimension entries */ + /* -------------------------------------------- */ + size = SWdiminfo(swathID, geodim); + if (size == -1) + { + status = -1; + HEpush(DFE_GENAPP, "SWdefdimmap", __FILE__, __LINE__); + HEreport("Geolocation dimension name: \"%s\" not found.\n", + geodim); + } + /* Data Dimension Search */ + /* --------------------- */ + if (status == 0) + { + size = SWdiminfo(swathID, datadim); + if (size == -1) + { + status = -1; + HEpush(DFE_GENAPP, "SWdefdimmap", __FILE__, __LINE__); + HEreport("Data dimension name: \"%s\" not found.\n", + datadim); + } + } + + /* Write Dimension Map to Structural MetaData */ + /* ------------------------------------------ */ + if (status == 0) + { + sprintf(mapname, "%s%s%s", geodim, "/", datadim); + metadata[0] = offset; + metadata[1] = increment; + + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + status = EHinsertmeta(sdInterfaceID, swathname, "s", 1L, + mapname, metadata); + + } + } + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdefidxmap | +| | +| DESCRIPTION: Defines indexed (non-linear) mapping between geolocation | +| and data dimensions | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| geodim char Geolocation dimension | +| datadim char Data dimension | +| index int32 Index mapping array | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWdefidxmap(int32 swathID, char *geodim, char *datadim, int32 index[]) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 attrVgrpID; /* Swath attribute ID */ + int32 vdataID; /* Mapping Index Vdata ID */ + int32 gsize; /* Size of geo dim */ + int32 dsize; /* Size of data dim */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 dum; /* Dummy variable */ + + uint8 *buf; /* Vdata field buffer */ + + char mapname[80];/* Mapping name (geodim/datadim) */ + char swathname[80]; /* Swath name */ + char utlbuf[256];/* Utility buffer */ + + + /* Check Swath ID */ + status = SWchkswid(swathID, "SWdefidxmap", &fid, &sdInterfaceID, &dum); + if (status == 0) + { + /* Search Dimension Vdata for dimension entries */ + /* -------------------------------------------- */ + + /* Geo Dimension Search */ + /* -------------------- */ + + gsize = SWdiminfo(swathID, geodim); + + if (gsize == -1) + { + status = -1; + HEpush(DFE_GENAPP, "SWdefidxmap", __FILE__, __LINE__); + HEreport("Geolocation dimension name: \"%s\" not found.\n", + geodim); + } + /* Data Dimension Search */ + /* --------------------- */ + if (status == 0) + { + dsize = SWdiminfo(swathID, datadim); + if (dsize == -1) + { + status = -1; + HEpush(DFE_GENAPP, "SWdefidxmap", __FILE__, __LINE__); + HEreport("Data dimension name: \"%s\" not found.\n", + datadim); + } + } + /* Define Index Vdata and Store Index Array */ + /* ---------------------------------------- */ + if (status == 0) + { + /* Get attribute Vgroup ID and allocate data buffer */ + /* ------------------------------------------------ */ + attrVgrpID = SWXSwath[swathID % idOffset].VIDTable[2]; + buf = (uint8 *) calloc(4 * gsize, 1); + if(buf == NULL) + { + HEpush(DFE_NOSPACE,"SWdefidxmap", __FILE__, __LINE__); + return(-1); + } + + /* Name: "INDXMAP:" + geodim + "/" + datadim */ + sprintf(utlbuf, "%s%s%s%s", "INDXMAP:", geodim, "/", datadim); + + vdataID = VSattach(fid, -1, "w"); + VSsetname(vdataID, utlbuf); + + /* Attribute Class */ + VSsetclass(vdataID, "Attr0.0"); + + /* Fieldname is "Index" */ + VSfdefine(vdataID, "Index", DFNT_INT32, gsize); + VSsetfields(vdataID, "Index"); + memcpy(buf, index, 4 * gsize); + + /* Write to vdata and free data buffer */ + VSwrite(vdataID, buf, 1, FULL_INTERLACE); + free(buf); + + /* Insert in Attribute Vgroup and detach Vdata */ + Vinsert(attrVgrpID, vdataID); + VSdetach(vdataID); + + + /* Write to Structural Metadata */ + sprintf(mapname, "%s%s%s", geodim, "/", datadim); + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + status = EHinsertmeta(sdInterfaceID, swathname, "s", 2L, + mapname, &dum); + + } + } + return (status); + +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdefcomp | +| | +| DESCRIPTION: Defines compression type and parameters | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| compcode int32 compression code | +| compparm intn compression parameters | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Sep 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWdefcomp(int32 swathID, int32 compcode, intn compparm[]) +{ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file id */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 sID; /* swathID - offset */ + + + /* Check for valid swath id */ + status = SWchkswid(swathID, "SWdefcomp", &fid, &sdInterfaceID, &swVgrpID); + + if (status == 0) + { + sID = swathID % idOffset; + + /* Set compression code in compression exteral array */ + SWXSwath[sID].compcode = compcode; + + switch (compcode) + { + /* Set NBIT compression parameters in compression external array */ + case HDFE_COMP_NBIT: + + SWXSwath[sID].compparm[0] = compparm[0]; + SWXSwath[sID].compparm[1] = compparm[1]; + SWXSwath[sID].compparm[2] = compparm[2]; + SWXSwath[sID].compparm[3] = compparm[3]; + + break; + + /* Set GZIP compression parameter in compression external array */ + case HDFE_COMP_DEFLATE: + + SWXSwath[sID].compparm[0] = compparm[0]; + + break; + + } + } + + return (status); +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdefinefield | +| | +| DESCRIPTION: Defines geolocation or data field within swath structure | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| fieldtype char geo/data fieldtype | +| fieldname char fieldname | +| dimlist char Dimension list (comma-separated list) | +| numbertype int32 field type | +| merge int32 merge code | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Check name for length | +| Sep 96 Joel Gales Make string array "dimbuf" dynamic | +| Oct 96 Joel Gales Make sure total length of "merged" Vdata < 64 | +| Jun 03 Abe Taaheri Supplied cast comp_coder_t in call to SDsetcompress | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +SWdefinefield(int32 swathID, char *fieldtype, char *fieldname, char *dimlist, + int32 numbertype, int32 merge) + +{ + intn i; /* Loop index */ + intn status; /* routine return status variable */ + intn found; /* utility found flag */ + intn foundNT = 0;/* found number type flag */ + intn foundAllDim = 1; /* found all dimensions flag */ + intn first = 1; /* first entry flag */ + intn fac; /* Geo (-1), Data (+1) field factor */ + int32 cnt = 0; + + int32 fid; /* HDF-EOS file ID */ + int32 vdataID; /* Vdata ID */ + int32 vgid; /* Geo/Data field Vgroup ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 sdid; /* SDS object ID */ + int32 dimid; /* SDS dimension ID */ + int32 recSize; /* Vdata record size */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 dims[8]; /* Dimension size array */ + int32 dimsize; /* Dimension size */ + int32 rank = 0; /* Field rank */ + int32 slen[32]; /* String length array */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 compcode; /* Compression code */ + int32 sID; /* SwathID - offset */ + + uint8 *oneDbuf; /* Vdata record buffer */ + char *dimbuf; /* Dimension buffer */ + char *comma; /* Pointer to comma */ + char *dimcheck; /* Dimension check buffer */ + char utlbuf[512];/* Utility buffer */ + char utlbuf2[256]; /* Utility buffer 2 */ + char *ptr[32]; /* String pointer array */ + char swathname[80]; /* Swath name */ + char errbuf1[128]; /* Error message buffer 1 */ + char errbuf2[128]; /* Error message buffer 2 */ + char compparmbuf[128]; /* Compression parmeter string buffer */ + + char *HDFcomp[5] = {"HDFE_COMP_NONE", "HDFE_COMP_RLE", + "HDFE_COMP_NBIT", "HDFE_COMP_SKPHUFF", + "HDFE_COMP_DEFLATE"}; + /* Compression code names */ + + uint16 good_number[10] = {3, 4, 5, 6, 20, 21, 22, 23, 24, 25}; + /* Valid number types */ + comp_info c_info; /* Compression parameter structure */ + + + + /* Setup error message strings */ + /* --------------------------- */ + strcpy(errbuf1, "SWXSDname array too small.\nPlease increase "); + strcat(errbuf1, "size of HDFE_NAMBUFSIZE in \"HdfEosDef.h\".\n"); + strcpy(errbuf2, "SWXSDdims array too small.\nPlease increase "); + strcat(errbuf2, "size of HDFE_DIMBUFSIZE in \"HdfEosDef.h\".\n"); + + + + /* + * Check for proper swath ID and return HDF-EOS file ID, SDinterface ID, + * and swath root Vgroup ID + */ + status = SWchkswid(swathID, "SWdefinefield", + &fid, &sdInterfaceID, &swVgrpID); + + + if (status == 0) + { + /* Remove offset from swath ID & get swathname */ + sID = swathID % idOffset; + Vgetname(swVgrpID, swathname); + + /* Allocate space for dimbuf, copy dimlist into it, & append comma */ + dimbuf = (char *) calloc(strlen(dimlist) + 64, 1); + if(dimbuf == NULL) + { + HEpush(DFE_NOSPACE,"SWdefinefield", __FILE__, __LINE__); + return(-1); + } + strcpy(dimbuf, dimlist); + strcat(dimbuf, ","); + + /* Find comma */ + comma = strchr(dimbuf, ','); + + + /* + * Loop through entries in dimension list to make sure they are + * defined in swath + */ + while (comma != NULL) + { + /* Copy dimension list entry to dimcheck */ + dimcheck = (char *) calloc(comma - dimbuf + 1, 1); + if(dimcheck == NULL) + { + HEpush(DFE_NOSPACE,"SWdefinefield", __FILE__, __LINE__); + free(dimbuf); + return(-1); + } + memcpy(dimcheck, dimbuf, comma - dimbuf); + + /* Get dimension size */ + dimsize = SWdiminfo(swathID, dimcheck); + + /* if != -1 then sent found flag, store size and increment rank */ + if (dimsize != -1) + { + dims[rank] = dimsize; + rank++; + } + else + { + /* + * If dimension list entry not found - set error return + * status, append name to utility buffer for error report + */ + status = -1; + foundAllDim = 0; + if (first == 1) + { + strcpy(utlbuf, dimcheck); + } + else + { + strcat(utlbuf, ","); + strcat(utlbuf, dimcheck); + } + first = 0; + } + + /* + * Go to next dimension entry, find next comma, & free up + * dimcheck buffer + */ + *comma = '\0'; /* zero out first comma */ + comma++; + comma = strchr(comma, ','); + if (comma != NULL) + { + for (i=0; i MAX_NC_NAME - 7) +** this was changed because HDF4.1r3 made a change in the +** hlimits.h file. We have notidfied NCSA and asked to have +** it made the same as in previous versions of HDF +** see ncr 26314. DaW Apr 2000 +*/ + + if (((intn) strlen(fieldname) > VSNAMELENMAX && rank == 1) || + ((intn) strlen(fieldname) > (256 - 7) && rank > 1)) + { + status = -1; + HEpush(DFE_GENAPP, "SWdefinefield", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" too long.\n", fieldname); + } + } + + + + + /* Check for valid numbertype */ + /* -------------------------- */ + if (status == 0) + { + for (i = 0; i < 10; i++) + { + if (numbertype == good_number[i]) + { + foundNT = 1; + } + } + + if (foundNT == 0) + { + HEpush(DFE_BADNUMTYPE, "SWdefinefield", __FILE__, __LINE__); + HEreport("Invalid number type: %d (%s).\n", + numbertype, fieldname); + status = -1; + } + } + + + /* Define Field */ + /* ------------ */ + if (status == 0) + { + /* Set factor & get Field Vgroup id */ + /* -------------------------------- */ + if (strcmp(fieldtype, "Geolocation Fields") == 0) + { + fac = -1; + vgid = SWXSwath[sID].VIDTable[0]; + } + else + { + fac = +1; + vgid = SWXSwath[sID].VIDTable[1]; + } + /* + * Note: "fac" is used to destinguish geo fields from data fields + * so that they are not merged together + */ + + + /* One D Fields */ + /* ------------ */ + if (rank == 1) + { + /* No Compression for 1D (Vdata) fields */ + compcode = HDFE_COMP_NONE; + + + /* If field non-appendable and merge set to AUTOMERGE ... */ + if (dims[0] != 0 && merge == HDFE_AUTOMERGE) + { + i = 0; + found = 0; + + /* Loop through previous entries in 1d combination array */ + while (SWX1dcomb[3 * i] != 0) + { + /* Get name of previous 1d combined field */ + vdataID = SWX1dcomb[3 * i + 2]; + VSgetname(vdataID, utlbuf); + + /* + * If dimension, field type (geo/data), and swath + * structure if current entry match a previous entry + * and combined name is less than max allowed then + * set "found" flag and exit loop + */ + if (SWX1dcomb[3 * i] == fac * dims[0] && + SWX1dcomb[3 * i + 1] == swVgrpID && + (intn) strlen(utlbuf) + + (intn) strlen(fieldname) + 1 <= + VSNAMELENMAX) + { + found = 1; + break; + } + /* Increment loop index */ + i++; + } + + + if (found == 0) + { + /* + * If no matching entry found then start new Vdata + * and store dimension size, swath root Vgroup ID, + * field Vdata and fieldname in external array + * "SWX1dcomb" + */ + vdataID = VSattach(fid, -1, "w"); + SWX1dcomb[3 * i] = fac * dims[0]; + SWX1dcomb[3 * i + 1] = swVgrpID; + SWX1dcomb[3 * i + 2] = vdataID; + VSsetname(vdataID, fieldname); + } + else + { + /* + * If match then concatanate current fieldname to + * previous matching fieldnames + */ + strcat(utlbuf, ","); + strcat(utlbuf, fieldname); + VSsetname(vdataID, utlbuf); + } + + /* Define field as field within Vdata */ + VSfdefine(vdataID, fieldname, numbertype, 1); + Vinsert(vgid, vdataID); + + } + else + { + /* 1d No Merge Section */ + + /* Get new vdata ID and establish field within Vdata */ + vdataID = VSattach(fid, -1, "w"); + VSsetname(vdataID, fieldname); + VSfdefine(vdataID, fieldname, numbertype, 1); + VSsetfields(vdataID, fieldname); + + recSize = VSsizeof(vdataID, fieldname); + if (dims[0] == 0) + { + /* + * If appendable field then write single record + * filled with 255 + */ + oneDbuf = (uint8 *) calloc(recSize, 1); + if(oneDbuf == NULL) + { + HEpush(DFE_NOSPACE,"SWdefinefield", __FILE__, __LINE__); + return(-1); + } + for (i = 0; i < recSize; i++) + oneDbuf[i] = 255; + VSwrite(vdataID, oneDbuf, 1, FULL_INTERLACE); + } + else + { + /* + * If non-appendable then write entire field with + * blank records + */ + oneDbuf = (uint8 *) calloc(recSize, dims[0]); + if(oneDbuf == NULL) + { + HEpush(DFE_NOSPACE,"SWdefinefield", __FILE__, __LINE__); + return(-1); + } + VSwrite(vdataID, oneDbuf, dims[0], FULL_INTERLACE); + } + free(oneDbuf); + + /* Insert Vdata into field Vgroup & detach */ + Vinsert(vgid, vdataID); + VSdetach(vdataID); + + } /* End No Merge Section */ + + } /* End 1d field Section */ + else + { + /* SDS Interface (Multi-dim fields) */ + /* -------------------------------- */ + + /* Get current compression code */ + compcode = SWXSwath[sID].compcode; + + /* + * If rank is less than or equal to 3 (and greater than 1) + * and AUTOMERGE is set and the first dimension is not + * appendable and the compression code is set to none then + * ... + */ + if (rank <= 3 && merge == HDFE_AUTOMERGE && dims[0] != 0 + && compcode == HDFE_COMP_NONE) + { + /* Find first empty slot in external combination array */ + /* --------------------------------------------------- */ + i = 0; + while (SWXSDcomb[5 * i] != 0) + { + i++; + } + + /* + * Store dimensions (with geo/data factor), swath root + * Vgroup ID, and number type in external combination + * array "SWXSDcomb" + */ + + if (rank == 2) + { + /* If 2-dim field then set lowest dimension to +/- 1 */ + SWXSDcomb[5 * i] = fac; + SWXSDcomb[5 * i + 1] = fac * dims[0]; + SWXSDcomb[5 * i + 2] = fac * dims[1]; + } + else + { + SWXSDcomb[5 * i] = fac * dims[0]; + SWXSDcomb[5 * i + 1] = fac * dims[1]; + SWXSDcomb[5 * i + 2] = fac * dims[2]; + } + + SWXSDcomb[5 * i + 3] = swVgrpID; + SWXSDcomb[5 * i + 4] = numbertype; + + + /* Concatanate fieldname with combined name string */ + /* ----------------------------------------------- */ + if ((intn) strlen(SWXSDname) + + (intn) strlen(fieldname) + 2 < HDFE_NAMBUFSIZE) + { + strcat(SWXSDname, fieldname); + strcat(SWXSDname, ","); + } + else + { + /* SWXSDname array too small! */ + /* -------------------------- */ + HEpush(DFE_GENAPP, "SWdefinefield", + __FILE__, __LINE__); + HEreport(errbuf1); + status = -1; + return (status); + } + + + + /* + * If 2-dim field then set lowest dimension (in 3-dim + * array) to "ONE" + */ + if (rank == 2) + { + if ((intn) strlen(SWXSDdims) + 5 < HDFE_DIMBUFSIZE) + { + strcat(SWXSDdims, "ONE,"); + } + else + { + /* SWXSDdims array too small! */ + /* -------------------------- */ + HEpush(DFE_GENAPP, "SWdefinefield", + __FILE__, __LINE__); + HEreport(errbuf2); + status = -1; + return (status); + } + + } + + /* + * Concatanate field dimlist to merged dimlist and + * separate fields with semi-colon + */ + if ((intn) strlen(SWXSDdims) + + (intn) strlen(dimlist) + 2 < HDFE_DIMBUFSIZE) + { + strcat(SWXSDdims, dimlist); + strcat(SWXSDdims, ";"); + } + else + { + /* SWXSDdims array too small! */ + /* -------------------------- */ + HEpush(DFE_GENAPP, "SWdefinefield", + __FILE__, __LINE__); + HEreport(errbuf2); + status = -1; + return (status); + } + + } /* End Multi-Dim Merge Section */ + else + { + /* Multi-Dim No Merge Section */ + /* ========================== */ + + /* Create SDS dataset */ + /* ------------------ */ + sdid = SDcreate(sdInterfaceID, fieldname, + numbertype, rank, dims); + + + /* Store Dimension Names in SDS */ + /* ---------------------------- */ + rank = EHparsestr(dimlist, ',', ptr, slen); + for (i = 0; i < rank; i++) + { + /* Dimension name = Swathname:Dimname */ + memcpy(utlbuf, ptr[i], slen[i]); + utlbuf[slen[i]] = 0; + strcat(utlbuf, ":"); + strcat(utlbuf, swathname); + + dimid = SDgetdimid(sdid, i); + SDsetdimname(dimid, utlbuf); + } + + + /* Setup compression parameters */ + if (compcode == HDFE_COMP_NBIT) + { + c_info.nbit.nt = numbertype; + c_info.nbit.sign_ext = SWXSwath[sID].compparm[0]; + c_info.nbit.fill_one = SWXSwath[sID].compparm[1]; + c_info.nbit.start_bit = SWXSwath[sID].compparm[2]; + c_info.nbit.bit_len = SWXSwath[sID].compparm[3]; + } + else if (compcode == HDFE_COMP_SKPHUFF) + { + c_info.skphuff.skp_size = (intn) DFKNTsize(numbertype); + } + else if (compcode == HDFE_COMP_DEFLATE) + { + c_info.deflate.level = SWXSwath[sID].compparm[0]; + } + + /* If field is compressed then call SDsetcompress */ + /* ---------------------------------------------- */ + if (compcode != HDFE_COMP_NONE) + { + status = SDsetcompress(sdid, (comp_coder_t) compcode, &c_info); + } + + + /* Attach to Vgroup */ + /* ---------------- */ + Vaddtagref(vgid, DFTAG_NDG, SDidtoref(sdid)); + + + /* Store SDS dataset IDs */ + /* --------------------- */ + if (SWXSwath[sID].nSDS > 0) + { + SWXSwath[sID].sdsID = (int32 *) + realloc((void *) SWXSwath[sID].sdsID, + (SWXSwath[sID].nSDS + 1) * 4); + if(SWXSwath[sID].sdsID == NULL) + { + HEpush(DFE_NOSPACE,"SWdefinefield", __FILE__, __LINE__); + return(-1); + } + + } + else + { + SWXSwath[sID].sdsID = (int32 *) calloc(1, 4); + if(SWXSwath[sID].sdsID == NULL) + { + HEpush(DFE_NOSPACE,"SWdefinefield", __FILE__, __LINE__); + return(-1); + } + } + SWXSwath[sID].sdsID[SWXSwath[sID].nSDS] = sdid; + SWXSwath[sID].nSDS++; + + } /* End Multi-Dim No Merge Section */ + + } /* End Multi-Dim Section */ + + + + /* Setup metadata string */ + /* --------------------- */ + sprintf(utlbuf, "%s%s%s", fieldname, ":", dimlist); + + + /* Setup compression metadata */ + /* -------------------------- */ + if (compcode != HDFE_COMP_NONE) + { + sprintf(utlbuf2, + "%s%s", + ":\n\t\t\t\tCompressionType=", HDFcomp[compcode]); + + switch (compcode) + { + case HDFE_COMP_NBIT: + + sprintf(compparmbuf, + "%s%d,%d,%d,%d%s", + "\n\t\t\t\tCompressionParams=(", + SWXSwath[sID].compparm[0], + SWXSwath[sID].compparm[1], + SWXSwath[sID].compparm[2], + SWXSwath[sID].compparm[3], ")"); + strcat(utlbuf2, compparmbuf); + break; + + + case HDFE_COMP_DEFLATE: + + sprintf(compparmbuf, + "%s%d", + "\n\t\t\t\tDeflateLevel=", + SWXSwath[sID].compparm[0]); + strcat(utlbuf2, compparmbuf); + break; + } + + /* Concatanate compression parameters with compression code */ + strcat(utlbuf, utlbuf2); + } + + + /* Insert field metadata within File Structural Metadata */ + /* ----------------------------------------------------- */ + if (strcmp(fieldtype, "Geolocation Fields") == 0) + { + status = EHinsertmeta(sdInterfaceID, swathname, "s", 3L, + utlbuf, &numbertype); + } + else + { + status = EHinsertmeta(sdInterfaceID, swathname, "s", 4L, + utlbuf, &numbertype); + } + + } + } + + /* If all dimensions not found then report error */ + /* --------------------------------------------- */ + if (foundAllDim == 0) + { + HEpush(DFE_GENAPP, "SWdefinefield", __FILE__, __LINE__); + HEreport("Dimension(s): \"%s\" not found (%s).\n", + utlbuf, fieldname); + status = -1; + } + + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdefgeofield | +| | +| DESCRIPTION: Defines geolocation field within swath structure (wrapper) | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| fieldname char fieldname | +| dimlist char Dimension list (comma-separated list) | +| numbertype int32 field type | +| merge int32 merge code | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWdefgeofield(int32 swathID, char *fieldname, char *dimlist, + int32 numbertype, int32 merge) +{ + intn status; /* routine return status variable */ + + /* Call SWdefinefield routine */ + /* -------------------------- */ + status = SWdefinefield(swathID, "Geolocation Fields", fieldname, dimlist, + numbertype, merge); + + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdefdatafield | +| | +| DESCRIPTION: Defines data field within swath structure (wrapper) | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| fieldname char fieldname | +| dimlist char Dimension list (comma-separated list) | +| numbertype int32 field type | +| merge int32 merge code | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWdefdatafield(int32 swathID, char *fieldname, char *dimlist, + int32 numbertype, int32 merge) +{ + intn status; /* routine return status variable */ + + /* Call SWdefinefield routine */ + /* -------------------------- */ + status = SWdefinefield(swathID, "Data Fields", fieldname, dimlist, + numbertype, merge); + + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWwritegeometa | +| | +| DESCRIPTION: Defines structural metadata for pre-existing geolocation | +| field within swath structure | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| fieldname char fieldname | +| dimlist char Dimension list (comma-separated list) | +| numbertype int32 field type | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWwritegeometa(int32 swathID, char *fieldname, char *dimlist, + int32 numbertype) +{ + intn status = 0; /* routine return status variable */ + + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 dum; /* dummy variable */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + + char utlbuf[256];/* Utility buffer */ + char swathname[80]; /* Swath name */ + + status = SWchkswid(swathID, "SWwritegeometa", &dum, &sdInterfaceID, + &dum); + + if (status == 0) + { + /* Setup and write field metadata */ + /* ------------------------------ */ + sprintf(utlbuf, "%s%s%s", fieldname, ":", dimlist); + + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + status = EHinsertmeta(sdInterfaceID, swathname, "s", 3L, + utlbuf, &numbertype); + } + + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWwritedatameta | +| | +| DESCRIPTION: Defines structural metadata for pre-existing data | +| field within swath structure | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| fieldname char fieldname | +| dimlist char Dimension list (comma-separated list) | +| numbertype int32 field type | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWwritedatameta(int32 swathID, char *fieldname, char *dimlist, + int32 numbertype) +{ + intn status = 0; /* routine return status variable */ + + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 dum; /* dummy variable */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + + char utlbuf[256];/* Utility buffer */ + char swathname[80]; /* Swath name */ + + status = SWchkswid(swathID, "SWwritedatameta", &dum, &sdInterfaceID, + &dum); + + if (status == 0) + { + /* Setup and write field metadata */ + /* ------------------------------ */ + sprintf(utlbuf, "%s%s%s", fieldname, ":", dimlist); + + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + status = EHinsertmeta(sdInterfaceID, swathname, "s", 4L, + utlbuf, &numbertype); + } + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWwrrdattr | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| attrname char attribute name | +| numbertype int32 attribute HDF numbertype | +| count int32 Number of attribute elements | +| wrcode char Read/Write Code "w/r" | +| datbuf void I/O buffer | +| | +| OUTPUTS: | +| datbuf | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Get Attribute Vgroup ID from external array | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +SWwrrdattr(int32 swathID, char *attrname, int32 numbertype, int32 count, + char *wrcode, VOIDP datbuf) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 attrVgrpID; /* Swath attribute ID */ + int32 dum; /* dummy variable */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + + /* Check Swath id */ + status = SWchkswid(swathID, "SWwrrdattr", &fid, &dum, &dum); + + if (status == 0) + { + /* Get attribute Vgroup ID and call EHattr to perform I/O */ + /* ------------------------------------------------------ */ + attrVgrpID = SWXSwath[swathID % idOffset].VIDTable[2]; + status = EHattr(fid, attrVgrpID, attrname, numbertype, count, + wrcode, datbuf); + } + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWwriteattr | +| | +| DESCRIPTION: Writes/updates attribute in a swath. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| attrname char attribute name | +| numbertype int32 attribute HDF numbertype | +| count int32 Number of attribute elements | +| datbuf void I/O buffer | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWwriteattr(int32 swathID, char *attrname, int32 numbertype, int32 count, + VOIDP datbuf) +{ + intn status = 0; /* routine return status variable */ + + /* Call SWwrrdattr routine to write attribute */ + /* ------------------------------------------ */ + status = SWwrrdattr(swathID, attrname, numbertype, count, "w", datbuf); + + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWreadattr | +| | +| DESCRIPTION: Reads attribute from a swath. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| attrname char attribute name | +| | +| OUTPUTS: | +| datbuf void I/O buffer | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWreadattr(int32 swathID, char *attrname, VOIDP datbuf) +{ + intn status = 0; /* routine return status variable */ + int32 dum = 0; /* dummy variable */ + + /* Call SWwrrdattr routine to read attribute */ + /* ----------------------------------------- */ + status = SWwrrdattr(swathID, attrname, dum, dum, "r", datbuf); + + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWattrinfo | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| attrname char attribute name | +| | +| OUTPUTS: | +| numbertype int32 attribute HDF numbertype | +| count int32 Number of attribute elements | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Get Attribute Vgroup ID from external array | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWattrinfo(int32 swathID, char *attrname, int32 * numbertype, int32 * count) +{ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 attrVgrpID; /* Swath attribute ID */ + int32 dum; /* dummy variable */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + + /* Check for valid swath ID */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWattrinfo", &fid, &dum, &dum); + + if (status == 0) + { + /* Get attribute Vgroup ID and call EHattrinfo */ + /* ------------------------------------------- */ + attrVgrpID = SWXSwath[swathID % idOffset].VIDTable[2]; + + status = EHattrinfo(fid, attrVgrpID, attrname, numbertype, + count); + } + return (status); +} + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWinqattrs | +| | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nattr int32 Number of attributes in swath struct | +| | +| INPUTS: | +| swath ID int32 swath structure ID | +| | +| OUTPUTS: | +| attrnames char Attribute names in swath struct | +| (Comma-separated list) | +| strbufsize int32 Attributes name list string length | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Initialize nattr | +| Oct 96 Joel Gales Get Attribute Vgroup ID from external array | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWinqattrs(int32 swathID, char *attrnames, int32 * strbufsize) +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 attrVgrpID; /* Swath attribute ID */ + int32 dum; /* dummy variable */ + int32 nattr = 0; /* Number of attributes */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + + /* Check Swath id */ + status = SWchkswid(swathID, "SWinqattrs", &fid, &dum, &dum); + + if (status == 0) + { + /* Get attribute Vgroup ID and call EHattrcat */ + /* ------------------------------------------ */ + attrVgrpID = SWXSwath[swathID % idOffset].VIDTable[2]; + + nattr = EHattrcat(fid, attrVgrpID, attrnames, strbufsize); + } + + return (nattr); +} + +#define REMQUOTE \ +\ +memmove(utlstr, utlstr + 1, strlen(utlstr) - 2); \ +utlstr[strlen(utlstr) - 2] = 0; + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWinqdims | +| | +| DESCRIPTION: Returns dimension names and values defined in swath structure | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nDim int32 Number of defined dimensions | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| | +| OUTPUTS: | +| dimnames char Dimension names (comma-separated) | +| dims int32 Dimension values | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Feb 97 Joel Gales Set nDim to -1 if status = -1 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWinqdims(int32 swathID, char *dimnames, int32 dims[]) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 size; /* Dimension size */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 nDim = 0; /* Number of dimensions */ + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char swathname[80]; /* Swath Name */ + char *utlstr; /* Utility string */ + + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"SWinqdims", __FILE__, __LINE__); + return(-1); + } + + /* Check for valid swath id */ + status = SWchkswid(swathID, "SWinqdims", &fid, &sdInterfaceID, &swVgrpID); + + if (status == 0) + { + /* If dimension names or sizes are desired ... */ + /* ------------------------------------------- */ + if (dimnames != NULL || dims != NULL) + { + /* Get swath name */ + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + + /* Get pointers to "Dimension" section within SM */ + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "Dimension", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + /* If dimension names are desired then "clear" name buffer */ + if (dimnames != NULL) + { + dimnames[0] = 0; + } + + + /* Begin loop through dimension entries in metadata */ + /* ------------------------------------------------ */ + while (1) + { + /* Search for OBJECT string */ + metaptrs[0] = strstr(metaptrs[0], "\t\tOBJECT="); + + /* If found within "Dimension" metadata section ... */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + /* Get Dimension Name (if desired) */ + if (dimnames != NULL) + { + /* Check 1st for old meta data then new */ + /* ------------------------------------ */ + EHgetmetavalue(metaptrs, "OBJECT", utlstr); + + /* + * If OBJECT value begins with double quote then old + * metadata, dimension name is OBJECT value. + * Otherwise search for "DimensionName" string + */ + if (utlstr[0] != '"') + { + metaptrs[0] = + strstr(metaptrs[0], "\t\t\t\tDimensionName="); + EHgetmetavalue(metaptrs, "DimensionName", utlstr); + } + + /* Strip off double quotes */ + /* ----------------------- */ + REMQUOTE + + /* If not first name then add comma delimitor */ + if (nDim > 0) + { + strcat(dimnames, ","); + } + /* Add dimension name to dimension list */ + strcat(dimnames, utlstr); + } + + /* Get Dimension Size (if desired) */ + if (dims != NULL) + { + EHgetmetavalue(metaptrs, "Size", utlstr); + size = atol(utlstr); + dims[nDim] = size; + } + /* Increment number of dimensions */ + nDim++; + } + else + /* No more dimensions found */ + { + break; + } + } + free(metabuf); + } + } + + + /* Set nDim to -1 if error status exists */ + /* ------------------------------------- */ + if (status == -1) + { + nDim = -1; + } + free(utlstr); + + return (nDim); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWinqmaps | +| | +| DESCRIPTION: Returns dimension mappings and offsets and increments | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nMap int32 Number of dimension mappings | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| | +| OUTPUTS: | +| dimmaps char dimension mappings (comma-separated) | +| offset int32 array of offsets | +| increment int32 array of increments | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Feb 97 Joel Gales Set nMap to -1 if status = -1 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWinqmaps(int32 swathID, char *dimmaps, int32 offset[], int32 increment[]) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 off; /* Mapping Offset */ + int32 incr; /* Mapping Increment */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 nMap = 0; /* Number of mappings */ + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char swathname[80]; /* Swath Name */ + char *utlstr; /* Utility string */ + + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"SWinqmaps", __FILE__, __LINE__); + return(-1); + } + + /* Check for valid swath id */ + status = SWchkswid(swathID, "SWinqmaps", &fid, &sdInterfaceID, &swVgrpID); + if (status == 0) + { + /* If mapping names or offsets or increments desired ... */ + /* ----------------------------------------------------- */ + if (dimmaps != NULL || offset != NULL || increment != NULL) + { + + /* Get swath name */ + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + + /* Get pointers to "DimensionMap" section within SM */ + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "DimensionMap", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + /* If mapping names are desired then "clear" name buffer */ + if (dimmaps != NULL) + { + dimmaps[0] = 0; + } + + /* Begin loop through mapping entries in metadata */ + /* ---------------------------------------------- */ + while (1) + { + /* Search for OBJECT string */ + metaptrs[0] = strstr(metaptrs[0], "\t\tOBJECT="); + + /* If found within "DimensionMap" metadata section ... */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + /* Get Geo & Data Dimensions (if desired) */ + if (dimmaps != NULL) + { + /* Get Geo Dim, remove quotes, add "/" */ + EHgetmetavalue(metaptrs, "GeoDimension", utlstr); + REMQUOTE + strcat(utlstr, "/"); + + /* if not first map then add comma delimitor */ + if (nMap > 0) + { + strcat(dimmaps, ","); + } + + /* Add to map list */ + strcat(dimmaps, utlstr); + + /* Get Data Dim, remove quotes */ + EHgetmetavalue(metaptrs, "DataDimension", utlstr); + REMQUOTE + + /* Add to map list */ + strcat(dimmaps, utlstr); + } + + /* Get Offset (if desired) */ + if (offset != NULL) + { + EHgetmetavalue(metaptrs, "Offset", utlstr); + off = atol(utlstr); + offset[nMap] = off; + } + + /* Get Increment (if desired) */ + if (increment != NULL) + { + EHgetmetavalue(metaptrs, "Increment", utlstr); + incr = atol(utlstr); + increment[nMap] = incr; + } + + /* Increment number of maps */ + nMap++; + } + else + /* No more mappings found */ + { + break; + } + } + free(metabuf); + } + } + + + /* Set nMap to -1 if error status exists */ + /* ------------------------------------- */ + if (status == -1) + { + nMap = -1; + } + free(utlstr); + + return (nMap); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWinqidxmaps | +| | +| DESCRIPTION: Returns indexed mappings and index sizes | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nMap int32 Number of indexed dimension mappings | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| | +| OUTPUTS: | +| idxmaps char indexed dimension mappings | +| (comma-separated) | +| idxsizes int32 Number of elements in each mapping | +| | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Feb 97 Joel Gales Set nMap to -1 if status = -1 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWinqidxmaps(int32 swathID, char *idxmaps, int32 idxsizes[]) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 nMap = 0; /* Number of mappings */ + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char swathname[80]; /* Swath Name */ + char *utlstr; /* Utility string */ + char *slash; /* Pointer to slash */ + + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"SWinqidxmaps", __FILE__, __LINE__); + return(-1); + } + /* Check for valid swath id */ + status = SWchkswid(swathID, "SWinqidxmaps", &fid, + &sdInterfaceID, &swVgrpID); + + if (status == 0) + { + /* If mapping names or index sizes desired ... */ + /* ------------------------------------------- */ + if (idxmaps != NULL || idxsizes != NULL) + { + /* Get swath name */ + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + + /* Get pointers to "IndexDimensionMap" section within SM */ + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "IndexDimensionMap", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + /* If mapping names are desired then "clear" name buffer */ + if (idxmaps != NULL) + { + idxmaps[0] = 0; + } + + /* Begin loop through mapping entries in metadata */ + /* ---------------------------------------------- */ + while (1) + { + /* Search for OBJECT string */ + metaptrs[0] = strstr(metaptrs[0], "\t\tOBJECT="); + + /* If found within "IndexDimensionMap" metadata section ... */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + /* Get Geo & Data Dimensions and # of indices */ + if (idxmaps != NULL) + { + /* Get Geo Dim, remove quotes, add "/" */ + EHgetmetavalue(metaptrs, "GeoDimension", utlstr); + REMQUOTE + strcat(utlstr, "/"); + + /* if not first map then add comma delimitor */ + if (nMap > 0) + { + strcat(idxmaps, ","); + } + + /* Add to map list */ + strcat(idxmaps, utlstr); + + + /* Get Index size (if desired) */ + if (idxsizes != NULL) + { + /* Parse off geo dimension and find its size */ + slash = strchr(utlstr, '/'); + *slash = 0; + idxsizes[nMap] = SWdiminfo(swathID, utlstr); + } + + + /* Get Data Dim, remove quotes */ + EHgetmetavalue(metaptrs, "DataDimension", utlstr); + REMQUOTE + + /* Add to map list */ + strcat(idxmaps, utlstr); + } + + /* Increment number of maps */ + nMap++; + } + else + /* No more mappings found */ + { + break; + } + } + free(metabuf); + } + } + + + /* Set nMap to -1 if error status exists */ + /* ------------------------------------- */ + if (status == -1) + { + nMap = -1; + } + free(utlstr); + + return (nMap); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWinqfields | +| | +| DESCRIPTION: Returns fieldnames, ranks and numbertypes defined in swath. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nFld int32 Number of (geo/data) fields in swath | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| fieldtype char field type (geo or data) | +| | +| | +| OUTPUTS: | +| fieldlist char Field names (comma-separated) | +| rank int32 Array of ranks | +| numbertype int32 Array of HDF number types | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Feb 97 Joel Gales Set nFld to -1 if status = -1 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static int32 +SWinqfields(int32 swathID, char *fieldtype, char *fieldlist, int32 rank[], + int32 numbertype[]) + +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 nFld = 0; /* Number of mappings */ + int32 slen[8]; /* String length array */ + + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char swathname[80]; /* Swath Name */ + char *utlstr; /* Utility string */ + char *utlstr2; /* Utility string 2 */ + char *ptr[8]; /* String pointer array */ + + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"SWinqfields", __FILE__, __LINE__); + return(-1); + } + + utlstr2 = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr2 == NULL) + { + HEpush(DFE_NOSPACE,"SWinqfields", __FILE__, __LINE__); + free(utlstr); + return(-1); + } + + /* Check for valid swath id */ + status = SWchkswid(swathID, "SWinqfields", + &fid, &sdInterfaceID, &swVgrpID); + + if (status == 0) + { + /* If field names, ranks, or number types desired ... */ + /* --------------------------------------------------- */ + if (fieldlist != NULL || rank != NULL || numbertype != NULL) + { + /* Get swath name */ + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + + /* Get pointers to "GeoField" or "DataField" section within SM */ + if (strcmp(fieldtype, "Geolocation Fields") == 0) + { + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "GeoField", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + free(utlstr2); + return(-1); + } + strcpy(utlstr2, "GeoFieldName"); + } + else + { + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "DataField", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + free(utlstr2); + return(-1); + } + strcpy(utlstr2, "DataFieldName"); + } + + + /* If field names are desired then "clear" name buffer */ + if (fieldlist != NULL) + { + fieldlist[0] = 0; + } + + + /* Begin loop through mapping entries in metadata */ + /* ---------------------------------------------- */ + while (1) + { + /* Search for OBJECT string */ + metaptrs[0] = strstr(metaptrs[0], "\t\tOBJECT="); + + /* If found within "Geo" or "Data" Field metadata section .. */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + /* Get Fieldnames (if desired) */ + if (fieldlist != NULL) + { + /* Check 1st for old meta data then new */ + /* ------------------------------------ */ + EHgetmetavalue(metaptrs, "OBJECT", utlstr); + + /* + * If OBJECT value begins with double quote then old + * metadata, field name is OBJECT value. Otherwise + * search for "GeoFieldName" or "DataFieldName" + * string + */ + + if (utlstr[0] != '"') + { + strcpy(utlstr, "\t\t\t\t"); + strcat(utlstr, utlstr2); + strcat(utlstr, "="); + metaptrs[0] = strstr(metaptrs[0], utlstr); + EHgetmetavalue(metaptrs, utlstr2, utlstr); + } + + /* Strip off double quotes */ + /* ----------------------- */ + REMQUOTE + + + /* Add to fieldlist */ + /* ---------------- */ + if (nFld > 0) + { + strcat(fieldlist, ","); + } + strcat(fieldlist, utlstr); + + } + /* Get Numbertype */ + if (numbertype != NULL) + { + EHgetmetavalue(metaptrs, "DataType", utlstr); + numbertype[nFld] = EHnumstr(utlstr); + } + /* + * Get Rank (if desired) by counting # of dimensions in + * "DimList" string + */ + if (rank != NULL) + { + EHgetmetavalue(metaptrs, "DimList", utlstr); + rank[nFld] = EHparsestr(utlstr, ',', ptr, slen); + } + /* Increment number of fields */ + nFld++; + } + else + /* No more fields found */ + { + break; + } + } + free(metabuf); + } + } + + /* Set nFld to -1 if error status exists */ + /* ------------------------------------- */ + if (status == -1) + { + nFld = -1; + } + + free(utlstr); + free(utlstr2); + + return (nFld); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWinqgeofields | +| | +| DESCRIPTION: Inquires about geo fields in swath | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nflds int32 Number of geo fields in swath | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| | +| OUTPUTS: | +| fieldlist char Field names (comma-separated) | +| rank int32 Array of ranks | +| numbertype int32 Array of HDF number types | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWinqgeofields(int32 swathID, char *fieldlist, int32 rank[], + int32 numbertype[]) +{ + + int32 nflds; /* Number of Geolocation fields */ + + /* Call "SWinqfields" routine */ + /* -------------------------- */ + nflds = SWinqfields(swathID, "Geolocation Fields", fieldlist, rank, + numbertype); + + return (nflds); + +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWinqdatafields | +| | +| DESCRIPTION: Inquires about data fields in swath | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nflds int32 Number of data fields in swath | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| | +| OUTPUTS: | +| fieldlist char Field names (comma-separated) | +| rank int32 Array of ranks | +| numbertype int32 Array of HDF number types | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWinqdatafields(int32 swathID, char *fieldlist, int32 rank[], + int32 numbertype[]) +{ + + int32 nflds; /* Number of Data fields */ + + /* Call "SWinqfields" routine */ + /* -------------------------- */ + nflds = SWinqfields(swathID, "Data Fields", fieldlist, rank, + numbertype); + + return (nflds); + +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWnentries | +| | +| DESCRIPTION: Returns number of entries and string buffer size | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nEntries int32 Number of entries | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| entrycode int32 Entry code | +| HDFE_NENTDIM (0) | +| HDFE_NENTMAP (1) | +| HDFE_NENTIMAP (2) | +| HDFE_NENTGFLD (3) | +| HDFE_NENTDFLD (4) | +| | +| | +| OUTPUTS: | +| strbufsize int32 Length of comma-separated list | +| (Does not include null-terminator | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| Feb 97 Joel Gales Set nEntries to -1 if status = -1 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWnentries(int32 swathID, int32 entrycode, int32 * strbufsize) + +{ + intn status; /* routine return status variable */ + intn i; /* Loop index */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 nEntries = 0; /* Number of entries */ + int32 metaflag; /* Old (0), New (1) metadata flag) */ + int32 nVal; /* Number of strings to search for */ + + char *metabuf = NULL; /* Pointer to structural metadata (SM) */ + char *metaptrs[2]; /* Pointers to begin and end of SM section */ + char swathname[80]; /* Swath Name */ + char *utlstr; /* Utility string */ + char valName[2][32]; /* Strings to search for */ + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"SWnemtries", __FILE__, __LINE__); + return(-1); + } + /* Check for valid swath id */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWnentries", &fid, &sdInterfaceID, &swVgrpID); + + if (status == 0) + { + /* Get swath name */ + Vgetname(SWXSwath[swathID % idOffset].IDTable, swathname); + + /* Zero out string buffer size */ + *strbufsize = 0; + + + /* + * Get pointer to relevant section within SM and Get names of + * metadata strings to inquire about + */ + switch (entrycode) + { + case HDFE_NENTDIM: + /* Dimensions */ + { + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "Dimension", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + nVal = 1; + strcpy(&valName[0][0], "DimensionName"); + } + break; + + case HDFE_NENTMAP: + /* Dimension Maps */ + { + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "DimensionMap", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + nVal = 2; + strcpy(&valName[0][0], "GeoDimension"); + strcpy(&valName[1][0], "DataDimension"); + } + break; + + case HDFE_NENTIMAP: + /* Indexed Dimension Maps */ + { + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "IndexDimensionMap", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + nVal = 2; + strcpy(&valName[0][0], "GeoDimension"); + strcpy(&valName[1][0], "DataDimension"); + } + break; + + case HDFE_NENTGFLD: + /* Geolocation Fields */ + { + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "GeoField", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + nVal = 1; + strcpy(&valName[0][0], "GeoFieldName"); + } + break; + + case HDFE_NENTDFLD: + /* Data Fields */ + { + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "DataField", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + nVal = 1; + strcpy(&valName[0][0], "DataFieldName"); + } + break; + } + + + /* + * Check for presence of 'GROUP="' string If found then old metadata, + * search on OBJECT string + */ + if (metabuf) + { + metaflag = (strstr(metabuf, "GROUP=\"") == NULL) ? 1 : 0; + if (metaflag == 0) + { + nVal = 1; + strcpy(&valName[0][0], "\t\tOBJECT"); + } + + + /* Begin loop through entries in metadata */ + /* -------------------------------------- */ + while (1) + { + /* Search for first string */ + strcpy(utlstr, &valName[0][0]); + strcat(utlstr, "="); + metaptrs[0] = strstr(metaptrs[0], utlstr); + + /* If found within relevant metadata section ... */ + if (metaptrs[0] < metaptrs[1] && metaptrs[0] != NULL) + { + for (i = 0; i < nVal; i++) + { + /* + * Get all string values Don't count quotes + */ + EHgetmetavalue(metaptrs, &valName[i][0], utlstr); + *strbufsize += strlen(utlstr) - 2; + } + /* Increment number of entries */ + nEntries++; + + /* Go to end of OBJECT */ + metaptrs[0] = strstr(metaptrs[0], "END_OBJECT"); + } + else + /* No more entries found */ + { + break; + } + } + free(metabuf); + } + + + /* Count comma separators & slashes (if mappings) */ + /* ---------------------------------------------- */ + if (nEntries > 0) + { + *strbufsize += nEntries - 1; + *strbufsize += (nVal - 1) * nEntries; + } + } + + + /* Set nEntries to -1 if error status exists */ + /* ----------------------------------------- */ + if (status == -1) + nEntries = -1; + + free(utlstr); + + return (nEntries); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWinqswath | +| | +| DESCRIPTION: Returns number and names of swath structures in file | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nSwath int32 Number of swath structures in file | +| | +| INPUTS: | +| filename char HDF-EOS filename | +| | +| OUTPUTS: | +| swathlist char List of swath names (comma-separated) | +| strbufsize int32 Length of swathlist | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWinqswath(char *filename, char *swathlist, int32 * strbufsize) +{ + int32 nSwath; /* Number of swath structures in file */ + + /* Call "EHinquire" routine */ + /* ------------------------ */ + nSwath = EHinquire(filename, "SWATH", swathlist, strbufsize); + + return (nSwath); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SW1dfldsrch | +| | +| DESCRIPTION: Retrieves information about a 1D field | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| fid int32 HDF-EOS file ID | +| swathID int32 swath structure ID | +| fieldname const char field name | +| access const char Access code (w/r) | +| | +| | +| OUTPUTS: | +| vgidout int32 Field (geo/data) vgroup ID | +| vdataIDout int32 Field Vdata ID | +| fldtype int32 Field type | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +SW1dfldsrch(int32 fid, int32 swathID, const char *fieldname, const char *access, + int32 * vgidout, int32 * vdataIDout, int32 * fldtype) + +{ + intn status = 0; /* routine return status variable */ + + int32 sID; /* SwathID - offset */ + int32 vgid; /* Swath Geo or Data Vgroup ID */ + int32 vdataID; /* 1d field vdata */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + + + /* Compute "reduced" swath ID */ + /* -------------------------- */ + sID = swathID % idOffset; + + + /* Get Geolocation Vgroup id and 1D field name Vdata id */ + /* ---------------------------------------------------- */ + vgid = SWXSwath[sID].VIDTable[0]; + vdataID = EHgetid(fid, vgid, fieldname, 1, access); + *fldtype = 0; + + + /* + * If name not found in Geolocation Vgroup then detach Geolocation Vgroup + * and search in Data Vgroup + */ + if (vdataID == -1) + { + vgid = SWXSwath[sID].VIDTable[1];; + vdataID = EHgetid(fid, vgid, fieldname, 1, access); + *fldtype = 1; + + /* If field also not found in Data Vgroup then set error status */ + /* ------------------------------------------------------------ */ + if (vdataID == -1) + { + status = -1; + vgid = -1; + vdataID = -1; + } + } + *vgidout = vgid; + *vdataIDout = vdataID; + + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWSDfldsrch | +| | +| DESCRIPTION: Retrieves information SDS field | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| sdInterfaceID int32 SD interface ID | +| fieldname const char field name | +| | +| | +| OUTPUTS: | +| sdid int32 SD element ID | +| rankSDS int32 Rank of SDS | +| rankFld int32 True rank of field (merging) | +| offset int32 Offset of field within merged field | +| dims int32 Dimensions of field | +| solo int32 Solo field flag | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Make metadata ODL compliant | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +SWSDfldsrch(int32 swathID, int32 sdInterfaceID, const char *fieldname, + int32 * sdid, int32 * rankSDS, int32 * rankFld, int32 * offset, + int32 dims[], int32 * solo) +{ + intn i; /* Loop index */ + intn status = -1;/* routine return status variable */ + + int32 sID; /* SwathID - offset */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 dum; /* Dummy variable */ + int32 dums[128]; /* Dummy array */ + int32 attrIndex; /* Attribute index */ + + char name[2048]; /* Merged-Field Names */ + char swathname[80]; /* Swath Name */ + char *utlstr; /* Utility string */ + char *metabuf; /* Pointer to structural metadata (SM) */ + char *metaptrs[2];/* Pointers to begin and end of SM section */ + char *oldmetaptr; /* Pointer within SM section */ + + + /* Allocate space for utility string */ + /* --------------------------------- */ + utlstr = (char *) calloc(UTLSTR_MAX_SIZE, sizeof(char)); + if(utlstr == NULL) + { + HEpush(DFE_NOSPACE,"SWSDfldsrch", __FILE__, __LINE__); + return(-1); + } + /* Set solo flag to 0 (no) */ + /* ----------------------- */ + *solo = 0; + + + /* Compute "reduced" swath ID */ + /* -------------------------- */ + sID = swathID % idOffset; + + + /* Loop through all SDSs in swath */ + /* ------------------------------ */ + for (i = 0; i < SWXSwath[sID].nSDS; i++) + { + /* If active SDS ... */ + /* ----------------- */ + if (SWXSwath[sID].sdsID[i] != 0) + { + /* Get SDS ID, name, rankSDS, and dimensions */ + /* ----------------------------------------- */ + *sdid = SWXSwath[sID].sdsID[i]; + SDgetinfo(*sdid, name, rankSDS, dims, &dum, &dum); + *rankFld = *rankSDS; + + /* If merged field ... */ + /* ------------------- */ + if (strstr(name, "MRGFLD_") == &name[0]) + { + /* Get swath name */ + /* -------------- */ + Vgetname(SWXSwath[sID].IDTable, swathname); + + + /* Get pointers to "MergedFields" section within SM */ + /* ------------------------------------------------ */ + metabuf = (char *) EHmetagroup(sdInterfaceID, swathname, "s", + "MergedFields", metaptrs); + if(metabuf == NULL) + { + free(utlstr); + return(-1); + } + + /* Store metaptr in order to recover */ + /* --------------------------------- */ + oldmetaptr = metaptrs[0]; + + + /* Search for Merged field name */ + /* ---------------------------- */ + sprintf(utlstr, "%s%s%s", "MergedFieldName=\"", + name, "\"\n"); + metaptrs[0] = strstr(metaptrs[0], utlstr); + + + /* If not found check for old metadata */ + /* ----------------------------------- */ + if (metaptrs[0] == NULL) + { + sprintf(utlstr, "%s%s%s", "OBJECT=\"", name, "\"\n"); + metaptrs[0] = strstr(oldmetaptr, utlstr); + } + + + /* Get field list and strip off leading and trailing quotes */ + EHgetmetavalue(metaptrs, "FieldList", name); /* not return status --xhua */ + memmove(name, name + 1, strlen(name) - 2); + name[strlen(name) - 2] = 0; + + /* Search for desired field within merged field list */ + sprintf(utlstr, "%s%s%s", "\"", fieldname, "\""); + dum = EHstrwithin(utlstr, name, ','); + + free(metabuf); + } + else + { + /* If solo (unmerged) check if SDS name matches fieldname */ + /* ------------------------------------------------------ */ + dum = EHstrwithin(fieldname, name, ','); + if (dum != -1) + { + *solo = 1; + *offset = 0; + } + } + + + /* If field found ... */ + /* ------------------ */ + if (dum != -1) + { + status = 0; + + /* If merged field ... */ + /* ------------------- */ + if (*solo == 0) + { + /* Get "Field Offsets" SDS attribute index */ + /* --------------------------------------- */ + attrIndex = SDfindattr(*sdid, "Field Offsets"); + + /* + * If attribute exists then get offset of desired field + * within merged field + */ + if (attrIndex != -1) + { + SDreadattr(*sdid, attrIndex, (VOIDP) dums); + *offset = dums[dum]; + } + + + /* Get "Field Dims" SDS attribute index */ + /* ------------------------------------ */ + attrIndex = SDfindattr(*sdid, "Field Dims"); + + /* + * If attribute exists then get 0th dimension of desired + * field within merged field + */ + if (attrIndex != -1) + { + SDreadattr(*sdid, attrIndex, (VOIDP) dums); + dims[0] = dums[dum]; + + /* If this dimension = 1 then field is really 2 dim */ + /* ------------------------------------------------ */ + if (dums[dum] == 1) + { + *rankFld = 2; + } + } + } + + + /* Break out of SDS loop */ + /* --------------------- */ + break; + } /* End of found field section */ + } + else + { + /* First non-active SDS signifies no more, break out of SDS loop */ + /* ------------------------------------------------------------- */ + break; + } + } + + free(utlstr); + + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWwrrdfield | +| | +| DESCRIPTION: Writes/Reads fields | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| fieldname const char fieldname | +| code const char Write/Read code (w/r) | +| start int32 start array | +| stride int32 stride array | +| edge int32 edge array | +| datbuf void data buffer for read | +| | +| | +| OUTPUTS: | +| datbuf void data buffer for write | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Feb 97 Joel Gales Stride = 1 HDF compression workaround | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +SWwrrdfield(int32 swathID, const char *fieldname, const char *code, + int32 start[], int32 stride[], int32 edge[], VOIDP datbuf) + +{ + intn i; /* Loop index */ + intn status = 0; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 vgid; /* Swath Geo or Data Vgroup ID */ + int32 sdid; /* SDS ID */ + int32 dum; /* Dummy variable */ + int32 rankSDS; /* Rank of SDS */ + int32 rankFld; /* Rank of field */ + + int32 vdataID; /* 1d field vdata */ + int32 recsize; /* Vdata record size */ + int32 fldsize; /* Field size */ + int32 nrec; /* Number of records in Vdata */ + + int32 offset[8]; /* I/O offset (start) */ + int32 incr[8]; /* I/O incrment (stride) */ + int32 count[8]; /* I/O count (edge) */ + int32 dims[8]; /* Field/SDS dimensions */ + int32 mrgOffset; /* Merged field offset */ + int32 nflds; /* Number of fields in Vdata */ + int32 strideOne; /* Strides = 1 flag */ + + uint8 *buf; /* I/O (transfer) buffer */ + uint8 *fillbuf; /* Fill value buffer */ + + char attrName[80]; /* Name of fill value attribute */ + char *ptr[64]; /* String pointer array */ + char fieldlist[256]; /* Vdata field list */ + + + /* Check for valid swath ID */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWwrrdfield", &fid, &sdInterfaceID, &dum); + + + if (status == 0) + { + + /* Check whether fieldname is in SDS (multi-dim field) */ + /* --------------------------------------------------- */ + status = SWSDfldsrch(swathID, sdInterfaceID, fieldname, &sdid, + &rankSDS, &rankFld, &mrgOffset, dims, &dum); + + /* Multi-Dimensional Field Section */ + /* ------------------------------- */ + if (status != -1) + { + /* Set I/O offset Section */ + /* ---------------------- */ + + /* + * If start == NULL (default) set I/O offset of 0th field to + * offset within merged field (if any) and the rest to 0 + */ + if (start == NULL) + { + for (i = 0; i < rankSDS; i++) + { + offset[i] = 0; + } + offset[0] = mrgOffset; + } + else + { + /* + * ... otherwise set I/O offset to user values, adjusting the + * 0th field with the merged field offset (if any) + */ + if (rankFld == rankSDS) + { + for (i = 0; i < rankSDS; i++) + { + offset[i] = start[i]; + } + offset[0] += mrgOffset; + } + else + { + /* + * If field really 2-dim merged in 3-dim field then set + * 0th field offset to merge offset and then next two to + * the user values + */ + for (i = 0; i < rankFld; i++) + { + offset[i + 1] = start[i]; + } + offset[0] = mrgOffset; + } + } + + + + /* Set I/O stride Section */ + /* ---------------------- */ + + /* + * If stride == NULL (default) set I/O stride to 1 + */ + if (stride == NULL) + { + for (i = 0; i < rankSDS; i++) + { + incr[i] = 1; + } + } + else + { + /* + * ... otherwise set I/O stride to user values + */ + if (rankFld == rankSDS) + { + for (i = 0; i < rankSDS; i++) + { + incr[i] = stride[i]; + } + } + else + { + /* + * If field really 2-dim merged in 3-dim field then set + * 0th field stride to 1 and then next two to the user + * values. + */ + for (i = 0; i < rankFld; i++) + { + incr[i + 1] = stride[i]; + } + incr[0] = 1; + } + } + + + + /* Set I/O count Section */ + /* --------------------- */ + + /* + * If edge == NULL (default) set I/O count to number of remaining + * entries (dims - start) / increment. Note that 0th field + * offset corrected for merged field offset (if any). + */ + if (edge == NULL) + { + for (i = 1; i < rankSDS; i++) + { + count[i] = (dims[i] - offset[i]) / incr[i]; + } + count[0] = (dims[0] - (offset[0] - mrgOffset)) / incr[0]; + } + else + { + /* + * ... otherwise set I/O count to user values + */ + if (rankFld == rankSDS) + { + for (i = 0; i < rankSDS; i++) + { + count[i] = edge[i]; + } + } + else + { + /* + * If field really 2-dim merged in 3-dim field then set + * 0th field count to 1 and then next two to the user + * values. + */ + for (i = 0; i < rankFld; i++) + { + count[i + 1] = edge[i]; + } + count[0] = 1; + } + } + + /* Perform I/O with relevant HDF I/O routine */ + /* ----------------------------------------- */ + if (strcmp(code, "w") == 0) + { + /* Set strideOne to true (1) */ + /* ------------------------- */ + strideOne = 1; + + + /* If incr[i] != 1 set strideOne to false (0) */ + /* ------------------------------------------ */ + for (i = 0; i < rankSDS; i++) + { + if (incr[i] != 1) + { + strideOne = 0; + break; + } + } + + + /* + * If strideOne is true use NULL parameter for stride. This + * is a work-around to HDF compression problem + */ + if (strideOne == 1) + { + status = SDwritedata(sdid, offset, NULL, count, + (VOIDP) datbuf); + } + else + { + status = SDwritedata(sdid, offset, incr, count, + (VOIDP) datbuf); + } + } + else + { + status = SDreaddata(sdid, offset, incr, count, + (VOIDP) datbuf); + } + } /* End of Multi-Dimensional Field Section */ + else + { + + /* One-Dimensional Field Section */ + /* ----------------------------- */ + + /* Check fieldname within 1d field Vgroups */ + /* --------------------------------------- */ + status = SW1dfldsrch(fid, swathID, fieldname, code, + &vgid, &vdataID, &dum); + + if (status != -1) + { + + /* Get number of records */ + /* --------------------- */ + nrec = VSelts(vdataID); + + + /* Set offset, increment, & count */ + /* ------------------------------ */ + offset[0] = (start == NULL) ? 0 : start[0]; + incr[0] = (stride == NULL) ? 1 : stride[0]; + count[0] = (edge == NULL) + ? (nrec - offset[0]) / incr[0] + : edge[0]; + + + + /* Write Section */ + /* ------------- */ + if (strcmp(code, "w") == 0) + { + /* Get size of field and setup fill buffer */ + /* --------------------------------------- */ + fldsize = VSsizeof(vdataID, (char *)fieldname); + fillbuf = (uint8 *) calloc(fldsize, 1); + if(fillbuf == NULL) + { + HEpush(DFE_NOSPACE,"SWwrrdfield", __FILE__, __LINE__); + return(-1); + } + + /* Get size of record in Vdata and setup I/O buffer */ + /* ------------------------------------------------ */ + VSQueryvsize(vdataID, &recsize); + buf = (uint8 *) calloc(recsize, count[0] * incr[0]); + if(buf == NULL) + { + HEpush(DFE_NOSPACE,"SWwrrdfield", __FILE__, __LINE__); + return(-1); + } + + + /* Get names and number of fields in each record */ + /* ---------------------------------------------- */ + VSgetfields(vdataID, fieldlist); + dum = EHstrwithin(fieldname, fieldlist, ','); + nflds = EHparsestr(fieldlist, ',', ptr, NULL); + + + /* Get Merged Field Offset (if any) */ + /* -------------------------------- */ + if (nflds > 1) + { + if (dum > 0) + { + *(ptr[dum] - 1) = 0; + mrgOffset = VSsizeof(vdataID, fieldlist); + *(ptr[dum] - 1) = ','; + } + else + { + mrgOffset = 0; + } + + /* Read records to recover previously written data */ + status = VSsetfields(vdataID, fieldlist); + status = VSseek(vdataID, offset[0]); + nrec = VSread(vdataID, buf, count[0] * incr[0], + FULL_INTERLACE); + } + else + { + mrgOffset = 0; + } + + + + /* Fill buffer with "Fill" value (if any) */ + /* -------------------------------------- */ + strcpy(attrName, "_FV_"); + strcat(attrName, fieldname); + + status = SWreadattr(swathID, attrName, (char *) fillbuf); + if (status == 0) + { + for (i = 0; i < count[0] * incr[0]; i++) + { + memcpy(buf + i * recsize + mrgOffset, + fillbuf, fldsize); + } + } + + + /* Write new data into buffer */ + /* -------------------------- */ + if (incr[0] == 1 && nflds == 1) + { + memcpy(buf, datbuf, count[0] * recsize); + } + else + { + for (i = 0; i < count[0]; i++) + { + memcpy(buf + i * recsize * incr[0] + mrgOffset, + (uint8 *) datbuf + i * fldsize, fldsize); + } + } + + + /* If append read last record */ + /* -------------------------- */ + if (offset[0] == nrec) + { + /* abe added "status =" to next line 8/8/97 */ + status = VSseek(vdataID, offset[0] - 1); + VSread(vdataID, fillbuf, 1, FULL_INTERLACE); + } + else + { + status = VSseek(vdataID, offset[0]); + } + + + /* Write data into Vdata */ + /* --------------------- */ + nrec = VSwrite(vdataID, buf, count[0] * incr[0], + FULL_INTERLACE); + + free(fillbuf); + if (status > 0) + status = 0; + + } /* End Write Section */ + else + { + /* Read Section */ + /* ------------ */ + status = VSsetfields(vdataID, fieldname); + fldsize = VSsizeof(vdataID, (char *)fieldname); + buf = (uint8 *) calloc(fldsize, count[0] * incr[0]); + if(buf == NULL) + { + HEpush(DFE_NOSPACE,"SWwrrdfield", __FILE__, __LINE__); + return(-1); + } + + (void) VSseek(vdataID, offset[0]); + (void) VSread(vdataID, buf, count[0] * incr[0], + FULL_INTERLACE); + + + /* Copy from input buffer to returned data buffer */ + /* ---------------------------------------------- */ + if (incr[0] == 1) + { + memcpy(datbuf, buf, count[0] * fldsize); + } + else + { + for (i = 0; i < count[0]; i++) + { + memcpy((uint8 *) datbuf + i * fldsize, + buf + i * fldsize * incr[0], fldsize); + } + } + + } /* End Read Section */ + + free(buf); + VSdetach(vdataID); + } + else + { + HEpush(DFE_GENAPP, "SWwrrdfield", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" does not exist.\n", fieldname); + } + } /* End One-D Field Section */ + + } + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWwritefield | +| | +| DESCRIPTION: Writes data to field | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| fieldname char fieldname | +| start int32 start array | +| stride int32 stride array | +| edge int32 edge array | +| | +| | +| OUTPUTS: | +| data void data buffer for write | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWwritefield(int32 swathID, char *fieldname, + int32 start[], int32 stride[], int32 edge[], VOIDP data) + +{ + intn status = 0; /* routine return status variable */ + + status = SWwrrdfield(swathID, fieldname, "w", start, stride, edge, + data); + return (status); +} + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWreadfield | +| | +| DESCRIPTION: Reads data from field | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| fieldname const char fieldname | +| start int32 start array | +| stride int32 stride array | +| edge int32 edge array | +| buffer void data buffer for read | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWreadfield(int32 swathID, const char *fieldname, + int32 start[], int32 stride[], int32 edge[], VOIDP buffer) + +{ + intn status = 0; /* routine return status variable */ + + status = SWwrrdfield(swathID, fieldname, "r", start, stride, edge, + buffer); + return (status); +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdefboxregion | +| | +| DESCRIPTION: Finds swath cross tracks within area of interest and returns | +| region ID | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| regionID int32 Region ID | +| | +| INPUTS: | +| swathID int32 Swath structure ID | +| cornerlon float64 dec deg Longitude of opposite corners of box | +| cornerlat float64 dec deg Latitude of opposite corners of box | +| mode int32 Search mode | +| HDFE_MIDPOINT - Use midpoint of Xtrack | +| HDFE_ENDPOINT - Use endpoints of Xtrack | +| HDFE_ANYPOINT - Use all points of Xtrack| +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Add ability to handle regions crossing date line | +| Dec 96 Joel Gales Add multiple vertical subsetting capability | +| Jul 98 Abe Taaheri Fixed core dump in SWregioninfo associated with | +| SWXRegion[k]->nRegions exceeding MAXNREGIONS in | +| this function | +| Aug 99 Abe Taaheri Fixed the code so that all cross tracks or all | +| points on the along track that fall inside the box | +| are identified. At the same time added code to | +| function "updatescene" to reject cases where there | +| is single cross track in the box (for LANDSAT) | +| Jun 03 Abe Taaheri Added a few lines to report error and return -1 if | +| regionID exceeded NSWATHREGN | +| Mar 04 Abe Taaheri Added recognition for GeodeticLatitude | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWdefboxregion(int32 swathID, float64 cornerlon[], float64 cornerlat[], + int32 mode) +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn k; /* Loop index */ + + intn status; /* routine return status variable */ + intn statLon; /* Status from SWfieldinfo for longitude */ + intn statLat; /* Status from SWfieldinfo for latitude */ + intn statCoLat = -1; /* Status from SWfieldinfo for + * Colatitude */ + intn statGeodeticLat = -1; /* Status from SWfieldinfo for + * GeodeticLatitude */ + + uint8 found = 0; /* Found flag */ + uint8 *flag; /* Pointer to track flag array */ + intn validReg = -1; /* -1 is invalid validReg */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath Vgroup ID */ + int32 rank; /* Rank of geolocation fields */ + int32 nt; /* Number type of geolocation fields */ + int32 dims[8]; /* Dimensions of geolocation fields */ + int32 nElem; /* Number of elements to read */ + int32 bndflag; /* +/-180 longitude boundary flag */ + int32 lonTest; /* Longitude test flag */ + int32 latTest; /* Latitude test flag */ + int32 start[2]; /* Start array (read) */ + int32 stride[2] = {1, 1}; /* Stride array (read) */ + int32 edge[2]; /* Edge array (read) */ + int32 regionID = -1; /* Region ID (return) */ + int32 anyStart[2];/* ANYPOINT start array (read) */ + int32 anyEdge[2]; /* ANYPOINT edge array (read) */ + + float32 temp32; /* Temporary float32 variable */ + + float64 lonTestVal; /* Longitude test value */ + float64 latTestVal; /* Latitude test value */ + float64 temp64; /* Temporary float64 variable */ + + char *lonArr; /* Longitude data array */ + char *latArr; /* Latitude data array */ + char dimlist[256]; /* Dimension list (geolocation + * fields) */ + char latName[17];/* Latitude field name */ + + + /* Check for valid swath ID */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWdefboxregion", &fid, &sdInterfaceID, + &swVgrpID); + + + /* Inclusion mode must be between 0 and 2 */ + /* -------------------------------------- */ + if (mode < 0 || mode > 2) + { + status = -1; + HEpush(DFE_GENAPP, "SWdefboxregion", __FILE__, __LINE__); + HEreport("Improper Inclusion Mode: %d.\n", mode); + } + + + if (status == 0) + { + /* Get "Longitude" field info */ + /* -------------------------- */ + statLon = SWfieldinfo(swathID, "Longitude", &rank, dims, &nt, dimlist); + if (statLon != 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWdefboxregion", __FILE__, __LINE__); + HEreport("\"Longitude\" field not found.\n"); + } + + /* Get "Latitude" field info */ + /* -------------------------- */ + statLat = SWfieldinfo(swathID, "Latitude", &rank, dims, &nt, dimlist); + if (statLat != 0) + { + /* If not found check for "Colatitude" field info */ + /* ---------------------------------------------- */ + statCoLat = SWfieldinfo(swathID, "Colatitude", &rank, dims, &nt, + dimlist); + if (statCoLat != 0) + { + /* Check again for Geodeticlatitude */ + statGeodeticLat = SWfieldinfo(swathID, + "GeodeticLatitude", &rank, + dims, &nt, dimlist); + if (statGeodeticLat != 0) + { + /* Neither "Latitude" nor "Colatitude" nor + "GeodeticLatitude" field found */ + /* ----------------------------------------------- */ + status = -1; + HEpush(DFE_GENAPP, "SWdefboxregion", __FILE__, __LINE__); + HEreport( + "Neither \"Latitude\" nor \"Colatitude\" nor \"GeodeticLatitude\" fields found.\n"); + } + else + { + /* Latitude field is "GeodeticLatitude" */ + /* ------------------------------ */ + strcpy(latName, "GeodeticLatitude"); + } + } + else + { + /* Latitude field is "Colatitude" */ + /* ------------------------------ */ + strcpy(latName, "Colatitude"); + } + } + else + { + /* Latitude field is "Latitude" */ + /* ---------------------------- */ + strcpy(latName, "Latitude"); + } + + + if (status == 0) + { + /* Search along entire "Track" dimension from beginning to end */ + /* ----------------------------------------------------------- */ + start[0] = 0; + edge[0] = dims[0]; + + + /* If 1D geolocation fields then set mode to MIDPOINT */ + /* -------------------------------------------------- */ + if (rank == 1) + { + mode = HDFE_MIDPOINT; + } + + + switch (mode) + { + /* If MIDPOINT search single point in middle of "CrossTrack" */ + /* --------------------------------------------------------- */ + case HDFE_MIDPOINT: + + start[1] = dims[1] / 2; + edge[1] = 1; + + break; + + /* If ENDPOINT search 2 points at either end of "CrossTrack" */ + /* --------------------------------------------------------- */ + case HDFE_ENDPOINT: + + start[1] = 0; + stride[1] = dims[1] - 1; + edge[1] = 2; + + break; + + /* If ANYPOINT do initial MIDPOINT search */ + /* -------------------------------------- */ + case HDFE_ANYPOINT: + + start[1] = dims[1] / 2; + edge[1] = 1; + + break; + } + + + /* Compute number of elements */ + /* -------------------------- */ + nElem = edge[0] * edge[1]; + + + /* Allocate space for longitude and latitude (float64) */ + /* --------------------------------------------------- */ + lonArr = (char *) calloc(nElem, sizeof(float64)); + if(lonArr == NULL) + { + HEpush(DFE_NOSPACE,"SWdefboxregion", __FILE__, __LINE__); + return(-1); + } + + latArr = (char *) calloc(nElem, sizeof(float64)); + if(latArr == NULL) + { + HEpush(DFE_NOSPACE,"SWdefboxregion", __FILE__, __LINE__); + free(lonArr); + return(-1); + } + + + /* Allocate space for flag array (uint8) */ + /* ------------------------------------- */ + flag = (uint8 *) calloc(edge[0] + 1, 1); + if(flag == NULL) + { + HEpush(DFE_NOSPACE,"SWdefboxregion", __FILE__, __LINE__); + free(lonArr); + free(latArr); + return(-1); + } + + + /* Read Longitude and Latitude fields */ + /* ---------------------------------- */ + status = SWreadfield(swathID, "Longitude", + start, stride, edge, lonArr); + status = SWreadfield(swathID, latName, + start, stride, edge, latArr); + + + + /* + * If geolocation fields are FLOAT32 then cast each entry as + * FLOAT64 + */ + if (nt == DFNT_FLOAT32) + { + for (i = nElem - 1; i >= 0; i--) + { + memcpy(&temp32, lonArr + 4 * i, 4); + temp64 = (float64) temp32; + memcpy(lonArr + 8 * i, &temp64, 8); + + memcpy(&temp32, latArr + 4 * i, 4); + temp64 = (float64) temp32; + memcpy(latArr + 8 * i, &temp64, 8); + } + } + + + /* Set boundary flag */ + /* ----------------- */ + + /* + * This variable is set to 1 if the region of interest crosses + * the +/- 180 longitude boundary + */ + bndflag = (cornerlon[0] < cornerlon[1]) ? 0 : 1; + + + + /* Main Search Loop */ + /* ---------------- */ + + /* For each track ... */ + /* ------------------ */ + + for (i = 0; i < edge[0]; i++) + { + /* For each value from Cross Track ... */ + /* ----------------------------------- */ + for (j = 0; j < edge[1]; j++) + { + /* Read in single lon & lat values from data buffers */ + /* ------------------------------------------------- */ + memcpy(&lonTestVal, &lonArr[8 * (i * edge[1] + j)], 8); + memcpy(&latTestVal, &latArr[8 * (i * edge[1] + j)], 8); + + + /* If longitude value > 180 convert to -180 to 180 range */ + /* ----------------------------------------------------- */ + if (lonTestVal > 180) + { + lonTestVal = lonTestVal - 360; + } + + /* If Colatitude value convert to latitude value */ + /* --------------------------------------------- */ + if (statCoLat == 0) + { + latTestVal = 90 - latTestVal; + } + + + /* Test if lat value is within range */ + /* --------------------------------- */ + latTest = (latTestVal >= cornerlat[0] && + latTestVal <= cornerlat[1]); + + + if (bndflag == 1) + { + /* + * If boundary flag set test whether longitude value + * is outside region and then flip + */ + lonTest = (lonTestVal >= cornerlon[1] && + lonTestVal <= cornerlon[0]); + lonTest = 1 - lonTest; + } + else + { + lonTest = (lonTestVal >= cornerlon[0] && + lonTestVal <= cornerlon[1]); + } + + + /* + * If both longitude and latitude are within region set + * flag on for this track + */ + if (lonTest + latTest == 2) + { + flag[i] = 1; + found = 1; + break; + } + } + } + + + + /* ANYPOINT search */ + /* --------------- */ + if (mode == HDFE_ANYPOINT && rank > 1) + { + free(lonArr); + free(latArr); + + /* Allocate space for an entire single cross track */ + /* ----------------------------------------------- */ + lonArr = (char *) calloc(dims[1], sizeof(float64)); + if(lonArr == NULL) + { + HEpush(DFE_NOSPACE,"SWdefboxregion", __FILE__, __LINE__); + return(-1); + } + + latArr = (char *) calloc(dims[1], sizeof(float64)); + if(latArr == NULL) + { + HEpush(DFE_NOSPACE,"SWdefboxregion", __FILE__, __LINE__); + free(lonArr); + return(-1); + } + + + /* Setup start and edge */ + /* -------------------- */ + anyStart[1] = 0; + anyEdge[0] = 1; + anyEdge[1] = dims[1]; + + + /* For each track starting from 0 */ + /* ------------------------------ */ + for (i = 0; i < edge[0]; i++) + { + + /* If cross track not in region (with MIDPOINT search ... */ + /* ------------------------------------------------------ */ + if (flag[i] == 0) + { + /* Setup track start */ + /* ----------------- */ + anyStart[0] = i; + + + /* Read in lon and lat values for cross track */ + /* ------------------------------------------ */ + status = SWreadfield(swathID, "Longitude", + anyStart, NULL, anyEdge, lonArr); + status = SWreadfield(swathID, latName, + anyStart, NULL, anyEdge, latArr); + + + + /* + * If geolocation fields are FLOAT32 then cast each + * entry as FLOAT64 + */ + if (nt == DFNT_FLOAT32) + { + for (j = dims[1] - 1; j >= 0; j--) + { + memcpy(&temp32, lonArr + 4 * j, 4); + temp64 = (float64) temp32; + memcpy(lonArr + 8 * j, &temp64, 8); + + memcpy(&temp32, latArr + 4 * j, 4); + temp64 = (float64) temp32; + memcpy(latArr + 8 * j, &temp64, 8); + } + } + + + /* For each value from Cross Track ... */ + /* ----------------------------------- */ + for (j = 0; j < dims[1]; j++) + { + /* Read in single lon & lat values from buffers */ + /* -------------------------------------------- */ + memcpy(&lonTestVal, &lonArr[8 * j], 8); + memcpy(&latTestVal, &latArr[8 * j], 8); + + + /* If lon value > 180 convert to -180 - 180 range */ + /* ---------------------------------------------- */ + if (lonTestVal > 180) + { + lonTestVal = lonTestVal - 360; + } + + /* If Colatitude value convert to latitude value */ + /* --------------------------------------------- */ + if (statCoLat == 0) + { + latTestVal = 90 - latTestVal; + } + + + /* Test if lat value is within range */ + /* --------------------------------- */ + latTest = (latTestVal >= cornerlat[0] && + latTestVal <= cornerlat[1]); + + + if (bndflag == 1) + { + /* + * If boundary flag set test whether + * longitude value is outside region and then + * flip + */ + lonTest = (lonTestVal >= cornerlon[1] && + lonTestVal <= cornerlon[0]); + lonTest = 1 - lonTest; + } + else + { + lonTest = (lonTestVal >= cornerlon[0] && + lonTestVal <= cornerlon[1]); + } + + + /* + * If both longitude and latitude are within + * region set flag on for this track + */ + if (lonTest + latTest == 2) + { + flag[i] = 1; + found = 1; + break; + } + } + } + } + } + + /* If within region setup Region Structure */ + /* --------------------------------------- */ + if (found == 1) + { + /* For all entries in SWXRegion array ... */ + /* -------------------------------------- */ + for (k = 0; k < NSWATHREGN; k++) + { + /* If empty region ... */ + /* ------------------- */ + if (SWXRegion[k] == 0) + { + /* Allocate space for region entry */ + /* ------------------------------- */ + SWXRegion[k] = (struct swathRegion *) + calloc(1, sizeof(struct swathRegion)); + if(SWXRegion[k] == NULL) + { + HEpush(DFE_NOSPACE,"SWdefboxregion", __FILE__, __LINE__); + return(-1); + } + + /* Store file and swath ID */ + /* ----------------------- */ + SWXRegion[k]->fid = fid; + SWXRegion[k]->swathID = swathID; + + + /* Set Start & Stop Vertical arrays to -1 */ + /* -------------------------------------- */ + for (j = 0; j < 8; j++) + { + SWXRegion[k]->StartVertical[j] = -1; + SWXRegion[k]->StopVertical[j] = -1; + SWXRegion[k]->StartScan[j] = -1; + SWXRegion[k]->StopScan[j] = -1; + } + + + /* Set region ID */ + /* ------------- */ + regionID = k; + break; + } + } + if (k >= NSWATHREGN) + { + HEpush(DFE_GENAPP, "SWdefboxregion", __FILE__, __LINE__); + HEreport( + "regionID exceeded NSWATHREGN.\n"); + return (-1); + } + + /* Find start and stop of regions */ + /* ------------------------------ */ + + /* Subtract previous flag value from current one */ + /* --------------------------------------------- */ + + /* + * Transisition points will have flag value (+1) start or + * (255 = (uint8) -1) stop of region + */ + for (i = edge[0]; i > 0; i--) + { + flag[i] -= flag[i - 1]; + } + + + for (i = 0; i <= edge[0]; i++) + { + /* Start of region */ + /* --------------- */ + if (flag[i] == 1) + { + /* Increment (multiple) region counter */ + /* ----------------------------------- */ + j = ++SWXRegion[k]->nRegions; + + /* if SWXRegion[k]->nRegions greater than MAXNREGIONS */ + /* free allocated memory and return FAIL */ + + if ((SWXRegion[k]->nRegions) > MAXNREGIONS) + { + HEpush(DFE_GENAPP, "SWdefboxregion", __FILE__, __LINE__); + HEreport("SWXRegion[%d]->nRegions exceeds MAXNREGIONS= %d.\n", k, MAXNREGIONS); + free(lonArr); + free(latArr); + free(flag); + return(-1); + } + + SWXRegion[k]->StartRegion[j - 1] = i; + } + + /* End of region */ + /* ------------- */ + if (flag[i] == 255) + { + SWXRegion[k]->StopRegion[j - 1] = i - 1; + validReg = 0; + } + } + } + free(lonArr); + free(latArr); + free(flag); + } + } + if(validReg==0) + { + return (regionID); + } + else + { + return (-1); + } + +} + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWregionindex | +| | +| DESCRIPTION: Finds swath cross tracks within area of interest and returns | +| region index and region ID | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| regionID int32 Region ID | +| | +| INPUTS: | +| swathID int32 Swath structure ID | +| cornerlon float64 dec deg Longitude of opposite corners of box | +| cornerlat float64 dec deg Latitude of opposite corners of box | +| mode int32 Search mode | +| HDFE_MIDPOINT - Use midpoint of Xtrack | +| HDFE_ENDPOINT - Use endpoints of Xtrack | +| HDFE_ANYPOINT - Use all points of Xtrack| +| | +| OUTPUTS: | +| geodim char geolocation track dimension | +| idxrange int32 indices of region for along track dim. | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Oct 96 Joel Gales Add ability to handle regions crossing date line | +| Dec 96 Joel Gales Add multiple vertical subsetting capability | +| Nov 97 Daw Add multiple vertical subsetting capability | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWregionindex(int32 swathID, float64 cornerlon[], float64 cornerlat[], + int32 mode, char *geodim, int32 idxrange[]) +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn k; /* Loop index */ + + intn l=0; /* Loop index */ + intn tmpVal = 0; /* temp value for start region Delyth Jones*/ + /*intn j1; */ /* Loop index */ + intn status; /* routine return status variable */ + intn mapstatus; /* status for type of mapping */ + intn statLon; /* Status from SWfieldinfo for longitude */ + intn statLat; /* Status from SWfieldinfo for latitude */ + intn statCoLat = -1; /* Status from SWfieldinfo for + * Colatitude */ + intn statGeodeticLat = -1; /* Status from SWfieldinfo for + * GeodeticLatitude */ + + uint8 found = 0; /* Found flag */ + uint8 *flag; /* Pointer to track flag array */ + intn validReg = -1; /* -1 is invalid validReg */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath Vgroup ID */ + int32 rank; /* Rank of geolocation fields */ + int32 nt; /* Number type of geolocation fields */ + int32 dims[8]; /* Dimensions of geolocation fields */ + int32 nElem; /* Number of elements to read */ + int32 bndflag; /* +/-180 longitude boundary flag */ + int32 lonTest; /* Longitude test flag */ + int32 latTest; /* Latitude test flag */ + int32 start[2]; /* Start array (read) */ + int32 stride[2] = {1, 1}; /* Stride array (read) */ + int32 edge[2]; /* Edge array (read) */ + int32 regionID = -1; /* Region ID (return) */ + int32 anyStart[2];/* ANYPOINT start array (read) */ + int32 anyEdge[2]; /* ANYPOINT edge array (read) */ + + float32 temp32; /* Temporary float32 variable */ + + float64 lonTestVal; /* Longitude test value */ + float64 latTestVal; /* Latitude test value */ + float64 temp64; /* Temporary float64 variable */ + + char *lonArr; /* Longitude data array */ + char *latArr; /* Latitude data array */ + char dimlist[256]; /* Dimension list (geolocation + * fields) */ + char latName[17];/* Latitude field name */ + + + /* Check for valid swath ID */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWregionindex", &fid, &sdInterfaceID, + &swVgrpID); + + + /* Inclusion mode must be between 0 and 2 */ + /* -------------------------------------- */ + if (mode < 0 || mode > 2) + { + status = -1; + HEpush(DFE_GENAPP, "SWregionindex", __FILE__, __LINE__); + HEreport("Improper Inclusion Mode: %d.\n", mode); + } + + + if (status == 0) + { + /* Get "Longitude" field info */ + /* -------------------------- */ + statLon = SWfieldinfo(swathID, "Longitude", &rank, dims, &nt, dimlist); + if (statLon != 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWregionindex", __FILE__, __LINE__); + HEreport("\"Longitude\" field not found.\n"); + } + + /* Get "Latitude" field info */ + /* -------------------------- */ + statLat = SWfieldinfo(swathID, "Latitude", &rank, dims, &nt, dimlist); + if (statLat != 0) + { + /* If not found check for "Colatitude" field info */ + /* ---------------------------------------------- */ + statCoLat = SWfieldinfo(swathID, "Colatitude", &rank, dims, &nt, + dimlist); + if (statCoLat != 0) + { + /* Check again for Geodeticlatitude */ + statGeodeticLat = SWfieldinfo(swathID, + "GeodeticLatitude", &rank, + dims, &nt, dimlist); + if (statGeodeticLat != 0) + { + /* Neither "Latitude" nor "Colatitude" field found */ + /* ----------------------------------------------- */ + status = -1; + HEpush(DFE_GENAPP, "SWregionindex", __FILE__, __LINE__); + HEreport( + "Neither \"Latitude\" nor \"Colatitude\" fields found.\n"); + } + else + { + /* Latitude field is "Colatitude" */ + /* ------------------------------ */ + strcpy(latName, "GeodeticLatitude"); + } + } + else + { + /* Latitude field is "Colatitude" */ + /* ------------------------------ */ + strcpy(latName, "Colatitude"); + } + } + else + { + /* Latitude field is "Latitude" */ + /* ---------------------------- */ + strcpy(latName, "Latitude"); + } + + /* This line modifies the dimlist variable so only the along-track */ + /* dimension remains. */ + /* --------------------------------------------------------------- */ + (void) strtok(dimlist,","); + mapstatus = SWgeomapinfo(swathID,dimlist); + (void) strcpy(geodim,dimlist); + + if (status == 0) + { + /* Search along entire "Track" dimension from beginning to end */ + /* ----------------------------------------------------------- */ + start[0] = 0; + edge[0] = dims[0]; + + + /* If 1D geolocation fields then set mode to MIDPOINT */ + /* -------------------------------------------------- */ + if (rank == 1) + { + mode = HDFE_MIDPOINT; + } + + + switch (mode) + { + /* If MIDPOINT search single point in middle of "CrossTrack" */ + /* --------------------------------------------------------- */ + case HDFE_MIDPOINT: + + start[1] = dims[1] / 2; + edge[1] = 1; + + break; + + /* If ENDPOINT search 2 points at either end of "CrossTrack" */ + /* --------------------------------------------------------- */ + case HDFE_ENDPOINT: + + start[1] = 0; + stride[1] = dims[1] - 1; + edge[1] = 2; + + break; + + /* If ANYPOINT do initial MIDPOINT search */ + /* -------------------------------------- */ + case HDFE_ANYPOINT: + + start[1] = dims[1] / 2; + edge[1] = 1; + + break; + } + + + /* Compute number of elements */ + /* -------------------------- */ + nElem = edge[0] * edge[1]; + + + /* Allocate space for longitude and latitude (float64) */ + /* --------------------------------------------------- */ + lonArr = (char *) calloc(nElem, sizeof(float64)); + if(lonArr == NULL) + { + HEpush(DFE_NOSPACE,"SWregionindex", __FILE__, __LINE__); + return(-1); + } + + latArr = (char *) calloc(nElem, sizeof(float64)); + if(latArr == NULL) + { + HEpush(DFE_NOSPACE,"SWregionindex", __FILE__, __LINE__); + free(lonArr); + return(-1); + } + + + /* Allocate space for flag array (uint8) */ + /* ------------------------------------- */ + flag = (uint8 *) calloc(edge[0] + 1, 1); + if(flag == NULL) + { + HEpush(DFE_NOSPACE,"SWregionindex", __FILE__, __LINE__); + free(lonArr); + free(latArr); + return(-1); + } + + + /* Read Longitude and Latitude fields */ + /* ---------------------------------- */ + status = SWreadfield(swathID, "Longitude", + start, stride, edge, lonArr); + status = SWreadfield(swathID, latName, + start, stride, edge, latArr); + + + + /* + * If geolocation fields are FLOAT32 then cast each entry as + * FLOAT64 + */ + if (nt == DFNT_FLOAT32) + { + for (i = nElem - 1; i >= 0; i--) + { + memcpy(&temp32, lonArr + 4 * i, 4); + temp64 = (float64) temp32; + memcpy(lonArr + 8 * i, &temp64, 8); + + memcpy(&temp32, latArr + 4 * i, 4); + temp64 = (float64) temp32; + memcpy(latArr + 8 * i, &temp64, 8); + } + } + + + /* Set boundary flag */ + /* ----------------- */ + + /* + * This variable is set to 1 if the region of interest crosses + * the +/- 180 longitude boundary + */ + bndflag = (cornerlon[0] < cornerlon[1]) ? 0 : 1; + + + + /* Main Search Loop */ + /* ---------------- */ + + /* For each track ... */ + /* ------------------ */ + for (i = 0; i < edge[0]; i++) + { + /* For each value from Cross Track ... */ + /* ----------------------------------- */ + for (j = 0; j < edge[1]; j++) + { + /* Read in single lon & lat values from data buffers */ + /* ------------------------------------------------- */ + memcpy(&lonTestVal, &lonArr[8 * (i * edge[1] + j)], 8); + memcpy(&latTestVal, &latArr[8 * (i * edge[1] + j)], 8); + + + /* If longitude value > 180 convert to -180 to 180 range */ + /* ----------------------------------------------------- */ + if (lonTestVal > 180) + { + lonTestVal = lonTestVal - 360; + } + + /* If Colatitude value convert to latitude value */ + /* --------------------------------------------- */ + if (statCoLat == 0) + { + latTestVal = 90 - latTestVal; + } + + + /* Test if lat value is within range */ + /* --------------------------------- */ + latTest = (latTestVal >= cornerlat[0] && + latTestVal <= cornerlat[1]); + + + if (bndflag == 1) + { + /* + * If boundary flag set test whether longitude value + * is outside region and then flip + */ + lonTest = (lonTestVal >= cornerlon[1] && + lonTestVal <= cornerlon[0]); + lonTest = 1 - lonTest; + } + else + { + lonTest = (lonTestVal >= cornerlon[0] && + lonTestVal <= cornerlon[1]); + } + + + /* + * If both longitude and latitude are within region set + * flag on for this track + */ + if (lonTest + latTest == 2) + { + flag[i] = 1; + found = 1; + break; + } + } + } + + + + /* ANYPOINT search */ + /* --------------- */ + if (mode == HDFE_ANYPOINT && rank > 1) + { + free(lonArr); + free(latArr); + + /* Allocate space for an entire single cross track */ + /* ----------------------------------------------- */ + lonArr = (char *) calloc(dims[1], sizeof(float64)); + if(lonArr == NULL) + { + HEpush(DFE_NOSPACE,"SWregionindex", __FILE__, __LINE__); + return(-1); + } + latArr = (char *) calloc(dims[1], sizeof(float64)); + if(latArr == NULL) + { + HEpush(DFE_NOSPACE,"SWregionindex", __FILE__, __LINE__); + free(lonArr); + return(-1); + } + + /* Setup start and edge */ + /* -------------------- */ + anyStart[1] = 0; + anyEdge[0] = 1; + anyEdge[1] = dims[1]; + + + /* For each track ... */ + /* ------------------ */ + for (i = 0; i < edge[0]; i++) + { + + /* If cross track not in region (with MIDPOINT search ... */ + /* ------------------------------------------------------ */ + if (flag[i] == 0) + { + /* Setup track start */ + /* ----------------- */ + anyStart[0] = i; + + + /* Read in lon and lat values for cross track */ + /* ------------------------------------------ */ + status = SWreadfield(swathID, "Longitude", + anyStart, NULL, anyEdge, lonArr); + status = SWreadfield(swathID, latName, + anyStart, NULL, anyEdge, latArr); + + + + /* + * If geolocation fields are FLOAT32 then cast each + * entry as FLOAT64 + */ + if (nt == DFNT_FLOAT32) + { + for (j = dims[1] - 1; j >= 0; j--) + { + memcpy(&temp32, lonArr + 4 * j, 4); + temp64 = (float64) temp32; + memcpy(lonArr + 8 * j, &temp64, 8); + + memcpy(&temp32, latArr + 4 * j, 4); + temp64 = (float64) temp32; + memcpy(latArr + 8 * j, &temp64, 8); + } + } + + + /* For each value from Cross Track ... */ + /* ----------------------------------- */ + for (j = 0; j < dims[1]; j++) + { + /* Read in single lon & lat values from buffers */ + /* -------------------------------------------- */ + memcpy(&lonTestVal, &lonArr[8 * j], 8); + memcpy(&latTestVal, &latArr[8 * j], 8); + + + /* If lon value > 180 convert to -180 - 180 range */ + /* ---------------------------------------------- */ + if (lonTestVal > 180) + { + lonTestVal = lonTestVal - 360; + } + + /* If Colatitude value convert to latitude value */ + /* --------------------------------------------- */ + if (statCoLat == 0) + { + latTestVal = 90 - latTestVal; + } + + + /* Test if lat value is within range */ + /* --------------------------------- */ + latTest = (latTestVal >= cornerlat[0] && + latTestVal <= cornerlat[1]); + + + if (bndflag == 1) + { + /* + * If boundary flag set test whether + * longitude value is outside region and then + * flip + */ + lonTest = (lonTestVal >= cornerlon[1] && + lonTestVal <= cornerlon[0]); + lonTest = 1 - lonTest; + } + else + { + lonTest = (lonTestVal >= cornerlon[0] && + lonTestVal <= cornerlon[1]); + } + + + /* + * If both longitude and latitude are within + * region set flag on for this track + */ + if (lonTest + latTest == 2) + { + flag[i] = 1; + found = 1; + break; + } + } + } + } + } + /* + for (j1 = 0; j1 < edge[0]; j1++) + { + idxrange[j1] = (int32) flag[j1]; + } + */ + /* If within region setup Region Structure */ + /* --------------------------------------- */ + if (found == 1) + { + /* For all entries in SWXRegion array ... */ + /* -------------------------------------- */ + for (k = 0; k < NSWATHREGN; k++) + { + /* If empty region ... */ + /* ------------------- */ + if (SWXRegion[k] == 0) + { + /* Allocate space for region entry */ + /* ------------------------------- */ + SWXRegion[k] = (struct swathRegion *) + calloc(1, sizeof(struct swathRegion)); + if(SWXRegion[k] == NULL) + { + HEpush(DFE_NOSPACE,"SWregionindex", __FILE__, __LINE__); + return(-1); + } + + /* Store file and swath ID */ + /* ----------------------- */ + SWXRegion[k]->fid = fid; + SWXRegion[k]->swathID = swathID; + + + /* Set Start & Stop Vertical arrays to -1 */ + /* -------------------------------------- */ + for (j = 0; j < 8; j++) + { + SWXRegion[k]->StartVertical[j] = -1; + SWXRegion[k]->StopVertical[j] = -1; + SWXRegion[k]->StartScan[j] = -1; + SWXRegion[k]->StopScan[j] = -1; + } + + + /* Set region ID */ + /* ------------- */ + regionID = k; + break; + } + } + if (k >= NSWATHREGN) + { + HEpush(DFE_GENAPP, "SWregionindex", __FILE__, __LINE__); + HEreport( + "regionID exceeded NSWATHREGN.\n"); + return (-1); + } + + /* Find start and stop of regions */ + /* ------------------------------ */ + + /* Subtract previous flag value from current one */ + /* --------------------------------------------- */ + + /* + * Transisition points will have flag value (+1) start or + * (255 = (uint8) -1) stop of region + */ + for (i = edge[0]; i > 0; i--) + { + flag[i] -= flag[i - 1]; + } + + + for (i = 0; i <= edge[0]; i++) + { + /* Start of region */ + /* --------------- */ + if (flag[i] == 1) + { + /* Delyth Jones Moved the increment of the region down + to next if statement j = ++SWXRegion[k]->nRegions; */ + + /* using temp value, if not equal to stop region + invalid region otherwise ok Delyth Jones */ + tmpVal = i+1; + } + + /* End of region */ + /* ------------- */ + if (flag[i] == 255) + { + if( tmpVal!=i ) + { + /* Increment (multiple) region counter */ + /* ----------------------------------- */ + j = ++SWXRegion[k]->nRegions; + + if (mapstatus == 2) + { + l = i; + if ((tmpVal-1) % 2 == 1) + { + tmpVal = tmpVal + 1; + } + + if ((l-1) % 2 == 0) + { + l = l - 1; + } + } + SWXRegion[k]->StartRegion[j - 1] = tmpVal-1; + idxrange[0] = tmpVal - 1; + SWXRegion[k]->StopRegion[j - 1] = l - 1; + idxrange[1] = l - 1; + validReg = 0; + } + } + + } + + } + free(lonArr); + free(latArr); + free(flag); + } + } + if(validReg==0) + { + return (regionID); + } + else + { + return (-1); + } + +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdeftimeperiod | +| | +| DESCRIPTION: Finds swath cross tracks observed during time period and | +| returns period ID | +| | +| region ID | +| DESCRIPTION: | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| periodID int32 (Period ID) or (-1) if failed | +| | +| INPUTS: | +| swathID int32 Swath structure ID | +| starttime float64 TAI sec Start of time period | +| stoptime float64 TAI sec Stop of time period | +| mode int32 Search mode | +| HDFE_MIDPOINT - Use midpoint of Xtrack | +| HDFE_ENDPOINT - Use endpoints of Xtrack | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWdeftimeperiod(int32 swathID, float64 starttime, float64 stoptime, + int32 mode) +{ + + intn i; /* Loop index */ + intn j; /* Loop index */ + intn k = 0; /* Loop index */ + intn status; /* routine return status variable */ + intn statTime; /* Status from SWfieldinfo for time */ + + uint8 found = 0; /* Found flag */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath Vgroup ID */ + int32 rank; /* Rank of geolocation fields */ + int32 nt; /* Number type of geolocation fields */ + int32 dims[8]; /* Dimensions of geolocation fields */ + int32 start[2]; /* Start array (read) */ + int32 stride[2] = {1, 1}; /* Stride array (read) */ + int32 edge[2]; /* Edge array (read) */ + int32 periodID = -1; /* Period ID (return) */ + int32 dum; /* Dummy (loop) variable */ + + float64 time64Test; /* Time test value */ + float64 *time64 = NULL; /* Time data array */ + + char dimlist[256]; /* Dimension list (geolocation fields) */ + + /* Check for valid swath ID */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWdeftimeperiod", &fid, &sdInterfaceID, + &swVgrpID); + + if (status == 0) + { + /* Get "Time" field info */ + /* --------------------- */ + statTime = SWfieldinfo(swathID, "Time", &rank, dims, &nt, dimlist); + if (statTime != 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWdeftimeperiod", __FILE__, __LINE__); + HEreport("\"Time\" field not found.\n"); + } + + if (status == 0) + { + /* Search along entire "Track" dimension from beginning to end */ + /* ----------------------------------------------------------- */ + start[0] = 0; + edge[0] = dims[0]; + + + /* If 1D geolocation fields then set mode to MIDPOINT */ + /* -------------------------------------------------- */ + if (rank == 1) + { + mode = HDFE_MIDPOINT; + } + + + switch (mode) + { + + /* If MIDPOINT search single point in middle of "CrossTrack" */ + /* --------------------------------------------------------- */ + case HDFE_MIDPOINT: + + start[1] = dims[1] / 2; + edge[1] = 1; + + + /* Allocate space for time data */ + /* ---------------------------- */ + time64 = (float64 *) calloc(edge[0], 8); + if(time64 == NULL) + { + HEpush(DFE_NOSPACE,"SWdeftimeperiod", __FILE__, __LINE__); + return(-1); + } + + + /* Read "Time" field */ + /* ----------------- */ + status = SWreadfield(swathID, "Time", + start, NULL, edge, time64); + break; + + + /* If ENDPOINT search 2 points at either end of "CrossTrack" */ + /* --------------------------------------------------------- */ + case HDFE_ENDPOINT: + start[1] = 0; + stride[1] = dims[1] - 1; + edge[1] = 2; + + + /* Allocate space for time data */ + /* ---------------------------- */ + time64 = (float64 *) calloc(edge[0] * 2, 8); + if(time64 == NULL) + { + HEpush(DFE_NOSPACE,"SWdeftimeperiod", __FILE__, __LINE__); + return(-1); + } + + /* Read "Time" field */ + /* ----------------- */ + status = SWreadfield(swathID, "Time", + start, stride, edge, time64); + break; + + } + + if (time64) + { + /* For each track (from top) ... */ + /* ----------------------------- */ + for (i = 0; i < edge[0]; i++) + { + /* For each value from Cross Track ... */ + /* ----------------------------------- */ + for (j = 0; j < edge[1]; j++) + { + + /* Get time test value */ + /* ------------------- */ + time64Test = time64[i * edge[1] + j]; + + + /* If within time period ... */ + /* ------------------------- */ + if (time64Test >= starttime && + time64Test <= stoptime) + { + /* Set found flag */ + /* -------------- */ + found = 1; + + + /* For all entries in SWXRegion array ... */ + /* -------------------------------------- */ + for (k = 0; k < NSWATHREGN; k++) + { + /* If empty region ... */ + /* ------------------- */ + if (SWXRegion[k] == 0) + { + /* Allocate space for region entry */ + /* ------------------------------- */ + SWXRegion[k] = (struct swathRegion *) + calloc(1, sizeof(struct swathRegion)); + if(SWXRegion[k] == NULL) + { + HEpush(DFE_NOSPACE,"SWdeftimeperiod", __FILE__, __LINE__); + return(-1); + } + + /* Store file and swath ID */ + /* ----------------------- */ + SWXRegion[k]->fid = fid; + SWXRegion[k]->swathID = swathID; + + + /* Set number of isolated regions to 1 */ + /* ----------------------------------- */ + SWXRegion[k]->nRegions = 1; + + + /* Set start of region to first track found */ + /* ---------------------------------------- */ + SWXRegion[k]->StartRegion[0] = i; + + + /* Set Start & Stop Vertical arrays to -1 */ + /* -------------------------------------- */ + for (dum = 0; dum < 8; dum++) + { + SWXRegion[k]->StartVertical[dum] = -1; + SWXRegion[k]->StopVertical[dum] = -1; + SWXRegion[k]->StartScan[dum] = -1; + SWXRegion[k]->StopScan[dum] = -1; + } + + + /* Set period ID */ + /* ------------- */ + periodID = k; + + break; /* Break from "k" loop */ + } + } + } + if (found == 1) + { + break; /* Break from "j" loop */ + } + } + if (found == 1) + { + break; /* Break from "i" loop */ + } + } + + + + /* Clear found flag */ + /* ---------------- */ + found = 0; + + + /* For each track (from bottom) ... */ + /* -------------------------------- */ + for (i = edge[0] - 1; i >= 0; i--) + { + /* For each value from Cross Track ... */ + /* ----------------------------------- */ + for (j = 0; j < edge[1]; j++) + { + + /* Get time test value */ + /* ------------------- */ + time64Test = time64[i * edge[1] + j]; + + + /* If within time period ... */ + /* ------------------------- */ + if (time64Test >= starttime && + time64Test <= stoptime) + { + /* Set found flag */ + /* -------------- */ + found = 1; + + /* Set start of region to first track found */ + /* ---------------------------------------- */ + SWXRegion[k]->StopRegion[0] = i; + + break; /* Break from "j" loop */ + } + } + if (found == 1) + { + break; /* Break from "i" loop */ + } + } + + free(time64); + } + } + } + + return (periodID); +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWextractregion | +| | +| DESCRIPTION: Retrieves data from specified region. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 Swath structure ID | +| regionID int32 Region ID | +| fieldname char Fieldname | +| externalflag int32 External geolocation fields flag | +| HDFE_INTERNAL (0) | +| HDFE_EXTERNAL (1) | +| | +| OUTPUTS: | +| buffer void Data buffer containing subsetted region | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Add vertical subsetting | +| Oct 96 Joel Gales Mapping offset value not read from SWmapinfo | +| Dec 96 Joel Gales Vert Subset overwriting data buffer | +| Dec 96 Joel Gales Add multiple vertical subsetting capability | +| Mar 97 Joel Gales Add support for index mapping | +| Jul 99 DaW Add support for floating scene subsetting | +| Feb 03 Terry Haran/ | +| Abe Taaheri Forced map offset to 0 so that data is extracted | +| without offset consideration. This will preserve | +| original mapping between geofields and the data | +| field. | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWextractregion(int32 swathID, int32 regionID, char *fieldname, + int32 externalflag, VOIDP buffer) + +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn k; /* Loop index */ + intn l; /* Loop index */ + intn status; /* routine return status variable */ + intn long_status = 3; /* routine return status variable */ + /* for longitude */ + intn land_status = 3; /* Used for L7 float scene sub. */ + intn statMap = -1; /* Status from SWmapinfo */ + + uint8 found = 0; /* Found flag */ + uint8 vfound = 0; /* Found flag for vertical subsetting*/ + /* --- xhua */ + uint8 scene_cnt = 0; /* Used for L7 float scene sub. */ + uint8 detect_cnt = 0; /* Used to convert scan to scanline */ + /* L7 float scene sub. */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath Vgroup ID */ + + int32 numtype = 0; /* Used for L7 float scene sub. */ + int32 count = 0; /* Used for L7 float scene sub. */ + int32 index; /* Geo Dim Index */ + int32 nDim; /* Number of dimensions */ + int32 slen[64]; /* String length array */ + int32 dum; /* Dummy variable */ + int32 offset; /* Mapping offset */ + int32 incr; /* Mapping increment */ + int32 nXtrk; /* Number of cross tracks */ + int32 scan_shift = 0; /* Used to take out partial scans */ + int32 dumdims[8]; /* Dimensions from SWfieldinfo */ + int32 start[8]; /* Start array for data read */ + int32 edge[8]; /* Edge array for data read */ + int32 dims[8]; /* Dimensions */ + int32 rank = 0; /* Field rank */ + int32 rk = 0; /* Field rank */ + int32 ntype = 0; /* Field number type */ + int32 bufOffset; /* Output buffer offset */ + int32 size; /* Size of data buffer */ + int32 idxMapElem = -1; /* Number of index map elements */ + int32 *idxmap = NULL; /* Pointer to index mapping array */ + + int32 startscanline = 0; + int32 stopscanline = 0; + char *dfieldlist = (char *)NULL; + int32 strbufsize = 0; + int32 dfrank[8]; + int32 numtype2[8]; + uint16 *buffer2 = (uint16 *)NULL; + uint16 *tbuffer = (uint16 *)NULL; + int32 dims2[8]; + int32 nt = 0; + int32 startscandim = -1; + int32 stopscandim = -1; + int32 rank2 = 0; + + char dimlist[256]; /* Dimension list */ + char geodim[256];/* Geolocation field dimension list */ + char tgeodim[256];/* Time field dimension list */ + char dgeodim[256];/* Data field dimension list for subsetting */ + char utlbuf[256];/* Utility buffer */ + char *ptr[64]; /* String pointer array */ + + + + /* Check for valid swath ID */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWextractregion", &fid, &sdInterfaceID, + &swVgrpID); + + + /* Check for valid region ID */ + /* ------------------------- */ + if (status == 0) + { + if (regionID < 0 || regionID >= NSWATHREGN) + { + status = -1; + HEpush(DFE_RANGE, "SWextractregion", __FILE__, __LINE__); + HEreport("Invalid Region id: %d.\n", regionID); + } + } + + + + /* Check for active region ID */ + /* -------------------------- */ + if (status == 0) + { + if (SWXRegion[regionID] == 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWextractregion", __FILE__, __LINE__); + HEreport("Inactive Region ID: %d.\n", regionID); + } + } + + + /* This code checks for the attribute detector_count */ + /* which is found in Landsat 7 files. It is used */ + /* for some of the loops. */ + /* ================================================= */ + if (SWXRegion[regionID]->scanflag == 1) + { + land_status = SWattrinfo(swathID, "detector_count", &numtype, &count); + if (land_status == 0) + land_status = SWreadattr(swathID, "detector_count", &detect_cnt); + } + + /* Check that geo file and data file are same for INTERNAL subsetting */ + /* ------------------------------------------------------------------ */ + if (status == 0) + { + if (SWXRegion[regionID]->fid != fid && externalflag != HDFE_EXTERNAL) + { + status = -1; + HEpush(DFE_GENAPP, "SWextractregion", __FILE__, __LINE__); + HEreport("Region is not defined for this file.\n"); + } + } + + + + /* Check that geo swath and data swath are same for INTERNAL subsetting */ + /* -------------------------------------------------------------------- */ + if (status == 0) + { + if (SWXRegion[regionID]->swathID != swathID && + externalflag != HDFE_EXTERNAL) + { + status = -1; + HEpush(DFE_GENAPP, "SWextractregion", __FILE__, __LINE__); + HEreport("Region is not defined for this Swath.\n"); + } + } + + + + /* Check for valid fieldname */ + /* ------------------------- */ + if (status == 0) + { + + /* Get data field info */ + /* ------------------- */ + status = SWfieldinfo(swathID, fieldname, &rank, + dims, &ntype, dimlist); + + if (status != 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWextractregion", __FILE__, __LINE__); + HEreport("Field \"%s\" Not Found.\n", fieldname); + } + } + + + /* No problems so proceed ... */ + /* -------------------------- */ + if (status == 0) + { + + + /* Initialize start and edge for all dimensions */ + /* -------------------------------------------- */ + for (j = 0; j < rank; j++) + { + start[j] = 0; + edge[j] = dims[j]; + } + + + /* Vertical Subset */ + /* --------------- */ + for (j = 0; j < 8; j++) + { + /* If active vertical subset ... */ + /* ----------------------------- */ + if (SWXRegion[regionID]->StartVertical[j] != -1) + { + + /* Find vertical dimension within dimlist */ + /* -------------------------------------- */ + dum = EHstrwithin(SWXRegion[regionID]->DimNamePtr[j], + dimlist, ','); + + /* If dimension found ... */ + /* ---------------------- */ + if (dum != -1) + { + /* Compute start and edge for vertical dimension */ + /* --------------------------------------------- */ + vfound = 1; /* xhua */ + start[dum] = SWXRegion[regionID]->StartVertical[j]; + edge[dum] = SWXRegion[regionID]->StopVertical[j] - + SWXRegion[regionID]->StartVertical[j] + 1; + } + else + { + /* Vertical dimension not found */ + /* ---------------------------- */ + status = -1; + HEpush(DFE_GENAPP, "SWextractregion", __FILE__, __LINE__); + HEreport("Vertical Dimension Not Found: \"%s\".\n", + SWXRegion[regionID]->DimNamePtr); + } + } + } /* End of Vertical Subset loop */ + + + + /* No problems so proceed ... */ + /* -------------------------- */ + if (status == 0) + { + /* If non-vertical subset regions defined ... */ + /* ------------------------------------------ */ + if (SWXRegion[regionID]->nRegions > 0) + { + + /* Get geolocation dimension name */ + /* ------------------------------ */ + status = SWfieldinfo(SWXRegion[regionID]->swathID, + "Longitude", &dum, + dumdims, &dum, geodim); + long_status = status; + + /* If Time field being used, check for dimensions */ + /* ---------------------------------------------- */ + if (timeflag == 1) + { + /* code change to fix time subset bug for Landsat7 */ + + status = SWfieldinfo(SWXRegion[regionID]->swathID, + "Time", &dum, + dumdims, &dum, tgeodim); + + if (strcmp(geodim, tgeodim) != 0) + { + strcpy(geodim, tgeodim); + } + } + timeflag = 0; + + /* If defscanregion being used, get dimensions */ + /* of field being used */ + /* ---------------------------------------------- */ + if (SWXRegion[regionID]->scanflag == 1) + { + (void) SWnentries(SWXRegion[regionID]->swathID,4,&strbufsize); + dfieldlist = (char *)calloc(strbufsize + 1, sizeof(char)); + (void) SWinqdatafields(SWXRegion[regionID]->swathID,dfieldlist,dfrank,numtype2); + status = SWfieldinfo(SWXRegion[regionID]->swathID,dfieldlist,&dum,dumdims,&dum,dgeodim); + + /* The dimensions have to be switched, because */ + /* the mappings force a geodim and datadim */ + /* so to find the mapping, the dimensions must */ + /* be switched, but the subsetting will still */ + /* be based on the correct dimensions */ + /* ------------------------------------------- */ + if (strcmp(dgeodim,dimlist) != 0 || long_status == -1) + { + strcpy(geodim,dimlist); + strcpy(dimlist,dgeodim); + } + } + + + /* Get "Track" (first) Dimension from geo dimlist */ + /* ---------------------------------------------- */ + nDim = EHparsestr(geodim, ',', ptr, slen); + geodim[slen[0]] = 0; + + + /* Parse Data Field Dimlist & find mapping */ + /* --------------------------------------- */ + nDim = EHparsestr(dimlist, ',', ptr, slen); + + + /* Loop through all dimensions and search for mapping */ + /* -------------------------------------------------- */ + for (i = 0; i < nDim; i++) + { + memcpy(utlbuf, ptr[i], slen[i]); + utlbuf[slen[i]] = 0; + statMap = SWmapinfo(swathID, geodim, utlbuf, + &offset, &incr); + + + /* + * Force offset to 0. + * We're not changing the mapping, so we want + * the original offset to apply to the subsetted data. + * Otherwise, bad things happen, such as subsetting + * past the end of the original data, and being unable + * to read the first elements of the + * original data. + * The offset is only important for aligning the + * data with interpolated (incr > 0) or decimated + * (incr < 0) geolocation information for the data. + */ + + offset = 0; + + + /* Mapping found */ + /* ------------- */ + if (statMap == 0) + { + found = 1; + index = i; + break; + } + } + + + /* If mapping not found check for geodim within dimlist */ + /* ---------------------------------------------------- */ + if (found == 0) + { + index = EHstrwithin(geodim, dimlist, ','); + + /* Geo dimension found within subset field dimlist */ + /* ----------------------------------------------- */ + if (index != -1) + { + found = 1; + offset = 0; + incr = 1; + } + } + + + + /* If mapping not found check for indexed mapping */ + /* ---------------------------------------------- */ + if (found == 0) + { + /* Get size of geo dim & allocate space of index mapping */ + /* ----------------------------------------------------- */ + dum = SWdiminfo(swathID, geodim); + + /* For Landsat files, the index mapping has two values */ + /* for each point, a left and right point. So for a 37 */ + /* scene band file there are 2x2 points for each scene */ + /* meaning, 2x2x37 = 148 values. The above function */ + /* only returns the number of values in the track */ + /* dimension. */ + /* ----------------------------------------------------- */ + if(land_status == 0) + if(strcmp(fieldname, "Latitude") == 0 || + strcmp(fieldname, "Longitude") == 0) + { + dum = dum * 2; + } + idxmap = (int32 *) calloc(dum, sizeof(int32)); + if(idxmap == NULL) + { + HEpush(DFE_NOSPACE,"SWextractregion", __FILE__, __LINE__); + return(-1); + } + + /* Loop through all dimensions and search for mapping */ + /* -------------------------------------------------- */ + for (i = 0; i < nDim; i++) + { + memcpy(utlbuf, ptr[i], slen[i]); + utlbuf[slen[i]] = 0; + + idxMapElem = + SWidxmapinfo(swathID, geodim, utlbuf, idxmap); + + + /* Mapping found */ + /* ------------- */ + if (idxMapElem != -1) + { + found = 1; + index = i; + break; + } + } + } + + + /* If regular mapping found ... */ + /* ---------------------------- */ + if (found == 1 && idxMapElem == -1) + { + for (k = 0; k < SWXRegion[regionID]->nRegions; k++) + { + if (k > 0) + { + /* Compute size in bytes of previous region */ + /* ---------------------------------------- */ + size = edge[0]; + for (j = 1; j < rank; j++) + { + size *= edge[j]; + } + size *= DFKNTsize(ntype); + + + /* Compute output buffer offset */ + /* ---------------------------- */ + bufOffset += size; + } + else + { + /* Initialize output buffer offset */ + /* ------------------------------- */ + bufOffset = 0; + } + + + /* Compute number of cross tracks in region */ + /* ---------------------------------------- */ + nXtrk = SWXRegion[regionID]->StopRegion[k] - + SWXRegion[regionID]->StartRegion[k] + 1; + + + /* Positive increment (geodim <= datadim) */ + /* -------------------------------------- */ + if (incr > 0) + { + if (SWXRegion[regionID]->scanflag == 1) + { + start[index] = SWXRegion[regionID]->StartRegion[k]/incr; + if(SWXRegion[regionID]->band8flag == 2 || + SWXRegion[regionID]->band8flag == 3) + { + start[index] = (SWXRegion[regionID]->StartRegion[k]+detect_cnt)/incr; + status = SWfieldinfo(SWXRegion[regionID]->swathID,"scan_no",&rank,dims2,&nt,dimlist); + buffer2 = (uint16 *)calloc(dims2[0], sizeof(uint16)); + status = SWreadfield(SWXRegion[regionID]->swathID,"scan_no",NULL,NULL,NULL,buffer2); + if(incr == 1) + start[index] = start[index] - (buffer2[0] * detect_cnt); + else + start[index] = start[index] - buffer2[0]; + free(buffer2); + } + scan_shift = nXtrk % incr; + if(scan_shift != 0) + nXtrk = nXtrk - scan_shift; + edge[index] = nXtrk / incr; + if (nXtrk % incr != 0) + edge[index]++; + if(long_status == -1 || incr == 1) + { + scan_shift = nXtrk % detect_cnt; + if(scan_shift != 0) + edge[index] = nXtrk - scan_shift; + } + + } + else + { + start[index] = SWXRegion[regionID]->StartRegion[k] * incr + offset; + edge[index] = nXtrk * incr - offset; + } + } + else + { + /* Negative increment (geodim > datadim) */ + /* ------------------------------------- */ + start[index] = SWXRegion[regionID]->StartRegion[k] + / (-incr) + offset; + edge[index] = nXtrk / (-incr); + + /* + * If Xtrk not exactly divisible by incr, round + * edge to next highest integer + */ + + if (nXtrk % (-incr) != 0) + { + edge[index]++; + } + } + + + /* Read Data into output buffer */ + /* ---------------------------- */ + status = SWreadfield(swathID, fieldname, + start, NULL, edge, + (uint8 *) buffer + bufOffset); + } + } + else if (found == 1 && idxMapElem != -1) + { + /* Indexed Mapping */ + /* --------------- */ + for (k = 0; k < SWXRegion[regionID]->nRegions; k++) + { + if (k > 0) + { + /* Compute size in bytes of previous region */ + /* ---------------------------------------- */ + size = edge[0]; + for (j = 1; j < rank; j++) + { + size *= edge[j]; + } + size *= DFKNTsize(ntype); + + + /* Compute output buffer offset */ + /* ---------------------------- */ + bufOffset += size; + } + else + { + /* Initialize output buffer offset */ + /* ------------------------------- */ + bufOffset = 0; + } + + + /* Compute start & edge from index mappings */ + /* ---------------------------------------- */ + if (SWXRegion[regionID]->scanflag == 1 && + (strcmp(fieldname, "Latitude") == 0 || + strcmp(fieldname, "Longitude") == 0)) + { + if (land_status == 0) + status = SWreadattr(swathID, "scene_count", &scene_cnt); + startscanline = SWXRegion[regionID]->StartRegion[k]; + stopscanline = SWXRegion[regionID]->StopRegion[k]; + if(SWXRegion[regionID]->band8flag == 2 || SWXRegion[regionID]->band8flag == 3) + { + status = SWfieldinfo(swathID,"scan_no",&rk,dims2,&nt,dimlist); + tbuffer = (uint16 *)calloc(dims2[0], sizeof(uint16)); + status =SWreadfield(swathID,"scan_no",NULL,NULL,NULL,tbuffer); + startscanline = startscanline - ((tbuffer[0] * detect_cnt) - detect_cnt); + stopscanline = stopscanline - ((tbuffer[0] * detect_cnt) - 1); + } + if(SWXRegion[regionID]->band8flag == 2 || + SWXRegion[regionID]->band8flag == 3) + { + if(startscandim == -1) + if(startscanline < idxmap[0]) + { + startscandim = 0; + start[index] = 0; + if(stopscanline > idxmap[scene_cnt * 2 - 1]) + { + stopscandim = scene_cnt*2 - startscandim; + edge[index] = scene_cnt*2 - startscandim; + } + } + } + j = 0; + for (l = 0; l < scene_cnt; l++) + { + if(idxmap[j] <= startscanline && idxmap[j+1] >= startscanline) + if(startscandim == -1) + { + start[index] = j; + startscandim = j; + } + if(idxmap[j] <= stopscanline && idxmap[j+1] >= stopscanline) + if(startscandim != -1) + { + edge[index] = j - start[index] + 2; + stopscandim = j - start[index] + 1; + } + j = j + 2; + } + if(SWXRegion[regionID]->band8flag == 1 || + SWXRegion[regionID]->band8flag == 2) + { + if(startscandim == -1) + if(startscanline < idxmap[0]) + { + startscandim = 0; + start[index] = 0; + } + if(stopscandim == -1) + if(stopscanline > idxmap[scene_cnt * 2 - 1]) + { + stopscandim = scene_cnt*2 - start[index]; + edge[index] = scene_cnt*2 - start[index]; + } + } + if(SWXRegion[regionID]->band8flag == 2) + { + if(startscandim == -1) + if(startscanline > idxmap[j - 1]) + { + status = SWfieldinfo(SWXRegion[regionID]->swathID,"scan_no",&rank2,dims2,&nt,dimlist); + buffer2 = (uint16 *)calloc(dims2[0], sizeof(uint16)); + status = SWreadfield(SWXRegion[regionID]->swathID,"scan_no",NULL,NULL,NULL,buffer2); + startscanline = startscanline - (buffer2[0] * detect_cnt); + stopscanline = stopscanline - (buffer2[0] * detect_cnt); + free(buffer2); + j = 0; + for (l = 0; l < scene_cnt; l++) + { + if(idxmap[j] <= startscanline && idxmap[j+1] >= startscanline) + { + start[index] = j; + } + if(idxmap[j] <= stopscanline && idxmap[j+1] >= stopscanline) + edge[index] = j - start[index] + 2; + j = j + 2; + if(idxmap[j] == 0 || idxmap[j+1] == 0) + l = scene_cnt; + } + + } + } + + } + else if(SWXRegion[regionID]->scanflag == 1 && + (strcmp(fieldname, "scene_center_latitude") == 0 || + strcmp(fieldname, "scene_center_longitude") == 0)) + { + if (land_status == 0) + status = SWreadattr(swathID, "scene_count", &scene_cnt); + startscanline = SWXRegion[regionID]->StartRegion[k]; + stopscanline = SWXRegion[regionID]->StopRegion[k]; + if(startscanline < idxmap[0]) + { + startscandim = 0; + start[index] = 0; + } + for (l = 0; l < scene_cnt-1; l++) + { + if(idxmap[l] <= startscanline && idxmap[l+1] >= startscanline) + if(startscandim == -1) + { + start[index] = l; + startscandim = l; + } + if(idxmap[l] <= stopscanline && idxmap[l+1] >= stopscanline) + if(stopscandim == -1) + { + edge[index] = l - start[index] + 2; + stopscandim = l + 1; + } + } + if(stopscandim == -1) + { + if(stopscanline > idxmap[scene_cnt - 1]) + { + edge[index] = scene_cnt - start[index]; + stopscandim = scene_cnt - 1; + } + } + + if(SWXRegion[regionID]->band8flag == 1) + { + if(stopscandim == -1) + if(stopscanline > idxmap[scene_cnt - 1]) + { + edge[index] = scene_cnt - start[index]; + stopscandim = scene_cnt -1; + } + } + if(SWXRegion[regionID]->band8flag == 2 || + SWXRegion[regionID]->band8flag == 3) + { + if(startscandim == -1) + { + if(startscanline < idxmap[0]) + { + startscandim = 0; + start[index] = 0; + edge[index] = stopscandim - startscandim + 1; + } + } + if(startscandim == -1) + { + startscanline = SWXRegion[regionID]->StartScan[k] * detect_cnt - detect_cnt; + stopscanline = SWXRegion[regionID]->StopScan[k] * detect_cnt - 1; + for (l = 0; l < scene_cnt-1; l++) + { + if(idxmap[l] <= startscanline && idxmap[l+1] >= startscanline) + start[index] = l; + if(idxmap[l] <= stopscanline && idxmap[l+1] >= stopscanline) + edge[index] = l - start[index] + 1; + } + } + } + } + else + { + if (SWXRegion[regionID]->scanflag == 1 && + strcmp(fieldname,dfieldlist) == 0) + { + start[index] = SWXRegion[regionID]->StartRegion[k]; + edge[index] = SWXRegion[regionID]->StopRegion[k] - + SWXRegion[regionID]->StartRegion[k] + 1; + if(SWXRegion[regionID]->band8flag == 2 || + SWXRegion[regionID]->band8flag == 3 ) + { + status = SWfieldinfo(SWXRegion[regionID]->swathID,"scan_no",&rank,dims2,&nt,dimlist); + buffer2 = (uint16 *)calloc(dims2[0], sizeof(uint16)); + status = SWreadfield(SWXRegion[regionID]->swathID,"scan_no",NULL,NULL,NULL,buffer2); + start[index] = start[index] - (buffer2[0] * detect_cnt - detect_cnt); + free(buffer2); + } + } + else + { + start[index] = idxmap[SWXRegion[regionID]->StartRegion[k]]; + + edge[index] = idxmap[SWXRegion[regionID]->StopRegion[k]] - + idxmap[SWXRegion[regionID]->StartRegion[k]] + 1; + } + } + /* Read Data into output buffer */ + /* ---------------------------- */ + status = SWreadfield(swathID, fieldname, + start, NULL, edge, + buffer); + if (SWXRegion[regionID]->scanflag == 1) + { + + if (strcmp(fieldname,"Longitude") == 0) + { + status = SWscan2longlat(swathID, fieldname, buffer, start, + edge, idxmap, startscanline, stopscanline); + } + if (strcmp(fieldname,"Latitude") == 0) + { + status = SWscan2longlat(swathID, fieldname, buffer, start, + edge, idxmap, startscanline, stopscanline); + } + } + } + } + else if(vfound == 1) /* Vertical subsetting */ + { /* found previously, */ + status = SWreadfield(swathID, fieldname, /* perform the vertical*/ + start, NULL, edge, /* subsetting. */ + (uint8 *) buffer); /* -- xhua */ + } + else + { + /* Mapping not found */ + /* ----------------- */ + status = -1; + HEpush(DFE_GENAPP, "SWextractregion", __FILE__, __LINE__); + HEreport("Mapping Not Defined for \"%s\" Dimension.\n", + geodim); + } + } + else + { + /* Read Data (Vert SS only) */ + /* ------------------------ */ + status = SWreadfield(swathID, fieldname, + start, NULL, edge, + (uint8 *) buffer); + } + } + } + + /* Free index mappings if applicable */ + /* --------------------------------- */ + if (idxmap != NULL) + { + free(idxmap); + } + if(dfieldlist != NULL) + free(dfieldlist); + + return (status); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWscan2longlat | +| | +| DESCRIPTION: Convert scanline to Long/Lat for floating scene subsetting. | +| This will calculate/interpolate the long/lat for a given | +| scanline. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 Swath structure ID | +| fieldname char Fieldname | +| buffer void Values to update | +| start int32 | +| edge int32 | +| idxmap int32 * Buffer of index mapping values | +| startscanline int32 Start of scan region | +| stopscanline int32 Stop of scan region | +| | +| OUTPUTS: | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jul 99 DaW Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +static intn +SWscan2longlat(int32 swathID, char *fieldname, VOIDP buffer, int32 start[], +int32 edge[], int32 *idxmap, int32 startscanline, int32 stopscanline) +{ + + enum corner {UL, UR, LL, LR}; + enum corner pos = UL; + enum corner pos2 = UL; + + uint8 scene_cnt = 0; /* Used to convert scan to scanline */ + /* L7 float scene sub. */ + float32 *buffer2; + float32 *bufferc; + float32 deg2rad = PI/180.00; + + float32 p1_long = 0.0; /* point 1, longitude */ + float32 p2_long = 0.0; /* point 2, longitude */ + float32 pi_long = 0.0; /* interpolated point, longitude */ + int32 scanline_p1 = 0; + + float32 p1_lat = 0.0; /* point 1, latitude */ + float32 p2_lat = 0.0; /* point 2, latitude */ + float32 pi_lat = 0.0; /* interpolated point, latitude */ + int32 scanline_p2 = 0; + + + float32 x_p1 = 0.0; /* Cartesian coordinates */ + float32 y_p1 = 0.0; /* point 1 */ + float32 z_p1 = 0.0; + + float32 x_p2 = 0.0; /* Cartesian coordinates */ + float32 y_p2 = 0.0; /* point 2 */ + float32 z_p2 = 0.0; + + float32 x_pi = 0.0; /* Cartesian coordinates */ + float32 y_pi = 0.0; /* interpolated point */ + float32 z_pi = 0.0; + int32 scanline_pi = 0; + + intn status = -1; + + int i = 0; + int p1_long_l90_flag = 0; + int p1_long_g90_flag = 0; + int p2_long_l90_flag = 0; + int p2_long_g90_flag = 0; + int fieldflag = 0; + + int numofval = 0; + + + + numofval = edge[0] * 2; + + + buffer2 = (float32 *)calloc(numofval, sizeof(float32)); + bufferc = (float32 *)calloc(numofval, sizeof(float32)); + memmove(bufferc, buffer, numofval*sizeof(float32)); + + (void) SWreadattr(swathID, "scene_count", &scene_cnt); + + if (strcmp(fieldname, "Longitude") == 0) + { + fieldflag = 1; + status = SWreadfield(swathID, "Latitude", start, NULL, edge, buffer2); + } + else if (strcmp(fieldname, "Latitude") == 0) + { + fieldflag = 2; + status = SWreadfield(swathID, "Longitude", start, NULL, edge, buffer2); + } + + for(i=0; i<4; i++) + { + switch(pos) + { + case UL: + if (fieldflag == 1) + { + p1_long = bufferc[0]; + p2_long = bufferc[2]; + p1_lat = buffer2[0]; + p2_lat = buffer2[2]; + } + if (fieldflag == 2) + { + p1_long = buffer2[0]; + p2_long = buffer2[2]; + p1_lat = bufferc[0]; + p2_lat = bufferc[2]; + } + scanline_p1 = idxmap[start[0]]; + scanline_p2 = idxmap[start[0]+1]; + scanline_pi = startscanline; + pos = UR; + break; + case UR: + if (fieldflag == 1) + { + p1_long = bufferc[1]; + p2_long = bufferc[3]; + p1_lat = buffer2[1]; + p2_lat = buffer2[3]; + } + if (fieldflag == 2) + { + p1_long = buffer2[1]; + p2_long = buffer2[3]; + p1_lat = bufferc[1]; + p2_lat = bufferc[3]; + } + scanline_p1 = idxmap[start[0]]; + scanline_p2 = idxmap[start[0]+1]; + scanline_pi = startscanline; + pos = LL; + break; + case LL: + if (fieldflag == 1) + { + p1_long = bufferc[numofval-4]; + p2_long = bufferc[numofval-2]; + p1_lat = buffer2[numofval-4]; + p2_lat = buffer2[numofval-2]; + } + if (fieldflag == 2) + { + p1_long = buffer2[numofval-4]; + p2_long = buffer2[numofval-2]; + p1_lat = bufferc[numofval-4]; + p2_lat = bufferc[numofval-2]; + } + scanline_p1 = idxmap[start[0] + edge[0] - 2]; + scanline_p2 = idxmap[start[0] + edge[0] - 1]; + scanline_pi = stopscanline; + pos = LR; + break; + case LR: + if (fieldflag == 1) + { + p1_long = bufferc[numofval-3]; + p2_long = bufferc[numofval-1]; + p1_lat = buffer2[numofval-3]; + p2_lat = buffer2[numofval-1]; + } + if (fieldflag == 2) + { + p1_long = buffer2[numofval-3]; + p2_long = buffer2[numofval-1]; + p1_lat = bufferc[numofval-3]; + p2_lat = bufferc[numofval-1]; + } + scanline_p1 = idxmap[start[0] + edge[0] - 2]; + scanline_p2 = idxmap[start[0] + edge[0] - 1]; + scanline_pi = stopscanline; + break; + } + + + + if (p1_long <= -90.0) + { + if (p2_long >= 90.0) + { + p1_long = p1_long + 180.0; + p2_long = p2_long - 180.0; + p1_long_l90_flag = 2; + } + else + { + p1_long = p1_long + 180.0; + p1_long_l90_flag = 1; + } + } + if (p1_long >= 90.0 && p1_long_l90_flag != 2) + { + if(p2_long <= -90.0) + { + p1_long = p1_long - 180.0; + p2_long = p2_long + 180.0; + p1_long_g90_flag = 2; + } + else + { + p1_long = p1_long - 90.0; + p1_long_g90_flag = 1; + } + } + if (p2_long <= -90.0) + { + if (p1_long < 0.0) + { + p2_long = p2_long + 90.0; + p1_long = p1_long + 90.0; + p2_long_l90_flag = 2; + } + else + { + p2_long = p2_long + 180.0; + p2_long_l90_flag = 1; + } + } + if (p2_long >= 90.0 && p1_long_l90_flag != 2) + { + p2_long = p2_long - 90.0; + p2_long_g90_flag = 1; + } + + + x_p1 = RADOE * cos((p1_long*deg2rad)) * sin((p1_lat*deg2rad)); + y_p1 = RADOE * sin((p1_long*deg2rad)) * sin((p1_lat*deg2rad)); + z_p1 = RADOE * cos((p1_lat*deg2rad)); + + + x_p2 = RADOE * cos((p2_long*deg2rad)) * sin((p2_lat*deg2rad)); + y_p2 = RADOE * sin((p2_long*deg2rad)) * sin((p2_lat*deg2rad)); + z_p2 = RADOE * cos((p2_lat*deg2rad)); + + x_pi = x_p1 + (x_p2 - x_p1)*(scanline_pi-scanline_p1)/(scanline_p2-scanline_p1); + y_pi = y_p1 + (y_p2 - y_p1)*(scanline_pi-scanline_p1)/(scanline_p2-scanline_p1); + z_pi = z_p1 + (z_p2 - z_p1)*(scanline_pi-scanline_p1)/(scanline_p2-scanline_p1); + + if (fieldflag == 1) + { + pi_long = atan(y_pi/x_pi)*180.0/PI; + if (p1_long_l90_flag == 1 || p2_long_l90_flag == 1) + { + pi_long = pi_long - 180.0; + p1_long_l90_flag = 0; + p2_long_l90_flag = 0; + } + if (p1_long_g90_flag == 1 || p2_long_g90_flag == 1) + { + pi_long = pi_long + 90.0; + p1_long_g90_flag = 0; + p2_long_g90_flag = 0; + } + if (p1_long_l90_flag == 2) + { + if (pi_long > 0.0) + pi_long = pi_long - 180.0; + else if (pi_long < 0.0) + pi_long = pi_long + 180.0; + p1_long_l90_flag = 0; + } + if (p1_long_g90_flag == 2) + { + if (pi_long > 0.0) + pi_long = pi_long - 180.0; + else if (pi_long < 0.0) + pi_long = pi_long + 180.0; + p1_long_g90_flag = 0; + } + if (p2_long_l90_flag == 2) + { + pi_long = pi_long - 90.0; + p2_long_l90_flag = 0; + } + + + + switch(pos2) + { + case UL: + bufferc[0] = pi_long; + pos2 = UR; + break; + case UR: + bufferc[1] = pi_long; + pos2 = LL; + break; + case LL: + if(stopscanline > idxmap[scene_cnt*2 - 1]) + break; + bufferc[numofval-2] = pi_long; + pos2 = LR; + break; + case LR: + if(stopscanline > idxmap[scene_cnt*2 - 1]) + break; + bufferc[numofval-1] = pi_long; + break; + } + + } + if (fieldflag == 2) + { + pi_lat = atan((sqrt(x_pi*x_pi + y_pi*y_pi)/z_pi))*180.0/PI; + switch(pos2) + { + case UL: + bufferc[0] = pi_lat; + pos2 = UR; + break; + case UR: + bufferc[1] = pi_lat; + pos2 = LL; + break; + case LL: + if(stopscanline > idxmap[scene_cnt*2 - 1]) + break; + bufferc[numofval-2] = pi_lat; + pos2 = LR; + break; + case LR: + if(stopscanline > idxmap[scene_cnt*2 - 1]) + break; + bufferc[numofval-1] = pi_lat; + break; + } + } + } + memmove(buffer, bufferc, numofval*sizeof(float32)); + free(buffer2); + free(bufferc); + return(status); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWextractperiod | +| | +| DESCRIPTION: Retrieves data from specified period. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 Swath structure ID | +| periodID int32 Period ID | +| fieldname char Fieldname | +| externalflag int32 External geolocation fields flag | +| HDFE_INTERNAL (0) | +| HDFE_EXTERNAL (1) | +| | +| OUTPUTS: | +| buffer void Data buffer containing subsetted region | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Jun 03 Abe Taaheri added clearing timeflag if SWextractregion failes | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWextractperiod(int32 swathID, int32 periodID, char *fieldname, + int32 externalflag, VOIDP buffer) + +{ + intn status; /* routine return status variable */ + + timeflag = 1; + + /* Call SWextractregion routine */ + /* ---------------------------- */ + status = SWextractregion(swathID, periodID, fieldname, externalflag, + (char *) buffer); + if (status != 0) timeflag = 0; /*clear timeflag if SWextractregion failed*/ + return (status); +} + + + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdupregion | +| | +| DESCRIPTION: Duplicates a region | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| newregionID int32 New region ID | +| | +| INPUTS: | +| oldregionID int32 Old region ID | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jan 97 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWdupregion(int32 oldregionID) +{ + intn i; /* Loop index */ + + int32 newregionID = -1; /* New region ID */ + + + /* Find first empty (inactive) region */ + /* ---------------------------------- */ + for (i = 0; i < NSWATHREGN; i++) + { + if (SWXRegion[i] == 0) + { + /* Allocate space for new swath region entry */ + /* ----------------------------------------- */ + SWXRegion[i] = (struct swathRegion *) + calloc(1, sizeof(struct swathRegion)); + if(SWXRegion[i] == NULL) + { + HEpush(DFE_NOSPACE,"SWdupregion", __FILE__, __LINE__); + return(-1); + } + + /* Copy old region structure data to new region */ + /* -------------------------------------------- */ + *SWXRegion[i] = *SWXRegion[oldregionID]; + + + /* Define new region ID */ + /* -------------------- */ + newregionID = i; + + break; + } + } + + return (newregionID); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWregioninfo | +| | +| DESCRIPTION: Returns size of region in bytes | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 Swath structure ID | +| regionID int32 Region ID | +| fieldname char Fieldname | +| | +| | +| OUTPUTS: | +| ntype int32 field number type | +| rank int32 field rank | +| dims int32 dimensions of field region | +| size int32 size in bytes of field region | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Add vertical subsetting | +| Dec 96 Joel Gales Add multiple vertical subsetting capability | +| Mar 97 Joel Gales Add support for index mapping | +| Jul 99 DaW Add support for floating scene subsetting | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWregioninfo(int32 swathID, int32 regionID, char *fieldname, + int32 * ntype, int32 * rank, int32 dims[], int32 * size) + +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn k; /* Loop index */ + intn l = 0; /* Loop index */ + intn status; /* routine return status variable */ + intn long_status = 3; /* routine return status variable for longitude */ + intn land_status = 3; /* Used for L7 float scene sub. */ + intn statMap = -1; /* Status from SWmapinfo */ + + uint8 found = 0; /* Found flag */ + uint8 detect_cnt = 0; /* Used for L7 float scene sub. */ + + int32 numtype = 0; /* Used for L7 float scene sub. */ + int32 count = 0; /* Used for L7 float scene sub. */ + + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath Vgroup ID */ + + int32 index; /* Geo Dim Index */ + int32 nDim; /* Number of dimensions */ + int32 slen[64]; /* String length array */ + int32 dum; /* Dummy variable */ + int32 incr; /* Mapping increment */ + int32 nXtrk = 0; /* Number of cross tracks */ + int32 scan_shift = 0; /* Used to take out partial scans */ + int32 startscandim = -1; /* Used for floating scene region size */ + int32 stopscandim = -1; /* Used for floating scene region size */ + int32 dumdims[8]; /* Dimensions from SWfieldinfo */ + int32 idxMapElem = -1; /* Number of index map elements */ + int32 *idxmap = NULL; /* Pointer to index mapping array */ + int32 datafld = 0; + + uint8 scene_cnt = 0; /* Number of scenes in swath */ + int32 startscanline = 0; + int32 stopscanline = 0; + char *dfieldlist = (char *)NULL; + int32 strbufsize = 0; + int32 dfrank[8]; + int32 numtype2[8]; + int32 rank2 = 0; + int32 rk = 0; + int32 dims2[8]; + int32 nt = 0; + uint16 *buffer2 = (uint16 *)NULL; + uint16 *tbuffer = (uint16 *)NULL; + + char dimlist[256]; /* Dimension list */ + char geodim[256];/* Geolocation field dimension list */ + char tgeodim[256];/* Time Geolocation field dimension list */ + char dgeodim[256];/* Data Subsetting field dimension list */ + char utlbuf[256];/* Utility buffer */ + char *ptr[64]; /* String pointer array */ + static const char errMesg[] = "Vertical Dimension Not Found: \"%s\".\n"; + + + + /* Set region size to -1 */ + /* --------------------- */ + *size = -1; + + + /* Check for valid swath ID */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWregioninfo", &fid, &sdInterfaceID, + &swVgrpID); + + + /* Check for valid region ID */ + /* ------------------------- */ + if (status == 0) + { + if (regionID < 0 || regionID >= NSWATHREGN) + { + status = -1; + HEpush(DFE_RANGE, "SWregioninfo", __FILE__, __LINE__); + HEreport("Invalid Region id: %d.\n", regionID); + } + } + + + + /* Check for active region ID */ + /* -------------------------- */ + if (status == 0) + { + if (SWXRegion[regionID] == 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWregioninfo", __FILE__, __LINE__); + HEreport("Inactive Region ID: %d.\n", regionID); + } + } + + /* This code checks for the attribute detector_count */ + /* which is found in Landsat 7 files. It is used */ + /* for some of the loops. */ + /* ================================================= */ + if (SWXRegion[regionID]->scanflag == 1) + { + land_status = SWattrinfo(swathID, "detector_count", &numtype, &count); + if (land_status == 0) + { + land_status = SWreadattr(swathID, "detector_count", &detect_cnt); + land_status = SWreadattr(swathID, "scene_count", &scene_cnt); + + } + } + + + + + /* Check for valid fieldname */ + /* ------------------------- */ + if (status == 0) + { + /* Get data field info */ + /* ------------------- */ + status = SWfieldinfo(swathID, fieldname, rank, + dims, ntype, dimlist); + + if (status != 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWregioninfo", __FILE__, __LINE__); + HEreport("Field \"%s\" Not Found.\n", fieldname); + } + } + + + + /* No problems so proceed ... */ + /* -------------------------- */ + if (status == 0) + { + /* If non-vertical subset regions defined ... */ + /* ------------------------------------------ */ + if (SWXRegion[regionID]->nRegions > 0 || SWXRegion[regionID]->scanflag == 1) + { + + /* Get geolocation dimension name */ + /* ------------------------------ */ + status = SWfieldinfo(SWXRegion[regionID]->swathID, + "Longitude", &dum, + dumdims, &dum, geodim); + long_status = status; + + /* If Time field being used, check for dimensions */ + /* ---------------------------------------------- */ + if (timeflag == 1) + { + /* code change to fix time subset bug for Landsat7 */ + + status = SWfieldinfo(SWXRegion[regionID]->swathID, + "Time", &dum, + dumdims, &dum, tgeodim); + + if (strcmp(geodim, tgeodim) != 0) + { + strcpy(geodim, tgeodim); + } + timeflag = 0; + } + + /* If defscanregion being used, get dimensions */ + /* of field being used */ + /* ---------------------------------------------- */ + if (SWXRegion[regionID]->scanflag == 1) + { + (void) SWnentries(SWXRegion[regionID]->swathID,4,&strbufsize); + dfieldlist = (char *)calloc(strbufsize + 1, sizeof(char)); + (void) SWinqdatafields(SWXRegion[regionID]->swathID,dfieldlist,dfrank,numtype2); + status = SWfieldinfo(SWXRegion[regionID]->swathID,dfieldlist,&dum,dumdims,&dum,dgeodim); + + /* The dimensions have to be switched, because */ + /* the mappings force a geodim and datadim */ + /* so to find the mapping, the dimensions must */ + /* be switched, but the subsetting will still */ + /* be based on the correct dimensions */ + /* "long_status == -1" added for CAL file which */ + /* doesn't have a Traditional geolocation field */ + /* ---------------------------------------------- */ + if (strcmp(dgeodim,dimlist) != 0 || long_status == -1) + { + strcpy(geodim,dimlist); + strcpy(dimlist,dgeodim); + } + } + + + /* Get "Track" (first) Dimension from geo dimlist */ + /* ---------------------------------------------- */ + nDim = EHparsestr(geodim, ',', ptr, slen); + geodim[slen[0]] = 0; + + + /* Parse Data Field Dimlist & find mapping */ + /* --------------------------------------- */ + nDim = EHparsestr(dimlist, ',', ptr, slen); + + + /* Loop through all dimensions and search for mapping */ + /* -------------------------------------------------- */ + for (i = 0; i < nDim; i++) + { + memcpy(utlbuf, ptr[i], slen[i]); + utlbuf[slen[i]] = 0; + statMap = SWmapinfo(swathID, geodim, utlbuf, + &dum, &incr); + + /* Mapping found */ + /* ------------- */ + if (statMap == 0) + { + found = 1; + index = i; + break; + } + } + + + /* If mapping not found check for geodim within dimlist */ + /* ---------------------------------------------------- */ + if (found == 0) + { + index = EHstrwithin(geodim, dimlist, ','); + + /* Geo dimension found within subset field dimlist */ + /* ----------------------------------------------- */ + if (index != -1) + { + found = 1; + incr = 1; + } + } + + + + /* If mapping not found check for indexed mapping */ + /* ---------------------------------------------- */ + if (found == 0) + { + /* Get size of geo dim & allocate space of index mapping */ + /* ----------------------------------------------------- */ + dum = SWdiminfo(swathID, geodim); + idxmap = (int32 *) calloc(dum, sizeof(int32)); + if(idxmap == NULL) + { + HEpush(DFE_NOSPACE,"SWregioninfo", __FILE__, __LINE__); + return(-1); + } + + /* Loop through all dimensions and search for mapping */ + /* -------------------------------------------------- */ + for (i = 0; i < nDim; i++) + { + memcpy(utlbuf, ptr[i], slen[i]); + utlbuf[slen[i]] = 0; + + idxMapElem = SWidxmapinfo(swathID, geodim, utlbuf, idxmap); + + + /* Mapping found */ + /* ------------- */ + if (idxMapElem != -1) + { + found = 1; + index = i; + break; + } + } + } + + + /* Regular Mapping Found */ + /* --------------------- */ + if (found == 1 && idxMapElem == -1) + { + dims[index] = 0; + + /* Loop through all regions */ + /* ------------------------ */ + for (k = 0; k < SWXRegion[regionID]->nRegions; k++) + { + /* Get number of cross tracks in particular region */ + /* ----------------------------------------------- */ + nXtrk = SWXRegion[regionID]->StopRegion[k] - + SWXRegion[regionID]->StartRegion[k] + 1; + + + /* If increment is positive (geodim <= datadim) ... */ + /* ------------------------------------------------ */ + if (incr > 0) + { + if (SWXRegion[regionID]->scanflag == 1) + { + scan_shift = nXtrk % incr; + if(scan_shift != 0) + nXtrk = nXtrk - scan_shift; + dims[index] += nXtrk/incr; + if(long_status == -1 || incr == 1) + { + scan_shift = nXtrk % detect_cnt; + if(scan_shift != 0) + dims[index] = nXtrk - scan_shift; + } + } + else + { + dims[index] += nXtrk * incr; + } + } + else + { + /* Negative increment (geodim > datadim) */ + /* ------------------------------------- */ + dims[index] += nXtrk / (-incr); + + /* + * If Xtrk not exactly divisible by incr, round dims + * to next highest integer + */ + if (nXtrk % (-incr) != 0) + { + dims[index]++; + } + } + } + } + else if (found == 1 && idxMapElem != -1) + { + + /* Indexed Mapping */ + /* --------------- */ + + dims[index] = 0; + + /* Loop through all regions */ + /* ------------------------ */ + for (k = 0; k < SWXRegion[regionID]->nRegions; k++) + { + j = 0; + if(SWXRegion[regionID]->scanflag == 1) + { + startscanline = SWXRegion[regionID]->StartRegion[k]; + stopscanline = SWXRegion[regionID]->StopRegion[k]; + if (strcmp(fieldname,dfieldlist) == 0) + { + dims[index] = stopscanline - startscanline + 1; + datafld = 1; + } + if (strcmp(fieldname, "Latitude") == 0 || + strcmp(fieldname, "Longitude") == 0) + { + if(SWXRegion[regionID]->band8flag == 2 || SWXRegion[regionID]->band8flag == 3) + { + status = SWfieldinfo(swathID,"scan_no",&rk,dims2,&nt,dimlist); + tbuffer = (uint16 *)calloc(dims2[0], sizeof(uint16)); + status =SWreadfield(swathID,"scan_no",NULL,NULL,NULL,tbuffer); + startscanline = startscanline - ((tbuffer[0] * detect_cnt) - detect_cnt); + stopscanline = stopscanline - ((tbuffer[0] * detect_cnt) - 1); + } + if(SWXRegion[regionID]->band8flag == 2 || + SWXRegion[regionID]->band8flag == 3) + { + if(startscandim == -1) + if(startscanline < idxmap[0]) + { + startscandim = 0; + dims[index] = 0; + if(stopscanline > idxmap[scene_cnt *2 - 1]) + { + stopscandim = scene_cnt*2 - startscandim; + dims[index] = scene_cnt*2 - startscandim; + } + } + } + for (l = 0; l < scene_cnt; l++) + { + if(idxmap[j] <= startscanline && idxmap[j+1] >= startscanline) + if(startscandim == -1) + { + dims[index] = j; + startscandim = j; + } + if(idxmap[j] <= stopscanline && idxmap[j+1] >= stopscanline) + if(startscandim != -1) + { + dims[index] = j - startscandim + 2; + stopscandim = j + 1; + } + j = j + 2; + if(idxmap[j] == 0 || idxmap[j+1] == 0) + l = scene_cnt; + } + if(SWXRegion[regionID]->band8flag == 1 || + SWXRegion[regionID]->band8flag == 2) + { + if(stopscandim == -1) + if(stopscanline > idxmap[scene_cnt * 2 - 1]) + { + stopscandim = scene_cnt*2 - dims[index]; + dims[index] = scene_cnt*2 - dims[index]; + } + } + if(SWXRegion[regionID]->band8flag == 3) + { + if(startscandim == -1) + if(startscanline < idxmap[0]) + { + startscandim = 0; + if(stopscandim != -1) + dims[index] = stopscandim - startscandim + 1; + } + } + if(SWXRegion[regionID]->band8flag == 2) + { + if(startscandim == -1) + if(startscanline > idxmap[j - 1]) + { + status = SWfieldinfo(SWXRegion[regionID]->swathID,"scan_no",&rank2,dims2,&nt,dimlist); + buffer2 = (uint16 *)calloc(dims2[0], sizeof(uint16)); + status = SWreadfield(SWXRegion[regionID]->swathID,"scan_no",NULL,NULL,NULL,buffer2); + startscanline = startscanline - (buffer2[0] * detect_cnt); + stopscanline = stopscanline - (buffer2[0] * detect_cnt); + free(buffer2); + j = 0; + for (l = 0; l < scene_cnt; l++) + { + if(idxmap[j] <= startscanline && idxmap[j+1] >= startscanline) + { + dims[index] = j; + startscandim = j; + } + if(idxmap[j] <= stopscanline && idxmap[j+1] >= stopscanline) + dims[index] = j - startscandim + 2; + j = j + 2; + if(idxmap[j] == 0 || idxmap[j+1] == 0) + l = scene_cnt; + } + + } + } + } + if (strcmp(fieldname, "scene_center_latitude") == 0 || + strcmp(fieldname, "scene_center_longitude") == 0) + { + startscanline = SWXRegion[regionID]->StartRegion[k]; + stopscanline = SWXRegion[regionID]->StopRegion[k]; + if(startscanline < idxmap[0]) + { + startscandim = 0; + dims[index] = 0; + } + for (l = 0; l < scene_cnt-1; l++) + { + if(idxmap[l] <= startscanline && idxmap[l+1] >= startscanline) + if(startscandim == -1) + { + dims[index] = l; + startscandim = l; + } + if(idxmap[l] <= stopscanline && idxmap[l+1] >= stopscanline) + { + dims[index] = l - startscandim + 2; + stopscandim = l + 1; + } + } + if(stopscandim == -1) + { + if(stopscanline > idxmap[scene_cnt - 1]) + { + dims[index] = scene_cnt - startscandim; + stopscandim = scene_cnt - 1; + } + } + if(SWXRegion[regionID]->band8flag == 1) + { + if(stopscandim == -1) + if(stopscanline > idxmap[scene_cnt - 1]) + { + dims[index] = scene_cnt - startscandim; + stopscandim = scene_cnt - 1; + } + } + if(SWXRegion[regionID]->band8flag == 2 || + SWXRegion[regionID]->band8flag == 3) + { + if(startscandim == -1) + { + if(startscanline < idxmap[0]) + { + startscandim = 0; + dims[index] = stopscandim - startscandim + 1; + } + } + if(startscandim == -1) + { + startscanline = SWXRegion[regionID]->StartScan[k] * detect_cnt; + stopscanline = SWXRegion[regionID]->StopScan[k] * detect_cnt; + for (l = 0; l < scene_cnt-1; l++) + { + if(idxmap[l] <= startscanline && idxmap[l+1] >= startscanline) + dims[index] = l; + if(idxmap[l] <= stopscanline && idxmap[l+1] >= stopscanline) + dims[index] = l - dims[index] + 1; + } + } + } + } + } + else + { + if (datafld != 1) + { + /* Get number of cross tracks in particular region */ + /* ----------------------------------------------- */ + nXtrk = idxmap[SWXRegion[regionID]->StopRegion[k]] - + idxmap[SWXRegion[regionID]->StartRegion[k]] + 1; + + dims[index] += nXtrk; + } + } + } + } + else + { + /* Mapping not found */ + /* ----------------- */ + status = -1; + HEpush(DFE_GENAPP, "SWregioninfo", + __FILE__, __LINE__); + HEreport( + "Mapping Not Defined for \"%s\" Dimension.\n", + geodim); + } + } + + + + /* Vertical Subset */ + /* --------------- */ + if (status == 0 || status == -1) /* check the vertical subset in any case -- xhua */ + { + for (j = 0; j < 8; j++) + { + /* If active vertical subset ... */ + /* ----------------------------- */ + if (SWXRegion[regionID]->StartVertical[j] != -1) + { + + /* Find vertical dimension within dimlist */ + /* -------------------------------------- */ + index = EHstrwithin(SWXRegion[regionID]->DimNamePtr[j], + dimlist, ','); + + /* If dimension found ... */ + /* ---------------------- */ + if (index != -1) + { + /* Compute dimension size */ + /* ---------------------- */ + dims[index] = + SWXRegion[regionID]->StopVertical[j] - + SWXRegion[regionID]->StartVertical[j] + 1; + } + else + { + /* Vertical dimension not found */ + /* ---------------------------- */ + status = -1; + *size = -1; + HEpush(DFE_GENAPP, "SWregioninfo", __FILE__, __LINE__); + HEreport(errMesg, SWXRegion[regionID]->DimNamePtr[j]); + } + } + } + + + + /* Compute size of region data buffer */ + /* ---------------------------------- */ + if (status == 0) + { + if(idxMapElem == 1 && SWXRegion[regionID]->scanflag == 1 && land_status == 0) + { + if(startscandim == dims[0]) + dims[0] = scene_cnt*2 - startscandim; + } + + /* Compute number of total elements */ + /* -------------------------------- */ + *size = dims[0]; + for (j = 1; j < *rank; j++) + { + *size *= dims[j]; + } + + /* Multiply by size in bytes of numbertype */ + /* --------------------------------------- */ + *size *= DFKNTsize(*ntype); + } + } + } + + + + /* Free index mappings if applicable */ + /* --------------------------------- */ + if (idxmap != NULL) + { + free(idxmap); + } + if(dfieldlist != NULL) + free(dfieldlist); + + return (status); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWperiodinfo | +| | +| DESCRIPTION: Returns size in bytes of region | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 Swath structure ID | +| periodID int32 Period ID | +| fieldname char Fieldname | +| | +| | +| OUTPUTS: | +| ntype int32 field number type | +| rank int32 field rank | +| dims int32 dimensions of field region | +| size int32 size in bytes of field region | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Jun 03 Abe Taaheri added clearing timeflag if SWregioninfo failes | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWperiodinfo(int32 swathID, int32 periodID, char *fieldname, + int32 * ntype, int32 * rank, int32 dims[], int32 * size) +{ + intn status; /* routine return status variable */ + + + timeflag = 1; + /* Call SWregioninfo */ + /* ----------------- */ + status = SWregioninfo(swathID, periodID, fieldname, ntype, rank, + dims, size); + if (status != 0) timeflag = 0;/* clear timeflag if SWregioninfo failed */ + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdefvrtregion | +| | +| DESCRIPTION: Finds elements of a monotonic field within a given range. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| regionID int32 Region ID | +| | +| INPUTS: | +| swathID int32 Swath structure ID | +| regionID int32 Region ID | +| vertObj char Vertical object to subset | +| range float64 Vertical subsetting range | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Aug 96 Joel Gales Original Programmer | +| Dec 96 Joel Gales Add multiple vertical subsetting capability | +| May 97 Joel Gales Check for supported field types | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ + + +/* Macro to initialize swath region entry */ +/* -------------------------------------- */ + +/* + * 1) Find empty (inactive) region. 2) Allocate space for region entry. 3) + * Store file ID and swath ID. 4) Set region ID. 5) Initialize vertical + * subset entries to -1. + */ + +#define SETSWTHREG \ +\ +for (k = 0; k < NSWATHREGN; k++) \ +{ \ + if (SWXRegion[k] == 0) \ + { \ + SWXRegion[k] = (struct swathRegion *) \ + calloc(1, sizeof(struct swathRegion)); \ + SWXRegion[k]->fid = fid; \ + SWXRegion[k]->swathID = swathID; \ + regionID = k; \ + for (j=0; j<8; j++) \ + { \ + SWXRegion[k]->StartVertical[j] = -1; \ + SWXRegion[k]->StopVertical[j] = -1; \ + SWXRegion[k]->StartScan[j] = -1; \ + SWXRegion[k]->StopScan[j] = -1; \ + SWXRegion[k]->band8flag = -1; \ + } \ + break; \ + } \ +} + + +/* Macro to fill vertical subset entry */ +/* ----------------------------------- */ + +/* + * 1) Find empty (inactive) vertical region. 2) Set start of vertical region. + * 3) Allocate space for name of vertical dimension. 4) Write vertical + * dimension name. + */ + +#define FILLVERTREG \ +for (j=0; j<8; j++) \ +{ \ + if (SWXRegion[regionID]->StartVertical[j] == -1) \ + { \ + SWXRegion[regionID]->StartVertical[j] = i; \ + SWXRegion[regionID]->DimNamePtr[j] = \ + (char *) malloc(slen + 1); \ + memcpy(SWXRegion[regionID]->DimNamePtr[j], \ + dimlist, slen + 1); \ + break; \ + } \ +} \ + + + +int32 +SWdefvrtregion(int32 swathID, int32 regionID, char *vertObj, float64 range[]) +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn k; /* Loop index */ + intn status; /* routine return status variable */ + + uint8 found = 0; /* Found flag */ + + int16 vertINT16; /* Temporary INT16 variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath Vgroup ID */ + int32 slen; /* String length */ + int32 rank; /* Field rank */ + int32 nt; /* Field numbertype */ + int32 dims[8]; /* Field dimensions */ + int32 size; /* Size of numbertype in bytes */ + int32 vertINT32; /* Temporary INT32 variable */ + + float32 vertFLT32; /* Temporary FLT32 variable */ + + float64 vertFLT64; /* Temporary FLT64 variable */ + + char *vertArr; /* Pointer to vertical field data buffer */ + char dimlist[256]; /* Dimension list */ + + + /* Check for valid swath ID */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWdefvrtregion", &fid, &sdInterfaceID, + &swVgrpID); + + if (status == 0) + { + /* Copy first 4 characters of vertObj into dimlist */ + /* ----------------------------------------------- */ + memcpy(dimlist, vertObj, 4); + dimlist[4] = 0; + + + + /* If first 4 characters of vertObj = "DIM:" ... */ + /* --------------------------------------------- */ + + /* Vertical Object is dimension name */ + /* --------------------------------- */ + if (strcmp(dimlist, "DIM:") == 0) + { + /* Get string length of vertObj (minus "DIM:) */ + /* ------------------------------------------ */ + slen = strlen(vertObj) - 4; + + + /* If regionID = -1 then setup swath region entry */ + /* ---------------------------------------------- */ + if (regionID == -1) + { + SETSWTHREG; + } + + + /* Find first empty (inactive) vertical subset entry */ + /* ------------------------------------------------- */ + for (j = 0; j < 8; j++) + { + if (SWXRegion[regionID]->StartVertical[j] == -1) + { + /* Store start & stop of vertical region */ + /* ------------------------------------- */ + SWXRegion[regionID]->StartVertical[j] = (int32) range[0]; + SWXRegion[regionID]->StopVertical[j] = (int32) range[1]; + + /* Store vertical dimension name */ + /* ----------------------------- */ + SWXRegion[regionID]->DimNamePtr[j] = + (char *) malloc(slen + 1); + if(SWXRegion[regionID]->DimNamePtr[j] == NULL) + { + HEpush(DFE_NOSPACE,"SWdefvrtregion", __FILE__, __LINE__); + return(-1); + } + memcpy(SWXRegion[regionID]->DimNamePtr[j], + vertObj + 4, slen + 1); + break; + } + } + } + else + { + + /* Vertical Object is fieldname */ + /* ---------------------------- */ + + + /* Check for valid fieldname */ + /* ------------------------- */ + status = SWfieldinfo(swathID, vertObj, &rank, dims, &nt, + dimlist); + + if (status != 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWdefvrtregion", __FILE__, __LINE__); + HEreport("Vertical Field: \"%s\" not found.\n", vertObj); + } + + + + /* Check for supported field types */ + /* ------------------------------- */ + if (nt != DFNT_INT16 && + nt != DFNT_INT32 && + nt != DFNT_FLOAT32 && + nt != DFNT_FLOAT64) + { + status = -1; + HEpush(DFE_GENAPP, "SWdefvrtregion", __FILE__, __LINE__); + HEreport("Fieldtype: %d not supported for vertical subsetting.\n", nt); + } + + + + /* Check that vertical dimension is 1D */ + /* ----------------------------------- */ + if (status == 0) + { + if (rank != 1) + { + status = -1; + HEpush(DFE_GENAPP, "SWdefvrtregion", __FILE__, __LINE__); + HEreport("Vertical Field: \"%s\" must be 1-dim.\n", + vertObj); + } + } + + + /* If no problems then continue */ + /* ---------------------------- */ + if (status == 0) + { + /* Get string length of vertical dimension */ + /* --------------------------------------- */ + slen = strlen(dimlist); + + + /* Get size in bytes of vertical field numbertype */ + /* ---------------------------------------------- */ + size = DFKNTsize(nt); + + + /* Allocate space for vertical field */ + /* --------------------------------- */ + vertArr = (char *) calloc(dims[0], size); + if(vertArr == NULL) + { + HEpush(DFE_NOSPACE,"SWdefvrtregion", __FILE__, __LINE__); + return(-1); + } + + /* Read vertical field */ + /* ------------------- */ + status = SWreadfield(swathID, vertObj, + NULL, NULL, NULL, vertArr); + + + + switch (nt) + { + case DFNT_INT16: + + for (i = 0; i < dims[0]; i++) + { + /* Get single element of vertical field */ + /* ------------------------------------ */ + memcpy(&vertINT16, vertArr + i * size, size); + + + /* If within range ... */ + /* ------------------- */ + if (vertINT16 >= range[0] && + vertINT16 <= range[1]) + { + /* Set found flag */ + /* -------------- */ + found = 1; + + + /* If regionID=-1 then setup swath region entry */ + /* -------------------------------------------- */ + if (regionID == -1) + { + SETSWTHREG; + } + + + /* Fill-in vertical region entries */ + /* ------------------------------- */ + FILLVERTREG; + + break; + } + } + + + /* If found read from "bottom" of data field */ + /* ----------------------------------------- */ + if (found == 1) + { + for (i = dims[0] - 1; i >= 0; i--) + { + /* Get single element of vertical field */ + /* ------------------------------------ */ + memcpy(&vertINT16, vertArr + i * size, size); + + + /* If within range ... */ + /* ------------------- */ + if (vertINT16 >= range[0] && + vertINT16 <= range[1]) + { + /* Set end of vertical region */ + /* -------------------------- */ + SWXRegion[regionID]->StopVertical[j] = i; + break; + } + } + } + else + { + /* No vertical entries within region */ + /* --------------------------------- */ + status = -1; + HEpush(DFE_GENAPP, "SWdefvrtregion", + __FILE__, __LINE__); + HEreport("No vertical field entries within region.\n"); + } + break; + + + case DFNT_INT32: + + for (i = 0; i < dims[0]; i++) + { + /* Get single element of vertical field */ + /* ------------------------------------ */ + memcpy(&vertINT32, vertArr + i * size, size); + + + /* If within range ... */ + /* ------------------- */ + if (vertINT32 >= range[0] && + vertINT32 <= range[1]) + { + /* Set found flag */ + /* -------------- */ + found = 1; + + + /* If regionID=-1 then setup swath region entry */ + /* -------------------------------------------- */ + if (regionID == -1) + { + SETSWTHREG; + } + + + /* Fill-in vertical region entries */ + /* ------------------------------- */ + FILLVERTREG; + + break; + } + } + + + /* If found read from "bottom" of data field */ + /* ----------------------------------------- */ + if (found == 1) + { + for (i = dims[0] - 1; i >= 0; i--) + { + /* Get single element of vertical field */ + /* ------------------------------------ */ + memcpy(&vertINT32, vertArr + i * size, size); + + + /* If within range ... */ + /* ------------------- */ + if (vertINT32 >= range[0] && + vertINT32 <= range[1]) + { + /* Set end of vertical region */ + /* -------------------------- */ + SWXRegion[regionID]->StopVertical[j] = i; + break; + } + } + } + else + { + /* No vertical entries within region */ + /* --------------------------------- */ + status = -1; + HEpush(DFE_GENAPP, "SWdefvrtregion", + __FILE__, __LINE__); + HEreport("No vertical field entries within region.\n"); + } + break; + + + case DFNT_FLOAT32: + + for (i = 0; i < dims[0]; i++) + { + /* Get single element of vertical field */ + /* ------------------------------------ */ + memcpy(&vertFLT32, vertArr + i * size, size); + + + /* If within range ... */ + /* ------------------- */ + if (vertFLT32 >= range[0] && + vertFLT32 <= range[1]) + { + /* Set found flag */ + /* -------------- */ + found = 1; + + + /* If regionID=-1 then setup swath region entry */ + /* -------------------------------------------- */ + if (regionID == -1) + { + SETSWTHREG; + } + + + /* Fill-in vertical region entries */ + /* ------------------------------- */ + FILLVERTREG; + + break; + } + } + + + /* If found read from "bottom" of data field */ + /* ----------------------------------------- */ + if (found == 1) + { + for (i = dims[0] - 1; i >= 0; i--) + { + /* Get single element of vertical field */ + /* ------------------------------------ */ + memcpy(&vertFLT32, vertArr + i * size, size); + + + /* If within range ... */ + /* ------------------- */ + if (vertFLT32 >= range[0] && + vertFLT32 <= range[1]) + { + /* Set end of vertical region */ + /* -------------------------- */ + SWXRegion[regionID]->StopVertical[j] = i; + break; + } + } + } + else + { + /* No vertical entries within region */ + /* --------------------------------- */ + status = -1; + HEpush(DFE_GENAPP, "SWdefvrtregion", + __FILE__, __LINE__); + HEreport("No vertical field entries within region.\n"); + } + break; + + + case DFNT_FLOAT64: + + for (i = 0; i < dims[0]; i++) + { + /* Get single element of vertical field */ + /* ------------------------------------ */ + memcpy(&vertFLT64, vertArr + i * size, size); + + + /* If within range ... */ + /* ------------------- */ + if (vertFLT64 >= range[0] && + vertFLT64 <= range[1]) + { + /* Set found flag */ + /* -------------- */ + found = 1; + + + /* If regionID=-1 then setup swath region entry */ + /* -------------------------------------------- */ + if (regionID == -1) + { + SETSWTHREG; + } + + + /* Fill-in vertical region entries */ + /* ------------------------------- */ + FILLVERTREG; + + break; + } + } + + + /* If found read from "bottom" of data field */ + /* ----------------------------------------- */ + if (found == 1) + { + for (i = dims[0] - 1; i >= 0; i--) + { + /* Get single element of vertical field */ + /* ------------------------------------ */ + memcpy(&vertFLT64, vertArr + i * size, size); + + /* If within range ... */ + /* ------------------- */ + if (vertFLT64 >= range[0] && + vertFLT64 <= range[1]) + { + /* Set end of vertical region */ + /* -------------------------- */ + SWXRegion[regionID]->StopVertical[j] = i; + break; + } + } + } + else + { + /* No vertical entries within region */ + /* --------------------------------- */ + status = -1; + HEpush(DFE_GENAPP, "SWdefvrtregion", + __FILE__, __LINE__); + HEreport("No vertical field entries within region.\n"); + } + break; + + } /* End of switch */ + free(vertArr); + } + } + } + + + /* Set regionID to -1 if bad return status */ + /* --------------------------------------- */ + if (status == -1) + { + regionID = -1; + } + + + return (regionID); +} + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdefscanregion | +| | +| DESCRIPTION: Initialize the region structure for Landsat 7 float scene | +| subset | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| regionID int32 Region ID | +| | +| INPUTS: | +| swathID int32 Swath structure ID | +| fieldname char Field name to subset | +| range float64 subsetting range | +| mode int32 HDFE_ENDPOINT, HDFE_MIDPOINT or | +| HDFE_ANYPOINT | +| | +| OUTPUTS: | +| regionID int32 Region ID | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jul 99 DaW Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWdefscanregion(int32 swathID, char *fieldname, float64 range[], int32 mode) +{ + intn j; /* Loop index */ + intn k; /* Loop index */ + intn status; /* routine return status variable */ + intn land_status = 3; /* routine return status variable */ + intn band81flag = 0; + intn band82flag = 0; + intn band83flag = 0; + uint8 detect_cnt = 0; /* Used to convert scan to scanline */ + /* L7 float scene sub. */ + uint8 scene_cnt = 0; + + + int32 nmtype = 0; /* Used for L7 float scene sub. */ + int32 count = 0; /* Used for L7 float scene sub. */ + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath Vgroup ID */ + int32 slen; /* String length */ + int32 dfrank[8]; /* data fields rank */ + int32 rank; /* Field rank */ + int32 numtype[8]; /* number type of data fields */ + int32 nt; /* Field numbertype */ + int32 dims[8]; /* Field dimensions */ + int32 dims2[8]; /* Field dimensions */ + int32 strbufsize = 0; /* string buffer size */ + int32 tmprange0 = 0; + + + uint16 *buffer = (uint16 *)NULL; + int32 *idxmap = (int32 *)NULL; + + int32 dimsize = 0; + + int32 regionID = -1; /* Region ID (return) */ + + float64 scan[2] = {0,0}; + float64 original_scan[2] = {0,0}; + + char dimlist[256]; /* Dimension list */ + char swathname[80]; + char *dfieldlist = (char *)NULL; /* data field list */ + char *tfieldname = (char *)NULL; /* temp field buffer */ + char *band81 = (char *)NULL; + char *band82 = (char *)NULL; + char *band83 = (char *)NULL; + + + /* Check for valid swath ID */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWdefscanregion", &fid, &sdInterfaceID, + &swVgrpID); + + /* This code checks for the attribute detector_count */ + /* which is found in Landsat 7 files. It is used */ + /* for some of the loops. The other code checks if */ + /* one scan is requested. */ + /* ================================================= */ + land_status = SWattrinfo(swathID, "detector_count", &nmtype, &count); + if (land_status == 0) + { + scan[0] = range[0]; + scan[1] = range[1]; + original_scan[0] = range[0]; + original_scan[1] = range[1]; + + land_status = SWreadattr(swathID, "scene_count", &scene_cnt); + land_status = SWreadattr(swathID, "detector_count", &detect_cnt); + if (range[0] == range[1]) + { + range[0] = range[0] * detect_cnt - detect_cnt; + range[1] = range[0] + detect_cnt - 1; + } + else + { + range[0] = range[0] * detect_cnt - detect_cnt; + range[1] = range[1] * detect_cnt - 1; + } + + Vgetname(SWXSwath[0].IDTable, swathname); + band81 = strstr(swathname, "B81"); + if (band81 != (char *)NULL) + band81flag = 1; + band82 = strstr(swathname, "B82"); + if (band82 != (char *)NULL) + band82flag = 1; + band83 = strstr(swathname, "B83"); + if (band83 != (char *)NULL) + band83flag = 1; + } + + + /* If fieldname is null then subsetting Landsat 7 */ + /* floating scene. Get data field name, assume */ + /* only one data field in swath */ + /* ---------------------------------------------- */ + + if (fieldname == (char *)NULL) + { + (void) SWnentries(swathID, 4, &strbufsize); + dfieldlist = (char *)calloc(strbufsize + 1, sizeof(char)); + (void) SWinqdatafields(swathID, dfieldlist, dfrank, numtype); + tfieldname = (char *)calloc(strbufsize + 1, sizeof(char)); + strcpy(tfieldname, dfieldlist); + } + else + { + slen = strlen(fieldname); + tfieldname = (char *)calloc(slen + 1, sizeof(char)); + strcpy(tfieldname, fieldname); + } + + /* Check for valid fieldname */ + /* ------------------------- */ + status = SWfieldinfo(swathID, tfieldname, &rank, dims, &nt, + dimlist); + + if (status != 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWdefscanregion", __FILE__, __LINE__); + HEreport("Field: \"%s\" not found.\n", tfieldname); + } + + + /* Check if input range values are within range of */ + /* data field */ + /* ----------------------------------------------- */ + if(status == 0) + { + status = SWfieldinfo(swathID, "scan_no", &rank, dims2, &nt, dimlist); + buffer = (uint16 *)calloc(dims2[0], sizeof(uint16)); + status = SWreadfield(swathID,"scan_no", NULL, NULL, NULL, buffer); + if(scan[0] > buffer[dims2[0]-1]) + { + HEpush(DFE_GENAPP, "SWdefscanregion", __FILE__, __LINE__); + HEreport("Range values not within bounds of data field\n"); + free(buffer); + buffer = (uint16 *)NULL; + if (dfieldlist != NULL) + free(dfieldlist); + free(tfieldname); + return(-1); + } + if(scan[0] < buffer[0]) + { + if(scan[1] < buffer[0]) + { + HEpush(DFE_GENAPP, "SWdefscanregion", __FILE__, __LINE__); + HEreport("Range values not within bounds of data field\n"); + free(buffer); + buffer = (uint16 *)NULL; + if (dfieldlist != NULL) + free(dfieldlist); + free(tfieldname); + return(-1); + } + else + { + scan[0] = buffer[0]; + range[0] = scan[0] * detect_cnt - detect_cnt; + } + } + if(scan[1] > buffer[dims2[0] - 1]) + { + scan[1] = buffer[dims2[0] - 1]; + range[1] = scan[1] * detect_cnt - 1; + } + } + + if(status == 0) + { + dimsize = SWdiminfo(swathID, "GeoTrack"); + if(dimsize > 0) + { + idxmap = (int32 *)calloc(dimsize, sizeof(int32)); + (void) SWidxmapinfo(swathID, "GeoTrack", "ScanLineTrack", idxmap); + tmprange0 = range[0]; + if(band82flag != 1 && band83flag != 1) + { + if (range[1] > idxmap[scene_cnt*2 - 1]) + { + range[1] = idxmap[scene_cnt*2 - 1]; + HEreport("Data length compared to geolocation length\n"); + } + } + if(band82flag == 1 || band83flag == 1) + { + tmprange0 = range[0] - (buffer[0] * detect_cnt - detect_cnt); + } + if(tmprange0 >= idxmap[scene_cnt * 2 - 1]) + { + HEpush(DFE_GENAPP, "SWdefscanregion", __FILE__, __LINE__); + HEreport( + "Range values not within bounds of Latitude/Longitude field(s)\n"); + if (dfieldlist != NULL) + free(dfieldlist); + free(tfieldname); + free(buffer); + free(idxmap); + return(-1); + } + } + } + + if (status == 0) + { + slen = strlen(tfieldname); + + SETSWTHREG; + + /* Find first empty (inactive) vertical subset entry */ + /* ------------------------------------------------- */ + for (j = 0; j < 8; j++) + { + if (SWXRegion[regionID]->StartVertical[j] == -1) + { + /* Store start & stop of region */ + /* ------------------------------------- */ + SWXRegion[regionID]->StartScan[j] = (int32) original_scan[0]; + SWXRegion[regionID]->StopScan[j] = (int32) original_scan[1]; + SWXRegion[regionID]->StartRegion[j] = (int32) range[0]; + SWXRegion[regionID]->StopRegion[j] = (int32) range[1]; + ++SWXRegion[regionID]->nRegions; + SWXRegion[regionID]->scanflag = 1; + if(band81flag == 1) + SWXRegion[regionID]->band8flag = 1; + if(band82flag == 1) + SWXRegion[regionID]->band8flag = 2; + if(band83flag == 1) + SWXRegion[regionID]->band8flag = 3; + break; + } + } + } + + + /* Set regionID to -1 if bad return status */ + /* --------------------------------------- */ + if (status == -1) + { + regionID = -1; + } + + if (dfieldlist != NULL) + free(dfieldlist); + free(tfieldname); + if (buffer != NULL) + free(buffer); + if (idxmap != NULL) + free(idxmap); + + return (regionID); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWsetfillvalue | +| | +| DESCRIPTION: Sets fill value for the specified field. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| fieldname char field name | +| fillval void fill value | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWsetfillvalue(int32 swathID, char *fieldname, VOIDP fillval) +{ + intn status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath root Vgroup ID */ + int32 sdid; /* SDS id */ + int32 nt; /* Number type */ + int32 dims[8]; /* Dimensions array */ + int32 dum; /* Dummy variable */ + int32 solo; /* "Solo" (non-merged) field flag */ + + char name[80]; /* Fill value "attribute" name */ + + /* Check for valid swath ID and get SDS interface ID */ + status = SWchkswid(swathID, "SWsetfillvalue", + &fid, &sdInterfaceID, &swVgrpID); + + if (status == 0) + { + /* Get field info */ + status = SWfieldinfo(swathID, fieldname, &dum, dims, &nt, NULL); + + if (status == 0) + { + /* Get SDS ID and solo flag */ + status = SWSDfldsrch(swathID, sdInterfaceID, fieldname, + &sdid, &dum, &dum, &dum, + dims, &solo); + + /* If unmerged field then call HDF set field routine */ + if (solo == 1) + { + status = SDsetfillvalue(sdid, fillval); + } + + /* + * Store fill value in attribute. Name is given by fieldname + * prepended with "_FV_" + */ + strcpy(name, "_FV_"); + strcat(name, fieldname); + status = SWwriteattr(swathID, name, nt, 1, fillval); + } + else + { + HEpush(DFE_GENAPP, "SWsetfillvalue", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" does not exist.\n", fieldname); + } + } + return (status); +} + + + + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWgetfillvalue | +| | +| DESCRIPTION: Retrieves fill value for a specified field. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| fieldname char field name | +| | +| OUTPUTS: | +| fillval void fill value | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWgetfillvalue(int32 swathID, char *fieldname, VOIDP fillval) +{ + intn status; /* routine return status variable */ + + int32 nt; /* Number type */ + int32 dims[8]; /* Dimensions array */ + int32 dum; /* Dummy variable */ + + char name[80]; /* Fill value "attribute" name */ + + /* Check for valid swath ID */ + status = SWchkswid(swathID, "SWgetfillvalue", &dum, &dum, &dum); + + if (status == 0) + { + /* Get field info */ + status = SWfieldinfo(swathID, fieldname, &dum, dims, &nt, NULL); + + if (status == 0) + { + /* Read fill value attribute */ + strcpy(name, "_FV_"); + strcat(name, fieldname); + status = SWreadattr(swathID, name, fillval); + } + else + { + HEpush(DFE_GENAPP, "SWgetfillvalue", __FILE__, __LINE__); + HEreport("Fieldname \"%s\" does not exist.\n", fieldname); + } + + } + return (status); +} + + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWdetach | +| | +| DESCRIPTION: Detachs swath structure and performs housekeeping | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 swath structure ID | +| | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| Aug 96 Joel Gales Cleanup Region External Structure | +| Sep 96 Joel Gales Setup dim names for SDsetdimnane in dimbuf1 rather | +| than utlstr | +| Nov 96 Joel Gales Call SWchkgdid to check for proper swath ID | +| Dec 96 Joel Gales Add multiple vertical subsetting garbage collection | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWdetach(int32 swathID) + +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn k; /* Loop index */ + intn status = 0; /* routine return status variable */ + intn statusFill = 0; /* return status from SWgetfillvalue */ + + uint8 *buf; /* Buffer for blank (initial) 1D records */ + + int32 vdataID; /* Vdata ID */ + int32 *namelen; /* Pointer to name string length array */ + int32 *dimlen; /* Pointer to dim string length array */ + int32 slen1[3]; /* String length array 1 */ + int32 slen2[3]; /* String length array 2 */ + int32 nflds; /* Number of fields */ + int32 match[5]; /* Merged field match array */ + int32 cmbfldcnt; /* Number of fields combined */ + int32 sdid; /* SDS ID */ + int32 vgid; /* Vgroup ID */ + int32 dims[3]; /* Dimension array */ + int32 *offset; /* Pointer to merged field offset array */ + int32 *indvdims; /* Pointer to merged field size array */ + int32 sdInterfaceID; /* SDS interface ID */ + int32 sID; /* Swath ID - offset */ + int32 nflds0; /* Number of fields */ + int32 *namelen0; /* Pointer to name string length array */ + int32 rank; /* Rank of merged field */ + int32 truerank; /* True rank of merged field */ + int32 idOffset = SWIDOFFSET; /* Swath ID offset */ + int32 dum; /* Dummy variable */ + + char *nambuf; /* Pointer to name buffer */ + char **nameptr; /* Pointer to name string pointer array */ + char **dimptr; /* Pointer to dim string pointer array */ + char **nameptr0; /* Pointer to name string pointer array */ + char *ptr1[3]; /* String pointer array */ + char *ptr2[3]; /* String pointer array */ + char dimbuf1[128]; /* Dimension buffer 1 */ + char dimbuf2[128]; /* Dimension buffer 2 */ + char swathname[VGNAMELENMAX + 1]; /* Swath name */ + char *utlbuf; /* Utility buffer */ + char fillval[32];/* Fill value buffer */ + + /* Check for proper swath ID and get SD interface ID */ + /* ------------------------------------------------- */ + status = SWchkswid(swathID, "SWdetach", &dum, &sdInterfaceID, &dum); + + if (status == 0) + { + /* Subtract off swath ID offset and get swath name */ + /* ----------------------------------------------- */ + sID = swathID % idOffset; + Vgetname(SWXSwath[sID].IDTable, swathname); + + + /* Create 1D "orphened" fields */ + /* --------------------------- */ + i = 0; + + /* Find "active" entries in 1d combination array */ + /* --------------------------------------------- */ + while (SWX1dcomb[3 * i] != 0) + { + /* For fields defined within swath... */ + /* ---------------------------------- */ + if (SWX1dcomb[3 * i + 1] == SWXSwath[sID].IDTable) + { + /* Get dimension size and vdata ID */ + /* ------------------------------- */ + dims[0] = abs(SWX1dcomb[3 * i]); + vdataID = SWX1dcomb[3 * i + 2]; + + /* Get fieldname (= vdata name) */ + /* ---------------------------- */ + nambuf = (char *) calloc(VSNAMELENMAX + 1, 1); + if(nambuf == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + return(-1); + } + + VSgetname(vdataID, nambuf); + + /* Set field within vdata */ + /* ---------------------- */ + VSsetfields(vdataID, nambuf); + + /* Write (blank) records */ + /* --------------------- */ + buf = (uint8 *) calloc(VSsizeof(vdataID, nambuf), dims[0]); + if(buf == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nambuf); + return(-1); + } + VSwrite(vdataID, buf, dims[0], FULL_INTERLACE); + + free(buf); + free(nambuf); + + /* Detach Vdata */ + /* ------------ */ + VSdetach(vdataID); + } + i++; + } + + + /* SDS combined fields */ + /* ------------------- */ + if (strlen(SWXSDname) == 0) + { + nflds = 0; + + /* Allocate "dummy" arrays so free() doesn't bomb later */ + /* ---------------------------------------------------- */ + nameptr = (char **) calloc(1, sizeof(char *)); + if(nameptr == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + return(-1); + } + namelen = (int32 *) calloc(1, sizeof(int32)); + if(namelen == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + return(-1); + } + nameptr0 = (char **) calloc(1, sizeof(char *)); + if(nameptr0 == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + return(-1); + } + namelen0 = (int32 *) calloc(1, sizeof(int32)); + if(namelen0 == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + return(-1); + } + dimptr = (char **) calloc(1, sizeof(char *)); + if(dimptr == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + return(-1); + } + dimlen = (int32 *) calloc(1, sizeof(int32)); + if(dimlen == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + return(-1); + } + offset = (int32 *) calloc(1, sizeof(int32)); + if(offset == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + free(dimlen); + return(-1); + } + indvdims = (int32 *) calloc(1, sizeof(int32)); + if(indvdims == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + free(dimlen); + free(offset); + return(-1); + } + } + else + { + /* + * "Trim Off" trailing "," and ";" in SWXSDname & SWXSDdims + * respectively + */ + SWXSDname[strlen(SWXSDname) - 1] = 0; + SWXSDdims[strlen(SWXSDdims) - 1] = 0; + + + /* Get number of fields from SWXSDname string */ + /* ------------------------------------------ */ + nflds = EHparsestr(SWXSDname, ',', NULL, NULL); + + + /* Allocate space for various dynamic arrays */ + /* ----------------------------------------- */ + nameptr = (char **) calloc(nflds, sizeof(char *)); + if(nameptr == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + return(-1); + } + namelen = (int32 *) calloc(nflds, sizeof(int32)); + if(namelen == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + return(-1); + } + nameptr0 = (char **) calloc(nflds, sizeof(char *)); + if(nameptr0 == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + return(-1); + } + namelen0 = (int32 *) calloc(nflds, sizeof(int32)); + if(namelen0 == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + return(-1); + } + dimptr = (char **) calloc(nflds, sizeof(char *)); + if(dimptr == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + return(-1); + } + dimlen = (int32 *) calloc(nflds, sizeof(int32)); + if(dimlen == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + return(-1); + } + offset = (int32 *) calloc(nflds, sizeof(int32)); + if(offset == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + free(dimlen); + return(-1); + } + indvdims = (int32 *) calloc(nflds, sizeof(int32)); + if(indvdims == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + free(dimlen); + free(offset); + return(-1); + } + + + /* Parse SWXSDname and SWXSDdims strings */ + /* ------------------------------------- */ + nflds = EHparsestr(SWXSDname, ',', nameptr, namelen); + nflds = EHparsestr(SWXSDdims, ';', dimptr, dimlen); + } + + + /* Loop through all the fields */ + /* --------------------------- */ + for (i = 0; i < nflds; i++) + { + /* If active entry and field is within swath to be detached ... */ + /* ------------------------------------------------------------ */ + if (SWXSDcomb[5 * i] != 0 && + SWXSDcomb[5 * i + 3] == SWXSwath[sID].IDTable) + { + nambuf = (char *) calloc(strlen(SWXSDname) + 1, 1); + if(nambuf == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + return(-1); + } + utlbuf = (char *) calloc(2 * strlen(SWXSDname) + 7, 1); + if(utlbuf == NULL) + { + HEpush(DFE_NOSPACE,"SWdetach", __FILE__, __LINE__); + free(nambuf); + return(-1); + } + /* Zero out dimbuf1 */ + /* ---------------- */ + for (k = 0; k < sizeof(dimbuf1); k++) + { + dimbuf1[k] = 0; + } + + + /* Load array to match, name & parse dims */ + /* -------------------------------------- */ + memcpy(match, &SWXSDcomb[5 * i], 20); + memcpy(nambuf, nameptr[i], namelen[i]); + + memcpy(dimbuf1, dimptr[i], dimlen[i]); + dum = EHparsestr(dimbuf1, ',', ptr1, slen1); + + + /* Separate combined (first) dimension from others */ + /* ----------------------------------------------- */ + dimbuf1[slen1[0]] = 0; + + offset[0] = 0; + indvdims[0] = abs(match[0]); + + /* + * Loop through remaining fields to check for matches with + * current one + */ + for (j = i + 1, cmbfldcnt = 0; j < nflds; j++) + { + if (SWXSDcomb[5 * j] != 0) + { + /* Zero out dimbuf2 */ + /* ---------------- */ + for (k = 0; k < sizeof(dimbuf2); k++) + { + dimbuf2[k] = 0; + } + + /* + * Parse the dimensions and separate first for this + * entry + */ + memcpy(dimbuf2, dimptr[j], dimlen[j]); + dum = EHparsestr(dimbuf2, ',', ptr2, slen2); + dimbuf2[slen2[0]] = 0; + + + /* + * If 2nd & 3rd dimension values and names (1st and + * 2nd for rank=2 array), swath ID, and numbertype + * are equal, then these fields can be combined. + */ + if (match[1] == SWXSDcomb[5 * j + 1] && + match[2] == SWXSDcomb[5 * j + 2] && + match[3] == SWXSDcomb[5 * j + 3] && + match[4] == SWXSDcomb[5 * j + 4] && + strcmp(dimbuf1 + slen1[0] + 1, + dimbuf2 + slen2[0] + 1) == 0) + { + /* Add to combined dimension size */ + /* ------------------------------ */ + match[0] += SWXSDcomb[5 * j]; + + /* Concatanate name */ + /* ---------------- */ + strcat(nambuf, ","); + memcpy(nambuf + strlen(nambuf), + nameptr[j], namelen[j]); + + /* + * Increment number of merged fields, store + * individual dims and dim offsets + */ + cmbfldcnt++; + indvdims[cmbfldcnt] = abs(SWXSDcomb[5 * j]); + offset[cmbfldcnt] = offset[cmbfldcnt - 1] + + indvdims[cmbfldcnt - 1]; + + /* Delete this field from combination list */ + /* --------------------------------------- */ + SWXSDcomb[5 * j] = 0; + } + } + } + + + /* Create SDS */ + /* ---------- */ + + /* Parse names string */ + /* ------------------ */ + nflds0 = EHparsestr(nambuf, ',', nameptr0, namelen0); + + if (abs(match[0]) == 1) + { + /* Two Dimensional Array (no merging has occured) */ + /* ---------------------------------------------- */ + dims[0] = abs(match[1]); + dims[1] = abs(match[2]); + + /* Create SDS */ + /* ---------- */ + rank = 2; + sdid = SDcreate(sdInterfaceID, nambuf, + SWXSDcomb[5 * i + 4], 2, dims); + } + else + { + /* Three Dimensional Array */ + /* ----------------------- */ + dims[0] = abs(match[0]); + dims[1] = abs(match[1]); + dims[2] = abs(match[2]); + + rank = 3; + + /* + * If merged fields then form string consisting of + * "MRGFLD_" + 1st field in merge + ":" + entire merged + * field list and store in utlbuf. Then write to + * MergedField metadata section + */ + if (cmbfldcnt > 0) + { + strcpy(utlbuf, "MRGFLD_"); + memcpy(utlbuf + 7, nameptr0[0], namelen0[0]); + utlbuf[7 + namelen0[0]] = 0; + strcat(utlbuf, ":"); + strcat(utlbuf, nambuf); + + status = EHinsertmeta(sdInterfaceID, swathname, "s", + 6L, utlbuf, NULL); + } + else + { + /* + * If not merged field then store field name in + * utlbuf + */ + strcpy(utlbuf, nambuf); + } + + /* Create SDS */ + /* ---------- */ + sdid = SDcreate(sdInterfaceID, utlbuf, + SWXSDcomb[5 * i + 4], 3, dims); + + + /* + * If merged field then store dimensions and offsets as + * SD attributes + */ + if (cmbfldcnt > 0) + { + SDsetattr(sdid, "Field Dims", DFNT_INT32, + cmbfldcnt + 1, (VOIDP) indvdims); + + SDsetattr(sdid, "Field Offsets", DFNT_INT32, + cmbfldcnt + 1, (VOIDP) offset); + } + } + + + + /* Register Dimensions in SDS */ + /* -------------------------- */ + for (k = 0; k < rank; k++) + { + if (rank == 2) + { + /* Copy k+1th dimension into dimbuf2 if rank = 2 */ + /* --------------------------------------------- */ + memcpy(dimbuf2, ptr1[k + 1], slen1[k + 1]); + dimbuf2[slen1[k + 1]] = 0; + } + else + { + /* Copy kth dimension into dimbuf2 if rank > 2 */ + /* ------------------------------------------- */ + memcpy(dimbuf2, ptr1[k], slen1[k]); + dimbuf2[slen1[k]] = 0; + } + + /* + * If first dimension and merged field then generate + * dimension name consisting of "MRGDIM:" + swathname + + * dimension size + */ + if (k == 0 && cmbfldcnt > 0) + { + sprintf(dimbuf2, "%s%s_%ld", "MRGDIM:", + swathname, (long)dims[0]); + } + else + { + /* Otherwise concatanate swathname to dim name */ + /* ------------------------------------------- */ + strcat(dimbuf2, ":"); + strcat(dimbuf2, swathname); + } + + /* Register dimensions using "SDsetdimname" */ + /* ---------------------------------------- */ + SDsetdimname(SDgetdimid(sdid, k), (char *) dimbuf2); + } + + + + /* Write Fill Value */ + /* ---------------- */ + for (k = 0; k < nflds0; k++) + { + /* Check if fill values has been set */ + /* --------------------------------- */ + memcpy(utlbuf, nameptr0[k], namelen0[k]); + utlbuf[namelen[k]] = 0; + statusFill = SWgetfillvalue(swathID, utlbuf, fillval); + + if (statusFill == 0) + { + /* + * If merged field then fill value must be stored + * manually using EHfillfld + */ + if (cmbfldcnt > 0) + { + dims[0] = indvdims[k]; + truerank = (dims[0] == 1) ? 2 : 3; + EHfillfld(sdid, rank, truerank, + DFKNTsize(match[4]), offset[k], + dims, fillval); + } + /* + * If single field then just use the HDF set fill + * function + */ + else + { + status = SDsetfillvalue(sdid, fillval); + } + } + } + + + /* + * Insert SDS within the appropriate Vgroup (geo or data) and + * "detach" newly-created SDS + */ + vgid = (match[0] < 0) + ? SWXSwath[sID].VIDTable[0] + : SWXSwath[sID].VIDTable[1]; + + Vaddtagref(vgid, DFTAG_NDG, SDidtoref(sdid)); + SDendaccess(sdid); + + free(nambuf); + free(utlbuf); + } + } + + + + /* "Contract" 1dcomb array */ + /* ----------------------- */ + i = 0; + while (SWX1dcomb[3 * i] != 0) + { + if (SWX1dcomb[3 * i + 1] == SWXSwath[sID].IDTable) + { + memcpy(&SWX1dcomb[3 * i], + &SWX1dcomb[3 * (i + 1)], + (512 - i - 1) * 3 * 4); + } + else + i++; + } + + + /* "Contract" SDcomb array */ + /* ----------------------- */ + for (i = 0; i < nflds; i++) + { + if (SWXSDcomb[5 * i + 3] == SWXSwath[sID].IDTable) + { + if (i == (nflds - 1)) + { + SWXSDcomb[5 * i] = 0; + *(nameptr[i] - (nflds != 1)) = 0; + *(dimptr[i] - (nflds != 1)) = 0; + } + else + { + memmove(&SWXSDcomb[5 * i], + &SWXSDcomb[5 * (i + 1)], + (512 - i - 1) * 5 * 4); + + memmove(nameptr[i], + nameptr[i + 1], + nameptr[0] + 2048 - nameptr[i + 1] - 1); + + memmove(dimptr[i], + dimptr[i + 1], + dimptr[0] + 2048 * 2 - dimptr[i + 1] - 1); + } + + i--; + nflds = EHparsestr(SWXSDname, ',', nameptr, namelen); + nflds = EHparsestr(SWXSDdims, ';', dimptr, dimlen); + } + } + + + /* Replace trailing delimitors on SWXSDname & SWXSDdims */ + /* ---------------------------------------------------- */ + if (nflds != 0) + { + strcat(SWXSDname, ","); + strcat(SWXSDdims, ";"); + } + + + + /* Free up a bunch of dynamically allocated arrays */ + /* ----------------------------------------------- */ + free(nameptr); + free(namelen); + free(nameptr0); + free(namelen0); + free(dimptr); + free(dimlen); + free(offset); + free(indvdims); + + + + + /* "Detach" from previously attached SDSs */ + /* -------------------------------------- */ + for (k = 0; k < SWXSwath[sID].nSDS; k++) + { + SDendaccess(SWXSwath[sID].sdsID[k]); + } + free(SWXSwath[sID].sdsID); + SWXSwath[sID].sdsID = 0; + SWXSwath[sID].nSDS = 0; + + + /* Detach Swath Vgroups */ + /* -------------------- */ + Vdetach(SWXSwath[sID].VIDTable[0]); + Vdetach(SWXSwath[sID].VIDTable[1]); + Vdetach(SWXSwath[sID].VIDTable[2]); + Vdetach(SWXSwath[sID].IDTable); + + + /* Delete entries from External Arrays */ + /* ----------------------------------- */ + SWXSwath[sID].active = 0; + SWXSwath[sID].VIDTable[0] = 0; + SWXSwath[sID].VIDTable[1] = 0; + SWXSwath[sID].VIDTable[2] = 0; + SWXSwath[sID].IDTable = 0; + SWXSwath[sID].fid = 0; + + + /* Free Region Pointers */ + /* -------------------- */ + for (k = 0; k < NSWATHREGN; k++) + { + if (SWXRegion[k] != 0 && + SWXRegion[k]->swathID == swathID) + { + for (i = 0; i < 8; i++) + { + if (SWXRegion[k]->DimNamePtr[i] != 0) + { + free(SWXRegion[k]->DimNamePtr[i]); + } + } + + free(SWXRegion[k]); + SWXRegion[k] = 0; + } + } + + } + return (status); +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWclose | +| | +| DESCRIPTION: Closes HDF-EOS file | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| fid int32 File ID | +| | +| OUTPUTS: | +| None | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Jun 96 Joel Gales Original Programmer | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWclose(int32 fid) + +{ + intn status = 0; /* routine return status variable */ + + /* Call EHclose to perform file close */ + /* ---------------------------------- */ + status = EHclose(fid); + + return (status); +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWupdatescene | +| | +| DESCRIPTION: Updates the StartRegion and StopRegion values | +| for a specified region. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| status intn return status (0) SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 Swath structure ID | +| regionID int32 Region ID | +| | +| NOTES: | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Nov 98 Xinmin Hua Original developing | +| Aug 99 Abe Taaheri Added code to exclude regions that have the same | +| start and stop. | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +intn +SWupdatescene(int32 swathID, int32 regionID) +{ + intn k; /* Loop index */ + int32 status; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath Vgroup ID */ + + int32 startReg; /* Indexed start region */ + int32 stopReg; /* Indexed stop region */ + int32 index[MAXNREGIONS]; /* to store indicies when stop and + start are different */ + + int32 ind; /* index */ + int32 tempnRegions; /* temp number of regions */ + + /* Check for valid swath ID */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWupdatescene", &fid, &sdInterfaceID, + &swVgrpID); + + + /* Check for valid region ID */ + /* ------------------------- */ + if (status == 0) + { + if (regionID < 0 || regionID >= NSWATHREGN) + { + status = -1; + HEpush(DFE_RANGE, "SWupdatescene", __FILE__, __LINE__); + HEreport("Invalid Region id: %d.\n", regionID); + } + } + + /* Check for active region ID */ + /* -------------------------- */ + if (status == 0) + { + if (SWXRegion[regionID] == 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWupdatescene", __FILE__, __LINE__); + HEreport("Inactive Region ID: %d.\n", regionID); + } + } + + if (status == 0) + { + tempnRegions = SWXRegion[regionID]->nRegions; + ind =0; + + for (k = 0; k < SWXRegion[regionID]->nRegions; k++) + { + startReg = SWXRegion[regionID]->StartRegion[k]; + stopReg = SWXRegion[regionID]->StopRegion[k]; + if(startReg == stopReg) + { + /* reduce number of regions by 1, if tempnRegions is 0 issue + error and break from loop*/ + tempnRegions -= 1; + + if(tempnRegions == 0) + { + /* first free allocated memory for SWXRegion[regionID] + in the function SWdefboxregion and make regionID + inactive */ + free(SWXRegion[regionID]); + SWXRegion[regionID] = 0; + status = -1; + HEpush(DFE_GENAPP, "SWupdatescene", __FILE__, __LINE__); + HEreport("Inactive Region ID: %d.\n", regionID); + break; + } + } + else + { + /* store index number of regions that have different start and + stop */ + index[ind] = k; + ind += 1; + } + } + if (status != 0) + { + return (status); + } + else + { + SWXRegion[regionID]->nRegions = tempnRegions; + } + /* keep starts and stops that are different in the structure */ + for (k = 0; k < SWXRegion[regionID]->nRegions; k++) + { + SWXRegion[regionID]->StartRegion[k] = + SWXRegion[regionID]->StartRegion[index[k]]; + SWXRegion[regionID]->StopRegion[k] = + SWXRegion[regionID]->StopRegion[index[k]]; + } + + } + + + if (status == 0) + { + + for (k = 0; k < SWXRegion[regionID]->nRegions; k++) + { + + startReg = SWXRegion[regionID]->StartRegion[k]; + stopReg = SWXRegion[regionID]->StopRegion[k]; + + if(startReg % 2 == 1) { + + SWXRegion[regionID]->StartRegion[k] = ++startReg; + + } + if(stopReg % 2 == 0) { + + SWXRegion[regionID]->StopRegion[k] = --stopReg; + + } + + } + + } + + return(status); + +} + +/*----------------------------------------------------------------------------| +| BEGIN_PROLOG | +| | +| FUNCTION: SWupdateidxmap | +| | +| DESCRIPTION: Updates the map index for a specified region. | +| | +| | +| Return Value Type Units Description | +| ============ ====== ========= ===================================== | +| nout int32 return Number of elements in output | +| index array if SUCCEED, (-1) FAIL | +| | +| INPUTS: | +| swathID int32 Swath structure ID | +| regionID int32 Region ID | +| indexin int32 array of index values | +| | +| OUTPUTS: | +| indexout int32 array of index values | +| indicies int32 array of start and stop in region | +| | +| NOTES: | +| | +| | +| Date Programmer Description | +| ====== ============ ================================================= | +| Aug 97 Abe Taaheri Original Programmer | +| AUG 97 Abe Taaheri Add support for index mapping | +| Sep 99 DaW Add support for Floating Scene Subsetting Landsat 7 | +| | +| END_PROLOG | +-----------------------------------------------------------------------------*/ +int32 +SWupdateidxmap(int32 swathID, int32 regionID, int32 indexin[], int32 indexout[], int32 indicies[]) +{ + intn i; /* Loop index */ + intn j; /* Loop index */ + intn k; /* Loop index */ + int32 status; /* routine return status variable */ + int32 land_status = 3; /* routine return status variable */ + + int32 fid; /* HDF-EOS file ID */ + int32 sdInterfaceID; /* HDF SDS interface ID */ + int32 swVgrpID; /* Swath Vgroup ID */ + + int32 numtype = 0; /* Used for L7 float scene sub. */ + int32 count = 0; /* Used for L7 float scene sub. */ + + int32 startReg = 0; /* Indexed start region */ + int32 stopReg = 0; /* Indexed stop region */ + int32 nout=-1; /* Number of elements in output index array */ + int32 indexoffset = 0; + uint8 scene_cnt = 0; /* Used for L7 float scene sub. */ + uint8 detect_cnt = 0; /* Used to convert scan to scanline */ + intn gtflag = 0; + intn ngtflag = 0; + int32 *buffer1 = (int32 *)NULL; + int32 *buffer2 = (int32 *)NULL; + + /* Check for valid swath ID */ + /* ------------------------ */ + status = SWchkswid(swathID, "SWupdateidxmap", &fid, &sdInterfaceID, + &swVgrpID); + + + /* Check for valid region ID */ + /* ------------------------- */ + if (status == 0) + { + if (regionID < 0 || regionID >= NSWATHREGN) + { + status = -1; + HEpush(DFE_RANGE, "SWupdateidxmap", __FILE__, __LINE__); + HEreport("Invalid Region id: %d.\n", regionID); + } + } + + /* Check for active region ID */ + /* -------------------------- */ + if (status == 0) + { + if (SWXRegion[regionID] == 0) + { + status = -1; + HEpush(DFE_GENAPP, "SWextractregion", __FILE__, __LINE__); + HEreport("Inactive Region ID: %d.\n", regionID); + } + } + + if (status == 0) + { + /* Loop through all regions */ + /* ------------------------ */ + for (k = 0; k < SWXRegion[regionID]->nRegions; k++) + { + + /* fix overlap index mapping problem for Landsat 7 */ + + startReg = SWXRegion[regionID]->StartRegion[k]; + stopReg = SWXRegion[regionID]->StopRegion[k]; + + + if(SWXRegion[regionID]->scanflag == 1) + { + indicies[0] = -1; + indicies[1] = -1; + j = 0; + /* This code checks for the attribute detector_count */ + /* which is found in Landsat 7 files. It is used */ + /* for some of the loops. */ + /* ================================================= */ + land_status = SWattrinfo(swathID, "scene_count", &numtype, &count); + if (land_status == 0) + { + land_status = SWreadattr(swathID, "scene_count", &scene_cnt); + land_status = SWreadattr(swathID, "detector_count", &detect_cnt); + } + + + /* calculate the offsets first */ + buffer1 = (int32 *)calloc(74, sizeof(int32)); + buffer2 = (int32 *)calloc(74, sizeof(int32)); + + status = SWidxmapinfo(swathID,"GeoTrack", + "ScanLineTrack", (int32*)buffer1); + status = SWidxmapinfo(swathID,"UpperTrack", + "ScanLineTrack", (int32*)buffer2); + + indexoffset = buffer2[0] - buffer1[0]; + free(buffer1); + free(buffer2); + + if(SWXRegion[regionID]->band8flag == -1) + { + for(i=0; i= startReg) + if(indicies[0] == -1) + indicies[0] = j; + if(indexin[j] <= stopReg && indexin[j+1] >= stopReg) + indicies[1] = j + 1; + j = j + 2; + if(indexin[j] == 0 || indexin[j+1] == 0) + i = scene_cnt; + } + if(indicies[0] == -1) + { + if(startReg <= indexin[0]) + indicies[0] = 0; + } + if(indicies[0] == -1) + { + j = 0; + for(i=0; i= startReg) + if(indicies[0] == -1) + indicies[0] = j; + j = j + 1; + if(indexin[j] == 0 || indexin[j+1] == 0) + i = scene_cnt; + } + } + if(indicies[1] == -1) + { + j = 0; + for(i=0; i= stopReg) + if(indicies[1] == -1) + indicies[1] = j + 1; + j = j + 1; + if(indexin[j] == 0 || indexin[j+1] == 0) + i = scene_cnt; + } + } + if(indicies[1] == -1) + if(stopReg > indexin[scene_cnt - 1]) + indicies[1] = scene_cnt - 1; + } + + /* This section of code handles exceptions in Landsat 7 */ + /* data. The Band 8 data - multiple files, data gaps */ + /* ===================================================== */ + if(SWXRegion[regionID]->band8flag == 1 || + SWXRegion[regionID]->band8flag == 2 || + SWXRegion[regionID]->band8flag == 3) + { + j = 0; + for(i=0; i= (indexin[j] + indexoffset - detect_cnt) && + startReg <= (indexin[j+1] + indexoffset - detect_cnt) ) + if(indicies[0] == -1) + indicies[0] = j; + if( stopReg >= (indexin[j] + indexoffset - detect_cnt ) && + stopReg <= (indexin[j+1] + indexoffset - detect_cnt ) ) + indicies[1] = j + 1; + j = j + 2; + if(indexin[j] == 0 || indexin[j+1] == 0) + i = scene_cnt; + } + if(SWXRegion[regionID]->band8flag == 1) + { + if(indicies[1] == -1) + if(stopReg > (indexin[j - 1] + indexoffset - detect_cnt)) + indicies[1] = j - 1; + } + if(SWXRegion[regionID]->band8flag == 2 || + SWXRegion[regionID]->band8flag == 3) + { + + if(startReg >= (indexin[j - 1] + indexoffset - detect_cnt)) + { + indicies[0] = -1; + indicies[1] = -1; + /* status = SWfieldinfo(swathID, "scan_no", &rank, dims2, &nt, dimlist); + buffer = (uint16 *)calloc(dims2[0], sizeof(uint16)); + status = SWreadfield(swathID,"scan_no", NULL, NULL, NULL, buffer); + indexoffset = buffer[0] * detect_cnt; + free(buffer); + startReg = startReg - (indexoffset - detect_cnt); + stopReg = stopReg - (indexoffset - 1); */ + j = 0; + for(i=0; i= (indexin[j] + indexoffset - detect_cnt) && + startReg <= (indexin[j+1] + indexoffset - detect_cnt) ) + if(indicies[0] == -1) + indicies[0] = j; + if( stopReg >= (indexin[j] + indexoffset - detect_cnt ) && + stopReg <= (indexin[j+1] + indexoffset - detect_cnt ) ) + indicies[1] = j + 1; + j = j + 2; + if(indexin[j] == 0 || indexin[j+1] == 0) + i = scene_cnt; + } + } + + if(indicies[0] == -1) + { + j = 0; + for(i=0; i= (indexin[j] + indexoffset - detect_cnt) && + startReg <= (indexin[j+1] + indexoffset - detect_cnt) ) + if(indicies[0] == -1) + indicies[0] = j; + + j = j + 2; + if(indexin[j] == 0 || indexin[j+1] == 0) + i = scene_cnt; + } + } + if(indicies[1] == -1) + if(stopReg > (indexin[j - 1] + indexoffset - detect_cnt) ) + indicies[1] = j - 1; + } + if(indicies[1] == -1) + { + j = 0; + for(i=0; i= (indexin[j] + indexoffset - detect_cnt ) && + stopReg <= (indexin[j+1] + indexoffset - detect_cnt ) ) + indicies[1] = j; + j = j + 2; + if(indexin[j] == 0 || indexin[j+1] == 0) + i = scene_cnt; + } + } + } + + if(ngtflag == 1) + { + for(i=0; i= indexin[j] && startReg <= indexin[j+1]) + if(indicies[0] == -1) + indicies[0] = j; + if( stopReg >= indexin[j] && stopReg <= indexin[j+1]) + indicies[1] = j + 1; + j = j + 2; + if(indexin[j] == 0 || indexin[j+1] == 0) + i = scene_cnt; + } + if(SWXRegion[regionID]->band8flag == 2) + { + if(startReg >= indexin[j] ) + { + if(indicies[0] == -1) + indicies[0] = j; + if(indicies[1] == -1) + indicies[1] = j; + } + if(indicies[0] == -1) + if(startReg <= indexin[0]) + indicies[0] = 0; + if(indicies[1] == -1) + if(stopReg > indexin[j]) + indicies[1] = j; + } + if(indicies[0] == -1) + { + j = 0; + for(i=0; i= indexin[j] && startReg <= indexin[j+1]) + indicies[0] = j; + j = j + 2; + if(indexin[j] == 0 || indexin[j+1] == 0) + i = scene_cnt; + } + } + if(indicies[1] == -1) + { + j = 0; + for(i=0; i= indexin[j] && stopReg <= indexin[j+1]) + indicies[1] = j; + j = j + 2; + if(indexin[j] == 0 || indexin[j+1] == 0) + i = scene_cnt; + } + } + if(indicies[1] == -1) + { + if(stopReg > indexin[j]) + indicies[1] = j; + } + } + if(indicies[0] == -1) + { + if(startReg <= (indexin[0]+ indexoffset - detect_cnt) ) + indicies[0] = 0; + if(indicies[1] == -1) + if(stopReg > (indexin[j] + indexoffset - detect_cnt)) + indicies[1] = j; + } + } + if (indicies[1] == -1) + { + if(SWXRegion[regionID]->band8flag == 2 || + SWXRegion[regionID]->band8flag == 3) + { + if(stopReg < (indexin[0] + indexoffset - detect_cnt)) + { + /*status = SWfieldinfo(swathID, "scan_no", &rank, dims2, &nt, dimlist); + buffer = (uint16 *)calloc(dims2[0], sizeof(uint16)); + status = SWreadfield(swathID,"scan_no", NULL, NULL, NULL, buffer); + indexoffset = buffer[0] * detect_cnt; + free(buffer); + startReg = startReg + (indexoffset - detect_cnt); + stopReg = stopReg + (indexoffset - 1); */ + if(stopReg >= (indexin[scene_cnt - 1] + indexoffset - detect_cnt)) + { + indicies[1] = scene_cnt - 1; + } + else + { + j = 0; + for(i=0;i= (indexin[j] + indexoffset - detect_cnt ) && + stopReg <= (indexin[j+1] + indexoffset - detect_cnt ) ) + indicies[1] = j; + j = j + 2; + if(indexin[j] == 0 || indexin[j+1] == 0) + i = scene_cnt; + } + } + } + + if(startReg > (indexin[j - 1] + indexoffset - detect_cnt )) + { + indicies[0] = -1; + indicies[1] = -1; + /*status = SWfieldinfo(swathID, "scan_no", &rank, dims2, &nt, dimlist); + buffer = (uint16 *)calloc(dims2[0], sizeof(uint16)); + status = SWreadfield(swathID,"scan_no", NULL, NULL, NULL, buffer); + indexoffset = buffer[0] * detect_cnt; + free(buffer); + startReg = startReg - (indexoffset - detect_cnt); + stopReg = stopReg - (indexoffset - 1);*/ + j = 0; + for(i=0; i= (indexin[j] + indexoffset - detect_cnt) && + startReg <= (indexin[j+1] + indexoffset - detect_cnt) ) + if(indicies[0] == -1) + indicies[0] = j; + if( stopReg >= (indexin[j] + indexoffset - detect_cnt ) && + stopReg <= (indexin[j+1] + indexoffset - detect_cnt ) ) + indicies[1] = j + 1; + j = j + 2; + if(indexin[j] == 0 || indexin[j+1] == 0) + i = scene_cnt; + } + if(indicies[0] == -1) + if(startReg < (indexin[0] + indexoffset - detect_cnt)) + indicies[0] = 0; + if(indicies[1] == -1) + if(stopReg > (indexin[j - 1] + indexoffset - detect_cnt)) + indicies[1] = j - 1; + } + } + } + } /* end of if for floating scene update */ + else + { + /* If start of region is odd then increment */ + /* ---------------------------------------- */ + if (startReg % 2 == 1) + { + startReg++; + } + + /* If end of region is even then decrement */ + /* --------------------------------------- */ + if (stopReg % 2 == 0) + { + stopReg--; + } + + indicies[0]=startReg; + indicies[1]=stopReg; + } + } + + if (indexout != NULL) + { + if(SWXRegion[regionID]->scanflag == 1) + { + nout = (indicies[1] - indicies[0] + 1); + j = 0; + if (nout == 1) + indexout[0] = indexin[indicies[0]]; + for(i=0; i +#define BCEA_COLS25 1383 /* total number of columns for EASE grid */ +#define BCEA_ROWS25 586 /* total number of rows for EASE grid */ +#define BCEA_CELL_M 25067.525 /* Cell size for EASE grid */ +#define BCEA_RE_M 6371228.0 /* Earth radius used in GCTP projection tools for + Behrmann Cylindrical Equal Area projection */ +#define DEFAULT_BCEA_LTRUESCALE 30.00 /*Latitude of true scale in DMS */ +#define BCEA_COS_PHI1 cos(DEFAULT_BCEA_LTRUESCALE *3.141592653589793238 /180.0) +#define PI 3.141592653589793238 +#define EASE_GRID_DEFAULT_UPLEFT_LON -180.0 +#define EASE_GRID_DEFAULT_UPLEFT_LAT 86.72 +#define EASE_GRID_DEFAULT_LOWRGT_LON 180.0 +#define EASE_GRID_DEFAULT_LOWRGT_LAT -86.72 + +#endif /* #ifndef EASE_H_ */ diff --git a/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/gctp_wrap.c b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/gctp_wrap.c new file mode 100644 index 000000000..97d2d2e9a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/gctp_wrap.c @@ -0,0 +1,178 @@ +/****************************************************************************** + * $Id: gctp_wrap.c 22751 2011-07-18 15:25:05Z winkey $ + * + * Project: Hierarchical Data Format Release 4 (HDF4) + * Purpose: This is the wrapper code to use OGR Coordinate Transformation + * services instead of GCTP library + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2004, Andrey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_srs_api.h" +#include + +#include "mfhdf.h" + +#include + +#ifndef PI +#ifndef M_PI +#define PI (3.141592653589793238) +#else +#define PI (M_PI) +#endif +#endif + +#define DEG (180.0 / PI) +#define RAD (PI / 180.0) + +/***** static vars to store the transformers in *****/ +/***** this is not thread safe *****/ + +static OGRCoordinateTransformationH hForCT, hInvCT; + +/****************************************************************************** + function for forward gctp transformation + + gctp expects Longitude and Latitude values to be in radians +******************************************************************************/ + +int32 osr_for( +double lon, /* (I) Longitude */ +double lat, /* (I) Latitude */ +double *x, /* (O) X projection coordinate */ +double *y) /* (O) Y projection coordinate */ +{ + + double dfX, dfY, dfZ = 0.0; + + dfX = lon * DEG; + dfY = lat * DEG; + + OCTTransform( hForCT, 1, &dfX, &dfY, &dfZ); + + *x = dfX; + *y = dfY; + + return 0; +} + +/****************************************************************************** + function to init a forward gctp transformer +******************************************************************************/ + +void for_init( +int32 outsys, /* output system code */ +int32 outzone, /* output zone number */ +float64 *outparm, /* output array of projection parameters */ +int32 outdatum, /* output datum */ +char *fn27, /* NAD 1927 parameter file */ +char *fn83, /* NAD 1983 parameter file */ +int32 *iflg, /* status flag */ +int32 (*for_trans[])(double, double, double *, double *)) + /* forward function pointer */ +{ + OGRSpatialReferenceH hOutSourceSRS, hLatLong = NULL; + + *iflg = 0; + + hOutSourceSRS = OSRNewSpatialReference( NULL ); + OSRImportFromUSGS( hOutSourceSRS, outsys, outzone, outparm, outdatum ); + hLatLong = OSRNewSpatialReference ( SRS_WKT_WGS84 ); + + hForCT = OCTNewCoordinateTransformation( hLatLong, hOutSourceSRS ); + + OSRDestroySpatialReference( hOutSourceSRS ); + OSRDestroySpatialReference( hLatLong ); + + for_trans[outsys] = osr_for; +} + +/****************************************************************************** + function for inverse gctp transformation + + gctp returns Longitude and Latitude values in radians +******************************************************************************/ + +int32 osr_inv( +double x, /* (I) X projection coordinate */ +double y, /* (I) Y projection coordinate */ +double *lon, /* (O) Longitude */ +double *lat) /* (O) Latitude */ +{ + + double dfX, dfY, dfZ = 0.0; + + dfX = x; + dfY = y; + + OCTTransform( hInvCT, 1, &dfX, &dfY, &dfZ ); + + *lon = dfX * RAD; + *lat = dfY * RAD; + + return 0; +} + +/****************************************************************************** + function to init a inverse gctp transformer +******************************************************************************/ + +void inv_init( +int32 insys, /* input system code */ +int32 inzone, /* input zone number */ +float64 *inparm, /* input array of projection parameters */ +int32 indatum, /* input datum code */ +char *fn27, /* NAD 1927 parameter file */ +char *fn83, /* NAD 1983 parameter file */ +int32 *iflg, /* status flag */ +int32 (*inv_trans[])(double, double, double*, double*)) + /* inverse function pointer */ +{ + + OGRSpatialReferenceH hInSourceSRS, hLatLong = NULL; + *iflg = 0; + + hInSourceSRS = OSRNewSpatialReference( NULL ); + OSRImportFromUSGS( hInSourceSRS, insys, inzone, inparm, indatum ); + + hLatLong = OSRNewSpatialReference ( SRS_WKT_WGS84 ); + + hInvCT = OCTNewCoordinateTransformation( hInSourceSRS, hLatLong ); + + OSRDestroySpatialReference( hInSourceSRS ); + OSRDestroySpatialReference( hLatLong ); + + inv_trans[insys] = osr_inv; +} + +/****************************************************************************** + function to cleanup the transformers + + note: gctp does not have a function that does this +******************************************************************************/ + +void gctp_destroy(void) { + OCTDestroyCoordinateTransformation ( hForCT ); + OCTDestroyCoordinateTransformation ( hInvCT ); +} diff --git a/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/makefile.vc b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/makefile.vc new file mode 100644 index 000000000..5dfe538ab --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/hdf-eos/makefile.vc @@ -0,0 +1,20 @@ + +OBJ = \ + SWapi.obj GDapi.obj EHapi.obj gctp_wrap.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I$(HDF4_DIR)\include -I.\ -I..\ -DFRMT_hdf4 -DWIN32 + +!IF "$(HDF4_HAS_MAXOPENFILES)" == "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DHDF4_HAS_MAXOPENFILES +!ENDIF + +default: $(OBJ) + xcopy /D /Y *.obj ..\..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/hdf4/hdf4compat.h b/bazaar/plugin/gdal/frmts/hdf4/hdf4compat.h new file mode 100644 index 000000000..208832f5c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/hdf4compat.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * $Id: hdf4compat.h 15691 2008-11-07 09:54:58Z dron $ + * + * Project: Hierarchical Data Format Release 4 (HDF4) + * Purpose: Header file for HDF4 compatibility + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2002, Andrey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _HDF4COMPAT_H_INCLUDED_ +#define _HDF4COMPAT_H_INCLUDED_ + +/* New versions of the HDF4 library define H4_xxx instead of xxx to + avoid potential conflicts with NetCDF-3 library. The following + tests enable compiling with older versions of HDF4 +*/ +#ifndef H4_MAX_VAR_DIMS +#define H4_MAX_VAR_DIMS MAX_VAR_DIMS +#endif +#ifndef H4_MAX_NC_DIMS +#define H4_MAX_NC_DIMS MAX_NC_DIMS +#endif +#ifndef H4_MAX_NC_NAME +#define H4_MAX_NC_NAME MAX_NC_NAME +#endif + +#endif diff --git a/bazaar/plugin/gdal/frmts/hdf4/hdf4dataset.cpp b/bazaar/plugin/gdal/frmts/hdf4/hdf4dataset.cpp new file mode 100644 index 000000000..bb4156a67 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/hdf4dataset.cpp @@ -0,0 +1,1275 @@ +/****************************************************************************** + * $Id: hdf4dataset.cpp 29208 2015-05-19 13:54:59Z rouault $ + * + * Project: Hierarchical Data Format Release 4 (HDF4) + * Purpose: HDF4 Datasets. Open HDF4 file, fetch metadata and list of + * subdatasets. + * This driver initially based on code supplied by Markus Neteler + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2002, Andrey Kiselev + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "hdf.h" +#include "mfhdf.h" + +#include "HdfEosDef.h" + +#include "gdal_priv.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" + +#include "hdf4compat.h" +#include "hdf4dataset.h" + +CPL_CVSID("$Id: hdf4dataset.cpp 29208 2015-05-19 13:54:59Z rouault $"); + +CPL_C_START +void GDALRegister_HDF4(void); +CPL_C_END + +extern const char *pszGDALSignature; + +CPLMutex *hHDF4Mutex = NULL; + +/************************************************************************/ +/* ==================================================================== */ +/* HDF4Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* HDF4Dataset() */ +/************************************************************************/ + +HDF4Dataset::HDF4Dataset() + +{ + hSD = 0; + hGR = 0; + nImages = 0; + iSubdatasetType = H4ST_UNKNOWN; + pszSubdatasetType = NULL; + papszGlobalMetadata = NULL; + papszSubDatasets = NULL; + bIsHDFEOS = 0; +} + +/************************************************************************/ +/* ~HDF4Dataset() */ +/************************************************************************/ + +HDF4Dataset::~HDF4Dataset() + +{ + CPLMutexHolderD(&hHDF4Mutex); + + if ( hSD ) + SDend( hSD ); + if ( hGR ) + GRend( hGR ); + if ( papszSubDatasets ) + CSLDestroy( papszSubDatasets ); + if ( papszGlobalMetadata ) + CSLDestroy( papszGlobalMetadata ); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **HDF4Dataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "SUBDATASETS", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **HDF4Dataset::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain != NULL && EQUALN( pszDomain, "SUBDATASETS", 11 ) ) + return papszSubDatasets; + else + return GDALDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* SPrintArray() */ +/* Prints numerical arrays in string buffer. */ +/* This function takes pfaDataArray as a pointer to printed array, */ +/* nValues as a number of values to print and pszDelimiter as a */ +/* field delimiting strings. */ +/* Pointer to filled buffer will be returned. */ +/************************************************************************/ + +char *SPrintArray( GDALDataType eDataType, const void *paDataArray, + int nValues, const char *pszDelimiter ) +{ + char *pszString, *pszField; + int i, iFieldSize, iStringSize; + + iFieldSize = 32 + strlen( pszDelimiter ); + pszField = (char *)CPLMalloc( iFieldSize + 1 ); + iStringSize = nValues * iFieldSize + 1; + pszString = (char *)CPLMalloc( iStringSize ); + memset( pszString, 0, iStringSize ); + for ( i = 0; i < nValues; i++ ) + { + switch ( eDataType ) + { + case GDT_Byte: + sprintf( pszField, "%d%s", ((GByte *)paDataArray)[i], + (i < nValues - 1)?pszDelimiter:"" ); + break; + case GDT_UInt16: + sprintf( pszField, "%u%s", ((GUInt16 *)paDataArray)[i], + (i < nValues - 1)?pszDelimiter:"" ); + break; + case GDT_Int16: + default: + sprintf( pszField, "%d%s", ((GInt16 *)paDataArray)[i], + (i < nValues - 1)?pszDelimiter:"" ); + break; + case GDT_UInt32: + sprintf( pszField, "%u%s", ((GUInt32 *)paDataArray)[i], + (i < nValues - 1)?pszDelimiter:"" ); + break; + case GDT_Int32: + sprintf( pszField, "%d%s", ((GInt32 *)paDataArray)[i], + (i < nValues - 1)?pszDelimiter:"" ); + break; + case GDT_Float32: + CPLsprintf( pszField, "%.10g%s", ((float *)paDataArray)[i], + (i < nValues - 1)?pszDelimiter:"" ); + break; + case GDT_Float64: + CPLsprintf( pszField, "%.15g%s", ((double *)paDataArray)[i], + (i < nValues - 1)?pszDelimiter:"" ); + break; + } + strcat( pszString, pszField ); + } + + CPLFree( pszField ); + return pszString; +} + +/************************************************************************/ +/* Translate HDF4 data type into GDAL data type */ +/************************************************************************/ +GDALDataType HDF4Dataset::GetDataType( int32 iNumType ) +{ + switch (iNumType) + { + case DFNT_CHAR8: // The same as DFNT_CHAR + case DFNT_UCHAR8: // The same as DFNT_UCHAR + case DFNT_INT8: + case DFNT_UINT8: + return GDT_Byte; + case DFNT_INT16: + return GDT_Int16; + case DFNT_UINT16: + return GDT_UInt16; + case DFNT_INT32: + return GDT_Int32; + case DFNT_UINT32: + return GDT_UInt32; + case DFNT_INT64: + return GDT_Unknown; + case DFNT_UINT64: + return GDT_Unknown; + case DFNT_FLOAT32: + return GDT_Float32; + case DFNT_FLOAT64: + return GDT_Float64; + default: + return GDT_Unknown; + } +} + +/************************************************************************/ +/* Return the human readable name of data type */ +/************************************************************************/ + +const char *HDF4Dataset::GetDataTypeName( int32 iNumType ) +{ + switch (iNumType) + { + case DFNT_CHAR8: // The same as DFNT_CHAR + return "8-bit character"; + case DFNT_UCHAR8: // The same as DFNT_UCHAR + return "8-bit unsigned character"; + case DFNT_INT8: + return "8-bit integer"; + case DFNT_UINT8: + return "8-bit unsigned integer"; + case DFNT_INT16: + return "16-bit integer"; + case DFNT_UINT16: + return "16-bit unsigned integer"; + case DFNT_INT32: + return "32-bit integer"; + case DFNT_UINT32: + return "32-bit unsigned integer"; + case DFNT_INT64: + return "64-bit integer"; + case DFNT_UINT64: + return "64-bit unsigned integer"; + case DFNT_FLOAT32: + return "32-bit floating-point"; + case DFNT_FLOAT64: + return "64-bit floating-point"; + default: + return "unknown type"; + } +} + +/************************************************************************/ +/* Return the size of data type in bytes */ +/************************************************************************/ + +int HDF4Dataset::GetDataTypeSize( int32 iNumType ) +{ + switch (iNumType) + { + case DFNT_CHAR8: // The same as DFNT_CHAR + case DFNT_UCHAR8: // The same as DFNT_UCHAR + case DFNT_INT8: + case DFNT_UINT8: + return 1; + case DFNT_INT16: + case DFNT_UINT16: + return 2; + case DFNT_INT32: + case DFNT_UINT32: + case DFNT_FLOAT32: + return 4; + case DFNT_INT64: + case DFNT_UINT64: + case DFNT_FLOAT64: + return 8; + default: + return 0; + } +} + +/************************************************************************/ +/* Convert value stored in the input buffer to double value. */ +/************************************************************************/ + +double HDF4Dataset::AnyTypeToDouble( int32 iNumType, void *pData ) +{ + switch ( iNumType ) + { + case DFNT_INT8: + return (double)*(char *)pData; + case DFNT_UINT8: + return (double)*(unsigned char *)pData; + case DFNT_INT16: + return (double)*(short *)pData; + case DFNT_UINT16: + return (double)*(unsigned short *)pData; + case DFNT_INT32: + return (double)*(int *)pData; + case DFNT_UINT32: + return (double)*(unsigned int *)pData; + case DFNT_INT64: + return (double)*(char *)pData; // highly suspicious ! Should be GIntBig. But cannot verify + case DFNT_UINT64: + return (double)*(GIntBig *)pData; + case DFNT_FLOAT32: + return (double)*(float *)pData; + case DFNT_FLOAT64: + return (double)*(double *)pData; + default: + return 0.0; + } +} + +/************************************************************************/ +/* Tokenize HDF-EOS attributes. */ +/************************************************************************/ + +char **HDF4Dataset::HDF4EOSTokenizeAttrs( const char * pszString ) + +{ + const char *pszDelimiters = " \t\n\r"; + char **papszRetList = NULL; + char *pszToken; + int nTokenMax, nTokenLen; + + pszToken = (char *) CPLCalloc( 10, 1 ); + nTokenMax = 10; + + while( pszString != NULL && *pszString != '\0' ) + { + int bInString = FALSE, bInBracket = FALSE; + + nTokenLen = 0; + + // Try to find the next delimeter, marking end of token + for( ; *pszString != '\0'; pszString++ ) + { + + // End if this is a delimeter skip it and break. + if ( !bInBracket && !bInString + && strchr(pszDelimiters, *pszString) != NULL ) + { + pszString++; + break; + } + + // Sometimes in bracketed tokens we may found a sort of + // paragraph formatting. We will remove unneeded spaces and new + // lines. + if ( bInBracket ) + if ( strchr("\r\n", *pszString) != NULL + || ( *pszString == ' ' + && strchr(" \r\n", *(pszString - 1)) != NULL ) ) + continue; + + if ( *pszString == '"' ) + { + if ( bInString ) + { + bInString = FALSE; + continue; + } + else + { + bInString = TRUE; + continue; + } + } + else if ( *pszString == '(' ) + { + bInBracket = TRUE; + continue; + } + else if ( *pszString == ')' ) + { + bInBracket = FALSE; + continue; + } + + if( nTokenLen >= nTokenMax - 2 ) + { + nTokenMax = nTokenMax * 2 + 10; + pszToken = (char *) CPLRealloc( pszToken, nTokenMax ); + } + + pszToken[nTokenLen] = *pszString; + nTokenLen++; + } + + pszToken[nTokenLen] = '\0'; + + if( pszToken[0] != '\0' ) + { + papszRetList = CSLAddString( papszRetList, pszToken ); + } + + // If the last token is an empty token, then we have to catch + // it now, otherwise we won't reenter the loop and it will be lost. + if ( *pszString == '\0' && strchr(pszDelimiters, *(pszString-1)) ) + { + papszRetList = CSLAddString( papszRetList, "" ); + } + } + + if( papszRetList == NULL ) + papszRetList = (char **) CPLCalloc( sizeof(char *), 1 ); + + CPLFree( pszToken ); + + return papszRetList; +} + +/************************************************************************/ +/* Find object name, class value in HDF-EOS attributes. */ +/* Function returns pointer to the string in list next behind */ +/* recognized object. */ +/************************************************************************/ + +char **HDF4Dataset::HDF4EOSGetObject( char **papszAttrList, + char **ppszAttrName, + char **ppszAttrClass, + char **ppszAttrValue ) +{ + int iCount, i, j; + *ppszAttrName = NULL; + *ppszAttrClass = NULL; + *ppszAttrValue = NULL; + + iCount = CSLCount( papszAttrList ); + for ( i = 0; i < iCount - 2; i++ ) + { + if ( EQUAL( papszAttrList[i], "OBJECT" ) ) + { + i += 2; + for ( j = 1; i + j < iCount - 2; j++ ) + { + if ( EQUAL( papszAttrList[i + j], "END_OBJECT" ) || + EQUAL( papszAttrList[i + j], "OBJECT" ) ) + return &papszAttrList[i + j]; + else if ( EQUAL( papszAttrList[i + j], "CLASS" ) ) + { + *ppszAttrClass = papszAttrList[i + j + 2]; + continue; + } + else if ( EQUAL( papszAttrList[i + j], "VALUE" ) ) + { + *ppszAttrName = papszAttrList[i]; + *ppszAttrValue = papszAttrList[i + j + 2]; + continue; + } + } + } + } + + return NULL; +} + +/************************************************************************/ +/* Translate HDF4-EOS attributes in GDAL metadata items */ +/************************************************************************/ + +char** HDF4Dataset::TranslateHDF4EOSAttributes( int32 iHandle, + int32 iAttribute, int32 nValues, char **papszMetadata ) +{ + char *pszData; + + pszData = (char *)CPLMalloc( (nValues + 1) * sizeof(char) ); + pszData[nValues] = '\0'; + SDreadattr( iHandle, iAttribute, pszData ); + // HDF4-EOS attributes has followed structure: + // + // GROUP = + // GROUPTYPE = + // + // GROUP = + // + // OBJECT = + // CLASS = + // NUM_VAL = + // VALUE = or + // END_OBJECT = + // + // ....... + // ....... + // ....... + // + // END_GROUP = + // + // ....... + // ....... + // ....... + // + // END_GROUP = + // END + // + // Used data types: + // --- unquoted character strings + // --- quoted character strings + // --- numerical value + // If NUM_VAL != 1 then values combined in lists: + // (,,...) + // or + // (,,...) + // + // Records within objects could come in any order, objects could contain + // other objects (and lack VALUE record), groups could contain other groups + // and objects. Names of groups and objects are not unique and may repeat. + // Objects may contains other types of records. + // + // We are interested in OBJECTS structures only. To avoid multiple items + // with the same name, names will be suffixed with the class values, e.g. + // + // OBJECT = PARAMETERNAME + // CLASS = "9" + // NUM_VAL = 1 + // VALUE = "Spectral IR Surf Bidirect Reflectivity" + // END_OBJECT = PARAMETERNAME + // + // will be translated into metadata record: + // + // PARAMETERNAME.9 = "Spectral IR Surf Bidirect Reflectivity" + + char *pszAttrName, *pszAttrClass, *pszAttrValue; + char *pszAddAttrName = NULL; + char **papszAttrList, **papszAttrs; + + papszAttrList = HDF4EOSTokenizeAttrs( pszData ); + papszAttrs = papszAttrList; + while ( papszAttrs ) + { + papszAttrs = HDF4EOSGetObject( papszAttrs, &pszAttrName, + &pszAttrClass, &pszAttrValue ); + if ( pszAttrName && pszAttrValue ) + { + // Now we should recognize special type of HDF EOS metastructures: + // ADDITIONALATTRIBUTENAME = + // PARAMETERVALUE = + if ( EQUAL( pszAttrName, "ADDITIONALATTRIBUTENAME" ) ) + pszAddAttrName = pszAttrValue; + else if ( pszAddAttrName && EQUAL( pszAttrName, "PARAMETERVALUE" ) ) + { + papszMetadata = + CSLAddNameValue( papszMetadata, pszAddAttrName, pszAttrValue ); + pszAddAttrName = NULL; + } + else + { + // Add class suffix to the key name if applicable + papszMetadata = CSLAddNameValue( papszMetadata, + pszAttrClass ? + CPLSPrintf("%s.%s", pszAttrName, pszAttrClass) : pszAttrName, + pszAttrValue ); + } + } + } + + CSLDestroy( papszAttrList ); + CPLFree( pszData ); + + return papszMetadata; +} + +/************************************************************************/ +/* Translate HDF4 attributes in GDAL metadata items */ +/************************************************************************/ + +char** HDF4Dataset::TranslateHDF4Attributes( int32 iHandle, + int32 iAttribute, char *pszAttrName, int32 iNumType, int32 nValues, + char **papszMetadata ) +{ + void *pData = NULL; + char *pszTemp = NULL; + +/* -------------------------------------------------------------------- */ +/* Allocate a buffer to hold the attribute data. */ +/* -------------------------------------------------------------------- */ + if ( iNumType == DFNT_CHAR8 || iNumType == DFNT_UCHAR8 ) + pData = CPLMalloc( (nValues + 1) * GetDataTypeSize(iNumType) ); + else + pData = CPLMalloc( nValues * GetDataTypeSize(iNumType) ); + +/* -------------------------------------------------------------------- */ +/* Read the attribute data. */ +/* -------------------------------------------------------------------- */ + SDreadattr( iHandle, iAttribute, pData ); + if ( iNumType == DFNT_CHAR8 || iNumType == DFNT_UCHAR8 ) + { + ((char *)pData)[nValues] = '\0'; + papszMetadata = CSLAddNameValue( papszMetadata, pszAttrName, + (const char *) pData ); + } + else + { + pszTemp = SPrintArray( GetDataType(iNumType), pData, nValues, ", " ); + papszMetadata = CSLAddNameValue( papszMetadata, pszAttrName, pszTemp ); + if ( pszTemp ) + CPLFree( pszTemp ); + } + + if ( pData ) + CPLFree( pData ); + + return papszMetadata; +} + +/************************************************************************/ +/* ReadGlobalAttributes() */ +/************************************************************************/ + +CPLErr HDF4Dataset::ReadGlobalAttributes( int32 iHandler ) +{ + int32 iAttribute, nValues, iNumType, nDatasets, nAttributes; + char szAttrName[H4_MAX_NC_NAME]; + +/* -------------------------------------------------------------------- */ +/* Obtain number of SDSs and global attributes in input file. */ +/* -------------------------------------------------------------------- */ + if ( SDfileinfo( iHandler, &nDatasets, &nAttributes ) != 0 ) + return CE_Failure; + + // Loop through the all attributes + for ( iAttribute = 0; iAttribute < nAttributes; iAttribute++ ) + { + // Get information about the attribute. Note that the first + // parameter is an SD interface identifier. + SDattrinfo( iHandler, iAttribute, szAttrName, &iNumType, &nValues ); + + if ( EQUALN( szAttrName, "coremetadata", 12 ) || + EQUALN( szAttrName, "archivemetadata.", 16 ) || + EQUALN( szAttrName, "productmetadata.", 16 ) || + EQUALN( szAttrName, "badpixelinformation", 19 ) || + EQUALN( szAttrName, "product_summary", 15 ) || + EQUALN( szAttrName, "dem_specific", 12 ) || + EQUALN( szAttrName, "bts_specific", 12 ) || + EQUALN( szAttrName, "etse_specific", 13 ) || + EQUALN( szAttrName, "dst_specific", 12 ) || + EQUALN( szAttrName, "acv_specific", 12 ) || + EQUALN( szAttrName, "act_specific", 12 ) || + EQUALN( szAttrName, "etst_specific", 13 ) || + EQUALN( szAttrName, "level_1_carryover", 17 ) ) + { + bIsHDFEOS = 1; + papszGlobalMetadata = TranslateHDF4EOSAttributes( iHandler, + iAttribute, nValues, papszGlobalMetadata ); + } + + // Skip "StructMetadata.N" records. We will fetch information + // from them using HDF-EOS API + else if ( EQUALN( szAttrName, "structmetadata.", 15 ) ) + { + bIsHDFEOS = 1; + continue; + } + + else + { + papszGlobalMetadata = TranslateHDF4Attributes( iHandler, + iAttribute, szAttrName, iNumType, nValues, papszGlobalMetadata ); + } + } + + return CE_None; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int HDF4Dataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 4 ) + return FALSE; + + if( memcmp(poOpenInfo->pabyHeader,"\016\003\023\001",4) != 0 ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *HDF4Dataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int32 i; + + if( !Identify( poOpenInfo ) ) + return NULL; + + CPLMutexHolderD(&hHDF4Mutex); + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ + int32 hHDF4; + + // Attempt to increase maximum number of opened HDF files +#ifdef HDF4_HAS_MAXOPENFILES + intn nCurrMax, nSysLimit; + + if ( SDget_maxopenfiles(&nCurrMax, &nSysLimit) >= 0 + && nCurrMax < nSysLimit ) + { + intn res = SDreset_maxopenfiles( nSysLimit ); + } +#endif /* HDF4_HAS_MAXOPENFILES */ + + hHDF4 = Hopen(poOpenInfo->pszFilename, DFACC_READ, 0); + + if( hHDF4 <= 0 ) + return( NULL ); + + Hclose( hHDF4 ); + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + HDF4Dataset *poDS; + + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + poDS = new HDF4Dataset(); + CPLAcquireMutex(hHDF4Mutex, 1000.0); + + if( poOpenInfo->fpL != NULL ) + { + VSIFCloseL(poOpenInfo->fpL); + poOpenInfo->fpL = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open HDF SDS Interface. */ +/* -------------------------------------------------------------------- */ + poDS->hSD = SDstart( poOpenInfo->pszFilename, DFACC_READ ); + + if ( poDS->hSD == -1 ) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open HDF4 file \"%s\" for SDS reading.\n", + poOpenInfo->pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Now read Global Attributes. */ +/* -------------------------------------------------------------------- */ + if ( poDS->ReadGlobalAttributes( poDS->hSD ) != CE_None ) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to read global attributes from HDF4 file \"%s\".\n", + poOpenInfo->pszFilename ); + return NULL; + } + + poDS->SetMetadata( poDS->papszGlobalMetadata, "" ); + +/* -------------------------------------------------------------------- */ +/* Determine type of file we read. */ +/* -------------------------------------------------------------------- */ + const char *pszValue; + + if ( (pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata, + "Signature")) + && EQUAL( pszValue, pszGDALSignature ) ) + { + poDS->iSubdatasetType = H4ST_GDAL; + poDS->pszSubdatasetType = "GDAL_HDF4"; + } + + else if ( (pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata, "Title")) + && EQUAL( pszValue, "SeaWiFS Level-1A Data" ) ) + { + poDS->iSubdatasetType = H4ST_SEAWIFS_L1A; + poDS->pszSubdatasetType = "SEAWIFS_L1A"; + } + + else if ( (pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata, "Title")) + && EQUAL( pszValue, "SeaWiFS Level-2 Data" ) ) + { + poDS->iSubdatasetType = H4ST_SEAWIFS_L2; + poDS->pszSubdatasetType = "SEAWIFS_L2"; + } + + else if ( (pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata, "Title")) + && EQUAL( pszValue, "SeaWiFS Level-3 Standard Mapped Image" ) ) + { + poDS->iSubdatasetType = H4ST_SEAWIFS_L3; + poDS->pszSubdatasetType = "SEAWIFS_L3"; + } + + else if ( (pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata, + "L1 File Generated By")) + && EQUALN( pszValue, "HYP version ", 12 ) ) + { + poDS->iSubdatasetType = H4ST_HYPERION_L1; + poDS->pszSubdatasetType = "HYPERION_L1"; + } + + else + { + poDS->iSubdatasetType = H4ST_UNKNOWN; + poDS->pszSubdatasetType = "UNKNOWN"; + } + +/* -------------------------------------------------------------------- */ +/* If we have HDF-EOS dataset, process it here. */ +/* -------------------------------------------------------------------- */ + char szName[VSNAMELENMAX + 1], szTemp[8192]; + char *pszString; + const char *pszName; + int nCount; + int32 aiDimSizes[H4_MAX_VAR_DIMS]; + int32 iRank, iNumType, nAttrs; + bool bIsHDF = true; + + // Sometimes "HDFEOSVersion" attribute is not defined and we will + // determine HDF-EOS datasets using other records + // (see ReadGlobalAttributes() method). + if ( poDS->bIsHDFEOS + || CSLFetchNameValue(poDS->papszGlobalMetadata, "HDFEOSVersion") ) + { + bIsHDF = false; + + int32 nSubDatasets, nStrBufSize; + +/* -------------------------------------------------------------------- */ +/* Process swath layers. */ +/* -------------------------------------------------------------------- */ + hHDF4 = SWopen( poOpenInfo->pszFilename, DFACC_READ ); + if( hHDF4 < 0) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open HDF-EOS file \"%s\" for swath reading.\n", + poOpenInfo->pszFilename ); + return NULL; + } + nSubDatasets = SWinqswath(poOpenInfo->pszFilename, NULL, &nStrBufSize); +#ifdef DEBUG + CPLDebug( "HDF4", "Number of HDF-EOS swaths: %d", (int)nSubDatasets ); +#endif + if ( nSubDatasets > 0 && nStrBufSize > 0 ) + { + char *pszSwathList; + char **papszSwaths; + + pszSwathList = (char *)CPLMalloc( nStrBufSize + 1 ); + SWinqswath( poOpenInfo->pszFilename, pszSwathList, &nStrBufSize ); + pszSwathList[nStrBufSize] = '\0'; + +#ifdef DEBUG + CPLDebug( "HDF4", "List of HDF-EOS swaths: %s", pszSwathList ); +#endif + + papszSwaths = + CSLTokenizeString2( pszSwathList, ",", CSLT_HONOURSTRINGS ); + CPLFree( pszSwathList ); + + if ( nSubDatasets != CSLCount(papszSwaths) ) + { + CSLDestroy( papszSwaths ); + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + CPLDebug( "HDF4", "Can not parse list of HDF-EOS grids." ); + return NULL; + } + + for ( i = 0; i < nSubDatasets; i++) + { + char *pszFieldList; + char **papszFields; + int32 *paiRank, *paiNumType; + int32 hSW, nFields, j; + + hSW = SWattach( hHDF4, papszSwaths[i] ); + + nFields = SWnentries( hSW, HDFE_NENTDFLD, &nStrBufSize ); + pszFieldList = (char *)CPLMalloc( nStrBufSize + 1 ); + paiRank = (int32 *)CPLMalloc( nFields * sizeof(int32) ); + paiNumType = (int32 *)CPLMalloc( nFields * sizeof(int32) ); + + SWinqdatafields( hSW, pszFieldList, paiRank, paiNumType ); + +#ifdef DEBUG + { + char *pszTmp = + SPrintArray( GDT_UInt32, paiRank, nFields, "," ); + + CPLDebug( "HDF4", "Number of data fields in swath %d: %d", + (int) i, (int) nFields ); + CPLDebug( "HDF4", "List of data fields in swath %d: %s", + (int) i, pszFieldList ); + CPLDebug( "HDF4", "Data fields ranks: %s", pszTmp ); + + CPLFree( pszTmp ); + } +#endif + + papszFields = CSLTokenizeString2( pszFieldList, ",", + CSLT_HONOURSTRINGS ); + + for ( j = 0; j < nFields; j++ ) + { + SWfieldinfo( hSW, papszFields[j], &iRank, aiDimSizes, + &iNumType, NULL ); + + if ( iRank < 2 ) + continue; + + // Add field to the list of GDAL subdatasets + nCount = CSLCount( poDS->papszSubDatasets ) / 2; + sprintf( szTemp, "SUBDATASET_%d_NAME", nCount + 1 ); + // We will use the field index as an identificator. + poDS->papszSubDatasets = + CSLSetNameValue( poDS->papszSubDatasets, szTemp, + CPLSPrintf("HDF4_EOS:EOS_SWATH:\"%s\":%s:%s", + poOpenInfo->pszFilename, + papszSwaths[i], papszFields[j]) ); + + sprintf( szTemp, "SUBDATASET_%d_DESC", nCount + 1 ); + pszString = SPrintArray( GDT_UInt32, aiDimSizes, + iRank, "x" ); + poDS->papszSubDatasets = + CSLSetNameValue( poDS->papszSubDatasets, szTemp, + CPLSPrintf( "[%s] %s %s (%s)", pszString, + papszFields[j], + papszSwaths[i], + poDS->GetDataTypeName(iNumType) ) ); + CPLFree( pszString ); + } + + CSLDestroy( papszFields ); + CPLFree( paiNumType ); + CPLFree( paiRank ); + CPLFree( pszFieldList ); + SWdetach( hSW ); + } + + CSLDestroy( papszSwaths ); + } + SWclose( hHDF4 ); + +/* -------------------------------------------------------------------- */ +/* Process grid layers. */ +/* -------------------------------------------------------------------- */ + hHDF4 = GDopen( poOpenInfo->pszFilename, DFACC_READ ); + nSubDatasets = GDinqgrid( poOpenInfo->pszFilename, NULL, &nStrBufSize ); +#ifdef DEBUG + CPLDebug( "HDF4", "Number of HDF-EOS grids: %d", (int)nSubDatasets ); +#endif + if ( nSubDatasets > 0 && nStrBufSize > 0 ) + { + char *pszGridList; + char **papszGrids; + + pszGridList = (char *)CPLMalloc( nStrBufSize + 1 ); + GDinqgrid( poOpenInfo->pszFilename, pszGridList, &nStrBufSize ); + +#ifdef DEBUG + CPLDebug( "HDF4", "List of HDF-EOS grids: %s", pszGridList ); +#endif + + papszGrids = + CSLTokenizeString2( pszGridList, ",", CSLT_HONOURSTRINGS ); + CPLFree( pszGridList ); + + if ( nSubDatasets != CSLCount(papszGrids) ) + { + CSLDestroy( papszGrids ); + GDclose( hHDF4 ); + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + CPLDebug( "HDF4", "Can not parse list of HDF-EOS grids." ); + return NULL; + } + + for ( i = 0; i < nSubDatasets; i++) + { + char *pszFieldList; + char **papszFields; + int32 *paiRank, *paiNumType; + int32 hGD, nFields, j; + + hGD = GDattach( hHDF4, papszGrids[i] ); + + nFields = GDnentries( hGD, HDFE_NENTDFLD, &nStrBufSize ); + pszFieldList = (char *)CPLMalloc( nStrBufSize + 1 ); + paiRank = (int32 *)CPLMalloc( nFields * sizeof(int32) ); + paiNumType = (int32 *)CPLMalloc( nFields * sizeof(int32) ); + + GDinqfields( hGD, pszFieldList, paiRank, paiNumType ); + +#ifdef DEBUG + { + char* pszTmp = + SPrintArray( GDT_UInt32, paiRank, nFields, "," ); + CPLDebug( "HDF4", "Number of fields in grid %d: %d", + (int) i, (int) nFields ); + CPLDebug( "HDF4", "List of fields in grid %d: %s", + (int) i, pszFieldList ); + CPLDebug( "HDF4", "Fields ranks: %s", + pszTmp ); + CPLFree( pszTmp ); + } +#endif + + papszFields = CSLTokenizeString2( pszFieldList, ",", + CSLT_HONOURSTRINGS ); + + for ( j = 0; j < nFields; j++ ) + { + GDfieldinfo( hGD, papszFields[j], &iRank, aiDimSizes, + &iNumType, NULL ); + + if ( iRank < 2 ) + continue; + + // Add field to the list of GDAL subdatasets + nCount = CSLCount( poDS->papszSubDatasets ) / 2; + sprintf( szTemp, "SUBDATASET_%d_NAME", nCount + 1 ); + // We will use the field index as an identificator. + poDS->papszSubDatasets = + CSLSetNameValue(poDS->papszSubDatasets, szTemp, + CPLSPrintf( "HDF4_EOS:EOS_GRID:\"%s\":%s:%s", + poOpenInfo->pszFilename, + papszGrids[i], papszFields[j])); + + sprintf( szTemp, "SUBDATASET_%d_DESC", nCount + 1 ); + pszString = SPrintArray( GDT_UInt32, aiDimSizes, + iRank, "x" ); + poDS->papszSubDatasets = + CSLSetNameValue( poDS->papszSubDatasets, szTemp, + CPLSPrintf("[%s] %s %s (%s)", pszString, + papszFields[j], + papszGrids[i], + poDS->GetDataTypeName(iNumType)) ); + CPLFree( pszString ); + } + + CSLDestroy( papszFields ); + CPLFree( paiNumType ); + CPLFree( paiRank ); + CPLFree( pszFieldList ); + GDdetach( hGD ); + } + + CSLDestroy( papszGrids ); + } + GDclose( hHDF4 ); + + bIsHDF = ( nSubDatasets == 0 ); // Try to read as HDF + } + + if( bIsHDF ) + { + +/* -------------------------------------------------------------------- */ +/* Make a list of subdatasets from SDSs contained in input HDF file. */ +/* -------------------------------------------------------------------- */ + int32 nDatasets; + + if ( SDfileinfo( poDS->hSD, &nDatasets, &nAttrs ) != 0 ) + return NULL; + + for ( i = 0; i < nDatasets; i++ ) + { + int32 iSDS; + + iSDS = SDselect( poDS->hSD, i ); + if ( SDgetinfo( iSDS, szName, &iRank, aiDimSizes, &iNumType, &nAttrs) != 0 ) + return NULL; + + if ( iRank == 1 ) // Skip 1D datsets + continue; + + // Do sort of known datasets. We will display only image bands + if ( (poDS->iSubdatasetType == H4ST_SEAWIFS_L1A ) && + !EQUALN( szName, "l1a_data", 8 ) ) + continue; + else + pszName = szName; + + // Add datasets with multiple dimensions to the list of GDAL subdatasets + nCount = CSLCount( poDS->papszSubDatasets ) / 2; + sprintf( szTemp, "SUBDATASET_%d_NAME", nCount + 1 ); + // We will use SDS index as an identificator, because SDS names + // are not unique. Filename also needed for further file opening + poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets, szTemp, + CPLSPrintf( "HDF4_SDS:%s:\"%s\":%ld", poDS->pszSubdatasetType, + poOpenInfo->pszFilename, (long)i) ); + sprintf( szTemp, "SUBDATASET_%d_DESC", nCount + 1 ); + pszString = SPrintArray( GDT_UInt32, aiDimSizes, iRank, "x" ); + poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets, szTemp, + CPLSPrintf( "[%s] %s (%s)", pszString, + pszName, poDS->GetDataTypeName(iNumType)) ); + CPLFree( pszString ); + + SDendaccess( iSDS ); + } + + SDend( poDS->hSD ); + poDS->hSD = 0; + } + +/* -------------------------------------------------------------------- */ +/* Build a list of raster images. Note, that HDF-EOS dataset may */ +/* contain a raster image as well. */ +/* -------------------------------------------------------------------- */ + + hHDF4 = Hopen(poOpenInfo->pszFilename, DFACC_READ, 0); + poDS->hGR = GRstart( hHDF4 ); + + if ( poDS->hGR != -1 ) + { + if ( GRfileinfo( poDS->hGR, &poDS->nImages, &nAttrs ) == -1 ) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + GRend( poDS->hGR ); + poDS->hGR = 0; + Hclose( hHDF4 ); + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + + for ( i = 0; i < poDS->nImages; i++ ) + { + int32 iInterlaceMode; + int32 iGR = GRselect( poDS->hGR, i ); + + // iRank in GR interface has another meaning. It represents number + // of samples per pixel. aiDimSizes has only two dimensions. + if ( GRgetiminfo( iGR, szName, &iRank, &iNumType, &iInterlaceMode, + aiDimSizes, &nAttrs ) != 0 ) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + GRend( poDS->hGR ); + poDS->hGR = 0; + Hclose( hHDF4 ); + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + nCount = CSLCount( poDS->papszSubDatasets ) / 2; + sprintf( szTemp, "SUBDATASET_%d_NAME", nCount + 1 ); + poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets, + szTemp,CPLSPrintf( "HDF4_GR:UNKNOWN:\"%s\":%ld", + poOpenInfo->pszFilename, (long)i)); + sprintf( szTemp, "SUBDATASET_%d_DESC", nCount + 1 ); + pszString = SPrintArray( GDT_UInt32, aiDimSizes, 2, "x" ); + poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets, + szTemp, CPLSPrintf( "[%sx%ld] %s (%s)", pszString, (long)iRank, + szName, poDS->GetDataTypeName(iNumType)) ); + CPLFree( pszString ); + + GRendaccess( iGR ); + } + + GRend( poDS->hGR ); + poDS->hGR = 0; + } + + Hclose( hHDF4 ); + + poDS->nRasterXSize = poDS->nRasterYSize = 512; // XXX: bogus values + + // Make sure we don't try to do any pam stuff with this dataset. + poDS->nPamFlags |= GPF_NOSAVE; + +/* -------------------------------------------------------------------- */ +/* If we have single subdataset only, open it immediately */ +/* -------------------------------------------------------------------- */ + if ( CSLCount( poDS->papszSubDatasets ) / 2 == 1 ) + { + char *pszSDSName; + pszSDSName = CPLStrdup( CSLFetchNameValue( poDS->papszSubDatasets, + "SUBDATASET_1_NAME" )); + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + poDS = NULL; + + GDALDataset* poRetDS = (GDALDataset*) GDALOpen( pszSDSName, poOpenInfo->eAccess ); + CPLFree( pszSDSName ); + + CPLAcquireMutex(hHDF4Mutex, 1000.0); + + if (poRetDS) + { + poRetDS->SetDescription(poOpenInfo->pszFilename); + } + + return poRetDS; + } + else + { +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + + CPLError( CE_Failure, CPLE_NotSupported, + "The HDF4 driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + } + + return( poDS ); +} + +/************************************************************************/ +/* HDF4UnloadDriver() */ +/************************************************************************/ + +static void HDF4UnloadDriver(CPL_UNUSED GDALDriver* poDriver) +{ + if( hHDF4Mutex != NULL ) + CPLDestroyMutex(hHDF4Mutex); + hHDF4Mutex = NULL; +} + +/************************************************************************/ +/* GDALRegister_HDF4() */ +/************************************************************************/ + +void GDALRegister_HDF4() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("HDF4 driver")) + return; + + if( GDALGetDriverByName( "HDF4" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "HDF4" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Hierarchical Data Format Release 4" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_hdf4.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "hdf" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + + poDriver->pfnOpen = HDF4Dataset::Open; + poDriver->pfnIdentify = HDF4Dataset::Identify; + poDriver->pfnUnloadDriver = HDF4UnloadDriver; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } + +#ifdef HDF4_PLUGIN + GDALRegister_HDF4Image(); +#endif + +} diff --git a/bazaar/plugin/gdal/frmts/hdf4/hdf4dataset.h b/bazaar/plugin/gdal/frmts/hdf4/hdf4dataset.h new file mode 100644 index 000000000..98e60d03d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/hdf4dataset.h @@ -0,0 +1,110 @@ +/****************************************************************************** + * $Id: hdf4dataset.h 28879 2015-04-09 11:05:49Z dron $ + * + * Project: Hierarchical Data Format Release 4 (HDF4) + * Purpose: Header file for HDF4 datasets reader. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2002, Andrey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _HDF4DATASET_H_INCLUDED_ +#define _HDF4DATASET_H_INCLUDED_ + +#include "cpl_list.h" +#include "gdal_pam.h" + +typedef enum // Types of dataset: +{ + HDF4_SDS, // Scientific Dataset + HDF4_GR, // General Raster Image + HDF4_EOS, // HDF EOS + HDF4_UNKNOWN +} HDF4DatasetType; + +typedef enum // Types of data products: +{ + H4ST_GDAL, // HDF written by GDAL + H4ST_EOS_GRID, // HDF-EOS Grid + H4ST_EOS_SWATH, // HDF-EOS Swath + H4ST_EOS_SWATH_GEOL, // HDF-EOS Swath Geolocation Array + H4ST_SEAWIFS_L1A, // SeaWiFS Level-1A Data + H4ST_SEAWIFS_L2, // SeaWiFS Level-2 Data + H4ST_SEAWIFS_L3, // SeaWiFS Level-3 Standard Mapped Image + H4ST_HYPERION_L1, // Hyperion L1 Data Product + H4ST_UNKNOWN +} HDF4SubdatasetType; + +/************************************************************************/ +/* ==================================================================== */ +/* HDF4Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +class HDF4Dataset : public GDALPamDataset +{ + + private: + + int bIsHDFEOS; + + static char **HDF4EOSTokenizeAttrs( const char *pszString ); + static char **HDF4EOSGetObject( char **papszAttrList, char **ppszAttrName, + char **ppszAttrClass, char **ppszAttrValue ); + + protected: + + int32 hGR, hSD; + int32 nImages; + HDF4SubdatasetType iSubdatasetType; + const char *pszSubdatasetType; + + char **papszGlobalMetadata; + char **papszSubDatasets; + + CPLErr ReadGlobalAttributes( int32 ); + + static GDALDataType GetDataType( int32 ) ; + static const char *GetDataTypeName( int32 ); + static int GetDataTypeSize( int32 ); + static double AnyTypeToDouble( int32, void * ); + static char **TranslateHDF4Attributes( int32, int32, char *, + int32, int32, char ** ); + static char **TranslateHDF4EOSAttributes( int32, int32, int32, + char ** ); + + public: + HDF4Dataset(); + ~HDF4Dataset(); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain = "" ); + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +char *SPrintArray( GDALDataType eDataType, const void *paDataArray, + int nValues, const char *pszDelimiter ); + + +#endif /* _HDF4DATASET_H_INCLUDED_ */ + diff --git a/bazaar/plugin/gdal/frmts/hdf4/hdf4imagedataset.cpp b/bazaar/plugin/gdal/frmts/hdf4/hdf4imagedataset.cpp new file mode 100644 index 000000000..93869db3a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/hdf4imagedataset.cpp @@ -0,0 +1,3857 @@ +/****************************************************************************** + * $Id: hdf4imagedataset.cpp 29212 2015-05-20 09:28:31Z rouault $ + * + * Project: Hierarchical Data Format Release 4 (HDF4) + * Purpose: Read subdatasets of HDF4 file. + * This driver initially based on code supplied by Markus Neteler + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2002, Andrey Kiselev + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include +#include "cpl_multiproc.h" + +#include "hdf.h" +#include "mfhdf.h" + +#include "HdfEosDef.h" + +#include "gdal_priv.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +#include "hdf4compat.h" +#include "hdf4dataset.h" + +#include "nasakeywordhandler.h" + +CPL_CVSID("$Id: hdf4imagedataset.cpp 29212 2015-05-20 09:28:31Z rouault $"); + +CPL_C_START +void GDALRegister_HDF4(void); +CPL_C_END + +#define HDF4_SDS_MAXNAMELEN 65 + +// Signature to recognize files written by GDAL +const char *pszGDALSignature = + "Created with GDAL (http://www.remotesensing.org/gdal/)"; + +extern CPLMutex *hHDF4Mutex; + +/************************************************************************/ +/* ==================================================================== */ +/* List of HDF-EOS Swath product types. */ +/* ==================================================================== */ +/************************************************************************/ + +enum HDF4EOSProduct +{ + PROD_UNKNOWN, + PROD_ASTER_L1A, + PROD_ASTER_L1B, + PROD_ASTER_L2, + PROD_ASTER_L3, + PROD_AST14DEM, + PROD_MODIS_L1B, + PROD_MODIS_L2 +}; + +/************************************************************************/ +/* ==================================================================== */ +/* HDF4ImageDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class HDF4ImageDataset : public HDF4Dataset +{ + friend class HDF4ImageRasterBand; + + char *pszFilename; + int32 hHDF4, iGR, iPal, iDataset; + int32 iRank, iNumType, nAttrs, + iInterlaceMode, iPalInterlaceMode, iPalDataType; + int32 nComps, nPalEntries; + int32 aiDimSizes[H4_MAX_VAR_DIMS]; + int iXDim, iYDim, iBandDim, i4Dim; + int nBandCount; + char **papszLocalMetadata; +#define N_COLOR_ENTRIES 256 + uint8 aiPaletteData[N_COLOR_ENTRIES][3]; // XXX: Static array for now + char szName[HDF4_SDS_MAXNAMELEN]; + char *pszSubdatasetName; + char *pszFieldName; + + GDALColorTable *poColorTable; + + OGRSpatialReference oSRS; + int bHasGeoTransform; + double adfGeoTransform[6]; + char *pszProjection; + char *pszGCPProjection; + GDAL_GCP *pasGCPList; + int nGCPCount; + + HDF4DatasetType iDatasetType; + + int32 iSDS; + + int nBlockPreferredXSize; + int nBlockPreferredYSize; + bool bReadTile; + + void ToGeoref( double *, double * ); + void GetImageDimensions( char * ); + void GetSwatAttrs( int32 ); + void GetGridAttrs( int32 hGD ); + void CaptureNRLGeoTransform(void); + void CaptureL1GMTLInfo(void); + void CaptureCoastwatchGCTPInfo(void); + void ProcessModisSDSGeolocation(void); + int ProcessSwathGeolocation( int32, char ** ); + + static long USGSMnemonicToCode( const char* ); + static void ReadCoordinates( const char*, double*, double* ); + + public: + HDF4ImageDataset(); + ~HDF4ImageDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); + virtual void FlushCache( void ); + CPLErr GetGeoTransform( double * padfTransform ); + virtual CPLErr SetGeoTransform( double * ); + const char *GetProjectionRef(); + virtual CPLErr SetProjection( const char * ); + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* HDF4ImageRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class HDF4ImageRasterBand : public GDALPamRasterBand +{ + friend class HDF4ImageDataset; + + int bNoDataSet; + double dfNoDataValue; + + int bHaveScale, bHaveOffset; + double dfScale; + double dfOffset; + + CPLString osUnitType; + + public: + + HDF4ImageRasterBand( HDF4ImageDataset *, int, GDALDataType ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual double GetNoDataValue( int * ); + virtual CPLErr SetNoDataValue( double ); + virtual double GetOffset( int *pbSuccess ); + virtual double GetScale( int *pbSuccess ); + virtual const char *GetUnitType(); +}; + +/************************************************************************/ +/* HDF4ImageRasterBand() */ +/************************************************************************/ + +HDF4ImageRasterBand::HDF4ImageRasterBand( HDF4ImageDataset *poDS, int nBand, + GDALDataType eType ) + +{ + this->poDS = poDS; + this->nBand = nBand; + eDataType = eType; + bNoDataSet = FALSE; + dfNoDataValue = -9999.0; + + bHaveScale = bHaveOffset = FALSE; + dfScale = 1.0; + dfOffset = 0.0; + + nBlockXSize = poDS->GetRasterXSize(); + + // Aim for a block of about 1000000 pixels. Chunking up substantially + // improves performance in some situations. For now we only chunk up for + // SDS and EOS based datasets since other variations haven't been tested. #2208 + if( poDS->iDatasetType == HDF4_SDS || + poDS->iDatasetType == HDF4_EOS) + { + int nChunkSize = + atoi( CPLGetConfigOption("HDF4_BLOCK_PIXELS", "1000000") ); + + nBlockYSize = nChunkSize / poDS->GetRasterXSize(); + nBlockYSize = MAX(1,MIN(nBlockYSize,poDS->GetRasterYSize())); + } + else + { + nBlockYSize = 1; + } + + /* HDF4_EOS:EOS_GRID case. We ensure that the block size matches */ + /* the raster width, as the IReadBlock() code can only handle multiple */ + /* blocks per row */ + if ( poDS->nBlockPreferredXSize == nBlockXSize && + poDS->nBlockPreferredYSize > 0 ) + { + if (poDS->nBlockPreferredYSize == 1) + { + /* Avoid defaulting to tile reading when the preferred height is 1 */ + /* as it leads to very poor performance with : */ + /* ftp://e4ftl01u.ecs.nasa.gov/MODIS_Composites/MOLT/MOD13Q1.005/2006.06.10/MOD13Q1.A2006161.h21v13.005.2008234103220.hd */ + poDS->bReadTile = FALSE; + } + else + { + nBlockYSize = poDS->nBlockPreferredYSize; + } + } + +/* -------------------------------------------------------------------- */ +/* We need to avoid using the tile based api if we aren't */ +/* matching the tile size. (#4672) */ +/* -------------------------------------------------------------------- */ + if( nBlockXSize != poDS->nBlockPreferredXSize + || nBlockYSize != poDS->nBlockPreferredYSize ) + { + poDS->bReadTile = FALSE; + } +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr HDF4ImageRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + HDF4ImageDataset *poGDS = (HDF4ImageDataset *) poDS; + int32 aiStart[H4_MAX_NC_DIMS], aiEdges[H4_MAX_NC_DIMS]; + CPLErr eErr = CE_None; + + CPLMutexHolderD(&hHDF4Mutex); + + if( poGDS->eAccess == GA_Update ) + { + memset( pImage, 0, + nBlockXSize * nBlockYSize * GDALGetDataTypeSize(eDataType) / 8 ); + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Work out some block oriented details. */ +/* -------------------------------------------------------------------- */ + int nYOff = nBlockYOff * nBlockYSize; + int nYSize = MIN(nYOff + nBlockYSize, poDS->GetRasterYSize()) - nYOff; + CPLAssert( nBlockXOff == 0 ); + +/* -------------------------------------------------------------------- */ +/* HDF files with external data files, such as some landsat */ +/* products (eg. data/hdf/L1G) need to be told what directory */ +/* to look in to find the external files. Normally this is the */ +/* directory holding the hdf file. */ +/* -------------------------------------------------------------------- */ + HXsetdir(CPLGetPath(poGDS->pszFilename)); + +/* -------------------------------------------------------------------- */ +/* Handle different configurations. */ +/* -------------------------------------------------------------------- */ + switch ( poGDS->iDatasetType ) + { + case HDF4_SDS: + { + /* We avoid doing SDselect() / SDendaccess() for each block access */ + /* as this is very slow when zlib compression is used */ + + if (poGDS->iSDS == FAIL) + poGDS->iSDS = SDselect( poGDS->hSD, poGDS->iDataset ); + + /* HDF rank: + A rank 2 dataset is an image read in scan-line order (2D). + A rank 3 dataset is a series of images which are read in + an image at a time to form a volume. + A rank 4 dataset may be thought of as a series of volumes. + + The "aiStart" array specifies the multi-dimensional index of the + starting corner of the hyperslab to read. The values are zero + based. + + The "edge" array specifies the number of values to read along + each dimension of the hyperslab. + + The "iStride" array allows for sub-sampling along each + dimension. If a iStride value is specified for a dimension, + that many values will be skipped over when reading along that + dimension. Specifying iStride = NULL in the C interface or + iStride = 1 in either interface specifies contiguous reading + of data. If the iStride values are set to 0, SDreaddata + returns FAIL (or -1). No matter what iStride value is + provided, data is always placed contiguously in buffer. + */ + switch ( poGDS->iRank ) + { + case 4: // 4Dim: volume-time + // FIXME: needs sample file. Does not work currently. + aiStart[3] = 0/* range: 0--aiDimSizes[3]-1 */; + aiEdges[3] = 1; + aiStart[2] = 0/* range: 0--aiDimSizes[2]-1 */; + aiEdges[2] = 1; + aiStart[1] = nYOff; aiEdges[1] = nYSize; + aiStart[0] = nBlockXOff; aiEdges[0] = nBlockXSize; + break; + case 3: // 3Dim: volume + aiStart[poGDS->iBandDim] = nBand - 1; + aiEdges[poGDS->iBandDim] = 1; + + aiStart[poGDS->iYDim] = nYOff; + aiEdges[poGDS->iYDim] = nYSize; + + aiStart[poGDS->iXDim] = nBlockXOff; + aiEdges[poGDS->iXDim] = nBlockXSize; + break; + case 2: // 2Dim: rows/cols + aiStart[poGDS->iYDim] = nYOff; + aiEdges[poGDS->iYDim] = nYSize; + + aiStart[poGDS->iXDim] = nBlockXOff; + aiEdges[poGDS->iXDim] = nBlockXSize; + break; + case 1: //1Dim: + aiStart[poGDS->iXDim] = nBlockXOff; + aiEdges[poGDS->iXDim] = nBlockXSize; + break; + } + + // Read HDF SDS array + if( SDreaddata( poGDS->iSDS, aiStart, NULL, aiEdges, pImage ) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SDreaddata() failed for block." ); + eErr = CE_Failure; + } + + //SDendaccess( iSDS ); + } + break; + + case HDF4_GR: + { + int nDataTypeSize = + GDALGetDataTypeSize(poGDS->GetDataType(poGDS->iNumType)) / 8; + GByte *pbBuffer = (GByte *) + CPLMalloc(nBlockXSize*nBlockYSize*poGDS->iRank*nBlockYSize); + int i, j; + + aiStart[poGDS->iYDim] = nYOff; + aiEdges[poGDS->iYDim] = nYSize; + + aiStart[poGDS->iXDim] = nBlockXOff; + aiEdges[poGDS->iXDim] = nBlockXSize; + + if( GRreadimage(poGDS->iGR, aiStart, NULL, aiEdges, pbBuffer) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GRreaddata() failed for block." ); + eErr = CE_Failure; + } + else + { + for ( i = 0, j = (nBand - 1) * nDataTypeSize; + i < nBlockXSize * nDataTypeSize; + i += nDataTypeSize, j += poGDS->nBands * nDataTypeSize ) + memcpy( (GByte *)pImage + i, pbBuffer + j, nDataTypeSize ); + } + + CPLFree( pbBuffer ); + } + break; + + case HDF4_EOS: + { + switch ( poGDS->iSubdatasetType ) + { + case H4ST_EOS_GRID: + { + int32 hGD; + + hGD = GDattach( poGDS->hHDF4, + poGDS->pszSubdatasetName ); + switch ( poGDS->iRank ) + { + case 4: // 4Dim: volume + aiStart[poGDS->i4Dim] = + (nBand - 1) + / poGDS->aiDimSizes[poGDS->iBandDim]; + aiEdges[poGDS->i4Dim] = 1; + + aiStart[poGDS->iBandDim] = + (nBand - 1) + % poGDS->aiDimSizes[poGDS->iBandDim]; + aiEdges[poGDS->iBandDim] = 1; + + aiStart[poGDS->iYDim] = nYOff; + aiEdges[poGDS->iYDim] = nYSize; + + aiStart[poGDS->iXDim] = nBlockXOff; + aiEdges[poGDS->iXDim] = nBlockXSize; + break; + case 3: // 3Dim: volume + aiStart[poGDS->iBandDim] = nBand - 1; + aiEdges[poGDS->iBandDim] = 1; + + aiStart[poGDS->iYDim] = nYOff; + aiEdges[poGDS->iYDim] = nYSize; + + aiStart[poGDS->iXDim] = nBlockXOff; + aiEdges[poGDS->iXDim] = nBlockXSize; + break; + case 2: // 2Dim: rows/cols + aiStart[poGDS->iYDim] = nYOff; + aiEdges[poGDS->iYDim] = nYSize; + + aiStart[poGDS->iXDim] = nBlockXOff; + aiEdges[poGDS->iXDim] = nBlockXSize; + break; + } + + /* Ensure that we don't overlap the bottom or right edges */ + /* of the dataset in order to use the GDreadtile() API */ + if ( poGDS->bReadTile && + (nBlockXOff + 1) * nBlockXSize <= nRasterXSize && + (nBlockYOff + 1) * nBlockYSize <= nRasterYSize ) + { + int32 tilecoords[] = { nBlockYOff , nBlockXOff }; + if( GDreadtile( hGD, poGDS->pszFieldName , tilecoords , pImage ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, "GDreadtile() failed for block." ); + eErr = CE_Failure; + } + } + else if( GDreadfield( hGD, poGDS->pszFieldName, + aiStart, NULL, aiEdges, pImage ) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GDreadfield() failed for block." ); + eErr = CE_Failure; + } + GDdetach( hGD ); + } + break; + + case H4ST_EOS_SWATH: + case H4ST_EOS_SWATH_GEOL: + { + int32 hSW; + + hSW = SWattach( poGDS->hHDF4, + poGDS->pszSubdatasetName ); + switch ( poGDS->iRank ) + { + case 3: // 3Dim: volume + aiStart[poGDS->iBandDim] = nBand - 1; + aiEdges[poGDS->iBandDim] = 1; + + aiStart[poGDS->iYDim] = nYOff; + aiEdges[poGDS->iYDim] = nYSize; + + aiStart[poGDS->iXDim] = nBlockXOff; + aiEdges[poGDS->iXDim] = nBlockXSize; + break; + case 2: // 2Dim: rows/cols + aiStart[poGDS->iYDim] = nYOff; + aiEdges[poGDS->iYDim] = nYSize; + + aiStart[poGDS->iXDim] = nBlockXOff; + aiEdges[poGDS->iXDim] = nBlockXSize; + break; + } + if( SWreadfield( hSW, poGDS->pszFieldName, + aiStart, NULL, aiEdges, pImage ) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SWreadfield() failed for block." ); + eErr = CE_Failure; + } + SWdetach( hSW ); + } + break; + default: + break; + } + } + break; + + default: + eErr = CE_Failure; + break; + } + + return eErr; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr HDF4ImageRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + HDF4ImageDataset *poGDS = (HDF4ImageDataset *)poDS; + int32 aiStart[H4_MAX_NC_DIMS], aiEdges[H4_MAX_NC_DIMS]; + CPLErr eErr = CE_None; + + CPLMutexHolderD(&hHDF4Mutex); + + CPLAssert( poGDS != NULL + && nBlockXOff == 0 + && nBlockYOff >= 0 + && pImage != NULL ); + +/* -------------------------------------------------------------------- */ +/* Work out some block oriented details. */ +/* -------------------------------------------------------------------- */ + int nYOff = nBlockYOff * nBlockYSize; + int nYSize = MIN(nYOff + nBlockYSize, poDS->GetRasterYSize()) - nYOff; + CPLAssert( nBlockXOff == 0 ); + +/* -------------------------------------------------------------------- */ +/* Process based on rank. */ +/* -------------------------------------------------------------------- */ + switch ( poGDS->iRank ) + { + case 3: + { + int32 iSDS = SDselect( poGDS->hSD, poGDS->iDataset ); + + aiStart[poGDS->iBandDim] = nBand - 1; + aiEdges[poGDS->iBandDim] = 1; + + aiStart[poGDS->iYDim] = nYOff; + aiEdges[poGDS->iYDim] = nYSize; + + aiStart[poGDS->iXDim] = nBlockXOff; + aiEdges[poGDS->iXDim] = nBlockXSize; + + if ( (SDwritedata( iSDS, aiStart, NULL, + aiEdges, (VOIDP)pImage )) < 0 ) + eErr = CE_Failure; + + SDendaccess( iSDS ); + } + break; + + case 2: + { + int32 iSDS = SDselect( poGDS->hSD, nBand - 1 ); + aiStart[poGDS->iYDim] = nYOff; + aiEdges[poGDS->iYDim] = nYSize; + + aiStart[poGDS->iXDim] = nBlockXOff; + aiEdges[poGDS->iXDim] = nBlockXSize; + + if ( (SDwritedata( iSDS, aiStart, NULL, + aiEdges, (VOIDP)pImage )) < 0 ) + eErr = CE_Failure; + + SDendaccess( iSDS ); + } + break; + + default: + eErr = CE_Failure; + break; + } + + return eErr; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *HDF4ImageRasterBand::GetColorTable() +{ + HDF4ImageDataset *poGDS = (HDF4ImageDataset *) poDS; + + return poGDS->poColorTable; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp HDF4ImageRasterBand::GetColorInterpretation() +{ + HDF4ImageDataset *poGDS = (HDF4ImageDataset *) poDS; + + if ( poGDS->iDatasetType == HDF4_SDS ) + return GCI_GrayIndex; + else if ( poGDS->iDatasetType == HDF4_GR ) + { + if ( poGDS->poColorTable != NULL ) + return GCI_PaletteIndex; + else if ( poGDS->nBands != 1 ) + { + if ( nBand == 1 ) + return GCI_RedBand; + else if ( nBand == 2 ) + return GCI_GreenBand; + else if ( nBand == 3 ) + return GCI_BlueBand; + else if ( nBand == 4 ) + return GCI_AlphaBand; + else + return GCI_Undefined; + } + else + return GCI_GrayIndex; + } + else + return GCI_GrayIndex; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double HDF4ImageRasterBand::GetNoDataValue( int * pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bNoDataSet; + + return dfNoDataValue; +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr HDF4ImageRasterBand::SetNoDataValue( double dfNoData ) + +{ + bNoDataSet = TRUE; + dfNoDataValue = dfNoData; + + return CE_None; +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +const char *HDF4ImageRasterBand::GetUnitType() + +{ + if( osUnitType.size() > 0 ) + return osUnitType; + else + return GDALRasterBand::GetUnitType(); +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +double HDF4ImageRasterBand::GetOffset( int *pbSuccess ) + +{ + if( bHaveOffset ) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + return dfOffset; + } + else + return GDALRasterBand::GetOffset( pbSuccess ); +} + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ + +double HDF4ImageRasterBand::GetScale( int *pbSuccess ) + +{ + if( bHaveScale ) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + return dfScale; + } + else + return GDALRasterBand::GetScale( pbSuccess ); +} + +/************************************************************************/ +/* ==================================================================== */ +/* HDF4ImageDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* HDF4ImageDataset() */ +/************************************************************************/ + +HDF4ImageDataset::HDF4ImageDataset() +{ + pszFilename = NULL; + hHDF4 = 0; + iGR = 0; + iPal = 0; + iDataset = 0; + iRank = 0; + iNumType = 0; + nAttrs = 0; + iInterlaceMode = 0; + iPalInterlaceMode = 0; + iPalDataType = 0; + nComps = 0; + nPalEntries = 0; + memset(aiDimSizes, 0, sizeof(aiDimSizes)); + iXDim = 0; + iYDim = 0; + iBandDim = -1; + i4Dim = 0; + nBandCount = 0; + papszLocalMetadata = NULL; + memset(aiPaletteData, 0, sizeof(aiPaletteData)); + memset(szName, 0, sizeof(szName)); + pszSubdatasetName = NULL; + pszFieldName = NULL; + poColorTable = NULL; + bHasGeoTransform = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + pszProjection = CPLStrdup( "" ); + pszGCPProjection = CPLStrdup( "" ); + pasGCPList = NULL; + nGCPCount = 0; + + iDatasetType = HDF4_UNKNOWN; + iSDS = FAIL; + + nBlockPreferredXSize = -1; + nBlockPreferredYSize = -1; + bReadTile = false; + +} + +/************************************************************************/ +/* ~HDF4ImageDataset() */ +/************************************************************************/ + +HDF4ImageDataset::~HDF4ImageDataset() +{ + CPLMutexHolderD(&hHDF4Mutex); + + FlushCache(); + + if ( pszFilename ) + CPLFree( pszFilename ); + if ( iSDS != FAIL ) + SDendaccess( iSDS ); + if ( hSD > 0 ) + SDend( hSD ); + hSD = 0; + if ( iGR > 0 ) + GRendaccess( iGR ); + if ( hGR > 0 ) + GRend( hGR ); + hGR = 0; + if ( pszSubdatasetName ) + CPLFree( pszSubdatasetName ); + if ( pszFieldName ) + CPLFree( pszFieldName ); + if ( papszLocalMetadata ) + CSLDestroy( papszLocalMetadata ); + if ( poColorTable != NULL ) + delete poColorTable; + if ( pszProjection ) + CPLFree( pszProjection ); + if ( pszGCPProjection ) + CPLFree( pszGCPProjection ); + if( nGCPCount > 0 ) + { + for( int i = 0; i < nGCPCount; i++ ) + { + if ( pasGCPList[i].pszId ) + CPLFree( pasGCPList[i].pszId ); + if ( pasGCPList[i].pszInfo ) + CPLFree( pasGCPList[i].pszInfo ); + } + + CPLFree( pasGCPList ); + } + if ( hHDF4 > 0 ) + { + switch ( iDatasetType ) + { + case HDF4_EOS: + switch ( iSubdatasetType ) + { + case H4ST_EOS_SWATH: + case H4ST_EOS_SWATH_GEOL: + SWclose( hHDF4 ); + break; + case H4ST_EOS_GRID: + GDclose( hHDF4 ); + default: + break; + } + break; + case HDF4_SDS: + case HDF4_GR: + hHDF4 = Hclose( hHDF4 ); + break; + default: + break; + } + } +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr HDF4ImageDataset::GetGeoTransform( double * padfTransform ) +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + + if ( !bHasGeoTransform ) + return CE_Failure; + + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr HDF4ImageDataset::SetGeoTransform( double * padfTransform ) +{ + bHasGeoTransform = TRUE; + memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 ); + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *HDF4ImageDataset::GetProjectionRef() + +{ + return pszProjection; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr HDF4ImageDataset::SetProjection( const char *pszNewProjection ) + +{ + if ( pszProjection ) + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszNewProjection ); + + return CE_None; +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int HDF4ImageDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *HDF4ImageDataset::GetGCPProjection() + +{ + if( nGCPCount > 0 ) + return pszGCPProjection; + else + return ""; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *HDF4ImageDataset::GetGCPs() +{ + return pasGCPList; +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void HDF4ImageDataset::FlushCache() + +{ + int iBand; + char *pszName; + const char *pszValue; + + CPLMutexHolderD(&hHDF4Mutex); + + GDALDataset::FlushCache(); + + if( eAccess == GA_ReadOnly ) + return; + + // Write out transformation matrix + pszValue = CPLSPrintf( "%f, %f, %f, %f, %f, %f", + adfGeoTransform[0], adfGeoTransform[1], + adfGeoTransform[2], adfGeoTransform[3], + adfGeoTransform[4], adfGeoTransform[5] ); + if ( (SDsetattr( hSD, "TransformationMatrix", DFNT_CHAR8, + strlen(pszValue) + 1, pszValue )) < 0 ) + { + CPLDebug( "HDF4Image", + "Cannot write transformation matrix to output file" ); + } + + // Write out projection + if ( pszProjection != NULL && !EQUAL( pszProjection, "" ) ) + { + if ( (SDsetattr( hSD, "Projection", DFNT_CHAR8, + strlen(pszProjection) + 1, pszProjection )) < 0 ) + { + CPLDebug( "HDF4Image", + "Cannot write projection information to output file"); + } + } + + // Store all metadata from source dataset as HDF attributes + if( GetMetadata() ) + { + char **papszMeta = GetMetadata(); + + while ( *papszMeta ) + { + pszName = NULL; + pszValue = CPLParseNameValue( *papszMeta++, &pszName ); + if ( pszName != NULL && (SDsetattr( hSD, pszName, DFNT_CHAR8, + strlen(pszValue) + 1, pszValue )) < 0 ) + { + CPLDebug( "HDF4Image", + "Cannot write metadata information to output file"); + } + + CPLFree( pszName ); + } + } + + // Write out NoData values + for ( iBand = 1; iBand <= nBands; iBand++ ) + { + HDF4ImageRasterBand *poBand = + (HDF4ImageRasterBand *)GetRasterBand(iBand); + + if ( poBand->bNoDataSet ) + { + pszName = CPLStrdup( CPLSPrintf( "NoDataValue%d", iBand ) ); + pszValue = CPLSPrintf( "%f", poBand->dfNoDataValue ); + if ( (SDsetattr( hSD, pszName, DFNT_CHAR8, + strlen(pszValue) + 1, pszValue )) < 0 ) + { + CPLDebug( "HDF4Image", + "Cannot write NoData value for band %d " + "to output file", iBand); + } + + CPLFree( pszName ); + } + } + + // Write out band descriptions + for ( iBand = 1; iBand <= nBands; iBand++ ) + { + HDF4ImageRasterBand *poBand = + (HDF4ImageRasterBand *)GetRasterBand(iBand); + + pszName = CPLStrdup( CPLSPrintf( "BandDesc%d", iBand ) ); + pszValue = poBand->GetDescription(); + if ( pszValue != NULL && !EQUAL( pszValue, "" ) ) + { + if ( (SDsetattr( hSD, pszName, DFNT_CHAR8, + strlen(pszValue) + 1, pszValue )) < 0 ) + { + CPLDebug( "HDF4Image", + "Cannot write band's %d description to output file", + iBand); + } + } + + CPLFree( pszName ); + } +} + +/************************************************************************/ +/* USGSMnemonicToCode() */ +/************************************************************************/ + +long HDF4ImageDataset::USGSMnemonicToCode( const char* pszMnemonic ) +{ + if ( EQUAL(pszMnemonic, "UTM") ) + return 1L; + else if ( EQUAL(pszMnemonic, "LAMCC") ) + return 4L; + else if ( EQUAL(pszMnemonic, "PS") ) + return 6L; + else if ( EQUAL(pszMnemonic, "PC") ) + return 7L; + else if ( EQUAL(pszMnemonic, "TM") ) + return 9L; + else if ( EQUAL(pszMnemonic, "EQRECT") ) + return 17L; + else if ( EQUAL(pszMnemonic, "OM") ) + return 20L; + else if ( EQUAL(pszMnemonic, "SOM") ) + return 22L; + else + return 1L; // UTM by default +} + +/************************************************************************/ +/* ToGeoref() */ +/************************************************************************/ + +void HDF4ImageDataset::ToGeoref( double *pdfGeoX, double *pdfGeoY ) +{ + OGRCoordinateTransformation *poTransform = NULL; + OGRSpatialReference *poLatLong = NULL; + poLatLong = oSRS.CloneGeogCS(); + poTransform = OGRCreateCoordinateTransformation( poLatLong, &oSRS ); + + if( poTransform != NULL ) + poTransform->Transform( 1, pdfGeoX, pdfGeoY, NULL ); + + if( poTransform != NULL ) + delete poTransform; + + if( poLatLong != NULL ) + delete poLatLong; +} + +/************************************************************************/ +/* ReadCoordinates() */ +/************************************************************************/ + +void HDF4ImageDataset::ReadCoordinates( const char *pszString, + double *pdfX, double *pdfY ) +{ + char **papszStrList; + papszStrList = CSLTokenizeString2( pszString, ", ", 0 ); + *pdfX = CPLAtof( papszStrList[0] ); + *pdfY = CPLAtof( papszStrList[1] ); + CSLDestroy( papszStrList ); +} + +/************************************************************************/ +/* CaptureL1GMTLInfo() */ +/************************************************************************/ + +/* FILE L71002025_02520010722_M_MTL.L1G + +GROUP = L1_METADATA_FILE + ... + GROUP = PRODUCT_METADATA + PRODUCT_TYPE = "L1G" + PROCESSING_SOFTWARE = "IAS_5.1" + EPHEMERIS_TYPE = "DEFINITIVE" + SPACECRAFT_ID = "Landsat7" + SENSOR_ID = "ETM+" + ACQUISITION_DATE = 2001-07-22 + WRS_PATH = 002 + STARTING_ROW = 025 + ENDING_ROW = 025 + BAND_COMBINATION = "12345--7-" + PRODUCT_UL_CORNER_LAT = 51.2704805 + PRODUCT_UL_CORNER_LON = -53.8914311 + PRODUCT_UR_CORNER_LAT = 50.8458100 + PRODUCT_UR_CORNER_LON = -50.9869091 + PRODUCT_LL_CORNER_LAT = 49.6960897 + PRODUCT_LL_CORNER_LON = -54.4047933 + PRODUCT_LR_CORNER_LAT = 49.2841436 + PRODUCT_LR_CORNER_LON = -51.5900428 + PRODUCT_UL_CORNER_MAPX = 298309.894 + PRODUCT_UL_CORNER_MAPY = 5683875.631 + PRODUCT_UR_CORNER_MAPX = 500921.624 + PRODUCT_UR_CORNER_MAPY = 5632678.683 + PRODUCT_LL_CORNER_MAPX = 254477.193 + PRODUCT_LL_CORNER_MAPY = 5510407.880 + PRODUCT_LR_CORNER_MAPX = 457088.923 + PRODUCT_LR_CORNER_MAPY = 5459210.932 + PRODUCT_SAMPLES_REF = 6967 + PRODUCT_LINES_REF = 5965 + BAND1_FILE_NAME = "L71002025_02520010722_B10.L1G" + BAND2_FILE_NAME = "L71002025_02520010722_B20.L1G" + BAND3_FILE_NAME = "L71002025_02520010722_B30.L1G" + BAND4_FILE_NAME = "L71002025_02520010722_B40.L1G" + BAND5_FILE_NAME = "L71002025_02520010722_B50.L1G" + BAND7_FILE_NAME = "L72002025_02520010722_B70.L1G" + METADATA_L1_FILE_NAME = "L71002025_02520010722_MTL.L1G" + CPF_FILE_NAME = "L7CPF20010701_20010930_06" + HDF_DIR_FILE_NAME = "L71002025_02520010722_HDF.L1G" + END_GROUP = PRODUCT_METADATA + ... + GROUP = PROJECTION_PARAMETERS + REFERENCE_DATUM = "NAD83" + REFERENCE_ELLIPSOID = "GRS80" + GRID_CELL_SIZE_PAN = 15.000000 + GRID_CELL_SIZE_THM = 60.000000 + GRID_CELL_SIZE_REF = 30.000000 + ORIENTATION = "NOM" + RESAMPLING_OPTION = "CC" + MAP_PROJECTION = "UTM" + END_GROUP = PROJECTION_PARAMETERS + GROUP = UTM_PARAMETERS + ZONE_NUMBER = 22 + END_GROUP = UTM_PARAMETERS +END_GROUP = L1_METADATA_FILE +END +*/ + +void HDF4ImageDataset::CaptureL1GMTLInfo() + +{ +/* -------------------------------------------------------------------- */ +/* Does the physical file look like it matches our expected */ +/* name pattern? */ +/* -------------------------------------------------------------------- */ + if( strlen(pszFilename) < 8 + || !EQUAL(pszFilename+strlen(pszFilename)-8,"_HDF.L1G") ) + return; + +/* -------------------------------------------------------------------- */ +/* Construct the name of the corresponding MTL file. We should */ +/* likely be able to extract that from the HDF itself but I'm */ +/* not sure where to find it. */ +/* -------------------------------------------------------------------- */ + CPLString osMTLFilename = pszFilename; + osMTLFilename.resize(osMTLFilename.length() - 8); + osMTLFilename += "_MTL.L1G"; + +/* -------------------------------------------------------------------- */ +/* Ingest the MTL using the NASAKeywordHandler written for the */ +/* PDS driver. */ +/* -------------------------------------------------------------------- */ + NASAKeywordHandler oMTL; + + VSILFILE *fp = VSIFOpenL( osMTLFilename, "r" ); + if( fp == NULL ) + return; + + if( !oMTL.Ingest( fp, 0 ) ) + { + VSIFCloseL( fp ); + return; + } + + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Note: Different variation of MTL files use different group names. */ +/* Check for LPGS_METADATA_FILE and L1_METADATA_FILE. */ +/* -------------------------------------------------------------------- */ + double dfULX, dfULY, dfLRX, dfLRY; + double dfLLX, dfLLY, dfURX, dfURY; + CPLString osPrefix; + + if( oMTL.GetKeyword( "LPGS_METADATA_FILE.PRODUCT_METADATA" + ".PRODUCT_UL_CORNER_LON", NULL ) ) + osPrefix = "LPGS_METADATA_FILE.PRODUCT_METADATA.PRODUCT_"; + else if( oMTL.GetKeyword( "L1_METADATA_FILE.PRODUCT_METADATA" + ".PRODUCT_UL_CORNER_LON", NULL ) ) + osPrefix = "L1_METADATA_FILE.PRODUCT_METADATA.PRODUCT_"; + else + return; + + dfULX = CPLAtof(oMTL.GetKeyword((osPrefix+"UL_CORNER_LON").c_str(), "0" )); + dfULY = CPLAtof(oMTL.GetKeyword((osPrefix+"UL_CORNER_LAT").c_str(), "0" )); + dfLRX = CPLAtof(oMTL.GetKeyword((osPrefix+"LR_CORNER_LON").c_str(), "0" )); + dfLRY = CPLAtof(oMTL.GetKeyword((osPrefix+"LR_CORNER_LAT").c_str(), "0" )); + dfLLX = CPLAtof(oMTL.GetKeyword((osPrefix+"LL_CORNER_LON").c_str(), "0" )); + dfLLY = CPLAtof(oMTL.GetKeyword((osPrefix+"LL_CORNER_LAT").c_str(), "0" )); + dfURX = CPLAtof(oMTL.GetKeyword((osPrefix+"UR_CORNER_LON").c_str(), "0" )); + dfURY = CPLAtof(oMTL.GetKeyword((osPrefix+"UR_CORNER_LAT").c_str(), "0" )); + + CPLFree( pszGCPProjection ); + pszGCPProjection = CPLStrdup( "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]" ); + + nGCPCount = 4; + pasGCPList = (GDAL_GCP *) CPLCalloc( nGCPCount, sizeof( GDAL_GCP ) ); + GDALInitGCPs( nGCPCount, pasGCPList ); + + pasGCPList[0].dfGCPX = dfULX; + pasGCPList[0].dfGCPY = dfULY; + pasGCPList[0].dfGCPPixel = 0.0; + pasGCPList[0].dfGCPLine = 0.0; + + pasGCPList[1].dfGCPX = dfURX; + pasGCPList[1].dfGCPY = dfURY; + pasGCPList[1].dfGCPPixel = GetRasterXSize(); + pasGCPList[1].dfGCPLine = 0.0; + + pasGCPList[2].dfGCPX = dfLLX; + pasGCPList[2].dfGCPY = dfLLY; + pasGCPList[2].dfGCPPixel = 0.0; + pasGCPList[2].dfGCPLine = GetRasterYSize(); + + pasGCPList[3].dfGCPX = dfLRX; + pasGCPList[3].dfGCPY = dfLRY; + pasGCPList[3].dfGCPPixel = GetRasterXSize(); + pasGCPList[3].dfGCPLine = GetRasterYSize(); +} + +/************************************************************************/ +/* CaptureNRLGeoTransform() */ +/* */ +/* Capture geotransform and coordinate system from NRL (Naval */ +/* Research Laboratory, Stennis Space Center) metadata. */ +/************************************************************************/ + +/* Example metadata: +Metadata: + createTime=Fri Oct 1 18:00:07 2004 + createSoftware=APS v2.8.4 + createPlatform=i686-pc-linux-gnu + createAgency=Naval Research Laboratory, Stennis Space Center + sensor=MODIS + sensorPlatform=TERRA-AM + sensorAgency=NASA + sensorType=whiskbroom + sensorSpectrum=Visible/Thermal + sensorNumberOfBands=36 + sensorBandUnits=nano meters + sensorBands=645, 858.5, 469, 555, 1240, 1640, 2130, 412.5, 443, 488, 531, 551, + 667, 678, 748, 869.5, 905, 936, 940, 3750, 3959, 3959, 4050, 4465.5, 4515.5, 13 +75, 6715, 7325, 8550, 9730, 11130, 12020, 13335, 13635, 13935, 14235 + sensorBandWidths=50, 35, 20, 20, 20, 24, 50, 15, 10, 10, 10, 10, 10, 10, 10, 1 +5, 30, 10, 50, 90, 60, 60, 60, 65, 67, 30, 360, 300, 300, 300, 500, 500, 300, 30 +0, 300, 300 + sensorNominalAltitudeInKM=705 + sensorScanWidthInKM=2330 + sensorResolutionInKM=1 + sensorPlatformType=Polar-orbiting Satellite + timeStartYear=2004 + timeStartDay=275 + timeStartTime=56400000 + timeStart=Fri Oct 1 15:40:00 2004 + timeDayNight=Day + timeEndYear=2004 + timeEndDay=275 + timeEndTime=56700000 + timeEnd=Fri Oct 1 15:45:00 2004 + inputMasks=HIGLINT,CLDICE,LAND,ATMFAIL + inputMasksInt=523 + processedVersion=1.2 + file=MODAM2004275.L3_Mosaic_NOAA_GMX + fileTitle=NRL Level-3 Mosaic + fileVersion=3.0 + fileClassification=UNCLASSIFIED + fileStatus=EXPERIMENTAL + navType=mapped + mapProjectionSystem=NRL(USGS) + mapProjection=Gomex + mapUpperLeft=31, -99 + mapUpperRight=31, -79 + mapLowerLeft=14.9844128048645, -99 + mapLowerRight=14.9844128048645, -79 + inputFiles=MODAM2004275154000.L3_NOAA_GMX + ... + */ + +void HDF4ImageDataset::CaptureNRLGeoTransform() + +{ +/* -------------------------------------------------------------------- */ +/* Collect the four corners. */ +/* -------------------------------------------------------------------- */ + double adfXY[8]; + static const char *apszItems[] = { + "mapUpperLeft", "mapUpperRight", "mapLowerLeft", "mapLowerRight" }; + int iCorner; + int bLLPossible = TRUE; + + for( iCorner = 0; iCorner < 4; iCorner++ ) + { + const char *pszCornerLoc = + CSLFetchNameValue( papszGlobalMetadata, apszItems[iCorner] ); + + if( pszCornerLoc == NULL ) + return; + + char **papszTokens = CSLTokenizeStringComplex( pszCornerLoc, ",", + FALSE, FALSE ); + if( CSLCount( papszTokens ) != 2 ) + return; + + adfXY[iCorner*2+0] = CPLAtof( papszTokens[1] ); + adfXY[iCorner*2+1] = CPLAtof( papszTokens[0] ); + + if( adfXY[iCorner*2+0] < -360 || adfXY[iCorner*2+0] > 360 + || adfXY[iCorner*2+1] < -90 || adfXY[iCorner*2+1] > 90 ) + bLLPossible = FALSE; + + CSLDestroy( papszTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Does this look like nice clean "northup" lat/long data? */ +/* -------------------------------------------------------------------- */ + if( adfXY[0*2+0] == adfXY[2*2+0] && adfXY[0*2+1] == adfXY[1*2+1] + && bLLPossible ) + { + bHasGeoTransform = TRUE; + adfGeoTransform[0] = adfXY[0*2+0]; + adfGeoTransform[1] = (adfXY[1*2+0] - adfXY[0*2+0]) / nRasterXSize; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = adfXY[0*2+1]; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = (adfXY[2*2+1] - adfXY[0*2+1]) / nRasterYSize; + + oSRS.SetWellKnownGeogCS( "WGS84" ); + CPLFree( pszProjection ); + oSRS.exportToWkt( &pszProjection ); + } + +/* -------------------------------------------------------------------- */ +/* Can we find the USGS Projection Parameters? */ +/* -------------------------------------------------------------------- */ + int bGotGCTPProjection = FALSE; + int iSDSIndex = FAIL, iSDS = FAIL; + const char *mapProjection = CSLFetchNameValue( papszGlobalMetadata, + "mapProjection" ); + + if( mapProjection ) + iSDSIndex = SDnametoindex( hSD, mapProjection ); + + if( iSDSIndex != FAIL ) + iSDS = SDselect( hSD, iSDSIndex ); + + if( iSDS != FAIL ) + { + char szName[HDF4_SDS_MAXNAMELEN]; + int32 iRank, iNumType, nAttrs; + int32 aiDimSizes[H4_MAX_VAR_DIMS]; + + double adfGCTP[29]; + int32 aiStart[H4_MAX_NC_DIMS], aiEdges[H4_MAX_NC_DIMS]; + + aiStart[0] = 0; + aiEdges[0] = 29; + + if( SDgetinfo( iSDS, szName, &iRank, aiDimSizes, &iNumType, + &nAttrs) == 0 + && iNumType == DFNT_FLOAT64 + && iRank == 1 + && aiDimSizes[0] >= 29 + && SDreaddata( iSDS, aiStart, NULL, aiEdges, adfGCTP ) == 0 + && oSRS.importFromUSGS( (long) adfGCTP[1], (long) adfGCTP[2], + adfGCTP+4, + (long) adfGCTP[3] ) == OGRERR_NONE ) + { + CPLDebug( "HDF4Image", "GCTP Parms = %g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g", + adfGCTP[0], + adfGCTP[1], + adfGCTP[2], + adfGCTP[3], + adfGCTP[4], + adfGCTP[5], + adfGCTP[6], + adfGCTP[7], + adfGCTP[8], + adfGCTP[9], + adfGCTP[10], + adfGCTP[11], + adfGCTP[12], + adfGCTP[13], + adfGCTP[14], + adfGCTP[15], + adfGCTP[16], + adfGCTP[17], + adfGCTP[18], + adfGCTP[19], + adfGCTP[20], + adfGCTP[21], + adfGCTP[22], + adfGCTP[23], + adfGCTP[24], + adfGCTP[25], + adfGCTP[26], + adfGCTP[27], + adfGCTP[28] ); + + CPLFree( pszProjection ); + oSRS.exportToWkt( &pszProjection ); + bGotGCTPProjection = TRUE; + } + + SDendaccess(iSDS); + } + +/* -------------------------------------------------------------------- */ +/* If we derived a GCTP based projection, then we need to */ +/* transform the lat/long corners into this projection and use */ +/* them to establish the geotransform. */ +/* -------------------------------------------------------------------- */ + if( bLLPossible && bGotGCTPProjection ) + { + double dfULX, dfULY, dfLRX, dfLRY; + OGRSpatialReference oWGS84; + + oWGS84.SetWellKnownGeogCS( "WGS84" ); + + OGRCoordinateTransformation *poCT = + OGRCreateCoordinateTransformation( &oWGS84, &oSRS ); + + dfULX = adfXY[0*2+0]; + dfULY = adfXY[0*2+1]; + + dfLRX = adfXY[3*2+0]; + dfLRY = adfXY[3*2+1]; + + if( poCT->Transform( 1, &dfULX, &dfULY ) + && poCT->Transform( 1, &dfLRX, &dfLRY ) ) + { + bHasGeoTransform = TRUE; + adfGeoTransform[0] = dfULX; + adfGeoTransform[1] = (dfLRX - dfULX) / nRasterXSize; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = dfULY; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = (dfLRY - dfULY) / nRasterYSize; + } + + delete poCT; + } +} + +/************************************************************************/ +/* CaptureCoastwatchGCTPInfo() */ +/************************************************************************/ + +/* Example Metadata from: + + http://coastwatch.noaa.gov/interface/most_recent.php?sensor=MODIS&product=chlorNASA + +Definitions at: + http://coastwatch.noaa.gov/cw_form_hdf.html + +Metadata: + satellite=Aqua + sensor=MODIS + origin=USDOC/NOAA/NESDIS CoastWatch + history=PGE01:4.1.12;PGE02:4.3.1.12;SeaDAS Version ?.?, MSl12 4.0.2, Linux 2.4.21-27.0.1.EL +cwregister GulfOfMexicoSinusoidal.hdf MODSCW.P2005023.1835.swath09.hdf MODSCW.P2005023.1835.GM16.mapped09.hdf +cwgraphics MODSCW.P2005023.1835.GM16.closest.hdf +cwmath --template chlor_a --expr chlor_a=select(and(l2_flags,514)!=0,nan,chlor_a) /data/aps/browse/lvl3/seadas/coastwatch/hdf/MODSCW_P2005023_1835_GM16_closest.hdf /data/aps/browse/lvl3/seadas/coastwatch/maskhdf/MODSCW_P2005023_1835_GM16_closest_chlora.hdf +cwmath --template latitude --expr latitude=latitude /data/aps/browse/lvl3/seadas/coastwatch/hdf/MODSCW_P2005023_1835_GM16_closest.hdf /data/aps/browse/lvl3/seadas/coastwatch/maskhdf/MODSCW_P2005023_1835_GM16_closest_chlora.hdf +cwmath --template longitude --expr longitude=longitude /data/aps/browse/lvl3/seadas/coastwatch/hdf/MODSCW_P2005023_1835_GM16_closest.hdf /data/aps/browse/lvl3/seadas/coastwatch/maskhdf/MODSCW_P2005023_1835_GM16_closest_chlora.hdf + cwhdf_version=3.2 + pass_type=day + pass_date=12806 + start_time=66906 + temporal_extent=298 + projection_type=mapped + projection=Sinusoidal + gctp_sys=16 + gctp_zone=62 + gctp_parm=6378137, 0, 0, 0, -89000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + gctp_datum=12 + et_affine=0, -1008.74836097881, 1008.74836097881, 0, -953126.102425113, 3447041.10282512 + rows=1540 + cols=2000 + polygon_latitude=31, 31, 31, 31, 31, 27.5095879249529, 24.0191758499058, 20.5287637748587, 17.0383516998116, 17.0383516998116, 17.0383516998116, 17.0383516998116, 17.0383516998116, 20.5287637748587, 24.0191758499058, 27.5095879249529, 31 + polygon_longitude=-99, -93.7108573344442, -88.4217146688883, -83.1325720033325, -77.8434293377767, -78.217853417453, -78.5303805448579, -78.7884829057512, -78.9979508907244, -83.7397542896832, -88.481557688642, -93.2233610876007, -97.9651644865595, -98.1529175079091, -98.3842631146439, -98.664391423662, -99 + orbit_type=ascending + raster_type=RasterPixelIsArea + swath_sync_lines=1 + + */ + +void HDF4ImageDataset::CaptureCoastwatchGCTPInfo() + +{ + if( CSLFetchNameValue( papszGlobalMetadata, "gctp_sys" ) == NULL + || CSLFetchNameValue( papszGlobalMetadata, "gctp_zone" ) == NULL + || CSLFetchNameValue( papszGlobalMetadata, "gctp_parm" ) == NULL + || CSLFetchNameValue( papszGlobalMetadata, "gctp_datum" ) == NULL + || CSLFetchNameValue( papszGlobalMetadata, "et_affine" ) == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Grab USGS/GCTP Parameters. */ +/* -------------------------------------------------------------------- */ + int nSys, nZone, nDatum, iParm; + double adfParms[15]; + char **papszTokens; + + nSys = atoi( CSLFetchNameValue( papszGlobalMetadata, "gctp_sys" ) ); + nZone = atoi( CSLFetchNameValue( papszGlobalMetadata, "gctp_zone" ) ); + nDatum = atoi( CSLFetchNameValue( papszGlobalMetadata, "gctp_datum" ) ); + + papszTokens = CSLTokenizeStringComplex( + CSLFetchNameValue( papszGlobalMetadata, "gctp_parm" ), ",", + FALSE, FALSE ); + if( CSLCount(papszTokens) < 15 ) + return; + + for( iParm = 0; iParm < 15; iParm++ ) + adfParms[iParm] = CPLAtof( papszTokens[iParm] ); + CSLDestroy( papszTokens ); + +/* -------------------------------------------------------------------- */ +/* Convert into an SRS. */ +/* -------------------------------------------------------------------- */ + + if( oSRS.importFromUSGS( nSys, nZone, adfParms, nDatum ) != OGRERR_NONE ) + return; + + CPLFree( pszProjection ); + oSRS.exportToWkt( &pszProjection ); + +/* -------------------------------------------------------------------- */ +/* Capture the affine transform info. */ +/* -------------------------------------------------------------------- */ + + papszTokens = CSLTokenizeStringComplex( + CSLFetchNameValue( papszGlobalMetadata, "et_affine" ), ",", + FALSE, FALSE ); + if( CSLCount(papszTokens) != 6 ) + return; + + // We don't seem to have proper ef_affine docs so I don't + // know which of these two coefficients goes where. + if( CPLAtof(papszTokens[0]) != 0.0 || CPLAtof(papszTokens[3]) != 0.0 ) + return; + + bHasGeoTransform = TRUE; + adfGeoTransform[0] = CPLAtof( papszTokens[4] ); + adfGeoTransform[1] = CPLAtof( papszTokens[2] ); + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = CPLAtof( papszTokens[5] ); + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = CPLAtof( papszTokens[1] ); + + // Middle of pixel adjustment. + adfGeoTransform[0] -= adfGeoTransform[1] * 0.5; + adfGeoTransform[3] -= adfGeoTransform[5] * 0.5; +} + +/************************************************************************/ +/* GetImageDimensions() */ +/************************************************************************/ + +void HDF4ImageDataset::GetImageDimensions( char *pszDimList ) +{ + char **papszDimList = CSLTokenizeString2( pszDimList, ",", + CSLT_HONOURSTRINGS ); + int i, nDimCount = CSLCount( papszDimList ); + + // TODO: check whether nDimCount is > 1 and do something if it isn't. + + // Search for the "Band" word in the name of dimension + // or take the first one as a number of bands + if ( iRank == 2 ) + nBandCount = 1; + else + { + for ( i = 0; i < nDimCount; i++ ) + { + if ( strstr( papszDimList[i], "band" ) ) + { + iBandDim = i; + nBandCount = aiDimSizes[i]; + // Handle 4D datasets + if ( iRank > 3 && i < nDimCount - 1 ) + { + // FIXME: is there a better way to search for + // the 4th dimension? + i4Dim = i + 1; + nBandCount *= aiDimSizes[i4Dim]; + } + break; + } + } + } + + // Search for the starting "X" and "Y" in the names or take + // the last two dimensions as X and Y sizes + iXDim = nDimCount - 1; + iYDim = nDimCount - 2; + + for ( i = 0; i < nDimCount; i++ ) + { + if ( EQUALN( papszDimList[i], "X", 1 ) && iBandDim != i ) + iXDim = i; + else if ( EQUALN( papszDimList[i], "Y", 1 ) && iBandDim != i ) + iYDim = i; + } + + // If didn't get a band dimension yet, but have an extra + // dimension, use it as the band dimension. + if ( iRank > 2 && iBandDim == -1 ) + { + if( iXDim != 0 && iYDim != 0 ) + iBandDim = 0; + else if( iXDim != 1 && iYDim != 1 ) + iBandDim = 1; + else if( iXDim != 2 && iYDim != 2 ) + iBandDim = 2; + + nBandCount = aiDimSizes[iBandDim]; + } + + CSLDestroy( papszDimList ); +} + +/************************************************************************/ +/* GetSwatAttrs() */ +/************************************************************************/ + +void HDF4ImageDataset::GetSwatAttrs( int32 hSW ) +{ +/* -------------------------------------------------------------------- */ +/* At the start we will fetch the global HDF attributes. */ +/* -------------------------------------------------------------------- */ + int32 hDummy; + + EHidinfo( hHDF4, &hDummy, &hSD ); + ReadGlobalAttributes( hSD ); + papszLocalMetadata = CSLDuplicate( papszGlobalMetadata ); + +/* -------------------------------------------------------------------- */ +/* Fetch the esoteric HDF-EOS attributes then. */ +/* -------------------------------------------------------------------- */ + int32 nStrBufSize = 0; + + if ( SWinqattrs( hSW, NULL, &nStrBufSize ) > 0 && nStrBufSize > 0 ) + { + char *pszAttrList; + char **papszAttributes; + int i, nAttrs; + + pszAttrList = (char *)CPLMalloc( nStrBufSize + 1 ); + SWinqattrs( hSW, pszAttrList, &nStrBufSize ); + +#ifdef DEBUG + CPLDebug( "HDF4Image", "List of attributes in swath \"%s\": %s", + pszFieldName, pszAttrList ); +#endif + + papszAttributes = CSLTokenizeString2( pszAttrList, ",", + CSLT_HONOURSTRINGS ); + nAttrs = CSLCount( papszAttributes ); + for ( i = 0; i < nAttrs; i++ ) + { + int32 iNumType, nValues; + void *pData = NULL; + char *pszTemp = NULL; + + SWattrinfo( hSW, papszAttributes[i], &iNumType, &nValues ); + + if ( iNumType == DFNT_CHAR8 || iNumType == DFNT_UCHAR8 ) + pData = CPLMalloc( (nValues + 1) * GetDataTypeSize(iNumType) ); + else + pData = CPLMalloc( nValues * GetDataTypeSize(iNumType) ); + + SWreadattr( hSW, papszAttributes[i], pData ); + + if ( iNumType == DFNT_CHAR8 || iNumType == DFNT_UCHAR8 ) + { + ((char *)pData)[nValues] = '\0'; + papszLocalMetadata = CSLAddNameValue( papszLocalMetadata, + papszAttributes[i], + (const char *) pData ); + } + else + { + pszTemp = SPrintArray( GetDataType(iNumType), pData, + nValues, ", " ); + papszLocalMetadata = CSLAddNameValue( papszLocalMetadata, + papszAttributes[i], + pszTemp ); + if ( pszTemp ) + CPLFree( pszTemp ); + } + + if ( pData ) + CPLFree( pData ); + + } + + CSLDestroy( papszAttributes ); + CPLFree( pszAttrList ); + } + +/* -------------------------------------------------------------------- */ +/* After fetching HDF-EOS specific stuff we will read the generic */ +/* HDF attributes and append them to the list of metadata. */ +/* -------------------------------------------------------------------- */ + int32 iSDS; + if ( SWsdid(hSW, pszFieldName, &iSDS) != -1 ) + { + int32 iRank, iNumType, iAttribute, nAttrs, nValues; + char szName[HDF4_SDS_MAXNAMELEN]; + int32 aiDimSizes[H4_MAX_VAR_DIMS]; + + if( SDgetinfo( iSDS, szName, &iRank, aiDimSizes, &iNumType, + &nAttrs) == 0 ) + { + for ( iAttribute = 0; iAttribute < nAttrs; iAttribute++ ) + { + char szAttrName[H4_MAX_NC_NAME]; + SDattrinfo( iSDS, iAttribute, szAttrName, + &iNumType, &nValues ); + papszLocalMetadata = + TranslateHDF4Attributes( iSDS, iAttribute, + szAttrName, iNumType, + nValues, papszLocalMetadata ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Finally make the whole list visible. */ +/* -------------------------------------------------------------------- */ + SetMetadata( papszLocalMetadata ); +} + +/************************************************************************/ +/* GetGridAttrs() */ +/************************************************************************/ + +void HDF4ImageDataset::GetGridAttrs( int32 hGD ) +{ +/* -------------------------------------------------------------------- */ +/* At the start we will fetch the global HDF attributes. */ +/* -------------------------------------------------------------------- */ + int32 hDummy; + + EHidinfo( hHDF4, &hDummy, &hSD ); + ReadGlobalAttributes( hSD ); + papszLocalMetadata = CSLDuplicate( papszGlobalMetadata ); + +/* -------------------------------------------------------------------- */ +/* Fetch the esoteric HDF-EOS attributes then. */ +/* -------------------------------------------------------------------- */ + int32 nStrBufSize = 0; + + if ( GDinqattrs( hGD, NULL, &nStrBufSize ) > 0 && nStrBufSize > 0 ) + { + char *pszAttrList; + char **papszAttributes; + int i, nAttrs; + + pszAttrList = (char *)CPLMalloc( nStrBufSize + 1 ); + GDinqattrs( hGD, pszAttrList, &nStrBufSize ); +#ifdef DEBUG + CPLDebug( "HDF4Image", "List of attributes in grid %s: %s", + pszFieldName, pszAttrList ); +#endif + papszAttributes = CSLTokenizeString2( pszAttrList, ",", + CSLT_HONOURSTRINGS ); + nAttrs = CSLCount( papszAttributes ); + for ( i = 0; i < nAttrs; i++ ) + { + int32 iNumType, nValues; + void *pData = NULL; + char *pszTemp = NULL; + + GDattrinfo( hGD, papszAttributes[i], &iNumType, &nValues ); + + if ( iNumType == DFNT_CHAR8 || iNumType == DFNT_UCHAR8 ) + pData = CPLMalloc( (nValues + 1) * GetDataTypeSize(iNumType) ); + else + pData = CPLMalloc( nValues * GetDataTypeSize(iNumType) ); + + GDreadattr( hGD, papszAttributes[i], pData ); + + if ( iNumType == DFNT_CHAR8 || iNumType == DFNT_UCHAR8 ) + { + ((char *)pData)[nValues] = '\0'; + papszLocalMetadata = CSLAddNameValue( papszLocalMetadata, + papszAttributes[i], + (const char *) pData ); + } + else + { + pszTemp = SPrintArray( GetDataType(iNumType), pData, + nValues, ", " ); + papszLocalMetadata = CSLAddNameValue( papszLocalMetadata, + papszAttributes[i], + pszTemp ); + if ( pszTemp ) + CPLFree( pszTemp ); + } + + if ( pData ) + CPLFree( pData ); + + } + + CSLDestroy( papszAttributes ); + CPLFree( pszAttrList ); + } + +/* -------------------------------------------------------------------- */ +/* After fetching HDF-EOS specific stuff we will read the generic */ +/* HDF attributes and append them to the list of metadata. */ +/* -------------------------------------------------------------------- */ + int32 iSDS; + if ( GDsdid(hGD, pszFieldName, &iSDS) != -1 ) + { + int32 iRank, iNumType, iAttribute, nAttrs, nValues; + char szName[HDF4_SDS_MAXNAMELEN]; + int32 aiDimSizes[H4_MAX_VAR_DIMS]; + + if( SDgetinfo( iSDS, szName, &iRank, aiDimSizes, &iNumType, + &nAttrs) == 0 ) + { + for ( iAttribute = 0; iAttribute < nAttrs; iAttribute++ ) + { + char szAttrName[H4_MAX_NC_NAME]; + SDattrinfo( iSDS, iAttribute, szAttrName, + &iNumType, &nValues ); + papszLocalMetadata = + TranslateHDF4Attributes( iSDS, iAttribute, + szAttrName, iNumType, + nValues, papszLocalMetadata ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Finally make the whole list visible. */ +/* -------------------------------------------------------------------- */ + SetMetadata( papszLocalMetadata ); +} + +/************************************************************************/ +/* ProcessModisSDSGeolocation() */ +/* */ +/* Recognise latitude and longitude geolocation arrays in */ +/* simple SDS datasets like: */ +/* */ +/* download.osgeo.org/gdal/data/hdf4/A2006005182000.L2_LAC_SST.x.hdf */ +/* */ +/* As reported in ticket #1895. */ +/************************************************************************/ + +void HDF4ImageDataset::ProcessModisSDSGeolocation(void) + +{ + int iDSIndex, iXIndex=-1, iYIndex=-1; + + // No point in assigning geolocation to the geolocation SDSes themselves. + if( EQUAL(szName,"longitude") || EQUAL(szName,"latitude") ) + return; + + if (nRasterYSize == 1) + return; + +/* -------------------------------------------------------------------- */ +/* Scan for latitude and longitude sections. */ +/* -------------------------------------------------------------------- */ + int32 nDatasets, nAttributes; + + if ( SDfileinfo( hSD, &nDatasets, &nAttributes ) != 0 ) + return; + + + int nLongitudeWidth = 0, nLongitudeHeight = 0; + int nLatitudeWidth = 0, nLatitudeHeight = 0; + for( iDSIndex = 0; iDSIndex < nDatasets; iDSIndex++ ) + { + int32 iRank, iNumType, nAttrs, iSDS; + char szName[HDF4_SDS_MAXNAMELEN]; + int32 aiDimSizes[H4_MAX_VAR_DIMS]; + + iSDS = SDselect( hSD, iDSIndex ); + + if( SDgetinfo( iSDS, szName, &iRank, aiDimSizes, &iNumType, + &nAttrs) == 0 ) + { + if( EQUAL(szName,"latitude") ) + { + iYIndex = iDSIndex; + if( iRank == 2 ) + { + nLatitudeWidth = aiDimSizes[1]; + nLatitudeHeight = aiDimSizes[0]; + } + } + + if( EQUAL(szName,"longitude") ) + { + iXIndex = iDSIndex; + if( iRank == 2 ) + { + nLongitudeWidth = aiDimSizes[1]; + nLongitudeHeight = aiDimSizes[0]; + } + } + } + + SDendaccess(iSDS); + } + + if( iXIndex == -1 || iYIndex == -1 ) + return; + + int nPixelOffset = 0, nLineOffset = 0; + int nPixelStep = 1, nLineStep = 1; + if( nLongitudeWidth != nLatitudeWidth || nLongitudeHeight != nLatitudeHeight ) + { + CPLDebug("HDF4", "Longitude and latitude subdatasets don't have same dimensions..."); + } + else if( nLongitudeWidth > 0 && nLongitudeHeight > 0 ) + { + nPixelStep = (int)(0.5 + 1.0 * nRasterXSize / nLongitudeWidth); + nLineStep = (int)(0.5 + 1.0 * nRasterYSize / nLongitudeHeight); + nPixelOffset = (nPixelStep-1) / 2; + nLineOffset = (nLineStep-1) / 2; + } + +/* -------------------------------------------------------------------- */ +/* We found geolocation information. Record it as metadata. */ +/* -------------------------------------------------------------------- */ + CPLString osWrk; + + SetMetadataItem( "SRS", SRS_WKT_WGS84, "GEOLOCATION" ); + + osWrk.Printf( "HDF4_SDS:UNKNOWN:\"%s\":%d", + pszFilename, iXIndex ); + SetMetadataItem( "X_DATASET", osWrk, "GEOLOCATION" ); + SetMetadataItem( "X_BAND", "1" , "GEOLOCATION" ); + + osWrk.Printf( "HDF4_SDS:UNKNOWN:\"%s\":%d", + pszFilename, iYIndex ); + SetMetadataItem( "Y_DATASET", osWrk, "GEOLOCATION" ); + SetMetadataItem( "Y_BAND", "1" , "GEOLOCATION" ); + + SetMetadataItem( "PIXEL_OFFSET", CPLSPrintf("%d", nPixelOffset), "GEOLOCATION" ); + SetMetadataItem( "PIXEL_STEP", CPLSPrintf("%d", nPixelStep), "GEOLOCATION" ); + + SetMetadataItem( "LINE_OFFSET", CPLSPrintf("%d", nLineOffset), "GEOLOCATION" ); + SetMetadataItem( "LINE_STEP", CPLSPrintf("%d", nLineStep), "GEOLOCATION" ); +} + +/************************************************************************/ +/* ProcessSwathGeolocation() */ +/* */ +/* Handle the swath geolocation data for a swath. Attach */ +/* geolocation metadata corresponding to it (if there is no */ +/* lattice), and also attach it as GCPs. This is only invoked */ +/* for EOS_SWATH, not EOS_SWATH_GEOL datasets. */ +/************************************************************************/ + +int HDF4ImageDataset::ProcessSwathGeolocation( int32 hSW, char **papszDimList ) +{ + char szXGeo[8192] = ""; + char szYGeo[8192] = ""; + char szPixel[8192]= ""; + char szLine[8192] = ""; + int32 iWrkNumType; + void *pLat = NULL, *pLong = NULL; + void *pLatticeX = NULL, *pLatticeY = NULL; + int32 iLatticeType, iLatticeDataSize = 0, iRank; + int32 nLatCount = 0, nLongCount = 0; + int32 nXPoints=0, nYPoints=0; + int32 nStrBufSize; + int i, j, iDataSize = 0, iPixelDim=-1,iLineDim=-1, iLongDim=-1, iLatDim=-1; + int32 *paiRank = NULL, *paiNumType = NULL, + *paiOffset = NULL, *paiIncrement = NULL; + +/* -------------------------------------------------------------------- */ +/* Determine a product name. */ +/* -------------------------------------------------------------------- */ + const char *pszProduct = + CSLFetchNameValue( papszLocalMetadata, "SHORTNAME" ); + + HDF4EOSProduct eProduct = PROD_UNKNOWN; + if ( pszProduct ) + { + if ( EQUALN(pszProduct, "ASTL1A", 6) ) + eProduct = PROD_ASTER_L1A; + else if ( EQUALN(pszProduct, "ASTL1B", 6) ) + eProduct = PROD_ASTER_L1B; + else if ( EQUALN(pszProduct, "AST_04", 6) + || EQUALN(pszProduct, "AST_05", 6) + || EQUALN(pszProduct, "AST_06", 6) + || EQUALN(pszProduct, "AST_07", 6) + || EQUALN(pszProduct, "AST_08", 6) + || EQUALN(pszProduct, "AST_09", 6) + || EQUALN(pszProduct, "AST13", 5) + || EQUALN(pszProduct, "AST3", 4) ) + eProduct = PROD_ASTER_L2; + else if ( EQUALN(pszProduct, "AST14", 5) ) + eProduct = PROD_ASTER_L3; + else if ( EQUALN(pszProduct, "MOD02", 5) + || EQUALN(pszProduct, "MYD02", 5) ) + eProduct = PROD_MODIS_L1B; + else if ( EQUALN(pszProduct, "MOD07_L2", 8) ) + eProduct = PROD_MODIS_L2; + } + +/* -------------------------------------------------------------------- */ +/* Read names of geolocation fields and corresponding */ +/* geolocation maps. */ +/* -------------------------------------------------------------------- */ + int32 nDataFields = SWnentries( hSW, HDFE_NENTGFLD, &nStrBufSize ); + char *pszGeoList = (char *)CPLMalloc( nStrBufSize + 1 ); + paiRank = (int32 *)CPLMalloc( nDataFields * sizeof(int32) ); + paiNumType = (int32 *)CPLMalloc( nDataFields * sizeof(int32) ); + + if ( nDataFields != + SWinqgeofields(hSW, pszGeoList, paiRank, paiNumType) ) + { + CPLDebug( "HDF4Image", + "Can't get the list of geolocation fields in swath \"%s\"", + pszSubdatasetName ); + } + +#ifdef DEBUG + else + { + char *pszTmp; + CPLDebug( "HDF4Image", + "Number of geolocation fields in swath \"%s\": %ld", + pszSubdatasetName, (long)nDataFields ); + CPLDebug( "HDF4Image", + "List of geolocation fields in swath \"%s\": %s", + pszSubdatasetName, pszGeoList ); + pszTmp = SPrintArray( GDT_UInt32, paiRank, + nDataFields, "," ); + CPLDebug( "HDF4Image", + "Geolocation fields ranks: %s", pszTmp ); + CPLFree( pszTmp ); + } +#endif + + CPLFree( paiRank ); + CPLFree( paiNumType ); + +/* -------------------------------------------------------------------- */ +/* Read geolocation data. */ +/* -------------------------------------------------------------------- */ + int32 nDimMaps = SWnentries( hSW, HDFE_NENTMAP, &nStrBufSize ); + if ( nDimMaps <= 0 ) + { + +#ifdef DEBUG + CPLDebug( "HDF4Image", "No geolocation maps in swath \"%s\"", + pszSubdatasetName ); + CPLDebug( "HDF4Image", + "Suppose one-to-one mapping. X field is \"%s\", Y field is \"%s\"", + papszDimList[iXDim], papszDimList[iYDim] ); +#endif + + strncpy( szPixel, papszDimList[iXDim], 8192 ); + strncpy( szLine, papszDimList[iYDim], 8192 ); + strncpy( szXGeo, papszDimList[iXDim], 8192 ); + strncpy( szYGeo, papszDimList[iYDim], 8192 ); + paiOffset = (int32 *)CPLCalloc( 2, sizeof(int32) ); + paiIncrement = (int32 *)CPLCalloc( 2, sizeof(int32) ); + paiOffset[0] = paiOffset[1] = 0; + paiIncrement[0] = paiIncrement[1] = 1; + } + else + { + char *pszDimMaps = (char *)CPLMalloc( nStrBufSize + 1 ); + paiOffset = (int32 *)CPLCalloc( nDimMaps, sizeof(int32) ); + paiIncrement = (int32 *)CPLCalloc( nDimMaps, sizeof(int32) ); + + *pszDimMaps = '\0'; + if ( nDimMaps != SWinqmaps(hSW, pszDimMaps, paiOffset, paiIncrement) ) + { + CPLDebug( "HDF4Image", + "Can't get the list of geolocation maps in swath \"%s\"", + pszSubdatasetName ); + } + +#ifdef DEBUG + else + { + char *pszTmp; + + CPLDebug( "HDF4Image", + "List of geolocation maps in swath \"%s\": %s", + pszSubdatasetName, pszDimMaps ); + pszTmp = SPrintArray( GDT_Int32, paiOffset, + nDimMaps, "," ); + CPLDebug( "HDF4Image", + "Geolocation map offsets: %s", pszTmp ); + CPLFree( pszTmp ); + pszTmp = SPrintArray( GDT_Int32, paiIncrement, + nDimMaps, "," ); + CPLDebug( "HDF4Image", + "Geolocation map increments: %s", pszTmp ); + CPLFree( pszTmp ); + } +#endif + + char **papszDimMap = CSLTokenizeString2( pszDimMaps, ",", + CSLT_HONOURSTRINGS ); + int nDimMapCount = CSLCount(papszDimMap); + + for ( i = 0; i < nDimMapCount; i++ ) + { + if ( strstr(papszDimMap[i], papszDimList[iXDim]) ) + { + strncpy( szPixel, papszDimList[iXDim], 8192 ); + strncpy( szXGeo, papszDimMap[i], 8192 ); + char *pszTemp = strchr( szXGeo, '/' ); + if ( pszTemp ) + *pszTemp = '\0'; + } + else if ( strstr(papszDimMap[i], papszDimList[iYDim]) ) + { + strncpy( szLine, papszDimList[iYDim], 8192 ); + strncpy( szYGeo, papszDimMap[i], 8192 ); + char *pszTemp = strchr( szYGeo, '/' ); + if ( pszTemp ) + *pszTemp = '\0'; + } + } + + CSLDestroy( papszDimMap ); + CPLFree( pszDimMaps ); + } + + if ( *szXGeo == 0 || *szYGeo == 0 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read geolocation fields. */ +/* -------------------------------------------------------------------- */ + char szGeoDimList[8192] = ""; + char **papszGeolocations = CSLTokenizeString2( pszGeoList, ",", + CSLT_HONOURSTRINGS ); + int nGeolocationsCount = CSLCount( papszGeolocations ); + int32 aiDimSizes[H4_MAX_VAR_DIMS]; + + for ( i = 0; i < nGeolocationsCount; i++ ) + { + char **papszGeoDimList = NULL; + + // Skip "SceneLineNumber" table if present in the list of geolocation + // fields. It is not needed to fetch geocoding data. + if ( EQUAL(papszGeolocations[i], "SceneLineNumber") ) + continue; + + if ( SWfieldinfo( hSW, papszGeolocations[i], &iRank, + aiDimSizes, &iWrkNumType, szGeoDimList ) < 0 ) + { + + CPLDebug( "HDF4Image", + "Can't read attributes of geolocation field \"%s\"", + papszGeolocations[i] ); + return FALSE; + } + + CPLDebug( "HDF4Image", + "List of dimensions in geolocation field \"%s\": %s", + papszGeolocations[i], szGeoDimList ); + + papszGeoDimList = CSLTokenizeString2( szGeoDimList, + ",", CSLT_HONOURSTRINGS ); + + if( CSLCount(papszGeoDimList) > H4_MAX_VAR_DIMS + || CSLFindString( papszGeoDimList, szXGeo ) == -1 + || CSLFindString( papszGeoDimList, szYGeo ) == -1 ) + { + CSLDestroy( papszGeoDimList ); + return FALSE; + } + + nXPoints = aiDimSizes[CSLFindString( papszGeoDimList, szXGeo )]; + nYPoints = aiDimSizes[CSLFindString( papszGeoDimList, szYGeo )]; + + if ( EQUAL(szPixel, papszDimList[iXDim]) ) + { + iPixelDim = 1; + iLineDim = 0; + } + else + { + iPixelDim = 0; + iLineDim = 1; + } + + iDataSize = GetDataTypeSize( iWrkNumType ); + if ( strstr( papszGeolocations[i], "Latitude" ) ) + { + iLatDim = i; + nLatCount = nXPoints * nYPoints; + pLat = CPLMalloc( nLatCount * iDataSize ); + if (SWreadfield( hSW, papszGeolocations[i], NULL, + NULL, NULL, (VOIDP)pLat ) < 0) + { + CPLDebug( "HDF4Image", + "Can't read geolocation field %s", + papszGeolocations[i]); + CPLFree( pLat ); + pLat = NULL; + } + } + else if ( strstr( papszGeolocations[i], "Longitude" ) ) + { + iLongDim = i; + nLongCount = nXPoints * nYPoints; + pLong = CPLMalloc( nLongCount * iDataSize ); + if (SWreadfield( hSW, papszGeolocations[i], NULL, + NULL, NULL, (VOIDP)pLong ) < 0) + { + CPLDebug( "HDF4Image", + "Can't read geolocation field %s", + papszGeolocations[i]); + CPLFree( pLong ); + pLong = NULL; + } + } + + CSLDestroy( papszGeoDimList ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a lattice table? */ +/* -------------------------------------------------------------------- */ + if (SWfieldinfo(hSW, "LatticePoint", &iRank, aiDimSizes, + &iLatticeType, szGeoDimList) == 0 + && iRank == 3 + && nXPoints == aiDimSizes[1] + && nYPoints == aiDimSizes[0] + && aiDimSizes[2] == 2 ) + { + int32 iStart[H4_MAX_NC_DIMS], iEdges[H4_MAX_NC_DIMS]; + + iLatticeDataSize = + GetDataTypeSize( iLatticeType ); + + iStart[1] = 0; + iEdges[1] = nXPoints; + + iStart[0] = 0; + iEdges[0] = nYPoints; + + iStart[2] = 0; + iEdges[2] = 1; + + pLatticeX = CPLMalloc( nLatCount * iLatticeDataSize ); + if (SWreadfield( hSW, "LatticePoint", iStart, NULL, + iEdges, (VOIDP)pLatticeX ) < 0) + { + CPLDebug( "HDF4Image", "Can't read lattice field" ); + CPLFree( pLatticeX ); + pLatticeX = NULL; + } + + iStart[2] = 1; + iEdges[2] = 1; + + pLatticeY = CPLMalloc( nLatCount * iLatticeDataSize ); + if (SWreadfield( hSW, "LatticePoint", iStart, NULL, + iEdges, (VOIDP)pLatticeY ) < 0) + { + CPLDebug( "HDF4Image", "Can't read lattice field" ); + CPLFree( pLatticeY ); + pLatticeY = NULL; + } + + } + +/* -------------------------------------------------------------------- */ +/* Determine whether to use no, partial or full GCPs. */ +/* -------------------------------------------------------------------- */ + const char *pszGEOL_AS_GCPS = CPLGetConfigOption( "GEOL_AS_GCPS", + "PARTIAL" ); + int iGCPStepX, iGCPStepY; + + if( EQUAL(pszGEOL_AS_GCPS,"NONE") ) + { + iGCPStepX = iGCPStepY = 0; + } + else if( EQUAL(pszGEOL_AS_GCPS,"FULL") ) + { + iGCPStepX = iGCPStepY = 1; + } + else + { + // aim for 10x10 grid or so. + iGCPStepX = MAX(1,((nXPoints-1) / 11)); + iGCPStepY = MAX(1,((nYPoints-1) / 11)); + } + +/* -------------------------------------------------------------------- */ +/* Fetch projection information for various datasets. */ +/* -------------------------------------------------------------------- */ + if ( nLatCount && nLongCount && nLatCount == nLongCount + && pLat && pLong ) + { + CPLFree( pszGCPProjection ); + pszGCPProjection = NULL; + + // ASTER Level 1A + if ( eProduct == PROD_ASTER_L1A ) + { + pszGCPProjection = CPLStrdup( "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],AUTHORITY[\"EPSG\",\"4326\"]]" ); + } + + // ASTER Level 1B, Level 2 + else if ( eProduct == PROD_ASTER_L1B + || eProduct == PROD_ASTER_L2 ) + { + // Constuct the metadata keys. + // A band number is taken from the field name. + const char *pszBand = strpbrk( pszFieldName, "0123456789" ); + + if ( !pszBand ) + pszBand = ""; + + char *pszProjLine = + CPLStrdup(CPLSPrintf("MPMETHOD%s", pszBand)); + char *pszParmsLine = + CPLStrdup(CPLSPrintf("PROJECTIONPARAMETERS%s", + pszBand)); + char *pszZoneLine = + CPLStrdup(CPLSPrintf("UTMZONECODE%s", + pszBand)); + char *pszEllipsoidLine = + CPLStrdup(CPLSPrintf("ELLIPSOIDANDDATUM%s", + pszBand)); + + // Fetch projection related values from the + // metadata. + const char *pszProj = + CSLFetchNameValue( papszLocalMetadata, + pszProjLine ); + const char *pszParms = + CSLFetchNameValue( papszLocalMetadata, + pszParmsLine ); + const char *pszZone = + CSLFetchNameValue( papszLocalMetadata, + pszZoneLine ); + const char* pszEllipsoid = + CSLFetchNameValue( papszLocalMetadata, + pszEllipsoidLine ); + +#ifdef DEBUG + CPLDebug( "HDF4Image", + "Projection %s=%s, parameters %s=%s, " + "zone %s=%s", + pszProjLine, pszProj, pszParmsLine, + pszParms, pszZoneLine, pszZone ); + CPLDebug( "HDF4Image", "Ellipsoid %s=%s", + pszEllipsoidLine, pszEllipsoid ); +#endif + + // Transform all mnemonical codes in the values. + int i, nParms; + // Projection is UTM by default + long iProjSys = (pszProj) ? + USGSMnemonicToCode(pszProj) : 1L; + long iZone = + (pszZone && iProjSys == 1L) ? atoi(pszZone): 0L; + char **papszEllipsoid = (pszEllipsoid) ? + CSLTokenizeString2( pszEllipsoid, ",", + CSLT_HONOURSTRINGS ) : NULL; + + long iEllipsoid = 8L; // WGS84 by default + if ( papszEllipsoid + && CSLCount(papszEllipsoid) > 0 ) + { + if (EQUAL( papszEllipsoid[0], "WGS84")) + iEllipsoid = 8L; + } + + double adfProjParms[15]; + char **papszParms = (pszParms) ? + CSLTokenizeString2( pszParms, ",", + CSLT_HONOURSTRINGS ) : NULL; + nParms = CSLCount(papszParms); + if (nParms >= 15) + nParms = 15; + for (i = 0; i < nParms; i++) + adfProjParms[i] = CPLAtof( papszParms[i] ); + for (; i < 15; i++) + adfProjParms[i] = 0.0; + + // Create projection definition + oSRS.importFromUSGS( iProjSys, iZone, + adfProjParms, iEllipsoid ); + oSRS.SetLinearUnits( SRS_UL_METER, 1.0 ); + oSRS.exportToWkt( &pszGCPProjection ); + + CSLDestroy( papszParms ); + CPLFree( pszEllipsoidLine ); + CPLFree( pszZoneLine ); + CPLFree( pszParmsLine ); + CPLFree( pszProjLine ); + } + + // ASTER Level 3 (DEM) + else if ( eProduct == PROD_ASTER_L3 ) + { + double dfCenterX, dfCenterY; + int iZone; + + ReadCoordinates( CSLFetchNameValue( + papszGlobalMetadata, "SCENECENTER" ), + &dfCenterY, &dfCenterX ); + + // Calculate UTM zone from scene center coordinates + iZone = 30 + (int) ((dfCenterX + 6.0) / 6.0); + + // Create projection definition + if( dfCenterY > 0 ) + oSRS.SetUTM( iZone, TRUE ); + else + oSRS.SetUTM( - iZone, FALSE ); + oSRS.SetWellKnownGeogCS( "WGS84" ); + oSRS.SetLinearUnits( SRS_UL_METER, 1.0 ); + oSRS.exportToWkt( &pszGCPProjection ); + } + + // MODIS L1B + else if ( eProduct == PROD_MODIS_L1B + || eProduct == PROD_MODIS_L2 ) + { + pszGCPProjection = CPLStrdup( SRS_WKT_WGS84 ); + } + +/* -------------------------------------------------------------------- */ +/* Fill the GCPs list. */ +/* -------------------------------------------------------------------- */ + if( iGCPStepX > 0 ) + { + nGCPCount = (((nXPoints-1) / iGCPStepX) + 1) + * (((nYPoints-1) / iGCPStepY) + 1); + + pasGCPList = (GDAL_GCP *) CPLCalloc( nGCPCount, sizeof( GDAL_GCP ) ); + GDALInitGCPs( nGCPCount, pasGCPList ); + + int iGCP = 0; + for ( i = 0; i < nYPoints; i += iGCPStepY ) + { + for ( j = 0; j < nXPoints; j += iGCPStepX ) + { + int iGeoOff = i * nXPoints + j; + + pasGCPList[iGCP].dfGCPX = + AnyTypeToDouble(iWrkNumType, + (void *)((char*)pLong+ iGeoOff*iDataSize)); + pasGCPList[iGCP].dfGCPY = + AnyTypeToDouble(iWrkNumType, + (void *)((char*)pLat + iGeoOff*iDataSize)); + + // GCPs in Level 1A/1B dataset are in geocentric + // coordinates. Convert them in geodetic (we + // will convert latitudes only, longitudes + // do not need to be converted, because + // they are the same). + // This calculation valid for WGS84 datum only. + if ( eProduct == PROD_ASTER_L1A + || eProduct == PROD_ASTER_L1B ) + { + pasGCPList[iGCP].dfGCPY = + atan(tan(pasGCPList[iGCP].dfGCPY + *PI/180)/0.99330562)*180/PI; + } + + ToGeoref(&pasGCPList[iGCP].dfGCPX, + &pasGCPList[iGCP].dfGCPY); + + pasGCPList[iGCP].dfGCPZ = 0.0; + + if ( pLatticeX && pLatticeY ) + { + pasGCPList[iGCP].dfGCPPixel = + AnyTypeToDouble(iLatticeType, + (void *)((char *)pLatticeX + + iGeoOff*iLatticeDataSize))+0.5; + pasGCPList[iGCP].dfGCPLine = + AnyTypeToDouble(iLatticeType, + (void *)((char *)pLatticeY + + iGeoOff*iLatticeDataSize))+0.5; + } + else if ( paiOffset && paiIncrement ) + { + pasGCPList[iGCP].dfGCPPixel = + paiOffset[iPixelDim] + + j * paiIncrement[iPixelDim] + 0.5; + pasGCPList[iGCP].dfGCPLine = + paiOffset[iLineDim] + + i * paiIncrement[iLineDim] + 0.5; + } + + iGCP++; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Establish geolocation metadata, but only if there is no */ +/* lattice. The lattice destroys the regularity of the grid. */ +/* -------------------------------------------------------------------- */ + if( pLatticeX == NULL + && iLatDim != -1 && iLongDim != -1 + && iPixelDim != -1 && iLineDim != -1 ) + { + CPLString osWrk; + + SetMetadataItem( "SRS", pszGCPProjection, "GEOLOCATION" ); + + osWrk.Printf( "HDF4_EOS:EOS_SWATH_GEOL:\"%s\":%s:%s", + pszFilename, pszSubdatasetName, + papszGeolocations[iLongDim] ); + SetMetadataItem( "X_DATASET", osWrk, "GEOLOCATION" ); + SetMetadataItem( "X_BAND", "1" , "GEOLOCATION" ); + + osWrk.Printf( "HDF4_EOS:EOS_SWATH_GEOL:\"%s\":%s:%s", + pszFilename, pszSubdatasetName, + papszGeolocations[iLatDim] ); + SetMetadataItem( "Y_DATASET", osWrk, "GEOLOCATION" ); + SetMetadataItem( "Y_BAND", "1" , "GEOLOCATION" ); + + if ( paiOffset && paiIncrement ) + { + osWrk.Printf( "%ld", (long)paiOffset[iPixelDim] ); + SetMetadataItem( "PIXEL_OFFSET", osWrk, "GEOLOCATION" ); + osWrk.Printf( "%ld", (long)paiIncrement[iPixelDim] ); + SetMetadataItem( "PIXEL_STEP", osWrk, "GEOLOCATION" ); + + osWrk.Printf( "%ld", (long)paiOffset[iLineDim] ); + SetMetadataItem( "LINE_OFFSET", osWrk, "GEOLOCATION" ); + osWrk.Printf( "%ld", (long)paiIncrement[iLineDim] ); + SetMetadataItem( "LINE_STEP", osWrk, "GEOLOCATION" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( pLatticeX ); + CPLFree( pLatticeY ); + CPLFree( pLat ); + CPLFree( pLong ); + + if( iGCPStepX == 0 ) + { + CPLFree( pszGCPProjection ); + pszGCPProjection = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( paiOffset ); + CPLFree( paiIncrement ); + CPLFree( pszGeoList ); + CSLDestroy( papszGeolocations ); + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *HDF4ImageDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + int i; + + if( !EQUALN( poOpenInfo->pszFilename, "HDF4_SDS:", 9 ) && + !EQUALN( poOpenInfo->pszFilename, "HDF4_GR:", 8 ) && + !EQUALN( poOpenInfo->pszFilename, "HDF4_GD:", 8 ) && + !EQUALN( poOpenInfo->pszFilename, "HDF4_EOS:", 9 ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + char **papszSubdatasetName; + HDF4ImageDataset *poDS; + + poDS = new HDF4ImageDataset( ); + if( poOpenInfo->fpL != NULL ) + { + VSIFCloseL(poOpenInfo->fpL); + poOpenInfo->fpL = NULL; + } + + CPLMutexHolderD(&hHDF4Mutex); + + papszSubdatasetName = CSLTokenizeString2( poOpenInfo->pszFilename, + ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES); + if ( CSLCount( papszSubdatasetName ) != 4 + && CSLCount( papszSubdatasetName ) != 5 + && CSLCount( papszSubdatasetName ) != 6 ) + { + CSLDestroy( papszSubdatasetName ); + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + + /* -------------------------------------------------------------------- */ + /* Check for drive name in windows HDF4:"D:\... */ + /* -------------------------------------------------------------------- */ + if (strlen(papszSubdatasetName[2]) == 1) + { + char* pszFilename = (char*) CPLMalloc( 2 + strlen(papszSubdatasetName[3]) + 1); + sprintf(pszFilename, "%s:%s", papszSubdatasetName[2], papszSubdatasetName[3]); + CPLFree(papszSubdatasetName[2]); + CPLFree(papszSubdatasetName[3]); + papszSubdatasetName[2] = pszFilename; + + /* Move following arguments one rank upper */ + papszSubdatasetName[3] = papszSubdatasetName[4]; + if (papszSubdatasetName[4] != NULL) + { + papszSubdatasetName[4] = papszSubdatasetName[5]; + papszSubdatasetName[5] = NULL; + } + } + + poDS->pszFilename = CPLStrdup( papszSubdatasetName[2] ); + + if( EQUAL( papszSubdatasetName[0], "HDF4_SDS" ) ) + poDS->iDatasetType = HDF4_SDS; + else if ( EQUAL( papszSubdatasetName[0], "HDF4_GR" ) ) + poDS->iDatasetType = HDF4_GR; + else if ( EQUAL( papszSubdatasetName[0], "HDF4_EOS" ) ) + poDS->iDatasetType = HDF4_EOS; + else + poDS->iDatasetType = HDF4_UNKNOWN; + + if( EQUAL( papszSubdatasetName[1], "GDAL_HDF4" ) ) + poDS->iSubdatasetType = H4ST_GDAL; + else if( EQUAL( papszSubdatasetName[1], "EOS_GRID" ) ) + poDS->iSubdatasetType = H4ST_EOS_GRID; + else if( EQUAL( papszSubdatasetName[1], "EOS_SWATH" ) ) + poDS->iSubdatasetType = H4ST_EOS_SWATH; + else if( EQUAL( papszSubdatasetName[1], "EOS_SWATH_GEOL" ) ) + poDS->iSubdatasetType = H4ST_EOS_SWATH_GEOL; + else if( EQUAL( papszSubdatasetName[1], "SEAWIFS_L3" ) ) + poDS->iSubdatasetType= H4ST_SEAWIFS_L3; + else if( EQUAL( papszSubdatasetName[1], "HYPERION_L1" ) ) + poDS->iSubdatasetType= H4ST_HYPERION_L1; + else + poDS->iSubdatasetType = H4ST_UNKNOWN; + +/* -------------------------------------------------------------------- */ +/* Is our file still here? */ +/* -------------------------------------------------------------------- */ + if ( !Hishdf( poDS->pszFilename ) ) + { + CSLDestroy( papszSubdatasetName ); + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Collect the remain (post filename) components to treat as */ +/* the subdataset name. */ +/* -------------------------------------------------------------------- */ + CPLString osSubdatasetName; + + osSubdatasetName = papszSubdatasetName[3]; + if( papszSubdatasetName[4] != NULL ) + { + osSubdatasetName += ":"; + osSubdatasetName += papszSubdatasetName[4]; + } + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ + int32 iAttribute, nValues, iAttrNumType; + double dfNoData = 0, dfScale = 1, dfOffset = 0; + int bNoDataSet = FALSE, bHaveScale = FALSE, bHaveOffset = FALSE; + const char *pszUnits = NULL, *pszDescription = NULL; + +/* -------------------------------------------------------------------- */ +/* Select SDS or GR to read from. */ +/* -------------------------------------------------------------------- */ + if ( poDS->iDatasetType == HDF4_EOS ) + { + poDS->pszSubdatasetName = CPLStrdup( papszSubdatasetName[3] ); + if (papszSubdatasetName[4] == NULL) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + poDS->pszFieldName = CPLStrdup( papszSubdatasetName[4] ); + } + else + poDS->iDataset = atoi( papszSubdatasetName[3] ); + CSLDestroy( papszSubdatasetName ); + + switch ( poDS->iDatasetType ) + { + case HDF4_EOS: + { + void *pNoDataValue = NULL; + + switch ( poDS->iSubdatasetType ) + { + +/* -------------------------------------------------------------------- */ +/* HDF-EOS Swath. */ +/* -------------------------------------------------------------------- */ + case H4ST_EOS_SWATH: + case H4ST_EOS_SWATH_GEOL: + { + int32 hSW; + char *pszDimList = NULL; + + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->hHDF4 = SWopen( poDS->pszFilename, DFACC_READ ); + else + poDS->hHDF4 = SWopen( poDS->pszFilename, DFACC_WRITE ); + + if( poDS->hHDF4 <= 0 ) + { + CPLDebug( "HDF4Image", "Can't open file \"%s\" for swath reading", + poDS->pszFilename ); + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return( NULL ); + } + + hSW = SWattach( poDS->hHDF4, poDS->pszSubdatasetName ); + if( hSW < 0 ) + { + CPLDebug( "HDF4Image", "Can't attach to subdataset %s", + poDS->pszSubdatasetName ); + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return( NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Decode the dimension map. */ +/* -------------------------------------------------------------------- */ + int32 nStrBufSize = 0; + + if ( SWnentries( hSW, HDFE_NENTDIM, &nStrBufSize ) < 0 + || nStrBufSize <= 0 ) + { + CPLDebug( "HDF4Image", + "Can't read a number of dimension maps." ); + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + + pszDimList = (char *)CPLMalloc( nStrBufSize + 1 ); + if ( SWfieldinfo( hSW, poDS->pszFieldName, &poDS->iRank, + poDS->aiDimSizes, &poDS->iNumType, + pszDimList ) < 0 ) + { + CPLDebug( "HDF4Image", "Can't read dimension maps." ); + CPLFree( pszDimList ); + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + pszDimList[nStrBufSize] = '\0'; +#ifdef DEBUG + CPLDebug( "HDF4Image", + "List of dimensions in swath \"%s\": %s", + poDS->pszFieldName, pszDimList ); +#endif + poDS->GetImageDimensions( pszDimList ); + +#ifdef DEBUG + CPLDebug( "HDF4Image", + "X dimension is %d, Y dimension is %d", + poDS->iXDim, poDS->iYDim ); +#endif + +/* -------------------------------------------------------------------- */ +/* Fetch metadata. */ +/* -------------------------------------------------------------------- */ + if( poDS->iSubdatasetType == H4ST_EOS_SWATH ) /* Not H4ST_EOS_SWATH_GEOL */ + poDS->GetSwatAttrs( hSW ); + +/* -------------------------------------------------------------------- */ +/* Fetch NODATA value. */ +/* -------------------------------------------------------------------- */ + pNoDataValue = + CPLMalloc( poDS->GetDataTypeSize(poDS->iNumType) ); + if ( SWgetfillvalue( hSW, poDS->pszFieldName, + pNoDataValue ) != -1 ) + { + dfNoData = poDS->AnyTypeToDouble( poDS->iNumType, + pNoDataValue ); + bNoDataSet = TRUE; + } + else + { + const char *pszNoData = + CSLFetchNameValue( poDS->papszLocalMetadata, + "_FillValue" ); + if ( pszNoData ) + { + dfNoData = CPLAtof( pszNoData ); + bNoDataSet = TRUE; + } + } + CPLFree( pNoDataValue ); + +/* -------------------------------------------------------------------- */ +/* Handle Geolocation processing. */ +/* -------------------------------------------------------------------- */ + if( poDS->iSubdatasetType == H4ST_EOS_SWATH ) /* Not H4ST_SWATH_GEOL */ + { + char **papszDimList = + CSLTokenizeString2( pszDimList, ",", + CSLT_HONOURSTRINGS ); + if( !poDS->ProcessSwathGeolocation( hSW, papszDimList ) ) + { + CPLDebug( "HDF4Image", + "No geolocation available for this swath." ); + } + CSLDestroy( papszDimList ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + CPLFree( pszDimList ); + SWdetach( hSW ); + } + break; + +/* -------------------------------------------------------------------- */ +/* HDF-EOS Grid. */ +/* -------------------------------------------------------------------- */ + case H4ST_EOS_GRID: + { + int32 hGD, iProjCode = 0, iZoneCode = 0, iSphereCode = 0; + int32 nXSize, nYSize; + char szDimList[8192]; + double adfUpLeft[2], adfLowRight[2], adfProjParms[15]; + + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->hHDF4 = GDopen( poDS->pszFilename, DFACC_READ ); + else + poDS->hHDF4 = GDopen( poDS->pszFilename, DFACC_WRITE ); + + if( poDS->hHDF4 <= 0 ) + { + CPLDebug( "HDF4Image", "Can't open file \"%s\" for grid reading", + poDS->pszFilename ); + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return( NULL ); + } + + hGD = GDattach( poDS->hHDF4, poDS->pszSubdatasetName ); + +/* -------------------------------------------------------------------- */ +/* Decode the dimension map. */ +/* -------------------------------------------------------------------- */ + GDfieldinfo( hGD, poDS->pszFieldName, &poDS->iRank, + poDS->aiDimSizes, &poDS->iNumType, szDimList ); +#ifdef DEBUG + CPLDebug( "HDF4Image", + "List of dimensions in grid %s: %s", + poDS->pszFieldName, szDimList); +#endif + poDS->GetImageDimensions( szDimList ); + + int32 tilecode, tilerank; + if( GDtileinfo( hGD , poDS->pszFieldName , &tilecode, &tilerank, NULL ) == 0 ) + { + if ( tilecode == HDFE_TILE ) + { + int32 *tiledims = NULL; + tiledims = (int32 *) CPLCalloc( tilerank , sizeof( int32 ) ); + GDtileinfo( hGD , poDS->pszFieldName , &tilecode, &tilerank, tiledims ); + if ( ( tilerank == 2 ) && ( poDS->iRank == tilerank ) ) + { + poDS->nBlockPreferredXSize = tiledims[1]; + poDS->nBlockPreferredYSize = tiledims[0]; + poDS->bReadTile = true; +#ifdef DEBUG + CPLDebug( "HDF4_EOS:EOS_GRID:","tilerank in grid %s: %d", + poDS->pszFieldName , (int)tilerank ); + CPLDebug( "HDF4_EOS:EOS_GRID:","tiledimens in grid %s: %d,%d", + poDS->pszFieldName , (int)tiledims[0] , (int)tiledims[1] ); +#endif + } +#ifdef DEBUG + else + { + CPLDebug( "HDF4_EOS:EOS_GRID:","tilerank in grid %s: %d not supported", + poDS->pszFieldName , (int)tilerank ); + } +#endif + CPLFree(tiledims); + } + else + { +#ifdef DEBUG + CPLDebug( "HDF4_EOS:EOS_GRID:","tilecode == HDFE_NOTILE in grid %s: %d", + poDS->pszFieldName , + (int)poDS->iRank ); +#endif + } + } +#ifdef DEBUG + else + { + CPLDebug( "HDF4_EOS:EOS_GRID:","ERROR GDtileinfo %s", poDS->pszFieldName ); + } +#endif + +/* -------------------------------------------------------------------- */ +/* Fetch projection information */ +/* -------------------------------------------------------------------- */ + if ( GDprojinfo( hGD, &iProjCode, &iZoneCode, + &iSphereCode, adfProjParms) >= 0 ) + { +#ifdef DEBUG + CPLDebug( "HDF4Image", + "Grid projection: " + "projection code: %ld, zone code %ld, " + "sphere code %ld", + (long)iProjCode, (long)iZoneCode, + (long)iSphereCode ); +#endif + poDS->oSRS.importFromUSGS( iProjCode, iZoneCode, + adfProjParms, iSphereCode, + USGS_ANGLE_RADIANS ); + + if ( poDS->pszProjection ) + CPLFree( poDS->pszProjection ); + poDS->oSRS.exportToWkt( &poDS->pszProjection ); + } + +/* -------------------------------------------------------------------- */ +/* Fetch geotransformation matrix */ +/* -------------------------------------------------------------------- */ + if ( GDgridinfo( hGD, &nXSize, &nYSize, + adfUpLeft, adfLowRight ) >= 0 ) + { +#ifdef DEBUG + CPLDebug( "HDF4Image", + "Grid geolocation: " + "top left X %f, top left Y %f, " + "low right X %f, low right Y %f, " + "cols %ld, rows %ld", + adfUpLeft[0], adfUpLeft[1], + adfLowRight[0], adfLowRight[1], + (long)nXSize, (long)nYSize ); +#endif + if ( iProjCode ) + { + // For projected systems coordinates are in meters + poDS->adfGeoTransform[1] = + (adfLowRight[0] - adfUpLeft[0]) / nXSize; + poDS->adfGeoTransform[5] = + (adfLowRight[1] - adfUpLeft[1]) / nYSize; + poDS->adfGeoTransform[0] = adfUpLeft[0]; + poDS->adfGeoTransform[3] = adfUpLeft[1]; + } + else + { + // Handle angular geographic coordinates here + poDS->adfGeoTransform[1] = + (CPLPackedDMSToDec(adfLowRight[0]) - + CPLPackedDMSToDec(adfUpLeft[0])) / nXSize; + poDS->adfGeoTransform[5] = + (CPLPackedDMSToDec(adfLowRight[1]) - + CPLPackedDMSToDec(adfUpLeft[1])) / nYSize; + poDS->adfGeoTransform[0] = + CPLPackedDMSToDec(adfUpLeft[0]); + poDS->adfGeoTransform[3] = + CPLPackedDMSToDec(adfUpLeft[1]); + } + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + poDS->bHasGeoTransform = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Fetch metadata. */ +/* -------------------------------------------------------------------- */ + poDS->GetGridAttrs( hGD ); + + GDdetach( hGD ); + +/* -------------------------------------------------------------------- */ +/* Fetch NODATA value. */ +/* -------------------------------------------------------------------- */ + pNoDataValue = + CPLMalloc( poDS->GetDataTypeSize(poDS->iNumType) ); + if ( GDgetfillvalue( hGD, poDS->pszFieldName, + pNoDataValue ) != -1 ) + { + dfNoData = poDS->AnyTypeToDouble( poDS->iNumType, + pNoDataValue ); + bNoDataSet = TRUE; + } + else + { + const char *pszNoData = + CSLFetchNameValue( poDS->papszLocalMetadata, + "_FillValue" ); + if ( pszNoData ) + { + dfNoData = CPLAtof( pszNoData ); + bNoDataSet = TRUE; + } + } + CPLFree( pNoDataValue ); + } + break; + + default: + break; + } + +/* -------------------------------------------------------------------- */ +/* Fetch unit type, scale, offset and description */ +/* Should be similar among various HDF-EOS kinds. */ +/* -------------------------------------------------------------------- */ + { + const char *pszTmp = + CSLFetchNameValue( poDS->papszLocalMetadata, + "scale_factor" ); + if ( pszTmp ) + { + dfScale = CPLAtof( pszTmp ); + // some producers (ie. lndcsm from LEDAPS) emit + // files with scale_factor=0 which is crazy to carry + // through. + if( dfScale == 0.0 ) + dfScale = 1.0; + + bHaveScale = (dfScale != 0.0); + } + + pszTmp = + CSLFetchNameValue( poDS->papszLocalMetadata, "add_offset" ); + if ( pszTmp ) + { + dfOffset = CPLAtof( pszTmp ); + bHaveOffset = TRUE; + } + + pszUnits = CSLFetchNameValue( poDS->papszLocalMetadata, + "units" ); + pszDescription = CSLFetchNameValue( poDS->papszLocalMetadata, + "long_name" ); + } + } + break; + +/* -------------------------------------------------------------------- */ +/* 'Plain' HDF scientific datasets. */ +/* -------------------------------------------------------------------- */ + case HDF4_SDS: + { + int32 iSDS; + + // Attempt to increase maximum number of opened HDF files +#ifdef HDF4_HAS_MAXOPENFILES + intn nCurrMax, nSysLimit; + + if ( SDget_maxopenfiles(&nCurrMax, &nSysLimit) >= 0 + && nCurrMax < nSysLimit ) + { + SDreset_maxopenfiles( nSysLimit ); + } +#endif /* HDF4_HAS_MAXOPENFILES */ + + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->hHDF4 = Hopen( poDS->pszFilename, DFACC_READ, 0 ); + else + poDS->hHDF4 = Hopen( poDS->pszFilename, DFACC_WRITE, 0 ); + + if( poDS->hHDF4 <= 0 ) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return( NULL ); + } + + poDS->hSD = SDstart( poDS->pszFilename, DFACC_READ ); + if ( poDS->hSD == -1 ) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + + if ( poDS->ReadGlobalAttributes( poDS->hSD ) != CE_None ) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + + int32 nDatasets, nAttrs; + + if ( SDfileinfo( poDS->hSD, &nDatasets, &nAttrs ) != 0 ) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + + if (poDS->iDataset < 0 || poDS->iDataset >= nDatasets) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Subdataset index should be between 0 and %ld", + (long int)nDatasets - 1); + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + + memset( poDS->aiDimSizes, 0, sizeof(int32) * H4_MAX_VAR_DIMS ); + iSDS = SDselect( poDS->hSD, poDS->iDataset ); + SDgetinfo( iSDS, poDS->szName, &poDS->iRank, poDS->aiDimSizes, + &poDS->iNumType, &poDS->nAttrs); + + // We will duplicate global metadata for every subdataset + poDS->papszLocalMetadata = + CSLDuplicate( poDS->papszGlobalMetadata ); + + for ( iAttribute = 0; iAttribute < poDS->nAttrs; iAttribute++ ) + { + char szAttrName[H4_MAX_NC_NAME]; + SDattrinfo( iSDS, iAttribute, szAttrName, + &iAttrNumType, &nValues ); + poDS->papszLocalMetadata = + poDS->TranslateHDF4Attributes( iSDS, iAttribute, + szAttrName, iAttrNumType, + nValues, + poDS->papszLocalMetadata ); + } + poDS->SetMetadata( poDS->papszLocalMetadata, "" ); + SDendaccess( iSDS ); + +#ifdef DEBUG + CPLDebug( "HDF4Image", + "aiDimSizes[0]=%ld, aiDimSizes[1]=%ld, " + "aiDimSizes[2]=%ld, aiDimSizes[3]=%ld", + (long)poDS->aiDimSizes[0], (long)poDS->aiDimSizes[1], + (long)poDS->aiDimSizes[2], (long)poDS->aiDimSizes[3] ); +#endif + + switch( poDS->iRank ) + { + case 1: + poDS->nBandCount = 1; + poDS->iXDim = 0; + poDS->iYDim = -1; + break; + case 2: + poDS->nBandCount = 1; + poDS->iXDim = 1; + poDS->iYDim = 0; + break; + case 3: + /* FIXME ? We should probably remove the following test as there are valid datasets */ + /* where the height is lower than the band number : for example + http://www.iapmw.unibe.ch/research/projects/FriOWL/data/otd/LISOTD_HRAC_V2.2.hdf */ + /* which is a 720x360 x 365 bands */ + /* Use a HACK for now */ + if( poDS->aiDimSizes[0] < poDS->aiDimSizes[2] && + !(poDS->aiDimSizes[0] == 360 && + poDS->aiDimSizes[1] == 720 && + poDS->aiDimSizes[2] == 365) ) + { + poDS->iBandDim = 0; + poDS->iXDim = 2; + poDS->iYDim = 1; + } + else + { + if( poDS->aiDimSizes[1] <= poDS->aiDimSizes[0] && + poDS->aiDimSizes[1] <= poDS->aiDimSizes[2] ) + { + poDS->iBandDim = 1; + poDS->iXDim = 2; + poDS->iYDim = 0; + } + else + { + poDS->iBandDim = 2; + poDS->iXDim = 1; + poDS->iYDim = 0; + + } + } + poDS->nBandCount = poDS->aiDimSizes[poDS->iBandDim]; + break; + case 4: // FIXME + poDS->nBandCount = poDS->aiDimSizes[2] * poDS->aiDimSizes[3]; + break; + default: + break; + } + + // We preset this because CaptureNRLGeoTransform needs it. + poDS->nRasterXSize = poDS->aiDimSizes[poDS->iXDim]; + if (poDS->iYDim >= 0) + poDS->nRasterYSize = poDS->aiDimSizes[poDS->iYDim]; + else + poDS->nRasterYSize = 1; + + // Special case projection info for NRL generated files. + const char *pszMapProjectionSystem = + CSLFetchNameValue(poDS->papszGlobalMetadata, + "mapProjectionSystem"); + if( pszMapProjectionSystem != NULL + && EQUAL(pszMapProjectionSystem,"NRL(USGS)") ) + { + poDS->CaptureNRLGeoTransform(); + } + + // Special case for coastwatch hdf files. + if( CSLFetchNameValue( poDS->papszGlobalMetadata, + "gctp_sys" ) != NULL ) + poDS->CaptureCoastwatchGCTPInfo(); + + // Special case for MODIS geolocation + poDS->ProcessModisSDSGeolocation(); + + // Special case for NASA/CCRS Landsat in HDF + poDS->CaptureL1GMTLInfo(); + } + break; + +/* -------------------------------------------------------------------- */ +/* 'Plain' HDF rasters. */ +/* -------------------------------------------------------------------- */ + case HDF4_GR: + + // Attempt to increase maximum number of opened HDF files +#ifdef HDF4_HAS_MAXOPENFILES + intn nCurrMax, nSysLimit; + + if ( SDget_maxopenfiles(&nCurrMax, &nSysLimit) >= 0 + && nCurrMax < nSysLimit ) + { + SDreset_maxopenfiles( nSysLimit ); + } +#endif /* HDF4_HAS_MAXOPENFILES */ + + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->hHDF4 = Hopen( poDS->pszFilename, DFACC_READ, 0 ); + else + poDS->hHDF4 = Hopen( poDS->pszFilename, DFACC_WRITE, 0 ); + + if( poDS->hHDF4 <= 0 ) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return( NULL ); + } + + poDS->hGR = GRstart( poDS->hHDF4 ); + if ( poDS->hGR == -1 ) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + + poDS->iGR = GRselect( poDS->hGR, poDS->iDataset ); + if ( GRgetiminfo( poDS->iGR, poDS->szName, + &poDS->iRank, &poDS->iNumType, + &poDS->iInterlaceMode, poDS->aiDimSizes, + &poDS->nAttrs ) != 0 ) + { + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + + // We will duplicate global metadata for every subdataset + poDS->papszLocalMetadata = CSLDuplicate( poDS->papszGlobalMetadata ); + + for ( iAttribute = 0; iAttribute < poDS->nAttrs; iAttribute++ ) + { + char szAttrName[H4_MAX_NC_NAME]; + GRattrinfo( poDS->iGR, iAttribute, szAttrName, + &iAttrNumType, &nValues ); + poDS->papszLocalMetadata = + poDS->TranslateHDF4Attributes( poDS->iGR, iAttribute, + szAttrName, iAttrNumType, + nValues, + poDS->papszLocalMetadata ); + } + poDS->SetMetadata( poDS->papszLocalMetadata, "" ); + // Read colour table + GDALColorEntry oEntry; + + poDS->iPal = GRgetlutid ( poDS->iGR, poDS->iDataset ); + if ( poDS->iPal != -1 ) + { + GRgetlutinfo( poDS->iPal, &poDS->nComps, &poDS->iPalDataType, + &poDS->iPalInterlaceMode, &poDS->nPalEntries ); + GRreadlut( poDS->iPal, poDS->aiPaletteData ); + poDS->poColorTable = new GDALColorTable(); + for( i = 0; i < N_COLOR_ENTRIES; i++ ) + { + oEntry.c1 = poDS->aiPaletteData[i][0]; + oEntry.c2 = poDS->aiPaletteData[i][1]; + oEntry.c3 = poDS->aiPaletteData[i][2]; + oEntry.c4 = 255; + + poDS->poColorTable->SetColorEntry( i, &oEntry ); + } + } + + poDS->iXDim = 0; + poDS->iYDim = 1; + poDS->nBandCount = poDS->iRank; + break; + default: + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + + poDS->nRasterXSize = poDS->aiDimSizes[poDS->iXDim]; + if (poDS->iYDim >= 0) + poDS->nRasterYSize = poDS->aiDimSizes[poDS->iYDim]; + else + poDS->nRasterYSize = 1; + + if ( poDS->iSubdatasetType == H4ST_HYPERION_L1 ) + { + // XXX: Hyperion SDSs has Height x Bands x Width dimensions scheme + if ( poDS->iRank > 2 ) + { + poDS->nBandCount = poDS->aiDimSizes[1]; + poDS->nRasterXSize = poDS->aiDimSizes[2]; + poDS->nRasterYSize = poDS->aiDimSizes[0]; + } + else + { + poDS->nBandCount = poDS->aiDimSizes[0]; + poDS->nRasterXSize = poDS->aiDimSizes[1]; + poDS->nRasterYSize = 1; + } + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( i = 1; i <= poDS->nBandCount; i++ ) + { + HDF4ImageRasterBand *poBand = + new HDF4ImageRasterBand( poDS, i, + poDS->GetDataType( poDS->iNumType ) ); + poDS->SetBand( i, poBand ); + + if ( bNoDataSet ) + poBand->SetNoDataValue( dfNoData ); + if ( bHaveScale ) + { + poBand->bHaveScale = TRUE; + poBand->dfScale = dfScale; + } + if ( bHaveOffset ) + { + poBand->bHaveOffset = TRUE; + poBand->dfOffset = dfOffset; + } + if ( pszUnits ) + poBand->osUnitType = pszUnits; + if ( pszDescription ) + poBand->SetDescription( pszDescription ); + } + +/* -------------------------------------------------------------------- */ +/* Now we will handle particular types of HDF products. Every */ +/* HDF product has its own structure. */ +/* -------------------------------------------------------------------- */ + + // Variables for reading georeferencing + double dfULX, dfULY, dfLRX, dfLRY; + + switch ( poDS->iSubdatasetType ) + { +/* -------------------------------------------------------------------- */ +/* HDF, created by GDAL. */ +/* -------------------------------------------------------------------- */ + case H4ST_GDAL: + { + const char *pszValue; + + CPLDebug( "HDF4Image", + "Input dataset interpreted as GDAL_HDF4" ); + + if ( (pszValue = + CSLFetchNameValue(poDS->papszGlobalMetadata, + "Projection")) ) + { + if ( poDS->pszProjection ) + CPLFree( poDS->pszProjection ); + poDS->pszProjection = CPLStrdup( pszValue ); + } + if ( (pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata, + "TransformationMatrix")) ) + { + int i = 0; + char *pszString = (char *) pszValue; + while ( *pszValue && i < 6 ) + { + poDS->adfGeoTransform[i++] = CPLStrtod(pszString, &pszString); + pszString++; + } + poDS->bHasGeoTransform = TRUE; + } + for( i = 1; i <= poDS->nBands; i++ ) + { + if ( (pszValue = + CSLFetchNameValue(poDS->papszGlobalMetadata, + CPLSPrintf("BandDesc%d", i))) ) + poDS->GetRasterBand( i )->SetDescription( pszValue ); + } + for( i = 1; i <= poDS->nBands; i++ ) + { + if ( (pszValue = + CSLFetchNameValue(poDS->papszGlobalMetadata, + CPLSPrintf("NoDataValue%d", i))) ) + poDS->GetRasterBand(i)->SetNoDataValue( CPLAtof(pszValue) ); + } + } + break; + +/* -------------------------------------------------------------------- */ +/* SeaWiFS Level 3 Standard Mapped Image Products. */ +/* Organized similar to MODIS Level 3 products. */ +/* -------------------------------------------------------------------- */ + case H4ST_SEAWIFS_L3: + { + CPLDebug( "HDF4Image", "Input dataset interpreted as SEAWIFS_L3" ); + + // Read band description + for ( i = 1; i <= poDS->nBands; i++ ) + { + poDS->GetRasterBand( i )->SetDescription( + CSLFetchNameValue( poDS->papszGlobalMetadata, "Parameter" ) ); + } + + // Read coordinate system and geotransform matrix + poDS->oSRS.SetWellKnownGeogCS( "WGS84" ); + + if ( EQUAL(CSLFetchNameValue(poDS->papszGlobalMetadata, + "Map Projection"), + "Equidistant Cylindrical") ) + { + poDS->oSRS.SetEquirectangular( 0.0, 0.0, 0.0, 0.0 ); + poDS->oSRS.SetLinearUnits( SRS_UL_METER, 1 ); + if ( poDS->pszProjection ) + CPLFree( poDS->pszProjection ); + poDS->oSRS.exportToWkt( &poDS->pszProjection ); + } + + dfULX = CPLAtof( CSLFetchNameValue(poDS->papszGlobalMetadata, + "Westernmost Longitude") ); + dfULY = CPLAtof( CSLFetchNameValue(poDS->papszGlobalMetadata, + "Northernmost Latitude") ); + dfLRX = CPLAtof( CSLFetchNameValue(poDS->papszGlobalMetadata, + "Easternmost Longitude") ); + dfLRY = CPLAtof( CSLFetchNameValue(poDS->papszGlobalMetadata, + "Southernmost Latitude") ); + poDS->ToGeoref( &dfULX, &dfULY ); + poDS->ToGeoref( &dfLRX, &dfLRY ); + poDS->adfGeoTransform[0] = dfULX; + poDS->adfGeoTransform[3] = dfULY; + poDS->adfGeoTransform[1] = (dfLRX - dfULX) / poDS->nRasterXSize; + poDS->adfGeoTransform[5] = (dfULY - dfLRY) / poDS->nRasterYSize; + if ( dfULY > 0) // Northern hemisphere + poDS->adfGeoTransform[5] = - poDS->adfGeoTransform[5]; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + poDS->bHasGeoTransform = TRUE; + } + break; + + +/* -------------------------------------------------------------------- */ +/* Generic SDS */ +/* -------------------------------------------------------------------- */ + case H4ST_UNKNOWN: + { + + // This is a coastwatch convention. + if( CSLFetchNameValue( poDS->papszLocalMetadata, "missing_value" ) ) + { + int i; + for( i = 1; i <= poDS->nBands; i++ ) + { + poDS->GetRasterBand(i)->SetNoDataValue( + CPLAtof( CSLFetchNameValue(poDS->papszLocalMetadata, + "missing_value") ) ); + } + } + + // Coastwatch offset and scale. + if( CSLFetchNameValue( poDS->papszLocalMetadata, "scale_factor" ) + && CSLFetchNameValue( poDS->papszLocalMetadata, "add_offset" ) ) + { + for( i = 1; i <= poDS->nBands; i++ ) + { + HDF4ImageRasterBand *poBand = + (HDF4ImageRasterBand *) poDS->GetRasterBand(i); + + poBand->bHaveScale = poBand->bHaveOffset = TRUE; + poBand->dfScale = + CPLAtof( CSLFetchNameValue( poDS->papszLocalMetadata, + "scale_factor" ) ); + // See #4891 regarding offset interpretation. + //poBand->dfOffset = -1 * poBand->dfScale * + // CPLAtof( CSLFetchNameValue( poDS->papszLocalMetadata, + // "add_offset" ) ); + poBand->dfOffset = + CPLAtof( CSLFetchNameValue( poDS->papszLocalMetadata, + "add_offset" ) ); + } + } + + // this is a modis level3 convention (data from ACT) + // Eg data/hdf/act/modis/MODAM2004280160000.L3_NOAA_GMX + + if( CSLFetchNameValue( poDS->papszLocalMetadata, + "scalingSlope" ) + && CSLFetchNameValue( poDS->papszLocalMetadata, + "scalingIntercept" ) ) + { + int i; + CPLString osUnits; + + if( CSLFetchNameValue( poDS->papszLocalMetadata, + "productUnits" ) ) + { + osUnits = CSLFetchNameValue( poDS->papszLocalMetadata, + "productUnits" ); + } + + for( i = 1; i <= poDS->nBands; i++ ) + { + HDF4ImageRasterBand *poBand = + (HDF4ImageRasterBand *) poDS->GetRasterBand(i); + + poBand->bHaveScale = poBand->bHaveOffset = TRUE; + poBand->dfScale = + CPLAtof( CSLFetchNameValue( poDS->papszLocalMetadata, + "scalingSlope" ) ); + poBand->dfOffset = + CPLAtof( CSLFetchNameValue( poDS->papszLocalMetadata, + "scalingIntercept" ) ); + + poBand->osUnitType = osUnits; + } + } + } + break; + +/* -------------------------------------------------------------------- */ +/* Hyperion Level 1. */ +/* -------------------------------------------------------------------- */ + case H4ST_HYPERION_L1: + { + CPLDebug( "HDF4Image", "Input dataset interpreted as HYPERION_L1" ); + } + break; + + default: + break; + } + +/* -------------------------------------------------------------------- */ +/* Setup PAM info for this subdataset */ +/* -------------------------------------------------------------------- */ + poDS->SetPhysicalFilename( poDS->pszFilename ); + poDS->SetSubdatasetName( osSubdatasetName ); + + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + poDS->TryLoadXML(); + + poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" ); + CPLAcquireMutex(hHDF4Mutex, 1000.0); + + return( poDS ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *HDF4ImageDataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char **papszOptions ) + +{ +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + HDF4ImageDataset *poDS; + const char *pszSDSName; + int iBand; + int32 iSDS = -1; + int32 aiDimSizes[H4_MAX_VAR_DIMS]; + + if( nBands == 0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to export files with zero bands." ); + return NULL; + } + + poDS = new HDF4ImageDataset(); + + CPLMutexHolderD(&hHDF4Mutex); + +/* -------------------------------------------------------------------- */ +/* Choose rank for the created dataset. */ +/* -------------------------------------------------------------------- */ + poDS->iRank = 3; + if ( CSLFetchNameValue( papszOptions, "RANK" ) != NULL && + EQUAL( CSLFetchNameValue( papszOptions, "RANK" ), "2" ) ) + poDS->iRank = 2; + + poDS->hSD = SDstart( pszFilename, DFACC_CREATE ); + if ( poDS->hSD == -1 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Can't create HDF4 file %s", pszFilename ); + CPLReleaseMutex(hHDF4Mutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hHDF4Mutex, 1000.0); + return NULL; + } + poDS->iXDim = 1; + poDS->iYDim = 0; + poDS->iBandDim = 2; + aiDimSizes[poDS->iXDim] = nXSize; + aiDimSizes[poDS->iYDim] = nYSize; + aiDimSizes[poDS->iBandDim] = nBands; + + if ( poDS->iRank == 2 ) + { + for ( iBand = 0; iBand < nBands; iBand++ ) + { + pszSDSName = CPLSPrintf( "Band%d", iBand ); + switch ( eType ) + { + case GDT_Float64: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_FLOAT64, + poDS->iRank, aiDimSizes ); + break; + case GDT_Float32: + iSDS = SDcreate( poDS-> hSD, pszSDSName, DFNT_FLOAT32, + poDS->iRank, aiDimSizes ); + break; + case GDT_UInt32: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_UINT32, + poDS->iRank, aiDimSizes ); + break; + case GDT_UInt16: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_UINT16, + poDS->iRank, aiDimSizes ); + break; + case GDT_Int32: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_INT32, + poDS->iRank, aiDimSizes ); + break; + case GDT_Int16: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_INT16, + poDS->iRank, aiDimSizes ); + break; + case GDT_Byte: + default: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_UINT8, + poDS->iRank, aiDimSizes ); + break; + } + SDendaccess( iSDS ); + } + } + else if ( poDS->iRank == 3 ) + { + pszSDSName = "3-dimensional Scientific Dataset"; + poDS->iDataset = 0; + switch ( eType ) + { + case GDT_Float64: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_FLOAT64, + poDS->iRank, aiDimSizes ); + break; + case GDT_Float32: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_FLOAT32, + poDS->iRank, aiDimSizes ); + break; + case GDT_UInt32: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_UINT32, + poDS->iRank, aiDimSizes ); + break; + case GDT_UInt16: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_UINT16, + poDS->iRank, aiDimSizes ); + break; + case GDT_Int32: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_INT32, + poDS->iRank, aiDimSizes ); + break; + case GDT_Int16: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_INT16, + poDS->iRank, aiDimSizes ); + break; + case GDT_Byte: + default: + iSDS = SDcreate( poDS->hSD, pszSDSName, DFNT_UINT8, + poDS->iRank, aiDimSizes ); + break; + } + } + else // Should never happen + return NULL; + + if ( iSDS < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't create SDS with rank %ld for file %s", + (long)poDS->iRank, pszFilename ); + return NULL; + } + + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->eAccess = GA_Update; + poDS->iDatasetType = HDF4_SDS; + poDS->iSubdatasetType = H4ST_GDAL; + poDS->nBands = nBands; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( iBand = 1; iBand <= nBands; iBand++ ) + poDS->SetBand( iBand, new HDF4ImageRasterBand( poDS, iBand, eType ) ); + + SDsetattr( poDS->hSD, "Signature", DFNT_CHAR8, strlen(pszGDALSignature) + 1, + pszGDALSignature ); + + return (GDALDataset *) poDS; +} + +/************************************************************************/ +/* GDALRegister_HDF4Image() */ +/************************************************************************/ + +void GDALRegister_HDF4Image() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "HDF4Image" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "HDF4Image" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "HDF4 Dataset" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_hdf4.html" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 Float32 Float64" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); + + poDriver->pfnOpen = HDF4ImageDataset::Open; + poDriver->pfnCreate = HDF4ImageDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/hdf4/makefile.vc b/bazaar/plugin/gdal/frmts/hdf4/makefile.vc new file mode 100644 index 000000000..4e2f3f43e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf4/makefile.vc @@ -0,0 +1,39 @@ +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +OBJ = hdf4dataset.obj hdf4imagedataset.obj + +PLUGIN_DLL = gdal_HDF4.dll + +EXTRAFLAGS = -I..\pds -I$(HDF4_DIR)\include -Ihdf-eos -DFRMT_hdf4 + +!IF "$(HDF4_PLUGIN)" == "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DHDF4_PLUGIN +!ENDIF + +!IF "$(HDF4_HAS_MAXOPENFILES)" == "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DHDF4_HAS_MAXOPENFILES +!ENDIF + + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + cd hdf-eos + $(MAKE) /f makefile.vc + +clean: + -del *.obj + cd hdf-eos + $(MAKE) /f makefile.vc clean + cd .. + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) ..\o\SWapi.obj ..\o\GDapi.obj ..\o\EHapi.obj ..\o\gctp_wrap.obj ..\o\nasakeywordhandler.obj $(GDALLIB) $(HDF4_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) diff --git a/bazaar/plugin/gdal/frmts/hdf5/GNUmakefile b/bazaar/plugin/gdal/frmts/hdf5/GNUmakefile new file mode 100644 index 000000000..bd87b6624 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf5/GNUmakefile @@ -0,0 +1,19 @@ + +include ../../GDALmake.opt + +OBJ = hdf5dataset.o hdf5imagedataset.o \ + bagdataset.o gh5_convenience.o iso19115_srs.o + +HDFEOS_OPTS = +SUBLIBS = + +CPPFLAGS := $(HDF5_INCLUDE) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) $(SUBLIBS) + +clean: + rm -f *.o $(O_OBJ) + +$(OBJ) $(O_OBJ): hdf5dataset.h + +install-obj: $(SUBLIBS) $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/hdf5/bagdataset.cpp b/bazaar/plugin/gdal/frmts/hdf5/bagdataset.cpp new file mode 100644 index 000000000..b18e3d2ff --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf5/bagdataset.cpp @@ -0,0 +1,885 @@ +/****************************************************************************** + * $Id: bagdataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: Hierarchical Data Format Release 5 (HDF5) + * Purpose: Read BAG datasets. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gh5_convenience.h" + +#include "gdal_pam.h" +#include "gdal_priv.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: bagdataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_BAG(void); +CPL_C_END + +OGRErr OGR_SRS_ImportFromISO19115( OGRSpatialReference *poThis, + const char *pszISOXML ); + +/************************************************************************/ +/* ==================================================================== */ +/* BAGDataset */ +/* ==================================================================== */ +/************************************************************************/ +class BAGDataset : public GDALPamDataset +{ + + friend class BAGRasterBand; + + hid_t hHDF5; + + char *pszProjection; + double adfGeoTransform[6]; + + void LoadMetadata(); + + char *pszXMLMetadata; + char *apszMDList[2]; + +public: + BAGDataset(); + ~BAGDataset(); + + virtual CPLErr GetGeoTransform( double * ); + virtual const char *GetProjectionRef(void); + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain = "" ); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + + OGRErr ParseWKTFromXML( const char *pszISOXML ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* BAGRasterBand */ +/* ==================================================================== */ +/************************************************************************/ +class BAGRasterBand : public GDALPamRasterBand +{ + friend class BAGDataset; + + hid_t hDatasetID; + hid_t native; + hid_t dataspace; + + bool bMinMaxSet; + double dfMinimum; + double dfMaximum; + +public: + + BAGRasterBand( BAGDataset *, int ); + ~BAGRasterBand(); + + bool Initialize( hid_t hDataset, const char *pszName ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual double GetNoDataValue( int * ); + + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum(int *pbSuccess = NULL ); +}; + +/************************************************************************/ +/* BAGRasterBand() */ +/************************************************************************/ +BAGRasterBand::BAGRasterBand( BAGDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + hDatasetID = -1; + dataspace = -1; + native = -1; + bMinMaxSet = false; +} + +/************************************************************************/ +/* ~BAGRasterBand() */ +/************************************************************************/ + +BAGRasterBand::~BAGRasterBand() +{ + if( dataspace > 0 ) + H5Sclose(dataspace); + + if( native > 0 ) + H5Tclose( native ); + + if( hDatasetID > 0 ) + H5Dclose( hDatasetID ); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +bool BAGRasterBand::Initialize( hid_t hDatasetID, const char *pszName ) + +{ + SetDescription( pszName ); + + this->hDatasetID = hDatasetID; + + hid_t datatype = H5Dget_type( hDatasetID ); + dataspace = H5Dget_space( hDatasetID ); + int n_dims = H5Sget_simple_extent_ndims( dataspace ); + native = H5Tget_native_type( datatype, H5T_DIR_ASCEND ); + hsize_t dims[3], maxdims[3]; + + eDataType = GH5_GetDataType( native ); + + if( n_dims == 2 ) + { + H5Sget_simple_extent_dims( dataspace, dims, maxdims ); + + nRasterXSize = (int) dims[1]; + nRasterYSize = (int) dims[0]; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Dataset not of rank 2." ); + return false; + } + + nBlockXSize = nRasterXSize; + nBlockYSize = 1; + +/* -------------------------------------------------------------------- */ +/* Check for chunksize, and use it as blocksize for optimized */ +/* reading. */ +/* -------------------------------------------------------------------- */ + hid_t listid = H5Dget_create_plist( hDatasetID ); + if (listid>0) + { + if(H5Pget_layout(listid) == H5D_CHUNKED) + { + hsize_t panChunkDims[3]; + int nDimSize = H5Pget_chunk(listid, 3, panChunkDims); + nBlockXSize = (int) panChunkDims[nDimSize-1]; + nBlockYSize = (int) panChunkDims[nDimSize-2]; + } + + int nfilters = H5Pget_nfilters( listid ); + + H5Z_filter_t filter; + char name[120]; + size_t cd_nelmts = 20; + unsigned int cd_values[20]; + unsigned int flags; + for (int i = 0; i < nfilters; i++) + { + filter = H5Pget_filter(listid, i, &flags, (size_t *)&cd_nelmts, cd_values, 120, name); + if (filter == H5Z_FILTER_DEFLATE) + poDS->SetMetadataItem( "COMPRESSION", "DEFLATE", "IMAGE_STRUCTURE" ); + else if (filter == H5Z_FILTER_NBIT) + poDS->SetMetadataItem( "COMPRESSION", "NBIT", "IMAGE_STRUCTURE" ); + else if (filter == H5Z_FILTER_SCALEOFFSET) + poDS->SetMetadataItem( "COMPRESSION", "SCALEOFFSET", "IMAGE_STRUCTURE" ); + else if (filter == H5Z_FILTER_SZIP) + poDS->SetMetadataItem( "COMPRESSION", "SZIP", "IMAGE_STRUCTURE" ); + } + + H5Pclose(listid); + } + +/* -------------------------------------------------------------------- */ +/* Load min/max information. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszName,"elevation") + && GH5_FetchAttribute( hDatasetID, "Maximum Elevation Value", + dfMaximum ) + && GH5_FetchAttribute( hDatasetID, "Minimum Elevation Value", + dfMinimum ) ) + bMinMaxSet = true; + else if( EQUAL(pszName,"uncertainty") + && GH5_FetchAttribute( hDatasetID, "Maximum Uncertainty Value", + dfMaximum ) + && GH5_FetchAttribute( hDatasetID, "Minimum Uncertainty Value", + dfMinimum ) ) + { + /* Some products where uncertainty band is completely set to nodata */ + /* wrongly declare minimum and maximum to 0.0 */ + if( dfMinimum != 0.0 && dfMaximum != 0.0 ) + bMinMaxSet = true; + } + else if( EQUAL(pszName,"nominal_elevation") + && GH5_FetchAttribute( hDatasetID, "max_value", + dfMaximum ) + && GH5_FetchAttribute( hDatasetID, "min_value", + dfMinimum ) ) + bMinMaxSet = true; + + return true; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double BAGRasterBand::GetMinimum( int * pbSuccess ) + +{ + if( bMinMaxSet ) + { + if( pbSuccess ) + *pbSuccess = TRUE; + return dfMinimum; + } + else + return GDALRasterBand::GetMinimum( pbSuccess ); +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double BAGRasterBand::GetMaximum( int * pbSuccess ) + +{ + if( bMinMaxSet ) + { + if( pbSuccess ) + *pbSuccess = TRUE; + return dfMaximum; + } + else + return GDALRasterBand::GetMaximum( pbSuccess ); +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ +double BAGRasterBand::GetNoDataValue( int * pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + if( EQUAL(GetDescription(),"elevation") ) + return 1000000.0; + else if( EQUAL(GetDescription(),"uncertainty") ) + return 1000000.0; + else if( EQUAL(GetDescription(),"nominal_elevation") ) + return 1000000.0; + else + return GDALPamRasterBand::GetNoDataValue( pbSuccess ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ +CPLErr BAGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + herr_t status; + hsize_t count[3]; + H5OFFSET_TYPE offset[3]; + int nSizeOfData; + hid_t memspace; + hsize_t col_dims[3]; + hsize_t rank; + + rank=2; + + offset[0] = MAX(0,nRasterYSize - (nBlockYOff+1)*nBlockYSize); + offset[1] = nBlockXOff*nBlockXSize; + count[0] = nBlockYSize; + count[1] = nBlockXSize; + + nSizeOfData = H5Tget_size( native ); + memset( pImage,0,nBlockXSize*nBlockYSize*nSizeOfData ); + +/* blocksize may not be a multiple of imagesize */ + count[0] = MIN( size_t(nBlockYSize), GetYSize() - offset[0]); + count[1] = MIN( size_t(nBlockXSize), GetXSize() - offset[1]); + + if( nRasterYSize - (nBlockYOff+1)*nBlockYSize < 0 ) + { + count[0] += (nRasterYSize - (nBlockYOff+1)*nBlockYSize); + } + +/* -------------------------------------------------------------------- */ +/* Select block from file space */ +/* -------------------------------------------------------------------- */ + status = H5Sselect_hyperslab( dataspace, + H5S_SELECT_SET, + offset, NULL, + count, NULL ); + +/* -------------------------------------------------------------------- */ +/* Create memory space to receive the data */ +/* -------------------------------------------------------------------- */ + col_dims[0]=nBlockYSize; + col_dims[1]=nBlockXSize; + memspace = H5Screate_simple( (int) rank, col_dims, NULL ); + H5OFFSET_TYPE mem_offset[3] = {0, 0, 0}; + status = H5Sselect_hyperslab(memspace, + H5S_SELECT_SET, + mem_offset, NULL, + count, NULL); + + status = H5Dread ( hDatasetID, + native, + memspace, + dataspace, + H5P_DEFAULT, + pImage ); + + H5Sclose( memspace ); + +/* -------------------------------------------------------------------- */ +/* Y flip the data. */ +/* -------------------------------------------------------------------- */ + int nLinesToFlip = count[0]; + int nLineSize = nSizeOfData * nBlockXSize; + GByte *pabyTemp = (GByte *) CPLMalloc(nLineSize); + + for( int iY = 0; iY < nLinesToFlip/2; iY++ ) + { + memcpy( pabyTemp, + ((GByte *)pImage) + iY * nLineSize, + nLineSize ); + memcpy( ((GByte *)pImage) + iY * nLineSize, + ((GByte *)pImage) + (nLinesToFlip-iY-1) * nLineSize, + nLineSize ); + memcpy( ((GByte *)pImage) + (nLinesToFlip-iY-1) * nLineSize, + pabyTemp, + nLineSize ); + } + + CPLFree( pabyTemp ); + +/* -------------------------------------------------------------------- */ +/* Return success or failure. */ +/* -------------------------------------------------------------------- */ + if( status < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "H5Dread() failed for block." ); + return CE_Failure; + } + else + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* BAGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* BAGDataset() */ +/************************************************************************/ + +BAGDataset::BAGDataset() +{ + hHDF5 = -1; + pszXMLMetadata = NULL; + pszProjection = NULL; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~BAGDataset() */ +/************************************************************************/ +BAGDataset::~BAGDataset( ) +{ + FlushCache(); + + if( hHDF5 >= 0 ) + H5Fclose( hHDF5 ); + + CPLFree( pszXMLMetadata ); + CPLFree( pszProjection ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int BAGDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Is it an HDF5 file? */ +/* -------------------------------------------------------------------- */ + static const char achSignature[] = "\211HDF\r\n\032\n"; + + if( poOpenInfo->pabyHeader == NULL + || memcmp(poOpenInfo->pabyHeader,achSignature,8) != 0 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Does it have the extension .bag? */ +/* -------------------------------------------------------------------- */ + if( !EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"bag") ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *BAGDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Confirm that this appears to be a BAG file. */ +/* -------------------------------------------------------------------- */ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The BAG driver does not support update access." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open the file as an HDF5 file. */ +/* -------------------------------------------------------------------- */ + hid_t hHDF5 = H5Fopen( poOpenInfo->pszFilename, + H5F_ACC_RDONLY, H5P_DEFAULT ); + + if( hHDF5 < 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm it is a BAG dataset by checking for the */ +/* BAG_Root/Bag Version attribute. */ +/* -------------------------------------------------------------------- */ + hid_t hBagRoot = H5Gopen( hHDF5, "/BAG_root" ); + hid_t hVersion = -1; + + if( hBagRoot >= 0 ) + hVersion = H5Aopen_name( hBagRoot, "Bag Version" ); + + if( hVersion < 0 ) + { + if( hBagRoot >= 0 ) + H5Gclose( hBagRoot ); + H5Fclose( hHDF5 ); + return NULL; + } + H5Aclose( hVersion ); + +/* -------------------------------------------------------------------- */ +/* Create a corresponding dataset. */ +/* -------------------------------------------------------------------- */ + BAGDataset *poDS = new BAGDataset(); + + poDS->hHDF5 = hHDF5; + +/* -------------------------------------------------------------------- */ +/* Extract version as metadata. */ +/* -------------------------------------------------------------------- */ + CPLString osVersion; + + if( GH5_FetchAttribute( hBagRoot, "Bag Version", osVersion ) ) + poDS->SetMetadataItem( "BagVersion", osVersion ); + + H5Gclose( hBagRoot ); + +/* -------------------------------------------------------------------- */ +/* Fetch the elevation dataset and attach as a band. */ +/* -------------------------------------------------------------------- */ + int nNextBand = 1; + hid_t hElevation = H5Dopen( hHDF5, "/BAG_root/elevation" ); + if( hElevation < 0 ) + { + delete poDS; + return NULL; + } + + BAGRasterBand *poElevBand = new BAGRasterBand( poDS, nNextBand ); + + if( !poElevBand->Initialize( hElevation, "elevation" ) ) + { + delete poElevBand; + delete poDS; + return NULL; + } + + poDS->nRasterXSize = poElevBand->nRasterXSize; + poDS->nRasterYSize = poElevBand->nRasterYSize; + + poDS->SetBand( nNextBand++, poElevBand ); + +/* -------------------------------------------------------------------- */ +/* Try to do the same for the uncertainty band. */ +/* -------------------------------------------------------------------- */ + hid_t hUncertainty = H5Dopen( hHDF5, "/BAG_root/uncertainty" ); + BAGRasterBand *poUBand = new BAGRasterBand( poDS, nNextBand ); + + if( hUncertainty >= 0 && poUBand->Initialize( hUncertainty, "uncertainty") ) + { + poDS->SetBand( nNextBand++, poUBand ); + } + else + delete poUBand; + +/* -------------------------------------------------------------------- */ +/* Try to do the same for the nominal_elevation band. */ +/* -------------------------------------------------------------------- */ + hid_t hNominal = -1; + + H5E_BEGIN_TRY { + hNominal = H5Dopen( hHDF5, "/BAG_root/nominal_elevation" ); + } H5E_END_TRY; + + BAGRasterBand *poNBand = new BAGRasterBand( poDS, nNextBand ); + if( hNominal >= 0 && poNBand->Initialize( hNominal, + "nominal_elevation" ) ) + { + poDS->SetBand( nNextBand++, poNBand ); + } + else + delete poNBand; + +/* -------------------------------------------------------------------- */ +/* Load the XML metadata. */ +/* -------------------------------------------------------------------- */ + poDS->LoadMetadata(); + +/* -------------------------------------------------------------------- */ +/* Setup/check for pam .aux.xml. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Setup overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* LoadMetadata() */ +/************************************************************************/ + +void BAGDataset::LoadMetadata() + +{ +/* -------------------------------------------------------------------- */ +/* Load the metadata from the file. */ +/* -------------------------------------------------------------------- */ + hid_t hMDDS = H5Dopen( hHDF5, "/BAG_root/metadata" ); + hid_t datatype = H5Dget_type( hMDDS ); + hid_t dataspace = H5Dget_space( hMDDS ); + hid_t native = H5Tget_native_type( datatype, H5T_DIR_ASCEND ); + hsize_t dims[3], maxdims[3]; + + H5Sget_simple_extent_dims( dataspace, dims, maxdims ); + + pszXMLMetadata = (char *) CPLCalloc((int) (dims[0]+1),1); + + H5Dread( hMDDS, native, H5S_ALL, dataspace, H5P_DEFAULT, pszXMLMetadata ); + + H5Tclose( native ); + H5Sclose( dataspace ); + H5Tclose( datatype ); + H5Dclose( hMDDS ); + + if( strlen(pszXMLMetadata) == 0 ) + return; + +/* -------------------------------------------------------------------- */ +/* Try to get the geotransform. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRoot = CPLParseXMLString( pszXMLMetadata ); + + if( psRoot == NULL ) + return; + + CPLStripXMLNamespace( psRoot, NULL, TRUE ); + + CPLXMLNode *psGeo = CPLSearchXMLNode( psRoot, "=MD_Georectified" ); + + if( psGeo != NULL ) + { + char **papszCornerTokens = + CSLTokenizeStringComplex( + CPLGetXMLValue( psGeo, "cornerPoints.Point.coordinates", "" ), + " ,", FALSE, FALSE ); + + if( CSLCount(papszCornerTokens ) == 4 ) + { + double dfLLX = CPLAtof( papszCornerTokens[0] ); + double dfLLY = CPLAtof( papszCornerTokens[1] ); + double dfURX = CPLAtof( papszCornerTokens[2] ); + double dfURY = CPLAtof( papszCornerTokens[3] ); + + adfGeoTransform[0] = dfLLX; + adfGeoTransform[1] = (dfURX - dfLLX) / (GetRasterXSize()-1); + adfGeoTransform[3] = dfURY; + adfGeoTransform[5] = (dfLLY - dfURY) / (GetRasterYSize()-1); + + adfGeoTransform[0] -= adfGeoTransform[1] * 0.5; + adfGeoTransform[3] -= adfGeoTransform[5] * 0.5; + } + CSLDestroy( papszCornerTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Try to get the coordinate system. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + + if( OGR_SRS_ImportFromISO19115( &oSRS, pszXMLMetadata ) + == OGRERR_NONE ) + { + oSRS.exportToWkt( &pszProjection ); + } + else + { + ParseWKTFromXML( pszXMLMetadata ); + } + +/* -------------------------------------------------------------------- */ +/* Fetch acquisition date. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psDateTime = CPLSearchXMLNode( psRoot, "=dateTime" ); + if( psDateTime != NULL ) + { + const char *pszDateTimeValue = CPLGetXMLValue( psDateTime, NULL, "" ); + if( pszDateTimeValue ) + SetMetadataItem( "BAG_DATETIME", pszDateTimeValue ); + } + + CPLDestroyXMLNode( psRoot ); +} + +/************************************************************************/ +/* ParseWKTFromXML() */ +/************************************************************************/ +OGRErr BAGDataset::ParseWKTFromXML( const char *pszISOXML ) +{ + OGRSpatialReference oSRS; + CPLXMLNode *psRoot = CPLParseXMLString( pszISOXML ); + OGRErr eOGRErr = OGRERR_FAILURE; + + if( psRoot == NULL ) + return eOGRErr; + + CPLStripXMLNamespace( psRoot, NULL, TRUE ); + + CPLXMLNode *psRSI = CPLSearchXMLNode( psRoot, "=referenceSystemInfo" ); + if( psRSI == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find in metadata." ); + CPLDestroyXMLNode( psRoot ); + return eOGRErr; + } + + oSRS.Clear(); + + const char *pszSRCodeString = + CPLGetXMLValue( psRSI, "MD_ReferenceSystem.referenceSystemIdentifier.RS_Identifier.code.CharacterString", NULL ); + if( pszSRCodeString == NULL ) + { + CPLDebug("BAG", + "Unable to find /MI_Metadata/referenceSystemInfo[1]/MD_ReferenceSystem[1]/referenceSystemIdentifier[1]/RS_Identifier[1]/code[1]/CharacterString[1] in metadata." ); + CPLDestroyXMLNode( psRoot ); + return eOGRErr; + } + + const char *pszSRCodeSpace = + CPLGetXMLValue( psRSI, "MD_ReferenceSystem.referenceSystemIdentifier.RS_Identifier.codeSpace.CharacterString", "" ); + if( !EQUAL( pszSRCodeSpace, "WKT" ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Spatial reference string is not in WKT." ); + CPLDestroyXMLNode( psRoot ); + return eOGRErr; + } + + char* pszWKT = const_cast< char* >( pszSRCodeString ); + if( oSRS.importFromWkt( &pszWKT ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed parsing WKT string \"%s\".", pszSRCodeString ); + CPLDestroyXMLNode( psRoot ); + return eOGRErr; + } + + oSRS.exportToWkt( &pszProjection ); + eOGRErr = OGRERR_NONE; + + psRSI = CPLSearchXMLNode( psRSI->psNext, "=referenceSystemInfo" ); + if( psRSI == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find second instance of in metadata." ); + CPLDestroyXMLNode( psRoot ); + return eOGRErr; + } + + pszSRCodeString = + CPLGetXMLValue( psRSI, "MD_ReferenceSystem.referenceSystemIdentifier.RS_Identifier.code.CharacterString", NULL ); + if( pszSRCodeString == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find /MI_Metadata/referenceSystemInfo[2]/MD_ReferenceSystem[1]/referenceSystemIdentifier[1]/RS_Identifier[1]/code[1]/CharacterString[1] in metadata." ); + CPLDestroyXMLNode( psRoot ); + return eOGRErr; + } + + pszSRCodeSpace = + CPLGetXMLValue( psRSI, "MD_ReferenceSystem.referenceSystemIdentifier.RS_Identifier.codeSpace.CharacterString", "" ); + if( !EQUAL( pszSRCodeSpace, "WKT" ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Spatial reference string is not in WKT." ); + CPLDestroyXMLNode( psRoot ); + return eOGRErr; + } + + if( EQUALN(pszSRCodeString, "VERTCS", 6 ) ) + { + CPLString oString( pszProjection ); + oString += ","; + oString += pszSRCodeString; + if ( pszProjection ) + CPLFree( pszProjection ); + pszProjection = CPLStrdup( oString ); + } + + CPLDestroyXMLNode( psRoot ); + + return eOGRErr; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr BAGDataset::GetGeoTransform( double *padfGeoTransform ) + +{ + if( adfGeoTransform[0] != 0.0 || adfGeoTransform[3] != 0.0 ) + { + memcpy( padfGeoTransform, adfGeoTransform, sizeof(double)*6 ); + return CE_None; + } + else + return GDALPamDataset::GetGeoTransform( padfGeoTransform ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *BAGDataset::GetProjectionRef() + +{ + if( pszProjection ) + return pszProjection; + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **BAGDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "xml:BAG", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **BAGDataset::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain != NULL && EQUAL(pszDomain,"xml:BAG") ) + { + apszMDList[0] = pszXMLMetadata; + apszMDList[1] = NULL; + + return apszMDList; + } + else + return GDALPamDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* GDALRegister_BAG() */ +/************************************************************************/ +void GDALRegister_BAG( ) + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("BAG")) + return; + + if( GDALGetDriverByName( "BAG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "BAG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Bathymetry Attributed Grid" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_bag.html" ); + poDriver->pfnOpen = BAGDataset::Open; + poDriver->pfnIdentify = BAGDataset::Identify; + + GetGDALDriverManager( )->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/hdf5/frmt_bag.html b/bazaar/plugin/gdal/frmts/hdf5/frmt_bag.html new file mode 100644 index 000000000..7cd95ef45 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf5/frmt_bag.html @@ -0,0 +1,38 @@ + + + + +BAG --- Bathymetry Attributed Grid + + + + +

BAG --- Bathymetry Attributed Grid

+ +This driver provides read-only support for bathymetry data in the BAG format. +BAG files are actually a specific product profile in an HDF5 file, but a +custom driver exists to present the data in a more convenient manner than +is available through the generic HDF5 driver.

+ +BAG files have two or three image bands representing Elevation (band 1), +Uncertainty (band 2) and Nominal Elevation (band 3) values for each cell in a +raster grid area.

+ +The geotransform and coordinate system is extracted from the internal XML +metadata provided with the dataset. However, some products may have +unsupported coordinate system formats.

+ +The full XML metadata is available in the "xml:BAG" metadata domain.

+ +Nodata, minimum and maximum values for each band are also provided.

+ +

See Also:

+ + + + + diff --git a/bazaar/plugin/gdal/frmts/hdf5/frmt_hdf5.html b/bazaar/plugin/gdal/frmts/hdf5/frmt_hdf5.html new file mode 100644 index 000000000..d20ef1997 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf5/frmt_hdf5.html @@ -0,0 +1,231 @@ + + + + +HDF5 --- Hierarchical Data Format Release 5 (HDF5) + + + + +

HDF5 --- Hierarchical Data Format Release 5 (HDF5)

+ +This driver intended for HDF5 file formats importing. This +modification is +suited for use with remote sensing data and fully compatible with +underlying HDF5. This driver can import HDF5-EOS files. Currently EOS use +HDF5 for data storing (telemetry form `Aura' satellites). In +the future they will switch to HDF5 format, which will be used for +telemetry from `Aura' satellite.

+ +

Multiple Image Handling (Subdatasets)

+ +Hierarchical Data Format is a container for several different datasets. +For data storing. HDF contains multidimensional arrays filled by data. +One HDF file may contain several arrays. They may differ in size, +number of dimensions.

+ +The first step is to get a report of the components images (arrays) in the +file using gdalinfo, and +then to import the desired images using gdal_translate. + +The gdalinfo utility lists all multidimensional subdatasets from the +input HDF file. The name of individual images (subdatasets) are assigned to +the SUBDATASET_n_NAME metadata item. The description for each image is +found in the SUBDATASET_n_DESC metadata item. For HDF5 images the +subdataset names will be formatted like this:

+ +HDF5:file_name:subdataset

+ +where:
file_name is the name of the input file, and
+subdataset is the dataset name of the array to use (for internal use in +GDAL).

+ +On the second step you should provide this name for gdalinfo or +gdal_translate for actual reading of the data.

+ +For example, we want to read data from the OMI/Aura Ozone (O3) dataset:

+

+$ gdalinfo OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5
+Driver: HDF5/Hierarchical Data Format Release 5
+Size is 512, 512
+Coordinate System is `'
+
+Subdatasets:
+  SUBDATASET_1_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/APrioriLayerO3
+  SUBDATASET_1_DESC=[1496x60x11] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/APrioriLayerO3 (32-bit floating-point)
+  SUBDATASET_2_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/AlgorithmFlags
+  SUBDATASET_2_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/AlgorithmFlags (8-bit unsigned character)
+  SUBDATASET_3_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/CloudFraction
+  SUBDATASET_3_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/CloudFraction (32-bit floating-point)
+  SUBDATASET_4_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/CloudTopPressure
+  SUBDATASET_4_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/CloudTopPressure (32-bit floating-point)
+  SUBDATASET_5_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/ColumnAmountO3
+  SUBDATASET_5_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/ColumnAmountO3 (32-bit floating-point)
+  SUBDATASET_6_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/LayerEfficiency
+  SUBDATASET_6_DESC=[1496x60x11] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/LayerEfficiency (32-bit floating-point)
+  SUBDATASET_7_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/NValue
+  SUBDATASET_7_DESC=[1496x60x12] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/NValue (32-bit floating-point)
+  SUBDATASET_8_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/O3BelowCloud
+  SUBDATASET_8_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/O3BelowCloud (32-bit floating-point)
+  SUBDATASET_9_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/QualityFlags
+  SUBDATASET_9_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/QualityFlags (16-bit unsigned integer)
+  SUBDATASET_10_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/Reflectivity331
+  SUBDATASET_10_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/Reflectivity331 (32-bit floating-point)
+  SUBDATASET_11_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/Reflectivity360
+  SUBDATASET_11_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/Reflectivity360 (32-bit floating-point)
+  SUBDATASET_12_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/Residual
+  SUBDATASET_12_DESC=[1496x60x12] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/Residual (32-bit floating-point)
+  SUBDATASET_13_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/ResidualStep1
+  SUBDATASET_13_DESC=[1496x60x12] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/ResidualStep1 (32-bit floating-point)
+  SUBDATASET_14_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/ResidualStep2
+  SUBDATASET_14_DESC=[1496x60x12] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/ResidualStep2 (32-bit floating-point)
+  SUBDATASET_15_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/SO2index
+  SUBDATASET_15_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/SO2index (32-bit floating-point)
+  SUBDATASET_16_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/Sensitivity
+  SUBDATASET_16_DESC=[1496x60x12] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/Sensitivity (32-bit floating-point)
+  SUBDATASET_17_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/StepOneO3
+  SUBDATASET_17_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/StepOneO3 (32-bit floating-point)
+  SUBDATASET_18_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/StepTwoO3
+  SUBDATASET_18_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/StepTwoO3 (32-bit floating-point)
+  SUBDATASET_19_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/TerrainPressure
+  SUBDATASET_19_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/TerrainPressure (32-bit floating-point)
+  SUBDATASET_20_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/UVAerosolIndex
+  SUBDATASET_20_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/UVAerosolIndex (32-bit floating-point)
+  SUBDATASET_21_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/dN_dR
+  SUBDATASET_21_DESC=[1496x60x12] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/dN_dR (32-bit floating-point)
+  SUBDATASET_22_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/dN_dT
+  SUBDATASET_22_DESC=[1496x60x12] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Data_Fields/dN_dT (32-bit floating-point)
+  SUBDATASET_23_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/GroundPixelQualityFlags
+  SUBDATASET_23_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/GroundPixelQualityFlags (16-bit unsigned integer)
+  SUBDATASET_24_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/Latitude
+  SUBDATASET_24_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/Latitude (32-bit floating-point)
+  SUBDATASET_25_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/Longitude
+  SUBDATASET_25_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/Longitude (32-bit floating-point)
+  SUBDATASET_26_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/RelativeAzimuthAngle
+  SUBDATASET_26_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/RelativeAzimuthAngle (32-bit floating-point)
+  SUBDATASET_27_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/SolarAzimuthAngle
+  SUBDATASET_27_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/SolarAzimuthAngle (32-bit floating-point)
+  SUBDATASET_28_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/SolarZenithAngle
+  SUBDATASET_28_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/SolarZenithAngle (32-bit floating-point)
+  SUBDATASET_29_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/TerrainHeight
+  SUBDATASET_29_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/TerrainHeight (16-bit integer)
+  SUBDATASET_30_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/ViewingAzimuthAngle
+  SUBDATASET_30_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/ViewingAzimuthAngle (32-bit floating-point)
+  SUBDATASET_31_NAME=HDF5:"OMI-Aura_L2-OMTO3_2005m0113t0224-o02648_v002-2005m0625t035355.he5"://HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/ViewingZenithAngle
+  SUBDATASET_31_DESC=[1496x60] //HDFEOS/SWATHS/OMI_Column_Amount_O3/Geolocation_Fields/ViewingZenithAngle (32-bit floating-point)
+Corner Coordinates:
+Upper Left  (    0.0,    0.0)
+Lower Left  (    0.0,  512.0)
+Upper Right (  512.0,    0.0)
+Lower Right (  512.0,  512.0)
+Center      (  256.0,  256.0)
+
+ +Now select one of the subdatasets, described as +[1645x60] CloudFraction (32-bit floating-point):

+

+$ gdalinfo HDF5:"OMI-Aura_L2-OMTO3_2005m0326t2307-o03709_v002-2005m0428t201311.he5":CloudFraction 
+Driver: HDF5Image/HDF5 Dataset
+Size is 60, 1645
+Coordinate System is:
+GEOGCS["WGS 84",
+    DATUM["WGS_1984",
+        SPHEROID["WGS 84",6378137,298.257223563,
+            AUTHORITY["EPSG","7030"]],
+        TOWGS84[0,0,0,0,0,0,0],
+        AUTHORITY["EPSG","6326"]],
+    PRIMEM["Greenwich",0,
+        AUTHORITY["EPSG","8901"]],
+    UNIT["degree",0.0174532925199433,
+        AUTHORITY["EPSG","9108"]],
+    AXIS["Lat",NORTH],
+    AXIS["Long",EAST],
+    AUTHORITY["EPSG","4326"]]
+GCP Projection = GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9108"]],AXIS["Lat",NORTH],AXIS["Long",EAST],AUTHORITY["EPSG","4326"]]
+GCP[  0]: Id=, Info=
+          (0.5,0.5) -> (261.575,-84.3495,0)
+GCP[  1]: Id=, Info=
+          (2.5,0.5) -> (240.826,-85.9928,0)
+GCP[  2]: Id=, Info=
+          (4.5,0.5) -> (216.754,-86.5932,0)
+GCP[  3]: Id=, Info=
+          (6.5,0.5) -> (195.5,-86.5541,0)
+GCP[  4]: Id=, Info=
+          (8.5,0.5) -> (180.265,-86.2009,0)
+GCP[  5]: Id=, Info=
+          (10.5,0.5) -> (170.011,-85.7315,0)
+GCP[  6]: Id=, Info=
+          (12.5,0.5) -> (162.987,-85.2337,0)
+
... 3000 GCPs are read from the file if Latitude and Longitude arrays are presents 
+Corner Coordinates: +Upper Left ( 0.0, 0.0) +Lower Left ( 0.0, 1645.0) +Upper Right ( 60.0, 0.0) +Lower Right ( 60.0, 1645.0) +Center ( 30.0, 822.5) +Band 1 Block=60x1 Type=Float32, ColorInterp=Undefined +Open GDAL Datasets: + 1 N DriverIsNULL 512x512x0 +
+ +You may use gdal_translate for reading image bands from this +dataset.

+ +Note that you should provide exactly the contents of the line marked +SUBDATASET_n_NAME to GDAL, including the HDF5: prefix.

+ +This driver is intended only for importing remote sensing and geospatial +datasets in form of raster images(2D or 3D arrays). If you want explore all +data contained in HDF file you should use another tools (you can find +information about different HDF tools using links at end of this page). + +

Georeference

+ +There is no universal way of storing georeferencing in HDF files. However, +some product types have mechanisms for saving georeferencing, and some of +these are supported by GDAL. Currently supported are (subdataset_type +shown in parenthesis):

+ +

    +
  • HDF5 OMI/Aura Ozone (O3) Total Column 1-Orbit L2 Swath 13x24km (Level-2 OMTO3) +
+ +

Metadata

+ +No Metadata are read at this time from the HDF5 files. + +

Driver building

+ +This driver builded on top of NCSA HDF5 library, so you need to dowload +prebuild HDF5 librariesI HDF5-1.6.4 library or higher. You also need zlib 1.2 +and szlib 2.0. For windows user be sure to set the attributes writable +(especialy if you are using cygwin) and that the DLLs can be located +somewhere by your PATH environment variable. + +You may also download source code NCSA HDF +Home Page (see links below).

+ + +

See Also:

+ +
    +
  • Implemented as gdal/frmts/hdf5/hdf5dataset.cpp +and gdal/frmts/hdf5/hdf5imagedataset.cpp.

    + +

  • The NCSA HDF5 Dowload Page +at the + +National Center for Supercomputing Applications + +
  • The HDFView is a visual tool for browsing and editing NCSA HDF4 and HDF5 files.

    + +Documentation to individual products, supported by this driver:

    +

    + + + diff --git a/bazaar/plugin/gdal/frmts/hdf5/gh5_convenience.cpp b/bazaar/plugin/gdal/frmts/hdf5/gh5_convenience.cpp new file mode 100644 index 000000000..ca48e9039 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf5/gh5_convenience.cpp @@ -0,0 +1,227 @@ +/****************************************************************************** + * $Id: gh5_convenience.cpp 26010 2013-05-17 23:31:00Z warmerdam $ + * + * Project: Hierarchical Data Format Release 5 (HDF5) + * Purpose: HDF5 convenience functions. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gh5_convenience.h" + +CPL_CVSID("$Id: gh5_convenience.cpp 26010 2013-05-17 23:31:00Z warmerdam $"); + +/************************************************************************/ +/* GH5_FetchAttribute(CPLString) */ +/************************************************************************/ + +bool GH5_FetchAttribute( hid_t loc_id, const char *pszAttrName, + CPLString &osResult, bool bReportError ) + +{ + bool retVal = false; + + hid_t hAttr = H5Aopen_name( loc_id, pszAttrName ); + + osResult.clear(); + + if( hAttr < 0 ) + { + if( bReportError ) + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to read attribute %s failed, not found.", + pszAttrName ); + return false; + } + + hid_t hAttrTypeID = H5Aget_type( hAttr ); + hid_t hAttrNativeType = H5Tget_native_type( hAttrTypeID, H5T_DIR_DEFAULT ); + + if( H5Tget_class( hAttrNativeType ) == H5T_STRING ) + { + int nAttrSize = H5Tget_size( hAttrTypeID ); + char *pachBuffer = (char *) CPLCalloc(nAttrSize+1,1); + H5Aread( hAttr, hAttrNativeType, pachBuffer ); + + osResult = pachBuffer; + CPLFree( pachBuffer ); + + retVal = true; + } + + else + { + if( bReportError ) + CPLError( CE_Failure, CPLE_AppDefined, + "Attribute %s of unsupported type for conversion to string.", + pszAttrName ); + + retVal = false; + } + + H5Tclose( hAttrNativeType ); + H5Tclose( hAttrTypeID ); + H5Aclose( hAttr ); + return retVal; +} + +/************************************************************************/ +/* GH5_FetchAttribute(double) */ +/************************************************************************/ + +bool GH5_FetchAttribute( hid_t loc_id, const char *pszAttrName, + double &dfResult, bool bReportError ) + +{ + hid_t hAttr = H5Aopen_name( loc_id, pszAttrName ); + + dfResult = 0.0; + if( hAttr < 0 ) + { + if( bReportError ) + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to read attribute %s failed, not found.", + pszAttrName ); + return false; + } + + hid_t hAttrTypeID = H5Aget_type( hAttr ); + hid_t hAttrNativeType = H5Tget_native_type( hAttrTypeID, H5T_DIR_DEFAULT ); + +/* -------------------------------------------------------------------- */ +/* Confirm that we have a single element value. */ +/* -------------------------------------------------------------------- */ + + hid_t hAttrSpace = H5Aget_space( hAttr ); + hsize_t anSize[64]; + int nAttrDims = H5Sget_simple_extent_dims( hAttrSpace, anSize, NULL ); + + int i, nAttrElements = 1; + + for( i=0; i < nAttrDims; i++ ) { + nAttrElements *= (int) anSize[i]; + } + + if( nAttrElements != 1 ) + { + if( bReportError ) + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to read attribute %s failed, count=%d, not 1.", + pszAttrName, nAttrElements ); + + H5Sclose( hAttrSpace ); + H5Tclose( hAttrNativeType ); + H5Tclose( hAttrTypeID ); + H5Aclose( hAttr ); + return false; + } + +/* -------------------------------------------------------------------- */ +/* Read the value. */ +/* -------------------------------------------------------------------- */ + void *buf = (void *)CPLMalloc( H5Tget_size( hAttrNativeType )); + H5Aread( hAttr, hAttrNativeType, buf ); + +/* -------------------------------------------------------------------- */ +/* Translate to double. */ +/* -------------------------------------------------------------------- */ + if( H5Tequal( H5T_NATIVE_INT, hAttrNativeType ) ) + dfResult = *((int *) buf); + else if( H5Tequal( H5T_NATIVE_FLOAT, hAttrNativeType ) ) + dfResult = *((float *) buf); + else if( H5Tequal( H5T_NATIVE_DOUBLE, hAttrNativeType ) ) + dfResult = *((double *) buf); + else + { + if( bReportError ) + CPLError( CE_Failure, CPLE_AppDefined, + "Attribute %s of unsupported type for conversion to double.", + pszAttrName ); + CPLFree( buf ); + + H5Sclose( hAttrSpace ); + H5Tclose( hAttrNativeType ); + H5Tclose( hAttrTypeID ); + H5Aclose( hAttr ); + + return false; + } + + CPLFree( buf ); + + H5Sclose( hAttrSpace ); + H5Tclose( hAttrNativeType ); + H5Tclose( hAttrTypeID ); + H5Aclose( hAttr ); + return true; +} + +/************************************************************************/ +/* GH5_GetDataType() */ +/* */ +/* Transform HDF5 datatype to GDAL datatype */ +/************************************************************************/ +GDALDataType GH5_GetDataType(hid_t TypeID) +{ + if( H5Tequal( H5T_NATIVE_CHAR, TypeID ) ) + return GDT_Byte; + else if( H5Tequal( H5T_NATIVE_SCHAR, TypeID ) ) + return GDT_Byte; + else if( H5Tequal( H5T_NATIVE_UCHAR, TypeID ) ) + return GDT_Byte; + else if( H5Tequal( H5T_NATIVE_SHORT, TypeID ) ) + return GDT_Int16; + else if( H5Tequal( H5T_NATIVE_USHORT, TypeID ) ) + return GDT_UInt16; + else if( H5Tequal( H5T_NATIVE_INT, TypeID ) ) + return GDT_Int32; + else if( H5Tequal( H5T_NATIVE_UINT, TypeID ) ) + return GDT_UInt32; + else if( H5Tequal( H5T_NATIVE_LONG, TypeID ) ) + { + if( sizeof(long) == 4 ) + return GDT_Int32; + else + return GDT_Unknown; + } + else if( H5Tequal( H5T_NATIVE_ULONG, TypeID ) ) + { + if( sizeof(unsigned long) == 4 ) + return GDT_UInt32; + else + return GDT_Unknown; + } + else if( H5Tequal( H5T_NATIVE_FLOAT, TypeID ) ) + return GDT_Float32; + else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) ) + return GDT_Float64; + else if( H5Tequal( H5T_NATIVE_LLONG, TypeID ) ) + return GDT_Unknown; + else if( H5Tequal( H5T_NATIVE_ULLONG, TypeID ) ) + return GDT_Unknown; + else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) ) + return GDT_Unknown; + + return GDT_Unknown; +} + diff --git a/bazaar/plugin/gdal/frmts/hdf5/gh5_convenience.h b/bazaar/plugin/gdal/frmts/hdf5/gh5_convenience.h new file mode 100644 index 000000000..cf5ae1854 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf5/gh5_convenience.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * $Id: gh5_convenience.h 17985 2009-11-10 13:39:46Z rouault $ + * + * Project: Hierarchical Data Format Release 5 (HDF5) + * Purpose: HDF5 convenience functions. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _GH5_CONVENIENCE_H_INCLUDED_ +#define _GH5_CONVENIENCE_H_INCLUDED_ + +#define H5_USE_16_API + +#include "hdf5.h" + +#include "cpl_string.h" +#include "gdal.h" + +/* release 1.6.3 or 1.6.4 changed the type of count in some api functions */ + +#if H5_VERS_MAJOR == 1 && H5_VERS_MINOR <= 6 \ + && (H5_VERS_MINOR < 6 || H5_VERS_RELEASE < 3) +# define H5OFFSET_TYPE hssize_t +#else +# define H5OFFSET_TYPE hsize_t +#endif + +bool GH5_FetchAttribute( hid_t loc_id, const char *pszName, + CPLString &osResult, bool bReportError = false ); +bool GH5_FetchAttribute( hid_t loc_id, const char *pszName, + double &dfResult, bool bReportError = false ); +GDALDataType GH5_GetDataType(hid_t TypeID); + +#endif /* ndef _GH5_CONVENIENCE_H_INCLUDED_ */ diff --git a/bazaar/plugin/gdal/frmts/hdf5/hdf5dataset.cpp b/bazaar/plugin/gdal/frmts/hdf5/hdf5dataset.cpp new file mode 100644 index 000000000..abd791030 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf5/hdf5dataset.cpp @@ -0,0 +1,1235 @@ +/****************************************************************************** + * $Id: hdf5dataset.cpp 28822 2015-03-30 13:56:19Z rouault $ + * + * Project: Hierarchical Data Format Release 5 (HDF5) + * Purpose: HDF5 Datasets. Open HDF5 file, fetch metadata and list of + * subdatasets. + * This driver initially based on code supplied by Markus Neteler + * Author: Denis Nadeau + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#define H5_USE_16_API + +#define MAX_METADATA_LEN 32768 + +#include "hdf5.h" + +#include "gdal_priv.h" +#include "cpl_string.h" +#include "hdf5dataset.h" + +CPL_CVSID("$Id: hdf5dataset.cpp 28822 2015-03-30 13:56:19Z rouault $"); + +CPL_C_START +void GDALRegister_HDF5(void); +CPL_C_END + + + +/************************************************************************/ +/* ==================================================================== */ +/* HDF5Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALRegister_HDF5() */ +/************************************************************************/ +void GDALRegister_HDF5() + +{ + GDALDriver *poDriver; + if( GDALGetDriverByName("HDF5") == NULL ) + { + poDriver = new GDALDriver(); + poDriver->SetDescription("HDF5"); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, + "Hierarchical Data Format Release 5"); + poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, + "frmt_hdf5.html"); + poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "hdf5"); + poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES"); + poDriver->pfnOpen = HDF5Dataset::Open; + poDriver->pfnIdentify = HDF5Dataset::Identify; + GetGDALDriverManager()->RegisterDriver(poDriver); + } + +#ifdef HDF5_PLUGIN + GDALRegister_HDF5Image(); +#endif + +} + +/************************************************************************/ +/* HDF5Dataset() */ +/************************************************************************/ +HDF5Dataset::HDF5Dataset() +{ + papszSubDatasets = NULL; + papszMetadata = NULL; + poH5RootGroup = NULL; + nSubDataCount = 0; + hHDF5 = -1; + hGroupID = -1; + bIsHDFEOS = FALSE; + nDatasetType = -1; +} + +/************************************************************************/ +/* ~HDF5Dataset() */ +/************************************************************************/ +HDF5Dataset::~HDF5Dataset() +{ + CSLDestroy( papszMetadata ); + if( hGroupID > 0 ) + H5Gclose( hGroupID ); + if( hHDF5 > 0 ) + H5Fclose( hHDF5 ); + CSLDestroy( papszSubDatasets ); + if( poH5RootGroup != NULL ) { + DestroyH5Objects( poH5RootGroup ); + CPLFree( poH5RootGroup->pszName ); + CPLFree( poH5RootGroup->pszPath ); + CPLFree( poH5RootGroup->pszUnderscorePath ); + CPLFree( poH5RootGroup->poHchild ); + CPLFree( poH5RootGroup ); + } +} + +/************************************************************************/ +/* GetDataType() */ +/* */ +/* Transform HDF5 datatype to GDAL datatype */ +/************************************************************************/ +GDALDataType HDF5Dataset::GetDataType(hid_t TypeID) +{ + if( H5Tequal( H5T_NATIVE_CHAR, TypeID ) ) + return GDT_Byte; + else if( H5Tequal( H5T_NATIVE_SCHAR, TypeID ) ) + return GDT_Byte; + else if( H5Tequal( H5T_NATIVE_UCHAR, TypeID ) ) + return GDT_Byte; + else if( H5Tequal( H5T_NATIVE_SHORT, TypeID ) ) + return GDT_Int16; + else if( H5Tequal( H5T_NATIVE_USHORT, TypeID ) ) + return GDT_UInt16; + else if( H5Tequal( H5T_NATIVE_INT, TypeID ) ) + return GDT_Int32; + else if( H5Tequal( H5T_NATIVE_UINT, TypeID ) ) + return GDT_UInt32; + else if( H5Tequal( H5T_NATIVE_LONG, TypeID ) ) + { + if( sizeof(long) == 4 ) + return GDT_Int32; + else + return GDT_Unknown; + } + else if( H5Tequal( H5T_NATIVE_ULONG, TypeID ) ) + { + if( sizeof(unsigned long) == 4 ) + return GDT_UInt32; + else + return GDT_Unknown; + } + else if( H5Tequal( H5T_NATIVE_FLOAT, TypeID ) ) + return GDT_Float32; + else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) ) + return GDT_Float64; + else if( H5Tequal( H5T_NATIVE_LLONG, TypeID ) ) + return GDT_Unknown; + else if( H5Tequal( H5T_NATIVE_ULLONG, TypeID ) ) + return GDT_Unknown; + else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) ) + return GDT_Unknown; + + return GDT_Unknown; +} + +/************************************************************************/ +/* GetDataTypeName() */ +/* */ +/* Return the human readable name of data type */ +/************************************************************************/ +const char *HDF5Dataset::GetDataTypeName(hid_t TypeID) +{ + if( H5Tequal( H5T_NATIVE_CHAR, TypeID ) ) + return "8-bit character"; + else if( H5Tequal( H5T_NATIVE_SCHAR, TypeID ) ) + return "8-bit signed character"; + else if( H5Tequal( H5T_NATIVE_UCHAR, TypeID ) ) + return "8-bit unsigned character"; + else if( H5Tequal( H5T_NATIVE_SHORT, TypeID ) ) + return "16-bit integer"; + else if( H5Tequal( H5T_NATIVE_USHORT, TypeID ) ) + return "16-bit unsigned integer"; + else if( H5Tequal( H5T_NATIVE_INT, TypeID ) ) + return "32-bit integer"; + else if( H5Tequal( H5T_NATIVE_UINT, TypeID ) ) + return "32-bit unsigned integer"; + else if( H5Tequal( H5T_NATIVE_LONG, TypeID ) ) + return "32/64-bit integer"; + else if( H5Tequal( H5T_NATIVE_ULONG, TypeID ) ) + return "32/64-bit unsigned integer"; + else if( H5Tequal( H5T_NATIVE_FLOAT, TypeID ) ) + return "32-bit floating-point"; + else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) ) + return "64-bit floating-point"; + else if( H5Tequal( H5T_NATIVE_LLONG, TypeID ) ) + return "64-bit integer"; + else if( H5Tequal( H5T_NATIVE_ULLONG, TypeID ) ) + return "64-bit unsigned integer"; + else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) ) + return "64-bit floating-point"; + + return "Unknown"; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int HDF5Dataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Is it an HDF5 file? */ +/* -------------------------------------------------------------------- */ + static const char achSignature[] = "\211HDF\r\n\032\n"; + + if( poOpenInfo->pabyHeader ) + { + if( memcmp(poOpenInfo->pabyHeader,achSignature,8) == 0 ) + { + /* The tests to avoid opening KEA and BAG drivers are not */ + /* necessary when drivers are built in the core lib, as they */ + /* are registered after HDF5, but in the case of plugins, we */ + /* cannot do assumptions about the registration order */ + + /* Avoid opening kea files if the kea driver is available */ + if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "KEA") && + GDALGetDriverByName("KEA") != NULL ) + { + return FALSE; + } + + /* Avoid opening kea files if the bag driver is available */ + if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "BAG") && + GDALGetDriverByName("BAG") != NULL ) + { + return FALSE; + } + + return TRUE; + } + + if( memcmp(poOpenInfo->pabyHeader,"",15) == 0) + { + if( H5Fis_hdf5(poOpenInfo->pszFilename) ) + return TRUE; + } + } + + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ +GDALDataset *HDF5Dataset::Open( GDALOpenInfo * poOpenInfo ) +{ + HDF5Dataset *poDS; + + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create datasource. */ +/* -------------------------------------------------------------------- */ + poDS = new HDF5Dataset(); + + poDS->SetDescription( poOpenInfo->pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ + poDS->hHDF5 = H5Fopen( poOpenInfo->pszFilename, + H5F_ACC_RDONLY, + H5P_DEFAULT ); + if( poDS->hHDF5 < 0 ) { + delete poDS; + return NULL; + } + + poDS->hGroupID = H5Gopen( poDS->hHDF5, "/" ); + if( poDS->hGroupID < 0 ) { + poDS->bIsHDFEOS=false; + delete poDS; + return NULL; + } + + poDS->bIsHDFEOS=true; + poDS->ReadGlobalAttributes( true ); + + poDS->SetMetadata( poDS->papszMetadata ); + + if ( CSLCount( poDS->papszSubDatasets ) / 2 >= 1 ) + poDS->SetMetadata( poDS->papszSubDatasets, "SUBDATASETS" ); + + // Make sure we don't try to do any pam stuff with this dataset. + poDS->nPamFlags |= GPF_NOSAVE; + +/* -------------------------------------------------------------------- */ +/* If we have single subdataset only, open it immediately */ +/* -------------------------------------------------------------------- */ + int nSubDatasets = CSLCount( poDS->papszSubDatasets ) / 2; + if( nSubDatasets == 1 ) + { + CPLString osDSName = CSLFetchNameValue( poDS->papszSubDatasets, + "SUBDATASET_1_NAME" ); + delete poDS; + return (GDALDataset *) GDALOpen( osDSName, poOpenInfo->eAccess ); + } + else + { +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + delete poDS; + CPLError( CE_Failure, CPLE_NotSupported, + "The HDF5 driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + } + return( poDS ); +} + +/************************************************************************/ +/* DestroyH5Objects() */ +/* */ +/* Erase all objects */ +/************************************************************************/ +void HDF5Dataset::DestroyH5Objects( HDF5GroupObjects *poH5Object ) +{ + unsigned i; + +/* -------------------------------------------------------------------- */ +/* Visit all objects */ +/* -------------------------------------------------------------------- */ + + for( i=0; i < poH5Object->nbObjs; i++ ) + if( poH5Object->poHchild+i != NULL ) + DestroyH5Objects( poH5Object->poHchild+i ); + + if( poH5Object->poHparent ==NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Erase some data */ +/* -------------------------------------------------------------------- */ + CPLFree( poH5Object->paDims ); + poH5Object->paDims = NULL; + + CPLFree( poH5Object->pszPath ); + poH5Object->pszPath = NULL; + + CPLFree( poH5Object->pszName ); + poH5Object->pszName = NULL; + + CPLFree( poH5Object->pszUnderscorePath ); + poH5Object->pszUnderscorePath = NULL; + + if( poH5Object->native > 0 ) + H5Tclose( poH5Object->native ); + poH5Object->native = 0; + +/* -------------------------------------------------------------------- */ +/* All Children are visited and can be deleted. */ +/* -------------------------------------------------------------------- */ + if( ( i==poH5Object->nbObjs ) && ( poH5Object->nbObjs!=0 ) ) { + CPLFree( poH5Object->poHchild ); + poH5Object->poHchild = NULL; + } + +} + +/************************************************************************/ +/* CreatePath() */ +/* */ +/* Find Dataset path for HDopen */ +/************************************************************************/ +char* CreatePath( HDF5GroupObjects *poH5Object ) +{ + char pszPath[8192]; + char pszUnderscoreSpaceInName[8192]; + char *popszPath; + int i; + char **papszPath; + +/* -------------------------------------------------------------------- */ +/* Recurse to the root path */ +/* -------------------------------------------------------------------- */ + pszPath[0]='\0'; + if( poH5Object->poHparent !=NULL ) { + popszPath=CreatePath( poH5Object->poHparent ); + strcpy( pszPath,popszPath ); + } + +/* -------------------------------------------------------------------- */ +/* add name to the path */ +/* -------------------------------------------------------------------- */ + if( !EQUAL( poH5Object->pszName,"/" ) ){ + strcat( pszPath,"/" ); + strcat( pszPath,poH5Object->pszName ); + } + +/* -------------------------------------------------------------------- */ +/* fill up path for each object */ +/* -------------------------------------------------------------------- */ + if( poH5Object->pszPath == NULL ) { + + if( strlen( poH5Object->pszName ) == 1 ) { + strcat(pszPath, poH5Object->pszName ); + strcpy(pszUnderscoreSpaceInName, poH5Object->pszName); + } + else { +/* -------------------------------------------------------------------- */ +/* Change space for underscore */ +/* -------------------------------------------------------------------- */ + papszPath = CSLTokenizeString2( pszPath, + " ", CSLT_HONOURSTRINGS ); + + strcpy(pszUnderscoreSpaceInName,papszPath[0]); + for( i=1; i < CSLCount( papszPath ); i++ ) { + strcat( pszUnderscoreSpaceInName, "_" ); + strcat( pszUnderscoreSpaceInName, papszPath[ i ] ); + } + CSLDestroy(papszPath); + + } + poH5Object->pszUnderscorePath = + CPLStrdup( pszUnderscoreSpaceInName ); + poH5Object->pszPath = CPLStrdup( pszPath ); + } + + return( poH5Object->pszPath ); +} + +/************************************************************************/ +/* HDF5GroupCheckDuplicate() */ +/* */ +/* Returns TRUE if an ancestor has the same objno[] as passed */ +/* in - used to avoid looping in files with "links up" #(3218). */ +/************************************************************************/ + +static int HDF5GroupCheckDuplicate( HDF5GroupObjects *poHparent, + unsigned long *objno ) + +{ + while( poHparent != NULL ) + { + if( poHparent->objno[0] == objno[0] + && poHparent->objno[1] == objno[1] ) + return TRUE; + + poHparent = poHparent->poHparent; + } + + return FALSE; +} + +/************************************************************************/ +/* HDF5CreateGroupObjs() */ +/* */ +/* Create HDF5 hierarchy into a linked list */ +/************************************************************************/ +herr_t HDF5CreateGroupObjs(hid_t hHDF5, const char *pszObjName, + void *poHObjParent) +{ + hid_t hGroupID; /* identifier of group */ + hid_t hDatasetID; /* identifier of dataset */ + hsize_t nbObjs=0; /* number of objects in a group */ + int nbAttrs=0; /* number of attributes in object */ + unsigned idx; + int n_dims; + H5G_stat_t oStatbuf; + hsize_t *dims=NULL; + hsize_t *maxdims=NULL; + hid_t datatype; + hid_t dataspace; + hid_t native; + + HDF5GroupObjects *poHchild; + HDF5GroupObjects *poHparent; + + poHparent = ( HDF5GroupObjects * ) poHObjParent; + poHchild=poHparent->poHchild; + + if( H5Gget_objinfo( hHDF5, pszObjName, FALSE, &oStatbuf ) < 0 ) + return -1; + + +/* -------------------------------------------------------------------- */ +/* Look for next child */ +/* -------------------------------------------------------------------- */ + for( idx=0; idx < poHparent->nbObjs; idx++ ) { + if( poHchild->pszName == NULL ) break; + poHchild++; + } + + if( idx == poHparent->nbObjs ) + return -1; // all children parsed + +/* -------------------------------------------------------------------- */ +/* Save child information */ +/* -------------------------------------------------------------------- */ + poHchild->pszName = CPLStrdup( pszObjName ); + + poHchild->nType = oStatbuf.type; + poHchild->nIndex = idx; + poHchild->poHparent = poHparent; + poHchild->nRank = 0; + poHchild->paDims = 0; + poHchild->HDatatype = 0; + poHchild->objno[0] = oStatbuf.objno[0]; + poHchild->objno[1] = oStatbuf.objno[1]; + if( poHchild->pszPath == NULL ) { + poHchild->pszPath = CreatePath( poHchild ); + } + if( poHparent->pszPath == NULL ) { + poHparent->pszPath = CreatePath( poHparent ); + } + + + switch ( oStatbuf.type ) + { + case H5G_LINK: + poHchild->nbAttrs = 0; + poHchild->nbObjs = 0; + poHchild->poHchild = NULL; + poHchild->nRank = 0; + poHchild->paDims = 0; + poHchild->HDatatype = 0; + break; + + case H5G_GROUP: + if( ( hGroupID = H5Gopen( hHDF5, pszObjName ) ) == -1 ) { + printf( "Error: unable to access \"%s\" group.\n", + pszObjName ); + return -1; + } + nbAttrs = H5Aget_num_attrs( hGroupID ); + H5Gget_num_objs( hGroupID, &nbObjs ); + poHchild->nbAttrs= nbAttrs; + poHchild->nbObjs = (int) nbObjs; + poHchild->nRank = 0; + poHchild->paDims = 0; + poHchild->HDatatype = 0; + + if( nbObjs > 0 ) { + poHchild->poHchild =( HDF5GroupObjects * ) + CPLCalloc( (int)nbObjs, sizeof( HDF5GroupObjects ) ); + memset( poHchild->poHchild, 0, + (size_t) (sizeof( HDF5GroupObjects ) * nbObjs) ); + } + else + poHchild->poHchild = NULL; + + if( !HDF5GroupCheckDuplicate( poHparent, oStatbuf.objno ) ) + H5Giterate( hHDF5, pszObjName, NULL, + HDF5CreateGroupObjs, (void*) poHchild ); + else + CPLDebug( "HDF5", "avoiding link looping on node '%s'.", + pszObjName ); + + H5Gclose( hGroupID ); + break; + + case H5G_DATASET: + + if( ( hDatasetID = H5Dopen( hHDF5, pszObjName ) ) == -1 ) { + printf( "Error: unable to access \"%s\" dataset.\n", + pszObjName ); + return -1; + } + nbAttrs = H5Aget_num_attrs( hDatasetID ); + datatype = H5Dget_type( hDatasetID ); + dataspace = H5Dget_space( hDatasetID ); + n_dims = H5Sget_simple_extent_ndims( dataspace ); + native = H5Tget_native_type( datatype, H5T_DIR_ASCEND ); + + if( n_dims > 0 ) { + dims = (hsize_t *) CPLCalloc( n_dims,sizeof( hsize_t ) ); + maxdims = (hsize_t *) CPLCalloc( n_dims,sizeof( hsize_t ) ); + } + H5Sget_simple_extent_dims( dataspace, dims, maxdims ); + if( maxdims != NULL ) + CPLFree( maxdims ); + + if( n_dims > 0 ) { + poHchild->nRank = n_dims; // rank of the array + poHchild->paDims = dims; // dimmension of the array. + poHchild->HDatatype = datatype; // HDF5 datatype + } + else { + poHchild->nRank = -1; + poHchild->paDims = NULL; + poHchild->HDatatype = 0; + } + poHchild->nbAttrs = nbAttrs; + poHchild->nbObjs = 0; + poHchild->poHchild = NULL; + poHchild->native = native; + H5Tclose( datatype ); + H5Sclose( dataspace ); + H5Dclose( hDatasetID ); + break; + + case H5G_TYPE: + poHchild->nbAttrs = 0; + poHchild->nbObjs = 0; + poHchild->poHchild = NULL; + poHchild->nRank = 0; + poHchild->paDims = 0; + poHchild->HDatatype = 0; + break; + + default: + break; + } + + return 0; +} + + +/************************************************************************/ +/* HDF5AttrIterate() */ +/************************************************************************/ + +herr_t HDF5AttrIterate( hid_t hH5ObjID, + const char *pszAttrName, + void *pDS ) +{ + hid_t hAttrID; + hid_t hAttrTypeID; + hid_t hAttrNativeType; + hid_t hAttrSpace; + + char *szData = NULL; + hsize_t nSize[64]; + unsigned int nAttrElmts; + hsize_t nAttrSize; + hsize_t i; + void *buf = NULL; + unsigned int nAttrDims; + + char **papszTokens; + + HDF5Dataset *poDS; + CPLString osKey; + char *szValue = NULL; + + poDS = (HDF5Dataset *) pDS; + + // Convert "/" into "_" for the path component + const char* pszPath = poDS->poH5CurrentObject->pszUnderscorePath; + if(pszPath != NULL && strlen(pszPath) > 0) + { + papszTokens = CSLTokenizeString2( pszPath, "/", CSLT_HONOURSTRINGS ); + + for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; ++i ) + { + if( i != 0) + osKey += '_'; + osKey += papszTokens[i]; + } + CSLDestroy( papszTokens ); + } + + // Convert whitespaces into "_" for the attribute name component + papszTokens = CSLTokenizeString2( pszAttrName, " ", + CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; ++i ) + { + if(!osKey.empty()) + osKey += '_'; + osKey += papszTokens[i]; + } + CSLDestroy( papszTokens ); + + hAttrID = H5Aopen_name( hH5ObjID, pszAttrName ); + hAttrTypeID = H5Aget_type( hAttrID ); + hAttrNativeType = H5Tget_native_type( hAttrTypeID, H5T_DIR_DEFAULT ); + hAttrSpace = H5Aget_space( hAttrID ); + nAttrDims = H5Sget_simple_extent_dims( hAttrSpace, nSize, NULL ); + + nAttrElmts = 1; + for( i=0; i < nAttrDims; i++ ) { + nAttrElmts *= (int) nSize[i]; + } + + if( H5Tget_class( hAttrNativeType ) == H5T_STRING ) + { + if ( H5Tis_variable_str(hAttrNativeType) ) + { + char** papszStrings; + papszStrings = (char**) CPLMalloc( nAttrElmts * sizeof(char*) ); + + // Read the values + H5Aread( hAttrID, hAttrNativeType, papszStrings ); + + // Concatenate all values as one string (separated by a space) + CPLString osVal = papszStrings[0]; + for( i=1; i < nAttrElmts; i++ ) { + osVal += " "; + osVal += papszStrings[i]; + } + + szValue = (char*) CPLMalloc(osVal.length() + 1); + strcpy( szValue, osVal.c_str() ); + + H5Dvlen_reclaim( hAttrNativeType, hAttrSpace, H5P_DEFAULT, + papszStrings ); + CPLFree( papszStrings ); + } + else + { + nAttrSize = H5Aget_storage_size( hAttrID ); + szValue = (char*) CPLMalloc((size_t) (nAttrSize+1)); + H5Aread( hAttrID, hAttrNativeType, szValue ); + szValue[nAttrSize] = '\0'; + } + } + else { + if( nAttrElmts > 0 ) { + buf = (void *) CPLMalloc( nAttrElmts* + H5Tget_size( hAttrNativeType )); + szData = (char*) CPLMalloc( 8192 ); + szValue = (char*) CPLMalloc( MAX_METADATA_LEN ); + szData[0] = '\0'; + szValue[0] ='\0'; + H5Aread( hAttrID, hAttrNativeType, buf ); + } + if( H5Tequal( H5T_NATIVE_CHAR, hAttrNativeType ) + || H5Tequal( H5T_NATIVE_SCHAR, hAttrNativeType ) ) { + for( i=0; i < nAttrElmts; i++ ) { + sprintf( szData, "%c ", ((char *) buf)[i]); + if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= + MAX_METADATA_LEN ) + CPLError( CE_Warning, CPLE_OutOfMemory, + "Header data too long. Truncated\n"); + } + } + else if( H5Tequal( H5T_NATIVE_UCHAR, hAttrNativeType ) ) { + for( i=0; i < nAttrElmts; i++ ) { + sprintf( szData, "%c", ((char *) buf)[i] ); + if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= + MAX_METADATA_LEN ) + CPLError( CE_Warning, CPLE_OutOfMemory, + "Header data too long. Truncated\n"); + } + } + else if( H5Tequal( H5T_NATIVE_SHORT, hAttrNativeType ) ) { + for( i=0; i < nAttrElmts; i++ ) { + sprintf( szData, "%d ", ((short *) buf)[i] ); + if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= + MAX_METADATA_LEN ) + CPLError( CE_Warning, CPLE_OutOfMemory, + "Header data too long. Truncated\n"); + } + } + else if( H5Tequal( H5T_NATIVE_USHORT, hAttrNativeType ) ) { + for( i=0; i < nAttrElmts; i++ ) { + sprintf( szData, "%ud ", ((unsigned short *) buf)[i] ); + if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= + MAX_METADATA_LEN ) + CPLError( CE_Warning, CPLE_OutOfMemory, + "Header data too long. Truncated\n"); + } + } + else if( H5Tequal( H5T_NATIVE_INT, hAttrNativeType ) ) { + for( i=0; i < nAttrElmts; i++ ) { + sprintf( szData, "%d ", ((int *) buf)[i] ); + if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= + MAX_METADATA_LEN ) + CPLError( CE_Warning, CPLE_OutOfMemory, + "Header data too long. Truncated\n"); + } + } + else if( H5Tequal( H5T_NATIVE_UINT, hAttrNativeType ) ) { + for( i=0; i < nAttrElmts; i++ ) { + sprintf( szData, "%ud ", ((unsigned int *) buf)[i] ); + if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= + MAX_METADATA_LEN ) + CPLError( CE_Warning, CPLE_OutOfMemory, + "Header data too long. Truncated\n"); + } + } + else if( H5Tequal( H5T_NATIVE_LONG, hAttrNativeType ) ) { + for( i=0; i < nAttrElmts; i++ ) { + sprintf( szData, "%ld ", ((long *)buf)[i] ); + if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= + MAX_METADATA_LEN ) + CPLError( CE_Warning, CPLE_OutOfMemory, + "Header data too long. Truncated\n"); + } + } + else if( H5Tequal( H5T_NATIVE_ULONG, hAttrNativeType ) ) { + for( i=0; i < nAttrElmts; i++ ) { + sprintf( szData, "%ld ", ((unsigned long *)buf)[i] ); + if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= + MAX_METADATA_LEN ) + CPLError( CE_Warning, CPLE_OutOfMemory, + "Header data too long. Truncated\n"); + } + } + else if( H5Tequal( H5T_NATIVE_FLOAT, hAttrNativeType ) ) { + for( i=0; i < nAttrElmts; i++ ) { + CPLsprintf( szData, "%.8g ", ((float *)buf)[i] ); + if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= + MAX_METADATA_LEN ) + CPLError( CE_Warning, CPLE_OutOfMemory, + "Header data too long. Truncated\n"); + } + } + else if( H5Tequal( H5T_NATIVE_DOUBLE, hAttrNativeType ) ) { + for( i=0; i < nAttrElmts; i++ ) { + CPLsprintf( szData, "%.15g ", ((double *)buf)[i] ); + if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= + MAX_METADATA_LEN ) + CPLError( CE_Warning, CPLE_OutOfMemory, + "Header data too long. Truncated\n"); + } + } + CPLFree( buf ); + + } + H5Sclose(hAttrSpace); + H5Tclose(hAttrNativeType); + H5Tclose(hAttrTypeID); + H5Aclose( hAttrID ); + poDS->papszMetadata = CSLSetNameValue( poDS->papszMetadata, osKey, szValue); + + CPLFree( szData ); + CPLFree( szValue ); + + return 0; +} + +/************************************************************************/ +/* CreateMetadata() */ +/************************************************************************/ +CPLErr HDF5Dataset::CreateMetadata( HDF5GroupObjects *poH5Object, int nType) +{ + hid_t hGroupID; /* identifier of group */ + hid_t hDatasetID; + int nbAttrs; + + HDF5Dataset *poDS; + + if( !poH5Object->pszPath ) + return CE_None; + + poDS = this; + + poH5CurrentObject = poH5Object; + nbAttrs = poH5Object->nbAttrs; + + if( poH5Object->pszPath == NULL || EQUAL(poH5Object->pszPath, "" ) ) + return CE_None; + + switch( nType ) { + + case H5G_GROUP: + + if( nbAttrs > 0 ) { + hGroupID = H5Gopen( hHDF5, poH5Object->pszPath ); + H5Aiterate( hGroupID, NULL, HDF5AttrIterate, (void *)poDS ); + H5Gclose( hGroupID ); + } + + break; + + case H5G_DATASET: + + if( nbAttrs > 0 ) { + hDatasetID = H5Dopen(hHDF5, poH5Object->pszPath ); + H5Aiterate( hDatasetID, NULL, HDF5AttrIterate, (void *)poDS ); + H5Dclose( hDatasetID ); + } + break; + + default: + break; + } + + return CE_None; +} + + +/************************************************************************/ +/* HDF5FindDatasetObjectsbyPath() */ +/* Find object by name */ +/************************************************************************/ +HDF5GroupObjects* HDF5Dataset::HDF5FindDatasetObjectsbyPath + ( HDF5GroupObjects *poH5Objects, const char* pszDatasetPath ) +{ + unsigned i; + HDF5Dataset *poDS; + HDF5GroupObjects *poObjectsFound; + poDS=this; + + if( poH5Objects->nType == H5G_DATASET && + EQUAL( poH5Objects->pszUnderscorePath,pszDatasetPath ) ) { + + /* printf("found it! %ld\n",(long) poH5Objects);*/ + return( poH5Objects ); + } + + if( poH5Objects->nbObjs >0 ) + for( i=0; i nbObjs; i++ ) { + poObjectsFound= + poDS->HDF5FindDatasetObjectsbyPath( poH5Objects->poHchild+i, + pszDatasetPath ); +/* -------------------------------------------------------------------- */ +/* Is this our dataset?? */ +/* -------------------------------------------------------------------- */ + if( poObjectsFound != NULL ) return( poObjectsFound ); + } +/* -------------------------------------------------------------------- */ +/* Dataset has not been found! */ +/* -------------------------------------------------------------------- */ + return( NULL ); + +} + + +/************************************************************************/ +/* HDF5FindDatasetObjects() */ +/* Find object by name */ +/************************************************************************/ +HDF5GroupObjects* HDF5Dataset::HDF5FindDatasetObjects + ( HDF5GroupObjects *poH5Objects, const char* pszDatasetName ) +{ + unsigned i; + HDF5Dataset *poDS; + HDF5GroupObjects *poObjectsFound; + poDS=this; + + if( poH5Objects->nType == H5G_DATASET && + EQUAL( poH5Objects->pszName,pszDatasetName ) ) { + + /* printf("found it! %ld\n",(long) poH5Objects);*/ + return( poH5Objects ); + } + + if( poH5Objects->nbObjs >0 ) + for( i=0; i nbObjs; i++ ) { + poObjectsFound= + poDS->HDF5FindDatasetObjects( poH5Objects->poHchild+i, + pszDatasetName ); +/* -------------------------------------------------------------------- */ +/* Is this our dataset?? */ +/* -------------------------------------------------------------------- */ + if( poObjectsFound != NULL ) return( poObjectsFound ); + + } +/* -------------------------------------------------------------------- */ +/* Dataset has not been found! */ +/* -------------------------------------------------------------------- */ + return( NULL ); + +} + + +/************************************************************************/ +/* HDF5ListGroupObjects() */ +/* */ +/* List all objects in HDF5 */ +/************************************************************************/ +CPLErr HDF5Dataset::HDF5ListGroupObjects( HDF5GroupObjects *poRootGroup, + int bSUBDATASET ) +{ + char szTemp[8192]; + char szDim[8192]; + HDF5Dataset *poDS; + poDS=this; + + if( poRootGroup->nbObjs >0 ) + for( hsize_t i=0; i < poRootGroup->nbObjs; i++ ) { + poDS->HDF5ListGroupObjects( poRootGroup->poHchild+i, bSUBDATASET ); + } + + + if( poRootGroup->nType == H5G_GROUP ) { + CreateMetadata( poRootGroup, H5G_GROUP ); + } + +/* -------------------------------------------------------------------- */ +/* Create Sub dataset list */ +/* -------------------------------------------------------------------- */ + if( (poRootGroup->nType == H5G_DATASET ) && bSUBDATASET + && poDS->GetDataType( poRootGroup->native ) == GDT_Unknown ) + { + CPLDebug( "HDF5", "Skipping unsupported %s of type %s", + poRootGroup->pszUnderscorePath, + poDS->GetDataTypeName( poRootGroup->native ) ); + } + else if( (poRootGroup->nType == H5G_DATASET ) && bSUBDATASET ) + { + CreateMetadata( poRootGroup, H5G_DATASET ); + + szDim[0]='\0'; + switch( poRootGroup->nRank ) { + case 3: + sprintf( szTemp,"%dx%dx%d", + (int)poRootGroup->paDims[0], + (int)poRootGroup->paDims[1], + (int)poRootGroup->paDims[2] ); + break; + + case 2: + sprintf( szTemp,"%dx%d", + (int)poRootGroup->paDims[0], + (int)poRootGroup->paDims[1] ); + break; + + default: + return CE_None; + + } + strcat( szDim,szTemp ); + + sprintf( szTemp, "SUBDATASET_%d_NAME", ++(poDS->nSubDataCount) ); + + poDS->papszSubDatasets = + CSLSetNameValue( poDS->papszSubDatasets, szTemp, + CPLSPrintf( "HDF5:\"%s\":%s", + poDS->GetDescription(), + poRootGroup->pszUnderscorePath ) ); + + sprintf( szTemp, "SUBDATASET_%d_DESC", poDS->nSubDataCount ); + + poDS->papszSubDatasets = + CSLSetNameValue( poDS->papszSubDatasets, szTemp, + CPLSPrintf( "[%s] %s (%s)", + szDim, + poRootGroup->pszUnderscorePath, + poDS->GetDataTypeName + ( poRootGroup->native ) ) ); + + } + + return CE_None; +} + + +/************************************************************************/ +/* ReadGlobalAttributes() */ +/************************************************************************/ +CPLErr HDF5Dataset::ReadGlobalAttributes(int bSUBDATASET) +{ + + HDF5GroupObjects *poRootGroup; + + poRootGroup = (HDF5GroupObjects*) CPLCalloc(sizeof(HDF5GroupObjects), 1); + + poH5RootGroup=poRootGroup; + poRootGroup->pszName = CPLStrdup( "/" ); + poRootGroup->nType = H5G_GROUP; + poRootGroup->poHparent = NULL; + poRootGroup->pszPath = NULL; + poRootGroup->pszUnderscorePath = NULL; + + if( hHDF5 < 0 ) { + printf( "hHDF5 <0!!\n" ); + return CE_None; + } + + H5G_stat_t oStatbuf; + if( H5Gget_objinfo( hHDF5, "/", FALSE, &oStatbuf ) < 0 ) + return CE_Failure; + poRootGroup->objno[0] = oStatbuf.objno[0]; + poRootGroup->objno[1] = oStatbuf.objno[1]; + + if( hGroupID > 0 ) + H5Gclose( hGroupID ); + hGroupID = H5Gopen( hHDF5, "/" ); + if( hGroupID < 0 ){ + printf( "hGroupID <0!!\n" ); + return CE_None; + } + + poRootGroup->nbAttrs = H5Aget_num_attrs( hGroupID ); + + H5Gget_num_objs( hGroupID, &( poRootGroup->nbObjs ) ); + + if( poRootGroup->nbObjs > 0 ) { + poRootGroup->poHchild = ( HDF5GroupObjects * ) + CPLCalloc( poRootGroup->nbObjs, + sizeof( HDF5GroupObjects ) ); + H5Giterate( hGroupID, "/", NULL, + HDF5CreateGroupObjs, (void *)poRootGroup ); + } + else poRootGroup->poHchild = NULL; + + HDF5ListGroupObjects( poRootGroup, bSUBDATASET ); + return CE_None; +} + + +/** + * Reads an array of double attributes from the HDF5 metadata. + * It reads the attributes directly on it's binary form directly, + * thus avoiding string conversions. + * + * Important: It allocates the memory for the attributes internally, + * so the caller must free the returned array after using it. + * @param pszAttrName Name of the attribute to be read. + * the attribute name must be the form: + * root attribute name + * SUBDATASET/subdataset attribute name + * @param pdfValues pointer wich will store the array of doubles read. + * @param nLen it stores the length of the array read. If NULL it doesn't + * inform the lenght of the array. + * @return CPLErr CE_None in case of success, CE_Failure in case of failure + */ +CPLErr HDF5Dataset::HDF5ReadDoubleAttr(const char* pszAttrFullPath, + double **pdfValues,int *nLen) +{ + CPLErr retVal = CE_Failure; + hid_t hAttrID=-1; + hid_t hAttrTypeID=-1; + hid_t hAttrNativeType=-1; + hid_t hAttrSpace=-1; + hid_t hObjAttrID=-1; + + hsize_t nSize[64]; + unsigned int nAttrElmts; + hsize_t i; + unsigned int nAttrDims; + + size_t nSlashPos; + + CPLString osAttrFullPath(pszAttrFullPath); + CPLString osObjName; + CPLString osAttrName; + + //Search for the last "/" in order to get the + //Path to the attribute + nSlashPos = osAttrFullPath.find_last_of("/"); + + //If objects name have been found + if(nSlashPos != CPLString::npos ) + { + //Split Object name (dataset, group) + osObjName = osAttrFullPath.substr(0,nSlashPos); + //Split attribute name + osAttrName = osAttrFullPath.substr(nSlashPos+1); + } + else + { + //By default the group is root, and + //the attribute is the full path + osObjName = "/"; + osAttrName = pszAttrFullPath; + } + + hObjAttrID = H5Oopen( hHDF5, osObjName.c_str(),H5P_DEFAULT); + + if(hObjAttrID < 0) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Object %s could not be opened\n", pszAttrFullPath); + retVal = CE_Failure; + } + else + { + //Open attribute handler by name, from the object handler opened + //earlier + hAttrID = H5Aopen_name( hObjAttrID, osAttrName.c_str()); + + //Check for errors opening the attribute + if(hAttrID <0) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attribute %s could not be opened\n", pszAttrFullPath); + retVal = CE_Failure; + } + else + { + hAttrTypeID = H5Aget_type( hAttrID ); + hAttrNativeType = H5Tget_native_type( hAttrTypeID, H5T_DIR_DEFAULT ); + hAttrSpace = H5Aget_space( hAttrID ); + nAttrDims = H5Sget_simple_extent_dims( hAttrSpace, nSize, NULL ); + + if( !H5Tequal( H5T_NATIVE_DOUBLE, hAttrNativeType ) ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attribute %s is not of type double\n", pszAttrFullPath); + retVal = CE_Failure; + } + else + { + //Get the ammount of elements + nAttrElmts = 1; + for( i=0; i < nAttrDims; i++ ) + { + //For multidimensional attributes + nAttrElmts *= nSize[i]; + } + + if(nLen != NULL) + *nLen = nAttrElmts; + + (*pdfValues) = (double *) CPLMalloc(nAttrElmts*sizeof(double)); + + //Read the attribute contents + if(H5Aread( hAttrID, hAttrNativeType, *pdfValues )<0) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attribute %s could not be opened\n", + pszAttrFullPath); + retVal = CE_Failure; + } + else + { + retVal = CE_None; + } + } + + H5Tclose(hAttrNativeType); + H5Tclose(hAttrTypeID); + H5Sclose(hAttrSpace); + H5Aclose(hAttrID); + } + H5Oclose(hObjAttrID); + } + + return retVal; +} diff --git a/bazaar/plugin/gdal/frmts/hdf5/hdf5dataset.h b/bazaar/plugin/gdal/frmts/hdf5/hdf5dataset.h new file mode 100644 index 000000000..f73cd482e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf5/hdf5dataset.h @@ -0,0 +1,122 @@ +/****************************************************************************** + * $Id: hdf5dataset.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: Hierarchical Data Format Release 5 (HDF5) + * Purpose: Header file for HDF5 datasets reader. + * Author: Denis Nadeau (denis.nadeau@gmail.com) + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _HDF5DATASET_H_INCLUDED_ +#define _HDF5DATASET_H_INCLUDED_ + +#include "gdal_pam.h" +#include "cpl_list.h" + +typedef struct HDF5GroupObjects { + char *pszName; + char *pszPath; + char *pszUnderscorePath; + char *pszTemp; + int nType; + int nIndex; + hsize_t nbObjs; + int nbAttrs; + int nRank; + hsize_t *paDims; + hid_t native; + hid_t HDatatype; + unsigned long objno[2]; + struct HDF5GroupObjects *poHparent; + struct HDF5GroupObjects *poHchild; +} HDF5GroupObjects; + + +herr_t HDF5CreateGroupObjs(hid_t, const char *,void *); + +/************************************************************************/ +/* ==================================================================== */ +/* HDF5Dataset */ +/* ==================================================================== */ +/************************************************************************/ +class HDF5Dataset : public GDALPamDataset +{ + protected: + + hid_t hHDF5; + hid_t hGroupID; /* H handler interface */ + char **papszSubDatasets; + int bIsHDFEOS; + int nDatasetType; + int nSubDataCount; + + + HDF5GroupObjects *poH5RootGroup; /* Contain hdf5 Groups information */ + + CPLErr ReadGlobalAttributes(int); + CPLErr HDF5ListGroupObjects(HDF5GroupObjects *, int ); + CPLErr CreateMetadata( HDF5GroupObjects *, int ); + + HDF5GroupObjects* HDF5FindDatasetObjects( HDF5GroupObjects *, const char * ); + HDF5GroupObjects* HDF5FindDatasetObjectsbyPath( HDF5GroupObjects *, const char * ); + char* CreatePath(HDF5GroupObjects *); + void DestroyH5Objects(HDF5GroupObjects *); + + GDALDataType GetDataType(hid_t); + const char * GetDataTypeName(hid_t); + + /** + * Reads an array of double attributes from the HDF5 metadata. + * It reads the attributes directly on it's binary form directly, + * thus avoiding string conversions. + * + * Important: It allocates the memory for the attributes internally, + * so the caller must free the returned array after using it. + * @param pszAttrName Name of the attribute to be read. + * the attribute name must be the form: + * root attribute name + * SUBDATASET/subdataset attribute name + * @param pdfValues pointer wich will store the array of doubles read. + * @param nLen it stores the length of the array read. If NULL it doesn't inform + * the lenght of the array. + * @return CPLErr CE_None in case of success, CE_Failure in case of failure + */ + CPLErr HDF5ReadDoubleAttr(const char* pszAttrName,double **pdfValues,int *nLen=NULL); + + public: + + char **papszMetadata; + HDF5GroupObjects *poH5CurrentObject; + + HDF5Dataset(); + ~HDF5Dataset(); + + static GDALDataset *Open(GDALOpenInfo *); + static int Identify(GDALOpenInfo *); +}; + + + +#endif /* _HDF5DATASET_H_INCLUDED_ */ + diff --git a/bazaar/plugin/gdal/frmts/hdf5/hdf5imagedataset.cpp b/bazaar/plugin/gdal/frmts/hdf5/hdf5imagedataset.cpp new file mode 100644 index 000000000..257ad283e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf5/hdf5imagedataset.cpp @@ -0,0 +1,1260 @@ +/****************************************************************************** + * $Id: hdf5imagedataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: Hierarchical Data Format Release 5 (HDF5) + * Purpose: Read subdatasets of HDF5 file. + * Author: Denis Nadeau + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#define H5_USE_16_API + +#include "hdf5.h" + +#include "gdal_pam.h" +#include "gdal_priv.h" +#include "cpl_string.h" +#include "hdf5dataset.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: hdf5imagedataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +CPL_C_START +void GDALRegister_HDF5Image(void); +CPL_C_END + +/* release 1.6.3 or 1.6.4 changed the type of count in some api functions */ + +#if H5_VERS_MAJOR == 1 && H5_VERS_MINOR <= 6 \ + && (H5_VERS_MINOR < 6 || H5_VERS_RELEASE < 3) +# define H5OFFSET_TYPE hssize_t +#else +# define H5OFFSET_TYPE hsize_t +#endif + +class HDF5ImageDataset : public HDF5Dataset +{ + typedef enum + { + UNKNOWN_PRODUCT=0, + CSK_PRODUCT + } Hdf5ProductType; + + typedef enum + { + PROD_UNKNOWN=0, + PROD_CSK_L0, + PROD_CSK_L1A, + PROD_CSK_L1B, + PROD_CSK_L1C, + PROD_CSK_L1D + } HDF5CSKProductEnum; + + friend class HDF5ImageRasterBand; + + char *pszProjection; + char *pszGCPProjection; + GDAL_GCP *pasGCPList; + int nGCPCount; + OGRSpatialReference oSRS; + + hsize_t *dims,*maxdims; + HDF5GroupObjects *poH5Objects; + int ndims,dimensions; + hid_t dataset_id; + hid_t dataspace_id; + hsize_t size; + haddr_t address; + hid_t datatype; + hid_t native; + H5T_class_t clas; + Hdf5ProductType iSubdatasetType; + HDF5CSKProductEnum iCSKProductType; + double adfGeoTransform[6]; + bool bHasGeoTransform; + + CPLErr CreateODIMH5Projection(); + +public: + HDF5ImageDataset(); + ~HDF5ImageDataset(); + + CPLErr CreateProjections( ); + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + + const char *GetProjectionRef(); + virtual int GetGCPCount( ); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs( ); + virtual CPLErr GetGeoTransform( double * padfTransform ); + + Hdf5ProductType GetSubdatasetType() const {return iSubdatasetType;} + HDF5CSKProductEnum GetCSKProductType() const {return iCSKProductType;} + + int IsComplexCSKL1A() const + { + return (GetSubdatasetType() == CSK_PRODUCT) && + (GetCSKProductType() == PROD_CSK_L1A) && + (ndims == 3); + } + int GetYIndex() const { return IsComplexCSKL1A() ? 0 : ndims - 2; } + int GetXIndex() const { return IsComplexCSKL1A() ? 1 : ndims - 1; } + + /** + * Identify if the subdataset has a known product format + * It stores a product identifier in iSubdatasetType, + * UNKNOWN_PRODUCT, if it isn't a recognizable format. + */ + void IdentifyProductType(); + + /** + * Captures Geolocation information from a COSMO-SKYMED + * file. + * The geoid will allways be WGS84 + * The projection type may be UTM or UPS, depending on the + * latitude from the center of the image. + * @param iProductType type of HDF5 subproduct, see HDF5CSKProduct + */ + void CaptureCSKGeolocation(int iProductType); + + /** + * Get Geotransform information for COSMO-SKYMED files + * In case of sucess it stores the transformation + * in adfGeoTransform. In case of failure it doesn't + * modify adfGeoTransform + * @param iProductType type of HDF5 subproduct, see HDF5CSKProduct + */ + void CaptureCSKGeoTransform(int iProductType); + + /** + * @param iProductType type of HDF5 subproduct, see HDF5CSKProduct + */ + void CaptureCSKGCPs(int iProductType); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* HDF5ImageDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* HDF5ImageDataset() */ +/************************************************************************/ +HDF5ImageDataset::HDF5ImageDataset() +{ + nGCPCount = 0; + pszProjection = NULL; + pszGCPProjection= NULL; + pasGCPList = NULL; + poH5Objects = NULL; + poH5RootGroup = NULL; + dims = NULL; + maxdims = NULL; + papszMetadata = NULL; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + iSubdatasetType = UNKNOWN_PRODUCT; + iCSKProductType = PROD_UNKNOWN; + bHasGeoTransform = false; + dataset_id = -1; + dataspace_id = -1; + datatype = -1; + native = -1; +} + +/************************************************************************/ +/* ~HDF5ImageDataset() */ +/************************************************************************/ +HDF5ImageDataset::~HDF5ImageDataset( ) +{ + FlushCache(); + + if( dataset_id > 0 ) + H5Dclose(dataset_id); + if( dataspace_id > 0 ) + H5Sclose(dataspace_id); + if( datatype > 0 ) + H5Tclose(datatype); + if( native > 0 ) + H5Tclose(native); + + CPLFree(pszProjection); + CPLFree(pszGCPProjection); + + if( dims ) + CPLFree( dims ); + + if( maxdims ) + CPLFree( maxdims ); + + if( nGCPCount > 0 ) + { + for( int i = 0; i < nGCPCount; i++ ) + { + if( pasGCPList[i].pszId ) + CPLFree( pasGCPList[i].pszId ); + if( pasGCPList[i].pszInfo ) + CPLFree( pasGCPList[i].pszInfo ); + } + + CPLFree( pasGCPList ); + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* Hdf5imagerasterband */ +/* ==================================================================== */ +/************************************************************************/ +class HDF5ImageRasterBand : public GDALPamRasterBand +{ + friend class HDF5ImageDataset; + + int bNoDataSet; + double dfNoDataValue; + +public: + + HDF5ImageRasterBand( HDF5ImageDataset *, int, GDALDataType ); + ~HDF5ImageRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual double GetNoDataValue( int * ); + virtual CPLErr SetNoDataValue( double ); + /* virtual CPLErr IWriteBlock( int, int, void * ); */ +}; + +/************************************************************************/ +/* ~HDF5ImageRasterBand() */ +/************************************************************************/ + +HDF5ImageRasterBand::~HDF5ImageRasterBand() +{ + +} +/************************************************************************/ +/* HDF5ImageRasterBand() */ +/************************************************************************/ +HDF5ImageRasterBand::HDF5ImageRasterBand( HDF5ImageDataset *poDS, int nBand, + GDALDataType eType ) + +{ + char **papszMetaGlobal; + this->poDS = poDS; + this->nBand = nBand; + eDataType = eType; + bNoDataSet = FALSE; + dfNoDataValue = -9999; + nBlockXSize = poDS->GetRasterXSize( ); + nBlockYSize = 1; + +/* -------------------------------------------------------------------- */ +/* Take a copy of Global Metadata since I can't pass Raster */ +/* variable to Iterate function. */ +/* -------------------------------------------------------------------- */ + papszMetaGlobal = CSLDuplicate( poDS->papszMetadata ); + CSLDestroy( poDS->papszMetadata ); + poDS->papszMetadata = NULL; + + if( poDS->poH5Objects->nType == H5G_DATASET ) { + poDS->CreateMetadata( poDS->poH5Objects, H5G_DATASET ); + } + +/* -------------------------------------------------------------------- */ +/* Recover Global Metadat and set Band Metadata */ +/* -------------------------------------------------------------------- */ + + SetMetadata( poDS->papszMetadata ); + + CSLDestroy( poDS->papszMetadata ); + poDS->papszMetadata = CSLDuplicate( papszMetaGlobal ); + CSLDestroy( papszMetaGlobal ); + + /* check for chunksize and set it as the blocksize (optimizes read) */ + hid_t listid = H5Dget_create_plist(((HDF5ImageDataset * )poDS)->dataset_id); + if (listid>0) + { + if(H5Pget_layout(listid) == H5D_CHUNKED) + { + hsize_t panChunkDims[3]; + CPL_UNUSED int nDimSize = H5Pget_chunk(listid, 3, panChunkDims); + CPLAssert(nDimSize == poDS->ndims); + nBlockXSize = (int) panChunkDims[poDS->GetXIndex()]; + nBlockYSize = (int) panChunkDims[poDS->GetYIndex()]; + } + + H5Pclose(listid); + } + +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ +double HDF5ImageRasterBand::GetNoDataValue( int * pbSuccess ) + +{ + if( bNoDataSet ) + { + if( pbSuccess ) + *pbSuccess = bNoDataSet; + + return dfNoDataValue; + } + else + return GDALPamRasterBand::GetNoDataValue( pbSuccess ); +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ +CPLErr HDF5ImageRasterBand::SetNoDataValue( double dfNoData ) + +{ + bNoDataSet = TRUE; + dfNoDataValue = dfNoData; + + return CE_None; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ +CPLErr HDF5ImageRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + herr_t status; + hsize_t count[3]; + H5OFFSET_TYPE offset[3]; + int nSizeOfData; + hid_t memspace; + hsize_t col_dims[3]; + hsize_t rank; + + HDF5ImageDataset *poGDS = ( HDF5ImageDataset * ) poDS; + + if( poGDS->eAccess == GA_Update ) { + memset( pImage, 0, + nBlockXSize * nBlockYSize * + GDALGetDataTypeSize( eDataType )/8 ); + return CE_None; + } + + if( poGDS->IsComplexCSKL1A() ) + { + rank = 3; + offset[2] = nBand-1; + count[2] = 1; + col_dims[2] = 1; + } + else if( poGDS->ndims == 3 ) + { + rank=3; + offset[0] = nBand-1; + count[0] = 1; + col_dims[0] = 1; + } + else + rank = 2; + + offset[poGDS->GetYIndex()] = nBlockYOff*nBlockYSize; + offset[poGDS->GetXIndex()] = nBlockXOff*nBlockXSize; + count[poGDS->GetYIndex()] = nBlockYSize; + count[poGDS->GetXIndex()] = nBlockXSize; + + nSizeOfData = H5Tget_size( poGDS->native ); + memset( pImage,0,nBlockXSize*nBlockYSize*nSizeOfData ); + + /* blocksize may not be a multiple of imagesize */ + count[poGDS->GetYIndex()] = MIN( size_t(nBlockYSize), + poDS->GetRasterYSize() - + offset[poGDS->GetYIndex()]); + count[poGDS->GetXIndex()] = MIN( size_t(nBlockXSize), + poDS->GetRasterXSize()- + offset[poGDS->GetXIndex()]); + +/* -------------------------------------------------------------------- */ +/* Select block from file space */ +/* -------------------------------------------------------------------- */ + status = H5Sselect_hyperslab( poGDS->dataspace_id, + H5S_SELECT_SET, + offset, NULL, + count, NULL ); + +/* -------------------------------------------------------------------- */ +/* Create memory space to receive the data */ +/* -------------------------------------------------------------------- */ + col_dims[poGDS->GetYIndex()]=nBlockYSize; + col_dims[poGDS->GetXIndex()]=nBlockXSize; + + memspace = H5Screate_simple( (int) rank, col_dims, NULL ); + H5OFFSET_TYPE mem_offset[3] = {0, 0, 0}; + status = H5Sselect_hyperslab(memspace, + H5S_SELECT_SET, + mem_offset, NULL, + count, NULL); + + status = H5Dread ( poGDS->dataset_id, + poGDS->native, + memspace, + poGDS->dataspace_id, + H5P_DEFAULT, + pImage ); + + H5Sclose( memspace ); + + if( status < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "H5Dread() failed for block." ); + return CE_Failure; + } + else + return CE_None; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int HDF5ImageDataset::Identify( GDALOpenInfo *poOpenInfo ) + +{ + if(!EQUALN( poOpenInfo->pszFilename, "HDF5:", 5 ) ) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ +GDALDataset *HDF5ImageDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + int i; + HDF5ImageDataset *poDS; + char szFilename[2048]; + + if(!EQUALN( poOpenInfo->pszFilename, "HDF5:", 5 ) || + strlen(poOpenInfo->pszFilename) > sizeof(szFilename) - 3 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The HDF5ImageDataset driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + poDS = new HDF5ImageDataset(); + + /* -------------------------------------------------------------------- */ + /* Create a corresponding GDALDataset. */ + /* -------------------------------------------------------------------- */ + /* printf("poOpenInfo->pszFilename %s\n",poOpenInfo->pszFilename); */ + char **papszName = + CSLTokenizeString2( poOpenInfo->pszFilename, + ":", CSLT_HONOURSTRINGS|CSLT_PRESERVEESCAPES ); + + if( !((CSLCount(papszName) == 3) || (CSLCount(papszName) == 4)) ) + { + CSLDestroy(papszName); + delete poDS; + return NULL; + } + + poDS->SetDescription( poOpenInfo->pszFilename ); + + /* -------------------------------------------------------------------- */ + /* Check for drive name in windows HDF5:"D:\... */ + /* -------------------------------------------------------------------- */ + CPLString osSubdatasetName; + + strcpy(szFilename, papszName[1]); + + if( strlen(papszName[1]) == 1 && papszName[3] != NULL ) + { + strcat(szFilename, ":"); + strcat(szFilename, papszName[2]); + osSubdatasetName = papszName[3]; + } + else + osSubdatasetName = papszName[2]; + + poDS->SetSubdatasetName( osSubdatasetName ); + + CSLDestroy(papszName); + papszName = NULL; + + if( !H5Fis_hdf5(szFilename) ) { + delete poDS; + return NULL; + } + + poDS->SetPhysicalFilename( szFilename ); + + /* -------------------------------------------------------------------- */ + /* Try opening the dataset. */ + /* -------------------------------------------------------------------- */ + poDS->hHDF5 = H5Fopen(szFilename, + H5F_ACC_RDONLY, + H5P_DEFAULT ); + + if( poDS->hHDF5 < 0 ) + { + delete poDS; + return NULL; + } + + poDS->hGroupID = H5Gopen( poDS->hHDF5, "/" ); + if( poDS->hGroupID < 0 ) + { + poDS->bIsHDFEOS=false; + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* THIS IS AN HDF5 FILE */ +/* -------------------------------------------------------------------- */ + poDS->bIsHDFEOS=TRUE; + poDS->ReadGlobalAttributes( FALSE ); + +/* -------------------------------------------------------------------- */ +/* Create HDF5 Data Hierarchy in a link list */ +/* -------------------------------------------------------------------- */ + poDS->poH5Objects = + poDS->HDF5FindDatasetObjectsbyPath( poDS->poH5RootGroup, + osSubdatasetName ); + + if( poDS->poH5Objects == NULL ) { + delete poDS; + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Retrieve HDF5 data information */ +/* -------------------------------------------------------------------- */ + poDS->dataset_id = H5Dopen( poDS->hHDF5,poDS->poH5Objects->pszPath ); + poDS->dataspace_id = H5Dget_space( poDS->dataset_id ); + poDS->ndims = H5Sget_simple_extent_ndims( poDS->dataspace_id ); + if( poDS->ndims < 0 ) + { + delete poDS; + return NULL; + } + poDS->dims = (hsize_t*)CPLCalloc( poDS->ndims, sizeof(hsize_t) ); + poDS->maxdims = (hsize_t*)CPLCalloc( poDS->ndims, sizeof(hsize_t) ); + poDS->dimensions = H5Sget_simple_extent_dims( poDS->dataspace_id, + poDS->dims, + poDS->maxdims ); + poDS->datatype = H5Dget_type( poDS->dataset_id ); + poDS->clas = H5Tget_class( poDS->datatype ); + poDS->size = H5Tget_size( poDS->datatype ); + poDS->address = H5Dget_offset( poDS->dataset_id ); + poDS->native = H5Tget_native_type( poDS->datatype, H5T_DIR_ASCEND ); + + // CSK code in IdentifyProductType() and CreateProjections() + // uses dataset metadata. + poDS->SetMetadata( poDS->papszMetadata ); + + // Check if the hdf5 is a well known product type + poDS->IdentifyProductType(); + + poDS->nRasterYSize=(int)poDS->dims[poDS->GetYIndex()]; // nRows + poDS->nRasterXSize=(int)poDS->dims[poDS->GetXIndex()]; // nCols + if( poDS->IsComplexCSKL1A() ) + { + poDS->nBands=(int) poDS->dims[2]; // nBands + } + else if( poDS->ndims == 3 ) + { + poDS->nBands=(int) poDS->dims[0]; + } + else + { + poDS->nBands=1; + } + + for( i = 1; i <= poDS->nBands; i++ ) { + HDF5ImageRasterBand *poBand = + new HDF5ImageRasterBand( poDS, i, + poDS->GetDataType( poDS->native ) ); + + poDS->SetBand( i, poBand ); + if( poBand->bNoDataSet ) + poBand->SetNoDataValue( 255 ); + } + + poDS->CreateProjections( ); + +/* -------------------------------------------------------------------- */ +/* Setup/check for pam .aux.xml. */ +/* -------------------------------------------------------------------- */ + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Setup overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" ); + + return( poDS ); +} + + +/************************************************************************/ +/* GDALRegister_HDF5Image() */ +/************************************************************************/ +void GDALRegister_HDF5Image( ) + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("HDF5Image driver")) + return; + + if( GDALGetDriverByName( "HDF5Image" ) == NULL ) + { + poDriver = new GDALDriver( ); + + poDriver->SetDescription( "HDF5Image" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "HDF5 Dataset" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_hdf5.html" ); + poDriver->pfnOpen = HDF5ImageDataset::Open; + poDriver->pfnIdentify = HDF5ImageDataset::Identify; + + GetGDALDriverManager( )->RegisterDriver( poDriver ); + } +} + +/************************************************************************/ +/* CreateODIMH5Projection() */ +/************************************************************************/ + +/* Reference : http://www.knmi.nl/opera/opera3/OPERA_2008_03_WP2.1b_ODIM_H5_v2.1.pdf */ +/* 4.3.2 where for geographically referenced image Groups */ +/* We don't use the where_xscale and where_yscale parameters, but recompute them */ +/* from the lower-left and upper-right coordinates. There's some difference. */ +/* As all those parameters are linked together, I'm not sure which one should be */ +/* considered as the reference */ + +CPLErr HDF5ImageDataset::CreateODIMH5Projection() +{ + const char* pszProj4String = GetMetadataItem("where_projdef"); + const char* pszLL_lon = GetMetadataItem("where_LL_lon"); + const char* pszLL_lat = GetMetadataItem("where_LL_lat"); + const char* pszUR_lon = GetMetadataItem("where_UR_lon"); + const char* pszUR_lat = GetMetadataItem("where_UR_lat"); + if( pszProj4String == NULL || + pszLL_lon == NULL || pszLL_lat == NULL || + pszUR_lon == NULL || pszUR_lat == NULL ) + return CE_Failure; + + if( oSRS.importFromProj4( pszProj4String ) != OGRERR_NONE ) + return CE_Failure; + + OGRSpatialReference oSRSWGS84; + oSRSWGS84.SetWellKnownGeogCS( "WGS84" ); + + OGRCoordinateTransformation* poCT = + OGRCreateCoordinateTransformation( &oSRSWGS84, &oSRS ); + if( poCT == NULL ) + return CE_Failure; + + /* Reproject corners from long,lat WGS84 to the target SRS */ + double dfLLX = CPLAtof(pszLL_lon); + double dfLLY = CPLAtof(pszLL_lat); + double dfURX = CPLAtof(pszUR_lon); + double dfURY = CPLAtof(pszUR_lat); + if( !poCT->Transform(1, &dfLLX, &dfLLY) || + !poCT->Transform(1, &dfURX, &dfURY) ) + { + delete poCT; + return CE_Failure; + } + delete poCT; + + /* Compute the geotransform now */ + double dfPixelX = (dfURX - dfLLX) / nRasterXSize; + double dfPixelY = (dfURY - dfLLY) / nRasterYSize; + + bHasGeoTransform = TRUE; + adfGeoTransform[0] = dfLLX; + adfGeoTransform[1] = dfPixelX; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = dfURY; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = -dfPixelY; + + CPLFree( pszProjection ); + oSRS.exportToWkt( &pszProjection ); + + return CE_None; +} + +/************************************************************************/ +/* CreateProjections() */ +/************************************************************************/ +CPLErr HDF5ImageDataset::CreateProjections() +{ + switch(iSubdatasetType) + { + case CSK_PRODUCT: + { + const char *osMissionLevel = NULL; + int productType = PROD_UNKNOWN; + + if(GetMetadataItem("Product_Type")!=NULL) + { + //Get the format's level + osMissionLevel = HDF5Dataset::GetMetadataItem("Product_Type"); + + if(EQUALN(osMissionLevel,"RAW",3)) + productType = PROD_CSK_L0; + + if(EQUALN(osMissionLevel,"SSC",3)) + productType = PROD_CSK_L1A; + + if(EQUALN(osMissionLevel,"DGM",3)) + productType = PROD_CSK_L1B; + + if(EQUALN(osMissionLevel,"GEC",3)) + productType = PROD_CSK_L1C; + + if(EQUALN(osMissionLevel,"GTC",3)) + productType = PROD_CSK_L1D; + } + + CaptureCSKGeoTransform(productType); + CaptureCSKGeolocation(productType); + CaptureCSKGCPs(productType); + + break; + } + case UNKNOWN_PRODUCT: + { +#define NBGCPLAT 100 +#define NBGCPLON 30 + + hid_t LatitudeDatasetID = -1; + hid_t LongitudeDatasetID = -1; + // hid_t LatitudeDataspaceID; + // hid_t LongitudeDataspaceID; + float* Latitude; + float* Longitude; + int i,j; + int nDeltaLat; + int nDeltaLon; + + nDeltaLat = nRasterYSize / NBGCPLAT; + nDeltaLon = nRasterXSize / NBGCPLON; + + if( nDeltaLat == 0 || nDeltaLon == 0 ) + return CE_None; + + /* -------------------------------------------------------------------- */ + /* Create HDF5 Data Hierarchy in a link list */ + /* -------------------------------------------------------------------- */ + poH5Objects=HDF5FindDatasetObjects( poH5RootGroup, "Latitude" ); + if( !poH5Objects ) { + if( GetMetadataItem("where_projdef") != NULL ) + return CreateODIMH5Projection(); + return CE_None; + } + /* -------------------------------------------------------------------- */ + /* The Lattitude and Longitude arrays must have a rank of 2 to */ + /* retrieve GCPs. */ + /* -------------------------------------------------------------------- */ + if( poH5Objects->nRank != 2 ) { + return CE_None; + } + + /* -------------------------------------------------------------------- */ + /* Retrieve HDF5 data information */ + /* -------------------------------------------------------------------- */ + LatitudeDatasetID = H5Dopen( hHDF5,poH5Objects->pszPath ); + // LatitudeDataspaceID = H5Dget_space( dataset_id ); + + poH5Objects=HDF5FindDatasetObjects( poH5RootGroup, "Longitude" ); + LongitudeDatasetID = H5Dopen( hHDF5,poH5Objects->pszPath ); + // LongitudeDataspaceID = H5Dget_space( dataset_id ); + + if( ( LatitudeDatasetID > 0 ) && ( LongitudeDatasetID > 0) ) { + + Latitude = ( float * ) CPLCalloc( nRasterYSize*nRasterXSize, + sizeof( float ) ); + Longitude = ( float * ) CPLCalloc( nRasterYSize*nRasterXSize, + sizeof( float ) ); + memset( Latitude, 0, nRasterXSize*nRasterYSize*sizeof( float ) ); + memset( Longitude, 0, nRasterXSize*nRasterYSize*sizeof( float ) ); + + H5Dread ( LatitudeDatasetID, + H5T_NATIVE_FLOAT, + H5S_ALL, + H5S_ALL, + H5P_DEFAULT, + Latitude ); + + H5Dread ( LongitudeDatasetID, + H5T_NATIVE_FLOAT, + H5S_ALL, + H5S_ALL, + H5P_DEFAULT, + Longitude ); + + oSRS.SetWellKnownGeogCS( "WGS84" ); + CPLFree(pszProjection); + CPLFree(pszGCPProjection); + oSRS.exportToWkt( &pszProjection ); + oSRS.exportToWkt( &pszGCPProjection ); + + /* -------------------------------------------------------------------- */ + /* Fill the GCPs list. */ + /* -------------------------------------------------------------------- */ + nGCPCount = nRasterYSize/nDeltaLat * nRasterXSize/nDeltaLon; + + pasGCPList = ( GDAL_GCP * ) + CPLCalloc( nGCPCount, sizeof( GDAL_GCP ) ); + + GDALInitGCPs( nGCPCount, pasGCPList ); + int k=0; + + int nYLimit = ((int)nRasterYSize/nDeltaLat) * nDeltaLat; + int nXLimit = ((int)nRasterXSize/nDeltaLon) * nDeltaLon; + for( j = 0; j < nYLimit; j+=nDeltaLat ) + { + for( i = 0; i < nXLimit; i+=nDeltaLon ) + { + int iGCP = j * nRasterXSize + i; + pasGCPList[k].dfGCPX = ( double ) Longitude[iGCP]+180.0; + pasGCPList[k].dfGCPY = ( double ) Latitude[iGCP]; + + pasGCPList[k].dfGCPPixel = i + 0.5; + pasGCPList[k++].dfGCPLine = j + 0.5; + + } + } + + CPLFree( Latitude ); + CPLFree( Longitude ); + } + + if( LatitudeDatasetID > 0 ) + H5Dclose(LatitudeDatasetID); + if( LongitudeDatasetID > 0 ) + H5Dclose(LongitudeDatasetID); + + break; + } + } + return CE_None; + +} +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *HDF5ImageDataset::GetProjectionRef( ) + +{ + if( pszProjection ) + return pszProjection; + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int HDF5ImageDataset::GetGCPCount( ) + +{ + if( nGCPCount > 0 ) + return nGCPCount; + else + return GDALPamDataset::GetGCPCount(); +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *HDF5ImageDataset::GetGCPProjection( ) + +{ + if( nGCPCount > 0 ) + return pszGCPProjection; + else + return GDALPamDataset::GetGCPProjection(); +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *HDF5ImageDataset::GetGCPs( ) +{ + if( nGCPCount > 0 ) + return pasGCPList; + else + return GDALPamDataset::GetGCPs(); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr HDF5ImageDataset::GetGeoTransform( double * padfTransform ) +{ + if ( bHasGeoTransform ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; + } + else + return GDALPamDataset::GetGeoTransform(padfTransform); +} + +/************************************************************************/ +/* IdentifyProductType() */ +/************************************************************************/ + +/** + * Identify if the subdataset has a known product format + * It stores a product identifier in iSubdatasetType, + * UNKNOWN_PRODUCT, if it isn't a recognizable format. + */ +void HDF5ImageDataset::IdentifyProductType() +{ + iSubdatasetType = UNKNOWN_PRODUCT; + +/************************************************************************/ +/* COSMO-SKYMED */ +/************************************************************************/ + const char *pszMissionId; + + //Get the Mission Id as a char *, because the + //field may not exist + pszMissionId = HDF5Dataset::GetMetadataItem("Mission_ID"); + + //If there is a Mission_ID field + if(pszMissionId != NULL && strstr(GetDescription(), "QLK") == NULL) + { + //Check if the mission type is CSK + if(EQUAL(pszMissionId,"CSK")) + { + iSubdatasetType = CSK_PRODUCT; + + const char *osMissionLevel = NULL; + + if(GetMetadataItem("Product_Type")!=NULL) + { + //Get the format's level + osMissionLevel = HDF5Dataset::GetMetadataItem("Product_Type"); + + if(EQUALN(osMissionLevel,"RAW",3)) + iCSKProductType = PROD_CSK_L0; + + if(EQUALN(osMissionLevel,"SCS",3)) + iCSKProductType = PROD_CSK_L1A; + + if(EQUALN(osMissionLevel,"DGM",3)) + iCSKProductType = PROD_CSK_L1B; + + if(EQUALN(osMissionLevel,"GEC",3)) + iCSKProductType = PROD_CSK_L1C; + + if(EQUALN(osMissionLevel,"GTC",3)) + iCSKProductType = PROD_CSK_L1D; + } + } + } +} + +/************************************************************************/ +/* CaptureCSKGeolocation() */ +/************************************************************************/ + +/** + * Captures Geolocation information from a COSMO-SKYMED + * file. + * The geoid will allways be WGS84 + * The projection type may be UTM or UPS, depending on the + * latitude from the center of the image. + * @param iProductType type of CSK subproduct, see HDF5CSKProduct + */ +void HDF5ImageDataset::CaptureCSKGeolocation(int iProductType) +{ + double *dfProjFalseEastNorth; + double *dfProjScaleFactor; + double *dfCenterCoord; + + //Set the ellipsoid to WGS84 + oSRS.SetWellKnownGeogCS( "WGS84" ); + + if(iProductType == PROD_CSK_L1C||iProductType == PROD_CSK_L1D) + { + //Check if all the metadata attributes are present + if(HDF5ReadDoubleAttr("Map Projection False East-North", &dfProjFalseEastNorth) == CE_Failure|| + HDF5ReadDoubleAttr("Map Projection Scale Factor", &dfProjScaleFactor) == CE_Failure|| + HDF5ReadDoubleAttr("Map Projection Centre", &dfCenterCoord) == CE_Failure|| + GetMetadataItem("Projection_ID") == NULL) + { + + pszProjection = CPLStrdup(""); + pszGCPProjection = CPLStrdup(""); + CPLError( CE_Failure, CPLE_OpenFailed, + "The CSK hdf5 file geolocation information is malformed\n" ); + } + else + { + //Fetch projection Type + CPLString osProjectionID = GetMetadataItem("Projection_ID"); + + //If the projection is UTM + if(EQUAL(osProjectionID,"UTM")) + { + // @TODO: use SetUTM + oSRS.SetProjCS(SRS_PT_TRANSVERSE_MERCATOR); + oSRS.SetTM(dfCenterCoord[0], + dfCenterCoord[1], + dfProjScaleFactor[0], + dfProjFalseEastNorth[0], + dfProjFalseEastNorth[1]); + } + else + { + //TODO Test! I didn't had any UPS projected files to test! + //If the projection is UPS + if(EQUAL(osProjectionID,"UPS")) + { + oSRS.SetProjCS(SRS_PT_POLAR_STEREOGRAPHIC); + oSRS.SetPS(dfCenterCoord[0], + dfCenterCoord[1], + dfProjScaleFactor[0], + dfProjFalseEastNorth[0], + dfProjFalseEastNorth[1]); + } + } + + //Export Projection to Wkt. + //In case of error then clean the projection + if (oSRS.exportToWkt(&pszProjection) != OGRERR_NONE) + pszProjection = CPLStrdup(""); + + CPLFree(dfCenterCoord); + CPLFree(dfProjScaleFactor); + CPLFree(dfProjFalseEastNorth); + } + } + else + { + //Export GCPProjection to Wkt. + //In case of error then clean the projection + if(oSRS.exportToWkt(&pszGCPProjection) != OGRERR_NONE) + pszGCPProjection = CPLStrdup(""); + } +} + +/************************************************************************/ +/* CaptureCSKGeoTransform() */ +/************************************************************************/ + +/** +* Get Geotransform information for COSMO-SKYMED files +* In case of sucess it stores the transformation +* in adfGeoTransform. In case of failure it doesn't +* modify adfGeoTransform +* @param iProductType type of CSK subproduct, see HDF5CSKProduct +*/ +void HDF5ImageDataset::CaptureCSKGeoTransform(int iProductType) +{ + double *pdOutUL; + double *pdLineSpacing; + double *pdColumnSpacing; + + CPLString osULCoord; + CPLString osULPath; + CPLString osLineSpacingPath, osColumnSpacingPath; + + const char *pszSubdatasetName = GetSubdatasetName(); + + bHasGeoTransform = FALSE; + //If the product level is not L1C or L1D then + //it doesn't have a valid projection + if(iProductType == PROD_CSK_L1C||iProductType == PROD_CSK_L1D) + { + //If there is a subdataset + if(pszSubdatasetName != NULL) + { + + osULPath = pszSubdatasetName ; + osULPath += "/Top Left East-North"; + + osLineSpacingPath = pszSubdatasetName; + osLineSpacingPath += "/Line Spacing"; + + osColumnSpacingPath = pszSubdatasetName; + osColumnSpacingPath += "/Column Spacing"; + + + //If it could find the attributes on the metadata + if(HDF5ReadDoubleAttr(osULPath.c_str(), &pdOutUL) == CE_Failure || + HDF5ReadDoubleAttr(osLineSpacingPath.c_str(), &pdLineSpacing) == CE_Failure || + HDF5ReadDoubleAttr(osColumnSpacingPath.c_str(), &pdColumnSpacing) == CE_Failure) + { + bHasGeoTransform = FALSE; + } + else + { +// geotransform[1] : width of pixel +// geotransform[4] : rotational coefficient, zero for north up images. +// geotransform[2] : rotational coefficient, zero for north up images. +// geotransform[5] : height of pixel (but negative) + + adfGeoTransform[0] = pdOutUL[0]; + adfGeoTransform[1] = pdLineSpacing[0]; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = pdOutUL[1]; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = -pdColumnSpacing[0]; + + + CPLFree(pdOutUL); + CPLFree(pdLineSpacing); + CPLFree(pdColumnSpacing); + + bHasGeoTransform = TRUE; + } + } + } +} + + +/************************************************************************/ +/* CaptureCSKGCPs() */ +/************************************************************************/ + +/** + * Retrieves and stores the GCPs from a COSMO-SKYMED dataset + * It only retrieves the GCPs for L0, L1A and L1B products + * for L1C and L1D products, geotransform is provided. + * The GCPs provided will be the Image's corners. + * @param iProductType type of CSK product @see HDF5CSKProductEnum + */ +void HDF5ImageDataset::CaptureCSKGCPs(int iProductType) +{ + //Only retrieve GCPs for L0,L1A and L1B products + if(iProductType == PROD_CSK_L0||iProductType == PROD_CSK_L1A|| + iProductType == PROD_CSK_L1B) + { + int i; + double *pdCornerCoordinates; + + nGCPCount=4; + pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),4); + CPLString osCornerName[4]; + double pdCornerPixel[4]; + double pdCornerLine[4]; + + const char *pszSubdatasetName = GetSubdatasetName(); + + //Load the subdataset name first + for(i=0;i <4;i++) + osCornerName[i] = pszSubdatasetName; + + //Load the attribute name, and raster coordinates for + //all the corners + osCornerName[0] += "/Top Left Geodetic Coordinates"; + pdCornerPixel[0] = 0; + pdCornerLine[0] = 0; + + osCornerName[1] += "/Top Right Geodetic Coordinates"; + pdCornerPixel[1] = GetRasterXSize(); + pdCornerLine[1] = 0; + + osCornerName[2] += "/Bottom Left Geodetic Coordinates"; + pdCornerPixel[2] = 0; + pdCornerLine[2] = GetRasterYSize(); + + osCornerName[3] += "/Bottom Right Geodetic Coordinates"; + pdCornerPixel[3] = GetRasterXSize(); + pdCornerLine[3] = GetRasterYSize(); + + //For all the image's corners + for(i=0;i<4;i++) + { + GDALInitGCPs( 1, pasGCPList + i ); + + CPLFree( pasGCPList[i].pszId ); + pasGCPList[i].pszId = NULL; + + //Retrieve the attributes + if(HDF5ReadDoubleAttr(osCornerName[i].c_str(), + &pdCornerCoordinates) == CE_Failure) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Error retrieving CSK GCPs\n" ); + // Free on failure, e.g. in case of QLK subdataset. + for( int i = 0; i < 4; i++ ) + { + if( pasGCPList[i].pszId ) + CPLFree( pasGCPList[i].pszId ); + if( pasGCPList[i].pszInfo ) + CPLFree( pasGCPList[i].pszInfo ); + } + CPLFree( pasGCPList ); + pasGCPList = NULL; + nGCPCount = 0; + break; + } + + //Fill the GCPs name + pasGCPList[i].pszId = CPLStrdup( osCornerName[i].c_str() ); + + //Fill the coordinates + pasGCPList[i].dfGCPX = pdCornerCoordinates[1]; + pasGCPList[i].dfGCPY = pdCornerCoordinates[0]; + pasGCPList[i].dfGCPZ = pdCornerCoordinates[2]; + pasGCPList[i].dfGCPPixel = pdCornerPixel[i]; + pasGCPList[i].dfGCPLine = pdCornerLine[i]; + + //Free the returned coordinates + CPLFree(pdCornerCoordinates); + } + } +} diff --git a/bazaar/plugin/gdal/frmts/hdf5/iso19115_srs.cpp b/bazaar/plugin/gdal/frmts/hdf5/iso19115_srs.cpp new file mode 100644 index 000000000..78dd88a52 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf5/iso19115_srs.cpp @@ -0,0 +1,148 @@ +/****************************************************************************** + * $Id: iso19115_srs.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: BAG Driver + * Purpose: Implements code to parse ISO 19115 metadata to extract a + * spatial reference system. Eventually intended to be made + * a method on OGRSpatialReference. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "cpl_minixml.h" +#include "cpl_error.h" + +CPL_CVSID("$Id: iso19115_srs.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* OGR_SRS_ImportFromISO19115() */ +/************************************************************************/ + +OGRErr OGR_SRS_ImportFromISO19115( OGRSpatialReference *poThis, + const char *pszISOXML ) + +{ +/* -------------------------------------------------------------------- */ +/* Parse the XML into tree form. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRoot = CPLParseXMLString( pszISOXML ); + + if( psRoot == NULL ) + return OGRERR_FAILURE; + + CPLStripXMLNamespace( psRoot, NULL, TRUE ); + + +/* -------------------------------------------------------------------- */ +/* For now we look for projection codes recognised in the BAG */ +/* format (see ons_fsd.pdf: Metadata Dataset Character String */ +/* Constants). */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRSI = CPLSearchXMLNode( psRoot, "=referenceSystemInfo" ); + if( psRSI == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find in metadata." ); + CPLDestroyXMLNode( psRoot ); + return OGRERR_FAILURE; + } + + poThis->Clear(); + +/* -------------------------------------------------------------------- */ +/* First, set the datum. */ +/* -------------------------------------------------------------------- */ + const char *pszDatum = + CPLGetXMLValue( psRSI, "MD_CRS.datum.RS_Identifier.code", "" ); + + if( strlen(pszDatum) > 0 + && poThis->SetWellKnownGeogCS( pszDatum ) != OGRERR_NONE ) + { + CPLDestroyXMLNode( psRoot ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Then try to extract the projection. */ +/* -------------------------------------------------------------------- */ + const char *pszProjection = + CPLGetXMLValue( psRSI, "MD_CRS.projection.RS_Identifier.code", "" ); + + if( EQUAL(pszProjection,"UTM") ) + { + int nZone = atoi(CPLGetXMLValue( psRSI, "MD_CRS.projectionParameters.MD_ProjectionParameters.zone", "0" )); + + /* + ** We have encountered files (#5152) that identify the southern + ** hemisphere with a false northing of 10000000 value. The existing + ** code checked for negative zones but it isn't clear if any actual + ** files use that. + */ + int bNorth = nZone > 0; + if( bNorth ) + { + const char *pszFalseNorthing = CPLGetXMLValue( psRSI, "MD_CRS.projectionParameters.MD_ProjectionParameters.falseNorthing", "" ); + if ( strlen(pszFalseNorthing) > 0 ) + { + if( EQUAL(pszFalseNorthing, "10000000")) + { + bNorth = FALSE; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, "falseNorthing value not recognized: %s", pszFalseNorthing); + } + } + } + poThis->SetUTM( ABS(nZone), bNorth ); + } + else if( EQUAL(pszProjection,"Geodetic") ) + { + const char *pszEllipsoid = + CPLGetXMLValue( psRSI, "MD_CRS.ellipsoid.RS_Identifier.code", "" ); + + if( !EQUAL(pszDatum, "WGS84") || + !EQUAL(pszEllipsoid, "WGS84") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "ISO 19115 parser does not support custom GCS." ); + CPLDestroyXMLNode( psRoot ); + return OGRERR_FAILURE; + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "projection = %s not recognised by ISO 19115 parser.", + pszProjection ); + CPLDestroyXMLNode( psRoot ); + return OGRERR_FAILURE; + } + + CPLDestroyXMLNode( psRoot ); + + return OGRERR_NONE; +} + diff --git a/bazaar/plugin/gdal/frmts/hdf5/makefile.vc b/bazaar/plugin/gdal/frmts/hdf5/makefile.vc new file mode 100644 index 000000000..e2e695d4d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hdf5/makefile.vc @@ -0,0 +1,36 @@ +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +OBJ = hdf5dataset.obj hdf5imagedataset.obj \ + bagdataset.obj gh5_convenience.obj iso19115_srs.obj + +PLUGIN_DLL = gdal_HDF5.dll + +EXTRAFLAGS = -I$(HDF5_DIR)\include -DWIN32 -D_HDF5USEDLL_ + +!IF "$(HDF5_PLUGIN)" == "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DHDF5_PLUGIN +!ENDIF + + +default: $(OBJ) + $(INSTALL) *.obj ..\o + +clean: + -del *.obj + -del *.dll + -del *.exp + -del *.lib + -del *.manifest + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(GDALLIB) $(HDF5_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) + diff --git a/bazaar/plugin/gdal/frmts/hf2/GNUmakefile b/bazaar/plugin/gdal/frmts/hf2/GNUmakefile new file mode 100644 index 000000000..bc4d2b1b2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hf2/GNUmakefile @@ -0,0 +1,13 @@ + +OBJ = hf2dataset.o + +include ../../GDALmake.opt + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/hf2/frmt_hf2.html b/bazaar/plugin/gdal/frmts/hf2/frmt_hf2.html new file mode 100644 index 000000000..7743cc74b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hf2/frmt_hf2.html @@ -0,0 +1,37 @@ + + +HF2 -- HF2/HFZ heightfield raster + + + + +

    HF2 -- HF2/HFZ heightfield raster

    + +(Available for GDAL >= 1.8.0) +

    +GDAL supports reading and writing HF2/HFZ/HF2.GZ heightfield raster datasets. +

    +HF2 is a heightfield format that records difference between consecutive cell values. HF2 files can also optionally be compressed by the gzip algorithm, and so the HF2.GZ files (or HFZ, equivalently) may be significantly smaller than the uncompressed data. The file format enables the user to have control on the desired accuracy through the vertical precision parameter. +

    +GDAL can read and write georeferencing information through extended header blocks. +

    + +

    Creation options

    + +
      +
    • COMPRESS=YES/NO : whether the file must be compressed with GZip or no. Defaults to NO
    • +
    • BLOCKSIZE=block_size : internal tile size. Must be >= 8. Defaults to 256
    • +
    • VERTICAL_PRECISION=vertical_precision : Must be > 0. Defaults to 0.01
    • +
    + +Increasing the vertical precision decreases the file size, especially with COMPRESS=YES, but at the loss of accuracy. + +

    See also

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/hf2/hf2dataset.cpp b/bazaar/plugin/gdal/frmts/hf2/hf2dataset.cpp new file mode 100644 index 000000000..6d2783420 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hf2/hf2dataset.cpp @@ -0,0 +1,1106 @@ +/****************************************************************************** + * $Id: hf2dataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: HF2 driver + * Purpose: GDALDataset driver for HF2/HFZ dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_string.h" +#include "gdal_pam.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: hf2dataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_HF2(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* HF2Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +class HF2RasterBand; + +class HF2Dataset : public GDALPamDataset +{ + friend class HF2RasterBand; + + VSILFILE *fp; + double adfGeoTransform[6]; + char *pszWKT; + vsi_l_offset *panBlockOffset; + + int nTileSize; + int bHasLoaderBlockMap; + int LoadBlockMap(); + + public: + HF2Dataset(); + virtual ~HF2Dataset(); + + virtual CPLErr GetGeoTransform( double * ); + virtual const char* GetProjectionRef(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset *CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* HF2RasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class HF2RasterBand : public GDALPamRasterBand +{ + friend class HF2Dataset; + + float* pafBlockData; + int nLastBlockYOff; + + public: + + HF2RasterBand( HF2Dataset *, int, GDALDataType ); + ~HF2RasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + + +/************************************************************************/ +/* HF2RasterBand() */ +/************************************************************************/ + +HF2RasterBand::HF2RasterBand( HF2Dataset *poDS, int nBand, GDALDataType eDT ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = eDT; + + nBlockXSize = poDS->nTileSize; + nBlockYSize = 1; + + pafBlockData = NULL; + nLastBlockYOff = -1; +} + +/************************************************************************/ +/* ~HF2RasterBand() */ +/************************************************************************/ + +HF2RasterBand::~HF2RasterBand() +{ + CPLFree(pafBlockData); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr HF2RasterBand::IReadBlock( int nBlockXOff, int nLineYOff, + void * pImage ) + +{ + HF2Dataset *poGDS = (HF2Dataset *) poDS; + + int nXBlocks = (nRasterXSize + nBlockXSize - 1) / nBlockXSize; + int nYBlocks = (nRasterYSize + nBlockXSize - 1) / nBlockXSize; + + if (!poGDS->LoadBlockMap()) + return CE_Failure; + + if (pafBlockData == NULL) + { + pafBlockData = (float*)VSIMalloc3(nXBlocks * sizeof(float), poGDS->nTileSize, poGDS->nTileSize); + if (pafBlockData == NULL) + return CE_Failure; + } + + nLineYOff = nRasterYSize - 1 - nLineYOff; + + int nBlockYOff = nLineYOff / nBlockXSize; + int nYOffInTile = nLineYOff % nBlockXSize; + + if (nBlockYOff != nLastBlockYOff) + { + nLastBlockYOff = nBlockYOff; + + memset(pafBlockData, 0, nXBlocks * sizeof(float) * nBlockXSize * nBlockXSize); + + /* 4 * nBlockXSize is the upper bound */ + void* pabyData = CPLMalloc( 4 * nBlockXSize ); + + int nxoff; + for(nxoff = 0; nxoff < nXBlocks; nxoff++) + { + VSIFSeekL(poGDS->fp, poGDS->panBlockOffset[(nYBlocks - 1 - nBlockYOff) * nXBlocks + nxoff], SEEK_SET); + float fScale, fOff; + VSIFReadL(&fScale, 4, 1, poGDS->fp); + VSIFReadL(&fOff, 4, 1, poGDS->fp); + CPL_LSBPTR32(&fScale); + CPL_LSBPTR32(&fOff); + + int nTileWidth = MIN(nBlockXSize, nRasterXSize - nxoff * nBlockXSize); + int nTileHeight = MIN(nBlockXSize, nRasterYSize - nBlockYOff * nBlockXSize); + + int j; + for(j=0;jfp); + if (nWordSize != 1 && nWordSize != 2 && nWordSize != 4) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unexpected word size : %d", (int)nWordSize); + break; + } + + GInt32 nVal; + VSIFReadL(&nVal, 4, 1, poGDS->fp); + CPL_LSBPTR32(&nVal); + VSIFReadL(pabyData, nWordSize * (nTileWidth - 1), 1, poGDS->fp); +#if defined(CPL_MSB) + if (nWordSize > 1) + GDALSwapWords(pabyData, nWordSize, nTileWidth - 1, nWordSize); +#endif + + pafBlockData[nxoff * nBlockXSize * nBlockXSize + j * nBlockXSize + 0] = nVal * fScale + fOff; + int i; + for(i=1;ipszFilename); + if ((EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "hfz") || + (strlen(poOpenInfo->pszFilename) > 6 && + EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6, "hf2.gz"))) && + !EQUALN(poOpenInfo->pszFilename, "/vsigzip/", 9)) + { + osFilename = "/vsigzip/"; + osFilename += poOpenInfo->pszFilename; + poOpenInfo = poOpenInfoToDelete = + new GDALOpenInfo(osFilename.c_str(), GA_ReadOnly, + poOpenInfo->GetSiblingFiles()); + } + + if (poOpenInfo->nHeaderBytes < 28) + { + delete poOpenInfoToDelete; + return FALSE; + } + + if (memcmp(poOpenInfo->pabyHeader, "HF2\0\0\0\0", 6) != 0) + { + delete poOpenInfoToDelete; + return FALSE; + } + + delete poOpenInfoToDelete; + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *HF2Dataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + CPLString osOriginalFilename(poOpenInfo->pszFilename); + + if (!Identify(poOpenInfo)) + return NULL; + + GDALOpenInfo* poOpenInfoToDelete = NULL; + /* GZipped .hf2 files are common, so automagically open them */ + /* if the /vsigzip/ has not been explicitly passed */ + CPLString osFilename(poOpenInfo->pszFilename); + if ((EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "hfz") || + (strlen(poOpenInfo->pszFilename) > 6 && + EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6, "hf2.gz"))) && + !EQUALN(poOpenInfo->pszFilename, "/vsigzip/", 9)) + { + osFilename = "/vsigzip/"; + osFilename += poOpenInfo->pszFilename; + poOpenInfo = poOpenInfoToDelete = + new GDALOpenInfo(osFilename.c_str(), GA_ReadOnly, + poOpenInfo->GetSiblingFiles()); + } + +/* -------------------------------------------------------------------- */ +/* Parse header */ +/* -------------------------------------------------------------------- */ + + int nXSize, nYSize; + memcpy(&nXSize, poOpenInfo->pabyHeader + 6, 4); + CPL_LSBPTR32(&nXSize); + memcpy(&nYSize, poOpenInfo->pabyHeader + 10, 4); + CPL_LSBPTR32(&nYSize); + + GUInt16 nTileSize; + memcpy(&nTileSize, poOpenInfo->pabyHeader + 14, 2); + CPL_LSBPTR16(&nTileSize); + + float fVertPres, fHorizScale; + memcpy(&fVertPres, poOpenInfo->pabyHeader + 16, 4); + CPL_LSBPTR32(&fVertPres); + memcpy(&fHorizScale, poOpenInfo->pabyHeader + 20, 4); + CPL_LSBPTR32(&fHorizScale); + + GUInt32 nExtendedHeaderLen; + memcpy(&nExtendedHeaderLen, poOpenInfo->pabyHeader + 24, 4); + CPL_LSBPTR32(&nExtendedHeaderLen); + + delete poOpenInfoToDelete; + poOpenInfoToDelete = NULL; + + if (nTileSize < 8) + return NULL; + if (nXSize <= 0 || nXSize > INT_MAX - nTileSize || + nYSize <= 0 || nYSize > INT_MAX - nTileSize) + return NULL; + /* To avoid later potential int overflows */ + if (nExtendedHeaderLen > 1024 * 65536) + return NULL; + + if (!GDALCheckDatasetDimensions(nXSize, nYSize)) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Parse extended blocks */ +/* -------------------------------------------------------------------- */ + + VSILFILE* fp = VSIFOpenL(osFilename.c_str(), "rb"); + if (fp == NULL) + return NULL; + + VSIFSeekL(fp, 28, SEEK_SET); + + int bHasExtent = FALSE; + double dfMinX = 0, dfMaxX = 0, dfMinY = 0, dfMaxY = 0; + int bHasUTMZone = FALSE; + GInt16 nUTMZone = 0; + int bHasEPSGDatumCode = FALSE; + GInt16 nEPSGDatumCode = 0; + int bHasEPSGCode = FALSE; + GInt16 nEPSGCode = 0; + int bHasRelativePrecision = FALSE; + float fRelativePrecision = 0; + char szApplicationName[256]; + szApplicationName[0] = 0; + + GUInt32 nExtendedHeaderOff = 0; + while(nExtendedHeaderOff < nExtendedHeaderLen) + { + char pabyBlockHeader[24]; + VSIFReadL(pabyBlockHeader, 24, 1, fp); + + char szBlockName[16 + 1]; + memcpy(szBlockName, pabyBlockHeader + 4, 16); + szBlockName[16] = 0; + GUInt32 nBlockSize; + memcpy(&nBlockSize, pabyBlockHeader + 20, 4); + CPL_LSBPTR32(&nBlockSize); + if (nBlockSize > 65536) + break; + + nExtendedHeaderOff += 24 + nBlockSize; + + if (strcmp(szBlockName, "georef-extents") == 0 && + nBlockSize == 34) + { + char pabyBlockData[34]; + VSIFReadL(pabyBlockData, 34, 1, fp); + + memcpy(&dfMinX, pabyBlockData + 2, 8); + CPL_LSBPTR64(&dfMinX); + memcpy(&dfMaxX, pabyBlockData + 2 + 8, 8); + CPL_LSBPTR64(&dfMaxX); + memcpy(&dfMinY, pabyBlockData + 2 + 8 + 8, 8); + CPL_LSBPTR64(&dfMinY); + memcpy(&dfMaxY, pabyBlockData + 2 + 8 + 8 + 8, 8); + CPL_LSBPTR64(&dfMaxY); + + bHasExtent = TRUE; + } + else if (strcmp(szBlockName, "georef-utm") == 0 && + nBlockSize == 2) + { + VSIFReadL(&nUTMZone, 2, 1, fp); + CPL_LSBPTR16(&nUTMZone); + CPLDebug("HF2", "UTM Zone = %d", nUTMZone); + + bHasUTMZone = TRUE; + } + else if (strcmp(szBlockName, "georef-datum") == 0 && + nBlockSize == 2) + { + VSIFReadL(&nEPSGDatumCode, 2, 1, fp); + CPL_LSBPTR16(&nEPSGDatumCode); + CPLDebug("HF2", "EPSG Datum Code = %d", nEPSGDatumCode); + + bHasEPSGDatumCode = TRUE; + } + else if (strcmp(szBlockName, "georef-epsg-prj") == 0 && + nBlockSize == 2) + { + VSIFReadL(&nEPSGCode, 2, 1, fp); + CPL_LSBPTR16(&nEPSGCode); + CPLDebug("HF2", "EPSG Code = %d", nEPSGCode); + + bHasEPSGCode = TRUE; + } + else if (strcmp(szBlockName, "precis-rel") == 0 && + nBlockSize == 4) + { + VSIFReadL(&fRelativePrecision, 4, 1, fp); + CPL_LSBPTR32(&fRelativePrecision); + + bHasRelativePrecision = TRUE; + } + else if (strcmp(szBlockName, "app-name") == 0 && + nBlockSize < 256) + { + VSIFReadL(szApplicationName, nBlockSize, 1, fp); + szApplicationName[nBlockSize] = 0; + } + else + { + CPLDebug("HF2", "Skipping block %s", szBlockName); + VSIFSeekL(fp, nBlockSize, SEEK_CUR); + } + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + HF2Dataset *poDS; + + poDS = new HF2Dataset(); + poDS->fp = fp; + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->nTileSize = nTileSize; + CPLDebug("HF2", "nXSize = %d, nYSize = %d, nTileSize = %d", nXSize, nYSize, nTileSize); + if (bHasExtent) + { + poDS->adfGeoTransform[0] = dfMinX; + poDS->adfGeoTransform[3] = dfMaxY; + poDS->adfGeoTransform[1] = (dfMaxX - dfMinX) / nXSize; + poDS->adfGeoTransform[5] = -(dfMaxY - dfMinY) / nYSize; + } + else + { + poDS->adfGeoTransform[1] = fHorizScale; + poDS->adfGeoTransform[5] = fHorizScale; + } + + if (bHasEPSGCode) + { + OGRSpatialReference oSRS; + if (oSRS.importFromEPSG(nEPSGCode) == OGRERR_NONE) + oSRS.exportToWkt(&poDS->pszWKT); + } + else + { + int bHasSRS = FALSE; + OGRSpatialReference oSRS; + oSRS.SetGeogCS("unknown", "unknown", "unknown", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING); + if (bHasEPSGDatumCode) + { + if (nEPSGDatumCode == 23 || nEPSGDatumCode == 6326) + { + bHasSRS = TRUE; + oSRS.SetWellKnownGeogCS("WGS84"); + } + else if (nEPSGDatumCode >= 6000) + { + char szName[32]; + sprintf( szName, "EPSG:%d", nEPSGDatumCode-2000 ); + oSRS.SetWellKnownGeogCS( szName ); + bHasSRS = TRUE; + } + } + + if (bHasUTMZone && ABS(nUTMZone) >= 1 && ABS(nUTMZone) <= 60) + { + bHasSRS = TRUE; + oSRS.SetUTM(ABS(nUTMZone), nUTMZone > 0); + } + if (bHasSRS) + oSRS.exportToWkt(&poDS->pszWKT); + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = 1; + int i; + for( i = 0; i < poDS->nBands; i++ ) + { + poDS->SetBand( i+1, new HF2RasterBand( poDS, i+1, GDT_Float32 ) ); + poDS->GetRasterBand(i+1)->SetUnitType("m"); + } + + if (szApplicationName[0] != '\0') + poDS->SetMetadataItem("APPLICATION_NAME", szApplicationName); + poDS->SetMetadataItem("VERTICAL_PRECISION", CPLString().Printf("%f", fVertPres)); + if (bHasRelativePrecision) + poDS->SetMetadataItem("RELATIVE_VERTICAL_PRECISION", CPLString().Printf("%f", fRelativePrecision)); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( osOriginalFilename.c_str() ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, osOriginalFilename.c_str() ); + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr HF2Dataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double)); + + return( CE_None ); +} + + +static void WriteShort(VSILFILE* fp, GInt16 val) +{ + CPL_LSBPTR16(&val); + VSIFWriteL(&val, 2, 1, fp); +} + +static void WriteInt(VSILFILE* fp, GInt32 val) +{ + CPL_LSBPTR32(&val); + VSIFWriteL(&val, 4, 1, fp); +} + +static void WriteFloat(VSILFILE* fp, float val) +{ + CPL_LSBPTR32(&val); + VSIFWriteL(&val, 4, 1, fp); +} + +static void WriteDouble(VSILFILE* fp, double val) +{ + CPL_LSBPTR64(&val); + VSIFWriteL(&val, 8, 1, fp); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset* HF2Dataset::CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "HF2 driver does not support source dataset with zero band.\n"); + return NULL; + } + + if (nBands != 1) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "HF2 driver only uses the first band of the dataset.\n"); + if (bStrict) + return NULL; + } + + if( pfnProgress && !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get source dataset info */ +/* -------------------------------------------------------------------- */ + + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + double adfGeoTransform[6]; + poSrcDS->GetGeoTransform(adfGeoTransform); + int bHasGeoTransform = !(adfGeoTransform[0] == 0 && + adfGeoTransform[1] == 1 && + adfGeoTransform[2] == 0 && + adfGeoTransform[3] == 0 && + adfGeoTransform[4] == 0 && + adfGeoTransform[5] == 1); + if (adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "HF2 driver does not support CreateCopy() from skewed or rotated dataset.\n"); + return NULL; + } + + GDALDataType eSrcDT = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + GDALDataType eReqDT; + float fVertPres = (float) 0.01; + if (eSrcDT == GDT_Byte || eSrcDT == GDT_Int16) + { + fVertPres = 1; + eReqDT = GDT_Int16; + } + else + eReqDT = GDT_Float32; + +/* -------------------------------------------------------------------- */ +/* Read creation options */ +/* -------------------------------------------------------------------- */ + const char* pszCompressed = CSLFetchNameValue(papszOptions, "COMPRESS"); + int bCompress = FALSE; + if (pszCompressed) + bCompress = CSLTestBoolean(pszCompressed); + + const char* pszVerticalPrecision = CSLFetchNameValue(papszOptions, "VERTICAL_PRECISION"); + if (pszVerticalPrecision) + { + fVertPres = (float) CPLAtofM(pszVerticalPrecision); + if (fVertPres <= 0) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Unsupported value for VERTICAL_PRECISION. Defaulting to 0.01"); + fVertPres = (float) 0.01; + } + if (eReqDT == GDT_Int16 && fVertPres > 1) + eReqDT = GDT_Float32; + } + + const char* pszBlockSize = CSLFetchNameValue(papszOptions, "BLOCKSIZE"); + int nTileSize = 256; + if (pszBlockSize) + { + nTileSize = atoi(pszBlockSize); + if (nTileSize < 8 || nTileSize > 4096) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Unsupported value for BLOCKSIZE. Defaulting to 256"); + nTileSize = 256; + } + } + +/* -------------------------------------------------------------------- */ +/* Parse source dataset georeferencing info */ +/* -------------------------------------------------------------------- */ + + int nExtendedHeaderLen = 0; + if (bHasGeoTransform) + nExtendedHeaderLen += 58; + const char* pszProjectionRef = poSrcDS->GetProjectionRef(); + int nDatumCode = -2; + int nUTMZone = 0; + int bNorth = FALSE; + int nEPSGCode = 0; + int nExtentUnits = 1; + if (pszProjectionRef != NULL && pszProjectionRef[0] != '\0') + { + OGRSpatialReference oSRS; + char* pszTemp = (char*) pszProjectionRef; + if (oSRS.importFromWkt(&pszTemp) == OGRERR_NONE) + { + const char* pszValue = NULL; + if( oSRS.GetAuthorityName( "GEOGCS|DATUM" ) != NULL + && EQUAL(oSRS.GetAuthorityName( "GEOGCS|DATUM" ),"EPSG") ) + nDatumCode = atoi(oSRS.GetAuthorityCode( "GEOGCS|DATUM" )); + else if ((pszValue = oSRS.GetAttrValue("GEOGCS|DATUM")) != NULL) + { + if (strstr(pszValue, "WGS") && strstr(pszValue, "84")) + nDatumCode = 6326; + } + + nUTMZone = oSRS.GetUTMZone(&bNorth); + } + if( oSRS.GetAuthorityName( "PROJCS" ) != NULL + && EQUAL(oSRS.GetAuthorityName( "PROJCS" ),"EPSG") ) + nEPSGCode = atoi(oSRS.GetAuthorityCode( "PROJCS" )); + + if( oSRS.IsGeographic() ) + { + nExtentUnits = 0; + } + else + { + double dfLinear = oSRS.GetLinearUnits(); + + if( ABS(dfLinear - 0.3048) < 0.0000001 ) + nExtentUnits = 2; + else if( ABS(dfLinear - CPLAtof(SRS_UL_US_FOOT_CONV)) < 0.00000001 ) + nExtentUnits = 3; + else + nExtentUnits = 1; + } + } + if (nDatumCode != -2) + nExtendedHeaderLen += 26; + if (nUTMZone != 0) + nExtendedHeaderLen += 26; + if (nEPSGCode) + nExtendedHeaderLen += 26; + +/* -------------------------------------------------------------------- */ +/* Create target file */ +/* -------------------------------------------------------------------- */ + + CPLString osFilename; + if (bCompress) + { + osFilename = "/vsigzip/"; + osFilename += pszFilename; + } + else + osFilename = pszFilename; + VSILFILE* fp = VSIFOpenL(osFilename.c_str(), "wb"); + if (fp == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot create %s", pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write header */ +/* -------------------------------------------------------------------- */ + + VSIFWriteL("HF2\0", 4, 1, fp); + WriteShort(fp, 0); + WriteInt(fp, nXSize); + WriteInt(fp, nYSize); + WriteShort(fp, (GInt16) nTileSize); + WriteFloat(fp, fVertPres); + float fHorizScale = (float) ((fabs(adfGeoTransform[1]) + fabs(adfGeoTransform[5])) / 2); + WriteFloat(fp, fHorizScale); + WriteInt(fp, nExtendedHeaderLen); + +/* -------------------------------------------------------------------- */ +/* Write extended header */ +/* -------------------------------------------------------------------- */ + + char szBlockName[16 + 1]; + if (bHasGeoTransform) + { + VSIFWriteL("bin\0", 4, 1, fp); + memset(szBlockName, 0, 16 + 1); + strcpy(szBlockName, "georef-extents"); + VSIFWriteL(szBlockName, 16, 1, fp); + WriteInt(fp, 34); + WriteShort(fp, (GInt16) nExtentUnits); + WriteDouble(fp, adfGeoTransform[0]); + WriteDouble(fp, adfGeoTransform[0] + nXSize * adfGeoTransform[1]); + WriteDouble(fp, adfGeoTransform[3] + nYSize * adfGeoTransform[5]); + WriteDouble(fp, adfGeoTransform[3]); + } + if (nUTMZone != 0) + { + VSIFWriteL("bin\0", 4, 1, fp); + memset(szBlockName, 0, 16 + 1); + strcpy(szBlockName, "georef-utm"); + VSIFWriteL(szBlockName, 16, 1, fp); + WriteInt(fp, 2); + WriteShort(fp, (GInt16) ((bNorth) ? nUTMZone : -nUTMZone)); + } + if (nDatumCode != -2) + { + VSIFWriteL("bin\0", 4, 1, fp); + memset(szBlockName, 0, 16 + 1); + strcpy(szBlockName, "georef-datum"); + VSIFWriteL(szBlockName, 16, 1, fp); + WriteInt(fp, 2); + WriteShort(fp, (GInt16) nDatumCode); + } + if (nEPSGCode != 0) + { + VSIFWriteL("bin\0", 4, 1, fp); + memset(szBlockName, 0, 16 + 1); + strcpy(szBlockName, "georef-epsg-prj"); + VSIFWriteL(szBlockName, 16, 1, fp); + WriteInt(fp, 2); + WriteShort(fp, (GInt16) nEPSGCode); + } + +/* -------------------------------------------------------------------- */ +/* Copy imagery */ +/* -------------------------------------------------------------------- */ + int nXBlocks = (nXSize + nTileSize - 1) / nTileSize; + int nYBlocks = (nYSize + nTileSize - 1) / nTileSize; + + void* pTileBuffer = (void*) VSIMalloc(nTileSize * nTileSize * (GDALGetDataTypeSize(eReqDT) / 8)); + if (pTileBuffer == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + VSIFCloseL(fp); + return NULL; + } + + int i, j, k, l; + CPLErr eErr = CE_None; + for(j=0;jGetRasterBand(1)->RasterIO(GF_Read, + i * nTileSize, MAX(0, nYSize - (j + 1) * nTileSize), + nReqXSize, nReqYSize, + pTileBuffer, nReqXSize, nReqYSize, + eReqDT, 0, 0, NULL); + if (eErr != CE_None) + break; + + if (eReqDT == GDT_Int16) + { + WriteFloat(fp, 1); /* scale */ + WriteFloat(fp, 0); /* offset */ + for(k=0;k 32767) + { + nWordSize = 4; + break; + } + if (nDiff < -128 || nDiff > 127) + nWordSize = 2; + nLastVal = nVal; + } + + VSIFWriteL(&nWordSize, 1, 1, fp); + nLastVal = ((short*)pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0]; + WriteInt(fp, nLastVal); + for(l=1;l= -128 && nDiff <= 127); + char chDiff = (char)nDiff; + VSIFWriteL(&chDiff, 1, 1, fp); + } + else if (nWordSize == 2) + { + CPLAssert(nDiff >= -32768 && nDiff <= 32767); + WriteShort(fp, (short)nDiff); + } + else + { + WriteInt(fp, nDiff); + } + nLastVal = nVal; + } + } + } + else + { + float fMinVal = ((float*)pTileBuffer)[0]; + float fMaxVal = fMinVal; + for(k=1;k fMaxVal) fMaxVal = fVal; + } + + float fIntRange = (fMaxVal - fMinVal) / fVertPres; + float fScale = (fMinVal == fMaxVal) ? 1 : (fMaxVal - fMinVal) / fIntRange; + float fOffset = fMinVal; + WriteFloat(fp, fScale); /* scale */ + WriteFloat(fp, fOffset); /* offset */ + for(k=0;k= -2147483648.0f && fIntLastVal <= 2147483647.0f); + int nLastVal = (int)fIntLastVal; + GByte nWordSize = 1; + for(l=1;l= -2147483648.0f && fIntVal <= 2147483647.0f); + int nVal = (int)fIntVal; + int nDiff = nVal - nLastVal; + CPLAssert((int)((GIntBig)nVal - nLastVal) == nDiff); + if (nDiff < -32768 || nDiff > 32767) + { + nWordSize = 4; + break; + } + if (nDiff < -128 || nDiff > 127) + nWordSize = 2; + nLastVal = nVal; + } + + VSIFWriteL(&nWordSize, 1, 1, fp); + fLastVal = ((float*)pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0]; + fIntLastVal = (fLastVal - fOffset) / fScale; + nLastVal = (int)fIntLastVal; + WriteInt(fp, nLastVal); + for(l=1;l= -128 && nDiff <= 127); + char chDiff = (char)nDiff; + VSIFWriteL(&chDiff, 1, 1, fp); + } + else if (nWordSize == 2) + { + CPLAssert(nDiff >= -32768 && nDiff <= 32767); + WriteShort(fp, (short)nDiff); + } + else + { + WriteInt(fp, nDiff); + } + nLastVal = nVal; + } + } + } + + if( pfnProgress && !pfnProgress( (j * nXBlocks + i + 1) * 1.0 / (nXBlocks * nYBlocks), NULL, pProgressData ) ) + { + eErr = CE_Failure; + break; + } + } + } + + CPLFree(pTileBuffer); + + VSIFCloseL(fp); + + if (eErr != CE_None) + return NULL; + + return (GDALDataset*) GDALOpen(osFilename.c_str(), GA_ReadOnly); +} + +/************************************************************************/ +/* GDALRegister_HF2() */ +/************************************************************************/ + +void GDALRegister_HF2() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "HF2" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "HF2" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "HF2/HFZ heightfield raster" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_hf2.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "hf2" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = HF2Dataset::Open; + poDriver->pfnIdentify = HF2Dataset::Identify; + poDriver->pfnCreateCopy = HF2Dataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/hf2/makefile.vc b/bazaar/plugin/gdal/frmts/hf2/makefile.vc new file mode 100644 index 000000000..6d59406ed --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hf2/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = hf2dataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/hfa/GNUmakefile b/bazaar/plugin/gdal/frmts/hfa/GNUmakefile new file mode 100644 index 000000000..27d6deebc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/GNUmakefile @@ -0,0 +1,24 @@ + + +include ../../GDALmake.opt + +HFAOBJ = hfaopen.o hfaentry.o hfadictionary.o hfafield.o hfatype.o \ + hfaband.o hfacompress.o +OBJ = $(HFAOBJ) hfadataset.o hfa_overviews.o + +ALL_C_FLAGS = $(CFLAGS) + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o hfatest $(O_OBJ) + +$(O_OBJ): hfa.h hfa_p.h + +hfatest: hfatest.$(OBJ_EXT) $(HFAOBJ:.o=.$(OBJ_EXT)) + $(LD) hfatest.$(OBJ_EXT) $(HFAOBJ:.o=.$(OBJ_EXT)) ../../port/*.$(OBJ_EXT) $(LIBS) -o hfatest + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + diff --git a/bazaar/plugin/gdal/frmts/hfa/TODO_Projections.txt b/bazaar/plugin/gdal/frmts/hfa/TODO_Projections.txt new file mode 100644 index 000000000..710905190 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/TODO_Projections.txt @@ -0,0 +1,17 @@ + +EPRJ_LAMBERT_CONFORMAL_CONIC: +EPRJ_TRANSVERSE_MERCATOR: + 1.6-esri has special handling for HARN / Wisconsin! + +EPRJ_HOTINE_OBLIQUE_MERCATOR: + What should we do if proParams[12] *not* > 0.0? + +EPRJ_KROVAK: + still issues around axis orientation - esri uses special parameters to + represent this. hfadataset.cpp is ignoring these parameters from .img. + +"Anchored LSR" (53) + - was EPRJ_LOCAL in 1.6-esri. + + + diff --git a/bazaar/plugin/gdal/frmts/hfa/frmt_hfa.html b/bazaar/plugin/gdal/frmts/hfa/frmt_hfa.html new file mode 100644 index 000000000..0e28b46cb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/frmt_hfa.html @@ -0,0 +1,90 @@ + + +HFA -- Erdas Imagine .img + + + + +

    HFA -- Erdas Imagine .img

    + +GDAL supports Erdas Imagine .img format for read access and write. The +driver supports reading overviews, palettes, and georeferencing. It +supports the erdas band types +u8, s8, u16, s16, u32, s32, f32, f64, c64 and c128.

    + +Compressed and missing tiles in Erdas files should be handled properly on read. +Files between 2GiB and 4GiB in size should work on Windows NT, and may work +on some Unix platforms. Files with external spill files (needed for datasets +larger than 2GiB) are also support for reading and writing.

    + +Metadata reading and writing is supported at the dataset level, and for +bands, but this is GDAL specific metadata - not metadata in an Imagine +recognised form. The metadata is stored in a table called GDAL_MetaData with +each column being a metadata item. The title is the key and the row 1 value +is the value.

    + +

    Creation Issues

    + +Erdas Imagine files can be created with any GDAL defined band type, including +the complex types. Created files may have any number of bands. Pseudo-Color +tables will be written if using the GDALDriver::CreateCopy() methodology. +Most projections should be supported though translation of unusual datums +(other than WGS84, WGS72, NAD83, and NAD27) may be problematic.

    + +Creation Options:

    + +

      +
    • BLOCKSIZE=blocksize: Tile width/height (32-2048). Default=64

      +

    • USE_SPILL=YES: Force the generation of a spill file +(by default spill file created for images larger 2GiB only). Default=NO

      +

    • COMPRESSED=YES: Create file as compressed. Use of spill file disables compression. Default=NO

      +

    • NBITS=1/2/4: Create file with special sub-byte data types.

      +

    • PIXELTYPE=[DEFAULT/SIGNEDBYTE]: By setting this to SIGNEDBYTE, a +new Byte file can be forced to be written as signed byte.

      +

    • AUX=YES: To create a .aux file. Default=NO

      +

    • IGNOREUTM=YES : Ignore UTM when selecting coordinate system - will use Transverse Mercator. Only used for Create() method. Default=NO

      +

    • STATISTICS=YES : To generate statistics and a histogram. Default=NO

      +

    • DEPENDENT_FILE=filename : Name of dependent file (must not have absolute path). Optional

      +

    • FORCETOPESTRING=YES: Force use of ArcGIS PE String in file instead of Imagine coordinate system format. In some cases this improves ArcGIS coordinate system compatability.

      +

    + +Erdas Imagine supports external creation of overviews (with gdaladdo for +instance). To force them to be created in an .rrd file (rather than inside +the original .img) set the global config option HFA_USE_RRD=YES).

    + +Layer names can be set and retrieved with the GDALSetDescription/GDALGetDescription calls on the Raster Band objects.

    + +

    Configuration Options

    +Currently two runtime configuration options are supported by the HFA driver: + +
      +
    • HFA_USE_RRD=YES/NO : Whether to force creation of external overviews in Erdas rrd format and with .rrd file name extension (gdaladdo with combination -ro --config USE_RRD YES creates overview file with .aux extension).

      +

    • HFA_COMPRESS_OVR=YES/NO : (GDAL >= 1.11) Whether to create compressed overviews. +Default is to only create compressed overviews when the file is compressed.

      +This configuration option can be used when building external overviews for a base image that is not in Erdas Imagine format. Resulting overview file will use the rrd structure and have .aux extension. +

      +gdaladdo out.tif --config USE_RRD YES --config HFA_COMPRESS_OVR YES 2 4 8
      +
      +Erdas Imagine and older ArcGIS versions may recognize overviews for some image formats only if they have .rrd extension. In this case use: +
      +gdaladdo out.tif --config USE_RRD YES --config HFA_USE_RRD YES --config HFA_COMPRESS_OVR YES 2 4 8
      +
      + +
    • +
    + +See Also:

    + +

    + + + diff --git a/bazaar/plugin/gdal/frmts/hfa/hfa.h b/bazaar/plugin/gdal/frmts/hfa/hfa.h new file mode 100644 index 000000000..a4fe34e53 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/hfa.h @@ -0,0 +1,333 @@ +/****************************************************************************** + * $Id: hfa.h 21687 2011-02-12 03:59:15Z warmerdam $ + * + * Project: Erdas Imagine (.img) Translator + * Purpose: Public (C callable) interface for the Erdas Imagine reading + * code. This include files, and it's implementing code depends + * on CPL, but not GDAL. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Intergraph Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _HFAOPEN_H_INCLUDED +#define _HFAOPEN_H_INCLUDED + +/* -------------------------------------------------------------------- */ +/* Include standard portability stuff. */ +/* -------------------------------------------------------------------- */ +#include "cpl_conv.h" +#include "cpl_string.h" + +#ifdef HFA_PRIVATE +typedef HFAInfo_t *HFAHandle; +#else +typedef void *HFAHandle; +#endif + +/* -------------------------------------------------------------------- */ +/* Structure definitions from eprj.h, with some type */ +/* simplifications. */ +/* -------------------------------------------------------------------- */ +typedef struct { + double x; /* coordinate x-value */ + double y; /* coordinate y-value */ +} Eprj_Coordinate; + + +typedef struct { + double width; /* pixelsize width */ + double height; /* pixelsize height */ +} Eprj_Size; + + +typedef struct { + char * proName; /* projection name */ + Eprj_Coordinate upperLeftCenter; /* map coordinates of center of + upper left pixel */ + Eprj_Coordinate lowerRightCenter; /* map coordinates of center of + lower right pixel */ + Eprj_Size pixelSize; /* pixel size in map units */ + char * units; /* units of the map */ +} Eprj_MapInfo; + +typedef enum { + EPRJ_INTERNAL, /* Indicates that the projection is built into + the eprj package as function calls */ + EPRJ_EXTERNAL /* Indicates that the projection is accessible + as an EXTERNal executable */ +} Eprj_ProType; + +typedef enum { + EPRJ_NAD27=1, /* Use the North America Datum 1927 */ + EPRJ_NAD83=2, /* Use the North America Datum 1983 */ + EPRJ_HARN /* Use the North America Datum High Accuracy + Reference Network */ +} Eprj_NAD; + +typedef enum { + EPRJ_DATUM_PARAMETRIC, /* The datum info is 7 doubles */ + EPRJ_DATUM_GRID, /* The datum info is a name */ + EPRJ_DATUM_REGRESSION, + EPRJ_DATUM_NONE +} Eprj_DatumType; + +typedef struct { + char *datumname; /* name of the datum */ + Eprj_DatumType type; /* The datum type */ + double params[7]; /* The parameters for type + EPRJ_DATUM_PARAMETRIC */ + char *gridname; /* name of the grid file */ +} Eprj_Datum; + +typedef struct { + char * sphereName; /* name of the ellipsoid */ + double a; /* semi-major axis of ellipsoid */ + double b; /* semi-minor axis of ellipsoid */ + double eSquared; /* eccentricity-squared */ + double radius; /* radius of the sphere */ +} Eprj_Spheroid; + +typedef struct { + Eprj_ProType proType; /* projection type */ + long proNumber; /* projection number for internal + projections */ + char * proExeName; /* projection executable name for + EXTERNal projections */ + char * proName; /* projection name */ + long proZone; /* projection zone (UTM, SP only) */ + double proParams[15]; /* projection parameters array in the + GCTP form */ + Eprj_Spheroid proSpheroid; /* projection spheroid */ +} Eprj_ProParameters; + +typedef struct { + int order; + double polycoefmtx[18]; + double polycoefvector[2]; +} Efga_Polynomial; + +/* -------------------------------------------------------------------- */ +/* Prototypes */ +/* -------------------------------------------------------------------- */ + +CPL_C_START + +HFAHandle CPL_DLL HFAOpen( const char * pszFilename, const char * pszMode ); +void CPL_DLL HFAClose( HFAHandle ); +CPLErr HFADelete( const char *pszFilename ); +CPLErr HFARenameReferences( HFAHandle, const char *, const char * ); + +HFAHandle CPL_DLL HFACreateLL( const char *pszFilename ); +HFAHandle CPL_DLL HFACreate( const char *pszFilename, int nXSize, int nYSize, + int nBands, int nDataType, char ** papszOptions ); +const char CPL_DLL *HFAGetIGEFilename( HFAHandle ); +CPLErr CPL_DLL HFAFlush( HFAHandle ); +int CPL_DLL HFACreateOverview( HFAHandle hHFA, int nBand, int nOverviewLevel, + const char *pszResampling ); + +const Eprj_MapInfo CPL_DLL *HFAGetMapInfo( HFAHandle ); +int CPL_DLL HFAGetGeoTransform( HFAHandle, double* ); +CPLErr CPL_DLL HFASetGeoTransform( HFAHandle, const char*, const char*,double*); +CPLErr CPL_DLL HFASetMapInfo( HFAHandle, const Eprj_MapInfo * ); +const Eprj_Datum CPL_DLL *HFAGetDatum( HFAHandle ); +CPLErr CPL_DLL HFASetDatum( HFAHandle, const Eprj_Datum * ); +const Eprj_ProParameters CPL_DLL *HFAGetProParameters( HFAHandle ); +char CPL_DLL *HFAGetPEString( HFAHandle ); +CPLErr CPL_DLL HFASetPEString( HFAHandle hHFA, const char *pszPEString ); +CPLErr CPL_DLL HFASetProParameters( HFAHandle, const Eprj_ProParameters * ); + +CPLErr CPL_DLL HFAGetRasterInfo( HFAHandle hHFA, int *pnXSize, int *pnYSize, + int *pnBands ); +CPLErr CPL_DLL HFAGetBandInfo( HFAHandle hHFA, int nBand, int * pnDataType, + int * pnBlockXSize, int * pnBlockYSize, + int *pnCompressionType ); +int CPL_DLL HFAGetBandNoData( HFAHandle hHFA, int nBand, double *pdfValue ); +CPLErr CPL_DLL HFASetBandNoData( HFAHandle hHFA, int nBand, double dfValue ); +int CPL_DLL HFAGetOverviewCount( HFAHandle hHFA, int nBand ); +CPLErr CPL_DLL HFAGetOverviewInfo( HFAHandle hHFA, int nBand, int nOverview, + int * pnXSize, int * pnYSize, + int * pnBlockXSize, int * pnBlockYSize, + int * pnHFADataType ); +CPLErr CPL_DLL HFAGetRasterBlock( HFAHandle hHFA, int nBand, int nXBlock, + int nYBlock, void * pData ); +CPLErr CPL_DLL HFAGetRasterBlockEx( HFAHandle hHFA, int nBand, int nXBlock, + int nYBlock, void * pData, int nDataSize ); +CPLErr CPL_DLL HFAGetOverviewRasterBlock( HFAHandle hHFA, int nBand, + int iOverview, + int nXBlock, int nYBlock, void * pData ); +CPLErr CPL_DLL HFAGetOverviewRasterBlockEx( HFAHandle hHFA, int nBand, + int iOverview, + int nXBlock, int nYBlock, void * pData, int nDataSize ); +CPLErr CPL_DLL HFASetRasterBlock( HFAHandle hHFA, int nBand, + int nXBlock, int nYBlock, + void * pData ); +CPLErr CPL_DLL HFASetOverviewRasterBlock( + HFAHandle hHFA, int nBand, int iOverview,int nXBlock, int nYBlock, + void * pData ); +const char * HFAGetBandName( HFAHandle hHFA, int nBand ); +void HFASetBandName( HFAHandle hHFA, int nBand, const char *pszName ); +int CPL_DLL HFAGetDataTypeBits( int ); +const char CPL_DLL *HFAGetDataTypeName( int ); +CPLErr CPL_DLL HFAGetPCT( HFAHandle, int, int *, + double **, double **, double ** , double **, + double **); +CPLErr CPL_DLL HFASetPCT( HFAHandle, int, int, double *, double *, double *, double * ); +void CPL_DLL HFADumpTree( HFAHandle, FILE * ); +void CPL_DLL HFADumpDictionary( HFAHandle, FILE * ); +CPLErr CPL_DLL HFAGetDataRange( HFAHandle, int, double *, double * ); +char CPL_DLL **HFAGetMetadata( HFAHandle hHFA, int nBand ); +CPLErr CPL_DLL HFASetMetadata( HFAHandle hHFA, int nBand, char ** ); +char CPL_DLL **HFAGetClassNames( HFAHandle hHFA, int nBand ); +int CPL_DLL +HFACreateLayer( HFAHandle psInfo, HFAEntry *poParent, + const char *pszLayerName, + int bOverview, int nBlockSize, + int bCreateCompressed, int bCreateLargeRaster, + int bDependentLayer, + int nXSize, int nYSize, int nDataType, + char **papszOptions, + + // these are only related to external (large) files + GIntBig nStackValidFlagsOffset, + GIntBig nStackDataOffset, + int nStackCount, int nStackIndex ); + +int CPL_DLL +HFAReadXFormStack( HFAHandle psInfo, + Efga_Polynomial **ppasPolyListForward, + Efga_Polynomial **ppasPolyListReverse ); +CPLErr CPL_DLL +HFAWriteXFormStack( HFAHandle psInfo, int nBand, int nXFormCount, + Efga_Polynomial **ppasPolyListForward, + Efga_Polynomial **ppasPolyListReverse ); +int CPL_DLL +HFAEvaluateXFormStack( int nStepCount, int bForward, + Efga_Polynomial *pasPolyList, + double *pdfX, double *pdfY ); + +char CPL_DLL **HFAReadCameraModel( HFAHandle psInfo ); + +char * +HFAPCSStructToWKT( const Eprj_Datum *psDatum, + const Eprj_ProParameters *psPro, + const Eprj_MapInfo *psMapInfo, + HFAEntry *poMapInformation ); + +/* -------------------------------------------------------------------- */ +/* data types. */ +/* -------------------------------------------------------------------- */ +#define EPT_u1 0 +#define EPT_u2 1 +#define EPT_u4 2 +#define EPT_u8 3 +#define EPT_s8 4 +#define EPT_u16 5 +#define EPT_s16 6 +#define EPT_u32 7 +#define EPT_s32 8 +#define EPT_f32 9 +#define EPT_f64 10 +#define EPT_c64 11 +#define EPT_c128 12 + +/* -------------------------------------------------------------------- */ +/* Projection codes. */ +/* -------------------------------------------------------------------- */ +#define EPRJ_LATLONG 0 +#define EPRJ_UTM 1 +#define EPRJ_STATE_PLANE 2 +#define EPRJ_ALBERS_CONIC_EQUAL_AREA 3 +#define EPRJ_LAMBERT_CONFORMAL_CONIC 4 +#define EPRJ_MERCATOR 5 +#define EPRJ_POLAR_STEREOGRAPHIC 6 +#define EPRJ_POLYCONIC 7 +#define EPRJ_EQUIDISTANT_CONIC 8 +#define EPRJ_TRANSVERSE_MERCATOR 9 +#define EPRJ_STEREOGRAPHIC 10 +#define EPRJ_LAMBERT_AZIMUTHAL_EQUAL_AREA 11 +#define EPRJ_AZIMUTHAL_EQUIDISTANT 12 +#define EPRJ_GNOMONIC 13 +#define EPRJ_ORTHOGRAPHIC 14 +#define EPRJ_GENERAL_VERTICAL_NEAR_SIDE_PERSPECTIVE 15 +#define EPRJ_SINUSOIDAL 16 +#define EPRJ_EQUIRECTANGULAR 17 +#define EPRJ_MILLER_CYLINDRICAL 18 +#define EPRJ_VANDERGRINTEN 19 +#define EPRJ_HOTINE_OBLIQUE_MERCATOR 20 +#define EPRJ_SPACE_OBLIQUE_MERCATOR 21 +#define EPRJ_MODIFIED_TRANSVERSE_MERCATOR 22 +#define EPRJ_EOSAT_SOM 23 +#define EPRJ_ROBINSON 24 +#define EPRJ_SOM_A_AND_B 25 +#define EPRJ_ALASKA_CONFORMAL 26 +#define EPRJ_INTERRUPTED_GOODE_HOMOLOSINE 27 +#define EPRJ_MOLLWEIDE 28 +#define EPRJ_INTERRUPTED_MOLLWEIDE 29 +#define EPRJ_HAMMER 30 +#define EPRJ_WAGNER_IV 31 +#define EPRJ_WAGNER_VII 32 +#define EPRJ_OBLATED_EQUAL_AREA 33 +#define EPRJ_PLATE_CARREE 34 +#define EPRJ_EQUIDISTANT_CYLINDRICAL 35 +#define EPRJ_GAUSS_KRUGER 36 +#define EPRJ_ECKERT_VI 37 +#define EPRJ_ECKERT_V 38 +#define EPRJ_ECKERT_IV 39 +#define EPRJ_ECKERT_III 40 +#define EPRJ_ECKERT_II 41 +#define EPRJ_ECKERT_I 42 +#define EPRJ_GALL_STEREOGRAPHIC 43 +#define EPRJ_BEHRMANN 44 +#define EPRJ_WINKEL_I 45 +#define EPRJ_WINKEL_II 46 +#define EPRJ_QUARTIC_AUTHALIC 47 +#define EPRJ_LOXIMUTHAL 48 +#define EPRJ_BONNE 49 +#define EPRJ_STEREOGRAPHIC_EXTENDED 50 +#define EPRJ_CASSINI 51 +#define EPRJ_TWO_POINT_EQUIDISTANT 52 +#define EPRJ_ANCHORED_LSR 53 +#define EPRJ_KROVAK 54 +#define EPRJ_DOUBLE_STEREOGRAPHIC 55 +#define EPRJ_AITOFF 56 +#define EPRJ_CRASTER_PARABOLIC 57 +#define EPRJ_CYLINDRICAL_EQUAL_AREA 58 +#define EPRJ_FLAT_POLAR_QUARTIC 59 +#define EPRJ_TIMES 60 +#define EPRJ_WINKEL_TRIPEL 61 +#define EPRJ_HAMMER_AITOFF 62 +#define EPRJ_VERTICAL_NEAR_SIDE_PERSPECTIVE 63 +#define EPRJ_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER 64 +#define EPRJ_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_CENTER 65 +#define EPRJ_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN 66 +#define EPRJ_LAMBERT_CONFORMAL_CONIC_1SP 67 +#define EPRJ_PSEUDO_MERCATOR 68 +#define EPRJ_MERCATOR_VARIANT_A 69 + +#define EPRJ_EXTERNAL_RSO "eprj_rso" +#define EPRJ_EXTERNAL_NZMG "nzmg" +#define EPRJ_EXTERNAL_INTEGERIZED_SINUSOIDAL "isin" + +CPL_C_END + +#endif /* ndef _HFAOPEN_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/hfa/hfa_overviews.cpp b/bazaar/plugin/gdal/frmts/hfa/hfa_overviews.cpp new file mode 100644 index 000000000..c0b03feda --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/hfa_overviews.cpp @@ -0,0 +1,127 @@ +/****************************************************************************** + * $Id: hfa_overviews.cpp 25583 2013-01-30 18:48:22Z bishop $ + * + * Project: Erdas Imagine Driver + * Purpose: Entry point for building overviews, used by non-imagine formats. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "hfa_p.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: hfa_overviews.cpp 25583 2013-01-30 18:48:22Z bishop $"); + +CPLErr HFAAuxBuildOverviews( const char *pszOvrFilename, + GDALDataset *poParentDS, + GDALDataset **ppoODS, + int nBands, int *panBandList, + int nNewOverviews, int *panNewOverviewList, + const char *pszResampling, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ +/* ==================================================================== */ +/* If the .aux file doesn't exist yet then create it now. */ +/* ==================================================================== */ + if( *ppoODS == NULL ) + { + GDALDataType eDT = GDT_Unknown; +/* -------------------------------------------------------------------- */ +/* Determine the band datatype, and verify that all bands are */ +/* the same. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 0; iBand < nBands; iBand++ ) + { + GDALRasterBand *poBand = + poParentDS->GetRasterBand( panBandList[iBand] ); + + if( iBand == 0 ) + eDT = poBand->GetRasterDataType(); + else + { + if( eDT != poBand->GetRasterDataType() ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "HFAAuxBuildOverviews() doesn't support a mixture of band" + " data types." ); + return CE_Failure; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create the HFA (.aux) file. We create it with */ +/* COMPRESSED=YES so that no space will be allocated for the */ +/* base band. */ +/* -------------------------------------------------------------------- */ + GDALDriver *poHFADriver = (GDALDriver *) GDALGetDriverByName("HFA"); + if (poHFADriver == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, + "HFA driver is unavailable." ); + return CE_Failure; + } + + const char *apszOptions[4] = { "COMPRESSED=YES", "AUX=YES", + NULL, NULL }; + + CPLString osDepFileOpt = "DEPENDENT_FILE="; + osDepFileOpt += CPLGetFilename(poParentDS->GetDescription()); + apszOptions[2] = osDepFileOpt.c_str(); + + *ppoODS = + poHFADriver->Create( pszOvrFilename, + poParentDS->GetRasterXSize(), + poParentDS->GetRasterYSize(), + poParentDS->GetRasterCount(), eDT, (char **)apszOptions ); + + if( *ppoODS == NULL ) + return CE_Failure; + } + +/* ==================================================================== */ +/* Create the layers. We depend on the normal buildoverviews */ +/* support for HFA to do this. But we disable the internal */ +/* computation of the imagery for these layers. */ +/* */ +/* We avoid regenerating the new layers here, because if we did */ +/* it would use the base layer from the .aux file as the source */ +/* data, and that is fake (all invalid tiles). */ +/* ==================================================================== */ + CPLString oAdjustedResampling = "NO_REGEN:"; + oAdjustedResampling += pszResampling; + + CPLErr eErr = + (*ppoODS)->BuildOverviews( oAdjustedResampling, + nNewOverviews, panNewOverviewList, + nBands, panBandList, + pfnProgress, pProgressData ); + + return eErr; +} + diff --git a/bazaar/plugin/gdal/frmts/hfa/hfa_p.h b/bazaar/plugin/gdal/frmts/hfa/hfa_p.h new file mode 100644 index 000000000..f330c894f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/hfa_p.h @@ -0,0 +1,472 @@ +/****************************************************************************** + * $Id: hfa_p.h 28275 2015-01-02 18:45:58Z rouault $ + * + * Project: Erdas Imagine (.img) Translator + * Purpose: Private class declarations for the HFA classes used to read + * Erdas Imagine (.img) files. Public (C callable) declarations + * are in hfa.h. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Intergraph Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _HFA_P_H_INCLUDED +#define _HFA_P_H_INCLUDED + +#include "cpl_port.h" +#include "cpl_error.h" +#include "cpl_vsi.h" +#include + +#ifdef CPL_LSB +# define HFAStandard(n,p) {} +#else + void HFAStandard( int, void *); +#endif + +class HFAEntry; +class HFAType; +class HFADictionary; +class HFABand; +class HFASpillFile; + +/************************************************************************/ +/* Flag indicating read/write, or read-only access to data. */ +/************************************************************************/ +typedef enum { + /*! Read only (no update) access */ HFA_ReadOnly = 0, + /*! Read/write access. */ HFA_Update = 1 +} HFAAccess; + +/************************************************************************/ +/* HFAInfo_t */ +/* */ +/* This is just a structure, and used hold info about the whole */ +/* dataset within hfaopen.cpp */ +/************************************************************************/ +typedef struct hfainfo { + VSILFILE *fp; + + char *pszPath; + char *pszFilename; /* sans path */ + char *pszIGEFilename; /* sans path */ + + HFAAccess eAccess; + + GUInt32 nEndOfFile; + GUInt32 nRootPos; + GUInt32 nDictionaryPos; + + GInt16 nEntryHeaderLength; + GInt32 nVersion; + + int bTreeDirty; + HFAEntry *poRoot; + + HFADictionary *poDictionary; + char *pszDictionary; + + int nXSize; + int nYSize; + + int nBands; + HFABand **papoBand; + + void *pMapInfo; + void *pDatum; + void *pProParameters; + + struct hfainfo *psDependent; +} HFAInfo_t; + +GUInt32 HFAAllocateSpace( HFAInfo_t *, GUInt32 ); +CPLErr HFAParseBandInfo( HFAInfo_t * ); +HFAInfo_t *HFAGetDependent( HFAInfo_t *, const char * ); +HFAInfo_t *HFACreateDependent( HFAInfo_t *psBase ); +int HFACreateSpillStack( HFAInfo_t *, int nXSize, int nYSize, int nLayers, + int nBlockSize, int nDataType, + GIntBig *pnValidFlagsOffset, + GIntBig *pnDataOffset ); + +const char ** GetHFAAuxMetaDataList(); + +double *HFAReadBFUniqueBins( HFAEntry *poBinFunc, int nPCTColors ); + +#define HFA_PRIVATE + +#include "hfa.h" + +/************************************************************************/ +/* HFABand */ +/************************************************************************/ + +class HFABand +{ + int nBlocks; + + // Used for single-file modification + vsi_l_offset *panBlockStart; + int *panBlockSize; + int *panBlockFlag; + + // Used for spill-file modification + vsi_l_offset nBlockStart; + vsi_l_offset nBlockSize; + int nLayerStackCount; + int nLayerStackIndex; + +#define BFLG_VALID 0x01 +#define BFLG_COMPRESSED 0x02 + + int nPCTColors; + double *apadfPCT[4]; + double *padfPCTBins; + + CPLErr LoadBlockInfo(); + CPLErr LoadExternalBlockInfo(); + + void ReAllocBlock( int iBlock, int nSize ); + void NullBlock( void * ); + + CPLString osOverName; + + public: + HFABand( HFAInfo_t *, HFAEntry * ); + ~HFABand(); + + HFAInfo_t *psInfo; + + VSILFILE *fpExternal; + + int nDataType; + HFAEntry *poNode; + + int nBlockXSize; + int nBlockYSize; + + int nWidth; + int nHeight; + + int nBlocksPerRow; + int nBlocksPerColumn; + + int bNoDataSet; + double dfNoData; + + int bOverviewsPending; + int nOverviews; + HFABand **papoOverviews; + + CPLErr GetRasterBlock( int nXBlock, int nYBlock, void * pData, int nDataSize ); + CPLErr SetRasterBlock( int nXBlock, int nYBlock, void * pData ); + + const char * GetBandName(); + void SetBandName(const char *pszName); + + CPLErr SetNoDataValue( double dfValue ); + + CPLErr GetPCT( int *, double **, double **, double **, double **, + double ** ); + CPLErr SetPCT( int, double *, double *, double *, double * ); + + int CreateOverview( int nOverviewLevel, const char *pszResampling ); + CPLErr CleanOverviews(); + + CPLErr LoadOverviews(); +}; + + +/************************************************************************/ +/* HFAEntry */ +/* */ +/* Base class for all entry types. Most entry types do not */ +/* have a subclass, and are just handled generically with this */ +/* class. */ +/************************************************************************/ +class HFAEntry +{ + int bDirty; + GUInt32 nFilePos; + + HFAInfo_t *psHFA; + HFAEntry *poParent; + HFAEntry *poPrev; + + GUInt32 nNextPos; + HFAEntry *poNext; + + GUInt32 nChildPos; + HFAEntry *poChild; + + char szName[64]; + char szType[32]; + + HFAType *poType; + + GUInt32 nDataPos; + GUInt32 nDataSize; + GByte *pabyData; + + void LoadData(); + + int GetFieldValue( const char *, char, void *, + int *pnRemainingDataSize ); + CPLErr SetFieldValue( const char *, char, void * ); + + int bIsMIFObject; + + HFAEntry(); + HFAEntry( HFAEntry *poContainer, + const char *pszMIFObjectPath, + const char * pszDictionnary, + const char * pszTypeName, + int nDataSizeIn, + GByte* pabyDataIn ); + std::vector FindChildren( const char *pszName, + const char *pszType, + int nRecLevel, + int* pbErrorDetected); + +public: + static HFAEntry* New( HFAInfo_t * psHFA, GUInt32 nPos, + HFAEntry * poParent, HFAEntry *poPrev); + + HFAEntry( HFAInfo_t *psHFA, + const char *pszNodeName, + const char *pszTypeName, + HFAEntry *poParent ); + + + virtual ~HFAEntry(); + + static HFAEntry* BuildEntryFromMIFObject( HFAEntry *poContainer, const char *pszMIFObjectPath ); + + CPLErr RemoveAndDestroy(); + + GUInt32 GetFilePos() { return nFilePos; } + + const char *GetName() { return szName; } + void SetName( const char *pszNodeName ); + + const char *GetType() { return szType; } + HFAType *GetTypeObject(); + + GByte *GetData() { LoadData(); return pabyData; } + GUInt32 GetDataPos() { return nDataPos; } + GUInt32 GetDataSize() { return nDataSize; } + + HFAEntry *GetChild(); + HFAEntry *GetNext(); + HFAEntry *GetNamedChild( const char * ); + std::vector FindChildren( const char *pszName, + const char *pszType); + + GInt32 GetIntField( const char *, CPLErr * = NULL ); + double GetDoubleField( const char *, CPLErr * = NULL ); + const char *GetStringField( const char *, CPLErr * = NULL, int *pnRemainingDataSize = NULL ); + GIntBig GetBigIntField( const char *, CPLErr * = NULL ); + int GetFieldCount( const char *, CPLErr * = NULL ); + + CPLErr SetIntField( const char *, int ); + CPLErr SetDoubleField( const char *, double ); + CPLErr SetStringField( const char *, const char * ); + + void DumpFieldValues( FILE *, const char * = NULL ); + + void SetPosition(); + CPLErr FlushToDisk(); + + void MarkDirty(); + GByte *MakeData( int nSize = 0 ); +}; + +/************************************************************************/ +/* HFAField */ +/* */ +/* A field in a HFAType in the dictionary. */ +/************************************************************************/ + +class HFAField +{ + public: + int nBytes; + + int nItemCount; + char chPointer; /* '\0', '*' or 'p' */ + char chItemType; /* 1|2|4|e|... */ + + char *pszItemObjectType; /* if chItemType == 'o' */ + HFAType *poItemObjectType; + + char **papszEnumNames; /* normally NULL if not an enum */ + + char *pszFieldName; + + char szNumberString[36]; /* buffer used to return an int as a string */ + + HFAField(); + ~HFAField(); + + const char *Initialize( const char * ); + + void CompleteDefn( HFADictionary * ); + + void Dump( FILE * ); + + int ExtractInstValue( const char * pszField, int nIndexValue, + GByte *pabyData, GUInt32 nDataOffset, int nDataSize, + char chReqType, void *pReqReturn, int *pnRemainingDataSize = NULL ); + + CPLErr SetInstValue( const char * pszField, int nIndexValue, + GByte *pabyData, GUInt32 nDataOffset, int nDataSize, + char chReqType, void *pValue ); + + void DumpInstValue( FILE *fpOut, + GByte *pabyData, GUInt32 nDataOffset, int nDataSize, + const char *pszPrefix = NULL ); + + int GetInstBytes( GByte *, int ); + int GetInstCount( GByte * pabyData, int nDataSize ); +}; + + +/************************************************************************/ +/* HFAType */ +/* */ +/* A type in the dictionary. */ +/************************************************************************/ + +class HFAType +{ + int bInCompleteDefn; + + public: + int nBytes; + + int nFields; + HFAField **papoFields; + + char *pszTypeName; + + HFAType(); + ~HFAType(); + + const char *Initialize( const char * ); + + void CompleteDefn( HFADictionary * ); + + void Dump( FILE * ); + + int GetInstBytes( GByte *, int ); + int GetInstCount( const char *pszField, + GByte *pabyData, GUInt32 nDataOffset, int nDataSize); + int ExtractInstValue( const char * pszField, + GByte *pabyData, GUInt32 nDataOffset, int nDataSize, + char chReqType, void *pReqReturn, int *pnRemainingDataSize ); + CPLErr SetInstValue( const char * pszField, + GByte *pabyData, GUInt32 nDataOffset, int nDataSize, + char chReqType, void * pValue ); + void DumpInstValue( FILE *fpOut, + GByte *pabyData, GUInt32 nDataOffset, int nDataSize, + const char *pszPrefix = NULL ); +}; + +/************************************************************************/ +/* HFADictionary */ +/************************************************************************/ + +class HFADictionary +{ + public: + int nTypes; + int nTypesMax; + HFAType **papoTypes; + + CPLString osDictionaryText; + int bDictionaryTextDirty; + + HFADictionary( const char *pszDict ); + ~HFADictionary(); + + HFAType *FindType( const char * ); + void AddType( HFAType * ); + + static int GetItemSize( char ); + + void Dump( FILE * ); +}; + +/************************************************************************/ +/* HFACompress */ +/* */ +/* Class that given a block of memory compresses the contents */ +/* using run length encoding as used by Imagine. */ +/************************************************************************/ + +class HFACompress +{ +public: + HFACompress( void *pData, GUInt32 nBlockSize, int nDataType ); + ~HFACompress(); + + // This is the method that does the work. + bool compressBlock(); + + // static method to allow us to query whether HFA type supported + static bool QueryDataTypeSupported( int nHFADataType ); + + // Get methods - only valid after compressBlock has been called. + GByte* getCounts() { return m_pCounts; }; + GUInt32 getCountSize() { return m_nSizeCounts; }; + GByte* getValues() { return m_pValues; }; + GUInt32 getValueSize() { return m_nSizeValues; }; + GUInt32 getMin() { return m_nMin; }; + GUInt32 getNumRuns() { return m_nNumRuns; }; + GByte getNumBits() { return m_nNumBits; }; + +private: + void makeCount( GUInt32 count, GByte *pCounter, GUInt32 *pnSizeCount ); + GUInt32 findMin( GByte *pNumBits ); + GUInt32 valueAsUInt32( GUInt32 index ); + void encodeValue( GUInt32 val, GUInt32 repeat ); + + void *m_pData; + GUInt32 m_nBlockSize; + GUInt32 m_nBlockCount; + int m_nDataType; + int m_nDataTypeNumBits; // the number of bits the datatype we are trying to compress takes + + GByte *m_pCounts; + GByte *m_pCurrCount; + GUInt32 m_nSizeCounts; + + GByte *m_pValues; + GByte *m_pCurrValues; + GUInt32 m_nSizeValues; + + GUInt32 m_nMin; + GUInt32 m_nNumRuns; + GByte m_nNumBits; // the number of bits needed to compress the range of values in the block + +}; + +#endif /* ndef _HFA_P_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/hfa/hfaband.cpp b/bazaar/plugin/gdal/frmts/hfa/hfaband.cpp new file mode 100644 index 000000000..1f2d433e9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/hfaband.cpp @@ -0,0 +1,2192 @@ +/****************************************************************************** + * $Id: hfaband.cpp 28435 2015-02-07 14:35:34Z rouault $ + * + * Project: Erdas Imagine (.img) Translator + * Purpose: Implementation of the HFABand, for accessing one Eimg_Layer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Intergraph Corporation + * Copyright (c) 2007-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "hfa_p.h" +#include "cpl_conv.h" + +/* include the compression code */ + +CPL_CVSID("$Id: hfaband.cpp 28435 2015-02-07 14:35:34Z rouault $"); + +/************************************************************************/ +/* HFABand() */ +/************************************************************************/ + +HFABand::HFABand( HFAInfo_t * psInfoIn, HFAEntry * poNodeIn ) + +{ + psInfo = psInfoIn; + poNode = poNodeIn; + + bOverviewsPending = TRUE; + + nBlockXSize = poNodeIn->GetIntField( "blockWidth" ); + nBlockYSize = poNodeIn->GetIntField( "blockHeight" ); + nDataType = poNodeIn->GetIntField( "pixelType" ); + + nWidth = poNodeIn->GetIntField( "width" ); + nHeight = poNodeIn->GetIntField( "height" ); + + panBlockStart = NULL; + panBlockSize = NULL; + panBlockFlag = NULL; + + nPCTColors = -1; + apadfPCT[0] = apadfPCT[1] = apadfPCT[2] = apadfPCT[3] = NULL; + padfPCTBins = NULL; + + nOverviews = 0; + papoOverviews = NULL; + + fpExternal = NULL; + + bNoDataSet = FALSE; + dfNoData = 0.0; + + if (nWidth <= 0 || nHeight <= 0 || nBlockXSize <= 0 || nBlockYSize <= 0) + { + nWidth = nHeight = 0; + CPLError(CE_Failure, CPLE_AppDefined, + "HFABand::HFABand : (nWidth <= 0 || nHeight <= 0 || nBlockXSize <= 0 || nBlockYSize <= 0)"); + return; + } + if (HFAGetDataTypeBits(nDataType) == 0) + { + nWidth = nHeight = 0; + CPLError(CE_Failure, CPLE_AppDefined, + "HFABand::HFABand : nDataType=%d unhandled", nDataType); + return; + } + + /* FIXME? : risk of overflow in additions and multiplication */ + nBlocksPerRow = (nWidth + nBlockXSize - 1) / nBlockXSize; + nBlocksPerColumn = (nHeight + nBlockYSize - 1) / nBlockYSize; + nBlocks = nBlocksPerRow * nBlocksPerColumn; + +/* -------------------------------------------------------------------- */ +/* Check for nodata. This is really an RDO (ESRI Raster Data */ +/* Objects?), not used by Imagine itself. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poNDNode = poNode->GetNamedChild("Eimg_NonInitializedValue"); + + if( poNDNode != NULL ) + { + bNoDataSet = TRUE; + dfNoData = poNDNode->GetDoubleField( "valueBD" ); + } + +} + +/************************************************************************/ +/* ~HFABand() */ +/************************************************************************/ + +HFABand::~HFABand() + +{ + for( int iOverview = 0; iOverview < nOverviews; iOverview++ ) + delete papoOverviews[iOverview]; + + if( nOverviews > 0 ) + CPLFree( papoOverviews ); + + if ( panBlockStart ) + CPLFree( panBlockStart ); + if ( panBlockSize ) + CPLFree( panBlockSize ); + if ( panBlockFlag ) + CPLFree( panBlockFlag ); + + CPLFree( apadfPCT[0] ); + CPLFree( apadfPCT[1] ); + CPLFree( apadfPCT[2] ); + CPLFree( apadfPCT[3] ); + CPLFree( padfPCTBins ); + + if( fpExternal != NULL ) + VSIFCloseL( fpExternal ); +} + +/************************************************************************/ +/* LoadOverviews() */ +/************************************************************************/ + +CPLErr HFABand::LoadOverviews() + +{ + if( !bOverviewsPending ) + return CE_None; + + bOverviewsPending = FALSE; + +/* -------------------------------------------------------------------- */ +/* Does this band have overviews? Try to find them. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poRRDNames = poNode->GetNamedChild( "RRDNamesList" ); + + if( poRRDNames != NULL ) + { + for( int iName = 0; TRUE; iName++ ) + { + char szField[128], *pszPath, *pszFilename, *pszEnd; + const char *pszName; + CPLErr eErr; + HFAEntry *poOvEntry; + int i; + HFAInfo_t *psHFA; + + sprintf( szField, "nameList[%d].string", iName ); + + pszName = poRRDNames->GetStringField( szField, &eErr ); + if( pszName == NULL || eErr != CE_None ) + break; + + pszFilename = CPLStrdup(pszName); + pszEnd = strstr(pszFilename,"(:"); + if( pszEnd == NULL ) + { + CPLFree( pszFilename ); + continue; + } + + pszName = pszEnd + 2; + pszEnd[0] = '\0'; + + char *pszJustFilename; + + pszJustFilename = CPLStrdup(CPLGetFilename(pszFilename)); + psHFA = HFAGetDependent( psInfo, pszJustFilename ); + CPLFree( pszJustFilename ); + + // Try finding the dependent file as this file with the + // extension .rrd. This is intended to address problems + // with users changing the names of their files. + if( psHFA == NULL ) + { + char *pszBasename = + CPLStrdup(CPLGetBasename(psInfo->pszFilename)); + + pszJustFilename = + CPLStrdup(CPLFormFilename(NULL, pszBasename, "rrd")); + CPLDebug( "HFA", "Failed to find overview file with expected name,\ntry %s instead.", + pszJustFilename ); + psHFA = HFAGetDependent( psInfo, pszJustFilename ); + CPLFree( pszJustFilename ); + CPLFree( pszBasename ); + } + + if( psHFA == NULL ) + { + CPLFree( pszFilename ); + continue; + } + + pszPath = pszEnd + 2; + if( pszPath[strlen(pszPath)-1] == ')' ) + pszPath[strlen(pszPath)-1] = '\0'; + + for( i=0; pszPath[i] != '\0'; i++ ) + { + if( pszPath[i] == ':' ) + pszPath[i] = '.'; + } + + poOvEntry = psHFA->poRoot->GetNamedChild( pszPath ); + CPLFree( pszFilename ); + + if( poOvEntry == NULL ) + continue; + + /* + * We have an overview node. Instanatiate a HFABand from it, + * and add to the list. + */ + papoOverviews = (HFABand **) + CPLRealloc(papoOverviews, sizeof(void*) * ++nOverviews ); + papoOverviews[nOverviews-1] = new HFABand( psHFA, poOvEntry ); + if (papoOverviews[nOverviews-1]->nWidth == 0) + { + nWidth = nHeight = 0; + delete papoOverviews[nOverviews-1]; + papoOverviews[nOverviews-1] = NULL; + return CE_None; + } + } + } + +/* -------------------------------------------------------------------- */ +/* If there are no overviews mentioned in this file, probe for */ +/* an .rrd file anyways. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poBandProxyNode = poNode; + HFAInfo_t *psOvHFA = psInfo; + + if( nOverviews == 0 + && EQUAL(CPLGetExtension(psInfo->pszFilename),"aux") ) + { + CPLString osRRDFilename = CPLResetExtension( psInfo->pszFilename,"rrd"); + CPLString osFullRRD = CPLFormFilename( psInfo->pszPath, osRRDFilename, + NULL ); + VSIStatBufL sStatBuf; + + if( VSIStatL( osFullRRD, &sStatBuf ) == 0 ) + { + psOvHFA = HFAGetDependent( psInfo, osRRDFilename ); + if( psOvHFA ) + poBandProxyNode = + psOvHFA->poRoot->GetNamedChild( poNode->GetName() ); + else + psOvHFA = psInfo; + } + } + +/* -------------------------------------------------------------------- */ +/* If there are no named overviews, try looking for unnamed */ +/* overviews within the same layer, as occurs in floodplain.img */ +/* for instance, or in the not-referenced rrd mentioned in #3463. */ +/* -------------------------------------------------------------------- */ + if( nOverviews == 0 && poBandProxyNode != NULL ) + { + HFAEntry *poChild; + + for( poChild = poBandProxyNode->GetChild(); + poChild != NULL; + poChild = poChild->GetNext() ) + { + if( EQUAL(poChild->GetType(),"Eimg_Layer_SubSample") ) + { + papoOverviews = (HFABand **) + CPLRealloc(papoOverviews, sizeof(void*) * ++nOverviews ); + papoOverviews[nOverviews-1] = new HFABand( psOvHFA, poChild ); + if (papoOverviews[nOverviews-1]->nWidth == 0) + { + nWidth = nHeight = 0; + delete papoOverviews[nOverviews-1]; + papoOverviews[nOverviews-1] = NULL; + return CE_None; + } + } + } + + int i1, i2; + + // bubble sort into biggest to smallest order. + for( i1 = 0; i1 < nOverviews; i1++ ) + { + for( i2 = 0; i2 < nOverviews-1; i2++ ) + { + if( papoOverviews[i2]->nWidth < + papoOverviews[i2+1]->nWidth ) + { + HFABand *poTemp = papoOverviews[i2+1]; + papoOverviews[i2+1] = papoOverviews[i2]; + papoOverviews[i2] = poTemp; + } + } + } + } + return CE_None; +} + +/************************************************************************/ +/* LoadBlockInfo() */ +/************************************************************************/ + +CPLErr HFABand::LoadBlockInfo() + +{ + int iBlock; + HFAEntry *poDMS; + + if( panBlockFlag != NULL ) + return( CE_None ); + + poDMS = poNode->GetNamedChild( "RasterDMS" ); + if( poDMS == NULL ) + { + if( poNode->GetNamedChild( "ExternalRasterDMS" ) != NULL ) + return LoadExternalBlockInfo(); + + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find RasterDMS field in Eimg_Layer with block list.\n"); + + return CE_Failure; + } + + panBlockStart = (vsi_l_offset *)VSIMalloc2(sizeof(vsi_l_offset), nBlocks); + panBlockSize = (int *) VSIMalloc2(sizeof(int), nBlocks); + panBlockFlag = (int *) VSIMalloc2(sizeof(int), nBlocks); + + if (panBlockStart == NULL || panBlockSize == NULL || panBlockFlag == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "HFABand::LoadBlockInfo : Out of memory\n"); + + CPLFree(panBlockStart); + CPLFree(panBlockSize); + CPLFree(panBlockFlag); + panBlockStart = NULL; + panBlockSize = NULL; + panBlockFlag = NULL; + return CE_Failure; + } + + for( iBlock = 0; iBlock < nBlocks; iBlock++ ) + { + CPLErr eErr = CE_None; + char szVarName[64]; + int nLogvalid, nCompressType; + + sprintf( szVarName, "blockinfo[%d].offset", iBlock ); + panBlockStart[iBlock] = (GUInt32)poDMS->GetIntField( szVarName, &eErr); + if( eErr == CE_Failure ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s", szVarName); + return eErr; + } + + sprintf( szVarName, "blockinfo[%d].size", iBlock ); + panBlockSize[iBlock] = poDMS->GetIntField( szVarName, &eErr ); + if( eErr == CE_Failure ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s", szVarName); + return eErr; + } + + sprintf( szVarName, "blockinfo[%d].logvalid", iBlock ); + nLogvalid = poDMS->GetIntField( szVarName, &eErr ); + if( eErr == CE_Failure ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s", szVarName); + return eErr; + } + + sprintf( szVarName, "blockinfo[%d].compressionType", iBlock ); + nCompressType = poDMS->GetIntField( szVarName, &eErr ); + if( eErr == CE_Failure ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s", szVarName); + return eErr; + } + + panBlockFlag[iBlock] = 0; + if( nLogvalid ) + panBlockFlag[iBlock] |= BFLG_VALID; + if( nCompressType != 0 ) + panBlockFlag[iBlock] |= BFLG_COMPRESSED; + } + + return( CE_None ); +} + +/************************************************************************/ +/* LoadExternalBlockInfo() */ +/************************************************************************/ + +CPLErr HFABand::LoadExternalBlockInfo() + +{ + int iBlock; + HFAEntry *poDMS; + + if( panBlockFlag != NULL ) + return( CE_None ); + +/* -------------------------------------------------------------------- */ +/* Get the info structure. */ +/* -------------------------------------------------------------------- */ + poDMS = poNode->GetNamedChild( "ExternalRasterDMS" ); + CPLAssert( poDMS != NULL ); + + nLayerStackCount = poDMS->GetIntField( "layerStackCount" ); + nLayerStackIndex = poDMS->GetIntField( "layerStackIndex" ); + +/* -------------------------------------------------------------------- */ +/* Open raw data file. */ +/* -------------------------------------------------------------------- */ + const char *pszFullFilename = HFAGetIGEFilename( psInfo ); + if (pszFullFilename == NULL) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Cannot find external data file name" ); + return CE_Failure; + } + + if( psInfo->eAccess == HFA_ReadOnly ) + fpExternal = VSIFOpenL( pszFullFilename, "rb" ); + else + fpExternal = VSIFOpenL( pszFullFilename, "r+b" ); + if( fpExternal == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open external data file:\n%s\n", + pszFullFilename ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Verify header. */ +/* -------------------------------------------------------------------- */ + char szHeader[49]; + + VSIFReadL( szHeader, 49, 1, fpExternal ); + + if( strncmp( szHeader, "ERDAS_IMG_EXTERNAL_RASTER", 26 ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Raw data file %s appears to be corrupt.\n", + pszFullFilename ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Allocate blockmap. */ +/* -------------------------------------------------------------------- */ + panBlockFlag = (int *) VSIMalloc2(sizeof(int), nBlocks); + if (panBlockFlag == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "HFABand::LoadExternalBlockInfo : Out of memory\n"); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Load the validity bitmap. */ +/* -------------------------------------------------------------------- */ + unsigned char *pabyBlockMap; + int nBytesPerRow; + + nBytesPerRow = (nBlocksPerRow + 7) / 8; + pabyBlockMap = (unsigned char *) + VSIMalloc(nBytesPerRow*nBlocksPerColumn+20); + if (pabyBlockMap == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "HFABand::LoadExternalBlockInfo : Out of memory\n"); + return CE_Failure; + } + + VSIFSeekL( fpExternal, + poDMS->GetBigIntField( "layerStackValidFlagsOffset" ), + SEEK_SET ); + + if( VSIFReadL( pabyBlockMap, nBytesPerRow * nBlocksPerColumn + 20, 1, + fpExternal ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read block validity map." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Establish block information. Block position is computed */ +/* from data base address. Blocks are never compressed. */ +/* Validity is determined from the validity bitmap. */ +/* -------------------------------------------------------------------- */ + nBlockStart = poDMS->GetBigIntField( "layerStackDataOffset" ); + nBlockSize = (nBlockXSize*nBlockYSize*HFAGetDataTypeBits(nDataType)+7) / 8; + + for( iBlock = 0; iBlock < nBlocks; iBlock++ ) + { + int nRow, nColumn, nBit; + + nColumn = iBlock % nBlocksPerRow; + nRow = iBlock / nBlocksPerRow; + nBit = nRow * nBytesPerRow * 8 + nColumn + 20 * 8; + + if( (pabyBlockMap[nBit>>3] >> (nBit&7)) & 0x1 ) + panBlockFlag[iBlock] = BFLG_VALID; + else + panBlockFlag[iBlock] = 0; + } + + CPLFree( pabyBlockMap ); + + return( CE_None ); +} + +/************************************************************************/ +/* UncompressBlock() */ +/* */ +/* Uncompress ESRI Grid compression format block. */ +/************************************************************************/ + +#define CHECK_ENOUGH_BYTES(n) \ + if (nSrcBytes < (n)) goto not_enough_bytes; + +static CPLErr UncompressBlock( GByte *pabyCData, int nSrcBytes, + GByte *pabyDest, int nMaxPixels, + int nDataType ) + +{ + GUInt32 nDataMin; + int nNumBits, nPixelsOutput=0; + GInt32 nNumRuns, nDataOffset; + GByte *pabyCounter, *pabyValues; + int nValueBitOffset; + int nCounterOffset; + + CHECK_ENOUGH_BYTES(13); + + memcpy( &nDataMin, pabyCData, 4 ); + nDataMin = CPL_LSBWORD32( nDataMin ); + + memcpy( &nNumRuns, pabyCData+4, 4 ); + nNumRuns = CPL_LSBWORD32( nNumRuns ); + + memcpy( &nDataOffset, pabyCData+8, 4 ); + nDataOffset = CPL_LSBWORD32( nDataOffset ); + + nNumBits = pabyCData[12]; + +/* ==================================================================== */ +/* If this is not run length encoded, but just reduced */ +/* precision, handle it now. */ +/* ==================================================================== */ + if( nNumRuns == -1 ) + { + pabyValues = pabyCData + 13; + nValueBitOffset = 0; + + if (nNumBits > INT_MAX / nMaxPixels || + nNumBits * nMaxPixels > INT_MAX - 7 || + (nNumBits * nMaxPixels + 7)/8 > INT_MAX - 13) + { + CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow : nNumBits * nMaxPixels + 7"); + return CE_Failure; + } + CHECK_ENOUGH_BYTES(13 + (nNumBits * nMaxPixels + 7)/8); + +/* -------------------------------------------------------------------- */ +/* Loop over block pixels. */ +/* -------------------------------------------------------------------- */ + for( nPixelsOutput = 0; nPixelsOutput < nMaxPixels; nPixelsOutput++ ) + { + int nDataValue, nRawValue; + +/* -------------------------------------------------------------------- */ +/* Extract the data value in a way that depends on the number */ +/* of bits in it. */ +/* -------------------------------------------------------------------- */ + if( nNumBits == 0 ) + { + nRawValue = 0; + } + else if( nNumBits == 1 ) + { + nRawValue = + (pabyValues[nValueBitOffset>>3] >> (nValueBitOffset&7)) & 0x1; + nValueBitOffset++; + } + else if( nNumBits == 2 ) + { + nRawValue = + (pabyValues[nValueBitOffset>>3] >> (nValueBitOffset&7)) & 0x3; + nValueBitOffset += 2; + } + else if( nNumBits == 4 ) + { + nRawValue = + (pabyValues[nValueBitOffset>>3] >> (nValueBitOffset&7)) & 0xf; + nValueBitOffset += 4; + } + else if( nNumBits == 8 ) + { + nRawValue = *pabyValues; + pabyValues++; + } + else if( nNumBits == 16 ) + { + nRawValue = 256 * *(pabyValues++); + nRawValue += *(pabyValues++); + } + else if( nNumBits == 32 ) + { + nRawValue = 256 * 256 * 256 * *(pabyValues++); + nRawValue += 256 * 256 * *(pabyValues++); + nRawValue += 256 * *(pabyValues++); + nRawValue += *(pabyValues++); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported nNumBits value : %d", nNumBits); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Offset by the minimum value. */ +/* -------------------------------------------------------------------- */ + nDataValue = nRawValue + nDataMin; + +/* -------------------------------------------------------------------- */ +/* Now apply to the output buffer in a type specific way. */ +/* -------------------------------------------------------------------- */ + if( nDataType == EPT_u8 ) + { + ((GByte *) pabyDest)[nPixelsOutput] = (GByte) nDataValue; + } + else if( nDataType == EPT_u1 ) + { + if( nDataValue == 1 ) + pabyDest[nPixelsOutput>>3] |= (1 << (nPixelsOutput & 0x7)); + else + pabyDest[nPixelsOutput>>3] &= ~(1<<(nPixelsOutput & 0x7)); + } + else if( nDataType == EPT_u2 ) + { + if( (nPixelsOutput & 0x3) == 0 ) + pabyDest[nPixelsOutput>>2] = (GByte) nDataValue; + else if( (nPixelsOutput & 0x3) == 1 ) + pabyDest[nPixelsOutput>>2] |= (GByte) (nDataValue<<2); + else if( (nPixelsOutput & 0x3) == 2 ) + pabyDest[nPixelsOutput>>2] |= (GByte) (nDataValue<<4); + else + pabyDest[nPixelsOutput>>2] |= (GByte) (nDataValue<<6); + } + else if( nDataType == EPT_u4 ) + { + if( (nPixelsOutput & 0x1) == 0 ) + pabyDest[nPixelsOutput>>1] = (GByte) nDataValue; + else + pabyDest[nPixelsOutput>>1] |= (GByte) (nDataValue<<4); + } + else if( nDataType == EPT_s8 ) + { + ((GByte *) pabyDest)[nPixelsOutput] = (GByte) nDataValue; + } + else if( nDataType == EPT_u16 ) + { + ((GUInt16 *) pabyDest)[nPixelsOutput] = (GUInt16) nDataValue; + } + else if( nDataType == EPT_s16 ) + { + ((GInt16 *) pabyDest)[nPixelsOutput] = (GInt16) nDataValue; + } + else if( nDataType == EPT_s32 ) + { + ((GInt32 *) pabyDest)[nPixelsOutput] = nDataValue; + } + else if( nDataType == EPT_u32 ) + { + ((GUInt32 *) pabyDest)[nPixelsOutput] = nDataValue; + } + else if( nDataType == EPT_f32 ) + { +/* -------------------------------------------------------------------- */ +/* Note, floating point values are handled as if they were signed */ +/* 32-bit integers (bug #1000). */ +/* -------------------------------------------------------------------- */ + memcpy(&(((float *) pabyDest)[nPixelsOutput]), + &nDataValue, + sizeof(float)); + } + else + { + CPLAssert( FALSE ); + } + } + + return CE_None; + } + +/* ==================================================================== */ +/* Establish data pointers for runs. */ +/* ==================================================================== */ + if (nNumRuns < 0 || nDataOffset < 0) + { + CPLError(CE_Failure, CPLE_AppDefined, "nNumRuns=%d, nDataOffset=%d", + nNumRuns, nDataOffset); + return CE_Failure; + } + + if (nNumBits > INT_MAX / nNumRuns || + nNumBits * nNumRuns > INT_MAX - 7 || + (nNumBits * nNumRuns + 7)/8 > INT_MAX - nDataOffset) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Integer overflow : nDataOffset + (nNumBits * nNumRuns + 7)/8"); + return CE_Failure; + } + CHECK_ENOUGH_BYTES(nDataOffset + (nNumBits * nNumRuns + 7)/8); + + pabyCounter = pabyCData + 13; + nCounterOffset = 13; + pabyValues = pabyCData + nDataOffset; + nValueBitOffset = 0; + +/* -------------------------------------------------------------------- */ +/* Loop over runs. */ +/* -------------------------------------------------------------------- */ + int iRun; + + for( iRun = 0; iRun < nNumRuns; iRun++ ) + { + int nRepeatCount = 0; + int nDataValue; + +/* -------------------------------------------------------------------- */ +/* Get the repeat count. This can be stored as one, two, three */ +/* or four bytes depending on the low order two bits of the */ +/* first byte. */ +/* -------------------------------------------------------------------- */ + CHECK_ENOUGH_BYTES(nCounterOffset+1); + if( ((*pabyCounter) & 0xc0) == 0x00 ) + { + nRepeatCount = (*(pabyCounter++)) & 0x3f; + nCounterOffset ++; + } + else if( ((*pabyCounter) & 0xc0) == 0x40 ) + { + CHECK_ENOUGH_BYTES(nCounterOffset + 2); + nRepeatCount = (*(pabyCounter++)) & 0x3f; + nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++)); + nCounterOffset += 2; + } + else if( ((*pabyCounter) & 0xc0) == 0x80 ) + { + CHECK_ENOUGH_BYTES(nCounterOffset + 3); + nRepeatCount = (*(pabyCounter++)) & 0x3f; + nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++)); + nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++)); + nCounterOffset += 3; + } + else if( ((*pabyCounter) & 0xc0) == 0xc0 ) + { + CHECK_ENOUGH_BYTES(nCounterOffset + 4); + nRepeatCount = (*(pabyCounter++)) & 0x3f; + nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++)); + nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++)); + nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++)); + nCounterOffset += 4; + } + +/* -------------------------------------------------------------------- */ +/* Extract the data value in a way that depends on the number */ +/* of bits in it. */ +/* -------------------------------------------------------------------- */ + if( nNumBits == 0 ) + { + nDataValue = 0; + } + else if( nNumBits == 1 ) + { + nDataValue = + (pabyValues[nValueBitOffset>>3] >> (nValueBitOffset&7)) & 0x1; + nValueBitOffset++; + } + else if( nNumBits == 2 ) + { + nDataValue = + (pabyValues[nValueBitOffset>>3] >> (nValueBitOffset&7)) & 0x3; + nValueBitOffset += 2; + } + else if( nNumBits == 4 ) + { + nDataValue = + (pabyValues[nValueBitOffset>>3] >> (nValueBitOffset&7)) & 0xf; + nValueBitOffset += 4; + } + else if( nNumBits == 8 ) + { + nDataValue = *pabyValues; + pabyValues++; + } + else if( nNumBits == 16 ) + { + nDataValue = 256 * *(pabyValues++); + nDataValue += *(pabyValues++); + } + else if( nNumBits == 32 ) + { + nDataValue = 256 * 256 * 256 * *(pabyValues++); + nDataValue += 256 * 256 * *(pabyValues++); + nDataValue += 256 * *(pabyValues++); + nDataValue += *(pabyValues++); + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "nNumBits = %d", nNumBits ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Offset by the minimum value. */ +/* -------------------------------------------------------------------- */ + nDataValue += nDataMin; + +/* -------------------------------------------------------------------- */ +/* Now apply to the output buffer in a type specific way. */ +/* -------------------------------------------------------------------- */ + if( nPixelsOutput + nRepeatCount > nMaxPixels ) + { + CPLDebug("HFA", "Repeat count too big : %d", nRepeatCount); + nRepeatCount = nMaxPixels - nPixelsOutput; + } + + if( nDataType == EPT_u8 ) + { + int i; + + for( i = 0; i < nRepeatCount; i++ ) + { + //CPLAssert( nDataValue < 256 ); + ((GByte *) pabyDest)[nPixelsOutput++] = (GByte)nDataValue; + } + } + else if( nDataType == EPT_u16 ) + { + int i; + + for( i = 0; i < nRepeatCount; i++ ) + { + ((GUInt16 *) pabyDest)[nPixelsOutput++] = (GUInt16)nDataValue; + } + } + else if( nDataType == EPT_s8 ) + { + int i; + + for( i = 0; i < nRepeatCount; i++ ) + { + //CPLAssert( nDataValue < 256 ); + ((GByte *) pabyDest)[nPixelsOutput++] = (GByte)nDataValue; + } + } + else if( nDataType == EPT_s16 ) + { + int i; + + for( i = 0; i < nRepeatCount; i++ ) + { + ((GInt16 *) pabyDest)[nPixelsOutput++] = (GInt16)nDataValue; + } + } + else if( nDataType == EPT_u32 ) + { + int i; + + for( i = 0; i < nRepeatCount; i++ ) + { + ((GUInt32 *) pabyDest)[nPixelsOutput++] = (GUInt32)nDataValue; + } + } + else if( nDataType == EPT_s32 ) + { + int i; + + for( i = 0; i < nRepeatCount; i++ ) + { + ((GInt32 *) pabyDest)[nPixelsOutput++] = (GInt32)nDataValue; + } + } + else if( nDataType == EPT_f32 ) + { + int i; + float fDataValue; + + memcpy( &fDataValue, &nDataValue, 4); + for( i = 0; i < nRepeatCount; i++ ) + { + ((float *) pabyDest)[nPixelsOutput++] = fDataValue; + } + } + else if( nDataType == EPT_u1 ) + { + int i; + + //CPLAssert( nDataValue == 0 || nDataValue == 1 ); + + if( nDataValue == 1 ) + { + for( i = 0; i < nRepeatCount; i++ ) + { + pabyDest[nPixelsOutput>>3] |= (1 << (nPixelsOutput & 0x7)); + nPixelsOutput++; + } + } + else + { + for( i = 0; i < nRepeatCount; i++ ) + { + pabyDest[nPixelsOutput>>3] &= ~(1<<(nPixelsOutput & 0x7)); + nPixelsOutput++; + } + } + } + else if( nDataType == EPT_u2 ) + { + int i; + + //CPLAssert( nDataValue >= 0 && nDataValue < 4 ); + + for( i = 0; i < nRepeatCount; i++ ) + { + if( (nPixelsOutput & 0x3) == 0 ) + pabyDest[nPixelsOutput>>2] = (GByte) nDataValue; + else if( (nPixelsOutput & 0x3) == 1 ) + pabyDest[nPixelsOutput>>2] |= (GByte) (nDataValue<<2); + else if( (nPixelsOutput & 0x3) == 2 ) + pabyDest[nPixelsOutput>>2] |= (GByte) (nDataValue<<4); + else + pabyDest[nPixelsOutput>>2] |= (GByte) (nDataValue<<6); + nPixelsOutput++; + } + } + else if( nDataType == EPT_u4 ) + { + int i; + + //CPLAssert( nDataValue >= 0 && nDataValue < 16 ); + + for( i = 0; i < nRepeatCount; i++ ) + { + if( (nPixelsOutput & 0x1) == 0 ) + pabyDest[nPixelsOutput>>1] = (GByte) nDataValue; + else + pabyDest[nPixelsOutput>>1] |= (GByte) (nDataValue<<4); + + nPixelsOutput++; + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to uncompress an unsupported pixel data type."); + return CE_Failure; + } + } + + return CE_None; + + not_enough_bytes: + + CPLError(CE_Failure, CPLE_AppDefined, "Not enough bytes in compressed block"); + return CE_Failure; +} + +/************************************************************************/ +/* NullBlock() */ +/* */ +/* Set the block buffer to zero or the nodata value as */ +/* appropriate. */ +/************************************************************************/ + +void HFABand::NullBlock( void *pData ) + +{ + int nChunkSize = MAX(1,HFAGetDataTypeBits(nDataType)/8); + int nWords = nBlockXSize * nBlockYSize; + + if( !bNoDataSet ) + { +#ifdef ESRI_BUILD + // We want special defaulting for 1 bit data in ArcGIS. + if ( nDataType >= EPT_u2 ) + memset( pData, 0, nChunkSize*nWords ); + else + memset( pData, 255, nChunkSize*nWords ); +#else + memset( pData, 0, nChunkSize*nWords ); +#endif + } + else + { + GByte abyTmp[16]; + int i; + + switch( nDataType ) + { + case EPT_u1: + { + nWords = (nWords + 7)/8; + if( dfNoData != 0.0 ) + ((unsigned char *) abyTmp)[0] = 0xff; + else + ((unsigned char *) abyTmp)[0] = 0x00; + } + break; + + case EPT_u2: + { + nWords = (nWords + 3)/4; + if( dfNoData == 0.0 ) + ((unsigned char *) abyTmp)[0] = 0x00; + else if( dfNoData == 1.0 ) + ((unsigned char *) abyTmp)[0] = 0x55; + else if( dfNoData == 2.0 ) + ((unsigned char *) abyTmp)[0] = 0xaa; + else + ((unsigned char *) abyTmp)[0] = 0xff; + } + break; + + case EPT_u4: + { + unsigned char byVal = + (unsigned char) MAX(0,MIN(15,(int)dfNoData)); + + nWords = (nWords + 1)/2; + + ((unsigned char *) abyTmp)[0] = byVal + (byVal << 4); + } + break; + + case EPT_u8: + ((unsigned char *) abyTmp)[0] = + (unsigned char) MAX(0,MIN(255,(int)dfNoData)); + break; + + case EPT_s8: + ((signed char *) abyTmp)[0] = + (signed char) MAX(-128,MIN(127,(int)dfNoData)); + break; + + case EPT_u16: + { + GUInt16 nTmp = (GUInt16) dfNoData; + memcpy(abyTmp, &nTmp, sizeof(nTmp)); + break; + } + + case EPT_s16: + { + GInt16 nTmp = (GInt16) dfNoData; + memcpy(abyTmp, &nTmp, sizeof(nTmp)); + break; + } + + case EPT_u32: + { + GUInt32 nTmp = (GUInt32) dfNoData; + memcpy(abyTmp, &nTmp, sizeof(nTmp)); + break; + } + + case EPT_s32: + { + GInt32 nTmp = (GInt32) dfNoData; + memcpy(abyTmp, &nTmp, sizeof(nTmp)); + break; + } + + case EPT_f32: + { + float fTmp = (float) dfNoData; + memcpy(abyTmp, &fTmp, sizeof(fTmp)); + break; + } + + case EPT_f64: + { + memcpy(abyTmp, &dfNoData, sizeof(dfNoData)); + break; + } + + case EPT_c64: + { + float fTmp = (float) dfNoData; + memcpy(abyTmp, &fTmp, sizeof(fTmp)); + memset(abyTmp+4, 0, sizeof(float)); + break; + } + + case EPT_c128: + { + memcpy(abyTmp, &dfNoData, sizeof(dfNoData)); + memset(abyTmp+8, 0, sizeof(double)); + break; + } + } + + for( i = 0; i < nWords; i++ ) + memcpy( ((GByte *) pData) + nChunkSize * i, + abyTmp, nChunkSize ); + } + +} + +/************************************************************************/ +/* GetRasterBlock() */ +/************************************************************************/ + +CPLErr HFABand::GetRasterBlock( int nXBlock, int nYBlock, void * pData, int nDataSize ) + +{ + int iBlock; + VSILFILE *fpData; + + if( LoadBlockInfo() != CE_None ) + return CE_Failure; + + iBlock = nXBlock + nYBlock * nBlocksPerRow; + +/* -------------------------------------------------------------------- */ +/* If the block isn't valid, we just return all zeros, and an */ +/* indication of success. */ +/* -------------------------------------------------------------------- */ + if( (panBlockFlag[iBlock] & BFLG_VALID) == 0 ) + { + NullBlock( pData ); + return( CE_None ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise we really read the data. */ +/* -------------------------------------------------------------------- */ + vsi_l_offset nBlockOffset; + + // Calculate block offset in case we have spill file. Use predefined + // block map otherwise. + if ( fpExternal ) + { + fpData = fpExternal; + nBlockOffset = nBlockStart + nBlockSize * iBlock * nLayerStackCount + + nLayerStackIndex * nBlockSize; + } + else + { + fpData = psInfo->fp; + nBlockOffset = panBlockStart[iBlock]; + nBlockSize = panBlockSize[iBlock]; + } + + if( VSIFSeekL( fpData, nBlockOffset, SEEK_SET ) != 0 ) + { + // XXX: We will not report error here, because file just may be + // in update state and data for this block will be available later + if ( psInfo->eAccess == HFA_Update ) + { + memset( pData, 0, + HFAGetDataTypeBits(nDataType)*nBlockXSize*nBlockYSize/8 ); + return CE_None; + } + else + { + CPLError( CE_Failure, CPLE_FileIO, + "Seek to %x:%08x on %p failed\n%s", + (int) (nBlockOffset >> 32), + (int) (nBlockOffset & 0xffffffff), + fpData, VSIStrerror(errno) ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* If the block is compressed, read into an intermediate buffer */ +/* and convert. */ +/* -------------------------------------------------------------------- */ + if( panBlockFlag[iBlock] & BFLG_COMPRESSED ) + { + GByte *pabyCData; + CPLErr eErr; + + pabyCData = (GByte *) VSIMalloc( (size_t) nBlockSize ); + if (pabyCData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "HFABand::GetRasterBlock : Out of memory\n"); + return CE_Failure; + } + + if( VSIFReadL( pabyCData, (size_t) nBlockSize, 1, fpData ) != 1 ) + { + CPLFree( pabyCData ); + + // XXX: Suppose that file in update state + if ( psInfo->eAccess == HFA_Update ) + { + memset( pData, 0, + HFAGetDataTypeBits(nDataType)*nBlockXSize*nBlockYSize/8 ); + return CE_None; + } + else + { + CPLError( CE_Failure, CPLE_FileIO, + "Read of %d bytes at %x:%08x on %p failed.\n%s", + (int) nBlockSize, + (int) (nBlockOffset >> 32), + (int) (nBlockOffset & 0xffffffff), + fpData, VSIStrerror(errno) ); + return CE_Failure; + } + } + + eErr = UncompressBlock( pabyCData, (int) nBlockSize, + (GByte *) pData, nBlockXSize*nBlockYSize, + nDataType ); + + CPLFree( pabyCData ); + + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Read uncompressed data directly into the return buffer. */ +/* -------------------------------------------------------------------- */ + if ( nDataSize != -1 && (nBlockSize > INT_MAX || + (int)nBlockSize > nDataSize) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid block size : %d", (int)nBlockSize); + return CE_Failure; + } + + if( VSIFReadL( pData, (size_t) nBlockSize, 1, fpData ) != 1 ) + { + memset( pData, 0, + HFAGetDataTypeBits(nDataType)*nBlockXSize*nBlockYSize/8 ); + + if( fpData != fpExternal ) + CPLDebug( "HFABand", + "Read of %x:%08x bytes at %d on %p failed.\n%s", + (int) nBlockSize, + (int) (nBlockOffset >> 32), + (int) (nBlockOffset & 0xffffffff), + fpData, VSIStrerror(errno) ); + + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Byte swap to local byte order if required. It appears that */ +/* raster data is always stored in Intel byte order in Imagine */ +/* files. */ +/* -------------------------------------------------------------------- */ + +#ifdef CPL_MSB + if( HFAGetDataTypeBits(nDataType) == 16 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ ) + CPL_SWAP16PTR( ((unsigned char *) pData) + ii*2 ); + } + else if( HFAGetDataTypeBits(nDataType) == 32 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ ) + CPL_SWAP32PTR( ((unsigned char *) pData) + ii*4 ); + } + else if( nDataType == EPT_f64 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ ) + CPL_SWAP64PTR( ((unsigned char *) pData) + ii*8 ); + } + else if( nDataType == EPT_c64 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize*2; ii++ ) + CPL_SWAP32PTR( ((unsigned char *) pData) + ii*4 ); + } + else if( nDataType == EPT_c128 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize*2; ii++ ) + CPL_SWAP64PTR( ((unsigned char *) pData) + ii*8 ); + } +#endif /* def CPL_MSB */ + + return( CE_None ); +} + +/************************************************************************/ +/* ReAllocBlock() */ +/************************************************************************/ + +void HFABand::ReAllocBlock( int iBlock, int nSize ) +{ + /* For compressed files - need to realloc the space for the block */ + + // TODO: Should check to see if panBlockStart[iBlock] is not zero then do a HFAFreeSpace() + // but that doesn't exist yet. + // Instead as in interim measure it will reuse the existing block if + // the new data will fit in. + if( ( panBlockStart[iBlock] != 0 ) && ( nSize <= panBlockSize[iBlock] ) ) + { + panBlockSize[iBlock] = nSize; + //fprintf( stderr, "Reusing block %d\n", iBlock ); + } + else + { + panBlockStart[iBlock] = HFAAllocateSpace( psInfo, nSize ); + + panBlockSize[iBlock] = nSize; + + // need to re - write this info to the RasterDMS node + HFAEntry *poDMS = poNode->GetNamedChild( "RasterDMS" ); + + char szVarName[64]; + sprintf( szVarName, "blockinfo[%d].offset", iBlock ); + poDMS->SetIntField( szVarName, (int) panBlockStart[iBlock] ); + + sprintf( szVarName, "blockinfo[%d].size", iBlock ); + poDMS->SetIntField( szVarName, panBlockSize[iBlock] ); + } + +} + + +/************************************************************************/ +/* SetRasterBlock() */ +/************************************************************************/ + +CPLErr HFABand::SetRasterBlock( int nXBlock, int nYBlock, void * pData ) + +{ + int iBlock; + VSILFILE *fpData; + + if( psInfo->eAccess == HFA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Attempt to write block to read-only HFA file failed." ); + return CE_Failure; + } + + if( LoadBlockInfo() != CE_None ) + return CE_Failure; + + iBlock = nXBlock + nYBlock * nBlocksPerRow; + +/* -------------------------------------------------------------------- */ +/* For now we don't support write invalid uncompressed blocks. */ +/* To do so we will need logic to make space at the end of the */ +/* file in the right size. */ +/* -------------------------------------------------------------------- */ + if( (panBlockFlag[iBlock] & BFLG_VALID) == 0 + && !(panBlockFlag[iBlock] & BFLG_COMPRESSED) + && panBlockStart[iBlock] == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to write to invalid tile with number %d " + "(X position %d, Y position %d). This\n operation currently " + "unsupported by HFABand::SetRasterBlock().\n", + iBlock, nXBlock, nYBlock ); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Move to the location that the data sits. */ +/* -------------------------------------------------------------------- */ + vsi_l_offset nBlockOffset; + + // Calculate block offset in case we have spill file. Use predefined + // block map otherwise. + if ( fpExternal ) + { + fpData = fpExternal; + nBlockOffset = nBlockStart + nBlockSize * iBlock * nLayerStackCount + + nLayerStackIndex * nBlockSize; + } + else + { + fpData = psInfo->fp; + nBlockOffset = panBlockStart[iBlock]; + nBlockSize = panBlockSize[iBlock]; + } + +/* ==================================================================== */ +/* Compressed Tile Handling. */ +/* ==================================================================== */ + if( panBlockFlag[iBlock] & BFLG_COMPRESSED ) + { + /* ------------------------------------------------------------ */ + /* Write compressed data. */ + /* ------------------------------------------------------------ */ + int nInBlockSize = (nBlockXSize * nBlockYSize * HFAGetDataTypeBits(nDataType) + 7 ) / 8; + + /* create the compressor object */ + HFACompress compress( pData, nInBlockSize, nDataType ); + if( compress.getCounts() == NULL || + compress.getValues() == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + return CE_Failure; + } + + /* compress the data */ + if( compress.compressBlock() ) + { + /* get the data out of the object */ + GByte *pCounts = compress.getCounts(); + GUInt32 nSizeCount = compress.getCountSize(); + GByte *pValues = compress.getValues(); + GUInt32 nSizeValues = compress.getValueSize(); + GUInt32 nMin = compress.getMin(); + GUInt32 nNumRuns = compress.getNumRuns(); + GByte nNumBits = compress.getNumBits(); + + /* Compensate for the header info */ + GUInt32 nDataOffset = nSizeCount + 13; + int nTotalSize = nSizeCount + nSizeValues + 13; + + //fprintf( stderr, "sizecount = %d sizevalues = %d min = %d numruns = %d numbits = %d\n", nSizeCount, nSizeValues, nMin, nNumRuns, (int)nNumBits ); + + // Allocate space for the compressed block and seek to it. + ReAllocBlock( iBlock, nTotalSize ); + + nBlockOffset = panBlockStart[iBlock]; + nBlockSize = panBlockSize[iBlock]; + + // Seek to offset + if( VSIFSeekL( fpData, nBlockOffset, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, "Seek to %x:%08x on %p failed\n%s", + (int) (nBlockOffset >> 32), + (int) (nBlockOffset & 0xffffffff), + fpData, VSIStrerror(errno) ); + return CE_Failure; + } + + /* -------------------------------------------------------------------- */ + /* Byte swap to local byte order if required. It appears that */ + /* raster data is always stored in Intel byte order in Imagine */ + /* files. */ + /* -------------------------------------------------------------------- */ + +#ifdef CPL_MSB + + CPL_SWAP32PTR( &nMin ); + CPL_SWAP32PTR( &nNumRuns ); + CPL_SWAP32PTR( &nDataOffset ); + +#endif /* def CPL_MSB */ + + /* Write out the Minimum value */ + VSIFWriteL( &nMin, (size_t) sizeof( nMin ), 1, fpData ); + + /* the number of runs */ + VSIFWriteL( &nNumRuns, (size_t) sizeof( nNumRuns ), 1, fpData ); + + /* The offset to the data */ + VSIFWriteL( &nDataOffset, (size_t) sizeof( nDataOffset ), 1, fpData ); + + /* The number of bits */ + VSIFWriteL( &nNumBits, (size_t) sizeof( nNumBits ), 1, fpData ); + + /* The counters - MSB stuff handled in HFACompress */ + VSIFWriteL( pCounts, (size_t) sizeof( GByte ), nSizeCount, fpData ); + + /* The values - MSB stuff handled in HFACompress */ + VSIFWriteL( pValues, (size_t) sizeof( GByte ), nSizeValues, fpData ); + + /* Compressed data is freed in the HFACompress destructor */ + } + else + { + /* If we have actually made the block bigger - ie does not compress well */ + panBlockFlag[iBlock] ^= BFLG_COMPRESSED; + // alloc more space for the uncompressed block + ReAllocBlock( iBlock, nInBlockSize ); + + nBlockOffset = panBlockStart[iBlock]; + nBlockSize = panBlockSize[iBlock]; + + /* Need to change the RasterDMS entry */ + HFAEntry *poDMS = poNode->GetNamedChild( "RasterDMS" ); + + char szVarName[64]; + sprintf( szVarName, "blockinfo[%d].compressionType", iBlock ); + poDMS->SetIntField( szVarName, 0 ); + } + +/* -------------------------------------------------------------------- */ +/* If the block was previously invalid, mark it as valid now. */ +/* -------------------------------------------------------------------- */ + if( (panBlockFlag[iBlock] & BFLG_VALID) == 0 ) + { + char szVarName[64]; + HFAEntry *poDMS = poNode->GetNamedChild( "RasterDMS" ); + + sprintf( szVarName, "blockinfo[%d].logvalid", iBlock ); + poDMS->SetStringField( szVarName, "true" ); + + panBlockFlag[iBlock] |= BFLG_VALID; + } + } + +/* ==================================================================== */ +/* Uncompressed TILE handling. */ +/* ==================================================================== */ + if( ( panBlockFlag[iBlock] & BFLG_COMPRESSED ) == 0 ) + { + + if( VSIFSeekL( fpData, nBlockOffset, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, "Seek to %x:%08x on %p failed\n%s", + (int) (nBlockOffset >> 32), + (int) (nBlockOffset & 0xffffffff), + fpData, VSIStrerror(errno) ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Byte swap to local byte order if required. It appears that */ +/* raster data is always stored in Intel byte order in Imagine */ +/* files. */ +/* -------------------------------------------------------------------- */ + +#ifdef CPL_MSB + if( HFAGetDataTypeBits(nDataType) == 16 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ ) + CPL_SWAP16PTR( ((unsigned char *) pData) + ii*2 ); + } + else if( HFAGetDataTypeBits(nDataType) == 32 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ ) + CPL_SWAP32PTR( ((unsigned char *) pData) + ii*4 ); + } + else if( nDataType == EPT_f64 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ ) + CPL_SWAP64PTR( ((unsigned char *) pData) + ii*8 ); + } + else if( nDataType == EPT_c64 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize*2; ii++ ) + CPL_SWAP32PTR( ((unsigned char *) pData) + ii*4 ); + } + else if( nDataType == EPT_c128 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize*2; ii++ ) + CPL_SWAP64PTR( ((unsigned char *) pData) + ii*8 ); + } +#endif /* def CPL_MSB */ + +/* -------------------------------------------------------------------- */ +/* Write uncompressed data. */ +/* -------------------------------------------------------------------- */ + if( VSIFWriteL( pData, (size_t) nBlockSize, 1, fpData ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Write of %d bytes at %x:%08x on %p failed.\n%s", + (int) nBlockSize, + (int) (nBlockOffset >> 32), + (int) (nBlockOffset & 0xffffffff), + fpData, VSIStrerror(errno) ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* If the block was previously invalid, mark it as valid now. */ +/* -------------------------------------------------------------------- */ + if( (panBlockFlag[iBlock] & BFLG_VALID) == 0 ) + { + char szVarName[64]; + HFAEntry *poDMS = poNode->GetNamedChild( "RasterDMS" ); + + sprintf( szVarName, "blockinfo[%d].logvalid", iBlock ); + poDMS->SetStringField( szVarName, "true" ); + + panBlockFlag[iBlock] |= BFLG_VALID; + } + } +/* -------------------------------------------------------------------- */ +/* Swap back, since we don't really have permission to change */ +/* the callers buffer. */ +/* -------------------------------------------------------------------- */ + +#ifdef CPL_MSB + if( HFAGetDataTypeBits(nDataType) == 16 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ ) + CPL_SWAP16PTR( ((unsigned char *) pData) + ii*2 ); + } + else if( HFAGetDataTypeBits(nDataType) == 32 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ ) + CPL_SWAP32PTR( ((unsigned char *) pData) + ii*4 ); + } + else if( nDataType == EPT_f64 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ ) + CPL_SWAP64PTR( ((unsigned char *) pData) + ii*8 ); + } + else if( nDataType == EPT_c64 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize*2; ii++ ) + CPL_SWAP32PTR( ((unsigned char *) pData) + ii*4 ); + } + else if( nDataType == EPT_c128 ) + { + for( int ii = 0; ii < nBlockXSize*nBlockYSize*2; ii++ ) + CPL_SWAP64PTR( ((unsigned char *) pData) + ii*8 ); + } +#endif /* def CPL_MSB */ + + return( CE_None ); +} + +/************************************************************************/ +/* GetBandName() */ +/* */ +/* Return the Layer Name */ +/************************************************************************/ + +const char * HFABand::GetBandName() +{ + if( strlen(poNode->GetName()) > 0 ) + return( poNode->GetName() ); + else + { + int iBand; + for( iBand = 0; iBand < psInfo->nBands; iBand++ ) + { + if( psInfo->papoBand[iBand] == this ) + { + osOverName.Printf( "Layer_%d", iBand+1 ); + return osOverName; + } + } + + osOverName.Printf( "Layer_%x", poNode->GetFilePos() ); + return osOverName; + } +} + +/************************************************************************/ +/* SetBandName() */ +/* */ +/* Set the Layer Name */ +/************************************************************************/ + +void HFABand::SetBandName(const char *pszName) +{ + if( psInfo->eAccess == HFA_Update ) + { + poNode->SetName(pszName); + } +} + +/************************************************************************/ +/* SetNoDataValue() */ +/* */ +/* Set the band no-data value */ +/************************************************************************/ + +CPLErr HFABand::SetNoDataValue( double dfValue ) +{ + CPLErr eErr = CE_Failure; + + if ( psInfo->eAccess == HFA_Update ) + { + HFAEntry *poNDNode = poNode->GetNamedChild( "Eimg_NonInitializedValue" ); + + if ( poNDNode == NULL ) + { + poNDNode = new HFAEntry( psInfo, + "Eimg_NonInitializedValue", + "Eimg_NonInitializedValue", + poNode ); + } + + poNDNode->MakeData( 8 + 12 + 8 ); + poNDNode->SetPosition(); + + poNDNode->SetIntField( "valueBD[-3]", EPT_f64 ); + poNDNode->SetIntField( "valueBD[-2]", 1 ); + poNDNode->SetIntField( "valueBD[-1]", 1 ); + if ( poNDNode->SetDoubleField( "valueBD[0]", dfValue) != CE_Failure ) + { + bNoDataSet = TRUE; + dfNoData = dfValue; + eErr = CE_None; + } + } + + return eErr; +} + +/************************************************************************/ +/* HFAReadBFUniqueBins() */ +/* */ +/* Attempt to read the bins used for a PCT or RAT from a */ +/* BinFunction node. On failure just return NULL. */ +/************************************************************************/ + +double *HFAReadBFUniqueBins( HFAEntry *poBinFunc, int nPCTColors ) + +{ +/* -------------------------------------------------------------------- */ +/* First confirm this is a "BFUnique" bin function. We don't */ +/* know what to do with any other types. */ +/* -------------------------------------------------------------------- */ + const char *pszBinFunctionType = + poBinFunc->GetStringField( "binFunction.type.string" ); + + if( pszBinFunctionType == NULL + || !EQUAL(pszBinFunctionType,"BFUnique") ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Process dictionary. */ +/* -------------------------------------------------------------------- */ + const char *pszDict = + poBinFunc->GetStringField( "binFunction.MIFDictionary.string" ); + if( pszDict == NULL ) + poBinFunc->GetStringField( "binFunction.MIFDictionary" ); + + HFADictionary oMiniDict( pszDict ); + + HFAType *poBFUnique = oMiniDict.FindType( "BFUnique" ); + if( poBFUnique == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Field the MIFObject raw data pointer. */ +/* -------------------------------------------------------------------- */ + const GByte *pabyMIFObject = (const GByte *) + poBinFunc->GetStringField("binFunction.MIFObject"); + + if( pabyMIFObject == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm that this is a 64bit floating point basearray. */ +/* -------------------------------------------------------------------- */ + if( pabyMIFObject[20] != 0x0a || pabyMIFObject[21] != 0x00 ) + { + CPLDebug( "HFA", "HFAReadPCTBins(): The basedata does not appear to be EGDA_TYPE_F64." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Decode bins. */ +/* -------------------------------------------------------------------- */ + double *padfBins = (double *) CPLCalloc(sizeof(double),nPCTColors); + int i; + + memcpy( padfBins, pabyMIFObject + 24, sizeof(double) * nPCTColors ); + + for( i = 0; i < nPCTColors; i++ ) + { + HFAStandard( 8, padfBins + i ); +// CPLDebug( "HFA", "Bin[%d] = %g", i, padfBins[i] ); + } + + return padfBins; +} + +/************************************************************************/ +/* GetPCT() */ +/* */ +/* Return PCT information, if any exists. */ +/************************************************************************/ + +CPLErr HFABand::GetPCT( int * pnColors, + double **ppadfRed, + double **ppadfGreen, + double **ppadfBlue, + double **ppadfAlpha, + double **ppadfBins ) + +{ + *pnColors = 0; + *ppadfRed = NULL; + *ppadfGreen = NULL; + *ppadfBlue = NULL; + *ppadfAlpha = NULL; + *ppadfBins = NULL; + +/* -------------------------------------------------------------------- */ +/* If we haven't already tried to load the colors, do so now. */ +/* -------------------------------------------------------------------- */ + if( nPCTColors == -1 ) + { + HFAEntry *poColumnEntry; + int i, iColumn; + + nPCTColors = 0; + + poColumnEntry = poNode->GetNamedChild("Descriptor_Table.Red"); + if( poColumnEntry == NULL ) + return( CE_Failure ); + + /* FIXME? : we could also check that nPCTColors is not too big */ + nPCTColors = poColumnEntry->GetIntField( "numRows" ); + for( iColumn = 0; iColumn < 4; iColumn++ ) + { + apadfPCT[iColumn] = (double *)VSIMalloc2(sizeof(double),nPCTColors); + if (apadfPCT[iColumn] == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Color palette will be ignored"); + return CE_Failure; + } + + if( iColumn == 0 ) + poColumnEntry = poNode->GetNamedChild("Descriptor_Table.Red"); + else if( iColumn == 1 ) + poColumnEntry= poNode->GetNamedChild("Descriptor_Table.Green"); + else if( iColumn == 2 ) + poColumnEntry = poNode->GetNamedChild("Descriptor_Table.Blue"); + else if( iColumn == 3 ) { + poColumnEntry = poNode->GetNamedChild("Descriptor_Table.Opacity"); + } + + if( poColumnEntry == NULL ) + { + double *pdCol = apadfPCT[iColumn]; + for( i = 0; i < nPCTColors; i++ ) + pdCol[i] = 1.0; + } + else + { + if (VSIFSeekL( psInfo->fp, poColumnEntry->GetIntField("columnDataPtr"), + SEEK_SET ) < 0) + { + CPLError( CE_Failure, CPLE_FileIO, + "VSIFSeekL() failed in HFABand::GetPCT()." ); + return CE_Failure; + } + if (VSIFReadL( apadfPCT[iColumn], sizeof(double), nPCTColors, + psInfo->fp) != (size_t)nPCTColors) + { + CPLError( CE_Failure, CPLE_FileIO, + "VSIFReadL() failed in HFABand::GetPCT()." ); + return CE_Failure; + } + + for( i = 0; i < nPCTColors; i++ ) + HFAStandard( 8, apadfPCT[iColumn] + i ); + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have a custom binning function? If so, try reading it. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poBinFunc = + poNode->GetNamedChild("Descriptor_Table.#Bin_Function840#"); + + if( poBinFunc != NULL ) + { + padfPCTBins = HFAReadBFUniqueBins( poBinFunc, nPCTColors ); + } + } + +/* -------------------------------------------------------------------- */ +/* Return the values. */ +/* -------------------------------------------------------------------- */ + if( nPCTColors == 0 ) + return( CE_Failure ); + + *pnColors = nPCTColors; + *ppadfRed = apadfPCT[0]; + *ppadfGreen = apadfPCT[1]; + *ppadfBlue = apadfPCT[2]; + *ppadfAlpha = apadfPCT[3]; + *ppadfBins = padfPCTBins; + + return( CE_None ); +} + +/************************************************************************/ +/* SetPCT() */ +/* */ +/* Set the PCT information for this band. */ +/************************************************************************/ + +CPLErr HFABand::SetPCT( int nColors, + double *padfRed, + double *padfGreen, + double *padfBlue , + double *padfAlpha) + +{ + static const char *apszColNames[4] = {"Red", "Green", "Blue", "Opacity"}; + HFAEntry *poEdsc_Table; + int iColumn; + +/* -------------------------------------------------------------------- */ +/* Do we need to try and clear any existing color table? */ +/* -------------------------------------------------------------------- */ + if( nColors == 0 ) + { + poEdsc_Table = poNode->GetNamedChild( "Descriptor_Table" ); + if( poEdsc_Table == NULL ) + return CE_None; + + for( iColumn = 0; iColumn < 4; iColumn++ ) + { + HFAEntry *poEdsc_Column; + + poEdsc_Column = poEdsc_Table->GetNamedChild(apszColNames[iColumn]); + if( poEdsc_Column ) + poEdsc_Column->RemoveAndDestroy(); + } + + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Create the Descriptor table. */ +/* -------------------------------------------------------------------- */ + poEdsc_Table = poNode->GetNamedChild( "Descriptor_Table" ); + if( poEdsc_Table == NULL + || !EQUAL(poEdsc_Table->GetType(),"Edsc_Table") ) + poEdsc_Table = new HFAEntry( psInfo, "Descriptor_Table", + "Edsc_Table", poNode ); + + poEdsc_Table->SetIntField( "numrows", nColors ); + +/* -------------------------------------------------------------------- */ +/* Create the Binning function node. I am not sure that we */ +/* really need this though. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poEdsc_BinFunction; + + poEdsc_BinFunction = poEdsc_Table->GetNamedChild( "#Bin_Function#" ); + if( poEdsc_BinFunction == NULL + || !EQUAL(poEdsc_BinFunction->GetType(),"Edsc_BinFunction") ) + poEdsc_BinFunction = new HFAEntry( psInfo, "#Bin_Function#", + "Edsc_BinFunction", + poEdsc_Table ); + + // Because of the BaseData we have to hardcode the size. + poEdsc_BinFunction->MakeData( 30 ); + + poEdsc_BinFunction->SetIntField( "numBins", nColors ); + poEdsc_BinFunction->SetStringField( "binFunction", "direct" ); + poEdsc_BinFunction->SetDoubleField( "minLimit", 0.0 ); + poEdsc_BinFunction->SetDoubleField( "maxLimit", nColors - 1.0 ); + +/* -------------------------------------------------------------------- */ +/* Process each color component */ +/* -------------------------------------------------------------------- */ + for( iColumn = 0; iColumn < 4; iColumn++ ) + { + HFAEntry *poEdsc_Column; + double *padfValues=NULL; + const char *pszName = apszColNames[iColumn]; + + if( iColumn == 0 ) + padfValues = padfRed; + else if( iColumn == 1 ) + padfValues = padfGreen; + else if( iColumn == 2 ) + padfValues = padfBlue; + else if( iColumn == 3 ) + padfValues = padfAlpha; + +/* -------------------------------------------------------------------- */ +/* Create the Edsc_Column. */ +/* -------------------------------------------------------------------- */ + poEdsc_Column = poEdsc_Table->GetNamedChild( pszName ); + if( poEdsc_Column == NULL + || !EQUAL(poEdsc_Column->GetType(),"Edsc_Column") ) + poEdsc_Column = new HFAEntry( psInfo, pszName, "Edsc_Column", + poEdsc_Table ); + + poEdsc_Column->SetIntField( "numRows", nColors ); + poEdsc_Column->SetStringField( "dataType", "real" ); + poEdsc_Column->SetIntField( "maxNumChars", 0 ); + +/* -------------------------------------------------------------------- */ +/* Write the data out. */ +/* -------------------------------------------------------------------- */ + int nOffset = HFAAllocateSpace( psInfo, 8*nColors); + double *padfFileData; + + poEdsc_Column->SetIntField( "columnDataPtr", nOffset ); + + padfFileData = (double *) CPLMalloc(nColors*sizeof(double)); + for( int iColor = 0; iColor < nColors; iColor++ ) + { + padfFileData[iColor] = padfValues[iColor]; + HFAStandard( 8, padfFileData + iColor ); + } + VSIFSeekL( psInfo->fp, nOffset, SEEK_SET ); + VSIFWriteL( padfFileData, 8, nColors, psInfo->fp ); + CPLFree( padfFileData ); + } + +/* -------------------------------------------------------------------- */ +/* Update the layer type to be thematic. */ +/* -------------------------------------------------------------------- */ + poNode->SetStringField( "layerType", "thematic" ); + + return( CE_None ); +} + +/************************************************************************/ +/* CreateOverview() */ +/************************************************************************/ + +int HFABand::CreateOverview( int nOverviewLevel, const char *pszResampling ) + +{ + + CPLString osLayerName; + int nOXSize, nOYSize; + + nOXSize = (psInfo->nXSize + nOverviewLevel - 1) / nOverviewLevel; + nOYSize = (psInfo->nYSize + nOverviewLevel - 1) / nOverviewLevel; + +/* -------------------------------------------------------------------- */ +/* Do we want to use a dependent file (.rrd) for the overviews? */ +/* Or just create them directly in this file? */ +/* -------------------------------------------------------------------- */ + HFAInfo_t *psRRDInfo = psInfo; + HFAEntry *poParent = poNode; + + if( CSLTestBoolean( CPLGetConfigOption( "HFA_USE_RRD", "NO" ) ) ) + { + psRRDInfo = HFACreateDependent( psInfo ); + + poParent = psRRDInfo->poRoot->GetNamedChild( GetBandName() ); + + // Need to create layer object. + if( poParent == NULL ) + { + poParent = + new HFAEntry( psRRDInfo, GetBandName(), + "Eimg_Layer", psRRDInfo->poRoot ); + } + } + +/* -------------------------------------------------------------------- */ +/* What pixel type should we use for the overview. Usually */ +/* this is the same as the base layer, but when */ +/* AVERAGE_BIT2GRAYSCALE is in effect we force it to u8 from u1. */ +/* -------------------------------------------------------------------- */ + int nOverviewDataType = nDataType; + + if( EQUALN(pszResampling,"AVERAGE_BIT2GR",14) ) + nOverviewDataType = EPT_u8; + +/* -------------------------------------------------------------------- */ +/* Eventually we need to decide on the whether to use the spill */ +/* file, primarily on the basis of whether the new overview */ +/* will drive our .img file size near 4GB. For now, just base */ +/* it on the config options. */ +/* -------------------------------------------------------------------- */ + int bCreateLargeRaster = CSLTestBoolean( + CPLGetConfigOption("USE_SPILL","NO") ); + GIntBig nValidFlagsOffset = 0, nDataOffset = 0; + + if( (psRRDInfo->nEndOfFile + + (nOXSize * (double) nOYSize) + * (HFAGetDataTypeBits(nOverviewDataType) / 8)) > 2000000000.0 ) + bCreateLargeRaster = TRUE; + + if( bCreateLargeRaster ) + { + if( !HFACreateSpillStack( psRRDInfo, nOXSize, nOYSize, 1, + 64, nOverviewDataType, + &nValidFlagsOffset, &nDataOffset ) ) + { + return -1; + } + } + +/* -------------------------------------------------------------------- */ +/* Are we compressed? If so, overview should be too (unless */ +/* HFA_COMPRESS_OVR is defined). */ +/* Check RasterDMS like HFAGetBandInfo */ +/* -------------------------------------------------------------------- */ + int bCompressionType = FALSE; + const char* pszCompressOvr = CPLGetConfigOption("HFA_COMPRESS_OVR", NULL); + if( pszCompressOvr != NULL ) + bCompressionType = CSLTestBoolean(pszCompressOvr); + else + { + HFAEntry *poDMS = poNode->GetNamedChild( "RasterDMS" ); + + if( poDMS != NULL ) + bCompressionType = poDMS->GetIntField( "compressionType" ) != 0; + } + +/* -------------------------------------------------------------------- */ +/* Create the layer. */ +/* -------------------------------------------------------------------- */ + osLayerName.Printf( "_ss_%d_", nOverviewLevel ); + + if( !HFACreateLayer( psRRDInfo, poParent, osLayerName, + TRUE, 64, bCompressionType, bCreateLargeRaster, FALSE, + nOXSize, nOYSize, nOverviewDataType, NULL, + nValidFlagsOffset, nDataOffset, 1, 0 ) ) + return -1; + + HFAEntry *poOverLayer = poParent->GetNamedChild( osLayerName ); + if( poOverLayer == NULL ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Create RRDNamesList list if it does not yet exist. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poRRDNamesList = poNode->GetNamedChild("RRDNamesList"); + if( poRRDNamesList == NULL ) + { + poRRDNamesList = new HFAEntry( psInfo, "RRDNamesList", + "Eimg_RRDNamesList", + poNode ); + poRRDNamesList->MakeData( 23+16+8+ 3000 /* hack for growth room*/ ); + + /* we need to hardcode file offset into the data, so locate it now */ + poRRDNamesList->SetPosition(); + + poRRDNamesList->SetStringField( "algorithm.string", + "IMAGINE 2X2 Resampling" ); + } + +/* -------------------------------------------------------------------- */ +/* Add new overview layer to RRDNamesList. */ +/* -------------------------------------------------------------------- */ + int iNextName = poRRDNamesList->GetFieldCount( "nameList" ); + char szName[50]; + CPLString osNodeName; + + sprintf( szName, "nameList[%d].string", iNextName ); + + osLayerName.Printf( "%s(:%s:_ss_%d_)", + psRRDInfo->pszFilename, GetBandName(), + nOverviewLevel ); + + // TODO: Need to add to end of array (thats pretty hard). + if( poRRDNamesList->SetStringField( szName, osLayerName ) != CE_None ) + { + poRRDNamesList->MakeData( poRRDNamesList->GetDataSize() + 3000 ); + if( poRRDNamesList->SetStringField( szName, osLayerName ) != CE_None ) + return -1; + } + +/* -------------------------------------------------------------------- */ +/* Add to the list of overviews for this band. */ +/* -------------------------------------------------------------------- */ + papoOverviews = (HFABand **) + CPLRealloc(papoOverviews, sizeof(void*) * ++nOverviews ); + papoOverviews[nOverviews-1] = new HFABand( psRRDInfo, poOverLayer ); + +/* -------------------------------------------------------------------- */ +/* If there is a nodata value, copy it to the overview band. */ +/* -------------------------------------------------------------------- */ + if( bNoDataSet ) + papoOverviews[nOverviews-1]->SetNoDataValue( dfNoData ); + + return nOverviews-1; +} diff --git a/bazaar/plugin/gdal/frmts/hfa/hfacompress.cpp b/bazaar/plugin/gdal/frmts/hfa/hfacompress.cpp new file mode 100644 index 000000000..3fcff7620 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/hfacompress.cpp @@ -0,0 +1,302 @@ +/****************************************************************************** + * $Id: hfacompress.cpp 23624 2011-12-21 19:31:43Z rouault $ + * + * Name: hfadataset.cpp + * Project: Erdas Imagine Driver + * Purpose: Imagine Compression code. + * Author: Sam Gillingham + * + ****************************************************************************** + * Copyright (c) 2005, Sam Gillingham + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "hfa_p.h" + +CPL_CVSID("$Id: hfacompress.cpp 23624 2011-12-21 19:31:43Z rouault $"); + +HFACompress::HFACompress( void *pData, GUInt32 nBlockSize, int nDataType ) +{ + m_pData = pData; + m_nDataType = nDataType; + m_nDataTypeNumBits = HFAGetDataTypeBits( m_nDataType ); + m_nBlockSize = nBlockSize; + m_nBlockCount = (nBlockSize * 8) / m_nDataTypeNumBits; + + /* Allocate some memory for the count and values - probably too big */ + /* About right for worst case scenario tho */ + m_pCounts = (GByte*)VSIMalloc( m_nBlockCount * sizeof(GUInt32) + sizeof(GUInt32) ); + m_nSizeCounts = 0; + + m_pValues = (GByte*)VSIMalloc( m_nBlockCount * sizeof(GUInt32) + sizeof(GUInt32) ); + m_nSizeValues = 0; + + m_nMin = 0; + m_nNumRuns = 0; + m_nNumBits = 0; +} + +HFACompress::~HFACompress() +{ + /* free the compressed data */ + CPLFree( m_pCounts ); + CPLFree( m_pValues ); +} + +/* returns the number of bits needed to encode a count */ +GByte _FindNumBits( GUInt32 range ) +{ + if( range < 0xff ) + { + return 8; + } + else if( range < 0xffff ) + { + return 16; + } + else + { + return 32; + } +} + +/* Gets the value from the uncompressed block as a GUInt32 no matter the data type */ +GUInt32 HFACompress::valueAsUInt32( GUInt32 iPixel ) +{ + GUInt32 val = 0; + + if( m_nDataTypeNumBits == 8 ) + { + val = ((GByte*)m_pData)[iPixel]; + } + else if( m_nDataTypeNumBits == 16 ) + { + val = ((GUInt16*)m_pData)[iPixel]; + } + else if( m_nDataTypeNumBits == 32 ) + { + val = ((GUInt32*)m_pData)[iPixel]; + } + else if( m_nDataTypeNumBits == 4 ) + { + if( iPixel % 2 == 0 ) + val = ((GByte*)m_pData)[iPixel/2] & 0x0f; + else + val = (((GByte*)m_pData)[iPixel/2] & 0xf0) >> 4; + } + else if( m_nDataTypeNumBits == 2 ) + { + if( iPixel % 4 == 0 ) + val = ((GByte*)m_pData)[iPixel/4] & 0x03; + else if( iPixel % 4 == 1 ) + val = (((GByte*)m_pData)[iPixel/4] & 0x0c) >> 2; + else if( iPixel % 4 == 2 ) + val = (((GByte*)m_pData)[iPixel/4] & 0x30) >> 4; + else + val = (((GByte*)m_pData)[iPixel/4] & 0xc0) >> 6; + } + else if( m_nDataTypeNumBits == 1 ) + { + if( ((GByte*)m_pData)[iPixel >> 3] & (0x1 << (iPixel & 0x07)) ) + val = 1; + else + val = 0; + } + else + { + /* Should not get to here - check in compressBlock() should return false if + we can't compress this blcok because we don't know about the type */ + CPLError( CE_Failure, CPLE_FileIO, "Imagine Datatype 0x%x (0x%x bits) not supported\n", + m_nDataType, + m_nDataTypeNumBits ); + CPLAssert( FALSE ); + } + + return val; +} + +/* Finds the minimum value in a type specific fashion. This value is + subtracted from each value in the compressed dataset. The maxmimum + value is also found and the number of bits that the range can be stored + is also returned. */ +/* TODO: Minimum value returned as pNumBits is now 8 - Imagine + can handle 1, 2, and 4 bits as well */ +GUInt32 HFACompress::findMin( GByte *pNumBits ) +{ +GUInt32 u32Val; +GUInt32 u32Min, u32Max; + + u32Min = valueAsUInt32( 0 ); + u32Max = u32Min; + + for( GUInt32 count = 1; count < m_nBlockCount; count++ ) + { + u32Val = valueAsUInt32( count ); + if( u32Val < u32Min ) + u32Min = u32Val; + else if( u32Val > u32Max ) + u32Max = u32Val; + } + + *pNumBits = _FindNumBits( u32Max - u32Min ); + + return u32Min; +} + +/* Codes the count in the way expected by Imagine - ie the lower 2 bits specify how many bytes + the count takes up */ +void HFACompress::makeCount( GUInt32 count, GByte *pCounter, GUInt32 *pnSizeCount ) +{ + /* Because Imagine stores the number of bits used in the + lower 2 bits of the data it restricts what we can use */ + if( count < 0x40 ) + { + pCounter[0] = (GByte) count; + *pnSizeCount = 1; + } + else if( count < 0x8000 ) + { + pCounter[1] = count & 0xff; + count /= 256; + pCounter[0] = (GByte) (count | 0x40); + *pnSizeCount = 2; + } + else if( count < 0x800000 ) + { + pCounter[2] = count & 0xff; + count /= 256; + pCounter[1] = count & 0xff; + count /= 256; + pCounter[0] = (GByte) (count | 0x80); + *pnSizeCount = 3; + } + else + { + pCounter[3] = count & 0xff; + count /= 256; + pCounter[2] = count & 0xff; + count /= 256; + pCounter[1] = count & 0xff; + count /= 256; + pCounter[0] = (GByte) (count | 0xc0); + *pnSizeCount = 4; + } +} + +/* Encodes the value depending on the number of bits we are using */ +void HFACompress::encodeValue( GUInt32 val, GUInt32 repeat ) +{ +GUInt32 nSizeCount; + + makeCount( repeat, m_pCurrCount, &nSizeCount ); + m_pCurrCount += nSizeCount; + if( m_nNumBits == 8 ) + { + /* Only storing 8 bits per value as the range is small */ + *(GByte*)m_pCurrValues = GByte(val - m_nMin); + m_pCurrValues += sizeof( GByte ); + } + else if( m_nNumBits == 16 ) + { + /* Only storing 16 bits per value as the range is small */ + *(GUInt16*)m_pCurrValues = GUInt16(val - m_nMin); +#ifndef CPL_MSB + CPL_SWAP16PTR( m_pCurrValues ); +#endif /* ndef CPL_MSB */ + m_pCurrValues += sizeof( GUInt16 ); + } + else + { + *(GUInt32*)m_pCurrValues = GUInt32(val - m_nMin); +#ifndef CPL_MSB + CPL_SWAP32PTR( m_pCurrValues ); +#endif /* ndef CPL_MSB */ + m_pCurrValues += sizeof( GUInt32 ); + } +} + +/* This is the guts of the file - call this to compress the block */ +/* returns false if the compression fails - ie compressed block bigger than input */ +bool HFACompress::compressBlock() +{ +GUInt32 nLastUnique = 0; + + /* Check we know about the datatype to be compressed. + If we can't compress it we should return false so that + the block cannot be compressed (we can handle just about + any type uncompressed) */ + if( ! QueryDataTypeSupported( m_nDataType ) ) + { + CPLDebug( "HFA", "Cannot compress HFA datatype 0x%x (0x%x bits). Writing uncompressed instead.\n", + m_nDataType, + m_nDataTypeNumBits ); + return false; + } + + /* reset our pointers */ + m_pCurrCount = m_pCounts; + m_pCurrValues = m_pValues; + + /* Get the minimum value - this can be subtracted from each value in the image */ + m_nMin = findMin( &m_nNumBits ); + + /* Go thru the block */ + GUInt32 u32Last, u32Val; + u32Last = valueAsUInt32( 0 ); + u32Val = u32Last; + for( GUInt32 count = 1; count < m_nBlockCount; count++ ) + { + u32Val = valueAsUInt32( count ); + if( u32Val != u32Last ) + { + /* The values have changed - ie a run has come to and end */ + encodeValue( u32Last, count - nLastUnique ); + + if( ( m_pCurrValues - m_pValues ) > (int) m_nBlockSize ) + { + return false; + } + + m_nNumRuns++; + u32Last = u32Val; + nLastUnique = count; + } + } + + /* OK we have done the block but haven't got the last run because we were only looking for a change in values */ + encodeValue( u32Last, m_nBlockCount - nLastUnique ); + m_nNumRuns++; + + /* set the size variables */ + m_nSizeCounts = m_pCurrCount - m_pCounts; + m_nSizeValues = m_pCurrValues - m_pValues; + + // The 13 is for the header size - maybe this should live with some constants somewhere? + return ( m_nSizeCounts + m_nSizeValues + 13 ) < m_nBlockSize; +} + +bool HFACompress::QueryDataTypeSupported( int nHFADataType ) +{ + int nBits = HFAGetDataTypeBits( nHFADataType ); + + return ( nBits == 8 ) || ( nBits == 16 ) || ( nBits == 32 ) || (nBits == 4) + || (nBits == 2) || (nBits == 1); +} + diff --git a/bazaar/plugin/gdal/frmts/hfa/hfadataset.cpp b/bazaar/plugin/gdal/frmts/hfa/hfadataset.cpp new file mode 100644 index 000000000..7ac2f95dc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/hfadataset.cpp @@ -0,0 +1,6112 @@ +/****************************************************************************** + * $Id: hfadataset.cpp 29206 2015-05-18 15:52:22Z rouault $ + * + * Name: hfadataset.cpp + * Project: Erdas Imagine Driver + * Purpose: Main driver for Erdas Imagine format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "gdal_rat.h" +#include "hfa_p.h" +#include "ogr_spatialref.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: hfadataset.cpp 29206 2015-05-18 15:52:22Z rouault $"); + +CPL_C_START +void GDALRegister_HFA(void); +CPL_C_END + +#ifndef PI +# define PI 3.14159265358979323846 +#endif + +#ifndef R2D +# define R2D (180/PI) +#endif +#ifndef D2R +# define D2R (PI/180) +#endif + +int WritePeStringIfNeeded(OGRSpatialReference* poSRS, HFAHandle hHFA); +void ClearSR(HFAHandle hHFA); + +static const char *apszDatumMap[] = { + /* Imagine name, WKT name */ + "NAD27", "North_American_Datum_1927", + "NAD83", "North_American_Datum_1983", + "WGS 84", "WGS_1984", + "WGS 1972", "WGS_1972", + "GDA94", "Geocentric_Datum_of_Australia_1994", + NULL, NULL +}; + +static const char *apszUnitMap[] = { + "meters", "1.0", + "meter", "1.0", + "m", "1.0", + "centimeters", "0.01", + "centimeter", "0.01", + "cm", "0.01", + "millimeters", "0.001", + "millimeter", "0.001", + "mm", "0.001", + "kilometers", "1000.0", + "kilometer", "1000.0", + "km", "1000.0", + "us_survey_feet", "0.3048006096012192", + "us_survey_foot", "0.3048006096012192", + "feet", "0.3048006096012192", + "foot", "0.3048006096012192", + "ft", "0.3048006096012192", + "international_feet", "0.3048", + "international_foot", "0.3048", + "inches", "0.0254000508001", + "inch", "0.0254000508001", + "in", "0.0254000508001", + "yards", "0.9144", + "yard", "0.9144", + "yd", "0.9144", + "miles", "1304.544", + "mile", "1304.544", + "mi", "1304.544", + "modified_american_feet", "0.3048122530", + "modified_american_foot", "0.3048122530", + "clarke_feet", "0.3047972651", + "clarke_foot", "0.3047972651", + "indian_feet", "0.3047995142", + "indian_foot", "0.3047995142", + NULL, NULL +}; + + +/* ==================================================================== */ +/* Table relating USGS and ESRI state plane zones. */ +/* ==================================================================== */ +static const int anUsgsEsriZones[] = +{ + 101, 3101, + 102, 3126, + 201, 3151, + 202, 3176, + 203, 3201, + 301, 3226, + 302, 3251, + 401, 3276, + 402, 3301, + 403, 3326, + 404, 3351, + 405, 3376, + 406, 3401, + 407, 3426, + 501, 3451, + 502, 3476, + 503, 3501, + 600, 3526, + 700, 3551, + 901, 3601, + 902, 3626, + 903, 3576, + 1001, 3651, + 1002, 3676, + 1101, 3701, + 1102, 3726, + 1103, 3751, + 1201, 3776, + 1202, 3801, + 1301, 3826, + 1302, 3851, + 1401, 3876, + 1402, 3901, + 1501, 3926, + 1502, 3951, + 1601, 3976, + 1602, 4001, + 1701, 4026, + 1702, 4051, + 1703, 6426, + 1801, 4076, + 1802, 4101, + 1900, 4126, + 2001, 4151, + 2002, 4176, + 2101, 4201, + 2102, 4226, + 2103, 4251, + 2111, 6351, + 2112, 6376, + 2113, 6401, + 2201, 4276, + 2202, 4301, + 2203, 4326, + 2301, 4351, + 2302, 4376, + 2401, 4401, + 2402, 4426, + 2403, 4451, + 2500, 0, + 2501, 4476, + 2502, 4501, + 2503, 4526, + 2600, 0, + 2601, 4551, + 2602, 4576, + 2701, 4601, + 2702, 4626, + 2703, 4651, + 2800, 4676, + 2900, 4701, + 3001, 4726, + 3002, 4751, + 3003, 4776, + 3101, 4801, + 3102, 4826, + 3103, 4851, + 3104, 4876, + 3200, 4901, + 3301, 4926, + 3302, 4951, + 3401, 4976, + 3402, 5001, + 3501, 5026, + 3502, 5051, + 3601, 5076, + 3602, 5101, + 3701, 5126, + 3702, 5151, + 3800, 5176, + 3900, 0, + 3901, 5201, + 3902, 5226, + 4001, 5251, + 4002, 5276, + 4100, 5301, + 4201, 5326, + 4202, 5351, + 4203, 5376, + 4204, 5401, + 4205, 5426, + 4301, 5451, + 4302, 5476, + 4303, 5501, + 4400, 5526, + 4501, 5551, + 4502, 5576, + 4601, 5601, + 4602, 5626, + 4701, 5651, + 4702, 5676, + 4801, 5701, + 4802, 5726, + 4803, 5751, + 4901, 5776, + 4902, 5801, + 4903, 5826, + 4904, 5851, + 5001, 6101, + 5002, 6126, + 5003, 6151, + 5004, 6176, + 5005, 6201, + 5006, 6226, + 5007, 6251, + 5008, 6276, + 5009, 6301, + 5010, 6326, + 5101, 5876, + 5102, 5901, + 5103, 5926, + 5104, 5951, + 5105, 5976, + 5201, 6001, + 5200, 6026, + 5200, 6076, + 5201, 6051, + 5202, 6051, + 5300, 0, + 5400, 0 +}; + + +/************************************************************************/ +/* ==================================================================== */ +/* HFADataset */ +/* ==================================================================== */ +/************************************************************************/ + +class HFARasterBand; + +class CPL_DLL HFADataset : public GDALPamDataset +{ + friend class HFARasterBand; + + HFAHandle hHFA; + + int bMetadataDirty; + + int bGeoDirty; + double adfGeoTransform[6]; + char *pszProjection; + + int bIgnoreUTM; + + CPLErr ReadProjection(); + CPLErr WriteProjection(); + int bForceToPEString; + + int nGCPCount; + GDAL_GCP asGCPList[36]; + + void UseXFormStack( int nStepCount, + Efga_Polynomial *pasPolyListForward, + Efga_Polynomial *pasPolyListReverse ); + + protected: + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ); + + public: + HFADataset(); + ~HFADataset(); + + static int Identify( GDALOpenInfo * ); + static CPLErr Rename( const char *pszNewName, const char *pszOldName); + static CPLErr CopyFiles( const char *pszNewName, const char *pszOldName); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); + static GDALDataset *CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + static CPLErr Delete( const char *pszFilename ); + + virtual char **GetFileList(void); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + virtual CPLErr SetMetadata( char **, const char * = "" ); + virtual CPLErr SetMetadataItem( const char *, const char *, const char * = "" ); + + virtual void FlushCache( void ); + virtual CPLErr IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* HFARasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class HFARasterBand : public GDALPamRasterBand +{ + friend class HFADataset; + friend class HFARasterAttributeTable; + + GDALColorTable *poCT; + + int nHFADataType; + + int nOverviews; + int nThisOverview; + HFARasterBand **papoOverviewBands; + + CPLErr CleanOverviews(); + + HFAHandle hHFA; + + int bMetadataDirty; + + GDALRasterAttributeTable *poDefaultRAT; + + void ReadAuxMetadata(); + void ReadHistogramMetadata(); + void EstablishOverviews(); + CPLErr WriteNamedRAT( const char *pszName, const GDALRasterAttributeTable *poRAT ); + + + public: + + HFARasterBand( HFADataset *, int, int ); + virtual ~HFARasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + + virtual const char *GetDescription() const; + virtual void SetDescription( const char * ); + + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual CPLErr SetColorTable( GDALColorTable * ); + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview( int ); + + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum(int *pbSuccess = NULL ); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual CPLErr SetNoDataValue( double dfValue ); + + virtual CPLErr SetMetadata( char **, const char * = "" ); + virtual CPLErr SetMetadataItem( const char *, const char *, const char * = "" ); + virtual CPLErr BuildOverviews( const char *, int, int *, + GDALProgressFunc, void * ); + + virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig ** ppanHistogram, + int bForce, + GDALProgressFunc, void *pProgressData); + + virtual GDALRasterAttributeTable *GetDefaultRAT(); + virtual CPLErr SetDefaultRAT( const GDALRasterAttributeTable * ); +}; + +class HFAAttributeField +{ +public: + CPLString sName; + GDALRATFieldType eType; + GDALRATFieldUsage eUsage; + int nDataOffset; + int nElementSize; + HFAEntry *poColumn; + int bIsBinValues; // handled differently + int bConvertColors; // map 0-1 floats to 0-255 ints +}; + +class HFARasterAttributeTable : public GDALRasterAttributeTable +{ +private: + + HFAHandle hHFA; + HFAEntry *poDT; + CPLString osName; + int nBand; + GDALAccess eAccess; + + std::vector aoFields; + int nRows; + + int bLinearBinning; + double dfRow0Min; + double dfBinSize; + + CPLString osWorkingResult; + + void AddColumn(const char *pszName, GDALRATFieldType eType, GDALRATFieldUsage eUsage, + int nDataOffset, int nElementSize, HFAEntry *poColumn, int bIsBinValues=FALSE, + int bConvertColors=FALSE) + { + HFAAttributeField aField; + aField.sName = pszName; + aField.eType = eType; + aField.eUsage = eUsage; + aField.nDataOffset = nDataOffset; + aField.nElementSize = nElementSize; + aField.poColumn = poColumn; + aField.bIsBinValues = bIsBinValues; + aField.bConvertColors = bConvertColors; + + this->aoFields.push_back(aField); + } + + void CreateDT() + { + this->poDT = new HFAEntry( this->hHFA->papoBand[this->nBand-1]->psInfo, + this->osName, "Edsc_Table", + this->hHFA->papoBand[this->nBand-1]->poNode ); + this->poDT->SetIntField( "numrows", nRows ); + } + +public: + HFARasterAttributeTable(HFARasterBand *poBand, const char *pszName); + ~HFARasterAttributeTable(); + + GDALDefaultRasterAttributeTable *Clone() const; + + virtual int GetColumnCount() const; + + virtual const char *GetNameOfCol( int ) const; + virtual GDALRATFieldUsage GetUsageOfCol( int ) const; + virtual GDALRATFieldType GetTypeOfCol( int ) const; + + virtual int GetColOfUsage( GDALRATFieldUsage ) const; + + virtual int GetRowCount() const; + + virtual const char *GetValueAsString( int iRow, int iField ) const; + virtual int GetValueAsInt( int iRow, int iField ) const; + virtual double GetValueAsDouble( int iRow, int iField ) const; + + virtual void SetValue( int iRow, int iField, const char *pszValue ); + virtual void SetValue( int iRow, int iField, double dfValue); + virtual void SetValue( int iRow, int iField, int nValue ); + + virtual CPLErr ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, double *pdfData); + virtual CPLErr ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, int *pnData); + virtual CPLErr ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, char **papszStrList); + + virtual int ChangesAreWrittenToFile(); + virtual void SetRowCount( int iCount ); + + virtual int GetRowOfValue( double dfValue ) const; + virtual int GetRowOfValue( int nValue ) const; + + virtual CPLErr CreateColumn( const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ); + virtual CPLErr SetLinearBinning( double dfRow0Min, double dfBinSize ); + virtual int GetLinearBinning( double *pdfRow0Min, double *pdfBinSize ) const; + + virtual CPLXMLNode *Serialize() const; + +protected: + CPLErr ColorsIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, int *pnData); +}; + +/************************************************************************/ +/* HFARasterAttributeTable() */ +/************************************************************************/ + +HFARasterAttributeTable::HFARasterAttributeTable(HFARasterBand *poBand, const char *pszName) +{ + this->hHFA = poBand->hHFA; + this->poDT = poBand->hHFA->papoBand[poBand->nBand-1]->poNode->GetNamedChild(pszName); + this->nBand = poBand->nBand; + this->eAccess = poBand->GetAccess(); + this->osName = pszName; + this->nRows = 0; + this->bLinearBinning = FALSE; + + if( this->poDT != NULL ) + { + this->nRows = this->poDT->GetIntField( "numRows" ); + +/* -------------------------------------------------------------------- */ +/* Scan under table for columns. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poDTChild; + + for( poDTChild = poDT->GetChild(); + poDTChild != NULL; + poDTChild = poDTChild->GetNext() ) + { + if( EQUAL(poDTChild->GetType(),"Edsc_BinFunction") ) + { + double dfMax = poDTChild->GetDoubleField( "maxLimit" ); + double dfMin = poDTChild->GetDoubleField( "minLimit" ); + int nBinCount = poDTChild->GetIntField( "numBins" ); + + if( nBinCount == this->nRows + && dfMax != dfMin && nBinCount != 0 ) + { + // can't call SetLinearBinning since it will re-write + // which we might not have permission to do + this->bLinearBinning = TRUE; + this->dfRow0Min = dfMin; + this->dfBinSize = (dfMax-dfMin) / (nBinCount-1); + } + } + + if( EQUAL(poDTChild->GetType(),"Edsc_BinFunction840") + && EQUAL(poDTChild->GetStringField( "binFunction.type.string" ), + "BFUnique") ) + { + AddColumn( "BinValues", GFT_Real, GFU_MinMax, 0, 0, poDTChild, TRUE); + } + + if( !EQUAL(poDTChild->GetType(),"Edsc_Column") ) + continue; + + int nOffset = poDTChild->GetIntField( "columnDataPtr" ); + const char * pszType = poDTChild->GetStringField( "dataType" ); + GDALRATFieldUsage eUsage = GFU_Generic; + int bConvertColors = FALSE; + + if( pszType == NULL || nOffset == 0 ) + continue; + + GDALRATFieldType eType; + if( EQUAL(pszType,"real") ) + eType = GFT_Real; + else if( EQUAL(pszType,"string") ) + eType = GFT_String; + else if( EQUALN(pszType,"int",3) ) + eType = GFT_Integer; + else + continue; + + if( EQUAL(poDTChild->GetName(),"Histogram") ) + eUsage = GFU_PixelCount; + else if( EQUAL(poDTChild->GetName(),"Red") ) + { + eUsage = GFU_Red; + // treat color columns as ints regardless + // of how they are stored + bConvertColors = (eType == GFT_Real); + eType = GFT_Integer; + } + else if( EQUAL(poDTChild->GetName(),"Green") ) + { + eUsage = GFU_Green; + bConvertColors = (eType == GFT_Real); + eType = GFT_Integer; + } + else if( EQUAL(poDTChild->GetName(),"Blue") ) + { + eUsage = GFU_Blue; + bConvertColors = (eType == GFT_Real); + eType = GFT_Integer; + } + else if( EQUAL(poDTChild->GetName(),"Opacity") ) + { + eUsage = GFU_Alpha; + bConvertColors = (eType == GFT_Real); + eType = GFT_Integer; + } + else if( EQUAL(poDTChild->GetName(),"Class_Names") ) + eUsage = GFU_Name; + + if( eType == GFT_Real ) + { + AddColumn(poDTChild->GetName(), GFT_Real, eUsage, nOffset, sizeof(double), poDTChild); + } + else if( eType == GFT_String ) + { + int nMaxNumChars = poDTChild->GetIntField( "maxNumChars" ); + AddColumn(poDTChild->GetName(), GFT_String, eUsage, nOffset, nMaxNumChars, poDTChild); + } + else if( eType == GFT_Integer ) + { + int nSize = sizeof(GInt32); + if( bConvertColors ) + nSize = sizeof(double); + AddColumn(poDTChild->GetName(), GFT_Integer, eUsage, nOffset, nSize, poDTChild, + FALSE, bConvertColors); + } + } + } +} + +/************************************************************************/ +/* ~HFARasterAttributeTable() */ +/************************************************************************/ + +HFARasterAttributeTable::~HFARasterAttributeTable() +{ + +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +GDALDefaultRasterAttributeTable *HFARasterAttributeTable::Clone() const +{ + if( ( GetRowCount() * GetColumnCount() ) > RAT_MAX_ELEM_FOR_CLONE ) + return NULL; + + GDALDefaultRasterAttributeTable *poRAT = new GDALDefaultRasterAttributeTable(); + + for( int iCol = 0; iCol < (int)aoFields.size(); iCol++) + { + poRAT->CreateColumn(aoFields[iCol].sName, aoFields[iCol].eType, aoFields[iCol].eUsage); + poRAT->SetRowCount(this->nRows); + + if( aoFields[iCol].eType == GFT_Integer ) + { + int *panColData = (int*)VSIMalloc2(sizeof(int), this->nRows); + if( panColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::Clone"); + delete poRAT; + return NULL; + } + + if( ((GDALDefaultRasterAttributeTable*)this)-> + ValuesIO(GF_Read, iCol, 0, this->nRows, panColData ) != CE_None ) + { + CPLFree(panColData); + delete poRAT; + return NULL; + } + + for( int iRow = 0; iRow < this->nRows; iRow++ ) + { + poRAT->SetValue(iRow, iCol, panColData[iRow]); + } + CPLFree(panColData); + } + if( aoFields[iCol].eType == GFT_Real ) + { + double *padfColData = (double*)VSIMalloc2(sizeof(double), this->nRows); + if( padfColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::Clone"); + delete poRAT; + return NULL; + } + + if( ((GDALDefaultRasterAttributeTable*)this)-> + ValuesIO(GF_Read, iCol, 0, this->nRows, padfColData ) != CE_None ) + { + CPLFree(padfColData); + delete poRAT; + return NULL; + } + + for( int iRow = 0; iRow < this->nRows; iRow++ ) + { + poRAT->SetValue(iRow, iCol, padfColData[iRow]); + } + CPLFree(padfColData); + } + if( aoFields[iCol].eType == GFT_String ) + { + char **papszColData = (char**)VSIMalloc2(sizeof(char*), this->nRows); + if( papszColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::Clone"); + delete poRAT; + return NULL; + } + + if( ((GDALDefaultRasterAttributeTable*)this)-> + ValuesIO(GF_Read, iCol, 0, this->nRows, papszColData ) != CE_None ) + { + CPLFree(papszColData); + delete poRAT; + return NULL; + } + + for( int iRow = 0; iRow < this->nRows; iRow++ ) + { + poRAT->SetValue(iRow, iCol, papszColData[iRow]); + CPLFree(papszColData[iRow]); + } + CPLFree(papszColData); + } + } + + if( this->bLinearBinning ) + poRAT->SetLinearBinning( this->dfRow0Min, this->dfBinSize ); + + return poRAT; +} + +/************************************************************************/ +/* GetColumnCount() */ +/************************************************************************/ + +int HFARasterAttributeTable::GetColumnCount() const +{ + return this->aoFields.size(); +} + +/************************************************************************/ +/* GetNameOfCol() */ +/************************************************************************/ + +const char *HFARasterAttributeTable::GetNameOfCol( int nCol ) const +{ + if( ( nCol < 0 ) || ( nCol >= (int)this->aoFields.size() ) ) + return NULL; + + return this->aoFields[nCol].sName; +} + +/************************************************************************/ +/* GetUsageOfCol() */ +/************************************************************************/ + +GDALRATFieldUsage HFARasterAttributeTable::GetUsageOfCol( int nCol ) const +{ + if( ( nCol < 0 ) || ( nCol >= (int)this->aoFields.size() ) ) + return GFU_Generic; + + return this->aoFields[nCol].eUsage; +} + +/************************************************************************/ +/* GetTypeOfCol() */ +/************************************************************************/ + +GDALRATFieldType HFARasterAttributeTable::GetTypeOfCol( int nCol ) const +{ + if( ( nCol < 0 ) || ( nCol >= (int)this->aoFields.size() ) ) + return GFT_Integer; + + return this->aoFields[nCol].eType; +} + +/************************************************************************/ +/* GetColOfUsage() */ +/************************************************************************/ + +int HFARasterAttributeTable::GetColOfUsage( GDALRATFieldUsage eUsage ) const +{ + unsigned int i; + + for( i = 0; i < this->aoFields.size(); i++ ) + { + if( this->aoFields[i].eUsage == eUsage ) + return i; + } + + return -1; + +} +/************************************************************************/ +/* GetRowCount() */ +/************************************************************************/ + +int HFARasterAttributeTable::GetRowCount() const +{ + return this->nRows; +} + +/************************************************************************/ +/* GetValueAsString() */ +/************************************************************************/ + +const char *HFARasterAttributeTable::GetValueAsString( int iRow, int iField ) const +{ + // Get ValuesIO do do the work + char *apszStrList[1]; + if( ((HFARasterAttributeTable*)this)-> + ValuesIO(GF_Read, iField, iRow, 1, apszStrList ) != CPLE_None ) + { + return ""; + } + + ((HFARasterAttributeTable *) this)->osWorkingResult = apszStrList[0]; + CPLFree(apszStrList[0]); + + return osWorkingResult; +} + +/************************************************************************/ +/* GetValueAsInt() */ +/************************************************************************/ + +int HFARasterAttributeTable::GetValueAsInt( int iRow, int iField ) const +{ + // Get ValuesIO do do the work + int nValue; + if( ((HFARasterAttributeTable*)this)-> + ValuesIO(GF_Read, iField, iRow, 1, &nValue ) != CE_None ) + { + return 0; + } + + return nValue; +} + +/************************************************************************/ +/* GetValueAsDouble() */ +/************************************************************************/ + +double HFARasterAttributeTable::GetValueAsDouble( int iRow, int iField ) const +{ + // Get ValuesIO do do the work + double dfValue; + if( ((HFARasterAttributeTable*)this)-> + ValuesIO(GF_Read, iField, iRow, 1, &dfValue ) != CE_None ) + { + return 0; + } + + return dfValue; +} + +/************************************************************************/ +/* SetValue() */ +/************************************************************************/ + +void HFARasterAttributeTable::SetValue( int iRow, int iField, const char *pszValue ) +{ + // Get ValuesIO do do the work + ValuesIO(GF_Write, iField, iRow, 1, (char**)&pszValue ); +} + +/************************************************************************/ +/* SetValue() */ +/************************************************************************/ + +void HFARasterAttributeTable::SetValue( int iRow, int iField, double dfValue) +{ + // Get ValuesIO do do the work + ValuesIO(GF_Write, iField, iRow, 1, &dfValue ); +} + +/************************************************************************/ +/* SetValue() */ +/************************************************************************/ + +void HFARasterAttributeTable::SetValue( int iRow, int iField, int nValue ) +{ + // Get ValuesIO do do the work + ValuesIO(GF_Write, iField, iRow, 1, &nValue ); +} + +/************************************************************************/ +/* ValuesIO() */ +/************************************************************************/ + +CPLErr HFARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, double *pdfData) +{ + if( ( eRWFlag == GF_Write ) && ( this->eAccess == GA_ReadOnly ) ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Dataset not open in update mode"); + return CE_Failure; + } + + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return CE_Failure; + } + + if( iStartRow < 0 || (iStartRow+iLength) > this->nRows ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iStartRow (%d) + iLength(%d) out of range.", iStartRow, iLength ); + + return CE_Failure; + } + + if( aoFields[iField].bConvertColors ) + { + // convert to/from float color field + int *panColData = (int*)VSIMalloc2(iLength, sizeof(int) ); + if( panColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::ValuesIO"); + CPLFree(panColData); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + for( int i = 0; i < iLength; i++ ) + panColData[i] = pdfData[i]; + } + + CPLErr ret = ColorsIO(eRWFlag, iField, iStartRow, iLength, panColData); + + if( eRWFlag == GF_Read ) + { + // copy them back to doubles + for( int i = 0; i < iLength; i++ ) + pdfData[i] = panColData[i]; + } + + CPLFree(panColData); + return ret; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + { + // allocate space for ints + int *panColData = (int*)VSIMalloc2(iLength, sizeof(int) ); + if( panColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::ValuesIO"); + CPLFree(panColData); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // copy the application supplied doubles to ints + for( int i = 0; i < iLength; i++ ) + panColData[i] = pdfData[i]; + } + + // do the ValuesIO as ints + CPLErr eVal = ValuesIO(eRWFlag, iField, iStartRow, iLength, panColData ); + if( eVal != CE_None ) + { + CPLFree(panColData); + return eVal; + } + + if( eRWFlag == GF_Read ) + { + // copy them back to doubles + for( int i = 0; i < iLength; i++ ) + pdfData[i] = panColData[i]; + } + + CPLFree(panColData); + } + break; + case GFT_Real: + { + if( (eRWFlag == GF_Read ) && aoFields[iField].bIsBinValues ) + { + // probably could change HFAReadBFUniqueBins to only read needed rows + double *padfBinValues = HFAReadBFUniqueBins( aoFields[iField].poColumn, iStartRow+iLength ); + memcpy(pdfData, &padfBinValues[iStartRow], sizeof(double) * iLength); + CPLFree(padfBinValues); + } + else + { + VSIFSeekL( hHFA->fp, aoFields[iField].nDataOffset + (iStartRow*aoFields[iField].nElementSize), SEEK_SET ); + + if( eRWFlag == GF_Read ) + { + if ((int)VSIFReadL(pdfData, sizeof(double), iLength, hHFA->fp ) != iLength) + { + CPLError( CE_Failure, CPLE_AppDefined, + "HFARasterAttributeTable::ValuesIO : Cannot read values"); + return CE_Failure; + } +#ifdef CPL_MSB + GDALSwapWords( pdfData, 8, iLength, 8 ); +#endif + } + else + { +#ifdef CPL_MSB + GDALSwapWords( pdfData, 8, iLength, 8 ); +#endif + // Note: HFAAllocateSpace now called by CreateColumn so space should exist + if((int)VSIFWriteL(pdfData, sizeof(double), iLength, hHFA->fp) != iLength) + { + CPLError( CE_Failure, CPLE_AppDefined, + "HFARasterAttributeTable::ValuesIO : Cannot write values"); + return CE_Failure; + } +#ifdef CPL_MSB + // swap back + GDALSwapWords( pdfData, 8, iLength, 8 ); +#endif + } + } + } + break; + case GFT_String: + { + // allocate space for string pointers + char **papszColData = (char**)VSIMalloc2(iLength, sizeof(char*)); + if( papszColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // copy the application supplied doubles to strings + for( int i = 0; i < iLength; i++ ) + { + osWorkingResult.Printf( "%.16g", pdfData[i] ); + papszColData[i] = CPLStrdup(osWorkingResult); + } + } + + // do the ValuesIO as strings + CPLErr eVal = ValuesIO(eRWFlag, iField, iStartRow, iLength, papszColData ); + if( eVal != CE_None ) + { + if( eRWFlag == GF_Write ) + { + for( int i = 0; i < iLength; i++ ) + CPLFree(papszColData[i]); + } + CPLFree(papszColData); + return eVal; + } + + if( eRWFlag == GF_Read ) + { + // copy them back to doubles + for( int i = 0; i < iLength; i++ ) + pdfData[i] = CPLAtof(papszColData[i]); + } + + // either we allocated them for write, or they were allocated + // by ValuesIO on read + for( int i = 0; i < iLength; i++ ) + CPLFree(papszColData[i]); + + CPLFree(papszColData); + } + break; + } + + return CE_None; +} + +/************************************************************************/ +/* ValuesIO() */ +/************************************************************************/ + +CPLErr HFARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, int *pnData) +{ + if( ( eRWFlag == GF_Write ) && ( this->eAccess == GA_ReadOnly ) ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Dataset not open in update mode"); + return CE_Failure; + } + + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return CE_Failure; + } + + if( iStartRow < 0 || (iStartRow+iLength) > this->nRows ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iStartRow (%d) + iLength(%d) out of range.", iStartRow, iLength ); + + return CE_Failure; + } + + if( aoFields[iField].bConvertColors ) + { + // convert to/from float color field + return ColorsIO(eRWFlag, iField, iStartRow, iLength, pnData); + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + { + VSIFSeekL( hHFA->fp, aoFields[iField].nDataOffset + (iStartRow*aoFields[iField].nElementSize), SEEK_SET ); + GInt32 *panColData = (GInt32*)VSIMalloc2(iLength, sizeof(GInt32)); + if( panColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Read ) + { + if ((int)VSIFReadL( panColData, sizeof(GInt32), iLength, hHFA->fp ) != iLength) + { + CPLError( CE_Failure, CPLE_AppDefined, + "HFARasterAttributeTable::ValuesIO : Cannot read values"); + CPLFree(panColData); + return CE_Failure; + } +#ifdef CPL_MSB + GDALSwapWords( panColData, 4, iLength, 4 ); +#endif + // now copy into application buffer. This extra step + // may not be necessary if sizeof(int) == sizeof(GInt32) + for( int i = 0; i < iLength; i++ ) + pnData[i] = panColData[i]; + } + else + { + // copy from application buffer + for( int i = 0; i < iLength; i++ ) + panColData[i] = pnData[i]; + +#ifdef CPL_MSB + GDALSwapWords( panColData, 4, iLength, 4 ); +#endif + // Note: HFAAllocateSpace now called by CreateColumn so space should exist + if((int)VSIFWriteL(panColData, sizeof(GInt32), iLength, hHFA->fp) != iLength) + { + CPLError( CE_Failure, CPLE_AppDefined, + "HFARasterAttributeTable::ValuesIO : Cannot write values"); + CPLFree(panColData); + return CE_Failure; + } + } + CPLFree(panColData); + } + break; + case GFT_Real: + { + // allocate space for doubles + double *padfColData = (double*)VSIMalloc2(iLength, sizeof(double) ); + if( padfColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // copy the application supplied ints to doubles + for( int i = 0; i < iLength; i++ ) + padfColData[i] = pnData[i]; + } + + // do the ValuesIO as doubles + CPLErr eVal = ValuesIO(eRWFlag, iField, iStartRow, iLength, padfColData ); + if( eVal != CE_None ) + { + CPLFree(padfColData); + return eVal; + } + + if( eRWFlag == GF_Read ) + { + // copy them back to ints + for( int i = 0; i < iLength; i++ ) + pnData[i] = padfColData[i]; + } + + CPLFree(padfColData); + } + break; + case GFT_String: + { + // allocate space for string pointers + char **papszColData = (char**)VSIMalloc2(iLength, sizeof(char*)); + if( papszColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // copy the application supplied ints to strings + for( int i = 0; i < iLength; i++ ) + { + osWorkingResult.Printf( "%d", pnData[i] ); + papszColData[i] = CPLStrdup(osWorkingResult); + } + } + + // do the ValuesIO as strings + CPLErr eVal = ValuesIO(eRWFlag, iField, iStartRow, iLength, papszColData ); + if( eVal != CE_None ) + { + if( eRWFlag == GF_Write ) + { + for( int i = 0; i < iLength; i++ ) + CPLFree(papszColData[i]); + } + CPLFree(papszColData); + return eVal; + } + + if( eRWFlag == GF_Read ) + { + // copy them back to ints + for( int i = 0; i < iLength; i++ ) + pnData[i] = atol(papszColData[i]); + } + + // either we allocated them for write, or they were allocated + // by ValuesIO on read + for( int i = 0; i < iLength; i++ ) + CPLFree(papszColData[i]); + + CPLFree(papszColData); + } + break; + } + + return CE_None; +} + +/************************************************************************/ +/* ValuesIO() */ +/************************************************************************/ + +CPLErr HFARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, char **papszStrList) +{ + if( ( eRWFlag == GF_Write ) && ( this->eAccess == GA_ReadOnly ) ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Dataset not open in update mode"); + return CE_Failure; + } + + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return CE_Failure; + } + + if( iStartRow < 0 || (iStartRow+iLength) > this->nRows ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iStartRow (%d) + iLength(%d) out of range.", iStartRow, iLength ); + + return CE_Failure; + } + + if( aoFields[iField].bConvertColors ) + { + // convert to/from float color field + int *panColData = (int*)VSIMalloc2(iLength, sizeof(int) ); + if( panColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::ValuesIO"); + CPLFree(panColData); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + for( int i = 0; i < iLength; i++ ) + panColData[i] = atol(papszStrList[i]); + } + + CPLErr ret = ColorsIO(eRWFlag, iField, iStartRow, iLength, panColData); + + if( eRWFlag == GF_Read ) + { + // copy them back to strings + for( int i = 0; i < iLength; i++ ) + { + osWorkingResult.Printf( "%d", panColData[i]); + papszStrList[i] = CPLStrdup(osWorkingResult); + } + } + + CPLFree(panColData); + return ret; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + { + // allocate space for ints + int *panColData = (int*)VSIMalloc2(iLength, sizeof(int) ); + if( panColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // convert user supplied strings to ints + for( int i = 0; i < iLength; i++ ) + panColData[i] = atol(papszStrList[i]); + } + + // call values IO to read/write ints + CPLErr eVal = ValuesIO(eRWFlag, iField, iStartRow, iLength, panColData); + if( eVal != CE_None ) + { + CPLFree(panColData); + return eVal; + } + + + if( eRWFlag == GF_Read ) + { + // convert ints back to strings + for( int i = 0; i < iLength; i++ ) + { + osWorkingResult.Printf( "%d", panColData[i]); + papszStrList[i] = CPLStrdup(osWorkingResult); + } + } + CPLFree(panColData); + } + break; + case GFT_Real: + { + // allocate space for doubles + double *padfColData = (double*)VSIMalloc2(iLength, sizeof(double) ); + if( padfColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // convert user supplied strings to doubles + for( int i = 0; i < iLength; i++ ) + padfColData[i] = CPLAtof(papszStrList[i]); + } + + // call value IO to read/write doubles + CPLErr eVal = ValuesIO(eRWFlag, iField, iStartRow, iLength, padfColData); + if( eVal != CE_None ) + { + CPLFree(padfColData); + return eVal; + } + + if( eRWFlag == GF_Read ) + { + // convert doubles back to strings + for( int i = 0; i < iLength; i++ ) + { + osWorkingResult.Printf( "%.16g", padfColData[i]); + papszStrList[i] = CPLStrdup(osWorkingResult); + } + } + CPLFree(padfColData); + } + break; + case GFT_String: + { + VSIFSeekL( hHFA->fp, aoFields[iField].nDataOffset + (iStartRow*aoFields[iField].nElementSize), SEEK_SET ); + char *pachColData = (char*)VSIMalloc2(iLength, aoFields[iField].nElementSize); + if( pachColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Read ) + { + if ((int)VSIFReadL( pachColData, aoFields[iField].nElementSize, iLength, hHFA->fp ) != iLength) + { + CPLError( CE_Failure, CPLE_AppDefined, + "HFARasterAttributeTable::ValuesIO : Cannot read values"); + CPLFree(pachColData); + return CE_Failure; + } + + // now copy into application buffer + for( int i = 0; i < iLength; i++ ) + { + osWorkingResult.assign(pachColData+aoFields[iField].nElementSize*i, aoFields[iField].nElementSize ); + papszStrList[i] = CPLStrdup(osWorkingResult); + } + } + else + { + // we need to check that these strings will fit in the allocated space + int nNewMaxChars = aoFields[iField].nElementSize, nStringSize; + for( int i = 0; i < iLength; i++ ) + { + nStringSize = strlen(papszStrList[i]) + 1; + if( nStringSize > nNewMaxChars ) + nNewMaxChars = nStringSize; + } + + if( nNewMaxChars > aoFields[iField].nElementSize ) + { + // OK we have a problem - the allocated space is not big enough + // we need to re-allocate the space and update the pointers + // and copy accross the old data + int nNewOffset = HFAAllocateSpace( this->hHFA->papoBand[this->nBand-1]->psInfo, + this->nRows * nNewMaxChars); + char *pszBuffer = (char*)VSIMalloc2(aoFields[iField].nElementSize, sizeof(char)); + char cNullByte = '\0'; + for( int i = 0; i < this->nRows; i++ ) + { + // seek to the old place + VSIFSeekL( hHFA->fp, aoFields[iField].nDataOffset + (i*aoFields[iField].nElementSize), SEEK_SET ); + // read in old data + VSIFReadL(pszBuffer, aoFields[iField].nElementSize, 1, hHFA->fp ); + // seek to new place + VSIFSeekL( hHFA->fp, nNewOffset + (i*nNewMaxChars), SEEK_SET ); + // write data to new place + VSIFWriteL(pszBuffer, aoFields[iField].nElementSize, 1, hHFA->fp); + // make sure there is a terminating null byte just to be safe + VSIFWriteL(&cNullByte, sizeof(char), 1, hHFA->fp); + } + // update our data structures + aoFields[iField].nElementSize = nNewMaxChars; + aoFields[iField].nDataOffset = nNewOffset; + // update file + aoFields[iField].poColumn->SetIntField( "columnDataPtr", nNewOffset ); + aoFields[iField].poColumn->SetIntField( "maxNumChars", nNewMaxChars ); + + // Note: there isn't an HFAFreeSpace so we can't un-allocate the old space in the file + CPLFree(pszBuffer); + + // re-allocate our buffer + CPLFree(pachColData); + pachColData = (char*)VSIMalloc2(iLength, nNewMaxChars); + if(pachColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + // lastly seek to the right place in the new space ready to write + VSIFSeekL( hHFA->fp, nNewOffset + (iStartRow*nNewMaxChars), SEEK_SET ); + } + + // copy from application buffer + for( int i = 0; i < iLength; i++ ) + strcpy(&pachColData[nNewMaxChars*i], papszStrList[i]); + + // Note: HFAAllocateSpace now called by CreateColumn so space should exist + if((int)VSIFWriteL(pachColData, aoFields[iField].nElementSize, iLength, hHFA->fp) != iLength) + { + CPLError( CE_Failure, CPLE_AppDefined, + "HFARasterAttributeTable::ValuesIO : Cannot write values"); + CPLFree(pachColData); + return CE_Failure; + } + } + CPLFree(pachColData); + } + break; + } + + return CE_None; +} + +/************************************************************************/ +/* ColorsIO() */ +/************************************************************************/ + +// Handle the fact that HFA stores colours as floats, but we need to +// read them in as ints 0...255 +CPLErr HFARasterAttributeTable::ColorsIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, int *pnData) +{ + // allocate space for doubles + double *padfData = (double*)VSIMalloc2(iLength, sizeof(double) ); + if( padfData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::ColorsIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // copy the application supplied ints to doubles + // and convert 0..255 to 0..1 in the same manner + // as the color table + for( int i = 0; i < iLength; i++ ) + padfData[i] = pnData[i] / 255.0; + } + + VSIFSeekL( hHFA->fp, aoFields[iField].nDataOffset + (iStartRow*aoFields[iField].nElementSize), SEEK_SET ); + + if( eRWFlag == GF_Read ) + { + if ((int)VSIFReadL(padfData, sizeof(double), iLength, hHFA->fp ) != iLength) + { + CPLError( CE_Failure, CPLE_AppDefined, + "HFARasterAttributeTable::ColorsIO : Cannot read values"); + return CE_Failure; + } +#ifdef CPL_MSB + GDALSwapWords( padfData, 8, iLength, 8 ); +#endif + } + else + { +#ifdef CPL_MSB + GDALSwapWords( padfData, 8, iLength, 8 ); +#endif + // Note: HFAAllocateSpace now called by CreateColumn so space should exist + if((int)VSIFWriteL(padfData, sizeof(double), iLength, hHFA->fp) != iLength) + { + CPLError( CE_Failure, CPLE_AppDefined, + "HFARasterAttributeTable::ColorsIO : Cannot write values"); + return CE_Failure; + } + } + + if( eRWFlag == GF_Read ) + { + // copy them back to ints converting 0..1 to 0..255 in + // the same manner as the color table + for( int i = 0; i < iLength; i++ ) + pnData[i] = MIN(255,(int) (padfData[i] * 256)); + } + + CPLFree(padfData); + + return CE_None; +} + +/************************************************************************/ +/* ChangesAreWrittenToFile() */ +/************************************************************************/ + +int HFARasterAttributeTable::ChangesAreWrittenToFile() +{ + return TRUE; +} + +/************************************************************************/ +/* SetRowCount() */ +/************************************************************************/ + +void HFARasterAttributeTable::SetRowCount( int iCount ) +{ + if( this->eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Dataset not open in update mode"); + return; + } + + if( iCount > this->nRows ) + { + // making the RAT larger - a bit hard + // We need to re-allocate space on disc + for( int iCol = 0; iCol < (int)this->aoFields.size(); iCol++ ) + { + // new space + int nNewOffset = HFAAllocateSpace( this->hHFA->papoBand[this->nBand-1]->psInfo, + iCount * aoFields[iCol].nElementSize); + + // only need to bother if there are actually rows + if( this->nRows > 0 ) + { + // temp buffer for this column + void *pData = VSIMalloc2(this->nRows, aoFields[iCol].nElementSize); + if( pData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in HFARasterAttributeTable::SetRowCount"); + return; + } + // read old data + VSIFSeekL( hHFA->fp, aoFields[iCol].nDataOffset, SEEK_SET ); + if((int)VSIFReadL(pData, aoFields[iCol].nElementSize, this->nRows, hHFA->fp) != this->nRows ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "HFARasterAttributeTable::SetRowCount : Cannot read values"); + CPLFree(pData); + return; + } + + // write data - new space will be uninitialised + VSIFSeekL( hHFA->fp, nNewOffset, SEEK_SET ); + if((int)VSIFWriteL(pData, aoFields[iCol].nElementSize, this->nRows, hHFA->fp) != this->nRows ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "HFARasterAttributeTable::SetRowCount : Cannot write values"); + CPLFree(pData); + return; + } + CPLFree(pData); + } + + // update our data structures + aoFields[iCol].nDataOffset = nNewOffset; + // update file + aoFields[iCol].poColumn->SetIntField( "columnDataPtr", nNewOffset ); + aoFields[iCol].poColumn->SetIntField( "numRows", iCount); + } + } + else if( iCount < this->nRows ) + { + // update the numRows + for( int iCol = 0; iCol < (int)this->aoFields.size(); iCol++ ) + { + aoFields[iCol].poColumn->SetIntField( "numRows", iCount); + } + } + + this->nRows = iCount; + + if( ( this->poDT != NULL ) && ( EQUAL(this->poDT->GetType(),"Edsc_Table"))) + { + this->poDT->SetIntField( "numrows", iCount ); + } +} + +/************************************************************************/ +/* GetRowOfValue() */ +/************************************************************************/ + +int HFARasterAttributeTable::GetRowOfValue( double dfValue ) const +{ +/* -------------------------------------------------------------------- */ +/* Handle case of regular binning. */ +/* -------------------------------------------------------------------- */ + if( bLinearBinning ) + { + int iBin = (int) floor((dfValue - dfRow0Min) / dfBinSize); + if( iBin < 0 || iBin >= this->nRows ) + return -1; + else + return iBin; + } + +/* -------------------------------------------------------------------- */ +/* Do we have any information? */ +/* -------------------------------------------------------------------- */ + int nMinCol = GetColOfUsage( GFU_Min ); + if( nMinCol == -1 ) + nMinCol = GetColOfUsage( GFU_MinMax ); + + int nMaxCol = GetColOfUsage( GFU_Max ); + if( nMaxCol == -1 ) + nMaxCol = GetColOfUsage( GFU_MinMax ); + + if( nMinCol == -1 && nMaxCol == -1 ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Search through rows for match. */ +/* -------------------------------------------------------------------- */ + int iRow; + + for( iRow = 0; iRow < this->nRows; iRow++ ) + { + if( nMinCol != -1 ) + { + while( iRow < this->nRows && dfValue < GetValueAsDouble(iRow, nMinCol) ) + iRow++; + + if( iRow == this->nRows ) + break; + } + + if( nMaxCol != -1 ) + { + if( dfValue > GetValueAsDouble(iRow, nMaxCol) ) + continue; + } + + return iRow; + } + + return -1; +} + +/************************************************************************/ +/* GetRowOfValue() */ +/* */ +/* Int arg for now just converted to double. Perhaps we will */ +/* handle this in a special way some day? */ +/************************************************************************/ + +int HFARasterAttributeTable::GetRowOfValue( int nValue ) const +{ + return GetRowOfValue( (double) nValue ); +} + +/************************************************************************/ +/* CreateColumn() */ +/************************************************************************/ + +CPLErr HFARasterAttributeTable::CreateColumn( const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ) +{ + if( this->eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Dataset not open in update mode"); + return CE_Failure; + } + + // do we have a descriptor table already? + if( this->poDT == NULL || !EQUAL(this->poDT->GetType(),"Edsc_Table") ) + CreateDT(); + + int bConvertColors = FALSE; + + // Imagine doesn't have a concept of usage - works of the names instead. + // must make sure name matches use + if( eFieldUsage == GFU_Red ) + { + pszFieldName = "Red"; + // create a real column in the file, but make it + // available as int to GDAL + bConvertColors = TRUE; + eFieldType = GFT_Real; + } + else if( eFieldUsage == GFU_Green ) + { + pszFieldName = "Green"; + bConvertColors = TRUE; + eFieldType = GFT_Real; + } + else if( eFieldUsage == GFU_Blue ) + { + pszFieldName = "Blue"; + bConvertColors = TRUE; + eFieldType = GFT_Real; + } + else if( eFieldUsage == GFU_Alpha ) + { + pszFieldName = "Opacity"; + bConvertColors = TRUE; + eFieldType = GFT_Real; + } + else if( eFieldUsage == GFU_PixelCount ) + { + pszFieldName = "Histogram"; + // histogram is always float in HFA + eFieldType = GFT_Real; + } + else if( eFieldUsage == GFU_Name ) + { + pszFieldName = "Class_Names"; + } + +/* -------------------------------------------------------------------- */ +/* Check to see if a column with pszFieldName exists and create it */ +/* if necessary. */ +/* -------------------------------------------------------------------- */ + + HFAEntry *poColumn; + poColumn = poDT->GetNamedChild(pszFieldName); + + if(poColumn == NULL || !EQUAL(poColumn->GetType(),"Edsc_Column")) + poColumn = new HFAEntry( this->hHFA->papoBand[this->nBand-1]->psInfo, + pszFieldName, "Edsc_Column", + this->poDT ); + + + poColumn->SetIntField( "numRows", this->nRows ); + int nElementSize = 0; + + if( eFieldType == GFT_Integer ) + { + nElementSize = sizeof(GInt32); + poColumn->SetStringField( "dataType", "integer" ); + } + else if( eFieldType == GFT_Real ) + { + nElementSize = sizeof(double); + poColumn->SetStringField( "dataType", "real" ); + } + else if( eFieldType == GFT_String ) + { + // just have to guess here since we don't have any + // strings to check + nElementSize = 10; + poColumn->SetStringField( "dataType", "string" ); + poColumn->SetIntField( "maxNumChars", nElementSize); + } + else + { + /* can't deal with any of the others yet */ + CPLError( CE_Failure, CPLE_NotSupported, + "Writing this data type in a column is not supported for this Raster Attribute Table."); + return CE_Failure; + } + + int nOffset = HFAAllocateSpace( this->hHFA->papoBand[this->nBand-1]->psInfo, + this->nRows * nElementSize ); + poColumn->SetIntField( "columnDataPtr", nOffset ); + + if( bConvertColors ) + // GDAL Int column + eFieldType = GFT_Integer; + + AddColumn(pszFieldName, eFieldType, eFieldUsage, nOffset, nElementSize, poColumn, FALSE, bConvertColors); + + return CE_None; +} + +/************************************************************************/ +/* SetLinearBinning() */ +/************************************************************************/ + +CPLErr HFARasterAttributeTable::SetLinearBinning( double dfRow0MinIn, double dfBinSizeIn ) +{ + if( this->eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Dataset not open in update mode"); + return CE_Failure; + } + + this->bLinearBinning = TRUE; + this->dfRow0Min = dfRow0MinIn; + this->dfBinSize = dfBinSizeIn; + + // do we have a descriptor table already? + if( this->poDT == NULL || !EQUAL(this->poDT->GetType(),"Edsc_Table") ) + CreateDT(); + + /* we should have an Edsc_BinFunction */ + HFAEntry *poBinFunction = this->poDT->GetNamedChild( "#Bin_Function#" ); + if( poBinFunction == NULL || !EQUAL(poBinFunction->GetType(),"Edsc_BinFunction") ) + poBinFunction = new HFAEntry( hHFA->papoBand[nBand-1]->psInfo, + "#Bin_Function#", "Edsc_BinFunction", + this->poDT ); + + // Because of the BaseData we have to hardcode the size. + poBinFunction->MakeData( 30 ); + + poBinFunction->SetStringField("binFunction", "direct"); + poBinFunction->SetDoubleField("minLimit",this->dfRow0Min); + poBinFunction->SetDoubleField("maxLimit",(this->nRows -1)*this->dfBinSize+this->dfRow0Min); + poBinFunction->SetIntField("numBins",this->nRows); + + return CE_None; +} + +/************************************************************************/ +/* GetLinearBinning() */ +/************************************************************************/ + +int HFARasterAttributeTable::GetLinearBinning( double *pdfRow0Min, double *pdfBinSize ) const +{ + if( !bLinearBinning ) + return FALSE; + + *pdfRow0Min = dfRow0Min; + *pdfBinSize = dfBinSize; + + return TRUE; +} + +/************************************************************************/ +/* Serialize() */ +/************************************************************************/ + +CPLXMLNode *HFARasterAttributeTable::Serialize() const +{ + if( ( GetRowCount() * GetColumnCount() ) > RAT_MAX_ELEM_FOR_CLONE ) + return NULL; + + return GDALRasterAttributeTable::Serialize(); +} + +/************************************************************************/ +/* HFARasterBand() */ +/************************************************************************/ + +HFARasterBand::HFARasterBand( HFADataset *poDS, int nBand, int iOverview ) + +{ + int nCompression; + + if( iOverview == -1 ) + this->poDS = poDS; + else + this->poDS = NULL; + + this->hHFA = poDS->hHFA; + this->nBand = nBand; + this->poCT = NULL; + this->nThisOverview = iOverview; + this->papoOverviewBands = NULL; + this->bMetadataDirty = FALSE; + this->poDefaultRAT = NULL; + this->nOverviews = -1; + + HFAGetBandInfo( hHFA, nBand, &nHFADataType, + &nBlockXSize, &nBlockYSize, &nCompression ); + +/* -------------------------------------------------------------------- */ +/* If this is an overview, we need to fetch the actual size, */ +/* and block size. */ +/* -------------------------------------------------------------------- */ + if( iOverview > -1 ) + { + int nHFADataTypeO; + + nOverviews = 0; + if (HFAGetOverviewInfo( hHFA, nBand, iOverview, + &nRasterXSize, &nRasterYSize, + &nBlockXSize, &nBlockYSize, &nHFADataTypeO ) != CE_None) + { + nRasterXSize = nRasterYSize = 0; + return; + } + +/* -------------------------------------------------------------------- */ +/* If we are an 8bit overview of a 1bit layer, we need to mark */ +/* ourselves as being "resample: average_bit2grayscale". */ +/* -------------------------------------------------------------------- */ + if( nHFADataType == EPT_u1 && nHFADataTypeO == EPT_u8 ) + { + GDALMajorObject::SetMetadataItem( "RESAMPLING", + "AVERAGE_BIT2GRAYSCALE" ); + GDALMajorObject::SetMetadataItem( "NBITS", "8" ); + nHFADataType = nHFADataTypeO; + } + } + +/* -------------------------------------------------------------------- */ +/* Set some other information. */ +/* -------------------------------------------------------------------- */ + if( nCompression != 0 ) + GDALMajorObject::SetMetadataItem( "COMPRESSION", "RLE", + "IMAGE_STRUCTURE" ); + + switch( nHFADataType ) + { + case EPT_u1: + case EPT_u2: + case EPT_u4: + case EPT_u8: + case EPT_s8: + eDataType = GDT_Byte; + break; + + case EPT_u16: + eDataType = GDT_UInt16; + break; + + case EPT_s16: + eDataType = GDT_Int16; + break; + + case EPT_u32: + eDataType = GDT_UInt32; + break; + + case EPT_s32: + eDataType = GDT_Int32; + break; + + case EPT_f32: + eDataType = GDT_Float32; + break; + + case EPT_f64: + eDataType = GDT_Float64; + break; + + case EPT_c64: + eDataType = GDT_CFloat32; + break; + + case EPT_c128: + eDataType = GDT_CFloat64; + break; + + default: + eDataType = GDT_Byte; + /* notdef: this should really report an error, but this isn't + so easy from within constructors. */ + CPLDebug( "GDAL", "Unsupported pixel type in HFARasterBand: %d.", + (int) nHFADataType ); + break; + } + + if( HFAGetDataTypeBits( nHFADataType ) < 8 ) + { + GDALMajorObject::SetMetadataItem( + "NBITS", + CPLString().Printf( "%d", HFAGetDataTypeBits( nHFADataType ) ), "IMAGE_STRUCTURE" ); + } + + if( nHFADataType == EPT_s8 ) + { + GDALMajorObject::SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", + "IMAGE_STRUCTURE" ); + } + +/* -------------------------------------------------------------------- */ +/* Collect color table if present. */ +/* -------------------------------------------------------------------- */ + double *padfRed, *padfGreen, *padfBlue, *padfAlpha, *padfBins; + int nColors; + + if( iOverview == -1 + && HFAGetPCT( hHFA, nBand, &nColors, + &padfRed, &padfGreen, &padfBlue, &padfAlpha, + &padfBins ) == CE_None + && nColors > 0 ) + { + poCT = new GDALColorTable(); + for( int iColor = 0; iColor < nColors; iColor++ ) + { + GDALColorEntry sEntry; + + // The following mapping assigns "equal sized" section of + // the [0...1] range to each possible output value and avoid + // rounding issues for the "normal" values generated using n/255. + // See bug #1732 for some discussion. + sEntry.c1 = MIN(255,(short) (padfRed[iColor] * 256)); + sEntry.c2 = MIN(255,(short) (padfGreen[iColor] * 256)); + sEntry.c3 = MIN(255,(short) (padfBlue[iColor] * 256)); + sEntry.c4 = MIN(255,(short) (padfAlpha[iColor] * 256)); + + if( padfBins != NULL ) + poCT->SetColorEntry( (int) padfBins[iColor], &sEntry ); + else + poCT->SetColorEntry( iColor, &sEntry ); + } + } + +} + +/************************************************************************/ +/* ~HFARasterBand() */ +/************************************************************************/ + +HFARasterBand::~HFARasterBand() + +{ + FlushCache(); + + for( int iOvIndex = 0; iOvIndex < nOverviews; iOvIndex++ ) + { + delete papoOverviewBands[iOvIndex]; + } + CPLFree( papoOverviewBands ); + + if( poCT != NULL ) + delete poCT; + + if( poDefaultRAT ) + delete poDefaultRAT; +} + +/************************************************************************/ +/* ReadAuxMetadata() */ +/************************************************************************/ + +void HFARasterBand::ReadAuxMetadata() + +{ + int i; + HFABand *poBand = hHFA->papoBand[nBand-1]; + + // only load metadata for full resolution layer. + if( nThisOverview != -1 ) + return; + + const char ** pszAuxMetaData = GetHFAAuxMetaDataList(); + for( i = 0; pszAuxMetaData[i] != NULL; i += 4 ) + { + HFAEntry *poEntry; + + if( strlen(pszAuxMetaData[i]) > 0 ) + poEntry = poBand->poNode->GetNamedChild( pszAuxMetaData[i] ); + else + poEntry = poBand->poNode; + + const char *pszFieldName = pszAuxMetaData[i+1] + 1; + CPLErr eErr = CE_None; + + if( poEntry == NULL ) + continue; + + switch( pszAuxMetaData[i+1][0] ) + { + case 'd': + { + int nCount, iValue; + double dfValue; + CPLString osValueList; + + nCount = poEntry->GetFieldCount( pszFieldName, &eErr ); + for( iValue = 0; eErr == CE_None && iValue < nCount; iValue++ ) + { + CPLString osSubFieldName; + osSubFieldName.Printf( "%s[%d]", pszFieldName, iValue ); + dfValue = poEntry->GetDoubleField( osSubFieldName, &eErr ); + if( eErr != CE_None ) + break; + + char szValueAsString[100]; + CPLsprintf( szValueAsString, "%.14g", dfValue ); + + if( iValue > 0 ) + osValueList += ","; + osValueList += szValueAsString; + } + if( eErr == CE_None ) + SetMetadataItem( pszAuxMetaData[i+2], osValueList ); + } + break; + case 'i': + case 'l': + { + int nValue, nCount, iValue; + CPLString osValueList; + + nCount = poEntry->GetFieldCount( pszFieldName, &eErr ); + for( iValue = 0; eErr == CE_None && iValue < nCount; iValue++ ) + { + CPLString osSubFieldName; + osSubFieldName.Printf( "%s[%d]", pszFieldName, iValue ); + nValue = poEntry->GetIntField( osSubFieldName, &eErr ); + if( eErr != CE_None ) + break; + + char szValueAsString[100]; + sprintf( szValueAsString, "%d", nValue ); + + if( iValue > 0 ) + osValueList += ","; + osValueList += szValueAsString; + } + if( eErr == CE_None ) + SetMetadataItem( pszAuxMetaData[i+2], osValueList ); + } + break; + case 's': + case 'e': + { + const char *pszValue; + pszValue = poEntry->GetStringField( pszFieldName, &eErr ); + if( eErr == CE_None ) + SetMetadataItem( pszAuxMetaData[i+2], pszValue ); + } + break; + default: + CPLAssert( FALSE ); + } + } +} + +/************************************************************************/ +/* ReadHistogramMetadata() */ +/************************************************************************/ + +void HFARasterBand::ReadHistogramMetadata() + +{ + int i; + HFABand *poBand = hHFA->papoBand[nBand-1]; + + // only load metadata for full resolution layer. + if( nThisOverview != -1 ) + return; + + HFAEntry *poEntry = + poBand->poNode->GetNamedChild( "Descriptor_Table.Histogram" ); + if ( poEntry == NULL ) + return; + + int nNumBins = poEntry->GetIntField( "numRows" ); + if (nNumBins < 0) + return; + +/* -------------------------------------------------------------------- */ +/* Fetch the histogram values. */ +/* -------------------------------------------------------------------- */ + + int nOffset = poEntry->GetIntField( "columnDataPtr" ); + const char * pszType = poEntry->GetStringField( "dataType" ); + int nBinSize = 4; + + if( pszType != NULL && EQUALN( "real", pszType, 4 ) ) + nBinSize = 8; + + GUIntBig *panHistValues = (GUIntBig *) VSIMalloc2(sizeof(GUIntBig), nNumBins); + GByte *pabyWorkBuf = (GByte *) VSIMalloc2(nBinSize, nNumBins); + + if (panHistValues == NULL || pabyWorkBuf == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate memory"); + VSIFree(panHistValues); + VSIFree(pabyWorkBuf); + return; + } + + VSIFSeekL( hHFA->fp, nOffset, SEEK_SET ); + + if( (int)VSIFReadL( pabyWorkBuf, nBinSize, nNumBins, hHFA->fp ) != nNumBins) + { + CPLError( CE_Failure, CPLE_FileIO, + "Cannot read histogram values." ); + CPLFree( panHistValues ); + CPLFree( pabyWorkBuf ); + return; + } + + // Swap into local order. + for( i = 0; i < nNumBins; i++ ) + HFAStandard( nBinSize, pabyWorkBuf + i*nBinSize ); + + if( nBinSize == 8 ) // source is doubles + { + for( i = 0; i < nNumBins; i++ ) + panHistValues[i] = (GUIntBig) ((double *) pabyWorkBuf)[i]; + } + else // source is 32bit integers + { + for( i = 0; i < nNumBins; i++ ) + panHistValues[i] = (GUIntBig) ((int *) pabyWorkBuf)[i]; + } + + CPLFree( pabyWorkBuf ); + pabyWorkBuf = NULL; + +/* -------------------------------------------------------------------- */ +/* Do we have unique values for the bins? */ +/* -------------------------------------------------------------------- */ + double *padfBinValues = NULL; + HFAEntry *poBinEntry = poBand->poNode->GetNamedChild( "Descriptor_Table.#Bin_Function840#" ); + + if( poBinEntry != NULL + && EQUAL(poBinEntry->GetType(),"Edsc_BinFunction840") + && EQUAL(poBinEntry->GetStringField( "binFunction.type.string" ), + "BFUnique") ) + padfBinValues = HFAReadBFUniqueBins( poBinEntry, nNumBins ); + + if( padfBinValues ) + { + int nMaxValue = 0; + int nMinValue = 1000000; + int bAllInteger = TRUE; + + for( i = 0; i < nNumBins; i++ ) + { + if( padfBinValues[i] != floor(padfBinValues[i]) ) + bAllInteger = FALSE; + + nMaxValue = MAX(nMaxValue,(int)padfBinValues[i]); + nMinValue = MIN(nMinValue,(int)padfBinValues[i]); + } + + if( nMinValue < 0 || nMaxValue > 1000 || !bAllInteger ) + { + CPLFree( padfBinValues ); + CPLFree( panHistValues ); + CPLDebug( "HFA", "Unable to offer histogram because unique values list is not convenient to reform as HISTOBINVALUES." ); + return; + } + + int nNewBins = nMaxValue + 1; + GUIntBig *panNewHistValues = (GUIntBig *) CPLCalloc(sizeof(GUIntBig),nNewBins); + + for( i = 0; i < nNumBins; i++ ) + panNewHistValues[(int) padfBinValues[i]] = panHistValues[i]; + + CPLFree( panHistValues ); + panHistValues = panNewHistValues; + nNumBins = nNewBins; + + SetMetadataItem( "STATISTICS_HISTOMIN", "0" ); + SetMetadataItem( "STATISTICS_HISTOMAX", + CPLString().Printf("%d", nMaxValue ) ); + SetMetadataItem( "STATISTICS_HISTONUMBINS", + CPLString().Printf("%d", nMaxValue+1 ) ); + + CPLFree(padfBinValues); + padfBinValues = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Format into HISTOBINVALUES text format. */ +/* -------------------------------------------------------------------- */ + unsigned int nBufSize = 1024; + char * pszBinValues = (char *)CPLMalloc( nBufSize ); + int nBinValuesLen = 0; + pszBinValues[0] = 0; + + for ( int nBin = 0; nBin < nNumBins; ++nBin ) + { + char szBuf[32]; + snprintf( szBuf, 31, CPL_FRMT_GUIB, panHistValues[nBin] ); + if ( ( nBinValuesLen + strlen( szBuf ) + 2 ) > nBufSize ) + { + nBufSize *= 2; + char* pszNewBinValues = (char *)VSIRealloc( pszBinValues, nBufSize ); + if (pszNewBinValues == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate memory"); + break; + } + pszBinValues = pszNewBinValues; + } + strcat( pszBinValues+nBinValuesLen, szBuf ); + strcat( pszBinValues+nBinValuesLen, "|" ); + nBinValuesLen += strlen(pszBinValues+nBinValuesLen); + } + + SetMetadataItem( "STATISTICS_HISTOBINVALUES", pszBinValues ); + CPLFree( panHistValues ); + CPLFree( pszBinValues ); +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double HFARasterBand::GetNoDataValue( int *pbSuccess ) + +{ + double dfNoData; + + if( HFAGetBandNoData( hHFA, nBand, &dfNoData ) ) + { + if( pbSuccess ) + *pbSuccess = TRUE; + return dfNoData; + } + else + return GDALPamRasterBand::GetNoDataValue( pbSuccess ); +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr HFARasterBand::SetNoDataValue( double dfValue ) +{ + return HFASetBandNoData( hHFA, nBand, dfValue ); +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double HFARasterBand::GetMinimum( int *pbSuccess ) + +{ + const char *pszValue = GetMetadataItem( "STATISTICS_MINIMUM" ); + + if( pszValue != NULL ) + { + if( pbSuccess ) + *pbSuccess = TRUE; + return CPLAtofM(pszValue); + } + else + { + return GDALRasterBand::GetMinimum( pbSuccess ); + } +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double HFARasterBand::GetMaximum( int *pbSuccess ) + +{ + const char *pszValue = GetMetadataItem( "STATISTICS_MAXIMUM" ); + + if( pszValue != NULL ) + { + if( pbSuccess ) + *pbSuccess = TRUE; + return CPLAtofM(pszValue); + } + else + { + return GDALRasterBand::GetMaximum( pbSuccess ); + } +} + +/************************************************************************/ +/* EstablishOverviews() */ +/* */ +/* Delayed population of overview information. */ +/************************************************************************/ + +void HFARasterBand::EstablishOverviews() + +{ + if( nOverviews != -1 ) + return; + + nOverviews = HFAGetOverviewCount( hHFA, nBand ); + if( nOverviews > 0 ) + { + papoOverviewBands = (HFARasterBand **) + CPLMalloc(sizeof(void*)*nOverviews); + + for( int iOvIndex = 0; iOvIndex < nOverviews; iOvIndex++ ) + { + papoOverviewBands[iOvIndex] = + new HFARasterBand( (HFADataset *) poDS, nBand, iOvIndex ); + if (papoOverviewBands[iOvIndex]->GetXSize() == 0) + { + delete papoOverviewBands[iOvIndex]; + papoOverviewBands[iOvIndex] = NULL; + } + } + } +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int HFARasterBand::GetOverviewCount() + +{ + EstablishOverviews(); + + if( nOverviews == 0 ) + return GDALRasterBand::GetOverviewCount(); + else + return nOverviews; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *HFARasterBand::GetOverview( int i ) + +{ + EstablishOverviews(); + + if( nOverviews == 0 ) + return GDALRasterBand::GetOverview( i ); + else if( i < 0 || i >= nOverviews ) + return NULL; + else + return papoOverviewBands[i]; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr HFARasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + CPLErr eErr; + + if( nThisOverview == -1 ) + eErr = HFAGetRasterBlockEx( hHFA, nBand, nBlockXOff, nBlockYOff, + pImage, + nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType) / 8) ); + else + { + eErr = HFAGetOverviewRasterBlockEx( hHFA, nBand, nThisOverview, + nBlockXOff, nBlockYOff, + pImage, + nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType) / 8)); + } + + if( eErr == CE_None && nHFADataType == EPT_u4 ) + { + GByte *pabyData = (GByte *) pImage; + + for( int ii = nBlockXSize * nBlockYSize - 2; ii >= 0; ii -= 2 ) + { + int k = ii>>1; + pabyData[ii+1] = (pabyData[k]>>4) & 0xf; + pabyData[ii] = (pabyData[k]) & 0xf; + } + } + if( eErr == CE_None && nHFADataType == EPT_u2 ) + { + GByte *pabyData = (GByte *) pImage; + + for( int ii = nBlockXSize * nBlockYSize - 4; ii >= 0; ii -= 4 ) + { + int k = ii>>2; + pabyData[ii+3] = (pabyData[k]>>6) & 0x3; + pabyData[ii+2] = (pabyData[k]>>4) & 0x3; + pabyData[ii+1] = (pabyData[k]>>2) & 0x3; + pabyData[ii] = (pabyData[k]) & 0x3; + } + } + if( eErr == CE_None && nHFADataType == EPT_u1) + { + GByte *pabyData = (GByte *) pImage; + + for( int ii = nBlockXSize * nBlockYSize - 1; ii >= 0; ii-- ) + { + if( (pabyData[ii>>3] & (1 << (ii & 0x7))) ) + pabyData[ii] = 1; + else + pabyData[ii] = 0; + } + } + + return eErr; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr HFARasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + GByte *pabyOutBuf = (GByte *) pImage; + +/* -------------------------------------------------------------------- */ +/* Do we need to pack 1/2/4 bit data? */ +/* -------------------------------------------------------------------- */ + if( nHFADataType == EPT_u1 + || nHFADataType == EPT_u2 + || nHFADataType == EPT_u4 ) + { + int nPixCount = nBlockXSize * nBlockYSize; + pabyOutBuf = (GByte *) VSIMalloc2(nBlockXSize, nBlockYSize); + if (pabyOutBuf == NULL) + return CE_Failure; + + if( nHFADataType == EPT_u1 ) + { + for( int ii = 0; ii < nPixCount - 7; ii += 8 ) + { + int k = ii>>3; + pabyOutBuf[k] = + (((GByte *) pImage)[ii] & 0x1) + | ((((GByte *) pImage)[ii+1]&0x1) << 1) + | ((((GByte *) pImage)[ii+2]&0x1) << 2) + | ((((GByte *) pImage)[ii+3]&0x1) << 3) + | ((((GByte *) pImage)[ii+4]&0x1) << 4) + | ((((GByte *) pImage)[ii+5]&0x1) << 5) + | ((((GByte *) pImage)[ii+6]&0x1) << 6) + | ((((GByte *) pImage)[ii+7]&0x1) << 7); + } + } + else if( nHFADataType == EPT_u2 ) + { + for( int ii = 0; ii < nPixCount - 3; ii += 4 ) + { + int k = ii>>2; + pabyOutBuf[k] = + (((GByte *) pImage)[ii] & 0x3) + | ((((GByte *) pImage)[ii+1]&0x3) << 2) + | ((((GByte *) pImage)[ii+2]&0x3) << 4) + | ((((GByte *) pImage)[ii+3]&0x3) << 6); + } + } + else if( nHFADataType == EPT_u4 ) + { + for( int ii = 0; ii < nPixCount - 1; ii += 2 ) + { + int k = ii>>1; + pabyOutBuf[k] = + (((GByte *) pImage)[ii] & 0xf) + | ((((GByte *) pImage)[ii+1]&0xf) << 4); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Actually write out. */ +/* -------------------------------------------------------------------- */ + CPLErr nRetCode; + + if( nThisOverview == -1 ) + nRetCode = HFASetRasterBlock( hHFA, nBand, nBlockXOff, nBlockYOff, + pabyOutBuf ); + else + nRetCode = HFASetOverviewRasterBlock( hHFA, nBand, nThisOverview, + nBlockXOff, nBlockYOff, + pabyOutBuf ); + + if( pabyOutBuf != pImage ) + CPLFree( pabyOutBuf ); + + return nRetCode; +} + +/************************************************************************/ +/* GetDescription() */ +/************************************************************************/ + +const char * HFARasterBand::GetDescription() const +{ + const char *pszName = HFAGetBandName( hHFA, nBand ); + + if( pszName == NULL ) + return GDALPamRasterBand::GetDescription(); + else + return pszName; +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ +void HFARasterBand::SetDescription( const char *pszName ) +{ + if( strlen(pszName) > 0 ) + HFASetBandName( hHFA, nBand, pszName ); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp HFARasterBand::GetColorInterpretation() + +{ + if( poCT != NULL ) + return GCI_PaletteIndex; + else + return GCI_Undefined; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *HFARasterBand::GetColorTable() + +{ + return poCT; +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr HFARasterBand::SetColorTable( GDALColorTable * poCTable ) + +{ + if( GetAccess() == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Unable to set color table on read-only file." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Special case if we are clearing the color table. */ +/* -------------------------------------------------------------------- */ + if( poCTable == NULL ) + { + delete poCT; + poCT = NULL; + + HFASetPCT( hHFA, nBand, 0, NULL, NULL, NULL, NULL ); + + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Write out the colortable, and update the configuration. */ +/* -------------------------------------------------------------------- */ + int nColors = poCTable->GetColorEntryCount(); + + double *padfRed, *padfGreen, *padfBlue, *padfAlpha; + + padfRed = (double *) CPLMalloc(sizeof(double) * nColors); + padfGreen = (double *) CPLMalloc(sizeof(double) * nColors); + padfBlue = (double *) CPLMalloc(sizeof(double) * nColors); + padfAlpha = (double *) CPLMalloc(sizeof(double) * nColors); + + for( int iColor = 0; iColor < nColors; iColor++ ) + { + GDALColorEntry sRGB; + + poCTable->GetColorEntryAsRGB( iColor, &sRGB ); + + padfRed[iColor] = sRGB.c1 / 255.0; + padfGreen[iColor] = sRGB.c2 / 255.0; + padfBlue[iColor] = sRGB.c3 / 255.0; + padfAlpha[iColor] = sRGB.c4 / 255.0; + } + + HFASetPCT( hHFA, nBand, nColors, + padfRed, padfGreen, padfBlue, padfAlpha); + + CPLFree( padfRed ); + CPLFree( padfGreen ); + CPLFree( padfBlue ); + CPLFree( padfAlpha ); + + if( poCT ) + delete poCT; + + poCT = poCTable->Clone(); + + return CE_None; +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr HFARasterBand::SetMetadata( char **papszMDIn, const char *pszDomain ) + +{ + bMetadataDirty = TRUE; + + return GDALPamRasterBand::SetMetadata( papszMDIn, pszDomain ); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr HFARasterBand::SetMetadataItem( const char *pszTag, const char *pszValue, + const char *pszDomain ) + +{ + bMetadataDirty = TRUE; + + return GDALPamRasterBand::SetMetadataItem( pszTag, pszValue, pszDomain ); +} + +/************************************************************************/ +/* CleanOverviews() */ +/************************************************************************/ + +CPLErr HFARasterBand::CleanOverviews() + +{ + if( nOverviews == 0 ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Clear our reference to overviews as bands. */ +/* -------------------------------------------------------------------- */ + int iOverview; + + for( iOverview = 0; iOverview < nOverviews; iOverview++ ) + delete papoOverviewBands[iOverview]; + + CPLFree( papoOverviewBands ); + papoOverviewBands = NULL; + nOverviews = 0; + +/* -------------------------------------------------------------------- */ +/* Search for any RRDNamesList and destroy it. */ +/* -------------------------------------------------------------------- */ + HFABand *poBand = hHFA->papoBand[nBand-1]; + HFAEntry *poEntry = poBand->poNode->GetNamedChild( "RRDNamesList" ); + if( poEntry != NULL ) + { + poEntry->RemoveAndDestroy(); + } + +/* -------------------------------------------------------------------- */ +/* Destroy and subsample layers under our band. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poChild; + for( poChild = poBand->poNode->GetChild(); + poChild != NULL; ) + { + HFAEntry *poNext = poChild->GetNext(); + + if( EQUAL(poChild->GetType(),"Eimg_Layer_SubSample") ) + poChild->RemoveAndDestroy(); + + poChild = poNext; + } + +/* -------------------------------------------------------------------- */ +/* Clean up dependent file if we are the last band under the */ +/* assumption there will be nothing else referencing it after */ +/* this. */ +/* -------------------------------------------------------------------- */ + if( hHFA->psDependent != hHFA && hHFA->psDependent != NULL ) + { + CPLString osFilename = + CPLFormFilename( hHFA->psDependent->pszPath, + hHFA->psDependent->pszFilename, NULL ); + + HFAClose( hHFA->psDependent ); + hHFA->psDependent = NULL; + + CPLDebug( "HFA", "Unlink(%s)", osFilename.c_str() ); + VSIUnlink( osFilename ); + } + + return CE_None; +} + +/************************************************************************/ +/* BuildOverviews() */ +/************************************************************************/ + +CPLErr HFARasterBand::BuildOverviews( const char *pszResampling, + int nReqOverviews, int *panOverviewList, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + int iOverview; + GDALRasterBand **papoOvBands; + int bNoRegen = FALSE; + + EstablishOverviews(); + + if( nThisOverview != -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to build overviews on an overview layer." ); + + return CE_Failure; + } + + if( nReqOverviews == 0 ) + return CleanOverviews(); + + papoOvBands = (GDALRasterBand **) CPLCalloc(sizeof(void*),nReqOverviews); + + if( EQUALN(pszResampling,"NO_REGEN:",9) ) + { + pszResampling += 9; + bNoRegen = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Loop over overview levels requested. */ +/* -------------------------------------------------------------------- */ + for( iOverview = 0; iOverview < nReqOverviews; iOverview++ ) + { +/* -------------------------------------------------------------------- */ +/* Find this overview level. */ +/* -------------------------------------------------------------------- */ + int i, iResult = -1, nReqOvLevel; + + nReqOvLevel = + GDALOvLevelAdjust2(panOverviewList[iOverview],nRasterXSize,nRasterYSize); + + for( i = 0; i < nOverviews && papoOvBands[iOverview] == NULL; i++ ) + { + int nThisOvLevel; + + if( papoOverviewBands[i] == NULL ) + { + CPLDebug("HFA", "Shouldn't happen happened at line %d", __LINE__); + continue; + } + + nThisOvLevel = GDALComputeOvFactor(papoOverviewBands[i]->GetXSize(), + GetXSize(), + papoOverviewBands[i]->GetYSize(), + GetYSize()); + + if( nReqOvLevel == nThisOvLevel ) + papoOvBands[iOverview] = papoOverviewBands[i]; + } + +/* -------------------------------------------------------------------- */ +/* If this overview level does not yet exist, create it now. */ +/* -------------------------------------------------------------------- */ + if( papoOvBands[iOverview] == NULL ) + { + iResult = HFACreateOverview( hHFA, nBand, + panOverviewList[iOverview], + pszResampling ); + if( iResult < 0 ) + return CE_Failure; + + if( papoOverviewBands == NULL && nOverviews == 0 && iResult > 0) + { + CPLDebug("HFA", "Shouldn't happen happened at line %d", __LINE__); + papoOverviewBands = (HFARasterBand **) + CPLCalloc( sizeof(void*), iResult ); + } + + nOverviews = iResult + 1; + papoOverviewBands = (HFARasterBand **) + CPLRealloc( papoOverviewBands, sizeof(void*) * nOverviews); + papoOverviewBands[iResult] = new HFARasterBand( + (HFADataset *) poDS, nBand, iResult ); + + papoOvBands[iOverview] = papoOverviewBands[iResult]; + } + + } + +/* -------------------------------------------------------------------- */ +/* Regenerate the overviews. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + + if( !bNoRegen ) + eErr = GDALRegenerateOverviews( (GDALRasterBandH) this, + nReqOverviews, + (GDALRasterBandH *) papoOvBands, + pszResampling, + pfnProgress, pProgressData ); + + CPLFree( papoOvBands ); + + return eErr; +} + +/************************************************************************/ +/* GetDefaultHistogram() */ +/************************************************************************/ + +CPLErr +HFARasterBand::GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig ** ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void *pProgressData) + +{ + if( GetMetadataItem( "STATISTICS_HISTOBINVALUES" ) != NULL + && GetMetadataItem( "STATISTICS_HISTOMIN" ) != NULL + && GetMetadataItem( "STATISTICS_HISTOMAX" ) != NULL ) + { + int i; + const char *pszNextBin; + const char *pszBinValues = + GetMetadataItem( "STATISTICS_HISTOBINVALUES" ); + + *pdfMin = CPLAtof(GetMetadataItem("STATISTICS_HISTOMIN")); + *pdfMax = CPLAtof(GetMetadataItem("STATISTICS_HISTOMAX")); + + *pnBuckets = 0; + for( i = 0; pszBinValues[i] != '\0'; i++ ) + { + if( pszBinValues[i] == '|' ) + (*pnBuckets)++; + } + + *ppanHistogram = (GUIntBig *) CPLCalloc(sizeof(GUIntBig),*pnBuckets); + + pszNextBin = pszBinValues; + for( i = 0; i < *pnBuckets; i++ ) + { + (*ppanHistogram)[i] = (GUIntBig) CPLAtoGIntBig(pszNextBin); + + while( *pszNextBin != '|' && *pszNextBin != '\0' ) + pszNextBin++; + if( *pszNextBin == '|' ) + pszNextBin++; + } + + // Adjust min/max to reflect outer edges of buckets. + double dfBucketWidth = (*pdfMax - *pdfMin) / (*pnBuckets-1); + *pdfMax += 0.5 * dfBucketWidth; + *pdfMin -= 0.5 * dfBucketWidth; + + return CE_None; + } + else + return GDALPamRasterBand::GetDefaultHistogram( pdfMin, pdfMax, + pnBuckets,ppanHistogram, + bForce, + pfnProgress, + pProgressData ); +} + +/************************************************************************/ +/* SetDefaultRAT() */ +/************************************************************************/ + +CPLErr HFARasterBand::SetDefaultRAT( const GDALRasterAttributeTable * poRAT ) + +{ + if( poRAT == NULL ) + return CE_Failure; + + return WriteNamedRAT( "Descriptor_Table", poRAT ); +} + +/************************************************************************/ +/* GetDefaultRAT() */ +/************************************************************************/ + +GDALRasterAttributeTable *HFARasterBand::GetDefaultRAT() + +{ + if( poDefaultRAT == NULL ) + poDefaultRAT = new HFARasterAttributeTable(this, "Descriptor_Table" ); + + return poDefaultRAT; +} + +/************************************************************************/ +/* WriteNamedRAT() */ +/************************************************************************/ + +CPLErr HFARasterBand::WriteNamedRAT( CPL_UNUSED const char *pszName, + CPL_UNUSED const GDALRasterAttributeTable *poRAT ) +{ +/* -------------------------------------------------------------------- */ +/* Find the requested table. */ +/* -------------------------------------------------------------------- */ + HFAEntry * poDT = hHFA->papoBand[nBand-1]->poNode->GetNamedChild( "Descriptor_Table" ); + if( poDT == NULL || !EQUAL(poDT->GetType(),"Edsc_Table") ) + poDT = new HFAEntry( hHFA->papoBand[nBand-1]->psInfo, + "Descriptor_Table", "Edsc_Table", + hHFA->papoBand[nBand-1]->poNode ); + + int nRowCount = poRAT->GetRowCount(); + + poDT->SetIntField( "numrows", nRowCount ); + /* Check if binning is set on this RAT */ + double dfBinSize, dfRow0Min; + if(poRAT->GetLinearBinning( &dfRow0Min, &dfBinSize)) + { + /* then it should have an Edsc_BinFunction */ + HFAEntry *poBinFunction = poDT->GetNamedChild( "#Bin_Function#" ); + if( poBinFunction == NULL || !EQUAL(poBinFunction->GetType(),"Edsc_BinFunction") ) + poBinFunction = new HFAEntry( hHFA->papoBand[nBand-1]->psInfo, + "#Bin_Function#", "Edsc_BinFunction", + poDT ); + + poBinFunction->SetStringField("binFunction", "direct"); + poBinFunction->SetDoubleField("minLimit",dfRow0Min); + poBinFunction->SetDoubleField("maxLimit",(nRowCount -1)*dfBinSize+dfRow0Min); + poBinFunction->SetIntField("numBins",nRowCount); + } + +/* -------------------------------------------------------------------- */ +/* Loop through each column in the RAT */ +/* -------------------------------------------------------------------- */ + for(int col = 0; col < poRAT->GetColumnCount(); col++) + { + const char *pszName = NULL; + + if( poRAT->GetUsageOfCol(col) == GFU_Red ) + { + pszName = "Red"; + } + else if( poRAT->GetUsageOfCol(col) == GFU_Green ) + { + pszName = "Green"; + } + else if( poRAT->GetUsageOfCol(col) == GFU_Blue ) + { + pszName = "Blue"; + } + else if( poRAT->GetUsageOfCol(col) == GFU_Alpha ) + { + pszName = "Opacity"; + } + else if( poRAT->GetUsageOfCol(col) == GFU_PixelCount ) + { + pszName = "Histogram"; + } + else if( poRAT->GetUsageOfCol(col) == GFU_Name ) + { + pszName = "Class_Names"; + } + else + { + pszName = poRAT->GetNameOfCol(col); + } + +/* -------------------------------------------------------------------- */ +/* Check to see if a column with pszName exists and create if */ +/* if necessary. */ +/* -------------------------------------------------------------------- */ + + HFAEntry *poColumn; + poColumn = poDT->GetNamedChild(pszName); + + if(poColumn == NULL || !EQUAL(poColumn->GetType(),"Edsc_Column")) + poColumn = new HFAEntry( hHFA->papoBand[nBand-1]->psInfo, + pszName, "Edsc_Column", + poDT ); + + + poColumn->SetIntField( "numRows", nRowCount ); + // color cols which are integer in GDAL are written as floats in HFA + int bIsColorCol = FALSE; + if( ( poRAT->GetUsageOfCol(col) == GFU_Red ) || ( poRAT->GetUsageOfCol(col) == GFU_Green ) || + ( poRAT->GetUsageOfCol(col) == GFU_Blue) || ( poRAT->GetUsageOfCol(col) == GFU_Alpha ) ) + { + bIsColorCol = TRUE; + } + + // write float also if a color column, or histogram + if( ( poRAT->GetTypeOfCol(col) == GFT_Real ) || bIsColorCol || (poRAT->GetUsageOfCol(col) == GFU_PixelCount) ) + { + int nOffset = HFAAllocateSpace( hHFA->papoBand[nBand-1]->psInfo, + nRowCount * sizeof(double) ); + poColumn->SetIntField( "columnDataPtr", nOffset ); + poColumn->SetStringField( "dataType", "real" ); + + double *padfColData = (double*)CPLCalloc( nRowCount, sizeof(double) ); + for( int i = 0; i < nRowCount; i++) + { + if( bIsColorCol ) + // stored 0..1 + padfColData[i] = poRAT->GetValueAsInt(i,col) / 255.0; + else + padfColData[i] = poRAT->GetValueAsDouble(i,col); + } +#ifdef CPL_MSB + GDALSwapWords( padfColData, 8, nRowCount, 8 ); +#endif + VSIFSeekL( hHFA->fp, nOffset, SEEK_SET ); + VSIFWriteL( padfColData, nRowCount, sizeof(double), hHFA->fp ); + CPLFree( padfColData ); + } + else if( poRAT->GetTypeOfCol(col) == GFT_String ) + { + unsigned int nMaxNumChars = 0, nNumChars; + /* find the length of the longest string */ + for( int i = 0; i < nRowCount; i++) + { + /* Include terminating byte */ + nNumChars = strlen(poRAT->GetValueAsString(i,col)) + 1; + if(nMaxNumChars < nNumChars) + { + nMaxNumChars = nNumChars; + } + } + + int nOffset = HFAAllocateSpace( hHFA->papoBand[nBand-1]->psInfo, + (nRowCount+1) * nMaxNumChars ); + poColumn->SetIntField( "columnDataPtr", nOffset ); + poColumn->SetStringField( "dataType", "string" ); + poColumn->SetIntField( "maxNumChars", nMaxNumChars ); + + char *pachColData = (char*)CPLCalloc(nRowCount+1,nMaxNumChars); + for( int i = 0; i < nRowCount; i++) + { + strcpy(&pachColData[nMaxNumChars*i],poRAT->GetValueAsString(i,col)); + } + VSIFSeekL( hHFA->fp, nOffset, SEEK_SET ); + VSIFWriteL( pachColData, nRowCount, nMaxNumChars, hHFA->fp ); + CPLFree( pachColData ); + } + else if (poRAT->GetTypeOfCol(col) == GFT_Integer) + { + int nOffset = HFAAllocateSpace( hHFA->papoBand[nBand-1]->psInfo, + nRowCount * sizeof(GInt32) ); + poColumn->SetIntField( "columnDataPtr", nOffset ); + poColumn->SetStringField( "dataType", "integer" ); + + GInt32 *panColData = (GInt32*)CPLCalloc(nRowCount, sizeof(GInt32)); + for( int i = 0; i < nRowCount; i++) + { + panColData[i] = poRAT->GetValueAsInt(i,col); + } +#ifdef CPL_MSB + GDALSwapWords( panColData, 4, nRowCount, 4 ); +#endif + VSIFSeekL( hHFA->fp, nOffset, SEEK_SET ); + VSIFWriteL( panColData, nRowCount, sizeof(GInt32), hHFA->fp ); + CPLFree( panColData ); + } + else + { + /* can't deal with any of the others yet */ + CPLError( CE_Failure, CPLE_NotSupported, + "Writing this data type in a column is not supported for this Raster Attribute Table."); + } + } + + return CE_None; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* HFADataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* HFADataset() */ +/************************************************************************/ + +HFADataset::HFADataset() + +{ + hHFA = NULL; + bGeoDirty = FALSE; + pszProjection = CPLStrdup(""); + bMetadataDirty = FALSE; + bIgnoreUTM = FALSE; + bForceToPEString = FALSE; + + nGCPCount = 0; +} + +/************************************************************************/ +/* ~HFADataset() */ +/************************************************************************/ + +HFADataset::~HFADataset() + +{ + FlushCache(); + +/* -------------------------------------------------------------------- */ +/* Destroy the raster bands if they exist. We forcably clean */ +/* them up now to avoid any effort to write to them after the */ +/* file is closed. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nBands && papoBands != NULL; i++ ) + { + if( papoBands[i] != NULL ) + delete papoBands[i]; + } + + CPLFree( papoBands ); + papoBands = NULL; + +/* -------------------------------------------------------------------- */ +/* Close the file */ +/* -------------------------------------------------------------------- */ + if( hHFA != NULL ) + { + HFAClose( hHFA ); + hHFA = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( pszProjection ); + if( nGCPCount > 0 ) + GDALDeinitGCPs( 36, asGCPList ); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void HFADataset::FlushCache() + +{ + GDALPamDataset::FlushCache(); + + if( eAccess != GA_Update ) + return; + + if( bGeoDirty ) + WriteProjection(); + + if( bMetadataDirty && GetMetadata() != NULL ) + { + HFASetMetadata( hHFA, 0, GetMetadata() ); + bMetadataDirty = FALSE; + } + + for( int iBand = 0; iBand < nBands; iBand++ ) + { + HFARasterBand *poBand = (HFARasterBand *) GetRasterBand(iBand+1); + if( poBand->bMetadataDirty && poBand->GetMetadata() != NULL ) + { + HFASetMetadata( hHFA, iBand+1, poBand->GetMetadata() ); + poBand->bMetadataDirty = FALSE; + } + } + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, asGCPList ); + } +} + +/************************************************************************/ +/* WriteProjection() */ +/************************************************************************/ + +CPLErr HFADataset::WriteProjection() + +{ + Eprj_Datum sDatum; + Eprj_ProParameters sPro; + Eprj_MapInfo sMapInfo; + OGRSpatialReference oSRS; + OGRSpatialReference *poGeogSRS = NULL; + int bHaveSRS; + char *pszP = pszProjection; + int bPEStringStored = FALSE; + + bGeoDirty = FALSE; + + if( pszProjection != NULL && strlen(pszProjection) > 0 + && oSRS.importFromWkt( &pszP ) == OGRERR_NONE ) + bHaveSRS = TRUE; + else + bHaveSRS = FALSE; + +/* -------------------------------------------------------------------- */ +/* Initialize projection and datum. */ +/* -------------------------------------------------------------------- */ + memset( &sPro, 0, sizeof(sPro) ); + memset( &sDatum, 0, sizeof(sDatum) ); + memset( &sMapInfo, 0, sizeof(sMapInfo) ); + +/* -------------------------------------------------------------------- */ +/* Collect datum information. */ +/* -------------------------------------------------------------------- */ + if( bHaveSRS ) + { + poGeogSRS = oSRS.CloneGeogCS(); + } + + if( poGeogSRS ) + { + int i; + + sDatum.datumname = (char *) poGeogSRS->GetAttrValue( "GEOGCS|DATUM" ); + if( sDatum.datumname == NULL ) + sDatum.datumname = (char*) ""; + + /* WKT to Imagine translation */ + for( i = 0; apszDatumMap[i] != NULL; i += 2 ) + { + if( EQUAL(sDatum.datumname,apszDatumMap[i+1]) ) + { + sDatum.datumname = (char *) apszDatumMap[i]; + break; + } + } + + /* Map some EPSG datum codes directly to Imagine names */ + int nGCS = poGeogSRS->GetEPSGGeogCS(); + + if( nGCS == 4326 ) + sDatum.datumname = (char*) "WGS 84"; + if( nGCS == 4322 ) + sDatum.datumname = (char*) "WGS 1972"; + if( nGCS == 4267 ) + sDatum.datumname = (char*) "NAD27"; + if( nGCS == 4269 ) + sDatum.datumname = (char*) "NAD83"; + if( nGCS == 4283 ) + sDatum.datumname = (char*) "GDA94"; + + if( poGeogSRS->GetTOWGS84( sDatum.params ) == OGRERR_NONE ) + sDatum.type = EPRJ_DATUM_PARAMETRIC; + else if( EQUAL(sDatum.datumname,"NAD27") ) + { + sDatum.type = EPRJ_DATUM_GRID; + sDatum.gridname = (char*) "nadcon.dat"; + } + else + { + /* we will default to this (effectively WGS84) for now */ + sDatum.type = EPRJ_DATUM_PARAMETRIC; + } + + /* Verify if we need to write a ESRI PE string */ + bPEStringStored = WritePeStringIfNeeded(&oSRS, hHFA); + + sPro.proSpheroid.sphereName = (char *) + poGeogSRS->GetAttrValue( "GEOGCS|DATUM|SPHEROID" ); + sPro.proSpheroid.a = poGeogSRS->GetSemiMajor(); + sPro.proSpheroid.b = poGeogSRS->GetSemiMinor(); + sPro.proSpheroid.radius = sPro.proSpheroid.a; + + double a2 = sPro.proSpheroid.a*sPro.proSpheroid.a; + double b2 = sPro.proSpheroid.b*sPro.proSpheroid.b; + + sPro.proSpheroid.eSquared = (a2-b2)/a2; + } + + if( sDatum.datumname == NULL ) + sDatum.datumname = (char*) ""; + if( sPro.proSpheroid.sphereName == NULL ) + sPro.proSpheroid.sphereName = (char*) ""; + +/* -------------------------------------------------------------------- */ +/* Recognise various projections. */ +/* -------------------------------------------------------------------- */ + const char * pszProjName = NULL; + + if( bHaveSRS ) + pszProjName = oSRS.GetAttrValue( "PROJCS|PROJECTION" ); + + if( bForceToPEString && !bPEStringStored ) + { + char *pszPEString = NULL; + oSRS.morphToESRI(); + oSRS.exportToWkt( &pszPEString ); + // need to transform this into ESRI format. + HFASetPEString( hHFA, pszPEString ); + CPLFree( pszPEString ); + + bPEStringStored = TRUE; + } + else if( pszProjName == NULL ) + { + if( bHaveSRS && oSRS.IsGeographic() ) + { + sPro.proNumber = EPRJ_LATLONG; + sPro.proName = (char*) "Geographic (Lat/Lon)"; + } + } + + /* FIXME/NOTDEF/TODO: Add State Plane */ + else if( !bIgnoreUTM && oSRS.GetUTMZone( NULL ) != 0 ) + { + int bNorth, nZone; + + nZone = oSRS.GetUTMZone( &bNorth ); + sPro.proNumber = EPRJ_UTM; + sPro.proName = (char*) "UTM"; + sPro.proZone = nZone; + if( bNorth ) + sPro.proParams[3] = 1.0; + else + sPro.proParams[3] = -1.0; + } + + else if( EQUAL(pszProjName,SRS_PT_ALBERS_CONIC_EQUAL_AREA) ) + { + sPro.proNumber = EPRJ_ALBERS_CONIC_EQUAL_AREA; + sPro.proName = (char*) "Albers Conical Equal Area"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1)*D2R; + sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_2)*D2R; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ) + { + sPro.proNumber = EPRJ_LAMBERT_CONFORMAL_CONIC; + sPro.proName = (char*) "Lambert Conformal Conic"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1)*D2R; + sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_2)*D2R; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_MERCATOR_1SP) + && oSRS.GetProjParm(SRS_PP_SCALE_FACTOR) == 1.0 ) + { + sPro.proNumber = EPRJ_MERCATOR; + sPro.proName = (char*) "Mercator"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_MERCATOR_1SP) ) + { + sPro.proNumber = EPRJ_MERCATOR_VARIANT_A; + sPro.proName = (char*) "Mercator (Variant A)"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN)*D2R; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR); + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_KROVAK) ) + { + sPro.proNumber = EPRJ_KROVAK; + sPro.proName = (char*) "Krovak"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR); + sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH)*D2R; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + sPro.proParams[9] = oSRS.GetProjParm(SRS_PP_PSEUDO_STD_PARALLEL_1); + + // XY plane rotation + sPro.proParams[8] = 0.0; + // X scale + sPro.proParams[10] = 1.0; + // Y scale + sPro.proParams[11] = 1.0; + } + else if( EQUAL(pszProjName,SRS_PT_POLAR_STEREOGRAPHIC) ) + { + sPro.proNumber = EPRJ_POLAR_STEREOGRAPHIC; + sPro.proName = (char*) "Polar Stereographic"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN)*D2R; + /* hopefully the scale factor is 1.0! */ + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_POLYCONIC) ) + { + sPro.proNumber = EPRJ_POLYCONIC; + sPro.proName = (char*) "Polyconic"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_EQUIDISTANT_CONIC) ) + { + sPro.proNumber = EPRJ_EQUIDISTANT_CONIC; + sPro.proName = (char*) "Equidistant Conic"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1)*D2R; + sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_2)*D2R; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + sPro.proParams[8] = 1.0; + } + else if( EQUAL(pszProjName,SRS_PT_TRANSVERSE_MERCATOR) ) + { + sPro.proNumber = EPRJ_TRANSVERSE_MERCATOR; + sPro.proName = (char*) "Transverse Mercator"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN)*D2R; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR,1.0); + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_STEREOGRAPHIC) ) + { + sPro.proNumber = EPRJ_STEREOGRAPHIC_EXTENDED; + sPro.proName = (char*) "Stereographic (Extended)"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR,1.0); + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) + { + sPro.proNumber = EPRJ_LAMBERT_AZIMUTHAL_EQUAL_AREA; + sPro.proName = (char*) "Lambert Azimuthal Equal-area"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_AZIMUTHAL_EQUIDISTANT) ) + { + sPro.proNumber = EPRJ_AZIMUTHAL_EQUIDISTANT; + sPro.proName = (char*) "Azimuthal Equidistant"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_GNOMONIC) ) + { + sPro.proNumber = EPRJ_GNOMONIC; + sPro.proName = (char*) "Gnomonic"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_ORTHOGRAPHIC) ) + { + sPro.proNumber = EPRJ_ORTHOGRAPHIC; + sPro.proName = (char*) "Orthographic"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_SINUSOIDAL) ) + { + sPro.proNumber = EPRJ_SINUSOIDAL; + sPro.proName = (char*) "Sinusoidal"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_EQUIRECTANGULAR) ) + { + sPro.proNumber = EPRJ_EQUIRECTANGULAR; + sPro.proName = (char*) "Equirectangular"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_MILLER_CYLINDRICAL) ) + { + sPro.proNumber = EPRJ_MILLER_CYLINDRICAL; + sPro.proName = (char*) "Miller Cylindrical"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER)*D2R; + /* hopefully the latitude is zero! */ + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_VANDERGRINTEN) ) + { + sPro.proNumber = EPRJ_VANDERGRINTEN; + sPro.proName = (char*) "Van der Grinten"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_HOTINE_OBLIQUE_MERCATOR) ) + { + sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR; + sPro.proName = (char*) "Oblique Mercator (Hotine)"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR,1.0); + sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH)*D2R; + /* hopefully the rectified grid angle is zero */ + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + sPro.proParams[12] = 1.0; + } + else if( EQUAL(pszProjName,SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER) ) + { + sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER; + sPro.proName = (char*) "Hotine Oblique Mercator Azimuth Center"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR,1.0); + sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH)*D2R; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + sPro.proParams[12] = 1.0; + } + else if( EQUAL(pszProjName,SRS_PT_ROBINSON) ) + { + sPro.proNumber = EPRJ_ROBINSON; + sPro.proName = (char*) "Robinson"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_MOLLWEIDE) ) + { + sPro.proNumber = EPRJ_MOLLWEIDE; + sPro.proName = (char*) "Mollweide"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_ECKERT_I) ) + { + sPro.proNumber = EPRJ_ECKERT_I; + sPro.proName = (char*) "Eckert I"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_ECKERT_II) ) + { + sPro.proNumber = EPRJ_ECKERT_II; + sPro.proName = (char*) "Eckert II"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_ECKERT_III) ) + { + sPro.proNumber = EPRJ_ECKERT_III; + sPro.proName = (char*) "Eckert III"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_ECKERT_IV) ) + { + sPro.proNumber = EPRJ_ECKERT_IV; + sPro.proName = (char*) "Eckert IV"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_ECKERT_V) ) + { + sPro.proNumber = EPRJ_ECKERT_V; + sPro.proName = (char*) "Eckert V"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_ECKERT_VI) ) + { + sPro.proNumber = EPRJ_ECKERT_VI; + sPro.proName = (char*) "Eckert VI"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_GALL_STEREOGRAPHIC) ) + { + sPro.proNumber = EPRJ_GALL_STEREOGRAPHIC; + sPro.proName = (char*) "Gall Stereographic"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_CASSINI_SOLDNER) ) + { + sPro.proNumber = EPRJ_CASSINI; + sPro.proName = (char*) "Cassini"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,SRS_PT_TWO_POINT_EQUIDISTANT) ) + { + sPro.proNumber = EPRJ_TWO_POINT_EQUIDISTANT; + sPro.proName = (char*) "Two_Point_Equidistant"; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + sPro.proParams[8] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_1, 0.0)*D2R; + sPro.proParams[9] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_1, 0.0)*D2R; + sPro.proParams[10] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_2, 60.0)*D2R; + sPro.proParams[11] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_2, 60.0)*D2R; + } + else if( EQUAL(pszProjName,SRS_PT_BONNE) ) + { + sPro.proNumber = EPRJ_BONNE; + sPro.proName = (char*) "Bonne"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,"Loximuthal") ) + { + sPro.proNumber = EPRJ_LOXIMUTHAL; + sPro.proName = (char*) "Loximuthal"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm("central_parallel")*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,"Quartic_Authalic") ) + { + sPro.proNumber = EPRJ_QUARTIC_AUTHALIC; + sPro.proName = (char*) "Quartic Authalic"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,"Winkel_I") ) + { + sPro.proNumber = EPRJ_WINKEL_I; + sPro.proName = (char*) "Winkel I"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,"Winkel_II") ) + { + sPro.proNumber = EPRJ_WINKEL_II; + sPro.proName = (char*) "Winkel II"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,"Behrmann") ) + { + sPro.proNumber = EPRJ_BEHRMANN; + sPro.proName = (char*) "Behrmann"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName,"Equidistant_Cylindrical") ) + { + sPro.proNumber = EPRJ_EQUIDISTANT_CYLINDRICAL; + sPro.proName = (char*) "Equidistant_Cylindrical"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1)*D2R; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName, SRS_PT_KROVAK) ) + { + sPro.proNumber = EPRJ_KROVAK; + sPro.proName = (char*) "Krovak"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0); + sPro.proParams[3] = oSRS.GetProjParm(SRS_PP_AZIMUTH)*D2R; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + sPro.proParams[8] = oSRS.GetProjParm("XY_Plane_Rotation", 0.0)*D2R; + sPro.proParams[9] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1)*D2R; + sPro.proParams[10] = oSRS.GetProjParm("X_Scale", 1.0); + sPro.proParams[11] = oSRS.GetProjParm("Y_Scale", 1.0); + } + else if( EQUAL(pszProjName, "Double_Stereographic") ) + { + sPro.proNumber = EPRJ_DOUBLE_STEREOGRAPHIC; + sPro.proName = (char*) "Double_Stereographic"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0); + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName, "Aitoff") ) + { + sPro.proNumber = EPRJ_AITOFF; + sPro.proName = (char*) "Aitoff"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName, "Craster_Parabolic") ) + { + sPro.proNumber = EPRJ_CRASTER_PARABOLIC; + sPro.proName = (char*) "Craster_Parabolic"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName, SRS_PT_CYLINDRICAL_EQUAL_AREA) ) + { + sPro.proNumber = EPRJ_CYLINDRICAL_EQUAL_AREA; + sPro.proName = (char*) "Cylindrical_Equal_Area"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1)*D2R; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName, "Flat_Polar_Quartic") ) + { + sPro.proNumber = EPRJ_FLAT_POLAR_QUARTIC; + sPro.proName = (char*) "Flat_Polar_Quartic"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName, "Times") ) + { + sPro.proNumber = EPRJ_TIMES; + sPro.proName = (char*) "Times"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName, "Winkel_Tripel") ) + { + sPro.proNumber = EPRJ_WINKEL_TRIPEL; + sPro.proName = (char*) "Winkel_Tripel"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_STANDARD_PARALLEL_1)*D2R; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName, "Hammer_Aitoff") ) + { + sPro.proNumber = EPRJ_HAMMER_AITOFF; + sPro.proName = (char*) "Hammer_Aitoff"; + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName, "Vertical_Near_Side_Perspective") ) + { + sPro.proNumber = EPRJ_VERTICAL_NEAR_SIDE_PERSPECTIVE; + sPro.proName = (char*) "Vertical_Near_Side_Perspective"; + sPro.proParams[2] = oSRS.GetProjParm("Height"); + sPro.proParams[4] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_CENTER, 75.0)*D2R; + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER, 40.0)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + } + else if( EQUAL(pszProjName, "Hotine_Oblique_Mercator_Two_Point_Center") ) + { + sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_CENTER; + sPro.proName = (char*) "Hotine_Oblique_Mercator_Two_Point_Center"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0); + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER, 40.0)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + sPro.proParams[8] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_1, 0.0)*D2R; + sPro.proParams[9] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_1, 0.0)*D2R; + sPro.proParams[10] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_2, 60.0)*D2R; + sPro.proParams[11] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_2, 60.0)*D2R; + } + else if( EQUAL(pszProjName, SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN) ) + { + sPro.proNumber = EPRJ_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN; + sPro.proName = (char*) "Hotine_Oblique_Mercator_Two_Point_Natural_Origin"; + sPro.proParams[2] = oSRS.GetProjParm(SRS_PP_SCALE_FACTOR, 1.0); + sPro.proParams[5] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_CENTER, 40.0)*D2R; + sPro.proParams[6] = oSRS.GetProjParm(SRS_PP_FALSE_EASTING); + sPro.proParams[7] = oSRS.GetProjParm(SRS_PP_FALSE_NORTHING); + sPro.proParams[8] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_1, 0.0)*D2R; + sPro.proParams[9] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_1, 0.0)*D2R; + sPro.proParams[10] = oSRS.GetProjParm(SRS_PP_LONGITUDE_OF_POINT_2, 60.0)*D2R; + sPro.proParams[11] = oSRS.GetProjParm(SRS_PP_LATITUDE_OF_POINT_2, 60.0)*D2R; + } + else if( EQUAL(pszProjName,"New_Zealand_Map_Grid") ) + { + sPro.proType = EPRJ_EXTERNAL; + sPro.proNumber = 0; + sPro.proExeName = (char*) EPRJ_EXTERNAL_NZMG; + sPro.proName = (char*) "New Zealand Map Grid"; + sPro.proZone = 0; + sPro.proParams[0] = 0; // false easting etc not stored in .img it seems + sPro.proParams[1] = 0; // always fixed by definition. + sPro.proParams[2] = 0; + sPro.proParams[3] = 0; + sPro.proParams[4] = 0; + sPro.proParams[5] = 0; + sPro.proParams[6] = 0; + sPro.proParams[7] = 0; + } + // Anything we can't map, we store as an ESRI PE_STRING + else if( oSRS.IsProjected() || oSRS.IsGeographic() ) + { + if(!bPEStringStored) + { + char *pszPEString = NULL; + oSRS.morphToESRI(); + oSRS.exportToWkt( &pszPEString ); + // need to transform this into ESRI format. + HFASetPEString( hHFA, pszPEString ); + CPLFree( pszPEString ); + bPEStringStored = TRUE; + } + } + else + { + CPLError( CE_Warning, CPLE_NotSupported, + "Projection %s not supported for translation to Imagine.", + pszProjName ); + } + +/* -------------------------------------------------------------------- */ +/* MapInfo */ +/* -------------------------------------------------------------------- */ + const char *pszPROJCS = oSRS.GetAttrValue( "PROJCS" ); + + if( pszPROJCS ) + sMapInfo.proName = (char *) pszPROJCS; + else if( bHaveSRS && sPro.proName != NULL ) + sMapInfo.proName = sPro.proName; + else + sMapInfo.proName = (char*) "Unknown"; + + sMapInfo.upperLeftCenter.x = + adfGeoTransform[0] + adfGeoTransform[1]*0.5; + sMapInfo.upperLeftCenter.y = + adfGeoTransform[3] + adfGeoTransform[5]*0.5; + + sMapInfo.lowerRightCenter.x = + adfGeoTransform[0] + adfGeoTransform[1] * (GetRasterXSize()-0.5); + sMapInfo.lowerRightCenter.y = + adfGeoTransform[3] + adfGeoTransform[5] * (GetRasterYSize()-0.5); + + sMapInfo.pixelSize.width = ABS(adfGeoTransform[1]); + sMapInfo.pixelSize.height = ABS(adfGeoTransform[5]); + +/* -------------------------------------------------------------------- */ +/* Handle units. Try to match up with a known name. */ +/* -------------------------------------------------------------------- */ + sMapInfo.units = (char*) "meters"; + + if( bHaveSRS && oSRS.IsGeographic() ) + sMapInfo.units = (char*) "dd"; + else if( bHaveSRS && oSRS.GetLinearUnits() != 1.0 ) + { + double dfClosestDiff = 100.0; + int iClosest=-1, iUnit; + char *pszUnitName = NULL; + double dfActualSize = oSRS.GetLinearUnits( &pszUnitName ); + + for( iUnit = 0; apszUnitMap[iUnit] != NULL; iUnit += 2 ) + { + if( fabs(CPLAtof(apszUnitMap[iUnit+1]) - dfActualSize) < dfClosestDiff ) + { + iClosest = iUnit; + dfClosestDiff = fabs(CPLAtof(apszUnitMap[iUnit+1])-dfActualSize); + } + } + + if( iClosest == -1 || fabs(dfClosestDiff/dfActualSize) > 0.0001 ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Unable to identify Erdas units matching %s/%gm,\n" + "output units will be wrong.", + pszUnitName, dfActualSize ); + } + else + sMapInfo.units = (char *) apszUnitMap[iClosest]; + + /* We need to convert false easting and northing to meters. */ + sPro.proParams[6] *= dfActualSize; + sPro.proParams[7] *= dfActualSize; + } + +/* -------------------------------------------------------------------- */ +/* Write out definitions. */ +/* -------------------------------------------------------------------- */ + if( adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0 ) + { + HFASetMapInfo( hHFA, &sMapInfo ); + } + else + { + HFASetGeoTransform( hHFA, + sMapInfo.proName, sMapInfo.units, + adfGeoTransform ); + } + + if( bHaveSRS && sPro.proName != NULL) + { + HFASetProParameters( hHFA, &sPro ); + HFASetDatum( hHFA, &sDatum ); + + if( !bPEStringStored ) + HFASetPEString( hHFA, "" ); + } + else if( !bPEStringStored ) + ClearSR(hHFA); + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + if( poGeogSRS != NULL ) + delete poGeogSRS; + + return CE_None; +} + +/************************************************************************/ +/* WritePeStringIfNeeded() */ +/************************************************************************/ +int WritePeStringIfNeeded(OGRSpatialReference* poSRS, HFAHandle hHFA) +{ + OGRBoolean ret = FALSE; + if(!poSRS || !hHFA) + return ret; + + const char *pszGEOGCS = poSRS->GetAttrValue( "GEOGCS" ); + const char *pszDatum = poSRS->GetAttrValue( "DATUM" ); + int gcsNameOffset = 0; + int datumNameOffset = 0; + if( pszGEOGCS == NULL ) + pszGEOGCS = ""; + if( pszDatum == NULL ) + pszDatum = ""; + if(strstr(pszGEOGCS, "GCS_")) + gcsNameOffset = strlen("GCS_"); + if(strstr(pszDatum, "D_")) + datumNameOffset = strlen("D_"); + + if(!EQUAL(pszGEOGCS+gcsNameOffset, pszDatum+datumNameOffset)) + ret = TRUE; + else + { + const char* name = poSRS->GetAttrValue("PRIMEM"); + if(name && !EQUAL(name,"Greenwich")) + ret = TRUE; + if(!ret) + { + OGR_SRSNode * poAUnits = poSRS->GetAttrNode( "GEOGCS|UNIT" ); + name = poAUnits->GetChild(0)->GetValue(); + if(name && !EQUAL(name,"Degree")) + ret = TRUE; + } + if(!ret) + { + name = poSRS->GetAttrValue("UNIT"); + if(name) + { + ret = TRUE; + for(int i=0; apszUnitMap[i] != NULL; i+=2) + if(EQUAL(name, apszUnitMap[i])) + ret = FALSE; + } + } + if(!ret) + { + int nGCS = poSRS->GetEPSGGeogCS(); + switch(nGCS) + { + case 4326: + if(!EQUAL(pszDatum+datumNameOffset, "WGS_84")) + ret = TRUE; + break; + case 4322: + if(!EQUAL(pszDatum+datumNameOffset, "WGS_72")) + ret = TRUE; + break; + case 4267: + if(!EQUAL(pszDatum+datumNameOffset, "North_America_1927")) + ret = TRUE; + break; + case 4269: + if(!EQUAL(pszDatum+datumNameOffset, "North_America_1983")) + ret = TRUE; + break; + } + } + } + if(ret) + { + char *pszPEString = NULL; + poSRS->morphToESRI(); + poSRS->exportToWkt( &pszPEString ); + HFASetPEString( hHFA, pszPEString ); + CPLFree( pszPEString ); + } + + return ret; +} + +/************************************************************************/ +/* ClearSR() */ +/************************************************************************/ +void ClearSR(HFAHandle hHFA) +{ + for( int iBand = 0; iBand < hHFA->nBands; iBand++ ) + { + HFAEntry *poMIEntry; + if( hHFA->papoBand[iBand]->poNode && (poMIEntry = hHFA->papoBand[iBand]->poNode->GetNamedChild("Projection")) != NULL ) + { + poMIEntry->MarkDirty(); + poMIEntry->SetIntField( "proType", 0 ); + poMIEntry->SetIntField( "proNumber", 0 ); + poMIEntry->SetStringField( "proExeName", ""); + poMIEntry->SetStringField( "proName", ""); + poMIEntry->SetIntField( "proZone", 0 ); + poMIEntry->SetDoubleField( "proParams[0]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[1]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[2]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[3]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[4]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[5]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[6]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[7]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[8]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[9]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[10]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[11]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[12]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[13]", 0.0 ); + poMIEntry->SetDoubleField( "proParams[14]", 0.0 ); + poMIEntry->SetStringField( "proSpheroid.sphereName", "" ); + poMIEntry->SetDoubleField( "proSpheroid.a", 0.0 ); + poMIEntry->SetDoubleField( "proSpheroid.b", 0.0 ); + poMIEntry->SetDoubleField( "proSpheroid.eSquared", 0.0 ); + poMIEntry->SetDoubleField( "proSpheroid.radius", 0.0 ); + HFAEntry* poDatumEntry = poMIEntry->GetNamedChild("Datum"); + if( poDatumEntry != NULL ) + { + poDatumEntry->MarkDirty(); + poDatumEntry->SetStringField( "datumname", "" ); + poDatumEntry->SetIntField( "type", 0 ); + poDatumEntry->SetDoubleField( "params[0]", 0.0 ); + poDatumEntry->SetDoubleField( "params[1]", 0.0 ); + poDatumEntry->SetDoubleField( "params[2]", 0.0 ); + poDatumEntry->SetDoubleField( "params[3]", 0.0 ); + poDatumEntry->SetDoubleField( "params[4]", 0.0 ); + poDatumEntry->SetDoubleField( "params[5]", 0.0 ); + poDatumEntry->SetDoubleField( "params[6]", 0.0 ); + poDatumEntry->SetStringField( "gridname", "" ); + } + poMIEntry->FlushToDisk(); + char* peStr = HFAGetPEString( hHFA ); + if( peStr != NULL && strlen(peStr) > 0 ) + HFASetPEString( hHFA, "" ); + } + } + return; +} + +/************************************************************************/ +/* ESRIToUSGSZone() */ +/* */ +/* Convert ESRI style state plane zones to USGS style state */ +/* plane zones. */ +/************************************************************************/ + +static int ESRIToUSGSZone( int nESRIZone ) + +{ + int nPairs = sizeof(anUsgsEsriZones) / (2*sizeof(int)); + int i; + + if( nESRIZone < 0 ) + return ABS(nESRIZone); + + for( i = 0; i < nPairs; i++ ) + { + if( anUsgsEsriZones[i*2+1] == nESRIZone ) + return anUsgsEsriZones[i*2]; + } + + return 0; +} + +/************************************************************************/ +/* PCSStructToWKT() */ +/* */ +/* Convert the datum, proparameters and mapinfo structures into */ +/* WKT format. */ +/************************************************************************/ + +char * +HFAPCSStructToWKT( const Eprj_Datum *psDatum, + const Eprj_ProParameters *psPro, + const Eprj_MapInfo *psMapInfo, + HFAEntry *poMapInformation ) + +{ + OGRSpatialReference oSRS; + char *pszNewProj = NULL; + +/* -------------------------------------------------------------------- */ +/* General case for Erdas style projections. */ +/* */ +/* We make a particular effort to adapt the mapinfo->proname as */ +/* the PROJCS[] name per #2422. */ +/* -------------------------------------------------------------------- */ + + if( psPro == NULL && psMapInfo != NULL ) + { + oSRS.SetLocalCS( psMapInfo->proName ); + } + + else if( psPro == NULL ) + { + return NULL; + } + + else if( psPro->proType == EPRJ_EXTERNAL ) + { + if( EQUALN(psPro->proExeName,EPRJ_EXTERNAL_NZMG,4) ) + { + /* -------------------------------------------------------------------- */ + /* handle NZMG which is an external projection see */ + /* http://www.linz.govt.nz/core/surveysystem/geodeticinfo\ */ + /* /datums-projections/projections/nzmg/index.html */ + /* -------------------------------------------------------------------- */ + /* Is there a better way that doesn't require hardcoding of these numbers? */ + oSRS.SetNZMG(-41.0,173.0,2510000,6023150); + } + else + { + oSRS.SetLocalCS( psPro->proName ); + } + } + + else if( psPro->proNumber != EPRJ_LATLONG + && psMapInfo != NULL ) + { + oSRS.SetProjCS( psMapInfo->proName ); + } + else if( psPro->proNumber != EPRJ_LATLONG ) + { + oSRS.SetProjCS( psPro->proName ); + } + +/* -------------------------------------------------------------------- */ +/* Handle units. It is important to deal with this first so */ +/* that the projection Set methods will automatically do */ +/* translation of linear values (like false easting) to PROJCS */ +/* units from meters. Erdas linear projection values are */ +/* always in meters. */ +/* -------------------------------------------------------------------- */ + int iUnitIndex = 0; + + if( oSRS.IsProjected() || oSRS.IsLocal() ) + { + const char *pszUnits = NULL; + + if( psMapInfo ) + pszUnits = psMapInfo->units; + else if( poMapInformation != NULL ) + pszUnits = poMapInformation->GetStringField( "units.string" ); + + if( pszUnits != NULL ) + { + for( iUnitIndex = 0; + apszUnitMap[iUnitIndex] != NULL; + iUnitIndex += 2 ) + { + if( EQUAL(apszUnitMap[iUnitIndex], pszUnits ) ) + break; + } + + if( apszUnitMap[iUnitIndex] == NULL ) + iUnitIndex = 0; + + oSRS.SetLinearUnits( pszUnits, + CPLAtof(apszUnitMap[iUnitIndex+1]) ); + } + else + oSRS.SetLinearUnits( SRS_UL_METER, 1.0 ); + } + + if( psPro == NULL ) + { + if( oSRS.IsLocal() ) + { + if( oSRS.exportToWkt( &pszNewProj ) == OGRERR_NONE ) + return pszNewProj; + else + { + pszNewProj = NULL; + return NULL; + } + } + else + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to work out ellipsoid and datum information. */ +/* -------------------------------------------------------------------- */ + const char *pszDatumName = psPro->proSpheroid.sphereName; + const char *pszEllipsoidName = psPro->proSpheroid.sphereName; + double dfInvFlattening; + + if( psDatum != NULL ) + { + int i; + + pszDatumName = psDatum->datumname; + + /* Imagine to WKT translation */ + for( i = 0; pszDatumName != NULL && apszDatumMap[i] != NULL; i += 2 ) + { + if( EQUAL(pszDatumName,apszDatumMap[i]) ) + { + pszDatumName = apszDatumMap[i+1]; + break; + } + } + } + + if( psPro->proSpheroid.a == 0.0 ) + ((Eprj_ProParameters *) psPro)->proSpheroid.a = 6378137.0; + if( psPro->proSpheroid.b == 0.0 ) + ((Eprj_ProParameters *) psPro)->proSpheroid.b = 6356752.3; + + dfInvFlattening = OSRCalcInvFlattening(psPro->proSpheroid.a, psPro->proSpheroid.b); + +/* -------------------------------------------------------------------- */ +/* Handle different projection methods. */ +/* -------------------------------------------------------------------- */ + switch( psPro->proNumber ) + { + case EPRJ_LATLONG: + break; + + case EPRJ_UTM: + // We change this to unnamed so that SetUTM will set the long + // UTM description. + oSRS.SetProjCS( "unnamed" ); + oSRS.SetUTM( psPro->proZone, psPro->proParams[3] >= 0.0 ); + + // The PCS name from the above function may be different with the input name. + // If there is a PCS name in psMapInfo that is different with + // the one in psPro, just use it as the PCS name. This case happens + // if the dataset's SR was written by the new GDAL. + if( psMapInfo && strlen(psMapInfo->proName) > 0 + && strlen(psPro->proName) > 0 + && !EQUAL(psMapInfo->proName, psPro->proName) ) + oSRS.SetProjCS( psMapInfo->proName ); + break; + + case EPRJ_STATE_PLANE: + { + char *pszUnitsName = NULL; + double dfLinearUnits = oSRS.GetLinearUnits( &pszUnitsName ); + + pszUnitsName = CPLStrdup( pszUnitsName ); + + /* Historically, hfa used esri state plane zone code. Try esri pe string first. */ + int zoneCode = ESRIToUSGSZone(psPro->proZone); + char nad[32]; + strcpy(nad, "HARN"); + if(psDatum) + strcpy(nad, psDatum->datumname); + char units[32]; + strcpy(units, "meters"); + if(psMapInfo) + strcpy(units, psMapInfo->units); + else if(pszUnitsName && strlen(pszUnitsName) > 0) + strcpy(units, pszUnitsName); + int proNu = 0; + if(psPro) + proNu = psPro->proNumber; + if(oSRS.ImportFromESRIStatePlaneWKT(zoneCode, nad, units, proNu) == OGRERR_NONE) + { + CPLFree( pszUnitsName ); + oSRS.morphFromESRI(); + oSRS.AutoIdentifyEPSG(); + oSRS.Fixup(); + if( oSRS.exportToWkt( &pszNewProj ) == OGRERR_NONE ) + return pszNewProj; + else + return NULL; + } + + /* Set state plane zone. Set NAD83/27 on basis of spheroid */ + oSRS.SetStatePlane( ESRIToUSGSZone(psPro->proZone), + fabs(psPro->proSpheroid.a - 6378137.0)< 1.0, + pszUnitsName, dfLinearUnits ); + + CPLFree( pszUnitsName ); + + // Same as the UTM, The following is needed. + if( psMapInfo && strlen(psMapInfo->proName) > 0 + && strlen(psPro->proName) > 0 + && !EQUAL(psMapInfo->proName, psPro->proName) ) + oSRS.SetProjCS( psMapInfo->proName ); + } + break; + + case EPRJ_ALBERS_CONIC_EQUAL_AREA: + oSRS.SetACEA( psPro->proParams[2]*R2D, psPro->proParams[3]*R2D, + psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_LAMBERT_CONFORMAL_CONIC: + // check the possible Wisconsin first + if(psDatum && psMapInfo && EQUAL(psDatum->datumname, "HARN")) + { + if(oSRS.ImportFromESRIWisconsinWKT("Lambert_Conformal_Conic", psPro->proParams[4]*R2D, psPro->proParams[5]*R2D, psMapInfo->units) == OGRERR_NONE) + { + oSRS.morphFromESRI(); + oSRS.AutoIdentifyEPSG(); + oSRS.Fixup(); + if( oSRS.exportToWkt( &pszNewProj ) == OGRERR_NONE ) + return pszNewProj; + } + } + oSRS.SetLCC( psPro->proParams[2]*R2D, psPro->proParams[3]*R2D, + psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_MERCATOR: + oSRS.SetMercator( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + 1.0, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_POLAR_STEREOGRAPHIC: + oSRS.SetPS( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + 1.0, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_POLYCONIC: + oSRS.SetPolyconic( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_EQUIDISTANT_CONIC: + double dfStdParallel2; + + if( psPro->proParams[8] != 0.0 ) + dfStdParallel2 = psPro->proParams[3]*R2D; + else + dfStdParallel2 = psPro->proParams[2]*R2D; + oSRS.SetEC( psPro->proParams[2]*R2D, dfStdParallel2, + psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_TRANSVERSE_MERCATOR: + case EPRJ_GAUSS_KRUGER: + // check the possible Wisconsin first + if(psDatum && psMapInfo && EQUAL(psDatum->datumname, "HARN")) + { + if(oSRS.ImportFromESRIWisconsinWKT("Transverse_Mercator", psPro->proParams[4]*R2D, psPro->proParams[5]*R2D, psMapInfo->units) == OGRERR_NONE) + { + oSRS.morphFromESRI(); + oSRS.AutoIdentifyEPSG(); + oSRS.Fixup(); + if( oSRS.exportToWkt( &pszNewProj ) == OGRERR_NONE ) + return pszNewProj; + } + } + oSRS.SetTM( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[2], + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_STEREOGRAPHIC: + oSRS.SetStereographic( psPro->proParams[5]*R2D,psPro->proParams[4]*R2D, + 1.0, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_LAMBERT_AZIMUTHAL_EQUAL_AREA: + oSRS.SetLAEA( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_AZIMUTHAL_EQUIDISTANT: + oSRS.SetAE( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_GNOMONIC: + oSRS.SetGnomonic( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_ORTHOGRAPHIC: + oSRS.SetOrthographic( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_SINUSOIDAL: + oSRS.SetSinusoidal( psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_PLATE_CARREE: + case EPRJ_EQUIRECTANGULAR: + oSRS.SetEquirectangular2( 0.0, + psPro->proParams[4]*R2D, + psPro->proParams[5]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_EQUIDISTANT_CYLINDRICAL: + oSRS.SetEquirectangular2( 0.0, + psPro->proParams[4]*R2D, + psPro->proParams[2]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_MILLER_CYLINDRICAL: + oSRS.SetMC( 0.0, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_VANDERGRINTEN: + oSRS.SetVDG( psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_HOTINE_OBLIQUE_MERCATOR: + if( psPro->proParams[12] > 0.0 ) + oSRS.SetHOM( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[3]*R2D, 0.0, + psPro->proParams[2], + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER: + oSRS.SetHOMAC( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[3]*R2D, 0.0, + psPro->proParams[2], + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_ROBINSON: + oSRS.SetRobinson( psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_MOLLWEIDE: + oSRS.SetMollweide( psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_GALL_STEREOGRAPHIC: + oSRS.SetGS( psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_ECKERT_I: + oSRS.SetEckert( 1, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_ECKERT_II: + oSRS.SetEckert( 2, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_ECKERT_III: + oSRS.SetEckert( 3, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_ECKERT_IV: + oSRS.SetEckert( 4, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_ECKERT_V: + oSRS.SetEckert( 5, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_ECKERT_VI: + oSRS.SetEckert( 6, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_CASSINI: + oSRS.SetCS( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_TWO_POINT_EQUIDISTANT: + oSRS.SetTPED( psPro->proParams[9] * R2D, + psPro->proParams[8] * R2D, + psPro->proParams[11] * R2D, + psPro->proParams[10] * R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_STEREOGRAPHIC_EXTENDED: + oSRS.SetStereographic( psPro->proParams[5]*R2D,psPro->proParams[4]*R2D, + psPro->proParams[2], + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_BONNE: + oSRS.SetBonne( psPro->proParams[2]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_LOXIMUTHAL: + { + oSRS.SetProjection( "Loximuthal" ); + oSRS.SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( "central_parallel", + psPro->proParams[5] * R2D ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_QUARTIC_AUTHALIC: + { + oSRS.SetProjection( "Quartic_Authalic" ); + oSRS.SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_WINKEL_I: + { + oSRS.SetProjection( "Winkel_I" ); + oSRS.SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, + psPro->proParams[2] * R2D ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_WINKEL_II: + { + oSRS.SetProjection( "Winkel_II" ); + oSRS.SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, + psPro->proParams[2] * R2D ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_BEHRMANN: + { + oSRS.SetProjection( "Behrmann" ); + oSRS.SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_KROVAK: + oSRS.SetKrovak( psPro->proParams[4]*R2D, psPro->proParams[5]*R2D, + psPro->proParams[3]*R2D, psPro->proParams[9]*R2D, + psPro->proParams[2], + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_DOUBLE_STEREOGRAPHIC: + { + oSRS.SetProjection( "Double_Stereographic" ); + oSRS.SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, + psPro->proParams[5] * R2D ); + oSRS.SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( SRS_PP_SCALE_FACTOR, psPro->proParams[2] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_AITOFF: + { + oSRS.SetProjection( "Aitoff" ); + oSRS.SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_CRASTER_PARABOLIC: + { + oSRS.SetProjection( "Craster_Parabolic" ); + oSRS.SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_CYLINDRICAL_EQUAL_AREA: + oSRS.SetCEA(psPro->proParams[2] * R2D, psPro->proParams[4] * R2D, + psPro->proParams[6], psPro->proParams[7]); + break; + + case EPRJ_FLAT_POLAR_QUARTIC: + { + oSRS.SetProjection( "Flat_Polar_Quartic" ); + oSRS.SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_TIMES: + { + oSRS.SetProjection( "Times" ); + oSRS.SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_WINKEL_TRIPEL: + { + oSRS.SetProjection( "Winkel_Tripel" ); + oSRS.SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, + psPro->proParams[2] * R2D ); + oSRS.SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_HAMMER_AITOFF: + { + oSRS.SetProjection( "Hammer_Aitoff" ); + oSRS.SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_VERTICAL_NEAR_SIDE_PERSPECTIVE: + { + oSRS.SetProjection( "Vertical_Near_Side_Perspective" ); + oSRS.SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, + psPro->proParams[5] * R2D ); + oSRS.SetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, + psPro->proParams[4] * R2D ); + oSRS.SetNormProjParm( "height", + psPro->proParams[2] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_CENTER: + { + oSRS.SetProjection( "Hotine_Oblique_Mercator_Twp_Point_Center" ); + oSRS.SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, + psPro->proParams[5] * R2D ); + oSRS.SetNormProjParm( SRS_PP_LATITUDE_OF_1ST_POINT, + psPro->proParams[9] * R2D ); + oSRS.SetNormProjParm( SRS_PP_LONGITUDE_OF_1ST_POINT, + psPro->proParams[8] * R2D ); + oSRS.SetNormProjParm( SRS_PP_LATITUDE_OF_2ND_POINT, + psPro->proParams[11] * R2D ); + oSRS.SetNormProjParm( SRS_PP_LONGITUDE_OF_2ND_POINT, + psPro->proParams[10] * R2D ); + oSRS.SetNormProjParm( SRS_PP_SCALE_FACTOR, + psPro->proParams[2] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_EASTING, psPro->proParams[6] ); + oSRS.SetNormProjParm( SRS_PP_FALSE_NORTHING, psPro->proParams[7] ); + } + break; + + case EPRJ_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN: + oSRS.SetHOM2PNO( psPro->proParams[5] * R2D, + psPro->proParams[8] * R2D, + psPro->proParams[9] * R2D, + psPro->proParams[10] * R2D, + psPro->proParams[11] * R2D, + psPro->proParams[2], + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_LAMBERT_CONFORMAL_CONIC_1SP: + oSRS.SetLCC1SP( psPro->proParams[3]*R2D, psPro->proParams[2]*R2D, + psPro->proParams[4], + psPro->proParams[5], psPro->proParams[6] ); + break; + + case EPRJ_MERCATOR_VARIANT_A: + oSRS.SetMercator( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + psPro->proParams[2], + psPro->proParams[6], psPro->proParams[7] ); + break; + + case EPRJ_PSEUDO_MERCATOR: // likely this is google mercator? + oSRS.SetMercator( psPro->proParams[5]*R2D, psPro->proParams[4]*R2D, + 1.0, + psPro->proParams[6], psPro->proParams[7] ); + break; + + default: + if( oSRS.IsProjected() ) + oSRS.GetRoot()->SetValue( "LOCAL_CS" ); + else + oSRS.SetLocalCS( psPro->proName ); + break; + } + +/* -------------------------------------------------------------------- */ +/* Try and set the GeogCS information. */ +/* -------------------------------------------------------------------- */ + if( oSRS.GetAttrNode("GEOGCS") == NULL + && oSRS.GetAttrNode("LOCAL_CS") == NULL ) + { + if( pszDatumName == NULL) + oSRS.SetGeogCS( pszDatumName, pszDatumName, pszEllipsoidName, + psPro->proSpheroid.a, dfInvFlattening ); + else if( EQUAL(pszDatumName,"WGS 84") + || EQUAL(pszDatumName,"WGS_1984") ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + else if( strstr(pszDatumName,"NAD27") != NULL + || EQUAL(pszDatumName,"North_American_Datum_1927") ) + oSRS.SetWellKnownGeogCS( "NAD27" ); + else if( strstr(pszDatumName,"NAD83") != NULL + || EQUAL(pszDatumName,"North_American_Datum_1983")) + oSRS.SetWellKnownGeogCS( "NAD83" ); + else + oSRS.SetGeogCS( pszDatumName, pszDatumName, pszEllipsoidName, + psPro->proSpheroid.a, dfInvFlattening ); + + if( psDatum != NULL && psDatum->type == EPRJ_DATUM_PARAMETRIC ) + { + oSRS.SetTOWGS84( psDatum->params[0], + psDatum->params[1], + psDatum->params[2], + psDatum->params[3], + psDatum->params[4], + psDatum->params[5], + psDatum->params[6] ); + } + } + +/* -------------------------------------------------------------------- */ +/* Try to insert authority information if possible. Fixup any */ +/* ordering oddities. */ +/* -------------------------------------------------------------------- */ + oSRS.AutoIdentifyEPSG(); + oSRS.Fixup(); + +/* -------------------------------------------------------------------- */ +/* Get the WKT representation of the coordinate system. */ +/* -------------------------------------------------------------------- */ + if( oSRS.exportToWkt( &pszNewProj ) == OGRERR_NONE ) + return pszNewProj; + else + { + return NULL; + } +} + +/************************************************************************/ +/* ReadProjection() */ +/************************************************************************/ + +CPLErr HFADataset::ReadProjection() + +{ + const Eprj_Datum *psDatum; + const Eprj_ProParameters *psPro; + const Eprj_MapInfo *psMapInfo; + OGRSpatialReference oSRS; + char *pszPE_COORDSYS; + +/* -------------------------------------------------------------------- */ +/* Special logic for PE string in ProjectionX node. */ +/* -------------------------------------------------------------------- */ + pszPE_COORDSYS = HFAGetPEString( hHFA ); + if( pszPE_COORDSYS != NULL + && strlen(pszPE_COORDSYS) > 0 + && oSRS.SetFromUserInput( pszPE_COORDSYS ) == OGRERR_NONE ) + { + CPLFree( pszPE_COORDSYS ); + + oSRS.morphFromESRI(); + oSRS.Fixup(); + + CPLFree( pszProjection ); + pszProjection = NULL; + oSRS.exportToWkt( &pszProjection ); + + return CE_None; + } + + CPLFree( pszPE_COORDSYS ); + +/* -------------------------------------------------------------------- */ +/* General case for Erdas style projections. */ +/* */ +/* We make a particular effort to adapt the mapinfo->proname as */ +/* the PROJCS[] name per #2422. */ +/* -------------------------------------------------------------------- */ + psDatum = HFAGetDatum( hHFA ); + psPro = HFAGetProParameters( hHFA ); + psMapInfo = HFAGetMapInfo( hHFA ); + + HFAEntry *poMapInformation = NULL; + if( psMapInfo == NULL ) + { + HFABand *poBand = hHFA->papoBand[0]; + poMapInformation = poBand->poNode->GetNamedChild("MapInformation"); + } + + CPLFree( pszProjection ); + + if((psMapInfo == NULL && poMapInformation == NULL) || + ((!psDatum || strlen(psDatum->datumname) == 0 || EQUAL(psDatum->datumname, "Unknown")) && + (!psPro || strlen(psPro->proName) == 0 || EQUAL(psPro->proName, "Unknown")) && + (psMapInfo && (strlen(psMapInfo->proName) == 0 || EQUAL(psMapInfo->proName, "Unknown"))) && + (!psPro || psPro->proZone == 0)) ) + { + pszProjection = CPLStrdup(""); + return CE_None; + } + + pszProjection = HFAPCSStructToWKT( psDatum, psPro, psMapInfo, + poMapInformation ); + + if( pszProjection != NULL ) + return CE_None; + else + { + pszProjection = CPLStrdup(""); + return CE_Failure; + } +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr HFADataset::IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + int i; + + if( GetAccess() == GA_ReadOnly ) + { + for( i = 0; i < nListBands; i++ ) + { + if (HFAGetOverviewCount(hHFA, panBandList[i]) > 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot add external overviews when there are already internal overviews"); + return CE_Failure; + } + } + + return GDALDataset::IBuildOverviews( pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); + } + + for( i = 0; i < nListBands; i++ ) + { + CPLErr eErr; + GDALRasterBand *poBand; + + void* pScaledProgressData = GDALCreateScaledProgress( + i * 1.0 / nListBands, (i + 1) * 1.0 / nListBands, + pfnProgress, pProgressData); + + poBand = GetRasterBand( panBandList[i] ); + + //GetRasterBand can return NULL + if(poBand == NULL) + { + CPLError(CE_Failure, CPLE_ObjectNull, + "GetRasterBand failed"); + return CE_Failure; + } + + eErr = + poBand->BuildOverviews( pszResampling, nOverviews, panOverviewList, + GDALScaledProgress, pScaledProgressData ); + + GDALDestroyScaledProgress(pScaledProgressData); + + if( eErr != CE_None ) + return eErr; + } + + return CE_None; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int HFADataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify that this is a HFA file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 15 + || !EQUALN((char *) poOpenInfo->pabyHeader,"EHFA_HEADER_TAG",15) ) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *HFADataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + HFAHandle hHFA; + int i; + +/* -------------------------------------------------------------------- */ +/* Verify that this is a HFA file. */ +/* -------------------------------------------------------------------- */ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + hHFA = HFAOpen( poOpenInfo->pszFilename, "r+" ); + else + hHFA = HFAOpen( poOpenInfo->pszFilename, "r" ); + + if( hHFA == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + HFADataset *poDS; + + poDS = new HFADataset(); + + poDS->hHFA = hHFA; + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Establish raster info. */ +/* -------------------------------------------------------------------- */ + HFAGetRasterInfo( hHFA, &poDS->nRasterXSize, &poDS->nRasterYSize, + &poDS->nBands ); + + if( poDS->nBands == 0 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open %s, it has zero usable bands.", + poOpenInfo->pszFilename ); + return NULL; + } + + if( poDS->nRasterXSize == 0 || poDS->nRasterYSize == 0 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open %s, it has no pixels.", + poOpenInfo->pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Get geotransform, or if that fails, try to find XForms to */ +/* build gcps, and metadata. */ +/* -------------------------------------------------------------------- */ + if( !HFAGetGeoTransform( hHFA, poDS->adfGeoTransform ) ) + { + Efga_Polynomial *pasPolyListForward = NULL; + Efga_Polynomial *pasPolyListReverse = NULL; + int nStepCount = + HFAReadXFormStack( hHFA, &pasPolyListForward, + &pasPolyListReverse ); + + if( nStepCount > 0 ) + { + poDS->UseXFormStack( nStepCount, + pasPolyListForward, + pasPolyListReverse ); + CPLFree( pasPolyListForward ); + CPLFree( pasPolyListReverse ); + } + } + +/* -------------------------------------------------------------------- */ +/* Get the projection. */ +/* -------------------------------------------------------------------- */ + poDS->ReadProjection(); + +/* -------------------------------------------------------------------- */ +/* Read the camera model as metadata, if present. */ +/* -------------------------------------------------------------------- */ + char **papszCM = HFAReadCameraModel( hHFA ); + + if( papszCM != NULL ) + { + poDS->SetMetadata( papszCM, "CAMERA_MODEL" ); + CSLDestroy( papszCM ); + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < poDS->nBands; i++ ) + { + poDS->SetBand( i+1, new HFARasterBand( poDS, i+1, -1 ) ); + } + +/* -------------------------------------------------------------------- */ +/* Collect GDAL custom Metadata, and "auxilary" metadata from */ +/* well known HFA structures for the bands. We defer this till */ +/* now to ensure that the bands are properly setup before */ +/* interacting with PAM. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < poDS->nBands; i++ ) + { + HFARasterBand *poBand = (HFARasterBand *) poDS->GetRasterBand( i+1 ); + + char **papszMD = HFAGetMetadata( hHFA, i+1 ); + if( papszMD != NULL ) + { + poBand->SetMetadata( papszMD ); + CSLDestroy( papszMD ); + } + + poBand->ReadAuxMetadata(); + poBand->ReadHistogramMetadata(); + } + +/* -------------------------------------------------------------------- */ +/* Check for GDAL style metadata. */ +/* -------------------------------------------------------------------- */ + char **papszMD = HFAGetMetadata( hHFA, 0 ); + if( papszMD != NULL ) + { + poDS->SetMetadata( papszMD ); + CSLDestroy( papszMD ); + } + +/* -------------------------------------------------------------------- */ +/* Check for dependent dataset value. */ +/* -------------------------------------------------------------------- */ + HFAInfo_t *psInfo = (HFAInfo_t *) hHFA; + HFAEntry *poEntry = psInfo->poRoot->GetNamedChild("DependentFile"); + if( poEntry != NULL ) + { + poDS->SetMetadataItem( "HFA_DEPENDENT_FILE", + poEntry->GetStringField( "dependent.string" ), + "HFA" ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Clear dirty metadata flags. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < poDS->nBands; i++ ) + { + HFARasterBand *poBand = (HFARasterBand *) poDS->GetRasterBand( i+1 ); + poBand->bMetadataDirty = FALSE; + } + poDS->bMetadataDirty = FALSE; + + return( poDS ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *HFADataset::GetProjectionRef() + +{ + return pszProjection; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr HFADataset::SetProjection( const char * pszNewProjection ) + +{ + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszNewProjection ); + bGeoDirty = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr HFADataset::SetMetadata( char **papszMDIn, const char *pszDomain ) + +{ + bMetadataDirty = TRUE; + + return GDALPamDataset::SetMetadata( papszMDIn, pszDomain ); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr HFADataset::SetMetadataItem( const char *pszTag, const char *pszValue, + const char *pszDomain ) + +{ + bMetadataDirty = TRUE; + + return GDALPamDataset::SetMetadataItem( pszTag, pszValue, pszDomain ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr HFADataset::GetGeoTransform( double * padfTransform ) + +{ + if( adfGeoTransform[0] != 0.0 + || adfGeoTransform[1] != 1.0 + || adfGeoTransform[2] != 0.0 + || adfGeoTransform[3] != 0.0 + || adfGeoTransform[4] != 0.0 + || adfGeoTransform[5] != 1.0 ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + return CE_None; + } + else + return GDALPamDataset::GetGeoTransform( padfTransform ); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr HFADataset::SetGeoTransform( double * padfTransform ) + +{ + memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 ); + bGeoDirty = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* IRasterIO() */ +/* */ +/* Multi-band raster io handler. Here we ensure that the block */ +/* based loading is used for spill file rasters. That is */ +/* because they are effectively pixel interleaved, so */ +/* processing all bands for a given block together avoid extra */ +/* seeks. */ +/************************************************************************/ + +CPLErr HFADataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + if( hHFA->papoBand[panBandMap[0]-1]->fpExternal != NULL + && nBandCount > 1 ) + return GDALDataset::BlockBasedRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); + else + return + GDALDataset::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); +} + +/************************************************************************/ +/* UseXFormStack() */ +/************************************************************************/ + +void HFADataset::UseXFormStack( int nStepCount, + Efga_Polynomial *pasPLForward, + Efga_Polynomial *pasPLReverse ) + +{ +/* -------------------------------------------------------------------- */ +/* Generate GCPs using the transform. */ +/* -------------------------------------------------------------------- */ + double dfXRatio, dfYRatio; + + nGCPCount = 0; + GDALInitGCPs( 36, asGCPList ); + + for( dfYRatio = 0.0; dfYRatio < 1.001; dfYRatio += 0.2 ) + { + for( dfXRatio = 0.0; dfXRatio < 1.001; dfXRatio += 0.2 ) + { + double dfLine = 0.5 + (GetRasterYSize()-1) * dfYRatio; + double dfPixel = 0.5 + (GetRasterXSize()-1) * dfXRatio; + int iGCP = nGCPCount; + + asGCPList[iGCP].dfGCPPixel = dfPixel; + asGCPList[iGCP].dfGCPLine = dfLine; + + asGCPList[iGCP].dfGCPX = dfPixel; + asGCPList[iGCP].dfGCPY = dfLine; + asGCPList[iGCP].dfGCPZ = 0.0; + + if( HFAEvaluateXFormStack( nStepCount, FALSE, pasPLReverse, + &(asGCPList[iGCP].dfGCPX), + &(asGCPList[iGCP].dfGCPY) ) ) + nGCPCount++; + } + } + +/* -------------------------------------------------------------------- */ +/* Store the transform as metadata. */ +/* -------------------------------------------------------------------- */ + int iStep, i; + + GDALMajorObject::SetMetadataItem( + "XFORM_STEPS", + CPLString().Printf("%d",nStepCount), + "XFORMS" ); + + for( iStep = 0; iStep < nStepCount; iStep++ ) + { + GDALMajorObject::SetMetadataItem( + CPLString().Printf("XFORM%d_ORDER", iStep), + CPLString().Printf("%d",pasPLForward[iStep].order), + "XFORMS" ); + + if( pasPLForward[iStep].order == 1 ) + { + for( i = 0; i < 4; i++ ) + GDALMajorObject::SetMetadataItem( + CPLString().Printf("XFORM%d_POLYCOEFMTX[%d]", iStep, i), + CPLString().Printf("%.15g", + pasPLForward[iStep].polycoefmtx[i]), + "XFORMS" ); + + for( i = 0; i < 2; i++ ) + GDALMajorObject::SetMetadataItem( + CPLString().Printf("XFORM%d_POLYCOEFVECTOR[%d]", iStep, i), + CPLString().Printf("%.15g", + pasPLForward[iStep].polycoefvector[i]), + "XFORMS" ); + + continue; + } + + int nCoefCount; + + if( pasPLForward[iStep].order == 2 ) + nCoefCount = 10; + else + { + CPLAssert( pasPLForward[iStep].order == 3 ); + nCoefCount = 18; + } + + for( i = 0; i < nCoefCount; i++ ) + GDALMajorObject::SetMetadataItem( + CPLString().Printf("XFORM%d_FWD_POLYCOEFMTX[%d]", iStep, i), + CPLString().Printf("%.15g", + pasPLForward[iStep].polycoefmtx[i]), + "XFORMS" ); + + for( i = 0; i < 2; i++ ) + GDALMajorObject::SetMetadataItem( + CPLString().Printf("XFORM%d_FWD_POLYCOEFVECTOR[%d]", iStep, i), + CPLString().Printf("%.15g", + pasPLForward[iStep].polycoefvector[i]), + "XFORMS" ); + + for( i = 0; i < nCoefCount; i++ ) + GDALMajorObject::SetMetadataItem( + CPLString().Printf("XFORM%d_REV_POLYCOEFMTX[%d]", iStep, i), + CPLString().Printf("%.15g", + pasPLReverse[iStep].polycoefmtx[i]), + "XFORMS" ); + + for( i = 0; i < 2; i++ ) + GDALMajorObject::SetMetadataItem( + CPLString().Printf("XFORM%d_REV_POLYCOEFVECTOR[%d]", iStep, i), + CPLString().Printf("%.15g", + pasPLReverse[iStep].polycoefvector[i]), + "XFORMS" ); + } +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int HFADataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *HFADataset::GetGCPProjection() + +{ + if( nGCPCount > 0 ) + return pszProjection; + else + return ""; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *HFADataset::GetGCPs() + +{ + return asGCPList; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **HFADataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + + if( HFAGetIGEFilename( hHFA ) != NULL ) + { + papszFileList = CSLAddString( papszFileList, + HFAGetIGEFilename( hHFA ) ); + } + + // Request an overview to force opening of dependent overview + // files. + if( nBands > 0 + && GetRasterBand(1)->GetOverviewCount() > 0 ) + GetRasterBand(1)->GetOverview(0); + + if( hHFA->psDependent != NULL ) + { + HFAInfo_t *psDep = hHFA->psDependent; + + papszFileList = + CSLAddString( papszFileList, + CPLFormFilename( psDep->pszPath, + psDep->pszFilename, NULL )); + + if( HFAGetIGEFilename( psDep ) != NULL ) + papszFileList = CSLAddString( papszFileList, + HFAGetIGEFilename( psDep ) ); + } + + return papszFileList; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *HFADataset::Create( const char * pszFilenameIn, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszParmList ) + +{ + int nHfaDataType; + int nBits = 0; + const char *pszPixelType; + + + if( CSLFetchNameValue( papszParmList, "NBITS" ) != NULL ) + nBits = atoi(CSLFetchNameValue(papszParmList,"NBITS")); + + pszPixelType = + CSLFetchNameValue( papszParmList, "PIXELTYPE" ); + if( pszPixelType == NULL ) + pszPixelType = ""; + +/* -------------------------------------------------------------------- */ +/* Translate the data type. */ +/* -------------------------------------------------------------------- */ + switch( eType ) + { + case GDT_Byte: + if( nBits == 1 ) + nHfaDataType = EPT_u1; + else if( nBits == 2 ) + nHfaDataType = EPT_u2; + else if( nBits == 4 ) + nHfaDataType = EPT_u4; + else if( EQUAL(pszPixelType,"SIGNEDBYTE") ) + nHfaDataType = EPT_s8; + else + nHfaDataType = EPT_u8; + break; + + case GDT_UInt16: + nHfaDataType = EPT_u16; + break; + + case GDT_Int16: + nHfaDataType = EPT_s16; + break; + + case GDT_Int32: + nHfaDataType = EPT_s32; + break; + + case GDT_UInt32: + nHfaDataType = EPT_u32; + break; + + case GDT_Float32: + nHfaDataType = EPT_f32; + break; + + case GDT_Float64: + nHfaDataType = EPT_f64; + break; + + case GDT_CFloat32: + nHfaDataType = EPT_c64; + break; + + case GDT_CFloat64: + nHfaDataType = EPT_c128; + break; + + default: + CPLError( CE_Failure, CPLE_NotSupported, + "Data type %s not supported by Erdas Imagine (HFA) format.\n", + GDALGetDataTypeName( eType ) ); + return NULL; + + } + +/* -------------------------------------------------------------------- */ +/* Create the new file. */ +/* -------------------------------------------------------------------- */ + HFAHandle hHFA; + + hHFA = HFACreate( pszFilenameIn, nXSize, nYSize, nBands, + nHfaDataType, papszParmList ); + if( hHFA == NULL ) + return NULL; + + HFAClose( hHFA ); + +/* -------------------------------------------------------------------- */ +/* Open the dataset normally. */ +/* -------------------------------------------------------------------- */ + HFADataset *poDS = (HFADataset *) GDALOpen( pszFilenameIn, GA_Update ); + +/* -------------------------------------------------------------------- */ +/* Special creation option to disable checking for UTM */ +/* parameters when writing the projection. This is a special */ +/* hack for sam.gillingham@nrm.qld.gov.au. */ +/* -------------------------------------------------------------------- */ + if( poDS != NULL ) + { + poDS->bIgnoreUTM = CSLFetchBoolean( papszParmList, "IGNOREUTM", + FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Sometimes we can improve ArcGIS compatability by forcing */ +/* generation of a PEString instead of traditional Imagine */ +/* coordinate system descriptions. */ +/* -------------------------------------------------------------------- */ + if( poDS != NULL ) + { + poDS->bForceToPEString = + CSLFetchBoolean( papszParmList, "FORCETOPESTRING", FALSE ); + } + + return poDS; + +} + +/************************************************************************/ +/* Rename() */ +/* */ +/* Custom Rename() implementation that knows how to update */ +/* filename references in .img and .aux files. */ +/************************************************************************/ + +CPLErr HFADataset::Rename( const char *pszNewName, const char *pszOldName ) + +{ +/* -------------------------------------------------------------------- */ +/* Rename all the files at the filesystem level. */ +/* -------------------------------------------------------------------- */ + GDALDriver *poDriver = (GDALDriver*) GDALGetDriverByName( "HFA" ); + + CPLErr eErr = poDriver->DefaultRename( pszNewName, pszOldName ); + + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Now try to go into the .img file and update RRDNames[] */ +/* lists. */ +/* -------------------------------------------------------------------- */ + CPLString osOldBasename, osNewBasename; + + osOldBasename = CPLGetBasename( pszOldName ); + osNewBasename = CPLGetBasename( pszNewName ); + + if( osOldBasename != osNewBasename ) + { + HFAHandle hHFA = HFAOpen( pszNewName, "r+" ); + + if( hHFA != NULL ) + { + eErr = HFARenameReferences( hHFA, osNewBasename, osOldBasename ); + + HFAGetOverviewCount( hHFA, 1 ); + + if( hHFA->psDependent != NULL ) + HFARenameReferences( hHFA->psDependent, + osNewBasename, osOldBasename ); + + HFAClose( hHFA ); + } + } + + return eErr; +} + +/************************************************************************/ +/* CopyFiles() */ +/* */ +/* Custom CopyFiles() implementation that knows how to update */ +/* filename references in .img and .aux files. */ +/************************************************************************/ + +CPLErr HFADataset::CopyFiles( const char *pszNewName, const char *pszOldName ) + +{ +/* -------------------------------------------------------------------- */ +/* Rename all the files at the filesystem level. */ +/* -------------------------------------------------------------------- */ + GDALDriver *poDriver = (GDALDriver*) GDALGetDriverByName( "HFA" ); + + CPLErr eErr = poDriver->DefaultCopyFiles( pszNewName, pszOldName ); + + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Now try to go into the .img file and update RRDNames[] */ +/* lists. */ +/* -------------------------------------------------------------------- */ + CPLString osOldBasename, osNewBasename; + + osOldBasename = CPLGetBasename( pszOldName ); + osNewBasename = CPLGetBasename( pszNewName ); + + if( osOldBasename != osNewBasename ) + { + HFAHandle hHFA = HFAOpen( pszNewName, "r+" ); + + if( hHFA != NULL ) + { + eErr = HFARenameReferences( hHFA, osNewBasename, osOldBasename ); + + HFAGetOverviewCount( hHFA, 1 ); + + if( hHFA->psDependent != NULL ) + HFARenameReferences( hHFA->psDependent, + osNewBasename, osOldBasename ); + + HFAClose( hHFA ); + } + } + + return eErr; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * +HFADataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + CPL_UNUSED int bStrict, + char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) +{ + HFADataset *poDS; + GDALDataType eType = GDT_Byte; + int iBand; + int nBandCount = poSrcDS->GetRasterCount(); + char **papszModOptions = CSLDuplicate( papszOptions ); + +/* -------------------------------------------------------------------- */ +/* Do we really just want to create an .aux file? */ +/* -------------------------------------------------------------------- */ + int bCreateAux = CSLFetchBoolean( papszOptions, "AUX", FALSE ); + +/* -------------------------------------------------------------------- */ +/* Establish a representative data type to use. */ +/* -------------------------------------------------------------------- */ + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterBand *poBand = poSrcDS->GetRasterBand( iBand+1 ); + eType = GDALDataTypeUnion( eType, poBand->GetRasterDataType() ); + } + +/* -------------------------------------------------------------------- */ +/* If we have PIXELTYPE metadadata in the source, pass it */ +/* through as a creation option. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue( papszOptions, "PIXELTYPE" ) == NULL + && nBandCount > 0 + && eType == GDT_Byte + && poSrcDS->GetRasterBand(1)->GetMetadataItem( "PIXELTYPE", + "IMAGE_STRUCTURE" ) ) + { + papszModOptions = + CSLSetNameValue( papszModOptions, "PIXELTYPE", + poSrcDS->GetRasterBand(1)->GetMetadataItem( + "PIXELTYPE", "IMAGE_STRUCTURE" ) ); + } + +/* -------------------------------------------------------------------- */ +/* Create the file. */ +/* -------------------------------------------------------------------- */ + poDS = (HFADataset *) Create( pszFilename, + poSrcDS->GetRasterXSize(), + poSrcDS->GetRasterYSize(), + nBandCount, + eType, papszModOptions ); + + CSLDestroy( papszModOptions ); + + if( poDS == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Does the source have a PCT for any of the bands? If so, */ +/* copy it over. */ +/* -------------------------------------------------------------------- */ + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterBand *poBand = poSrcDS->GetRasterBand( iBand+1 ); + GDALColorTable *poCT; + + poCT = poBand->GetColorTable(); + if( poCT != NULL ) + { + poDS->GetRasterBand(iBand+1)->SetColorTable(poCT); + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have metadata for any of the bands or the dataset as a */ +/* whole? */ +/* -------------------------------------------------------------------- */ + if( poSrcDS->GetMetadata() != NULL ) + poDS->SetMetadata( poSrcDS->GetMetadata() ); + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + int bSuccess; + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + GDALRasterBand *poDstBand = poDS->GetRasterBand(iBand+1); + + if( poSrcBand->GetMetadata() != NULL ) + poDstBand->SetMetadata( poSrcBand->GetMetadata() ); + + if( strlen(poSrcBand->GetDescription()) > 0 ) + poDstBand->SetDescription( poSrcBand->GetDescription() ); + + double dfNoDataValue = poSrcBand->GetNoDataValue( &bSuccess ); + if( bSuccess ) + poDstBand->SetNoDataValue( dfNoDataValue ); + } + +/* -------------------------------------------------------------------- */ +/* Copy projection information. */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + const char *pszProj; + + if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None + && (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 + || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 + || adfGeoTransform[4] != 0.0 || fabs(adfGeoTransform[5]) != 1.0)) + poDS->SetGeoTransform( adfGeoTransform ); + + pszProj = poSrcDS->GetProjectionRef(); + if( pszProj != NULL && strlen(pszProj) > 0 ) + poDS->SetProjection( pszProj ); + +/* -------------------------------------------------------------------- */ +/* Copy the imagery. */ +/* -------------------------------------------------------------------- */ + if( !bCreateAux ) + { + CPLErr eErr; + + eErr = GDALDatasetCopyWholeRaster( (GDALDatasetH) poSrcDS, + (GDALDatasetH) poDS, + NULL, pfnProgress, pProgressData ); + + if( eErr != CE_None ) + { + delete poDS; + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Do we want to generate statistics and a histogram? */ +/* -------------------------------------------------------------------- */ + if( CSLFetchBoolean( papszOptions, "STATISTICS", FALSE ) ) + { + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + double dfMin, dfMax, dfMean, dfStdDev; + char **papszStatsMD = NULL; + + // ----------------------------------------------------------- + // Statistics + // ----------------------------------------------------------- + + if( poSrcBand->GetStatistics( TRUE, FALSE, &dfMin, &dfMax, + &dfMean, &dfStdDev ) == CE_None + || poSrcBand->ComputeStatistics( TRUE, &dfMin, &dfMax, + &dfMean, &dfStdDev, + pfnProgress, pProgressData ) + == CE_None ) + { + CPLString osValue; + + papszStatsMD = + CSLSetNameValue( papszStatsMD, "STATISTICS_MINIMUM", + osValue.Printf( "%.15g", dfMin ) ); + papszStatsMD = + CSLSetNameValue( papszStatsMD, "STATISTICS_MAXIMUM", + osValue.Printf( "%.15g", dfMax ) ); + papszStatsMD = + CSLSetNameValue( papszStatsMD, "STATISTICS_MEAN", + osValue.Printf( "%.15g", dfMean ) ); + papszStatsMD = + CSLSetNameValue( papszStatsMD, "STATISTICS_STDDEV", + osValue.Printf( "%.15g", dfStdDev ) ); + } + + // ----------------------------------------------------------- + // Histogram + // ----------------------------------------------------------- + + int nBuckets; + GUIntBig *panHistogram = NULL; + + if( poSrcBand->GetDefaultHistogram( &dfMin, &dfMax, + &nBuckets, &panHistogram, + TRUE, + pfnProgress, pProgressData ) + == CE_None ) + { + CPLString osValue; + char *pszBinValues = (char *) CPLCalloc(20,nBuckets+1); + int iBin, nBinValuesLen = 0; + double dfBinWidth = (dfMax - dfMin) / nBuckets; + + papszStatsMD = CSLSetNameValue( + papszStatsMD, "STATISTICS_HISTOMIN", + osValue.Printf( "%.15g", dfMin+dfBinWidth*0.5 ) ); + papszStatsMD = CSLSetNameValue( + papszStatsMD, "STATISTICS_HISTOMAX", + osValue.Printf( "%.15g", dfMax-dfBinWidth*0.5 ) ); + papszStatsMD = + CSLSetNameValue( papszStatsMD, "STATISTICS_HISTONUMBINS", + osValue.Printf( "%d", nBuckets ) ); + + for( iBin = 0; iBin < nBuckets; iBin++ ) + { + + strcat( pszBinValues+nBinValuesLen, + osValue.Printf( CPL_FRMT_GUIB, panHistogram[iBin]) ); + strcat( pszBinValues+nBinValuesLen, "|" ); + nBinValuesLen += strlen(pszBinValues+nBinValuesLen); + } + papszStatsMD = + CSLSetNameValue( papszStatsMD, "STATISTICS_HISTOBINVALUES", + pszBinValues ); + CPLFree( pszBinValues ); + } + + CPLFree(panHistogram); + + if( CSLCount(papszStatsMD) > 0 ) + HFASetMetadata( poDS->hHFA, iBand+1, papszStatsMD ); + + CSLDestroy( papszStatsMD ); + } + } + +/* -------------------------------------------------------------------- */ +/* All report completion. */ +/* -------------------------------------------------------------------- */ + if( !pfnProgress( 1.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated" ); + delete poDS; + + GDALDriver *poHFADriver = + (GDALDriver *) GDALGetDriverByName( "HFA" ); + poHFADriver->Delete( pszFilename ); + return NULL; + } + + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_HFA() */ +/************************************************************************/ + +void GDALRegister_HFA() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "HFA" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "HFA" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Erdas Imagine Images (.img)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_hfa.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "img" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 Float32 Float64 CFloat32 CFloat64" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = HFADataset::Open; + poDriver->pfnCreate = HFADataset::Create; + poDriver->pfnCreateCopy = HFADataset::CreateCopy; + poDriver->pfnIdentify = HFADataset::Identify; + poDriver->pfnRename = HFADataset::Rename; + poDriver->pfnCopyFiles = HFADataset::CopyFiles; + + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/hfa/hfadictionary.cpp b/bazaar/plugin/gdal/frmts/hfa/hfadictionary.cpp new file mode 100644 index 000000000..ed26cb9c0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/hfadictionary.cpp @@ -0,0 +1,276 @@ +/****************************************************************************** + * $Id: hfadictionary.cpp 21184 2010-12-01 03:11:03Z warmerdam $ + * + * Project: Erdas Imagine (.img) Translator + * Purpose: Implementation of the HFADictionary class for managing the + * dictionary read from the HFA file. Most work done by the + * HFAType, and HFAField classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Intergraph Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "hfa_p.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: hfadictionary.cpp 21184 2010-12-01 03:11:03Z warmerdam $"); + +static const char *apszDefDefn[] = { + + "Edsc_Table", + "{1:lnumrows,}Edsc_Table", + + "Edsc_Column", + "{1:lnumRows,1:LcolumnDataPtr,1:e4:integer,real,complex,string,dataType,1:lmaxNumChars,}Edsc_Column", + + "Eprj_Size", + "{1:dwidth,1:dheight,}Eprj_Size", + + "Eprj_Coordinate", + "{1:dx,1:dy,}Eprj_Coordinate", + + "Eprj_MapInfo", + "{0:pcproName,1:*oEprj_Coordinate,upperLeftCenter,1:*oEprj_Coordinate,lowerRightCenter,1:*oEprj_Size,pixelSize,0:pcunits,}Eprj_MapInfo", + + "Eimg_StatisticsParameters830", + "{0:poEmif_String,LayerNames,1:*bExcludedValues,1:oEmif_String,AOIname,1:lSkipFactorX,1:lSkipFactorY,1:*oEdsc_BinFunction,BinFunction,}Eimg_StatisticsParameters830", + + "Esta_Statistics", + "{1:dminimum,1:dmaximum,1:dmean,1:dmedian,1:dmode,1:dstddev,}Esta_Statistics", + + "Edsc_BinFunction", + "{1:lnumBins,1:e4:direct,linear,logarithmic,explicit,binFunctionType,1:dminLimit,1:dmaxLimit,1:*bbinLimits,}Edsc_BinFunction", + + "Eimg_NonInitializedValue", + "{1:*bvalueBD,}Eimg_NonInitializedValue", + + "Eprj_MapProjection842", + "{1:x{1:x{0:pcstring,}Emif_String,type,1:x{0:pcstring,}Emif_String,MIFDictionary,0:pCMIFObject,}Emif_MIFObject,projection,1:x{0:pcstring,}Emif_String,title,}Eprj_MapProjection842", + + "Emif_MIFObject", + "{1:x{0:pcstring,}Emif_String,type,1:x{0:pcstring,}Emif_String,MIFDictionary,0:pCMIFObject,}Emif_MIFObject", + + "Eprj_ProParameters", + "{1:e2:EPRJ_INTERNAL,EPRJ_EXTERNAL,proType,1:lproNumber,0:pcproExeName,0:pcproName,1:lproZone,0:pdproParams,1:*oEprj_Spheroid,proSpheroid,}Eprj_ProParameters", + + "Eprj_Datum", + "{0:pcdatumname,1:e3:EPRJ_DATUM_PARAMETRIC,EPRJ_DATUM_GRID,EPRJ_DATUM_REGRESSION,type,0:pdparams,0:pcgridname,}Eprj_Datum", + + "Eprj_Spheroid", + "{0:pcsphereName,1:da,1:db,1:deSquared,1:dradius,}Eprj_Spheroid", + + NULL, + NULL }; + + + +/************************************************************************/ +/* ==================================================================== */ +/* HFADictionary */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* HFADictionary() */ +/************************************************************************/ + +HFADictionary::HFADictionary( const char * pszString ) + +{ + int i; + + nTypes = 0; + nTypesMax = 0; + papoTypes = NULL; + + osDictionaryText = pszString; + bDictionaryTextDirty = FALSE; + +/* -------------------------------------------------------------------- */ +/* Read all the types. */ +/* -------------------------------------------------------------------- */ + while( pszString != NULL && *pszString != '.' ) + { + HFAType *poNewType; + + poNewType = new HFAType(); + pszString = poNewType->Initialize( pszString ); + + if( pszString != NULL ) + AddType( poNewType ); + else + delete poNewType; + } + +/* -------------------------------------------------------------------- */ +/* Complete the definitions. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nTypes; i++ ) + { + papoTypes[i]->CompleteDefn( this ); + } +} + +/************************************************************************/ +/* ~HFADictionary() */ +/************************************************************************/ + +HFADictionary::~HFADictionary() + +{ + int i; + + for( i = 0; i < nTypes; i++ ) + delete papoTypes[i]; + + CPLFree( papoTypes ); +} + +/************************************************************************/ +/* AddType() */ +/************************************************************************/ + +void HFADictionary::AddType( HFAType *poType ) + +{ + if( nTypes == nTypesMax ) + { + nTypesMax = nTypes * 2 + 10; + papoTypes = (HFAType **) CPLRealloc( papoTypes, + sizeof(void*) * nTypesMax ); + } + + papoTypes[nTypes++] = poType; +} + +/************************************************************************/ +/* FindType() */ +/************************************************************************/ + +HFAType * HFADictionary::FindType( const char * pszName ) + +{ + int i; + + for( i = 0; i < nTypes; i++ ) + { + if( papoTypes[i]->pszTypeName != NULL && + strcmp(pszName,papoTypes[i]->pszTypeName) == 0 ) + return( papoTypes[i] ); + } + +/* -------------------------------------------------------------------- */ +/* Check if this is a type have other knowledge of. If so, add */ +/* it to the dictionary now. I'm not sure how some files end */ +/* up being distributed using types not in the dictionary. */ +/* -------------------------------------------------------------------- */ + for( i = 0; apszDefDefn[i] != NULL; i += 2 ) + { + if( strcmp( pszName, apszDefDefn[i] ) == 0 ) + { + HFAType *poNewType = new HFAType(); + + poNewType->Initialize( apszDefDefn[i+1] ); + AddType( poNewType ); + poNewType->CompleteDefn( this ); + + if( osDictionaryText.size() > 0 ) + osDictionaryText.erase( osDictionaryText.size() - 1, 1 ); + osDictionaryText += apszDefDefn[i+1]; + osDictionaryText += ",."; + + bDictionaryTextDirty = TRUE; + + return poNewType; + } + } + + return NULL; +} + +/************************************************************************/ +/* GetItemSize() */ +/* */ +/* Get the size of a basic (atomic) item. */ +/************************************************************************/ + +int HFADictionary::GetItemSize( char chType ) + +{ + switch( chType ) + { + case '1': + case '2': + case '4': + case 'c': + case 'C': + return 1; + + case 'e': + case 's': + case 'S': + return 2; + + case 't': + case 'l': + case 'L': + case 'f': + return 4; + + case 'd': + case 'm': + return 8; + + case 'M': + return 16; + + case 'b': + return -1; + + case 'o': + case 'x': + return 0; + + default: + CPLAssert( FALSE ); + } + + return 0; +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +void HFADictionary::Dump( FILE * fp ) + +{ + int i; + + VSIFPrintf( fp, "\nHFADictionary:\n" ); + + for( i = 0; i < nTypes; i++ ) + { + papoTypes[i]->Dump( fp ); + } +} diff --git a/bazaar/plugin/gdal/frmts/hfa/hfaentry.cpp b/bazaar/plugin/gdal/frmts/hfa/hfaentry.cpp new file mode 100644 index 000000000..8d36c4667 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/hfaentry.cpp @@ -0,0 +1,1131 @@ +/****************************************************************************** + * $Id: hfaentry.cpp 28275 2015-01-02 18:45:58Z rouault $ + * + * Project: Erdas Imagine (.img) Translator + * Purpose: Implementation of the HFAEntry class for reading and relating + * one node in the HFA object tree structure. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Intergraph Corporation + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * hfaentry.cpp + * + * Implementation of the HFAEntry class. + * + */ + +#include "hfa_p.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: hfaentry.cpp 28275 2015-01-02 18:45:58Z rouault $"); + +/************************************************************************/ +/* HFAEntry() */ +/************************************************************************/ + +HFAEntry::HFAEntry() +{ + psHFA = NULL; + + nFilePos = 0; + bDirty = FALSE; + bIsMIFObject = FALSE; + + poParent = NULL; + poPrev = NULL; + + poNext = poChild = NULL; + + nDataPos = nDataSize = 0; + nNextPos = nChildPos = 0; + + szName[0] = szType[0] = '\0'; + + pabyData = NULL; + + poType = NULL; +} + +/************************************************************************/ +/* HFAEntry() */ +/* */ +/* Construct an HFAEntry from the source file. */ +/************************************************************************/ + +HFAEntry* HFAEntry::New( HFAInfo_t * psHFAIn, GUInt32 nPos, + HFAEntry * poParentIn, HFAEntry * poPrevIn ) + +{ + HFAEntry* poEntry = new HFAEntry; + poEntry->psHFA = psHFAIn; + + poEntry->nFilePos = nPos; + poEntry->poParent = poParentIn; + poEntry->poPrev = poPrevIn; + +/* -------------------------------------------------------------------- */ +/* Read the entry information from the file. */ +/* -------------------------------------------------------------------- */ + GInt32 anEntryNums[6]; + int i; + + if( VSIFSeekL( poEntry->psHFA->fp, poEntry->nFilePos, SEEK_SET ) == -1 + || VSIFReadL( anEntryNums, sizeof(GInt32), 6, poEntry->psHFA->fp ) < 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "VSIFReadL(%p,6*4) @ %d failed in HFAEntry().\n%s", + poEntry->psHFA->fp, poEntry->nFilePos, VSIStrerror( errno ) ); + delete poEntry; + return NULL; + } + + for( i = 0; i < 6; i++ ) + HFAStandard( 4, anEntryNums + i ); + + poEntry->nNextPos = anEntryNums[0]; + poEntry->nChildPos = anEntryNums[3]; + poEntry->nDataPos = anEntryNums[4]; + poEntry->nDataSize = anEntryNums[5]; + +/* -------------------------------------------------------------------- */ +/* Read the name, and type. */ +/* -------------------------------------------------------------------- */ + if( VSIFReadL( poEntry->szName, 1, 64, poEntry->psHFA->fp ) < 1 + || VSIFReadL( poEntry->szType, 1, 32, poEntry->psHFA->fp ) < 1 ) + { + poEntry->szName[sizeof(poEntry->szName)-1] = '\0'; + poEntry->szType[sizeof(poEntry->szType)-1] = '\0'; + CPLError( CE_Failure, CPLE_FileIO, + "VSIFReadL() failed in HFAEntry()." ); + delete poEntry; + return NULL; + } + poEntry->szName[sizeof(poEntry->szName)-1] = '\0'; + poEntry->szType[sizeof(poEntry->szType)-1] = '\0'; + return poEntry; +} + +/************************************************************************/ +/* HFAEntry() */ +/* */ +/* Construct an HFAEntry in memory, with the intention that it */ +/* would be written to disk later. */ +/************************************************************************/ + +HFAEntry::HFAEntry( HFAInfo_t * psHFAIn, + const char * pszNodeName, + const char * pszTypeName, + HFAEntry * poParentIn ) + +{ +/* -------------------------------------------------------------------- */ +/* Initialize Entry */ +/* -------------------------------------------------------------------- */ + psHFA = psHFAIn; + + nFilePos = 0; + bIsMIFObject = FALSE; + + poParent = poParentIn; + poPrev = poNext = poChild = NULL; + + nDataPos = nDataSize = 0; + nNextPos = nChildPos = 0; + + SetName( pszNodeName ); + memset( szType, 0, sizeof(szType) ); + strncpy( szType, pszTypeName, sizeof(szType) ); + szType[sizeof(szType)-1] = '\0'; + + pabyData = NULL; + poType = NULL; + +/* -------------------------------------------------------------------- */ +/* Update the previous or parent node to refer to this one. */ +/* -------------------------------------------------------------------- */ + if( poParent == NULL ) + { + /* do nothing */ + } + else if( poParent->poChild == NULL ) + { + poParent->poChild = this; + poParent->MarkDirty(); + } + else + { + poPrev = poParent->poChild; + while( poPrev->poNext != NULL ) + poPrev = poPrev->poNext; + + poPrev->poNext = this; + poPrev->MarkDirty(); + } + + MarkDirty(); +} + +/************************************************************************/ +/* BuildEntryFromMIFObject() */ +/* */ +/* Create a pseudo-HFAEntry wrapping a MIFObject. */ +/************************************************************************/ + +HFAEntry* HFAEntry::BuildEntryFromMIFObject( HFAEntry *poContainer, const char *pszMIFObjectPath ) +{ + const char* pszField; + CPLString osFieldName; + + osFieldName.Printf("%s.%s", pszMIFObjectPath, "MIFDictionary" ); + pszField = poContainer->GetStringField( osFieldName.c_str() ); + if (pszField == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry", + osFieldName.c_str()); + return NULL; + } + CPLString osDictionnary = pszField; + + osFieldName.Printf("%s.%s", pszMIFObjectPath, "type.string" ); + pszField = poContainer->GetStringField( osFieldName.c_str() ); + if (pszField == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry", + osFieldName.c_str()); + return NULL; + } + CPLString osType = pszField; + + osFieldName.Printf("%s.%s", pszMIFObjectPath, "MIFObject" ); + int nRemainingDataSize = 0; + pszField = poContainer->GetStringField( osFieldName.c_str(), + NULL, &nRemainingDataSize ); + if (pszField == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry", + osFieldName.c_str()); + return NULL; + } + + GInt32 nMIFObjectSize; + // we rudely look before the field data to get at the pointer/size info + memcpy( &nMIFObjectSize, pszField-8, 4 ); + HFAStandard( 4, &nMIFObjectSize ); + if (nMIFObjectSize <= 0) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid MIF object size (%d)", + nMIFObjectSize); + return NULL; + } + + // check that we won't copy more bytes than available in the buffer + if (nMIFObjectSize > nRemainingDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid MIF object size (%d > %d)", + nMIFObjectSize, nRemainingDataSize); + return NULL; + } + + GByte* pabyData = (GByte *) VSIMalloc(nMIFObjectSize); + if (pabyData == NULL) + return NULL; + + memcpy( pabyData, pszField, nMIFObjectSize ); + + return new HFAEntry(poContainer, pszMIFObjectPath, + osDictionnary, osType, + nMIFObjectSize, pabyData); + +} + +/************************************************************************/ +/* HFAEntry() */ +/* */ +/* Create a pseudo-HFAEntry wrapping a MIFObject. */ +/************************************************************************/ + +HFAEntry::HFAEntry( CPL_UNUSED HFAEntry * poContainer, + CPL_UNUSED const char *pszMIFObjectPath, + const char * pszDictionnary, + const char * pszTypeName, + int nDataSizeIn, + GByte* pabyDataIn ) +{ +/* -------------------------------------------------------------------- */ +/* Initialize Entry */ +/* -------------------------------------------------------------------- */ + nFilePos = 0; + + poParent = poPrev = poNext = poChild = NULL; + + bIsMIFObject = TRUE; + + nDataPos = nDataSize = 0; + nNextPos = nChildPos = 0; + + memset( szName, 0, sizeof(szName) ); + +/* -------------------------------------------------------------------- */ +/* Create a dummy HFAInfo_t. */ +/* -------------------------------------------------------------------- */ + psHFA = (HFAInfo_t *) CPLCalloc(sizeof(HFAInfo_t),1); + + psHFA->eAccess = HFA_ReadOnly; + psHFA->bTreeDirty = FALSE; + psHFA->poRoot = this; + + psHFA->poDictionary = new HFADictionary( pszDictionnary ); + +/* -------------------------------------------------------------------- */ +/* Work out the type for this MIFObject. */ +/* -------------------------------------------------------------------- */ + memset( szType, 0, sizeof(szType) ); + strncpy( szType, pszTypeName, sizeof(szType) ); + szType[sizeof(szType)-1] = '\0'; + + poType = psHFA->poDictionary->FindType( szType ); + + nDataSize = nDataSizeIn; + pabyData = pabyDataIn; +} + +/************************************************************************/ +/* ~HFAEntry() */ +/* */ +/* Ensure that children are cleaned up when this node is */ +/* cleaned up. */ +/************************************************************************/ + +HFAEntry::~HFAEntry() + +{ + CPLFree( pabyData ); + + if( poNext != NULL ) + delete poNext; + + if( poChild != NULL ) + delete poChild; + + if( bIsMIFObject ) + { + delete psHFA->poDictionary; + CPLFree( psHFA ); + } +} + +/************************************************************************/ +/* RemoveAndDestroy() */ +/* */ +/* Removes this entry, and it's children from the current */ +/* tree. The parent and/or siblings are appropriately updated */ +/* so that they will be flushed back to disk without the */ +/* reference to this node. */ +/************************************************************************/ + +CPLErr HFAEntry::RemoveAndDestroy() + +{ + if( poPrev != NULL ) + { + poPrev->poNext = poNext; + if( poNext != NULL ) + poPrev->nNextPos = poNext->nFilePos; + else + poPrev->nNextPos = 0; + poPrev->MarkDirty(); + } + if( poParent != NULL && poParent->poChild == this ) + { + poParent->poChild = poNext; + if( poNext ) + poParent->nChildPos = poNext->nFilePos; + else + poParent->nChildPos = 0; + poParent->MarkDirty(); + } + + if( poNext != NULL ) + { + poNext->poPrev = poPrev; + } + + poNext = NULL; + poPrev = NULL; + poParent = NULL; + + delete this; + + return CE_None; +} + +/************************************************************************/ +/* SetName() */ +/* */ +/* Changes the name assigned to this node */ +/************************************************************************/ + +void HFAEntry::SetName( const char *pszNodeName ) +{ + memset( szName, 0, sizeof(szName) ); + strncpy( szName, pszNodeName, sizeof(szName) ); + szName[sizeof(szName)-1] = '\0'; + + MarkDirty(); +} + +/************************************************************************/ +/* GetChild() */ +/************************************************************************/ + +HFAEntry *HFAEntry::GetChild() + +{ +/* -------------------------------------------------------------------- */ +/* Do we need to create the child node? */ +/* -------------------------------------------------------------------- */ + if( poChild == NULL && nChildPos != 0 ) + { + poChild = HFAEntry::New( psHFA, nChildPos, this, NULL ); + if( poChild == NULL ) + nChildPos = 0; + } + + return( poChild ); +} + +/************************************************************************/ +/* GetNext() */ +/************************************************************************/ + +HFAEntry *HFAEntry::GetNext() + +{ +/* -------------------------------------------------------------------- */ +/* Do we need to create the next node? */ +/* -------------------------------------------------------------------- */ + if( poNext == NULL && nNextPos != 0 ) + { + // Check if we have a loop on the next node in this sibling chain. + HFAEntry *poPast; + + for( poPast = this; + poPast != NULL && poPast->nFilePos != nNextPos; + poPast = poPast->poPrev ) {} + + if( poPast != NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Corrupt (looping) entry in %s, ignoring some entries after %s.", + psHFA->pszFilename, + szName ); + nNextPos = 0; + return NULL; + } + + poNext = HFAEntry::New( psHFA, nNextPos, poParent, this ); + if( poNext == NULL ) + nNextPos = 0; + } + + return( poNext ); +} + +/************************************************************************/ +/* LoadData() */ +/* */ +/* Load the data for this entry, and build up the field */ +/* information for it. */ +/************************************************************************/ + +void HFAEntry::LoadData() + +{ + if( pabyData != NULL || nDataSize == 0 ) + return; + +/* -------------------------------------------------------------------- */ +/* Allocate buffer, and read data. */ +/* -------------------------------------------------------------------- */ + pabyData = (GByte *) VSIMalloc(nDataSize + 1); + if (pabyData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "VSIMalloc() failed in HFAEntry::LoadData()." ); + return; + } + + if( VSIFSeekL( psHFA->fp, nDataPos, SEEK_SET ) < 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "VSIFSeekL() failed in HFAEntry::LoadData()." ); + return; + } + + if( VSIFReadL( pabyData, 1, nDataSize, psHFA->fp ) < 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "VSIFReadL() failed in HFAEntry::LoadData()." ); + return; + } + + /* Make sure the buffer is always null terminated to avoid */ + /* issues when extracting strings from a corrupted file */ + pabyData[nDataSize] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Get the type corresponding to this entry. */ +/* -------------------------------------------------------------------- */ + poType = psHFA->poDictionary->FindType( szType ); + if( poType == NULL ) + return; +} + +/************************************************************************/ +/* GetTypeObject() */ +/************************************************************************/ + +HFAType *HFAEntry::GetTypeObject() + +{ + if( poType == NULL ) + poType = psHFA->poDictionary->FindType( szType ); + + return poType; +} + +/************************************************************************/ +/* MakeData() */ +/* */ +/* Create a data block on the this HFAEntry in memory. By */ +/* default it will create the data the correct size for fixed */ +/* sized types, or do nothing for variable length types. */ +/* However, the caller can supply a desired size for variable */ +/* sized fields. */ +/************************************************************************/ + +GByte *HFAEntry::MakeData( int nSize ) + +{ + if( poType == NULL ) + { + poType = psHFA->poDictionary->FindType( szType ); + if( poType == NULL ) + return NULL; + } + + if( nSize == 0 && poType->nBytes > 0 ) + nSize = poType->nBytes; + + if( (int) nDataSize < nSize && nSize > 0 ) + { + pabyData = (GByte *) CPLRealloc(pabyData, nSize); + memset( pabyData + nDataSize, 0, nSize - nDataSize ); + nDataSize = nSize; + + MarkDirty(); + +/* -------------------------------------------------------------------- */ +/* If the data already had a file position, we now need to */ +/* clear that, forcing it to be rewritten at the end of the */ +/* file. Referencing nodes will need to be marked dirty so */ +/* they are rewritten. */ +/* -------------------------------------------------------------------- */ + if( nFilePos != 0 ) + { + nFilePos = 0; + nDataPos = 0; + if (poPrev != NULL) poPrev->MarkDirty(); + if (poNext != NULL) poNext->MarkDirty(); + if (poChild != NULL) poChild->MarkDirty(); + if (poParent != NULL) poParent->MarkDirty(); + } + } + else + LoadData(); // make sure the data is loaded before we return pointer. + + return pabyData; +} + + +/************************************************************************/ +/* DumpFieldValues() */ +/************************************************************************/ + +void HFAEntry::DumpFieldValues( FILE * fp, const char * pszPrefix ) + +{ + if( pszPrefix == NULL ) + pszPrefix = ""; + + LoadData(); + + if( pabyData == NULL || poType == NULL ) + return; + + poType->DumpInstValue( fp, + pabyData, nDataPos, nDataSize, + pszPrefix ); +} + +/************************************************************************/ +/* FindChildren() */ +/* */ +/* Find all the children of the current node that match the */ +/* name and type provided. Either may be NULL if it is not a */ +/* factor. The pszName should be just the node name, not a */ +/* path. */ +/************************************************************************/ + +std::vector HFAEntry::FindChildren( const char *pszName, + const char *pszType, + int nRecLevel, + int* pbErrorDetected ) + +{ + std::vector apoChildren; + HFAEntry *poEntry; + + if( *pbErrorDetected ) + return apoChildren; + if( nRecLevel == 50 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Bad entry structure: recursion detected !"); + *pbErrorDetected = TRUE; + return apoChildren; + } + + for( poEntry = GetChild(); poEntry != NULL; poEntry = poEntry->GetNext() ) + { + std::vector apoEntryChildren; + size_t i; + + if( (pszName == NULL || EQUAL(poEntry->GetName(),pszName)) + && (pszType == NULL || EQUAL(poEntry->GetType(),pszType)) ) + apoChildren.push_back( poEntry ); + + apoEntryChildren = poEntry->FindChildren( pszName, pszType, nRecLevel + 1, + pbErrorDetected); + if( *pbErrorDetected ) + return apoChildren; + + for( i = 0; i < apoEntryChildren.size(); i++ ) + apoChildren.push_back( apoEntryChildren[i] ); + } + + return apoChildren; +} + +std::vector HFAEntry::FindChildren( const char *pszName, + const char *pszType) + +{ + int bErrorDetected = FALSE; + return FindChildren(pszName, pszType, 0, &bErrorDetected); +} + +/************************************************************************/ +/* GetNamedChild() */ +/************************************************************************/ + +HFAEntry *HFAEntry::GetNamedChild( const char * pszName ) + +{ + int nNameLen; + HFAEntry *poEntry; + +/* -------------------------------------------------------------------- */ +/* Establish how much of this name path is for the next child. */ +/* Up to the '.' or end of estring. */ +/* -------------------------------------------------------------------- */ + for( nNameLen = 0; + pszName[nNameLen] != '.' + && pszName[nNameLen] != '\0' + && pszName[nNameLen] != ':'; + nNameLen++ ) {} + +/* -------------------------------------------------------------------- */ +/* Scan children looking for this name. */ +/* -------------------------------------------------------------------- */ + for( poEntry = GetChild(); poEntry != NULL; poEntry = poEntry->GetNext() ) + { + if( EQUALN(poEntry->GetName(),pszName,nNameLen) + && (int) strlen(poEntry->GetName()) == nNameLen ) + { + if( pszName[nNameLen] == '.' ) + { + HFAEntry *poResult; + + poResult = poEntry->GetNamedChild( pszName+nNameLen+1 ); + if( poResult != NULL ) + return poResult; + } + else + return poEntry; + } + } + + return NULL; +} + +/************************************************************************/ +/* GetFieldValue() */ +/************************************************************************/ + +int HFAEntry::GetFieldValue( const char * pszFieldPath, + char chReqType, void *pReqReturn, + int *pnRemainingDataSize) + +{ + HFAEntry *poEntry = this; + +/* -------------------------------------------------------------------- */ +/* Is there a node path in this string? */ +/* -------------------------------------------------------------------- */ + if( strchr(pszFieldPath,':') != NULL ) + { + poEntry = GetNamedChild( pszFieldPath ); + if( poEntry == NULL ) + return FALSE; + + pszFieldPath = strchr(pszFieldPath,':') + 1; + } + +/* -------------------------------------------------------------------- */ +/* Do we have the data and type for this node? */ +/* -------------------------------------------------------------------- */ + LoadData(); + + if( pabyData == NULL ) + return FALSE; + + if( poType == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Extract the instance information. */ +/* -------------------------------------------------------------------- */ + + + return( poType->ExtractInstValue( pszFieldPath, + pabyData, nDataPos, nDataSize, + chReqType, pReqReturn, pnRemainingDataSize ) ); +} + +/************************************************************************/ +/* GetFieldCount() */ +/************************************************************************/ + +int HFAEntry::GetFieldCount( const char * pszFieldPath, CPL_UNUSED CPLErr *peErr ) +{ + HFAEntry *poEntry = this; + +/* -------------------------------------------------------------------- */ +/* Is there a node path in this string? */ +/* -------------------------------------------------------------------- */ + if( strchr(pszFieldPath,':') != NULL ) + { + poEntry = GetNamedChild( pszFieldPath ); + if( poEntry == NULL ) + return -1; + + pszFieldPath = strchr(pszFieldPath,':') + 1; + } + +/* -------------------------------------------------------------------- */ +/* Do we have the data and type for this node? */ +/* -------------------------------------------------------------------- */ + LoadData(); + + if( pabyData == NULL ) + return -1; + + if( poType == NULL ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Extract the instance information. */ +/* -------------------------------------------------------------------- */ + + return( poType->GetInstCount( pszFieldPath, + pabyData, nDataPos, nDataSize ) ); +} + +/************************************************************************/ +/* GetIntField() */ +/************************************************************************/ + +GInt32 HFAEntry::GetIntField( const char * pszFieldPath, CPLErr *peErr ) + +{ + GInt32 nIntValue; + + if( !GetFieldValue( pszFieldPath, 'i', &nIntValue, NULL ) ) + { + if( peErr != NULL ) + *peErr = CE_Failure; + + return 0; + } + else + { + if( peErr != NULL ) + *peErr = CE_None; + + return nIntValue; + } +} + +/************************************************************************/ +/* GetBigIntField() */ +/* */ +/* This is just a helper method that reads two ULONG array */ +/* entries as a GBigInt. The passed name should be the name of */ +/* the array with no array index. Array indexes 0 and 1 will */ +/* be concatenated. */ +/************************************************************************/ + +GIntBig HFAEntry::GetBigIntField( const char *pszFieldPath, CPLErr *peErr ) + +{ + GUInt32 nLower, nUpper; + char szFullFieldPath[1024]; + + sprintf( szFullFieldPath, "%s[0]", pszFieldPath ); + nLower = GetIntField( szFullFieldPath, peErr ); + if( peErr != NULL && *peErr != CE_None ) + return 0; + + sprintf( szFullFieldPath, "%s[1]", pszFieldPath ); + nUpper = GetIntField( szFullFieldPath, peErr ); + if( peErr != NULL && *peErr != CE_None ) + return 0; + + return nLower + (((GIntBig) nUpper) << 32); +} + +/************************************************************************/ +/* GetDoubleField() */ +/************************************************************************/ + +double HFAEntry::GetDoubleField( const char * pszFieldPath, CPLErr *peErr ) + +{ + double dfDoubleValue; + + if( !GetFieldValue( pszFieldPath, 'd', &dfDoubleValue, NULL ) ) + { + if( peErr != NULL ) + *peErr = CE_Failure; + + return 0.0; + } + else + { + if( peErr != NULL ) + *peErr = CE_None; + + return dfDoubleValue; + } +} + +/************************************************************************/ +/* GetStringField() */ +/************************************************************************/ + +const char *HFAEntry::GetStringField( const char * pszFieldPath, CPLErr *peErr, + int *pnRemainingDataSize) + +{ + char *pszResult = NULL; + + if( !GetFieldValue( pszFieldPath, 's', &pszResult, pnRemainingDataSize ) ) + { + if( peErr != NULL ) + *peErr = CE_Failure; + + return NULL; + } + else + { + if( peErr != NULL ) + *peErr = CE_None; + + return pszResult; + } +} + +/************************************************************************/ +/* SetFieldValue() */ +/************************************************************************/ + +CPLErr HFAEntry::SetFieldValue( const char * pszFieldPath, + char chReqType, void *pValue ) + +{ + HFAEntry *poEntry = this; + +/* -------------------------------------------------------------------- */ +/* Is there a node path in this string? */ +/* -------------------------------------------------------------------- */ + if( strchr(pszFieldPath,':') != NULL ) + { + poEntry = GetNamedChild( pszFieldPath ); + if( poEntry == NULL ) + return CE_Failure; + + pszFieldPath = strchr(pszFieldPath,':') + 1; + } + +/* -------------------------------------------------------------------- */ +/* Do we have the data and type for this node? Try loading */ +/* from a file, or instantiating a new node. */ +/* -------------------------------------------------------------------- */ + LoadData(); + if( MakeData() == NULL + || pabyData == NULL + || poType == NULL ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Extract the instance information. */ +/* -------------------------------------------------------------------- */ + MarkDirty(); + + return( poType->SetInstValue( pszFieldPath, + pabyData, nDataPos, nDataSize, + chReqType, pValue ) ); +} + +/************************************************************************/ +/* SetStringField() */ +/************************************************************************/ + +CPLErr HFAEntry::SetStringField( const char * pszFieldPath, + const char * pszValue ) + +{ + return SetFieldValue( pszFieldPath, 's', (void *) pszValue ); +} + +/************************************************************************/ +/* SetIntField() */ +/************************************************************************/ + +CPLErr HFAEntry::SetIntField( const char * pszFieldPath, int nValue ) + +{ + return SetFieldValue( pszFieldPath, 'i', &nValue ); +} + +/************************************************************************/ +/* SetDoubleField() */ +/************************************************************************/ + +CPLErr HFAEntry::SetDoubleField( const char * pszFieldPath, + double dfValue ) + +{ + return SetFieldValue( pszFieldPath, 'd', &dfValue ); +} + +/************************************************************************/ +/* SetPosition() */ +/* */ +/* Set the disk position for this entry, and recursively apply */ +/* to any children of this node. The parent will take care of */ +/* our siblings. */ +/************************************************************************/ + +void HFAEntry::SetPosition() + +{ +/* -------------------------------------------------------------------- */ +/* Establish the location of this entry, and it's data. */ +/* -------------------------------------------------------------------- */ + if( nFilePos == 0 ) + { + nFilePos = HFAAllocateSpace( psHFA, + psHFA->nEntryHeaderLength + + nDataSize ); + + if( nDataSize > 0 ) + nDataPos = nFilePos + psHFA->nEntryHeaderLength; + } + +/* -------------------------------------------------------------------- */ +/* Force all children to set their position. */ +/* -------------------------------------------------------------------- */ + for( HFAEntry *poThisChild = poChild; + poThisChild != NULL; + poThisChild = poThisChild->poNext ) + { + poThisChild->SetPosition(); + } +} + +/************************************************************************/ +/* FlushToDisk() */ +/* */ +/* Write this entry, and it's data to disk if the entries */ +/* information is dirty. Also force children to do the same. */ +/************************************************************************/ + +CPLErr HFAEntry::FlushToDisk() + +{ + CPLErr eErr = CE_None; + +/* -------------------------------------------------------------------- */ +/* If we are the root node, call SetPosition() on the whole */ +/* tree to ensure that all entries have an allocated position. */ +/* -------------------------------------------------------------------- */ + if( poParent == NULL ) + SetPosition(); + +/* ==================================================================== */ +/* Only write this node out if it is dirty. */ +/* ==================================================================== */ + if( bDirty ) + { +/* -------------------------------------------------------------------- */ +/* Ensure we know where the relative entries are located. */ +/* -------------------------------------------------------------------- */ + if( poNext != NULL ) + nNextPos = poNext->nFilePos; + + if( poChild != NULL ) + nChildPos = poChild->nFilePos; + +/* -------------------------------------------------------------------- */ +/* Write the Ehfa_Entry fields. */ +/* -------------------------------------------------------------------- */ + GUInt32 nLong; + + //VSIFFlushL( psHFA->fp ); + if( VSIFSeekL( psHFA->fp, nFilePos, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to %d for writing, out of disk space?", + nFilePos ); + return CE_Failure; + } + + nLong = nNextPos; + HFAStandard( 4, &nLong ); + VSIFWriteL( &nLong, 4, 1, psHFA->fp ); + + if( poPrev != NULL ) + nLong = poPrev->nFilePos; + else + nLong = 0; + HFAStandard( 4, &nLong ); + VSIFWriteL( &nLong, 4, 1, psHFA->fp ); + + if( poParent != NULL ) + nLong = poParent->nFilePos; + else + nLong = 0; + HFAStandard( 4, &nLong ); + VSIFWriteL( &nLong, 4, 1, psHFA->fp ); + + nLong = nChildPos; + HFAStandard( 4, &nLong ); + VSIFWriteL( &nLong, 4, 1, psHFA->fp ); + + + nLong = nDataPos; + HFAStandard( 4, &nLong ); + VSIFWriteL( &nLong, 4, 1, psHFA->fp ); + + nLong = nDataSize; + HFAStandard( 4, &nLong ); + VSIFWriteL( &nLong, 4, 1, psHFA->fp ); + + VSIFWriteL( szName, 1, 64, psHFA->fp ); + VSIFWriteL( szType, 1, 32, psHFA->fp ); + + nLong = 0; /* Should we keep the time, or set it more reasonably? */ + if( VSIFWriteL( &nLong, 4, 1, psHFA->fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to write HFAEntry %s(%s), out of disk space?", + szName, szType ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Write out the data. */ +/* -------------------------------------------------------------------- */ + //VSIFFlushL( psHFA->fp ); + if( nDataSize > 0 && pabyData != NULL ) + { + if( VSIFSeekL( psHFA->fp, nDataPos, SEEK_SET ) != 0 + || VSIFWriteL( pabyData, nDataSize, 1, psHFA->fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to write %d bytes HFAEntry %s(%s) data,\n" + "out of disk space?", + nDataSize, szName, szType ); + return CE_Failure; + } + } + + //VSIFFlushL( psHFA->fp ); + } + +/* -------------------------------------------------------------------- */ +/* Process all the children of this node */ +/* -------------------------------------------------------------------- */ + for( HFAEntry *poThisChild = poChild; + poThisChild != NULL; + poThisChild = poThisChild->poNext ) + { + eErr = poThisChild->FlushToDisk(); + if( eErr != CE_None ) + return eErr; + } + + bDirty = FALSE; + + return CE_None; +} + +/************************************************************************/ +/* MarkDirty() */ +/* */ +/* Mark this node as dirty (in need of writing to disk), and */ +/* also mark the tree as a whole as being dirty. */ +/************************************************************************/ + +void HFAEntry::MarkDirty() + +{ + bDirty = TRUE; + psHFA->bTreeDirty = TRUE; +} diff --git a/bazaar/plugin/gdal/frmts/hfa/hfafield.cpp b/bazaar/plugin/gdal/frmts/hfa/hfafield.cpp new file mode 100644 index 000000000..26b1f4841 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/hfafield.cpp @@ -0,0 +1,1593 @@ +/****************************************************************************** + * $Id: hfafield.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: Erdas Imagine (.img) Translator + * Purpose: Implementation of the HFAField class for managing information + * about one field in a HFA dictionary type. Managed by HFAType. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Intergraph Corporation + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "hfa_p.h" + +CPL_CVSID("$Id: hfafield.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +#define MAX_ENTRY_REPORT 16 + +/************************************************************************/ +/* ==================================================================== */ +/* HFAField */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* HFAField() */ +/************************************************************************/ + +HFAField::HFAField() + +{ + nBytes = 0; + + nItemCount = 0; + chPointer = '\0'; + chItemType = '\0'; + + pszItemObjectType = NULL; + poItemObjectType = NULL; + + papszEnumNames = NULL; + + pszFieldName = NULL; +} + +/************************************************************************/ +/* ~HFAField() */ +/************************************************************************/ + +HFAField::~HFAField() + +{ + CPLFree( pszItemObjectType ); + CSLDestroy( papszEnumNames ); + CPLFree( pszFieldName ); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +const char *HFAField::Initialize( const char * pszInput ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Read the number. */ +/* -------------------------------------------------------------------- */ + nItemCount = atoi(pszInput); + + while( *pszInput != '\0' && *pszInput != ':' ) + pszInput++; + + if( *pszInput == '\0' ) + return NULL; + + pszInput++; + +/* -------------------------------------------------------------------- */ +/* Is this a pointer? */ +/* -------------------------------------------------------------------- */ + if( *pszInput == 'p' || *pszInput == '*' ) + chPointer = *(pszInput++); + +/* -------------------------------------------------------------------- */ +/* Get the general type */ +/* -------------------------------------------------------------------- */ + if( *pszInput == '\0' ) + return NULL; + + chItemType = *(pszInput++); + + if ( strchr( "124cCesStlLfdmMbox", chItemType) == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unrecognized item type : %c", chItemType); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* If this is an object, we extract the type of the object. */ +/* -------------------------------------------------------------------- */ + if( chItemType == 'o' ) + { + for( i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++ ) {} + if (pszInput[i] == '\0') + return NULL; + + pszItemObjectType = (char *) CPLMalloc(i+1); + strncpy( pszItemObjectType, pszInput, i ); + pszItemObjectType[i] = '\0'; + + pszInput += i+1; + } + +/* -------------------------------------------------------------------- */ +/* If this is an inline object, we need to skip past the */ +/* definition, and then extract the object class name. */ +/* */ +/* We ignore the actual definition, so if the object type isn't */ +/* already defined, things will not work properly. See the */ +/* file lceugr250_00_pct.aux for an example of inline defs. */ +/* -------------------------------------------------------------------- */ + if( chItemType == 'x' && *pszInput == '{' ) + { + int nBraceDepth = 1; + pszInput++; + + // Skip past the definition. + while( nBraceDepth > 0 && *pszInput != '\0' ) + { + if( *pszInput == '{' ) + nBraceDepth++; + else if( *pszInput == '}' ) + nBraceDepth--; + + pszInput++; + } + if (*pszInput == '\0') + return NULL; + + chItemType = 'o'; + + // find the comma terminating the type name. + for( i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++ ) {} + if (pszInput[i] == '\0') + return NULL; + + pszItemObjectType = (char *) CPLMalloc(i+1); + strncpy( pszItemObjectType, pszInput, i ); + pszItemObjectType[i] = '\0'; + + pszInput += i+1; + } + +/* -------------------------------------------------------------------- */ +/* If this is an enumeration we have to extract all the */ +/* enumeration values. */ +/* -------------------------------------------------------------------- */ + if( chItemType == 'e' ) + { + int nEnumCount = atoi(pszInput); + int iEnum; + + if (nEnumCount < 0 || nEnumCount > 100000) + return NULL; + + pszInput = strchr(pszInput,':'); + if( pszInput == NULL ) + return NULL; + + pszInput++; + + papszEnumNames = (char **) VSICalloc(sizeof(char *), nEnumCount+1); + if (papszEnumNames == NULL) + return NULL; + + for( iEnum = 0; iEnum < nEnumCount; iEnum++ ) + { + char *pszToken; + + for( i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++ ) {} + + if( pszInput[i] != ',' ) + return NULL; + + pszToken = (char *) CPLMalloc(i+1); + strncpy( pszToken, pszInput, i ); + pszToken[i] = '\0'; + + papszEnumNames[iEnum] = pszToken; + + pszInput += i+1; + } + } + +/* -------------------------------------------------------------------- */ +/* Extract the field name. */ +/* -------------------------------------------------------------------- */ + for( i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++ ) {} + if (pszInput[i] == '\0') + return NULL; + + pszFieldName = (char *) CPLMalloc(i+1); + strncpy( pszFieldName, pszInput, i ); + pszFieldName[i] = '\0'; + + pszInput += i+1; + + return( pszInput ); +} + +/************************************************************************/ +/* CompleteDefn() */ +/* */ +/* Establish size, and pointers to component types. */ +/************************************************************************/ + +void HFAField::CompleteDefn( HFADictionary * poDict ) + +{ +/* -------------------------------------------------------------------- */ +/* Get a reference to the type object if we have a type name */ +/* for this field (not a built in). */ +/* -------------------------------------------------------------------- */ + if( pszItemObjectType != NULL ) + poItemObjectType = poDict->FindType( pszItemObjectType ); + +/* -------------------------------------------------------------------- */ +/* Figure out the size. */ +/* -------------------------------------------------------------------- */ + if( chPointer == 'p' ) + { + nBytes = -1; /* we can't know the instance size */ + } + else if( poItemObjectType != NULL ) + { + poItemObjectType->CompleteDefn( poDict ); + if( poItemObjectType->nBytes == -1 ) + nBytes = -1; + else + nBytes = poItemObjectType->nBytes * nItemCount; + + if( chPointer == '*' && nBytes != -1 ) + nBytes += 8; /* count, and offset */ + } + else + { + nBytes = poDict->GetItemSize( chItemType ) * nItemCount; + } +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +void HFAField::Dump( FILE * fp ) + +{ + const char *pszTypeName; + + switch( chItemType ) + { + case '1': + pszTypeName = "U1"; + break; + + case '2': + pszTypeName = "U2"; + break; + + case '4': + pszTypeName = "U4"; + break; + + case 'c': + pszTypeName = "UCHAR"; + break; + + case 'C': + pszTypeName = "CHAR"; + break; + + case 'e': + pszTypeName = "ENUM"; + break; + + case 's': + pszTypeName = "USHORT"; + break; + + case 'S': + pszTypeName = "SHORT"; + break; + + case 't': + pszTypeName = "TIME"; + break; + + case 'l': + pszTypeName = "ULONG"; + break; + + case 'L': + pszTypeName = "LONG"; + break; + + case 'f': + pszTypeName = "FLOAT"; + break; + + case 'd': + pszTypeName = "DOUBLE"; + break; + + case 'm': + pszTypeName = "COMPLEX"; + break; + + case 'M': + pszTypeName = "DCOMPLEX"; + break; + + case 'b': + pszTypeName = "BASEDATA"; + break; + + case 'o': + pszTypeName = pszItemObjectType; + break; + + case 'x': + pszTypeName = "InlineType"; + break; + + default: + CPLAssert( FALSE ); + pszTypeName = "Unknown"; + } + + VSIFPrintf( fp, " %-19s %c %s[%d];\n", + pszTypeName, + chPointer ? chPointer : ' ', + pszFieldName, nItemCount ); + + if( papszEnumNames != NULL ) + { + int i; + + for( i = 0; papszEnumNames[i] != NULL; i++ ) + { + VSIFPrintf( fp, " %s=%d\n", + papszEnumNames[i], i ); + } + } +} + +/************************************************************************/ +/* SetInstValue() */ +/************************************************************************/ + +CPLErr +HFAField::SetInstValue( const char * pszField, int nIndexValue, + GByte *pabyData, GUInt32 nDataOffset, int nDataSize, + char chReqType, void *pValue ) + +{ +/* -------------------------------------------------------------------- */ +/* If this field contains a pointer, then we will adjust the */ +/* data offset relative to it. */ +/* -------------------------------------------------------------------- */ + if( chPointer != '\0' ) + { + GUInt32 nCount; + GUInt32 nOffset; + + /* set the count for fixed sized arrays */ + if( nBytes > -1 ) + nCount = nItemCount; + + // The count returned for BASEDATA's are the contents, + // but here we really want to mark it as one BASEDATA instance + // (see #2144) + if( chItemType == 'b' ) + nCount = 1; + + /* Set the size from string length */ + else if( chReqType == 's' && (chItemType == 'c' || chItemType == 'C')) + { + if( pValue == NULL ) + nCount = 0; + else + nCount = strlen((char *) pValue) + 1; + } + + /* set size based on index ... assumes in-order setting of array */ + else + nCount = nIndexValue+1; + + if( (int) nCount + 8 > nDataSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to extend field %s in node past end of data,\n" + "not currently supported.", + pszField ); + return CE_Failure; + } + + // we will update the object count iff we are writing beyond the end + memcpy( &nOffset, pabyData, 4 ); + HFAStandard( 4, &nOffset ); + if( nOffset < nCount ) + { + nOffset = nCount; + HFAStandard( 4, &nOffset ); + memcpy( pabyData, &nOffset, 4 ); + } + + if( pValue == NULL ) + nOffset = 0; + else + nOffset = nDataOffset + 8; + HFAStandard( 4, &nOffset ); + memcpy( pabyData+4, &nOffset, 4 ); + + pabyData += 8; + + nDataOffset += 8; + nDataSize -= 8; + } + +/* -------------------------------------------------------------------- */ +/* pointers to char or uchar arrays requested as strings are */ +/* handled as a special case. */ +/* -------------------------------------------------------------------- */ + if( (chItemType == 'c' || chItemType == 'C') && chReqType == 's' ) + { + int nBytesToCopy; + + if( nBytes == -1 ) + { + if( pValue == NULL ) + nBytesToCopy = 0; + else + nBytesToCopy = strlen((char *) pValue) + 1; + } + else + nBytesToCopy = nBytes; + + if( nBytesToCopy > nDataSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to extend field %s in node past end of data,\n" + "not currently supported.", + pszField ); + return CE_Failure; + } + + memset( pabyData, 0, nBytesToCopy ); + + if( pValue != NULL ) + strncpy( (char *) pabyData, (char *) pValue, nBytesToCopy ); + + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Translate the passed type into different representations. */ +/* -------------------------------------------------------------------- */ + int nIntValue; + double dfDoubleValue; + + if( chReqType == 's' ) + { + nIntValue = atoi((char *) pValue); + dfDoubleValue = CPLAtof((char *) pValue); + } + else if( chReqType == 'd' ) + { + dfDoubleValue = *((double *) pValue); + if( dfDoubleValue > INT_MAX ) + nIntValue = INT_MAX; + else if( dfDoubleValue < INT_MIN ) + nIntValue = INT_MIN; + else + nIntValue = (int) dfDoubleValue; + } + else if( chReqType == 'i' ) + { + dfDoubleValue = *((int *) pValue); + nIntValue = *((int *) pValue); + } + else if( chReqType == 'p' ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "HFAField::SetInstValue() not supported yet for pointer values." ); + + return CE_Failure; + } + else + { + CPLAssert( FALSE ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Handle by type. */ +/* -------------------------------------------------------------------- */ + switch( chItemType ) + { + case 'c': + case 'C': + if( nIndexValue + 1 > nDataSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to extend field %s in node past end of data,\n" + "not currently supported.", + pszField ); + return CE_Failure; + } + + if( chReqType == 's' ) + pabyData[nIndexValue] = ((char *) pValue)[0]; + else + pabyData[nIndexValue] = (char) nIntValue; + break; + + case 'e': + case 's': + { + if( chItemType == 'e' && chReqType == 's' ) + { + nIntValue = CSLFindString( papszEnumNames, (char *) pValue ); + if( nIntValue == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to set enumerated field with unknown" + " value `%s'.", + (char *) pValue ); + return CE_Failure; + } + } + + unsigned short nNumber = (unsigned short) nIntValue; + + if( nIndexValue*2 + 2 > nDataSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to extend field %s in node past end of data,\n" + "not currently supported.", + pszField ); + return CE_Failure; + } + + HFAStandard( 2, &nNumber ); + memcpy( pabyData + nIndexValue*2, &nNumber, 2 ); + } + break; + + case 'S': + { + short nNumber; + + if( nIndexValue*2 + 2 > nDataSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to extend field %s in node past end of data,\n" + "not currently supported.", + pszField ); + return CE_Failure; + } + + nNumber = (short) nIntValue; + HFAStandard( 2, &nNumber ); + memcpy( pabyData + nIndexValue*2, &nNumber, 2 ); + } + break; + + case 't': + case 'l': + { + GUInt32 nNumber = nIntValue; + + if( nIndexValue*4 + 4 > nDataSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to extend field %s in node past end of data,\n" + "not currently supported.", + pszField ); + return CE_Failure; + } + + HFAStandard( 4, &nNumber ); + memcpy( pabyData + nIndexValue*4, &nNumber, 4 ); + } + break; + + case 'L': + { + GInt32 nNumber = nIntValue; + + if( nIndexValue*4 + 4 > nDataSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to extend field %s in node past end of data,\n" + "not currently supported.", + pszField ); + return CE_Failure; + } + + HFAStandard( 4, &nNumber ); + memcpy( pabyData + nIndexValue*4, &nNumber, 4 ); + } + break; + + case 'f': + { + float fNumber = (float) dfDoubleValue; + + if( nIndexValue*4 + 4 > nDataSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to extend field %s in node past end of data,\n" + "not currently supported.", + pszField ); + return CE_Failure; + } + + HFAStandard( 4, &fNumber ); + memcpy( pabyData + nIndexValue*4, &fNumber, 4 ); + } + break; + + case 'd': + { + double dfNumber = dfDoubleValue; + + if( nIndexValue*8 + 8 > nDataSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to extend field %s in node past end of data,\n" + "not currently supported.", + pszField ); + return CE_Failure; + } + + HFAStandard( 8, &dfNumber ); + memcpy( pabyData + nIndexValue*8, &dfNumber, 8 ); + } + break; + + case 'b': + { + GInt32 nRows = 1; + GInt32 nColumns = 1; + GInt16 nBaseItemType; + + // Extract existing rows, columns, and datatype. + memcpy( &nRows, pabyData, 4 ); + HFAStandard( 4, &nRows ); + memcpy( &nColumns, pabyData+4, 4 ); + HFAStandard( 4, &nColumns ); + memcpy( &nBaseItemType, pabyData+8, 2 ); + HFAStandard( 2, &nBaseItemType ); + + // Are we using special index values to update the rows, columnrs + // or type? + + if( nIndexValue == -3 ) + nBaseItemType = (GInt16) nIntValue; + else if( nIndexValue == -2 ) + nColumns = nIntValue; + else if( nIndexValue == -1 ) + nRows = nIntValue; + + if( nIndexValue < -3 || nIndexValue >= nRows * nColumns ) + return CE_Failure; + + // Write back the rows, columns and basedatatype. + HFAStandard( 4, &nRows ); + memcpy( pabyData, &nRows, 4 ); + HFAStandard( 4, &nColumns ); + memcpy( pabyData+4, &nColumns, 4 ); + HFAStandard( 2, &nBaseItemType ); + memcpy ( pabyData + 8, &nBaseItemType, 2 ); + HFAStandard( 2, &nBaseItemType ); // swap back for our use. + + // We ignore the 2 byte objecttype value. + + nDataSize -= 12; + + if( nIndexValue >= 0 ) + { + if( (nIndexValue+1) * (HFAGetDataTypeBits(nBaseItemType)/8) + > nDataSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to extend field %s in node past end of data,\n" + "not currently supported.", + pszField ); + return CE_Failure; + } + + if( nBaseItemType == EPT_f64 ) + { + double dfNumber = dfDoubleValue; + + HFAStandard( 8, &dfNumber ); + memcpy( pabyData + 12 + nIndexValue * 8, &dfNumber, 8 ); + } + else if (nBaseItemType == EPT_u8) + { + unsigned char nNumber = (unsigned char)dfDoubleValue; + memcpy( pabyData + 12 + nIndexValue, &nNumber, 1); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Setting basedata field %s with type %s not currently supported.", + pszField, HFAGetDataTypeName( nBaseItemType ) ); + return CE_Failure; + } + } + } + break; + + case 'o': + if( poItemObjectType != NULL ) + { + int nExtraOffset = 0; + int iIndexCounter; + + if( poItemObjectType->nBytes > 0 ) + { + if (nIndexValue != 0 && poItemObjectType->nBytes > INT_MAX / nIndexValue) + return CE_Failure; + nExtraOffset = poItemObjectType->nBytes * nIndexValue; + } + else + { + for( iIndexCounter = 0; + iIndexCounter < nIndexValue && nExtraOffset < nDataSize; + iIndexCounter++ ) + { + int nInc = poItemObjectType->GetInstBytes(pabyData + nExtraOffset, + nDataSize - nExtraOffset); + if (nInc < 0 || nExtraOffset > INT_MAX - nInc) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value"); + return CE_Failure; + } + + nExtraOffset += nInc; + } + } + + if (nExtraOffset >= nDataSize) + return CE_Failure; + + if( pszField != NULL && strlen(pszField) > 0 ) + { + return( poItemObjectType-> + SetInstValue( pszField, pabyData + nExtraOffset, + nDataOffset + nExtraOffset, + nDataSize - nExtraOffset, + chReqType, pValue ) ); + } + else + { + CPLAssert( FALSE ); + return CE_Failure; + } + } + break; + + default: + CPLAssert( FALSE ); + return CE_Failure; + break; + } + + return CE_None; +} + +/************************************************************************/ +/* ExtractInstValue() */ +/* */ +/* Extract the value of an instance of a field. */ +/* */ +/* pszField should be NULL if this field is not a */ +/* substructure. */ +/************************************************************************/ + +int +HFAField::ExtractInstValue( const char * pszField, int nIndexValue, + GByte *pabyData, GUInt32 nDataOffset, int nDataSize, + char chReqType, void *pReqReturn, int *pnRemainingDataSize ) + +{ + char *pszStringRet = NULL; + int nIntRet = 0; + double dfDoubleRet = 0.0; + int nInstItemCount = GetInstCount( pabyData, nDataSize ); + GByte *pabyRawData = NULL; + + if (pnRemainingDataSize) + *pnRemainingDataSize = -1; + +/* -------------------------------------------------------------------- */ +/* Check the index value is valid. */ +/* */ +/* Eventually this will have to account for variable fields. */ +/* -------------------------------------------------------------------- */ + if( nIndexValue < 0 || nIndexValue >= nInstItemCount ) + { + if( chItemType == 'b' && nIndexValue >= -3 && nIndexValue < 0 ) + /* ok - special index values */; + else + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If this field contains a pointer, then we will adjust the */ +/* data offset relative to it. */ +/* -------------------------------------------------------------------- */ + if( chPointer != '\0' ) + { + GUInt32 nOffset; + + if (nDataSize < 8) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + + memcpy( &nOffset, pabyData+4, 4 ); + HFAStandard( 4, &nOffset ); + + if( nOffset != (GUInt32) (nDataOffset + 8) ) + { +#ifdef notdef + CPLError( CE_Warning, CPLE_AppDefined, + "%s.%s points at %d, not %d as expected\n", + pszFieldName, pszField ? pszField : "", + nOffset, nDataOffset+8 ); +#endif + } + + pabyData += 8; + + nDataOffset += 8; + nDataSize -= 8; + } + +/* -------------------------------------------------------------------- */ +/* pointers to char or uchar arrays requested as strings are */ +/* handled as a special case. */ +/* -------------------------------------------------------------------- */ + if( (chItemType == 'c' || chItemType == 'C') && chReqType == 's' ) + { + *((GByte **)pReqReturn) = pabyData; + if (pnRemainingDataSize) + *pnRemainingDataSize = nDataSize; + return( pabyData != NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Handle by type. */ +/* -------------------------------------------------------------------- */ + switch( chItemType ) + { + case 'c': + case 'C': + if (nIndexValue >= nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + nIntRet = pabyData[nIndexValue]; + dfDoubleRet = nIntRet; + break; + + case 'e': + case 's': + { + unsigned short nNumber; + if (nIndexValue*2 + 2 > nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + memcpy( &nNumber, pabyData + nIndexValue*2, 2 ); + HFAStandard( 2, &nNumber ); + nIntRet = nNumber; + dfDoubleRet = nIntRet; + + if( chItemType == 'e' + && nIntRet >= 0 && nIntRet < CSLCount(papszEnumNames) ) + { + pszStringRet = papszEnumNames[nIntRet]; + } + } + break; + + case 'S': + { + short nNumber; + if (nIndexValue*2 + 2 > nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + memcpy( &nNumber, pabyData + nIndexValue*2, 2 ); + HFAStandard( 2, &nNumber ); + nIntRet = nNumber; + dfDoubleRet = nIntRet; + } + break; + + case 't': + case 'l': + { + GUInt32 nNumber; + if (nIndexValue*4 + 4 > nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + memcpy( &nNumber, pabyData + nIndexValue*4, 4 ); + HFAStandard( 4, &nNumber ); + nIntRet = nNumber; + dfDoubleRet = nIntRet; + } + break; + + case 'L': + { + GInt32 nNumber; + if (nIndexValue*4 + 4 > nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + memcpy( &nNumber, pabyData + nIndexValue*4, 4 ); + HFAStandard( 4, &nNumber ); + nIntRet = nNumber; + dfDoubleRet = nIntRet; + } + break; + + case 'f': + { + float fNumber; + if (nIndexValue*4 + 4 > nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + memcpy( &fNumber, pabyData + nIndexValue*4, 4 ); + HFAStandard( 4, &fNumber ); + dfDoubleRet = fNumber; + nIntRet = (int) fNumber; + } + break; + + case 'd': + { + double dfNumber; + if (nIndexValue*8 + 8 > nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + memcpy( &dfNumber, pabyData + nIndexValue*8, 8 ); + HFAStandard( 8, &dfNumber ); + dfDoubleRet = dfNumber; + nIntRet = (int) dfNumber; + } + break; + + case 'b': + { + GInt32 nRows, nColumns; + GInt16 nBaseItemType; + + if( nDataSize < 12 ) + return FALSE; + + memcpy( &nRows, pabyData, 4 ); + HFAStandard( 4, &nRows ); + memcpy( &nColumns, pabyData+4, 4 ); + HFAStandard( 4, &nColumns ); + memcpy( &nBaseItemType, pabyData+8, 2 ); + HFAStandard( 2, &nBaseItemType ); + // We ignore the 2 byte objecttype value. + + if( nIndexValue < -3 || nIndexValue >= nRows * nColumns ) + return FALSE; + + pabyData += 12; + nDataSize -= 12; + + if( nIndexValue == -3 ) + { + dfDoubleRet = nIntRet = nBaseItemType; + } + else if( nIndexValue == -2 ) + { + dfDoubleRet = nIntRet = nColumns; + } + else if( nIndexValue == -1 ) + { + dfDoubleRet = nIntRet = nRows; + } + else if( nBaseItemType == EPT_u1 ) + { + if (nIndexValue*8 >= nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + + if( pabyData[nIndexValue>>3] & (1 << (nIndexValue & 0x7)) ) + { + dfDoubleRet = 1; + nIntRet = 1; + } + else + { + dfDoubleRet = 0.0; + nIntRet = 0; + } + } + else if( nBaseItemType == EPT_u2 ) + { + int nBitOffset = nIndexValue & 0x3; + int nByteOffset = nIndexValue >> 2; + int nMask = 0x3; + + if (nByteOffset >= nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + + nIntRet = (pabyData[nByteOffset] >> nBitOffset) & nMask; + dfDoubleRet = nIntRet; + } + else if( nBaseItemType == EPT_u4 ) + { + int nBitOffset = nIndexValue & 0x7; + int nByteOffset = nIndexValue >> 3; + int nMask = 0x7; + + if (nByteOffset >= nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + + nIntRet = (pabyData[nByteOffset] >> nBitOffset) & nMask; + dfDoubleRet = nIntRet; + } + else if( nBaseItemType == EPT_u8 ) + { + if (nIndexValue >= nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + dfDoubleRet = pabyData[nIndexValue]; + nIntRet = pabyData[nIndexValue]; + } + else if( nBaseItemType == EPT_s8 ) + { + if (nIndexValue >= nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + dfDoubleRet = ((signed char *)pabyData)[nIndexValue]; + nIntRet = ((signed char *)pabyData)[nIndexValue]; + } + else if( nBaseItemType == EPT_s16 ) + { + GInt16 nValue; + if (nIndexValue*2 + 2 > nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + memcpy( &nValue, pabyData + 2*nIndexValue, 2 ); + HFAStandard( 2, &nValue ); + + dfDoubleRet = nValue; + nIntRet = nValue; + } + else if( nBaseItemType == EPT_u16 ) + { + GUInt16 nValue; + if (nIndexValue*2 + 2 > nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + memcpy( &nValue, pabyData + 2*nIndexValue, 2 ); + HFAStandard( 2, &nValue ); + + dfDoubleRet = nValue; + nIntRet = nValue; + } + else if( nBaseItemType == EPT_s32 ) + { + GInt32 nValue; + if (nIndexValue*4 + 4 > nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + memcpy( &nValue, pabyData + 4*nIndexValue, 4 ); + HFAStandard( 4, &nValue ); + + dfDoubleRet = nValue; + nIntRet = nValue; + } + else if( nBaseItemType == EPT_u32 ) + { + GUInt32 nValue; + if (nIndexValue*4 + 4 > nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + memcpy( &nValue, pabyData + 4*nIndexValue, 4 ); + HFAStandard( 4, &nValue ); + + dfDoubleRet = nValue; + nIntRet = nValue; + } + else if( nBaseItemType == EPT_f32 ) + { + float fValue; + if (nIndexValue*4 + 4 > nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + memcpy( &fValue, pabyData + 4*nIndexValue, 4 ); + HFAStandard( 4, &fValue ); + + dfDoubleRet = fValue; + nIntRet = (int) fValue; + } + else if( nBaseItemType == EPT_f64 ) + { + double dfValue; + if (nIndexValue*8 + 8 > nDataSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return FALSE; + } + memcpy( &dfValue, pabyData+8*nIndexValue, 8 ); + HFAStandard( 8, &dfValue ); + + dfDoubleRet = dfValue; + if( dfDoubleRet > INT_MAX ) + nIntRet = INT_MAX; + else if( dfDoubleRet < INT_MIN ) + nIntRet = INT_MIN; + else + nIntRet = (int) dfDoubleRet; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Unknown base item type : %d", nBaseItemType); + return FALSE; + } + } + break; + + case 'o': + if( poItemObjectType != NULL ) + { + int nExtraOffset = 0; + int iIndexCounter; + + if( poItemObjectType->nBytes > 0 ) + { + if (nIndexValue != 0 && poItemObjectType->nBytes > INT_MAX / nIndexValue) + return CE_Failure; + nExtraOffset = poItemObjectType->nBytes * nIndexValue; + } + else + { + for( iIndexCounter = 0; + iIndexCounter < nIndexValue && nExtraOffset < nDataSize; + iIndexCounter++ ) + { + int nInc = poItemObjectType->GetInstBytes(pabyData + nExtraOffset, + nDataSize - nExtraOffset); + if (nInc < 0 || nExtraOffset > INT_MAX - nInc) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value"); + return CE_Failure; + } + + nExtraOffset += nInc; + } + } + + if (nExtraOffset >= nDataSize) + return CE_Failure; + + pabyRawData = pabyData + nExtraOffset; + + if( pszField != NULL && strlen(pszField) > 0 ) + { + return( poItemObjectType-> + ExtractInstValue( pszField, pabyRawData, + nDataOffset + nExtraOffset, + nDataSize - nExtraOffset, + chReqType, pReqReturn, pnRemainingDataSize ) ); + } + } + break; + + default: + return FALSE; + break; + } + +/* -------------------------------------------------------------------- */ +/* Return the appropriate representation. */ +/* -------------------------------------------------------------------- */ + if( chReqType == 's' ) + { + if( pszStringRet == NULL ) + { + /* HFAEntry:: BuildEntryFromMIFObject() expects to have always */ + /* 8 bytes before the data. In normal situations, it should */ + /* not go here, but that can happen if the file is corrupted */ + /* so reserve the first 8 bytes before the string to contain null bytes */ + memset(szNumberString, 0, 8); + CPLsprintf( szNumberString + 8, "%.14g", dfDoubleRet ); + pszStringRet = szNumberString + 8; + } + + *((char **) pReqReturn) = pszStringRet; + return( TRUE ); + } + else if( chReqType == 'd' ) + { + *((double *)pReqReturn) = dfDoubleRet; + return( TRUE ); + } + else if( chReqType == 'i' ) + { + *((int *) pReqReturn) = nIntRet; + return( TRUE ); + } + else if( chReqType == 'p' ) + { + *((GByte **) pReqReturn) = pabyRawData; + return( TRUE ); + } + else + { + CPLAssert( FALSE ); + return FALSE; + } +} + +/************************************************************************/ +/* GetInstBytes() */ +/* */ +/* Get the number of bytes in a particular instance of a */ +/* field. This will normally be the fixed internal nBytes */ +/* value, but for pointer objects will include the variable */ +/* portion. */ +/************************************************************************/ + +int HFAField::GetInstBytes( GByte *pabyData, int nDataSize ) + +{ + int nCount; + int nInstBytes = 0; + + if( nBytes > -1 ) + return nBytes; + + if( chPointer != '\0' ) + { + if (nDataSize < 4) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return -1; + } + + memcpy( &nCount, pabyData, 4 ); + HFAStandard( 4, &nCount ); + + pabyData += 8; + nInstBytes += 8; + } + else + nCount = 1; + + if( chItemType == 'b' && nCount != 0 ) // BASEDATA + { + if (nDataSize - nInstBytes < 4+4+2) + { + CPLError(CE_Failure, CPLE_AppDefined, "Buffer too small"); + return -1; + } + + GInt32 nRows, nColumns; + GInt16 nBaseItemType; + + memcpy( &nRows, pabyData, 4 ); + HFAStandard( 4, &nRows ); + memcpy( &nColumns, pabyData+4, 4 ); + HFAStandard( 4, &nColumns ); + memcpy( &nBaseItemType, pabyData+8, 2 ); + HFAStandard( 2, &nBaseItemType ); + + nInstBytes += 12; + + if (nRows < 0 || nColumns < 0) + return -1; + if (nColumns != 0 && nRows > INT_MAX / nColumns) + return -1; + if (nColumns != 0 && ((HFAGetDataTypeBits(nBaseItemType) + 7) / 8) * nRows > INT_MAX / nColumns) + return -1; + if (((HFAGetDataTypeBits(nBaseItemType) + 7) / 8) * nRows * nColumns > INT_MAX - nInstBytes) + return -1; + + nInstBytes += + ((HFAGetDataTypeBits(nBaseItemType) + 7) / 8) * nRows * nColumns; + } + else if( poItemObjectType == NULL ) + { + if (nCount != 0 && HFADictionary::GetItemSize(chItemType) > INT_MAX / nCount) + return -1; + nInstBytes += nCount * HFADictionary::GetItemSize(chItemType); + } + else + { + int i; + + for( i = 0; i < nCount && + nInstBytes < nDataSize && + nInstBytes >= 0; i++ ) + { + int nThisBytes; + + nThisBytes = + poItemObjectType->GetInstBytes( pabyData, + nDataSize - nInstBytes ); + if (nThisBytes < 0 || nInstBytes > INT_MAX - nThisBytes) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value"); + return -1; + } + + nInstBytes += nThisBytes; + pabyData += nThisBytes; + } + } + + return( nInstBytes ); +} + +/************************************************************************/ +/* GetInstCount() */ +/* */ +/* Get the count for a particular instance of a field. This */ +/* will normally be the built in value, but for variable fields */ +/* this is extracted from the data itself. */ +/************************************************************************/ + +int HFAField::GetInstCount( GByte * pabyData, int nDataSize ) + +{ + if( chPointer == '\0' ) + return nItemCount; + else if( chItemType == 'b' ) + { + GInt32 nRows, nColumns; + + if( nDataSize < 20 ) + return 0; + + memcpy( &nRows, pabyData+8, 4 ); + HFAStandard( 4, &nRows ); + memcpy( &nColumns, pabyData+12, 4 ); + HFAStandard( 4, &nColumns ); + + if (nRows < 0 || nColumns < 0) + return 0; + if (nColumns != 0 && nRows > INT_MAX / nColumns) + return 0; + + return nRows * nColumns; + } + else + { + GInt32 nCount; + + if( nDataSize < 4 ) + return 0; + + memcpy( &nCount, pabyData, 4 ); + HFAStandard( 4, &nCount ); + return nCount; + } +} + +/************************************************************************/ +/* DumpInstValue() */ +/************************************************************************/ + +void HFAField::DumpInstValue( FILE *fpOut, + GByte *pabyData, GUInt32 nDataOffset, int nDataSize, + const char *pszPrefix ) + +{ + int iEntry, nEntries; + void *pReturn; + char szLongFieldName[256]; + + nEntries = GetInstCount( pabyData, nDataSize ); + +/* -------------------------------------------------------------------- */ +/* Special case for arrays of chars or uchars which are printed */ +/* as a string. */ +/* -------------------------------------------------------------------- */ + if( (chItemType == 'c' || chItemType == 'C') && nEntries > 0 ) + { + if( ExtractInstValue( NULL, 0, + pabyData, nDataOffset, nDataSize, + 's', &pReturn ) ) + VSIFPrintf( fpOut, "%s%s = `%s'\n", + pszPrefix, pszFieldName, + (char *) pReturn ); + else + VSIFPrintf( fpOut, "%s%s = (access failed)\n", + pszPrefix, pszFieldName ); + + return; + } + +/* -------------------------------------------------------------------- */ +/* For BASEDATA objects, we want to first dump their dimension */ +/* and type. */ +/* -------------------------------------------------------------------- */ + if( chItemType == 'b' ) + { + int nDataType, nRows, nColumns; + int bSuccess = ExtractInstValue( NULL, -3, pabyData, nDataOffset, + nDataSize, 'i', &nDataType ); + if (bSuccess) + { + ExtractInstValue( NULL, -2, pabyData, nDataOffset, + nDataSize, 'i', &nColumns ); + ExtractInstValue( NULL, -1, pabyData, nDataOffset, + nDataSize, 'i', &nRows ); + VSIFPrintf( fpOut, "%sBASEDATA(%s): %dx%d of %s\n", + pszPrefix, pszFieldName, + nColumns, nRows, HFAGetDataTypeName( nDataType ) ); + } + else + { + VSIFPrintf( fpOut, "%sBASEDATA(%s): empty\n", + pszPrefix, pszFieldName ); + } + } + +/* -------------------------------------------------------------------- */ +/* Dump each entry in the field array. */ +/* -------------------------------------------------------------------- */ + for( iEntry = 0; iEntry < MIN(MAX_ENTRY_REPORT,nEntries); iEntry++ ) + { + if( nEntries == 1 ) + VSIFPrintf( fpOut, "%s%s = ", pszPrefix, pszFieldName ); + else + VSIFPrintf( fpOut, "%s%s[%d] = ", + pszPrefix, pszFieldName, iEntry ); + + switch( chItemType ) + { + case 'f': + case 'd': + { + double dfValue; + if( ExtractInstValue( NULL, iEntry, + pabyData, nDataOffset, nDataSize, + 'd', &dfValue ) ) + VSIFPrintf( fpOut, "%f\n", dfValue ); + else + VSIFPrintf( fpOut, "(access failed)\n" ); + } + break; + + case 'b': + { + double dfValue; + + if( ExtractInstValue( NULL, iEntry, + pabyData, nDataOffset, nDataSize, + 'd', &dfValue ) ) + VSIFPrintf( fpOut, "%s%.15g\n", pszPrefix, dfValue ); + else + VSIFPrintf( fpOut, "%s(access failed)\n", pszPrefix ); + } + break; + + case 'e': + if( ExtractInstValue( NULL, iEntry, + pabyData, nDataOffset, nDataSize, + 's', &pReturn ) ) + VSIFPrintf( fpOut, "%s\n", (char *) pReturn ); + else + VSIFPrintf( fpOut, "(access failed)\n" ); + break; + + case 'o': + if( !ExtractInstValue( NULL, iEntry, + pabyData, nDataOffset, nDataSize, + 'p', &pReturn ) ) + { + VSIFPrintf( fpOut, "(access failed)\n" ); + } + else + { + int nByteOffset; + + VSIFPrintf( fpOut, "\n" ); + + nByteOffset = ((GByte *) pReturn) - pabyData; + + sprintf( szLongFieldName, "%s ", pszPrefix ); + + if( poItemObjectType ) + poItemObjectType->DumpInstValue( fpOut, + pabyData + nByteOffset, + nDataOffset + nByteOffset, + nDataSize - nByteOffset, + szLongFieldName ); + } + break; + + default: + { + GInt32 nIntValue; + + if( ExtractInstValue( NULL, iEntry, + pabyData, nDataOffset, nDataSize, + 'i', &nIntValue ) ) + VSIFPrintf( fpOut, "%d\n", nIntValue ); + else + VSIFPrintf( fpOut, "(access failed)\n" ); + } + break; + } + } + + if( nEntries > MAX_ENTRY_REPORT ) + printf( "%s ... remaining instances omitted ...\n", pszPrefix ); + + if( nEntries == 0 ) + VSIFPrintf( fpOut, "%s%s = (no values)\n", pszPrefix, pszFieldName ); + +} diff --git a/bazaar/plugin/gdal/frmts/hfa/hfaopen.cpp b/bazaar/plugin/gdal/frmts/hfa/hfaopen.cpp new file mode 100644 index 000000000..00267754b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/hfaopen.cpp @@ -0,0 +1,3972 @@ +/****************************************************************************** + * $Id: hfaopen.cpp 28831 2015-04-01 16:46:05Z rouault $ + * + * Project: Erdas Imagine (.img) Translator + * Purpose: Supporting functions for HFA (.img) ... main (C callable) API + * that is not dependent on GDAL (just CPL). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Intergraph Corporation + * Copyright (c) 2007-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * hfaopen.cpp + * + * Supporting routines for reading Erdas Imagine (.imf) Heirarchical + * File Architecture files. This is intended to be a library independent + * of the GDAL core, but dependent on the Common Portability Library. + * + */ + +#include "hfa_p.h" +#include "cpl_conv.h" +#include +#include + +CPL_CVSID("$Id: hfaopen.cpp 28831 2015-04-01 16:46:05Z rouault $"); + + +static const char *apszAuxMetadataItems[] = { + +// node/entry field_name metadata_key type + + "Statistics", "dminimum", "STATISTICS_MINIMUM", "Esta_Statistics", + "Statistics", "dmaximum", "STATISTICS_MAXIMUM", "Esta_Statistics", + "Statistics", "dmean", "STATISTICS_MEAN", "Esta_Statistics", + "Statistics", "dmedian", "STATISTICS_MEDIAN", "Esta_Statistics", + "Statistics", "dmode", "STATISTICS_MODE", "Esta_Statistics", + "Statistics", "dstddev", "STATISTICS_STDDEV", "Esta_Statistics", + "HistogramParameters", "lBinFunction.numBins", "STATISTICS_HISTONUMBINS","Eimg_StatisticsParameters830", + "HistogramParameters", "dBinFunction.minLimit", "STATISTICS_HISTOMIN", "Eimg_StatisticsParameters830", + "HistogramParameters", "dBinFunction.maxLimit", "STATISTICS_HISTOMAX", "Eimg_StatisticsParameters830", + "StatisticsParameters", "lSkipFactorX", "STATISTICS_SKIPFACTORX", "", + "StatisticsParameters", "lSkipFactorY", "STATISTICS_SKIPFACTORY", "", + "StatisticsParameters", "dExcludedValues", "STATISTICS_EXCLUDEDVALUES","", + "", "elayerType", "LAYER_TYPE", "", + NULL +}; + + +const char ** GetHFAAuxMetaDataList() +{ + return apszAuxMetadataItems; +} + + +/************************************************************************/ +/* HFAGetDictionary() */ +/************************************************************************/ + +static char * HFAGetDictionary( HFAHandle hHFA ) + +{ + int nDictMax = 100; + char *pszDictionary = (char *) CPLMalloc(nDictMax); + int nDictSize = 0; + + VSIFSeekL( hHFA->fp, hHFA->nDictionaryPos, SEEK_SET ); + + while( TRUE ) + { + if( nDictSize >= nDictMax-1 ) + { + nDictMax = nDictSize * 2 + 100; + pszDictionary = (char *) CPLRealloc(pszDictionary, nDictMax ); + } + + if( VSIFReadL( pszDictionary + nDictSize, 1, 1, hHFA->fp ) < 1 + || pszDictionary[nDictSize] == '\0' + || (nDictSize > 2 && pszDictionary[nDictSize-2] == ',' + && pszDictionary[nDictSize-1] == '.') ) + break; + + nDictSize++; + } + + pszDictionary[nDictSize] = '\0'; + + + return( pszDictionary ); +} + +/************************************************************************/ +/* HFAOpen() */ +/************************************************************************/ + +HFAHandle HFAOpen( const char * pszFilename, const char * pszAccess ) + +{ + VSILFILE *fp; + char szHeader[16]; + HFAInfo_t *psInfo; + GUInt32 nHeaderPos; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszAccess,"r") || EQUAL(pszAccess,"rb" ) ) + fp = VSIFOpenL( pszFilename, "rb" ); + else + fp = VSIFOpenL( pszFilename, "r+b" ); + + /* should this be changed to use some sort of CPLFOpen() which will + set the error? */ + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "File open of %s failed.", + pszFilename ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read and verify the header. */ +/* -------------------------------------------------------------------- */ + if( VSIFReadL( szHeader, 16, 1, fp ) < 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to read 16 byte header failed for\n%s.", + pszFilename ); + + return NULL; + } + + if( !EQUALN(szHeader,"EHFA_HEADER_TAG",15) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "File %s is not an Imagine HFA file ... header wrong.", + pszFilename ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the HFAInfo_t */ +/* -------------------------------------------------------------------- */ + psInfo = (HFAInfo_t *) CPLCalloc(sizeof(HFAInfo_t),1); + + psInfo->pszFilename = CPLStrdup(CPLGetFilename(pszFilename)); + psInfo->pszPath = CPLStrdup(CPLGetPath(pszFilename)); + psInfo->fp = fp; + if( EQUAL(pszAccess,"r") || EQUAL(pszAccess,"rb" ) ) + psInfo->eAccess = HFA_ReadOnly; + else + psInfo->eAccess = HFA_Update; + psInfo->bTreeDirty = FALSE; + +/* -------------------------------------------------------------------- */ +/* Where is the header? */ +/* -------------------------------------------------------------------- */ + VSIFReadL( &nHeaderPos, sizeof(GInt32), 1, fp ); + HFAStandard( 4, &nHeaderPos ); + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( fp, nHeaderPos, SEEK_SET ); + + VSIFReadL( &(psInfo->nVersion), sizeof(GInt32), 1, fp ); + HFAStandard( 4, &(psInfo->nVersion) ); + + VSIFReadL( szHeader, 4, 1, fp ); /* skip freeList */ + + VSIFReadL( &(psInfo->nRootPos), sizeof(GInt32), 1, fp ); + HFAStandard( 4, &(psInfo->nRootPos) ); + + VSIFReadL( &(psInfo->nEntryHeaderLength), sizeof(GInt16), 1, fp ); + HFAStandard( 2, &(psInfo->nEntryHeaderLength) ); + + VSIFReadL( &(psInfo->nDictionaryPos), sizeof(GInt32), 1, fp ); + HFAStandard( 4, &(psInfo->nDictionaryPos) ); + +/* -------------------------------------------------------------------- */ +/* Collect file size. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( fp, 0, SEEK_END ); + psInfo->nEndOfFile = (GUInt32) VSIFTellL( fp ); + +/* -------------------------------------------------------------------- */ +/* Instantiate the root entry. */ +/* -------------------------------------------------------------------- */ + psInfo->poRoot = HFAEntry::New( psInfo, psInfo->nRootPos, NULL, NULL ); + if( psInfo->poRoot == NULL ) + { + CPLFree(psInfo); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the dictionary */ +/* -------------------------------------------------------------------- */ + psInfo->pszDictionary = HFAGetDictionary( psInfo ); + psInfo->poDictionary = new HFADictionary( psInfo->pszDictionary ); + +/* -------------------------------------------------------------------- */ +/* Collect band definitions. */ +/* -------------------------------------------------------------------- */ + HFAParseBandInfo( psInfo ); + + return psInfo; +} + +/************************************************************************/ +/* HFACreateDependent() */ +/* */ +/* Create a .rrd file for the named file if it does not exist, */ +/* or return the existing dependent if it already exists. */ +/************************************************************************/ + +HFAInfo_t *HFACreateDependent( HFAInfo_t *psBase ) + +{ + if( psBase->psDependent != NULL ) + return psBase->psDependent; + +/* -------------------------------------------------------------------- */ +/* Create desired RRD filename. */ +/* -------------------------------------------------------------------- */ + CPLString oBasename = CPLGetBasename( psBase->pszFilename ); + CPLString oRRDFilename = + CPLFormFilename( psBase->pszPath, oBasename, "rrd" ); + +/* -------------------------------------------------------------------- */ +/* Does this file already exist? If so, re-use it. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFOpenL( oRRDFilename, "rb" ); + if( fp != NULL ) + { + VSIFCloseL( fp ); + psBase->psDependent = HFAOpen( oRRDFilename, "rb" ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise create it now. */ +/* -------------------------------------------------------------------- */ + HFAInfo_t *psDep; + psDep = psBase->psDependent = HFACreateLL( oRRDFilename ); + +/* -------------------------------------------------------------------- */ +/* Add the DependentFile node with the pointer back to the */ +/* parent. When working from an .aux file we really want the */ +/* .rrd to point back to the original file, not the .aux file. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poEntry = psBase->poRoot->GetNamedChild("DependentFile"); + const char *pszDependentFile = NULL; + if( poEntry != NULL ) + pszDependentFile = poEntry->GetStringField( "dependent.string" ); + if( pszDependentFile == NULL ) + pszDependentFile = psBase->pszFilename; + + HFAEntry *poDF = new HFAEntry( psDep, "DependentFile", + "Eimg_DependentFile", psDep->poRoot ); + + poDF->MakeData( strlen(pszDependentFile) + 50 ); + poDF->SetPosition(); + poDF->SetStringField( "dependent.string", pszDependentFile ); + + return psDep; +} + +/************************************************************************/ +/* HFAGetDependent() */ +/************************************************************************/ + +HFAInfo_t *HFAGetDependent( HFAInfo_t *psBase, const char *pszFilename ) + +{ + if( EQUAL(pszFilename,psBase->pszFilename) ) + return psBase; + + if( psBase->psDependent != NULL ) + { + if( EQUAL(pszFilename,psBase->psDependent->pszFilename) ) + return psBase->psDependent; + else + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to open the dependent file. */ +/* -------------------------------------------------------------------- */ + char *pszDependent; + VSILFILE *fp; + const char* pszMode = psBase->eAccess == HFA_Update ? "r+b" : "rb"; + + pszDependent = CPLStrdup( + CPLFormFilename( psBase->pszPath, pszFilename, NULL ) ); + + fp = VSIFOpenL( pszDependent, pszMode ); + if( fp != NULL ) + { + VSIFCloseL( fp ); + psBase->psDependent = HFAOpen( pszDependent, pszMode ); + } + + CPLFree( pszDependent ); + + return psBase->psDependent; +} + + +/************************************************************************/ +/* HFAParseBandInfo() */ +/* */ +/* This is used by HFAOpen() and HFACreate() to initialize the */ +/* band structures. */ +/************************************************************************/ + +CPLErr HFAParseBandInfo( HFAInfo_t *psInfo ) + +{ + HFAEntry *poNode; + +/* -------------------------------------------------------------------- */ +/* Find the first band node. */ +/* -------------------------------------------------------------------- */ + psInfo->nBands = 0; + poNode = psInfo->poRoot->GetChild(); + while( poNode != NULL ) + { + if( EQUAL(poNode->GetType(),"Eimg_Layer") + && poNode->GetIntField("width") > 0 + && poNode->GetIntField("height") > 0 ) + { + if( psInfo->nBands == 0 ) + { + psInfo->nXSize = poNode->GetIntField("width"); + psInfo->nYSize = poNode->GetIntField("height"); + } + else if( poNode->GetIntField("width") != psInfo->nXSize + || poNode->GetIntField("height") != psInfo->nYSize ) + { + return CE_Failure; + } + + psInfo->papoBand = (HFABand **) + CPLRealloc(psInfo->papoBand, + sizeof(HFABand *) * (psInfo->nBands+1)); + psInfo->papoBand[psInfo->nBands] = new HFABand( psInfo, poNode ); + if (psInfo->papoBand[psInfo->nBands]->nWidth == 0) + { + delete psInfo->papoBand[psInfo->nBands]; + return CE_Failure; + } + psInfo->nBands++; + } + + poNode = poNode->GetNext(); + } + + return CE_None; +} + +/************************************************************************/ +/* HFAClose() */ +/************************************************************************/ + +void HFAClose( HFAHandle hHFA ) + +{ + int i; + + if( hHFA->eAccess == HFA_Update && (hHFA->bTreeDirty || hHFA->poDictionary->bDictionaryTextDirty) ) + HFAFlush( hHFA ); + + if( hHFA->psDependent != NULL ) + HFAClose( hHFA->psDependent ); + + delete hHFA->poRoot; + + VSIFCloseL( hHFA->fp ); + + if( hHFA->poDictionary != NULL ) + delete hHFA->poDictionary; + + CPLFree( hHFA->pszDictionary ); + CPLFree( hHFA->pszFilename ); + CPLFree( hHFA->pszIGEFilename ); + CPLFree( hHFA->pszPath ); + + for( i = 0; i < hHFA->nBands; i++ ) + { + delete hHFA->papoBand[i]; + } + + CPLFree( hHFA->papoBand ); + + if( hHFA->pProParameters != NULL ) + { + Eprj_ProParameters *psProParms = (Eprj_ProParameters *) + hHFA->pProParameters; + + CPLFree( psProParms->proExeName ); + CPLFree( psProParms->proName ); + CPLFree( psProParms->proSpheroid.sphereName ); + + CPLFree( psProParms ); + } + + if( hHFA->pDatum != NULL ) + { + CPLFree( ((Eprj_Datum *) hHFA->pDatum)->datumname ); + CPLFree( ((Eprj_Datum *) hHFA->pDatum)->gridname ); + CPLFree( hHFA->pDatum ); + } + + if( hHFA->pMapInfo != NULL ) + { + CPLFree( ((Eprj_MapInfo *) hHFA->pMapInfo)->proName ); + CPLFree( ((Eprj_MapInfo *) hHFA->pMapInfo)->units ); + CPLFree( hHFA->pMapInfo ); + } + + CPLFree( hHFA ); +} + +/************************************************************************/ +/* HFARemove() */ +/* Used from HFADelete() function. */ +/************************************************************************/ + +CPLErr HFARemove( const char *pszFilename ) + +{ + VSIStatBufL sStat; + + if( VSIStatL( pszFilename, &sStat ) == 0 && VSI_ISREG( sStat.st_mode ) ) + { + if( VSIUnlink( pszFilename ) == 0 ) + return CE_None; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to unlink %s failed.\n", pszFilename ); + return CE_Failure; + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to delete %s, not a file.\n", pszFilename ); + return CE_Failure; + } +} + +/************************************************************************/ +/* HFADelete() */ +/************************************************************************/ + +CPLErr HFADelete( const char *pszFilename ) + +{ + HFAInfo_t *psInfo = HFAOpen( pszFilename, "rb" ); + HFAEntry *poDMS = NULL; + HFAEntry *poLayer = NULL; + HFAEntry *poNode = NULL; + + if( psInfo != NULL ) + { + poNode = psInfo->poRoot->GetChild(); + while( ( poNode != NULL ) && ( poLayer == NULL ) ) + { + if( EQUAL(poNode->GetType(),"Eimg_Layer") ) + { + poLayer = poNode; + } + poNode = poNode->GetNext(); + } + + if( poLayer != NULL ) + poDMS = poLayer->GetNamedChild( "ExternalRasterDMS" ); + + if ( poDMS ) + { + const char *pszRawFilename = + poDMS->GetStringField( "fileName.string" ); + + if( pszRawFilename != NULL ) + HFARemove( CPLFormFilename( psInfo->pszPath, + pszRawFilename, NULL ) ); + } + + HFAClose( psInfo ); + } + return HFARemove( pszFilename ); +} + +/************************************************************************/ +/* HFAGetRasterInfo() */ +/************************************************************************/ + +CPLErr HFAGetRasterInfo( HFAHandle hHFA, int * pnXSize, int * pnYSize, + int * pnBands ) + +{ + if( pnXSize != NULL ) + *pnXSize = hHFA->nXSize; + if( pnYSize != NULL ) + *pnYSize = hHFA->nYSize; + if( pnBands != NULL ) + *pnBands = hHFA->nBands; + return CE_None; +} + +/************************************************************************/ +/* HFAGetBandInfo() */ +/************************************************************************/ + +CPLErr HFAGetBandInfo( HFAHandle hHFA, int nBand, int * pnDataType, + int * pnBlockXSize, int * pnBlockYSize, + int *pnCompressionType ) + +{ + if( nBand < 0 || nBand > hHFA->nBands ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + HFABand *poBand = hHFA->papoBand[nBand-1]; + + if( pnDataType != NULL ) + *pnDataType = poBand->nDataType; + + if( pnBlockXSize != NULL ) + *pnBlockXSize = poBand->nBlockXSize; + + if( pnBlockYSize != NULL ) + *pnBlockYSize = poBand->nBlockYSize; + +/* -------------------------------------------------------------------- */ +/* Get compression code from RasterDMS. */ +/* -------------------------------------------------------------------- */ + if( pnCompressionType != NULL ) + { + HFAEntry *poDMS; + + *pnCompressionType = 0; + + poDMS = poBand->poNode->GetNamedChild( "RasterDMS" ); + + if( poDMS != NULL ) + *pnCompressionType = poDMS->GetIntField( "compressionType" ); + } + + return( CE_None ); +} + +/************************************************************************/ +/* HFAGetBandNoData() */ +/* */ +/* returns TRUE if value is set, otherwise FALSE. */ +/************************************************************************/ + +int HFAGetBandNoData( HFAHandle hHFA, int nBand, double *pdfNoData ) + +{ + if( nBand < 0 || nBand > hHFA->nBands ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + HFABand *poBand = hHFA->papoBand[nBand-1]; + + if( !poBand->bNoDataSet && poBand->nOverviews > 0 ) + { + poBand = poBand->papoOverviews[0]; + if( poBand == NULL ) + return FALSE; + } + + *pdfNoData = poBand->dfNoData; + return poBand->bNoDataSet; +} + +/************************************************************************/ +/* HFASetBandNoData() */ +/* */ +/* attempts to set a no-data value on the given band */ +/************************************************************************/ + +CPLErr HFASetBandNoData( HFAHandle hHFA, int nBand, double dfValue ) + +{ + if ( nBand < 0 || nBand > hHFA->nBands ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + HFABand *poBand = hHFA->papoBand[nBand - 1]; + + return poBand->SetNoDataValue( dfValue ); +} + +/************************************************************************/ +/* HFAGetOverviewCount() */ +/************************************************************************/ + +int HFAGetOverviewCount( HFAHandle hHFA, int nBand ) + +{ + HFABand *poBand; + + if( nBand < 0 || nBand > hHFA->nBands ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + poBand = hHFA->papoBand[nBand-1]; + poBand->LoadOverviews(); + + return poBand->nOverviews; +} + +/************************************************************************/ +/* HFAGetOverviewInfo() */ +/************************************************************************/ + +CPLErr HFAGetOverviewInfo( HFAHandle hHFA, int nBand, int iOverview, + int * pnXSize, int * pnYSize, + int * pnBlockXSize, int * pnBlockYSize, + int * pnHFADataType ) + +{ + HFABand *poBand; + + if( nBand < 0 || nBand > hHFA->nBands ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + + poBand = hHFA->papoBand[nBand-1]; + poBand->LoadOverviews(); + + if( iOverview < 0 || iOverview >= poBand->nOverviews ) + { + CPLAssert( FALSE ); + return CE_Failure; + } + poBand = poBand->papoOverviews[iOverview]; + if( poBand == NULL ) + { + return CE_Failure; + } + + if( pnXSize != NULL ) + *pnXSize = poBand->nWidth; + + if( pnYSize != NULL ) + *pnYSize = poBand->nHeight; + + if( pnBlockXSize != NULL ) + *pnBlockXSize = poBand->nBlockXSize; + + if( pnBlockYSize != NULL ) + *pnBlockYSize = poBand->nBlockYSize; + + if( pnHFADataType != NULL ) + *pnHFADataType = poBand->nDataType; + + return( CE_None ); +} + +/************************************************************************/ +/* HFAGetRasterBlock() */ +/************************************************************************/ + +CPLErr HFAGetRasterBlock( HFAHandle hHFA, int nBand, + int nXBlock, int nYBlock, void * pData ) + +{ + return HFAGetRasterBlockEx(hHFA, nBand, nXBlock, nYBlock, pData, -1); +} + +/************************************************************************/ +/* HFAGetRasterBlockEx() */ +/************************************************************************/ + +CPLErr HFAGetRasterBlockEx( HFAHandle hHFA, int nBand, + int nXBlock, int nYBlock, void * pData, int nDataSize ) + +{ + if( nBand < 1 || nBand > hHFA->nBands ) + return CE_Failure; + + return( hHFA->papoBand[nBand-1]->GetRasterBlock(nXBlock,nYBlock,pData,nDataSize) ); +} + +/************************************************************************/ +/* HFAGetOverviewRasterBlock() */ +/************************************************************************/ + +CPLErr HFAGetOverviewRasterBlock( HFAHandle hHFA, int nBand, int iOverview, + int nXBlock, int nYBlock, void * pData ) + +{ + return HFAGetOverviewRasterBlockEx(hHFA, nBand, iOverview, nXBlock, nYBlock, pData, -1); +} + +/************************************************************************/ +/* HFAGetOverviewRasterBlockEx() */ +/************************************************************************/ + +CPLErr HFAGetOverviewRasterBlockEx( HFAHandle hHFA, int nBand, int iOverview, + int nXBlock, int nYBlock, void * pData, int nDataSize ) + +{ + if( nBand < 1 || nBand > hHFA->nBands ) + return CE_Failure; + + if( iOverview < 0 || iOverview >= hHFA->papoBand[nBand-1]->nOverviews ) + return CE_Failure; + + return( hHFA->papoBand[nBand-1]->papoOverviews[iOverview]-> + GetRasterBlock(nXBlock,nYBlock,pData, nDataSize) ); +} + +/************************************************************************/ +/* HFASetRasterBlock() */ +/************************************************************************/ + +CPLErr HFASetRasterBlock( HFAHandle hHFA, int nBand, + int nXBlock, int nYBlock, void * pData ) + +{ + if( nBand < 1 || nBand > hHFA->nBands ) + return CE_Failure; + + return( hHFA->papoBand[nBand-1]->SetRasterBlock(nXBlock,nYBlock,pData) ); +} + +/************************************************************************/ +/* HFASetRasterBlock() */ +/************************************************************************/ + +CPLErr HFASetOverviewRasterBlock( HFAHandle hHFA, int nBand, int iOverview, + int nXBlock, int nYBlock, void * pData ) + +{ + if( nBand < 1 || nBand > hHFA->nBands ) + return CE_Failure; + + if( iOverview < 0 || iOverview >= hHFA->papoBand[nBand-1]->nOverviews ) + return CE_Failure; + + return( hHFA->papoBand[nBand-1]->papoOverviews[iOverview]-> + SetRasterBlock(nXBlock,nYBlock,pData) ); +} + +/************************************************************************/ +/* HFAGetBandName() */ +/************************************************************************/ + +const char * HFAGetBandName( HFAHandle hHFA, int nBand ) +{ + if( nBand < 1 || nBand > hHFA->nBands ) + return ""; + + return( hHFA->papoBand[nBand-1]->GetBandName() ); +} + +/************************************************************************/ +/* HFASetBandName() */ +/************************************************************************/ + +void HFASetBandName( HFAHandle hHFA, int nBand, const char *pszName ) +{ + if( nBand < 1 || nBand > hHFA->nBands ) + return; + + hHFA->papoBand[nBand-1]->SetBandName( pszName ); +} + +/************************************************************************/ +/* HFAGetDataTypeBits() */ +/************************************************************************/ + +int HFAGetDataTypeBits( int nDataType ) + +{ + switch( nDataType ) + { + case EPT_u1: + return 1; + + case EPT_u2: + return 2; + + case EPT_u4: + return 4; + + case EPT_u8: + case EPT_s8: + return 8; + + case EPT_u16: + case EPT_s16: + return 16; + + case EPT_u32: + case EPT_s32: + case EPT_f32: + return 32; + + case EPT_f64: + case EPT_c64: + return 64; + + case EPT_c128: + return 128; + } + + return 0; +} + +/************************************************************************/ +/* HFAGetDataTypeName() */ +/************************************************************************/ + +const char *HFAGetDataTypeName( int nDataType ) + +{ + switch( nDataType ) + { + case EPT_u1: + return "u1"; + + case EPT_u2: + return "u2"; + + case EPT_u4: + return "u4"; + + case EPT_u8: + return "u8"; + + case EPT_s8: + return "s8"; + + case EPT_u16: + return "u16"; + + case EPT_s16: + return "s16"; + + case EPT_u32: + return "u32"; + + case EPT_s32: + return "s32"; + + case EPT_f32: + return "f32"; + + case EPT_f64: + return "f64"; + + case EPT_c64: + return "c64"; + + case EPT_c128: + return "c128"; + + default: + return "unknown"; + } +} + +/************************************************************************/ +/* HFAGetMapInfo() */ +/************************************************************************/ + +const Eprj_MapInfo *HFAGetMapInfo( HFAHandle hHFA ) + +{ + HFAEntry *poMIEntry; + Eprj_MapInfo *psMapInfo; + + if( hHFA->nBands < 1 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Do we already have it? */ +/* -------------------------------------------------------------------- */ + if( hHFA->pMapInfo != NULL ) + return( (Eprj_MapInfo *) hHFA->pMapInfo ); + +/* -------------------------------------------------------------------- */ +/* Get the HFA node. If we don't find it under the usual name */ +/* we search for any node of the right type (#3338). */ +/* -------------------------------------------------------------------- */ + poMIEntry = hHFA->papoBand[0]->poNode->GetNamedChild( "Map_Info" ); + if( poMIEntry == NULL ) + { + HFAEntry *poChild; + for( poChild = hHFA->papoBand[0]->poNode->GetChild(); + poChild != NULL && poMIEntry == NULL; + poChild = poChild->GetNext() ) + { + if( EQUAL(poChild->GetType(),"Eprj_MapInfo") ) + poMIEntry = poChild; + } + } + + if( poMIEntry == NULL ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Allocate the structure. */ +/* -------------------------------------------------------------------- */ + psMapInfo = (Eprj_MapInfo *) CPLCalloc(sizeof(Eprj_MapInfo),1); + +/* -------------------------------------------------------------------- */ +/* Fetch the fields. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + psMapInfo->proName = CPLStrdup(poMIEntry->GetStringField("proName")); + + psMapInfo->upperLeftCenter.x = + poMIEntry->GetDoubleField("upperLeftCenter.x"); + psMapInfo->upperLeftCenter.y = + poMIEntry->GetDoubleField("upperLeftCenter.y"); + + psMapInfo->lowerRightCenter.x = + poMIEntry->GetDoubleField("lowerRightCenter.x"); + psMapInfo->lowerRightCenter.y = + poMIEntry->GetDoubleField("lowerRightCenter.y"); + + psMapInfo->pixelSize.width = + poMIEntry->GetDoubleField("pixelSize.width",&eErr); + psMapInfo->pixelSize.height = + poMIEntry->GetDoubleField("pixelSize.height",&eErr); + + // The following is basically a hack to get files with + // non-standard MapInfo's that misname the pixelSize fields. (#3338) + if( eErr != CE_None ) + { + psMapInfo->pixelSize.width = + poMIEntry->GetDoubleField("pixelSize.x"); + psMapInfo->pixelSize.height = + poMIEntry->GetDoubleField("pixelSize.y"); + } + + psMapInfo->units = CPLStrdup(poMIEntry->GetStringField("units")); + + hHFA->pMapInfo = (void *) psMapInfo; + + return psMapInfo; +} + +/************************************************************************/ +/* HFAInvGeoTransform() */ +/************************************************************************/ + +static int HFAInvGeoTransform( double *gt_in, double *gt_out ) + +{ + double det, inv_det; + + /* we assume a 3rd row that is [1 0 0] */ + + /* Compute determinate */ + + det = gt_in[1] * gt_in[5] - gt_in[2] * gt_in[4]; + + if( fabs(det) < 0.000000000000001 ) + return 0; + + inv_det = 1.0 / det; + + /* compute adjoint, and devide by determinate */ + + gt_out[1] = gt_in[5] * inv_det; + gt_out[4] = -gt_in[4] * inv_det; + + gt_out[2] = -gt_in[2] * inv_det; + gt_out[5] = gt_in[1] * inv_det; + + gt_out[0] = ( gt_in[2] * gt_in[3] - gt_in[0] * gt_in[5]) * inv_det; + gt_out[3] = (-gt_in[1] * gt_in[3] + gt_in[0] * gt_in[4]) * inv_det; + + return 1; +} + +/************************************************************************/ +/* HFAGetGeoTransform() */ +/************************************************************************/ + +int HFAGetGeoTransform( HFAHandle hHFA, double *padfGeoTransform ) + +{ + const Eprj_MapInfo *psMapInfo = HFAGetMapInfo( hHFA ); + + padfGeoTransform[0] = 0.0; + padfGeoTransform[1] = 1.0; + padfGeoTransform[2] = 0.0; + padfGeoTransform[3] = 0.0; + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = 1.0; + +/* -------------------------------------------------------------------- */ +/* Simple (north up) MapInfo approach. */ +/* -------------------------------------------------------------------- */ + if( psMapInfo != NULL ) + { + padfGeoTransform[0] = psMapInfo->upperLeftCenter.x + - psMapInfo->pixelSize.width*0.5; + padfGeoTransform[1] = psMapInfo->pixelSize.width; + if(padfGeoTransform[1] == 0.0) + padfGeoTransform[1] = 1.0; + padfGeoTransform[2] = 0.0; + if( psMapInfo->upperLeftCenter.y >= psMapInfo->lowerRightCenter.y ) + padfGeoTransform[5] = - psMapInfo->pixelSize.height; + else + padfGeoTransform[5] = psMapInfo->pixelSize.height; + if(padfGeoTransform[5] == 0.0) + padfGeoTransform[5] = 1.0; + + padfGeoTransform[3] = psMapInfo->upperLeftCenter.y + - padfGeoTransform[5]*0.5; + padfGeoTransform[4] = 0.0; + + // special logic to fixup odd angular units. + if( EQUAL(psMapInfo->units,"ds") ) + { + padfGeoTransform[0] /= 3600.0; + padfGeoTransform[1] /= 3600.0; + padfGeoTransform[2] /= 3600.0; + padfGeoTransform[3] /= 3600.0; + padfGeoTransform[4] /= 3600.0; + padfGeoTransform[5] /= 3600.0; + } + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Try for a MapToPixelXForm affine polynomial supporting */ +/* rotated and sheared affine transformations. */ +/* -------------------------------------------------------------------- */ + if( hHFA->nBands == 0 ) + return FALSE; + + HFAEntry *poXForm0 = + hHFA->papoBand[0]->poNode->GetNamedChild( "MapToPixelXForm.XForm0" ); + + if( poXForm0 == NULL ) + return FALSE; + + if( poXForm0->GetIntField( "order" ) != 1 + || poXForm0->GetIntField( "numdimtransform" ) != 2 + || poXForm0->GetIntField( "numdimpolynomial" ) != 2 + || poXForm0->GetIntField( "termcount" ) != 3 ) + return FALSE; + + // Verify that there aren't any further xform steps. + if( hHFA->papoBand[0]->poNode->GetNamedChild( "MapToPixelXForm.XForm1" ) + != NULL ) + return FALSE; + + // we should check that the exponent list is 0 0 1 0 0 1 but + // we don't because we are lazy + + // fetch geotransform values. + double adfXForm[6]; + + adfXForm[0] = poXForm0->GetDoubleField( "polycoefvector[0]" ); + adfXForm[1] = poXForm0->GetDoubleField( "polycoefmtx[0]" ); + adfXForm[4] = poXForm0->GetDoubleField( "polycoefmtx[1]" ); + adfXForm[3] = poXForm0->GetDoubleField( "polycoefvector[1]" ); + adfXForm[2] = poXForm0->GetDoubleField( "polycoefmtx[2]" ); + adfXForm[5] = poXForm0->GetDoubleField( "polycoefmtx[3]" ); + + // invert + + HFAInvGeoTransform( adfXForm, padfGeoTransform ); + + // Adjust origin from center of top left pixel to top left corner + // of top left pixel. + + padfGeoTransform[0] -= padfGeoTransform[1] * 0.5; + padfGeoTransform[0] -= padfGeoTransform[2] * 0.5; + padfGeoTransform[3] -= padfGeoTransform[4] * 0.5; + padfGeoTransform[3] -= padfGeoTransform[5] * 0.5; + + return TRUE; +} + +/************************************************************************/ +/* HFASetMapInfo() */ +/************************************************************************/ + +CPLErr HFASetMapInfo( HFAHandle hHFA, const Eprj_MapInfo *poMapInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Loop over bands, setting information on each one. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < hHFA->nBands; iBand++ ) + { + HFAEntry *poMIEntry; + +/* -------------------------------------------------------------------- */ +/* Create a new Map_Info if there isn't one present already. */ +/* -------------------------------------------------------------------- */ + poMIEntry = hHFA->papoBand[iBand]->poNode->GetNamedChild( "Map_Info" ); + if( poMIEntry == NULL ) + { + poMIEntry = new HFAEntry( hHFA, "Map_Info", "Eprj_MapInfo", + hHFA->papoBand[iBand]->poNode ); + } + + poMIEntry->MarkDirty(); + +/* -------------------------------------------------------------------- */ +/* Ensure we have enough space for all the data. */ +/* -------------------------------------------------------------------- */ + int nSize; + GByte *pabyData; + + nSize = 48 + 40 + + strlen(poMapInfo->proName) + 1 + + strlen(poMapInfo->units) + 1; + + pabyData = poMIEntry->MakeData( nSize ); + memset( pabyData, 0, nSize ); + + poMIEntry->SetPosition(); + +/* -------------------------------------------------------------------- */ +/* Write the various fields. */ +/* -------------------------------------------------------------------- */ + poMIEntry->SetStringField( "proName", poMapInfo->proName ); + + poMIEntry->SetDoubleField( "upperLeftCenter.x", + poMapInfo->upperLeftCenter.x ); + poMIEntry->SetDoubleField( "upperLeftCenter.y", + poMapInfo->upperLeftCenter.y ); + + poMIEntry->SetDoubleField( "lowerRightCenter.x", + poMapInfo->lowerRightCenter.x ); + poMIEntry->SetDoubleField( "lowerRightCenter.y", + poMapInfo->lowerRightCenter.y ); + + poMIEntry->SetDoubleField( "pixelSize.width", + poMapInfo->pixelSize.width ); + poMIEntry->SetDoubleField( "pixelSize.height", + poMapInfo->pixelSize.height ); + + poMIEntry->SetStringField( "units", poMapInfo->units ); + } + + return CE_None; +} + +/************************************************************************/ +/* HFAGetPEString() */ +/* */ +/* Some files have a ProjectionX node contining the ESRI style */ +/* PE_STRING. This function allows fetching from it. */ +/************************************************************************/ + +char *HFAGetPEString( HFAHandle hHFA ) + +{ + if( hHFA->nBands == 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get the HFA node. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poProX; + + poProX = hHFA->papoBand[0]->poNode->GetNamedChild( "ProjectionX" ); + if( poProX == NULL ) + return NULL; + + const char *pszType = poProX->GetStringField( "projection.type.string" ); + if( pszType == NULL || !EQUAL(pszType,"PE_COORDSYS") ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Use a gross hack to scan ahead to the actual projection */ +/* string. We do it this way because we don't have general */ +/* handling for MIFObjects. */ +/* -------------------------------------------------------------------- */ + GByte *pabyData = poProX->GetData(); + int nDataSize = poProX->GetDataSize(); + + while( nDataSize > 10 + && !EQUALN((const char *) pabyData,"PE_COORDSYS,.",13) ) { + pabyData++; + nDataSize--; + } + + if( nDataSize < 31 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Skip ahead to the actual string. */ +/* -------------------------------------------------------------------- */ + pabyData += 30; + nDataSize -= 30; + + return CPLStrdup( (const char *) pabyData ); +} + +/************************************************************************/ +/* HFASetPEString() */ +/************************************************************************/ + +CPLErr HFASetPEString( HFAHandle hHFA, const char *pszPEString ) + +{ +/* -------------------------------------------------------------------- */ +/* Loop over bands, setting information on each one. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 0; iBand < hHFA->nBands; iBand++ ) + { + HFAEntry *poProX; + +/* -------------------------------------------------------------------- */ +/* Verify we don't already have the node, since update-in-place */ +/* is likely to be more complicated. */ +/* -------------------------------------------------------------------- */ + poProX = hHFA->papoBand[iBand]->poNode->GetNamedChild( "ProjectionX" ); + +/* -------------------------------------------------------------------- */ +/* If we are setting an empty string then a missing entry is */ +/* equivelent. */ +/* -------------------------------------------------------------------- */ + if( strlen(pszPEString) == 0 && poProX == NULL ) + continue; + +/* -------------------------------------------------------------------- */ +/* Create the node. */ +/* -------------------------------------------------------------------- */ + if( poProX == NULL ) + { + poProX = new HFAEntry( hHFA, "ProjectionX","Eprj_MapProjection842", + hHFA->papoBand[iBand]->poNode ); + if( poProX == NULL || poProX->GetTypeObject() == NULL ) + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Prepare the data area with some extra space just in case. */ +/* -------------------------------------------------------------------- */ + GByte *pabyData = poProX->MakeData( 700 + strlen(pszPEString) ); + if( !pabyData ) + return CE_Failure; + + memset( pabyData, 0, 250+strlen(pszPEString) ); + + poProX->SetPosition(); + + poProX->SetStringField( "projection.type.string", "PE_COORDSYS" ); + poProX->SetStringField( "projection.MIFDictionary.string", + "{0:pcstring,}Emif_String,{1:x{0:pcstring,}Emif_String,coordSys,}PE_COORDSYS,." ); + +/* -------------------------------------------------------------------- */ +/* Use a gross hack to scan ahead to the actual projection */ +/* string. We do it this way because we don't have general */ +/* handling for MIFObjects. */ +/* -------------------------------------------------------------------- */ + pabyData = poProX->GetData(); + int nDataSize = poProX->GetDataSize(); + GUInt32 iOffset = poProX->GetDataPos(); + GUInt32 nSize; + + while( nDataSize > 10 + && !EQUALN((const char *) pabyData,"PE_COORDSYS,.",13) ) { + pabyData++; + nDataSize--; + iOffset++; + } + + CPLAssert( nDataSize > (int) strlen(pszPEString) + 10 ); + + pabyData += 14; + iOffset += 14; + +/* -------------------------------------------------------------------- */ +/* Set the size and offset of the mifobject. */ +/* -------------------------------------------------------------------- */ + iOffset += 8; + + nSize = strlen(pszPEString) + 9; + + HFAStandard( 4, &nSize ); + memcpy( pabyData, &nSize, 4 ); + pabyData += 4; + + HFAStandard( 4, &iOffset ); + memcpy( pabyData, &iOffset, 4 ); + pabyData += 4; + +/* -------------------------------------------------------------------- */ +/* Set the size and offset of the string value. */ +/* -------------------------------------------------------------------- */ + nSize = strlen(pszPEString) + 1; + + HFAStandard( 4, &nSize ); + memcpy( pabyData, &nSize, 4 ); + pabyData += 4; + + iOffset = 8; + HFAStandard( 4, &iOffset ); + memcpy( pabyData, &iOffset, 4 ); + pabyData += 4; + +/* -------------------------------------------------------------------- */ +/* Place the string itself. */ +/* -------------------------------------------------------------------- */ + memcpy( pabyData, pszPEString, strlen(pszPEString)+1 ); + + poProX->SetStringField( "title.string", "PE" ); + } + + return CE_None; +} + +/************************************************************************/ +/* HFAGetProParameters() */ +/************************************************************************/ + +const Eprj_ProParameters *HFAGetProParameters( HFAHandle hHFA ) + +{ + HFAEntry *poMIEntry; + Eprj_ProParameters *psProParms; + int i; + + if( hHFA->nBands < 1 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Do we already have it? */ +/* -------------------------------------------------------------------- */ + if( hHFA->pProParameters != NULL ) + return( (Eprj_ProParameters *) hHFA->pProParameters ); + +/* -------------------------------------------------------------------- */ +/* Get the HFA node. */ +/* -------------------------------------------------------------------- */ + poMIEntry = hHFA->papoBand[0]->poNode->GetNamedChild( "Projection" ); + if( poMIEntry == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Allocate the structure. */ +/* -------------------------------------------------------------------- */ + psProParms = (Eprj_ProParameters *)CPLCalloc(sizeof(Eprj_ProParameters),1); + +/* -------------------------------------------------------------------- */ +/* Fetch the fields. */ +/* -------------------------------------------------------------------- */ + psProParms->proType = (Eprj_ProType) poMIEntry->GetIntField("proType"); + psProParms->proNumber = poMIEntry->GetIntField("proNumber"); + psProParms->proExeName =CPLStrdup(poMIEntry->GetStringField("proExeName")); + psProParms->proName = CPLStrdup(poMIEntry->GetStringField("proName")); + psProParms->proZone = poMIEntry->GetIntField("proZone"); + + for( i = 0; i < 15; i++ ) + { + char szFieldName[40]; + + sprintf( szFieldName, "proParams[%d]", i ); + psProParms->proParams[i] = poMIEntry->GetDoubleField(szFieldName); + } + + psProParms->proSpheroid.sphereName = + CPLStrdup(poMIEntry->GetStringField("proSpheroid.sphereName")); + psProParms->proSpheroid.a = poMIEntry->GetDoubleField("proSpheroid.a"); + psProParms->proSpheroid.b = poMIEntry->GetDoubleField("proSpheroid.b"); + psProParms->proSpheroid.eSquared = + poMIEntry->GetDoubleField("proSpheroid.eSquared"); + psProParms->proSpheroid.radius = + poMIEntry->GetDoubleField("proSpheroid.radius"); + + hHFA->pProParameters = (void *) psProParms; + + return psProParms; +} + +/************************************************************************/ +/* HFASetProParameters() */ +/************************************************************************/ + +CPLErr HFASetProParameters( HFAHandle hHFA, const Eprj_ProParameters *poPro ) + +{ +/* -------------------------------------------------------------------- */ +/* Loop over bands, setting information on each one. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < hHFA->nBands; iBand++ ) + { + HFAEntry *poMIEntry; + +/* -------------------------------------------------------------------- */ +/* Create a new Projection if there isn't one present already. */ +/* -------------------------------------------------------------------- */ + poMIEntry = hHFA->papoBand[iBand]->poNode->GetNamedChild("Projection"); + if( poMIEntry == NULL ) + { + poMIEntry = new HFAEntry( hHFA, "Projection","Eprj_ProParameters", + hHFA->papoBand[iBand]->poNode ); + } + + poMIEntry->MarkDirty(); + +/* -------------------------------------------------------------------- */ +/* Ensure we have enough space for all the data. */ +/* -------------------------------------------------------------------- */ + int nSize; + GByte *pabyData; + + nSize = 34 + 15 * 8 + + 8 + strlen(poPro->proName) + 1 + + 32 + 8 + strlen(poPro->proSpheroid.sphereName) + 1; + + if( poPro->proExeName != NULL ) + nSize += strlen(poPro->proExeName) + 1; + + pabyData = poMIEntry->MakeData( nSize ); + if(!pabyData) + return CE_Failure; + + poMIEntry->SetPosition(); + + // Initialize the whole thing to zeros for a clean start. + memset( poMIEntry->GetData(), 0, poMIEntry->GetDataSize() ); + +/* -------------------------------------------------------------------- */ +/* Write the various fields. */ +/* -------------------------------------------------------------------- */ + poMIEntry->SetIntField( "proType", poPro->proType ); + + poMIEntry->SetIntField( "proNumber", poPro->proNumber ); + + poMIEntry->SetStringField( "proExeName", poPro->proExeName ); + poMIEntry->SetStringField( "proName", poPro->proName ); + poMIEntry->SetIntField( "proZone", poPro->proZone ); + poMIEntry->SetDoubleField( "proParams[0]", poPro->proParams[0] ); + poMIEntry->SetDoubleField( "proParams[1]", poPro->proParams[1] ); + poMIEntry->SetDoubleField( "proParams[2]", poPro->proParams[2] ); + poMIEntry->SetDoubleField( "proParams[3]", poPro->proParams[3] ); + poMIEntry->SetDoubleField( "proParams[4]", poPro->proParams[4] ); + poMIEntry->SetDoubleField( "proParams[5]", poPro->proParams[5] ); + poMIEntry->SetDoubleField( "proParams[6]", poPro->proParams[6] ); + poMIEntry->SetDoubleField( "proParams[7]", poPro->proParams[7] ); + poMIEntry->SetDoubleField( "proParams[8]", poPro->proParams[8] ); + poMIEntry->SetDoubleField( "proParams[9]", poPro->proParams[9] ); + poMIEntry->SetDoubleField( "proParams[10]", poPro->proParams[10] ); + poMIEntry->SetDoubleField( "proParams[11]", poPro->proParams[11] ); + poMIEntry->SetDoubleField( "proParams[12]", poPro->proParams[12] ); + poMIEntry->SetDoubleField( "proParams[13]", poPro->proParams[13] ); + poMIEntry->SetDoubleField( "proParams[14]", poPro->proParams[14] ); + poMIEntry->SetStringField( "proSpheroid.sphereName", + poPro->proSpheroid.sphereName ); + poMIEntry->SetDoubleField( "proSpheroid.a", + poPro->proSpheroid.a ); + poMIEntry->SetDoubleField( "proSpheroid.b", + poPro->proSpheroid.b ); + poMIEntry->SetDoubleField( "proSpheroid.eSquared", + poPro->proSpheroid.eSquared ); + poMIEntry->SetDoubleField( "proSpheroid.radius", + poPro->proSpheroid.radius ); + } + + return CE_None; +} + +/************************************************************************/ +/* HFAGetDatum() */ +/************************************************************************/ + +const Eprj_Datum *HFAGetDatum( HFAHandle hHFA ) + +{ + HFAEntry *poMIEntry; + Eprj_Datum *psDatum; + int i; + + if( hHFA->nBands < 1 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Do we already have it? */ +/* -------------------------------------------------------------------- */ + if( hHFA->pDatum != NULL ) + return( (Eprj_Datum *) hHFA->pDatum ); + +/* -------------------------------------------------------------------- */ +/* Get the HFA node. */ +/* -------------------------------------------------------------------- */ + poMIEntry = hHFA->papoBand[0]->poNode->GetNamedChild( "Projection.Datum" ); + if( poMIEntry == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Allocate the structure. */ +/* -------------------------------------------------------------------- */ + psDatum = (Eprj_Datum *) CPLCalloc(sizeof(Eprj_Datum),1); + +/* -------------------------------------------------------------------- */ +/* Fetch the fields. */ +/* -------------------------------------------------------------------- */ + psDatum->datumname = CPLStrdup(poMIEntry->GetStringField("datumname")); + psDatum->type = (Eprj_DatumType) poMIEntry->GetIntField("type"); + + for( i = 0; i < 7; i++ ) + { + char szFieldName[30]; + + sprintf( szFieldName, "params[%d]", i ); + psDatum->params[i] = poMIEntry->GetDoubleField(szFieldName); + } + + psDatum->gridname = CPLStrdup(poMIEntry->GetStringField("gridname")); + + hHFA->pDatum = (void *) psDatum; + + return psDatum; +} + +/************************************************************************/ +/* HFASetDatum() */ +/************************************************************************/ + +CPLErr HFASetDatum( HFAHandle hHFA, const Eprj_Datum *poDatum ) + +{ +/* -------------------------------------------------------------------- */ +/* Loop over bands, setting information on each one. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < hHFA->nBands; iBand++ ) + { + HFAEntry *poDatumEntry=NULL, *poProParms; + +/* -------------------------------------------------------------------- */ +/* Create a new Projection if there isn't one present already. */ +/* -------------------------------------------------------------------- */ + poProParms = + hHFA->papoBand[iBand]->poNode->GetNamedChild("Projection"); + if( poProParms == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't add Eprj_Datum with no Eprj_ProjParameters." ); + return CE_Failure; + } + + poDatumEntry = poProParms->GetNamedChild("Datum"); + if( poDatumEntry == NULL ) + { + poDatumEntry = new HFAEntry( hHFA, "Datum","Eprj_Datum", + poProParms ); + } + + poDatumEntry->MarkDirty(); + +/* -------------------------------------------------------------------- */ +/* Ensure we have enough space for all the data. */ +/* -------------------------------------------------------------------- */ + int nSize; + GByte *pabyData; + + nSize = 26 + strlen(poDatum->datumname) + 1 + 7*8; + + if( poDatum->gridname != NULL ) + nSize += strlen(poDatum->gridname) + 1; + + pabyData = poDatumEntry->MakeData( nSize ); + if(!pabyData) + return CE_Failure; + + poDatumEntry->SetPosition(); + + // Initialize the whole thing to zeros for a clean start. + memset( poDatumEntry->GetData(), 0, poDatumEntry->GetDataSize() ); + +/* -------------------------------------------------------------------- */ +/* Write the various fields. */ +/* -------------------------------------------------------------------- */ + poDatumEntry->SetStringField( "datumname", poDatum->datumname ); + poDatumEntry->SetIntField( "type", poDatum->type ); + + poDatumEntry->SetDoubleField( "params[0]", poDatum->params[0] ); + poDatumEntry->SetDoubleField( "params[1]", poDatum->params[1] ); + poDatumEntry->SetDoubleField( "params[2]", poDatum->params[2] ); + poDatumEntry->SetDoubleField( "params[3]", poDatum->params[3] ); + poDatumEntry->SetDoubleField( "params[4]", poDatum->params[4] ); + poDatumEntry->SetDoubleField( "params[5]", poDatum->params[5] ); + poDatumEntry->SetDoubleField( "params[6]", poDatum->params[6] ); + + poDatumEntry->SetStringField( "gridname", poDatum->gridname ); + } + + return CE_None; +} + +/************************************************************************/ +/* HFAGetPCT() */ +/* */ +/* Read the PCT from a band, if it has one. */ +/************************************************************************/ + +CPLErr HFAGetPCT( HFAHandle hHFA, int nBand, int *pnColors, + double **ppadfRed, double **ppadfGreen, + double **ppadfBlue , double **ppadfAlpha, + double **ppadfBins ) + +{ + if( nBand < 1 || nBand > hHFA->nBands ) + return CE_Failure; + + return( hHFA->papoBand[nBand-1]->GetPCT( pnColors, ppadfRed, + ppadfGreen, ppadfBlue, + ppadfAlpha, ppadfBins ) ); +} + +/************************************************************************/ +/* HFASetPCT() */ +/* */ +/* Set the PCT on a band. */ +/************************************************************************/ + +CPLErr HFASetPCT( HFAHandle hHFA, int nBand, int nColors, + double *padfRed, double *padfGreen, double *padfBlue, + double *padfAlpha ) + +{ + if( nBand < 1 || nBand > hHFA->nBands ) + return CE_Failure; + + return( hHFA->papoBand[nBand-1]->SetPCT( nColors, padfRed, + padfGreen, padfBlue, padfAlpha ) ); +} + +/************************************************************************/ +/* HFAGetDataRange() */ +/************************************************************************/ + +CPLErr HFAGetDataRange( HFAHandle hHFA, int nBand, + double * pdfMin, double *pdfMax ) + +{ + HFAEntry *poBinInfo; + + if( nBand < 1 || nBand > hHFA->nBands ) + return CE_Failure; + + poBinInfo = hHFA->papoBand[nBand-1]->poNode->GetNamedChild("Statistics" ); + + if( poBinInfo == NULL ) + return( CE_Failure ); + + *pdfMin = poBinInfo->GetDoubleField( "minimum" ); + *pdfMax = poBinInfo->GetDoubleField( "maximum" ); + + if( *pdfMax > *pdfMin ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* HFADumpNode() */ +/************************************************************************/ + +static void HFADumpNode( HFAEntry *poEntry, int nIndent, int bVerbose, + FILE * fp ) + +{ + static char szSpaces[256]; + int i; + + for( i = 0; i < nIndent*2; i++ ) + szSpaces[i] = ' '; + szSpaces[nIndent*2] = '\0'; + + fprintf( fp, "%s%s(%s) @ %d + %d @ %d\n", szSpaces, + poEntry->GetName(), poEntry->GetType(), + poEntry->GetFilePos(), + poEntry->GetDataSize(), poEntry->GetDataPos() ); + + if( bVerbose ) + { + strcat( szSpaces, "+ " ); + poEntry->DumpFieldValues( fp, szSpaces ); + fprintf( fp, "\n" ); + } + + if( poEntry->GetChild() != NULL ) + HFADumpNode( poEntry->GetChild(), nIndent+1, bVerbose, fp ); + + if( poEntry->GetNext() != NULL ) + HFADumpNode( poEntry->GetNext(), nIndent, bVerbose, fp ); +} + +/************************************************************************/ +/* HFADumpTree() */ +/* */ +/* Dump the tree of information in a HFA file. */ +/************************************************************************/ + +void HFADumpTree( HFAHandle hHFA, FILE * fpOut ) + +{ + HFADumpNode( hHFA->poRoot, 0, TRUE, fpOut ); +} + +/************************************************************************/ +/* HFADumpDictionary() */ +/* */ +/* Dump the dictionary (in raw, and parsed form) to the named */ +/* device. */ +/************************************************************************/ + +void HFADumpDictionary( HFAHandle hHFA, FILE * fpOut ) + +{ + fprintf( fpOut, "%s\n", hHFA->pszDictionary ); + + hHFA->poDictionary->Dump( fpOut ); +} + +/************************************************************************/ +/* HFAStandard() */ +/* */ +/* Swap byte order on MSB systems. */ +/************************************************************************/ + +#ifdef CPL_MSB +void HFAStandard( int nBytes, void * pData ) + +{ + int i; + GByte *pabyData = (GByte *) pData; + + for( i = nBytes/2-1; i >= 0; i-- ) + { + GByte byTemp; + + byTemp = pabyData[i]; + pabyData[i] = pabyData[nBytes-i-1]; + pabyData[nBytes-i-1] = byTemp; + } +} +#endif + +/* ==================================================================== */ +/* Default data dictionary. Emitted verbatim into the imagine */ +/* file. */ +/* ==================================================================== */ + +static const char *aszDefaultDD[] = { +"{1:lversion,1:LfreeList,1:LrootEntryPtr,1:sentryHeaderLength,1:LdictionaryPtr,}Ehfa_File,{1:Lnext,1:Lprev,1:Lparent,1:Lchild,1:Ldata,1:ldataSize,64:cname,32:ctype,1:tmodTime,}Ehfa_Entry,{16:clabel,1:LheaderPtr,}Ehfa_HeaderTag,{1:LfreeList,1:lfreeSize,}Ehfa_FreeListNode,{1:lsize,1:Lptr,}Ehfa_Data,{1:lwidth,1:lheight,1:e3:thematic,athematic,fft of real-valued data,layerType,", +"1:e13:u1,u2,u4,u8,s8,u16,s16,u32,s32,f32,f64,c64,c128,pixelType,1:lblockWidth,1:lblockHeight,}Eimg_Layer,{1:lwidth,1:lheight,1:e3:thematic,athematic,fft of real-valued data,layerType,1:e13:u1,u2,u4,u8,s8,u16,s16,u32,s32,f32,f64,c64,c128,pixelType,1:lblockWidth,1:lblockHeight,}Eimg_Layer_SubSample,{1:e2:raster,vector,type,1:LdictionaryPtr,}Ehfa_Layer,{1:LspaceUsedForRasterData,}ImgFormatInfo831,{1:sfileCode,1:Loffset,1:lsize,1:e2:false,true,logvalid,", +"1:e2:no compression,ESRI GRID compression,compressionType,}Edms_VirtualBlockInfo,{1:lmin,1:lmax,}Edms_FreeIDList,{1:lnumvirtualblocks,1:lnumobjectsperblock,1:lnextobjectnum,1:e2:no compression,RLC compression,compressionType,0:poEdms_VirtualBlockInfo,blockinfo,0:poEdms_FreeIDList,freelist,1:tmodTime,}Edms_State,{0:pcstring,}Emif_String,{1:oEmif_String,fileName,2:LlayerStackValidFlagsOffset,2:LlayerStackDataOffset,1:LlayerStackCount,1:LlayerStackIndex,}ImgExternalRaster,{1:oEmif_String,algorithm,0:poEmif_String,nameList,}Eimg_RRDNamesList,{1:oEmif_String,projection,1:oEmif_String,units,}Eimg_MapInformation,", +"{1:oEmif_String,dependent,}Eimg_DependentFile,{1:oEmif_String,ImageLayerName,}Eimg_DependentLayerName,{1:lnumrows,1:lnumcolumns,1:e13:EGDA_TYPE_U1,EGDA_TYPE_U2,EGDA_TYPE_U4,EGDA_TYPE_U8,EGDA_TYPE_S8,EGDA_TYPE_U16,EGDA_TYPE_S16,EGDA_TYPE_U32,EGDA_TYPE_S32,EGDA_TYPE_F32,EGDA_TYPE_F64,EGDA_TYPE_C64,EGDA_TYPE_C128,datatype,1:e4:EGDA_SCALAR_OBJECT,EGDA_TABLE_OBJECT,EGDA_MATRIX_OBJECT,EGDA_RASTER_OBJECT,objecttype,}Egda_BaseData,{1:*bvalueBD,}Eimg_NonInitializedValue,{1:dx,1:dy,}Eprj_Coordinate,{1:dwidth,1:dheight,}Eprj_Size,{0:pcproName,1:*oEprj_Coordinate,upperLeftCenter,", +"1:*oEprj_Coordinate,lowerRightCenter,1:*oEprj_Size,pixelSize,0:pcunits,}Eprj_MapInfo,{0:pcdatumname,1:e3:EPRJ_DATUM_PARAMETRIC,EPRJ_DATUM_GRID,EPRJ_DATUM_REGRESSION,type,0:pdparams,0:pcgridname,}Eprj_Datum,{0:pcsphereName,1:da,1:db,1:deSquared,1:dradius,}Eprj_Spheroid,{1:e2:EPRJ_INTERNAL,EPRJ_EXTERNAL,proType,1:lproNumber,0:pcproExeName,0:pcproName,1:lproZone,0:pdproParams,1:*oEprj_Spheroid,proSpheroid,}Eprj_ProParameters,{1:dminimum,1:dmaximum,1:dmean,1:dmedian,1:dmode,1:dstddev,}Esta_Statistics,{1:lnumBins,1:e4:direct,linear,logarithmic,explicit,binFunctionType,1:dminLimit,1:dmaxLimit,1:*bbinLimits,}Edsc_BinFunction,{0:poEmif_String,LayerNames,1:*bExcludedValues,1:oEmif_String,AOIname,", +"1:lSkipFactorX,1:lSkipFactorY,1:*oEdsc_BinFunction,BinFunction,}Eimg_StatisticsParameters830,{1:lnumrows,}Edsc_Table,{1:lnumRows,1:LcolumnDataPtr,1:e4:integer,real,complex,string,dataType,1:lmaxNumChars,}Edsc_Column,{1:lposition,0:pcname,1:e2:EMSC_FALSE,EMSC_TRUE,editable,1:e3:LEFT,CENTER,RIGHT,alignment,0:pcformat,1:e3:DEFAULT,APPLY,AUTO-APPLY,formulamode,0:pcformula,1:dcolumnwidth,0:pcunits,1:e5:NO_COLOR,RED,GREEN,BLUE,COLOR,colorflag,0:pcgreenname,0:pcbluename,}Eded_ColumnAttributes_1,{1:lversion,1:lnumobjects,1:e2:EAOI_UNION,EAOI_INTERSECTION,operation,}Eaoi_AreaOfInterest,", +"{1:x{0:pcstring,}Emif_String,type,1:x{0:pcstring,}Emif_String,MIFDictionary,0:pCMIFObject,}Emif_MIFObject,", +"{1:x{1:x{0:pcstring,}Emif_String,type,1:x{0:pcstring,}Emif_String,MIFDictionary,0:pCMIFObject,}Emif_MIFObject,projection,1:x{0:pcstring,}Emif_String,title,}Eprj_MapProjection842,", +"{0:poEmif_String,titleList,}Exfr_GenericXFormHeader,{1:lorder,1:lnumdimtransform,1:lnumdimpolynomial,1:ltermcount,0:plexponentlist,1:*bpolycoefmtx,1:*bpolycoefvector,}Efga_Polynomial,", +".", +NULL +}; + + + +/************************************************************************/ +/* HFACreateLL() */ +/* */ +/* Low level creation of an Imagine file. Writes out the */ +/* Ehfa_HeaderTag, dictionary and Ehfa_File. */ +/************************************************************************/ + +HFAHandle HFACreateLL( const char * pszFilename ) + +{ + VSILFILE *fp; + HFAInfo_t *psInfo; + +/* -------------------------------------------------------------------- */ +/* Create the file in the file system. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszFilename, "w+b" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Creation of file %s failed.", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the HFAInfo_t */ +/* -------------------------------------------------------------------- */ + psInfo = (HFAInfo_t *) CPLCalloc(sizeof(HFAInfo_t),1); + + psInfo->fp = fp; + psInfo->eAccess = HFA_Update; + psInfo->nXSize = 0; + psInfo->nYSize = 0; + psInfo->nBands = 0; + psInfo->papoBand = NULL; + psInfo->pMapInfo = NULL; + psInfo->pDatum = NULL; + psInfo->pProParameters = NULL; + psInfo->bTreeDirty = FALSE; + psInfo->pszFilename = CPLStrdup(CPLGetFilename(pszFilename)); + psInfo->pszPath = CPLStrdup(CPLGetPath(pszFilename)); + +/* -------------------------------------------------------------------- */ +/* Write out the Ehfa_HeaderTag */ +/* -------------------------------------------------------------------- */ + GInt32 nHeaderPos; + + VSIFWriteL( (void *) "EHFA_HEADER_TAG", 1, 16, fp ); + + nHeaderPos = 20; + HFAStandard( 4, &nHeaderPos ); + VSIFWriteL( &nHeaderPos, 4, 1, fp ); + +/* -------------------------------------------------------------------- */ +/* Write the Ehfa_File node, locked in at offset 20. */ +/* -------------------------------------------------------------------- */ + GInt32 nVersion = 1, nFreeList = 0, nRootEntry = 0; + GInt16 nEntryHeaderLength = 128; + GInt32 nDictionaryPtr = 38; + + psInfo->nEntryHeaderLength = nEntryHeaderLength; + psInfo->nRootPos = 0; + psInfo->nDictionaryPos = nDictionaryPtr; + psInfo->nVersion = nVersion; + + HFAStandard( 4, &nVersion ); + HFAStandard( 4, &nFreeList ); + HFAStandard( 4, &nRootEntry ); + HFAStandard( 2, &nEntryHeaderLength ); + HFAStandard( 4, &nDictionaryPtr ); + + VSIFWriteL( &nVersion, 4, 1, fp ); + VSIFWriteL( &nFreeList, 4, 1, fp ); + VSIFWriteL( &nRootEntry, 4, 1, fp ); + VSIFWriteL( &nEntryHeaderLength, 2, 1, fp ); + VSIFWriteL( &nDictionaryPtr, 4, 1, fp ); + +/* -------------------------------------------------------------------- */ +/* Write the dictionary, locked in at location 38. Note that */ +/* we jump through a bunch of hoops to operate on the */ +/* dictionary in chunks because some compiles (such as VC++) */ +/* don't allow particularly large static strings. */ +/* -------------------------------------------------------------------- */ + int nDictLen = 0, iChunk; + + for( iChunk = 0; aszDefaultDD[iChunk] != NULL; iChunk++ ) + nDictLen += strlen(aszDefaultDD[iChunk]); + + psInfo->pszDictionary = (char *) CPLMalloc(nDictLen+1); + psInfo->pszDictionary[0] = '\0'; + + for( iChunk = 0; aszDefaultDD[iChunk] != NULL; iChunk++ ) + strcat( psInfo->pszDictionary, aszDefaultDD[iChunk] ); + + VSIFWriteL( (void *) psInfo->pszDictionary, 1, + strlen(psInfo->pszDictionary)+1, fp ); + + psInfo->poDictionary = new HFADictionary( psInfo->pszDictionary ); + + psInfo->nEndOfFile = (GUInt32) VSIFTellL( fp ); + +/* -------------------------------------------------------------------- */ +/* Create a root entry. */ +/* -------------------------------------------------------------------- */ + psInfo->poRoot = new HFAEntry( psInfo, "root", "root", NULL ); + +/* -------------------------------------------------------------------- */ +/* If an .ige or .rrd file exists with the same base name, */ +/* delete them. (#1784) */ +/* -------------------------------------------------------------------- */ + CPLString osExtension = CPLGetExtension(pszFilename); + if( !EQUAL(osExtension,"rrd") && !EQUAL(osExtension,"aux") ) + { + CPLString osPath = CPLGetPath( pszFilename ); + CPLString osBasename = CPLGetBasename( pszFilename ); + VSIStatBufL sStatBuf; + CPLString osSupFile = CPLFormCIFilename( osPath, osBasename, "rrd" ); + + if( VSIStatL( osSupFile, &sStatBuf ) == 0 ) + VSIUnlink( osSupFile ); + + osSupFile = CPLFormCIFilename( osPath, osBasename, "ige" ); + + if( VSIStatL( osSupFile, &sStatBuf ) == 0 ) + VSIUnlink( osSupFile ); + } + + return psInfo; +} + +/************************************************************************/ +/* HFAAllocateSpace() */ +/* */ +/* Return an area in the file to the caller to write the */ +/* requested number of bytes. Currently this is always at the */ +/* end of the file, but eventually we might actually keep track */ +/* of free space. The HFAInfo_t's concept of file size is */ +/* updated, even if nothing ever gets written to this region. */ +/* */ +/* Returns the offset to the requested space, or zero one */ +/* failure. */ +/************************************************************************/ + +GUInt32 HFAAllocateSpace( HFAInfo_t *psInfo, GUInt32 nBytes ) + +{ + /* should check if this will wrap over 2GB limit */ + + psInfo->nEndOfFile += nBytes; + return psInfo->nEndOfFile - nBytes; +} + +/************************************************************************/ +/* HFAFlush() */ +/* */ +/* Write out any dirty tree information to disk, putting the */ +/* disk file in a consistent state. */ +/************************************************************************/ + +CPLErr HFAFlush( HFAHandle hHFA ) + +{ + CPLErr eErr; + + if( !hHFA->bTreeDirty && !hHFA->poDictionary->bDictionaryTextDirty ) + return CE_None; + + CPLAssert( hHFA->poRoot != NULL ); + +/* -------------------------------------------------------------------- */ +/* Flush HFAEntry tree to disk. */ +/* -------------------------------------------------------------------- */ + if( hHFA->bTreeDirty ) + { + eErr = hHFA->poRoot->FlushToDisk(); + if( eErr != CE_None ) + return eErr; + + hHFA->bTreeDirty = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Flush Dictionary to disk. */ +/* -------------------------------------------------------------------- */ + GUInt32 nNewDictionaryPos = hHFA->nDictionaryPos; + + if( hHFA->poDictionary->bDictionaryTextDirty ) + { + VSIFSeekL( hHFA->fp, 0, SEEK_END ); + nNewDictionaryPos = (GUInt32) VSIFTellL( hHFA->fp ); + VSIFWriteL( hHFA->poDictionary->osDictionaryText.c_str(), + strlen(hHFA->poDictionary->osDictionaryText.c_str()) + 1, + 1, hHFA->fp ); + hHFA->poDictionary->bDictionaryTextDirty = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* do we need to update the Ehfa_File pointer to the root node? */ +/* -------------------------------------------------------------------- */ + if( hHFA->nRootPos != hHFA->poRoot->GetFilePos() + || nNewDictionaryPos != hHFA->nDictionaryPos ) + { + GUInt32 nOffset; + GUInt32 nHeaderPos; + + VSIFSeekL( hHFA->fp, 16, SEEK_SET ); + VSIFReadL( &nHeaderPos, sizeof(GInt32), 1, hHFA->fp ); + HFAStandard( 4, &nHeaderPos ); + + nOffset = hHFA->nRootPos = hHFA->poRoot->GetFilePos(); + HFAStandard( 4, &nOffset ); + VSIFSeekL( hHFA->fp, nHeaderPos+8, SEEK_SET ); + VSIFWriteL( &nOffset, 4, 1, hHFA->fp ); + + nOffset = hHFA->nDictionaryPos = nNewDictionaryPos; + HFAStandard( 4, &nOffset ); + VSIFSeekL( hHFA->fp, nHeaderPos+14, SEEK_SET ); + VSIFWriteL( &nOffset, 4, 1, hHFA->fp ); + } + + return CE_None; +} + +/************************************************************************/ +/* HFACreateLayer() */ +/* */ +/* Create a layer object, and corresponding RasterDMS. */ +/* Suitable for use with primary layers, and overviews. */ +/************************************************************************/ + +int +HFACreateLayer( HFAHandle psInfo, HFAEntry *poParent, + const char *pszLayerName, + int bOverview, int nBlockSize, + int bCreateCompressed, int bCreateLargeRaster, + int bDependentLayer, + int nXSize, int nYSize, int nDataType, + CPL_UNUSED char **papszOptions, + + // these are only related to external (large) files + GIntBig nStackValidFlagsOffset, + GIntBig nStackDataOffset, + int nStackCount, int nStackIndex ) + +{ + + HFAEntry *poEimg_Layer; + const char *pszLayerType; + + if( bOverview ) + pszLayerType = "Eimg_Layer_SubSample"; + else + pszLayerType = "Eimg_Layer"; + + if (nBlockSize <= 0) + { + CPLError(CE_Failure, CPLE_IllegalArg, "HFACreateLayer : nBlockXSize < 0"); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Work out some details about the tiling scheme. */ +/* -------------------------------------------------------------------- */ + int nBlocksPerRow, nBlocksPerColumn, nBlocks, nBytesPerBlock; + + nBlocksPerRow = (nXSize + nBlockSize - 1) / nBlockSize; + nBlocksPerColumn = (nYSize + nBlockSize - 1) / nBlockSize; + nBlocks = nBlocksPerRow * nBlocksPerColumn; + nBytesPerBlock = (nBlockSize * nBlockSize + * HFAGetDataTypeBits(nDataType) + 7) / 8; + +/* -------------------------------------------------------------------- */ +/* Create the Eimg_Layer for the band. */ +/* -------------------------------------------------------------------- */ + poEimg_Layer = + new HFAEntry( psInfo, pszLayerName, pszLayerType, poParent ); + + poEimg_Layer->SetIntField( "width", nXSize ); + poEimg_Layer->SetIntField( "height", nYSize ); + poEimg_Layer->SetStringField( "layerType", "athematic" ); + poEimg_Layer->SetIntField( "pixelType", nDataType ); + poEimg_Layer->SetIntField( "blockWidth", nBlockSize ); + poEimg_Layer->SetIntField( "blockHeight", nBlockSize ); + +/* -------------------------------------------------------------------- */ +/* Create the RasterDMS (block list). This is a complex type */ +/* with pointers, and variable size. We set the superstructure */ +/* ourselves rather than trying to have the HFA type management */ +/* system do it for us (since this would be hard to implement). */ +/* -------------------------------------------------------------------- */ + if ( !bCreateLargeRaster && !bDependentLayer ) + { + int nDmsSize; + HFAEntry *poEdms_State; + GByte *pabyData; + + poEdms_State = + new HFAEntry( psInfo, "RasterDMS", "Edms_State", poEimg_Layer ); + + nDmsSize = 14 * nBlocks + 38; + pabyData = poEdms_State->MakeData( nDmsSize ); + + /* set some simple values */ + poEdms_State->SetIntField( "numvirtualblocks", nBlocks ); + poEdms_State->SetIntField( "numobjectsperblock", + nBlockSize*nBlockSize ); + poEdms_State->SetIntField( "nextobjectnum", + nBlockSize*nBlockSize*nBlocks ); + + /* Is file compressed or not? */ + if( bCreateCompressed ) + { + poEdms_State->SetStringField( "compressionType", "RLC compression" ); + } + else + { + poEdms_State->SetStringField( "compressionType", "no compression" ); + } + + /* we need to hardcode file offset into the data, so locate it now */ + poEdms_State->SetPosition(); + + /* Set block info headers */ + GUInt32 nValue; + + /* blockinfo count */ + nValue = nBlocks; + HFAStandard( 4, &nValue ); + memcpy( pabyData + 14, &nValue, 4 ); + + /* blockinfo position */ + nValue = poEdms_State->GetDataPos() + 22; + HFAStandard( 4, &nValue ); + memcpy( pabyData + 18, &nValue, 4 ); + + /* Set each blockinfo */ + for( int iBlock = 0; iBlock < nBlocks; iBlock++ ) + { + GInt16 nValue16; + int nOffset = 22 + 14 * iBlock; + + /* fileCode */ + nValue16 = 0; + HFAStandard( 2, &nValue16 ); + memcpy( pabyData + nOffset, &nValue16, 2 ); + + /* offset */ + if( bCreateCompressed ) + { + /* flag it with zero offset - will allocate space when we compress it */ + nValue = 0; + } + else + { + nValue = HFAAllocateSpace( psInfo, nBytesPerBlock ); + } + HFAStandard( 4, &nValue ); + memcpy( pabyData + nOffset + 2, &nValue, 4 ); + + /* size */ + if( bCreateCompressed ) + { + /* flag it with zero size - don't know until we compress it */ + nValue = 0; + } + else + { + nValue = nBytesPerBlock; + } + HFAStandard( 4, &nValue ); + memcpy( pabyData + nOffset + 6, &nValue, 4 ); + + /* logValid (false) */ + nValue16 = 0; + HFAStandard( 2, &nValue16 ); + memcpy( pabyData + nOffset + 10, &nValue16, 2 ); + + /* compressionType */ + if( bCreateCompressed ) + nValue16 = 1; + else + nValue16 = 0; + + HFAStandard( 2, &nValue16 ); + memcpy( pabyData + nOffset + 12, &nValue16, 2 ); + } + + } +/* -------------------------------------------------------------------- */ +/* Create ExternalRasterDMS object. */ +/* -------------------------------------------------------------------- */ + else if( bCreateLargeRaster ) + { + HFAEntry *poEdms_State; + + poEdms_State = + new HFAEntry( psInfo, "ExternalRasterDMS", + "ImgExternalRaster", poEimg_Layer ); + poEdms_State->MakeData( 8 + strlen(psInfo->pszIGEFilename) + 1 + 6 * 4 ); + + poEdms_State->SetStringField( "fileName.string", + psInfo->pszIGEFilename ); + + poEdms_State->SetIntField( "layerStackValidFlagsOffset[0]", + (int) (nStackValidFlagsOffset & 0xFFFFFFFF)); + poEdms_State->SetIntField( "layerStackValidFlagsOffset[1]", + (int) (nStackValidFlagsOffset >> 32) ); + + poEdms_State->SetIntField( "layerStackDataOffset[0]", + (int) (nStackDataOffset & 0xFFFFFFFF) ); + poEdms_State->SetIntField( "layerStackDataOffset[1]", + (int) (nStackDataOffset >> 32 ) ); + poEdms_State->SetIntField( "layerStackCount", nStackCount ); + poEdms_State->SetIntField( "layerStackIndex", nStackIndex ); + } + +/* -------------------------------------------------------------------- */ +/* Dependent... */ +/* -------------------------------------------------------------------- */ + else if( bDependentLayer ) + { + HFAEntry *poDepLayerName; + + poDepLayerName = + new HFAEntry( psInfo, "DependentLayerName", + "Eimg_DependentLayerName", poEimg_Layer ); + poDepLayerName->MakeData( 8 + strlen(pszLayerName) + 2 ); + + poDepLayerName->SetStringField( "ImageLayerName.string", + pszLayerName ); + } + +/* -------------------------------------------------------------------- */ +/* Create the Ehfa_Layer. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poEhfa_Layer; + GUInt32 nLDict; + char szLDict[128], chBandType; + + if( nDataType == EPT_u1 ) + chBandType = '1'; + else if( nDataType == EPT_u2 ) + chBandType = '2'; + else if( nDataType == EPT_u4 ) + chBandType = '4'; + else if( nDataType == EPT_u8 ) + chBandType = 'c'; + else if( nDataType == EPT_s8 ) + chBandType = 'C'; + else if( nDataType == EPT_u16 ) + chBandType = 's'; + else if( nDataType == EPT_s16 ) + chBandType = 'S'; + else if( nDataType == EPT_u32 ) + // for some reason erdas imagine expects an L for unsinged 32 bit ints + // otherwise it gives strange "out of memory errors" + chBandType = 'L'; + else if( nDataType == EPT_s32 ) + chBandType = 'L'; + else if( nDataType == EPT_f32 ) + chBandType = 'f'; + else if( nDataType == EPT_f64 ) + chBandType = 'd'; + else if( nDataType == EPT_c64 ) + chBandType = 'm'; + else if( nDataType == EPT_c128 ) + chBandType = 'M'; + else + { + CPLAssert( FALSE ); + chBandType = 'c'; + } + + // the first value in the entry below gives the number of pixels within a block + sprintf( szLDict, "{%d:%cdata,}RasterDMS,.", nBlockSize*nBlockSize, chBandType ); + + poEhfa_Layer = new HFAEntry( psInfo, "Ehfa_Layer", "Ehfa_Layer", + poEimg_Layer ); + poEhfa_Layer->MakeData(); + poEhfa_Layer->SetPosition(); + nLDict = HFAAllocateSpace( psInfo, strlen(szLDict) + 1 ); + + poEhfa_Layer->SetStringField( "type", "raster" ); + poEhfa_Layer->SetIntField( "dictionaryPtr", nLDict ); + + VSIFSeekL( psInfo->fp, nLDict, SEEK_SET ); + VSIFWriteL( (void *) szLDict, strlen(szLDict) + 1, 1, psInfo->fp ); + + return TRUE; +} + + +/************************************************************************/ +/* HFACreate() */ +/************************************************************************/ + +HFAHandle HFACreate( const char * pszFilename, + int nXSize, int nYSize, int nBands, + int nDataType, char ** papszOptions ) + +{ + HFAHandle psInfo; + int nBlockSize = 64; + const char * pszValue = CSLFetchNameValue( papszOptions, "BLOCKSIZE" ); + + if ( pszValue != NULL ) + { + nBlockSize = atoi( pszValue ); + // check for sane values + if ( (( nBlockSize < 32 ) || (nBlockSize > 2048)) + && !CSLTestBoolean(CPLGetConfigOption("FORCE_BLOCKSIZE", "NO")) ) + { + nBlockSize = 64; + } + } + int bCreateLargeRaster = CSLFetchBoolean(papszOptions,"USE_SPILL", + FALSE); + int bCreateCompressed = + CSLFetchBoolean(papszOptions,"COMPRESS", FALSE) + || CSLFetchBoolean(papszOptions,"COMPRESSED", FALSE); + int bCreateAux = CSLFetchBoolean(papszOptions,"AUX", FALSE); + + char *pszFullFilename = NULL, *pszRawFilename = NULL; + +/* -------------------------------------------------------------------- */ +/* Create the low level structure. */ +/* -------------------------------------------------------------------- */ + psInfo = HFACreateLL( pszFilename ); + if( psInfo == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the DependentFile node if requested. */ +/* -------------------------------------------------------------------- */ + const char *pszDependentFile = + CSLFetchNameValue( papszOptions, "DEPENDENT_FILE" ); + + if( pszDependentFile != NULL ) + { + HFAEntry *poDF = new HFAEntry( psInfo, "DependentFile", + "Eimg_DependentFile", psInfo->poRoot ); + + poDF->MakeData( strlen(pszDependentFile) + 50 ); + poDF->SetPosition(); + poDF->SetStringField( "dependent.string", pszDependentFile ); + } + +/* -------------------------------------------------------------------- */ +/* Work out some details about the tiling scheme. */ +/* -------------------------------------------------------------------- */ + int nBlocksPerRow, nBlocksPerColumn, nBlocks, nBytesPerBlock; + + nBlocksPerRow = (nXSize + nBlockSize - 1) / nBlockSize; + nBlocksPerColumn = (nYSize + nBlockSize - 1) / nBlockSize; + nBlocks = nBlocksPerRow * nBlocksPerColumn; + nBytesPerBlock = (nBlockSize * nBlockSize + * HFAGetDataTypeBits(nDataType) + 7) / 8; + + CPLDebug( "HFACreate", "Blocks per row %d, blocks per column %d, " + "total number of blocks %d, bytes per block %d.", + nBlocksPerRow, nBlocksPerColumn, nBlocks, nBytesPerBlock ); + +/* -------------------------------------------------------------------- */ +/* Check whether we should create external large file with */ +/* image. We create a spill file if the amount of imagery is */ +/* close to 2GB. We don't check the amount of auxiliary */ +/* information, so in theory if there were an awful lot of */ +/* non-imagery data our approximate size could be smaller than */ +/* the file will actually we be. We leave room for 10MB of */ +/* auxiliary data. */ +/* We can also force spill file creation using option */ +/* SPILL_FILE=YES. */ +/* -------------------------------------------------------------------- */ + double dfApproxSize = (double)nBytesPerBlock * (double)nBlocks * + (double)nBands + 10000000.0; + + if( dfApproxSize > 2147483648.0 && !bCreateAux ) + bCreateLargeRaster = TRUE; + + // erdas imagine creates this entry even if an external spill file is used + if( !bCreateAux ) + { + HFAEntry *poImgFormat; + poImgFormat = new HFAEntry( psInfo, "IMGFormatInfo", + "ImgFormatInfo831", psInfo->poRoot ); + poImgFormat->MakeData(); + if ( bCreateLargeRaster ) + { + poImgFormat->SetIntField( "spaceUsedForRasterData", 0 ); + bCreateCompressed = FALSE; // Can't be compressed if we are creating a spillfile + } + else + { + poImgFormat->SetIntField( "spaceUsedForRasterData", + nBytesPerBlock*nBlocks*nBands ); + } + } + +/* -------------------------------------------------------------------- */ +/* Create external file and write its header. */ +/* -------------------------------------------------------------------- */ + GIntBig nValidFlagsOffset = 0, nDataOffset = 0; + + if( bCreateLargeRaster ) + { + if( !HFACreateSpillStack( psInfo, nXSize, nYSize, nBands, + nBlockSize, nDataType, + &nValidFlagsOffset, &nDataOffset ) ) + { + CPLFree( pszRawFilename ); + CPLFree( pszFullFilename ); + return NULL; + } + } + +/* ==================================================================== */ +/* Create each band (layer) */ +/* ==================================================================== */ + int iBand; + + for( iBand = 0; iBand < nBands; iBand++ ) + { + char szName[128]; + + sprintf( szName, "Layer_%d", iBand + 1 ); + + if( !HFACreateLayer( psInfo, psInfo->poRoot, szName, FALSE, nBlockSize, + bCreateCompressed, bCreateLargeRaster, bCreateAux, + nXSize, nYSize, nDataType, papszOptions, + nValidFlagsOffset, nDataOffset, + nBands, iBand ) ) + { + HFAClose( psInfo ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize the band information. */ +/* -------------------------------------------------------------------- */ + HFAParseBandInfo( psInfo ); + + return psInfo; +} + +/************************************************************************/ +/* HFACreateOverview() */ +/* */ +/* Create an overview layer object for a band. */ +/************************************************************************/ + +int HFACreateOverview( HFAHandle hHFA, int nBand, int nOverviewLevel, + const char *pszResampling ) + +{ + if( nBand < 1 || nBand > hHFA->nBands ) + return -1; + else + { + HFABand *poBand = hHFA->papoBand[nBand-1]; + return poBand->CreateOverview( nOverviewLevel, pszResampling ); + } +} + +/************************************************************************/ +/* HFAGetMetadata() */ +/* */ +/* Read metadata structured in a table called GDAL_MetaData. */ +/************************************************************************/ + +char ** HFAGetMetadata( HFAHandle hHFA, int nBand ) + +{ + HFAEntry *poTable; + + if( nBand > 0 && nBand <= hHFA->nBands ) + poTable = hHFA->papoBand[nBand - 1]->poNode->GetChild(); + else if( nBand == 0 ) + poTable = hHFA->poRoot->GetChild(); + else + return NULL; + + for( ; poTable != NULL && !EQUAL(poTable->GetName(),"GDAL_MetaData"); + poTable = poTable->GetNext() ) {} + + if( poTable == NULL || !EQUAL(poTable->GetType(),"Edsc_Table") ) + return NULL; + + if( poTable->GetIntField( "numRows" ) != 1 ) + { + CPLDebug( "HFADataset", "GDAL_MetaData.numRows = %d, expected 1!", + poTable->GetIntField( "numRows" ) ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Loop over each column. Each column will be one metadata */ +/* entry, with the title being the key, and the row value being */ +/* the value. There is only ever one row in GDAL_MetaData */ +/* tables. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poColumn; + char **papszMD = NULL; + + for( poColumn = poTable->GetChild(); + poColumn != NULL; + poColumn = poColumn->GetNext() ) + { + const char *pszValue; + int columnDataPtr; + + // Skip the #Bin_Function# entry. + if( EQUALN(poColumn->GetName(),"#",1) ) + continue; + + pszValue = poColumn->GetStringField( "dataType" ); + if( pszValue == NULL || !EQUAL(pszValue,"string") ) + continue; + + columnDataPtr = poColumn->GetIntField( "columnDataPtr" ); + if( columnDataPtr == 0 ) + continue; + +/* -------------------------------------------------------------------- */ +/* read up to nMaxNumChars bytes from the indicated location. */ +/* allocate required space temporarily */ +/* nMaxNumChars should have been set by GDAL orginally so we should*/ +/* trust it, but who knows... */ +/* -------------------------------------------------------------------- */ + int nMaxNumChars = poColumn->GetIntField( "maxNumChars" ); + + if( nMaxNumChars == 0 ) + { + papszMD = CSLSetNameValue( papszMD, poColumn->GetName(), "" ); + } + else + { + char *pszMDValue = (char*) VSIMalloc(nMaxNumChars); + if (pszMDValue == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "HFAGetMetadata : Out of memory while allocating %d bytes", nMaxNumChars); + continue; + } + + if( VSIFSeekL( hHFA->fp, columnDataPtr, SEEK_SET ) != 0 ) + continue; + + int nMDBytes = VSIFReadL( pszMDValue, 1, nMaxNumChars, hHFA->fp ); + if( nMDBytes == 0 ) + { + CPLFree( pszMDValue ); + continue; + } + + pszMDValue[nMaxNumChars-1] = '\0'; + + papszMD = CSLSetNameValue( papszMD, poColumn->GetName(), + pszMDValue ); + CPLFree( pszMDValue ); + } + } + + return papszMD; +} + +/************************************************************************/ +/* HFASetGDALMetadata() */ +/* */ +/* This function is used to set metadata in a table called */ +/* GDAL_MetaData. It is called by HFASetMetadata() for all */ +/* metadata items that aren't some specific supported */ +/* information (like histogram or stats info). */ +/************************************************************************/ + +static CPLErr +HFASetGDALMetadata( HFAHandle hHFA, int nBand, char **papszMD ) + +{ + if( papszMD == NULL ) + return CE_None; + + HFAEntry *poNode; + + if( nBand > 0 && nBand <= hHFA->nBands ) + poNode = hHFA->papoBand[nBand - 1]->poNode; + else if( nBand == 0 ) + poNode = hHFA->poRoot; + else + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Create the Descriptor table. */ +/* Check we have no table with this name already */ +/* -------------------------------------------------------------------- */ + HFAEntry *poEdsc_Table = poNode->GetNamedChild( "GDAL_MetaData" ); + + if( poEdsc_Table == NULL || !EQUAL(poEdsc_Table->GetType(),"Edsc_Table") ) + poEdsc_Table = new HFAEntry( hHFA, "GDAL_MetaData", "Edsc_Table", + poNode ); + + poEdsc_Table->SetIntField( "numrows", 1 ); + +/* -------------------------------------------------------------------- */ +/* Create the Binning function node. I am not sure that we */ +/* really need this though. */ +/* Check it doesn't exist already */ +/* -------------------------------------------------------------------- */ + HFAEntry *poEdsc_BinFunction = + poEdsc_Table->GetNamedChild( "#Bin_Function#" ); + + if( poEdsc_BinFunction == NULL + || !EQUAL(poEdsc_BinFunction->GetType(),"Edsc_BinFunction") ) + poEdsc_BinFunction = new HFAEntry( hHFA, "#Bin_Function#", + "Edsc_BinFunction", poEdsc_Table ); + + // Because of the BaseData we have to hardcode the size. + poEdsc_BinFunction->MakeData( 30 ); + + poEdsc_BinFunction->SetIntField( "numBins", 1 ); + poEdsc_BinFunction->SetStringField( "binFunction", "direct" ); + poEdsc_BinFunction->SetDoubleField( "minLimit", 0.0 ); + poEdsc_BinFunction->SetDoubleField( "maxLimit", 0.0 ); + +/* -------------------------------------------------------------------- */ +/* Process each metadata item as a separate column. */ +/* -------------------------------------------------------------------- */ + for( int iColumn = 0; papszMD[iColumn] != NULL; iColumn++ ) + { + HFAEntry *poEdsc_Column; + char *pszKey = NULL; + const char *pszValue; + + pszValue = CPLParseNameValue( papszMD[iColumn], &pszKey ); + if( pszValue == NULL ) + continue; + +/* -------------------------------------------------------------------- */ +/* Create the Edsc_Column. */ +/* Check it doesn't exist already */ +/* -------------------------------------------------------------------- */ + poEdsc_Column = poEdsc_Table->GetNamedChild(pszKey); + + if( poEdsc_Column == NULL + || !EQUAL(poEdsc_Column->GetType(),"Edsc_Column") ) + poEdsc_Column = new HFAEntry( hHFA, pszKey, "Edsc_Column", + poEdsc_Table ); + + poEdsc_Column->SetIntField( "numRows", 1 ); + poEdsc_Column->SetStringField( "dataType", "string" ); + poEdsc_Column->SetIntField( "maxNumChars", strlen(pszValue)+1 ); + +/* -------------------------------------------------------------------- */ +/* Write the data out. */ +/* -------------------------------------------------------------------- */ + int nOffset = HFAAllocateSpace( hHFA, strlen(pszValue)+1); + + poEdsc_Column->SetIntField( "columnDataPtr", nOffset ); + + VSIFSeekL( hHFA->fp, nOffset, SEEK_SET ); + VSIFWriteL( (void *) pszValue, 1, strlen(pszValue)+1, hHFA->fp ); + + CPLFree( pszKey ); + } + + return CE_Failure; +} + +/************************************************************************/ +/* HFASetMetadata() */ +/************************************************************************/ + +CPLErr HFASetMetadata( HFAHandle hHFA, int nBand, char **papszMD ) + +{ + char **papszGDALMD = NULL; + + if( CSLCount(papszMD) == 0 ) + return CE_None; + + HFAEntry *poNode; + + if( nBand > 0 && nBand <= hHFA->nBands ) + poNode = hHFA->papoBand[nBand - 1]->poNode; + else if( nBand == 0 ) + poNode = hHFA->poRoot; + else + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Check if the Metadata is an "known" entity which should be */ +/* stored in a better place. */ +/* -------------------------------------------------------------------- */ + char * pszBinValues = NULL; + int bCreatedHistogramParameters = FALSE; + int bCreatedStatistics = FALSE; + const char ** pszAuxMetaData = GetHFAAuxMetaDataList(); + // check each metadata item + for( int iColumn = 0; papszMD[iColumn] != NULL; iColumn++ ) + { + char *pszKey = NULL; + const char *pszValue; + + pszValue = CPLParseNameValue( papszMD[iColumn], &pszKey ); + if( pszValue == NULL ) + continue; + + // know look if its known + int i; + for( i = 0; pszAuxMetaData[i] != NULL; i += 4 ) + { + if ( EQUALN( pszAuxMetaData[i + 2], pszKey, strlen(pszKey) ) ) + break; + } + if ( pszAuxMetaData[i] != NULL ) + { + // found one, get the right entry + HFAEntry *poEntry; + + if( strlen(pszAuxMetaData[i]) > 0 ) + poEntry = poNode->GetNamedChild( pszAuxMetaData[i] ); + else + poEntry = poNode; + + if( poEntry == NULL && strlen(pszAuxMetaData[i+3]) > 0 ) + { + // child does not yet exist --> create it + poEntry = new HFAEntry( hHFA, pszAuxMetaData[i], pszAuxMetaData[i+3], + poNode ); + + if ( EQUALN( "Statistics", pszAuxMetaData[i], 10 ) ) + bCreatedStatistics = TRUE; + + if( EQUALN( "HistogramParameters", pszAuxMetaData[i], 19 ) ) + { + // this is a bit nasty I need to set the string field for the object + // first because the SetStringField sets the count for the object + // BinFunction to the length of the string + poEntry->MakeData( 70 ); + poEntry->SetStringField( "BinFunction.binFunctionType", "direct" ); + + bCreatedHistogramParameters = TRUE; + } + } + if ( poEntry == NULL ) + { + CPLFree( pszKey ); + continue; + } + + const char *pszFieldName = pszAuxMetaData[i+1] + 1; + switch( pszAuxMetaData[i+1][0] ) + { + case 'd': + { + double dfValue = CPLAtof( pszValue ); + poEntry->SetDoubleField( pszFieldName, dfValue ); + } + break; + case 'i': + case 'l': + { + int nValue = atoi( pszValue ); + poEntry->SetIntField( pszFieldName, nValue ); + } + break; + case 's': + case 'e': + { + poEntry->SetStringField( pszFieldName, pszValue ); + } + break; + default: + CPLAssert( FALSE ); + } + } + else if ( EQUALN( "STATISTICS_HISTOBINVALUES", pszKey, strlen(pszKey) ) ) + { + pszBinValues = CPLStrdup( pszValue ); + } + else + papszGDALMD = CSLAddString( papszGDALMD, papszMD[iColumn] ); + + CPLFree( pszKey ); + } + +/* -------------------------------------------------------------------- */ +/* Special case to write out the histogram. */ +/* -------------------------------------------------------------------- */ + if ( pszBinValues != NULL ) + { + HFAEntry * poEntry = poNode->GetNamedChild( "HistogramParameters" ); + if ( poEntry != NULL && bCreatedHistogramParameters ) + { + // if this node exists we have added Histogram data -- complete with some defaults + poEntry->SetIntField( "SkipFactorX", 1 ); + poEntry->SetIntField( "SkipFactorY", 1 ); + + int nNumBins = poEntry->GetIntField( "BinFunction.numBins" ); + double dMinLimit = poEntry->GetDoubleField( "BinFunction.minLimit" ); + double dMaxLimit = poEntry->GetDoubleField( "BinFunction.maxLimit" ); + + // fill the descriptor table - check it isn't there already + poEntry = poNode->GetNamedChild( "Descriptor_Table" ); + if( poEntry == NULL || !EQUAL(poEntry->GetType(),"Edsc_Table") ) + poEntry = new HFAEntry( hHFA, "Descriptor_Table", "Edsc_Table", poNode ); + + poEntry->SetIntField( "numRows", nNumBins ); + + // bin function + HFAEntry * poBinFunc = poEntry->GetNamedChild( "#Bin_Function#" ); + if( poBinFunc == NULL || !EQUAL(poBinFunc->GetType(),"Edsc_BinFunction") ) + poBinFunc = new HFAEntry( hHFA, "#Bin_Function#", "Edsc_BinFunction", poEntry ); + + poBinFunc->MakeData( 30 ); + poBinFunc->SetIntField( "numBins", nNumBins ); + poBinFunc->SetDoubleField( "minLimit", dMinLimit ); + poBinFunc->SetDoubleField( "maxLimit", dMaxLimit ); + // direct for thematic layers, linear otherwise + if ( EQUALN ( poNode->GetStringField("layerType"), "thematic", 8) ) + poBinFunc->SetStringField( "binFunctionType", "direct" ); + else + poBinFunc->SetStringField( "binFunctionType", "linear" ); + + // we need a child named histogram + HFAEntry * poHisto = poEntry->GetNamedChild( "Histogram" ); + if( poHisto == NULL || !EQUAL(poHisto->GetType(),"Edsc_Column") ) + poHisto = new HFAEntry( hHFA, "Histogram", "Edsc_Column", poEntry ); + + poHisto->SetIntField( "numRows", nNumBins ); + // allocate space for the bin values + GUInt32 nOffset = HFAAllocateSpace( hHFA, nNumBins*8 ); + poHisto->SetIntField( "columnDataPtr", nOffset ); + poHisto->SetStringField( "dataType", "real" ); + poHisto->SetIntField( "maxNumChars", 0 ); + // write out histogram data + char * pszWork = pszBinValues; + for ( int nBin = 0; nBin < nNumBins; ++nBin ) + { + char * pszEnd = strchr( pszWork, '|' ); + if ( pszEnd != NULL ) + { + *pszEnd = 0; + VSIFSeekL( hHFA->fp, nOffset + 8*nBin, SEEK_SET ); + double nValue = CPLAtof( pszWork ); + HFAStandard( 8, &nValue ); + + VSIFWriteL( (void *)&nValue, 1, 8, hHFA->fp ); + pszWork = pszEnd + 1; + } + } + } + else if ( poEntry != NULL ) + { + // In this case, there are HistogramParameters present, but we did not + // create them. However, we might be modifying them, in the case where + // the data has changed and the histogram counts need to be updated. It could + // be worse than that, but that is all we are going to cope with for now. + // We are assuming that we did not change any of the other stuff, like + // skip factors and so forth. The main need for this case is for programs + // (such as Imagine itself) which will happily modify the pixel values + // without re-calculating the histogram counts. + int nNumBins = poEntry->GetIntField( "BinFunction.numBins" ); + HFAEntry *poEntryDescrTbl = poNode->GetNamedChild( "Descriptor_Table" ); + HFAEntry *poHisto = NULL; + if ( poEntryDescrTbl != NULL) { + poHisto = poEntryDescrTbl->GetNamedChild( "Histogram" ); + } + if ( poHisto != NULL ) { + int nOffset = poHisto->GetIntField( "columnDataPtr" ); + // write out histogram data + char * pszWork = pszBinValues; + + // Check whether histogram counts were written as int or double + bool bCountIsInt = TRUE; + const char *pszDataType = poHisto->GetStringField("dataType"); + if ( EQUALN(pszDataType, "real", strlen(pszDataType)) ) + { + bCountIsInt = FALSE; + } + for ( int nBin = 0; nBin < nNumBins; ++nBin ) + { + char * pszEnd = strchr( pszWork, '|' ); + if ( pszEnd != NULL ) + { + *pszEnd = 0; + if ( bCountIsInt ) { + // Histogram counts were written as ints, so re-write them the same way + VSIFSeekL( hHFA->fp, nOffset + 4*nBin, SEEK_SET ); + int nValue = atoi( pszWork ); + HFAStandard( 4, &nValue ); + VSIFWriteL( (void *)&nValue, 1, 4, hHFA->fp ); + } else { + // Histogram were written as doubles, as is now the default behaviour + VSIFSeekL( hHFA->fp, nOffset + 8*nBin, SEEK_SET ); + double nValue = CPLAtof( pszWork ); + HFAStandard( 8, &nValue ); + VSIFWriteL( (void *)&nValue, 1, 8, hHFA->fp ); + } + pszWork = pszEnd + 1; + } + } + } + } + CPLFree( pszBinValues ); + } + +/* -------------------------------------------------------------------- */ +/* If we created a statistics node then try to create a */ +/* StatisticsParameters node too. */ +/* -------------------------------------------------------------------- */ + if( bCreatedStatistics ) + { + HFAEntry *poEntry = + new HFAEntry( hHFA, "StatisticsParameters", + "Eimg_StatisticsParameters830", poNode ); + + poEntry->MakeData( 70 ); + //poEntry->SetStringField( "BinFunction.binFunctionType", "linear" ); + + poEntry->SetIntField( "SkipFactorX", 1 ); + poEntry->SetIntField( "SkipFactorY", 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Write out metadata items without a special place. */ +/* -------------------------------------------------------------------- */ + if( CSLCount( papszGDALMD) != 0 ) + { + CPLErr eErr = HFASetGDALMetadata( hHFA, nBand, papszGDALMD ); + + CSLDestroy( papszGDALMD ); + return eErr; + } + else + return CE_Failure; +} + +/************************************************************************/ +/* HFAGetIGEFilename() */ +/* */ +/* Returns the .ige filename if one is associated with this */ +/* object. For files not newly created we need to scan the */ +/* bands for spill files. Presumably there will only be one. */ +/* */ +/* NOTE: Returns full path, not just the filename portion. */ +/************************************************************************/ + +const char *HFAGetIGEFilename( HFAHandle hHFA ) + +{ + if( hHFA->pszIGEFilename == NULL ) + { + HFAEntry *poDMS = NULL; + std::vector apoDMSList = + hHFA->poRoot->FindChildren( NULL, "ImgExternalRaster" ); + + if( apoDMSList.size() > 0 ) + poDMS = apoDMSList[0]; + +/* -------------------------------------------------------------------- */ +/* Get the IGE filename from if we have an ExternalRasterDMS */ +/* -------------------------------------------------------------------- */ + if( poDMS ) + { + const char *pszRawFilename = + poDMS->GetStringField( "fileName.string" ); + + if( pszRawFilename != NULL ) + { + VSIStatBufL sStatBuf; + CPLString osFullFilename = + CPLFormFilename( hHFA->pszPath, pszRawFilename, NULL ); + + if( VSIStatL( osFullFilename, &sStatBuf ) != 0 ) + { + CPLString osExtension = CPLGetExtension(pszRawFilename); + CPLString osBasename = CPLGetBasename(hHFA->pszFilename); + CPLString osFullFilename = + CPLFormFilename( hHFA->pszPath, osBasename, + osExtension ); + + if( VSIStatL( osFullFilename, &sStatBuf ) == 0 ) + hHFA->pszIGEFilename = + CPLStrdup( + CPLFormFilename( NULL, osBasename, + osExtension ) ); + else + hHFA->pszIGEFilename = CPLStrdup( pszRawFilename ); + } + else + hHFA->pszIGEFilename = CPLStrdup( pszRawFilename ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Return the full filename. */ +/* -------------------------------------------------------------------- */ + if( hHFA->pszIGEFilename ) + return CPLFormFilename( hHFA->pszPath, hHFA->pszIGEFilename, NULL ); + else + return NULL; +} + +/************************************************************************/ +/* HFACreateSpillStack() */ +/* */ +/* Create a new stack of raster layers in the spill (.ige) */ +/* file. Create the spill file if it didn't exist before. */ +/************************************************************************/ + +int HFACreateSpillStack( HFAInfo_t *psInfo, int nXSize, int nYSize, + int nLayers, int nBlockSize, int nDataType, + GIntBig *pnValidFlagsOffset, + GIntBig *pnDataOffset ) + +{ +/* -------------------------------------------------------------------- */ +/* Form .ige filename. */ +/* -------------------------------------------------------------------- */ + char *pszFullFilename; + + if (nBlockSize <= 0) + { + CPLError(CE_Failure, CPLE_IllegalArg, "HFACreateSpillStack : nBlockXSize < 0"); + return FALSE; + } + + if( psInfo->pszIGEFilename == NULL ) + { + if( EQUAL(CPLGetExtension(psInfo->pszFilename),"rrd") ) + psInfo->pszIGEFilename = + CPLStrdup( CPLResetExtension( psInfo->pszFilename, "rde" ) ); + else if( EQUAL(CPLGetExtension(psInfo->pszFilename),"aux") ) + psInfo->pszIGEFilename = + CPLStrdup( CPLResetExtension( psInfo->pszFilename, "axe" ) ); + else + psInfo->pszIGEFilename = + CPLStrdup( CPLResetExtension( psInfo->pszFilename, "ige" ) ); + } + + pszFullFilename = + CPLStrdup( CPLFormFilename( psInfo->pszPath, psInfo->pszIGEFilename, NULL ) ); + +/* -------------------------------------------------------------------- */ +/* Try and open it. If we fail, create it and write the magic */ +/* header. */ +/* -------------------------------------------------------------------- */ + static const char *pszMagick = "ERDAS_IMG_EXTERNAL_RASTER"; + VSILFILE *fpVSIL; + + fpVSIL = VSIFOpenL( pszFullFilename, "r+b" ); + if( fpVSIL == NULL ) + { + fpVSIL = VSIFOpenL( pszFullFilename, "w+" ); + if( fpVSIL == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create spill file %s.\n%s", + psInfo->pszIGEFilename, VSIStrerror( errno ) ); + return FALSE; + } + + VSIFWriteL( (void *) pszMagick, 1, strlen(pszMagick)+1, fpVSIL ); + } + + CPLFree( pszFullFilename ); + +/* -------------------------------------------------------------------- */ +/* Work out some details about the tiling scheme. */ +/* -------------------------------------------------------------------- */ + int nBlocksPerRow, nBlocksPerColumn, /* nBlocks, */ nBytesPerBlock; + int nBytesPerRow, nBlockMapSize /* , iFlagsSize */; + + nBlocksPerRow = (nXSize + nBlockSize - 1) / nBlockSize; + nBlocksPerColumn = (nYSize + nBlockSize - 1) / nBlockSize; + /* nBlocks = nBlocksPerRow * nBlocksPerColumn; */ + nBytesPerBlock = (nBlockSize * nBlockSize + * HFAGetDataTypeBits(nDataType) + 7) / 8; + + nBytesPerRow = ( nBlocksPerRow + 7 ) / 8; + nBlockMapSize = nBytesPerRow * nBlocksPerColumn; + /* iFlagsSize = nBlockMapSize + 20; */ + +/* -------------------------------------------------------------------- */ +/* Write stack prefix information. */ +/* -------------------------------------------------------------------- */ + GByte bUnknown; + GInt32 nValue32; + + VSIFSeekL( fpVSIL, 0, SEEK_END ); + + bUnknown = 1; + VSIFWriteL( &bUnknown, 1, 1, fpVSIL ); + nValue32 = nLayers; + HFAStandard( 4, &nValue32 ); + VSIFWriteL( &nValue32, 4, 1, fpVSIL ); + nValue32 = nXSize; + HFAStandard( 4, &nValue32 ); + VSIFWriteL( &nValue32, 4, 1, fpVSIL ); + nValue32 = nYSize; + HFAStandard( 4, &nValue32 ); + VSIFWriteL( &nValue32, 4, 1, fpVSIL ); + nValue32 = nBlockSize; + HFAStandard( 4, &nValue32 ); + VSIFWriteL( &nValue32, 4, 1, fpVSIL ); + VSIFWriteL( &nValue32, 4, 1, fpVSIL ); + bUnknown = 3; + VSIFWriteL( &bUnknown, 1, 1, fpVSIL ); + bUnknown = 0; + VSIFWriteL( &bUnknown, 1, 1, fpVSIL ); + +/* -------------------------------------------------------------------- */ +/* Write out ValidFlags section(s). */ +/* -------------------------------------------------------------------- */ + unsigned char *pabyBlockMap; + int iBand; + + *pnValidFlagsOffset = VSIFTellL( fpVSIL ); + + pabyBlockMap = (unsigned char *) VSIMalloc( nBlockMapSize ); + if (pabyBlockMap == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "HFACreateSpillStack : Out of memory"); + VSIFCloseL( fpVSIL ); + return FALSE; + } + + memset( pabyBlockMap, 0xff, nBlockMapSize ); + for ( iBand = 0; iBand < nLayers; iBand++ ) + { + int i, iRemainder; + + nValue32 = 1; // Unknown + HFAStandard( 4, &nValue32 ); + VSIFWriteL( &nValue32, 4, 1, fpVSIL ); + nValue32 = 0; // Unknown + VSIFWriteL( &nValue32, 4, 1, fpVSIL ); + nValue32 = nBlocksPerColumn; + HFAStandard( 4, &nValue32 ); + VSIFWriteL( &nValue32, 4, 1, fpVSIL ); + nValue32 = nBlocksPerRow; + HFAStandard( 4, &nValue32 ); + VSIFWriteL( &nValue32, 4, 1, fpVSIL ); + nValue32 = 0x30000; // Unknown + HFAStandard( 4, &nValue32 ); + VSIFWriteL( &nValue32, 4, 1, fpVSIL ); + + iRemainder = nBlocksPerRow % 8; + CPLDebug( "HFACreate", + "Block map size %d, bytes per row %d, remainder %d.", + nBlockMapSize, nBytesPerRow, iRemainder ); + if ( iRemainder ) + { + for ( i = nBytesPerRow - 1; i < nBlockMapSize; i+=nBytesPerRow ) + pabyBlockMap[i] = (GByte) ((1<pszIGEFilename, + (double) nTileDataSize - 1 + *pnDataOffset, + VSIStrerror( errno ) ); + + VSIFCloseL( fpVSIL ); + return FALSE; + } + + VSIFCloseL( fpVSIL ); + + return TRUE; +} + +/************************************************************************/ +/* HFAReadAndValidatePoly() */ +/************************************************************************/ + +static int HFAReadAndValidatePoly( HFAEntry *poTarget, + const char *pszName, + Efga_Polynomial *psRetPoly ) + +{ + CPLString osFldName; + + memset( psRetPoly, 0, sizeof(Efga_Polynomial) ); + + osFldName.Printf( "%sorder", pszName ); + psRetPoly->order = poTarget->GetIntField(osFldName); + + if( psRetPoly->order < 1 || psRetPoly->order > 3 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Validate that things are in a "well known" form. */ +/* -------------------------------------------------------------------- */ + int numdimtransform, numdimpolynomial, termcount; + + osFldName.Printf( "%snumdimtransform", pszName ); + numdimtransform = poTarget->GetIntField(osFldName); + + osFldName.Printf( "%snumdimpolynomial", pszName ); + numdimpolynomial = poTarget->GetIntField(osFldName); + + osFldName.Printf( "%stermcount", pszName ); + termcount = poTarget->GetIntField(osFldName); + + if( numdimtransform != 2 || numdimpolynomial != 2 ) + return FALSE; + + if( (psRetPoly->order == 1 && termcount != 3) + || (psRetPoly->order == 2 && termcount != 6) + || (psRetPoly->order == 3 && termcount != 10) ) + return FALSE; + + // we don't check the exponent organization for now. Hopefully + // it is always standard. + +/* -------------------------------------------------------------------- */ +/* Get coefficients. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < termcount*2 - 2; i++ ) + { + osFldName.Printf( "%spolycoefmtx[%d]", pszName, i ); + psRetPoly->polycoefmtx[i] = poTarget->GetDoubleField(osFldName); + } + + for( i = 0; i < 2; i++ ) + { + osFldName.Printf( "%spolycoefvector[%d]", pszName, i ); + psRetPoly->polycoefvector[i] = poTarget->GetDoubleField(osFldName); + } + + return TRUE; +} + +/************************************************************************/ +/* HFAReadXFormStack() */ +/************************************************************************/ + + +int HFAReadXFormStack( HFAHandle hHFA, + Efga_Polynomial **ppasPolyListForward, + Efga_Polynomial **ppasPolyListReverse ) + +{ + if( hHFA->nBands == 0 ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Get the HFA node. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poXFormHeader; + + poXFormHeader = hHFA->papoBand[0]->poNode->GetNamedChild( "MapToPixelXForm" ); + if( poXFormHeader == NULL ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Loop over children, collecting XForms. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poXForm; + int nStepCount = 0; + *ppasPolyListForward = NULL; + *ppasPolyListReverse = NULL; + + for( poXForm = poXFormHeader->GetChild(); + poXForm != NULL; + poXForm = poXForm->GetNext() ) + { + int bSuccess = FALSE; + Efga_Polynomial sForward, sReverse; + memset( &sForward, 0, sizeof(sForward) ); + memset( &sReverse, 0, sizeof(sReverse) ); + + if( EQUAL(poXForm->GetType(),"Efga_Polynomial") ) + { + bSuccess = + HFAReadAndValidatePoly( poXForm, "", &sForward ); + + if( bSuccess ) + { + double adfGT[6], adfInvGT[6]; + + adfGT[0] = sForward.polycoefvector[0]; + adfGT[1] = sForward.polycoefmtx[0]; + adfGT[2] = sForward.polycoefmtx[2]; + adfGT[3] = sForward.polycoefvector[1]; + adfGT[4] = sForward.polycoefmtx[1]; + adfGT[5] = sForward.polycoefmtx[3]; + + bSuccess = HFAInvGeoTransform( adfGT, adfInvGT ); + + sReverse.order = sForward.order; + sReverse.polycoefvector[0] = adfInvGT[0]; + sReverse.polycoefmtx[0] = adfInvGT[1]; + sReverse.polycoefmtx[2] = adfInvGT[2]; + sReverse.polycoefvector[1] = adfInvGT[3]; + sReverse.polycoefmtx[1] = adfInvGT[4]; + sReverse.polycoefmtx[3] = adfInvGT[5]; + } + } + else if( EQUAL(poXForm->GetType(),"GM_PolyPair") ) + { + bSuccess = + HFAReadAndValidatePoly( poXForm, "forward.", &sForward ); + bSuccess = bSuccess && + HFAReadAndValidatePoly( poXForm, "reverse.", &sReverse ); + } + + if( bSuccess ) + { + nStepCount++; + *ppasPolyListForward = (Efga_Polynomial *) + CPLRealloc( *ppasPolyListForward, + sizeof(Efga_Polynomial) * nStepCount); + memcpy( *ppasPolyListForward + nStepCount - 1, + &sForward, sizeof(sForward) ); + + *ppasPolyListReverse = (Efga_Polynomial *) + CPLRealloc( *ppasPolyListReverse, + sizeof(Efga_Polynomial) * nStepCount); + memcpy( *ppasPolyListReverse + nStepCount - 1, + &sReverse, sizeof(sReverse) ); + } + } + + return nStepCount; +} + +/************************************************************************/ +/* HFAEvaluateXFormStack() */ +/************************************************************************/ + +int HFAEvaluateXFormStack( int nStepCount, int bForward, + Efga_Polynomial *pasPolyList, + double *pdfX, double *pdfY ) + +{ + int iStep; + + for( iStep = 0; iStep < nStepCount; iStep++ ) + { + double dfXOut, dfYOut; + Efga_Polynomial *psStep; + + if( bForward ) + psStep = pasPolyList + iStep; + else + psStep = pasPolyList + nStepCount - iStep - 1; + + if( psStep->order == 1 ) + { + dfXOut = psStep->polycoefvector[0] + + psStep->polycoefmtx[0] * *pdfX + + psStep->polycoefmtx[2] * *pdfY; + + dfYOut = psStep->polycoefvector[1] + + psStep->polycoefmtx[1] * *pdfX + + psStep->polycoefmtx[3] * *pdfY; + + *pdfX = dfXOut; + *pdfY = dfYOut; + } + else if( psStep->order == 2 ) + { + dfXOut = psStep->polycoefvector[0] + + psStep->polycoefmtx[0] * *pdfX + + psStep->polycoefmtx[2] * *pdfY + + psStep->polycoefmtx[4] * *pdfX * *pdfX + + psStep->polycoefmtx[6] * *pdfX * *pdfY + + psStep->polycoefmtx[8] * *pdfY * *pdfY; + dfYOut = psStep->polycoefvector[1] + + psStep->polycoefmtx[1] * *pdfX + + psStep->polycoefmtx[3] * *pdfY + + psStep->polycoefmtx[5] * *pdfX * *pdfX + + psStep->polycoefmtx[7] * *pdfX * *pdfY + + psStep->polycoefmtx[9] * *pdfY * *pdfY; + + *pdfX = dfXOut; + *pdfY = dfYOut; + } + else if( psStep->order == 3 ) + { + dfXOut = psStep->polycoefvector[0] + + psStep->polycoefmtx[ 0] * *pdfX + + psStep->polycoefmtx[ 2] * *pdfY + + psStep->polycoefmtx[ 4] * *pdfX * *pdfX + + psStep->polycoefmtx[ 6] * *pdfX * *pdfY + + psStep->polycoefmtx[ 8] * *pdfY * *pdfY + + psStep->polycoefmtx[10] * *pdfX * *pdfX * *pdfX + + psStep->polycoefmtx[12] * *pdfX * *pdfX * *pdfY + + psStep->polycoefmtx[14] * *pdfX * *pdfY * *pdfY + + psStep->polycoefmtx[16] * *pdfY * *pdfY * *pdfY; + dfYOut = psStep->polycoefvector[1] + + psStep->polycoefmtx[ 1] * *pdfX + + psStep->polycoefmtx[ 3] * *pdfY + + psStep->polycoefmtx[ 5] * *pdfX * *pdfX + + psStep->polycoefmtx[ 7] * *pdfX * *pdfY + + psStep->polycoefmtx[ 9] * *pdfY * *pdfY + + psStep->polycoefmtx[11] * *pdfX * *pdfX * *pdfX + + psStep->polycoefmtx[13] * *pdfX * *pdfX * *pdfY + + psStep->polycoefmtx[15] * *pdfX * *pdfY * *pdfY + + psStep->polycoefmtx[17] * *pdfY * *pdfY * *pdfY; + + *pdfX = dfXOut; + *pdfY = dfYOut; + } + else + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* HFAWriteXFormStack() */ +/************************************************************************/ + +CPLErr HFAWriteXFormStack( HFAHandle hHFA, int nBand, int nXFormCount, + Efga_Polynomial **ppasPolyListForward, + Efga_Polynomial **ppasPolyListReverse ) + +{ + if( nXFormCount == 0 ) + return CE_None; + + if( ppasPolyListForward[0]->order != 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "For now HFAWriteXFormStack() only supports order 1 polynomials" ); + return CE_Failure; + } + + if( nBand < 0 || nBand > hHFA->nBands ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* If no band number is provided, operate on all bands. */ +/* -------------------------------------------------------------------- */ + if( nBand == 0 ) + { + CPLErr eErr = CE_None; + + for( nBand = 1; nBand <= hHFA->nBands; nBand++ ) + { + eErr = HFAWriteXFormStack( hHFA, nBand, nXFormCount, + ppasPolyListForward, + ppasPolyListReverse ); + if( eErr != CE_None ) + return eErr; + } + + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Fetch our band node. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poBandNode = hHFA->papoBand[nBand-1]->poNode; + HFAEntry *poXFormHeader; + + poXFormHeader = poBandNode->GetNamedChild( "MapToPixelXForm" ); + if( poXFormHeader == NULL ) + { + poXFormHeader = new HFAEntry( hHFA, "MapToPixelXForm", + "Exfr_GenericXFormHeader", poBandNode ); + poXFormHeader->MakeData( 23 ); + poXFormHeader->SetPosition(); + poXFormHeader->SetStringField( "titleList.string", "Affine" ); + } + +/* -------------------------------------------------------------------- */ +/* Loop over XForms. */ +/* -------------------------------------------------------------------- */ + for( int iXForm = 0; iXForm < nXFormCount; iXForm++ ) + { + Efga_Polynomial *psForward = *ppasPolyListForward + iXForm; + CPLString osXFormName; + osXFormName.Printf( "XForm%d", iXForm ); + + HFAEntry *poXForm = poXFormHeader->GetNamedChild( osXFormName ); + + if( poXForm == NULL ) + { + poXForm = new HFAEntry( hHFA, osXFormName, "Efga_Polynomial", + poXFormHeader ); + poXForm->MakeData( 136 ); + poXForm->SetPosition(); + } + + poXForm->SetIntField( "order", 1 ); + poXForm->SetIntField( "numdimtransform", 2 ); + poXForm->SetIntField( "numdimpolynomial", 2 ); + poXForm->SetIntField( "termcount", 3 ); + poXForm->SetIntField( "exponentlist[0]", 0 ); + poXForm->SetIntField( "exponentlist[1]", 0 ); + poXForm->SetIntField( "exponentlist[2]", 1 ); + poXForm->SetIntField( "exponentlist[3]", 0 ); + poXForm->SetIntField( "exponentlist[4]", 0 ); + poXForm->SetIntField( "exponentlist[5]", 1 ); + + poXForm->SetIntField( "polycoefmtx[-3]", EPT_f64 ); + poXForm->SetIntField( "polycoefmtx[-2]", 2 ); + poXForm->SetIntField( "polycoefmtx[-1]", 2 ); + poXForm->SetDoubleField( "polycoefmtx[0]", + psForward->polycoefmtx[0] ); + poXForm->SetDoubleField( "polycoefmtx[1]", + psForward->polycoefmtx[1] ); + poXForm->SetDoubleField( "polycoefmtx[2]", + psForward->polycoefmtx[2] ); + poXForm->SetDoubleField( "polycoefmtx[3]", + psForward->polycoefmtx[3] ); + + poXForm->SetIntField( "polycoefvector[-3]", EPT_f64 ); + poXForm->SetIntField( "polycoefvector[-2]", 1 ); + poXForm->SetIntField( "polycoefvector[-1]", 2 ); + poXForm->SetDoubleField( "polycoefvector[0]", + psForward->polycoefvector[0] ); + poXForm->SetDoubleField( "polycoefvector[1]", + psForward->polycoefvector[1] ); + } + + return CE_None; +} + +/************************************************************************/ +/* HFAReadCameraModel() */ +/************************************************************************/ + +char **HFAReadCameraModel( HFAHandle hHFA ) + +{ + if( hHFA->nBands == 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get the camera model node, and confirm it's type. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poXForm; + + poXForm = + hHFA->papoBand[0]->poNode->GetNamedChild( "MapToPixelXForm.XForm0" ); + if( poXForm == NULL ) + return NULL; + + if( !EQUAL(poXForm->GetType(),"Camera_ModelX") ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Convert the values to metadata. */ +/* -------------------------------------------------------------------- */ + const char *pszValue; + int i; + char **papszMD = NULL; + static const char *apszFields[] = { + "direction", "refType", "demsource", "PhotoDirection", "RotationSystem", + "demfilename", "demzunits", + "forSrcAffine[0]", "forSrcAffine[1]", "forSrcAffine[2]", + "forSrcAffine[3]", "forSrcAffine[4]", "forSrcAffine[5]", + "forDstAffine[0]", "forDstAffine[1]", "forDstAffine[2]", + "forDstAffine[3]", "forDstAffine[4]", "forDstAffine[5]", + "invSrcAffine[0]", "invSrcAffine[1]", "invSrcAffine[2]", + "invSrcAffine[3]", "invSrcAffine[4]", "invSrcAffine[5]", + "invDstAffine[0]", "invDstAffine[1]", "invDstAffine[2]", + "invDstAffine[3]", "invDstAffine[4]", "invDstAffine[5]", + "z_mean", "lat0", "lon0", + "coeffs[0]", "coeffs[1]", "coeffs[2]", + "coeffs[3]", "coeffs[4]", "coeffs[5]", + "coeffs[6]", "coeffs[7]", "coeffs[8]", + "LensDistortion[0]", "LensDistortion[1]", "LensDistortion[2]", + NULL }; + + for( i = 0; apszFields[i] != NULL; i++ ) + { + pszValue = poXForm->GetStringField( apszFields[i] ); + if( pszValue == NULL ) + pszValue = ""; + + papszMD = CSLSetNameValue( papszMD, apszFields[i], pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Create a pseudo-entry for the MIFObject with the */ +/* outputProjection. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poProjInfo = HFAEntry::BuildEntryFromMIFObject( poXForm, "outputProjection" ); + if (poProjInfo) + { + /* -------------------------------------------------------------------- */ + /* Fetch the datum. */ + /* -------------------------------------------------------------------- */ + Eprj_Datum sDatum; + + memset( &sDatum, 0, sizeof(sDatum)); + + sDatum.datumname = + (char *) poProjInfo->GetStringField("earthModel.datum.datumname"); + sDatum.type = (Eprj_DatumType) poProjInfo->GetIntField( + "earthModel.datum.type"); + + for( i = 0; i < 7; i++ ) + { + char szFieldName[60]; + + sprintf( szFieldName, "earthModel.datum.params[%d]", i ); + sDatum.params[i] = poProjInfo->GetDoubleField(szFieldName); + } + + sDatum.gridname = (char *) + poProjInfo->GetStringField("earthModel.datum.gridname"); + + /* -------------------------------------------------------------------- */ + /* Fetch the projection parameters. */ + /* -------------------------------------------------------------------- */ + Eprj_ProParameters sPro; + + memset( &sPro, 0, sizeof(sPro) ); + + sPro.proType = (Eprj_ProType) poProjInfo->GetIntField("projectionObject.proType"); + sPro.proNumber = poProjInfo->GetIntField("projectionObject.proNumber"); + sPro.proExeName = (char *) poProjInfo->GetStringField("projectionObject.proExeName"); + sPro.proName = (char *) poProjInfo->GetStringField("projectionObject.proName"); + sPro.proZone = poProjInfo->GetIntField("projectionObject.proZone"); + + for( i = 0; i < 15; i++ ) + { + char szFieldName[40]; + + sprintf( szFieldName, "projectionObject.proParams[%d]", i ); + sPro.proParams[i] = poProjInfo->GetDoubleField(szFieldName); + } + + /* -------------------------------------------------------------------- */ + /* Fetch the spheroid. */ + /* -------------------------------------------------------------------- */ + sPro.proSpheroid.sphereName = (char *) + poProjInfo->GetStringField("earthModel.proSpheroid.sphereName"); + sPro.proSpheroid.a = poProjInfo->GetDoubleField("earthModel.proSpheroid.a"); + sPro.proSpheroid.b = poProjInfo->GetDoubleField("earthModel.proSpheroid.b"); + sPro.proSpheroid.eSquared = + poProjInfo->GetDoubleField("earthModel.proSpheroid.eSquared"); + sPro.proSpheroid.radius = + poProjInfo->GetDoubleField("earthModel.proSpheroid.radius"); + + /* -------------------------------------------------------------------- */ + /* Fetch the projection info. */ + /* -------------------------------------------------------------------- */ + char *pszProjection; + + // poProjInfo->DumpFieldValues( stdout, "" ); + + pszProjection = HFAPCSStructToWKT( &sDatum, &sPro, NULL, NULL ); + + if( pszProjection ) + { + papszMD = + CSLSetNameValue( papszMD, "outputProjection", pszProjection ); + CPLFree( pszProjection ); + } + + delete poProjInfo; + } + +/* -------------------------------------------------------------------- */ +/* Fetch the horizontal units. */ +/* -------------------------------------------------------------------- */ + pszValue = poXForm->GetStringField( "outputHorizontalUnits.string" ); + if( pszValue == NULL ) + pszValue = ""; + + papszMD = CSLSetNameValue( papszMD, "outputHorizontalUnits", pszValue ); + +/* -------------------------------------------------------------------- */ +/* Fetch the elevationinfo. */ +/* -------------------------------------------------------------------- */ + HFAEntry *poElevInfo = HFAEntry::BuildEntryFromMIFObject( poXForm, "outputElevationInfo" ); + if ( poElevInfo ) + { + //poElevInfo->DumpFieldValues( stdout, "" ); + + if( poElevInfo->GetDataSize() != 0 ) + { + static const char *apszEFields[] = { + "verticalDatum.datumname", + "verticalDatum.type", + "elevationUnit", + "elevationType", + NULL }; + + for( i = 0; apszEFields[i] != NULL; i++ ) + { + pszValue = poElevInfo->GetStringField( apszEFields[i] ); + if( pszValue == NULL ) + pszValue = ""; + + papszMD = CSLSetNameValue( papszMD, apszEFields[i], pszValue ); + } + } + + delete poElevInfo; + } + + return papszMD; +} + +/************************************************************************/ +/* HFASetGeoTransform() */ +/* */ +/* Set a MapInformation and XForm block. Allows for rotated */ +/* and shared geotransforms. */ +/************************************************************************/ + +CPLErr HFASetGeoTransform( HFAHandle hHFA, + const char *pszProName, + const char *pszUnits, + double *padfGeoTransform ) + +{ +/* -------------------------------------------------------------------- */ +/* Write MapInformation. */ +/* -------------------------------------------------------------------- */ + int nBand; + + for( nBand = 1; nBand <= hHFA->nBands; nBand++ ) + { + HFAEntry *poBandNode = hHFA->papoBand[nBand-1]->poNode; + + HFAEntry *poMI = poBandNode->GetNamedChild( "MapInformation" ); + if( poMI == NULL ) + { + poMI = new HFAEntry( hHFA, "MapInformation", + "Eimg_MapInformation", poBandNode ); + poMI->MakeData( 18 + strlen(pszProName) + strlen(pszUnits) ); + poMI->SetPosition(); + } + + poMI->SetStringField( "projection.string", pszProName ); + poMI->SetStringField( "units.string", pszUnits ); + } + +/* -------------------------------------------------------------------- */ +/* Write XForm. */ +/* -------------------------------------------------------------------- */ + Efga_Polynomial sForward, sReverse; + double adfAdjTransform[6], adfRevTransform[6]; + + // Offset by half pixel. + + memcpy( adfAdjTransform, padfGeoTransform, sizeof(double) * 6 ); + adfAdjTransform[0] += adfAdjTransform[1] * 0.5; + adfAdjTransform[0] += adfAdjTransform[2] * 0.5; + adfAdjTransform[3] += adfAdjTransform[4] * 0.5; + adfAdjTransform[3] += adfAdjTransform[5] * 0.5; + + // Invert + HFAInvGeoTransform( adfAdjTransform, adfRevTransform ); + + // Assign to polynomial object. + + sForward.order = 1; + sForward.polycoefvector[0] = adfRevTransform[0]; + sForward.polycoefmtx[0] = adfRevTransform[1]; + sForward.polycoefmtx[1] = adfRevTransform[4]; + sForward.polycoefvector[1] = adfRevTransform[3]; + sForward.polycoefmtx[2] = adfRevTransform[2]; + sForward.polycoefmtx[3] = adfRevTransform[5]; + + sReverse = sForward; + Efga_Polynomial *psForward=&sForward, *psReverse=&sReverse; + + return HFAWriteXFormStack( hHFA, 0, 1, &psForward, &psReverse ); +} + +/************************************************************************/ +/* HFARenameReferences() */ +/* */ +/* Rename references in this .img file from the old basename to */ +/* a new basename. This should be passed on to .aux and .rrd */ +/* files and should include references to .aux, .rrd and .ige. */ +/************************************************************************/ + +CPLErr HFARenameReferences( HFAHandle hHFA, + const char *pszNewBase, + const char *pszOldBase ) + +{ +/* -------------------------------------------------------------------- */ +/* Handle RRDNamesList updates. */ +/* -------------------------------------------------------------------- */ + size_t iNode; + std::vector apoNodeList = + hHFA->poRoot->FindChildren( "RRDNamesList", NULL ); + + for( iNode = 0; iNode < apoNodeList.size(); iNode++ ) + { + HFAEntry *poRRDNL = apoNodeList[iNode]; + std::vector aosNL; + + // Collect all the existing names. + int i, nNameCount = poRRDNL->GetFieldCount( "nameList" ); + + CPLString osAlgorithm = poRRDNL->GetStringField("algorithm.string"); + for( i = 0; i < nNameCount; i++ ) + { + CPLString osFN; + osFN.Printf( "nameList[%d].string", i ); + aosNL.push_back( poRRDNL->GetStringField(osFN) ); + } + + // Adjust the names to the new form. + for( i = 0; i < nNameCount; i++ ) + { + if( strncmp(aosNL[i],pszOldBase,strlen(pszOldBase)) == 0 ) + { + CPLString osNew = pszNewBase; + osNew += aosNL[i].c_str() + strlen(pszOldBase); + aosNL[i] = osNew; + } + } + + // try to make sure the RRDNamesList is big enough to hold the + // adjusted name list. + if( strlen(pszNewBase) > strlen(pszOldBase) ) + { + CPLDebug( "HFA", "Growing RRDNamesList to hold new names" ); + poRRDNL->MakeData( poRRDNL->GetDataSize() + + nNameCount * (strlen(pszNewBase) - strlen(pszOldBase)) ); + } + + // Initialize the whole thing to zeros for a clean start. + memset( poRRDNL->GetData(), 0, poRRDNL->GetDataSize() ); + + // Write the updates back to the file. + poRRDNL->SetStringField( "algorithm.string", osAlgorithm ); + for( i = 0; i < nNameCount; i++ ) + { + CPLString osFN; + osFN.Printf( "nameList[%d].string", i ); + poRRDNL->SetStringField( osFN, aosNL[i] ); + } + } + +/* -------------------------------------------------------------------- */ +/* spill file references. */ +/* -------------------------------------------------------------------- */ + apoNodeList = + hHFA->poRoot->FindChildren( "ExternalRasterDMS", "ImgExternalRaster" ); + + for( iNode = 0; iNode < apoNodeList.size(); iNode++ ) + { + HFAEntry *poERDMS = apoNodeList[iNode]; + + if( poERDMS == NULL ) + continue; + + // Fetch all existing values. + CPLString osFileName = poERDMS->GetStringField("fileName.string"); + GInt32 anValidFlagsOffset[2], anStackDataOffset[2]; + GInt32 nStackCount, nStackIndex; + + anValidFlagsOffset[0] = + poERDMS->GetIntField( "layerStackValidFlagsOffset[0]" ); + anValidFlagsOffset[1] = + poERDMS->GetIntField( "layerStackValidFlagsOffset[1]" ); + + anStackDataOffset[0] = + poERDMS->GetIntField( "layerStackDataOffset[0]" ); + anStackDataOffset[1] = + poERDMS->GetIntField( "layerStackDataOffset[1]" ); + + nStackCount = poERDMS->GetIntField( "layerStackCount" ); + nStackIndex = poERDMS->GetIntField( "layerStackIndex" ); + + // Update the filename. + if( strncmp(osFileName,pszOldBase,strlen(pszOldBase)) == 0 ) + { + CPLString osNew = pszNewBase; + osNew += osFileName.c_str() + strlen(pszOldBase); + osFileName = osNew; + } + + // Grow the node if needed. + if( strlen(pszNewBase) > strlen(pszOldBase) ) + { + CPLDebug( "HFA", "Growing ExternalRasterDMS to hold new names" ); + poERDMS->MakeData( poERDMS->GetDataSize() + + (strlen(pszNewBase) - strlen(pszOldBase)) ); + } + + // Initialize the whole thing to zeros for a clean start. + memset( poERDMS->GetData(), 0, poERDMS->GetDataSize() ); + + // Write it all out again, this may change the size of the node. + poERDMS->SetStringField( "fileName.string", osFileName ); + poERDMS->SetIntField( "layerStackValidFlagsOffset[0]", + anValidFlagsOffset[0] ); + poERDMS->SetIntField( "layerStackValidFlagsOffset[1]", + anValidFlagsOffset[1] ); + + poERDMS->SetIntField( "layerStackDataOffset[0]", + anStackDataOffset[0] ); + poERDMS->SetIntField( "layerStackDataOffset[1]", + anStackDataOffset[1] ); + + poERDMS->SetIntField( "layerStackCount", nStackCount ); + poERDMS->SetIntField( "layerStackIndex", nStackIndex ); + } + +/* -------------------------------------------------------------------- */ +/* DependentFile */ +/* -------------------------------------------------------------------- */ + apoNodeList = + hHFA->poRoot->FindChildren( "DependentFile", "Eimg_DependentFile" ); + + for( iNode = 0; iNode < apoNodeList.size(); iNode++ ) + { + CPLString osFileName = apoNodeList[iNode]-> + GetStringField("dependent.string"); + + // Grow the node if needed. + if( strlen(pszNewBase) > strlen(pszOldBase) ) + { + CPLDebug( "HFA", "Growing DependentFile to hold new names" ); + apoNodeList[iNode]->MakeData( apoNodeList[iNode]->GetDataSize() + + (strlen(pszNewBase) + - strlen(pszOldBase)) ); + } + + // Update the filename. + if( strncmp(osFileName,pszOldBase,strlen(pszOldBase)) == 0 ) + { + CPLString osNew = pszNewBase; + osNew += osFileName.c_str() + strlen(pszOldBase); + osFileName = osNew; + } + + apoNodeList[iNode]->SetStringField( "dependent.string", osFileName ); + } + + return CE_None; +} diff --git a/bazaar/plugin/gdal/frmts/hfa/hfatest.cpp b/bazaar/plugin/gdal/frmts/hfa/hfatest.cpp new file mode 100644 index 000000000..07b28faf1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/hfatest.cpp @@ -0,0 +1,232 @@ +/****************************************************************************** + * $Id: hfatest.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: Erdas Imagine (.img) Translator + * Purpose: Testing mainline for HFA services - transitory. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Intergraph Corporation + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "hfa_p.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: hfatest.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* Usage() */ +/************************************************************************/ + +static void Usage() + +{ + printf( "hfatest [-dd] [-dt] [-dr] filename\n" ); +} + +/************************************************************************/ +/* Stub for HFAPCSStructToWKT, defined in hfadataset.cpp but used by */ +/* hfaopen.cpp */ +/************************************************************************/ +#ifndef WIN32 +char * +HFAPCSStructToWKT( const Eprj_Datum *psDatum, + const Eprj_ProParameters *psPro, + const Eprj_MapInfo *psMapInfo, + HFAEntry *poMapInformation ) +{ + return NULL; +} +#endif + +/************************************************************************/ +/* main() */ +/************************************************************************/ + +int main( int argc, char ** argv ) + +{ + const char *pszFilename = NULL; + int nDumpTree = FALSE; + int nDumpDict = FALSE; + int nRastReport = FALSE; + int i, nXSize, nYSize, nBands; + HFAHandle hHFA; + const Eprj_MapInfo *psMapInfo; + const Eprj_ProParameters *psProParameters; + const Eprj_Datum *psDatum; + +/* -------------------------------------------------------------------- */ +/* Handle arguments. */ +/* -------------------------------------------------------------------- */ + for( i = 1; i < argc; i++ ) + { + if( EQUAL(argv[i],"-dd") ) + nDumpDict = TRUE; + else if( EQUAL(argv[i],"-dt") ) + nDumpTree = TRUE; + else if( EQUAL(argv[i],"-dr") ) + nRastReport = TRUE; + else if( pszFilename == NULL ) + pszFilename = argv[i]; + else + { + Usage(); + exit( 1 ); + } + } + + if( pszFilename == NULL ) + { + Usage(); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + hHFA = HFAOpen( pszFilename, "r" ); + + if( hHFA == NULL ) + { + printf( "HFAOpen() failed.\n" ); + exit( 100 ); + } + +/* -------------------------------------------------------------------- */ +/* Do we want to walk the tree dumping out general information? */ +/* -------------------------------------------------------------------- */ + if( nDumpDict ) + { + HFADumpDictionary( hHFA, stdout ); + } + +/* -------------------------------------------------------------------- */ +/* Do we want to walk the tree dumping out general information? */ +/* -------------------------------------------------------------------- */ + if( nDumpTree ) + { + HFADumpTree( hHFA, stdout ); + } + +/* -------------------------------------------------------------------- */ +/* Dump indirectly collected data about bands. */ +/* -------------------------------------------------------------------- */ + HFAGetRasterInfo( hHFA, &nXSize, &nYSize, &nBands ); + + if( nRastReport ) + { + printf( "Raster Size = %d x %d\n", nXSize, nYSize ); + + for( i = 1; i <= nBands; i++ ) + { + int nDataType, nColors, nOverviews, iOverview; + double *padfRed, *padfGreen, *padfBlue, *padfAlpha, *padfBins; + int nBlockXSize, nBlockYSize, nCompressionType; + + HFAGetBandInfo( hHFA, i, &nDataType, &nBlockXSize, &nBlockYSize, + &nCompressionType ); + nOverviews = HFAGetOverviewCount( hHFA, i ); + + printf( "Band %d: %dx%d tiles, type = %d\n", + i, nBlockXSize, nBlockYSize, nDataType ); + + for( iOverview=0; iOverview < nOverviews; iOverview++ ) + { + HFAGetOverviewInfo( hHFA, i, iOverview, + &nXSize, &nYSize, + &nBlockXSize, &nBlockYSize, NULL ); + printf( " Overview: %dx%d (blocksize %dx%d)\n", + nXSize, nYSize, nBlockXSize, nBlockYSize ); + } + + if( HFAGetPCT( hHFA, i, &nColors, &padfRed, &padfGreen, + &padfBlue, &padfAlpha, &padfBins ) + == CE_None ) + { + int j; + + for( j = 0; j < nColors; j++ ) + { + printf( "PCT[%d] = %f,%f,%f %f\n", + (padfBins != NULL) ? (int) padfBins[j] : j, + padfRed[j], padfGreen[j], + padfBlue[j], padfAlpha[j]); + } + } + +/* -------------------------------------------------------------------- */ +/* Report statistics. We need to dig directly into the C++ API. */ +/* -------------------------------------------------------------------- */ + HFABand *poBand = hHFA->papoBand[i-1]; + HFAEntry *poStats = poBand->poNode->GetNamedChild( "Statistics" ); + + if( poStats != NULL ) + { + printf( " Min: %g Max: %g Mean: %g\n", + poStats->GetDoubleField( "minimum" ), + poStats->GetDoubleField( "maximum" ), + poStats->GetDoubleField( "mean" ) ); + printf( " Median: %g Mode: %g Stddev: %g\n", + poStats->GetDoubleField( "median" ), + poStats->GetDoubleField( "mode" ), + poStats->GetDoubleField( "stddev" ) ); + } + else + printf( " No Statistics found.\n" ); + } + +/* -------------------------------------------------------------------- */ +/* Dump the map info structure. */ +/* -------------------------------------------------------------------- */ + psMapInfo = HFAGetMapInfo( hHFA ); + + if( psMapInfo != NULL ) + { + printf( "MapInfo.proName = %s\n", psMapInfo->proName ); + printf( "MapInfo.upperLeftCenter.x = %.2f\n", + psMapInfo->upperLeftCenter.x ); + printf( "MapInfo.upperLeftCenter.y = %.2f\n", + psMapInfo->upperLeftCenter.y ); + } + else + { + printf( "No Map Info found\n" ); + } + + } + + psProParameters = HFAGetProParameters( hHFA ); + + psDatum = HFAGetDatum( hHFA ); + + HFAClose( hHFA ); + + VSICleanupFileManager(); + CPLCleanupTLS(); + +#ifdef DBMALLOC + malloc_dump(1); +#endif + + exit( 0 ); +} diff --git a/bazaar/plugin/gdal/frmts/hfa/hfatype.cpp b/bazaar/plugin/gdal/frmts/hfa/hfatype.cpp new file mode 100644 index 000000000..da95e36dc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/hfatype.cpp @@ -0,0 +1,530 @@ +/****************************************************************************** + * $Id: hfatype.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: Erdas Imagine (.img) Translator + * Purpose: Implementation of the HFAType class, for managing one type + * defined in the HFA data dictionary. Managed by HFADictionary. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Intergraph Corporation + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "hfa_p.h" + +CPL_CVSID("$Id: hfatype.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* ==================================================================== */ +/* HFAType */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* HFAType() */ +/************************************************************************/ + +HFAType::HFAType() + +{ + nBytes = 0; + nFields = 0; + papoFields = NULL; + pszTypeName = NULL; + bInCompleteDefn = FALSE; +} + +/************************************************************************/ +/* ~HFAType() */ +/************************************************************************/ + +HFAType::~HFAType() + +{ + int i; + + for( i = 0; i < nFields; i++ ) + { + delete papoFields[i]; + } + + CPLFree( papoFields ); + + CPLFree( pszTypeName ); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +const char *HFAType::Initialize( const char * pszInput ) + +{ + int i; + + + if( *pszInput != '{' ) + { + if( *pszInput != '\0' ) + CPLDebug( "HFAType", "Initialize(%60.60s) - unexpected input.", + pszInput ); + + while( *pszInput != '{' && *pszInput != '\0' ) + pszInput++; + + if( *pszInput == '\0' ) + return NULL; + } + + pszInput++; + +/* -------------------------------------------------------------------- */ +/* Read the field definitions. */ +/* -------------------------------------------------------------------- */ + while( pszInput != NULL && *pszInput != '}' ) + { + HFAField *poNewField = new HFAField(); + + pszInput = poNewField->Initialize( pszInput ); + if( pszInput != NULL ) + { + papoFields = (HFAField **) + CPLRealloc(papoFields, sizeof(void*) * (nFields+1) ); + papoFields[nFields++] = poNewField; + } + else + delete poNewField; + } + + if( pszInput == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Collect the name. */ +/* -------------------------------------------------------------------- */ + pszInput++; /* skip `}' */ + + for( i = 0; pszInput[i] != '\0' && pszInput[i] != ','; i++ ) {} + if (pszInput[i] == '\0') + return NULL; + + pszTypeName = (char *) CPLMalloc(i+1); + strncpy( pszTypeName, pszInput, i ); + pszTypeName[i] = '\0'; + + pszInput += i+1; + + return( pszInput ); +} + +/************************************************************************/ +/* CompleteDefn() */ +/************************************************************************/ + +void HFAType::CompleteDefn( HFADictionary * poDict ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* This may already be done, if an earlier object required this */ +/* object (as a field), and forced an early computation of the */ +/* size. */ +/* -------------------------------------------------------------------- */ + if( nBytes != 0 ) + return; + + + if( bInCompleteDefn ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Recursion detected in HFAType::CompleteDefn()"); + return; + } + bInCompleteDefn = TRUE; + +/* -------------------------------------------------------------------- */ +/* Complete each of the fields, totaling up the sizes. This */ +/* isn't really accurate for object with variable sized */ +/* subobjects. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nFields; i++ ) + { + papoFields[i]->CompleteDefn( poDict ); + if( papoFields[i]->nBytes < 0 || nBytes == -1 ) + nBytes = -1; + else + nBytes += papoFields[i]->nBytes; + } + + bInCompleteDefn = FALSE; +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +void HFAType::Dump( FILE * fp ) + +{ + int i; + + VSIFPrintf( fp, "HFAType %s/%d bytes\n", pszTypeName, nBytes ); + + for( i = 0; i < nFields; i++ ) + { + papoFields[i]->Dump( fp ); + } + + VSIFPrintf( fp, "\n" ); +} + +/************************************************************************/ +/* SetInstValue() */ +/************************************************************************/ + +CPLErr +HFAType::SetInstValue( const char * pszFieldPath, + GByte *pabyData, GUInt32 nDataOffset, int nDataSize, + char chReqType, void *pValue ) + +{ + int nArrayIndex = 0, nNameLen, iField, nByteOffset; + const char *pszRemainder; + +/* -------------------------------------------------------------------- */ +/* Parse end of field name, possible index value and */ +/* establish where the remaining fields (if any) would start. */ +/* -------------------------------------------------------------------- */ + if( strchr(pszFieldPath,'[') != NULL ) + { + const char *pszEnd = strchr(pszFieldPath,'['); + + nArrayIndex = atoi(pszEnd+1); + nNameLen = pszEnd - pszFieldPath; + + pszRemainder = strchr(pszFieldPath,'.'); + if( pszRemainder != NULL ) + pszRemainder++; + } + + else if( strchr(pszFieldPath,'.') != NULL ) + { + const char *pszEnd = strchr(pszFieldPath,'.'); + + nNameLen = pszEnd - pszFieldPath; + + pszRemainder = pszEnd + 1; + } + + else + { + nNameLen = strlen(pszFieldPath); + pszRemainder = pszFieldPath/*NULL*/; + } + +/* -------------------------------------------------------------------- */ +/* Find this field within this type, if possible. */ +/* -------------------------------------------------------------------- */ + nByteOffset = 0; + for( iField = 0; iField < nFields && nByteOffset < nDataSize; iField++ ) + { + if( EQUALN(pszFieldPath,papoFields[iField]->pszFieldName,nNameLen) + && papoFields[iField]->pszFieldName[nNameLen] == '\0' ) + { + break; + } + + int nInc = papoFields[iField]->GetInstBytes( pabyData+nByteOffset, + nDataSize - nByteOffset ); + + if (nInc < 0 || nByteOffset > INT_MAX - nInc) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value"); + return CE_Failure; + } + + nByteOffset += nInc; + } + + if( iField == nFields || nByteOffset >= nDataSize ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Extract this field value, and return. */ +/* -------------------------------------------------------------------- */ + return( papoFields[iField]->SetInstValue( pszRemainder, nArrayIndex, + pabyData + nByteOffset, + nDataOffset + nByteOffset, + nDataSize - nByteOffset, + chReqType, pValue ) ); +} + +/************************************************************************/ +/* GetInstCount() */ +/************************************************************************/ + +int +HFAType::GetInstCount( const char * pszFieldPath, + GByte *pabyData, + CPL_UNUSED GUInt32 nDataOffset, + int nDataSize ) +{ + /* int nArrayIndex = 0; */ + int nNameLen, iField, nByteOffset; + const char *pszRemainder; + +/* -------------------------------------------------------------------- */ +/* Parse end of field name, possible index value and */ +/* establish where the remaining fields (if any) would start. */ +/* -------------------------------------------------------------------- */ + if( strchr(pszFieldPath,'[') != NULL ) + { + const char *pszEnd = strchr(pszFieldPath,'['); + + /* nArrayIndex = atoi(pszEnd+1); */ + nNameLen = pszEnd - pszFieldPath; + + pszRemainder = strchr(pszFieldPath,'.'); + if( pszRemainder != NULL ) + pszRemainder++; + } + + else if( strchr(pszFieldPath,'.') != NULL ) + { + const char *pszEnd = strchr(pszFieldPath,'.'); + + nNameLen = pszEnd - pszFieldPath; + + pszRemainder = pszEnd + 1; + } + + else + { + nNameLen = strlen(pszFieldPath); + pszRemainder = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Find this field within this type, if possible. */ +/* -------------------------------------------------------------------- */ + nByteOffset = 0; + for( iField = 0; iField < nFields && nByteOffset < nDataSize; iField++ ) + { + if( EQUALN(pszFieldPath,papoFields[iField]->pszFieldName,nNameLen) + && papoFields[iField]->pszFieldName[nNameLen] == '\0' ) + { + break; + } + + int nInc = papoFields[iField]->GetInstBytes( pabyData+nByteOffset, + nDataSize - nByteOffset ); + + if (nInc < 0 || nByteOffset > INT_MAX - nInc) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value"); + return -1; + } + + nByteOffset += nInc; + } + + if( iField == nFields || nByteOffset >= nDataSize ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Extract this field value, and return. */ +/* -------------------------------------------------------------------- */ + return( papoFields[iField]->GetInstCount( pabyData + nByteOffset, + nDataSize - nByteOffset ) ); +} + +/************************************************************************/ +/* ExtractInstValue() */ +/* */ +/* Extract the value of a field instance within this type. */ +/* Most of the work is done by the ExtractInstValue() for the */ +/* HFAField, but this methond does the field name parsing. */ +/* */ +/* field names have the form: */ +/* */ +/* fieldname{[index]}{.fieldname...} */ +/* */ +/* eg. */ +/* abc - field abc[0] */ +/* abc[3] - field abc[3] */ +/* abc[2].def - field def[0] of */ +/* the third abc struct. */ +/************************************************************************/ + +int +HFAType::ExtractInstValue( const char * pszFieldPath, + GByte *pabyData, GUInt32 nDataOffset, int nDataSize, + char chReqType, void *pReqReturn, int *pnRemainingDataSize ) + +{ + int nArrayIndex = 0, nNameLen, iField, nByteOffset; + const char *pszRemainder; + +/* -------------------------------------------------------------------- */ +/* Parse end of field name, possible index value and */ +/* establish where the remaining fields (if any) would start. */ +/* -------------------------------------------------------------------- */ + const char *pszFirstArray = strchr(pszFieldPath,'['); + const char *pszFirstDot = strchr(pszFieldPath,'.'); + + if( pszFirstArray != NULL + && (pszFirstDot == NULL + || pszFirstDot > pszFirstArray) ) + { + const char *pszEnd = pszFirstArray; + + nArrayIndex = atoi(pszEnd+1); + nNameLen = pszEnd - pszFieldPath; + + pszRemainder = strchr(pszFieldPath,'.'); + if( pszRemainder != NULL ) + pszRemainder++; + } + + else if( pszFirstDot != NULL ) + { + const char *pszEnd = pszFirstDot; + + nNameLen = pszEnd - pszFieldPath; + + pszRemainder = pszEnd + 1; + } + + else + { + nNameLen = strlen(pszFieldPath); + pszRemainder = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Find this field within this type, if possible. */ +/* -------------------------------------------------------------------- */ + nByteOffset = 0; + for( iField = 0; iField < nFields && nByteOffset < nDataSize; iField++ ) + { + if( EQUALN(pszFieldPath,papoFields[iField]->pszFieldName,nNameLen) + && papoFields[iField]->pszFieldName[nNameLen] == '\0' ) + { + break; + } + + int nInc = papoFields[iField]->GetInstBytes( pabyData+nByteOffset, + nDataSize - nByteOffset ); + + if (nInc < 0 || nByteOffset > INT_MAX - nInc) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value"); + return FALSE; + } + + nByteOffset += nInc; + } + + if( iField == nFields || nByteOffset >= nDataSize ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Extract this field value, and return. */ +/* -------------------------------------------------------------------- */ + return( papoFields[iField]-> + ExtractInstValue( pszRemainder, nArrayIndex, + pabyData + nByteOffset, + nDataOffset + nByteOffset, + nDataSize - nByteOffset, + chReqType, pReqReturn, + pnRemainingDataSize) ); +} + + +/************************************************************************/ +/* DumpInstValue() */ +/************************************************************************/ + +void HFAType::DumpInstValue( FILE * fpOut, + GByte *pabyData, GUInt32 nDataOffset, + int nDataSize, const char * pszPrefix ) + +{ + int iField; + + for ( iField = 0; iField < nFields && nDataSize > 0; iField++ ) + { + HFAField *poField = papoFields[iField]; + int nInstBytes; + + poField->DumpInstValue( fpOut, pabyData, nDataOffset, + nDataSize, pszPrefix ); + + nInstBytes = poField->GetInstBytes( pabyData, nDataSize ); + if (nInstBytes < 0 || nDataOffset > UINT_MAX - nInstBytes) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value"); + return; + } + + pabyData += nInstBytes; + nDataOffset += nInstBytes; + nDataSize -= nInstBytes; + } +} + +/************************************************************************/ +/* GetInstBytes() */ +/* */ +/* How many bytes in this particular instance of this type? */ +/************************************************************************/ + +int HFAType::GetInstBytes( GByte *pabyData, int nDataSize ) + +{ + if( nBytes >= 0 ) + return( nBytes ); + else + { + int nTotal = 0; + int iField; + + for( iField = 0; iField < nFields && nTotal < nDataSize; iField++ ) + { + HFAField *poField = papoFields[iField]; + + int nInstBytes = poField->GetInstBytes( pabyData, + nDataSize - nTotal ); + if (nInstBytes < 0 || nTotal > INT_MAX - nInstBytes) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid return value"); + return -1; + } + + pabyData += nInstBytes; + nTotal += nInstBytes; + } + + return( nTotal ); + } +} diff --git a/bazaar/plugin/gdal/frmts/hfa/makefile.vc b/bazaar/plugin/gdal/frmts/hfa/makefile.vc new file mode 100644 index 000000000..f38e2bb7a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/hfa/makefile.vc @@ -0,0 +1,25 @@ + +OBJ = hfaband.obj hfadataset.obj hfadictionary.obj hfaentry.obj \ + hfafield.obj hfaopen.obj hfatype.obj hfacompress.obj \ + hfa_overviews.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + for %I in ($(OBJ)) do xcopy /D /Y %I ..\o + +clean: + -del *.obj + -del *.exe + -del *.ilk + -del *.exp + -del *.lib + -del *.pdb + -del *.manifest + +hfatest.exe: hfatest.cpp $(GDALLIB) + $(CC) $(CFLAGS) hfatest.cpp $(OBJ) $(GDALLIB) + if exist $@.manifest mt -manifest $@.manifest -outputresource:$@;1 + del hfatest.obj diff --git a/bazaar/plugin/gdal/frmts/idrisi/GNUmakefile b/bazaar/plugin/gdal/frmts/idrisi/GNUmakefile new file mode 100644 index 000000000..3258dec47 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/idrisi/GNUmakefile @@ -0,0 +1,15 @@ +GDAL_ROOT = ../.. + +include $(GDAL_ROOT)/GDALmake.opt + +OBJ = IdrisiDataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + diff --git a/bazaar/plugin/gdal/frmts/idrisi/IdrisiDataset.cpp b/bazaar/plugin/gdal/frmts/idrisi/IdrisiDataset.cpp new file mode 100644 index 000000000..ac56d28d9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/idrisi/IdrisiDataset.cpp @@ -0,0 +1,3361 @@ +/***************************************************************************** +* $Id: IdrisiDataset.cpp 27942 2014-11-11 00:57:41Z rouault $ +* +* Project: Idrisi Raster Image File Driver +* Purpose: Read/write Idrisi Raster Image Format RST +* Author: Ivan Lucena, [lucena_ivan at hotmail.com] +* +* Revised by Hongmei Zhu, February, 2013 +* honzhu@clarku.edu +* Clark Labs/Clark University +* +****************************************************************************** +* Copyright( c ) 2006, Ivan Lucena + * Copyright (c) 2007-2012, Even Rouault +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files( the "Software" ), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" +#include "ogr_spatialref.h" +#include "gdal_pam.h" +#include "gdal_alg.h" +#include "gdal_rat.h" +#include "idrisi.h" + +CPL_CVSID( "$Id: IdrisiDataset.cpp 27942 2014-11-11 00:57:41Z rouault $" ); + +CPL_C_START +void GDALRegister_IDRISI( void); +CPL_C_END + +#ifdef WIN32 +#define PATHDELIM '\\' +#else +#define PATHDELIM '/' +#endif + +//----- Safe numeric conversion, NULL as zero +#define atoi_nz(s) (s == NULL ? (int) 0 : atoi(s)) +#define CPLAtof_nz(s) (s == NULL ? (double) 0.0 : CPLAtof(s)) + +//----- file extensions: +#define extRST "rst" +#define extRDC "rdc" +#define extSMP "smp" +#define extREF "ref" +#define extRSTu "RST" +#define extRDCu "RDC" +#define extSMPu "SMP" +#define extREFu "REF" + +//----- field names on rdc file: +#define rdcFILE_FORMAT "file format " +#define rdcFILE_TITLE "file title " +#define rdcDATA_TYPE "data type " +#define rdcFILE_TYPE "file type " +#define rdcCOLUMNS "columns " +#define rdcROWS "rows " +#define rdcREF_SYSTEM "ref. system " +#define rdcREF_UNITS "ref. units " +#define rdcUNIT_DIST "unit dist. " +#define rdcMIN_X "min. X " +#define rdcMAX_X "max. X " +#define rdcMIN_Y "min. Y " +#define rdcMAX_Y "max. Y " +#define rdcPOSN_ERROR "pos'n error " +#define rdcRESOLUTION "resolution " +#define rdcMIN_VALUE "min. value " +#define rdcMAX_VALUE "max. value " +#define rdcDISPLAY_MIN "display min " +#define rdcDISPLAY_MAX "display max " +#define rdcVALUE_UNITS "value units " +#define rdcVALUE_ERROR "value error " +#define rdcFLAG_VALUE "flag value " +#define rdcFLAG_DEFN "flag def'n " +#define rdcFLAG_DEFN2 "flag def`n " +#define rdcLEGEND_CATS "legend cats " +#define rdcLINEAGES "lineage " +#define rdcCOMMENTS "comment " +#define rdcCODE_N "code %6d " + +//----- ".ref" file field names: +#define refREF_SYSTEM "ref. system " +#define refREF_SYSTEM2 "ref.system " +#define refPROJECTION "projection " +#define refDATUM "datum " +#define refDELTA_WGS84 "delta WGS84 " +#define refELLIPSOID "ellipsoid " +#define refMAJOR_SAX "major s-ax " +#define refMINOR_SAX "minor s-ax " +#define refORIGIN_LONG "origin long " +#define refORIGIN_LAT "origin lat " +#define refORIGIN_X "origin X " +#define refORIGIN_Y "origin Y " +#define refSCALE_FAC "scale fac " +#define refUNITS "units " +#define refPARAMETERS "parameters " +#define refSTANDL_1 "stand ln 1 " +#define refSTANDL_2 "stand ln 2 " + +//----- standard values: +#define rstVERSION "Idrisi Raster A.1" +#define rstBYTE "byte" +#define rstINTEGER "integer" +#define rstREAL "real" +#define rstRGB24 "rgb24" +#define rstDEGREE "deg" +#define rstMETER "m" +#define rstLATLONG "latlong" +#define rstLATLONG2 "lat/long" +#define rstPLANE "plane" +#define rstUTM "utm-%d%c" +#define rstSPC "spc%2d%2s%d" + +//----- palette file( .smp ) header size: +#define smpHEADERSIZE 18 + +//----- check if file exists: +bool FileExists( const char *pszPath ); + +//----- Reference Table +struct ReferenceTab { + int nCode; + const char *pszName; +}; + +//----- USA State's reference table to USGS PCS Code +static const ReferenceTab aoUSStateTable[] = { + {101, "al"}, + {201, "az"}, + {301, "ar"}, + {401, "ca"}, + {501, "co"}, + {600, "ct"}, + {700, "de"}, + {901, "fl"}, + {1001, "ga"}, + {1101, "id"}, + {1201, "il"}, + {1301, "in"}, + {1401, "ia"}, + {1501, "ks"}, + {1601, "ky"}, + {1701, "la"}, + {1801, "me"}, + {1900, "md"}, + {2001, "ma"}, + {2111, "mi"}, + {2201, "mn"}, + {2301, "ms"}, + {2401, "mo"}, + {2500, "mt"}, + {2600, "ne"}, + {2701, "nv"}, + {2800, "nh"}, + {2900, "nj"}, + {3001, "nm"}, + {3101, "ny"}, + {3200, "nc"}, + {3301, "nd"}, + {3401, "oh"}, + {3501, "ok"}, + {3601, "or"}, + {3701, "pa"}, + {3800, "ri"}, + {3900, "sc"}, + {4001, "sd"}, + {4100, "tn"}, + {4201, "tx"}, + {4301, "ut"}, + {4400, "vt"}, + {4501, "va"}, + {4601, "wa"}, + {4701, "wv"}, + {4801, "wv"}, + {4901, "wy"}, + {5001, "ak"}, + {5101, "hi"}, + {5200, "pr"} +}; + +//---- The origin table for USA State Plane Systems +struct OriginTab83 { + double longitude; + double latitude; + const char *spcs; +}; + +#define ORIGIN_COUNT 148 + +//---- USA State plane coordinate system in IDRISI +static const OriginTab83 SPCS83Origin[] = { + {85.83, 30.50, "SPC83AL1"}, + {87.50, 30.00, "SPC83AL2"}, + {176.00, 51.00, "SPC83AK0"}, + {142.00, 54.00, "SPC83AK2"}, + {146.00, 54.00, "SPC83AK3"}, + {150.00, 54.00, "SPC83AK4"}, + {154.00, 54.00, "SPC83AK5"}, + {158.00, 54.00, "SPC83AK6"}, + {162.00, 54.00, "SPC83AK7"}, + {166.00, 54.00, "SPC83AK8"}, + {170.00, 54.00, "SPC83AK9"}, + {110.17, 31.00, "SPC83AZ1"}, + {111.92, 31.00, "SPC83AZ2"}, + {113.75, 31.00, "SPC83AZ3"}, + {92.00, 34.33, "SPC83AR1"}, + {92.00, 32.67, "SPC83AR2"}, + {122.00, 39.33, "SPC83CA1"}, + {122.00, 37.67, "SPC83CA2"}, + {120.50, 36.50, "SPC83CA3"}, + {119.00, 35.33, "SPC83CA4"}, + {118.00, 33.50, "SPC83CA5"}, + {116.25, 32.17, "SPC83CA6"}, + {105.50, 39.33, "SPC83CO1"}, + {105.50, 37.83, "SPC83CO2"}, + {105.50, 36.67, "SPC83CO3"}, + {72.75, 40.83, "SPC83CT1"}, + {75.42, 38.00, "SPC83DE1"}, + {81.00, 24.33, "SPC83FL1"}, + {82.00, 24.33, "SPC83FL2"}, + {84.50, 29.00, "SPC83FL3"}, + {82.17, 30.00, "SPC83GA1"}, + {84.17, 30.00, "SPC83GA2"}, + {155.50, 18.83, "SPC83HI1"}, + {156.67, 20.33, "SPC83HI2"}, + {158.00, 21.17, "SPC83HI3"}, + {159.50, 21.83, "SPC83HI4"}, + {160.17, 21.67, "SPC83HI5"}, + {112.17, 41.67, "SPC83ID1"}, + {114.00, 41.67, "SPC83ID2"}, + {115.75, 41.67, "SPC83ID3"}, + {88.33, 36.67, "SPC83IL1"}, + {90.17, 36.67, "SPC83IL1"}, + {85.67, 37.50, "SPC83IN1"}, + {87.08, 37.50, "SPC83IN2"}, + {93.50, 41.50, "SPC83IA1"}, + {93.50, 40.00, "SPC83IA1"}, + {98.00, 38.33, "SPC83KS1"}, + {98.50, 36.67, "SPC83KS2"}, + {84.25, 37.50, "SPC83KY1"}, + {85.75, 36.33, "SPC83KY2"}, + {92.50, 30.50, "SPC83LA1"}, + {91.33, 28.50, "SPC83LA2"}, + {91.33, 25.50, "SPC83LA3"}, + {92.50, 30.67, "SPC27LA1"},//NAD27 system + {91.33, 28.67, "SPC27LA2"}, + {91.33, 25.67, "SPC27LA3"},// + {68.50, 43.67, "SPC83ME1"}, + {68.50, 43.83, "SPC27ME1"},//NAD27 + {70.17, 42.83, "SPC83ME2"}, + {77.00, 37.67, "SPC83MD1"},// + {77.00, 37.83, "SPC27MD1"},//NAD27 + {71.50, 41.00, "SPC83MA1"}, + {70.50, 41.00, "SPC83MA2"}, + {87.00, 44.78, "SPC83MI1"}, + {84.37, 43.32, "SPC83MI2"}, + {84.37, 41.50, "SPC83MI3"}, + {84.33, 43.32, "SPC27MI2"},//NAD27 L + {84.33, 41.50, "SPC27MI3"},//NAD27 L + {83.67, 41.50, "SPC27MI4"},//NAD27 TM + {85.75, 41.50, "SPC27MI5"},//NAD27 TM + {88.75, 41.50, "SPC27MI6"},//NAD27 TM + {93.10, 46.50, "SPC83MN1"}, + {94.25, 45.00, "SPC83MN2"}, + {94.00, 43.00, "SPC83MN3"}, + {88.83, 29.50, "SPC83MS1"}, + {90.33, 29.50, "SPC83MS2"}, + {88.83, 29.67, "SPC83MS1"},//NAD27 + {90.33, 30.50, "SPC83MS2"},// + {90.50, 35.83, "SPC83MO1"}, + {92.50, 35.83, "SPC83MO2"}, + {94.50, 36.17, "SPC83MO3"}, + {109.50, 44.25, "SPC83MT1"}, + {109.50, 47.00, "SPC27MT1"},//NAD27 + {109.50, 45.83, "SPC27MT2"}, + {109.50, 44.00, "SPC27MT3"},// + {100.00, 39.83, "SPC83NE1"}, + {115.58, 34.75, "SPC83NV1"}, + {116.67, 34.75, "SPC83NV2"}, + {118.58, 34.75, "SPC83NV3"}, + {71.67, 42.50, "SPC83NH1"}, + {74.50, 38.83, "SPC83NJ1"}, + {74.67, 38.83, "SPC27NJ1"},//NAD27 + {104.33, 31.00, "SPC83NM1"}, + {106.25, 31.00, "SPC83NM2"}, + {107.83, 31.00, "SPC83NM3"}, + {74.50, 38.83, "SPC83NY1"}, + {76.58, 40.00, "SPC83NY2"}, + {78.58, 40.00, "SPC83NY3"}, + {74.00, 40.17, "SPC83NY4"}, + {74.33, 40.00, "SPC27NY1"},//NAD27 + {74.00, 40.50, "SPC27NY4"},// + {79.00, 33.75, "SPC83NC1"}, + {100.50, 47.00, "SPC83ND1"}, + {100.50, 45.67, "SPC83ND2"}, + {82.50, 39.67, "SPC83OH1"}, + {82.50, 38.00, "SPC83OH2"}, + {98.00, 35.00, "SPC83OK1"}, + {98.00, 33.33, "SPC83OK2"}, + {120.50, 43.67, "SPC83OR1"}, + {120.50, 41.67, "SPC83OR2"}, + {77.75, 40.17, "SPC83PA1"}, + {77.75, 39.33, "SPC83PA2"}, + {71.50, 41.08, "SPC83RI1"}, + {81.00, 31.83, "SPC83SC1"}, + {81.00, 33.00, "SPC27SC1"},//NAD27 + {81.00, 31.83, "SPC27SC2"},//NAD27 + {100.00, 43.83, "SPC83SD1"}, + {100.33, 42.33, "SPC83SD2"}, + {86.00, 34.33, "SPC83TN1"}, + {86.00, 34.67, "SPC27TN1"},//NAD27 + {101.50, 34.00, "SPC83TX1"},// + {98.50, 31.67, "SPC83TX2"}, + {100.33, 29.67, "SPC83TX3"}, + {99.00, 27.83, "SPC83TX4"}, + {98.50, 25.67, "SPC83TX5"}, + {97.50, 31.67, "SPC27TX2"},//NAD27 + {111.50, 40.33, "SPC83UT1"}, + {111.50, 38.33, "SPC83UT2"}, + {111.50, 36.67, "SPC83UT3"}, + {72.50, 42.50, "SPC83VT1"}, + {78.50, 37.67, "SPC83VA1"}, + {78.50, 36.33, "SPC83VA2"}, + {120.83, 47.00, "SPC83WA1"}, + {120.50, 45.33, "SPC83WA2"}, + {79.50, 38.50, "SPC83WV1"}, + {81.00, 37.00, "SPC83WV2"}, + {90.00, 45.17, "SPC83WI1"}, + {90.00, 43.83, "SPC83WI2"}, + {90.00, 42.00, "SPC83WI3"}, + {105.17, 40.50, "SPC83WY1"}, + {107.33, 40.50, "SPC83WY2"}, + {108.75, 40.50, "SPC83WY3"}, + {110.08, 40.50, "SPC83WY4"}, + {105.17, 40.67, "SPC27WY1"},//NAD27 + {105.33, 40.67, "SPC27WY2"}, + {108.75, 40.67, "SPC27WY3"}, + {110.08, 40.67, "SPC27WY4"},// + {66.43, 17.83, "SPC83PR1"} +}; + +//Get IDRISI State Plane name by origin +char *GetSpcs(double dfLon, double dfLat); + +//change NAD from 83 to 27 +void NAD83to27( char *pszOutRef, char *pszInRef); + +#define US_STATE_COUNT ( sizeof( aoUSStateTable ) / sizeof( ReferenceTab ) ) + +//----- Get the Code of a US State +int GetStateCode ( const char *pszState ); + +//----- Get the state name of a Code +const char *GetStateName( int nCode ); + +//----- Conversion Table definition +struct ConvertionTab { + const char *pszName; + int nDefaultI; + int nDefaultG; + double dfConv; +}; + +//----- Linear Unit Conversion Table +static const ConvertionTab aoLinearUnitsConv[] = { + {"m", /* 0 */ 0, 1, 1.0}, + {SRS_UL_METER, /* 1 */ 0, 1, 1.0}, + {"meters", /* 2 */ 0, 1, 1.0}, + {"metre", /* 3 */ 0, 1, 1.0}, + + {"ft", /* 4 */ 4, 5, 0.3048}, + {SRS_UL_FOOT, /* 5 */ 4, 5, 0.3048}, + {"feet", /* 6 */ 4, 5, 0.3048}, + {"foot_us", /* 7 */ 4, 5, 0.3048006}, + {"u.s. foot", /* 8 */ 4, 5, 0.3048006}, + + {"mi", /* 9 */ 9, 10, 1612.9}, + {"mile", /* 10 */ 9, 10, 1612.9}, + {"miles", /* 11 */ 9, 10, 1612.9}, + + {"km", /* 12 */ 12, 13, 1000.0}, + {"kilometers", /* 13 */ 12, 13, 1000.0}, + {"kilometer", /* 14 */ 12, 13, 1000.0}, + {"kilometre", /* 15 */ 12, 13, 1000.0}, + + {"deg", /* 16 */ 16, 17, 0.0}, + {SRS_UA_DEGREE, /* 17 */ 16, 17, 0.0}, + {"degrees", /* 18 */ 16, 17, 0.0}, + + {"rad", /* 19 */ 19, 20, 0.0}, + {SRS_UA_RADIAN, /* 20 */ 19, 20, 0.0}, + {"radians", /* 21 */ 19, 20, 0.0} +}; +#define LINEAR_UNITS_COUNT (sizeof(aoLinearUnitsConv) / sizeof(ConvertionTab)) + +//----- Get the index of a given linear unit +int GetUnitIndex( const char *pszUnitName ); + +//----- Get the defaut name +char *GetUnitDefault( const char *pszUnitName, const char *pszToMeter = NULL ); + +//----- Get the "to meter" +int GetToMeterIndex( const char *pszToMeter ); + +//----- CSLSaveCRLF +int SaveAsCRLF(char **papszStrList, const char *pszFname); + +//----- Classes pre-definition: +class IdrisiDataset; +class IdrisiRasterBand; + +// ---------------------------------------------------------------------------- +// Idrisi GDALDataset +// ---------------------------------------------------------------------------- + +class IdrisiDataset : public GDALPamDataset +{ + friend class IdrisiRasterBand; + +private: + VSILFILE *fp; + + char *pszFilename; + char *pszDocFilename; + char **papszRDC; + double adfGeoTransform[6]; + + char *pszProjection; + char **papszCategories; + char *pszUnitType; + //move GeoReference2Wkt() into header file + CPLErr Wkt2GeoReference( const char *pszProjString, + char **pszRefSystem, + char **pszRefUnit ); + +protected: + GDALColorTable *poColorTable; + +public: + IdrisiDataset(); + ~IdrisiDataset(); + + static GDALDataset *Open( GDALOpenInfo *poOpenInfo ); + static GDALDataset *Create( const char *pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + char **papszOptions ); + static GDALDataset *CreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + virtual char **GetFileList(void); + virtual CPLErr GetGeoTransform( double *padfTransform ); + virtual CPLErr SetGeoTransform( double *padfTransform ); + virtual const char *GetProjectionRef( void ); + virtual CPLErr SetProjection( const char *pszProjString ); +}; + +// ---------------------------------------------------------------------------- +// Idrisi GDALPamRasterBand +// ---------------------------------------------------------------------------- + +class IdrisiRasterBand : public GDALPamRasterBand +{ + friend class IdrisiDataset; + + GDALRasterAttributeTable *poDefaultRAT; + +private: + int nRecordSize; + GByte *pabyScanLine; + +public: + IdrisiRasterBand( IdrisiDataset *poDS, + int nBand, + GDALDataType eDataType ); + ~IdrisiRasterBand(); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum( int *pbSuccess = NULL ); + virtual CPLErr IReadBlock( int nBlockXOff, int nBlockYOff, void *pImage ); + virtual CPLErr IWriteBlock( int nBlockXOff, int nBlockYOff, void *pImage ); + virtual GDALColorTable *GetColorTable(); + virtual GDALColorInterp GetColorInterpretation(); + virtual char **GetCategoryNames(); + virtual const char *GetUnitType(); + + virtual CPLErr SetCategoryNames( char **papszCategoryNames ); + virtual CPLErr SetNoDataValue( double dfNoDataValue ); + virtual CPLErr SetColorTable( GDALColorTable *poColorTable ); + virtual CPLErr SetUnitType( const char *pszUnitType ); + virtual CPLErr SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ); + CPLErr SetMinMax( double dfMin, double dfMax ); + virtual GDALRasterAttributeTable *GetDefaultRAT(); + virtual CPLErr SetDefaultRAT( const GDALRasterAttributeTable * ); + + float fMaximum; + float fMinimum; + bool bFirstVal; +}; + +// ------------------------------------------------------------------------ // +// Implementation of IdrisiDataset // +// ------------------------------------------------------------------------ // + +/************************************************************************/ +/* IdrisiDataset() */ +/************************************************************************/ + +IdrisiDataset::IdrisiDataset() +{ + pszFilename = NULL; + fp = NULL; + papszRDC = NULL; + pszDocFilename = NULL; + pszProjection = NULL; + poColorTable = new GDALColorTable(); + papszCategories = NULL; + pszUnitType = NULL; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~IdrisiDataset() */ +/************************************************************************/ + +IdrisiDataset::~IdrisiDataset() +{ + FlushCache(); + + if( papszRDC != NULL ) + { + + int i; + + //int bSuccessMin = FALSE; + //int bSuccessMax = FALSE; + + double dfMin = 0.0; + double dfMax = 0.0; + double dfMean = 0.0; + double dfStdDev = 0.0; + + for( i = 0; i < nBands; i++ ) + { + IdrisiRasterBand *poBand = (IdrisiRasterBand*) GetRasterBand( i + 1 ); + poBand->ComputeStatistics( false, &dfMin, &dfMax, &dfMean, &dfStdDev, NULL, NULL); + /* + dfMin = poBand->GetMinimum( &bSuccessMin ); + dfMax = poBand->GetMaximum( &bSuccessMax ); + + if( ! ( bSuccessMin && bSuccessMax ) ) + { + poBand->GetStatistics( false, true, &dfMin, &dfMax, NULL, NULL ); + } + */ + poBand->SetMinMax( dfMin, dfMax); + } + + if( eAccess == GA_Update ) + { + CSLSetNameValueSeparator( papszRDC, ": " ); + SaveAsCRLF( papszRDC, pszDocFilename ); + } + CSLDestroy( papszRDC ); + } + + if( poColorTable ) + { + delete poColorTable; + } + CPLFree( pszFilename ); + CPLFree( pszDocFilename ); + CPLFree( pszProjection ); + CSLDestroy( papszCategories ); + CPLFree( pszUnitType ); + + if( fp != NULL ) + VSIFCloseL( fp ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *IdrisiDataset::Open( GDALOpenInfo *poOpenInfo ) +{ + if( ( poOpenInfo->fpL == NULL ) || (EQUAL( CPLGetExtension( poOpenInfo->pszFilename ), extRST ) == FALSE ))//modified + return NULL; + + // -------------------------------------------------------------------- + // Check the documentation file .rdc + // -------------------------------------------------------------------- + + const char *pszLDocFilename = CPLResetExtension( poOpenInfo->pszFilename, extRDC ); + + if( ! FileExists( pszLDocFilename ) ) + { + pszLDocFilename = CPLResetExtension( poOpenInfo->pszFilename, extRDCu ); + + if( ! FileExists( pszLDocFilename ) ) + { + return NULL; + } + } + + char **papszLRDC = CSLLoad( pszLDocFilename ); + + CSLSetNameValueSeparator( papszLRDC, ":" ); + + const char *pszVersion = CSLFetchNameValue( papszLRDC, rdcFILE_FORMAT ); + + if( pszVersion == NULL || !EQUAL( pszVersion, rstVERSION ) ) + { + CSLDestroy( papszLRDC ); + return NULL; + } + + // -------------------------------------------------------------------- + // Create a corresponding GDALDataset + // -------------------------------------------------------------------- + + IdrisiDataset *poDS; + + poDS = new IdrisiDataset(); + poDS->eAccess = poOpenInfo->eAccess; + poDS->pszFilename = CPLStrdup( poOpenInfo->pszFilename ); + + if( poOpenInfo->eAccess == GA_ReadOnly ) + { + poDS->fp = VSIFOpenL( poDS->pszFilename, "rb" ); + } + else + { + poDS->fp = VSIFOpenL( poDS->pszFilename, "r+b" ); + } + + if( poDS->fp == NULL ) + { + CSLDestroy( papszLRDC ); + delete poDS; + return NULL; + } + + poDS->pszDocFilename = CPLStrdup( pszLDocFilename ); + poDS->papszRDC = CSLDuplicate( papszLRDC ); + CSLDestroy( papszLRDC ); + + // -------------------------------------------------------------------- + // Load information from rdc + // -------------------------------------------------------------------- + + poDS->nRasterXSize = atoi_nz( CSLFetchNameValue( poDS->papszRDC, rdcCOLUMNS ) ); + poDS->nRasterYSize = atoi_nz( CSLFetchNameValue( poDS->papszRDC, rdcROWS ) ); + + // -------------------------------------------------------------------- + // Create band information + // -------------------------------------------------------------------- + + const char *pszDataType = CSLFetchNameValue( poDS->papszRDC, rdcDATA_TYPE ); + if( pszDataType == NULL ) + { + delete poDS; + return NULL; + } + + if( EQUAL( pszDataType, rstBYTE ) ) + { + poDS->nBands = 1; + poDS->SetBand( 1, new IdrisiRasterBand( poDS, 1, GDT_Byte ) ); + } + else if( EQUAL( pszDataType, rstINTEGER ) ) + { + poDS->nBands = 1; + poDS->SetBand( 1, new IdrisiRasterBand( poDS, 1, GDT_Int16 ) ); + } + else if( EQUAL( pszDataType, rstREAL ) ) + { + poDS->nBands = 1; + poDS->SetBand( 1, new IdrisiRasterBand( poDS, 1, GDT_Float32 ) ); + } + else if( EQUAL( pszDataType, rstRGB24 ) ) + { + poDS->nBands = 3; + poDS->SetBand( 1, new IdrisiRasterBand( poDS, 1, GDT_Byte ) ); + poDS->SetBand( 2, new IdrisiRasterBand( poDS, 2, GDT_Byte ) ); + poDS->SetBand( 3, new IdrisiRasterBand( poDS, 3, GDT_Byte ) ); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unknown data type : %s", pszDataType); + delete poDS; + return NULL; + } + + int i; + for(i=0;inBands;i++) + { + IdrisiRasterBand* band = (IdrisiRasterBand*) poDS->GetRasterBand(i+1); + if (band->pabyScanLine == NULL) + { + delete poDS; + return NULL; + } + } + + // -------------------------------------------------------------------- + // Load the transformation matrix + // -------------------------------------------------------------------- + + const char *pszMinX = CSLFetchNameValue( poDS->papszRDC, rdcMIN_X ); + const char *pszMaxX = CSLFetchNameValue( poDS->papszRDC, rdcMAX_X ); + const char *pszMinY = CSLFetchNameValue( poDS->papszRDC, rdcMIN_Y ); + const char *pszMaxY = CSLFetchNameValue( poDS->papszRDC, rdcMAX_Y ); + const char *pszUnit = CSLFetchNameValue( poDS->papszRDC, rdcUNIT_DIST ); + + if( pszMinX != NULL && strlen( pszMinX ) > 0 && + pszMaxX != NULL && strlen( pszMaxX ) > 0 && + pszMinY != NULL && strlen( pszMinY ) > 0 && + pszMaxY != NULL && strlen( pszMaxY ) > 0 && + pszUnit != NULL && strlen( pszUnit ) > 0 ) + { + double dfMinX, dfMaxX, dfMinY, dfMaxY, dfUnit, dfXPixSz, dfYPixSz; + + dfMinX = CPLAtof_nz( pszMinX ); + dfMaxX = CPLAtof_nz( pszMaxX ); + dfMinY = CPLAtof_nz( pszMinY ); + dfMaxY = CPLAtof_nz( pszMaxY ); + dfUnit = CPLAtof_nz( pszUnit ); + + dfMinX = dfMinX * dfUnit; + dfMaxX = dfMaxX * dfUnit; + dfMinY = dfMinY * dfUnit; + dfMaxY = dfMaxY * dfUnit; + + dfYPixSz = ( dfMinY - dfMaxY ) / poDS->nRasterYSize; + dfXPixSz = ( dfMaxX - dfMinX ) / poDS->nRasterXSize; + + poDS->adfGeoTransform[0] = dfMinX; + poDS->adfGeoTransform[1] = dfXPixSz; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = dfMaxY; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = dfYPixSz; + } + + // -------------------------------------------------------------------- + // Set Color Table in the presence of a smp file + // -------------------------------------------------------------------- + + if( poDS->nBands != 3 ) + { + const char *pszSMPFilename = CPLResetExtension( poDS->pszFilename, extSMP ); + VSILFILE *fpSMP; + if( ( fpSMP = VSIFOpenL( pszSMPFilename, "rb" ) ) != NULL ) + { + int dfMaxValue = atoi_nz( CSLFetchNameValue( poDS->papszRDC, rdcMAX_VALUE ) ); + int nCatCount = atoi_nz( CSLFetchNameValue( poDS->papszRDC, rdcLEGEND_CATS ) ); + if( nCatCount == 0 ) + dfMaxValue = 255; + VSIFSeekL( fpSMP, smpHEADERSIZE, SEEK_SET ); + GDALColorEntry oEntry; + unsigned char aucRGB[3]; + int i = 0; + while( ( VSIFReadL( &aucRGB, sizeof( aucRGB ), 1, fpSMP ) ) &&( i <= dfMaxValue ) ) + { + oEntry.c1 = (short) aucRGB[0]; + oEntry.c2 = (short) aucRGB[1]; + oEntry.c3 = (short) aucRGB[2]; + oEntry.c4 = (short) 255; + poDS->poColorTable->SetColorEntry( i, &oEntry ); + i++; + } + VSIFCloseL( fpSMP ); + } + } + + // -------------------------------------------------------------------- + // Check for Unit Type + // -------------------------------------------------------------------- + + const char *pszValueUnit = CSLFetchNameValue( poDS->papszRDC, rdcVALUE_UNITS ); + + if( pszValueUnit == NULL ) + poDS->pszUnitType = CPLStrdup( "unspecified" ); + else + { + if( EQUALN( pszValueUnit, "meter", 4 ) ) + { + poDS->pszUnitType = CPLStrdup( "m" ); + } + else if( EQUALN( pszValueUnit, "feet", 4 ) ) + { + poDS->pszUnitType = CPLStrdup( "ft" ); + } + else + poDS->pszUnitType = CPLStrdup( pszValueUnit ); + } + + // -------------------------------------------------------------------- + // Check for category names. + // -------------------------------------------------------------------- + + int nCatCount = atoi_nz( CSLFetchNameValue( poDS->papszRDC, rdcLEGEND_CATS ) ); + + if( nCatCount > 0 ) + { + // ---------------------------------------------------------------- + // Sequentialize categories names, from 0 to the last "code n" + // ---------------------------------------------------------------- + + int nCode = 0; + int nCount = 0; + int nLine = -1; + for( int i = 0;( i < CSLCount( poDS->papszRDC ) ) &&( nLine == -1 ); i++ ) + if( EQUALN( poDS->papszRDC[i], rdcLEGEND_CATS, 11 ) ) + nLine = i;//get the line where legend cats is + if( nLine > 0 ) + { + sscanf( poDS->papszRDC[++nLine], rdcCODE_N, &nCode );//asign legend cats to nCode + for( int i = 0;( i < 255 ) &&( nCount < nCatCount ); i++ ) + { + if( i == nCode ) + { + poDS->papszCategories = + CSLAddString( poDS->papszCategories, + CPLParseNameValue( poDS->papszRDC[nLine], NULL ) ); + nCount++; + if( nCount < nCatCount ) + sscanf( poDS->papszRDC[++nLine], rdcCODE_N, &nCode ); + } + else + poDS->papszCategories = CSLAddString( poDS->papszCategories, "" ); + } + } + } + + /* -------------------------------------------------------------------- */ + /* Automatic Generated Color Table */ + /* -------------------------------------------------------------------- */ + + if( poDS->papszCategories != NULL && + ( poDS->poColorTable->GetColorEntryCount() == 0 ) ) + { + int nEntryCount = CSLCount(poDS->papszCategories); + + GDALColorEntry sFromColor; + sFromColor.c1 = (short) ( 255 ); + sFromColor.c2 = (short) ( 0 ); + sFromColor.c3 = (short) ( 0 ); + sFromColor.c4 = (short) ( 255 ); + + GDALColorEntry sToColor; + sToColor.c1 = (short) ( 0 ); + sToColor.c2 = (short) ( 0 ); + sToColor.c3 = (short) ( 255 ); + sToColor.c4 = (short) ( 255 ); + + poDS->poColorTable->CreateColorRamp( + 0, &sFromColor, ( nEntryCount - 1 ), &sToColor ); + } + + /* -------------------------------------------------------------------- */ + /* Initialize any PAM information. */ + /* -------------------------------------------------------------------- */ + + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + + /* -------------------------------------------------------------------- */ + /* Check for external overviews. */ + /* -------------------------------------------------------------------- */ + + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *IdrisiDataset::Create( const char *pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + char **papszOptions ) +{ + (void) papszOptions; + + // -------------------------------------------------------------------- + // Check input options + // -------------------------------------------------------------------- + + if( nBands != 1 && nBands != 3) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create IDRISI dataset with an illegal number of bands(%d)." + " Try again by selecting a specific band if possible. \n", nBands); + return NULL; + } + + if( nBands == 3 && eType != GDT_Byte ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create IDRISI dataset with an unsupported combination " + "of the number of bands(%d) and data type(%s). \n", + nBands, GDALGetDataTypeName( eType ) ); + return NULL; + } + + // ---------------------------------------------------------------- + // Create the header file with minimun information + // ---------------------------------------------------------------- + + const char *pszLDataType; + + switch( eType ) + { + case GDT_Byte: + if( nBands == 1 ) + pszLDataType = rstBYTE; + else + pszLDataType = rstRGB24; + break; + case GDT_Int16: + pszLDataType = rstINTEGER; + break; + case GDT_Float32: + pszLDataType = rstREAL; + break; + //--- process compatible data types + case (GDT_UInt16): + pszLDataType = rstINTEGER; + CPLError( CE_Warning, CPLE_AppDefined, + "This process requires a conversion from %s to signed 16-bit %s, " + "which may cause data loss.\n", + GDALGetDataTypeName( eType ), rstINTEGER); + break; + case GDT_UInt32: + pszLDataType = rstINTEGER; + CPLError( CE_Warning, CPLE_AppDefined, + "This process requires a conversion from %s to signed 16-bit %s, " + "which may cause data loss.\n", + GDALGetDataTypeName( eType ), rstINTEGER); + break; + case GDT_Int32: + pszLDataType = rstINTEGER; + CPLError( CE_Warning, CPLE_AppDefined, + "This process requires a conversion from %s to signed 16-bit %s, " + "which may cause data loss.\n", + GDALGetDataTypeName( eType ), rstINTEGER); + break; + case GDT_Float64: + pszLDataType = rstREAL; + CPLError( CE_Warning, CPLE_AppDefined, + "This process requires a conversion from %s to float 32-bit %s, " + "which may cause data loss.\n", + GDALGetDataTypeName( eType ), rstREAL); + break; + default: + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create IDRISI dataset with an illegal " + "data type(%s).\n", + GDALGetDataTypeName( eType ) ); + return NULL; + }; + + char **papszLRDC; + + papszLRDC = NULL; + papszLRDC = CSLAddNameValue( papszLRDC, rdcFILE_FORMAT, rstVERSION ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcFILE_TITLE, "" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcDATA_TYPE, pszLDataType ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcFILE_TYPE, "binary" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcCOLUMNS, CPLSPrintf( "%d", nXSize ) ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcROWS, CPLSPrintf( "%d", nYSize ) ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcREF_SYSTEM, "plane" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcREF_UNITS, "m" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcUNIT_DIST, "1" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcMIN_X, "0" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcMAX_X, CPLSPrintf( "%d", nXSize ) ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcMIN_Y, "0" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcMAX_Y, CPLSPrintf( "%d", nYSize ) ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcPOSN_ERROR, "unspecified" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcRESOLUTION, "1.0" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcMIN_VALUE, "0" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcMAX_VALUE, "0" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcDISPLAY_MIN, "0" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcDISPLAY_MAX, "0" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcVALUE_UNITS, "unspecified" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcVALUE_ERROR, "unspecified" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcFLAG_VALUE, "none" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcFLAG_DEFN, "none" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcLEGEND_CATS, "0" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcLINEAGES, "" ); + papszLRDC = CSLAddNameValue( papszLRDC, rdcCOMMENTS, "" ); + + const char *pszLDocFilename; + pszLDocFilename = CPLResetExtension( pszFilename, extRDC ); + + CSLSetNameValueSeparator( papszLRDC, ": " ); + SaveAsCRLF( papszLRDC, pszLDocFilename ); + CSLDestroy( papszLRDC ); + + // ---------------------------------------------------------------- + // Create an empty data file + // ---------------------------------------------------------------- + + VSILFILE *fp; + + fp = VSIFOpenL( pszFilename, "wb+" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file %s' failed.\n", pszFilename ); + return NULL; + } + VSIFCloseL( fp ); + + return (IdrisiDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset *IdrisiDataset::CreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, + char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressData ) +{ + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + + // ------------------------------------------------------------------------ + // Check number of bands + // ------------------------------------------------------------------------ + if ( !( poSrcDS->GetRasterCount() == 1 ) && !( poSrcDS->GetRasterCount() == 3 )) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create IDRISI dataset with an illegal number of bands(%d)." + " Try again by selecting a specific band if possible.\n", + poSrcDS->GetRasterCount() ); + return NULL; + + } + if ( ( poSrcDS->GetRasterCount() == 3 ) && + ( ( poSrcDS->GetRasterBand( 1 )->GetRasterDataType() != GDT_Byte ) || + ( poSrcDS->GetRasterBand( 2 )->GetRasterDataType() != GDT_Byte ) || + ( poSrcDS->GetRasterBand( 3 )->GetRasterDataType() != GDT_Byte ) ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create IDRISI dataset with an unsupported " + "data type when there are three bands. Only BYTE allowed.\n" + "Try again by selecting a specific band to convert if possible.\n"); + return NULL; + } + + // ------------------------------------------------------------------------ + // Check Data types + // ------------------------------------------------------------------------ + + int i; + + for( i = 1; i <= poSrcDS->GetRasterCount(); i++ ) + { + GDALDataType eType = poSrcDS->GetRasterBand( i )->GetRasterDataType(); + + if( bStrict ) + { + if( eType != GDT_Byte && + eType != GDT_Int16 && + eType != GDT_Float32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create IDRISI dataset in strict mode " + "with an illegal data type(%s).\n", + GDALGetDataTypeName( eType ) ); + return NULL; + } + } + else + { + if( eType != GDT_Byte && + eType != GDT_Int16 && + eType != GDT_UInt16 && + eType != GDT_UInt32 && + eType != GDT_Int32 && + eType != GDT_Float32 && + eType != GDT_Float64 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create IDRISI dataset with an illegal data type(%s).\n", + GDALGetDataTypeName( eType ) ); + return NULL; + } + } + } + + // -------------------------------------------------------------------- + // Define data type + // -------------------------------------------------------------------- + + GDALRasterBand *poBand = poSrcDS->GetRasterBand( 1 ); + GDALDataType eType = poBand->GetRasterDataType(); + + int bSuccessMin = FALSE; + int bSuccessMax = FALSE; + + double dfMin; + double dfMax; + + dfMin = poBand->GetMinimum( &bSuccessMin ); + dfMax = poBand->GetMaximum( &bSuccessMax ); + + if( ! ( bSuccessMin && bSuccessMax ) ) + { + poBand->GetStatistics( false, true, &dfMin, &dfMax, NULL, NULL ); + } + + if(!( ( eType == GDT_Byte ) || + ( eType == GDT_Int16 ) || + ( eType == GDT_Float32 ) ) ) + { + if( eType == GDT_Float64 ) + { + eType = GDT_Float32; + } + else + { + if( ( dfMin < (double) SHRT_MIN ) || + ( dfMax > (double) SHRT_MAX ) ) + { + eType = GDT_Float32; + } + else + { + eType = GDT_Int16; + } + } + } + + // -------------------------------------------------------------------- + // Create the dataset + // -------------------------------------------------------------------- + + IdrisiDataset *poDS; + + poDS = (IdrisiDataset *) IdrisiDataset::Create( pszFilename, + poSrcDS->GetRasterXSize(), + poSrcDS->GetRasterYSize(), + poSrcDS->GetRasterCount(), + eType, + papszOptions ); + + if( poDS == NULL ) + return NULL; + + // -------------------------------------------------------------------- + // Copy information to the dataset + // -------------------------------------------------------------------- + + double adfGeoTransform[6]; + + if (!EQUAL(poSrcDS->GetProjectionRef(),"")) + { + poSrcDS->GetGeoTransform(adfGeoTransform); + poDS->SetGeoTransform(adfGeoTransform); + poDS->SetProjection( poSrcDS->GetProjectionRef() ); + } + + // -------------------------------------------------------------------- + // Copy information to the raster band(s) + // -------------------------------------------------------------------- + + GDALRasterBand *poSrcBand; + int bHasNoDataValue; + double dfNoDataValue; + + for( i = 1; i <= poDS->nBands; i++ ) + { + poSrcBand = poSrcDS->GetRasterBand( i ); + IdrisiRasterBand* poDstBand = (IdrisiRasterBand*) poDS->GetRasterBand( i ); + + if( poDS->nBands == 1 ) + { + poDstBand->SetUnitType( poSrcBand->GetUnitType() ); + poDstBand->SetColorTable( poSrcBand->GetColorTable() ); + poDstBand->SetCategoryNames( poSrcBand->GetCategoryNames() ); + + const GDALRasterAttributeTable *poRAT = poSrcBand->GetDefaultRAT(); + + if( poRAT != NULL ) + { + poDstBand->SetDefaultRAT( poRAT ); + } + } + + dfMin = poSrcBand->GetMinimum( NULL ); + dfMax = poSrcBand->GetMaximum( NULL ); + poDstBand->SetMinMax( dfMin, dfMax ); + dfNoDataValue = poSrcBand->GetNoDataValue( &bHasNoDataValue ); + if( bHasNoDataValue ) + poDstBand->SetNoDataValue( dfNoDataValue ); + } + + // -------------------------------------------------------------------- + // Copy image data + // -------------------------------------------------------------------- + + GDALDatasetCopyWholeRaster( (GDALDatasetH) poSrcDS, + (GDALDatasetH) poDS, NULL, + pfnProgress, pProgressData ); + + // -------------------------------------------------------------------- + // Finalize + // -------------------------------------------------------------------- + + poDS->FlushCache(); + + return poDS; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **IdrisiDataset::GetFileList() +{ + char **papszFileList = GDALPamDataset::GetFileList(); + const char *pszAssociated; + + // -------------------------------------------------------------------- + // Symbol table file + // -------------------------------------------------------------------- + + pszAssociated = CPLResetExtension( pszFilename, extSMP ); + + if( FileExists( pszAssociated ) ) + { + papszFileList = CSLAddString( papszFileList, pszAssociated ); + } + else + { + pszAssociated = CPLResetExtension( pszFilename, extSMPu ); + + if( FileExists( pszAssociated ) ) + { + papszFileList = CSLAddString( papszFileList, pszAssociated ); + } + } + + // -------------------------------------------------------------------- + // Documentation file + // -------------------------------------------------------------------- + + pszAssociated = CPLResetExtension( pszFilename, extRDC ); + + if( FileExists( pszAssociated ) ) + { + papszFileList = CSLAddString( papszFileList, pszAssociated ); + } + else + { + pszAssociated = CPLResetExtension( pszFilename, extRDCu ); + + if( FileExists( pszAssociated ) ) + { + papszFileList = CSLAddString( papszFileList, pszAssociated ); + } + } + + // -------------------------------------------------------------------- + // Reference file + // -------------------------------------------------------------------- + + pszAssociated = CPLResetExtension( pszFilename, extREF ); + + if( FileExists( pszAssociated ) ) + { + papszFileList = CSLAddString( papszFileList, pszAssociated ); + } + else + { + pszAssociated = CPLResetExtension( pszFilename, extREFu ); + + if( FileExists( pszAssociated ) ) + { + papszFileList = CSLAddString( papszFileList, pszAssociated ); + } + } + + return papszFileList; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr IdrisiDataset::GetGeoTransform( double * padfTransform ) +{ + if( GDALPamDataset::GetGeoTransform( padfTransform ) != CE_None ) + { + memcpy( padfTransform, adfGeoTransform, sizeof( double ) * 6 ); + /* + if( adfGeoTransform[0] == 0.0 + && adfGeoTransform[1] == 1.0 + && adfGeoTransform[2] == 0.0 + && adfGeoTransform[3] == 0.0 + && adfGeoTransform[4] == 0.0 + && adfGeoTransform[5] == 1.0 ) + return CE_Failure; + */ + } + + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr IdrisiDataset::SetGeoTransform( double * padfTransform ) +{ + if( padfTransform[2] != 0.0 || padfTransform[4] != 0.0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to set rotated geotransform on Idrisi Raster file.\n" + "Idrisi Raster does not support rotation.\n" ); + return CE_Failure; + } + + // -------------------------------------------------------------------- + // Update the .rdc file + // -------------------------------------------------------------------- + + double dfMinX, dfMaxX, dfMinY, dfMaxY, dfXPixSz, dfYPixSz; + + dfXPixSz = padfTransform[1]; + dfYPixSz = padfTransform[5]; + dfMinX = padfTransform[0]; + dfMaxX = ( dfXPixSz * nRasterXSize ) + dfMinX; + + if( dfYPixSz < 0 ) + { + dfMaxY = padfTransform[3]; + dfMinY = ( dfYPixSz * nRasterYSize ) + padfTransform[3]; + } + else + { + dfMaxY = ( dfYPixSz * nRasterYSize ) + padfTransform[3]; + dfMinY = padfTransform[3]; + } + + papszRDC = CSLSetNameValue( papszRDC, rdcMIN_X, CPLSPrintf( "%.7f", dfMinX ) ); + papszRDC = CSLSetNameValue( papszRDC, rdcMAX_X, CPLSPrintf( "%.7f", dfMaxX ) ); + papszRDC = CSLSetNameValue( papszRDC, rdcMIN_Y, CPLSPrintf( "%.7f", dfMinY ) ); + papszRDC = CSLSetNameValue( papszRDC, rdcMAX_Y, CPLSPrintf( "%.7f", dfMaxY ) ); + papszRDC = CSLSetNameValue( papszRDC, rdcRESOLUTION, CPLSPrintf( "%.7f", fabs( dfYPixSz ) ) ); + + // -------------------------------------------------------------------- + // Update the Dataset attribute + // -------------------------------------------------------------------- + + memcpy( adfGeoTransform, padfTransform, sizeof( double ) * 6 ); + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *IdrisiDataset::GetProjectionRef( void ) +{ + const char *pszPamSRS = GDALPamDataset::GetProjectionRef(); + + if( pszPamSRS != NULL && strlen( pszPamSRS ) > 0 ) + return pszPamSRS; + + if( pszProjection == NULL ) + { + const char *pszRefSystem = CSLFetchNameValue( papszRDC, rdcREF_SYSTEM ); + const char *pszRefUnit = CSLFetchNameValue( papszRDC, rdcREF_UNITS ); + + if (pszRefSystem != NULL && pszRefUnit != NULL) + IdrisiGeoReference2Wkt( pszFilename, pszRefSystem, pszRefUnit, &pszProjection ); + else + pszProjection = CPLStrdup(""); + } + return pszProjection; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr IdrisiDataset::SetProjection( const char *pszProjString ) +{ + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszProjString ); + CPLErr eResult = CE_None; + + char *pszRefSystem = NULL; + char *pszRefUnit = NULL; + + eResult = Wkt2GeoReference( pszProjString, &pszRefSystem, &pszRefUnit ); + + papszRDC = CSLSetNameValue( papszRDC, rdcREF_SYSTEM, pszRefSystem ); + papszRDC = CSLSetNameValue( papszRDC, rdcREF_UNITS, pszRefUnit ); + + CPLFree( pszRefSystem ); + CPLFree( pszRefUnit ); + + return eResult; +} + +/************************************************************************/ +/* IdrisiRasterBand() */ +/************************************************************************/ + +IdrisiRasterBand::IdrisiRasterBand( IdrisiDataset *poDS, + int nBand, + GDALDataType eDataType ) +{ + this->poDS = poDS; + this->nBand = nBand; + this->eDataType = eDataType; + this->poDefaultRAT = NULL; + this->fMinimum = 0.0; + this->fMaximum = 0.0; + this->bFirstVal = true; + + // -------------------------------------------------------------------- + // Set Dimension + // -------------------------------------------------------------------- + + nBlockYSize = 1; + nBlockXSize = poDS->GetRasterXSize(); + + // -------------------------------------------------------------------- + // Get ready for reading and writing + // -------------------------------------------------------------------- + + nRecordSize = poDS->GetRasterXSize() * GDALGetDataTypeSize( eDataType ) / 8; + + pabyScanLine = (GByte*) VSIMalloc2( nRecordSize, poDS->nBands ); + + if( pabyScanLine == NULL ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "IdrisiRasterBand::IdrisiRasterBand : Out of memory (nRasterXSize = %d)", + poDS->GetRasterXSize()); + } + + nRecordSize *= poDS->nBands; +} + +/************************************************************************/ +/* ~IdrisiRasterBand() */ +/************************************************************************/ + +IdrisiRasterBand::~IdrisiRasterBand() +{ + CPLFree( pabyScanLine ); + + if( poDefaultRAT ) + { + delete poDefaultRAT; + } +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr IdrisiRasterBand::IReadBlock( int nBlockXOff, + int nBlockYOff, + void *pImage ) +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + if( VSIFSeekL( poGDS->fp, + vsi_l_offset(nRecordSize) * nBlockYOff, SEEK_SET ) < 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't seek(%s) block with X offset %d and Y offset %d.\n%s", + poGDS->pszFilename, nBlockXOff, nBlockYOff, VSIStrerror( errno ) ); + return CE_Failure; + } + + if( (int) VSIFReadL( pabyScanLine, 1, nRecordSize, poGDS->fp ) < nRecordSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't read(%s) block with X offset %d and Y offset %d.\n%s", + poGDS->pszFilename, nBlockXOff, nBlockYOff, VSIStrerror( errno ) ); + return CE_Failure; + } + + if( poGDS->nBands == 3 ) + { + int i, j; + for( i = 0, j = ( 3 - nBand ); i < nBlockXSize; i++, j += 3 ) + { + ( (GByte*) pImage )[i] = pabyScanLine[j]; + } + } + else + { + memcpy( pImage, pabyScanLine, nRecordSize ); + } + +#ifdef CPL_MSB + if( eDataType == GDT_Float32 ) + GDALSwapWords( pImage, 4, nBlockXSize * nBlockYSize, 4 ); +#endif + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr IdrisiRasterBand::IWriteBlock( int nBlockXOff, + int nBlockYOff, + void *pImage ) +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + +#ifdef CPL_MSB + // Swap in input buffer if needed. + if( eDataType == GDT_Float32 ) + GDALSwapWords( pImage, 4, nBlockXSize * nBlockYSize, 4 ); +#endif + + if( poGDS->nBands == 1 ) + { + memcpy( pabyScanLine, pImage, nRecordSize ); + } + else + { + if( nBand > 1 ) + { + VSIFSeekL( poGDS->fp, + vsi_l_offset(nRecordSize) * nBlockYOff, SEEK_SET ); + VSIFReadL( pabyScanLine, 1, nRecordSize, poGDS->fp ); + } + int i, j; + for( i = 0, j = ( 3 - nBand ); i < nBlockXSize; i++, j += 3 ) + { + pabyScanLine[j] = ( (GByte *) pImage )[i]; + } + } + +#ifdef CPL_MSB + // Swap input buffer back to original form. + if( eDataType == GDT_Float32 ) + GDALSwapWords( pImage, 4, nBlockXSize * nBlockYSize, 4 ); +#endif + + VSIFSeekL( poGDS->fp, vsi_l_offset(nRecordSize) * nBlockYOff, SEEK_SET ); + + if( (int) VSIFWriteL( pabyScanLine, 1, nRecordSize, poGDS->fp ) < nRecordSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't write(%s) block with X offset %d and Y offset %d.\n%s", + poGDS->pszFilename, nBlockXOff, nBlockYOff, VSIStrerror( errno ) ); + return CE_Failure; + } + + int bHasNoDataValue = FALSE; + float fNoDataValue = (float) GetNoDataValue(&bHasNoDataValue); + + // -------------------------------------------------------------------- + // Search for the minimum and maximum values + // -------------------------------------------------------------------- + + int i, j; + + if( eDataType == GDT_Float32 ) + { + for( i = 0; i < nBlockXSize; i++ ) + { + float fVal = ((float*) pabyScanLine)[i]; //this is fine + if( !bHasNoDataValue || fVal != fNoDataValue ) + { + if( bFirstVal ) + { + fMinimum = fMaximum = fVal; + bFirstVal = false; + } + else + { + if( fVal < fMinimum) fMinimum = fVal; + if( fVal > fMaximum) fMaximum = fVal; + } + } + } + } + else if( eDataType == GDT_Int16 ) + { + for( i = 0; i < nBlockXSize; i++ ) + { + float fVal = (float) ((GInt16*) pabyScanLine)[i]; + if( !bHasNoDataValue || fVal != fNoDataValue ) + { + if( bFirstVal ) + { + fMinimum = fMaximum = fVal; + bFirstVal = false; + } + else + { + if( fVal < fMinimum) fMinimum = fVal; + if( fVal > fMaximum) fMaximum = fVal; + } + } + } + } + else if( poGDS->nBands == 1 ) + { + for( i = 0; i < nBlockXSize; i++ ) + { + float fVal = (float) ((GByte*) pabyScanLine)[i]; + if( !bHasNoDataValue || fVal != fNoDataValue ) + { + if( bFirstVal ) + { + fMinimum = fMaximum = fVal; + bFirstVal = false; + } + else + { + if( fVal < fMinimum) fMinimum = fVal;//I don't change this part, keep it as it is + if( fVal > fMaximum) fMaximum = fVal; + } + } + } + } + else + { + for( i = 0, j = ( 3 - nBand ); i < nBlockXSize; i++, j += 3 ) + { + float fVal = (float) ((GByte*) pabyScanLine)[j]; + if( !bHasNoDataValue || fVal != fNoDataValue ) + { + if( bFirstVal ) + { + fMinimum = fMaximum = fVal; + bFirstVal = false; + } + else + { + if( fVal < fMinimum) fMinimum = fVal; + if( fVal > fMaximum) fMaximum = fVal; + } + } + } + } + + return CE_None; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double IdrisiRasterBand::GetMinimum( int *pbSuccess ) +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + if (CSLFetchNameValue( poGDS->papszRDC, rdcMIN_VALUE ) == NULL) + return GDALPamRasterBand::GetMinimum(pbSuccess); + + double adfMinValue[3]; + CPLsscanf( CSLFetchNameValue( poGDS->papszRDC, rdcMIN_VALUE ), "%lf %lf %lf", + &adfMinValue[0], &adfMinValue[1], &adfMinValue[2] ); + + if( pbSuccess ) + { + *pbSuccess = true; + } + + return adfMinValue[this->nBand - 1]; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double IdrisiRasterBand::GetMaximum( int *pbSuccess ) +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + if (CSLFetchNameValue( poGDS->papszRDC, rdcMAX_VALUE ) == NULL) + return GDALPamRasterBand::GetMaximum(pbSuccess); + + double adfMaxValue[3]; + CPLsscanf( CSLFetchNameValue( poGDS->papszRDC, rdcMAX_VALUE ), "%lf %lf %lf", + &adfMaxValue[0], &adfMaxValue[1], &adfMaxValue[2] ); + + if( pbSuccess ) + { + *pbSuccess = true; + } + + return adfMaxValue[this->nBand - 1]; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double IdrisiRasterBand::GetNoDataValue( int *pbSuccess ) +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + double dfNoData; + const char *pszFlagDefn; + + if( CSLFetchNameValue( poGDS->papszRDC, rdcFLAG_DEFN ) != NULL ) + pszFlagDefn = CSLFetchNameValue( poGDS->papszRDC, rdcFLAG_DEFN ); + else if( CSLFetchNameValue( poGDS->papszRDC, rdcFLAG_DEFN2 ) != NULL ) + pszFlagDefn = CSLFetchNameValue( poGDS->papszRDC, rdcFLAG_DEFN2 ); + else + pszFlagDefn = CPLStrdup( "none" ); + + // ------------------------------------------------------------------------ + // If Flag_Def is not "none", Flag_Value means "background" + // or "missing data" + // ------------------------------------------------------------------------ + + if( ! EQUAL( pszFlagDefn, "none" ) ) + { + dfNoData = CPLAtof_nz( CSLFetchNameValue( poGDS->papszRDC, rdcFLAG_VALUE ) ); + if( pbSuccess ) + *pbSuccess = TRUE; + } + else + { + dfNoData = -9999.0; /* this value should be ignored */ + if( pbSuccess ) + *pbSuccess = FALSE; + } + + return dfNoData; +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr IdrisiRasterBand::SetNoDataValue( double dfNoDataValue ) +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + poGDS->papszRDC = + CSLSetNameValue( poGDS->papszRDC, rdcFLAG_VALUE, CPLSPrintf( "%.7g", dfNoDataValue ) ); + poGDS->papszRDC = + CSLSetNameValue( poGDS->papszRDC, rdcFLAG_DEFN, "missing data" ); + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp IdrisiRasterBand::GetColorInterpretation() +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + if( poGDS->nBands == 3 ) + { + switch( nBand ) + { + case 1: return GCI_BlueBand; + case 2: return GCI_GreenBand; + case 3: return GCI_RedBand; + } + } + else if( poGDS->poColorTable->GetColorEntryCount() > 0 ) + { + return GCI_PaletteIndex; + } + return GCI_GrayIndex; +} + +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +char **IdrisiRasterBand::GetCategoryNames() +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + return poGDS->papszCategories; +} + +/************************************************************************/ +/* SetCategoryNames() */ +/************************************************************************/ + +CPLErr IdrisiRasterBand::SetCategoryNames( char **papszCategoryNames ) +{ + int nCatCount = CSLCount( papszCategoryNames ); + + if( nCatCount == 0 ) + return CE_None; + + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + CSLDestroy( poGDS->papszCategories ); + poGDS->papszCategories = CSLDuplicate( papszCategoryNames ); + + // ------------------------------------------------------ + // Search for the "Legend cats : N" line + // ------------------------------------------------------ + + int i, nLine = -1; + for( i = 0;( i < CSLCount( poGDS->papszRDC ) ) &&( nLine == -1 ); i++ ) + if( EQUALN( poGDS->papszRDC[i], rdcLEGEND_CATS, 12 ) ) + nLine = i; + + if( nLine < 0 ) + return CE_None; + + int nCount = atoi_nz( CSLFetchNameValue( poGDS->papszRDC, rdcLEGEND_CATS ) ); + + // ------------------------------------------------------ + // Delte old instance of the categoty names + // ------------------------------------------------------ + + if( nCount > 0 ) + poGDS->papszRDC = CSLRemoveStrings( poGDS->papszRDC, nLine + 1, nCount, NULL ); + + nCount = 0; + + for( i = 0; i < nCatCount; i++ ) + { + if( ( strlen( papszCategoryNames[i] ) > 0 ) ) + { + poGDS->papszRDC = CSLInsertString( poGDS->papszRDC,( nLine + nCount + 1 ), + CPLSPrintf( "%s:%s", CPLSPrintf( rdcCODE_N, i ), papszCategoryNames[i] ) ); + nCount++; + } + } + + poGDS->papszRDC = CSLSetNameValue( poGDS->papszRDC, rdcLEGEND_CATS, CPLSPrintf( "%d", nCount ) );//this is fine + + return CE_None; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *IdrisiRasterBand::GetColorTable() +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + if( poGDS->poColorTable->GetColorEntryCount() == 0 ) + { + return NULL; + } + else + { + return poGDS->poColorTable; + } +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr IdrisiRasterBand::SetColorTable( GDALColorTable *poColorTable ) +{ + if( poColorTable == NULL ) + { + return CE_None; + } + + if( poColorTable->GetColorEntryCount() == 0 ) + { + return CE_None; + } + + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + delete poGDS->poColorTable; + + poGDS->poColorTable = poColorTable->Clone(); + + const char *pszSMPFilename; + pszSMPFilename = CPLResetExtension( poGDS->pszFilename, extSMP ); + VSILFILE *fpSMP; + + if( ( fpSMP = VSIFOpenL( pszSMPFilename, "w" ) ) != NULL ) + { + VSIFWriteL( "[Idrisi]", 8, 1, fpSMP ); + GByte nPlatform = 1; VSIFWriteL( &nPlatform, 1, 1, fpSMP ); + GByte nVersion = 11; VSIFWriteL( &nVersion, 1, 1, fpSMP ); + GByte nDepth = 8; VSIFWriteL( &nDepth, 1, 1, fpSMP ); + GByte nHeadSz = 18; VSIFWriteL( &nHeadSz, 1, 1, fpSMP ); + GUInt16 nCount = 255; VSIFWriteL( &nCount, 2, 1, fpSMP ); + GUInt16 nMix = 0; VSIFWriteL( &nMix, 2, 1, fpSMP ); + GUInt16 nMax = 255; VSIFWriteL( &nMax, 2, 1, fpSMP ); + + GDALColorEntry oEntry; + GByte aucRGB[3]; + int i; + + for( i = 0; i < poColorTable->GetColorEntryCount(); i++ ) + { + poColorTable->GetColorEntryAsRGB( i, &oEntry ); + aucRGB[0] = (GByte) oEntry.c1; + aucRGB[1] = (GByte) oEntry.c2; + aucRGB[2] = (GByte) oEntry.c3; + VSIFWriteL( &aucRGB, 3, 1, fpSMP ); + } + /* smp files always have 256 occurences */ + for( i = poColorTable->GetColorEntryCount(); i <= 255; i++ ) + { + poColorTable->GetColorEntryAsRGB( i, &oEntry ); + aucRGB[0] = (GByte) 0; + aucRGB[1] = (GByte) 0; + aucRGB[2] = (GByte) 0; + VSIFWriteL( &aucRGB, 3, 1, fpSMP ); + } + VSIFCloseL( fpSMP ); + } + + return CE_None; +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +const char *IdrisiRasterBand::GetUnitType() +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + return poGDS->pszUnitType; +} + +/************************************************************************/ +/* SetUnitType() */ +/************************************************************************/ + +CPLErr IdrisiRasterBand::SetUnitType( const char *pszUnitType ) +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + if( strlen( pszUnitType ) == 0 ) + { + poGDS->papszRDC = + CSLSetNameValue( poGDS->papszRDC, rdcVALUE_UNITS, "unspecified" ); + } + else + { + poGDS->papszRDC = + CSLSetNameValue( poGDS->papszRDC, rdcVALUE_UNITS, pszUnitType ); + } + + return CE_None; +} + +/************************************************************************/ +/* SetMinMax() */ +/************************************************************************/ + +CPLErr IdrisiRasterBand::SetMinMax( double dfMin, double dfMax ) +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + fMinimum = (float)dfMin; + fMaximum = (float)dfMax; + + double adfMin[3] = {0.0, 0.0, 0.0}; + double adfMax[3] = {0.0, 0.0, 0.0}; + + if (CSLFetchNameValue( poGDS->papszRDC, rdcMIN_VALUE ) != NULL) + CPLsscanf( CSLFetchNameValue( poGDS->papszRDC, rdcMIN_VALUE ), "%lf %lf %lf", &adfMin[0], &adfMin[1], &adfMin[2] ); + if (CSLFetchNameValue( poGDS->papszRDC, rdcMAX_VALUE ) != NULL) + CPLsscanf( CSLFetchNameValue( poGDS->papszRDC, rdcMAX_VALUE ), "%lf %lf %lf", &adfMax[0], &adfMax[1], &adfMax[2] ); + + adfMin[nBand - 1] = dfMin; + adfMax[nBand - 1] = dfMax; + + if( poGDS->nBands == 3 ) + { + poGDS->papszRDC = + CSLSetNameValue( poGDS->papszRDC, rdcMIN_VALUE, CPLSPrintf( "%.8g %.8g %.8g", adfMin[0], adfMin[1], adfMin[2] ) ); + poGDS->papszRDC = + CSLSetNameValue( poGDS->papszRDC, rdcMAX_VALUE, CPLSPrintf( "%.8g %.8g %.8g", adfMax[0], adfMax[1], adfMax[2] ) ); + poGDS->papszRDC = + CSLSetNameValue( poGDS->papszRDC, rdcDISPLAY_MIN, CPLSPrintf( "%.8g %.8g %.8g", adfMin[0], adfMin[1], adfMin[2] ) ); + poGDS->papszRDC = + CSLSetNameValue( poGDS->papszRDC, rdcDISPLAY_MAX, CPLSPrintf( "%.8g %.8g %.8g", adfMax[0], adfMax[1], adfMax[2] ) ); + } + else + { + poGDS->papszRDC = + CSLSetNameValue( poGDS->papszRDC, rdcMIN_VALUE, CPLSPrintf( "%.8g", adfMin[0] ) ); + poGDS->papszRDC = + CSLSetNameValue( poGDS->papszRDC, rdcMAX_VALUE, CPLSPrintf( "%.8g", adfMax[0] ) ); + poGDS->papszRDC = + CSLSetNameValue( poGDS->papszRDC, rdcDISPLAY_MIN, CPLSPrintf( "%.8g", adfMin[0] ) ); + poGDS->papszRDC = + CSLSetNameValue( poGDS->papszRDC, rdcDISPLAY_MAX, CPLSPrintf( "%.8g", adfMax[0] ) ); + } + + return CE_None; +} + +/************************************************************************/ +/* SetStatistics() */ +/************************************************************************/ + +CPLErr IdrisiRasterBand::SetStatistics( double dfMin, double dfMax, double dfMean, double dfStdDev ) +{ + SetMinMax(dfMin, dfMax); + + return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev); +} + +/************************************************************************/ +/* SetDefaultRAT() */ +/************************************************************************/ + +CPLErr IdrisiRasterBand::SetDefaultRAT( const GDALRasterAttributeTable *poRAT ) +{ + if( ! poRAT ) + { + return CE_Failure; + } + + // ---------------------------------------------------------- + // Get field indecies + // ---------------------------------------------------------- + + int iValue = -1; + int iRed = poRAT->GetColOfUsage( GFU_Red ); + int iGreen = poRAT->GetColOfUsage( GFU_Green ); + int iBlue = poRAT->GetColOfUsage( GFU_Blue ); + int iName = -1; + int i; + + GDALColorTable *poCT = NULL; + char **papszNames = NULL; + + int nFact = 1; + + // ---------------------------------------------------------- + // Seek for "Value" field index (AGIS standards field name) + // ---------------------------------------------------------- + + if( GetColorTable() == 0 || GetColorTable()->GetColorEntryCount() == 0 ) + { + for( i = 0; i < poRAT->GetColumnCount(); i++ ) + { + if EQUALN( "Value", poRAT->GetNameOfCol( i ), 5 ) + { + iValue = i; + break; + } + } + + if( iRed != -1 && iGreen != -1 && iBlue != -1 ) + { + poCT = new GDALColorTable(); + nFact = poRAT->GetTypeOfCol( iRed ) == GFT_Real ? 255 : 1; + } + } + + // ---------------------------------------------------------- + // Seek for Name field index + // ---------------------------------------------------------- + + if( GetCategoryNames() == 0 || CSLCount( GetCategoryNames() ) == 0 ) + { + iName = poRAT->GetColOfUsage( GFU_Name ); + if( iName == -1 ) + { + for( i = 0; i < poRAT->GetColumnCount(); i++ ) + { + if EQUALN( "Class_Name", poRAT->GetNameOfCol( i ), 10 ) + { + iName = i; + break; + } + else if EQUALN( "Categor", poRAT->GetNameOfCol( i ), 7 ) + { + iName = i; + break; + } + else if EQUALN( "Name", poRAT->GetNameOfCol( i ), 4 ) + { + iName = i; + break; + } + } + } + + /* if still can't find it use the first String column */ + + if( iName == -1 ) + { + for( i = 0; i < poRAT->GetColumnCount(); i++ ) + { + if( poRAT->GetTypeOfCol( i ) == GFT_String ) + { + iName = i; + break; + } + } + } + + // ---------------------------------------------------------- + // Incomplete Attribute Table; + // ---------------------------------------------------------- + + if( iName == -1 ) + { + iName = iValue; + } + } + + // ---------------------------------------------------------- + // Initialization + // ---------------------------------------------------------- + + double dRed = 0.0; + double dGreen = 0.0; + double dBlue = 0.0; + + // ---------------------------------------------------------- + // Load values + // ---------------------------------------------------------- + + GDALColorEntry sColor; + int iEntry = 0; + int iOut = 0; + int nEntryCount = poRAT->GetRowCount(); + int nValue = 0; + + if( iValue != -1 ) + { + nValue = poRAT->GetValueAsInt( iEntry, iValue ); + } + + for( iOut = 0; iOut < 65535 && ( iEntry < nEntryCount ); iOut++ ) + { + if( iOut == nValue ) + { + if( poCT ) + { + dRed = poRAT->GetValueAsDouble( iEntry, iRed ); + dGreen = poRAT->GetValueAsDouble( iEntry, iGreen ); + dBlue = poRAT->GetValueAsDouble( iEntry, iBlue ); + sColor.c1 = (short) ( dRed * nFact ); + sColor.c2 = (short) ( dGreen * nFact ); + sColor.c3 = (short) ( dBlue * nFact ); + sColor.c4 = (short) ( 255 / nFact ); + poCT->SetColorEntry( iEntry, &sColor ); + } + + if( iName != -1 ) + { + papszNames = CSLAddString( papszNames, + poRAT->GetValueAsString( iEntry, iName ) ); + } + + /* Advance on the table */ + + if( ( ++iEntry ) < nEntryCount ) + { + if( iValue != -1 ) + nValue = poRAT->GetValueAsInt( iEntry, iValue ); + else + nValue = iEntry; + } + } + else if( iOut < nValue ) + { + if( poCT ) + { + sColor.c1 = (short) 0; + sColor.c2 = (short) 0; + sColor.c3 = (short) 0; + sColor.c4 = (short) 255; + poCT->SetColorEntry( iEntry, &sColor ); + } + + if( iName != -1 ) + papszNames = CSLAddString( papszNames, "" ); + } + } + + // ---------------------------------------------------------- + // Set Color Table + // ---------------------------------------------------------- + + if( poCT ) + { + SetColorTable( poCT ); + delete poCT; + } + + // ---------------------------------------------------------- + // Update Category Names + // ---------------------------------------------------------- + + if( papszNames ) + { + SetCategoryNames( papszNames ); + CSLDestroy( papszNames ); + } + + // ---------------------------------------------------------- + // Update Attribute Table + // ---------------------------------------------------------- + + if( poDefaultRAT ) + { + delete poDefaultRAT; + } + + poDefaultRAT = poRAT->Clone(); + + return CE_None; +} + +/************************************************************************/ +/* GetDefaultRAT() */ +/************************************************************************/ + +GDALRasterAttributeTable *IdrisiRasterBand::GetDefaultRAT() +{ + IdrisiDataset *poGDS = (IdrisiDataset *) poDS; + + if( poGDS->papszCategories == NULL ) + { + return NULL; + } + + bool bHasColorTable = poGDS->poColorTable->GetColorEntryCount() > 0; + + // ---------------------------------------------------------- + // Create the bands Attribute Table + // ---------------------------------------------------------- + + if( poDefaultRAT ) + { + delete poDefaultRAT; + } + + poDefaultRAT = new GDALDefaultRasterAttributeTable(); + + // ---------------------------------------------------------- + // Create (Value, Red, Green, Blue, Alpha, Class_Name) fields + // ---------------------------------------------------------- + + poDefaultRAT->CreateColumn( "Value", GFT_Integer, GFU_Generic ); + poDefaultRAT->CreateColumn( "Value_1", GFT_Integer, GFU_MinMax ); + + if( bHasColorTable ) + { + poDefaultRAT->CreateColumn( "Red", GFT_Integer, GFU_Red ); + poDefaultRAT->CreateColumn( "Green", GFT_Integer, GFU_Green ); + poDefaultRAT->CreateColumn( "Blue", GFT_Integer, GFU_Blue ); + poDefaultRAT->CreateColumn( "Alpha", GFT_Integer, GFU_Alpha ); + } + poDefaultRAT->CreateColumn( "Class_name", GFT_String, GFU_Name ); + + // ---------------------------------------------------------- + // Loop throught the Category Names + // ---------------------------------------------------------- + + GDALColorEntry sEntry; + int iName = poDefaultRAT->GetColOfUsage( GFU_Name ); + int nEntryCount = CSLCount( poGDS->papszCategories ); + int iEntry = 0; + int iRows = 0; + + for( iEntry = 0; iEntry < nEntryCount; iEntry++ ) + { + if EQUAL( poGDS->papszCategories[iEntry], "" ) + { + continue; // Eliminate the empty ones + } + poDefaultRAT->SetRowCount( poDefaultRAT->GetRowCount() + 1 ); + poDefaultRAT->SetValue( iRows, 0, iEntry ); + poDefaultRAT->SetValue( iRows, 1, iEntry ); + if( bHasColorTable ) + { + poGDS->poColorTable->GetColorEntryAsRGB( iEntry, &sEntry ); + poDefaultRAT->SetValue( iRows, 2, sEntry.c1 ); + poDefaultRAT->SetValue( iRows, 3, sEntry.c2 ); + poDefaultRAT->SetValue( iRows, 4, sEntry.c3 ); + poDefaultRAT->SetValue( iRows, 5, sEntry.c4 ); + } + poDefaultRAT->SetValue( iRows, iName, poGDS->papszCategories[iEntry] ); + iRows++; + } + + return poDefaultRAT; +} + +/************************************************************************/ +/* IdrisiGeoReference2Wkt() */ +/************************************************************************/ + +/*** +* Converts Idrisi geographic reference information to OpenGIS WKT. +* +* The Idrisi metadata file contain two fields that describe the +* geographic reference, RefSystem and RefUnit. +* +* RefSystem can contains the world "plane" or the name of a georeference +* file .ref that details the geographic reference +* system( coordinate system and projection parameters ). RefUnits +* indicates the unit of the image bounds. +* +* The georeference files are generally located in the product installation +* folder $IDRISIDIR\Georef, but they are first looked for in the same +* folder as the data file. +* +* If a Reference system names can be recognized by a name convention +* it will be interpreted without the need to read the georeference file. +* That includes "latlong" and all the UTM and State Plane zones. +* +* RefSystem "latlong" means that the data is not project and the coordinate +* system is WGS84. RefSystem "plane" means that the there is no coordinate +* system but the it is possible to calculate areas and distance by looking +* at the RefUnits. +* +* If the environment variable IDRISIDIR is not set and the georeference file +* need to be read then the projection string will result as unknown. +***/ + +CPLErr IdrisiGeoReference2Wkt( const char* pszFilename, + const char *pszRefSystem, + const char *pszRefUnits, + char **ppszProjString ) +{ + OGRSpatialReference oSRS; + + *ppszProjString = NULL; + + // --------------------------------------------------------- + // Plane + // --------------------------------------------------------- + + if( EQUAL( pszRefSystem, rstPLANE ) ) + { + oSRS.SetLocalCS( "Plane" ); + int nUnit = GetUnitIndex( pszRefUnits ); + if( nUnit > -1 ) + { + int nDeft = aoLinearUnitsConv[nUnit].nDefaultG; + oSRS.SetLinearUnits( aoLinearUnitsConv[nDeft].pszName, + aoLinearUnitsConv[nDeft].dfConv ); + } + oSRS.exportToWkt( ppszProjString ); + return CE_None; + } + + // --------------------------------------------------------- + // Latlong + // --------------------------------------------------------- + + if( EQUAL( pszRefSystem, rstLATLONG ) || + EQUAL( pszRefSystem, rstLATLONG2 ) ) + { + oSRS.SetWellKnownGeogCS( "WGS84" ); + oSRS.exportToWkt( ppszProjString ); + return CE_None; + } + + // --------------------------------------------------------- + // Prepare for scanning in lower case + // --------------------------------------------------------- + + char *pszRefSystemLower; + pszRefSystemLower = CPLStrdup( pszRefSystem ); + CPLStrlwr( pszRefSystemLower ); + + // --------------------------------------------------------- + // UTM naming convention( ex.: utm-30n ) + // --------------------------------------------------------- + + if( EQUALN( pszRefSystem, rstUTM, 3 ) ) + { + int nZone; + char cNorth; + sscanf( pszRefSystemLower, rstUTM, &nZone, &cNorth ); + oSRS.SetWellKnownGeogCS( "WGS84" ); + oSRS.SetUTM( nZone,( cNorth == 'n' ) ); + oSRS.exportToWkt( ppszProjString ); + CPLFree( pszRefSystemLower ); + return CE_None; + } + + // --------------------------------------------------------- + // State Plane naming convention( ex.: spc83ma1 ) + // --------------------------------------------------------- + + if( EQUALN( pszRefSystem, rstSPC, 3 ) ) + { + int nNAD; + int nZone; + char szState[3]; + sscanf( pszRefSystemLower, rstSPC, &nNAD, szState, &nZone ); + int nSPCode = GetStateCode( szState ); + if( nSPCode != -1 ) + { + nZone = ( nZone == 1 ? nSPCode : nSPCode + nZone - 1 ); + + if( oSRS.SetStatePlane( nZone, ( nNAD == 83 ) ) != OGRERR_FAILURE ) + { + oSRS.exportToWkt( ppszProjString ); + CPLFree( pszRefSystemLower ); + return CE_None; + } + + // ---------------------------------------------------------- + // If SetStatePlane fails, set GeoCS as NAD Datum and let it + // try to read the projection info from georeference file( * ) + // ---------------------------------------------------------- + + oSRS.SetWellKnownGeogCS( CPLSPrintf( "NAD%d", nNAD ) ); + } + } + + CPLFree( pszRefSystemLower ); + pszRefSystemLower = NULL; + + // ------------------------------------------------------------------ + // Search for georeference file .ref + // ------------------------------------------------------------------ + + const char *pszFName = CPLSPrintf( "%s%c%s.ref", + CPLGetDirname( pszFilename ), PATHDELIM, pszRefSystem ); + + if( ! FileExists( pszFName ) ) + { + // ------------------------------------------------------------------ + // Look at $IDRISIDIR\Georef\.ref + // ------------------------------------------------------------------ + + const char *pszIdrisiDir = CPLGetConfigOption( "IDRISIDIR", NULL ); + + if( ( pszIdrisiDir ) != NULL ) + { + pszFName = CPLSPrintf( "%s%cgeoref%c%s.ref", + pszIdrisiDir, PATHDELIM, PATHDELIM, pszRefSystem ); + } + } + + // ------------------------------------------------------------------ + // Cannot find georeference file + // ------------------------------------------------------------------ + + if( ! FileExists( pszFName ) ) + { + CPLDebug( "RST", "Cannot find Idrisi georeference file %s", + pszRefSystem ); + + if( oSRS.IsGeographic() == FALSE ) /* see State Plane remarks( * ) */ + { + oSRS.SetLocalCS( "Unknown" ); + int nUnit = GetUnitIndex( pszRefUnits ); + if( nUnit > -1 ) + { + int nDeft = aoLinearUnitsConv[nUnit].nDefaultG; + oSRS.SetLinearUnits( aoLinearUnitsConv[nDeft].pszName, + aoLinearUnitsConv[nDeft].dfConv ); + } + } + oSRS.exportToWkt( ppszProjString ); + return CE_Failure; + } + + // ------------------------------------------------------------------ + // Read values from georeference file + // ------------------------------------------------------------------ + + char **papszRef = CSLLoad( pszFName ); + CSLSetNameValueSeparator( papszRef, ":" ); + + char *pszGeorefName; + + const char* pszREF_SYSTEM = CSLFetchNameValue( papszRef, refREF_SYSTEM ); + if( pszREF_SYSTEM != NULL && EQUAL( pszREF_SYSTEM, "" ) == FALSE ) + { + pszGeorefName = CPLStrdup( pszREF_SYSTEM ); + } + else + { + pszGeorefName = CPLStrdup( CSLFetchNameValue( papszRef, refREF_SYSTEM2 ) ); + } + char *pszProjName = CPLStrdup( CSLFetchNameValue( papszRef, refPROJECTION ) ); + char *pszDatum = CPLStrdup( CSLFetchNameValue( papszRef, refDATUM ) ); + char *pszEllipsoid = CPLStrdup( CSLFetchNameValue( papszRef, refELLIPSOID ) ); + double dfCenterLat = CPLAtof_nz( CSLFetchNameValue( papszRef, refORIGIN_LAT ) ); + double dfCenterLong = CPLAtof_nz( CSLFetchNameValue( papszRef, refORIGIN_LONG ) ); + double dfSemiMajor = CPLAtof_nz( CSLFetchNameValue( papszRef, refMAJOR_SAX ) ); + double dfSemiMinor = CPLAtof_nz( CSLFetchNameValue( papszRef, refMINOR_SAX ) ); + double dfFalseEasting = CPLAtof_nz( CSLFetchNameValue( papszRef, refORIGIN_X ) ); + double dfFalseNorthing = CPLAtof_nz( CSLFetchNameValue( papszRef, refORIGIN_Y ) ); + double dfStdP1 = CPLAtof_nz( CSLFetchNameValue( papszRef, refSTANDL_1 ) ); + double dfStdP2 = CPLAtof_nz( CSLFetchNameValue( papszRef, refSTANDL_2 ) ); + double dfScale; + double adfToWGS84[3] = { 0.0, 0.0, 0.0 }; + + const char* pszToWGS84 = CSLFetchNameValue( papszRef, refDELTA_WGS84 ); + if (pszToWGS84) + CPLsscanf( pszToWGS84, "%lf %lf %lf", + &adfToWGS84[0], &adfToWGS84[1], &adfToWGS84[2] ); + + const char* pszSCALE_FAC = CSLFetchNameValue( papszRef, refSCALE_FAC ); + if( pszSCALE_FAC == NULL || EQUAL( pszSCALE_FAC, "na" ) ) + dfScale = 1.0; + else + dfScale = CPLAtof_nz( pszSCALE_FAC ); + + CSLDestroy( papszRef ); + + // ---------------------------------------------------------------------- + // Set the Geographic Coordinate System + // ---------------------------------------------------------------------- + + if( oSRS.IsGeographic() == FALSE ) /* see State Plane remarks(*) */ + { + int nEPSG = 0; + + // ---------------------------------------------------------------------- + // Is it a WGS84 equivalent? + // ---------------------------------------------------------------------- + + if( ( EQUALN( pszEllipsoid, "WGS", 3 ) ) &&( strstr( pszEllipsoid, "84" ) ) && + ( EQUALN( pszDatum, "WGS", 3 ) ) &&( strstr( pszDatum, "84" ) ) && + ( adfToWGS84[0] == 0.0 ) &&( adfToWGS84[1] == 0.0 ) &&( adfToWGS84[2] == 0.0 ) ) + { + nEPSG = 4326; + } + + // ---------------------------------------------------------------------- + // Match GCS's DATUM_NAME by using 'ApproxString' over Datum + // ---------------------------------------------------------------------- + + if( nEPSG == 0 ) + { + nEPSG = atoi_nz( CSVGetField( CSVFilename( "gcs.csv" ), + "DATUM_NAME", pszDatum, CC_ApproxString, "COORD_REF_SYS_CODE" ) ); + } + + // ---------------------------------------------------------------------- + // Match GCS's COORD_REF_SYS_NAME by using 'ApproxString' over Datum + // ---------------------------------------------------------------------- + + if( nEPSG == 0 ) + { + nEPSG = atoi_nz( CSVGetField( CSVFilename( "gcs.csv" ), + "COORD_REF_SYS_NAME", pszDatum, CC_ApproxString, "COORD_REF_SYS_CODE" ) ); + } + + if( nEPSG != 0 ) + { + oSRS.importFromEPSG( nEPSG ); + } + else + { + // -------------------------------------------------- + // Create GeogCS based on the georeference file info + // -------------------------------------------------- + + oSRS.SetGeogCS( pszRefSystem, + pszDatum, + pszEllipsoid, + dfSemiMajor, + (dfSemiMinor == dfSemiMajor) ? 0.0 : ( -1.0 /( dfSemiMinor / dfSemiMajor - 1.0 ) ) ); + } + + // ---------------------------------------------------------------------- + // Note: That will override EPSG info: + // ---------------------------------------------------------------------- + + oSRS.SetTOWGS84( adfToWGS84[0], adfToWGS84[1], adfToWGS84[2] ); + } + + // ---------------------------------------------------------------------- + // If the georeference file tells that it is a non project system: + // ---------------------------------------------------------------------- + + if( EQUAL( pszProjName, "none" ) ) + { + oSRS.exportToWkt( ppszProjString ); + + CPLFree( pszGeorefName ); + CPLFree( pszProjName ); + CPLFree( pszDatum ); + CPLFree( pszEllipsoid ); + + return CE_None; + } + + // ---------------------------------------------------------------------- + // Create Projection information based on georeference file info + // ---------------------------------------------------------------------- + + // Idrisi user's Manual, Supported Projection: + // + // Mercator + // Transverse Mercator + // Gauss-Kruger + // Lambert Conformal Conic + // Plate Carree + // Hammer Aitoff + // Lambert North Polar Azimuthal Equal Area + // Lambert South Polar Azimuthal Equal Area + // Lambert Transverse Azimuthal Equal Area + // Lambert Oblique Polar Azimuthal Equal Area + // North Polar Stereographic + // South Polar Stereographic + // Transverse Stereographic + // Oblique Stereographic + // Albers Equal Area Conic + // Sinusoidal + // + + if( EQUAL( pszProjName, "Mercator" ) ) + { + oSRS.SetMercator( dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing ); + } + else if EQUAL( pszProjName, "Transverse Mercator" ) + { + oSRS.SetTM( dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing ); + } + else if EQUALN( pszProjName, "Gauss-Kruger", 9 ) + { + oSRS.SetTM( dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing ); + } + else if( EQUAL( pszProjName, "Lambert Conformal Conic" ) ) + { + oSRS.SetLCC( dfStdP1, dfStdP2, dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing ); + } + else if( EQUALN( pszProjName, "Plate Carr" "\xE9" "e", 10 ) ) /* 'eacute' in ISO-8859-1 */ + { + oSRS.SetEquirectangular( dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing ); + } + else if( EQUAL( pszProjName, "Hammer Aitoff" ) ) + { + oSRS.SetProjection( pszProjName ); + oSRS.SetProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + oSRS.SetProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + oSRS.SetProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + oSRS.SetProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + } + else if( EQUALN( pszProjName, "Lambert North Polar Azimuthal Equal Area", 15 ) || + EQUALN( pszProjName, "Lambert South Polar Azimuthal Equal Area", 15 ) || + EQUALN( pszProjName, "Lambert Transverse Azimuthal Equal Area", 15 ) || + EQUALN( pszProjName, "Lambert Oblique Polar Azimuthal Equal Area", 15 ) ) + { + oSRS.SetLAEA( dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing ); + } + else if( EQUALN( pszProjName, "North Polar Stereographic", 15 ) || + EQUALN( pszProjName, "South Polar Stereographic", 15 ) ) + { + oSRS.SetPS( dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing ); + } + else if( EQUALN( pszProjName, "Transverse Stereographic", 15 ) ) + { + oSRS.SetStereographic( dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing ); + } + else if( EQUALN( pszProjName, "Oblique Stereographic", 15 ) ) + { + oSRS.SetOS( dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing ); + } + else if( EQUAL( pszProjName, "Alber's Equal Area Conic" ) || + EQUAL( pszProjName, "Albers Equal Area Conic" ) ) + { + oSRS.SetACEA( dfStdP1, dfStdP2, dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing ); + } + else if( EQUAL( pszProjName, "Sinusoidal" ) ) + { + oSRS.SetSinusoidal( dfCenterLong, dfFalseEasting, dfFalseNorthing ); + } + else + { + CPLError( CE_Warning, CPLE_NotSupported, + "Projection not listed on Idrisi User's Manual( v.15.0/2005 ).\n\t" + "[\"%s\" in georeference file \"%s\"]", + pszProjName, pszFName ); + oSRS.Clear(); + oSRS.exportToWkt( ppszProjString ); + + CPLFree( pszGeorefName ); + CPLFree( pszProjName ); + CPLFree( pszDatum ); + CPLFree( pszEllipsoid ); + + return CE_Warning; + } + + // ---------------------------------------------------------------------- + // Set the Linear Units + // ---------------------------------------------------------------------- + + int nUnit = GetUnitIndex( pszRefUnits ); + if( nUnit > -1 ) + { + int nDeft = aoLinearUnitsConv[nUnit].nDefaultG; + oSRS.SetLinearUnits( aoLinearUnitsConv[nDeft].pszName, + aoLinearUnitsConv[nDeft].dfConv ); + } + else + { + oSRS.SetLinearUnits( "unknown", 1.0 ); + } + + // ---------------------------------------------------------------------- + // Name ProjCS with the name on the georeference file + // ---------------------------------------------------------------------- + + oSRS.SetProjCS( pszGeorefName ); + + oSRS.exportToWkt( ppszProjString ); + + CPLFree( pszGeorefName ); + CPLFree( pszProjName ); + CPLFree( pszDatum ); + CPLFree( pszEllipsoid ); + + return CE_None; +} + +/************************************************************************/ +/* Wkt2GeoReference() */ +/************************************************************************/ + +/*** +* Converts OpenGIS WKT to Idrisi geographic reference information. +* +* That function will fill up the two parameters RefSystem and RefUnit +* that goes into the Idrisi metadata. But it could also create +* a companying georeference file to the output if necessary. +* +* First it will try to identify the ProjString as Local, WGS84 or +* one of the Idrisi name convention reference systems +* otherwise, if the projection system is supported by Idrisi, +* it will create a companying georeference files. +***/ + +CPLErr IdrisiDataset::Wkt2GeoReference( const char *pszProjString, + char **pszRefSystem, + char **pszRefUnit ) +{ + // ----------------------------------------------------- + // Plane with default "Meters" + // ----------------------------------------------------- + + if EQUAL( pszProjString, "" ) + { + *pszRefSystem = CPLStrdup( rstPLANE ); + *pszRefUnit = CPLStrdup( rstMETER ); + return CE_None; + } + + OGRSpatialReference oSRS; + oSRS.importFromWkt( (char **) &pszProjString ); + + // ----------------------------------------------------- + // Local => Plane + Linear Unit + // ----------------------------------------------------- + + if( oSRS.IsLocal() ) + { + *pszRefSystem = CPLStrdup( rstPLANE ); + *pszRefUnit = GetUnitDefault( oSRS.GetAttrValue( "UNIT" ), + CPLSPrintf( "%f", oSRS.GetLinearUnits() ) ); + return CE_None; + } + + // ----------------------------------------------------- + // Test to identify WGS84 => Latlong + Angular Unit + // ----------------------------------------------------- + + if( oSRS.IsGeographic() ) + { + char *pszSpheroid = CPLStrdup( oSRS.GetAttrValue( "SPHEROID" ) ); + char *pszAuthName = CPLStrdup( oSRS.GetAuthorityName( "GEOGCS" ) ); + char *pszDatum = CPLStrdup( oSRS.GetAttrValue( "DATUM" ) ); + int nGCSCode = -1; + if EQUAL( pszAuthName, "EPSG" ) + { + nGCSCode = atoi( oSRS.GetAuthorityCode( "GEOGCS" ) ); + } + if( ( nGCSCode == 4326 ) ||( + ( EQUALN( pszSpheroid, "WGS", 3 ) ) &&( strstr( pszSpheroid, "84" ) ) && + ( EQUALN( pszDatum, "WGS", 3 ) ) &&( strstr( pszDatum, "84" ) ) ) ) + { + *pszRefSystem = CPLStrdup( rstLATLONG ); + *pszRefUnit = CPLStrdup( rstDEGREE ); + + CPLFree( pszSpheroid ); + CPLFree( pszAuthName ); + CPLFree( pszDatum ); + + return CE_None; + } + + CPLFree( pszSpheroid ); + CPLFree( pszAuthName ); + CPLFree( pszDatum ); + } + + // ----------------------------------------------------- + // Prepare to match some projections + // ----------------------------------------------------- + + const char *pszProjName = oSRS.GetAttrValue( "PROJECTION" ); + + if( pszProjName == NULL ) + { + pszProjName = ""; + } + + // ----------------------------------------------------- + // Check for UTM zones + // ----------------------------------------------------- + + if( EQUAL( pszProjName, SRS_PT_TRANSVERSE_MERCATOR ) ) + { + int nZone = oSRS.GetUTMZone(); + + if( ( nZone != 0 ) && ( EQUAL( oSRS.GetAttrValue( "DATUM" ), SRS_DN_WGS84 ) ) ) + { + double dfNorth = oSRS.GetProjParm( SRS_PP_FALSE_NORTHING ); + *pszRefSystem = CPLStrdup( CPLSPrintf( rstUTM, nZone,( dfNorth == 0.0 ? 'n' : 's' ) ) ); + *pszRefUnit = CPLStrdup( rstMETER ); + return CE_None; + } + } + + // ----------------------------------------------------- + // Check for State Plane + // ----------------------------------------------------- + +#ifndef GDAL_RST_PLUGIN + + if( EQUAL( pszProjName, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP ) || + EQUAL( pszProjName, SRS_PT_TRANSVERSE_MERCATOR ) ) + { + const char *pszPCSCode; + const char *pszID = CPLStrdup( oSRS.GetAuthorityCode( "PROJCS" ) ); + if( strlen( pszID ) > 0 ) + { + pszPCSCode = CPLStrdup( CSVGetField( CSVFilename( "stateplane.csv" ), + "EPSG_PCS_CODE", pszID, CC_Integer, "ID" ) ); + if( strlen( pszPCSCode ) > 0 ) + { + int nNADYear = 83; + int nZone = pszPCSCode[strlen( pszPCSCode ) - 1] - '0'; + int nSPCode = atoi_nz( pszPCSCode ); + + if( nZone == 0 ) + nZone = 1; + else + nSPCode = nSPCode - nZone + 1; + + if( nSPCode > 10000 ) + { + nNADYear = 27; + nSPCode -= 10000; + } + char *pszState = CPLStrdup( GetStateName( nSPCode ) ); + if( ! EQUAL( pszState, "" ) ) + { + *pszRefSystem = CPLStrdup( CPLSPrintf( rstSPC, nNADYear, pszState, nZone ) ); + *pszRefUnit = GetUnitDefault( oSRS.GetAttrValue( "UNIT" ), + CPLSPrintf( "%f", oSRS.GetLinearUnits() ) ); + return CE_None; + } + } + }// + + //if EPSG code is missing, go to following steps to work with origin + double dfLon = 0.0; + double dfLat = 0.0; + + const char *pszNAD83 = "83"; + const char *pszNAD27 = "27"; + int isOldNAD = FALSE; + + const char *pszDatumValue = oSRS.GetAttrValue("DATUM",0); + if( (strstr(pszDatumValue, pszNAD83) == NULL) && (strstr(pszDatumValue, pszNAD27) != NULL )) + //strcpy(pszNAD, "27"); + isOldNAD = TRUE; + + if ( (oSRS.FindProjParm("central_meridian",NULL) != -1) && (oSRS.FindProjParm("central_meridian",NULL) != -1) ) + { + dfLon = oSRS.GetProjParm("central_meridian"); + dfLat = oSRS.GetProjParm("latitude_of_origin"); + dfLon = (int)(fabs(dfLon) * 100 + 0.5) / 100.0; + dfLat = (int)(fabs(dfLat) * 100 + 0.5) / 100.0; + *pszRefSystem = CPLStrdup(GetSpcs(dfLon, dfLat)); + } + + if(*pszRefSystem != NULL) + { + //Convert 83 TO 27 + if(isOldNAD) + { + char pszOutRefSystem[9]; + NAD83to27(pszOutRefSystem, *pszRefSystem); + *pszRefSystem = CPLStrdup(pszOutRefSystem); + } + *pszRefUnit = GetUnitDefault( oSRS.GetAttrValue( "UNIT" ), CPLSPrintf( "%f", oSRS.GetLinearUnits() ) ); + return CE_None; + } + + } + +#endif // GDAL_RST_PLUGIN + + const char *pszProjectionOut = NULL; + + if( oSRS.IsProjected() ) + { + // --------------------------------------------------------- + // Check for supported projections + // --------------------------------------------------------- + + if EQUAL( pszProjName, SRS_PT_MERCATOR_1SP ) + { + pszProjectionOut = "Mercator" ; + } + if EQUAL( pszProjName, SRS_PT_TRANSVERSE_MERCATOR ) + { + pszProjectionOut = "Transverse Mercator" ; + } + else if EQUAL( pszProjName, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP ) + { + pszProjectionOut = "Lambert Conformal Conic" ; + } + else if EQUAL( pszProjName, SRS_PT_EQUIRECTANGULAR ) + { + pszProjectionOut = "Plate Carr" "\xE9" "e" ; /* 'eacute' in ISO-8859-1 */ + } + else if EQUAL( pszProjName, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA ) + { + double dfCenterLat = oSRS.GetProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0, NULL ); + if( dfCenterLat == 0.0 ) + pszProjectionOut = "Lambert Transverse Azimuthal Equal Area" ; + else if( fabs( dfCenterLat ) == 90.0 ) + pszProjectionOut = "Lambert Oblique Polar Azimuthal Equal Area" ; + else if( dfCenterLat > 0.0 ) + pszProjectionOut = "Lambert North Oblique Azimuthal Equal Area" ; + else + pszProjectionOut = "Lambert South Oblique Azimuthal Equal Area" ; + } + else if EQUAL( pszProjName, SRS_PT_POLAR_STEREOGRAPHIC ) + { + if( oSRS.GetProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0, NULL ) > 0 ) + pszProjectionOut = "North Polar Stereographic" ; + else + pszProjectionOut = "South Polar Stereographic" ; + } + else if EQUAL( pszProjName, SRS_PT_STEREOGRAPHIC ) + { + pszProjectionOut = "Transverse Stereographic" ; + } + else if EQUAL( pszProjName, SRS_PT_OBLIQUE_STEREOGRAPHIC ) + { + pszProjectionOut = "Oblique Stereographic" ; + } + else if EQUAL( pszProjName, SRS_PT_SINUSOIDAL ) + { + pszProjectionOut = "Sinusoidal" ; + } + else if EQUAL( pszProjName, SRS_PT_ALBERS_CONIC_EQUAL_AREA ) + { + pszProjectionOut = "Alber's Equal Area Conic" ; + } + + // --------------------------------------------------------- + // Failure, Projection system not suppotted + // --------------------------------------------------------- + + if( pszProjectionOut == NULL ) + { + CPLDebug( "RST", + "Not supported by RST driver: PROJECTION[\"%s\"]", + pszProjName ); + + *pszRefSystem = CPLStrdup( rstPLANE ); + *pszRefUnit = CPLStrdup( rstMETER ); + return CE_Failure; + } + } + else + { + pszProjectionOut = "none" ; + } + + // --------------------------------------------------------- + // Prepare to write ref file + // --------------------------------------------------------- + + char *pszGeorefName = CPLStrdup( "Unknown" ); + char *pszDatum = CPLStrdup( oSRS.GetAttrValue( "DATUM" ) ); + char *pszEllipsoid = CPLStrdup( oSRS.GetAttrValue( "SPHEROID" ) ); + double dfSemiMajor = oSRS.GetSemiMajor(); + double dfSemiMinor = oSRS.GetSemiMinor(); + double adfToWGS84[3]; + oSRS.GetTOWGS84( adfToWGS84, 3 ); + + double dfCenterLat = 0.0; + double dfCenterLong = 0.0; + double dfFalseNorthing = 0.0; + double dfFalseEasting = 0.0; + double dfScale = 1.0; + int nParameters = 0; + double dfStdP1 = 0.0; + double dfStdP2 = 0.0; + char *pszAngularUnit = CPLStrdup( oSRS.GetAttrValue( "GEOGCS|UNIT" ) ); + char *pszLinearUnit; + + if( oSRS.IsProjected() ) + { + CPLFree( pszGeorefName ); + pszGeorefName = CPLStrdup( oSRS.GetAttrValue( "PROJCS" ) ); + dfCenterLat = oSRS.GetProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0, NULL ); + dfCenterLong = oSRS.GetProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0, NULL ); + dfFalseNorthing = oSRS.GetProjParm( SRS_PP_FALSE_NORTHING, 0.0, NULL ); + dfFalseEasting = oSRS.GetProjParm( SRS_PP_FALSE_EASTING, 0.0, NULL ); + dfScale = oSRS.GetProjParm( SRS_PP_SCALE_FACTOR, 0.0, NULL ); + dfStdP1 = oSRS.GetProjParm( SRS_PP_STANDARD_PARALLEL_1, -0.1, NULL ); + dfStdP2 = oSRS.GetProjParm( SRS_PP_STANDARD_PARALLEL_2, -0.1, NULL ); + if( dfStdP1 != -0.1 ) + { + nParameters = 1; + if( dfStdP2 != -0.1 ) + nParameters = 2; + } + pszLinearUnit = GetUnitDefault( oSRS.GetAttrValue( "PROJCS|UNIT" ), + CPLSPrintf( "%f", oSRS.GetLinearUnits() ) ) ; + } + else + { + pszLinearUnit = GetUnitDefault( pszAngularUnit ); + } + + // --------------------------------------------------------- + // Create a companion georeference file for this dataset + // --------------------------------------------------------- + + char **papszRef = NULL; + papszRef = CSLAddNameValue( papszRef, refREF_SYSTEM, pszGeorefName ); + papszRef = CSLAddNameValue( papszRef, refPROJECTION, pszProjectionOut ); + papszRef = CSLAddNameValue( papszRef, refDATUM, pszDatum ); + papszRef = CSLAddNameValue( papszRef, refDELTA_WGS84, CPLSPrintf( "%.3g %.3g %.3g", + adfToWGS84[0], adfToWGS84[1], adfToWGS84[2] ) ); + papszRef = CSLAddNameValue( papszRef, refELLIPSOID, pszEllipsoid ); + papszRef = CSLAddNameValue( papszRef, refMAJOR_SAX, CPLSPrintf( "%.3f", dfSemiMajor ) ); + papszRef = CSLAddNameValue( papszRef, refMINOR_SAX, CPLSPrintf( "%.3f", dfSemiMinor ) ); + papszRef = CSLAddNameValue( papszRef, refORIGIN_LONG, CPLSPrintf( "%.9g", dfCenterLong ) ); + papszRef = CSLAddNameValue( papszRef, refORIGIN_LAT, CPLSPrintf( "%.9g", dfCenterLat ) ); + papszRef = CSLAddNameValue( papszRef, refORIGIN_X, CPLSPrintf( "%.9g", dfFalseEasting ) ); + papszRef = CSLAddNameValue( papszRef, refORIGIN_Y, CPLSPrintf( "%.9g", dfFalseNorthing ) ); + papszRef = CSLAddNameValue( papszRef, refSCALE_FAC, CPLSPrintf( "%.9g", dfScale ) ); + papszRef = CSLAddNameValue( papszRef, refUNITS, pszLinearUnit ); + papszRef = CSLAddNameValue( papszRef, refPARAMETERS, CPLSPrintf( "%1d", nParameters ) ); + if( nParameters > 0 ) + papszRef = CSLAddNameValue( papszRef, refSTANDL_1, CPLSPrintf( "%.9g", dfStdP1 ) ); + if( nParameters > 1 ) + papszRef = CSLAddNameValue( papszRef, refSTANDL_2, CPLSPrintf( "%.9g", dfStdP2 ) ); + CSLSetNameValueSeparator( papszRef, ": " ); + SaveAsCRLF( papszRef, CPLResetExtension( pszFilename, extREF ) ); + CSLDestroy( papszRef ); + + *pszRefSystem = CPLStrdup( CPLGetBasename( pszFilename ) ); + *pszRefUnit = CPLStrdup( pszLinearUnit ); + + CPLFree( pszGeorefName ); + CPLFree( pszDatum ); + CPLFree( pszEllipsoid ); + CPLFree( pszLinearUnit ); + CPLFree( pszAngularUnit ); + + return CE_None; +} + +/************************************************************************/ +/* FileExists() */ +/************************************************************************/ + +bool FileExists( const char *pszPath ) +{ + VSIStatBufL sStat; + + return (bool) ( VSIStatL( pszPath, &sStat ) == 0 ); +} + +/************************************************************************/ +/* GetStateCode() */ +/************************************************************************/ + +int GetStateCode( const char *pszState ) +{ + unsigned int i; + + for( i = 0; i < US_STATE_COUNT; i++ ) + { + if EQUAL( pszState, aoUSStateTable[i].pszName ) + { + return aoUSStateTable[i].nCode; + } + } + return -1; +} + +/************************************************************************/ +/* GetStateName() */ +/************************************************************************/ + +const char *GetStateName( int nCode ) +{ + unsigned int i; + + for( i = 0; i < US_STATE_COUNT; i++ ) + { + if( nCode == aoUSStateTable[i].nCode ) + { + return aoUSStateTable[i].pszName; + } + } + return NULL; +} + + +/************************************************************************/ +/* GetSpcs() */ +/************************************************************************/ + +char *GetSpcs(double dfLon, double dfLat) +{ + int i; + for( i=0; iSetDescription( "RST" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, rstVERSION ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_Idrisi.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, extRST ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte Int16 Float32" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = IdrisiDataset::Open; + poDriver->pfnCreate = IdrisiDataset::Create; + poDriver->pfnCreateCopy = IdrisiDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/idrisi/frmt_Idrisi.html b/bazaar/plugin/gdal/frmts/idrisi/frmt_Idrisi.html new file mode 100644 index 000000000..e3421779d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/idrisi/frmt_Idrisi.html @@ -0,0 +1,57 @@ + + + RST --- Idrisi Raster Format + + +

    RST --- Idrisi Raster Format

    +

    This format is basically a raw one. There is just one band per + files, except in the RGB24 data type where the Red, Green and Blue bands are + store interleafed by pixels in the order Blue, Green and Red. The others data + type are unsigned 8 bits integer with values from 0 to 255 or signed 16 bits + integer with values from -32.768 to 32.767 or 32 bits single precision floating + point.32 bits. The description of the file is stored in a companying text file, + extension RDC. +

    +

    The RDC image description file doesn't include color table, or + detailed geographic referencing information. The color table if present can be + obtained by another companying file using the same base name as the RST file + and SMP as extension. +

    +

    For geographical referencing identification, the RDC file + contains information that points to a file that holds the geographic reference + details. Those files uses extension REF and +  resides in the same folder as the + RST image or more likely in the Idrisi installation folders.

    +

    Therefore the presence or absence of the Idrisi software in + the running operation system will determine the way that this driver will work. + By setting the environment variable IDRISIDIR pointing to the Idrisi main + installation folder will enable GDAL to find more detailed information about + geographical reference and projection in the REF files.

    +

    Note that the RST driver recognizes the name convention used + in Idrisi for UTM and State Plane geographic reference so it doesn't need to + access the REF files. That is the case for RDC file that specify "utm-30n" or + "spc87ma1" in the "ref. system" field. Note that exporting to RST in any other + geographical reference system will generate a suggested REF content in the + comment section of the RDC file. +

    +

    +
      +
    • + ".rst" the raw image file +
    • + ".rdc" the description file +
    • + ".smp" the color table file +
    • + ".ref" the geographical reference file
    • +
    +

    See Also:

    +
      +
    • + Implemented as gdal/frmts/idrisi/idrisiraster.cpp. +
    • + www.idrisi.com +
    • +
    + + diff --git a/bazaar/plugin/gdal/frmts/idrisi/idrisi.h b/bazaar/plugin/gdal/frmts/idrisi/idrisi.h new file mode 100644 index 000000000..912f17079 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/idrisi/idrisi.h @@ -0,0 +1,41 @@ +/***************************************************************************** +* $Id: idrisi.h 27044 2014-03-16 23:41:27Z rouault $ +* +* Project: Idrisi Raster Image File Driver +* Purpose: Read/write Idrisi Raster Image Format RST +* Author: Ivan Lucena, [lucena_ivan at hotmail.com] +* +****************************************************************************** +* Copyright( c ) 2006, Ivan Lucena + * Copyright (c) 2011, Even Rouault +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files( the "Software" ), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +****************************************************************************/ + +#ifndef _IDRISI_H_INCLUDED +#define _IDRISI_H_INCLUDED + +#include "cpl_error.h" + +CPLErr IdrisiGeoReference2Wkt( const char* pszFilename, + const char *pszRefSystem, + const char *pszRefUnits, + char **ppszProjString ); + +#endif /* _IDRISI_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/idrisi/makefile.vc b/bazaar/plugin/gdal/frmts/idrisi/makefile.vc new file mode 100644 index 000000000..2941035e1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/idrisi/makefile.vc @@ -0,0 +1,15 @@ +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +OBJ = idrisiDataset.obj + +EXTRAFLAGS = -D_USE_MATH_DEFINES + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + cd .. + +clean: + -del *.obj + cd .. diff --git a/bazaar/plugin/gdal/frmts/idrisi/rdc.txt b/bazaar/plugin/gdal/frmts/idrisi/rdc.txt new file mode 100644 index 000000000..a19c562a9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/idrisi/rdc.txt @@ -0,0 +1,126 @@ +Image Documentation File (.rdc) -- File Structure + +Use + +Image Documentation files contain important information about their corresponding Image data files (i.e., its metadata, or header information). Whenever an IDRISI module accesses an Image file, it also accesses the accompanying Image Documentation file. Metadata may be used to examine Image Documentation files. + +File Contents + +Following are the fields that are stored in Image Documentation files. They may be broken down into four major groups: + +Information about the image as a whole: + +file format: The format and version of the file format. + +file title: A descriptive name of the file. It is this text that appears with the title option enabled on display. + +data type: The type of numbers stored in the file. Allowable entries are byte, integer, real, RGB8 and RGB24. (See the description for Image files.) + +file type: The format in which the Image file is stored. Allowable entries are ASCII, Binary and Packed Binary (See Image files.) + +columns: The number of columns in the image. This is extremely important as it tells IDRISI modules how to construct the rectangular image from the stored values. (See Image files.) + +rows: The number of rows in the image. + + +Information about the georeferencing system of the file: + +ref. system: The name of the geographic referencing system used with the file. This may be Plane, Lat/Long, or a specific referencing system defined by a Reference System Parameter file. + +ref. units: The unit of measure used in the specified reference system. Allowable entries are m, ft, mi, km, deg and radians. + +unit dist.: The scaling factor between the given coordinates and actual measurements on the ground. This will almost always be 1. The unit distance answers the question, "If I move one unit in the reference system described here, how far have I moved on the ground, measuring in reference units?" + +min X: The minimum X coordinate (left edge) of the image. + +max X: The maximum X coordinate (right edge) of the image. + +min Y: The minimum Y coordinate (bottom edge) of the image. + +max Y: The maximum Y coordinate (top edge) of the image. + +pos'n error: A measure of the accuracy of the positions in the image. This field can be used to record the RMS (Root Mean Square) error of locational positions in the reference system. At present, this field is not analytical, but rather is for informational purposes only. (See RESAMPLE Note 5.) + +resolution: The inherent resolution of the image. In most cases, this should correspond to the result of dividing the range of reference coordinates in X by the number of columns in the image. However, there are some rare instances where it might differ from this result. An example is the case of LANDSAT Band 6 (Thermal) imagery. The resolution of those data is actually 120 meters, and would be recorded as such in this field. However, the data is distributed in an apparent 30 meter format to make them physically match the dimensions of the other bands of the scene. The resolution field is a way of correctly indicating the underlying resolution of these data. + +Information about the values stored in the file: + +min value: The minimum value in the image. If the file is of RGB24 type minimum values for the red, green and blue bands of the composite are listed in that order. + +max value: The maximum value in the image. If the file is of RGB24 type maximum values for the red, green and blue bands of the composite are listed in that order. + +display min: The value in the image to display with the first palette color and legend box. + +display max: The value in the image to display with the last palette color and legend box. + +value units: The unit of measure of the values in the image. It is suggested that the term classes be used for all qualitative data sets, and that whenever standard linear units are appropriate, that the same abbreviations that are used for reference units should also be used (m, ft, mi, km, deg, rad). + +value error: This field is very important and should be filled out whenever possible. It records the error in the data values that appear in image cells. For qualitative data, this should be recorded as a proportional error. For quantitative data, the value here should be an RMS error figure. For example, for a DEM, an RMS error of 3 would indicate 68% of all values will be within ± 3 meters of the stated elevation, that approximately 95% will be within ± 6 meters, and so on. This field is analytical for some modules (e.g., PCLASS) and it is intended that it will become more so in the future. + +flag value: Any value in the image that is not a data value, but rather has a special meaning. If there is no flag value, this entry should remain blank. + +flag def'n: Definition of the above flag value. The most common data flags are those used to indicate background cells and missing data cells. This field is analytical for some modules (e.g., SURFACE, Geostatistics) and will become more so in the future. The key words background and missing data are specifically recognized by some modules. Other terms are only informational in this version. If there is no flag value, this entry should remain blank. + +legend cats: The number of legend categories present. Legend entries are optional. If there is no legend, 0 should be entered in this field. If legend categories do exist, there will be as many lines following as there are legend categories. If no legend categories exist, these lines are not necessary. (See the example below.) + +Other information about the file: + +The following four entries are optional, and any number of each may be entered at the end of the file Metadata can be used to enter this information or they can be entered manually so long as each has the correct term in the 14 character descriptive field to the left. These are all text fields and are included to facilitate complete documentation of the Image file. At present, these last fields are for information only and are not read by IDRISI modules (although some modules will write them). + +comment: Any additional information about the data may be recorded here. +lineage: Description of the history by which the values were recorded/derived. +completeness: The degree to which the values describe the subject matter indicated. +consistency: The logical consistency of the file. + +Note that the lineage, consistency and completeness fields are intended to meet the recommendations of the U.S. National Committee for Digital Cartographic Data Standards (NCDCDS). Along with the pos'n error and value error fields, they provide a means of adhering to the proposed standards for reporting digital cartographic data quality. For further information about the NCDCDS standard, refer to the January 1988 issue of The American Cartographer, 15(1). + +Creation + +Documentation files may be created using the module METADATA. They are created automatically by any IDRISI module that produces an Image file. + +Structure + +Image Documentation files are stored in ASCII format. The first 14 characters of each line describe the contents of the line, while the remaining characters contain the actual information. + +Example + +For example, the documentation file for a soils image might look like this: + +file format : IDRISI Raster A.1 + +file title : Major Soils Groups +data type : byte +file type : binary +columns : 512 +rows : 480 +ref. system : plane +ref. units : m +unit dist. : 1 +min. X : 0 +max. X : 15360 +min. Y : 0 +max. Y : 14400 +pos'n error : unknown +resolution : 30 +min. value : 0 +max. value : 3 +display min : 0 +display max : 3 +value units : classes +value error : 0.15 +flag value : 0 +flag def'n : background +legend cats : 3 +code 1 : Podzol Soils + +code 2 : Brown Podzolic Soils +code 3 : Gray-Brown Podzolic Soils +lineage : Soil polygons derived from 1:5000 scale color air photography +lineage : and ground truth, with the final compilation being adjusted to +lineage : the map base by hand. +comment : Value error determined by statistical accuracy assessment +comment : based on a stratified random sample of 37 points. + +Note + +In versions of IDRISI prior to idrisi32, these files had a .DOC extension. The format has also changed. To convert images from earlier versions to idrisi32, or to convert an idrisi32 image to an earlier version, use IDRISI Conversion Tools from the File menu. \ No newline at end of file diff --git a/bazaar/plugin/gdal/frmts/idrisi/rst.txt b/bazaar/plugin/gdal/frmts/idrisi/rst.txt new file mode 100644 index 000000000..145ee632c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/idrisi/rst.txt @@ -0,0 +1,76 @@ +Image File (.rst) -- File Structure + +Use + +Image files are the data files most commonly used in IDRISI. They store the raster data layers used in analyses. + +File Contents + +Image files contain the attribute value of each pixel in the image. + +Creation + +Image files are created by most IDRISI modules. A variety of non-IDRISI format raster data may also be converted to Image files using the File/Import modules. + +Structure and Examples + +While the logical structure of an Image file is a grid, the actual structure, as it is stored, is a single long column of numbers. For instance, an image consisting of 3 rows by 5 columns is stored as a single column of 15 numbers. It is the Image Documentation file that allows IDRISI modules to construct the grid from this list. An image that looks like this: + +10 15 9 10 1 +1 14 10 11 13 +14 13 11 10 12 + +has an image file that looks like this: + +10 +15 +9 +10 +1 +1 +14 +10 +11 +13 +14 +13 +11 +10 +12 + +The numbers in an image file may be integer, byte or real. This is termed the data type. + +1. Integers are whole numbers within the range - 32768 to + 32767. +2. Byte values are positive integer numbers ranging from 0 to 255. +3. Real numbers have a fractional part, or are whole numbers outside the integer range. + +The real data types can store values within a range of ± 1 x 10 to the power of 38 with a precision of 7 significant figures. IDRISI supports the standard IEEE 4-byte real number format. +Two other data types are supported by Idrisi32. These are RGB8 and RGB24. Both of these are produced by the COMPOSITE module. An RGB8 file has byte data values, but these values have special meaning. The RGB24 data type indicates a band-interleaved-by-pixel format that is displayed in true color. For more information on both RGB8 and RGB24 files, see COMPOSITE. + +Image files may be stored in ASCII, binary or packed binary file types. The module CONVERT may be used to change data or file type. However, most routines in IDRISI expect the binary format only. + +The packed binary format is a special data compression format for binary integer or byte data. The compression technique used is run-length encoding. The number of occurrences of a value is given, followed by that value, and so on. The run proceeds from left to right and may carry over from the last cell of one row to the first cell of the next row. Primarily, the packed binary format will only save space when there are several cells with identical values next to one another. If there are not sufficient repeating values, the packed binary file may actually be larger than the same file stored as regular binary. + +The following image would require 15 bytes if stored as a byte binary file. + +10 10 10 10 10 +10 9 9 10 11 +11 9 9 9 9 + +Converting the file to packed binary would yield the following file, which would require only 10 bytes of disk space: + +6 +10 +2 +9 +1 +10 +2 +11 +4 +9 + +Note + +In versions of IDRISI prior to idrisi32, these files had an .img extension. The image file structure is identical to that of earlier versions. + diff --git a/bazaar/plugin/gdal/frmts/ilwis/GNUmakefile b/bazaar/plugin/gdal/frmts/ilwis/GNUmakefile new file mode 100644 index 000000000..4d3028843 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ilwis/GNUmakefile @@ -0,0 +1,11 @@ + +include ../../GDALmake.opt + +OBJ = ilwisdataset.o ilwiscoordinatesystem.o + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/ilwis/frmt_ilwis.html b/bazaar/plugin/gdal/frmts/ilwis/frmt_ilwis.html new file mode 100644 index 000000000..0cde4472d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ilwis/frmt_ilwis.html @@ -0,0 +1,44 @@ + + +ILWIS -- Raster Map + + + + +

    ILWIS -- Raster Map

    + +This driver implements reading and writing of ILWIS raster maps and map lists. +Select the raster fileswith the.mpr (for raster map) or the.mpl (for maplist) extensions.

    + +

    Features:

    + +
      +
    • Support for Byte, Int16, Int32 and Float64 pixel data types.
    • + +
    • Supports map lists with an associated set of ILWIS raster maps.
    • +
    • Read and write geo-reference (.grf). + Support for geo-referencing transform is limited to north-oriented + GeoRefCorner only. If possible the affine transform is + computed from the corner coordinates.
    • +
    • Read and write coordinate files (.csy). + Support is limited to: Projection type of Projection and Lat/Lon type that + are defined in .csy file, the rest of + pre-defined projection types are ignored.
    • +
    + +

    Limitations:

    + +
      +
    • Map lists with internal raster map storage (such as produced + through Import General Raster) are not supported.
    • + +
    • ILWIS domain (.dom) and representation (.rpr) files are currently ignored.
    • +
    + +

    NOTE: Implemented in gdal/frmts/ilwis. + +

    See Also: http://www.itc.nl/ilwis/default.asp .

    + + + + diff --git a/bazaar/plugin/gdal/frmts/ilwis/ilwiscoordinatesystem.cpp b/bazaar/plugin/gdal/frmts/ilwis/ilwiscoordinatesystem.cpp new file mode 100644 index 000000000..d94eca7ae --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ilwis/ilwiscoordinatesystem.cpp @@ -0,0 +1,1192 @@ +/****************************************************************************** + * + * Purpose: Translation from ILWIS coordinate system information. + * Author: Lichun Wang, lichun@itc.nl + * + ****************************************************************************** + * Copyright (c) 2004, ITC + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "cpl_conv.h" +#include "ilwisdataset.h" + +#include +using namespace std; + +typedef struct +{ + const char *pszIlwisDatum; + const char *pszWKTDatum; + int nEPSGCode; +} IlwisDatums; + +typedef struct +{ + const char *pszIlwisEllips; + int nEPSGCode; + double semiMajor; + double invFlattening; +} IlwisEllips; + +string ReadElement(string section, string entry, string filename); +bool WriteElement(string sSection, string sEntry, string fn, string sValue); +bool WriteElement(string sSection, string sEntry, string fn, int nValue); +bool WriteElement(string sSection, string sEntry, string fn, double dValue); + +static const IlwisDatums iwDatums[] = +{ + { "Adindan", "Adindan", 4201 }, + { "Afgooye", "Afgooye", 4205 }, + //AGREF --- skipped + { "Ain el Abd 1970", "Ain_el_Abd_1970", 4204 }, + { "American Samoa 1962", "American_Samoa_1962", 4169 }, + //Anna 1 Astro 1965 --- skipped + { "Antigua Island Astro 1943", "Antigua_1943", 4601 }, + { "Arc 1950", "Arc_1950", 4209 }, //Arc 1950 + { "Arc 1960", "Arc_1960", 4210 }, //Arc 1960 + //Ascension Island 1958 + //Astro Beacon E 1945 + //Astro DOS 71/4 + //Astro Tern Island (FRIG) 1961 + //Astronomical Station 1952 + { "Australian Geodetic 1966", "Australian_Geodetic_Datum_1966", 4202 }, + { "Australian Geodetic 1984", "Australian_Geodetic_Datum_1984", 4203 }, + //Ayabelle Lighthouse + //Bellevue (IGN) + { "Bermuda 1957", "Bermuda_1957", 4216 }, + { "Bissau", "Bissau", 4165 }, + { "Bogota Observatory (1975)", "Bogota", 4218 }, + { "Bukit Rimpah", "Bukit_Rimpah", 4219 }, + //Camp Area Astro + { "Campo Inchauspe", "Campo_Inchauspe", 4221 }, + //Canton Astro 1966 + { "Cape", "Cape", 4222 }, + //Cape Canaveral + { "Carthage", "Carthage", 4223 }, + { "CH1903", "CH1903", 4149 }, + //Chatham Island Astro 1971 + { "Chua Astro", "Chua", 4224 }, + { "Corrego Alegre", "Corrego_Alegre", 4225 }, + //Croatia + //D-PAF (Orbits) + { "Dabola", "Dabola_1981", 4155 }, + //Deception Island + //Djakarta (Batavia) + //DOS 1968 + //Easter Island 1967 + //Estonia 1937 + { "European 1950 (ED 50)", "European_Datum_1950", 4154 }, + //European 1979 (ED 79 + //Fort Thomas 1955 + { "Gan 1970", "Gandajika_1970", 4233 }, + //Geodetic Datum 1949 + //Graciosa Base SW 1948 + //Guam 1963 + { "Gunung Segara", "Gunung_Segara", 4613 }, + //GUX 1 Astro + { "Herat North", "Herat_North", 4255 }, + //Hermannskogel + //Hjorsey 1955 + //Hong Kong 1963 + { "Hu-Tzu-Shan", "Hu_Tzu_Shan", 4236 }, + //Indian (Bangladesh) + //Indian (India, Nepal) + //Indian (Pakistan) + { "Indian 1954", "Indian_1954", 4239 }, + { "Indian 1960", "Indian_1960", 4131 }, + { "Indian 1975", "Indian_1975", 4240 }, + { "Indonesian 1974", "Indonesian_Datum_1974", 4238 }, + //Ireland 1965 + //ISTS 061 Astro 1968 + //ISTS 073 Astro 1969 + //Johnston Island 1961 + { "Kandawala", "Kandawala", 4244 }, + //Kerguelen Island 1949 + { "Kertau 1948", "Kertau", 4245 }, + //Kusaie Astro 1951 + //L. C. 5 Astro 1961 + { "Leigon", "Leigon", 4250 }, + { "Liberia 1964", "Liberia_1964", 4251 }, + { "Luzon", "Luzon_1911", 4253 }, + //M'Poraloko + { "Mahe 1971", "Mahe_1971", 4256 }, + { "Massawa", "Massawa", 4262 }, + { "Merchich", "Merchich", 4261 }, + { "MGI (Hermannskogel)", "Militar_Geographische_Institute",4312 }, + //Midway Astro 1961 + { "Minna", "Minna", 4263 }, + { "Montserrat Island Astro 1958", "Montserrat_1958", 4604 }, + { "Nahrwan", "Nahrwan_1967", 4270 }, + { "Naparima BWI", "Naparima_1955", 4158 }, + { "North American 1927 (NAD 27)", "North_American_Datum_1927", 4267 }, + { "North American 1983 (NAD 83)", "North_American_Datum_1983", 4269 }, + //North Sahara 1959 + { "NTF (Nouvelle Triangulation de France)", "Nouvelle_Triangulation_Francaise", 4807 }, + //Observatorio Meteorologico 1939 + //Old Egyptian 1907 + { "Old Hawaiian", "Old_Hawaiian", 4135 }, + //Oman + //Ordnance Survey Great Britain 1936 + //Pico de las Nieves + //Pitcairn Astro 1967 + //Point 58 + { "Pointe Noire 1948", "Pointe_Noire", 4282 }, + { "Porto Santo 1936", "Porto_Santo",4615 }, + //Potsdam (Rauenburg) + { "Potsdam (Rauenburg)", "Deutsches_Hauptdreiecksnetz", 4314 }, + { "Provisional South American 1956", "Provisional_South_American_Datum_1956", 4248 }, + //Provisional South Chilean 1963 + { "Puerto Rico", "Puerto_Rico", 4139 }, + { "Pulkovo 1942", "Pulkovo_1942", 4178 }, + //{ "Qatar National", "Qatar_National_Datum_1995", 4614 }, + { "Qornoq", "Qornoq", 4287 }, + { "Puerto Rico", "Puerto_Rico", 4139 }, + //Reunion + { "Rome 1940", "Monte_Mario", 4806 }, + { "RT90", "Rikets_koordinatsystem_1990", 4124 }, + { "Rijks Driehoeksmeting", "Amersfoort", 4289 }, + { "S-42 (Pulkovo 1942)", "Pulkovo_1942", 4178 }, + //{ "S-JTSK", "Jednotne_Trigonometricke_Site_Katastralni", 4156 }, + //Santo (DOS) 1965 + //Sao Braz + { "Sapper Hill 1943", "Sapper_Hill_1943", 4292 }, + { "Schwarzeck", "Schwarzeck", 4293 }, + { "Selvagem Grande 1938", "Selvagem_Grande", 4616 }, + //vSGS 1985 + //Sierra Leone 1960 + { "South American 1969", "South_American_Datum_1969", 4291 }, + //South Asia + { "Tananarive Observatory 1925", "Tananarive_1925", 4297 }, + { "Timbalai 1948", "Timbalai_1948", 4298 }, + { "Tokyo", "Tokyo", 4301 }, + //Tristan Astro 1968 + //Viti Levu 1916 + { "Voirol 1874", "Voirol_1875", 4304 }, + //Voirol 1960 + //Wake Island Astro 1952 + //Wake-Eniwetok 1960 + { "WGS 1972", "WGS_1972", 4322 }, + { "WGS 1984", "WGS_1984", 4326 }, + { "Yacare", "Yacare", 4309 }, + { "Zanderij", "Zanderij", 4311 }, + { NULL, NULL, 0 } +}; + +static const IlwisEllips iwEllips[] = +{ + { "Sphere", 7035, 6371007, 0.0 }, //rad 6370997 m (normal sphere) + { "Airy 1830", 7031, 6377563.396, 299.3249646 }, + { "Modified Airy", 7002, 6377340.189, 299.3249646 }, + { "ATS77", 7204, 6378135.0, 298.257000006 }, + { "Australian National", 7003, 6378160, 298.249997276 }, + { "Bessel 1841", 7042, 6377397.155, 299.1528128}, + { "Bessel 1841 (Japan By Law)", 7046 , 6377397.155, 299.152815351 }, + { "Bessel 1841 (Namibia)", 7006, 6377483.865, 299.1528128 }, + { "Clarke 1866", 7008, 6378206.4, 294.9786982 }, + { "Clarke 1880", 7034, 6378249.145, 293.465 }, + { "Clarke 1880 (IGN)", 7011, 6378249.2, 293.466 }, + // FIXME: D-PAF (Orbits) --- skipped + // FIXME: Du Plessis Modified --- skipped + // FIXME: Du Plessis Reconstituted --- skipped + { "Everest (India 1830)", 7015, 6377276.345, 300.8017 }, + // Everest (India 1956) --- skipped + // Everest (Malaysia 1969) --- skipped + { "Everest (E. Malaysia and Brunei)", 7016, 6377298.556, 300.8017 }, + { "Everest (Malay. and Singapore 1948)", 7018, 6377304.063, 300.8017 }, + { "Everest (Pakistan)", 7044, 6377309.613, 300.8017 }, + // Everest (Sabah Sarawak) --- skipped + // Fischer 1960 --- skipped + // Fischer 1960 (Modified) --- skipped + // Fischer 1968 --- skipped + { "GRS 80", 7019, 6378137, 298.257222101 }, + { "Helmert 1906", 7020, 6378200, 298.3 }, + // Hough 1960 --- skipped + { "Indonesian 1974", 7021, 6378160, 298.247 }, + { "International 1924", 7022, 6378388, 297 }, + { "Krassovsky 1940", 7024, 6378245, 298.3 }, + // New_International 1967 + // SGS 85 + // South American 1969 + // WGS 60 + // WGS 66 + { "WGS 72", 7020, 6378135.0, 298.259998590 }, + { "WGS 84", 7030, 6378137, 298.257223563 }, + { NULL, 0, 0.0, 0.0 } +}; + +#ifndef PI +# define PI 3.14159265358979323846 +#endif + +#ifndef R2D +# define R2D (180/PI) +#endif +#ifndef D2R +# define D2R (PI/180) +#endif + +/* ==================================================================== */ +/* Some "standard" strings. */ +/* ==================================================================== */ + +#define ILW_False_Easting "False Easting" +#define ILW_False_Northing "False Northing" +#define ILW_Central_Meridian "Central Meridian" +#define ILW_Central_Parallel "Central Parallel" +#define ILW_Standard_Parallel_1 "Standard Parallel 1" +#define ILW_Standard_Parallel_2 "Standard Parallel 2" +#define ILW_Scale_Factor "Scale Factor" +#define ILW_Latitude_True_Scale "Latitude of True Scale" +#define ILW_Height_Persp_Center "Height Persp. Center" + +double ReadPrjParms(string section, string entry, string filename) +{ + string str = ReadElement(section, entry, filename); + //string str=""; + if (str.length() != 0) + return CPLAtof(str.c_str()); + else + return 0; +} + +static int fetchParms(string csyFileName, double * padfPrjParams) +{ + int i; + + //Fill all projection parameters with zero + for ( i = 0; i < 13; i++ ) + padfPrjParams[i] = 0.0; + + string pszProj = ReadElement("CoordSystem", "Projection", csyFileName); + string pszEllips = ReadElement("CoordSystem", "Ellipsoid", csyFileName); + + //fetch info about a custom ellipsoid + if( EQUALN( pszEllips.c_str(), "User Defined", 12 ) ) + { + padfPrjParams[0] = ReadPrjParms("Ellipsoid", "a", csyFileName); + padfPrjParams[2] = ReadPrjParms("Ellipsoid", "1/f", csyFileName); + } + else if( EQUALN( pszEllips.c_str(), "Sphere", 6 ) ) + { + padfPrjParams[0] = ReadPrjParms("CoordSystem", "Sphere Radius", csyFileName); + } + + padfPrjParams[3] = ReadPrjParms("Projection", "False Easting", csyFileName); + padfPrjParams[4] = ReadPrjParms("Projection", "False Northing", csyFileName); + + padfPrjParams[5] = ReadPrjParms("Projection", "Central Parallel", csyFileName); + padfPrjParams[6] = ReadPrjParms("Projection", "Central Meridian", csyFileName); + + padfPrjParams[7] = ReadPrjParms("Projection", "Standard Parallel 1", csyFileName); + padfPrjParams[8] = ReadPrjParms("Projection", "Standard Parallel 2", csyFileName); + + padfPrjParams[9] = ReadPrjParms("Projection", "Scale Factor", csyFileName); + padfPrjParams[10] = ReadPrjParms("Projection", "Latitude of True Scale", csyFileName); + padfPrjParams[11] = ReadPrjParms("Projection", "Zone", csyFileName); + padfPrjParams[12] = ReadPrjParms("Projection", ILW_Height_Persp_Center, csyFileName); + + return true; +} + +/************************************************************************/ +/* mapTMParms */ +/************************************************************************/ +/** + * fetch the parameters from ILWIS projection definition for + * --- Gauss-Krueger Germany. + * --- Gauss Colombia + * --- Gauss-Boaga Italy +**/ +static int mapTMParms(string sProj, double dfZone, double &dfFalseEasting, double &dfCentralMeridian) +{ + if( EQUALN( sProj.c_str(), "Gauss-Krueger Germany", 21 ) ) + { + //Zone number must be in the range 1 to 3 + dfCentralMeridian = 6.0 + (dfZone - 1) * 3; + dfFalseEasting = 2500000 + (dfZone - 1) * 1000000; + } + else if( EQUALN( sProj.c_str(), "Gauss-Boaga Italy", 17 ) ) + { + if ( dfZone == 1) + { + dfCentralMeridian = 9; + dfFalseEasting = 1500000; + } + else if ( dfZone == 2) + { + dfCentralMeridian = 15; + dfFalseEasting = 2520000; + } + else + return false; + } + else if( EQUALN( sProj.c_str(), "Gauss Colombia", 14 ) ) + { + //Zone number must be in the range 1 to 4 + dfCentralMeridian = -77.08097220 + (dfZone - 1) * 3; + } + return true; +} + +/************************************************************************/ +/* scaleFromLATTS() */ +/************************************************************************/ +/** + * Compute the scale factor from Latitude_Of_True_Scale parameter. + * +**/ +static void scaleFromLATTS( string sEllips, double phits, double &scale ) +{ + if( EQUALN( sEllips.c_str(), "Sphere", 6 ) ) + { + scale = cos(phits); + } + else + { + const IlwisEllips *piwEllips = iwEllips; + double e2 = 0.0; + while ( piwEllips->pszIlwisEllips ) + { + if( EQUALN( sEllips.c_str(), piwEllips->pszIlwisEllips, strlen(piwEllips->pszIlwisEllips) ) ) + { + double a = piwEllips->semiMajor; + double b = a * ( 1 - piwEllips->invFlattening); + e2 = ( a*a - b*b ) /( a*a ); + break; + } + piwEllips++; + } + scale = cos(phits) / sqrt (1. - e2 * sin(phits) * sin(phits)); + } +} + +/************************************************************************/ +/* ReadProjection() */ +/************************************************************************/ + +/** + * Import coordinate system from ILWIS projection definition. + * + * The method will import projection definition in ILWIS, + * It uses 13 parameters to define the coordinate system + * and datum/ellipsoid specified in the padfPrjParams array. + * + * @param csyFileName Name of .csy file +**/ + +CPLErr ILWISDataset::ReadProjection( string csyFileName ) +{ + string pszEllips; + string pszDatum; + string pszProj; + + //translate ILWIS pre-defined coordinate systems + if( EQUALN( csyFileName.c_str(), "latlon.csy", 10 )) + { + pszProj = "LatLon"; + pszDatum = ""; + pszEllips = "Sphere"; + } + else if ( EQUALN( csyFileName.c_str(), "LatlonWGS84.csy", 15 )) + { + pszProj = "LatLon"; + pszDatum = "WGS 1984"; + pszEllips = "WGS 84"; + } + else + { + pszProj = ReadElement("CoordSystem", "Type", csyFileName); + if( !EQUALN( pszProj.c_str(), "LatLon", 7 ) ) + pszProj = ReadElement("CoordSystem", "Projection", csyFileName); + pszDatum = ReadElement("CoordSystem", "Datum", csyFileName); + pszEllips = ReadElement("CoordSystem", "Ellipsoid", csyFileName); + } + +/* -------------------------------------------------------------------- */ +/* Fetch array containing 13 coordinate system parameters */ +/* -------------------------------------------------------------------- */ + double padfPrjParams[13]; + fetchParms(csyFileName, padfPrjParams); + + OGRSpatialReference oSRS; +/* -------------------------------------------------------------------- */ +/* Operate on the basis of the projection name. */ +/* -------------------------------------------------------------------- */ + if( EQUALN( pszProj.c_str(), "LatLon", 7 ) ) + { + //set datum later + } + else if( EQUALN( pszProj.c_str(), "Albers EqualArea Conic", 22 ) ) + { + oSRS.SetProjCS("Albers EqualArea Conic"); + oSRS.SetACEA( padfPrjParams[7], padfPrjParams[8], + padfPrjParams[5], padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + + } + else if( EQUALN( pszProj.c_str(), "Azimuthal Equidistant", 21 ) ) + { + oSRS.SetProjCS("Azimuthal Equidistant"); + oSRS.SetAE( padfPrjParams[5], padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Central Cylindrical", 19 ) ) + { + //Use Central Parallel for dfStdP1 + //padfPrjParams[5] is always to zero + oSRS.SetProjCS("Central Cylindrical"); + oSRS.SetCEA( padfPrjParams[5], padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Cassini", 7 ) ) + { + //Use Latitude_Of_True_Scale for dfCenterLat + //Scale Factor 1.0 should always be defined + oSRS.SetProjCS("Cassini"); + oSRS.SetCS( padfPrjParams[10], padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "DutchRD", 7 ) ) + { + oSRS.SetProjCS("DutchRD"); + oSRS.SetStereographic ( 52.156160556, 5.387638889, + 0.9999079, + 155000, 463000); + + } + else if( EQUALN( pszProj.c_str(), "Equidistant Conic", 17 ) ) + { + oSRS.SetProjCS("Equidistant Conic"); + oSRS.SetEC( padfPrjParams[7], padfPrjParams[8], + padfPrjParams[5], padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Gauss-Krueger Germany", 21 ) ) + { + //FalseNorthing and CenterLat are always set to 0 + //Scale 1.0 is defined + //FalseEasting and CentralMeridian are defined by the selected zone + mapTMParms("Gauss-Krueger Germany", padfPrjParams[11], + padfPrjParams[3], padfPrjParams[6]); + oSRS.SetProjCS("Gauss-Krueger Germany"); + oSRS.SetTM( 0, padfPrjParams[6], + 1.0, + padfPrjParams[3], 0 ); + } + else if ( EQUALN( pszProj.c_str(),"Gauss-Boaga Italy", 17 ) ) + { + //FalseNorthing and CenterLat are always set to 0 + //Scale 0.9996 is defined + //FalseEasting and CentralMeridian are defined by the selected zone + mapTMParms("Gauss-Boaga Italy", padfPrjParams[11], + padfPrjParams[3], padfPrjParams[6]); + oSRS.SetProjCS("Gauss-Boaga Italy"); + oSRS.SetTM( 0, padfPrjParams[6], + 0.9996, + padfPrjParams[3], 0 ); + } + else if ( EQUALN( pszProj.c_str(),"Gauss Colombia", 14 )) + { + // 1000000 used for FalseNorthing and FalseEasting + // 1.0 used for scale + // CenterLat is defined 45.1609259259259 + // CentralMeridian is defined by the selected zone + mapTMParms("Gauss Colombia", padfPrjParams[11], + padfPrjParams[3], padfPrjParams[6]); + oSRS.SetProjCS("Gauss Colombia"); + oSRS.SetTM( 45.1609259259259, padfPrjParams[6], + 1.0, + 1000000, 1000000 ); + } + else if( EQUALN( pszProj.c_str(), "Gnomonic", 8 ) ) + { + oSRS.SetProjCS("Gnomonic"); + oSRS.SetGnomonic( padfPrjParams[5], padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Lambert Conformal Conic", 23 ) ) + { + // should use 1.0 for scale factor in Ilwis definition + oSRS.SetProjCS("Lambert Conformal Conic"); + oSRS.SetLCC( padfPrjParams[7], padfPrjParams[8], + padfPrjParams[5], padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Lambert Cylind EqualArea", 24 ) ) + { + // Latitude_Of_True_Scale used for dfStdP1 ? + oSRS.SetProjCS("Lambert Conformal Conic"); + oSRS.SetCEA( padfPrjParams[10], + padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Mercator", 8 ) ) + { + // use 0 for CenterLat, scale is computed from the + // Latitude_Of_True_Scale + scaleFromLATTS( pszEllips, padfPrjParams[10], padfPrjParams[9] ); + oSRS.SetProjCS("Mercator"); + oSRS.SetMercator( 0, padfPrjParams[6], + padfPrjParams[9], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Miller", 6 ) ) + { + // use 0 for CenterLat + oSRS.SetProjCS("Miller"); + oSRS.SetMC( 0, padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Mollweide", 9 ) ) + { + oSRS.SetProjCS("Mollweide"); + oSRS.SetMollweide( padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Orthographic", 12 ) ) + { + oSRS.SetProjCS("Orthographic"); + oSRS.SetOrthographic ( padfPrjParams[5], padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Plate Carree", 12 ) || + EQUALN( pszProj.c_str(), "Plate Rectangle", 15 )) + { + // set 0.0 for CenterLat for Plate Carree projection + // skipp Latitude_Of_True_Scale for Plate Rectangle projection definition + oSRS.SetProjCS(pszProj.c_str()); + oSRS.SetEquirectangular( padfPrjParams[5], padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "PolyConic", 9 ) ) + { + // skipp scale factor + oSRS.SetProjCS("PolyConic"); + oSRS.SetPolyconic( padfPrjParams[5], padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Robinson", 8 ) ) + { + oSRS.SetProjCS("Robinson"); + oSRS.SetRobinson( padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Sinusoidal", 10 ) ) + { + oSRS.SetProjCS("Sinusoidal"); + oSRS.SetSinusoidal( padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "Stereographic", 13 ) ) + { + oSRS.SetProjCS("Stereographic"); + oSRS.SetStereographic( padfPrjParams[5], padfPrjParams[6], + padfPrjParams[9], + padfPrjParams[3], padfPrjParams[4] ); + + } + else if( EQUALN( pszProj.c_str(), "Transverse Mercator", 19 ) ) + { + oSRS.SetProjCS("Transverse Mercator"); + oSRS.SetStereographic( padfPrjParams[5], padfPrjParams[6], + padfPrjParams[9], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "UTM", 3 ) ) + { + string pszNH = ReadElement("Projection", "Northern Hemisphere", csyFileName); + oSRS.SetProjCS("UTM"); + if( EQUALN( pszNH.c_str(), "Yes", 3 ) ) + oSRS.SetUTM( (int) padfPrjParams[11], 1); + else + oSRS.SetUTM( (int) padfPrjParams[11], 0); + } + else if( EQUALN( pszProj.c_str(), "VanderGrinten", 13 ) ) + { + oSRS.SetVDG( padfPrjParams[6], + padfPrjParams[3], padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "GeoStationary Satellite", 23 ) ) + { + oSRS.SetGEOS( padfPrjParams[6], + padfPrjParams[12], + padfPrjParams[3], + padfPrjParams[4] ); + } + else if( EQUALN( pszProj.c_str(), "MSG Perspective", 15 ) ) + { + oSRS.SetGEOS( padfPrjParams[6], + padfPrjParams[12], + padfPrjParams[3], + padfPrjParams[4] ); + } + else + { + oSRS.SetLocalCS( pszProj.c_str() ); + } +/* -------------------------------------------------------------------- */ +/* Try to translate the datum/spheroid. */ +/* -------------------------------------------------------------------- */ + + if ( !oSRS.IsLocal() ) + { + const IlwisDatums *piwDatum = iwDatums; + + // Search for matching datum + while ( piwDatum->pszIlwisDatum ) + { + if( EQUALN( pszDatum.c_str(), piwDatum->pszIlwisDatum, strlen(piwDatum->pszIlwisDatum) ) ) + { + OGRSpatialReference oOGR; + oOGR.importFromEPSG( piwDatum->nEPSGCode ); + oSRS.CopyGeogCSFrom( &oOGR ); + break; + } + piwDatum++; + } //end of searchong for matching datum + + +/* -------------------------------------------------------------------- */ +/* If no matching for datum definition, fetch info about an */ +/* ellipsoid. semi major axis is always returned in meters */ +/* -------------------------------------------------------------------- */ + const IlwisEllips *piwEllips = iwEllips; + if (pszEllips.length() == 0) + pszEllips="Sphere"; + if ( !piwDatum->pszIlwisDatum ) + + { + while ( piwEllips->pszIlwisEllips ) + { + if( EQUALN( pszEllips.c_str(), piwEllips->pszIlwisEllips, strlen(piwEllips->pszIlwisEllips) ) ) + { + double dfSemiMajor = piwEllips->semiMajor; + if( EQUALN( pszEllips.c_str(), "Sphere", 6 ) && padfPrjParams[0] != 0 ) + { + dfSemiMajor = padfPrjParams[0]; + } + oSRS.SetGeogCS( CPLSPrintf( + "Unknown datum based upon the %s ellipsoid", + piwEllips->pszIlwisEllips ), + CPLSPrintf( + "Not specified (based on %s spheroid)", + piwEllips->pszIlwisEllips ), + piwEllips->pszIlwisEllips, + dfSemiMajor, + piwEllips->invFlattening, + NULL, 0.0, NULL, 0.0 ); + oSRS.SetAuthority( "SPHEROID", "EPSG", piwEllips->nEPSGCode ); + + break; + } + piwEllips++; + } //end of searching for matching ellipsoid + } + +/* -------------------------------------------------------------------- */ +/* If no matching for ellipsoid definition, fetch info about an */ +/* user defined ellipsoid. If cannot find, default to WGS 84 */ +/* -------------------------------------------------------------------- */ + if ( !piwEllips->pszIlwisEllips ) + { + + if( EQUALN( pszEllips.c_str(), "User Defined", 12 ) ) + { + + oSRS.SetGeogCS( "Unknown datum based upon the custom ellipsoid", + "Not specified (based on custom ellipsoid)", + "Custom ellipsoid", + padfPrjParams[0], padfPrjParams[2], + NULL, 0, NULL, 0 ); + } + else + { + //if cannot find the user defined ellips, default to WGS84 + oSRS.SetWellKnownGeogCS( "WGS84" ); + } + } + + } // end of if ( !IsLocal() ) + +/* -------------------------------------------------------------------- */ +/* Units translation */ +/* -------------------------------------------------------------------- */ + if( oSRS.IsLocal() || oSRS.IsProjected() ) + { + oSRS.SetLinearUnits( SRS_UL_METER, 1.0 ); + } + oSRS.FixupOrdering(); + CPLFree(pszProjection); + oSRS.exportToWkt( &pszProjection ); + + + return CE_None; +} + +void WriteFalseEastNorth(string csFileName, OGRSpatialReference oSRS) +{ + WriteElement("Projection", ILW_False_Easting, csFileName, + oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0)); + WriteElement("Projection", ILW_False_Northing, csFileName, + oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0)); +} + +void WriteProjectionName(string csFileName, string stProjection) +{ + WriteElement("CoordSystem", "Type", csFileName, "Projection"); + WriteElement("CoordSystem", "Projection", csFileName, stProjection); +} + +void WriteUTM(string csFileName, OGRSpatialReference oSRS) +{ + int bNorth, nZone; + + nZone = oSRS.GetUTMZone( &bNorth ); + WriteElement("CoordSystem", "Type", csFileName, "Projection"); + WriteElement("CoordSystem", "Projection", csFileName, "UTM"); + if (bNorth) + WriteElement("Projection", "Northern Hemisphere", csFileName, "Yes"); + else + WriteElement("Projection", "Northern Hemisphere", csFileName, "No"); + WriteElement("Projection", "Zone", csFileName, nZone); +} + +void WriteAlbersConicEqualArea(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Albers EqualArea Conic"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Central_Parallel, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + WriteElement("Projection", ILW_Standard_Parallel_1, csFileName, + oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0)); + WriteElement("Projection", ILW_Standard_Parallel_2, csFileName, + oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0)); +} +void WriteAzimuthalEquidistant(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Azimuthal Equidistant"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Central_Parallel, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + WriteElement("Projection", ILW_Scale_Factor, csFileName, "1.0000000000"); +} +void WriteCylindricalEqualArea(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Central Cylindrical"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); +} + +void WriteCassiniSoldner(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Cassini"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Latitude_True_Scale, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + WriteElement("Projection", ILW_Scale_Factor, csFileName, "1.0000000000"); +} + +void WriteStereographic(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Stereographic"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Central_Parallel, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + WriteElement("Projection", ILW_Scale_Factor, csFileName, + oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 0.0)); +} + +void WriteEquidistantConic(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Equidistant Conic"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Central_Parallel, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + WriteElement("Projection", ILW_Standard_Parallel_1, csFileName, + oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0)); + WriteElement("Projection", ILW_Standard_Parallel_2, csFileName, + oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0)); +} + +void WriteTransverseMercator(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Transverse Mercator"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Central_Parallel, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + WriteElement("Projection", ILW_Scale_Factor, csFileName, + oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 0.0)); +} + +void WriteGnomonic(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Gnomonic"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Central_Parallel, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); +} + +void WriteLambertConformalConic(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Lambert Conformal Conic"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Central_Parallel, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + WriteElement("Projection", ILW_Scale_Factor, csFileName, "1.0000000000"); +} + +void WriteLambertConformalConic2SP(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Lambert Conformal Conic"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Central_Parallel, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + WriteElement("Projection", ILW_Scale_Factor, csFileName, "1.0000000000"); + WriteElement("Projection", ILW_Standard_Parallel_1, csFileName, + oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0)); + WriteElement("Projection", ILW_Standard_Parallel_2, csFileName, + oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0)); +} + +void WriteLambertAzimuthalEqualArea(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Lambert Azimuthal EqualArea"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Central_Parallel, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); +} + +void WriteMercator_1SP(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Mercator"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Latitude_True_Scale, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); +} + +void WriteMillerCylindrical(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Miller"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); +} + +void WriteMolleweide(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Mollweide"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); +} + +void WriteOrthographic(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Orthographic"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Central_Parallel, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); +} + +void WritePlateRectangle(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Plate Rectangle"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Central_Parallel, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + WriteElement("Projection", ILW_Latitude_True_Scale, csFileName, "0.0000000000"); +} + +void WritePolyConic(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "PolyConic"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Central_Parallel, csFileName, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0)); + WriteElement("Projection", ILW_Scale_Factor, csFileName, "1.0000000000"); +} + +void WriteRobinson(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Robinson"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); +} + +void WriteSinusoidal(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "Sinusoidal"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); +} + +void WriteVanderGrinten(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "VanderGrinten"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); +} + +void WriteGeoStatSat(string csFileName, OGRSpatialReference oSRS) +{ + WriteProjectionName(csFileName, "GeoStationary Satellite"); + WriteFalseEastNorth(csFileName, oSRS); + WriteElement("Projection", ILW_Central_Meridian, csFileName, + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)); + WriteElement("Projection", ILW_Scale_Factor, csFileName, "1.0000000000"); + WriteElement("Projection", ILW_Height_Persp_Center, csFileName, + oSRS.GetNormProjParm(SRS_PP_SATELLITE_HEIGHT, 35785831.0)); +} + +/************************************************************************/ +/* WriteProjection() */ +/************************************************************************/ +/** + * Export coordinate system in ILWIS projection definition. + * + * Converts the loaded coordinate reference system into ILWIS projection + * definition to the extent possible. + */ +CPLErr ILWISDataset::WriteProjection() + +{ + OGRSpatialReference oSRS; + OGRSpatialReference *poGeogSRS = NULL; + int bHaveSRS; + char *pszP = pszProjection; + + string csFileName = CPLResetExtension(osFileName, "csy" ); + string pszBaseName = string(CPLGetBasename( osFileName )); + string pszPath = string(CPLGetPath( osFileName )); + bool fProjection = ((strlen(pszProjection)>0) && (pszProjection != NULL)); + if( fProjection && (oSRS.importFromWkt( &pszP ) == OGRERR_NONE) ) + { + bHaveSRS = TRUE; + } + else + bHaveSRS = FALSE; + + const IlwisDatums *piwDatum = iwDatums; + string pszEllips; + string pszDatum; + string pszProj; + +/* -------------------------------------------------------------------- */ +/* Collect datum/ellips information. */ +/* -------------------------------------------------------------------- */ + if( bHaveSRS ) + { + poGeogSRS = oSRS.CloneGeogCS(); + } + + string grFileName = CPLResetExtension(osFileName, "grf" ); + string csy; + if( poGeogSRS ) + { + csy = pszBaseName + ".csy"; + + WriteElement("Ilwis", "Type", csFileName, "CoordSystem"); + pszDatum = poGeogSRS->GetAttrValue( "GEOGCS|DATUM" ); + + /* WKT to ILWIS translation */ + while ( piwDatum->pszWKTDatum) + { + if( EQUALN( pszDatum.c_str(), piwDatum->pszWKTDatum, strlen(piwDatum->pszWKTDatum) ) ) + { + WriteElement("CoordSystem", "Datum", csFileName, piwDatum->pszIlwisDatum); + break; + } + piwDatum++; + } //end of searchong for matching datum + WriteElement("CoordSystem", "Width", csFileName, 28); + double a, /* b, */f; + pszEllips = poGeogSRS->GetAttrValue( "GEOGCS|DATUM|SPHEROID" ); + a = poGeogSRS->GetSemiMajor(); + /* b = */ poGeogSRS->GetSemiMinor(); + f = poGeogSRS->GetInvFlattening(); + WriteElement("CoordSystem", "Ellipsoid", csFileName, "User Defined"); + WriteElement("Ellipsoid", "a", csFileName, a); + WriteElement("Ellipsoid", "1/f", csFileName, f); + } + else + csy = "unknown.csy"; + +/* -------------------------------------------------------------------- */ +/* Determine to write a geo-referencing file for the dataset to create */ +/* -------------------------------------------------------------------- */ + if( adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 + || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 + || adfGeoTransform[4] != 0.0 || fabs(adfGeoTransform[5]) != 1.0) + WriteElement("GeoRef", "CoordSystem", grFileName, csy); + +/* -------------------------------------------------------------------- */ +/* Recognise various projections. */ +/* -------------------------------------------------------------------- */ + const char * pszProjName = NULL; + + if( bHaveSRS ) + pszProjName = oSRS.GetAttrValue( "PROJCS|PROJECTION" ); + + if( pszProjName == NULL ) + { + if( bHaveSRS && oSRS.IsGeographic() ) + { + WriteElement("CoordSystem", "Type", csFileName, "LatLon"); + } + } + else if( oSRS.GetUTMZone( NULL ) != 0 ) + { + WriteUTM(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_ALBERS_CONIC_EQUAL_AREA) ) + { + WriteAlbersConicEqualArea(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_AZIMUTHAL_EQUIDISTANT) ) + { + WriteAzimuthalEquidistant(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_CYLINDRICAL_EQUAL_AREA) ) + { + WriteCylindricalEqualArea(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_CASSINI_SOLDNER) ) + { + WriteCassiniSoldner(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_STEREOGRAPHIC) ) + { + WriteStereographic(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_EQUIDISTANT_CONIC) ) + { + WriteEquidistantConic(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_TRANSVERSE_MERCATOR) ) + { + WriteTransverseMercator(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_GNOMONIC) ) + { + WriteGnomonic(csFileName, oSRS); + } + else if( EQUAL(pszProjName,"Lambert_Conformal_Conic") ) + { + WriteLambertConformalConic(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) ) + { + WriteLambertConformalConic(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ) + { + WriteLambertConformalConic2SP(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) + { + WriteLambertAzimuthalEqualArea(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_MERCATOR_1SP) ) + { + WriteMercator_1SP(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_MILLER_CYLINDRICAL) ) + { + WriteMillerCylindrical(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_MOLLWEIDE) ) + { + WriteMolleweide(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_ORTHOGRAPHIC) ) + { + WriteOrthographic(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_EQUIRECTANGULAR) ) + { + WritePlateRectangle(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_POLYCONIC) ) + { + WritePolyConic(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_ROBINSON) ) + { + WriteRobinson(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_SINUSOIDAL) ) + { + WriteSinusoidal(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_VANDERGRINTEN) ) + { + WriteVanderGrinten(csFileName, oSRS); + } + else if( EQUAL(pszProjName,SRS_PT_GEOSTATIONARY_SATELLITE) ) + { + WriteGeoStatSat(csFileName, oSRS); + } + else + { + // Projection unknown by ILWIS + + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + if( poGeogSRS != NULL ) + delete poGeogSRS; + + return CE_None; +} diff --git a/bazaar/plugin/gdal/frmts/ilwis/ilwisdataset.cpp b/bazaar/plugin/gdal/frmts/ilwis/ilwisdataset.cpp new file mode 100644 index 000000000..b0ba8aa6d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ilwis/ilwisdataset.cpp @@ -0,0 +1,2062 @@ +/****************************************************************************** + * + * Project: ILWIS Driver + * Purpose: GDALDataset driver for ILWIS translator for read/write support. + * Author: Lichun Wang, lichun@itc.nl + * + ****************************************************************************** + * Copyright (c) 2004, ITC + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + +#include "ilwisdataset.h" +#include +#include + +#include + +using namespace std; + +// IniFile.cpp: implementation of the IniFile class. +// +////////////////////////////////////////////////////////////////////// +bool CompareAsNum::operator() (const string& s1, const string& s2) const +{ + long Num1 = atoi(s1.c_str()); + long Num2 = atoi(s2.c_str()); + return Num1 < Num2; +} + +static string TrimSpaces(const string& input) +{ + // find first non space + if ( input.empty()) + return string(); + + size_t iFirstNonSpace = input.find_first_not_of(' '); + size_t iFindLastSpace = input.find_last_not_of(' '); + if (iFirstNonSpace == string::npos || iFindLastSpace == string::npos) + return string(); + + return input.substr(iFirstNonSpace, iFindLastSpace - iFirstNonSpace + 1); +} + +static string GetLine(VSILFILE* fil) +{ + const char *p = CPLReadLineL( fil ); + if (p == NULL) + return string(); + + CPLString osWrk = p; + osWrk.Trim(); + return string(osWrk); +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +IniFile::IniFile(const string& filenam) +{ + filename = filenam; + Load(); + bChanged = false; // Start tracking changes +} + +IniFile::~IniFile() +{ + if (bChanged) + { + Store(); + bChanged = false; + } + + for (Sections::iterator iter = sections.begin(); iter != sections.end(); ++iter) + { + (*(*iter).second).clear(); + delete (*iter).second; + } + + sections.clear(); +} + +void IniFile::SetKeyValue(const string& section, const string& key, const string& value) +{ + Sections::iterator iterSect = sections.find(section); + if (iterSect == sections.end()) + { + // Add a new section, with one new key/value entry + SectionEntries *entries = new SectionEntries; + (*entries)[key] = value; + sections[section] = entries; + } + else + { + // Add one new key/value entry in an existing section + SectionEntries *entries = (*iterSect).second; + (*entries)[key] = value; + } + bChanged = true; +} + +string IniFile::GetKeyValue(const string& section, const string& key) +{ + Sections::iterator iterSect = sections.find(section); + if (iterSect != sections.end()) + { + SectionEntries *entries = (*iterSect).second; + SectionEntries::iterator iterEntry = (*entries).find(key); + if (iterEntry != (*entries).end()) + return (*iterEntry).second; + } + + return string(); +} + +void IniFile::RemoveKeyValue(const string& section, const string& key) +{ + Sections::iterator iterSect = sections.find(section); + if (iterSect != sections.end()) + { + // The section exists, now erase entry "key" + SectionEntries *entries = (*iterSect).second; + (*entries).erase(key); + bChanged = true; + } +} + +void IniFile::RemoveSection(const string& section) +{ + Sections::iterator iterSect = sections.find(section); + if (iterSect != sections.end()) + { + // The section exists, so remove it and all its entries. + SectionEntries *entries = (*iterSect).second; + (*entries).clear(); + sections.erase(iterSect); + bChanged = true; + } +} + +void IniFile::Load() +{ + enum ParseState { FindSection, FindKey, ReadFindKey, StoreKey, None } state; + VSILFILE *filIni = VSIFOpenL(filename.c_str(), "r"); + if (filIni == NULL) + return; + + string section, key, value; + state = FindSection; + string s; + while (!VSIFEofL(filIni) || !s.empty() ) + { + switch (state) + { + case FindSection: + s = GetLine(filIni); + if (s.empty()) + continue; + + if (s[0] == '[') + { + size_t iLast = s.find_first_of(']'); + if (iLast != string::npos) + { + section = s.substr(1, iLast - 1); + state = ReadFindKey; + } + } + else + state = FindKey; + break; + case ReadFindKey: + s = GetLine(filIni); // fall through (no break) + case FindKey: + { + size_t iEqu = s.find_first_of('='); + if (iEqu != string::npos) + { + key = s.substr(0, iEqu); + value = s.substr(iEqu + 1); + state = StoreKey; + } + else + state = ReadFindKey; + } + break; + case StoreKey: + SetKeyValue(section, key, value); + state = FindSection; + break; + + case None: + // Do we need to do anything? Perhaps this never occurs. + break; + } + } + + VSIFCloseL(filIni); +} + +void IniFile::Store() +{ + VSILFILE *filIni = VSIFOpenL(filename.c_str(), "w+"); + if (filIni == NULL) + return; + + Sections::iterator iterSect; + for (iterSect = sections.begin(); iterSect != sections.end(); ++iterSect) + { + CPLString osLine; + + // write the section name + osLine.Printf( "[%s]\r\n", (*iterSect).first.c_str()); + VSIFWriteL( osLine.c_str(), 1, strlen(osLine), filIni ); + SectionEntries *entries = (*iterSect).second; + SectionEntries::iterator iterEntry; + for (iterEntry = (*entries).begin(); iterEntry != (*entries).end(); ++iterEntry) + { + string key = (*iterEntry).first; + osLine.Printf( "%s=%s\r\n", + TrimSpaces(key).c_str(), (*iterEntry).second.c_str()); + VSIFWriteL( osLine.c_str(), 1, strlen(osLine), filIni ); + } + + VSIFWriteL( "\r\n", 1, 2, filIni ); + } + + VSIFCloseL( filIni ); +} + +// End of the implementation of IniFile class. /////////////////////// +////////////////////////////////////////////////////////////////////// + +static long longConv(double x) { + if ((x == rUNDEF) || (x > LONG_MAX) || (x < LONG_MIN)) + return iUNDEF; + else + return (long)floor(x + 0.5); +} + +string ReadElement(string section, string entry, string filename) +{ + if (section.length() == 0) + return string(); + if (entry.length() == 0) + return string(); + if (filename.length() == 0) + return string(); + + IniFile MyIniFile (filename); + + return MyIniFile.GetKeyValue(section, entry);; +} + +bool WriteElement(string sSection, string sEntry, + string fn, string sValue) +{ + if (0 == fn.length()) + return false; + + IniFile MyIniFile (fn); + + MyIniFile.SetKeyValue(sSection, sEntry, sValue); + return true; +} + +bool WriteElement(string sSection, string sEntry, + string fn, int nValue) +{ + if (0 == fn.length()) + return false; + + char strdouble[45]; + sprintf(strdouble, "%d", nValue); + string sValue = string(strdouble); + return WriteElement(sSection, sEntry, fn, sValue) != 0; +} + +bool WriteElement(string sSection, string sEntry, + string fn, double dValue) +{ + if (0 == fn.length()) + return false; + + char strdouble[45]; + CPLsprintf(strdouble, "%.6f", dValue); + string sValue = string(strdouble); + return WriteElement(sSection, sEntry, fn, sValue) != 0; +} + +static CPLErr GetRowCol(string str,int &Row, int &Col) +{ + string delimStr = " ,;"; + size_t iPos = str.find_first_of(delimStr); + if (iPos != string::npos) + { + Row = atoi(str.substr(0, iPos).c_str()); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Read of RowCol failed."); + return CE_Failure; + } + iPos = str.find_last_of(delimStr); + if (iPos != string::npos) + { + Col = atoi(str.substr(iPos+1, str.length()-iPos).c_str()); + } + return CE_None; +} + +//! Converts ILWIS data type to GDAL data type. +static GDALDataType ILWIS2GDALType(ilwisStoreType stStoreType) +{ + GDALDataType eDataType = GDT_Unknown; + + switch (stStoreType){ + case stByte: { + eDataType = GDT_Byte; + break; + } + case stInt:{ + eDataType = GDT_Int16; + break; + } + case stLong:{ + eDataType = GDT_Int32; + break; + } + case stFloat:{ + eDataType = GDT_Float32; + break; + } + case stReal:{ + eDataType = GDT_Float64; + break; + } + default: { + break; + } + } + + return eDataType; +} + +//Determine store type of ILWIS raster +static string GDALType2ILWIS(GDALDataType type) +{ + string sStoreType; + sStoreType = ""; + switch( type ) + { + case GDT_Byte:{ + sStoreType = "Byte"; + break; + } + case GDT_Int16: + case GDT_UInt16:{ + sStoreType = "Int"; + break; + } + case GDT_Int32: + case GDT_UInt32:{ + sStoreType = "Long"; + break; + } + case GDT_Float32:{ + sStoreType = "Float"; + break; + } + case GDT_Float64:{ + sStoreType = "Real"; + break; + } + default:{ + CPLError( CE_Failure, CPLE_NotSupported, + "Data type %s not supported by ILWIS format.\n", + GDALGetDataTypeName( type ) ); + break; + } + } + return sStoreType; +} + +static CPLErr GetStoreType(string pszFileName, ilwisStoreType &stStoreType) +{ + string st = ReadElement("MapStore", "Type", pszFileName.c_str()); + + if( EQUAL(st.c_str(),"byte")) + { + stStoreType = stByte; + } + else if( EQUAL(st.c_str(),"int")) + { + stStoreType = stInt; + } + else if( EQUAL(st.c_str(),"long")) + { + stStoreType = stLong; + } + else if( EQUAL(st.c_str(),"float")) + { + stStoreType = stFloat; + } + else if( EQUAL(st.c_str(),"real")) + { + stStoreType = stReal; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported ILWIS store type."); + return CE_Failure; + } + return CE_None; +} + + +ILWISDataset::ILWISDataset() + +{ + bGeoDirty = FALSE; + bNewDataset = FALSE; + pszProjection = CPLStrdup(""); + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~ILWISDataset() */ +/************************************************************************/ + +ILWISDataset::~ILWISDataset() + +{ + FlushCache(); + CPLFree( pszProjection ); +} + +/************************************************************************/ +/* CollectTransformCoef() */ +/* */ +/* Collect the geotransform, support for the GeoRefCorners */ +/* georeferencing only; We use the extent of the coordinates */ +/* to determine the pixelsize in X and Y direction. Then calculate */ +/* the transform coefficients from the extent and pixelsize */ +/************************************************************************/ + +void ILWISDataset::CollectTransformCoef(string &pszRefName) + +{ + pszRefName = ""; + string georef; + if ( EQUAL(pszFileType.c_str(),"Map") ) + georef = ReadElement("Map", "GeoRef", osFileName); + else + georef = ReadElement("MapList", "GeoRef", osFileName); + + //Capture the geotransform, only if the georef is not 'none', + //otherwise, the default transform should be returned. + if( (georef.length() != 0) && !EQUAL(georef.c_str(),"none")) + { + //Form the geo-referencing name + string pszBaseName = string(CPLGetBasename(georef.c_str()) ); + string pszPath = string(CPLGetPath( osFileName )); + pszRefName = string(CPLFormFilename(pszPath.c_str(), + pszBaseName.c_str(),"grf" )); + + //Check the geo-reference type,support for the GeoRefCorners only + string georeftype = ReadElement("GeoRef", "Type", pszRefName); + if (EQUAL(georeftype.c_str(),"GeoRefCorners")) + { + //Center or top-left corner of the pixel approach? + string IsCorner = ReadElement("GeoRefCorners", "CornersOfCorners", pszRefName); + + //Collect the extent of the coordinates + string sMinX = ReadElement("GeoRefCorners", "MinX", pszRefName); + string sMinY = ReadElement("GeoRefCorners", "MinY", pszRefName); + string sMaxX = ReadElement("GeoRefCorners", "MaxX", pszRefName); + string sMaxY = ReadElement("GeoRefCorners", "MaxY", pszRefName); + + //Calculate pixel size in X and Y direction from the extent + double deltaX = CPLAtof(sMaxX.c_str()) - CPLAtof(sMinX.c_str()); + double deltaY = CPLAtof(sMaxY.c_str()) - CPLAtof(sMinY.c_str()); + + double PixelSizeX = deltaX / (double)nRasterXSize; + double PixelSizeY = deltaY / (double)nRasterYSize; + + if (EQUAL(IsCorner.c_str(),"Yes")) + { + adfGeoTransform[0] = CPLAtof(sMinX.c_str()); + adfGeoTransform[3] = CPLAtof(sMaxY.c_str()); + } + else + { + adfGeoTransform[0] = CPLAtof(sMinX.c_str()) - PixelSizeX/2.0; + adfGeoTransform[3] = CPLAtof(sMaxY.c_str()) + PixelSizeY/2.0; + } + + adfGeoTransform[1] = PixelSizeX; + adfGeoTransform[2] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = -PixelSizeY; + } + + } + +} + +/************************************************************************/ +/* WriteGeoReference() */ +/* */ +/* Try to write a geo-reference file for the dataset to create */ +/************************************************************************/ + +CPLErr ILWISDataset::WriteGeoReference() +{ + //check wheather we should write out a georeference file. + //dataset must be north up + if( adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 + || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 + || adfGeoTransform[4] != 0.0 || fabs(adfGeoTransform[5]) != 1.0 ) + { + SetGeoTransform( adfGeoTransform ); // is this needed? + if (adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0) + { + int nXSize = GetRasterXSize(); + int nYSize = GetRasterYSize(); + double dLLLat = (adfGeoTransform[3] + + nYSize * adfGeoTransform[5] ); + double dLLLong = (adfGeoTransform[0] ); + double dURLat = (adfGeoTransform[3] ); + double dURLong = (adfGeoTransform[0] + + nXSize * adfGeoTransform[1] ); + + string grFileName = CPLResetExtension(osFileName, "grf" ); + WriteElement("Ilwis", "Type", grFileName, "GeoRef"); + WriteElement("GeoRef", "lines", grFileName, nYSize); + WriteElement("GeoRef", "columns", grFileName, nXSize); + WriteElement("GeoRef", "Type", grFileName, "GeoRefCorners"); + WriteElement("GeoRefCorners", "CornersOfCorners", grFileName, "Yes"); + WriteElement("GeoRefCorners", "MinX", grFileName, dLLLong); + WriteElement("GeoRefCorners", "MinY", grFileName, dLLLat); + WriteElement("GeoRefCorners", "MaxX", grFileName, dURLong); + WriteElement("GeoRefCorners", "MaxY", grFileName, dURLat); + + //Re-write the GeoRef property to raster ODF + //Form band file name + string sBaseName = string(CPLGetBasename(osFileName) ); + string sPath = string(CPLGetPath(osFileName)); + if (nBands == 1) + { + WriteElement("Map", "GeoRef", osFileName, sBaseName + ".grf"); + } + else + { + for( int iBand = 0; iBand < nBands; iBand++ ) + { + if (iBand == 0) + WriteElement("MapList", "GeoRef", osFileName, sBaseName + ".grf"); + char pszName[100]; + sprintf(pszName, "%s_band_%d", sBaseName.c_str(),iBand + 1 ); + string pszODFName = string(CPLFormFilename(sPath.c_str(),pszName,"mpr")); + WriteElement("Map", "GeoRef", pszODFName, sBaseName + ".grf"); + } + } + } + } + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *ILWISDataset::GetProjectionRef() + +{ + return ( pszProjection ); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr ILWISDataset::SetProjection( const char * pszNewProjection ) + +{ + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszNewProjection ); + bGeoDirty = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr ILWISDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return( CE_None ); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr ILWISDataset::SetGeoTransform( double * padfTransform ) + +{ + memmove( adfGeoTransform, padfTransform, sizeof(double)*6 ); + + if (adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0) + bGeoDirty = TRUE; + + return CE_None; +} + +bool CheckASCII(unsigned char * buf, int size) +{ + for (int i = 0; i < size; ++i) + if (!isascii(buf[i])) + return false; + + return true; +} +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *ILWISDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Does this look like an ILWIS file */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 1 ) + return NULL; + + string sExt = CPLGetExtension( poOpenInfo->pszFilename ); + if (!EQUAL(sExt.c_str(),"mpr") && !EQUAL(sExt.c_str(),"mpl")) + return NULL; + + if (!CheckASCII(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes)) + return NULL; + + string ilwistype = ReadElement("Ilwis", "Type", poOpenInfo->pszFilename); + if( ilwistype.length() == 0) + return NULL; + + string sFileType; //map or map list + int iBandCount; + string mapsize; + string maptype = ReadElement("BaseMap", "Type", poOpenInfo->pszFilename); + string sBaseName = string(CPLGetBasename(poOpenInfo->pszFilename) ); + string sPath = string(CPLGetPath( poOpenInfo->pszFilename)); + + //Verify whether it is a map list or a map + if( EQUAL(ilwistype.c_str(),"MapList") ) + { + sFileType = string("MapList"); + string sMaps = ReadElement("MapList", "Maps", poOpenInfo->pszFilename); + iBandCount = atoi(sMaps.c_str()); + mapsize = ReadElement("MapList", "Size", poOpenInfo->pszFilename); + for (int iBand = 0; iBand < iBandCount; ++iBand ) + { + //Form the band file name. + char cBandName[45]; + sprintf( cBandName, "Map%d", iBand); + string sBandName = ReadElement("MapList", string(cBandName), poOpenInfo->pszFilename); + string pszBandBaseName = string(CPLGetBasename(sBandName.c_str()) ); + string pszBandPath = string(CPLGetPath( sBandName.c_str())); + if ( 0 == pszBandPath.length() ) + { + sBandName = string(CPLFormFilename(sPath.c_str(), + pszBandBaseName.c_str(),"mpr" )); + } + //Verify the file exetension, it must be an ILWIS raw data file + //with extension .mp#, otherwise, unsupported + //This drive only supports a map list which stores a set of ILWIS raster maps, + string sMapStoreName = ReadElement("MapStore", "Data", sBandName); + string sExt = CPLGetExtension( sMapStoreName.c_str() ); + if ( !EQUALN( sExt.c_str(), "mp#", 3 )) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported ILWIS data file. \n" + "can't treat as raster.\n" ); + return FALSE; + } + } + } + else if(EQUAL(ilwistype.c_str(),"BaseMap") && EQUAL(maptype.c_str(),"Map")) + { + sFileType = "Map"; + iBandCount = 1; + mapsize = ReadElement("Map", "Size", poOpenInfo->pszFilename); + string sMapType = ReadElement("Map", "Type", poOpenInfo->pszFilename); + ilwisStoreType stStoreType; + if ( + GetStoreType(string(poOpenInfo->pszFilename), stStoreType) != CE_None ) + { + //CPLError( CE_Failure, CPLE_AppDefined, + // "Unsupported ILWIS data file. \n" + // "can't treat as raster.\n" ); + return FALSE; + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported ILWIS data file. \n" + "can't treat as raster.\n" ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + ILWISDataset *poDS; + poDS = new ILWISDataset(); + +/* -------------------------------------------------------------------- */ +/* Capture raster size from ILWIS file (.mpr). */ +/* -------------------------------------------------------------------- */ + int Row = 0, Col = 0; + if ( GetRowCol(mapsize, Row, Col) != CE_None) + return FALSE; + poDS->nRasterXSize = Col; + poDS->nRasterYSize = Row; + poDS->osFileName = poOpenInfo->pszFilename; + poDS->pszFileType = sFileType; +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + //poDS->pszFileName = new char[strlen(poOpenInfo->pszFilename) + 1]; + poDS->nBands = iBandCount; + for( int iBand = 0; iBand < poDS->nBands; iBand++ ) + { + poDS->SetBand( iBand+1, new ILWISRasterBand( poDS, iBand+1 ) ); + } + +/* -------------------------------------------------------------------- */ +/* Collect the geotransform coefficients */ +/* -------------------------------------------------------------------- */ + string pszGeoRef; + poDS->CollectTransformCoef(pszGeoRef); + +/* -------------------------------------------------------------------- */ +/* Translation from ILWIS coordinate system definition */ +/* -------------------------------------------------------------------- */ + if( (pszGeoRef.length() != 0) && !EQUAL(pszGeoRef.c_str(),"none")) + { + + // Fetch coordinate system + string csy = ReadElement("GeoRef", "CoordSystem", pszGeoRef); + string pszProj; + + if( (csy.length() != 0) && !EQUAL(csy.c_str(),"unknown.csy")) + { + + //Form the coordinate system file name + if( !(EQUALN( csy.c_str(), "latlon.csy", 10 )) && + !(EQUALN( csy.c_str(), "LatlonWGS84.csy", 15 ))) + { + string pszBaseName = string(CPLGetBasename(csy.c_str()) ); + string pszPath = string(CPLGetPath( poDS->osFileName )); + csy = string(CPLFormFilename(pszPath.c_str(), + pszBaseName.c_str(),"csy" )); + pszProj = ReadElement("CoordSystem", "Type", csy); + if (pszProj.length() == 0 ) //default to projection + pszProj = "Projection"; + } + else + { + pszProj = "LatLon"; + } + + if( (EQUALN( pszProj.c_str(), "LatLon", 6 )) || + (EQUALN( pszProj.c_str(), "Projection", 10 ))) + poDS->ReadProjection( csy ); + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return( poDS ); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void ILWISDataset::FlushCache() + +{ + GDALDataset::FlushCache(); + + if( bGeoDirty == TRUE ) + { + WriteGeoReference(); + WriteProjection(); + bGeoDirty = FALSE; + } +} + +/************************************************************************/ +/* Create() */ +/* */ +/* Create a new ILWIS file. */ +/************************************************************************/ + +GDALDataset *ILWISDataset::Create(const char* pszFilename, + int nXSize, int nYSize, + int nBands, GDALDataType eType, + CPL_UNUSED char** papszParmList) +{ + ILWISDataset *poDS; + int iBand; + +/* -------------------------------------------------------------------- */ +/* Verify input options. */ +/* -------------------------------------------------------------------- */ + if( eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_Int32 + && eType != GDT_Float32 && eType != GDT_Float64 && eType != GDT_UInt16 && eType != GDT_UInt32) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create ILWIS dataset with an illegal\n" + "data type (%s).\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Translate the data type. */ +/* Determine store type of ILWIS raster */ +/* -------------------------------------------------------------------- */ + string sDomain= "value.dom"; + double stepsize = 1; + string sStoreType = GDALType2ILWIS(eType); + if( EQUAL(sStoreType.c_str(),"")) + return NULL; + else if( EQUAL(sStoreType.c_str(),"Real") || EQUAL(sStoreType.c_str(),"float")) + stepsize = 0; + + string pszBaseName = string(CPLGetBasename( pszFilename )); + string pszPath = string(CPLGetPath( pszFilename )); + +/* -------------------------------------------------------------------- */ +/* Write out object definition file for each band */ +/* -------------------------------------------------------------------- */ + string pszODFName; + string pszDataBaseName; + string pszFileName; + + char strsize[45]; + sprintf(strsize, "%d %d", nYSize, nXSize); + + //Form map/maplist name. + if ( nBands == 1 ) + { + pszODFName = string(CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"mpr")); + pszDataBaseName = pszBaseName; + pszFileName = CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"mpr"); + } + else + { + pszFileName = CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"mpl"); + WriteElement("Ilwis", "Type", string(pszFileName), "MapList"); + WriteElement("MapList", "GeoRef", string(pszFileName), "none.grf"); + WriteElement("MapList", "Size", string(pszFileName), string(strsize)); + WriteElement("MapList", "Maps", string(pszFileName), nBands); + } + + for( iBand = 0; iBand < nBands; iBand++ ) + { + if ( nBands > 1 ) + { + char pszBandName[100]; + sprintf(pszBandName, "%s_band_%d", pszBaseName.c_str(),iBand + 1 ); + pszODFName = string(pszBandName) + ".mpr"; + pszDataBaseName = string(pszBandName); + sprintf(pszBandName, "Map%d", iBand); + WriteElement("MapList", string(pszBandName), string(pszFileName), pszODFName); + pszODFName = CPLFormFilename(pszPath.c_str(),pszDataBaseName.c_str(),"mpr"); + } +/* -------------------------------------------------------------------- */ +/* Write data definition per band (.mpr) */ +/* -------------------------------------------------------------------- */ + + WriteElement("Ilwis", "Type", pszODFName, "BaseMap"); + WriteElement("BaseMap", "Type", pszODFName, "Map"); + WriteElement("Map", "Type", pszODFName, "MapStore"); + + WriteElement("BaseMap", "Domain", pszODFName, sDomain); + string pszDataName = pszDataBaseName + ".mp#"; + WriteElement("MapStore", "Data", pszODFName, pszDataName); + WriteElement("MapStore", "Structure", pszODFName, "Line"); + // sStoreType is used by ILWISRasterBand constructor to determine eDataType + WriteElement("MapStore", "Type", pszODFName, sStoreType); + + // For now write-out a "Range" that is as broad as possible. + // If later a better range is found (by inspecting metadata in the source dataset), + // the "Range" will be overwritten by a better version. + double adfMinMax[2]; + adfMinMax[0] = -9999999.9; + adfMinMax[1] = 9999999.9; + char strdouble[45]; + CPLsprintf(strdouble, "%.3f:%.3f:%3f:offset=0", adfMinMax[0], adfMinMax[1],stepsize); + string range = string(strdouble); + WriteElement("BaseMap", "Range", pszODFName, range); + + WriteElement("Map", "GeoRef", pszODFName, "none.grf"); + WriteElement("Map", "Size", pszODFName, string(strsize)); + +/* -------------------------------------------------------------------- */ +/* Try to create the data file. */ +/* -------------------------------------------------------------------- */ + pszDataName = CPLResetExtension(pszODFName.c_str(), "mp#" ); + + VSILFILE *fp = VSIFOpenL( pszDataName.c_str(), "wb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create file %s.\n", pszDataName.c_str() ); + return NULL; + } + VSIFCloseL( fp ); + } + poDS = new ILWISDataset(); + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->nBands = nBands; + poDS->eAccess = GA_Update; + poDS->bNewDataset = TRUE; + poDS->SetDescription(pszFilename); + poDS->osFileName = pszFileName; + poDS->pszIlwFileName = string(pszFileName); + if ( nBands == 1 ) + poDS->pszFileType = "Map"; + else + poDS->pszFileType = "MapList"; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + { + poDS->SetBand( iBand, new ILWISRasterBand( poDS, iBand ) ); + } + + return poDS; + //return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * +ILWISDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + + ILWISDataset *poDS; + GDALDataType eType = GDT_Byte; + int iBand; + (void) bStrict; + + + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int nBands = poSrcDS->GetRasterCount(); + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the basic dataset. */ +/* -------------------------------------------------------------------- */ + for( iBand = 0; iBand < nBands; iBand++ ) + { + GDALRasterBand *poBand = poSrcDS->GetRasterBand( iBand+1 ); + eType = GDALDataTypeUnion( eType, poBand->GetRasterDataType() ); + } + + poDS = (ILWISDataset *) Create( pszFilename, + poSrcDS->GetRasterXSize(), + poSrcDS->GetRasterYSize(), + nBands, + eType, papszOptions ); + + if( poDS == NULL ) + return NULL; + string pszBaseName = string(CPLGetBasename( pszFilename )); + string pszPath = string(CPLGetPath( pszFilename )); + +/* -------------------------------------------------------------------- */ +/* Copy and geo-transform and projection information. */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + string georef = "none.grf"; + + //check wheather we should create georeference file. + //source dataset must be north up + if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None + && (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 + || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 + || adfGeoTransform[4] != 0.0 || fabs(adfGeoTransform[5]) != 1.0)) + { + poDS->SetGeoTransform( adfGeoTransform ); + if (adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0) + georef = pszBaseName + ".grf"; + } + + const char *pszProj = poSrcDS->GetProjectionRef(); + if( pszProj != NULL && strlen(pszProj) > 0 ) + poDS->SetProjection( pszProj ); + +/* -------------------------------------------------------------------- */ +/* Create the output raster files for each band */ +/* -------------------------------------------------------------------- */ + + for( iBand = 0; iBand < nBands; iBand++ ) + { + VSILFILE *fpData; + GByte *pData; + + GDALRasterBand *poBand = poSrcDS->GetRasterBand( iBand+1 ); + ILWISRasterBand *desBand = (ILWISRasterBand *) poDS->GetRasterBand( iBand+1 ); + +/* -------------------------------------------------------------------- */ +/* Translate the data type. */ +/* -------------------------------------------------------------------- */ + int nLineSize = nXSize * GDALGetDataTypeSize(eType) / 8; + pData = (GByte *) CPLMalloc( nLineSize ); + + //Determine the nodata value + int bHasNoDataValue; + double dNoDataValue = poBand->GetNoDataValue(&bHasNoDataValue); + + //Determine store type of ILWIS raster + string sStoreType = GDALType2ILWIS( eType ); + double stepsize = 1; + if( EQUAL(sStoreType.c_str(),"")) + return NULL; + else if( EQUAL(sStoreType.c_str(),"Real") || EQUAL(sStoreType.c_str(),"float")) + stepsize = 0; + + //Form the image file name, create the object definition file. + string pszODFName; + string pszDataBaseName; + if (nBands == 1) + { + pszODFName = string(CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"mpr")); + pszDataBaseName = pszBaseName; + } + else + { + char pszName[100]; + sprintf(pszName, "%s_band_%d", pszBaseName.c_str(),iBand + 1 ); + pszODFName = string(CPLFormFilename(pszPath.c_str(),pszName,"mpr")); + pszDataBaseName = string(pszName); + } +/* -------------------------------------------------------------------- */ +/* Write data definition file for each band (.mpr) */ +/* -------------------------------------------------------------------- */ + + double adfMinMax[2]; + int bGotMin, bGotMax; + + adfMinMax[0] = poBand->GetMinimum( &bGotMin ); + adfMinMax[1] = poBand->GetMaximum( &bGotMax ); + if( ! (bGotMin && bGotMax) ) + GDALComputeRasterMinMax((GDALRasterBandH)poBand, FALSE, adfMinMax); + if ((!CPLIsNan(adfMinMax[0])) && CPLIsFinite(adfMinMax[0]) && (!CPLIsNan(adfMinMax[1])) && CPLIsFinite(adfMinMax[1])) + { + // only write a range if we got a correct one from the source dataset (otherwise ILWIS can't show the map properly) + char strdouble[45]; + CPLsprintf(strdouble, "%.3f:%.3f:%3f:offset=0", adfMinMax[0], adfMinMax[1],stepsize); + string range = string(strdouble); + WriteElement("BaseMap", "Range", pszODFName, range); + } + WriteElement("Map", "GeoRef", pszODFName, georef); + +/* -------------------------------------------------------------------- */ +/* Loop over image, copy the image data. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + + //For file name for raw data, and create binary files. + string pszDataFileName = CPLResetExtension(pszODFName.c_str(), "mp#" ); + + fpData = desBand->fpRaw; + if( fpData == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + + for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ ) + { + eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + pData, nXSize, 1, eType, + 0, 0, NULL ); + + if( eErr == CE_None ) + { + if (bHasNoDataValue) + { + // pData may have entries with value = dNoDataValue + // ILWIS uses a fixed value for nodata, depending on the data-type + // Therefore translate the NoDataValue from each band to ILWIS + for (int iCol = 0; iCol < nXSize; iCol++ ) + { + if( EQUAL(sStoreType.c_str(),"Byte")) + { + if ( ((GByte * )pData)[iCol] == dNoDataValue ) + (( GByte * )pData)[iCol] = 0; + } + else if( EQUAL(sStoreType.c_str(),"Int")) + { + if ( ((GInt16 * )pData)[iCol] == dNoDataValue ) + (( GInt16 * )pData)[iCol] = shUNDEF; + } + else if( EQUAL(sStoreType.c_str(),"Long")) + { + if ( ((GInt32 * )pData)[iCol] == dNoDataValue ) + (( GInt32 * )pData)[iCol] = iUNDEF; + } + else if( EQUAL(sStoreType.c_str(),"float")) + { + if ((((float * )pData)[iCol] == dNoDataValue ) || (CPLIsNan((( float * )pData)[iCol]))) + (( float * )pData)[iCol] = flUNDEF; + } + else if( EQUAL(sStoreType.c_str(),"Real")) + { + if ((((double * )pData)[iCol] == dNoDataValue ) || (CPLIsNan((( double * )pData)[iCol]))) + (( double * )pData)[iCol] = rUNDEF; + } + } + } + int iSize = VSIFWriteL( pData, 1, nLineSize, desBand->fpRaw ); + if ( iSize < 1 ) + { + CPLFree( pData ); + //CPLFree( pData32 ); + CPLError( CE_Failure, CPLE_FileIO, + "Write of file failed with fwrite error."); + return NULL; + } + } + if( !pfnProgress(iLine / (nYSize * nBands), NULL, pProgressData ) ) + return NULL; + } + VSIFFlushL( fpData ); + CPLFree( pData ); + } + + poDS->FlushCache(); + + if( !pfnProgress( 1.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated" ); + delete poDS; + + GDALDriver *poILWISDriver = + (GDALDriver *) GDALGetDriverByName( "ILWIS" ); + poILWISDriver->Delete( pszFilename ); + return NULL; + } + + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} + +/************************************************************************/ +/* ILWISRasterBand() */ +/************************************************************************/ + +ILWISRasterBand::ILWISRasterBand( ILWISDataset *poDS, int nBand ) + +{ + string sBandName; + if ( EQUAL(poDS->pszFileType.c_str(),"Map")) + sBandName = string(poDS->osFileName); + else //map list + { + //Form the band name + char cBandName[45]; + sprintf( cBandName, "Map%d", nBand-1); + sBandName = ReadElement("MapList", string(cBandName), string(poDS->osFileName)); + string sInputPath = string(CPLGetPath( poDS->osFileName)); + string sBandPath = string(CPLGetPath( sBandName.c_str())); + string sBandBaseName = string(CPLGetBasename( sBandName.c_str())); + if ( 0==sBandPath.length() ) + sBandName = string(CPLFormFilename(sInputPath.c_str(),sBandBaseName.c_str(),"mpr" )); + else + sBandName = string(CPLFormFilename(sBandPath.c_str(),sBandBaseName.c_str(),"mpr" )); + } + + if (poDS->bNewDataset) + { + // Called from Create(): + // eDataType is defaulted to GDT_Byte by GDALRasterBand::GDALRasterBand + // Here we set it to match the value of sStoreType (that was set in ILWISDataset::Create) + // Unfortunately we can't take advantage of the ILWIS "ValueRange" object that would use + // the most compact storeType possible, without going through all values. + GetStoreType(sBandName, psInfo.stStoreType); + eDataType = ILWIS2GDALType(psInfo.stStoreType); + } + else // Called from Open(), thus convert ILWIS type from ODF to eDataType + GetILWISInfo(sBandName); + this->poDS = poDS; + this->nBand = nBand; + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + switch (psInfo.stStoreType) + { + case stByte: + nSizePerPixel = GDALGetDataTypeSize(GDT_Byte) / 8; + break; + case stInt: + nSizePerPixel = GDALGetDataTypeSize(GDT_Int16) / 8; + break; + case stLong: + nSizePerPixel = GDALGetDataTypeSize(GDT_Int32) / 8; + break; + case stFloat: + nSizePerPixel = GDALGetDataTypeSize(GDT_Float32) / 8; + break; + case stReal: + nSizePerPixel = GDALGetDataTypeSize(GDT_Float64) / 8; + break; + } + ILWISOpen(sBandName); +} + +/************************************************************************/ +/* ~ILWISRasterBand() */ +/************************************************************************/ + +ILWISRasterBand::~ILWISRasterBand() + +{ + if( fpRaw != NULL ) + { + VSIFCloseL( fpRaw ); + fpRaw = NULL; + } +} + + +/************************************************************************/ +/* ILWISOpen() */ +/************************************************************************/ +void ILWISRasterBand::ILWISOpen( string pszFileName ) +{ + ILWISDataset* dataset = (ILWISDataset*) poDS; + string pszDataFile; + pszDataFile = string(CPLResetExtension( pszFileName.c_str(), "mp#" )); + + fpRaw = VSIFOpenL( pszDataFile.c_str(), (dataset->eAccess == GA_Update) ? "rb+" : "rb"); +} + +/************************************************************************/ +/* ReadValueDomainProperties() */ +/************************************************************************/ +// Helper function for GetILWISInfo, to avoid code-duplication +// Unfortunately with side-effect (changes members psInfo and eDataType) +void ILWISRasterBand::ReadValueDomainProperties(string pszFileName) +{ + string rangeString = ReadElement("BaseMap", "Range", pszFileName.c_str()); + psInfo.vr = ValueRange(rangeString); + double rStep = psInfo.vr.get_rStep(); + if ( rStep != 0 ) + { + psInfo.bUseValueRange = true; // use ILWIS ValueRange object to convert from "raw" to "value" + double rMin = psInfo.vr.get_rLo(); + double rMax = psInfo.vr.get_rHi(); + if (rStep - (long)rStep == 0.0) // Integer values + { + if ( rMin >= 0 && rMax <= UCHAR_MAX) + eDataType = GDT_Byte; + else if ( rMin >= SHRT_MIN && rMax <= SHRT_MAX) + eDataType = GDT_Int16; + else if ( rMin >= 0 && rMax <= USHRT_MAX) + eDataType = GDT_UInt16; + else if ( rMin >= INT_MIN && rMax <= INT_MAX) + eDataType = GDT_Int32; + else if ( rMin >= 0 && rMax <= UINT_MAX) + eDataType = GDT_UInt32; + else + eDataType = GDT_Float64; + } + else // Floating point values + { + if ((rMin >= -FLT_MAX) && (rMax <= FLT_MAX) && (fabs(rStep) >= FLT_EPSILON)) // is "float" good enough? + eDataType = GDT_Float32; + else + eDataType = GDT_Float64; + } + } + else + { + if (psInfo.stStoreType == stFloat) // is "float" good enough? + eDataType = GDT_Float32; + else + eDataType = GDT_Float64; + } +} + +/************************************************************************/ +/* GetILWISInfo() */ +/************************************************************************/ +// Calculates members psInfo and eDataType +CPLErr ILWISRasterBand::GetILWISInfo(string pszFileName) +{ + // Fill the psInfo struct with defaults. + // Get the store type from the ODF + if (GetStoreType(pszFileName, psInfo.stStoreType) != CE_None) + { + return CE_Failure; + } + psInfo.bUseValueRange = false; + psInfo.stDomain = ""; + + // ILWIS has several (currently 22) predefined "system-domains", that influence the data-type + // The user can also create domains. The possible types for these are "class", "identifier", "bool" and "value" + // The last one has Type=DomainValue + // Here we make an effort to determine the most-compact gdal-type (eDataType) that is suitable + // for the data in the current ILWIS band. + // First check against all predefined domain names (the "system-domains") + // If no match is found, read the domain ODF from disk, and get its type + // We have hardcoded the system domains here, because ILWIS may not be installed, and even if it is, + // we don't know where (thus it is useless to attempt to read a system-domain-file). + + string domName = ReadElement("BaseMap", "Domain", pszFileName.c_str()); + string pszBaseName = string(CPLGetBasename( domName.c_str() )); + string pszPath = string(CPLGetPath( pszFileName.c_str() )); + + // Check against all "system-domains" + if ( EQUAL(pszBaseName.c_str(),"value") // is it a system domain with Type=DomainValue? + || EQUAL(pszBaseName.c_str(),"count") + || EQUAL(pszBaseName.c_str(),"distance") + || EQUAL(pszBaseName.c_str(),"min1to1") + || EQUAL(pszBaseName.c_str(),"nilto1") + || EQUAL(pszBaseName.c_str(),"noaa") + || EQUAL(pszBaseName.c_str(),"perc") + || EQUAL(pszBaseName.c_str(),"radar") ) + { + ReadValueDomainProperties(pszFileName); + } + else if( EQUAL(pszBaseName.c_str(),"bool") + || EQUAL(pszBaseName.c_str(),"byte") + || EQUAL(pszBaseName.c_str(),"bit") + || EQUAL(pszBaseName.c_str(),"image") + || EQUAL(pszBaseName.c_str(),"colorcmp") + || EQUAL(pszBaseName.c_str(),"flowdirection") + || EQUAL(pszBaseName.c_str(),"hortonratio") + || EQUAL(pszBaseName.c_str(),"yesno") ) + { + eDataType = GDT_Byte; + if( EQUAL(pszBaseName.c_str(),"image") + || EQUAL(pszBaseName.c_str(),"colorcmp")) + psInfo.stDomain = pszBaseName; + } + else if( EQUAL(pszBaseName.c_str(),"color") + || EQUAL(pszBaseName.c_str(),"none" ) + || EQUAL(pszBaseName.c_str(),"coordbuf") + || EQUAL(pszBaseName.c_str(),"binary") + || EQUAL(pszBaseName.c_str(),"string") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported ILWIS domain type."); + return CE_Failure; + } + else + { + // No match found. Assume it is a self-created domain. Read its type and decide the GDAL type. + string pszDomainFileName = string(CPLFormFilename(pszPath.c_str(),pszBaseName.c_str(),"dom" )); + string domType = ReadElement("Domain", "Type", pszDomainFileName.c_str()); + if EQUAL(domType.c_str(),"domainvalue") // is it a self-created domain of type=DomainValue? + { + ReadValueDomainProperties(pszFileName); + } + else if((!EQUAL(domType.c_str(),"domainbit")) + && (!EQUAL(domType.c_str(),"domainstring")) + && (!EQUAL(domType.c_str(),"domaincolor")) + && (!EQUAL(domType.c_str(),"domainbinary")) + && (!EQUAL(domType.c_str(),"domaincoordBuf")) + && (!EQUAL(domType.c_str(),"domaincoord"))) + { + // Type is "DomainClass", "DomainBool" or "DomainIdentifier". + // For now we set the GDAL storeType be the same as the ILWIS storeType + // The user will have to convert the classes manually. + eDataType = ILWIS2GDALType(psInfo.stStoreType); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported ILWIS domain type."); + return CE_Failure; + } + } + + return CE_None; +} + +/** This driver defines a Block to be the entire raster; The method reads + each line as a block. it reads the data into pImage. + + @param nBlockXOff This must be zero for this driver + @param pImage Dump the data here + + @return A CPLErr code. This implementation returns a CE_Failure if the + block offsets are non-zero, If successful, returns CE_None. */ +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ +CPLErr ILWISRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + // pImage is empty; this function fills it with data from fpRaw + // (ILWIS data to foreign data) + + // If the x block offset is non-zero, something is wrong. + CPLAssert( nBlockXOff == 0 ); + + int nBlockSize = nBlockXSize * nBlockYSize * nSizePerPixel; + if( fpRaw == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open ILWIS data file."); + return( CE_Failure ); + } + +/* -------------------------------------------------------------------- */ +/* Handle the case of a strip in a writable file that doesn't */ +/* exist yet, but that we want to read. Just set to zeros and */ +/* return. */ +/* -------------------------------------------------------------------- */ + ILWISDataset* poIDS = (ILWISDataset*) poDS; + +#ifdef notdef + if( poIDS->bNewDataset && (poIDS->eAccess == GA_Update)) + { + FillWithNoData(pImage); + return CE_None; + } +#endif + + VSIFSeekL( fpRaw, nBlockSize*nBlockYOff, SEEK_SET ); + void *pData = (char *)CPLMalloc(nBlockSize); + if (VSIFReadL( pData, 1, nBlockSize, fpRaw ) < 1) + { + if( poIDS->bNewDataset ) + { + FillWithNoData(pImage); + return CE_None; + } + else + { + CPLFree( pData ); + CPLError( CE_Failure, CPLE_FileIO, + "Read of file failed with fread error."); + return CE_Failure; + } + } + + // Copy the data from pData to pImage, and convert the store-type + // The data in pData has store-type = psInfo.stStoreType + // The data in pImage has store-type = eDataType + // They may not match, because we have chosen the most compact store-type, + // and for GDAL this may be different than for ILWIS. + + switch (psInfo.stStoreType) + { + int iCol; + case stByte: + for( iCol = 0; iCol < nBlockXSize; iCol++ ) + { + double rV = psInfo.bUseValueRange ? psInfo.vr.rValue(((GByte *) pData)[iCol]) : ((GByte *) pData)[iCol]; + SetValue(pImage, iCol, rV); + } + break; + case stInt: + for( iCol = 0; iCol < nBlockXSize; iCol++ ) + { + double rV = psInfo.bUseValueRange ? psInfo.vr.rValue(((GInt16 *) pData)[iCol]) : ((GInt16 *) pData)[iCol]; + SetValue(pImage, iCol, rV); + } + break; + case stLong: + for( iCol = 0; iCol < nBlockXSize; iCol++ ) + { + double rV = psInfo.bUseValueRange ? psInfo.vr.rValue(((GInt32 *) pData)[iCol]) : ((GInt32 *) pData)[iCol]; + SetValue(pImage, iCol, rV); + } + break; + case stFloat: + for( iCol = 0; iCol < nBlockXSize; iCol++ ) + ((float *) pImage)[iCol] = ((float *) pData)[iCol]; + break; + case stReal: + for( iCol = 0; iCol < nBlockXSize; iCol++ ) + ((double *) pImage)[iCol] = ((double *) pData)[iCol]; + break; + default: + CPLAssert(0); + } + + // Officially we should also translate "nodata" values, but at this point + // we can't tell what's the "nodata" value of the destination (foreign) dataset + + CPLFree( pData ); + + return CE_None; +} + +void ILWISRasterBand::SetValue(void *pImage, int i, double rV) { + switch ( eDataType ) { + case GDT_Byte: + ((GByte *)pImage)[i] = (GByte) rV; + break; + case GDT_UInt16: + ((GUInt16 *) pImage)[i] = (GUInt16) rV; + break; + case GDT_Int16: + ((GInt16 *) pImage)[i] = (GInt16) rV; + break; + case GDT_UInt32: + ((GUInt32 *) pImage)[i] = (GUInt32) rV; + break; + case GDT_Int32: + ((GInt32 *) pImage)[i] = (GInt32) rV; + break; + case GDT_Float32: + ((float *) pImage)[i] = (float) rV; + break; + case GDT_Float64: + ((double *) pImage)[i] = rV; + break; + default: + CPLAssert(0); + } +} + +double ILWISRasterBand::GetValue(void *pImage, int i) { + double rV = 0; // Does GDAL have an official nodata value? + switch ( eDataType ) { + case GDT_Byte: + rV = ((GByte *)pImage)[i]; + break; + case GDT_UInt16: + rV = ((GUInt16 *) pImage)[i]; + break; + case GDT_Int16: + rV = ((GInt16 *) pImage)[i]; + break; + case GDT_UInt32: + rV = ((GUInt32 *) pImage)[i]; + break; + case GDT_Int32: + rV = ((GInt32 *) pImage)[i]; + break; + case GDT_Float32: + rV = ((float *) pImage)[i]; + break; + case GDT_Float64: + rV = ((double *) pImage)[i]; + break; + default: + CPLAssert(0); + } + return rV; +} + +void ILWISRasterBand::FillWithNoData(void * pImage) +{ + if (psInfo.stStoreType == stByte) + memset(pImage, 0, nBlockXSize * nBlockYSize); + else + { + switch (psInfo.stStoreType) + { + case stInt: + ((GInt16*)pImage)[0] = shUNDEF; + break; + case stLong: + ((GInt32*)pImage)[0] = iUNDEF; + break; + case stFloat: + ((float*)pImage)[0] = flUNDEF; + break; + case stReal: + ((double*)pImage)[0] = rUNDEF; + break; + default: // should there be handling for stByte? + break; + } + int iItemSize = GDALGetDataTypeSize(eDataType) / 8; + for (int i = 1; i < nBlockXSize * nBlockYSize; ++i) + memcpy( ((char*)pImage) + iItemSize * i, (char*)pImage + iItemSize * (i - 1), iItemSize); + } +} + +/************************************************************************/ +/* IWriteBlock() */ +/* */ +/************************************************************************/ + +CPLErr ILWISRasterBand::IWriteBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff, + void* pImage) +{ + // pImage has data; this function reads this data and stores it to fpRaw + // (foreign data to ILWIS data) + + // Note that this function will not overwrite existing data in fpRaw, but + // it will "fill gaps" marked by "nodata" values + + ILWISDataset* dataset = (ILWISDataset*) poDS; + + CPLAssert( dataset != NULL + && nBlockXOff == 0 + && nBlockYOff >= 0 + && pImage != NULL ); + + int nXSize = dataset->GetRasterXSize(); + int nBlockSize = nBlockXSize * nBlockYSize * nSizePerPixel; + void *pData = CPLMalloc(nBlockSize); + + VSIFSeekL( fpRaw, nBlockSize * nBlockYOff, SEEK_SET ); + + bool fDataExists = (VSIFReadL( pData, 1, nBlockSize, fpRaw ) >= 1); + + // Copy the data from pImage to pData, and convert the store-type + // The data in pData has store-type = psInfo.stStoreType + // The data in pImage has store-type = eDataType + // They may not match, because we have chosen the most compact store-type, + // and for GDAL this may be different than for ILWIS. + + if( fDataExists ) + { + // fpRaw (thus pData) already has data + // Take care to not overwrite it + // thus only fill in gaps (nodata values) + switch (psInfo.stStoreType) + { + int iCol; + case stByte: + for (iCol = 0; iCol < nXSize; iCol++ ) + if ((( GByte * )pData)[iCol] == 0) + { + double rV = GetValue(pImage, iCol); + (( GByte * )pData)[iCol] = (GByte) + (psInfo.bUseValueRange ? psInfo.vr.iRaw(rV) : rV); + } + break; + case stInt: + for (iCol = 0; iCol < nXSize; iCol++ ) + if ((( GInt16 * )pData)[iCol] == shUNDEF) + { + double rV = GetValue(pImage, iCol); + (( GInt16 * )pData)[iCol] = (GInt16) + (psInfo.bUseValueRange ? psInfo.vr.iRaw(rV) : rV); + } + break; + case stLong: + for (iCol = 0; iCol < nXSize; iCol++ ) + if ((( GInt32 * )pData)[iCol] == iUNDEF) + { + double rV = GetValue(pImage, iCol); + (( GInt32 * )pData)[iCol] = (GInt32) + (psInfo.bUseValueRange ? psInfo.vr.iRaw(rV) : rV); + } + break; + case stFloat: + for (iCol = 0; iCol < nXSize; iCol++ ) + if ((( float * )pData)[iCol] == flUNDEF) + (( float * )pData)[iCol] = ((float* )pImage)[iCol]; + break; + case stReal: + for (iCol = 0; iCol < nXSize; iCol++ ) + if ((( double * )pData)[iCol] == rUNDEF) + (( double * )pData)[iCol] = ((double* )pImage)[iCol]; + break; + } + } + else + { + // fpRaw (thus pData) is still empty, just write the data + switch (psInfo.stStoreType) + { + int iCol; + case stByte: + for (iCol = 0; iCol < nXSize; iCol++ ) + { + double rV = GetValue(pImage, iCol); + (( GByte * )pData)[iCol] = (GByte) + (psInfo.bUseValueRange ? psInfo.vr.iRaw(rV) : rV); + } + break; + case stInt: + for (iCol = 0; iCol < nXSize; iCol++ ) + { + double rV = GetValue(pImage, iCol); + (( GInt16 * )pData)[iCol] = (GInt16) + (psInfo.bUseValueRange ? psInfo.vr.iRaw(rV) : rV); + } + break; + case stLong: + for (iCol = 0; iCol < nXSize; iCol++ ) + { + double rV = GetValue(pImage, iCol); + ((GInt32 *)pData)[iCol] = (GInt32) + (psInfo.bUseValueRange ? psInfo.vr.iRaw(rV) : rV); + } + break; + case stFloat: + for (iCol = 0; iCol < nXSize; iCol++ ) + (( float * )pData)[iCol] = ((float* )pImage)[iCol]; + break; + case stReal: + for (iCol = 0; iCol < nXSize; iCol++ ) + (( double * )pData)[iCol] = ((double* )pImage)[iCol]; + break; + } + } + + // Officially we should also translate "nodata" values, but at this point + // we can't tell what's the "nodata" value of the source (foreign) dataset + + VSIFSeekL( fpRaw, nBlockSize * nBlockYOff, SEEK_SET ); + + if (VSIFWriteL( pData, 1, nBlockSize, fpRaw ) < 1) + { + CPLFree( pData ); + CPLError( CE_Failure, CPLE_FileIO, + "Write of file failed with fwrite error."); + return CE_Failure; + } + + CPLFree( pData ); + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ +double ILWISRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + if( eDataType == GDT_Float64 ) + return rUNDEF; + else if( eDataType == GDT_Int32) + return iUNDEF; + else if( eDataType == GDT_Int16) + return shUNDEF; + else if( eDataType == GDT_Float32) + return flUNDEF; + else if( EQUAL(psInfo.stDomain.c_str(),"image") + || EQUAL(psInfo.stDomain.c_str(),"colorcmp")) + { + *pbSuccess = false; + return 0; + } + else + return 0; +} + +/************************************************************************/ +/* ValueRange() */ +/************************************************************************/ +double Max(double a, double b) +{ return (a>=b && a!=rUNDEF) ? a : b; } + +static double doubleConv(const char* s) +{ + if (s == 0) return rUNDEF; + char *endptr; + char *begin = const_cast(s); + + // skip leading spaces; strtol will return 0 on a string with only spaces + // which is not what we want + while (isspace((unsigned char)*begin)) ++begin; + + if (strlen(begin) == 0) return rUNDEF; + errno = 0; + double r = CPLStrtod(begin, &endptr); + if ((0 == *endptr) && (errno==0)) + return r; + while (*endptr != 0) { // check trailing spaces + if (*endptr != ' ') + return rUNDEF; + endptr++; + } + return r; +} + +ValueRange::ValueRange(string sRng) +{ + char* sRange = new char[sRng.length() + 1]; + for (unsigned int i = 0; i < sRng.length(); ++i) + sRange[i] = sRng[i]; + sRange[sRng.length()] = 0; + + char *p1 = strchr(sRange, ':'); + if (0 == p1) + return; + + char *p3 = strstr(sRange, ",offset="); + if (0 == p3) + p3 = strstr(sRange, ":offset="); + _r0 = rUNDEF; + if (0 != p3) { + _r0 = doubleConv(p3+8); + *p3 = 0; + } + char *p2 = strrchr(sRange, ':'); + _rStep = 1; + if (p1 != p2) { // step + _rStep = doubleConv(p2+1); + *p2 = 0; + } + + p2 = strchr(sRange, ':'); + if (p2 != 0) { + *p2 = 0; + _rLo = CPLAtof(sRange); + _rHi = CPLAtof(p2+1); + } + else { + _rLo = CPLAtof(sRange); + _rHi = _rLo; + } + init(_r0); + + delete [] sRange; +} + +ValueRange::ValueRange(double min, double max) // step = 1 +{ + _rLo = min; + _rHi = max; + _rStep = 1; + init(); +} + +ValueRange::ValueRange(double min, double max, double step) +{ + _rLo = min; + _rHi = max; + _rStep = step; + init(); +} + +static ilwisStoreType stNeeded(unsigned long iNr) +{ + if (iNr <= 256) + return stByte; + if (iNr <= SHRT_MAX) + return stInt; + return stLong; +} + +void ValueRange::init() +{ + init(rUNDEF); +} + +void ValueRange::init(double rRaw0) +{ + try { + _iDec = 0; + if (_rStep < 0) + _rStep = 0; + double r = _rStep; + if (r <= 1e-20) + _iDec = 3; + else while (r - floor(r) > 1e-20) { + r *= 10; + _iDec++; + if (_iDec > 10) + break; + } + + short iBeforeDec = 1; + double rMax = MAX(fabs(get_rLo()), fabs(get_rHi())); + if (rMax != 0) + iBeforeDec = (short)floor(log10(rMax)) + 1; + if (get_rLo() < 0) + iBeforeDec++; + _iWidth = (short) (iBeforeDec + _iDec); + if (_iDec > 0) + _iWidth++; + if (_iWidth > 12) + _iWidth = 12; + if (_rStep < 1e-06) + { + st = stReal; + _rStep = 0; + } + else { + r = get_rHi() - get_rLo(); + if (r <= ULONG_MAX) { + r /= _rStep; + r += 1; + } + r += 1; + if (r > LONG_MAX) + st = stReal; + else { + st = stNeeded((unsigned long)floor(r+0.5)); + if (st < stByte) + st = stByte; + } + } + if (rUNDEF != rRaw0) + _r0 = rRaw0; + else { + _r0 = 0; + if (st <= stByte) + _r0 = -1; + } + if (st > stInt) + iRawUndef = iUNDEF; + else if (st == stInt) + iRawUndef = shUNDEF; + else + iRawUndef = 0; + } + catch (std::exception*) { + st = stReal; + _r0 = 0; + _rStep = 0.0001; + _rHi = 1e300; + _rLo = -1e300; + iRawUndef = iUNDEF; + } +} + +string ValueRange::ToString() +{ + char buffer[200]; + if (fabs(get_rLo()) > 1.0e20 || fabs(get_rHi()) > 1.0e20) + CPLsprintf(buffer, "%g:%g:%f:offset=%g", get_rLo(), get_rHi(), get_rStep(), get_rRaw0()); + else if (get_iDec() >= 0) + CPLsprintf(buffer, "%.*f:%.*f:%.*f:offset=%.0f", get_iDec(), get_rLo(), get_iDec(), get_rHi(), get_iDec(), get_rStep(), get_rRaw0()); + else + CPLsprintf(buffer, "%f:%f:%f:offset=%.0f", get_rLo(), get_rHi(), get_rStep(), get_rRaw0()); + return string(buffer); +} + +double ValueRange::rValue(int iRaw) +{ + if (iRaw == iUNDEF || iRaw == iRawUndef) + return rUNDEF; + double rVal = iRaw + _r0; + rVal *= _rStep; + if (get_rLo() == get_rHi()) + return rVal; + double rEpsilon = _rStep == 0.0 ? 1e-6 : _rStep / 3.0; // avoid any rounding problems with an epsilon directly based on the + // the stepsize + if ((rVal - get_rLo() < -rEpsilon) || (rVal - get_rHi() > rEpsilon)) + return rUNDEF; + return rVal; +} + +int ValueRange::iRaw(double rValue) +{ + if (rValue == rUNDEF) // || !fContains(rValue)) + return iUNDEF; + double rEpsilon = _rStep == 0.0 ? 1e-6 : _rStep / 3.0; + if (rValue - get_rLo() < -rEpsilon) // take a little rounding tolerance + return iUNDEF; + else if (rValue - get_rHi() > rEpsilon) // take a little rounding tolerance + return iUNDEF; + rValue /= _rStep; + double rVal = floor(rValue+0.5); + rVal -= _r0; + long iVal; + iVal = longConv(rVal); + return iVal; +} + + +/************************************************************************/ +/* GDALRegister_ILWIS() */ +/************************************************************************/ + +void GDALRegister_ILWIS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ILWIS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ILWIS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ILWIS Raster Map" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "mpr/mpl" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 Int32 Float64" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = ILWISDataset::Open; + poDriver->pfnCreate = ILWISDataset::Create; + poDriver->pfnCreateCopy = ILWISDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/ilwis/ilwisdataset.h b/bazaar/plugin/gdal/frmts/ilwis/ilwisdataset.h new file mode 100644 index 000000000..072690fc2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ilwis/ilwisdataset.h @@ -0,0 +1,214 @@ +/****************************************************************************** + * + * Project: ILWIS Driver + * Purpose: GDALDataset driver for ILWIS translator for read/write support. + * Author: Lichun Wang, lichun@itc.nl + * + ****************************************************************************** + * Copyright (c) 2004, ITC + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifdef _MSC_VER +#pragma warning(disable : 4786) +#pragma warning(disable : 4503) +#endif + +#include "gdal_pam.h" +#include "cpl_csv.h" +#include "ogr_spatialref.h" + +#ifdef WIN32 +#include +#endif + +#include +#include +#include +#include + +CPL_C_START +void GDALRegister_ILWIS(void); +CPL_C_END + +#define shUNDEF -32767 +#define iUNDEF -2147483647 +#define flUNDEF ((float)-1e38) +#define rUNDEF ((double)-1e308) + +enum ilwisStoreType +{ + stByte, + stInt, + stLong, + stFloat, + stReal +}; + +class ValueRange +{ +public: + ValueRange(double min, double max); // step = 1 + ValueRange(double min, double max, double step); + ValueRange(std::string str); + std::string ToString(); + ilwisStoreType get_NeededStoreType() { return st; } + double get_rLo() { return _rLo; } + double get_rHi() { return _rHi; } + double get_rStep() { return _rStep; } + double get_rRaw0() { return _r0; } + int get_iDec() { return _iDec; } + double rValue(int raw); + int iRaw(double value); + +private: + void init(double rRaw0); + void init(); + double _rLo, _rHi; + double _rStep; + int _iDec; + double _r0; + int iRawUndef; + short _iWidth; + ilwisStoreType st; +}; + +/************************************************************************/ +/* ILWISInfo */ +/************************************************************************/ + +struct ILWISInfo +{ + ILWISInfo() : bUseValueRange(false), vr(0, 0) {} + bool bUseValueRange; + ValueRange vr; + ilwisStoreType stStoreType; + std::string stDomain; +}; + +/************************************************************************/ +/* ILWISRasterBand */ +/************************************************************************/ + +class ILWISDataset; + +class ILWISRasterBand : public GDALPamRasterBand +{ + friend class ILWISDataset; +public: + VSILFILE *fpRaw; + ILWISInfo psInfo; + int nSizePerPixel; + + ILWISRasterBand( ILWISDataset *, int ); + ~ILWISRasterBand(); + CPLErr GetILWISInfo(std::string pszFileName); + void ILWISOpen( std::string pszFilename); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + virtual double GetNoDataValue( int *pbSuccess ); + +private: + void FillWithNoData(void * pImage); + void SetValue(void *pImage, int i, double rV); + double GetValue(void *pImage, int i); + void ReadValueDomainProperties(std::string pszFileName); +}; + +/************************************************************************/ +/* ILWISDataset */ +/************************************************************************/ +class ILWISDataset : public GDALPamDataset +{ + friend class ILWISRasterBand; + CPLString osFileName; + std::string pszIlwFileName; + char *pszProjection; + double adfGeoTransform[6]; + int bGeoDirty; + int bNewDataset; /* product of Create() */ + std::string pszFileType; //indicating the input dataset: Map/MapList + CPLErr ReadProjection( std::string csyFileName); + CPLErr WriteProjection(); + CPLErr WriteGeoReference(); + void CollectTransformCoef(std::string &pszRefFile ); + +public: + ILWISDataset(); + ~ILWISDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + + static GDALDataset *CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + + static GDALDataset *Create(const char* pszFilename, + int nXSize, int nYSize, + int nBands, GDALDataType eType, + char** papszParmList); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual CPLErr SetGeoTransform( double * ); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual void FlushCache( void ); +}; + +// IniFile.h: interface for the IniFile class. +// +////////////////////////////////////////////////////////////////////// + +class CompareAsNum +{ +public: + bool operator() (const std::string&, const std::string&) const; +}; + +typedef std::map SectionEntries; +typedef std::map Sections; + +class IniFile +{ +public: + IniFile(const std::string& filename); + virtual ~IniFile(); + + void SetKeyValue(const std::string& section, const std::string& key, const std::string& value); + std::string GetKeyValue(const std::string& section, const std::string& key); + + void RemoveKeyValue(const std::string& section, const std::string& key); + void RemoveSection(const std::string& section); + +private: + std::string filename; + Sections sections; + bool bChanged; + + void Load(); + void Store(); +}; + + diff --git a/bazaar/plugin/gdal/frmts/ilwis/makefile.vc b/bazaar/plugin/gdal/frmts/ilwis/makefile.vc new file mode 100644 index 000000000..9897821f3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ilwis/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ilwisdataset.obj ilwiscoordinatesystem.obj + +EXTRAFLAGS = -I..\iso8211 + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/ingr/GNUmakefile b/bazaar/plugin/gdal/frmts/ingr/GNUmakefile new file mode 100644 index 000000000..ef5649cf9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ingr/GNUmakefile @@ -0,0 +1,24 @@ +GDAL_ROOT = ../.. + +include $(GDAL_ROOT)/GDALmake.opt + +OBJ = IntergraphDataset.o IntergraphBand.o IngrTypes.o JpegHelper.o + +CPPFLAGS := -I../gtiff $(CPPFLAGS) + +ifeq ($(TIFF_SETTING),internal) +ifeq ($(RENAME_INTERNAL_LIBTIFF_SYMBOLS),yes) +CPPFLAGS := $(CPPFLAGS) -DRENAME_INTERNAL_LIBTIFF_SYMBOLS +endif +CPPFLAGS := -I../gtiff/libtiff $(CPPFLAGS) +endif + +default: $(OBJ:.o=.$(OBJ_EXT)) + +$(OBJ) $(O_OBJ): IngrTypes.h IntergraphBand.h IntergraphDataset.h JpegHelper.h + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + diff --git a/bazaar/plugin/gdal/frmts/ingr/IngrTypes.cpp b/bazaar/plugin/gdal/frmts/ingr/IngrTypes.cpp new file mode 100644 index 000000000..b8282012e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ingr/IngrTypes.cpp @@ -0,0 +1,1757 @@ +/***************************************************************************** + * $Id: IngrTypes.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: Intergraph Raster Format support + * Purpose: Types support function + * Author: Ivan Lucena, [lucena_ivan at hotmail.com] + * + ****************************************************************************** + * Copyright (c) 2007, Ivan Lucena + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files ( the "Software" ), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "IngrTypes.h" +#include "JpegHelper.h" + +#ifdef DEBUG +#include "stdio.h" +#endif + +static const INGR_FormatDescription INGR_FormatTable[] = { + {PackedBinary, "Packed Binary", GDT_Byte}, + {ByteInteger, "Byte Integer", GDT_Byte}, + {WordIntegers, "Word Integers", GDT_Int16}, + {Integers32Bit, "Integers 32Bit", GDT_Int32}, + {FloatingPoint32Bit, "Floating Point 32Bit", GDT_Float32}, + {FloatingPoint64Bit, "Floating Point 64Bit", GDT_Float64}, + {Complex, "Complex", GDT_CFloat32}, + {DoublePrecisionComplex, "Double Precision Complex", GDT_CFloat64}, + {RunLengthEncoded, "Run Length Encoded Bitonal", GDT_Byte}, + {RunLengthEncodedC, "Run Length Encoded Color", GDT_Byte}, + {FigureOfMerit, "Figure of Merit", GDT_Byte}, + {DTMFlags, "DTMFlags", GDT_Byte}, + {RLEVariableValuesWithZS, "RLE Variable Values With ZS", GDT_Byte}, + {RLEBinaryValues, "RLE Binary Values", GDT_Byte}, + {RLEVariableValues, "RLE Variable Values", GDT_Byte}, + {RLEVariableValuesWithZ, "RLE Variable Values With Z", GDT_Byte}, + {RLEVariableValuesC, "RLE Variable Values C", GDT_Byte}, + {RLEVariableValuesN, "RLE Variable Values N", GDT_Byte}, + {QuadTreeEncoded, "Quad Tree Encoded", GDT_Byte}, + {CCITTGroup4, "CCITT Group 4", GDT_Byte}, + {RunLengthEncodedRGB, "Run Length Encoded RGB", GDT_Byte}, + {VariableRunLength, "Variable Run Length", GDT_Byte}, + {AdaptiveRGB, "Adaptive RGB", GDT_Byte}, + {Uncompressed24bit, "Uncompressed 24bit", GDT_Byte}, + {AdaptiveGrayScale, "Adaptive Gray Scale", GDT_Byte}, + {JPEGGRAY, "JPEG GRAY", GDT_Byte}, + {JPEGRGB, "JPEG RGB", GDT_Byte}, + {JPEGCYMK, "JPEG CYMK", GDT_Byte}, + {TiledRasterData, "Tiled Raste Data", GDT_Byte}, + {NotUsedReserved, "Not Used( Reserved )", GDT_Byte}, + {ContinuousTone, "Continuous Tone", GDT_Byte}, + {LineArt, "LineArt", GDT_Byte} +}; + +static const char *IngrOrientation[] = { + "Upper Left Vertical", + "Upper Right Vertical", + "Lower Left Vertical", + "Lower Right Vertical", + "Upper Left Horizontal", + "Upper Right Horizontal", + "Lower Left Horizontal", + "Lower Right Horizontal"}; + +static const GByte BitReverseTable[256] = +{ + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + +// ----------------------------------------------------------------------------- +// Scanline Orientation Flip Matrix +// ----------------------------------------------------------------------------- + +static const double INGR_URV_Flip[16] = + { + 1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + }; +static const double INGR_LLV_Flip[16] = + { + -1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + }; +static const double INGR_LRV_Flip[16] = + { + -1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + }; +static const double INGR_ULH_Flip[16] = + { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, -1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + }; +static const double INGR_URH_Flip[16] = + { + 1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, -1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + }; +static const double INGR_LLH_Flip[16] = + { + -1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, -1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + }; +static const double INGR_LRH_Flip[16] = + { + -1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, -1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + }; + +void INGR_MultiplyMatrix( double *padfA, real64 *padfB, const double *padfC ) +{ + int i; + int j; + + for( i = 0; i < 4; i++ ) + { + for( j = 0; j < 4; j++ ) + { + padfA[(i * 4) + j] = (double) + padfB[(i * 4) + 0] * padfC[(0 * 4) + j] + + padfB[(i * 4) + 1] * padfC[(1 * 4) + j] + + padfB[(i * 4) + 2] * padfC[(2 * 4) + j] + + padfB[(i * 4) + 3] * padfC[(3 * 4) + j]; + } + } +} + +// ----------------------------------------------------------------------------- +// INGR_GetDataType() +// ----------------------------------------------------------------------------- + +GDALDataType CPL_STDCALL INGR_GetDataType( uint16 eCode ) +{ + unsigned int i; + + for( i = 0; i < FORMAT_TAB_COUNT; i++ ) + { + if( eCode == INGR_FormatTable[i].eFormatCode ) + { + return INGR_FormatTable[i].eDataType; + } + } + + return GDT_Unknown; +} + +// ----------------------------------------------------------------------------- +// INGR_GetFormatName() +// ----------------------------------------------------------------------------- + +const char * CPL_STDCALL INGR_GetFormatName( uint16 eCode ) +{ + unsigned int i; + + for( i = 0; i < FORMAT_TAB_COUNT; i++ ) + { + if( eCode == INGR_FormatTable[i].eFormatCode ) + { + return INGR_FormatTable[i].pszName; + } + } + + return "Not Identified"; +} + +// ----------------------------------------------------------------------------- +// INGR_GetOrientation() +// ----------------------------------------------------------------------------- + +const char * CPL_STDCALL INGR_GetOrientation( uint8 nIndex ) +{ + if (nIndex < sizeof(IngrOrientation) / sizeof(IngrOrientation[0])) + return IngrOrientation[nIndex]; + else + return "invalid orientation"; +} + +// ----------------------------------------------------------------------------- +// INGR_GetFormat() +// ----------------------------------------------------------------------------- + +INGR_Format CPL_STDCALL INGR_GetFormat( GDALDataType eType, + const char *pszCompression ) +{ + if( EQUAL( pszCompression, "None" ) || + EQUAL( pszCompression, "" ) ) + { + switch ( eType ) + { + case GDT_Byte: return ByteInteger; + case GDT_Int16: return WordIntegers; + case GDT_UInt16: return WordIntegers; + case GDT_Int32: return Integers32Bit; + case GDT_UInt32: return Integers32Bit; + case GDT_Float32: return FloatingPoint32Bit; + case GDT_Float64: return FloatingPoint64Bit; + default: return ByteInteger; + } + } + + unsigned int i; + + for( i = 0; i < FORMAT_TAB_COUNT; i++ ) + { + if( EQUAL( pszCompression, INGR_FormatTable[i].pszName ) ) + { + return (INGR_Format) INGR_FormatTable[i].eFormatCode; + } + } + + return ByteInteger; +} + +// ----------------------------------------------------------------------------- +// INGR_GetTransMatrix() +// ----------------------------------------------------------------------------- + +void CPL_STDCALL INGR_GetTransMatrix( INGR_HeaderOne *pHeaderOne, + double *padfGeoTransform ) +{ + // ------------------------------------------------------------- + // Check for empty transformation matrix + // ------------------------------------------------------------- + + if( pHeaderOne->TransformationMatrix[0] == 0.0 && + pHeaderOne->TransformationMatrix[2] == 0.0 && + pHeaderOne->TransformationMatrix[3] == 0.0 && + pHeaderOne->TransformationMatrix[4] == 0.0 && + pHeaderOne->TransformationMatrix[5] == 0.0 && + pHeaderOne->TransformationMatrix[7] == 0.0 ) + { + padfGeoTransform[0] = 0.0; + padfGeoTransform[1] = 1.0; + padfGeoTransform[2] = 0.0; + padfGeoTransform[3] = 0.0; + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = 1.0; + return; + } + + // ------------------------------------------------------------- + // Calculate Concatened Tranformation Matrix based on Orientation + // ------------------------------------------------------------- + + double adfConcat[16]; + + switch( (INGR_Orientation ) pHeaderOne->ScanlineOrientation ) + { + case UpperLeftVertical: + { + unsigned int i = 0; + for(i = 0; i < 16; i++) + { + adfConcat[i] = (double) pHeaderOne->TransformationMatrix[i]; + } + } + break; + case UpperRightVertical: + INGR_MultiplyMatrix( adfConcat, pHeaderOne->TransformationMatrix, INGR_URV_Flip ); + break; + case LowerLeftVertical: + INGR_MultiplyMatrix( adfConcat, pHeaderOne->TransformationMatrix, INGR_LLV_Flip ); + break; + case LowerRightVertical: + INGR_MultiplyMatrix( adfConcat, pHeaderOne->TransformationMatrix, INGR_LRV_Flip ); + break; + case UpperLeftHorizontal: + INGR_MultiplyMatrix( adfConcat, pHeaderOne->TransformationMatrix, INGR_ULH_Flip ); + break; + case UpperRightHorizontal: + INGR_MultiplyMatrix( adfConcat, pHeaderOne->TransformationMatrix, INGR_URH_Flip ); + break; + case LowerLeftHorizontal: + INGR_MultiplyMatrix( adfConcat, pHeaderOne->TransformationMatrix, INGR_LLH_Flip ); + break; + case LowerRightHorizontal: + INGR_MultiplyMatrix( adfConcat, pHeaderOne->TransformationMatrix, INGR_LRH_Flip ); + break; + } + + // ------------------------------------------------------------- + // Convert to GDAL GeoTransformation Matrix + // ------------------------------------------------------------- + + padfGeoTransform[0] = adfConcat[3] - adfConcat[0] / 2; + padfGeoTransform[1] = adfConcat[0]; + padfGeoTransform[2] = adfConcat[1]; + padfGeoTransform[3] = adfConcat[7] + adfConcat[5] / 2; + padfGeoTransform[4] = adfConcat[4]; + padfGeoTransform[5] = - adfConcat[5]; +} + +// ----------------------------------------------------------------------------- +// INGR_SetTransMatrix() +// ----------------------------------------------------------------------------- + +void CPL_STDCALL INGR_SetTransMatrix( real64 *padfMatrix, double *padfGeoTransform ) +{ + unsigned int i; + + for( i = 0; i < 15; i++ ) + { + padfMatrix[i] = 0.0; + } + + padfMatrix[10] = 1.0; + padfMatrix[15] = 1.0; + + padfMatrix[3] = padfGeoTransform[0] + padfGeoTransform[1] / 2; + padfMatrix[0] = padfGeoTransform[1]; + padfMatrix[1] = padfGeoTransform[2]; + padfMatrix[7] = padfGeoTransform[3] + padfGeoTransform[5] / 2; + padfMatrix[4] = padfGeoTransform[4]; + padfMatrix[5] = - padfGeoTransform[5]; +} + +// ----------------------------------------------------------------------------- +// INGR_SetIGDSColors() +// ----------------------------------------------------------------------------- + +uint32 CPL_STDCALL INGR_SetIGDSColors( GDALColorTable *poColorTable, + INGR_ColorTable256 *pColorTableIGDS ) +{ + GDALColorEntry oEntry; + int i; + + for( i = 0; i < poColorTable->GetColorEntryCount(); i++ ) + { + poColorTable->GetColorEntryAsRGB( i, &oEntry ); + pColorTableIGDS->Entry[i].v_red = (uint8) oEntry.c1; + pColorTableIGDS->Entry[i].v_green = (uint8) oEntry.c2; + pColorTableIGDS->Entry[i].v_blue = (uint8) oEntry.c3; + } + + return i; +} + +// ----------------------------------------------------------------------------- +// INGR_GetTileDirectory() +// ----------------------------------------------------------------------------- + +uint32 CPL_STDCALL INGR_GetTileDirectory( VSILFILE *fp, + uint32 nOffset, + int nBandXSize, + int nBandYSize, + INGR_TileHeader *pTileDir, + INGR_TileItem **pahTiles) +{ + if( fp == NULL || + nBandXSize < 1 || + nBandYSize < 1 || + pTileDir == NULL ) + { + return 0; + } + + // ------------------------------------------------------------- + // Read it from the begging of the data segment + // ------------------------------------------------------------- + + GByte abyBuf[SIZEOF_TDIR]; + + if( ( VSIFSeekL( fp, nOffset, SEEK_SET ) == -1 ) || + ( VSIFReadL( abyBuf, 1, SIZEOF_TDIR, fp ) == 0 ) ) + { + CPLDebug("INGR", "Error reading tiles header"); + return 0; + } + + INGR_TileHeaderDiskToMem( pTileDir, abyBuf ); + + if (pTileDir->TileSize == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid tile size : %d", pTileDir->TileSize); + return 0; + } + + // ---------------------------------------------------------------- + // Calculate the number of tiles + // ---------------------------------------------------------------- + + int nTilesPerCol = (int) ceil( (float) nBandXSize / pTileDir->TileSize ); + int nTilesPerRow = (int) ceil( (float) nBandYSize / pTileDir->TileSize ); + + uint32 nTiles = nTilesPerCol * nTilesPerRow; + + // ---------------------------------------------------------------- + // Load the tile table (first tile s already read) + // ---------------------------------------------------------------- + + *pahTiles = (INGR_TileItem*) VSICalloc( nTiles, SIZEOF_TILE ); + GByte *pabyBuf = (GByte*) VSICalloc( ( nTiles - 1 ), SIZEOF_TILE ); + + if (*pahTiles == NULL || pabyBuf == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + CPLFree( *pahTiles ); + *pahTiles = NULL; + CPLFree( pabyBuf ); + return 0; + } + + (*pahTiles)[0].Start = pTileDir->First.Start; + (*pahTiles)[0].Allocated = pTileDir->First.Allocated; + (*pahTiles)[0].Used = pTileDir->First.Used; + + if( nTiles > 1 && + ( VSIFReadL( pabyBuf, ( nTiles - 1 ), SIZEOF_TILE, fp ) == 0 ) ) + { + CPLDebug("INGR", "Error reading tiles table"); + CPLFree( *pahTiles ); + *pahTiles = NULL; + CPLFree( pabyBuf ); + return 0; + } + + unsigned int i; + + for( i = 1; i < nTiles; i++ ) + { + INGR_TileItemDiskToMem( &((*pahTiles)[i]), + &pabyBuf[ (i - 1) * SIZEOF_TILE] ); + } + + CPLFree( pabyBuf ); + return nTiles; +} + +// ----------------------------------------------------------------------------- +// INGR_GetIGDSColors() +// ----------------------------------------------------------------------------- + +void CPL_STDCALL INGR_GetIGDSColors( VSILFILE *fp, + uint32 nOffset, + uint32 nEntries, + GDALColorTable *poColorTable ) +{ + if( fp == NULL || + nEntries == 0 || + nEntries > 256 || + poColorTable == NULL ) + { + return; + } + + // ------------------------------------------------------------- + // Read it from the middle of the second block + // ------------------------------------------------------------- + + uint32 nStart = nOffset + SIZEOF_HDR1 + SIZEOF_HDR2_A; + + INGR_ColorTable256 hIGDSColors; + + /* nEntries >= 0 && nEntries <= 256, so alloc is safe */ + GByte *pabyBuf = (GByte*) CPLCalloc( nEntries, SIZEOF_IGDS ); + + if( ( VSIFSeekL( fp, nStart, SEEK_SET ) == -1 ) || + ( VSIFReadL( pabyBuf, nEntries, SIZEOF_IGDS, fp ) == 0 ) ) + { + CPLFree( pabyBuf ); + return; + } + + unsigned int i = 0; + unsigned int n = 0; + + for( i = 0; i < nEntries; i++ ) + { + BUF2STRC( pabyBuf, n, hIGDSColors.Entry[i].v_red ); + BUF2STRC( pabyBuf, n, hIGDSColors.Entry[i].v_green ); + BUF2STRC( pabyBuf, n, hIGDSColors.Entry[i].v_blue ); + } + + CPLFree( pabyBuf ); + + // ------------------------------------------------------------- + // Read it to poColorTable + // ------------------------------------------------------------- + + GDALColorEntry oEntry; + + oEntry.c4 = 255; + + for( i = 0; i < nEntries; i++ ) + { + oEntry.c1 = hIGDSColors.Entry[i].v_red; + oEntry.c2 = hIGDSColors.Entry[i].v_green; + oEntry.c3 = hIGDSColors.Entry[i].v_blue; + poColorTable->SetColorEntry( i, &oEntry ); + } +} + +// ----------------------------------------------------------------------------- +// INGR_SetEnvironColors() +// ----------------------------------------------------------------------------- + +uint32 CPL_STDCALL INGR_SetEnvironColors( GDALColorTable *poColorTable, + INGR_ColorTableVar *pEnvironTable ) +{ + GDALColorEntry oEntry; + real32 fNormFactor = 0xfff / 255; + int i; + + for( i = 0; i < poColorTable->GetColorEntryCount(); i++ ) + { + poColorTable->GetColorEntryAsRGB( i, &oEntry ); + pEnvironTable->Entry[i].v_slot = (uint16) i; + pEnvironTable->Entry[i].v_red = (uint16) ( oEntry.c1 * fNormFactor ); + pEnvironTable->Entry[i].v_green = (uint16) ( oEntry.c2 * fNormFactor ); + pEnvironTable->Entry[i].v_blue = (uint16) ( oEntry.c3 * fNormFactor ); + } + + return i; +} + +// ----------------------------------------------------------------------------- +// INGR_GetEnvironVColors() +// ----------------------------------------------------------------------------- + +void CPL_STDCALL INGR_GetEnvironVColors( VSILFILE *fp, + uint32 nOffset, + uint32 nEntries, + GDALColorTable *poColorTable ) +{ + if( fp == NULL || + nEntries == 0 || + poColorTable == NULL ) + { + return; + } + + // ------------------------------------------------------------- + // Read it from the third block + // ------------------------------------------------------------- + + uint32 nStart = nOffset + SIZEOF_HDR1 + SIZEOF_HDR2; + + INGR_ColorTableVar hVLTColors; + + hVLTColors.Entry = (vlt_slot*) VSICalloc( nEntries, SIZEOF_VLTS ); + + GByte *pabyBuf = (GByte*) VSICalloc( nEntries, SIZEOF_VLTS ); + + if (hVLTColors.Entry == NULL || pabyBuf == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + CPLFree( pabyBuf ); + CPLFree( hVLTColors.Entry ); + return; + } + + if( ( VSIFSeekL( fp, nStart, SEEK_SET ) == -1 ) || + ( VSIFReadL( pabyBuf, nEntries, SIZEOF_VLTS, fp ) == 0 ) ) + { + CPLFree( pabyBuf ); + CPLFree( hVLTColors.Entry ); + return; + } + + unsigned int i = 0; + unsigned int n = 0; + + for( i = 0; i < nEntries; i++ ) + { + BUF2STRC( pabyBuf, n, hVLTColors.Entry[i].v_slot ); + BUF2STRC( pabyBuf, n, hVLTColors.Entry[i].v_red ); + BUF2STRC( pabyBuf, n, hVLTColors.Entry[i].v_green ); + BUF2STRC( pabyBuf, n, hVLTColors.Entry[i].v_blue ); + } + + CPLFree( pabyBuf ); + + +#if defined(CPL_MSB) + for (i = 0; i < nEntries; i++) + { + CPL_LSBPTR16(&hVLTColors.Entry[i].v_slot); + CPL_LSBPTR16(&hVLTColors.Entry[i].v_red); + CPL_LSBPTR16(&hVLTColors.Entry[i].v_green); + CPL_LSBPTR16(&hVLTColors.Entry[i].v_blue); + } +#endif + + // ------------------------------------------------------------- + // Get Maximum Intensity and Index Values + // ------------------------------------------------------------- + + real32 fMaxRed = 0.0; + real32 fMaxGreen = 0.0; + real32 fMaxBlues = 0.0; + + for( i = 0; i < nEntries; i++ ) + { + fMaxRed = MAX( fMaxRed , hVLTColors.Entry[i].v_red ); + fMaxGreen = MAX( fMaxGreen, hVLTColors.Entry[i].v_green ); + fMaxBlues = MAX( fMaxBlues, hVLTColors.Entry[i].v_blue ); + } + + // ------------------------------------------------------------- + // Calculate Normalization Factor + // ------------------------------------------------------------- + + real32 fNormFactor = 0.0; + + fNormFactor = ( fMaxRed > fMaxGreen ? fMaxRed : fMaxGreen ); + fNormFactor = ( fNormFactor > fMaxBlues ? fNormFactor : fMaxBlues ); + if (fNormFactor) + fNormFactor = 255 / fNormFactor; + + // ------------------------------------------------------------- + // Loads GDAL Color Table ( filling the wholes ) + // ------------------------------------------------------------- + + GDALColorEntry oEntry; + + for( i = 0; i < nEntries; i++ ) + { + oEntry.c1 = (short) ( hVLTColors.Entry[i].v_red * fNormFactor ); + oEntry.c2 = (short) ( hVLTColors.Entry[i].v_green * fNormFactor ); + oEntry.c3 = (short) ( hVLTColors.Entry[i].v_blue * fNormFactor ); + oEntry.c4 = (short) 255; + poColorTable->SetColorEntry( hVLTColors.Entry[i].v_slot, &oEntry ); + } + + CPLFree( hVLTColors.Entry ); +} + +// ----------------------------------------------------------------------------- +// SetMiniMax() +// ----------------------------------------------------------------------------- + +INGR_MinMax CPL_STDCALL INGR_SetMinMax( GDALDataType eType, double dValue ) +{ + INGR_MinMax uResult; + + switch ( eType ) + { + case GDT_Byte: + uResult.AsUint8 = (uint8) dValue; + break; + case GDT_Int16: + uResult.AsUint16 = (int16) dValue; + break; + case GDT_UInt16: + uResult.AsUint16 = (uint16) dValue; + break; + case GDT_Int32: + uResult.AsUint32 = (int32) dValue; + break; + case GDT_UInt32: + uResult.AsUint32 = (uint32) dValue; + break; + case GDT_Float32: + uResult.AsReal32 = (real32) dValue; + break; + case GDT_Float64: + uResult.AsReal64 = (real64) dValue; + default: + uResult.AsUint8 = (uint8) 0; + } + + return uResult; +} + +// ----------------------------------------------------------------------------- +// INGR_GetMinMax() +// ----------------------------------------------------------------------------- + +double CPL_STDCALL INGR_GetMinMax( GDALDataType eType, INGR_MinMax hValue ) +{ + switch ( eType ) + { + case GDT_Byte: return (double) hValue.AsUint8; + case GDT_Int16: return (double) hValue.AsUint16; + case GDT_UInt16: return (double) hValue.AsUint16; + case GDT_Int32: return (double) hValue.AsUint32; + case GDT_UInt32: return (double) hValue.AsUint32; + case GDT_Float32: return (double) hValue.AsReal32; + case GDT_Float64: return (double) hValue.AsReal64; + default: return (double) 0.0; + } +} + +// ----------------------------------------------------------------------------- +// INGR_GetDataBlockSize() +// ----------------------------------------------------------------------------- + +uint32 CPL_STDCALL INGR_GetDataBlockSize( const char *pszFilename, + uint32 nBandOffset, + uint32 nDataOffset ) +{ + if( nBandOffset == 0 ) + { + // ------------------------------------------------------------- + // Until the end of the file + // ------------------------------------------------------------- + + VSIStatBufL sStat; + VSIStatL( pszFilename, &sStat ); + return (uint32) (sStat.st_size - nDataOffset); + } + else + { + // ------------------------------------------------------------- + // Until the end of the band + // ------------------------------------------------------------- + + return nBandOffset - nDataOffset; + } +} + +// ----------------------------------------------------------------------------- +// INGR_CreateVirtualFile() +// ----------------------------------------------------------------------------- + +INGR_VirtualFile CPL_STDCALL INGR_CreateVirtualFile( const char *pszFilename, + INGR_Format eFormat, + int nXSize, + int nYSize, + int nTileSize, + int nQuality, + GByte *pabyBuffer, + int nBufferSize, + int nBand ) +{ + INGR_VirtualFile hVirtual; + + hVirtual.pszFileName = CPLSPrintf( "/vsimem/%s.virtual", + CPLGetBasename( pszFilename ) ); + + int nJPGComponents = 1; + + switch( eFormat ) + { + case JPEGRGB: + nJPGComponents = 3; + case JPEGGRAY: + { + GByte *pabyHeader = (GByte*) CPLCalloc( 1, 2048 ); + int nHeaderSize = JPGHLP_HeaderMaker( pabyHeader, + nTileSize, + nTileSize, + nJPGComponents, + 0, + nQuality ); + VSILFILE *fp = VSIFOpenL( hVirtual.pszFileName, "w+" ); + VSIFWriteL( pabyHeader, 1, nHeaderSize, fp ); + VSIFWriteL( pabyBuffer, 1, nBufferSize, fp ); + VSIFCloseL( fp ); + CPLFree( pabyHeader ); + break; + } + case CCITTGroup4: + { + REVERSEBITSBUFFER( pabyBuffer, nBufferSize ); + VSILFILE *fpL = VSIFOpenL( hVirtual.pszFileName, "w+" ); + TIFF *hTIFF = VSI_TIFFOpen( hVirtual.pszFileName, "w+", fpL ); + TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, nXSize ); + TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, nYSize ); + TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, 1 ); + TIFFSetField( hTIFF, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT ); + TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ); + TIFFSetField( hTIFF, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB ); + TIFFSetField( hTIFF, TIFFTAG_ROWSPERSTRIP, -1 ); + TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, 1 ); + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE ); + TIFFSetField( hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX4 ); + TIFFWriteRawStrip( hTIFF, 0, pabyBuffer, nBufferSize ); + TIFFWriteDirectory( hTIFF ); + TIFFClose( hTIFF ); + VSIFCloseL(fpL); + break; + } + default: + return hVirtual; + } + + hVirtual.poDS = (GDALDataset*) GDALOpen( hVirtual.pszFileName, GA_ReadOnly ); + + if( hVirtual.poDS ) + { + hVirtual.poBand = (GDALRasterBand*) GDALGetRasterBand( hVirtual.poDS, nBand ); + } + + return hVirtual; +} + +// ----------------------------------------------------------------------------- +// INGR_ReleaseVirtual() +// ----------------------------------------------------------------------------- + +void CPL_STDCALL INGR_ReleaseVirtual( INGR_VirtualFile *poTiffMem ) +{ + delete poTiffMem->poDS; + VSIUnlink( poTiffMem->pszFileName ); +} + +// ----------------------------------------------------------------------------- +// INGR_ReleaseVirtual() +// ----------------------------------------------------------------------------- + +int CPL_STDCALL INGR_ReadJpegQuality( VSILFILE *fp, uint32 nAppDataOfseet, + uint32 nSeekLimit ) +{ + if( nAppDataOfseet == 0 ) + { + return INGR_JPEGQDEFAULT; + } + + INGR_JPEGAppData hJpegData; + uint32 nNext = nAppDataOfseet; + + GByte abyBuf[SIZEOF_JPGAD]; + + do + { + if( ( VSIFSeekL( fp, nNext, SEEK_SET ) == -1 ) || + ( VSIFReadL( abyBuf, 1, SIZEOF_JPGAD, fp ) == 0 ) ) + { + return INGR_JPEGQDEFAULT; + } + + INGR_JPEGAppDataDiskToMem(&hJpegData, abyBuf); + + nNext += hJpegData.RemainingLength; + + if( nNext > ( nSeekLimit - SIZEOF_JPGAD ) ) + { + return INGR_JPEGQDEFAULT; + } + } + while( ! ( hJpegData.ApplicationType == 2 && + hJpegData.SubTypeCode == 12 ) ); + + return hJpegData.JpegQuality; +} + +// ----------------------------------------------------------------------------- +// INGR_Decode() +// +// Decode the various RLE compression options. +// +// Pass NULL as pabyDstData to obtain pnBytesConsumed and bypass decompression. +// ----------------------------------------------------------------------------- + +int CPL_STDCALL +INGR_Decode( INGR_Format eFormat, GByte *pabySrcData, GByte *pabyDstData, + uint32 nSrcBytes, uint32 nBlockSize, uint32 *pnBytesConsumed ) + +{ + switch( eFormat ) + { + case RunLengthEncoded: + return INGR_DecodeRunLengthBitonal( pabySrcData, pabyDstData, + nSrcBytes, nBlockSize, + pnBytesConsumed ); + + case RunLengthEncodedC: + return INGR_DecodeRunLengthPaletted( pabySrcData, pabyDstData, + nSrcBytes, nBlockSize, + pnBytesConsumed ); + + default: + return INGR_DecodeRunLength( pabySrcData, pabyDstData, + nSrcBytes, nBlockSize, + pnBytesConsumed ); + } +} + +// ----------------------------------------------------------------------------- +// INGR_DecodeRunLength() +// ----------------------------------------------------------------------------- + +int CPL_STDCALL INGR_DecodeRunLength( GByte *pabySrcData, GByte *pabyDstData, + uint32 nSrcBytes, uint32 nBlockSize, + uint32 *pnBytesConsumed ) +{ + signed char cAtomHead; + + unsigned int nRun; + unsigned int i; + unsigned int iInput; + unsigned int iOutput; + unsigned int inc; + + iInput = 0; + iOutput = 0; + + while( ( iInput < nSrcBytes ) && ( iOutput < nBlockSize ) ) + { + cAtomHead = (char) pabySrcData[iInput++]; + + if( cAtomHead > 0 ) + { + nRun = cAtomHead; + + if (pabyDstData) + { + for( i = 0; i < nRun && iInput < nSrcBytes && iOutput < nBlockSize; i++ ) + { + pabyDstData[iOutput++] = pabySrcData[iInput++]; + } + } + else + { + inc = MIN(nRun, MIN(nSrcBytes - iInput, nBlockSize - iOutput)); + iInput += inc; + iOutput += inc; + } + } + else if( cAtomHead < 0 ) + { + nRun = abs( cAtomHead ); + + if (pabyDstData) + { + for( i = 0; i < nRun && iInput < nSrcBytes && iOutput < nBlockSize; i++ ) + { + pabyDstData[iOutput++] = pabySrcData[iInput]; + } + } + else + { + inc = MIN(nRun, MIN(nSrcBytes - iInput, nBlockSize - iOutput)); + iOutput += inc; + } + iInput++; + } + } + + if( pnBytesConsumed != NULL ) + *pnBytesConsumed = iInput; + + return iOutput; +} + +// ----------------------------------------------------------------------------- +// INGR_DecodeRunLengthPaletted() +// ----------------------------------------------------------------------------- + +int CPL_STDCALL +INGR_DecodeRunLengthPaletted( GByte *pabySrcData, GByte *pabyDstData, + uint32 nSrcBytes, uint32 nBlockSize, + uint32 *pnBytesConsumed ) +{ + unsigned short nColor; + unsigned short nCount; + + unsigned int i; + unsigned int iInput; + unsigned int iOutput; + + unsigned short *pauiSrc = (unsigned short *) pabySrcData; + unsigned int nSrcShorts = nSrcBytes / 2; + + iInput = 0; + iOutput = 0; + + if ( nSrcShorts == 0 ) + return 0; + + do + { + nCount = 0; + nColor = CPL_LSBWORD16( pauiSrc[ iInput ] ); + iInput++; + + if( nColor == 0x5900 || + nColor == 0x5901 ) + { + iInput++; + continue; + } + + if ( iInput < nSrcShorts ) + { + nCount = CPL_LSBWORD16( pauiSrc[ iInput ] ); + iInput++; + } + + if (pabyDstData) + { + for( i = 0; i < nCount && iOutput < nBlockSize; i++ ) + { + pabyDstData[iOutput++] = (unsigned char) nColor; + } + } + else + { + iOutput += MIN(nCount, nBlockSize - iOutput); + } + } + while( ( iInput < nSrcShorts ) && ( iOutput < nBlockSize) ); + + if( pnBytesConsumed != NULL ) + *pnBytesConsumed = iInput * 2; + + return iOutput; +} + +// ----------------------------------------------------------------------------- +// INGR_DecodeRunLengthBitonal() +// ----------------------------------------------------------------------------- + +int CPL_STDCALL +INGR_DecodeRunLengthBitonal( GByte *pabySrcData, GByte *pabyDstData, + uint32 nSrcBytes, uint32 nBlockSize, + uint32 *pnBytesConsumed ) +{ + unsigned short i; + unsigned int j; + unsigned int iInput = 0; + unsigned int iOutput = 0; + unsigned short *pauiSrc = (unsigned short *) pabySrcData; + unsigned int nSrcShorts = nSrcBytes / 2; + unsigned short nRun; + unsigned char nValue = 0; + bool bHeader = true; + + if (nSrcShorts == 0) + return 0; + + + // Check for scanline header + do + { + unsigned int nWordsInScanline; + unsigned int nTotal; + + if( CPL_LSBWORD16(pauiSrc[0]) != 0x5900 ) + { + bHeader = false; + break; + } + + if (nBlockSize < 0x00005900) + { + // Can only be a header since we can never have a span of 22784 + // if the width of the scanline is known to be less than that. + break; + } + + // Here follows a more stringent test that this is really a scanline header + // and not a span in a file that has no scanline headers. + // Here is the scanline header information + // 0: 0x5900 + // 2: words to follow + // 4: line id (mod 16 bits) + // 6: 0x0000 (pixels to skip, assumed to be 0) + + // Scanline with header has minimum of 5 entries. + if (nSrcShorts < 5) + { + bHeader = false; + break; + } + + // Test that words to follow is at least 3 and odd + // Test that pixels to skip is 0 + if ((CPL_LSBWORD16(pauiSrc[1]) < 3) || + ((CPL_LSBWORD16(pauiSrc[1]) & 1) == 0) || + (CPL_LSBWORD16(pauiSrc[3]) != 0)) + { + bHeader = false; + break; + } + + nWordsInScanline = ((unsigned int) CPL_LSBWORD16(pauiSrc[1])) + 2; + if (nSrcShorts >= nWordsInScanline + 5) + { + // Do some quick extra tests on next scanline. + + // Check that the next scanline starts with 0x5900 + // Check that the next scanline's words-to-follow is at least 3 and odd + // Check that the next scanline's skip pixel offset is 0 + // Check that the next scanline's line number is 1 more than this one. + if ((CPL_LSBWORD16(pauiSrc[nWordsInScanline]) != 0x5900) || + (CPL_LSBWORD16(pauiSrc[nWordsInScanline+1]) < 3) || + ((CPL_LSBWORD16(pauiSrc[nWordsInScanline+1]) & 1) == 0) || + (CPL_LSBWORD16(pauiSrc[nWordsInScanline+3]) != 0) || + (((((unsigned int)CPL_LSBWORD16(pauiSrc[2])) + 1) & 0x0000FFFF) != + ((unsigned int)CPL_LSBWORD16(pauiSrc[nWordsInScanline+2])))) + { + bHeader = false; + break; + } + } + else if (nSrcShorts < nWordsInScanline) + { + // Cannot be a header since there is not enough data. + bHeader = false; + break; + } + + // If we get here, we add all the span values and see if they add up to the nBlockSize. + j = 0; + nTotal = 0; + + for(;j < nWordsInScanline - 4; j++) + { + nTotal += (unsigned int) CPL_LSBWORD16(pauiSrc[j+4]); + } + + if (nTotal != nBlockSize) + bHeader = false; + + // Fall through. We have a valid scanline header... probably. + + } while(0); + + if( bHeader ) + iInput+=4; // 0x5900 tag, line id, line data size, skip offset + + if (iInput >= nSrcShorts) + return 0; + + do + { + nRun = CPL_LSBWORD16(pauiSrc[ iInput ]); + iInput++; + + if (pabyDstData) + { + for( i = 0; i < nRun && iOutput < nBlockSize; i++ ) + { + pabyDstData[ iOutput++ ] = nValue; + } + + nValue = ( nValue == 1 ? 0 : 1 ); + } + else + { + iOutput += MIN(nRun, nBlockSize - iOutput); + } + + } + while( ( iInput < nSrcShorts ) && ( iOutput < nBlockSize ) ); + + // Skip over any empty end of line spans. + if ((iInput < nSrcShorts) && (CPL_LSBWORD16(pauiSrc[ iInput ]) == 0)) + { + while((iInput < nSrcShorts) && (CPL_LSBWORD16(pauiSrc[ iInput ]) == 0)) + iInput++; + + // Should never be pairs of consecutive empty spans, + // except at end and start of two scanlines. + // We must adjust to start at the correct location in the + // next scanline, otherwise the colours will be inverted. + // iInput should be odd since scanline is + // supposed to start and end with OFF span. + if ((iInput&1) == 0) + iInput--; + } + + + if( pnBytesConsumed != NULL ) + *pnBytesConsumed = iInput * 2; + + return iOutput; +} + +// ----------------------------------------------------------------------------- +// INGR_DecodeRunLengthBitonalTiled() +// ----------------------------------------------------------------------------- + +int CPL_STDCALL +INGR_DecodeRunLengthBitonalTiled( GByte *pabySrcData, GByte *pabyDstData, + uint32 nSrcBytes, uint32 nBlockSize, + uint32 *pnBytesConsumed ) +{ + unsigned short i; + unsigned int iInput = 0; + unsigned int iOutput = 0; + unsigned short *pauiSrc = (unsigned short *) pabySrcData; + unsigned int nSrcShorts = nSrcBytes / 2; + unsigned short nRun = 0; + unsigned char nValue = 0; + unsigned short previous = 0; + + if (nSrcShorts == 0) + return 0; + + if( CPL_LSBWORD16(pauiSrc[0]) != 0x5900 ) + { + nRun = 256; + nValue = 0; + previous = 0; + do + { + previous = nRun; + + nRun = CPL_LSBWORD16(pauiSrc[ iInput ]); + iInput++; + + if( nRun == 0 && previous == 0 ) // new line + { + nValue = 0; + } + + for( i = 0; i < nRun && iOutput < nBlockSize; i++ ) + { + pabyDstData[ iOutput++ ] = nValue; + } + + if( nRun != 0 ) + { + nValue = ( nValue == 1 ? 0 : 1 ); + } + } + while( ( iInput < nSrcShorts ) && ( iOutput < nBlockSize ) ); + } + else + { + do + { + nRun = CPL_LSBWORD16(pauiSrc[ iInput ]); + iInput++; + + if( nRun == 0x5900 ) + { + iInput+=3; // line id, data size, skip offset + continue; + } + + for( i = 0; i < nRun && iOutput < nBlockSize; i++ ) + { + pabyDstData[ iOutput++ ] = nValue; + } + + nValue = ( nValue == 1 ? 0 : 1 ); + } + while( ( iInput < nSrcShorts ) && ( iOutput < nBlockSize ) ); + } + + if( pnBytesConsumed != NULL ) + { + *pnBytesConsumed = iInput * 2; + } + + return iOutput; +} + +void CPL_STDCALL INGR_HeaderOneDiskToMem(INGR_HeaderOne* pHeaderOne, const GByte *pabyBuf) +{ + unsigned int n = 0; + + BUF2STRC( pabyBuf, n, pHeaderOne->HeaderType ); + BUF2STRC( pabyBuf, n, pHeaderOne->WordsToFollow ); + BUF2STRC( pabyBuf, n, pHeaderOne->DataTypeCode ); + BUF2STRC( pabyBuf, n, pHeaderOne->ApplicationType ); + BUF2STRC( pabyBuf, n, pHeaderOne->XViewOrigin ); + BUF2STRC( pabyBuf, n, pHeaderOne->YViewOrigin ); + BUF2STRC( pabyBuf, n, pHeaderOne->ZViewOrigin ); + BUF2STRC( pabyBuf, n, pHeaderOne->XViewExtent ); + BUF2STRC( pabyBuf, n, pHeaderOne->YViewExtent ); + BUF2STRC( pabyBuf, n, pHeaderOne->ZViewExtent ); + BUF2STRC( pabyBuf, n, pHeaderOne->TransformationMatrix ); + BUF2STRC( pabyBuf, n, pHeaderOne->PixelsPerLine ); + BUF2STRC( pabyBuf, n, pHeaderOne->NumberOfLines ); + BUF2STRC( pabyBuf, n, pHeaderOne->DeviceResolution ); + BUF2STRC( pabyBuf, n, pHeaderOne->ScanlineOrientation ); + BUF2STRC( pabyBuf, n, pHeaderOne->ScannableFlag ); + BUF2STRC( pabyBuf, n, pHeaderOne->RotationAngle ); + BUF2STRC( pabyBuf, n, pHeaderOne->SkewAngle ); + BUF2STRC( pabyBuf, n, pHeaderOne->DataTypeModifier ); + BUF2STRC( pabyBuf, n, pHeaderOne->DesignFileName ); + BUF2STRC( pabyBuf, n, pHeaderOne->DataBaseFileName ); + BUF2STRC( pabyBuf, n, pHeaderOne->ParentGridFileName ); + BUF2STRC( pabyBuf, n, pHeaderOne->FileDescription ); + BUF2STRC( pabyBuf, n, pHeaderOne->Minimum ); + BUF2STRC( pabyBuf, n, pHeaderOne->Maximum ); + BUF2STRC( pabyBuf, n, pHeaderOne->Reserved ); + BUF2STRC( pabyBuf, n, pHeaderOne->GridFileVersion ); + +#if defined(CPL_MSB) + CPL_LSBPTR16(&pHeaderOne->WordsToFollow); + CPL_LSBPTR16(&pHeaderOne->DataTypeCode); + CPL_LSBPTR16(&pHeaderOne->ApplicationType); + CPL_LSBPTR32(&pHeaderOne->PixelsPerLine); + CPL_LSBPTR32(&pHeaderOne->NumberOfLines); + CPL_LSBPTR16(&pHeaderOne->DeviceResolution); + CPL_LSBPTR16(&pHeaderOne->DataTypeModifier); + switch (INGR_GetDataType(pHeaderOne->DataTypeCode)) + { + case GDT_Byte: + pHeaderOne->Minimum.AsUint8 = *(uint8*)&(pHeaderOne->Minimum); + pHeaderOne->Maximum.AsUint8 = *(uint8*)&(pHeaderOne->Maximum); + break; + case GDT_Int16: + pHeaderOne->Minimum.AsUint16 = CPL_LSBWORD16(*(uint16*)&(pHeaderOne->Minimum)); + pHeaderOne->Maximum.AsUint16 = CPL_LSBWORD16(*(uint16*)&(pHeaderOne->Maximum)); + break; + case GDT_UInt16: + pHeaderOne->Minimum.AsUint16 = CPL_LSBWORD16(*(uint16*)&(pHeaderOne->Minimum)); + pHeaderOne->Maximum.AsUint16 = CPL_LSBWORD16(*(uint16*)&(pHeaderOne->Maximum)); + break; + case GDT_Int32: + pHeaderOne->Minimum.AsUint32 = CPL_LSBWORD32(*(uint32*)&(pHeaderOne->Minimum)); + pHeaderOne->Maximum.AsUint32 = CPL_LSBWORD32(*(uint32*)&(pHeaderOne->Maximum)); + break; + case GDT_UInt32: + pHeaderOne->Minimum.AsUint32 = CPL_LSBWORD32(*(uint32*)&(pHeaderOne->Minimum)); + pHeaderOne->Maximum.AsUint32 = CPL_LSBWORD32(*(uint32*)&(pHeaderOne->Maximum)); + break; + /* FIXME ? I'm not sure this is correct for floats */ + case GDT_Float32: + pHeaderOne->Minimum.AsUint32 = CPL_LSBWORD32(*(uint32*)&(pHeaderOne->Minimum)); + pHeaderOne->Maximum.AsUint32 = CPL_LSBWORD32(*(uint32*)&(pHeaderOne->Maximum)); + break; + case GDT_Float64: + CPL_LSBPTR64(&pHeaderOne->Minimum.AsReal64); CPL_LSBPTR64(&pHeaderOne->Maximum.AsReal64); + break; + default: break; + } +#endif + + // -------------------------------------------------------------------- + // Convert WAX REAL*8 to IEEE double + // -------------------------------------------------------------------- + + if( pHeaderOne->GridFileVersion == 1 || + ( pHeaderOne->GridFileVersion == 2 && + ( pHeaderOne->TransformationMatrix[10] != 1.0 && + pHeaderOne->TransformationMatrix[15] != 1.0 ) ) ) + { + INGR_DGN2IEEEDouble( &pHeaderOne->XViewOrigin ); + INGR_DGN2IEEEDouble( &pHeaderOne->YViewOrigin ); + INGR_DGN2IEEEDouble( &pHeaderOne->ZViewOrigin ); + INGR_DGN2IEEEDouble( &pHeaderOne->XViewExtent ); + INGR_DGN2IEEEDouble( &pHeaderOne->YViewExtent ); + INGR_DGN2IEEEDouble( &pHeaderOne->ZViewExtent ); + INGR_DGN2IEEEDouble( &pHeaderOne->RotationAngle ); + INGR_DGN2IEEEDouble( &pHeaderOne->SkewAngle ); + + uint8 i; + + for( i = 0; i < 16; i++ ) + { + INGR_DGN2IEEEDouble( &pHeaderOne->TransformationMatrix[i]); + } + } + else if (pHeaderOne->GridFileVersion == 3) + { +#ifdef CPL_MSB + CPL_LSBPTR64( &pHeaderOne->XViewOrigin ); + CPL_LSBPTR64( &pHeaderOne->YViewOrigin ); + CPL_LSBPTR64( &pHeaderOne->ZViewOrigin ); + CPL_LSBPTR64( &pHeaderOne->XViewExtent ); + CPL_LSBPTR64( &pHeaderOne->YViewExtent ); + CPL_LSBPTR64( &pHeaderOne->ZViewExtent ); + CPL_LSBPTR64( &pHeaderOne->RotationAngle ); + CPL_LSBPTR64( &pHeaderOne->SkewAngle ); + + uint8 i; + + for( i = 0; i < 16; i++ ) + { + CPL_LSBPTR64( &pHeaderOne->TransformationMatrix[i]); + } +#endif + } +} + +void CPL_STDCALL INGR_HeaderOneMemToDisk(const INGR_HeaderOne* pHeaderOne, GByte *pabyBuf) +{ + unsigned int n = 0; + INGR_HeaderOne* pLSBHeaderOne; +#if defined(CPL_MSB) + pLSBHeaderOne = (INGR_HeaderOne* )CPLMalloc(sizeof(INGR_HeaderOne)); + memcpy(pLSBHeaderOne, pHeaderOne, sizeof(INGR_HeaderOne)); + + switch (INGR_GetDataType(pLSBHeaderOne->DataTypeCode)) + { + case GDT_Byte: *(uint8*)&(pLSBHeaderOne->Minimum) = pLSBHeaderOne->Minimum.AsUint8; + *(uint8*)&(pLSBHeaderOne->Maximum) = pLSBHeaderOne->Maximum.AsUint8; break; + case GDT_Int16: *(uint16*)&(pLSBHeaderOne->Minimum) = CPL_LSBWORD16(pLSBHeaderOne->Minimum.AsUint16); + *(uint16*)&(pLSBHeaderOne->Maximum) = CPL_LSBWORD16(pLSBHeaderOne->Maximum.AsUint16); break; + case GDT_UInt16: *(uint16*)&(pLSBHeaderOne->Minimum) = CPL_LSBWORD16(pLSBHeaderOne->Minimum.AsUint16); + *(uint16*)&(pLSBHeaderOne->Maximum) = CPL_LSBWORD16(pLSBHeaderOne->Maximum.AsUint16); break; + case GDT_Int32: *(uint32*)&(pLSBHeaderOne->Minimum) = CPL_LSBWORD32(pLSBHeaderOne->Minimum.AsUint32); + *(uint32*)&(pLSBHeaderOne->Maximum) = CPL_LSBWORD32(pLSBHeaderOne->Maximum.AsUint32); break; + case GDT_UInt32: *(uint32*)&(pLSBHeaderOne->Minimum) = CPL_LSBWORD32(pLSBHeaderOne->Minimum.AsUint32); + *(uint32*)&(pLSBHeaderOne->Maximum) = CPL_LSBWORD32(pLSBHeaderOne->Maximum.AsUint32); break; + /* FIXME ? I'm not sure this is correct for floats */ + case GDT_Float32: *(uint32*)&(pLSBHeaderOne->Minimum) = CPL_LSBWORD32(pLSBHeaderOne->Minimum.AsUint32); + *(uint32*)&(pLSBHeaderOne->Maximum) = CPL_LSBWORD32(pLSBHeaderOne->Maximum.AsUint32); break; + case GDT_Float64: CPL_LSBPTR64(&pLSBHeaderOne->Minimum.AsReal64); CPL_LSBPTR64(&pLSBHeaderOne->Maximum.AsReal64); break; + default: break; + } + + CPL_LSBPTR16(&pLSBHeaderOne->WordsToFollow); + CPL_LSBPTR16(&pLSBHeaderOne->DataTypeCode); + CPL_LSBPTR16(&pLSBHeaderOne->ApplicationType); + CPL_LSBPTR32(&pLSBHeaderOne->PixelsPerLine); + CPL_LSBPTR32(&pLSBHeaderOne->NumberOfLines); + CPL_LSBPTR16(&pLSBHeaderOne->DeviceResolution); + CPL_LSBPTR16(&pLSBHeaderOne->DataTypeModifier); + + if (pLSBHeaderOne->GridFileVersion == 3) + { + CPL_LSBPTR64( &pLSBHeaderOne->XViewOrigin ); + CPL_LSBPTR64( &pLSBHeaderOne->YViewOrigin ); + CPL_LSBPTR64( &pLSBHeaderOne->ZViewOrigin ); + CPL_LSBPTR64( &pLSBHeaderOne->XViewExtent ); + CPL_LSBPTR64( &pLSBHeaderOne->YViewExtent ); + CPL_LSBPTR64( &pLSBHeaderOne->ZViewExtent ); + CPL_LSBPTR64( &pLSBHeaderOne->RotationAngle ); + CPL_LSBPTR64( &pLSBHeaderOne->SkewAngle ); + + uint8 i; + + for( i = 0; i < 16; i++ ) + { + CPL_LSBPTR64( &pLSBHeaderOne->TransformationMatrix[i]); + } + } +#else + pLSBHeaderOne = (INGR_HeaderOne* )pHeaderOne; +#endif + + STRC2BUF( pabyBuf, n, pLSBHeaderOne->HeaderType ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->WordsToFollow ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->DataTypeCode ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->ApplicationType ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->XViewOrigin ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->YViewOrigin ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->ZViewOrigin ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->XViewExtent ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->YViewExtent ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->ZViewExtent ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->TransformationMatrix ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->PixelsPerLine ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->NumberOfLines ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->DeviceResolution ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->ScanlineOrientation ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->ScannableFlag ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->RotationAngle ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->SkewAngle ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->DataTypeModifier ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->DesignFileName ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->DataBaseFileName ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->ParentGridFileName ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->FileDescription ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->Minimum ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->Maximum ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->Reserved ); + STRC2BUF( pabyBuf, n, pLSBHeaderOne->GridFileVersion ); + +#if defined(CPL_MSB) + CPLFree(pLSBHeaderOne); +#endif +} + +void CPL_STDCALL INGR_HeaderTwoADiskToMem(INGR_HeaderTwoA* pHeaderTwo, const GByte *pabyBuf) +{ + unsigned int n = 0; + + BUF2STRC( pabyBuf, n, pHeaderTwo->Gain ); + BUF2STRC( pabyBuf, n, pHeaderTwo->OffsetThreshold ); + BUF2STRC( pabyBuf, n, pHeaderTwo->View1 ); + BUF2STRC( pabyBuf, n, pHeaderTwo->View2 ); + BUF2STRC( pabyBuf, n, pHeaderTwo->ViewNumber ); + BUF2STRC( pabyBuf, n, pHeaderTwo->Reserved2 ); + BUF2STRC( pabyBuf, n, pHeaderTwo->Reserved3 ); + BUF2STRC( pabyBuf, n, pHeaderTwo->AspectRatio ); + BUF2STRC( pabyBuf, n, pHeaderTwo->CatenatedFilePointer ); + BUF2STRC( pabyBuf, n, pHeaderTwo->ColorTableType ); + BUF2STRC( pabyBuf, n, pHeaderTwo->Reserved8 ); + BUF2STRC( pabyBuf, n, pHeaderTwo->NumberOfCTEntries ); + BUF2STRC( pabyBuf, n, pHeaderTwo->ApplicationPacketPointer ); + BUF2STRC( pabyBuf, n, pHeaderTwo->ApplicationPacketLength ); + BUF2STRC( pabyBuf, n, pHeaderTwo->Reserved ); + +#if defined(CPL_MSB) + CPL_LSBPTR64(&pHeaderTwo->AspectRatio); + CPL_LSBPTR32(&pHeaderTwo->CatenatedFilePointer); + CPL_LSBPTR16(&pHeaderTwo->ColorTableType); + CPL_LSBPTR32(&pHeaderTwo->NumberOfCTEntries); + CPL_LSBPTR32(&pHeaderTwo->ApplicationPacketPointer); + CPL_LSBPTR32(&pHeaderTwo->ApplicationPacketLength); +#endif +} + +void CPL_STDCALL INGR_HeaderTwoAMemToDisk(const INGR_HeaderTwoA* pHeaderTwo, GByte *pabyBuf) +{ + unsigned int n = 0; + INGR_HeaderTwoA* pLSBHeaderTwo; +#if defined(CPL_MSB) + pLSBHeaderTwo = (INGR_HeaderTwoA* )CPLMalloc(sizeof(INGR_HeaderTwoA)); + memcpy(pLSBHeaderTwo, pHeaderTwo, sizeof(INGR_HeaderTwoA)); + + CPL_LSBPTR64(&pLSBHeaderTwo->AspectRatio); + CPL_LSBPTR32(&pLSBHeaderTwo->CatenatedFilePointer); + CPL_LSBPTR16(&pLSBHeaderTwo->ColorTableType); + CPL_LSBPTR32(&pLSBHeaderTwo->NumberOfCTEntries); + CPL_LSBPTR32(&pLSBHeaderTwo->ApplicationPacketPointer); + CPL_LSBPTR32(&pLSBHeaderTwo->ApplicationPacketLength); +#else + pLSBHeaderTwo = (INGR_HeaderTwoA* )pHeaderTwo; +#endif + + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->Gain ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->OffsetThreshold ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->View1 ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->View2 ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->ViewNumber ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->Reserved2 ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->Reserved3 ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->AspectRatio ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->CatenatedFilePointer ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->ColorTableType ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->Reserved8 ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->NumberOfCTEntries ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->ApplicationPacketPointer ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->ApplicationPacketLength ); + STRC2BUF( pabyBuf, n, pLSBHeaderTwo->Reserved ); + +#if defined(CPL_MSB) + CPLFree(pLSBHeaderTwo); +#endif +} + +void CPL_STDCALL INGR_TileHeaderDiskToMem(INGR_TileHeader* pTileHeader, const GByte *pabyBuf) +{ + unsigned int n = 0; + + BUF2STRC( pabyBuf, n, pTileHeader->ApplicationType ); + BUF2STRC( pabyBuf, n, pTileHeader->SubTypeCode ); + BUF2STRC( pabyBuf, n, pTileHeader->WordsToFollow ); + BUF2STRC( pabyBuf, n, pTileHeader->PacketVersion ); + BUF2STRC( pabyBuf, n, pTileHeader->Identifier ); + BUF2STRC( pabyBuf, n, pTileHeader->Reserved ); + BUF2STRC( pabyBuf, n, pTileHeader->Properties ); + BUF2STRC( pabyBuf, n, pTileHeader->DataTypeCode ); + BUF2STRC( pabyBuf, n, pTileHeader->Reserved2 ); + BUF2STRC( pabyBuf, n, pTileHeader->TileSize ); + BUF2STRC( pabyBuf, n, pTileHeader->Reserved3 ); + BUF2STRC( pabyBuf, n, pTileHeader->First.Start ); + BUF2STRC( pabyBuf, n, pTileHeader->First.Allocated ); + BUF2STRC( pabyBuf, n, pTileHeader->First.Used ); + +#if defined(CPL_MSB) + CPL_LSBPTR16(&pTileHeader->ApplicationType); + CPL_LSBPTR16(&pTileHeader->SubTypeCode); + CPL_LSBPTR32(&pTileHeader->WordsToFollow); + CPL_LSBPTR16(&pTileHeader->PacketVersion); + CPL_LSBPTR16(&pTileHeader->Identifier); + CPL_LSBPTR16(&pTileHeader->Properties); + CPL_LSBPTR16(&pTileHeader->DataTypeCode); + CPL_LSBPTR32(&pTileHeader->TileSize); + CPL_LSBPTR32(&pTileHeader->First.Start); + CPL_LSBPTR32(&pTileHeader->First.Allocated); + CPL_LSBPTR32(&pTileHeader->First.Used); +#endif +} + +void CPL_STDCALL INGR_TileItemDiskToMem(INGR_TileItem* pTileItem, const GByte *pabyBuf) +{ + unsigned int n = 0; + + BUF2STRC( pabyBuf, n, pTileItem->Start ); + BUF2STRC( pabyBuf, n, pTileItem->Allocated ); + BUF2STRC( pabyBuf, n, pTileItem->Used ); + +#if defined(CPL_MSB) + CPL_LSBPTR32(&pTileItem->Start); + CPL_LSBPTR32(&pTileItem->Allocated); + CPL_LSBPTR32(&pTileItem->Used); +#endif +} + +void CPL_STDCALL INGR_JPEGAppDataDiskToMem(INGR_JPEGAppData* pJPEGAppData, const GByte *pabyBuf) +{ + unsigned int n = 0; + + BUF2STRC( pabyBuf, n, pJPEGAppData->ApplicationType ); + BUF2STRC( pabyBuf, n, pJPEGAppData->SubTypeCode ); + BUF2STRC( pabyBuf, n, pJPEGAppData->RemainingLength ); + BUF2STRC( pabyBuf, n, pJPEGAppData->PacketVersion ); + BUF2STRC( pabyBuf, n, pJPEGAppData->JpegQuality ); + +#if defined(CPL_MSB) + CPL_LSBPTR16(&pJPEGAppData->ApplicationType); + CPL_LSBPTR16(&pJPEGAppData->SubTypeCode); + CPL_LSBPTR32(&pJPEGAppData->RemainingLength); + CPL_LSBPTR16(&pJPEGAppData->PacketVersion); + CPL_LSBPTR16(&pJPEGAppData->JpegQuality); +#endif +} + + +// ------------------------------------------------------------------ +// Pasted from the DNG OGR Driver to avoid dependency on OGR +// ------------------------------------------------------------------ + +typedef struct dbl { + GUInt32 hi; + GUInt32 lo; +} double64_t; + +/************************************************************************/ +/* INGR_DGN2IEEEDouble() */ +/************************************************************************/ + +void INGR_DGN2IEEEDouble(void * dbl) + +{ + double64_t dt; + GUInt32 sign; + GUInt32 exponent; + GUInt32 rndbits; + unsigned char *src; + unsigned char *dest; + +/* -------------------------------------------------------------------- */ +/* Arrange the VAX double so that it may be accessed by a */ +/* double64_t structure, (two GUInt32s). */ +/* -------------------------------------------------------------------- */ + src = (unsigned char *) dbl; + dest = (unsigned char *) &dt; +#ifdef CPL_LSB + dest[2] = src[0]; + dest[3] = src[1]; + dest[0] = src[2]; + dest[1] = src[3]; + dest[6] = src[4]; + dest[7] = src[5]; + dest[4] = src[6]; + dest[5] = src[7]; +#else + dest[1] = src[0]; + dest[0] = src[1]; + dest[3] = src[2]; + dest[2] = src[3]; + dest[5] = src[4]; + dest[4] = src[5]; + dest[7] = src[6]; + dest[6] = src[7]; +#endif + +/* -------------------------------------------------------------------- */ +/* Save the sign of the double */ +/* -------------------------------------------------------------------- */ + sign = dt.hi & 0x80000000; + +/* -------------------------------------------------------------------- */ +/* Adjust the exponent so that we may work with it */ +/* -------------------------------------------------------------------- */ + exponent = dt.hi >> 23; + exponent = exponent & 0x000000ff; + + if (exponent) + exponent = exponent -129 + 1023; + +/* -------------------------------------------------------------------- */ +/* Save the bits that we are discarding so we can round properly */ +/* -------------------------------------------------------------------- */ + rndbits = dt.lo & 0x00000007; + + dt.lo = dt.lo >> 3; + dt.lo = (dt.lo & 0x1fffffff) | (dt.hi << 29); + + if (rndbits) + dt.lo = dt.lo | 0x00000001; + +/* -------------------------------------------------------------------- */ +/* Shift the hi-order int over 3 and insert the exponent and sign */ +/* -------------------------------------------------------------------- */ + dt.hi = dt.hi >> 3; + dt.hi = dt.hi & 0x000fffff; + dt.hi = dt.hi | (exponent << 20) | sign; + + + +#ifdef CPL_LSB +/* -------------------------------------------------------------------- */ +/* Change the number to a byte swapped format */ +/* -------------------------------------------------------------------- */ + src = (unsigned char *) &dt; + dest = (unsigned char *) dbl; + + dest[0] = src[4]; + dest[1] = src[5]; + dest[2] = src[6]; + dest[3] = src[7]; + dest[4] = src[0]; + dest[5] = src[1]; + dest[6] = src[2]; + dest[7] = src[3]; +#else + memcpy( dbl, &dt, 8 ); +#endif +} diff --git a/bazaar/plugin/gdal/frmts/ingr/IngrTypes.h b/bazaar/plugin/gdal/frmts/ingr/IngrTypes.h new file mode 100644 index 000000000..70c9ffba8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ingr/IngrTypes.h @@ -0,0 +1,575 @@ +/***************************************************************************** + * $Id: IngrTypes.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: Intergraph Raster Format support + * Purpose: Types, constants and functions definition + * Author: Ivan Lucena, [lucena_ivan at hotmail.com] + * + ****************************************************************************** + * Copyright (c) 2007, Ivan Lucena + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files ( the "Software" ), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#ifndef INGR_TYPES_H_INCLUDED +#define INGR_TYPES_H_INCLUDED + +#include "cpl_conv.h" +#include "cpl_port.h" +#include "gdal.h" +#include "gdal_priv.h" +#include "math.h" + +CPL_C_START +#include "tiffio.h" +CPL_C_END + +// ---------------------------------------------------------------------------- +// Magic number, identification and limits +// ---------------------------------------------------------------------------- + +#define INGR_HEADER_TYPE 9 +#define INGR_HEADER_VERSION 8 +#define INGR_HEADER_2D 0 +#define INGR_HEADER_3D 3 +#define INGR_RSVC_MAX_NAME 32 +#define INGR_JPEGQDEFAULT 30 + +// ---------------------------------------------------------------------------- +// Data type convention +// ---------------------------------------------------------------------------- + +typedef signed char int8; +typedef unsigned char uint8; +typedef signed short int16; +typedef unsigned short uint16; +typedef double real64; +typedef float real32; + +// ---------------------------------------------------------------------------- +// Header Element Type Word ( HTC ) +// ---------------------------------------------------------------------------- + +typedef struct { +#if defined(CPL_LSB) + uint16 Version : 6; // ??????00 00000000 + uint16 Is2Dor3D : 2; // 000000?? 00000000 +#else + uint16 Is2Dor3D : 2; // 000000?? 00000000 + uint16 Version : 6; // ??????00 00000000 +#endif + uint16 Type : 8; // 00000000 ???????? +} INGR_HeaderType; + +// ---------------------------------------------------------------------------- +// Data type dependent Minimum and Maximum type +// ---------------------------------------------------------------------------- + +typedef union +{ + uint8 AsUint8; + uint16 AsUint16; + uint32 AsUint32; + real32 AsReal32; + real64 AsReal64; +} INGR_MinMax; + +// ---------------------------------------------------------------------------- +// Raster Format Types +// ---------------------------------------------------------------------------- + +typedef enum { + PackedBinary = 1, // 1 bit / pixel + ByteInteger = 2, // 8 bits / pixel + WordIntegers = 3, // 16 bits / pixel + Integers32Bit = 4, + FloatingPoint32Bit = 5, + FloatingPoint64Bit = 6, + Complex = 7, // 64 bits / pixel + DoublePrecisionComplex = 8, + RunLengthEncoded = 9, // Bi-level Images + RunLengthEncodedC = 10, // Gray Scale, Color + FigureOfMerit = 11, // FOM + DTMFlags = 12, + RLEVariableValuesWithZS = 13, // Simple + RLEBinaryValues = 14, // w/ Edge Type + RLEVariableValues = 15, // w/ Edge Type + RLEVariableValuesWithZ = 16, // w/ Edge Type + RLEVariableValuesC = 17, // Color Table and Shade + RLEVariableValuesN = 18, // w/ Normals + QuadTreeEncoded = 19, + CCITTGroup4 = 24, // Bi-level Images + RunLengthEncodedRGB = 25, // Full Color + VariableRunLength = 26, + AdaptiveRGB = 27, // Full Color + Uncompressed24bit = 28, // Full Color + AdaptiveGrayScale = 29, + JPEGGRAY = 30, // Gray Scale + JPEGRGB = 31, // Full Color RGB + JPEGCYMK = 32, // CYMK + TiledRasterData = 65, // See tile directory Data Type Code (DTC) + NotUsedReserved = 66, + ContinuousTone = 67, // CYMK + LineArt = 68 // CYMK/RGB +} INGR_Format; + +struct INGR_FormatDescription { + INGR_Format eFormatCode; + const char *pszName; + GDALDataType eDataType; +}; + +#define FORMAT_TAB_COUNT 32 + +// ---------------------------------------------------------------------------- +// Raster Application Types +// ---------------------------------------------------------------------------- + +typedef enum { + GenericRasterImageFile = 0, + DigitalTerrainModeling = 1, + GridDataUtilities = 2, + DrawingScanning = 3, + ImageProcessing = 4, + HiddenSurfaces = 5, + ImagitexScannerProduct = 6, + ScreenCopyPlotting = 7, + IMAGEandMicroStationImager = 8, + ModelView = 9 +} INGR_Application; + +// ---------------------------------------------------------------------------- +// Scan line orientation codes +// ---------------------------------------------------------------------------- + +typedef enum { + UpperLeftVertical = 0, + UpperRightVertical = 1, + LowerLeftVertical = 2, + LowerRightVertical = 3, + UpperLeftHorizontal = 4, + UpperRightHorizontal = 5, + LowerLeftHorizontal = 6, + LowerRightHorizontal = 7 +} INGR_Orientation; + +// ---------------------------------------------------------------------------- +// Scannable flag field codes +// ---------------------------------------------------------------------------- + +typedef enum { + HasLineHeader = 1, + // Every line of raster data has a 4 word + // raster line header at the beginning of + // the line. In the line header, the Words + // to Follow field specifies the amount + // of data following the field, indicating + // the start of the next scanline of raster + // data + NoLineHeader = 0 + // No raster line headers exist. The application + // must calculate where lines of raster data + // start and end. This process is simple for + // non-run length encoded data. It is a fixed + // value; therefore, the line length can be + // calculated from the data type ( DTC ) and + // the number of pixels per line ( PPL ). In + // a run length compression case, the data must + // be decoded to find the end of a raster line. +} INGR_IndexingMethod; + + +// ---------------------------------------------------------------------------- +// Color Table Values ( CTV ) +// ---------------------------------------------------------------------------- + +typedef enum { + NoColorTable = 0, + IGDSColorTable = 1, + EnvironVColorTable = 2 +} INGR_ColorTableType; + +// ---------------------------------------------------------------------------- +// Environ-V Color Tables Entrie +// ---------------------------------------------------------------------------- + +struct vlt_slot +{ + uint16 v_slot; + uint16 v_red; + uint16 v_green; + uint16 v_blue; +}; + +// ---------------------------------------------------------------------------- +// IGDS Color Tables Entrie +// ---------------------------------------------------------------------------- + +struct igds_slot +{ + uint8 v_red; + uint8 v_green; + uint8 v_blue; +}; + +// ---------------------------------------------------------------------------- +// Header Block One data items +// ---------------------------------------------------------------------------- + +typedef struct { + INGR_HeaderType HeaderType; + uint16 WordsToFollow; + uint16 DataTypeCode; + uint16 ApplicationType; + real64 XViewOrigin; + real64 YViewOrigin; + real64 ZViewOrigin; + real64 XViewExtent; + real64 YViewExtent; + real64 ZViewExtent; + real64 TransformationMatrix[16]; + uint32 PixelsPerLine; + uint32 NumberOfLines; + int16 DeviceResolution; + uint8 ScanlineOrientation; + uint8 ScannableFlag; + real64 RotationAngle; + real64 SkewAngle; + uint16 DataTypeModifier; + char DesignFileName[66]; + char DataBaseFileName[66]; + char ParentGridFileName[66]; + char FileDescription[80]; + INGR_MinMax Minimum; + INGR_MinMax Maximum; + char Reserved[3]; + uint8 GridFileVersion; +} INGR_HeaderOne; + +// ---------------------------------------------------------------------------- +// Block two field descriptions +// ---------------------------------------------------------------------------- + +typedef struct { + uint8 Gain; + uint8 OffsetThreshold; + uint8 View1; + uint8 View2; + uint8 ViewNumber; + uint8 Reserved2; + uint16 Reserved3; + real64 AspectRatio; + uint32 CatenatedFilePointer; + uint16 ColorTableType; + uint16 Reserved8; + uint32 NumberOfCTEntries; + uint32 ApplicationPacketPointer; + uint32 ApplicationPacketLength; + uint16 Reserved[110]; +} INGR_HeaderTwoA; + +typedef struct { + uint16 ApplicationData[128]; +} INGR_HeaderTwoB; + +// ---------------------------------------------------------------------------- +// 2nd half of Block two plus another block as a static 256 entries color table +// ---------------------------------------------------------------------------- + +typedef struct { + igds_slot Entry[256]; +} INGR_ColorTable256; + +// ---------------------------------------------------------------------------- +// Extra Block( s ) for dynamic allocated color table with intensit level entries +// ---------------------------------------------------------------------------- + +typedef struct { + vlt_slot *Entry; +} INGR_ColorTableVar; + +// ---------------------------------------------------------------------------- +// Tile Directory Item +// ---------------------------------------------------------------------------- + +typedef struct { + uint32 Start; + uint32 Allocated; + uint32 Used; +} INGR_TileItem; + +// ---------------------------------------------------------------------------- +// Tile Directory Header +// ---------------------------------------------------------------------------- + +typedef struct { + uint16 ApplicationType; + uint16 SubTypeCode; + uint32 WordsToFollow; + uint16 PacketVersion; + uint16 Identifier; + uint16 Reserved[2]; + uint16 Properties; + uint16 DataTypeCode; + uint8 Reserved2[100]; + uint32 TileSize; + uint32 Reserved3; + INGR_TileItem First; +} INGR_TileHeader; + +// ---------------------------------------------------------------------------- +// In Memory Tiff holder +// ---------------------------------------------------------------------------- + +typedef struct { + GDALDataset *poDS; + GDALRasterBand *poBand; + const char *pszFileName; +} INGR_VirtualFile; + +// ---------------------------------------------------------------------------- +// Header Size +// ---------------------------------------------------------------------------- + +/* + Headers Blocks without Color Table + + +-------------+ - 0 - Header Block One + | 512 | + | | + +-------------+ - 512 - Header Block Two ( First Half ) + | 256 | + +-------------+ - 768 - Header Block Two ( Second Half ) + | 256 | ( Application Data ) + +-------------+ - 1024 - Extra Header Info or Image Data + | ... | + + Headers Blocks with IGDS Color Table + + +-------------+ - 0 - Header Block One + | 512 | + | | + +-------------+ - 512 - Header Block Two ( First Half ) + | 256 | + +-------------+ - 768 - IGDS 256 Entries + | 768 | Color Table + | | + | | + +-------------+ - 1536 - Extra Header Info or Image Data + | ... | + + Headers Blocks with EnvironV Color Table + + +-------------+ - 0 - Header Block One + | 512 | + | | + +-------------+ - 512 - Header Block Two + | 512 | + | | + +-------------+ - 1024 - EnvironV Color + : n x 512 : Table + : : + : : + +-------------+ ( n+2 )x512 - Extra Header Info or Image Data + | ... | + +*/ + +#define SIZEOF_HDR1 512 +#define SIZEOF_HDR2_A 256 +#define SIZEOF_HDR2_B 256 +#define SIZEOF_HDR2 512 +#define SIZEOF_CTAB 768 +#define SIZEOF_TDIR 140 +#define SIZEOF_TILE 12 +#define SIZEOF_JPGAD 12 +#define SIZEOF_VLTS 8 +#define SIZEOF_IGDS 3 + +// ---------------------------------------------------------------------------- +// Functions +// ---------------------------------------------------------------------------- + +// ------------------------------------------------------------------ +// Copied the DNG OGR Driver +// ------------------------------------------------------------------ + +void INGR_DGN2IEEEDouble(void * dbl); + +// ------------------------------------------------------------------ +// Compression, Data Format, Data Type related funtions +// ------------------------------------------------------------------ + +uint32 CPL_STDCALL INGR_GetDataBlockSize( const char *pszFileName, + uint32 nBandOffset, + uint32 nDataOffset ); + +uint32 CPL_STDCALL INGR_GetTileDirectory( VSILFILE *fp, + uint32 nOffset, + int nBandXSize, + int nBandYSize, + INGR_TileHeader *pTileDir, + INGR_TileItem **pahTiles); + +INGR_Format CPL_STDCALL INGR_GetFormat( GDALDataType eType, + const char *pszCompression ); + +const char * CPL_STDCALL INGR_GetFormatName( uint16 eCode ); + +GDALDataType CPL_STDCALL INGR_GetDataType( uint16 eCode ); + +const char * CPL_STDCALL INGR_GetOrientation( uint8 nIndex ); + +// ------------------------------------------------------------------ +// Transformation Matrix conversion +// ------------------------------------------------------------------ + +void CPL_STDCALL INGR_GetTransMatrix( INGR_HeaderOne *pHeaderOne, + double *padfGeoTransform ); +void CPL_STDCALL INGR_SetTransMatrix( real64 *padfMatrix, + double *padfGeoTransform ); + +// ------------------------------------------------------------------ +// Color Table conversion +// ------------------------------------------------------------------ + +void CPL_STDCALL INGR_GetIGDSColors( VSILFILE *fp, + uint32 nOffset, + uint32 nEntries, + GDALColorTable *poColorTable ); +uint32 CPL_STDCALL INGR_SetIGDSColors( GDALColorTable *poColorTable, + INGR_ColorTable256 *pColorTableIGDS ); + +void CPL_STDCALL INGR_GetEnvironVColors( VSILFILE *fp, + uint32 nOffset, + uint32 nEntries, + GDALColorTable *poColorTable ); +uint32 CPL_STDCALL INGR_SetEnvironColors( GDALColorTable *poColorTable, + INGR_ColorTableVar *pEnvironTable ); + +// ------------------------------------------------------------------ +// Get, Set Min & Max +// ------------------------------------------------------------------ + +INGR_MinMax CPL_STDCALL INGR_SetMinMax( GDALDataType eType, double dVal ); +double CPL_STDCALL INGR_GetMinMax( GDALDataType eType, INGR_MinMax hVal ); + +// ------------------------------------------------------------------ +// Run Length decoders +// ------------------------------------------------------------------ + +int CPL_STDCALL +INGR_Decode( INGR_Format eFormat, + GByte *pabySrcData, GByte *pabyDstData, + uint32 nSrcBytes, uint32 nBlockSize, + uint32 *pnBytesConsumed ); + +int CPL_STDCALL +INGR_DecodeRunLength( GByte *pabySrcData, GByte *pabyDstData, + uint32 nSrcBytes, uint32 nBlockSize, + uint32 *pnBytesConsumed ); + +int CPL_STDCALL +INGR_DecodeRunLengthBitonal( GByte *pabySrcData, GByte *pabyDstData, + uint32 nSrcBytes, uint32 nBlockSize, + uint32 *pnBytesConsumed ); + +int CPL_STDCALL +INGR_DecodeRunLengthBitonalTiled( GByte *pabySrcData, GByte *pabyDstData, + uint32 nSrcBytes, uint32 nBlockSize, + uint32 *pnBytesConsumed ); + +int CPL_STDCALL +INGR_DecodeRunLengthPaletted( GByte *pabySrcData, GByte *pabyDstData, + uint32 nSrcBytes, uint32 nBlockSize, + uint32 *pnBytesConsumed ); + +// ------------------------------------------------------------------ +// GeoTiff in memory helper +// ------------------------------------------------------------------ + +#include "tifvsi.h" + +INGR_VirtualFile CPL_STDCALL INGR_CreateVirtualFile( const char *pszFilename, + INGR_Format eFormat, + int nXSize, + int nYSize, + int nTileSize, + int nQuality, + GByte *pabyBuffer, + int nBufferSize, + int nBand ); + +void CPL_STDCALL INGR_ReleaseVirtual( INGR_VirtualFile *poTiffMen ); + +int CPL_STDCALL INGR_ReadJpegQuality( VSILFILE *fp, + uint32 nAppDataOfseet, + uint32 nSeekLimit ); + +typedef struct { + uint16 ApplicationType; + uint16 SubTypeCode; + uint32 RemainingLength; + uint16 PacketVersion; + uint16 JpegQuality; +} INGR_JPEGAppData; + +// ------------------------------------------------------------------ +// Reverse Bit order for CCITT data +// ------------------------------------------------------------------ + +#define REVERSEBITS(b) (BitReverseTable[b]) +#define REVERSEBITSBUFFER(bb, bz) \ + int ibb; \ + for( ibb = 0; ibb < bz; ibb++ ) \ + bb[ibb] = REVERSEBITS( bb[ibb] ) + +// ------------------------------------------------------------------ +// Struct reading helpers +// ------------------------------------------------------------------ + +#define BUF2STRC(bb, nn, ff) \ +{ \ + int ss = sizeof(ff); \ + memcpy( &ff, &bb[nn], ss); \ + nn += ss; \ +} + +#define STRC2BUF(bb, nn, ff) \ +{ \ + int ss = sizeof(ff); \ + memcpy( &bb[nn], &ff, ss); \ + nn += ss; \ +} + +// ------------------------------------------------------------------ +// Fix Endianness issues +// ------------------------------------------------------------------ + +void CPL_STDCALL INGR_HeaderOneDiskToMem(INGR_HeaderOne* pHeaderOne, const GByte *pabyBuf); +void CPL_STDCALL INGR_HeaderOneMemToDisk(const INGR_HeaderOne* pHeaderOne, GByte *pabyBuf); +void CPL_STDCALL INGR_HeaderTwoADiskToMem(INGR_HeaderTwoA* pHeaderTwo, const GByte *pabyBuf); +void CPL_STDCALL INGR_HeaderTwoAMemToDisk(const INGR_HeaderTwoA* pHeaderTwo, GByte *pabyBuf); +void CPL_STDCALL INGR_TileHeaderDiskToMem(INGR_TileHeader* pTileHeader, const GByte *pabyBuf); +void CPL_STDCALL INGR_TileItemDiskToMem(INGR_TileItem* pTileItem, const GByte *pabyBuf); +void CPL_STDCALL INGR_JPEGAppDataDiskToMem(INGR_JPEGAppData* pJPEGAppData, const GByte *pabyBuf); + +#endif + diff --git a/bazaar/plugin/gdal/frmts/ingr/IntergraphBand.cpp b/bazaar/plugin/gdal/frmts/ingr/IntergraphBand.cpp new file mode 100644 index 000000000..3cb14229b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ingr/IntergraphBand.cpp @@ -0,0 +1,1269 @@ +/***************************************************************************** + * $Id: IntergraphBand.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: Intergraph Raster Format support + * Purpose: Read/Write Intergraph Raster Format, band support + * Author: Ivan Lucena, [lucena_ivan at hotmail.com] + * + ****************************************************************************** + * Copyright (c) 2007, Ivan Lucena + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files ( the "Software" ), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" +#include "ogr_spatialref.h" +#include "gdal_pam.h" +#include "gdal_alg.h" +#include "math.h" + +#include "IntergraphDataset.h" +#include "IntergraphBand.h" +#include "IngrTypes.h" + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::IntergraphRasterBand() +// ---------------------------------------------------------------------------- + +IntergraphRasterBand::IntergraphRasterBand( IntergraphDataset *poDS, + int nBand, + int nBandOffset, + GDALDataType eType ) +{ + this->poColorTable = new GDALColorTable(); + + this->poDS = poDS; + this->nBand = nBand != 0 ? nBand : poDS->nBands; + this->nTiles = 0; + this->eDataType = eType; + this->pabyBlockBuf = NULL; + this->pahTiles = NULL; + this->nRGBIndex = 0; + this->nBandStart = nBandOffset; + this->bTiled = FALSE; + + // -------------------------------------------------------------------- + // Get Header Info + // -------------------------------------------------------------------- + + memcpy(&hHeaderOne, &poDS->hHeaderOne, sizeof(hHeaderOne)); + memcpy(&hHeaderTwo, &poDS->hHeaderTwo, sizeof(hHeaderTwo)); + + // -------------------------------------------------------------------- + // Get the image start from Words to Follow (WTF) + // -------------------------------------------------------------------- + + nDataOffset = nBandOffset + 2 + ( 2 * ( hHeaderOne.WordsToFollow + 1 ) ); + + // -------------------------------------------------------------------- + // Get Color Tabel from Color Table Type (CTV) + // -------------------------------------------------------------------- + + uint32 nEntries = hHeaderTwo.NumberOfCTEntries; + + if( nEntries > 0 ) + { + switch ( hHeaderTwo.ColorTableType ) + { + case EnvironVColorTable: + INGR_GetEnvironVColors( poDS->fp, nBandOffset, nEntries, poColorTable ); + if (poColorTable->GetColorEntryCount() == 0) + return; + break; + case IGDSColorTable: + INGR_GetIGDSColors( poDS->fp, nBandOffset, nEntries, poColorTable ); + if (poColorTable->GetColorEntryCount() == 0) + return; + break; + default: + CPLDebug( "INGR", "Wrong Color table type (%d), number of colors (%d)", + hHeaderTwo.ColorTableType, nEntries ); + } + } + + // -------------------------------------------------------------------- + // Set Dimension + // -------------------------------------------------------------------- + + nRasterXSize = hHeaderOne.PixelsPerLine; + nRasterYSize = hHeaderOne.NumberOfLines; + + nBlockXSize = nRasterXSize; + nBlockYSize = 1; + + // -------------------------------------------------------------------- + // Get tile directory + // -------------------------------------------------------------------- + + this->eFormat = (INGR_Format) hHeaderOne.DataTypeCode; + + this->bTiled = (hHeaderOne.DataTypeCode == TiledRasterData); + + if( bTiled ) + { + nTiles = INGR_GetTileDirectory( poDS->fp, + nDataOffset, + nRasterXSize, + nRasterYSize, + &hTileDir, + &pahTiles ); + if (nTiles == 0) + return; + + eFormat = (INGR_Format) hTileDir.DataTypeCode; + + // ---------------------------------------------------------------- + // Set blocks dimensions based on tiles + // ---------------------------------------------------------------- + nBlockXSize = hTileDir.TileSize; + nBlockYSize = hTileDir.TileSize; + } + + if (nBlockXSize <= 0 || nBlockYSize <= 0) + { + pabyBlockBuf = NULL; + CPLError(CE_Failure, CPLE_AppDefined, "Invalid block dimensions"); + return; + } + + // -------------------------------------------------------------------- + // Incomplete tiles have Block Offset greater than: + // -------------------------------------------------------------------- + + nFullBlocksX = ( nRasterXSize / nBlockXSize ); + nFullBlocksY = ( nRasterYSize / nBlockYSize ); + + // -------------------------------------------------------------------- + // Get the Data Type from Format + // -------------------------------------------------------------------- + + this->eDataType = INGR_GetDataType( (uint16) eFormat ); + + // -------------------------------------------------------------------- + // Allocate buffer for a Block of data + // -------------------------------------------------------------------- + + nBlockBufSize = nBlockXSize * nBlockYSize * + GDALGetDataTypeSize( eDataType ) / 8; + + if (eFormat == RunLengthEncoded) + { + pabyBlockBuf = (GByte*) VSIMalloc3( nBlockXSize*4+2, nBlockYSize, + GDALGetDataTypeSize( eDataType ) / 8); + } + else + { + pabyBlockBuf = (GByte*) VSIMalloc3( nBlockXSize, nBlockYSize, + GDALGetDataTypeSize( eDataType ) / 8); + } + + if (pabyBlockBuf == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocate %d bytes", nBlockBufSize); + return; + } + + // -------------------------------------------------------------------- + // More Metadata Information + // -------------------------------------------------------------------- + + SetMetadataItem( "FORMAT", INGR_GetFormatName( (uint16) eFormat ), + "IMAGE_STRUCTURE" ); + + if( bTiled ) + { + SetMetadataItem( "TILESSIZE", CPLSPrintf ("%d", hTileDir.TileSize), + "IMAGE_STRUCTURE" ); + } + else + { + SetMetadataItem( "TILED", "NO", "IMAGE_STRUCTURE" ); + } + + SetMetadataItem( "ORIENTATION", + INGR_GetOrientation( hHeaderOne.ScanlineOrientation ), + "IMAGE_STRUCTURE" ); + + if( eFormat == PackedBinary || + eFormat == RunLengthEncoded || + eFormat == CCITTGroup4 ) + { + SetMetadataItem( "NBITS", "1", "IMAGE_STRUCTURE" ); + } + + this->nRLEOffset = 0; +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::~IntergraphRasterBand() +// ---------------------------------------------------------------------------- + +IntergraphRasterBand::~IntergraphRasterBand() +{ + if( pabyBlockBuf ) + { + CPLFree( pabyBlockBuf ); + } + + if( pahTiles ) + { + CPLFree( pahTiles ); + } + + if( poColorTable ) + { + delete poColorTable; + } +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::GetMinimum() +// ---------------------------------------------------------------------------- + +double IntergraphRasterBand::GetMinimum( int *pbSuccess ) +{ + + double dMinimum = INGR_GetMinMax( eDataType, hHeaderOne.Minimum ); + double dMaximum = INGR_GetMinMax( eDataType, hHeaderOne.Maximum ); + + if( pbSuccess ) + { + *pbSuccess = dMinimum == dMaximum ? FALSE : TRUE; + } + + return dMinimum; +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::GetMaximum() +// ---------------------------------------------------------------------------- + +double IntergraphRasterBand::GetMaximum( int *pbSuccess ) +{ + double dMinimum = INGR_GetMinMax( eDataType, hHeaderOne.Minimum ); + double dMaximum = INGR_GetMinMax( eDataType, hHeaderOne.Maximum ); + + if( pbSuccess ) + { + *pbSuccess = dMinimum == dMaximum ? FALSE : TRUE; + } + + return dMaximum; +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::GetColorInterpretation() +// ---------------------------------------------------------------------------- + +GDALColorInterp IntergraphRasterBand::GetColorInterpretation() +{ + if( eFormat == AdaptiveRGB || + eFormat == Uncompressed24bit || + eFormat == ContinuousTone ) + { + switch( nRGBIndex ) + { + case 1: + return GCI_RedBand; + case 2: + return GCI_GreenBand; + case 3: + return GCI_BlueBand; + } + return GCI_GrayIndex; + } + else + { + if( poColorTable->GetColorEntryCount() > 0 ) + { + return GCI_PaletteIndex; + } + else + { + return GCI_GrayIndex; + } + } + +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::GetColorTable() +// ---------------------------------------------------------------------------- + +GDALColorTable *IntergraphRasterBand::GetColorTable() +{ + if( poColorTable->GetColorEntryCount() == 0 ) + { + return NULL; + } + else + { + return poColorTable; + } +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::SetColorTable() +// ---------------------------------------------------------------------------- + +CPLErr IntergraphRasterBand::SetColorTable( GDALColorTable *poColorTable ) +{ + if( poColorTable == NULL ) + { + return CE_None; + } + + delete this->poColorTable; + this->poColorTable = poColorTable->Clone(); + + return CE_None; +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::SetStatistics() +// ---------------------------------------------------------------------------- + +CPLErr IntergraphRasterBand::SetStatistics( double dfMin, + double dfMax, + double dfMean, + double dfStdDev ) +{ + hHeaderOne.Minimum = INGR_SetMinMax( eDataType, dfMin ); + hHeaderOne.Maximum = INGR_SetMinMax( eDataType, dfMax ); + + return GDALRasterBand::SetStatistics( dfMin, dfMax, dfMean, dfStdDev ); +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::IReadBlock() +// ---------------------------------------------------------------------------- + +CPLErr IntergraphRasterBand::IReadBlock( int nBlockXOff, + int nBlockYOff, + void *pImage ) +{ + // -------------------------------------------------------------------- + // Load Block Buffer + // -------------------------------------------------------------------- + if (HandleUninstantiatedTile( nBlockXOff, nBlockYOff, pImage )) + return CE_None; + + uint32 nBytesRead = LoadBlockBuf( nBlockXOff, nBlockYOff, nBlockBufSize, pabyBlockBuf ); + + if( nBytesRead == 0 ) + { + memset( pImage, 0, nBlockXSize * nBlockYSize * + GDALGetDataTypeSize( eDataType ) / 8 ); + CPLError( CE_Failure, CPLE_FileIO, + "Can't read (%s) tile with X offset %d and Y offset %d.\n", + ((IntergraphDataset*)poDS)->pszFilename, nBlockXOff, nBlockYOff ); + return CE_Failure; + } + + // -------------------------------------------------------------------- + // Reshape blocks if needed + // -------------------------------------------------------------------- + + if( nBlockXOff == nFullBlocksX || + nBlockYOff == nFullBlocksY ) + { + ReshapeBlock( nBlockXOff, nBlockYOff, nBlockBufSize, pabyBlockBuf ); + } + + // -------------------------------------------------------------------- + // Copy block buffer to image + // -------------------------------------------------------------------- + + memcpy( pImage, pabyBlockBuf, nBlockXSize * nBlockYSize * + GDALGetDataTypeSize( eDataType ) / 8 ); + +#ifdef CPL_MSB + if( eDataType == GDT_Int16 || eDataType == GDT_UInt16) + GDALSwapWords( pImage, 2, nBlockXSize * nBlockYSize, 2 ); + else if( eDataType == GDT_Int32 || eDataType == GDT_UInt32 || eDataType == GDT_Float32 ) + GDALSwapWords( pImage, 4, nBlockXSize * nBlockYSize, 4 ); + else if (eDataType == GDT_Float64 ) + GDALSwapWords( pImage, 8, nBlockXSize * nBlockYSize, 8 ); +#endif + + return CE_None; +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::HandleUninstantiatedTile() +// ---------------------------------------------------------------------------- + +int IntergraphRasterBand::HandleUninstantiatedTile(int nBlockXOff, + int nBlockYOff, + void* pImage) +{ + if( bTiled && pahTiles[nBlockXOff + nBlockYOff * nBlocksPerRow].Start == 0 ) + { + // ------------------------------------------------------------ + // Uninstantieted tile, unique value + // ------------------------------------------------------------ + int nColor = pahTiles[nBlockXOff + nBlockYOff * nBlocksPerRow].Used; + switch( GetColorInterpretation() ) + { + case GCI_RedBand: + nColor >>= 16; break; + case GCI_GreenBand: + nColor >>= 8; break; + default: + break; + } + memset( pImage, nColor, nBlockXSize * nBlockYSize * + GDALGetDataTypeSize( eDataType ) / 8 ); + return TRUE; + } + else + return FALSE; +} + +// ---------------------------------------------------------------------------- +// IntergraphRGBBand::IntergraphRGBBand() +// ---------------------------------------------------------------------------- + +IntergraphRGBBand::IntergraphRGBBand( IntergraphDataset *poDS, + int nBand, + int nBandOffset, + int nRGorB ) + : IntergraphRasterBand( poDS, nBand, nBandOffset ) +{ + if (pabyBlockBuf == NULL) + return; + + nRGBIndex = (uint8) nRGorB; + + // -------------------------------------------------------------------- + // Reallocate buffer for a block of RGB Data + // -------------------------------------------------------------------- + + nBlockBufSize *= 3; + CPLFree( pabyBlockBuf ); + pabyBlockBuf = (GByte*) VSIMalloc( nBlockBufSize ); + if (pabyBlockBuf == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocate %d bytes", nBlockBufSize); + } +} + +// ---------------------------------------------------------------------------- +// IntergraphRGBBand::IReadBlock() +// ---------------------------------------------------------------------------- + +CPLErr IntergraphRGBBand::IReadBlock( int nBlockXOff, + int nBlockYOff, + void *pImage ) +{ + if( IntergraphRasterBand::IReadBlock( nBlockXOff, + nBlockYOff, + pImage ) != CE_None ) + { + return CE_Failure; + } + + // -------------------------------------------------------------------- + // Extract the band of interest from the block buffer + // -------------------------------------------------------------------- + + int i, j; + + for ( i = 0, j = ( nRGBIndex - 1 ); + i < ( nBlockXSize * nBlockYSize ); + i++, j += 3 ) + { + ( (GByte*) pImage )[i] = pabyBlockBuf[j]; + } + + return CE_None; +} + +// ---------------------------------------------------------------------------- +// IntergraphRLEBand::IntergraphRLEBand() +// ---------------------------------------------------------------------------- + +IntergraphRLEBand::IntergraphRLEBand( IntergraphDataset *poDS, + int nBand, + int nBandOffset, + int nRGorB ) + : IntergraphRasterBand( poDS, nBand, nBandOffset ) +{ + nRLESize = 0; + nRGBIndex = (uint8) nRGorB; + bRLEBlockLoaded = FALSE; + pabyRLEBlock = NULL; + panRLELineOffset = NULL; + + if (pabyBlockBuf == NULL) + return; + + if( ! this->bTiled ) + { + // ------------------------------------------------------------ + // Load all rows at once + // ------------------------------------------------------------ + + nFullBlocksX = 1; + + if( eFormat == RunLengthEncodedC || eFormat == RunLengthEncoded ) + { + nBlockYSize = 1; + panRLELineOffset = (uint32 *) + CPLCalloc(sizeof(uint32),nRasterYSize); + nFullBlocksY = nRasterYSize; + } + else + { + nBlockYSize = nRasterYSize; + nFullBlocksY = 1; + } + + nRLESize = INGR_GetDataBlockSize( poDS->pszFilename, + hHeaderTwo.CatenatedFilePointer, + nDataOffset); + + nBlockBufSize = nBlockXSize * nBlockYSize; + } + else + { + // ------------------------------------------------------------ + // Find the biggest tile + // ------------------------------------------------------------ + + uint32 iTiles; + for( iTiles = 0; iTiles < nTiles; iTiles++) + { + nRLESize = MAX( pahTiles[iTiles].Used, nRLESize ); + } + } + + // ---------------------------------------------------------------- + // Realocate the decompressed Buffer + // ---------------------------------------------------------------- + + if( eFormat == AdaptiveRGB || + eFormat == ContinuousTone ) + { + nBlockBufSize *= 3; + } + + CPLFree( pabyBlockBuf ); + pabyBlockBuf = (GByte*) VSIMalloc( nBlockBufSize ); + if (pabyBlockBuf == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocate %d bytes", nBlockBufSize); + } + + // ---------------------------------------------------------------- + // Create a RLE buffer + // ---------------------------------------------------------------- + + pabyRLEBlock = (GByte*) VSIMalloc( nRLESize ); + if (pabyRLEBlock == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocate %d bytes", nRLESize); + } + + // ---------------------------------------------------------------- + // Set a black and white Color Table + // ---------------------------------------------------------------- + + if( eFormat == RunLengthEncoded ) + { + BlackWhiteCT( true ); + } + +} + +// ---------------------------------------------------------------------------- +// IntergraphRLEBand::IntergraphRLEBand() +// ---------------------------------------------------------------------------- + +IntergraphRLEBand::~IntergraphRLEBand() +{ + CPLFree( pabyRLEBlock ); + CPLFree( panRLELineOffset ); +} + +// ---------------------------------------------------------------------------- +// IntergraphRLEBand::IReadBlock() +// ---------------------------------------------------------------------------- + +CPLErr IntergraphRLEBand::IReadBlock( int nBlockXOff, + int nBlockYOff, + void *pImage ) +{ + // -------------------------------------------------------------------- + // Load Block Buffer + // -------------------------------------------------------------------- + + uint32 nBytesRead; + + if( bTiled || !bRLEBlockLoaded ) + { + if (HandleUninstantiatedTile( nBlockXOff, nBlockYOff, pImage )) + return CE_None; + + if (!bTiled) + { + // With RLE, we want to load all of the data. + // So load (0,0) since that's the only offset that will load everything. + nBytesRead = LoadBlockBuf( 0, 0, nRLESize, pabyRLEBlock ); + } + else + { + nBytesRead = LoadBlockBuf( nBlockXOff, nBlockYOff, nRLESize, pabyRLEBlock ); + } + bRLEBlockLoaded = TRUE; + } + else + nBytesRead = nRLESize; + + if( nBytesRead == 0 ) + { + memset( pImage, 0, nBlockXSize * nBlockYSize * + GDALGetDataTypeSize( eDataType ) / 8 ); + CPLError( CE_Failure, CPLE_FileIO, + "Can't read (%s) tile with X offset %d and Y offset %d.\n%s", + ((IntergraphDataset*)poDS)->pszFilename, nBlockXOff, nBlockYOff, + VSIStrerror( errno ) ); + return CE_Failure; + } + + // ---------------------------------------------------------------- + // Calculate the resulting image dimmention + // ---------------------------------------------------------------- + + int nVirtualXSize = nBlockXSize; + int nVirtualYSize = nBlockYSize; + + if( nBlockXOff == nFullBlocksX ) + { + nVirtualXSize = nRasterXSize % nBlockXSize; + } + + if( nBlockYOff == nFullBlocksY ) + { + nVirtualYSize = nRasterYSize % nBlockYSize; + } + + // -------------------------------------------------------------------- + // Decode Run Length + // -------------------------------------------------------------------- + + if( bTiled && eFormat == RunLengthEncoded ) + { + nBytesRead = + INGR_DecodeRunLengthBitonalTiled( pabyRLEBlock, pabyBlockBuf, + nRLESize, nBlockBufSize, NULL ); + } + + else if( bTiled || panRLELineOffset == NULL ) + { + nBytesRead = INGR_Decode( eFormat, pabyRLEBlock, pabyBlockBuf, + nRLESize, nBlockBufSize, + NULL ); + } + + else + { + uint32 nBytesConsumed; + + // If we are missing the offset to this line, process all + // preceding lines that are not initialized. + if( nBlockYOff > 0 && panRLELineOffset[nBlockYOff] == 0 ) + { + int iLine = nBlockYOff - 1; + // Find the last line that is initialized (or line 0). + while ((iLine != 0) && (panRLELineOffset[iLine] == 0)) + iLine--; + for( ; iLine < nBlockYOff; iLine++ ) + { + // Pass NULL as destination so that no decompression + // actually takes place. + INGR_Decode( eFormat, + pabyRLEBlock + panRLELineOffset[iLine], + NULL, nRLESize - panRLELineOffset[iLine], nBlockBufSize, + &nBytesConsumed ); + + if( iLine < nRasterYSize-1 ) + panRLELineOffset[iLine+1] = + panRLELineOffset[iLine] + nBytesConsumed; + } + } + + // Read the requested line. + nBytesRead = + INGR_Decode( eFormat, + pabyRLEBlock + panRLELineOffset[nBlockYOff], + pabyBlockBuf, nRLESize - panRLELineOffset[nBlockYOff], nBlockBufSize, + &nBytesConsumed ); + + if( nBlockYOff < nRasterYSize-1 ) + panRLELineOffset[nBlockYOff+1] = + panRLELineOffset[nBlockYOff] + nBytesConsumed; + } + + // -------------------------------------------------------------------- + // Reshape blocks if needed + // -------------------------------------------------------------------- + + if( nBlockXOff == nFullBlocksX || + nBlockYOff == nFullBlocksY ) + { + ReshapeBlock( nBlockXOff, nBlockYOff, nBlockBufSize, pabyBlockBuf ); + } + + // -------------------------------------------------------------------- + // Extract the band of interest from the block buffer (BIL) + // -------------------------------------------------------------------- + + if( eFormat == AdaptiveRGB || + eFormat == ContinuousTone ) + { + int i, j; + GByte *pabyImage = (GByte*) pImage; + j = ( nRGBIndex - 1 ) * nVirtualXSize; + for ( i = 0; i < nVirtualYSize; i++ ) + { + memcpy( &pabyImage[i * nBlockXSize], &pabyBlockBuf[j], nBlockXSize ); + j += ( 3 * nBlockXSize ); + } + } + else + { + memcpy( pImage, pabyBlockBuf, nBlockBufSize ); + } + + return CE_None; +} + +// ---------------------------------------------------------------------------- +// IntergraphBitmapBand::IntergraphBitmapBand() +// ---------------------------------------------------------------------------- + +IntergraphBitmapBand::IntergraphBitmapBand( IntergraphDataset *poDS, + int nBand, + int nBandOffset, + int nRGorB ) + : IntergraphRasterBand( poDS, nBand, nBandOffset, GDT_Byte ) +{ + nBMPSize = 0; + nRGBBand = nRGorB; + pabyBMPBlock = NULL; + + if (pabyBlockBuf == NULL) + return; + + + if( ! this->bTiled ) + { + // ------------------------------------------------------------ + // Load all rows at once + // ------------------------------------------------------------ + + nBlockYSize = nRasterYSize; + nBMPSize = INGR_GetDataBlockSize( poDS->pszFilename, + hHeaderTwo.CatenatedFilePointer, + nDataOffset); + } + else + { + // ------------------------------------------------------------ + // Find the biggest tile + // ------------------------------------------------------------ + + uint32 iTiles; + for( iTiles = 0; iTiles < nTiles; iTiles++) + { + nBMPSize = MAX( pahTiles[iTiles].Used, nBMPSize ); + } + } + + // ---------------------------------------------------------------- + // Create a Bitmap buffer + // ---------------------------------------------------------------- + + pabyBMPBlock = (GByte*) VSIMalloc( nBMPSize ); + if (pabyBMPBlock == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocate %d bytes", nBMPSize); + } + + // ---------------------------------------------------------------- + // Set a black and white Color Table + // ---------------------------------------------------------------- + + if( eFormat == CCITTGroup4 ) + { + BlackWhiteCT( true ); + } + + // ---------------------------------------------------------------- + // Read JPEG Quality from Application Data + // ---------------------------------------------------------------- + + if( eFormat == JPEGGRAY || + eFormat == JPEGRGB || + eFormat == JPEGCYMK ) + { + nQuality = INGR_ReadJpegQuality( poDS->fp, + hHeaderTwo.ApplicationPacketPointer, + nDataOffset ); + } +} + +// ---------------------------------------------------------------------------- +// IntergraphBitmapBand::~IntergraphBitmapBand() +// ---------------------------------------------------------------------------- + +IntergraphBitmapBand::~IntergraphBitmapBand() +{ + CPLFree( pabyBMPBlock ); +} + +// ---------------------------------------------------------------------------- +// IntergraphBitmapBand::GetColorInterpretation() +// ---------------------------------------------------------------------------- + +GDALColorInterp IntergraphBitmapBand::GetColorInterpretation() +{ + if( eFormat == JPEGRGB) + { + switch( nRGBBand ) + { + case 1: + return GCI_RedBand; + case 2: + return GCI_GreenBand; + case 3: + return GCI_BlueBand; + } + return GCI_GrayIndex; + } + else + { + if( poColorTable->GetColorEntryCount() > 0 ) + { + return GCI_PaletteIndex; + } + else + { + return GCI_GrayIndex; + } + } + +} + +// ---------------------------------------------------------------------------- +// IntergraphBitmapBand::IReadBlock() +// ---------------------------------------------------------------------------- + +CPLErr IntergraphBitmapBand::IReadBlock( int nBlockXOff, + int nBlockYOff, + void *pImage ) +{ + IntergraphDataset *poGDS = ( IntergraphDataset * ) poDS; + + // ---------------------------------------------------------------- + // Load the block of a tile or a whole image + // ---------------------------------------------------------------- + if (HandleUninstantiatedTile( nBlockXOff, nBlockYOff, pImage )) + return CE_None; + + uint32 nBytesRead = LoadBlockBuf( nBlockXOff, nBlockYOff, nBMPSize, pabyBMPBlock ); + + if( nBytesRead == 0 ) + { + memset( pImage, 0, nBlockXSize * nBlockYSize * + GDALGetDataTypeSize( eDataType ) / 8 ); + CPLError( CE_Failure, CPLE_FileIO, + "Can't read (%s) tile with X offset %d and Y offset %d.\n%s", + ((IntergraphDataset*)poDS)->pszFilename, nBlockXOff, nBlockYOff, + VSIStrerror( errno ) ); + return CE_Failure; + } + + // ---------------------------------------------------------------- + // Calculate the resulting image dimmention + // ---------------------------------------------------------------- + + int nVirtualXSize = nBlockXSize; + int nVirtualYSize = nBlockYSize; + + if( nBlockXOff == nFullBlocksX ) + { + nVirtualXSize = nRasterXSize % nBlockXSize; + } + + if( nBlockYOff == nFullBlocksY ) + { + nVirtualYSize = nRasterYSize % nBlockYSize; + } + + // ---------------------------------------------------------------- + // Create an in memory small tiff file (~400K) + // ---------------------------------------------------------------- + + poGDS->hVirtual = INGR_CreateVirtualFile( poGDS->pszFilename, + eFormat, + nVirtualXSize, + nVirtualYSize, + hTileDir.TileSize, + nQuality, + pabyBMPBlock, + nBytesRead, + nRGBBand ); + + if( poGDS->hVirtual.poDS == NULL ) + { + memset( pImage, 0, nBlockXSize * nBlockYSize * + GDALGetDataTypeSize( eDataType ) / 8 ); + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open virtual file.\n" + "Is the GTIFF and JPEG driver available?" ); + return CE_Failure; + } + + // ---------------------------------------------------------------- + // Read the unique block from the in memory file and release it + // ---------------------------------------------------------------- + + poGDS->hVirtual.poBand->RasterIO( GF_Read, 0, 0, + nVirtualXSize, nVirtualYSize, pImage, + nVirtualXSize, nVirtualYSize, GDT_Byte, 0, 0, NULL ); + + // -------------------------------------------------------------------- + // Reshape blocks if needed + // -------------------------------------------------------------------- + + if( nBlockXOff == nFullBlocksX || + nBlockYOff == nFullBlocksY ) + { + ReshapeBlock( nBlockXOff, nBlockYOff, nBlockBufSize, (GByte*) pImage ); + } + + INGR_ReleaseVirtual( &poGDS->hVirtual ); + + return CE_None; +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::LoadBlockBuf() +// ---------------------------------------------------------------------------- + +int IntergraphRasterBand::LoadBlockBuf( int nBlockXOff, + int nBlockYOff, + int nBlobkBytes, + GByte *pabyBlock ) +{ + IntergraphDataset *poGDS = ( IntergraphDataset * ) poDS; + + uint32 nSeekOffset = 0; + uint32 nReadSize = 0; + uint32 nBlockId = 0; + + // -------------------------------------------------------------------- + // Read from tiles or read from strip + // -------------------------------------------------------------------- + + if( bTiled ) + { + nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow; + + if( pahTiles[nBlockId].Start == 0 ) + { + return 0; + } + + nSeekOffset = pahTiles[nBlockId].Start + nDataOffset; + nReadSize = pahTiles[nBlockId].Used; + + if( (int) nReadSize > nBlobkBytes ) + { + CPLDebug( "INGR", + "LoadBlockBuf(%d,%d) - trimmed tile size from %d to %d.", + nBlockXOff, nBlockYOff, + (int) nReadSize, (int) nBlobkBytes ); + nReadSize = nBlobkBytes; + } + } + else + { + nSeekOffset = nDataOffset + ( nBlockBufSize * nBlockYOff ); + nReadSize = nBlobkBytes; + } + + if( VSIFSeekL( poGDS->fp, nSeekOffset, SEEK_SET ) < 0 ) + { + return 0; + } + + return VSIFReadL( pabyBlock, 1, nReadSize, poGDS->fp ); +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::ReshapeBlock() +// ---------------------------------------------------------------------------- + +/** + * Complete Tile with zeroes to fill up a Block + * + * ### ##000 ###### ###00 + * ### => ##000 , 000000 or ###00 + * ##000 000000 00000 + ***/ + +void IntergraphRasterBand::ReshapeBlock( int nBlockXOff, + int nBlockYOff, + int nBlockBytes, + GByte *pabyBlock ) +{ + GByte *pabyTile = (GByte*) CPLCalloc( 1, nBlockBufSize ); + + memcpy( pabyTile, pabyBlock, nBlockBytes ); + memset( pabyBlock, 0, nBlockBytes ); + + int nColSize = nBlockXSize; + int nRowSize = nBlockYSize; + int nCellBytes = GDALGetDataTypeSize( eDataType ) / 8; + + if( nBlockXOff + 1 == nBlocksPerRow ) + { + nColSize = nRasterXSize % nBlockXSize; + } + + if( nBlockYOff + 1 == nBlocksPerColumn ) + { + nRowSize = nRasterYSize % nBlockYSize; + } + + if( nRGBIndex > 0 ) + { + nCellBytes = nCellBytes * 3; + } + + for( int iRow = 0; iRow < nRowSize; iRow++ ) + { + memcpy( pabyBlock + ( iRow * nCellBytes * nBlockXSize ), + pabyTile + ( iRow * nCellBytes * nColSize ), + nCellBytes * nColSize); + } + + CPLFree( pabyTile ); +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::IWriteBlock() +// ---------------------------------------------------------------------------- + +CPLErr IntergraphRasterBand::IWriteBlock( int nBlockXOff, + int nBlockYOff, + void *pImage ) +{ + uint32 nBlockSize = nBlockBufSize; + uint32 nBlockOffset = nBlockBufSize * nBlockYOff; + + IntergraphDataset *poGDS = ( IntergraphDataset * ) poDS; + + if( ( nBlockXOff == 0 ) && ( nBlockYOff == 0 ) ) + { + FlushBandHeader(); + } + + if( nRGBIndex > 0 ) + { + if( nBand > 1 ) + { + VSIFSeekL( poGDS->fp, nDataOffset + ( nBlockBufSize * nBlockYOff ), SEEK_SET ); + VSIFReadL( pabyBlockBuf, 1, nBlockBufSize, poGDS->fp ); + } + int i, j; + for( i = 0, j = ( 3 - nRGBIndex ); i < nBlockXSize; i++, j += 3 ) + { + pabyBlockBuf[j] = ( ( GByte * ) pImage )[i]; + } + } + else if (eFormat == RunLengthEncoded) + { + // Series of [OFF, ON,] OFF spans. + int nLastCount = 0; + GByte *pInput = ( GByte * ) pImage; + GInt16 *pOutput = ( GInt16 * ) pabyBlockBuf; + + nBlockOffset = this->nRLEOffset * 2; + int nRLECount = 0; + GByte nValue = 0; // Start with OFF spans. + + for(uint32 i=0; i32767) + { + pOutput[nRLECount++] = CPL_LSBWORD16(32767); + pOutput[nRLECount++] = CPL_LSBWORD16(0); + nLastCount -= 32767; + } + pOutput[nRLECount++] = CPL_LSBWORD16(nLastCount); + nLastCount = 1; + nValue ^= 1; + } + } + + // Output tail end of scanline + while(nLastCount>32767) + { + pOutput[nRLECount++] = CPL_LSBWORD16(32767); + pOutput[nRLECount++] = CPL_LSBWORD16(0); + nLastCount -= 32767; + } + + if (nLastCount != 0) + { + pOutput[nRLECount++] = CPL_LSBWORD16(nLastCount); + nLastCount = 0; + nValue ^= 1; + } + + if (nValue == 0) + pOutput[nRLECount++] = CPL_LSBWORD16(0); + + this->nRLEOffset += nRLECount; + nBlockSize = nRLECount * 2; + } + else + { + memcpy( pabyBlockBuf, pImage, nBlockBufSize ); +#ifdef CPL_MSB + if( eDataType == GDT_Int16 || eDataType == GDT_UInt16) + GDALSwapWords( pabyBlockBuf, 2, nBlockXSize * nBlockYSize, 2 ); + else if( eDataType == GDT_Int32 || eDataType == GDT_UInt32 || eDataType == GDT_Float32 ) + GDALSwapWords( pabyBlockBuf, 4, nBlockXSize * nBlockYSize, 4 ); + else if (eDataType == GDT_Float64 ) + GDALSwapWords( pabyBlockBuf, 8, nBlockXSize * nBlockYSize, 8 ); +#endif + } + + VSIFSeekL( poGDS->fp, nDataOffset + nBlockOffset, SEEK_SET ); + + if( ( uint32 ) VSIFWriteL( pabyBlockBuf, 1, nBlockSize, poGDS->fp ) < nBlockSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't write (%s) block with X offset %d and Y offset %d.\n%s", + poGDS->pszFilename, nBlockXOff, nBlockYOff, VSIStrerror( errno ) ); + return CE_Failure; + } + + return CE_None; +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::FlushBandHeader() +// ---------------------------------------------------------------------------- + +void IntergraphRasterBand::FlushBandHeader( void ) +{ + if( nRGBIndex > 1 ) + { + return; + } + + IntergraphDataset *poGDS = ( IntergraphDataset* ) poDS; + + INGR_ColorTable256 hCTab; + + if( poColorTable->GetColorEntryCount() > 0 ) + { + hHeaderTwo.ColorTableType = IGDSColorTable; + hHeaderTwo.NumberOfCTEntries = poColorTable->GetColorEntryCount(); + INGR_SetIGDSColors( poColorTable, &hCTab ); + } + + if( nBand > poDS->GetRasterCount() ) + { + hHeaderTwo.CatenatedFilePointer = nBand * + ( ( 3 * SIZEOF_HDR1 ) + ( nBlockBufSize * nRasterYSize ) ); + } + + VSIFSeekL( poGDS->fp, nBandStart, SEEK_SET ); + + GByte abyBuf[MAX(SIZEOF_HDR1,SIZEOF_CTAB)]; + + INGR_HeaderOneMemToDisk( &hHeaderOne, abyBuf ); + + VSIFWriteL( abyBuf, 1, SIZEOF_HDR1, poGDS->fp ); + + INGR_HeaderTwoAMemToDisk( &hHeaderTwo, abyBuf ); + + VSIFWriteL( abyBuf, 1, SIZEOF_HDR2_A, poGDS->fp ); + + unsigned int i = 0; + unsigned int n = 0; + + for( i = 0; i < 256; i++ ) + { + STRC2BUF( abyBuf, n, hCTab.Entry[i].v_red ); + STRC2BUF( abyBuf, n, hCTab.Entry[i].v_green ); + STRC2BUF( abyBuf, n, hCTab.Entry[i].v_blue ); + } + + VSIFWriteL( abyBuf, 1, SIZEOF_CTAB, poGDS->fp ); +} + +// ---------------------------------------------------------------------------- +// IntergraphRasterBand::BlackWhiteCT() +// ---------------------------------------------------------------------------- + +void IntergraphRasterBand::BlackWhiteCT( bool bReverse ) +{ + GDALColorEntry oBlack; + GDALColorEntry oWhite; + + oWhite.c1 = (short) 255; + oWhite.c2 = (short) 255; + oWhite.c3 = (short) 255; + oWhite.c4 = (short) 255; + + oBlack.c1 = (short) 0; + oBlack.c2 = (short) 0; + oBlack.c3 = (short) 0; + oBlack.c4 = (short) 255; + + if( bReverse ) + { + poColorTable->SetColorEntry( 0, &oWhite ); + poColorTable->SetColorEntry( 1, &oBlack ); + } + else + { + poColorTable->SetColorEntry( 0, &oBlack ); + poColorTable->SetColorEntry( 1, &oWhite ); + } +} diff --git a/bazaar/plugin/gdal/frmts/ingr/IntergraphBand.h b/bazaar/plugin/gdal/frmts/ingr/IntergraphBand.h new file mode 100644 index 000000000..9439aaf1b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ingr/IntergraphBand.h @@ -0,0 +1,150 @@ +/***************************************************************************** +* $Id: IntergraphBand.h 27044 2014-03-16 23:41:27Z rouault $ +* +* Project: Intergraph Raster Format support +* Purpose: Read selected types of Intergraph Raster Format +* Author: Ivan Lucena, [lucena_ivan at hotmail.com] +* +****************************************************************************** +* Copyright (c) 2007, Ivan Lucena + * Copyright (c) 2007-2013, Even Rouault +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files ( the "Software" ), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*****************************************************************************/ + +#include "IngrTypes.h" + +// ---------------------------------------------------------------------------- +// Intergraph IntergraphRasterBand +// ---------------------------------------------------------------------------- + +class IntergraphRasterBand : public GDALPamRasterBand +{ + friend class IntergraphDataset; + +protected: + GDALColorTable *poColorTable; + uint32 nDataOffset; + uint32 nBlockBufSize; + uint32 nBandStart; + uint8 nRGBIndex; + + INGR_Format eFormat; + bool bTiled; + int nFullBlocksX; + int nFullBlocksY; + + GByte *pabyBlockBuf; + uint32 nTiles; + + INGR_TileItem *pahTiles; + + INGR_HeaderOne hHeaderOne; + INGR_HeaderTwoA hHeaderTwo; + INGR_TileHeader hTileDir; + + int nRLEOffset; + +public: + IntergraphRasterBand( IntergraphDataset *poDS, + int nBand, + int nBandOffset, + GDALDataType eType = GDT_Unknown); + ~IntergraphRasterBand(); + + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum( int *pbSuccess = NULL ); + virtual GDALColorTable *GetColorTable(); + virtual GDALColorInterp GetColorInterpretation(); + virtual CPLErr IReadBlock( int nBlockXOff, int nBlockYOff, void *pImage ); + virtual CPLErr IWriteBlock( int nBlockXOff, int nBlockYOff, void *pImage ); + virtual CPLErr SetColorTable( GDALColorTable *poColorTable ); + virtual CPLErr SetStatistics( double dfMin, double dfMax, double dfMean, double dfStdDev ); + +protected: + int HandleUninstantiatedTile( int nBlockXOff, int nBlockYOff, void* pImage); + int LoadBlockBuf( int nBlockXOff, int nBlockYOff, int nBlockBytes, GByte *pabyBlock ); + void ReshapeBlock( int nBlockXOff, int nBlockYOff, int nBlockBytes, GByte *pabyBlock ); + void FlushBandHeader( void ); + void BlackWhiteCT( bool bReverse = false ); +}; + +// ---------------------------------------------------------------------------- +// Intergraph IntergraphRGBBand +// ---------------------------------------------------------------------------- + +class IntergraphRGBBand : public IntergraphRasterBand +{ +public: + IntergraphRGBBand( IntergraphDataset *poDS, + int nBand, + int nBandOffset, + int nRGorB ); + + virtual CPLErr IReadBlock( int nBlockXOff, int nBlockYOff, void *pImage ); +}; + +// ---------------------------------------------------------------------------- +// Intergraph IntergraphBitmapBand +// ---------------------------------------------------------------------------- + +class IntergraphBitmapBand : public IntergraphRasterBand +{ + friend class IntergraphDataset; + +private: + GByte *pabyBMPBlock; + uint32 nBMPSize; + int nQuality; + int nRGBBand; + +public: + IntergraphBitmapBand( IntergraphDataset *poDS, + int nBand, + int nBandOffset, + int nRGorB = 1 ); + ~IntergraphBitmapBand(); + + virtual CPLErr IReadBlock( int nBlockXOff, int nBlockYOff, void *pImage ); + virtual GDALColorInterp GetColorInterpretation(); +}; + +// ---------------------------------------------------------------------------- +// Intergraph IntergraphRLEBand +// ---------------------------------------------------------------------------- + +class IntergraphRLEBand : public IntergraphRasterBand +{ + friend class IntergraphDataset; + +private: + GByte *pabyRLEBlock; + uint32 nRLESize; + int bRLEBlockLoaded; + uint32 *panRLELineOffset; + +public: + IntergraphRLEBand( IntergraphDataset *poDS, + int nBand, + int nBandOffset, + int nRGorB = 0); + ~IntergraphRLEBand(); + + virtual CPLErr IReadBlock( int nBlockXOff, int nBlockYOff, void *pImage ); +}; diff --git a/bazaar/plugin/gdal/frmts/ingr/IntergraphDataset.cpp b/bazaar/plugin/gdal/frmts/ingr/IntergraphDataset.cpp new file mode 100644 index 000000000..4cea75a13 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ingr/IntergraphDataset.cpp @@ -0,0 +1,906 @@ +/***************************************************************************** + * $Id: IntergraphDataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: Intergraph Raster Format support + * Purpose: Read/Write Intergraph Raster Format, dataset support + * Author: Ivan Lucena, [lucena_ivan at hotmail.com] + * + ****************************************************************************** + * Copyright (c) 2007, Ivan Lucena + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files ( the "Software" ), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" +#include "ogr_spatialref.h" +#include "gdal_pam.h" +#include "gdal_alg.h" + +#include "IntergraphDataset.h" +#include "IntergraphBand.h" +#include "IngrTypes.h" + +// ---------------------------------------------------------------------------- +// IntergraphDataset::IntergraphDataset() +// ---------------------------------------------------------------------------- + +IntergraphDataset::IntergraphDataset() +{ + pszFilename = NULL; + fp = NULL; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + hVirtual.poDS = NULL; + hVirtual.poBand = NULL; + hVirtual.pszFileName = NULL; + + memset(&hHeaderOne, 0, sizeof(hHeaderOne)); + memset(&hHeaderTwo, 0, sizeof(hHeaderTwo)); +} + +// ---------------------------------------------------------------------------- +// IntergraphDataset::~IntergraphDataset() +// ---------------------------------------------------------------------------- + +IntergraphDataset::~IntergraphDataset() +{ + FlushCache(); + + CPLFree( pszFilename ); + + if( fp != NULL ) + { + VSIFCloseL( fp ); + } +} + +// ---------------------------------------------------------------------------- +// IntergraphDataset::Open() +// ---------------------------------------------------------------------------- + +GDALDataset *IntergraphDataset::Open( GDALOpenInfo *poOpenInfo ) +{ + if( poOpenInfo->nHeaderBytes < 1024 ) + { + return NULL; + } + + // -------------------------------------------------------------------- + // Assign Header Information + // -------------------------------------------------------------------- + + INGR_HeaderOne hHeaderOne; + + INGR_HeaderOneDiskToMem( &hHeaderOne, (GByte*) poOpenInfo->pabyHeader); + + // -------------------------------------------------------------------- + // Check Header Type (HTC) Version + // -------------------------------------------------------------------- + + if( hHeaderOne.HeaderType.Version != INGR_HEADER_VERSION ) + { + return NULL; + } + + // -------------------------------------------------------------------- + // Check Header Type (HTC) 2D / 3D Flag + // -------------------------------------------------------------------- + + if( ( hHeaderOne.HeaderType.Is2Dor3D != INGR_HEADER_2D ) && + ( hHeaderOne.HeaderType.Is2Dor3D != INGR_HEADER_3D ) ) + { + return NULL; + } + + // -------------------------------------------------------------------- + // Check Header Type (HTC) Type Flag + // -------------------------------------------------------------------- + + if( hHeaderOne.HeaderType.Type != INGR_HEADER_TYPE ) + { + return NULL; + } + + // -------------------------------------------------------------------- + // Check Grid File Version (VER) + // -------------------------------------------------------------------- + + if( hHeaderOne.GridFileVersion != 1 && + hHeaderOne.GridFileVersion != 2 && + hHeaderOne.GridFileVersion != 3 ) + { + return NULL; + } + + // -------------------------------------------------------------------- + // Check Words To Follow (WTC) Minimum Value + // -------------------------------------------------------------------- + + if( hHeaderOne.WordsToFollow < 254 ) + { + return NULL; + } + + // -------------------------------------------------------------------- + // Check Words To Follow (WTC) Integrity + // -------------------------------------------------------------------- + + float fHeaderBlocks = (float) ( hHeaderOne.WordsToFollow + 2 ) / 256; + + if( ( fHeaderBlocks - (int) fHeaderBlocks ) != 0.0 ) + { + return NULL; + } + + // -------------------------------------------------------------------- + // Get Data Type Code (DTC) => Format Type + // -------------------------------------------------------------------- + + INGR_Format eFormat = (INGR_Format) hHeaderOne.DataTypeCode; + + // -------------------------------------------------------------------- + // We need to scan around the file, so we open it now. + // -------------------------------------------------------------------- + + VSILFILE *fp; + + if( poOpenInfo->eAccess == GA_ReadOnly ) + { + fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + } + else + { + fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" ); + } + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, "%s", VSIStrerror( errno ) ); + return NULL; + } + + // -------------------------------------------------------------------- + // Get Format Type from the tile directory + // -------------------------------------------------------------------- + + if( hHeaderOne.DataTypeCode == TiledRasterData ) + { + INGR_TileHeader hTileDir; + + int nOffset = 2 + ( 2 * ( hHeaderOne.WordsToFollow + 1 ) ); + + GByte abyBuffer[SIZEOF_TDIR]; + + if( (VSIFSeekL( fp, nOffset, SEEK_SET ) == -1 ) || + (VSIFReadL( abyBuffer, 1, SIZEOF_TDIR, fp ) == 0) ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_AppDefined, + "Error reading tiles header" ); + return NULL; + } + + INGR_TileHeaderDiskToMem( &hTileDir, abyBuffer ); + + if( ! + ( hTileDir.ApplicationType == 1 && + hTileDir.SubTypeCode == 7 && + ( hTileDir.WordsToFollow % 4 ) == 0 && + hTileDir.PacketVersion == 1 && + hTileDir.Identifier == 1 ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot recognize tiles header info"); + VSIFCloseL( fp ); + return NULL; + } + + eFormat = (INGR_Format) hTileDir.DataTypeCode; + } + + // -------------------------------------------------------------------- + // Check Scannable Flag + // -------------------------------------------------------------------- +/* + if (hHeaderOne.ScannableFlag == HasLineHeader) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Intergraph Raster Scannable Line Header not supported yet" ); + VSIFCloseL( fp ); + return NULL; + } +*/ + // -------------------------------------------------------------------- + // Check supported Format Type + // -------------------------------------------------------------------- + + if( eFormat != ByteInteger && + eFormat != WordIntegers && + eFormat != Integers32Bit && + eFormat != FloatingPoint32Bit && + eFormat != FloatingPoint64Bit && + eFormat != RunLengthEncoded && + eFormat != RunLengthEncodedC && + eFormat != CCITTGroup4 && + eFormat != AdaptiveRGB && + eFormat != Uncompressed24bit && + eFormat != AdaptiveGrayScale && + eFormat != ContinuousTone && + eFormat != JPEGGRAY && + eFormat != JPEGRGB && + eFormat != JPEGCYMK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Intergraph Raster Format %d ( \"%s\" ) not supported", + hHeaderOne.DataTypeCode, INGR_GetFormatName( (uint16) eFormat ) ); + VSIFCloseL( fp ); + return NULL; + } + + // ----------------------------------------------------------------- + // Create a corresponding GDALDataset + // ----------------------------------------------------------------- + + IntergraphDataset *poDS; + + poDS = new IntergraphDataset(); + poDS->eAccess = poOpenInfo->eAccess; + poDS->pszFilename = CPLStrdup( poOpenInfo->pszFilename ); + poDS->fp = fp; + + // -------------------------------------------------------------------- + // Get X Size from Pixels Per Line (PPL) + // -------------------------------------------------------------------- + + poDS->nRasterXSize = hHeaderOne.PixelsPerLine; + + // -------------------------------------------------------------------- + // Get Y Size from Number of Lines (NOL) + // -------------------------------------------------------------------- + + poDS->nRasterYSize = hHeaderOne.NumberOfLines; + + if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid dimensions : %d x %d", + poDS->nRasterXSize, poDS->nRasterYSize); + delete poDS; + return NULL; + } + + // -------------------------------------------------------------------- + // Get Geo Transformation from Homogeneous Transformation Matrix (TRN) + // -------------------------------------------------------------------- + + INGR_GetTransMatrix( &hHeaderOne, poDS->adfGeoTransform ); + + // -------------------------------------------------------------------- + // Set Metadata Information + // -------------------------------------------------------------------- + + poDS->SetMetadataItem( "VERSION", + CPLSPrintf ( "%d", hHeaderOne.GridFileVersion ), "IMAGE_STRUCTURE" ); + poDS->SetMetadataItem( "RESOLUTION", + CPLSPrintf ( "%d", (hHeaderOne.DeviceResolution < 0)?-hHeaderOne.DeviceResolution:1) ); + + // -------------------------------------------------------------------- + // Create Band Information + // -------------------------------------------------------------------- + + int nBands = 0; + int nBandOffset = 0; + + GByte abyBuf[MAX(SIZEOF_HDR1,SIZEOF_HDR2_A)]; + + do + { + VSIFSeekL( poDS->fp, nBandOffset, SEEK_SET ); + + VSIFReadL( abyBuf, 1, SIZEOF_HDR1, poDS->fp ); + + INGR_HeaderOneDiskToMem( &poDS->hHeaderOne, abyBuf ); + + VSIFReadL( abyBuf, 1, SIZEOF_HDR2_A, poDS->fp ); + + INGR_HeaderTwoADiskToMem( &poDS->hHeaderTwo, abyBuf ); + + switch( eFormat ) + { + case JPEGRGB: + case JPEGCYMK: + { + IntergraphBitmapBand* poBand; + nBands++; + poDS->SetBand( nBands, + poBand = new IntergraphBitmapBand( poDS, nBands, nBandOffset, 1 )); + if (poBand->pabyBMPBlock == NULL) + { + delete poDS; + return NULL; + } + nBands++; + poDS->SetBand( nBands, + poBand = new IntergraphBitmapBand( poDS, nBands, nBandOffset, 2 )); + if (poBand->pabyBMPBlock == NULL) + { + delete poDS; + return NULL; + } + nBands++; + poDS->SetBand( nBands, + poBand = new IntergraphBitmapBand( poDS, nBands, nBandOffset, 3 )); + if (poBand->pabyBMPBlock == NULL) + { + delete poDS; + return NULL; + } + break; + } + case JPEGGRAY: + case CCITTGroup4: + { + IntergraphBitmapBand* poBand; + nBands++; + poDS->SetBand( nBands, + poBand = new IntergraphBitmapBand( poDS, nBands, nBandOffset )); + if (poBand->pabyBMPBlock == NULL) + { + delete poDS; + return NULL; + } + break; + } + case RunLengthEncoded: + case RunLengthEncodedC: + case AdaptiveGrayScale: + { + IntergraphRLEBand* poBand; + nBands++; + poDS->SetBand( nBands, + poBand = new IntergraphRLEBand( poDS, nBands, nBandOffset )); + if (poBand->pabyBlockBuf == NULL || poBand->pabyRLEBlock == NULL) + { + delete poDS; + return NULL; + } + break; + } + case AdaptiveRGB: + case ContinuousTone: + { + IntergraphRLEBand* poBand; + nBands++; + poDS->SetBand( nBands, + poBand = new IntergraphRLEBand( poDS, nBands, nBandOffset, 1 )); + if (poBand->pabyBlockBuf == NULL || poBand->pabyRLEBlock == NULL) + { + delete poDS; + return NULL; + } + nBands++; + poDS->SetBand( nBands, + poBand = new IntergraphRLEBand( poDS, nBands, nBandOffset, 2 )); + if (poBand->pabyBlockBuf == NULL || poBand->pabyRLEBlock == NULL) + { + delete poDS; + return NULL; + } + nBands++; + poDS->SetBand( nBands, + poBand = new IntergraphRLEBand( poDS, nBands, nBandOffset, 3 )); + if (poBand->pabyBlockBuf == NULL || poBand->pabyRLEBlock == NULL) + { + delete poDS; + return NULL; + } + break; + } + case Uncompressed24bit: + { + IntergraphRGBBand* poBand; + nBands++; + poDS->SetBand( nBands, + poBand = new IntergraphRGBBand( poDS, nBands, nBandOffset, 1 )); + if (poBand->pabyBlockBuf == NULL) + { + delete poDS; + return NULL; + } + nBands++; + poDS->SetBand( nBands, + poBand = new IntergraphRGBBand( poDS, nBands, nBandOffset, 2 )); + if (poBand->pabyBlockBuf == NULL) + { + delete poDS; + return NULL; + } + nBands++; + poDS->SetBand( nBands, + poBand = new IntergraphRGBBand( poDS, nBands, nBandOffset, 3 )); + if (poBand->pabyBlockBuf == NULL) + { + delete poDS; + return NULL; + } + break; + } + default: + { + IntergraphRasterBand* poBand; + nBands++; + poDS->SetBand( nBands, + poBand = new IntergraphRasterBand( poDS, nBands, nBandOffset )); + if (poBand->pabyBlockBuf == NULL) + { + delete poDS; + return NULL; + } + } + } + + // ---------------------------------------------------------------- + // Get next band offset from Catenated File Pointer (CFP) + // ---------------------------------------------------------------- + + nBandOffset = poDS->hHeaderTwo.CatenatedFilePointer; + } + while( nBandOffset != 0 ); + + poDS->nBands = nBands; + + // -------------------------------------------------------------------- + // Initialize any PAM information + // -------------------------------------------------------------------- + + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + + /* -------------------------------------------------------------------- */ + /* Check for external overviews. */ + /* -------------------------------------------------------------------- */ + + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return ( poDS ); +} + +// ---------------------------------------------------------------------------- +// IntergraphDataset::Create() +// ---------------------------------------------------------------------------- + +GDALDataset *IntergraphDataset::Create( const char *pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + char **papszOptions ) +{ + int nDeviceResolution = 1; + const char *pszValue; + const char *pszCompression = NULL; + + pszValue = CSLFetchNameValue(papszOptions, "RESOLUTION"); + if( pszValue != NULL ) + nDeviceResolution = -atoi( pszValue ); + + char *pszExtension = CPLStrlwr(CPLStrdup(CPLGetExtension(pszFilename))); + if ( EQUAL( pszExtension, "rle" ) ) + pszCompression = INGR_GetFormatName(RunLengthEncoded); + CPLFree(pszExtension); + + if( eType != GDT_Byte && + eType != GDT_Int16 && + eType != GDT_Int32 && + eType != GDT_UInt16 && + eType != GDT_UInt32 && + eType != GDT_Float32&& + eType != GDT_Float64 ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Data type not supported (%s)", + GDALGetDataTypeName( eType ) ); + return NULL; + } + + // -------------------------------------------------------------------- + // Fill headers with minimun information + // -------------------------------------------------------------------- + + INGR_HeaderOne hHdr1; + INGR_HeaderTwoA hHdr2; + INGR_ColorTable256 hCTab; + int i; + + memset(&hHdr1, 0, SIZEOF_HDR1); + memset(&hHdr2, 0, SIZEOF_HDR2_A); + memset(&hCTab, 0, SIZEOF_CTAB); + + hHdr1.HeaderType.Version = INGR_HEADER_VERSION; + hHdr1.HeaderType.Type = INGR_HEADER_TYPE; + hHdr1.HeaderType.Is2Dor3D = INGR_HEADER_2D; + hHdr1.DataTypeCode = (uint16) INGR_GetFormat( eType, (pszCompression!=NULL)?pszCompression:"None" ); + hHdr1.WordsToFollow = ( ( SIZEOF_HDR1 * 3 ) / 2 ) - 2; + hHdr1.ApplicationType = GenericRasterImageFile; + hHdr1.XViewOrigin = 0.0; + hHdr1.YViewOrigin = 0.0; + hHdr1.ZViewOrigin = 0.0; + hHdr1.XViewExtent = 0.0; + hHdr1.YViewExtent = 0.0; + hHdr1.ZViewExtent = 0.0; + for( i = 0; i < 15; i++ ) + hHdr1.TransformationMatrix[i] = 0.0; + hHdr1.TransformationMatrix[15] = 1.0; + hHdr1.PixelsPerLine = nXSize; + hHdr1.NumberOfLines = nYSize; + hHdr1.DeviceResolution = nDeviceResolution; + hHdr1.ScanlineOrientation = UpperLeftHorizontal; + hHdr1.ScannableFlag = NoLineHeader; + hHdr1.RotationAngle = 0.0; + hHdr1.SkewAngle = 0.0; + hHdr1.DataTypeModifier = 0; + hHdr1.DesignFileName[0] = '\0'; + hHdr1.DataBaseFileName[0] = '\0'; + hHdr1.ParentGridFileName[0] = '\0'; + hHdr1.FileDescription[0] = '\0'; + hHdr1.Minimum = INGR_SetMinMax( eType, 0.0 ); + hHdr1.Maximum = INGR_SetMinMax( eType, 0.0 ); + hHdr1.GridFileVersion = 3; + hHdr1.Reserved[0] = 0; + hHdr1.Reserved[1] = 0; + hHdr1.Reserved[2] = 0; + hHdr2.Gain = 0; + hHdr2.OffsetThreshold = 0; + hHdr2.View1 = 0; + hHdr2.View2 = 0; + hHdr2.ViewNumber = 0; + hHdr2.Reserved2 = 0; + hHdr2.Reserved3 = 0; + hHdr2.AspectRatio = nXSize / nYSize; + hHdr2.CatenatedFilePointer = 0; + hHdr2.ColorTableType = NoColorTable; + hHdr2.NumberOfCTEntries = 0; + hHdr2.Reserved8 = 0; + for( i = 0; i < 110; i++ ) + hHdr2.Reserved[i] = 0; + hHdr2.ApplicationPacketLength = 0; + hHdr2.ApplicationPacketPointer = 0; + + // -------------------------------------------------------------------- + // RGB Composite assumption + // -------------------------------------------------------------------- + + if( eType == GDT_Byte && + nBands == 3 ) + { + hHdr1.DataTypeCode = Uncompressed24bit; + } + + // -------------------------------------------------------------------- + // Create output file with minimum header info + // -------------------------------------------------------------------- + + VSILFILE *fp = VSIFOpenL( pszFilename, "wb+" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file %s' failed.\n", pszFilename ); + return NULL; + } + + GByte abyBuf[MAX(SIZEOF_HDR1,SIZEOF_CTAB)]; + + INGR_HeaderOneMemToDisk( &hHdr1, abyBuf ); + + VSIFWriteL( abyBuf, 1, SIZEOF_HDR1, fp ); + + INGR_HeaderTwoAMemToDisk( &hHdr2, abyBuf ); + + VSIFWriteL( abyBuf, 1, SIZEOF_HDR2_A, fp ); + + unsigned int n = 0; + + for( i = 0; i < 256; i++ ) + { + STRC2BUF( abyBuf, n, hCTab.Entry[i].v_red ); + STRC2BUF( abyBuf, n, hCTab.Entry[i].v_green ); + STRC2BUF( abyBuf, n, hCTab.Entry[i].v_blue ); + } + + VSIFWriteL( abyBuf, 1, SIZEOF_CTAB, fp ); + + VSIFCloseL( fp ); + + // -------------------------------------------------------------------- + // Returns a new IntergraphDataset from the created file + // -------------------------------------------------------------------- + + return ( IntergraphDataset * ) GDALOpen( pszFilename, GA_Update ); +} + +// ---------------------------------------------------------------------------- +// IntergraphDataset::CreateCopy() +// ---------------------------------------------------------------------------- + +GDALDataset *IntergraphDataset::CreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, + char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressData ) +{ + (void) bStrict; + + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Intergraph driver does not support source dataset with zero band.\n"); + return NULL; + } + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + return NULL; + } + + // -------------------------------------------------------------------- + // Query GDAL Data Type + // -------------------------------------------------------------------- + + GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + + // -------------------------------------------------------------------- + // Copy metadata + // -------------------------------------------------------------------- + + char **papszCreateOptions = CSLDuplicate( papszOptions ); + const char *pszValue; + + pszValue = CSLFetchNameValue(papszCreateOptions, "RESOLUTION"); + if( pszValue == NULL ) + { + const char *value = poSrcDS->GetMetadataItem("RESOLUTION"); + if (value) + { + papszCreateOptions = CSLSetNameValue( papszCreateOptions, "RESOLUTION", + value ); + } + } + + // -------------------------------------------------------------------- + // Create IntergraphDataset + // -------------------------------------------------------------------- + + IntergraphDataset *poDstDS; + + poDstDS = (IntergraphDataset*) IntergraphDataset::Create( pszFilename, + poSrcDS->GetRasterXSize(), + poSrcDS->GetRasterYSize(), + poSrcDS->GetRasterCount(), + eType, + papszCreateOptions ); + + CSLDestroy( papszCreateOptions ); + + if( poDstDS == NULL ) + { + return NULL; + } + + // -------------------------------------------------------------------- + // Copy Transformation Matrix to the dataset + // -------------------------------------------------------------------- + + double adfGeoTransform[6]; + + poDstDS->SetProjection( poSrcDS->GetProjectionRef() ); + poSrcDS->GetGeoTransform( adfGeoTransform ); + poDstDS->SetGeoTransform( adfGeoTransform ); + + // -------------------------------------------------------------------- + // Copy information to the raster band + // -------------------------------------------------------------------- + + GDALRasterBand *poSrcBand; + GDALRasterBand *poDstBand; + double dfMin; + double dfMax; + double dfMean; + double dfStdDev = -1; + + for( int i = 1; i <= poDstDS->nBands; i++) + { + delete poDstDS->GetRasterBand(i); + } + poDstDS->nBands = 0; + + if( poDstDS->hHeaderOne.DataTypeCode == Uncompressed24bit ) + { + poDstDS->SetBand( 1, new IntergraphRGBBand( poDstDS, 1, 0, 3 ) ); + poDstDS->SetBand( 2, new IntergraphRGBBand( poDstDS, 2, 0, 2 ) ); + poDstDS->SetBand( 3, new IntergraphRGBBand( poDstDS, 3, 0, 1 ) ); + poDstDS->nBands = 3; + } + else + { + for( int i = 1; i <= poSrcDS->GetRasterCount(); i++ ) + { + poSrcBand = poSrcDS->GetRasterBand(i); + eType = poSrcDS->GetRasterBand(i)->GetRasterDataType(); + + poDstBand = new IntergraphRasterBand( poDstDS, i, 0, eType ); + poDstDS->SetBand( i, poDstBand ); + + poDstBand->SetCategoryNames( poSrcBand->GetCategoryNames() ); + poDstBand->SetColorTable( poSrcBand->GetColorTable() ); + poSrcBand->GetStatistics( false, true, &dfMin, &dfMax, &dfMean, &dfStdDev ); + poDstBand->SetStatistics( dfMin, dfMax, dfMean, dfStdDev ); + } + } + + // -------------------------------------------------------------------- + // Copy image data + // -------------------------------------------------------------------- + + int nXSize = poDstDS->GetRasterXSize(); + int nYSize = poDstDS->GetRasterYSize(); + + int nBlockXSize; + int nBlockYSize; + + CPLErr eErr = CE_None; + + for( int iBand = 1; iBand <= poSrcDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand *poDstBand = poDstDS->GetRasterBand( iBand ); + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand ); + + // ------------------------------------------------------------ + // Copy Untiled / Uncompressed + // ------------------------------------------------------------ + + int iYOffset, iXOffset; + void *pData; + + poSrcBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + nBlockXSize = nXSize; + nBlockYSize = 1; + + pData = CPLMalloc( nBlockXSize * nBlockYSize * GDALGetDataTypeSize( eType ) / 8 ); + + for( iYOffset = 0; iYOffset < nYSize; iYOffset += nBlockYSize ) + { + for( iXOffset = 0; iXOffset < nXSize; iXOffset += nBlockXSize ) + { + eErr = poSrcBand->RasterIO( GF_Read, + iXOffset, iYOffset, + nBlockXSize, nBlockYSize, + pData, nBlockXSize, nBlockYSize, + eType, 0, 0, NULL ); + if( eErr != CE_None ) + { + return NULL; + } + eErr = poDstBand->RasterIO( GF_Write, + iXOffset, iYOffset, + nBlockXSize, nBlockYSize, + pData, nBlockXSize, nBlockYSize, + eType, 0, 0, NULL ); + if( eErr != CE_None ) + { + return NULL; + } + } + if( ( eErr == CE_None ) && ( ! pfnProgress( + ( iYOffset + 1 ) / ( double ) nYSize, NULL, pProgressData ) ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated CreateCopy()" ); + } + } + CPLFree( pData ); + } + + // -------------------------------------------------------------------- + // Finalize + // -------------------------------------------------------------------- + + poDstDS->FlushCache(); + + return poDstDS; +} + +// ---------------------------------------------------------------------------- +// IntergraphDataset::GetGeoTransform() +// ---------------------------------------------------------------------------- + +CPLErr IntergraphDataset::GetGeoTransform( double *padfTransform ) +{ + if( GDALPamDataset::GetGeoTransform( padfTransform ) != CE_None ) + { + memcpy( padfTransform, adfGeoTransform, sizeof( double ) * 6 ); + } + + return CE_None; +} + +// ---------------------------------------------------------------------------- +// IntergraphDataset::SetGeoTransform() +// ---------------------------------------------------------------------------- + +CPLErr IntergraphDataset::SetGeoTransform( double *padfTransform ) +{ + if( GDALPamDataset::SetGeoTransform( padfTransform ) != CE_None ) + { + memcpy( adfGeoTransform, padfTransform, sizeof( double ) * 6 ); + } + + INGR_SetTransMatrix( hHeaderOne.TransformationMatrix, padfTransform ); + + return CE_None; +} + +// ---------------------------------------------------------------------------- +// IntergraphDataset::SetProjection() +// ---------------------------------------------------------------------------- + +CPLErr IntergraphDataset::SetProjection( const char *pszProjString ) +{ + (void) pszProjString; + + return CE_None; +} + +// ---------------------------------------------------------------------------- +// GDALRegister_INGR() +// ---------------------------------------------------------------------------- + +void GDALRegister_INGR() +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "INGR" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "INGR" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "Intergraph Raster" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_IntergraphRaster.html" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 Int32 Float32 Float64" ); + poDriver->pfnOpen = IntergraphDataset::Open; + poDriver->pfnCreate = IntergraphDataset::Create; + poDriver->pfnCreateCopy = IntergraphDataset::CreateCopy; + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/ingr/IntergraphDataset.h b/bazaar/plugin/gdal/frmts/ingr/IntergraphDataset.h new file mode 100644 index 000000000..29b4a892b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ingr/IntergraphDataset.h @@ -0,0 +1,75 @@ +/***************************************************************************** + * $Id: IntergraphDataset.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: Intergraph Raster Format support + * Purpose: Read selected types of Intergraph Raster Format + * Author: Ivan Lucena, [lucena_ivan at hotmail.com] + * + ****************************************************************************** + * Copyright (c) 2007, Ivan Lucena + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files ( the "Software" ), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "IngrTypes.h" + +// ---------------------------------------------------------------------------- +// Intergraph GDALDataset +// ---------------------------------------------------------------------------- + +class IntergraphDataset : public GDALPamDataset +{ + friend class IntergraphRasterBand; + friend class IntergraphRGBBand; + friend class IntergraphBitmapBand; + friend class IntergraphRLEBand; + +private: + VSILFILE *fp; + char *pszFilename; + double adfGeoTransform[6]; + + INGR_HeaderOne hHeaderOne; + INGR_HeaderTwoA hHeaderTwo; + INGR_VirtualFile hVirtual; + +public: + IntergraphDataset(); + ~IntergraphDataset(); + + static GDALDataset *Open( GDALOpenInfo *poOpenInfo ); + static GDALDataset *Create( const char *pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + char **papszOptions ); + static GDALDataset *CreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + + virtual CPLErr GetGeoTransform( double *padfTransform ); + virtual CPLErr SetGeoTransform( double *padfTransform ); + virtual CPLErr SetProjection( const char *pszProjString ); +}; + diff --git a/bazaar/plugin/gdal/frmts/ingr/JpegHelper.cpp b/bazaar/plugin/gdal/frmts/ingr/JpegHelper.cpp new file mode 100644 index 000000000..79fdd69d7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ingr/JpegHelper.cpp @@ -0,0 +1,317 @@ +/***************************************************************************** + * $Id: $ + * + * Project: Creates a jpeg header + * Purpose: Abreviated JPEG support + * Author: Ivan Lucena, [lucena_ivan at hotmail.com] + * + ****************************************************************************** + * Copyright (c) 2007, Ivan Lucena + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files ( the "Software" ), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#include "JpegHelper.h" + +static const GByte JPGHLP_1DC_Codes[] = { + 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, +}; + +static const GByte JPGHLP_1AC_Codes[] = { + 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125, +}; + +static const GByte JPGHLP_1DC_Symbols[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +}; + +static const GByte JPGHLP_1AC_Symbols[] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xCA, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, +}; + +static const GByte JPGHLP_2AC_Codes[] = { + 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119, +}; + +static const GByte JPGHLP_2DC_Codes[] = { + 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, +}; + +static const GByte JPGHLP_2DC_Symbols[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +}; + +static const GByte JPGHLP_2AC_Symbols[] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xCA, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, +}; + +static const GByte JPGHLP_DQT_luminace[64] = { + 10, 7, 6, 10, 14, 24, 31, 37, + 7, 7, 8, 11, 16, 35, 36, 33, + 8, 8, 10, 14, 24, 34, 41, 34, + 8, 10, 13, 17, 31, 52, 48, 37, + 11, 13, 22, 34, 41, 65, 62, 46, + 14, 21, 33, 38, 49, 62, 68, 55, + 29, 38, 47, 52, 62, 73, 72, 61, + 43, 55, 57, 59, 67, 60, 62, 59 +}; + +static const GByte JPGHLP_DQT_chrominance[64] = { + 10, 11, 14, 28, 59, 59, 59, 59, + 11, 13, 16, 40, 59, 59, 59, 59, + 14, 16, 34, 59, 59, 59, 59, 59, + 28, 40, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59 +}; + +static const GByte ZIGZAG[64] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +#define ZIGZAGCPY(ou, in) \ + { int i; for( i = 0; i < 64; i++ ) ou[ZIGZAG[i]] = in[i]; } + +#define ADJUST(tb, op, vl) \ + { int i; for( i = 0; i < 64; i++ ) tb[i] = (GByte) (tb[i] op vl); } + +int JPGHLP_HeaderMaker( GByte *pabyBuffer, + const int nCols, + const int nRows, + const int nComponents, + CPL_UNUSED const int nRestart, + const int nQuality ) +{ + int i; + + GByte *pabNext = pabyBuffer; + + // ------------------------------------------------------------------------ + // Start of Image + // ------------------------------------------------------------------------ + + *( pabNext++ ) = 0xFF; // Tag Mark + *( pabNext++ ) = 0xD8; // SOI + + // ------------------------------------------------------------------------ + // Application Segment + // ------------------------------------------------------------------------ + + *( pabNext++ ) = 0xFF; // Tag Mark + *( pabNext++ ) = 0xE0; // APP0 + *( pabNext++ ) = 0x00; // Segment Length (msb) + *( pabNext++ ) = 0x10; // Segment Length (lsb) + *( pabNext++ ) = 0x4a; // 'J' + *( pabNext++ ) = 0x46; // 'F' + *( pabNext++ ) = 0x49; // 'I' + *( pabNext++ ) = 0x46; // 'F' + *( pabNext++ ) = 0x00; // '\0' + *( pabNext++ ) = 0x01; // Version 1 + *( pabNext++ ) = 0x01; // Sub Version 1 + *( pabNext++ ) = 0x00; // Pixels per inch, 4 Bits for X, 4 Bits for Y + *( pabNext++ ) = 0x00; // Horizontal Pixel Density (msb) + *( pabNext++ ) = 0x01; // Horizontal Pixel Density (lsb) + *( pabNext++ ) = 0x00; // Vertical Pixel Density (msb) + *( pabNext++ ) = 0x01; // Vertical Pixel Density (lsb) + *( pabNext++ ) = 0x00; // Thumbnail Width + *( pabNext++ ) = 0x00; // Thumbnail Height + + // ------------------------------------------------------------------------ + // Quantization Table Segment + // ------------------------------------------------------------------------ + + GByte abQuantTables[2][64]; + ZIGZAGCPY( abQuantTables[0], JPGHLP_DQT_luminace ); + ZIGZAGCPY( abQuantTables[1], JPGHLP_DQT_chrominance ); + + if( nQuality == 30 ) + { + ADJUST( abQuantTables[0], *, 0.5 ); + ADJUST( abQuantTables[1], *, 0.5 ); + } + + for( i = 0; i < 2 && i < nComponents; i++ ) + { + *( pabNext++ ) = 0xFF; // Tag Mark + *( pabNext++ ) = 0xDB; // DQT + *( pabNext++ ) = 0; // Segment Length (msb) + *( pabNext++ ) = 67; // Length (msb) + *( pabNext++ ) = (GByte) i; // Table ID + memcpy( pabNext, abQuantTables[i], 64 ); + pabNext += 64; + } + + // ------------------------------------------------------------------------ + // Start Of Frame Segment + // ------------------------------------------------------------------------ + + *( pabNext++ ) = 0xFF; + *( pabNext++ ) = 0xC0; // SOF + *( pabNext++ ) = 0; // Segment Length (msb) + if ( nComponents > 1 ) + *( pabNext++ ) = 17; // Segment Length (lsb) + else + *( pabNext++ ) = 11; // Segment Length (lsb) + *( pabNext++ ) = 8; // 8-bit Precision + *( pabNext++ ) = (GByte) (nRows >> 8); // Height in rows (msb) + *( pabNext++ ) = (GByte) nRows;// Height in rows (lsb) + *( pabNext++ ) = (GByte) (nCols >> 8); // Width in columns (msb) + *( pabNext++ ) = (GByte) nCols;// Width in columns (lsb) + *( pabNext++ ) = (GByte) nComponents;// Number of components + *( pabNext++ ) = 0; // Component ID + *( pabNext++ ) = 0x21; // Hozontal/Vertical Sampling + *( pabNext++ ) = 0; // Quantization table ID + if ( nComponents > 1 ) + { + *( pabNext++ ) = 1; // Component ID + *( pabNext++ ) = 0x11; // Hozontal/Vertical Sampling + *( pabNext++ ) = 1; // Quantization table ID + *( pabNext++ ) = 2; // Component ID + *( pabNext++ ) = 0x11; // Hozontal/Vertical Sampling + *( pabNext++ ) = 1; // Quantization table ID + } + + // ------------------------------------------------------------------------ + // Huffman Table Segments + // ------------------------------------------------------------------------ + + const GByte *pabHuffTab[2][4]; + pabHuffTab[0][0] = JPGHLP_1DC_Codes; + pabHuffTab[0][1] = JPGHLP_1AC_Codes; + pabHuffTab[0][2] = JPGHLP_1DC_Symbols; + pabHuffTab[0][3] = JPGHLP_1AC_Symbols; + + pabHuffTab[1][0] = JPGHLP_2DC_Codes; + pabHuffTab[1][1] = JPGHLP_2AC_Codes; + pabHuffTab[1][2] = JPGHLP_2DC_Symbols; + pabHuffTab[1][3] = JPGHLP_2AC_Symbols; + + int pnHTs[2][4]; + pnHTs[0][0] = sizeof(JPGHLP_1DC_Codes); + pnHTs[0][1] = sizeof(JPGHLP_1AC_Codes); + pnHTs[0][2] = sizeof(JPGHLP_1DC_Symbols); + pnHTs[0][3] = sizeof(JPGHLP_1AC_Symbols); + + pnHTs[1][0] = sizeof(JPGHLP_2DC_Codes); + pnHTs[1][1] = sizeof(JPGHLP_2AC_Codes); + pnHTs[1][2] = sizeof(JPGHLP_2DC_Symbols); + pnHTs[1][3] = sizeof(JPGHLP_2AC_Symbols); + + int j, k; + for( i = 0; i < 2 && i < nComponents; i++ ) + { + for( j = 0; j < 2; j++ ) + { + k = j + 2; + int nCodes = pnHTs[i][j]; + int nSymbols = pnHTs[i][k]; + *( pabNext++ ) = 0xFF; // Tag Mark + *( pabNext++ ) = 0xc4; // DHT + *( pabNext++ ) = 0; // Segment Length (msb) + *( pabNext++ ) = (GByte) (3 + nCodes + nSymbols); // Segment Length (lsb) + *( pabNext++ ) = (GByte) ((j << 4) | i); // Table ID + memcpy( pabNext, pabHuffTab[i][j], nCodes ); + pabNext += nCodes; + memcpy( pabNext, pabHuffTab[i][k], nSymbols ); + pabNext += nSymbols; + } + } + + // ------------------------------------------------------------------------ + // Start Of Scan Segment + // ------------------------------------------------------------------------ + + *( pabNext++ ) = 0xFF; // Tag Mark + *( pabNext++ ) = 0xDA; // SOS + if (nComponents > 1 ) + { + *( pabNext++ ) = 0; // Segment Length (msb) + *( pabNext++ ) = 12; // Segment Length (lsb) + *( pabNext++ ) = 3; // Number of components + *( pabNext++ ) = 0; // Components 0 + *( pabNext++ ) = 0; // Huffman table ID + *( pabNext++ ) = 1; // Components 1 + *( pabNext++ ) = 0x11; // Huffman table ID + *( pabNext++ ) = 2; // Components 2 + *( pabNext++ ) = 0x11; // Huffman table ID + } + else + { + *( pabNext++ ) = 0; // Segment Length (msb) + *( pabNext++ ) = 8; // Segment Length (lsb) + *( pabNext++ ) = 1; // Number of components + *( pabNext++ ) = 0; // Components 0 + *( pabNext++ ) = 0; // Huffman table ID + } + *( pabNext++ ) = 0; // First DCT coefficient + *( pabNext++ ) = 63; // Last DCT coefficient + *( pabNext++ ) = 0; // Spectral selection + + return pabNext - pabyBuffer; +} diff --git a/bazaar/plugin/gdal/frmts/ingr/JpegHelper.h b/bazaar/plugin/gdal/frmts/ingr/JpegHelper.h new file mode 100644 index 000000000..b97f46dd6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ingr/JpegHelper.h @@ -0,0 +1,40 @@ +/***************************************************************************** + * $Id: $ + * + * Project: Project: Creates a jpeg header + * Purpose: Abreviated JPEG support + * Author: Ivan Lucena, [lucena_ivan at hotmail.com] + * + ****************************************************************************** + * Copyright (c) 2007, Ivan Lucena + * Copyright (c) 2007, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files ( the "Software" ), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_port.h" + +int JPGHLP_HeaderMaker( GByte *pabyBuffer, + const int nCols, + const int nRows, + const int nComponents, + const int nRestart, + const int nQuality ); diff --git a/bazaar/plugin/gdal/frmts/ingr/frmt_intergraphraster.html b/bazaar/plugin/gdal/frmts/ingr/frmt_intergraphraster.html new file mode 100644 index 000000000..4be174023 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ingr/frmt_intergraphraster.html @@ -0,0 +1,231 @@ + + + + +INGR --- Intergraph Raster Format + + + + +

    INGR --- Intergraph Raster Format

    + +This format is supported for read and writes access.

    + +The Intergraph Raster File Format was the native file format used by +Intergraph software applications to store raster data. It is manifested in +several internal data formats.

    + +

    Reading INGR Files

    + +Those are the data formats that the INGR driver supports for reading:

    + +

      +
    • 2 - Byte Integer
    • +
    • 3 - Word Integer
    • +
    • 4 - Integers 32 bit
    • +
    • 5 - Floating Point 32 bit
    • +
    • 6 - Floating Point 64 bit
    • +
    • 9 - Run Length Encoded
    • +
    • 10 - Run Length Encoded Color
    • +
    • 24 - CCITT Group 4
    • +
    • 27 - Adaptive RGB
    • +
    • 28 - Uncompressed 24 bit
    • +
    • 29 - Adaptive Gray Scale
    • +
    • 30 - JPEG GRAY
    • +
    • 31 - JPEG RGB
    • +
    • 32 - JPEG CYMK
    • +
    • 65 - Tiled
    • +
    • 67 - Continuous Tone 
    • +
    + +The format "65 - Tiled" is not a format; it is just an indication that the file +is tiled. In this case the tile header contains the real data format code that could be any of the above formats. The INGR driver can read tiled and untilled +instance of any of the supported data formats.

    + +

    Writing INGR Files

    + +Those are the data formats that the INGR driver supports for writing:

    + +

      +
    • 2 - Byte Integer
    • +
    • 3 - Word Integers
    • +
    • 4 - Integers 32Bit
    • +
    • 5 - Floating Point 32Bit
    • +
    • 6 - Floating Point 64Bit
    • +
    + +Type 9 RLE bitonal compression is used when outputting ".rle" file. Other file types are uncompressed.

    + +Note that writing in that format is not encouraged.

    + +

    File Extension

    + +The following is a partial listing of INGR file extensions:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    .cot

    +
    +

    8-bit grayscale or color table data

    +
    +

    .ctc

    +
    +

    8-bit grayscale using PackBits-type compression (uncommon)

    +
    +

    .rgb

    +
    +

    24-bit color and grayscale (uncompressed and PackBits-type compression)

    +
    +

    .ctb

    +
    +

    8-bit color table data (uncompressed or run-length encoded)

    +
    +

    .grd

    +
    +

    8, 16 and 32 bit elevation data

    +
    +

    .crl

    +
    +

    8 or 16 bit, run-length compressed grayscale or color table data

    +
    +

    .tpe

    +
    +

    8 or 16 bit, run-length compressed grayscale or color table data

    +
    +

    .lsr

    +
    +

    8 or 16 bit, run-length compressed grayscale or color table data

    +
    +

    .rle

    +
    +

    1-bit run-length compressed data (16-bit runs)

    +
    +

    .cit

    +
    +

    CCITT G3 or G4 1-bit data

    +
    +

    .g3

    +
    +

    CCITT G3 1-bit data

    +
    +

    .g4

    +
    +

    CCITT G4 1-bit data

    +
    +

    .tg4

    +
    +

    CCITT G4 1-bit data (tiled)

    +
    +

    .cmp

    +
    +

    JPEG grayscale, RGB, or CMYK

    +
    +

    .jpg +

    +
    +

    JPEG grayscale, RGB, or CMYK

    +
    +

    +
    + +The INGR driver does not require any especial file +extension in order to identify or create an INGR file.

    + +

    Georeference

    + +The INGR driver does not support reading or writing +georeference information. The reason for that is because there is no universal +way of storing georeferencing in INGR files. It could have georeference stored +in a companying .dgn file or in application specific data storage inside the +file itself.

    + +

    Metadata

    + +The following creation option and bandset metadata is available.

    +

      +
    • RESOLUTION: This is the DPI (dots per inch). Microns not supported.
    • +
    + + +

    See Also:

    + +For more information:

    + +

    + + + diff --git a/bazaar/plugin/gdal/frmts/ingr/makefile.vc b/bazaar/plugin/gdal/frmts/ingr/makefile.vc new file mode 100644 index 000000000..8f03724bb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ingr/makefile.vc @@ -0,0 +1,15 @@ +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +OBJ = intergraphdataset.obj intergraphband.obj ingrtypes.obj jpeghelper.obj + +EXTRAFLAGS = -D_USE_MATH_DEFINES -I..\gtiff -I..\gtiff\libtiff + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + cd .. + +clean: + -del *.obj + cd .. diff --git a/bazaar/plugin/gdal/frmts/iris/GNUmakefile b/bazaar/plugin/gdal/frmts/iris/GNUmakefile new file mode 100644 index 000000000..a5f821714 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iris/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = irisdataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/iris/irisdataset.cpp b/bazaar/plugin/gdal/frmts/iris/irisdataset.cpp new file mode 100644 index 000000000..cb7d0f753 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iris/irisdataset.cpp @@ -0,0 +1,992 @@ +/****************************************************************************** + * $Id: irisdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: IRIS Reader + * Purpose: All code for IRIS format Reader + * Author: Roger Veciana, rveciana@gmail.com + * Portions are adapted from code copyright (C) 2005-2012 + * Chris Veness under a CC-BY 3.0 licence + * + ****************************************************************************** + * Copyright (c) 2012, Roger Veciana + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef DEG2RAD +# define DEG2RAD (M_PI/180.0) +#endif + +#ifndef RAD2DEG +# define RAD2DEG (180.0/M_PI) +#endif + +#include "gdal_pam.h" +#include "ogr_spatialref.h" +#include + + +CPL_CVSID("$Id: irisdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +CPL_C_START +void GDALRegister_IRIS(void); +CPL_C_END + +#define ARRAY_ELEMENT_COUNT(x) ((sizeof(x))/sizeof(x[0])) + +/************************************************************************/ +/* ==================================================================== */ +/* IRISDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class IRISRasterBand; + +class IRISDataset : public GDALPamDataset +{ + friend class IRISRasterBand; + + VSILFILE *fp; + GByte abyHeader[640]; + int bNoDataSet; + double dfNoDataValue; + static const char* const aszProductNames[]; + static const char* const aszDataTypeCodes[]; + static const char* const aszDataTypes[]; + static const char* const aszProjections[]; + unsigned short nProductCode; + unsigned short nDataTypeCode; + unsigned char nProjectionCode; + float fNyquistVelocity; + char* pszSRS_WKT; + double adfGeoTransform[6]; + int bHasLoadedProjection; + void LoadProjection(); + std::pair GeodesicCalculation(float fLat, float fLon, float fAngle, float fDist, float fEquatorialRadius, float fPolarRadius, float fFlattening); + public: + IRISDataset(); + ~IRISDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef(); + +}; + +const char* const IRISDataset::aszProductNames[]= { + "", "PPI", "RHI", "CAPPI", "CROSS", "TOPS", "TRACK", "RAIN1", "RAINN", + "VVP", "VIL", "SHEAR", "WARN", "CATCH", "RTI", "RAW", "MAX", "USER", + "USERV", "OTHER", "STATUS", "SLINE", "WIND", "BEAM", "TEXT", "FCAST", + "NDOP", "IMAGE", "COMP", "TDWR", "GAGE", "DWELL", "SRI", "BASE", "HMAX"}; + +const char* const IRISDataset::aszDataTypeCodes[]={ + "XHDR", "DBT" ,"dBZ", "VEL", "WIDTH", "ZDR", "ORAIN", "dBZC", "DBT2", + "dBZ2", "VEL2", "WIDTH2", "ZDR2", "RAINRATE2", "KDP", "KDP2", "PHIDP", + "VELC", "SQI", "RHOHV", "RHOHV2", "dBZC2", "VELC2", "SQI2", "PHIDP2", + "LDRH", "LDRH2", "LDRV", "LDRV2", "FLAGS", "FLAGS2", "FLOAT32", "HEIGHT", + "VIL2", "NULL", "SHEAR", "DIVERGE2", "FLIQUID2", "USER", "OTHER", "DEFORM2", + "VVEL2", "HVEL2", "HDIR2", "AXDIL2", "TIME2", "RHOH", "RHOH2", "RHOV", + "RHOV2", "PHIH", "PHIH2", "PHIV", "PHIV2", "USER2", "HCLASS", "HCLASS2", + "ZDRC", "ZDRC2", "TEMPERATURE16", "VIR16", "DBTV8", "DBTV16", "DBZV8", + "DBZV16", "SNR8", "SNR16", "ALBEDO8", "ALBEDO16", "VILD16", "TURB16"}; +const char* const IRISDataset::aszDataTypes[]={ + "Extended Headers","Total H power (1 byte)","Clutter Corrected H reflectivity (1 byte)", + "Velocity (1 byte)","Width (1 byte)","Differential reflectivity (1 byte)", + "Old Rainfall rate (stored as dBZ)","Fully corrected reflectivity (1 byte)", + "Uncorrected reflectivity (2 byte)","Corrected reflectivity (2 byte)", + "Velocity (2 byte)","Width (2 byte)","Differential reflectivity (2 byte)", + "Rainfall rate (2 byte)","Kdp (specific differential phase)(1 byte)", + "Kdp (specific differential phase)(2 byte)","PHIdp (differential phase)(1 byte)", + "Corrected Velocity (1 byte)","SQI (1 byte)","RhoHV(0) (1 byte)","RhoHV(0) (2 byte)", + "Fully corrected reflectivity (2 byte)","Corrected Velocity (2 byte)","SQI (2 byte)", + "PHIdp (differential phase)(2 byte)","LDR H to V (1 byte)","LDR H to V (2 byte)", + "LDR V to H (1 byte)","LDR V to H (2 byte)","Individual flag bits for each bin","", + "Test of floating format", "Height (1/10 km) (1 byte)", "Linear liquid (.001mm) (2 byte)", + "Data type is not applicable", "Wind Shear (1 byte)", "Divergence (.001 10**-4) (2-byte)", + "Floated liquid (2 byte)", "User type, unspecified data (1 byte)", + "Unspecified data, no color legend", "Deformation (.001 10**-4) (2-byte)", + "Vertical velocity (.01 m/s) (2-byte)", "Horizontal velocity (.01 m/s) (2-byte)", + "Horizontal wind direction (.1 degree) (2-byte)", "Axis of Dillitation (.1 degree) (2-byte)", + "Time of data (seconds) (2-byte)", "Rho H to V (1 byte)", "Rho H to V (2 byte)", + "Rho V to H (1 byte)", "Rho V to H (2 byte)", "Phi H to V (1 byte)", "Phi H to V (2 byte)", + "Phi V to H (1 byte)", "Phi V to H (2 byte)", "User type, unspecified data (2 byte)", + "Hydrometeor class (1 byte)", "Hydrometeor class (2 byte)", "Corrected Differential reflectivity (1 byte)", + "Corrected Differential reflectivity (2 byte)", "Temperature (2 byte)", + "Vertically Integrated Reflectivity (2 byte)", "Total V Power (1 byte)", "Total V Power (2 byte)", + "Clutter Corrected V Reflectivity (1 byte)", "Clutter Corrected V Reflectivity (2 byte)", + "Signal to Noise ratio (1 byte)", "Signal to Noise ratio (2 byte)", "Albedo (1 byte)", + "Albedo (2 byte)", "VIL Density (2 byte)", "Turbulence (2 byte)"}; +const char* const IRISDataset::aszProjections[]={ + "Azimutal equidistant","Mercator","Polar Stereographic","UTM", + "Prespective from geosync","Equidistant cylindrical","Gnomonic", + "Gauss conformal","Lambert conformal conic"}; + +/************************************************************************/ +/* ==================================================================== */ +/* IRISRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class IRISRasterBand : public GDALPamRasterBand +{ + friend class IRISDataset; + + + unsigned char* pszRecord; + int bBufferAllocFailed; + + public: + IRISRasterBand( IRISDataset *, int ); + ~IRISRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + + virtual double GetNoDataValue( int * ); + virtual CPLErr SetNoDataValue( double ); +}; + + +/************************************************************************/ +/* IRISRasterBand() */ +/************************************************************************/ + +IRISRasterBand::IRISRasterBand( IRISDataset *poDS, int nBand ) +{ + this->poDS = poDS; + this->nBand = nBand; + eDataType = GDT_Float32; + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + pszRecord = NULL; + bBufferAllocFailed = FALSE; +} + +IRISRasterBand::~IRISRasterBand() +{ + VSIFree(pszRecord); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr IRISRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) + +{ + IRISDataset *poGDS = (IRISDataset *) poDS; + + //Every product type has it's own size. TODO: Move it like dataType + int nDataLength = 1; + if(poGDS->nDataTypeCode == 2){nDataLength=1;} + else if(poGDS->nDataTypeCode == 37){nDataLength=2;} + else if(poGDS->nDataTypeCode == 33){nDataLength=2;} + else if(poGDS->nDataTypeCode == 32){nDataLength=1;} + + int i; + //We allocate space for storing a record: + if (pszRecord == NULL) + { + if (bBufferAllocFailed) + return CE_Failure; + + pszRecord = (unsigned char *) VSIMalloc(nBlockXSize*nDataLength); + + if (pszRecord == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate scanline buffer"); + bBufferAllocFailed = TRUE; + return CE_Failure; + } + } + + //Prepare to read (640 is the header size in bytes) and read (the y axis in the IRIS files in the inverse direction) + //The previous bands are also added as an offset + + VSIFSeekL( poGDS->fp, 640 + (vsi_l_offset)nDataLength*poGDS->GetRasterXSize()*poGDS->GetRasterYSize()*(this->nBand-1) + + (vsi_l_offset)nBlockXSize*nDataLength*(poGDS->GetRasterYSize()-1-nBlockYOff), SEEK_SET ); + + if( (int)VSIFReadL( pszRecord, nBlockXSize*nDataLength, 1, poGDS->fp ) != 1 ) + return CE_Failure; + + //If datatype is dbZ or dBT: + //See point 3.3.3 at page 3.33 of the manual + if(poGDS->nDataTypeCode == 2 || poGDS->nDataTypeCode == 1){ + float fVal; + for (i=0;inDataTypeCode == 8 || poGDS->nDataTypeCode == 9){ + float fVal; + for (i=0;inDataTypeCode == 37){ + unsigned short nVal, nExp, nMantissa; + float fVal2=0; + for (i=0;i>12; + nMantissa = nVal - (nExp<<12); + if (nVal == 65535) + fVal2 = -9999; + else if (nExp == 0) + fVal2 = (float) nMantissa / 1000.0; + else + fVal2 = (float)((nMantissa+4096)<<(nExp-1))/1000.0; + ((float *) pImage)[i] = fVal2; + } + //VIL2 (VIL products) + //See point 3.3.41 at page 3.54 of the manual + } else if(poGDS->nDataTypeCode == 33){ + float fVal; + for (i=0;inDataTypeCode == 32){ + unsigned char nVal; + for (i=0;inDataTypeCode == 3){ + float fVal; + for (i=0;ifNyquistVelocity * (fVal - 128)/127; + ((float *) pImage)[i] = fVal; + } + //SHEAR (1-Byte Shear) + //See point 3.3.23 at page 3.39 of the manual + } else if(poGDS->nDataTypeCode == 35){ + float fVal; + for (i=0;ibNoDataSet && poGDS->dfNoDataValue == dfNoData ) + // return CE_None; + + + poGDS->bNoDataSet = TRUE; + poGDS->dfNoDataValue = dfNoData; + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double IRISRasterBand::GetNoDataValue( int * pbSuccess ) + +{ + IRISDataset *poGDS = (IRISDataset *) poDS; + + + if( poGDS->bNoDataSet ) + { + if( pbSuccess ) + *pbSuccess = TRUE; + + return poGDS->dfNoDataValue; + } + + return GDALPamRasterBand::GetNoDataValue( pbSuccess ); +} + + +/************************************************************************/ +/* ==================================================================== */ +/* IRISDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* IRISDataset() */ +/************************************************************************/ + +IRISDataset::IRISDataset() + +{ + bHasLoadedProjection = FALSE; + fp = NULL; + pszSRS_WKT = NULL; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~IRISDataset() */ +/************************************************************************/ + +IRISDataset::~IRISDataset() + +{ + FlushCache(); + if( fp != NULL ) + VSIFCloseL( fp ); + CPLFree( pszSRS_WKT ); +} + +/************************************************************************/ +/* Calculates the projection and Geotransform */ +/************************************************************************/ +void IRISDataset::LoadProjection() +{ + bHasLoadedProjection = TRUE; + float fEquatorialRadius = float( (CPL_LSBUINT32PTR (abyHeader+220+320+12)))/100; //They give it in cm + float fInvFlattening = float( (CPL_LSBUINT32PTR (abyHeader+224+320+12)))/1000000; //Point 3.2.27 pag 3-15 + float fFlattening; + float fPolarRadius; + + if(fEquatorialRadius == 0){ // if Radius is 0, change to 6371000 Point 3.2.27 pag 3-15 (old IRIS verions) + fEquatorialRadius = 6371000; + fPolarRadius = fEquatorialRadius; + fInvFlattening = 0; + fFlattening = 0; + } else { + if (fInvFlattening == 0){ //When inverse flattening is infinite, they use 0 + fFlattening = 0; + fPolarRadius = fEquatorialRadius; + } else { + fFlattening = 1/fInvFlattening; + fPolarRadius = fEquatorialRadius * (1-fFlattening); + } + } + + float fCenterLon = 360 * float((CPL_LSBUINT32PTR (abyHeader+112+320+12))) / 4294967295LL; + float fCenterLat = 360 * float((CPL_LSBUINT32PTR (abyHeader+108+320+12))) / 4294967295LL; + + float fProjRefLon = 360 * float((CPL_LSBUINT32PTR (abyHeader+244+320+12))) / 4294967295LL; + float fProjRefLat = 360 * float((CPL_LSBUINT32PTR (abyHeader+240+320+12))) / 4294967295LL; + + float fRadarLocX, fRadarLocY, fScaleX, fScaleY; + + fRadarLocX = float (CPL_LSBSINT32PTR (abyHeader + 112 + 12 )) / 1000; + fRadarLocY = float (CPL_LSBSINT32PTR (abyHeader + 116 + 12 )) / 1000; + + fScaleX = float (CPL_LSBSINT32PTR (abyHeader + 88 + 12 )) / 100; + fScaleY = float (CPL_LSBSINT32PTR (abyHeader + 92 + 12 )) / 100; + + OGRSpatialReference oSRSOut; + + ////MERCATOR PROJECTION + if(EQUAL(aszProjections[nProjectionCode],"Mercator")){ + OGRCoordinateTransformation *poTransform = NULL; + OGRSpatialReference oSRSLatLon; + + oSRSOut.SetGeogCS("unnamed ellipse", + "unknown", + "unnamed", + fEquatorialRadius, fInvFlattening, + "Greenwich", 0.0, + "degree", 0.0174532925199433); + + oSRSOut.SetMercator(fProjRefLat,fProjRefLon,1,0,0); + oSRSOut.exportToWkt(&pszSRS_WKT); + + //The center coordinates are given in LatLon on the defined ellipsoid. Necessary to calculate geotransform. + + oSRSLatLon.SetGeogCS("unnamed ellipse", + "unknown", + "unnamed", + fEquatorialRadius, fInvFlattening, + "Greenwich", 0.0, + "degree", 0.0174532925199433); + + poTransform = OGRCreateCoordinateTransformation( &oSRSLatLon, + &oSRSOut ); + std::pair oPositionX2 = GeodesicCalculation(fCenterLat, fCenterLon, 90, fScaleX, fEquatorialRadius, fPolarRadius, fFlattening); + std::pair oPositionY2 = GeodesicCalculation(fCenterLat, fCenterLon, 0, fScaleY, fEquatorialRadius, fPolarRadius, fFlattening); + + double dfLon2, dfLat2; + dfLon2 = oPositionX2.first; + dfLat2 = oPositionY2.second; + double dfX, dfY, dfX2, dfY2; + dfX = fCenterLon ; + dfY = fCenterLat ; + dfX2 = dfLon2; + dfY2 = dfLat2; + + if( poTransform == NULL || !poTransform->Transform( 1, &dfX, &dfY ) ) + CPLError( CE_Failure, CPLE_None, "Transformation Failed\n" ); + + if( poTransform == NULL || !poTransform->Transform( 1, &dfX2, &dfY2 ) ) + CPLError( CE_Failure, CPLE_None, "Transformation Failed\n" ); + + adfGeoTransform[0] = dfX - (fRadarLocX * (dfX2 - dfX)); + adfGeoTransform[1] = dfX2 - dfX; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = dfY + (fRadarLocY * (dfY2 - dfY)); + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = -1*(dfY2 - dfY); + + delete poTransform; + + }else if(EQUAL(aszProjections[nProjectionCode],"Azimutal equidistant")){ + + oSRSOut.SetGeogCS("unnamed ellipse", + "unknown", + "unnamed", + fEquatorialRadius, fInvFlattening, + "Greenwich", 0.0, + "degree", 0.0174532925199433); + oSRSOut.SetAE(fProjRefLat,fProjRefLon,0,0); + oSRSOut.exportToWkt(&pszSRS_WKT) ; + adfGeoTransform[0] = -1*(fRadarLocX*fScaleX); + adfGeoTransform[1] = fScaleX; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = fRadarLocY*fScaleY; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = -1*fScaleY; + //When the projection is different from Mercator or Azimutal equidistant, we set a standard geotransform + } else { + adfGeoTransform[0] = -1*(fRadarLocX*fScaleX); + adfGeoTransform[1] = fScaleX; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = fRadarLocY*fScaleY; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = -1*fScaleY; + } + +} + +/******************************************************************************/ +/* The geotransform in Mercator projection must be calculated transforming */ +/* distance to degrees over the ellipsoid, using Vincenty's formula. */ +/* The following method is ported from a version for Javascript by Chris */ +/* Veness distributed under a CC-BY 3.0 licence, whose conditions is that the */ +/* following copyright notice is retained as well as the link to : */ +/* http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html */ +/******************************************************************************/ + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Vincenty Direct Solution of Geodesics on the Ellipsoid (c) Chris Veness 2005-2012 */ +/* */ +/* from: Vincenty direct formula - T Vincenty, "Direct and Inverse Solutions of Geodesics on the */ +/* Ellipsoid with application of nested equations", Survey Review, vol XXII no 176, 1975 */ +/* http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +std::pair IRISDataset::GeodesicCalculation(float fLat, float fLon, float fAngle, float fDist, float fEquatorialRadius, float fPolarRadius, float fFlattening) +{ + std::pair oOutput; + double dfAlpha1 = DEG2RAD * fAngle; + double dfSinAlpha1 = sin(dfAlpha1); + double dfCosAlpha1 = cos(dfAlpha1); + + double dfTanU1 = (1-fFlattening) * tan(fLat*DEG2RAD); + double dfCosU1 = 1 / sqrt((1 + dfTanU1*dfTanU1)); + double dfSinU1 = dfTanU1*dfCosU1; + + double dfSigma1 = atan2(dfTanU1, dfCosAlpha1); + double dfSinAlpha = dfCosU1 * dfSinAlpha1; + double dfCosSqAlpha = 1 - dfSinAlpha*dfSinAlpha; + double dfUSq = dfCosSqAlpha * (fEquatorialRadius*fEquatorialRadius - fPolarRadius*fPolarRadius) / (fPolarRadius*fPolarRadius); + double dfA = 1 + dfUSq/16384*(4096+dfUSq*(-768+dfUSq*(320-175*dfUSq))); + double dfB = dfUSq/1024 * (256+dfUSq*(-128+dfUSq*(74-47*dfUSq))); + + double dfSigma = fDist / (fPolarRadius*dfA); + double dfSigmaP = 2*M_PI; + + double dfSinSigma = 0.0; + double dfCosSigma = 0.0; + double dfCos2SigmaM = 0.0; + double dfDeltaSigma; + + while (fabs(dfSigma-dfSigmaP) > 1e-12) { + dfCos2SigmaM = cos(2*dfSigma1 + dfSigma); + dfSinSigma = sin(dfSigma); + dfCosSigma = cos(dfSigma); + dfDeltaSigma = dfB*dfSinSigma*(dfCos2SigmaM+dfB/4*(dfCosSigma*(-1+2*dfCos2SigmaM*dfCos2SigmaM)- + dfB/6*dfCos2SigmaM*(-3+4*dfSinSigma*dfSinSigma)*(-3+4*dfCos2SigmaM*dfCos2SigmaM))); + dfSigmaP = dfSigma; + dfSigma = fDist / (fPolarRadius*dfA) + dfDeltaSigma; + } + + double dfTmp = dfSinU1*dfSinSigma - dfCosU1*dfCosSigma*dfCosAlpha1; + double dfLat2 = atan2(dfSinU1*dfCosSigma + dfCosU1*dfSinSigma*dfCosAlpha1, + (1-fFlattening)*sqrt(dfSinAlpha*dfSinAlpha + dfTmp*dfTmp)); + double dfLambda = atan2(dfSinSigma*dfSinAlpha1, dfCosU1*dfCosSigma - dfSinU1*dfSinSigma*dfCosAlpha1); + double dfC = fFlattening/16*dfCosSqAlpha*(4+fFlattening*(4-3*dfCosSqAlpha)); + double dfL = dfLambda - (1-dfC) * fFlattening * dfSinAlpha * + (dfSigma + dfC*dfSinSigma*(dfCos2SigmaM+dfC*dfCosSigma*(-1+2*dfCos2SigmaM*dfCos2SigmaM))); + double dfLon2 = fLon*DEG2RAD+dfL; + if (dfLon2 > M_PI) + dfLon2 = dfLon2 - 2*M_PI; + if (dfLon2 < -1*M_PI) + dfLon2 = dfLon2 + 2*M_PI; + oOutput.first = dfLon2*RAD2DEG; + oOutput.second = dfLat2*RAD2DEG; + + return oOutput; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr IRISDataset::GetGeoTransform( double * padfTransform ) + +{ + if (!bHasLoadedProjection) + LoadProjection(); + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *IRISDataset::GetProjectionRef(){ + if (!bHasLoadedProjection) + LoadProjection(); + return pszSRS_WKT; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int IRISDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + +/* -------------------------------------------------------------------- */ +/* Confirm that the file is an IRIS file */ +/* -------------------------------------------------------------------- */ + //Si no el posem, peta al fer el translate, quan s'obre Identify des de GDALIdentifyDriver + if( poOpenInfo->nHeaderBytes < 640 ) + return FALSE; + + + short nId1 = CPL_LSBSINT16PTR(poOpenInfo->pabyHeader); + short nId2 = CPL_LSBSINT16PTR(poOpenInfo->pabyHeader+12); + unsigned short nType = CPL_LSBUINT16PTR (poOpenInfo->pabyHeader+24); + + /*Check if the two headers are 27 (product hdr) & 26 (product configuration), and the product type is in the range 1 -> 34*/ + if( !(nId1 == 27 && nId2 == 26 && nType > 0 && nType < 35) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* FillString() */ +/************************************************************************/ + +static void FillString(char* szBuffer, size_t nBufferSize, void* pSrcBuffer) +{ + for(size_t i = 0; i < nBufferSize - 1; i++) + szBuffer[i] = ((char*)pSrcBuffer)[i]; + szBuffer[nBufferSize-1] = '\0'; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *IRISDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if (!Identify(poOpenInfo)) + return NULL; +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The IRIS driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + IRISDataset *poDS; + + poDS = new IRISDataset(); + + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + if (poDS->fp == NULL) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + VSIFReadL( poDS->abyHeader, 1, 640, poDS->fp ); + int nXSize = CPL_LSBSINT32PTR(poDS->abyHeader+100+12); + int nYSize = CPL_LSBSINT32PTR(poDS->abyHeader+104+12); + int nNumBands = CPL_LSBSINT32PTR(poDS->abyHeader+108+12); + + poDS->nRasterXSize = nXSize; + + poDS->nRasterYSize = nYSize; + if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid dimensions : %d x %d", + poDS->nRasterXSize, poDS->nRasterYSize); + delete poDS; + return NULL; + } + + if( !GDALCheckBandCount(nNumBands, TRUE) ) + { + delete poDS; + return NULL; + } + + + +/* -------------------------------------------------------------------- */ +/* Setting the Metadata */ +/* -------------------------------------------------------------------- */ + //See point 3.2.26 at page 3.12 of the manual + poDS->nProductCode = CPL_LSBUINT16PTR (poDS->abyHeader+12+12); + poDS->SetMetadataItem( "PRODUCT_ID", CPLString().Printf("%d", poDS->nProductCode )); + if( poDS->nProductCode >= ARRAY_ELEMENT_COUNT(poDS->aszProductNames) ) + { + delete poDS; + return NULL; + } + + poDS->SetMetadataItem( "PRODUCT",poDS->aszProductNames[poDS->nProductCode]); + + poDS->nDataTypeCode = CPL_LSBUINT16PTR (poDS->abyHeader+130+12); + if( poDS->nDataTypeCode >= ARRAY_ELEMENT_COUNT(poDS->aszDataTypeCodes) ) + { + delete poDS; + return NULL; + } + poDS->SetMetadataItem( "DATA_TYPE_CODE",poDS->aszDataTypeCodes[poDS->nDataTypeCode]); + + if( poDS->nDataTypeCode >= ARRAY_ELEMENT_COUNT(poDS->aszDataTypes) ) + { + delete poDS; + return NULL; + } + poDS->SetMetadataItem( "DATA_TYPE",poDS->aszDataTypes[poDS->nDataTypeCode]); + + unsigned short nDataTypeInputCode = CPL_LSBUINT16PTR (poDS->abyHeader+144+12); + if( nDataTypeInputCode >= ARRAY_ELEMENT_COUNT(poDS->aszDataTypeCodes) ) + { + delete poDS; + return NULL; + } + poDS->SetMetadataItem( "DATA_TYPE_INPUT_CODE",poDS->aszDataTypeCodes[nDataTypeInputCode]); + + unsigned short nDataTypeInput = CPL_LSBUINT16PTR (poDS->abyHeader+144+12); + if( nDataTypeInput >= ARRAY_ELEMENT_COUNT(poDS->aszDataTypes) ) + { + delete poDS; + return NULL; + } + poDS->SetMetadataItem( "DATA_TYPE_INPUT",poDS->aszDataTypes[nDataTypeInput]); + + poDS->nProjectionCode = * (unsigned char *) (poDS->abyHeader+146+12); + if( poDS->nProjectionCode >= ARRAY_ELEMENT_COUNT(poDS->aszProjections) ) + { + delete poDS; + return NULL; + } + + ////TIMES + int nSeconds = CPL_LSBSINT32PTR(poDS->abyHeader+20+12); + + int nHour = (nSeconds - (nSeconds%3600)) /3600; + int nMinute = ((nSeconds - nHour * 3600) - (nSeconds - nHour * 3600)%60)/ 60; + int nSecond = nSeconds - nHour * 3600 - nMinute * 60; + + short nYear = CPL_LSBSINT16PTR(poDS->abyHeader+26+12); + short nMonth = CPL_LSBSINT16PTR(poDS->abyHeader+28+12); + short nDay = CPL_LSBSINT16PTR(poDS->abyHeader+30+12); + + poDS->SetMetadataItem( "TIME_PRODUCT_GENERATED", CPLString().Printf("%d-%02d-%02d %02d:%02d:%02d", nYear, nMonth, nDay, nHour, nMinute, nSecond ) ); + + + nSeconds = CPL_LSBSINT32PTR(poDS->abyHeader+32+12); + + nHour = (nSeconds - (nSeconds%3600)) /3600; + nMinute = ((nSeconds - nHour * 3600) - (nSeconds - nHour * 3600)%60)/ 60; + nSecond = nSeconds - nHour * 3600 - nMinute * 60; + + nYear = CPL_LSBSINT16PTR(poDS->abyHeader+26+12); + nMonth = CPL_LSBSINT16PTR(poDS->abyHeader+28+12); + nDay = CPL_LSBSINT16PTR(poDS->abyHeader+30+12); + + poDS->SetMetadataItem( "TIME_INPUT_INGEST_SWEEP", CPLString().Printf("%d-%02d-%02d %02d:%02d:%02d", nYear, nMonth, nDay, nHour, nMinute, nSecond ) ); + + ///Site and task information + + char szSiteName[17] = ""; //Must have one extra char for string end! + char szVersionName[9] = ""; + + FillString(szSiteName, sizeof(szSiteName), poDS->abyHeader+320+12); + FillString(szVersionName, sizeof(szVersionName), poDS->abyHeader+16+320+12); + poDS->SetMetadataItem( "PRODUCT_SITE_NAME",szSiteName); + poDS->SetMetadataItem( "PRODUCT_SITE_IRIS_VERSION",szVersionName); + + FillString(szSiteName, sizeof(szSiteName), poDS->abyHeader+90+320+12); + FillString(szVersionName, sizeof(szVersionName), poDS->abyHeader+24+320+12); + poDS->SetMetadataItem( "INGEST_SITE_NAME",szSiteName); + poDS->SetMetadataItem( "INGEST_SITE_IRIS_VERSION",szVersionName); + + FillString(szSiteName, sizeof(szSiteName), poDS->abyHeader+74+320+12); + poDS->SetMetadataItem( "INGEST_HARDWARE_NAME",szSiteName); + + char szConfigFile[13] = ""; + FillString(szConfigFile, sizeof(szConfigFile), poDS->abyHeader+62+12); + poDS->SetMetadataItem( "PRODUCT_CONFIGURATION_NAME",szConfigFile); + + char szTaskName[13] = ""; + FillString(szTaskName, sizeof(szTaskName), poDS->abyHeader+74+12); + poDS->SetMetadataItem( "TASK_NAME",szTaskName); + + short nRadarHeight = CPL_LSBSINT16PTR(poDS->abyHeader+284+320+12); + poDS->SetMetadataItem( "RADAR_HEIGHT",CPLString().Printf("%d m",nRadarHeight)); + short nGroundHeight = CPL_LSBSINT16PTR(poDS->abyHeader+118+320+12); + poDS->SetMetadataItem( "GROUND_HEIGHT",CPLString().Printf("%d m",nRadarHeight-nGroundHeight)); //Ground height over the sea level + + unsigned short nFlags = CPL_LSBUINT16PTR (poDS->abyHeader+86+12); + //Get eleventh bit + nFlags=nFlags<<4; + nFlags=nFlags>>15; + if (nFlags == 1){ + poDS->SetMetadataItem( "COMPOSITED_PRODUCT","YES"); + unsigned int compositedMask = CPL_LSBUINT32PTR (poDS->abyHeader+232+320+12); + poDS->SetMetadataItem( "COMPOSITED_PRODUCT_MASK",CPLString().Printf("0x%08x",compositedMask)); + } else{ + poDS->SetMetadataItem( "COMPOSITED_PRODUCT","NO"); + } + + //Wave values + poDS->SetMetadataItem( "PRF",CPLString().Printf("%d Hz",CPL_LSBSINT32PTR(poDS->abyHeader+120+320+12))); + poDS->SetMetadataItem( "WAVELENGTH",CPLString().Printf("%4.2f cm",(float) CPL_LSBSINT32PTR(poDS->abyHeader+148+320+12)/100)); + unsigned short nPolarizationType = CPL_LSBUINT16PTR (poDS->abyHeader+172+320+12); + float fNyquist = (CPL_LSBSINT32PTR(poDS->abyHeader+120+320+12))*((float) CPL_LSBSINT32PTR(poDS->abyHeader+148+320+12)/10000)/4; //See section 3.3.37 & 3.2.54 + if (nPolarizationType == 1) + fNyquist = fNyquist * 2; + else if(nPolarizationType == 2) + fNyquist = fNyquist * 3; + else if(nPolarizationType == 3) + fNyquist = fNyquist * 4; + poDS->fNyquistVelocity = fNyquist; + poDS->SetMetadataItem( "NYQUIST_VELOCITY",CPLString().Printf("%.2f m/s",fNyquist)); + + ///Product dependent metadata (stored in 80 bytes fromm 162 bytes at the product header) See point 3.2.30 at page 3.19 of the manual + //See point 3.2.25 at page 3.12 of the manual + if (EQUAL(poDS->aszProductNames[poDS->nProductCode],"PPI")){ + //Degrees = 360 * (Binary Angle)*2^N + //float fElevation = 360 * float((CPL_LSBUINT16PTR (poDS->abyHeader+164+12))) / 65536; + float fElevation = 360 * float((CPL_LSBSINT16PTR (poDS->abyHeader+164+12))) / 65536; + + poDS->SetMetadataItem( "PPI_ELEVATION_ANGLE",CPLString().Printf("%f",fElevation)); + if (EQUAL(poDS->aszDataTypeCodes[poDS->nDataTypeCode],"dBZ")) + poDS->SetMetadataItem( "DATA_TYPE_UNITS","dBZ"); + else + poDS->SetMetadataItem( "DATA_TYPE_UNITS","m/s"); + //See point 3.2.2 at page 3.2 of the manual + } else if (EQUAL(poDS->aszProductNames[poDS->nProductCode],"CAPPI")){ + float fElevation = ((float) CPL_LSBSINT32PTR(poDS->abyHeader+4+164+12))/100; + poDS->SetMetadataItem( "CAPPI_BOTTOM_HEIGHT",CPLString().Printf("%.1f m",fElevation)); + float fAzimuthSmoothingForShear = 360 * float((CPL_LSBUINT16PTR (poDS->abyHeader+10+164+12))) / 65536; + poDS->SetMetadataItem( "AZIMUTH_SMOOTHING_FOR_SHEAR" ,CPLString().Printf("%.1f", fAzimuthSmoothingForShear)); + unsigned int nMaxAgeVVPCorrection = CPL_LSBUINT32PTR (poDS->abyHeader+24+164+12); + poDS->SetMetadataItem( "MAX_AGE_FOR_SHEAR_VVP_CORRECTION" ,CPLString().Printf("%d s", nMaxAgeVVPCorrection)); + if (EQUAL(poDS->aszDataTypeCodes[poDS->nDataTypeCode],"dBZ")) + poDS->SetMetadataItem( "DATA_TYPE_UNITS","dBZ"); + else + poDS->SetMetadataItem( "DATA_TYPE_UNITS","m/s"); + //See point 3.2.32 at page 3.19 of the manual + } else if (EQUAL(poDS->aszProductNames[poDS->nProductCode],"RAIN1") || EQUAL(poDS->aszProductNames[poDS->nProductCode],"RAINN")){ + short nNumProducts = CPL_LSBSINT16PTR(poDS->abyHeader+170+320+12); + poDS->SetMetadataItem( "NUM_FILES_USED",CPLString().Printf("%d",nNumProducts)); + + float fMinZAcum= (float)((CPL_LSBUINT32PTR (poDS->abyHeader+164+12))-32768)/1000; + poDS->SetMetadataItem( "MINIMUM_Z_TO_ACUMULATE",CPLString().Printf("%f",fMinZAcum)); + + unsigned short nSecondsOfAccumulation = CPL_LSBUINT16PTR (poDS->abyHeader+6+164+12); + poDS->SetMetadataItem( "SECONDS_OF_ACCUMULATION",CPLString().Printf("%d s",nSecondsOfAccumulation)); + + unsigned int nSpanInputFiles = CPL_LSBUINT32PTR (poDS->abyHeader+24+164+12); + poDS->SetMetadataItem( "SPAN_OF_INPUT_FILES",CPLString().Printf("%d s",nSpanInputFiles)); + poDS->SetMetadataItem( "DATA_TYPE_UNITS","mm"); + + char szInputProductName[13] = ""; + for(int k=0; k<12;k++) + szInputProductName[k] = * (char *) (poDS->abyHeader+k+12+164+12); + poDS->SetMetadataItem( "INPUT_PRODUCT_NAME",CPLString().Printf("%s",szInputProductName)); + + if (EQUAL(poDS->aszProductNames[poDS->nProductCode],"RAINN")) + poDS->SetMetadataItem( "NUM_HOURS_ACCUMULATE",CPLString().Printf("%d",CPL_LSBUINT16PTR (poDS->abyHeader+10+164+12))); + + //See point 3.2.73 at page 3.36 of the manual + } else if (EQUAL(poDS->aszProductNames[poDS->nProductCode],"VIL")){ + float fBottomHeigthInterval = (float) CPL_LSBSINT32PTR(poDS->abyHeader+4+164+12) / 100; + poDS->SetMetadataItem( "BOTTOM_OF_HEIGTH_INTERVAL",CPLString().Printf("%.1f m",fBottomHeigthInterval)); + float fTopHeigthInterval = (float) CPL_LSBSINT32PTR(poDS->abyHeader+8+164+12) / 100; + poDS->SetMetadataItem( "TOP_OF_HEIGTH_INTERVAL",CPLString().Printf("%.1f m",fTopHeigthInterval)); + poDS->SetMetadataItem( "VIL_DENSITY_NOT_AVAILABLE_VALUE","-1"); + poDS->SetMetadataItem( "DATA_TYPE_UNITS","mm"); + //See point 3.2.68 at page 3.36 of the manual + } else if (EQUAL(poDS->aszProductNames[poDS->nProductCode],"TOPS")){ + float fZThreshold = (float) CPL_LSBSINT16PTR(poDS->abyHeader+4+164+12) / 16; + poDS->SetMetadataItem( "Z_THRESHOLD",CPLString().Printf("%.1f dBZ",fZThreshold)); + poDS->SetMetadataItem( "ECHO_TOPS_NOT_AVAILABLE_VALUE","-1"); + poDS->SetMetadataItem( "DATA_TYPE_UNITS","km"); + //See point 3.2.20 at page 3.10 of the manual + } else if (EQUAL(poDS->aszProductNames[poDS->nProductCode],"MAX")){ + float fBottomInterval = (float) CPL_LSBSINT32PTR(poDS->abyHeader+4+164+12) / 100; + poDS->SetMetadataItem( "BOTTOM_OF_INTERVAL",CPLString().Printf("%.1f m",fBottomInterval)); + float fTopInterval = (float) CPL_LSBSINT32PTR(poDS->abyHeader+8+164+12) / 100; + poDS->SetMetadataItem( "TOP_OF_INTERVAL",CPLString().Printf("%.1f m",fTopInterval)); + int nNumPixelsSidePanels = CPL_LSBSINT32PTR(poDS->abyHeader+12+164+12); + poDS->SetMetadataItem( "NUM_PIXELS_SIDE_PANELS",CPLString().Printf("%d",nNumPixelsSidePanels)); + short nHorizontalSmootherSidePanels = CPL_LSBSINT16PTR(poDS->abyHeader+16+164+12); + poDS->SetMetadataItem( "HORIZONTAL_SMOOTHER_SIDE_PANELS",CPLString().Printf("%d",nHorizontalSmootherSidePanels)); + short nVerticalSmootherSidePanels = CPL_LSBSINT16PTR(poDS->abyHeader+18+164+12); + poDS->SetMetadataItem( "VERTICAL_SMOOTHER_SIDE_PANELS",CPLString().Printf("%d",nVerticalSmootherSidePanels)); + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for (int iBandNum = 1; iBandNum <= nNumBands; iBandNum++) { + poDS->SetBand( iBandNum, new IRISRasterBand( poDS, iBandNum )); + + poDS->GetRasterBand(iBandNum)->SetNoDataValue(-9999); + //Calculating the band height to include it in the band metadata. Only for the CAPPI product + if (EQUAL(poDS->aszProductNames[poDS->nProductCode],"CAPPI")){ + float fScaleZ = float (CPL_LSBSINT32PTR (poDS->abyHeader + 96 + 12 )) / 100; + float fOffset = ((float) CPL_LSBSINT32PTR(poDS->abyHeader+4+164+12))/100; + + poDS->GetRasterBand(iBandNum)->SetMetadataItem("height",CPLString().Printf("%.0f m",fOffset + fScaleZ*(iBandNum-1))); + } + } +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_IRIS() */ +/************************************************************************/ + +void GDALRegister_IRIS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "IRIS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "IRIS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "IRIS data (.PPI, .CAPPi etc)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#IRIS" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "ppi" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = IRISDataset::Open; + poDriver->pfnIdentify = IRISDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/iris/makefile.vc b/bazaar/plugin/gdal/frmts/iris/makefile.vc new file mode 100644 index 000000000..31ec435a9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iris/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = irisdataset.obj + +EXTRAFLAGS = + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/iso8211/8211createfromxml.cpp b/bazaar/plugin/gdal/frmts/iso8211/8211createfromxml.cpp new file mode 100644 index 000000000..012988f14 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/8211createfromxml.cpp @@ -0,0 +1,306 @@ +/****************************************************************************** + * $Id: 8211createfromxml.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: ISO8211 library + * Purpose: Create a 8211 file from a XML dump file generated by "8211dump -xml" + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_minixml.h" +#include "iso8211.h" +#include +#include + +CPL_CVSID("$Id: 8211createfromxml.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +int main(int nArgc, char* papszArgv[]) +{ + const char *pszFilename = NULL, *pszOutFilename = NULL; + DDFModule oModule; + +/* -------------------------------------------------------------------- */ +/* Check arguments. */ +/* -------------------------------------------------------------------- */ + for( int iArg = 1; iArg < nArgc; iArg++ ) + { + if( pszFilename == NULL ) + pszFilename = papszArgv[iArg]; + else if( pszOutFilename == NULL ) + pszOutFilename = papszArgv[iArg]; + else + { + pszFilename = NULL; + break; + } + } + + if( pszFilename == NULL ) + { + printf( "Usage: 8211createfromxml filename.xml outfilename\n" ); + exit( 1 ); + } + + CPLXMLNode* poRoot = CPLParseXMLFile( pszFilename ); + if( poRoot == NULL ) + { + fprintf(stderr, "Cannot parse XML file '%s'\n", pszFilename); + exit( 1 ); + } + + CPLXMLNode* poXMLDDFModule = CPLSearchXMLNode(poRoot, "=DDFModule"); + if( poXMLDDFModule == NULL ) + { + fprintf(stderr, "Cannot find DDFModule node in XML file '%s'\n", pszFilename); + exit( 1 ); + } + + /* Compute the size of the DDFField tag */ + CPLXMLNode* psIter = poXMLDDFModule->psChild; + int nSizeFieldTag = 0; + while( psIter != NULL ) + { + if( psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "DDFFieldDefn") == 0 ) + { + const char* pszTag = CPLGetXMLValue(psIter, "tag", ""); + if( nSizeFieldTag == 0 ) + nSizeFieldTag = (int)strlen(pszTag); + else if( nSizeFieldTag != (int)strlen(pszTag) ) + { + fprintf(stderr, "All fields have not the same tag size\n"); + exit( 1 ); + } + } + psIter = psIter->psNext; + } + + char chInterchangeLevel = '3'; + char chLeaderIden = 'L'; + char chCodeExtensionIndicator = 'E'; + char chVersionNumber = '1'; + char chAppIndicator = ' '; + const char *pszExtendedCharSet = " ! "; + int nSizeFieldLength = 3; + int nSizeFieldPos = 4; + + oModule.Initialize(chInterchangeLevel, + chLeaderIden, + chCodeExtensionIndicator, + chVersionNumber, + chAppIndicator, + pszExtendedCharSet, + nSizeFieldLength, + nSizeFieldPos, + nSizeFieldTag); + + int bCreated = FALSE; + + /* Create DDFFieldDefn and DDFRecord elements */ + psIter = poXMLDDFModule->psChild; + while( psIter != NULL ) + { + if( psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "DDFFieldDefn") == 0 ) + { + DDFFieldDefn* poFDefn = new DDFFieldDefn(); + + DDF_data_struct_code eStructCode = dsc_elementary; + const char* pszStructCode = CPLGetXMLValue(psIter, "dataStructCode", ""); + if( strcmp(pszStructCode, "elementary") == 0 ) eStructCode = dsc_elementary; + else if( strcmp(pszStructCode, "vector") == 0 ) eStructCode = dsc_vector; + else if( strcmp(pszStructCode, "array") == 0 ) eStructCode = dsc_array; + else if( strcmp(pszStructCode, "concatenated") == 0 ) eStructCode = dsc_concatenated; + + DDF_data_type_code eTypeCode = dtc_char_string; + const char* pszTypeCode = CPLGetXMLValue(psIter, "dataTypeCode", ""); + if( strcmp(pszTypeCode, "char_string") == 0 ) eTypeCode = dtc_char_string; + else if( strcmp(pszTypeCode, "implicit_point") == 0 ) eTypeCode = dtc_implicit_point; + else if( strcmp(pszTypeCode, "explicit_point") == 0 ) eTypeCode = dtc_explicit_point; + else if( strcmp(pszTypeCode, "explicit_point_scaled") == 0 ) eTypeCode = dtc_explicit_point_scaled; + else if( strcmp(pszTypeCode, "char_bit_string") == 0 ) eTypeCode = dtc_char_bit_string; + else if( strcmp(pszTypeCode, "bit_string") == 0 ) eTypeCode = dtc_bit_string; + else if( strcmp(pszTypeCode, "mixed_data_type") == 0 ) eTypeCode = dtc_mixed_data_type; + + const char* pszFormatControls = CPLGetXMLValue(psIter, "formatControls", NULL); + if( eStructCode != dsc_elementary ) + pszFormatControls = NULL; + + const char* pszArrayDescr = CPLGetXMLValue(psIter, "arrayDescr", ""); + if( eStructCode == dsc_vector ) + pszArrayDescr = ""; + else if( eStructCode == dsc_array ) + pszArrayDescr = "*"; + + poFDefn->Create( CPLGetXMLValue(psIter, "tag", ""), + CPLGetXMLValue(psIter, "fieldName", ""), + pszArrayDescr, + eStructCode, eTypeCode, + pszFormatControls ); + + CPLXMLNode* psSubIter = psIter->psChild; + while( psSubIter != NULL ) + { + if( psSubIter->eType == CXT_Element && + strcmp(psSubIter->pszValue, "DDFSubfieldDefn") == 0 ) + { + poFDefn->AddSubfield( CPLGetXMLValue(psSubIter, "name", ""), + CPLGetXMLValue(psSubIter, "format", "") ); + } + psSubIter = psSubIter->psNext; + } + + oModule.AddField( poFDefn ); + } + else if( psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "DDFRecord") == 0 ) + { + if( !bCreated ) + { + oModule.Create( pszOutFilename ); + bCreated = TRUE; + } + + DDFRecord *poRec = new DDFRecord( &oModule ); + std::map oMapField; + + CPLXMLNode* psSubIter = psIter->psChild; + while( psSubIter != NULL ) + { + if( psSubIter->eType == CXT_Element && + strcmp(psSubIter->pszValue, "DDFField") == 0 ) + { + DDFField *poField; + const char* pszFieldName = CPLGetXMLValue(psSubIter, "name", ""); + DDFFieldDefn* poFieldDefn = oModule.FindFieldDefn( pszFieldName ); + if( poFieldDefn == NULL ) + { + fprintf(stderr, "Can't find field '%s'\n", pszFieldName ); + exit(1); + } + + int nFieldOcc = oMapField[pszFieldName]; + oMapField[pszFieldName] ++ ; + + poField = poRec->AddField( poFieldDefn ); + const char* pszValue = CPLGetXMLValue(psSubIter, "value", NULL); + if( pszValue != NULL && strncmp(pszValue, "0x", 2) == 0 ) + { + pszValue += 2; + int nDataLen = (int)strlen(pszValue) / 2; + char* pabyData = (char*) malloc(nDataLen); + for(int i=0;i= 'A' && c <= 'F' ) + nHigh = 10 + c - 'A'; + else + nHigh = c - '0'; + c = pszValue[2*i + 1]; + if( c >= 'A' && c <= 'F' ) + nLow = 10 + c - 'A'; + else + nLow = c - '0'; + pabyData[i] = (nHigh << 4) + nLow; + } + poRec->SetFieldRaw( poField, nFieldOcc, (const char *) pabyData, nDataLen ); + free(pabyData); + } + else + { + CPLXMLNode* psSubfieldIter = psSubIter->psChild; + std::map oMapSubfield; + while( psSubfieldIter != NULL ) + { + if( psSubfieldIter->eType == CXT_Element && + strcmp(psSubfieldIter->pszValue, "DDFSubfield") == 0 ) + { + const char* pszSubfieldName = CPLGetXMLValue(psSubfieldIter, "name", ""); + const char* pszSubfieldType = CPLGetXMLValue(psSubfieldIter, "type", ""); + const char* pszSubfieldValue = CPLGetXMLValue(psSubfieldIter, NULL, ""); + int nOcc = oMapSubfield[pszSubfieldName]; + oMapSubfield[pszSubfieldName] ++ ; + if( strcmp(pszSubfieldType, "float") == 0 ) + { + poRec->SetFloatSubfield( pszFieldName, nFieldOcc, pszSubfieldName, nOcc, + CPLAtof(pszSubfieldValue) ); + } + else if( strcmp(pszSubfieldType, "integer") == 0 ) + { + poRec->SetIntSubfield( pszFieldName, nFieldOcc, pszSubfieldName, nOcc, + atoi(pszSubfieldValue) ); + } + else if( strcmp(pszSubfieldType, "string") == 0 ) + { + poRec->SetStringSubfield( pszFieldName, nFieldOcc, pszSubfieldName, nOcc, + pszSubfieldValue ); + } + else if( strcmp(pszSubfieldType, "binary") == 0 && + strncmp(pszSubfieldValue, "0x", 2) == 0 ) + { + pszSubfieldValue += 2; + int nDataLen = (int)strlen(pszSubfieldValue) / 2; + char* pabyData = (char*) malloc(nDataLen); + for(int i=0;i= 'A' && c <= 'F' ) + nHigh = 10 + c - 'A'; + else + nHigh = c - '0'; + c = pszSubfieldValue[2*i + 1]; + if( c >= 'A' && c <= 'F' ) + nLow = 10 + c - 'A'; + else + nLow = c - '0'; + pabyData[i] = (nHigh << 4) + nLow; + } + poRec->SetStringSubfield( pszFieldName, nFieldOcc, pszSubfieldName, nOcc, + pabyData, nDataLen ); + free(pabyData); + } + } + psSubfieldIter = psSubfieldIter->psNext; + } + } + } + psSubIter = psSubIter->psNext; + } + + poRec->Write(); + delete poRec; + } + + psIter = psIter->psNext; + } + + CPLDestroyXMLNode(poRoot); + + oModule.Close(); + + return 0; +} diff --git a/bazaar/plugin/gdal/frmts/iso8211/8211dump.cpp b/bazaar/plugin/gdal/frmts/iso8211/8211dump.cpp new file mode 100644 index 000000000..212ab2394 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/8211dump.cpp @@ -0,0 +1,293 @@ +/****************************************************************************** + * $Id: 8211dump.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: SDTS Translator + * Purpose: Dump 8211 file in verbose form - just a junk program. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "iso8211.h" +#include "cpl_vsi.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: 8211dump.cpp 27044 2014-03-16 23:41:27Z rouault $"); + + +int main( int nArgc, char ** papszArgv ) + +{ + DDFModule oModule; + const char *pszFilename = NULL; + int bFSPTHack = FALSE; + int bXML = FALSE; + +/* -------------------------------------------------------------------- */ +/* Check arguments. */ +/* -------------------------------------------------------------------- */ + for( int iArg = 1; iArg < nArgc; iArg++ ) + { + if( EQUAL(papszArgv[iArg],"-fspt_repeating") ) + bFSPTHack = TRUE; + else if( EQUAL(papszArgv[iArg],"-xml") ) + bXML = TRUE; + else + pszFilename = papszArgv[iArg]; + } + + if( pszFilename == NULL ) + { + printf( "Usage: 8211dump [-xml] [-fspt_repeating] filename\n" ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Open file. */ +/* -------------------------------------------------------------------- */ + if( !oModule.Open( pszFilename ) ) + exit( 1 ); + +/* -------------------------------------------------------------------- */ +/* Apply FSPT hack if required. */ +/* -------------------------------------------------------------------- */ + if( bFSPTHack ) + { + DDFFieldDefn *poFSPT = oModule.FindFieldDefn( "FSPT" ); + + if( poFSPT == NULL ) + fprintf( stderr, + "unable to find FSPT field to set repeating flag.\n" ); + else + poFSPT->SetRepeatingFlag( TRUE ); + } + +/* -------------------------------------------------------------------- */ +/* Dump header, and all records. */ +/* -------------------------------------------------------------------- */ + DDFRecord *poRecord; + if( bXML ) + { + printf("\n"); + + int nFieldDefnCount = oModule.GetFieldCount(); + for( int i = 0; i < nFieldDefnCount; i++ ) + { + DDFFieldDefn* poFieldDefn = oModule.GetField(i); + const char* pszDataStructCode; + switch( poFieldDefn->GetDataStructCode() ) + { + case dsc_elementary: + pszDataStructCode = "elementary"; + break; + + case dsc_vector: + pszDataStructCode = "vector"; + break; + + case dsc_array: + pszDataStructCode = "array"; + break; + + case dsc_concatenated: + pszDataStructCode = "concatenated"; + break; + + default: + pszDataStructCode = "(unknown)"; + break; + } + + const char* pszDataTypeCode; + switch( poFieldDefn->GetDataTypeCode() ) + { + case dtc_char_string: + pszDataTypeCode = "char_string"; + break; + + case dtc_implicit_point: + pszDataTypeCode = "implicit_point"; + break; + + case dtc_explicit_point: + pszDataTypeCode = "explicit_point"; + break; + + case dtc_explicit_point_scaled: + pszDataTypeCode = "explicit_point_scaled"; + break; + + case dtc_char_bit_string: + pszDataTypeCode = "char_bit_string"; + break; + + case dtc_bit_string: + pszDataTypeCode = "bit_string"; + break; + + case dtc_mixed_data_type: + pszDataTypeCode = "mixed_data_type"; + break; + + default: + pszDataTypeCode = "(unknown)"; + break; + } + + printf("\n", + poFieldDefn->GetName(), + poFieldDefn->GetDescription(), + poFieldDefn->GetArrayDescr(), + poFieldDefn->GetFormatControls(), + pszDataStructCode, + pszDataTypeCode); + int nSubfieldCount = poFieldDefn->GetSubfieldCount(); + for( int iSubField = 0; iSubField < nSubfieldCount; iSubField++ ) + { + DDFSubfieldDefn* poSubFieldDefn = poFieldDefn->GetSubfield(iSubField); + printf(" \n", + poSubFieldDefn->GetName(), poSubFieldDefn->GetFormat()); + } + printf("\n"); + } + + for( poRecord = oModule.ReadRecord(); + poRecord != NULL; poRecord = oModule.ReadRecord() ) + { + printf("\n"); + int nFieldCount = poRecord->GetFieldCount(); + for( int iField = 0; iField < nFieldCount; iField++ ) + { + DDFField* poField = poRecord->GetField(iField); + DDFFieldDefn* poDefn = poField->GetFieldDefn(); + const char* pszFieldName = poDefn->GetName(); + printf(" GetRepeatCount() > 1 ) + printf(" repeatCount=\"%d\"", poField->GetRepeatCount()); + int iOffset = 0, nLoopCount; + int nRepeatCount = poField->GetRepeatCount(); + const char* pachData = poField->GetData(); + int nDataSize = poField->GetDataSize(); + if( nRepeatCount == 1 && poDefn->GetSubfieldCount() == 0 ) + { + printf(" value=\"0x"); + for( int i = 0; i < nDataSize - 1; i++ ) + printf( "%02X", pachData[i] ); + printf("\">\n"); + } + else + printf(">\n"); + for( nLoopCount = 0; nLoopCount < nRepeatCount; nLoopCount++ ) + { + for( int iSubField = 0; iSubField < poDefn->GetSubfieldCount(); iSubField++ ) + { + int nBytesConsumed; + DDFSubfieldDefn* poSubFieldDefn = poDefn->GetSubfield(iSubField); + const char* pszSubFieldName = poSubFieldDefn->GetName(); + printf(" GetType(); + const char* pachSubdata = pachData + iOffset; + int nMaxBytes = nDataSize - iOffset; + if( eType == DDFFloat ) + printf("type=\"float\">%f", + poSubFieldDefn->ExtractFloatData( pachSubdata, nMaxBytes, NULL ) ); + else if( eType == DDFInt ) + printf("type=\"integer\">%d", + poSubFieldDefn->ExtractIntData( pachSubdata, nMaxBytes, NULL ) ); + else if( eType == DDFBinaryString ) + { + int nBytes, i; + GByte *pabyBString = (GByte *) + poSubFieldDefn->ExtractStringData( pachSubdata, nMaxBytes, &nBytes ); + + printf( "type=\"binary\">0x" ); + for( i = 0; i < nBytes; i++ ) + printf( "%02X", pabyBString[i] ); + } + else + { + GByte* pabyString = (GByte *)poSubFieldDefn->ExtractStringData( pachSubdata, nMaxBytes, NULL ); + int bBinary = FALSE; + int i; + for( i = 0; pabyString[i] != '\0'; i ++ ) + { + if( pabyString[i] < 32 || pabyString[i] > 127 ) + { + bBinary = TRUE; + break; + } + } + if( bBinary ) + { + printf( "type=\"binary\">0x" ); + for( i = 0; pabyString[i] != '\0'; i ++ ) + printf( "%02X", pabyString[i] ); + } + else + { + char* pszEscaped = CPLEscapeString((const char*)pabyString, -1, CPLES_XML); + printf("type=\"string\">%s", pszEscaped); + CPLFree(pszEscaped); + } + } + printf("\n"); + + poSubFieldDefn->GetDataLength( pachSubdata, nMaxBytes, &nBytesConsumed ); + + iOffset += nBytesConsumed; + } + } + printf(" \n"); + } + printf("\n"); + } + printf("\n"); + } + else + { + oModule.Dump( stdout ); + long nStartLoc; + + nStartLoc = VSIFTellL( oModule.GetFP() ); + for( poRecord = oModule.ReadRecord(); + poRecord != NULL; poRecord = oModule.ReadRecord() ) + { + printf( "File Offset: %ld\n", nStartLoc ); + poRecord->Dump( stdout ); + + nStartLoc = VSIFTellL( oModule.GetFP() ); + } + } + + oModule.Close(); + +#ifdef DBMALLOC + malloc_dump(1); +#endif + +} + + + diff --git a/bazaar/plugin/gdal/frmts/iso8211/8211view.cpp b/bazaar/plugin/gdal/frmts/iso8211/8211view.cpp new file mode 100644 index 000000000..216c5b7d8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/8211view.cpp @@ -0,0 +1,247 @@ +/* **************************************************************************** + * $Id: 8211view.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: SDTS Translator + * Purpose: Example program dumping data in 8211 data to stdout. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "iso8211.h" + +CPL_CVSID("$Id: 8211view.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +static void ViewRecordField( DDFField * poField ); +static int ViewSubfield( DDFSubfieldDefn *poSFDefn, + const char * pachFieldData, + int nBytesRemaining ); + +/* **********************************************************************/ +/* main() */ +/* **********************************************************************/ + +int main( int nArgc, char ** papszArgv ) + +{ + DDFModule oModule; + const char *pszFilename = NULL; + int bFSPTHack = FALSE; + + for( int iArg = 1; iArg < nArgc; iArg++ ) + { + if( EQUAL(papszArgv[iArg],"-fspt_repeating") ) + bFSPTHack = TRUE; + else + pszFilename = papszArgv[iArg]; + } + + if( pszFilename == NULL ) + { + printf( "Usage: 8211view filename\n" ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Open the file. Note that by default errors are reported to */ +/* stderr, so we don't bother doing it ourselves. */ +/* -------------------------------------------------------------------- */ + if( !oModule.Open( pszFilename ) ) + { + exit( 1 ); + } + + if( bFSPTHack ) + { + DDFFieldDefn *poFSPT = oModule.FindFieldDefn( "FSPT" ); + + if( poFSPT == NULL ) + fprintf( stderr, + "unable to find FSPT field to set repeating flag.\n" ); + else + poFSPT->SetRepeatingFlag( TRUE ); + } + +/* -------------------------------------------------------------------- */ +/* Loop reading records till there are none left. */ +/* -------------------------------------------------------------------- */ + DDFRecord *poRecord; + int iRecord = 0; + + while( (poRecord = oModule.ReadRecord()) != NULL ) + { + printf( "Record %d (%d bytes)\n", + ++iRecord, poRecord->GetDataSize() ); + + /* ------------------------------------------------------------ */ + /* Loop over each field in this particular record. */ + /* ------------------------------------------------------------ */ + for( int iField = 0; iField < poRecord->GetFieldCount(); iField++ ) + { + DDFField *poField = poRecord->GetField( iField ); + + ViewRecordField( poField ); + } + } +} + +/* **********************************************************************/ +/* ViewRecordField() */ +/* */ +/* Dump the contents of a field instance in a record. */ +/* **********************************************************************/ + +static void ViewRecordField( DDFField * poField ) + +{ + int nBytesRemaining; + const char *pachFieldData; + DDFFieldDefn *poFieldDefn = poField->GetFieldDefn(); + + // Report general information about the field. + printf( " Field %s: %s\n", + poFieldDefn->GetName(), poFieldDefn->GetDescription() ); + + // Get pointer to this fields raw data. We will move through + // it consuming data as we report subfield values. + + pachFieldData = poField->GetData(); + nBytesRemaining = poField->GetDataSize(); + + /* -------------------------------------------------------- */ + /* Loop over the repeat count for this fields */ + /* subfields. The repeat count will almost */ + /* always be one. */ + /* -------------------------------------------------------- */ + int iRepeat; + + for( iRepeat = 0; iRepeat < poField->GetRepeatCount(); iRepeat++ ) + { + + /* -------------------------------------------------------- */ + /* Loop over all the subfields of this field, advancing */ + /* the data pointer as we consume data. */ + /* -------------------------------------------------------- */ + int iSF; + + for( iSF = 0; iSF < poFieldDefn->GetSubfieldCount(); iSF++ ) + { + DDFSubfieldDefn *poSFDefn = poFieldDefn->GetSubfield( iSF ); + int nBytesConsumed; + + nBytesConsumed = ViewSubfield( poSFDefn, pachFieldData, + nBytesRemaining ); + + nBytesRemaining -= nBytesConsumed; + pachFieldData += nBytesConsumed; + } + } +} + +/* **********************************************************************/ +/* ViewSubfield() */ +/* **********************************************************************/ + +static int ViewSubfield( DDFSubfieldDefn *poSFDefn, + const char * pachFieldData, + int nBytesRemaining ) + +{ + int nBytesConsumed = 0; + + switch( poSFDefn->GetType() ) + { + case DDFInt: + if( poSFDefn->GetBinaryFormat() == DDFSubfieldDefn::UInt ) + printf( " %s = %u\n", + poSFDefn->GetName(), + poSFDefn->ExtractIntData( pachFieldData, nBytesRemaining, + &nBytesConsumed ) ); + else + printf( " %s = %d\n", + poSFDefn->GetName(), + poSFDefn->ExtractIntData( pachFieldData, nBytesRemaining, + &nBytesConsumed ) ); + break; + + case DDFFloat: + printf( " %s = %f\n", + poSFDefn->GetName(), + poSFDefn->ExtractFloatData( pachFieldData, nBytesRemaining, + &nBytesConsumed ) ); + break; + + case DDFString: + printf( " %s = `%s'\n", + poSFDefn->GetName(), + poSFDefn->ExtractStringData( pachFieldData, nBytesRemaining, + &nBytesConsumed ) ); + break; + + case DDFBinaryString: + { + int i; + //rjensen 19-Feb-2002 5 integer variables to decode NAME and LNAM + int vrid_rcnm=0; + int vrid_rcid=0; + int foid_agen=0; + int foid_find=0; + int foid_fids=0; + + GByte *pabyBString = (GByte *) + poSFDefn->ExtractStringData( pachFieldData, nBytesRemaining, + &nBytesConsumed ); + + printf( " %s = 0x", poSFDefn->GetName() ); + for( i = 0; i < MIN(nBytesConsumed,24); i++ ) + printf( "%02X", pabyBString[i] ); + + if( nBytesConsumed > 24 ) + printf( "%s", "..." ); + + // rjensen 19-Feb-2002 S57 quick hack. decode NAME and LNAM bitfields + if ( EQUAL(poSFDefn->GetName(),"NAME") ) + { + vrid_rcnm=pabyBString[0]; + vrid_rcid=pabyBString[1] + (pabyBString[2]*256)+ + (pabyBString[3]*65536)+ (pabyBString[4]*16777216); + printf("\tVRID RCNM = %d,RCID = %u",vrid_rcnm,vrid_rcid); + } + else if ( EQUAL(poSFDefn->GetName(),"LNAM") ) + { + foid_agen=pabyBString[0] + (pabyBString[1]*256); + foid_find=pabyBString[2] + (pabyBString[3]*256)+ + (pabyBString[4]*65536)+ (pabyBString[5]*16777216); + foid_fids=pabyBString[6] + (pabyBString[7]*256); + printf("\tFOID AGEN = %u,FIDN = %u,FIDS = %u", + foid_agen,foid_find,foid_fids); + } + + printf( "\n" ); + } + break; + + } + + return nBytesConsumed; +} diff --git a/bazaar/plugin/gdal/frmts/iso8211/Doxyfile b/bazaar/plugin/gdal/frmts/iso8211/Doxyfile new file mode 100644 index 000000000..773cdd23a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/Doxyfile @@ -0,0 +1,255 @@ +# This file describes the settings to be used by doxygen for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of word surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = ISO8211Lib + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each page. A value of NO (the default) enables the index and the +# value YES disables it. + +DISABLE_INDEX = NO + +# If the EXTRACT_ALL tag is set to YES all classes and functions will be +# included in the documentation, even if no documentation was available. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members inside documented classes or files. + +HIDE_UNDOC_MEMBERS = YES + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output + +GENERATE_HTML = YES + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the FULL_PATH_NAMES tag is set to YES Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used + +FULL_PATH_NAMES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +FILE_PATTERNS = *.h *.cpp *.c *.dox + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = . + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. + +MACRO_EXPANSION = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). In the former case 1 is used as the +# definition. + +PREDEFINED = + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED tag. + +EXPAND_ONLY_PREDEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/local/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = /usr/local/bin/ + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/bazaar/plugin/gdal/frmts/iso8211/GNUmakefile b/bazaar/plugin/gdal/frmts/iso8211/GNUmakefile new file mode 100644 index 000000000..749e7605e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/GNUmakefile @@ -0,0 +1,88 @@ + +include ../../GDALmake.opt + +VERSION = 1.4 +DISTDIR = iso8211lib-$(VERSION) +WEB_DIR = /u/www/projects/iso8211 + +CPPFLAGS := -I. -I../../port $(CPPFLAGS) + +ISOLIB = libiso8211.a +OBJ = ddfmodule.o ddfutils.o ddffielddefn.o ddfrecord.o ddffield.o \ + ddfsubfielddefn.o + +default: $(ISOLIB) + +all: $(ISOLIB) 8211dump$(EXE) 8211view$(EXE) 8211createfromxml$(EXE) mkcatalog$(EXE) docs + +clean: + rm -rf *.o 8211dump$(EXE) 8211view$(EXE) 8211createfromxml$(EXE) $(DISTDIR) $(DISTDIR).tar.gz html/* \ + $(ISOLIB) + +dist-clean: clean + rm -rf $(DISTDIR) + +$(ISOLIB): $(OBJ:.o=.$(OBJ_EXT)) + $(AR) r $(ISOLIB) $? + $(RANLIB) $(ISOLIB) + +8211createfromxml$(EXE): 8211createfromxml.$(OBJ_EXT) + $(LD) $(LDFLAGS) 8211createfromxml.$(OBJ_EXT) $(CONFIG_LIBS) -o 8211createfromxml$(EXE) + +8211dump$(EXE): 8211dump.$(OBJ_EXT) + $(LD) $(LDFLAGS) 8211dump.$(OBJ_EXT) $(CONFIG_LIBS) -o 8211dump$(EXE) + +8211view$(EXE): 8211view.$(OBJ_EXT) + $(LD) $(LDFLAGS) 8211view.$(OBJ_EXT) $(CONFIG_LIBS) -o 8211view$(EXE) + +timetest$(EXE): timetest.$(OBJ_EXT) + $(LD) $(LDFLAGS) timetest.$(OBJ_EXT) $(CONFIG_LIBS) -o timetest$(EXE) + +upd_test$(EXE): upd_test.$(OBJ_EXT) + $(LD) $(LDFLAGS) upd_test.$(OBJ_EXT) $(CONFIG_LIBS) -o upd_test$(EXE) + +mkcatalog$(EXE): mkcatalog.$(OBJ_EXT) + $(LD) $(LDFLAGS) mkcatalog.$(OBJ_EXT) $(CONFIG_LIBS) -o mkcatalog$(EXE) + +docs: + rm -rf html + mkdir html + doxygen + +dist: docs + rm -rf $(DISTDIR) + mkdir $(DISTDIR) + mkdir $(DISTDIR)/html + cp html/* $(DISTDIR)/html + autoconf + cp *.cpp *.h configure Makefile.in $(DISTDIR) + rm configure + cp ../../port/{cpl_error{.h,.cpp},cpl_port.h,cpl_string.{h,cpp}} $(DISTDIR) + cp ../../port/{cpl_vsisimple.cpp,cpl_config.h.in} $(DISTDIR) + cp ../../port/{cpl_multiproc.cpp,cpl_multiproc.h} $(DISTDIR) + cp ../../port/{cpl_vsi.h,cpl_conv.{cpp,h},cpl_path.cpp} $(DISTDIR) + cp ../../port/cpl_{vsil.cpp,vsi_mem.cpp,vsil_win32.cpp} $(DISTDIR) + cp ../../port/cpl_{vsil_unix_stdio_64.cpp,dir.cpp} $(DISTDIR) + cp ../../port/cpl_{multiproc.{cpp,h},vsi_private.h} $(DISTDIR) + cp ../../port/cpl_config.h.vc $(DISTDIR) + tar czf $(DISTDIR).tar.gz $(DISTDIR) + zip -r $(DISTDIR).zip $(DISTDIR) + +update-web: dist docs + cp html/* $(WEB_DIR) + cp $(DISTDIR).tar.gz $(DISTDIR).zip $(WEB_DIR) + scp html/* $(DISTDIR).tar.gz $(DISTDIR).zip \ + warmerda@www.gdal.org:home.gdal.org/projects/iso8211 + + +test: 8211dump + @./teststream.sh > t1.out + @if test "`diff t1.out teststream.out`" = '' ; then \ + echo "******* Stream 1 Succeeded *********"; \ + rm t1.out; \ + else \ + echo "******* Stream 1 Failed *********"; \ + diff t1.out teststream.out; \ + fi + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/iso8211/Makefile.in b/bazaar/plugin/gdal/frmts/iso8211/Makefile.in new file mode 100644 index 000000000..33559f350 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/Makefile.in @@ -0,0 +1,74 @@ + +OBJ = ddfmodule.o ddfutils.o ddffielddefn.o ddfrecord.o ddffield.o \ + ddfsubfielddefn.o \ + \ + cpl_error.o cpl_vsisimple.o cpl_string.o cpl_conv.o cpl_multiproc.o \ + cpl_vsil.o cpl_vsi_mem.o cpl_vsil_unix_stdio_64.o cpl_dir.o \ + cpl_conv.o cpl_path.o + +CXXFLAGS = @CXXFLAGS@ @CXX_WFLAGS@ +LIBS = @LIBS@ -lm +CXX = @CXX@ + + +default: 8211view + +libiso8211.a: $(OBJ) + ar r libiso8211.a $(OBJ) + +ddfmodule.o: ddfmodule.cpp + $(CXX) -c $(CXXFLAGS) ddfmodule.cpp + +ddfutils.o: ddfutils.cpp + $(CXX) -c $(CXXFLAGS) ddfutils.cpp + +ddffielddefn.o: ddffielddefn.cpp + $(CXX) -c $(CXXFLAGS) ddffielddefn.cpp + +ddfrecord.o: ddfrecord.cpp + $(CXX) -c $(CXXFLAGS) ddfrecord.cpp + +ddffield.o: ddffield.cpp + $(CXX) -c $(CXXFLAGS) ddffield.cpp + +ddfsubfielddefn.o: ddfsubfielddefn.cpp + $(CXX) -c $(CXXFLAGS) ddfsubfielddefn.cpp + +cpl_error.o: cpl_error.cpp + $(CXX) -c $(CXXFLAGS) cpl_error.cpp + +cpl_string.o: cpl_string.cpp + $(CXX) -c $(CXXFLAGS) cpl_string.cpp + +cpl_conv.o: cpl_conv.cpp + $(CXX) -c $(CXXFLAGS) cpl_conv.cpp + +cpl_vsisimple.o: cpl_vsisimple.cpp + $(CXX) -c $(CXXFLAGS) cpl_vsisimple.cpp + +# +# Mainlines +# + +8211view.o: 8211view.cpp + $(CXX) -c $(CXXFLAGS) 8211view.cpp + +8211dump.o: 8211dump.cpp + $(CXX) -c $(CXXFLAGS) 8211dump.cpp + +8211view: 8211view.o libiso8211.a + $(CXX) $(CXXFLAGS) 8211view.o libiso8211.a $(LIBS) -o 8211view + +8211dump: 8211dump.o libiso8211.a + $(CXX) $(CXXFLAGS) 8211dump.o libiso8211.a $(LIBS) -o 8211dump + + +test: 8211dump + @./teststream.sh > t1.out + @if test "`diff t1.out teststream.out`" = '' ; then \ + echo "******* Stream 1 Succeeded *********"; \ + rm t1.out; \ + else \ + echo "******* Stream 1 Failed *********"; \ + diff t1.out teststream.out; \ + fi diff --git a/bazaar/plugin/gdal/frmts/iso8211/aclocal.m4 b/bazaar/plugin/gdal/frmts/iso8211/aclocal.m4 new file mode 100644 index 000000000..d4d18f50c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/aclocal.m4 @@ -0,0 +1,15 @@ +AC_DEFUN(AC_COMPILER_WFLAGS, +[ + # Remove -g from compile flags, we will add via CFG variable if + # we need it. + CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-g //"` + CFLAGS=`echo "$CFLAGS " | sed "s/-g //"` + + # check for GNU compiler, and use -Wall + if test "$GXX" = "yes"; then + CXX_WFLAGS="-Wall" + AC_DEFINE(USE_GNUCC) + fi + AC_SUBST(CXX_WFLAGS,$CXX_WFLAGS) + AC_SUBST(C_WFLAGS,$C_WFLAGS) +]) diff --git a/bazaar/plugin/gdal/frmts/iso8211/configure.in b/bazaar/plugin/gdal/frmts/iso8211/configure.in new file mode 100644 index 000000000..b81f09d98 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/configure.in @@ -0,0 +1,18 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(Makefile.in) +AC_CONFIG_HEADER(cpl_config.h) + +dnl Checks for programs. +AC_PROG_CXX + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(assert.h fcntl.h unistd.h dbmalloc.h dlfcn.h stdint.h limits.h locale.h values.h float.h errno.h) + +dnl Checks for library functions. +AC_C_BIGENDIAN +AC_FUNC_VPRINTF + +AC_COMPILER_WFLAGS + +AC_OUTPUT(Makefile) diff --git a/bazaar/plugin/gdal/frmts/iso8211/ddffield.cpp b/bazaar/plugin/gdal/frmts/iso8211/ddffield.cpp new file mode 100644 index 000000000..d239bab61 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/ddffield.cpp @@ -0,0 +1,331 @@ +/****************************************************************************** + * $Id: ddffield.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: ISO 8211 Access + * Purpose: Implements the DDFField class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "iso8211.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ddffield.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +// Note, we implement no constructor for this class to make instantiation +// cheaper. It is required that the Initialize() be called before anything +// else. + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void DDFField::Initialize( DDFFieldDefn *poDefnIn, const char * pachDataIn, + int nDataSizeIn ) + +{ + pachData = pachDataIn; + nDataSize = nDataSizeIn; + poDefn = poDefnIn; +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +/** + * Write out field contents to debugging file. + * + * A variety of information about this field, and all it's + * subfields is written to the given debugging file handle. Note that + * field definition information (ala DDFFieldDefn) isn't written. + * + * @param fp The standard io file handle to write to. ie. stderr + */ + +void DDFField::Dump( FILE * fp ) + +{ + int nMaxRepeat = 8; + + if( getenv("DDF_MAXDUMP") != NULL ) + nMaxRepeat = atoi(getenv("DDF_MAXDUMP")); + + fprintf( fp, " DDFField:\n" ); + fprintf( fp, " Tag = `%s'\n", poDefn->GetName() ); + fprintf( fp, " DataSize = %d\n", nDataSize ); + + fprintf( fp, " Data = `" ); + for( int i = 0; i < MIN(nDataSize,40); i++ ) + { + if( pachData[i] < 32 || pachData[i] > 126 ) + fprintf( fp, "\\%02X", ((unsigned char *) pachData)[i] ); + else + fprintf( fp, "%c", pachData[i] ); + } + + if( nDataSize > 40 ) + fprintf( fp, "..." ); + fprintf( fp, "'\n" ); + +/* -------------------------------------------------------------------- */ +/* dump the data of the subfields. */ +/* -------------------------------------------------------------------- */ + int iOffset = 0, nLoopCount; + + for( nLoopCount = 0; nLoopCount < GetRepeatCount(); nLoopCount++ ) + { + if( nLoopCount > nMaxRepeat ) + { + fprintf( fp, " ...\n" ); + break; + } + + for( int i = 0; i < poDefn->GetSubfieldCount(); i++ ) + { + int nBytesConsumed; + + poDefn->GetSubfield(i)->DumpData( pachData + iOffset, + nDataSize - iOffset, fp ); + + poDefn->GetSubfield(i)->GetDataLength( pachData + iOffset, + nDataSize - iOffset, + &nBytesConsumed ); + + iOffset += nBytesConsumed; + } + } +} + +/************************************************************************/ +/* GetSubfieldData() */ +/************************************************************************/ + +/** + * Fetch raw data pointer for a particular subfield of this field. + * + * The passed DDFSubfieldDefn (poSFDefn) should be acquired from the + * DDFFieldDefn corresponding with this field. This is normally done + * once before reading any records. This method involves a series of + * calls to DDFSubfield::GetDataLength() in order to track through the + * DDFField data to that belonging to the requested subfield. This can + * be relatively expensive.

    + * + * @param poSFDefn The definition of the subfield for which the raw + * data pointer is desired. + * @param pnMaxBytes The maximum number of bytes that can be accessed from + * the returned data pointer is placed in this int, unless it is NULL. + * @param iSubfieldIndex The instance of this subfield to fetch. Use zero + * (the default) for the first instance. + * + * @return A pointer into the DDFField's data that belongs to the subfield. + * This returned pointer is invalidated by the next record read + * (DDFRecord::ReadRecord()) and the returned pointer should not be freed + * by the application. + */ + +const char *DDFField::GetSubfieldData( DDFSubfieldDefn *poSFDefn, + int *pnMaxBytes, int iSubfieldIndex ) + +{ + int iOffset = 0; + + if( poSFDefn == NULL ) + return NULL; + + if( iSubfieldIndex > 0 && poDefn->GetFixedWidth() > 0 ) + { + iOffset = poDefn->GetFixedWidth() * iSubfieldIndex; + iSubfieldIndex = 0; + } + + while( iSubfieldIndex >= 0 ) + { + for( int iSF = 0; iSF < poDefn->GetSubfieldCount(); iSF++ ) + { + int nBytesConsumed; + DDFSubfieldDefn * poThisSFDefn = poDefn->GetSubfield( iSF ); + + if( poThisSFDefn == poSFDefn && iSubfieldIndex == 0 ) + { + if( pnMaxBytes != NULL ) + *pnMaxBytes = nDataSize - iOffset; + + return pachData + iOffset; + } + + poThisSFDefn->GetDataLength( pachData+iOffset, nDataSize - iOffset, + &nBytesConsumed); + iOffset += nBytesConsumed; + } + + iSubfieldIndex--; + } + + // We didn't find our target subfield or instance! + return NULL; +} + +/************************************************************************/ +/* GetRepeatCount() */ +/************************************************************************/ + +/** + * How many times do the subfields of this record repeat? This + * will always be one for non-repeating fields. + * + * @return The number of times that the subfields of this record occur + * in this record. This will be one for non-repeating fields. + * + * @see 8211view example program + * for demonstation of handling repeated fields properly. + */ + +int DDFField::GetRepeatCount() + +{ + if( !poDefn->IsRepeating() ) + return 1; + +/* -------------------------------------------------------------------- */ +/* The occurance count depends on how many copies of this */ +/* field's list of subfields can fit into the data space. */ +/* -------------------------------------------------------------------- */ + if( poDefn->GetFixedWidth() ) + { + return nDataSize / poDefn->GetFixedWidth(); + } + +/* -------------------------------------------------------------------- */ +/* Note that it may be legal to have repeating variable width */ +/* subfields, but I don't have any samples, so I ignore it for */ +/* now. */ +/* */ +/* The file data/cape_royal_AZ_DEM/1183XREF.DDF has a repeating */ +/* variable length field, but the count is one, so it isn't */ +/* much value for testing. */ +/* -------------------------------------------------------------------- */ + int iOffset = 0, iRepeatCount = 1; + + while( TRUE ) + { + for( int iSF = 0; iSF < poDefn->GetSubfieldCount(); iSF++ ) + { + int nBytesConsumed; + DDFSubfieldDefn * poThisSFDefn = poDefn->GetSubfield( iSF ); + + if( poThisSFDefn->GetWidth() > nDataSize - iOffset ) + nBytesConsumed = poThisSFDefn->GetWidth(); + else + poThisSFDefn->GetDataLength( pachData+iOffset, + nDataSize - iOffset, + &nBytesConsumed); + + iOffset += nBytesConsumed; + if( iOffset > nDataSize ) + return iRepeatCount - 1; + } + + if( iOffset > nDataSize - 2 ) + return iRepeatCount; + + iRepeatCount++; + } +} + +/************************************************************************/ +/* GetInstanceData() */ +/************************************************************************/ + +/** + * Get field instance data and size. + * + * The returned data pointer and size values are suitable for use with + * DDFRecord::SetFieldRaw(). + * + * @param nInstance a value from 0 to GetRepeatCount()-1. + * @param pnInstanceSize a location to put the size (in bytes) of the + * field instance data returned. This size will include the unit terminator + * (if any), but not the field terminator. This size pointer may be NULL + * if not needed. + * + * @return the data pointer, or NULL on error. + */ + +const char *DDFField::GetInstanceData( int nInstance, + int *pnInstanceSize ) + +{ + int nRepeatCount = GetRepeatCount(); + const char *pachWrkData; + + if( nInstance < 0 || nInstance >= nRepeatCount ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Special case for fields without subfields (like "0001"). We */ +/* don't currently handle repeating simple fields. */ +/* -------------------------------------------------------------------- */ + if( poDefn->GetSubfieldCount() == 0 ) + { + pachWrkData = GetData(); + if( pnInstanceSize != 0 ) + *pnInstanceSize = GetDataSize(); + return pachWrkData; + } + +/* -------------------------------------------------------------------- */ +/* Get a pointer to the start of the existing data for this */ +/* iteration of the field. */ +/* -------------------------------------------------------------------- */ + int nBytesRemaining1, nBytesRemaining2; + DDFSubfieldDefn *poFirstSubfield; + + poFirstSubfield = poDefn->GetSubfield(0); + + pachWrkData = GetSubfieldData(poFirstSubfield, &nBytesRemaining1, + nInstance); + +/* -------------------------------------------------------------------- */ +/* Figure out the size of the entire field instance, including */ +/* unit terminators, but not any trailing field terminator. */ +/* -------------------------------------------------------------------- */ + if( pnInstanceSize != NULL ) + { + DDFSubfieldDefn *poLastSubfield; + int nLastSubfieldWidth; + const char *pachLastData; + + poLastSubfield = poDefn->GetSubfield(poDefn->GetSubfieldCount()-1); + + pachLastData = GetSubfieldData( poLastSubfield, &nBytesRemaining2, + nInstance ); + poLastSubfield->GetDataLength( pachLastData, nBytesRemaining2, + &nLastSubfieldWidth ); + + *pnInstanceSize = + nBytesRemaining1 - (nBytesRemaining2 - nLastSubfieldWidth); + } + + return pachWrkData; +} diff --git a/bazaar/plugin/gdal/frmts/iso8211/ddffielddefn.cpp b/bazaar/plugin/gdal/frmts/iso8211/ddffielddefn.cpp new file mode 100644 index 000000000..725a6570a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/ddffielddefn.cpp @@ -0,0 +1,882 @@ +/****************************************************************************** + * $Id: ddffielddefn.cpp 28348 2015-01-23 15:27:13Z rouault $ + * + * Project: ISO 8211 Access + * Purpose: Implements the DDFFieldDefn class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "iso8211.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: ddffielddefn.cpp 28348 2015-01-23 15:27:13Z rouault $"); + +#define CPLE_DiscardedFormat 1301 + +/************************************************************************/ +/* DDFFieldDefn() */ +/************************************************************************/ + +DDFFieldDefn::DDFFieldDefn() + +{ + poModule = NULL; + pszTag = NULL; + _fieldName = NULL; + _arrayDescr = NULL; + _formatControls = NULL; + nSubfieldCount = 0; + papoSubfields = NULL; + bRepeatingSubfields = FALSE; + nFixedWidth = 0; +} + +/************************************************************************/ +/* ~DDFFieldDefn() */ +/************************************************************************/ + +DDFFieldDefn::~DDFFieldDefn() + +{ + int i; + + CPLFree( pszTag ); + CPLFree( _fieldName ); + CPLFree( _arrayDescr ); + CPLFree( _formatControls ); + + for( i = 0; i < nSubfieldCount; i++ ) + delete papoSubfields[i]; + CPLFree( papoSubfields ); +} + +/************************************************************************/ +/* AddSubfield() */ +/************************************************************************/ + +void DDFFieldDefn::AddSubfield( const char *pszName, + const char *pszFormat ) + +{ + DDFSubfieldDefn *poSFDefn = new DDFSubfieldDefn; + + poSFDefn->SetName( pszName ); + poSFDefn->SetFormat( pszFormat ); + AddSubfield( poSFDefn ); +} + +/************************************************************************/ +/* AddSubfield() */ +/************************************************************************/ + +void DDFFieldDefn::AddSubfield( DDFSubfieldDefn *poNewSFDefn, + int bDontAddToFormat ) + +{ + nSubfieldCount++; + papoSubfields = (DDFSubfieldDefn ** ) + CPLRealloc( papoSubfields, sizeof(void*) * nSubfieldCount ); + papoSubfields[nSubfieldCount-1] = poNewSFDefn; + + if( bDontAddToFormat ) + return; + +/* -------------------------------------------------------------------- */ +/* Add this format to the format list. We don't bother */ +/* aggregating formats here. */ +/* -------------------------------------------------------------------- */ + if( _formatControls == NULL || strlen(_formatControls) == 0 ) + { + CPLFree( _formatControls ); + _formatControls = CPLStrdup( "()" ); + } + + int nOldLen = strlen(_formatControls); + + char *pszNewFormatControls = (char *) + CPLMalloc(nOldLen+3+strlen(poNewSFDefn->GetFormat())); + + strcpy( pszNewFormatControls, _formatControls ); + pszNewFormatControls[nOldLen-1] = '\0'; + if( pszNewFormatControls[nOldLen-2] != '(' ) + strcat( pszNewFormatControls, "," ); + + strcat( pszNewFormatControls, poNewSFDefn->GetFormat() ); + strcat( pszNewFormatControls, ")" ); + + CPLFree( _formatControls ); + _formatControls = pszNewFormatControls; + +/* -------------------------------------------------------------------- */ +/* Add the subfield name to the list. */ +/* -------------------------------------------------------------------- */ + if( _arrayDescr == NULL ) + _arrayDescr = CPLStrdup(""); + + _arrayDescr = (char *) + CPLRealloc(_arrayDescr, + strlen(_arrayDescr)+strlen(poNewSFDefn->GetName())+2); + if( strlen(_arrayDescr) > 0 && + (_arrayDescr[0] != '*' || strlen(_arrayDescr) > 1) ) + strcat( _arrayDescr, "!" ); + strcat( _arrayDescr, poNewSFDefn->GetName() ); +} + +/************************************************************************/ +/* Create() */ +/* */ +/* Initialize a new field defn from application input, instead */ +/* of from an existing file. */ +/************************************************************************/ + +int DDFFieldDefn::Create( const char *pszTag, const char *pszFieldName, + const char *pszDescription, + DDF_data_struct_code eDataStructCode, + DDF_data_type_code eDataTypeCode, + const char *pszFormat ) + +{ + CPLAssert( this->pszTag == NULL ); + poModule = NULL; + this->pszTag = CPLStrdup( pszTag ); + _fieldName = CPLStrdup( pszFieldName ); + _arrayDescr = CPLStrdup( pszDescription ); + + _data_struct_code = eDataStructCode; + _data_type_code = eDataTypeCode; + + if( pszFormat != NULL ) + _formatControls = CPLStrdup( pszFormat ); + else + _formatControls = CPLStrdup( "" ); + + if( pszDescription != NULL && *pszDescription == '*' ) + bRepeatingSubfields = TRUE; + + return TRUE; +} + +/************************************************************************/ +/* GenerateDDREntry() */ +/************************************************************************/ + +int DDFFieldDefn::GenerateDDREntry( char **ppachData, + int *pnLength ) + +{ + *pnLength = 9 + strlen(_fieldName) + 1 + + strlen(_arrayDescr) + 1 + + strlen(_formatControls) + 1; + + if( strlen(_formatControls) == 0 ) + *pnLength -= 1; + + if( ppachData == NULL ) + return TRUE; + + *ppachData = (char *) CPLMalloc( *pnLength+1 ); + + if( _data_struct_code == dsc_elementary ) + (*ppachData)[0] = '0'; + else if( _data_struct_code == dsc_vector ) + (*ppachData)[0] = '1'; + else if( _data_struct_code == dsc_array ) + (*ppachData)[0] = '2'; + else if( _data_struct_code == dsc_concatenated ) + (*ppachData)[0] = '3'; + + if( _data_type_code == dtc_char_string ) + (*ppachData)[1] = '0'; + else if( _data_type_code == dtc_implicit_point ) + (*ppachData)[1] = '1'; + else if( _data_type_code == dtc_explicit_point ) + (*ppachData)[1] = '2'; + else if( _data_type_code == dtc_explicit_point_scaled ) + (*ppachData)[1] = '3'; + else if( _data_type_code == dtc_char_bit_string ) + (*ppachData)[1] = '4'; + else if( _data_type_code == dtc_bit_string ) + (*ppachData)[1] = '5'; + else if( _data_type_code == dtc_mixed_data_type ) + (*ppachData)[1] = '6'; + + (*ppachData)[2] = '0'; + (*ppachData)[3] = '0'; + (*ppachData)[4] = ';'; + (*ppachData)[5] = '&'; + (*ppachData)[6] = ' '; + (*ppachData)[7] = ' '; + (*ppachData)[8] = ' '; + sprintf( *ppachData + 9, "%s%c%s", + _fieldName, DDF_UNIT_TERMINATOR, _arrayDescr ); + + if( strlen(_formatControls) > 0 ) + sprintf( *ppachData + strlen(*ppachData), "%c%s", + DDF_UNIT_TERMINATOR, _formatControls ); + sprintf( *ppachData + strlen(*ppachData), "%c", DDF_FIELD_TERMINATOR ); + + return TRUE; +} + +/************************************************************************/ +/* Initialize() */ +/* */ +/* Initialize the field definition from the information in the */ +/* DDR record. This is called by DDFModule::Open(). */ +/************************************************************************/ + +int DDFFieldDefn::Initialize( DDFModule * poModuleIn, + const char * pszTagIn, + int nFieldEntrySize, + const char * pachFieldArea ) + +{ + int iFDOffset = poModuleIn->GetFieldControlLength(); + int nCharsConsumed; + + poModule = poModuleIn; + + pszTag = CPLStrdup( pszTagIn ); + +/* -------------------------------------------------------------------- */ +/* Set the data struct and type codes. */ +/* -------------------------------------------------------------------- */ + switch( pachFieldArea[0] ) + { + case ' ': /* for ADRG, DIGEST USRP, DIGEST ASRP files */ + case '0': + _data_struct_code = dsc_elementary; + break; + + case '1': + _data_struct_code = dsc_vector; + break; + + case '2': + _data_struct_code = dsc_array; + break; + + case '3': + _data_struct_code = dsc_concatenated; + break; + + default: + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised data_struct_code value %c.\n" + "Field %s initialization incorrect.", + pachFieldArea[0], pszTag ); + _data_struct_code = dsc_elementary; + } + + switch( pachFieldArea[1] ) + { + case ' ': /* for ADRG, DIGEST USRP, DIGEST ASRP files */ + case '0': + _data_type_code = dtc_char_string; + break; + + case '1': + _data_type_code = dtc_implicit_point; + break; + + case '2': + _data_type_code = dtc_explicit_point; + break; + + case '3': + _data_type_code = dtc_explicit_point_scaled; + break; + + case '4': + _data_type_code = dtc_char_bit_string; + break; + + case '5': + _data_type_code = dtc_bit_string; + break; + + case '6': + _data_type_code = dtc_mixed_data_type; + break; + + default: + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised data_type_code value %c.\n" + "Field %s initialization incorrect.", + pachFieldArea[1], pszTag ); + _data_type_code = dtc_char_string; + } + +/* -------------------------------------------------------------------- */ +/* Capture the field name, description (sub field names), and */ +/* format statements. */ +/* -------------------------------------------------------------------- */ + + _fieldName = + DDFFetchVariable( pachFieldArea + iFDOffset, + nFieldEntrySize - iFDOffset, + DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, + &nCharsConsumed ); + iFDOffset += nCharsConsumed; + + _arrayDescr = + DDFFetchVariable( pachFieldArea + iFDOffset, + nFieldEntrySize - iFDOffset, + DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, + &nCharsConsumed ); + iFDOffset += nCharsConsumed; + + _formatControls = + DDFFetchVariable( pachFieldArea + iFDOffset, + nFieldEntrySize - iFDOffset, + DDF_UNIT_TERMINATOR, DDF_FIELD_TERMINATOR, + &nCharsConsumed ); + +/* -------------------------------------------------------------------- */ +/* Parse the subfield info. */ +/* -------------------------------------------------------------------- */ + if( _data_struct_code != dsc_elementary ) + { + if( !BuildSubfields() ) + return FALSE; + + if( !ApplyFormats() ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +/** + * Write out field definition info to debugging file. + * + * A variety of information about this field definition, and all it's + * subfields is written to the give debugging file handle. + * + * @param fp The standard io file handle to write to. ie. stderr + */ + +void DDFFieldDefn::Dump( FILE * fp ) + +{ + const char *pszValue = ""; + + fprintf( fp, " DDFFieldDefn:\n" ); + fprintf( fp, " Tag = `%s'\n", pszTag ); + fprintf( fp, " _fieldName = `%s'\n", _fieldName ); + fprintf( fp, " _arrayDescr = `%s'\n", _arrayDescr ); + fprintf( fp, " _formatControls = `%s'\n", _formatControls ); + + switch( _data_struct_code ) + { + case dsc_elementary: + pszValue = "elementary"; + break; + + case dsc_vector: + pszValue = "vector"; + break; + + case dsc_array: + pszValue = "array"; + break; + + case dsc_concatenated: + pszValue = "concatenated"; + break; + + default: + CPLAssert( FALSE ); + pszValue = "(unknown)"; + } + + fprintf( fp, " _data_struct_code = %s\n", pszValue ); + + switch( _data_type_code ) + { + case dtc_char_string: + pszValue = "char_string"; + break; + + case dtc_implicit_point: + pszValue = "implicit_point"; + break; + + case dtc_explicit_point: + pszValue = "explicit_point"; + break; + + case dtc_explicit_point_scaled: + pszValue = "explicit_point_scaled"; + break; + + case dtc_char_bit_string: + pszValue = "char_bit_string"; + break; + + case dtc_bit_string: + pszValue = "bit_string"; + break; + + case dtc_mixed_data_type: + pszValue = "mixed_data_type"; + break; + + default: + CPLAssert( FALSE ); + pszValue = "(unknown)"; + break; + } + + fprintf( fp, " _data_type_code = %s\n", pszValue ); + + for( int i = 0; i < nSubfieldCount; i++ ) + papoSubfields[i]->Dump( fp ); +} + +/************************************************************************/ +/* BuildSubfields() */ +/* */ +/* Based on the _arrayDescr build a set of subfields. */ +/************************************************************************/ + +int DDFFieldDefn::BuildSubfields() + +{ + char **papszSubfieldNames; + const char *pszSublist = _arrayDescr; + +/* -------------------------------------------------------------------- */ +/* It is valid to define a field with _arrayDesc */ +/* '*STPT!CTPT!ENPT*YCOO!XCOO' and formatControls '(2b24)'. */ +/* This basically indicates that there are 3 (YCOO,XCOO) */ +/* structures named STPT, CTPT and ENPT. But we can't handle */ +/* such a case gracefully here, so we just ignore the */ +/* "structure names" and treat such a thing as a repeating */ +/* YCOO/XCOO array. This occurs with the AR2D field of some */ +/* AML S-57 files for instance. */ +/* */ +/* We accomplish this by ignoring everything before the last */ +/* '*' in the subfield list. */ +/* -------------------------------------------------------------------- */ + if( strrchr(pszSublist, '*') != NULL ) + pszSublist = strrchr(pszSublist,'*'); + +/* -------------------------------------------------------------------- */ +/* Strip off the repeating marker, when it occurs, but mark our */ +/* field as repeating. */ +/* -------------------------------------------------------------------- */ + if( pszSublist[0] == '*' ) + { + bRepeatingSubfields = TRUE; + pszSublist++; + } + +/* -------------------------------------------------------------------- */ +/* split list of fields . */ +/* -------------------------------------------------------------------- */ + papszSubfieldNames = CSLTokenizeStringComplex( pszSublist, "!", + FALSE, FALSE ); + +/* -------------------------------------------------------------------- */ +/* minimally initialize the subfields. More will be done later. */ +/* -------------------------------------------------------------------- */ + int nSFCount = CSLCount( papszSubfieldNames ); + for( int iSF = 0; iSF < nSFCount; iSF++ ) + { + DDFSubfieldDefn *poSFDefn = new DDFSubfieldDefn; + + poSFDefn->SetName( papszSubfieldNames[iSF] ); + AddSubfield( poSFDefn, TRUE ); + } + + CSLDestroy( papszSubfieldNames ); + + return TRUE; +} + +/************************************************************************/ +/* ExtractSubstring() */ +/* */ +/* Extract a substring terminated by a comma (or end of */ +/* string). Commas in brackets are ignored as terminated with */ +/* bracket nesting understood gracefully. If the returned */ +/* string would being and end with a bracket then strip off the */ +/* brackets. */ +/* */ +/* Given a string like "(A,3(B,C),D),X,Y)" return "A,3(B,C),D". */ +/* Give a string like "3A,2C" return "3A". */ +/************************************************************************/ + +char *DDFFieldDefn::ExtractSubstring( const char * pszSrc ) + +{ + int nBracket=0, i; + char *pszReturn; + + for( i = 0; + pszSrc[i] != '\0' && (nBracket > 0 || pszSrc[i] != ','); + i++ ) + { + if( pszSrc[i] == '(' ) + nBracket++; + else if( pszSrc[i] == ')' ) + nBracket--; + } + + if( pszSrc[0] == '(' ) + { + pszReturn = CPLStrdup( pszSrc + 1 ); + pszReturn[i-2] = '\0'; + } + else + { + pszReturn = CPLStrdup( pszSrc ); + pszReturn[i] = '\0'; + } + + return pszReturn; +} + +/************************************************************************/ +/* ExpandFormat() */ +/************************************************************************/ + +char *DDFFieldDefn::ExpandFormat( const char * pszSrc ) + +{ + int nDestMax = 32; + char *pszDest = (char *) CPLMalloc(nDestMax+1); + int iSrc, iDst; + int nRepeat = 0; + + iSrc = 0; + iDst = 0; + pszDest[0] = '\0'; + + while( pszSrc[iSrc] != '\0' ) + { + /* This is presumably an extra level of brackets around some + binary stuff related to rescaning which we don't care to do + (see 6.4.3.3 of the standard. We just strip off the extra + layer of brackets */ + if( (iSrc == 0 || pszSrc[iSrc-1] == ',') && pszSrc[iSrc] == '(' ) + { + char *pszContents = ExtractSubstring( pszSrc+iSrc ); + char *pszExpandedContents = ExpandFormat( pszContents ); + + if( (int) (strlen(pszExpandedContents) + strlen(pszDest) + 1) + > nDestMax ) + { + nDestMax = 2 * (strlen(pszExpandedContents) + strlen(pszDest)); + pszDest = (char *) CPLRealloc(pszDest,nDestMax+1); + } + + strcat( pszDest, pszExpandedContents ); + iDst = strlen(pszDest); + + iSrc = iSrc + strlen(pszContents) + 2; + + CPLFree( pszContents ); + CPLFree( pszExpandedContents ); + } + + /* this is a repeated subclause */ + else if( (iSrc == 0 || pszSrc[iSrc-1] == ',') + && isdigit(pszSrc[iSrc]) ) + { + const char *pszNext; + nRepeat = atoi(pszSrc+iSrc); + + // skip over repeat count. + for( pszNext = pszSrc+iSrc; isdigit(*pszNext); pszNext++ ) + iSrc++; + + char *pszContents = ExtractSubstring( pszNext ); + char *pszExpandedContents = ExpandFormat( pszContents ); + + for( int i = 0; i < nRepeat; i++ ) + { + if( (int) (strlen(pszExpandedContents) + strlen(pszDest) + 1 + 1) + > nDestMax ) + { + nDestMax = + 2 * (strlen(pszExpandedContents) + strlen(pszDest) + 1); + pszDest = (char *) CPLRealloc(pszDest,nDestMax+1); + } + + strcat( pszDest, pszExpandedContents ); + if( i < nRepeat-1 ) + strcat( pszDest, "," ); + } + + iDst = strlen(pszDest); + + if( pszNext[0] == '(' ) + iSrc = iSrc + strlen(pszContents) + 2; + else + iSrc = iSrc + strlen(pszContents); + + CPLFree( pszContents ); + CPLFree( pszExpandedContents ); + } + else + { + if( iDst+1 >= nDestMax ) + { + nDestMax = 2 * iDst; + pszDest = (char *) CPLRealloc(pszDest,nDestMax); + } + + pszDest[iDst++] = pszSrc[iSrc++]; + pszDest[iDst] = '\0'; + } + } + + return pszDest; +} + +/************************************************************************/ +/* ApplyFormats() */ +/* */ +/* This method parses the format string partially, and then */ +/* applies a subfield format string to each subfield object. */ +/* It in turn does final parsing of the subfield formats. */ +/************************************************************************/ + +int DDFFieldDefn::ApplyFormats() + +{ + char *pszFormatList; + char **papszFormatItems; + +/* -------------------------------------------------------------------- */ +/* Verify that the format string is contained within brackets. */ +/* -------------------------------------------------------------------- */ + if( strlen(_formatControls) < 2 + || _formatControls[0] != '(' + || _formatControls[strlen(_formatControls)-1] != ')' ) + { + CPLError( CE_Warning, CPLE_DiscardedFormat, + "Format controls for `%s' field missing brackets:%s", + pszTag, _formatControls ); + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Duplicate the string, and strip off the brackets. */ +/* -------------------------------------------------------------------- */ + + pszFormatList = ExpandFormat( _formatControls ); + +/* -------------------------------------------------------------------- */ +/* Tokenize based on commas. */ +/* -------------------------------------------------------------------- */ + papszFormatItems = + CSLTokenizeStringComplex(pszFormatList, ",", FALSE, FALSE ); + + CPLFree( pszFormatList ); + +/* -------------------------------------------------------------------- */ +/* Apply the format items to subfields. */ +/* -------------------------------------------------------------------- */ + int iFormatItem; + + for( iFormatItem = 0; + papszFormatItems[iFormatItem] != NULL; + iFormatItem++ ) + { + const char *pszPastPrefix; + + pszPastPrefix = papszFormatItems[iFormatItem]; + while( *pszPastPrefix >= '0' && *pszPastPrefix <= '9' ) + pszPastPrefix++; + + /////////////////////////////////////////////////////////////// + // Did we get too many formats for the subfields created + // by names? This may be legal by the 8211 specification, but + // isn't encountered in any formats we care about so we just + // blow. + + if( iFormatItem >= nSubfieldCount ) + { + CPLError( CE_Warning, CPLE_DiscardedFormat, + "Got more formats than subfields for field `%s'.", + pszTag ); + break; + } + + if( !papoSubfields[iFormatItem]->SetFormat(pszPastPrefix) ) + { + CSLDestroy( papszFormatItems ); + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Verify that we got enough formats, cleanup and return. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( papszFormatItems ); + + if( iFormatItem < nSubfieldCount ) + { + CPLError( CE_Warning, CPLE_DiscardedFormat, + "Got less formats than subfields for field `%s'.", + pszTag ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If all the fields are fixed width, then we are fixed width */ +/* too. This is important for repeating fields. */ +/* -------------------------------------------------------------------- */ + nFixedWidth = 0; + for( int i = 0; i < nSubfieldCount; i++ ) + { + if( papoSubfields[i]->GetWidth() == 0 ) + { + nFixedWidth = 0; + break; + } + else + nFixedWidth += papoSubfields[i]->GetWidth(); + } + + return TRUE; +} + +/************************************************************************/ +/* FindSubfieldDefn() */ +/************************************************************************/ + +/** + * Find a subfield definition by it's mnemonic tag. + * + * @param pszMnemonic The name of the field. + * + * @return The subfield pointer, or NULL if there isn't any such subfield. + */ + + +DDFSubfieldDefn *DDFFieldDefn::FindSubfieldDefn( const char * pszMnemonic ) + +{ + for( int i = 0; i < nSubfieldCount; i++ ) + { + if( EQUAL(papoSubfields[i]->GetName(),pszMnemonic) ) + return papoSubfields[i]; + } + + return NULL; +} + +/************************************************************************/ +/* GetSubfield() */ +/* */ +/* Fetch a subfield by it's index. */ +/************************************************************************/ + +/** + * Fetch a subfield by index. + * + * @param i The index subfield index. (Between 0 and GetSubfieldCount()-1) + * + * @return The subfield pointer, or NULL if the index is out of range. + */ + +DDFSubfieldDefn *DDFFieldDefn::GetSubfield( int i ) + +{ + if( i < 0 || i >= nSubfieldCount ) + { + CPLAssert( FALSE ); + return NULL; + } + + return papoSubfields[i]; +} + +/************************************************************************/ +/* GetDefaultValue() */ +/************************************************************************/ + +/** + * Return default data for field instance. + */ + +char *DDFFieldDefn::GetDefaultValue( int *pnSize ) + +{ +/* -------------------------------------------------------------------- */ +/* Loop once collecting the sum of the subfield lengths. */ +/* -------------------------------------------------------------------- */ + int iSubfield; + int nTotalSize = 0; + + for( iSubfield = 0; iSubfield < nSubfieldCount; iSubfield++ ) + { + int nSubfieldSize; + + if( !papoSubfields[iSubfield]->GetDefaultValue( NULL, 0, + &nSubfieldSize ) ) + return NULL; + nTotalSize += nSubfieldSize; + } + +/* -------------------------------------------------------------------- */ +/* Allocate buffer. */ +/* -------------------------------------------------------------------- */ + char *pachData = (char *) CPLMalloc( nTotalSize ); + + if( pnSize != NULL ) + *pnSize = nTotalSize; + +/* -------------------------------------------------------------------- */ +/* Loop again, collecting actual default values. */ +/* -------------------------------------------------------------------- */ + int nOffset = 0; + for( iSubfield = 0; iSubfield < nSubfieldCount; iSubfield++ ) + { + int nSubfieldSize; + + if( !papoSubfields[iSubfield]->GetDefaultValue( + pachData + nOffset, nTotalSize - nOffset, &nSubfieldSize ) ) + { + CPLAssert( FALSE ); + return NULL; + } + + nOffset += nSubfieldSize; + } + + CPLAssert( nOffset == nTotalSize ); + + return pachData; +} diff --git a/bazaar/plugin/gdal/frmts/iso8211/ddfmodule.cpp b/bazaar/plugin/gdal/frmts/iso8211/ddfmodule.cpp new file mode 100644 index 000000000..bdc6e28c3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/ddfmodule.cpp @@ -0,0 +1,727 @@ +/****************************************************************************** + * $Id: ddfmodule.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: ISO 8211 Access + * Purpose: Implements the DDFModule class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "iso8211.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ddfmodule.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* DDFModule() */ +/************************************************************************/ + +/** + * The constructor. + */ + +DDFModule::DDFModule() + +{ + nFieldDefnCount = 0; + papoFieldDefns = NULL; + poRecord = NULL; + + papoClones = NULL; + nCloneCount = nMaxCloneCount = 0; + + fpDDF = NULL; + bReadOnly = TRUE; + + _interchangeLevel = '\0'; + _inlineCodeExtensionIndicator = '\0'; + _versionNumber = '\0'; + _appIndicator = '\0'; + _fieldControlLength = '\0'; + strcpy( _extendedCharSet, " ! " ); + + _recLength = 0; + _leaderIden = 'L'; + _fieldAreaStart = 0; + _sizeFieldLength = 0; + _sizeFieldPos = 0; + _sizeFieldTag = 0; +} + +/************************************************************************/ +/* ~DDFModule() */ +/************************************************************************/ + +/** + * The destructor. + */ + +DDFModule::~DDFModule() + +{ + Close(); +} + +/************************************************************************/ +/* Close() */ +/* */ +/* Note that closing a file also destroys essentially all other */ +/* module datastructures. */ +/************************************************************************/ + +/** + * Close an ISO 8211 file. + */ + +void DDFModule::Close() + +{ +/* -------------------------------------------------------------------- */ +/* Close the file. */ +/* -------------------------------------------------------------------- */ + if( fpDDF != NULL ) + { + VSIFCloseL( fpDDF ); + fpDDF = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup the working record. */ +/* -------------------------------------------------------------------- */ + if( poRecord != NULL ) + { + delete poRecord; + poRecord = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup the clones. Deleting them will cause a callback to */ +/* remove them from the list. */ +/* -------------------------------------------------------------------- */ + while( nCloneCount > 0 ) + delete papoClones[0]; + + nMaxCloneCount = 0; + CPLFree( papoClones ); + papoClones = NULL; + +/* -------------------------------------------------------------------- */ +/* Cleanup the field definitions. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nFieldDefnCount; i++ ) + delete papoFieldDefns[i]; + CPLFree( papoFieldDefns ); + papoFieldDefns = NULL; + nFieldDefnCount = 0; +} + +/************************************************************************/ +/* Open() */ +/* */ +/* Open an ISO 8211 file, and read the DDR record to build the */ +/* field definitions. */ +/************************************************************************/ + +/** + * Open a ISO 8211 (DDF) file for reading. + * + * If the open succeeds the data descriptive record (DDR) will have been + * read, and all the field and subfield definitions will be available. + * + * @param pszFilename The name of the file to open. + * @param bFailQuietly If FALSE a CPL Error is issued for non-8211 files, + * otherwise quietly return NULL. + * + * @return FALSE if the open fails or TRUE if it succeeds. Errors messages + * are issued internally with CPLError(). + */ + +int DDFModule::Open( const char * pszFilename, int bFailQuietly ) + +{ + static const int nLeaderSize = 24; + +/* -------------------------------------------------------------------- */ +/* Close the existing file if there is one. */ +/* -------------------------------------------------------------------- */ + if( fpDDF != NULL ) + Close(); + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + fpDDF = VSIFOpenL( pszFilename, "rb" ); + + if( fpDDF == NULL ) + { + if( !bFailQuietly ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open DDF file `%s'.", + pszFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Read the 24 byte leader. */ +/* -------------------------------------------------------------------- */ + char achLeader[nLeaderSize]; + + if( (int)VSIFReadL( achLeader, 1, nLeaderSize, fpDDF ) != nLeaderSize ) + { + VSIFCloseL( fpDDF ); + fpDDF = NULL; + + if( !bFailQuietly ) + CPLError( CE_Failure, CPLE_FileIO, + "Leader is short on DDF file `%s'.", + pszFilename ); + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Verify that this appears to be a valid DDF file. */ +/* -------------------------------------------------------------------- */ + int i, bValid = TRUE; + + for( i = 0; i < nLeaderSize; i++ ) + { + if( achLeader[i] < 32 || achLeader[i] > 126 ) + bValid = FALSE; + } + + if( achLeader[5] != '1' && achLeader[5] != '2' && achLeader[5] != '3' ) + bValid = FALSE; + + if( achLeader[6] != 'L' ) + bValid = FALSE; + if( achLeader[8] != '1' && achLeader[8] != ' ' ) + bValid = FALSE; + +/* -------------------------------------------------------------------- */ +/* Extract information from leader. */ +/* -------------------------------------------------------------------- */ + + if( bValid ) + { + _recLength = DDFScanInt( achLeader+0, 5 ); + _interchangeLevel = achLeader[5]; + _leaderIden = achLeader[6]; + _inlineCodeExtensionIndicator = achLeader[7]; + _versionNumber = achLeader[8]; + _appIndicator = achLeader[9]; + _fieldControlLength = DDFScanInt(achLeader+10,2); + _fieldAreaStart = DDFScanInt(achLeader+12,5); + _extendedCharSet[0] = achLeader[17]; + _extendedCharSet[1] = achLeader[18]; + _extendedCharSet[2] = achLeader[19]; + _extendedCharSet[3] = '\0'; + _sizeFieldLength = DDFScanInt(achLeader+20,1); + _sizeFieldPos = DDFScanInt(achLeader+21,1); + _sizeFieldTag = DDFScanInt(achLeader+23,1); + + if( _recLength < nLeaderSize || _fieldControlLength == 0 + || _fieldAreaStart < 24 || _sizeFieldLength == 0 + || _sizeFieldPos == 0 || _sizeFieldTag == 0 ) + { + bValid = FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* If the header is invalid, then clean up, report the error */ +/* and return. */ +/* -------------------------------------------------------------------- */ + if( !bValid ) + { + VSIFCloseL( fpDDF ); + fpDDF = NULL; + + if( !bFailQuietly ) + CPLError( CE_Failure, CPLE_AppDefined, + "File `%s' does not appear to have\n" + "a valid ISO 8211 header.\n", + pszFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Read the whole record info memory. */ +/* -------------------------------------------------------------------- */ + char *pachRecord; + + pachRecord = (char *) CPLMalloc(_recLength); + memcpy( pachRecord, achLeader, nLeaderSize ); + + if( (int)VSIFReadL( pachRecord+nLeaderSize, 1, _recLength-nLeaderSize, fpDDF ) + != _recLength - nLeaderSize ) + { + if( !bFailQuietly ) + CPLError( CE_Failure, CPLE_FileIO, + "Header record is short on DDF file `%s'.", + pszFilename ); + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* First make a pass counting the directory entries. */ +/* -------------------------------------------------------------------- */ + int nFieldEntryWidth, nFDCount = 0; + + nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag; + + for( i = nLeaderSize; i < _recLength; i += nFieldEntryWidth ) + { + if( pachRecord[i] == DDF_FIELD_TERMINATOR ) + break; + + nFDCount++; + } + +/* -------------------------------------------------------------------- */ +/* Allocate, and read field definitions. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nFDCount; i++ ) + { + char szTag[128]; + int nEntryOffset = nLeaderSize + i*nFieldEntryWidth; + int nFieldLength, nFieldPos; + DDFFieldDefn *poFDefn; + + strncpy( szTag, pachRecord+nEntryOffset, _sizeFieldTag ); + szTag[_sizeFieldTag] = '\0'; + + nEntryOffset += _sizeFieldTag; + nFieldLength = DDFScanInt( pachRecord+nEntryOffset, _sizeFieldLength ); + + nEntryOffset += _sizeFieldLength; + nFieldPos = DDFScanInt( pachRecord+nEntryOffset, _sizeFieldPos ); + + if (_fieldAreaStart+nFieldPos < 0 || + _recLength - (_fieldAreaStart+nFieldPos) < nFieldLength) + { + if( !bFailQuietly ) + CPLError( CE_Failure, CPLE_FileIO, + "Header record invalid on DDF file `%s'.", + pszFilename ); + + CPLFree( pachRecord ); + return FALSE; + } + + poFDefn = new DDFFieldDefn(); + if( poFDefn->Initialize( this, szTag, nFieldLength, + pachRecord+_fieldAreaStart+nFieldPos ) ) + AddField( poFDefn ); + else + delete poFDefn; + } + + CPLFree( pachRecord ); + +/* -------------------------------------------------------------------- */ +/* Record the current file offset, the beginning of the first */ +/* data record. */ +/* -------------------------------------------------------------------- */ + nFirstRecordOffset = (long)VSIFTellL( fpDDF ); + + return TRUE; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +int DDFModule::Initialize( char chInterchangeLevel, + char chLeaderIden, + char chCodeExtensionIndicator, + char chVersionNumber, + char chAppIndicator, + const char *pszExtendedCharSet, + int nSizeFieldLength, + int nSizeFieldPos, + int nSizeFieldTag ) + +{ + _interchangeLevel = chInterchangeLevel; + _leaderIden = chLeaderIden; + _inlineCodeExtensionIndicator = chCodeExtensionIndicator; + _versionNumber = chVersionNumber; + _appIndicator = chAppIndicator; + strcpy( _extendedCharSet, pszExtendedCharSet ); + _sizeFieldLength = nSizeFieldLength; + _sizeFieldPos = nSizeFieldPos; + _sizeFieldTag = nSizeFieldTag; + + return TRUE; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int DDFModule::Create( const char *pszFilename ) + +{ + CPLAssert( fpDDF == NULL ); + +/* -------------------------------------------------------------------- */ +/* Create the file on disk. */ +/* -------------------------------------------------------------------- */ + fpDDF = VSIFOpenL( pszFilename, "wb+" ); + if( fpDDF == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create file %s, check path and permissions.", + pszFilename ); + return FALSE; + } + + bReadOnly = FALSE; + +/* -------------------------------------------------------------------- */ +/* Prepare all the field definition information. */ +/* -------------------------------------------------------------------- */ + int iField; + + _fieldControlLength = 9; + _recLength = 24 + + nFieldDefnCount * (_sizeFieldLength+_sizeFieldPos+_sizeFieldTag) + + 1; + + _fieldAreaStart = _recLength; + + for( iField=0; iField < nFieldDefnCount; iField++ ) + { + int nLength; + + papoFieldDefns[iField]->GenerateDDREntry( NULL, &nLength ); + _recLength += nLength; + } + +/* -------------------------------------------------------------------- */ +/* Setup 24 byte leader. */ +/* -------------------------------------------------------------------- */ + char achLeader[25]; + + sprintf( achLeader+0, "%05d", (int) _recLength ); + achLeader[5] = _interchangeLevel; + achLeader[6] = _leaderIden; + achLeader[7] = _inlineCodeExtensionIndicator; + achLeader[8] = _versionNumber; + achLeader[9] = _appIndicator; + sprintf( achLeader+10, "%02d", (int) _fieldControlLength ); + sprintf( achLeader+12, "%05d", (int) _fieldAreaStart ); + strncpy( achLeader+17, _extendedCharSet, 3 ); + sprintf( achLeader+20, "%1d", (int) _sizeFieldLength ); + sprintf( achLeader+21, "%1d", (int) _sizeFieldPos ); + achLeader[22] = '0'; + sprintf( achLeader+23, "%1d", (int) _sizeFieldTag ); + VSIFWriteL( achLeader, 24, 1, fpDDF ); + +/* -------------------------------------------------------------------- */ +/* Write out directory entries. */ +/* -------------------------------------------------------------------- */ + int nOffset = 0; + for( iField=0; iField < nFieldDefnCount; iField++ ) + { + char achDirEntry[255]; + char szFormat[32]; + int nLength; + + CPLAssert(_sizeFieldLength + _sizeFieldPos + _sizeFieldTag < (int)sizeof(achDirEntry)); + + papoFieldDefns[iField]->GenerateDDREntry( NULL, &nLength ); + + CPLAssert( (int)strlen(papoFieldDefns[iField]->GetName()) == _sizeFieldTag ); + strcpy( achDirEntry, papoFieldDefns[iField]->GetName() ); + sprintf(szFormat, "%%0%dd", (int)_sizeFieldLength); + sprintf( achDirEntry + _sizeFieldTag, szFormat, nLength ); + sprintf(szFormat, "%%0%dd", (int)_sizeFieldTag); + sprintf( achDirEntry + _sizeFieldTag + _sizeFieldLength, + szFormat, nOffset ); + nOffset += nLength; + + VSIFWriteL( achDirEntry, _sizeFieldLength + _sizeFieldPos + _sizeFieldTag, 1, fpDDF ); + } + + char chUT = DDF_FIELD_TERMINATOR; + VSIFWriteL( &chUT, 1, 1, fpDDF ); + +/* -------------------------------------------------------------------- */ +/* Write out the field descriptions themselves. */ +/* -------------------------------------------------------------------- */ + for( iField=0; iField < nFieldDefnCount; iField++ ) + { + char *pachData; + int nLength; + + papoFieldDefns[iField]->GenerateDDREntry( &pachData, &nLength ); + VSIFWriteL( pachData, nLength, 1, fpDDF ); + CPLFree( pachData ); + } + + return TRUE; +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +/** + * Write out module info to debugging file. + * + * A variety of information about the module is written to the debugging + * file. This includes all the field and subfield definitions read from + * the header. + * + * @param fp The standard io file handle to write to. ie. stderr. + */ + +void DDFModule::Dump( FILE * fp ) + +{ + fprintf( fp, "DDFModule:\n" ); + fprintf( fp, " _recLength = %ld\n", _recLength ); + fprintf( fp, " _interchangeLevel = %c\n", _interchangeLevel ); + fprintf( fp, " _leaderIden = %c\n", _leaderIden ); + fprintf( fp, " _inlineCodeExtensionIndicator = %c\n", + _inlineCodeExtensionIndicator ); + fprintf( fp, " _versionNumber = %c\n", _versionNumber ); + fprintf( fp, " _appIndicator = %c\n", _appIndicator ); + fprintf( fp, " _extendedCharSet = `%s'\n", _extendedCharSet ); + fprintf( fp, " _fieldControlLength = %d\n", _fieldControlLength ); + fprintf( fp, " _fieldAreaStart = %ld\n", _fieldAreaStart ); + fprintf( fp, " _sizeFieldLength = %ld\n", _sizeFieldLength ); + fprintf( fp, " _sizeFieldPos = %ld\n", _sizeFieldPos ); + fprintf( fp, " _sizeFieldTag = %ld\n", _sizeFieldTag ); + + for( int i = 0; i < nFieldDefnCount; i++ ) + { + papoFieldDefns[i]->Dump( fp ); + } +} + +/************************************************************************/ +/* FindFieldDefn() */ +/************************************************************************/ + +/** + * Fetch the definition of the named field. + * + * This function will scan the DDFFieldDefn's on this module, to find + * one with the indicated field name. + * + * @param pszFieldName The name of the field to search for. The comparison is + * case insensitive. + * + * @return A pointer to the request DDFFieldDefn object is returned, or NULL + * if none matching the name are found. The return object remains owned by + * the DDFModule, and should not be deleted by application code. + */ + +DDFFieldDefn *DDFModule::FindFieldDefn( const char *pszFieldName ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* This pass tries to reduce the cost of comparing strings by */ +/* first checking the first character, and by using strcmp() */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nFieldDefnCount; i++ ) + { + const char *pszThisName = papoFieldDefns[i]->GetName(); + + if( *pszThisName == *pszFieldName + && strcmp( pszFieldName+1, pszThisName+1) == 0 ) + return papoFieldDefns[i]; + } + +/* -------------------------------------------------------------------- */ +/* Now do a more general check. Application code may not */ +/* always use the correct name case. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nFieldDefnCount; i++ ) + { + if( EQUAL(pszFieldName, papoFieldDefns[i]->GetName()) ) + return papoFieldDefns[i]; + } + + return NULL; +} + +/************************************************************************/ +/* ReadRecord() */ +/* */ +/* Read one record from the file, and return to the */ +/* application. The returned record is owned by the module, */ +/* and is reused from call to call in order to preserve headers */ +/* when they aren't being re-read from record to record. */ +/************************************************************************/ + +/** + * Read one record from the file. + * + * @return A pointer to a DDFRecord object is returned, or NULL if a read + * error, or end of file occurs. The returned record is owned by the + * module, and should not be deleted by the application. The record is + * only valid untill the next ReadRecord() at which point it is overwritten. + */ + +DDFRecord *DDFModule::ReadRecord() + +{ + if( poRecord == NULL ) + poRecord = new DDFRecord( this ); + + if( poRecord->Read() ) + return poRecord; + else + return NULL; +} + +/************************************************************************/ +/* AddField() */ +/************************************************************************/ + +/** + * Add new field definition. + * + * Field definitions may only be added to DDFModules being used for + * writing, not those being used for reading. Ownership of the + * DDFFieldDefn object is taken by the DDFModule. + * + * @param poNewFDefn definition to be added to the module. + */ + +void DDFModule::AddField( DDFFieldDefn *poNewFDefn ) + +{ + nFieldDefnCount++; + papoFieldDefns = (DDFFieldDefn **) + CPLRealloc(papoFieldDefns, sizeof(void*)*nFieldDefnCount); + papoFieldDefns[nFieldDefnCount-1] = poNewFDefn; +} + +/************************************************************************/ +/* GetField() */ +/************************************************************************/ + +/** + * Fetch a field definition by index. + * + * @param i (from 0 to GetFieldCount() - 1. + * @return the returned field pointer or NULL if the index is out of range. + */ + +DDFFieldDefn *DDFModule::GetField(int i) + +{ + if( i < 0 || i >= nFieldDefnCount ) + return NULL; + else + return papoFieldDefns[i]; +} + +/************************************************************************/ +/* AddCloneRecord() */ +/* */ +/* We want to keep track of cloned records, so we can clean */ +/* them up when the module is destroyed. */ +/************************************************************************/ + +void DDFModule::AddCloneRecord( DDFRecord * poRecord ) + +{ +/* -------------------------------------------------------------------- */ +/* Do we need to grow the container array? */ +/* -------------------------------------------------------------------- */ + if( nCloneCount == nMaxCloneCount ) + { + nMaxCloneCount = nCloneCount*2 + 20; + papoClones = (DDFRecord **) CPLRealloc(papoClones, + nMaxCloneCount * sizeof(void*)); + } + +/* -------------------------------------------------------------------- */ +/* Add to the list. */ +/* -------------------------------------------------------------------- */ + papoClones[nCloneCount++] = poRecord; +} + +/************************************************************************/ +/* RemoveCloneRecord() */ +/************************************************************************/ + +void DDFModule::RemoveCloneRecord( DDFRecord * poRecord ) + +{ + int i; + + for( i = 0; i < nCloneCount; i++ ) + { + if( papoClones[i] == poRecord ) + { + papoClones[i] = papoClones[nCloneCount-1]; + nCloneCount--; + return; + } + } + + CPLAssert( FALSE ); +} + +/************************************************************************/ +/* Rewind() */ +/************************************************************************/ + +/** + * Return to first record. + * + * The next call to ReadRecord() will read the first data record in the file. + * + * @param nOffset the offset in the file to return to. By default this is + * -1, a special value indicating that reading should return to the first + * data record. Otherwise it is an absolute byte offset in the file. + */ + +void DDFModule::Rewind( long nOffset ) + +{ + if( nOffset == -1 ) + nOffset = nFirstRecordOffset; + + if( fpDDF == NULL ) + return; + + VSIFSeekL( fpDDF, nOffset, SEEK_SET ); + + if( nOffset == nFirstRecordOffset && poRecord != NULL ) + poRecord->Clear(); + +} diff --git a/bazaar/plugin/gdal/frmts/iso8211/ddfrecord.cpp b/bazaar/plugin/gdal/frmts/iso8211/ddfrecord.cpp new file mode 100644 index 000000000..d66f88309 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/ddfrecord.cpp @@ -0,0 +1,1919 @@ +/****************************************************************************** + * $Id: ddfrecord.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: ISO 8211 Access + * Purpose: Implements the DDFRecord class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "iso8211.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ddfrecord.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +static const int nLeaderSize = 24; + +/************************************************************************/ +/* DDFRecord() */ +/************************************************************************/ + +DDFRecord::DDFRecord( DDFModule * poModuleIn ) + +{ + poModule = poModuleIn; + + nReuseHeader = FALSE; + + nFieldOffset = 0; + + nDataSize = 0; + pachData = NULL; + + nFieldCount = 0; + paoFields = NULL; + + bIsClone = FALSE; + + _sizeFieldTag = poModuleIn->GetSizeFieldTag(); + _sizeFieldPos = 0; + _sizeFieldLength = 0; +} + +/************************************************************************/ +/* ~DDFRecord() */ +/************************************************************************/ + +DDFRecord::~DDFRecord() + +{ + Clear(); + + if( bIsClone ) + poModule->RemoveCloneRecord( this ); +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +/** + * Write out record contents to debugging file. + * + * A variety of information about this record, and all it's fields and + * subfields is written to the given debugging file handle. Note that + * field definition information (ala DDFFieldDefn) isn't written. + * + * @param fp The standard io file handle to write to. ie. stderr + */ + +void DDFRecord::Dump( FILE * fp ) + +{ + fprintf( fp, "DDFRecord:\n" ); + fprintf( fp, " nReuseHeader = %d\n", nReuseHeader ); + fprintf( fp, " nDataSize = %d\n", nDataSize ); + fprintf( fp, + " _sizeFieldLength=%d, _sizeFieldPos=%d, _sizeFieldTag=%d\n", + _sizeFieldLength, _sizeFieldPos, _sizeFieldTag ); + + for( int i = 0; i < nFieldCount; i++ ) + { + paoFields[i].Dump( fp ); + } +} + +/************************************************************************/ +/* Read() */ +/* */ +/* Read a record of data from the file, and parse the header to */ +/* build a field list for the record (or reuse the existing one */ +/* if reusing headers). It is expected that the file pointer */ +/* will be positioned at the beginning of a data record. It is */ +/* the DDFModule's responsibility to do so. */ +/* */ +/* This method should only be called by the DDFModule class. */ +/************************************************************************/ + +int DDFRecord::Read() + +{ +/* -------------------------------------------------------------------- */ +/* Redefine the record on the basis of the header if needed. */ +/* As a side effect this will read the data for the record as well.*/ +/* -------------------------------------------------------------------- */ + if( !nReuseHeader ) + { + return( ReadHeader() ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise we read just the data and carefully overlay it on */ +/* the previous records data without disturbing the rest of the */ +/* record. */ +/* -------------------------------------------------------------------- */ + size_t nReadBytes; + + nReadBytes = VSIFReadL( pachData + nFieldOffset, 1, + nDataSize - nFieldOffset, + poModule->GetFP() ); + if( nReadBytes != (size_t) (nDataSize - nFieldOffset) + && nReadBytes == 0 + && VSIFEofL( poModule->GetFP() ) ) + { + return FALSE; + } + else if( nReadBytes != (size_t) (nDataSize - nFieldOffset) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Data record is short on DDF file.\n" ); + + return FALSE; + } + + // notdef: eventually we may have to do something at this point to + // notify the DDFField's that their data values have changed. + + return TRUE; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +/** + * Write record out to module. + * + * This method writes the current record to the module to which it is + * attached. Normally this would be at the end of the file, and only used + * for modules newly created with DDFModule::Create(). Rewriting existing + * records is not supported at this time. Calling Write() multiple times + * on a DDFRecord will result it multiple copies being written at the end of + * the module. + * + * @return TRUE on success or FALSE on failure. + */ + +int DDFRecord::Write() + +{ + if( !ResetDirectory() ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Prepare leader. */ +/* -------------------------------------------------------------------- */ + char szLeader[nLeaderSize+1]; + + memset( szLeader, ' ', nLeaderSize ); + + sprintf( szLeader+0, "%05d", (int) (nDataSize + nLeaderSize) ); + szLeader[5] = ' '; + szLeader[6] = 'D'; + + sprintf( szLeader + 12, "%05d", (int) (nFieldOffset + nLeaderSize) ); + szLeader[17] = ' '; + + szLeader[20] = (char) ('0' + _sizeFieldLength); + szLeader[21] = (char) ('0' + _sizeFieldPos); + szLeader[22] = '0'; + szLeader[23] = (char) ('0' + _sizeFieldTag); + + /* notdef: lots of stuff missing */ + +/* -------------------------------------------------------------------- */ +/* Write the leader. */ +/* -------------------------------------------------------------------- */ + VSIFWriteL( szLeader, nLeaderSize, 1, poModule->GetFP() ); + +/* -------------------------------------------------------------------- */ +/* Write the remainder of the record. */ +/* -------------------------------------------------------------------- */ + VSIFWriteL( pachData, nDataSize, 1, poModule->GetFP() ); + + return TRUE; +} + +/************************************************************************/ +/* Clear() */ +/* */ +/* Clear any information associated with the last header in */ +/* preparation for reading a new header. */ +/************************************************************************/ + +void DDFRecord::Clear() + +{ + if( paoFields != NULL ) + delete[] paoFields; + + paoFields = NULL; + nFieldCount = 0; + + if( pachData != NULL ) + CPLFree( pachData ); + + pachData = NULL; + nDataSize = 0; + nReuseHeader = FALSE; +} + +/************************************************************************/ +/* ReadHeader() */ +/* */ +/* This perform the header reading and parsing job for the */ +/* Read() method. It reads the header, and builds a field */ +/* list. */ +/************************************************************************/ + +int DDFRecord::ReadHeader() + +{ +/* -------------------------------------------------------------------- */ +/* Clear any existing information. */ +/* -------------------------------------------------------------------- */ + Clear(); + +/* -------------------------------------------------------------------- */ +/* Read the 24 byte leader. */ +/* -------------------------------------------------------------------- */ + char achLeader[nLeaderSize]; + int nReadBytes; + + nReadBytes = VSIFReadL(achLeader,1,nLeaderSize,poModule->GetFP()); + if( nReadBytes == 0 && VSIFEofL( poModule->GetFP() ) ) + { + return FALSE; + } + else if( nReadBytes != (int) nLeaderSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Leader is short on DDF file." ); + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Extract information from leader. */ +/* -------------------------------------------------------------------- */ + int _recLength, _fieldAreaStart; + char _leaderIden; + + _recLength = DDFScanInt( achLeader+0, 5 ); + _leaderIden = achLeader[6]; + _fieldAreaStart = DDFScanInt(achLeader+12,5); + + _sizeFieldLength = achLeader[20] - '0'; + _sizeFieldPos = achLeader[21] - '0'; + _sizeFieldTag = achLeader[23] - '0'; + + if( _sizeFieldLength < 0 || _sizeFieldLength > 9 + || _sizeFieldPos < 0 || _sizeFieldPos > 9 + || _sizeFieldTag < 0 || _sizeFieldTag > 9 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "ISO8211 record leader appears to be corrupt." ); + return FALSE; + } + + if( _leaderIden == 'R' ) + nReuseHeader = TRUE; + + nFieldOffset = _fieldAreaStart - nLeaderSize; + +/* -------------------------------------------------------------------- */ +/* Is there anything seemly screwy about this record? */ +/* -------------------------------------------------------------------- */ + if(( _recLength <= 24 || _recLength > 100000000 + || _fieldAreaStart < 24 || _fieldAreaStart > 100000 ) + && (_recLength != 0)) + { + CPLError( CE_Failure, CPLE_FileIO, + "Data record appears to be corrupt on DDF file.\n" + " -- ensure that the files were uncompressed without modifying\n" + "carriage return/linefeeds (by default WINZIP does this)." ); + + return FALSE; + } + +/* ==================================================================== */ +/* Handle the normal case with the record length available. */ +/* ==================================================================== */ + if(_recLength != 0) { +/* -------------------------------------------------------------------- */ +/* Read the remainder of the record. */ +/* -------------------------------------------------------------------- */ + nDataSize = _recLength - nLeaderSize; + pachData = (char *) CPLMalloc(nDataSize); + + if( VSIFReadL( pachData, 1, nDataSize, poModule->GetFP()) != + (size_t) nDataSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Data record is short on DDF file." ); + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If we don't find a field terminator at the end of the record */ +/* we will read extra bytes till we get to it. */ +/* -------------------------------------------------------------------- */ + while( pachData[nDataSize-1] != DDF_FIELD_TERMINATOR + && (nDataSize < 2 || pachData[nDataSize-2] != DDF_FIELD_TERMINATOR) ) + { + nDataSize++; + pachData = (char *) CPLRealloc(pachData,nDataSize); + + if( VSIFReadL( pachData + nDataSize - 1, 1, 1, poModule->GetFP() ) + != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Data record is short on DDF file." ); + + return FALSE; + } + CPLDebug( "ISO8211", + "Didn't find field terminator, read one more byte." ); + } + +/* -------------------------------------------------------------------- */ +/* Loop over the directory entries, making a pass counting them. */ +/* -------------------------------------------------------------------- */ + int i; + int nFieldEntryWidth; + + nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag; + if( nFieldEntryWidth <= 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Invalid entry width = %d", nFieldEntryWidth); + return FALSE; + } + + nFieldCount = 0; + for( i = 0; i < nDataSize; i += nFieldEntryWidth ) + { + if( pachData[i] == DDF_FIELD_TERMINATOR ) + break; + + nFieldCount++; + } + +/* -------------------------------------------------------------------- */ +/* Allocate, and read field definitions. */ +/* -------------------------------------------------------------------- */ + paoFields = new DDFField[nFieldCount]; + + for( i = 0; i < nFieldCount; i++ ) + { + char szTag[128]; + int nEntryOffset = i*nFieldEntryWidth; + int nFieldLength, nFieldPos; + +/* -------------------------------------------------------------------- */ +/* Read the position information and tag. */ +/* -------------------------------------------------------------------- */ + strncpy( szTag, pachData+nEntryOffset, _sizeFieldTag ); + szTag[_sizeFieldTag] = '\0'; + + nEntryOffset += _sizeFieldTag; + nFieldLength = DDFScanInt( pachData+nEntryOffset, _sizeFieldLength ); + + nEntryOffset += _sizeFieldLength; + nFieldPos = DDFScanInt( pachData+nEntryOffset, _sizeFieldPos ); + +/* -------------------------------------------------------------------- */ +/* Find the corresponding field in the module directory. */ +/* -------------------------------------------------------------------- */ + DDFFieldDefn *poFieldDefn = poModule->FindFieldDefn( szTag ); + + if( poFieldDefn == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Undefined field `%s' encountered in data record.", + szTag ); + return FALSE; + } + + if (_fieldAreaStart + nFieldPos - nLeaderSize < 0 || + nDataSize - (_fieldAreaStart + nFieldPos - nLeaderSize) < nFieldLength) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Not enough byte to initialize field `%s'.", + szTag ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Assign info the DDFField. */ +/* -------------------------------------------------------------------- */ + paoFields[i].Initialize( poFieldDefn, + pachData + _fieldAreaStart + nFieldPos - nLeaderSize, + nFieldLength ); + } + + return TRUE; + } +/* ==================================================================== */ +/* Handle the exceptional case where the record length is */ +/* zero. In this case we have to read all the data based on */ +/* the size of data items as per ISO8211 spec Annex C, 1.5.1. */ +/* */ +/* See Bugzilla bug 181 and test with file US4CN21M.000. */ +/* ==================================================================== */ + else { + CPLDebug( "ISO8211", + "Record with zero length, use variant (C.1.5.1) logic." ); + + /* ----------------------------------------------------------------- */ + /* _recLength == 0, handle the large record. */ + /* */ + /* Read the remainder of the record. */ + /* ----------------------------------------------------------------- */ + nDataSize = 0; + pachData = NULL; + + /* ----------------------------------------------------------------- */ + /* Loop over the directory entries, making a pass counting them. */ + /* ----------------------------------------------------------------- */ + int nFieldEntryWidth = _sizeFieldLength + _sizeFieldPos + _sizeFieldTag; + nFieldCount = 0; + int i=0; + + if (nFieldEntryWidth == 0) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Invalid record buffer size : %d.", + nFieldEntryWidth ); + return FALSE; + } + + char *tmpBuf = (char*)VSIMalloc(nFieldEntryWidth); + + if( tmpBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Attempt to allocate %d byte ISO8211 record buffer failed.", + nFieldEntryWidth ); + return FALSE; + } + + // while we're not at the end, store this entry, + // and keep on reading... + do { + // read an Entry: + if(nFieldEntryWidth != + (int) VSIFReadL(tmpBuf, 1, nFieldEntryWidth, poModule->GetFP())) { + CPLError(CE_Failure, CPLE_FileIO, + "Data record is short on DDF file."); + CPLFree(tmpBuf); + return FALSE; + } + + // move this temp buffer into more permanent storage: + char *newBuf = (char*)CPLMalloc(nDataSize+nFieldEntryWidth); + if(pachData!=NULL) { + memcpy(newBuf, pachData, nDataSize); + CPLFree(pachData); + } + memcpy(&newBuf[nDataSize], tmpBuf, nFieldEntryWidth); + pachData = newBuf; + nDataSize += nFieldEntryWidth; + + if(DDF_FIELD_TERMINATOR != tmpBuf[0]) { + nFieldCount++; + } + } + while(DDF_FIELD_TERMINATOR != tmpBuf[0]); + + CPLFree(tmpBuf); + tmpBuf = NULL; + + // -------------------------------------------------------------------- + // Now, rewind a little. Only the TERMINATOR should have been read + // -------------------------------------------------------------------- + int rewindSize = nFieldEntryWidth - 1; + VSILFILE *fp = poModule->GetFP(); + vsi_l_offset pos = VSIFTellL(fp) - rewindSize; + VSIFSeekL(fp, pos, SEEK_SET); + nDataSize -= rewindSize; + + // -------------------------------------------------------------------- + // Okay, now let's populate the heck out of pachData... + // -------------------------------------------------------------------- + for(i=0; i= 0 ) + tmpBuf = (char*)VSIMalloc(nFieldLength); + if( tmpBuf == NULL ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate %d bytes", nFieldLength); + return FALSE; + } + + // read an Entry: + if(nFieldLength != + (int) VSIFReadL(tmpBuf, 1, nFieldLength, poModule->GetFP())) { + CPLError(CE_Failure, CPLE_FileIO, + "Data record is short on DDF file."); + CPLFree(tmpBuf); + return FALSE; + } + + // move this temp buffer into more permanent storage: + char *newBuf = (char*)VSIMalloc(nDataSize+nFieldLength); + if( newBuf == NULL ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate %d bytes", nDataSize + nFieldLength); + CPLFree(tmpBuf); + return FALSE; + } + memcpy(newBuf, pachData, nDataSize); + CPLFree(pachData); + memcpy(&newBuf[nDataSize], tmpBuf, nFieldLength); + CPLFree(tmpBuf); + pachData = newBuf; + nDataSize += nFieldLength; + } + + /* ----------------------------------------------------------------- */ + /* Allocate, and read field definitions. */ + /* ----------------------------------------------------------------- */ + paoFields = new DDFField[nFieldCount]; + + for( i = 0; i < nFieldCount; i++ ) + { + char szTag[128]; + int nEntryOffset = i*nFieldEntryWidth; + int nFieldLength, nFieldPos; + + /* ------------------------------------------------------------- */ + /* Read the position information and tag. */ + /* ------------------------------------------------------------- */ + strncpy( szTag, pachData+nEntryOffset, _sizeFieldTag ); + szTag[_sizeFieldTag] = '\0'; + + nEntryOffset += _sizeFieldTag; + nFieldLength = DDFScanInt( pachData+nEntryOffset, _sizeFieldLength ); + + nEntryOffset += _sizeFieldLength; + nFieldPos = DDFScanInt( pachData+nEntryOffset, _sizeFieldPos ); + + /* ------------------------------------------------------------- */ + /* Find the corresponding field in the module directory. */ + /* ------------------------------------------------------------- */ + DDFFieldDefn *poFieldDefn = poModule->FindFieldDefn( szTag ); + + if( poFieldDefn == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Undefined field `%s' encountered in data record.", + szTag ); + return FALSE; + } + + if (_fieldAreaStart + nFieldPos - nLeaderSize < 0 || + nDataSize - (_fieldAreaStart + nFieldPos - nLeaderSize) < nFieldLength) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Not enough byte to initialize field `%s'.", + szTag ); + return FALSE; + } + + /* ------------------------------------------------------------- */ + /* Assign info the DDFField. */ + /* ------------------------------------------------------------- */ + + paoFields[i].Initialize( poFieldDefn, + pachData + _fieldAreaStart + + nFieldPos - nLeaderSize, + nFieldLength ); + } + + return TRUE; + } +} + +/************************************************************************/ +/* FindField() */ +/************************************************************************/ + +/** + * Find the named field within this record. + * + * @param pszName The name of the field to fetch. The comparison is + * case insensitive. + * @param iFieldIndex The instance of this field to fetch. Use zero (the + * default) for the first instance. + * + * @return Pointer to the requested DDFField. This pointer is to an + * internal object, and should not be freed. It remains valid until + * the next record read. + */ + +DDFField * DDFRecord::FindField( const char * pszName, int iFieldIndex ) + +{ + for( int i = 0; i < nFieldCount; i++ ) + { + if( EQUAL(paoFields[i].GetFieldDefn()->GetName(),pszName) ) + { + if( iFieldIndex == 0 ) + return paoFields + i; + else + iFieldIndex--; + } + } + + return NULL; +} + +/************************************************************************/ +/* GetField() */ +/************************************************************************/ + +/** + * Fetch field object based on index. + * + * @param i The index of the field to fetch. Between 0 and GetFieldCount()-1. + * + * @return A DDFField pointer, or NULL if the index is out of range. + */ + +DDFField *DDFRecord::GetField( int i ) + +{ + if( i < 0 || i >= nFieldCount ) + return NULL; + else + return paoFields + i; +} + +/************************************************************************/ +/* GetIntSubfield() */ +/************************************************************************/ + +/** + * Fetch value of a subfield as an integer. This is a convenience + * function for fetching a subfield of a field within this record. + * + * @param pszField The name of the field containing the subfield. + * @param iFieldIndex The instance of this field within the record. Use + * zero for the first instance of this field. + * @param pszSubfield The name of the subfield within the selected field. + * @param iSubfieldIndex The instance of this subfield within the record. + * Use zero for the first instance. + * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch + * succeeds, or FALSE if it fails. Use NULL if you don't want to check + * success. + * @return The value of the subfield, or zero if it failed for some reason. + */ + +int DDFRecord::GetIntSubfield( const char * pszField, int iFieldIndex, + const char * pszSubfield, int iSubfieldIndex, + int * pnSuccess ) + +{ + DDFField *poField; + int nDummyErr; + + if( pnSuccess == NULL ) + pnSuccess = &nDummyErr; + + *pnSuccess = FALSE; + +/* -------------------------------------------------------------------- */ +/* Fetch the field. If this fails, return zero. */ +/* -------------------------------------------------------------------- */ + poField = FindField( pszField, iFieldIndex ); + if( poField == NULL ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Get the subfield definition */ +/* -------------------------------------------------------------------- */ + DDFSubfieldDefn *poSFDefn; + + poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield ); + if( poSFDefn == NULL ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Get a pointer to the data. */ +/* -------------------------------------------------------------------- */ + int nBytesRemaining; + + const char *pachData = poField->GetSubfieldData(poSFDefn, + &nBytesRemaining, + iSubfieldIndex); + +/* -------------------------------------------------------------------- */ +/* Return the extracted value. */ +/* */ +/* Assume an error has occured if no bytes are consumed. */ +/* -------------------------------------------------------------------- */ + int nConsumedBytes = 0; + int nResult = poSFDefn->ExtractIntData( pachData, nBytesRemaining, + &nConsumedBytes ); + + if( nConsumedBytes > 0 ) + *pnSuccess = TRUE; + + return nResult; +} + +/************************************************************************/ +/* GetFloatSubfield() */ +/************************************************************************/ + +/** + * Fetch value of a subfield as a float (double). This is a convenience + * function for fetching a subfield of a field within this record. + * + * @param pszField The name of the field containing the subfield. + * @param iFieldIndex The instance of this field within the record. Use + * zero for the first instance of this field. + * @param pszSubfield The name of the subfield within the selected field. + * @param iSubfieldIndex The instance of this subfield within the record. + * Use zero for the first instance. + * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch + * succeeds, or FALSE if it fails. Use NULL if you don't want to check + * success. + * @return The value of the subfield, or zero if it failed for some reason. + */ + +double DDFRecord::GetFloatSubfield( const char * pszField, int iFieldIndex, + const char * pszSubfield, int iSubfieldIndex, + int * pnSuccess ) + +{ + DDFField *poField; + int nDummyErr; + + if( pnSuccess == NULL ) + pnSuccess = &nDummyErr; + + *pnSuccess = FALSE; + +/* -------------------------------------------------------------------- */ +/* Fetch the field. If this fails, return zero. */ +/* -------------------------------------------------------------------- */ + poField = FindField( pszField, iFieldIndex ); + if( poField == NULL ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Get the subfield definition */ +/* -------------------------------------------------------------------- */ + DDFSubfieldDefn *poSFDefn; + + poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield ); + if( poSFDefn == NULL ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Get a pointer to the data. */ +/* -------------------------------------------------------------------- */ + int nBytesRemaining; + + const char *pachData = poField->GetSubfieldData(poSFDefn, + &nBytesRemaining, + iSubfieldIndex); + +/* -------------------------------------------------------------------- */ +/* Return the extracted value. */ +/* -------------------------------------------------------------------- */ + int nConsumedBytes = 0; + double dfResult = poSFDefn->ExtractFloatData( pachData, nBytesRemaining, + &nConsumedBytes ); + + if( nConsumedBytes > 0 ) + *pnSuccess = TRUE; + + return dfResult; +} + +/************************************************************************/ +/* GetStringSubfield() */ +/************************************************************************/ + +/** + * Fetch value of a subfield as a string. This is a convenience + * function for fetching a subfield of a field within this record. + * + * @param pszField The name of the field containing the subfield. + * @param iFieldIndex The instance of this field within the record. Use + * zero for the first instance of this field. + * @param pszSubfield The name of the subfield within the selected field. + * @param iSubfieldIndex The instance of this subfield within the record. + * Use zero for the first instance. + * @param pnSuccess Pointer to an int which will be set to TRUE if the fetch + * succeeds, or FALSE if it fails. Use NULL if you don't want to check + * success. + * @return The value of the subfield, or NULL if it failed for some reason. + * The returned pointer is to internal data and should not be modified or + * freed by the application. + */ + +const char * +DDFRecord::GetStringSubfield( const char * pszField, int iFieldIndex, + const char * pszSubfield, int iSubfieldIndex, + int * pnSuccess ) + +{ + DDFField *poField; + int nDummyErr; + + if( pnSuccess == NULL ) + pnSuccess = &nDummyErr; + + *pnSuccess = FALSE; + +/* -------------------------------------------------------------------- */ +/* Fetch the field. If this fails, return zero. */ +/* -------------------------------------------------------------------- */ + poField = FindField( pszField, iFieldIndex ); + if( poField == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get the subfield definition */ +/* -------------------------------------------------------------------- */ + DDFSubfieldDefn *poSFDefn; + + poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield ); + if( poSFDefn == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get a pointer to the data. */ +/* -------------------------------------------------------------------- */ + int nBytesRemaining; + + const char *pachData = poField->GetSubfieldData(poSFDefn, + &nBytesRemaining, + iSubfieldIndex); + +/* -------------------------------------------------------------------- */ +/* Return the extracted value. */ +/* -------------------------------------------------------------------- */ + *pnSuccess = TRUE; + + return( poSFDefn->ExtractStringData( pachData, nBytesRemaining, NULL ) ); +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +/** + * Make a copy of a record. + * + * This method is used to make a copy of a record that will become (mostly) + * the properly of application. However, it is automatically destroyed if + * the DDFModule it was created relative to is destroyed, as it's field + * and subfield definitions relate to that DDFModule. However, it does + * persist even when the record returned by DDFModule::ReadRecord() is + * invalidated, such as when reading a new record. This allows an application + * to cache whole DDFRecords. + * + * @return A new copy of the DDFRecord. This can be delete'd by the + * application when no longer needed, otherwise it will be cleaned up when + * the DDFModule it relates to is destroyed or closed. + */ + +DDFRecord * DDFRecord::Clone() + +{ + DDFRecord *poNR; + + poNR = new DDFRecord( poModule ); + + poNR->nReuseHeader = FALSE; + poNR->nFieldOffset = nFieldOffset; + + poNR->nDataSize = nDataSize; + poNR->pachData = (char *) CPLMalloc(nDataSize); + memcpy( poNR->pachData, pachData, nDataSize ); + + poNR->nFieldCount = nFieldCount; + poNR->paoFields = new DDFField[nFieldCount]; + for( int i = 0; i < nFieldCount; i++ ) + { + int nOffset; + + nOffset = (paoFields[i].GetData() - pachData); + poNR->paoFields[i].Initialize( paoFields[i].GetFieldDefn(), + poNR->pachData + nOffset, + paoFields[i].GetDataSize() ); + } + + poNR->bIsClone = TRUE; + poModule->AddCloneRecord( poNR ); + + return poNR; +} + +/************************************************************************/ +/* CloneOn() */ +/************************************************************************/ + +/** + * Recreate a record referencing another module. + * + * Works similarly to the DDFRecord::Clone() method, but creates the + * new record with reference to a different DDFModule. All DDFFieldDefn + * references are transcribed onto the new module based on field names. + * If any fields don't have a similarly named field on the target module + * the operation will fail. No validation of field types and properties + * is done, but this operation is intended only to be used between + * modules with matching definitions of all affected fields. + * + * The new record will be managed as a clone by the target module in + * a manner similar to regular clones. + * + * @param poTargetModule the module on which the record copy should be + * created. + * + * @return NULL on failure or a pointer to the cloned record. + */ + +DDFRecord *DDFRecord::CloneOn( DDFModule *poTargetModule ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify that all fields have a corresponding field definition */ +/* on the target module. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nFieldCount; i++ ) + { + DDFFieldDefn *poDefn = paoFields[i].GetFieldDefn(); + + if( poTargetModule->FindFieldDefn( poDefn->GetName() ) == NULL ) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a clone. */ +/* -------------------------------------------------------------------- */ + DDFRecord *poClone; + + poClone = Clone(); + +/* -------------------------------------------------------------------- */ +/* Update all internal information to reference other module. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nFieldCount; i++ ) + { + DDFField *poField = poClone->paoFields+i; + DDFFieldDefn *poDefn; + + poDefn = poTargetModule->FindFieldDefn( + poField->GetFieldDefn()->GetName() ); + + poField->Initialize( poDefn, poField->GetData(), + poField->GetDataSize() ); + } + + poModule->RemoveCloneRecord( poClone ); + poClone->poModule = poTargetModule; + poTargetModule->AddCloneRecord( poClone ); + + return poClone; +} + + +/************************************************************************/ +/* DeleteField() */ +/************************************************************************/ + +/** + * Delete a field instance from a record. + * + * Remove a field from this record, cleaning up the data + * portion and repacking the fields list. We don't try to + * reallocate the data area of the record to be smaller. + * + * NOTE: This method doesn't actually remove the header + * information for this field from the record tag list yet. + * This should be added if the resulting record is even to be + * written back to disk! + * + * @param poTarget the field instance on this record to delete. + * + * @return TRUE on success, or FALSE on failure. Failure can occur if + * poTarget isn't really a field on this record. + */ + +int DDFRecord::DeleteField( DDFField *poTarget ) + +{ + int iTarget, i; + +/* -------------------------------------------------------------------- */ +/* Find which field we are to delete. */ +/* -------------------------------------------------------------------- */ + for( iTarget = 0; iTarget < nFieldCount; iTarget++ ) + { + if( paoFields + iTarget == poTarget ) + break; + } + + if( iTarget == nFieldCount ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Change the target fields data size to zero. This takes care */ +/* of repacking the data array, and updating all the following */ +/* field data pointers. */ +/* -------------------------------------------------------------------- */ + ResizeField( poTarget, 0 ); + +/* -------------------------------------------------------------------- */ +/* remove the target field, moving down all the other fields */ +/* one step in the field list. */ +/* -------------------------------------------------------------------- */ + for( i = iTarget; i < nFieldCount-1; i++ ) + { + paoFields[i] = paoFields[i+1]; + } + + nFieldCount--; + + return TRUE; +} + +/************************************************************************/ +/* ResizeField() */ +/************************************************************************/ + +/** + * Alter field data size within record. + * + * This method will rearrange a DDFRecord altering the amount of space + * reserved for one of the existing fields. All following fields will + * be shifted accordingly. This includes updating the DDFField infos, + * and actually moving stuff within the data array after reallocating + * to the desired size. + * + * @param poField the field to alter. + * @param nNewDataSize the number of data bytes to be reserved for the field. + * + * @return TRUE on success or FALSE on failure. + */ + +int DDFRecord::ResizeField( DDFField *poField, int nNewDataSize ) + +{ + int iTarget, i; + int nBytesToMove; + +/* -------------------------------------------------------------------- */ +/* Find which field we are to resize. */ +/* -------------------------------------------------------------------- */ + for( iTarget = 0; iTarget < nFieldCount; iTarget++ ) + { + if( paoFields + iTarget == poField ) + break; + } + + if( iTarget == nFieldCount ) + { + CPLAssert( FALSE ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Reallocate the data buffer accordingly. */ +/* -------------------------------------------------------------------- */ + int nBytesToAdd = nNewDataSize - poField->GetDataSize(); + const char *pachOldData = pachData; + + // Don't realloc things smaller ... we will cut off some data. + if( nBytesToAdd > 0 ) + pachData = (char *) CPLRealloc(pachData, nDataSize + nBytesToAdd ); + + nDataSize += nBytesToAdd; + +/* -------------------------------------------------------------------- */ +/* How much data needs to be shifted up or down after this field? */ +/* -------------------------------------------------------------------- */ + nBytesToMove = nDataSize + - (poField->GetData()+poField->GetDataSize()-pachOldData+nBytesToAdd); + +/* -------------------------------------------------------------------- */ +/* Update fields to point into newly allocated buffer. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nFieldCount; i++ ) + { + int nOffset; + + nOffset = paoFields[i].GetData() - pachOldData; + paoFields[i].Initialize( paoFields[i].GetFieldDefn(), + pachData + nOffset, + paoFields[i].GetDataSize() ); + } + +/* -------------------------------------------------------------------- */ +/* Shift the data beyond this field up or down as needed. */ +/* -------------------------------------------------------------------- */ + if( nBytesToMove > 0 ) + memmove( (char *)poField->GetData()+poField->GetDataSize()+nBytesToAdd, + (char *)poField->GetData()+poField->GetDataSize(), + nBytesToMove ); + +/* -------------------------------------------------------------------- */ +/* Update the target fields info. */ +/* -------------------------------------------------------------------- */ + poField->Initialize( poField->GetFieldDefn(), + poField->GetData(), + poField->GetDataSize() + nBytesToAdd ); + +/* -------------------------------------------------------------------- */ +/* Shift all following fields down, and update their data */ +/* locations. */ +/* -------------------------------------------------------------------- */ + if( nBytesToAdd < 0 ) + { + for( i = iTarget+1; i < nFieldCount; i++ ) + { + char *pszOldDataLocation; + + pszOldDataLocation = (char *) paoFields[i].GetData(); + + paoFields[i].Initialize( paoFields[i].GetFieldDefn(), + pszOldDataLocation + nBytesToAdd, + paoFields[i].GetDataSize() ); + } + } + else + { + for( i = nFieldCount-1; i > iTarget; i-- ) + { + char *pszOldDataLocation; + + pszOldDataLocation = (char *) paoFields[i].GetData(); + + paoFields[i].Initialize( paoFields[i].GetFieldDefn(), + pszOldDataLocation + nBytesToAdd, + paoFields[i].GetDataSize() ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* AddField() */ +/************************************************************************/ + +/** + * Add a new field to record. + * + * Add a new zero sized field to the record. The new field is always + * added at the end of the record. + * + * NOTE: This method doesn't currently update the header information for + * the record to include the field information for this field, so the + * resulting record image isn't suitable for writing to disk. However, + * everything else about the record state should be updated properly to + * reflect the new field. + * + * @param poDefn the definition of the field to be added. + * + * @return the field object on success, or NULL on failure. + */ + +DDFField *DDFRecord::AddField( DDFFieldDefn *poDefn ) + +{ +/* -------------------------------------------------------------------- */ +/* Reallocate the fields array larger by one, and initialize */ +/* the new field. */ +/* -------------------------------------------------------------------- */ + DDFField *paoNewFields; + + paoNewFields = new DDFField[nFieldCount+1]; + if( nFieldCount > 0 ) + { + memcpy( paoNewFields, paoFields, sizeof(DDFField) * nFieldCount ); + delete[] paoFields; + } + paoFields = paoNewFields; + nFieldCount++; + +/* -------------------------------------------------------------------- */ +/* Initialize the new field properly. */ +/* -------------------------------------------------------------------- */ + if( nFieldCount == 1 ) + { + paoFields[0].Initialize( poDefn, GetData(), 0 ); + } + else + { + paoFields[nFieldCount-1].Initialize( + poDefn, + paoFields[nFieldCount-2].GetData() + + paoFields[nFieldCount-2].GetDataSize(), + 0 ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize field. */ +/* -------------------------------------------------------------------- */ + CreateDefaultFieldInstance( paoFields + nFieldCount-1, 0 ); + + return paoFields + (nFieldCount - 1); +} + +/************************************************************************/ +/* SetFieldRaw() */ +/************************************************************************/ + +/** + * Set the raw contents of a field instance. + * + * @param poField the field to set data within. + * @param iIndexWithinField The instance of this field to replace. Must + * be a value between 0 and GetRepeatCount(). If GetRepeatCount() is used, a + * new instance of the field is appeneded. + * @param pachRawData the raw data to replace this field instance with. + * @param nRawDataSize the number of bytes pointed to by pachRawData. + * + * @return TRUE on success or FALSE on failure. + */ + +int +DDFRecord::SetFieldRaw( DDFField *poField, int iIndexWithinField, + const char *pachRawData, int nRawDataSize ) + +{ + int iTarget, nRepeatCount; + +/* -------------------------------------------------------------------- */ +/* Find which field we are to update. */ +/* -------------------------------------------------------------------- */ + for( iTarget = 0; iTarget < nFieldCount; iTarget++ ) + { + if( paoFields + iTarget == poField ) + break; + } + + if( iTarget == nFieldCount ) + return FALSE; + + nRepeatCount = poField->GetRepeatCount(); + + if( iIndexWithinField < 0 || iIndexWithinField > nRepeatCount ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Are we adding an instance? This is easier and different */ +/* than replacing an existing instance. */ +/* -------------------------------------------------------------------- */ + if( iIndexWithinField == nRepeatCount + || !poField->GetFieldDefn()->IsRepeating() ) + { + char *pachFieldData; + int nOldSize; + + if( !poField->GetFieldDefn()->IsRepeating() && iIndexWithinField != 0 ) + return FALSE; + + nOldSize = poField->GetDataSize(); + if( nOldSize == 0 ) + nOldSize++; // for added DDF_FIELD_TERMINATOR. + + if( !ResizeField( poField, nOldSize + nRawDataSize ) ) + return FALSE; + + pachFieldData = (char *) poField->GetData(); + memcpy( pachFieldData + nOldSize - 1, + pachRawData, nRawDataSize ); + pachFieldData[nOldSize+nRawDataSize-1] = DDF_FIELD_TERMINATOR; + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Get a pointer to the start of the existing data for this */ +/* iteration of the field. */ +/* -------------------------------------------------------------------- */ + const char *pachWrkData; + int nInstanceSize; + + // We special case this to avoid alot of warnings when initializing + // the field the first time. + if( poField->GetDataSize() == 0 ) + { + pachWrkData = poField->GetData(); + nInstanceSize = 0; + } + else + { + pachWrkData = poField->GetInstanceData( iIndexWithinField, + &nInstanceSize ); + } + +/* -------------------------------------------------------------------- */ +/* Create new image of this whole field. */ +/* -------------------------------------------------------------------- */ + char *pachNewImage; + int nPreBytes, nPostBytes, nNewFieldSize; + + nNewFieldSize = poField->GetDataSize() - nInstanceSize + nRawDataSize; + + pachNewImage = (char *) CPLMalloc(nNewFieldSize); + + nPreBytes = pachWrkData - poField->GetData(); + nPostBytes = poField->GetDataSize() - nPreBytes - nInstanceSize; + + memcpy( pachNewImage, poField->GetData(), nPreBytes ); + memcpy( pachNewImage + nPreBytes + nRawDataSize, + poField->GetData() + nPreBytes + nInstanceSize, + nPostBytes ); + memcpy( pachNewImage + nPreBytes, pachRawData, nRawDataSize ); + +/* -------------------------------------------------------------------- */ +/* Resize the field to the desired new size. */ +/* -------------------------------------------------------------------- */ + ResizeField( poField, nNewFieldSize ); + + memcpy( (void *) poField->GetData(), pachNewImage, nNewFieldSize ); + CPLFree( pachNewImage ); + + return TRUE; +} + +/************************************************************************/ +/* UpdateFieldRaw() */ +/************************************************************************/ + +int +DDFRecord::UpdateFieldRaw( DDFField *poField, int iIndexWithinField, + int nStartOffset, int nOldSize, + const char *pachRawData, int nRawDataSize ) + +{ + int iTarget, nRepeatCount; + +/* -------------------------------------------------------------------- */ +/* Find which field we are to update. */ +/* -------------------------------------------------------------------- */ + for( iTarget = 0; iTarget < nFieldCount; iTarget++ ) + { + if( paoFields + iTarget == poField ) + break; + } + + if( iTarget == nFieldCount ) + return FALSE; + + nRepeatCount = poField->GetRepeatCount(); + + if( iIndexWithinField < 0 || iIndexWithinField >= nRepeatCount ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Figure out how much pre and post data there is. */ +/* -------------------------------------------------------------------- */ + char *pachWrkData; + int nInstanceSize, nPostBytes, nPreBytes; + + pachWrkData = (char *) poField->GetInstanceData( iIndexWithinField, + &nInstanceSize ); + nPreBytes = pachWrkData - poField->GetData() + nStartOffset; + nPostBytes = poField->GetDataSize() - nPreBytes - nOldSize; + +/* -------------------------------------------------------------------- */ +/* If we aren't changing the size, just copy over the existing */ +/* data. */ +/* -------------------------------------------------------------------- */ + if( nOldSize == nRawDataSize ) + { + memcpy( pachWrkData + nStartOffset, pachRawData, nRawDataSize ); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* If we are shrinking, move in the new data, and shuffle down */ +/* the old before resizing. */ +/* -------------------------------------------------------------------- */ + if( nRawDataSize < nOldSize ) + { + memcpy( ((char*) poField->GetData()) + nPreBytes, + pachRawData, nRawDataSize ); + memmove( ((char *) poField->GetData()) + nPreBytes + nRawDataSize, + ((char *) poField->GetData()) + nPreBytes + nOldSize, + nPostBytes ); + } + +/* -------------------------------------------------------------------- */ +/* Resize the whole buffer. */ +/* -------------------------------------------------------------------- */ + if( !ResizeField( poField, + poField->GetDataSize() - nOldSize + nRawDataSize ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* If we growing the buffer, shuffle up the post data, and */ +/* move in our new values. */ +/* -------------------------------------------------------------------- */ + if( nRawDataSize >= nOldSize ) + { + memmove( ((char *) poField->GetData()) + nPreBytes + nRawDataSize, + ((char *) poField->GetData()) + nPreBytes + nOldSize, + nPostBytes ); + memcpy( ((char*) poField->GetData()) + nPreBytes, + pachRawData, nRawDataSize ); + } + + return TRUE; +} + +/************************************************************************/ +/* ResetDirectory() */ +/* */ +/* Re-prepares the directory information for the record. */ +/************************************************************************/ + +int DDFRecord::ResetDirectory() + +{ + int iField; + +/* -------------------------------------------------------------------- */ +/* Eventually we should try to optimize the size of offset and */ +/* field length. For now we will use 5 for each which is */ +/* pretty big. */ +/* -------------------------------------------------------------------- */ + _sizeFieldPos = 5; + _sizeFieldLength = 5; + +/* -------------------------------------------------------------------- */ +/* Compute how large the directory needs to be. */ +/* -------------------------------------------------------------------- */ + int nEntrySize, nDirSize; + + nEntrySize = _sizeFieldPos + _sizeFieldLength + _sizeFieldTag; + nDirSize = nEntrySize * nFieldCount + 1; + +/* -------------------------------------------------------------------- */ +/* If the directory size is different than what is currently */ +/* reserved for it, we must resize. */ +/* -------------------------------------------------------------------- */ + if( nDirSize != nFieldOffset ) + { + char *pachNewData; + int nNewDataSize; + + nNewDataSize = nDataSize - nFieldOffset + nDirSize; + pachNewData = (char *) CPLMalloc(nNewDataSize); + memcpy( pachNewData + nDirSize, + pachData + nFieldOffset, + nNewDataSize - nDirSize ); + + for( iField = 0; iField < nFieldCount; iField++ ) + { + int nOffset; + DDFField *poField = GetField( iField ); + + nOffset = poField->GetData() - pachData - nFieldOffset + nDirSize; + poField->Initialize( poField->GetFieldDefn(), + pachNewData + nOffset, + poField->GetDataSize() ); + } + + CPLFree( pachData ); + pachData = pachNewData; + nDataSize = nNewDataSize; + nFieldOffset = nDirSize; + } + +/* -------------------------------------------------------------------- */ +/* Now set each directory entry. */ +/* -------------------------------------------------------------------- */ + for( iField = 0; iField < nFieldCount; iField++ ) + { + DDFField *poField = GetField( iField ); + DDFFieldDefn *poDefn = poField->GetFieldDefn(); + char szFormat[128]; + + sprintf( szFormat, "%%%ds%%0%dd%%0%dd", + _sizeFieldTag, _sizeFieldLength, _sizeFieldPos ); + + sprintf( pachData + nEntrySize * iField, szFormat, + poDefn->GetName(), poField->GetDataSize(), + poField->GetData() - pachData - nFieldOffset ); + } + + pachData[nEntrySize * nFieldCount] = DDF_FIELD_TERMINATOR; + + return TRUE; +} + +/************************************************************************/ +/* CreateDefaultFieldInstance() */ +/************************************************************************/ + +/** + * Initialize default instance. + * + * This method is normally only used internally by the AddField() method + * to initialize the new field instance with default subfield values. It + * installs default data for one instance of the field in the record + * using the DDFFieldDefn::GetDefaultValue() method and + * DDFRecord::SetFieldRaw(). + * + * @param poField the field within the record to be assign a default + * instance. + * @param iIndexWithinField the instance to set (may not have been tested with + * values other than 0). + * + * @return TRUE on success or FALSE on failure. + */ + +int DDFRecord::CreateDefaultFieldInstance( DDFField *poField, + int iIndexWithinField ) + +{ + int nRawSize, nSuccess; + char *pachRawData; + + pachRawData = poField->GetFieldDefn()->GetDefaultValue( &nRawSize ); + if( pachRawData == NULL ) + return FALSE; + + nSuccess = SetFieldRaw( poField, iIndexWithinField, pachRawData, nRawSize); + + CPLFree( pachRawData ); + + return nSuccess; +} + +/************************************************************************/ +/* SetStringSubfield() */ +/************************************************************************/ + +/** + * Set a string subfield in record. + * + * The value of a given subfield is replaced with a new string value + * formatted appropriately. + * + * @param pszField the field name to operate on. + * @param iFieldIndex the field index to operate on (zero based). + * @param pszSubfield the subfield name to operate on. + * @param iSubfieldIndex the subfield index to operate on (zero based). + * @param pszValue the new string to place in the subfield. This may be + * arbitrary binary bytes if nValueLength is specified. + * @param nValueLength the number of valid bytes in pszValue, may be -1 to + * internally fetch with strlen(). + * + * @return TRUE if successful, and FALSE if not. + */ + +int DDFRecord::SetStringSubfield( const char *pszField, int iFieldIndex, + const char *pszSubfield, int iSubfieldIndex, + const char *pszValue, int nValueLength ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch the field. If this fails, return zero. */ +/* -------------------------------------------------------------------- */ + DDFField *poField; + + poField = FindField( pszField, iFieldIndex ); + if( poField == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get the subfield definition */ +/* -------------------------------------------------------------------- */ + DDFSubfieldDefn *poSFDefn; + + poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield ); + if( poSFDefn == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* How long will the formatted value be? */ +/* -------------------------------------------------------------------- */ + int nFormattedLen; + + if( !poSFDefn->FormatStringValue( NULL, 0, &nFormattedLen, pszValue, + nValueLength ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get a pointer to the data. */ +/* -------------------------------------------------------------------- */ + int nMaxBytes; + char *pachSubfieldData = (char *) + poField->GetSubfieldData(poSFDefn, &nMaxBytes, + iSubfieldIndex); + +/* -------------------------------------------------------------------- */ +/* Add new instance if we have run out of data. */ +/* -------------------------------------------------------------------- */ + if( nMaxBytes == 0 + || (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR) ) + { + CreateDefaultFieldInstance( poField, iSubfieldIndex ); + + // Refetch. + pachSubfieldData = (char *) + poField->GetSubfieldData(poSFDefn, &nMaxBytes, + iSubfieldIndex); + } + +/* -------------------------------------------------------------------- */ +/* If the new length matches the existing length, just overlay */ +/* and return. */ +/* -------------------------------------------------------------------- */ + int nExistingLength; + + poSFDefn->GetDataLength( pachSubfieldData, nMaxBytes, &nExistingLength ); + + if( nExistingLength == nFormattedLen ) + { + return poSFDefn->FormatStringValue( pachSubfieldData, nFormattedLen, + NULL, pszValue, nValueLength ); + } + +/* -------------------------------------------------------------------- */ +/* We will need to resize the raw data. */ +/* -------------------------------------------------------------------- */ + const char *pachFieldInstData; + int nInstanceSize, nStartOffset, nSuccess; + char *pachNewData; + + pachFieldInstData = poField->GetInstanceData( iFieldIndex, + &nInstanceSize ); + + nStartOffset = pachSubfieldData - pachFieldInstData; + + pachNewData = (char *) CPLMalloc(nFormattedLen); + poSFDefn->FormatStringValue( pachNewData, nFormattedLen, NULL, + pszValue, nValueLength ); + + nSuccess = UpdateFieldRaw( poField, iFieldIndex, + nStartOffset, nExistingLength, + pachNewData, nFormattedLen ); + + CPLFree( pachNewData ); + + return nSuccess; +} + +/************************************************************************/ +/* SetIntSubfield() */ +/************************************************************************/ + +/** + * Set an integer subfield in record. + * + * The value of a given subfield is replaced with a new integer value + * formatted appropriately. + * + * @param pszField the field name to operate on. + * @param iFieldIndex the field index to operate on (zero based). + * @param pszSubfield the subfield name to operate on. + * @param iSubfieldIndex the subfield index to operate on (zero based). + * @param nNewValue the new value to place in the subfield. + * + * @return TRUE if successful, and FALSE if not. + */ + +int DDFRecord::SetIntSubfield( const char *pszField, int iFieldIndex, + const char *pszSubfield, int iSubfieldIndex, + int nNewValue ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch the field. If this fails, return zero. */ +/* -------------------------------------------------------------------- */ + DDFField *poField; + + poField = FindField( pszField, iFieldIndex ); + if( poField == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get the subfield definition */ +/* -------------------------------------------------------------------- */ + DDFSubfieldDefn *poSFDefn; + + poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield ); + if( poSFDefn == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* How long will the formatted value be? */ +/* -------------------------------------------------------------------- */ + int nFormattedLen; + + if( !poSFDefn->FormatIntValue( NULL, 0, &nFormattedLen, nNewValue ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get a pointer to the data. */ +/* -------------------------------------------------------------------- */ + int nMaxBytes; + char *pachSubfieldData = (char *) + poField->GetSubfieldData(poSFDefn, &nMaxBytes, + iSubfieldIndex); + +/* -------------------------------------------------------------------- */ +/* Add new instance if we have run out of data. */ +/* -------------------------------------------------------------------- */ + if( nMaxBytes == 0 + || (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR) ) + { + CreateDefaultFieldInstance( poField, iSubfieldIndex ); + + // Refetch. + pachSubfieldData = (char *) + poField->GetSubfieldData(poSFDefn, &nMaxBytes, + iSubfieldIndex); + } + +/* -------------------------------------------------------------------- */ +/* If the new length matches the existing length, just overlay */ +/* and return. */ +/* -------------------------------------------------------------------- */ + int nExistingLength; + + poSFDefn->GetDataLength( pachSubfieldData, nMaxBytes, &nExistingLength ); + + if( nExistingLength == nFormattedLen ) + { + return poSFDefn->FormatIntValue( pachSubfieldData, nFormattedLen, + NULL, nNewValue ); + } + +/* -------------------------------------------------------------------- */ +/* We will need to resize the raw data. */ +/* -------------------------------------------------------------------- */ + const char *pachFieldInstData; + int nInstanceSize, nStartOffset, nSuccess; + char *pachNewData; + + pachFieldInstData = poField->GetInstanceData( iFieldIndex, + &nInstanceSize ); + + nStartOffset = pachSubfieldData - pachFieldInstData; + + pachNewData = (char *) CPLMalloc(nFormattedLen); + poSFDefn->FormatIntValue( pachNewData, nFormattedLen, NULL, + nNewValue ); + + nSuccess = UpdateFieldRaw( poField, iFieldIndex, + nStartOffset, nExistingLength, + pachNewData, nFormattedLen ); + + CPLFree( pachNewData ); + + return nSuccess; +} + +/************************************************************************/ +/* SetFloatSubfield() */ +/************************************************************************/ + +/** + * Set a float subfield in record. + * + * The value of a given subfield is replaced with a new float value + * formatted appropriately. + * + * @param pszField the field name to operate on. + * @param iFieldIndex the field index to operate on (zero based). + * @param pszSubfield the subfield name to operate on. + * @param iSubfieldIndex the subfield index to operate on (zero based). + * @param dfNewValue the new value to place in the subfield. + * + * @return TRUE if successful, and FALSE if not. + */ + +int DDFRecord::SetFloatSubfield( const char *pszField, int iFieldIndex, + const char *pszSubfield, int iSubfieldIndex, + double dfNewValue ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch the field. If this fails, return zero. */ +/* -------------------------------------------------------------------- */ + DDFField *poField; + + poField = FindField( pszField, iFieldIndex ); + if( poField == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get the subfield definition */ +/* -------------------------------------------------------------------- */ + DDFSubfieldDefn *poSFDefn; + + poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield ); + if( poSFDefn == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* How long will the formatted value be? */ +/* -------------------------------------------------------------------- */ + int nFormattedLen; + + if( !poSFDefn->FormatFloatValue( NULL, 0, &nFormattedLen, dfNewValue ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get a pointer to the data. */ +/* -------------------------------------------------------------------- */ + int nMaxBytes; + char *pachSubfieldData = (char *) + poField->GetSubfieldData(poSFDefn, &nMaxBytes, + iSubfieldIndex); + +/* -------------------------------------------------------------------- */ +/* Add new instance if we have run out of data. */ +/* -------------------------------------------------------------------- */ + if( nMaxBytes == 0 + || (nMaxBytes == 1 && pachSubfieldData[0] == DDF_FIELD_TERMINATOR) ) + { + CreateDefaultFieldInstance( poField, iSubfieldIndex ); + + // Refetch. + pachSubfieldData = (char *) + poField->GetSubfieldData(poSFDefn, &nMaxBytes, + iSubfieldIndex); + } + +/* -------------------------------------------------------------------- */ +/* If the new length matches the existing length, just overlay */ +/* and return. */ +/* -------------------------------------------------------------------- */ + int nExistingLength; + + poSFDefn->GetDataLength( pachSubfieldData, nMaxBytes, &nExistingLength ); + + if( nExistingLength == nFormattedLen ) + { + return poSFDefn->FormatFloatValue( pachSubfieldData, nFormattedLen, + NULL, dfNewValue ); + } + +/* -------------------------------------------------------------------- */ +/* We will need to resize the raw data. */ +/* -------------------------------------------------------------------- */ + const char *pachFieldInstData; + int nInstanceSize, nStartOffset, nSuccess; + char *pachNewData; + + pachFieldInstData = poField->GetInstanceData( iFieldIndex, + &nInstanceSize ); + + nStartOffset = (int) (pachSubfieldData - pachFieldInstData); + + pachNewData = (char *) CPLMalloc(nFormattedLen); + poSFDefn->FormatFloatValue( pachNewData, nFormattedLen, NULL, + dfNewValue ); + + nSuccess = UpdateFieldRaw( poField, iFieldIndex, + nStartOffset, nExistingLength, + pachNewData, nFormattedLen ); + + CPLFree( pachNewData ); + + return nSuccess; +} diff --git a/bazaar/plugin/gdal/frmts/iso8211/ddfsubfielddefn.cpp b/bazaar/plugin/gdal/frmts/iso8211/ddfsubfielddefn.cpp new file mode 100644 index 000000000..341b31d31 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/ddfsubfielddefn.cpp @@ -0,0 +1,1002 @@ +/****************************************************************************** + * $Id: ddfsubfielddefn.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: ISO 8211 Access + * Purpose: Implements the DDFSubfieldDefn class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "iso8211.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ddfsubfielddefn.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* DDFSubfieldDefn() */ +/************************************************************************/ + +DDFSubfieldDefn::DDFSubfieldDefn() + +{ + pszName = NULL; + + bIsVariable = TRUE; + nFormatWidth = 0; + chFormatDelimeter = DDF_UNIT_TERMINATOR; + eBinaryFormat = NotBinary; + eType = DDFString; + + pszFormatString = CPLStrdup(""); + + nMaxBufChars = 0; + pachBuffer = NULL; +} + +/************************************************************************/ +/* ~DDFSubfieldDefn() */ +/************************************************************************/ + +DDFSubfieldDefn::~DDFSubfieldDefn() + +{ + CPLFree( pszName ); + CPLFree( pszFormatString ); + CPLFree( pachBuffer ); +} + +/************************************************************************/ +/* SetName() */ +/************************************************************************/ + +void DDFSubfieldDefn::SetName( const char * pszNewName ) + +{ + int i; + + CPLFree( pszName ); + + pszName = CPLStrdup( pszNewName ); + + for( i = strlen(pszName)-1; i > 0 && pszName[i] == ' '; i-- ) + pszName[i] = '\0'; +} + +/************************************************************************/ +/* SetFormat() */ +/* */ +/* While interpreting the format string we don't support: */ +/* */ +/* o Passing an explicit terminator for variable length field. */ +/* o 'X' for unused data ... this should really be filtered */ +/* out by DDFFieldDefn::ApplyFormats(), but isn't. */ +/* o 'B' bitstrings that aren't a multiple of eight. */ +/************************************************************************/ + +int DDFSubfieldDefn::SetFormat( const char * pszFormat ) + +{ + CPLFree( pszFormatString ); + pszFormatString = CPLStrdup( pszFormat ); + +/* -------------------------------------------------------------------- */ +/* These values will likely be used. */ +/* -------------------------------------------------------------------- */ + if( pszFormatString[1] == '(' ) + { + nFormatWidth = atoi(pszFormatString+2); + bIsVariable = nFormatWidth == 0; + } + else + bIsVariable = TRUE; + +/* -------------------------------------------------------------------- */ +/* Interpret the format string. */ +/* -------------------------------------------------------------------- */ + switch( pszFormatString[0] ) + { + case 'A': + case 'C': // It isn't clear to me how this is different than 'A' + eType = DDFString; + break; + + case 'R': + eType = DDFFloat; + break; + + case 'I': + case 'S': + eType = DDFInt; + break; + + case 'B': + case 'b': + // Is the width expressed in bits? (is it a bitstring) + bIsVariable = FALSE; + if( pszFormatString[1] == '(' ) + { + if( atoi(pszFormatString+2) % 8 != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Format width %s is invalid.", + pszFormatString+2 ); + return FALSE; + } + + nFormatWidth = atoi(pszFormatString+2) / 8; + eBinaryFormat = SInt; // good default, works for SDTS. + + if( nFormatWidth < 5 ) + eType = DDFInt; + else + eType = DDFBinaryString; + } + + // or do we have a binary type indicator? (is it binary) + else + { + eBinaryFormat = (DDFBinaryFormat) (pszFormatString[1] - '0'); + nFormatWidth = atoi(pszFormatString+2); + + if( eBinaryFormat == SInt || eBinaryFormat == UInt ) + eType = DDFInt; + else + eType = DDFFloat; + } + break; + + case 'X': + // 'X' is extra space, and shouldn't be directly assigned to a + // subfield ... I haven't encountered it in use yet though. + CPLError( CE_Failure, CPLE_AppDefined, + "Format type of `%c' not supported.\n", + pszFormatString[0] ); + + return FALSE; + + default: + CPLError( CE_Failure, CPLE_AppDefined, + "Format type of `%c' not recognised.\n", + pszFormatString[0] ); + + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +/** + * Write out subfield definition info to debugging file. + * + * A variety of information about this field definition is written to the + * give debugging file handle. + * + * @param fp The standard io file handle to write to. ie. stderr + */ + +void DDFSubfieldDefn::Dump( FILE * fp ) + +{ + fprintf( fp, " DDFSubfieldDefn:\n" ); + fprintf( fp, " Label = `%s'\n", pszName ); + fprintf( fp, " FormatString = `%s'\n", pszFormatString ); +} + +/************************************************************************/ +/* GetDataLength() */ +/* */ +/* This method will scan for the end of a variable field. */ +/************************************************************************/ + +/** + * Scan for the end of variable length data. Given a pointer to the data + * for this subfield (from within a DDFRecord) this method will return the + * number of bytes which are data for this subfield. The number of bytes + * consumed as part of this field can also be fetched. This number may + * be one longer than the length if there is a terminator character + * used.

    + * + * This method is mainly for internal use, or for applications which + * want the raw binary data to interpret themselves. Otherwise use one + * of ExtractStringData(), ExtractIntData() or ExtractFloatData(). + * + * @param pachSourceData The pointer to the raw data for this field. This + * may have come from DDFRecord::GetData(), taking into account skip factors + * over previous subfields data. + * @param nMaxBytes The maximum number of bytes that are accessable after + * pachSourceData. + * @param pnConsumedBytes Pointer to an integer into which the number of + * bytes consumed by this field should be written. May be NULL to ignore. + * + * @return The number of bytes at pachSourceData which are actual data for + * this record (not including unit, or field terminator). + */ + +int DDFSubfieldDefn::GetDataLength( const char * pachSourceData, + int nMaxBytes, int * pnConsumedBytes ) + +{ + if( !bIsVariable ) + { + if( nFormatWidth > nMaxBytes ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Only %d bytes available for subfield %s with\n" + "format string %s ... returning shortened data.", + nMaxBytes, pszName, pszFormatString ); + + if( pnConsumedBytes != NULL ) + *pnConsumedBytes = nMaxBytes; + + return nMaxBytes; + } + else + { + if( pnConsumedBytes != NULL ) + *pnConsumedBytes = nFormatWidth; + + return nFormatWidth; + } + } + else + { + int nLength = 0; + int bAsciiField = TRUE; + int extraConsumedBytes = 0; + + /* We only check for the field terminator because of some buggy + * datasets with missing format terminators. However, we have found + * the field terminator and unit terminators are legal characters + * within the fields of some extended datasets (such as JP34NC94.000). + * So we don't check for the field terminator and unit terminators as + * a single byte if the field appears to be multi-byte which we + * establish by checking for the buffer ending with 0x1e 0x00 (a + * two byte field terminator). + * + * In the case of S57, the subfield ATVL of the NATF field can be + * encoded in lexical level 2 (see S57 specification, Edition 3.1, + * paragraph 2.4 and 2.5). In that case the Unit Terminator and Field + * Terminator are followed by the NULL character. + * A better fix would be to read the NALL tag in the DSSI to check + * that the lexical level is 2, instead of relying on the value of + * the first byte as we are doing - but that is not information + * that is available at the libiso8211 level (bug #1526) + */ + + // If the whole field ends with 0x1e 0x00 then we assume this + // field is a double byte character set. + if( nMaxBytes > 1 + && (pachSourceData[nMaxBytes-2] == chFormatDelimeter + || pachSourceData[nMaxBytes-2] == DDF_FIELD_TERMINATOR) + && pachSourceData[nMaxBytes-1] == 0x00 ) + bAsciiField = FALSE; + +// if( !bAsciiField ) +// CPLDebug( "ISO8211", "Non-ASCII field detected." ); + + while( nLength < nMaxBytes) + { + if (bAsciiField) + { + if (pachSourceData[nLength] == chFormatDelimeter || + pachSourceData[nLength] == DDF_FIELD_TERMINATOR) + break; + } + else + { + if (nLength > 0 + && (pachSourceData[nLength-1] == chFormatDelimeter + || pachSourceData[nLength-1] == DDF_FIELD_TERMINATOR) + && pachSourceData[nLength] == 0) + { + // Suck up the field terminator if one follows + // or else it will be interpreted as a new subfield. + // This is a pretty ugly counter-intuitive hack! + if (nLength+1 < nMaxBytes && + pachSourceData[nLength+1] == DDF_FIELD_TERMINATOR) + extraConsumedBytes++; + break; + } + } + + nLength++; + } + + if( pnConsumedBytes != NULL ) + { + if( nMaxBytes == 0 ) + *pnConsumedBytes = nLength + extraConsumedBytes; + else + *pnConsumedBytes = nLength + extraConsumedBytes + 1; + } + + return nLength; + } +} + +/************************************************************************/ +/* ExtractStringData() */ +/************************************************************************/ + +/** + * Extract a zero terminated string containing the data for this subfield. + * Given a pointer to the data + * for this subfield (from within a DDFRecord) this method will return the + * data for this subfield. The number of bytes + * consumed as part of this field can also be fetched. This number may + * be one longer than the string length if there is a terminator character + * used.

    + * + * This function will return the raw binary data of a subfield for + * types other than DDFString, including data past zero chars. This is + * the standard way of extracting DDFBinaryString subfields for instance.

    + * + * @param pachSourceData The pointer to the raw data for this field. This + * may have come from DDFRecord::GetData(), taking into account skip factors + * over previous subfields data. + * @param nMaxBytes The maximum number of bytes that are accessable after + * pachSourceData. + * @param pnConsumedBytes Pointer to an integer into which the number of + * bytes consumed by this field should be written. May be NULL to ignore. + * This is used as a skip factor to increment pachSourceData to point to the + * next subfields data. + * + * @return A pointer to a buffer containing the data for this field. The + * returned pointer is to an internal buffer which is invalidated on the + * next ExtractStringData() call on this DDFSubfieldDefn(). It should not + * be freed by the application. + * + * @see ExtractIntData(), ExtractFloatData() + */ + +const char * +DDFSubfieldDefn::ExtractStringData( const char * pachSourceData, + int nMaxBytes, int * pnConsumedBytes ) + +{ + int nLength = GetDataLength( pachSourceData, nMaxBytes, + pnConsumedBytes ); + +/* -------------------------------------------------------------------- */ +/* Do we need to grow the buffer. */ +/* -------------------------------------------------------------------- */ + if( nMaxBufChars < nLength+1 ) + { + CPLFree( pachBuffer ); + + nMaxBufChars = nLength+1; + pachBuffer = (char *) CPLMalloc(nMaxBufChars); + } + +/* -------------------------------------------------------------------- */ +/* Copy the data to the buffer. We use memcpy() so that it */ +/* will work for binary data. */ +/* -------------------------------------------------------------------- */ + memcpy( pachBuffer, pachSourceData, nLength ); + pachBuffer[nLength] = '\0'; + + return pachBuffer; +} + +/************************************************************************/ +/* ExtractFloatData() */ +/************************************************************************/ + +/** + * Extract a subfield value as a float. Given a pointer to the data + * for this subfield (from within a DDFRecord) this method will return the + * floating point data for this subfield. The number of bytes + * consumed as part of this field can also be fetched. This method may be + * called for any type of subfield, and will return zero if the subfield is + * not numeric. + * + * @param pachSourceData The pointer to the raw data for this field. This + * may have come from DDFRecord::GetData(), taking into account skip factors + * over previous subfields data. + * @param nMaxBytes The maximum number of bytes that are accessable after + * pachSourceData. + * @param pnConsumedBytes Pointer to an integer into which the number of + * bytes consumed by this field should be written. May be NULL to ignore. + * This is used as a skip factor to increment pachSourceData to point to the + * next subfields data. + * + * @return The subfield's numeric value (or zero if it isn't numeric). + * + * @see ExtractIntData(), ExtractStringData() + */ + +double +DDFSubfieldDefn::ExtractFloatData( const char * pachSourceData, + int nMaxBytes, int * pnConsumedBytes ) + +{ + switch( pszFormatString[0] ) + { + case 'A': + case 'I': + case 'R': + case 'S': + case 'C': + return CPLAtof(ExtractStringData(pachSourceData, nMaxBytes, + pnConsumedBytes)); + + case 'B': + case 'b': + { + unsigned char abyData[8]; + void* pabyData = abyData; + + if( nFormatWidth > nMaxBytes ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Attempt to extract float subfield %s with format %s\n" + "failed as only %d bytes available. Using zero.", + pszName, pszFormatString, nMaxBytes ); + return 0; + } + + if( pnConsumedBytes != NULL ) + *pnConsumedBytes = nFormatWidth; + + // Byte swap the data if it isn't in machine native format. + // In any event we copy it into our buffer to ensure it is + // word aligned. +#ifdef CPL_LSB + if( pszFormatString[0] == 'B' ) +#else + if( pszFormatString[0] == 'b' ) +#endif + { + for( int i = 0; i < nFormatWidth; i++ ) + abyData[nFormatWidth-i-1] = pachSourceData[i]; + } + else + { + memcpy( abyData, pachSourceData, nFormatWidth ); + } + + // Interpret the bytes of data. + switch( eBinaryFormat ) + { + case UInt: + if( nFormatWidth == 1 ) + return( abyData[0] ); + else if( nFormatWidth == 2 ) + return( *((GUInt16 *) pabyData) ); + else if( nFormatWidth == 4 ) + return( *((GUInt32 *) pabyData) ); + else + { + //CPLAssert( FALSE ); + return 0.0; + } + + case SInt: + if( nFormatWidth == 1 ) + return( *((signed char *) abyData) ); + else if( nFormatWidth == 2 ) + return( *((GInt16 *) pabyData) ); + else if( nFormatWidth == 4 ) + return( *((GInt32 *) pabyData) ); + else + { + //CPLAssert( FALSE ); + return 0.0; + } + + case FloatReal: + if( nFormatWidth == 4 ) + return( *((float *) pabyData) ); + else if( nFormatWidth == 8 ) + return( *((double *) pabyData) ); + else + { + //CPLAssert( FALSE ); + return 0.0; + } + + case NotBinary: + case FPReal: + case FloatComplex: + //CPLAssert( FALSE ); + return 0.0; + } + break; + // end of 'b'/'B' case. + } + + default: + //CPLAssert( FALSE ); + return 0.0; + } + + //CPLAssert( FALSE ); + return 0.0; +} + +/************************************************************************/ +/* ExtractIntData() */ +/************************************************************************/ + +/** + * Extract a subfield value as an integer. Given a pointer to the data + * for this subfield (from within a DDFRecord) this method will return the + * int data for this subfield. The number of bytes + * consumed as part of this field can also be fetched. This method may be + * called for any type of subfield, and will return zero if the subfield is + * not numeric. + * + * @param pachSourceData The pointer to the raw data for this field. This + * may have come from DDFRecord::GetData(), taking into account skip factors + * over previous subfields data. + * @param nMaxBytes The maximum number of bytes that are accessable after + * pachSourceData. + * @param pnConsumedBytes Pointer to an integer into which the number of + * bytes consumed by this field should be written. May be NULL to ignore. + * This is used as a skip factor to increment pachSourceData to point to the + * next subfields data. + * + * @return The subfield's numeric value (or zero if it isn't numeric). + * + * @see ExtractFloatData(), ExtractStringData() + */ + +int +DDFSubfieldDefn::ExtractIntData( const char * pachSourceData, + int nMaxBytes, int * pnConsumedBytes ) + +{ + switch( pszFormatString[0] ) + { + case 'A': + case 'I': + case 'R': + case 'S': + case 'C': + return atoi(ExtractStringData(pachSourceData, nMaxBytes, + pnConsumedBytes)); + + case 'B': + case 'b': + { + unsigned char abyData[8]; + void* pabyData = abyData; + + if( nFormatWidth > nMaxBytes || nFormatWidth >= (int)sizeof(abyData) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Attempt to extract int subfield %s with format %s\n" + "failed as only %d bytes available. Using zero.", + pszName, pszFormatString, MIN(nMaxBytes, (int)sizeof(abyData)) ); + return 0; + } + + if( pnConsumedBytes != NULL ) + *pnConsumedBytes = nFormatWidth; + + // Byte swap the data if it isn't in machine native format. + // In any event we copy it into our buffer to ensure it is + // word aligned. +#ifdef CPL_LSB + if( pszFormatString[0] == 'B' ) +#else + if( pszFormatString[0] == 'b' ) +#endif + { + for( int i = 0; i < nFormatWidth; i++ ) + abyData[nFormatWidth-i-1] = pachSourceData[i]; + } + else + { + memcpy( abyData, pachSourceData, nFormatWidth ); + } + + // Interpret the bytes of data. + switch( eBinaryFormat ) + { + case UInt: + if( nFormatWidth == 4 ) + return( (int) *((GUInt32 *) pabyData) ); + else if( nFormatWidth == 1 ) + return( abyData[0] ); + else if( nFormatWidth == 2 ) + return( *((GUInt16 *) pabyData) ); + else + { + //CPLAssert( FALSE ); + return 0; + } + + case SInt: + if( nFormatWidth == 4 ) + return( *((GInt32 *) pabyData) ); + else if( nFormatWidth == 1 ) + return( *((signed char *) abyData) ); + else if( nFormatWidth == 2 ) + return( *((GInt16 *) pabyData) ); + else + { + //CPLAssert( FALSE ); + return 0; + } + + case FloatReal: + if( nFormatWidth == 4 ) + return( (int) *((float *) pabyData) ); + else if( nFormatWidth == 8 ) + return( (int) *((double *) pabyData) ); + else + { + //CPLAssert( FALSE ); + return 0; + } + + case NotBinary: + case FPReal: + case FloatComplex: + //CPLAssert( FALSE ); + return 0; + } + break; + // end of 'b'/'B' case. + } + + default: + //CPLAssert( FALSE ); + return 0; + } + + //CPLAssert( FALSE ); + return 0; +} + +/************************************************************************/ +/* DumpData() */ +/* */ +/* Dump the instance data for this subfield from a data */ +/* record. This fits into the output dump stream of a DDFField. */ +/************************************************************************/ + +/** + * Dump subfield value to debugging file. + * + * @param pachData Pointer to data for this subfield. + * @param nMaxBytes Maximum number of bytes available in pachData. + * @param fp File to write report to. + */ + +void DDFSubfieldDefn::DumpData( const char * pachData, int nMaxBytes, + FILE * fp ) + +{ + if( eType == DDFFloat ) + fprintf( fp, " Subfield `%s' = %f\n", + pszName, + ExtractFloatData( pachData, nMaxBytes, NULL ) ); + else if( eType == DDFInt ) + fprintf( fp, " Subfield `%s' = %d\n", + pszName, + ExtractIntData( pachData, nMaxBytes, NULL ) ); + else if( eType == DDFBinaryString ) + { + int nBytes, i; + GByte *pabyBString = (GByte *) ExtractStringData( pachData, nMaxBytes, &nBytes ); + + fprintf( fp, " Subfield `%s' = 0x", pszName ); + for( i = 0; i < MIN(nBytes,24); i++ ) + fprintf( fp, "%02X", pabyBString[i] ); + + if( nBytes > 24 ) + fprintf( fp, "%s", "..." ); + + fprintf( fp, "\n" ); + } + else + fprintf( fp, " Subfield `%s' = `%s'\n", + pszName, + ExtractStringData( pachData, nMaxBytes, NULL ) ); +} + +/************************************************************************/ +/* GetDefaultValue() */ +/************************************************************************/ + +/** + * Get default data. + * + * Returns the default subfield data contents for this subfield definition. + * For variable length numbers this will normally be "0". + * For variable length strings it will be "". For fixed + * length numbers it is zero filled. For fixed length strings it is space + * filled. For binary numbers it is binary zero filled. + * + * @param pachData the buffer into which the returned default will be placed. + * May be NULL if just querying default size. + * @param nBytesAvailable the size of pachData in bytes. + * @param pnBytesUsed will receive the size of the subfield default data in + * bytes. + * + * @return TRUE on success or FALSE on failure or if the passed buffer is too + * small to hold the default. + */ + +int DDFSubfieldDefn::GetDefaultValue( char *pachData, int nBytesAvailable, + int *pnBytesUsed ) + +{ + int nDefaultSize; + + if( !bIsVariable ) + nDefaultSize = nFormatWidth; + else + nDefaultSize = 1; + + if( pnBytesUsed != NULL ) + *pnBytesUsed = nDefaultSize; + + if( pachData == NULL ) + return TRUE; + + if( nBytesAvailable < nDefaultSize ) + return FALSE; + + if( bIsVariable ) + { + pachData[0] = DDF_UNIT_TERMINATOR; + } + else + { + if( GetBinaryFormat() == NotBinary ) + { + if( GetType() == DDFInt || GetType() == DDFFloat ) + memset( pachData, '0', nDefaultSize ); + else + memset( pachData, ' ', nDefaultSize ); + } + else + memset( pachData, 0, nDefaultSize ); + } + + return TRUE; +} + +/************************************************************************/ +/* FormatStringValue() */ +/************************************************************************/ + +/** + * Format string subfield value. + * + * Returns a buffer with the passed in string value reformatted in a way + * suitable for storage in a DDFField for this subfield. + */ + +int DDFSubfieldDefn::FormatStringValue( char *pachData, int nBytesAvailable, + int *pnBytesUsed, + const char *pszValue, + int nValueLength ) + +{ + int nSize; + + if( nValueLength == -1 ) + nValueLength = strlen(pszValue); + + if( bIsVariable ) + { + nSize = nValueLength + 1; + } + else + { + nSize = nFormatWidth; + } + + if( pnBytesUsed != NULL ) + *pnBytesUsed = nSize; + + if( pachData == NULL ) + return TRUE; + + if( nBytesAvailable < nSize ) + return FALSE; + + if( bIsVariable ) + { + strncpy( pachData, pszValue, nSize-1 ); + pachData[nSize-1] = DDF_UNIT_TERMINATOR; + } + else + { + if( GetBinaryFormat() == NotBinary ) + { + memset( pachData, ' ', nSize ); + memcpy( pachData, pszValue, MIN(nValueLength,nSize) ); + } + else + { + memset( pachData, 0, nSize ); + memcpy( pachData, pszValue, MIN(nValueLength,nSize) ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* FormatIntValue() */ +/************************************************************************/ + +/** + * Format int subfield value. + * + * Returns a buffer with the passed in int value reformatted in a way + * suitable for storage in a DDFField for this subfield. + */ + +int DDFSubfieldDefn::FormatIntValue( char *pachData, int nBytesAvailable, + int *pnBytesUsed, int nNewValue ) + +{ + int nSize; + char szWork[30]; + + sprintf( szWork, "%d", nNewValue ); + + if( bIsVariable ) + { + nSize = strlen(szWork) + 1; + } + else + { + nSize = nFormatWidth; + + if( GetBinaryFormat() == NotBinary && (int) strlen(szWork) > nSize ) + return FALSE; + } + + if( pnBytesUsed != NULL ) + *pnBytesUsed = nSize; + + if( pachData == NULL ) + return TRUE; + + if( nBytesAvailable < nSize ) + return FALSE; + + if( bIsVariable ) + { + strncpy( pachData, szWork, nSize-1 ); + pachData[nSize-1] = DDF_UNIT_TERMINATOR; + } + else + { + GUInt32 nMask = 0xff; + int i; + + switch( GetBinaryFormat() ) + { + case NotBinary: + memset( pachData, '0', nSize ); + strncpy( pachData + nSize - strlen(szWork), szWork, + strlen(szWork) ); + break; + + case UInt: + case SInt: + for( i = 0; i < nFormatWidth; i++ ) + { + int iOut; + + // big endian required? + if( pszFormatString[0] == 'B' ) + iOut = nFormatWidth - i - 1; + else + iOut = i; + + pachData[iOut] = (char)((nNewValue & nMask) >> (i*8)); + nMask *= 256; + } + break; + + case FloatReal: + CPLAssert( FALSE ); + break; + + default: + CPLAssert( FALSE ); + break; + } + } + + return TRUE; +} + +/************************************************************************/ +/* FormatFloatValue() */ +/************************************************************************/ + +/** + * Format float subfield value. + * + * Returns a buffer with the passed in float value reformatted in a way + * suitable for storage in a DDFField for this subfield. + */ + +int DDFSubfieldDefn::FormatFloatValue( char *pachData, int nBytesAvailable, + int *pnBytesUsed, double dfNewValue ) + +{ + int nSize; + char szWork[120]; + + CPLsprintf( szWork, "%.16g", dfNewValue ); + + if( bIsVariable ) + { + nSize = strlen(szWork) + 1; + } + else + { + nSize = nFormatWidth; + + if( GetBinaryFormat() == NotBinary && (int) strlen(szWork) > nSize ) + return FALSE; + } + + if( pnBytesUsed != NULL ) + *pnBytesUsed = nSize; + + if( pachData == NULL ) + return TRUE; + + if( nBytesAvailable < nSize ) + return FALSE; + + if( bIsVariable ) + { + strncpy( pachData, szWork, nSize-1 ); + pachData[nSize-1] = DDF_UNIT_TERMINATOR; + } + else + { + if( GetBinaryFormat() == NotBinary ) + { + memset( pachData, '0', nSize ); + strncpy( pachData + nSize - strlen(szWork), szWork, + strlen(szWork) ); + } + else + { + CPLAssert( FALSE ); + /* implement me */ + } + } + + return TRUE; +} diff --git a/bazaar/plugin/gdal/frmts/iso8211/ddfutils.cpp b/bazaar/plugin/gdal/frmts/iso8211/ddfutils.cpp new file mode 100644 index 000000000..50b0da730 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/ddfutils.cpp @@ -0,0 +1,101 @@ +/****************************************************************************** + * $Id: ddfutils.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: ISO 8211 Access + * Purpose: Various utility functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "iso8211.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ddfutils.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* DDFScanInt() */ +/* */ +/* Read up to nMaxChars from the passed string, and interpret */ +/* as an integer. */ +/************************************************************************/ + +long DDFScanInt( const char * pszString, int nMaxChars ) + +{ + char szWorking[33]; + + if( nMaxChars > 32 || nMaxChars == 0 ) + nMaxChars = 32; + + memcpy( szWorking, pszString, nMaxChars ); + szWorking[nMaxChars] = '\0'; + + return( atoi(szWorking) ); +} + +/************************************************************************/ +/* DDFScanVariable() */ +/* */ +/* Establish the length of a variable length string in a */ +/* record. */ +/************************************************************************/ + +int DDFScanVariable( const char *pszRecord, int nMaxChars, int nDelimChar ) + +{ + int i; + + for( i = 0; i < nMaxChars-1 && pszRecord[i] != nDelimChar; i++ ) {} + + return i; +} + +/************************************************************************/ +/* DDFFetchVariable() */ +/* */ +/* Fetch a variable length string from a record, and allocate */ +/* it as a new string (with CPLStrdup()). */ +/************************************************************************/ + +char * DDFFetchVariable( const char *pszRecord, int nMaxChars, + int nDelimChar1, int nDelimChar2, + int *pnConsumedChars ) + +{ + int i; + char *pszReturn; + + for( i = 0; i < nMaxChars-1 && pszRecord[i] != nDelimChar1 + && pszRecord[i] != nDelimChar2; i++ ) {} + + *pnConsumedChars = i; + if( i < nMaxChars + && (pszRecord[i] == nDelimChar1 || pszRecord[i] == nDelimChar2) ) + (*pnConsumedChars)++; + + pszReturn = (char *) CPLMalloc(i+1); + pszReturn[i] = '\0'; + strncpy( pszReturn, pszRecord, i ); + + return pszReturn; +} diff --git a/bazaar/plugin/gdal/frmts/iso8211/intro.dox b/bazaar/plugin/gdal/frmts/iso8211/intro.dox new file mode 100644 index 000000000..da726d9a5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/intro.dox @@ -0,0 +1,207 @@ +/*! \mainpage ISO8211Lib + +

    Introduction

    + +ISO8211Lib is intended to be a simple reader for ISO/IEC 8211 formatted files, +particularly those that are part of SDTS and S-57 datasets. It consists +of open source, easy to compile and integrate C++ code.

    + +

    ISO 8211 Background

    + +The ISO 8211 +FAQ has some good background on ISO 8211 formatted files. I will briefly +introduce it here, with reference to the library classes representing +the components.

    + +An 8211 file (DDFModule) +consists of a series of logical records. The fiarst record +is special, and is called the DDR (Data Description Record). It basically +contains definitions of all the data objects (fields or DDFFieldDefn objects) +that can occur on the following data records.

    + +The remainder of the records are known as DRs (data records - DDFRecord). They +each contain one or more field (DDFField) instances. What fields appear +on what records is not defined by ISO 8211, though more specific requirements +may be implied by a particular data standard such as SDTS or S-57.

    + +Each field instance has a name, and consists of a series of subfields. A +given +field always has the same subfields in each field instance, and these subfields +are defined in the DDR (DDFSubfieldDefn), in association with their +field definition (DDFFieldDefn). A field may appear 0, 1, or many times in a DR.

    + +Each subfield has a name, format (from the DDFSubfieldDefn) and +actual subfield data for a particular DR. +Some fields contain an array of their group of +subfields. For instance a coordinate field may have X and Y +subfields, and they may repeat many times within one coordinate field +indicating a series of points.

    + +This would be a real good place for a UML diagram of ISO 8211, and +the corresponding library classes!

    + +

    Development Information

    + +The iso8211.h contains the definitions for all public ISO8211Lib +classes, enumerations and other services.

    + +To establish access to an ISO 8211 dataset, instantiate a DDFModule object, +and then use the DDFModule::Open() method. This will read the DDR, and +establish all the DDFFieldDefn, and DDFSubfieldDefn objects which can be +queried off the DDFModule.

    + +The use DDFModule::ReadRecord() to fetch data records (DDFRecord). When +a record is read, a list of field objects (DDFField) on that record are +created. They can be queried with various DDFRecord methods.

    + +Data pointers for individual subfields of a DDFField can be fetched with +DDFField::GetSubfieldData(). The interpreted value can then be extracted +with the appropriate one of DDFSubfieldDefn::ExtractIntValue(), +DDFSubfieldDefn::ExtractStringValue(), or DDFSubfieldDefn::ExtractFloatValue(). +Note that there is no object instantiated for individual subfields of a +DDFField. Instead the application extracts a pointer to the subfields +raw data, and then uses the DDFSubfieldDefn for that subfield to extract +a useable value from the raw data.

    + +Once the end of the file has been encountered (DDFModule::ReadRecord() returns +NULL), the DDFModule should be deleted, which will close the file, and +cleanup all records, definitions and related objects.

    + +

    Class APIs

    + +
      +
    • DDFModule class. +
    • DDFFieldDefn class. +
    • DDFSubfieldDefn class. +
    • DDFRecord class. +
    • DDFField class. +
    + +A complete Example Reader should clarify +simple use of ISO8211Lib.

    + +

    Related Information

    + +
      + +
    • The ISO 8211 standard can be ordered through +ISO. It cost me about $200CDN.

      + +

    • The ISO/IEC 8211/DDFS +Home Page contains tutorials and some code by Dr. Alfred A. +Brooks, one of the originators of the 8211 standard.

      + +

    • The +ISO/IEC 8211 Home Page has some python code for parsing 8211 files, and +some other useful background.

      + +

    • The SDTS++ +library from the USGS +includes support for ISO 8211. It doesn't include some of the +1994 additions to ISO 8211, but it is sufficient for SDTS, and quite +elegantly done. Also supports writing ISO 8211 files.

      + +

    • The USGS also has an older +FIPS123 +library which supports the older profile of ISO 8211 (to some extent).

      + +

    + +

    Licensing

    + +This library is offered as Open Source. +In particular, it is offered under the X Consortium license which doesn't +attempt to impose any copyleft, or credit requirements on users of the code.

    + +The precise license text is:

    + + + Copyright (c) 1999, Frank Warmerdam +

    + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: +

    + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. +

    + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +

    + + +

    Building the Source

    + +
      + +
    1. First, fetch the source. The most recent source should be accessable at +an url such as + +http://home.gdal.org/projects/iso8211/iso8211lib-1.4.zip.

      + +

    2. Untar the source.

      + +% unzip iso8211lib-1.4.zip + + +

    3. On unix you can now type ``configure'' to establish configuration +options.

      + +

    4. On unix you can now type make to build libiso8211.a, and the sample +mainline 8211view.

      + +

    + +Windows developers will have to create their own makefile/project but +can base it on the very simple Makefile.in provided. As well, you would +need to copy cpl_config.h.in to cpl_config.h, and modify as needed. The +default will likely work OK, but may result in some compiler warnings. +Let me know if you are having difficulties, and I will prepare a VC++ +makefile.

    + +

    Author and Acknowledgements

    + +The primary author of ISO8211Lib is +Frank Warmerdam, and I can be reached at +warmerdam@pobox.com. I am eager to +receive bug reports, and also open to praise or suggestions.

    + +I would like to thank:

    + +

      +
    • Safe Software +who funded development of this library, and agreed for it to be Open Source.

      + +

    • Mark Colletti, a primary author of SDTS++ from +which I derived most of what I know about ISO 8211 and who was very +supportive, answering a variety of questions.

      + +

    • Tony J Ibbs, author of the ISO/IEC 8211 home page who answered +a number of questions, and collected a variety of very useful information. +

      + +

    • Rodney Jenson, for a detailed bug report related to repeating variable +length fields (from S-57).

      + +

    + +I would also like to dedicate this library to the memory of Sol Katz. +Sol released a variety of SDTS (and hence ISO8211) translators, at substantial +personal effort, to the GIS community along with the many other generous +contributions he made to the community. His example has been an inspiration +to me, and I hope similar efforts on my part will contribute to his memory.

    + +*/ + +/*! +\page ISO8211_Example +\include 8211view.cpp +*/ diff --git a/bazaar/plugin/gdal/frmts/iso8211/iso8211.h b/bazaar/plugin/gdal/frmts/iso8211/iso8211.h new file mode 100644 index 000000000..81b51d479 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/iso8211.h @@ -0,0 +1,526 @@ +/****************************************************************************** + * $Id: iso8211.h 25841 2013-04-02 21:30:03Z rouault $ + * + * Project: ISO 8211 Access + * Purpose: Main declarations for ISO 8211. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _ISO8211_H_INCLUDED +#define _ISO8211_H_INCLUDED + +#include "cpl_port.h" +#include "cpl_vsi.h" + +/** + General data type + */ +typedef enum { + DDFInt, + DDFFloat, + DDFString, + DDFBinaryString +} DDFDataType; + +/************************************************************************/ +/* These should really be private to the library ... they are */ +/* mostly conveniences. */ +/************************************************************************/ + +long CPL_ODLL DDFScanInt( const char *pszString, int nMaxChars ); +int CPL_ODLL DDFScanVariable( const char * pszString, int nMaxChars, int nDelimChar ); +char CPL_ODLL *DDFFetchVariable( const char *pszString, int nMaxChars, + int nDelimChar1, int nDelimChar2, + int *pnConsumedChars ); + +#define DDF_FIELD_TERMINATOR 30 +#define DDF_UNIT_TERMINATOR 31 + +/************************************************************************/ +/* Predeclarations */ +/************************************************************************/ + +class DDFFieldDefn; +class DDFSubfieldDefn; +class DDFRecord; +class DDFField; + +/************************************************************************/ +/* DDFModule */ +/************************************************************************/ + +/** + The primary class for reading ISO 8211 files. This class contains all + the information read from the DDR record, and is used to read records + from the file. + +*/ + +class CPL_ODLL DDFModule +{ + public: + DDFModule(); + ~DDFModule(); + + int Open( const char * pszFilename, int bFailQuietly = FALSE ); + int Create( const char *pszFilename ); + void Close(); + + int Initialize( char chInterchangeLevel = '3', + char chLeaderIden = 'L', + char chCodeExtensionIndicator = 'E', + char chVersionNumber = '1', + char chAppIndicator = ' ', + const char *pszExtendedCharSet = " ! ", + int nSizeFieldLength = 3, + int nSizeFieldPos = 4, + int nSizeFieldTag = 4 ); + + void Dump( FILE * fp ); + + DDFRecord *ReadRecord( void ); + void Rewind( long nOffset = -1 ); + + DDFFieldDefn *FindFieldDefn( const char * ); + + /** Fetch the number of defined fields. */ + + int GetFieldCount() { return nFieldDefnCount; } + DDFFieldDefn *GetField(int); + void AddField( DDFFieldDefn *poNewFDefn ); + + // This is really just for internal use. + int GetFieldControlLength() { return _fieldControlLength; } + void AddCloneRecord( DDFRecord * ); + void RemoveCloneRecord( DDFRecord * ); + + // This is just for DDFRecord. + VSILFILE *GetFP() { return fpDDF; } + int GetSizeFieldTag() const { return (int)_sizeFieldTag; } + + private: + VSILFILE *fpDDF; + int bReadOnly; + long nFirstRecordOffset; + + char _interchangeLevel; + char _inlineCodeExtensionIndicator; + char _versionNumber; + char _appIndicator; + int _fieldControlLength; + char _extendedCharSet[4]; + + long _recLength; + char _leaderIden; + long _fieldAreaStart; + long _sizeFieldLength; + long _sizeFieldPos; + long _sizeFieldTag; + + // One DirEntry per field. + int nFieldDefnCount; + DDFFieldDefn **papoFieldDefns; + + DDFRecord *poRecord; + + int nCloneCount; + int nMaxCloneCount; + DDFRecord **papoClones; +}; + +/************************************************************************/ +/* DDFFieldDefn */ +/************************************************************************/ + + typedef enum { dsc_elementary, dsc_vector, dsc_array, dsc_concatenated } DDF_data_struct_code; + typedef enum { dtc_char_string, + dtc_implicit_point, + dtc_explicit_point, + dtc_explicit_point_scaled, + dtc_char_bit_string, + dtc_bit_string, + dtc_mixed_data_type } DDF_data_type_code; + +/** + * Information from the DDR defining one field. Note that just because + * a field is defined for a DDFModule doesn't mean that it actually occurs + * on any records in the module. DDFFieldDefns are normally just significant + * as containers of the DDFSubfieldDefns. + */ + +class CPL_ODLL DDFFieldDefn +{ + public: + DDFFieldDefn(); + ~DDFFieldDefn(); + + int Create( const char *pszTag, const char *pszFieldName, + const char *pszDescription, + DDF_data_struct_code eDataStructCode, + DDF_data_type_code eDataTypeCode, + const char *pszFormat = NULL ); + void AddSubfield( DDFSubfieldDefn *poNewSFDefn, + int bDontAddToFormat = FALSE ); + void AddSubfield( const char *pszName, const char *pszFormat ); + int GenerateDDREntry( char **ppachData, int *pnLength ); + + int Initialize( DDFModule * poModule, const char *pszTag, + int nSize, const char * pachRecord ); + + void Dump( FILE * fp ); + + /** Fetch a pointer to the field name (tag). + * @return this is an internal copy and shouldn't be freed. + */ + const char *GetName() { return pszTag; } + + /** Fetch a longer descriptio of this field. + * @return this is an internal copy and shouldn't be freed. + */ + const char *GetDescription() { return _fieldName; } + + /** Get the number of subfields. */ + int GetSubfieldCount() { return nSubfieldCount; } + + DDFSubfieldDefn *GetSubfield( int i ); + DDFSubfieldDefn *FindSubfieldDefn( const char * ); + + /** + * Get the width of this field. This function isn't normally used + * by applications. + * + * @return The width of the field in bytes, or zero if the field is not + * apparently of a fixed width. + */ + int GetFixedWidth() { return nFixedWidth; } + + /** + * Fetch repeating flag. + * @see DDFField::GetRepeatCount() + * @return TRUE if the field is marked as repeating. + */ + int IsRepeating() { return bRepeatingSubfields; } + + static char *ExpandFormat( const char * ); + + /** this is just for an S-57 hack for swedish data */ + void SetRepeatingFlag( int n ) { bRepeatingSubfields = n; } + + char *GetDefaultValue( int *pnSize ); + + const char *GetArrayDescr() const { return _arrayDescr; } + const char *GetFormatControls() const { return _formatControls; } + DDF_data_struct_code GetDataStructCode() const { return _data_struct_code; } + DDF_data_type_code GetDataTypeCode() const { return _data_type_code; } + + private: + + static char *ExtractSubstring( const char * ); + + DDFModule * poModule; + char * pszTag; + + char * _fieldName; + char * _arrayDescr; + char * _formatControls; + + int bRepeatingSubfields; + int nFixedWidth; // zero if variable. + + int BuildSubfields(); + int ApplyFormats(); + + DDF_data_struct_code _data_struct_code; + + DDF_data_type_code _data_type_code; + + int nSubfieldCount; + DDFSubfieldDefn **papoSubfields; +}; + +/************************************************************************/ +/* DDFSubfieldDefn */ +/* */ +/* Information from the DDR record for one subfield of a */ +/* particular field. */ +/************************************************************************/ + +/** + * Information from the DDR record describing one subfield of a DDFFieldDefn. + * All subfields of a field will occur in each occurance of that field + * (as a DDFField) in a DDFRecord. Subfield's actually contain formatted + * data (as instances within a record). + */ + +class CPL_ODLL DDFSubfieldDefn +{ +public: + + DDFSubfieldDefn(); + ~DDFSubfieldDefn(); + + void SetName( const char * pszName ); + + /** Get pointer to subfield name. */ + const char *GetName() { return pszName; } + + /** Get pointer to subfield format string */ + const char *GetFormat() { return pszFormatString; } + int SetFormat( const char * pszFormat ); + + /** + * Get the general type of the subfield. This can be used to + * determine which of ExtractFloatData(), ExtractIntData() or + * ExtractStringData() should be used. + * @return The subfield type. One of DDFInt, DDFFloat, DDFString or + * DDFBinaryString. + */ + + DDFDataType GetType() { return eType; } + + double ExtractFloatData( const char *pachData, int nMaxBytes, + int * pnConsumedBytes ); + int ExtractIntData( const char *pachData, int nMaxBytes, + int * pnConsumedBytes ); + const char *ExtractStringData( const char *pachData, int nMaxBytes, + int * pnConsumedBytes ); + int GetDataLength( const char *, int, int * ); + void DumpData( const char *pachData, int nMaxBytes, FILE * fp ); + + int FormatStringValue( char *pachData, int nBytesAvailable, + int *pnBytesUsed, const char *pszValue, + int nValueLength = -1 ); + + int FormatIntValue( char *pachData, int nBytesAvailable, + int *pnBytesUsed, int nNewValue ); + + int FormatFloatValue( char *pachData, int nBytesAvailable, + int *pnBytesUsed, double dfNewValue ); + + /** Get the subfield width (zero for variable). */ + int GetWidth() { return nFormatWidth; } // zero for variable. + + int GetDefaultValue( char *pachData, int nBytesAvailable, + int *pnBytesUsed ); + + void Dump( FILE * fp ); + +/** + Binary format: this is the digit immediately following the B or b for + binary formats. + */ +typedef enum { + NotBinary=0, + UInt=1, + SInt=2, + FPReal=3, + FloatReal=4, + FloatComplex=5 +} DDFBinaryFormat; + + DDFBinaryFormat GetBinaryFormat(void) const { return eBinaryFormat; } + + +private: + + char *pszName; // a.k.a. subfield mnemonic + char *pszFormatString; + + DDFDataType eType; + DDFBinaryFormat eBinaryFormat; + +/* -------------------------------------------------------------------- */ +/* bIsVariable determines whether we using the */ +/* chFormatDelimeter (TRUE), or the fixed width (FALSE). */ +/* -------------------------------------------------------------------- */ + int bIsVariable; + + char chFormatDelimeter; + int nFormatWidth; + +/* -------------------------------------------------------------------- */ +/* Fetched string cache. This is where we hold the values */ +/* returned from ExtractStringData(). */ +/* -------------------------------------------------------------------- */ + int nMaxBufChars; + char *pachBuffer; +}; + +/************************************************************************/ +/* DDFRecord */ +/* */ +/* Class that contains one DR record from a file. We read into */ +/* the same record object repeatedly to ensure that repeated */ +/* leaders can be easily preserved. */ +/************************************************************************/ + +/** + * Contains instance data from one data record (DR). The data is contained + * as a list of DDFField instances partitioning the raw data into fields. + */ + +class CPL_ODLL DDFRecord +{ + public: + DDFRecord( DDFModule * ); + ~DDFRecord(); + + DDFRecord *Clone(); + DDFRecord *CloneOn( DDFModule * ); + + void Dump( FILE * ); + + /** Get the number of DDFFields on this record. */ + int GetFieldCount() { return nFieldCount; } + + DDFField *FindField( const char *, int = 0 ); + DDFField *GetField( int ); + + int GetIntSubfield( const char *, int, const char *, int, + int * = NULL ); + double GetFloatSubfield( const char *, int, const char *, int, + int * = NULL ); + const char *GetStringSubfield( const char *, int, const char *, int, + int * = NULL ); + + int SetIntSubfield( const char *pszField, int iFieldIndex, + const char *pszSubfield, int iSubfieldIndex, + int nValue ); + int SetStringSubfield( const char *pszField, int iFieldIndex, + const char *pszSubfield, int iSubfieldIndex, + const char *pszValue, int nValueLength=-1 ); + int SetFloatSubfield( const char *pszField, int iFieldIndex, + const char *pszSubfield, int iSubfieldIndex, + double dfNewValue ); + + /** Fetch size of records raw data (GetData()) in bytes. */ + int GetDataSize() { return nDataSize; } + + /** + * Fetch the raw data for this record. The returned pointer is effectively + * to the data for the first field of the record, and is of size + * GetDataSize(). + */ + const char *GetData() { return pachData; } + + /** + * Fetch the DDFModule with which this record is associated. + */ + + DDFModule * GetModule() { return poModule; } + + int ResizeField( DDFField *poField, int nNewDataSize ); + int DeleteField( DDFField *poField ); + DDFField* AddField( DDFFieldDefn * ); + + int CreateDefaultFieldInstance( DDFField *poField, int iIndexWithinField ); + + int SetFieldRaw( DDFField *poField, int iIndexWithinField, + const char *pachRawData, int nRawDataSize ); + int UpdateFieldRaw( DDFField *poField, int iIndexWithinField, + int nStartOffset, int nOldSize, + const char *pachRawData, int nRawDataSize ); + + int Write(); + + // This is really just for the DDFModule class. + int Read(); + void Clear(); + int ResetDirectory(); + + private: + + int ReadHeader(); + + DDFModule *poModule; + + int nReuseHeader; + + int nFieldOffset; // field data area, not dir entries. + + int _sizeFieldTag; + int _sizeFieldPos; + int _sizeFieldLength; + + int nDataSize; // Whole record except leader with header + char *pachData; + + int nFieldCount; + DDFField *paoFields; + + int bIsClone; +}; + +/************************************************************************/ +/* DDFField */ +/* */ +/* This object represents one field in a DDFRecord. */ +/************************************************************************/ + +/** + * This object represents one field in a DDFRecord. This + * models an instance of the fields data, rather than it's data definition + * which is handled by the DDFFieldDefn class. Note that a DDFField + * doesn't have DDFSubfield children as you would expect. To extract + * subfield values use GetSubfieldData() to find the right data pointer and + * then use ExtractIntData(), ExtractFloatData() or ExtractStringData(). + */ + +class CPL_ODLL DDFField +{ + public: + void Initialize( DDFFieldDefn *, const char *pszData, + int nSize ); + + void Dump( FILE * fp ); + + const char *GetSubfieldData( DDFSubfieldDefn *, + int * = NULL, int = 0 ); + + const char *GetInstanceData( int nInstance, int *pnSize ); + + /** + * Return the pointer to the entire data block for this record. This + * is an internal copy, and shouldn't be freed by the application. + */ + const char *GetData() { return pachData; } + + /** Return the number of bytes in the data block returned by GetData(). */ + int GetDataSize() { return nDataSize; } + + int GetRepeatCount(); + + /** Fetch the corresponding DDFFieldDefn. */ + DDFFieldDefn *GetFieldDefn() { return poDefn; } + + private: + DDFFieldDefn *poDefn; + + int nDataSize; + + const char *pachData; +}; + + +#endif /* ndef _ISO8211_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/iso8211/makefile.vc b/bazaar/plugin/gdal/frmts/iso8211/makefile.vc new file mode 100644 index 000000000..0c7e26c32 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/makefile.vc @@ -0,0 +1,42 @@ + +OBJ = ddfmodule.obj ddfutils.obj ddffielddefn.obj ddfrecord.obj \ + ddffield.obj ddfsubfielddefn.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + lib /out:libiso8211.lib $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + -del *.exe + -del *.dll + -del *.lib + -del *.manifest + -del *.exp + +all: default 8211view.exe 8211dump.exe 8211createfromxml.exe + +iso8211.dll: $(OBJ) $(CPLLIB) + link /dll $(LDEBUG) $(OBJ) $(CPLLIB) ../zlib/*.obj \ + /out:iso8211.dll /implib:iso8211_i.lib + if exist $@.manifest mt -manifest $@.manifest -outputresource:$@;1 + +8211view.exe: 8211view.obj $(OBJ) $(CPLLIB) + $(CC) $(CFLAGS) 8211view.obj $(OBJ) $(CPLLIB) $(LIBICONV_LIBRARY) $(CURL_LIB) ../zlib/*.obj /link $(LINKER_FLAGS) + if exist $@.manifest mt -manifest $@.manifest -outputresource:$@;1 + del 8211view.obj + +8211dump.exe: 8211dump.obj $(OBJ) $(CPLLIB) + $(CC) $(CFLAGS) 8211dump.obj $(OBJ) $(CPLLIB) $(LIBICONV_LIBRARY) $(CURL_LIB) ../zlib/*.obj /link $(LINKER_FLAGS) + if exist $@.manifest mt -manifest $@.manifest -outputresource:$@;1 + del 8211dump.obj + +8211createfromxml.exe: 8211createfromxml.obj $(OBJ) $(CPLLIB) + $(CC) $(CFLAGS) 8211createfromxml.obj $(OBJ) $(CPLLIB) $(LIBICONV_LIBRARY) $(CURL_LIB) ../zlib/*.obj /link $(LINKER_FLAGS) + if exist $@.manifest mt -manifest $@.manifest -outputresource:$@;1 + del 8211createfromxml.obj + diff --git a/bazaar/plugin/gdal/frmts/iso8211/mkcatalog.cpp b/bazaar/plugin/gdal/frmts/iso8211/mkcatalog.cpp new file mode 100644 index 000000000..cf8161791 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/mkcatalog.cpp @@ -0,0 +1,424 @@ +/* **************************************************************************** + * $Id: mkcatalog.cpp 16861 2009-04-26 19:22:29Z rouault $ + * + * Project: ISO8211 Library + * Purpose: Test ISO8211 writing capability. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "iso8211.h" + +CPL_CVSID("$Id: mkcatalog.cpp 16861 2009-04-26 19:22:29Z rouault $"); + + +/************************************************************************/ +/* mk_s57() */ +/************************************************************************/ + +void mk_s57() + +{ + DDFModule oModule; + DDFFieldDefn *poFDefn; + + oModule.Initialize(); + +/* -------------------------------------------------------------------- */ +/* Create the '0000' definition. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "0000", "", "0001DSIDDSIDDSSI0001DSPM0001VRIDVRIDATTVVRIDVRPCVRIDVRPTVRIDSGCCVRIDSG2DVRIDSG3D0001FRIDFRIDFOIDFRIDATTFFRIDNATFFRIDFFPCFRIDFFPTFRIDFSPCFRIDFSPT", dsc_elementary, dtc_char_string ); + + oModule.AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the '0001' definition. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "0001", "ISO 8211 Record Identifier", "", + dsc_elementary, dtc_bit_string, + "(b12)" ); + + oModule.AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the DSID field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "DSID", "Data set identification field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "RCNM", "b11" ); + poFDefn->AddSubfield( "RCID", "b14" ); + poFDefn->AddSubfield( "EXPP", "b11" ); + poFDefn->AddSubfield( "INTU", "b11" ); + poFDefn->AddSubfield( "DSNM", "A" ); + poFDefn->AddSubfield( "EDTN", "A" ); + poFDefn->AddSubfield( "UPDN", "A" ); + poFDefn->AddSubfield( "UADT", "A(8)" ); + poFDefn->AddSubfield( "ISDT", "A(8)" ); + poFDefn->AddSubfield( "STED", "R(4)" ); + poFDefn->AddSubfield( "PRSP", "b11" ); + poFDefn->AddSubfield( "PSDN", "A" ); + poFDefn->AddSubfield( "PRED", "A" ); + poFDefn->AddSubfield( "PROF", "b11" ); + poFDefn->AddSubfield( "AGEN", "b12" ); + poFDefn->AddSubfield( "COMT", "A" ); + + oModule.AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the DSSI field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "DSSI", "Data set structure information field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "DSTR", "b11" ); + poFDefn->AddSubfield( "AALL", "b11" ); + poFDefn->AddSubfield( "NALL", "b11" ); + poFDefn->AddSubfield( "NOMR", "b14" ); + poFDefn->AddSubfield( "NOCR", "b14" ); + poFDefn->AddSubfield( "NOGR", "b14" ); + poFDefn->AddSubfield( "NOLR", "b14" ); + poFDefn->AddSubfield( "NOIN", "b14" ); + poFDefn->AddSubfield( "NOCN", "b14" ); + poFDefn->AddSubfield( "NOED", "b14" ); + poFDefn->AddSubfield( "NOFA", "b14" ); + + oModule.AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the DSPM field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "DSPM", "Data set parameter field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "RCNM", "b11" ); + poFDefn->AddSubfield( "RCID", "b14" ); + poFDefn->AddSubfield( "HDAT", "b11" ); + poFDefn->AddSubfield( "VDAT", "b11" ); + poFDefn->AddSubfield( "SDAT", "b11" ); + poFDefn->AddSubfield( "CSCL", "b14" ); + poFDefn->AddSubfield( "DUNI", "b11" ); + poFDefn->AddSubfield( "HUNI", "b11" ); + poFDefn->AddSubfield( "PUNI", "b11" ); + poFDefn->AddSubfield( "COUN", "b11" ); + poFDefn->AddSubfield( "COMF", "b14" ); + poFDefn->AddSubfield( "SOMF", "b14" ); + poFDefn->AddSubfield( "COMT", "A" ); + + oModule.AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the VRID field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "VRID", "Vector record identifier field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "RCNM", "b11" ); + poFDefn->AddSubfield( "RCID", "b14" ); + poFDefn->AddSubfield( "RVER", "b12" ); + poFDefn->AddSubfield( "RUIN", "b11" ); + + oModule.AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the ATTV field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "ATTV", "Vector record attribute field", "", + dsc_vector, dtc_mixed_data_type ); + + /* how do I mark this as repeating? */ + poFDefn->AddSubfield( "ATTL", "b12" ); + poFDefn->AddSubfield( "ATVL", "A" ); + + oModule.AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the SG2D field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "SG2D", "2-D coordinate field", "*", + dsc_vector, dtc_mixed_data_type ); + + /* how do I mark this as repeating? */ + poFDefn->AddSubfield( "YCOO", "b24" ); + poFDefn->AddSubfield( "XCOO", "b24" ); + + oModule.AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the SG3D field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "SG3D", "3-D coordinate (sounding array) field", "*", + dsc_vector, dtc_mixed_data_type ); + + /* how do I mark this as repeating? */ + poFDefn->AddSubfield( "YCOO", "b24" ); + poFDefn->AddSubfield( "XCOO", "b24" ); + poFDefn->AddSubfield( "VE3D", "b24" ); + + oModule.AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ + +// need to add: VRPC, VRPT, SGCC, FRID, FOID, ATTF, NATF, FFPC, +// FFPT, FSPC, and FSPT + +/* -------------------------------------------------------------------- */ +/* Create file. */ +/* -------------------------------------------------------------------- */ + oModule.Dump( stdout ); + + oModule.Create( "out.000" ); + +/* -------------------------------------------------------------------- */ +/* Create a record. */ +/* -------------------------------------------------------------------- */ + DDFRecord *poRec = new DDFRecord( &oModule ); + DDFField *poField; + + poField = poRec->AddField( oModule.FindFieldDefn( "0001" ) ); + poRec->SetFieldRaw( poField, 0, "\1\0\036", 3 ); + + poField = poRec->AddField( oModule.FindFieldDefn( "DSID" ) ); + + poRec->SetIntSubfield ( "DSID", 0, "RCNM", 0, 10 ); + poRec->SetIntSubfield ( "DSID", 0, "RCID", 0, 1 ); + poRec->SetIntSubfield ( "DSID", 0, "EXPP", 0, 1 ); + poRec->SetIntSubfield ( "DSID", 0, "INTU", 0, 4 ); + poRec->SetStringSubfield( "DSID", 0, "DSNM", 0, "GB4X0000.000" ); + poRec->SetStringSubfield( "DSID", 0, "EDTN", 0, "2" ); + poRec->SetStringSubfield( "DSID", 0, "UPDN", 0, "0" ); + poRec->SetStringSubfield( "DSID", 0, "UADT", 0, "20010409" ); + poRec->SetStringSubfield( "DSID", 0, "ISDT", 0, "20010409" ); + poRec->SetFloatSubfield ( "DSID", 0, "STED", 0, 3.1 ); + poRec->SetIntSubfield ( "DSID", 0, "PRSP", 0, 1 ); + poRec->SetStringSubfield( "DSID", 0, "PSDN", 0, "" ); + poRec->SetStringSubfield( "DSID", 0, "PRED", 0, "2.0" ); + poRec->SetIntSubfield ( "DSID", 0, "PROF", 0, 1 ); + poRec->SetIntSubfield ( "DSID", 0, "AGEN", 0, 540 ); + poRec->SetStringSubfield( "DSID", 0, "COMT", 0, "" ); + + poField = poRec->AddField( oModule.FindFieldDefn( "DSSI" ) ); + + poRec->SetIntSubfield ( "DSSI", 0, "DSTR", 0, 2 ); + poRec->SetIntSubfield ( "DSSI", 0, "AALL", 0, 1 ); + poRec->SetIntSubfield ( "DSSI", 0, "NALL", 0, 1 ); + poRec->SetIntSubfield ( "DSSI", 0, "NOMR", 0, 22 ); + poRec->SetIntSubfield ( "DSSI", 0, "NOCR", 0, 0 ); + poRec->SetIntSubfield ( "DSSI", 0, "NOGR", 0, 2141 ); + poRec->SetIntSubfield ( "DSSI", 0, "NOLR", 0, 15 ); + poRec->SetIntSubfield ( "DSSI", 0, "NOIN", 0, 512 ); + poRec->SetIntSubfield ( "DSSI", 0, "NOCN", 0, 2181 ); + poRec->SetIntSubfield ( "DSSI", 0, "NOED", 0, 3192 ); + poRec->SetIntSubfield ( "DSSI", 0, "NOFA", 0, 0 ); + + poRec->Write(); + delete poRec; + +/* -------------------------------------------------------------------- */ +/* Create a record. */ +/* -------------------------------------------------------------------- */ + poRec = new DDFRecord( &oModule ); + + poField = poRec->AddField( oModule.FindFieldDefn( "0001" ) ); + poRec->SetFieldRaw( poField, 0, "\2\0\036", 3 ); + + poField = poRec->AddField( oModule.FindFieldDefn( "DSPM" ) ); + + poRec->SetIntSubfield ( "DSPM", 0, "RCNM", 0, 20 ); + poRec->SetIntSubfield ( "DSPM", 0, "RCID", 0, 1 ); + poRec->SetIntSubfield ( "DSPM", 0, "HDAT", 0, 2 ); + poRec->SetIntSubfield ( "DSPM", 0, "VDAT", 0, 17 ); + poRec->SetIntSubfield ( "DSPM", 0, "SDAT", 0, 23 ); + poRec->SetIntSubfield ( "DSPM", 0, "CSCL", 0, 52000 ); + poRec->SetIntSubfield ( "DSPM", 0, "DUNI", 0, 1 ); + poRec->SetIntSubfield ( "DSPM", 0, "HUNI", 0, 1 ); + poRec->SetIntSubfield ( "DSPM", 0, "PUNI", 0, 1 ); + poRec->SetIntSubfield ( "DSPM", 0, "COUN", 0, 1 ); + poRec->SetIntSubfield ( "DSPM", 0, "COMF", 0, 1000000 ); + poRec->SetIntSubfield ( "DSPM", 0, "SOMF", 0, 10 ); + + poRec->Write(); + delete poRec; + +/* -------------------------------------------------------------------- */ +/* Create a record. */ +/* -------------------------------------------------------------------- */ + poRec = new DDFRecord( &oModule ); + + poField = poRec->AddField( oModule.FindFieldDefn( "0001" ) ); + poRec->SetFieldRaw( poField, 0, "\3\0\036", 3 ); + + poField = poRec->AddField( oModule.FindFieldDefn( "VRID" ) ); + + poRec->SetIntSubfield ( "VRID", 0, "RCNM", 0, 110 ); + poRec->SetIntSubfield ( "VRID", 0, "RCID", 0, 518 ); + poRec->SetIntSubfield ( "VRID", 0, "RVER", 0, 1 ); + poRec->SetIntSubfield ( "VRID", 0, "RUIN", 0, 1 ); + + poField = poRec->AddField( oModule.FindFieldDefn( "SG3D" ) ); + + poRec->SetIntSubfield ( "SG3D", 0, "YCOO", 0, -325998702 ); + poRec->SetIntSubfield ( "SG3D", 0, "XCOO", 0, 612175350 ); + poRec->SetIntSubfield ( "SG3D", 0, "VE3D", 0, 174 ); + + poRec->SetIntSubfield ( "SG3D", 0, "YCOO", 1, -325995189 ); + poRec->SetIntSubfield ( "SG3D", 0, "XCOO", 1, 612228812 ); + poRec->SetIntSubfield ( "SG3D", 0, "VE3D", 1, 400 ); + + poRec->Write(); + + delete poRec; +} + +/************************************************************************/ +/* mk_catalog() */ +/************************************************************************/ + +void mk_catalog() + +{ + DDFModule oModule; + DDFFieldDefn *poFDefn; + + oModule.Initialize(); + +/* -------------------------------------------------------------------- */ +/* Create the '0000' definition. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "0000", "", "0001CATD", + dsc_elementary, + dtc_char_string ); + + oModule.AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the '0000' definition. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "0001", "ISO 8211 Record Identifier", "", + dsc_elementary, dtc_bit_string, + "(b12)" ); + + oModule.AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the CATD field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "CATD", "Catalog Directory field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "RCNM", "A(2)" ); + poFDefn->AddSubfield( "RCID", "I(10)" ); + poFDefn->AddSubfield( "FILE", "A" ); + poFDefn->AddSubfield( "LFIL", "A" ); + poFDefn->AddSubfield( "VOLM", "A" ); + poFDefn->AddSubfield( "IMPL", "A(3)" ); + poFDefn->AddSubfield( "SLAT", "R" ); + poFDefn->AddSubfield( "WLON", "R" ); + poFDefn->AddSubfield( "NLAT", "R" ); + poFDefn->AddSubfield( "ELON", "R" ); + poFDefn->AddSubfield( "CRCS", "A" ); + poFDefn->AddSubfield( "COMT", "A" ); + + oModule.AddField( poFDefn ); + + oModule.Dump( stdout ); + + oModule.Create( "out.ddf" ); + +/* -------------------------------------------------------------------- */ +/* Create a record. */ +/* -------------------------------------------------------------------- */ + DDFRecord *poRec = new DDFRecord( &oModule ); + DDFField *poField; + + poField = poRec->AddField( oModule.FindFieldDefn( "0001" ) ); + poRec->SetFieldRaw( poField, 0, "\0\0\036", 3 ); + + poField = poRec->AddField( oModule.FindFieldDefn( "CATD" ) ); + poRec->SetStringSubfield( "CATD", 0, "RCNM", 0, "CD" ); + poRec->SetIntSubfield ( "CATD", 0, "RCID", 0, 1 ); + poRec->SetStringSubfield( "CATD", 0, "FILE", 0, "CATALOG.030" ); + poRec->SetStringSubfield( "CATD", 0, "VOLM", 0, "V01X01" ); + poRec->SetStringSubfield( "CATD", 0, "IMPL", 0, "ASC" ); + poRec->SetStringSubfield( "CATD", 0, "COMT", 0, + "Exchange Set Catalog file" ); + poRec->Write(); + delete poRec; + +/* -------------------------------------------------------------------- */ +/* Create a record. */ +/* -------------------------------------------------------------------- */ + poRec = new DDFRecord( &oModule ); + + poField = poRec->AddField( oModule.FindFieldDefn( "0001" ) ); + poRec->SetFieldRaw( poField, 0, "\1\0\036", 3 ); + + poField = poRec->AddField( oModule.FindFieldDefn( "CATD" ) ); + poRec->SetStringSubfield( "CATD", 0, "RCNM", 0, "CD" ); + poRec->SetIntSubfield ( "CATD", 0, "RCID", 0, 2 ); + poRec->SetStringSubfield( "CATD", 0, "FILE", 0, "No410810.000" ); + poRec->SetStringSubfield( "CATD", 0, "VOLM", 0, "V01X01" ); + poRec->SetStringSubfield( "CATD", 0, "IMPL", 0, "BIN" ); + poRec->SetFloatSubfield ( "CATD", 0, "SLAT", 0, 59.000005 ); + poRec->SetFloatSubfield ( "CATD", 0, "WLON", 0, 4.999996 ); + poRec->SetFloatSubfield ( "CATD", 0, "NLAT", 0, 59.500003 ); + poRec->SetFloatSubfield ( "CATD", 0, "ELON", 0, 5.499997 ); + poRec->SetStringSubfield( "CATD", 0, "CRCS", 0, "555C3AD4" ); + poRec->Write(); + delete poRec; +} + +/* **********************************************************************/ +/* main() */ +/* **********************************************************************/ + +int main( int nArgc, char ** papszArgv ) + +{ + mk_s57(); +} + diff --git a/bazaar/plugin/gdal/frmts/iso8211/teststream.out b/bazaar/plugin/gdal/frmts/iso8211/teststream.out new file mode 100644 index 000000000..ae326c379 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/teststream.out @@ -0,0 +1,2986 @@ +--------------------------------------------------------------------- +-- 1183CEL0.DDF +--------------------------------------------------------------------- +DDFModule: + _recLength = 187 + _interchangeLevel = 2 + _leaderIden = L + _inlineCodeExtensionIndicator = + _versionNumber = + _appIndicator = + _fieldControlLength = 6 + _fieldAreaStart = 61 + _sizeFieldLength = 2 + _sizeFieldPos = 3 + _sizeFieldTag = 4 + DDFFieldDefn: + Tag = `0000' + _fieldName = `1183CEL0.DDF' + _arrayDescr = `' + _formatControls = `' + _data_struct_code = elementary + _data_type_code = char_string + DDFFieldDefn: + Tag = `0001' + _fieldName = `DDF RECORD IDENTIFIER' + _arrayDescr = `' + _formatControls = `' + _data_struct_code = elementary + _data_type_code = implicit_point + DDFFieldDefn: + Tag = `CELL' + _fieldName = `Cell' + _arrayDescr = `MODN!RCID!ROWI!COLI' + _formatControls = `(A,3I)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `MODN' + FormatString = `A' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `I' + DDFSubfieldDefn: + Label = `ROWI' + FormatString = `I' + DDFSubfieldDefn: + Label = `COLI' + FormatString = `I' + DDFFieldDefn: + Tag = `CVLS' + _fieldName = `Cell Values' + _arrayDescr = `*ELEVATION' + _formatControls = `(B(16))' + _data_struct_code = array + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `ELEVATION' + FormatString = `B(16)' +DDFRecord: + nReuseHeader = 0 + nDataSize = 803 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `0\1E' + DDFField: + Tag = `CELL' + DataSize = 11 + Data = `CEL0\1F1\1F1\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 1 + Subfield `ROWI' = 1 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05=\05<\05<\05>\05F\05B\05?\05?\05E\05H\05V\05]\05e\05s\05~...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1341 + Subfield `ELEVATION' = 1340 + Subfield `ELEVATION' = 1340 + Subfield `ELEVATION' = 1342 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 803 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `1\1E' + DDFField: + Tag = `CELL' + DataSize = 11 + Data = `CEL0\1F2\1F2\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 2 + Subfield `ROWI' = 2 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05K\05M\05N\05T\05V\05U\05S\05M\05X\05U\05U\05e\05q\05z\05\FFFFFF8B...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1355 + Subfield `ELEVATION' = 1357 + Subfield `ELEVATION' = 1358 + Subfield `ELEVATION' = 1364 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 803 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `2\1E' + DDFField: + Tag = `CELL' + DataSize = 11 + Data = `CEL0\1F3\1F3\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 3 + Subfield `ROWI' = 3 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05[\05^\05_\05`\05g\05f\05c\05^\05e\05i\05o\05o\05w\05\FFFFFF88\05\FFFFFF91...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1371 + Subfield `ELEVATION' = 1374 + Subfield `ELEVATION' = 1375 + Subfield `ELEVATION' = 1376 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 803 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `3\1E' + DDFField: + Tag = `CELL' + DataSize = 11 + Data = `CEL0\1F4\1F4\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 4 + Subfield `ROWI' = 4 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05k\05m\05n\05t\05{\05w\05t\05s\05r\05|\05}\05\FFFFFF89\05\FFFFFF84\05\FFFFFF8E\05\FFFFFFA2...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1387 + Subfield `ELEVATION' = 1389 + Subfield `ELEVATION' = 1390 + Subfield `ELEVATION' = 1396 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 803 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `4\1E' + DDFField: + Tag = `CELL' + DataSize = 11 + Data = `CEL0\1F5\1F5\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 5 + Subfield `ROWI' = 5 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05\7F\05\FFFFFF81\05\FFFFFF83\05\FFFFFF87\05\FFFFFF8C\05\FFFFFF8C\05\FFFFFF8A\05\FFFFFF8B\05\FFFFFF8A\05\FFFFFF84\05\FFFFFF89\05\FFFFFF8F\05\FFFFFF98\05\FFFFFF9C\05\FFFFFFA3\05\FFFFFFAB...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1407 + Subfield `ELEVATION' = 1409 + Subfield `ELEVATION' = 1411 + Subfield `ELEVATION' = 1415 + Subfield `ELEVATION' = 1420 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 803 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `5\1E' + DDFField: + Tag = `CELL' + DataSize = 11 + Data = `CEL0\1F6\1F6\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 6 + Subfield `ROWI' = 6 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05\FFFFFF91\05\FFFFFF95\05\FFFFFF99\05\FFFFFF9C\05\FFFFFF9F\05\FFFFFFA0\05\FFFFFF9D\05\FFFFFF9F\05\FFFFFFA0\05\FFFFFF94\05\FFFFFF97\05\FFFFFF9D\05\FFFFFFA6\05\FFFFFFB0\05\FFFFFFB5\05\FFFFFFB6...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1425 + Subfield `ELEVATION' = 1429 + Subfield `ELEVATION' = 1433 + Subfield `ELEVATION' = 1436 + Subfield `ELEVATION' = 1439 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 803 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `6\1E' + DDFField: + Tag = `CELL' + DataSize = 11 + Data = `CEL0\1F7\1F7\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 7 + Subfield `ROWI' = 7 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05\FFFFFFAA\05\FFFFFFAD\05\FFFFFFAE\05\FFFFFFB1\05\FFFFFFB2\05\FFFFFFB1\05\FFFFFFB0\05\FFFFFFB2\05\FFFFFFAF\05\FFFFFFA2\05\FFFFFFA2\05\FFFFFFAD\05\FFFFFFB2\05\FFFFFFC0\05\FFFFFFC9\05\FFFFFFCA...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1450 + Subfield `ELEVATION' = 1453 + Subfield `ELEVATION' = 1454 + Subfield `ELEVATION' = 1457 + Subfield `ELEVATION' = 1458 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 803 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `7\1E' + DDFField: + Tag = `CELL' + DataSize = 11 + Data = `CEL0\1F8\1F8\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 8 + Subfield `ROWI' = 8 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05\FFFFFFBE\05\FFFFFFC4\05\FFFFFFC8\05\FFFFFFCB\05\FFFFFFCE\05\FFFFFFCE\05\FFFFFFCE\05\FFFFFFCF\05\FFFFFFC5\05\FFFFFFB2\05\FFFFFFAD\05\FFFFFFBC\05\FFFFFFC5\05\FFFFFFD0\05\FFFFFFE0\05\FFFFFFF4...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1470 + Subfield `ELEVATION' = 1476 + Subfield `ELEVATION' = 1480 + Subfield `ELEVATION' = 1483 + Subfield `ELEVATION' = 1486 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 803 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `8\1E' + DDFField: + Tag = `CELL' + DataSize = 11 + Data = `CEL0\1F9\1F9\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 9 + Subfield `ROWI' = 9 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\05\FFFFFFD4\05\FFFFFFD8\05\FFFFFFDD\06\0A\06&\06%\06$\06\1E\05\FFFFFFD6\05\FFFFFFBF\05\FFFFFFB9\05\FFFFFFCD\05\FFFFFFE1\05\FFFFFFFC\06\08\06_...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1492 + Subfield `ELEVATION' = 1496 + Subfield `ELEVATION' = 1501 + Subfield `ELEVATION' = 1546 + Subfield `ELEVATION' = 1574 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 805 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `9\1E' + DDFField: + Tag = `CELL' + DataSize = 13 + Data = `CEL0\1F10\1F10\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 10 + Subfield `ROWI' = 10 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\06\08\06\13\06&\06\FFFFFF88\06\FFFFFF87\06\FFFFFF89\06\FFFFFF8B\06\7F\06\02\05\FFFFFFCF\05\FFFFFFCA\05\FFFFFFE4\06W\06\FFFFFF8D\06\FFFFFF9A\06\FFFFFFA1...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1544 + Subfield `ELEVATION' = 1555 + Subfield `ELEVATION' = 1574 + Subfield `ELEVATION' = 1672 + Subfield `ELEVATION' = 1671 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 806 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `10\1E' + DDFField: + Tag = `CELL' + DataSize = 13 + Data = `CEL0\1F11\1F11\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 11 + Subfield `ROWI' = 11 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\06(\06\7F\06\FFFFFF93\06\FFFFFF9B\06\FFFFFF9F\06\FFFFFF9C\06\FFFFFF99\06\FFFFFF91\06W\05\FFFFFFDB\05\FFFFFFE3\06\0B\06\FFFFFF94\06\FFFFFF9D\06\FFFFFFA5\06\FFFFFFAA...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1576 + Subfield `ELEVATION' = 1663 + Subfield `ELEVATION' = 1683 + Subfield `ELEVATION' = 1691 + Subfield `ELEVATION' = 1695 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 806 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `11\1E' + DDFField: + Tag = `CELL' + DataSize = 13 + Data = `CEL0\1F12\1F12\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 12 + Subfield `ROWI' = 12 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\06{\06\FFFFFF95\06\FFFFFF9A\06\FFFFFF9F\06\FFFFFFA1\06\FFFFFFA1\06\FFFFFF9C\06\FFFFFF94\06_\05\FFFFFFE7\06\08\06\FFFFFF93\06\FFFFFF9B\06\FFFFFFA4\06\FFFFFFAE\06\FFFFFFB7...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1659 + Subfield `ELEVATION' = 1685 + Subfield `ELEVATION' = 1690 + Subfield `ELEVATION' = 1695 + Subfield `ELEVATION' = 1697 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 806 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `12\1E' + DDFField: + Tag = `CELL' + DataSize = 13 + Data = `CEL0\1F13\1F13\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 13 + Subfield `ROWI' = 13 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\06%\06\FFFFFF8C\06\FFFFFF96\06\FFFFFF9A\06\FFFFFF9D\06\FFFFFF9E\06\FFFFFF9D\06\FFFFFF96\06\FFFFFF81\06\08\06\FFFFFF8E\06\FFFFFF9B\06\FFFFFFA2\06\FFFFFFAA\06\FFFFFFB9\06\FFFFFFC6...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1573 + Subfield `ELEVATION' = 1676 + Subfield `ELEVATION' = 1686 + Subfield `ELEVATION' = 1690 + Subfield `ELEVATION' = 1693 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 806 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `13\1E' + DDFField: + Tag = `CELL' + DataSize = 13 + Data = `CEL0\1F14\1F14\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 14 + Subfield `ROWI' = 14 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\06$\060\06\FFFFFF93\06\FFFFFF97\06\FFFFFF9C\06\FFFFFF9C\06\FFFFFF9A\06\FFFFFF9A\06\FFFFFF8C\06\FFFFFF87\06\FFFFFF98\06\FFFFFFA0\06\FFFFFFA6\06\FFFFFFAE\06\FFFFFFB9\06\FFFFFFCC...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1572 + Subfield `ELEVATION' = 1584 + Subfield `ELEVATION' = 1683 + Subfield `ELEVATION' = 1687 + Subfield `ELEVATION' = 1692 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 806 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `14\1E' + DDFField: + Tag = `CELL' + DataSize = 13 + Data = `CEL0\1F15\1F15\1F1\1E' + Subfield `MODN' = `CEL0' + Subfield `RCID' = 15 + Subfield `ROWI' = 15 + Subfield `COLI' = 1 + DDFField: + Tag = `CVLS' + DataSize = 759 + Data = `\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\FFFFFF80\02\06$\06%\06\FFFFFF87\06\FFFFFF94\06\FFFFFF97\06\FFFFFF98\06\FFFFFF98\06\FFFFFF98\06\FFFFFF99\06\FFFFFF94\06\FFFFFF9C\06\FFFFFFA1\06\FFFFFFA5\06\FFFFFFAD\06\FFFFFFB7\06\FFFFFFCA...' + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = -32766 + Subfield `ELEVATION' = 1572 + Subfield `ELEVATION' = 1573 + Subfield `ELEVATION' = 1671 + Subfield `ELEVATION' = 1684 + Subfield `ELEVATION' = 1687 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 806 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `15\1E' + DDFField: + Tag = `CELL' + DataSize = 13 +--------------------------------------------------------------------- +-- CA49995B.000 +--------------------------------------------------------------------- +DDFModule: + _recLength = 2048 + _interchangeLevel = 3 + _leaderIden = L + _inlineCodeExtensionIndicator = E + _versionNumber = 1 + _appIndicator = + _fieldControlLength = 9 + _fieldAreaStart = 345 + _sizeFieldLength = 6 + _sizeFieldPos = 6 + _sizeFieldTag = 4 + DDFFieldDefn: + Tag = `0000' + _fieldName = `' + _arrayDescr = `0001DSIDDSIDDSSI0001DSPM0001FRIDFRIDFOIDFRIDATTFFRIDNATFFRIDFFPCFRIDFFPTFRIDFSPCFRIDFSPT0001VRIDVRIDATTVVRIDVRPCVRIDVRPTVRIDSGCCVRIDSG2DVRIDSG3D' + _formatControls = `' + _data_struct_code = elementary + _data_type_code = char_string + DDFFieldDefn: + Tag = `0001' + _fieldName = `ISO 8211 Record Identifier' + _arrayDescr = `' + _formatControls = `(b12)' + _data_struct_code = elementary + _data_type_code = bit_string + DDFFieldDefn: + Tag = `DSID' + _fieldName = `Data set identification field' + _arrayDescr = `RCNM!RCID!EXPP!INTU!DSNM!EDTN!UPDN!UADT!ISDT!STED!PRSP!PSDN!PRED!PROF!AGEN!COMT' + _formatControls = `(b11,b14,2b11,3A,2A(8),R(4),b11,2A,b11,b12,A)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `RCNM' + FormatString = `b11' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `b14' + DDFSubfieldDefn: + Label = `EXPP' + FormatString = `b11' + DDFSubfieldDefn: + Label = `INTU' + FormatString = `b11' + DDFSubfieldDefn: + Label = `DSNM' + FormatString = `A' + DDFSubfieldDefn: + Label = `EDTN' + FormatString = `A' + DDFSubfieldDefn: + Label = `UPDN' + FormatString = `A' + DDFSubfieldDefn: + Label = `UADT' + FormatString = `A(8)' + DDFSubfieldDefn: + Label = `ISDT' + FormatString = `A(8)' + DDFSubfieldDefn: + Label = `STED' + FormatString = `R(4)' + DDFSubfieldDefn: + Label = `PRSP' + FormatString = `b11' + DDFSubfieldDefn: + Label = `PSDN' + FormatString = `A' + DDFSubfieldDefn: + Label = `PRED' + FormatString = `A' + DDFSubfieldDefn: + Label = `PROF' + FormatString = `b11' + DDFSubfieldDefn: + Label = `AGEN' + FormatString = `b12' + DDFSubfieldDefn: + Label = `COMT' + FormatString = `A' + DDFFieldDefn: + Tag = `DSSI' + _fieldName = `Data set structure information field' + _arrayDescr = `DSTR!AALL!NALL!NOMR!NOCR!NOGR!NOLR!NOIN!NOCN!NOED!NOFA' + _formatControls = `(3b11,8b14)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `DSTR' + FormatString = `b11' + DDFSubfieldDefn: + Label = `AALL' + FormatString = `b11' + DDFSubfieldDefn: + Label = `NALL' + FormatString = `b11' + DDFSubfieldDefn: + Label = `NOMR' + FormatString = `b14' + DDFSubfieldDefn: + Label = `NOCR' + FormatString = `b14' + DDFSubfieldDefn: + Label = `NOGR' + FormatString = `b14' + DDFSubfieldDefn: + Label = `NOLR' + FormatString = `b14' + DDFSubfieldDefn: + Label = `NOIN' + FormatString = `b14' + DDFSubfieldDefn: + Label = `NOCN' + FormatString = `b14' + DDFSubfieldDefn: + Label = `NOED' + FormatString = `b14' + DDFSubfieldDefn: + Label = `NOFA' + FormatString = `b14' + DDFFieldDefn: + Tag = `DSPM' + _fieldName = `Data set parameter field' + _arrayDescr = `RCNM!RCID!HDAT!VDAT!SDAT!CSCL!DUNI!HUNI!PUNI!COUN!COMF!SOMF!COMT' + _formatControls = `(b11,b14,3b11,b14,4b11,2b14,A)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `RCNM' + FormatString = `b11' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `b14' + DDFSubfieldDefn: + Label = `HDAT' + FormatString = `b11' + DDFSubfieldDefn: + Label = `VDAT' + FormatString = `b11' + DDFSubfieldDefn: + Label = `SDAT' + FormatString = `b11' + DDFSubfieldDefn: + Label = `CSCL' + FormatString = `b14' + DDFSubfieldDefn: + Label = `DUNI' + FormatString = `b11' + DDFSubfieldDefn: + Label = `HUNI' + FormatString = `b11' + DDFSubfieldDefn: + Label = `PUNI' + FormatString = `b11' + DDFSubfieldDefn: + Label = `COUN' + FormatString = `b11' + DDFSubfieldDefn: + Label = `COMF' + FormatString = `b14' + DDFSubfieldDefn: + Label = `SOMF' + FormatString = `b14' + DDFSubfieldDefn: + Label = `COMT' + FormatString = `A' + DDFFieldDefn: + Tag = `FRID' + _fieldName = `Feature record identifier field' + _arrayDescr = `RCNM!RCID!PRIM!GRUP!OBJL!RVER!RUIN' + _formatControls = `(b11,b14,2b11,2b12,b11)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `RCNM' + FormatString = `b11' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `b14' + DDFSubfieldDefn: + Label = `PRIM' + FormatString = `b11' + DDFSubfieldDefn: + Label = `GRUP' + FormatString = `b11' + DDFSubfieldDefn: + Label = `OBJL' + FormatString = `b12' + DDFSubfieldDefn: + Label = `RVER' + FormatString = `b12' + DDFSubfieldDefn: + Label = `RUIN' + FormatString = `b11' + DDFFieldDefn: + Tag = `FOID' + _fieldName = `Feature object identifier field' + _arrayDescr = `AGEN!FIDN!FIDS' + _formatControls = `(b12,b14,b12)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `AGEN' + FormatString = `b12' + DDFSubfieldDefn: + Label = `FIDN' + FormatString = `b14' + DDFSubfieldDefn: + Label = `FIDS' + FormatString = `b12' + DDFFieldDefn: + Tag = `ATTF' + _fieldName = `Feature record attribute field' + _arrayDescr = `*ATTL!ATVL' + _formatControls = `(b12,A)' + _data_struct_code = array + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `ATTL' + FormatString = `b12' + DDFSubfieldDefn: + Label = `ATVL' + FormatString = `A' + DDFFieldDefn: + Tag = `NATF' + _fieldName = `Feature record national attribute field' + _arrayDescr = `*ATTL!ATVL' + _formatControls = `(b12,A)' + _data_struct_code = array + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `ATTL' + FormatString = `b12' + DDFSubfieldDefn: + Label = `ATVL' + FormatString = `A' + DDFFieldDefn: + Tag = `FFPC' + _fieldName = `Feature record to feature object pointer control field' + _arrayDescr = `FFUI!FFIX!NFPT' + _formatControls = `(b11,2b12)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `FFUI' + FormatString = `b11' + DDFSubfieldDefn: + Label = `FFIX' + FormatString = `b12' + DDFSubfieldDefn: + Label = `NFPT' + FormatString = `b12' + DDFFieldDefn: + Tag = `FFPT' + _fieldName = `Feature record to feature object pointer field' + _arrayDescr = `*LNAM!RIND!COMT' + _formatControls = `(B(64),b11,A)' + _data_struct_code = array + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `LNAM' + FormatString = `B(64)' + DDFSubfieldDefn: + Label = `RIND' + FormatString = `b11' + DDFSubfieldDefn: + Label = `COMT' + FormatString = `A' + DDFFieldDefn: + Tag = `FSPC' + _fieldName = `Feature record to spatial record pointer control field' + _arrayDescr = `FSUI!FSIX!NSPT' + _formatControls = `(b11,2b12)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `FSUI' + FormatString = `b11' + DDFSubfieldDefn: + Label = `FSIX' + FormatString = `b12' + DDFSubfieldDefn: + Label = `NSPT' + FormatString = `b12' + DDFFieldDefn: + Tag = `FSPT' + _fieldName = `Feature record to spatial record pointer field' + _arrayDescr = `*NAME!ORNT!USAG!MASK' + _formatControls = `(B(40),3b11)' + _data_struct_code = array + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `NAME' + FormatString = `B(40)' + DDFSubfieldDefn: + Label = `ORNT' + FormatString = `b11' + DDFSubfieldDefn: + Label = `USAG' + FormatString = `b11' + DDFSubfieldDefn: + Label = `MASK' + FormatString = `b11' + DDFFieldDefn: + Tag = `VRID' + _fieldName = `Vector record identifier field' + _arrayDescr = `RCNM!RCID!RVER!RUIN' + _formatControls = `(b11,b14,b12,b11)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `RCNM' + FormatString = `b11' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `b14' + DDFSubfieldDefn: + Label = `RVER' + FormatString = `b12' + DDFSubfieldDefn: + Label = `RUIN' + FormatString = `b11' + DDFFieldDefn: + Tag = `ATTV' + _fieldName = `Vector record attribute field' + _arrayDescr = `*ATTL!ATVL' + _formatControls = `(b12,A)' + _data_struct_code = array + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `ATTL' + FormatString = `b12' + DDFSubfieldDefn: + Label = `ATVL' + FormatString = `A' + DDFFieldDefn: + Tag = `VRPC' + _fieldName = `Vector record pointer control field' + _arrayDescr = `VPUI!VPIX!NVPT' + _formatControls = `(b11,2b12)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `VPUI' + FormatString = `b11' + DDFSubfieldDefn: + Label = `VPIX' + FormatString = `b12' + DDFSubfieldDefn: + Label = `NVPT' + FormatString = `b12' + DDFFieldDefn: + Tag = `VRPT' + _fieldName = `Vector record pointer field' + _arrayDescr = `*NAME!ORNT!USAG!TOPI!MASK' + _formatControls = `(B(40),4b11)' + _data_struct_code = array + _data_type_code = char_string + DDFSubfieldDefn: + Label = `NAME' + FormatString = `B(40)' + DDFSubfieldDefn: + Label = `ORNT' + FormatString = `b11' + DDFSubfieldDefn: + Label = `USAG' + FormatString = `b11' + DDFSubfieldDefn: + Label = `TOPI' + FormatString = `b11' + DDFSubfieldDefn: + Label = `MASK' + FormatString = `b11' + DDFFieldDefn: + Tag = `SGCC' + _fieldName = `Coordinate control field' + _arrayDescr = `CCUI!CCIX!CCNC' + _formatControls = `(b11,2b12)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `CCUI' + FormatString = `b11' + DDFSubfieldDefn: + Label = `CCIX' + FormatString = `b12' + DDFSubfieldDefn: + Label = `CCNC' + FormatString = `b12' + DDFFieldDefn: + Tag = `SG2D' + _fieldName = `2-D Coordinate field' + _arrayDescr = `*YCOO!XCOO' + _formatControls = `(2b24)' + _data_struct_code = array + _data_type_code = bit_string + DDFSubfieldDefn: + Label = `YCOO' + FormatString = `b24' + DDFSubfieldDefn: + Label = `XCOO' + FormatString = `b24' + DDFFieldDefn: + Tag = `SG3D' + _fieldName = `3-D Coordinate field' + _arrayDescr = `*YCOO!XCOO!VE3D' + _formatControls = `(3b24)' + _data_struct_code = array + _data_type_code = bit_string + DDFSubfieldDefn: + Label = `YCOO' + FormatString = `b24' + DDFSubfieldDefn: + Label = `XCOO' + FormatString = `b24' + DDFSubfieldDefn: + Label = `VE3D' + FormatString = `b24' +DDFRecord: + nReuseHeader = 0 + nDataSize = 189 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `\03\00\1E' + DDFField: + Tag = `DSID' + DataSize = 107 + Data = `\0A\01\00\00\00\01\04CA49995B.000\1F1\1F0\1F1997091019970910...' + Subfield `RCNM' = 10 + Subfield `RCID' = 1 + Subfield `EXPP' = 1 + Subfield `INTU' = 4 + Subfield `DSNM' = `CA49995B.000' + Subfield `EDTN' = `1' + Subfield `UPDN' = `0' + Subfield `UADT' = `19970910' + Subfield `ISDT' = `19970910' + Subfield `STED' = 3.000000 + Subfield `PRSP' = 1 + Subfield `PSDN' = `' + Subfield `PRED' = `1.0' + Subfield `PROF' = 1 + Subfield `AGEN' = 50 + Subfield `COMT' = `DATASET CREATED BY NAUTICAL DATA INTERNATIONAL, INC.' + DDFField: + Tag = `DSSI' + DataSize = 36 + Data = `\02\00\00\04\00\00\00\00\00\00\00\FFFFFFCA\00\00\00\01\00\00\009\00\00\00\FFFFFF90\00\00\00\FFFFFFAA\00\00\00\00\00\00\00\1E' + Subfield `DSTR' = 2 + Subfield `AALL' = 0 + Subfield `NALL' = 0 + Subfield `NOMR' = 4 + Subfield `NOCR' = 0 + Subfield `NOGR' = 202 + Subfield `NOLR' = 1 + Subfield `NOIN' = 57 + Subfield `NOCN' = 144 + Subfield `NOED' = 170 + Subfield `NOFA' = 0 +DDFRecord: + nReuseHeader = 0 + nDataSize = 58 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `\03\00\1E' + DDFField: + Tag = `DSPM' + DataSize = 26 + Data = `\14\02\00\00\00\02\1C\04\FFFFFFA8a\00\00\01\01\01\01@B\0F\00\0A\00\00\00\1F\1E' + Subfield `RCNM' = 20 + Subfield `RCID' = 2 + Subfield `HDAT' = 2 + Subfield `VDAT' = 28 + Subfield `SDAT' = 4 + Subfield `CSCL' = 25000 + Subfield `DUNI' = 1 + Subfield `HUNI' = 1 + Subfield `PUNI' = 1 + Subfield `COUN' = 1 + Subfield `COMF' = 1000000 + Subfield `SOMF' = 10 + Subfield `COMT' = `' +DDFRecord: + nReuseHeader = 0 + nDataSize = 788 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `\03\00\1E' + DDFField: + Tag = `VRID' + DataSize = 9 + Data = `n9\00\00\00\01\00\01\1E' + Subfield `RCNM' = 110 + Subfield `RCID' = 57 + Subfield `RVER' = 1 + Subfield `RUIN' = 1 +--------------------------------------------------------------------- +-- TWFLA009.DDF +--------------------------------------------------------------------- +DDFModule: + _recLength = 224 + _interchangeLevel = 2 + _leaderIden = L + _inlineCodeExtensionIndicator = + _versionNumber = + _appIndicator = + _fieldControlLength = 6 + _fieldAreaStart = 61 + _sizeFieldLength = 2 + _sizeFieldPos = 3 + _sizeFieldTag = 4 + DDFFieldDefn: + Tag = `0000' + _fieldName = `TWFLA009.DDF' + _arrayDescr = `' + _formatControls = `' + _data_struct_code = elementary + _data_type_code = char_string + DDFFieldDefn: + Tag = `0001' + _fieldName = `DDF RECORD IDENTIFIER' + _arrayDescr = `' + _formatControls = `' + _data_struct_code = elementary + _data_type_code = implicit_point + DDFFieldDefn: + Tag = `ATPR' + _fieldName = `Attribute Primary' + _arrayDescr = `MODN!RCID' + _formatControls = `(A,I)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `MODN' + FormatString = `A' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `I' + DDFFieldDefn: + Tag = `ATTP' + _fieldName = `Primary Attributes' + _arrayDescr = `ENTITY_LABEL!FEATURE_ID!DIMENSION!PHC' + _formatControls = `(A,I,2A)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `ENTITY_LABEL' + FormatString = `A' + DDFSubfieldDefn: + Label = `FEATURE_ID' + FormatString = `I' + DDFSubfieldDefn: + Label = `DIMENSION' + FormatString = `A' + DDFSubfieldDefn: + Label = `PHC' + FormatString = `A' +DDFRecord: + nReuseHeader = 0 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `0\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `A009\1F1\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 1 + DDFField: + Tag = `ATTP' + DataSize = 53 + Data = `POPULATED PLACE\1F401\1FTwo-Dimensional\1FNot ...' + Subfield `ENTITY_LABEL' = `POPULATED PLACE' + Subfield `FEATURE_ID' = 401 + Subfield `DIMENSION' = `Two-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 81 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `1\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `A009\1F2\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 2 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `BUILDING\1F1309\1FTwo-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `BUILDING' + Subfield `FEATURE_ID' = 1309 + Subfield `DIMENSION' = `Two-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 81 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `2\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `A009\1F3\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 3 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `BUILDING\1F1307\1FTwo-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `BUILDING' + Subfield `FEATURE_ID' = 1307 + Subfield `DIMENSION' = `Two-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 81 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `3\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `A009\1F4\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 4 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `BUILDING\1F1308\1FTwo-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `BUILDING' + Subfield `FEATURE_ID' = 1308 + Subfield `DIMENSION' = `Two-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 81 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `4\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `A009\1F5\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 5 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `BUILDING\1F1306\1FTwo-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `BUILDING' + Subfield `FEATURE_ID' = 1306 + Subfield `DIMENSION' = `Two-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 81 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `5\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `A009\1F6\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 6 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `BUILDING\1F1305\1FTwo-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `BUILDING' + Subfield `FEATURE_ID' = 1305 + Subfield `DIMENSION' = `Two-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 81 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `6\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `A009\1F7\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 7 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `BUILDING\1F1304\1FTwo-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `BUILDING' + Subfield `FEATURE_ID' = 1304 + Subfield `DIMENSION' = `Two-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 80 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `7\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `A009\1F8\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 8 + DDFField: + Tag = `ATTP' + DataSize = 46 + Data = `CEMETERY\1F101\1FTwo-Dimensional\1FNot Photore...' + Subfield `ENTITY_LABEL' = `CEMETERY' + Subfield `FEATURE_ID' = 101 + Subfield `DIMENSION' = `Two-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 81 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `8\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `A009\1F9\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 9 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `BUILDING\1F1303\1FTwo-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `BUILDING' + Subfield `FEATURE_ID' = 1303 + Subfield `DIMENSION' = `Two-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 82 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `9\1E' + DDFField: + Tag = `ATPR' + DataSize = 8 + Data = `A009\1F10\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 10 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `BUILDING\1F1301\1FTwo-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `BUILDING' + Subfield `FEATURE_ID' = 1301 + Subfield `DIMENSION' = `Two-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 83 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `10\1E' + DDFField: + Tag = `ATPR' + DataSize = 8 + Data = `A009\1F11\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 11 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `BUILDING\1F1302\1FTwo-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `BUILDING' + Subfield `FEATURE_ID' = 1302 + Subfield `DIMENSION' = `Two-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 83 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `11\1E' + DDFField: + Tag = `ATPR' + DataSize = 8 + Data = `A009\1F12\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 12 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `FENCE LINE\1F14\1FOne-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `FENCE LINE' + Subfield `FEATURE_ID' = 14 + Subfield `DIMENSION' = `One-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 83 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `12\1E' + DDFField: + Tag = `ATPR' + DataSize = 8 + Data = `A009\1F13\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 13 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `FENCE LINE\1F15\1FOne-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `FENCE LINE' + Subfield `FEATURE_ID' = 15 + Subfield `DIMENSION' = `One-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 83 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `13\1E' + DDFField: + Tag = `ATPR' + DataSize = 8 + Data = `A009\1F14\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 14 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `FENCE LINE\1F16\1FOne-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `FENCE LINE' + Subfield `FEATURE_ID' = 16 + Subfield `DIMENSION' = `One-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 83 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `14\1E' + DDFField: + Tag = `ATPR' + DataSize = 8 + Data = `A009\1F15\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 15 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `FENCE LINE\1F13\1FOne-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `FENCE LINE' + Subfield `FEATURE_ID' = 13 + Subfield `DIMENSION' = `One-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 82 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `15\1E' + DDFField: + Tag = `ATPR' + DataSize = 8 + Data = `A009\1F16\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 16 + DDFField: + Tag = `ATTP' + DataSize = 46 + Data = `FENCE LINE\1F7\1FOne-Dimensional\1FNot Photore...' + Subfield `ENTITY_LABEL' = `FENCE LINE' + Subfield `FEATURE_ID' = 7 + Subfield `DIMENSION' = `One-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 82 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `16\1E' + DDFField: + Tag = `ATPR' + DataSize = 8 + Data = `A009\1F17\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 17 + DDFField: + Tag = `ATTP' + DataSize = 46 + Data = `FENCE LINE\1F6\1FOne-Dimensional\1FNot Photore...' + Subfield `ENTITY_LABEL' = `FENCE LINE' + Subfield `FEATURE_ID' = 6 + Subfield `DIMENSION' = `One-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 82 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `17\1E' + DDFField: + Tag = `ATPR' + DataSize = 8 + Data = `A009\1F18\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 18 + DDFField: + Tag = `ATTP' + DataSize = 46 + Data = `FENCE LINE\1F9\1FOne-Dimensional\1FNot Photore...' + Subfield `ENTITY_LABEL' = `FENCE LINE' + Subfield `FEATURE_ID' = 9 + Subfield `DIMENSION' = `One-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 83 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `18\1E' + DDFField: + Tag = `ATPR' + DataSize = 8 + Data = `A009\1F19\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 19 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `FENCE LINE\1F10\1FOne-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `FENCE LINE' + Subfield `FEATURE_ID' = 10 + Subfield `DIMENSION' = `One-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 83 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `19\1E' + DDFField: + Tag = `ATPR' + DataSize = 8 + Data = `A009\1F20\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 20 + DDFField: + Tag = `ATTP' + DataSize = 47 + Data = `FENCE LINE\1F12\1FOne-Dimensional\1FNot Photor...' + Subfield `ENTITY_LABEL' = `FENCE LINE' + Subfield `FEATURE_ID' = 12 + Subfield `DIMENSION' = `One-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: + nReuseHeader = 0 + nDataSize = 82 + DDFField: + Tag = `0001' + DataSize = 3 + Data = `20\1E' + DDFField: + Tag = `ATPR' + DataSize = 8 + Data = `A009\1F21\1E' + Subfield `MODN' = `A009' + Subfield `RCID' = 21 + DDFField: + Tag = `ATTP' + DataSize = 46 + Data = `FENCE LINE\1F8\1FOne-Dimensional\1FNot Photore...' + Subfield `ENTITY_LABEL' = `FENCE LINE' + Subfield `FEATURE_ID' = 8 + Subfield `DIMENSION' = `One-Dimensional' + Subfield `PHC' = `Not Photorevised' +DDFRecord: +--------------------------------------------------------------------- +-- SC01CATD.DDF +--------------------------------------------------------------------- +DDFModule: + _recLength = 160 + _interchangeLevel = 2 + _leaderIden = L + _inlineCodeExtensionIndicator = + _versionNumber = + _appIndicator = + _fieldControlLength = 6 + _fieldAreaStart = 49 + _sizeFieldLength = 2 + _sizeFieldPos = 2 + _sizeFieldTag = 4 + DDFFieldDefn: + Tag = `0000' + _fieldName = `SC01CATD' + _arrayDescr = `' + _formatControls = `' + _data_struct_code = elementary + _data_type_code = char_string + DDFFieldDefn: + Tag = `0001' + _fieldName = `DDF RECORD IDENTIFIER' + _arrayDescr = `' + _formatControls = `' + _data_struct_code = elementary + _data_type_code = implicit_point + DDFFieldDefn: + Tag = `CATD' + _fieldName = `CATALOG/DIRECTORY' + _arrayDescr = `MODN!RCID!NAME!TYPE!FILE!EXTR!MVER' + _formatControls = `(A,I,5A)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `MODN' + FormatString = `A' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `I' + DDFSubfieldDefn: + Label = `NAME' + FormatString = `A' + DDFSubfieldDefn: + Label = `TYPE' + FormatString = `A' + DDFSubfieldDefn: + Label = `FILE' + FormatString = `A' + DDFSubfieldDefn: + Label = `EXTR' + FormatString = `A' + DDFSubfieldDefn: + Label = `MVER' + FormatString = `A' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 1\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 1\1FIDEN\1FIdentification ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 1 + Subfield `NAME' = `IDEN' + Subfield `TYPE' = `Identification ' + Subfield `FILE' = `SC01IDEN.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 2\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 2\1FCATD\1FCatalog/Directory ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 2 + Subfield `NAME' = `CATD' + Subfield `TYPE' = `Catalog/Directory ' + Subfield `FILE' = `SC01CATD.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 3\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 3\1FCATX\1FCatalog/Cross-Reference...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 3 + Subfield `NAME' = `CATX' + Subfield `TYPE' = `Catalog/Cross-Reference ' + Subfield `FILE' = `SC01CATX.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 4\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 4\1FCATS\1FCatalog/Spatial Domain ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 4 + Subfield `NAME' = `CATS' + Subfield `TYPE' = `Catalog/Spatial Domain ' + Subfield `FILE' = `SC01CATS.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 5\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 5\1FIREF\1FInternal Spatial Refere...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 5 + Subfield `NAME' = `IREF' + Subfield `TYPE' = `Internal Spatial Reference' + Subfield `FILE' = `SC01IREF.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 6\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 6\1FXREF\1FExternal Spatial Refere...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 6 + Subfield `NAME' = `XREF' + Subfield `TYPE' = `External Spatial Reference' + Subfield `FILE' = `SC01XREF.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 7\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 7\1FMDEF\1FData Dictionary/Definit...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 7 + Subfield `NAME' = `MDEF' + Subfield `TYPE' = `Data Dictionary/Definition' + Subfield `FILE' = `DLG3MDEF.DDF' + Subfield `EXTR' = `Y' + Subfield `MVER' = ` 3.00' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 8\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 8\1FMDOM\1FData Dictionary/Domain ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 8 + Subfield `NAME' = `MDOM' + Subfield `TYPE' = `Data Dictionary/Domain ' + Subfield `FILE' = `DLG3MDOM.DDF' + Subfield `EXTR' = `Y' + Subfield `MVER' = ` 3.00' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 9\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 9\1FDDSH\1FData Dictionary/Schema ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 9 + Subfield `NAME' = `DDSH' + Subfield `TYPE' = `Data Dictionary/Schema ' + Subfield `FILE' = `SC01DDSH.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 10\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 10\1FSTAT\1FTransfer Statistics ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 10 + Subfield `NAME' = `STAT' + Subfield `TYPE' = `Transfer Statistics ' + Subfield `FILE' = `SC01STAT.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 11\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 11\1FDQHL\1FLineage ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 11 + Subfield `NAME' = `DQHL' + Subfield `TYPE' = `Lineage ' + Subfield `FILE' = `SC01DQHL.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 12\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 12\1FDQPA\1FPositional Accuracy ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 12 + Subfield `NAME' = `DQPA' + Subfield `TYPE' = `Positional Accuracy ' + Subfield `FILE' = `SC01DQPA.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 13\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 13\1FDQAA\1FAttribute Accuracy ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 13 + Subfield `NAME' = `DQAA' + Subfield `TYPE' = `Attribute Accuracy ' + Subfield `FILE' = `SC01DQAA.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 14\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 14\1FDQLC\1FLogical Consistency ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 14 + Subfield `NAME' = `DQLC' + Subfield `TYPE' = `Logical Consistency ' + Subfield `FILE' = `SC01DQLC.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 15\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 15\1FDQCG\1FCompleteness ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 15 + Subfield `NAME' = `DQCG' + Subfield `TYPE' = `Completeness ' + Subfield `FILE' = `SC01DQCG.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 16\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 16\1FASCF\1FAttribute Primary ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 16 + Subfield `NAME' = `ASCF' + Subfield `TYPE' = `Attribute Primary ' + Subfield `FILE' = `SC01ASCF.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 17\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 17\1FAHDR\1FAttribute Primary ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 17 + Subfield `NAME' = `AHDR' + Subfield `TYPE' = `Attribute Primary ' + Subfield `FILE' = `SC01AHDR.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 18\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 18\1FFF01\1FComposite ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 18 + Subfield `NAME' = `FF01' + Subfield `TYPE' = `Composite ' + Subfield `FILE' = `SC01FF01.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 19\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 19\1FNP01\1FPoint-Node ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 19 + Subfield `NAME' = `NP01' + Subfield `TYPE' = `Point-Node ' + Subfield `FILE' = `SC01NP01.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 20\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 20\1FNA01\1FPoint-Node ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 20 + Subfield `NAME' = `NA01' + Subfield `TYPE' = `Point-Node ' + Subfield `FILE' = `SC01NA01.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 21\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 21\1FNO01\1FPoint-Node ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 21 + Subfield `NAME' = `NO01' + Subfield `TYPE' = `Point-Node ' + Subfield `FILE' = `SC01NO01.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 22\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 22\1FLE01\1FLine ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 22 + Subfield `NAME' = `LE01' + Subfield `TYPE' = `Line ' + Subfield `FILE' = `SC01LE01.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +DDFRecord: + nReuseHeader = 1 + nDataSize = 87 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 23\1E' + DDFField: + Tag = `CATD' + DataSize = 65 + Data = `CATD\1F 23\1FPC01\1FPolygon ...' + Subfield `MODN' = `CATD' + Subfield `RCID' = 23 + Subfield `NAME' = `PC01' + Subfield `TYPE' = `Polygon ' + Subfield `FILE' = `SC01PC01.DDF' + Subfield `EXTR' = `N' + Subfield `MVER' = ` ' +--------------------------------------------------------------------- +-- SC01LE01.DDF +--------------------------------------------------------------------- +DDFModule: + _recLength = 441 + _interchangeLevel = 2 + _leaderIden = L + _inlineCodeExtensionIndicator = + _versionNumber = + _appIndicator = + _fieldControlLength = 6 + _fieldAreaStart = 106 + _sizeFieldLength = 2 + _sizeFieldPos = 3 + _sizeFieldTag = 4 + DDFFieldDefn: + Tag = `0000' + _fieldName = `SC01LE01' + _arrayDescr = `' + _formatControls = `' + _data_struct_code = elementary + _data_type_code = char_string + DDFFieldDefn: + Tag = `0001' + _fieldName = `DDF RECORD IDENTIFIER' + _arrayDescr = `' + _formatControls = `' + _data_struct_code = elementary + _data_type_code = implicit_point + DDFFieldDefn: + Tag = `LINE' + _fieldName = `LINE' + _arrayDescr = `MODN!RCID!OBRP' + _formatControls = `(A(4),I(6),A(2))' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `MODN' + FormatString = `A(4)' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `I(6)' + DDFSubfieldDefn: + Label = `OBRP' + FormatString = `A(2)' + DDFFieldDefn: + Tag = `ATID' + _fieldName = `ATTRIBUTE ID' + _arrayDescr = `*MODN!RCID' + _formatControls = `(A(4),I(6))' + _data_struct_code = array + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `MODN' + FormatString = `A(4)' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `I(6)' + DDFFieldDefn: + Tag = `PIDL' + _fieldName = `POLYGON ID LEFT' + _arrayDescr = `MODN!RCID' + _formatControls = `(A(4),I(6))' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `MODN' + FormatString = `A(4)' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `I(6)' + DDFFieldDefn: + Tag = `PIDR' + _fieldName = `POLYGON ID RIGHT' + _arrayDescr = `MODN!RCID' + _formatControls = `(A(4),I(6))' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `MODN' + FormatString = `A(4)' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `I(6)' + DDFFieldDefn: + Tag = `SNID' + _fieldName = `STARTNODE ID' + _arrayDescr = `MODN!RCID' + _formatControls = `(A(4),I(6))' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `MODN' + FormatString = `A(4)' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `I(6)' + DDFFieldDefn: + Tag = `ENID' + _fieldName = `ENDNODE ID' + _arrayDescr = `MODN!RCID' + _formatControls = `(A(4),I(6))' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `MODN' + FormatString = `A(4)' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `I(6)' + DDFFieldDefn: + Tag = `SADR' + _fieldName = `SPATIAL ADDRESS' + _arrayDescr = `*X!Y' + _formatControls = `((2B(32)))' + _data_struct_code = array + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `X' + FormatString = `B(32)' + DDFSubfieldDefn: + Label = `Y' + FormatString = `B(32)' +DDFRecord: + nReuseHeader = 0 + nDataSize = 321 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 1\1E' + DDFField: + Tag = `LINE' + DataSize = 13 + Data = `LE01 1LE\1E' + Subfield `MODN' = `LE01' + Subfield `RCID' = 1 + Subfield `OBRP' = `LE' + DDFField: + Tag = `PIDL' + DataSize = 11 + Data = `PC01 1\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 1 + DDFField: + Tag = `PIDR' + DataSize = 11 + Data = `PC01 3\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 3 + DDFField: + Tag = `SNID' + DataSize = 11 + Data = `NO01 2\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 2 + DDFField: + Tag = `ENID' + DataSize = 11 + Data = `NO01 1\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 1 + DDFField: + Tag = `SADR' + DataSize = 193 + Data = `\03`\18\FFFFFFEA\18\FFFFFFBD\02~\03`\FFFFFFD4\FFFFFF8D\18\FFFFFFBD\03\FFFFFFDF\03a\FFFFFF900\18\FFFFFFBD\05|\03bK\FFFFFFD4\18\FFFFFFBD\06\FFFFFFDD\03c\07v\18\FFFFFFBD\08z...' + Subfield `X' = 56629482 + Subfield `Y' = 415040126 + Subfield `X' = 56677517 + Subfield `Y' = 415040479 + Subfield `X' = 56725552 + Subfield `Y' = 415040892 + Subfield `X' = 56773588 + Subfield `Y' = 415041245 + Subfield `X' = 56821622 + Subfield `Y' = 415041658 + Subfield `X' = 56869657 + Subfield `Y' = 415042072 + Subfield `X' = 56917692 + Subfield `Y' = 415042424 + Subfield `X' = 56965727 + Subfield `Y' = 415042838 + Subfield `X' = 57013823 + Subfield `Y' = 415043252 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 273 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 2\1E' + DDFField: + Tag = `LINE' + DataSize = 13 + Data = `LE01 2LE\1E' + Subfield `MODN' = `LE01' + Subfield `RCID' = 2 + Subfield `OBRP' = `LE' + DDFField: + Tag = `PIDL' + DataSize = 11 + Data = `PC01 2\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 2 + DDFField: + Tag = `PIDR' + DataSize = 11 + Data = `PC01 3\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 3 + DDFField: + Tag = `SNID' + DataSize = 11 + Data = `NO01 3\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 3 + DDFField: + Tag = `ENID' + DataSize = 11 + Data = `NO01 3\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 3 + DDFField: + Tag = `SADR' + DataSize = 145 + Data = `\03a\1E\0A\18\FFFFFFBBp&\03a\17-\18\FFFFFFBBk\17\03a\11\FFFFFFBE\18\FFFFFFBBfH\03a\0E\FFFFFFEB\18\FFFFFFBBb5\03a\0Fv\18\FFFFFFBBZ\FFFFFF98...' + Subfield `X' = 56696330 + Subfield `Y' = 414937126 + Subfield `X' = 56694573 + Subfield `Y' = 414935831 + Subfield `X' = 56693182 + Subfield `Y' = 414934600 + Subfield `X' = 56692459 + Subfield `Y' = 414933557 + Subfield `X' = 56692598 + Subfield `Y' = 414931608 + Subfield `X' = 56692974 + Subfield `Y' = 414930453 + Subfield `X' = 56693957 + Subfield `Y' = 414929547 + Subfield `X' = 56695241 + Subfield `Y' = 414929131 + Subfield `X' = 56696765 + Subfield `Y' = 414929083 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 521 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 3\1E' + DDFField: + Tag = `LINE' + DataSize = 13 + Data = `LE01 3LE\1E' + Subfield `MODN' = `LE01' + Subfield `RCID' = 3 + Subfield `OBRP' = `LE' + DDFField: + Tag = `PIDL' + DataSize = 11 + Data = `PC01 4\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 4 + DDFField: + Tag = `PIDR' + DataSize = 11 + Data = `PC01 3\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 3 + DDFField: + Tag = `SNID' + DataSize = 11 + Data = `NO01 4\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 4 + DDFField: + Tag = `ENID' + DataSize = 11 + Data = `NO01 4\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 4 + DDFField: + Tag = `SADR' + DataSize = 393 + Data = `\03k\0F\11\18\FFFFFFB8\FFFFFF96\FFFFFFFC\03k\16\FFFFFF80\18\FFFFFFB8\FFFFFFAC{\03k\19\FFFFFFBD\18\FFFFFFB8\FFFFFFB7\FFFFFFF0\03k\1D7\18\FFFFFFB8\FFFFFFC2\FFFFFFEC\03k!f\18\FFFFFFB8\FFFFFFCE\FFFFFFA1...' + Subfield `X' = 57347857 + Subfield `Y' = 414750460 + Subfield `X' = 57349760 + Subfield `Y' = 414755963 + Subfield `X' = 57350589 + Subfield `Y' = 414758896 + Subfield `X' = 57351479 + Subfield `Y' = 414761708 + Subfield `X' = 57352550 + Subfield `Y' = 414764705 + Subfield `X' = 57352418 + Subfield `Y' = 414765984 + Subfield `X' = 57351435 + Subfield `Y' = 414766829 + Subfield `X' = 57348441 + Subfield `Y' = 414767656 + Subfield `X' = 57343968 + Subfield `Y' = 414770239 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 146 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 4\1E' + DDFField: + Tag = `LINE' + DataSize = 13 + Data = `LE01 4LE\1E' + Subfield `MODN' = `LE01' + Subfield `RCID' = 4 + Subfield `OBRP' = `LE' + DDFField: + Tag = `PIDL' + DataSize = 11 + Data = `PC01 3\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 3 + DDFField: + Tag = `PIDR' + DataSize = 11 + Data = `PC01 1\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 1 + DDFField: + Tag = `SNID' + DataSize = 11 + Data = `NO01 2\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 2 + DDFField: + Tag = `ENID' + DataSize = 11 + Data = `NO01 5\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 5 + DDFField: + Tag = `SADR' + DataSize = 25 + Data = `\03`\18\FFFFFFEA\18\FFFFFFBD\02~\03`\1BN\18\FFFFFFBB\FFFFFFD0\FFFFFFFD\03`#\FFFFFFEB\18\FFFFFFB7\FFFFFF95\FFFFFF8D\1E' + Subfield `X' = 56629482 + Subfield `Y' = 415040126 + Subfield `X' = 56630094 + Subfield `Y' = 414961917 + Subfield `X' = 56632299 + Subfield `Y' = 414684557 +DDFRecord: + nReuseHeader = 0 + nDataSize = 138 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 5\1E' + DDFField: + Tag = `LINE' + DataSize = 13 + Data = `LE01 5LE\1E' + Subfield `MODN' = `LE01' + Subfield `RCID' = 5 + Subfield `OBRP' = `LE' + DDFField: + Tag = `PIDL' + DataSize = 11 + Data = `PC01 5\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 5 + DDFField: + Tag = `PIDR' + DataSize = 11 + Data = `PC01 1\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 1 + DDFField: + Tag = `SNID' + DataSize = 11 + Data = `NO01 5\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 5 + DDFField: + Tag = `ENID' + DataSize = 11 + Data = `NO01 7\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 7 + DDFField: + Tag = `SADR' + DataSize = 17 + Data = `\03`#\FFFFFFEB\18\FFFFFFB7\FFFFFF95\FFFFFF8D\03`%c\18\FFFFFFB6\FFFFFFEBL\1E' + Subfield `X' = 56632299 + Subfield `Y' = 414684557 + Subfield `X' = 56632675 + Subfield `Y' = 414640972 +DDFRecord: + nReuseHeader = 0 + nDataSize = 377 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 6\1E' + DDFField: + Tag = `LINE' + DataSize = 13 + Data = `LE01 6LE\1E' + Subfield `MODN' = `LE01' + Subfield `RCID' = 6 + Subfield `OBRP' = `LE' + DDFField: + Tag = `PIDL' + DataSize = 11 + Data = `PC01 6\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 6 + DDFField: + Tag = `PIDR' + DataSize = 11 + Data = `PC01 3\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 3 + DDFField: + Tag = `SNID' + DataSize = 11 + Data = `NO01 6\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 6 + DDFField: + Tag = `ENID' + DataSize = 11 + Data = `NO01 6\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 6 + DDFField: + Tag = `SADR' + DataSize = 249 + Data = `\03h\0CM\18\FFFFFFB6\FFFFFFEF#\03h\0C\FFFFFFCC\18\FFFFFFB6\FFFFFFED\00\03h\11\\18\FFFFFFB6\FFFFFFE8\FFFFFFC0\03h\15/\18\FFFFFFB6\FFFFFFE6\FFFFFFE1\03h+\FFFFFFE1\18\FFFFFFB6\FFFFFFDE\FFFFFF80...' + Subfield `X' = 57150541 + Subfield `Y' = 414641955 + Subfield `X' = 57150668 + Subfield `Y' = 414641408 + Subfield `X' = 57151836 + Subfield `Y' = 414640320 + Subfield `X' = 57152815 + Subfield `Y' = 414639841 + Subfield `X' = 57158625 + Subfield `Y' = 414637696 + Subfield `X' = 57159664 + Subfield `Y' = 414637462 + Subfield `X' = 57160944 + Subfield `Y' = 414637473 + Subfield `X' = 57162828 + Subfield `Y' = 414638098 + Subfield `X' = 57165074 + Subfield `Y' = 414639215 + ... +DDFRecord: + nReuseHeader = 0 + nDataSize = 138 + DDFField: + Tag = `0001' + DataSize = 7 + Data = ` 7\1E' + DDFField: + Tag = `LINE' + DataSize = 13 + Data = `LE01 7LE\1E' + Subfield `MODN' = `LE01' + Subfield `RCID' = 7 + Subfield `OBRP' = `LE' + DDFField: + Tag = `PIDL' + DataSize = 11 + Data = `PC01 7\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 7 + DDFField: + Tag = `PIDR' + DataSize = 11 + Data = `PC01 1\1E' + Subfield `MODN' = `PC01' + Subfield `RCID' = 1 + DDFField: + Tag = `SNID' + DataSize = 11 + Data = `NO01 7\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 7 + DDFField: + Tag = `ENID' + DataSize = 11 + Data = `NO01 8\1E' + Subfield `MODN' = `NO01' + Subfield `RCID' = 8 + DDFField: + Tag = `SADR' + DataSize = 17 + Data = `\03`%c\18\FFFFFFB6\FFFFFFEBL\03`%\FFFFFF8A\18\FFFFFFB6\FFFFFFD9p\1E' +--------------------------------------------------------------------- +-- TSTPAGEO.DDF +--------------------------------------------------------------------- +DDFModule: + _recLength = 682 + _interchangeLevel = 2 + _leaderIden = L + _inlineCodeExtensionIndicator = + _versionNumber = + _appIndicator = + _fieldControlLength = 6 + _fieldAreaStart = 75 + _sizeFieldLength = 3 + _sizeFieldPos = 3 + _sizeFieldTag = 4 + DDFFieldDefn: + Tag = `0000' + _fieldName = `TSTPAGEO' + _arrayDescr = `' + _formatControls = `' + _data_struct_code = elementary + _data_type_code = char_string + DDFFieldDefn: + Tag = `0001' + _fieldName = `DDF RECORD IDENTIFIER' + _arrayDescr = `' + _formatControls = `' + _data_struct_code = elementary + _data_type_code = implicit_point + DDFFieldDefn: + Tag = `ATPR' + _fieldName = `ATTRIBUTE PRIMARY' + _arrayDescr = `MODN!RCID' + _formatControls = `(A,I)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `MODN' + FormatString = `A' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `I' + DDFFieldDefn: + Tag = `OBID' + _fieldName = `SPATIAL OBJECT ID' + _arrayDescr = `MODN!RCID' + _formatControls = `(A,I)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `MODN' + FormatString = `A' + DDFSubfieldDefn: + Label = `RCID' + FormatString = `I' + DDFFieldDefn: + Tag = `ATTP' + _fieldName = `PRIMARY ATTRIBUTES' + _arrayDescr = `PUB_DATE!DESIG!PID!STATE!COUNTY!QUAD!HORZ_DATUM!LATITUDE!LONGITUDE!HORZ_CTRL!HORZ_ORDER!VERT_DATUM!HEIGHT_M!HEIGHT_FT!VERT_CTRL!VERT_ORDER!VERT_CLASS!EPOCH_DATE!X_GEO_COOR!Y_GEO_COOR!Z_GEO_COOR!LAPLAC_COR!LAPLAC_COR_DET!ELP_HT!ELP_HT_DET!ELP_ORDER!ELP_CLASS!GEOID_HT!GEOID_HT_DET!DYN_HT_M!DYN_HT_FT!DYN_HT_DET!MOD_GRAV!MOD_GR_DET!MON_MARK!MON_SETNG!MON_PROJ!MON_STAMP!MON_STABIL!SATT_OBS!SATT_DATE!ROD_PIPE_D!SLEEVE_DEP' + _formatControls = `(12A,2R,3A,4R,2(R,A),2A,R,A,2R,10A,2R)' + _data_struct_code = vector + _data_type_code = mixed_data_type + DDFSubfieldDefn: + Label = `PUB_DATE' + FormatString = `A' + DDFSubfieldDefn: + Label = `DESIG' + FormatString = `A' + DDFSubfieldDefn: + Label = `PID' + FormatString = `A' + DDFSubfieldDefn: + Label = `STATE' + FormatString = `A' + DDFSubfieldDefn: + Label = `COUNTY' + FormatString = `A' + DDFSubfieldDefn: + Label = `QUAD' + FormatString = `A' + DDFSubfieldDefn: + Label = `HORZ_DATUM' + FormatString = `A' + DDFSubfieldDefn: + Label = `LATITUDE' + FormatString = `A' + DDFSubfieldDefn: + Label = `LONGITUDE' + FormatString = `A' + DDFSubfieldDefn: + Label = `HORZ_CTRL' + FormatString = `A' + DDFSubfieldDefn: + Label = `HORZ_ORDER' + FormatString = `A' + DDFSubfieldDefn: + Label = `VERT_DATUM' + FormatString = `A' + DDFSubfieldDefn: + Label = `HEIGHT_M' + FormatString = `R' + DDFSubfieldDefn: + Label = `HEIGHT_FT' + FormatString = `R' + DDFSubfieldDefn: + Label = `VERT_CTRL' + FormatString = `A' + DDFSubfieldDefn: + Label = `VERT_ORDER' + FormatString = `A' + DDFSubfieldDefn: + Label = `VERT_CLASS' + FormatString = `A' + DDFSubfieldDefn: + Label = `EPOCH_DATE' + FormatString = `R' + DDFSubfieldDefn: + Label = `X_GEO_COOR' + FormatString = `R' + DDFSubfieldDefn: + Label = `Y_GEO_COOR' + FormatString = `R' + DDFSubfieldDefn: + Label = `Z_GEO_COOR' + FormatString = `R' + DDFSubfieldDefn: + Label = `LAPLAC_COR' + FormatString = `R' + DDFSubfieldDefn: + Label = `LAPLAC_COR_DET' + FormatString = `A' + DDFSubfieldDefn: + Label = `ELP_HT' + FormatString = `R' + DDFSubfieldDefn: + Label = `ELP_HT_DET' + FormatString = `A' + DDFSubfieldDefn: + Label = `ELP_ORDER' + FormatString = `A' + DDFSubfieldDefn: + Label = `ELP_CLASS' + FormatString = `A' + DDFSubfieldDefn: + Label = `GEOID_HT' + FormatString = `R' + DDFSubfieldDefn: + Label = `GEOID_HT_DET' + FormatString = `A' + DDFSubfieldDefn: + Label = `DYN_HT_M' + FormatString = `R' + DDFSubfieldDefn: + Label = `DYN_HT_FT' + FormatString = `R' + DDFSubfieldDefn: + Label = `DYN_HT_DET' + FormatString = `A' + DDFSubfieldDefn: + Label = `MOD_GRAV' + FormatString = `A' + DDFSubfieldDefn: + Label = `MOD_GR_DET' + FormatString = `A' + DDFSubfieldDefn: + Label = `MON_MARK' + FormatString = `A' + DDFSubfieldDefn: + Label = `MON_SETNG' + FormatString = `A' + DDFSubfieldDefn: + Label = `MON_PROJ' + FormatString = `A' + DDFSubfieldDefn: + Label = `MON_STAMP' + FormatString = `A' + DDFSubfieldDefn: + Label = `MON_STABIL' + FormatString = `A' + DDFSubfieldDefn: + Label = `SATT_OBS' + FormatString = `A' + DDFSubfieldDefn: + Label = `SATT_DATE' + FormatString = `A' + DDFSubfieldDefn: + Label = `ROD_PIPE_D' + FormatString = `R' + DDFSubfieldDefn: + Label = `SLEEVE_DEP' + FormatString = `R' +DDFRecord: + nReuseHeader = 0 + nDataSize = 367 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `1\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `AGEO\1F1\1E' + Subfield `MODN' = `AGEO' + Subfield `RCID' = 1 + DDFField: + Tag = `OBID' + DataSize = 7 + Data = `NE01\1F1\1E' + Subfield `MODN' = `NE01' + Subfield `RCID' = 1 + DDFField: + Tag = `ATTP' + DataSize = 314 + Data = `JANUARY 31, 2000\1FG 687\1FEV0052\1FCA\1FSAN BER...' + Subfield `PUB_DATE' = `JANUARY 31, 2000' + Subfield `DESIG' = `G 687' + Subfield `PID' = `EV0052' + Subfield `STATE' = `CA' + Subfield `COUNTY' = `SAN BERNARDINO' + Subfield `QUAD' = `BARSTOW (1974)' + Subfield `HORZ_DATUM' = `NAD 83(1986)' + Subfield `LATITUDE' = `34 58 52. N' + Subfield `LONGITUDE' = `117 00 03. W' + Subfield `HORZ_CTRL' = `SCALED' + Subfield `HORZ_ORDER' = `' + Subfield `VERT_DATUM' = `NAVD 88' + Subfield `HEIGHT_M' = 815.146000 + Subfield `HEIGHT_FT' = 2674.360000 + Subfield `VERT_CTRL' = `ADJUSTED' + Subfield `VERT_ORDER' = `FIRST ' + Subfield `VERT_CLASS' = `I' + Subfield `EPOCH_DATE' = 0.000000 + Subfield `X_GEO_COOR' = 0.000000 + Subfield `Y_GEO_COOR' = 0.000000 + Subfield `Z_GEO_COOR' = 0.000000 + Subfield `LAPLAC_COR' = 0.000000 + Subfield `LAPLAC_COR_DET' = `' + Subfield `ELP_HT' = 0.000000 + Subfield `ELP_HT_DET' = `' + Subfield `ELP_ORDER' = `' + Subfield `ELP_CLASS' = `' + Subfield `GEOID_HT' = -31.200000 + Subfield `GEOID_HT_DET' = `GEOID99' + Subfield `DYN_HT_M' = 814.215000 + Subfield `DYN_HT_FT' = 2671.300000 + Subfield `DYN_HT_DET' = `COMP' + Subfield `MOD_GRAV' = `979466.3' + Subfield `MOD_GR_DET' = `NAVD 88' + Subfield `MON_MARK' = `DB = BENCH MARK DISK' + Subfield `MON_SETNG' = `7 = SET IN TOP OF CONCRETE MONUMENT' + Subfield `MON_PROJ' = `' + Subfield `MON_STAMP' = `G 687 1943' + Subfield `MON_STABIL' = `C' + Subfield `SATT_OBS' = `' + Subfield `SATT_DATE' = `' + Subfield `ROD_PIPE_D' = 0.000000 + Subfield `SLEEVE_DEP' = 0.000000 +DDFRecord: + nReuseHeader = 0 + nDataSize = 407 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `2\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `AGEO\1F2\1E' + Subfield `MODN' = `AGEO' + Subfield `RCID' = 2 + DDFField: + Tag = `OBID' + DataSize = 7 + Data = `NE01\1F2\1E' + Subfield `MODN' = `NE01' + Subfield `RCID' = 2 + DDFField: + Tag = `ATTP' + DataSize = 354 + Data = `JANUARY 31, 2000\1FC 1297\1FEV3524\1FCA\1FSAN BE...' + Subfield `PUB_DATE' = `JANUARY 31, 2000' + Subfield `DESIG' = `C 1297' + Subfield `PID' = `EV3524' + Subfield `STATE' = `CA' + Subfield `COUNTY' = `SAN BERNARDINO' + Subfield `QUAD' = `NEBO (1971)' + Subfield `HORZ_DATUM' = `NAD 83(1986)' + Subfield `LATITUDE' = `34 59 23. N' + Subfield `LONGITUDE' = `116 59 07. W' + Subfield `HORZ_CTRL' = `SCALED' + Subfield `HORZ_ORDER' = `' + Subfield `VERT_DATUM' = `NAVD 88' + Subfield `HEIGHT_M' = 810.587000 + Subfield `HEIGHT_FT' = 2659.400000 + Subfield `VERT_CTRL' = `ADJUSTED' + Subfield `VERT_ORDER' = `FIRST ' + Subfield `VERT_CLASS' = `I' + Subfield `EPOCH_DATE' = 0.000000 + Subfield `X_GEO_COOR' = 0.000000 + Subfield `Y_GEO_COOR' = 0.000000 + Subfield `Z_GEO_COOR' = 0.000000 + Subfield `LAPLAC_COR' = 0.000000 + Subfield `LAPLAC_COR_DET' = `' + Subfield `ELP_HT' = 0.000000 + Subfield `ELP_HT_DET' = `' + Subfield `ELP_ORDER' = `' + Subfield `ELP_CLASS' = `' + Subfield `GEOID_HT' = -31.190000 + Subfield `GEOID_HT_DET' = `GEOID99' + Subfield `DYN_HT_M' = 809.663000 + Subfield `DYN_HT_FT' = 2656.370000 + Subfield `DYN_HT_DET' = `COMP' + Subfield `MOD_GRAV' = `979467.8' + Subfield `MOD_GR_DET' = `NAVD 88' + Subfield `MON_MARK' = `DB = BENCH MARK DISK' + Subfield `MON_SETNG' = `16 = (FASTENED TO) A METAL ROD WITH BASE PLATE BURIED/SCREWED G: INTO GROUND' + Subfield `MON_PROJ' = `' + Subfield `MON_STAMP' = `C 1297 1977' + Subfield `MON_STABIL' = `C' + Subfield `SATT_OBS' = `' + Subfield `SATT_DATE' = `' + Subfield `ROD_PIPE_D' = 0.000000 + Subfield `SLEEVE_DEP' = 0.000000 +DDFRecord: + nReuseHeader = 0 + nDataSize = 373 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `3\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `AGEO\1F3\1E' + Subfield `MODN' = `AGEO' + Subfield `RCID' = 3 + DDFField: + Tag = `OBID' + DataSize = 7 + Data = `NE01\1F3\1E' + Subfield `MODN' = `NE01' + Subfield `RCID' = 3 + DDFField: + Tag = `ATTP' + DataSize = 320 + Data = `JANUARY 31, 2000\1FJ 687\1FFT0044\1FCA\1FSAN BER...' + Subfield `PUB_DATE' = `JANUARY 31, 2000' + Subfield `DESIG' = `J 687' + Subfield `PID' = `FT0044' + Subfield `STATE' = `CA' + Subfield `COUNTY' = `SAN BERNARDINO' + Subfield `QUAD' = `LANE MOUNTAIN (1986)' + Subfield `HORZ_DATUM' = `NAD 83(1986)' + Subfield `LATITUDE' = `35 00 07. N' + Subfield `LONGITUDE' = `116 58 39. W' + Subfield `HORZ_CTRL' = `SCALED' + Subfield `HORZ_ORDER' = `' + Subfield `VERT_DATUM' = `NAVD 88' + Subfield `HEIGHT_M' = 838.565000 + Subfield `HEIGHT_FT' = 2751.190000 + Subfield `VERT_CTRL' = `ADJUSTED' + Subfield `VERT_ORDER' = `FIRST ' + Subfield `VERT_CLASS' = `I' + Subfield `EPOCH_DATE' = 0.000000 + Subfield `X_GEO_COOR' = 0.000000 + Subfield `Y_GEO_COOR' = 0.000000 + Subfield `Z_GEO_COOR' = 0.000000 + Subfield `LAPLAC_COR' = 0.000000 + Subfield `LAPLAC_COR_DET' = `' + Subfield `ELP_HT' = 0.000000 + Subfield `ELP_HT_DET' = `' + Subfield `ELP_ORDER' = `' + Subfield `ELP_CLASS' = `' + Subfield `GEOID_HT' = -31.160000 + Subfield `GEOID_HT_DET' = `GEOID99' + Subfield `DYN_HT_M' = 837.601000 + Subfield `DYN_HT_FT' = 2748.030000 + Subfield `DYN_HT_DET' = `COMP' + Subfield `MOD_GRAV' = `979457.7' + Subfield `MOD_GR_DET' = `NAVD 88' + Subfield `MON_MARK' = `DB = BENCH MARK DISK' + Subfield `MON_SETNG' = `7 = SET IN TOP OF CONCRETE MONUMENT' + Subfield `MON_PROJ' = `' + Subfield `MON_STAMP' = `J 687 1943' + Subfield `MON_STABIL' = `C' + Subfield `SATT_OBS' = `' + Subfield `SATT_DATE' = `' + Subfield `ROD_PIPE_D' = 0.000000 + Subfield `SLEEVE_DEP' = 0.000000 +DDFRecord: + nReuseHeader = 0 + nDataSize = 387 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `4\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `AGEO\1F4\1E' + Subfield `MODN' = `AGEO' + Subfield `RCID' = 4 + DDFField: + Tag = `OBID' + DataSize = 7 + Data = `NE01\1F4\1E' + Subfield `MODN' = `NE01' + Subfield `RCID' = 4 + DDFField: + Tag = `ATTP' + DataSize = 334 + Data = `JANUARY 31, 2000\1F2892\1FFT0043\1FCA\1FSAN BERN...' + Subfield `PUB_DATE' = `JANUARY 31, 2000' + Subfield `DESIG' = `2892' + Subfield `PID' = `FT0043' + Subfield `STATE' = `CA' + Subfield `COUNTY' = `SAN BERNARDINO' + Subfield `QUAD' = `LANE MOUNTAIN (1986)' + Subfield `HORZ_DATUM' = `NAD 83(1986)' + Subfield `LATITUDE' = `35 00 44. N' + Subfield `LONGITUDE' = `116 58 07. W' + Subfield `HORZ_CTRL' = `SCALED' + Subfield `HORZ_ORDER' = `' + Subfield `VERT_DATUM' = `NAVD 88' + Subfield `HEIGHT_M' = 882.390000 + Subfield `HEIGHT_FT' = 2894.970000 + Subfield `VERT_CTRL' = `ADJUSTED' + Subfield `VERT_ORDER' = `FIRST ' + Subfield `VERT_CLASS' = `I' + Subfield `EPOCH_DATE' = 0.000000 + Subfield `X_GEO_COOR' = 0.000000 + Subfield `Y_GEO_COOR' = 0.000000 + Subfield `Z_GEO_COOR' = 0.000000 + Subfield `LAPLAC_COR' = 0.000000 + Subfield `LAPLAC_COR_DET' = `' + Subfield `ELP_HT' = 0.000000 + Subfield `ELP_HT_DET' = `' + Subfield `ELP_ORDER' = `' + Subfield `ELP_CLASS' = `' + Subfield `GEOID_HT' = -31.140000 + Subfield `GEOID_HT_DET' = `GEOID99' + Subfield `DYN_HT_M' = 881.369000 + Subfield `DYN_HT_FT' = 2891.620000 + Subfield `DYN_HT_DET' = `COMP' + Subfield `MOD_GRAV' = `979447.3' + Subfield `MOD_GR_DET' = `NAVD 88' + Subfield `MON_MARK' = `DD = SURVEY DISK' + Subfield `MON_SETNG' = `17 = SET INTO TOP OF METAL PIPE DRIVEN INTO GROUND' + Subfield `MON_PROJ' = `' + Subfield `MON_STAMP' = `2892 B 26 1906' + Subfield `MON_STABIL' = `D' + Subfield `SATT_OBS' = `' + Subfield `SATT_DATE' = `' + Subfield `ROD_PIPE_D' = 0.000000 + Subfield `SLEEVE_DEP' = 0.000000 +DDFRecord: + nReuseHeader = 0 + nDataSize = 373 + DDFField: + Tag = `0001' + DataSize = 2 + Data = `5\1E' + DDFField: + Tag = `ATPR' + DataSize = 7 + Data = `AGEO\1F5\1E' + Subfield `MODN' = `AGEO' + Subfield `RCID' = 5 + DDFField: + Tag = `OBID' + DataSize = 7 + Data = `NE01\1F5\1E' + Subfield `MODN' = `NE01' + Subfield `RCID' = 5 + DDFField: + Tag = `ATTP' + DataSize = 320 + Data = `JANUARY 31, 2000\1FK 687\1FFT0042\1FCA\1FSAN BER...' + Subfield `PUB_DATE' = `JANUARY 31, 2000' + Subfield `DESIG' = `K 687' + Subfield `PID' = `FT0042' + Subfield `STATE' = `CA' + Subfield `COUNTY' = `SAN BERNARDINO' + Subfield `QUAD' = `LANE MOUNTAIN (1986)' + Subfield `HORZ_DATUM' = `NAD 83(1986)' + Subfield `LATITUDE' = `35 00 51. N' + Subfield `LONGITUDE' = `116 58 07. W' + Subfield `HORZ_CTRL' = `SCALED' + Subfield `HORZ_ORDER' = `' + Subfield `VERT_DATUM' = `NAVD 88' + Subfield `HEIGHT_M' = 889.871000 + Subfield `HEIGHT_FT' = 2919.520000 + Subfield `VERT_CTRL' = `ADJUSTED' + Subfield `VERT_ORDER' = `FIRST ' + Subfield `VERT_CLASS' = `I' + Subfield `EPOCH_DATE' = 0.000000 + Subfield `X_GEO_COOR' = 0.000000 + Subfield `Y_GEO_COOR' = 0.000000 + Subfield `Z_GEO_COOR' = 0.000000 + Subfield `LAPLAC_COR' = 0.000000 + Subfield `LAPLAC_COR_DET' = `' + Subfield `ELP_HT' = 0.000000 + Subfield `ELP_HT_DET' = `' diff --git a/bazaar/plugin/gdal/frmts/iso8211/teststream.sh b/bazaar/plugin/gdal/frmts/iso8211/teststream.sh new file mode 100644 index 000000000..c77c517a6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/teststream.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +DATADIR=testdata + +for file in 1183CEL0.DDF CA49995B.000 TWFLA009.DDF SC01CATD.DDF SC01LE01.DDF TSTPAGEO.DDF ; \ + do + + echo "---------------------------------------------------------------------" + echo "-- $file" + echo "---------------------------------------------------------------------" + 8211dump $DATADIR/$file | head -500 + +done + + + diff --git a/bazaar/plugin/gdal/frmts/iso8211/timetest.cpp b/bazaar/plugin/gdal/frmts/iso8211/timetest.cpp new file mode 100644 index 000000000..163c451e7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/iso8211/timetest.cpp @@ -0,0 +1,183 @@ +/* **************************************************************************** + * $Id: timetest.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: SDTS Translator + * Purpose: Example program dumping data in 8211 data to stdout. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "iso8211.h" + +static void ViewRecordField( DDFField * poField ); +static int ViewSubfield( DDFSubfieldDefn *poSFDefn, + const char * pachFieldData, + int nBytesRemaining ); + +/* **********************************************************************/ +/* main() */ +/* **********************************************************************/ + +int main( int nArgc, char ** papszArgv ) + +{ + DDFModule oModule; + const char *pszFilename; + int i; + + if( nArgc > 1 ) + pszFilename = papszArgv[1]; + else + { + printf( "Usage: 8211view filename\n" ); + exit( 1 ); + } + + for( i = 0; i < 40; i++ ) + { +/* -------------------------------------------------------------------- */ +/* Open the file. Note that by default errors are reported to */ +/* stderr, so we don't bother doing it ourselves. */ +/* -------------------------------------------------------------------- */ + if( !oModule.Open( pszFilename ) ) + { + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Loop reading records till there are none left. */ +/* -------------------------------------------------------------------- */ + DDFRecord *poRecord; + int nRecordCount = 0; + int nFieldCount = 0; + + + while( (poRecord = oModule.ReadRecord()) != NULL ) + { + /* ------------------------------------------------------------ */ + /* Loop over each field in this particular record. */ + /* ------------------------------------------------------------ */ + for( int iField = 0; iField < poRecord->GetFieldCount(); iField++ ) + { + DDFField *poField = poRecord->GetField( iField ); + + ViewRecordField( poField ); + + nFieldCount++; + } + + nRecordCount++; + } + + oModule.Close(); + + printf( "Read %d records, %d fields.\n", nRecordCount, nFieldCount ); + } +} + +/* **********************************************************************/ +/* ViewRecordField() */ +/* */ +/* Dump the contents of a field instance in a record. */ +/* **********************************************************************/ + +static void ViewRecordField( DDFField * poField ) + +{ + int nBytesRemaining; + const char *pachFieldData; + DDFFieldDefn *poFieldDefn = poField->GetFieldDefn(); + + // Get pointer to this fields raw data. We will move through + // it consuming data as we report subfield values. + + pachFieldData = poField->GetData(); + nBytesRemaining = poField->GetDataSize(); + + /* -------------------------------------------------------- */ + /* Loop over the repeat count for this fields */ + /* subfields. The repeat count will almost */ + /* always be one. */ + /* -------------------------------------------------------- */ + int iRepeat, nRepeatCount; + + nRepeatCount = poField->GetRepeatCount(); + + for( iRepeat = 0; iRepeat < nRepeatCount; iRepeat++ ) + { + + /* -------------------------------------------------------- */ + /* Loop over all the subfields of this field, advancing */ + /* the data pointer as we consume data. */ + /* -------------------------------------------------------- */ + int iSF; + + for( iSF = 0; iSF < poFieldDefn->GetSubfieldCount(); iSF++ ) + { + DDFSubfieldDefn *poSFDefn = poFieldDefn->GetSubfield( iSF ); + int nBytesConsumed; + + nBytesConsumed = ViewSubfield( poSFDefn, pachFieldData, + nBytesRemaining ); + + nBytesRemaining -= nBytesConsumed; + pachFieldData += nBytesConsumed; + } + } +} + +/* **********************************************************************/ +/* ViewSubfield() */ +/* **********************************************************************/ + +static int ViewSubfield( DDFSubfieldDefn *poSFDefn, + const char * pachFieldData, + int nBytesRemaining ) + +{ + int nBytesConsumed = 0; + + switch( poSFDefn->GetType() ) + { + case DDFInt: + poSFDefn->ExtractIntData( pachFieldData, nBytesRemaining, + &nBytesConsumed ); + break; + + case DDFFloat: + poSFDefn->ExtractFloatData( pachFieldData, nBytesRemaining, + &nBytesConsumed ); + break; + + case DDFString: + poSFDefn->ExtractStringData( pachFieldData, nBytesRemaining, + &nBytesConsumed ); + break; + + default: + break; + } + + return nBytesConsumed; +} diff --git a/bazaar/plugin/gdal/frmts/jaxapalsar/GNUmakefile b/bazaar/plugin/gdal/frmts/jaxapalsar/GNUmakefile new file mode 100644 index 000000000..2d89b129d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jaxapalsar/GNUmakefile @@ -0,0 +1,10 @@ +include ../../GDALmake.opt + +OBJ = jaxapalsardataset.o + + +default: $(OBJ:.o=.$(OBJ_EXT)) +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/jaxapalsar/frmt_palsar.html b/bazaar/plugin/gdal/frmts/jaxapalsar/frmt_palsar.html new file mode 100644 index 000000000..2d2abb031 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jaxapalsar/frmt_palsar.html @@ -0,0 +1,39 @@ + + +JAXA PALSAR Level 1.1 and Level 1.5 processed products support + + + +

    JAXA PALSAR Processed Products

    + +

    This driver provides enhanced support for processed PALSAR products from the JAXA PALSAR processor. This encompasses products acquired from the following organizations:

    + +
      +
    • JAXA (Japanese Aerospace eXploration Agency) +
    • AADN (Alaska Satellite Facility) +
    • ESA (European Space Agency) +
    + +This driver does not support products created using the Vexcel processor (i.e. products distributed by ERSDAC and affiliated organizations). + +

    Support is provided for the following features of PALSAR products:

    + +
      +
    • Reading Level 1.1 and 1.5 processed products
    • +
    • Georeferencing for Level 1.5 products
    • +
    • Basic metadata (sensor information, ground pixel spacing, etc.)
    • +
    • Multi-channel data (i.e. dual-polarization or fully polarimetric datasets)
    • +
    + +

    This is a read-only driver.

    + +

    To open a PALSAR product, select the volume directory file (for example, VOL-ALPSR000000000-P1.5_UA or VOL-ALPSR000000000-P1.1__A). The driver will then use the information contained in the volume directory file to find the various image files (the IMG-* files), as well as the Leader file. Note that the Leader file is essential for correct operation of the driver.

    + +

    See Also:

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/jaxapalsar/jaxapalsardataset.cpp b/bazaar/plugin/gdal/frmts/jaxapalsar/jaxapalsardataset.cpp new file mode 100644 index 000000000..a6717a8bf --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jaxapalsar/jaxapalsardataset.cpp @@ -0,0 +1,662 @@ +/****************************************************************************** + * $Id: jaxapalsardataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: PALSAR JAXA imagery reader + * Purpose: Support for PALSAR L1.1/1.5 imagery and appropriate metadata from + * JAXA and JAXA-supported ground stations (ASF, ESA, etc.). This + * driver does not support ERSDAC products. + * Author: Philippe Vachon + * + ****************************************************************************** + * Copyright (c) 2007, Philippe P. Vachon + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" + +CPL_CVSID("$Id: jaxapalsardataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +CPL_C_START +void GDALRegister_PALSARJaxa(void); +CPL_C_END + +#if defined(WIN32) || defined(WIN32CE) +#define SEP_STRING "\\" +#else +#define SEP_STRING "/" +#endif + +/* read binary fields */ +#ifdef CPL_LSB +#define READ_WORD(f, x) \ + do { \ + VSIFReadL( &(x), 4, 1, (f) ); \ + (x) = CPL_SWAP32( (x) ); \ + } while (0); +#define READ_SHORT(f, x) \ + do { \ + VSIFReadL( &(x), 2, 1, (f) ); \ + (x) = CPL_SWAP16( (x) ); \ + } while (0); +#else +#define READ_WORD(f, x) do { VSIFReadL( &(x), 4, 1, (f) ); } while (0); +#define READ_SHORT(f, x) do { VSIFReadL( &(x), 2, 1, (f) ); } while (0); +#endif /* def CPL_LSB */ +#define READ_BYTE(f, x) do { VSIFReadL( &(x), 1, 1, (f) ); } while (0); + +/* read floating point value stored as ASCII */ +#define READ_CHAR_FLOAT(n, l, f) \ + do {\ + char psBuf[(l+1)]; \ + psBuf[(l)] = '\0'; \ + VSIFReadL( &psBuf, (l), 1, (f) );\ + (n) = CPLAtof( psBuf );\ + } while (0); + +/* read numbers stored as ASCII */ +#define READ_CHAR_VAL(x, n, f) \ + do { \ + char psBuf[(n+1)]; \ + psBuf[(n)] = '\0';\ + VSIFReadL( &psBuf, (n), 1, (f) ); \ + (x) = atoi(psBuf); \ + } while (0); + +/* read string fields + * note: string must be size of field to be extracted + 1 + */ +#define READ_STRING(s, n, f) \ + do { \ + VSIFReadL( &(s), 1, (n), (f) ); \ + (s)[(n)] = '\0'; \ + } while (0); + +/*************************************************************************/ +/* a few key offsets in the volume directory file */ +#define VOL_DESC_RECORD_LENGTH 360 +#define FILE_PTR_RECORD_LENGTH 360 +#define NUM_RECORDS_OFFSET 160 + +/* a few key offsets and values within the File Pointer record */ +#define REF_FILE_CLASS_CODE_OFFSET 66 +#define REF_FILE_CLASS_CODE_LENGTH 4 +#define FILE_NAME_OFFSET 310 + +/* some image option descriptor records */ +#define BITS_PER_SAMPLE_OFFSET 216 +#define BITS_PER_SAMPLE_LENGTH 4 +#define SAMPLES_PER_GROUP_OFFSET 220 +#define SAMPLES_PER_GROUP_LENGTH 4 +#define NUMBER_LINES_OFFSET 236 +#define NUMBER_LINES_LENGTH 8 +#define SAR_DATA_RECORD_LENGTH_OFFSET 186 +#define SAR_DATA_RECORD_LENGTH_LENGTH 6 + +#define IMAGE_OPT_DESC_LENGTH 720 + +#define SIG_DAT_REC_OFFSET 412 +#define PROC_DAT_REC_OFFSET 192 + +/* metadata to be extracted from the leader file */ +#define LEADER_FILE_DESCRIPTOR_LENGTH 720 +#define DATA_SET_SUMMARY_LENGTH 4096 + +/* relative to end of leader file descriptor */ +#define EFFECTIVE_LOOKS_AZIMUTH_OFFSET 1174 /* floating point text */ +#define EFFECTIVE_LOOKS_AZIMUTH_LENGTH 16 + +/* relative to leader file descriptor + dataset summary length */ +#define PIXEL_SPACING_OFFSET 92 +#define LINE_SPACING_OFFSET 108 +#define ALPHANUMERIC_PROJECTION_NAME_OFFSET 412 +#define TOP_LEFT_LAT_OFFSET 1072 +#define TOP_LEFT_LON_OFFSET 1088 +#define TOP_RIGHT_LAT_OFFSET 1104 +#define TOP_RIGHT_LON_OFFSET 1120 +#define BOTTOM_RIGHT_LAT_OFFSET 1136 +#define BOTTOM_RIGHT_LON_OFFSET 1152 +#define BOTTOM_LEFT_LAT_OFFSET 1168 +#define BOTTOM_LEFT_LON_OFFSET 1184 + +/* a few useful enums */ +enum eFileType { + level_11 = 0, + level_15, + level_10 +}; + +enum ePolarization { + hh = 0, + hv, + vh, + vv +}; + +/************************************************************************/ +/* ==================================================================== */ +/* PALSARJaxaDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class PALSARJaxaRasterBand; + +class PALSARJaxaDataset : public GDALPamDataset { + friend class PALSARJaxaRasterBand; +private: + GDAL_GCP *pasGCPList; + int nGCPCount; + eFileType nFileType; +public: + PALSARJaxaDataset(); + ~PALSARJaxaDataset(); + + int GetGCPCount(); + const GDAL_GCP *GetGCPs(); + + static GDALDataset *Open( GDALOpenInfo *poOpenInfo ); + static int Identify( GDALOpenInfo *poOpenInfo ); + static void ReadMetadata( PALSARJaxaDataset *poDS, VSILFILE *fp ); +}; + +PALSARJaxaDataset::PALSARJaxaDataset() +{ + pasGCPList = NULL; + nGCPCount = 0; +} + +PALSARJaxaDataset::~PALSARJaxaDataset() +{ + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* PALSARJaxaRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class PALSARJaxaRasterBand : public GDALRasterBand { + VSILFILE *fp; + int nRasterXSize; + int nRasterYSize; + ePolarization nPolarization; + eFileType nFileType; + int nBitsPerSample; + int nSamplesPerGroup; + int nRecordSize; +public: + PALSARJaxaRasterBand( PALSARJaxaDataset *poDS, int nBand, VSILFILE *fp ); + ~PALSARJaxaRasterBand(); + + CPLErr IReadBlock( int nBlockXOff, int nBlockYOff, void *pImage ); +}; + +/************************************************************************/ +/* PALSARJaxaRasterBand() */ +/************************************************************************/ + +PALSARJaxaRasterBand::PALSARJaxaRasterBand( PALSARJaxaDataset *poDS, + int nBand, VSILFILE *fp ) +{ + this->fp = fp; + + /* Read image options record to determine the type of data */ + VSIFSeekL( fp, BITS_PER_SAMPLE_OFFSET, SEEK_SET ); + nBitsPerSample = 0; + nSamplesPerGroup = 0; + READ_CHAR_VAL( nBitsPerSample, BITS_PER_SAMPLE_LENGTH, fp ); + READ_CHAR_VAL( nSamplesPerGroup, SAMPLES_PER_GROUP_LENGTH, fp ); + + if (nBitsPerSample == 32 && nSamplesPerGroup == 2) { + eDataType = GDT_CFloat32; + nFileType = level_11; + } + else if (nBitsPerSample == 8 && nSamplesPerGroup == 2) { + eDataType = GDT_CInt16; /* shuold be 2 x signed byte */ + nFileType = level_10; + } + else { + eDataType = GDT_UInt16; + nFileType = level_15; + } + + poDS->nFileType = nFileType; + + /* Read number of range/azimuth lines */ + VSIFSeekL( fp, NUMBER_LINES_OFFSET, SEEK_SET ); + READ_CHAR_VAL( nRasterYSize, NUMBER_LINES_LENGTH, fp ); + VSIFSeekL( fp, SAR_DATA_RECORD_LENGTH_OFFSET, SEEK_SET ); + READ_CHAR_VAL( nRecordSize, SAR_DATA_RECORD_LENGTH_LENGTH, fp ); + nRasterXSize = (nRecordSize - + (nFileType != level_15 ? SIG_DAT_REC_OFFSET : PROC_DAT_REC_OFFSET)) + / ((nBitsPerSample / 8) * nSamplesPerGroup); + + poDS->nRasterXSize = nRasterXSize; + poDS->nRasterYSize = nRasterYSize; + + /* Polarization */ + switch (nBand) { + case 0: + nPolarization = hh; + SetMetadataItem( "POLARIMETRIC_INTERP", "HH" ); + break; + case 1: + nPolarization = hv; + SetMetadataItem( "POLARIMETRIC_INTERP", "HV" ); + break; + case 2: + nPolarization = vh; + SetMetadataItem( "POLARIMETRIC_INTERP", "VH" ); + break; + case 3: + nPolarization = vv; + SetMetadataItem( "POLARIMETRIC_INTERP", "VV" ); + break; + } + + /* size of block we can read */ + nBlockXSize = nRasterXSize; + nBlockYSize = 1; + + /* set the file pointer to the first SAR data record */ + VSIFSeekL( fp, IMAGE_OPT_DESC_LENGTH, SEEK_SET ); +} + +/************************************************************************/ +/* ~PALSARJaxaRasterBand() */ +/************************************************************************/ + +PALSARJaxaRasterBand::~PALSARJaxaRasterBand() +{ + if (fp) + VSIFCloseL(fp); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr PALSARJaxaRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void *pImage ) +{ + int nNumBytes = 0; + if (nFileType == level_11) { + nNumBytes = 8; + } + else { + nNumBytes = 2; + } + + int nOffset = IMAGE_OPT_DESC_LENGTH + ((nBlockYOff - 1) * nRecordSize) + + (nFileType == level_11 ? SIG_DAT_REC_OFFSET : PROC_DAT_REC_OFFSET); + + VSIFSeekL( fp, nOffset, SEEK_SET ); + VSIFReadL( pImage, nNumBytes, nRasterXSize, fp ); + +#ifdef CPL_LSB + if (nFileType == level_11) + GDALSwapWords( pImage, 4, nBlockXSize * 2, 4 ); + else + GDALSwapWords( pImage, 2, nBlockXSize, 2 ); +#endif + + return CE_None; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* PALSARJaxaDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ReadMetadata() */ +/************************************************************************/ + +int PALSARJaxaDataset::GetGCPCount() { + return nGCPCount; +} + + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *PALSARJaxaDataset::GetGCPs() { + return pasGCPList; +} + + +/************************************************************************/ +/* ReadMetadata() */ +/************************************************************************/ + +void PALSARJaxaDataset::ReadMetadata( PALSARJaxaDataset *poDS, VSILFILE *fp ) { + /* seek to the end fo the leader file descriptor */ + VSIFSeekL( fp, LEADER_FILE_DESCRIPTOR_LENGTH, SEEK_SET ); + if (poDS->nFileType == level_10) { + poDS->SetMetadataItem( "PRODUCT_LEVEL", "1.0" ); + poDS->SetMetadataItem( "AZIMUTH_LOOKS", "1.0" ); + } + else if (poDS->nFileType == level_11) { + poDS->SetMetadataItem( "PRODUCT_LEVEL", "1.1" ); + poDS->SetMetadataItem( "AZIMUTH_LOOKS", "1.0" ); + } + else { + poDS->SetMetadataItem( "PRODUCT_LEVEL", "1.5" ); + /* extract equivalent number of looks */ + VSIFSeekL( fp, LEADER_FILE_DESCRIPTOR_LENGTH + + EFFECTIVE_LOOKS_AZIMUTH_OFFSET, SEEK_SET ); + char pszENL[17]; + double dfENL; + READ_CHAR_FLOAT(dfENL, 16, fp); + sprintf( pszENL, "%-16.1f", dfENL ); + poDS->SetMetadataItem( "AZIMUTH_LOOKS", pszENL ); + + /* extract pixel spacings */ + VSIFSeekL( fp, LEADER_FILE_DESCRIPTOR_LENGTH + + DATA_SET_SUMMARY_LENGTH + PIXEL_SPACING_OFFSET, SEEK_SET ); + double dfPixelSpacing; + double dfLineSpacing; + char pszPixelSpacing[33]; + char pszLineSpacing[33]; + READ_CHAR_FLOAT(dfPixelSpacing, 16, fp); + READ_CHAR_FLOAT(dfLineSpacing, 16, fp); + sprintf( pszPixelSpacing, "%-32.1f",dfPixelSpacing ); + sprintf( pszLineSpacing, "%-32.1f", dfLineSpacing ); + poDS->SetMetadataItem( "PIXEL_SPACING", pszPixelSpacing ); + poDS->SetMetadataItem( "LINE_SPACING", pszPixelSpacing ); + + /* Alphanumeric projection name */ + VSIFSeekL( fp, LEADER_FILE_DESCRIPTOR_LENGTH + + DATA_SET_SUMMARY_LENGTH + ALPHANUMERIC_PROJECTION_NAME_OFFSET, + SEEK_SET ); + char pszProjName[33]; + READ_STRING(pszProjName, 32, fp); + poDS->SetMetadataItem( "PROJECTION_NAME", pszProjName ); + + /* Extract corner GCPs */ + poDS->nGCPCount = 4; + poDS->pasGCPList = (GDAL_GCP *)CPLCalloc( sizeof(GDAL_GCP), + poDS->nGCPCount ); + GDALInitGCPs( poDS->nGCPCount, poDS->pasGCPList ); + + /* setup the GCPs */ + int i; + for (i = 0; i < poDS->nGCPCount; i++) { + char pszID[2]; + sprintf( pszID, "%d", i + 1); + CPLFree(poDS->pasGCPList[i].pszId); + poDS->pasGCPList[i].pszId = CPLStrdup( pszID ); + poDS->pasGCPList[i].dfGCPZ = 0.0; + } + + double dfTemp = 0.0; + /* seek to start of GCPs */ + VSIFSeekL( fp, LEADER_FILE_DESCRIPTOR_LENGTH + + DATA_SET_SUMMARY_LENGTH + TOP_LEFT_LAT_OFFSET, SEEK_SET ); + + /* top-left GCP */ + READ_CHAR_FLOAT(dfTemp, 16, fp); + poDS->pasGCPList[0].dfGCPY = dfTemp; + READ_CHAR_FLOAT(dfTemp, 16, fp); + poDS->pasGCPList[0].dfGCPX = dfTemp; + poDS->pasGCPList[0].dfGCPLine = 0.5; + poDS->pasGCPList[0].dfGCPPixel = 0.5; + + /* top right GCP */ + READ_CHAR_FLOAT(dfTemp, 16, fp); + poDS->pasGCPList[1].dfGCPY = dfTemp; + READ_CHAR_FLOAT(dfTemp, 16, fp); + poDS->pasGCPList[1].dfGCPX = dfTemp; + poDS->pasGCPList[1].dfGCPLine = 0.5; + poDS->pasGCPList[1].dfGCPPixel = poDS->nRasterYSize - 0.5; + + /* bottom right GCP */ + READ_CHAR_FLOAT(dfTemp, 16, fp); + poDS->pasGCPList[2].dfGCPY = dfTemp; + READ_CHAR_FLOAT(dfTemp, 16, fp); + poDS->pasGCPList[2].dfGCPX = dfTemp; + poDS->pasGCPList[2].dfGCPLine = poDS->nRasterYSize - 0.5; + poDS->pasGCPList[2].dfGCPPixel = poDS->nRasterYSize - 0.5; + + /* bottom left GCP */ + READ_CHAR_FLOAT(dfTemp, 16, fp); + poDS->pasGCPList[3].dfGCPY = dfTemp; + READ_CHAR_FLOAT(dfTemp, 16, fp); + poDS->pasGCPList[3].dfGCPX = dfTemp; + poDS->pasGCPList[3].dfGCPLine = poDS->nRasterYSize - 0.5; + poDS->pasGCPList[3].dfGCPPixel = 0.5; + } + + /* some generic metadata items */ + poDS->SetMetadataItem( "SENSOR_BAND", "L" ); /* PALSAR is L-band */ + poDS->SetMetadataItem( "RANGE_LOOKS", "1.0" ); + + /* Check if this is a PolSAR dataset */ + if ( poDS->GetRasterCount() == 4 ) { + /* PALSAR data is only available from JAXA in Scattering Matrix form */ + poDS->SetMetadataItem( "MATRIX_REPRESENTATION", "SCATTERING" ); + } + +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int PALSARJaxaDataset::Identify( GDALOpenInfo *poOpenInfo ) { + if ( poOpenInfo->nHeaderBytes < 360 ) + return 0; + + /* First, check that this is a PALSAR image indeed */ + if ( !EQUALN((char *)(poOpenInfo->pabyHeader + 60),"AL", 2) + || !EQUALN(CPLGetBasename((char *)(poOpenInfo->pszFilename)) + 4, + "ALPSR", 5) ) + { + return 0; + } + + VSILFILE *fpL = VSIFOpenL( poOpenInfo->pszFilename, "r" ); + if( fpL == NULL ) + return FALSE; + + /* Check that this is a volume directory file */ + int nRecordSeq = 0; + int nRecordSubtype = 0; + int nRecordType = 0; + int nSecondSubtype = 0; + int nThirdSubtype = 0; + int nLengthRecord = 0; + + VSIFSeekL(fpL, 0, SEEK_SET); + + READ_WORD(fpL, nRecordSeq); + READ_BYTE(fpL, nRecordSubtype); + READ_BYTE(fpL, nRecordType); + READ_BYTE(fpL, nSecondSubtype); + READ_BYTE(fpL, nThirdSubtype); + READ_WORD(fpL, nLengthRecord); + + VSIFCloseL( fpL ); + + /* Check that we have the right record */ + if ( nRecordSeq == 1 && nRecordSubtype == 192 && nRecordType == 192 && + nSecondSubtype == 18 && nThirdSubtype == 18 && nLengthRecord == 360 ) + { + return 1; + } + + return 0; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ +GDALDataset *PALSARJaxaDataset::Open( GDALOpenInfo * poOpenInfo ) { + /* Check that this actually is a JAXA PALSAR product */ + if ( !PALSARJaxaDataset::Identify(poOpenInfo) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The JAXAPALSAR driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + PALSARJaxaDataset *poDS = new PALSARJaxaDataset(); + + /* Get the suffix of the filename, we'll need this */ + char *pszSuffix = VSIStrdup( (char *) + (CPLGetFilename( poOpenInfo->pszFilename ) + 3) ); + + /* Try to read each of the polarizations */ + char *pszImgFile = (char *)VSIMalloc( + strlen( CPLGetDirname( poOpenInfo->pszFilename ) ) + + strlen( pszSuffix ) + 8 ); + + int nBandNum = 1; + + /* HH */ + VSILFILE *fpHH; + sprintf( pszImgFile, "%s%sIMG-HH%s", + CPLGetDirname(poOpenInfo->pszFilename), SEP_STRING, pszSuffix ); + fpHH = VSIFOpenL( pszImgFile, "rb" ); + if (fpHH != NULL) { + poDS->SetBand( nBandNum, new PALSARJaxaRasterBand( poDS, 0, fpHH ) ); + nBandNum++; + } + + /* HV */ + VSILFILE *fpHV; + sprintf( pszImgFile, "%s%sIMG-HV%s", + CPLGetDirname(poOpenInfo->pszFilename), SEP_STRING, pszSuffix ); + fpHV = VSIFOpenL( pszImgFile, "rb" ); + if (fpHV != NULL) { + poDS->SetBand( nBandNum, new PALSARJaxaRasterBand( poDS, 1, fpHV ) ); + nBandNum++; + } + + /* VH */ + VSILFILE *fpVH; + sprintf( pszImgFile, "%s%sIMG-VH%s", + CPLGetDirname(poOpenInfo->pszFilename), SEP_STRING, pszSuffix ); + fpVH = VSIFOpenL( pszImgFile, "rb" ); + if (fpVH != NULL) { + poDS->SetBand( nBandNum, new PALSARJaxaRasterBand( poDS, 2, fpVH ) ); + nBandNum++; + } + + /* VV */ + VSILFILE *fpVV; + sprintf( pszImgFile, "%s%sIMG-VV%s", + CPLGetDirname(poOpenInfo->pszFilename), SEP_STRING, pszSuffix ); + fpVV = VSIFOpenL( pszImgFile, "rb" ); + if (fpVV != NULL) { + poDS->SetBand( nBandNum, new PALSARJaxaRasterBand( poDS, 3, fpVV ) ); + nBandNum++; + } + + VSIFree( pszImgFile ); + + /* did we get at least one band? */ + if (fpVV == NULL && fpVH == NULL && fpHV == NULL && fpHH == NULL) { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find any image data. Aborting opening as PALSAR image."); + delete poDS; + VSIFree( pszSuffix ); + return NULL; + } + + /* Level 1.0 products are not supported */ + if (poDS->nFileType == level_10) { + CPLError( CE_Failure, CPLE_AppDefined, + "ALOS PALSAR Level 1.0 products are not supported. Aborting opening as PALSAR image."); + delete poDS; + VSIFree( pszSuffix ); + return NULL; + } + + /* read metadata from Leader file. */ + char *pszLeaderFilename = (char *)VSIMalloc( + strlen( CPLGetDirname( poOpenInfo->pszFilename ) ) + + strlen(pszSuffix) + 5 ); + sprintf( pszLeaderFilename, "%s%sLED%s", + CPLGetDirname( poOpenInfo->pszFilename ) , SEP_STRING, pszSuffix ); + + VSILFILE *fpLeader = VSIFOpenL( pszLeaderFilename, "rb" ); + /* check if the leader is actually present */ + if (fpLeader != NULL) { + ReadMetadata(poDS, fpLeader); + VSIFCloseL(fpLeader); + } + + VSIFree(pszLeaderFilename); + + VSIFree( pszSuffix ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_PALSARJaxa() */ +/************************************************************************/ + +void GDALRegister_PALSARJaxa() { + GDALDriver *poDriver; + + if( GDALGetDriverByName( "JAXAPALSAR" ) == NULL ) { + poDriver = new GDALDriver(); + poDriver->SetDescription( "JAXAPALSAR" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "JAXA PALSAR Product Reader (Level 1.1/1.5)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_palsar.html" ); + poDriver->pfnOpen = PALSARJaxaDataset::Open; + poDriver->pfnIdentify = PALSARJaxaDataset::Identify; + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/jaxapalsar/makefile.vc b/bazaar/plugin/gdal/frmts/jaxapalsar/makefile.vc new file mode 100644 index 000000000..7965457e6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jaxapalsar/makefile.vc @@ -0,0 +1,12 @@ +OBJ = jaxapalsardataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/jdem/GNUmakefile b/bazaar/plugin/gdal/frmts/jdem/GNUmakefile new file mode 100644 index 000000000..8a7c5e211 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jdem/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = jdemdataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/jdem/jdemdataset.cpp b/bazaar/plugin/gdal/frmts/jdem/jdemdataset.cpp new file mode 100644 index 000000000..5bd7a923c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jdem/jdemdataset.cpp @@ -0,0 +1,405 @@ +/****************************************************************************** + * $Id: jdemdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: JDEM Reader + * Purpose: All code for Japanese DEM Reader + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" + +CPL_CVSID("$Id: jdemdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +CPL_C_START +void GDALRegister_JDEM(void); +CPL_C_END + +/************************************************************************/ +/* JDEMGetField() */ +/************************************************************************/ + +static int JDEMGetField( char *pszField, int nWidth ) + +{ + char szWork[32]; + + CPLAssert( nWidth < (int) sizeof(szWork) ); + + strncpy( szWork, pszField, nWidth ); + szWork[nWidth] = '\0'; + + return atoi(szWork); +} + +/************************************************************************/ +/* JDEMGetAngle() */ +/************************************************************************/ + +static double JDEMGetAngle( char *pszField ) + +{ + int nAngle = JDEMGetField( pszField, 7 ); + int nDegree, nMin, nSec; + + // Note, this isn't very general purpose, but it would appear + // from the field widths that angles are never negative. Nice + // to be a country in the "first quadrant". + + nDegree = nAngle / 10000; + nMin = (nAngle / 100) % 100; + nSec = nAngle % 100; + + return nDegree + nMin / 60.0 + nSec / 3600.0; +} + +/************************************************************************/ +/* ==================================================================== */ +/* JDEMDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class JDEMRasterBand; + +class JDEMDataset : public GDALPamDataset +{ + friend class JDEMRasterBand; + + VSILFILE *fp; + GByte abyHeader[1012]; + + public: + JDEMDataset(); + ~JDEMDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JDEMRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class JDEMRasterBand : public GDALPamRasterBand +{ + friend class JDEMDataset; + + int nRecordSize; + char* pszRecord; + int bBufferAllocFailed; + + public: + + JDEMRasterBand( JDEMDataset *, int ); + ~JDEMRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + + +/************************************************************************/ +/* JDEMRasterBand() */ +/************************************************************************/ + +JDEMRasterBand::JDEMRasterBand( JDEMDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Float32; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + /* Cannot overflow as nBlockXSize <= 999 */ + nRecordSize = nBlockXSize*5 + 9 + 2; + pszRecord = NULL; + bBufferAllocFailed = FALSE; +} + +/************************************************************************/ +/* ~JDEMRasterBand() */ +/************************************************************************/ + +JDEMRasterBand::~JDEMRasterBand() +{ + VSIFree(pszRecord); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr JDEMRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) + +{ + JDEMDataset *poGDS = (JDEMDataset *) poDS; + int i; + + if (pszRecord == NULL) + { + if (bBufferAllocFailed) + return CE_Failure; + + pszRecord = (char *) VSIMalloc(nRecordSize); + if (pszRecord == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate scanline buffer"); + bBufferAllocFailed = TRUE; + return CE_Failure; + } + } + + VSIFSeekL( poGDS->fp, 1011 + nRecordSize*nBlockYOff, SEEK_SET ); + + VSIFReadL( pszRecord, 1, nRecordSize, poGDS->fp ); + + if( !EQUALN((char *) poGDS->abyHeader,pszRecord,6) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JDEM Scanline corrupt. Perhaps file was not transferred\n" + "in binary mode?" ); + return CE_Failure; + } + + if( JDEMGetField( pszRecord + 6, 3 ) != nBlockYOff + 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JDEM scanline out of order, JDEM driver does not\n" + "currently support partial datasets." ); + return CE_Failure; + } + + for( i = 0; i < nBlockXSize; i++ ) + ((float *) pImage)[i] = (float) + (JDEMGetField( pszRecord + 9 + 5 * i, 5) * 0.1); + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* JDEMDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* JDEMDataset() */ +/************************************************************************/ + +JDEMDataset::JDEMDataset() : fp(NULL) + +{ + fp = NULL; +} + +/************************************************************************/ +/* ~JDEMDataset() */ +/************************************************************************/ + +JDEMDataset::~JDEMDataset() + +{ + FlushCache(); + if( fp != NULL ) + VSIFCloseL( fp ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr JDEMDataset::GetGeoTransform( double * padfTransform ) + +{ + double dfLLLat, dfLLLong, dfURLat, dfURLong; + + dfLLLat = JDEMGetAngle( (char *) abyHeader + 29 ); + dfLLLong = JDEMGetAngle( (char *) abyHeader + 36 ); + dfURLat = JDEMGetAngle( (char *) abyHeader + 43 ); + dfURLong = JDEMGetAngle( (char *) abyHeader + 50 ); + + padfTransform[0] = dfLLLong; + padfTransform[3] = dfURLat; + padfTransform[1] = (dfURLong - dfLLLong) / GetRasterXSize(); + padfTransform[2] = 0.0; + + padfTransform[4] = 0.0; + padfTransform[5] = -1 * (dfURLat - dfLLLat) / GetRasterYSize(); + + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *JDEMDataset::GetProjectionRef() + +{ + return( "GEOGCS[\"Tokyo\",DATUM[\"Tokyo\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",7004]],TOWGS84[-148,507,685,0,0,0,0],AUTHORITY[\"EPSG\",6301]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"DMSH\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AUTHORITY[\"EPSG\",4301]]" ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int JDEMDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Confirm that the header has what appears to be dates in the */ +/* expected locations. Sadly this is a relatively weak test. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 50 ) + return FALSE; + + /* check if century values seem reasonable */ + if( (!EQUALN((char *)poOpenInfo->pabyHeader+11,"19",2) + && !EQUALN((char *)poOpenInfo->pabyHeader+11,"20",2)) + || (!EQUALN((char *)poOpenInfo->pabyHeader+15,"19",2) + && !EQUALN((char *)poOpenInfo->pabyHeader+15,"20",2)) + || (!EQUALN((char *)poOpenInfo->pabyHeader+19,"19",2) + && !EQUALN((char *)poOpenInfo->pabyHeader+19,"20",2)) ) + { + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *JDEMDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Confirm that the header is compatible with a JDEM dataset. */ +/* -------------------------------------------------------------------- */ + if (!Identify(poOpenInfo)) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The JDEM driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + /* Check that the file pointer from GDALOpenInfo* is available */ + if( poOpenInfo->fpL == NULL ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + JDEMDataset *poDS; + + poDS = new JDEMDataset(); + + /* Borrow the file pointer from GDALOpenInfo* */ + poDS->fp = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + VSIFReadL( poDS->abyHeader, 1, 1012, poDS->fp ); + + poDS->nRasterXSize = JDEMGetField( (char *) poDS->abyHeader + 23, 3 ); + poDS->nRasterYSize = JDEMGetField( (char *) poDS->abyHeader + 26, 3 ); + if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid dimensions : %d x %d", + poDS->nRasterXSize, poDS->nRasterYSize); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->SetBand( 1, new JDEMRasterBand( poDS, 1 )); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_JDEM() */ +/************************************************************************/ + +void GDALRegister_JDEM() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "JDEM" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "JDEM" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Japanese DEM (.mem)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#JDEM" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "mem" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = JDEMDataset::Open; + poDriver->pfnIdentify = JDEMDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/jdem/makefile.vc b/bazaar/plugin/gdal/frmts/jdem/makefile.vc new file mode 100644 index 000000000..d4a2635cf --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jdem/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = jdemdataset.obj + +EXTRAFLAGS = -I..\iso8211 + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/jp2kak/GNUmakefile b/bazaar/plugin/gdal/frmts/jp2kak/GNUmakefile new file mode 100644 index 000000000..5a3e7c039 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jp2kak/GNUmakefile @@ -0,0 +1,33 @@ + +include ../../GDALmake.opt + +ifneq (($KAKDIR),) +ifneq ($(HAVE_LIBTOOL),yes) +include $(GDAL_ROOT)/frmts/jp2kak/jp2kak.lst +endif +endif + +OBJ = jp2kakdataset.o + +KAKINC = -I$(KAKDIR)/coresys/common -I$(KAKDIR)/apps/compressed_io \ + -I$(KAKDIR)/apps/jp2 -I$(KAKDIR)/apps/image -I$(KAKDIR)/apps/args \ + -I$(KAKDIR)/apps/support -I$(KAKDIR)/apps/kdu_compress + +APPOBJ = $(KAK_OBJ) + +INSTOBJ = $(foreach d,$(APPOBJ),../o/$(notdir $(d))) + +#CXXFLAGS := $(CXXFLAGS) -DFILEIO_DEBUG + +CPPFLAGS := $(KAKINC) -I. $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + rm -f $(INSTOBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + cp $(APPOBJ) ../o + +$(OBJ) $(O_OBJ): subfile_source.h diff --git a/bazaar/plugin/gdal/frmts/jp2kak/frmt_jp2kak.html b/bazaar/plugin/gdal/frmts/jp2kak/frmt_jp2kak.html new file mode 100644 index 000000000..5c09aa35f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jp2kak/frmt_jp2kak.html @@ -0,0 +1,168 @@ + + +JP2KAK -- JPEG-2000 (based on Kakadu) + + + + +

    JP2KAK -- JPEG-2000 (based on Kakadu)

    + +Most forms of JPEG2000 JP2 and JPC compressed images (ISO/IEC 15444-1) can be +read with GDAL using a driver based on the Kakadu library. As well, new images +can be written. Existing images cannot be updated in place.

    + +The JPEG2000 file format supports lossy and lossless compression of +8bit and 16bit images with 1 or more bands (components). Via the +GeoJP2 (tm) mechanism, +GeoTIFF style coordinate system and georeferencing information can be embedded +within the JP2 file. JPEG2000 files use a substantially different format and +compression mechanism than the traditional JPEG compression and JPEG JFIF +format. They are distinct compression mechanisms produced by the same +group. JPEG2000 is based on wavelet compression.

    + +The JPEG2000 driver documented on this page (the JP2KAK driver) is implemented +on top of the proprietary Kakadu +library. This is a high quality and high performance JPEG2000 library in wide +used in the geospatial and general imaging community. However, it is not free, +and so normally builds of GDAL from source will not include support for this +driver unless the builder purchases a license for the library and configures +accordingly. GDAL includes another JPEG2000 driver +based on the free JasPer library.

    + +When reading images this driver will represent the bands as being +Byte (8bit unsigned), 16 bit signed or 16 bit unsigned. Georeferencing +and coordinate system information will be available if the file is a +GeoJP2 (tm) file. Files color encoded in YCbCr color space will be +automatically translated to RGB. Paletted images are also supported.

    + +Starting with GDAL 1.9.0, XMP metadata can be extracted from JPEG2000 files, and will be +stored as XML raw content in the xml:XMP metadata domain.

    + +

    Configuration Options

    + +The JP2KAK driver supports the following +Config Options. +These runtime options can be used to alter the behavior of the driver. + +
      +
    • JP2KAK_THREADS=n: By default an effort is made to take advantage of +multi-threading on multi-core computers using default rules from the Kakadu +library. This option may be set to a value of zero to avoid using additional +threads or to a specific count to create the requested number of worker threads.

      + +

    • JP2KAK_FUSSY=YES/NO: This can be set to YES to turn on fussy reporting +of problems with the JPEG2000 data stream. Defaults to NO.

      + +

    • JP2KAK_RESILIENT=YES/NO: This can be set to YES to force Kakadu to +maximimize resilience with incorrectly created JPEG2000 data files, likely at +some cost in performance. This is likely to be necessary if, amoung other reasons, +you get an error message about "Expected to find EPH marker following packet header" +or error reports indicating the need to run with the resilient and sequential flags +on. Defaults to NO.

      + +

    + +

    Option Options

    + +(GDAL >= 2.0 ) + +The following open option is available: +
      +
    • 1BIT_ALPHA_PROMOTION=YES/NO: Whether a 1-bit alpha channel should be promoted to 8-bit. +Defaults to YES.

    • +
    + +

    Creation Issues

    + +JPEG2000 files can only be created using the CreateCopy mechanism to +copy from an existing dataset.

    + +JPEG2000 overviews are maintained as part of the mathematical description of +the image. Overviews cannot be built as a separate process, but on read the +image will generally be represented as having overview levels at various +power of two factors.

    + +Creation Options:

    + +

      + +
    • QUALITY=n: Set the compressed size ratio as a percentage +of the size of the uncompressed image. The default is 20 indicating that +the resulting image should be 20% of the size of the uncompressed image. +Actual final image size may not exactly match that requested depending on +various factors. A value of 100 will result in use of the lossless compression +algorithm . On typical image data, if you specify a value greater than 65, it +might be worth trying with QUALITY=100 instead as lossless compression might +produce better compression than lossy compression.

      + +

    • BLOCKXSIZE=n: Set the tile width to use. Defaults to 20000. +

      + +

    • BLOCKYSIZE=n: Set the tile height to use. Defaults to image height. +

      + +

    • FLUSH=TRUE/FALSE: Enable/Disable incremental flushing when writing files. Required to be FALSE for RLPC and LRPC Corder. May use a lot of memory when FALSE while writing large images. Defaults to TRUE. +

      + +

    • GMLJP2=YES/NO: Indicates whether a GML box +conforming to the OGC GML in JPEG2000 specification should be included in the +file. Unless GMLJP2V2_DEF is used, the version of the GMLJP2 box will be +version 1. Defaults to YES.

      + +

    • GMLJP2V2_DEF=filename: (Starting with GDAL 2.0) Indicates whether a GML box +conforming to the +OGC GML in JPEG2000, version 2 specification should be included in the +file. filename must point to a file with a JSon content that defines how +the GMLJP2 v2 box should be built. See +GMLJP2v2 definition file section in documentation of the JP2OpenJPEG driver +for the syntax of the JSon configuration file. +It is also possible to directly pass the JSon content inlined as a string. +If filename is just set to YES, a minimal instance will be built.

      + +

    • GeoJP2=YES/NO: Indicates whether a UUID/GeoTIFF box conforming to the GeoJP2 (GeoTIFF in JPEG2000) specification should be included in the file. Defaults to YES.

      + +

    • LAYERS=n: Control the number of layers produced. These are +sort of like resolution layers, but not exactly. The default value is +12 and this works well in most situations.

      + +

    • ROI=xoff,yoff,xsize,ysize: Selects a region to be a region of +interest to process with higher data quality. The various "R" flags below may +be used to control the amount better. For example the settings +"ROI=0,0,100,100", "Rweight=7" would encode the top left 100x100 area of +the image with considerable higher quality compared to the rest of the image. +

      + +

    + +The following creation options are tightly tied to the Kakadu library, and +are considered to be for advanced use only. Consult Kakadu documentation +to better understand their meaning.

    + +

      +
    • Corder: Defaults to "PRCL". +
    • Cprecincts: Defaults to "{512,512},{256,512},{128,512},{64,512},{32,512},{16,512},{8,512},{4,512},{2,512}". +
    • ORGgen_plt: Defaults to "yes". +
    • Cmodes: Kakadu library default used. +
    • Clevels: Kakadu library default used. +
    • Rshift: Kakadu library default used. +
    • Rlevels: Kakadu library default used. +
    • Rweight: Kakadu library default used. +
    + +See Also:

    + +

      + +
    • Implemented as gdal/frmts/jp2kak/jp2kakdataset.cpp.
    • + +
    • If you're using a Kakadu release before v7.5, configure & compile GDAL with eg. CXXFLAGS="-DKDU_MAJOR_VERSION=7 -DKDU_MINOR_VERSION=3 -DKDU_PATCH_VERSION=2" for Kakadu version 7.3.2.
    • + +
    • JPEG2000 for Geospatial +Applications page, includes GeoJP2(tm) discussion.
    • + +
    • Alternate JPEG2000 driver.
    • + +
    + + + diff --git a/bazaar/plugin/gdal/frmts/jp2kak/jp2kak.lst b/bazaar/plugin/gdal/frmts/jp2kak/jp2kak.lst new file mode 100644 index 000000000..005b50951 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jp2kak/jp2kak.lst @@ -0,0 +1,27 @@ +KAK_OBJ = \ + $(KAKDIR)/apps/make/args.o \ + $(KAKDIR)/apps/make/image_in.o \ + $(KAKDIR)/apps/make/image_out.o \ + $(KAKDIR)/apps/make/jp2.o \ + $(KAKDIR)/apps/make/mj2.o \ + $(KAKDIR)/apps/make/palette.o \ + $(KAKDIR)/apps/make/roi_sources.o \ + $(KAKDIR)/apps/make/kdu_region_decompressor.o \ + $(KAKDIR)/apps/make/kdu_stripe_decompressor.o + +# The following are just for Kakadu 5.1 or later, comment out if older. +KAK_OBJ += \ + $(KAKDIR)/apps/make/kdu_tiff.o \ + $(KAKDIR)/apps/make/jpx.o + +# The following are just for Kakadu 7.0 later. +ifneq ($(wildcard $(KAKDIR)/apps/make/ssse3_stripe_transfe?.o),) +KAK_OBJ += \ + $(KAKDIR)/apps/make/ssse3_stripe_transfer.o +endif + +# The following are for Kakadu 7.5 and later. +ifneq ($(wildcard $(KAKDIR)/apps/make/ssse3_region_decompressor.o),) +KAK_OBJ += \ + $(KAKDIR)/apps/make/ssse3_region_decompressor.o +endif diff --git a/bazaar/plugin/gdal/frmts/jp2kak/jp2kakdataset.cpp b/bazaar/plugin/gdal/frmts/jp2kak/jp2kakdataset.cpp new file mode 100644 index 000000000..74ddb0d13 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jp2kak/jp2kakdataset.cpp @@ -0,0 +1,2875 @@ +/****************************************************************************** + * $Id: jp2kakdataset.cpp 29075 2015-04-30 12:51:21Z rouault $ + * + * Project: JPEG-2000 + * Purpose: Implementation of the ISO/IEC 15444-1 standard based on Kakadu. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdaljp2abstractdataset.h" +#include "gdaljp2metadata.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include "jp2_local.h" + +// Kakadu core includes +#include "kdu_elementary.h" +#include "kdu_messaging.h" +#include "kdu_params.h" +#include "kdu_compressed.h" +#include "kdu_sample_processing.h" +#include "kdu_stripe_decompressor.h" +#include "kdu_arch.h" + +#include "subfile_source.h" +#include "vsil_target.h" + +// Application level includes +#include "kdu_file_io.h" +#include "jp2.h" + +// ROI related. +#include "kdu_roi_processing.h" +#include "kdu_image.h" +#include "roi_sources.h" + +// I don't think JPIP support currently works due to changes in +// classes like kdu_window ... some fixing required if someone wants it. +// #define USE_JPIP + +#ifdef USE_JPIP +# include "kdu_client.h" +#else +# define kdu_client void +#endif + +CPL_CVSID("$Id: jp2kakdataset.cpp 29075 2015-04-30 12:51:21Z rouault $"); + +// Before v7.5 Kakadu does not advertise its version well +// After v7.5 Kakadu has KDU_{MAJOR,MINOR,PATCH}_VERSION defines so it's easier +// For older releases compile with them manually specified +#ifndef KDU_MAJOR_VERSION +# error Compile with eg. -DKDU_MAJOR_VERSION=7 -DKDU_MINOR_VERSION=3 -DKDU_PATCH_VERSION=2 to specify Kakadu library version +#endif + +#if KDU_MAJOR_VERSION > 7 || (KDU_MAJOR_VERSION == 7 && KDU_MINOR_VERSION >= 5) + using namespace kdu_core; + using namespace kdu_supp; +#endif + +// #define KAKADU_JPX 1 + +static int kakadu_initialized = FALSE; + +static unsigned char jp2_header[] = +{0x00,0x00,0x00,0x0c,0x6a,0x50,0x20,0x20,0x0d,0x0a,0x87,0x0a}; + +static unsigned char jpc_header[] = +{0xff,0x4f}; + +/* -------------------------------------------------------------------- */ +/* The number of tiles at a time we will push through the */ +/* encoder per flush when writing jpeg2000 streams. */ +/* -------------------------------------------------------------------- */ +#define TILE_CHUNK_SIZE 1024 + +/************************************************************************/ +/* ==================================================================== */ +/* JP2KAKDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class JP2KAKDataset : public GDALJP2AbstractDataset +{ + friend class JP2KAKRasterBand; + + kdu_codestream oCodeStream; + kdu_compressed_source *poInput; + kdu_compressed_source *poRawInput; + jp2_family_src *family; + kdu_client *jpip_client; + kdu_dims dims; + int nResCount; + bool bPreferNPReads; + kdu_thread_env *poThreadEnv; + + int bCached; + int bResilient; + int bFussy; + bool bUseYCC; + + bool bPromoteTo8Bit; + + int TestUseBlockIO( int, int, int, int, int, int, + GDALDataType, int, int * ); + CPLErr DirectRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + + public: + JP2KAKDataset(); + ~JP2KAKDataset(); + + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + + static void KakaduInitialize(); + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JP2KAKRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class JP2KAKRasterBand : public GDALPamRasterBand +{ + friend class JP2KAKDataset; + + JP2KAKDataset *poBaseDS; + + int nDiscardLevels; + + kdu_dims band_dims; + + int nOverviewCount; + JP2KAKRasterBand **papoOverviewBand; + + kdu_client *jpip_client; + + kdu_codestream oCodeStream; + + GDALColorTable oCT; + + int bYCbCrReported; + + GDALColorInterp eInterp; + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); + + int HasExternalOverviews() + { return GDALPamRasterBand::GetOverviewCount() != 0; } + + public: + + JP2KAKRasterBand( int, int, kdu_codestream, int, kdu_client *, + jp2_channels, JP2KAKDataset * ); + ~JP2KAKRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview( int ); + + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + + // internal + + void ApplyPalette( jp2_palette oJP2Palette ); + void ProcessYCbCrTile(kdu_tile tile, GByte *pabyBuffer, + int nBlockXOff, int nBlockYOff, + int nTileOffsetX, int nTileOffsetY ); + void ProcessTile(kdu_tile tile, GByte *pabyBuffer ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* Set up messaging services */ +/* ==================================================================== */ +/************************************************************************/ + +class kdu_cpl_error_message : public kdu_thread_safe_message +{ +public: // Member classes + kdu_cpl_error_message( CPLErr eErrClass ) + { + m_eErrClass = eErrClass; + m_pszError = NULL; + } + + void put_text(const char *string) + { + if( m_pszError == NULL ) + m_pszError = CPLStrdup( string ); + else + { + m_pszError = (char *) + CPLRealloc(m_pszError, strlen(m_pszError) + strlen(string)+1 ); + strcat( m_pszError, string ); + } + } + + class JP2KAKException + { + }; + + void flush(bool end_of_message=false) + { + kdu_thread_safe_message::flush(end_of_message); + + if( m_pszError == NULL ) + return; + if( m_pszError[strlen(m_pszError)-1] == '\n' ) + m_pszError[strlen(m_pszError)-1] = '\0'; + + CPLError( m_eErrClass, CPLE_AppDefined, "%s", m_pszError ); + CPLFree( m_pszError ); + m_pszError = NULL; + + if( end_of_message && m_eErrClass == CE_Failure ) + { + throw JP2KAKException(); + } + } + +private: + CPLErr m_eErrClass; + char *m_pszError; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JP2KAKRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* JP2KAKRasterBand() */ +/************************************************************************/ + +JP2KAKRasterBand::JP2KAKRasterBand( int nBand, int nDiscardLevels, + kdu_codestream oCodeStream, + int nResCount, kdu_client *jpip_client, + jp2_channels oJP2Channels, + JP2KAKDataset *poBaseDSIn ) + +{ + this->nBand = nBand; + this->poBaseDS = poBaseDSIn; + + bYCbCrReported = FALSE; + + if( oCodeStream.get_bit_depth(nBand-1) > 8 + && oCodeStream.get_bit_depth(nBand-1) <= 16 + && oCodeStream.get_signed(nBand-1) ) + this->eDataType = GDT_Int16; + else if( oCodeStream.get_bit_depth(nBand-1) > 8 + && oCodeStream.get_bit_depth(nBand-1) <= 16 + && !oCodeStream.get_signed(nBand-1) ) + this->eDataType = GDT_UInt16; + else + this->eDataType = GDT_Byte; + + this->nDiscardLevels = nDiscardLevels; + this->oCodeStream = oCodeStream; + + this->jpip_client = jpip_client; + + oCodeStream.apply_input_restrictions( 0, 0, nDiscardLevels, 0, NULL ); + oCodeStream.get_dims( 0, band_dims ); + + this->nRasterXSize = band_dims.size.x; + this->nRasterYSize = band_dims.size.y; + +/* -------------------------------------------------------------------- */ +/* Capture some useful metadata. */ +/* -------------------------------------------------------------------- */ + if( oCodeStream.get_bit_depth(nBand-1) % 8 != 0 && !poBaseDSIn->bPromoteTo8Bit ) + { + SetMetadataItem( "NBITS", + CPLString().Printf("%d",oCodeStream.get_bit_depth(nBand-1)), + "IMAGE_STRUCTURE" ); + } + SetMetadataItem( "COMPRESSION", "JP2000", "IMAGE_STRUCTURE" ); + +/* -------------------------------------------------------------------- */ +/* Use a 2048x128 "virtual" block size unless the file is small. */ +/* -------------------------------------------------------------------- */ + if( nRasterXSize >= 2048 ) + nBlockXSize = 2048; + else + nBlockXSize = nRasterXSize; + + if( nRasterYSize >= 256 ) + nBlockYSize = 128; + else + nBlockYSize = nRasterYSize; + +/* -------------------------------------------------------------------- */ +/* Figure out the color interpretation for this band. */ +/* -------------------------------------------------------------------- */ + + eInterp = GCI_Undefined; + + if( oJP2Channels.exists() ) + { + int nRedIndex=-1, nGreenIndex=-1, nBlueIndex=-1, nLutIndex; + int nCSI; + + if( oJP2Channels.get_num_colours() == 3 ) + { + oJP2Channels.get_colour_mapping( 0, nRedIndex, nLutIndex, nCSI ); + oJP2Channels.get_colour_mapping( 1, nGreenIndex, nLutIndex, nCSI ); + oJP2Channels.get_colour_mapping( 2, nBlueIndex, nLutIndex, nCSI ); + } + else + { + oJP2Channels.get_colour_mapping( 0, nRedIndex, nLutIndex, nCSI ); + if( nBand == 1 ) + eInterp = GCI_GrayIndex; + } + + if( eInterp != GCI_Undefined ) + /* nothing to do */; + + // If we have LUT info, it is a palette image. + else if( nLutIndex != -1 ) + eInterp = GCI_PaletteIndex; + + // Establish color band this is. + else if( nRedIndex == nBand-1 ) + eInterp = GCI_RedBand; + else if( nGreenIndex == nBand-1 ) + eInterp = GCI_GreenBand; + else if( nBlueIndex == nBand-1 ) + eInterp = GCI_BlueBand; + else + eInterp = GCI_Undefined; + + // Could this band be an alpha band? + if( eInterp == GCI_Undefined ) + { + int color_idx, opacity_idx, lut_idx; + + for( color_idx = 0; + color_idx < oJP2Channels.get_num_colours(); color_idx++ ) + { + if( oJP2Channels.get_opacity_mapping( color_idx, opacity_idx, + lut_idx, nCSI ) ) + { + if( opacity_idx == nBand - 1 ) + eInterp = GCI_AlphaBand; + } + if( oJP2Channels.get_premult_mapping( color_idx, opacity_idx, + lut_idx, nCSI ) ) + { + if( opacity_idx == nBand - 1 ) + eInterp = GCI_AlphaBand; + } + } + } + } + else if( nBand == 1 ) + eInterp = GCI_RedBand; + else if( nBand == 2 ) + eInterp = GCI_GreenBand; + else if( nBand == 3 ) + eInterp = GCI_BlueBand; + else + eInterp = GCI_GrayIndex; + +/* -------------------------------------------------------------------- */ +/* Do we have any overviews? Only check if we are the full res */ +/* image. */ +/* -------------------------------------------------------------------- */ + nOverviewCount = 0; + papoOverviewBand = 0; + if( nDiscardLevels == 0 && GDALPamRasterBand::GetOverviewCount() == 0 ) + { + int nXSize = nRasterXSize, nYSize = nRasterYSize; + + for( int nDiscard = 1; nDiscard < nResCount; nDiscard++ ) + { + kdu_dims dims; + + nXSize = (nXSize+1) / 2; + nYSize = (nYSize+1) / 2; + + if( (nXSize+nYSize) < 128 || nXSize < 4 || nYSize < 4 ) + continue; /* skip super reduced resolution layers */ + + oCodeStream.apply_input_restrictions( 0, 0, nDiscard, 0, NULL ); + oCodeStream.get_dims( 0, dims ); + + if( (dims.size.x == nXSize || dims.size.x == nXSize-1) + && (dims.size.y == nYSize || dims.size.y == nYSize-1) ) + { + nOverviewCount++; + papoOverviewBand = (JP2KAKRasterBand **) + CPLRealloc( papoOverviewBand, + sizeof(void*) * nOverviewCount ); + papoOverviewBand[nOverviewCount-1] = + new JP2KAKRasterBand( nBand, nDiscard, oCodeStream, 0, + jpip_client, oJP2Channels, + poBaseDS ); + } + else + { + CPLDebug( "GDAL", "Discard %dx%d JPEG2000 overview layer,\n" + "expected %dx%d.", + dims.size.x, dims.size.y, nXSize, nYSize ); + } + } + } +} + +/************************************************************************/ +/* ~JP2KAKRasterBand() */ +/************************************************************************/ + +JP2KAKRasterBand::~JP2KAKRasterBand() + +{ + for( int i = 0; i < nOverviewCount; i++ ) + delete papoOverviewBand[i]; + + CPLFree( papoOverviewBand ); +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int JP2KAKRasterBand::GetOverviewCount() + +{ + if( GDALPamRasterBand::GetOverviewCount() > 0 ) + return GDALPamRasterBand::GetOverviewCount(); + else + return nOverviewCount; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *JP2KAKRasterBand::GetOverview( int iOverviewIndex ) + +{ + if( GDALPamRasterBand::GetOverviewCount() > 0 ) + return GDALPamRasterBand::GetOverview( iOverviewIndex ); + + else if( iOverviewIndex < 0 || iOverviewIndex >= nOverviewCount ) + return NULL; + else + return papoOverviewBand[iOverviewIndex]; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr JP2KAKRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + int nWordSize = GDALGetDataTypeSize( eDataType ) / 8; + int nOvMult = 1, nLevelsLeft = nDiscardLevels; + while( nLevelsLeft-- > 0 ) + nOvMult *= 2; + + CPLDebug( "JP2KAK", "IReadBlock(%d,%d) on band %d.", + nBlockXOff, nBlockYOff, nBand ); + +/* -------------------------------------------------------------------- */ +/* Compute the normal window, and buffer size. */ +/* -------------------------------------------------------------------- */ + int nWXOff, nWYOff, nWXSize, nWYSize, nXSize, nYSize; + + nWXOff = nBlockXOff * nBlockXSize * nOvMult; + nWYOff = nBlockYOff * nBlockYSize * nOvMult; + nWXSize = nBlockXSize * nOvMult; + nWYSize = nBlockYSize * nOvMult; + + nXSize = nBlockXSize; + nYSize = nBlockYSize; + +/* -------------------------------------------------------------------- */ +/* Adjust if we have a partial block on the right or bottom of */ +/* the image. Unfortunately despite some care I can't seem to */ +/* always get partial tiles to come from the desired overview */ +/* level depending on how various things round - hopefully not */ +/* a big deal. */ +/* -------------------------------------------------------------------- */ + if( nWXOff + nWXSize > poBaseDS->GetRasterXSize() ) + { + nWXSize = poBaseDS->GetRasterXSize() - nWXOff; + nXSize = nRasterXSize - nBlockXSize * nBlockXOff; + } + + if( nWYOff + nWYSize > poBaseDS->GetRasterYSize() ) + { + nWYSize = poBaseDS->GetRasterYSize() - nWYOff; + nYSize = nRasterYSize - nBlockYSize * nBlockYOff; + } + + if( nXSize != nBlockXSize || nYSize != nBlockYSize ) + memset( pImage, 0, nBlockXSize * nBlockYSize * nWordSize ); + +/* -------------------------------------------------------------------- */ +/* By default we invoke just for the requested band, directly */ +/* into the target buffer. */ +/* -------------------------------------------------------------------- */ + GDALRasterIOExtraArg sExtraArg; + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + + if( !poBaseDS->bUseYCC ) + { + return poBaseDS->DirectRasterIO( GF_Read, + nWXOff, nWYOff, nWXSize, nWYSize, + pImage, nXSize, nYSize, + eDataType, 1, &nBand, + nWordSize, nWordSize*nBlockXSize, 0, &sExtraArg ); + } + +/* -------------------------------------------------------------------- */ +/* But for YCC or possible other effectively pixel interleaved */ +/* products, we read all bands into a single buffer, fetch out */ +/* what we want, and push the rest into the block cache. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + std::vector anBands; + int iBand; + + for( iBand = 0; iBand < poBaseDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand* poBand = poBaseDS->GetRasterBand(iBand+1); + if ( poBand->GetRasterDataType() != eDataType ) + continue; + anBands.push_back(iBand+1); + } + + GByte *pabyWrkBuffer = (GByte *) + VSIMalloc3( nWordSize * anBands.size(), nBlockXSize, nBlockYSize ); + if( pabyWrkBuffer == NULL ) + return CE_Failure; + + eErr = poBaseDS->DirectRasterIO( GF_Read, + nWXOff, nWYOff, nWXSize, nWYSize, + pabyWrkBuffer, nXSize, nYSize, + eDataType, anBands.size(), &anBands[0], + nWordSize, nWordSize*nBlockXSize, + nWordSize*nBlockXSize*nBlockYSize, + &sExtraArg ); + + if( eErr == CE_None ) + { + int nBandStart = 0; + for( iBand = 0; iBand < (int) anBands.size(); iBand++ ) + { + if( anBands[iBand] == nBand ) + { + // application requested band. + memcpy( pImage, pabyWrkBuffer + nBandStart, + nWordSize * nBlockXSize * nBlockYSize ); + } + else + { + // all others are pushed into cache. + GDALRasterBand *poBaseBand = + poBaseDS->GetRasterBand(anBands[iBand]); + JP2KAKRasterBand *poBand = NULL; + + if( nDiscardLevels == 0 ) + poBand = (JP2KAKRasterBand *) poBaseBand; + else + { + int iOver; + + for( iOver = 0; iOver < poBaseBand->GetOverviewCount(); iOver++ ) + { + poBand = (JP2KAKRasterBand *) + poBaseBand->GetOverview( iOver ); + if( poBand->nDiscardLevels == nDiscardLevels ) + break; + } + if( iOver == poBaseBand->GetOverviewCount() ) + { + CPLAssert( FALSE ); + } + } + + GDALRasterBlock *poBlock = NULL; + + if( poBand != NULL ) + poBlock = poBand->GetLockedBlockRef( nBlockXOff, nBlockYOff, TRUE ); + + if( poBlock ) + { + memcpy( poBlock->GetDataRef(), pabyWrkBuffer + nBandStart, + nWordSize * nBlockXSize * nBlockYSize ); + poBlock->DropLock(); + } + } + + nBandStart += nWordSize * nBlockXSize * nBlockYSize; + } + } + + VSIFree( pabyWrkBuffer ); + return eErr; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr +JP2KAKRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ +/* -------------------------------------------------------------------- */ +/* We need various criteria to skip out to block based methods. */ +/* -------------------------------------------------------------------- */ + if( poBaseDS->TestUseBlockIO( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + eBufType, 1, &nBand ) ) + return GDALPamRasterBand::IRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + else + { + int nOverviewDiscard = nDiscardLevels; + + // Adjust request for overview level. + while( nOverviewDiscard > 0 ) + { + nXOff = nXOff * 2; + nYOff = nYOff * 2; + nXSize = nXSize * 2; + nYSize = nYSize * 2; + nOverviewDiscard--; + } + + return poBaseDS->DirectRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + 1, &nBand, nPixelSpace, nLineSpace, 0, psExtraArg ); + } +} + +/************************************************************************/ +/* ApplyPalette() */ +/************************************************************************/ + +void JP2KAKRasterBand::ApplyPalette( jp2_palette oJP2Palette ) + +{ +/* -------------------------------------------------------------------- */ +/* Do we have a reasonable LUT configuration? RGB or RGBA? */ +/* -------------------------------------------------------------------- */ + if( !oJP2Palette.exists() ) + return; + + if( oJP2Palette.get_num_luts() == 0 || oJP2Palette.get_num_entries() == 0 ) + return; + + if( oJP2Palette.get_num_luts() < 3 ) + { + CPLDebug( "JP2KAK", "JP2KAKRasterBand::ApplyPalette()\n" + "Odd get_num_luts() value (%d)", + oJP2Palette.get_num_luts() ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Fetch the lut entries. Note that they are normalized in the */ +/* -0.5 to 0.5 range. */ +/* -------------------------------------------------------------------- */ + float *pafLUT; + int iColor, nCount = oJP2Palette.get_num_entries(); + + pafLUT = (float *) CPLCalloc(sizeof(float)*4, nCount); + + oJP2Palette.get_lut(0, pafLUT + 0); + oJP2Palette.get_lut(1, pafLUT + nCount); + oJP2Palette.get_lut(2, pafLUT + nCount*2); + + if( oJP2Palette.get_num_luts() == 4 ) + { + oJP2Palette.get_lut( 3, pafLUT + nCount*3 ); + } + else + { + for( iColor = 0; iColor < nCount; iColor++ ) + { + pafLUT[nCount*3 + iColor] = 0.5; + } + } + +/* -------------------------------------------------------------------- */ +/* Apply to GDAL colortable. */ +/* -------------------------------------------------------------------- */ + for( iColor=0; iColor < nCount; iColor++ ) + { + GDALColorEntry sEntry; + + sEntry.c1 = (short) MAX(0,MIN(255,pafLUT[iColor + nCount*0]*256+128)); + sEntry.c2 = (short) MAX(0,MIN(255,pafLUT[iColor + nCount*1]*256+128)); + sEntry.c3 = (short) MAX(0,MIN(255,pafLUT[iColor + nCount*2]*256+128)); + sEntry.c4 = (short) MAX(0,MIN(255,pafLUT[iColor + nCount*3]*256+128)); + + oCT.SetColorEntry( iColor, &sEntry ); + } + + CPLFree( pafLUT ); + + eInterp = GCI_PaletteIndex; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp JP2KAKRasterBand::GetColorInterpretation() + +{ + return eInterp; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *JP2KAKRasterBand::GetColorTable() + +{ + if( oCT.GetColorEntryCount() > 0 ) + return &oCT; + else + return NULL; +} + +/************************************************************************/ +/* ==================================================================== */ +/* JP2KAKDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* JP2KAKDataset() */ +/************************************************************************/ + +JP2KAKDataset::JP2KAKDataset() + +{ + poInput = NULL; + poRawInput = NULL; + family = NULL; + jpip_client = NULL; + poThreadEnv = NULL; + + bCached = 0; + bPreferNPReads = false; + bPromoteTo8Bit = false; + + poDriver = (GDALDriver*) GDALGetDriverByName( "JP2KAK" ); +} + +/************************************************************************/ +/* ~JP2KAKDataset() */ +/************************************************************************/ + +JP2KAKDataset::~JP2KAKDataset() + +{ + FlushCache(); + + if( poInput != NULL ) + { + oCodeStream.destroy(); + poInput->close(); + delete poInput; + if( family ) + { + family->close(); + delete family; + } + if( poRawInput != NULL ) + delete poRawInput; +#ifdef USE_JPIP + if( jpip_client != NULL ) + { + jpip_client->close(); + delete jpip_client; + } +#endif + } + + if( poThreadEnv != NULL ) + { + poThreadEnv->terminate(NULL,true); + poThreadEnv->destroy(); + delete poThreadEnv; + } +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr JP2KAKDataset::IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ +/* -------------------------------------------------------------------- */ +/* In order for building external overviews to work properly we */ +/* discard any concept of internal overviews when the user */ +/* first requests to build external overviews. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < GetRasterCount(); iBand++ ) + { + JP2KAKRasterBand *poBand = + (JP2KAKRasterBand *) GetRasterBand( iBand+1 ); + + for( int i = 0; i < poBand->nOverviewCount; i++ ) + delete poBand->papoOverviewBand[i]; + + CPLFree( poBand->papoOverviewBand ); + poBand->papoOverviewBand = NULL; + poBand->nOverviewCount = 0; + } + + return GDALPamDataset::IBuildOverviews( pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* KakaduInitialize() */ +/************************************************************************/ + +void JP2KAKDataset::KakaduInitialize() + +{ +/* -------------------------------------------------------------------- */ +/* Initialize Kakadu warning/error reporting subsystem. */ +/* -------------------------------------------------------------------- */ + if( !kakadu_initialized ) + { + kakadu_initialized = TRUE; + + kdu_cpl_error_message oErrHandler( CE_Failure ); + kdu_cpl_error_message oWarningHandler( CE_Warning ); + + kdu_customize_warnings(new kdu_cpl_error_message( CE_Warning ) ); + kdu_customize_errors(new kdu_cpl_error_message( CE_Failure ) ); + } +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int JP2KAKDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Check header */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < (int) sizeof(jp2_header) ) + { + const char *pszExtension = NULL; + + pszExtension = CPLGetExtension( poOpenInfo->pszFilename ); + if( (EQUALN(poOpenInfo->pszFilename,"http://",7) + || EQUALN(poOpenInfo->pszFilename,"https://",8) + || EQUALN(poOpenInfo->pszFilename,"jpip://",7)) + && EQUAL(pszExtension,"jp2") ) + { +#ifdef USE_JPIP + return TRUE; +#else + return FALSE; +#endif + } + else if( EQUALN(poOpenInfo->pszFilename,"J2K_SUBFILE:",12) ) + return TRUE; + else + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Any extension is supported for JP2 files. Only selected */ +/* extensions are supported for JPC files since the standard */ +/* prefix is so short (two bytes). */ +/* -------------------------------------------------------------------- */ + if( memcmp(poOpenInfo->pabyHeader,jp2_header,sizeof(jp2_header)) == 0 ) + return TRUE; + else if( memcmp( poOpenInfo->pabyHeader, jpc_header, + sizeof(jpc_header) ) == 0 ) + { + const char *pszExtension = NULL; + + pszExtension = CPLGetExtension( poOpenInfo->pszFilename ); + if( EQUAL(pszExtension,"jpc") + || EQUAL(pszExtension,"j2k") + || EQUAL(pszExtension,"jp2") + || EQUAL(pszExtension,"jpx") + || EQUAL(pszExtension,"j2c") ) + return TRUE; + + // We also want to handle jpc datastreams vis /vsisubfile. + if( strstr(poOpenInfo->pszFilename,"vsisubfile") != NULL ) + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *JP2KAKDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + subfile_source *poRawInput = NULL; + const char *pszExtension = NULL; + int bIsJPIP = FALSE; + int bIsSubfile = FALSE; + GByte *pabyHeader = NULL; + + if( !Identify( poOpenInfo ) ) + return NULL; + + int bResilient = CSLTestBoolean( + CPLGetConfigOption( "JP2KAK_RESILIENT", "NO" ) ); + + /* Doesn't seem to bring any real performance gain on Linux */ + int bBuffered = CSLTestBoolean( + CPLGetConfigOption( "JP2KAK_BUFFERED", +#ifdef WIN32 + "YES" +#else + "NO" +#endif + ) ); + +/* -------------------------------------------------------------------- */ +/* Handle setting up datasource for JPIP. */ +/* -------------------------------------------------------------------- */ + KakaduInitialize(); + + pszExtension = CPLGetExtension( poOpenInfo->pszFilename ); + if( poOpenInfo->nHeaderBytes < 16 ) + { + if( (EQUALN(poOpenInfo->pszFilename,"http://",7) + || EQUALN(poOpenInfo->pszFilename,"https://",8) + || EQUALN(poOpenInfo->pszFilename,"jpip://",7)) + && EQUAL(pszExtension,"jp2") ) + { + bIsJPIP = TRUE; + } + else if( EQUALN(poOpenInfo->pszFilename,"J2K_SUBFILE:",12) ) + { + static GByte abySubfileHeader[16]; + + try + { + poRawInput = new subfile_source; + poRawInput->open( poOpenInfo->pszFilename, bResilient, bBuffered ); + poRawInput->seek( 0 ); + + poRawInput->read( abySubfileHeader, 16 ); + poRawInput->seek( 0 ); + } + catch( ... ) + { + return NULL; + } + + pabyHeader = abySubfileHeader; + + bIsSubfile = TRUE; + } + else + return NULL; + } + else + { + pabyHeader = poOpenInfo->pabyHeader; + } + +/* -------------------------------------------------------------------- */ +/* If we think this should be access via vsil, then open it using */ +/* subfile_source. We do this if it does not seem to open normally*/ +/* or if we want to operate in resilient (sequential) mode. */ +/* -------------------------------------------------------------------- */ + VSIStatBuf sStat; + if( poRawInput == NULL + && !bIsJPIP + && (bBuffered || bResilient || VSIStat(poOpenInfo->pszFilename, &sStat) != 0) ) + { + try + { + poRawInput = new subfile_source; + poRawInput->open( poOpenInfo->pszFilename, bResilient, bBuffered ); + poRawInput->seek( 0 ); + } + catch( ... ) + { + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* If the header is a JP2 header, mark this as a JP2 dataset. */ +/* -------------------------------------------------------------------- */ + if( pabyHeader && memcmp(pabyHeader,jp2_header,sizeof(jp2_header)) == 0 ) + pszExtension = "jp2"; + +/* -------------------------------------------------------------------- */ +/* Try to open the file in a manner depending on the extension. */ +/* -------------------------------------------------------------------- */ + kdu_compressed_source *poInput = NULL; + kdu_client *jpip_client = NULL; + jp2_palette oJP2Palette; + jp2_channels oJP2Channels; + + jp2_family_src *family = NULL; + + try + { + if( bIsJPIP ) + { +#ifdef USE_JPIP + jp2_source *jp2_src; + char *pszWrk = CPLStrdup(strstr(poOpenInfo->pszFilename,"://")+3); + char *pszRequest = strstr(pszWrk,"/"); + + if( pszRequest == NULL ) + { + CPLDebug( "JP2KAK", + "Failed to parse JPIP server and request." ); + CPLFree( pszWrk ); + return NULL; + } + + *(pszRequest++) = '\0'; + + CPLDebug( "JP2KAK", "server=%s, request=%s", + pszWrk, pszRequest ); + + CPLSleep( 15.0 ); + jpip_client = new kdu_client; + jpip_client->connect( pszWrk, NULL, pszRequest, "http-tcp", + "" ); + + CPLDebug( "JP2KAK", "After connect()" ); + + bool bin0_complete = false; + + while( jpip_client->get_databin_length(KDU_META_DATABIN,0,0, + &bin0_complete) <= 0 + || !bin0_complete ) + CPLSleep( 0.25 ); + + family = new jp2_family_src; + family->open( jpip_client ); + + jp2_src = new jp2_source; + jp2_src->open( family ); + jp2_src->read_header(); + + while( !jpip_client->is_idle() ) + CPLSleep( 0.25 ); + + if( jpip_client->is_alive() ) + CPLDebug( "JP2KAK", "connect() seems to be complete." ); + else + { + CPLDebug( "JP2KAK", "connect() seems to have failed." ); + return NULL; + } + + oJP2Channels = jp2_src->access_channels(); + + poInput = jp2_src; +#else + CPLError( CE_Failure, CPLE_OpenFailed, + "JPIP Protocol not supported by GDAL with Kakadu 3.4 or on Unix." ); + return NULL; +#endif + } + else if( pszExtension != NULL + && (EQUAL(pszExtension,"jp2") || EQUAL(pszExtension,"jpx")) ) + { + jp2_source *jp2_src; + + family = new jp2_family_src; + if( poRawInput != NULL ) + family->open( poRawInput ); + else + family->open( poOpenInfo->pszFilename, true ); + jp2_src = new jp2_source; + if( !jp2_src->open( family ) || + !jp2_src->read_header() ) + { + CPLDebug( "JP2KAK", "Cannot read JP2 boxes" ); + delete jp2_src; + delete family; + return NULL; + } + + poInput = jp2_src; + + oJP2Palette = jp2_src->access_palette(); + oJP2Channels = jp2_src->access_channels(); + + jp2_colour oColors = jp2_src->access_colour(); + if( oColors.get_space() != JP2_sRGB_SPACE + && oColors.get_space() != JP2_sLUM_SPACE ) + { + CPLDebug( "JP2KAK", "Unusual ColorSpace=%d, not further interpreted.", + (int) oColors.get_space() ); + } + } + else if( poRawInput == NULL ) + { + poInput = new kdu_simple_file_source( poOpenInfo->pszFilename ); + } + else + { + poInput = poRawInput; + poRawInput = NULL; + } + } + catch( ... ) + { + CPLDebug( "JP2KAK", "Trapped Kakadu exception." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + JP2KAKDataset *poDS = NULL; + + try + { + poDS = new JP2KAKDataset(); + + poDS->poInput = poInput; + poDS->poRawInput = poRawInput; + poDS->oCodeStream.create( poInput ); + poDS->oCodeStream.set_persistent(); + + poDS->bCached = bBuffered; + poDS->bResilient = bResilient; + poDS->bFussy = CSLTestBoolean( + CPLGetConfigOption( "JP2KAK_FUSSY", "NO" ) ); + + if( poDS->bFussy ) + poDS->oCodeStream.set_fussy(); + if( poDS->bResilient ) + poDS->oCodeStream.set_resilient(); + + poDS->jpip_client = jpip_client; + + poDS->family = family; + +/* -------------------------------------------------------------------- */ +/* Get overall image size. */ +/* -------------------------------------------------------------------- */ + poDS->oCodeStream.get_dims( 0, poDS->dims ); + + poDS->nRasterXSize = poDS->dims.size.x; + poDS->nRasterYSize = poDS->dims.size.y; + +/* -------------------------------------------------------------------- */ +/* Ensure that all the components have the same dimensions. If */ +/* not, just process the first dimension. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = poDS->oCodeStream.get_num_components(); + + if (poDS->nBands > 1 ) + { + int iDim; + + for( iDim = 1; iDim < poDS->nBands; iDim++ ) + { + kdu_dims dim_this_comp; + + poDS->oCodeStream.get_dims(iDim, dim_this_comp); + + if( dim_this_comp != poDS->dims ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Some components have mismatched dimensions, " + "ignoring all but first." ); + poDS->nBands = 1; + break; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Setup the thread environment. */ +/* -------------------------------------------------------------------- */ + int nNumThreads = atoi(CPLGetConfigOption("JP2KAK_THREADS","-1")); + if( nNumThreads == -1 ) + nNumThreads = kdu_get_num_processors()-1; + + if( nNumThreads > 0 ) + { + int iThread; + + poDS->poThreadEnv = new kdu_thread_env; + poDS->poThreadEnv->create(); + + for( iThread=0; iThread < nNumThreads; iThread++ ) + { + if( !poDS->poThreadEnv->add_thread() ) + break; + } + CPLDebug( "JP2KAK", "Using %d threads.", nNumThreads ); + } + else + CPLDebug( "JP2KAK", "Operating in singlethreaded mode." ); + +/* -------------------------------------------------------------------- */ +/* Is this a file with poor internal navigation that will end */ +/* up using a great deal of memory if we use keep persistent */ +/* parsed information around? (#3295) */ +/* -------------------------------------------------------------------- */ + siz_params *siz = poDS->oCodeStream.access_siz(); + kdu_params *cod = siz->access_cluster(COD_params); + bool use_precincts; + + cod->get(Cuse_precincts,0,0,use_precincts); + + const char *pszPersist = CPLGetConfigOption( "JP2KAK_PERSIST", "AUTO"); + if( EQUAL(pszPersist,"AUTO") ) + { + if( !use_precincts && !bIsJPIP + && (poDS->nRasterXSize * (double) poDS->nRasterYSize) + > 100000000.0 ) + poDS->bPreferNPReads = true; + } + else + poDS->bPreferNPReads = !CSLTestBoolean(pszPersist); + + CPLDebug( "JP2KAK", "Cuse_precincts=%d, PreferNonPersistentReads=%d", + (int) use_precincts, poDS->bPreferNPReads ); + +/* -------------------------------------------------------------------- */ +/* Deduce some other info about the dataset. */ +/* -------------------------------------------------------------------- */ + int order; + + cod->get(Corder,0,0,order); + + if( order == Corder_LRCP ) + { + CPLDebug( "JP2KAK", "order=LRCP" ); + poDS->SetMetadataItem("Corder","LRCP"); + } + else if( order == Corder_RLCP ) + { + CPLDebug( "JP2KAK", "order=RLCP" ); + poDS->SetMetadataItem("Corder","RLCP"); + } + else if( order == Corder_RPCL ) + { + CPLDebug( "JP2KAK", "order=RPCL" ); + poDS->SetMetadataItem("Corder","RPCL"); + } + else if( order == Corder_PCRL ) + { + CPLDebug( "JP2KAK", "order=PCRL" ); + poDS->SetMetadataItem("Corder","PCRL"); + } + else if( order == Corder_CPRL ) + { + CPLDebug( "JP2KAK", "order=CPRL" ); + poDS->SetMetadataItem("Corder","CPRL"); + } + else + { + CPLDebug( "JP2KAK", "order=%d, not recognized.", order ); + } + + poDS->bUseYCC = false; + cod->get(Cycc,0,0,poDS->bUseYCC); + if( poDS->bUseYCC ) + CPLDebug( "JP2KAK", "ycc=true" ); + +/* -------------------------------------------------------------------- */ +/* find out how many resolutions levels are available. */ +/* -------------------------------------------------------------------- */ + kdu_dims tile_indices; + poDS->oCodeStream.get_valid_tiles(tile_indices); + + kdu_tile tile = poDS->oCodeStream.open_tile(tile_indices.pos); + poDS->nResCount = tile.access_component(0).get_num_resolutions(); + tile.close(); + + CPLDebug( "JP2KAK", "nResCount=%d", poDS->nResCount ); + + +/* -------------------------------------------------------------------- */ +/* Should we promote alpha channel to 8 bits ? */ +/* -------------------------------------------------------------------- */ + poDS->bPromoteTo8Bit = (poDS->nBands == 4 && + poDS->oCodeStream.get_bit_depth(0) == 8 && + poDS->oCodeStream.get_bit_depth(1) == 8 && + poDS->oCodeStream.get_bit_depth(2) == 8 && + poDS->oCodeStream.get_bit_depth(3) == 1 && + CSLFetchBoolean(poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION", TRUE)); + if( poDS->bPromoteTo8Bit ) + CPLDebug( "JP2KAK", "Fourth (alpha) band is promoted from 1 bit to 8 bit"); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + { + JP2KAKRasterBand *poBand = + new JP2KAKRasterBand(iBand,0,poDS->oCodeStream,poDS->nResCount, + jpip_client, oJP2Channels, poDS ); + + if( iBand == 1 && oJP2Palette.exists() ) + poBand->ApplyPalette( oJP2Palette ); + + poDS->SetBand( iBand, poBand ); + } + +/* -------------------------------------------------------------------- */ +/* Look for supporting coordinate system information. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes != 0 ) + { + poDS->LoadJP2Metadata(poOpenInfo); + } + +/* -------------------------------------------------------------------- */ +/* Establish our corresponding physical file. */ +/* -------------------------------------------------------------------- */ + CPLString osPhysicalFilename = poOpenInfo->pszFilename; + + if( bIsSubfile || EQUALN(poOpenInfo->pszFilename,"/vsisubfile/",12) ) + { + if( strstr(poOpenInfo->pszFilename,",") != NULL ) + osPhysicalFilename = strstr(poOpenInfo->pszFilename,",") + 1; + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + if( !bIsSubfile ) + poDS->TryLoadXML(); + else + poDS->nPamFlags |= GPF_NOSAVE; + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, osPhysicalFilename ); + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + delete poDS; + CPLError( CE_Failure, CPLE_NotSupported, + "The JP2KAK driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Vector layers */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nOpenFlags & GDAL_OF_VECTOR ) + { + poDS->LoadVectorLayers( + CSLFetchBoolean(poOpenInfo->papszOpenOptions, "OPEN_REMOTE_GML", FALSE)); + + // If file opened in vector-only mode and there's no vector, + // return + if( (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 && + poDS->GetLayerCount() == 0 ) + { + delete poDS; + return NULL; + } + } + + return( poDS ); + } + +/* -------------------------------------------------------------------- */ +/* Catch all fatal kakadu errors and cleanup a bit. */ +/* -------------------------------------------------------------------- */ + catch( ... ) + { + CPLDebug( "JP2KAK", "JP2KAKDataset::Open() - caught exception." ); + if( poDS != NULL ) + delete poDS; + + return NULL; + } +} + +/************************************************************************/ +/* DirectRasterIO() */ +/************************************************************************/ + +CPLErr +JP2KAKDataset::DirectRasterIO( CPL_UNUSED GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ + kdu_codestream *poCodeStream = &oCodeStream; + const char *pszPersistency = ""; + + CPLAssert( eBufType == GDT_Byte + || eBufType == GDT_Int16 + || eBufType == GDT_UInt16 ); + +/* -------------------------------------------------------------------- */ +/* Do we want to do this non-persistently? If so, we need to */ +/* open the file, and establish a local codestream. */ +/* -------------------------------------------------------------------- */ + subfile_source subfile_src; + jp2_source wrk_jp2_src; + jp2_family_src wrk_family; + kdu_codestream oWCodeStream; + + if( bPreferNPReads ) + { + subfile_src.open( GetDescription(), bResilient, bCached ); + + if( family != NULL ) + { + wrk_family.open( &subfile_src ); + wrk_jp2_src.open( &wrk_family ); + wrk_jp2_src.read_header(); + + oWCodeStream.create( &wrk_jp2_src ); + } + else + { + oWCodeStream.create( &subfile_src ); + } + + if( bFussy ) + oWCodeStream.set_fussy(); + if( bResilient ) + oWCodeStream.set_resilient(); + + poCodeStream= &oWCodeStream; + + pszPersistency = "(non-persistent)"; + } + +/* -------------------------------------------------------------------- */ +/* Select optimal resolution level. */ +/* -------------------------------------------------------------------- */ + int nDiscardLevels = 0; + int nResMult = 1; + + while( nDiscardLevels < nResCount - 1 + && nBufXSize * nResMult * 2 < nXSize * 1.01 + && nBufYSize * nResMult * 2 < nYSize * 1.01 ) + { + nDiscardLevels++; + nResMult = nResMult * 2; + } + +/* -------------------------------------------------------------------- */ +/* Prepare component indices list. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr=CE_None; + int *component_indices; + int *stripe_heights, *sample_offsets, *sample_gaps, *row_gaps, *precisions; + bool *is_signed; + int i; + + component_indices = (int *) CPLMalloc(sizeof(int) * nBandCount); + stripe_heights = (int *) CPLMalloc(sizeof(int) * nBandCount); + sample_offsets = (int *) CPLMalloc(sizeof(int) * nBandCount); + sample_gaps = (int *) CPLMalloc(sizeof(int) * nBandCount); + row_gaps = (int *) CPLMalloc(sizeof(int) * nBandCount); + precisions = (int *) CPLMalloc(sizeof(int) * nBandCount); + is_signed = (bool *) CPLMalloc(sizeof(bool) * nBandCount); + + for( i = 0; i < nBandCount; i++ ) + component_indices[i] = panBandMap[i] - 1; + +/* -------------------------------------------------------------------- */ +/* Setup a ROI matching the block requested, and select desired */ +/* bands (components). */ +/* -------------------------------------------------------------------- */ + try + { + kdu_dims dims; + poCodeStream->apply_input_restrictions( 0, 0, nDiscardLevels, 0, NULL ); + poCodeStream->get_dims( 0, dims ); + + dims.pos.x = dims.pos.x + nXOff/nResMult; + dims.pos.y = dims.pos.y + nYOff/nResMult; + dims.size.x = nXSize/nResMult; + dims.size.y = nYSize/nResMult; + + kdu_dims dims_roi; + + poCodeStream->map_region( 0, dims, dims_roi ); + poCodeStream->apply_input_restrictions( nBandCount, component_indices, + nDiscardLevels, 0, &dims_roi, + KDU_WANT_OUTPUT_COMPONENTS); + +/* -------------------------------------------------------------------- */ +/* Special case where the data is being requested exactly at */ +/* this resolution. Avoid any extra sampling pass. */ +/* -------------------------------------------------------------------- */ + if( nBufXSize == dims.size.x && nBufYSize == dims.size.y ) + { + kdu_stripe_decompressor decompressor; + decompressor.start(*poCodeStream,false,false,poThreadEnv); + + CPLDebug( "JP2KAK", "DirectRasterIO() for %d,%d,%d,%d -> %dx%d (no intermediate) %s", + nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, + pszPersistency ); + + for( i = 0; i < nBandCount; i++ ) + { + stripe_heights[i] = dims.size.y; + precisions[i] = poCodeStream->get_bit_depth(i); + if( eBufType == GDT_Byte ) + { + is_signed[i] = false; + sample_offsets[i] = i * nBandSpace; + sample_gaps[i] = nPixelSpace; + row_gaps[i] = nLineSpace; + } + else if( eBufType == GDT_Int16 ) + { + is_signed[i] = true; + sample_offsets[i] = i * nBandSpace / 2; + sample_gaps[i] = nPixelSpace / 2; + row_gaps[i] = nLineSpace / 2; + } + else if( eBufType == GDT_UInt16 ) + { + is_signed[i] = false; + sample_offsets[i] = i * nBandSpace / 2; + sample_gaps[i] = nPixelSpace / 2; + row_gaps[i] = nLineSpace / 2; + /* Introduced in r25136 with an unrelated commit message. + Reverted per ticket #5328 + if( precisions[i] == 12 ) + { + CPLDebug( "JP2KAK", "16bit extend 12 bit data." ); + precisions[i] = 16; + }*/ + } + + } + + if( eBufType == GDT_Byte ) + decompressor.pull_stripe( (kdu_byte *) pData, stripe_heights, + sample_offsets, sample_gaps, row_gaps, + precisions ); + else + decompressor.pull_stripe( (kdu_int16 *) pData, stripe_heights, + sample_offsets, sample_gaps,row_gaps, + precisions, is_signed ); + decompressor.finish(); + } + +/* -------------------------------------------------------------------- */ +/* More general case - first pull into working buffer. */ +/* -------------------------------------------------------------------- */ + else + { + GByte *pabyIntermediate = (GByte *) + VSIMalloc3(dims.size.x, dims.size.y, 2*nBandCount ); + if( pabyIntermediate == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Failed to allocate %d byte intermediate decompression buffer for jpeg2000.", + dims.size.x * dims.size.y * nBandCount ); + + return CE_Failure; + } + + CPLDebug( "JP2KAK", + "DirectRasterIO() for %d,%d,%d,%d -> %dx%d -> %dx%d %s", + nXOff, nYOff, nXSize, nYSize, + dims.size.x, dims.size.y, + nBufXSize, nBufYSize, + pszPersistency ); + + kdu_stripe_decompressor decompressor; + decompressor.start(*poCodeStream,false,false,poThreadEnv); + + for( i = 0; i < nBandCount; i++ ) + { + stripe_heights[i] = dims.size.y; + precisions[i] = poCodeStream->get_bit_depth(i); + + if( eBufType == GDT_Int16 || eBufType == GDT_UInt16 ) + { + if( eBufType == GDT_Int16 ) + is_signed[i] = true; + else + is_signed[i] = false; + } + } + + if( eBufType == GDT_Byte ) + decompressor.pull_stripe( (kdu_byte *) pabyIntermediate, + stripe_heights, NULL, NULL, NULL, + precisions ); + else + decompressor.pull_stripe( (kdu_int16 *) pabyIntermediate, + stripe_heights, + NULL, NULL, NULL, + precisions, is_signed ); + + decompressor.finish(); + +/* -------------------------------------------------------------------- */ +/* Then resample (normally downsample) from the intermediate */ +/* buffer into the final buffer in the desired output layout. */ +/* -------------------------------------------------------------------- */ + int iY, iX; + double dfYRatio = dims.size.y / (double) nBufYSize; + double dfXRatio = dims.size.x / (double) nBufXSize; + + for( iY = 0; iY < nBufYSize; iY++ ) + { + int iSrcY = (int) floor( (iY + 0.5) * dfYRatio ); + + iSrcY = MIN(iSrcY, dims.size.y-1); + + for( iX = 0; iX < nBufXSize; iX++ ) + { + int iSrcX = (int) floor( (iX + 0.5) * dfXRatio ); + + iSrcX = MIN(iSrcX, dims.size.x-1); + + for( i = 0; i < nBandCount; i++ ) + { + if( eBufType == GDT_Byte ) + ((GByte *) pData)[iX*nPixelSpace + + iY*nLineSpace + + i*nBandSpace] = + pabyIntermediate[iSrcX*nBandCount + + iSrcY*dims.size.x*nBandCount + + i]; + else if( eBufType == GDT_Int16 + || eBufType == GDT_UInt16 ) + ((GUInt16 *) pData)[iX*nPixelSpace/2 + + iY*nLineSpace/2 + + i*nBandSpace/2] = + ((GUInt16 *)pabyIntermediate)[ + iSrcX*nBandCount + + iSrcY*dims.size.x*nBandCount + + i]; + } + } + } + + CPLFree( pabyIntermediate ); + } + } +/* -------------------------------------------------------------------- */ +/* Catch interal Kakadu errors. */ +/* -------------------------------------------------------------------- */ + catch( ... ) + { + eErr = CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* 1-bit alpha promotion. */ +/* -------------------------------------------------------------------- */ + if( nBandCount == 4 && bPromoteTo8Bit ) + { + for(int j=0;jGetRasterDataType() + || ( eDataType != GDT_Byte + && eDataType != GDT_Int16 + && eDataType != GDT_UInt16 ) ) + return TRUE; + + int i, j; + + for( i = 0; i < nBandCount; i++ ) + { + for( j = i+1; j < nBandCount; j++ ) + if( panBandList[j] == panBandList[i] ) + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* If we have external overviews built, and they could be used */ +/* to satisfy this request, we will avoid DirectRasterIO() */ +/* which would ignore them. */ +/* -------------------------------------------------------------------- */ + if( GetRasterCount() == 0 ) + return TRUE; + + JP2KAKRasterBand *poWrkBand = (JP2KAKRasterBand*) GetRasterBand(1); + if( poWrkBand->HasExternalOverviews() ) + { + int nOverview; + int nXOff2=nXOff, nYOff2=nYOff, nXSize2=nXSize, nYSize2=nYSize; + + nOverview = + GDALBandGetBestOverviewLevel2( poWrkBand, + nXOff2, nYOff2, nXSize2, nYSize2, + nBufXSize, nBufYSize, NULL); + if (nOverview >= 0 ) + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* The rest of the rules are io strategy stuff, and use */ +/* configuration checks. */ +/* -------------------------------------------------------------------- */ + int bUseBlockedIO = bForceCachedIO; + + if( nYSize == 1 || nXSize * ((double) nYSize) < 100.0 ) + bUseBlockedIO = TRUE; + + if( nBufYSize == 1 || nBufXSize * ((double) nBufYSize) < 100.0 ) + bUseBlockedIO = TRUE; + + if( strlen(CPLGetConfigOption( "GDAL_ONE_BIG_READ", "")) > 0 ) + bUseBlockedIO = + !CSLTestBoolean(CPLGetConfigOption( "GDAL_ONE_BIG_READ", "")); + + return bUseBlockedIO; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr JP2KAKDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ +/* -------------------------------------------------------------------- */ +/* We need various criteria to skip out to block based methods. */ +/* -------------------------------------------------------------------- */ + if( TestUseBlockIO( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap ) ) + return GDALPamDataset::IRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); + else + return DirectRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); +} + +/************************************************************************/ +/* JP2KAKWriteBox() */ +/* */ +/* Write out the passed box and delete it. */ +/************************************************************************/ + +static void JP2KAKWriteBox( jp2_target *jp2_out, GDALJP2Box *poBox ) + +{ + GUInt32 nBoxType; + + if( poBox == NULL ) + return; + + memcpy( &nBoxType, poBox->GetType(), 4 ); + CPL_MSBPTR32( &nBoxType ); + +/* -------------------------------------------------------------------- */ +/* Write to a box on the JP2 file. */ +/* -------------------------------------------------------------------- */ + jp2_out->open_next( nBoxType ); + + jp2_out->write( (kdu_byte *) poBox->GetWritableData(), + (int) poBox->GetDataLength() ); + + jp2_out->close(); + + delete poBox; +} + +/************************************************************************/ +/* JP2KAKCreateCopy_WriteTile() */ +/************************************************************************/ + +static int +JP2KAKCreateCopy_WriteTile( GDALDataset *poSrcDS, kdu_tile &oTile, + kdu_roi_image *poROIImage, + int nXOff, int nYOff, int nXSize, int nYSize, + bool bReversible, int nBits, GDALDataType eType, + kdu_codestream &oCodeStream, int bFlushEnabled, + kdu_long *layer_bytes, int layer_count, + GDALProgressFunc pfnProgress, void * pProgressData, + bool bComseg ) + +{ +/* -------------------------------------------------------------------- */ +/* Create one big tile, and a compressing engine, and line */ +/* buffer for each component. */ +/* -------------------------------------------------------------------- */ + int c, num_components = oTile.get_num_components(); + kdu_push_ifc *engines = new kdu_push_ifc[num_components]; + kdu_line_buf *lines = new kdu_line_buf[num_components]; + kdu_sample_allocator allocator; + + // Ticket #4050 patch : use a 32 bits kdu_line_buf for GDT_UInt16 reversible compression + // ToDo: test for GDT_UInt16? + bool bUseShorts = bReversible; + if ((eType == GDT_UInt16)&&(bReversible)) + bUseShorts = false; + + for (c=0; c < num_components; c++) + { + kdu_resolution res = oTile.access_component(c).access_resolution(); + kdu_roi_node *roi_node = NULL; + + if( poROIImage != NULL ) + { + kdu_dims dims; + + res.get_dims(dims); + roi_node = poROIImage->acquire_node(c,dims); + } +#if KDU_MAJOR_VERSION >= 7 + lines[c].pre_create(&allocator,nXSize,bReversible,bUseShorts,0,0); +#else + lines[c].pre_create(&allocator,nXSize,bReversible,bUseShorts); +#endif + engines[c] = kdu_analysis(res,&allocator,bUseShorts,1.0F,roi_node); + } + + try + { +#if KDU_MAJOR_VERSION > 7 || (KDU_MAJOR_VERSION == 7 && (KDU_MINOR_VERSION > 3 || KDU_MINOR_VERSION == 3 && KDU_PATCH_VERSION >= 1)) + allocator.finalize(oCodeStream); +#else + allocator.finalize(); +#endif + + for (c=0; c < num_components; c++) + lines[c].create(); + } + catch( ... ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "allocate.finalize() failed, likely out of memory for compression information." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Write whole image. Write 1024 lines of each component, then */ +/* go back to the first, and do again. This gives the rate */ +/* computing machine all components to make good estimates. */ +/* -------------------------------------------------------------------- */ + int iLine, iLinesWritten = 0; + + GByte *pabyBuffer = (GByte *) + CPLMalloc(nXSize * (GDALGetDataTypeSize(eType)/8) ); + + CPLAssert( !oTile.get_ycc() ); + + int bRet = TRUE; + for( iLine = 0; iLine < nYSize && bRet; iLine += TILE_CHUNK_SIZE ) + { + for (c=0; c < num_components && bRet; c++) + { + GDALRasterBand *poBand = poSrcDS->GetRasterBand( c+1 ); + int iSubline = 0; + + for( iSubline = iLine; + iSubline < iLine+TILE_CHUNK_SIZE && iSubline < nYSize; + iSubline++ ) + { + if( poBand->RasterIO( GF_Read, + nXOff, nYOff+iSubline, nXSize, 1, + (void *) pabyBuffer, nXSize, 1, eType, + 0, 0, NULL ) == CE_Failure ) + { + bRet = FALSE; + break; + } + + if( bReversible && eType == GDT_Byte ) + { + kdu_sample16 *dest = lines[c].get_buf16(); + kdu_byte *sp = pabyBuffer; + + for (int n=nXSize; n > 0; n--, dest++, sp++) + dest->ival = ((kdu_int16)(*sp)) - 128; + } + else if( bReversible && eType == GDT_Int16 ) + { + kdu_sample16 *dest = lines[c].get_buf16(); + GInt16 *sp = (GInt16 *) pabyBuffer; + + for (int n=nXSize; n > 0; n--, dest++, sp++) + dest->ival = *sp; + } + else if( bReversible && eType == GDT_UInt16 ) + { + // Ticket #4050 patch : use a 32 bits kdu_line_buf for GDT_UInt16 reversible compression + kdu_sample32 *dest = lines[c].get_buf32(); + GUInt16 *sp = (GUInt16 *) pabyBuffer; + + for (int n=nXSize; n > 0; n--, dest++, sp++) + dest->ival = (kdu_int32)(*sp)-32768; + } + else if( eType == GDT_Byte ) + { + kdu_sample32 *dest = lines[c].get_buf32(); + kdu_byte *sp = pabyBuffer; + int nOffset = 1 << (nBits-1); + float fScale = (float) (1.0 / (1 << nBits)); + + for (int n=nXSize; n > 0; n--, dest++, sp++) + dest->fval = (float) + ((((kdu_int16)(*sp))-nOffset) * fScale); + } + else if( eType == GDT_Int16 ) + { + kdu_sample32 *dest = lines[c].get_buf32(); + GInt16 *sp = (GInt16 *) pabyBuffer; + float fScale = (float) (1.0 / (1 << nBits)); + + for (int n=nXSize; n > 0; n--, dest++, sp++) + dest->fval = (float) + (((kdu_int16)(*sp)) * fScale); + } + else if( eType == GDT_UInt16 ) + { + kdu_sample32 *dest = lines[c].get_buf32(); + GUInt16 *sp = (GUInt16 *) pabyBuffer; + int nOffset = 1 << (nBits-1); + float fScale = (float) (1.0 / (1 << nBits)); + + for (int n=nXSize; n > 0; n--, dest++, sp++) + dest->fval = (float) + (((int)(*sp) - nOffset) * fScale); + } + else if( eType == GDT_Float32 ) + { + kdu_sample32 *dest = lines[c].get_buf32(); + float *sp = (float *) pabyBuffer; + + for (int n=nXSize; n > 0; n--, dest++, sp++) + dest->fval = *sp; /* scale it? */ + } + +#if KDU_MAJOR_VERSION >= 7 + engines[c].push(lines[c]); +#else + engines[c].push(lines[c],true); +#endif + + iLinesWritten++; + + if( !pfnProgress( iLinesWritten + / (double) (num_components * nYSize), + NULL, pProgressData ) ) + { + bRet = FALSE; + break; + } + } + } + if( !bRet ) + break; + + if( oCodeStream.ready_for_flush() && bFlushEnabled ) + { + CPLDebug( "JP2KAK", + "Calling oCodeStream.flush() at line %d", + MIN(nYSize,iLine+TILE_CHUNK_SIZE) ); + try + { + oCodeStream.flush( layer_bytes, layer_count, NULL, + true, bComseg ); + } + catch(...) + { + bRet = FALSE; + } + } + else if( bFlushEnabled ) + CPLDebug( "JP2KAK", + "read_for_flush() is false at line %d.", + iLine ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup resources. */ +/* -------------------------------------------------------------------- */ + for( c = 0; c < num_components; c++ ) + engines[c].destroy(); + + delete[] engines; + delete[] lines; + + CPLFree( pabyBuffer ); + + if( poROIImage != NULL ) + delete poROIImage; + + return bRet; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +static GDALDataset * +JP2KAKCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int nTileXSize = nXSize; + int nTileYSize = nYSize; + bool bReversible = false; + int bFlushEnabled = CSLFetchBoolean( papszOptions, "FLUSH", TRUE ); + int nBits; + + if( poSrcDS->GetRasterCount() == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Creating zero band files not supported by JP2KAK driver." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Initialize Kakadu warning/error reporting subsystem. */ +/* -------------------------------------------------------------------- */ + if( !kakadu_initialized ) + { + kakadu_initialized = TRUE; + + kdu_cpl_error_message oErrHandler( CE_Failure ); + kdu_cpl_error_message oWarningHandler( CE_Warning ); + + kdu_customize_warnings(new kdu_cpl_error_message( CE_Warning ) ); + kdu_customize_errors(new kdu_cpl_error_message( CE_Failure ) ); + } + +/* -------------------------------------------------------------------- */ +/* What data type should we use? We assume all datatypes match */ +/* the first band. */ +/* -------------------------------------------------------------------- */ + GDALDataType eType; + GDALRasterBand *poPrototypeBand = poSrcDS->GetRasterBand(1); + + eType = poPrototypeBand->GetRasterDataType(); + if( eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_UInt16 + && eType != GDT_Float32 ) + { + if( bStrict ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "JP2KAK (JPEG2000) driver does not support data type %s.", + GDALGetDataTypeName( eType ) ); + return NULL; + } + + CPLError(CE_Warning, CPLE_AppDefined, + "JP2KAK (JPEG2000) driver does not support data type %s, forcing to Float32.", + GDALGetDataTypeName( eType ) ); + + eType = GDT_Float32; + } + +/* -------------------------------------------------------------------- */ +/* Do we want to write a pseudo-colored image? */ +/* -------------------------------------------------------------------- */ + int bHaveCT = poPrototypeBand->GetColorTable() != NULL + && poSrcDS->GetRasterCount() == 1; + +/* -------------------------------------------------------------------- */ +/* How many layers? */ +/* -------------------------------------------------------------------- */ + int layer_count = 12; + + if( CSLFetchNameValue(papszOptions,"LAYERS") != NULL ) + layer_count = atoi(CSLFetchNameValue(papszOptions,"LAYERS")); + else if( CSLFetchNameValue(papszOptions,"Clayers") != NULL ) + layer_count = atoi(CSLFetchNameValue(papszOptions,"Clayers")); + +/* -------------------------------------------------------------------- */ +/* Establish how many bytes of data we want for each layer. */ +/* We take the quality as a percentage, so if QUALITY of 50 is */ +/* selected, we will set the base layer to 50% the default size. */ +/* We let the other layers be computed internally. */ +/* -------------------------------------------------------------------- */ + kdu_long *layer_bytes; + double dfQuality = 20.0; + + layer_bytes = (kdu_long *) CPLCalloc(sizeof(kdu_long),layer_count); + + if( CSLFetchNameValue(papszOptions,"QUALITY") != NULL ) + { + dfQuality = CPLAtof(CSLFetchNameValue(papszOptions,"QUALITY")); + } + + if( dfQuality < 0.01 || dfQuality > 100.0 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "QUALITY=%s is not a legal value in the range 0.01-100.", + CSLFetchNameValue(papszOptions,"QUALITY") ); + CPLFree(layer_bytes); + return NULL; + } + + if( dfQuality < 99.5 ) + { + double dfLayerBytes = + (nXSize * ((double) nYSize) * dfQuality / 100.0); + + dfLayerBytes *= (GDALGetDataTypeSize(eType) / 8); + dfLayerBytes *= GDALGetRasterCount(poSrcDS); + + if( dfLayerBytes > 2000000000.0 && sizeof(kdu_long) == 4 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Trimmming maximum size of file 2GB from %.1fGB\n" + "to avoid overflow of kdu_long layer size.", + dfLayerBytes / 1000000000.0 ); + dfLayerBytes = 2000000000.0; + } + + layer_bytes[layer_count-1] = (kdu_long) dfLayerBytes; + + CPLDebug( "JP2KAK", "layer_bytes[] = %g\n", + (double) layer_bytes[layer_count-1] ); + } + else + bReversible = true; + +/* -------------------------------------------------------------------- */ +/* Do we want to use more than one tile? */ +/* -------------------------------------------------------------------- */ + if( nTileXSize > 25000 ) + { + // Don't generate tiles that are terrible wide by default, as + // they consume alot of memory for the compression engine. + nTileXSize = 20000; + } + + if( (nTileYSize / TILE_CHUNK_SIZE) > 253) + { + // We don't want to process a tile in more than 255 chunks as there + // is a limit on the number of tile parts in a tile and we are likely + // to flush out a tile part for each processing chunk. If we might + // go over try trimming our Y tile size such that we will get about + // 200 tile parts. + nTileYSize = 200 * TILE_CHUNK_SIZE; + } + + if( CSLFetchNameValue( papszOptions, "BLOCKXSIZE" ) != NULL ) + nTileXSize = atoi(CSLFetchNameValue( papszOptions, "BLOCKXSIZE")); + + if( CSLFetchNameValue( papszOptions, "BLOCKYSIZE" ) != NULL ) + nTileYSize = atoi(CSLFetchNameValue( papszOptions, "BLOCKYSIZE")); + +/* -------------------------------------------------------------------- */ +/* Avoid splitting into too many tiles - apparently limiting to */ +/* 64K tiles. There is a hard limit on the number of tiles */ +/* allowed in JPEG2000. */ +/* -------------------------------------------------------------------- */ + while( (double)nXSize*(double)nYSize + / (double)nTileXSize / (double)nTileYSize / 1024.0 >= 64.0 ) + { + nTileXSize *= 2; + nTileYSize *= 2; + } + + if( nTileXSize > nXSize ) nTileXSize = nXSize; + if( nTileYSize > nYSize ) nTileYSize = nYSize; + + CPLDebug( "JP2KAK", "Final JPEG2000 Tile Size is %dP x %dL.", + nTileXSize, nTileYSize ); + +/* -------------------------------------------------------------------- */ +/* Do we want a comment segment emitted? */ +/* -------------------------------------------------------------------- */ + bool bComseg; + + if( CSLFetchBoolean( papszOptions, "COMSEG", TRUE ) ) + bComseg = true; + else + bComseg = false; + +/* -------------------------------------------------------------------- */ +/* Work out the precision. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue( papszOptions, "NBITS" ) != NULL ) + nBits = atoi(CSLFetchNameValue(papszOptions,"NBITS")); + else if( poPrototypeBand->GetMetadataItem( "NBITS", "IMAGE_STRUCTURE" ) + != NULL ) + nBits = atoi(poPrototypeBand->GetMetadataItem( "NBITS", + "IMAGE_STRUCTURE" )); + else + nBits = GDALGetDataTypeSize(eType); + +/* -------------------------------------------------------------------- */ +/* Establish the general image parameters. */ +/* -------------------------------------------------------------------- */ + siz_params oSizeParams; + + oSizeParams.set( Scomponents, 0, 0, poSrcDS->GetRasterCount() ); + oSizeParams.set( Sdims, 0, 0, nYSize ); + oSizeParams.set( Sdims, 0, 1, nXSize ); + oSizeParams.set( Sprecision, 0, 0, nBits ); + if( eType == GDT_UInt16 || eType == GDT_Byte ) + oSizeParams.set( Ssigned, 0, 0, false ); + else + oSizeParams.set( Ssigned, 0, 0, true ); + + if( nTileXSize != nXSize || nTileYSize != nYSize ) + { + oSizeParams.set( Stiles, 0, 0, nTileYSize ); + oSizeParams.set( Stiles, 0, 1, nTileXSize ); + + CPLDebug( "JP2KAK", "Stiles=%d,%d", nTileYSize, nTileXSize ); + } + + kdu_params *poSizeRef = &oSizeParams; poSizeRef->finalize(); + +/* -------------------------------------------------------------------- */ +/* Open output file, and setup codestream. */ +/* -------------------------------------------------------------------- */ + jp2_family_tgt family; +#ifdef KAKADU_JPX + jpx_family_tgt jpx_family; + jpx_target jpx_out; + int bIsJPX = !EQUAL(CPLGetExtension(pszFilename),"jpf"); +#else + int bIsJPX = FALSE; +#endif + + kdu_compressed_target *poOutputFile = NULL; + jp2_target jp2_out; + int bIsJP2 = !EQUAL(CPLGetExtension(pszFilename),"jpc") + && !bIsJPX; + kdu_codestream oCodeStream; + + vsil_target oVSILTarget; + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + + try + { + oVSILTarget.open( pszFilename, "w" ); + + if( bIsJP2 ) + { + //family.open( pszFilename ); + family.open( &oVSILTarget ); + + jp2_out.open( &family ); + poOutputFile = &jp2_out; + } +#ifdef KAKADU_JPX + else if( bIsJPX ) + { + jpx_family.open( pszFilename ); + + jpx_out.open( &jpx_family ); + jpx_out.add_codestream(); + } +#endif + else + poOutputFile = &oVSILTarget; + + oCodeStream.create(&oSizeParams, poOutputFile ); + } + catch( ... ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we have a high res region of interest? */ +/* -------------------------------------------------------------------- */ + kdu_roi_image *poROIImage = NULL; + char **papszROIDefs = CSLFetchNameValueMultiple( papszOptions, "ROI" ); + int iROI; + + for( iROI = 0; papszROIDefs != NULL && papszROIDefs[iROI] != NULL; iROI++ ) + { + kdu_dims region; + char **papszTokens = CSLTokenizeStringComplex( papszROIDefs[iROI], ",", + FALSE, FALSE ); + + if( CSLCount(papszTokens) != 4 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Skipping corrupt ROI def = \n%s", + papszROIDefs[iROI] ); + continue; + } + + region.pos.x = atoi(papszTokens[0]); + region.pos.y = atoi(papszTokens[1]); + region.size.x = atoi(papszTokens[2]); + region.size.y = atoi(papszTokens[3]); + + CSLDestroy( papszTokens ); + + poROIImage = new kdu_roi_rect(oCodeStream,region); + } + CSLDestroy(papszROIDefs); + +/* -------------------------------------------------------------------- */ +/* Set some particular parameters. */ +/* -------------------------------------------------------------------- */ + oCodeStream.access_siz()->parse_string( + CPLString().Printf("Clayers=%d",layer_count).c_str()); + oCodeStream.access_siz()->parse_string("Cycc=no"); + if( eType == GDT_Int16 || eType == GDT_UInt16 ) + oCodeStream.access_siz()->parse_string("Qstep=0.0000152588"); + + if( bReversible ) + oCodeStream.access_siz()->parse_string("Creversible=yes"); + else + oCodeStream.access_siz()->parse_string("Creversible=no"); + +/* -------------------------------------------------------------------- */ +/* Set some user-overridable parameters. */ +/* -------------------------------------------------------------------- */ + int iParm; + const char *apszParms[] = + { "Corder", "PCRL", + "Cprecincts", "{512,512},{256,512},{128,512},{64,512},{32,512},{16,512},{8,512},{4,512},{2,512}", + "ORGgen_plt", "yes", + "ORGgen_tlm", NULL, + "Qguard", NULL, + "Cmodes", NULL, + "Clevels", NULL, + "Cblk", NULL, + "Rshift", NULL, + "Rlevels", NULL, + "Rweight", NULL, + "Sprofile", NULL, + NULL, NULL }; + + for( iParm = 0; apszParms[iParm] != NULL; iParm += 2 ) + { + const char *pszValue = + CSLFetchNameValue( papszOptions, apszParms[iParm] ); + + if( pszValue == NULL ) + pszValue = apszParms[iParm+1]; + + if( pszValue != NULL ) + { + CPLString osOpt; + + osOpt.Printf( "%s=%s", apszParms[iParm], pszValue ); + try + { + oCodeStream.access_siz()->parse_string( osOpt ); + } + catch( ... ) + { + CPLFree(layer_bytes); + if( bIsJP2 ) + { + jp2_out.close(); + family.close(); + } + else + { + poOutputFile->close(); + } + return NULL; + } + + CPLDebug( "JP2KAK", "parse_string(%s)", osOpt.c_str() ); + } + } + + oCodeStream.access_siz()->finalize_all(); + +/* -------------------------------------------------------------------- */ +/* Some JP2 specific parameters. */ +/* -------------------------------------------------------------------- */ + if( bIsJP2 ) + { + // Set dimensional information (all redundant with the SIZ marker segment) + jp2_dimensions dims = jp2_out.access_dimensions(); + dims.init(&oSizeParams); + + // Set colour space information (mandatory) + jp2_colour colour = jp2_out.access_colour(); + + if( bHaveCT || poSrcDS->GetRasterCount() == 3 ) + colour.init( JP2_sRGB_SPACE ); + else if( poSrcDS->GetRasterCount() >= 4 + && poSrcDS->GetRasterBand(4)->GetColorInterpretation() + == GCI_AlphaBand ) + { + colour.init( JP2_sRGB_SPACE ); + jp2_out.access_channels().init( 3 ); + jp2_out.access_channels().set_colour_mapping(0,0); + jp2_out.access_channels().set_colour_mapping(1,1); + jp2_out.access_channels().set_colour_mapping(2,2); + jp2_out.access_channels().set_opacity_mapping(0,3); + jp2_out.access_channels().set_opacity_mapping(1,3); + jp2_out.access_channels().set_opacity_mapping(2,3); + } + else if( poSrcDS->GetRasterCount() >= 2 + && poSrcDS->GetRasterBand(2)->GetColorInterpretation() + == GCI_AlphaBand ) + { + colour.init( JP2_sLUM_SPACE ); + jp2_out.access_channels().init( 1 ); + jp2_out.access_channels().set_colour_mapping(0,0); + jp2_out.access_channels().set_opacity_mapping(0,1); + } + else + colour.init( JP2_sLUM_SPACE ); + + // Resolution + if( poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION") != NULL + && poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION") != NULL + && poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT") != NULL ) + { + jp2_resolution res = jp2_out.access_resolution(); + double dfXRes = + CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION")); + double dfYRes = + CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION")); + + if( atoi(poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT")) == 2 ) + { + // convert pixels per inch to pixels per cm. + dfXRes = dfXRes * 39.37 / 100.0; + dfYRes = dfYRes * 39.37 / 100.0; + } + + // convert to pixels per meter. + dfXRes *= 100.0; + dfYRes *= 100.0; + + if( dfXRes != 0.0 && dfYRes != 0.0 ) + { + if( fabs(dfXRes/dfYRes - 1.0) > 0.00001 ) + res.init( dfYRes/dfXRes ); + else + res.init( 1.0 ); + res.set_resolution( dfXRes, true ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Write JP2 pseudocolor table if available. */ +/* -------------------------------------------------------------------- */ + if( bIsJP2 && bHaveCT ) + { + jp2_palette oJP2Palette; + GDALColorTable *poCT = poPrototypeBand->GetColorTable(); + int iColor, nCount = poCT->GetColorEntryCount(); + kdu_int32 *panLUT = (kdu_int32 *) + CPLMalloc(sizeof(kdu_int32) * nCount * 3); + + oJP2Palette = jp2_out.access_palette(); + oJP2Palette.init( 3, nCount ); + + for( iColor = 0; iColor < nCount; iColor++ ) + { + GDALColorEntry sEntry; + + poCT->GetColorEntryAsRGB( iColor, &sEntry ); + panLUT[iColor + nCount * 0] = sEntry.c1; + panLUT[iColor + nCount * 1] = sEntry.c2; + panLUT[iColor + nCount * 2] = sEntry.c3; + } + + oJP2Palette.set_lut( 0, panLUT + nCount * 0, 8, false ); + oJP2Palette.set_lut( 1, panLUT + nCount * 1, 8, false ); + oJP2Palette.set_lut( 2, panLUT + nCount * 2, 8, false ); + + CPLFree( panLUT ); + + jp2_channels oJP2Channels = jp2_out.access_channels(); + + oJP2Channels.init( 3 ); + oJP2Channels.set_colour_mapping( 0, 0, 0 ); + oJP2Channels.set_colour_mapping( 1, 0, 1 ); + oJP2Channels.set_colour_mapping( 2, 0, 2 ); + } + + if( bIsJP2 ) + { + jp2_out.write_header(); + } + +/* -------------------------------------------------------------------- */ +/* Set the GeoTIFF and GML boxes if georeferencing is available, */ +/* and this is a JP2 file. */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + if( bIsJP2 + && ((poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None + && (adfGeoTransform[0] != 0.0 + || adfGeoTransform[1] != 1.0 + || adfGeoTransform[2] != 0.0 + || adfGeoTransform[3] != 0.0 + || adfGeoTransform[4] != 0.0 + || ABS(adfGeoTransform[5]) != 1.0)) + || poSrcDS->GetGCPCount() > 0 + || poSrcDS->GetMetadata("RPC") != NULL) ) + { + GDALJP2Metadata oJP2MD; + + if( poSrcDS->GetGCPCount() > 0 ) + { + oJP2MD.SetProjection( poSrcDS->GetGCPProjection() ); + oJP2MD.SetGCPs( poSrcDS->GetGCPCount(), poSrcDS->GetGCPs() ); + } + else + { + oJP2MD.SetProjection( poSrcDS->GetProjectionRef() ); + oJP2MD.SetGeoTransform( adfGeoTransform ); + } + + oJP2MD.SetRPCMD( poSrcDS->GetMetadata("RPC") ); + + const char* pszAreaOrPoint = poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT); + oJP2MD.bPixelIsPoint = pszAreaOrPoint != NULL && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT); + + if( CSLFetchBoolean( papszOptions, "GMLJP2", TRUE ) ) + { + const char* pszGMLJP2V2Def = CSLFetchNameValue( papszOptions, "GMLJP2V2_DEF" ); + if( pszGMLJP2V2Def != NULL ) + JP2KAKWriteBox( &jp2_out, oJP2MD.CreateGMLJP2V2(nXSize,nYSize,pszGMLJP2V2Def,poSrcDS) ); + else + JP2KAKWriteBox( &jp2_out, oJP2MD.CreateGMLJP2(nXSize,nYSize) ); + } + if( CSLFetchBoolean( papszOptions, "GeoJP2", TRUE ) ) + JP2KAKWriteBox( &jp2_out, oJP2MD.CreateJP2GeoTIFF() ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have any XML boxes we want to preserve? */ +/* -------------------------------------------------------------------- */ + int iBox; + + for( iBox = 0; TRUE; iBox++ ) + { + CPLString oName; + char **papszMD; + + oName.Printf( "xml:BOX_%d", iBox ); + papszMD = poSrcDS->GetMetadata( oName ); + + if( papszMD == NULL || CSLCount(papszMD) != 1 ) + break; + + GDALJP2Box *poXMLBox = new GDALJP2Box(); + + poXMLBox->SetType( "xml " ); + poXMLBox->SetWritableData( strlen(papszMD[0])+1, + (GByte *) papszMD[0] ); + JP2KAKWriteBox( &jp2_out, poXMLBox ); + } + +/* -------------------------------------------------------------------- */ +/* Open codestream box. */ +/* -------------------------------------------------------------------- */ + if( bIsJP2 ) + jp2_out.open_codestream(); + +/* -------------------------------------------------------------------- */ +/* Create one big tile, and a compressing engine, and line */ +/* buffer for each component. */ +/* -------------------------------------------------------------------- */ + int iTileXOff, iTileYOff; + double dfPixelsDone = 0.0; + double dfPixelsTotal = nXSize * (double) nYSize; + + for( iTileYOff = 0; iTileYOff < nYSize; iTileYOff += nTileYSize ) + { + for( iTileXOff = 0; iTileXOff < nXSize; iTileXOff += nTileXSize ) + { + kdu_tile oTile = oCodeStream.open_tile( + kdu_coords(iTileXOff/nTileXSize,iTileYOff/nTileYSize)); + int nThisTileXSize, nThisTileYSize; + + // --------------------------------------------------------------- + // Is this a partial tile on the right or bottom? + if( iTileXOff + nTileXSize < nXSize ) + nThisTileXSize = nTileXSize; + else + nThisTileXSize = nXSize - iTileXOff; + + if( iTileYOff + nTileYSize < nYSize ) + nThisTileYSize = nTileYSize; + else + nThisTileYSize = nYSize - iTileYOff; + + // --------------------------------------------------------------- + // Setup scaled progress monitor + + void *pScaledProgressData; + double dfPixelsDoneAfter = + dfPixelsDone + (nThisTileXSize * (double) nThisTileYSize); + + pScaledProgressData = + GDALCreateScaledProgress( dfPixelsDone / dfPixelsTotal, + dfPixelsDoneAfter / dfPixelsTotal, + pfnProgress, pProgressData ); + if( !JP2KAKCreateCopy_WriteTile( poSrcDS, oTile, poROIImage, + iTileXOff, iTileYOff, + nThisTileXSize, nThisTileYSize, + bReversible, nBits, eType, + oCodeStream, bFlushEnabled, + layer_bytes, layer_count, + GDALScaledProgress, + pScaledProgressData, bComseg ) ) + { + GDALDestroyScaledProgress( pScaledProgressData ); + + oCodeStream.destroy(); + poOutputFile->close(); + VSIUnlink( pszFilename ); + return NULL; + } + + GDALDestroyScaledProgress( pScaledProgressData ); + dfPixelsDone = dfPixelsDoneAfter; + + oTile.close(); + } + } + +/* -------------------------------------------------------------------- */ +/* Finish flushing out results. */ +/* -------------------------------------------------------------------- */ + oCodeStream.flush(layer_bytes, layer_count, NULL, true, bComseg ); + oCodeStream.destroy(); + + CPLFree( layer_bytes ); + + if( bIsJP2 ) + { + jp2_out.close(); + family.close(); + } + else + { + poOutputFile->close(); + } + + oVSILTarget.close(); + + if( !pfnProgress( 1.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); + GDALPamDataset *poDS = (GDALPamDataset*) JP2KAKDataset::Open(&oOpenInfo); + + if( poDS ) + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_JP2KAK() */ +/************************************************************************/ + +void GDALRegister_JP2KAK() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("JP2KAK driver")) + return; + + if( GDALGetDriverByName( "JP2KAK" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "JP2KAK" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "JPEG-2000 (based on Kakadu " + KDU_CORE_VERSION ")" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_jp2kak.html" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16" ); + poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/jp2" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jp2" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); + + poDriver->pfnOpen = JP2KAKDataset::Open; + poDriver->pfnIdentify = JP2KAKDataset::Identify; + poDriver->pfnCreateCopy = JP2KAKCreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/jp2kak/makefile.vc b/bazaar/plugin/gdal/frmts/jp2kak/makefile.vc new file mode 100644 index 000000000..db4e524bb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jp2kak/makefile.vc @@ -0,0 +1,37 @@ + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +KAKINC = -I$(KAKSRC)/managed/all_includes \ + -I$(KAKSRC)/apps/jp2 +OBJ_EXT = obj +OBJ_PREFIX = $(KAKDIR)\v6_generated_x86 +OBJ_CONFIG = release + +KAK_APPS_OBJ = \ + $(OBJ_PREFIX)\compress\$(OBJ_CONFIG)\args.obj \ + $(OBJ_PREFIX)\compress\$(OBJ_CONFIG)\image_in.obj \ + $(OBJ_PREFIX)\expand\$(OBJ_CONFIG)\image_out.obj \ + $(OBJ_PREFIX)\compress\$(OBJ_CONFIG)\jp2.obj \ + $(OBJ_PREFIX)\merge\$(OBJ_CONFIG)\mj2.obj \ + $(OBJ_PREFIX)\compress\$(OBJ_CONFIG)\roi_sources.obj \ + $(OBJ_PREFIX)\compress\$(OBJ_CONFIG)\palette.obj \ + $(OBJ_PREFIX)\buffered_expand\$(OBJ_CONFIG)\kdu_stripe_decompressor.obj \ + $(OBJ_PREFIX)\render\$(OBJ_CONFIG)\kdu_region_decompressor.obj \ + $(OBJ_PREFIX)\compress\$(OBJ_CONFIG)\kdu_tiff.obj \ + $(OBJ_PREFIX)\compress\$(OBJ_CONFIG)\jpx.obj + +OBJ = jp2kakdataset.obj +EXTRAFLAGS = $(KAKINC) /DKDU_PENTIUM_MSVC /EHsc + +default: $(OBJ) kakinstall + xcopy /D /Y *.obj ..\o + +kakinstall: + for %d in ( $(KAK_APPS_OBJ) ) do \ + xcopy /D /Y %d ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/jp2kak/subfile_source.h b/bazaar/plugin/gdal/frmts/jp2kak/subfile_source.h new file mode 100644 index 000000000..731e226d5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jp2kak/subfile_source.h @@ -0,0 +1,189 @@ +/****************************************************************************** + * $Id: subfile_source.h 27787 2014-10-03 01:08:03Z rcoup $ + * + * Project: JPEG-2000 + * Purpose: Implements read-only virtual io on a subregion of a file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "kdu_file_io.h" +#include "cpl_error.h" +#include "cpl_vsi_virtual.h" + +#if KDU_MAJOR_VERSION > 7 || (KDU_MAJOR_VERSION == 7 && KDU_MINOR_VERSION >= 5) + using namespace kdu_core; + using namespace kdu_supp; +#endif + +#define IO_CHUNK_SIZE 65536L +#define IO_BUFFER_SIZE 1048576L +/************************************************************************/ +/* subfile_source */ +/************************************************************************/ + +class subfile_source : public kdu_compressed_source { + + public: + subfile_source() { file = NULL; } + ~subfile_source() { close(); } + + + bool exists() { return (file != NULL); } + + bool operator!() { return (file == NULL); } + + void open(const char *fname, int bSequential, int bCached ) + { + const char *real_filename; + close(); + + if( EQUALN( fname, "J2K_SUBFILE:",12) ) + { + char** papszTokens = CSLTokenizeString2(fname + 12, ",", 0); + if (CSLCount(papszTokens) >= 2) + { + subfile_offset = (int) CPLScanUIntBig(papszTokens[0], strlen(papszTokens[0])); + subfile_size = (int) CPLScanUIntBig(papszTokens[1], strlen(papszTokens[1])); + } + else + { + kdu_error e; + + e << "Corrupt subfile definition:" << fname; + return; + } + CSLDestroy(papszTokens); + + real_filename = strstr(fname,","); + if( real_filename != NULL ) + real_filename = strstr(real_filename+1,","); + if( real_filename != NULL ) + real_filename++; + else + { + kdu_error e; + + e << "Could not find filename in subfile definition." << fname; + return; + } + } + else + { + real_filename = fname; + subfile_offset = 0; + subfile_size = 0; + } + + file = VSIFOpenL( real_filename, "r"); + if( file == NULL ) + { + kdu_error e; + e << "Unable to open compressed data file, \"" << + real_filename << "\"!"; + return; + } + + if ( bCached ) + { + file = (VSILFILE*)VSICreateCachedFile( (VSIVirtualHandle*)file, IO_CHUNK_SIZE, IO_BUFFER_SIZE ); + if( file == NULL ) + { + kdu_error e; + e << "Unable to open compressed data file, \"" << + real_filename << "\"!"; + return; + } + } + + if( bSequential ) + capabilities = KDU_SOURCE_CAP_SEQUENTIAL; + else + capabilities = KDU_SOURCE_CAP_SEQUENTIAL | KDU_SOURCE_CAP_SEEKABLE; + + seek_origin = subfile_offset; + seek( 0 ); + } + + int get_capabilities() { return capabilities; } + + bool seek(kdu_long offset) + { + assert(file != NULL); + if( file == NULL ) + return false; + + if (!(capabilities & KDU_SOURCE_CAP_SEEKABLE)) + return false; + + if( VSIFSeekL( file, seek_origin+offset, SEEK_SET ) == 0 ) + return true; + else + return false; + } + + bool set_seek_origin(kdu_long position) + { + if (!(capabilities & KDU_SOURCE_CAP_SEEKABLE)) + return false; + seek_origin = position + subfile_offset; + return true; + } + + kdu_long get_pos(bool absolute) + { + if (file == NULL) return -1; + kdu_long result = VSIFTellL( file ); + if (!absolute) + result -= seek_origin; + else + result -= subfile_offset; + return result; + } + + int read(kdu_byte *buf, int num_bytes) + { + assert(file != NULL); + + num_bytes = VSIFReadL(buf,1,(size_t) num_bytes,file); + return num_bytes; + } + + bool close() + { + if (file != NULL) + VSIFCloseL( file ); + file = NULL; + return true; + } + + private: // Data + int capabilities; + kdu_long seek_origin; + + int subfile_offset; + int subfile_size; + + VSILFILE *file; + }; diff --git a/bazaar/plugin/gdal/frmts/jp2kak/vsil_target.h b/bazaar/plugin/gdal/frmts/jp2kak/vsil_target.h new file mode 100644 index 000000000..0ae0daa47 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jp2kak/vsil_target.h @@ -0,0 +1,101 @@ +/****************************************************************************** + * $Id: subfile_source.h 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: JPEG-2000 + * Purpose: Implements VSI*L based writer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "kdu_file_io.h" +#include "cpl_error.h" +#include "cpl_vsi.h" + +/************************************************************************/ +/* vsil_target */ +/************************************************************************/ + +class vsil_target : public kdu_compressed_target { + +public: + vsil_target() { file = NULL; } + ~vsil_target() { close(); } + + void open(const char *fname, const char *access ) + { + close(); + file = VSIFOpenL( fname, access ); + if( file == NULL ) + { + kdu_error e; + e << "Unable to open compressed data file, \"" << + fname << "\"!"; + return; + } + } + + bool write(const kdu_byte *buf, int num_bytes) + { + if( file == NULL ) + return false; + + if( (int) VSIFWriteL( buf, 1, num_bytes, file ) != num_bytes ) + return false; + else + return true; + } + + bool start_rewrite(kdu_long backtrack) + { + if( file == NULL ) + return false; + + if( VSIFSeekL( file, VSIFTellL(file)-backtrack, SEEK_SET ) != 0 ) + return false; + else + return true; + } + + bool end_rewrite() + { + if( file == NULL ) + return false; + + if( VSIFSeekL( file, 0, SEEK_END ) != 0 ) + return false; + else + return true; + } + + bool close() + { + if (file != NULL) + VSIFCloseL( file ); + file = NULL; + return true; + } + +private: // Data + VSILFILE *file; +}; + diff --git a/bazaar/plugin/gdal/frmts/jpeg/GNUmakefile b/bazaar/plugin/gdal/frmts/jpeg/GNUmakefile new file mode 100644 index 000000000..7a40ade23 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/GNUmakefile @@ -0,0 +1,68 @@ + +include ../../GDALmake.opt + +ifeq ($(JPEG_SETTING),internal) +OBJ = \ + jcapimin.o jcapistd.o jccoefct.o jccolor.o jcdctmgr.o jchuff.o \ + jcinit.o jcmainct.o jcmarker.o jcmaster.o jcomapi.o jcparam.o \ + jcphuff.o jcprepct.o jcsample.o jctrans.o jdapimin.o jdapistd.o \ + jdatadst.o jdatasrc.o jdcoefct.o jdcolor.o jddctmgr.o jdhuff.o \ + jdinput.o jdmainct.o jdmarker.o jdmaster.o jdmerge.o jdphuff.o \ + jdpostct.o jdsample.o jdtrans.o jerror.o jfdctflt.o jfdctfst.o \ + jfdctint.o jidctflt.o jidctfst.o jidctint.o jidctred.o jquant1.o \ + jquant2.o jutils.o jmemmgr.o jmemansi.o \ + \ + jpgdataset.o vsidataio.o +XTRA_OPT = -Ilibjpeg -DDEFAULT_MAX_MEM=500000000L +else +OBJ = jpgdataset.o vsidataio.o +XTRA_OPT = +endif + +ifeq ($(JPEG12_ENABLED),yes) +XTRA_OPT_12 = -Ilibjpeg12 -DDEFAULT_MAX_MEM=500000000L +XTRA_OPT := -DJPEG_DUAL_MODE_8_12 $(XTRA_OPT) +OBJ := jcapimin12.o jcapistd12.o jccoefct12.o jccolor12.o jcdctmgr12.o jchuff12.o \ + jcinit12.o jcmainct12.o jcmarker12.o jcmaster12.o jcomapi12.o jcparam12.o \ + jcphuff12.o jcprepct12.o jcsample12.o jctrans12.o jdapimin12.o jdapistd12.o \ + jdatadst12.o jdatasrc12.o jdcoefct12.o jdcolor12.o jddctmgr12.o jdhuff12.o \ + jdinput12.o jdmainct12.o jdmarker12.o jdmaster12.o jdmerge12.o jdphuff12.o \ + jdpostct12.o jdsample12.o jdtrans12.o jerror12.o jfdctflt12.o jfdctfst12.o \ + jfdctint12.o jidctflt12.o jidctfst12.o jidctint12.o jidctred12.o jquant112.o \ + jquant212.o jutils12.o jmemmgr12.o jmemansi12.o \ + $(OBJ) jpgdataset_12.o +endif + +CPPFLAGS := $(XTRA_OPT) $(CPPFLAGS) -I../mem + +default: install-obj + +$(O_OBJ): jpgdataset.cpp + +clean: + rm -f *.o $(O_OBJ) libjpeg12/*.c libjpeg12/*.h + +../o/%.$(OBJ_EXT): libjpeg/%.c + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +../o/%.$(OBJ_EXT): libjpeg12/%.c + $(CC) -c $(XTRA_OPT_12) $(CPPFLAGS) $(CFLAGS) $< -o $@ + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +import: + mv libjpeg/jmorecfg.h libjpeg/jmorecfg.h.safe + mv libjpeg/jconfig.h libjpeg/jconfig.h.safe + (cd libjpeg; copymatch.sh ~/libjpeg *.c *.h) + mv libjpeg/jmorecfg.h.safe libjpeg/jmorecfg.h + mv libjpeg/jconfig.h.safe libjpeg/jconfig.h + +libjpeg12/jcapimin12.c: libjpeg/jcapimin.c + cp libjpeg/*.h libjpeg12 + cp libjpeg12/jmorecfg.h.12 libjpeg12/jmorecfg.h + for x in libjpeg/*.c ; do \ + b=`basename $$x .c`; \ + cp $$x libjpeg12/$${b}12.c; \ + done diff --git a/bazaar/plugin/gdal/frmts/jpeg/frmt_jpeg.html b/bazaar/plugin/gdal/frmts/jpeg/frmt_jpeg.html new file mode 100644 index 000000000..fe8d5460b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/frmt_jpeg.html @@ -0,0 +1,149 @@ + + +JPEG -- JPEG JFIF File Format + + + + +

    JPEG -- JPEG JFIF File Format

    + +The JPEG JFIF format is supported for reading, and batch writing, but not +update in place. JPEG files are represented as one band (greyscale) or three +band (RGB) datasets with Byte valued bands.

    + +The driver will automatically convert images whose color space is YCbCr, CMYK or +YCbCrK to RGB, unless GDAL_JPEG_TO_RGB is set to NO (YES is the default). When +color space translation to RGB is done, the source color space is indicated in the +SOURCE_COLOR_SPACE metedata of the IMAGE_STRUCTURE domain.

    + +There is currently no support for georeferencing information or metadata for +JPEG files. But if an ESRI world file exists with the .jgw, .jpgw/.jpegw or +.wld suffixes, it will be read and used to establish the geotransform for +the image. If available a MapInfo .tab file will also be used for +georeferencing. Overviews can be built for JPEG files as an external +.ovr file.

    + +The driver also supports the "zlib compressed mask appended to the file" +approach used by a few data providers to add a bitmask to identify pixels that +are not valid data. +See RFC 15 for further details.

    + +Starting with GDAL 1.10.1, the driver can deal with bitmask where the bits are +ordered with most significant bit first (whereas the usual convention is least significant +bit first). The driver will try to autodetect that situation, but the heuristics +may fail. In that circumstance, you can set the JPEG_MASK_BIT_ORDER configuration option to +MSB. Bitmask can also be completely ignored by specifying JPEG_READ_MASK to NO.

    + +The GDAL JPEG Driver is built using the Independent JPEG Group's jpeg +library. Also note that the GeoTIFF driver supports tiled TIFF with JPEG +compressed tiles.

    + +To be able to read and write JPEG images with 12-bit sample, you can build GDAL +with its internal libjpeg (based on IJG libjpeg-6b, with additional changes for 12-bit +sample support), or explicitly pass --with-jpeg12=yes to configure script when building +with external libjpeg. See +"8 and 12 bit JPEG in TIFF" wiki page for more details.

    + +It is also possible to use the JPEG driver with the libjpeg-turbo, a +version of libjpeg, API and ABI compatible with IJG libjpeg-6b, which uses MMX, SSE, +and SSE2 SIMD instructions to accelerate baseline JPEG compression/decompression.

    + +Starting with GDAL 1.9.0, XMP metadata can be extracted from the file, and will be +stored as XML raw content in the xml:XMP metadata domain.

    + +Starting with GDAL 2.0, embedded EXIF thumbnails (with JPEG compression) can be +used as overviews, and generated by GDAL.

    + +

    Color Profile Metadata

    + +

    Starting with GDAL 1.11, GDAL can deal with the following color profile metadata in the COLOR_PROFILE domain:

    +
      +
    • SOURCE_ICC_PROFILE (Base64 encoded ICC profile embedded in file.)
    • +
    + +

    Note that this metadata property can only be used on the original raw pixel data. If automatic conversion to RGB has been done, the color profile information cannot be used.

    + +

    This metadata tag can be used as creation options.

    + +

    Error management

    + +While decoding, libjpeg has resiliency towards some errors in the JPEG datastream and will +try to recover from them as much of possible. Starting with GDAL 1.11.2, +such errors will be reported as GDAL Warnings, but can optionaly be considered +as true Errors by setting the GDAL_ERROR_ON_LIBJPEG_WARNING configuration option +to TRUE. + +

    Creation Options

    + +JPEG files are created using the "JPEG" driver code. Only Byte band types +are supported, and only 1 and 3 band (RGB) configurations. JPEG file creation +is implemented by the batch (CreateCopy) method. YCbCr, CMYK or YCbCrK colorspaces +are not supported in creation. If the source dataset has a nodata mask, it will be +appended as a zlib compressed mask to the JPEG file.

    + +

      + +
    • WORLDFILE=YES: Force the generation of an associated ESRI world +file (with the extension .wld). +

      + +

    • QUALITY=n: By default the quality flag is set to 75, but this +option can be used to select other values. Values must be in the +range 10-100. Low values result in higher compression ratios, but poorer +image quality. Values above 95 are not meaningfully better quality but +can but substantially larger.

      + +

    • PROGRESSIVE=ON: Enabled generation of progressive JPEGs. In some +cases these will display a reduced resolution image in viewers such as +Netscape, and Internet Explorer, before the full file has been downloaded. +However, some applications cannot read progressive JPEGs at all. GDAL can +read progressive JPEGs, but takes no advantage of their progressive nature.

      + +

    • INTERNAL_MASK=YES/NO: By default, if needed, an internal mask +in the "zlib compressed mask appended to the file" approach is written +to identify pixels that are not valid data. Starting with GDAL 1.10, this +can be disabled by setting this option to NO.

      + +

    • ARITHMETIC=YES/NO: (Starting with GDAL 1.10) To enable arithmetic coding. +Not enabled in all libjpeg builds, because of possible legal restrictions.

      + +

    • BLOCK=1...16: (Starting with GDAL 1.10 and libjpeg 8c) DCT block size. +All values from 1 to 16 are possible. Default is 8 (baseline format). A value other +than 8 will produce files incompatible with versions prior to libjpeg 8c.

      + +

    • COLOR_TRANSFORM=RGB or RGB1: (Starting with GDAL 1.10 and libjpeg 9). +Set to RGB1 for lossless RGB. Note: this will produce files incompatible with +versions prior to libjpeg 9.

      + +

    • SOURCE_ICC_PROFILE=value: (Starting with GDAL 1.11). +ICC profile encoded in Base64.

      + +

    • COMMENT=string: (Starting with GDAL 2.0). String to embed in a +comment JPEG marker. When reading, such strings are exposed in the COMMENT +metadata item.

      + +

    • EXIF_THUMBNAIL=YES/NO: (Starting with GDAL 2.0). Whether to generate +an EXIF thumbnail(overview), itself JPEG compressed. Defaults to NO. If enabled, +the maximum dimension of the thumbnail will be 128, if neither THUMBNAIL_WIDTH nor +THUMBNAIL_HEIGHT are specified.

      + +

    • THUMBNAIL_WIDTH=n: (Starting with GDAL 2.0). Width of thumbnail. Only +taken into account if EXIF_THUMBNAIL=YES.

      + +

    • THUMBNAIL_HEIGHT=n: (Starting with GDAL 2.0). Height of thumbnail. Only +taken into account if EXIF_THUMBNAIL=YES.

      + +

    + +

    See Also:

    +

    + +

    + + + + diff --git a/bazaar/plugin/gdal/frmts/jpeg/jpgdataset.cpp b/bazaar/plugin/gdal/frmts/jpeg/jpgdataset.cpp new file mode 100644 index 000000000..60edbfff8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/jpgdataset.cpp @@ -0,0 +1,3740 @@ +/****************************************************************************** + * $Id: jpgdataset.cpp 29267 2015-05-29 20:47:00Z rouault $ + * + * Project: JPEG JFIF Driver + * Purpose: Implement GDAL JPEG Support based on IJG libjpeg. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Portions Copyright (c) Her majesty the Queen in right of Canada as + * represented by the Minister of National Defence, 2006. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "gdalexif.h" +#include "memdataset.h" + +#include + +#define TIFF_VERSION 42 + +#define TIFF_BIGENDIAN 0x4d4d +#define TIFF_LITTLEENDIAN 0x4949 + +#define JPEG_TIFF_IMAGEWIDTH 0x100 +#define JPEG_TIFF_IMAGEHEIGHT 0x101 +#define JPEG_TIFF_COMPRESSION 0x103 +#define JPEG_EXIF_JPEGIFOFSET 0x201 +#define JPEG_EXIF_JPEGIFBYTECOUNT 0x202 +/* + * TIFF header. + */ +typedef struct { + GUInt16 tiff_magic; /* magic number (defines byte order) */ + GUInt16 tiff_version; /* TIFF version number */ + GUInt32 tiff_diroff; /* byte offset to first directory */ +} TIFFHeader; + +CPL_CVSID("$Id: jpgdataset.cpp 29267 2015-05-29 20:47:00Z rouault $"); + +CPL_C_START +#ifdef LIBJPEG_12_PATH +# include LIBJPEG_12_PATH +#else +# include "jpeglib.h" +#endif +CPL_C_END + +// we believe it is ok to use setjmp() in this situation. +#ifdef _MSC_VER +# pragma warning(disable:4611) +#endif + +#if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset) +GDALDataset* JPEGDataset12Open(const char* pszFilename, + VSILFILE* fpLin, + char** papszSiblingFiles, + int nScaleFactor, + int bDoPAMInitialize); +GDALDataset* JPEGDataset12CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); +#endif + +CPL_C_START +void GDALRegister_JPEG(void); +CPL_C_END + +#include "vsidataio.h" + +/* +* Do we want to do special processing suitable for when JSAMPLE is a +* 16bit value? +*/ +#if defined(JPEG_LIB_MK1) +# define JPEG_LIB_MK1_OR_12BIT 1 +#elif BITS_IN_JSAMPLE == 12 +# define JPEG_LIB_MK1_OR_12BIT 1 +#endif + +class JPGDatasetCommon; +GDALRasterBand* JPGCreateBand(JPGDatasetCommon* poDS, int nBand); + +CPLErr JPGAppendMask( const char *pszJPGFilename, GDALRasterBand *poMask, + GDALProgressFunc pfnProgress, void * pProgressData ); +void JPGAddEXIFOverview( GDALDataType eWorkDT, + GDALDataset* poSrcDS, char** papszOptions, + j_compress_ptr cinfo, + void (*p_jpeg_write_m_header) (j_compress_ptr cinfo, int marker, unsigned int datalen), + void (*p_jpeg_write_m_byte) (j_compress_ptr cinfo, int val), + GDALDataset *(pCreateCopy)( const char *, GDALDataset *, + int, char **, + GDALProgressFunc pfnProgress, + void * pProgressData ) ); +void JPGAddICCProfile( struct jpeg_compress_struct *pInfo, + const char *pszICCProfile, + void (*p_jpeg_write_m_header) (j_compress_ptr cinfo, int marker, unsigned int datalen), + void (*p_jpeg_write_m_byte) (j_compress_ptr cinfo, int val)); + +typedef struct +{ + jmp_buf setjmp_buffer; + int bNonFatalErrorEncountered; + void (*p_previous_emit_message)(j_common_ptr cinfo, int msg_level); +} GDALJPEGErrorStruct; + +/************************************************************************/ +/* ==================================================================== */ +/* JPGDatasetCommon */ +/* ==================================================================== */ +/************************************************************************/ + +class JPGRasterBand; +class JPGMaskBand; + +class JPGDatasetCommon : public GDALPamDataset +{ +protected: + friend class JPGRasterBand; + friend class JPGMaskBand; + + GDALJPEGErrorStruct sErrorStruct; + int ErrorOutOnNonFatalError(); + + int nScaleFactor; + int bHasInitInternalOverviews; + int nInternalOverviewsCurrent; + int nInternalOverviewsToFree; + GDALDataset** papoInternalOverviews; + void InitInternalOverviews(); + GDALDataset* InitEXIFOverview(); + + char *pszProjection; + int bGeoTransformValid; + double adfGeoTransform[6]; + int nGCPCount; + GDAL_GCP *pasGCPList; + + VSILFILE *fpImage; + GUIntBig nSubfileOffset; + + int nLoadedScanline; + GByte *pabyScanline; + + int bHasReadEXIFMetadata; + int bHasReadXMPMetadata; + int bHasReadICCMetadata; + char **papszMetadata; + char **papszSubDatasets; + int bigendian; + int nExifOffset; + int nInterOffset; + int nGPSOffset; + int bSwabflag; + int nTiffDirStart; + int nTIFFHEADER; + int bHasDoneJpegCreateDecompress; + int bHasDoneJpegStartDecompress; + + virtual CPLErr LoadScanline(int) = 0; + virtual CPLErr Restart() = 0; + + virtual int GetDataPrecision() = 0; + virtual int GetOutColorSpace() = 0; + + int EXIFInit(VSILFILE *); + void ReadICCProfile(); + + void CheckForMask(); + void DecompressMask(); + + void ReadEXIFMetadata(); + void ReadXMPMetadata(); + + int bHasCheckedForMask; + JPGMaskBand *poMaskBand; + GByte *pabyBitMask; + int bMaskLSBOrder; + + GByte *pabyCMask; + int nCMaskSize; + + J_COLOR_SPACE eGDALColorSpace; /* color space exposed by GDAL. Not necessarily the in_color_space nor */ + /* the out_color_space of JPEG library */ + + int bIsSubfile; + int bHasTriedLoadWorldFileOrTab; + void LoadWorldFileOrTab(); + CPLString osWldFilename; + + virtual int CloseDependentDatasets(); + + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + + public: + JPGDatasetCommon(); + ~JPGDatasetCommon(); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ); + + virtual CPLErr GetGeoTransform( double * ); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + + virtual char **GetFileList(void); + + virtual void FlushCache(void); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + + static void EmitMessage(j_common_ptr cinfo, int msg_level); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JPGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class JPGDataset : public JPGDatasetCommon +{ + struct jpeg_decompress_struct sDInfo; + struct jpeg_error_mgr sJErr; + + virtual CPLErr LoadScanline(int); + virtual CPLErr Restart(); + virtual int GetDataPrecision() { return sDInfo.data_precision; } + virtual int GetOutColorSpace() { return sDInfo.out_color_space; } + + int nQLevel; +#if !defined(JPGDataset) + void LoadDefaultTables(int); +#endif + void SetScaleNumAndDenom(); + + public: + JPGDataset(); + ~JPGDataset(); + + static GDALDataset *Open( const char* pszFilename, + VSILFILE* fpLin, + char** papszSiblingFiles, + int nScaleFactor, + int bDoPAMInitialize ); + static GDALDataset* CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + + static void ErrorExit(j_common_ptr cinfo); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JPGRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class JPGRasterBand : public GDALPamRasterBand +{ + friend class JPGDatasetCommon; + + /* We have to keep a pointer to the JPGDataset that this JPGRasterBand + belongs to. In some case, we may have this->poGDS != this->poDS + For example for a JPGRasterBand that is set to a NITFDataset... + In other words, this->poDS doesn't necessary point to a JPGDataset + See ticket #1807. + */ + JPGDatasetCommon *poGDS; + + public: + + JPGRasterBand( JPGDatasetCommon *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); + + virtual GDALRasterBand *GetMaskBand(); + virtual int GetMaskFlags(); + + virtual GDALRasterBand *GetOverview(int i); + virtual int GetOverviewCount(); +}; + +#if !defined(JPGDataset) + +/************************************************************************/ +/* ==================================================================== */ +/* JPGMaskBand */ +/* ==================================================================== */ +/************************************************************************/ + +class JPGMaskBand : public GDALRasterBand +{ + protected: + virtual CPLErr IReadBlock( int, int, void * ); + + public: + JPGMaskBand( JPGDataset *poDS ); +}; + +/************************************************************************/ +/* ReadEXIFMetadata() */ +/************************************************************************/ +void JPGDatasetCommon::ReadEXIFMetadata() +{ + if (bHasReadEXIFMetadata) + return; + + CPLAssert(papszMetadata == NULL); + + /* Save current position to avoid disturbing JPEG stream decoding */ + vsi_l_offset nCurOffset = VSIFTellL(fpImage); + + if( EXIFInit(fpImage) ) + { + EXIFExtractMetadata(papszMetadata, + fpImage, nTiffDirStart, + bSwabflag, nTIFFHEADER, + nExifOffset, nInterOffset, nGPSOffset); + + if(nExifOffset > 0){ + EXIFExtractMetadata(papszMetadata, + fpImage, nExifOffset, + bSwabflag, nTIFFHEADER, + nExifOffset, nInterOffset, nGPSOffset); + } + if(nInterOffset > 0) { + EXIFExtractMetadata(papszMetadata, + fpImage, nInterOffset, + bSwabflag, nTIFFHEADER, + nExifOffset, nInterOffset, nGPSOffset); + } + if(nGPSOffset > 0) { + EXIFExtractMetadata(papszMetadata, + fpImage, nGPSOffset, + bSwabflag, nTIFFHEADER, + nExifOffset, nInterOffset, nGPSOffset); + } + + /* Avoid setting the PAM dirty bit just for that */ + int nOldPamFlags = nPamFlags; + + /* Append metadata from PAM after EXIF metadata */ + papszMetadata = CSLMerge(papszMetadata, GDALPamDataset::GetMetadata()); + SetMetadata( papszMetadata ); + + nPamFlags = nOldPamFlags; + } + + VSIFSeekL( fpImage, nCurOffset, SEEK_SET ); + + bHasReadEXIFMetadata = TRUE; +} + +/************************************************************************/ +/* ReadXMPMetadata() */ +/************************************************************************/ + +/* See §2.1.3 of http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf */ + +void JPGDatasetCommon::ReadXMPMetadata() +{ + if (bHasReadXMPMetadata) + return; + + /* Save current position to avoid disturbing JPEG stream decoding */ + vsi_l_offset nCurOffset = VSIFTellL(fpImage); + +/* -------------------------------------------------------------------- */ +/* Search for APP1 chunk. */ +/* -------------------------------------------------------------------- */ + GByte abyChunkHeader[2+2+29]; + int nChunkLoc = 2; + int bFoundXMP = FALSE; + + for( ; TRUE; ) + { + if( VSIFSeekL( fpImage, nChunkLoc, SEEK_SET ) != 0 ) + break; + + if( VSIFReadL( abyChunkHeader, sizeof(abyChunkHeader), 1, fpImage ) != 1 ) + break; + + nChunkLoc += 2 + abyChunkHeader[2] * 256 + abyChunkHeader[3]; + // COM marker + if( abyChunkHeader[0] == 0xFF && abyChunkHeader[1] == 0xFE ) + continue; + + if( abyChunkHeader[0] != 0xFF + || (abyChunkHeader[1] & 0xf0) != 0xe0 ) + break; // Not an APP chunk. + + if( abyChunkHeader[1] == 0xe1 + && strncmp((const char *) abyChunkHeader + 4,"http://ns.adobe.com/xap/1.0/",28) == 0 ) + { + bFoundXMP = TRUE; + break; // APP1 - XMP + } + } + + if (bFoundXMP) + { + int nXMPLength = abyChunkHeader[2] * 256 + abyChunkHeader[3]; + if (nXMPLength > 2 + 29) + { + char* pszXMP = (char*)VSIMalloc(nXMPLength - 2 - 29 + 1); + if (pszXMP) + { + if (VSIFReadL( pszXMP, nXMPLength - 2 - 29, 1, fpImage ) == 1) + { + pszXMP[nXMPLength - 2 - 29] = '\0'; + + /* Avoid setting the PAM dirty bit just for that */ + int nOldPamFlags = nPamFlags; + + char *apszMDList[2]; + apszMDList[0] = pszXMP; + apszMDList[1] = NULL; + SetMetadata(apszMDList, "xml:XMP"); + + nPamFlags = nOldPamFlags; + } + VSIFree(pszXMP); + } + } + } + + VSIFSeekL( fpImage, nCurOffset, SEEK_SET ); + + bHasReadXMPMetadata = TRUE; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **JPGDatasetCommon::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "xml:XMP", "COLOR_PROFILE", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ +char **JPGDatasetCommon::GetMetadata( const char * pszDomain ) +{ + if (fpImage == NULL) + return NULL; + if (eAccess == GA_ReadOnly && !bHasReadEXIFMetadata && + (pszDomain == NULL || EQUAL(pszDomain, ""))) + ReadEXIFMetadata(); + if (eAccess == GA_ReadOnly && !bHasReadXMPMetadata && + pszDomain != NULL && EQUAL(pszDomain, "xml:XMP")) + ReadXMPMetadata(); + if (eAccess == GA_ReadOnly && !bHasReadICCMetadata && + pszDomain != NULL && EQUAL(pszDomain, "COLOR_PROFILE")) + ReadICCProfile(); + return GDALPamDataset::GetMetadata(pszDomain); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ +const char *JPGDatasetCommon::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if (fpImage == NULL) + return NULL; + if (eAccess == GA_ReadOnly && !bHasReadEXIFMetadata && + (pszDomain == NULL || EQUAL(pszDomain, "")) && + pszName != NULL && + (EQUAL(pszName, "COMMENT") || EQUALN(pszName, "EXIF_", 5))) + ReadEXIFMetadata(); + if (eAccess == GA_ReadOnly && !bHasReadICCMetadata && + pszDomain != NULL && EQUAL(pszDomain, "COLOR_PROFILE")) + ReadICCProfile(); + return GDALPamDataset::GetMetadataItem(pszName, pszDomain); +} + +/************************************************************************/ +/* ReadICCProfile() */ +/* */ +/* Read ICC Profile from APP2 data */ +/************************************************************************/ +void JPGDatasetCommon::ReadICCProfile() +{ + if (bHasReadICCMetadata) + return; + bHasReadICCMetadata = TRUE; + + vsi_l_offset nCurOffset = VSIFTellL(fpImage); + + int nTotalSize = 0; + int nChunkCount = -1; + int anChunkSize[256]; + char *apChunk[256]; + + memset(anChunkSize, 0, 256 * sizeof(int)); + memset(apChunk, 0, 256 * sizeof(char*)); + +/* -------------------------------------------------------------------- */ +/* Search for APP2 chunk. */ +/* -------------------------------------------------------------------- */ + GByte abyChunkHeader[18]; + int nChunkLoc = 2; + bool bOk = true; + + for( ; TRUE; ) + { + if( VSIFSeekL( fpImage, nChunkLoc, SEEK_SET ) != 0 ) + break; + + if( VSIFReadL( abyChunkHeader, sizeof(abyChunkHeader), 1, fpImage ) != 1 ) + break; + + if( abyChunkHeader[0] != 0xFF ) + break; // Not a valid tag + + if (abyChunkHeader[1] == 0xD9) + break; // End of image + + if ((abyChunkHeader[1] >= 0xD0) && (abyChunkHeader[1] <= 0xD8)) + { + // Restart tags have no length + nChunkLoc += 2; + continue; + } + + int nChunkLength = abyChunkHeader[2] * 256 + abyChunkHeader[3]; + + if( abyChunkHeader[1] == 0xe2 + && memcmp((const char *) abyChunkHeader + 4,"ICC_PROFILE\0",12) == 0 ) + { + /* Get length and segment ID */ + /* Header: */ + /* APP2 tag: 2 bytes */ + /* App Length: 2 bytes */ + /* ICC_PROFILE\0 tag: 12 bytes */ + /* Segment index: 1 bytes */ + /* Total segments: 1 bytes */ + int nICCChunkLength = nChunkLength - 16; + int nICCChunkID = abyChunkHeader[16]; + int nICCMaxChunkID = abyChunkHeader[17]; + + if (nChunkCount == -1) + nChunkCount = nICCMaxChunkID; + + /* Check that all max segment counts are the same */ + if (nICCMaxChunkID != nChunkCount) + { + bOk = false; + break; + } + + /* Check that no segment ID is larger than the total segment count */ + if ((nICCChunkID > nChunkCount) || (nICCChunkID == 0) || (nChunkCount == 0)) + { + bOk = false; + break; + } + + /* Check if ICC segment already loaded */ + if (apChunk[nICCChunkID-1] != NULL) + { + bOk = false; + break; + } + + /* Load it */ + apChunk[nICCChunkID-1] = (char*)VSIMalloc(nICCChunkLength); + anChunkSize[nICCChunkID-1] = nICCChunkLength; + + if( VSIFReadL( apChunk[nICCChunkID-1], nICCChunkLength, 1, fpImage ) != 1 ) + { + bOk = false; + break; + } + } + + nChunkLoc += 2 + nChunkLength; + } + + /* Get total size and verify that there are no missing segments */ + if (bOk) + { + for(int i = 0; i < nChunkCount; i++) + { + if (apChunk[i] == NULL) + { + /* Missing segment... abort */ + bOk = false; + break; + + } + nTotalSize += anChunkSize[i]; + } + } + + /* Merge all segments together and set metadata */ + if (bOk && nChunkCount > 0) + { + char *pBuffer = (char*)VSIMalloc(nTotalSize); + char *pBufferPtr = pBuffer; + for(int i = 0; i < nChunkCount; i++) + { + memcpy(pBufferPtr, apChunk[i], anChunkSize[i]); + pBufferPtr += anChunkSize[i]; + } + + /* Escape the profile */ + char *pszBase64Profile = CPLBase64Encode(nTotalSize, (const GByte*)pBuffer); + + /* Avoid setting the PAM dirty bit just for that */ + int nOldPamFlags = nPamFlags; + + /* Set ICC profile metadata */ + SetMetadataItem( "SOURCE_ICC_PROFILE", pszBase64Profile, "COLOR_PROFILE" ); + + nPamFlags = nOldPamFlags; + + VSIFree(pBuffer); + CPLFree(pszBase64Profile); + } + + /* Cleanup */ + for(int i = 0; i < nChunkCount; i++) + { + if (apChunk[i] != NULL) + VSIFree(apChunk[i]); + } + + VSIFSeekL( fpImage, nCurOffset, SEEK_SET ); +} + +/************************************************************************/ +/* EXIFInit() */ +/* */ +/* Create Metadata from Information file directory APP1 */ +/************************************************************************/ +int JPGDatasetCommon::EXIFInit(VSILFILE *fp) +{ + int one = 1; + TIFFHeader hdr; + + if( nTiffDirStart == 0 ) + return FALSE; + else if( nTiffDirStart > 0 ) + return TRUE; + nTiffDirStart = 0; + + bigendian = (*(char *)&one == 0); + +/* -------------------------------------------------------------------- */ +/* Search for APP1 chunk. */ +/* -------------------------------------------------------------------- */ + GByte abyChunkHeader[10]; + int nChunkLoc = 2; + + for( ; TRUE; ) + { + if( VSIFSeekL( fp, nChunkLoc, SEEK_SET ) != 0 ) + return FALSE; + + if( VSIFReadL( abyChunkHeader, sizeof(abyChunkHeader), 1, fp ) != 1 ) + return FALSE; + + int nChunkLength = abyChunkHeader[2] * 256 + abyChunkHeader[3]; + // COM marker + if( abyChunkHeader[0] == 0xFF && abyChunkHeader[1] == 0xFE && + nChunkLength >= 2 ) + { + char* pszComment = (char*)CPLMalloc(nChunkLength - 2 + 1); + if( nChunkLength > 2 && + VSIFSeekL( fp, nChunkLoc + 4, SEEK_SET ) == 0 && + VSIFReadL(pszComment, nChunkLength - 2, 1, fp) == 1 ) + { + pszComment[nChunkLength-2] = 0; + /* Avoid setting the PAM dirty bit just for that */ + int nOldPamFlags = nPamFlags; + /* Set ICC profile metadata */ + SetMetadataItem( "COMMENT", pszComment ); + nPamFlags = nOldPamFlags; + } + CPLFree(pszComment); + } + else + { + if( abyChunkHeader[0] != 0xFF + || (abyChunkHeader[1] & 0xf0) != 0xe0 ) + break; // Not an APP chunk. + + if( abyChunkHeader[1] == 0xe1 + && strncmp((const char *) abyChunkHeader + 4,"Exif",4) == 0 ) + { + nTIFFHEADER = nChunkLoc + 10; + } + } + + nChunkLoc += 2 + nChunkLength; + } + + if( nTIFFHEADER < 0 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read TIFF header */ +/* -------------------------------------------------------------------- */ + VSIFSeekL(fp, nTIFFHEADER, SEEK_SET); + if(VSIFReadL(&hdr,1,sizeof(hdr),fp) != sizeof(hdr)) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read %d byte from image header.", + (int) sizeof(hdr)); + return FALSE; + } + + if (hdr.tiff_magic != TIFF_BIGENDIAN && hdr.tiff_magic != TIFF_LITTLEENDIAN) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Not a TIFF file, bad magic number %u (%#x)", + hdr.tiff_magic, hdr.tiff_magic); + return FALSE; + } + + if (hdr.tiff_magic == TIFF_BIGENDIAN) bSwabflag = !bigendian; + if (hdr.tiff_magic == TIFF_LITTLEENDIAN) bSwabflag = bigendian; + + + if (bSwabflag) { + CPL_SWAP16PTR(&hdr.tiff_version); + CPL_SWAP32PTR(&hdr.tiff_diroff); + } + + + if (hdr.tiff_version != TIFF_VERSION) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Not a TIFF file, bad version number %u (%#x)", + hdr.tiff_version, hdr.tiff_version); + return FALSE; + } + nTiffDirStart = hdr.tiff_diroff; + + CPLDebug( "JPEG", "Magic: %#x <%s-endian> Version: %#x\n", + hdr.tiff_magic, + hdr.tiff_magic == TIFF_BIGENDIAN ? "big" : "little", + hdr.tiff_version ); + + return TRUE; +} + +/************************************************************************/ +/* JPGMaskBand() */ +/************************************************************************/ + +JPGMaskBand::JPGMaskBand( JPGDataset *poDS ) + +{ + this->poDS = poDS; + nBand = 0; + + nRasterXSize = poDS->GetRasterXSize(); + nRasterYSize = poDS->GetRasterYSize(); + + eDataType = GDT_Byte; + nBlockXSize = nRasterXSize; + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr JPGMaskBand::IReadBlock( CPL_UNUSED int nBlockX, int nBlockY, void *pImage ) +{ + JPGDataset *poJDS = (JPGDataset *) poDS; + +/* -------------------------------------------------------------------- */ +/* Make sure the mask is loaded and decompressed. */ +/* -------------------------------------------------------------------- */ + poJDS->DecompressMask(); + if( poJDS->pabyBitMask == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Set mask based on bitmask for this scanline. */ +/* -------------------------------------------------------------------- */ + int iX; + GUInt32 iBit = (GUInt32)nBlockY * (GUInt32)nBlockXSize; + + if( poJDS->bMaskLSBOrder ) + { + for( iX = 0; iX < nBlockXSize; iX++ ) + { + if( poJDS->pabyBitMask[iBit>>3] & (0x1 << (iBit&7)) ) + ((GByte *) pImage)[iX] = 255; + else + ((GByte *) pImage)[iX] = 0; + iBit++; + } + } + else + { + for( iX = 0; iX < nBlockXSize; iX++ ) + { + if( poJDS->pabyBitMask[iBit>>3] & (0x1 << (7 - (iBit&7))) ) + ((GByte *) pImage)[iX] = 255; + else + ((GByte *) pImage)[iX] = 0; + iBit++; + } + } + + return CE_None; +} + +/************************************************************************/ +/* JPGRasterBand() */ +/************************************************************************/ + +JPGRasterBand::JPGRasterBand( JPGDatasetCommon *poDS, int nBand ) + +{ + this->poDS = poGDS = poDS; + + this->nBand = nBand; + if( poDS->GetDataPrecision() == 12 ) + eDataType = GDT_UInt16; + else + eDataType = GDT_Byte; + + nBlockXSize = poDS->nRasterXSize;; + nBlockYSize = 1; + + GDALMajorObject::SetMetadataItem("COMPRESSION","JPEG","IMAGE_STRUCTURE"); +} + +/************************************************************************/ +/* JPGCreateBand() */ +/************************************************************************/ + +GDALRasterBand* JPGCreateBand(JPGDatasetCommon* poDS, int nBand) +{ + return new JPGRasterBand(poDS, nBand); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr JPGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + CPLErr eErr; + int nXSize = GetXSize(); + int nWordSize = GDALGetDataTypeSize(eDataType) / 8; + + CPLAssert( nBlockXOff == 0 ); + + if (poGDS->fpImage == NULL) + { + memset( pImage, 0, nXSize * nWordSize ); + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Load the desired scanline into the working buffer. */ +/* -------------------------------------------------------------------- */ + eErr = poGDS->LoadScanline( nBlockYOff ); + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Transfer between the working buffer the the callers buffer. */ +/* -------------------------------------------------------------------- */ + if( poGDS->GetRasterCount() == 1 ) + { +#ifdef JPEG_LIB_MK1_OR_12BIT + GDALCopyWords( poGDS->pabyScanline, GDT_UInt16, 2, + pImage, eDataType, nWordSize, + nXSize ); +#else + memcpy( pImage, poGDS->pabyScanline, nXSize * nWordSize ); +#endif + } + else + { +#ifdef JPEG_LIB_MK1_OR_12BIT + GDALCopyWords( poGDS->pabyScanline + (nBand-1) * 2, + GDT_UInt16, 6, + pImage, eDataType, nWordSize, + nXSize ); +#else + if (poGDS->eGDALColorSpace == JCS_RGB && + poGDS->GetOutColorSpace() == JCS_CMYK) + { + CPLAssert(eDataType == GDT_Byte); + int i; + if (nBand == 1) + { + for(i=0;ipabyScanline[i * 4 + 0]; + int K = poGDS->pabyScanline[i * 4 + 3]; + ((GByte*)pImage)[i] = (GByte) ((C * K) / 255); + } + } + else if (nBand == 2) + { + for(i=0;ipabyScanline[i * 4 + 1]; + int K = poGDS->pabyScanline[i * 4 + 3]; + ((GByte*)pImage)[i] = (GByte) ((M * K) / 255); + } + } + else if (nBand == 3) + { + for(i=0;ipabyScanline[i * 4 + 2]; + int K = poGDS->pabyScanline[i * 4 + 3]; + ((GByte*)pImage)[i] = (GByte) ((Y * K) / 255); + } + } + } + else + { + GDALCopyWords( poGDS->pabyScanline + (nBand-1) * nWordSize, + eDataType, nWordSize * poGDS->GetRasterCount(), + pImage, eDataType, nWordSize, + nXSize ); + } +#endif + } + +/* -------------------------------------------------------------------- */ +/* Forceably load the other bands associated with this scanline. */ +/* -------------------------------------------------------------------- */ + if( nBand == 1 ) + { + GDALRasterBlock *poBlock; + + int iBand; + for(iBand = 2; iBand <= poGDS->GetRasterCount() ; iBand++) + { + poBlock = + poGDS->GetRasterBand(iBand)->GetLockedBlockRef(nBlockXOff,nBlockYOff); + if( poBlock != NULL ) + poBlock->DropLock(); + } + } + + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp JPGRasterBand::GetColorInterpretation() + +{ + if( poGDS->eGDALColorSpace == JCS_GRAYSCALE ) + return GCI_GrayIndex; + + else if( poGDS->eGDALColorSpace == JCS_RGB) + { + if ( nBand == 1 ) + return GCI_RedBand; + + else if( nBand == 2 ) + return GCI_GreenBand; + + else + return GCI_BlueBand; + } + else if( poGDS->eGDALColorSpace == JCS_CMYK) + { + if ( nBand == 1 ) + return GCI_CyanBand; + + else if( nBand == 2 ) + return GCI_MagentaBand; + + else if ( nBand == 3 ) + return GCI_YellowBand; + + else + return GCI_BlackBand; + } + else if( poGDS->eGDALColorSpace == JCS_YCbCr || + poGDS->eGDALColorSpace == JCS_YCCK) + { + if ( nBand == 1 ) + return GCI_YCbCr_YBand; + + else if( nBand == 2 ) + return GCI_YCbCr_CbBand; + + else if ( nBand == 3 ) + return GCI_YCbCr_CrBand; + + else + return GCI_BlackBand; + } + else + { + CPLAssert(0); + return GCI_Undefined; + } +} + +/************************************************************************/ +/* GetMaskBand() */ +/************************************************************************/ + +GDALRasterBand *JPGRasterBand::GetMaskBand() + +{ + if (poGDS->nScaleFactor > 1 ) + return GDALPamRasterBand::GetMaskBand(); + + if (poGDS->fpImage == NULL) + return NULL; + + if( !poGDS->bHasCheckedForMask) + { + if( CSLTestBoolean(CPLGetConfigOption("JPEG_READ_MASK", "YES"))) + poGDS->CheckForMask(); + poGDS->bHasCheckedForMask = TRUE; + } + if( poGDS->pabyCMask ) + { + if( poGDS->poMaskBand == NULL ) + poGDS->poMaskBand = new JPGMaskBand( (JPGDataset *) poDS ); + + return poGDS->poMaskBand; + } + else + return GDALPamRasterBand::GetMaskBand(); +} + +/************************************************************************/ +/* GetMaskFlags() */ +/************************************************************************/ + +int JPGRasterBand::GetMaskFlags() + +{ + if (poGDS->nScaleFactor > 1 ) + return GDALPamRasterBand::GetMaskFlags(); + + if (poGDS->fpImage == NULL) + return 0; + + GetMaskBand(); + if( poGDS->poMaskBand != NULL ) + return GMF_PER_DATASET; + else + return GDALPamRasterBand::GetMaskFlags(); +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand* JPGRasterBand::GetOverview(int i) +{ + poGDS->InitInternalOverviews(); + + if( poGDS->nInternalOverviewsCurrent == 0 ) + return GDALPamRasterBand::GetOverview(i); + + if( i < 0 || i >= poGDS->nInternalOverviewsCurrent ) + return NULL; + else + return poGDS->papoInternalOverviews[i]->GetRasterBand(nBand); +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int JPGRasterBand::GetOverviewCount() +{ + poGDS->InitInternalOverviews(); + + if( poGDS->nInternalOverviewsCurrent == 0 ) + return GDALPamRasterBand::GetOverviewCount(); + + return poGDS->nInternalOverviewsCurrent; +} + +/************************************************************************/ +/* ==================================================================== */ +/* JPGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +JPGDatasetCommon::JPGDatasetCommon() + +{ + fpImage = NULL; + + nScaleFactor = 1; + bHasInitInternalOverviews = FALSE; + nInternalOverviewsCurrent = 0; + nInternalOverviewsToFree = 0; + papoInternalOverviews = NULL; + + pabyScanline = NULL; + nLoadedScanline = -1; + + bHasReadEXIFMetadata = FALSE; + bHasReadXMPMetadata = FALSE; + bHasReadICCMetadata = FALSE; + papszMetadata = NULL; + papszSubDatasets= NULL; + nExifOffset = -1; + nInterOffset = -1; + nGPSOffset = -1; + nTiffDirStart = -1; + nTIFFHEADER = -1; + + pszProjection = NULL; + bGeoTransformValid = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + nGCPCount = 0; + pasGCPList = NULL; + + bHasDoneJpegCreateDecompress = FALSE; + bHasDoneJpegStartDecompress = FALSE; + + bHasCheckedForMask = FALSE; + poMaskBand = NULL; + pabyBitMask = NULL; + bMaskLSBOrder = TRUE; + pabyCMask = NULL; + nCMaskSize = 0; + + eGDALColorSpace = JCS_UNKNOWN; + + bIsSubfile = FALSE; + bHasTriedLoadWorldFileOrTab = FALSE; + + sErrorStruct.bNonFatalErrorEncountered = FALSE; + sErrorStruct.p_previous_emit_message = NULL; +} + +/************************************************************************/ +/* ~JPGDataset() */ +/************************************************************************/ + +JPGDatasetCommon::~JPGDatasetCommon() + +{ + if( fpImage != NULL ) + VSIFCloseL( fpImage ); + + if( pabyScanline != NULL ) + CPLFree( pabyScanline ); + if( papszMetadata != NULL ) + CSLDestroy( papszMetadata ); + + if ( pszProjection ) + CPLFree( pszProjection ); + + if ( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + CPLFree( pabyBitMask ); + CPLFree( pabyCMask ); + delete poMaskBand; + + CloseDependentDatasets(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int JPGDatasetCommon::CloseDependentDatasets() +{ + int bRet = GDALPamDataset::CloseDependentDatasets(); + if( nInternalOverviewsToFree ) + { + bRet = TRUE; + for(int i = 0; i < nInternalOverviewsToFree; i++) + delete papoInternalOverviews[i]; + nInternalOverviewsToFree = 0; + } + CPLFree(papoInternalOverviews); + papoInternalOverviews = NULL; + + return bRet; +} + +/************************************************************************/ +/* InitEXIFOverview() */ +/************************************************************************/ + +GDALDataset* JPGDatasetCommon::InitEXIFOverview() +{ + if( !EXIFInit(fpImage) ) + return NULL; + + GUInt16 nEntryCount; +/* -------------------------------------------------------------------- */ +/* Read number of entry in directory */ +/* -------------------------------------------------------------------- */ + if( VSIFSeekL(fpImage, nTiffDirStart+nTIFFHEADER, SEEK_SET) != 0 + || VSIFReadL(&nEntryCount,1,sizeof(GUInt16),fpImage) != sizeof(GUInt16) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error reading EXIF Directory count at %d.", + nTiffDirStart + nTIFFHEADER ); + return NULL; + } + + if (bSwabflag) + TIFFSwabShort(&nEntryCount); + + // Some files are corrupt, a large entry count is a sign of this. + if( nEntryCount > 125 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Ignoring EXIF directory with unlikely entry count (%d).", + nEntryCount ); + return NULL; + } + + // Skip EXIF entries + VSIFSeekL(fpImage, nEntryCount * sizeof(TIFFDirEntry), SEEK_CUR ); + + // Read offset of next directory (IFD1) + GUInt32 nNextDirOff; + if( VSIFReadL(&nNextDirOff, 1, sizeof(GUInt32), fpImage) != sizeof(GUInt32) ) + return NULL; + if( bSwabflag ) + CPL_SWAP32PTR(&nNextDirOff); + if( nNextDirOff == 0 || nNextDirOff > 0xFFFFFFFFU - nTIFFHEADER ) + return NULL; + + // Seek to IFD1 + if( VSIFSeekL(fpImage, nTIFFHEADER+nNextDirOff, SEEK_SET) != 0 + || VSIFReadL(&nEntryCount,1,sizeof(GUInt16),fpImage) != sizeof(GUInt16) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error reading IFD1 Directory count at %d.", + nTIFFHEADER + nNextDirOff ); + return NULL; + } + + if (bSwabflag) + TIFFSwabShort(&nEntryCount); + if( nEntryCount > 125 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Ignoring IFD1 directory with unlikely entry count (%d).", + nEntryCount ); + return NULL; + } + /* CPLDebug("JPEG", "IFD1 entry count = %d", nEntryCount); */ + + int nImageWidth = 0, nImageHeight = 0, nCompression = 6; + GUInt32 nJpegIFOffset = 0, nJpegIFByteCount = 0; + for( int i = 0; i < nEntryCount; i ++ ) + { + TIFFDirEntry sEntry; + if( VSIFReadL(&sEntry,1,sizeof(sEntry),fpImage) != sizeof(sEntry) ) + { + CPLError( CE_Warning, CPLE_AppDefined, "Cannot read entry %d of IFD1", + i ); + return NULL; + } + if (bSwabflag) + { + TIFFSwabShort(&sEntry.tdir_tag); + TIFFSwabShort(&sEntry.tdir_type); + TIFFSwabLong (&sEntry.tdir_count); + TIFFSwabLong (&sEntry.tdir_offset); + } + /*CPLDebug("JPEG", "tag = %d (0x%4X), type = %d, count = %d, offset = %d", + sEntry.tdir_tag, sEntry.tdir_tag, sEntry.tdir_type, sEntry.tdir_count, sEntry.tdir_offset );*/ + + if( (sEntry.tdir_type == TIFF_SHORT || sEntry.tdir_type == TIFF_LONG) && + sEntry.tdir_count == 1 ) + { + switch( sEntry.tdir_tag ) + { + case JPEG_TIFF_IMAGEWIDTH: + nImageWidth = sEntry.tdir_offset; + break; + case JPEG_TIFF_IMAGEHEIGHT: + nImageHeight = sEntry.tdir_offset; + break; + case JPEG_TIFF_COMPRESSION: + nCompression = sEntry.tdir_offset; + break; + case JPEG_EXIF_JPEGIFOFSET: + nJpegIFOffset = sEntry.tdir_offset; + break; + case JPEG_EXIF_JPEGIFBYTECOUNT: + nJpegIFByteCount = sEntry.tdir_offset; + break; + default: + break; + } + } + } + if( nCompression != 6 || + nImageWidth >= nRasterXSize || + nImageHeight >= nRasterYSize || + nJpegIFOffset == 0 || + nJpegIFOffset > 0xFFFFFFFFU - nTIFFHEADER || + (int)nJpegIFByteCount <= 0 ) + { + return NULL; + } + + const char* pszSubfile = CPLSPrintf("JPEG_SUBFILE:%u,%d,%s", + nTIFFHEADER + nJpegIFOffset, nJpegIFByteCount, GetDescription()); + return JPGDataset::Open(pszSubfile, NULL, NULL, 1, FALSE); +} + +/************************************************************************/ +/* InitInternalOverviews() */ +/************************************************************************/ + +void JPGDatasetCommon::InitInternalOverviews() +{ + if( bHasInitInternalOverviews ) + return; + bHasInitInternalOverviews = TRUE; + +/* -------------------------------------------------------------------- */ +/* Instanciate on-the-fly overviews (if no external ones). */ +/* -------------------------------------------------------------------- */ + if( nScaleFactor == 1 && GetRasterBand(1)->GetOverviewCount() == 0 ) + { + /* EXIF overview */ + GDALDataset* poEXIFOverview = NULL; + if( nRasterXSize > 512 || nRasterYSize > 512 ) + { + vsi_l_offset nCurOffset = VSIFTellL(fpImage); + poEXIFOverview = InitEXIFOverview(); + if( poEXIFOverview != NULL ) + { + if( poEXIFOverview->GetRasterCount() != nBands || + poEXIFOverview->GetRasterXSize() >= nRasterXSize || + poEXIFOverview->GetRasterYSize() >= nRasterYSize ) + { + GDALClose(poEXIFOverview); + poEXIFOverview = NULL; + } + else + { + CPLDebug("JPEG", "EXIF overview (%d x %d) detected", + poEXIFOverview->GetRasterXSize(), + poEXIFOverview->GetRasterYSize()); + } + } + VSIFSeekL( fpImage, nCurOffset, SEEK_SET ); + } + + /* libjpeg-6b only suppports 2, 4 and 8 scale denominators */ + /* TODO: Later versions support more */ + + int i; + int nImplicitOverviews = 0; + + /* For the needs of the implicit JPEG-in-TIFF overview mechanism */ + if( CSLTestBoolean(CPLGetConfigOption("JPEG_FORCE_INTERNAL_OVERVIEWS", "NO")) ) + nImplicitOverviews = 3; + else + { + for(i = 2; i >= 0; i--) + { + if( nRasterXSize >= (256 << i) || nRasterYSize >= (256 << i) ) + { + nImplicitOverviews = i + 1; + break; + } + } + } + + if( nImplicitOverviews > 0 ) + { + papoInternalOverviews = (GDALDataset**) + CPLMalloc((nImplicitOverviews + (poEXIFOverview ? 1 : 0)) * sizeof(GDALDataset*)); + for(i = 0; i < nImplicitOverviews; i++ ) + { + if( poEXIFOverview != NULL && + poEXIFOverview->GetRasterXSize() >= nRasterXSize >> (i + 1) ) + { + break; + } + GDALDataset* poImplicitOverview = + JPGDataset::Open(GetDescription(), NULL, NULL, 1 << (i + 1), FALSE); + if( poImplicitOverview == NULL ) + break; + papoInternalOverviews[nInternalOverviewsCurrent] = poImplicitOverview; + nInternalOverviewsCurrent ++; + nInternalOverviewsToFree ++; + } + if( poEXIFOverview != NULL ) + { + papoInternalOverviews[nInternalOverviewsCurrent] = poEXIFOverview; + nInternalOverviewsCurrent ++; + nInternalOverviewsToFree ++; + } + } + else if( poEXIFOverview ) + { + papoInternalOverviews = (GDALDataset**) CPLMalloc(sizeof(GDALDataset*)); + papoInternalOverviews[0] = poEXIFOverview; + nInternalOverviewsCurrent ++; + nInternalOverviewsToFree ++; + } + } +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr JPGDatasetCommon::IBuildOverviews( const char *pszResampling, + int nOverviewsListCount, + int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + CPLErr eErr; + + bHasInitInternalOverviews = TRUE; + nInternalOverviewsCurrent = 0; + + eErr = GDALPamDataset::IBuildOverviews(pszResampling, + nOverviewsListCount, + panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData); + + return eErr; +} + +/************************************************************************/ +/* ErrorOutOnNonFatalError() */ +/************************************************************************/ + +int JPGDatasetCommon::ErrorOutOnNonFatalError() +{ + if( sErrorStruct.bNonFatalErrorEncountered ) + { + sErrorStruct.bNonFatalErrorEncountered = FALSE; + return TRUE; + } + return FALSE; +} + + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void JPGDatasetCommon::FlushCache() + +{ + GDALPamDataset::FlushCache(); + + if (bHasDoneJpegStartDecompress) + { + Restart(); + } + + /* For the needs of the implicit JPEG-in-TIFF overview mechanism */ + for(int i = 0; i < nInternalOverviewsCurrent; i++) + papoInternalOverviews[i]->FlushCache(); +} + +#endif // !defined(JPGDataset) + +/************************************************************************/ +/* JPGDataset() */ +/************************************************************************/ + +JPGDataset::JPGDataset() + +{ + sDInfo.data_precision = 8; +} + +/************************************************************************/ +/* ~JPGDataset() */ +/************************************************************************/ + +JPGDataset::~JPGDataset() + +{ + GDALPamDataset::FlushCache(); + + if (bHasDoneJpegStartDecompress) + { + jpeg_abort_decompress( &sDInfo ); + } + if (bHasDoneJpegCreateDecompress) + { + jpeg_destroy_decompress( &sDInfo ); + } +} + +/************************************************************************/ +/* LoadScanline() */ +/************************************************************************/ + +CPLErr JPGDataset::LoadScanline( int iLine ) + +{ + if( nLoadedScanline == iLine ) + return CE_None; + + // setup to trap a fatal error. + if (setjmp(sErrorStruct.setjmp_buffer)) + return CE_Failure; + + if (!bHasDoneJpegStartDecompress) + { + jpeg_start_decompress( &sDInfo ); + bHasDoneJpegStartDecompress = TRUE; + } + + if( pabyScanline == NULL ) + { + int nJPEGBands = 0; + switch(sDInfo.out_color_space) + { + case JCS_GRAYSCALE: + nJPEGBands = 1; + break; + case JCS_RGB: + case JCS_YCbCr: + nJPEGBands = 3; + break; + case JCS_CMYK: + case JCS_YCCK: + nJPEGBands = 4; + break; + + default: + CPLAssert(0); + } + + pabyScanline = (GByte *) + CPLMalloc(nJPEGBands * GetRasterXSize() * 2); + } + + if( iLine < nLoadedScanline ) + { + if( Restart() != CE_None ) + return CE_Failure; + } + + while( nLoadedScanline < iLine ) + { + JSAMPLE *ppSamples; + + ppSamples = (JSAMPLE *) pabyScanline; + jpeg_read_scanlines( &sDInfo, &ppSamples, 1 ); + if( ErrorOutOnNonFatalError() ) + return CE_Failure; + nLoadedScanline++; + } + + return CE_None; +} + +/************************************************************************/ +/* LoadDefaultTables() */ +/************************************************************************/ + +#if !defined(JPGDataset) + +#define Q1table GDALJPEG_Q1table +#define Q2table GDALJPEG_Q2table +#define Q3table GDALJPEG_Q3table +#define Q4table GDALJPEG_Q4table +#define Q5table GDALJPEG_Q5table +#define AC_BITS GDALJPEG_AC_BITS +#define AC_HUFFVAL GDALJPEG_AC_HUFFVAL +#define DC_BITS GDALJPEG_DC_BITS +#define DC_HUFFVAL GDALJPEG_DC_HUFFVAL + +static const GByte Q1table[64] = +{ + 8, 72, 72, 72, 72, 72, 72, 72, // 0 - 7 + 72, 72, 78, 74, 76, 74, 78, 89, // 8 - 15 + 81, 84, 84, 81, 89, 106, 93, 94, // 16 - 23 + 99, 94, 93, 106, 129, 111, 108, 116, // 24 - 31 + 116, 108, 111, 129, 135, 128, 136, 145, // 32 - 39 + 136, 128, 135, 155, 160, 177, 177, 160, // 40 - 47 + 155, 193, 213, 228, 213, 193, 255, 255, // 48 - 55 + 255, 255, 255, 255, 255, 255, 255, 255 // 56 - 63 +}; + +static const GByte Q2table[64] = +{ + 8, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 39, 37, 38, 37, 39, 45, 41, 42, 42, 41, 45, 53, + 47, 47, 50, 47, 47, 53, 65, 56, 54, 59, 59, 54, 56, 65, 68, 64, 69, 73, + 69, 64, 68, 78, 81, 89, 89, 81, 78, 98,108,115,108, 98,130,144,144,130, + 178,190,178,243,243,255 +}; + +static const GByte Q3table[64] = +{ + 8, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 11, 10, 11, 10, 11, 13, 11, 12, 12, 11, 13, 15, + 13, 13, 14, 13, 13, 15, 18, 16, 15, 16, 16, 15, 16, 18, 19, 18, 19, 21, + 19, 18, 19, 22, 23, 25, 25, 23, 22, 27, 30, 32, 30, 27, 36, 40, 40, 36, + 50, 53, 50, 68, 68, 91 +}; + +static const GByte Q4table[64] = +{ + 8, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 8, 7, 8, 7, 8, 9, 8, 8, 8, 8, 9, 11, + 9, 9, 10, 9, 9, 11, 13, 11, 11, 12, 12, 11, 11, 13, 14, 13, 14, 15, + 14, 13, 14, 16, 16, 18, 18, 16, 16, 20, 22, 23, 22, 20, 26, 29, 29, 26, + 36, 38, 36, 49, 49, 65 +}; + +static const GByte Q5table[64] = +{ + 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, + 5, 5, 6, 5, 5, 6, 7, 6, 6, 6, 6, 6, 6, 7, 8, 7, 8, 8, + 8, 7, 8, 9, 9, 10, 10, 9, 9, 11, 12, 13, 12, 11, 14, 16, 16, 14, + 20, 21, 20, 27, 27, 36 +}; + +static const GByte AC_BITS[16] = +{ 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125 }; + +static const GByte AC_HUFFVAL[256] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, + 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, + 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, + 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, + 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, + 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, + 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, + 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, + 0xF9, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const GByte DC_BITS[16] = +{ 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }; + +static const GByte DC_HUFFVAL[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B }; + +void JPGDataset::LoadDefaultTables( int n ) +{ + if( nQLevel < 1 ) + return; + +/* -------------------------------------------------------------------- */ +/* Load quantization table */ +/* -------------------------------------------------------------------- */ + int i; + JQUANT_TBL *quant_ptr; + const GByte *pabyQTable; + + if( nQLevel == 1 ) + pabyQTable = Q1table; + else if( nQLevel == 2 ) + pabyQTable = Q2table; + else if( nQLevel == 3 ) + pabyQTable = Q3table; + else if( nQLevel == 4 ) + pabyQTable = Q4table; + else if( nQLevel == 5 ) + pabyQTable = Q5table; + else + return; + + if (sDInfo.quant_tbl_ptrs[n] == NULL) + sDInfo.quant_tbl_ptrs[n] = + jpeg_alloc_quant_table((j_common_ptr) &(sDInfo)); + + quant_ptr = sDInfo.quant_tbl_ptrs[n]; /* quant_ptr is JQUANT_TBL* */ + for (i = 0; i < 64; i++) { + /* Qtable[] is desired quantization table, in natural array order */ + quant_ptr->quantval[i] = pabyQTable[i]; + } + +/* -------------------------------------------------------------------- */ +/* Load AC huffman table. */ +/* -------------------------------------------------------------------- */ + JHUFF_TBL *huff_ptr; + + if (sDInfo.ac_huff_tbl_ptrs[n] == NULL) + sDInfo.ac_huff_tbl_ptrs[n] = + jpeg_alloc_huff_table((j_common_ptr)&sDInfo); + + huff_ptr = sDInfo.ac_huff_tbl_ptrs[n]; /* huff_ptr is JHUFF_TBL* */ + + for (i = 1; i <= 16; i++) { + /* counts[i] is number of Huffman codes of length i bits, i=1..16 */ + huff_ptr->bits[i] = AC_BITS[i-1]; + } + + for (i = 0; i < 256; i++) { + /* symbols[] is the list of Huffman symbols, in code-length order */ + huff_ptr->huffval[i] = AC_HUFFVAL[i]; + } + +/* -------------------------------------------------------------------- */ +/* Load DC huffman table. */ +/* -------------------------------------------------------------------- */ + if (sDInfo.dc_huff_tbl_ptrs[n] == NULL) + sDInfo.dc_huff_tbl_ptrs[n] = + jpeg_alloc_huff_table((j_common_ptr)&sDInfo); + + huff_ptr = sDInfo.dc_huff_tbl_ptrs[n]; /* huff_ptr is JHUFF_TBL* */ + + for (i = 1; i <= 16; i++) { + /* counts[i] is number of Huffman codes of length i bits, i=1..16 */ + huff_ptr->bits[i] = DC_BITS[i-1]; + } + + for (i = 0; i < 256; i++) { + /* symbols[] is the list of Huffman symbols, in code-length order */ + huff_ptr->huffval[i] = DC_HUFFVAL[i]; + } + +} +#endif // !defined(JPGDataset) + +/************************************************************************/ +/* SetScaleNumAndDenom() */ +/************************************************************************/ + +void JPGDataset::SetScaleNumAndDenom() +{ +#if JPEG_LIB_VERSION > 62 + sDInfo.scale_num = 8 / nScaleFactor; + sDInfo.scale_denom = 8; +#else + sDInfo.scale_num = 1; + sDInfo.scale_denom = nScaleFactor; +#endif +} + +/************************************************************************/ +/* Restart() */ +/* */ +/* Restart compressor at the beginning of the file. */ +/************************************************************************/ + +CPLErr JPGDataset::Restart() + +{ + // setup to trap a fatal error. + if (setjmp(sErrorStruct.setjmp_buffer)) + return CE_Failure; + + J_COLOR_SPACE colorSpace = sDInfo.out_color_space; + J_COLOR_SPACE jpegColorSpace = sDInfo.jpeg_color_space; + + jpeg_abort_decompress( &sDInfo ); + jpeg_destroy_decompress( &sDInfo ); + jpeg_create_decompress( &sDInfo ); + + +#if !defined(JPGDataset) + LoadDefaultTables( 0 ); + LoadDefaultTables( 1 ); + LoadDefaultTables( 2 ); + LoadDefaultTables( 3 ); +#endif // !defined(JPGDataset) + +/* -------------------------------------------------------------------- */ +/* restart io. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( fpImage, nSubfileOffset, SEEK_SET ); + + jpeg_vsiio_src( &sDInfo, fpImage ); + jpeg_read_header( &sDInfo, TRUE ); + + sDInfo.out_color_space = colorSpace; + nLoadedScanline = -1; + SetScaleNumAndDenom(); + + /* The following errors could happen when "recycling" an existing dataset */ + /* particularly when triggered by the implicit overviews of JPEG-in-TIFF */ + /* with a corrupted TIFF file */ + if( nRasterXSize != (int)(sDInfo.image_width + nScaleFactor - 1) / nScaleFactor || + nRasterYSize != (int)(sDInfo.image_height + nScaleFactor - 1) / nScaleFactor ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unexpected image dimension (%d x %d), where as (%d x %d) was expected", + (int)(sDInfo.image_width + nScaleFactor - 1) / nScaleFactor, + (int)(sDInfo.image_height + nScaleFactor - 1) / nScaleFactor, + nRasterXSize, nRasterYSize); + bHasDoneJpegStartDecompress = FALSE; + } + else if( jpegColorSpace != sDInfo.jpeg_color_space ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unexpected jpeg color space : %d", + sDInfo.jpeg_color_space); + bHasDoneJpegStartDecompress = FALSE; + } + else + { + jpeg_start_decompress( &sDInfo ); + bHasDoneJpegStartDecompress = TRUE; + } + + return CE_None; +} + +#if !defined(JPGDataset) + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr JPGDatasetCommon::GetGeoTransform( double * padfTransform ) + +{ + CPLErr eErr = GDALPamDataset::GetGeoTransform( padfTransform ); + if( eErr != CE_Failure ) + return eErr; + + LoadWorldFileOrTab(); + + if( bGeoTransformValid ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + + return CE_None; + } + else + return eErr; +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int JPGDatasetCommon::GetGCPCount() + +{ + int nPAMGCPCount = GDALPamDataset::GetGCPCount(); + if( nPAMGCPCount != 0 ) + return nPAMGCPCount; + + LoadWorldFileOrTab(); + + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *JPGDatasetCommon::GetGCPProjection() + +{ + int nPAMGCPCount = GDALPamDataset::GetGCPCount(); + if( nPAMGCPCount != 0 ) + return GDALPamDataset::GetGCPProjection(); + + LoadWorldFileOrTab(); + + if( pszProjection && nGCPCount > 0 ) + return pszProjection; + else + return ""; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *JPGDatasetCommon::GetGCPs() + +{ + int nPAMGCPCount = GDALPamDataset::GetGCPCount(); + if( nPAMGCPCount != 0 ) + return GDALPamDataset::GetGCPs(); + + LoadWorldFileOrTab(); + + return pasGCPList; +} + +/************************************************************************/ +/* IRasterIO() */ +/* */ +/* Checks for what might be the most common read case */ +/* (reading an entire 8bit, RGB JPEG), and */ +/* optimizes for that case */ +/************************************************************************/ + +CPLErr JPGDatasetCommon::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + if((eRWFlag == GF_Read) && + (nBandCount == 3) && + (nBands == 3) && + (nXOff == 0) && (nYOff == 0) && + (nXSize == nBufXSize) && (nXSize == nRasterXSize) && + (nYSize == nBufYSize) && (nYSize == nRasterYSize) && + (eBufType == GDT_Byte) && (GetDataPrecision() != 12) && + (pData != NULL) && + (panBandMap != NULL) && + (panBandMap[0] == 1) && (panBandMap[1] == 2) && (panBandMap[2] == 3) && + /* those color spaces need transformation to RGB */ + GetOutColorSpace() != JCS_YCCK && GetOutColorSpace() != JCS_CMYK ) + { + Restart(); + int y; + CPLErr tmpError; + int x; + + // Pixel interleaved case + if( nBandSpace == 1 ) + { + for(y = 0; y < nYSize; ++y) + { + tmpError = LoadScanline(y); + if(tmpError != CE_None) return tmpError; + + if( nPixelSpace == 3 ) + { + memcpy(&(((GByte*)pData)[(y*nLineSpace)]), + pabyScanline, 3 * nXSize); + } + else + { + for(x = 0; x < nXSize; ++x) + { + memcpy(&(((GByte*)pData)[(y*nLineSpace) + (x*nPixelSpace)]), + (const GByte*)&(pabyScanline[x*3]), 3); + } + } + } + } + else + { + for(y = 0; y < nYSize; ++y) + { + tmpError = LoadScanline(y); + if(tmpError != CE_None) return tmpError; + for(x = 0; x < nXSize; ++x) + { + ((GByte*)pData)[(y*nLineSpace) + (x*nPixelSpace)] = pabyScanline[x*3]; + ((GByte*)pData)[(y*nLineSpace) + (x*nPixelSpace) + nBandSpace] = pabyScanline[x*3+1]; + ((GByte*)pData)[(y*nLineSpace) + (x*nPixelSpace) + 2 * nBandSpace] = pabyScanline[x*3+2]; + } + } + } + + return CE_None; + } + + return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + psExtraArg); +} + +#if JPEG_LIB_VERSION_MAJOR < 9 +/************************************************************************/ +/* JPEGDatasetIsJPEGLS() */ +/************************************************************************/ + +static int JPEGDatasetIsJPEGLS( GDALOpenInfo * poOpenInfo ) + +{ + GByte *pabyHeader = poOpenInfo->pabyHeader; + int nHeaderBytes = poOpenInfo->nHeaderBytes; + + if( nHeaderBytes < 10 ) + return FALSE; + + if( pabyHeader[0] != 0xff + || pabyHeader[1] != 0xd8 ) + return FALSE; + + int nOffset = 2; + for (;nOffset + 4 < nHeaderBytes;) + { + if (pabyHeader[nOffset] != 0xFF) + return FALSE; + + int nMarker = pabyHeader[nOffset + 1]; + if (nMarker == 0xF7 /* JPEG Extension 7, JPEG-LS */) + return TRUE; + if (nMarker == 0xF8 /* JPEG Extension 8, JPEG-LS Extension */) + return TRUE; + if (nMarker == 0xC3 /* Start of Frame 3 */) + return TRUE; + if (nMarker == 0xC7 /* Start of Frame 7 */) + return TRUE; + if (nMarker == 0xCB /* Start of Frame 11 */) + return TRUE; + if (nMarker == 0xCF /* Start of Frame 15 */) + return TRUE; + + nOffset += 2 + pabyHeader[nOffset + 2] * 256 + pabyHeader[nOffset + 3]; + } + + return FALSE; +} +#endif + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int JPGDatasetCommon::Identify( GDALOpenInfo * poOpenInfo ) + +{ + GByte *pabyHeader = NULL; + int nHeaderBytes = poOpenInfo->nHeaderBytes; + +/* -------------------------------------------------------------------- */ +/* If it is a subfile, read the JPEG header. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(poOpenInfo->pszFilename,"JPEG_SUBFILE:",13) ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* First we check to see if the file has the expected header */ +/* bytes. */ +/* -------------------------------------------------------------------- */ + pabyHeader = poOpenInfo->pabyHeader; + + if( nHeaderBytes < 10 ) + return FALSE; + + if( pabyHeader[0] != 0xff + || pabyHeader[1] != 0xd8 + || pabyHeader[2] != 0xff ) + return FALSE; + +#if JPEG_LIB_VERSION_MAJOR < 9 + if (JPEGDatasetIsJPEGLS(poOpenInfo)) + { + return FALSE; + } +#endif + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *JPGDatasetCommon::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) ) + return NULL; + + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The JPEG driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + VSILFILE* fpL = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + + return JPGDataset::Open(poOpenInfo->pszFilename, + fpL, + poOpenInfo->GetSiblingFiles(), + 1, TRUE); +} + +#endif // !defined(JPGDataset) + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *JPGDataset::Open( const char* pszFilename, + VSILFILE* fpLin, + char** papszSiblingFiles, + int nScaleFactor, + int bDoPAMInitialize ) + +{ +/* -------------------------------------------------------------------- */ +/* If it is a subfile, read the JPEG header. */ +/* -------------------------------------------------------------------- */ + int bIsSubfile = FALSE; + GUIntBig subfile_offset = 0; + GUIntBig subfile_size = 0; + const char *real_filename = pszFilename; + int nQLevel = -1; + + if( EQUALN(pszFilename,"JPEG_SUBFILE:",13) ) + { + char** papszTokens; + int bScan = FALSE; + + if( EQUALN(pszFilename,"JPEG_SUBFILE:Q",14) ) + { + papszTokens = CSLTokenizeString2(pszFilename + 14, ",", 0); + if (CSLCount(papszTokens) >= 3) + { + nQLevel = atoi(papszTokens[0]); + subfile_offset = CPLScanUIntBig(papszTokens[1], strlen(papszTokens[1])); + subfile_size = CPLScanUIntBig(papszTokens[2], strlen(papszTokens[2])); + bScan = TRUE; + } + CSLDestroy(papszTokens); + } + else + { + papszTokens = CSLTokenizeString2(pszFilename + 13, ",", 0); + if (CSLCount(papszTokens) >= 2) + { + subfile_offset = CPLScanUIntBig(papszTokens[0], strlen(papszTokens[0])); + subfile_size = CPLScanUIntBig(papszTokens[1], strlen(papszTokens[1])); + bScan = TRUE; + } + CSLDestroy(papszTokens); + } + + if( !bScan ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Corrupt subfile definition: %s", + pszFilename ); + return NULL; + } + + real_filename = strstr(pszFilename,","); + if( real_filename != NULL ) + real_filename = strstr(real_filename+1,","); + if( real_filename != NULL && nQLevel != -1 ) + real_filename = strstr(real_filename+1,","); + if( real_filename != NULL ) + real_filename++; + else + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Could not find filename in subfile definition."); + return NULL; + } + + CPLDebug( "JPG", + "real_filename %s, offset=" CPL_FRMT_GUIB ", size=" CPL_FRMT_GUIB "\n", + real_filename, subfile_offset, subfile_size); + + bIsSubfile = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Open the file using the large file api if necessary. */ +/* -------------------------------------------------------------------- */ + VSILFILE* fpImage; + + if( fpLin == NULL ) + { + fpImage = VSIFOpenL( real_filename, "rb" ); + + if( fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "VSIFOpenL(%s) failed unexpectedly in jpgdataset.cpp", + real_filename ); + return NULL; + } + } + else + fpImage = fpLin; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + JPGDataset *poDS; + + poDS = new JPGDataset(); + poDS->nQLevel = nQLevel; + poDS->fpImage = fpImage; + +/* -------------------------------------------------------------------- */ +/* Move to the start of jpeg data. */ +/* -------------------------------------------------------------------- */ + poDS->nSubfileOffset = subfile_offset; + VSIFSeekL( poDS->fpImage, poDS->nSubfileOffset, SEEK_SET ); + + poDS->eAccess = GA_ReadOnly; + + /* Will detect mismatch between compile-time and run-time libjpeg versions */ + if (setjmp(poDS->sErrorStruct.setjmp_buffer)) + { + delete poDS; + return NULL; + } + + poDS->sDInfo.err = jpeg_std_error( &(poDS->sJErr) ); + poDS->sJErr.error_exit = JPGDataset::ErrorExit; + poDS->sErrorStruct.p_previous_emit_message = poDS->sJErr.emit_message; + poDS->sJErr.emit_message = JPGDatasetCommon::EmitMessage; + poDS->sDInfo.client_data = (void *) &(poDS->sErrorStruct); + + jpeg_create_decompress( &(poDS->sDInfo) ); + poDS->bHasDoneJpegCreateDecompress = TRUE; + + /* This is to address bug related in ticket #1795 */ + if (CPLGetConfigOption("JPEGMEM", NULL) == NULL) + { + /* If the user doesn't provide a value for JPEGMEM, we want to be sure */ + /* that at least 500 MB will be used before creating the temporary file */ + poDS->sDInfo.mem->max_memory_to_use = + MAX(poDS->sDInfo.mem->max_memory_to_use, 500 * 1024 * 1024); + } + +/* -------------------------------------------------------------------- */ +/* Preload default NITF JPEG quantization tables. */ +/* -------------------------------------------------------------------- */ + +#if !defined(JPGDataset) + poDS->LoadDefaultTables( 0 ); + poDS->LoadDefaultTables( 1 ); + poDS->LoadDefaultTables( 2 ); + poDS->LoadDefaultTables( 3 ); +#endif // !defined(JPGDataset) + +/* -------------------------------------------------------------------- */ +/* If a fatal error occurs after this, we will return NULL */ +/* -------------------------------------------------------------------- */ + if (setjmp(poDS->sErrorStruct.setjmp_buffer)) + { +#if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset) + if (poDS->sDInfo.data_precision == 12) + { + fpImage = poDS->fpImage; + poDS->fpImage = NULL; + delete poDS; + return JPEGDataset12Open(pszFilename, fpImage, papszSiblingFiles, + nScaleFactor, bDoPAMInitialize); + } +#endif + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read pre-image data after ensuring the file is rewound. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( poDS->fpImage, poDS->nSubfileOffset, SEEK_SET ); + + jpeg_vsiio_src( &(poDS->sDInfo), poDS->fpImage ); + jpeg_read_header( &(poDS->sDInfo), TRUE ); + + if( poDS->sDInfo.data_precision != 8 + && poDS->sDInfo.data_precision != 12 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GDAL JPEG Driver doesn't support files with precision of" + " other than 8 or 12 bits." ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + + poDS->nScaleFactor = nScaleFactor; + poDS->SetScaleNumAndDenom(); + poDS->nRasterXSize = (poDS->sDInfo.image_width + nScaleFactor - 1) / nScaleFactor; + poDS->nRasterYSize = (poDS->sDInfo.image_height + nScaleFactor - 1) / nScaleFactor; + + poDS->sDInfo.out_color_space = poDS->sDInfo.jpeg_color_space; + poDS->eGDALColorSpace = poDS->sDInfo.jpeg_color_space; + + if( poDS->sDInfo.jpeg_color_space == JCS_GRAYSCALE ) + { + poDS->nBands = 1; + } + else if( poDS->sDInfo.jpeg_color_space == JCS_RGB ) + { + poDS->nBands = 3; + } + else if( poDS->sDInfo.jpeg_color_space == JCS_YCbCr ) + { + poDS->nBands = 3; + if (CSLTestBoolean(CPLGetConfigOption("GDAL_JPEG_TO_RGB", "YES"))) + { + poDS->sDInfo.out_color_space = JCS_RGB; + poDS->eGDALColorSpace = JCS_RGB; + poDS->SetMetadataItem( "SOURCE_COLOR_SPACE", "YCbCr", "IMAGE_STRUCTURE" ); + } + } + else if( poDS->sDInfo.jpeg_color_space == JCS_CMYK ) + { + if (CSLTestBoolean(CPLGetConfigOption("GDAL_JPEG_TO_RGB", "YES"))) + { + poDS->eGDALColorSpace = JCS_RGB; + poDS->nBands = 3; + poDS->SetMetadataItem( "SOURCE_COLOR_SPACE", "CMYK", "IMAGE_STRUCTURE" ); + } + else + { + poDS->nBands = 4; + } + } + else if( poDS->sDInfo.jpeg_color_space == JCS_YCCK ) + { + if (CSLTestBoolean(CPLGetConfigOption("GDAL_JPEG_TO_RGB", "YES"))) + { + poDS->eGDALColorSpace = JCS_RGB; + poDS->nBands = 3; + poDS->SetMetadataItem( "SOURCE_COLOR_SPACE", "YCbCrK", "IMAGE_STRUCTURE" ); + + /* libjpeg does the translation from YCrCbK -> CMYK internally */ + /* and we'll do the translation to RGB in IReadBlock() */ + poDS->sDInfo.out_color_space = JCS_CMYK; + } + else + { + poDS->nBands = 4; + } + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unrecognised jpeg_color_space value of %d.\n", + poDS->sDInfo.jpeg_color_space ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < poDS->nBands; iBand++ ) + poDS->SetBand( iBand+1, JPGCreateBand( poDS, iBand+1 ) ); + +/* -------------------------------------------------------------------- */ +/* More metadata. */ +/* -------------------------------------------------------------------- */ + if( poDS->nBands > 1 ) + { + poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + poDS->SetMetadataItem( "COMPRESSION", "JPEG", "IMAGE_STRUCTURE" ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( pszFilename ); + + if( nScaleFactor == 1 && bDoPAMInitialize ) + { + if( !bIsSubfile ) + poDS->TryLoadXML( papszSiblingFiles ); + else + poDS->nPamFlags |= GPF_NOSAVE; + +/* -------------------------------------------------------------------- */ +/* Open (external) overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, real_filename, papszSiblingFiles ); + + /* In the case of a file downloaded through the HTTP driver, this one */ + /* will unlink the temporary /vsimem file just after GDALOpen(), so */ + /* later VSIFOpenL() when reading internal overviews would fail. */ + /* Initialize them now */ + if( strncmp(real_filename, "/vsimem/http_", strlen("/vsimem/http_")) == 0 ) + { + poDS->InitInternalOverviews(); + } + } + else + poDS->nPamFlags |= GPF_NOSAVE; + + poDS->bIsSubfile = bIsSubfile; + + return poDS; +} + +#if !defined(JPGDataset) + +/************************************************************************/ +/* LoadWorldFileOrTab() */ +/************************************************************************/ + +void JPGDatasetCommon::LoadWorldFileOrTab() +{ + if (bIsSubfile) + return; + if (bHasTriedLoadWorldFileOrTab) + return; + bHasTriedLoadWorldFileOrTab = TRUE; + + char* pszWldFilename = NULL; + + /* TIROS3 JPEG files have a .wld extension, so don't look for .wld as */ + /* as worldfile ! */ + int bEndsWithWld = strlen(GetDescription()) > 4 && + EQUAL( GetDescription() + strlen(GetDescription()) - 4, ".wld"); + bGeoTransformValid = + GDALReadWorldFile2( GetDescription(), NULL, + adfGeoTransform, + oOvManager.GetSiblingFiles(), &pszWldFilename ) + || GDALReadWorldFile2( GetDescription(), ".jpw", + adfGeoTransform, + oOvManager.GetSiblingFiles(), &pszWldFilename ) + || ( !bEndsWithWld && GDALReadWorldFile2( GetDescription(), ".wld", + adfGeoTransform, + oOvManager.GetSiblingFiles(), &pszWldFilename )); + + if( !bGeoTransformValid ) + { + int bTabFileOK = + GDALReadTabFile2( GetDescription(), adfGeoTransform, + &pszProjection, + &nGCPCount, &pasGCPList, + oOvManager.GetSiblingFiles(), &pszWldFilename ); + + if( bTabFileOK && nGCPCount == 0 ) + bGeoTransformValid = TRUE; + } + + if (pszWldFilename) + { + osWldFilename = pszWldFilename; + CPLFree(pszWldFilename); + } +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **JPGDatasetCommon::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + + LoadWorldFileOrTab(); + + if (osWldFilename.size() != 0 && + CSLFindString(papszFileList, osWldFilename) == -1) + { + papszFileList = CSLAddString( papszFileList, osWldFilename ); + } + + return papszFileList; +} + +/************************************************************************/ +/* CheckForMask() */ +/************************************************************************/ + +void JPGDatasetCommon::CheckForMask() + +{ + GIntBig nFileSize; + GUInt32 nImageSize; + + /* Save current position to avoid disturbing JPEG stream decoding */ + vsi_l_offset nCurOffset = VSIFTellL(fpImage); + +/* -------------------------------------------------------------------- */ +/* Go to the end of the file, pull off four bytes, and see if */ +/* it is plausibly the size of the real image data. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( fpImage, 0, SEEK_END ); + nFileSize = VSIFTellL( fpImage ); + VSIFSeekL( fpImage, nFileSize - 4, SEEK_SET ); + + VSIFReadL( &nImageSize, 4, 1, fpImage ); + CPL_LSBPTR32( &nImageSize ); + + if( nImageSize < nFileSize / 2 || nImageSize > nFileSize - 4 ) + goto end; + +/* -------------------------------------------------------------------- */ +/* If that seems ok, seek back, and verify that just preceeding */ +/* the bitmask is an apparent end-of-jpeg-data marker. */ +/* -------------------------------------------------------------------- */ + GByte abyEOD[2]; + + VSIFSeekL( fpImage, nImageSize - 2, SEEK_SET ); + VSIFReadL( abyEOD, 2, 1, fpImage ); + if( abyEOD[0] != 0xff || abyEOD[1] != 0xd9 ) + goto end; + +/* -------------------------------------------------------------------- */ +/* We seem to have a mask. Read it in. */ +/* -------------------------------------------------------------------- */ + nCMaskSize = (int) (nFileSize - nImageSize - 4); + pabyCMask = (GByte *) VSIMalloc(nCMaskSize); + if (pabyCMask == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate memory (%d bytes) for mask compressed buffer", + nCMaskSize); + goto end; + } + VSIFReadL( pabyCMask, nCMaskSize, 1, fpImage ); + + CPLDebug( "JPEG", "Got %d byte compressed bitmask.", + nCMaskSize ); + +end: + VSIFSeekL( fpImage, nCurOffset, SEEK_SET ); +} + +/************************************************************************/ +/* DecompressMask() */ +/************************************************************************/ + +void JPGDatasetCommon::DecompressMask() + +{ + if( pabyCMask == NULL || pabyBitMask != NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Allocate 1bit buffer - may be slightly larger than needed. */ +/* -------------------------------------------------------------------- */ + int nBufSize = nRasterYSize * ((nRasterXSize+7)/8); + pabyBitMask = (GByte *) VSIMalloc( nBufSize ); + if (pabyBitMask == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate memory (%d bytes) for mask uncompressed buffer", + nBufSize); + CPLFree(pabyCMask); + pabyCMask = NULL; + return; + } + +/* -------------------------------------------------------------------- */ +/* Decompress */ +/* -------------------------------------------------------------------- */ + void* pOut = CPLZLibInflate( pabyCMask, nCMaskSize, + pabyBitMask, nBufSize, NULL ); +/* -------------------------------------------------------------------- */ +/* Cleanup if an error occurs. */ +/* -------------------------------------------------------------------- */ + if( pOut == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failure decoding JPEG validity bitmask." ); + CPLFree( pabyCMask ); + pabyCMask = NULL; + + CPLFree( pabyBitMask ); + pabyBitMask = NULL; + } + else + { + const char* pszJPEGMaskBitOrder = CPLGetConfigOption("JPEG_MASK_BIT_ORDER", "AUTO"); + if( EQUAL(pszJPEGMaskBitOrder, "LSB") ) + bMaskLSBOrder = TRUE; + else if( EQUAL(pszJPEGMaskBitOrder, "MSB") ) + bMaskLSBOrder = FALSE; + else if( nRasterXSize > 8 && nRasterYSize > 1 ) + { + /* Test MSB ordering hypothesis in a very restrictive case where it is */ + /* *obviously* ordered as MSB ! (unless someone coded something */ + /* specifically to defeat the below logic) */ + /* The case considered here is dop_465_6100.jpg from #5102 */ + /* The mask is identical for each line, starting with 1's and ending with 0's */ + /* (or starting with 0's and ending with 1's), and no other intermediate change. */ + /* We can detect the MSB ordering since the lsb bits at the end of the first */ + /* line will be set with the 1's of the beginning of the second line. */ + /* We can only be sure of this heuristics if the change of value occurs in the */ + /* middle of a byte, or if the raster width is not a multiple of 8. */ + int iX; + int nPrevValBit = 0; + int nChangedValBit = 0; + for( iX = 0; iX < nRasterXSize; iX++ ) + { + int nValBit = (pabyBitMask[iX>>3] & (0x1 << (7 - (iX&7)))) != 0; + if( iX == 0 ) + nPrevValBit = nValBit; + else if( nValBit != nPrevValBit ) + { + nPrevValBit = nValBit; + nChangedValBit ++; + if( nChangedValBit == 1 ) + { + int bValChangedOnByteBoundary = ((iX % 8) == 0); + if( bValChangedOnByteBoundary && ((nRasterXSize % 8) == 0 ) ) + break; + } + else + break; + } + int iNextLineX = iX + nRasterXSize; + int nNextLineValBit = (pabyBitMask[iNextLineX>>3] & (0x1 << (7 - (iNextLineX&7)))) != 0; + if( nValBit != nNextLineValBit ) + break; + } + + if( iX == nRasterXSize ) + { + CPLDebug("JPEG", "Bit ordering in mask is guessed to be msb (unusual)"); + bMaskLSBOrder = FALSE; + } + else + bMaskLSBOrder = TRUE; + } + else + bMaskLSBOrder = TRUE; + } +} + +#endif // !defined(JPGDataset) + +/************************************************************************/ +/* ErrorExit() */ +/************************************************************************/ + +void JPGDataset::ErrorExit(j_common_ptr cinfo) +{ + GDALJPEGErrorStruct* psErrorStruct = (GDALJPEGErrorStruct* ) cinfo->client_data; + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + +/* Avoid error for a 12bit JPEG if reading from the 8bit JPEG driver and */ +/* we have JPEG_DUAL_MODE_8_12 support, as we'll try again with 12bit JPEG */ +/* driver */ +#if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset) + if (strstr(buffer, "Unsupported JPEG data precision 12") == NULL) +#endif + CPLError( CE_Failure, CPLE_AppDefined, + "libjpeg: %s", buffer ); + + /* Return control to the setjmp point */ + longjmp(psErrorStruct->setjmp_buffer, 1); +} + +#if !defined(JPGDataset) + +/************************************************************************/ +/* EmitMessage() */ +/************************************************************************/ + +void JPGDatasetCommon::EmitMessage(j_common_ptr cinfo, int msg_level) +{ + GDALJPEGErrorStruct* psErrorStruct = (GDALJPEGErrorStruct* ) cinfo->client_data; + if( msg_level >= 0 ) /* Trace message */ + { + if( psErrorStruct->p_previous_emit_message != NULL ) + psErrorStruct->p_previous_emit_message(cinfo, msg_level); + } + else /* Warning : libjpeg will try to recover but the image will be likely corrupted */ + { + struct jpeg_error_mgr * err = cinfo->err; + /* It's a warning message. Since corrupt files may generate many warnings, + * the policy implemented here is to show only the first warning, + * unless trace_level >= 3. + */ + if (err->num_warnings == 0 || err->trace_level >= 3) + { + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + if( CSLTestBoolean(CPLGetConfigOption("GDAL_ERROR_ON_LIBJPEG_WARNING", "NO")) ) + { + psErrorStruct->bNonFatalErrorEncountered = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, "libjpeg: %s", buffer ); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, "libjpeg: %s (this warning can be turned as an error by setting GDAL_ERROR_ON_LIBJPEG_WARNING to TRUE)", buffer ); + } + } + + /* Always count warnings in num_warnings. */ + err->num_warnings++; + } +} + +/************************************************************************/ +/* JPGAddICCProfile() */ +/* */ +/* This function adds an ICC profile to a JPEG file. */ +/************************************************************************/ + +void JPGAddICCProfile( struct jpeg_compress_struct *pInfo, + const char *pszICCProfile, + void (*p_jpeg_write_m_header) (j_compress_ptr cinfo, int marker, unsigned int datalen), + void (*p_jpeg_write_m_byte) (j_compress_ptr cinfo, int val)) +{ + if( pszICCProfile == NULL ) + return; + + /* Write out each segment of the ICC profile */ + char *pEmbedBuffer = CPLStrdup(pszICCProfile); + GInt32 nEmbedLen = CPLBase64DecodeInPlace((GByte*)pEmbedBuffer); + char *pEmbedPtr = pEmbedBuffer; + char const * const paHeader = "ICC_PROFILE"; + int nSegments = (nEmbedLen + 65518) / 65519; + int nSegmentID = 1; + + while( nEmbedLen != 0) + { + /* 65535 - 16 bytes for header = 65519 */ + int nChunkLen = (nEmbedLen > 65519)?65519:nEmbedLen; + nEmbedLen -= nChunkLen; + + /* write marker and length. */ + p_jpeg_write_m_header(pInfo, JPEG_APP0 + 2, + (unsigned int) (nChunkLen + 14)); + + /* Write identifier */ + for(int i = 0; i < 12; i++) + p_jpeg_write_m_byte(pInfo, paHeader[i]); + + /* Write ID and max ID */ + p_jpeg_write_m_byte(pInfo, nSegmentID); + p_jpeg_write_m_byte(pInfo, nSegments); + + /* Write ICC Profile */ + for(int i = 0; i < nChunkLen; i++) + p_jpeg_write_m_byte(pInfo, pEmbedPtr[i]); + + nSegmentID++; + + pEmbedPtr += nChunkLen; + } + + CPLFree(pEmbedBuffer); +} + +/************************************************************************/ +/* JPGAppendMask() */ +/* */ +/* This function appends a zlib compressed bitmask to a JPEG */ +/* file (or really any file) pulled from an existing mask band. */ +/************************************************************************/ + +// MSVC does not know that memset() has initialized sStream. +#ifdef _MSC_VER +# pragma warning(disable:4701) +#endif + +CPLErr JPGAppendMask( const char *pszJPGFilename, GDALRasterBand *poMask, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + int nXSize = poMask->GetXSize(); + int nYSize = poMask->GetYSize(); + int nBitBufSize = nYSize * ((nXSize+7)/8); + int iX, iY; + GByte *pabyBitBuf, *pabyMaskLine; + CPLErr eErr = CE_None; + +/* -------------------------------------------------------------------- */ +/* Allocate uncompressed bit buffer. */ +/* -------------------------------------------------------------------- */ + pabyBitBuf = (GByte *) VSICalloc(1,nBitBufSize); + + pabyMaskLine = (GByte *) VSIMalloc(nXSize); + if (pabyBitBuf == NULL || pabyMaskLine == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + eErr = CE_Failure; + } + + /* No reason to set it to MSB, unless for debugging purposes */ + /* to be able to generate a unusual LSB ordered mask (#5102) */ + const char* pszJPEGMaskBitOrder = CPLGetConfigOption("JPEG_WRITE_MASK_BIT_ORDER", "LSB"); + int bMaskLSBOrder = EQUAL(pszJPEGMaskBitOrder, "LSB"); + +/* -------------------------------------------------------------------- */ +/* Set bit buffer from mask band, scanline by scanline. */ +/* -------------------------------------------------------------------- */ + GUInt32 iBit = 0; + for( iY = 0; eErr == CE_None && iY < nYSize; iY++ ) + { + eErr = poMask->RasterIO( GF_Read, 0, iY, nXSize, 1, + pabyMaskLine, nXSize, 1, GDT_Byte, 0, 0, NULL ); + if( eErr != CE_None ) + break; + + if( bMaskLSBOrder ) + { + for( iX = 0; iX < nXSize; iX++ ) + { + if( pabyMaskLine[iX] != 0 ) + pabyBitBuf[iBit>>3] |= (0x1 << (iBit&7)); + + iBit++; + } + } + else + { + for( iX = 0; iX < nXSize; iX++ ) + { + if( pabyMaskLine[iX] != 0 ) + pabyBitBuf[iBit>>3] |= (0x1 << (7 - (iBit&7))); + + iBit++; + } + } + + if( eErr == CE_None + && !pfnProgress( (iY + 1) / (double) nYSize, NULL, pProgressData ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated JPGAppendMask()" ); + } + } + + CPLFree( pabyMaskLine ); + +/* -------------------------------------------------------------------- */ +/* Compress */ +/* -------------------------------------------------------------------- */ + GByte *pabyCMask = NULL; + + if( eErr == CE_None ) + { + pabyCMask = (GByte *) VSIMalloc(nBitBufSize + 30); + if (pabyCMask == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + eErr = CE_Failure; + } + } + + size_t nTotalOut = 0; + if ( eErr == CE_None ) + { + if( CPLZLibDeflate( pabyBitBuf, nBitBufSize, 9, + pabyCMask, nBitBufSize + 30, + &nTotalOut ) == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Deflate compression of jpeg bit mask failed." ); + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Write to disk, along with image file size. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None ) + { + VSILFILE *fpOut; + GUInt32 nImageSize; + + fpOut = VSIFOpenL( pszJPGFilename, "r+" ); + if( fpOut == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to open jpeg to append bitmask." ); + eErr = CE_Failure; + } + else + { + VSIFSeekL( fpOut, 0, SEEK_END ); + + nImageSize = (GUInt32) VSIFTellL( fpOut ); + CPL_LSBPTR32( &nImageSize ); + + if( VSIFWriteL( pabyCMask, 1, nTotalOut, fpOut ) + != nTotalOut ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failure writing compressed bitmask.\n%s", + VSIStrerror( errno ) ); + eErr = CE_Failure; + } + else + VSIFWriteL( &nImageSize, 4, 1, fpOut ); + + VSIFCloseL( fpOut ); + } + } + + CPLFree( pabyBitBuf ); + CPLFree( pabyCMask ); + + return eErr; +} + +/************************************************************************/ +/* JPGAddEXIFOverview() */ +/************************************************************************/ + +void JPGAddEXIFOverview( GDALDataType eWorkDT, + GDALDataset* poSrcDS, char** papszOptions, + j_compress_ptr cinfo, + void (*p_jpeg_write_m_header) (j_compress_ptr cinfo, int marker, unsigned int datalen), + void (*p_jpeg_write_m_byte) (j_compress_ptr cinfo, int val), + GDALDataset *(pCreateCopy)( const char *, GDALDataset *, + int, char **, + GDALProgressFunc pfnProgress, + void * pProgressData )) +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + + int bGenerateEXIFThumbnail = + CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "EXIF_THUMBNAIL", "NO")); + const char* pszThumbnailWidth = CSLFetchNameValue(papszOptions, "THUMBNAIL_WIDTH"); + const char* pszThumbnailHeight = CSLFetchNameValue(papszOptions, "THUMBNAIL_HEIGHT"); + int nOvrWidth = 0, nOvrHeight = 0; + if( pszThumbnailWidth == NULL && pszThumbnailHeight == NULL ) + { + if( nXSize >= nYSize ) + { + nOvrWidth = 128; + } + else + { + nOvrHeight = 128; + } + } + if( pszThumbnailWidth != NULL ) + { + nOvrWidth = atoi(pszThumbnailWidth); + if( nOvrWidth < 32 ) + nOvrWidth = 32; + if( nOvrWidth > 1024 ) + nOvrWidth = 1024; + } + if( pszThumbnailHeight != NULL ) + { + nOvrHeight = atoi(pszThumbnailHeight); + if( nOvrHeight < 32 ) + nOvrHeight = 32; + if( nOvrHeight > 1024 ) + nOvrHeight = 1024; + } + if( nOvrWidth == 0 ) + { + nOvrWidth = (int)((GIntBig)nOvrHeight * nXSize / nYSize); + if( nOvrWidth == 0 ) nOvrWidth = 1; + } + else if( nOvrHeight == 0 ) + { + nOvrHeight = (int)((GIntBig)nOvrWidth * nYSize / nXSize); + if( nOvrHeight == 0 ) nOvrHeight = 1; + } + + if( bGenerateEXIFThumbnail && nXSize > nOvrWidth && nYSize > nOvrHeight ) + { + GDALDataset* poMemDS = MEMDataset::Create("", nOvrWidth, nOvrHeight, nBands, eWorkDT, NULL); + GDALRasterBand** papoSrcBands = (GDALRasterBand**)CPLMalloc(nBands * sizeof(GDALRasterBand*)); + GDALRasterBand*** papapoOverviewBands= (GDALRasterBand***)CPLMalloc(nBands * sizeof(GDALRasterBand**)); + int i; + for(i=0;iGetRasterBand(i+1); + papapoOverviewBands[i] = (GDALRasterBand**)CPLMalloc(sizeof(GDALRasterBand*)); + papapoOverviewBands[i][0] = poMemDS->GetRasterBand(i+1); + } + CPLErr eErr = GDALRegenerateOverviewsMultiBand(nBands, papoSrcBands, + 1, papapoOverviewBands, + "AVERAGE", NULL, NULL); + CPLFree(papoSrcBands); + for(i=0;i> 8 ); + p_jpeg_write_m_byte( cinfo, TIFF_VERSION ); + p_jpeg_write_m_byte( cinfo, 0x00 ); + + p_jpeg_write_m_byte( cinfo, 8 ); /* Offset of IFD0 */ + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, 0 ); /* Number of entries of IFD0 */ + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, 14 ); /* Offset of IFD1 */ + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, 5 ); /* Number of entries of IFD1 */ + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, JPEG_TIFF_IMAGEWIDTH & 0xff ); + p_jpeg_write_m_byte( cinfo, (JPEG_TIFF_IMAGEWIDTH >> 8) & 0xff ); + + p_jpeg_write_m_byte( cinfo, TIFF_LONG ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, 1 ); /* 1 value */ + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, nOvrWidth & 0xff ); + p_jpeg_write_m_byte( cinfo, nOvrWidth >> 8 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, JPEG_TIFF_IMAGEHEIGHT & 0xff ); + p_jpeg_write_m_byte( cinfo, JPEG_TIFF_IMAGEHEIGHT >> 8 ); + + p_jpeg_write_m_byte( cinfo, TIFF_LONG ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, 1 ); /* 1 value */ + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, nOvrHeight & 0xff ); + p_jpeg_write_m_byte( cinfo, nOvrHeight >> 8 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, JPEG_TIFF_COMPRESSION & 0xff ); + p_jpeg_write_m_byte( cinfo, JPEG_TIFF_COMPRESSION >> 8 ); + + p_jpeg_write_m_byte( cinfo, TIFF_SHORT ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, 1 ); /* 1 value */ + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, 6 ); /* JPEG compression */ + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, JPEG_EXIF_JPEGIFOFSET & 0xff ); + p_jpeg_write_m_byte( cinfo, JPEG_EXIF_JPEGIFOFSET >> 8 ); + + p_jpeg_write_m_byte( cinfo, TIFF_LONG ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, 1 ); /* 1 value */ + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + const unsigned int nJPEGIfOffset = 16 + 5 * 12 + 4; + p_jpeg_write_m_byte( cinfo, nJPEGIfOffset & 0xff ); + p_jpeg_write_m_byte( cinfo, nJPEGIfOffset >> 8 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, JPEG_EXIF_JPEGIFBYTECOUNT & 0xff ); + p_jpeg_write_m_byte( cinfo, JPEG_EXIF_JPEGIFBYTECOUNT >> 8 ); + + p_jpeg_write_m_byte( cinfo, TIFF_LONG ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, 1 ); /* 1 value */ + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, nJPEGIfByteCount & 0xff ); + p_jpeg_write_m_byte( cinfo, nJPEGIfByteCount >> 8 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + p_jpeg_write_m_byte( cinfo, 0 ); /* Offset of IFD2 == 0 ==> end of TIFF directory */ + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + p_jpeg_write_m_byte( cinfo, 0 ); + + for(int i=0; i<(int)nJPEGIfByteCount; i++) + p_jpeg_write_m_byte( cinfo, pabyOvr[i] ); + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot write EXIF thumbnail. The size of the EXIF segment exceeds 65536 bytes"); + } + CPLFree(pabyOvr); + } +} + +#endif // !defined(JPGDataset) + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * +JPGDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int nQuality = 75; + int bProgressive = FALSE; + int nCloneFlags = GCIF_PAM_DEFAULT; + const char* pszVal; + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + if( nBands != 1 && nBands != 3 && nBands != 4 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "JPEG driver doesn't support %d bands. Must be 1 (grey), " + "3 (RGB) or 4 bands.\n", nBands ); + + return NULL; + } + + if (nBands == 1 && + poSrcDS->GetRasterBand(1)->GetColorTable() != NULL) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "JPEG driver ignores color table. " + "The source raster band will be considered as grey level.\n" + "Consider using color table expansion (-expand option in gdal_translate)\n"); + if (bStrict) + return NULL; + } + + GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + +#if defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12) + if( eDT != GDT_Byte && eDT != GDT_UInt16 ) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "JPEG driver doesn't support data type %s. " + "Only eight and twelve bit bands supported (Mk1 libjpeg).\n", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); + + if (bStrict) + return NULL; + } + + if( eDT == GDT_UInt16 || eDT == GDT_Int16 ) + { +#if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset) + return JPEGDataset12CreateCopy(pszFilename, poSrcDS, + bStrict, papszOptions, + pfnProgress, pProgressData ); +#else + eDT = GDT_UInt16; +#endif + } + else + eDT = GDT_Byte; + +#else + if( eDT != GDT_Byte ) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "JPEG driver doesn't support data type %s. " + "Only eight bit byte bands supported.\n", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); + + if (bStrict) + return NULL; + } + + eDT = GDT_Byte; // force to 8bit. +#endif + +/* -------------------------------------------------------------------- */ +/* What options has the user selected? */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue(papszOptions,"QUALITY") != NULL ) + { + nQuality = atoi(CSLFetchNameValue(papszOptions,"QUALITY")); + if( nQuality < 10 || nQuality > 100 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "QUALITY=%s is not a legal value in the range 10-100.", + CSLFetchNameValue(papszOptions,"QUALITY") ); + return NULL; + } + } + + bProgressive = CSLFetchBoolean( papszOptions, "PROGRESSIVE", FALSE ); + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpImage; + + fpImage = VSIFOpenL( pszFilename, "wb" ); + if( fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create jpeg file %s.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Initialize JPG access to the file. */ +/* -------------------------------------------------------------------- */ + struct jpeg_compress_struct sCInfo; + struct jpeg_error_mgr sJErr; + GDALJPEGErrorStruct sErrorStruct; + sErrorStruct.bNonFatalErrorEncountered = FALSE; + + if (setjmp(sErrorStruct.setjmp_buffer)) + { + VSIFCloseL( fpImage ); + return NULL; + } + + sCInfo.err = jpeg_std_error( &sJErr ); + sJErr.error_exit = JPGDataset::ErrorExit; + sErrorStruct.p_previous_emit_message = sJErr.emit_message; + sJErr.emit_message = JPGDatasetCommon::EmitMessage; + sCInfo.client_data = (void *) &(sErrorStruct); + + jpeg_create_compress( &sCInfo ); + + jpeg_vsiio_dest( &sCInfo, fpImage ); + + sCInfo.image_width = nXSize; + sCInfo.image_height = nYSize; + sCInfo.input_components = nBands; + + if( nBands == 3 ) + sCInfo.in_color_space = JCS_RGB; + else if( nBands == 1 ) + sCInfo.in_color_space = JCS_GRAYSCALE; + else + sCInfo.in_color_space = JCS_UNKNOWN; + + jpeg_set_defaults( &sCInfo ); + + /* This is to address bug related in ticket #1795 */ + if (CPLGetConfigOption("JPEGMEM", NULL) == NULL) + { + /* If the user doesn't provide a value for JPEGMEM, we want to be sure */ + /* that at least 500 MB will be used before creating the temporary file */ + sCInfo.mem->max_memory_to_use = + MAX(sCInfo.mem->max_memory_to_use, 500 * 1024 * 1024); + } + + if( eDT == GDT_UInt16 ) + { + sCInfo.data_precision = 12; + } + else + { + sCInfo.data_precision = 8; + } + + pszVal = CSLFetchNameValue(papszOptions, "ARITHMETIC"); + if( pszVal ) + sCInfo.arith_code = CSLTestBoolean(pszVal); + + /* Optimized Huffman coding. Supposedly slower according to libjpeg doc */ + /* but no longer significant with today computer standards */ + if( !sCInfo.arith_code ) + sCInfo.optimize_coding = TRUE; + +#if JPEG_LIB_VERSION_MAJOR >= 8 && \ + (JPEG_LIB_VERSION_MAJOR > 8 || JPEG_LIB_VERSION_MINOR >= 3) + pszVal = CSLFetchNameValue(papszOptions, "BLOCK"); + if( pszVal ) + sCInfo.block_size = atoi(pszVal); +#endif + +#if JPEG_LIB_VERSION_MAJOR >= 9 + pszVal = CSLFetchNameValue(papszOptions, "COLOR_TRANSFORM"); + if( pszVal ) + { + sCInfo.color_transform = EQUAL(pszVal, "RGB1") ? JCT_SUBTRACT_GREEN : JCT_NONE; + jpeg_set_colorspace(&sCInfo, JCS_RGB); + } + else +#endif + + /* Mostly for debugging purposes */ + if( nBands == 3 && CSLTestBoolean(CPLGetConfigOption("JPEG_WRITE_RGB", "NO")) ) + { + jpeg_set_colorspace(&sCInfo, JCS_RGB); + } + + GDALDataType eWorkDT; +#ifdef JPEG_LIB_MK1 + sCInfo.bits_in_jsample = sCInfo.data_precision; + eWorkDT = GDT_UInt16; /* Always force to 16 bit for JPEG_LIB_MK1 */ +#else + eWorkDT = eDT; +#endif + + jpeg_set_quality( &sCInfo, nQuality, TRUE ); + + if( bProgressive ) + jpeg_simple_progression( &sCInfo ); + + jpeg_start_compress( &sCInfo, TRUE ); + + JPGAddEXIFOverview( eWorkDT, poSrcDS, papszOptions, + &sCInfo, jpeg_write_m_header, jpeg_write_m_byte, + CreateCopy ); + +/* -------------------------------------------------------------------- */ +/* Add comment if available */ +/* -------------------------------------------------------------------- */ + const char *pszComment = CSLFetchNameValue(papszOptions, "COMMENT"); + if( pszComment ) + jpeg_write_marker(&sCInfo, JPEG_COM, (const JOCTET*)pszComment, + (unsigned int)strlen(pszComment)); + +/* -------------------------------------------------------------------- */ +/* Save ICC profile if available */ +/* -------------------------------------------------------------------- */ + const char *pszICCProfile = CSLFetchNameValue(papszOptions, "SOURCE_ICC_PROFILE"); + if (pszICCProfile == NULL) + pszICCProfile = poSrcDS->GetMetadataItem( "SOURCE_ICC_PROFILE", "COLOR_PROFILE" ); + + if (pszICCProfile != NULL) + JPGAddICCProfile( &sCInfo, pszICCProfile, + jpeg_write_m_header, jpeg_write_m_byte ); + +/* -------------------------------------------------------------------- */ +/* Does the source have a mask? If so, we will append it to the */ +/* jpeg file after the imagery. */ +/* -------------------------------------------------------------------- */ + int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags(); + int bAppendMask =( !(nMaskFlags & GMF_ALL_VALID) + && (nBands == 1 || (nMaskFlags & GMF_PER_DATASET)) ); + + bAppendMask &= CSLFetchBoolean( papszOptions, "INTERNAL_MASK", TRUE ); + +/* -------------------------------------------------------------------- */ +/* Loop over image, copying image data. */ +/* -------------------------------------------------------------------- */ + GByte *pabyScanline; + CPLErr eErr = CE_None; + int nWorkDTSize = GDALGetDataTypeSize(eWorkDT) / 8; + bool bClipWarn = false; + + pabyScanline = (GByte *) CPLMalloc( nBands * nXSize * nWorkDTSize ); + + for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ ) + { + JSAMPLE *ppSamples; + + eErr = poSrcDS->RasterIO( GF_Read, 0, iLine, nXSize, 1, + pabyScanline, nXSize, 1, eWorkDT, + nBands, NULL, + nBands*nWorkDTSize, + nBands * nXSize * nWorkDTSize, nWorkDTSize, NULL ); + + // clamp 16bit values to 12bit. + if( nWorkDTSize == 2 ) + { + GUInt16 *panScanline = (GUInt16 *) pabyScanline; + int iPixel; + + for( iPixel = 0; iPixel < nXSize*nBands; iPixel++ ) + { + if( panScanline[iPixel] > 4095 ) + { + panScanline[iPixel] = 4095; + if( !bClipWarn ) + { + bClipWarn = true; + CPLError( CE_Warning, CPLE_AppDefined, + "One or more pixels clipped to fit 12bit domain for jpeg output." ); + } + } + } + } + + ppSamples = (JSAMPLE *) pabyScanline; + + if( eErr == CE_None ) + jpeg_write_scanlines( &sCInfo, &ppSamples, 1 ); + + if( eErr == CE_None + && !pfnProgress( (iLine+1) / ((bAppendMask ? 2 : 1) * (double) nYSize), + NULL, pProgressData ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup and close. */ +/* -------------------------------------------------------------------- */ + CPLFree( pabyScanline ); + + if( eErr == CE_None ) + jpeg_finish_compress( &sCInfo ); + jpeg_destroy_compress( &sCInfo ); + + VSIFCloseL( fpImage ); + + if( eErr != CE_None ) + { + VSIUnlink( pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Append masks to the jpeg file if necessary. */ +/* -------------------------------------------------------------------- */ + if( bAppendMask ) + { + CPLDebug( "JPEG", "Appending Mask Bitmap" ); + + void* pScaledData = GDALCreateScaledProgress( 0.5, 1, pfnProgress, pProgressData ); + eErr = JPGAppendMask( pszFilename, poSrcDS->GetRasterBand(1)->GetMaskBand(), + GDALScaledProgress, pScaledData ); + GDALDestroyScaledProgress( pScaledData ); + nCloneFlags &= (~GCIF_MASK); + + if( eErr != CE_None ) + { + VSIUnlink( pszFilename ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Do we need a world file? */ +/* -------------------------------------------------------------------- */ + if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) ) + { + double adfGeoTransform[6]; + + poSrcDS->GetGeoTransform( adfGeoTransform ); + GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform ); + } + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + + /* If outputing to stdout, we can't reopen it, so we'll return */ + /* a fake dataset to make the caller happy */ + if( CSLTestBoolean(CPLGetConfigOption("GDAL_OPEN_AFTER_COPY", "YES")) ) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + JPGDataset *poDS = (JPGDataset*) Open( pszFilename, NULL, NULL, 1, TRUE ); + CPLPopErrorHandler(); + if( poDS ) + { + poDS->CloneInfo( poSrcDS, nCloneFlags ); + return poDS; + } + + CPLErrorReset(); + } + + JPGDataset* poJPG_DS = new JPGDataset(); + poJPG_DS->nRasterXSize = nXSize; + poJPG_DS->nRasterYSize = nYSize; + for(int i=0;iSetBand( i+1, JPGCreateBand( poJPG_DS, i+1) ); + return poJPG_DS; +} + +/************************************************************************/ +/* GDALRegister_JPEG() */ +/************************************************************************/ + +#if !defined(JPGDataset) + +class GDALJPGDriver: public GDALDriver +{ + public: + GDALJPGDriver() {} + + char **GetMetadata( const char * pszDomain = "" ); + const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + +}; + +char** GDALJPGDriver::GetMetadata( const char * pszDomain ) +{ + GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST); + return GDALDriver::GetMetadata(pszDomain); +} + +static void GDALJPEGIsArithmeticCodingAvailableErrorExit(j_common_ptr cinfo) +{ + jmp_buf* p_setjmp_buffer = (jmp_buf* ) cinfo->client_data; + /* Return control to the setjmp point */ + longjmp(*p_setjmp_buffer, 1); +} + +/* Runtime check if arithmetic coding is available */ +static int GDALJPEGIsArithmeticCodingAvailable() +{ + struct jpeg_compress_struct sCInfo; + struct jpeg_error_mgr sJErr; + jmp_buf setjmp_buffer; + if (setjmp(setjmp_buffer)) + { + jpeg_destroy_compress( &sCInfo ); + return FALSE; + } + sCInfo.err = jpeg_std_error( &sJErr ); + sJErr.error_exit = GDALJPEGIsArithmeticCodingAvailableErrorExit; + sCInfo.client_data = (void *) &(setjmp_buffer); + jpeg_create_compress( &sCInfo ); + /* Hopefully nothing will be written */ + jpeg_stdio_dest(&sCInfo, stderr); + sCInfo.image_width = 1; + sCInfo.image_height = 1; + sCInfo.input_components = 1; + sCInfo.in_color_space = JCS_UNKNOWN; + jpeg_set_defaults( &sCInfo ); + sCInfo.arith_code = TRUE; + jpeg_start_compress( &sCInfo, FALSE ); + jpeg_abort_compress( &sCInfo ); + jpeg_destroy_compress( &sCInfo ); + + return TRUE; +} + +const char *GDALJPGDriver::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if( pszName != NULL && EQUAL(pszName, GDAL_DMD_CREATIONOPTIONLIST) && + (pszDomain == NULL || EQUAL(pszDomain, "")) && + GDALDriver::GetMetadataItem(pszName, pszDomain) == NULL ) + { + CPLString osCreationOptions = +"\n" +" " +#endif +" \n"; + SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, osCreationOptions ); + } + return GDALDriver::GetMetadataItem(pszName, pszDomain); +} + +void GDALRegister_JPEG() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "JPEG" ) == NULL ) + { + poDriver = new GDALJPGDriver(); + + poDriver->SetDescription( "JPEG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "JPEG JFIF" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_jpeg.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jpg" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "jpg jpeg" ); + poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/jpeg" ); + +#if defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12) + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16" ); +#else + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte" ); +#endif + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = JPGDatasetCommon::Identify; + poDriver->pfnOpen = JPGDatasetCommon::Open; + poDriver->pfnCreateCopy = JPGDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} +#endif diff --git a/bazaar/plugin/gdal/frmts/jpeg/jpgdataset_12.cpp b/bazaar/plugin/gdal/frmts/jpeg/jpgdataset_12.cpp new file mode 100644 index 000000000..611537036 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/jpgdataset_12.cpp @@ -0,0 +1,56 @@ +/****************************************************************************** + * $Id: jpgdataset_12.cpp 28028 2014-11-30 11:46:11Z rouault $ + * + * Project: JPEG JFIF Driver + * Purpose: Implement GDAL JPEG Support based on IJG libjpeg. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#if defined(JPEG_DUAL_MODE_8_12) +#define LIBJPEG_12_PATH "libjpeg12/jpeglib.h" +#define JPGDataset JPGDataset12 +#include "jpgdataset.cpp" + +GDALDataset* JPEGDataset12Open(const char* pszFilename, + VSILFILE* fpLin, + char** papszSiblingFiles, + int nScaleFactor, + int bDoPAMInitialize) +{ + return JPGDataset12::Open(pszFilename, fpLin, papszSiblingFiles, nScaleFactor, + bDoPAMInitialize); +} + +GDALDataset* JPEGDataset12CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + return JPGDataset12::CreateCopy(pszFilename, poSrcDS, + bStrict, papszOptions, + pfnProgress, pProgressData); +} + +#endif /* defined(JPEG_DUAL_MODE_8_12) */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/README b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/README new file mode 100644 index 000000000..065dbcf7a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/README @@ -0,0 +1,395 @@ +GDAL specific +------------- + +The patch http://src.chromium.org/viewvc/chrome/trunk/src/third_party/libjpeg/jdmarker.c?view=patch&r1=228354&r2=228353&pathrev=228354 +for CVE-2013-6629 has been applied on top of libjpeg 6b. +See https://bugzilla.redhat.com/show_bug.cgi?id=1031734 + +End of GDAL specific +-------------------- + +The Independent JPEG Group's JPEG software +========================================== + +README for release 6b of 27-Mar-1998 +==================================== + +This distribution contains the sixth public release of the Independent JPEG +Group's free JPEG software. You are welcome to redistribute this software and +to use it for any purpose, subject to the conditions under LEGAL ISSUES, below. + +Serious users of this software (particularly those incorporating it into +larger programs) should contact IJG at jpeg-info@uunet.uu.net to be added to +our electronic mailing list. Mailing list members are notified of updates +and have a chance to participate in technical discussions, etc. + +This software is the work of Tom Lane, Philip Gladstone, Jim Boucher, +Lee Crocker, Julian Minguillon, Luis Ortiz, George Phillips, Davide Rossi, +Guido Vollbeding, Ge' Weijers, and other members of the Independent JPEG +Group. + +IJG is not affiliated with the official ISO JPEG standards committee. + + +DOCUMENTATION ROADMAP +===================== + +This file contains the following sections: + +OVERVIEW General description of JPEG and the IJG software. +LEGAL ISSUES Copyright, lack of warranty, terms of distribution. +REFERENCES Where to learn more about JPEG. +ARCHIVE LOCATIONS Where to find newer versions of this software. +RELATED SOFTWARE Other stuff you should get. +FILE FORMAT WARS Software *not* to get. +TO DO Plans for future IJG releases. + +Other documentation files in the distribution are: + +User documentation: + install.doc How to configure and install the IJG software. + usage.doc Usage instructions for cjpeg, djpeg, jpegtran, + rdjpgcom, and wrjpgcom. + *.1 Unix-style man pages for programs (same info as usage.doc). + wizard.doc Advanced usage instructions for JPEG wizards only. + change.log Version-to-version change highlights. +Programmer and internal documentation: + libjpeg.doc How to use the JPEG library in your own programs. + example.c Sample code for calling the JPEG library. + structure.doc Overview of the JPEG library's internal structure. + filelist.doc Road map of IJG files. + coderules.doc Coding style rules --- please read if you contribute code. + +Please read at least the files install.doc and usage.doc. Useful information +can also be found in the JPEG FAQ (Frequently Asked Questions) article. See +ARCHIVE LOCATIONS below to find out where to obtain the FAQ article. + +If you want to understand how the JPEG code works, we suggest reading one or +more of the REFERENCES, then looking at the documentation files (in roughly +the order listed) before diving into the code. + + +OVERVIEW +======== + +This package contains C software to implement JPEG image compression and +decompression. JPEG (pronounced "jay-peg") is a standardized compression +method for full-color and gray-scale images. JPEG is intended for compressing +"real-world" scenes; line drawings, cartoons and other non-realistic images +are not its strong suit. JPEG is lossy, meaning that the output image is not +exactly identical to the input image. Hence you must not use JPEG if you +have to have identical output bits. However, on typical photographic images, +very good compression levels can be obtained with no visible change, and +remarkably high compression levels are possible if you can tolerate a +low-quality image. For more details, see the references, or just experiment +with various compression settings. + +This software implements JPEG baseline, extended-sequential, and progressive +compression processes. Provision is made for supporting all variants of these +processes, although some uncommon parameter settings aren't implemented yet. +For legal reasons, we are not distributing code for the arithmetic-coding +variants of JPEG; see LEGAL ISSUES. We have made no provision for supporting +the hierarchical or lossless processes defined in the standard. + +We provide a set of library routines for reading and writing JPEG image files, +plus two sample applications "cjpeg" and "djpeg", which use the library to +perform conversion between JPEG and some other popular image file formats. +The library is intended to be reused in other applications. + +In order to support file conversion and viewing software, we have included +considerable functionality beyond the bare JPEG coding/decoding capability; +for example, the color quantization modules are not strictly part of JPEG +decoding, but they are essential for output to colormapped file formats or +colormapped displays. These extra functions can be compiled out of the +library if not required for a particular application. We have also included +"jpegtran", a utility for lossless transcoding between different JPEG +processes, and "rdjpgcom" and "wrjpgcom", two simple applications for +inserting and extracting textual comments in JFIF files. + +The emphasis in designing this software has been on achieving portability and +flexibility, while also making it fast enough to be useful. In particular, +the software is not intended to be read as a tutorial on JPEG. (See the +REFERENCES section for introductory material.) Rather, it is intended to +be reliable, portable, industrial-strength code. We do not claim to have +achieved that goal in every aspect of the software, but we strive for it. + +We welcome the use of this software as a component of commercial products. +No royalty is required, but we do ask for an acknowledgement in product +documentation, as described under LEGAL ISSUES. + + +LEGAL ISSUES +============ + +In plain English: + +1. We don't promise that this software works. (But if you find any bugs, + please let us know!) +2. You can use this software for whatever you want. You don't have to pay us. +3. You may not pretend that you wrote this software. If you use it in a + program, you must acknowledge somewhere in your documentation that + you've used the IJG code. + +In legalese: + +The authors make NO WARRANTY or representation, either express or implied, +with respect to this software, its quality, accuracy, merchantability, or +fitness for a particular purpose. This software is provided "AS IS", and you, +its user, assume the entire risk as to its quality and accuracy. + +This software is copyright (C) 1991-1998, Thomas G. Lane. +All Rights Reserved except as specified below. + +Permission is hereby granted to use, copy, modify, and distribute this +software (or portions thereof) for any purpose, without fee, subject to these +conditions: +(1) If any part of the source code for this software is distributed, then this +README file must be included, with this copyright and no-warranty notice +unaltered; and any additions, deletions, or changes to the original files +must be clearly indicated in accompanying documentation. +(2) If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the work of +the Independent JPEG Group". +(3) Permission for use of this software is granted only if the user accepts +full responsibility for any undesirable consequences; the authors accept +NO LIABILITY for damages of any kind. + +These conditions apply to any software derived from or based on the IJG code, +not just to the unmodified library. If you use our work, you ought to +acknowledge us. + +Permission is NOT granted for the use of any IJG author's name or company name +in advertising or publicity relating to this software or products derived from +it. This software may be referred to only as "the Independent JPEG Group's +software". + +We specifically permit and encourage the use of this software as the basis of +commercial products, provided that all warranty or liability claims are +assumed by the product vendor. + + +ansi2knr.c is included in this distribution by permission of L. Peter Deutsch, +sole proprietor of its copyright holder, Aladdin Enterprises of Menlo Park, CA. +ansi2knr.c is NOT covered by the above copyright and conditions, but instead +by the usual distribution terms of the Free Software Foundation; principally, +that you must include source code if you redistribute it. (See the file +ansi2knr.c for full details.) However, since ansi2knr.c is not needed as part +of any program generated from the IJG code, this does not limit you more than +the foregoing paragraphs do. + +The Unix configuration script "configure" was produced with GNU Autoconf. +It is copyright by the Free Software Foundation but is freely distributable. +The same holds for its supporting scripts (config.guess, config.sub, +ltconfig, ltmain.sh). Another support script, install-sh, is copyright +by M.I.T. but is also freely distributable. + +It appears that the arithmetic coding option of the JPEG spec is covered by +patents owned by IBM, AT&T, and Mitsubishi. Hence arithmetic coding cannot +legally be used without obtaining one or more licenses. For this reason, +support for arithmetic coding has been removed from the free JPEG software. +(Since arithmetic coding provides only a marginal gain over the unpatented +Huffman mode, it is unlikely that very many implementations will support it.) +So far as we are aware, there are no patent restrictions on the remaining +code. + +The IJG distribution formerly included code to read and write GIF files. +To avoid entanglement with the Unisys LZW patent, GIF reading support has +been removed altogether, and the GIF writer has been simplified to produce +"uncompressed GIFs". This technique does not use the LZW algorithm; the +resulting GIF files are larger than usual, but are readable by all standard +GIF decoders. + +We are required to state that + "The Graphics Interchange Format(c) is the Copyright property of + CompuServe Incorporated. GIF(sm) is a Service Mark property of + CompuServe Incorporated." + + +REFERENCES +========== + +We highly recommend reading one or more of these references before trying to +understand the innards of the JPEG software. + +The best short technical introduction to the JPEG compression algorithm is + Wallace, Gregory K. "The JPEG Still Picture Compression Standard", + Communications of the ACM, April 1991 (vol. 34 no. 4), pp. 30-44. +(Adjacent articles in that issue discuss MPEG motion picture compression, +applications of JPEG, and related topics.) If you don't have the CACM issue +handy, a PostScript file containing a revised version of Wallace's article is +available at ftp://ftp.uu.net/graphics/jpeg/wallace.ps.gz. The file (actually +a preprint for an article that appeared in IEEE Trans. Consumer Electronics) +omits the sample images that appeared in CACM, but it includes corrections +and some added material. Note: the Wallace article is copyright ACM and IEEE, +and it may not be used for commercial purposes. + +A somewhat less technical, more leisurely introduction to JPEG can be found in +"The Data Compression Book" by Mark Nelson and Jean-loup Gailly, published by +M&T Books (New York), 2nd ed. 1996, ISBN 1-55851-434-1. This book provides +good explanations and example C code for a multitude of compression methods +including JPEG. It is an excellent source if you are comfortable reading C +code but don't know much about data compression in general. The book's JPEG +sample code is far from industrial-strength, but when you are ready to look +at a full implementation, you've got one here... + +The best full description of JPEG is the textbook "JPEG Still Image Data +Compression Standard" by William B. Pennebaker and Joan L. Mitchell, published +by Van Nostrand Reinhold, 1993, ISBN 0-442-01272-1. Price US$59.95, 638 pp. +The book includes the complete text of the ISO JPEG standards (DIS 10918-1 +and draft DIS 10918-2). This is by far the most complete exposition of JPEG +in existence, and we highly recommend it. + +The JPEG standard itself is not available electronically; you must order a +paper copy through ISO or ITU. (Unless you feel a need to own a certified +official copy, we recommend buying the Pennebaker and Mitchell book instead; +it's much cheaper and includes a great deal of useful explanatory material.) +In the USA, copies of the standard may be ordered from ANSI Sales at (212) +642-4900, or from Global Engineering Documents at (800) 854-7179. (ANSI +doesn't take credit card orders, but Global does.) It's not cheap: as of +1992, ANSI was charging $95 for Part 1 and $47 for Part 2, plus 7% +shipping/handling. The standard is divided into two parts, Part 1 being the +actual specification, while Part 2 covers compliance testing methods. Part 1 +is titled "Digital Compression and Coding of Continuous-tone Still Images, +Part 1: Requirements and guidelines" and has document numbers ISO/IEC IS +10918-1, ITU-T T.81. Part 2 is titled "Digital Compression and Coding of +Continuous-tone Still Images, Part 2: Compliance testing" and has document +numbers ISO/IEC IS 10918-2, ITU-T T.83. + +Some extensions to the original JPEG standard are defined in JPEG Part 3, +a newer ISO standard numbered ISO/IEC IS 10918-3 and ITU-T T.84. IJG +currently does not support any Part 3 extensions. + +The JPEG standard does not specify all details of an interchangeable file +format. For the omitted details we follow the "JFIF" conventions, revision +1.02. A copy of the JFIF spec is available from: + Literature Department + C-Cube Microsystems, Inc. + 1778 McCarthy Blvd. + Milpitas, CA 95035 + phone (408) 944-6300, fax (408) 944-6314 +A PostScript version of this document is available by FTP at +ftp://ftp.uu.net/graphics/jpeg/jfif.ps.gz. There is also a plain text +version at ftp://ftp.uu.net/graphics/jpeg/jfif.txt.gz, but it is missing +the figures. + +The TIFF 6.0 file format specification can be obtained by FTP from +ftp://ftp.sgi.com/graphics/tiff/TIFF6.ps.gz. The JPEG incorporation scheme +found in the TIFF 6.0 spec of 3-June-92 has a number of serious problems. +IJG does not recommend use of the TIFF 6.0 design (TIFF Compression tag 6). +Instead, we recommend the JPEG design proposed by TIFF Technical Note #2 +(Compression tag 7). Copies of this Note can be obtained from ftp.sgi.com or +from ftp://ftp.uu.net/graphics/jpeg/. It is expected that the next revision +of the TIFF spec will replace the 6.0 JPEG design with the Note's design. +Although IJG's own code does not support TIFF/JPEG, the free libtiff library +uses our library to implement TIFF/JPEG per the Note. libtiff is available +from ftp://ftp.sgi.com/graphics/tiff/. + + +ARCHIVE LOCATIONS +================= + +The "official" archive site for this software is ftp.uu.net (Internet +address 192.48.96.9). The most recent released version can always be found +there in directory graphics/jpeg. This particular version will be archived +as ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz. If you don't have +direct Internet access, UUNET's archives are also available via UUCP; contact +help@uunet.uu.net for information on retrieving files that way. + +Numerous Internet sites maintain copies of the UUNET files. However, only +ftp.uu.net is guaranteed to have the latest official version. + +You can also obtain this software in DOS-compatible "zip" archive format from +the SimTel archives (ftp://ftp.simtel.net/pub/simtelnet/msdos/graphics/), or +on CompuServe in the Graphics Support forum (GO CIS:GRAPHSUP), library 12 +"JPEG Tools". Again, these versions may sometimes lag behind the ftp.uu.net +release. + +The JPEG FAQ (Frequently Asked Questions) article is a useful source of +general information about JPEG. It is updated constantly and therefore is +not included in this distribution. The FAQ is posted every two weeks to +Usenet newsgroups comp.graphics.misc, news.answers, and other groups. +It is available on the World Wide Web at http://www.faqs.org/faqs/jpeg-faq/ +and other news.answers archive sites, including the official news.answers +archive at rtfm.mit.edu: ftp://rtfm.mit.edu/pub/usenet/news.answers/jpeg-faq/. +If you don't have Web or FTP access, send e-mail to mail-server@rtfm.mit.edu +with body + send usenet/news.answers/jpeg-faq/part1 + send usenet/news.answers/jpeg-faq/part2 + + +RELATED SOFTWARE +================ + +Numerous viewing and image manipulation programs now support JPEG. (Quite a +few of them use this library to do so.) The JPEG FAQ described above lists +some of the more popular free and shareware viewers, and tells where to +obtain them on Internet. + +If you are on a Unix machine, we highly recommend Jef Poskanzer's free +PBMPLUS software, which provides many useful operations on PPM-format image +files. In particular, it can convert PPM images to and from a wide range of +other formats, thus making cjpeg/djpeg considerably more useful. The latest +version is distributed by the NetPBM group, and is available from numerous +sites, notably ftp://wuarchive.wustl.edu/graphics/graphics/packages/NetPBM/. +Unfortunately PBMPLUS/NETPBM is not nearly as portable as the IJG software is; +you are likely to have difficulty making it work on any non-Unix machine. + +A different free JPEG implementation, written by the PVRG group at Stanford, +is available from ftp://havefun.stanford.edu/pub/jpeg/. This program +is designed for research and experimentation rather than production use; +it is slower, harder to use, and less portable than the IJG code, but it +is easier to read and modify. Also, the PVRG code supports lossless JPEG, +which we do not. (On the other hand, it doesn't do progressive JPEG.) + + +FILE FORMAT WARS +================ + +Some JPEG programs produce files that are not compatible with our library. +The root of the problem is that the ISO JPEG committee failed to specify a +concrete file format. Some vendors "filled in the blanks" on their own, +creating proprietary formats that no one else could read. (For example, none +of the early commercial JPEG implementations for the Macintosh were able to +exchange compressed files.) + +The file format we have adopted is called JFIF (see REFERENCES). This format +has been agreed to by a number of major commercial JPEG vendors, and it has +become the de facto standard. JFIF is a minimal or "low end" representation. +We recommend the use of TIFF/JPEG (TIFF revision 6.0 as modified by TIFF +Technical Note #2) for "high end" applications that need to record a lot of +additional data about an image. TIFF/JPEG is fairly new and not yet widely +supported, unfortunately. + +The upcoming JPEG Part 3 standard defines a file format called SPIFF. +SPIFF is interoperable with JFIF, in the sense that most JFIF decoders should +be able to read the most common variant of SPIFF. SPIFF has some technical +advantages over JFIF, but its major claim to fame is simply that it is an +official standard rather than an informal one. At this point it is unclear +whether SPIFF will supersede JFIF or whether JFIF will remain the de-facto +standard. IJG intends to support SPIFF once the standard is frozen, but we +have not decided whether it should become our default output format or not. +(In any case, our decoder will remain capable of reading JFIF indefinitely.) + +Various proprietary file formats incorporating JPEG compression also exist. +We have little or no sympathy for the existence of these formats. Indeed, +one of the original reasons for developing this free software was to help +force convergence on common, open format standards for JPEG files. Don't +use a proprietary file format! + + +TO DO +===== + +The major thrust for v7 will probably be improvement of visual quality. +The current method for scaling the quantization tables is known not to be +very good at low Q values. We also intend to investigate block boundary +smoothing, "poor man's variable quantization", and other means of improving +quality-vs-file-size performance without sacrificing compatibility. + +In future versions, we are considering supporting some of the upcoming JPEG +Part 3 extensions --- principally, variable quantization and the SPIFF file +format. + +As always, speeding things up is of great interest. + +Please send bug reports, offers of help, etc. to jpeg-info@uunet.uu.net. diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcapimin.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcapimin.c new file mode 100644 index 000000000..54fb8c58c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcapimin.c @@ -0,0 +1,280 @@ +/* + * jcapimin.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the compression half + * of the JPEG library. These are the "minimum" API routines that may be + * needed in either the normal full-compression case or the transcoding-only + * case. + * + * Most of the routines intended to be called directly by an application + * are in this file or in jcapistd.c. But also see jcparam.c for + * parameter-setup helper routines, jcomapi.c for routines shared by + * compression and decompression, and jctrans.c for the transcoding case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Initialization of a JPEG compression object. + * The error manager must already be set up (in case memory manager fails). + */ + +GLOBAL(void) +jpeg_CreateCompress (j_compress_ptr cinfo, int version, size_t structsize) +{ + int i; + + /* Guard against version mismatches between library and caller. */ + cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */ + if (version != JPEG_LIB_VERSION) + ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); + if (structsize != SIZEOF(struct jpeg_compress_struct)) + ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, + (int) SIZEOF(struct jpeg_compress_struct), (int) structsize); + + /* For debugging purposes, we zero the whole master structure. + * But the application has already set the err pointer, and may have set + * client_data, so we have to save and restore those fields. + * Note: if application hasn't set client_data, tools like Purify may + * complain here. + */ + { + struct jpeg_error_mgr * err = cinfo->err; + void * client_data = cinfo->client_data; /* ignore Purify complaint here */ + MEMZERO(cinfo, SIZEOF(struct jpeg_compress_struct)); + cinfo->err = err; + cinfo->client_data = client_data; + } + cinfo->is_decompressor = FALSE; + + /* Initialize a memory manager instance for this object */ + jinit_memory_mgr((j_common_ptr) cinfo); + + /* Zero out pointers to permanent structures. */ + cinfo->progress = NULL; + cinfo->dest = NULL; + + cinfo->comp_info = NULL; + + for (i = 0; i < NUM_QUANT_TBLS; i++) + cinfo->quant_tbl_ptrs[i] = NULL; + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + cinfo->dc_huff_tbl_ptrs[i] = NULL; + cinfo->ac_huff_tbl_ptrs[i] = NULL; + } + + cinfo->script_space = NULL; + + cinfo->input_gamma = 1.0; /* in case application forgets */ + + /* OK, I'm ready */ + cinfo->global_state = CSTATE_START; +} + + +/* + * Destruction of a JPEG compression object + */ + +GLOBAL(void) +jpeg_destroy_compress (j_compress_ptr cinfo) +{ + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Abort processing of a JPEG compression operation, + * but don't destroy the object itself. + */ + +GLOBAL(void) +jpeg_abort_compress (j_compress_ptr cinfo) +{ + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Forcibly suppress or un-suppress all quantization and Huffman tables. + * Marks all currently defined tables as already written (if suppress) + * or not written (if !suppress). This will control whether they get emitted + * by a subsequent jpeg_start_compress call. + * + * This routine is exported for use by applications that want to produce + * abbreviated JPEG datastreams. It logically belongs in jcparam.c, but + * since it is called by jpeg_start_compress, we put it here --- otherwise + * jcparam.o would be linked whether the application used it or not. + */ + +GLOBAL(void) +jpeg_suppress_tables (j_compress_ptr cinfo, boolean suppress) +{ + int i; + JQUANT_TBL * qtbl; + JHUFF_TBL * htbl; + + for (i = 0; i < NUM_QUANT_TBLS; i++) { + if ((qtbl = cinfo->quant_tbl_ptrs[i]) != NULL) + qtbl->sent_table = suppress; + } + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + if ((htbl = cinfo->dc_huff_tbl_ptrs[i]) != NULL) + htbl->sent_table = suppress; + if ((htbl = cinfo->ac_huff_tbl_ptrs[i]) != NULL) + htbl->sent_table = suppress; + } +} + + +/* + * Finish JPEG compression. + * + * If a multipass operating mode was selected, this may do a great deal of + * work including most of the actual output. + */ + +GLOBAL(void) +jpeg_finish_compress (j_compress_ptr cinfo) +{ + JDIMENSION iMCU_row; + + if (cinfo->global_state == CSTATE_SCANNING || + cinfo->global_state == CSTATE_RAW_OK) { + /* Terminate first pass */ + if (cinfo->next_scanline < cinfo->image_height) + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + (*cinfo->master->finish_pass) (cinfo); + } else if (cinfo->global_state != CSTATE_WRCOEFS) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Perform any remaining passes */ + while (! cinfo->master->is_last_pass) { + (*cinfo->master->prepare_for_pass) (cinfo); + for (iMCU_row = 0; iMCU_row < cinfo->total_iMCU_rows; iMCU_row++) { + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) iMCU_row; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + /* We bypass the main controller and invoke coef controller directly; + * all work is being done from the coefficient buffer. + */ + if (! (*cinfo->coef->compress_data) (cinfo, (JSAMPIMAGE) NULL)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + } + (*cinfo->master->finish_pass) (cinfo); + } + /* Write EOI, do final cleanup */ + (*cinfo->marker->write_file_trailer) (cinfo); + (*cinfo->dest->term_destination) (cinfo); + /* We can use jpeg_abort to release memory and reset global_state */ + jpeg_abort((j_common_ptr) cinfo); +} + + +/* + * Write a special marker. + * This is only recommended for writing COM or APPn markers. + * Must be called after jpeg_start_compress() and before + * first call to jpeg_write_scanlines() or jpeg_write_raw_data(). + */ + +GLOBAL(void) +jpeg_write_marker (j_compress_ptr cinfo, int marker, + const JOCTET *dataptr, unsigned int datalen) +{ + JMETHOD(void, write_marker_byte, (j_compress_ptr info, int val)); + + if (cinfo->next_scanline != 0 || + (cinfo->global_state != CSTATE_SCANNING && + cinfo->global_state != CSTATE_RAW_OK && + cinfo->global_state != CSTATE_WRCOEFS)) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + (*cinfo->marker->write_marker_header) (cinfo, marker, datalen); + write_marker_byte = cinfo->marker->write_marker_byte; /* copy for speed */ + while (datalen--) { + (*write_marker_byte) (cinfo, *dataptr); + dataptr++; + } +} + +/* Same, but piecemeal. */ + +GLOBAL(void) +jpeg_write_m_header (j_compress_ptr cinfo, int marker, unsigned int datalen) +{ + if (cinfo->next_scanline != 0 || + (cinfo->global_state != CSTATE_SCANNING && + cinfo->global_state != CSTATE_RAW_OK && + cinfo->global_state != CSTATE_WRCOEFS)) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + (*cinfo->marker->write_marker_header) (cinfo, marker, datalen); +} + +GLOBAL(void) +jpeg_write_m_byte (j_compress_ptr cinfo, int val) +{ + (*cinfo->marker->write_marker_byte) (cinfo, val); +} + + +/* + * Alternate compression function: just write an abbreviated table file. + * Before calling this, all parameters and a data destination must be set up. + * + * To produce a pair of files containing abbreviated tables and abbreviated + * image data, one would proceed as follows: + * + * initialize JPEG object + * set JPEG parameters + * set destination to table file + * jpeg_write_tables(cinfo); + * set destination to image file + * jpeg_start_compress(cinfo, FALSE); + * write data... + * jpeg_finish_compress(cinfo); + * + * jpeg_write_tables has the side effect of marking all tables written + * (same as jpeg_suppress_tables(..., TRUE)). Thus a subsequent start_compress + * will not re-emit the tables unless it is passed write_all_tables=TRUE. + */ + +GLOBAL(void) +jpeg_write_tables (j_compress_ptr cinfo) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Initialize the marker writer ... bit of a crock to do it here. */ + jinit_marker_writer(cinfo); + /* Write them tables! */ + (*cinfo->marker->write_tables_only) (cinfo); + /* And clean up. */ + (*cinfo->dest->term_destination) (cinfo); + /* + * In library releases up through v6a, we called jpeg_abort() here to free + * any working memory allocated by the destination manager and marker + * writer. Some applications had a problem with that: they allocated space + * of their own from the library memory manager, and didn't want it to go + * away during write_tables. So now we do nothing. This will cause a + * memory leak if an app calls write_tables repeatedly without doing a full + * compression cycle or otherwise resetting the JPEG object. However, that + * seems less bad than unexpectedly freeing memory in the normal case. + * An app that prefers the old behavior can call jpeg_abort for itself after + * each call to jpeg_write_tables(). + */ +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcapistd.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcapistd.c new file mode 100644 index 000000000..c0320b1b1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcapistd.c @@ -0,0 +1,161 @@ +/* + * jcapistd.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the compression half + * of the JPEG library. These are the "standard" API routines that are + * used in the normal full-compression case. They are not used by a + * transcoding-only application. Note that if an application links in + * jpeg_start_compress, it will end up linking in the entire compressor. + * We thus must separate this file from jcapimin.c to avoid linking the + * whole compression library into a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Compression initialization. + * Before calling this, all parameters and a data destination must be set up. + * + * We require a write_all_tables parameter as a failsafe check when writing + * multiple datastreams from the same compression object. Since prior runs + * will have left all the tables marked sent_table=TRUE, a subsequent run + * would emit an abbreviated stream (no tables) by default. This may be what + * is wanted, but for safety's sake it should not be the default behavior: + * programmers should have to make a deliberate choice to emit abbreviated + * images. Therefore the documentation and examples should encourage people + * to pass write_all_tables=TRUE; then it will take active thought to do the + * wrong thing. + */ + +GLOBAL(void) +jpeg_start_compress (j_compress_ptr cinfo, boolean write_all_tables) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (write_all_tables) + jpeg_suppress_tables(cinfo, FALSE); /* mark all tables to be written */ + + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Perform master selection of active modules */ + jinit_compress_master(cinfo); + /* Set up for the first pass */ + (*cinfo->master->prepare_for_pass) (cinfo); + /* Ready for application to drive first pass through jpeg_write_scanlines + * or jpeg_write_raw_data. + */ + cinfo->next_scanline = 0; + cinfo->global_state = (cinfo->raw_data_in ? CSTATE_RAW_OK : CSTATE_SCANNING); +} + + +/* + * Write some scanlines of data to the JPEG compressor. + * + * The return value will be the number of lines actually written. + * This should be less than the supplied num_lines only in case that + * the data destination module has requested suspension of the compressor, + * or if more than image_height scanlines are passed in. + * + * Note: we warn about excess calls to jpeg_write_scanlines() since + * this likely signals an application programmer error. However, + * excess scanlines passed in the last valid call are *silently* ignored, + * so that the application need not adjust num_lines for end-of-image + * when using a multiple-scanline buffer. + */ + +GLOBAL(JDIMENSION) +jpeg_write_scanlines (j_compress_ptr cinfo, JSAMPARRAY scanlines, + JDIMENSION num_lines) +{ + JDIMENSION row_ctr, rows_left; + + if (cinfo->global_state != CSTATE_SCANNING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->next_scanline >= cinfo->image_height) + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->next_scanline; + cinfo->progress->pass_limit = (long) cinfo->image_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Give master control module another chance if this is first call to + * jpeg_write_scanlines. This lets output of the frame/scan headers be + * delayed so that application can write COM, etc, markers between + * jpeg_start_compress and jpeg_write_scanlines. + */ + if (cinfo->master->call_pass_startup) + (*cinfo->master->pass_startup) (cinfo); + + /* Ignore any extra scanlines at bottom of image. */ + rows_left = cinfo->image_height - cinfo->next_scanline; + if (num_lines > rows_left) + num_lines = rows_left; + + row_ctr = 0; + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, num_lines); + cinfo->next_scanline += row_ctr; + return row_ctr; +} + + +/* + * Alternate entry point to write raw data. + * Processes exactly one iMCU row per call, unless suspended. + */ + +GLOBAL(JDIMENSION) +jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION num_lines) +{ + JDIMENSION lines_per_iMCU_row; + + if (cinfo->global_state != CSTATE_RAW_OK) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->next_scanline >= cinfo->image_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->next_scanline; + cinfo->progress->pass_limit = (long) cinfo->image_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Give master control module another chance if this is first call to + * jpeg_write_raw_data. This lets output of the frame/scan headers be + * delayed so that application can write COM, etc, markers between + * jpeg_start_compress and jpeg_write_raw_data. + */ + if (cinfo->master->call_pass_startup) + (*cinfo->master->pass_startup) (cinfo); + + /* Verify that at least one iMCU row has been passed. */ + lines_per_iMCU_row = cinfo->max_v_samp_factor * DCTSIZE; + if (num_lines < lines_per_iMCU_row) + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + /* Directly compress the row. */ + if (! (*cinfo->coef->compress_data) (cinfo, data)) { + /* If compressor did not consume the whole row, suspend processing. */ + return 0; + } + + /* OK, we processed one iMCU row. */ + cinfo->next_scanline += lines_per_iMCU_row; + return lines_per_iMCU_row; +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jccoefct.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jccoefct.c new file mode 100644 index 000000000..6c1d57f19 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jccoefct.c @@ -0,0 +1,450 @@ +/* + * jccoefct.c + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the coefficient buffer controller for compression. + * This controller is the top level of the JPEG compressor proper. + * The coefficient buffer lies between forward-DCT and entropy encoding steps. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#include "cpl_port.h" + +/* We use a full-image coefficient buffer when doing Huffman optimization, + * and also for writing multiple-scan JPEG files. In all cases, the DCT + * step is run during the first pass, and subsequent passes need only read + * the buffered coefficients. + */ +#ifdef ENTROPY_OPT_SUPPORTED +#define FULL_COEF_BUFFER_SUPPORTED +#else +#ifdef C_MULTISCAN_FILES_SUPPORTED +#define FULL_COEF_BUFFER_SUPPORTED +#endif +#endif + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* For single-pass compression, it's sufficient to buffer just one MCU + * (although this may prove a bit slow in practice). We allocate a + * workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each + * MCU constructed and sent. (On 80x86, the workspace is FAR even though + * it's not really very big; this is to keep the module interfaces unchanged + * when a large coefficient buffer is necessary.) + * In multi-pass modes, this array points to the current MCU's blocks + * within the virtual arrays. + */ + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; + + /* In multi-pass modes, we need a virtual block array for each component. */ + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + + +/* Forward declarations */ +METHODDEF(boolean) compress_data + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); +#ifdef FULL_COEF_BUFFER_SUPPORTED +METHODDEF(boolean) compress_first_pass + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); +METHODDEF(boolean) compress_output + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); +#endif + + +LOCAL(void) +start_iMCU_row (j_compress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->mcu_ctr = 0; + coef->MCU_vert_offset = 0; +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + coef->iMCU_row_num = 0; + start_iMCU_row(cinfo); + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (coef->whole_image[0] != NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + coef->pub.compress_data = compress_data; + break; +#ifdef FULL_COEF_BUFFER_SUPPORTED + case JBUF_SAVE_AND_PASS: + if (coef->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + coef->pub.compress_data = compress_first_pass; + break; + case JBUF_CRANK_DEST: + if (coef->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + coef->pub.compress_data = compress_output; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +/* + * Process some data in the single-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the image. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf contains a plane for each component in image, + * which we index according to the component's SOF position. + */ + +METHODDEF(boolean) +compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, bi, ci, yindex, yoffset, blockcnt; + JDIMENSION ypos, xpos; + jpeg_component_info *compptr; + + /* Loop to write as much as one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num <= last_MCU_col; + MCU_col_num++) { + /* Determine where data comes from in input_buf and do the DCT thing. + * Each call on forward_DCT processes a horizontal row of DCT blocks + * as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks + * sequentially. Dummy blocks at the right or bottom edge are filled in + * specially. The data in them does not matter for image reconstruction, + * so we fill them with values that will encode to the smallest amount of + * data, viz: all zeroes in the AC entries, DC entries equal to previous + * block's DC value. (Thanks to Thomas Kinsman for this idea.) + */ + blkn = 0; + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + xpos = MCU_col_num * compptr->MCU_sample_width; + ypos = yoffset * DCTSIZE; /* ypos == (yoffset+yindex) * DCTSIZE */ + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (coef->iMCU_row_num < last_iMCU_row || + yoffset+yindex < compptr->last_row_height) { + (*cinfo->fdct->forward_DCT) (cinfo, compptr, + input_buf[compptr->component_index], + coef->MCU_buffer[blkn], + ypos, xpos, (JDIMENSION) blockcnt); + if (blockcnt < compptr->MCU_width) { + /* Create some dummy blocks at the right edge of the image. */ + jzero_far((void FAR *) coef->MCU_buffer[blkn + blockcnt], + (compptr->MCU_width - blockcnt) * SIZEOF(JBLOCK)); + for (bi = blockcnt; bi < compptr->MCU_width; bi++) { + coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn+bi-1][0][0]; + } + } + } else { + /* Create a row of dummy blocks at the bottom of the image. */ + jzero_far((void FAR *) coef->MCU_buffer[blkn], + compptr->MCU_width * SIZEOF(JBLOCK)); + for (bi = 0; bi < compptr->MCU_width; bi++) { + coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn-1][0][0]; + } + } + blkn += compptr->MCU_width; + ypos += DCTSIZE; + } + } + /* Try to write the MCU. In event of a suspension failure, we will + * re-DCT the MCU on restart (a bit inefficient, could be fixed...) + */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + + +#ifdef FULL_COEF_BUFFER_SUPPORTED + +/* + * Process some data in the first pass of a multi-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the image. + * This amount of data is read from the source buffer, DCT'd and quantized, + * and saved into the virtual arrays. We also generate suitable dummy blocks + * as needed at the right and lower edges. (The dummy blocks are constructed + * in the virtual arrays, which have been padded appropriately.) This makes + * it possible for subsequent passes not to worry about real vs. dummy blocks. + * + * We must also emit the data to the entropy encoder. This is conveniently + * done by calling compress_output() after we've loaded the current strip + * of the virtual arrays. + * + * NB: input_buf contains a plane for each component in image. All + * components are DCT'd and loaded into the virtual arrays in this pass. + * However, it may be that only a subset of the components are emitted to + * the entropy encoder during this first pass; be careful about looking + * at the scan-dependent variables (MCU dimensions, etc). + */ + +METHODDEF(boolean) +compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION blocks_across, MCUs_across, MCUindex; + int bi, ci, h_samp_factor, block_row, block_rows, ndummy; + JCOEF lastDC; + jpeg_component_info *compptr; + JBLOCKARRAY buffer; + JBLOCKROW thisblockrow, lastblockrow; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Align the virtual buffer for this component. */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, TRUE); + /* Count non-dummy DCT block rows in this iMCU row. */ + if (coef->iMCU_row_num < last_iMCU_row) + block_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here, since may not be set! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + } + blocks_across = compptr->width_in_blocks; + h_samp_factor = compptr->h_samp_factor; + /* Count number of dummy blocks to be added at the right margin. */ + ndummy = (int) (blocks_across % h_samp_factor); + if (ndummy > 0) + ndummy = h_samp_factor - ndummy; + /* Perform DCT for all non-dummy blocks in this iMCU row. Each call + * on forward_DCT processes a complete horizontal row of DCT blocks. + */ + for (block_row = 0; block_row < block_rows; block_row++) { + thisblockrow = buffer[block_row]; + (*cinfo->fdct->forward_DCT) (cinfo, compptr, + input_buf[ci], thisblockrow, + (JDIMENSION) (block_row * DCTSIZE), + (JDIMENSION) 0, blocks_across); + if (ndummy > 0) { + /* Create dummy blocks at the right edge of the image. */ + thisblockrow += blocks_across; /* => first dummy block */ + jzero_far((void FAR *) thisblockrow, ndummy * SIZEOF(JBLOCK)); + lastDC = thisblockrow[-1][0]; + for (bi = 0; bi < ndummy; bi++) { + thisblockrow[bi][0] = lastDC; + } + } + } + /* If at end of image, create dummy block rows as needed. + * The tricky part here is that within each MCU, we want the DC values + * of the dummy blocks to match the last real block's DC value. + * This squeezes a few more bytes out of the resulting file... + */ + if (coef->iMCU_row_num == last_iMCU_row) { + blocks_across += ndummy; /* include lower right corner */ + MCUs_across = blocks_across / h_samp_factor; + for (block_row = block_rows; block_row < compptr->v_samp_factor; + block_row++) { + thisblockrow = buffer[block_row]; + lastblockrow = buffer[block_row-1]; + jzero_far((void FAR *) thisblockrow, + (size_t) (blocks_across * SIZEOF(JBLOCK))); + for (MCUindex = 0; MCUindex < MCUs_across; MCUindex++) { + lastDC = lastblockrow[h_samp_factor-1][0]; + for (bi = 0; bi < h_samp_factor; bi++) { + thisblockrow[bi][0] = lastDC; + } + thisblockrow += h_samp_factor; /* advance to next MCU in row */ + lastblockrow += h_samp_factor; + } + } + } + } + /* NB: compress_output will increment iMCU_row_num if successful. + * A suspension return will result in redoing all the work above next time. + */ + + /* Emit data to the entropy encoder, sharing code with subsequent passes */ + return compress_output(cinfo, input_buf); +} + + +/* + * Process some data in subsequent passes of a multi-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the scan. + * The data is obtained from the virtual arrays and fed to the entropy coder. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf is ignored; it is likely to be a NULL pointer. + */ + +METHODDEF(boolean) +compress_output (j_compress_ptr cinfo, CPL_UNUSED JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + int blkn, ci, xindex, yindex, yoffset; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. + * NB: during first pass, this is safe only because the buffers will + * already be aligned properly, so jmemmgr.c won't need to do any I/O. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + coef->MCU_buffer[blkn++] = buffer_ptr++; + } + } + } + /* Try to write the MCU. */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + +#endif /* FULL_COEF_BUFFER_SUPPORTED */ + + +/* + * Initialize coefficient buffer controller. + */ + +GLOBAL(void) +jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_coef_ptr coef; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef; + + /* Create the coefficient buffer. */ + if (need_full_buffer) { +#ifdef FULL_COEF_BUFFER_SUPPORTED + /* Allocate a full-image virtual array for each component, */ + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + int ci; + jpeg_component_info *compptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) compptr->v_samp_factor); + } +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + } else { + /* We only need a single-MCU buffer. */ + JBLOCKROW buffer; + int i; + + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { + coef->MCU_buffer[i] = buffer + i; + } + coef->whole_image[0] = NULL; /* flag for no virtual arrays */ + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jccolor.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jccolor.c new file mode 100644 index 000000000..bbd44b304 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jccolor.c @@ -0,0 +1,460 @@ +/* + * jccolor.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains input colorspace conversion routines. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#include "cpl_port.h" + +/* Private subobject */ + +typedef struct { + struct jpeg_color_converter pub; /* public fields */ + + /* Private state for RGB->YCC conversion */ + INT32 * rgb_ycc_tab; /* => table for RGB to YCbCr conversion */ +} my_color_converter; + +typedef my_color_converter * my_cconvert_ptr; + + +/**************** RGB -> YCbCr conversion: most common case **************/ + +/* + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * Y = 0.29900 * R + 0.58700 * G + 0.11400 * B + * Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTERJSAMPLE + * Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTERJSAMPLE + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + * Note: older versions of the IJG code used a zero offset of MAXJSAMPLE/2, + * rather than CENTERJSAMPLE, for Cb and Cr. This gave equal positive and + * negative swings for Cb/Cr, but meant that grayscale values (Cb=Cr=0) + * were not represented exactly. Now we sacrifice exact representation of + * maximum red and maximum blue in order to get exact grayscales. + * + * To avoid floating-point arithmetic, we represent the fractional constants + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + * the products by 2^16, with appropriate rounding, to get the correct answer. + * + * For even more speed, we avoid doing any multiplications in the inner loop + * by precalculating the constants times R,G,B for all possible values. + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + * for 12-bit samples it is still acceptable. It's not very reasonable for + * 16-bit samples, but if you want lossless storage you shouldn't be changing + * colorspace anyway. + * The CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included + * in the tables to save adding them separately in the inner loop. + */ + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define CBCR_OFFSET ((INT32) CENTERJSAMPLE << SCALEBITS) +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L< Y section */ +#define G_Y_OFF (1*(MAXJSAMPLE+1)) /* offset to G => Y section */ +#define B_Y_OFF (2*(MAXJSAMPLE+1)) /* etc. */ +#define R_CB_OFF (3*(MAXJSAMPLE+1)) +#define G_CB_OFF (4*(MAXJSAMPLE+1)) +#define B_CB_OFF (5*(MAXJSAMPLE+1)) +#define R_CR_OFF B_CB_OFF /* B=>Cb, R=>Cr are the same */ +#define G_CR_OFF (6*(MAXJSAMPLE+1)) +#define B_CR_OFF (7*(MAXJSAMPLE+1)) +#define TABLE_SIZE (8*(MAXJSAMPLE+1)) + + +/* + * Initialize for RGB->YCC colorspace conversion. + */ + +METHODDEF(void) +rgb_ycc_start (j_compress_ptr cinfo) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + INT32 * rgb_ycc_tab; + INT32 i; + + /* Allocate and fill in the conversion tables. */ + cconvert->rgb_ycc_tab = rgb_ycc_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (TABLE_SIZE * SIZEOF(INT32))); + + for (i = 0; i <= MAXJSAMPLE; i++) { + rgb_ycc_tab[i+R_Y_OFF] = FIX(0.29900) * i; + rgb_ycc_tab[i+G_Y_OFF] = FIX(0.58700) * i; + rgb_ycc_tab[i+B_Y_OFF] = FIX(0.11400) * i + ONE_HALF; + rgb_ycc_tab[i+R_CB_OFF] = (-FIX(0.16874)) * i; + rgb_ycc_tab[i+G_CB_OFF] = (-FIX(0.33126)) * i; + /* We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr. + * This ensures that the maximum output will round to MAXJSAMPLE + * not MAXJSAMPLE+1, and thus that we don't have to range-limit. + */ + rgb_ycc_tab[i+B_CB_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; +/* B=>Cb and R=>Cr tables are the same + rgb_ycc_tab[i+R_CR_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; +*/ + rgb_ycc_tab[i+G_CR_OFF] = (-FIX(0.41869)) * i; + rgb_ycc_tab[i+B_CR_OFF] = (-FIX(0.08131)) * i; + } +} + + +/* + * Convert some rows of samples to the JPEG colorspace. + * + * Note that we change from the application's interleaved-pixel format + * to our internal noninterleaved, one-plane-per-component format. + * The input buffer is therefore three times as wide as the output buffer. + * + * A starting row offset is provided only for the output buffer. The caller + * can easily adjust the passed input_buf value to accommodate any row + * offset required on that side. + */ + +METHODDEF(void) +rgb_ycc_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr0, outptr1, outptr2; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr0 = output_buf[0][output_row]; + outptr1 = output_buf[1][output_row]; + outptr2 = output_buf[2][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = GETJSAMPLE(inptr[RGB_RED]); + g = GETJSAMPLE(inptr[RGB_GREEN]); + b = GETJSAMPLE(inptr[RGB_BLUE]); + inptr += RGB_PIXELSIZE; + /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + * must be too; we do not need an explicit range-limiting operation. + * Hence the value being shifted is never negative, and we don't + * need the general RIGHT_SHIFT macro. + */ + /* Y */ + outptr0[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + /* Cb */ + outptr1[col] = (JSAMPLE) + ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF]) + >> SCALEBITS); + /* Cr */ + outptr2[col] = (JSAMPLE) + ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF]) + >> SCALEBITS); + } + } +} + + +/**************** Cases other than RGB -> YCbCr **************/ + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles RGB->grayscale conversion, which is the same + * as the RGB->Y portion of RGB->YCbCr. + * We assume rgb_ycc_start has been called (we only use the Y tables). + */ + +METHODDEF(void) +rgb_gray_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr = output_buf[0][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = GETJSAMPLE(inptr[RGB_RED]); + g = GETJSAMPLE(inptr[RGB_GREEN]); + b = GETJSAMPLE(inptr[RGB_BLUE]); + inptr += RGB_PIXELSIZE; + /* Y */ + outptr[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + } + } +} + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles Adobe-style CMYK->YCCK conversion, + * where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same + * conversion as above, while passing K (black) unchanged. + * We assume rgb_ycc_start has been called. + */ + +METHODDEF(void) +cmyk_ycck_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr0, outptr1, outptr2, outptr3; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr0 = output_buf[0][output_row]; + outptr1 = output_buf[1][output_row]; + outptr2 = output_buf[2][output_row]; + outptr3 = output_buf[3][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = MAXJSAMPLE - GETJSAMPLE(inptr[0]); + g = MAXJSAMPLE - GETJSAMPLE(inptr[1]); + b = MAXJSAMPLE - GETJSAMPLE(inptr[2]); + /* K passes through as-is */ + outptr3[col] = inptr[3]; /* don't need GETJSAMPLE here */ + inptr += 4; + /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + * must be too; we do not need an explicit range-limiting operation. + * Hence the value being shifted is never negative, and we don't + * need the general RIGHT_SHIFT macro. + */ + /* Y */ + outptr0[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + /* Cb */ + outptr1[col] = (JSAMPLE) + ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF]) + >> SCALEBITS); + /* Cr */ + outptr2[col] = (JSAMPLE) + ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF]) + >> SCALEBITS); + } + } +} + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles grayscale output with no conversion. + * The source can be either plain grayscale or YCbCr (since Y == gray). + */ + +METHODDEF(void) +grayscale_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + int instride = cinfo->input_components; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr = output_buf[0][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + outptr[col] = inptr[0]; /* don't need GETJSAMPLE() here */ + inptr += instride; + } + } +} + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles multi-component colorspaces without conversion. + * We assume input_components == num_components. + */ + +METHODDEF(void) +null_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + register int ci; + int nc = cinfo->num_components; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + /* It seems fastest to make a separate pass for each component. */ + for (ci = 0; ci < nc; ci++) { + inptr = *input_buf; + outptr = output_buf[ci][output_row]; + for (col = 0; col < num_cols; col++) { + outptr[col] = inptr[ci]; /* don't need GETJSAMPLE() here */ + inptr += nc; + } + } + input_buf++; + output_row++; + } +} + + +/* + * Empty method for start_pass. + */ + +METHODDEF(void) +null_method (CPL_UNUSED j_compress_ptr cinfo) +{ + /* no work needed */ +} + + +/* + * Module initialization routine for input colorspace conversion. + */ + +GLOBAL(void) +jinit_color_converter (j_compress_ptr cinfo) +{ + my_cconvert_ptr cconvert; + + cconvert = (my_cconvert_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_color_converter)); + cinfo->cconvert = (struct jpeg_color_converter *) cconvert; + /* set start_pass to null method until we find out differently */ + cconvert->pub.start_pass = null_method; + + /* Make sure input_components agrees with in_color_space */ + switch (cinfo->in_color_space) { + case JCS_GRAYSCALE: + if (cinfo->input_components != 1) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + + case JCS_RGB: +#if RGB_PIXELSIZE != 3 + if (cinfo->input_components != RGB_PIXELSIZE) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; +#endif /* else share code with YCbCr */ + + case JCS_YCbCr: + if (cinfo->input_components != 3) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + + case JCS_CMYK: + case JCS_YCCK: + if (cinfo->input_components != 4) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + + default: /* JCS_UNKNOWN can be anything */ + if (cinfo->input_components < 1) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + } + + /* Check num_components, set conversion method based on requested space */ + switch (cinfo->jpeg_color_space) { + case JCS_GRAYSCALE: + if (cinfo->num_components != 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_GRAYSCALE) + cconvert->pub.color_convert = grayscale_convert; + else if (cinfo->in_color_space == JCS_RGB) { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = rgb_gray_convert; + } else if (cinfo->in_color_space == JCS_YCbCr) + cconvert->pub.color_convert = grayscale_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_RGB: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_RGB && RGB_PIXELSIZE == 3) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_YCbCr: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_RGB) { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = rgb_ycc_convert; + } else if (cinfo->in_color_space == JCS_YCbCr) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_CMYK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_CMYK) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_YCCK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_CMYK) { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = cmyk_ycck_convert; + } else if (cinfo->in_color_space == JCS_YCCK) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + default: /* allow null conversion of JCS_UNKNOWN */ + if (cinfo->jpeg_color_space != cinfo->in_color_space || + cinfo->num_components != cinfo->input_components) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + cconvert->pub.color_convert = null_convert; + break; + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcdctmgr.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcdctmgr.c new file mode 100644 index 000000000..61fa79b9e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcdctmgr.c @@ -0,0 +1,387 @@ +/* + * jcdctmgr.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the forward-DCT management logic. + * This code selects a particular DCT implementation to be used, + * and it performs related housekeeping chores including coefficient + * quantization. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + + +/* Private subobject for this module */ + +typedef struct { + struct jpeg_forward_dct pub; /* public fields */ + + /* Pointer to the DCT routine actually in use */ + forward_DCT_method_ptr do_dct; + + /* The actual post-DCT divisors --- not identical to the quant table + * entries, because of scaling (especially for an unnormalized DCT). + * Each table is given in normal array order. + */ + DCTELEM * divisors[NUM_QUANT_TBLS]; + +#ifdef DCT_FLOAT_SUPPORTED + /* Same as above for the floating-point case. */ + float_DCT_method_ptr do_float_dct; + FAST_FLOAT * float_divisors[NUM_QUANT_TBLS]; +#endif +} my_fdct_controller; + +typedef my_fdct_controller * my_fdct_ptr; + + +/* + * Initialize for a processing pass. + * Verify that all referenced Q-tables are present, and set up + * the divisor table for each one. + * In the current implementation, DCT of all components is done during + * the first pass, even if only some components will be output in the + * first scan. Hence all components should be examined here. + */ + +METHODDEF(void) +start_pass_fdctmgr (j_compress_ptr cinfo) +{ + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + int ci, qtblno, i; + jpeg_component_info *compptr; + JQUANT_TBL * qtbl; + DCTELEM * dtbl; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + qtblno = compptr->quant_tbl_no; + /* Make sure specified quantization table is present */ + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + cinfo->quant_tbl_ptrs[qtblno] == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + qtbl = cinfo->quant_tbl_ptrs[qtblno]; + /* Compute divisors for this quant table */ + /* We may do this more than once for same table, but it's not a big deal */ + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + /* For LL&M IDCT method, divisors are equal to raw quantization + * coefficients multiplied by 8 (to counteract scaling). + */ + if (fdct->divisors[qtblno] == NULL) { + fdct->divisors[qtblno] = (DCTELEM *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(DCTELEM)); + } + dtbl = fdct->divisors[qtblno]; + for (i = 0; i < DCTSIZE2; i++) { + dtbl[i] = ((DCTELEM) qtbl->quantval[i]) << 3; + } + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + { + /* For AA&N IDCT method, divisors are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * We apply a further scale factor of 8. + */ +#define CONST_BITS 14 + static const INT16 aanscales[DCTSIZE2] = { + /* precomputed values scaled up by 14 bits */ + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + }; + SHIFT_TEMPS + + if (fdct->divisors[qtblno] == NULL) { + fdct->divisors[qtblno] = (DCTELEM *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(DCTELEM)); + } + dtbl = fdct->divisors[qtblno]; + for (i = 0; i < DCTSIZE2; i++) { + dtbl[i] = (DCTELEM) + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[i], + (INT32) aanscales[i]), + CONST_BITS-3); + } + } + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + { + /* For float AA&N IDCT method, divisors are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * We apply a further scale factor of 8. + * What's actually stored is 1/divisor so that the inner loop can + * use a multiplication rather than a division. + */ + FAST_FLOAT * fdtbl; + int row, col; + static const double aanscalefactor[DCTSIZE] = { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + + if (fdct->float_divisors[qtblno] == NULL) { + fdct->float_divisors[qtblno] = (FAST_FLOAT *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(FAST_FLOAT)); + } + fdtbl = fdct->float_divisors[qtblno]; + i = 0; + for (row = 0; row < DCTSIZE; row++) { + for (col = 0; col < DCTSIZE; col++) { + fdtbl[i] = (FAST_FLOAT) + (1.0 / (((double) qtbl->quantval[i] * + aanscalefactor[row] * aanscalefactor[col] * 8.0))); + i++; + } + } + } + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + } +} + + +/* + * Perform forward DCT on one or more blocks of a component. + * + * The input samples are taken from the sample_data[] array starting at + * position start_row/start_col, and moving to the right for any additional + * blocks. The quantized coefficients are returned in coef_blocks[]. + */ + +METHODDEF(void) +forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks) +/* This version is used for integer DCT implementations. */ +{ + /* This routine is heavily used, so it's worth coding it tightly. */ + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + forward_DCT_method_ptr do_dct = fdct->do_dct; + DCTELEM * divisors = fdct->divisors[compptr->quant_tbl_no]; + DCTELEM workspace[DCTSIZE2]; /* work area for FDCT subroutine */ + JDIMENSION bi; + + sample_data += start_row; /* fold in the vertical offset once */ + + for (bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) { + /* Load data into workspace, applying unsigned->signed conversion */ + { register DCTELEM *workspaceptr; + register JSAMPROW elemptr; + register int elemr; + + workspaceptr = workspace; + for (elemr = 0; elemr < DCTSIZE; elemr++) { + elemptr = sample_data[elemr] + start_col; +#if DCTSIZE == 8 /* unroll the inner loop */ + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; +#else + { register int elemc; + for (elemc = DCTSIZE; elemc > 0; elemc--) { + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + } + } +#endif + } + } + + /* Perform the DCT */ + (*do_dct) (workspace); + + /* Quantize/descale the coefficients, and store into coef_blocks[] */ + { register DCTELEM temp, qval; + register int i; + register JCOEFPTR output_ptr = coef_blocks[bi]; + + for (i = 0; i < DCTSIZE2; i++) { + qval = divisors[i]; + temp = workspace[i]; + /* Divide the coefficient value by qval, ensuring proper rounding. + * Since C does not specify the direction of rounding for negative + * quotients, we have to force the dividend positive for portability. + * + * In most files, at least half of the output values will be zero + * (at default quantization settings, more like three-quarters...) + * so we should ensure that this case is fast. On many machines, + * a comparison is enough cheaper than a divide to make a special test + * a win. Since both inputs will be nonnegative, we need only test + * for a < b to discover whether a/b is 0. + * If your machine's division is fast enough, define FAST_DIVIDE. + */ +#ifdef FAST_DIVIDE +#define DIVIDE_BY(a,b) a /= b +#else +#define DIVIDE_BY(a,b) if (a >= b) a /= b; else a = 0 +#endif + if (temp < 0) { + temp = -temp; + temp += qval>>1; /* for rounding */ + DIVIDE_BY(temp, qval); + temp = -temp; + } else { + temp += qval>>1; /* for rounding */ + DIVIDE_BY(temp, qval); + } + output_ptr[i] = (JCOEF) temp; + } + } + } +} + + +#ifdef DCT_FLOAT_SUPPORTED + +METHODDEF(void) +forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks) +/* This version is used for floating-point DCT implementations. */ +{ + /* This routine is heavily used, so it's worth coding it tightly. */ + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + float_DCT_method_ptr do_dct = fdct->do_float_dct; + FAST_FLOAT * divisors = fdct->float_divisors[compptr->quant_tbl_no]; + FAST_FLOAT workspace[DCTSIZE2]; /* work area for FDCT subroutine */ + JDIMENSION bi; + + sample_data += start_row; /* fold in the vertical offset once */ + + for (bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) { + /* Load data into workspace, applying unsigned->signed conversion */ + { register FAST_FLOAT *workspaceptr; + register JSAMPROW elemptr; + register int elemr; + + workspaceptr = workspace; + for (elemr = 0; elemr < DCTSIZE; elemr++) { + elemptr = sample_data[elemr] + start_col; +#if DCTSIZE == 8 /* unroll the inner loop */ + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); +#else + { register int elemc; + for (elemc = DCTSIZE; elemc > 0; elemc--) { + *workspaceptr++ = (FAST_FLOAT) + (GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + } + } +#endif + } + } + + /* Perform the DCT */ + (*do_dct) (workspace); + + /* Quantize/descale the coefficients, and store into coef_blocks[] */ + { register FAST_FLOAT temp; + register int i; + register JCOEFPTR output_ptr = coef_blocks[bi]; + + for (i = 0; i < DCTSIZE2; i++) { + /* Apply the quantization and scaling factor */ + temp = workspace[i] * divisors[i]; + /* Round to nearest integer. + * Since C does not specify the direction of rounding for negative + * quotients, we have to force the dividend positive for portability. + * The maximum coefficient size is +-16K (for 12-bit data), so this + * code should work for either 16-bit or 32-bit ints. + */ + output_ptr[i] = (JCOEF) ((int) (temp + (FAST_FLOAT) 16384.5) - 16384); + } + } + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ + + +/* + * Initialize FDCT manager. + */ + +GLOBAL(void) +jinit_forward_dct (j_compress_ptr cinfo) +{ + my_fdct_ptr fdct; + int i; + + fdct = (my_fdct_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_fdct_controller)); + cinfo->fdct = (struct jpeg_forward_dct *) fdct; + fdct->pub.start_pass = start_pass_fdctmgr; + + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + fdct->pub.forward_DCT = forward_DCT; + fdct->do_dct = jpeg_fdct_islow; + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + fdct->pub.forward_DCT = forward_DCT; + fdct->do_dct = jpeg_fdct_ifast; + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + fdct->pub.forward_DCT = forward_DCT_float; + fdct->do_float_dct = jpeg_fdct_float; + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + + /* Mark divisor tables unallocated */ + for (i = 0; i < NUM_QUANT_TBLS; i++) { + fdct->divisors[i] = NULL; +#ifdef DCT_FLOAT_SUPPORTED + fdct->float_divisors[i] = NULL; +#endif + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jchuff.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jchuff.c new file mode 100644 index 000000000..5737ba9b6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jchuff.c @@ -0,0 +1,912 @@ +/* + * jchuff.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy encoding routines. + * + * Much of the complexity here has to do with supporting output suspension. + * If the data destination module demands suspension, we want to be able to + * back up to the start of the current MCU. To do this, we copy state + * variables into local working storage, and update them back to the + * permanent JPEG objects only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jchuff.h" /* Declarations shared with jcphuff.c */ + + +/* Expanded entropy encoder object for Huffman encoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + INT32 put_buffer; /* current bit-accumulation buffer */ + int put_bits; /* # of bits now in it */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).put_buffer = (src).put_buffer, \ + (dest).put_bits = (src).put_bits, \ + (dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + + savable_state saved; /* Bit buffer & DC state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + int next_restart_num; /* next restart number to write (0-7) */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + c_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + c_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + +#ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ + long * dc_count_ptrs[NUM_HUFF_TBLS]; + long * ac_count_ptrs[NUM_HUFF_TBLS]; +#endif +} huff_entropy_encoder; + +typedef huff_entropy_encoder * huff_entropy_ptr; + +/* Working state while writing an MCU. + * This struct contains all the fields that are needed by subroutines. + */ + +typedef struct { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + savable_state cur; /* Current bit buffer & DC state */ + j_compress_ptr cinfo; /* dump_buffer needs access to this */ +} working_state; + + +/* Forward declarations */ +METHODDEF(boolean) encode_mcu_huff JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_huff JPP((j_compress_ptr cinfo)); +#ifdef ENTROPY_OPT_SUPPORTED +METHODDEF(boolean) encode_mcu_gather JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_gather JPP((j_compress_ptr cinfo)); +#endif + + +/* + * Initialize for a Huffman-compressed scan. + * If gather_statistics is TRUE, we do not output anything during the scan, + * just count the Huffman symbols used and generate Huffman code tables. + */ + +METHODDEF(void) +start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + entropy->pub.encode_mcu = encode_mcu_gather; + entropy->pub.finish_pass = finish_pass_gather; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + entropy->pub.encode_mcu = encode_mcu_huff; + entropy->pub.finish_pass = finish_pass_huff; + } + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + /* Check for invalid table indexes */ + /* (make_c_derived_tbl does this in the other path) */ + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + if (actbl < 0 || actbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl); + /* Allocate and zero the statistics tables */ + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + if (entropy->dc_count_ptrs[dctbl] == NULL) + entropy->dc_count_ptrs[dctbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->dc_count_ptrs[dctbl], 257 * SIZEOF(long)); + if (entropy->ac_count_ptrs[actbl] == NULL) + entropy->ac_count_ptrs[actbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->ac_count_ptrs[actbl], 257 * SIZEOF(long)); +#endif + } else { + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_c_derived_tbl(cinfo, TRUE, dctbl, + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_c_derived_tbl(cinfo, FALSE, actbl, + & entropy->ac_derived_tbls[actbl]); + } + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Initialize bit buffer to empty */ + entropy->saved.put_buffer = 0; + entropy->saved.put_bits = 0; + + /* Initialize restart stuff */ + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num = 0; +} + + +/* + * Compute the derived values for a Huffman table. + * This routine also performs some validation checks on the table. + * + * Note this is also used by jcphuff.c. + */ + +GLOBAL(void) +jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, + c_derived_tbl ** pdtbl) +{ + JHUFF_TBL *htbl; + c_derived_tbl *dtbl; + int p, i, l, lastp, si, maxsymbol; + char huffsize[257]; + unsigned int huffcode[257]; + unsigned int code; + + /* Note that huffsize[] and huffcode[] are filled in code-length order, + * paralleling the order of the symbols themselves in htbl->huffval[]. + */ + + /* Find the input Huffman table */ + if (tblno < 0 || tblno >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + htbl = + isDC ? cinfo->dc_huff_tbl_ptrs[tblno] : cinfo->ac_huff_tbl_ptrs[tblno]; + if (htbl == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + + /* Allocate a workspace if we haven't already done so. */ + if (*pdtbl == NULL) + *pdtbl = (c_derived_tbl *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(c_derived_tbl)); + dtbl = *pdtbl; + + /* Figure C.1: make table of Huffman code length for each symbol */ + + p = 0; + for (l = 1; l <= 16; l++) { + i = (int) htbl->bits[l]; + if (i < 0 || p + i > 256) /* protect against table overrun */ + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + while (i--) + huffsize[p++] = (char) l; + } + huffsize[p] = 0; + lastp = p; + + /* Figure C.2: generate the codes themselves */ + /* We also validate that the counts represent a legal Huffman code tree. */ + + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p]) { + while (((int) huffsize[p]) == si) { + huffcode[p++] = code; + code++; + } + /* code is now 1 more than the last code used for codelength si; but + * it must still fit in si bits, since no code is allowed to be all ones. + */ + if (((INT32) code) >= (((INT32) 1) << si)) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + code <<= 1; + si++; + } + + /* Figure C.3: generate encoding tables */ + /* These are code and size indexed by symbol value */ + + /* Set all codeless symbols to have code length 0; + * this lets us detect duplicate VAL entries here, and later + * allows emit_bits to detect any attempt to emit such symbols. + */ + MEMZERO(dtbl->ehufsi, SIZEOF(dtbl->ehufsi)); + + /* This is also a convenient place to check for out-of-range + * and duplicated VAL entries. We allow 0..255 for AC symbols + * but only 0..15 for DC. (We could constrain them further + * based on data depth and mode, but this seems enough.) + */ + maxsymbol = isDC ? 15 : 255; + + for (p = 0; p < lastp; p++) { + i = htbl->huffval[p]; + if (i < 0 || i > maxsymbol || dtbl->ehufsi[i]) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + dtbl->ehufco[i] = huffcode[p]; + dtbl->ehufsi[i] = huffsize[p]; + } +} + + +/* Outputting bytes to the file */ + +/* Emit a byte, taking 'action' if must suspend. */ +#define emit_byte(state,val,action) \ + { *(state)->next_output_byte++ = (JOCTET) (val); \ + if (--(state)->free_in_buffer == 0) \ + if (! dump_buffer(state)) \ + { action; } } + + +LOCAL(boolean) +dump_buffer (working_state * state) +/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */ +{ + struct jpeg_destination_mgr * dest = state->cinfo->dest; + + dest->next_output_byte = state->next_output_byte; + dest->free_in_buffer = state->free_in_buffer; + + if (! (*dest->empty_output_buffer) (state->cinfo)) + return FALSE; + /* After a successful buffer dump, must reset buffer pointers */ + state->next_output_byte = dest->next_output_byte; + state->free_in_buffer = dest->free_in_buffer; + return TRUE; +} + + +/* Outputting bits to the file */ + +/* Only the right 24 bits of put_buffer are used; the valid bits are + * left-justified in this part. At most 16 bits can be passed to emit_bits + * in one call, and we never retain more than 7 bits in put_buffer + * between calls, so 24 bits are sufficient. + */ + +INLINE +LOCAL(boolean) +emit_bits (working_state * state, unsigned int code, int size) +/* Emit some bits; return TRUE if successful, FALSE if must suspend */ +{ + /* This routine is heavily used, so it's worth coding tightly. */ + register INT32 put_buffer = (INT32) code; + register int put_bits = state->cur.put_bits; + + /* if size is 0, caller used an invalid Huffman table entry */ + if (size == 0) + ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE); + + put_buffer &= (((INT32) 1)<cur.put_buffer; /* and merge with old buffer contents */ + + while (put_bits >= 8) { + int c = (int) ((put_buffer >> 16) & 0xFF); + + emit_byte(state, c, return FALSE); + if (c == 0xFF) { /* need to stuff a zero byte? */ + emit_byte(state, 0, return FALSE); + } + put_buffer <<= 8; + put_bits -= 8; + } + + state->cur.put_buffer = put_buffer; /* update state variables */ + state->cur.put_bits = put_bits; + + return TRUE; +} + + +LOCAL(boolean) +flush_bits (working_state * state) +{ + if (! emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ + return FALSE; + state->cur.put_buffer = 0; /* and reset bit-buffer to empty */ + state->cur.put_bits = 0; + return TRUE; +} + + +/* Encode a single block's worth of coefficients */ + +LOCAL(boolean) +encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, + c_derived_tbl *dctbl, c_derived_tbl *actbl) +{ + register int temp, temp2; + register int nbits; + register int k, r, i; + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + temp = temp2 = block[0] - last_dc_val; + + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); + + /* Emit the Huffman-coded symbol for the number of bits */ + if (! emit_bits(state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) + return FALSE; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (nbits) /* emit_bits rejects calls with size 0 */ + if (! emit_bits(state, (unsigned int) temp2, nbits)) + return FALSE; + + /* Encode the AC coefficients per section F.1.2.2 */ + + r = 0; /* r = run length of zeros */ + + for (k = 1; k < DCTSIZE2; k++) { + if ((temp = block[jpeg_natural_order[k]]) == 0) { + r++; + } else { + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + if (! emit_bits(state, actbl->ehufco[0xF0], actbl->ehufsi[0xF0])) + return FALSE; + r -= 16; + } + + temp2 = temp; + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); + + /* Emit Huffman symbol for run length / number of bits */ + i = (r << 4) + nbits; + if (! emit_bits(state, actbl->ehufco[i], actbl->ehufsi[i])) + return FALSE; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (! emit_bits(state, (unsigned int) temp2, nbits)) + return FALSE; + + r = 0; + } + } + + /* If the last coef(s) were zero, emit an end-of-block code */ + if (r > 0) + if (! emit_bits(state, actbl->ehufco[0], actbl->ehufsi[0])) + return FALSE; + + return TRUE; +} + + +/* + * Emit a restart marker & resynchronize predictions. + */ + +LOCAL(boolean) +emit_restart (working_state * state, int restart_num) +{ + int ci; + + if (! flush_bits(state)) + return FALSE; + + emit_byte(state, 0xFF, return FALSE); + emit_byte(state, JPEG_RST0 + restart_num, return FALSE); + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < state->cinfo->comps_in_scan; ci++) + state->cur.last_dc_val[ci] = 0; + + /* The restart counter is not updated until we successfully write the MCU. */ + + return TRUE; +} + + +/* + * Encode and output one MCU's worth of Huffman-compressed coefficients. + */ + +METHODDEF(boolean) +encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + working_state state; + int blkn, ci; + jpeg_component_info * compptr; + + /* Load up working state */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + ASSIGN_STATE(state.cur, entropy->saved); + state.cinfo = cinfo; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! emit_restart(&state, entropy->next_restart_num)) + return FALSE; + } + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + if (! encode_one_block(&state, + MCU_data[blkn][0], state.cur.last_dc_val[ci], + entropy->dc_derived_tbls[compptr->dc_tbl_no], + entropy->ac_derived_tbls[compptr->ac_tbl_no])) + return FALSE; + /* Update last_dc_val */ + state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + + /* Completed MCU, so update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + ASSIGN_STATE(entropy->saved, state.cur); + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * Finish up at the end of a Huffman-compressed scan. + */ + +METHODDEF(void) +finish_pass_huff (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + working_state state; + + /* Load up working state ... flush_bits needs it */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + ASSIGN_STATE(state.cur, entropy->saved); + state.cinfo = cinfo; + + /* Flush out the last data */ + if (! flush_bits(&state)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + + /* Update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + ASSIGN_STATE(entropy->saved, state.cur); +} + + +/* + * Huffman coding optimization. + * + * We first scan the supplied data and count the number of uses of each symbol + * that is to be Huffman-coded. (This process MUST agree with the code above.) + * Then we build a Huffman coding tree for the observed counts. + * Symbols which are not needed at all for the particular image are not + * assigned any code, which saves space in the DHT marker as well as in + * the compressed data. + */ + +#ifdef ENTROPY_OPT_SUPPORTED + + +/* Process a single block's worth of coefficients */ + +LOCAL(void) +htest_one_block (j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val, + long dc_counts[], long ac_counts[]) +{ + register int temp; + register int nbits; + register int k, r; + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + temp = block[0] - last_dc_val; + if (temp < 0) + temp = -temp; + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count the Huffman symbol for the number of bits */ + dc_counts[nbits]++; + + /* Encode the AC coefficients per section F.1.2.2 */ + + r = 0; /* r = run length of zeros */ + + for (k = 1; k < DCTSIZE2; k++) { + if ((temp = block[jpeg_natural_order[k]]) == 0) { + r++; + } else { + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + ac_counts[0xF0]++; + r -= 16; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + if (temp < 0) + temp = -temp; + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count Huffman symbol for run length / number of bits */ + ac_counts[(r << 4) + nbits]++; + + r = 0; + } + } + + /* If the last coef(s) were zero, emit an end-of-block code */ + if (r > 0) + ac_counts[0]++; +} + + +/* + * Trial-encode one MCU's worth of Huffman-compressed coefficients. + * No data is actually output, so no suspension return is possible. + */ + +METHODDEF(boolean) +encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int blkn, ci; + jpeg_component_info * compptr; + + /* Take care of restart intervals if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + /* Update restart state */ + entropy->restarts_to_go = cinfo->restart_interval; + } + entropy->restarts_to_go--; + } + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + htest_one_block(cinfo, MCU_data[blkn][0], entropy->saved.last_dc_val[ci], + entropy->dc_count_ptrs[compptr->dc_tbl_no], + entropy->ac_count_ptrs[compptr->ac_tbl_no]); + entropy->saved.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + + return TRUE; +} + + +/* + * Generate the best Huffman code table for the given counts, fill htbl. + * Note this is also used by jcphuff.c. + * + * The JPEG standard requires that no symbol be assigned a codeword of all + * one bits (so that padding bits added at the end of a compressed segment + * can't look like a valid code). Because of the canonical ordering of + * codewords, this just means that there must be an unused slot in the + * longest codeword length category. Section K.2 of the JPEG spec suggests + * reserving such a slot by pretending that symbol 256 is a valid symbol + * with count 1. In theory that's not optimal; giving it count zero but + * including it in the symbol set anyway should give a better Huffman code. + * But the theoretically better code actually seems to come out worse in + * practice, because it produces more all-ones bytes (which incur stuffed + * zero bytes in the final file). In any case the difference is tiny. + * + * The JPEG standard requires Huffman codes to be no more than 16 bits long. + * If some symbols have a very small but nonzero probability, the Huffman tree + * must be adjusted to meet the code length restriction. We currently use + * the adjustment method suggested in JPEG section K.2. This method is *not* + * optimal; it may not choose the best possible limited-length code. But + * typically only very-low-frequency symbols will be given less-than-optimal + * lengths, so the code is almost optimal. Experimental comparisons against + * an optimal limited-length-code algorithm indicate that the difference is + * microscopic --- usually less than a hundredth of a percent of total size. + * So the extra complexity of an optimal algorithm doesn't seem worthwhile. + */ + +GLOBAL(void) +jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) +{ +#define MAX_CLEN 32 /* assumed maximum initial code length */ + UINT8 bits[MAX_CLEN+1]; /* bits[k] = # of symbols with code length k */ + int codesize[257]; /* codesize[k] = code length of symbol k */ + int others[257]; /* next symbol in current branch of tree */ + int c1, c2; + int p, i, j; + long v; + + /* This algorithm is explained in section K.2 of the JPEG standard */ + + MEMZERO(bits, SIZEOF(bits)); + MEMZERO(codesize, SIZEOF(codesize)); + for (i = 0; i < 257; i++) + others[i] = -1; /* init links to empty */ + + freq[256] = 1; /* make sure 256 has a nonzero count */ + /* Including the pseudo-symbol 256 in the Huffman procedure guarantees + * that no real symbol is given code-value of all ones, because 256 + * will be placed last in the largest codeword category. + */ + + /* Huffman's basic algorithm to assign optimal code lengths to symbols */ + + for (;;) { + /* Find the smallest nonzero frequency, set c1 = its symbol */ + /* In case of ties, take the larger symbol number */ + c1 = -1; + v = 1000000000L; + for (i = 0; i <= 256; i++) { + if (freq[i] && freq[i] <= v) { + v = freq[i]; + c1 = i; + } + } + + /* Find the next smallest nonzero frequency, set c2 = its symbol */ + /* In case of ties, take the larger symbol number */ + c2 = -1; + v = 1000000000L; + for (i = 0; i <= 256; i++) { + if (freq[i] && freq[i] <= v && i != c1) { + v = freq[i]; + c2 = i; + } + } + + /* Done if we've merged everything into one frequency */ + if (c2 < 0) + break; + + /* Else merge the two counts/trees */ + freq[c1] += freq[c2]; + freq[c2] = 0; + + /* Increment the codesize of everything in c1's tree branch */ + codesize[c1]++; + while (others[c1] >= 0) { + c1 = others[c1]; + codesize[c1]++; + } + + others[c1] = c2; /* chain c2 onto c1's tree branch */ + + /* Increment the codesize of everything in c2's tree branch */ + codesize[c2]++; + while (others[c2] >= 0) { + c2 = others[c2]; + codesize[c2]++; + } + } + + /* Now count the number of symbols of each code length */ + for (i = 0; i <= 256; i++) { + if (codesize[i]) { + /* The JPEG standard seems to think that this can't happen, */ + /* but I'm paranoid... */ + if (codesize[i] > MAX_CLEN) + ERREXIT(cinfo, JERR_HUFF_CLEN_OVERFLOW); + + bits[codesize[i]]++; + } + } + + /* JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure + * Huffman procedure assigned any such lengths, we must adjust the coding. + * Here is what the JPEG spec says about how this next bit works: + * Since symbols are paired for the longest Huffman code, the symbols are + * removed from this length category two at a time. The prefix for the pair + * (which is one bit shorter) is allocated to one of the pair; then, + * skipping the BITS entry for that prefix length, a code word from the next + * shortest nonzero BITS entry is converted into a prefix for two code words + * one bit longer. + */ + + for (i = MAX_CLEN; i > 16; i--) { + while (bits[i] > 0) { + j = i - 2; /* find length of new prefix to be used */ + while (bits[j] == 0) + j--; + + bits[i] -= 2; /* remove two symbols */ + bits[i-1]++; /* one goes in this length */ + bits[j+1] += 2; /* two new symbols in this length */ + bits[j]--; /* symbol of this length is now a prefix */ + } + } + + /* Remove the count for the pseudo-symbol 256 from the largest codelength */ + while (bits[i] == 0) /* find largest codelength still in use */ + i--; + bits[i]--; + + /* Return final symbol counts (only for lengths 0..16) */ + MEMCOPY(htbl->bits, bits, SIZEOF(htbl->bits)); + + /* Return a list of the symbols sorted by code length */ + /* It's not real clear to me why we don't need to consider the codelength + * changes made above, but the JPEG spec seems to think this works. + */ + p = 0; + for (i = 1; i <= MAX_CLEN; i++) { + for (j = 0; j <= 255; j++) { + if (codesize[j] == i) { + htbl->huffval[p] = (UINT8) j; + p++; + } + } + } + + /* Set sent_table FALSE so updated table will be written to JPEG file. */ + htbl->sent_table = FALSE; +} + + +/* + * Finish up a statistics-gathering pass and create the new Huffman tables. + */ + +METHODDEF(void) +finish_pass_gather (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + JHUFF_TBL **htblptr; + boolean did_dc[NUM_HUFF_TBLS]; + boolean did_ac[NUM_HUFF_TBLS]; + + /* It's important not to apply jpeg_gen_optimal_table more than once + * per table, because it clobbers the input frequency counts! + */ + MEMZERO(did_dc, SIZEOF(did_dc)); + MEMZERO(did_ac, SIZEOF(did_ac)); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + if (! did_dc[dctbl]) { + htblptr = & cinfo->dc_huff_tbl_ptrs[dctbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->dc_count_ptrs[dctbl]); + did_dc[dctbl] = TRUE; + } + if (! did_ac[actbl]) { + htblptr = & cinfo->ac_huff_tbl_ptrs[actbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->ac_count_ptrs[actbl]); + did_ac[actbl] = TRUE; + } + } +} + + +#endif /* ENTROPY_OPT_SUPPORTED */ + + +/* + * Module initialization routine for Huffman entropy encoding. + */ + +GLOBAL(void) +jinit_huff_encoder (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_encoder)); + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_huff; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; +#ifdef ENTROPY_OPT_SUPPORTED + entropy->dc_count_ptrs[i] = entropy->ac_count_ptrs[i] = NULL; +#endif + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jchuff.h b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jchuff.h new file mode 100644 index 000000000..fc679e8e0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jchuff.h @@ -0,0 +1,52 @@ +/* + * jchuff.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for Huffman entropy encoding routines + * that are shared between the sequential encoder (jchuff.c) and the + * progressive encoder (jcphuff.c). No other modules need to see these. + */ + +/* The legal range of a DCT coefficient is + * -1024 .. +1023 for 8-bit data; + * -16384 .. +16383 for 12-bit data. + * Hence the magnitude should always fit in 10 or 14 bits respectively. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MAX_COEF_BITS 10 +#else +#define MAX_COEF_BITS 14 +#endif + +/* Derived data constructed for each Huffman table */ + +typedef struct { + unsigned int ehufco[256]; /* code for each symbol */ + char ehufsi[256]; /* length of code for each symbol */ + /* If no code has been allocated for a symbol S, ehufsi[S] contains 0 */ +} c_derived_tbl; + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_make_c_derived_tbl jMkCDerived +#define jpeg_gen_optimal_table jGenOptTbl +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +#ifdef NEED_12_BIT_NAMES +#define jpeg_make_c_derived_tbl jpeg_make_c_derived_tbl_12 +#define jpeg_gen_optimal_table jpeg_gen_optimal_table_12 +#endif + +/* Expand a Huffman table definition into the derived format */ +EXTERN(void) jpeg_make_c_derived_tbl + JPP((j_compress_ptr cinfo, boolean isDC, int tblno, + c_derived_tbl ** pdtbl)); + +/* Generate an optimal table definition given the specified counts */ +EXTERN(void) jpeg_gen_optimal_table + JPP((j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[])); diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcinit.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcinit.c new file mode 100644 index 000000000..5efffe331 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcinit.c @@ -0,0 +1,72 @@ +/* + * jcinit.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains initialization logic for the JPEG compressor. + * This routine is in charge of selecting the modules to be executed and + * making an initialization call to each one. + * + * Logically, this code belongs in jcmaster.c. It's split out because + * linking this routine implies linking the entire compression library. + * For a transcoding-only application, we want to be able to use jcmaster.c + * without linking in the whole library. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Master selection of compression modules. + * This is done once at the start of processing an image. We determine + * which modules will be used and give them appropriate initialization calls. + */ + +GLOBAL(void) +jinit_compress_master (j_compress_ptr cinfo) +{ + /* Initialize master control (includes parameter checking/processing) */ + jinit_c_master_control(cinfo, FALSE /* full compression */); + + /* Preprocessing */ + if (! cinfo->raw_data_in) { + jinit_color_converter(cinfo); + jinit_downsampler(cinfo); + jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */); + } + /* Forward DCT */ + jinit_forward_dct(cinfo); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + jinit_phuff_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_encoder(cinfo); + } + + /* Need a full-image coefficient buffer in any multi-pass mode. */ + jinit_c_coef_controller(cinfo, + (boolean) (cinfo->num_scans > 1 || cinfo->optimize_coding)); + jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */); + + jinit_marker_writer(cinfo); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Write the datastream header (SOI) immediately. + * Frame and scan headers are postponed till later. + * This lets application insert special markers after the SOI. + */ + (*cinfo->marker->write_file_header) (cinfo); +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcmainct.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcmainct.c new file mode 100644 index 000000000..a39c2ad96 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcmainct.c @@ -0,0 +1,293 @@ +/* + * jcmainct.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the main buffer controller for compression. + * The main buffer lies between the pre-processor and the JPEG + * compressor proper; it holds downsampled data in the JPEG colorspace. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Note: currently, there is no operating mode in which a full-image buffer + * is needed at this step. If there were, that mode could not be used with + * "raw data" input, since this module is bypassed in that case. However, + * we've left the code here for possible use in special applications. + */ +#undef FULL_MAIN_BUFFER_SUPPORTED + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_main_controller pub; /* public fields */ + + JDIMENSION cur_iMCU_row; /* number of current iMCU row */ + JDIMENSION rowgroup_ctr; /* counts row groups received in iMCU row */ + boolean suspended; /* remember if we suspended output */ + J_BUF_MODE pass_mode; /* current operating mode */ + + /* If using just a strip buffer, this points to the entire set of buffers + * (we allocate one for each component). In the full-image case, this + * points to the currently accessible strips of the virtual arrays. + */ + JSAMPARRAY buffer[MAX_COMPONENTS]; + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + /* If using full-image storage, this array holds pointers to virtual-array + * control blocks for each component. Unused if not full-image storage. + */ + jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; +#endif +} my_main_controller; + +typedef my_main_controller * my_main_ptr; + + +/* Forward declarations */ +METHODDEF(void) process_data_simple_main + JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail)); +#ifdef FULL_MAIN_BUFFER_SUPPORTED +METHODDEF(void) process_data_buffer_main + JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail)); +#endif + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_main (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_main_ptr mainp = (my_main_ptr) cinfo->main; + + /* Do nothing in raw-data mode. */ + if (cinfo->raw_data_in) + return; + + mainp->cur_iMCU_row = 0; /* initialize counters */ + mainp->rowgroup_ctr = 0; + mainp->suspended = FALSE; + mainp->pass_mode = pass_mode; /* save mode for use by process_data */ + + switch (pass_mode) { + case JBUF_PASS_THRU: +#ifdef FULL_MAIN_BUFFER_SUPPORTED + if (mainp->whole_image[0] != NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + mainp->pub.process_data = process_data_simple_main; + break; +#ifdef FULL_MAIN_BUFFER_SUPPORTED + case JBUF_SAVE_SOURCE: + case JBUF_CRANK_DEST: + case JBUF_SAVE_AND_PASS: + if (mainp->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + mainp->pub.process_data = process_data_buffer_main; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +/* + * Process some data. + * This routine handles the simple pass-through mode, + * where we have only a strip buffer. + */ + +METHODDEF(void) +process_data_simple_main (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail) +{ + my_main_ptr mainp = (my_main_ptr) cinfo->main; + + while (mainp->cur_iMCU_row < cinfo->total_iMCU_rows) { + /* Read input data if we haven't filled the main buffer yet */ + if (mainp->rowgroup_ctr < DCTSIZE) + (*cinfo->prep->pre_process_data) (cinfo, + input_buf, in_row_ctr, in_rows_avail, + mainp->buffer, &mainp->rowgroup_ctr, + (JDIMENSION) DCTSIZE); + + /* If we don't have a full iMCU row buffered, return to application for + * more data. Note that preprocessor will always pad to fill the iMCU row + * at the bottom of the image. + */ + if (mainp->rowgroup_ctr != DCTSIZE) + return; + + /* Send the completed row to the compressor */ + if (! (*cinfo->coef->compress_data) (cinfo, mainp->buffer)) { + /* If compressor did not consume the whole row, then we must need to + * suspend processing and return to the application. In this situation + * we pretend we didn't yet consume the last input row; otherwise, if + * it happened to be the last row of the image, the application would + * think we were done. + */ + if (! mainp->suspended) { + (*in_row_ctr)--; + mainp->suspended = TRUE; + } + return; + } + /* We did finish the row. Undo our little suspension hack if a previous + * call suspended; then mark the main buffer empty. + */ + if (mainp->suspended) { + (*in_row_ctr)++; + mainp->suspended = FALSE; + } + mainp->rowgroup_ctr = 0; + mainp->cur_iMCU_row++; + } +} + + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + +/* + * Process some data. + * This routine handles all of the modes that use a full-size buffer. + */ + +METHODDEF(void) +process_data_buffer_main (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci; + jpeg_component_info *compptr; + boolean writing = (mainp->pass_mode != JBUF_CRANK_DEST); + + while (mainp->cur_iMCU_row < cinfo->total_iMCU_rows) { + /* Realign the virtual buffers if at the start of an iMCU row. */ + if (mainp->rowgroup_ctr == 0) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + mainp->buffer[ci] = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, mainp->whole_image[ci], + mainp->cur_iMCU_row * (compptr->v_samp_factor * DCTSIZE), + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE), writing); + } + /* In a read pass, pretend we just read some source data. */ + if (! writing) { + *in_row_ctr += cinfo->max_v_samp_factor * DCTSIZE; + mainp->rowgroup_ctr = DCTSIZE; + } + } + + /* If a write pass, read input data until the current iMCU row is full. */ + /* Note: preprocessor will pad if necessary to fill the last iMCU row. */ + if (writing) { + (*cinfo->prep->pre_process_data) (cinfo, + input_buf, in_row_ctr, in_rows_avail, + mainp->buffer, &mainp->rowgroup_ctr, + (JDIMENSION) DCTSIZE); + /* Return to application if we need more data to fill the iMCU row. */ + if (mainp->rowgroup_ctr < DCTSIZE) + return; + } + + /* Emit data, unless this is a sink-only pass. */ + if (mainp->pass_mode != JBUF_SAVE_SOURCE) { + if (! (*cinfo->coef->compress_data) (cinfo, mainp->buffer)) { + /* If compressor did not consume the whole row, then we must need to + * suspend processing and return to the application. In this situation + * we pretend we didn't yet consume the last input row; otherwise, if + * it happened to be the last row of the image, the application would + * think we were done. + */ + if (! mainp->suspended) { + (*in_row_ctr)--; + mainp->suspended = TRUE; + } + return; + } + /* We did finish the row. Undo our little suspension hack if a previous + * call suspended; then mark the main buffer empty. + */ + if (mainp->suspended) { + (*in_row_ctr)++; + mainp->suspended = FALSE; + } + } + + /* If get here, we are done with this iMCU row. Mark buffer empty. */ + mainp->rowgroup_ctr = 0; + mainp->cur_iMCU_row++; + } +} + +#endif /* FULL_MAIN_BUFFER_SUPPORTED */ + + +/* + * Initialize main buffer controller. + */ + +GLOBAL(void) +jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_main_ptr mainp; + int ci; + jpeg_component_info *compptr; + + mainp = (my_main_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_main_controller)); + cinfo->main = (struct jpeg_c_main_controller *) mainp; + mainp->pub.start_pass = start_pass_main; + + /* We don't need to create a buffer in raw-data mode. */ + if (cinfo->raw_data_in) + return; + + /* Create the buffer. It holds downsampled data, so each component + * may be of a different size. + */ + if (need_full_buffer) { +#ifdef FULL_MAIN_BUFFER_SUPPORTED + /* Allocate a full-image virtual array for each component */ + /* Note we pad the bottom to a multiple of the iMCU height */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + mainp->whole_image[ci] = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + compptr->width_in_blocks * DCTSIZE, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor) * DCTSIZE, + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE)); + } +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + } else { +#ifdef FULL_MAIN_BUFFER_SUPPORTED + mainp->whole_image[0] = NULL; /* flag for no virtual arrays */ +#endif + /* Allocate a strip buffer for each component */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + mainp->buffer[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + compptr->width_in_blocks * DCTSIZE, + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE)); + } + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcmarker.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcmarker.c new file mode 100644 index 000000000..125a15d7c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcmarker.c @@ -0,0 +1,665 @@ +/* + * jcmarker.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write JPEG datastream markers. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#include "cpl_port.h" + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_DAC = 0xcc, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_TEM = 0x01, + + M_ERROR = 0x100 +} JPEG_MARKER; + + +/* Private state */ + +typedef struct { + struct jpeg_marker_writer pub; /* public fields */ + + unsigned int last_restart_interval; /* last DRI value emitted; 0 after SOI */ +} my_marker_writer; + +typedef my_marker_writer * my_marker_ptr; + + +/* + * Basic output routines. + * + * Note that we do not support suspension while writing a marker. + * Therefore, an application using suspension must ensure that there is + * enough buffer space for the initial markers (typ. 600-700 bytes) before + * calling jpeg_start_compress, and enough space to write the trailing EOI + * (a few bytes) before calling jpeg_finish_compress. Multipass compression + * modes are not supported at all with suspension, so those two are the only + * points where markers will be written. + */ + +LOCAL(void) +emit_byte (j_compress_ptr cinfo, int val) +/* Emit a byte */ +{ + struct jpeg_destination_mgr * dest = cinfo->dest; + + *(dest->next_output_byte)++ = (JOCTET) val; + if (--dest->free_in_buffer == 0) { + if (! (*dest->empty_output_buffer) (cinfo)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + } +} + + +LOCAL(void) +emit_marker (j_compress_ptr cinfo, JPEG_MARKER mark) +/* Emit a marker code */ +{ + emit_byte(cinfo, 0xFF); + emit_byte(cinfo, (int) mark); +} + + +LOCAL(void) +emit_2bytes (j_compress_ptr cinfo, int value) +/* Emit a 2-byte integer; these are always MSB first in JPEG files */ +{ + emit_byte(cinfo, (value >> 8) & 0xFF); + emit_byte(cinfo, value & 0xFF); +} + + +/* + * Routines to write specific marker types. + */ + +LOCAL(int) +emit_dqt (j_compress_ptr cinfo, int index) +/* Emit a DQT marker */ +/* Returns the precision used (0 = 8bits, 1 = 16bits) for baseline checking */ +{ + JQUANT_TBL * qtbl = cinfo->quant_tbl_ptrs[index]; + int prec; + int i; + + if (qtbl == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, index); + + prec = 0; + for (i = 0; i < DCTSIZE2; i++) { + if (qtbl->quantval[i] > 255) + prec = 1; + } + + if (! qtbl->sent_table) { + emit_marker(cinfo, M_DQT); + + emit_2bytes(cinfo, prec ? DCTSIZE2*2 + 1 + 2 : DCTSIZE2 + 1 + 2); + + emit_byte(cinfo, index + (prec<<4)); + + for (i = 0; i < DCTSIZE2; i++) { + /* The table entries must be emitted in zigzag order. */ + unsigned int qval = qtbl->quantval[jpeg_natural_order[i]]; + if (prec) + emit_byte(cinfo, (int) (qval >> 8)); + emit_byte(cinfo, (int) (qval & 0xFF)); + } + + qtbl->sent_table = TRUE; + } + + return prec; +} + + +LOCAL(void) +emit_dht (j_compress_ptr cinfo, int index, boolean is_ac) +/* Emit a DHT marker */ +{ + JHUFF_TBL * htbl; + int length, i; + + if (is_ac) { + htbl = cinfo->ac_huff_tbl_ptrs[index]; + index += 0x10; /* output index has AC bit set */ + } else { + htbl = cinfo->dc_huff_tbl_ptrs[index]; + } + + if (htbl == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, index); + + if (! htbl->sent_table) { + emit_marker(cinfo, M_DHT); + + length = 0; + for (i = 1; i <= 16; i++) + length += htbl->bits[i]; + + emit_2bytes(cinfo, length + 2 + 1 + 16); + emit_byte(cinfo, index); + + for (i = 1; i <= 16; i++) + emit_byte(cinfo, htbl->bits[i]); + + for (i = 0; i < length; i++) + emit_byte(cinfo, htbl->huffval[i]); + + htbl->sent_table = TRUE; + } +} + + +LOCAL(void) +emit_dac (CPL_UNUSED j_compress_ptr cinfo) +/* Emit a DAC marker */ +/* Since the useful info is so small, we want to emit all the tables in */ +/* one DAC marker. Therefore this routine does its own scan of the table. */ +{ +#ifdef C_ARITH_CODING_SUPPORTED + char dc_in_use[NUM_ARITH_TBLS]; + char ac_in_use[NUM_ARITH_TBLS]; + int length, i; + jpeg_component_info *compptr; + + for (i = 0; i < NUM_ARITH_TBLS; i++) + dc_in_use[i] = ac_in_use[i] = 0; + + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + dc_in_use[compptr->dc_tbl_no] = 1; + ac_in_use[compptr->ac_tbl_no] = 1; + } + + length = 0; + for (i = 0; i < NUM_ARITH_TBLS; i++) + length += dc_in_use[i] + ac_in_use[i]; + + emit_marker(cinfo, M_DAC); + + emit_2bytes(cinfo, length*2 + 2); + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + if (dc_in_use[i]) { + emit_byte(cinfo, i); + emit_byte(cinfo, cinfo->arith_dc_L[i] + (cinfo->arith_dc_U[i]<<4)); + } + if (ac_in_use[i]) { + emit_byte(cinfo, i + 0x10); + emit_byte(cinfo, cinfo->arith_ac_K[i]); + } + } +#endif /* C_ARITH_CODING_SUPPORTED */ +} + + +LOCAL(void) +emit_dri (j_compress_ptr cinfo) +/* Emit a DRI marker */ +{ + emit_marker(cinfo, M_DRI); + + emit_2bytes(cinfo, 4); /* fixed length */ + + emit_2bytes(cinfo, (int) cinfo->restart_interval); +} + + +LOCAL(void) +emit_sof (j_compress_ptr cinfo, JPEG_MARKER code) +/* Emit a SOF marker */ +{ + int ci; + jpeg_component_info *compptr; + + emit_marker(cinfo, code); + + emit_2bytes(cinfo, 3 * cinfo->num_components + 2 + 5 + 1); /* length */ + + /* Make sure image isn't bigger than SOF field can handle */ + if ((long) cinfo->image_height > 65535L || + (long) cinfo->image_width > 65535L) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) 65535); + + emit_byte(cinfo, cinfo->data_precision); + emit_2bytes(cinfo, (int) cinfo->image_height); + emit_2bytes(cinfo, (int) cinfo->image_width); + + emit_byte(cinfo, cinfo->num_components); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + emit_byte(cinfo, compptr->component_id); + emit_byte(cinfo, (compptr->h_samp_factor << 4) + compptr->v_samp_factor); + emit_byte(cinfo, compptr->quant_tbl_no); + } +} + + +LOCAL(void) +emit_sos (j_compress_ptr cinfo) +/* Emit a SOS marker */ +{ + int i, td, ta; + jpeg_component_info *compptr; + + emit_marker(cinfo, M_SOS); + + emit_2bytes(cinfo, 2 * cinfo->comps_in_scan + 2 + 1 + 3); /* length */ + + emit_byte(cinfo, cinfo->comps_in_scan); + + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + emit_byte(cinfo, compptr->component_id); + td = compptr->dc_tbl_no; + ta = compptr->ac_tbl_no; + if (cinfo->progressive_mode) { + /* Progressive mode: only DC or only AC tables are used in one scan; + * furthermore, Huffman coding of DC refinement uses no table at all. + * We emit 0 for unused field(s); this is recommended by the P&M text + * but does not seem to be specified in the standard. + */ + if (cinfo->Ss == 0) { + ta = 0; /* DC scan */ + if (cinfo->Ah != 0 && !cinfo->arith_code) + td = 0; /* no DC table either */ + } else { + td = 0; /* AC scan */ + } + } + emit_byte(cinfo, (td << 4) + ta); + } + + emit_byte(cinfo, cinfo->Ss); + emit_byte(cinfo, cinfo->Se); + emit_byte(cinfo, (cinfo->Ah << 4) + cinfo->Al); +} + + +LOCAL(void) +emit_jfif_app0 (j_compress_ptr cinfo) +/* Emit a JFIF-compliant APP0 marker */ +{ + /* + * Length of APP0 block (2 bytes) + * Block ID (4 bytes - ASCII "JFIF") + * Zero byte (1 byte to terminate the ID string) + * Version Major, Minor (2 bytes - major first) + * Units (1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm) + * Xdpu (2 bytes - dots per unit horizontal) + * Ydpu (2 bytes - dots per unit vertical) + * Thumbnail X size (1 byte) + * Thumbnail Y size (1 byte) + */ + + emit_marker(cinfo, M_APP0); + + emit_2bytes(cinfo, 2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); /* length */ + + emit_byte(cinfo, 0x4A); /* Identifier: ASCII "JFIF" */ + emit_byte(cinfo, 0x46); + emit_byte(cinfo, 0x49); + emit_byte(cinfo, 0x46); + emit_byte(cinfo, 0); + emit_byte(cinfo, cinfo->JFIF_major_version); /* Version fields */ + emit_byte(cinfo, cinfo->JFIF_minor_version); + emit_byte(cinfo, cinfo->density_unit); /* Pixel size information */ + emit_2bytes(cinfo, (int) cinfo->X_density); + emit_2bytes(cinfo, (int) cinfo->Y_density); + emit_byte(cinfo, 0); /* No thumbnail image */ + emit_byte(cinfo, 0); +} + + +LOCAL(void) +emit_adobe_app14 (j_compress_ptr cinfo) +/* Emit an Adobe APP14 marker */ +{ + /* + * Length of APP14 block (2 bytes) + * Block ID (5 bytes - ASCII "Adobe") + * Version Number (2 bytes - currently 100) + * Flags0 (2 bytes - currently 0) + * Flags1 (2 bytes - currently 0) + * Color transform (1 byte) + * + * Although Adobe TN 5116 mentions Version = 101, all the Adobe files + * now in circulation seem to use Version = 100, so that's what we write. + * + * We write the color transform byte as 1 if the JPEG color space is + * YCbCr, 2 if it's YCCK, 0 otherwise. Adobe's definition has to do with + * whether the encoder performed a transformation, which is pretty useless. + */ + + emit_marker(cinfo, M_APP14); + + emit_2bytes(cinfo, 2 + 5 + 2 + 2 + 2 + 1); /* length */ + + emit_byte(cinfo, 0x41); /* Identifier: ASCII "Adobe" */ + emit_byte(cinfo, 0x64); + emit_byte(cinfo, 0x6F); + emit_byte(cinfo, 0x62); + emit_byte(cinfo, 0x65); + emit_2bytes(cinfo, 100); /* Version */ + emit_2bytes(cinfo, 0); /* Flags0 */ + emit_2bytes(cinfo, 0); /* Flags1 */ + switch (cinfo->jpeg_color_space) { + case JCS_YCbCr: + emit_byte(cinfo, 1); /* Color transform = 1 */ + break; + case JCS_YCCK: + emit_byte(cinfo, 2); /* Color transform = 2 */ + break; + default: + emit_byte(cinfo, 0); /* Color transform = 0 */ + break; + } +} + + +/* + * These routines allow writing an arbitrary marker with parameters. + * The only intended use is to emit COM or APPn markers after calling + * write_file_header and before calling write_frame_header. + * Other uses are not guaranteed to produce desirable results. + * Counting the parameter bytes properly is the caller's responsibility. + */ + +METHODDEF(void) +write_marker_header (j_compress_ptr cinfo, int marker, unsigned int datalen) +/* Emit an arbitrary marker header */ +{ + if (datalen > (unsigned int) 65533) /* safety check */ + ERREXIT(cinfo, JERR_BAD_LENGTH); + + emit_marker(cinfo, (JPEG_MARKER) marker); + + emit_2bytes(cinfo, (int) (datalen + 2)); /* total length */ +} + +METHODDEF(void) +write_marker_byte (j_compress_ptr cinfo, int val) +/* Emit one byte of marker parameters following write_marker_header */ +{ + emit_byte(cinfo, val); +} + + +/* + * Write datastream header. + * This consists of an SOI and optional APPn markers. + * We recommend use of the JFIF marker, but not the Adobe marker, + * when using YCbCr or grayscale data. The JFIF marker should NOT + * be used for any other JPEG colorspace. The Adobe marker is helpful + * to distinguish RGB, CMYK, and YCCK colorspaces. + * Note that an application can write additional header markers after + * jpeg_start_compress returns. + */ + +METHODDEF(void) +write_file_header (j_compress_ptr cinfo) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + + emit_marker(cinfo, M_SOI); /* first the SOI */ + + /* SOI is defined to reset restart interval to 0 */ + marker->last_restart_interval = 0; + + if (cinfo->write_JFIF_header) /* next an optional JFIF APP0 */ + emit_jfif_app0(cinfo); + if (cinfo->write_Adobe_marker) /* next an optional Adobe APP14 */ + emit_adobe_app14(cinfo); +} + + +/* + * Write frame header. + * This consists of DQT and SOFn markers. + * Note that we do not emit the SOF until we have emitted the DQT(s). + * This avoids compatibility problems with incorrect implementations that + * try to error-check the quant table numbers as soon as they see the SOF. + */ + +METHODDEF(void) +write_frame_header (j_compress_ptr cinfo) +{ + int ci, prec; + boolean is_baseline; + jpeg_component_info *compptr; + + /* Emit DQT for each quantization table. + * Note that emit_dqt() suppresses any duplicate tables. + */ + prec = 0; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + prec += emit_dqt(cinfo, compptr->quant_tbl_no); + } + /* now prec is nonzero iff there are any 16-bit quant tables. */ + + /* Check for a non-baseline specification. + * Note we assume that Huffman table numbers won't be changed later. + */ + if (cinfo->arith_code || cinfo->progressive_mode || + cinfo->data_precision != 8) { + is_baseline = FALSE; + } else { + is_baseline = TRUE; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->dc_tbl_no > 1 || compptr->ac_tbl_no > 1) + is_baseline = FALSE; + } + if (prec && is_baseline) { + is_baseline = FALSE; + /* If it's baseline except for quantizer size, warn the user */ + TRACEMS(cinfo, 0, JTRC_16BIT_TABLES); + } + } + + /* Emit the proper SOF marker */ + if (cinfo->arith_code) { + emit_sof(cinfo, M_SOF9); /* SOF code for arithmetic coding */ + } else { + if (cinfo->progressive_mode) + emit_sof(cinfo, M_SOF2); /* SOF code for progressive Huffman */ + else if (is_baseline) + emit_sof(cinfo, M_SOF0); /* SOF code for baseline implementation */ + else + emit_sof(cinfo, M_SOF1); /* SOF code for non-baseline Huffman file */ + } +} + + +/* + * Write scan header. + * This consists of DHT or DAC markers, optional DRI, and SOS. + * Compressed data will be written following the SOS. + */ + +METHODDEF(void) +write_scan_header (j_compress_ptr cinfo) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + int i; + jpeg_component_info *compptr; + + if (cinfo->arith_code) { + /* Emit arith conditioning info. We may have some duplication + * if the file has multiple scans, but it's so small it's hardly + * worth worrying about. + */ + emit_dac(cinfo); + } else { + /* Emit Huffman tables. + * Note that emit_dht() suppresses any duplicate tables. + */ + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + if (cinfo->progressive_mode) { + /* Progressive mode: only DC or only AC tables are used in one scan */ + if (cinfo->Ss == 0) { + if (cinfo->Ah == 0) /* DC needs no table for refinement scan */ + emit_dht(cinfo, compptr->dc_tbl_no, FALSE); + } else { + emit_dht(cinfo, compptr->ac_tbl_no, TRUE); + } + } else { + /* Sequential mode: need both DC and AC tables */ + emit_dht(cinfo, compptr->dc_tbl_no, FALSE); + emit_dht(cinfo, compptr->ac_tbl_no, TRUE); + } + } + } + + /* Emit DRI if required --- note that DRI value could change for each scan. + * We avoid wasting space with unnecessary DRIs, however. + */ + if (cinfo->restart_interval != marker->last_restart_interval) { + emit_dri(cinfo); + marker->last_restart_interval = cinfo->restart_interval; + } + + emit_sos(cinfo); +} + + +/* + * Write datastream trailer. + */ + +METHODDEF(void) +write_file_trailer (j_compress_ptr cinfo) +{ + emit_marker(cinfo, M_EOI); +} + + +/* + * Write an abbreviated table-specification datastream. + * This consists of SOI, DQT and DHT tables, and EOI. + * Any table that is defined and not marked sent_table = TRUE will be + * emitted. Note that all tables will be marked sent_table = TRUE at exit. + */ + +METHODDEF(void) +write_tables_only (j_compress_ptr cinfo) +{ + int i; + + emit_marker(cinfo, M_SOI); + + for (i = 0; i < NUM_QUANT_TBLS; i++) { + if (cinfo->quant_tbl_ptrs[i] != NULL) + (void) emit_dqt(cinfo, i); + } + + if (! cinfo->arith_code) { + for (i = 0; i < NUM_HUFF_TBLS; i++) { + if (cinfo->dc_huff_tbl_ptrs[i] != NULL) + emit_dht(cinfo, i, FALSE); + if (cinfo->ac_huff_tbl_ptrs[i] != NULL) + emit_dht(cinfo, i, TRUE); + } + } + + emit_marker(cinfo, M_EOI); +} + + +/* + * Initialize the marker writer module. + */ + +GLOBAL(void) +jinit_marker_writer (j_compress_ptr cinfo) +{ + my_marker_ptr marker; + + /* Create the subobject */ + marker = (my_marker_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_marker_writer)); + cinfo->marker = (struct jpeg_marker_writer *) marker; + /* Initialize method pointers */ + marker->pub.write_file_header = write_file_header; + marker->pub.write_frame_header = write_frame_header; + marker->pub.write_scan_header = write_scan_header; + marker->pub.write_file_trailer = write_file_trailer; + marker->pub.write_tables_only = write_tables_only; + marker->pub.write_marker_header = write_marker_header; + marker->pub.write_marker_byte = write_marker_byte; + /* Initialize private state */ + marker->last_restart_interval = 0; +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcmaster.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcmaster.c new file mode 100644 index 000000000..aab4020b8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcmaster.c @@ -0,0 +1,590 @@ +/* + * jcmaster.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains master control logic for the JPEG compressor. + * These routines are concerned with parameter validation, initial setup, + * and inter-pass control (determining the number of passes and the work + * to be done in each pass). + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef enum { + main_pass, /* input data, also do first output step */ + huff_opt_pass, /* Huffman code optimization pass */ + output_pass /* data output pass */ +} c_pass_type; + +typedef struct { + struct jpeg_comp_master pub; /* public fields */ + + c_pass_type pass_type; /* the type of the current pass */ + + int pass_number; /* # of passes completed */ + int total_passes; /* total # of passes needed */ + + int scan_number; /* current index in scan_info[] */ +} my_comp_master; + +typedef my_comp_master * my_master_ptr; + + +/* + * Support routines that do various essential calculations. + */ + +LOCAL(void) +initial_setup (j_compress_ptr cinfo) +/* Do computations that are needed before master selection phase */ +{ + int ci; + jpeg_component_info *compptr; + long samplesperrow; + JDIMENSION jd_samplesperrow; + + /* Sanity check on image dimensions */ + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + || cinfo->num_components <= 0 || cinfo->input_components <= 0) + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + /* Make sure image isn't bigger than I can handle */ + if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || + (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + /* Width of an input scanline must be representable as JDIMENSION. */ + samplesperrow = (long) cinfo->image_width * (long) cinfo->input_components; + jd_samplesperrow = (JDIMENSION) samplesperrow; + if ((long) jd_samplesperrow != samplesperrow) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* For now, precision must match compiled-in value... */ + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + /* Check that number of components won't exceed internal array sizes */ + if (cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + + /* Compute maximum sampling factors; check factor validity */ + cinfo->max_h_samp_factor = 1; + cinfo->max_v_samp_factor = 1; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + ERREXIT(cinfo, JERR_BAD_SAMPLING); + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + compptr->h_samp_factor); + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + compptr->v_samp_factor); + } + + /* Compute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Fill in the correct component_index value; don't rely on application */ + compptr->component_index = ci; + /* For compression, we never do DCT scaling. */ + compptr->DCT_scaled_size = DCTSIZE; + /* Size in DCT blocks */ + compptr->width_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->height_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + /* Size in samples */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) cinfo->max_h_samp_factor); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) cinfo->max_v_samp_factor); + /* Mark component needed (this flag isn't actually used for compression) */ + compptr->component_needed = TRUE; + } + + /* Compute number of fully interleaved MCU rows (number of times that + * main controller will call coefficient controller). + */ + cinfo->total_iMCU_rows = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); +} + + +#ifdef C_MULTISCAN_FILES_SUPPORTED + +LOCAL(void) +validate_script (j_compress_ptr cinfo) +/* Verify that the scan script in cinfo->scan_info[] is valid; also + * determine whether it uses progressive JPEG, and set cinfo->progressive_mode. + */ +{ + const jpeg_scan_info * scanptr; + int scanno, ncomps, ci, coefi, thisi; + int Ss, Se, Ah, Al; + boolean component_sent[MAX_COMPONENTS]; +#ifdef C_PROGRESSIVE_SUPPORTED + int * last_bitpos_ptr; + int last_bitpos[MAX_COMPONENTS][DCTSIZE2]; + /* -1 until that coefficient has been seen; then last Al for it */ +#endif + + if (cinfo->num_scans <= 0) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, 0); + + /* For sequential JPEG, all scans must have Ss=0, Se=DCTSIZE2-1; + * for progressive JPEG, no scan can have this. + */ + scanptr = cinfo->scan_info; + if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2-1) { +#ifdef C_PROGRESSIVE_SUPPORTED + cinfo->progressive_mode = TRUE; + last_bitpos_ptr = & last_bitpos[0][0]; + for (ci = 0; ci < cinfo->num_components; ci++) + for (coefi = 0; coefi < DCTSIZE2; coefi++) + *last_bitpos_ptr++ = -1; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + cinfo->progressive_mode = FALSE; + for (ci = 0; ci < cinfo->num_components; ci++) + component_sent[ci] = FALSE; + } + + for (scanno = 1; scanno <= cinfo->num_scans; scanptr++, scanno++) { + /* Validate component indexes */ + ncomps = scanptr->comps_in_scan; + if (ncomps <= 0 || ncomps > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, ncomps, MAX_COMPS_IN_SCAN); + for (ci = 0; ci < ncomps; ci++) { + thisi = scanptr->component_index[ci]; + if (thisi < 0 || thisi >= cinfo->num_components) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + /* Components must appear in SOF order within each scan */ + if (ci > 0 && thisi <= scanptr->component_index[ci-1]) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + } + /* Validate progression parameters */ + Ss = scanptr->Ss; + Se = scanptr->Se; + Ah = scanptr->Ah; + Al = scanptr->Al; + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + /* The JPEG spec simply gives the ranges 0..13 for Ah and Al, but that + * seems wrong: the upper bound ought to depend on data precision. + * Perhaps they really meant 0..N+1 for N-bit precision. + * Here we allow 0..10 for 8-bit data; Al larger than 10 results in + * out-of-range reconstructed DC values during the first DC scan, + * which might cause problems for some decoders. + */ +#if BITS_IN_JSAMPLE == 8 +#define MAX_AH_AL 10 +#else +#define MAX_AH_AL 13 +#endif + if (Ss < 0 || Ss >= DCTSIZE2 || Se < Ss || Se >= DCTSIZE2 || + Ah < 0 || Ah > MAX_AH_AL || Al < 0 || Al > MAX_AH_AL) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + if (Ss == 0) { + if (Se != 0) /* DC and AC together not OK */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } else { + if (ncomps != 1) /* AC scans must be for only one component */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } + for (ci = 0; ci < ncomps; ci++) { + last_bitpos_ptr = & last_bitpos[scanptr->component_index[ci]][0]; + if (Ss != 0 && last_bitpos_ptr[0] < 0) /* AC without prior DC scan */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + for (coefi = Ss; coefi <= Se; coefi++) { + if (last_bitpos_ptr[coefi] < 0) { + /* first scan of this coefficient */ + if (Ah != 0) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } else { + /* not first scan */ + if (Ah != last_bitpos_ptr[coefi] || Al != Ah-1) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } + last_bitpos_ptr[coefi] = Al; + } + } +#endif + } else { + /* For sequential JPEG, all progression parameters must be these: */ + if (Ss != 0 || Se != DCTSIZE2-1 || Ah != 0 || Al != 0) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + /* Make sure components are not sent twice */ + for (ci = 0; ci < ncomps; ci++) { + thisi = scanptr->component_index[ci]; + if (component_sent[thisi]) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + component_sent[thisi] = TRUE; + } + } + } + + /* Now verify that everything got sent. */ + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + /* For progressive mode, we only check that at least some DC data + * got sent for each component; the spec does not require that all bits + * of all coefficients be transmitted. Would it be wiser to enforce + * transmission of all coefficient bits?? + */ + for (ci = 0; ci < cinfo->num_components; ci++) { + if (last_bitpos[ci][0] < 0) + ERREXIT(cinfo, JERR_MISSING_DATA); + } +#endif + } else { + for (ci = 0; ci < cinfo->num_components; ci++) { + if (! component_sent[ci]) + ERREXIT(cinfo, JERR_MISSING_DATA); + } + } +} + +#endif /* C_MULTISCAN_FILES_SUPPORTED */ + + +LOCAL(void) +select_scan_parameters (j_compress_ptr cinfo) +/* Set up the scan parameters for the current scan */ +{ + int ci; + +#ifdef C_MULTISCAN_FILES_SUPPORTED + if (cinfo->scan_info != NULL) { + /* Prepare for current scan --- the script is already validated */ + my_master_ptr master = (my_master_ptr) cinfo->master; + const jpeg_scan_info * scanptr = cinfo->scan_info + master->scan_number; + + cinfo->comps_in_scan = scanptr->comps_in_scan; + for (ci = 0; ci < scanptr->comps_in_scan; ci++) { + cinfo->cur_comp_info[ci] = + &cinfo->comp_info[scanptr->component_index[ci]]; + } + cinfo->Ss = scanptr->Ss; + cinfo->Se = scanptr->Se; + cinfo->Ah = scanptr->Ah; + cinfo->Al = scanptr->Al; + } + else +#endif + { + /* Prepare for single sequential-JPEG scan containing all components */ + if (cinfo->num_components > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPS_IN_SCAN); + cinfo->comps_in_scan = cinfo->num_components; + for (ci = 0; ci < cinfo->num_components; ci++) { + cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci]; + } + cinfo->Ss = 0; + cinfo->Se = DCTSIZE2-1; + cinfo->Ah = 0; + cinfo->Al = 0; + } +} + + +LOCAL(void) +per_scan_setup (j_compress_ptr cinfo) +/* Do computations that are needed before processing a JPEG scan */ +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] are already set */ +{ + int ci, mcublks, tmp; + jpeg_component_info *compptr; + + if (cinfo->comps_in_scan == 1) { + + /* Noninterleaved (single-component) scan */ + compptr = cinfo->cur_comp_info[0]; + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = compptr->width_in_blocks; + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + /* For noninterleaved scan, always one block per MCU */ + compptr->MCU_width = 1; + compptr->MCU_height = 1; + compptr->MCU_blocks = 1; + compptr->MCU_sample_width = DCTSIZE; + compptr->last_col_width = 1; + /* For noninterleaved scans, it is convenient to define last_row_height + * as the number of block rows present in the last iMCU row. + */ + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (tmp == 0) tmp = compptr->v_samp_factor; + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + cinfo->blocks_in_MCU = 1; + cinfo->MCU_membership[0] = 0; + + } else { + + /* Interleaved (multi-component) scan */ + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + MAX_COMPS_IN_SCAN); + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, + (long) (cinfo->max_h_samp_factor*DCTSIZE)); + cinfo->MCU_rows_in_scan = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + cinfo->blocks_in_MCU = 0; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Sampling factors give # of blocks of component in each MCU */ + compptr->MCU_width = compptr->h_samp_factor; + compptr->MCU_height = compptr->v_samp_factor; + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + compptr->MCU_sample_width = compptr->MCU_width * DCTSIZE; + /* Figure number of non-dummy blocks in last MCU column & row */ + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + if (tmp == 0) tmp = compptr->MCU_width; + compptr->last_col_width = tmp; + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + if (tmp == 0) tmp = compptr->MCU_height; + compptr->last_row_height = tmp; + /* Prepare array describing MCU composition */ + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > C_MAX_BLOCKS_IN_MCU) + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + while (mcublks-- > 0) { + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + } + } + + } + + /* Convert restart specified in rows to actual MCU count. */ + /* Note that count must fit in 16 bits, so we provide limiting. */ + if (cinfo->restart_in_rows > 0) { + long nominal = (long) cinfo->restart_in_rows * (long) cinfo->MCUs_per_row; + cinfo->restart_interval = (unsigned int) MIN(nominal, 65535L); + } +} + + +/* + * Per-pass setup. + * This is called at the beginning of each pass. We determine which modules + * will be active during this pass and give them appropriate start_pass calls. + * We also set is_last_pass to indicate whether any more passes will be + * required. + */ + +METHODDEF(void) +prepare_for_pass (j_compress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + switch (master->pass_type) { + case main_pass: + /* Initial pass: will collect input data, and do either Huffman + * optimization or data output for the first scan. + */ + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + if (! cinfo->raw_data_in) { + (*cinfo->cconvert->start_pass) (cinfo); + (*cinfo->downsample->start_pass) (cinfo); + (*cinfo->prep->start_pass) (cinfo, JBUF_PASS_THRU); + } + (*cinfo->fdct->start_pass) (cinfo); + (*cinfo->entropy->start_pass) (cinfo, cinfo->optimize_coding); + (*cinfo->coef->start_pass) (cinfo, + (master->total_passes > 1 ? + JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + if (cinfo->optimize_coding) { + /* No immediate data output; postpone writing frame/scan headers */ + master->pub.call_pass_startup = FALSE; + } else { + /* Will write frame/scan headers at first jpeg_write_scanlines call */ + master->pub.call_pass_startup = TRUE; + } + break; +#ifdef ENTROPY_OPT_SUPPORTED + case huff_opt_pass: + /* Do Huffman optimization for a scan after the first one. */ + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code) { + (*cinfo->entropy->start_pass) (cinfo, TRUE); + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); + master->pub.call_pass_startup = FALSE; + break; + } + /* Special case: Huffman DC refinement scans need no Huffman table + * and therefore we can skip the optimization pass for them. + */ + master->pass_type = output_pass; + master->pass_number++; + /*FALLTHROUGH*/ +#endif + case output_pass: + /* Do a data-output pass. */ + /* We need not repeat per-scan setup if prior optimization pass did it. */ + if (! cinfo->optimize_coding) { + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + } + (*cinfo->entropy->start_pass) (cinfo, FALSE); + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); + /* We emit frame/scan headers now */ + if (master->scan_number == 0) + (*cinfo->marker->write_frame_header) (cinfo); + (*cinfo->marker->write_scan_header) (cinfo); + master->pub.call_pass_startup = FALSE; + break; + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + } + + master->pub.is_last_pass = (master->pass_number == master->total_passes-1); + + /* Set up progress monitor's pass info if present */ + if (cinfo->progress != NULL) { + cinfo->progress->completed_passes = master->pass_number; + cinfo->progress->total_passes = master->total_passes; + } +} + + +/* + * Special start-of-pass hook. + * This is called by jpeg_write_scanlines if call_pass_startup is TRUE. + * In single-pass processing, we need this hook because we don't want to + * write frame/scan headers during jpeg_start_compress; we want to let the + * application write COM markers etc. between jpeg_start_compress and the + * jpeg_write_scanlines loop. + * In multi-pass processing, this routine is not used. + */ + +METHODDEF(void) +pass_startup (j_compress_ptr cinfo) +{ + cinfo->master->call_pass_startup = FALSE; /* reset flag so call only once */ + + (*cinfo->marker->write_frame_header) (cinfo); + (*cinfo->marker->write_scan_header) (cinfo); +} + + +/* + * Finish up at end of pass. + */ + +METHODDEF(void) +finish_pass_master (j_compress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + /* The entropy coder always needs an end-of-pass call, + * either to analyze statistics or to flush its output buffer. + */ + (*cinfo->entropy->finish_pass) (cinfo); + + /* Update state for next pass */ + switch (master->pass_type) { + case main_pass: + /* next pass is either output of scan 0 (after optimization) + * or output of scan 1 (if no optimization). + */ + master->pass_type = output_pass; + if (! cinfo->optimize_coding) + master->scan_number++; + break; + case huff_opt_pass: + /* next pass is always output of current scan */ + master->pass_type = output_pass; + break; + case output_pass: + /* next pass is either optimization or output of next scan */ + if (cinfo->optimize_coding) + master->pass_type = huff_opt_pass; + master->scan_number++; + break; + } + + master->pass_number++; +} + + +/* + * Initialize master compression control. + */ + +GLOBAL(void) +jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) +{ + my_master_ptr master; + + master = (my_master_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_comp_master)); + cinfo->master = (struct jpeg_comp_master *) master; + master->pub.prepare_for_pass = prepare_for_pass; + master->pub.pass_startup = pass_startup; + master->pub.finish_pass = finish_pass_master; + master->pub.is_last_pass = FALSE; + + /* Validate parameters, determine derived values */ + initial_setup(cinfo); + + if (cinfo->scan_info != NULL) { +#ifdef C_MULTISCAN_FILES_SUPPORTED + validate_script(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + cinfo->progressive_mode = FALSE; + cinfo->num_scans = 1; + } + + if (cinfo->progressive_mode) /* TEMPORARY HACK ??? */ + cinfo->optimize_coding = TRUE; /* assume default tables no good for progressive mode */ + + /* Initialize my private state */ + if (transcode_only) { + /* no main pass in transcoding */ + if (cinfo->optimize_coding) + master->pass_type = huff_opt_pass; + else + master->pass_type = output_pass; + } else { + /* for normal compression, first pass is always this type: */ + master->pass_type = main_pass; + } + master->scan_number = 0; + master->pass_number = 0; + if (cinfo->optimize_coding) + master->total_passes = cinfo->num_scans * 2; + else + master->total_passes = cinfo->num_scans; +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcomapi.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcomapi.c new file mode 100644 index 000000000..9b1fa7568 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcomapi.c @@ -0,0 +1,106 @@ +/* + * jcomapi.c + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface routines that are used for both + * compression and decompression. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Abort processing of a JPEG compression or decompression operation, + * but don't destroy the object itself. + * + * For this, we merely clean up all the nonpermanent memory pools. + * Note that temp files (virtual arrays) are not allowed to belong to + * the permanent pool, so we will be able to close all temp files here. + * Closing a data source or destination, if necessary, is the application's + * responsibility. + */ + +GLOBAL(void) +jpeg_abort (j_common_ptr cinfo) +{ + int pool; + + /* Do nothing if called on a not-initialized or destroyed JPEG object. */ + if (cinfo->mem == NULL) + return; + + /* Releasing pools in reverse order might help avoid fragmentation + * with some (brain-damaged) malloc libraries. + */ + for (pool = JPOOL_NUMPOOLS-1; pool > JPOOL_PERMANENT; pool--) { + (*cinfo->mem->free_pool) (cinfo, pool); + } + + /* Reset overall state for possible reuse of object */ + if (cinfo->is_decompressor) { + cinfo->global_state = DSTATE_START; + /* Try to keep application from accessing now-deleted marker list. + * A bit kludgy to do it here, but this is the most central place. + */ + ((j_decompress_ptr) cinfo)->marker_list = NULL; + } else { + cinfo->global_state = CSTATE_START; + } +} + + +/* + * Destruction of a JPEG object. + * + * Everything gets deallocated except the master jpeg_compress_struct itself + * and the error manager struct. Both of these are supplied by the application + * and must be freed, if necessary, by the application. (Often they are on + * the stack and so don't need to be freed anyway.) + * Closing a data source or destination, if necessary, is the application's + * responsibility. + */ + +GLOBAL(void) +jpeg_destroy (j_common_ptr cinfo) +{ + /* We need only tell the memory manager to release everything. */ + /* NB: mem pointer is NULL if memory mgr failed to initialize. */ + if (cinfo->mem != NULL) + (*cinfo->mem->self_destruct) (cinfo); + cinfo->mem = NULL; /* be safe if jpeg_destroy is called twice */ + cinfo->global_state = 0; /* mark it destroyed */ +} + + +/* + * Convenience routines for allocating quantization and Huffman tables. + * (Would jutils.c be a more reasonable place to put these?) + */ + +GLOBAL(JQUANT_TBL *) +jpeg_alloc_quant_table (j_common_ptr cinfo) +{ + JQUANT_TBL *tbl; + + tbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JQUANT_TBL)); + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + return tbl; +} + + +GLOBAL(JHUFF_TBL *) +jpeg_alloc_huff_table (j_common_ptr cinfo) +{ + JHUFF_TBL *tbl; + + tbl = (JHUFF_TBL *) + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JHUFF_TBL)); + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + return tbl; +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jconfig.h b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jconfig.h new file mode 100644 index 000000000..5845c1f9d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jconfig.h @@ -0,0 +1,45 @@ +/* jconfig.h. Generated automatically by configure. */ +/* jconfig.cfg --- source file edited by configure script */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +#undef void +#undef const +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H 1 +#define HAVE_STDLIB_H 1 +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS +#undef NEED_SHORT_EXTERNAL_NAMES +/* Define this if you get warnings about undefined structures. */ +#undef INCOMPLETE_TYPES_BROKEN + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED +#define INLINE +/* These are for configuring the JPEG memory manager. */ +#undef DEFAULT_MAX_MEM +#undef NO_MKTEMP + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#undef TWO_FILE_COMMANDLINE +#undef NEED_SIGNAL_CATCHER +#undef DONT_USE_B_MODE + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. */ +#undef PROGRESS_REPORT + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcparam.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcparam.c new file mode 100644 index 000000000..6f987d8e2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcparam.c @@ -0,0 +1,688 @@ +/* + * jcparam.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains optional default-setting code for the JPEG compressor. + * Applications do not have to use this file, but those that don't use it + * must know a lot more about the innards of the JPEG code. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Quantization table setup routines + */ + +GLOBAL(void) +jpeg_add_quant_table (j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, boolean force_baseline) +/* Define a quantization table equal to the basic_table times + * a scale factor (given as a percentage). + * If force_baseline is TRUE, the computed quantization table entries + * are limited to 1..255 for JPEG baseline compatibility. + */ +{ + JQUANT_TBL ** qtblptr; + int i; + long temp; + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (which_tbl < 0 || which_tbl >= NUM_QUANT_TBLS) + ERREXIT1(cinfo, JERR_DQT_INDEX, which_tbl); + + qtblptr = & cinfo->quant_tbl_ptrs[which_tbl]; + + if (*qtblptr == NULL) + *qtblptr = jpeg_alloc_quant_table((j_common_ptr) cinfo); + + for (i = 0; i < DCTSIZE2; i++) { + temp = ((long) basic_table[i] * scale_factor + 50L) / 100L; + /* limit the values to the valid range */ + if (temp <= 0L) temp = 1L; + if (temp > 32767L) temp = 32767L; /* max quantizer needed for 12 bits */ + if (force_baseline && temp > 255L) + temp = 255L; /* limit to baseline range if requested */ + (*qtblptr)->quantval[i] = (UINT16) temp; + } + + /* Initialize sent_table FALSE so table will be written to JPEG file. */ + (*qtblptr)->sent_table = FALSE; +} + + +GLOBAL(void) +jpeg_set_linear_quality (j_compress_ptr cinfo, int scale_factor, + boolean force_baseline) +/* Set or change the 'quality' (quantization) setting, using default tables + * and a straight percentage-scaling quality scale. In most cases it's better + * to use jpeg_set_quality (below); this entry point is provided for + * applications that insist on a linear percentage scaling. + */ +{ + /* These are the sample quantization tables given in JPEG spec section K.1. + * The spec says that the values given produce "good" quality, and + * when divided by 2, "very good" quality. + */ + static const unsigned int std_luminance_quant_tbl[DCTSIZE2] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 + }; + static const unsigned int std_chrominance_quant_tbl[DCTSIZE2] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + }; + + /* Set up two quantization tables using the specified scaling */ + jpeg_add_quant_table(cinfo, 0, std_luminance_quant_tbl, + scale_factor, force_baseline); + jpeg_add_quant_table(cinfo, 1, std_chrominance_quant_tbl, + scale_factor, force_baseline); +} + + +GLOBAL(int) +jpeg_quality_scaling (int quality) +/* Convert a user-specified quality rating to a percentage scaling factor + * for an underlying quantization table, using our recommended scaling curve. + * The input 'quality' factor should be 0 (terrible) to 100 (very good). + */ +{ + /* Safety limit on quality factor. Convert 0 to 1 to avoid zero divide. */ + if (quality <= 0) quality = 1; + if (quality > 100) quality = 100; + + /* The basic table is used as-is (scaling 100) for a quality of 50. + * Qualities 50..100 are converted to scaling percentage 200 - 2*Q; + * note that at Q=100 the scaling is 0, which will cause jpeg_add_quant_table + * to make all the table entries 1 (hence, minimum quantization loss). + * Qualities 1..50 are converted to scaling percentage 5000/Q. + */ + if (quality < 50) + quality = 5000 / quality; + else + quality = 200 - quality*2; + + return quality; +} + + +GLOBAL(void) +jpeg_set_quality (j_compress_ptr cinfo, int quality, boolean force_baseline) +/* Set or change the 'quality' (quantization) setting, using default tables. + * This is the standard quality-adjusting entry point for typical user + * interfaces; only those who want detailed control over quantization tables + * would use the preceding three routines directly. + */ +{ + /* Convert user 0-100 rating to percentage scaling */ + quality = jpeg_quality_scaling(quality); + + /* Set up standard quality tables */ + jpeg_set_linear_quality(cinfo, quality, force_baseline); +} + + +/* + * Huffman table setup routines + */ + +LOCAL(void) +add_huff_table (j_compress_ptr cinfo, + JHUFF_TBL **htblptr, const UINT8 *bits, const UINT8 *val) +/* Define a Huffman table */ +{ + int nsymbols, len; + + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + /* Copy the number-of-symbols-of-each-code-length counts */ + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + + /* Validate the counts. We do this here mainly so we can copy the right + * number of symbols from the val[] array, without risking marching off + * the end of memory. jchuff.c will do a more thorough test later. + */ + nsymbols = 0; + for (len = 1; len <= 16; len++) + nsymbols += bits[len]; + if (nsymbols < 1 || nsymbols > 256) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + + MEMCOPY((*htblptr)->huffval, val, nsymbols * SIZEOF(UINT8)); + + /* Initialize sent_table FALSE so table will be written to JPEG file. */ + (*htblptr)->sent_table = FALSE; +} + + +#if BITS_IN_JSAMPLE == 8 +LOCAL(void) +std_huff_tables (j_compress_ptr cinfo) +/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */ +/* IMPORTANT: these are only valid for 8-bit data precision! */ +{ + static const UINT8 bits_dc_luminance[17] = + { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }; + static const UINT8 val_dc_luminance[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + static const UINT8 bits_dc_chrominance[17] = + { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; + static const UINT8 val_dc_chrominance[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + static const UINT8 bits_ac_luminance[17] = + { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }; + static const UINT8 val_ac_luminance[] = + { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa }; + + static const UINT8 bits_ac_chrominance[17] = + { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }; + static const UINT8 val_ac_chrominance[] = + { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa }; + + add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[0], + bits_dc_luminance, val_dc_luminance); + add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[0], + bits_ac_luminance, val_ac_luminance); + add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[1], + bits_dc_chrominance, val_dc_chrominance); + add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[1], + bits_ac_chrominance, val_ac_chrominance); +} +#endif /* BITS_IN_JSAMPLE == 8 */ + +#if BITS_IN_JSAMPLE == 12 +LOCAL(void) +std_huff_tables (j_compress_ptr cinfo) +/* + * Note: these are not really "standard" since the specification includes + * no 12bit tables. But they should work with any image, and at least + * moderately adequate as default tables. + * https://sourceforge.net/tracker/?func=detail&aid=2809979&group_id=159521&atid=812162 + */ +{ + static const UINT8 bits_dc_luminance[17] = + { 0, 0, 2, 3, 1, 0, 3, 1, 0, 3, 1, 1, 1, 0, 0, 0 }; + static const UINT8 val_dc_luminance[] = + { 11, 12, 9, 10, 13, 8, 6, 7, 14, 5, 0, 3, 4, 1, 15, 2 }; + + static const UINT8 bits_dc_chrominance[17] = + { 0, 0, 2, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }; + static const UINT8 val_dc_chrominance[] = + { 9, 10, 8, 11, 7, 12, 5, 6, 13, 14, 4, 3, 2, 1, 0, 15 }; + + static const UINT8 bits_ac_luminance[17] = + { 0, 0, 1, 4, 2, 2, 2, 1, 4, 1, 2, 0, 1, 0, 1, 0, 235 }; + + static const UINT8 val_ac_luminance[] = + { +0x02,0x01,0x03,0x04,0x05,0x06,0x07,0x08,0x12,0x09,0x11,0x13,0x00,0x14,0x21,0x22 +,0x15,0x0a,0x23,0x31,0x16,0x32,0x17,0x24,0x33,0x41,0x18,0x25,0x42,0x51,0x0b,0x26 +,0x19,0x43,0x52,0x61,0x35,0x62,0x71,0x0c,0x0d,0x0e,0x0f,0x10,0x1a,0x1b,0x1c,0x1d +,0x1e,0x1f,0x20,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x34,0x36,0x37 +,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x44,0x45,0x46,0x47,0x48,0x49,0x4a +,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c +,0x5d,0x5e,0x5f,0x60,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e +,0x6f,0x70,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f +,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f +,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f +,0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf +,0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf +,0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf +,0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf +,0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef +,0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff + }; + + static const UINT8 bits_ac_chrominance[17] = + { 0, 0, 1, 3, 2, 5, 1, 5, 5, 3, 7, 4, 4, 3, 4, 6, 203 }; + static const UINT8 val_ac_chrominance[] = + { +0x01,0x02,0x03,0x11,0x04,0x21,0x00,0x05,0x06,0x12,0x31,0x41,0x07,0x13,0x22,0x51 +,0x61,0x08,0x14,0x32,0x71,0x81,0x42,0x91,0xa1,0x09,0x15,0x23,0x52,0xb1,0xc1,0xf0 +,0x16,0x62,0xd1,0xe1,0x0a,0x24,0x72,0xf1,0x17,0x82,0x92,0x33,0x43,0x53,0xb2,0x0b +,0x0c,0x18,0x35,0xa2,0xc2,0x0d,0x0e,0x0f,0x10,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f +,0x20,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x34,0x36,0x37 +,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x44,0x45,0x46,0x47,0x48,0x49,0x4a +,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d +,0x5e,0x5f,0x60,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f +,0x70,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x83 +,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,0x90,0x93,0x94,0x95 +,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,0xa0,0xa3,0xa4,0xa5,0xa6,0xa7 +,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,0xb0,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9 +,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,0xc0,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb +,0xcc,0xcd,0xce,0xcf,0xd0,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc +,0xdd,0xde,0xdf,0xe0,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed +,0xee,0xef,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff + }; + + add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[0], + bits_dc_luminance, val_dc_luminance); + add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[0], + bits_ac_luminance, val_ac_luminance); + add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[1], + bits_dc_chrominance, val_dc_chrominance); + add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[1], + bits_ac_chrominance, val_ac_chrominance); +} +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* + * Default parameter setup for compression. + * + * Applications that don't choose to use this routine must do their + * own setup of all these parameters. Alternately, you can call this + * to establish defaults and then alter parameters selectively. This + * is the recommended approach since, if we add any new parameters, + * your code will still work (they'll be set to reasonable defaults). + */ + +GLOBAL(void) +jpeg_set_defaults (j_compress_ptr cinfo) +{ + int i; + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Allocate comp_info array large enough for maximum component count. + * Array is made permanent in case application wants to compress + * multiple images at same param settings. + */ + if (cinfo->comp_info == NULL) + cinfo->comp_info = (jpeg_component_info *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + MAX_COMPONENTS * SIZEOF(jpeg_component_info)); + + /* Initialize everything not dependent on the color space */ + + cinfo->data_precision = BITS_IN_JSAMPLE; + /* Set up two quantization tables using default quality of 75 */ + jpeg_set_quality(cinfo, 75, TRUE); + /* Set up two Huffman tables */ + std_huff_tables(cinfo); + + /* Initialize default arithmetic coding conditioning */ + for (i = 0; i < NUM_ARITH_TBLS; i++) { + cinfo->arith_dc_L[i] = 0; + cinfo->arith_dc_U[i] = 1; + cinfo->arith_ac_K[i] = 5; + } + + /* Default is no multiple-scan output */ + cinfo->scan_info = NULL; + cinfo->num_scans = 0; + + /* Expect normal source image, not raw downsampled data */ + cinfo->raw_data_in = FALSE; + + /* Use Huffman coding, not arithmetic coding, by default */ + cinfo->arith_code = FALSE; + + /* By default, don't do extra passes to optimize entropy coding */ + cinfo->optimize_coding = FALSE; + /* The standard Huffman tables are only valid for 8-bit data precision. + * If the precision is higher, force optimization on so that usable + * tables will be computed. This test can be removed if default tables + * are supplied that are valid for the desired precision. + */ + if (cinfo->data_precision > 8) + cinfo->optimize_coding = TRUE; + + /* By default, use the simpler non-cosited sampling alignment */ + cinfo->CCIR601_sampling = FALSE; + + /* No input smoothing */ + cinfo->smoothing_factor = 0; + + /* DCT algorithm preference */ + cinfo->dct_method = JDCT_DEFAULT; + + /* No restart markers */ + cinfo->restart_interval = 0; + cinfo->restart_in_rows = 0; + + /* Fill in default JFIF marker parameters. Note that whether the marker + * will actually be written is determined by jpeg_set_colorspace. + * + * By default, the library emits JFIF version code 1.01. + * An application that wants to emit JFIF 1.02 extension markers should set + * JFIF_minor_version to 2. We could probably get away with just defaulting + * to 1.02, but there may still be some decoders in use that will complain + * about that; saying 1.01 should minimize compatibility problems. + */ + cinfo->JFIF_major_version = 1; /* Default JFIF version = 1.01 */ + cinfo->JFIF_minor_version = 1; + cinfo->density_unit = 0; /* Pixel size is unknown by default */ + cinfo->X_density = 1; /* Pixel aspect ratio is square by default */ + cinfo->Y_density = 1; + + /* Choose JPEG colorspace based on input space, set defaults accordingly */ + + jpeg_default_colorspace(cinfo); +} + + +/* + * Select an appropriate JPEG colorspace for in_color_space. + */ + +GLOBAL(void) +jpeg_default_colorspace (j_compress_ptr cinfo) +{ + switch (cinfo->in_color_space) { + case JCS_GRAYSCALE: + jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); + break; + case JCS_RGB: + jpeg_set_colorspace(cinfo, JCS_YCbCr); + break; + case JCS_YCbCr: + jpeg_set_colorspace(cinfo, JCS_YCbCr); + break; + case JCS_CMYK: + jpeg_set_colorspace(cinfo, JCS_CMYK); /* By default, no translation */ + break; + case JCS_YCCK: + jpeg_set_colorspace(cinfo, JCS_YCCK); + break; + case JCS_UNKNOWN: + jpeg_set_colorspace(cinfo, JCS_UNKNOWN); + break; + default: + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + } +} + + +/* + * Set the JPEG colorspace, and choose colorspace-dependent default values. + */ + +GLOBAL(void) +jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) +{ + jpeg_component_info * compptr; + int ci; + +#define SET_COMP(index,id,hsamp,vsamp,quant,dctbl,actbl) \ + (compptr = &cinfo->comp_info[index], \ + compptr->component_id = (id), \ + compptr->h_samp_factor = (hsamp), \ + compptr->v_samp_factor = (vsamp), \ + compptr->quant_tbl_no = (quant), \ + compptr->dc_tbl_no = (dctbl), \ + compptr->ac_tbl_no = (actbl) ) + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* For all colorspaces, we use Q and Huff tables 0 for luminance components, + * tables 1 for chrominance components. + */ + + cinfo->jpeg_color_space = colorspace; + + cinfo->write_JFIF_header = FALSE; /* No marker for non-JFIF colorspaces */ + cinfo->write_Adobe_marker = FALSE; /* write no Adobe marker by default */ + + switch (colorspace) { + case JCS_GRAYSCALE: + cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */ + cinfo->num_components = 1; + /* JFIF specifies component ID 1 */ + SET_COMP(0, 1, 1,1, 0, 0,0); + break; + case JCS_RGB: + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag RGB */ + cinfo->num_components = 3; + SET_COMP(0, 0x52 /* 'R' */, 1,1, 0, 0,0); + SET_COMP(1, 0x47 /* 'G' */, 1,1, 0, 0,0); + SET_COMP(2, 0x42 /* 'B' */, 1,1, 0, 0,0); + break; + case JCS_YCbCr: + cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */ + cinfo->num_components = 3; + /* JFIF specifies component IDs 1,2,3 */ + /* We default to 2x2 subsamples of chrominance */ + SET_COMP(0, 1, 2,2, 0, 0,0); + SET_COMP(1, 2, 1,1, 1, 1,1); + SET_COMP(2, 3, 1,1, 1, 1,1); + break; + case JCS_CMYK: + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag CMYK */ + cinfo->num_components = 4; + SET_COMP(0, 0x43 /* 'C' */, 1,1, 0, 0,0); + SET_COMP(1, 0x4D /* 'M' */, 1,1, 0, 0,0); + SET_COMP(2, 0x59 /* 'Y' */, 1,1, 0, 0,0); + SET_COMP(3, 0x4B /* 'K' */, 1,1, 0, 0,0); + break; + case JCS_YCCK: + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag YCCK */ + cinfo->num_components = 4; + SET_COMP(0, 1, 2,2, 0, 0,0); + SET_COMP(1, 2, 1,1, 1, 1,1); + SET_COMP(2, 3, 1,1, 1, 1,1); + SET_COMP(3, 4, 2,2, 0, 0,0); + break; + case JCS_UNKNOWN: + cinfo->num_components = cinfo->input_components; + if (cinfo->num_components < 1 || cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + for (ci = 0; ci < cinfo->num_components; ci++) { + SET_COMP(ci, ci, 1,1, 0, 0,0); + } + break; + default: + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + } +} + + +#ifdef C_PROGRESSIVE_SUPPORTED + +LOCAL(jpeg_scan_info *) +fill_a_scan (jpeg_scan_info * scanptr, int ci, + int Ss, int Se, int Ah, int Al) +/* Support routine: generate one scan for specified component */ +{ + scanptr->comps_in_scan = 1; + scanptr->component_index[0] = ci; + scanptr->Ss = Ss; + scanptr->Se = Se; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + return scanptr; +} + +LOCAL(jpeg_scan_info *) +fill_scans (jpeg_scan_info * scanptr, int ncomps, + int Ss, int Se, int Ah, int Al) +/* Support routine: generate one scan for each component */ +{ + int ci; + + for (ci = 0; ci < ncomps; ci++) { + scanptr->comps_in_scan = 1; + scanptr->component_index[0] = ci; + scanptr->Ss = Ss; + scanptr->Se = Se; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + } + return scanptr; +} + +LOCAL(jpeg_scan_info *) +fill_dc_scans (jpeg_scan_info * scanptr, int ncomps, int Ah, int Al) +/* Support routine: generate interleaved DC scan if possible, else N scans */ +{ + int ci; + + if (ncomps <= MAX_COMPS_IN_SCAN) { + /* Single interleaved DC scan */ + scanptr->comps_in_scan = ncomps; + for (ci = 0; ci < ncomps; ci++) + scanptr->component_index[ci] = ci; + scanptr->Ss = scanptr->Se = 0; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + } else { + /* Noninterleaved DC scan for each component */ + scanptr = fill_scans(scanptr, ncomps, 0, 0, Ah, Al); + } + return scanptr; +} + + +/* + * Create a recommended progressive-JPEG script. + * cinfo->num_components and cinfo->jpeg_color_space must be correct. + */ + +GLOBAL(void) +jpeg_simple_progression (j_compress_ptr cinfo) +{ + int ncomps = cinfo->num_components; + int nscans; + jpeg_scan_info * scanptr; + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Figure space needed for script. Calculation must match code below! */ + if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { + /* Custom script for YCbCr color images. */ + nscans = 10; + } else { + /* All-purpose script for other color spaces. */ + if (ncomps > MAX_COMPS_IN_SCAN) + nscans = 6 * ncomps; /* 2 DC + 4 AC scans per component */ + else + nscans = 2 + 4 * ncomps; /* 2 DC scans; 4 AC scans per component */ + } + + /* Allocate space for script. + * We need to put it in the permanent pool in case the application performs + * multiple compressions without changing the settings. To avoid a memory + * leak if jpeg_simple_progression is called repeatedly for the same JPEG + * object, we try to re-use previously allocated space, and we allocate + * enough space to handle YCbCr even if initially asked for grayscale. + */ + if (cinfo->script_space == NULL || cinfo->script_space_size < nscans) { + cinfo->script_space_size = MAX(nscans, 10); + cinfo->script_space = (jpeg_scan_info *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + cinfo->script_space_size * SIZEOF(jpeg_scan_info)); + } + scanptr = cinfo->script_space; + cinfo->scan_info = scanptr; + cinfo->num_scans = nscans; + + if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { + /* Custom script for YCbCr color images. */ + /* Initial DC scan */ + scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); + /* Initial AC scan: get some luma data out in a hurry */ + scanptr = fill_a_scan(scanptr, 0, 1, 5, 0, 2); + /* Chroma data is too small to be worth expending many scans on */ + scanptr = fill_a_scan(scanptr, 2, 1, 63, 0, 1); + scanptr = fill_a_scan(scanptr, 1, 1, 63, 0, 1); + /* Complete spectral selection for luma AC */ + scanptr = fill_a_scan(scanptr, 0, 6, 63, 0, 2); + /* Refine next bit of luma AC */ + scanptr = fill_a_scan(scanptr, 0, 1, 63, 2, 1); + /* Finish DC successive approximation */ + scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); + /* Finish AC successive approximation */ + scanptr = fill_a_scan(scanptr, 2, 1, 63, 1, 0); + scanptr = fill_a_scan(scanptr, 1, 1, 63, 1, 0); + /* Luma bottom bit comes last since it's usually largest scan */ + scanptr = fill_a_scan(scanptr, 0, 1, 63, 1, 0); + } else { + /* All-purpose script for other color spaces. */ + /* Successive approximation first pass */ + scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); + scanptr = fill_scans(scanptr, ncomps, 1, 5, 0, 2); + scanptr = fill_scans(scanptr, ncomps, 6, 63, 0, 2); + /* Successive approximation second pass */ + scanptr = fill_scans(scanptr, ncomps, 1, 63, 2, 1); + /* Successive approximation final pass */ + scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); + scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0); + } +} + +#endif /* C_PROGRESSIVE_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcphuff.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcphuff.c new file mode 100644 index 000000000..07f9178b0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcphuff.c @@ -0,0 +1,833 @@ +/* + * jcphuff.c + * + * Copyright (C) 1995-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy encoding routines for progressive JPEG. + * + * We do not support output suspension in this module, since the library + * currently does not allow multiple-scan files to be written with output + * suspension. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jchuff.h" /* Declarations shared with jchuff.c */ + +#ifdef C_PROGRESSIVE_SUPPORTED + +/* Expanded entropy encoder object for progressive Huffman encoding. */ + +typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + + /* Mode flag: TRUE for optimization, FALSE for actual data output */ + boolean gather_statistics; + + /* Bit-level coding status. + * next_output_byte/free_in_buffer are local copies of cinfo->dest fields. + */ + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + INT32 put_buffer; /* current bit-accumulation buffer */ + int put_bits; /* # of bits now in it */ + j_compress_ptr cinfo; /* link to cinfo (needed for dump_buffer) */ + + /* Coding status for DC components */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ + + /* Coding status for AC components */ + int ac_tbl_no; /* the table number of the single component */ + unsigned int EOBRUN; /* run length of EOBs */ + unsigned int BE; /* # of buffered correction bits before MCU */ + char * bit_buffer; /* buffer for correction bits (1 per char) */ + /* packing correction bits tightly would save some space but cost time... */ + + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + int next_restart_num; /* next restart number to write (0-7) */ + + /* Pointers to derived tables (these workspaces have image lifespan). + * Since any one scan codes only DC or only AC, we only need one set + * of tables, not one for DC and one for AC. + */ + c_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; + + /* Statistics tables for optimization; again, one set is enough */ + long * count_ptrs[NUM_HUFF_TBLS]; +} phuff_entropy_encoder; + +typedef phuff_entropy_encoder * phuff_entropy_ptr; + +/* MAX_CORR_BITS is the number of bits the AC refinement correction-bit + * buffer can hold. Larger sizes may slightly improve compression, but + * 1000 is already well into the realm of overkill. + * The minimum safe size is 64 bits. + */ + +#define MAX_CORR_BITS 1000 /* Max # of correction bits I can buffer */ + +/* IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than INT32. + * We assume that int right shift is unsigned if INT32 right shift is, + * which should be safe. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define ISHIFT_TEMPS int ishift_temp; +#define IRIGHT_SHIFT(x,shft) \ + ((ishift_temp = (x)) < 0 ? \ + (ishift_temp >> (shft)) | ((~0) << (16-(shft))) : \ + (ishift_temp >> (shft))) +#else +#define ISHIFT_TEMPS +#define IRIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + +/* Forward declarations */ +METHODDEF(boolean) encode_mcu_DC_first JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) encode_mcu_AC_first JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) encode_mcu_DC_refine JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) encode_mcu_AC_refine JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_phuff JPP((j_compress_ptr cinfo)); +METHODDEF(void) finish_pass_gather_phuff JPP((j_compress_ptr cinfo)); + + +/* + * Initialize for a Huffman-compressed scan using progressive JPEG. + */ + +METHODDEF(void) +start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + boolean is_DC_band; + int ci, tbl; + jpeg_component_info * compptr; + + entropy->cinfo = cinfo; + entropy->gather_statistics = gather_statistics; + + is_DC_band = (cinfo->Ss == 0); + + /* We assume jcmaster.c already validated the scan parameters. */ + + /* Select execution routines */ + if (cinfo->Ah == 0) { + if (is_DC_band) + entropy->pub.encode_mcu = encode_mcu_DC_first; + else + entropy->pub.encode_mcu = encode_mcu_AC_first; + } else { + if (is_DC_band) + entropy->pub.encode_mcu = encode_mcu_DC_refine; + else { + entropy->pub.encode_mcu = encode_mcu_AC_refine; + /* AC refinement needs a correction bit buffer */ + if (entropy->bit_buffer == NULL) + entropy->bit_buffer = (char *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + MAX_CORR_BITS * SIZEOF(char)); + } + } + if (gather_statistics) + entropy->pub.finish_pass = finish_pass_gather_phuff; + else + entropy->pub.finish_pass = finish_pass_phuff; + + /* Only DC coefficients may be interleaved, so cinfo->comps_in_scan = 1 + * for AC coefficients. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Initialize DC predictions to 0 */ + entropy->last_dc_val[ci] = 0; + /* Get table index */ + if (is_DC_band) { + if (cinfo->Ah != 0) /* DC refinement needs no table */ + continue; + tbl = compptr->dc_tbl_no; + } else { + entropy->ac_tbl_no = tbl = compptr->ac_tbl_no; + } + if (gather_statistics) { + /* Check for invalid table index */ + /* (make_c_derived_tbl does this in the other path) */ + if (tbl < 0 || tbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tbl); + /* Allocate and zero the statistics tables */ + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + if (entropy->count_ptrs[tbl] == NULL) + entropy->count_ptrs[tbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->count_ptrs[tbl], 257 * SIZEOF(long)); + } else { + /* Compute derived values for Huffman table */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_c_derived_tbl(cinfo, is_DC_band, tbl, + & entropy->derived_tbls[tbl]); + } + } + + /* Initialize AC stuff */ + entropy->EOBRUN = 0; + entropy->BE = 0; + + /* Initialize bit buffer to empty */ + entropy->put_buffer = 0; + entropy->put_bits = 0; + + /* Initialize restart stuff */ + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num = 0; +} + + +/* Outputting bytes to the file. + * NB: these must be called only when actually outputting, + * that is, entropy->gather_statistics == FALSE. + */ + +/* Emit a byte */ +#define emit_byte(entropy,val) \ + { *(entropy)->next_output_byte++ = (JOCTET) (val); \ + if (--(entropy)->free_in_buffer == 0) \ + dump_buffer(entropy); } + + +LOCAL(void) +dump_buffer (phuff_entropy_ptr entropy) +/* Empty the output buffer; we do not support suspension in this module. */ +{ + struct jpeg_destination_mgr * dest = entropy->cinfo->dest; + + if (! (*dest->empty_output_buffer) (entropy->cinfo)) + ERREXIT(entropy->cinfo, JERR_CANT_SUSPEND); + /* After a successful buffer dump, must reset buffer pointers */ + entropy->next_output_byte = dest->next_output_byte; + entropy->free_in_buffer = dest->free_in_buffer; +} + + +/* Outputting bits to the file */ + +/* Only the right 24 bits of put_buffer are used; the valid bits are + * left-justified in this part. At most 16 bits can be passed to emit_bits + * in one call, and we never retain more than 7 bits in put_buffer + * between calls, so 24 bits are sufficient. + */ + +INLINE +LOCAL(void) +emit_bits (phuff_entropy_ptr entropy, unsigned int code, int size) +/* Emit some bits, unless we are in gather mode */ +{ + /* This routine is heavily used, so it's worth coding tightly. */ + register INT32 put_buffer = (INT32) code; + register int put_bits = entropy->put_bits; + + /* if size is 0, caller used an invalid Huffman table entry */ + if (size == 0) + ERREXIT(entropy->cinfo, JERR_HUFF_MISSING_CODE); + + if (entropy->gather_statistics) + return; /* do nothing if we're only getting stats */ + + put_buffer &= (((INT32) 1)<put_buffer; /* and merge with old buffer contents */ + + while (put_bits >= 8) { + int c = (int) ((put_buffer >> 16) & 0xFF); + + emit_byte(entropy, c); + if (c == 0xFF) { /* need to stuff a zero byte? */ + emit_byte(entropy, 0); + } + put_buffer <<= 8; + put_bits -= 8; + } + + entropy->put_buffer = put_buffer; /* update variables */ + entropy->put_bits = put_bits; +} + + +LOCAL(void) +flush_bits (phuff_entropy_ptr entropy) +{ + emit_bits(entropy, 0x7F, 7); /* fill any partial byte with ones */ + entropy->put_buffer = 0; /* and reset bit-buffer to empty */ + entropy->put_bits = 0; +} + + +/* + * Emit (or just count) a Huffman symbol. + */ + +INLINE +LOCAL(void) +emit_symbol (phuff_entropy_ptr entropy, int tbl_no, int symbol) +{ + if (entropy->gather_statistics) + entropy->count_ptrs[tbl_no][symbol]++; + else { + c_derived_tbl * tbl = entropy->derived_tbls[tbl_no]; + emit_bits(entropy, tbl->ehufco[symbol], tbl->ehufsi[symbol]); + } +} + + +/* + * Emit bits from a correction bit buffer. + */ + +LOCAL(void) +emit_buffered_bits (phuff_entropy_ptr entropy, char * bufstart, + unsigned int nbits) +{ + if (entropy->gather_statistics) + return; /* no real work */ + + while (nbits > 0) { + emit_bits(entropy, (unsigned int) (*bufstart), 1); + bufstart++; + nbits--; + } +} + + +/* + * Emit any pending EOBRUN symbol. + */ + +LOCAL(void) +emit_eobrun (phuff_entropy_ptr entropy) +{ + register int temp, nbits; + + if (entropy->EOBRUN > 0) { /* if there is any pending EOBRUN */ + temp = entropy->EOBRUN; + nbits = 0; + while ((temp >>= 1)) + nbits++; + /* safety check: shouldn't happen given limited correction-bit buffer */ + if (nbits > 14) + ERREXIT(entropy->cinfo, JERR_HUFF_MISSING_CODE); + + emit_symbol(entropy, entropy->ac_tbl_no, nbits << 4); + if (nbits) + emit_bits(entropy, entropy->EOBRUN, nbits); + + entropy->EOBRUN = 0; + + /* Emit any buffered correction bits */ + emit_buffered_bits(entropy, entropy->bit_buffer, entropy->BE); + entropy->BE = 0; + } +} + + +/* + * Emit a restart marker & resynchronize predictions. + */ + +LOCAL(void) +emit_restart (phuff_entropy_ptr entropy, int restart_num) +{ + int ci; + + emit_eobrun(entropy); + + if (! entropy->gather_statistics) { + flush_bits(entropy); + emit_byte(entropy, 0xFF); + emit_byte(entropy, JPEG_RST0 + restart_num); + } + + if (entropy->cinfo->Ss == 0) { + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < entropy->cinfo->comps_in_scan; ci++) + entropy->last_dc_val[ci] = 0; + } else { + /* Re-initialize all AC-related fields to 0 */ + entropy->EOBRUN = 0; + entropy->BE = 0; + } +} + + +/* + * MCU encoding for DC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + register int temp, temp2; + register int nbits; + int blkn, ci; + int Al = cinfo->Al; + JBLOCKROW block; + jpeg_component_info * compptr; + ISHIFT_TEMPS + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart(entropy, entropy->next_restart_num); + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + + /* Compute the DC value after the required point transform by Al. + * This is simply an arithmetic right shift. + */ + temp2 = IRIGHT_SHIFT((int) ((*block)[0]), Al); + + /* DC differences are figured on the point-transformed values. */ + temp = temp2 - entropy->last_dc_val[ci]; + entropy->last_dc_val[ci] = temp2; + + /* Encode the DC coefficient difference per section G.1.2.1 */ + temp2 = temp; + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count/emit the Huffman-coded symbol for the number of bits */ + emit_symbol(entropy, compptr->dc_tbl_no, nbits); + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (nbits) /* emit_bits rejects calls with size 0 */ + emit_bits(entropy, (unsigned int) temp2, nbits); + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * MCU encoding for AC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + register int temp, temp2; + register int nbits; + register int r, k; + int Se = cinfo->Se; + int Al = cinfo->Al; + JBLOCKROW block; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart(entropy, entropy->next_restart_num); + + /* Encode the MCU data block */ + block = MCU_data[0]; + + /* Encode the AC coefficients per section G.1.2.2, fig. G.3 */ + + r = 0; /* r = run length of zeros */ + + for (k = cinfo->Ss; k <= Se; k++) { + if ((temp = (*block)[jpeg_natural_order[k]]) == 0) { + r++; + continue; + } + /* We must apply the point transform by Al. For AC coefficients this + * is an integer division with rounding towards 0. To do this portably + * in C, we shift after obtaining the absolute value; so the code is + * interwoven with finding the abs value (temp) and output bits (temp2). + */ + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + temp >>= Al; /* apply the point transform */ + /* For a negative coef, want temp2 = bitwise complement of abs(coef) */ + temp2 = ~temp; + } else { + temp >>= Al; /* apply the point transform */ + temp2 = temp; + } + /* Watch out for case that nonzero coef is zero after point transform */ + if (temp == 0) { + r++; + continue; + } + + /* Emit any pending EOBRUN */ + if (entropy->EOBRUN > 0) + emit_eobrun(entropy); + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + emit_symbol(entropy, entropy->ac_tbl_no, 0xF0); + r -= 16; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count/emit Huffman symbol for run length / number of bits */ + emit_symbol(entropy, entropy->ac_tbl_no, (r << 4) + nbits); + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + emit_bits(entropy, (unsigned int) temp2, nbits); + + r = 0; /* reset zero run length */ + } + + if (r > 0) { /* If there are trailing zeroes, */ + entropy->EOBRUN++; /* count an EOB */ + if (entropy->EOBRUN == 0x7FFF) + emit_eobrun(entropy); /* force it out to avoid overflow */ + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * MCU encoding for DC successive approximation refinement scan. + * Note: we assume such scans can be multi-component, although the spec + * is not very clear on the point. + */ + +METHODDEF(boolean) +encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + register int temp; + int blkn; + int Al = cinfo->Al; + JBLOCKROW block; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart(entropy, entropy->next_restart_num); + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + + /* We simply emit the Al'th bit of the DC coefficient value. */ + temp = (*block)[0]; + emit_bits(entropy, (unsigned int) (temp >> Al), 1); + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * MCU encoding for AC successive approximation refinement scan. + */ + +METHODDEF(boolean) +encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + register int temp; + register int r, k; + int EOB; + char *BR_buffer; + unsigned int BR; + int Se = cinfo->Se; + int Al = cinfo->Al; + JBLOCKROW block; + int absvalues[DCTSIZE2]; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart(entropy, entropy->next_restart_num); + + /* Encode the MCU data block */ + block = MCU_data[0]; + + /* It is convenient to make a pre-pass to determine the transformed + * coefficients' absolute values and the EOB position. + */ + EOB = 0; + for (k = cinfo->Ss; k <= Se; k++) { + temp = (*block)[jpeg_natural_order[k]]; + /* We must apply the point transform by Al. For AC coefficients this + * is an integer division with rounding towards 0. To do this portably + * in C, we shift after obtaining the absolute value. + */ + if (temp < 0) + temp = -temp; /* temp is abs value of input */ + temp >>= Al; /* apply the point transform */ + absvalues[k] = temp; /* save abs value for main pass */ + if (temp == 1) + EOB = k; /* EOB = index of last newly-nonzero coef */ + } + + /* Encode the AC coefficients per section G.1.2.3, fig. G.7 */ + + r = 0; /* r = run length of zeros */ + BR = 0; /* BR = count of buffered bits added now */ + BR_buffer = entropy->bit_buffer + entropy->BE; /* Append bits to buffer */ + + for (k = cinfo->Ss; k <= Se; k++) { + if ((temp = absvalues[k]) == 0) { + r++; + continue; + } + + /* Emit any required ZRLs, but not if they can be folded into EOB */ + while (r > 15 && k <= EOB) { + /* emit any pending EOBRUN and the BE correction bits */ + emit_eobrun(entropy); + /* Emit ZRL */ + emit_symbol(entropy, entropy->ac_tbl_no, 0xF0); + r -= 16; + /* Emit buffered correction bits that must be associated with ZRL */ + emit_buffered_bits(entropy, BR_buffer, BR); + BR_buffer = entropy->bit_buffer; /* BE bits are gone now */ + BR = 0; + } + + /* If the coef was previously nonzero, it only needs a correction bit. + * NOTE: a straight translation of the spec's figure G.7 would suggest + * that we also need to test r > 15. But if r > 15, we can only get here + * if k > EOB, which implies that this coefficient is not 1. + */ + if (temp > 1) { + /* The correction bit is the next bit of the absolute value. */ + BR_buffer[BR++] = (char) (temp & 1); + continue; + } + + /* Emit any pending EOBRUN and the BE correction bits */ + emit_eobrun(entropy); + + /* Count/emit Huffman symbol for run length / number of bits */ + emit_symbol(entropy, entropy->ac_tbl_no, (r << 4) + 1); + + /* Emit output bit for newly-nonzero coef */ + temp = ((*block)[jpeg_natural_order[k]] < 0) ? 0 : 1; + emit_bits(entropy, (unsigned int) temp, 1); + + /* Emit buffered correction bits that must be associated with this code */ + emit_buffered_bits(entropy, BR_buffer, BR); + BR_buffer = entropy->bit_buffer; /* BE bits are gone now */ + BR = 0; + r = 0; /* reset zero run length */ + } + + if (r > 0 || BR > 0) { /* If there are trailing zeroes, */ + entropy->EOBRUN++; /* count an EOB */ + entropy->BE += BR; /* concat my correction bits to older ones */ + /* We force out the EOB if we risk either: + * 1. overflow of the EOB counter; + * 2. overflow of the correction bit buffer during the next MCU. + */ + if (entropy->EOBRUN == 0x7FFF || entropy->BE > (MAX_CORR_BITS-DCTSIZE2+1)) + emit_eobrun(entropy); + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * Finish up at the end of a Huffman-compressed progressive scan. + */ + +METHODDEF(void) +finish_pass_phuff (j_compress_ptr cinfo) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Flush out any buffered data */ + emit_eobrun(entropy); + flush_bits(entropy); + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; +} + + +/* + * Finish up a statistics-gathering pass and create the new Huffman tables. + */ + +METHODDEF(void) +finish_pass_gather_phuff (j_compress_ptr cinfo) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + boolean is_DC_band; + int ci, tbl; + jpeg_component_info * compptr; + JHUFF_TBL **htblptr; + boolean did[NUM_HUFF_TBLS]; + + /* Flush out buffered data (all we care about is counting the EOB symbol) */ + emit_eobrun(entropy); + + is_DC_band = (cinfo->Ss == 0); + + /* It's important not to apply jpeg_gen_optimal_table more than once + * per table, because it clobbers the input frequency counts! + */ + MEMZERO(did, SIZEOF(did)); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + if (is_DC_band) { + if (cinfo->Ah != 0) /* DC refinement needs no table */ + continue; + tbl = compptr->dc_tbl_no; + } else { + tbl = compptr->ac_tbl_no; + } + if (! did[tbl]) { + if (is_DC_band) + htblptr = & cinfo->dc_huff_tbl_ptrs[tbl]; + else + htblptr = & cinfo->ac_huff_tbl_ptrs[tbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->count_ptrs[tbl]); + did[tbl] = TRUE; + } + } +} + + +/* + * Module initialization routine for progressive Huffman entropy encoding. + */ + +GLOBAL(void) +jinit_phuff_encoder (j_compress_ptr cinfo) +{ + phuff_entropy_ptr entropy; + int i; + + entropy = (phuff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(phuff_entropy_encoder)); + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_phuff; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->derived_tbls[i] = NULL; + entropy->count_ptrs[i] = NULL; + } + entropy->bit_buffer = NULL; /* needed only in AC refinement scan */ +} + +#endif /* C_PROGRESSIVE_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcprepct.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcprepct.c new file mode 100644 index 000000000..fa93333db --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcprepct.c @@ -0,0 +1,354 @@ +/* + * jcprepct.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the compression preprocessing controller. + * This controller manages the color conversion, downsampling, + * and edge expansion steps. + * + * Most of the complexity here is associated with buffering input rows + * as required by the downsampler. See the comments at the head of + * jcsample.c for the downsampler's needs. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* At present, jcsample.c can request context rows only for smoothing. + * In the future, we might also need context rows for CCIR601 sampling + * or other more-complex downsampling procedures. The code to support + * context rows should be compiled only if needed. + */ +#ifdef INPUT_SMOOTHING_SUPPORTED +#define CONTEXT_ROWS_SUPPORTED +#endif + + +/* + * For the simple (no-context-row) case, we just need to buffer one + * row group's worth of pixels for the downsampling step. At the bottom of + * the image, we pad to a full row group by replicating the last pixel row. + * The downsampler's last output row is then replicated if needed to pad + * out to a full iMCU row. + * + * When providing context rows, we must buffer three row groups' worth of + * pixels. Three row groups are physically allocated, but the row pointer + * arrays are made five row groups high, with the extra pointers above and + * below "wrapping around" to point to the last and first real row groups. + * This allows the downsampler to access the proper context rows. + * At the top and bottom of the image, we create dummy context rows by + * copying the first or last real pixel row. This copying could be avoided + * by pointer hacking as is done in jdmainct.c, but it doesn't seem worth the + * trouble on the compression side. + */ + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_prep_controller pub; /* public fields */ + + /* Downsampling input buffer. This buffer holds color-converted data + * until we have enough to do a downsample step. + */ + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + JDIMENSION rows_to_go; /* counts rows remaining in source image */ + int next_buf_row; /* index of next row to store in color_buf */ + +#ifdef CONTEXT_ROWS_SUPPORTED /* only needed for context case */ + int this_row_group; /* starting row index of group to process */ + int next_buf_stop; /* downsample when we reach this index */ +#endif +} my_prep_controller; + +typedef my_prep_controller * my_prep_ptr; + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_prep (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + + if (pass_mode != JBUF_PASS_THRU) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + /* Initialize total-height counter for detecting bottom of image */ + prep->rows_to_go = cinfo->image_height; + /* Mark the conversion buffer empty */ + prep->next_buf_row = 0; +#ifdef CONTEXT_ROWS_SUPPORTED + /* Preset additional state variables for context mode. + * These aren't used in non-context mode, so we needn't test which mode. + */ + prep->this_row_group = 0; + /* Set next_buf_stop to stop after two row groups have been read in. */ + prep->next_buf_stop = 2 * cinfo->max_v_samp_factor; +#endif +} + + +/* + * Expand an image vertically from height input_rows to height output_rows, + * by duplicating the bottom row. + */ + +LOCAL(void) +expand_bottom_edge (JSAMPARRAY image_data, JDIMENSION num_cols, + int input_rows, int output_rows) +{ + register int row; + + for (row = input_rows; row < output_rows; row++) { + jcopy_sample_rows(image_data, input_rows-1, image_data, row, + 1, num_cols); + } +} + + +/* + * Process some data in the simple no-context case. + * + * Preprocessor output data is counted in "row groups". A row group + * is defined to be v_samp_factor sample rows of each component. + * Downsampling will produce this much data from each max_v_samp_factor + * input rows. + */ + +METHODDEF(void) +pre_process_data (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + int numrows, ci; + JDIMENSION inrows; + jpeg_component_info * compptr; + + while (*in_row_ctr < in_rows_avail && + *out_row_group_ctr < out_row_groups_avail) { + /* Do color conversion to fill the conversion buffer. */ + inrows = in_rows_avail - *in_row_ctr; + numrows = cinfo->max_v_samp_factor - prep->next_buf_row; + numrows = (int) MIN((JDIMENSION) numrows, inrows); + (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr, + prep->color_buf, + (JDIMENSION) prep->next_buf_row, + numrows); + *in_row_ctr += numrows; + prep->next_buf_row += numrows; + prep->rows_to_go -= numrows; + /* If at bottom of image, pad to fill the conversion buffer. */ + if (prep->rows_to_go == 0 && + prep->next_buf_row < cinfo->max_v_samp_factor) { + for (ci = 0; ci < cinfo->num_components; ci++) { + expand_bottom_edge(prep->color_buf[ci], cinfo->image_width, + prep->next_buf_row, cinfo->max_v_samp_factor); + } + prep->next_buf_row = cinfo->max_v_samp_factor; + } + /* If we've filled the conversion buffer, empty it. */ + if (prep->next_buf_row == cinfo->max_v_samp_factor) { + (*cinfo->downsample->downsample) (cinfo, + prep->color_buf, (JDIMENSION) 0, + output_buf, *out_row_group_ctr); + prep->next_buf_row = 0; + (*out_row_group_ctr)++; + } + /* If at bottom of image, pad the output to a full iMCU height. + * Note we assume the caller is providing a one-iMCU-height output buffer! + */ + if (prep->rows_to_go == 0 && + *out_row_group_ctr < out_row_groups_avail) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + expand_bottom_edge(output_buf[ci], + compptr->width_in_blocks * DCTSIZE, + (int) (*out_row_group_ctr * compptr->v_samp_factor), + (int) (out_row_groups_avail * compptr->v_samp_factor)); + } + *out_row_group_ctr = out_row_groups_avail; + break; /* can exit outer loop without test */ + } + } +} + + +#ifdef CONTEXT_ROWS_SUPPORTED + +/* + * Process some data in the context case. + */ + +METHODDEF(void) +pre_process_context (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + int numrows, ci; + int buf_height = cinfo->max_v_samp_factor * 3; + JDIMENSION inrows; + + while (*out_row_group_ctr < out_row_groups_avail) { + if (*in_row_ctr < in_rows_avail) { + /* Do color conversion to fill the conversion buffer. */ + inrows = in_rows_avail - *in_row_ctr; + numrows = prep->next_buf_stop - prep->next_buf_row; + numrows = (int) MIN((JDIMENSION) numrows, inrows); + (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr, + prep->color_buf, + (JDIMENSION) prep->next_buf_row, + numrows); + /* Pad at top of image, if first time through */ + if (prep->rows_to_go == cinfo->image_height) { + for (ci = 0; ci < cinfo->num_components; ci++) { + int row; + for (row = 1; row <= cinfo->max_v_samp_factor; row++) { + jcopy_sample_rows(prep->color_buf[ci], 0, + prep->color_buf[ci], -row, + 1, cinfo->image_width); + } + } + } + *in_row_ctr += numrows; + prep->next_buf_row += numrows; + prep->rows_to_go -= numrows; + } else { + /* Return for more data, unless we are at the bottom of the image. */ + if (prep->rows_to_go != 0) + break; + /* When at bottom of image, pad to fill the conversion buffer. */ + if (prep->next_buf_row < prep->next_buf_stop) { + for (ci = 0; ci < cinfo->num_components; ci++) { + expand_bottom_edge(prep->color_buf[ci], cinfo->image_width, + prep->next_buf_row, prep->next_buf_stop); + } + prep->next_buf_row = prep->next_buf_stop; + } + } + /* If we've gotten enough data, downsample a row group. */ + if (prep->next_buf_row == prep->next_buf_stop) { + (*cinfo->downsample->downsample) (cinfo, + prep->color_buf, + (JDIMENSION) prep->this_row_group, + output_buf, *out_row_group_ctr); + (*out_row_group_ctr)++; + /* Advance pointers with wraparound as necessary. */ + prep->this_row_group += cinfo->max_v_samp_factor; + if (prep->this_row_group >= buf_height) + prep->this_row_group = 0; + if (prep->next_buf_row >= buf_height) + prep->next_buf_row = 0; + prep->next_buf_stop = prep->next_buf_row + cinfo->max_v_samp_factor; + } + } +} + + +/* + * Create the wrapped-around downsampling input buffer needed for context mode. + */ + +LOCAL(void) +create_context_buffer (j_compress_ptr cinfo) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + int rgroup_height = cinfo->max_v_samp_factor; + int ci, i; + jpeg_component_info * compptr; + JSAMPARRAY true_buffer, fake_buffer; + + /* Grab enough space for fake row pointers for all the components; + * we need five row groups' worth of pointers for each component. + */ + fake_buffer = (JSAMPARRAY) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (cinfo->num_components * 5 * rgroup_height) * + SIZEOF(JSAMPROW)); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Allocate the actual buffer space (3 row groups) for this component. + * We make the buffer wide enough to allow the downsampler to edge-expand + * horizontally within the buffer, if it so chooses. + */ + true_buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (((long) compptr->width_in_blocks * DCTSIZE * + cinfo->max_h_samp_factor) / compptr->h_samp_factor), + (JDIMENSION) (3 * rgroup_height)); + /* Copy true buffer row pointers into the middle of the fake row array */ + MEMCOPY(fake_buffer + rgroup_height, true_buffer, + 3 * rgroup_height * SIZEOF(JSAMPROW)); + /* Fill in the above and below wraparound pointers */ + for (i = 0; i < rgroup_height; i++) { + fake_buffer[i] = true_buffer[2 * rgroup_height + i]; + fake_buffer[4 * rgroup_height + i] = true_buffer[i]; + } + prep->color_buf[ci] = fake_buffer + rgroup_height; + fake_buffer += 5 * rgroup_height; /* point to space for next component */ + } +} + +#endif /* CONTEXT_ROWS_SUPPORTED */ + + +/* + * Initialize preprocessing controller. + */ + +GLOBAL(void) +jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_prep_ptr prep; + int ci; + jpeg_component_info * compptr; + + if (need_full_buffer) /* safety check */ + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + prep = (my_prep_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_prep_controller)); + cinfo->prep = (struct jpeg_c_prep_controller *) prep; + prep->pub.start_pass = start_pass_prep; + + /* Allocate the color conversion buffer. + * We make the buffer wide enough to allow the downsampler to edge-expand + * horizontally within the buffer, if it so chooses. + */ + if (cinfo->downsample->need_context_rows) { + /* Set up to provide context rows */ +#ifdef CONTEXT_ROWS_SUPPORTED + prep->pub.pre_process_data = pre_process_context; + create_context_buffer(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* No context, just make it tall enough for one row group */ + prep->pub.pre_process_data = pre_process_data; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + prep->color_buf[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (((long) compptr->width_in_blocks * DCTSIZE * + cinfo->max_h_samp_factor) / compptr->h_samp_factor), + (JDIMENSION) cinfo->max_v_samp_factor); + } + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcsample.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcsample.c new file mode 100644 index 000000000..c544c7dc9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jcsample.c @@ -0,0 +1,520 @@ +/* + * jcsample.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains downsampling routines. + * + * Downsampling input data is counted in "row groups". A row group + * is defined to be max_v_samp_factor pixel rows of each component, + * from which the downsampler produces v_samp_factor sample rows. + * A single row group is processed in each call to the downsampler module. + * + * The downsampler is responsible for edge-expansion of its output data + * to fill an integral number of DCT blocks horizontally. The source buffer + * may be modified if it is helpful for this purpose (the source buffer is + * allocated wide enough to correspond to the desired output width). + * The caller (the prep controller) is responsible for vertical padding. + * + * The downsampler may request "context rows" by setting need_context_rows + * during startup. In this case, the input arrays will contain at least + * one row group's worth of pixels above and below the passed-in data; + * the caller will create dummy rows at image top and bottom by replicating + * the first or last real pixel row. + * + * An excellent reference for image resampling is + * Digital Image Warping, George Wolberg, 1990. + * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. + * + * The downsampling algorithm used here is a simple average of the source + * pixels covered by the output pixel. The hi-falutin sampling literature + * refers to this as a "box filter". In general the characteristics of a box + * filter are not very good, but for the specific cases we normally use (1:1 + * and 2:1 ratios) the box is equivalent to a "triangle filter" which is not + * nearly so bad. If you intend to use other sampling ratios, you'd be well + * advised to improve this code. + * + * A simple input-smoothing capability is provided. This is mainly intended + * for cleaning up color-dithered GIF input files (if you find it inadequate, + * we suggest using an external filtering program such as pnmconvol). When + * enabled, each input pixel P is replaced by a weighted sum of itself and its + * eight neighbors. P's weight is 1-8*SF and each neighbor's weight is SF, + * where SF = (smoothing_factor / 1024). + * Currently, smoothing is only supported for 2h2v sampling factors. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#include "cpl_port.h" + +/* Pointer to routine to downsample a single component */ +typedef JMETHOD(void, downsample1_ptr, + (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data)); + +/* Private subobject */ + +typedef struct { + struct jpeg_downsampler pub; /* public fields */ + + /* Downsampling method pointers, one per component */ + downsample1_ptr methods[MAX_COMPONENTS]; +} my_downsampler; + +typedef my_downsampler * my_downsample_ptr; + + +/* + * Initialize for a downsampling pass. + */ + +METHODDEF(void) +start_pass_downsample (CPL_UNUSED j_compress_ptr cinfo) +{ + /* no work for now */ +} + + +/* + * Expand a component horizontally from width input_cols to width output_cols, + * by duplicating the rightmost samples. + */ + +LOCAL(void) +expand_right_edge (JSAMPARRAY image_data, int num_rows, + JDIMENSION input_cols, JDIMENSION output_cols) +{ + register JSAMPROW ptr; + register JSAMPLE pixval; + register int count; + int row; + int numcols = (int) (output_cols - input_cols); + + if (numcols > 0) { + for (row = 0; row < num_rows; row++) { + ptr = image_data[row] + input_cols; + pixval = ptr[-1]; /* don't need GETJSAMPLE() here */ + for (count = numcols; count > 0; count--) + *ptr++ = pixval; + } + } +} + + +/* + * Do downsampling for a whole row group (all components). + * + * In this version we simply downsample each component independently. + */ + +METHODDEF(void) +sep_downsample (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, JDIMENSION out_row_group_index) +{ + my_downsample_ptr downsample = (my_downsample_ptr) cinfo->downsample; + int ci; + jpeg_component_info * compptr; + JSAMPARRAY in_ptr, out_ptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + in_ptr = input_buf[ci] + in_row_index; + out_ptr = output_buf[ci] + (out_row_group_index * compptr->v_samp_factor); + (*downsample->methods[ci]) (cinfo, compptr, in_ptr, out_ptr); + } +} + + +/* + * Downsample pixel values of a single component. + * One row group is processed per call. + * This version handles arbitrary integral sampling ratios, without smoothing. + * Note that this version is not actually used for customary sampling ratios. + */ + +METHODDEF(void) +int_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v; + JDIMENSION outcol, outcol_h; /* outcol_h == outcol*h_expand */ + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + JSAMPROW inptr, outptr; + INT32 outvalue; + + h_expand = cinfo->max_h_samp_factor / compptr->h_samp_factor; + v_expand = cinfo->max_v_samp_factor / compptr->v_samp_factor; + numpix = h_expand * v_expand; + numpix2 = numpix/2; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data, cinfo->max_v_samp_factor, + cinfo->image_width, output_cols * h_expand); + + inrow = 0; + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + for (outcol = 0, outcol_h = 0; outcol < output_cols; + outcol++, outcol_h += h_expand) { + outvalue = 0; + for (v = 0; v < v_expand; v++) { + inptr = input_data[inrow+v] + outcol_h; + for (h = 0; h < h_expand; h++) { + outvalue += (INT32) GETJSAMPLE(*inptr++); + } + } + *outptr++ = (JSAMPLE) ((outvalue + numpix2) / numpix); + } + inrow += v_expand; + } +} + + +/* + * Downsample pixel values of a single component. + * This version handles the special case of a full-size component, + * without smoothing. + */ + +METHODDEF(void) +fullsize_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + /* Copy the data */ + jcopy_sample_rows(input_data, 0, output_data, 0, + cinfo->max_v_samp_factor, cinfo->image_width); + /* Edge-expand */ + expand_right_edge(output_data, cinfo->max_v_samp_factor, + cinfo->image_width, compptr->width_in_blocks * DCTSIZE); +} + + +/* + * Downsample pixel values of a single component. + * This version handles the common case of 2:1 horizontal and 1:1 vertical, + * without smoothing. + * + * A note about the "bias" calculations: when rounding fractional values to + * integer, we do not want to always round 0.5 up to the next integer. + * If we did that, we'd introduce a noticeable bias towards larger values. + * Instead, this code is arranged so that 0.5 will be rounded up or down at + * alternate pixel locations (a simple ordered dither pattern). + */ + +METHODDEF(void) +h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int outrow; + JDIMENSION outcol; + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + register JSAMPROW inptr, outptr; + register int bias; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data, cinfo->max_v_samp_factor, + cinfo->image_width, output_cols * 2); + + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + inptr = input_data[outrow]; + bias = 0; /* bias = 0,1,0,1,... for successive samples */ + for (outcol = 0; outcol < output_cols; outcol++) { + *outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr) + GETJSAMPLE(inptr[1]) + + bias) >> 1); + bias ^= 1; /* 0=>1, 1=>0 */ + inptr += 2; + } + } +} + + +/* + * Downsample pixel values of a single component. + * This version handles the standard case of 2:1 horizontal and 2:1 vertical, + * without smoothing. + */ + +METHODDEF(void) +h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow, outrow; + JDIMENSION outcol; + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + register JSAMPROW inptr0, inptr1, outptr; + register int bias; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data, cinfo->max_v_samp_factor, + cinfo->image_width, output_cols * 2); + + inrow = 0; + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + inptr0 = input_data[inrow]; + inptr1 = input_data[inrow+1]; + bias = 1; /* bias = 1,2,1,2,... for successive samples */ + for (outcol = 0; outcol < output_cols; outcol++) { + *outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]) + + bias) >> 2); + bias ^= 3; /* 1=>2, 2=>1 */ + inptr0 += 2; inptr1 += 2; + } + inrow += 2; + } +} + + +#ifdef INPUT_SMOOTHING_SUPPORTED + +/* + * Downsample pixel values of a single component. + * This version handles the standard case of 2:1 horizontal and 2:1 vertical, + * with smoothing. One row of context is required. + */ + +METHODDEF(void) +h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow, outrow; + JDIMENSION colctr; + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + register JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr; + INT32 membersum, neighsum, memberscale, neighscale; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2, + cinfo->image_width, output_cols * 2); + + /* We don't bother to form the individual "smoothed" input pixel values; + * we can directly compute the output which is the average of the four + * smoothed values. Each of the four member pixels contributes a fraction + * (1-8*SF) to its own smoothed image and a fraction SF to each of the three + * other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final + * output. The four corner-adjacent neighbor pixels contribute a fraction + * SF to just one smoothed pixel, or SF/4 to the final output; while the + * eight edge-adjacent neighbors contribute SF to each of two smoothed + * pixels, or SF/2 overall. In order to use integer arithmetic, these + * factors are scaled by 2^16 = 65536. + * Also recall that SF = smoothing_factor / 1024. + */ + + memberscale = 16384 - cinfo->smoothing_factor * 80; /* scaled (1-5*SF)/4 */ + neighscale = cinfo->smoothing_factor * 16; /* scaled SF/4 */ + + inrow = 0; + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + inptr0 = input_data[inrow]; + inptr1 = input_data[inrow+1]; + above_ptr = input_data[inrow-1]; + below_ptr = input_data[inrow+2]; + + /* Special case for first column: pretend column -1 is same as column 0 */ + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[2]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[2]); + neighsum += neighsum; + neighsum += GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[2]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[2]); + membersum = membersum * memberscale + neighsum * neighscale; + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; + + for (colctr = output_cols - 2; colctr > 0; colctr--) { + /* sum of pixels directly mapped to this output element */ + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + /* sum of edge-neighbor pixels */ + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[2]) + + GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[2]); + /* The edge-neighbors count twice as much as corner-neighbors */ + neighsum += neighsum; + /* Add in the corner-neighbors */ + neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[2]) + + GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[2]); + /* form final output scaled up by 2^16 */ + membersum = membersum * memberscale + neighsum * neighscale; + /* round, descale and output it */ + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; + } + + /* Special case for last column */ + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[1]); + neighsum += neighsum; + neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[1]); + membersum = membersum * memberscale + neighsum * neighscale; + *outptr = (JSAMPLE) ((membersum + 32768) >> 16); + + inrow += 2; + } +} + + +/* + * Downsample pixel values of a single component. + * This version handles the special case of a full-size component, + * with smoothing. One row of context is required. + */ + +METHODDEF(void) +fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int outrow; + JDIMENSION colctr; + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + register JSAMPROW inptr, above_ptr, below_ptr, outptr; + INT32 membersum, neighsum, memberscale, neighscale; + int colsum, lastcolsum, nextcolsum; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2, + cinfo->image_width, output_cols); + + /* Each of the eight neighbor pixels contributes a fraction SF to the + * smoothed pixel, while the main pixel contributes (1-8*SF). In order + * to use integer arithmetic, these factors are multiplied by 2^16 = 65536. + * Also recall that SF = smoothing_factor / 1024. + */ + + memberscale = 65536L - cinfo->smoothing_factor * 512L; /* scaled 1-8*SF */ + neighscale = cinfo->smoothing_factor * 64; /* scaled SF */ + + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + inptr = input_data[outrow]; + above_ptr = input_data[outrow-1]; + below_ptr = input_data[outrow+1]; + + /* Special case for first column */ + colsum = GETJSAMPLE(*above_ptr++) + GETJSAMPLE(*below_ptr++) + + GETJSAMPLE(*inptr); + membersum = GETJSAMPLE(*inptr++); + nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) + + GETJSAMPLE(*inptr); + neighsum = colsum + (colsum - membersum) + nextcolsum; + membersum = membersum * memberscale + neighsum * neighscale; + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + lastcolsum = colsum; colsum = nextcolsum; + + for (colctr = output_cols - 2; colctr > 0; colctr--) { + membersum = GETJSAMPLE(*inptr++); + above_ptr++; below_ptr++; + nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) + + GETJSAMPLE(*inptr); + neighsum = lastcolsum + (colsum - membersum) + nextcolsum; + membersum = membersum * memberscale + neighsum * neighscale; + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + lastcolsum = colsum; colsum = nextcolsum; + } + + /* Special case for last column */ + membersum = GETJSAMPLE(*inptr); + neighsum = lastcolsum + (colsum - membersum) + colsum; + membersum = membersum * memberscale + neighsum * neighscale; + *outptr = (JSAMPLE) ((membersum + 32768) >> 16); + + } +} + +#endif /* INPUT_SMOOTHING_SUPPORTED */ + + +/* + * Module initialization routine for downsampling. + * Note that we must select a routine for each component. + */ + +GLOBAL(void) +jinit_downsampler (j_compress_ptr cinfo) +{ + my_downsample_ptr downsample; + int ci; + jpeg_component_info * compptr; + boolean smoothok = TRUE; + + downsample = (my_downsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_downsampler)); + cinfo->downsample = (struct jpeg_downsampler *) downsample; + downsample->pub.start_pass = start_pass_downsample; + downsample->pub.downsample = sep_downsample; + downsample->pub.need_context_rows = FALSE; + + if (cinfo->CCIR601_sampling) + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + /* Verify we can handle the sampling factors, and set up method pointers */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor == cinfo->max_h_samp_factor && + compptr->v_samp_factor == cinfo->max_v_samp_factor) { +#ifdef INPUT_SMOOTHING_SUPPORTED + if (cinfo->smoothing_factor) { + downsample->methods[ci] = fullsize_smooth_downsample; + downsample->pub.need_context_rows = TRUE; + } else +#endif + downsample->methods[ci] = fullsize_downsample; + } else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor && + compptr->v_samp_factor == cinfo->max_v_samp_factor) { + smoothok = FALSE; + downsample->methods[ci] = h2v1_downsample; + } else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor && + compptr->v_samp_factor * 2 == cinfo->max_v_samp_factor) { +#ifdef INPUT_SMOOTHING_SUPPORTED + if (cinfo->smoothing_factor) { + downsample->methods[ci] = h2v2_smooth_downsample; + downsample->pub.need_context_rows = TRUE; + } else +#endif + downsample->methods[ci] = h2v2_downsample; + } else if ((cinfo->max_h_samp_factor % compptr->h_samp_factor) == 0 && + (cinfo->max_v_samp_factor % compptr->v_samp_factor) == 0) { + smoothok = FALSE; + downsample->methods[ci] = int_downsample; + } else + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + } + +#ifdef INPUT_SMOOTHING_SUPPORTED + if (cinfo->smoothing_factor && !smoothok) + TRACEMS(cinfo, 0, JTRC_SMOOTH_NOTIMPL); +#endif +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jctrans.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jctrans.c new file mode 100644 index 000000000..6c2314d0b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jctrans.c @@ -0,0 +1,389 @@ +/* + * jctrans.c + * + * Copyright (C) 1995-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains library routines for transcoding compression, + * that is, writing raw DCT coefficient arrays to an output JPEG file. + * The routines in jcapimin.c will also be needed by a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#include "cpl_port.h" + +/* Forward declarations */ +LOCAL(void) transencode_master_selection + JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); +LOCAL(void) transencode_coef_controller + JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); + + +/* + * Compression initialization for writing raw-coefficient data. + * Before calling this, all parameters and a data destination must be set up. + * Call jpeg_finish_compress() to actually write the data. + * + * The number of passed virtual arrays must match cinfo->num_components. + * Note that the virtual arrays need not be filled or even realized at + * the time write_coefficients is called; indeed, if the virtual arrays + * were requested from this compression object's memory manager, they + * typically will be realized during this routine and filled afterwards. + */ + +GLOBAL(void) +jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Mark all tables to be written */ + jpeg_suppress_tables(cinfo, FALSE); + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Perform master selection of active modules */ + transencode_master_selection(cinfo, coef_arrays); + /* Wait for jpeg_finish_compress() call */ + cinfo->next_scanline = 0; /* so jpeg_write_marker works */ + cinfo->global_state = CSTATE_WRCOEFS; +} + + +/* + * Initialize the compression object with default parameters, + * then copy from the source object all parameters needed for lossless + * transcoding. Parameters that can be varied without loss (such as + * scan script and Huffman optimization) are left in their default states. + */ + +GLOBAL(void) +jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo) +{ + JQUANT_TBL ** qtblptr; + jpeg_component_info *incomp, *outcomp; + JQUANT_TBL *c_quant, *slot_quant; + int tblno, ci, coefi; + + /* Safety check to ensure start_compress not called yet. */ + if (dstinfo->global_state != CSTATE_START) + ERREXIT1(dstinfo, JERR_BAD_STATE, dstinfo->global_state); + /* Copy fundamental image dimensions */ + dstinfo->image_width = srcinfo->image_width; + dstinfo->image_height = srcinfo->image_height; + dstinfo->input_components = srcinfo->num_components; + dstinfo->in_color_space = srcinfo->jpeg_color_space; + /* Initialize all parameters to default values */ + jpeg_set_defaults(dstinfo); + /* jpeg_set_defaults may choose wrong colorspace, eg YCbCr if input is RGB. + * Fix it to get the right header markers for the image colorspace. + */ + jpeg_set_colorspace(dstinfo, srcinfo->jpeg_color_space); + dstinfo->data_precision = srcinfo->data_precision; + dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling; + /* Copy the source's quantization tables. */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + if (srcinfo->quant_tbl_ptrs[tblno] != NULL) { + qtblptr = & dstinfo->quant_tbl_ptrs[tblno]; + if (*qtblptr == NULL) + *qtblptr = jpeg_alloc_quant_table((j_common_ptr) dstinfo); + MEMCOPY((*qtblptr)->quantval, + srcinfo->quant_tbl_ptrs[tblno]->quantval, + SIZEOF((*qtblptr)->quantval)); + (*qtblptr)->sent_table = FALSE; + } + } + /* Copy the source's per-component info. + * Note we assume jpeg_set_defaults has allocated the dest comp_info array. + */ + dstinfo->num_components = srcinfo->num_components; + if (dstinfo->num_components < 1 || dstinfo->num_components > MAX_COMPONENTS) + ERREXIT2(dstinfo, JERR_COMPONENT_COUNT, dstinfo->num_components, + MAX_COMPONENTS); + for (ci = 0, incomp = srcinfo->comp_info, outcomp = dstinfo->comp_info; + ci < dstinfo->num_components; ci++, incomp++, outcomp++) { + outcomp->component_id = incomp->component_id; + outcomp->h_samp_factor = incomp->h_samp_factor; + outcomp->v_samp_factor = incomp->v_samp_factor; + outcomp->quant_tbl_no = incomp->quant_tbl_no; + /* Make sure saved quantization table for component matches the qtable + * slot. If not, the input file re-used this qtable slot. + * IJG encoder currently cannot duplicate this. + */ + tblno = outcomp->quant_tbl_no; + if (tblno < 0 || tblno >= NUM_QUANT_TBLS || + srcinfo->quant_tbl_ptrs[tblno] == NULL) + ERREXIT1(dstinfo, JERR_NO_QUANT_TABLE, tblno); + slot_quant = srcinfo->quant_tbl_ptrs[tblno]; + c_quant = incomp->quant_table; + if (c_quant != NULL) { + for (coefi = 0; coefi < DCTSIZE2; coefi++) { + if (c_quant->quantval[coefi] != slot_quant->quantval[coefi]) + ERREXIT1(dstinfo, JERR_MISMATCHED_QUANT_TABLE, tblno); + } + } + /* Note: we do not copy the source's Huffman table assignments; + * instead we rely on jpeg_set_colorspace to have made a suitable choice. + */ + } + /* Also copy JFIF version and resolution information, if available. + * Strictly speaking this isn't "critical" info, but it's nearly + * always appropriate to copy it if available. In particular, + * if the application chooses to copy JFIF 1.02 extension markers from + * the source file, we need to copy the version to make sure we don't + * emit a file that has 1.02 extensions but a claimed version of 1.01. + * We will *not*, however, copy version info from mislabeled "2.01" files. + */ + if (srcinfo->saw_JFIF_marker) { + if (srcinfo->JFIF_major_version == 1) { + dstinfo->JFIF_major_version = srcinfo->JFIF_major_version; + dstinfo->JFIF_minor_version = srcinfo->JFIF_minor_version; + } + dstinfo->density_unit = srcinfo->density_unit; + dstinfo->X_density = srcinfo->X_density; + dstinfo->Y_density = srcinfo->Y_density; + } +} + + +/* + * Master selection of compression modules for transcoding. + * This substitutes for jcinit.c's initialization of the full compressor. + */ + +LOCAL(void) +transencode_master_selection (j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays) +{ + /* Although we don't actually use input_components for transcoding, + * jcmaster.c's initial_setup will complain if input_components is 0. + */ + cinfo->input_components = 1; + /* Initialize master control (includes parameter checking/processing) */ + jinit_c_master_control(cinfo, TRUE /* transcode only */); + + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + jinit_phuff_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_encoder(cinfo); + } + + /* We need a special coefficient buffer controller. */ + transencode_coef_controller(cinfo, coef_arrays); + + jinit_marker_writer(cinfo); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Write the datastream header (SOI, JFIF) immediately. + * Frame and scan headers are postponed till later. + * This lets application insert special markers after the SOI. + */ + (*cinfo->marker->write_file_header) (cinfo); +} + + +/* + * The rest of this file is a special implementation of the coefficient + * buffer controller. This is similar to jccoefct.c, but it handles only + * output from presupplied virtual arrays. Furthermore, we generate any + * dummy padding blocks on-the-fly rather than expecting them to be present + * in the arrays. + */ + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* Virtual block array for each component. */ + jvirt_barray_ptr * whole_image; + + /* Workspace for constructing dummy blocks at right/bottom edges. */ + JBLOCKROW dummy_buffer[C_MAX_BLOCKS_IN_MCU]; +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + + +LOCAL(void) +start_iMCU_row (j_compress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->mcu_ctr = 0; + coef->MCU_vert_offset = 0; +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + if (pass_mode != JBUF_CRANK_DEST) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + coef->iMCU_row_num = 0; + start_iMCU_row(cinfo); +} + + +/* + * Process some data. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the scan. + * The data is obtained from the virtual arrays and fed to the entropy coder. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf is ignored; it is likely to be a NULL pointer. + */ + +METHODDEF(boolean) +compress_output (j_compress_ptr cinfo, CPL_UNUSED JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, ci, xindex, yindex, yoffset, blockcnt; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (coef->iMCU_row_num < last_iMCU_row || + yindex+yoffset < compptr->last_row_height) { + /* Fill in pointers to real blocks in this row */ + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < blockcnt; xindex++) + MCU_buffer[blkn++] = buffer_ptr++; + } else { + /* At bottom of image, need a whole row of dummy blocks */ + xindex = 0; + } + /* Fill in any dummy blocks needed in this row. + * Dummy blocks are filled in the same way as in jccoefct.c: + * all zeroes in the AC entries, DC entries equal to previous + * block's DC value. The init routine has already zeroed the + * AC entries, so we need only set the DC entries correctly. + */ + for (; xindex < compptr->MCU_width; xindex++) { + MCU_buffer[blkn] = coef->dummy_buffer[blkn]; + MCU_buffer[blkn][0][0] = MCU_buffer[blkn-1][0][0]; + blkn++; + } + } + } + /* Try to write the MCU. */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + + +/* + * Initialize coefficient buffer controller. + * + * Each passed coefficient array must be the right size for that + * coefficient: width_in_blocks wide and height_in_blocks high, + * with unitheight at least v_samp_factor. + */ + +LOCAL(void) +transencode_coef_controller (j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays) +{ + my_coef_ptr coef; + JBLOCKROW buffer; + int i; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef; + coef->pub.compress_data = compress_output; + + /* Save pointer to virtual arrays */ + coef->whole_image = coef_arrays; + + /* Allocate and pre-zero space for dummy DCT blocks. */ + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + jzero_far((void FAR *) buffer, C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { + coef->dummy_buffer[i] = buffer + i; + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdapimin.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdapimin.c new file mode 100644 index 000000000..cadb59fce --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdapimin.c @@ -0,0 +1,395 @@ +/* + * jdapimin.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the decompression half + * of the JPEG library. These are the "minimum" API routines that may be + * needed in either the normal full-decompression case or the + * transcoding-only case. + * + * Most of the routines intended to be called directly by an application + * are in this file or in jdapistd.c. But also see jcomapi.c for routines + * shared by compression and decompression, and jdtrans.c for the transcoding + * case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Initialization of a JPEG decompression object. + * The error manager must already be set up (in case memory manager fails). + */ + +GLOBAL(void) +jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, size_t structsize) +{ + int i; + + /* Guard against version mismatches between library and caller. */ + cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */ + if (version != JPEG_LIB_VERSION) + ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); + if (structsize != SIZEOF(struct jpeg_decompress_struct)) + ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, + (int) SIZEOF(struct jpeg_decompress_struct), (int) structsize); + + /* For debugging purposes, we zero the whole master structure. + * But the application has already set the err pointer, and may have set + * client_data, so we have to save and restore those fields. + * Note: if application hasn't set client_data, tools like Purify may + * complain here. + */ + { + struct jpeg_error_mgr * err = cinfo->err; + void * client_data = cinfo->client_data; /* ignore Purify complaint here */ + MEMZERO(cinfo, SIZEOF(struct jpeg_decompress_struct)); + cinfo->err = err; + cinfo->client_data = client_data; + } + cinfo->is_decompressor = TRUE; + + /* Initialize a memory manager instance for this object */ + jinit_memory_mgr((j_common_ptr) cinfo); + + /* Zero out pointers to permanent structures. */ + cinfo->progress = NULL; + cinfo->src = NULL; + + for (i = 0; i < NUM_QUANT_TBLS; i++) + cinfo->quant_tbl_ptrs[i] = NULL; + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + cinfo->dc_huff_tbl_ptrs[i] = NULL; + cinfo->ac_huff_tbl_ptrs[i] = NULL; + } + + /* Initialize marker processor so application can override methods + * for COM, APPn markers before calling jpeg_read_header. + */ + cinfo->marker_list = NULL; + jinit_marker_reader(cinfo); + + /* And initialize the overall input controller. */ + jinit_input_controller(cinfo); + + /* OK, I'm ready */ + cinfo->global_state = DSTATE_START; +} + + +/* + * Destruction of a JPEG decompression object + */ + +GLOBAL(void) +jpeg_destroy_decompress (j_decompress_ptr cinfo) +{ + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Abort processing of a JPEG decompression operation, + * but don't destroy the object itself. + */ + +GLOBAL(void) +jpeg_abort_decompress (j_decompress_ptr cinfo) +{ + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Set default decompression parameters. + */ + +LOCAL(void) +default_decompress_parms (j_decompress_ptr cinfo) +{ + /* Guess the input colorspace, and set output colorspace accordingly. */ + /* (Wish JPEG committee had provided a real way to specify this...) */ + /* Note application may override our guesses. */ + switch (cinfo->num_components) { + case 1: + cinfo->jpeg_color_space = JCS_GRAYSCALE; + cinfo->out_color_space = JCS_GRAYSCALE; + break; + + case 3: + if (cinfo->saw_JFIF_marker) { + cinfo->jpeg_color_space = JCS_YCbCr; /* JFIF implies YCbCr */ + } else if (cinfo->saw_Adobe_marker) { + switch (cinfo->Adobe_transform) { + case 0: + cinfo->jpeg_color_space = JCS_RGB; + break; + case 1: + cinfo->jpeg_color_space = JCS_YCbCr; + break; + default: + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + break; + } + } else { + /* Saw no special markers, try to guess from the component IDs */ + int cid0 = cinfo->comp_info[0].component_id; + int cid1 = cinfo->comp_info[1].component_id; + int cid2 = cinfo->comp_info[2].component_id; + + if (cid0 == 1 && cid1 == 2 && cid2 == 3) + cinfo->jpeg_color_space = JCS_YCbCr; /* assume JFIF w/out marker */ + else if (cid0 == 82 && cid1 == 71 && cid2 == 66) + cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ + else { + TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + } + } + /* Always guess RGB is proper output colorspace. */ + cinfo->out_color_space = JCS_RGB; + break; + + case 4: + if (cinfo->saw_Adobe_marker) { + switch (cinfo->Adobe_transform) { + case 0: + cinfo->jpeg_color_space = JCS_CMYK; + break; + case 2: + cinfo->jpeg_color_space = JCS_YCCK; + break; + default: + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + cinfo->jpeg_color_space = JCS_YCCK; /* assume it's YCCK */ + break; + } + } else { + /* No special markers, assume straight CMYK. */ + cinfo->jpeg_color_space = JCS_CMYK; + } + cinfo->out_color_space = JCS_CMYK; + break; + + default: + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->out_color_space = JCS_UNKNOWN; + break; + } + + /* Set defaults for other decompression parameters. */ + cinfo->scale_num = 1; /* 1:1 scaling */ + cinfo->scale_denom = 1; + cinfo->output_gamma = 1.0; + cinfo->buffered_image = FALSE; + cinfo->raw_data_out = FALSE; + cinfo->dct_method = JDCT_DEFAULT; + cinfo->do_fancy_upsampling = TRUE; + cinfo->do_block_smoothing = TRUE; + cinfo->quantize_colors = FALSE; + /* We set these in case application only sets quantize_colors. */ + cinfo->dither_mode = JDITHER_FS; +#ifdef QUANT_2PASS_SUPPORTED + cinfo->two_pass_quantize = TRUE; +#else + cinfo->two_pass_quantize = FALSE; +#endif + cinfo->desired_number_of_colors = 256; + cinfo->colormap = NULL; + /* Initialize for no mode change in buffered-image mode. */ + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; +} + + +/* + * Decompression startup: read start of JPEG datastream to see what's there. + * Need only initialize JPEG object and supply a data source before calling. + * + * This routine will read as far as the first SOS marker (ie, actual start of + * compressed data), and will save all tables and parameters in the JPEG + * object. It will also initialize the decompression parameters to default + * values, and finally return JPEG_HEADER_OK. On return, the application may + * adjust the decompression parameters and then call jpeg_start_decompress. + * (Or, if the application only wanted to determine the image parameters, + * the data need not be decompressed. In that case, call jpeg_abort or + * jpeg_destroy to release any temporary space.) + * If an abbreviated (tables only) datastream is presented, the routine will + * return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then + * re-use the JPEG object to read the abbreviated image datastream(s). + * It is unnecessary (but OK) to call jpeg_abort in this case. + * The JPEG_SUSPENDED return code only occurs if the data source module + * requests suspension of the decompressor. In this case the application + * should load more source data and then re-call jpeg_read_header to resume + * processing. + * If a non-suspending data source is used and require_image is TRUE, then the + * return code need not be inspected since only JPEG_HEADER_OK is possible. + * + * This routine is now just a front end to jpeg_consume_input, with some + * extra error checking. + */ + +GLOBAL(int) +jpeg_read_header (j_decompress_ptr cinfo, boolean require_image) +{ + int retcode; + + if (cinfo->global_state != DSTATE_START && + cinfo->global_state != DSTATE_INHEADER) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + retcode = jpeg_consume_input(cinfo); + + switch (retcode) { + case JPEG_REACHED_SOS: + retcode = JPEG_HEADER_OK; + break; + case JPEG_REACHED_EOI: + if (require_image) /* Complain if application wanted an image */ + ERREXIT(cinfo, JERR_NO_IMAGE); + /* Reset to start state; it would be safer to require the application to + * call jpeg_abort, but we can't change it now for compatibility reasons. + * A side effect is to free any temporary memory (there shouldn't be any). + */ + jpeg_abort((j_common_ptr) cinfo); /* sets state = DSTATE_START */ + retcode = JPEG_HEADER_TABLES_ONLY; + break; + case JPEG_SUSPENDED: + /* no work */ + break; + } + + return retcode; +} + + +/* + * Consume data in advance of what the decompressor requires. + * This can be called at any time once the decompressor object has + * been created and a data source has been set up. + * + * This routine is essentially a state machine that handles a couple + * of critical state-transition actions, namely initial setup and + * transition from header scanning to ready-for-start_decompress. + * All the actual input is done via the input controller's consume_input + * method. + */ + +GLOBAL(int) +jpeg_consume_input (j_decompress_ptr cinfo) +{ + int retcode = JPEG_SUSPENDED; + + /* NB: every possible DSTATE value should be listed in this switch */ + switch (cinfo->global_state) { + case DSTATE_START: + /* Start-of-datastream actions: reset appropriate modules */ + (*cinfo->inputctl->reset_input_controller) (cinfo); + /* Initialize application's data source module */ + (*cinfo->src->init_source) (cinfo); + cinfo->global_state = DSTATE_INHEADER; + /*FALLTHROUGH*/ + case DSTATE_INHEADER: + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_REACHED_SOS) { /* Found SOS, prepare to decompress */ + /* Set up default parameters based on header data */ + default_decompress_parms(cinfo); + /* Set global state: ready for start_decompress */ + cinfo->global_state = DSTATE_READY; + } + break; + case DSTATE_READY: + /* Can't advance past first SOS until start_decompress is called */ + retcode = JPEG_REACHED_SOS; + break; + case DSTATE_PRELOAD: + case DSTATE_PRESCAN: + case DSTATE_SCANNING: + case DSTATE_RAW_OK: + case DSTATE_BUFIMAGE: + case DSTATE_BUFPOST: + case DSTATE_STOPPING: + retcode = (*cinfo->inputctl->consume_input) (cinfo); + break; + default: + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + return retcode; +} + + +/* + * Have we finished reading the input file? + */ + +GLOBAL(boolean) +jpeg_input_complete (j_decompress_ptr cinfo) +{ + /* Check for valid jpeg object */ + if (cinfo->global_state < DSTATE_START || + cinfo->global_state > DSTATE_STOPPING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return cinfo->inputctl->eoi_reached; +} + + +/* + * Is there more than one scan? + */ + +GLOBAL(boolean) +jpeg_has_multiple_scans (j_decompress_ptr cinfo) +{ + /* Only valid after jpeg_read_header completes */ + if (cinfo->global_state < DSTATE_READY || + cinfo->global_state > DSTATE_STOPPING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return cinfo->inputctl->has_multiple_scans; +} + + +/* + * Finish JPEG decompression. + * + * This will normally just verify the file trailer and release temp storage. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL(boolean) +jpeg_finish_decompress (j_decompress_ptr cinfo) +{ + if ((cinfo->global_state == DSTATE_SCANNING || + cinfo->global_state == DSTATE_RAW_OK) && ! cinfo->buffered_image) { + /* Terminate final pass of non-buffered mode */ + if (cinfo->output_scanline < cinfo->output_height) + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + (*cinfo->master->finish_output_pass) (cinfo); + cinfo->global_state = DSTATE_STOPPING; + } else if (cinfo->global_state == DSTATE_BUFIMAGE) { + /* Finishing after a buffered-image operation */ + cinfo->global_state = DSTATE_STOPPING; + } else if (cinfo->global_state != DSTATE_STOPPING) { + /* STOPPING = repeat call after a suspension, anything else is error */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + /* Read until EOI */ + while (! cinfo->inputctl->eoi_reached) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return FALSE; /* Suspend, come back later */ + } + /* Do final cleanup */ + (*cinfo->src->term_source) (cinfo); + /* We can use jpeg_abort to release memory and reset global_state */ + jpeg_abort((j_common_ptr) cinfo); + return TRUE; +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdapistd.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdapistd.c new file mode 100644 index 000000000..c8e3fa0c3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdapistd.c @@ -0,0 +1,275 @@ +/* + * jdapistd.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the decompression half + * of the JPEG library. These are the "standard" API routines that are + * used in the normal full-decompression case. They are not used by a + * transcoding-only application. Note that if an application links in + * jpeg_start_decompress, it will end up linking in the entire decompressor. + * We thus must separate this file from jdapimin.c to avoid linking the + * whole decompression library into a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL(boolean) output_pass_setup JPP((j_decompress_ptr cinfo)); + + +/* + * Decompression initialization. + * jpeg_read_header must be completed before calling this. + * + * If a multipass operating mode was selected, this will do all but the + * last pass, and thus may take a great deal of time. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL(boolean) +jpeg_start_decompress (j_decompress_ptr cinfo) +{ + if (cinfo->global_state == DSTATE_READY) { + /* First call: initialize master control, select active modules */ + jinit_master_decompress(cinfo); + if (cinfo->buffered_image) { + /* No more work here; expecting jpeg_start_output next */ + cinfo->global_state = DSTATE_BUFIMAGE; + return TRUE; + } + cinfo->global_state = DSTATE_PRELOAD; + } + if (cinfo->global_state == DSTATE_PRELOAD) { + /* If file has multiple scans, absorb them all into the coef buffer */ + if (cinfo->inputctl->has_multiple_scans) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + for (;;) { + int retcode; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + /* Absorb some more input */ + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_SUSPENDED) + return FALSE; + if (retcode == JPEG_REACHED_EOI) + break; + /* Advance progress counter if appropriate */ + if (cinfo->progress != NULL && + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + /* jdmaster underestimated number of scans; ratchet up one scan */ + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + } + } + } +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + } + cinfo->output_scan_number = cinfo->input_scan_number; + } else if (cinfo->global_state != DSTATE_PRESCAN) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Perform any dummy output passes, and set up for the final pass */ + return output_pass_setup(cinfo); +} + + +/* + * Set up for an output pass, and perform any dummy pass(es) needed. + * Common subroutine for jpeg_start_decompress and jpeg_start_output. + * Entry: global_state = DSTATE_PRESCAN only if previously suspended. + * Exit: If done, returns TRUE and sets global_state for proper output mode. + * If suspended, returns FALSE and sets global_state = DSTATE_PRESCAN. + */ + +LOCAL(boolean) +output_pass_setup (j_decompress_ptr cinfo) +{ + if (cinfo->global_state != DSTATE_PRESCAN) { + /* First call: do pass setup */ + (*cinfo->master->prepare_for_output_pass) (cinfo); + cinfo->output_scanline = 0; + cinfo->global_state = DSTATE_PRESCAN; + } + /* Loop over any required dummy passes */ + while (cinfo->master->is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Crank through the dummy pass */ + while (cinfo->output_scanline < cinfo->output_height) { + JDIMENSION last_scanline; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + /* Process some data */ + last_scanline = cinfo->output_scanline; + (*cinfo->main->process_data) (cinfo, (JSAMPARRAY) NULL, + &cinfo->output_scanline, (JDIMENSION) 0); + if (cinfo->output_scanline == last_scanline) + return FALSE; /* No progress made, must suspend */ + } + /* Finish up dummy pass, and set up for another one */ + (*cinfo->master->finish_output_pass) (cinfo); + (*cinfo->master->prepare_for_output_pass) (cinfo); + cinfo->output_scanline = 0; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } + /* Ready for application to drive output pass through + * jpeg_read_scanlines or jpeg_read_raw_data. + */ + cinfo->global_state = cinfo->raw_data_out ? DSTATE_RAW_OK : DSTATE_SCANNING; + return TRUE; +} + + +/* + * Read some scanlines of data from the JPEG decompressor. + * + * The return value will be the number of lines actually read. + * This may be less than the number requested in several cases, + * including bottom of image, data source suspension, and operating + * modes that emit multiple scanlines at a time. + * + * Note: we warn about excess calls to jpeg_read_scanlines() since + * this likely signals an application programmer error. However, + * an oversize buffer (max_lines > scanlines remaining) is not an error. + */ + +GLOBAL(JDIMENSION) +jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines, + JDIMENSION max_lines) +{ + JDIMENSION row_ctr; + + if (cinfo->global_state != DSTATE_SCANNING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->output_scanline >= cinfo->output_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Process some data */ + row_ctr = 0; + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines); + cinfo->output_scanline += row_ctr; + return row_ctr; +} + + +/* + * Alternate entry point to read raw data. + * Processes exactly one iMCU row per call, unless suspended. + */ + +GLOBAL(JDIMENSION) +jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION max_lines) +{ + JDIMENSION lines_per_iMCU_row; + + if (cinfo->global_state != DSTATE_RAW_OK) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->output_scanline >= cinfo->output_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Verify that at least one iMCU row can be returned. */ + lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size; + if (max_lines < lines_per_iMCU_row) + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + /* Decompress directly into user's buffer. */ + if (! (*cinfo->coef->decompress_data) (cinfo, data)) + return 0; /* suspension forced, can do nothing more */ + + /* OK, we processed one iMCU row. */ + cinfo->output_scanline += lines_per_iMCU_row; + return lines_per_iMCU_row; +} + + +/* Additional entry points for buffered-image mode. */ + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Initialize for an output pass in buffered-image mode. + */ + +GLOBAL(boolean) +jpeg_start_output (j_decompress_ptr cinfo, int scan_number) +{ + if (cinfo->global_state != DSTATE_BUFIMAGE && + cinfo->global_state != DSTATE_PRESCAN) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Limit scan number to valid range */ + if (scan_number <= 0) + scan_number = 1; + if (cinfo->inputctl->eoi_reached && + scan_number > cinfo->input_scan_number) + scan_number = cinfo->input_scan_number; + cinfo->output_scan_number = scan_number; + /* Perform any dummy output passes, and set up for the real pass */ + return output_pass_setup(cinfo); +} + + +/* + * Finish up after an output pass in buffered-image mode. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL(boolean) +jpeg_finish_output (j_decompress_ptr cinfo) +{ + if ((cinfo->global_state == DSTATE_SCANNING || + cinfo->global_state == DSTATE_RAW_OK) && cinfo->buffered_image) { + /* Terminate this pass. */ + /* We do not require the whole pass to have been completed. */ + (*cinfo->master->finish_output_pass) (cinfo); + cinfo->global_state = DSTATE_BUFPOST; + } else if (cinfo->global_state != DSTATE_BUFPOST) { + /* BUFPOST = repeat call after a suspension, anything else is error */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + /* Read markers looking for SOS or EOI */ + while (cinfo->input_scan_number <= cinfo->output_scan_number && + ! cinfo->inputctl->eoi_reached) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return FALSE; /* Suspend, come back later */ + } + cinfo->global_state = DSTATE_BUFIMAGE; + return TRUE; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdatadst.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdatadst.c new file mode 100644 index 000000000..a8f6fb0e0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdatadst.c @@ -0,0 +1,151 @@ +/* + * jdatadst.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains compression data destination routines for the case of + * emitting JPEG data to a file (or any stdio stream). While these routines + * are sufficient for most applications, some will want to use a different + * destination manager. + * IMPORTANT: we assume that fwrite() will correctly transcribe an array of + * JOCTETs into 8-bit-wide elements on external storage. If char is wider + * than 8 bits on your machine, you may need to do some tweaking. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jerror.h" + + +/* Expanded data destination object for stdio output */ + +typedef struct { + struct jpeg_destination_mgr pub; /* public fields */ + + FILE * outfile; /* target stream */ + JOCTET * buffer; /* start of buffer */ +} my_destination_mgr; + +typedef my_destination_mgr * my_dest_ptr; + +#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ + + +/* + * Initialize destination --- called by jpeg_start_compress + * before any data is actually written. + */ + +METHODDEF(void) +init_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + /* Allocate the output buffer --- it will be released when done with image */ + dest->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + OUTPUT_BUF_SIZE * SIZEOF(JOCTET)); + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; +} + + +/* + * Empty the output buffer --- called whenever buffer fills up. + * + * In typical applications, this should write the entire output buffer + * (ignoring the current state of next_output_byte & free_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been dumped. + * + * In applications that need to be able to suspend compression due to output + * overrun, a FALSE return indicates that the buffer cannot be emptied now. + * In this situation, the compressor will return to its caller (possibly with + * an indication that it has not accepted all the supplied scanlines). The + * application should resume compression after it has made more room in the + * output buffer. Note that there are substantial restrictions on the use of + * suspension --- see the documentation. + * + * When suspending, the compressor will back up to a convenient restart point + * (typically the start of the current MCU). next_output_byte & free_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point will be regenerated after resumption, so do not + * write it out when emptying the buffer externally. + */ + +METHODDEF(boolean) +empty_output_buffer (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + if (JFWRITE(dest->outfile, dest->buffer, OUTPUT_BUF_SIZE) != + (size_t) OUTPUT_BUF_SIZE) + ERREXIT(cinfo, JERR_FILE_WRITE); + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + + return TRUE; +} + + +/* + * Terminate destination --- called by jpeg_finish_compress + * after all data has been written. Usually needs to flush buffer. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF(void) +term_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; + + /* Write any data remaining in the buffer */ + if (datacount > 0) { + if (JFWRITE(dest->outfile, dest->buffer, datacount) != datacount) + ERREXIT(cinfo, JERR_FILE_WRITE); + } + fflush(dest->outfile); + /* Make sure we wrote the output file OK */ + if (ferror(dest->outfile)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * Prepare for output to a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing compression. + */ + +GLOBAL(void) +jpeg_stdio_dest (j_compress_ptr cinfo, FILE * outfile) +{ + my_dest_ptr dest; + + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_stdio_dest. + * This makes it dangerous to use this manager and a different destination + * manager serially with the same JPEG object, because their private object + * sizes may be different. Caveat programmer. + */ + if (cinfo->dest == NULL) { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_destination_mgr)); + } + + dest = (my_dest_ptr) cinfo->dest; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; + dest->outfile = outfile; +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdatasrc.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdatasrc.c new file mode 100644 index 000000000..9de896a72 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdatasrc.c @@ -0,0 +1,213 @@ +/* + * jdatasrc.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains decompression data source routines for the case of + * reading JPEG data from a file (or any stdio stream). While these routines + * are sufficient for most applications, some will want to use a different + * source manager. + * IMPORTANT: we assume that fread() will correctly transcribe an array of + * JOCTETs from 8-bit-wide elements on external storage. If char is wider + * than 8 bits on your machine, you may need to do some tweaking. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jerror.h" + +#include "cpl_port.h" + +/* Expanded data source object for stdio input */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + + FILE * infile; /* source stream */ + JOCTET * buffer; /* start of buffer */ + boolean start_of_file; /* have we gotten any data yet? */ +} my_source_mgr; + +typedef my_source_mgr * my_src_ptr; + +#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ + + +/* + * Initialize source --- called by jpeg_read_header + * before any data is actually read. + */ + +METHODDEF(void) +init_source (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_file = TRUE; +} + + +/* + * Fill the input buffer --- called whenever buffer is emptied. + * + * In typical applications, this should read fresh data into the buffer + * (ignoring the current state of next_input_byte & bytes_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been reloaded. It is not necessary to + * fill the buffer entirely, only to obtain at least one more byte. + * + * There is no such thing as an EOF return. If the end of the file has been + * reached, the routine has a choice of ERREXIT() or inserting fake data into + * the buffer. In most cases, generating a warning message and inserting a + * fake EOI marker is the best course of action --- this will allow the + * decompressor to output however much of the image is there. However, + * the resulting error message is misleading if the real problem is an empty + * input file, so we handle that case specially. + * + * In applications that need to be able to suspend compression due to input + * not being available yet, a FALSE return indicates that no more data can be + * obtained right now, but more may be forthcoming later. In this situation, + * the decompressor will return to its caller (with an indication of the + * number of scanlines it has read, if any). The application should resume + * decompression after it has loaded more data into the input buffer. Note + * that there are substantial restrictions on the use of suspension --- see + * the documentation. + * + * When suspending, the decompressor will back up to a convenient restart point + * (typically the start of the current MCU). next_input_byte & bytes_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point must be rescanned after resumption, so move it to + * the front of the buffer rather than discarding it. + */ + +METHODDEF(boolean) +fill_input_buffer (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + size_t nbytes; + + nbytes = JFREAD(src->infile, src->buffer, INPUT_BUF_SIZE); + + if (nbytes <= 0) { + if (src->start_of_file) /* Treat empty input file as fatal error */ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + WARNMS(cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = FALSE; + + return TRUE; +} + + +/* + * Skip data --- used to skip over a potentially large amount of + * uninteresting data (such as an APPn marker). + * + * Writers of suspendable-input applications must note that skip_input_data + * is not granted the right to give a suspension return. If the skip extends + * beyond the data currently in the buffer, the buffer can be marked empty so + * that the next read will cause a fill_input_buffer call that can suspend. + * Arranging for additional bytes to be discarded before reloading the input + * buffer is the application writer's problem. + */ + +METHODDEF(void) +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + + +/* + * An additional method that can be provided by data source modules is the + * resync_to_restart method for error recovery in the presence of RST markers. + * For the moment, this source module just uses the default resync method + * provided by the JPEG library. That method assumes that no backtracking + * is possible. + */ + + +/* + * Terminate source --- called by jpeg_finish_decompress + * after all data has been read. Often a no-op. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF(void) +term_source (CPL_UNUSED j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* + * Prepare for input from a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing decompression. + */ + +GLOBAL(void) +jpeg_stdio_src (j_decompress_ptr cinfo, FILE * infile) +{ + my_src_ptr src; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_stdio_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_source_mgr)); + src = (my_src_ptr) cinfo->src; + src->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + INPUT_BUF_SIZE * SIZEOF(JOCTET)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->infile = infile; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdcoefct.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdcoefct.c new file mode 100644 index 000000000..01fe5a597 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdcoefct.c @@ -0,0 +1,738 @@ +/* + * jdcoefct.c + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the coefficient buffer controller for decompression. + * This controller is the top level of the JPEG decompressor proper. + * The coefficient buffer lies between entropy decoding and inverse-DCT steps. + * + * In buffered-image mode, this controller is the interface between + * input-oriented processing and output-oriented processing. + * Also, the input side (only) is used when reading a file for transcoding. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#include "cpl_port.h" + +/* Block smoothing is only applicable for progressive JPEG, so: */ +#ifndef D_PROGRESSIVE_SUPPORTED +#undef BLOCK_SMOOTHING_SUPPORTED +#endif + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + + /* These variables keep track of the current location of the input side. */ + /* cinfo->input_iMCU_row is also used for this. */ + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + /* In single-pass modes, it's sufficient to buffer just one MCU. + * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, + * and let the entropy decoder write into that workspace each time. + * (On 80x86, the workspace is FAR even though it's not really very big; + * this is to keep the module interfaces unchanged when a large coefficient + * buffer is necessary.) + * In multi-pass modes, this array points to the current MCU's blocks + * within the virtual arrays; it is used only by the input side. + */ + JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU]; + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* In multi-pass modes, we need a virtual block array for each component. */ + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; +#endif + +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* When doing block smoothing, we latch coefficient Al values here */ + int * coef_bits_latch; +#define SAVED_COEFS 6 /* we save coef_bits[0..5] */ +#endif +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + +/* Forward declarations */ +METHODDEF(int) decompress_onepass + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#ifdef D_MULTISCAN_FILES_SUPPORTED +METHODDEF(int) decompress_data + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#endif +#ifdef BLOCK_SMOOTHING_SUPPORTED +LOCAL(boolean) smoothing_ok JPP((j_decompress_ptr cinfo)); +METHODDEF(int) decompress_smooth_data + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#endif + + +LOCAL(void) +start_iMCU_row (j_decompress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row (input side) */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->MCU_ctr = 0; + coef->MCU_vert_offset = 0; +} + + +/* + * Initialize for an input processing pass. + */ + +METHODDEF(void) +start_input_pass (j_decompress_ptr cinfo) +{ + cinfo->input_iMCU_row = 0; + start_iMCU_row(cinfo); +} + + +/* + * Initialize for an output processing pass. + */ + +METHODDEF(void) +start_output_pass (j_decompress_ptr cinfo) +{ +#ifdef BLOCK_SMOOTHING_SUPPORTED + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* If multipass, check to see whether to use block smoothing on this pass */ + if (coef->pub.coef_arrays != NULL) { + if (cinfo->do_block_smoothing && smoothing_ok(cinfo)) + coef->pub.decompress_data = decompress_smooth_data; + else + coef->pub.decompress_data = decompress_data; + } +#endif + cinfo->output_iMCU_row = 0; +} + + +/* + * Decompress and return some data in the single-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Input and output must run in lockstep since we have only a one-MCU buffer. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image, + * which we index according to the component's SOF position. + */ + +METHODDEF(int) +decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, ci, xindex, yindex, yoffset, useful_width; + JSAMPARRAY output_ptr; + JDIMENSION start_col, output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + + /* Loop to process as much as one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->MCU_ctr; MCU_col_num <= last_MCU_col; + MCU_col_num++) { + /* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ + jzero_far((void FAR *) coef->MCU_buffer[0], + (size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK))); + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->MCU_ctr = MCU_col_num; + return JPEG_SUSPENDED; + } + /* Determine where data should go in output_buf and do the IDCT thing. + * We skip dummy blocks at the right and bottom edges (but blkn gets + * incremented past them!). Note the inner loop relies on having + * allocated the MCU_buffer[] blocks sequentially. + */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) { + blkn += compptr->MCU_blocks; + continue; + } + inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; + useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + output_ptr = output_buf[compptr->component_index] + + yoffset * compptr->DCT_scaled_size; + start_col = MCU_col_num * compptr->MCU_sample_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (cinfo->input_iMCU_row < last_iMCU_row || + yoffset+yindex < compptr->last_row_height) { + output_col = start_col; + for (xindex = 0; xindex < useful_width; xindex++) { + (*inverse_DCT) (cinfo, compptr, + (JCOEFPTR) coef->MCU_buffer[blkn+xindex], + output_ptr, output_col); + output_col += compptr->DCT_scaled_size; + } + } + blkn += compptr->MCU_width; + output_ptr += compptr->DCT_scaled_size; + } + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->MCU_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + cinfo->output_iMCU_row++; + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + + +/* + * Dummy consume-input routine for single-pass operation. + */ + +METHODDEF(int) +dummy_consume_data (CPL_UNUSED j_decompress_ptr cinfo) +{ + return JPEG_SUSPENDED; /* Always indicate nothing was done */ +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Consume input data and store it in the full-image coefficient buffer. + * We read as much as one fully interleaved MCU row ("iMCU" row) per call, + * ie, v_samp_factor block rows for each component in the scan. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + */ + +METHODDEF(int) +consume_data (j_decompress_ptr cinfo) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + int blkn, ci, xindex, yindex, yoffset; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + cinfo->input_iMCU_row * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, TRUE); + /* Note: entropy decoder expects buffer to be zeroed, + * but this is handled automatically by the memory manager + * because we requested a pre-zeroed array. + */ + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->MCU_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + coef->MCU_buffer[blkn++] = buffer_ptr++; + } + } + } + /* Try to fetch the MCU. */ + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->MCU_ctr = MCU_col_num; + return JPEG_SUSPENDED; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->MCU_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + + +/* + * Decompress and return some data in the multi-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image. + */ + +METHODDEF(int) +decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION block_num; + int ci, block_row, block_rows; + JBLOCKARRAY buffer; + JBLOCKROW buffer_ptr; + JSAMPARRAY output_ptr; + JDIMENSION output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number < cinfo->output_scan_number || + (cinfo->input_scan_number == cinfo->output_scan_number && + cinfo->input_iMCU_row <= cinfo->output_iMCU_row)) { + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) + continue; + /* Align the virtual buffer for this component. */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + cinfo->output_iMCU_row * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + /* Count non-dummy DCT block rows in this iMCU row. */ + if (cinfo->output_iMCU_row < last_iMCU_row) + block_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + } + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + output_ptr = output_buf[ci]; + /* Loop over all DCT blocks to be processed. */ + for (block_row = 0; block_row < block_rows; block_row++) { + buffer_ptr = buffer[block_row]; + output_col = 0; + for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { + (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, + output_ptr, output_col); + buffer_ptr++; + output_col += compptr->DCT_scaled_size; + } + output_ptr += compptr->DCT_scaled_size; + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +#ifdef BLOCK_SMOOTHING_SUPPORTED + +/* + * This code applies interblock smoothing as described by section K.8 + * of the JPEG standard: the first 5 AC coefficients are estimated from + * the DC values of a DCT block and its 8 neighboring blocks. + * We apply smoothing only for progressive JPEG decoding, and only if + * the coefficients it can estimate are not yet known to full precision. + */ + +/* Natural-order array positions of the first 5 zigzag-order coefficients */ +#define Q01_POS 1 +#define Q10_POS 8 +#define Q20_POS 16 +#define Q11_POS 9 +#define Q02_POS 2 + +/* + * Determine whether block smoothing is applicable and safe. + * We also latch the current states of the coef_bits[] entries for the + * AC coefficients; otherwise, if the input side of the decompressor + * advances into a new scan, we might think the coefficients are known + * more accurately than they really are. + */ + +LOCAL(boolean) +smoothing_ok (j_decompress_ptr cinfo) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + boolean smoothing_useful = FALSE; + int ci, coefi; + jpeg_component_info *compptr; + JQUANT_TBL * qtable; + int * coef_bits; + int * coef_bits_latch; + + if (! cinfo->progressive_mode || cinfo->coef_bits == NULL) + return FALSE; + + /* Allocate latch area if not already done */ + if (coef->coef_bits_latch == NULL) + coef->coef_bits_latch = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * + (SAVED_COEFS * SIZEOF(int))); + coef_bits_latch = coef->coef_bits_latch; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* All components' quantization values must already be latched. */ + if ((qtable = compptr->quant_table) == NULL) + return FALSE; + /* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */ + if (qtable->quantval[0] == 0 || + qtable->quantval[Q01_POS] == 0 || + qtable->quantval[Q10_POS] == 0 || + qtable->quantval[Q20_POS] == 0 || + qtable->quantval[Q11_POS] == 0 || + qtable->quantval[Q02_POS] == 0) + return FALSE; + /* DC values must be at least partly known for all components. */ + coef_bits = cinfo->coef_bits[ci]; + if (coef_bits[0] < 0) + return FALSE; + /* Block smoothing is helpful if some AC coefficients remain inaccurate. */ + for (coefi = 1; coefi <= 5; coefi++) { + coef_bits_latch[coefi] = coef_bits[coefi]; + if (coef_bits[coefi] != 0) + smoothing_useful = TRUE; + } + coef_bits_latch += SAVED_COEFS; + } + + return smoothing_useful; +} + + +/* + * Variant of decompress_data for use when doing block smoothing. + */ + +METHODDEF(int) +decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION block_num, last_block_column; + int ci, block_row, block_rows, access_rows; + JBLOCKARRAY buffer; + JBLOCKROW buffer_ptr, prev_block_row, next_block_row; + JSAMPARRAY output_ptr; + JDIMENSION output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + boolean first_row, last_row; + JBLOCK workspace; + int *coef_bits; + JQUANT_TBL *quanttbl; + INT32 Q00,Q01,Q02,Q10,Q11,Q20, num; + int DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9; + int Al, pred; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number <= cinfo->output_scan_number && + ! cinfo->inputctl->eoi_reached) { + if (cinfo->input_scan_number == cinfo->output_scan_number) { + /* If input is working on current scan, we ordinarily want it to + * have completed the current row. But if input scan is DC, + * we want it to keep one row ahead so that next block row's DC + * values are up to date. + */ + JDIMENSION delta = (cinfo->Ss == 0) ? 1 : 0; + if (cinfo->input_iMCU_row > cinfo->output_iMCU_row+delta) + break; + } + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) + continue; + /* Count non-dummy DCT block rows in this iMCU row. */ + if (cinfo->output_iMCU_row < last_iMCU_row) { + block_rows = compptr->v_samp_factor; + access_rows = block_rows * 2; /* this and next iMCU row */ + last_row = FALSE; + } else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + access_rows = block_rows; /* this iMCU row only */ + last_row = TRUE; + } + /* Align the virtual buffer for this component. */ + if (cinfo->output_iMCU_row > 0) { + access_rows += compptr->v_samp_factor; /* prior iMCU row too */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + (cinfo->output_iMCU_row - 1) * compptr->v_samp_factor, + (JDIMENSION) access_rows, FALSE); + buffer += compptr->v_samp_factor; /* point to current iMCU row */ + first_row = FALSE; + } else { + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + (JDIMENSION) 0, (JDIMENSION) access_rows, FALSE); + first_row = TRUE; + } + /* Fetch component-dependent info */ + coef_bits = coef->coef_bits_latch + (ci * SAVED_COEFS); + quanttbl = compptr->quant_table; + Q00 = quanttbl->quantval[0]; + Q01 = quanttbl->quantval[Q01_POS]; + Q10 = quanttbl->quantval[Q10_POS]; + Q20 = quanttbl->quantval[Q20_POS]; + Q11 = quanttbl->quantval[Q11_POS]; + Q02 = quanttbl->quantval[Q02_POS]; + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + output_ptr = output_buf[ci]; + /* Loop over all DCT blocks to be processed. */ + for (block_row = 0; block_row < block_rows; block_row++) { + buffer_ptr = buffer[block_row]; + if (first_row && block_row == 0) + prev_block_row = buffer_ptr; + else + prev_block_row = buffer[block_row-1]; + if (last_row && block_row == block_rows-1) + next_block_row = buffer_ptr; + else + next_block_row = buffer[block_row+1]; + /* We fetch the surrounding DC values using a sliding-register approach. + * Initialize all nine here so as to do the right thing on narrow pics. + */ + DC1 = DC2 = DC3 = (int) prev_block_row[0][0]; + DC4 = DC5 = DC6 = (int) buffer_ptr[0][0]; + DC7 = DC8 = DC9 = (int) next_block_row[0][0]; + output_col = 0; + last_block_column = compptr->width_in_blocks - 1; + for (block_num = 0; block_num <= last_block_column; block_num++) { + /* Fetch current DCT block into workspace so we can modify it. */ + jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1); + /* Update DC values */ + if (block_num < last_block_column) { + DC3 = (int) prev_block_row[1][0]; + DC6 = (int) buffer_ptr[1][0]; + DC9 = (int) next_block_row[1][0]; + } + /* Compute coefficient estimates per K.8. + * An estimate is applied only if coefficient is still zero, + * and is not known to be fully accurate. + */ + /* AC01 */ + if ((Al=coef_bits[1]) != 0 && workspace[1] == 0) { + num = 36 * Q00 * (DC4 - DC6); + if (num >= 0) { + pred = (int) (((Q01<<7) + num) / (Q01<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q10<<7) + num) / (Q10<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q20<<7) + num) / (Q20<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q11<<7) + num) / (Q11<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q02<<7) + num) / (Q02<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<DCT_scaled_size; + } + output_ptr += compptr->DCT_scaled_size; + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* BLOCK_SMOOTHING_SUPPORTED */ + + +/* + * Initialize coefficient buffer controller. + */ + +GLOBAL(void) +jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_coef_ptr coef; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_d_coef_controller *) coef; + coef->pub.start_input_pass = start_input_pass; + coef->pub.start_output_pass = start_output_pass; +#ifdef BLOCK_SMOOTHING_SUPPORTED + coef->coef_bits_latch = NULL; +#endif + + /* Create the coefficient buffer. */ + if (need_full_buffer) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* Allocate a full-image virtual array for each component, */ + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + /* Note we ask for a pre-zeroed array. */ + int ci, access_rows; + jpeg_component_info *compptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + access_rows = compptr->v_samp_factor; +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* If block smoothing could be used, need a bigger window */ + if (cinfo->progressive_mode) + access_rows *= 3; +#endif + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, TRUE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) access_rows); + } + coef->pub.consume_data = consume_data; + coef->pub.decompress_data = decompress_data; + coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* We only need a single-MCU buffer. */ + JBLOCKROW buffer; + int i; + + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) { + coef->MCU_buffer[i] = buffer + i; + } + coef->pub.consume_data = dummy_consume_data; + coef->pub.decompress_data = decompress_onepass; + coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */ + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdcolor.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdcolor.c new file mode 100644 index 000000000..eced921ef --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdcolor.c @@ -0,0 +1,397 @@ +/* + * jdcolor.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains output colorspace conversion routines. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#include "cpl_port.h" + +/* Private subobject */ + +typedef struct { + struct jpeg_color_deconverter pub; /* public fields */ + + /* Private state for YCC->RGB conversion */ + int * Cr_r_tab; /* => table for Cr to R conversion */ + int * Cb_b_tab; /* => table for Cb to B conversion */ + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ +} my_color_deconverter; + +typedef my_color_deconverter * my_cconvert_ptr; + + +/**************** YCbCr -> RGB conversion: most common case **************/ + +/* + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * where Cb and Cr represent the incoming values less CENTERJSAMPLE. + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + * + * To avoid floating-point arithmetic, we represent the fractional constants + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + * the products by 2^16, with appropriate rounding, to get the correct answer. + * Notice that Y, being an integral input, does not contribute any fraction + * so it need not participate in the rounding. + * + * For even more speed, we avoid doing any multiplications in the inner loop + * by precalculating the constants times Cb and Cr for all possible values. + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + * for 12-bit samples it is still acceptable. It's not very reasonable for + * 16-bit samples, but if you want lossless storage you shouldn't be changing + * colorspace anyway. + * The Cr=>R and Cb=>B values can be rounded to integers in advance; the + * values for the G calculation are left scaled up, since we must add them + * together before rounding. + */ + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + */ + +LOCAL(void) +build_ycc_rgb_table (j_decompress_ptr cinfo) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + int i; + INT32 x; + SHIFT_TEMPS + + cconvert->Cr_r_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + cconvert->Cb_b_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + cconvert->Cr_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + cconvert->Cb_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + /* Cr=>R value is nearest int to 1.40200 * x */ + cconvert->Cr_r_tab[i] = (int) + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + /* Cb=>B value is nearest int to 1.77200 * x */ + cconvert->Cb_b_tab[i] = (int) + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + /* Cr=>G value is scaled-up -0.71414 * x */ + cconvert->Cr_g_tab[i] = (- FIX(0.71414)) * x; + /* Cb=>G value is scaled-up -0.34414 * x */ + /* We also add in ONE_HALF so that need not do it in inner loop */ + cconvert->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + } +} + + +/* + * Convert some rows of samples to the output colorspace. + * + * Note that we change from noninterleaved, one-plane-per-component format + * to interleaved-pixel format. The output buffer is therefore three times + * as wide as the input buffer. + * A starting row offset is provided only for the input buffer. The caller + * can easily adjust the passed output_buf value to accommodate any row + * offset required on that side. + */ + +METHODDEF(void) +ycc_rgb_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[RGB_RED] = range_limit[y + Crrtab[cr]]; + outptr[RGB_GREEN] = range_limit[y + + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS))]; + outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]]; + outptr += RGB_PIXELSIZE; + } + } +} + + +/**************** Cases other than YCbCr -> RGB **************/ + + +/* + * Color conversion for no colorspace change: just copy the data, + * converting from separate-planes to interleaved representation. + */ + +METHODDEF(void) +null_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + register JSAMPROW inptr, outptr; + register JDIMENSION count; + register int num_components = cinfo->num_components; + JDIMENSION num_cols = cinfo->output_width; + int ci; + + while (--num_rows >= 0) { + for (ci = 0; ci < num_components; ci++) { + inptr = input_buf[ci][input_row]; + outptr = output_buf[0] + ci; + for (count = num_cols; count > 0; count--) { + *outptr = *inptr++; /* needn't bother with GETJSAMPLE() here */ + outptr += num_components; + } + } + input_row++; + output_buf++; + } +} + + +/* + * Color conversion for grayscale: just copy the data. + * This also works for YCbCr -> grayscale conversion, in which + * we just copy the Y (luminance) component and ignore chrominance. + */ + +METHODDEF(void) +grayscale_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + jcopy_sample_rows(input_buf[0], (int) input_row, output_buf, 0, + num_rows, cinfo->output_width); +} + + +/* + * Convert grayscale to RGB: just duplicate the graylevel three times. + * This is provided to support applications that don't want to cope + * with grayscale as a separate case. + */ + +METHODDEF(void) +gray_rgb_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + register JSAMPROW inptr, outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + + while (--num_rows >= 0) { + inptr = input_buf[0][input_row++]; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + /* We can dispense with GETJSAMPLE() here */ + outptr[RGB_RED] = outptr[RGB_GREEN] = outptr[RGB_BLUE] = inptr[col]; + outptr += RGB_PIXELSIZE; + } + } +} + + +/* + * Adobe-style YCCK->CMYK conversion. + * We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same + * conversion as above, while passing K (black) unchanged. + * We assume build_ycc_rgb_table has been called. + */ + +METHODDEF(void) +ycck_cmyk_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2, inptr3; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + inptr3 = input_buf[3][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[0] = range_limit[MAXJSAMPLE - (y + Crrtab[cr])]; /* red */ + outptr[1] = range_limit[MAXJSAMPLE - (y + /* green */ + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS)))]; + outptr[2] = range_limit[MAXJSAMPLE - (y + Cbbtab[cb])]; /* blue */ + /* K passes through unchanged */ + outptr[3] = inptr3[col]; /* don't need GETJSAMPLE here */ + outptr += 4; + } + } +} + + +/* + * Empty method for start_pass. + */ + +METHODDEF(void) +start_pass_dcolor (CPL_UNUSED j_decompress_ptr cinfo) +{ + /* no work needed */ +} + + +/* + * Module initialization routine for output colorspace conversion. + */ + +GLOBAL(void) +jinit_color_deconverter (j_decompress_ptr cinfo) +{ + my_cconvert_ptr cconvert; + int ci; + + cconvert = (my_cconvert_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_color_deconverter)); + cinfo->cconvert = (struct jpeg_color_deconverter *) cconvert; + cconvert->pub.start_pass = start_pass_dcolor; + + /* Make sure num_components agrees with jpeg_color_space */ + switch (cinfo->jpeg_color_space) { + case JCS_GRAYSCALE: + if (cinfo->num_components != 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + case JCS_RGB: + case JCS_YCbCr: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + case JCS_CMYK: + case JCS_YCCK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + default: /* JCS_UNKNOWN can be anything */ + if (cinfo->num_components < 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + } + + /* Set out_color_components and conversion method based on requested space. + * Also clear the component_needed flags for any unused components, + * so that earlier pipeline stages can avoid useless computation. + */ + + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + if (cinfo->jpeg_color_space == JCS_GRAYSCALE || + cinfo->jpeg_color_space == JCS_YCbCr) { + cconvert->pub.color_convert = grayscale_convert; + /* For color->grayscale conversion, only the Y (0) component is needed */ + for (ci = 1; ci < cinfo->num_components; ci++) + cinfo->comp_info[ci].component_needed = FALSE; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_RGB: + cinfo->out_color_components = RGB_PIXELSIZE; + if (cinfo->jpeg_color_space == JCS_YCbCr) { + cconvert->pub.color_convert = ycc_rgb_convert; + build_ycc_rgb_table(cinfo); + } else if (cinfo->jpeg_color_space == JCS_GRAYSCALE) { + cconvert->pub.color_convert = gray_rgb_convert; + } else if (cinfo->jpeg_color_space == JCS_RGB && RGB_PIXELSIZE == 3) { + cconvert->pub.color_convert = null_convert; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_CMYK: + cinfo->out_color_components = 4; + if (cinfo->jpeg_color_space == JCS_YCCK) { + cconvert->pub.color_convert = ycck_cmyk_convert; + build_ycc_rgb_table(cinfo); + } else if (cinfo->jpeg_color_space == JCS_CMYK) { + cconvert->pub.color_convert = null_convert; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + default: + /* Permit null conversion to same output space */ + if (cinfo->out_color_space == cinfo->jpeg_color_space) { + cinfo->out_color_components = cinfo->num_components; + cconvert->pub.color_convert = null_convert; + } else /* unsupported non-null conversion */ + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + } + + if (cinfo->quantize_colors) + cinfo->output_components = 1; /* single colormapped output component */ + else + cinfo->output_components = cinfo->out_color_components; +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdct.h b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdct.h new file mode 100644 index 000000000..39b593e16 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdct.h @@ -0,0 +1,188 @@ +/* + * jdct.h + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This include file contains common declarations for the forward and + * inverse DCT modules. These declarations are private to the DCT managers + * (jcdctmgr.c, jddctmgr.c) and the individual DCT algorithms. + * The individual DCT algorithms are kept in separate files to ease + * machine-dependent tuning (e.g., assembly coding). + */ + + +/* + * A forward DCT routine is given a pointer to a work area of type DCTELEM[]; + * the DCT is to be performed in-place in that buffer. Type DCTELEM is int + * for 8-bit samples, INT32 for 12-bit samples. (NOTE: Floating-point DCT + * implementations use an array of type FAST_FLOAT, instead.) + * The DCT inputs are expected to be signed (range +-CENTERJSAMPLE). + * The DCT outputs are returned scaled up by a factor of 8; they therefore + * have a range of +-8K for 8-bit data, +-128K for 12-bit data. This + * convention improves accuracy in integer implementations and saves some + * work in floating-point ones. + * Quantization of the output coefficients is done by jcdctmgr.c. + */ + +#if BITS_IN_JSAMPLE == 8 +typedef int DCTELEM; /* 16 or 32 bits is fine */ +#else +typedef INT32 DCTELEM; /* must have 32 bits */ +#endif + +typedef JMETHOD(void, forward_DCT_method_ptr, (DCTELEM * data)); +typedef JMETHOD(void, float_DCT_method_ptr, (FAST_FLOAT * data)); + + +/* + * An inverse DCT routine is given a pointer to the input JBLOCK and a pointer + * to an output sample array. The routine must dequantize the input data as + * well as perform the IDCT; for dequantization, it uses the multiplier table + * pointed to by compptr->dct_table. The output data is to be placed into the + * sample array starting at a specified column. (Any row offset needed will + * be applied to the array pointer before it is passed to the IDCT code.) + * Note that the number of samples emitted by the IDCT routine is + * DCT_scaled_size * DCT_scaled_size. + */ + +/* typedef inverse_DCT_method_ptr is declared in jpegint.h */ + +/* + * Each IDCT routine has its own ideas about the best dct_table element type. + */ + +typedef MULTIPLIER ISLOW_MULT_TYPE; /* short or int, whichever is faster */ +#if BITS_IN_JSAMPLE == 8 +typedef MULTIPLIER IFAST_MULT_TYPE; /* 16 bits is OK, use short if faster */ +#define IFAST_SCALE_BITS 2 /* fractional bits in scale factors */ +#else +typedef INT32 IFAST_MULT_TYPE; /* need 32 bits for scaled quantizers */ +#define IFAST_SCALE_BITS 13 /* fractional bits in scale factors */ +#endif +typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */ + + +/* + * Each IDCT routine is responsible for range-limiting its results and + * converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could + * be quite far out of range if the input data is corrupt, so a bulletproof + * range-limiting step is required. We use a mask-and-table-lookup method + * to do the combined operations quickly. See the comments with + * prepare_range_limit_table (in jdmaster.c) for more info. + */ + +#define IDCT_range_limit(cinfo) ((cinfo)->sample_range_limit + CENTERJSAMPLE) + +#define RANGE_MASK (MAXJSAMPLE * 4 + 3) /* 2 bits wider than legal samples */ + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_fdct_islow jFDislow +#define jpeg_fdct_ifast jFDifast +#define jpeg_fdct_float jFDfloat +#define jpeg_idct_islow jRDislow +#define jpeg_idct_ifast jRDifast +#define jpeg_idct_float jRDfloat +#define jpeg_idct_4x4 jRD4x4 +#define jpeg_idct_2x2 jRD2x2 +#define jpeg_idct_1x1 jRD1x1 +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +#ifdef NEED_12_BIT_NAMES +#define jpeg_fdct_islow jpeg_fdct_islow_12 +#define jpeg_fdct_ifast jpeg_fdct_ifast_12 +#define jpeg_fdct_float jpeg_fdct_float_12 +#define jpeg_idct_islow jpeg_idct_islow_12 +#define jpeg_idct_ifast jpeg_idct_ifast_12 +#define jpeg_idct_float jpeg_idct_float_12 +#define jpeg_idct_4x4 jpeg_idct_4x4_12 +#define jpeg_idct_2x2 jpeg_idct_2x2_12 +#define jpeg_idct_1x1 jpeg_idct_1x1_12 +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Extern declarations for the forward and inverse DCT routines. */ + +EXTERN(void) jpeg_fdct_islow JPP((DCTELEM * data)); +EXTERN(void) jpeg_fdct_ifast JPP((DCTELEM * data)); +EXTERN(void) jpeg_fdct_float JPP((FAST_FLOAT * data)); + +EXTERN(void) jpeg_idct_islow + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_ifast + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_float + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_4x4 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_2x2 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_1x1 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + + +/* + * Macros for handling fixed-point arithmetic; these are used by many + * but not all of the DCT/IDCT modules. + * + * All values are expected to be of type INT32. + * Fractional constants are scaled left by CONST_BITS bits. + * CONST_BITS is defined within each module using these macros, + * and may differ from one module to the next. + */ + +#define ONE ((INT32) 1) +#define CONST_SCALE (ONE << CONST_BITS) + +/* Convert a positive real constant to an integer scaled by CONST_SCALE. + * Caution: some C compilers fail to reduce "FIX(constant)" at compile time, + * thus causing a lot of useless floating-point operations at run time. + */ + +#define FIX(x) ((INT32) ((x) * CONST_SCALE + 0.5)) + +/* Descale and correctly round an INT32 value that's scaled by N bits. + * We assume RIGHT_SHIFT rounds towards minus infinity, so adding + * the fudge factor is correct for either sign of X. + */ + +#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n) + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * This macro is used only when the two inputs will actually be no more than + * 16 bits wide, so that a 16x16->32 bit multiply can be used instead of a + * full 32x32 multiply. This provides a useful speedup on many machines. + * Unfortunately there is no way to specify a 16x16->32 multiply portably + * in C, but some C compilers will do the right thing if you provide the + * correct combination of casts. + */ + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT16) (const))) +#endif +#ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */ +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT32) (const))) +#endif + +#ifndef MULTIPLY16C16 /* default definition */ +#define MULTIPLY16C16(var,const) ((var) * (const)) +#endif + +/* Same except both inputs are variables. */ + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY16V16(var1,var2) (((INT16) (var1)) * ((INT16) (var2))) +#endif + +#ifndef MULTIPLY16V16 /* default definition */ +#define MULTIPLY16V16(var1,var2) ((var1) * (var2)) +#endif diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jddctmgr.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jddctmgr.c new file mode 100644 index 000000000..bbf8d0e92 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jddctmgr.c @@ -0,0 +1,269 @@ +/* + * jddctmgr.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the inverse-DCT management logic. + * This code selects a particular IDCT implementation to be used, + * and it performs related housekeeping chores. No code in this file + * is executed per IDCT step, only during output pass setup. + * + * Note that the IDCT routines are responsible for performing coefficient + * dequantization as well as the IDCT proper. This module sets up the + * dequantization multiplier table needed by the IDCT routine. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + + +/* + * The decompressor input side (jdinput.c) saves away the appropriate + * quantization table for each component at the start of the first scan + * involving that component. (This is necessary in order to correctly + * decode files that reuse Q-table slots.) + * When we are ready to make an output pass, the saved Q-table is converted + * to a multiplier table that will actually be used by the IDCT routine. + * The multiplier table contents are IDCT-method-dependent. To support + * application changes in IDCT method between scans, we can remake the + * multiplier tables if necessary. + * In buffered-image mode, the first output pass may occur before any data + * has been seen for some components, and thus before their Q-tables have + * been saved away. To handle this case, multiplier tables are preset + * to zeroes; the result of the IDCT will be a neutral gray level. + */ + + +/* Private subobject for this module */ + +typedef struct { + struct jpeg_inverse_dct pub; /* public fields */ + + /* This array contains the IDCT method code that each multiplier table + * is currently set up for, or -1 if it's not yet set up. + * The actual multiplier tables are pointed to by dct_table in the + * per-component comp_info structures. + */ + int cur_method[MAX_COMPONENTS]; +} my_idct_controller; + +typedef my_idct_controller * my_idct_ptr; + + +/* Allocated multiplier tables: big enough for any supported variant */ + +typedef union { + ISLOW_MULT_TYPE islow_array[DCTSIZE2]; +#ifdef DCT_IFAST_SUPPORTED + IFAST_MULT_TYPE ifast_array[DCTSIZE2]; +#endif +#ifdef DCT_FLOAT_SUPPORTED + FLOAT_MULT_TYPE float_array[DCTSIZE2]; +#endif +} multiplier_table; + + +/* The current scaled-IDCT routines require ISLOW-style multiplier tables, + * so be sure to compile that code if either ISLOW or SCALING is requested. + */ +#ifdef DCT_ISLOW_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#else +#ifdef IDCT_SCALING_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#endif +#endif + + +/* + * Prepare for an output pass. + * Here we select the proper IDCT routine for each component and build + * a matching multiplier table. + */ + +METHODDEF(void) +start_pass (j_decompress_ptr cinfo) +{ + my_idct_ptr idct = (my_idct_ptr) cinfo->idct; + int ci, i; + jpeg_component_info *compptr; + int method = 0; + inverse_DCT_method_ptr method_ptr = NULL; + JQUANT_TBL * qtbl; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Select the proper IDCT routine for this component's scaling */ + switch (compptr->DCT_scaled_size) { +#ifdef IDCT_SCALING_SUPPORTED + case 1: + method_ptr = jpeg_idct_1x1; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; + case 2: + method_ptr = jpeg_idct_2x2; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; + case 4: + method_ptr = jpeg_idct_4x4; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; +#endif + case DCTSIZE: + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + method_ptr = jpeg_idct_islow; + method = JDCT_ISLOW; + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + method_ptr = jpeg_idct_ifast; + method = JDCT_IFAST; + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + method_ptr = jpeg_idct_float; + method = JDCT_FLOAT; + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + break; + default: + ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->DCT_scaled_size); + break; + } + idct->pub.inverse_DCT[ci] = method_ptr; + /* Create multiplier table from quant table. + * However, we can skip this if the component is uninteresting + * or if we already built the table. Also, if no quant table + * has yet been saved for the component, we leave the + * multiplier table all-zero; we'll be reading zeroes from the + * coefficient controller's buffer anyway. + */ + if (! compptr->component_needed || idct->cur_method[ci] == method) + continue; + qtbl = compptr->quant_table; + if (qtbl == NULL) /* happens if no data yet for component */ + continue; + idct->cur_method[ci] = method; + switch (method) { +#ifdef PROVIDE_ISLOW_TABLES + case JDCT_ISLOW: + { + /* For LL&M IDCT method, multipliers are equal to raw quantization + * coefficients, but are stored as ints to ensure access efficiency. + */ + ISLOW_MULT_TYPE * ismtbl = (ISLOW_MULT_TYPE *) compptr->dct_table; + for (i = 0; i < DCTSIZE2; i++) { + ismtbl[i] = (ISLOW_MULT_TYPE) qtbl->quantval[i]; + } + } + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + { + /* For AA&N IDCT method, multipliers are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * For integer operation, the multiplier table is to be scaled by + * IFAST_SCALE_BITS. + */ + IFAST_MULT_TYPE * ifmtbl = (IFAST_MULT_TYPE *) compptr->dct_table; +#define CONST_BITS 14 + static const INT16 aanscales[DCTSIZE2] = { + /* precomputed values scaled up by 14 bits */ + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + }; + SHIFT_TEMPS + + for (i = 0; i < DCTSIZE2; i++) { + ifmtbl[i] = (IFAST_MULT_TYPE) + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[i], + (INT32) aanscales[i]), + CONST_BITS-IFAST_SCALE_BITS); + } + } + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + { + /* For float AA&N IDCT method, multipliers are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + */ + FLOAT_MULT_TYPE * fmtbl = (FLOAT_MULT_TYPE *) compptr->dct_table; + int row, col; + static const double aanscalefactor[DCTSIZE] = { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + + i = 0; + for (row = 0; row < DCTSIZE; row++) { + for (col = 0; col < DCTSIZE; col++) { + fmtbl[i] = (FLOAT_MULT_TYPE) + ((double) qtbl->quantval[i] * + aanscalefactor[row] * aanscalefactor[col]); + i++; + } + } + } + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + } +} + + +/* + * Initialize IDCT manager. + */ + +GLOBAL(void) +jinit_inverse_dct (j_decompress_ptr cinfo) +{ + my_idct_ptr idct; + int ci; + jpeg_component_info *compptr; + + idct = (my_idct_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_idct_controller)); + cinfo->idct = (struct jpeg_inverse_dct *) idct; + idct->pub.start_pass = start_pass; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Allocate and pre-zero a multiplier table for each component */ + compptr->dct_table = + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(multiplier_table)); + MEMZERO(compptr->dct_table, SIZEOF(multiplier_table)); + /* Mark multiplier table not yet set up for any method */ + idct->cur_method[ci] = -1; + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdhuff.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdhuff.c new file mode 100644 index 000000000..a87274ede --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdhuff.c @@ -0,0 +1,657 @@ +/* + * jdhuff.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy decoding routines. + * + * Much of the complexity here has to do with supporting input suspension. + * If the data source module demands suspension, we want to be able to back + * up to the start of the current MCU. To do this, we copy state variables + * into local working storage, and update them back to the permanent + * storage only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdhuff.h" /* Declarations shared with jdphuff.c */ + + +/* + * Expanded entropy decoder object for Huffman decoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + savable_state saved; /* Other state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + + /* Precalculated info set up by start_pass for use in decode_mcu: */ + + /* Pointers to derived tables to be used for each block within an MCU */ + d_derived_tbl * dc_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + d_derived_tbl * ac_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + /* Whether we care about the DC and AC coefficient values for each block */ + boolean dc_needed[D_MAX_BLOCKS_IN_MCU]; + boolean ac_needed[D_MAX_BLOCKS_IN_MCU]; +} huff_entropy_decoder; + +typedef huff_entropy_decoder * huff_entropy_ptr; + + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF(void) +start_pass_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, blkn, dctbl, actbl; + jpeg_component_info * compptr; + + /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. + * This ought to be an error condition, but we make it a warning because + * there are some baseline files out there with all zeroes in these bytes. + */ + if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 || + cinfo->Ah != 0 || cinfo->Al != 0) + WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_d_derived_tbl(cinfo, TRUE, dctbl, + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_d_derived_tbl(cinfo, FALSE, actbl, + & entropy->ac_derived_tbls[actbl]); + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Precalculate decoding info for each block in an MCU of this scan */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + /* Precalculate which table to use for each block */ + entropy->dc_cur_tbls[blkn] = entropy->dc_derived_tbls[compptr->dc_tbl_no]; + entropy->ac_cur_tbls[blkn] = entropy->ac_derived_tbls[compptr->ac_tbl_no]; + /* Decide whether we really care about the coefficient values */ + if (compptr->component_needed) { + entropy->dc_needed[blkn] = TRUE; + /* we don't need the ACs if producing a 1/8th-size image */ + entropy->ac_needed[blkn] = (compptr->DCT_scaled_size > 1); + } else { + entropy->dc_needed[blkn] = entropy->ac_needed[blkn] = FALSE; + } + } + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->pub.insufficient_data = FALSE; + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + + +/* + * Compute the derived values for a Huffman table. + * This routine also performs some validation checks on the table. + * + * Note this is also used by jdphuff.c. + */ + +GLOBAL(void) +jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, + d_derived_tbl ** pdtbl) +{ + JHUFF_TBL *htbl; + d_derived_tbl *dtbl; + int p, i, l, si, numsymbols; + int lookbits, ctr; + char huffsize[257]; + unsigned int huffcode[257]; + unsigned int code; + + /* Note that huffsize[] and huffcode[] are filled in code-length order, + * paralleling the order of the symbols themselves in htbl->huffval[]. + */ + + /* Find the input Huffman table */ + if (tblno < 0 || tblno >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + htbl = + isDC ? cinfo->dc_huff_tbl_ptrs[tblno] : cinfo->ac_huff_tbl_ptrs[tblno]; + if (htbl == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + + /* Allocate a workspace if we haven't already done so. */ + if (*pdtbl == NULL) + *pdtbl = (d_derived_tbl *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(d_derived_tbl)); + dtbl = *pdtbl; + dtbl->pub = htbl; /* fill in back link */ + + /* Figure C.1: make table of Huffman code length for each symbol */ + + p = 0; + for (l = 1; l <= 16; l++) { + i = (int) htbl->bits[l]; + if (i < 0 || p + i > 256) /* protect against table overrun */ + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + while (i--) + huffsize[p++] = (char) l; + } + huffsize[p] = 0; + numsymbols = p; + + /* Figure C.2: generate the codes themselves */ + /* We also validate that the counts represent a legal Huffman code tree. */ + + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p]) { + while (((int) huffsize[p]) == si) { + huffcode[p++] = code; + code++; + } + /* code is now 1 more than the last code used for codelength si; but + * it must still fit in si bits, since no code is allowed to be all ones. + */ + if (((INT32) code) >= (((INT32) 1) << si)) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + code <<= 1; + si++; + } + + /* Figure F.15: generate decoding tables for bit-sequential decoding */ + + p = 0; + for (l = 1; l <= 16; l++) { + if (htbl->bits[l]) { + /* valoffset[l] = huffval[] index of 1st symbol of code length l, + * minus the minimum code of length l + */ + dtbl->valoffset[l] = (INT32) p - (INT32) huffcode[p]; + p += htbl->bits[l]; + dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */ + } else { + dtbl->maxcode[l] = -1; /* -1 if no codes of this length */ + } + } + dtbl->maxcode[17] = 0xFFFFFL; /* ensures jpeg_huff_decode terminates */ + + /* Compute lookahead tables to speed up decoding. + * First we set all the table entries to 0, indicating "too long"; + * then we iterate through the Huffman codes that are short enough and + * fill in all the entries that correspond to bit sequences starting + * with that code. + */ + + MEMZERO(dtbl->look_nbits, SIZEOF(dtbl->look_nbits)); + + p = 0; + for (l = 1; l <= HUFF_LOOKAHEAD; l++) { + for (i = 1; i <= (int) htbl->bits[l]; i++, p++) { + /* l = current code's length, p = its index in huffcode[] & huffval[]. */ + /* Generate left-justified code followed by all possible bit sequences */ + lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l); + for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) { + dtbl->look_nbits[lookbits] = l; + dtbl->look_sym[lookbits] = htbl->huffval[p]; + lookbits++; + } + } + } + + /* Validate symbols as being reasonable. + * For AC tables, we make no check, but accept all byte values 0..255. + * For DC tables, we require the symbols to be in range 0..15. + * (Tighter bounds could be applied depending on the data depth and mode, + * but this is sufficient to ensure safe decoding.) + */ + if (isDC) { + for (i = 0; i < numsymbols; i++) { + int sym = htbl->huffval[i]; + if (sym < 0 || sym > 15) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + } + } +} + + +/* + * Out-of-line code for bit fetching (shared with jdphuff.c). + * See jdhuff.h for info about usage. + * Note: current values of get_buffer and bits_left are passed as parameters, + * but are returned in the corresponding fields of the state struct. + * + * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width + * of get_buffer to be used. (On machines with wider words, an even larger + * buffer could be used.) However, on some machines 32-bit shifts are + * quite slow and take time proportional to the number of places shifted. + * (This is true with most PC compilers, for instance.) In this case it may + * be a win to set MIN_GET_BITS to the minimum value of 15. This reduces the + * average shift distance at the cost of more calls to jpeg_fill_bit_buffer. + */ + +#ifdef SLOW_SHIFT_32 +#define MIN_GET_BITS 15 /* minimum allowable value */ +#else +#define MIN_GET_BITS (BIT_BUF_SIZE-7) +#endif + + +GLOBAL(boolean) +jpeg_fill_bit_buffer (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + int nbits) +/* Load up the bit buffer to a depth of at least nbits */ +{ + /* Copy heavily used state fields into locals (hopefully registers) */ + register const JOCTET * next_input_byte = state->next_input_byte; + register size_t bytes_in_buffer = state->bytes_in_buffer; + j_decompress_ptr cinfo = state->cinfo; + + /* Attempt to load at least MIN_GET_BITS bits into get_buffer. */ + /* (It is assumed that no request will be for more than that many bits.) */ + /* We fail to do so only if we hit a marker or are forced to suspend. */ + + if (cinfo->unread_marker == 0) { /* cannot advance past a marker */ + while (bits_left < MIN_GET_BITS) { + register int c; + + /* Attempt to read a byte */ + if (bytes_in_buffer == 0) { + + cinfo->src->next_input_byte = next_input_byte; + cinfo->src->bytes_in_buffer = bytes_in_buffer; + + if (! (*cinfo->src->fill_input_buffer) (cinfo)) + return FALSE; + next_input_byte = cinfo->src->next_input_byte; + bytes_in_buffer = cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + + /* If it's 0xFF, check and discard stuffed zero byte */ + if (c == 0xFF) { + /* Loop here to discard any padding FF's on terminating marker, + * so that we can save a valid unread_marker value. NOTE: we will + * accept multiple FF's followed by a 0 as meaning a single FF data + * byte. This data pattern is not valid according to the standard. + */ + do { + if (bytes_in_buffer == 0) { + cinfo->src->next_input_byte = next_input_byte; + cinfo->src->bytes_in_buffer = bytes_in_buffer; + if (! (*cinfo->src->fill_input_buffer) (cinfo)) + return FALSE; + next_input_byte = cinfo->src->next_input_byte; + bytes_in_buffer = cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + } while (c == 0xFF); + + if (c == 0) { + /* Found FF/00, which represents an FF data byte */ + c = 0xFF; + } else { + /* Oops, it's actually a marker indicating end of compressed data. + * Save the marker code for later use. + * Fine point: it might appear that we should save the marker into + * bitread working state, not straight into permanent state. But + * once we have hit a marker, we cannot need to suspend within the + * current MCU, because we will read no more bytes from the data + * source. So it is OK to update permanent state right away. + */ + cinfo->unread_marker = c; + /* See if we need to insert some fake zero bits. */ + goto no_more_bytes; + } + } + + /* OK, load c into get_buffer */ + get_buffer = (get_buffer << 8) | c; + bits_left += 8; + } /* end while */ + } else { + no_more_bytes: + /* We get here if we've read the marker that terminates the compressed + * data segment. There should be enough bits in the buffer register + * to satisfy the request; if so, no problem. + */ + if (nbits > bits_left) { + /* Uh-oh. Report corrupted data to user and stuff zeroes into + * the data stream, so that we can produce some kind of image. + * We use a nonvolatile flag to ensure that only one warning message + * appears per data segment. + */ + if (! cinfo->entropy->insufficient_data) { + WARNMS(cinfo, JWRN_HIT_MARKER); + cinfo->entropy->insufficient_data = TRUE; + } + /* Fill the buffer with zero bits */ + get_buffer <<= MIN_GET_BITS - bits_left; + bits_left = MIN_GET_BITS; + } + } + + /* Unload the local registers */ + state->next_input_byte = next_input_byte; + state->bytes_in_buffer = bytes_in_buffer; + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + return TRUE; +} + + +/* + * Out-of-line code for Huffman code decoding. + * See jdhuff.h for info about usage. + */ + +GLOBAL(int) +jpeg_huff_decode (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + d_derived_tbl * htbl, int min_bits) +{ + register int l = min_bits; + register INT32 code; + + /* HUFF_DECODE has determined that the code is at least min_bits */ + /* bits long, so fetch that many bits in one swoop. */ + + CHECK_BIT_BUFFER(*state, l, return -1); + code = GET_BITS(l); + + /* Collect the rest of the Huffman code one bit at a time. */ + /* This is per Figure F.16 in the JPEG spec. */ + + while (code > htbl->maxcode[l]) { + code <<= 1; + CHECK_BIT_BUFFER(*state, 1, return -1); + code |= GET_BITS(1); + l++; + } + + /* Unload the local registers */ + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + /* With garbage input we may reach the sentinel value l = 17. */ + + if (l > 16) { + WARNMS(state->cinfo, JWRN_HUFF_BAD_CODE); + return 0; /* fake a zero as the safest result */ + } + + return htbl->pub->huffval[ (int) (code + htbl->valoffset[l]) ]; +} + + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and add will be faster than a table lookup. + */ + +#ifdef AVOID_TABLES + +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) + +#else + +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = /* entry n is 2**(n-1) */ + { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ + { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + +#endif /* AVOID_TABLES */ + + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL(boolean) +process_restart (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; + + /* Reset out-of-data flag, unless read_restart_marker left us smack up + * against a marker. In that case we will end up treating the next data + * segment as empty, and we can avoid producing bogus output pixels by + * leaving the flag set. + */ + if (cinfo->unread_marker == 0) + entropy->pub.insufficient_data = FALSE; + + return TRUE; +} + + +/* + * Decode and return one MCU's worth of Huffman-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. + * (Wholesale zeroing is usually a little faster than retail...) + * + * Returns FALSE if data source requested suspension. In that case no + * changes have been made to permanent state. (Exception: some output + * coefficients may already have been assigned. This is harmless for + * this module, since we'll just re-assign them on the next call.) + */ + +METHODDEF(boolean) +decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int blkn; + BITREAD_STATE_VARS; + savable_state state; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + JBLOCKROW block = MCU_data[blkn]; + d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn]; + d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn]; + register int s, k, r; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + if (entropy->dc_needed[blkn]) { + /* Convert DC difference to actual value, update last_dc_val */ + int ci = cinfo->MCU_membership[blkn]; + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ + (*block)[0] = (JCOEF) s; + } + + if (entropy->ac_needed[blkn]) { + + /* Section F.2.2.2: decode the AC coefficients */ + /* Since zeroes are skipped, output area must be cleared beforehand */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label2); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Output coefficient in natural (dezigzagged) order. + * Note: the extra entries in jpeg_natural_order[] will save us + * if k >= DCTSIZE2, which could happen if the data is corrupted. + */ + (*block)[jpeg_natural_order[k]] = (JCOEF) s; + } else { + if (r != 15) + break; + k += 15; + } + } + + } else { + + /* Section F.2.2.2: decode the AC coefficients */ + /* In this path we just discard the values */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label3); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } else { + if (r != 15) + break; + k += 15; + } + } + + } + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * Module initialization routine for Huffman entropy decoding. + */ + +GLOBAL(void) +jinit_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_huff_decoder; + entropy->pub.decode_mcu = decode_mcu; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdhuff.h b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdhuff.h new file mode 100644 index 000000000..a3511046b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdhuff.h @@ -0,0 +1,207 @@ +/* + * jdhuff.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for Huffman entropy decoding routines + * that are shared between the sequential decoder (jdhuff.c) and the + * progressive decoder (jdphuff.c). No other modules need to see these. + */ + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_make_d_derived_tbl jMkDDerived +#define jpeg_fill_bit_buffer jFilBitBuf +#define jpeg_huff_decode jHufDecode +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +#ifdef NEED_12_BIT_NAMES +#define jpeg_make_d_derived_tbl jpeg_make_d_derived_tbl_12 +#define jpeg_fill_bit_buffer jpeg_fill_bit_buffer_12 +#define jpeg_huff_decode jpeg_huff_decode_12 +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Derived data constructed for each Huffman table */ + +#define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */ + +typedef struct { + /* Basic tables: (element [0] of each array is unused) */ + INT32 maxcode[18]; /* largest code of length k (-1 if none) */ + /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ + INT32 valoffset[17]; /* huffval[] offset for codes of length k */ + /* valoffset[k] = huffval[] index of 1st symbol of code length k, less + * the smallest code of length k; so given a code of length k, the + * corresponding symbol is huffval[code + valoffset[k]] + */ + + /* Link to public Huffman table (needed only in jpeg_huff_decode) */ + JHUFF_TBL *pub; + + /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of + * the input data stream. If the next Huffman code is no more + * than HUFF_LOOKAHEAD bits long, we can obtain its length and + * the corresponding symbol directly from these tables. + */ + int look_nbits[1< 32 bits on your machine, and shifting/masking longs is + * reasonably fast, making bit_buf_type be long and setting BIT_BUF_SIZE + * appropriately should be a win. Unfortunately we can't define the size + * with something like #define BIT_BUF_SIZE (sizeof(bit_buf_type)*8) + * because not all machines measure sizeof in 8-bit bytes. + */ + +typedef struct { /* Bitreading state saved across MCUs */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ +} bitread_perm_state; + +typedef struct { /* Bitreading working state within an MCU */ + /* Current data source location */ + /* We need a copy, rather than munging the original, in case of suspension */ + const JOCTET * next_input_byte; /* => next byte to read from source */ + size_t bytes_in_buffer; /* # of bytes remaining in source buffer */ + /* Bit input buffer --- note these values are kept in register variables, + * not in this struct, inside the inner loops. + */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ + /* Pointer needed by jpeg_fill_bit_buffer. */ + j_decompress_ptr cinfo; /* back link to decompress master record */ +} bitread_working_state; + +/* Macros to declare and load/save bitread local variables. */ +#define BITREAD_STATE_VARS \ + register bit_buf_type get_buffer; \ + register int bits_left; \ + bitread_working_state br_state + +#define BITREAD_LOAD_STATE(cinfop,permstate) \ + br_state.cinfo = cinfop; \ + br_state.next_input_byte = cinfop->src->next_input_byte; \ + br_state.bytes_in_buffer = cinfop->src->bytes_in_buffer; \ + get_buffer = permstate.get_buffer; \ + bits_left = permstate.bits_left; + +#define BITREAD_SAVE_STATE(cinfop,permstate) \ + cinfop->src->next_input_byte = br_state.next_input_byte; \ + cinfop->src->bytes_in_buffer = br_state.bytes_in_buffer; \ + permstate.get_buffer = get_buffer; \ + permstate.bits_left = bits_left + +/* + * These macros provide the in-line portion of bit fetching. + * Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer + * before using GET_BITS, PEEK_BITS, or DROP_BITS. + * The variables get_buffer and bits_left are assumed to be locals, + * but the state struct might not be (jpeg_huff_decode needs this). + * CHECK_BIT_BUFFER(state,n,action); + * Ensure there are N bits in get_buffer; if suspend, take action. + * val = GET_BITS(n); + * Fetch next N bits. + * val = PEEK_BITS(n); + * Fetch next N bits without removing them from the buffer. + * DROP_BITS(n); + * Discard next N bits. + * The value N should be a simple variable, not an expression, because it + * is evaluated multiple times. + */ + +#define CHECK_BIT_BUFFER(state,nbits,action) \ + { if (bits_left < (nbits)) { \ + if (! jpeg_fill_bit_buffer(&(state),get_buffer,bits_left,nbits)) \ + { action; } \ + get_buffer = (state).get_buffer; bits_left = (state).bits_left; } } + +#define GET_BITS(nbits) \ + (((int) (get_buffer >> (bits_left -= (nbits)))) & ((1<<(nbits))-1)) + +#define PEEK_BITS(nbits) \ + (((int) (get_buffer >> (bits_left - (nbits)))) & ((1<<(nbits))-1)) + +#define DROP_BITS(nbits) \ + (bits_left -= (nbits)) + +/* Load up the bit buffer to a depth of at least nbits */ +EXTERN(boolean) jpeg_fill_bit_buffer + JPP((bitread_working_state * state, register bit_buf_type get_buffer, + register int bits_left, int nbits)); + + +/* + * Code for extracting next Huffman-coded symbol from input bit stream. + * Again, this is time-critical and we make the main paths be macros. + * + * We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits + * without looping. Usually, more than 95% of the Huffman codes will be 8 + * or fewer bits long. The few overlength codes are handled with a loop, + * which need not be inline code. + * + * Notes about the HUFF_DECODE macro: + * 1. Near the end of the data segment, we may fail to get enough bits + * for a lookahead. In that case, we do it the hard way. + * 2. If the lookahead table contains no entry, the next code must be + * more than HUFF_LOOKAHEAD bits long. + * 3. jpeg_huff_decode returns -1 if forced to suspend. + */ + +#define HUFF_DECODE(result,state,htbl,failaction,slowlabel) \ +{ register int nb, look; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + if (! jpeg_fill_bit_buffer(&state,get_buffer,bits_left, 0)) {failaction;} \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + nb = 1; goto slowlabel; \ + } \ + } \ + look = PEEK_BITS(HUFF_LOOKAHEAD); \ + if ((nb = htbl->look_nbits[look]) != 0) { \ + DROP_BITS(nb); \ + result = htbl->look_sym[look]; \ + } else { \ + nb = HUFF_LOOKAHEAD+1; \ +slowlabel: \ + if ((result=jpeg_huff_decode(&state,get_buffer,bits_left,htbl,nb)) < 0) \ + { failaction; } \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + } \ +} + +/* Out-of-line case for Huffman code fetching */ +EXTERN(int) jpeg_huff_decode + JPP((bitread_working_state * state, register bit_buf_type get_buffer, + register int bits_left, d_derived_tbl * htbl, int min_bits)); diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdinput.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdinput.c new file mode 100644 index 000000000..0c2ac8f12 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdinput.c @@ -0,0 +1,381 @@ +/* + * jdinput.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains input control logic for the JPEG decompressor. + * These routines are concerned with controlling the decompressor's input + * processing (marker reading and coefficient decoding). The actual input + * reading is done in jdmarker.c, jdhuff.c, and jdphuff.c. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef struct { + struct jpeg_input_controller pub; /* public fields */ + + boolean inheaders; /* TRUE until first SOS is reached */ +} my_input_controller; + +typedef my_input_controller * my_inputctl_ptr; + + +/* Forward declarations */ +METHODDEF(int) consume_markers JPP((j_decompress_ptr cinfo)); + + +/* + * Routines to calculate various quantities related to the size of the image. + */ + +LOCAL(void) +initial_setup (j_decompress_ptr cinfo) +/* Called once, when first SOS marker is reached */ +{ + int ci; + jpeg_component_info *compptr; + + /* Make sure image isn't bigger than I can handle */ + if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || + (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + /* For now, precision must match compiled-in value... */ + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + /* Check that number of components won't exceed internal array sizes */ + if (cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + + /* Compute maximum sampling factors; check factor validity */ + cinfo->max_h_samp_factor = 1; + cinfo->max_v_samp_factor = 1; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + ERREXIT(cinfo, JERR_BAD_SAMPLING); + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + compptr->h_samp_factor); + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + compptr->v_samp_factor); + } + + /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. + * In the full decompressor, this will be overridden by jdmaster.c; + * but in the transcoder, jdmaster.c is not used, so we must do it here. + */ + cinfo->min_DCT_scaled_size = DCTSIZE; + + /* Compute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->DCT_scaled_size = DCTSIZE; + /* Size in DCT blocks */ + compptr->width_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->height_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + /* downsampled_width and downsampled_height will also be overridden by + * jdmaster.c if we are doing full decompression. The transcoder library + * doesn't use these values, but the calling application might. + */ + /* Size in samples */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) cinfo->max_h_samp_factor); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) cinfo->max_v_samp_factor); + /* Mark component needed, until color conversion says otherwise */ + compptr->component_needed = TRUE; + /* Mark no quantization table yet saved for component */ + compptr->quant_table = NULL; + } + + /* Compute number of fully interleaved MCU rows. */ + cinfo->total_iMCU_rows = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + /* Decide whether file contains multiple scans */ + if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode) + cinfo->inputctl->has_multiple_scans = TRUE; + else + cinfo->inputctl->has_multiple_scans = FALSE; +} + + +LOCAL(void) +per_scan_setup (j_decompress_ptr cinfo) +/* Do computations that are needed before processing a JPEG scan */ +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] were set from SOS marker */ +{ + int ci, mcublks, tmp; + jpeg_component_info *compptr; + + if (cinfo->comps_in_scan == 1) { + + /* Noninterleaved (single-component) scan */ + compptr = cinfo->cur_comp_info[0]; + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = compptr->width_in_blocks; + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + /* For noninterleaved scan, always one block per MCU */ + compptr->MCU_width = 1; + compptr->MCU_height = 1; + compptr->MCU_blocks = 1; + compptr->MCU_sample_width = compptr->DCT_scaled_size; + compptr->last_col_width = 1; + /* For noninterleaved scans, it is convenient to define last_row_height + * as the number of block rows present in the last iMCU row. + */ + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (tmp == 0) tmp = compptr->v_samp_factor; + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + cinfo->blocks_in_MCU = 1; + cinfo->MCU_membership[0] = 0; + + } else { + + /* Interleaved (multi-component) scan */ + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + MAX_COMPS_IN_SCAN); + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, + (long) (cinfo->max_h_samp_factor*DCTSIZE)); + cinfo->MCU_rows_in_scan = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + cinfo->blocks_in_MCU = 0; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Sampling factors give # of blocks of component in each MCU */ + compptr->MCU_width = compptr->h_samp_factor; + compptr->MCU_height = compptr->v_samp_factor; + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + compptr->MCU_sample_width = compptr->MCU_width * compptr->DCT_scaled_size; + /* Figure number of non-dummy blocks in last MCU column & row */ + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + if (tmp == 0) tmp = compptr->MCU_width; + compptr->last_col_width = tmp; + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + if (tmp == 0) tmp = compptr->MCU_height; + compptr->last_row_height = tmp; + /* Prepare array describing MCU composition */ + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU) + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + while (mcublks-- > 0) { + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + } + } + + } +} + + +/* + * Save away a copy of the Q-table referenced by each component present + * in the current scan, unless already saved during a prior scan. + * + * In a multiple-scan JPEG file, the encoder could assign different components + * the same Q-table slot number, but change table definitions between scans + * so that each component uses a different Q-table. (The IJG encoder is not + * currently capable of doing this, but other encoders might.) Since we want + * to be able to dequantize all the components at the end of the file, this + * means that we have to save away the table actually used for each component. + * We do this by copying the table at the start of the first scan containing + * the component. + * The JPEG spec prohibits the encoder from changing the contents of a Q-table + * slot between scans of a component using that slot. If the encoder does so + * anyway, this decoder will simply use the Q-table values that were current + * at the start of the first scan for the component. + * + * The decompressor output side looks only at the saved quant tables, + * not at the current Q-table slots. + */ + +LOCAL(void) +latch_quant_tables (j_decompress_ptr cinfo) +{ + int ci, qtblno; + jpeg_component_info *compptr; + JQUANT_TBL * qtbl; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* No work if we already saved Q-table for this component */ + if (compptr->quant_table != NULL) + continue; + /* Make sure specified quantization table is present */ + qtblno = compptr->quant_tbl_no; + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + cinfo->quant_tbl_ptrs[qtblno] == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + /* OK, save away the quantization table */ + qtbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(JQUANT_TBL)); + MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); + compptr->quant_table = qtbl; + } +} + + +/* + * Initialize the input modules to read a scan of compressed data. + * The first call to this is done by jdmaster.c after initializing + * the entire decompressor (during jpeg_start_decompress). + * Subsequent calls come from consume_markers, below. + */ + +METHODDEF(void) +start_input_pass (j_decompress_ptr cinfo) +{ + per_scan_setup(cinfo); + latch_quant_tables(cinfo); + (*cinfo->entropy->start_pass) (cinfo); + (*cinfo->coef->start_input_pass) (cinfo); + cinfo->inputctl->consume_input = cinfo->coef->consume_data; +} + + +/* + * Finish up after inputting a compressed-data scan. + * This is called by the coefficient controller after it's read all + * the expected data of the scan. + */ + +METHODDEF(void) +finish_input_pass (j_decompress_ptr cinfo) +{ + cinfo->inputctl->consume_input = consume_markers; +} + + +/* + * Read JPEG markers before, between, or after compressed-data scans. + * Change state as necessary when a new scan is reached. + * Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + * + * The consume_input method pointer points either here or to the + * coefficient controller's consume_data routine, depending on whether + * we are reading a compressed data segment or inter-segment markers. + */ + +METHODDEF(int) +consume_markers (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + int val; + + if (inputctl->pub.eoi_reached) /* After hitting EOI, read no further */ + return JPEG_REACHED_EOI; + + val = (*cinfo->marker->read_markers) (cinfo); + + switch (val) { + case JPEG_REACHED_SOS: /* Found SOS */ + if (inputctl->inheaders) { /* 1st SOS */ + initial_setup(cinfo); + inputctl->inheaders = FALSE; + /* Note: start_input_pass must be called by jdmaster.c + * before any more input can be consumed. jdapimin.c is + * responsible for enforcing this sequencing. + */ + } else { /* 2nd or later SOS marker */ + if (! inputctl->pub.has_multiple_scans) + ERREXIT(cinfo, JERR_EOI_EXPECTED); /* Oops, I wasn't expecting this! */ + start_input_pass(cinfo); + } + break; + case JPEG_REACHED_EOI: /* Found EOI */ + inputctl->pub.eoi_reached = TRUE; + if (inputctl->inheaders) { /* Tables-only datastream, apparently */ + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_NO_SOS); + } else { + /* Prevent infinite loop in coef ctlr's decompress_data routine + * if user set output_scan_number larger than number of scans. + */ + if (cinfo->output_scan_number > cinfo->input_scan_number) + cinfo->output_scan_number = cinfo->input_scan_number; + } + break; + case JPEG_SUSPENDED: + break; + } + + return val; +} + + +/* + * Reset state to begin a fresh datastream. + */ + +METHODDEF(void) +reset_input_controller (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + + inputctl->pub.consume_input = consume_markers; + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + inputctl->pub.eoi_reached = FALSE; + inputctl->inheaders = TRUE; + /* Reset other modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->marker->reset_marker_reader) (cinfo); + /* Reset progression state -- would be cleaner if entropy decoder did this */ + cinfo->coef_bits = NULL; +} + + +/* + * Initialize the input controller module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL(void) +jinit_input_controller (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl; + + /* Create subobject in permanent pool */ + inputctl = (my_inputctl_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_input_controller)); + cinfo->inputctl = (struct jpeg_input_controller *) inputctl; + /* Initialize method pointers */ + inputctl->pub.consume_input = consume_markers; + inputctl->pub.reset_input_controller = reset_input_controller; + inputctl->pub.start_input_pass = start_input_pass; + inputctl->pub.finish_input_pass = finish_input_pass; + /* Initialize state: can't use reset_input_controller since we don't + * want to try to reset other modules yet. + */ + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + inputctl->pub.eoi_reached = FALSE; + inputctl->inheaders = TRUE; +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdmainct.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdmainct.c new file mode 100644 index 000000000..0e0072469 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdmainct.c @@ -0,0 +1,512 @@ +/* + * jdmainct.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the main buffer controller for decompression. + * The main buffer lies between the JPEG decompressor proper and the + * post-processor; it holds downsampled data in the JPEG colorspace. + * + * Note that this code is bypassed in raw-data mode, since the application + * supplies the equivalent of the main buffer in that case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * In the current system design, the main buffer need never be a full-image + * buffer; any full-height buffers will be found inside the coefficient or + * postprocessing controllers. Nonetheless, the main controller is not + * trivial. Its responsibility is to provide context rows for upsampling/ + * rescaling, and doing this in an efficient fashion is a bit tricky. + * + * Postprocessor input data is counted in "row groups". A row group + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + * sample rows of each component. (We require DCT_scaled_size values to be + * chosen such that these numbers are integers. In practice DCT_scaled_size + * values will likely be powers of two, so we actually have the stronger + * condition that DCT_scaled_size / min_DCT_scaled_size is an integer.) + * Upsampling will typically produce max_v_samp_factor pixel rows from each + * row group (times any additional scale factor that the upsampler is + * applying). + * + * The coefficient controller will deliver data to us one iMCU row at a time; + * each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or + * exactly min_DCT_scaled_size row groups. (This amount of data corresponds + * to one row of MCUs when the image is fully interleaved.) Note that the + * number of sample rows varies across components, but the number of row + * groups does not. Some garbage sample rows may be included in the last iMCU + * row at the bottom of the image. + * + * Depending on the vertical scaling algorithm used, the upsampler may need + * access to the sample row(s) above and below its current input row group. + * The upsampler is required to set need_context_rows TRUE at global selection + * time if so. When need_context_rows is FALSE, this controller can simply + * obtain one iMCU row at a time from the coefficient controller and dole it + * out as row groups to the postprocessor. + * + * When need_context_rows is TRUE, this controller guarantees that the buffer + * passed to postprocessing contains at least one row group's worth of samples + * above and below the row group(s) being processed. Note that the context + * rows "above" the first passed row group appear at negative row offsets in + * the passed buffer. At the top and bottom of the image, the required + * context rows are manufactured by duplicating the first or last real sample + * row; this avoids having special cases in the upsampling inner loops. + * + * The amount of context is fixed at one row group just because that's a + * convenient number for this controller to work with. The existing + * upsamplers really only need one sample row of context. An upsampler + * supporting arbitrary output rescaling might wish for more than one row + * group of context when shrinking the image; tough, we don't handle that. + * (This is justified by the assumption that downsizing will be handled mostly + * by adjusting the DCT_scaled_size values, so that the actual scale factor at + * the upsample step needn't be much less than one.) + * + * To provide the desired context, we have to retain the last two row groups + * of one iMCU row while reading in the next iMCU row. (The last row group + * can't be processed until we have another row group for its below-context, + * and so we have to save the next-to-last group too for its above-context.) + * We could do this most simply by copying data around in our buffer, but + * that'd be very slow. We can avoid copying any data by creating a rather + * strange pointer structure. Here's how it works. We allocate a workspace + * consisting of M+2 row groups (where M = min_DCT_scaled_size is the number + * of row groups per iMCU row). We create two sets of redundant pointers to + * the workspace. Labeling the physical row groups 0 to M+1, the synthesized + * pointer lists look like this: + * M+1 M-1 + * master pointer --> 0 master pointer --> 0 + * 1 1 + * ... ... + * M-3 M-3 + * M-2 M + * M-1 M+1 + * M M-2 + * M+1 M-1 + * 0 0 + * We read alternate iMCU rows using each master pointer; thus the last two + * row groups of the previous iMCU row remain un-overwritten in the workspace. + * The pointer lists are set up so that the required context rows appear to + * be adjacent to the proper places when we pass the pointer lists to the + * upsampler. + * + * The above pictures describe the normal state of the pointer lists. + * At top and bottom of the image, we diddle the pointer lists to duplicate + * the first or last sample row as necessary (this is cheaper than copying + * sample rows around). + * + * This scheme breaks down if M < 2, ie, min_DCT_scaled_size is 1. In that + * situation each iMCU row provides only one row group so the buffering logic + * must be different (eg, we must read two iMCU rows before we can emit the + * first row group). For now, we simply do not support providing context + * rows when min_DCT_scaled_size is 1. That combination seems unlikely to + * be worth providing --- if someone wants a 1/8th-size preview, they probably + * want it quick and dirty, so a context-free upsampler is sufficient. + */ + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_main_controller pub; /* public fields */ + + /* Pointer to allocated workspace (M or M+2 row groups). */ + JSAMPARRAY buffer[MAX_COMPONENTS]; + + boolean buffer_full; /* Have we gotten an iMCU row from decoder? */ + JDIMENSION rowgroup_ctr; /* counts row groups output to postprocessor */ + + /* Remaining fields are only used in the context case. */ + + /* These are the master pointers to the funny-order pointer lists. */ + JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */ + + int whichptr; /* indicates which pointer set is now in use */ + int context_state; /* process_data state machine status */ + JDIMENSION rowgroups_avail; /* row groups available to postprocessor */ + JDIMENSION iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ +} my_main_controller; + +typedef my_main_controller * my_main_ptr; + +/* context_state values: */ +#define CTX_PREPARE_FOR_IMCU 0 /* need to prepare for MCU row */ +#define CTX_PROCESS_IMCU 1 /* feeding iMCU to postprocessor */ +#define CTX_POSTPONED_ROW 2 /* feeding postponed row group */ + + +/* Forward declarations */ +METHODDEF(void) process_data_simple_main + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +METHODDEF(void) process_data_context_main + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +#ifdef QUANT_2PASS_SUPPORTED +METHODDEF(void) process_data_crank_post + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +#endif + + +LOCAL(void) +alloc_funny_pointers (j_decompress_ptr cinfo) +/* Allocate space for the funny pointer lists. + * This is done only once, not once per pass. + */ +{ + my_main_ptr mainp = (my_main_ptr) cinfo->main; + int ci, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf; + + /* Get top-level space for component array pointers. + * We alloc both arrays with one call to save a few cycles. + */ + mainp->xbuffer[0] = (JSAMPIMAGE) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * 2 * SIZEOF(JSAMPARRAY)); + mainp->xbuffer[1] = mainp->xbuffer[0] + cinfo->num_components; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + /* Get space for pointer lists --- M+4 row groups in each list. + * We alloc both pointer lists with one call to save a few cycles. + */ + xbuf = (JSAMPARRAY) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 2 * (rgroup * (M + 4)) * SIZEOF(JSAMPROW)); + xbuf += rgroup; /* want one row group at negative offsets */ + mainp->xbuffer[0][ci] = xbuf; + xbuf += rgroup * (M + 4); + mainp->xbuffer[1][ci] = xbuf; + } +} + + +LOCAL(void) +make_funny_pointers (j_decompress_ptr cinfo) +/* Create the funny pointer lists discussed in the comments above. + * The actual workspace is already allocated (in mainp->buffer), + * and the space for the pointer lists is allocated too. + * This routine just fills in the curiously ordered lists. + * This will be repeated at the beginning of each pass. + */ +{ + my_main_ptr mainp = (my_main_ptr) cinfo->main; + int ci, i, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY buf, xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + xbuf0 = mainp->xbuffer[0][ci]; + xbuf1 = mainp->xbuffer[1][ci]; + /* First copy the workspace pointers as-is */ + buf = mainp->buffer[ci]; + for (i = 0; i < rgroup * (M + 2); i++) { + xbuf0[i] = xbuf1[i] = buf[i]; + } + /* In the second list, put the last four row groups in swapped order */ + for (i = 0; i < rgroup * 2; i++) { + xbuf1[rgroup*(M-2) + i] = buf[rgroup*M + i]; + xbuf1[rgroup*M + i] = buf[rgroup*(M-2) + i]; + } + /* The wraparound pointers at top and bottom will be filled later + * (see set_wraparound_pointers, below). Initially we want the "above" + * pointers to duplicate the first actual data line. This only needs + * to happen in xbuffer[0]. + */ + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[0]; + } + } +} + + +LOCAL(void) +set_wraparound_pointers (j_decompress_ptr cinfo) +/* Set up the "wraparound" pointers at top and bottom of the pointer lists. + * This changes the pointer list state from top-of-image to the normal state. + */ +{ + my_main_ptr mainp = (my_main_ptr) cinfo->main; + int ci, i, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + xbuf0 = mainp->xbuffer[0][ci]; + xbuf1 = mainp->xbuffer[1][ci]; + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i]; + xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i]; + xbuf0[rgroup*(M+2) + i] = xbuf0[i]; + xbuf1[rgroup*(M+2) + i] = xbuf1[i]; + } + } +} + + +LOCAL(void) +set_bottom_pointers (j_decompress_ptr cinfo) +/* Change the pointer lists to duplicate the last sample row at the bottom + * of the image. whichptr indicates which xbuffer holds the final iMCU row. + * Also sets rowgroups_avail to indicate number of nondummy row groups in row. + */ +{ + my_main_ptr mainp = (my_main_ptr) cinfo->main; + int ci, i, rgroup, iMCUheight, rows_left; + jpeg_component_info *compptr; + JSAMPARRAY xbuf; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Count sample rows in one iMCU row and in one row group */ + iMCUheight = compptr->v_samp_factor * compptr->DCT_scaled_size; + rgroup = iMCUheight / cinfo->min_DCT_scaled_size; + /* Count nondummy sample rows remaining for this component */ + rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight); + if (rows_left == 0) rows_left = iMCUheight; + /* Count nondummy row groups. Should get same answer for each component, + * so we need only do it once. + */ + if (ci == 0) { + mainp->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1); + } + /* Duplicate the last real sample row rgroup*2 times; this pads out the + * last partial rowgroup and ensures at least one full rowgroup of context. + */ + xbuf = mainp->xbuffer[mainp->whichptr][ci]; + for (i = 0; i < rgroup * 2; i++) { + xbuf[rows_left + i] = xbuf[rows_left-1]; + } + } +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_main (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_main_ptr mainp = (my_main_ptr) cinfo->main; + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (cinfo->upsample->need_context_rows) { + mainp->pub.process_data = process_data_context_main; + make_funny_pointers(cinfo); /* Create the xbuffer[] lists */ + mainp->whichptr = 0; /* Read first iMCU row into xbuffer[0] */ + mainp->context_state = CTX_PREPARE_FOR_IMCU; + mainp->iMCU_row_ctr = 0; + } else { + /* Simple case with no context needed */ + mainp->pub.process_data = process_data_simple_main; + } + mainp->buffer_full = FALSE; /* Mark buffer empty */ + mainp->rowgroup_ctr = 0; + break; +#ifdef QUANT_2PASS_SUPPORTED + case JBUF_CRANK_DEST: + /* For last pass of 2-pass quantization, just crank the postprocessor */ + mainp->pub.process_data = process_data_crank_post; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +/* + * Process some data. + * This handles the simple case where no context is required. + */ + +METHODDEF(void) +process_data_simple_main (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_main_ptr mainp = (my_main_ptr) cinfo->main; + JDIMENSION rowgroups_avail; + + /* Read input data if we haven't filled the main buffer yet */ + if (! mainp->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, mainp->buffer)) + return; /* suspension forced, can do nothing more */ + mainp->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + } + + /* There are always min_DCT_scaled_size row groups in an iMCU row. */ + rowgroups_avail = (JDIMENSION) cinfo->min_DCT_scaled_size; + /* Note: at the bottom of the image, we may pass extra garbage row groups + * to the postprocessor. The postprocessor has to check for bottom + * of image anyway (at row resolution), so no point in us doing it too. + */ + + /* Feed the postprocessor */ + (*cinfo->post->post_process_data) (cinfo, mainp->buffer, + &mainp->rowgroup_ctr, rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + + /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ + if (mainp->rowgroup_ctr >= rowgroups_avail) { + mainp->buffer_full = FALSE; + mainp->rowgroup_ctr = 0; + } +} + + +/* + * Process some data. + * This handles the case where context rows must be provided. + */ + +METHODDEF(void) +process_data_context_main (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_main_ptr mainp = (my_main_ptr) cinfo->main; + + /* Read input data if we haven't filled the main buffer yet */ + if (! mainp->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, + mainp->xbuffer[mainp->whichptr])) + return; /* suspension forced, can do nothing more */ + mainp->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + mainp->iMCU_row_ctr++; /* count rows received */ + } + + /* Postprocessor typically will not swallow all the input data it is handed + * in one call (due to filling the output buffer first). Must be prepared + * to exit and restart. This switch lets us keep track of how far we got. + * Note that each case falls through to the next on successful completion. + */ + switch (mainp->context_state) { + case CTX_POSTPONED_ROW: + /* Call postprocessor using previously set pointers for postponed row */ + (*cinfo->post->post_process_data) (cinfo, mainp->xbuffer[mainp->whichptr], + &mainp->rowgroup_ctr, mainp->rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + if (mainp->rowgroup_ctr < mainp->rowgroups_avail) + return; /* Need to suspend */ + mainp->context_state = CTX_PREPARE_FOR_IMCU; + if (*out_row_ctr >= out_rows_avail) + return; /* Postprocessor exactly filled output buf */ + /*FALLTHROUGH*/ + case CTX_PREPARE_FOR_IMCU: + /* Prepare to process first M-1 row groups of this iMCU row */ + mainp->rowgroup_ctr = 0; + mainp->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size - 1); + /* Check for bottom of image: if so, tweak pointers to "duplicate" + * the last sample row, and adjust rowgroups_avail to ignore padding rows. + */ + if (mainp->iMCU_row_ctr == cinfo->total_iMCU_rows) + set_bottom_pointers(cinfo); + mainp->context_state = CTX_PROCESS_IMCU; + /*FALLTHROUGH*/ + case CTX_PROCESS_IMCU: + /* Call postprocessor using previously set pointers */ + (*cinfo->post->post_process_data) (cinfo, mainp->xbuffer[mainp->whichptr], + &mainp->rowgroup_ctr, mainp->rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + if (mainp->rowgroup_ctr < mainp->rowgroups_avail) + return; /* Need to suspend */ + /* After the first iMCU, change wraparound pointers to normal state */ + if (mainp->iMCU_row_ctr == 1) + set_wraparound_pointers(cinfo); + /* Prepare to load new iMCU row using other xbuffer list */ + mainp->whichptr ^= 1; /* 0=>1 or 1=>0 */ + mainp->buffer_full = FALSE; + /* Still need to process last row group of this iMCU row, */ + /* which is saved at index M+1 of the other xbuffer */ + mainp->rowgroup_ctr = (JDIMENSION) (cinfo->min_DCT_scaled_size + 1); + mainp->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size + 2); + mainp->context_state = CTX_POSTPONED_ROW; + } +} + + +/* + * Process some data. + * Final pass of two-pass quantization: just call the postprocessor. + * Source data will be the postprocessor controller's internal buffer. + */ + +#ifdef QUANT_2PASS_SUPPORTED + +METHODDEF(void) +process_data_crank_post (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + (*cinfo->post->post_process_data) (cinfo, (JSAMPIMAGE) NULL, + (JDIMENSION *) NULL, (JDIMENSION) 0, + output_buf, out_row_ctr, out_rows_avail); +} + +#endif /* QUANT_2PASS_SUPPORTED */ + + +/* + * Initialize main buffer controller. + */ + +GLOBAL(void) +jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_main_ptr mainp; + int ci, rgroup, ngroups; + jpeg_component_info *compptr; + + mainp = (my_main_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_main_controller)); + cinfo->main = (struct jpeg_d_main_controller *) mainp; + mainp->pub.start_pass = start_pass_main; + + if (need_full_buffer) /* shouldn't happen */ + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + /* Allocate the workspace. + * ngroups is the number of row groups we need. + */ + if (cinfo->upsample->need_context_rows) { + if (cinfo->min_DCT_scaled_size < 2) /* unsupported, see comments above */ + ERREXIT(cinfo, JERR_NOTIMPL); + alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */ + ngroups = cinfo->min_DCT_scaled_size + 2; + } else { + ngroups = cinfo->min_DCT_scaled_size; + } + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + mainp->buffer[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + compptr->width_in_blocks * compptr->DCT_scaled_size, + (JDIMENSION) (rgroup * ngroups)); + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdmarker.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdmarker.c new file mode 100644 index 000000000..c9325af23 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdmarker.c @@ -0,0 +1,1366 @@ +/* + * jdmarker.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to decode JPEG datastream markers. + * Most of the complexity arises from our desire to support input + * suspension: if not all of the data for a marker is available, + * we must exit back to the application. On resumption, we reprocess + * the marker. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_DAC = 0xcc, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_TEM = 0x01, + + M_ERROR = 0x100 +} JPEG_MARKER; + + +/* Private state */ + +typedef struct { + struct jpeg_marker_reader pub; /* public fields */ + + /* Application-overridable marker processing methods */ + jpeg_marker_parser_method process_COM; + jpeg_marker_parser_method process_APPn[16]; + + /* Limit on marker data length to save for each marker type */ + unsigned int length_limit_COM; + unsigned int length_limit_APPn[16]; + + /* Status of COM/APPn marker saving */ + jpeg_saved_marker_ptr cur_marker; /* NULL if not processing a marker */ + unsigned int bytes_read; /* data bytes read so far in marker */ + /* Note: cur_marker is not linked into marker_list until it's all read. */ +} my_marker_reader; + +typedef my_marker_reader * my_marker_ptr; + + +/* + * Macros for fetching data from the data source module. + * + * At all times, cinfo->src->next_input_byte and ->bytes_in_buffer reflect + * the current restart point; we update them only when we have reached a + * suitable place to restart if a suspension occurs. + */ + +/* Declare and initialize local copies of input pointer/count */ +#define INPUT_VARS(cinfo) \ + struct jpeg_source_mgr * datasrc = (cinfo)->src; \ + const JOCTET * next_input_byte = datasrc->next_input_byte; \ + size_t bytes_in_buffer = datasrc->bytes_in_buffer + +/* Unload the local copies --- do this only at a restart boundary */ +#define INPUT_SYNC(cinfo) \ + ( datasrc->next_input_byte = next_input_byte, \ + datasrc->bytes_in_buffer = bytes_in_buffer ) + +/* Reload the local copies --- used only in MAKE_BYTE_AVAIL */ +#define INPUT_RELOAD(cinfo) \ + ( next_input_byte = datasrc->next_input_byte, \ + bytes_in_buffer = datasrc->bytes_in_buffer ) + +/* Internal macro for INPUT_BYTE and INPUT_2BYTES: make a byte available. + * Note we do *not* do INPUT_SYNC before calling fill_input_buffer, + * but we must reload the local copies after a successful fill. + */ +#define MAKE_BYTE_AVAIL(cinfo,action) \ + if (bytes_in_buffer == 0) { \ + if (! (*datasrc->fill_input_buffer) (cinfo)) \ + { action; } \ + INPUT_RELOAD(cinfo); \ + } + +/* Read a byte into variable V. + * If must suspend, take the specified action (typically "return FALSE"). + */ +#define INPUT_BYTE(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + bytes_in_buffer--; \ + V = GETJOCTET(*next_input_byte++); ) + +/* As above, but read two bytes interpreted as an unsigned 16-bit integer. + * V should be declared unsigned int or perhaps INT32. + */ +#define INPUT_2BYTES(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + bytes_in_buffer--; \ + V = ((unsigned int) GETJOCTET(*next_input_byte++)) << 8; \ + MAKE_BYTE_AVAIL(cinfo,action); \ + bytes_in_buffer--; \ + V += GETJOCTET(*next_input_byte++); ) + + +/* + * Routines to process JPEG markers. + * + * Entry condition: JPEG marker itself has been read and its code saved + * in cinfo->unread_marker; input restart point is just after the marker. + * + * Exit: if return TRUE, have read and processed any parameters, and have + * updated the restart point to point after the parameters. + * If return FALSE, was forced to suspend before reaching end of + * marker parameters; restart point has not been moved. Same routine + * will be called again after application supplies more input data. + * + * This approach to suspension assumes that all of a marker's parameters + * can fit into a single input bufferload. This should hold for "normal" + * markers. Some COM/APPn markers might have large parameter segments + * that might not fit. If we are simply dropping such a marker, we use + * skip_input_data to get past it, and thereby put the problem on the + * source manager's shoulders. If we are saving the marker's contents + * into memory, we use a slightly different convention: when forced to + * suspend, the marker processor updates the restart point to the end of + * what it's consumed (ie, the end of the buffer) before returning FALSE. + * On resumption, cinfo->unread_marker still contains the marker code, + * but the data source will point to the next chunk of marker data. + * The marker processor must retain internal state to deal with this. + * + * Note that we don't bother to avoid duplicate trace messages if a + * suspension occurs within marker parameters. Other side effects + * require more care. + */ + + +LOCAL(boolean) +get_soi (j_decompress_ptr cinfo) +/* Process an SOI marker */ +{ + int i; + + TRACEMS(cinfo, 1, JTRC_SOI); + + if (cinfo->marker->saw_SOI) + ERREXIT(cinfo, JERR_SOI_DUPLICATE); + + /* Reset all parameters that are defined to be reset by SOI */ + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + cinfo->arith_dc_L[i] = 0; + cinfo->arith_dc_U[i] = 1; + cinfo->arith_ac_K[i] = 5; + } + cinfo->restart_interval = 0; + + /* Set initial assumptions for colorspace etc */ + + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->CCIR601_sampling = FALSE; /* Assume non-CCIR sampling??? */ + + cinfo->saw_JFIF_marker = FALSE; + cinfo->JFIF_major_version = 1; /* set default JFIF APP0 values */ + cinfo->JFIF_minor_version = 1; + cinfo->density_unit = 0; + cinfo->X_density = 1; + cinfo->Y_density = 1; + cinfo->saw_Adobe_marker = FALSE; + cinfo->Adobe_transform = 0; + + cinfo->marker->saw_SOI = TRUE; + + return TRUE; +} + + +LOCAL(boolean) +get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) +/* Process a SOFn marker */ +{ + INT32 length; + int c, ci; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + cinfo->progressive_mode = is_prog; + cinfo->arith_code = is_arith; + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, cinfo->data_precision, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_height, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_width, return FALSE); + INPUT_BYTE(cinfo, cinfo->num_components, return FALSE); + + length -= 8; + + TRACEMS4(cinfo, 1, JTRC_SOF, cinfo->unread_marker, + (int) cinfo->image_width, (int) cinfo->image_height, + cinfo->num_components); + + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_DUPLICATE); + + /* We don't support files in which the image height is initially specified */ + /* as 0 and is later redefined by DNL. As long as we have to check that, */ + /* might as well have a general sanity check. */ + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + || cinfo->num_components <= 0) + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + if (length != (cinfo->num_components * 3)) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + if (cinfo->comp_info == NULL) /* do only once, even if suspend */ + cinfo->comp_info = (jpeg_component_info *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * SIZEOF(jpeg_component_info)); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->component_index = ci; + INPUT_BYTE(cinfo, compptr->component_id, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + compptr->h_samp_factor = (c >> 4) & 15; + compptr->v_samp_factor = (c ) & 15; + INPUT_BYTE(cinfo, compptr->quant_tbl_no, return FALSE); + + TRACEMS4(cinfo, 1, JTRC_SOF_COMPONENT, + compptr->component_id, compptr->h_samp_factor, + compptr->v_samp_factor, compptr->quant_tbl_no); + } + + cinfo->marker->saw_SOF = TRUE; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL(boolean) +get_sos (j_decompress_ptr cinfo) +/* Process a SOS marker */ +{ + INT32 length; + int i, ci, n, c, cc; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + if (! cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOS_NO_SOF); + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, n, return FALSE); /* Number of components */ + + TRACEMS1(cinfo, 1, JTRC_SOS, n); + + if (length != (n * 2 + 6) || n < 1 || n > MAX_COMPS_IN_SCAN) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + cinfo->comps_in_scan = n; + + /* Collect the component-spec parameters */ + + for (i = 0; i < n; i++) { + INPUT_BYTE(cinfo, cc, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (cc == compptr->component_id) + goto id_found; + } + + ERREXIT1(cinfo, JERR_BAD_COMPONENT_ID, cc); + + id_found: + + cinfo->cur_comp_info[i] = compptr; + compptr->dc_tbl_no = (c >> 4) & 15; + compptr->ac_tbl_no = (c ) & 15; + + TRACEMS3(cinfo, 1, JTRC_SOS_COMPONENT, cc, + compptr->dc_tbl_no, compptr->ac_tbl_no); + + /* This CSi (cc) should differ from the previous CSi */ + for (ci = 0; ci < i; ci++) { + if (cinfo->cur_comp_info[ci] == compptr) + ERREXIT1(cinfo, JERR_BAD_COMPONENT_ID, cc); + } + } + + /* Collect the additional scan parameters Ss, Se, Ah/Al. */ + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ss = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Se = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ah = (c >> 4) & 15; + cinfo->Al = (c ) & 15; + + TRACEMS4(cinfo, 1, JTRC_SOS_PARAMS, cinfo->Ss, cinfo->Se, + cinfo->Ah, cinfo->Al); + + /* Prepare to scan data & restart markers */ + cinfo->marker->next_restart_num = 0; + + /* Count another SOS marker */ + cinfo->input_scan_number++; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +#ifdef D_ARITH_CODING_SUPPORTED + +LOCAL(boolean) +get_dac (j_decompress_ptr cinfo) +/* Process a DAC marker */ +{ + INT32 length; + int index, val; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, index, return FALSE); + INPUT_BYTE(cinfo, val, return FALSE); + + length -= 2; + + TRACEMS2(cinfo, 1, JTRC_DAC, index, val); + + if (index < 0 || index >= (2*NUM_ARITH_TBLS)) + ERREXIT1(cinfo, JERR_DAC_INDEX, index); + + if (index >= NUM_ARITH_TBLS) { /* define AC table */ + cinfo->arith_ac_K[index-NUM_ARITH_TBLS] = (UINT8) val; + } else { /* define DC table */ + cinfo->arith_dc_L[index] = (UINT8) (val & 0x0F); + cinfo->arith_dc_U[index] = (UINT8) (val >> 4); + if (cinfo->arith_dc_L[index] > cinfo->arith_dc_U[index]) + ERREXIT1(cinfo, JERR_DAC_VALUE, val); + } + } + + if (length != 0) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_SYNC(cinfo); + return TRUE; +} + +#else /* ! D_ARITH_CODING_SUPPORTED */ + +#define get_dac(cinfo) skip_variable(cinfo) + +#endif /* D_ARITH_CODING_SUPPORTED */ + + +LOCAL(boolean) +get_dht (j_decompress_ptr cinfo) +/* Process a DHT marker */ +{ + INT32 length; + UINT8 bits[17]; + UINT8 huffval[256]; + int i, index, count; + JHUFF_TBL **htblptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 16) { + INPUT_BYTE(cinfo, index, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DHT, index); + + bits[0] = 0; + count = 0; + for (i = 1; i <= 16; i++) { + INPUT_BYTE(cinfo, bits[i], return FALSE); + count += bits[i]; + } + + length -= 1 + 16; + + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[1], bits[2], bits[3], bits[4], + bits[5], bits[6], bits[7], bits[8]); + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[9], bits[10], bits[11], bits[12], + bits[13], bits[14], bits[15], bits[16]); + + /* Here we just do minimal validation of the counts to avoid walking + * off the end of our table space. jdhuff.c will check more carefully. + */ + if (count > 256 || ((INT32) count) > length) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + + for (i = 0; i < count; i++) + INPUT_BYTE(cinfo, huffval[i], return FALSE); + + length -= count; + + if (index & 0x10) { /* AC table definition */ + index -= 0x10; + htblptr = &cinfo->ac_huff_tbl_ptrs[index]; + } else { /* DC table definition */ + htblptr = &cinfo->dc_huff_tbl_ptrs[index]; + } + + if (index < 0 || index >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_DHT_INDEX, index); + + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + MEMCOPY((*htblptr)->huffval, huffval, SIZEOF((*htblptr)->huffval)); + } + + if (length != 0) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL(boolean) +get_dqt (j_decompress_ptr cinfo) +/* Process a DQT marker */ +{ + INT32 length; + int n, i, prec; + unsigned int tmp; + JQUANT_TBL *quant_ptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, n, return FALSE); + prec = n >> 4; + n &= 0x0F; + + TRACEMS2(cinfo, 1, JTRC_DQT, n, prec); + + if (n >= NUM_QUANT_TBLS) + ERREXIT1(cinfo, JERR_DQT_INDEX, n); + + if (cinfo->quant_tbl_ptrs[n] == NULL) + cinfo->quant_tbl_ptrs[n] = jpeg_alloc_quant_table((j_common_ptr) cinfo); + quant_ptr = cinfo->quant_tbl_ptrs[n]; + + for (i = 0; i < DCTSIZE2; i++) { + if (prec) + INPUT_2BYTES(cinfo, tmp, return FALSE); + else + INPUT_BYTE(cinfo, tmp, return FALSE); + /* We convert the zigzag-order table to natural array order. */ + quant_ptr->quantval[jpeg_natural_order[i]] = (UINT16) tmp; + } + + if (cinfo->err->trace_level >= 2) { + for (i = 0; i < DCTSIZE2; i += 8) { + TRACEMS8(cinfo, 2, JTRC_QUANTVALS, + quant_ptr->quantval[i], quant_ptr->quantval[i+1], + quant_ptr->quantval[i+2], quant_ptr->quantval[i+3], + quant_ptr->quantval[i+4], quant_ptr->quantval[i+5], + quant_ptr->quantval[i+6], quant_ptr->quantval[i+7]); + } + } + + length -= DCTSIZE2+1; + if (prec) length -= DCTSIZE2; + } + + if (length != 0) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL(boolean) +get_dri (j_decompress_ptr cinfo) +/* Process a DRI marker */ +{ + INT32 length; + unsigned int tmp; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + + if (length != 4) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_2BYTES(cinfo, tmp, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DRI, tmp); + + cinfo->restart_interval = tmp; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +/* + * Routines for processing APPn and COM markers. + * These are either saved in memory or discarded, per application request. + * APP0 and APP14 are specially checked to see if they are + * JFIF and Adobe markers, respectively. + */ + +#define APP0_DATA_LEN 14 /* Length of interesting data in APP0 */ +#define APP14_DATA_LEN 12 /* Length of interesting data in APP14 */ +#define APPN_DATA_LEN 14 /* Must be the largest of the above!! */ + + +LOCAL(void) +examine_app0 (j_decompress_ptr cinfo, JOCTET FAR * data, + unsigned int datalen, INT32 remaining) +/* Examine first few bytes from an APP0. + * Take appropriate action if it is a JFIF marker. + * datalen is # of bytes at data[], remaining is length of rest of marker data. + */ +{ + INT32 totallen = (INT32) datalen + remaining; + + if (datalen >= APP0_DATA_LEN && + GETJOCTET(data[0]) == 0x4A && + GETJOCTET(data[1]) == 0x46 && + GETJOCTET(data[2]) == 0x49 && + GETJOCTET(data[3]) == 0x46 && + GETJOCTET(data[4]) == 0) { + /* Found JFIF APP0 marker: save info */ + cinfo->saw_JFIF_marker = TRUE; + cinfo->JFIF_major_version = GETJOCTET(data[5]); + cinfo->JFIF_minor_version = GETJOCTET(data[6]); + cinfo->density_unit = GETJOCTET(data[7]); + cinfo->X_density = (GETJOCTET(data[8]) << 8) + GETJOCTET(data[9]); + cinfo->Y_density = (GETJOCTET(data[10]) << 8) + GETJOCTET(data[11]); + /* Check version. + * Major version must be 1, anything else signals an incompatible change. + * (We used to treat this as an error, but now it's a nonfatal warning, + * because some bozo at Hijaak couldn't read the spec.) + * Minor version should be 0..2, but process anyway if newer. + */ + if (cinfo->JFIF_major_version != 1) + WARNMS2(cinfo, JWRN_JFIF_MAJOR, + cinfo->JFIF_major_version, cinfo->JFIF_minor_version); + /* Generate trace messages */ + TRACEMS5(cinfo, 1, JTRC_JFIF, + cinfo->JFIF_major_version, cinfo->JFIF_minor_version, + cinfo->X_density, cinfo->Y_density, cinfo->density_unit); + /* Validate thumbnail dimensions and issue appropriate messages */ + if (GETJOCTET(data[12]) | GETJOCTET(data[13])) + TRACEMS2(cinfo, 1, JTRC_JFIF_THUMBNAIL, + GETJOCTET(data[12]), GETJOCTET(data[13])); + totallen -= APP0_DATA_LEN; + if (totallen != + ((INT32)GETJOCTET(data[12]) * (INT32)GETJOCTET(data[13]) * (INT32) 3)) + TRACEMS1(cinfo, 1, JTRC_JFIF_BADTHUMBNAILSIZE, (int) totallen); + } else if (datalen >= 6 && + GETJOCTET(data[0]) == 0x4A && + GETJOCTET(data[1]) == 0x46 && + GETJOCTET(data[2]) == 0x58 && + GETJOCTET(data[3]) == 0x58 && + GETJOCTET(data[4]) == 0) { + /* Found JFIF "JFXX" extension APP0 marker */ + /* The library doesn't actually do anything with these, + * but we try to produce a helpful trace message. + */ + switch (GETJOCTET(data[5])) { + case 0x10: + TRACEMS1(cinfo, 1, JTRC_THUMB_JPEG, (int) totallen); + break; + case 0x11: + TRACEMS1(cinfo, 1, JTRC_THUMB_PALETTE, (int) totallen); + break; + case 0x13: + TRACEMS1(cinfo, 1, JTRC_THUMB_RGB, (int) totallen); + break; + default: + TRACEMS2(cinfo, 1, JTRC_JFIF_EXTENSION, + GETJOCTET(data[5]), (int) totallen); + break; + } + } else { + /* Start of APP0 does not match "JFIF" or "JFXX", or too short */ + TRACEMS1(cinfo, 1, JTRC_APP0, (int) totallen); + } +} + + +LOCAL(void) +examine_app14 (j_decompress_ptr cinfo, JOCTET FAR * data, + unsigned int datalen, INT32 remaining) +/* Examine first few bytes from an APP14. + * Take appropriate action if it is an Adobe marker. + * datalen is # of bytes at data[], remaining is length of rest of marker data. + */ +{ + unsigned int version, flags0, flags1, transform; + + if (datalen >= APP14_DATA_LEN && + GETJOCTET(data[0]) == 0x41 && + GETJOCTET(data[1]) == 0x64 && + GETJOCTET(data[2]) == 0x6F && + GETJOCTET(data[3]) == 0x62 && + GETJOCTET(data[4]) == 0x65) { + /* Found Adobe APP14 marker */ + version = (GETJOCTET(data[5]) << 8) + GETJOCTET(data[6]); + flags0 = (GETJOCTET(data[7]) << 8) + GETJOCTET(data[8]); + flags1 = (GETJOCTET(data[9]) << 8) + GETJOCTET(data[10]); + transform = GETJOCTET(data[11]); + TRACEMS4(cinfo, 1, JTRC_ADOBE, version, flags0, flags1, transform); + cinfo->saw_Adobe_marker = TRUE; + cinfo->Adobe_transform = (UINT8) transform; + } else { + /* Start of APP14 does not match "Adobe", or too short */ + TRACEMS1(cinfo, 1, JTRC_APP14, (int) (datalen + remaining)); + } +} + + +METHODDEF(boolean) +get_interesting_appn (j_decompress_ptr cinfo) +/* Process an APP0 or APP14 marker without saving it */ +{ + INT32 length; + JOCTET b[APPN_DATA_LEN]; + unsigned int i, numtoread; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + /* get the interesting part of the marker data */ + if (length >= APPN_DATA_LEN) + numtoread = APPN_DATA_LEN; + else if (length > 0) + numtoread = (unsigned int) length; + else + numtoread = 0; + for (i = 0; i < numtoread; i++) + INPUT_BYTE(cinfo, b[i], return FALSE); + length -= numtoread; + + /* process it */ + switch (cinfo->unread_marker) { + case M_APP0: + examine_app0(cinfo, (JOCTET FAR *) b, numtoread, length); + break; + case M_APP14: + examine_app14(cinfo, (JOCTET FAR *) b, numtoread, length); + break; + default: + /* can't get here unless jpeg_save_markers chooses wrong processor */ + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker); + break; + } + + /* skip any remaining data -- could be lots */ + INPUT_SYNC(cinfo); + if (length > 0) + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + + +#ifdef SAVE_MARKERS_SUPPORTED + +METHODDEF(boolean) +save_marker (j_decompress_ptr cinfo) +/* Save an APPn or COM marker into the marker list */ +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + jpeg_saved_marker_ptr cur_marker = marker->cur_marker; + unsigned int bytes_read, data_length; + JOCTET FAR * data; + INT32 length = 0; + INPUT_VARS(cinfo); + + if (cur_marker == NULL) { + /* begin reading a marker */ + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + if (length >= 0) { /* watch out for bogus length word */ + /* figure out how much we want to save */ + unsigned int limit; + if (cinfo->unread_marker == (int) M_COM) + limit = marker->length_limit_COM; + else + limit = marker->length_limit_APPn[cinfo->unread_marker - (int) M_APP0]; + if ((unsigned int) length < limit) + limit = (unsigned int) length; + /* allocate and initialize the marker item */ + cur_marker = (jpeg_saved_marker_ptr) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(struct jpeg_marker_struct) + limit); + cur_marker->next = NULL; + cur_marker->marker = (UINT8) cinfo->unread_marker; + cur_marker->original_length = (unsigned int) length; + cur_marker->data_length = limit; + /* data area is just beyond the jpeg_marker_struct */ + data = cur_marker->data = (JOCTET FAR *) (cur_marker + 1); + marker->cur_marker = cur_marker; + marker->bytes_read = 0; + bytes_read = 0; + data_length = limit; + } else { + /* deal with bogus length word */ + bytes_read = data_length = 0; + data = NULL; + } + } else { + /* resume reading a marker */ + bytes_read = marker->bytes_read; + data_length = cur_marker->data_length; + data = cur_marker->data + bytes_read; + } + + while (bytes_read < data_length) { + INPUT_SYNC(cinfo); /* move the restart point to here */ + marker->bytes_read = bytes_read; + /* If there's not at least one byte in buffer, suspend */ + MAKE_BYTE_AVAIL(cinfo, return FALSE); + /* Copy bytes with reasonable rapidity */ + while (bytes_read < data_length && bytes_in_buffer > 0) { + *data++ = *next_input_byte++; + bytes_in_buffer--; + bytes_read++; + } + } + + /* Done reading what we want to read */ + if (cur_marker != NULL) { /* will be NULL if bogus length word */ + /* Add new marker to end of list */ + if (cinfo->marker_list == NULL) { + cinfo->marker_list = cur_marker; + } else { + jpeg_saved_marker_ptr prev = cinfo->marker_list; + while (prev->next != NULL) + prev = prev->next; + prev->next = cur_marker; + } + /* Reset pointer & calc remaining data length */ + data = cur_marker->data; + length = cur_marker->original_length - data_length; + } + /* Reset to initial state for next marker */ + marker->cur_marker = NULL; + + /* Process the marker if interesting; else just make a generic trace msg */ + switch (cinfo->unread_marker) { + case M_APP0: + examine_app0(cinfo, data, data_length, length); + break; + case M_APP14: + examine_app14(cinfo, data, data_length, length); + break; + default: + TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, + (int) (data_length + length)); + break; + } + + /* skip any remaining data -- could be lots */ + INPUT_SYNC(cinfo); /* do before skip_input_data */ + if (length > 0) + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + +#endif /* SAVE_MARKERS_SUPPORTED */ + + +METHODDEF(boolean) +skip_variable (j_decompress_ptr cinfo) +/* Skip over an unknown or uninteresting variable-length marker */ +{ + INT32 length; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, (int) length); + + INPUT_SYNC(cinfo); /* do before skip_input_data */ + if (length > 0) + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + + +/* + * Find the next JPEG marker, save it in cinfo->unread_marker. + * Returns FALSE if had to suspend before reaching a marker; + * in that case cinfo->unread_marker is unchanged. + * + * Note that the result might not be a valid marker code, + * but it will never be 0 or FF. + */ + +LOCAL(boolean) +next_marker (j_decompress_ptr cinfo) +{ + int c; + INPUT_VARS(cinfo); + + for (;;) { + INPUT_BYTE(cinfo, c, return FALSE); + /* Skip any non-FF bytes. + * This may look a bit inefficient, but it will not occur in a valid file. + * We sync after each discarded byte so that a suspending data source + * can discard the byte from its buffer. + */ + while (c != 0xFF) { + cinfo->marker->discarded_bytes++; + INPUT_SYNC(cinfo); + INPUT_BYTE(cinfo, c, return FALSE); + } + /* This loop swallows any duplicate FF bytes. Extra FFs are legal as + * pad bytes, so don't count them in discarded_bytes. We assume there + * will not be so many consecutive FF bytes as to overflow a suspending + * data source's input buffer. + */ + do { + INPUT_BYTE(cinfo, c, return FALSE); + } while (c == 0xFF); + if (c != 0) + break; /* found a valid marker, exit loop */ + /* Reach here if we found a stuffed-zero data sequence (FF/00). + * Discard it and loop back to try again. + */ + cinfo->marker->discarded_bytes += 2; + INPUT_SYNC(cinfo); + } + + if (cinfo->marker->discarded_bytes != 0) { + WARNMS2(cinfo, JWRN_EXTRANEOUS_DATA, cinfo->marker->discarded_bytes, c); + cinfo->marker->discarded_bytes = 0; + } + + cinfo->unread_marker = c; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL(boolean) +first_marker (j_decompress_ptr cinfo) +/* Like next_marker, but used to obtain the initial SOI marker. */ +/* For this marker, we do not allow preceding garbage or fill; otherwise, + * we might well scan an entire input file before realizing it ain't JPEG. + * If an application wants to process non-JFIF files, it must seek to the + * SOI before calling the JPEG library. + */ +{ + int c, c2; + INPUT_VARS(cinfo); + + INPUT_BYTE(cinfo, c, return FALSE); + INPUT_BYTE(cinfo, c2, return FALSE); + if (c != 0xFF || c2 != (int) M_SOI) + ERREXIT2(cinfo, JERR_NO_SOI, c, c2); + + cinfo->unread_marker = c2; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +/* + * Read markers until SOS or EOI. + * + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + +METHODDEF(int) +read_markers (j_decompress_ptr cinfo) +{ + /* Outer loop repeats once for each marker. */ + for (;;) { + /* Collect the marker proper, unless we already did. */ + /* NB: first_marker() enforces the requirement that SOI appear first. */ + if (cinfo->unread_marker == 0) { + if (! cinfo->marker->saw_SOI) { + if (! first_marker(cinfo)) + return JPEG_SUSPENDED; + } else { + if (! next_marker(cinfo)) + return JPEG_SUSPENDED; + } + } + /* At this point cinfo->unread_marker contains the marker code and the + * input point is just past the marker proper, but before any parameters. + * A suspension will cause us to return with this state still true. + */ + switch (cinfo->unread_marker) { + case M_SOI: + if (! get_soi(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_SOF0: /* Baseline */ + case M_SOF1: /* Extended sequential, Huffman */ + if (! get_sof(cinfo, FALSE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF2: /* Progressive, Huffman */ + if (! get_sof(cinfo, TRUE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF9: /* Extended sequential, arithmetic */ + if (! get_sof(cinfo, FALSE, TRUE)) + return JPEG_SUSPENDED; + break; + + case M_SOF10: /* Progressive, arithmetic */ + if (! get_sof(cinfo, TRUE, TRUE)) + return JPEG_SUSPENDED; + break; + + /* Currently unsupported SOFn types */ + case M_SOF3: /* Lossless, Huffman */ + case M_SOF5: /* Differential sequential, Huffman */ + case M_SOF6: /* Differential progressive, Huffman */ + case M_SOF7: /* Differential lossless, Huffman */ + case M_JPG: /* Reserved for JPEG extensions */ + case M_SOF11: /* Lossless, arithmetic */ + case M_SOF13: /* Differential sequential, arithmetic */ + case M_SOF14: /* Differential progressive, arithmetic */ + case M_SOF15: /* Differential lossless, arithmetic */ + ERREXIT1(cinfo, JERR_SOF_UNSUPPORTED, cinfo->unread_marker); + break; + + case M_SOS: + if (! get_sos(cinfo)) + return JPEG_SUSPENDED; + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_SOS; + + case M_EOI: + TRACEMS(cinfo, 1, JTRC_EOI); + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_EOI; + + case M_DAC: + if (! get_dac(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DHT: + if (! get_dht(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DQT: + if (! get_dqt(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DRI: + if (! get_dri(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_APP0: + case M_APP1: + case M_APP2: + case M_APP3: + case M_APP4: + case M_APP5: + case M_APP6: + case M_APP7: + case M_APP8: + case M_APP9: + case M_APP10: + case M_APP11: + case M_APP12: + case M_APP13: + case M_APP14: + case M_APP15: + if (! (*((my_marker_ptr) cinfo->marker)->process_APPn[ + cinfo->unread_marker - (int) M_APP0]) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_COM: + if (! (*((my_marker_ptr) cinfo->marker)->process_COM) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_RST0: /* these are all parameterless */ + case M_RST1: + case M_RST2: + case M_RST3: + case M_RST4: + case M_RST5: + case M_RST6: + case M_RST7: + case M_TEM: + TRACEMS1(cinfo, 1, JTRC_PARMLESS_MARKER, cinfo->unread_marker); + break; + + case M_DNL: /* Ignore DNL ... perhaps the wrong thing */ + if (! skip_variable(cinfo)) + return JPEG_SUSPENDED; + break; + + default: /* must be DHP, EXP, JPGn, or RESn */ + /* For now, we treat the reserved markers as fatal errors since they are + * likely to be used to signal incompatible JPEG Part 3 extensions. + * Once the JPEG 3 version-number marker is well defined, this code + * ought to change! + */ + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker); + break; + } + /* Successfully processed marker, so reset state variable */ + cinfo->unread_marker = 0; + } /* end loop */ +} + + +/* + * Read a restart marker, which is expected to appear next in the datastream; + * if the marker is not there, take appropriate recovery action. + * Returns FALSE if suspension is required. + * + * This is called by the entropy decoder after it has read an appropriate + * number of MCUs. cinfo->unread_marker may be nonzero if the entropy decoder + * has already read a marker from the data source. Under normal conditions + * cinfo->unread_marker will be reset to 0 before returning; if not reset, + * it holds a marker which the decoder will be unable to read past. + */ + +METHODDEF(boolean) +read_restart_marker (j_decompress_ptr cinfo) +{ + /* Obtain a marker unless we already did. */ + /* Note that next_marker will complain if it skips any data. */ + if (cinfo->unread_marker == 0) { + if (! next_marker(cinfo)) + return FALSE; + } + + if (cinfo->unread_marker == + ((int) M_RST0 + cinfo->marker->next_restart_num)) { + /* Normal case --- swallow the marker and let entropy decoder continue */ + TRACEMS1(cinfo, 3, JTRC_RST, cinfo->marker->next_restart_num); + cinfo->unread_marker = 0; + } else { + /* Uh-oh, the restart markers have been messed up. */ + /* Let the data source manager determine how to resync. */ + if (! (*cinfo->src->resync_to_restart) (cinfo, + cinfo->marker->next_restart_num)) + return FALSE; + } + + /* Update next-restart state */ + cinfo->marker->next_restart_num = (cinfo->marker->next_restart_num + 1) & 7; + + return TRUE; +} + + +/* + * This is the default resync_to_restart method for data source managers + * to use if they don't have any better approach. Some data source managers + * may be able to back up, or may have additional knowledge about the data + * which permits a more intelligent recovery strategy; such managers would + * presumably supply their own resync method. + * + * read_restart_marker calls resync_to_restart if it finds a marker other than + * the restart marker it was expecting. (This code is *not* used unless + * a nonzero restart interval has been declared.) cinfo->unread_marker is + * the marker code actually found (might be anything, except 0 or FF). + * The desired restart marker number (0..7) is passed as a parameter. + * This routine is supposed to apply whatever error recovery strategy seems + * appropriate in order to position the input stream to the next data segment. + * Note that cinfo->unread_marker is treated as a marker appearing before + * the current data-source input point; usually it should be reset to zero + * before returning. + * Returns FALSE if suspension is required. + * + * This implementation is substantially constrained by wanting to treat the + * input as a data stream; this means we can't back up. Therefore, we have + * only the following actions to work with: + * 1. Simply discard the marker and let the entropy decoder resume at next + * byte of file. + * 2. Read forward until we find another marker, discarding intervening + * data. (In theory we could look ahead within the current bufferload, + * without having to discard data if we don't find the desired marker. + * This idea is not implemented here, in part because it makes behavior + * dependent on buffer size and chance buffer-boundary positions.) + * 3. Leave the marker unread (by failing to zero cinfo->unread_marker). + * This will cause the entropy decoder to process an empty data segment, + * inserting dummy zeroes, and then we will reprocess the marker. + * + * #2 is appropriate if we think the desired marker lies ahead, while #3 is + * appropriate if the found marker is a future restart marker (indicating + * that we have missed the desired restart marker, probably because it got + * corrupted). + * We apply #2 or #3 if the found marker is a restart marker no more than + * two counts behind or ahead of the expected one. We also apply #2 if the + * found marker is not a legal JPEG marker code (it's certainly bogus data). + * If the found marker is a restart marker more than 2 counts away, we do #1 + * (too much risk that the marker is erroneous; with luck we will be able to + * resync at some future point). + * For any valid non-restart JPEG marker, we apply #3. This keeps us from + * overrunning the end of a scan. An implementation limited to single-scan + * files might find it better to apply #2 for markers other than EOI, since + * any other marker would have to be bogus data in that case. + */ + +GLOBAL(boolean) +jpeg_resync_to_restart (j_decompress_ptr cinfo, int desired) +{ + int marker = cinfo->unread_marker; + int action = 1; + + /* Always put up a warning. */ + WARNMS2(cinfo, JWRN_MUST_RESYNC, marker, desired); + + /* Outer loop handles repeated decision after scanning forward. */ + for (;;) { + if (marker < (int) M_SOF0) + action = 2; /* invalid marker */ + else if (marker < (int) M_RST0 || marker > (int) M_RST7) + action = 3; /* valid non-restart marker */ + else { + if (marker == ((int) M_RST0 + ((desired+1) & 7)) || + marker == ((int) M_RST0 + ((desired+2) & 7))) + action = 3; /* one of the next two expected restarts */ + else if (marker == ((int) M_RST0 + ((desired-1) & 7)) || + marker == ((int) M_RST0 + ((desired-2) & 7))) + action = 2; /* a prior restart, so advance */ + else + action = 1; /* desired restart or too far away */ + } + TRACEMS2(cinfo, 4, JTRC_RECOVERY_ACTION, marker, action); + switch (action) { + case 1: + /* Discard marker and let entropy decoder resume processing. */ + cinfo->unread_marker = 0; + return TRUE; + case 2: + /* Scan to the next marker, and repeat the decision loop. */ + if (! next_marker(cinfo)) + return FALSE; + marker = cinfo->unread_marker; + break; + case 3: + /* Return without advancing past this marker. */ + /* Entropy decoder will be forced to process an empty segment. */ + return TRUE; + } + } /* end loop */ +} + + +/* + * Reset marker processing state to begin a fresh datastream. + */ + +METHODDEF(void) +reset_marker_reader (j_decompress_ptr cinfo) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + + cinfo->comp_info = NULL; /* until allocated by get_sof */ + cinfo->input_scan_number = 0; /* no SOS seen yet */ + cinfo->unread_marker = 0; /* no pending marker */ + marker->pub.saw_SOI = FALSE; /* set internal state too */ + marker->pub.saw_SOF = FALSE; + marker->pub.discarded_bytes = 0; + marker->cur_marker = NULL; +} + + +/* + * Initialize the marker reader module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL(void) +jinit_marker_reader (j_decompress_ptr cinfo) +{ + my_marker_ptr marker; + int i; + + /* Create subobject in permanent pool */ + marker = (my_marker_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_marker_reader)); + cinfo->marker = (struct jpeg_marker_reader *) marker; + /* Initialize public method pointers */ + marker->pub.reset_marker_reader = reset_marker_reader; + marker->pub.read_markers = read_markers; + marker->pub.read_restart_marker = read_restart_marker; + /* Initialize COM/APPn processing. + * By default, we examine and then discard APP0 and APP14, + * but simply discard COM and all other APPn. + */ + marker->process_COM = skip_variable; + marker->length_limit_COM = 0; + for (i = 0; i < 16; i++) { + marker->process_APPn[i] = skip_variable; + marker->length_limit_APPn[i] = 0; + } + marker->process_APPn[0] = get_interesting_appn; + marker->process_APPn[14] = get_interesting_appn; + /* Reset marker processing state */ + reset_marker_reader(cinfo); +} + + +/* + * Control saving of COM and APPn markers into marker_list. + */ + +#ifdef SAVE_MARKERS_SUPPORTED + +GLOBAL(void) +jpeg_save_markers (j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + long maxlength; + jpeg_marker_parser_method processor; + + /* Length limit mustn't be larger than what we can allocate + * (should only be a concern in a 16-bit environment). + */ + maxlength = cinfo->mem->max_alloc_chunk - SIZEOF(struct jpeg_marker_struct); + if (((long) length_limit) > maxlength) + length_limit = (unsigned int) maxlength; + + /* Choose processor routine to use. + * APP0/APP14 have special requirements. + */ + if (length_limit) { + processor = save_marker; + /* If saving APP0/APP14, save at least enough for our internal use. */ + if (marker_code == (int) M_APP0 && length_limit < APP0_DATA_LEN) + length_limit = APP0_DATA_LEN; + else if (marker_code == (int) M_APP14 && length_limit < APP14_DATA_LEN) + length_limit = APP14_DATA_LEN; + } else { + processor = skip_variable; + /* If discarding APP0/APP14, use our regular on-the-fly processor. */ + if (marker_code == (int) M_APP0 || marker_code == (int) M_APP14) + processor = get_interesting_appn; + } + + if (marker_code == (int) M_COM) { + marker->process_COM = processor; + marker->length_limit_COM = length_limit; + } else if (marker_code >= (int) M_APP0 && marker_code <= (int) M_APP15) { + marker->process_APPn[marker_code - (int) M_APP0] = processor; + marker->length_limit_APPn[marker_code - (int) M_APP0] = length_limit; + } else + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code); +} + +#endif /* SAVE_MARKERS_SUPPORTED */ + + +/* + * Install a special processing method for COM or APPn markers. + */ + +GLOBAL(void) +jpeg_set_marker_processor (j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + + if (marker_code == (int) M_COM) + marker->process_COM = routine; + else if (marker_code >= (int) M_APP0 && marker_code <= (int) M_APP15) + marker->process_APPn[marker_code - (int) M_APP0] = routine; + else + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code); +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdmaster.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdmaster.c new file mode 100644 index 000000000..2802c5b7b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdmaster.c @@ -0,0 +1,557 @@ +/* + * jdmaster.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains master control logic for the JPEG decompressor. + * These routines are concerned with selecting the modules to be executed + * and with determining the number of passes and the work to be done in each + * pass. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef struct { + struct jpeg_decomp_master pub; /* public fields */ + + int pass_number; /* # of passes completed */ + + boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ + + /* Saved references to initialized quantizer modules, + * in case we need to switch modes. + */ + struct jpeg_color_quantizer * quantizer_1pass; + struct jpeg_color_quantizer * quantizer_2pass; +} my_decomp_master; + +typedef my_decomp_master * my_master_ptr; + + +/* + * Determine whether merged upsample/color conversion should be used. + * CRUCIAL: this must match the actual capabilities of jdmerge.c! + */ + +LOCAL(boolean) +use_merged_upsample (j_decompress_ptr cinfo) +{ +#ifdef UPSAMPLE_MERGING_SUPPORTED + /* Merging is the equivalent of plain box-filter upsampling */ + if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling) + return FALSE; + /* jdmerge.c only supports YCC=>RGB color conversion */ + if (cinfo->jpeg_color_space != JCS_YCbCr || cinfo->num_components != 3 || + cinfo->out_color_space != JCS_RGB || + cinfo->out_color_components != RGB_PIXELSIZE) + return FALSE; + /* and it only handles 2h1v or 2h2v sampling ratios */ + if (cinfo->comp_info[0].h_samp_factor != 2 || + cinfo->comp_info[1].h_samp_factor != 1 || + cinfo->comp_info[2].h_samp_factor != 1 || + cinfo->comp_info[0].v_samp_factor > 2 || + cinfo->comp_info[1].v_samp_factor != 1 || + cinfo->comp_info[2].v_samp_factor != 1) + return FALSE; + /* furthermore, it doesn't work if we've scaled the IDCTs differently */ + if (cinfo->comp_info[0].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[1].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[2].DCT_scaled_size != cinfo->min_DCT_scaled_size) + return FALSE; + /* ??? also need to test for upsample-time rescaling, when & if supported */ + return TRUE; /* by golly, it'll work... */ +#else + return FALSE; +#endif +} + + +/* + * Compute output image dimensions and related values. + * NOTE: this is exported for possible use by application. + * Hence it mustn't do anything that can't be done twice. + * Also note that it may be called before the master module is initialized! + */ + +GLOBAL(void) +jpeg_calc_output_dimensions (j_decompress_ptr cinfo) +/* Do computations that are needed before master selection phase */ +{ +#ifdef IDCT_SCALING_SUPPORTED + int ci; + jpeg_component_info *compptr; +#endif + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_READY) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + +#ifdef IDCT_SCALING_SUPPORTED + + /* Compute actual output image dimensions and DCT scaling choices. */ + if (cinfo->scale_num * 8 <= cinfo->scale_denom) { + /* Provide 1/8 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 8L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 8L); + cinfo->min_DCT_scaled_size = 1; + } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) { + /* Provide 1/4 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 4L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 4L); + cinfo->min_DCT_scaled_size = 2; + } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) { + /* Provide 1/2 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 2L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 2L); + cinfo->min_DCT_scaled_size = 4; + } else { + /* Provide 1/1 scaling */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + cinfo->min_DCT_scaled_size = DCTSIZE; + } + /* In selecting the actual DCT scaling for each component, we try to + * scale up the chroma components via IDCT scaling rather than upsampling. + * This saves time if the upsampler gets to use 1:1 scaling. + * Note this code assumes that the supported DCT scalings are powers of 2. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + int ssize = cinfo->min_DCT_scaled_size; + while (ssize < DCTSIZE && + (compptr->h_samp_factor * ssize * 2 <= + cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size) && + (compptr->v_samp_factor * ssize * 2 <= + cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size)) { + ssize = ssize * 2; + } + compptr->DCT_scaled_size = ssize; + } + + /* Recompute downsampled dimensions of components; + * application needs to know these if using raw downsampled data. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Size in samples, after IDCT scaling */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * + (long) (compptr->h_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * + (long) (compptr->v_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + } + +#else /* !IDCT_SCALING_SUPPORTED */ + + /* Hardwire it to "no scaling" */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, + * and has computed unscaled downsampled_width and downsampled_height. + */ + +#endif /* IDCT_SCALING_SUPPORTED */ + + /* Report number of components in selected colorspace. */ + /* Probably this should be in the color conversion module... */ + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + break; + case JCS_RGB: +#if RGB_PIXELSIZE != 3 + cinfo->out_color_components = RGB_PIXELSIZE; + break; +#endif /* else share code with YCbCr */ + case JCS_YCbCr: + cinfo->out_color_components = 3; + break; + case JCS_CMYK: + case JCS_YCCK: + cinfo->out_color_components = 4; + break; + default: /* else must be same colorspace as in file */ + cinfo->out_color_components = cinfo->num_components; + break; + } + cinfo->output_components = (cinfo->quantize_colors ? 1 : + cinfo->out_color_components); + + /* See if upsampler will want to emit more than one row at a time */ + if (use_merged_upsample(cinfo)) + cinfo->rec_outbuf_height = cinfo->max_v_samp_factor; + else + cinfo->rec_outbuf_height = 1; +} + + +/* + * Several decompression processes need to range-limit values to the range + * 0..MAXJSAMPLE; the input value may fall somewhat outside this range + * due to noise introduced by quantization, roundoff error, etc. These + * processes are inner loops and need to be as fast as possible. On most + * machines, particularly CPUs with pipelines or instruction prefetch, + * a (subscript-check-less) C table lookup + * x = sample_range_limit[x]; + * is faster than explicit tests + * if (x < 0) x = 0; + * else if (x > MAXJSAMPLE) x = MAXJSAMPLE; + * These processes all use a common table prepared by the routine below. + * + * For most steps we can mathematically guarantee that the initial value + * of x is within MAXJSAMPLE+1 of the legal range, so a table running from + * -(MAXJSAMPLE+1) to 2*MAXJSAMPLE+1 is sufficient. But for the initial + * limiting step (just after the IDCT), a wildly out-of-range value is + * possible if the input data is corrupt. To avoid any chance of indexing + * off the end of memory and getting a bad-pointer trap, we perform the + * post-IDCT limiting thus: + * x = range_limit[x & MASK]; + * where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit + * samples. Under normal circumstances this is more than enough range and + * a correct output will be generated; with bogus input data the mask will + * cause wraparound, and we will safely generate a bogus-but-in-range output. + * For the post-IDCT step, we want to convert the data from signed to unsigned + * representation by adding CENTERJSAMPLE at the same time that we limit it. + * So the post-IDCT limiting table ends up looking like this: + * CENTERJSAMPLE,CENTERJSAMPLE+1,...,MAXJSAMPLE, + * MAXJSAMPLE (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0 (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0,1,...,CENTERJSAMPLE-1 + * Negative inputs select values from the upper half of the table after + * masking. + * + * We can save some space by overlapping the start of the post-IDCT table + * with the simpler range limiting table. The post-IDCT table begins at + * sample_range_limit + CENTERJSAMPLE. + * + * Note that the table is allocated in near data space on PCs; it's small + * enough and used often enough to justify this. + */ + +LOCAL(void) +prepare_range_limit_table (j_decompress_ptr cinfo) +/* Allocate and fill in the sample_range_limit table */ +{ + JSAMPLE * table; + int i; + + table = (JSAMPLE *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + table += (MAXJSAMPLE+1); /* allow negative subscripts of simple table */ + cinfo->sample_range_limit = table; + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + MEMZERO(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * SIZEOF(JSAMPLE)); + /* Main part of "simple" table: limit[x] = x */ + for (i = 0; i <= MAXJSAMPLE; i++) + table[i] = (JSAMPLE) i; + table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */ + /* End of simple table, rest of first half of post-IDCT table */ + for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++) + table[i] = MAXJSAMPLE; + /* Second half of post-IDCT table */ + MEMZERO(table + (2 * (MAXJSAMPLE+1)), + (2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + MEMCOPY(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE), + cinfo->sample_range_limit, CENTERJSAMPLE * SIZEOF(JSAMPLE)); +} + + +/* + * Master selection of decompression modules. + * This is done once at jpeg_start_decompress time. We determine + * which modules will be used and give them appropriate initialization calls. + * We also initialize the decompressor input side to begin consuming data. + * + * Since jpeg_read_header has finished, we know what is in the SOF + * and (first) SOS markers. We also have all the application parameter + * settings. + */ + +LOCAL(void) +master_selection (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + boolean use_c_buffer; + long samplesperrow; + JDIMENSION jd_samplesperrow; + + /* Initialize dimensions and other stuff */ + jpeg_calc_output_dimensions(cinfo); + prepare_range_limit_table(cinfo); + + /* Width of an output scanline must be representable as JDIMENSION. */ + samplesperrow = (long) cinfo->output_width * (long) cinfo->out_color_components; + jd_samplesperrow = (JDIMENSION) samplesperrow; + if ((long) jd_samplesperrow != samplesperrow) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* Initialize my private state */ + master->pass_number = 0; + master->using_merged_upsample = use_merged_upsample(cinfo); + + /* Color quantizer selection */ + master->quantizer_1pass = NULL; + master->quantizer_2pass = NULL; + /* No mode changes if not using buffered-image mode. */ + if (! cinfo->quantize_colors || ! cinfo->buffered_image) { + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + } + if (cinfo->quantize_colors) { + if (cinfo->raw_data_out) + ERREXIT(cinfo, JERR_NOTIMPL); + /* 2-pass quantizer only works in 3-component color space. */ + if (cinfo->out_color_components != 3) { + cinfo->enable_1pass_quant = TRUE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + cinfo->colormap = NULL; + } else if (cinfo->colormap != NULL) { + cinfo->enable_external_quant = TRUE; + } else if (cinfo->two_pass_quantize) { + cinfo->enable_2pass_quant = TRUE; + } else { + cinfo->enable_1pass_quant = TRUE; + } + + if (cinfo->enable_1pass_quant) { +#ifdef QUANT_1PASS_SUPPORTED + jinit_1pass_quantizer(cinfo); + master->quantizer_1pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + + /* We use the 2-pass code to map to external colormaps. */ + if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) { +#ifdef QUANT_2PASS_SUPPORTED + jinit_2pass_quantizer(cinfo); + master->quantizer_2pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + /* If both quantizers are initialized, the 2-pass one is left active; + * this is necessary for starting with quantization to an external map. + */ + } + + /* Post-processing: in particular, color conversion first */ + if (! cinfo->raw_data_out) { + if (master->using_merged_upsample) { +#ifdef UPSAMPLE_MERGING_SUPPORTED + jinit_merged_upsampler(cinfo); /* does color conversion too */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + jinit_color_deconverter(cinfo); + jinit_upsampler(cinfo); + } + jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); + } + /* Inverse DCT */ + jinit_inverse_dct(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; + jinit_d_coef_controller(cinfo, use_c_buffer); + + if (! cinfo->raw_data_out) + jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* If jpeg_start_decompress will read the whole file, initialize + * progress monitoring appropriately. The input step is counted + * as one pass. + */ + if (cinfo->progress != NULL && ! cinfo->buffered_image && + cinfo->inputctl->has_multiple_scans) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = (cinfo->enable_2pass_quant ? 3 : 2); + /* Count the input pass as done */ + master->pass_number++; + } +#endif /* D_MULTISCAN_FILES_SUPPORTED */ +} + + +/* + * Per-pass setup. + * This is called at the beginning of each output pass. We determine which + * modules will be active during this pass and give them appropriate + * start_pass calls. We also set is_dummy_pass to indicate whether this + * is a "real" output pass or a dummy pass for color quantization. + * (In the latter case, jdapistd.c will crank the pass to completion.) + */ + +METHODDEF(void) +prepare_for_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + if (master->pub.is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Final pass of 2-pass quantization */ + master->pub.is_dummy_pass = FALSE; + (*cinfo->cquantize->start_pass) (cinfo, FALSE); + (*cinfo->post->start_pass) (cinfo, JBUF_CRANK_DEST); + (*cinfo->main->start_pass) (cinfo, JBUF_CRANK_DEST); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + if (cinfo->quantize_colors && cinfo->colormap == NULL) { + /* Select new quantization method */ + if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) { + cinfo->cquantize = master->quantizer_2pass; + master->pub.is_dummy_pass = TRUE; + } else if (cinfo->enable_1pass_quant) { + cinfo->cquantize = master->quantizer_1pass; + } else { + ERREXIT(cinfo, JERR_MODE_CHANGE); + } + } + (*cinfo->idct->start_pass) (cinfo); + (*cinfo->coef->start_output_pass) (cinfo); + if (! cinfo->raw_data_out) { + if (! master->using_merged_upsample) + (*cinfo->cconvert->start_pass) (cinfo); + (*cinfo->upsample->start_pass) (cinfo); + if (cinfo->quantize_colors) + (*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass); + (*cinfo->post->start_pass) (cinfo, + (master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + } + } + + /* Set up progress monitor's pass info if present */ + if (cinfo->progress != NULL) { + cinfo->progress->completed_passes = master->pass_number; + cinfo->progress->total_passes = master->pass_number + + (master->pub.is_dummy_pass ? 2 : 1); + /* In buffered-image mode, we assume one more output pass if EOI not + * yet reached, but no more passes if EOI has been reached. + */ + if (cinfo->buffered_image && ! cinfo->inputctl->eoi_reached) { + cinfo->progress->total_passes += (cinfo->enable_2pass_quant ? 2 : 1); + } + } +} + + +/* + * Finish up at end of an output pass. + */ + +METHODDEF(void) +finish_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + if (cinfo->quantize_colors) + (*cinfo->cquantize->finish_pass) (cinfo); + master->pass_number++; +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Switch to a new external colormap between output passes. + */ + +GLOBAL(void) +jpeg_new_colormap (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_BUFIMAGE) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->quantize_colors && cinfo->enable_external_quant && + cinfo->colormap != NULL) { + /* Select 2-pass quantizer for external colormap use */ + cinfo->cquantize = master->quantizer_2pass; + /* Notify quantizer of colormap change */ + (*cinfo->cquantize->new_color_map) (cinfo); + master->pub.is_dummy_pass = FALSE; /* just in case */ + } else + ERREXIT(cinfo, JERR_MODE_CHANGE); +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +/* + * Initialize master decompression control and select active modules. + * This is performed at the start of jpeg_start_decompress. + */ + +GLOBAL(void) +jinit_master_decompress (j_decompress_ptr cinfo) +{ + my_master_ptr master; + + master = (my_master_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_decomp_master)); + cinfo->master = (struct jpeg_decomp_master *) master; + master->pub.prepare_for_output_pass = prepare_for_output_pass; + master->pub.finish_output_pass = finish_output_pass; + + master->pub.is_dummy_pass = FALSE; + + master_selection(cinfo); +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdmerge.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdmerge.c new file mode 100644 index 000000000..3e2ab0e2d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdmerge.c @@ -0,0 +1,402 @@ +/* + * jdmerge.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains code for merged upsampling/color conversion. + * + * This file combines functions from jdsample.c and jdcolor.c; + * read those files first to understand what's going on. + * + * When the chroma components are to be upsampled by simple replication + * (ie, box filtering), we can save some work in color conversion by + * calculating all the output pixels corresponding to a pair of chroma + * samples at one time. In the conversion equations + * R = Y + K1 * Cr + * G = Y + K2 * Cb + K3 * Cr + * B = Y + K4 * Cb + * only the Y term varies among the group of pixels corresponding to a pair + * of chroma samples, so the rest of the terms can be calculated just once. + * At typical sampling ratios, this eliminates half or three-quarters of the + * multiplications needed for color conversion. + * + * This file currently provides implementations for the following cases: + * YCbCr => RGB color conversion only. + * Sampling ratios of 2h1v or 2h2v. + * No scaling needed at upsample time. + * Corner-aligned (non-CCIR601) sampling alignment. + * Other special cases could be added, but in most applications these are + * the only common cases. (For uncommon cases we fall back on the more + * general code in jdsample.c and jdcolor.c.) + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#include "cpl_port.h" + +#ifdef UPSAMPLE_MERGING_SUPPORTED + + +/* Private subobject */ + +typedef struct { + struct jpeg_upsampler pub; /* public fields */ + + /* Pointer to routine to do actual upsampling/conversion of one row group */ + JMETHOD(void, upmethod, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf)); + + /* Private state for YCC->RGB conversion */ + int * Cr_r_tab; /* => table for Cr to R conversion */ + int * Cb_b_tab; /* => table for Cb to B conversion */ + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ + + /* For 2:1 vertical sampling, we produce two output rows at a time. + * We need a "spare" row buffer to hold the second output row if the + * application provides just a one-row buffer; we also use the spare + * to discard the dummy last row if the image height is odd. + */ + JSAMPROW spare_row; + boolean spare_full; /* T if spare buffer is occupied */ + + JDIMENSION out_row_width; /* samples per output row */ + JDIMENSION rows_to_go; /* counts rows remaining in image */ +} my_upsampler; + +typedef my_upsampler * my_upsample_ptr; + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + * This is taken directly from jdcolor.c; see that file for more info. + */ + +LOCAL(void) +build_ycc_rgb_table (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + int i; + INT32 x; + SHIFT_TEMPS + + upsample->Cr_r_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + upsample->Cb_b_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + upsample->Cr_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + upsample->Cb_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + /* Cr=>R value is nearest int to 1.40200 * x */ + upsample->Cr_r_tab[i] = (int) + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + /* Cb=>B value is nearest int to 1.77200 * x */ + upsample->Cb_b_tab[i] = (int) + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + /* Cr=>G value is scaled-up -0.71414 * x */ + upsample->Cr_g_tab[i] = (- FIX(0.71414)) * x; + /* Cb=>G value is scaled-up -0.34414 * x */ + /* We also add in ONE_HALF so that need not do it in inner loop */ + upsample->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + } +} + + +/* + * Initialize for an upsampling pass. + */ + +METHODDEF(void) +start_pass_merged_upsample (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Mark the spare buffer empty */ + upsample->spare_full = FALSE; + /* Initialize total-height counter for detecting bottom of image */ + upsample->rows_to_go = cinfo->output_height; +} + + +/* + * Control routine to do upsampling (and color conversion). + * + * The control routine just handles the row buffering considerations. + */ + +METHODDEF(void) +merged_2v_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + CPL_UNUSED JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +/* 2:1 vertical sampling case: may need a spare row. */ +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + JSAMPROW work_ptrs[2]; + JDIMENSION num_rows; /* number of rows returned to caller */ + + if (upsample->spare_full) { + /* If we have a spare row saved from a previous cycle, just return it. */ + jcopy_sample_rows(& upsample->spare_row, 0, output_buf + *out_row_ctr, 0, + 1, upsample->out_row_width); + num_rows = 1; + upsample->spare_full = FALSE; + } else { + /* Figure number of rows to return to caller. */ + num_rows = 2; + /* Not more than the distance to the end of the image. */ + if (num_rows > upsample->rows_to_go) + num_rows = upsample->rows_to_go; + /* And not more than what the client can accept: */ + out_rows_avail -= *out_row_ctr; + if (num_rows > out_rows_avail) + num_rows = out_rows_avail; + /* Create output pointer array for upsampler. */ + work_ptrs[0] = output_buf[*out_row_ctr]; + if (num_rows > 1) { + work_ptrs[1] = output_buf[*out_row_ctr + 1]; + } else { + work_ptrs[1] = upsample->spare_row; + upsample->spare_full = TRUE; + } + /* Now do the upsampling. */ + (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, work_ptrs); + } + + /* Adjust counts */ + *out_row_ctr += num_rows; + upsample->rows_to_go -= num_rows; + /* When the buffer is emptied, declare this input row group consumed */ + if (! upsample->spare_full) + (*in_row_group_ctr)++; +} + + +METHODDEF(void) +merged_1v_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + CPL_UNUSED JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + CPL_UNUSED JDIMENSION out_rows_avail) +/* 1:1 vertical sampling case: much easier, never need a spare row. */ +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Just do the upsampling. */ + (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, + output_buf + *out_row_ctr); + /* Adjust counts */ + (*out_row_ctr)++; + (*in_row_group_ctr)++; +} + + +/* + * These are the routines invoked by the control routines to do + * the actual upsampling/conversion. One row group is processed per call. + * + * Note: since we may be writing directly into application-supplied buffers, + * we have to be honest about the output width; we can't assume the buffer + * has been rounded up to an even width. + */ + + +/* + * Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. + */ + +METHODDEF(void) +h2v1_merged_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + register int y, cred, cgreen, cblue; + int cb, cr; + register JSAMPROW outptr; + JSAMPROW inptr0, inptr1, inptr2; + JDIMENSION col; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + int * Crrtab = upsample->Cr_r_tab; + int * Cbbtab = upsample->Cb_b_tab; + INT32 * Crgtab = upsample->Cr_g_tab; + INT32 * Cbgtab = upsample->Cb_g_tab; + SHIFT_TEMPS + + inptr0 = input_buf[0][in_row_group_ctr]; + inptr1 = input_buf[1][in_row_group_ctr]; + inptr2 = input_buf[2][in_row_group_ctr]; + outptr = output_buf[0]; + /* Loop for each pair of output pixels */ + for (col = cinfo->output_width >> 1; col > 0; col--) { + /* Do the chroma part of the calculation */ + cb = GETJSAMPLE(*inptr1++); + cr = GETJSAMPLE(*inptr2++); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + /* Fetch 2 Y values and emit 2 pixels */ + y = GETJSAMPLE(*inptr0++); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + outptr += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr0++); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + outptr += RGB_PIXELSIZE; + } + /* If image width is odd, do the last output column separately */ + if (cinfo->output_width & 1) { + cb = GETJSAMPLE(*inptr1); + cr = GETJSAMPLE(*inptr2); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + y = GETJSAMPLE(*inptr0); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + } +} + + +/* + * Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical. + */ + +METHODDEF(void) +h2v2_merged_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + register int y, cred, cgreen, cblue; + int cb, cr; + register JSAMPROW outptr0, outptr1; + JSAMPROW inptr00, inptr01, inptr1, inptr2; + JDIMENSION col; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + int * Crrtab = upsample->Cr_r_tab; + int * Cbbtab = upsample->Cb_b_tab; + INT32 * Crgtab = upsample->Cr_g_tab; + INT32 * Cbgtab = upsample->Cb_g_tab; + SHIFT_TEMPS + + inptr00 = input_buf[0][in_row_group_ctr*2]; + inptr01 = input_buf[0][in_row_group_ctr*2 + 1]; + inptr1 = input_buf[1][in_row_group_ctr]; + inptr2 = input_buf[2][in_row_group_ctr]; + outptr0 = output_buf[0]; + outptr1 = output_buf[1]; + /* Loop for each group of output pixels */ + for (col = cinfo->output_width >> 1; col > 0; col--) { + /* Do the chroma part of the calculation */ + cb = GETJSAMPLE(*inptr1++); + cr = GETJSAMPLE(*inptr2++); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + /* Fetch 4 Y values and emit 4 pixels */ + y = GETJSAMPLE(*inptr00++); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + outptr0 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr00++); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + outptr0 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr01++); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + outptr1 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr01++); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + outptr1 += RGB_PIXELSIZE; + } + /* If image width is odd, do the last output column separately */ + if (cinfo->output_width & 1) { + cb = GETJSAMPLE(*inptr1); + cr = GETJSAMPLE(*inptr2); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + y = GETJSAMPLE(*inptr00); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + y = GETJSAMPLE(*inptr01); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + } +} + + +/* + * Module initialization routine for merged upsampling/color conversion. + * + * NB: this is called under the conditions determined by use_merged_upsample() + * in jdmaster.c. That routine MUST correspond to the actual capabilities + * of this module; no safety checks are made here. + */ + +GLOBAL(void) +jinit_merged_upsampler (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample; + + upsample = (my_upsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_upsampler)); + cinfo->upsample = (struct jpeg_upsampler *) upsample; + upsample->pub.start_pass = start_pass_merged_upsample; + upsample->pub.need_context_rows = FALSE; + + upsample->out_row_width = cinfo->output_width * cinfo->out_color_components; + + if (cinfo->max_v_samp_factor == 2) { + upsample->pub.upsample = merged_2v_upsample; + upsample->upmethod = h2v2_merged_upsample; + /* Allocate a spare row buffer */ + upsample->spare_row = (JSAMPROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (size_t) (upsample->out_row_width * SIZEOF(JSAMPLE))); + } else { + upsample->pub.upsample = merged_1v_upsample; + upsample->upmethod = h2v1_merged_upsample; + /* No spare row needed */ + upsample->spare_row = NULL; + } + + build_ycc_rgb_table(cinfo); +} + +#endif /* UPSAMPLE_MERGING_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdphuff.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdphuff.c new file mode 100644 index 000000000..226780994 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdphuff.c @@ -0,0 +1,668 @@ +/* + * jdphuff.c + * + * Copyright (C) 1995-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy decoding routines for progressive JPEG. + * + * Much of the complexity here has to do with supporting input suspension. + * If the data source module demands suspension, we want to be able to back + * up to the start of the current MCU. To do this, we copy state variables + * into local working storage, and update them back to the permanent + * storage only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdhuff.h" /* Declarations shared with jdhuff.c */ + + +#ifdef D_PROGRESSIVE_SUPPORTED + +/* + * Expanded entropy decoder object for progressive Huffman decoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + unsigned int EOBRUN; /* remaining EOBs in EOBRUN */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).EOBRUN = (src).EOBRUN, \ + (dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + savable_state saved; /* Other state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; + + d_derived_tbl * ac_derived_tbl; /* active table during an AC scan */ +} phuff_entropy_decoder; + +typedef phuff_entropy_decoder * phuff_entropy_ptr; + +/* Forward declarations */ +METHODDEF(boolean) decode_mcu_DC_first JPP((j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) decode_mcu_AC_first JPP((j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) decode_mcu_DC_refine JPP((j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) decode_mcu_AC_refine JPP((j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); + + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF(void) +start_pass_phuff_decoder (j_decompress_ptr cinfo) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + boolean is_DC_band, bad; + int ci, coefi, tbl; + int *coef_bit_ptr; + jpeg_component_info * compptr; + + is_DC_band = (cinfo->Ss == 0); + + /* Validate scan parameters */ + bad = FALSE; + if (is_DC_band) { + if (cinfo->Se != 0) + bad = TRUE; + } else { + /* need not check Ss/Se < 0 since they came from unsigned bytes */ + if (cinfo->Ss > cinfo->Se || cinfo->Se >= DCTSIZE2) + bad = TRUE; + /* AC scans may have only one component */ + if (cinfo->comps_in_scan != 1) + bad = TRUE; + } + if (cinfo->Ah != 0) { + /* Successive approximation refinement scan: must have Al = Ah-1. */ + if (cinfo->Al != cinfo->Ah-1) + bad = TRUE; + } + if (cinfo->Al > 13) /* need not check for < 0 */ + bad = TRUE; + /* Arguably the maximum Al value should be less than 13 for 8-bit precision, + * but the spec doesn't say so, and we try to be liberal about what we + * accept. Note: large Al values could result in out-of-range DC + * coefficients during early scans, leading to bizarre displays due to + * overflows in the IDCT math. But we won't crash. + */ + if (bad) + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); + /* Update progression status, and verify that scan order is legal. + * Note that inter-scan inconsistencies are treated as warnings + * not fatal errors ... not clear if this is right way to behave. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + int cindex = cinfo->cur_comp_info[ci]->component_index; + coef_bit_ptr = & cinfo->coef_bits[cindex][0]; + if (!is_DC_band && coef_bit_ptr[0] < 0) /* AC without prior DC scan */ + WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, 0); + for (coefi = cinfo->Ss; coefi <= cinfo->Se; coefi++) { + int expected = (coef_bit_ptr[coefi] < 0) ? 0 : coef_bit_ptr[coefi]; + if (cinfo->Ah != expected) + WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, coefi); + coef_bit_ptr[coefi] = cinfo->Al; + } + } + + /* Select MCU decoding routine */ + if (cinfo->Ah == 0) { + if (is_DC_band) + entropy->pub.decode_mcu = decode_mcu_DC_first; + else + entropy->pub.decode_mcu = decode_mcu_AC_first; + } else { + if (is_DC_band) + entropy->pub.decode_mcu = decode_mcu_DC_refine; + else + entropy->pub.decode_mcu = decode_mcu_AC_refine; + } + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Make sure requested tables are present, and compute derived tables. + * We may build same derived table more than once, but it's not expensive. + */ + if (is_DC_band) { + if (cinfo->Ah == 0) { /* DC refinement needs no table */ + tbl = compptr->dc_tbl_no; + jpeg_make_d_derived_tbl(cinfo, TRUE, tbl, + & entropy->derived_tbls[tbl]); + } + } else { + tbl = compptr->ac_tbl_no; + jpeg_make_d_derived_tbl(cinfo, FALSE, tbl, + & entropy->derived_tbls[tbl]); + /* remember the single active table */ + entropy->ac_derived_tbl = entropy->derived_tbls[tbl]; + } + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->pub.insufficient_data = FALSE; + + /* Initialize private state variables */ + entropy->saved.EOBRUN = 0; + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and add will be faster than a table lookup. + */ + +#ifdef AVOID_TABLES + +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) + +#else + +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = /* entry n is 2**(n-1) */ + { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ + { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + +#endif /* AVOID_TABLES */ + + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL(boolean) +process_restart (j_decompress_ptr cinfo) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + int ci; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + /* Re-init EOB run count, too */ + entropy->saved.EOBRUN = 0; + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; + + /* Reset out-of-data flag, unless read_restart_marker left us smack up + * against a marker. In that case we will end up treating the next data + * segment as empty, and we can avoid producing bogus output pixels by + * leaving the flag set. + */ + if (cinfo->unread_marker == 0) + entropy->pub.insufficient_data = FALSE; + + return TRUE; +} + + +/* + * Huffman MCU decoding. + * Each of these routines decodes and returns one MCU's worth of + * Huffman-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA IS INITIALLY ZEROED BY THE CALLER. + * + * We return FALSE if data source requested suspension. In that case no + * changes have been made to permanent state. (Exception: some output + * coefficients may already have been assigned. This is harmless for + * spectral selection, since we'll just re-assign them on the next call. + * Successive approximation AC refinement has to be more careful, however.) + */ + +/* + * MCU decoding for DC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + int Al = cinfo->Al; + register int s, r; + int blkn, ci; + JBLOCKROW block; + BITREAD_STATE_VARS; + savable_state state; + d_derived_tbl * tbl; + jpeg_component_info * compptr; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + tbl = entropy->derived_tbls[compptr->dc_tbl_no]; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, tbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + /* Convert DC difference to actual value, update last_dc_val */ + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Scale and output the coefficient (assumes jpeg_natural_order[0]=0) */ + (*block)[0] = (JCOEF) (s << Al); + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * MCU decoding for AC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + int Se = cinfo->Se; + int Al = cinfo->Al; + register int s, k, r; + unsigned int EOBRUN; + JBLOCKROW block; + BITREAD_STATE_VARS; + d_derived_tbl * tbl; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state. + * We can avoid loading/saving bitread state if in an EOB run. + */ + EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we need */ + + /* There is always only one block per MCU */ + + if (EOBRUN > 0) /* if it's a band of zeroes... */ + EOBRUN--; /* ...process it now (we do nothing) */ + else { + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + block = MCU_data[0]; + tbl = entropy->ac_derived_tbl; + + for (k = cinfo->Ss; k <= Se; k++) { + HUFF_DECODE(s, br_state, tbl, return FALSE, label2); + r = s >> 4; + s &= 15; + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Scale and output coefficient in natural (dezigzagged) order */ + (*block)[jpeg_natural_order[k]] = (JCOEF) (s << Al); + } else { + if (r == 15) { /* ZRL */ + k += 15; /* skip 15 zeroes in band */ + } else { /* EOBr, run length is 2^r + appended bits */ + EOBRUN = 1 << r; + if (r) { /* EOBr, r > 0 */ + CHECK_BIT_BUFFER(br_state, r, return FALSE); + r = GET_BITS(r); + EOBRUN += r; + } + EOBRUN--; /* this band is processed at this moment */ + break; /* force end-of-band */ + } + } + } + + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + } + + /* Completed MCU, so update state */ + entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we need */ + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * MCU decoding for DC successive approximation refinement scan. + * Note: we assume such scans can be multi-component, although the spec + * is not very clear on the point. + */ + +METHODDEF(boolean) +decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + int blkn; + JBLOCKROW block; + BITREAD_STATE_VARS; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* Not worth the cycles to check insufficient_data here, + * since we will not change the data anyway if we read zeroes. + */ + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + + /* Encoded data is simply the next bit of the two's-complement DC value */ + CHECK_BIT_BUFFER(br_state, 1, return FALSE); + if (GET_BITS(1)) + (*block)[0] |= p1; + /* Note: since we use |=, repeating the assignment later is safe */ + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * MCU decoding for AC successive approximation refinement scan. + */ + +METHODDEF(boolean) +decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + int Se = cinfo->Se; + int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + int m1 = (-1) << cinfo->Al; /* -1 in the bit position being coded */ + register int s, k, r; + unsigned int EOBRUN; + JBLOCKROW block; + JCOEFPTR thiscoef; + BITREAD_STATE_VARS; + d_derived_tbl * tbl; + int num_newnz; + int newnz_pos[DCTSIZE2]; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, don't modify the MCU. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we need */ + + /* There is always only one block per MCU */ + block = MCU_data[0]; + tbl = entropy->ac_derived_tbl; + + /* If we are forced to suspend, we must undo the assignments to any newly + * nonzero coefficients in the block, because otherwise we'd get confused + * next time about which coefficients were already nonzero. + * But we need not undo addition of bits to already-nonzero coefficients; + * instead, we can test the current bit to see if we already did it. + */ + num_newnz = 0; + + /* initialize coefficient loop counter to start of band */ + k = cinfo->Ss; + + if (EOBRUN == 0) { + for (; k <= Se; k++) { + HUFF_DECODE(s, br_state, tbl, goto undoit, label3); + r = s >> 4; + s &= 15; + if (s) { + if (s != 1) /* size of new coef should always be 1 */ + WARNMS(cinfo, JWRN_HUFF_BAD_CODE); + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + if (GET_BITS(1)) + s = p1; /* newly nonzero coef is positive */ + else + s = m1; /* newly nonzero coef is negative */ + } else { + if (r != 15) { + EOBRUN = 1 << r; /* EOBr, run length is 2^r + appended bits */ + if (r) { + CHECK_BIT_BUFFER(br_state, r, goto undoit); + r = GET_BITS(r); + EOBRUN += r; + } + break; /* rest of block is handled by EOB logic */ + } + /* note s = 0 for processing ZRL */ + } + /* Advance over already-nonzero coefs and r still-zero coefs, + * appending correction bits to the nonzeroes. A correction bit is 1 + * if the absolute value of the coefficient must be increased. + */ + do { + thiscoef = *block + jpeg_natural_order[k]; + if (*thiscoef != 0) { + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + if (GET_BITS(1)) { + if ((*thiscoef & p1) == 0) { /* do nothing if already set it */ + if (*thiscoef >= 0) + *thiscoef += p1; + else + *thiscoef += m1; + } + } + } else { + if (--r < 0) + break; /* reached target zero coefficient */ + } + k++; + } while (k <= Se); + if (s) { + int pos = jpeg_natural_order[k]; + /* Output newly nonzero coefficient */ + (*block)[pos] = (JCOEF) s; + /* Remember its position in case we have to suspend */ + newnz_pos[num_newnz++] = pos; + } + } + } + + if (EOBRUN > 0) { + /* Scan any remaining coefficient positions after the end-of-band + * (the last newly nonzero coefficient, if any). Append a correction + * bit to each already-nonzero coefficient. A correction bit is 1 + * if the absolute value of the coefficient must be increased. + */ + for (; k <= Se; k++) { + thiscoef = *block + jpeg_natural_order[k]; + if (*thiscoef != 0) { + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + if (GET_BITS(1)) { + if ((*thiscoef & p1) == 0) { /* do nothing if already changed it */ + if (*thiscoef >= 0) + *thiscoef += p1; + else + *thiscoef += m1; + } + } + } + } + /* Count one block completed in EOB run */ + EOBRUN--; + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we need */ + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; + +undoit: + /* Re-zero any output coefficients that we made newly nonzero */ + while (num_newnz > 0) + (*block)[newnz_pos[--num_newnz]] = 0; + + return FALSE; +} + + +/* + * Module initialization routine for progressive Huffman entropy decoding. + */ + +GLOBAL(void) +jinit_phuff_decoder (j_decompress_ptr cinfo) +{ + phuff_entropy_ptr entropy; + int *coef_bit_ptr; + int ci, i; + + entropy = (phuff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(phuff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_phuff_decoder; + + /* Mark derived tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->derived_tbls[i] = NULL; + } + + /* Create progression status table */ + cinfo->coef_bits = (int (*)[DCTSIZE2]) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components*DCTSIZE2*SIZEOF(int)); + coef_bit_ptr = & cinfo->coef_bits[0][0]; + for (ci = 0; ci < cinfo->num_components; ci++) + for (i = 0; i < DCTSIZE2; i++) + *coef_bit_ptr++ = -1; +} + +#endif /* D_PROGRESSIVE_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdpostct.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdpostct.c new file mode 100644 index 000000000..93c3bb378 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdpostct.c @@ -0,0 +1,291 @@ +/* + * jdpostct.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the decompression postprocessing controller. + * This controller manages the upsampling, color conversion, and color + * quantization/reduction steps; specifically, it controls the buffering + * between upsample/color conversion and color quantization/reduction. + * + * If no color quantization/reduction is required, then this module has no + * work to do, and it just hands off to the upsample/color conversion code. + * An integrated upsample/convert/quantize process would replace this module + * entirely. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#include "cpl_port.h" + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_post_controller pub; /* public fields */ + + /* Color quantization source buffer: this holds output data from + * the upsample/color conversion step to be passed to the quantizer. + * For two-pass color quantization, we need a full-image buffer; + * for one-pass operation, a strip buffer is sufficient. + */ + jvirt_sarray_ptr whole_image; /* virtual array, or NULL if one-pass */ + JSAMPARRAY buffer; /* strip buffer, or current strip of virtual */ + JDIMENSION strip_height; /* buffer size in rows */ + /* for two-pass mode only: */ + JDIMENSION starting_row; /* row # of first row in current strip */ + JDIMENSION next_row; /* index of next row to fill/empty in strip */ +} my_post_controller; + +typedef my_post_controller * my_post_ptr; + + +/* Forward declarations */ +METHODDEF(void) post_process_1pass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +#ifdef QUANT_2PASS_SUPPORTED +METHODDEF(void) post_process_prepass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +METHODDEF(void) post_process_2pass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +#endif + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (cinfo->quantize_colors) { + /* Single-pass processing with color quantization. */ + post->pub.post_process_data = post_process_1pass; + /* We could be doing buffered-image output before starting a 2-pass + * color quantization; in that case, jinit_d_post_controller did not + * allocate a strip buffer. Use the virtual-array buffer as workspace. + */ + if (post->buffer == NULL) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + (JDIMENSION) 0, post->strip_height, TRUE); + } + } else { + /* For single-pass processing without color quantization, + * I have no work to do; just call the upsampler directly. + */ + post->pub.post_process_data = cinfo->upsample->upsample; + } + break; +#ifdef QUANT_2PASS_SUPPORTED + case JBUF_SAVE_AND_PASS: + /* First pass of 2-pass quantization */ + if (post->whole_image == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + post->pub.post_process_data = post_process_prepass; + break; + case JBUF_CRANK_DEST: + /* Second pass of 2-pass quantization */ + if (post->whole_image == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + post->pub.post_process_data = post_process_2pass; + break; +#endif /* QUANT_2PASS_SUPPORTED */ + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } + post->starting_row = post->next_row = 0; +} + + +/* + * Process some data in the one-pass (strip buffer) case. + * This is used for color precision reduction as well as one-pass quantization. + */ + +METHODDEF(void) +post_process_1pass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION num_rows, max_rows; + + /* Fill the buffer, but not more than what we can dump out in one go. */ + /* Note we rely on the upsampler to detect bottom of image. */ + max_rows = out_rows_avail - *out_row_ctr; + if (max_rows > post->strip_height) + max_rows = post->strip_height; + num_rows = 0; + (*cinfo->upsample->upsample) (cinfo, + input_buf, in_row_group_ctr, in_row_groups_avail, + post->buffer, &num_rows, max_rows); + /* Quantize and emit data. */ + (*cinfo->cquantize->color_quantize) (cinfo, + post->buffer, output_buf + *out_row_ctr, (int) num_rows); + *out_row_ctr += num_rows; +} + + +#ifdef QUANT_2PASS_SUPPORTED + +/* + * Process some data in the first pass of 2-pass quantization. + */ + +METHODDEF(void) +post_process_prepass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + CPL_UNUSED JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + CPL_UNUSED JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION old_next_row, num_rows; + + /* Reposition virtual buffer if at start of strip. */ + if (post->next_row == 0) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + post->starting_row, post->strip_height, TRUE); + } + + /* Upsample some data (up to a strip height's worth). */ + old_next_row = post->next_row; + (*cinfo->upsample->upsample) (cinfo, + input_buf, in_row_group_ctr, in_row_groups_avail, + post->buffer, &post->next_row, post->strip_height); + + /* Allow quantizer to scan new data. No data is emitted, */ + /* but we advance out_row_ctr so outer loop can tell when we're done. */ + if (post->next_row > old_next_row) { + num_rows = post->next_row - old_next_row; + (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row, + (JSAMPARRAY) NULL, (int) num_rows); + *out_row_ctr += num_rows; + } + + /* Advance if we filled the strip. */ + if (post->next_row >= post->strip_height) { + post->starting_row += post->strip_height; + post->next_row = 0; + } +} + + +/* + * Process some data in the second pass of 2-pass quantization. + */ + +METHODDEF(void) +post_process_2pass (j_decompress_ptr cinfo, + CPL_UNUSED JSAMPIMAGE input_buf, CPL_UNUSED JDIMENSION *in_row_group_ctr, + CPL_UNUSED JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION num_rows, max_rows; + + /* Reposition virtual buffer if at start of strip. */ + if (post->next_row == 0) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + post->starting_row, post->strip_height, FALSE); + } + + /* Determine number of rows to emit. */ + num_rows = post->strip_height - post->next_row; /* available in strip */ + max_rows = out_rows_avail - *out_row_ctr; /* available in output area */ + if (num_rows > max_rows) + num_rows = max_rows; + /* We have to check bottom of image here, can't depend on upsampler. */ + max_rows = cinfo->output_height - post->starting_row; + if (num_rows > max_rows) + num_rows = max_rows; + + /* Quantize and emit data. */ + (*cinfo->cquantize->color_quantize) (cinfo, + post->buffer + post->next_row, output_buf + *out_row_ctr, + (int) num_rows); + *out_row_ctr += num_rows; + + /* Advance if we filled the strip. */ + post->next_row += num_rows; + if (post->next_row >= post->strip_height) { + post->starting_row += post->strip_height; + post->next_row = 0; + } +} + +#endif /* QUANT_2PASS_SUPPORTED */ + + +/* + * Initialize postprocessing controller. + */ + +GLOBAL(void) +jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_post_ptr post; + + post = (my_post_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_post_controller)); + cinfo->post = (struct jpeg_d_post_controller *) post; + post->pub.start_pass = start_pass_dpost; + post->whole_image = NULL; /* flag for no virtual arrays */ + post->buffer = NULL; /* flag for no strip buffer */ + + /* Create the quantization buffer, if needed */ + if (cinfo->quantize_colors) { + /* The buffer strip height is max_v_samp_factor, which is typically + * an efficient number of rows for upsampling to return. + * (In the presence of output rescaling, we might want to be smarter?) + */ + post->strip_height = (JDIMENSION) cinfo->max_v_samp_factor; + if (need_full_buffer) { + /* Two-pass color quantization: need full-image storage. */ + /* We round up the number of rows to a multiple of the strip height. */ +#ifdef QUANT_2PASS_SUPPORTED + post->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + cinfo->output_width * cinfo->out_color_components, + (JDIMENSION) jround_up((long) cinfo->output_height, + (long) post->strip_height), + post->strip_height); +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + /* One-pass color quantization: just make a strip buffer. */ + post->buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->output_width * cinfo->out_color_components, + post->strip_height); + } + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdsample.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdsample.c new file mode 100644 index 000000000..f9979d322 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdsample.c @@ -0,0 +1,479 @@ +/* + * jdsample.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains upsampling routines. + * + * Upsampling input data is counted in "row groups". A row group + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + * sample rows of each component. Upsampling will normally produce + * max_v_samp_factor pixel rows from each row group (but this could vary + * if the upsampler is applying a scale factor of its own). + * + * An excellent reference for image resampling is + * Digital Image Warping, George Wolberg, 1990. + * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#include "cpl_port.h" + +/* Pointer to routine to upsample a single component */ +typedef JMETHOD(void, upsample1_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)); + +/* Private subobject */ + +typedef struct { + struct jpeg_upsampler pub; /* public fields */ + + /* Color conversion buffer. When using separate upsampling and color + * conversion steps, this buffer holds one upsampled row group until it + * has been color converted and output. + * Note: we do not allocate any storage for component(s) which are full-size, + * ie do not need rescaling. The corresponding entry of color_buf[] is + * simply set to point to the input data array, thereby avoiding copying. + */ + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + /* Per-component upsampling method pointers */ + upsample1_ptr methods[MAX_COMPONENTS]; + + int next_row_out; /* counts rows emitted from color_buf */ + JDIMENSION rows_to_go; /* counts rows remaining in image */ + + /* Height of an input row group for each component. */ + int rowgroup_height[MAX_COMPONENTS]; + + /* These arrays save pixel expansion factors so that int_expand need not + * recompute them each time. They are unused for other upsampling methods. + */ + UINT8 h_expand[MAX_COMPONENTS]; + UINT8 v_expand[MAX_COMPONENTS]; +} my_upsampler; + +typedef my_upsampler * my_upsample_ptr; + + +/* + * Initialize for an upsampling pass. + */ + +METHODDEF(void) +start_pass_upsample (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Mark the conversion buffer empty */ + upsample->next_row_out = cinfo->max_v_samp_factor; + /* Initialize total-height counter for detecting bottom of image */ + upsample->rows_to_go = cinfo->output_height; +} + + +/* + * Control routine to do upsampling (and color conversion). + * + * In this version we upsample each component independently. + * We upsample one row group into the conversion buffer, then apply + * color conversion a row at a time. + */ + +METHODDEF(void) +sep_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + CPL_UNUSED JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + int ci; + jpeg_component_info * compptr; + JDIMENSION num_rows; + + /* Fill the conversion buffer, if it's empty */ + if (upsample->next_row_out >= cinfo->max_v_samp_factor) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Invoke per-component upsample method. Notice we pass a POINTER + * to color_buf[ci], so that fullsize_upsample can change it. + */ + (*upsample->methods[ci]) (cinfo, compptr, + input_buf[ci] + (*in_row_group_ctr * upsample->rowgroup_height[ci]), + upsample->color_buf + ci); + } + upsample->next_row_out = 0; + } + + /* Color-convert and emit rows */ + + /* How many we have in the buffer: */ + num_rows = (JDIMENSION) (cinfo->max_v_samp_factor - upsample->next_row_out); + /* Not more than the distance to the end of the image. Need this test + * in case the image height is not a multiple of max_v_samp_factor: + */ + if (num_rows > upsample->rows_to_go) + num_rows = upsample->rows_to_go; + /* And not more than what the client can accept: */ + out_rows_avail -= *out_row_ctr; + if (num_rows > out_rows_avail) + num_rows = out_rows_avail; + + (*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf, + (JDIMENSION) upsample->next_row_out, + output_buf + *out_row_ctr, + (int) num_rows); + + /* Adjust counts */ + *out_row_ctr += num_rows; + upsample->rows_to_go -= num_rows; + upsample->next_row_out += num_rows; + /* When the buffer is emptied, declare this input row group consumed */ + if (upsample->next_row_out >= cinfo->max_v_samp_factor) + (*in_row_group_ctr)++; +} + + +/* + * These are the routines invoked by sep_upsample to upsample pixel values + * of a single component. One row group is processed per call. + */ + + +/* + * For full-size components, we just make color_buf[ci] point at the + * input buffer, and thus avoid copying any data. Note that this is + * safe only because sep_upsample doesn't declare the input row group + * "consumed" until we are done color converting and emitting it. + */ + +METHODDEF(void) +fullsize_upsample (CPL_UNUSED j_decompress_ptr cinfo, CPL_UNUSED jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + *output_data_ptr = input_data; +} + + +/* + * This is a no-op version used for "uninteresting" components. + * These components will not be referenced by color conversion. + */ + +METHODDEF(void) +noop_upsample (CPL_UNUSED j_decompress_ptr cinfo, CPL_UNUSED jpeg_component_info * compptr, + CPL_UNUSED JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + *output_data_ptr = NULL; /* safety check */ +} + + +/* + * This version handles any integral sampling ratios. + * This is not used for typical JPEG files, so it need not be fast. + * Nor, for that matter, is it particularly accurate: the algorithm is + * simple replication of the input pixel onto the corresponding output + * pixels. The hi-falutin sampling literature refers to this as a + * "box filter". A box filter tends to introduce visible artifacts, + * so if you are actually going to use 3:1 or 4:1 sampling ratios + * you would be well advised to improve this code. + */ + +METHODDEF(void) +int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + register int h; + JSAMPROW outend; + int h_expand, v_expand; + int inrow, outrow; + + h_expand = upsample->h_expand[compptr->component_index]; + v_expand = upsample->v_expand[compptr->component_index]; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + /* Generate one output row with proper horizontal expansion */ + inptr = input_data[inrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + for (h = h_expand; h > 0; h--) { + *outptr++ = invalue; + } + } + /* Generate any additional output rows by duplicating the first one */ + if (v_expand > 1) { + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + v_expand-1, cinfo->output_width); + } + inrow++; + outrow += v_expand; + } +} + + +/* + * Fast processing for the common case of 2:1 horizontal and 1:1 vertical. + * It's still a box filter. + */ + +METHODDEF(void) +h2v1_upsample (j_decompress_ptr cinfo, CPL_UNUSED jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + JSAMPROW outend; + int inrow; + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + inptr = input_data[inrow]; + outptr = output_data[inrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + *outptr++ = invalue; + *outptr++ = invalue; + } + } +} + + +/* + * Fast processing for the common case of 2:1 horizontal and 2:1 vertical. + * It's still a box filter. + */ + +METHODDEF(void) +h2v2_upsample (j_decompress_ptr cinfo, CPL_UNUSED jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + JSAMPROW outend; + int inrow, outrow; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + inptr = input_data[inrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + *outptr++ = invalue; + *outptr++ = invalue; + } + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + 1, cinfo->output_width); + inrow++; + outrow += 2; + } +} + + +/* + * Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. + * + * The upsampling algorithm is linear interpolation between pixel centers, + * also known as a "triangle filter". This is a good compromise between + * speed and visual quality. The centers of the output pixels are 1/4 and 3/4 + * of the way between input pixel centers. + * + * A note about the "bias" calculations: when rounding fractional values to + * integer, we do not want to always round 0.5 up to the next integer. + * If we did that, we'd introduce a noticeable bias towards larger values. + * Instead, this code is arranged so that 0.5 will be rounded up or down at + * alternate pixel locations (a simple ordered dither pattern). + */ + +METHODDEF(void) +h2v1_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register int invalue; + register JDIMENSION colctr; + int inrow; + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + inptr = input_data[inrow]; + outptr = output_data[inrow]; + /* Special case for first column */ + invalue = GETJSAMPLE(*inptr++); + *outptr++ = (JSAMPLE) invalue; + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(*inptr) + 2) >> 2); + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + /* General case: 3/4 * nearer pixel + 1/4 * further pixel */ + invalue = GETJSAMPLE(*inptr++) * 3; + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(inptr[-2]) + 1) >> 2); + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(*inptr) + 2) >> 2); + } + + /* Special case for last column */ + invalue = GETJSAMPLE(*inptr); + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(inptr[-1]) + 1) >> 2); + *outptr++ = (JSAMPLE) invalue; + } +} + + +/* + * Fancy processing for the common case of 2:1 horizontal and 2:1 vertical. + * Again a triangle filter; see comments for h2v1 case, above. + * + * It is OK for us to reference the adjacent input rows because we demanded + * context from the main buffer controller (see initialization code). + */ + +METHODDEF(void) +h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr0, inptr1, outptr; +#if BITS_IN_JSAMPLE == 8 + register int thiscolsum, lastcolsum, nextcolsum; +#else + register INT32 thiscolsum, lastcolsum, nextcolsum; +#endif + register JDIMENSION colctr; + int inrow, outrow, v; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + for (v = 0; v < 2; v++) { + /* inptr0 points to nearest input row, inptr1 points to next nearest */ + inptr0 = input_data[inrow]; + if (v == 0) /* next nearest is row above */ + inptr1 = input_data[inrow-1]; + else /* next nearest is row below */ + inptr1 = input_data[inrow+1]; + outptr = output_data[outrow++]; + + /* Special case for first column */ + thiscolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + /* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */ + /* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */ + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + } + + /* Special case for last column */ + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 7) >> 4); + } + inrow++; + } +} + + +/* + * Module initialization routine for upsampling. + */ + +GLOBAL(void) +jinit_upsampler (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample; + int ci; + jpeg_component_info * compptr; + boolean need_buffer, do_fancy; + int h_in_group, v_in_group, h_out_group, v_out_group; + + upsample = (my_upsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_upsampler)); + cinfo->upsample = (struct jpeg_upsampler *) upsample; + upsample->pub.start_pass = start_pass_upsample; + upsample->pub.upsample = sep_upsample; + upsample->pub.need_context_rows = FALSE; /* until we find out differently */ + + if (cinfo->CCIR601_sampling) /* this isn't supported */ + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + /* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1, + * so don't ask for it. + */ + do_fancy = cinfo->do_fancy_upsampling && cinfo->min_DCT_scaled_size > 1; + + /* Verify we can handle the sampling factors, select per-component methods, + * and create storage as needed. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Compute size of an "input group" after IDCT scaling. This many samples + * are to be converted to max_h_samp_factor * max_v_samp_factor pixels. + */ + h_in_group = (compptr->h_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; + v_in_group = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; + h_out_group = cinfo->max_h_samp_factor; + v_out_group = cinfo->max_v_samp_factor; + upsample->rowgroup_height[ci] = v_in_group; /* save for use later */ + need_buffer = TRUE; + if (! compptr->component_needed) { + /* Don't bother to upsample an uninteresting component. */ + upsample->methods[ci] = noop_upsample; + need_buffer = FALSE; + } else if (h_in_group == h_out_group && v_in_group == v_out_group) { + /* Fullsize components can be processed without any work. */ + upsample->methods[ci] = fullsize_upsample; + need_buffer = FALSE; + } else if (h_in_group * 2 == h_out_group && + v_in_group == v_out_group) { + /* Special cases for 2h1v upsampling */ + if (do_fancy && compptr->downsampled_width > 2) + upsample->methods[ci] = h2v1_fancy_upsample; + else + upsample->methods[ci] = h2v1_upsample; + } else if (h_in_group * 2 == h_out_group && + v_in_group * 2 == v_out_group) { + /* Special cases for 2h2v upsampling */ + if (do_fancy && compptr->downsampled_width > 2) { + upsample->methods[ci] = h2v2_fancy_upsample; + upsample->pub.need_context_rows = TRUE; + } else + upsample->methods[ci] = h2v2_upsample; + } else if ((h_out_group % h_in_group) == 0 && + (v_out_group % v_in_group) == 0) { + /* Generic integral-factors upsampling method */ + upsample->methods[ci] = int_upsample; + upsample->h_expand[ci] = (UINT8) (h_out_group / h_in_group); + upsample->v_expand[ci] = (UINT8) (v_out_group / v_in_group); + } else + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + if (need_buffer) { + upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) jround_up((long) cinfo->output_width, + (long) cinfo->max_h_samp_factor), + (JDIMENSION) cinfo->max_v_samp_factor); + } + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdtrans.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdtrans.c new file mode 100644 index 000000000..6c0ab715d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jdtrans.c @@ -0,0 +1,143 @@ +/* + * jdtrans.c + * + * Copyright (C) 1995-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains library routines for transcoding decompression, + * that is, reading raw DCT coefficient arrays from an input JPEG file. + * The routines in jdapimin.c will also be needed by a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL(void) transdecode_master_selection JPP((j_decompress_ptr cinfo)); + + +/* + * Read the coefficient arrays from a JPEG file. + * jpeg_read_header must be completed before calling this. + * + * The entire image is read into a set of virtual coefficient-block arrays, + * one per component. The return value is a pointer to the array of + * virtual-array descriptors. These can be manipulated directly via the + * JPEG memory manager, or handed off to jpeg_write_coefficients(). + * To release the memory occupied by the virtual arrays, call + * jpeg_finish_decompress() when done with the data. + * + * An alternative usage is to simply obtain access to the coefficient arrays + * during a buffered-image-mode decompression operation. This is allowed + * after any jpeg_finish_output() call. The arrays can be accessed until + * jpeg_finish_decompress() is called. (Note that any call to the library + * may reposition the arrays, so don't rely on access_virt_barray() results + * to stay valid across library calls.) + * + * Returns NULL if suspended. This case need be checked only if + * a suspending data source is used. + */ + +GLOBAL(jvirt_barray_ptr *) +jpeg_read_coefficients (j_decompress_ptr cinfo) +{ + if (cinfo->global_state == DSTATE_READY) { + /* First call: initialize active modules */ + transdecode_master_selection(cinfo); + cinfo->global_state = DSTATE_RDCOEFS; + } + if (cinfo->global_state == DSTATE_RDCOEFS) { + /* Absorb whole file into the coef buffer */ + for (;;) { + int retcode; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + /* Absorb some more input */ + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_SUSPENDED) + return NULL; + if (retcode == JPEG_REACHED_EOI) + break; + /* Advance progress counter if appropriate */ + if (cinfo->progress != NULL && + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + /* startup underestimated number of scans; ratchet up one scan */ + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + } + } + } + /* Set state so that jpeg_finish_decompress does the right thing */ + cinfo->global_state = DSTATE_STOPPING; + } + /* At this point we should be in state DSTATE_STOPPING if being used + * standalone, or in state DSTATE_BUFIMAGE if being invoked to get access + * to the coefficients during a full buffered-image-mode decompression. + */ + if ((cinfo->global_state == DSTATE_STOPPING || + cinfo->global_state == DSTATE_BUFIMAGE) && cinfo->buffered_image) { + return cinfo->coef->coef_arrays; + } + /* Oops, improper usage */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return NULL; /* keep compiler happy */ +} + + +/* + * Master selection of decompression modules for transcoding. + * This substitutes for jdmaster.c's initialization of the full decompressor. + */ + +LOCAL(void) +transdecode_master_selection (j_decompress_ptr cinfo) +{ + /* This is effectively a buffered-image operation. */ + cinfo->buffered_image = TRUE; + + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Always get a full-image coefficient buffer. */ + jinit_d_coef_controller(cinfo, TRUE); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + + /* Initialize progress monitoring. */ + if (cinfo->progress != NULL) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else if (cinfo->inputctl->has_multiple_scans) { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } else { + nscans = 1; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = 1; + } +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jerror.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jerror.c new file mode 100644 index 000000000..09c6c6ec8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jerror.c @@ -0,0 +1,256 @@ +/* + * jerror.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains simple error-reporting and trace-message routines. + * These are suitable for Unix-like systems and others where writing to + * stderr is the right thing to do. Many applications will want to replace + * some or all of these routines. + * + * If you define USE_WINDOWS_MESSAGEBOX in jconfig.h or in the makefile, + * you get a Windows-specific hack to display error messages in a dialog box. + * It ain't much, but it beats dropping error messages into the bit bucket, + * which is what happens to output to stderr under most Windows C compilers. + * + * These routines are used by both the compression and decompression code. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jversion.h" +#include "jerror.h" + +#ifdef USE_WINDOWS_MESSAGEBOX +#include +#endif + +#ifndef EXIT_FAILURE /* define exit() codes if not provided */ +#define EXIT_FAILURE 1 +#endif + + +/* + * Create the message string table. + * We do this from the master message list in jerror.h by re-reading + * jerror.h with a suitable definition for macro JMESSAGE. + * The message table is made an external symbol just in case any applications + * want to refer to it directly. + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_message_table jMsgTable +#endif + +#ifdef NEED_12_BIT_NAMES +#define jpeg_std_message_table jpeg_std_message_table_12 +#endif + +#define JMESSAGE(code,string) string , + +const char * const jpeg_std_message_table[] = { +#include "jerror.h" + NULL +}; + + +/* + * Error exit handler: must not return to caller. + * + * Applications may override this if they want to get control back after + * an error. Typically one would longjmp somewhere instead of exiting. + * The setjmp buffer can be made a private field within an expanded error + * handler object. Note that the info needed to generate an error message + * is stored in the error object, so you can generate the message now or + * later, at your convenience. + * You should make sure that the JPEG object is cleaned up (with jpeg_abort + * or jpeg_destroy) at some point. + */ + +METHODDEF(void) +error_exit (j_common_ptr cinfo) +{ + /* Always display the message */ + (*cinfo->err->output_message) (cinfo); + + /* Let the memory manager delete any temp files before we die */ + jpeg_destroy(cinfo); + + exit(EXIT_FAILURE); +} + + +/* + * Actual output of an error or trace message. + * Applications may override this method to send JPEG messages somewhere + * other than stderr. + * + * On Windows, printing to stderr is generally completely useless, + * so we provide optional code to produce an error-dialog popup. + * Most Windows applications will still prefer to override this routine, + * but if they don't, it'll do something at least marginally useful. + * + * NOTE: to use the library in an environment that doesn't support the + * C stdio library, you may have to delete the call to fprintf() entirely, + * not just not use this routine. + */ + +METHODDEF(void) +output_message (j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + +#ifdef USE_WINDOWS_MESSAGEBOX + /* Display it in a message dialog box */ + MessageBox(GetActiveWindow(), buffer, "JPEG Library Error", + MB_OK | MB_ICONERROR); +#else + /* Send it to stderr, adding a newline */ + fprintf(stderr, "%s\n", buffer); +#endif +} + + +/* + * Decide whether to emit a trace or warning message. + * msg_level is one of: + * -1: recoverable corrupt-data warning, may want to abort. + * 0: important advisory messages (always display to user). + * 1: first level of tracing detail. + * 2,3,...: successively more detailed tracing messages. + * An application might override this method if it wanted to abort on warnings + * or change the policy about which messages to display. + */ + +METHODDEF(void) +emit_message (j_common_ptr cinfo, int msg_level) +{ + struct jpeg_error_mgr * err = cinfo->err; + + if (msg_level < 0) { + /* It's a warning message. Since corrupt files may generate many warnings, + * the policy implemented here is to show only the first warning, + * unless trace_level >= 3. + */ + if (err->num_warnings == 0 || err->trace_level >= 3) + (*err->output_message) (cinfo); + /* Always count warnings in num_warnings. */ + err->num_warnings++; + } else { + /* It's a trace message. Show it if trace_level >= msg_level. */ + if (err->trace_level >= msg_level) + (*err->output_message) (cinfo); + } +} + + +/* + * Format a message string for the most recent JPEG error or message. + * The message is stored into buffer, which should be at least JMSG_LENGTH_MAX + * characters. Note that no '\n' character is added to the string. + * Few applications should need to override this method. + */ + +METHODDEF(void) +format_message (j_common_ptr cinfo, char * buffer) +{ + struct jpeg_error_mgr * err = cinfo->err; + int msg_code = err->msg_code; + const char * msgtext = NULL; + const char * msgptr; + char ch; + boolean isstring; + + /* Look up message string in proper table */ + if (msg_code > 0 && msg_code <= err->last_jpeg_message) { + msgtext = err->jpeg_message_table[msg_code]; + } else if (err->addon_message_table != NULL && + msg_code >= err->first_addon_message && + msg_code <= err->last_addon_message) { + msgtext = err->addon_message_table[msg_code - err->first_addon_message]; + } + + /* Defend against bogus message number */ + if (msgtext == NULL) { + err->msg_parm.i[0] = msg_code; + msgtext = err->jpeg_message_table[0]; + } + + /* Check for string parameter, as indicated by %s in the message text */ + isstring = FALSE; + msgptr = msgtext; + while ((ch = *msgptr++) != '\0') { + if (ch == '%') { + if (*msgptr == 's') isstring = TRUE; + break; + } + } + + /* Format the message into the passed buffer */ + if (isstring) + sprintf(buffer, msgtext, err->msg_parm.s); + else + sprintf(buffer, msgtext, + err->msg_parm.i[0], err->msg_parm.i[1], + err->msg_parm.i[2], err->msg_parm.i[3], + err->msg_parm.i[4], err->msg_parm.i[5], + err->msg_parm.i[6], err->msg_parm.i[7]); +} + + +/* + * Reset error state variables at start of a new image. + * This is called during compression startup to reset trace/error + * processing to default state, without losing any application-specific + * method pointers. An application might possibly want to override + * this method if it has additional error processing state. + */ + +METHODDEF(void) +reset_error_mgr (j_common_ptr cinfo) +{ + cinfo->err->num_warnings = 0; + /* trace_level is not reset since it is an application-supplied parameter */ + cinfo->err->msg_code = 0; /* may be useful as a flag for "no error" */ +} + + +/* + * Fill in the standard error-handling methods in a jpeg_error_mgr object. + * Typical call is: + * struct jpeg_compress_struct cinfo; + * struct jpeg_error_mgr err; + * + * cinfo.err = jpeg_std_error(&err); + * after which the application may override some of the methods. + */ + +GLOBAL(struct jpeg_error_mgr *) +jpeg_std_error (struct jpeg_error_mgr * err) +{ + err->error_exit = error_exit; + err->emit_message = emit_message; + err->output_message = output_message; + err->format_message = format_message; + err->reset_error_mgr = reset_error_mgr; + + err->trace_level = 0; /* default = no tracing */ + err->num_warnings = 0; /* no warnings emitted yet */ + err->msg_code = 0; /* may be useful as a flag for "no error" */ + + /* Initialize message table pointers */ + err->jpeg_message_table = jpeg_std_message_table; + err->last_jpeg_message = (int) JMSG_LASTMSGCODE - 1; + + err->addon_message_table = NULL; + err->first_addon_message = 0; /* for safety */ + err->last_addon_message = 0; + + return err; +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jerror.h b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jerror.h new file mode 100644 index 000000000..fc2fffeac --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jerror.h @@ -0,0 +1,291 @@ +/* + * jerror.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_ARITH_NOTIMPL, + "Sorry, there are legal restrictions on arithmetic coding") +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, + "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jfdctflt.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jfdctflt.c new file mode 100644 index 000000000..79d7a0078 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jfdctflt.c @@ -0,0 +1,168 @@ +/* + * jfdctflt.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a floating-point implementation of the + * forward DCT (Discrete Cosine Transform). + * + * This implementation should be more accurate than either of the integer + * DCT implementations. However, it may not give the same results on all + * machines because of differences in roundoff behavior. Speed will depend + * on the hardware's floating point capacity. + * + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with a fixed-point + * implementation, accuracy is lost due to imprecise representation of the + * scaled quantization values. However, that problem does not arise if + * we use floating point arithmetic. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_FLOAT_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_float (FAST_FLOAT * data) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z1, z2, z3, z4, z5, z11, z13; + FAST_FLOAT *dataptr; + int ctr; + + /* Pass 1: process rows. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + dataptr[4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + dataptr[2] = tmp13 + z1; /* phase 5 */ + dataptr[6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[5] = z13 + z2; /* phase 6 */ + dataptr[3] = z13 - z2; + dataptr[1] = z11 + z4; + dataptr[7] = z11 - z4; + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + dataptr[DCTSIZE*6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + dataptr[DCTSIZE*3] = z13 - z2; + dataptr[DCTSIZE*1] = z11 + z4; + dataptr[DCTSIZE*7] = z11 - z4; + + dataptr++; /* advance pointer to next column */ + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jfdctfst.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jfdctfst.c new file mode 100644 index 000000000..ccb378a3b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jfdctfst.c @@ -0,0 +1,224 @@ +/* + * jfdctfst.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a fast, not so accurate integer implementation of the + * forward DCT (Discrete Cosine Transform). + * + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with fixed-point math, + * accuracy is lost due to imprecise representation of the scaled + * quantization values. The smaller the quantization table entry, the less + * precise the scaled value, so this implementation does worse with high- + * quality-setting files than with low-quality ones. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_IFAST_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Scaling decisions are generally the same as in the LL&M algorithm; + * see jfdctint.c for more details. However, we choose to descale + * (right shift) multiplication products as soon as they are formed, + * rather than carrying additional fractional bits into subsequent additions. + * This compromises accuracy slightly, but it lets us save a few shifts. + * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) + * everywhere except in the multiplications proper; this saves a good deal + * of work on 16-bit-int machines. + * + * Again to save a few shifts, the intermediate results between pass 1 and + * pass 2 are not upscaled, but are represented only to integral precision. + * + * A final compromise is to represent the multiplicative constants to only + * 8 fractional bits, rather than 13. This saves some shifting work on some + * machines, and may also reduce the cost of multiplication (since there + * are fewer one-bits in the constants). + */ + +#define CONST_BITS 8 + + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 8 +#define FIX_0_382683433 ((INT32) 98) /* FIX(0.382683433) */ +#define FIX_0_541196100 ((INT32) 139) /* FIX(0.541196100) */ +#define FIX_0_707106781 ((INT32) 181) /* FIX(0.707106781) */ +#define FIX_1_306562965 ((INT32) 334) /* FIX(1.306562965) */ +#else +#define FIX_0_382683433 FIX(0.382683433) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_707106781 FIX(0.707106781) +#define FIX_1_306562965 FIX(1.306562965) +#endif + + +/* We can gain a little more speed, with a further compromise in accuracy, + * by omitting the addition in a descaling shift. This yields an incorrectly + * rounded result half the time... + */ + +#ifndef USE_ACCURATE_ROUNDING +#undef DESCALE +#define DESCALE(x,n) RIGHT_SHIFT(x, n) +#endif + + +/* Multiply a DCTELEM variable by an INT32 constant, and immediately + * descale to yield a DCTELEM result. + */ + +#define MULTIPLY(var,const) ((DCTELEM) DESCALE((var) * (const), CONST_BITS)) + + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_ifast (DCTELEM * data) +{ + DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + DCTELEM tmp10, tmp11, tmp12, tmp13; + DCTELEM z1, z2, z3, z4, z5, z11, z13; + DCTELEM *dataptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + dataptr[4] = tmp10 - tmp11; + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + dataptr[2] = tmp13 + z1; /* phase 5 */ + dataptr[6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[5] = z13 + z2; /* phase 6 */ + dataptr[3] = z13 - z2; + dataptr[1] = z11 + z4; + dataptr[7] = z11 - z4; + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + dataptr[DCTSIZE*6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + dataptr[DCTSIZE*3] = z13 - z2; + dataptr[DCTSIZE*1] = z11 + z4; + dataptr[DCTSIZE*7] = z11 - z4; + + dataptr++; /* advance pointer to next column */ + } +} + +#endif /* DCT_IFAST_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jfdctint.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jfdctint.c new file mode 100644 index 000000000..0a78b64ae --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jfdctint.c @@ -0,0 +1,283 @@ +/* + * jfdctint.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a slow-but-accurate integer implementation of the + * forward DCT (Discrete Cosine Transform). + * + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * This implementation is based on an algorithm described in + * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT + * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, + * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. + * The primary algorithm described there uses 11 multiplies and 29 adds. + * We use their alternate method with 12 multiplies and 32 adds. + * The advantage of this method is that no data path contains more than one + * multiplication; this allows a very simple and accurate implementation in + * scaled fixed-point arithmetic, with a minimal number of shifts. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_ISLOW_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* + * The poop on this scaling stuff is as follows: + * + * Each 1-D DCT step produces outputs which are a factor of sqrt(N) + * larger than the true DCT outputs. The final outputs are therefore + * a factor of N larger than desired; since N=8 this can be cured by + * a simple right shift at the end of the algorithm. The advantage of + * this arrangement is that we save two multiplications per 1-D DCT, + * because the y0 and y4 outputs need not be divided by sqrt(N). + * In the IJG code, this factor of 8 is removed by the quantization step + * (in jcdctmgr.c), NOT in this module. + * + * We have to do addition and subtraction of the integer inputs, which + * is no problem, and multiplication by fractional constants, which is + * a problem to do in integer arithmetic. We multiply all the constants + * by CONST_SCALE and convert them to integer constants (thus retaining + * CONST_BITS bits of precision in the constants). After doing a + * multiplication we have to divide the product by CONST_SCALE, with proper + * rounding, to produce the correct output. This division can be done + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + * as long as possible so that partial sums can be added together with + * full fractional precision. + * + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + * they are represented to better-than-integral precision. These outputs + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + * with the recommended scaling. (For 12-bit sample data, the intermediate + * array is INT32 anyway.) + * + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + * shows that the values given below are the most effective. + */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 13 +#define PASS1_BITS 2 +#else +#define CONST_BITS 13 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 13 +#define FIX_0_298631336 ((INT32) 2446) /* FIX(0.298631336) */ +#define FIX_0_390180644 ((INT32) 3196) /* FIX(0.390180644) */ +#define FIX_0_541196100 ((INT32) 4433) /* FIX(0.541196100) */ +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ +#define FIX_1_175875602 ((INT32) 9633) /* FIX(1.175875602) */ +#define FIX_1_501321110 ((INT32) 12299) /* FIX(1.501321110) */ +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ +#define FIX_1_961570560 ((INT32) 16069) /* FIX(1.961570560) */ +#define FIX_2_053119869 ((INT32) 16819) /* FIX(2.053119869) */ +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ +#define FIX_3_072711026 ((INT32) 25172) /* FIX(3.072711026) */ +#else +#define FIX_0_298631336 FIX(0.298631336) +#define FIX_0_390180644 FIX(0.390180644) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_765366865 FIX(0.765366865) +#define FIX_0_899976223 FIX(0.899976223) +#define FIX_1_175875602 FIX(1.175875602) +#define FIX_1_501321110 FIX(1.501321110) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_1_961570560 FIX(1.961570560) +#define FIX_2_053119869 FIX(2.053119869) +#define FIX_2_562915447 FIX(2.562915447) +#define FIX_3_072711026 FIX(3.072711026) +#endif + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + * For 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) +#else +#define MULTIPLY(var,const) ((var) * (const)) +#endif + + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_islow (DCTELEM * data) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2, z3, z4, z5; + DCTELEM *dataptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part per LL&M figure 1 --- note that published figure is faulty; + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + */ + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[0] = (DCTELEM) ((tmp10 + tmp11) << PASS1_BITS); + dataptr[4] = (DCTELEM) ((tmp10 - tmp11) << PASS1_BITS); + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + dataptr[2] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp13, FIX_0_765366865), + CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp12, - FIX_1_847759065), + CONST_BITS-PASS1_BITS); + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + * cK represents cos(K*pi/16). + * i0..i3 in the paper are tmp4..tmp7 here. + */ + + z1 = tmp4 + tmp7; + z2 = tmp5 + tmp6; + z3 = tmp4 + tmp6; + z4 = tmp5 + tmp7; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp4 = MULTIPLY(tmp4, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp5 = MULTIPLY(tmp5, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp6 = MULTIPLY(tmp6, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp7 = MULTIPLY(tmp7, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + dataptr[7] = (DCTELEM) DESCALE(tmp4 + z1 + z3, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp5 + z2 + z4, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp6 + z2 + z3, CONST_BITS-PASS1_BITS); + dataptr[1] = (DCTELEM) DESCALE(tmp7 + z1 + z4, CONST_BITS-PASS1_BITS); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part per LL&M figure 1 --- note that published figure is faulty; + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + */ + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = (DCTELEM) DESCALE(tmp10 + tmp11, PASS1_BITS); + dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp10 - tmp11, PASS1_BITS); + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp13, FIX_0_765366865), + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp12, - FIX_1_847759065), + CONST_BITS+PASS1_BITS); + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + * cK represents cos(K*pi/16). + * i0..i3 in the paper are tmp4..tmp7 here. + */ + + z1 = tmp4 + tmp7; + z2 = tmp5 + tmp6; + z3 = tmp4 + tmp6; + z4 = tmp5 + tmp7; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp4 = MULTIPLY(tmp4, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp5 = MULTIPLY(tmp5, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp6 = MULTIPLY(tmp6, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp7 = MULTIPLY(tmp7, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp4 + z1 + z3, + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp5 + z2 + z4, + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp6 + z2 + z3, + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp7 + z1 + z4, + CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + +#endif /* DCT_ISLOW_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jidctflt.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jidctflt.c new file mode 100644 index 000000000..0188ce3df --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jidctflt.c @@ -0,0 +1,242 @@ +/* + * jidctflt.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a floating-point implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * This implementation should be more accurate than either of the integer + * IDCT implementations. However, it may not give the same results on all + * machines because of differences in roundoff behavior. Speed will depend + * on the hardware's floating point capacity. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with a fixed-point + * implementation, accuracy is lost due to imprecise representation of the + * scaled quantization values. However, that problem does not arise if + * we use floating point arithmetic. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_FLOAT_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce a float result. + */ + +#define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) + + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL(void) +jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z5, z10, z11, z12, z13; + JCOEFPTR inptr; + FLOAT_MULT_TYPE * quantptr; + FAST_FLOAT * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (FLOAT_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = (tmp1 - tmp3) * ((FAST_FLOAT) 1.414213562) - tmp13; /* 2*c4 */ + + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); /* 2*c4 */ + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + wsptr[DCTSIZE*0] = tmp0 + tmp7; + wsptr[DCTSIZE*7] = tmp0 - tmp7; + wsptr[DCTSIZE*1] = tmp1 + tmp6; + wsptr[DCTSIZE*6] = tmp1 - tmp6; + wsptr[DCTSIZE*2] = tmp2 + tmp5; + wsptr[DCTSIZE*5] = tmp2 - tmp5; + wsptr[DCTSIZE*4] = tmp3 + tmp4; + wsptr[DCTSIZE*3] = tmp3 - tmp4; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * And testing floats for zero is relatively expensive, so we don't bother. + */ + + /* Even part */ + + tmp10 = wsptr[0] + wsptr[4]; + tmp11 = wsptr[0] - wsptr[4]; + + tmp13 = wsptr[2] + wsptr[6]; + tmp12 = (wsptr[2] - wsptr[6]) * ((FAST_FLOAT) 1.414213562) - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = wsptr[5] + wsptr[3]; + z10 = wsptr[5] - wsptr[3]; + z11 = wsptr[1] + wsptr[7]; + z12 = wsptr[1] - wsptr[7]; + + tmp7 = z11 + z13; + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + outptr[0] = range_limit[(int) DESCALE((INT32) (tmp0 + tmp7), 3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) DESCALE((INT32) (tmp0 - tmp7), 3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE((INT32) (tmp1 + tmp6), 3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) DESCALE((INT32) (tmp1 - tmp6), 3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) DESCALE((INT32) (tmp2 + tmp5), 3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) DESCALE((INT32) (tmp2 - tmp5), 3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) DESCALE((INT32) (tmp3 + tmp4), 3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) DESCALE((INT32) (tmp3 - tmp4), 3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jidctfst.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jidctfst.c new file mode 100644 index 000000000..dba4216fb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jidctfst.c @@ -0,0 +1,368 @@ +/* + * jidctfst.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a fast, not so accurate integer implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with fixed-point math, + * accuracy is lost due to imprecise representation of the scaled + * quantization values. The smaller the quantization table entry, the less + * precise the scaled value, so this implementation does worse with high- + * quality-setting files than with low-quality ones. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_IFAST_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Scaling decisions are generally the same as in the LL&M algorithm; + * see jidctint.c for more details. However, we choose to descale + * (right shift) multiplication products as soon as they are formed, + * rather than carrying additional fractional bits into subsequent additions. + * This compromises accuracy slightly, but it lets us save a few shifts. + * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) + * everywhere except in the multiplications proper; this saves a good deal + * of work on 16-bit-int machines. + * + * The dequantized coefficients are not integers because the AA&N scaling + * factors have been incorporated. We represent them scaled up by PASS1_BITS, + * so that the first and second IDCT rounds have the same input scaling. + * For 8-bit JSAMPLEs, we choose IFAST_SCALE_BITS = PASS1_BITS so as to + * avoid a descaling shift; this compromises accuracy rather drastically + * for small quantization table entries, but it saves a lot of shifts. + * For 12-bit JSAMPLEs, there's no hope of using 16x16 multiplies anyway, + * so we use a much larger scaling factor to preserve accuracy. + * + * A final compromise is to represent the multiplicative constants to only + * 8 fractional bits, rather than 13. This saves some shifting work on some + * machines, and may also reduce the cost of multiplication (since there + * are fewer one-bits in the constants). + */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 8 +#define PASS1_BITS 2 +#else +#define CONST_BITS 8 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 8 +#define FIX_1_082392200 ((INT32) 277) /* FIX(1.082392200) */ +#define FIX_1_414213562 ((INT32) 362) /* FIX(1.414213562) */ +#define FIX_1_847759065 ((INT32) 473) /* FIX(1.847759065) */ +#define FIX_2_613125930 ((INT32) 669) /* FIX(2.613125930) */ +#else +#define FIX_1_082392200 FIX(1.082392200) +#define FIX_1_414213562 FIX(1.414213562) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_2_613125930 FIX(2.613125930) +#endif + + +/* We can gain a little more speed, with a further compromise in accuracy, + * by omitting the addition in a descaling shift. This yields an incorrectly + * rounded result half the time... + */ + +#ifndef USE_ACCURATE_ROUNDING +#undef DESCALE +#define DESCALE(x,n) RIGHT_SHIFT(x, n) +#endif + + +/* Multiply a DCTELEM variable by an INT32 constant, and immediately + * descale to yield a DCTELEM result. + */ + +#define MULTIPLY(var,const) ((DCTELEM) DESCALE((var) * (const), CONST_BITS)) + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce a DCTELEM result. For 8-bit data a 16x16->16 + * multiplication will do. For 12-bit data, the multiplier table is + * declared INT32, so a 32-bit multiply will be used. + */ + +#if BITS_IN_JSAMPLE == 8 +#define DEQUANTIZE(coef,quantval) (((IFAST_MULT_TYPE) (coef)) * (quantval)) +#else +#define DEQUANTIZE(coef,quantval) \ + DESCALE((coef)*(quantval), IFAST_SCALE_BITS-PASS1_BITS) +#endif + + +/* Like DESCALE, but applies to a DCTELEM and produces an int. + * We assume that int right shift is unsigned if INT32 right shift is. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define ISHIFT_TEMPS DCTELEM ishift_temp; +#if BITS_IN_JSAMPLE == 8 +#define DCTELEMBITS 16 /* DCTELEM may be 16 or 32 bits */ +#else +#define DCTELEMBITS 32 /* DCTELEM must be 32 bits */ +#endif +#define IRIGHT_SHIFT(x,shft) \ + ((ishift_temp = (x)) < 0 ? \ + (ishift_temp >> (shft)) | ((~((DCTELEM) 0)) << (DCTELEMBITS-(shft))) : \ + (ishift_temp >> (shft))) +#else +#define ISHIFT_TEMPS +#define IRIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + +#ifdef USE_ACCURATE_ROUNDING +#define IDESCALE(x,n) ((int) IRIGHT_SHIFT((x) + (1 << ((n)-1)), n)) +#else +#define IDESCALE(x,n) ((int) IRIGHT_SHIFT(x, n)) +#endif + + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL(void) +jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + DCTELEM tmp10, tmp11, tmp12, tmp13; + DCTELEM z5, z10, z11, z12, z13; + JCOEFPTR inptr; + IFAST_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS /* for DESCALE */ + ISHIFT_TEMPS /* for IDESCALE */ + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (IFAST_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + int dcval = (int) DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; /* 2*c4 */ + + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + wsptr[DCTSIZE*0] = (int) (tmp0 + tmp7); + wsptr[DCTSIZE*7] = (int) (tmp0 - tmp7); + wsptr[DCTSIZE*1] = (int) (tmp1 + tmp6); + wsptr[DCTSIZE*6] = (int) (tmp1 - tmp6); + wsptr[DCTSIZE*2] = (int) (tmp2 + tmp5); + wsptr[DCTSIZE*5] = (int) (tmp2 - tmp5); + wsptr[DCTSIZE*4] = (int) (tmp3 + tmp4); + wsptr[DCTSIZE*3] = (int) (tmp3 - tmp4); + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * On machines with very fast multiplication, it's possible that the + * test takes more time than it's worth. In that case this section + * may be commented out. + */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 && + wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[IDESCALE(wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + outptr[2] = dcval; + outptr[3] = dcval; + outptr[4] = dcval; + outptr[5] = dcval; + outptr[6] = dcval; + outptr[7] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part */ + + tmp10 = ((DCTELEM) wsptr[0] + (DCTELEM) wsptr[4]); + tmp11 = ((DCTELEM) wsptr[0] - (DCTELEM) wsptr[4]); + + tmp13 = ((DCTELEM) wsptr[2] + (DCTELEM) wsptr[6]); + tmp12 = MULTIPLY((DCTELEM) wsptr[2] - (DCTELEM) wsptr[6], FIX_1_414213562) + - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = (DCTELEM) wsptr[5] + (DCTELEM) wsptr[3]; + z10 = (DCTELEM) wsptr[5] - (DCTELEM) wsptr[3]; + z11 = (DCTELEM) wsptr[1] + (DCTELEM) wsptr[7]; + z12 = (DCTELEM) wsptr[1] - (DCTELEM) wsptr[7]; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + outptr[0] = range_limit[IDESCALE(tmp0 + tmp7, PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[IDESCALE(tmp0 - tmp7, PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[IDESCALE(tmp1 + tmp6, PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[IDESCALE(tmp1 - tmp6, PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[IDESCALE(tmp2 + tmp5, PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[IDESCALE(tmp2 - tmp5, PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[IDESCALE(tmp3 + tmp4, PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[IDESCALE(tmp3 - tmp4, PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_IFAST_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jidctint.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jidctint.c new file mode 100644 index 000000000..a72b3207c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jidctint.c @@ -0,0 +1,389 @@ +/* + * jidctint.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a slow-but-accurate integer implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on an algorithm described in + * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT + * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, + * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. + * The primary algorithm described there uses 11 multiplies and 29 adds. + * We use their alternate method with 12 multiplies and 32 adds. + * The advantage of this method is that no data path contains more than one + * multiplication; this allows a very simple and accurate implementation in + * scaled fixed-point arithmetic, with a minimal number of shifts. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_ISLOW_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* + * The poop on this scaling stuff is as follows: + * + * Each 1-D IDCT step produces outputs which are a factor of sqrt(N) + * larger than the true IDCT outputs. The final outputs are therefore + * a factor of N larger than desired; since N=8 this can be cured by + * a simple right shift at the end of the algorithm. The advantage of + * this arrangement is that we save two multiplications per 1-D IDCT, + * because the y0 and y4 inputs need not be divided by sqrt(N). + * + * We have to do addition and subtraction of the integer inputs, which + * is no problem, and multiplication by fractional constants, which is + * a problem to do in integer arithmetic. We multiply all the constants + * by CONST_SCALE and convert them to integer constants (thus retaining + * CONST_BITS bits of precision in the constants). After doing a + * multiplication we have to divide the product by CONST_SCALE, with proper + * rounding, to produce the correct output. This division can be done + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + * as long as possible so that partial sums can be added together with + * full fractional precision. + * + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + * they are represented to better-than-integral precision. These outputs + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + * with the recommended scaling. (To scale up 12-bit sample data further, an + * intermediate INT32 array would be needed.) + * + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + * shows that the values given below are the most effective. + */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 13 +#define PASS1_BITS 2 +#else +#define CONST_BITS 13 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 13 +#define FIX_0_298631336 ((INT32) 2446) /* FIX(0.298631336) */ +#define FIX_0_390180644 ((INT32) 3196) /* FIX(0.390180644) */ +#define FIX_0_541196100 ((INT32) 4433) /* FIX(0.541196100) */ +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ +#define FIX_1_175875602 ((INT32) 9633) /* FIX(1.175875602) */ +#define FIX_1_501321110 ((INT32) 12299) /* FIX(1.501321110) */ +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ +#define FIX_1_961570560 ((INT32) 16069) /* FIX(1.961570560) */ +#define FIX_2_053119869 ((INT32) 16819) /* FIX(2.053119869) */ +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ +#define FIX_3_072711026 ((INT32) 25172) /* FIX(3.072711026) */ +#else +#define FIX_0_298631336 FIX(0.298631336) +#define FIX_0_390180644 FIX(0.390180644) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_765366865 FIX(0.765366865) +#define FIX_0_899976223 FIX(0.899976223) +#define FIX_1_175875602 FIX(1.175875602) +#define FIX_1_501321110 FIX(1.501321110) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_1_961570560 FIX(1.961570560) +#define FIX_2_053119869 FIX(2.053119869) +#define FIX_2_562915447 FIX(2.562915447) +#define FIX_3_072711026 FIX(3.072711026) +#endif + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + * For 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) +#else +#define MULTIPLY(var,const) ((var) * (const)) +#endif + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce an int result. In this module, both inputs and result + * are 16 bits or less, so either int or short multiply will work. + */ + +#define DEQUANTIZE(coef,quantval) (((ISLOW_MULT_TYPE) (coef)) * (quantval)) + + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL(void) +jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2, z3, z4, z5; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); + tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); + + z2 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + + tmp0 = (z2 + z3) << CONST_BITS; + tmp1 = (z2 - z3) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + z1 = tmp0 + tmp3; + z2 = tmp1 + tmp2; + z3 = tmp0 + tmp2; + z4 = tmp1 + tmp3; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*7] = (int) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*1] = (int) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*6] = (int) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*5] = (int) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*3] = (int) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*4] = (int) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * On machines with very fast multiplication, it's possible that the + * test takes more time than it's worth. In that case this section + * may be commented out. + */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 && + wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + outptr[2] = dcval; + outptr[3] = dcval; + outptr[4] = dcval; + outptr[5] = dcval; + outptr[6] = dcval; + outptr[7] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = (INT32) wsptr[2]; + z3 = (INT32) wsptr[6]; + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); + tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); + + tmp0 = ((INT32) wsptr[0] + (INT32) wsptr[4]) << CONST_BITS; + tmp1 = ((INT32) wsptr[0] - (INT32) wsptr[4]) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = (INT32) wsptr[7]; + tmp1 = (INT32) wsptr[5]; + tmp2 = (INT32) wsptr[3]; + tmp3 = (INT32) wsptr[1]; + + z1 = tmp0 + tmp3; + z2 = tmp1 + tmp2; + z3 = tmp0 + tmp2; + z4 = tmp1 + tmp3; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) DESCALE(tmp10 - tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE(tmp11 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) DESCALE(tmp11 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) DESCALE(tmp12 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) DESCALE(tmp12 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) DESCALE(tmp13 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) DESCALE(tmp13 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_ISLOW_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jidctred.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jidctred.c new file mode 100644 index 000000000..421f3c7ca --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jidctred.c @@ -0,0 +1,398 @@ +/* + * jidctred.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains inverse-DCT routines that produce reduced-size output: + * either 4x4, 2x2, or 1x1 pixels from an 8x8 DCT block. + * + * The implementation is based on the Loeffler, Ligtenberg and Moschytz (LL&M) + * algorithm used in jidctint.c. We simply replace each 8-to-8 1-D IDCT step + * with an 8-to-4 step that produces the four averages of two adjacent outputs + * (or an 8-to-2 step producing two averages of four outputs, for 2x2 output). + * These steps were derived by computing the corresponding values at the end + * of the normal LL&M code, then simplifying as much as possible. + * + * 1x1 is trivial: just take the DC coefficient divided by 8. + * + * See jidctint.c for additional comments. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef IDCT_SCALING_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Scaling is the same as in jidctint.c. */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 13 +#define PASS1_BITS 2 +#else +#define CONST_BITS 13 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 13 +#define FIX_0_211164243 ((INT32) 1730) /* FIX(0.211164243) */ +#define FIX_0_509795579 ((INT32) 4176) /* FIX(0.509795579) */ +#define FIX_0_601344887 ((INT32) 4926) /* FIX(0.601344887) */ +#define FIX_0_720959822 ((INT32) 5906) /* FIX(0.720959822) */ +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ +#define FIX_0_850430095 ((INT32) 6967) /* FIX(0.850430095) */ +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ +#define FIX_1_061594337 ((INT32) 8697) /* FIX(1.061594337) */ +#define FIX_1_272758580 ((INT32) 10426) /* FIX(1.272758580) */ +#define FIX_1_451774981 ((INT32) 11893) /* FIX(1.451774981) */ +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ +#define FIX_2_172734803 ((INT32) 17799) /* FIX(2.172734803) */ +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ +#define FIX_3_624509785 ((INT32) 29692) /* FIX(3.624509785) */ +#else +#define FIX_0_211164243 FIX(0.211164243) +#define FIX_0_509795579 FIX(0.509795579) +#define FIX_0_601344887 FIX(0.601344887) +#define FIX_0_720959822 FIX(0.720959822) +#define FIX_0_765366865 FIX(0.765366865) +#define FIX_0_850430095 FIX(0.850430095) +#define FIX_0_899976223 FIX(0.899976223) +#define FIX_1_061594337 FIX(1.061594337) +#define FIX_1_272758580 FIX(1.272758580) +#define FIX_1_451774981 FIX(1.451774981) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_2_172734803 FIX(2.172734803) +#define FIX_2_562915447 FIX(2.562915447) +#define FIX_3_624509785 FIX(3.624509785) +#endif + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + * For 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) +#else +#define MULTIPLY(var,const) ((var) * (const)) +#endif + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce an int result. In this module, both inputs and result + * are 16 bits or less, so either int or short multiply will work. + */ + +#define DEQUANTIZE(coef,quantval) (((ISLOW_MULT_TYPE) (coef)) * (quantval)) + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 4x4 output block. + */ + +GLOBAL(void) +jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp2, tmp10, tmp12; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE*4]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; inptr++, quantptr++, wsptr++, ctr--) { + /* Don't bother to process column 4, because second pass won't use it */ + if (ctr == DCTSIZE-4) + continue; + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*5] == 0 && + inptr[DCTSIZE*6] == 0 && inptr[DCTSIZE*7] == 0) { + /* AC terms all zero; we need not examine term 4 for 4x4 output */ + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp0 <<= (CONST_BITS+1); + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp2 = MULTIPLY(z2, FIX_1_847759065) + MULTIPLY(z3, - FIX_0_765366865); + + tmp10 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + z2 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z3 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + tmp0 = MULTIPLY(z1, - FIX_0_211164243) /* sqrt(2) * (c3-c1) */ + + MULTIPLY(z2, FIX_1_451774981) /* sqrt(2) * (c3+c7) */ + + MULTIPLY(z3, - FIX_2_172734803) /* sqrt(2) * (-c1-c5) */ + + MULTIPLY(z4, FIX_1_061594337); /* sqrt(2) * (c5+c7) */ + + tmp2 = MULTIPLY(z1, - FIX_0_509795579) /* sqrt(2) * (c7-c5) */ + + MULTIPLY(z2, - FIX_0_601344887) /* sqrt(2) * (c5-c1) */ + + MULTIPLY(z3, FIX_0_899976223) /* sqrt(2) * (c3-c7) */ + + MULTIPLY(z4, FIX_2_562915447); /* sqrt(2) * (c1+c3) */ + + /* Final output stage */ + + wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp2, CONST_BITS-PASS1_BITS+1); + wsptr[DCTSIZE*3] = (int) DESCALE(tmp10 - tmp2, CONST_BITS-PASS1_BITS+1); + wsptr[DCTSIZE*1] = (int) DESCALE(tmp12 + tmp0, CONST_BITS-PASS1_BITS+1); + wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 - tmp0, CONST_BITS-PASS1_BITS+1); + } + + /* Pass 2: process 4 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 4; ctr++) { + outptr = output_buf[ctr] + output_col; + /* It's not clear whether a zero row test is worthwhile here ... */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && + wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + outptr[2] = dcval; + outptr[3] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part */ + + tmp0 = ((INT32) wsptr[0]) << (CONST_BITS+1); + + tmp2 = MULTIPLY((INT32) wsptr[2], FIX_1_847759065) + + MULTIPLY((INT32) wsptr[6], - FIX_0_765366865); + + tmp10 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + + /* Odd part */ + + z1 = (INT32) wsptr[7]; + z2 = (INT32) wsptr[5]; + z3 = (INT32) wsptr[3]; + z4 = (INT32) wsptr[1]; + + tmp0 = MULTIPLY(z1, - FIX_0_211164243) /* sqrt(2) * (c3-c1) */ + + MULTIPLY(z2, FIX_1_451774981) /* sqrt(2) * (c3+c7) */ + + MULTIPLY(z3, - FIX_2_172734803) /* sqrt(2) * (-c1-c5) */ + + MULTIPLY(z4, FIX_1_061594337); /* sqrt(2) * (c5+c7) */ + + tmp2 = MULTIPLY(z1, - FIX_0_509795579) /* sqrt(2) * (c7-c5) */ + + MULTIPLY(z2, - FIX_0_601344887) /* sqrt(2) * (c5-c1) */ + + MULTIPLY(z3, FIX_0_899976223) /* sqrt(2) * (c3-c7) */ + + MULTIPLY(z4, FIX_2_562915447); /* sqrt(2) * (c1+c3) */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp2, + CONST_BITS+PASS1_BITS+3+1) + & RANGE_MASK]; + outptr[3] = range_limit[(int) DESCALE(tmp10 - tmp2, + CONST_BITS+PASS1_BITS+3+1) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE(tmp12 + tmp0, + CONST_BITS+PASS1_BITS+3+1) + & RANGE_MASK]; + outptr[2] = range_limit[(int) DESCALE(tmp12 - tmp0, + CONST_BITS+PASS1_BITS+3+1) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 2x2 output block. + */ + +GLOBAL(void) +jpeg_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp10, z1; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE*2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; inptr++, quantptr++, wsptr++, ctr--) { + /* Don't bother to process columns 2,4,6 */ + if (ctr == DCTSIZE-2 || ctr == DCTSIZE-4 || ctr == DCTSIZE-6) + continue; + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*3] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*7] == 0) { + /* AC terms all zero; we need not examine terms 2,4,6 for 2x2 output */ + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + + continue; + } + + /* Even part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp10 = z1 << (CONST_BITS+2); + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + tmp0 = MULTIPLY(z1, - FIX_0_720959822); /* sqrt(2) * (c7-c5+c3-c1) */ + z1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp0 += MULTIPLY(z1, FIX_0_850430095); /* sqrt(2) * (-c1+c3+c5+c7) */ + z1 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp0 += MULTIPLY(z1, - FIX_1_272758580); /* sqrt(2) * (-c1+c3-c5-c7) */ + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp0 += MULTIPLY(z1, FIX_3_624509785); /* sqrt(2) * (c1+c3+c5+c7) */ + + /* Final output stage */ + + wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp0, CONST_BITS-PASS1_BITS+2); + wsptr[DCTSIZE*1] = (int) DESCALE(tmp10 - tmp0, CONST_BITS-PASS1_BITS+2); + } + + /* Pass 2: process 2 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 2; ctr++) { + outptr = output_buf[ctr] + output_col; + /* It's not clear whether a zero row test is worthwhile here ... */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[3] == 0 && wsptr[5] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part */ + + tmp10 = ((INT32) wsptr[0]) << (CONST_BITS+2); + + /* Odd part */ + + tmp0 = MULTIPLY((INT32) wsptr[7], - FIX_0_720959822) /* sqrt(2) * (c7-c5+c3-c1) */ + + MULTIPLY((INT32) wsptr[5], FIX_0_850430095) /* sqrt(2) * (-c1+c3+c5+c7) */ + + MULTIPLY((INT32) wsptr[3], - FIX_1_272758580) /* sqrt(2) * (-c1+c3-c5-c7) */ + + MULTIPLY((INT32) wsptr[1], FIX_3_624509785); /* sqrt(2) * (c1+c3+c5+c7) */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp0, + CONST_BITS+PASS1_BITS+3+2) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3+2) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 1x1 output block. + */ + +GLOBAL(void) +jpeg_idct_1x1 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + int dcval; + ISLOW_MULT_TYPE * quantptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + SHIFT_TEMPS + + /* We hardly need an inverse DCT routine for this: just take the + * average pixel value, which is one-eighth of the DC coefficient. + */ + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + dcval = DEQUANTIZE(coef_block[0], quantptr[0]); + dcval = (int) DESCALE((INT32) dcval, 3); + + output_buf[0][output_col] = range_limit[dcval & RANGE_MASK]; +} + +#endif /* IDCT_SCALING_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jinclude.h b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jinclude.h new file mode 100644 index 000000000..0a4f15146 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jinclude.h @@ -0,0 +1,91 @@ +/* + * jinclude.h + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file exists to provide a single place to fix any problems with + * including the wrong system include files. (Common problems are taken + * care of by the standard jconfig symbols, but on really weird systems + * you may have to edit this file.) + * + * NOTE: this file is NOT intended to be included by applications using the + * JPEG library. Most applications need only include jpeglib.h. + */ + + +/* Include auto-config file to find out which system include files we need. */ + +#include "jconfig.h" /* auto configuration options */ +#define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */ + +/* + * We need the NULL macro and size_t typedef. + * On an ANSI-conforming system it is sufficient to include . + * Otherwise, we get them from or ; we may have to + * pull in as well. + * Note that the core JPEG library does not require ; + * only the default error handler and data source/destination modules do. + * But we must pull it in because of the references to FILE in jpeglib.h. + * You can remove those references if you want to compile without . + */ + +#ifdef HAVE_STDDEF_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef NEED_SYS_TYPES_H +#include +#endif + +#include + +/* + * We need memory copying and zeroing functions, plus strncpy(). + * ANSI and System V implementations declare these in . + * BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). + * Some systems may declare memset and memcpy in . + * + * NOTE: we assume the size parameters to these functions are of type size_t. + * Change the casts in these macros if not! + */ + +#ifdef NEED_BSD_STRINGS + +#include +#define MEMZERO(target,size) bzero((void *)(target), (size_t)(size)) +#define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size)) + +#else /* not BSD, assume ANSI/SysV string lib */ + +#include +#define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size)) +#define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) + +#endif + +/* + * In ANSI C, and indeed any rational implementation, size_t is also the + * type returned by sizeof(). However, it seems there are some irrational + * implementations out there, in which sizeof() returns an int even though + * size_t is defined as long or unsigned long. To ensure consistent results + * we always use this SIZEOF() macro in place of using sizeof() directly. + */ + +#define SIZEOF(object) ((size_t) sizeof(object)) + +/* + * The modules that use fread() and fwrite() always invoke them through + * these macros. On some systems you may need to twiddle the argument casts. + * CAUTION: argument order is different from underlying functions! + */ + +#define JFREAD(file,buf,sizeofbuf) \ + ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) +#define JFWRITE(file,buf,sizeofbuf) \ + ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jmemansi.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jmemansi.c new file mode 100644 index 000000000..27a22c594 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jmemansi.c @@ -0,0 +1,169 @@ +/* + * jmemansi.c + * + * Copyright (C) 1992-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides a simple generic implementation of the system- + * dependent portion of the JPEG memory manager. This implementation + * assumes that you have the ANSI-standard library routine tmpfile(). + * Also, the problem of determining the amount of memory available + * is shoved onto the user. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +#include "cpl_port.h" + +#ifndef HAVE_STDLIB_H /* should declare malloc(),free() */ +extern void * malloc JPP((size_t size)); +extern void free JPP((void *ptr)); +#endif + +#ifndef SEEK_SET /* pre-ANSI systems may not define this; */ +#define SEEK_SET 0 /* if not, assume 0 is correct */ +#endif + + +/* + * Memory allocation and freeing are controlled by the regular library + * routines malloc() and free(). + */ + +GLOBAL(void *) +jpeg_get_small (CPL_UNUSED j_common_ptr cinfo, size_t sizeofobject) +{ + return (void *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_small (CPL_UNUSED j_common_ptr cinfo, void * object, CPL_UNUSED size_t sizeofobject) +{ + free(object); +} + + +/* + * "Large" objects are treated the same as "small" ones. + * NB: although we include FAR keywords in the routine declarations, + * this file won't actually work in 80x86 small/medium model; at least, + * you probably won't be able to process useful-size images in only 64KB. + */ + +GLOBAL(void FAR *) +jpeg_get_large (CPL_UNUSED j_common_ptr cinfo, size_t sizeofobject) +{ + return (void FAR *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_large (CPL_UNUSED j_common_ptr cinfo, void FAR * object, CPL_UNUSED size_t sizeofobject) +{ + free(object); +} + + +/* + * This routine computes the total memory space available for allocation. + * It's impossible to do this in a portable way; our current solution is + * to make the user tell us (with a default value set at compile time). + * If you can actually get the available space, it's a good idea to subtract + * a slop factor of 5% or so. + */ + +#ifndef DEFAULT_MAX_MEM /* so can override from makefile */ +#define DEFAULT_MAX_MEM 1000000L /* default: one megabyte */ +#endif + +GLOBAL(long) +jpeg_mem_available (j_common_ptr cinfo, CPL_UNUSED long min_bytes_needed, + CPL_UNUSED long max_bytes_needed, long already_allocated) +{ + return cinfo->mem->max_memory_to_use - already_allocated; +} + + +/* + * Backing store (temporary file) management. + * Backing store objects are only used when the value returned by + * jpeg_mem_available is less than the total space needed. You can dispense + * with these routines if you have plenty of virtual memory; see jmemnobs.c. + */ + + +METHODDEF(void) +read_backing_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + if (fseek(info->temp_file, file_offset, SEEK_SET)) + ERREXIT(cinfo, JERR_TFILE_SEEK); + if (JFREAD(info->temp_file, buffer_address, byte_count) + != (size_t) byte_count) + ERREXIT(cinfo, JERR_TFILE_READ); +} + + +METHODDEF(void) +write_backing_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + if (fseek(info->temp_file, file_offset, SEEK_SET)) + ERREXIT(cinfo, JERR_TFILE_SEEK); + if (JFWRITE(info->temp_file, buffer_address, byte_count) + != (size_t) byte_count) + ERREXIT(cinfo, JERR_TFILE_WRITE); +} + + +METHODDEF(void) +close_backing_store (CPL_UNUSED j_common_ptr cinfo, backing_store_ptr info) +{ + fclose(info->temp_file); + /* Since this implementation uses tmpfile() to create the file, + * no explicit file deletion is needed. + */ +} + + +/* + * Initial opening of a backing-store object. + * + * This version uses tmpfile(), which constructs a suitable file name + * behind the scenes. We don't have to use info->temp_name[] at all; + * indeed, we can't even find out the actual name of the temp file. + */ + +GLOBAL(void) +jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info, + CPL_UNUSED long total_bytes_needed) +{ + if ((info->temp_file = tmpfile()) == NULL) + ERREXITS(cinfo, JERR_TFILE_CREATE, ""); + info->read_backing_store = read_backing_store; + info->write_backing_store = write_backing_store; + info->close_backing_store = close_backing_store; +} + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. + */ + +GLOBAL(long) +jpeg_mem_init (CPL_UNUSED j_common_ptr cinfo) +{ + return DEFAULT_MAX_MEM; /* default for max_memory_to_use */ +} + +GLOBAL(void) +jpeg_mem_term (CPL_UNUSED j_common_ptr cinfo) +{ + /* no work */ +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jmemmgr.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jmemmgr.c new file mode 100644 index 000000000..d801b322d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jmemmgr.c @@ -0,0 +1,1118 @@ +/* + * jmemmgr.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the JPEG system-independent memory management + * routines. This code is usable across a wide variety of machines; most + * of the system dependencies have been isolated in a separate file. + * The major functions provided here are: + * * pool-based allocation and freeing of memory; + * * policy decisions about how to divide available memory among the + * virtual arrays; + * * control logic for swapping virtual arrays between main memory and + * backing storage. + * The separate system-dependent file provides the actual backing-storage + * access code, and it contains the policy decision about how much total + * main memory to use. + * This file is system-dependent in the sense that some of its functions + * are unnecessary in some systems. For example, if there is enough virtual + * memory so that backing storage will never be used, much of the virtual + * array control logic could be removed. (Of course, if you have that much + * memory then you shouldn't care about a little bit of unused code...) + */ + +#define JPEG_INTERNALS +#define AM_MEMORY_MANAGER /* we define jvirt_Xarray_control structs */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +#ifndef NO_GETENV +#ifndef HAVE_STDLIB_H /* should declare getenv() */ +extern char * getenv JPP((const char * name)); +#endif +#endif + + +/* + * Some important notes: + * The allocation routines provided here must never return NULL. + * They should exit to error_exit if unsuccessful. + * + * It's not a good idea to try to merge the sarray and barray routines, + * even though they are textually almost the same, because samples are + * usually stored as bytes while coefficients are shorts or ints. Thus, + * in machines where byte pointers have a different representation from + * word pointers, the resulting machine code could not be the same. + */ + + +/* + * Many machines require storage alignment: longs must start on 4-byte + * boundaries, doubles on 8-byte boundaries, etc. On such machines, malloc() + * always returns pointers that are multiples of the worst-case alignment + * requirement, and we had better do so too. + * There isn't any really portable way to determine the worst-case alignment + * requirement. This module assumes that the alignment requirement is + * multiples of sizeof(ALIGN_TYPE). + * By default, we define ALIGN_TYPE as double. This is necessary on some + * workstations (where doubles really do need 8-byte alignment) and will work + * fine on nearly everything. If your machine has lesser alignment needs, + * you can save a few bytes by making ALIGN_TYPE smaller. + * The only place I know of where this will NOT work is certain Macintosh + * 680x0 compilers that define double as a 10-byte IEEE extended float. + * Doing 10-byte alignment is counterproductive because longwords won't be + * aligned well. Put "#define ALIGN_TYPE long" in jconfig.h if you have + * such a compiler. + */ + +#ifndef ALIGN_TYPE /* so can override from jconfig.h */ +#define ALIGN_TYPE double +#endif + + +/* + * We allocate objects from "pools", where each pool is gotten with a single + * request to jpeg_get_small() or jpeg_get_large(). There is no per-object + * overhead within a pool, except for alignment padding. Each pool has a + * header with a link to the next pool of the same class. + * Small and large pool headers are identical except that the latter's + * link pointer must be FAR on 80x86 machines. + * Notice that the "real" header fields are union'ed with a dummy ALIGN_TYPE + * field. This forces the compiler to make SIZEOF(small_pool_hdr) a multiple + * of the alignment requirement of ALIGN_TYPE. + */ + +typedef union small_pool_struct * small_pool_ptr; + +typedef union small_pool_struct { + struct { + small_pool_ptr next; /* next in list of pools */ + size_t bytes_used; /* how many bytes already used within pool */ + size_t bytes_left; /* bytes still available in this pool */ + } hdr; + ALIGN_TYPE dummy; /* included in union to ensure alignment */ +} small_pool_hdr; + +typedef union large_pool_struct FAR * large_pool_ptr; + +typedef union large_pool_struct { + struct { + large_pool_ptr next; /* next in list of pools */ + size_t bytes_used; /* how many bytes already used within pool */ + size_t bytes_left; /* bytes still available in this pool */ + } hdr; + ALIGN_TYPE dummy; /* included in union to ensure alignment */ +} large_pool_hdr; + + +/* + * Here is the full definition of a memory manager object. + */ + +typedef struct { + struct jpeg_memory_mgr pub; /* public fields */ + + /* Each pool identifier (lifetime class) names a linked list of pools. */ + small_pool_ptr small_list[JPOOL_NUMPOOLS]; + large_pool_ptr large_list[JPOOL_NUMPOOLS]; + + /* Since we only have one lifetime class of virtual arrays, only one + * linked list is necessary (for each datatype). Note that the virtual + * array control blocks being linked together are actually stored somewhere + * in the small-pool list. + */ + jvirt_sarray_ptr virt_sarray_list; + jvirt_barray_ptr virt_barray_list; + + /* This counts total space obtained from jpeg_get_small/large */ + long total_space_allocated; + + /* alloc_sarray and alloc_barray set this value for use by virtual + * array routines. + */ + JDIMENSION last_rowsperchunk; /* from most recent alloc_sarray/barray */ +} my_memory_mgr; + +typedef my_memory_mgr * my_mem_ptr; + + +/* + * The control blocks for virtual arrays. + * Note that these blocks are allocated in the "small" pool area. + * System-dependent info for the associated backing store (if any) is hidden + * inside the backing_store_info struct. + */ + +struct jvirt_sarray_control { + JSAMPARRAY mem_buffer; /* => the in-memory buffer */ + JDIMENSION rows_in_array; /* total virtual array height */ + JDIMENSION samplesperrow; /* width of array (and of memory buffer) */ + JDIMENSION maxaccess; /* max rows accessed by access_virt_sarray */ + JDIMENSION rows_in_mem; /* height of memory buffer */ + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + boolean pre_zero; /* pre-zero mode requested? */ + boolean dirty; /* do current buffer contents need written? */ + boolean b_s_open; /* is backing-store data valid? */ + jvirt_sarray_ptr next; /* link to next virtual sarray control block */ + backing_store_info b_s_info; /* System-dependent control info */ +}; + +struct jvirt_barray_control { + JBLOCKARRAY mem_buffer; /* => the in-memory buffer */ + JDIMENSION rows_in_array; /* total virtual array height */ + JDIMENSION blocksperrow; /* width of array (and of memory buffer) */ + JDIMENSION maxaccess; /* max rows accessed by access_virt_barray */ + JDIMENSION rows_in_mem; /* height of memory buffer */ + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + boolean pre_zero; /* pre-zero mode requested? */ + boolean dirty; /* do current buffer contents need written? */ + boolean b_s_open; /* is backing-store data valid? */ + jvirt_barray_ptr next; /* link to next virtual barray control block */ + backing_store_info b_s_info; /* System-dependent control info */ +}; + + +#ifdef MEM_STATS /* optional extra stuff for statistics */ + +LOCAL(void) +print_mem_stats (j_common_ptr cinfo, int pool_id) +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr shdr_ptr; + large_pool_ptr lhdr_ptr; + + /* Since this is only a debugging stub, we can cheat a little by using + * fprintf directly rather than going through the trace message code. + * This is helpful because message parm array can't handle longs. + */ + fprintf(stderr, "Freeing pool %d, total space = %ld\n", + pool_id, mem->total_space_allocated); + + for (lhdr_ptr = mem->large_list[pool_id]; lhdr_ptr != NULL; + lhdr_ptr = lhdr_ptr->hdr.next) { + fprintf(stderr, " Large chunk used %ld\n", + (long) lhdr_ptr->hdr.bytes_used); + } + + for (shdr_ptr = mem->small_list[pool_id]; shdr_ptr != NULL; + shdr_ptr = shdr_ptr->hdr.next) { + fprintf(stderr, " Small chunk used %ld free %ld\n", + (long) shdr_ptr->hdr.bytes_used, + (long) shdr_ptr->hdr.bytes_left); + } +} + +#endif /* MEM_STATS */ + + +LOCAL(void) +out_of_memory (j_common_ptr cinfo, int which) +/* Report an out-of-memory error and stop execution */ +/* If we compiled MEM_STATS support, report alloc requests before dying */ +{ +#ifdef MEM_STATS + cinfo->err->trace_level = 2; /* force self_destruct to report stats */ +#endif + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, which); +} + + +/* + * Allocation of "small" objects. + * + * For these, we use pooled storage. When a new pool must be created, + * we try to get enough space for the current request plus a "slop" factor, + * where the slop will be the amount of leftover space in the new pool. + * The speed vs. space tradeoff is largely determined by the slop values. + * A different slop value is provided for each pool class (lifetime), + * and we also distinguish the first pool of a class from later ones. + * NOTE: the values given work fairly well on both 16- and 32-bit-int + * machines, but may be too small if longs are 64 bits or more. + */ + +static const size_t first_pool_slop[JPOOL_NUMPOOLS] = +{ + 1600, /* first PERMANENT pool */ + 16000 /* first IMAGE pool */ +}; + +static const size_t extra_pool_slop[JPOOL_NUMPOOLS] = +{ + 0, /* additional PERMANENT pools */ + 5000 /* additional IMAGE pools */ +}; + +#define MIN_SLOP 50 /* greater than 0 to avoid futile looping */ + + +METHODDEF(void *) +alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject) +/* Allocate a "small" object */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr hdr_ptr, prev_hdr_ptr; + char * data_ptr; + size_t odd_bytes, min_request, slop; + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(small_pool_hdr))) + out_of_memory(cinfo, 1); /* request exceeds malloc's ability */ + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + /* See if space is available in any existing pool */ + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + prev_hdr_ptr = NULL; + hdr_ptr = mem->small_list[pool_id]; + while (hdr_ptr != NULL) { + if (hdr_ptr->hdr.bytes_left >= sizeofobject) + break; /* found pool with enough space */ + prev_hdr_ptr = hdr_ptr; + hdr_ptr = hdr_ptr->hdr.next; + } + + /* Time to make a new pool? */ + if (hdr_ptr == NULL) { + /* min_request is what we need now, slop is what will be leftover */ + min_request = sizeofobject + SIZEOF(small_pool_hdr); + if (prev_hdr_ptr == NULL) /* first pool in class? */ + slop = first_pool_slop[pool_id]; + else + slop = extra_pool_slop[pool_id]; + /* Don't ask for more than MAX_ALLOC_CHUNK */ + if (slop > (size_t) (MAX_ALLOC_CHUNK-min_request)) + slop = (size_t) (MAX_ALLOC_CHUNK-min_request); + /* Try to get space, if fail reduce slop and try again */ + for (;;) { + hdr_ptr = (small_pool_ptr) jpeg_get_small(cinfo, min_request + slop); + if (hdr_ptr != NULL) + break; + slop /= 2; + if (slop < MIN_SLOP) /* give up when it gets real small */ + out_of_memory(cinfo, 2); /* jpeg_get_small failed */ + } + mem->total_space_allocated += min_request + slop; + /* Success, initialize the new pool header and add to end of list */ + hdr_ptr->hdr.next = NULL; + hdr_ptr->hdr.bytes_used = 0; + hdr_ptr->hdr.bytes_left = sizeofobject + slop; + if (prev_hdr_ptr == NULL) /* first pool in class? */ + mem->small_list[pool_id] = hdr_ptr; + else + prev_hdr_ptr->hdr.next = hdr_ptr; + } + + /* OK, allocate the object from the current pool */ + data_ptr = (char *) (hdr_ptr + 1); /* point to first data byte in pool */ + data_ptr += hdr_ptr->hdr.bytes_used; /* point to place for object */ + hdr_ptr->hdr.bytes_used += sizeofobject; + hdr_ptr->hdr.bytes_left -= sizeofobject; + + return (void *) data_ptr; +} + + +/* + * Allocation of "large" objects. + * + * The external semantics of these are the same as "small" objects, + * except that FAR pointers are used on 80x86. However the pool + * management heuristics are quite different. We assume that each + * request is large enough that it may as well be passed directly to + * jpeg_get_large; the pool management just links everything together + * so that we can free it all on demand. + * Note: the major use of "large" objects is in JSAMPARRAY and JBLOCKARRAY + * structures. The routines that create these structures (see below) + * deliberately bunch rows together to ensure a large request size. + */ + +METHODDEF(void FAR *) +alloc_large (j_common_ptr cinfo, int pool_id, size_t sizeofobject) +/* Allocate a "large" object */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + large_pool_ptr hdr_ptr; + size_t odd_bytes; + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr))) + out_of_memory(cinfo, 3); /* request exceeds malloc's ability */ + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + /* Always make a new pool */ + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + hdr_ptr = (large_pool_ptr) jpeg_get_large(cinfo, sizeofobject + + SIZEOF(large_pool_hdr)); + if (hdr_ptr == NULL) + out_of_memory(cinfo, 4); /* jpeg_get_large failed */ + mem->total_space_allocated += sizeofobject + SIZEOF(large_pool_hdr); + + /* Success, initialize the new pool header and add to list */ + hdr_ptr->hdr.next = mem->large_list[pool_id]; + /* We maintain space counts in each pool header for statistical purposes, + * even though they are not needed for allocation. + */ + hdr_ptr->hdr.bytes_used = sizeofobject; + hdr_ptr->hdr.bytes_left = 0; + mem->large_list[pool_id] = hdr_ptr; + + return (void FAR *) (hdr_ptr + 1); /* point to first data byte in pool */ +} + + +/* + * Creation of 2-D sample arrays. + * The pointers are in near heap, the samples themselves in FAR heap. + * + * To minimize allocation overhead and to allow I/O of large contiguous + * blocks, we allocate the sample rows in groups of as many rows as possible + * without exceeding MAX_ALLOC_CHUNK total bytes per allocation request. + * NB: the virtual array control routines, later in this file, know about + * this chunking of rows. The rowsperchunk value is left in the mem manager + * object so that it can be saved away if this sarray is the workspace for + * a virtual array. + */ + +METHODDEF(JSAMPARRAY) +alloc_sarray (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, JDIMENSION numrows) +/* Allocate a 2-D sample array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + JSAMPARRAY result; + JSAMPROW workspace; + JDIMENSION rowsperchunk, currow, i; + long ltemp; + + /* Calculate max # of rows allowed in one allocation chunk */ + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + ((long) samplesperrow * SIZEOF(JSAMPLE)); + if (ltemp <= 0) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < (long) numrows) + rowsperchunk = (JDIMENSION) ltemp; + else + rowsperchunk = numrows; + mem->last_rowsperchunk = rowsperchunk; + + /* Get space for row pointers (small object) */ + result = (JSAMPARRAY) alloc_small(cinfo, pool_id, + (size_t) (numrows * SIZEOF(JSAMPROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JSAMPROW) alloc_large(cinfo, pool_id, + (size_t) ((size_t) rowsperchunk * (size_t) samplesperrow + * SIZEOF(JSAMPLE))); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += samplesperrow; + } + } + + return result; +} + + +/* + * Creation of 2-D coefficient-block arrays. + * This is essentially the same as the code for sample arrays, above. + */ + +METHODDEF(JBLOCKARRAY) +alloc_barray (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, JDIMENSION numrows) +/* Allocate a 2-D coefficient-block array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + JBLOCKARRAY result; + JBLOCKROW workspace; + JDIMENSION rowsperchunk, currow, i; + long ltemp; + + /* Calculate max # of rows allowed in one allocation chunk */ + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + ((long) blocksperrow * SIZEOF(JBLOCK)); + if (ltemp <= 0) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < (long) numrows) + rowsperchunk = (JDIMENSION) ltemp; + else + rowsperchunk = numrows; + mem->last_rowsperchunk = rowsperchunk; + + /* Get space for row pointers (small object) */ + result = (JBLOCKARRAY) alloc_small(cinfo, pool_id, + (size_t) (numrows * SIZEOF(JBLOCKROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JBLOCKROW) alloc_large(cinfo, pool_id, + (size_t) ((size_t) rowsperchunk * (size_t) blocksperrow + * SIZEOF(JBLOCK))); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += blocksperrow; + } + } + + return result; +} + + +/* + * About virtual array management: + * + * The above "normal" array routines are only used to allocate strip buffers + * (as wide as the image, but just a few rows high). Full-image-sized buffers + * are handled as "virtual" arrays. The array is still accessed a strip at a + * time, but the memory manager must save the whole array for repeated + * accesses. The intended implementation is that there is a strip buffer in + * memory (as high as is possible given the desired memory limit), plus a + * backing file that holds the rest of the array. + * + * The request_virt_array routines are told the total size of the image and + * the maximum number of rows that will be accessed at once. The in-memory + * buffer must be at least as large as the maxaccess value. + * + * The request routines create control blocks but not the in-memory buffers. + * That is postponed until realize_virt_arrays is called. At that time the + * total amount of space needed is known (approximately, anyway), so free + * memory can be divided up fairly. + * + * The access_virt_array routines are responsible for making a specific strip + * area accessible (after reading or writing the backing file, if necessary). + * Note that the access routines are told whether the caller intends to modify + * the accessed strip; during a read-only pass this saves having to rewrite + * data to disk. The access routines are also responsible for pre-zeroing + * any newly accessed rows, if pre-zeroing was requested. + * + * In current usage, the access requests are usually for nonoverlapping + * strips; that is, successive access start_row numbers differ by exactly + * num_rows = maxaccess. This means we can get good performance with simple + * buffer dump/reload logic, by making the in-memory buffer be a multiple + * of the access height; then there will never be accesses across bufferload + * boundaries. The code will still work with overlapping access requests, + * but it doesn't handle bufferload overlaps very efficiently. + */ + + +METHODDEF(jvirt_sarray_ptr) +request_virt_sarray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + JDIMENSION samplesperrow, JDIMENSION numrows, + JDIMENSION maxaccess) +/* Request a virtual 2-D sample array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + jvirt_sarray_ptr result; + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + if (pool_id != JPOOL_IMAGE) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + /* get control block */ + result = (jvirt_sarray_ptr) alloc_small(cinfo, pool_id, + SIZEOF(struct jvirt_sarray_control)); + + result->mem_buffer = NULL; /* marks array not yet realized */ + result->rows_in_array = numrows; + result->samplesperrow = samplesperrow; + result->maxaccess = maxaccess; + result->pre_zero = pre_zero; + result->b_s_open = FALSE; /* no associated backing-store object */ + result->next = mem->virt_sarray_list; /* add to list of virtual arrays */ + mem->virt_sarray_list = result; + + return result; +} + + +METHODDEF(jvirt_barray_ptr) +request_virt_barray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + JDIMENSION blocksperrow, JDIMENSION numrows, + JDIMENSION maxaccess) +/* Request a virtual 2-D coefficient-block array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + jvirt_barray_ptr result; + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + if (pool_id != JPOOL_IMAGE) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + /* get control block */ + result = (jvirt_barray_ptr) alloc_small(cinfo, pool_id, + SIZEOF(struct jvirt_barray_control)); + + result->mem_buffer = NULL; /* marks array not yet realized */ + result->rows_in_array = numrows; + result->blocksperrow = blocksperrow; + result->maxaccess = maxaccess; + result->pre_zero = pre_zero; + result->b_s_open = FALSE; /* no associated backing-store object */ + result->next = mem->virt_barray_list; /* add to list of virtual arrays */ + mem->virt_barray_list = result; + + return result; +} + + +METHODDEF(void) +realize_virt_arrays (j_common_ptr cinfo) +/* Allocate the in-memory buffers for any unrealized virtual arrays */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + long space_per_minheight, maximum_space, avail_mem; + long minheights, max_minheights; + jvirt_sarray_ptr sptr; + jvirt_barray_ptr bptr; + + /* Compute the minimum space needed (maxaccess rows in each buffer) + * and the maximum space needed (full image height in each buffer). + * These may be of use to the system-dependent jpeg_mem_available routine. + */ + space_per_minheight = 0; + maximum_space = 0; + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + space_per_minheight += (long) sptr->maxaccess * + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + maximum_space += (long) sptr->rows_in_array * + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + } + } + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + space_per_minheight += (long) bptr->maxaccess * + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + maximum_space += (long) bptr->rows_in_array * + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + } + } + + if (space_per_minheight <= 0) + return; /* no unrealized arrays, no work */ + + /* Determine amount of memory to actually use; this is system-dependent. */ + avail_mem = jpeg_mem_available(cinfo, space_per_minheight, maximum_space, + mem->total_space_allocated); + + /* If the maximum space needed is available, make all the buffers full + * height; otherwise parcel it out with the same number of minheights + * in each buffer. + */ + if (avail_mem >= maximum_space) + max_minheights = 1000000000L; + else { + max_minheights = avail_mem / space_per_minheight; + /* If there doesn't seem to be enough space, try to get the minimum + * anyway. This allows a "stub" implementation of jpeg_mem_available(). + */ + if (max_minheights <= 0) + max_minheights = 1; + } + + /* Allocate the in-memory buffers and initialize backing store as needed. */ + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + minheights = ((long) sptr->rows_in_array - 1L) / sptr->maxaccess + 1L; + if (minheights <= max_minheights) { + /* This buffer fits in memory */ + sptr->rows_in_mem = sptr->rows_in_array; + } else { + /* It doesn't fit in memory, create backing store. */ + sptr->rows_in_mem = (JDIMENSION) (max_minheights * sptr->maxaccess); + jpeg_open_backing_store(cinfo, & sptr->b_s_info, + (long) sptr->rows_in_array * + (long) sptr->samplesperrow * + (long) SIZEOF(JSAMPLE)); + sptr->b_s_open = TRUE; + } + sptr->mem_buffer = alloc_sarray(cinfo, JPOOL_IMAGE, + sptr->samplesperrow, sptr->rows_in_mem); + sptr->rowsperchunk = mem->last_rowsperchunk; + sptr->cur_start_row = 0; + sptr->first_undef_row = 0; + sptr->dirty = FALSE; + } + } + + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + minheights = ((long) bptr->rows_in_array - 1L) / bptr->maxaccess + 1L; + if (minheights <= max_minheights) { + /* This buffer fits in memory */ + bptr->rows_in_mem = bptr->rows_in_array; + } else { + /* It doesn't fit in memory, create backing store. */ + bptr->rows_in_mem = (JDIMENSION) (max_minheights * bptr->maxaccess); + jpeg_open_backing_store(cinfo, & bptr->b_s_info, + (long) bptr->rows_in_array * + (long) bptr->blocksperrow * + (long) SIZEOF(JBLOCK)); + bptr->b_s_open = TRUE; + } + bptr->mem_buffer = alloc_barray(cinfo, JPOOL_IMAGE, + bptr->blocksperrow, bptr->rows_in_mem); + bptr->rowsperchunk = mem->last_rowsperchunk; + bptr->cur_start_row = 0; + bptr->first_undef_row = 0; + bptr->dirty = FALSE; + } + } +} + + +LOCAL(void) +do_sarray_io (j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing) +/* Do backing store read or write of a virtual sample array */ +{ + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + bytesperrow = (long) ptr->samplesperrow * SIZEOF(JSAMPLE); + file_offset = ptr->cur_start_row * bytesperrow; + /* Loop to read or write each allocation chunk in mem_buffer */ + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + /* One chunk, but check for short chunk at end of buffer */ + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + /* Transfer no more than is currently defined */ + thisrow = (long) ptr->cur_start_row + i; + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + /* Transfer no more than fits in file */ + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + if (rows <= 0) /* this chunk might be past end of file! */ + break; + byte_count = rows * bytesperrow; + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + file_offset += byte_count; + } +} + + +LOCAL(void) +do_barray_io (j_common_ptr cinfo, jvirt_barray_ptr ptr, boolean writing) +/* Do backing store read or write of a virtual coefficient-block array */ +{ + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + bytesperrow = (long) ptr->blocksperrow * SIZEOF(JBLOCK); + file_offset = ptr->cur_start_row * bytesperrow; + /* Loop to read or write each allocation chunk in mem_buffer */ + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + /* One chunk, but check for short chunk at end of buffer */ + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + /* Transfer no more than is currently defined */ + thisrow = (long) ptr->cur_start_row + i; + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + /* Transfer no more than fits in file */ + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + if (rows <= 0) /* this chunk might be past end of file! */ + break; + byte_count = rows * bytesperrow; + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + file_offset += byte_count; + } +} + + +METHODDEF(JSAMPARRAY) +access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable) +/* Access the part of a virtual sample array starting at start_row */ +/* and extending for num_rows rows. writable is true if */ +/* caller intends to modify the accessed area. */ +{ + JDIMENSION end_row = start_row + num_rows; + JDIMENSION undef_row; + + /* debugging check */ + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + ptr->mem_buffer == NULL) + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + /* Make the desired part of the virtual array accessible */ + if (start_row < ptr->cur_start_row || + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + if (! ptr->b_s_open) + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + /* Flush old buffer contents if necessary */ + if (ptr->dirty) { + do_sarray_io(cinfo, ptr, TRUE); + ptr->dirty = FALSE; + } + /* Decide what part of virtual array to access. + * Algorithm: if target address > current window, assume forward scan, + * load starting at target address. If target address < current window, + * assume backward scan, load so that target area is top of window. + * Note that when switching from forward write to forward read, will have + * start_row = 0, so the limiting case applies and we load from 0 anyway. + */ + if (start_row > ptr->cur_start_row) { + ptr->cur_start_row = start_row; + } else { + /* use long arithmetic here to avoid overflow & unsigned problems */ + long ltemp; + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + if (ltemp < 0) + ltemp = 0; /* don't fall off front end of file */ + ptr->cur_start_row = (JDIMENSION) ltemp; + } + /* Read in the selected part of the array. + * During the initial write pass, we will do no actual read + * because the selected part is all undefined. + */ + do_sarray_io(cinfo, ptr, FALSE); + } + /* Ensure the accessed part of the array is defined; prezero if needed. + * To improve locality of access, we only prezero the part of the array + * that the caller is about to access, not the entire in-memory array. + */ + if (ptr->first_undef_row < end_row) { + if (ptr->first_undef_row < start_row) { + if (writable) /* writer skipped over a section of array */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row = start_row; /* but reader is allowed to read ahead */ + } else { + undef_row = ptr->first_undef_row; + } + if (writable) + ptr->first_undef_row = end_row; + if (ptr->pre_zero) { + size_t bytesperrow = (size_t) ptr->samplesperrow * SIZEOF(JSAMPLE); + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + end_row -= ptr->cur_start_row; + while (undef_row < end_row) { + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + undef_row++; + } + } else { + if (! writable) /* reader looking at undefined data */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + } + } + /* Flag the buffer dirty if caller will write in it */ + if (writable) + ptr->dirty = TRUE; + /* Return address of proper part of the buffer */ + return ptr->mem_buffer + (start_row - ptr->cur_start_row); +} + + +METHODDEF(JBLOCKARRAY) +access_virt_barray (j_common_ptr cinfo, jvirt_barray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable) +/* Access the part of a virtual block array starting at start_row */ +/* and extending for num_rows rows. writable is true if */ +/* caller intends to modify the accessed area. */ +{ + JDIMENSION end_row = start_row + num_rows; + JDIMENSION undef_row; + + /* debugging check */ + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + ptr->mem_buffer == NULL) + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + /* Make the desired part of the virtual array accessible */ + if (start_row < ptr->cur_start_row || + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + if (! ptr->b_s_open) + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + /* Flush old buffer contents if necessary */ + if (ptr->dirty) { + do_barray_io(cinfo, ptr, TRUE); + ptr->dirty = FALSE; + } + /* Decide what part of virtual array to access. + * Algorithm: if target address > current window, assume forward scan, + * load starting at target address. If target address < current window, + * assume backward scan, load so that target area is top of window. + * Note that when switching from forward write to forward read, will have + * start_row = 0, so the limiting case applies and we load from 0 anyway. + */ + if (start_row > ptr->cur_start_row) { + ptr->cur_start_row = start_row; + } else { + /* use long arithmetic here to avoid overflow & unsigned problems */ + long ltemp; + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + if (ltemp < 0) + ltemp = 0; /* don't fall off front end of file */ + ptr->cur_start_row = (JDIMENSION) ltemp; + } + /* Read in the selected part of the array. + * During the initial write pass, we will do no actual read + * because the selected part is all undefined. + */ + do_barray_io(cinfo, ptr, FALSE); + } + /* Ensure the accessed part of the array is defined; prezero if needed. + * To improve locality of access, we only prezero the part of the array + * that the caller is about to access, not the entire in-memory array. + */ + if (ptr->first_undef_row < end_row) { + if (ptr->first_undef_row < start_row) { + if (writable) /* writer skipped over a section of array */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row = start_row; /* but reader is allowed to read ahead */ + } else { + undef_row = ptr->first_undef_row; + } + if (writable) + ptr->first_undef_row = end_row; + if (ptr->pre_zero) { + size_t bytesperrow = (size_t) ptr->blocksperrow * SIZEOF(JBLOCK); + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + end_row -= ptr->cur_start_row; + while (undef_row < end_row) { + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + undef_row++; + } + } else { + if (! writable) /* reader looking at undefined data */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + } + } + /* Flag the buffer dirty if caller will write in it */ + if (writable) + ptr->dirty = TRUE; + /* Return address of proper part of the buffer */ + return ptr->mem_buffer + (start_row - ptr->cur_start_row); +} + + +/* + * Release all objects belonging to a specified pool. + */ + +METHODDEF(void) +free_pool (j_common_ptr cinfo, int pool_id) +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr shdr_ptr; + large_pool_ptr lhdr_ptr; + size_t space_freed; + + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + +#ifdef MEM_STATS + if (cinfo->err->trace_level > 1) + print_mem_stats(cinfo, pool_id); /* print pool's memory usage statistics */ +#endif + + /* If freeing IMAGE pool, close any virtual arrays first */ + if (pool_id == JPOOL_IMAGE) { + jvirt_sarray_ptr sptr; + jvirt_barray_ptr bptr; + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->b_s_open) { /* there may be no backing store */ + sptr->b_s_open = FALSE; /* prevent recursive close if error */ + (*sptr->b_s_info.close_backing_store) (cinfo, & sptr->b_s_info); + } + } + mem->virt_sarray_list = NULL; + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->b_s_open) { /* there may be no backing store */ + bptr->b_s_open = FALSE; /* prevent recursive close if error */ + (*bptr->b_s_info.close_backing_store) (cinfo, & bptr->b_s_info); + } + } + mem->virt_barray_list = NULL; + } + + /* Release large objects */ + lhdr_ptr = mem->large_list[pool_id]; + mem->large_list[pool_id] = NULL; + + while (lhdr_ptr != NULL) { + large_pool_ptr next_lhdr_ptr = lhdr_ptr->hdr.next; + space_freed = lhdr_ptr->hdr.bytes_used + + lhdr_ptr->hdr.bytes_left + + SIZEOF(large_pool_hdr); + jpeg_free_large(cinfo, (void FAR *) lhdr_ptr, space_freed); + mem->total_space_allocated -= space_freed; + lhdr_ptr = next_lhdr_ptr; + } + + /* Release small objects */ + shdr_ptr = mem->small_list[pool_id]; + mem->small_list[pool_id] = NULL; + + while (shdr_ptr != NULL) { + small_pool_ptr next_shdr_ptr = shdr_ptr->hdr.next; + space_freed = shdr_ptr->hdr.bytes_used + + shdr_ptr->hdr.bytes_left + + SIZEOF(small_pool_hdr); + jpeg_free_small(cinfo, (void *) shdr_ptr, space_freed); + mem->total_space_allocated -= space_freed; + shdr_ptr = next_shdr_ptr; + } +} + + +/* + * Close up shop entirely. + * Note that this cannot be called unless cinfo->mem is non-NULL. + */ + +METHODDEF(void) +self_destruct (j_common_ptr cinfo) +{ + int pool; + + /* Close all backing store, release all memory. + * Releasing pools in reverse order might help avoid fragmentation + * with some (brain-damaged) malloc libraries. + */ + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + free_pool(cinfo, pool); + } + + /* Release the memory manager control block too. */ + jpeg_free_small(cinfo, (void *) cinfo->mem, SIZEOF(my_memory_mgr)); + cinfo->mem = NULL; /* ensures I will be called only once */ + + jpeg_mem_term(cinfo); /* system-dependent cleanup */ +} + + +/* + * Memory manager initialization. + * When this is called, only the error manager pointer is valid in cinfo! + */ + +GLOBAL(void) +jinit_memory_mgr (j_common_ptr cinfo) +{ + my_mem_ptr mem; + long max_to_use; + int pool; + size_t test_mac; + + cinfo->mem = NULL; /* for safety if init fails */ + + /* Check for configuration errors. + * SIZEOF(ALIGN_TYPE) should be a power of 2; otherwise, it probably + * doesn't reflect any real hardware alignment requirement. + * The test is a little tricky: for X>0, X and X-1 have no one-bits + * in common if and only if X is a power of 2, ie has only one one-bit. + * Some compilers may give an "unreachable code" warning here; ignore it. + */ + if ((SIZEOF(ALIGN_TYPE) & (SIZEOF(ALIGN_TYPE)-1)) != 0) + ERREXIT(cinfo, JERR_BAD_ALIGN_TYPE); + /* MAX_ALLOC_CHUNK must be representable as type size_t, and must be + * a multiple of SIZEOF(ALIGN_TYPE). + * Again, an "unreachable code" warning may be ignored here. + * But a "constant too large" warning means you need to fix MAX_ALLOC_CHUNK. + */ + test_mac = (size_t) MAX_ALLOC_CHUNK; + if ((long) test_mac != MAX_ALLOC_CHUNK || + (MAX_ALLOC_CHUNK % SIZEOF(ALIGN_TYPE)) != 0) + ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK); + + max_to_use = jpeg_mem_init(cinfo); /* system-dependent initialization */ + + /* Attempt to allocate memory manager's control block */ + mem = (my_mem_ptr) jpeg_get_small(cinfo, SIZEOF(my_memory_mgr)); + + if (mem == NULL) { + jpeg_mem_term(cinfo); /* system-dependent cleanup */ + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0); + } + + /* OK, fill in the method pointers */ + mem->pub.alloc_small = alloc_small; + mem->pub.alloc_large = alloc_large; + mem->pub.alloc_sarray = alloc_sarray; + mem->pub.alloc_barray = alloc_barray; + mem->pub.request_virt_sarray = request_virt_sarray; + mem->pub.request_virt_barray = request_virt_barray; + mem->pub.realize_virt_arrays = realize_virt_arrays; + mem->pub.access_virt_sarray = access_virt_sarray; + mem->pub.access_virt_barray = access_virt_barray; + mem->pub.free_pool = free_pool; + mem->pub.self_destruct = self_destruct; + + /* Make MAX_ALLOC_CHUNK accessible to other modules */ + mem->pub.max_alloc_chunk = MAX_ALLOC_CHUNK; + + /* Initialize working state */ + mem->pub.max_memory_to_use = max_to_use; + + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + mem->small_list[pool] = NULL; + mem->large_list[pool] = NULL; + } + mem->virt_sarray_list = NULL; + mem->virt_barray_list = NULL; + + mem->total_space_allocated = SIZEOF(my_memory_mgr); + + /* Declare ourselves open for business */ + cinfo->mem = & mem->pub; + + /* Check for an environment variable JPEGMEM; if found, override the + * default max_memory setting from jpeg_mem_init. Note that the + * surrounding application may again override this value. + * If your system doesn't support getenv(), define NO_GETENV to disable + * this feature. + */ +#ifndef NO_GETENV + { char * memenv; + + if ((memenv = getenv("JPEGMEM")) != NULL) { + char ch = 'x'; + + if (sscanf(memenv, "%ld%c", &max_to_use, &ch) > 0) { + if (ch == 'm' || ch == 'M') + max_to_use *= 1000L; + mem->pub.max_memory_to_use = max_to_use * 1000L; + } + } + } +#endif + +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jmemsys.h b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jmemsys.h new file mode 100644 index 000000000..0bbd8989a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jmemsys.h @@ -0,0 +1,209 @@ +/* + * jmemsys.h + * + * Copyright (C) 1992-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This include file defines the interface between the system-independent + * and system-dependent portions of the JPEG memory manager. No other + * modules need include it. (The system-independent portion is jmemmgr.c; + * there are several different versions of the system-dependent portion.) + * + * This file works as-is for the system-dependent memory managers supplied + * in the IJG distribution. You may need to modify it if you write a + * custom memory manager. If system-dependent changes are needed in + * this file, the best method is to #ifdef them based on a configuration + * symbol supplied in jconfig.h, as we have done with USE_MSDOS_MEMMGR + * and USE_MAC_MEMMGR. + */ + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_get_small jGetSmall +#define jpeg_free_small jFreeSmall +#define jpeg_get_large jGetLarge +#define jpeg_free_large jFreeLarge +#define jpeg_mem_available jMemAvail +#define jpeg_open_backing_store jOpenBackStore +#define jpeg_mem_init jMemInit +#define jpeg_mem_term jMemTerm +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +#ifdef NEED_12_BIT_NAMES +#define jpeg_get_small jpeg_get_small_12 +#define jpeg_free_small jpeg_free_small_12 +#define jpeg_get_large jpeg_get_large_12 +#define jpeg_free_large jpeg_free_large_12 +#define jpeg_mem_available jpeg_mem_available_12 +#define jpeg_open_backing_store jpeg_open_backing_store_12 +#define jpeg_mem_init jpeg_mem_init_12 +#define jpeg_mem_term jpeg_mem_term_12 +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* + * These two functions are used to allocate and release small chunks of + * memory. (Typically the total amount requested through jpeg_get_small is + * no more than 20K or so; this will be requested in chunks of a few K each.) + * Behavior should be the same as for the standard library functions malloc + * and free; in particular, jpeg_get_small must return NULL on failure. + * On most systems, these ARE malloc and free. jpeg_free_small is passed the + * size of the object being freed, just in case it's needed. + * On an 80x86 machine using small-data memory model, these manage near heap. + */ + +EXTERN(void *) jpeg_get_small JPP((j_common_ptr cinfo, size_t sizeofobject)); +EXTERN(void) jpeg_free_small JPP((j_common_ptr cinfo, void * object, + size_t sizeofobject)); + +/* + * These two functions are used to allocate and release large chunks of + * memory (up to the total free space designated by jpeg_mem_available). + * The interface is the same as above, except that on an 80x86 machine, + * far pointers are used. On most other machines these are identical to + * the jpeg_get/free_small routines; but we keep them separate anyway, + * in case a different allocation strategy is desirable for large chunks. + */ + +EXTERN(void FAR *) jpeg_get_large JPP((j_common_ptr cinfo, + size_t sizeofobject)); +EXTERN(void) jpeg_free_large JPP((j_common_ptr cinfo, void FAR * object, + size_t sizeofobject)); + +/* + * The macro MAX_ALLOC_CHUNK designates the maximum number of bytes that may + * be requested in a single call to jpeg_get_large (and jpeg_get_small for that + * matter, but that case should never come into play). This macro is needed + * to model the 64Kb-segment-size limit of far addressing on 80x86 machines. + * On those machines, we expect that jconfig.h will provide a proper value. + * On machines with 32-bit flat address spaces, any large constant may be used. + * + * NB: jmemmgr.c expects that MAX_ALLOC_CHUNK will be representable as type + * size_t and will be a multiple of sizeof(align_type). + */ + +#ifndef MAX_ALLOC_CHUNK /* may be overridden in jconfig.h */ +#define MAX_ALLOC_CHUNK 1000000000L +#endif + +/* + * This routine computes the total space still available for allocation by + * jpeg_get_large. If more space than this is needed, backing store will be + * used. NOTE: any memory already allocated must not be counted. + * + * There is a minimum space requirement, corresponding to the minimum + * feasible buffer sizes; jmemmgr.c will request that much space even if + * jpeg_mem_available returns zero. The maximum space needed, enough to hold + * all working storage in memory, is also passed in case it is useful. + * Finally, the total space already allocated is passed. If no better + * method is available, cinfo->mem->max_memory_to_use - already_allocated + * is often a suitable calculation. + * + * It is OK for jpeg_mem_available to underestimate the space available + * (that'll just lead to more backing-store access than is really necessary). + * However, an overestimate will lead to failure. Hence it's wise to subtract + * a slop factor from the true available space. 5% should be enough. + * + * On machines with lots of virtual memory, any large constant may be returned. + * Conversely, zero may be returned to always use the minimum amount of memory. + */ + +EXTERN(long) jpeg_mem_available JPP((j_common_ptr cinfo, + long min_bytes_needed, + long max_bytes_needed, + long already_allocated)); + + +/* + * This structure holds whatever state is needed to access a single + * backing-store object. The read/write/close method pointers are called + * by jmemmgr.c to manipulate the backing-store object; all other fields + * are private to the system-dependent backing store routines. + */ + +#define TEMP_NAME_LENGTH 64 /* max length of a temporary file's name */ + + +#ifdef USE_MSDOS_MEMMGR /* DOS-specific junk */ + +typedef unsigned short XMSH; /* type of extended-memory handles */ +typedef unsigned short EMSH; /* type of expanded-memory handles */ + +typedef union { + short file_handle; /* DOS file handle if it's a temp file */ + XMSH xms_handle; /* handle if it's a chunk of XMS */ + EMSH ems_handle; /* handle if it's a chunk of EMS */ +} handle_union; + +#endif /* USE_MSDOS_MEMMGR */ + +#ifdef USE_MAC_MEMMGR /* Mac-specific junk */ +#include +#endif /* USE_MAC_MEMMGR */ + + +typedef struct backing_store_struct * backing_store_ptr; + +typedef struct backing_store_struct { + /* Methods for reading/writing/closing this backing-store object */ + JMETHOD(void, read_backing_store, (j_common_ptr cinfo, + backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count)); + JMETHOD(void, write_backing_store, (j_common_ptr cinfo, + backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count)); + JMETHOD(void, close_backing_store, (j_common_ptr cinfo, + backing_store_ptr info)); + + /* Private fields for system-dependent backing-store management */ +#ifdef USE_MSDOS_MEMMGR + /* For the MS-DOS manager (jmemdos.c), we need: */ + handle_union handle; /* reference to backing-store storage object */ + char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ +#else +#ifdef USE_MAC_MEMMGR + /* For the Mac manager (jmemmac.c), we need: */ + short temp_file; /* file reference number to temp file */ + FSSpec tempSpec; /* the FSSpec for the temp file */ + char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ +#else + /* For a typical implementation with temp files, we need: */ + FILE * temp_file; /* stdio reference to temp file */ + char temp_name[TEMP_NAME_LENGTH]; /* name of temp file */ +#endif +#endif +} backing_store_info; + + +/* + * Initial opening of a backing-store object. This must fill in the + * read/write/close pointers in the object. The read/write routines + * may take an error exit if the specified maximum file size is exceeded. + * (If jpeg_mem_available always returns a large value, this routine can + * just take an error exit.) + */ + +EXTERN(void) jpeg_open_backing_store JPP((j_common_ptr cinfo, + backing_store_ptr info, + long total_bytes_needed)); + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. jpeg_mem_init will be called before anything is + * allocated (and, therefore, nothing in cinfo is of use except the error + * manager pointer). It should return a suitable default value for + * max_memory_to_use; this may subsequently be overridden by the surrounding + * application. (Note that max_memory_to_use is only important if + * jpeg_mem_available chooses to consult it ... no one else will.) + * jpeg_mem_term may assume that all requested memory has been freed and that + * all opened backing-store objects have been closed. + */ + +EXTERN(long) jpeg_mem_init JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_mem_term JPP((j_common_ptr cinfo)); diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jmorecfg.h b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jmorecfg.h new file mode 100644 index 000000000..2c0edf9a0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jmorecfg.h @@ -0,0 +1,371 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H +typedef long INT32; +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +# if defined (_WIN32) +# ifndef __RPCNDR_H__ + typedef unsigned char boolean; +# endif +# else + typedef int boolean; +# endif +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Arithmetic coding is unsupported for legal reasons. Complaints to IBM. */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#undef C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#undef D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jpegint.h b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jpegint.h new file mode 100644 index 000000000..8b5b771fd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jpegint.h @@ -0,0 +1,428 @@ +/* + * jpegint.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides common declarations for the various JPEG modules. + * These declarations are considered internal to the JPEG library; most + * applications using the library shouldn't need to include this file. + */ + + +/* Declarations for both compression & decompression */ + +typedef enum { /* Operating modes for buffer controllers */ + JBUF_PASS_THRU, /* Plain stripwise operation */ + /* Remaining modes require a full-image buffer to have been created */ + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ +} J_BUF_MODE; + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ +#define CSTATE_START 100 /* after create_compress */ +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ +#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */ +#define DSTATE_START 200 /* after create_decompress */ +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ +#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */ +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */ +#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ +#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + + +/* Declarations for compression modules */ + +/* Master control module */ +struct jpeg_comp_master { + JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo)); + JMETHOD(void, pass_startup, (j_compress_ptr cinfo)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean call_pass_startup; /* True if pass_startup must be called */ + boolean is_last_pass; /* True during last pass */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_c_main_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail)); +}; + +/* Compression preprocessing (downsampling input buffer control) */ +struct jpeg_c_prep_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_c_coef_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf)); +}; + +/* Colorspace conversion */ +struct jpeg_color_converter { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, color_convert, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +}; + +/* Downsampling */ +struct jpeg_downsampler { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, downsample, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, + JDIMENSION out_row_group_index)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Forward DCT (also controls coefficient quantization) */ +struct jpeg_forward_dct { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + /* perhaps this should be an array??? */ + JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks)); +}; + +/* Entropy encoding */ +struct jpeg_entropy_encoder { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); +}; + +/* Marker writing */ +struct jpeg_marker_writer { + JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_frame_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_scan_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo)); + JMETHOD(void, write_tables_only, (j_compress_ptr cinfo)); + /* These routines are exported to allow insertion of extra markers */ + /* Probably only COM and APPn markers should be written this way */ + JMETHOD(void, write_marker_header, (j_compress_ptr cinfo, int marker, + unsigned int datalen)); + JMETHOD(void, write_marker_byte, (j_compress_ptr cinfo, int val)); +}; + + +/* Declarations for decompression modules */ + +/* Master control module */ +struct jpeg_decomp_master { + JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ +}; + +/* Input control module */ +struct jpeg_input_controller { + JMETHOD(int, consume_input, (j_decompress_ptr cinfo)); + JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo)); + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean has_multiple_scans; /* True if file has multiple scans */ + boolean eoi_reached; /* True when EOI has been consumed */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_d_main_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_d_coef_controller { + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); + JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, + JSAMPIMAGE output_buf)); + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; +}; + +/* Decompression postprocessing (color quantization buffer control) */ +struct jpeg_d_post_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Marker reading & parsing */ +struct jpeg_marker_reader { + JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo)); + /* Read markers until SOS or EOI. + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + JMETHOD(int, read_markers, (j_decompress_ptr cinfo)); + /* Read a restart marker --- exported for use by entropy decoder only */ + jpeg_marker_parser_method read_restart_marker; + + /* State of marker reader --- nominally internal, but applications + * supplying COM or APPn handlers might like to know the state. + */ + boolean saw_SOI; /* found SOI? */ + boolean saw_SOF; /* found SOF? */ + int next_restart_num; /* next restart number expected (0-7) */ + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ +}; + +/* Entropy decoding */ +struct jpeg_entropy_decoder { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); + + /* This is here to share code between baseline and progressive decoders; */ + /* other modules probably should not use it */ + boolean insufficient_data; /* set TRUE after emitting warning */ +}; + +/* Inverse DCT (also performs dequantization) */ +typedef JMETHOD(void, inverse_DCT_method_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col)); + +struct jpeg_inverse_dct { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + +/* Upsampling (note that upsampler must also call color converter) */ +struct jpeg_upsampler { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, upsample, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Colorspace conversion */ +struct jpeg_color_deconverter { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, color_convert, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +}; + +/* Color quantization or color precision reduction */ +struct jpeg_color_quantizer { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, + int num_rows)); + JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); +}; + + +/* Miscellaneous useful macros */ + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS INT32 shift_temp; +#define RIGHT_SHIFT(x,shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jinit_compress_master jICompress +#define jinit_c_master_control jICMaster +#define jinit_c_main_controller jICMainC +#define jinit_c_prep_controller jICPrepC +#define jinit_c_coef_controller jICCoefC +#define jinit_color_converter jICColor +#define jinit_downsampler jIDownsampler +#define jinit_forward_dct jIFDCT +#define jinit_huff_encoder jIHEncoder +#define jinit_phuff_encoder jIPHEncoder +#define jinit_marker_writer jIMWriter +#define jinit_master_decompress jIDMaster +#define jinit_d_main_controller jIDMainC +#define jinit_d_coef_controller jIDCoefC +#define jinit_d_post_controller jIDPostC +#define jinit_input_controller jIInCtlr +#define jinit_marker_reader jIMReader +#define jinit_huff_decoder jIHDecoder +#define jinit_phuff_decoder jIPHDecoder +#define jinit_inverse_dct jIIDCT +#define jinit_upsampler jIUpsampler +#define jinit_color_deconverter jIDColor +#define jinit_1pass_quantizer jI1Quant +#define jinit_2pass_quantizer jI2Quant +#define jinit_merged_upsampler jIMUpsampler +#define jinit_memory_mgr jIMemMgr +#define jdiv_round_up jDivRound +#define jround_up jRound +#define jcopy_sample_rows jCopySamples +#define jcopy_block_row jCopyBlocks +#define jzero_far jZeroFar +#define jpeg_zigzag_order jZIGTable +#define jpeg_natural_order jZAGTable +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +#ifdef NEED_12_BIT_NAMES +#define jinit_compress_master jinit_compress_master_12 +#define jinit_c_master_control jinit_c_master_control_12 +#define jinit_c_main_controller jinit_c_main_controller_12 +#define jinit_c_prep_controller jinit_c_prep_controller_12 +#define jinit_c_coef_controller jinit_c_coef_controller_12 +#define jinit_color_converter jinit_color_converter_12 +#define jinit_downsampler jinit_downsampler_12 +#define jinit_forward_dct jinit_forward_dct_12 +#define jinit_huff_encoder jinit_huff_encoder_12 +#define jinit_phuff_encoder jinit_phuff_encoder_12 +#define jinit_marker_writer jinit_marker_writer_12 +#define jinit_master_decompress jinit_master_decompress_12 +#define jinit_d_main_controller jinit_d_main_controller_12 +#define jinit_d_coef_controller jinit_d_coef_controller_12 +#define jinit_d_post_controller jinit_d_post_controller_12 +#define jinit_input_controller jinit_input_controller_12 +#define jinit_marker_reader jinit_marker_reader_12 +#define jinit_huff_decoder jinit_huff_decoder_12 +#define jinit_phuff_decoder jinit_phuff_decoder_12 +#define jinit_inverse_dct jinit_inverse_dct_12 +#define jinit_upsampler jinit_upsampler_12 +#define jinit_color_deconverter jinit_color_deconverter_12 +#define jinit_1pass_quantizer jinit_1pass_quantizer_12 +#define jinit_2pass_quantizer jinit_2pass_quantizer_12 +#define jinit_merged_upsampler jinit_merged_upsampler_12 +#define jinit_memory_mgr jinit_memory_mgr_12 +#define jdiv_round_up jdiv_round_up_12 +#define jround_up jround_up_12 +#define jcopy_sample_rows jcopy_sample_rows_12 +#define jcopy_block_row jcopy_block_row_12 +#define jzero_far jzero_far_12 +#define jpeg_zigzag_order jpeg_zigzag_order_12 +#define jpeg_natural_order jpeg_natural_order_12 +#endif + + +/* Compression module initialization routines */ +EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo, + boolean transcode_only)); +EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_downsampler JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_phuff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo)); +/* Decompression module initialization routines */ +EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_marker_reader JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_upsampler JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_color_deconverter JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); +/* Memory manager initialization */ +EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo)); + +/* Utility routines in jutils.c */ +EXTERN(long) jdiv_round_up JPP((long a, long b)); +EXTERN(long) jround_up JPP((long a, long b)); +EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols)); +EXTERN(void) jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks)); +EXTERN(void) jzero_far JPP((void FAR * target, size_t bytestozero)); +/* Constant tables in jutils.c */ +#if 0 /* This table is not actually needed in v6a */ +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ +#endif +extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ + +/* Suppress undefined-structure complaints if necessary. */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +#endif +#endif /* INCOMPLETE_TYPES_BROKEN */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jpeglib.h b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jpeglib.h new file mode 100644 index 000000000..100333ee6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jpeglib.h @@ -0,0 +1,1150 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". + */ + +#define JPEG_LIB_VERSION 62 /* Version 6b */ + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples. Always DCTSIZE for compression. + * For decompression this is the size of the output from one DCT block, + * reflecting any scaling we choose to apply during the IDCT step. + * Values of 1,2,4,8 are likely to be supported. Note that different + * components may receive different IDCT scalings. + */ + int DCT_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface), thus + * downsampled_width = ceil(image_width * Hi/Hmax) + * and similarly for height. For decompression, IDCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Sometimes it is desirable to build with special external names for 12bit, so that 8bit and 12bit + jpeg DLLs can be used in the same applications. */ + +#ifdef NEED_12_BIT_NAMES +#define jpeg_std_error jpeg_std_error_12 +#define jpeg_CreateCompress jpeg_CreateCompress_12 +#define jpeg_CreateDecompress jpeg_CreateDecompress_12 +#define jpeg_destroy_compress jpeg_destroy_compress_12 +#define jpeg_destroy_decompress jpeg_destroy_decompress_12 +#define jpeg_stdio_dest jpeg_stdio_dest_12 +#define jpeg_stdio_src jpeg_stdio_src_12 +#define jpeg_set_defaults jpeg_set_defaults_12 +#define jpeg_set_colorspace jpeg_set_colorspace_12 +#define jpeg_default_colorspace jpeg_default_colorspace_12 +#define jpeg_set_quality jpeg_set_quality_12 +#define jpeg_set_linear_quality jpeg_set_linear_quality_12 +#define jpeg_add_quant_table jpeg_add_quant_table_12 +#define jpeg_quality_scaling jpeg_quality_scaling_12 +#define jpeg_simple_progression jpeg_simple_progression_12 +#define jpeg_suppress_tables jpeg_suppress_tables_12 +#define jpeg_alloc_quant_table jpeg_alloc_quant_table_12 +#define jpeg_alloc_huff_table jpeg_alloc_huff_table_12 +#define jpeg_start_compress jpeg_start_compress_12 +#define jpeg_write_scanlines jpeg_write_scanlines_12 +#define jpeg_finish_compress jpeg_finish_compress_12 +#define jpeg_write_raw_data jpeg_write_raw_data_12 +#define jpeg_write_marker jpeg_write_marker_12 +#define jpeg_write_m_header jpeg_write_m_header_12 +#define jpeg_write_m_byte jpeg_write_m_byte_12 +#define jpeg_write_tables jpeg_write_tables_12 +#define jpeg_read_header jpeg_read_header_12 +#define jpeg_start_decompress jpeg_start_decompress_12 +#define jpeg_read_scanlines jpeg_read_scanlines_12 +#define jpeg_finish_decompress jpeg_finish_decompress_12 +#define jpeg_read_raw_data jpeg_read_raw_data_12 +#define jpeg_has_multiple_scans jpeg_has_multiple_scans_12 +#define jpeg_start_output jpeg_start_output_12 +#define jpeg_finish_output jpeg_finish_output_12 +#define jpeg_input_complete jpeg_input_complete_12 +#define jpeg_new_colormap jpeg_new_colormap_12 +#define jpeg_consume_input jpeg_consume_input_12 +#define jpeg_calc_output_dimensions jpeg_calc_output_dimensions_12 +#define jpeg_save_markers jpeg_save_markers_12 +#define jpeg_set_marker_processor jpeg_set_marker_processor_12 +#define jpeg_read_coefficients jpeg_read_coefficients_12 +#define jpeg_write_coefficients jpeg_write_coefficients_12 +#define jpeg_copy_critical_parameters jpeg_copy_critical_parameters_12 +#define jpeg_abort_compress jpeg_abort_compress_12 +#define jpeg_abort_decompress jpeg_abort_decompress_12 +#define jpeg_abort jpeg_abort_12 +#define jpeg_destroy jpeg_destroy_12 +#define jpeg_resync_to_restart jpeg_resync_to_restart_12 +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.doc concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#endif /* JPEGLIB_H */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jquant1.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jquant1.c new file mode 100644 index 000000000..56a3dc885 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jquant1.c @@ -0,0 +1,858 @@ +/* + * jquant1.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains 1-pass color quantization (color mapping) routines. + * These routines provide mapping to a fixed color map using equally spaced + * color values. Optional Floyd-Steinberg or ordered dithering is available. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#include "cpl_port.h" + +#ifdef QUANT_1PASS_SUPPORTED + + +/* + * The main purpose of 1-pass quantization is to provide a fast, if not very + * high quality, colormapped output capability. A 2-pass quantizer usually + * gives better visual quality; however, for quantized grayscale output this + * quantizer is perfectly adequate. Dithering is highly recommended with this + * quantizer, though you can turn it off if you really want to. + * + * In 1-pass quantization the colormap must be chosen in advance of seeing the + * image. We use a map consisting of all combinations of Ncolors[i] color + * values for the i'th component. The Ncolors[] values are chosen so that + * their product, the total number of colors, is no more than that requested. + * (In most cases, the product will be somewhat less.) + * + * Since the colormap is orthogonal, the representative value for each color + * component can be determined without considering the other components; + * then these indexes can be combined into a colormap index by a standard + * N-dimensional-array-subscript calculation. Most of the arithmetic involved + * can be precalculated and stored in the lookup table colorindex[]. + * colorindex[i][j] maps pixel value j in component i to the nearest + * representative value (grid plane) for that component; this index is + * multiplied by the array stride for component i, so that the + * index of the colormap entry closest to a given pixel value is just + * sum( colorindex[component-number][pixel-component-value] ) + * Aside from being fast, this scheme allows for variable spacing between + * representative values with no additional lookup cost. + * + * If gamma correction has been applied in color conversion, it might be wise + * to adjust the color grid spacing so that the representative colors are + * equidistant in linear space. At this writing, gamma correction is not + * implemented by jdcolor, so nothing is done here. + */ + + +/* Declarations for ordered dithering. + * + * We use a standard 16x16 ordered dither array. The basic concept of ordered + * dithering is described in many references, for instance Dale Schumacher's + * chapter II.2 of Graphics Gems II (James Arvo, ed. Academic Press, 1991). + * In place of Schumacher's comparisons against a "threshold" value, we add a + * "dither" value to the input pixel and then round the result to the nearest + * output value. The dither value is equivalent to (0.5 - threshold) times + * the distance between output values. For ordered dithering, we assume that + * the output colors are equally spaced; if not, results will probably be + * worse, since the dither may be too much or too little at a given point. + * + * The normal calculation would be to form pixel value + dither, range-limit + * this to 0..MAXJSAMPLE, and then index into the colorindex table as usual. + * We can skip the separate range-limiting step by extending the colorindex + * table in both directions. + */ + +#define ODITHER_SIZE 16 /* dimension of dither matrix */ +/* NB: if ODITHER_SIZE is not a power of 2, ODITHER_MASK uses will break */ +#define ODITHER_CELLS (ODITHER_SIZE*ODITHER_SIZE) /* # cells in matrix */ +#define ODITHER_MASK (ODITHER_SIZE-1) /* mask for wrapping around counters */ + +typedef int ODITHER_MATRIX[ODITHER_SIZE][ODITHER_SIZE]; +typedef int (*ODITHER_MATRIX_PTR)[ODITHER_SIZE]; + +static const UINT8 base_dither_matrix[ODITHER_SIZE][ODITHER_SIZE] = { + /* Bayer's order-4 dither array. Generated by the code given in + * Stephen Hawley's article "Ordered Dithering" in Graphics Gems I. + * The values in this array must range from 0 to ODITHER_CELLS-1. + */ + { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, + { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, + { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } +}; + + +/* Declarations for Floyd-Steinberg dithering. + * + * Errors are accumulated into the array fserrors[], at a resolution of + * 1/16th of a pixel count. The error at a given pixel is propagated + * to its not-yet-processed neighbors using the standard F-S fractions, + * ... (here) 7/16 + * 3/16 5/16 1/16 + * We work left-to-right on even rows, right-to-left on odd rows. + * + * We can get away with a single array (holding one row's worth of errors) + * by using it to store the current row's errors at pixel columns not yet + * processed, but the next row's errors at columns already processed. We + * need only a few extra variables to hold the errors immediately around the + * current column. (If we are lucky, those variables are in registers, but + * even if not, they're probably cheaper to access than array elements are.) + * + * The fserrors[] array is indexed [component#][position]. + * We provide (#columns + 2) entries per component; the extra entry at each + * end saves us from special-casing the first and last pixels. + * + * Note: on a wide image, we might not have enough room in a PC's near data + * segment to hold the error array; so it is allocated with alloc_large. + */ + +#if BITS_IN_JSAMPLE == 8 +typedef INT16 FSERROR; /* 16 bits should be enough */ +typedef int LOCFSERROR; /* use 'int' for calculation temps */ +#else +typedef INT32 FSERROR; /* may need more than 16 bits */ +typedef INT32 LOCFSERROR; /* be sure calculation temps are big enough */ +#endif + +typedef FSERROR FAR *FSERRPTR; /* pointer to error array (in FAR storage!) */ + + +/* Private subobject */ + +#define MAX_Q_COMPS 4 /* max components I can handle */ + +typedef struct { + struct jpeg_color_quantizer pub; /* public fields */ + + /* Initially allocated colormap is saved here */ + JSAMPARRAY sv_colormap; /* The color map as a 2-D pixel array */ + int sv_actual; /* number of entries in use */ + + JSAMPARRAY colorindex; /* Precomputed mapping for speed */ + /* colorindex[i][j] = index of color closest to pixel value j in component i, + * premultiplied as described above. Since colormap indexes must fit into + * JSAMPLEs, the entries of this array will too. + */ + boolean is_padded; /* is the colorindex padded for odither? */ + + int Ncolors[MAX_Q_COMPS]; /* # of values alloced to each component */ + + /* Variables for ordered dithering */ + int row_index; /* cur row's vertical index in dither matrix */ + ODITHER_MATRIX_PTR odither[MAX_Q_COMPS]; /* one dither array per component */ + + /* Variables for Floyd-Steinberg dithering */ + FSERRPTR fserrors[MAX_Q_COMPS]; /* accumulated errors */ + boolean on_odd_row; /* flag to remember which row we are on */ +} my_cquantizer; + +typedef my_cquantizer * my_cquantize_ptr; + + +/* + * Policy-making subroutines for create_colormap and create_colorindex. + * These routines determine the colormap to be used. The rest of the module + * only assumes that the colormap is orthogonal. + * + * * select_ncolors decides how to divvy up the available colors + * among the components. + * * output_value defines the set of representative values for a component. + * * largest_input_value defines the mapping from input values to + * representative values for a component. + * Note that the latter two routines may impose different policies for + * different components, though this is not currently done. + */ + + +LOCAL(int) +select_ncolors (j_decompress_ptr cinfo, int Ncolors[]) +/* Determine allocation of desired colors to components, */ +/* and fill in Ncolors[] array to indicate choice. */ +/* Return value is total number of colors (product of Ncolors[] values). */ +{ + int nc = cinfo->out_color_components; /* number of color components */ + int max_colors = cinfo->desired_number_of_colors; + int total_colors, iroot, i, j; + boolean changed; + long temp; + static const int RGB_order[3] = { RGB_GREEN, RGB_RED, RGB_BLUE }; + + /* We can allocate at least the nc'th root of max_colors per component. */ + /* Compute floor(nc'th root of max_colors). */ + iroot = 1; + do { + iroot++; + temp = iroot; /* set temp = iroot ** nc */ + for (i = 1; i < nc; i++) + temp *= iroot; + } while (temp <= (long) max_colors); /* repeat till iroot exceeds root */ + iroot--; /* now iroot = floor(root) */ + + /* Must have at least 2 color values per component */ + if (iroot < 2) + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, (int) temp); + + /* Initialize to iroot color values for each component */ + total_colors = 1; + for (i = 0; i < nc; i++) { + Ncolors[i] = iroot; + total_colors *= iroot; + } + /* We may be able to increment the count for one or more components without + * exceeding max_colors, though we know not all can be incremented. + * Sometimes, the first component can be incremented more than once! + * (Example: for 16 colors, we start at 2*2*2, go to 3*2*2, then 4*2*2.) + * In RGB colorspace, try to increment G first, then R, then B. + */ + do { + changed = FALSE; + for (i = 0; i < nc; i++) { + j = (cinfo->out_color_space == JCS_RGB ? RGB_order[i] : i); + /* calculate new total_colors if Ncolors[j] is incremented */ + temp = total_colors / Ncolors[j]; + temp *= Ncolors[j]+1; /* done in long arith to avoid oflo */ + if (temp > (long) max_colors) + break; /* won't fit, done with this pass */ + Ncolors[j]++; /* OK, apply the increment */ + total_colors = (int) temp; + changed = TRUE; + } + } while (changed); + + return total_colors; +} + + +LOCAL(int) +output_value (CPL_UNUSED j_decompress_ptr cinfo, CPL_UNUSED int ci, int j, int maxj) +/* Return j'th output value, where j will range from 0 to maxj */ +/* The output values must fall in 0..MAXJSAMPLE in increasing order */ +{ + /* We always provide values 0 and MAXJSAMPLE for each component; + * any additional values are equally spaced between these limits. + * (Forcing the upper and lower values to the limits ensures that + * dithering can't produce a color outside the selected gamut.) + */ + return (int) (((INT32) j * MAXJSAMPLE + maxj/2) / maxj); +} + + +LOCAL(int) +largest_input_value (CPL_UNUSED j_decompress_ptr cinfo, CPL_UNUSED int ci, int j, int maxj) +/* Return largest input value that should map to j'th output value */ +/* Must have largest(j=0) >= 0, and largest(j=maxj) >= MAXJSAMPLE */ +{ + /* Breakpoints are halfway between values returned by output_value */ + return (int) (((INT32) (2*j + 1) * MAXJSAMPLE + maxj) / (2*maxj)); +} + + +/* + * Create the colormap. + */ + +LOCAL(void) +create_colormap (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + JSAMPARRAY colormap; /* Created colormap */ + int total_colors; /* Number of distinct output colors */ + int i,j,k, nci, blksize, blkdist, ptr, val; + + /* Select number of colors for each component */ + total_colors = select_ncolors(cinfo, cquantize->Ncolors); + + /* Report selected color counts */ + if (cinfo->out_color_components == 3) + TRACEMS4(cinfo, 1, JTRC_QUANT_3_NCOLORS, + total_colors, cquantize->Ncolors[0], + cquantize->Ncolors[1], cquantize->Ncolors[2]); + else + TRACEMS1(cinfo, 1, JTRC_QUANT_NCOLORS, total_colors); + + /* Allocate and fill in the colormap. */ + /* The colors are ordered in the map in standard row-major order, */ + /* i.e. rightmost (highest-indexed) color changes most rapidly. */ + + colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) total_colors, (JDIMENSION) cinfo->out_color_components); + + /* blksize is number of adjacent repeated entries for a component */ + /* blkdist is distance between groups of identical entries for a component */ + blkdist = total_colors; + + for (i = 0; i < cinfo->out_color_components; i++) { + /* fill in colormap entries for i'th color component */ + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + blksize = blkdist / nci; + for (j = 0; j < nci; j++) { + /* Compute j'th output value (out of nci) for component */ + val = output_value(cinfo, i, j, nci-1); + /* Fill in all colormap entries that have this value of this component */ + for (ptr = j * blksize; ptr < total_colors; ptr += blkdist) { + /* fill in blksize entries beginning at ptr */ + for (k = 0; k < blksize; k++) + colormap[i][ptr+k] = (JSAMPLE) val; + } + } + blkdist = blksize; /* blksize of this color is blkdist of next */ + } + + /* Save the colormap in private storage, + * where it will survive color quantization mode changes. + */ + cquantize->sv_colormap = colormap; + cquantize->sv_actual = total_colors; +} + + +/* + * Create the color index table. + */ + +LOCAL(void) +create_colorindex (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + JSAMPROW indexptr; + int i,j,k, nci, blksize, val, pad; + + /* For ordered dither, we pad the color index tables by MAXJSAMPLE in + * each direction (input index values can be -MAXJSAMPLE .. 2*MAXJSAMPLE). + * This is not necessary in the other dithering modes. However, we + * flag whether it was done in case user changes dithering mode. + */ + if (cinfo->dither_mode == JDITHER_ORDERED) { + pad = MAXJSAMPLE*2; + cquantize->is_padded = TRUE; + } else { + pad = 0; + cquantize->is_padded = FALSE; + } + + cquantize->colorindex = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (MAXJSAMPLE+1 + pad), + (JDIMENSION) cinfo->out_color_components); + + /* blksize is number of adjacent repeated entries for a component */ + blksize = cquantize->sv_actual; + + for (i = 0; i < cinfo->out_color_components; i++) { + /* fill in colorindex entries for i'th color component */ + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + blksize = blksize / nci; + + /* adjust colorindex pointers to provide padding at negative indexes. */ + if (pad) + cquantize->colorindex[i] += MAXJSAMPLE; + + /* in loop, val = index of current output value, */ + /* and k = largest j that maps to current val */ + indexptr = cquantize->colorindex[i]; + val = 0; + k = largest_input_value(cinfo, i, 0, nci-1); + for (j = 0; j <= MAXJSAMPLE; j++) { + while (j > k) /* advance val if past boundary */ + k = largest_input_value(cinfo, i, ++val, nci-1); + /* premultiply so that no multiplication needed in main processing */ + indexptr[j] = (JSAMPLE) (val * blksize); + } + /* Pad at both ends if necessary */ + if (pad) + for (j = 1; j <= MAXJSAMPLE; j++) { + indexptr[-j] = indexptr[0]; + indexptr[MAXJSAMPLE+j] = indexptr[MAXJSAMPLE]; + } + } +} + + +/* + * Create an ordered-dither array for a component having ncolors + * distinct output values. + */ + +LOCAL(ODITHER_MATRIX_PTR) +make_odither_array (j_decompress_ptr cinfo, int ncolors) +{ + ODITHER_MATRIX_PTR odither; + int j,k; + INT32 num,den; + + odither = (ODITHER_MATRIX_PTR) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(ODITHER_MATRIX)); + /* The inter-value distance for this color is MAXJSAMPLE/(ncolors-1). + * Hence the dither value for the matrix cell with fill order f + * (f=0..N-1) should be (N-1-2*f)/(2*N) * MAXJSAMPLE/(ncolors-1). + * On 16-bit-int machine, be careful to avoid overflow. + */ + den = 2 * ODITHER_CELLS * ((INT32) (ncolors - 1)); + for (j = 0; j < ODITHER_SIZE; j++) { + for (k = 0; k < ODITHER_SIZE; k++) { + num = ((INT32) (ODITHER_CELLS-1 - 2*((int)base_dither_matrix[j][k]))) + * MAXJSAMPLE; + /* Ensure round towards zero despite C's lack of consistency + * about rounding negative values in integer division... + */ + odither[j][k] = (int) (num<0 ? -((-num)/den) : num/den); + } + } + return odither; +} + + +/* + * Create the ordered-dither tables. + * Components having the same number of representative colors may + * share a dither table. + */ + +LOCAL(void) +create_odither_tables (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + ODITHER_MATRIX_PTR odither; + int i, j, nci; + + for (i = 0; i < cinfo->out_color_components; i++) { + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + odither = NULL; /* search for matching prior component */ + for (j = 0; j < i; j++) { + if (nci == cquantize->Ncolors[j]) { + odither = cquantize->odither[j]; + break; + } + } + if (odither == NULL) /* need a new table? */ + odither = make_odither_array(cinfo, nci); + cquantize->odither[i] = odither; + } +} + + +/* + * Map some rows of pixels to the output colormapped representation. + */ + +METHODDEF(void) +color_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* General case, no dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + JSAMPARRAY colorindex = cquantize->colorindex; + register int pixcode, ci; + register JSAMPROW ptrin, ptrout; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + register int nc = cinfo->out_color_components; + + for (row = 0; row < num_rows; row++) { + ptrin = input_buf[row]; + ptrout = output_buf[row]; + for (col = width; col > 0; col--) { + pixcode = 0; + for (ci = 0; ci < nc; ci++) { + pixcode += GETJSAMPLE(colorindex[ci][GETJSAMPLE(*ptrin++)]); + } + *ptrout++ = (JSAMPLE) pixcode; + } + } +} + + +METHODDEF(void) +color_quantize3 (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* Fast path for out_color_components==3, no dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register int pixcode; + register JSAMPROW ptrin, ptrout; + JSAMPROW colorindex0 = cquantize->colorindex[0]; + JSAMPROW colorindex1 = cquantize->colorindex[1]; + JSAMPROW colorindex2 = cquantize->colorindex[2]; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + ptrin = input_buf[row]; + ptrout = output_buf[row]; + for (col = width; col > 0; col--) { + pixcode = GETJSAMPLE(colorindex0[GETJSAMPLE(*ptrin++)]); + pixcode += GETJSAMPLE(colorindex1[GETJSAMPLE(*ptrin++)]); + pixcode += GETJSAMPLE(colorindex2[GETJSAMPLE(*ptrin++)]); + *ptrout++ = (JSAMPLE) pixcode; + } + } +} + + +METHODDEF(void) +quantize_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* General case, with ordered dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register JSAMPROW input_ptr; + register JSAMPROW output_ptr; + JSAMPROW colorindex_ci; + int * dither; /* points to active row of dither matrix */ + int row_index, col_index; /* current indexes into dither matrix */ + int nc = cinfo->out_color_components; + int ci; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + /* Initialize output values to 0 so can process components separately */ + jzero_far((void FAR *) output_buf[row], + (size_t) (width * SIZEOF(JSAMPLE))); + row_index = cquantize->row_index; + for (ci = 0; ci < nc; ci++) { + input_ptr = input_buf[row] + ci; + output_ptr = output_buf[row]; + colorindex_ci = cquantize->colorindex[ci]; + dither = cquantize->odither[ci][row_index]; + col_index = 0; + + for (col = width; col > 0; col--) { + /* Form pixel value + dither, range-limit to 0..MAXJSAMPLE, + * select output value, accumulate into output code for this pixel. + * Range-limiting need not be done explicitly, as we have extended + * the colorindex table to produce the right answers for out-of-range + * inputs. The maximum dither is +- MAXJSAMPLE; this sets the + * required amount of padding. + */ + *output_ptr += colorindex_ci[GETJSAMPLE(*input_ptr)+dither[col_index]]; + input_ptr += nc; + output_ptr++; + col_index = (col_index + 1) & ODITHER_MASK; + } + } + /* Advance row index for next row */ + row_index = (row_index + 1) & ODITHER_MASK; + cquantize->row_index = row_index; + } +} + + +METHODDEF(void) +quantize3_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* Fast path for out_color_components==3, with ordered dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register int pixcode; + register JSAMPROW input_ptr; + register JSAMPROW output_ptr; + JSAMPROW colorindex0 = cquantize->colorindex[0]; + JSAMPROW colorindex1 = cquantize->colorindex[1]; + JSAMPROW colorindex2 = cquantize->colorindex[2]; + int * dither0; /* points to active row of dither matrix */ + int * dither1; + int * dither2; + int row_index, col_index; /* current indexes into dither matrix */ + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + row_index = cquantize->row_index; + input_ptr = input_buf[row]; + output_ptr = output_buf[row]; + dither0 = cquantize->odither[0][row_index]; + dither1 = cquantize->odither[1][row_index]; + dither2 = cquantize->odither[2][row_index]; + col_index = 0; + + for (col = width; col > 0; col--) { + pixcode = GETJSAMPLE(colorindex0[GETJSAMPLE(*input_ptr++) + + dither0[col_index]]); + pixcode += GETJSAMPLE(colorindex1[GETJSAMPLE(*input_ptr++) + + dither1[col_index]]); + pixcode += GETJSAMPLE(colorindex2[GETJSAMPLE(*input_ptr++) + + dither2[col_index]]); + *output_ptr++ = (JSAMPLE) pixcode; + col_index = (col_index + 1) & ODITHER_MASK; + } + row_index = (row_index + 1) & ODITHER_MASK; + cquantize->row_index = row_index; + } +} + + +METHODDEF(void) +quantize_fs_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* General case, with Floyd-Steinberg dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register LOCFSERROR cur; /* current error or pixel value */ + LOCFSERROR belowerr; /* error for pixel below cur */ + LOCFSERROR bpreverr; /* error for below/prev col */ + LOCFSERROR bnexterr; /* error for below/next col */ + LOCFSERROR delta; + register FSERRPTR errorptr; /* => fserrors[] at column before current */ + register JSAMPROW input_ptr; + register JSAMPROW output_ptr; + JSAMPROW colorindex_ci; + JSAMPROW colormap_ci; + int pixcode; + int nc = cinfo->out_color_components; + int dir; /* 1 for left-to-right, -1 for right-to-left */ + int dirnc; /* dir * nc */ + int ci; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + JSAMPLE *range_limit = cinfo->sample_range_limit; + SHIFT_TEMPS + + for (row = 0; row < num_rows; row++) { + /* Initialize output values to 0 so can process components separately */ + jzero_far((void FAR *) output_buf[row], + (size_t) (width * SIZEOF(JSAMPLE))); + for (ci = 0; ci < nc; ci++) { + input_ptr = input_buf[row] + ci; + output_ptr = output_buf[row]; + if (cquantize->on_odd_row) { + /* work right to left in this row */ + input_ptr += (width-1) * nc; /* so point to rightmost pixel */ + output_ptr += width-1; + dir = -1; + dirnc = -nc; + errorptr = cquantize->fserrors[ci] + (width+1); /* => entry after last column */ + } else { + /* work left to right in this row */ + dir = 1; + dirnc = nc; + errorptr = cquantize->fserrors[ci]; /* => entry before first column */ + } + colorindex_ci = cquantize->colorindex[ci]; + colormap_ci = cquantize->sv_colormap[ci]; + /* Preset error values: no error propagated to first pixel from left */ + cur = 0; + /* and no error propagated to row below yet */ + belowerr = bpreverr = 0; + + for (col = width; col > 0; col--) { + /* cur holds the error propagated from the previous pixel on the + * current line. Add the error propagated from the previous line + * to form the complete error correction term for this pixel, and + * round the error term (which is expressed * 16) to an integer. + * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct + * for either sign of the error value. + * Note: errorptr points to *previous* column's array entry. + */ + cur = RIGHT_SHIFT(cur + errorptr[dir] + 8, 4); + /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. + * The maximum error is +- MAXJSAMPLE; this sets the required size + * of the range_limit array. + */ + cur += GETJSAMPLE(*input_ptr); + cur = GETJSAMPLE(range_limit[cur]); + /* Select output value, accumulate into output code for this pixel */ + pixcode = GETJSAMPLE(colorindex_ci[cur]); + *output_ptr += (JSAMPLE) pixcode; + /* Compute actual representation error at this pixel */ + /* Note: we can do this even though we don't have the final */ + /* pixel code, because the colormap is orthogonal. */ + cur -= GETJSAMPLE(colormap_ci[pixcode]); + /* Compute error fractions to be propagated to adjacent pixels. + * Add these into the running sums, and simultaneously shift the + * next-line error sums left by 1 column. + */ + bnexterr = cur; + delta = cur * 2; + cur += delta; /* form error * 3 */ + errorptr[0] = (FSERROR) (bpreverr + cur); + cur += delta; /* form error * 5 */ + bpreverr = belowerr + cur; + belowerr = bnexterr; + cur += delta; /* form error * 7 */ + /* At this point cur contains the 7/16 error value to be propagated + * to the next pixel on the current line, and all the errors for the + * next line have been shifted over. We are therefore ready to move on. + */ + input_ptr += dirnc; /* advance input ptr to next column */ + output_ptr += dir; /* advance output ptr to next column */ + errorptr += dir; /* advance errorptr to current column */ + } + /* Post-loop cleanup: we must unload the final error value into the + * final fserrors[] entry. Note we need not unload belowerr because + * it is for the dummy column before or after the actual array. + */ + errorptr[0] = (FSERROR) bpreverr; /* unload prev err into array */ + } + cquantize->on_odd_row = (cquantize->on_odd_row ? FALSE : TRUE); + } +} + + +/* + * Allocate workspace for Floyd-Steinberg errors. + */ + +LOCAL(void) +alloc_fs_workspace (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + size_t arraysize; + int i; + + arraysize = (size_t) ((cinfo->output_width + 2) * SIZEOF(FSERROR)); + for (i = 0; i < cinfo->out_color_components; i++) { + cquantize->fserrors[i] = (FSERRPTR) + (*cinfo->mem->alloc_large)((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize); + } +} + + +/* + * Initialize for one-pass color quantization. + */ + +METHODDEF(void) +start_pass_1_quant (j_decompress_ptr cinfo, CPL_UNUSED boolean is_pre_scan) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + size_t arraysize; + int i; + + /* Install my colormap. */ + cinfo->colormap = cquantize->sv_colormap; + cinfo->actual_number_of_colors = cquantize->sv_actual; + + /* Initialize for desired dithering mode. */ + switch (cinfo->dither_mode) { + case JDITHER_NONE: + if (cinfo->out_color_components == 3) + cquantize->pub.color_quantize = color_quantize3; + else + cquantize->pub.color_quantize = color_quantize; + break; + case JDITHER_ORDERED: + if (cinfo->out_color_components == 3) + cquantize->pub.color_quantize = quantize3_ord_dither; + else + cquantize->pub.color_quantize = quantize_ord_dither; + cquantize->row_index = 0; /* initialize state for ordered dither */ + /* If user changed to ordered dither from another mode, + * we must recreate the color index table with padding. + * This will cost extra space, but probably isn't very likely. + */ + if (! cquantize->is_padded) + create_colorindex(cinfo); + /* Create ordered-dither tables if we didn't already. */ + if (cquantize->odither[0] == NULL) + create_odither_tables(cinfo); + break; + case JDITHER_FS: + cquantize->pub.color_quantize = quantize_fs_dither; + cquantize->on_odd_row = FALSE; /* initialize state for F-S dither */ + /* Allocate Floyd-Steinberg workspace if didn't already. */ + if (cquantize->fserrors[0] == NULL) + alloc_fs_workspace(cinfo); + /* Initialize the propagated errors to zero. */ + arraysize = (size_t) ((cinfo->output_width + 2) * SIZEOF(FSERROR)); + for (i = 0; i < cinfo->out_color_components; i++) + jzero_far((void FAR *) cquantize->fserrors[i], arraysize); + break; + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } +} + + +/* + * Finish up at the end of the pass. + */ + +METHODDEF(void) +finish_pass_1_quant (CPL_UNUSED j_decompress_ptr cinfo) +{ + /* no work in 1-pass case */ +} + + +/* + * Switch to a new external colormap between output passes. + * Shouldn't get to this module! + */ + +METHODDEF(void) +new_color_map_1_quant (j_decompress_ptr cinfo) +{ + ERREXIT(cinfo, JERR_MODE_CHANGE); +} + + +/* + * Module initialization routine for 1-pass color quantization. + */ + +GLOBAL(void) +jinit_1pass_quantizer (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize; + + cquantize = (my_cquantize_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_cquantizer)); + cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize; + cquantize->pub.start_pass = start_pass_1_quant; + cquantize->pub.finish_pass = finish_pass_1_quant; + cquantize->pub.new_color_map = new_color_map_1_quant; + cquantize->fserrors[0] = NULL; /* Flag FS workspace not allocated */ + cquantize->odither[0] = NULL; /* Also flag odither arrays not allocated */ + + /* Make sure my internal arrays won't overflow */ + if (cinfo->out_color_components > MAX_Q_COMPS) + ERREXIT1(cinfo, JERR_QUANT_COMPONENTS, MAX_Q_COMPS); + /* Make sure colormap indexes can be represented by JSAMPLEs */ + if (cinfo->desired_number_of_colors > (MAXJSAMPLE+1)) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXJSAMPLE+1); + + /* Create the colormap and color index table. */ + create_colormap(cinfo); + create_colorindex(cinfo); + + /* Allocate Floyd-Steinberg workspace now if requested. + * We do this now since it is FAR storage and may affect the memory + * manager's space calculations. If the user changes to FS dither + * mode in a later pass, we will allocate the space then, and will + * possibly overrun the max_memory_to_use setting. + */ + if (cinfo->dither_mode == JDITHER_FS) + alloc_fs_workspace(cinfo); +} + +#endif /* QUANT_1PASS_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jquant2.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jquant2.c new file mode 100644 index 000000000..f9f597b0e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jquant2.c @@ -0,0 +1,1312 @@ +/* + * jquant2.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains 2-pass color quantization (color mapping) routines. + * These routines provide selection of a custom color map for an image, + * followed by mapping of the image to that color map, with optional + * Floyd-Steinberg dithering. + * It is also possible to use just the second pass to map to an arbitrary + * externally-given color map. + * + * Note: ordered dithering is not supported, since there isn't any fast + * way to compute intercolor distances; it's unclear that ordered dither's + * fundamental assumptions even hold with an irregularly spaced color map. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#include "cpl_port.h" + +#ifdef QUANT_2PASS_SUPPORTED + + +/* + * This module implements the well-known Heckbert paradigm for color + * quantization. Most of the ideas used here can be traced back to + * Heckbert's seminal paper + * Heckbert, Paul. "Color Image Quantization for Frame Buffer Display", + * Proc. SIGGRAPH '82, Computer Graphics v.16 #3 (July 1982), pp 297-304. + * + * In the first pass over the image, we accumulate a histogram showing the + * usage count of each possible color. To keep the histogram to a reasonable + * size, we reduce the precision of the input; typical practice is to retain + * 5 or 6 bits per color, so that 8 or 4 different input values are counted + * in the same histogram cell. + * + * Next, the color-selection step begins with a box representing the whole + * color space, and repeatedly splits the "largest" remaining box until we + * have as many boxes as desired colors. Then the mean color in each + * remaining box becomes one of the possible output colors. + * + * The second pass over the image maps each input pixel to the closest output + * color (optionally after applying a Floyd-Steinberg dithering correction). + * This mapping is logically trivial, but making it go fast enough requires + * considerable care. + * + * Heckbert-style quantizers vary a good deal in their policies for choosing + * the "largest" box and deciding where to cut it. The particular policies + * used here have proved out well in experimental comparisons, but better ones + * may yet be found. + * + * In earlier versions of the IJG code, this module quantized in YCbCr color + * space, processing the raw upsampled data without a color conversion step. + * This allowed the color conversion math to be done only once per colormap + * entry, not once per pixel. However, that optimization precluded other + * useful optimizations (such as merging color conversion with upsampling) + * and it also interfered with desired capabilities such as quantizing to an + * externally-supplied colormap. We have therefore abandoned that approach. + * The present code works in the post-conversion color space, typically RGB. + * + * To improve the visual quality of the results, we actually work in scaled + * RGB space, giving G distances more weight than R, and R in turn more than + * B. To do everything in integer math, we must use integer scale factors. + * The 2/3/1 scale factors used here correspond loosely to the relative + * weights of the colors in the NTSC grayscale equation. + * If you want to use this code to quantize a non-RGB color space, you'll + * probably need to change these scale factors. + */ + +#define R_SCALE 2 /* scale R distances by this much */ +#define G_SCALE 3 /* scale G distances by this much */ +#define B_SCALE 1 /* and B by this much */ + +/* Relabel R/G/B as components 0/1/2, respecting the RGB ordering defined + * in jmorecfg.h. As the code stands, it will do the right thing for R,G,B + * and B,G,R orders. If you define some other weird order in jmorecfg.h, + * you'll get compile errors until you extend this logic. In that case + * you'll probably want to tweak the histogram sizes too. + */ + +#if RGB_RED == 0 +#define C0_SCALE R_SCALE +#endif +#if RGB_BLUE == 0 +#define C0_SCALE B_SCALE +#endif +#if RGB_GREEN == 1 +#define C1_SCALE G_SCALE +#endif +#if RGB_RED == 2 +#define C2_SCALE R_SCALE +#endif +#if RGB_BLUE == 2 +#define C2_SCALE B_SCALE +#endif + + +/* + * First we have the histogram data structure and routines for creating it. + * + * The number of bits of precision can be adjusted by changing these symbols. + * We recommend keeping 6 bits for G and 5 each for R and B. + * If you have plenty of memory and cycles, 6 bits all around gives marginally + * better results; if you are short of memory, 5 bits all around will save + * some space but degrade the results. + * To maintain a fully accurate histogram, we'd need to allocate a "long" + * (preferably unsigned long) for each cell. In practice this is overkill; + * we can get by with 16 bits per cell. Few of the cell counts will overflow, + * and clamping those that do overflow to the maximum value will give close- + * enough results. This reduces the recommended histogram size from 256Kb + * to 128Kb, which is a useful savings on PC-class machines. + * (In the second pass the histogram space is re-used for pixel mapping data; + * in that capacity, each cell must be able to store zero to the number of + * desired colors. 16 bits/cell is plenty for that too.) + * Since the JPEG code is intended to run in small memory model on 80x86 + * machines, we can't just allocate the histogram in one chunk. Instead + * of a true 3-D array, we use a row of pointers to 2-D arrays. Each + * pointer corresponds to a C0 value (typically 2^5 = 32 pointers) and + * each 2-D array has 2^6*2^5 = 2048 or 2^6*2^6 = 4096 entries. Note that + * on 80x86 machines, the pointer row is in near memory but the actual + * arrays are in far memory (same arrangement as we use for image arrays). + */ + +#define MAXNUMCOLORS (MAXJSAMPLE+1) /* maximum size of colormap */ + +/* These will do the right thing for either R,G,B or B,G,R color order, + * but you may not like the results for other color orders. + */ +#define HIST_C0_BITS 5 /* bits of precision in R/B histogram */ +#define HIST_C1_BITS 6 /* bits of precision in G histogram */ +#define HIST_C2_BITS 5 /* bits of precision in B/R histogram */ + +/* Number of elements along histogram axes. */ +#define HIST_C0_ELEMS (1<cquantize; + register JSAMPROW ptr; + register histptr histp; + register hist3d histogram = cquantize->histogram; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + ptr = input_buf[row]; + for (col = width; col > 0; col--) { + /* get pixel value and index into the histogram */ + histp = & histogram[GETJSAMPLE(ptr[0]) >> C0_SHIFT] + [GETJSAMPLE(ptr[1]) >> C1_SHIFT] + [GETJSAMPLE(ptr[2]) >> C2_SHIFT]; + /* increment, check for overflow and undo increment if so. */ + if (++(*histp) <= 0) + (*histp)--; + ptr += 3; + } + } +} + + +/* + * Next we have the really interesting routines: selection of a colormap + * given the completed histogram. + * These routines work with a list of "boxes", each representing a rectangular + * subset of the input color space (to histogram precision). + */ + +typedef struct { + /* The bounds of the box (inclusive); expressed as histogram indexes */ + int c0min, c0max; + int c1min, c1max; + int c2min, c2max; + /* The volume (actually 2-norm) of the box */ + INT32 volume; + /* The number of nonzero histogram cells within this box */ + long colorcount; +} box; + +typedef box * boxptr; + + +LOCAL(boxptr) +find_biggest_color_pop (boxptr boxlist, int numboxes) +/* Find the splittable box with the largest color population */ +/* Returns NULL if no splittable boxes remain */ +{ + register boxptr boxp; + register int i; + register long maxc = 0; + boxptr which = NULL; + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { + if (boxp->colorcount > maxc && boxp->volume > 0) { + which = boxp; + maxc = boxp->colorcount; + } + } + return which; +} + + +LOCAL(boxptr) +find_biggest_volume (boxptr boxlist, int numboxes) +/* Find the splittable box with the largest (scaled) volume */ +/* Returns NULL if no splittable boxes remain */ +{ + register boxptr boxp; + register int i; + register INT32 maxv = 0; + boxptr which = NULL; + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { + if (boxp->volume > maxv) { + which = boxp; + maxv = boxp->volume; + } + } + return which; +} + + +LOCAL(void) +update_box (j_decompress_ptr cinfo, boxptr boxp) +/* Shrink the min/max bounds of a box to enclose only nonzero elements, */ +/* and recompute its volume and population */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + histptr histp; + int c0,c1,c2; + int c0min,c0max,c1min,c1max,c2min,c2max; + INT32 dist0,dist1,dist2; + long ccount; + + c0min = boxp->c0min; c0max = boxp->c0max; + c1min = boxp->c1min; c1max = boxp->c1max; + c2min = boxp->c2min; c2max = boxp->c2max; + + if (c0max > c0min) + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c0min = c0min = c0; + goto have_c0min; + } + } + have_c0min: + if (c0max > c0min) + for (c0 = c0max; c0 >= c0min; c0--) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c0max = c0max = c0; + goto have_c0max; + } + } + have_c0max: + if (c1max > c1min) + for (c1 = c1min; c1 <= c1max; c1++) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c1min = c1min = c1; + goto have_c1min; + } + } + have_c1min: + if (c1max > c1min) + for (c1 = c1max; c1 >= c1min; c1--) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c1max = c1max = c1; + goto have_c1max; + } + } + have_c1max: + if (c2max > c2min) + for (c2 = c2min; c2 <= c2max; c2++) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1min][c2]; + for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) + if (*histp != 0) { + boxp->c2min = c2min = c2; + goto have_c2min; + } + } + have_c2min: + if (c2max > c2min) + for (c2 = c2max; c2 >= c2min; c2--) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1min][c2]; + for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) + if (*histp != 0) { + boxp->c2max = c2max = c2; + goto have_c2max; + } + } + have_c2max: + + /* Update box volume. + * We use 2-norm rather than real volume here; this biases the method + * against making long narrow boxes, and it has the side benefit that + * a box is splittable iff norm > 0. + * Since the differences are expressed in histogram-cell units, + * we have to shift back to JSAMPLE units to get consistent distances; + * after which, we scale according to the selected distance scale factors. + */ + dist0 = ((c0max - c0min) << C0_SHIFT) * C0_SCALE; + dist1 = ((c1max - c1min) << C1_SHIFT) * C1_SCALE; + dist2 = ((c2max - c2min) << C2_SHIFT) * C2_SCALE; + boxp->volume = dist0*dist0 + dist1*dist1 + dist2*dist2; + + /* Now scan remaining volume of box and compute population */ + ccount = 0; + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++, histp++) + if (*histp != 0) { + ccount++; + } + } + boxp->colorcount = ccount; +} + + +LOCAL(int) +median_cut (j_decompress_ptr cinfo, boxptr boxlist, int numboxes, + int desired_colors) +/* Repeatedly select and split the largest box until we have enough boxes */ +{ + int n,lb; + int c0,c1,c2,cmax; + register boxptr b1,b2; + + while (numboxes < desired_colors) { + /* Select box to split. + * Current algorithm: by population for first half, then by volume. + */ + if (numboxes*2 <= desired_colors) { + b1 = find_biggest_color_pop(boxlist, numboxes); + } else { + b1 = find_biggest_volume(boxlist, numboxes); + } + if (b1 == NULL) /* no splittable boxes left! */ + break; + b2 = &boxlist[numboxes]; /* where new box will go */ + /* Copy the color bounds to the new box. */ + b2->c0max = b1->c0max; b2->c1max = b1->c1max; b2->c2max = b1->c2max; + b2->c0min = b1->c0min; b2->c1min = b1->c1min; b2->c2min = b1->c2min; + /* Choose which axis to split the box on. + * Current algorithm: longest scaled axis. + * See notes in update_box about scaling distances. + */ + c0 = ((b1->c0max - b1->c0min) << C0_SHIFT) * C0_SCALE; + c1 = ((b1->c1max - b1->c1min) << C1_SHIFT) * C1_SCALE; + c2 = ((b1->c2max - b1->c2min) << C2_SHIFT) * C2_SCALE; + /* We want to break any ties in favor of green, then red, blue last. + * This code does the right thing for R,G,B or B,G,R color orders only. + */ +#if RGB_RED == 0 + cmax = c1; n = 1; + if (c0 > cmax) { cmax = c0; n = 0; } + if (c2 > cmax) { n = 2; } +#else + cmax = c1; n = 1; + if (c2 > cmax) { cmax = c2; n = 2; } + if (c0 > cmax) { n = 0; } +#endif + /* Choose split point along selected axis, and update box bounds. + * Current algorithm: split at halfway point. + * (Since the box has been shrunk to minimum volume, + * any split will produce two nonempty subboxes.) + * Note that lb value is max for lower box, so must be < old max. + */ + switch (n) { + case 0: + lb = (b1->c0max + b1->c0min) / 2; + b1->c0max = lb; + b2->c0min = lb+1; + break; + case 1: + lb = (b1->c1max + b1->c1min) / 2; + b1->c1max = lb; + b2->c1min = lb+1; + break; + case 2: + lb = (b1->c2max + b1->c2min) / 2; + b1->c2max = lb; + b2->c2min = lb+1; + break; + } + /* Update stats for boxes */ + update_box(cinfo, b1); + update_box(cinfo, b2); + numboxes++; + } + return numboxes; +} + + +LOCAL(void) +compute_color (j_decompress_ptr cinfo, boxptr boxp, int icolor) +/* Compute representative color for a box, put it in colormap[icolor] */ +{ + /* Current algorithm: mean weighted by pixels (not colors) */ + /* Note it is important to get the rounding correct! */ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + histptr histp; + int c0,c1,c2; + int c0min,c0max,c1min,c1max,c2min,c2max; + long count; + long total = 0; + long c0total = 0; + long c1total = 0; + long c2total = 0; + + c0min = boxp->c0min; c0max = boxp->c0max; + c1min = boxp->c1min; c1max = boxp->c1max; + c2min = boxp->c2min; c2max = boxp->c2max; + + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) { + if ((count = *histp++) != 0) { + total += count; + c0total += ((c0 << C0_SHIFT) + ((1<>1)) * count; + c1total += ((c1 << C1_SHIFT) + ((1<>1)) * count; + c2total += ((c2 << C2_SHIFT) + ((1<>1)) * count; + } + } + } + + cinfo->colormap[0][icolor] = (JSAMPLE) ((c0total + (total>>1)) / total); + cinfo->colormap[1][icolor] = (JSAMPLE) ((c1total + (total>>1)) / total); + cinfo->colormap[2][icolor] = (JSAMPLE) ((c2total + (total>>1)) / total); +} + + +LOCAL(void) +select_colors (j_decompress_ptr cinfo, int desired_colors) +/* Master routine for color selection */ +{ + boxptr boxlist; + int numboxes; + int i; + + /* Allocate workspace for box list */ + boxlist = (boxptr) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, desired_colors * SIZEOF(box)); + /* Initialize one box containing whole space */ + numboxes = 1; + boxlist[0].c0min = 0; + boxlist[0].c0max = MAXJSAMPLE >> C0_SHIFT; + boxlist[0].c1min = 0; + boxlist[0].c1max = MAXJSAMPLE >> C1_SHIFT; + boxlist[0].c2min = 0; + boxlist[0].c2max = MAXJSAMPLE >> C2_SHIFT; + /* Shrink it to actually-used volume and set its statistics */ + update_box(cinfo, & boxlist[0]); + /* Perform median-cut to produce final box list */ + numboxes = median_cut(cinfo, boxlist, numboxes, desired_colors); + /* Compute the representative color for each box, fill colormap */ + for (i = 0; i < numboxes; i++) + compute_color(cinfo, & boxlist[i], i); + cinfo->actual_number_of_colors = numboxes; + TRACEMS1(cinfo, 1, JTRC_QUANT_SELECTED, numboxes); +} + + +/* + * These routines are concerned with the time-critical task of mapping input + * colors to the nearest color in the selected colormap. + * + * We re-use the histogram space as an "inverse color map", essentially a + * cache for the results of nearest-color searches. All colors within a + * histogram cell will be mapped to the same colormap entry, namely the one + * closest to the cell's center. This may not be quite the closest entry to + * the actual input color, but it's almost as good. A zero in the cache + * indicates we haven't found the nearest color for that cell yet; the array + * is cleared to zeroes before starting the mapping pass. When we find the + * nearest color for a cell, its colormap index plus one is recorded in the + * cache for future use. The pass2 scanning routines call fill_inverse_cmap + * when they need to use an unfilled entry in the cache. + * + * Our method of efficiently finding nearest colors is based on the "locally + * sorted search" idea described by Heckbert and on the incremental distance + * calculation described by Spencer W. Thomas in chapter III.1 of Graphics + * Gems II (James Arvo, ed. Academic Press, 1991). Thomas points out that + * the distances from a given colormap entry to each cell of the histogram can + * be computed quickly using an incremental method: the differences between + * distances to adjacent cells themselves differ by a constant. This allows a + * fairly fast implementation of the "brute force" approach of computing the + * distance from every colormap entry to every histogram cell. Unfortunately, + * it needs a work array to hold the best-distance-so-far for each histogram + * cell (because the inner loop has to be over cells, not colormap entries). + * The work array elements have to be INT32s, so the work array would need + * 256Kb at our recommended precision. This is not feasible in DOS machines. + * + * To get around these problems, we apply Thomas' method to compute the + * nearest colors for only the cells within a small subbox of the histogram. + * The work array need be only as big as the subbox, so the memory usage + * problem is solved. Furthermore, we need not fill subboxes that are never + * referenced in pass2; many images use only part of the color gamut, so a + * fair amount of work is saved. An additional advantage of this + * approach is that we can apply Heckbert's locality criterion to quickly + * eliminate colormap entries that are far away from the subbox; typically + * three-fourths of the colormap entries are rejected by Heckbert's criterion, + * and we need not compute their distances to individual cells in the subbox. + * The speed of this approach is heavily influenced by the subbox size: too + * small means too much overhead, too big loses because Heckbert's criterion + * can't eliminate as many colormap entries. Empirically the best subbox + * size seems to be about 1/512th of the histogram (1/8th in each direction). + * + * Thomas' article also describes a refined method which is asymptotically + * faster than the brute-force method, but it is also far more complex and + * cannot efficiently be applied to small subboxes. It is therefore not + * useful for programs intended to be portable to DOS machines. On machines + * with plenty of memory, filling the whole histogram in one shot with Thomas' + * refined method might be faster than the present code --- but then again, + * it might not be any faster, and it's certainly more complicated. + */ + + +/* log2(histogram cells in update box) for each axis; this can be adjusted */ +#define BOX_C0_LOG (HIST_C0_BITS-3) +#define BOX_C1_LOG (HIST_C1_BITS-3) +#define BOX_C2_LOG (HIST_C2_BITS-3) + +#define BOX_C0_ELEMS (1<actual_number_of_colors; + int maxc0, maxc1, maxc2; + int centerc0, centerc1, centerc2; + int i, x, ncolors; + INT32 minmaxdist, min_dist, max_dist, tdist; + INT32 mindist[MAXNUMCOLORS]; /* min distance to colormap entry i */ + + /* Compute true coordinates of update box's upper corner and center. + * Actually we compute the coordinates of the center of the upper-corner + * histogram cell, which are the upper bounds of the volume we care about. + * Note that since ">>" rounds down, the "center" values may be closer to + * min than to max; hence comparisons to them must be "<=", not "<". + */ + maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT)); + centerc0 = (minc0 + maxc0) >> 1; + maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT)); + centerc1 = (minc1 + maxc1) >> 1; + maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT)); + centerc2 = (minc2 + maxc2) >> 1; + + /* For each color in colormap, find: + * 1. its minimum squared-distance to any point in the update box + * (zero if color is within update box); + * 2. its maximum squared-distance to any point in the update box. + * Both of these can be found by considering only the corners of the box. + * We save the minimum distance for each color in mindist[]; + * only the smallest maximum distance is of interest. + */ + minmaxdist = 0x7FFFFFFFL; + + for (i = 0; i < numcolors; i++) { + /* We compute the squared-c0-distance term, then add in the other two. */ + x = GETJSAMPLE(cinfo->colormap[0][i]); + if (x < minc0) { + tdist = (x - minc0) * C0_SCALE; + min_dist = tdist*tdist; + tdist = (x - maxc0) * C0_SCALE; + max_dist = tdist*tdist; + } else if (x > maxc0) { + tdist = (x - maxc0) * C0_SCALE; + min_dist = tdist*tdist; + tdist = (x - minc0) * C0_SCALE; + max_dist = tdist*tdist; + } else { + /* within cell range so no contribution to min_dist */ + min_dist = 0; + if (x <= centerc0) { + tdist = (x - maxc0) * C0_SCALE; + max_dist = tdist*tdist; + } else { + tdist = (x - minc0) * C0_SCALE; + max_dist = tdist*tdist; + } + } + + x = GETJSAMPLE(cinfo->colormap[1][i]); + if (x < minc1) { + tdist = (x - minc1) * C1_SCALE; + min_dist += tdist*tdist; + tdist = (x - maxc1) * C1_SCALE; + max_dist += tdist*tdist; + } else if (x > maxc1) { + tdist = (x - maxc1) * C1_SCALE; + min_dist += tdist*tdist; + tdist = (x - minc1) * C1_SCALE; + max_dist += tdist*tdist; + } else { + /* within cell range so no contribution to min_dist */ + if (x <= centerc1) { + tdist = (x - maxc1) * C1_SCALE; + max_dist += tdist*tdist; + } else { + tdist = (x - minc1) * C1_SCALE; + max_dist += tdist*tdist; + } + } + + x = GETJSAMPLE(cinfo->colormap[2][i]); + if (x < minc2) { + tdist = (x - minc2) * C2_SCALE; + min_dist += tdist*tdist; + tdist = (x - maxc2) * C2_SCALE; + max_dist += tdist*tdist; + } else if (x > maxc2) { + tdist = (x - maxc2) * C2_SCALE; + min_dist += tdist*tdist; + tdist = (x - minc2) * C2_SCALE; + max_dist += tdist*tdist; + } else { + /* within cell range so no contribution to min_dist */ + if (x <= centerc2) { + tdist = (x - maxc2) * C2_SCALE; + max_dist += tdist*tdist; + } else { + tdist = (x - minc2) * C2_SCALE; + max_dist += tdist*tdist; + } + } + + mindist[i] = min_dist; /* save away the results */ + if (max_dist < minmaxdist) + minmaxdist = max_dist; + } + + /* Now we know that no cell in the update box is more than minmaxdist + * away from some colormap entry. Therefore, only colors that are + * within minmaxdist of some part of the box need be considered. + */ + ncolors = 0; + for (i = 0; i < numcolors; i++) { + if (mindist[i] <= minmaxdist) + colorlist[ncolors++] = (JSAMPLE) i; + } + return ncolors; +} + + +LOCAL(void) +find_best_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, + int numcolors, JSAMPLE colorlist[], JSAMPLE bestcolor[]) +/* Find the closest colormap entry for each cell in the update box, + * given the list of candidate colors prepared by find_nearby_colors. + * Return the indexes of the closest entries in the bestcolor[] array. + * This routine uses Thomas' incremental distance calculation method to + * find the distance from a colormap entry to successive cells in the box. + */ +{ + int ic0, ic1, ic2; + int i, icolor; + register INT32 * bptr; /* pointer into bestdist[] array */ + JSAMPLE * cptr; /* pointer into bestcolor[] array */ + INT32 dist0, dist1; /* initial distance values */ + register INT32 dist2; /* current distance in inner loop */ + INT32 xx0, xx1; /* distance increments */ + register INT32 xx2; + INT32 inc0, inc1, inc2; /* initial values for increments */ + /* This array holds the distance to the nearest-so-far color for each cell */ + INT32 bestdist[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + + /* Initialize best-distance for each cell of the update box */ + bptr = bestdist; + for (i = BOX_C0_ELEMS*BOX_C1_ELEMS*BOX_C2_ELEMS-1; i >= 0; i--) + *bptr++ = 0x7FFFFFFFL; + + /* For each color selected by find_nearby_colors, + * compute its distance to the center of each cell in the box. + * If that's less than best-so-far, update best distance and color number. + */ + + /* Nominal steps between cell centers ("x" in Thomas article) */ +#define STEP_C0 ((1 << C0_SHIFT) * C0_SCALE) +#define STEP_C1 ((1 << C1_SHIFT) * C1_SCALE) +#define STEP_C2 ((1 << C2_SHIFT) * C2_SCALE) + + for (i = 0; i < numcolors; i++) { + icolor = GETJSAMPLE(colorlist[i]); + /* Compute (square of) distance from minc0/c1/c2 to this color */ + inc0 = (minc0 - GETJSAMPLE(cinfo->colormap[0][icolor])) * C0_SCALE; + dist0 = inc0*inc0; + inc1 = (minc1 - GETJSAMPLE(cinfo->colormap[1][icolor])) * C1_SCALE; + dist0 += inc1*inc1; + inc2 = (minc2 - GETJSAMPLE(cinfo->colormap[2][icolor])) * C2_SCALE; + dist0 += inc2*inc2; + /* Form the initial difference increments */ + inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0; + inc1 = inc1 * (2 * STEP_C1) + STEP_C1 * STEP_C1; + inc2 = inc2 * (2 * STEP_C2) + STEP_C2 * STEP_C2; + /* Now loop over all cells in box, updating distance per Thomas method */ + bptr = bestdist; + cptr = bestcolor; + xx0 = inc0; + for (ic0 = BOX_C0_ELEMS-1; ic0 >= 0; ic0--) { + dist1 = dist0; + xx1 = inc1; + for (ic1 = BOX_C1_ELEMS-1; ic1 >= 0; ic1--) { + dist2 = dist1; + xx2 = inc2; + for (ic2 = BOX_C2_ELEMS-1; ic2 >= 0; ic2--) { + if (dist2 < *bptr) { + *bptr = dist2; + *cptr = (JSAMPLE) icolor; + } + dist2 += xx2; + xx2 += 2 * STEP_C2 * STEP_C2; + bptr++; + cptr++; + } + dist1 += xx1; + xx1 += 2 * STEP_C1 * STEP_C1; + } + dist0 += xx0; + xx0 += 2 * STEP_C0 * STEP_C0; + } + } +} + + +LOCAL(void) +fill_inverse_cmap (j_decompress_ptr cinfo, int c0, int c1, int c2) +/* Fill the inverse-colormap entries in the update box that contains */ +/* histogram cell c0/c1/c2. (Only that one cell MUST be filled, but */ +/* we can fill as many others as we wish.) */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + int minc0, minc1, minc2; /* lower left corner of update box */ + int ic0, ic1, ic2; + register JSAMPLE * cptr; /* pointer into bestcolor[] array */ + register histptr cachep; /* pointer into main cache array */ + /* This array lists the candidate colormap indexes. */ + JSAMPLE colorlist[MAXNUMCOLORS]; + int numcolors; /* number of candidate colors */ + /* This array holds the actually closest colormap index for each cell. */ + JSAMPLE bestcolor[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + + /* Convert cell coordinates to update box ID */ + c0 >>= BOX_C0_LOG; + c1 >>= BOX_C1_LOG; + c2 >>= BOX_C2_LOG; + + /* Compute true coordinates of update box's origin corner. + * Actually we compute the coordinates of the center of the corner + * histogram cell, which are the lower bounds of the volume we care about. + */ + minc0 = (c0 << BOX_C0_SHIFT) + ((1 << C0_SHIFT) >> 1); + minc1 = (c1 << BOX_C1_SHIFT) + ((1 << C1_SHIFT) >> 1); + minc2 = (c2 << BOX_C2_SHIFT) + ((1 << C2_SHIFT) >> 1); + + /* Determine which colormap entries are close enough to be candidates + * for the nearest entry to some cell in the update box. + */ + numcolors = find_nearby_colors(cinfo, minc0, minc1, minc2, colorlist); + + /* Determine the actually nearest colors. */ + find_best_colors(cinfo, minc0, minc1, minc2, numcolors, colorlist, + bestcolor); + + /* Save the best color numbers (plus 1) in the main cache array */ + c0 <<= BOX_C0_LOG; /* convert ID back to base cell indexes */ + c1 <<= BOX_C1_LOG; + c2 <<= BOX_C2_LOG; + cptr = bestcolor; + for (ic0 = 0; ic0 < BOX_C0_ELEMS; ic0++) { + for (ic1 = 0; ic1 < BOX_C1_ELEMS; ic1++) { + cachep = & histogram[c0+ic0][c1+ic1][c2]; + for (ic2 = 0; ic2 < BOX_C2_ELEMS; ic2++) { + *cachep++ = (histcell) (GETJSAMPLE(*cptr++) + 1); + } + } + } +} + + +/* + * Map some rows of pixels to the output colormapped representation. + */ + +METHODDEF(void) +pass2_no_dither (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows) +/* This version performs no dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + register JSAMPROW inptr, outptr; + register histptr cachep; + register int c0, c1, c2; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + inptr = input_buf[row]; + outptr = output_buf[row]; + for (col = width; col > 0; col--) { + /* get pixel value and index into the cache */ + c0 = GETJSAMPLE(*inptr++) >> C0_SHIFT; + c1 = GETJSAMPLE(*inptr++) >> C1_SHIFT; + c2 = GETJSAMPLE(*inptr++) >> C2_SHIFT; + cachep = & histogram[c0][c1][c2]; + /* If we have not seen this color before, find nearest colormap entry */ + /* and update the cache */ + if (*cachep == 0) + fill_inverse_cmap(cinfo, c0,c1,c2); + /* Now emit the colormap index for this cell */ + *outptr++ = (JSAMPLE) (*cachep - 1); + } + } +} + + +METHODDEF(void) +pass2_fs_dither (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows) +/* This version performs Floyd-Steinberg dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + register LOCFSERROR cur0, cur1, cur2; /* current error or pixel value */ + LOCFSERROR belowerr0, belowerr1, belowerr2; /* error for pixel below cur */ + LOCFSERROR bpreverr0, bpreverr1, bpreverr2; /* error for below/prev col */ + register FSERRPTR errorptr; /* => fserrors[] at column before current */ + JSAMPROW inptr; /* => current input pixel */ + JSAMPROW outptr; /* => current output pixel */ + histptr cachep; + int dir; /* +1 or -1 depending on direction */ + int dir3; /* 3*dir, for advancing inptr & errorptr */ + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + JSAMPLE *range_limit = cinfo->sample_range_limit; + int *error_limit = cquantize->error_limiter; + JSAMPROW colormap0 = cinfo->colormap[0]; + JSAMPROW colormap1 = cinfo->colormap[1]; + JSAMPROW colormap2 = cinfo->colormap[2]; + SHIFT_TEMPS + + for (row = 0; row < num_rows; row++) { + inptr = input_buf[row]; + outptr = output_buf[row]; + if (cquantize->on_odd_row) { + /* work right to left in this row */ + inptr += (width-1) * 3; /* so point to rightmost pixel */ + outptr += width-1; + dir = -1; + dir3 = -3; + errorptr = cquantize->fserrors + (width+1)*3; /* => entry after last column */ + cquantize->on_odd_row = FALSE; /* flip for next time */ + } else { + /* work left to right in this row */ + dir = 1; + dir3 = 3; + errorptr = cquantize->fserrors; /* => entry before first real column */ + cquantize->on_odd_row = TRUE; /* flip for next time */ + } + /* Preset error values: no error propagated to first pixel from left */ + cur0 = cur1 = cur2 = 0; + /* and no error propagated to row below yet */ + belowerr0 = belowerr1 = belowerr2 = 0; + bpreverr0 = bpreverr1 = bpreverr2 = 0; + + for (col = width; col > 0; col--) { + /* curN holds the error propagated from the previous pixel on the + * current line. Add the error propagated from the previous line + * to form the complete error correction term for this pixel, and + * round the error term (which is expressed * 16) to an integer. + * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct + * for either sign of the error value. + * Note: errorptr points to *previous* column's array entry. + */ + cur0 = RIGHT_SHIFT(cur0 + errorptr[dir3+0] + 8, 4); + cur1 = RIGHT_SHIFT(cur1 + errorptr[dir3+1] + 8, 4); + cur2 = RIGHT_SHIFT(cur2 + errorptr[dir3+2] + 8, 4); + /* Limit the error using transfer function set by init_error_limit. + * See comments with init_error_limit for rationale. + */ + cur0 = error_limit[cur0]; + cur1 = error_limit[cur1]; + cur2 = error_limit[cur2]; + /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. + * The maximum error is +- MAXJSAMPLE (or less with error limiting); + * this sets the required size of the range_limit array. + */ + cur0 += GETJSAMPLE(inptr[0]); + cur1 += GETJSAMPLE(inptr[1]); + cur2 += GETJSAMPLE(inptr[2]); + cur0 = GETJSAMPLE(range_limit[cur0]); + cur1 = GETJSAMPLE(range_limit[cur1]); + cur2 = GETJSAMPLE(range_limit[cur2]); + /* Index into the cache with adjusted pixel value */ + cachep = & histogram[cur0>>C0_SHIFT][cur1>>C1_SHIFT][cur2>>C2_SHIFT]; + /* If we have not seen this color before, find nearest colormap */ + /* entry and update the cache */ + if (*cachep == 0) + fill_inverse_cmap(cinfo, cur0>>C0_SHIFT,cur1>>C1_SHIFT,cur2>>C2_SHIFT); + /* Now emit the colormap index for this cell */ + { register int pixcode = *cachep - 1; + *outptr = (JSAMPLE) pixcode; + /* Compute representation error for this pixel */ + cur0 -= GETJSAMPLE(colormap0[pixcode]); + cur1 -= GETJSAMPLE(colormap1[pixcode]); + cur2 -= GETJSAMPLE(colormap2[pixcode]); + } + /* Compute error fractions to be propagated to adjacent pixels. + * Add these into the running sums, and simultaneously shift the + * next-line error sums left by 1 column. + */ + { register LOCFSERROR bnexterr, delta; + + bnexterr = cur0; /* Process component 0 */ + delta = cur0 * 2; + cur0 += delta; /* form error * 3 */ + errorptr[0] = (FSERROR) (bpreverr0 + cur0); + cur0 += delta; /* form error * 5 */ + bpreverr0 = belowerr0 + cur0; + belowerr0 = bnexterr; + cur0 += delta; /* form error * 7 */ + bnexterr = cur1; /* Process component 1 */ + delta = cur1 * 2; + cur1 += delta; /* form error * 3 */ + errorptr[1] = (FSERROR) (bpreverr1 + cur1); + cur1 += delta; /* form error * 5 */ + bpreverr1 = belowerr1 + cur1; + belowerr1 = bnexterr; + cur1 += delta; /* form error * 7 */ + bnexterr = cur2; /* Process component 2 */ + delta = cur2 * 2; + cur2 += delta; /* form error * 3 */ + errorptr[2] = (FSERROR) (bpreverr2 + cur2); + cur2 += delta; /* form error * 5 */ + bpreverr2 = belowerr2 + cur2; + belowerr2 = bnexterr; + cur2 += delta; /* form error * 7 */ + } + /* At this point curN contains the 7/16 error value to be propagated + * to the next pixel on the current line, and all the errors for the + * next line have been shifted over. We are therefore ready to move on. + */ + inptr += dir3; /* Advance pixel pointers to next column */ + outptr += dir; + errorptr += dir3; /* advance errorptr to current column */ + } + /* Post-loop cleanup: we must unload the final error values into the + * final fserrors[] entry. Note we need not unload belowerrN because + * it is for the dummy column before or after the actual array. + */ + errorptr[0] = (FSERROR) bpreverr0; /* unload prev errs into array */ + errorptr[1] = (FSERROR) bpreverr1; + errorptr[2] = (FSERROR) bpreverr2; + } +} + + +/* + * Initialize the error-limiting transfer function (lookup table). + * The raw F-S error computation can potentially compute error values of up to + * +- MAXJSAMPLE. But we want the maximum correction applied to a pixel to be + * much less, otherwise obviously wrong pixels will be created. (Typical + * effects include weird fringes at color-area boundaries, isolated bright + * pixels in a dark area, etc.) The standard advice for avoiding this problem + * is to ensure that the "corners" of the color cube are allocated as output + * colors; then repeated errors in the same direction cannot cause cascading + * error buildup. However, that only prevents the error from getting + * completely out of hand; Aaron Giles reports that error limiting improves + * the results even with corner colors allocated. + * A simple clamping of the error values to about +- MAXJSAMPLE/8 works pretty + * well, but the smoother transfer function used below is even better. Thanks + * to Aaron Giles for this idea. + */ + +LOCAL(void) +init_error_limit (j_decompress_ptr cinfo) +/* Allocate and fill in the error_limiter table */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + int * table; + int in, out; + + table = (int *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, (MAXJSAMPLE*2+1) * SIZEOF(int)); + table += MAXJSAMPLE; /* so can index -MAXJSAMPLE .. +MAXJSAMPLE */ + cquantize->error_limiter = table; + +#define STEPSIZE ((MAXJSAMPLE+1)/16) + /* Map errors 1:1 up to +- MAXJSAMPLE/16 */ + out = 0; + for (in = 0; in < STEPSIZE; in++, out++) { + table[in] = out; table[-in] = -out; + } + /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */ + for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1) { + table[in] = out; table[-in] = -out; + } + /* Clamp the rest to final out value (which is (MAXJSAMPLE+1)/8) */ + for (; in <= MAXJSAMPLE; in++) { + table[in] = out; table[-in] = -out; + } +#undef STEPSIZE +} + + +/* + * Finish up at the end of each pass. + */ + +METHODDEF(void) +finish_pass1 (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + /* Select the representative colors and fill in cinfo->colormap */ + cinfo->colormap = cquantize->sv_colormap; + select_colors(cinfo, cquantize->desired); + /* Force next pass to zero the color index table */ + cquantize->needs_zeroed = TRUE; +} + + +METHODDEF(void) +finish_pass2 (CPL_UNUSED j_decompress_ptr cinfo) +{ + /* no work */ +} + + +/* + * Initialize for each processing pass. + */ + +METHODDEF(void) +start_pass_2_quant (j_decompress_ptr cinfo, boolean is_pre_scan) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + int i; + + /* Only F-S dithering or no dithering is supported. */ + /* If user asks for ordered dither, give him F-S. */ + if (cinfo->dither_mode != JDITHER_NONE) + cinfo->dither_mode = JDITHER_FS; + + if (is_pre_scan) { + /* Set up method pointers */ + cquantize->pub.color_quantize = prescan_quantize; + cquantize->pub.finish_pass = finish_pass1; + cquantize->needs_zeroed = TRUE; /* Always zero histogram */ + } else { + /* Set up method pointers */ + if (cinfo->dither_mode == JDITHER_FS) + cquantize->pub.color_quantize = pass2_fs_dither; + else + cquantize->pub.color_quantize = pass2_no_dither; + cquantize->pub.finish_pass = finish_pass2; + + /* Make sure color count is acceptable */ + i = cinfo->actual_number_of_colors; + if (i < 1) + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 1); + if (i > MAXNUMCOLORS) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); + + if (cinfo->dither_mode == JDITHER_FS) { + size_t arraysize = (size_t) ((cinfo->output_width + 2) * + (3 * SIZEOF(FSERROR))); + /* Allocate Floyd-Steinberg workspace if we didn't already. */ + if (cquantize->fserrors == NULL) + cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large) + ((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize); + /* Initialize the propagated errors to zero. */ + jzero_far((void FAR *) cquantize->fserrors, arraysize); + /* Make the error-limit table if we didn't already. */ + if (cquantize->error_limiter == NULL) + init_error_limit(cinfo); + cquantize->on_odd_row = FALSE; + } + + } + /* Zero the histogram or inverse color map, if necessary */ + if (cquantize->needs_zeroed) { + for (i = 0; i < HIST_C0_ELEMS; i++) { + jzero_far((void FAR *) histogram[i], + HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell)); + } + cquantize->needs_zeroed = FALSE; + } +} + + +/* + * Switch to a new external colormap between output passes. + */ + +METHODDEF(void) +new_color_map_2_quant (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + /* Reset the inverse color map */ + cquantize->needs_zeroed = TRUE; +} + + +/* + * Module initialization routine for 2-pass color quantization. + */ + +GLOBAL(void) +jinit_2pass_quantizer (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize; + int i; + + cquantize = (my_cquantize_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_cquantizer)); + cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize; + cquantize->pub.start_pass = start_pass_2_quant; + cquantize->pub.new_color_map = new_color_map_2_quant; + cquantize->fserrors = NULL; /* flag optional arrays not allocated */ + cquantize->error_limiter = NULL; + + /* Make sure jdmaster didn't give me a case I can't handle */ + if (cinfo->out_color_components != 3) + ERREXIT(cinfo, JERR_NOTIMPL); + + /* Allocate the histogram/inverse colormap storage */ + cquantize->histogram = (hist3d) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, HIST_C0_ELEMS * SIZEOF(hist2d)); + for (i = 0; i < HIST_C0_ELEMS; i++) { + cquantize->histogram[i] = (hist2d) (*cinfo->mem->alloc_large) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell)); + } + cquantize->needs_zeroed = TRUE; /* histogram is garbage now */ + + /* Allocate storage for the completed colormap, if required. + * We do this now since it is FAR storage and may affect + * the memory manager's space calculations. + */ + if (cinfo->enable_2pass_quant) { + /* Make sure color count is acceptable */ + int desired = cinfo->desired_number_of_colors; + /* Lower bound on # of colors ... somewhat arbitrary as long as > 0 */ + if (desired < 8) + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 8); + /* Make sure colormap indexes can be represented by JSAMPLEs */ + if (desired > MAXNUMCOLORS) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); + cquantize->sv_colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo,JPOOL_IMAGE, (JDIMENSION) desired, (JDIMENSION) 3); + cquantize->desired = desired; + } else + cquantize->sv_colormap = NULL; + + /* Only F-S dithering or no dithering is supported. */ + /* If user asks for ordered dither, give him F-S. */ + if (cinfo->dither_mode != JDITHER_NONE) + cinfo->dither_mode = JDITHER_FS; + + /* Allocate Floyd-Steinberg workspace if necessary. + * This isn't really needed until pass 2, but again it is FAR storage. + * Although we will cope with a later change in dither_mode, + * we do not promise to honor max_memory_to_use if dither_mode changes. + */ + if (cinfo->dither_mode == JDITHER_FS) { + cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (size_t) ((cinfo->output_width + 2) * (3 * SIZEOF(FSERROR)))); + /* Might as well create the error-limiting table too. */ + init_error_limit(cinfo); + } +} + +#endif /* QUANT_2PASS_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jutils.c b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jutils.c new file mode 100644 index 000000000..d18a95556 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jutils.c @@ -0,0 +1,179 @@ +/* + * jutils.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains tables and miscellaneous utility routines needed + * for both compression and decompression. + * Note we prefix all global names with "j" to minimize conflicts with + * a surrounding application. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * jpeg_zigzag_order[i] is the zigzag-order position of the i'th element + * of a DCT block read in natural order (left to right, top to bottom). + */ + +#if 0 /* This table is not actually needed in v6a */ + +const int jpeg_zigzag_order[DCTSIZE2] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +#endif + +/* + * jpeg_natural_order[i] is the natural-order position of the i'th element + * of zigzag order. + * + * When reading corrupted data, the Huffman decoders could attempt + * to reference an entry beyond the end of this array (if the decoded + * zero run length reaches past the end of the block). To prevent + * wild stores without adding an inner-loop test, we put some extra + * "63"s after the real entries. This will cause the extra coefficient + * to be stored in location 63 of the block, not somewhere random. + * The worst case would be a run-length of 15, which means we need 16 + * fake entries. + */ + +const int jpeg_natural_order[DCTSIZE2+16] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + 63, 63, 63, 63, 63, 63, 63, 63 +}; + + +/* + * Arithmetic utilities + */ + +GLOBAL(long) +jdiv_round_up (long a, long b) +/* Compute a/b rounded up to next integer, ie, ceil(a/b) */ +/* Assumes a >= 0, b > 0 */ +{ + return (a + b - 1L) / b; +} + + +GLOBAL(long) +jround_up (long a, long b) +/* Compute a rounded up to next multiple of b, ie, ceil(a/b)*b */ +/* Assumes a >= 0, b > 0 */ +{ + a += b - 1L; + return a - (a % b); +} + + +/* On normal machines we can apply MEMCOPY() and MEMZERO() to sample arrays + * and coefficient-block arrays. This won't work on 80x86 because the arrays + * are FAR and we're assuming a small-pointer memory model. However, some + * DOS compilers provide far-pointer versions of memcpy() and memset() even + * in the small-model libraries. These will be used if USE_FMEM is defined. + * Otherwise, the routines below do it the hard way. (The performance cost + * is not all that great, because these routines aren't very heavily used.) + */ + +#ifndef NEED_FAR_POINTERS /* normal case, same as regular macros */ +#define FMEMCOPY(dest,src,size) MEMCOPY(dest,src,size) +#define FMEMZERO(target,size) MEMZERO(target,size) +#else /* 80x86 case, define if we can */ +#ifdef USE_FMEM +#define FMEMCOPY(dest,src,size) _fmemcpy((void FAR *)(dest), (const void FAR *)(src), (size_t)(size)) +#define FMEMZERO(target,size) _fmemset((void FAR *)(target), 0, (size_t)(size)) +#endif +#endif + + +GLOBAL(void) +jcopy_sample_rows (JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols) +/* Copy some rows of samples from one place to another. + * num_rows rows are copied from input_array[source_row++] + * to output_array[dest_row++]; these areas may overlap for duplication. + * The source and destination arrays must be at least as wide as num_cols. + */ +{ + register JSAMPROW inptr, outptr; +#ifdef FMEMCOPY + register size_t count = (size_t) (num_cols * SIZEOF(JSAMPLE)); +#else + register JDIMENSION count; +#endif + register int row; + + input_array += source_row; + output_array += dest_row; + + for (row = num_rows; row > 0; row--) { + inptr = *input_array++; + outptr = *output_array++; +#ifdef FMEMCOPY + FMEMCOPY(outptr, inptr, count); +#else + for (count = num_cols; count > 0; count--) + *outptr++ = *inptr++; /* needn't bother with GETJSAMPLE() here */ +#endif + } +} + + +GLOBAL(void) +jcopy_block_row (JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks) +/* Copy a row of coefficient blocks from one place to another. */ +{ +#ifdef FMEMCOPY + FMEMCOPY(output_row, input_row, num_blocks * (DCTSIZE2 * SIZEOF(JCOEF))); +#else + register JCOEFPTR inptr, outptr; + register long count; + + inptr = (JCOEFPTR) input_row; + outptr = (JCOEFPTR) output_row; + for (count = (long) num_blocks * DCTSIZE2; count > 0; count--) { + *outptr++ = *inptr++; + } +#endif +} + + +GLOBAL(void) +jzero_far (void FAR * target, size_t bytestozero) +/* Zero out a chunk of FAR memory. */ +/* This might be sample-array data, block-array data, or alloc_large data. */ +{ +#ifdef FMEMZERO + FMEMZERO(target, bytestozero); +#else + register char FAR * ptr = (char FAR *) target; + register size_t count; + + for (count = bytestozero; count > 0; count--) { + *ptr++ = 0; + } +#endif +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jversion.h b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jversion.h new file mode 100644 index 000000000..6472c58d3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/jversion.h @@ -0,0 +1,14 @@ +/* + * jversion.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains software version identification. + */ + + +#define JVERSION "6b 27-Mar-1998" + +#define JCOPYRIGHT "Copyright (C) 1998, Thomas G. Lane" diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg/makefile.vc b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/makefile.vc new file mode 100644 index 000000000..88be6c16f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg/makefile.vc @@ -0,0 +1,25 @@ +GDAL_ROOT = ..\..\.. +!INCLUDE $(GDAL_ROOT)\nmake.opt + + +OBJ = \ + jcapimin.obj jcapistd.obj jccoefct.obj jccolor.obj jcdctmgr.obj jchuff.obj \ + jcinit.obj jcmainct.obj jcmarker.obj jcmaster.obj jcomapi.obj jcparam.obj \ + jcphuff.obj jcprepct.obj jcsample.obj jctrans.obj jdapimin.obj jdapistd.obj \ + jdatadst.obj jdatasrc.obj jdcoefct.obj jdcolor.obj jddctmgr.obj jdhuff.obj \ + jdinput.obj jdmainct.obj jdmarker.obj jdmaster.obj jdmerge.obj jdphuff.obj \ + jdpostct.obj jdsample.obj jdtrans.obj jerror.obj jfdctflt.obj jfdctfst.obj \ + jfdctint.obj jidctflt.obj jidctfst.obj jidctint.obj jidctred.obj jquant1.obj \ + jquant2.obj jutils.obj jmemmgr.obj jmemansi.obj + +GDAL_ROOT = ..\..\.. + +EXTRAFLAGS = -DDEFAULT_MAX_MEM=500000000L $(SOFTWARNFLAGS) + +default: $(OBJ) + lib /out:libjpeg.lib $(OBJ) + xcopy /D /Y *.obj ..\..\o + +clean: + -del *.lib + -del *.obj diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg12/jmorecfg.h.12 b/bazaar/plugin/gdal/frmts/jpeg/libjpeg12/jmorecfg.h.12 new file mode 100644 index 000000000..fc671e083 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg12/jmorecfg.h.12 @@ -0,0 +1,372 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 12 /* use 8 or 12 */ +#define NEED_12_BIT_NAMES + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H +typedef long INT32; +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +# if defined (_WIN32) +# ifndef __RPCNDR_H__ + typedef unsigned char boolean; +# endif +# else + typedef int boolean; +# endif +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Arithmetic coding is unsupported for legal reasons. Complaints to IBM. */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#undef C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#undef D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/bazaar/plugin/gdal/frmts/jpeg/libjpeg12/makefile.vc b/bazaar/plugin/gdal/frmts/jpeg/libjpeg12/makefile.vc new file mode 100644 index 000000000..389ebf4e5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/libjpeg12/makefile.vc @@ -0,0 +1,76 @@ +GDAL_ROOT = ..\..\.. +!INCLUDE $(GDAL_ROOT)\nmake.opt + + +OBJ = \ + jcapimin12.obj jcapistd12.obj jccoefct12.obj jccolor12.obj jcdctmgr12.obj jchuff12.obj \ + jcinit12.obj jcmainct12.obj jcmarker12.obj jcmaster12.obj jcomapi12.obj jcparam12.obj \ + jcphuff12.obj jcprepct12.obj jcsample12.obj jctrans12.obj jdapimin12.obj jdapistd12.obj \ + jdatadst12.obj jdatasrc12.obj jdcoefct12.obj jdcolor12.obj jddctmgr12.obj jdhuff12.obj \ + jdinput12.obj jdmainct12.obj jdmarker12.obj jdmaster12.obj jdmerge12.obj jdphuff12.obj \ + jdpostct12.obj jdsample12.obj jdtrans12.obj jerror12.obj jfdctflt12.obj jfdctfst12.obj \ + jfdctint12.obj jidctflt12.obj jidctfst12.obj jidctint12.obj jidctred12.obj jquant112.obj \ + jquant212.obj jutils12.obj jmemmgr12.obj jmemansi12.obj + +GDAL_ROOT = ..\..\.. + +EXTRAFLAGS = -DDEFAULT_MAX_MEM=500000000L $(SOFTWARNFLAGS) + +default: jcapimin12.c $(OBJ) + xcopy /D /Y *.obj ..\..\o + +jcapimin12.c: ../libjpeg/jcapimin.c + xcopy /Y ..\libjpeg\*.h + xcopy /Y jmorecfg.h.12 jmorecfg.h + xcopy /Y ..\libjpeg\*.c + ren jcapimin.c jcapimin12.c + ren jcapistd.c jcapistd12.c + ren jccoefct.c jccoefct12.c + ren jccolor.c jccolor12.c + ren jcdctmgr.c jcdctmgr12.c + ren jchuff.c jchuff12.c + ren jcinit.c jcinit12.c + ren jcmainct.c jcmainct12.c + ren jcmarker.c jcmarker12.c + ren jcmaster.c jcmaster12.c + ren jcomapi.c jcomapi12.c + ren jcparam.c jcparam12.c + ren jcphuff.c jcphuff12.c + ren jcprepct.c jcprepct12.c + ren jcsample.c jcsample12.c + ren jctrans.c jctrans12.c + ren jdapimin.c jdapimin12.c + ren jdapistd.c jdapistd12.c + ren jdatadst.c jdatadst12.c + ren jdatasrc.c jdatasrc12.c + ren jdcoefct.c jdcoefct12.c + ren jdcolor.c jdcolor12.c + ren jddctmgr.c jddctmgr12.c + ren jdhuff.c jdhuff12.c + ren jdinput.c jdinput12.c + ren jdmainct.c jdmainct12.c + ren jdmarker.c jdmarker12.c + ren jdmaster.c jdmaster12.c + ren jdmerge.c jdmerge12.c + ren jdphuff.c jdphuff12.c + ren jdpostct.c jdpostct12.c + ren jdsample.c jdsample12.c + ren jdtrans.c jdtrans12.c + ren jerror.c jerror12.c + ren jfdctflt.c jfdctflt12.c + ren jfdctfst.c jfdctfst12.c + ren jfdctint.c jfdctint12.c + ren jidctflt.c jidctflt12.c + ren jidctfst.c jidctfst12.c + ren jidctint.c jidctint12.c + ren jidctred.c jidctred12.c + ren jmemansi.c jmemansi12.c + ren jmemmgr.c jmemmgr12.c + ren jquant1.c jquant112.c + ren jquant2.c jquant212.c + ren jutils.c jutils12.c + +clean: + -del *.obj + -del *.c + -del *.h diff --git a/bazaar/plugin/gdal/frmts/jpeg/makefile.vc b/bazaar/plugin/gdal/frmts/jpeg/makefile.vc new file mode 100644 index 000000000..85c8b1490 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/makefile.vc @@ -0,0 +1,44 @@ +GDAL_ROOT = ..\.. +!INCLUDE $(GDAL_ROOT)\nmake.opt + + +OBJ = jpgdataset.obj jpgdataset_12.obj vsidataio.obj + + +!IFDEF JPEG_EXTERNAL_LIB +EXTRAFLAGS = -I$(JPEGDIR) -I..\mem +!ELSE +EXTRAFLAGS = -Ilibjpeg $(JPEG12_FLAGS) -I..\mem +!ENDIF + +!IFDEF JPEG12_SUPPORTED +JPEG12_FLAGS = -DJPEG_DUAL_MODE_8_12 +EXTRA_DEP = libjpeg12src +!ENDIF + +default: $(EXTRA_DEP) $(OBJ) + xcopy /D /Y *.obj ..\o +!IFNDEF JPEG_EXTERNAL_LIB + cd libjpeg + $(MAKE) /f makefile.vc + cd .. +!ENDIF +!IFDEF JPEG12_SUPPORTED + cd libjpeg12 + $(MAKE) /f makefile.vc + cd .. +!ENDIF + +clean: + -del *.obj + cd libjpeg + $(MAKE) /f makefile.vc clean + cd .. + cd libjpeg12 + $(MAKE) /f makefile.vc clean + cd .. + +libjpeg12src: + cd libjpeg12 + $(MAKE) /f makefile.vc jcapimin12.c + cd .. diff --git a/bazaar/plugin/gdal/frmts/jpeg/vsidataio.cpp b/bazaar/plugin/gdal/frmts/jpeg/vsidataio.cpp new file mode 100644 index 000000000..f03d5e723 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/vsidataio.cpp @@ -0,0 +1,432 @@ +/****************************************************************************** + * $Id: vsidataio.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: JPEG JFIF Driver + * Purpose: Implement JPEG read/write io indirection through VSI. + * Author: Frank Warmerdam, warmerdam@pobox.com + * Code partially derived from libjpeg jdatasrc.c and jdatadst.c. + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "vsidataio.h" + +CPL_CVSID("$Id: vsidataio.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +CPL_C_START +#include "jerror.h" +CPL_C_END + + +/* Expanded data source object for stdio input */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + + VSILFILE * infile; /* source stream */ + JOCTET * buffer; /* start of buffer */ + boolean start_of_file; /* have we gotten any data yet? */ +} my_source_mgr; + +typedef my_source_mgr * my_src_ptr; + +#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ + + +/* + * Initialize source --- called by jpeg_read_header + * before any data is actually read. + */ + +METHODDEF(void) +init_source (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_file = TRUE; +} + + +/* + * Fill the input buffer --- called whenever buffer is emptied. + * + * In typical applications, this should read fresh data into the buffer + * (ignoring the current state of next_input_byte & bytes_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been reloaded. It is not necessary to + * fill the buffer entirely, only to obtain at least one more byte. + * + * There is no such thing as an EOF return. If the end of the file has been + * reached, the routine has a choice of ERREXIT() or inserting fake data into + * the buffer. In most cases, generating a warning message and inserting a + * fake EOI marker is the best course of action --- this will allow the + * decompressor to output however much of the image is there. However, + * the resulting error message is misleading if the real problem is an empty + * input file, so we handle that case specially. + * + * In applications that need to be able to suspend compression due to input + * not being available yet, a FALSE return indicates that no more data can be + * obtained right now, but more may be forthcoming later. In this situation, + * the decompressor will return to its caller (with an indication of the + * number of scanlines it has read, if any). The application should resume + * decompression after it has loaded more data into the input buffer. Note + * that there are substantial restrictions on the use of suspension --- see + * the documentation. + * + * When suspending, the decompressor will back up to a convenient restart point + * (typically the start of the current MCU). next_input_byte & bytes_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point must be rescanned after resumption, so move it to + * the front of the buffer rather than discarding it. + */ + +METHODDEF(boolean) +fill_input_buffer (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + size_t nbytes; + + nbytes = VSIFReadL(src->buffer, 1, INPUT_BUF_SIZE, src->infile); + + if (nbytes <= 0) { + if (src->start_of_file) /* Treat empty input file as fatal error */ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + WARNMS(cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = FALSE; + + return TRUE; +} + +/* + * The Intel IPP performance libraries do not necessarily read the + * entire contents of the buffer with each pass, so each re-fill + * copies the remaining buffer bytes to the front of the buffer, + * then fills up the rest with new data. + */ +#ifdef IPPJ_HUFF +METHODDEF(boolean) +fill_input_buffer_ipp (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + size_t bytes_left = src->pub.bytes_in_buffer; + size_t bytes_to_read = INPUT_BUF_SIZE - bytes_left; + size_t nbytes; + + if(src->start_of_file || cinfo->progressive_mode) + { + return fill_input_buffer(cinfo); + } + + memmove(src->buffer,src->pub.next_input_byte,bytes_left); + + nbytes = VSIFReadL(src->buffer + bytes_left, 1, bytes_to_read, src->infile); + + if(nbytes <= 0) + { + if(src->start_of_file) + { + /* Treat empty input file as fatal error */ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + } + + if(src->pub.bytes_in_buffer == 0 && cinfo->unread_marker == 0) + { + WARNMS(cinfo, JWRN_JPEG_EOF); + + /* Insert a fake EOI marker */ + src->buffer[0] = (JOCTET)0xFF; + src->buffer[1] = (JOCTET)JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = bytes_left + nbytes; + src->start_of_file = FALSE; + + return TRUE; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = bytes_left + nbytes; + src->start_of_file = FALSE; + + return TRUE; +} +#endif /* IPPJ_HUFF */ + + +/* + * Skip data --- used to skip over a potentially large amount of + * uninteresting data (such as an APPn marker). + * + * Writers of suspendable-input applications must note that skip_input_data + * is not granted the right to give a suspension return. If the skip extends + * beyond the data currently in the buffer, the buffer can be marked empty so + * that the next read will cause a fill_input_buffer call that can suspend. + * Arranging for additional bytes to be discarded before reloading the input + * buffer is the application writer's problem. + */ + +METHODDEF(void) +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + + +/* + * An additional method that can be provided by data source modules is the + * resync_to_restart method for error recovery in the presence of RST markers. + * For the moment, this source module just uses the default resync method + * provided by the JPEG library. That method assumes that no backtracking + * is possible. + */ + + +/* + * Terminate source --- called by jpeg_finish_decompress + * after all data has been read. Often a no-op. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF(void) +term_source (CPL_UNUSED j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* + * Prepare for input from a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing decompression. + */ + +void jpeg_vsiio_src (j_decompress_ptr cinfo, VSILFILE * infile) +{ + my_src_ptr src; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_stdio_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof(my_source_mgr)); + src = (my_src_ptr) cinfo->src; + src->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + INPUT_BUF_SIZE * sizeof(JOCTET)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = init_source; +#ifdef IPPJ_HUFF + src->pub.fill_input_buffer = fill_input_buffer_ipp; +#else + src->pub.fill_input_buffer = fill_input_buffer; +#endif + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->infile = infile; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ +} + + +/* ==================================================================== */ +/* The rest was derived from jdatadst.c */ +/* ==================================================================== */ + +/* Expanded data destination object for stdio output */ + +typedef struct { + struct jpeg_destination_mgr pub; /* public fields */ + + VSILFILE * outfile; /* target stream */ + JOCTET * buffer; /* start of buffer */ +} my_destination_mgr; + +typedef my_destination_mgr * my_dest_ptr; + +#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ + + +/* + * Initialize destination --- called by jpeg_start_compress + * before any data is actually written. + */ + +METHODDEF(void) +init_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + /* Allocate the output buffer --- it will be released when done with image */ + dest->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + OUTPUT_BUF_SIZE * sizeof(JOCTET)); + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; +} + + +/* + * Empty the output buffer --- called whenever buffer fills up. + * + * In typical applications, this should write the entire output buffer + * (ignoring the current state of next_output_byte & free_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been dumped. + * + * In applications that need to be able to suspend compression due to output + * overrun, a FALSE return indicates that the buffer cannot be emptied now. + * In this situation, the compressor will return to its caller (possibly with + * an indication that it has not accepted all the supplied scanlines). The + * application should resume compression after it has made more room in the + * output buffer. Note that there are substantial restrictions on the use of + * suspension --- see the documentation. + * + * When suspending, the compressor will back up to a convenient restart point + * (typically the start of the current MCU). next_output_byte & free_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point will be regenerated after resumption, so do not + * write it out when emptying the buffer externally. + */ + +METHODDEF(boolean) +empty_output_buffer (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + size_t bytes_to_write = OUTPUT_BUF_SIZE; + +#ifdef IPPJ_HUFF +/* + * The Intel IPP performance libraries do not necessarily fill up + * the whole output buffer with each compression pass, so we only + * want to write out the parts of the buffer that are full. + */ + if(! cinfo->progressive_mode) { + bytes_to_write -= dest->pub.free_in_buffer; + } +#endif + + if (VSIFWriteL(dest->buffer, 1, bytes_to_write, dest->outfile) != bytes_to_write) + ERREXIT(cinfo, JERR_FILE_WRITE); + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + + return TRUE; +} + +/* + * Terminate destination --- called by jpeg_finish_compress + * after all data has been written. Usually needs to flush buffer. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF(void) +term_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; + + /* Write any data remaining in the buffer */ + if (datacount > 0) { + if (VSIFWriteL(dest->buffer, 1, datacount, dest->outfile) != datacount) + ERREXIT(cinfo, JERR_FILE_WRITE); + } + if( VSIFFlushL(dest->outfile) != 0 ) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * Prepare for output to a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing compression. + */ + +void +jpeg_vsiio_dest (j_compress_ptr cinfo, VSILFILE * outfile) +{ + my_dest_ptr dest; + + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_stdio_dest. + * This makes it dangerous to use this manager and a different destination + * manager serially with the same JPEG object, because their private object + * sizes may be different. Caveat programmer. + */ + if (cinfo->dest == NULL) { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof(my_destination_mgr)); + } + + dest = (my_dest_ptr) cinfo->dest; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; + dest->outfile = outfile; +} diff --git a/bazaar/plugin/gdal/frmts/jpeg/vsidataio.h b/bazaar/plugin/gdal/frmts/jpeg/vsidataio.h new file mode 100644 index 000000000..57a87f39b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg/vsidataio.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * $Id: vsidataio.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: JPEG JFIF Driver + * Purpose: Implement JPEG read/write io indirection through VSI. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef VSIDATAIO_H_INCLUDED +#define VSIDATAIO_H_INCLUDED + +#include "cpl_vsi.h" + +CPL_C_START +#include "jpeglib.h" +CPL_C_END + +void jpeg_vsiio_src (j_decompress_ptr cinfo, VSILFILE * infile); +void jpeg_vsiio_dest (j_compress_ptr cinfo, VSILFILE * outfile); + +#endif // VSIDATAIO_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/jpeg2000/GNUmakefile b/bazaar/plugin/gdal/frmts/jpeg2000/GNUmakefile new file mode 100644 index 000000000..a67b3ab81 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg2000/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = jpeg2000dataset.o jpeg2000_vsil_io.o + +CPPFLAGS := $(JASPER_FLAGS) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f $(OBJ) $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/jpeg2000/frmt_jpeg2000.html b/bazaar/plugin/gdal/frmts/jpeg2000/frmt_jpeg2000.html new file mode 100644 index 000000000..b994964ce --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg2000/frmt_jpeg2000.html @@ -0,0 +1,296 @@ + + +JPEG2000 --- Implementation of the JPEG-2000 part 1 +(ISO/IEC 15444-1) standard + + + + +

    JPEG2000 --- Implementation of the JPEG-2000 part 1

    + +This implementation based on JasPer software (see below).

    + +Starting with GDAL 1.9.0, XMP metadata can be extracted from JPEG2000 files, and will be +stored as XML raw content in the xml:XMP metadata domain.

    + +

    Option Options

    + +(GDAL >= 2.0 ) + +The following open option is available: +
      +
    • 1BIT_ALPHA_PROMOTION=YES/NO: Whether a 1-bit alpha channel should be promoted to 8-bit. +Defaults to YES.

    • +
    + +

    Creation Options

    +
      +
    • WORLDFILE=ON: Force the generation of an associated + ESRI world file (.wld).

      + +

    • FORMAT=JP2|JPC: Specify output file format.

      + +

    • GMLJP2=YES/NO: (Starting with GDAL 2.0) Indicates whether a GML box +conforming to the OGC GML in JPEG2000 specification should be included in the +file. Unless GMLJP2V2_DEF is used, the version of the GMLJP2 box will be +version 1. As implemented currently, the GMLJP2 box will be written after the +codestream. Defaults to YES.

      + +

    • GMLJP2V2_DEF=filename: (Starting with GDAL 2.0) Indicates whether a GML box +conforming to the +OGC GML in JPEG2000, version 2 specification should be included in the +file. filename must point to a file with a JSon content that defines how +the GMLJP2 v2 box should be built. See +GMLJP2v2 definition file section in documentation of the JP2OpenJPEG driver +for the syntax of the JSon configuration file. +It is also possible to directly pass the JSon content inlined as a string. +If filename is just set to YES, a minimal instance will be built. +As implemented currently, the GMLJP2 box will be written after the codestream.

      + +

    • GeoJP2=YES/NO: (Option starting with GDAL 2.0, but already enabled in +previous versions. Require a modified Jasper with GeoJP2 support enabled) +Indicates whether a UUID/GeoTIFF +box conforming to the GeoJP2 (GeoTIFF in JPEG2000) specification should be +included in the file. Defaults to YES.

      + +

    • Encoding parameters, directly delivered to the JasPer library + described in the JasPer documentation. Quoted from the docs:

      + + ``The following options are supported by the encoder: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      imgareatlx=x Set the x-coordinate of the top-left corner + of the image area to x.
      imgareatly=ySet the y-coordinate of the top-left corner + of the image area to y.
      tilegrdtlx=xSet the x-coordinate of the top-left corner + of the tiling grid to x.
      tilegrdtly=ySet the y-coordinate of the top-left corner + of the tiling grid to y.
      tilewidth=wSet the nominal tile width to w.
      tileheight=hSet the nominal tile height to h.
      prcwidth=wSet the precinct width to w. The argument w must be an + integer power of two. The default value is 32768.
      prcheight=hSet the precinct height to h. The argument h must be an + integer power of two. The default value is 32768.
      cblkwidth=wSet the nominal code block width to w. The argument + w must be an integer power of two. + The default value is 64.
      cblkheight=hSet the nominal code block height to h. The argument + h must be an integer power of two. + The default value is 64.
      mode=mSet the coding mode to m. The argument m must have + one of the following values: +
      + + + + + + + + + + +
      Value Description
      int integer mode
      real real mode
      + If lossless coding is desired, the integer mode must + be used. By default, the integer mode is employed. The + choice of mode also determines which multicomponent + and wavelet transforms (if any) are employed.
      rate=rSpecify the target rate. The argument r is a positive + real number. Since a rate of one corresponds + to no compression, one should never need + to explicitly specify a rate greater than one. + By default, the target rate is considered + to be infinite.
      ilyrrates=[, ,. . . , ]Specify the rates for any intermediate layers. + The argument to this option is a comma separated + list of N rates. Each rate is a positive real number. + The rates must increase monotonically. The last rate + in the list should be less than or equal to the + overall rate (as specified with the rate option).
      prg=pSet the progression order to p. The argument + p must have one of the following values: +
      + + + + + + + + + + + + + + + + + + + + + + + + +
      Value Description
      lrcplayer-resolution-component-position (LRCP) + progressive (i.e., rate scalable)
      rlcpresolution-layer-component-position (RLCP) + progressive (i.e., resolution scalable)
      rpclresolution-position-component-layer (RPCL) + progressive
      pcrlposition-component-resolution-layer (PCRL) + progressive
      cprlcomponent-position-resolution-layer (CPRL) + progressive
      + By default, LRCP progressive ordering is employed. + Note that the RPCL and PCRL progressions are not valid + for all possible image geometries. + (See standard for more details.)
      nomctDisallow the use of any multicomponent transform.
      numrlvls=nSet the number of resolution levels to n. The argument + n must be an integer that is greater than or equal + to one. The default value is 6.
      sopGenerate SOP marker segments.
      ephGenerate EPH marker segments.
      lazyEnable lazy coding mode (a.k.a. arithmetic coding + bypass).
      termallTerminate all coding passes.
      segsymUse segmentation symbols.
      vcausalUse vertically stripe causal contexts.
      ptermUse predictable termination.
      resetprobReset the probability models after each coding pass. +
      numgbits=nSet the number of guard bits to n.''
      + +

    + +

    See Also:

    + + + +Other JPEG2000 GDAL drivers : + + + + diff --git a/bazaar/plugin/gdal/frmts/jpeg2000/jpeg2000_vsil_io.cpp b/bazaar/plugin/gdal/frmts/jpeg2000/jpeg2000_vsil_io.cpp new file mode 100644 index 000000000..e1c017cf2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg2000/jpeg2000_vsil_io.cpp @@ -0,0 +1,288 @@ +/****************************************************************************** + * $Id: jpeg2000_vsil_io.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: JPEG-2000 + * Purpose: Return a stream for a VSIL file + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ******************************************************************************/ + +/* Following code is mostly derived from jas_stream.c, which is licenced */ +/* under the below terms */ + +/* + * Copyright (c) 1999-2000 Image Power, Inc. and the University of + * British Columbia. + * Copyright (c) 2001-2003 Michael David Adams. + * All rights reserved. + * Copyright (c) 2009-2010, Even Rouault + */ + +/* __START_OF_JASPER_LICENSE__ + * + * JasPer License Version 2.0 + * + * Copyright (c) 2001-2006 Michael David Adams + * Copyright (c) 1999-2000 Image Power, Inc. + * Copyright (c) 1999-2000 The University of British Columbia + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person (the + * "User") obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * 1. The above copyright notices and this permission notice (which + * includes the disclaimer below) shall be included in all copies or + * substantial portions of the Software. + * + * 2. The name of a copyright holder shall not be used to endorse or + * promote products derived from the Software without specific prior + * written permission. + * + * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS + * LICENSE. NO USE OF THE SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER + * THIS DISCLAIMER. THE SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS + * "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. NO ASSURANCES ARE + * PROVIDED BY THE COPYRIGHT HOLDERS THAT THE SOFTWARE DOES NOT INFRINGE + * THE PATENT OR OTHER INTELLECTUAL PROPERTY RIGHTS OF ANY OTHER ENTITY. + * EACH COPYRIGHT HOLDER DISCLAIMS ANY LIABILITY TO THE USER FOR CLAIMS + * BROUGHT BY ANY OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL + * PROPERTY RIGHTS OR OTHERWISE. AS A CONDITION TO EXERCISING THE RIGHTS + * GRANTED HEREUNDER, EACH USER HEREBY ASSUMES SOLE RESPONSIBILITY TO SECURE + * ANY OTHER INTELLECTUAL PROPERTY RIGHTS NEEDED, IF ANY. THE SOFTWARE + * IS NOT FAULT-TOLERANT AND IS NOT INTENDED FOR USE IN MISSION-CRITICAL + * SYSTEMS, SUCH AS THOSE USED IN THE OPERATION OF NUCLEAR FACILITIES, + * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL + * SYSTEMS, DIRECT LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH + * THE FAILURE OF THE SOFTWARE OR SYSTEM COULD LEAD DIRECTLY TO DEATH, + * PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH + * RISK ACTIVITIES"). THE COPYRIGHT HOLDERS SPECIFICALLY DISCLAIM ANY + * EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES. + * + * __END_OF_JASPER_LICENSE__ + */ + +#include "jpeg2000_vsil_io.h" +#include "cpl_vsi.h" + +CPL_CVSID("$Id: jpeg2000_vsil_io.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/* + * File descriptor file object. + */ +typedef struct { + VSILFILE* fp; +} jas_stream_VSIFL_t; + +/******************************************************************************\ +* File stream object. +\******************************************************************************/ + +static int JPEG2000_VSIL_read(jas_stream_obj_t *obj, char *buf, int cnt) +{ + jas_stream_VSIFL_t *fileobj = JAS_CAST(jas_stream_VSIFL_t *, obj); + return VSIFReadL(buf, 1, cnt, fileobj->fp); +} + +static int JPEG2000_VSIL_write(jas_stream_obj_t *obj, char *buf, int cnt) +{ + jas_stream_VSIFL_t *fileobj = JAS_CAST(jas_stream_VSIFL_t *, obj); + return VSIFWriteL(buf, 1, cnt, fileobj->fp); +} + +static long JPEG2000_VSIL_seek(jas_stream_obj_t *obj, long offset, int origin) +{ + jas_stream_VSIFL_t *fileobj = JAS_CAST(jas_stream_VSIFL_t *, obj); + if (offset < 0 && origin == SEEK_CUR) + { + origin = SEEK_SET; + offset += VSIFTellL(fileobj->fp); + } + else if (offset < 0 && origin == SEEK_END) + { + origin = SEEK_SET; + VSIFSeekL(fileobj->fp, 0, SEEK_END); + offset += VSIFTellL(fileobj->fp); + } + VSIFSeekL(fileobj->fp, offset, origin); + return VSIFTellL(fileobj->fp); +} + +static int JPEG2000_VSIL_close(jas_stream_obj_t *obj) +{ + jas_stream_VSIFL_t *fileobj = JAS_CAST(jas_stream_VSIFL_t *, obj); + VSIFCloseL(fileobj->fp); + fileobj->fp = NULL; + jas_free(fileobj); + return 0; +} + +static jas_stream_ops_t JPEG2000_VSIL_stream_fileops = { + JPEG2000_VSIL_read, + JPEG2000_VSIL_write, + JPEG2000_VSIL_seek, + JPEG2000_VSIL_close +}; + +/******************************************************************************\ +* Code for opening and closing streams. +\******************************************************************************/ + +static jas_stream_t *JPEG2000_VSIL_jas_stream_create() +{ + jas_stream_t *stream; + + if (!(stream = (jas_stream_t*) jas_malloc(sizeof(jas_stream_t)))) { + return 0; + } + stream->openmode_ = 0; + stream->bufmode_ = 0; + stream->flags_ = 0; + stream->bufbase_ = 0; + stream->bufstart_ = 0; + stream->bufsize_ = 0; + stream->ptr_ = 0; + stream->cnt_ = 0; + stream->ops_ = 0; + stream->obj_ = 0; + stream->rwcnt_ = 0; + stream->rwlimit_ = -1; + + return stream; +} + +static void JPEG2000_VSIL_jas_stream_destroy(jas_stream_t *stream) +{ + /* If the memory for the buffer was allocated with malloc, free + this memory. */ + if ((stream->bufmode_ & JAS_STREAM_FREEBUF) && stream->bufbase_) { + jas_free(stream->bufbase_); + stream->bufbase_ = 0; + } + jas_free(stream); +} + + +/******************************************************************************\ +* Buffer initialization code. +\******************************************************************************/ + +static void JPEG2000_VSIL_jas_stream_initbuf(jas_stream_t *stream, int bufmode, char *buf, + int bufsize) +{ + /* If this function is being called, the buffer should not have been + initialized yet. */ + assert(!stream->bufbase_); + + if (bufmode != JAS_STREAM_UNBUF) { + /* The full- or line-buffered mode is being employed. */ + if (!buf) { + /* The caller has not specified a buffer to employ, so allocate + one. */ + if ((stream->bufbase_ = (unsigned char*)jas_malloc(JAS_STREAM_BUFSIZE + + JAS_STREAM_MAXPUTBACK))) { + stream->bufmode_ |= JAS_STREAM_FREEBUF; + stream->bufsize_ = JAS_STREAM_BUFSIZE; + } else { + /* The buffer allocation has failed. Resort to unbuffered + operation. */ + stream->bufbase_ = stream->tinybuf_; + stream->bufsize_ = 1; + } + } else { + /* The caller has specified a buffer to employ. */ + /* The buffer must be large enough to accommodate maximum + putback. */ + assert(bufsize > JAS_STREAM_MAXPUTBACK); + stream->bufbase_ = JAS_CAST(uchar *, buf); + stream->bufsize_ = bufsize - JAS_STREAM_MAXPUTBACK; + } + } else { + /* The unbuffered mode is being employed. */ + /* A buffer should not have been supplied by the caller. */ + assert(!buf); + /* Use a trivial one-character buffer. */ + stream->bufbase_ = stream->tinybuf_; + stream->bufsize_ = 1; + } + stream->bufstart_ = &stream->bufbase_[JAS_STREAM_MAXPUTBACK]; + stream->ptr_ = stream->bufstart_; + stream->cnt_ = 0; + stream->bufmode_ |= bufmode & JAS_STREAM_BUFMODEMASK; +} + +static int JPEG2000_VSIL_jas_strtoopenmode(const char *s) +{ + int openmode = 0; + while (*s != '\0') { + switch (*s) { + case 'r': + openmode |= JAS_STREAM_READ; + break; + case 'w': + openmode |= JAS_STREAM_WRITE | JAS_STREAM_CREATE; + break; + case 'b': + openmode |= JAS_STREAM_BINARY; + break; + case 'a': + openmode |= JAS_STREAM_APPEND; + break; + case '+': + openmode |= JAS_STREAM_READ | JAS_STREAM_WRITE; + break; + default: + break; + } + ++s; + } + return openmode; +} + +jas_stream_t *JPEG2000_VSIL_fopen(const char *filename, const char *mode) +{ + jas_stream_t *stream; + jas_stream_VSIFL_t *obj; + + /* Allocate a stream object. */ + if (!(stream = JPEG2000_VSIL_jas_stream_create())) { + return 0; + } + + /* Parse the mode string. */ + stream->openmode_ = JPEG2000_VSIL_jas_strtoopenmode(mode); + + /* Allocate space for the underlying file stream object. */ + if (!(obj = (jas_stream_VSIFL_t*) jas_malloc(sizeof(jas_stream_VSIFL_t)))) { + JPEG2000_VSIL_jas_stream_destroy(stream); + return 0; + } + obj->fp = NULL; + stream->obj_ = (void *) obj; + + /* Select the operations for a file stream object. */ + stream->ops_ = &JPEG2000_VSIL_stream_fileops; + + /* Open the underlying file. */ + if ((obj->fp = VSIFOpenL(filename, mode)) == NULL) { + JPEG2000_VSIL_jas_stream_destroy(stream); + return 0; + } + + /* By default, use full buffering for this type of stream. */ + JPEG2000_VSIL_jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0); + + return stream; +} diff --git a/bazaar/plugin/gdal/frmts/jpeg2000/jpeg2000_vsil_io.h b/bazaar/plugin/gdal/frmts/jpeg2000/jpeg2000_vsil_io.h new file mode 100644 index 000000000..7a560e29a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg2000/jpeg2000_vsil_io.h @@ -0,0 +1,37 @@ +/****************************************************************************** + * $Id: jpeg2000_vsil_io.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: JPEG-2000 + * Purpose: Return a stream for a VSIL file + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef JPEG2000_VSIL_IO_H +#define JPEG2000_VSIL_IO_H + +#include + +jas_stream_t *JPEG2000_VSIL_fopen(const char *filename, const char *mode); + +#endif diff --git a/bazaar/plugin/gdal/frmts/jpeg2000/jpeg2000dataset.cpp b/bazaar/plugin/gdal/frmts/jpeg2000/jpeg2000dataset.cpp new file mode 100644 index 000000000..69f46d443 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg2000/jpeg2000dataset.cpp @@ -0,0 +1,1418 @@ +/****************************************************************************** + * $Id: jpeg2000dataset.cpp 29171 2015-05-07 19:49:07Z rouault $ + * + * Project: JPEG-2000 + * Purpose: Partial implementation of the ISO/IEC 15444-1 standard + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2002, Andrey Kiselev + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdaljp2abstractdataset.h" +#include "gdaljp2metadata.h" +#include "cpl_string.h" + +#include +#include "jpeg2000_vsil_io.h" + +CPL_CVSID("$Id: jpeg2000dataset.cpp 29171 2015-05-07 19:49:07Z rouault $"); + +CPL_C_START +void GDALRegister_JPEG2000(void); +CPL_C_END + +// XXX: Part of code below extracted from the JasPer internal headers and +// must be in sync with JasPer version (this one works with JasPer 1.900.1) +#define JP2_FTYP_MAXCOMPATCODES 32 +#define JP2_BOX_IHDR 0x69686472 /* Image Header */ +#define JP2_BOX_BPCC 0x62706363 /* Bits Per Component */ +#define JP2_BOX_PCLR 0x70636c72 /* Palette */ +#define JP2_BOX_UUID 0x75756964 /* UUID */ +extern "C" { +typedef struct { + uint_fast32_t magic; +} jp2_jp_t; +typedef struct { + uint_fast32_t majver; + uint_fast32_t minver; + uint_fast32_t numcompatcodes; + uint_fast32_t compatcodes[JP2_FTYP_MAXCOMPATCODES]; +} jp2_ftyp_t; +typedef struct { + uint_fast32_t width; + uint_fast32_t height; + uint_fast16_t numcmpts; + uint_fast8_t bpc; + uint_fast8_t comptype; + uint_fast8_t csunk; + uint_fast8_t ipr; +} jp2_ihdr_t; +typedef struct { + uint_fast16_t numcmpts; + uint_fast8_t *bpcs; +} jp2_bpcc_t; +typedef struct { + uint_fast8_t method; + uint_fast8_t pri; + uint_fast8_t approx; + uint_fast32_t csid; + uint_fast8_t *iccp; + int iccplen; +} jp2_colr_t; +typedef struct { + uint_fast16_t numlutents; + uint_fast8_t numchans; + int_fast32_t *lutdata; + uint_fast8_t *bpc; +} jp2_pclr_t; +typedef struct { + uint_fast16_t channo; + uint_fast16_t type; + uint_fast16_t assoc; +} jp2_cdefchan_t; +typedef struct { + uint_fast16_t numchans; + jp2_cdefchan_t *ents; +} jp2_cdef_t; +typedef struct { + uint_fast16_t cmptno; + uint_fast8_t map; + uint_fast8_t pcol; +} jp2_cmapent_t; + +typedef struct { + uint_fast16_t numchans; + jp2_cmapent_t *ents; +} jp2_cmap_t; + +#ifdef HAVE_JASPER_UUID +typedef struct { + uint_fast32_t datalen; + uint_fast8_t uuid[16]; + uint_fast8_t *data; +} jp2_uuid_t; +#endif + +struct jp2_boxops_s; +typedef struct { + + struct jp2_boxops_s *ops; + struct jp2_boxinfo_s *info; + + uint_fast32_t type; + + /* The length of the box including the (variable-length) header. */ + uint_fast32_t len; + + /* The length of the box data. */ + uint_fast32_t datalen; + + union { + jp2_jp_t jp; + jp2_ftyp_t ftyp; + jp2_ihdr_t ihdr; + jp2_bpcc_t bpcc; + jp2_colr_t colr; + jp2_pclr_t pclr; + jp2_cdef_t cdef; + jp2_cmap_t cmap; +#ifdef HAVE_JASPER_UUID + jp2_uuid_t uuid; +#endif + } data; + +} jp2_box_t; + +typedef struct jp2_boxops_s { + void (*init)(jp2_box_t *box); + void (*destroy)(jp2_box_t *box); + int (*getdata)(jp2_box_t *box, jas_stream_t *in); + int (*putdata)(jp2_box_t *box, jas_stream_t *out); + void (*dumpdata)(jp2_box_t *box, FILE *out); +} jp2_boxops_t; + +extern jp2_box_t *jp2_box_create(int type); +extern void jp2_box_destroy(jp2_box_t *box); +extern jp2_box_t *jp2_box_get(jas_stream_t *in); +extern int jp2_box_put(jp2_box_t *box, jas_stream_t *out); +#ifdef HAVE_JASPER_UUID +int jp2_encode_uuid(jas_image_t *image, jas_stream_t *out, + char *optstr, jp2_box_t *uuid); +#endif +} +// XXX: End of JasPer header. + +/************************************************************************/ +/* ==================================================================== */ +/* JPEG2000Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +class JPEG2000Dataset : public GDALJP2AbstractDataset +{ + friend class JPEG2000RasterBand; + + jas_stream_t *psStream; + jas_image_t *psImage; + int iFormat; + int bPromoteTo8Bit; + + int bAlreadyDecoded; + int DecodeImage(); + + public: + JPEG2000Dataset(); + ~JPEG2000Dataset(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JPEG2000RasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class JPEG2000RasterBand : public GDALPamRasterBand +{ + friend class JPEG2000Dataset; + + // NOTE: poDS may be altered for NITF/JPEG2000 files! + JPEG2000Dataset *poGDS; + + jas_matrix_t *psMatrix; + + int iDepth; + int bSignedness; + + public: + + JPEG2000RasterBand( JPEG2000Dataset *, int, int, int ); + ~JPEG2000RasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); +}; + + +/************************************************************************/ +/* JPEG2000RasterBand() */ +/************************************************************************/ + +JPEG2000RasterBand::JPEG2000RasterBand( JPEG2000Dataset *poDS, int nBand, + int iDepth, int bSignedness ) + +{ + this->poDS = poDS; + poGDS = poDS; + this->nBand = nBand; + this->iDepth = iDepth; + this->bSignedness = bSignedness; + + // XXX: JasPer can't handle data with depth > 32 bits + // Maximum possible depth for JPEG2000 is 38! + switch ( bSignedness ) + { + case 1: // Signed component + if (iDepth <= 8) + this->eDataType = GDT_Byte; // FIXME: should be signed, + // but we haven't signed byte + // data type in GDAL + else if (iDepth <= 16) + this->eDataType = GDT_Int16; + else if (iDepth <= 32) + this->eDataType = GDT_Int32; + break; + case 0: // Unsigned component + default: + if (iDepth <= 8) + this->eDataType = GDT_Byte; + else if (iDepth <= 16) + this->eDataType = GDT_UInt16; + else if (iDepth <= 32) + this->eDataType = GDT_UInt32; + break; + } + // FIXME: Figure out optimal block size! + // Should the block size be fixed or determined dynamically? + nBlockXSize = MIN(256, poDS->nRasterXSize); + nBlockYSize = MIN(256, poDS->nRasterYSize); + psMatrix = jas_matrix_create(nBlockYSize, nBlockXSize); + + if( iDepth % 8 != 0 && !poDS->bPromoteTo8Bit ) + { + SetMetadataItem( "NBITS", + CPLString().Printf("%d",iDepth), + "IMAGE_STRUCTURE" ); + } + SetMetadataItem( "COMPRESSION", "JP2000", "IMAGE_STRUCTURE" ); +} + +/************************************************************************/ +/* ~JPEG2000RasterBand() */ +/************************************************************************/ + +JPEG2000RasterBand::~JPEG2000RasterBand() +{ + if ( psMatrix ) + jas_matrix_destroy( psMatrix ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr JPEG2000RasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + int i, j; + + // Decode image from the stream, if not yet + if ( !poGDS->DecodeImage() ) + { + return CE_Failure; + } + + // Now we can calculate the pixel offset of the top left by multiplying + // block offset with the block size. + + /* In case the dimensions of the image are not multiple of the block dimensions */ + /* take care of not requesting more pixels than available for the blocks at the */ + /* right or bottom of the image */ + int nWidthToRead = MIN(nBlockXSize, poGDS->nRasterXSize - nBlockXOff * nBlockXSize); + int nHeightToRead = MIN(nBlockYSize, poGDS->nRasterYSize - nBlockYOff * nBlockYSize); + + jas_image_readcmpt( poGDS->psImage, nBand - 1, + nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, + nWidthToRead, nHeightToRead, psMatrix ); + + int nWordSize = GDALGetDataTypeSize(eDataType) / 8; + int nLineSize = nBlockXSize * nWordSize; + GByte* ptr = (GByte*)pImage; + + /* Pad incomplete blocks at the right or bottom of the image */ + if (nWidthToRead != nBlockXSize || nHeightToRead != nBlockYSize) + memset(pImage, 0, nLineSize * nBlockYSize); + + for( i = 0; i < nHeightToRead; i++, ptr += nLineSize ) + { + for( j = 0; j < nWidthToRead; j++ ) + { + // XXX: We need casting because matrix element always + // has 32 bit depth in JasPer + // FIXME: what about float values? + switch( eDataType ) + { + case GDT_Int16: + { + ((GInt16*)ptr)[j] = (GInt16)jas_matrix_get(psMatrix, i, j); + } + break; + case GDT_Int32: + { + ((GInt32*)ptr)[j] = (GInt32)jas_matrix_get(psMatrix, i, j); + } + break; + case GDT_UInt16: + { + ((GUInt16*)ptr)[j] = (GUInt16)jas_matrix_get(psMatrix, i, j); + } + break; + case GDT_UInt32: + { + ((GUInt32*)ptr)[j] = (GUInt32)jas_matrix_get(psMatrix, i, j); + } + break; + case GDT_Byte: + default: + { + ((GByte*)ptr)[j] = (GByte)jas_matrix_get(psMatrix, i, j); + } + break; + } + } + } + + if( poGDS->bPromoteTo8Bit && nBand == 4 ) + { + ptr = (GByte*)pImage; + for( i = 0; i < nHeightToRead; i++, ptr += nLineSize ) + { + for( j = 0; j < nWidthToRead; j++ ) + { + ((GByte*)ptr)[j] *= 255; + } + } + } + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp JPEG2000RasterBand::GetColorInterpretation() +{ + // Decode image from the stream, if not yet + if ( !poGDS->DecodeImage() ) + { + return GCI_Undefined; + } + + if ( jas_clrspc_fam( jas_image_clrspc( poGDS->psImage ) ) == + JAS_CLRSPC_FAM_GRAY ) + return GCI_GrayIndex; + else if ( jas_clrspc_fam( jas_image_clrspc( poGDS->psImage ) ) == + JAS_CLRSPC_FAM_RGB ) + { + switch ( jas_image_cmpttype( poGDS->psImage, nBand - 1 ) ) + { + case JAS_IMAGE_CT_RGB_R: + return GCI_RedBand; + case JAS_IMAGE_CT_RGB_G: + return GCI_GreenBand; + case JAS_IMAGE_CT_RGB_B: + return GCI_BlueBand; + case JAS_IMAGE_CT_OPACITY: + return GCI_AlphaBand; + default: + return GCI_Undefined; + } + } + else + return GCI_Undefined; +} + +/************************************************************************/ +/* ==================================================================== */ +/* JPEG2000Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* JPEG2000Dataset() */ +/************************************************************************/ + +JPEG2000Dataset::JPEG2000Dataset() +{ + psStream = NULL; + psImage = NULL; + nBands = 0; + bAlreadyDecoded = FALSE; + bPromoteTo8Bit = FALSE; + + poDriver = (GDALDriver *)GDALGetDriverByName("JPEG2000"); +} + +/************************************************************************/ +/* ~JPEG2000Dataset() */ +/************************************************************************/ + +JPEG2000Dataset::~JPEG2000Dataset() + +{ + FlushCache(); + + if ( psStream ) + jas_stream_close( psStream ); + if ( psImage ) + jas_image_destroy( psImage ); +} + +/************************************************************************/ +/* DecodeImage() */ +/************************************************************************/ +int JPEG2000Dataset::DecodeImage() +{ + if (bAlreadyDecoded) + return psImage != NULL; + + bAlreadyDecoded = TRUE; + if ( !( psImage = jas_image_decode(psStream, iFormat, 0) ) ) + { + CPLDebug( "JPEG2000", "Unable to decode image. Format: %s, %d", + jas_image_fmttostr( iFormat ), iFormat ); + return FALSE; + } + + /* Case of a JP2 image : check that the properties given by */ + /* the JP2 boxes match the ones of the code stream */ + if (nBands != 0) + { + if (nBands != jas_image_numcmpts( psImage )) + { + CPLError(CE_Failure, CPLE_AppDefined, + "The number of components indicated in the IHDR box (%d) mismatch " + "the value specified in the code stream (%d)", + nBands, jas_image_numcmpts( psImage )); + jas_image_destroy( psImage ); + psImage = NULL; + return FALSE; + } + + if (nRasterXSize != jas_image_cmptwidth( psImage, 0 ) || + nRasterYSize != jas_image_cmptheight( psImage, 0 ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "The dimensions indicated in the IHDR box (%d x %d) mismatch " + "the value specified in the code stream (%d x %d)", + nRasterXSize, nRasterYSize, + (int)jas_image_cmptwidth( psImage, 0 ), + (int)jas_image_cmptheight( psImage, 0 )); + jas_image_destroy( psImage ); + psImage = NULL; + return FALSE; + } + + int iBand; + for ( iBand = 0; iBand < nBands; iBand++ ) + { + JPEG2000RasterBand* poBand = (JPEG2000RasterBand*) GetRasterBand(iBand+1); + if (poBand->iDepth != jas_image_cmptprec( psImage, iBand ) || + poBand->bSignedness != jas_image_cmptsgnd( psImage, iBand )) + { + CPLError(CE_Failure, CPLE_AppDefined, + "The bit depth of band %d indicated in the IHDR box (%d) mismatch " + "the value specified in the code stream (%d)", + iBand + 1, poBand->iDepth, jas_image_cmptprec( psImage, iBand )); + jas_image_destroy( psImage ); + psImage = NULL; + return FALSE; + } + } + } + + /* Ask for YCbCr -> RGB translation */ + if ( jas_clrspc_fam( jas_image_clrspc( psImage ) ) == + JAS_CLRSPC_FAM_YCBCR ) + { + jas_image_t *psRGBImage; + jas_cmprof_t *psRGBProf; + CPLDebug( "JPEG2000", "forcing conversion to sRGB"); + if (!(psRGBProf = jas_cmprof_createfromclrspc(JAS_CLRSPC_SRGB))) { + CPLDebug( "JPEG2000", "cannot create sRGB profile"); + return TRUE; + } + if (!(psRGBImage = jas_image_chclrspc(psImage, psRGBProf, JAS_CMXFORM_INTENT_PER))) { + CPLDebug( "JPEG2000", "cannot convert to sRGB"); + jas_cmprof_destroy(psRGBProf); + return TRUE; + } + jas_image_destroy(psImage); + jas_cmprof_destroy(psRGBProf); + psImage = psRGBImage; + } + + return TRUE; +} + +static void JPEG2000Init() +{ + static int bHasInit = FALSE; + if (!bHasInit) + { + bHasInit = TRUE; + jas_init(); + } +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int JPEG2000Dataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + static const unsigned char jpc_header[] = {0xff,0x4f}; + static const unsigned char jp2_box_jp[] = {0x6a,0x50,0x20,0x20}; /* 'jP ' */ + + if( poOpenInfo->nHeaderBytes >= 16 + && (memcmp( poOpenInfo->pabyHeader, jpc_header, + sizeof(jpc_header) ) == 0 + || memcmp( poOpenInfo->pabyHeader + 4, jp2_box_jp, + sizeof(jp2_box_jp) ) == 0 + /* PGX file*/ + || (memcmp( poOpenInfo->pabyHeader, "PG", 2) == 0 && + (poOpenInfo->pabyHeader[2] == ' ' || poOpenInfo->pabyHeader[2] == '\t') && + (memcmp( poOpenInfo->pabyHeader + 3, "ML", 2) == 0 || + memcmp( poOpenInfo->pabyHeader + 3, "LM", 2) == 0))) ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *JPEG2000Dataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int iFormat; + char *pszFormatName = NULL; + jas_stream_t *sS; + + if (!Identify(poOpenInfo)) + return NULL; + + JPEG2000Init(); + if( !(sS = JPEG2000_VSIL_fopen( poOpenInfo->pszFilename, "rb" )) ) + { + return NULL; + } + + iFormat = jas_image_getfmt( sS ); + if ( !(pszFormatName = jas_image_fmttostr( iFormat )) ) + { + jas_stream_close( sS ); + return NULL; + } + if ( strlen( pszFormatName ) < 3 || + (!EQUALN( pszFormatName, "jp2", 3 ) && + !EQUALN( pszFormatName, "jpc", 3 ) && + !EQUALN( pszFormatName, "pgx", 3 )) ) + { + CPLDebug( "JPEG2000", "JasPer reports file is format type `%s'.", + pszFormatName ); + jas_stream_close( sS ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + jas_stream_close(sS); + CPLError( CE_Failure, CPLE_NotSupported, + "The JPEG2000 driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + JPEG2000Dataset *poDS; + int *paiDepth = NULL, *pabSignedness = NULL; + int iBand; + + poDS = new JPEG2000Dataset(); + + poDS->psStream = sS; + poDS->iFormat = iFormat; + + if ( EQUALN( pszFormatName, "jp2", 3 ) ) + { + // XXX: Hack to read JP2 boxes from input file. JasPer hasn't public + // API call for such things, so we will use internal JasPer functions. + jp2_box_t *box; + box = 0; + while ( ( box = jp2_box_get(poDS->psStream) ) ) + { + switch (box->type) + { + case JP2_BOX_IHDR: + poDS->nBands = box->data.ihdr.numcmpts; + poDS->nRasterXSize = box->data.ihdr.width; + poDS->nRasterYSize = box->data.ihdr.height; + CPLDebug( "JPEG2000", + "IHDR box found. Dump: " + "width=%d, height=%d, numcmpts=%d, bpp=%d", + (int)box->data.ihdr.width, (int)box->data.ihdr.height, + (int)box->data.ihdr.numcmpts, (box->data.ihdr.bpc & 0x7F) + 1 ); + /* ISO/IEC 15444-1:2004 I.5.3.1 specifies that 255 means that all */ + /* components have not the same bit depth and/or sign and that a */ + /* BPCC box must then follow to specify them for each component */ + if ( box->data.ihdr.bpc != 255 ) + { + paiDepth = (int *)CPLMalloc(poDS->nBands * sizeof(int)); + pabSignedness = (int *)CPLMalloc(poDS->nBands * sizeof(int)); + for ( iBand = 0; iBand < poDS->nBands; iBand++ ) + { + paiDepth[iBand] = (box->data.ihdr.bpc & 0x7F) + 1; + pabSignedness[iBand] = box->data.ihdr.bpc >> 7; + CPLDebug( "JPEG2000", + "Component %d: bpp=%d, signedness=%d", + iBand, paiDepth[iBand], pabSignedness[iBand] ); + } + } + break; + + case JP2_BOX_BPCC: + CPLDebug( "JPEG2000", "BPCC box found. Dump:" ); + if ( !paiDepth && !pabSignedness ) + { + paiDepth = (int *) + CPLMalloc( box->data.bpcc.numcmpts * sizeof(int) ); + pabSignedness = (int *) + CPLMalloc( box->data.bpcc.numcmpts * sizeof(int) ); + for( iBand = 0; iBand < (int)box->data.bpcc.numcmpts; iBand++ ) + { + paiDepth[iBand] = (box->data.bpcc.bpcs[iBand] & 0x7F) + 1; + pabSignedness[iBand] = box->data.bpcc.bpcs[iBand] >> 7; + CPLDebug( "JPEG2000", + "Component %d: bpp=%d, signedness=%d", + iBand, paiDepth[iBand], pabSignedness[iBand] ); + } + } + break; + + case JP2_BOX_PCLR: + CPLDebug( "JPEG2000", + "PCLR box found. Dump: number of LUT entries=%d, " + "number of resulting channels=%d", + (int)box->data.pclr.numlutents, box->data.pclr.numchans ); + poDS->nBands = box->data.pclr.numchans; + if ( paiDepth ) + CPLFree( paiDepth ); + if ( pabSignedness ) + CPLFree( pabSignedness ); + paiDepth = (int *) + CPLMalloc( box->data.pclr.numchans * sizeof(int) ); + pabSignedness = (int *) + CPLMalloc( box->data.pclr.numchans * sizeof(int) ); + for( iBand = 0; iBand < (int)box->data.pclr.numchans; iBand++ ) + { + paiDepth[iBand] = (box->data.pclr.bpc[iBand] & 0x7F) + 1; + pabSignedness[iBand] = box->data.pclr.bpc[iBand] >> 7; + CPLDebug( "JPEG2000", + "Component %d: bpp=%d, signedness=%d", + iBand, paiDepth[iBand], pabSignedness[iBand] ); + } + break; + } + jp2_box_destroy( box ); + box = 0; + } + if( !paiDepth || !pabSignedness ) + { + delete poDS; + CPLDebug( "JPEG2000", "Unable to read JP2 header boxes.\n" ); + return NULL; + } + if ( jas_stream_rewind( poDS->psStream ) < 0 ) + { + delete poDS; + CPLDebug( "JPEG2000", "Unable to rewind input stream.\n" ); + return NULL; + } + } + else + { + if ( !poDS->DecodeImage() ) + { + delete poDS; + return NULL; + } + + poDS->nBands = jas_image_numcmpts( poDS->psImage ); + poDS->nRasterXSize = jas_image_cmptwidth( poDS->psImage, 0 ); + poDS->nRasterYSize = jas_image_cmptheight( poDS->psImage, 0 ); + paiDepth = (int *)CPLMalloc( poDS->nBands * sizeof(int) ); + pabSignedness = (int *)CPLMalloc( poDS->nBands * sizeof(int) ); + for ( iBand = 0; iBand < poDS->nBands; iBand++ ) + { + paiDepth[iBand] = jas_image_cmptprec( poDS->psImage, iBand ); + pabSignedness[iBand] = jas_image_cmptsgnd( poDS->psImage, iBand ); + } + } + + if ( !GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || + !GDALCheckBandCount(poDS->nBands, 0) ) + { + CPLFree( paiDepth ); + CPLFree( pabSignedness ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Should we promote alpha channel to 8 bits ? */ +/* -------------------------------------------------------------------- */ + poDS->bPromoteTo8Bit = (poDS->nBands == 4 && + paiDepth[0] == 8 && + paiDepth[1] == 8 && + paiDepth[2] == 8 && + paiDepth[3] == 1 && + CSLFetchBoolean(poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION", TRUE)); + if( poDS->bPromoteTo8Bit ) + CPLDebug( "JPEG2000", "Fourth (alpha) band is promoted from 1 bit to 8 bit"); + +/* -------------------------------------------------------------------- */ + +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + + + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + { + poDS->SetBand( iBand, new JPEG2000RasterBand( poDS, iBand, + paiDepth[iBand - 1], pabSignedness[iBand - 1] ) ); + + } + + if ( paiDepth ) + CPLFree( paiDepth ); + if ( pabSignedness ) + CPLFree( pabSignedness ); + + poDS->LoadJP2Metadata(poOpenInfo); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Vector layers */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nOpenFlags & GDAL_OF_VECTOR ) + { + poDS->LoadVectorLayers( + CSLFetchBoolean(poOpenInfo->papszOpenOptions, "OPEN_REMOTE_GML", FALSE)); + + // If file opened in vector-only mode and there's no vector, + // return + if( (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 && + poDS->GetLayerCount() == 0 ) + { + delete poDS; + return NULL; + } + } + + return( poDS ); +} + +/************************************************************************/ +/* JPEG2000CreateCopy() */ +/************************************************************************/ + +static GDALDataset * +JPEG2000CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int iBand; + GDALRasterBand *poBand; + + if( nBands == 0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to export files with zero bands." ); + return NULL; + } + + if (poSrcDS->GetRasterBand(1)->GetColorTable() != NULL) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "JPEG2000 driver ignores color table. " + "The source raster band will be considered as grey level.\n" + "Consider using color table expansion (-expand option in gdal_translate)\n"); + if (bStrict) + return NULL; + } + + for ( iBand = 0; iBand < nBands; iBand++ ) + { + poBand = poSrcDS->GetRasterBand( iBand + 1); + + switch ( poBand->GetRasterDataType() ) + { + case GDT_Byte: + case GDT_Int16: + case GDT_UInt16: + break; + + default: + if( !CSLTestBoolean(CPLGetConfigOption("JPEG2000_FORCE_CREATION", "NO")) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "A band of the source dataset is of type %s, which might cause crashes in libjasper. " + "Set JPEG2000_FORCE_CREATION configuration option to YES to attempt the creation of the file.", + GDALGetDataTypeName(poBand->GetRasterDataType())); + return NULL; + } + break; + } + } + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + jas_stream_t *psStream; + jas_image_t *psImage; + + JPEG2000Init(); + const char* pszAccess = EQUALN(pszFilename, "/vsisubfile/", 12) ? "r+b" : "w+b"; + if( !(psStream = JPEG2000_VSIL_fopen( pszFilename, pszAccess) ) ) + { + CPLError( CE_Failure, CPLE_FileIO, "Unable to create file %s.\n", + pszFilename ); + return NULL; + } + + if ( !(psImage = jas_image_create0()) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Unable to create image %s.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Loop over image, copying image data. */ +/* -------------------------------------------------------------------- */ + GUInt32 *paiScanline; + int iLine, iPixel; + CPLErr eErr = CE_None; + jas_matrix_t *psMatrix; + jas_image_cmptparm_t *sComps; // Array of pointers to image components + + sComps = (jas_image_cmptparm_t*) + CPLMalloc( nBands * sizeof(jas_image_cmptparm_t) ); + + if ( !(psMatrix = jas_matrix_create( 1, nXSize )) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to create matrix with size %dx%d.\n", 1, nYSize ); + CPLFree( sComps ); + jas_image_destroy( psImage ); + return NULL; + } + paiScanline = (GUInt32 *) CPLMalloc( nXSize * + GDALGetDataTypeSize(GDT_UInt32) / 8 ); + + for ( iBand = 0; iBand < nBands; iBand++ ) + { + poBand = poSrcDS->GetRasterBand( iBand + 1); + + sComps[iBand].tlx = sComps[iBand].tly = 0; + sComps[iBand].hstep = sComps[iBand].vstep = 1; + sComps[iBand].width = nXSize; + sComps[iBand].height = nYSize; + sComps[iBand].prec = GDALGetDataTypeSize( poBand->GetRasterDataType() ); + switch ( poBand->GetRasterDataType() ) + { + case GDT_Int16: + case GDT_Int32: + case GDT_Float32: + case GDT_Float64: + sComps[iBand].sgnd = 1; + break; + case GDT_Byte: + case GDT_UInt16: + case GDT_UInt32: + default: + sComps[iBand].sgnd = 0; + break; + } + jas_image_addcmpt(psImage, iBand, sComps); + + for( iLine = 0; eErr == CE_None && iLine < nYSize; iLine++ ) + { + eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + paiScanline, nXSize, 1, GDT_UInt32, + sizeof(GUInt32), sizeof(GUInt32) * nXSize, NULL ); + for ( iPixel = 0; iPixel < nXSize; iPixel++ ) + jas_matrix_setv( psMatrix, iPixel, paiScanline[iPixel] ); + + if( (jas_image_writecmpt(psImage, iBand, 0, iLine, + nXSize, 1, psMatrix)) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to write scanline %d of the component %d.\n", + iLine, iBand ); + jas_matrix_destroy( psMatrix ); + CPLFree( paiScanline ); + CPLFree( sComps ); + jas_image_destroy( psImage ); + return NULL; + } + + if( eErr == CE_None && + !pfnProgress( ((iLine + 1) + iBand * nYSize) / + ((double) nYSize * nBands), + NULL, pProgressData) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Read compression parameters and encode the image. */ +/* -------------------------------------------------------------------- */ + int i, j; + const int OPTSMAX = 4096; + const char *pszFormatName; + char pszOptionBuf[OPTSMAX + 1]; + + const char *apszComprOptions[]= + { + "imgareatlx", + "imgareatly", + "tilegrdtlx", + "tilegrdtly", + "tilewidth", + "tileheight", + "prcwidth", + "prcheight", + "cblkwidth", + "cblkheight", + "mode", + "rate", + "ilyrrates", + "prg", + "numrlvls", + "sop", + "eph", + "lazy", + "termall", + "segsym", + "vcausal", + "pterm", + "resetprob", + "numgbits", + NULL + }; + + pszFormatName = CSLFetchNameValue( papszOptions, "FORMAT" ); + if ( !pszFormatName || + (!EQUALN( pszFormatName, "jp2", 3 ) && + !EQUALN( pszFormatName, "jpc", 3 ) ) ) + pszFormatName = "jp2"; + + pszOptionBuf[0] = '\0'; + if ( papszOptions ) + { + CPLDebug( "JPEG2000", "User supplied parameters:" ); + for ( i = 0; papszOptions[i] != NULL; i++ ) + { + CPLDebug( "JPEG2000", "%s\n", papszOptions[i] ); + for ( j = 0; apszComprOptions[j] != NULL; j++ ) + if( EQUALN( apszComprOptions[j], papszOptions[i], + strlen(apszComprOptions[j]) ) ) + { + int m, n; + + n = strlen( pszOptionBuf ); + m = n + strlen( papszOptions[i] ) + 1; + if ( m > OPTSMAX ) + break; + if ( n > 0 ) + { + strcat( pszOptionBuf, "\n" ); + } + strcat( pszOptionBuf, papszOptions[i] ); + } + } + } + CPLDebug( "JPEG2000", "Parameters, delivered to the JasPer library:" ); + CPLDebug( "JPEG2000", "%s", pszOptionBuf ); + + if ( nBands == 1 ) // Grayscale + { + jas_image_setclrspc( psImage, JAS_CLRSPC_SGRAY ); + jas_image_setcmpttype( psImage, 0, JAS_IMAGE_CT_GRAY_Y ); + } + else if ( nBands == 3 || nBands == 4 ) // Assume as RGB(A) + { + jas_image_setclrspc( psImage, JAS_CLRSPC_SRGB ); + for ( iBand = 0; iBand < nBands; iBand++ ) + { + poBand = poSrcDS->GetRasterBand( iBand + 1); + switch ( poBand->GetColorInterpretation() ) + { + case GCI_RedBand: + jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_RGB_R ); + break; + case GCI_GreenBand: + jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_RGB_G ); + break; + case GCI_BlueBand: + jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_RGB_B ); + break; + case GCI_AlphaBand: + jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_OPACITY ); + break; + default: + jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_UNKNOWN ); + break; + } + } + } + else // Unknown + { + /* JAS_CLRSPC_UNKNOWN causes crashes in Jasper jp2_enc.c at line 231 */ + /* iccprof = jas_iccprof_createfromcmprof(jas_image_cmprof(image)); */ + /* but if we explictely set the cmprof, it does not work better */ + /* since it would abort at line 281 later ... */ + /* So the best option is to switch to gray colorspace */ + /* And we need to switch at the band level too, otherwise Kakadu or */ + /* JP2MrSID don't like it */ + //jas_image_setclrspc( psImage, JAS_CLRSPC_UNKNOWN ); + jas_image_setclrspc( psImage, JAS_CLRSPC_SGRAY ); + for ( iBand = 0; iBand < nBands; iBand++ ) + //jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_UNKNOWN ); + jas_image_setcmpttype( psImage, iBand, JAS_IMAGE_CT_GRAY_Y ); + } + +/* -------------------------------------------------------------------- */ +/* Set the GeoTIFF box if georeferencing is available, and this */ +/* is a JP2 file. */ +/* -------------------------------------------------------------------- */ + if ( EQUALN( pszFormatName, "jp2", 3 ) ) + { +#ifdef HAVE_JASPER_UUID + double adfGeoTransform[6]; + if( CSLFetchBoolean( papszOptions, "GeoJP2", TRUE ) && + ((poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None + && (adfGeoTransform[0] != 0.0 + || adfGeoTransform[1] != 1.0 + || adfGeoTransform[2] != 0.0 + || adfGeoTransform[3] != 0.0 + || adfGeoTransform[4] != 0.0 + || ABS(adfGeoTransform[5]) != 1.0)) + || poSrcDS->GetGCPCount() > 0 + || poSrcDS->GetMetadata("RPC") != NULL ) ) + { + GDALJP2Metadata oJP2Geo; + + if( poSrcDS->GetGCPCount() > 0 ) + { + oJP2Geo.SetProjection( poSrcDS->GetGCPProjection() ); + oJP2Geo.SetGCPs( poSrcDS->GetGCPCount(), poSrcDS->GetGCPs() ); + } + else + { + oJP2Geo.SetProjection( poSrcDS->GetProjectionRef() ); + oJP2Geo.SetGeoTransform( adfGeoTransform ); + } + + oJP2Geo.SetRPCMD( poSrcDS->GetMetadata("RPC") ); + + const char* pszAreaOrPoint = poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT); + oJP2Geo.bPixelIsPoint = pszAreaOrPoint != NULL && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT); + + GDALJP2Box *poBox = oJP2Geo.CreateJP2GeoTIFF(); + jp2_box_t *box = jp2_box_create( JP2_BOX_UUID ); + memcpy( box->data.uuid.uuid, poBox->GetUUID(), 16 ); + box->data.uuid.datalen = poBox->GetDataLength() - 16; + box->data.uuid.data = + (uint_fast8_t *)jas_malloc( poBox->GetDataLength() - 16 ); + memcpy( box->data.uuid.data, poBox->GetWritableData() + 16, + poBox->GetDataLength() - 16 ); + delete poBox; + poBox = NULL; + + if ( jp2_encode_uuid( psImage, psStream, pszOptionBuf, box) < 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to encode image %s.", pszFilename ); + jp2_box_destroy( box ); + jas_matrix_destroy( psMatrix ); + CPLFree( paiScanline ); + CPLFree( sComps ); + jas_image_destroy( psImage ); + return NULL; + } + jp2_box_destroy( box ); + } + else + { +#endif + if ( jp2_encode( psImage, psStream, pszOptionBuf) < 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to encode image %s.", pszFilename ); + jas_matrix_destroy( psMatrix ); + CPLFree( paiScanline ); + CPLFree( sComps ); + jas_image_destroy( psImage ); + return NULL; + } +#ifdef HAVE_JASPER_UUID + } +#endif + } + else // Write JPC code stream + { + if ( jpc_encode(psImage, psStream, pszOptionBuf) < 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to encode image %s.\n", pszFilename ); + jas_matrix_destroy( psMatrix ); + CPLFree( paiScanline ); + CPLFree( sComps ); + jas_image_destroy( psImage ); + return NULL; + } + } + + jas_stream_flush( psStream ); + + jas_matrix_destroy( psMatrix ); + CPLFree( paiScanline ); + CPLFree( sComps ); + jas_image_destroy( psImage ); + if ( jas_stream_close( psStream ) ) + { + CPLError( CE_Failure, CPLE_FileIO, "Unable to close file %s.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Add GMLJP2 box at end of file. */ +/* -------------------------------------------------------------------- */ + if ( EQUALN( pszFormatName, "jp2", 3 ) ) + { + double adfGeoTransform[6]; + if( CSLFetchBoolean( papszOptions, "GMLJP2", TRUE ) && + poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None && + poSrcDS->GetProjectionRef() != NULL && + poSrcDS->GetProjectionRef()[0] != '\0' ) + { + VSILFILE* fp = VSIFOpenL(pszFilename, "rb+"); + if( fp ) + { + // Look for jp2c box and patch its LBox to be the real box size + // instead of zero + int bOK = FALSE; + GUInt32 nLBox; + GUInt32 nTBox; + + while(TRUE) + { + if( VSIFReadL(&nLBox, 4, 1, fp) != 1 || + VSIFReadL(&nTBox, 4, 1, fp) != 1 ) + break; + nLBox = CPL_MSBWORD32( nLBox ); + if( memcmp(&nTBox, "jp2c", 4) == 0 ) + { + if( nLBox >= 8 ) + { + bOK = TRUE; + break; + } + if( nLBox == 0 ) + { + vsi_l_offset nPos = VSIFTellL(fp); + VSIFSeekL(fp, 0, SEEK_END); + vsi_l_offset nEnd = VSIFTellL(fp); + VSIFSeekL(fp, nPos - 8, SEEK_SET); + nLBox = (GUInt32)(8 + nEnd - nPos); + if( nLBox == (vsi_l_offset)8 + nEnd - nPos ) + { + nLBox = CPL_MSBWORD32( nLBox ); + VSIFWriteL(&nLBox, 1, 4, fp); + bOK = TRUE; + } + } + break; + } + if( nLBox < 8 ) + break; + VSIFSeekL(fp, nLBox - 8, SEEK_CUR); + } + + // Can write GMLJP2 box + if( bOK ) + { + GDALJP2Metadata oJP2MD; + oJP2MD.SetProjection( poSrcDS->GetProjectionRef() ); + oJP2MD.SetGeoTransform( adfGeoTransform ); + GDALJP2Box *poBox; + const char* pszGMLJP2V2Def = CSLFetchNameValue( papszOptions, "GMLJP2V2_DEF" ); + if( pszGMLJP2V2Def != NULL ) + poBox = oJP2MD.CreateGMLJP2V2(nXSize,nYSize,pszGMLJP2V2Def,poSrcDS); + else + poBox = oJP2MD.CreateGMLJP2(nXSize,nYSize); + + nLBox = (int) poBox->GetDataLength() + 8; + nLBox = CPL_MSBWORD32( nLBox ); + memcpy(&nTBox, poBox->GetType(), 4); + + VSIFSeekL(fp, 0, SEEK_END); + VSIFWriteL( &nLBox, 4, 1, fp ); + VSIFWriteL( &nTBox, 4, 1, fp ); + VSIFWriteL(poBox->GetWritableData(), 1, (int) poBox->GetDataLength(), fp); + VSIFCloseL(fp); + + delete poBox; + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Do we need a world file? */ +/* -------------------------------------------------------------------- */ + if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) ) + { + double adfGeoTransform[6]; + + poSrcDS->GetGeoTransform( adfGeoTransform ); + GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform ); + } + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); + GDALPamDataset *poDS = (GDALPamDataset*) JPEG2000Dataset::Open(&oOpenInfo); + + if( poDS ) + { + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT & (~GCIF_METADATA) ); + + /* Only write relevant metadata to PAM, and if needed */ + char** papszSrcMD = CSLDuplicate(poSrcDS->GetMetadata()); + papszSrcMD = CSLSetNameValue(papszSrcMD, GDALMD_AREA_OR_POINT, NULL); + papszSrcMD = CSLSetNameValue(papszSrcMD, "Corder", NULL); + for(char** papszSrcMDIter = papszSrcMD; + papszSrcMDIter && *papszSrcMDIter; ) + { + /* Remove entries like KEY= (without value) */ + if( (*papszSrcMDIter)[0] && + (*papszSrcMDIter)[strlen((*papszSrcMDIter))-1] == '=' ) + { + CPLFree(*papszSrcMDIter); + memmove(papszSrcMDIter, papszSrcMDIter + 1, + sizeof(char*) * (CSLCount(papszSrcMDIter + 1) + 1)); + } + else + ++papszSrcMDIter; + } + char** papszMD = CSLDuplicate(poDS->GetMetadata()); + papszMD = CSLSetNameValue(papszMD, GDALMD_AREA_OR_POINT, NULL); + if( papszSrcMD && papszSrcMD[0] != NULL && + CSLCount(papszSrcMD) != CSLCount(papszMD) ) + { + poDS->SetMetadata(papszSrcMD); + } + CSLDestroy(papszSrcMD); + CSLDestroy(papszMD); + } + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_JPEG2000() */ +/************************************************************************/ + +void GDALRegister_JPEG2000() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("JPEG2000 driver")) + return; + + if( GDALGetDriverByName( "JPEG2000" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "JPEG2000" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "JPEG-2000 part 1 (ISO/IEC 15444-1), based on Jasper library" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_jpeg2000.html" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32" ); + poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/jp2" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jp2" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" " ); + + poDriver->pfnIdentify = JPEG2000Dataset::Identify; + poDriver->pfnOpen = JPEG2000Dataset::Open; + poDriver->pfnCreateCopy = JPEG2000CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/jpeg2000/makefile.vc b/bazaar/plugin/gdal/frmts/jpeg2000/makefile.vc new file mode 100644 index 000000000..47f354866 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpeg2000/makefile.vc @@ -0,0 +1,14 @@ + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +OBJ = jpeg2000dataset.obj jpeg2000_vsil_io.obj +EXTRAFLAGS = $(JASPER_INCLUDE) /DWIN32 -DFRMT_jpeg2000 + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/jpegls/GNUmakefile b/bazaar/plugin/gdal/frmts/jpegls/GNUmakefile new file mode 100644 index 000000000..6c7ff47b0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpegls/GNUmakefile @@ -0,0 +1,13 @@ + +OBJ = jpeglsdataset.o + +include ../../GDALmake.opt + +CPPFLAGS := $(CPPFLAGS) $(CHARLS_INC) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/jpegls/frmt_jpegls.html b/bazaar/plugin/gdal/frmts/jpegls/frmt_jpegls.html new file mode 100644 index 000000000..9695e6f31 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpegls/frmt_jpegls.html @@ -0,0 +1,35 @@ + + +JPEGLS + + + + +

    JPEGLS

    + +

    (GDAL >= 1.8.0)

    + +

    This driver is an implementation of a JPEG-LS reader/writer based on the Open Source CharLS library (BSD style license). +At the time of writing, the CharLS library in its 1.0 version has no "make install" target. Consequently, the integration of the driver in the +GDAL build system is a big rough. On Unix, you have to edit the GDALmake.opt file and edit the lines related to CHARLS.

    + +

    The driver can read and write lossless or near-lossless images. Note that it is not aimed at dealing with too big images (unless +enough virtual memory is available), since the whole image must be compressed/decompressed in a single operation.

    + +

    Creation Options

    + +
      +
    • INTERLEAVE=PIXEL/LINE/BAND : Data interleaving in compressed stream. Default to BAND.

    • +
    • LOSS_FACTOR=error_threshold : 0 (the default) means loss-less compression. Any higher value will be the maximum bound for the error.

    • +
    + +

    See Also:

    + + + + + + diff --git a/bazaar/plugin/gdal/frmts/jpegls/jpeglsdataset.cpp b/bazaar/plugin/gdal/frmts/jpegls/jpeglsdataset.cpp new file mode 100644 index 000000000..d13807de6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpegls/jpeglsdataset.cpp @@ -0,0 +1,695 @@ +/****************************************************************************** + * $Id: jpeglsdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $ + * + * Project: JPEGLS driver based on CharLS library + * Purpose: JPEGLS driver based on CharLS library + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" + +/* CharLS header */ +#include + +extern "C" void GDALRegister_JPEGLS(); + +/* g++ -Wall -g fmrts/jpegls/jpeglsdataset.cpp -shared -fPIC -o gdal_JPEGLS.so -Iport -Igcore -L. -lgdal -I/home/even/charls-1.0 -L/home/even/charls-1.0/build -lCharLS */ + +CPL_CVSID("$Id: jpeglsdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $"); + +/************************************************************************/ +/* ==================================================================== */ +/* JPEGLSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class JPEGLSDataset : public GDALPamDataset +{ + friend class JPEGLSRasterBand; + + CPLString osFilename; + GByte* pabyUncompressedData; + int bHasUncompressed; + int nBitsPerSample; + int nOffset; + + CPLErr Uncompress(); + + static int Identify( GDALOpenInfo * poOpenInfo, int& bIsDCOM ); + + public: + JPEGLSDataset(); + ~JPEGLSDataset(); + + static int Identify( GDALOpenInfo * poOpenInfo ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JPEGLSRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class JPEGLSRasterBand : public GDALPamRasterBand +{ + friend class JPEGLSDataset; + + public: + + JPEGLSRasterBand( JPEGLSDataset * poDS, int nBand); + ~JPEGLSRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); +}; + + +/************************************************************************/ +/* JPEGLSRasterBand() */ +/************************************************************************/ + +JPEGLSRasterBand::JPEGLSRasterBand( JPEGLSDataset *poDS, int nBand) + +{ + this->poDS = poDS; + this->nBand = nBand; + this->eDataType = (poDS->nBitsPerSample <= 8) ? GDT_Byte : GDT_Int16; + this->nBlockXSize = poDS->nRasterXSize; + this->nBlockYSize = poDS->nRasterYSize; +} + +/************************************************************************/ +/* ~JPEGLSRasterBand() */ +/************************************************************************/ + +JPEGLSRasterBand::~JPEGLSRasterBand() +{ +} + + +/************************************************************************/ +/* JPEGLSGetErrorAsString() */ +/************************************************************************/ + +static const char* JPEGLSGetErrorAsString(JLS_ERROR eCode) +{ + switch(eCode) + { + case OK: return "OK"; + case InvalidJlsParameters: return "InvalidJlsParameters"; + case ParameterValueNotSupported: return "ParameterValueNotSupported"; + case UncompressedBufferTooSmall: return "UncompressedBufferTooSmall"; + case CompressedBufferTooSmall: return "CompressedBufferTooSmall"; + case InvalidCompressedData: return "InvalidCompressedData"; + case ImageTypeNotSupported: return "ImageTypeNotSupported"; + case UnsupportedBitDepthForTransform: return "UnsupportedBitDepthForTransform"; + case UnsupportedColorTransform: return "UnsupportedColorTransform"; + default: return "unknown"; + }; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr JPEGLSRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + JPEGLSDataset *poGDS = (JPEGLSDataset *) poDS; + + if (!poGDS->bHasUncompressed) + { + CPLErr eErr = poGDS->Uncompress(); + if (eErr != CE_None) + return eErr; + } + + if (poGDS->pabyUncompressedData == NULL) + return CE_Failure; + + int i, j; + if (eDataType == GDT_Byte) + { + for(j=0;jpabyUncompressedData[ + poGDS->nBands * (j * nBlockXSize + i) + nBand - 1]; + } + } + } + else + { + for(j=0;jpabyUncompressedData)[ + poGDS->nBands * (j * nBlockXSize + i) + nBand - 1]; + } + } + } + + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp JPEGLSRasterBand::GetColorInterpretation() +{ + JPEGLSDataset *poGDS = (JPEGLSDataset *) poDS; + + if (poGDS->nBands == 1) + return GCI_GrayIndex; + else if (poGDS->nBands == 3 || poGDS->nBands == 4) + { + switch(nBand) + { + case 1: + return GCI_RedBand; + case 2: + return GCI_GreenBand; + case 3: + return GCI_BlueBand; + case 4: + return GCI_AlphaBand; + default: + return GCI_Undefined; + } + } + + return GCI_Undefined; +} + +/************************************************************************/ +/* ==================================================================== */ +/* JPEGLSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* JPEGLSDataset() */ +/************************************************************************/ + +JPEGLSDataset::JPEGLSDataset() +{ + pabyUncompressedData = NULL; + bHasUncompressed = FALSE; + nBitsPerSample = 0; +} + +/************************************************************************/ +/* ~JPEGLSDataset() */ +/************************************************************************/ + +JPEGLSDataset::~JPEGLSDataset() + +{ + VSIFree(pabyUncompressedData); +} + +/************************************************************************/ +/* Uncompress() */ +/************************************************************************/ + +CPLErr JPEGLSDataset::Uncompress() +{ + if (bHasUncompressed) + return CE_None; + + bHasUncompressed = TRUE; + + VSILFILE* fp = VSIFOpenL(osFilename, "rb"); + if (!fp) + return CE_Failure; + + VSIFSeekL(fp, 0, SEEK_END); + int nFileSize = (int)VSIFTellL(fp) - nOffset; + VSIFSeekL(fp, 0, SEEK_SET); + + GByte* pabyCompressedData = (GByte*)VSIMalloc(nFileSize); + if (pabyCompressedData == NULL) + { + VSIFCloseL(fp); + return CE_Failure; + } + + VSIFSeekL(fp, nOffset, SEEK_SET); + VSIFReadL(pabyCompressedData, 1, nFileSize, fp); + VSIFCloseL(fp); + fp = NULL; + + int nUncompressedSize = nRasterXSize * nRasterYSize * + nBands * (GDALGetDataTypeSize(GetRasterBand(1)->GetRasterDataType()) / 8); + pabyUncompressedData = (GByte*)VSIMalloc(nUncompressedSize); + if (pabyUncompressedData == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + VSIFree(pabyCompressedData); + return CE_Failure; + } + + + JLS_ERROR eError = JpegLsDecode(pabyUncompressedData, nUncompressedSize, pabyCompressedData, nFileSize, NULL); + if (eError != OK) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Uncompression of data failed : %s", + JPEGLSGetErrorAsString(eError)); + VSIFree(pabyCompressedData); + VSIFree(pabyUncompressedData); + pabyUncompressedData = NULL; + return CE_Failure; + } + + VSIFree(pabyCompressedData); + + return CE_None; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int JPEGLSDataset::Identify( GDALOpenInfo * poOpenInfo, int& bIsDCOM ) + +{ + GByte *pabyHeader = poOpenInfo->pabyHeader; + int nHeaderBytes = poOpenInfo->nHeaderBytes; + + bIsDCOM = FALSE; + + if( nHeaderBytes < 10 ) + return FALSE; + + if( pabyHeader[0] != 0xff + || pabyHeader[1] != 0xd8 ) + { + /* Is it a DICOM JPEG-LS ? */ + if (nHeaderBytes < 1024) + return FALSE; + + char abyEmpty[128]; + memset(abyEmpty, 0, sizeof(abyEmpty)); + if (memcmp(pabyHeader, abyEmpty, sizeof(abyEmpty)) != 0) + return FALSE; + + if (memcmp(pabyHeader + 128, "DICM", 4) != 0) + return FALSE; + + int i; + for(i=0;i<1024 - 22;i++) + { + if (memcmp(pabyHeader + i, "1.2.840.10008.1.2.4.80", 22) == 0) + { + bIsDCOM = TRUE; + return TRUE; + } + if (memcmp(pabyHeader + i, "1.2.840.10008.1.2.4.81", 22) == 0) + { + bIsDCOM = TRUE; + return TRUE; + } + } + + return FALSE; + } + + int nOffset = 2; + for (;nOffset + 4 < nHeaderBytes;) + { + if (pabyHeader[nOffset] != 0xFF) + return FALSE; + + int nMarker = pabyHeader[nOffset + 1]; + if (nMarker == 0xF7 /* JPEG Extension 7, JPEG-LS */) + return TRUE; + if (nMarker == 0xC3 /* Start of Frame 3 */) + return TRUE; + + nOffset += 2 + pabyHeader[nOffset + 2] * 256 + pabyHeader[nOffset + 3]; + } + + return FALSE; +} + +int JPEGLSDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + int bIsDCOM; + return Identify(poOpenInfo, bIsDCOM); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *JPEGLSDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int bIsDCOM; + if (!Identify(poOpenInfo, bIsDCOM)) + return NULL; + + JlsParameters sParams; + + JLS_ERROR eError; + int nOffset = 0; + + if (!bIsDCOM) + { + eError = JpegLsReadHeader(poOpenInfo->pabyHeader, + poOpenInfo->nHeaderBytes, &sParams); + } + else + { + VSILFILE* fp = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + if (fp == NULL) + return NULL; + GByte abyBuffer[1028]; + GByte abySignature[] = { 0xFF, 0xD8, 0xFF, 0xF7 }; + while(TRUE) + { + if (VSIFReadL(abyBuffer, 1, 1028, fp) != 1028) + { + VSIFCloseL(fp); + return NULL; + } + int i; + for(i=0;i<1024;i++) + { + if (memcmp(abyBuffer + i, abySignature, 4) == 0) + { + nOffset += i; + break; + } + } + if (i != 1024) + break; + nOffset += 1024; + VSIFSeekL(fp, nOffset, SEEK_SET); + } + + VSIFSeekL(fp, nOffset, SEEK_SET); + VSIFReadL(abyBuffer, 1, 1024, fp); + eError = JpegLsReadHeader(abyBuffer, 1024, &sParams); + VSIFCloseL(fp); + if (eError == OK) + { + CPLDebug("JPEGLS", "JPEGLS image found at offset %d", nOffset); + } + } + if (eError != OK) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read header : %s", + JPEGLSGetErrorAsString(eError)); + return NULL; + } + + if (sParams.bitspersample > 16) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupport bitspersample : %d", + sParams.bitspersample); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + JPEGLSDataset *poDS; + int iBand; + + poDS = new JPEGLSDataset(); + poDS->osFilename = poOpenInfo->pszFilename; + poDS->nRasterXSize = sParams.width; + poDS->nRasterYSize = sParams.height; + poDS->nBands = sParams.components; + poDS->nBitsPerSample = sParams.bitspersample; + poDS->nOffset = nOffset; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + { + poDS->SetBand( iBand, new JPEGLSRasterBand( poDS, iBand) ); + + if (poDS->nBitsPerSample != 8 && poDS->nBitsPerSample != 16) + { + poDS->GetRasterBand(iBand)->SetMetadataItem( "NBITS", + CPLString().Printf( "%d", poDS->nBitsPerSample ), + "IMAGE_STRUCTURE" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* JPEGCreateCopy() */ +/************************************************************************/ + +GDALDataset * +JPEGLSDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + if( nBands != 1 && nBands != 3 && nBands != 4 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "JPGLS driver doesn't support %d bands. Must be 1 (grey), " + "3 (RGB) or 4 bands.\n", nBands ); + + return NULL; + } + + if (nBands == 1 && + poSrcDS->GetRasterBand(1)->GetColorTable() != NULL) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "JPGLS driver ignores color table. " + "The source raster band will be considered as grey level.\n" + "Consider using color table expansion (-expand option in gdal_translate)\n"); + if (bStrict) + return NULL; + } + + GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + + if( eDT != GDT_Byte && eDT != GDT_Int16 ) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "JPGLS driver doesn't support data type %s", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); + + if (bStrict) + return NULL; + } + + int nWordSize = GDALGetDataTypeSize(eDT) / 8; + int nUncompressedSize = nXSize * nYSize * nBands * nWordSize; + int nCompressedSize = nUncompressedSize + 256; /* FIXME? bug in charls-1.0beta ?. I needed a "+ something" to avoid erros on byte.tif */ + GByte* pabyDataCompressed = (GByte*)VSIMalloc(nCompressedSize); + GByte* pabyDataUncompressed = (GByte*)VSIMalloc(nUncompressedSize); + if (pabyDataCompressed == NULL || pabyDataUncompressed == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + VSIFree(pabyDataCompressed); + VSIFree(pabyDataUncompressed); + return NULL; + } + + CPLErr eErr; + eErr = poSrcDS->s(GF_Read, 0, 0, nXSize, nYSize, + pabyDataUncompressed, nXSize, nYSize, + eDT, nBands, NULL, + nBands * nWordSize, nBands * nWordSize * nXSize, nWordSize, NULL); + if (eErr != CE_None) + { + VSIFree(pabyDataCompressed); + VSIFree(pabyDataUncompressed); + return NULL; + } + + size_t nWritten = 0; + + JlsParameters sParams; + memset(&sParams, 0, sizeof(sParams)); + sParams.width = nXSize; + sParams.height = nYSize; + sParams.bitspersample = (eDT == GDT_Byte) ? 8 : 16; + sParams.ilv = ILV_NONE; + + const char* pszINTERLEAVE = CSLFetchNameValue( papszOptions, "INTERLEAVE" ); + if (pszINTERLEAVE) + { + if (EQUAL(pszINTERLEAVE, "PIXEL")) + sParams.ilv = ILV_SAMPLE; + else if (EQUAL(pszINTERLEAVE, "LINE")) + sParams.ilv = ILV_LINE; + else if (EQUAL(pszINTERLEAVE, "BAND")) + sParams.ilv = ILV_NONE; + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unsupported value for INTERLEAVE : %s. Defaulting to BAND", + pszINTERLEAVE); + } + } + + const char* pszLOSSFACTOR = CSLFetchNameValue( papszOptions, "LOSS_FACTOR" ); + if (pszLOSSFACTOR) + { + int nLOSSFACTOR = atoi(pszLOSSFACTOR); + if (nLOSSFACTOR >= 0) + sParams.allowedlossyerror = nLOSSFACTOR; + } + + const char* pszNBITS = poSrcDS->GetRasterBand(1)->GetMetadataItem( "NBITS", "IMAGE_STRUCTURE" ); + if (pszNBITS != NULL) + { + int nBits = atoi(pszNBITS); + if (nBits != 8 && nBits != 16) + sParams.bitspersample = nBits; + } + + sParams.components = nBands; + JLS_ERROR eError = JpegLsEncode(pabyDataCompressed, nCompressedSize, + &nWritten, + pabyDataUncompressed, nUncompressedSize, + &sParams); + + VSIFree(pabyDataUncompressed); + pabyDataUncompressed = NULL; + + if (eError != OK) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Compression of data failed : %s", + JPEGLSGetErrorAsString(eError)); + VSIFree(pabyDataCompressed); + return NULL; + } + + VSILFILE* fp = VSIFOpenL(pszFilename, "wb"); + if (fp == NULL) + { + VSIFree(pabyDataCompressed); + return NULL; + } + VSIFWriteL(pabyDataCompressed, 1, nWritten, fp); + + VSIFree(pabyDataCompressed); + + VSIFCloseL(fp); + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + JPEGLSDataset *poDS = (JPEGLSDataset *) GDALOpen( pszFilename, GA_ReadOnly ); + + if( poDS ) + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_JPEGLS() */ +/************************************************************************/ + +void GDALRegister_JPEGLS() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("JPEGLS driver")) + return; + + if( GDALGetDriverByName( "JPEGLS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "JPEGLS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "JPEGLS" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_jpegls.html" ); + //poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/jls" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jls" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"\n" +" " +" \n" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = JPEGLSDataset::Identify; + poDriver->pfnOpen = JPEGLSDataset::Open; + poDriver->pfnCreateCopy = JPEGLSDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/jpipkak/GNUmakefile b/bazaar/plugin/gdal/frmts/jpipkak/GNUmakefile new file mode 100644 index 000000000..0144e0207 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpipkak/GNUmakefile @@ -0,0 +1,34 @@ + +include ../../GDALmake.opt + +OBJ = jpipkakdataset.o kdu_cache.o + +KAKINC = -I$(KAKDIR)/coresys/common -I$(KAKDIR)/apps/compressed_io \ + -I$(KAKDIR)/apps/jp2 -I$(KAKDIR)/apps/image -I$(KAKDIR)/apps/args \ + -I$(KAKDIR)/apps/support -I$(KAKDIR)/apps/kdu_compress \ + -I$(KAKDIR)/apps/caching_sources + +APPOBJ = $(KAK_OBJ) + +INSTOBJ = $(foreach d,$(APPOBJ),../o/$(notdir $(d))) + +#CXXFLAGS := $(CXXFLAGS) -DFILEIO_DEBUG + +CPPFLAGS := $(KAKINC) -I. $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +# kdu_cache.cpp does not appear to be built by default by the Kakadu +# apps makefiles, so we copy it and build it ourselves. Perhaps this +# won't be needed with newer versions of Kakadu. Tested with 6.2. +kdu_cache.cpp: $(KAKDIR)/apps/caching_sources/kdu_cache.cpp + cp $(KAKDIR)/apps/caching_sources/kdu_cache.cpp . + +clean: + rm -f *.o $(O_OBJ) + rm -f $(INSTOBJ) + rm -f kdu_cache.cpp + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + + diff --git a/bazaar/plugin/gdal/frmts/jpipkak/components.PNG b/bazaar/plugin/gdal/frmts/jpipkak/components.PNG new file mode 100644 index 000000000..b63ec4b99 Binary files /dev/null and b/bazaar/plugin/gdal/frmts/jpipkak/components.PNG differ diff --git a/bazaar/plugin/gdal/frmts/jpipkak/frmt_jpipkak.html b/bazaar/plugin/gdal/frmts/jpipkak/frmt_jpipkak.html new file mode 100644 index 000000000..af34fe5c9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpipkak/frmt_jpipkak.html @@ -0,0 +1,249 @@ + + +JPIPKAK - JPIP Streaming + + +

    JPIPKAK - JPIP Streaming

    +

    + JPEG 2000 Interactive Protocol (JPIP) flexibility with respect to random access, code stream reordering and incremental decoding is highly exploitable in a networked environment allowing access to remote large files using limited bandwidth connections or high contention networks. +

    +

    JPIPKAK - JPIP Overview

    +

    + A brief overview of the JPIP event sequence is presented in this section, more information can be found at JPEG 2000 Interactive Protocol (Part 9 – JPIP) and the specification can (and should) be purchased from ISO. +

    +

    + An earlier version of JPEG 2000 Part 9 is available here http://www.jpeg.org/public/fcd15444-9v2.pdf, noting the ISO copyright, diagrams are not replicated in this documentation. +

    +

    + The JPIP protocol has been abstracted in this format driver, requests are made at the 1:1 resolution level. +

    + JPIP Sequence Diagram +
      +
    1. Initial JPIP request for a target image, a target id, a session over http, data to be returned as a jpp-stream are requested and a maximum length is put on the response. In this case no initial window is requested, though it can be. +Server responds with a target identifier that can be used to identify the image on the server and a JPIP-cnew response header which includes the path to the JPIP server which will handle all future requests and a cid session identifier. A session is required so that that the server can model the state of the client connection, only sending the data that is required. +
    2. +
    3. Client requests particular view windows on the target image with a maximum response length and includes the session identifier established in the previous communication. +'fsiz' is used to identify the resolution associated with the requested view-window. The values 'fx' and 'fy' specify the dimensions of the desired image resolution. +'roff' is used to identify the upper left hand corner off the spatial region associated with the requested view-windw. +'rsiz' is used to identify the horizontal and vertical extents of the spatial region associated with the requested view-window.
    4. +
    +

    JPIPKAK -approach

    +

    + The JPIPKAK driver uses an approach that was first demonstrated here, J2KViewer, by Juan Pablo Garcia Ortiz of separating the communication layer (socket / http) from the Kakadu kdu_cache object. Separating the communication layer from the data object is desirable since it allows the use of optimized http client libraries such as libcurl, Apache HttpClient (note that jportiz used a plain Java socket) and allows SSL communication between the client and server. +

    +

    +Kakadu's implementation of client communication with a JPIP server uses a socket, and this socket connection holds the state for this client session. A client session with Kakadu can be recreated using the JPIP cache operations between client and server, but no use of traditional HTTP cookies is supported since JPIP is neutral to the transport layer. +

    +

    +The JPIPKAK driver is written using a HTTP client library with the Kakadu cache object and supports optimized communication with a JPIP server (which may or may not support HTTP sessions) and the high performance of the kakadu kdu_region_decompressor. +

    + Component Diagram +

    JPIPKAK - implementation

    +

    + The implementation supports the GDAL C++ and C API, and provides an initial SWIG wrapper for this driver with a Java ImageIO example (TODO - qGIS Example). +

    +

    + The driver uses a simple threading model to support requesting reads of the data and remote fetching. This threading model supports two separate client windows, with just one connection to the server. Requests to the server are multiplexed to utilize available bandwidth efficiently. The client identifies these windows by using “0” (low) or “1” (high) values to a “PRIORITY” metadata request option. +

    + +

    + Note: SSL support +

    +

    If the client is built with support for SSL, then driver determines whether to use SSL if the request is a jpips:// protocol as opposed to jpip:// . Note that the driver does not verify server certificates using the Curl certificate bundle and is currently set to accept all SSL server certificates. +

    +

    + Note: libCurl +

    +

    + JPIP sets client/server values using HTTP headers, modifications have been made to the GDAL HTTP portability library to support this. +

    +
    + GDAL Sequence Diagram +
      +
    1. + GDALGetDatasetDriver +

      + Fetch the driver to which this dataset relates. +

      +
    2. +
    3. + Open +

      + If the filename contained in the GDALOpenInfo object has a case insensitive URI scheme of jpip or jpips the JPIPKAKDataset is created and initialised, otherwise NULL is returned. +

      +
    4. +
    5. + Initialize +

      + Initialisation involves making an initial connection to the JPIP Server to establish a session and to retrieve the initial metadata about the image (ref. JPIP Sequence Diagram). +

      +

      + If the connection fails, the function returns false and the Open function returns NULL indicating that opening the dataset with this driver failed. +

      +

      + If the connection is successful, then subsequent requests to the JPIP server are made to retrieve all the available metadata about the image. Metadata items are set using the GDALMajorObject->SetMetadataItem in the "JPIP" domain. +

      +

      + If the metadata returned from the server includes GeoJP2 UUID box, or a GMLJP2 XML box then this metadata is parsed and sets the geographic metadata of this dataset. +

      +
    6. +
    7. + GDALGetMetadata +

      + C API to JPIPKAKDataset->GetMetadata +

      +
    8. +
    9. + GetMetadata +

      + returns metadata for the "JPIP" domain, keys are "JPIP_NQUALITYLAYERS", "JPIP_NRESOLUTIONLEVELS", "JPIP_NCOMPS" and "JPIP_SPRECISION" +

      +
    10. +
    11. + GDALEndAsyncRasterIO +

      + If the asynchronous raster IO is active and not required, the C API calls JPIPKAKDataset->EndAsyncRasterIO +

      +
    12. +
    13. + EndAsyncRasterIO +

      + The JPIPKAKAsyncRasterIO object is deleted +

      +
    14. +
    15. + delete +

      +

      +
    16. +
    17. + GDALBeginAsyncRasterIO +

      + C API to JPIPKAKDataset->BeginAsyncRasterIO +

      +
    18. +
    19. + BeginAsyncRasterIO +

      + The client has set the requested view window at 1:1 and have optionally set the discard level, quality layers and thread priority metadata items. +

      +
    20. +
    21. + Create +

      + Creates a JPIPKAKAsyncRasterIO Object +

      +
    22. +
    23. + Start +

      + Configures the kakadu machinery and starts a background thread (if not already running) to communicate to the server the current view window request. The background thread results in the kdu_cache object being updated until the JPIP server sends an "End Of Response" (EOR) message for the current view window request. +

      +
    24. +
    25. + GDALLockBuffer +

      + C API to LockBuffer +

      +
    26. +
    27. + LockBuffer +

      + Not implemented in JPIPKAKAsyncRasterIO, a lock is acquired in JPIPKAKAsyncRasterIO->GetNextUpdatedRegion +

      +
    28. +
    29. + GDALGetNextUpdatedRegion +

      + C API to GetNextUpdatedRegion +

      +
    30. +
    31. + GetNextUpdatedRegion +

      + The function decompresses the available data to generate an image (according to the dataset buffer type set in JPIPKAKDataset->BeginAsyncRasterIO) + The window width, height (at the requested discard level) decompressed is returned in the region pointer and can be rendered by the client. + The status of the rendering operation is one of GARIO_PENDING, GARIO_UPDATE, GARIO_ERROR, GARIO_COMPLETE from the GDALAsyncStatusType structure. GARIO_UPDATE, GARIO_PENDING require more reads of GetNextUpdatedRegion to get the full image data, this is the progressive rendering of JPIP. GARIO_COMPLETE indicates the window is complete. +

      +

      + GDALAsyncStatusType is a structure used byGetNextUpdatedRegion to indicate whether the function should be called again when either kakadu has more data in its cache to decompress, or the server has not sent an End Of Response (EOR) message to indicate the request window is complete. +

      +

      + The region passed into this function is passed by reference, and the caller can read this region when the result returns to find the region that has been decompressed. The image data is packed into the buffer, e.g. RGB if the region requested has 3 components. +

      +
    32. +
    33. + GDALUnlockBuffer +

      + C Api to UnlockBuffer +

      +
    34. +
    35. + UnlockBuffer +

      + Not implemented in JPIPKAKAsyncRasterIO, a lock is acquired in JPIPKAKAsyncRasterIO->GetNextUpdatedRegion +

      +
    36. +
    37. + Draw +

      + Client renders image data +

      +
    38. +
    39. + GDALLockBuffer +

      +

      +
    40. +
    41. + LockBuffer +

      +

      +
    42. +
    43. + GDALGetNextUpdatedRegion +

      +

      +
    44. +
    45. + GetNextUpdatedRegion +

      +

      +
    46. +
    47. + GDALUnlockBuffer +

      +

      +
    48. +
    49. + UnlockBuffer +

      +

      +
    50. +
    51. + Draw +

      +

      +
    52. +
    +

    JPIPKAK - installation requirements

    + +

    Currently only a Windows makefile is provided, however this should compile on Linux as well as there are no Windows dependencies.

    +

    See Also: +

    +

    +

    NOTES

    +

    + Driver originally developed by ITT VIS and donated to GDAL to enable SSL enabled JPIP client streaming of remote JPEG 2000 datasets. +

    + + \ No newline at end of file diff --git a/bazaar/plugin/gdal/frmts/jpipkak/gdalsequence.PNG b/bazaar/plugin/gdal/frmts/jpipkak/gdalsequence.PNG new file mode 100644 index 000000000..5ea4a5b7e Binary files /dev/null and b/bazaar/plugin/gdal/frmts/jpipkak/gdalsequence.PNG differ diff --git a/bazaar/plugin/gdal/frmts/jpipkak/jpipkakdataset.cpp b/bazaar/plugin/gdal/frmts/jpipkak/jpipkakdataset.cpp new file mode 100644 index 000000000..a0253cf2f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpipkak/jpipkakdataset.cpp @@ -0,0 +1,2126 @@ +/****************************************************************************** + * $Id: jpipkakdataset.cpp 2008-10-01 nbarker $ + * + * Project: jpip read driver + * Purpose: GDAL bindings for JPIP. + * Author: Norman Barker, ITT VIS, norman.barker@gmail.com + * + ****************************************************************************** + * ITT Visual Information Systems grants you use of this code, under the + * following license: + * + * Copyright (c) 2000-2007, ITT Visual Information Solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +**/ + +#include "jpipkakdataset.h" + + +/* +** The following are for testing premature stream termination support. +** This is a mechanism to test handling of failed or incomplete reads +** from the server, and is not normally active. For this reason we +** don't worry about the non-threadsafe nature of the debug support +** variables below. +*/ + +#ifdef DEBUG +# define PST_DEBUG 1 +#endif + +#ifdef PST_DEBUG +static int nPSTTargetInstance = -1; +static int nPSTThisInstance = -1; +static int nPSTTargetOffset = -1; +#endif + +/************************************************************************/ +/* ==================================================================== */ +/* Set up messaging services */ +/* ==================================================================== */ +/************************************************************************/ + +class jpipkak_kdu_cpl_error_message : public kdu_message +{ +public: // Member classes + jpipkak_kdu_cpl_error_message( CPLErr eErrClass ) + { + m_eErrClass = eErrClass; + m_pszError = NULL; + } + + void put_text(const char *string) + { + if( m_pszError == NULL ) + m_pszError = CPLStrdup( string ); + else + { + m_pszError = (char *) + CPLRealloc(m_pszError, strlen(m_pszError) + strlen(string)+1 ); + strcat( m_pszError, string ); + } + } + + class JP2KAKException + { + }; + + void flush(bool end_of_message=false) + { + if( m_pszError == NULL ) + return; + if( m_pszError[strlen(m_pszError)-1] == '\n' ) + m_pszError[strlen(m_pszError)-1] = '\0'; + + CPLError( m_eErrClass, CPLE_AppDefined, "%s", m_pszError ); + CPLFree( m_pszError ); + m_pszError = NULL; + + if( end_of_message && m_eErrClass == CE_Failure ) + { + throw new JP2KAKException(); + } + } + +private: + CPLErr m_eErrClass; + char *m_pszError; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JPIPKAKRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* JPIPKAKRasterBand() */ +/************************************************************************/ + +JPIPKAKRasterBand::JPIPKAKRasterBand( int nBand, int nDiscardLevels, + kdu_codestream *oCodeStream, + int nResCount, + JPIPKAKDataset *poBaseDSIn ) + +{ + this->nBand = nBand; + poBaseDS = poBaseDSIn; + + eDataType = poBaseDSIn->eDT; + + this->nDiscardLevels = nDiscardLevels; + this->oCodeStream = oCodeStream; + + oCodeStream->apply_input_restrictions( 0, 0, nDiscardLevels, 0, NULL ); + oCodeStream->get_dims( 0, band_dims ); + + nRasterXSize = band_dims.size.x; + nRasterYSize = band_dims.size.y; + +/* -------------------------------------------------------------------- */ +/* Use a 2048x128 "virtual" block size unless the file is small. */ +/* -------------------------------------------------------------------- */ + if( nRasterXSize >= 2048 ) + nBlockXSize = 2048; + else + nBlockXSize = nRasterXSize; + + if( nRasterYSize >= 256 ) + nBlockYSize = 128; + else + nBlockYSize = nRasterYSize; + +/* -------------------------------------------------------------------- */ +/* Figure out the color interpretation for this band. */ +/* -------------------------------------------------------------------- */ + + eInterp = GCI_Undefined; + +/* -------------------------------------------------------------------- */ +/* Do we have any overviews? Only check if we are the full res */ +/* image. */ +/* -------------------------------------------------------------------- */ + nOverviewCount = 0; + papoOverviewBand = 0; + + if( nDiscardLevels == 0 ) + { + int nXSize = nRasterXSize, nYSize = nRasterYSize; + + for( int nDiscard = 1; nDiscard < nResCount; nDiscard++ ) + { + kdu_dims dims; + + nXSize = (nXSize+1) / 2; + nYSize = (nYSize+1) / 2; + + if( (nXSize+nYSize) < 128 || nXSize < 4 || nYSize < 4 ) + continue; /* skip super reduced resolution layers */ + + oCodeStream->apply_input_restrictions( 0, 0, nDiscard, 0, NULL ); + oCodeStream->get_dims( 0, dims ); + + if( (dims.size.x == nXSize || dims.size.x == nXSize-1) + && (dims.size.y == nYSize || dims.size.y == nYSize-1) ) + { + nOverviewCount++; + papoOverviewBand = (JPIPKAKRasterBand **) + CPLRealloc( papoOverviewBand, + sizeof(void*) * nOverviewCount ); + papoOverviewBand[nOverviewCount-1] = + new JPIPKAKRasterBand( nBand, nDiscard, oCodeStream, 0, + poBaseDS ); + } + else + { + CPLDebug( "GDAL", "Discard %dx%d JPEG2000 overview layer,\n" + "expected %dx%d.", + dims.size.x, dims.size.y, nXSize, nYSize ); + } + } + } +} + +/************************************************************************/ +/* ~JPIPKAKRasterBand() */ +/************************************************************************/ + +JPIPKAKRasterBand::~JPIPKAKRasterBand() + +{ + for( int i = 0; i < nOverviewCount; i++ ) + delete papoOverviewBand[i]; + + CPLFree( papoOverviewBand ); +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int JPIPKAKRasterBand::GetOverviewCount() + +{ + return nOverviewCount; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *JPIPKAKRasterBand::GetOverview( int iOverviewIndex ) + +{ + if( iOverviewIndex < 0 || iOverviewIndex >= nOverviewCount ) + return NULL; + else + return papoOverviewBand[iOverviewIndex]; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr JPIPKAKRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + CPLDebug( "JPIPKAK", "IReadBlock(%d,%d) on band %d.", + nBlockXOff, nBlockYOff, nBand ); + +/* -------------------------------------------------------------------- */ +/* Fix the buffer layer. */ +/* -------------------------------------------------------------------- */ + int nPixelSpace = GDALGetDataTypeSize(eDataType) / 8; + int nLineSpace = nPixelSpace * nBlockXSize; + int nBandSpace = nLineSpace * nBlockYSize; + +/* -------------------------------------------------------------------- */ +/* Zoom up file window based on overview level so we are */ +/* referring to the full res image. */ +/* -------------------------------------------------------------------- */ + int nZoom = 1 << nDiscardLevels; + + int xOff = nBlockXOff * nBlockXSize * nZoom; + int yOff = nBlockYOff * nBlockYSize * nZoom; + int xSize = nBlockXSize * nZoom; + int ySize = nBlockYSize * nZoom; + + int nBufXSize = nBlockXSize; + int nBufYSize = nBlockYSize; + +/* -------------------------------------------------------------------- */ +/* Make adjustments for partial blocks on right and bottom. */ +/* -------------------------------------------------------------------- */ + if( xOff + xSize > poBaseDS->GetRasterXSize() ) + { + xSize = poBaseDS->GetRasterXSize() - xOff; + nBufXSize= MAX(xSize/nZoom,1); + } + + if( yOff + ySize > poBaseDS->GetRasterYSize() ) + { + ySize = poBaseDS->GetRasterYSize() - yOff; + nBufYSize = MAX(ySize/nZoom,1); + } + +/* -------------------------------------------------------------------- */ +/* Start the reader and run till complete. */ +/* -------------------------------------------------------------------- */ + GDALAsyncReader* ario = poBaseDS-> + BeginAsyncReader(xOff, yOff, xSize, ySize, + pImage, nBufXSize,nBufYSize, + eDataType, 1, &nBand, + nPixelSpace, nLineSpace, nBandSpace, NULL); + + if( ario == NULL ) + return CE_Failure; + + int nXBufOff; // absolute x image offset + int nYBufOff; // abolute y image offset + int nXBufSize; + int nYBufSize; + + GDALAsyncStatusType status; + + do + { + status = ario->GetNextUpdatedRegion(-1.0, + &nXBufOff, &nYBufOff, + &nXBufSize, &nYBufSize ); + } while (status != GARIO_ERROR && status != GARIO_COMPLETE ); + + poBaseDS->EndAsyncReader(ario); + + if (status == GARIO_ERROR) + return CE_Failure; + else + return CE_None; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr +JPIPKAKRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ +/* -------------------------------------------------------------------- */ +/* We need various criteria to skip out to block based methods. */ +/* -------------------------------------------------------------------- */ + if( poBaseDS->TestUseBlockIO( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + eBufType, 1, &nBand ) ) + return GDALPamRasterBand::IRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + +/* -------------------------------------------------------------------- */ +/* Otherwise do this as a single uncached async rasterio. */ +/* -------------------------------------------------------------------- */ + GDALAsyncReader* ario = + poBaseDS->BeginAsyncReader(nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + 1, &nBand, + nPixelSpace, nLineSpace, 0, NULL); + + if( ario == NULL ) + return CE_Failure; + + GDALAsyncStatusType status; + + do + { + int nXBufOff,nYBufOff,nXBufSize,nYBufSize; + + status = ario->GetNextUpdatedRegion(-1.0, + &nXBufOff, &nYBufOff, + &nXBufSize, &nYBufSize ); + } while (status != GARIO_ERROR && status != GARIO_COMPLETE ); + + poBaseDS->EndAsyncReader(ario); + + if (status == GARIO_ERROR) + return CE_Failure; + else + return CE_None; +} + +/*****************************************/ +/* JPIPKAKDataset() */ +/*****************************************/ +JPIPKAKDataset::JPIPKAKDataset() +{ + pszPath = NULL; + pszCid = NULL; + pszProjection = NULL; + + poCache = NULL; + poCodestream = NULL; + poDecompressor = NULL; + + nPos = 0; + nVBASLen = 0; + nVBASFirstByte = 0; + + nClassId = 0; + nCodestream = 0; + nDatabins = 0; + bWindowDone = FALSE; + bGeoTransformValid = FALSE; + + bNeedReinitialize = FALSE; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + nGCPCount = 0; + pasGCPList = NULL; + + bHighThreadRunning = 0; + bLowThreadRunning = 0; + bHighThreadFinished = 0; + bLowThreadFinished = 0; + nHighThreadByteCount = 0; + nLowThreadByteCount = 0; + + pGlobalMutex = CPLCreateMutex(); + CPLReleaseMutex(pGlobalMutex); +} + +/*****************************************/ +/* ~JPIPKAKDataset() */ +/*****************************************/ +JPIPKAKDataset::~JPIPKAKDataset() +{ + CPLHTTPCleanup(); + + Deinitialize(); + + CPLFree(pszProjection); + pszProjection = NULL; + + CPLFree(pszPath); + + if (nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } +} + +/************************************************************************/ +/* Deinitialize() */ +/* */ +/* Cleanup stuff that we will rebuild during a */ +/* reinitialization. */ +/************************************************************************/ + +void JPIPKAKDataset::Deinitialize() + +{ + CPLFree(pszCid); + pszCid = NULL; + + // frees decompressor as well + if (poCodestream) + { + poCodestream->destroy(); + delete poCodestream; + poCodestream = NULL; + } + + delete poDecompressor; + poDecompressor = NULL; + + delete poCache; + poCache = NULL; + + bNeedReinitialize = TRUE; +} + +/*****************************************/ +/* Initialize() */ +/*****************************************/ +int JPIPKAKDataset::Initialize(const char* pszDatasetName, int bReinitializing ) +{ + // set up message handlers + jpipkak_kdu_cpl_error_message oErrHandler( CE_Failure ); + jpipkak_kdu_cpl_error_message oWarningHandler( CE_Warning ); + kdu_customize_warnings(new jpipkak_kdu_cpl_error_message( CE_Warning ) ); + kdu_customize_errors(new jpipkak_kdu_cpl_error_message( CE_Failure ) ); + + // create necessary http headers + CPLString osHeaders = "HEADERS=Accept: jpp-stream"; + CPLString osPersistent; + + osPersistent.Printf( "PERSISTENT=JPIPKAK:%p", this ); + + char *apszOptions[] = { + (char *) osHeaders.c_str(), + (char *) osPersistent.c_str(), + NULL + }; + + // Setup url to have http in place of jpip protocol indicator. + CPLString osURL = "http"; + osURL += (pszDatasetName + 4); + + CPLAssert( strncmp(pszDatasetName,"jpip",4) == 0 ); + + // make initial request to the server for a session, we are going to + // assume that the jpip communication is stateful, rather than one-shot + // stateless requests append pszUrl with jpip request parameters for a + // stateful session (multi-shot communications) + // "cnew=http&type=jpp-stream&stream=0&tid=0&len=" + CPLString osRequest; + osRequest.Printf("%s?%s%i", osURL.c_str(), + "cnew=http&type=jpp-stream&stream=0&tid=0&len=", 2000); + + CPLHTTPResult *psResult = CPLHTTPFetch(osRequest, apszOptions); + + if ( psResult == NULL) + return FALSE; + + if( psResult->nDataLen == 0 || psResult->pabyData == NULL ) + { + + CPLError(CE_Failure, CPLE_AppDefined, + "No data was returned from the given URL" ); + CPLHTTPDestroyResult( psResult ); + return FALSE; + } + + if (psResult->nStatus != 0) + { + CPLHTTPDestroyResult( psResult ); + CPLError(CE_Failure, CPLE_AppDefined, + "Curl reports error: %d: %s", psResult->nStatus, psResult->pszErrBuf ); + return FALSE; + } + + // parse the response headers, and the initial data until we get to the + // codestream definition + char** pszHdrs = psResult->papszHeaders; + const char* pszCnew = CSLFetchNameValue(pszHdrs, "JPIP-cnew"); + + if( pszCnew == NULL ) + { + if( psResult->pszContentType != NULL + && EQUALN(psResult->pszContentType,"text/html",9) ) + CPLDebug( "JPIPKAK", "%s", + psResult->pabyData ); + + CPLHTTPDestroyResult( psResult ); + CPLError(CE_Failure, CPLE_AppDefined, + "Unable to parse required cnew and tid response headers" ); + + return FALSE; + } + + // parse cnew response + // JPIP-cnew: + // cid=DC69DF980A641A4BBDEB50E484A66578,path=MyPath,transport=http + char **papszTokens = CSLTokenizeString2( pszCnew, ",", CSLT_HONOURSTRINGS); + for (int i = 0; i < CSLCount(papszTokens); i++) + { + // looking for cid, path + if (EQUALN(papszTokens[i], "cid", 3)) + { + char *pszKey = NULL; + const char *pszValue = CPLParseNameValue(papszTokens[i], &pszKey ); + pszCid = CPLStrdup(pszValue); + CPLFree( pszKey ); + } + + if (EQUALN(papszTokens[i], "path", 4)) + { + char *pszKey = NULL; + const char *pszValue = CPLParseNameValue(papszTokens[i], &pszKey ); + pszPath = CPLStrdup(pszValue); + CPLFree( pszKey ); + } + } + + CSLDestroy(papszTokens); + + if( pszPath == NULL || pszCid == NULL ) + { + CPLHTTPDestroyResult(psResult); + CPLError(CE_Failure, CPLE_AppDefined, "Error parsing path and cid from cnew - %s", pszCnew); + return FALSE; + } + + // ok, good to go with jpip, get to the codestream before returning + // successful initialisation of the driver + try + { + poCache = new kdu_cache(); + poCodestream = new kdu_codestream(); + poDecompressor = new kdu_region_decompressor(); + + int bFinished = FALSE; + int bError = FALSE; + bFinished = ReadFromInput(psResult->pabyData, psResult->nDataLen, + bError ); + CPLHTTPDestroyResult(psResult); + + // continue making requests in the main thread to get all the available + // metadata for data bin 0, and reach the codestream + + // format the new request + // and set as pszRequestUrl; + // get the protocol from the original request + size_t found = osRequest.find_first_of("/"); + CPLString osProtocol = osRequest.substr(0, found + 2); + osRequest.erase(0, found + 2); + // find context path + found = osRequest.find_first_of("/"); + osRequest.erase(found); + + osRequestUrl.Printf("%s%s/%s?cid=%s&stream=0&len=%i", osProtocol.c_str(), osRequest.c_str(), pszPath, pszCid, 2000); + + while (!bFinished && !bError ) + { + CPLHTTPResult *psResult = CPLHTTPFetch(osRequestUrl, apszOptions); + bFinished = ReadFromInput(psResult->pabyData, psResult->nDataLen, + bError ); + CPLHTTPDestroyResult(psResult); + } + + if( bError ) + return FALSE; + + // clean up osRequest, remove variable len= parameter + size_t pos = osRequestUrl.find_last_of("&"); + osRequestUrl.erase(pos); + + // create codestream + poCache->set_read_scope(KDU_MAIN_HEADER_DATABIN, 0, 0); + poCodestream->create(poCache); + poCodestream->set_persistent(); + +/* -------------------------------------------------------------------- */ +/* If this is a reinitialization then we can hop out at this */ +/* point. The rest of the stuff was already done, and */ +/* hopefully the configuration is unchanged. */ +/* -------------------------------------------------------------------- */ + if( bReinitializing ) + { + bNeedReinitialize = FALSE; + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Collect GDAL raster configuration information. */ +/* -------------------------------------------------------------------- */ + kdu_channel_mapping oChannels; + oChannels.configure(*poCodestream); + kdu_coords* ref_expansion = new kdu_coords(1, 1); + + // get available resolutions, image width / height etc. + kdu_dims view_dims = poDecompressor-> + get_rendered_image_dims(*poCodestream, &oChannels, -1, 0, + *ref_expansion, *ref_expansion, + KDU_WANT_OUTPUT_COMPONENTS); + + nRasterXSize = view_dims.size.x; + nRasterYSize = view_dims.size.y; + + // Establish the datatype - we will use the same datatype for + // all bands based on the first. This really doesn't do something + // great for >16 bit images. + if( poCodestream->get_bit_depth(0) > 8 + && poCodestream->get_signed(0) ) + { + eDT = GDT_Int16; + } + else if( poCodestream->get_bit_depth(0) > 8 + && !poCodestream->get_signed(0) ) + { + eDT = GDT_UInt16; + } + else + eDT = GDT_Byte; + + if( poCodestream->get_bit_depth(0) % 8 != 8 + && poCodestream->get_bit_depth(0) < 16 ) + SetMetadataItem( + "NBITS", + CPLString().Printf("%d",poCodestream->get_bit_depth(0)), + "IMAGE_STRUCTURE" ); + + // TODO add color interpretation + + // calculate overviews + siz_params* siz_in = poCodestream->access_siz(); + kdu_params* cod_in = siz_in->access_cluster("COD"); + + delete ref_expansion; + + siz_in->get("Scomponents", 0, 0, nComps); + siz_in->get("Sprecision", 0, 0, nBitDepth); + + cod_in->get("Clayers", 0, 0, nQualityLayers); + cod_in->get("Clevels", 0, 0, nResLevels); + + bYCC=TRUE; + cod_in->get("Cycc", 0, 0, bYCC); + + } + catch(...) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Trapped Kakadu exception attempting to initialize JPIP access." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* YCC images are always processed as 3 bands. */ +/* -------------------------------------------------------------------- */ + if( bYCC ) + nBands = 3; + else + nBands = nComps; + +/* -------------------------------------------------------------------- */ +/* Setup band objects. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 1; iBand <= nBands; iBand++ ) + { + JPIPKAKRasterBand *poBand = + new JPIPKAKRasterBand(iBand,0,poCodestream,nResLevels, + this ); + + SetBand( iBand, poBand ); + } + + // set specific metadata items + CPLString osNQualityLayers; + osNQualityLayers.Printf("%i", nQualityLayers); + CPLString osNResolutionLevels; + osNResolutionLevels.Printf("%i", nResLevels); + CPLString osNComps; + osNComps.Printf("%i", nComps); + CPLString osBitDepth; + osBitDepth.Printf("%i", nBitDepth); + + SetMetadataItem("JPIP_NQUALITYLAYERS", osNQualityLayers.c_str(), "JPIP"); + SetMetadataItem("JPIP_NRESOLUTIONLEVELS", osNResolutionLevels.c_str(), "JPIP"); + SetMetadataItem("JPIP_NCOMPS", osNComps.c_str(), "JPIP"); + SetMetadataItem("JPIP_SPRECISION", osBitDepth.c_str(), "JPIP"); + + if( bYCC ) + SetMetadataItem("JPIP_YCC", "YES", "JPIP"); + else + SetMetadataItem("JPIP_YCC", "NO", "JPIP"); + +/* ==================================================================== */ +/* Parse geojp2, or gmljp2, we will assume that the core */ +/* metadata of gml or a geojp2 uuid have been sent in the */ +/* initial metadata response. */ +/* If the server has used placeholder boxes for this */ +/* information then the image will be interpreted as x,y */ +/* ==================================================================== */ + GDALJP2Metadata oJP2Geo; + int nLen = poCache->get_databin_length(KDU_META_DATABIN, nCodestream, 0); + + if( nLen == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unable to open stream to parse metadata boxes" ); + return FALSE; + } + + // create in memory file using vsimem + CPLString osFileBoxName; + osFileBoxName.Printf("/vsimem/jpip/%s.dat", pszCid); + VSILFILE *fpLL = VSIFOpenL(osFileBoxName.c_str(), "w+"); + poCache->set_read_scope(KDU_META_DATABIN, nCodestream, 0); + kdu_byte* pabyBuffer = (kdu_byte *)CPLMalloc(nLen); + poCache->read(pabyBuffer, nLen); + VSIFWriteL(pabyBuffer, nLen, 1, fpLL); + CPLFree( pabyBuffer ); + + VSIFFlushL(fpLL); + VSIFSeekL(fpLL, 0, SEEK_SET); + + nPamFlags |= GPF_NOSAVE; + + try + { + oJP2Geo.ReadBoxes(fpLL); + // parse gml first, followed by geojp2 as a fallback + if (oJP2Geo.ParseGMLCoverageDesc() || oJP2Geo.ParseJP2GeoTIFF()) + { + pszProjection = CPLStrdup(oJP2Geo.pszProjection); + bGeoTransformValid = TRUE; + + memcpy(adfGeoTransform, oJP2Geo.adfGeoTransform, + sizeof(double) * 6 ); + nGCPCount = oJP2Geo.nGCPCount; + pasGCPList = oJP2Geo.pasGCPList; + + oJP2Geo.pasGCPList = NULL; + oJP2Geo.nGCPCount = 0; + + int iBox; + + for( iBox = 0; + oJP2Geo.papszGMLMetadata + && oJP2Geo.papszGMLMetadata[iBox] != NULL; + iBox++ ) + { + char *pszName = NULL; + const char *pszXML = + CPLParseNameValue( oJP2Geo.papszGMLMetadata[iBox], + &pszName ); + CPLString osDomain; + char *apszMDList[2]; + + osDomain.Printf( "xml:%s", pszName ); + apszMDList[0] = (char *) pszXML; + apszMDList[1] = NULL; + + GDALPamDataset::SetMetadata( apszMDList, osDomain ); + CPLFree( pszName ); + } + } + else + { + // treat as cartesian, no geo metadata + CPLError(CE_Warning, CPLE_AppDefined, + "Parsed metadata boxes from jpip stream, geographic metadata not found - is the server using placeholders for this data?" ); + + } + } + catch(...) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unable to parse geographic metadata boxes from jpip stream" ); + } + + VSIFCloseL(fpLL); + VSIUnlink( osFileBoxName.c_str()); + + bNeedReinitialize = FALSE; + + return TRUE; +} + +/******************************************/ +/* ReadVBAS() */ +/******************************************/ +long JPIPKAKDataset::ReadVBAS(GByte* pabyData, int nLen ) +{ + int c = -1; + long val = 0; + nVBASLen = 0; + + while ((c & 0x80) != 0) + { + if (nVBASLen >= 9) + { + CPLError(CE_Failure, CPLE_AppDefined, + "VBAS Length not supported"); + return -1; + } + + if (nPos > nLen) + { + CPLError(CE_Failure, CPLE_AppDefined, + "EOF reached before completing VBAS"); + return -1; + } + +#ifdef PST_DEBUG + if( nPSTThisInstance == nPSTTargetInstance + && nPos >= nPSTTargetOffset ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Artificial PST EOF reached before completing VBAS"); + return -1; + } +#endif + + c = pabyData[nPos]; + nPos++; + + val = (val << 7) | (long)(c & 0x7F); + + if (nVBASLen == 0) + nVBASFirstByte = c; + + nVBASLen++; + } + + return val; +} + +/******************************************/ +/* ReadSegment() */ +/******************************************/ +JPIPDataSegment* JPIPKAKDataset::ReadSegment(GByte* pabyData, int nLen, + int& bError ) +{ + long nId = ReadVBAS(pabyData, nLen); + bError = FALSE; + + if (nId < 0) + { + bError = TRUE; + return NULL; + } + else + { + JPIPDataSegment* segment = new JPIPDataSegment(); + segment->SetId(nId); + + if (nVBASFirstByte == 0) + { + segment->SetEOR(TRUE); + segment->SetId(pabyData[nPos]); + } + else + { + segment->SetEOR(FALSE); + nId &= ~(0x70 << ((nVBASLen -1) * 7)); + segment->SetId(nId); + segment->SetFinal((nVBASFirstByte & 0x10) != 0); + + int m = (nVBASFirstByte & 0x7F) >> 5; + + if (m == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid Bin-ID value format"); + bError = TRUE; + return NULL; + } + else if (m >= 2) { + nClassId = ReadVBAS(pabyData, nLen); + if (m > 2) + { + nCodestream = ReadVBAS(pabyData, nLen); + if( nCodestream < 0 ) + { + bError = TRUE; + return NULL; + } + } + } + + long nNextVal; + + segment->SetClassId(nClassId); + segment->SetCodestreamIdx(nCodestream); + + nNextVal = ReadVBAS(pabyData, nLen); + if( nNextVal == -1 ) + { + bError = TRUE; + return NULL; + } + else + segment->SetOffset( nNextVal ); + + nNextVal = ReadVBAS(pabyData, nLen); + if( nNextVal == -1 ) + { + bError = TRUE; + return NULL; + } + else + segment->SetLen(nNextVal); + } + + if ((segment->GetLen() > 0) && (!segment->IsEOR())) + { + GByte* pabyDataSegment = (GByte *) CPLCalloc(1,segment->GetLen()); + + // copy data from input array pabyData to the data segment + memcpy(pabyDataSegment, + pabyData + nPos, + segment->GetLen()); + + segment->SetData(pabyDataSegment); + } + + nPos += segment->GetLen(); + + if (!segment->IsEOR()) + nDatabins++; + + if ((segment->GetId() == JPIPKAKDataset::JPIP_EOR_WINDOW_DONE) && (segment->IsEOR())) + bWindowDone = TRUE; + + return segment; + } +} + +/******************************************/ +/* KakaduClassId() */ +/******************************************/ +int JPIPKAKDataset::KakaduClassId(int nClassId) +{ + if (nClassId == 0) + return KDU_PRECINCT_DATABIN; + else if (nClassId == 2) + return KDU_TILE_HEADER_DATABIN; + else if (nClassId == 6) + return KDU_MAIN_HEADER_DATABIN; + else if (nClassId == 8) + return KDU_META_DATABIN; + else if (nClassId == 4) + return KDU_TILE_DATABIN; + else + return -1; +} + +/******************************************/ +/* ReadFromInput() */ +/******************************************/ +int JPIPKAKDataset::ReadFromInput(GByte* pabyData, int nLen, int &bError ) +{ + int res = FALSE; + bError = FALSE; + + if (nLen <= 0 ) + return FALSE; + +#ifdef PST_DEBUG + nPSTThisInstance++; + if( CPLGetConfigOption( "PST_OFFSET", NULL ) != NULL ) + { + nPSTTargetOffset = atoi(CPLGetConfigOption("PST_OFFSET","0")); + nPSTTargetInstance = 0; + } + if( CPLGetConfigOption( "PST_INSTANCE", NULL ) != NULL ) + { + nPSTTargetInstance = atoi(CPLGetConfigOption("PST_INSTANCE","0")); + } + + if( nPSTTargetOffset != -1 && nPSTThisInstance == 0 ) + { + CPLDebug( "JPIPKAK", "Premature Stream Termination Activated, PST_OFFSET=%d, PST_INSTANCE=%d", + nPSTTargetOffset, nPSTTargetInstance ); + } + if( nPSTTargetOffset != -1 + && nPSTThisInstance == nPSTTargetInstance ) + { + CPLDebug( "JPIPKAK", "Premature Stream Termination in force for this input instance, PST_OFFSET=%d, data length=%d", + nPSTTargetOffset, nLen ); + } +#endif // def PST_DEBUG + + // parse the data stream, reading the vbas and adding to the kakadu cache + // we could parse all the boxes by hand, and just add data to the kakadu cache + // we will do it the easy way and retrieve the metadata through the kakadu query api + + nPos = 0; + JPIPDataSegment* pSegment = NULL; + + while ((pSegment = ReadSegment(pabyData, nLen, bError)) != NULL) + { + + if (pSegment->IsEOR()) + { + if ((pSegment->GetId() == JPIPKAKDataset::JPIP_EOR_IMAGE_DONE) || + (pSegment->GetId() == JPIPKAKDataset::JPIP_EOR_WINDOW_DONE)) + res = TRUE; + + delete pSegment; + break; + } + else + { + // add data to kakadu + //CPLDebug("JPIPKAK", "Parsed JPIP Segment class=%i stream=%i id=%i offset=%i len=%i isFinal=%i isEOR=%i", pSegment->GetClassId(), pSegment->GetCodestreamIdx(), pSegment->GetId(), pSegment->GetOffset(), pSegment->GetLen(), pSegment->IsFinal(), pSegment->IsEOR()); + poCache->add_to_databin(KakaduClassId(pSegment->GetClassId()), pSegment->GetCodestreamIdx(), + pSegment->GetId(), pSegment->GetData(), pSegment->GetOffset(), pSegment->GetLen(), pSegment->IsFinal()); + + delete pSegment; + } + } + + return res; +} + + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *JPIPKAKDataset::GetProjectionRef() + +{ + if( pszProjection && *pszProjection ) + return( pszProjection ); + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr JPIPKAKDataset::GetGeoTransform( double * padfTransform ) + +{ + if( bGeoTransformValid ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + + return CE_None; + } + else + return GDALPamDataset::GetGeoTransform( padfTransform ); +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int JPIPKAKDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *JPIPKAKDataset::GetGCPProjection() + +{ + if( nGCPCount > 0 ) + return pszProjection; + else + return ""; +} + +/************************************************************************/ +/* GetGCP() */ +/************************************************************************/ + +const GDAL_GCP *JPIPKAKDataset::GetGCPs() + +{ + return pasGCPList; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr JPIPKAKDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ +/* -------------------------------------------------------------------- */ +/* We need various criteria to skip out to block based methods. */ +/* -------------------------------------------------------------------- */ + if( TestUseBlockIO( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap ) ) + return GDALPamDataset::IRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); + +/* -------------------------------------------------------------------- */ +/* Otherwise do this as a single uncached async rasterio. */ +/* -------------------------------------------------------------------- */ + GDALAsyncReader* ario = + BeginAsyncReader(nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, NULL); + + if( ario == NULL ) + return CE_Failure; + + GDALAsyncStatusType status; + + do + { + int nXBufOff,nYBufOff,nXBufSize,nYBufSize; + + status = ario->GetNextUpdatedRegion(-1.0, + &nXBufOff, &nYBufOff, + &nXBufSize, &nYBufSize ); + } while (status != GARIO_ERROR && status != GARIO_COMPLETE ); + + EndAsyncReader(ario); + + if (status == GARIO_ERROR) + return CE_Failure; + else + return CE_None; +} + +/************************************************************************/ +/* TestUseBlockIO() */ +/************************************************************************/ + +int +JPIPKAKDataset::TestUseBlockIO( CPL_UNUSED int nXOff, CPL_UNUSED int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + CPL_UNUSED GDALDataType eDataType, + int nBandCount, int *panBandList ) + +{ +/* -------------------------------------------------------------------- */ +/* Due to limitations in DirectRasterIO() we can only handle */ +/* it no duplicates in the band list. */ +/* -------------------------------------------------------------------- */ + int i, j; + + for( i = 0; i < nBandCount; i++ ) + { + for( j = i+1; j < nBandCount; j++ ) + if( panBandList[j] == panBandList[i] ) + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* The rest of the rules are io strategy stuff, and use */ +/* configuration checks. */ +/* -------------------------------------------------------------------- */ + int bUseBlockedIO = bForceCachedIO; + + if( nYSize == 1 || nXSize * ((double) nYSize) < 100.0 ) + bUseBlockedIO = TRUE; + + if( nBufYSize == 1 || nBufXSize * ((double) nBufYSize) < 100.0 ) + bUseBlockedIO = TRUE; + + if( bUseBlockedIO + && CSLTestBoolean( CPLGetConfigOption( "GDAL_ONE_BIG_READ", "NO") ) ) + bUseBlockedIO = FALSE; + + return bUseBlockedIO; +} + +/*************************************************************************/ +/* BeginAsyncReader() */ +/*************************************************************************/ + +GDALAsyncReader* +JPIPKAKDataset::BeginAsyncReader(int xOff, int yOff, + int xSize, int ySize, + void *pBuf, + int bufXSize, int bufYSize, + GDALDataType bufType, + int nBandCount, int* pBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, + char **papszOptions) +{ + CPLDebug( "JPIP", "BeginAsyncReadeR(%d,%d,%d,%d -> %dx%d)", + xOff, yOff, xSize, ySize, bufXSize, bufYSize ); + +/* -------------------------------------------------------------------- */ +/* Recreate the code stream access if needed. */ +/* -------------------------------------------------------------------- */ + if( bNeedReinitialize ) + { + CPLDebug( "JPIPKAK", "\n\nReinitializing after error! ******\n" ); + + Deinitialize(); + if( !Initialize(GetDescription(),TRUE) ) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Provide default packing if needed. */ +/* -------------------------------------------------------------------- */ + if( nPixelSpace == 0 ) + nPixelSpace = GDALGetDataTypeSize(bufType) / 8; + if( nLineSpace == 0 ) + nLineSpace = nPixelSpace * bufXSize; + if( nBandSpace == 0 ) + nBandSpace = nLineSpace * bufYSize; + +/* -------------------------------------------------------------------- */ +/* check we have sensible values for windowing. */ +/* -------------------------------------------------------------------- */ + if (xOff > GetRasterXSize() + || yOff > GetRasterYSize() + || (xOff + xSize) > GetRasterXSize() + || (yOff + ySize) > GetRasterYSize() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Requested window (%d,%d %dx%d) off dataset.", + xOff, yOff, xSize, ySize ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Record request information. */ +/* -------------------------------------------------------------------- */ + JPIPKAKAsyncReader* ario = new JPIPKAKAsyncReader(); + ario->poDS = this; + ario->nBufXSize = bufXSize; + ario->nBufYSize = bufYSize; + ario->eBufType = bufType; + ario->nBandCount = nBandCount; + ario->nXOff = xOff; + ario->nYOff = yOff; + ario->nXSize = xSize; + ario->nYSize = ySize; + + ario->panBandMap = new int[nBandCount]; + if (pBandMap) + { + for (int i = 0; i < nBandCount; i++) + ario->panBandMap[i] = pBandMap[i]; + } + else + { + for (int i = 0; i < nBandCount; i++) + ario->panBandMap[i] = i+1; + } + +/* -------------------------------------------------------------------- */ +/* If the buffer type is of other than images type, we need to */ +/* allocate a private buffer the same type as the image which */ +/* will be converted later. */ +/* -------------------------------------------------------------------- */ + if( bufType != eDT ) + { + ario->nPixelSpace = GDALGetDataTypeSize(eDT) / 8; + ario->nLineSpace = ario->nPixelSpace * bufXSize; + ario->nBandSpace = ario->nLineSpace * bufYSize; + + ario->nAppPixelSpace = nPixelSpace; + ario->nAppLineSpace = nLineSpace; + ario->nAppBandSpace = nBandSpace; + + ario->pBuf = VSIMalloc3(bufXSize,bufYSize,ario->nPixelSpace*nBandCount); + if( ario->pBuf == NULL ) + { + delete ario; + CPLError( CE_Failure, CPLE_OutOfMemory, + "Failed to allocate %d byte work buffer.", + bufXSize * bufYSize * ario->nPixelSpace ); + return NULL; + } + + ario->pAppBuf = pBuf; + } + else + { + ario->pBuf = pBuf; + ario->pAppBuf = pBuf; + + ario->nAppPixelSpace = ario->nPixelSpace = nPixelSpace; + ario->nAppLineSpace = ario->nLineSpace = nLineSpace; + ario->nAppBandSpace = ario->nBandSpace = nBandSpace; + } + + // parse options + const char* pszLevel = CSLFetchNameValue(papszOptions, "LEVEL"); + const char* pszLayers = CSLFetchNameValue(papszOptions, "LAYERS"); + const char* pszPriority = CSLFetchNameValue(papszOptions, "PRIORITY"); + + if (pszLayers) + ario->nQualityLayers = atoi(pszLayers); + else + ario->nQualityLayers = nQualityLayers; + + if (pszPriority) + { + if (EQUAL(pszPriority, "0")) + ario->bHighPriority = 0; + else + ario->bHighPriority = 1; + } + else + ario->bHighPriority = 1; + +/* -------------------------------------------------------------------- */ +/* Select an appropriate level based on the ratio of buffer */ +/* size to full resolution image. We aim for the next */ +/* resolution *lower* than we might expect for the target */ +/* buffer unless it falls on a power of two. This is because */ +/* the region decompressor only seems to support upsampling */ +/* via the numerator/denominator magic. */ +/* -------------------------------------------------------------------- */ + if (pszLevel) + ario->nLevel = atoi(pszLevel); + else + { + int nRXSize = xSize, nRYSize = ySize; + ario->nLevel = 0; + + while( ario->nLevel < nResLevels + && (nRXSize > bufXSize || nRYSize > bufYSize) ) + { + nRXSize = (nRXSize+1) / 2; + nRYSize = (nRYSize+1) / 2; + ario->nLevel++; + } + } + + ario->Start(); + + return ario; +} + +/************************************************************************/ +/* EndAsyncReader() */ +/************************************************************************/ + +void JPIPKAKDataset::EndAsyncReader(GDALAsyncReader *poARIO) +{ + delete poARIO; +} + + +/*****************************************/ +/* Open() */ +/*****************************************/ +GDALDataset *JPIPKAKDataset::Open(GDALOpenInfo * poOpenInfo) +{ + // test jpip and jpips, assuming jpip is using http as the transport layer + // jpip = http, jpips = https (note SSL is allowed, but jpips is not in the ISO spec) + if (EQUALN(poOpenInfo->pszFilename,"jpip://", 7) + || EQUALN(poOpenInfo->pszFilename,"jpips://",8)) + { + // perform the initial connection + // using cpl_http for the connection + if (CPLHTTPEnabled() == TRUE) + { + JPIPKAKDataset *poDS; + poDS = new JPIPKAKDataset(); + if (poDS->Initialize(poOpenInfo->pszFilename,FALSE)) + { + poDS->SetDescription( poOpenInfo->pszFilename ); + return poDS; + } + else + { + delete poDS; + return NULL; + } + } + else + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s within JPIPKAK driver CPL HTTP not enabled.\n", + poOpenInfo->pszFilename ); + return NULL; + } + } + else + return NULL; +} + +/************************************************************************/ +/* GDALRegister_JPIPKAK() */ +/************************************************************************/ + +void GDALRegister_JPIPKAK() +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("JPIPKAK driver")) + return; + + if( GDALGetDriverByName( "JPIPKAK" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "JPIPKAK" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "JPIP (based on Kakadu)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_jpipkak.html" ); + poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/jpp-stream" ); + + poDriver->pfnOpen = JPIPKAKDataset::Open; + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + +/************************************************************************/ +/* JPIPKAKAsyncReader */ +/************************************************************************/ +JPIPKAKAsyncReader::JPIPKAKAsyncReader() +{ + panBandMap = NULL; + pAppBuf = pBuf = NULL; + nDataRead = 0; +} + +/************************************************************************/ +/* ~JPIPKAKAsyncReader */ +/************************************************************************/ +JPIPKAKAsyncReader::~JPIPKAKAsyncReader() +{ + Stop(); + + // don't own the buffer + delete [] panBandMap; + + if( pAppBuf != pBuf ) + CPLFree( pBuf ); +} + +/************************************************************************/ +/* Start() */ +/************************************************************************/ + +void JPIPKAKAsyncReader::Start() +{ + JPIPKAKDataset *poJDS = (JPIPKAKDataset*)poDS; + + // stop the currently running thread + // start making requests to the server to the server + nDataRead = 0; + bComplete = 0; + + // check a thread is not already running + if (((bHighPriority) && (poJDS->bHighThreadRunning)) || + ((!bHighPriority) && (poJDS->bLowThreadRunning))) + CPLError(CE_Failure, CPLE_AppDefined, "JPIPKAKAsyncReader supports at most two concurrent server communication threads"); + else + { + // Ensure we are working against full res + ((JPIPKAKDataset*)poDS)->poCodestream-> + apply_input_restrictions( 0, 0, 0, 0, NULL ); + + // calculate the url the worker function is going to retrieve + // calculate the kakadu adjust image size + channels.configure(*(((JPIPKAKDataset*)poDS)->poCodestream)); + + // find current canvas width and height in the cache and check we don't + // exceed this in our process request + kdu_dims view_dims; + kdu_coords ref_expansion; + ref_expansion.x = 1; + ref_expansion.y = 1; + + view_dims = ((JPIPKAKDataset*)poDS)->poDecompressor-> + get_rendered_image_dims(*((JPIPKAKDataset*)poDS)->poCodestream, &channels, + -1, nLevel, + ref_expansion ); + + kdu_coords* view_siz = view_dims.access_size(); + + // Establish the decimation implied by our resolution level. + int nRes = 1; + if (nLevel > 0) + nRes = 2 << (nLevel - 1); + + // setup expansion to account for the difference between + // the selected level and the buffer resolution. + exp_numerator.x = nBufXSize; + exp_numerator.y = nBufYSize; + + exp_denominator.x = (int) ceil(nXSize / (double) nRes); + exp_denominator.y = (int) ceil(nYSize / (double) nRes); + + // formulate jpip parameters and adjust offsets for current level + int fx = view_siz->x / nRes; + int fy = view_siz->y / nRes; + + rr_win.pos.x = (int) ceil(nXOff / (1.0 * nRes)); // roffx + rr_win.pos.y = (int) ceil(nYOff / (1.0 * nRes)); // roffy + rr_win.size.x = (int) ceil(nXSize / (1.0 * nRes)); // rsizx + rr_win.size.y = (int) ceil(nYSize / (1.0 * nRes)); // rsizy + + if ( rr_win.pos.x + rr_win.size.x > fx) + rr_win.size.x = fx - rr_win.pos.x; + if ( rr_win.pos.y + rr_win.size.y > fy) + rr_win.size.y = fy - rr_win.pos.y; + + CPLString jpipUrl; + CPLString comps; + + if( poJDS->bYCC ) + { + comps = "0,1,2"; + } + else + { + for (int i = 0; i < nBandCount; i++) + comps.Printf("%s%i,", comps.c_str(), panBandMap[i]-1); + + comps.erase(comps.length() -1); + } + + jpipUrl.Printf("%s&type=jpp-stream&roff=%i,%i&rsiz=%i,%i&fsiz=%i,%i,closest&quality=%i&comps=%s", + ((JPIPKAKDataset*)poDS)->osRequestUrl.c_str(), + rr_win.pos.x, rr_win.pos.y, + rr_win.size.x, rr_win.size.y, + fx, fy, + nQualityLayers, comps.c_str()); + + JPIPRequest* pRequest = new JPIPRequest(); + pRequest->bPriority = bHighPriority; + pRequest->osRequest = jpipUrl; + pRequest->poARIO = this; + + if( bHighPriority ) + poJDS->bHighThreadFinished = 0; + else + poJDS->bLowThreadFinished = 0; + + //CPLDebug("JPIPKAKAsyncReader", "THREADING TURNED OFF"); + if (CPLCreateThread(JPIPWorkerFunc, pRequest) == -1) + CPLError(CE_Failure, CPLE_AppDefined, + "Unable to create worker jpip thread" ); + // run in main thread as a test + //JPIPWorkerFunc(pRequest); + } +} + +/************************************************************************/ +/* Stop() */ +/************************************************************************/ +void JPIPKAKAsyncReader::Stop() +{ + JPIPKAKDataset *poJDS = (JPIPKAKDataset*)poDS; + + bComplete = 1; + if (poJDS->pGlobalMutex) + { + if (((bHighPriority) && (!poJDS->bHighThreadFinished)) || + ((!bHighPriority) && (!poJDS->bLowThreadFinished))) + { + CPLDebug( "JPIPKAK", "JPIPKAKAsyncReader::Stop() requested." ); + + // stop the thread + if (bHighPriority) + { + CPLAcquireMutex(poJDS->pGlobalMutex, 100.0); + poJDS->bHighThreadRunning = 0; + CPLReleaseMutex(poJDS->pGlobalMutex); + + while (!poJDS->bHighThreadFinished) + CPLSleep(0.1); + } + else + { + CPLAcquireMutex(poJDS->pGlobalMutex, 100.0); + poJDS->bLowThreadRunning = 0; + CPLReleaseMutex(poJDS->pGlobalMutex); + + while (!poJDS->bLowThreadFinished) + { + CPLSleep(0.1); + } + } + CPLDebug( "JPIPKAK", "JPIPKAKAsyncReader::Stop() confirmed." ); + } + } +} + +/************************************************************************/ +/* GetNextUpdatedRegion() */ +/************************************************************************/ + +GDALAsyncStatusType +JPIPKAKAsyncReader::GetNextUpdatedRegion(double dfTimeout, + int* pnxbufoff, + int* pnybufoff, + int* pnxbufsize, + int* pnybufsize) +{ + GDALAsyncStatusType result = GARIO_ERROR; + JPIPKAKDataset *poJDS = (JPIPKAKDataset*)poDS; + + long nSize = 0; + // take a snapshot of the volatile variables + if (bHighPriority) + { + const long s = poJDS->nHighThreadByteCount - nDataRead; + nSize = s; + } + else + { + const long s = poJDS->nLowThreadByteCount - nDataRead; + nSize = s; + } + +/* -------------------------------------------------------------------- */ +/* Wait for new data to return if required. */ +/* -------------------------------------------------------------------- */ + if ((nSize == 0) && dfTimeout != 0 ) + { + // poll for change in cache size + clock_t end_wait = 0; + + end_wait = clock() + (int) (dfTimeout * CLOCKS_PER_SEC); + while ((nSize == 0) && ((bHighPriority && poJDS->bHighThreadRunning) || + (!bHighPriority && poJDS->bLowThreadRunning))) + { + if (end_wait) + if (clock() > end_wait && dfTimeout >= 0 ) + break; + + CPLSleep(0.1); + + if (bHighPriority) + { + const long s = poJDS->nHighThreadByteCount - nDataRead; + nSize = s; + } + else + { + const long s = poJDS->nLowThreadByteCount - nDataRead; + nSize = s; + } + } + } + + // if there is no pending data and we don't want to wait. + if( nSize == 0 ) + { + *pnxbufoff = 0; + *pnybufoff = 0; + *pnxbufsize = 0; + *pnybufsize = 0; + + // Indicate an error if the thread finished prematurely + if( (bHighPriority + && !poJDS->bHighThreadRunning + && poJDS->bHighThreadFinished) + || (!bHighPriority + && !poJDS->bLowThreadRunning + && poJDS->bLowThreadFinished) ) + { + if( osErrorMsg != "" ) + CPLError( CE_Failure, CPLE_AppDefined, + "%s", osErrorMsg.c_str() ); + else + CPLError( CE_Failure, CPLE_AppDefined, + "Working thread failed without complete data. (%d,%d,%d)", + bHighPriority, + poJDS->bHighThreadRunning, + poJDS->bHighThreadFinished ); + poJDS->bNeedReinitialize = TRUE; + return GARIO_ERROR; + } + + // Otherwise there is still pending data to wait for. + return GARIO_PENDING; + } + +/* -------------------------------------------------------------------- */ +/* Establish the canvas region with the expansion factor */ +/* applied, and compute region from the original window cut */ +/* down to the target canvas. */ +/* -------------------------------------------------------------------- */ + kdu_dims view_dims, region; + int nBytesPerPixel = GDALGetDataTypeSize(poJDS->eDT) / 8; + int nPrecision = 0; + + try + { + // Ensure we are working against full res + poJDS->poCodestream->apply_input_restrictions( 0, 0, 0, 0, NULL ); + + view_dims = poJDS->poDecompressor->get_rendered_image_dims( + *poJDS->poCodestream, &channels, + -1, nLevel, exp_numerator, exp_denominator ); + + double x_ratio, y_ratio; + x_ratio = view_dims.size.x / (double) poDS->GetRasterXSize(); + y_ratio = view_dims.size.y / (double) poDS->GetRasterYSize(); + + region = rr_win; + + region.pos.x = (int) ceil(region.pos.x * x_ratio); + region.pos.y = (int) ceil(region.pos.y * y_ratio); + region.size.x = (int) ceil(region.size.x * x_ratio); + region.size.y = (int) ceil(region.size.y * y_ratio); + + region.size.x = MIN(region.size.x,nBufXSize); + region.size.y = MIN(region.size.y,nBufYSize); + + if( region.pos.x + region.size.x > view_dims.size.x ) + region.size.x = view_dims.size.x - region.pos.x; + if( region.pos.y + region.size.y > view_dims.size.y ) + region.size.y = view_dims.size.y - region.pos.y; + + region.pos += view_dims.pos; + + CPLAssert( nBytesPerPixel == 1 || nBytesPerPixel == 2 ); + + if( poJDS->poCodestream->get_bit_depth(0) > 16 ) + nPrecision = 16; + } + catch(...) + { + // The error handler should already have posted an error message. + return GARIO_ERROR; + } + +/* ==================================================================== */ +/* Now we process the available cached jpeg2000 data into */ +/* imagery. The kdu_region_decompressor only seems to support */ +/* reading back one or three components at a time, we may need */ +/* to do severalp processing passes to get the bands we */ +/* want. We try to do groups of three were possible, and handle */ +/* the rest one band at a time. */ +/* ==================================================================== */ + int nBandsCompleted = 0; + + while( nBandsCompleted < nBandCount ) + { +/* -------------------------------------------------------------------- */ +/* Set up channel list requested. */ +/* -------------------------------------------------------------------- */ + std::vector component_indices; + unsigned int i; + + if( nBandCount - nBandsCompleted >= 3 ) + { + CPLDebug( "JPIPKAK", "process bands %d,%d,%d", + panBandMap[nBandsCompleted], + panBandMap[nBandsCompleted+1], + panBandMap[nBandsCompleted+2] ); + + component_indices.push_back( panBandMap[nBandsCompleted++]-1 ); + component_indices.push_back( panBandMap[nBandsCompleted++]-1 ); + component_indices.push_back( panBandMap[nBandsCompleted++]-1 ); + } + else + { + CPLDebug( "JPIPKAK", "process band %d", + panBandMap[nBandsCompleted] ); + component_indices.push_back( panBandMap[nBandsCompleted++]-1 ); + } + +/* -------------------------------------------------------------------- */ +/* Apply region, channel and overview level restrictions. */ +/* -------------------------------------------------------------------- */ + kdu_dims region_pass = region; + + poJDS->poCodestream->apply_input_restrictions( + component_indices.size(), &(component_indices[0]), + nLevel, nQualityLayers, ®ion_pass, + KDU_WANT_CODESTREAM_COMPONENTS); + + channels.configure(*(poJDS->poCodestream)); + + for( i=0; i < component_indices.size(); i++ ) + channels.source_components[i] = component_indices[i]; + + kdu_dims incomplete_region = region_pass; + kdu_coords origin = region_pass.pos; + + int bIsDecompressing = FALSE; + + CPLAcquireMutex(poJDS->pGlobalMutex, 100.0); + + try + { + + bIsDecompressing = poJDS->poDecompressor->start( + *(poJDS->poCodestream), + &channels, -1, nLevel, nQualityLayers, + region_pass, exp_numerator, exp_denominator, TRUE); + + *pnxbufoff = 0; + *pnybufoff = 0; + *pnxbufsize = region_pass.access_size()->x; + *pnybufsize = region_pass.access_size()->y; + + // Setup channel buffers + std::vector channel_bufs; + + for( i=0; i < component_indices.size(); i++ ) + channel_bufs.push_back( + ((kdu_byte *) pBuf) + + (i+nBandsCompleted-component_indices.size()) * nBandSpace ); + + int pixel_gap = nPixelSpace / nBytesPerPixel; + int row_gap = nLineSpace / nBytesPerPixel; + + while ((bIsDecompressing == 1) || (incomplete_region.area() != 0)) + { + if( nBytesPerPixel == 1 ) + { + bIsDecompressing = poJDS->poDecompressor-> + process(&(channel_bufs[0]), false, + pixel_gap, origin, row_gap, 1000000, 0, + incomplete_region, region_pass, + 0, false ); + } + else if( nBytesPerPixel == 2 ) + { + bIsDecompressing = poJDS->poDecompressor-> + process((kdu_uint16**) &(channel_bufs[0]), false, + pixel_gap, origin, row_gap, 1000000, 0, + incomplete_region, region_pass, + nPrecision, false ); + } + + CPLDebug( "JPIPKAK", + "processed=%d,%d %dx%d - incomplete=%d,%d %dx%d", + region_pass.pos.x, region_pass.pos.y, + region_pass.size.x, region_pass.size.y, + incomplete_region.pos.x, incomplete_region.pos.y, + incomplete_region.size.x, incomplete_region.size.y ); + } + + poJDS->poDecompressor->finish(); + CPLReleaseMutex(poJDS->pGlobalMutex); + + } + catch(...) + { + poJDS->poDecompressor->finish(); + CPLReleaseMutex(poJDS->pGlobalMutex); + // The error handler should already have posted an error message. + return GARIO_ERROR; + } + } // nBandsCompleted < nBandCount + + +/* -------------------------------------------------------------------- */ +/* If the application buffer is of a different type than our */ +/* band we need to copy into the application buffer at this */ +/* point. */ +/* */ +/* We could optimize to only update affected area, but that is */ +/* always the whole area for the JPIP driver it seems. */ +/* -------------------------------------------------------------------- */ + if( pAppBuf != pBuf ) + { + int iY, iBand; + GByte *pabySrc = (GByte *) pBuf; + GByte *pabyDst = (GByte *) pAppBuf; + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + for( iY = 0; iY < nBufYSize; iY++ ) + { + GDALCopyWords( pabySrc + nLineSpace * iY + nBandSpace * iBand, + poJDS->eDT, nPixelSpace, + pabyDst + nAppLineSpace*iY + nAppBandSpace*iBand, + eBufType, nAppPixelSpace, + nBufXSize ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* has there been any more data read while were have been processing?*/ +/* -------------------------------------------------------------------- */ + long size = 0; + if (bHighPriority) + { + const long x = poJDS->nHighThreadByteCount - nDataRead; + size = x; + } + else + { + const long x = poJDS->nLowThreadByteCount - nDataRead; + size = x; + } + + if ((bComplete) && (nSize == size)) + result = GARIO_COMPLETE; + else + result = GARIO_UPDATE; + + nDataRead += nSize; + + if( result == GARIO_ERROR ) + poJDS->bNeedReinitialize = TRUE; + + return result; +} + +/************************************************************************/ +/* JPIPDataSegment() */ +/************************************************************************/ +JPIPDataSegment::JPIPDataSegment() +{ + nId = 0; + nAux = 0; + nClassId = 0; + nCodestream = 0; + nOffset = 0; + nLen = 0; + pabyData = NULL; + bIsFinal = FALSE; + bIsEOR = FALSE; +} + +/************************************************************************/ +/* ~JPIPDataSegment() */ +/************************************************************************/ +JPIPDataSegment::~JPIPDataSegment() +{ + CPLFree(pabyData); +} + +/************************************************************************/ +/* JPIPWorkerFunc() */ +/************************************************************************/ +static void JPIPWorkerFunc(void *req) +{ + int nCurrentTransmissionLength = 2000; + int nMinimumTransmissionLength = 2000; + + JPIPRequest *pRequest = (JPIPRequest *)req; + JPIPKAKDataset *poJDS = + (JPIPKAKDataset*)(pRequest->poARIO->GetGDALDataset()); + + int bPriority = pRequest->bPriority; + + CPLAcquireMutex(poJDS->pGlobalMutex, 100.0); + + CPLDebug( "JPIPKAK", "working thread starting." ); + // set the running status + if (bPriority) + { + poJDS->bHighThreadRunning = 1; + poJDS->bHighThreadFinished = 0; + } + else + { + poJDS->bLowThreadRunning = 1; + poJDS->bLowThreadFinished = 0; + } + + CPLReleaseMutex(poJDS->pGlobalMutex); + + CPLString osHeaders = "HEADERS="; + osHeaders += "Accept: jpp-stream"; + CPLString osPersistent; + + osPersistent.Printf( "PERSISTENT=JPIPKAK:%p", poJDS ); + + char *apszOptions[] = { + (char *) osHeaders.c_str(), + (char *) osPersistent.c_str(), + NULL + }; + + while (TRUE) + { + // modulate the len= parameter to use the currently available bandwidth + long nStart = clock(); + + if (((bPriority) && (!poJDS->bHighThreadRunning)) || ((!bPriority) && (!poJDS->bLowThreadRunning))) + break; + + // make jpip requests + // adjust transmission length + CPLString osCurrentRequest; + osCurrentRequest.Printf("%s&len=%i", pRequest->osRequest.c_str(), nCurrentTransmissionLength); + CPLHTTPResult *psResult = CPLHTTPFetch(osCurrentRequest, apszOptions); + if (psResult->nDataLen == 0) + { + CPLAcquireMutex(poJDS->pGlobalMutex, 100.0); + if( psResult->pszErrBuf ) + pRequest->poARIO-> + osErrorMsg.Printf( "zero data returned from server, timeout?\n%s", psResult->pszErrBuf ); + else + pRequest->poARIO-> + osErrorMsg = "zero data returned from server, timeout?"; + + // status is not being set, always zero in cpl_http + CPLDebug("JPIPWorkerFunc", "zero data returned from server"); + CPLReleaseMutex(poJDS->pGlobalMutex); + break; + } + + if( psResult->pszContentType != NULL ) + CPLDebug( "JPIPKAK", "Content-type: %s", psResult->pszContentType ); + + if( psResult->pszContentType != NULL + && strstr(psResult->pszContentType,"html") != NULL ) + { + CPLDebug( "JPIPKAK", "%s", psResult->pabyData ); + } + + int bytes = psResult->nDataLen; + long nEnd = clock(); + + if ((nEnd - nStart) > 0) + nCurrentTransmissionLength = (int) MAX(bytes / ((1.0 * (nEnd - nStart)) / CLOCKS_PER_SEC), nMinimumTransmissionLength); + + + CPLAcquireMutex(poJDS->pGlobalMutex, 100.0); + + int bError; + int bComplete = ((JPIPKAKDataset*)pRequest->poARIO->GetGDALDataset())->ReadFromInput(psResult->pabyData, psResult->nDataLen, bError); + if (bPriority) + poJDS->nHighThreadByteCount += psResult->nDataLen; + else + poJDS->nLowThreadByteCount += psResult->nDataLen; + + pRequest->poARIO->SetComplete(bComplete); + + CPLReleaseMutex(poJDS->pGlobalMutex); + CPLHTTPDestroyResult(psResult); + + if (bComplete || bError ) + break; + } + + CPLAcquireMutex(poJDS->pGlobalMutex, 100.0); + + CPLDebug( "JPIPKAK", "Worker shutting down." ); + + if (bPriority) + { + poJDS->bHighThreadRunning = 0; + poJDS->bHighThreadFinished = 1; + } + else + { + poJDS->bLowThreadRunning = 0; + poJDS->bLowThreadFinished = 1; + } + + CPLReleaseMutex(poJDS->pGlobalMutex); + + // end of thread + delete pRequest; +} + diff --git a/bazaar/plugin/gdal/frmts/jpipkak/jpipkakdataset.h b/bazaar/plugin/gdal/frmts/jpipkak/jpipkakdataset.h new file mode 100644 index 000000000..45117a677 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpipkak/jpipkakdataset.h @@ -0,0 +1,301 @@ +/****************************************************************************** + * $Id: jpipkakdataset.cpp 2008-10-01 nbarker $ + * + * Project: jpip read driver + * Purpose: GDAL bindings for JPIP. + * Author: Norman Barker, ITT VIS, norman.barker@gmail.com + * + ****************************************************************************** + * ITT Visual Information Systems grants you use of this code, under the following license: + * + * Copyright (c) 2000-2007, ITT Visual Information Solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +**/ + +#include "gdal_pam.h" +#include "gdaljp2metadata.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_http.h" +#include "cpl_vsi.h" +#include "cpl_multiproc.h" + +#include "kdu_cache.h" +#include "kdu_region_decompressor.h" +#include "kdu_file_io.h" + +#include + + +#if KDU_MAJOR_VERSION > 7 || (KDU_MAJOR_VERSION == 7 && KDU_MINOR_VERSION >= 5) + using namespace kdu_core; + using namespace kdu_supp; +#endif + + +static void JPIPWorkerFunc(void *); + +/************************************************************************/ +/* ==================================================================== */ +/* JPIPDataSegment */ +/* ==================================================================== */ +/************************************************************************/ +class JPIPDataSegment +{ +private: + long nId; + long nAux; + long nClassId; + long nCodestream; + long nOffset; + long nLen; + GByte* pabyData; + int bIsFinal; + int bIsEOR; +public: + int GetId(){return nId;} + int GetAux(){return nAux;} + int GetClassId(){return nClassId;} + int GetCodestreamIdx(){return nCodestream;} + int GetOffset(){return nOffset;} + int GetLen(){return nLen;} + GByte* GetData(){return pabyData;} + int IsFinal(){return bIsFinal;} + int IsEOR(){return bIsEOR;} + + void SetId(long nId){this->nId = nId;} + void SetAux(long nAux){this->nAux = nAux;} + void SetClassId(long nClassId){this->nClassId = nClassId;} + void SetCodestreamIdx(long nCodestream){this->nCodestream = nCodestream;} + void SetOffset(long nOffset){this->nOffset = nOffset;} + void SetLen(long nLen){this->nLen = nLen;} + void SetData(GByte* pabyData){this->pabyData = pabyData;} + void SetFinal(int bIsFinal){this->bIsFinal = bIsFinal;} + void SetEOR(int bIsEOR){this->bIsEOR = bIsEOR;} + JPIPDataSegment(); + ~JPIPDataSegment(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JPIPKAKDataset */ +/* ==================================================================== */ +/************************************************************************/ +class JPIPKAKDataset: public GDALPamDataset +{ +private: + int bNeedReinitialize; + CPLString osRequestUrl; + char* pszTid; + char* pszPath; + char* pszCid; + char* pszProjection; + + int nPos; + int nVBASLen; + int nVBASFirstByte; + int nClassId; + int nQualityLayers; + int nResLevels; + int nComps; + int nBitDepth; + int bYCC; + GDALDataType eDT; + + int nCodestream; + long nDatabins; + + double adfGeoTransform[6]; + + int bWindowDone; + int bGeoTransformValid; + + int nGCPCount; + GDAL_GCP *pasGCPList; + + // kakadu + kdu_codestream *poCodestream; + kdu_region_decompressor *poDecompressor; + kdu_cache *poCache; + + long ReadVBAS(GByte* pabyData, int nLen); + JPIPDataSegment* ReadSegment(GByte* pabyData, int nLen, int& bError); + int Initialize(const char* url, int bReinitializing ); + void Deinitialize(); + int KakaduClassId(int nClassId); + + CPLMutex *pGlobalMutex; + + // support two communication threads to the server, a main and an overview thread + volatile int bHighThreadRunning; + volatile int bLowThreadRunning; + volatile int bHighThreadFinished; + volatile int bLowThreadFinished; + + // transmission counts + volatile long nHighThreadByteCount; + volatile long nLowThreadByteCount; + +public: + JPIPKAKDataset(); + virtual ~JPIPKAKDataset(); + + // progressive methods + virtual GDALAsyncReader* BeginAsyncReader(int xOff, int yOff, + int xSize, int ySize, + void *pBuf, + int bufXSize, int bufYSize, + GDALDataType bufType, + int nBandCount, int* bandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, + char **papszOptions); + + virtual void EndAsyncReader(GDALAsyncReader *); + int GetNQualityLayers(){return nQualityLayers;} + int GetNResolutionLevels(){return nResLevels;} + int GetNComponents(){return nComps;} + + int ReadFromInput(GByte* pabyData, int nLen, int& bError ); + + int TestUseBlockIO( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, GDALDataType eDataType, + int nBandCount, int *panBandList ); + + //gdaldataset methods + virtual CPLErr GetGeoTransform( double * ); + virtual const char *GetProjectionRef(void); + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + static GDALDataset *Open(GDALOpenInfo *); + static const GByte JPIP_EOR_IMAGE_DONE = 1; + static const GByte JPIP_EOR_WINDOW_DONE = 2; + static const GByte MAIN_HEADER_DATA_BIN_CLASS = 6; + static const GByte META_DATA_BIN_CLASS = 8; + static const GByte PRECINCT_DATA_BIN_CLASS = 0; + static const GByte TILE_HEADER_DATA_BIN_CLASS = 2; + static const GByte TILE_DATA_BIN_CLASS = 4; + + friend class JPIPKAKAsyncReader; + friend class JPIPKAKRasterBand; + friend void JPIPWorkerFunc(void*); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JPIPKAKRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class JPIPKAKRasterBand : public GDALPamRasterBand +{ + friend class JPIPKAKDataset; + + JPIPKAKDataset *poBaseDS; + + int nDiscardLevels; + + kdu_dims band_dims; + + int nOverviewCount; + JPIPKAKRasterBand **papoOverviewBand; + + kdu_codestream *oCodeStream; + + GDALColorTable oCT; + GDALColorInterp eInterp; + +public: + + JPIPKAKRasterBand( int, int, kdu_codestream *, int, + JPIPKAKDataset * ); + ~JPIPKAKRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview( int ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JPIPKAKAsyncReader */ +/* ==================================================================== */ +/************************************************************************/ + +class JPIPKAKAsyncReader : public GDALAsyncReader +{ +private: + void *pAppBuf; + int nAppPixelSpace, nAppLineSpace, nAppBandSpace; + + int nDataRead; + int nLevel; + int nQualityLayers; + int bHighPriority; + int bComplete; + kdu_channel_mapping channels; + kdu_coords exp_numerator, exp_denominator; + + kdu_dims rr_win; // user requested window expressed on reduced res level + + void Start(); + void Stop(); + +public: + JPIPKAKAsyncReader(); + virtual ~JPIPKAKAsyncReader(); + + virtual GDALAsyncStatusType GetNextUpdatedRegion(double timeout, + int* pnxbufoff, + int* pnybufoff, + int* pnxbufsize, + int* pnybufsize); + void SetComplete(int bFinished){this->bComplete = bFinished;}; + + friend class JPIPKAKDataset; + + CPLString osErrorMsg; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JPIPRequest */ +/* ==================================================================== */ +/************************************************************************/ +struct JPIPRequest +{ + int bPriority; + CPLString osRequest; + JPIPKAKAsyncReader* poARIO; +}; diff --git a/bazaar/plugin/gdal/frmts/jpipkak/jpipsequence.PNG b/bazaar/plugin/gdal/frmts/jpipkak/jpipsequence.PNG new file mode 100644 index 000000000..96bac5745 Binary files /dev/null and b/bazaar/plugin/gdal/frmts/jpipkak/jpipsequence.PNG differ diff --git a/bazaar/plugin/gdal/frmts/jpipkak/makefile.vc b/bazaar/plugin/gdal/frmts/jpipkak/makefile.vc new file mode 100644 index 000000000..24abd9eee --- /dev/null +++ b/bazaar/plugin/gdal/frmts/jpipkak/makefile.vc @@ -0,0 +1,21 @@ + +GDAL_ROOT = ..\.. + +KAKINC = -I$(KAKSRC)/managed/all_includes \ + -I$(KAKSRC)/apps/jp2 -I$(KAKSRC)/apps/caching_sources + +OBJ = jpipkakdataset.obj kdu_cache.obj +EXTRAFLAGS = $(KAKINC) -DKDU_PENTIUM_MSVC /EHsc + +!INCLUDE $(GDAL_ROOT)\nmake.opt + + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +kdu_cache.cpp: $(KAKSRC)\apps\caching_sources\kdu_cache.cpp + copy $(KAKSRC)\apps\caching_sources\kdu_cache.cpp + +clean: + -del *.obj + -del *.dll diff --git a/bazaar/plugin/gdal/frmts/kea/GNUmakefile b/bazaar/plugin/gdal/frmts/kea/GNUmakefile new file mode 100644 index 000000000..e63bc9878 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/GNUmakefile @@ -0,0 +1,15 @@ + +include ../../GDALmake.opt + +OBJ = keaband.o keacopy.o keadataset.o keadriver.o keamaskband.o keaoverview.o kearat.o + +CPPFLAGS := $(KEA_INC) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(OBJ) $(O_OBJ): keaband.h keacopy.h keadataset.h keamaskband.h keaoverview.h kearat.h + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/kea/frmt_kea.html b/bazaar/plugin/gdal/frmts/kea/frmt_kea.html new file mode 100644 index 000000000..287a86c8a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/frmt_kea.html @@ -0,0 +1,63 @@ + + + + +KEA + + + + +

    KEA

    + +

    Starting with GDAL 2.0, GDAL can read, create and update files in the KEA format, through the libkea library.

    + +

    KEA is an image file format, named after the New Zealand bird, that provides a full implementation +of the GDAL data model and is implemented within a HDF5 file. A software library, likea, is used +to access the file format. The format has comparable performance with existing formats while +producing smaller file sizes and is already within active use for a number of projects within +Landcare Research, New Zealand, and the wider community.

    + +

    The KEA format supports the following features of the GDAL data model: +

      +
    • Multiple-band support, with possible different datatypes. Bands can be added to an existing dataset with AddBand() API
    • +
    • Image blocking support
    • +
    • Reading, creation and update of data of image blocks
    • +
    • Affine geotransform, WKT projection, GCP
    • +
    • Metadata at dataset and band level
    • +
    • Per-band description
    • +
    • Per-band nodata and color interpration
    • +
    • Per-band color table
    • +
    • Per-band RAT (Raster Attribute Table) of arbitrary size
    • +
    • Internal overviews and mask bands
    • +
    +
  • + +

    Creation options

    + +

    The following creation options are available. Some are rather esoteric and +should rarely be specified, unless the user has good knowledge of the working +of the underlying HDF5 format.

    + +
      +
    • IMAGEBLOCKSIZE=integer_value: The size of each block for image data. Defaults to 256

    • +
    • ATTBLOCKSIZE=integer_value: The size of each block for attribute data. Defaults to 1000

    • +
    • MDC_NELMTS=integer_value: Number of elements in the meta data cache. Defaults to 0. See the Data caching page of HDF5 documentation.

    • +
    • RDCC_NELMTS=integer_value: Number of elements in the raw data chunk cache. Defaults to 512. See the Data caching page of HDF5 documentation.

    • +
    • RDCC_NBYTES=integer_value: Total size of the raw data chunk cache, in bytes. Defaults to 1048576. See the Data caching page of HDF5 documentation.

    • +
    • RDCC_W0=floating_point_value between 0 and 1: Preemption policy. Defaults to 0.75. See the Data caching page of HDF5 documentation.

    • +
    • SIEVE_BUF=integer_value: Sets the maximum size of the data sieve buffer. Defaults to 65536. See H5Pset_sieve_buf_size() documentation

    • +
    • META_BLOCKSIZE=integer_value: Sets the minimum size of metadata block allocations. Defaults to 2048. See H5Pset_meta_block_size() documentation

    • +
    • DEFLATE=integer_value: Compression level between 0 (no compression) to 9 (max compression). Defaults to 1

    • +
    • THEMATIC=YES/NO: If YES then all bands are set to thematic. Defaults to NO

    • +
    + +

    See Also:

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/kea/keaband.cpp b/bazaar/plugin/gdal/frmts/kea/keaband.cpp new file mode 100644 index 000000000..287b2caa0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/keaband.cpp @@ -0,0 +1,971 @@ +/* + * $Id: keaband.cpp 28011 2014-11-26 13:47:09Z rouault $ + * keaband.cpp + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "keaband.h" +#include "keaoverview.h" +#include "keamaskband.h" +#include "kearat.h" + +#include "gdal_rat.h" +#include "libkea/KEAAttributeTable.h" + +#include +#include + +#include + +// constructor +KEARasterBand::KEARasterBand( KEADataset *pDataset, int nSrcBand, GDALAccess eAccess, kealib::KEAImageIO *pImageIO, int *pRefCount ) +{ + this->poDS = pDataset; // our pointer onto the dataset + this->nBand = nSrcBand; // this is the band we are + this->m_eKEADataType = pImageIO->getImageBandDataType(nSrcBand); // get the data type as KEA enum + this->eDataType = KEA_to_GDAL_Type( m_eKEADataType ); // convert to GDAL enum + this->nBlockXSize = pImageIO->getImageBlockSize(nSrcBand); // get the native blocksize + this->nBlockYSize = pImageIO->getImageBlockSize(nSrcBand); + this->nRasterXSize = this->poDS->GetRasterXSize(); // ask the dataset for the total image size + this->nRasterYSize = this->poDS->GetRasterYSize(); + this->eAccess = eAccess; + + if( pImageIO->attributeTablePresent(nSrcBand) ) + { + this->m_nAttributeChunkSize = pImageIO->getAttributeTableChunkSize(nSrcBand); + } + else + { + this->m_nAttributeChunkSize = -1; // don't report + } + + // grab the imageio class and its refcount + this->m_pImageIO = pImageIO; + this->m_pnRefCount = pRefCount; + // increment the refcount as we now have a reference to imageio + (*this->m_pnRefCount)++; + + // initialise overview variables + m_nOverviews = 0; + m_panOverviewBands = NULL; + + // mask band + m_pMaskBand = NULL; + m_bMaskBandOwned = false; + + // grab the description here + this->sDescription = pImageIO->getImageBandDescription(nSrcBand); + + this->m_pAttributeTable = NULL; // no RAT yet + this->m_pColorTable = NULL; // no color table yet + + // initialise the metadata as a CPLStringList + m_papszMetadataList = NULL; + this->UpdateMetadataList(); +} + +// destructor +KEARasterBand::~KEARasterBand() +{ + // destroy RAT if any + delete this->m_pAttributeTable; + // destroy color table if any + delete this->m_pColorTable; + // destroy the metadata + CSLDestroy(this->m_papszMetadataList); + // delete any overview bands + this->deleteOverviewObjects(); + + // if GDAL created the mask it will delete it + if( m_bMaskBandOwned ) + { + delete m_pMaskBand; + } + + // according to the docs, this is required + this->FlushCache(); + + // decrement the recount and delete if needed + (*m_pnRefCount)--; + if( *m_pnRefCount == 0 ) + { + try + { + m_pImageIO->close(); + } + catch (kealib::KEAIOException &e) + { + } + delete m_pImageIO; + delete m_pnRefCount; + } +} + +// internal method that updates the metadata into m_papszMetadataList +void KEARasterBand::UpdateMetadataList() +{ + std::vector< std::pair > data; + + // get all the metadata and iterate through + data = this->m_pImageIO->getImageBandMetaData(this->nBand); + for(std::vector< std::pair >::iterator iterMetaData = data.begin(); iterMetaData != data.end(); ++iterMetaData) + { + // add to our list + m_papszMetadataList = CSLSetNameValue(m_papszMetadataList, iterMetaData->first.c_str(), iterMetaData->second.c_str()); + } + // we have a pseudo metadata item that tells if we are thematic + // or continuous like the HFA driver + if( this->m_pImageIO->getImageBandLayerType(this->nBand) == kealib::kea_continuous ) + { + m_papszMetadataList = CSLSetNameValue(m_papszMetadataList, "LAYER_TYPE", "athematic" ); + } + else + { + m_papszMetadataList = CSLSetNameValue(m_papszMetadataList, "LAYER_TYPE", "thematic" ); + } + // attribute table chunksize + if( this->m_nAttributeChunkSize != -1 ) + { + char szTemp[100]; + snprintf(szTemp, 100, "%d", this->m_nAttributeChunkSize ); + m_papszMetadataList = CSLSetNameValue(m_papszMetadataList, "ATTRIBUTETABLE_CHUNKSIZE", szTemp ); + } +} + +// internal method to create the overviews +void KEARasterBand::CreateOverviews(int nOverviews, int *panOverviewList) +{ + // delete any existing overview bands + this->deleteOverviewObjects(); + + // allocate space + m_panOverviewBands = (KEAOverview**)CPLMalloc(sizeof(KEAOverview*) * nOverviews); + m_nOverviews = nOverviews; + + // loop through and create the overviews + int nFactor, nXSize, nYSize; + for( int nCount = 0; nCount < m_nOverviews; nCount++ ) + { + nFactor = panOverviewList[nCount]; + // divide by the factor to get the new size + nXSize = this->nRasterXSize / nFactor; + nYSize = this->nRasterYSize / nFactor; + + // tell image io to create a new overview + this->m_pImageIO->createOverview(this->nBand, nCount + 1, nXSize, nYSize); + + // create one of our objects to represent it + m_panOverviewBands[nCount] = new KEAOverview((KEADataset*)this->poDS, this->nBand, GA_Update, + this->m_pImageIO, this->m_pnRefCount, nCount + 1, nXSize, nYSize); + } +} + +// virtual method to read a block +CPLErr KEARasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, void * pImage ) +{ + try + { + // GDAL deals in blocks - if we are at the end of a row + // we need to adjust the amount read so we don't go over the edge + int nxsize = this->nBlockXSize; + int nxtotalsize = this->nBlockXSize * (nBlockXOff + 1); + if( nxtotalsize > this->nRasterXSize ) + { + nxsize -= (nxtotalsize - this->nRasterXSize); + } + int nysize = this->nBlockYSize; + int nytotalsize = this->nBlockYSize * (nBlockYOff + 1); + if( nytotalsize > this->nRasterYSize ) + { + nysize -= (nytotalsize - this->nRasterYSize); + } + this->m_pImageIO->readImageBlock2Band( this->nBand, pImage, this->nBlockXSize * nBlockXOff, + this->nBlockYSize * nBlockYOff, + nxsize, nysize, this->nBlockXSize, this->nBlockYSize, + this->m_eKEADataType ); + return CE_None; + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to read file: %s", e.what() ); + return CE_Failure; + } +} + +// virtual method to write a block +CPLErr KEARasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, void * pImage ) +{ + try + { + // GDAL deals in blocks - if we are at the end of a row + // we need to adjust the amount written so we don't go over the edge + int nxsize = this->nBlockXSize; + int nxtotalsize = this->nBlockXSize * (nBlockXOff + 1); + if( nxtotalsize > this->nRasterXSize ) + { + nxsize -= (nxtotalsize - this->nRasterXSize); + } + int nysize = this->nBlockYSize; + int nytotalsize = this->nBlockYSize * (nBlockYOff + 1); + if( nytotalsize > this->nRasterYSize ) + { + nysize -= (nytotalsize - this->nRasterYSize); + } + + this->m_pImageIO->writeImageBlock2Band( this->nBand, pImage, this->nBlockXSize * nBlockXOff, + this->nBlockYSize * nBlockYOff, + nxsize, nysize, this->nBlockXSize, this->nBlockYSize, + this->m_eKEADataType ); + return CE_None; + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to write file: %s", e.what() ); + return CE_Failure; + } +} + +void KEARasterBand::SetDescription(const char *pszDescription) +{ + try + { + this->m_pImageIO->setImageBandDescription(this->nBand, pszDescription); + GDALPamRasterBand::SetDescription(pszDescription); + } + catch (kealib::KEAIOException &e) + { + // ignore? + } +} + +// set a metadata item +CPLErr KEARasterBand::SetMetadataItem(const char *pszName, const char *pszValue, const char *pszDomain) +{ + // only deal with 'default' domain - no geolocation etc + if( ( pszDomain != NULL ) && ( *pszDomain != '\0' ) ) + return CE_Failure; + try + { + // if it is LAYER_TYPE handle it seperately + if( EQUAL( pszName, "LAYER_TYPE" ) ) + { + if( EQUAL( pszValue, "athematic" ) ) + { + this->m_pImageIO->setImageBandLayerType(this->nBand, kealib::kea_continuous ); + } + else + { + this->m_pImageIO->setImageBandLayerType(this->nBand, kealib::kea_thematic ); + } + } + else + { + // otherwise set it as normal + this->m_pImageIO->setImageBandMetaData(this->nBand, pszName, pszValue ); + } + // CSLSetNameValue will update if already there + m_papszMetadataList = CSLSetNameValue( m_papszMetadataList, pszName, pszValue ); + return CE_None; + } + catch (kealib::KEAIOException &e) + { + return CE_Failure; + } +} + +// get a single metdata item +const char *KEARasterBand::GetMetadataItem (const char *pszName, const char *pszDomain) +{ + // only deal with 'default' domain - no geolocation etc + if( ( pszDomain != NULL ) && ( *pszDomain != '\0' ) ) + return NULL; + // get it out of the CSLStringList so we can be sure it is persistant + return CSLFetchNameValue(m_papszMetadataList, pszName); +} + +// get all the metadata as a CSLStringList +char **KEARasterBand::GetMetadata(const char *pszDomain) +{ + // only deal with 'default' domain - no geolocation etc + if( ( pszDomain != NULL ) && ( *pszDomain != '\0' ) ) + return NULL; + // conveniently we already have it in this format + return m_papszMetadataList; +} + +// set the metdata as a CSLStringList +CPLErr KEARasterBand::SetMetadata(char **papszMetadata, const char *pszDomain) +{ + // only deal with 'default' domain - no geolocation etc + if( ( pszDomain != NULL ) && ( *pszDomain != '\0' ) ) + return CE_Failure; + int nIndex = 0; + char *pszName; + const char *pszValue; + try + { + // iterate through each one + while( papszMetadata[nIndex] != NULL ) + { + pszName = NULL; + pszValue = CPLParseNameValue( papszMetadata[nIndex], &pszName ); + if( pszValue == NULL ) + pszValue = ""; + if( pszName != NULL ) + { + // it is LAYER_TYPE? if so handle seperately + if( EQUAL( pszName, "LAYER_TYPE" ) ) + { + if( EQUAL( pszValue, "athematic" ) ) + { + this->m_pImageIO->setImageBandLayerType(this->nBand, kealib::kea_continuous ); + } + else + { + this->m_pImageIO->setImageBandLayerType(this->nBand, kealib::kea_thematic ); + } + } + else + { + // write it into the image + this->m_pImageIO->setImageBandMetaData(this->nBand, pszName, pszValue ); + } + CPLFree(pszName); + } + nIndex++; + } + } + catch (kealib::KEAIOException &e) + { + return CE_Failure; + } + // destroy our list and duplicate the one passed in + // and use that as our list from now on + CSLDestroy(m_papszMetadataList); + m_papszMetadataList = CSLDuplicate(papszMetadata); + return CE_None; +} + +// get the no data value +double KEARasterBand::GetNoDataValue(int *pbSuccess) +{ + try + { + double dVal; + this->m_pImageIO->getNoDataValue(this->nBand, &dVal, kealib::kea_64float); + if( pbSuccess != NULL ) + *pbSuccess = 1; + + return dVal; + } + catch (kealib::KEAIOException &e) + { + if( pbSuccess != NULL ) + *pbSuccess = 0; + return -1; + } +} + +// set the no data value +CPLErr KEARasterBand::SetNoDataValue(double dfNoData) +{ + // need to check for out of range values + bool bSet = true; + GDALDataType dtype = this->GetRasterDataType(); + switch( dtype ) + { + case GDT_Byte: + bSet = (dfNoData >= 0) && (dfNoData <= UCHAR_MAX); + break; + case GDT_UInt16: + bSet = (dfNoData >= 0) && (dfNoData <= USHRT_MAX); + break; + case GDT_Int16: + bSet = (dfNoData >= SHRT_MIN) && (dfNoData <= SHRT_MAX); + break; + case GDT_UInt32: + bSet = (dfNoData >= 0) && (dfNoData <= UINT_MAX); + break; + case GDT_Int32: + bSet = (dfNoData >= INT_MIN) && (dfNoData <= INT_MAX); + break; + default: + // for other types we can't really tell if outside the range + break; + } + + try + { + if( bSet ) + { + this->m_pImageIO->setNoDataValue(this->nBand, &dfNoData, kealib::kea_64float); + } + else + { + this->m_pImageIO->undefineNoDataValue(this->nBand); + } + return CE_None; + } + catch (kealib::KEAIOException &e) + { + return CE_Failure; + } +} + +GDALRasterAttributeTable *KEARasterBand::GetDefaultRAT() +{ + if( this->m_pAttributeTable == NULL ) + { + try + { + // we assume this is never NULL - creates a new one if none exists + kealib::KEAAttributeTable *pKEATable = this->m_pImageIO->getAttributeTable(kealib::kea_att_file, this->nBand); + this->m_pAttributeTable = new KEARasterAttributeTable(pKEATable); + } + catch(kealib::KEAException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to read attributes: %s", e.what() ); + } + } + return this->m_pAttributeTable; +} + +CPLErr KEARasterBand::SetDefaultRAT(const GDALRasterAttributeTable *poRAT) +{ + if( poRAT == NULL ) + return CE_Failure; + + try + { + KEARasterAttributeTable *pKEATable = (KEARasterAttributeTable*)this->GetDefaultRAT(); + + int numRows = poRAT->GetRowCount(); + pKEATable->SetRowCount(numRows); + + for( int nGDALColumnIndex = 0; nGDALColumnIndex < poRAT->GetColumnCount(); nGDALColumnIndex++ ) + { + const char *pszColumnName = poRAT->GetNameOfCol(nGDALColumnIndex); + GDALRATFieldType eFieldType = poRAT->GetTypeOfCol(nGDALColumnIndex); + + // do we have it? + bool bExists = false; + int nKEAColumnIndex; + for( nKEAColumnIndex = 0; nKEAColumnIndex < pKEATable->GetColumnCount(); nKEAColumnIndex++ ) + { + if( EQUAL(pszColumnName, pKEATable->GetNameOfCol(nKEAColumnIndex) )) + { + bExists = true; + break; + } + } + + if( !bExists ) + { + if( pKEATable->CreateColumn(pszColumnName, eFieldType, + poRAT->GetUsageOfCol(nGDALColumnIndex) ) != CE_None ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to create column"); + return CE_Failure; + } + nKEAColumnIndex = pKEATable->GetColumnCount() - 1; + } + + if( numRows == 0 ) + continue; + + // ok now copy data + if( eFieldType == GFT_Integer ) + { + int *panIntData = (int*)VSIMalloc2(numRows, sizeof(int)); + if( panIntData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::SetDefaultRAT"); + return CE_Failure; + } + + if( ((GDALRasterAttributeTable*)poRAT)->ValuesIO(GF_Read, nGDALColumnIndex, 0, numRows, panIntData ) == CE_None ) + { + pKEATable->ValuesIO(GF_Write, nKEAColumnIndex, 0, numRows, panIntData); + } + CPLFree(panIntData); + } + else if( eFieldType == GFT_Real ) + { + double *padfFloatData = (double*)VSIMalloc2(numRows, sizeof(double)); + if( padfFloatData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::SetDefaultRAT"); + return CE_Failure; + } + + if( ((GDALRasterAttributeTable*)poRAT)->ValuesIO(GF_Read, nGDALColumnIndex, 0, numRows, padfFloatData ) == CE_None ) + { + pKEATable->ValuesIO(GF_Write, nKEAColumnIndex, 0, numRows, padfFloatData); + } + CPLFree(padfFloatData); + } + else + { + char **papszStringData = (char**)VSIMalloc2(numRows, sizeof(char*)); + if( papszStringData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::SetDefaultRAT"); + return CE_Failure; + } + + if( ((GDALRasterAttributeTable*)poRAT)->ValuesIO(GF_Read, nGDALColumnIndex, 0, numRows, papszStringData ) == CE_None ) + { + pKEATable->ValuesIO(GF_Write, nKEAColumnIndex, 0, numRows, papszStringData); + for( int n = 0; n < numRows; n++ ) + CPLFree(papszStringData[n]); + } + CPLFree(papszStringData); + + } + } + } + catch(kealib::KEAException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to write attributes: %s", e.what() ); + return CE_Failure; + } + return CE_None; +} + +GDALColorTable *KEARasterBand::GetColorTable() +{ + if( this->m_pColorTable == NULL ) + { + try + { + GDALRasterAttributeTable *pKEATable = this->GetDefaultRAT(); + int nRedIdx = -1; + int nGreenIdx = -1; + int nBlueIdx = -1; + int nAlphaIdx = -1; + + for( int nColIdx = 0; nColIdx < pKEATable->GetColumnCount(); nColIdx++ ) + { + if( pKEATable->GetTypeOfCol(nColIdx) == GFT_Integer ) + { + GDALRATFieldUsage eFieldUsage = pKEATable->GetUsageOfCol(nColIdx); + if( eFieldUsage == GFU_Red ) + nRedIdx = nColIdx; + else if( eFieldUsage == GFU_Green ) + nGreenIdx = nColIdx; + else if( eFieldUsage == GFU_Blue ) + nBlueIdx = nColIdx; + else if( eFieldUsage == GFU_Alpha ) + nAlphaIdx = nColIdx; + } + } + + if( ( nRedIdx != -1 ) && ( nGreenIdx != -1 ) && ( nBlueIdx != -1 ) && ( nAlphaIdx != -1 ) ) + { + // we need to create one - only do RGB palettes + this->m_pColorTable = new GDALColorTable(GPI_RGB); + + // OK go through each row and fill in the fields + for( int nRowIndex = 0; nRowIndex < pKEATable->GetRowCount(); nRowIndex++ ) + { + // maybe could be more efficient using ValuesIO + GDALColorEntry colorEntry; + colorEntry.c1 = pKEATable->GetValueAsInt(nRowIndex, nRedIdx); + colorEntry.c2 = pKEATable->GetValueAsInt(nRowIndex, nGreenIdx); + colorEntry.c3 = pKEATable->GetValueAsInt(nRowIndex, nBlueIdx); + colorEntry.c4 = pKEATable->GetValueAsInt(nRowIndex, nAlphaIdx); + this->m_pColorTable->SetColorEntry(nRowIndex, &colorEntry); + } + } + } + catch(kealib::KEAException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to read color table: %s", e.what() ); + delete this->m_pColorTable; + this->m_pColorTable = NULL; + } + } + return this->m_pColorTable; +} + +CPLErr KEARasterBand::SetColorTable(GDALColorTable *poCT) +{ + if( poCT == NULL ) + return CE_Failure; + + try + { + GDALRasterAttributeTable *pKEATable = this->GetDefaultRAT(); + int nRedIdx = -1; + int nGreenIdx = -1; + int nBlueIdx = -1; + int nAlphaIdx = -1; + + if( poCT->GetColorEntryCount() > pKEATable->GetRowCount() ) + { + pKEATable->SetRowCount(poCT->GetColorEntryCount()); + } + + for( int nColIdx = 0; nColIdx < pKEATable->GetColumnCount(); nColIdx++ ) + { + if( pKEATable->GetTypeOfCol(nColIdx) == GFT_Integer ) + { + GDALRATFieldUsage eFieldUsage = pKEATable->GetUsageOfCol(nColIdx); + if( eFieldUsage == GFU_Red ) + nRedIdx = nColIdx; + else if( eFieldUsage == GFU_Green ) + nGreenIdx = nColIdx; + else if( eFieldUsage == GFU_Blue ) + nBlueIdx = nColIdx; + else if( eFieldUsage == GFU_Alpha ) + nAlphaIdx = nColIdx; + } + } + + // create if needed + if( nRedIdx == -1 ) + { + if( pKEATable->CreateColumn("Red", GFT_Integer, GFU_Red ) != CE_None ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to create column" ); + return CE_Failure; + } + nRedIdx = pKEATable->GetColumnCount() - 1; + } + if( nGreenIdx == -1 ) + { + if( pKEATable->CreateColumn("Green", GFT_Integer, GFU_Green ) != CE_None ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to create column" ); + return CE_Failure; + } + nGreenIdx = pKEATable->GetColumnCount() - 1; + } + if( nBlueIdx == -1 ) + { + if( pKEATable->CreateColumn("Blue", GFT_Integer, GFU_Blue ) != CE_None ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to create column" ); + return CE_Failure; + } + nBlueIdx = pKEATable->GetColumnCount() - 1; + } + if( nAlphaIdx == -1 ) + { + if( pKEATable->CreateColumn("Alpha", GFT_Integer, GFU_Alpha ) != CE_None ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to create column" ); + return CE_Failure; + } + nAlphaIdx = pKEATable->GetColumnCount() - 1; + } + + // OK go through each row and fill in the fields + for( int nRowIndex = 0; nRowIndex < poCT->GetColorEntryCount(); nRowIndex++ ) + { + // maybe could be more efficient using ValuesIO + GDALColorEntry colorEntry; + poCT->GetColorEntryAsRGB(nRowIndex, &colorEntry); + pKEATable->SetValue(nRowIndex, nRedIdx, colorEntry.c1); + pKEATable->SetValue(nRowIndex, nGreenIdx, colorEntry.c2); + pKEATable->SetValue(nRowIndex, nBlueIdx, colorEntry.c3); + pKEATable->SetValue(nRowIndex, nAlphaIdx, colorEntry.c4); + } + + // out of date + delete this->m_pColorTable; + this->m_pColorTable = NULL; + } + catch(kealib::KEAException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to write color table: %s", e.what() ); + return CE_Failure; + } + return CE_None; +} + +GDALColorInterp KEARasterBand::GetColorInterpretation() +{ + kealib::KEABandClrInterp ekeainterp; + try + { + ekeainterp = this->m_pImageIO->getImageBandClrInterp(this->nBand); + } + catch(kealib::KEAException &e) + { + return GCI_GrayIndex; + } + + GDALColorInterp egdalinterp; + switch(ekeainterp) + { + case kealib::kea_generic: + case kealib::kea_greyindex: + egdalinterp = GCI_GrayIndex; + break; + case kealib::kea_paletteindex: + egdalinterp = GCI_PaletteIndex; + break; + case kealib::kea_redband: + egdalinterp = GCI_RedBand; + break; + case kealib::kea_greenband: + egdalinterp = GCI_GreenBand; + break; + case kealib::kea_blueband: + egdalinterp = GCI_BlueBand; + break; + case kealib::kea_alphaband: + egdalinterp = GCI_AlphaBand; + break; + case kealib::kea_hueband: + egdalinterp = GCI_HueBand; + break; + case kealib::kea_saturationband: + egdalinterp = GCI_SaturationBand; + break; + case kealib::kea_lightnessband: + egdalinterp = GCI_LightnessBand; + break; + case kealib::kea_cyanband: + egdalinterp = GCI_CyanBand; + break; + case kealib::kea_magentaband: + egdalinterp = GCI_MagentaBand; + break; + case kealib::kea_yellowband: + egdalinterp = GCI_YellowBand; + break; + case kealib::kea_blackband: + egdalinterp = GCI_BlackBand; + break; + case kealib::kea_ycbcr_yband: + egdalinterp = GCI_YCbCr_YBand; + break; + case kealib::kea_ycbcr_cbband: + egdalinterp = GCI_YCbCr_CbBand; + break; + case kealib::kea_ycbcr_crband: + egdalinterp = GCI_YCbCr_CrBand; + break; + default: + egdalinterp = GCI_GrayIndex; + break; + } + + return egdalinterp; +} + +CPLErr KEARasterBand::SetColorInterpretation(GDALColorInterp egdalinterp) +{ + kealib::KEABandClrInterp ekeainterp; + switch(egdalinterp) + { + case GCI_GrayIndex: + ekeainterp = kealib::kea_greyindex; + break; + case GCI_PaletteIndex: + ekeainterp = kealib::kea_paletteindex; + break; + case GCI_RedBand: + ekeainterp = kealib::kea_redband; + break; + case GCI_GreenBand: + ekeainterp = kealib::kea_greenband; + break; + case GCI_BlueBand: + ekeainterp = kealib::kea_blueband; + break; + case GCI_AlphaBand: + ekeainterp = kealib::kea_alphaband; + break; + case GCI_HueBand: + ekeainterp = kealib::kea_hueband; + break; + case GCI_SaturationBand: + ekeainterp = kealib::kea_saturationband; + break; + case GCI_LightnessBand: + ekeainterp = kealib::kea_lightnessband; + break; + case GCI_CyanBand: + ekeainterp = kealib::kea_cyanband; + break; + case GCI_MagentaBand: + ekeainterp = kealib::kea_magentaband; + break; + case GCI_YellowBand: + ekeainterp = kealib::kea_yellowband; + break; + case GCI_BlackBand: + ekeainterp = kealib::kea_blackband; + break; + case GCI_YCbCr_YBand: + ekeainterp = kealib::kea_ycbcr_yband; + break; + case GCI_YCbCr_CbBand: + ekeainterp = kealib::kea_ycbcr_cbband; + break; + case GCI_YCbCr_CrBand: + ekeainterp = kealib::kea_ycbcr_crband; + break; + default: + ekeainterp = kealib::kea_greyindex; + break; + } + + try + { + this->m_pImageIO->setImageBandClrInterp(this->nBand, ekeainterp); + } + catch(kealib::KEAException &e) + { + // do nothing? The docs say CE_Failure only if unsupporte by format + } + return CE_None; +} + +// clean up our overview objects +void KEARasterBand::deleteOverviewObjects() +{ + // deletes the objects - not the overviews themselves + int nCount; + for( nCount = 0; nCount < m_nOverviews; nCount++ ) + { + delete m_panOverviewBands[nCount]; + } + CPLFree(m_panOverviewBands); + m_panOverviewBands = NULL; + m_nOverviews = 0; +} + +// read in any overviews in the file into our array of objects +void KEARasterBand::readExistingOverviews() +{ + // delete any existing overview bands + this->deleteOverviewObjects(); + + m_nOverviews = this->m_pImageIO->getNumOfOverviews(this->nBand); + m_panOverviewBands = (KEAOverview**)CPLMalloc(sizeof(KEAOverview*) * m_nOverviews); + + uint64_t nXSize, nYSize; + for( int nCount = 0; nCount < m_nOverviews; nCount++ ) + { + this->m_pImageIO->getOverviewSize(this->nBand, nCount + 1, &nXSize, &nYSize); + m_panOverviewBands[nCount] = new KEAOverview((KEADataset*)this->poDS, this->nBand, GA_ReadOnly, + this->m_pImageIO, this->m_pnRefCount, nCount + 1, nXSize, nYSize); + } +} + +// number of overviews +int KEARasterBand::GetOverviewCount() +{ + return m_nOverviews; +} + +// get a given overview +GDALRasterBand* KEARasterBand::GetOverview(int nOverview) +{ + if( nOverview < 0 || nOverview >= m_nOverviews ) + { + return NULL; + } + else + { + return m_panOverviewBands[nOverview]; + } +} + +CPLErr KEARasterBand::CreateMaskBand(CPL_UNUSED int nFlags) +{ + if( m_bMaskBandOwned ) + delete m_pMaskBand; + m_pMaskBand = NULL; + try + { + this->m_pImageIO->createMask(this->nBand); + } + catch(kealib::KEAException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to create mask band: %s", e.what()); + return CE_Failure; + } + return CE_None; +} + +GDALRasterBand* KEARasterBand::GetMaskBand() +{ + if( m_pMaskBand == NULL ) + { + try + { + if( this->m_pImageIO->maskCreated(this->nBand) ) + { + m_pMaskBand = new KEAMaskBand(this, this->m_pImageIO, this->m_pnRefCount); + m_bMaskBandOwned = true; + } + else + { + // use the base class implementation - GDAL will delete + //fprintf( stderr, "returning base GetMaskBand()\n" ); + m_pMaskBand = GDALPamRasterBand::GetMaskBand(); + } + } + catch(kealib::KEAException &e) + { + // do nothing? + } + } + return m_pMaskBand; +} + +int KEARasterBand::GetMaskFlags() +{ + try + { + if( ! this->m_pImageIO->maskCreated(this->nBand) ) + { + // need to return the base class one since we are using + // the base class implementation of GetMaskBand() + //fprintf( stderr, "returning base GetMaskFlags()\n" ); + return GDALPamRasterBand::GetMaskFlags(); + } + } + catch(kealib::KEAException &e) + { + // do nothing? + } + + // none of the other flags seem to make sense... + return 0; +} + diff --git a/bazaar/plugin/gdal/frmts/kea/keaband.h b/bazaar/plugin/gdal/frmts/kea/keaband.h new file mode 100644 index 000000000..24b556e77 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/keaband.h @@ -0,0 +1,119 @@ +/* + * $Id: keaband.h 29258 2015-05-28 22:08:43Z rouault $ + * keaband.h + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef KEABAND_H +#define KEABAND_H + +#include "gdal_pam.h" +#if defined(USE_GCC_VISIBILITY_FLAG) && !defined(DllExport) +#define DllExport CPL_DLL +#endif +#include "keadataset.h" + +class KEAOverview; +class KEAMaskBand; + +// Provides the implementation of a GDAL raster band +class KEARasterBand : public GDALPamRasterBand +{ +private: + int *m_pnRefCount; // reference count of m_pImageIO + + int m_nOverviews; // number of overviews + KEAOverview **m_panOverviewBands; // array of overview objects + GDALRasterBand *m_pMaskBand; // pointer to mask band if one exists (and been requested) + bool m_bMaskBandOwned; // do we delete it or not? + + GDALRasterAttributeTable *m_pAttributeTable; // pointer to the attribute table + // created on first call to GetDefaultRAT() + GDALColorTable *m_pColorTable; // pointer to the color table + // created on first call to GetColorTable() + + int m_nAttributeChunkSize; // for reporting via the metadata +public: + // constructor/destructor + KEARasterBand( KEADataset *pDataset, int nSrcBand, GDALAccess eAccess, kealib::KEAImageIO *pImageIO, int *pRefCount ); + ~KEARasterBand(); + + // virtual methods for overview support + int GetOverviewCount(); + GDALRasterBand* GetOverview(int nOverview); + + // virtual methods for band names (aka description) + void SetDescription(const char *); + + // virtual methods for handling the metadata + CPLErr SetMetadataItem (const char *pszName, const char *pszValue, const char *pszDomain=""); + const char *GetMetadataItem (const char *pszName, const char *pszDomain=""); + char **GetMetadata(const char *pszDomain=""); + CPLErr SetMetadata(char **papszMetadata, const char *pszDomain=""); + + // virtual methods for the no data value + double GetNoDataValue(int *pbSuccess=NULL); + CPLErr SetNoDataValue(double dfNoData); + + // virtual methods for RATs + GDALRasterAttributeTable *GetDefaultRAT(); + CPLErr SetDefaultRAT(const GDALRasterAttributeTable *poRAT); + + // virtual methods for color tables + GDALColorTable *GetColorTable(); + CPLErr SetColorTable(GDALColorTable *poCT); + + // virtual methods for color interpretation + GDALColorInterp GetColorInterpretation(); + CPLErr SetColorInterpretation(GDALColorInterp gdalinterp); + + // virtual mthods for band masks + CPLErr CreateMaskBand(int nFlags); + GDALRasterBand* GetMaskBand(); + int GetMaskFlags(); + + // internal methods for overviews + void readExistingOverviews(); + void deleteOverviewObjects(); + void CreateOverviews(int nOverviews, int *panOverviewList); + KEAOverview** GetOverviewList() { return m_panOverviewBands; } + +protected: + // methods for accessing data as blocks + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + + // updates m_papszMetadataList + void UpdateMetadataList(); + + kealib::KEAImageIO *m_pImageIO; // our image access pointer - refcounted + char **m_papszMetadataList; // CPLStringList of metadata + kealib::KEADataType m_eKEADataType; // data type as KEA enum +}; + + +#endif //KEABAND_H diff --git a/bazaar/plugin/gdal/frmts/kea/keacopy.cpp b/bazaar/plugin/gdal/frmts/kea/keacopy.cpp new file mode 100644 index 000000000..83017652b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/keacopy.cpp @@ -0,0 +1,489 @@ +/* + * $Id: keacopy.cpp 28437 2015-02-07 15:50:57Z rouault $ + * keacopy.cpp + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include "gdal_priv.h" +#include "gdal_rat.h" +#include "libkea/KEAImageIO.h" +#include "libkea/KEAAttributeTable.h" +#include "libkea/KEAAttributeTableInMem.h" + +// Support functions for CreateCopy() + +// Copies GDAL Band to KEA Band if nOverview == -1 +// Otherwise it is assumed we are writing to the specified overview +static +bool KEACopyRasterData( GDALRasterBand *pBand, kealib::KEAImageIO *pImageIO, int nBand, int nOverview, int nTotalBands, GDALProgressFunc pfnProgress, void *pProgressData) +{ + // get some info + kealib::KEADataType eKeaType = pImageIO->getImageBandDataType(nBand); + unsigned int nBlockSize; + if( nOverview == -1 ) + nBlockSize = pImageIO->getImageBlockSize( nBand ); + else + nBlockSize = pImageIO->getOverviewBlockSize(nBand, nOverview); + + GDALDataType eGDALType = pBand->GetRasterDataType(); + unsigned int nXSize = pBand->GetXSize(); + unsigned int nYSize = pBand->GetYSize(); + + // allocate some space + int nPixelSize = GDALGetDataTypeSize( eGDALType ) / 8; + void *pData = VSIMalloc3( nPixelSize, nBlockSize, nBlockSize); + if( pData == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Unable to allocate memory" ); + return false; + } + // for progress + int nTotalBlocks = std::ceil( (double)nXSize / (double)nBlockSize ) * std::ceil( (double)nYSize / (double)nBlockSize ); + int nBlocksComplete = 0; + double dLastFraction = -1; + // go through the image + for( unsigned int nY = 0; nY < nYSize; nY += nBlockSize ) + { + // adjust for edge blocks + unsigned int nysize = nBlockSize; + unsigned int nytotalsize = nY + nBlockSize; + if( nytotalsize > nYSize ) + nysize -= (nytotalsize - nYSize); + for( unsigned int nX = 0; nX < nXSize; nX += nBlockSize ) + { + // adjust for edge blocks + unsigned int nxsize = nBlockSize; + unsigned int nxtotalsize = nX + nBlockSize; + if( nxtotalsize > nXSize ) + nxsize -= (nxtotalsize - nXSize); + + // read in from GDAL + if( pBand->RasterIO( GF_Read, nX, nY, nxsize, nysize, pData, nxsize, nysize, eGDALType, nPixelSize, nPixelSize * nBlockSize, NULL) != CE_None ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Unable to read blcok at %d %d\n", nX, nY ); + return false; + } + // write out to KEA + if( nOverview == -1 ) + pImageIO->writeImageBlock2Band( nBand, pData, nX, nY, nxsize, nysize, nBlockSize, nBlockSize, eKeaType); + else + pImageIO->writeToOverview( nBand, nOverview, pData, nX, nY, nxsize, nysize, nBlockSize, nBlockSize, eKeaType); + + // progress + nBlocksComplete++; + if( nOverview == -1 ) + { + double dFraction = (((double)nBlocksComplete / (double)nTotalBlocks) / (double)nTotalBands) + ((double)(nBand-1) * (1.0 / (double)nTotalBands)); + if( dFraction != dLastFraction ) + { + if( !pfnProgress( dFraction, NULL, pProgressData ) ) + { + CPLFree( pData ); + return false; + } + dLastFraction = dFraction; + } + } + } + } + + CPLFree( pData ); + return true; +} + +static const int RAT_CHUNKSIZE = 1000; +// copies the raster attribute table +static void KEACopyRAT(GDALRasterBand *pBand, kealib::KEAImageIO *pImageIO, int nBand) +{ + const GDALRasterAttributeTable *gdalAtt = pBand->GetDefaultRAT(); + if((gdalAtt != NULL) && (gdalAtt->GetRowCount() > 0)) + { + // some operations depend on whether the input dataset is HFA + int bInputHFA = pBand->GetDataset()->GetDriver() != NULL && + EQUAL(pBand->GetDataset()->GetDriver()->GetDescription(), "HFA"); + + kealib::KEAAttributeTable *keaAtt = pImageIO->getAttributeTable(kealib::kea_att_file, nBand); + + /*bool redDef = false; + int redIdx = -1; + bool greenDef = false; + int greenIdx = -1; + bool blueDef = false; + int blueIdx = -1; + bool alphaDef = false; + int alphaIdx = -1;*/ + + int numCols = gdalAtt->GetColumnCount(); + std::vector *fields = new std::vector(); + kealib::KEAATTField *field; + for(int ni = 0; ni < numCols; ++ni) + { + field = new kealib::KEAATTField(); + field->name = gdalAtt->GetNameOfCol(ni); + + field->dataType = kealib::kea_att_string; + switch(gdalAtt->GetTypeOfCol(ni)) + { + case GFT_Integer: + field->dataType = kealib::kea_att_int; + break; + case GFT_Real: + field->dataType = kealib::kea_att_float; + break; + case GFT_String: + field->dataType = kealib::kea_att_string; + break; + default: + // leave as "kea_att_string" + break; + } + + if(bInputHFA && (field->name == "Histogram")) + { + field->usage = "PixelCount"; + field->dataType = kealib::kea_att_int; + } + else if(bInputHFA && (field->name == "Opacity")) + { + field->name = "Alpha"; + field->usage = "Alpha"; + field->dataType = kealib::kea_att_int; + /*alphaDef = true; + alphaIdx = ni;*/ + } + else + { + field->usage = "Generic"; + switch(gdalAtt->GetUsageOfCol(ni)) + { + case GFU_PixelCount: + field->usage = "PixelCount"; + break; + case GFU_Name: + field->usage = "Name"; + break; + case GFU_Red: + field->usage = "Red"; + if( bInputHFA ) + { + field->dataType = kealib::kea_att_int; + /*redDef = true; + redIdx = ni;*/ + } + break; + case GFU_Green: + field->usage = "Green"; + if( bInputHFA ) + { + field->dataType = kealib::kea_att_int; + /*greenDef = true; + greenIdx = ni;*/ + } + break; + case GFU_Blue: + field->usage = "Blue"; + if( bInputHFA ) + { + field->dataType = kealib::kea_att_int; + /*blueDef = true; + blueIdx = ni;*/ + } + break; + case GFU_Alpha: + field->usage = "Alpha"; + break; + default: + // leave as "Generic" + break; + } + } + + fields->push_back(field); + } + + keaAtt->addFields(fields); // This function will populate the field indexs used within the KEA RAT. + + int numRows = gdalAtt->GetRowCount(); + keaAtt->addRows(numRows); + + int *pnIntBuffer = new int[RAT_CHUNKSIZE]; + int64_t *pnInt64Buffer = new int64_t[RAT_CHUNKSIZE]; + double *pfDoubleBuffer = new double[RAT_CHUNKSIZE]; + for(int ni = 0; ni < numRows; ni += RAT_CHUNKSIZE ) + { + int nLength = RAT_CHUNKSIZE; + if( ( ni + nLength ) > numRows ) + { + nLength = numRows - ni; + } + for(int nj = 0; nj < numCols; ++nj) + { + field = fields->at(nj); + + switch(field->dataType) + { + case kealib::kea_att_int: + ((GDALRasterAttributeTable*)gdalAtt)->ValuesIO(GF_Read, nj, ni, nLength, pnIntBuffer); + for( int i = 0; i < nLength; i++ ) + { + pnInt64Buffer[i] = pnIntBuffer[i]; + } + keaAtt->setIntFields(ni, nLength, field->idx, pnInt64Buffer); + break; + case kealib::kea_att_float: + ((GDALRasterAttributeTable*)gdalAtt)->ValuesIO(GF_Read, nj, ni, nLength, pfDoubleBuffer); + keaAtt->setFloatFields(ni, nLength, field->idx, pfDoubleBuffer); + break; + case kealib::kea_att_string: + { + char **papszColData = (char**)VSIMalloc2(nLength, sizeof(char*)); + ((GDALRasterAttributeTable*)gdalAtt)->ValuesIO(GF_Read, nj, ni, nLength, papszColData); + + std::vector aStringBuffer; + for( int i = 0; i < nLength; i++ ) + { + aStringBuffer.push_back(papszColData[i]); + } + + for( int i = 0; i < nLength; i++ ) + CPLFree(papszColData[i]); + CPLFree(papszColData); + + keaAtt->setStringFields(ni, nLength, field->idx, &aStringBuffer); + } + break; + default: + // Ignore as data type is not known or available from a HFA/GDAL RAT." + break; + } + } + } + + delete[] pnIntBuffer; + delete[] pnInt64Buffer; + delete[] pfDoubleBuffer; + + delete keaAtt; + for(std::vector::iterator iterField = fields->begin(); iterField != fields->end(); ++iterField) + { + delete *iterField; + } + delete fields; + } +} + +// copies the metadata +// pass nBand == -1 to copy a dataset's metadata +// or band index to copy a band's metadata +static void KEACopyMetadata( GDALMajorObject *pObject, kealib::KEAImageIO *pImageIO, int nBand) +{ + char **ppszMetadata = pObject->GetMetadata(); + if( ppszMetadata != NULL ) + { + char *pszName; + const char *pszValue; + int nCount = 0; + while( ppszMetadata[nCount] != NULL ) + { + pszName = NULL; + pszValue = CPLParseNameValue( ppszMetadata[nCount], &pszName ); + if( pszValue == NULL ) + pszValue = ""; + if( pszName != NULL ) + { + // it is LAYER_TYPE and a Band? if so handle seperately + if( ( nBand != -1 ) && EQUAL( pszName, "LAYER_TYPE" ) ) + { + if( EQUAL( pszValue, "athematic" ) ) + { + pImageIO->setImageBandLayerType(nBand, kealib::kea_continuous ); + } + else + { + pImageIO->setImageBandLayerType(nBand, kealib::kea_thematic ); + } + } + else if( ( nBand != -1 ) && EQUAL( pszName, "STATISTICS_HISTOBINVALUES") ) + { + // this gets copied accross as part of the attributes + // so ignore for now + } + else + { + // write it into the image + if( nBand != -1 ) + pImageIO->setImageBandMetaData(nBand, pszName, pszValue ); + else + pImageIO->setImageMetaData(pszName, pszValue ); + } + CPLFree(pszName); + } + nCount++; + } + } +} + +// copies the description over +static void KEACopyDescription(GDALRasterBand *pBand, kealib::KEAImageIO *pImageIO, int nBand) +{ + const char *pszDesc = pBand->GetDescription(); + pImageIO->setImageBandDescription(nBand, pszDesc); +} + +// copies the no data value accross +static void KEACopyNoData(GDALRasterBand *pBand, kealib::KEAImageIO *pImageIO, int nBand) +{ + int bSuccess = 0; + double dNoData = pBand->GetNoDataValue(&bSuccess); + if( bSuccess ) + { + pImageIO->setNoDataValue(nBand, &dNoData, kealib::kea_64float); + } +} + +static bool KEACopyBand( GDALRasterBand *pBand, kealib::KEAImageIO *pImageIO, int nBand, int nTotalbands, GDALProgressFunc pfnProgress, void *pProgressData) +{ + // first copy the raster data over + if( !KEACopyRasterData( pBand, pImageIO, nBand, -1, nTotalbands, pfnProgress, pProgressData) ) + return false; + + // are there any overviews? + int nOverviews = pBand->GetOverviewCount(); + for( int nOverviewCount = 0; nOverviewCount < nOverviews; nOverviewCount++ ) + { + GDALRasterBand *pOverview = pBand->GetOverview(nOverviewCount); + int nOverviewXSize = pOverview->GetXSize(); + int nOverviewYSize = pOverview->GetYSize(); + pImageIO->createOverview( nBand, nOverviewCount + 1, nOverviewXSize, nOverviewYSize); + if( !KEACopyRasterData( pOverview, pImageIO, nBand, nOverviewCount + 1, nTotalbands, pfnProgress, pProgressData) ) + return false; + } + + // now metadata + KEACopyMetadata(pBand, pImageIO, nBand); + + // and attributes + KEACopyRAT(pBand, pImageIO, nBand); + + // and description + KEACopyDescription(pBand, pImageIO, nBand); + + // and no data + KEACopyNoData(pBand, pImageIO, nBand); + + return true; +} + +static void KEACopySpatialInfo(GDALDataset *pDataset, kealib::KEAImageIO *pImageIO) +{ + kealib::KEAImageSpatialInfo *pSpatialInfo = pImageIO->getSpatialInfo(); + + double padfTransform[6]; + if( pDataset->GetGeoTransform(padfTransform) == CE_None ) + { + // convert back from GDAL's array format + pSpatialInfo->tlX = padfTransform[0]; + pSpatialInfo->xRes = padfTransform[1]; + pSpatialInfo->xRot = padfTransform[2]; + pSpatialInfo->tlY = padfTransform[3]; + pSpatialInfo->yRot = padfTransform[4]; + pSpatialInfo->yRes = padfTransform[5]; + } + + const char *pszProjection = pDataset->GetProjectionRef(); + pSpatialInfo->wktString = pszProjection; + + pImageIO->setSpatialInfo( pSpatialInfo ); +} + +// copies the GCP's accross +static void KEACopyGCPs(GDALDataset *pDataset, kealib::KEAImageIO *pImageIO) +{ + int nGCPs = pDataset->GetGCPCount(); + + if( nGCPs > 0 ) + { + std::vector KEAGCPs; + const GDAL_GCP *pGDALGCPs = pDataset->GetGCPs(); + + for( int n = 0; n < nGCPs; n++ ) + { + kealib::KEAImageGCP *pGCP = new kealib::KEAImageGCP; + pGCP->pszId = pGDALGCPs[n].pszId; + pGCP->pszInfo = pGDALGCPs[n].pszInfo; + pGCP->dfGCPPixel = pGDALGCPs[n].dfGCPPixel; + pGCP->dfGCPLine = pGDALGCPs[n].dfGCPLine; + pGCP->dfGCPX = pGDALGCPs[n].dfGCPX; + pGCP->dfGCPY = pGDALGCPs[n].dfGCPY; + pGCP->dfGCPZ = pGDALGCPs[n].dfGCPZ; + KEAGCPs.push_back(pGCP); + } + + const char *pszGCPProj = pDataset->GetGCPProjection(); + try + { + pImageIO->setGCPs(&KEAGCPs, pszGCPProj); + } + catch(kealib::KEAException &e) + { + } + + for( std::vector::iterator itr = KEAGCPs.begin(); itr != KEAGCPs.end(); itr++) + { + delete (*itr); + } + } +} + + + +bool KEACopyFile( GDALDataset *pDataset, kealib::KEAImageIO *pImageIO, GDALProgressFunc pfnProgress, void *pProgressData ) +{ + // Main function - copies pDataset to pImageIO + + // copy accross the spatial info + KEACopySpatialInfo( pDataset, pImageIO); + + // dataset metadata + KEACopyMetadata(pDataset, pImageIO, -1); + + // GCPs + KEACopyGCPs(pDataset, pImageIO); + + // now copy all the bands over + int nBands = pDataset->GetRasterCount(); + for( int nBand = 0; nBand < nBands; nBand++ ) + { + GDALRasterBand *pBand = pDataset->GetRasterBand(nBand + 1); + if( !KEACopyBand( pBand, pImageIO, nBand +1, nBands, pfnProgress, pProgressData ) ) + return false; + } + + pfnProgress( 1.0, NULL, pProgressData ); + return true; +} diff --git a/bazaar/plugin/gdal/frmts/kea/keacopy.h b/bazaar/plugin/gdal/frmts/kea/keacopy.h new file mode 100644 index 000000000..d2c6825c3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/keacopy.h @@ -0,0 +1,42 @@ +/* + * $Id: keacopy.h 29258 2015-05-28 22:08:43Z rouault $ + * keacopy.h + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef KEACOPY_H +#define KEACOPY_H + +#include "gdal_priv.h" +#if defined(USE_GCC_VISIBILITY_FLAG) && !defined(DllExport) +#define DllExport CPL_DLL +#endif +#include "libkea/KEAImageIO.h" + +bool KEACopyFile( GDALDataset *pDataset, kealib::KEAImageIO *pImageIO, GDALProgressFunc pfnProgress, void *pProgressData ); + +#endif diff --git a/bazaar/plugin/gdal/frmts/kea/keadataset.cpp b/bazaar/plugin/gdal/frmts/kea/keadataset.cpp new file mode 100644 index 000000000..4826e0daf --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/keadataset.cpp @@ -0,0 +1,867 @@ +/* + * $Id: keadataset.cpp 28825 2015-03-30 14:48:50Z rouault $ + * keadataset.cpp + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "keadataset.h" +#include "keaband.h" +#include "keacopy.h" + +#include "libkea/KEACommon.h" + +// Function for converting a libkea type into a GDAL type +GDALDataType KEA_to_GDAL_Type( kealib::KEADataType ekeaType ) +{ + GDALDataType egdalType = GDT_Unknown; + switch( ekeaType ) + { + case kealib::kea_8int: + case kealib::kea_8uint: + egdalType = GDT_Byte; + break; + case kealib::kea_16int: + egdalType = GDT_Int16; + break; + case kealib::kea_32int: + egdalType = GDT_Int32; + break; + case kealib::kea_16uint: + egdalType = GDT_UInt16; + break; + case kealib::kea_32uint: + egdalType = GDT_UInt32; + break; + case kealib::kea_32float: + egdalType = GDT_Float32; + break; + case kealib::kea_64float: + egdalType = GDT_Float64; + break; + default: + egdalType = GDT_Unknown; + break; + } + return egdalType; +} + +// function for converting a GDAL type to a kealib type +kealib::KEADataType GDAL_to_KEA_Type( GDALDataType egdalType ) +{ + kealib::KEADataType ekeaType = kealib::kea_undefined; + switch( egdalType ) + { + case GDT_Byte: + ekeaType = kealib::kea_8uint; + break; + case GDT_Int16: + ekeaType = kealib::kea_16int; + break; + case GDT_Int32: + ekeaType = kealib::kea_32int; + break; + case GDT_UInt16: + ekeaType = kealib::kea_16uint; + break; + case GDT_UInt32: + ekeaType = kealib::kea_32uint; + break; + case GDT_Float32: + ekeaType = kealib::kea_32float; + break; + case GDT_Float64: + ekeaType = kealib::kea_64float; + break; + default: + ekeaType = kealib::kea_undefined; + break; + } + return ekeaType; +} + +// static function - pointer set in driver +GDALDataset *KEADataset::Open( GDALOpenInfo * poOpenInfo ) +{ + if( Identify( poOpenInfo ) ) + { + try + { + // try and open it in the appropriate mode + H5::H5File *pH5File; + if( poOpenInfo->eAccess == GA_ReadOnly ) + { + pH5File = kealib::KEAImageIO::openKeaH5RDOnly( poOpenInfo->pszFilename ); + } + else + { + pH5File = kealib::KEAImageIO::openKeaH5RW( poOpenInfo->pszFilename ); + } + // create the KEADataset object + KEADataset *pDataset = new KEADataset( pH5File, poOpenInfo->eAccess ); + + // set the description as the name + pDataset->SetDescription( poOpenInfo->pszFilename ); + + return pDataset; + } + catch (kealib::KEAIOException &e) + { + // was a problem - can't be a valid file + return NULL; + } + } + else + { + // not a KEA file + return NULL; + } +} + +// static function- pointer set in driver +// this function is called in preference to Open +// +int KEADataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + bool bisKEA = false; + +/* -------------------------------------------------------------------- */ +/* Is it an HDF5 file? */ +/* -------------------------------------------------------------------- */ + static const char achSignature[] = "\211HDF\r\n\032\n"; + + if( poOpenInfo->pabyHeader == NULL || + memcmp(poOpenInfo->pabyHeader,achSignature,8) != 0 ) + { + return false; + } + + try + { + // is this a KEA file? + bisKEA = kealib::KEAImageIO::isKEAImage( poOpenInfo->pszFilename ); + } + catch (kealib::KEAIOException &e) + { + bisKEA = false; + } + if( bisKEA ) + return 1; + else + return 0; +} + + +// static function +H5::H5File *KEADataset::CreateLL( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszParmList ) +{ + GDALDriverH hDriver = GDALGetDriverByName( "KEA" ); + if( ( hDriver == NULL ) || !GDALValidateCreationOptions( hDriver, papszParmList ) ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed. Invalid creation option(s)\n", pszFilename); + return NULL; + } + // process any creation options in papszParmList + // default value + unsigned int nimageblockSize = kealib::KEA_IMAGE_CHUNK_SIZE; + // see if they have provided a different value + const char *pszValue = CSLFetchNameValue( papszParmList, "IMAGEBLOCKSIZE" ); + if( pszValue != NULL ) + nimageblockSize = (unsigned int) atol( pszValue ); + + unsigned int nattblockSize = kealib::KEA_ATT_CHUNK_SIZE; + pszValue = CSLFetchNameValue( papszParmList, "ATTBLOCKSIZE" ); + if( pszValue != NULL ) + nattblockSize = (unsigned int) atol( pszValue ); + + unsigned int nmdcElmts = kealib::KEA_MDC_NELMTS; + pszValue = CSLFetchNameValue( papszParmList, "MDC_NELMTS" ); + if( pszValue != NULL ) + nmdcElmts = (unsigned int) atol( pszValue ); + + hsize_t nrdccNElmts = kealib::KEA_RDCC_NELMTS; + pszValue = CSLFetchNameValue( papszParmList, "RDCC_NELMTS" ); + if( pszValue != NULL ) + nrdccNElmts = (unsigned int) atol( pszValue ); + + hsize_t nrdccNBytes = kealib::KEA_RDCC_NBYTES; + pszValue = CSLFetchNameValue( papszParmList, "RDCC_NBYTES" ); + if( pszValue != NULL ) + nrdccNBytes = (unsigned int) atol( pszValue ); + + double nrdccW0 = kealib::KEA_RDCC_W0; + pszValue = CSLFetchNameValue( papszParmList, "RDCC_W0" ); + if( pszValue != NULL ) + nrdccW0 = CPLAtof( pszValue ); + + hsize_t nsieveBuf = kealib::KEA_SIEVE_BUF; + pszValue = CSLFetchNameValue( papszParmList, "SIEVE_BUF" ); + if( pszValue != NULL ) + nsieveBuf = (unsigned int) atol( pszValue ); + + hsize_t nmetaBlockSize = kealib::KEA_META_BLOCKSIZE; + pszValue = CSLFetchNameValue( papszParmList, "META_BLOCKSIZE" ); + if( pszValue != NULL ) + nmetaBlockSize = (unsigned int) atol( pszValue ); + + unsigned int ndeflate = kealib::KEA_DEFLATE; + pszValue = CSLFetchNameValue( papszParmList, "DEFLATE" ); + if( pszValue != NULL ) + ndeflate = (unsigned int) atol( pszValue ); + + kealib::KEADataType keaDataType = GDAL_to_KEA_Type( eType ); + if( nBands > 0 && keaDataType == kealib::kea_undefined ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Data type %s not supported in KEA", + GDALGetDataTypeName(eType) ); + return NULL; + } + + try + { + // now create it + H5::H5File *keaImgH5File = kealib::KEAImageIO::createKEAImage( pszFilename, + keaDataType, + nXSize, nYSize, nBands, + NULL, NULL, nimageblockSize, + nattblockSize, nmdcElmts, nrdccNElmts, + nrdccNBytes, nrdccW0, nsieveBuf, + nmetaBlockSize, ndeflate ); + return keaImgH5File; + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed. Error: %s\n", + pszFilename, e.what() ); + return NULL; + } +} + +// static function- pointer set in driver +GDALDataset *KEADataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszParmList ) +{ + H5::H5File *keaImgH5File = CreateLL( pszFilename, nXSize, nYSize, nBands, + eType, papszParmList ); + if( keaImgH5File == NULL ) + return NULL; + + bool bThematic = CSLTestBoolean(CSLFetchNameValueDef( papszParmList, "THEMATIC", "FALSE" )); + + try + { + // create our dataset object + KEADataset *pDataset = new KEADataset( keaImgH5File, GA_Update ); + + pDataset->SetDescription( pszFilename ); + + // set all to thematic if asked + if( bThematic ) + { + for( int nCount = 0; nCount < nBands; nCount++ ) + { + GDALRasterBand *pBand = pDataset->GetRasterBand(nCount+1); + pBand->SetMetadataItem("LAYER_TYPE", "thematic"); + } + } + + return pDataset; + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed. Error: %s\n", + pszFilename, e.what() ); + return NULL; + } +} + +GDALDataset *KEADataset::CreateCopy( const char * pszFilename, GDALDataset *pSrcDs, + CPL_UNUSED int bStrict, char ** papszParmList, + GDALProgressFunc pfnProgress, void *pProgressData ) +{ + // get the data out of the input dataset + int nXSize = pSrcDs->GetRasterXSize(); + int nYSize = pSrcDs->GetRasterYSize(); + int nBands = pSrcDs->GetRasterCount(); + + GDALDataType eType = (nBands == 0) ? GDT_Unknown : pSrcDs->GetRasterBand(1)->GetRasterDataType(); + H5::H5File *keaImgH5File = CreateLL( pszFilename, nXSize, nYSize, nBands, + eType, papszParmList ); + if( keaImgH5File == NULL ) + return NULL; + + bool bThematic = CSLTestBoolean(CSLFetchNameValueDef( papszParmList, "THEMATIC", "FALSE" )); + + try + { + // create the imageio + kealib::KEAImageIO *pImageIO = new kealib::KEAImageIO(); + + // open the file + pImageIO->openKEAImageHeader( keaImgH5File ); + + // copy file + if( !KEACopyFile( pSrcDs, pImageIO, pfnProgress, pProgressData) ) + { + delete pImageIO; + return NULL; + } + + // close it + try + { + pImageIO->close(); + } + catch (kealib::KEAIOException &e) + { + } + delete pImageIO; + + // now open it again - because the constructor loads all the info + // in we need to copy the data first.... + keaImgH5File = kealib::KEAImageIO::openKeaH5RW( pszFilename ); + + // and wrap it in a dataset + KEADataset *pDataset = new KEADataset( keaImgH5File, GA_Update ); + pDataset->SetDescription( pszFilename ); + + // set all to thematic if asked - overrides whatever set by CopyFile + if( bThematic ) + { + for( int nCount = 0; nCount < nBands; nCount++ ) + { + GDALRasterBand *pBand = pDataset->GetRasterBand(nCount+1); + pBand->SetMetadataItem("LAYER_TYPE", "thematic"); + } + } + + for( int nCount = 0; nCount < nBands; nCount++ ) + { + pDataset->GetRasterBand(nCount+1)->SetColorInterpretation( + pSrcDs->GetRasterBand(nCount+1)->GetColorInterpretation()); + } + + // KEA has no concept of per-dataset mask band for now. + for( int nCount = 0; nCount < nBands; nCount++ ) + { + if( pSrcDs->GetRasterBand(nCount+1)->GetMaskFlags() == 0 ) // Per-band mask + { + pDataset->GetRasterBand(nCount+1)->CreateMaskBand(0); + GDALRasterBandCopyWholeRaster( + (GDALRasterBandH)pSrcDs->GetRasterBand(nCount+1)->GetMaskBand(), + (GDALRasterBandH)pDataset->GetRasterBand(nCount+1)->GetMaskBand(), + NULL, NULL, NULL); + } + } + + return pDataset; + } + catch (kealib::KEAException &e) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed. Error: %s\n", + pszFilename, e.what() ); + return NULL; + } + +} + +// constructor +KEADataset::KEADataset( H5::H5File *keaImgH5File, GDALAccess eAccess ) +{ + try + { + // create the image IO and initilize the refcount + m_pImageIO = new kealib::KEAImageIO(); + m_pnRefcount = new int(1); + + // NULL until we read them in + m_papszMetadataList = NULL; + m_pGCPs = NULL; + m_pszGCPProjection = NULL; + + // open the file + m_pImageIO->openKEAImageHeader( keaImgH5File ); + kealib::KEAImageSpatialInfo *pSpatialInfo = m_pImageIO->getSpatialInfo(); + + // get the dimensions + this->nBands = m_pImageIO->getNumOfImageBands(); + this->nRasterXSize = pSpatialInfo->xSize; + this->nRasterYSize = pSpatialInfo->ySize; + this->eAccess = eAccess; + + // create all the bands + for( int nCount = 0; nCount < nBands; nCount++ ) + { + // note GDAL uses indices starting at 1 and so does kealib + // create band object + KEARasterBand *pBand = new KEARasterBand( this, nCount + 1, eAccess, m_pImageIO, m_pnRefcount ); + // read in overviews + pBand->readExistingOverviews(); + // set the band into this dataset + this->SetBand( nCount + 1, pBand ); + } + + // read in the metadata + this->UpdateMetadataList(); + } + catch (kealib::KEAIOException &e) + { + // ignore? + CPLError( CE_Warning, CPLE_AppDefined, + "Caught exception in KEADataset constructor %s", e.what() ); + } +} + +KEADataset::~KEADataset() +{ + // destroy the metadata + CSLDestroy(m_papszMetadataList); + // decrement the refcount and delete if needed + (*m_pnRefcount)--; + if( *m_pnRefcount == 0 ) + { + try + { + m_pImageIO->close(); + } + catch (kealib::KEAIOException &e) + { + } + delete m_pImageIO; + delete m_pnRefcount; + } + this->DestroyGCPs(); + CPLFree( m_pszGCPProjection ); +} + +// read in the metadata into our CSLStringList +void KEADataset::UpdateMetadataList() +{ + std::vector< std::pair > odata; + // get all the metadata + odata = this->m_pImageIO->getImageMetaData(); + for(std::vector< std::pair >::iterator iterMetaData = odata.begin(); iterMetaData != odata.end(); ++iterMetaData) + { + m_papszMetadataList = CSLSetNameValue(m_papszMetadataList, iterMetaData->first.c_str(), iterMetaData->second.c_str()); + } +} + +// read in the geotransform +CPLErr KEADataset::GetGeoTransform( double * padfTransform ) +{ + try + { + kealib::KEAImageSpatialInfo *pSpatialInfo = m_pImageIO->getSpatialInfo(); + // GDAL uses an array format + padfTransform[0] = pSpatialInfo->tlX; + padfTransform[1] = pSpatialInfo->xRes; + padfTransform[2] = pSpatialInfo->xRot; + padfTransform[3] = pSpatialInfo->tlY; + padfTransform[4] = pSpatialInfo->yRot; + padfTransform[5] = pSpatialInfo->yRes; + + return CE_None; + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to read geotransform: %s", e.what() ); + return CE_Failure; + } +} + +// read in the projection ref +const char *KEADataset::GetProjectionRef() +{ + try + { + kealib::KEAImageSpatialInfo *pSpatialInfo = m_pImageIO->getSpatialInfo(); + // should be safe since pSpatialInfo should be around a while... + return pSpatialInfo->wktString.c_str(); + } + catch (kealib::KEAIOException &e) + { + return NULL; + } +} + +// set the geotransform +CPLErr KEADataset::SetGeoTransform (double *padfTransform ) +{ + try + { + // get the spatial info and update it + kealib::KEAImageSpatialInfo *pSpatialInfo = m_pImageIO->getSpatialInfo(); + // convert back from GDAL's array format + pSpatialInfo->tlX = padfTransform[0]; + pSpatialInfo->xRes = padfTransform[1]; + pSpatialInfo->xRot = padfTransform[2]; + pSpatialInfo->tlY = padfTransform[3]; + pSpatialInfo->yRot = padfTransform[4]; + pSpatialInfo->yRes = padfTransform[5]; + + m_pImageIO->setSpatialInfo( pSpatialInfo ); + return CE_None; + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to write geotransform: %s", e.what() ); + return CE_Failure; + } +} + +// set the projection +CPLErr KEADataset::SetProjection( const char *pszWKT ) +{ + try + { + // get the spatial info and update it + kealib::KEAImageSpatialInfo *pSpatialInfo = m_pImageIO->getSpatialInfo(); + + pSpatialInfo->wktString = pszWKT; + + m_pImageIO->setSpatialInfo( pSpatialInfo ); + return CE_None; + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to write projection: %s", e.what() ); + return CE_Failure; + } +} + +// Thought this might be handy to pass back to the application +void * KEADataset::GetInternalHandle(const char *) +{ + return m_pImageIO; +} + +// this is called by GDALDataset::BuildOverviews. we implement this function to support +// building of overviews +CPLErr KEADataset::IBuildOverviews(const char *pszResampling, int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, GDALProgressFunc pfnProgress, + void *pProgressData) +{ + // go through the list of bands that have been passed in + int nCurrentBand, nOK = 1; + for( int nBandCount = 0; (nBandCount < nListBands) && nOK; nBandCount++ ) + { + // get the band number + nCurrentBand = panBandList[nBandCount]; + // get the band + KEARasterBand *pBand = (KEARasterBand*)this->GetRasterBand(nCurrentBand); + // create the overview object + pBand->CreateOverviews( nOverviews, panOverviewList ); + + // get GDAL to do the hard work. It will calculate the overviews and write them + // back into the objects + if( GDALRegenerateOverviews( (GDALRasterBandH)pBand, nOverviews, (GDALRasterBandH*)pBand->GetOverviewList(), + pszResampling, pfnProgress, pProgressData ) != CE_None ) + { + nOK = 0; + } + } + if( !nOK ) + { + return CE_Failure; + } + else + { + return CE_None; + } +} + +// set a single metadata item +CPLErr KEADataset::SetMetadataItem(const char *pszName, const char *pszValue, const char *pszDomain) +{ + // only deal with 'default' domain - no geolocation etc + if( ( pszDomain != NULL ) && ( *pszDomain != '\0' ) ) + return CE_Failure; + + try + { + this->m_pImageIO->setImageMetaData(pszName, pszValue ); + // CSLSetNameValue will update if already there + m_papszMetadataList = CSLSetNameValue( m_papszMetadataList, pszName, pszValue ); + return CE_None; + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to write metadata: %s", e.what() ); + return CE_Failure; + } +} + +// get a single metadata item +const char *KEADataset::GetMetadataItem (const char *pszName, const char *pszDomain) +{ + // only deal with 'default' domain - no geolocation etc + if( ( pszDomain != NULL ) && ( *pszDomain != '\0' ) ) + return NULL; + // string returned from CSLFetchNameValue should be persistant + return CSLFetchNameValue(m_papszMetadataList, pszName); +} + +// get the whole metadata as CSLStringList +char **KEADataset::GetMetadata(const char *pszDomain) +{ + // only deal with 'default' domain - no geolocation etc + if( ( pszDomain != NULL ) && ( *pszDomain != '\0' ) ) + return NULL; + // this is what we store it as anyway + return m_papszMetadataList; +} + +// set the whole metadata as a CSLStringList +CPLErr KEADataset::SetMetadata(char **papszMetadata, const char *pszDomain) +{ + // only deal with 'default' domain - no geolocation etc + if( ( pszDomain != NULL ) && ( *pszDomain != '\0' ) ) + return CE_Failure; + + int nIndex = 0; + char *pszName; + const char *pszValue; + try + { + // go through each item + while( papszMetadata[nIndex] != NULL ) + { + // get the value/name + pszName = NULL; + pszValue = CPLParseNameValue( papszMetadata[nIndex], &pszName ); + if( pszValue == NULL ) + pszValue = ""; + if( pszName != NULL ) + { + // set it with imageio + this->m_pImageIO->setImageMetaData(pszName, pszValue ); + CPLFree(pszName); + } + nIndex++; + } + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to write metadata: %s", e.what() ); + return CE_Failure; + } + + // destroy our one and replace it + CSLDestroy(m_papszMetadataList); + m_papszMetadataList = CSLDuplicate(papszMetadata); + return CE_None; +} + +CPLErr KEADataset::AddBand(GDALDataType eType, char **papszOptions) +{ + // process any creation options in papszOptions + unsigned int nimageBlockSize = kealib::KEA_IMAGE_CHUNK_SIZE; + unsigned int nattBlockSize = kealib::KEA_ATT_CHUNK_SIZE; + unsigned int ndeflate = kealib::KEA_DEFLATE; + if (papszOptions != NULL) { + const char *pszValue = CSLFetchNameValue(papszOptions,"IMAGEBLOCKSIZE"); + if ( pszValue != NULL ) { + nimageBlockSize = atol(pszValue); + } + + pszValue = CSLFetchNameValue(papszOptions, "ATTBLOCKSIZE"); + if (pszValue != NULL) { + nattBlockSize = atol(pszValue); + } + + pszValue = CSLFetchNameValue(papszOptions, "DEFLATE"); + if (pszValue != NULL) { + ndeflate = atol(pszValue); + } + } + + kealib::KEADataType keaDataType = GDAL_to_KEA_Type( eType ); + if( keaDataType == kealib::kea_undefined ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Data type %s not supported in KEA", + GDALGetDataTypeName(eType) ); + return CE_Failure; + } + + try { + m_pImageIO->addImageBand(keaDataType, "", nimageBlockSize, + nattBlockSize, ndeflate); + } catch (kealib::KEAIOException &e) { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create band: %s", e.what() ); + return CE_Failure; + } + + // create a new band and add it to the dataset + // note GDAL uses indices starting at 1 and so does kealib + KEARasterBand *pBand = new KEARasterBand(this, this->nBands+1, this->eAccess, + m_pImageIO, m_pnRefcount); + this->SetBand(this->nBands+1, pBand); + + return CE_None; +} + + +int KEADataset::GetGCPCount() +{ + try + { + return m_pImageIO->getGCPCount(); + } + catch (kealib::KEAIOException &e) + { + return 0; + } + +} + +const char* KEADataset::GetGCPProjection() +{ + if( m_pszGCPProjection == NULL ) + { + try + { + std::string sProj = m_pImageIO->getGCPProjection(); + m_pszGCPProjection = CPLStrdup( sProj.c_str() ); + } + catch (kealib::KEAIOException &e) + { + return NULL; + } + } + return m_pszGCPProjection; +} + +const GDAL_GCP* KEADataset::GetGCPs() +{ + if( m_pGCPs == NULL ) + { + // convert to GDAL data structures + try + { + unsigned int nCount = m_pImageIO->getGCPCount(); + std::vector *pKEAGCPs = m_pImageIO->getGCPs(); + + m_pGCPs = (GDAL_GCP*)CPLCalloc(nCount, sizeof(GDAL_GCP)); + for( unsigned int nIndex = 0; nIndex < nCount; nIndex++) + { + GDAL_GCP *pGCP = &m_pGCPs[nIndex]; + kealib::KEAImageGCP *pKEAGCP = pKEAGCPs->at(nIndex); + pGCP->pszId = CPLStrdup( pKEAGCP->pszId.c_str() ); + pGCP->pszInfo = CPLStrdup( pKEAGCP->pszInfo.c_str() ); + pGCP->dfGCPPixel = pKEAGCP->dfGCPPixel; + pGCP->dfGCPLine = pKEAGCP->dfGCPLine; + pGCP->dfGCPX = pKEAGCP->dfGCPX; + pGCP->dfGCPY = pKEAGCP->dfGCPY; + pGCP->dfGCPZ = pKEAGCP->dfGCPZ; + + delete pKEAGCP; + } + + delete pKEAGCPs; + } + catch (kealib::KEAIOException &e) + { + return NULL; + } + } + return m_pGCPs; +} + +CPLErr KEADataset::SetGCPs(int nGCPCount, const GDAL_GCP *pasGCPList, const char *pszGCPProjection) +{ + this->DestroyGCPs(); + CPLFree( m_pszGCPProjection ); + m_pszGCPProjection = NULL; + CPLErr result = CE_None; + + std::vector *pKEAGCPs = new std::vector(nGCPCount); + for( int nIndex = 0; nIndex < nGCPCount; nIndex++ ) + { + const GDAL_GCP *pGCP = &pasGCPList[nIndex]; + kealib::KEAImageGCP *pKEA = new kealib::KEAImageGCP; + pKEA->pszId = pGCP->pszId; + pKEA->pszInfo = pGCP->pszInfo; + pKEA->dfGCPPixel = pGCP->dfGCPPixel; + pKEA->dfGCPLine = pGCP->dfGCPLine; + pKEA->dfGCPX = pGCP->dfGCPX; + pKEA->dfGCPY = pGCP->dfGCPY; + pKEA->dfGCPZ = pGCP->dfGCPZ; + pKEAGCPs->at(nIndex) = pKEA; + } + try + { + m_pImageIO->setGCPs(pKEAGCPs, pszGCPProjection); + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to write GCPs: %s", e.what() ); + result = CE_Failure; + } + + for( std::vector::iterator itr = pKEAGCPs->begin(); itr != pKEAGCPs->end(); itr++) + { + kealib::KEAImageGCP *pKEA = (*itr); + delete pKEA; + } + delete pKEAGCPs; + + return result; +} + +void KEADataset::DestroyGCPs() +{ + if( m_pGCPs != NULL ) + { + // we assume this is always the same as the internal list... + int nCount = this->GetGCPCount(); + for( int nIndex = 0; nIndex < nCount; nIndex++ ) + { + GDAL_GCP *pGCP = &m_pGCPs[nIndex]; + CPLFree( pGCP->pszId ); + CPLFree( pGCP->pszInfo ); + } + CPLFree( m_pGCPs ); + m_pGCPs = NULL; + } +} diff --git a/bazaar/plugin/gdal/frmts/kea/keadataset.h b/bazaar/plugin/gdal/frmts/kea/keadataset.h new file mode 100644 index 000000000..836cfabce --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/keadataset.h @@ -0,0 +1,115 @@ +/* + * $Id: keadataset.h 29258 2015-05-28 22:08:43Z rouault $ + * keadataset.h + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef KEADATASET_H +#define KEADATASET_H + +#include "gdal_pam.h" +#if defined(USE_GCC_VISIBILITY_FLAG) && !defined(DllExport) +#define DllExport CPL_DLL +#endif +#include "libkea/KEAImageIO.h" + +// class that implements a GDAL dataset +class KEADataset : public GDALPamDataset +{ + static H5::H5File *CreateLL( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszParmList ); + +public: + // constructor/destructor + KEADataset( H5::H5File *keaImgH5File, GDALAccess eAccess ); + ~KEADataset(); + + // static methods that handle open and creation + // the driver class has pointers to these + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * poOpenInfo ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszParmList ); + static GDALDataset *CreateCopy( const char * pszFilename, GDALDataset *pSrcDs, + int bStrict, char ** papszParmList, + GDALProgressFunc pfnProgress, void *pProgressData ); + + // virtual methods for dealing with transform and projection + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef(); + + CPLErr SetGeoTransform (double *padfTransform ); + CPLErr SetProjection( const char *pszWKT ); + + // method to get a pointer to the imageio class + void *GetInternalHandle (const char *); + + // virtual methods for dealing with metadata + CPLErr SetMetadataItem (const char *pszName, const char *pszValue, const char *pszDomain=""); + const char *GetMetadataItem (const char *pszName, const char *pszDomain=""); + + char **GetMetadata(const char *pszDomain=""); + CPLErr SetMetadata(char **papszMetadata, const char *pszDomain=""); + + // virtual method for adding new image bands + CPLErr AddBand(GDALDataType eType, char **papszOptions = NULL); + + // GCPs + int GetGCPCount(); + const char* GetGCPProjection(); + const GDAL_GCP* GetGCPs(); + CPLErr SetGCPs(int nGCPCount, const GDAL_GCP *pasGCPList, const char *pszGCPProjection); + +protected: + // this method builds overviews for the specified bands. + virtual CPLErr IBuildOverviews(const char *pszResampling, int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, GDALProgressFunc pfnProgress, + void *pProgressData); + + // internal method to update m_papszMetadataList + void UpdateMetadataList(); + + void DestroyGCPs(); + +private: + // pointer to KEAImageIO class and the refcount for it + kealib::KEAImageIO *m_pImageIO; + int *m_pnRefcount; + char **m_papszMetadataList; // CSLStringList for metadata + GDAL_GCP *m_pGCPs; + char *m_pszGCPProjection; +}; + +// conversion functions +GDALDataType KEA_to_GDAL_Type( kealib::KEADataType ekeaType ); +kealib::KEADataType GDAL_to_KEA_Type( GDALDataType egdalType ); + +#endif //KEADATASET_H diff --git a/bazaar/plugin/gdal/frmts/kea/keadriver.cpp b/bazaar/plugin/gdal/frmts/kea/keadriver.cpp new file mode 100644 index 000000000..46c1f3637 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/keadriver.cpp @@ -0,0 +1,93 @@ +/* + * $Id: keadriver.cpp 28041 2014-12-01 11:33:47Z rouault $ + * keadriver.cpp + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "keadataset.h" + +CPL_C_START +void CPL_DLL GDALRegister_KEA(void); +CPL_C_END + +// method to register this driver +void GDALRegister_KEA() +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("KEA")) + return; + + if( GDALGetDriverByName( "KEA" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "KEA" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "KEA Image Format (.kea)" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "kea" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_kea.html" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 Float32 Float64" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +CPLSPrintf("\ + \ +", + (int)kealib::KEA_IMAGE_CHUNK_SIZE, + (int)kealib::KEA_ATT_CHUNK_SIZE, + (int)kealib::KEA_MDC_NELMTS, + (int)kealib::KEA_RDCC_NELMTS, + (int)kealib::KEA_RDCC_NBYTES, + kealib::KEA_RDCC_W0, + (int)kealib::KEA_SIEVE_BUF, + (int)kealib::KEA_META_BLOCKSIZE, + kealib::KEA_DEFLATE)); + + // pointer to open function + poDriver->pfnOpen = KEADataset::Open; + // pointer to identify function + poDriver->pfnIdentify = KEADataset::Identify; + // pointer to create function + poDriver->pfnCreate = KEADataset::Create; + // pointer to create copy function + poDriver->pfnCreateCopy = KEADataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/kea/keamaskband.cpp b/bazaar/plugin/gdal/frmts/kea/keamaskband.cpp new file mode 100644 index 000000000..9f7ac2a24 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/keamaskband.cpp @@ -0,0 +1,143 @@ +/* + * $Id: keamaskband.cpp 28011 2014-11-26 13:47:09Z rouault $ + * keamaskband.cpp + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "keamaskband.h" + +// constructor +KEAMaskBand::KEAMaskBand(GDALRasterBand *pParent, + kealib::KEAImageIO *pImageIO, int *pRefCount) +{ + m_nSrcBand = pParent->GetBand(); + poDS = NULL; + nBand = 0; + + nRasterXSize = pParent->GetXSize(); + nRasterYSize = pParent->GetYSize(); + + eDataType = GDT_Byte; + pParent->GetBlockSize( &nBlockXSize, &nBlockYSize ); + eAccess = pParent->GetAccess(); + + // grab the imageio class and its refcount + this->m_pImageIO = pImageIO; + this->m_pnRefCount = pRefCount; + // increment the refcount as we now have a reference to imageio + (*this->m_pnRefCount)++; +} + +KEAMaskBand::~KEAMaskBand() +{ + // according to the docs, this is required + this->FlushCache(); + + // decrement the recount and delete if needed + (*m_pnRefCount)--; + if( *m_pnRefCount == 0 ) + { + try + { + m_pImageIO->close(); + } + catch (kealib::KEAIOException &e) + { + } + delete m_pImageIO; + delete m_pnRefCount; + } +} + +// overridden implementation - calls readImageBlock2BandMask instead +CPLErr KEAMaskBand::IReadBlock( int nBlockXOff, int nBlockYOff, void * pImage ) +{ + try + { + // GDAL deals in blocks - if we are at the end of a row + // we need to adjust the amount read so we don't go over the edge + int nxsize = this->nBlockXSize; + int nxtotalsize = this->nBlockXSize * (nBlockXOff + 1); + if( nxtotalsize > this->nRasterXSize ) + { + nxsize -= (nxtotalsize - this->nRasterXSize); + } + int nysize = this->nBlockYSize; + int nytotalsize = this->nBlockYSize * (nBlockYOff + 1); + if( nytotalsize > this->nRasterYSize ) + { + nysize -= (nytotalsize - this->nRasterYSize); + } + this->m_pImageIO->readImageBlock2BandMask( this->m_nSrcBand, + pImage, this->nBlockXSize * nBlockXOff, + this->nBlockYSize * nBlockYOff, + nxsize, nysize, this->nBlockXSize, this->nBlockYSize, + kealib::kea_8uint ); + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to read file: %s", e.what() ); + return CE_Failure; + } + return CE_None; +} + +// overridden implementation - calls writeImageBlock2BandMask instead +CPLErr KEAMaskBand::IWriteBlock( int nBlockXOff, int nBlockYOff, void * pImage ) +{ + try + { + // GDAL deals in blocks - if we are at the end of a row + // we need to adjust the amount written so we don't go over the edge + int nxsize = this->nBlockXSize; + int nxtotalsize = this->nBlockXSize * (nBlockXOff + 1); + if( nxtotalsize > this->nRasterXSize ) + { + nxsize -= (nxtotalsize - this->nRasterXSize); + } + int nysize = this->nBlockYSize; + int nytotalsize = this->nBlockYSize * (nBlockYOff + 1); + if( nytotalsize > this->nRasterYSize ) + { + nysize -= (nytotalsize - this->nRasterYSize); + } + + this->m_pImageIO-> writeImageBlock2BandMask( this->m_nSrcBand, + pImage, this->nBlockXSize * nBlockXOff, + this->nBlockYSize * nBlockYOff, + nxsize, nysize, this->nBlockXSize, this->nBlockYSize, + kealib::kea_8uint ); + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to write file: %s", e.what() ); + return CE_Failure; + } + return CE_None; +} diff --git a/bazaar/plugin/gdal/frmts/kea/keamaskband.h b/bazaar/plugin/gdal/frmts/kea/keamaskband.h new file mode 100644 index 000000000..06ce9112e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/keamaskband.h @@ -0,0 +1,56 @@ +/* + * $Id: keamaskband.h 29258 2015-05-28 22:08:43Z rouault $ + * keamaskband.h + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef KEAMASKBAND_H +#define KEAMASKBAND_H + +#include "gdal_priv.h" +#if defined(USE_GCC_VISIBILITY_FLAG) && !defined(DllExport) +#define DllExport CPL_DLL +#endif +#include "libkea/KEAImageIO.h" + +class KEAMaskBand : public GDALRasterBand +{ + int m_nSrcBand; + kealib::KEAImageIO *m_pImageIO; // our image access pointer - refcounted + int *m_pnRefCount; // reference count of m_pImageIO +public: + KEAMaskBand(GDALRasterBand *pParent, kealib::KEAImageIO *pImageIO, int *pRefCount ); + ~KEAMaskBand(); + +protected: + // we just override these functions from GDALRasterBand + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + +}; + +#endif //KEAMASKBAND_H diff --git a/bazaar/plugin/gdal/frmts/kea/keaoverview.cpp b/bazaar/plugin/gdal/frmts/kea/keaoverview.cpp new file mode 100644 index 000000000..df6d3b022 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/keaoverview.cpp @@ -0,0 +1,131 @@ +/* + * $Id: keaoverview.cpp 28011 2014-11-26 13:47:09Z rouault $ + * keaoverview.cpp + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "keaoverview.h" + +// constructor +KEAOverview::KEAOverview(KEADataset *pDataset, int nSrcBand, GDALAccess eAccess, + kealib::KEAImageIO *pImageIO, int *pRefCount, + int nOverviewIndex, uint64_t nXSize, uint64_t nYSize) + : KEARasterBand( pDataset, nSrcBand, eAccess, pImageIO, pRefCount ) +{ + this->m_nOverviewIndex = nOverviewIndex; + // overridden from the band - not the same size as the band obviously + this->nBlockXSize = pImageIO->getOverviewBlockSize(nSrcBand, nOverviewIndex); + this->nBlockYSize = pImageIO->getOverviewBlockSize(nSrcBand, nOverviewIndex); + this->nRasterXSize = nXSize; + this->nRasterYSize = nYSize; +} + +KEAOverview::~KEAOverview() +{ + +} + +// overridden implementation - calls readFromOverview instead +CPLErr KEAOverview::IReadBlock( int nBlockXOff, int nBlockYOff, void * pImage ) +{ + try + { + // GDAL deals in blocks - if we are at the end of a row + // we need to adjust the amount read so we don't go over the edge + int nxsize = this->nBlockXSize; + int nxtotalsize = this->nBlockXSize * (nBlockXOff + 1); + if( nxtotalsize > this->nRasterXSize ) + { + nxsize -= (nxtotalsize - this->nRasterXSize); + } + int nysize = this->nBlockYSize; + int nytotalsize = this->nBlockYSize * (nBlockYOff + 1); + if( nytotalsize > this->nRasterYSize ) + { + nysize -= (nytotalsize - this->nRasterYSize); + } + this->m_pImageIO->readFromOverview( this->nBand, this->m_nOverviewIndex, + pImage, this->nBlockXSize * nBlockXOff, + this->nBlockYSize * nBlockYOff, + nxsize, nysize, this->nBlockXSize, this->nBlockYSize, + this->m_eKEADataType ); + return CE_None; + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to read file: %s", e.what() ); + return CE_Failure; + } +} + +// overridden implementation - calls writeToOverview instead +CPLErr KEAOverview::IWriteBlock( int nBlockXOff, int nBlockYOff, void * pImage ) +{ + try + { + // GDAL deals in blocks - if we are at the end of a row + // we need to adjust the amount written so we don't go over the edge + int nxsize = this->nBlockXSize; + int nxtotalsize = this->nBlockXSize * (nBlockXOff + 1); + if( nxtotalsize > this->nRasterXSize ) + { + nxsize -= (nxtotalsize - this->nRasterXSize); + } + int nysize = this->nBlockYSize; + int nytotalsize = this->nBlockYSize * (nBlockYOff + 1); + if( nytotalsize > this->nRasterYSize ) + { + nysize -= (nytotalsize - this->nRasterYSize); + } + + this->m_pImageIO->writeToOverview( this->nBand, this->m_nOverviewIndex, + pImage, this->nBlockXSize * nBlockXOff, + this->nBlockYSize * nBlockYOff, + nxsize, nysize, this->nBlockXSize, this->nBlockYSize, + this->m_eKEADataType ); + return CE_None; + } + catch (kealib::KEAIOException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to write file: %s", e.what() ); + return CE_Failure; + } +} + +GDALRasterAttributeTable *KEAOverview::GetDefaultRAT() +{ + // KEARasterBand implements this, but we don't want to + return NULL; +} + +CPLErr KEAOverview::SetDefaultRAT(CPL_UNUSED const GDALRasterAttributeTable *poRAT) +{ + // KEARasterBand implements this, but we don't want to + return CE_Failure; +} diff --git a/bazaar/plugin/gdal/frmts/kea/keaoverview.h b/bazaar/plugin/gdal/frmts/kea/keaoverview.h new file mode 100644 index 000000000..b3f8c2ddd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/keaoverview.h @@ -0,0 +1,65 @@ +/* + * $Id: keaoverview.h 29258 2015-05-28 22:08:43Z rouault $ + * keaoverview.h + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef KEAOVERVIEW_H +#define KEAOVERVIEW_H + +#include "cpl_port.h" +#if defined(USE_GCC_VISIBILITY_FLAG) && !defined(DllExport) +#define DllExport CPL_DLL +#endif +#include "keaband.h" + +// overview class. Derives from our band class +// and just overrited and read/write block functions +class KEAOverview : public KEARasterBand +{ + int m_nOverviewIndex; // the index of this overview +public: + KEAOverview(KEADataset *pDataset, int nSrcBand, GDALAccess eAccess, + kealib::KEAImageIO *pImageIO, int *pRefCount, + int nOverviewIndex, uint64_t nXSize, uint64_t nYSize ); + ~KEAOverview(); + + // virtual methods for RATs - not implemented for overviews + GDALRasterAttributeTable *GetDefaultRAT(); + + CPLErr SetDefaultRAT(const GDALRasterAttributeTable *poRAT); + + // note that Color Table stuff implemented in base class + // so could be some duplication if overview asked for color table + +protected: + // we just override these functions from KEARasterBand + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); +}; + +#endif //KEAOVERVIEW_H diff --git a/bazaar/plugin/gdal/frmts/kea/kearat.cpp b/bazaar/plugin/gdal/frmts/kea/kearat.cpp new file mode 100644 index 000000000..886e40621 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/kearat.cpp @@ -0,0 +1,940 @@ +/* + * $Id: kearat.cpp 28014 2014-11-26 14:43:45Z rouault $ + * kearat.cpp + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include "kearat.h" + +KEARasterAttributeTable::KEARasterAttributeTable(kealib::KEAAttributeTable *poKEATable) +{ + for( size_t nColumnIndex = 0; nColumnIndex < poKEATable->getMaxGlobalColIdx(); nColumnIndex++ ) + { + kealib::KEAATTField sKEAField; + try + { + sKEAField = poKEATable->getField(nColumnIndex); + } + catch(kealib::KEAATTException &e) + { + // pKEATable->getField raised exception because we have a missing column + continue; + } + m_aoFields.push_back(sKEAField); + } + m_poKEATable = poKEATable; +} + +KEARasterAttributeTable::~KEARasterAttributeTable() +{ + // can't just delete thanks to Windows + kealib::KEAAttributeTable::destroyAttributeTable(m_poKEATable); +} + +GDALDefaultRasterAttributeTable *KEARasterAttributeTable::Clone() const +{ + if( ( GetRowCount() * GetColumnCount() ) > RAT_MAX_ELEM_FOR_CLONE ) + return NULL; + + GDALDefaultRasterAttributeTable *poRAT = new GDALDefaultRasterAttributeTable(); + + for( int iCol = 0; iCol < (int)m_aoFields.size(); iCol++) + { + CPLString sName = m_aoFields[iCol].name; + CPLString sUsage = m_aoFields[iCol].usage; + GDALRATFieldUsage eGDALUsage; + if( sUsage == "PixelCount" ) + eGDALUsage = GFU_PixelCount; + else if( sUsage == "Name" ) + eGDALUsage = GFU_Name; + else if( sUsage == "Red" ) + eGDALUsage = GFU_Red; + else if( sUsage == "Green" ) + eGDALUsage = GFU_Green; + else if( sUsage == "Blue" ) + eGDALUsage = GFU_Blue; + else if( sUsage == "Alpha" ) + eGDALUsage = GFU_Alpha; + else + { + // don't recognise any other special names - generic column + eGDALUsage = GFU_Generic; + } + + GDALRATFieldType eGDALType; + switch( m_aoFields[iCol].dataType ) + { + case kealib::kea_att_bool: + case kealib::kea_att_int: + eGDALType = GFT_Integer; + break; + case kealib::kea_att_float: + eGDALType = GFT_Real; + break; + case kealib::kea_att_string: + eGDALType = GFT_String; + break; + default: + eGDALType = GFT_Integer; + break; + } + poRAT->CreateColumn(sName, eGDALType, eGDALUsage); + poRAT->SetRowCount(m_poKEATable->getSize()); + + if( m_poKEATable->getSize() == 0 ) + continue; + + if( eGDALType == GFT_Integer ) + { + int *panColData = (int*)VSIMalloc2(sizeof(int), m_poKEATable->getSize()); + if( panColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::Clone"); + delete poRAT; + return NULL; + } + + if( (const_cast(this))-> + ValuesIO(GF_Read, iCol, 0, m_poKEATable->getSize(), panColData ) != CE_None ) + { + CPLFree(panColData); + delete poRAT; + return NULL; + } + + for( int iRow = 0; iRow < (int)m_poKEATable->getSize(); iRow++ ) + { + poRAT->SetValue(iRow, iCol, panColData[iRow]); + } + CPLFree(panColData); + } + if( eGDALType == GFT_Real ) + { + double *padfColData = (double*)VSIMalloc2(sizeof(double), m_poKEATable->getSize()); + if( padfColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::Clone"); + delete poRAT; + return NULL; + } + if( (const_cast(this))-> + ValuesIO(GF_Read, iCol, 0, m_poKEATable->getSize(), padfColData ) != CE_None ) + { + CPLFree(padfColData); + delete poRAT; + return NULL; + } + + for( int iRow = 0; iRow < (int)m_poKEATable->getSize(); iRow++ ) + { + poRAT->SetValue(iRow, iCol, padfColData[iRow]); + } + CPLFree(padfColData); + } + if( eGDALType == GFT_String ) + { + char **papszColData = (char**)VSIMalloc2(sizeof(char*), m_poKEATable->getSize()); + if( papszColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::Clone"); + delete poRAT; + return NULL; + } + + if( (const_cast(this))-> + ValuesIO(GF_Read, iCol, 0, m_poKEATable->getSize(), papszColData ) != CE_None ) + { + CPLFree(papszColData); + delete poRAT; + return NULL; + } + + for( int iRow = 0; iRow < (int)m_poKEATable->getSize(); iRow++ ) + { + poRAT->SetValue(iRow, iCol, papszColData[iRow]); + CPLFree(papszColData[iRow]); + } + CPLFree(papszColData); + } + } + return poRAT; +} + + +int KEARasterAttributeTable::GetColumnCount() const +{ + return (int)m_aoFields.size(); +} + + +const char *KEARasterAttributeTable::GetNameOfCol(int nCol) const +{ + if( ( nCol < 0 ) || ( nCol >= (int)m_aoFields.size() ) ) + return NULL; + + return m_aoFields[nCol].name.c_str(); +} + +GDALRATFieldUsage KEARasterAttributeTable::GetUsageOfCol( int nCol ) const +{ + if( ( nCol < 0 ) || ( nCol >= (int)m_aoFields.size() ) ) + return GFU_Generic; + + GDALRATFieldUsage eGDALUsage; + std::string keausage = m_aoFields[nCol].usage; + + if( keausage == "PixelCount" ) + eGDALUsage = GFU_PixelCount; + else if( keausage == "Name" ) + eGDALUsage = GFU_Name; + else if( keausage == "Red" ) + eGDALUsage = GFU_Red; + else if( keausage == "Green" ) + eGDALUsage = GFU_Green; + else if( keausage == "Blue" ) + eGDALUsage = GFU_Blue; + else if( keausage == "Alpha" ) + eGDALUsage = GFU_Alpha; + else + { + // don't recognise any other special names - generic column + eGDALUsage = GFU_Generic; + } + + return eGDALUsage; +} + +GDALRATFieldType KEARasterAttributeTable::GetTypeOfCol( int nCol ) const +{ + if( ( nCol < 0 ) || ( nCol >= (int)m_aoFields.size() ) ) + return GFT_Integer; + + GDALRATFieldType eGDALType; + switch( m_aoFields[nCol].dataType ) + { + case kealib::kea_att_bool: + case kealib::kea_att_int: + eGDALType = GFT_Integer; + break; + case kealib::kea_att_float: + eGDALType = GFT_Real; + break; + case kealib::kea_att_string: + eGDALType = GFT_String; + break; + default: + eGDALType = GFT_Integer; + break; + } + return eGDALType; +} + + +int KEARasterAttributeTable::GetColOfUsage( GDALRATFieldUsage eUsage ) const +{ + unsigned int i; + + std::string keausage; + switch(eUsage) + { + case GFU_PixelCount: + keausage = "PixelCount"; + break; + case GFU_Name: + keausage = "Name"; + break; + case GFU_Red: + keausage = "Red"; + break; + case GFU_Green: + keausage = "Green"; + break; + case GFU_Blue: + keausage = "Blue"; + break; + case GFU_Alpha: + keausage = "Alpha"; + break; + default: + keausage = "Generic"; + break; + } + + for( i = 0; i < m_aoFields.size(); i++ ) + { + if( m_aoFields[i].usage == keausage ) + return i; + } + return -1; +} + +int KEARasterAttributeTable::GetRowCount() const +{ + return (int)m_poKEATable->getSize(); +} + +const char *KEARasterAttributeTable::GetValueAsString( int iRow, int iField ) const +{ + // Get ValuesIO do do the work + char *apszStrList[1]; + if( (const_cast(this))-> + ValuesIO(GF_Read, iField, iRow, 1, apszStrList ) != CPLE_None ) + { + return ""; + } + + const_cast(this)->osWorkingResult = apszStrList[0]; + CPLFree(apszStrList[0]); + + return osWorkingResult; +} + +int KEARasterAttributeTable::GetValueAsInt( int iRow, int iField ) const +{ + // Get ValuesIO do do the work + int nValue; + if( (const_cast(this))-> + ValuesIO(GF_Read, iField, iRow, 1, &nValue ) != CE_None ) + { + return 0; + } + + return nValue; +} + +double KEARasterAttributeTable::GetValueAsDouble( int iRow, int iField ) const +{ + // Get ValuesIO do do the work + double dfValue; + if( (const_cast(this))-> + ValuesIO(GF_Read, iField, iRow, 1, &dfValue ) != CE_None ) + { + return 0; + } + + return dfValue; +} + +void KEARasterAttributeTable::SetValue( int iRow, int iField, const char *pszValue ) +{ + // Get ValuesIO do do the work + ValuesIO(GF_Write, iField, iRow, 1, const_cast(&pszValue) ); +} + +void KEARasterAttributeTable::SetValue( int iRow, int iField, double dfValue) +{ + // Get ValuesIO do do the work + ValuesIO(GF_Write, iField, iRow, 1, &dfValue ); +} + +void KEARasterAttributeTable::SetValue( int iRow, int iField, int nValue ) +{ + // Get ValuesIO do do the work + ValuesIO(GF_Write, iField, iRow, 1, &nValue ); +} + +CPLErr KEARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, double *pdfData) +{ + /*if( ( eRWFlag == GF_Write ) && ( this->eAccess == GA_ReadOnly ) ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Dataset not open in update mode"); + return CE_Failure; + }*/ + + if( iField < 0 || iField >= (int) m_aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return CE_Failure; + } + + if( iStartRow < 0 || (iStartRow+iLength) > (int)m_poKEATable->getSize() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iStartRow (%d) + iLength(%d) out of range.", iStartRow, iLength ); + + return CE_Failure; + } + + switch( m_aoFields[iField].dataType ) + { + case kealib::kea_att_bool: + case kealib::kea_att_int: + { + // allocate space for ints + int *panColData = (int*)VSIMalloc2(iLength, sizeof(int) ); + if( panColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // copy the application supplied doubles to ints + for( int i = 0; i < iLength; i++ ) + panColData[i] = pdfData[i]; + } + + // do the ValuesIO as ints + CPLErr eVal = ValuesIO(eRWFlag, iField, iStartRow, iLength, panColData ); + if( eVal != CE_None ) + { + CPLFree(panColData); + return eVal; + } + + if( eRWFlag == GF_Read ) + { + // copy them back to doubles + for( int i = 0; i < iLength; i++ ) + pdfData[i] = panColData[i]; + } + + CPLFree(panColData); + } + break; + case kealib::kea_att_float: + { + try + { + if( eRWFlag == GF_Read ) + m_poKEATable->getFloatFields(iStartRow, iLength, m_aoFields[iField].idx, pdfData); + else + m_poKEATable->setFloatFields(iStartRow, iLength, m_aoFields[iField].idx, pdfData); + } + catch(kealib::KEAException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to read/write attribute table: %s", e.what() ); + return CE_Failure; + } + } + break; + case kealib::kea_att_string: + { + // allocate space for string pointers + char **papszColData = (char**)VSIMalloc2(iLength, sizeof(char*)); + if( papszColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // copy the application supplied doubles to strings + for( int i = 0; i < iLength; i++ ) + { + osWorkingResult.Printf( "%.16g", pdfData[i] ); + papszColData[i] = CPLStrdup(osWorkingResult); + } + } + + // do the ValuesIO as strings + CPLErr eVal = ValuesIO(eRWFlag, iField, iStartRow, iLength, papszColData ); + if( eVal != CE_None ) + { + if( eRWFlag == GF_Write ) + { + for( int i = 0; i < iLength; i++ ) + CPLFree(papszColData[i]); + } + CPLFree(papszColData); + return eVal; + } + + if( eRWFlag == GF_Read ) + { + // copy them back to doubles + for( int i = 0; i < iLength; i++ ) + pdfData[i] = CPLAtof(papszColData[i]); + } + + // either we allocated them for write, or they were allocated + // by ValuesIO on read + for( int i = 0; i < iLength; i++ ) + CPLFree(papszColData[i]); + + CPLFree(papszColData); + } + break; + default: + break; + } + return CE_None; +} + +CPLErr KEARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, int *pnData) +{ + /*if( ( eRWFlag == GF_Write ) && ( this->eAccess == GA_ReadOnly ) ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Dataset not open in update mode"); + return CE_Failure; + }*/ + + if( iField < 0 || iField >= (int) m_aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return CE_Failure; + } + + if( iStartRow < 0 || (iStartRow+iLength) > (int)m_poKEATable->getSize() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iStartRow (%d) + iLength(%d) out of range.", iStartRow, iLength ); + + return CE_Failure; + } + + switch( m_aoFields[iField].dataType ) + { + case kealib::kea_att_bool: + { + // need to convert to/from bools + bool *panColData = (bool*)VSIMalloc2(iLength, sizeof(bool) ); + if( panColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // copy the application supplied ints to bools + for( int i = 0; i < iLength; i++ ) + { + panColData[i] = (pnData[i] != 0); + } + } + + try + { + if( eRWFlag == GF_Read ) + m_poKEATable->getBoolFields(iStartRow, iLength, m_aoFields[iField].idx, panColData); + else + m_poKEATable->setBoolFields(iStartRow, iLength, m_aoFields[iField].idx, panColData); + } + catch(kealib::KEAException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to read/write attribute table: %s", e.what() ); + return CE_Failure; + } + + if( eRWFlag == GF_Read ) + { + // copy them back to ints + for( int i = 0; i < iLength; i++ ) + pnData[i] = panColData[i]? 1 : 0; + } + CPLFree(panColData); + } + break; + case kealib::kea_att_int: + { + // need to convert to/from int64_t + int64_t *panColData = (int64_t*)VSIMalloc2(iLength, sizeof(int64_t) ); + if( panColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // copy the application supplied ints to int64t + for( int i = 0; i < iLength; i++ ) + panColData[i] = pnData[i]; + } + + try + { + if( eRWFlag == GF_Read ) + m_poKEATable->getIntFields(iStartRow, iLength, m_aoFields[iField].idx, panColData); + else + m_poKEATable->setIntFields(iStartRow, iLength, m_aoFields[iField].idx, panColData); + } + catch(kealib::KEAException &e) + { + fprintf(stderr,"Failed to read/write attribute table: %s %d %d %ld\n", e.what(), iStartRow, iLength, m_poKEATable->getSize() ); + CPLError( CE_Failure, CPLE_AppDefined, "Failed to read/write attribute table: %s", e.what() ); + return CE_Failure; + } + + if( eRWFlag == GF_Read ) + { + // copy them back to ints + for( int i = 0; i < iLength; i++ ) + pnData[i] = panColData[i]; + } + CPLFree(panColData); + } + break; + case kealib::kea_att_float: + { + // allocate space for doubles + double *padfColData = (double*)VSIMalloc2(iLength, sizeof(double) ); + if( padfColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // copy the application supplied ints to doubles + for( int i = 0; i < iLength; i++ ) + padfColData[i] = pnData[i]; + } + + // do the ValuesIO as doubles + CPLErr eVal = ValuesIO(eRWFlag, iField, iStartRow, iLength, padfColData ); + if( eVal != CE_None ) + { + CPLFree(padfColData); + return eVal; + } + + if( eRWFlag == GF_Read ) + { + // copy them back to ints + for( int i = 0; i < iLength; i++ ) + pnData[i] = padfColData[i]; + } + + CPLFree(padfColData); + } + break; + case kealib::kea_att_string: + { + // allocate space for string pointers + char **papszColData = (char**)VSIMalloc2(iLength, sizeof(char*)); + if( papszColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // copy the application supplied ints to strings + for( int i = 0; i < iLength; i++ ) + { + osWorkingResult.Printf( "%d", pnData[i] ); + papszColData[i] = CPLStrdup(osWorkingResult); + } + } + + // do the ValuesIO as strings + CPLErr eVal = ValuesIO(eRWFlag, iField, iStartRow, iLength, papszColData ); + if( eVal != CE_None ) + { + if( eRWFlag == GF_Write ) + { + for( int i = 0; i < iLength; i++ ) + CPLFree(papszColData[i]); + } + CPLFree(papszColData); + return eVal; + } + + if( eRWFlag == GF_Read ) + { + // copy them back to ints + for( int i = 0; i < iLength; i++ ) + pnData[i] = atol(papszColData[i]); + } + + // either we allocated them for write, or they were allocated + // by ValuesIO on read + for( int i = 0; i < iLength; i++ ) + CPLFree(papszColData[i]); + + CPLFree(papszColData); + } + break; + default: + break; + } + return CE_None; +} + +CPLErr KEARasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, char **papszStrList) +{ + /*if( ( eRWFlag == GF_Write ) && ( this->eAccess == GA_ReadOnly ) ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Dataset not open in update mode"); + return CE_Failure; + }*/ + + if( iField < 0 || iField >= (int) m_aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return CE_Failure; + } + + if( iStartRow < 0 || (iStartRow+iLength) > (int)m_poKEATable->getSize() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iStartRow (%d) + iLength(%d) out of range.", iStartRow, iLength ); + + return CE_Failure; + } + + switch( m_aoFields[iField].dataType ) + { + case kealib::kea_att_bool: + case kealib::kea_att_int: + { + // allocate space for ints + int *panColData = (int*)VSIMalloc2(iLength, sizeof(int) ); + if( panColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + if( eRWFlag == GF_Write ) + { + // convert user supplied strings to ints + for( int i = 0; i < iLength; i++ ) + panColData[i] = atol(papszStrList[i]); + } + + // call values IO to read/write ints + CPLErr eVal = ValuesIO(eRWFlag, iField, iStartRow, iLength, panColData); + if( eVal != CE_None ) + { + CPLFree(panColData); + return eVal; + } + + + if( eRWFlag == GF_Read ) + { + // convert ints back to strings + for( int i = 0; i < iLength; i++ ) + { + osWorkingResult.Printf( "%d", panColData[i]); + papszStrList[i] = CPLStrdup(osWorkingResult); + } + } + CPLFree(panColData); + } + break; + case kealib::kea_att_float: + { + // allocate space for doubles + double *padfColData = (double*)VSIMalloc2(iLength, sizeof(double) ); + if( padfColData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Memory Allocation failed in KEARasterAttributeTable::ValuesIO"); + return CE_Failure; + } + + if( eRWFlag == GF_Write ) + { + // convert user supplied strings to doubles + for( int i = 0; i < iLength; i++ ) + padfColData[i] = CPLAtof(papszStrList[i]); + } + + // call value IO to read/write doubles + CPLErr eVal = ValuesIO(eRWFlag, iField, iStartRow, iLength, padfColData); + if( eVal != CE_None ) + { + CPLFree(padfColData); + return eVal; + } + + if( eRWFlag == GF_Read ) + { + // convert doubles back to strings + for( int i = 0; i < iLength; i++ ) + { + osWorkingResult.Printf( "%.16g", padfColData[i]); + papszStrList[i] = CPLStrdup(osWorkingResult); + } + } + CPLFree(padfColData); + + } + break; + case kealib::kea_att_string: + { + try + { + if( eRWFlag == GF_Read ) + { + std::vector aStrings; + m_poKEATable->getStringFields(iStartRow, iLength, m_aoFields[iField].idx, &aStrings); + for( std::vector::size_type i = 0; i < aStrings.size(); i++ ) + { + // Copy using CPLStrdup so user can call CPLFree + papszStrList[i] = CPLStrdup(aStrings[i].c_str()); + } + } + else + { + // need to convert to a vector first + std::vector aStrings; + for( int i = 0; i < iLength; i++ ) + { + aStrings.push_back(papszStrList[i]); + } + m_poKEATable->setStringFields(iStartRow, iLength, m_aoFields[iField].idx, &aStrings); + } + } + catch(kealib::KEAException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to read/write attribute table: %s", e.what() ); + return CE_Failure; + } + } + break; + default: + break; + } + return CE_None; +} + +int KEARasterAttributeTable::ChangesAreWrittenToFile() +{ + return TRUE; +} + +void KEARasterAttributeTable::SetRowCount( int iCount ) +{ + /*if( this->eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Dataset not open in update mode"); + return; + }*/ + + if( iCount > (int)m_poKEATable->getSize() ) + { + m_poKEATable->addRows(iCount - m_poKEATable->getSize()); + } + // can't shrink +} + +CPLErr KEARasterAttributeTable::CreateColumn( const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ) +{ + /*if( this->eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Dataset not open in update mode"); + return CE_Failure; + }*/ + + std::string strUsage = "Generic"; + switch(eFieldUsage) + { + case GFU_PixelCount: + strUsage = "PixelCount"; + eFieldType = GFT_Real; + break; + case GFU_Name: + strUsage = "Name"; + eFieldType = GFT_String; + break; + case GFU_Red: + strUsage = "Red"; + eFieldType = GFT_Integer; + break; + case GFU_Green: + strUsage = "Green"; + eFieldType = GFT_Integer; + break; + case GFU_Blue: + strUsage = "Blue"; + eFieldType = GFT_Integer; + break; + case GFU_Alpha: + strUsage = "Alpha"; + eFieldType = GFT_Integer; + break; + default: + // leave as "Generic" + break; + } + + try + { + if(eFieldType == GFT_Integer) + { + m_poKEATable->addAttIntField(pszFieldName, 0, strUsage); + } + else if(eFieldType == GFT_Real) + { + m_poKEATable->addAttFloatField(pszFieldName, 0, strUsage); + } + else + { + m_poKEATable->addAttStringField(pszFieldName, "", strUsage); + } + + // assume we can just grab this now + kealib::KEAATTField sKEAField = m_poKEATable->getField(pszFieldName); + m_aoFields.push_back(sKEAField); + } + catch(kealib::KEAException &e) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to add column: %s", e.what() ); + return CE_Failure; + } + + return CE_None; +} + +CPLXMLNode *KEARasterAttributeTable::Serialize() const +{ + if( ( GetRowCount() * GetColumnCount() ) > RAT_MAX_ELEM_FOR_CLONE ) + return NULL; + + return GDALRasterAttributeTable::Serialize(); +} diff --git a/bazaar/plugin/gdal/frmts/kea/kearat.h b/bazaar/plugin/gdal/frmts/kea/kearat.h new file mode 100644 index 000000000..2213de18a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/kearat.h @@ -0,0 +1,87 @@ +/* + * $Id: kearat.h 29258 2015-05-28 22:08:43Z rouault $ + * kearat.h + * + * Created by Pete Bunting on 01/08/2012. + * Copyright 2012 LibKEA. All rights reserved. + * + * This file is part of LibKEA. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef KEARAT_H +#define KEARAT_H + +#include "gdal_priv.h" +#include "gdal_rat.h" +#if defined(USE_GCC_VISIBILITY_FLAG) && !defined(DllExport) +#define DllExport CPL_DLL +#endif +#include "keaband.h" + +class KEARasterAttributeTable : public GDALRasterAttributeTable +{ +private: + kealib::KEAAttributeTable *m_poKEATable; + std::vector m_aoFields; + CPLString osWorkingResult; + +public: + KEARasterAttributeTable(kealib::KEAAttributeTable *poKEATable); + ~KEARasterAttributeTable(); + + GDALDefaultRasterAttributeTable *Clone() const; + + virtual int GetColumnCount() const; + + virtual const char *GetNameOfCol( int ) const; + virtual GDALRATFieldUsage GetUsageOfCol( int ) const; + virtual GDALRATFieldType GetTypeOfCol( int ) const; + + virtual int GetColOfUsage( GDALRATFieldUsage ) const; + + virtual int GetRowCount() const; + + virtual const char *GetValueAsString( int iRow, int iField ) const; + virtual int GetValueAsInt( int iRow, int iField ) const; + virtual double GetValueAsDouble( int iRow, int iField ) const; + + virtual void SetValue( int iRow, int iField, const char *pszValue ); + virtual void SetValue( int iRow, int iField, double dfValue); + virtual void SetValue( int iRow, int iField, int nValue ); + + virtual CPLErr ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, double *pdfData); + virtual CPLErr ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, int *pnData); + virtual CPLErr ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, char **papszStrList); + + virtual int ChangesAreWrittenToFile(); + virtual void SetRowCount( int iCount ); + + virtual CPLErr CreateColumn( const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ); + + virtual CPLXMLNode *Serialize() const; + +}; + +#endif //KEARAT_H diff --git a/bazaar/plugin/gdal/frmts/kea/makefile.vc b/bazaar/plugin/gdal/frmts/kea/makefile.vc new file mode 100644 index 000000000..1a1ec4533 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kea/makefile.vc @@ -0,0 +1,26 @@ + +OBJ = keaband.obj keacopy.obj keadataset.obj keadriver.obj keamaskband.obj keaoverview.obj kearat.obj + +EXTRAFLAGS = $(KEA_CFLAGS) + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + +PLUGIN_DLL = gdal_KEA.dll + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(GDALLIB) $(KEA_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) diff --git a/bazaar/plugin/gdal/frmts/kmlsuperoverlay/GNUmakefile b/bazaar/plugin/gdal/frmts/kmlsuperoverlay/GNUmakefile new file mode 100644 index 000000000..8cccabd44 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kmlsuperoverlay/GNUmakefile @@ -0,0 +1,15 @@ + + +include ../../GDALmake.opt + +OBJ = kmlsuperoverlaydataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + diff --git a/bazaar/plugin/gdal/frmts/kmlsuperoverlay/kmlsuperoverlaydataset.cpp b/bazaar/plugin/gdal/frmts/kmlsuperoverlay/kmlsuperoverlaydataset.cpp new file mode 100644 index 000000000..860f9e75a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kmlsuperoverlay/kmlsuperoverlaydataset.cpp @@ -0,0 +1,2519 @@ +/****************************************************************************** + * $Id$ + * + * Project: KmlSuperOverlay + * Purpose: Implements write support for KML superoverlay - KMZ. + * Author: Harsh Govind, harsh.govind@spadac.com + * + ****************************************************************************** + * Copyright (c) 2010, SPADAC Inc. + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "kmlsuperoverlaydataset.h" + +#include /* fabs */ +#include /* strdup */ +#include +#include +#include +#include +#include + +#include "cpl_error.h" +#include "cpl_string.h" +#include "cpl_conv.h" +#include "cpl_vsi.h" +#include "ogr_spatialref.h" +#include "../vrt/gdal_vrt.h" +#include "../vrt/vrtdataset.h" + +using namespace std; + +/************************************************************************/ +/* GenerateTiles() */ +/************************************************************************/ +void GenerateTiles(std::string filename, + CPL_UNUSED int zoom, + int rxsize, + int rysize, + CPL_UNUSED int ix, + CPL_UNUSED int iy, + int rx, int ry, int dxsize, + int dysize, int bands, + GDALDataset* poSrcDs, + GDALDriver* poOutputTileDriver, + GDALDriver* poMemDriver, + bool isJpegDriver) +{ + GDALDataset* poTmpDataset = NULL; + GDALRasterBand* alphaBand = NULL; + + GByte* pafScanline = new GByte[dxsize]; + bool* hadnoData = new bool[dxsize]; + + if (isJpegDriver && bands == 4) + bands = 3; + + poTmpDataset = poMemDriver->Create("", dxsize, dysize, bands, GDT_Byte, NULL); + + if (isJpegDriver == false)//Jpeg dataset only has one or three bands + { + if (bands < 4)//add transparency to files with one band or three bands + { + poTmpDataset->AddBand(GDT_Byte); + alphaBand = poTmpDataset->GetRasterBand(poTmpDataset->GetRasterCount()); + } + } + + int rowOffset = rysize/dysize; + int loopCount = rysize/rowOffset; + for (int row = 0; row < loopCount; row++) + { + if (isJpegDriver == false) + { + for (int i = 0; i < dxsize; i++) + { + hadnoData[i] = false; + } + } + + for (int band = 1; band <= bands; band++) + { + GDALRasterBand* poBand = poSrcDs->GetRasterBand(band); + + int hasNoData = 0; + bool isSigned = false; + double noDataValue = poBand->GetNoDataValue(&hasNoData); + const char* pixelType = poBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + if (pixelType) + { + if (strcmp(pixelType, "SIGNEDBYTE") == 0) + { + isSigned = true; + } + } + + int yOffset = ry + row * rowOffset; + bool bReadFailed = false; + if (poBand) + { + CPLErr errTest = + poBand->RasterIO( GF_Read, rx, yOffset, rxsize, rowOffset, pafScanline, dxsize, 1, GDT_Byte, 0, 0, NULL); + + if ( errTest == CE_Failure ) + { + hasNoData = 1; + bReadFailed = true; + } + } + + + //fill the true or false for hadnoData array if the source data has nodata value + if (isJpegDriver == false) + { + if (hasNoData == 1) + { + for (int j = 0; j < dxsize; j++) + { + double v = pafScanline[j]; + double tmpv = v; + if (isSigned) + { + tmpv -= 128; + } + if (tmpv == noDataValue || bReadFailed == true) + { + hadnoData[j] = true; + } + } + } + } + + if (bReadFailed == false) + { + GDALRasterBand* poBandtmp = poTmpDataset->GetRasterBand(band); + poBandtmp->RasterIO(GF_Write, 0, row, dxsize, 1, pafScanline, dxsize, 1, GDT_Byte, + 0, 0, NULL); + } + } + + //fill the values for alpha band + if (isJpegDriver == false) + { + if (alphaBand) + { + for (int i = 0; i < dxsize; i++) + { + if (hadnoData[i] == true) + { + pafScanline[i] = 0; + } + else + { + pafScanline[i] = 255; + } + } + + alphaBand->RasterIO(GF_Write, 0, row, dxsize, 1, pafScanline, dxsize, 1, GDT_Byte, + 0, 0, NULL); + } + } + } + + delete [] pafScanline; + delete [] hadnoData; + + CPLString osOpenAfterCopy = CPLGetConfigOption("GDAL_OPEN_AFTER_COPY", ""); + CPLSetThreadLocalConfigOption("GDAL_OPEN_AFTER_COPY", "NO"); + /* to prevent CreateCopy() from calling QuietDelete() */ + char** papszOptions = CSLAddNameValue(NULL, "QUIET_DELETE_ON_CREATE_COPY", "NO"); + GDALDataset* outDs = poOutputTileDriver->CreateCopy(filename.c_str(), poTmpDataset, FALSE, papszOptions, NULL, NULL); + CSLDestroy(papszOptions); + CPLSetThreadLocalConfigOption("GDAL_OPEN_AFTER_COPY", osOpenAfterCopy.size() ? osOpenAfterCopy.c_str() : NULL); + + GDALClose(poTmpDataset); + if (outDs) + GDALClose(outDs); +} + +/************************************************************************/ +/* GenerateRootKml() */ +/************************************************************************/ + +static +int GenerateRootKml(const char* filename, + const char* kmlfilename, + double north, + double south, + double east, + double west, + int tilesize, + const char* pszOverlayName, + const char* pszOverlayDescription) +{ + VSILFILE* fp = VSIFOpenL(filename, "wb"); + if (fp == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", + filename); + return FALSE; + } + int minlodpixels = tilesize/2; + + const char* tmpfilename = CPLGetBasename(kmlfilename); + if( pszOverlayName == NULL ) + pszOverlayName = tmpfilename; + + // If we haven't writen any features yet, output the layer's schema + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\t\n"); + char* pszEncoded = CPLEscapeString(pszOverlayName, -1, CPLES_XML); + VSIFPrintfL(fp, "\t\t%s\n", pszEncoded); + CPLFree(pszEncoded); + if( pszOverlayDescription == NULL ) + { + VSIFPrintfL(fp, "\t\t\n"); + } + else + { + pszEncoded = CPLEscapeString(pszOverlayDescription, -1, CPLES_XML); + VSIFPrintfL(fp, "\t\t%s\n", pszEncoded); + CPLFree(pszEncoded); + } + VSIFPrintfL(fp, "\t\t#hideChildrenStyle\n"); + VSIFPrintfL(fp, "\t\t\n"); + /*VSIFPrintfL(fp, "\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t%f\n", north); + VSIFPrintfL(fp, "\t\t\t\t%f\n", south); + VSIFPrintfL(fp, "\t\t\t\t%f\n", east); + VSIFPrintfL(fp, "\t\t\t\t%f\n", west); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\n");*/ + VSIFPrintfL(fp, "\t\t\n"); + VSIFPrintfL(fp, "\t\t\t1\n"); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t\t%f\n", north); + VSIFPrintfL(fp, "\t\t\t\t\t%f\n", south); + VSIFPrintfL(fp, "\t\t\t\t\t%f\n", east); + VSIFPrintfL(fp, "\t\t\t\t\t%f\n", west); + VSIFPrintfL(fp, "\t\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t\t%d\n", minlodpixels); + VSIFPrintfL(fp, "\t\t\t\t\t-1\n"); + VSIFPrintfL(fp, "\t\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t0/0/0.kml\n"); + VSIFPrintfL(fp, "\t\t\t\tonRegion\n"); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\n"); + VSIFPrintfL(fp, "\t\n"); + VSIFPrintfL(fp, "\n"); + + VSIFCloseL(fp); + return TRUE; +} + +/************************************************************************/ +/* GenerateChildKml() */ +/************************************************************************/ + +static +int GenerateChildKml(std::string filename, + int zoom, int ix, int iy, + double zoomxpixel, double zoomypixel, int dxsize, int dysize, + double south, double west, int xsize, + int ysize, int maxzoom, + OGRCoordinateTransformation * poTransform, + std::string fileExt, + bool fixAntiMeridian, + const char* pszAltitude, + const char* pszAltitudeMode) +{ + double tnorth = south + zoomypixel *((iy + 1)*dysize); + double tsouth = south + zoomypixel *(iy*dysize); + double teast = west + zoomxpixel*((ix+1)*dxsize); + double twest = west + zoomxpixel*ix*dxsize; + + double upperleftT = twest; + double lowerleftT = twest; + + double rightbottomT = tsouth; + double leftbottomT = tsouth; + + double lefttopT = tnorth; + double righttopT = tnorth; + + double lowerrightT = teast; + double upperrightT = teast; + + if (poTransform) + { + poTransform->Transform(1, &twest, &tsouth); + poTransform->Transform(1, &teast, &tnorth); + + poTransform->Transform(1, &upperleftT, &lefttopT); + poTransform->Transform(1, &upperrightT, &righttopT); + poTransform->Transform(1, &lowerrightT, &rightbottomT); + poTransform->Transform(1, &lowerleftT, &leftbottomT); + } + + if ( fixAntiMeridian && teast < twest) + { + teast += 360; + lowerrightT += 360; + upperrightT += 360; + } + + std::vector xchildren; + std::vector ychildern; + + int minLodPixels = 128; + if (zoom == 0) + { + minLodPixels = 1; + } + + int maxLodPix = -1; + if ( zoom < maxzoom ) + { + double zareasize = pow(2.0, (maxzoom - zoom - 1))*dxsize; + double zareasize1 = pow(2.0, (maxzoom - zoom - 1))*dysize; + xchildren.push_back(ix*2); + int tmp = ix*2 + 1; + int tmp1 = (int)ceil(xsize / zareasize); + if (tmp < tmp1) + { + xchildren.push_back(ix*2+1); + } + ychildern.push_back(iy*2); + tmp = iy*2 + 1; + tmp1 = (int)ceil(ysize / zareasize1); + if (tmp < tmp1) + { + ychildern.push_back(iy*2+1); + } + maxLodPix = 2048; + } + + VSILFILE* fp = VSIFOpenL(filename.c_str(), "wb"); + if (fp == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", + filename.c_str()); + return FALSE; + } + + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\t\n"); + VSIFPrintfL(fp, "\t\t%d/%d/%d.kml\n", zoom, ix, iy); + VSIFPrintfL(fp, "\t\t#hideChildrenStyle\n"); + VSIFPrintfL(fp, "\t\t\n"); + VSIFPrintfL(fp, "\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t%f\n", tnorth); + VSIFPrintfL(fp, "\t\t\t\t%f\n", tsouth); + VSIFPrintfL(fp, "\t\t\t\t%f\n", teast); + VSIFPrintfL(fp, "\t\t\t\t%f\n", twest); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t%d\n", minLodPixels); + VSIFPrintfL(fp, "\t\t\t\t%d\n", maxLodPix); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\n"); + VSIFPrintfL(fp, "\t\t\n"); + VSIFPrintfL(fp, "\t\t\t%d\n", zoom); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t%d%s\n", iy, fileExt.c_str()); + VSIFPrintfL(fp, "\t\t\t\n"); + + if( pszAltitude != NULL ) + { + VSIFPrintfL(fp, "\t\t\t%s\n", pszAltitude); + } + if( pszAltitudeMode != NULL && + (strcmp(pszAltitudeMode, "clampToGround") == 0 || + strcmp(pszAltitudeMode, "absolute") == 0) ) + { + VSIFPrintfL(fp, "\t\t\t%s\n", pszAltitudeMode); + } + else if( pszAltitudeMode != NULL && + (strcmp(pszAltitudeMode, "relativeToSeaFloor") == 0 || + strcmp(pszAltitudeMode, "clampToSeaFloor") == 0) ) + { + VSIFPrintfL(fp, "\t\t\t%s\n", pszAltitudeMode); + } + + /* When possible, use . I've noticed otherwise that */ + /* if using with extents of the size of a country or */ + /* continent, the overlay is really bad placed in GoogleEarth */ + if( lowerleftT == upperleftT && lowerrightT == upperrightT && + leftbottomT == rightbottomT && righttopT == lefttopT ) + { + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t%f\n", tnorth); + VSIFPrintfL(fp, "\t\t\t\t%f\n", tsouth); + VSIFPrintfL(fp, "\t\t\t\t%f\n", teast); + VSIFPrintfL(fp, "\t\t\t\t%f\n", twest); + VSIFPrintfL(fp, "\t\t\t\n"); + } + else + { + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t\t%f,%f,0\n", lowerleftT, leftbottomT); + VSIFPrintfL(fp, "\t\t\t\t\t%f,%f,0\n", lowerrightT, rightbottomT); + VSIFPrintfL(fp, "\t\t\t\t\t%f,%f,0\n", upperrightT, righttopT); + VSIFPrintfL(fp, "\t\t\t\t\t%f,%f,0\n", upperleftT, lefttopT); + VSIFPrintfL(fp, "\t\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\n"); + } + VSIFPrintfL(fp, "\t\t\n"); + + for (unsigned int i = 0; i < xchildren.size(); i++) + { + int cx = xchildren[i]; + for (unsigned int j = 0; j < ychildern.size(); j++) + { + int cy = ychildern[j]; + + double cnorth = south + zoomypixel/2 *((cy + 1)*dysize); + double csouth = south + zoomypixel/2 *(cy*dysize); + double ceast = west + zoomxpixel/2*((cx+1)*dxsize); + double cwest = west + zoomxpixel/2*cx*dxsize; + + if (poTransform) + { + poTransform->Transform(1, &cwest, &csouth); + poTransform->Transform(1, &ceast, &cnorth); + } + + if ( fixAntiMeridian && ceast < cwest ) + { + ceast += 360; + } + + VSIFPrintfL(fp, "\t\t\n"); + VSIFPrintfL(fp, "\t\t\t%d/%d/%d%s\n", zoom+1, cx, cy, fileExt.c_str()); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t\t%f\n", cnorth); + VSIFPrintfL(fp, "\t\t\t\t\t%f\n", csouth); + VSIFPrintfL(fp, "\t\t\t\t\t%f\n", ceast); + VSIFPrintfL(fp, "\t\t\t\t\t%f\n", cwest); + VSIFPrintfL(fp, "\t\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t\t128\n"); + VSIFPrintfL(fp, "\t\t\t\t\t-1\n"); + VSIFPrintfL(fp, "\t\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\t../../%d/%d/%d.kml\n", zoom+1, cx, cy); + VSIFPrintfL(fp, "\t\t\t\tonRegion\n"); + VSIFPrintfL(fp, "\t\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\t\n"); + VSIFPrintfL(fp, "\t\t\n"); + } + } + + VSIFPrintfL(fp, "\t\n"); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); + + return TRUE; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +class KmlSuperOverlayDummyDataset: public GDALDataset +{ + public: + KmlSuperOverlayDummyDataset() {} +}; + +static +GDALDataset *KmlSuperOverlayCreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + CPL_UNUSED int bStrict, + char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData) +{ + bool isKmz = false; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + int bands = poSrcDS->GetRasterCount(); + if (bands != 1 && bands != 3 && bands != 4) + return NULL; + + //correct the file and get the directory + char* output_dir = NULL; + if (pszFilename == NULL) + { + output_dir = CPLGetCurrentDir(); + pszFilename = CPLFormFilename(output_dir, "doc", "kml"); + } + else + { + const char* extension = CPLGetExtension(pszFilename); + if (!EQUAL(extension,"kml") && !EQUAL(extension,"kmz")) + { + CPLError( CE_Failure, CPLE_None, + "File extension should be kml or kmz." ); + return NULL; + } + if (EQUAL(extension,"kmz")) + { + isKmz = true; + } + + output_dir = CPLStrdup(CPLGetPath(pszFilename)); + if (strcmp(output_dir, "") == 0) + { + CPLFree(output_dir); + output_dir = CPLGetCurrentDir(); + } + } + CPLString outDir = output_dir ? output_dir : ""; + CPLFree(output_dir); + output_dir = NULL; + + VSILFILE* zipHandle = NULL; + if (isKmz) + { + outDir = "/vsizip/"; + outDir += pszFilename; + zipHandle = VSIFOpenL(outDir, "wb"); + if( zipHandle == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", pszFilename); + return NULL; + } + } + + GDALDriver* poOutputTileDriver = NULL; + bool isJpegDriver = true; + + const char* pszFormat = CSLFetchNameValueDef(papszOptions, "FORMAT", "JPEG"); + if (EQUAL(pszFormat, "PNG")) + { + isJpegDriver = false; + } + + GDALDriver* poMemDriver = GetGDALDriverManager()->GetDriverByName("MEM"); + poOutputTileDriver = GetGDALDriverManager()->GetDriverByName(pszFormat); + + if( poMemDriver == NULL || poOutputTileDriver == NULL) + { + CPLError( CE_Failure, CPLE_None, + "Image export driver was not found.." ); + if( zipHandle != NULL ) + { + VSIFCloseL(zipHandle); + VSIUnlink(pszFilename); + } + return NULL; + } + + int xsize = poSrcDS->GetRasterXSize(); + int ysize = poSrcDS->GetRasterYSize(); + + double north = 0.0; + double south = 0.0; + double east = 0.0; + double west = 0.0; + + double adfGeoTransform[6]; + + if( poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None ) + { + north = adfGeoTransform[3]; + south = adfGeoTransform[3] + adfGeoTransform[5]*ysize; + east = adfGeoTransform[0] + adfGeoTransform[1]*xsize; + west = adfGeoTransform[0]; + } + + OGRCoordinateTransformation * poTransform = NULL; + if (poSrcDS->GetProjectionRef() != NULL) + { + OGRSpatialReference poDsUTM; + + char* projStr = (char*)poSrcDS->GetProjectionRef(); + + if (poDsUTM.importFromWkt(&projStr) == OGRERR_NONE) + { + if (poDsUTM.IsProjected()) + { + OGRSpatialReference poLatLong; + poLatLong.SetWellKnownGeogCS( "WGS84" ); + + poTransform = OGRCreateCoordinateTransformation( &poDsUTM, &poLatLong ); + if( poTransform != NULL ) + { + poTransform->Transform(1, &west, &south); + poTransform->Transform(1, &east, &north); + } + } + } + } + + bool fixAntiMeridian = CSLFetchBoolean( papszOptions, "FIX_ANTIMERIDIAN", FALSE ); + if ( fixAntiMeridian && east < west ) + { + east += 360; + } + + //Zoom levels of the pyramid. + int maxzoom = 0; + int tilexsize; + int tileysize; + // Let the longer side determine the max zoom level and x/y tilesizes. + if ( xsize >= ysize ) + { + double dtilexsize = xsize; + while (dtilexsize > 400) //calculate x tile size + { + dtilexsize = dtilexsize/2; + maxzoom ++; + } + tilexsize = (int)dtilexsize; + tileysize = (int)( (double)(dtilexsize * ysize) / xsize ); + } + else + { + double dtileysize = ysize; + while (dtileysize > 400) //calculate y tile size + { + dtileysize = dtileysize/2; + maxzoom ++; + } + + tileysize = (int)dtileysize; + tilexsize = (int)( (double)(dtileysize * xsize) / ysize ); + } + + std::vector zoomxpixels; + std::vector zoomypixels; + for (int zoom = 0; zoom < maxzoom + 1; zoom++) + { + zoomxpixels.push_back(adfGeoTransform[1] * pow(2.0, (maxzoom - zoom))); + // zoomypixels.push_back(abs(adfGeoTransform[5]) * pow(2.0, (maxzoom - zoom))); + zoomypixels.push_back(fabs(adfGeoTransform[5]) * pow(2.0, (maxzoom - zoom))); + } + + std::string tmpFileName; + std::vector fileVector; + int nRet; + + const char* pszOverlayName = CSLFetchNameValue(papszOptions, "NAME"); + const char* pszOverlayDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION"); + + if (isKmz) + { + tmpFileName = CPLFormFilename(outDir, "doc.kml", NULL); + nRet = GenerateRootKml(tmpFileName.c_str(), pszFilename, + north, south, east, west, (int)tilexsize, + pszOverlayName, pszOverlayDescription); + fileVector.push_back(tmpFileName); + } + else + { + nRet = GenerateRootKml(pszFilename, pszFilename, + north, south, east, west, (int)tilexsize, + pszOverlayName, pszOverlayDescription); + } + + if (nRet == FALSE) + { + OGRCoordinateTransformation::DestroyCT( poTransform ); + if( zipHandle != NULL ) + { + VSIFCloseL(zipHandle); + VSIUnlink(pszFilename); + } + return NULL; + } + + const char* pszAltitude = CSLFetchNameValue(papszOptions, "ALTITUDE"); + const char* pszAltitudeMode = CSLFetchNameValue(papszOptions, "ALTITUDEMODE"); + if( pszAltitudeMode != NULL ) + { + if( strcmp(pszAltitudeMode, "clampToGround") == 0 ) + { + pszAltitudeMode = NULL; + pszAltitude = NULL; + } + else if( strcmp(pszAltitudeMode, "absolute") == 0 ) + { + if( pszAltitude == NULL ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Using ALTITUDE=0 as default value"); + pszAltitude = "0"; + } + } + else if( strcmp(pszAltitudeMode, "relativeToSeaFloor") == 0 ) + { + /* nothing to do */ + } + else if( strcmp(pszAltitudeMode, "clampToSeaFloor") == 0 ) + { + pszAltitude = NULL; + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, "Ignoring unhandled value of ALTITUDEMODE"); + pszAltitudeMode = NULL; + pszAltitude = NULL; + } + } + + int zoom; + int nTotalTiles = 0; + int nTileCount = 0; + + for (zoom = maxzoom; zoom >= 0; --zoom) + { + int rmaxxsize = tilexsize * (1 << (maxzoom-zoom)); + int rmaxysize = tileysize * (1 << (maxzoom-zoom)); + + int xloop = (int)xsize/rmaxxsize; + int yloop = (int)ysize/rmaxysize; + nTotalTiles += xloop * yloop; + } + + for (zoom = maxzoom; zoom >= 0; --zoom) + { + int rmaxxsize = tilexsize * (1 << (maxzoom-zoom)); + int rmaxysize = tileysize * (1 << (maxzoom-zoom)); + + int xloop = (int)xsize/rmaxxsize; + int yloop = (int)ysize/rmaxysize; + + xloop = xloop>0 ? xloop : 1; + yloop = yloop>0 ? yloop : 1; + + std::stringstream zoomStr; + zoomStr << zoom; + + std::string zoomDir = outDir; + zoomDir+= "/" + zoomStr.str(); + VSIMkdir(zoomDir.c_str(), 0775); + + for (int ix = 0; ix < xloop; ix++) + { + int rxsize = (int)(rmaxxsize); + int rx = (int)(ix * rmaxxsize); + int dxsize = (int)(rxsize/rmaxxsize * tilexsize); + + std::stringstream ixStr; + ixStr << ix; + + zoomDir = outDir; + zoomDir+= "/" + zoomStr.str(); + zoomDir+= "/" + ixStr.str(); + VSIMkdir(zoomDir.c_str(), 0775); + + for (int iy = 0; iy < yloop; iy++) + { + int rysize = (int)(rmaxysize); + int ry = (int)(ysize - (iy * rmaxysize)) - rysize; + int dysize = (int)(rysize/rmaxysize * tileysize); + + std::stringstream iyStr; + iyStr << iy; + + std::string fileExt = ".jpg"; + if (isJpegDriver == false) + { + fileExt = ".png"; + } + std::string filename = zoomDir + "/" + iyStr.str() + fileExt; + if (isKmz) + { + fileVector.push_back(filename); + } + + GenerateTiles(filename, zoom, rxsize, rysize, ix, iy, rx, ry, dxsize, + dysize, bands, poSrcDS, poOutputTileDriver, poMemDriver, isJpegDriver); + std::string childKmlfile = zoomDir + "/" + iyStr.str() + ".kml"; + if (isKmz) + { + fileVector.push_back(childKmlfile); + } + + double tmpSouth = adfGeoTransform[3] + adfGeoTransform[5]*ysize; + double zoomxpix = zoomxpixels[zoom]; + double zoomypix = zoomypixels[zoom]; + if (zoomxpix == 0) + { + zoomxpix = 1; + } + + if (zoomypix == 0) + { + zoomypix = 1; + } + + GenerateChildKml(childKmlfile, zoom, ix, iy, zoomxpix, zoomypix, + dxsize, dysize, tmpSouth, adfGeoTransform[0], + xsize, ysize, maxzoom, poTransform, fileExt, fixAntiMeridian, + pszAltitude, pszAltitudeMode); + + nTileCount ++; + pfnProgress(1.0 * nTileCount / nTotalTiles, "", pProgressData); + } + } + } + + OGRCoordinateTransformation::DestroyCT( poTransform ); + poTransform = NULL; + + if( zipHandle != NULL ) + { + VSIFCloseL(zipHandle); + } + + GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); + GDALDataset* poDS = KmlSuperOverlayReadDataset::Open(&oOpenInfo); + if( poDS == NULL ) + poDS = new KmlSuperOverlayDummyDataset(); + return poDS; +} + + +/************************************************************************/ +/* KMLRemoveSlash() */ +/************************************************************************/ + +/* replace "a/b/../c" pattern by "a/c" */ +static CPLString KMLRemoveSlash(const char* pszPathIn) +{ + char* pszPath = CPLStrdup(pszPathIn); + + while(TRUE) + { + char* pszSlashDotDot = strstr(pszPath, "/../"); + if (pszSlashDotDot == NULL || pszSlashDotDot == pszPath) + break; + char* pszSlashBefore = pszSlashDotDot-1; + while(pszSlashBefore > pszPath && *pszSlashBefore != '/') + pszSlashBefore --; + if (pszSlashBefore == pszPath) + break; + memmove(pszSlashBefore + 1, pszSlashDotDot + 4, + strlen(pszSlashDotDot + 4) + 1); + } + CPLString osRet = pszPath; + CPLFree(pszPath); + return osRet; +} + +/************************************************************************/ +/* KmlSuperOverlayReadDataset() */ +/************************************************************************/ + +KmlSuperOverlayReadDataset::KmlSuperOverlayReadDataset() + +{ + nFactor = 1; + psRoot = NULL; + psDocument = NULL; + poDSIcon = NULL; + adfGeoTransform[0] = 0; + adfGeoTransform[1] = 1; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = 0; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = 1; + + nOverviewCount = 0; + papoOverviewDS = NULL; + bIsOvr = FALSE; + + poParent = NULL; + psFirstLink = NULL; + psLastLink = NULL; +} + +/************************************************************************/ +/* ~KmlSuperOverlayReadDataset() */ +/************************************************************************/ + +KmlSuperOverlayReadDataset::~KmlSuperOverlayReadDataset() + +{ + if( psRoot != NULL ) + CPLDestroyXMLNode( psRoot ); + CloseDependentDatasets(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int KmlSuperOverlayReadDataset::CloseDependentDatasets() +{ + int bRet = FALSE; + if( poDSIcon != NULL ) + { + CPLString osFilename(poDSIcon->GetDescription()); + delete poDSIcon; + VSIUnlink(osFilename); + poDSIcon = NULL; + bRet = TRUE; + } + + LinkedDataset* psCur = psFirstLink; + psFirstLink = NULL; + psLastLink = NULL; + + while( psCur != NULL ) + { + LinkedDataset* psNext = psCur->psNext; + if( psCur->poDS != NULL ) + { + if( psCur->poDS->nRefCount == 1 ) + bRet = TRUE; + GDALClose(psCur->poDS); + } + delete psCur; + psCur = psNext; + } + + if( nOverviewCount > 0 ) + { + bRet = TRUE; + for(int i = 0; i < nOverviewCount; i++) + delete papoOverviewDS[i]; + CPLFree(papoOverviewDS); + nOverviewCount = 0; + papoOverviewDS = NULL; + } + + return bRet; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *KmlSuperOverlayReadDataset::GetProjectionRef() + +{ + return SRS_WKT_WGS84; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr KmlSuperOverlayReadDataset::GetGeoTransform( double * padfGeoTransform ) +{ + memcpy(padfGeoTransform, adfGeoTransform, 6 * sizeof(double)); + return CE_None; +} + +/************************************************************************/ +/* KmlSuperOverlayRasterBand() */ +/************************************************************************/ + +KmlSuperOverlayRasterBand::KmlSuperOverlayRasterBand(KmlSuperOverlayReadDataset* poDS, + CPL_UNUSED int nBand) +{ + nRasterXSize = poDS->nRasterXSize; + nRasterYSize = poDS->nRasterYSize; + eDataType = GDT_Byte; + nBlockXSize = 256; + nBlockYSize = 256; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr KmlSuperOverlayRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, void *pData ) +{ + int nXOff = nBlockXOff * nBlockXSize; + int nYOff = nBlockYOff * nBlockYSize; + int nXSize = nBlockXSize; + int nYSize = nBlockYSize; + if( nXOff + nXSize > nRasterXSize ) + nXSize = nRasterXSize - nXOff; + if( nYOff + nYSize > nRasterYSize ) + nYSize = nRasterYSize - nYOff; + + GDALRasterIOExtraArg sExtraArg; + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + + return IRasterIO( GF_Read, + nXOff, + nYOff, + nXSize, + nYSize, + pData, + nXSize, + nYSize, + eDataType, + 1, + nBlockXSize, &sExtraArg ); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp KmlSuperOverlayRasterBand::GetColorInterpretation() +{ + return (GDALColorInterp)(GCI_RedBand + nBand - 1); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr KmlSuperOverlayRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ + KmlSuperOverlayReadDataset* poGDS = (KmlSuperOverlayReadDataset* )poDS; + + return poGDS->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + 1, &nBand, + nPixelSpace, nLineSpace, 0, psExtraArg ); +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int KmlSuperOverlayRasterBand::GetOverviewCount() +{ + KmlSuperOverlayReadDataset* poGDS = (KmlSuperOverlayReadDataset* )poDS; + + return poGDS->nOverviewCount; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *KmlSuperOverlayRasterBand::GetOverview(int iOvr) +{ + KmlSuperOverlayReadDataset* poGDS = (KmlSuperOverlayReadDataset* )poDS; + + if( iOvr < 0 || iOvr >= poGDS->nOverviewCount ) + return NULL; + + return poGDS->papoOverviewDS[iOvr]->GetRasterBand(nBand); +} + +/************************************************************************/ +/* KmlSuperOverlayGetBoundingBox() */ +/************************************************************************/ + +static +int KmlSuperOverlayGetBoundingBox(CPLXMLNode* psNode, double* adfExtents) +{ + CPLXMLNode* psBox = CPLGetXMLNode(psNode, "LatLonBox"); + if( psBox == NULL ) + psBox = CPLGetXMLNode(psNode, "LatLonAltBox"); + if( psBox == NULL ) + return FALSE; + + const char* pszNorth = CPLGetXMLValue(psBox, "north", NULL); + const char* pszSouth = CPLGetXMLValue(psBox, "south", NULL); + const char* pszEast = CPLGetXMLValue(psBox, "east", NULL); + const char* pszWest = CPLGetXMLValue(psBox, "west", NULL); + if( pszNorth == NULL || pszSouth == NULL || pszEast == NULL || pszWest == NULL ) + return FALSE; + + adfExtents[0] = CPLAtof(pszWest); + adfExtents[1] = CPLAtof(pszSouth); + adfExtents[2] = CPLAtof(pszEast); + adfExtents[3] = CPLAtof(pszNorth); + return TRUE; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +class SubImageDesc +{ + public: + GDALDataset* poDS; + double adfExtents[4]; +}; + +CPLErr KmlSuperOverlayReadDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) +{ + if( eRWFlag == GF_Write ) + return CE_Failure; + + if( bIsOvr ) + return poParent->IRasterIO( eRWFlag, + nXOff * (poParent->nFactor / nFactor), + nYOff * (poParent->nFactor / nFactor), + nXSize * (poParent->nFactor / nFactor), + nYSize * (poParent->nFactor / nFactor), + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg); + + double dfXOff = 1.0 * nXOff / nFactor; + double dfYOff = 1.0 * nYOff / nFactor; + double dfXSize = 1.0 * nXSize / nFactor; + double dfYSize = 1.0 * nYSize / nFactor; + + int nIconCount = poDSIcon->GetRasterCount(); + + if( nBufXSize > dfXSize || nBufYSize > dfYSize ) + { + double dfRequestXMin = adfGeoTransform[0] + nXOff * adfGeoTransform[1]; + double dfRequestXMax = adfGeoTransform[0] + (nXOff + nXSize) * adfGeoTransform[1]; + double dfRequestYMin = adfGeoTransform[3] + (nYOff + nYSize) * adfGeoTransform[5]; + double dfRequestYMax = adfGeoTransform[3] + nYOff * adfGeoTransform[5]; + + CPLXMLNode* psIter = psDocument->psChild; + std::vector aosImages; + double dfXRes = adfGeoTransform[1] * nFactor; + double dfYRes = -adfGeoTransform[5] * nFactor; + double dfNewXRes = dfXRes; + double dfNewYRes = dfYRes; + + while( psIter != NULL ) + { + CPLXMLNode* psRegion = NULL; + CPLXMLNode* psLink = NULL; + double adfExtents[4]; + const char* pszHref = NULL; + if( psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "NetworkLink") == 0 && + (psRegion = CPLGetXMLNode(psIter, "Region")) != NULL && + (psLink = CPLGetXMLNode(psIter, "Link")) != NULL && + KmlSuperOverlayGetBoundingBox(psRegion, adfExtents) && + (pszHref = CPLGetXMLValue(psLink, "href", NULL)) != NULL ) + { + if( dfRequestXMin < adfExtents[2] && dfRequestXMax > adfExtents[0] && + dfRequestYMin < adfExtents[3] && dfRequestYMax > adfExtents[1] ) + { + CPLString osSubFilename; + if( strncmp(pszHref, "http", 4) == 0) + osSubFilename = CPLSPrintf("/vsicurl_streaming/%s", pszHref); + else + { + const char* pszBaseFilename = osFilename.c_str(); + if( EQUAL(CPLGetExtension(pszBaseFilename), "kmz") && + strncmp(pszBaseFilename, "/vsizip/", 8) != 0 ) + { + osSubFilename = "/vsizip/"; + osSubFilename += CPLGetPath(pszBaseFilename); + osSubFilename += "/"; + osSubFilename += pszHref; + } + else + { + osSubFilename = CPLFormFilename(CPLGetPath(pszBaseFilename), pszHref, NULL); + } + osSubFilename = KMLRemoveSlash(osSubFilename); + } + + KmlSuperOverlayReadDataset* poSubImageDS = NULL; + if( EQUAL(CPLGetExtension(osSubFilename), "kml") ) + { + KmlSuperOverlayReadDataset* poRoot = poParent ? poParent : this; + LinkedDataset* psLink = poRoot->oMapChildren[osSubFilename]; + if( psLink == NULL ) + { + if( poRoot->oMapChildren.size() == 64 ) + { + psLink = poRoot->psLastLink; + CPLAssert(psLink); + poRoot->oMapChildren.erase(psLink->osSubFilename); + GDALClose(psLink->poDS); + if( psLink->psPrev != NULL ) + { + poRoot->psLastLink = psLink->psPrev; + psLink->psPrev->psNext = NULL; + } + else + { + CPLAssert(psLink == poRoot->psFirstLink); + poRoot->psFirstLink = poRoot->psLastLink = NULL; + } + } + else + psLink = new LinkedDataset(); + + poRoot->oMapChildren[osSubFilename] = psLink; + poSubImageDS = (KmlSuperOverlayReadDataset*) + KmlSuperOverlayReadDataset::Open(osSubFilename, poRoot); + if( poSubImageDS ) + poSubImageDS->MarkAsShared(); + else + CPLDebug("KMLSuperOverlay", "Cannt open %s", osSubFilename.c_str()); + psLink->osSubFilename = osSubFilename; + psLink->poDS = poSubImageDS; + psLink->psPrev = NULL; + psLink->psNext = poRoot->psFirstLink; + if( poRoot->psFirstLink != NULL ) + { + CPLAssert(poRoot->psFirstLink->psPrev == NULL); + poRoot->psFirstLink->psPrev = psLink; + } + else + poRoot->psLastLink = psLink; + poRoot->psFirstLink = psLink; + } + else + { + poSubImageDS = psLink->poDS; + if( psLink != poRoot->psFirstLink ) + { + if( psLink == poRoot->psLastLink ) + { + poRoot->psLastLink = psLink->psPrev; + CPLAssert(poRoot->psLastLink != NULL ); + poRoot->psLastLink->psNext = NULL; + } + else + psLink->psNext->psPrev = psLink->psPrev; + CPLAssert( psLink->psPrev != NULL ); + psLink->psPrev->psNext = psLink->psNext; + psLink->psPrev = NULL; + poRoot->psFirstLink->psPrev = psLink; + psLink->psNext = poRoot->psFirstLink; + poRoot->psFirstLink = psLink; + } + } + } + if( poSubImageDS ) + { + int nSubImageXSize = poSubImageDS->GetRasterXSize(); + int nSubImageYSize = poSubImageDS->GetRasterYSize(); + adfExtents[0] = poSubImageDS->adfGeoTransform[0]; + adfExtents[1] = poSubImageDS->adfGeoTransform[3] + nSubImageYSize * poSubImageDS->adfGeoTransform[5]; + adfExtents[2] = poSubImageDS->adfGeoTransform[0] + nSubImageXSize * poSubImageDS->adfGeoTransform[1]; + adfExtents[3] = poSubImageDS->adfGeoTransform[3]; + + double dfSubXRes = (adfExtents[2] - adfExtents[0]) / nSubImageXSize; + double dfSubYRes = (adfExtents[3] - adfExtents[1]) / nSubImageYSize; + + if( dfSubXRes < dfNewXRes ) dfNewXRes = dfSubXRes; + if( dfSubYRes < dfNewYRes ) dfNewYRes = dfSubYRes; + + SubImageDesc oImageDesc; + oImageDesc.poDS = poSubImageDS; + poSubImageDS->Reference(); + memcpy(oImageDesc.adfExtents, adfExtents, 4 * sizeof(double)); + aosImages.push_back(oImageDesc); + } + } + } + psIter = psIter->psNext; + } + + if( dfNewXRes < dfXRes || dfNewYRes < dfYRes ) + { + int i; + double dfXFactor = dfXRes / dfNewXRes; + double dfYFactor = dfYRes / dfNewYRes; + VRTDataset* poVRTDS = new VRTDataset( + (int)(nRasterXSize * dfXFactor + 0.5), + (int)(nRasterYSize * dfYFactor + 0.5)); + + for(int iBandIdx = 0; iBandIdx < 4; iBandIdx++ ) + { + VRTAddBand( (VRTDatasetH) poVRTDS, GDT_Byte, NULL ); + + int nBand = iBandIdx + 1; + if( nBand <= nIconCount || (nIconCount == 1 && nBand != 4) ) + { + VRTAddSimpleSource( (VRTSourcedRasterBandH) poVRTDS->GetRasterBand(iBandIdx + 1), + (GDALRasterBandH) poDSIcon->GetRasterBand(nBand <= nIconCount ? nBand : 1), + 0, 0, + nRasterXSize, + nRasterYSize, + 0, 0, + poVRTDS->GetRasterXSize(), + poVRTDS->GetRasterYSize(), + NULL, VRT_NODATA_UNSET); + } + else + { + VRTAddComplexSource( (VRTSourcedRasterBandH) poVRTDS->GetRasterBand(iBandIdx + 1), + (GDALRasterBandH) poDSIcon->GetRasterBand(1), + 0, 0, + nRasterXSize, + nRasterYSize, + 0, 0, + poVRTDS->GetRasterXSize(), + poVRTDS->GetRasterYSize(), + VRT_NODATA_UNSET, 0, 255); + } + } + + for(i=0; i < (int)aosImages.size(); i++) + { + int nDstXOff = (int)((aosImages[i].adfExtents[0] - adfGeoTransform[0]) / dfNewXRes + 0.5); + int nDstYOff = (int)((adfGeoTransform[3] - aosImages[i].adfExtents[3]) / dfNewYRes + 0.5); + int nDstXSize = (int)((aosImages[i].adfExtents[2] - aosImages[i].adfExtents[0]) / dfNewXRes + 0.5); + int nDstYSize = (int)((aosImages[i].adfExtents[3] - aosImages[i].adfExtents[1]) / dfNewYRes + 0.5); + + int nSrcBandCount = aosImages[i].poDS->GetRasterCount(); + for(int iBandIdx = 0; iBandIdx < 4; iBandIdx++ ) + { + int nBand = iBandIdx + 1; + if( nBand <= nSrcBandCount || (nSrcBandCount == 1 && nBand != 4) ) + { + VRTAddSimpleSource( (VRTSourcedRasterBandH) poVRTDS->GetRasterBand(iBandIdx + 1), + (GDALRasterBandH) aosImages[i].poDS->GetRasterBand(nBand <= nSrcBandCount ? nBand : 1), + 0, 0, + aosImages[i].poDS->GetRasterXSize(), + aosImages[i].poDS->GetRasterYSize(), + nDstXOff, nDstYOff, nDstXSize, nDstYSize, + NULL, VRT_NODATA_UNSET); + } + else + { + VRTAddComplexSource( (VRTSourcedRasterBandH) poVRTDS->GetRasterBand(iBandIdx + 1), + (GDALRasterBandH) aosImages[i].poDS->GetRasterBand(1), + 0, 0, + aosImages[i].poDS->GetRasterXSize(), + aosImages[i].poDS->GetRasterYSize(), + nDstXOff, nDstYOff, nDstXSize, nDstYSize, + VRT_NODATA_UNSET, 0, 255); + } + } + } + + int nReqXOff = (int)(dfXOff * dfXFactor + 0.5); + int nReqYOff = (int)(dfYOff * dfYFactor + 0.5); + int nReqXSize = (int)(dfXSize * dfXFactor + 0.5); + int nReqYSize = (int)(dfYSize * dfYFactor + 0.5); + if( nReqXOff + nReqXSize > poVRTDS->GetRasterXSize() ) + nReqXSize = poVRTDS->GetRasterXSize() - nReqXOff; + if( nReqYOff + nReqYSize > poVRTDS->GetRasterYSize() ) + nReqYSize = poVRTDS->GetRasterYSize() - nReqYOff; + + CPLErr eErr = poVRTDS->RasterIO( eRWFlag, + nReqXOff, + nReqYOff, + nReqXSize, + nReqYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + psExtraArg); + + for(i=0; i < (int)aosImages.size(); i++) + { + aosImages[i].poDS->Dereference(); + } + + delete poVRTDS; + + return eErr; + } + } + + GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress; + void *pProgressDataGlobal = psExtraArg->pProgressData; + + for(int iBandIdx = 0; iBandIdx < nBandCount; iBandIdx++ ) + { + int nBand = panBandMap[iBandIdx]; + + if( (nIconCount > 1 || nBand == 4) && nBand > nIconCount ) + { + GByte nVal = (nBand == 4) ? 255 : 0; + for(int j = 0; j < nBufYSize; j ++ ) + { + GDALCopyWords( &nVal, GDT_Byte, 0, + ((GByte*) pData) + j * nLineSpace + iBandIdx * nBandSpace, eBufType, nPixelSpace, + nBufXSize ); + } + continue; + } + + int nIconBand = (nIconCount == 1) ? 1 : nBand; + + int nReqXOff = (int)(dfXOff + 0.5); + int nReqYOff = (int)(dfYOff + 0.5); + int nReqXSize = (int)(dfXSize + 0.5); + int nReqYSize = (int)(dfYSize + 0.5); + if( nReqXOff + nReqXSize > poDSIcon->GetRasterXSize() ) + nReqXSize = poDSIcon->GetRasterXSize() - nReqXOff; + if( nReqYOff + nReqYSize > poDSIcon->GetRasterYSize() ) + nReqYSize = poDSIcon->GetRasterYSize() - nReqYOff; + + psExtraArg->pfnProgress = GDALScaledProgress; + psExtraArg->pProgressData = + GDALCreateScaledProgress( 1.0 * iBandIdx / nBandCount, + 1.0 * (iBandIdx + 1) / nBandCount, + pfnProgressGlobal, + pProgressDataGlobal ); + + poDSIcon->GetRasterBand(nIconBand)->RasterIO( eRWFlag, + nReqXOff, + nReqYOff, + nReqXSize, + nReqYSize, + ((GByte*) pData) + nBandSpace * iBandIdx, + nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, + psExtraArg); + + GDALDestroyScaledProgress( psExtraArg->pProgressData ); + } + + psExtraArg->pfnProgress = pfnProgressGlobal; + psExtraArg->pProgressData = pProgressDataGlobal; + + return CE_None; + +} + +/************************************************************************/ +/* KmlSuperOverlayFindRegionStart() */ +/************************************************************************/ + +static +int KmlSuperOverlayFindRegionStartInternal(CPLXMLNode* psNode, + CPLXMLNode** ppsRegion, + CPLXMLNode** ppsDocument, + CPLXMLNode** ppsGroundOverlay, + CPLXMLNode** ppsLink) +{ + CPLXMLNode* psRegion = NULL; + CPLXMLNode* psLink = NULL; + CPLXMLNode* psGroundOverlay = NULL; + if( strcmp(psNode->pszValue, "NetworkLink") == 0 && + (psRegion = CPLGetXMLNode(psNode, "Region")) != NULL && + (psLink = CPLGetXMLNode(psNode, "Link")) != NULL ) + { + *ppsRegion = psRegion; + *ppsLink = psLink; + return TRUE; + } + if( strcmp(psNode->pszValue, "Document") == 0 && + (psRegion = CPLGetXMLNode(psNode, "Region")) != NULL && + (psGroundOverlay = CPLGetXMLNode(psNode, "GroundOverlay")) != NULL ) + { + *ppsDocument = psNode; + *ppsRegion = psRegion; + *ppsGroundOverlay = psGroundOverlay; + return TRUE; + } + + CPLXMLNode* psIter = psNode->psChild; + while(psIter != NULL) + { + if( psIter->eType == CXT_Element ) + { + if( KmlSuperOverlayFindRegionStartInternal(psIter, ppsRegion, ppsDocument, + ppsGroundOverlay, ppsLink) ) + return TRUE; + } + + psIter = psIter->psNext; + } + + return FALSE; +} + + +static +int KmlSuperOverlayFindRegionStart(CPLXMLNode* psNode, + CPLXMLNode** ppsRegion, + CPLXMLNode** ppsDocument, + CPLXMLNode** ppsGroundOverlay, + CPLXMLNode** ppsLink) +{ + CPLXMLNode* psIter = psNode; + while(psIter != NULL) + { + if( psIter->eType == CXT_Element ) + { + if( KmlSuperOverlayFindRegionStartInternal(psIter, ppsRegion, ppsDocument, + ppsGroundOverlay, ppsLink) ) + return TRUE; + } + + psIter = psIter->psNext; + } + + return FALSE; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int KmlSuperOverlayReadDataset::Identify(GDALOpenInfo * poOpenInfo) + +{ + const char* pszExt = CPLGetExtension(poOpenInfo->pszFilename); + if( EQUAL(pszExt, "kmz") ) + return -1; + if( poOpenInfo->nHeaderBytes == 0 ) + return FALSE; + if( !EQUAL(pszExt, "kml") || + strstr((const char*)poOpenInfo->pabyHeader, "pabyHeader, "") != NULL && + strstr((const char*)poOpenInfo->pabyHeader, "") != NULL && + strstr((const char*)poOpenInfo->pabyHeader, "") != NULL ) + return TRUE; + + if( strstr((const char*)poOpenInfo->pabyHeader, "") != NULL && + strstr((const char*)poOpenInfo->pabyHeader, "") != NULL && + strstr((const char*)poOpenInfo->pabyHeader, "") != NULL ) + return TRUE; + + if( i == 0 && !poOpenInfo->TryToIngest(1024*10) ) + return FALSE; + } + + return -1; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *KmlSuperOverlayReadDataset::Open(GDALOpenInfo * poOpenInfo) + +{ + if( Identify(poOpenInfo) == FALSE ) + return NULL; + + return Open(poOpenInfo->pszFilename); +} + +/************************************************************************/ +/* KmlSuperOverlayLoadIcon() */ +/************************************************************************/ + +#define BUFFER_SIZE 20000000 + +static +GDALDataset* KmlSuperOverlayLoadIcon(const char* pszBaseFilename, const char* pszIcon) +{ + const char* pszExt = CPLGetExtension(pszIcon); + if( !EQUAL(pszExt, "png") && !EQUAL(pszExt, "jpg") && !EQUAL(pszExt, "jpeg") ) + { + return NULL; + } + + CPLString osSubFilename; + if( strncmp(pszIcon, "http", 4) == 0) + osSubFilename = CPLSPrintf("/vsicurl_streaming/%s", pszIcon); + else + { + osSubFilename = CPLFormFilename(CPLGetPath(pszBaseFilename), pszIcon, NULL); + osSubFilename = KMLRemoveSlash(osSubFilename); + } + + VSILFILE* fp = VSIFOpenL(osSubFilename, "rb"); + if( fp == NULL ) + { + return NULL; + } + GByte* pabyBuffer = (GByte*) VSIMalloc(BUFFER_SIZE); + if( pabyBuffer == NULL ) + { + VSIFCloseL(fp); + return NULL; + } + int nRead = (int)VSIFReadL(pabyBuffer, 1, BUFFER_SIZE, fp); + VSIFCloseL(fp); + if( nRead == BUFFER_SIZE ) + { + CPLFree(pabyBuffer); + return NULL; + } + + static int nInc = 0; + osSubFilename = CPLSPrintf("/vsimem/kmlsuperoverlay/%d_%p", nInc++, pszBaseFilename); + VSIFCloseL(VSIFileFromMemBuffer( osSubFilename, pabyBuffer, nRead, TRUE) ); + + GDALDataset* poDSIcon = (GDALDataset* )GDALOpen(osSubFilename, GA_ReadOnly); + if( poDSIcon == NULL ) + { + VSIUnlink(osSubFilename); + return NULL; + } + + return poDSIcon; +} + + +/************************************************************************/ +/* KmlSuperOverlayComputeDepth() */ +/************************************************************************/ + +static void KmlSuperOverlayComputeDepth(CPLString osFilename, + CPLXMLNode* psDocument, + int& nLevel) +{ + CPLXMLNode* psIter = psDocument->psChild; + while(psIter != NULL) + { + const char* pszHref = NULL; + if( psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "NetworkLink") == 0 && + CPLGetXMLNode(psIter, "Region") != NULL && + (pszHref = CPLGetXMLValue(psIter, "Link.href", NULL)) != NULL ) + { + const char* pszExt = CPLGetExtension(pszHref); + if( EQUAL(pszExt, "kml") ) + { + CPLString osSubFilename; + if( strncmp(pszHref, "http", 4) == 0) + osSubFilename = CPLSPrintf("/vsicurl_streaming/%s", pszHref); + else + { + osSubFilename = CPLFormFilename(CPLGetPath(osFilename), pszHref, NULL), + osSubFilename = KMLRemoveSlash(osSubFilename); + } + + VSILFILE* fp = VSIFOpenL(osSubFilename, "rb"); + if( fp != NULL ) + { + char* pszBuffer = (char*) CPLMalloc(BUFFER_SIZE+1); + int nRead = (int)VSIFReadL(pszBuffer, 1, BUFFER_SIZE, fp); + pszBuffer[nRead] = '\0'; + VSIFCloseL(fp); + if( nRead == BUFFER_SIZE ) + { + CPLFree(pszBuffer); + } + else + { + CPLXMLNode* psNode = CPLParseXMLString(pszBuffer); + CPLFree(pszBuffer); + if( psNode != NULL ) + { + CPLXMLNode* psRegion = NULL; + CPLXMLNode* psNewDocument = NULL; + CPLXMLNode* psGroundOverlay = NULL; + CPLXMLNode* psLink = NULL; + if( KmlSuperOverlayFindRegionStart(psNode, &psRegion, + &psNewDocument, &psGroundOverlay, &psLink) && + psNewDocument != NULL && nLevel < 20 ) + { + nLevel ++; + KmlSuperOverlayComputeDepth(osSubFilename, psNewDocument, nLevel); + } + CPLDestroyXMLNode(psNode); + break; + } + } + } + } + } + psIter = psIter->psNext; + } +} + +/************************************************************************/ +/* KmlSingleDocRasterDataset */ +/************************************************************************/ + +class KmlSingleDocRasterRasterBand; + +struct KmlSingleDocRasterTilesDesc +{ + int nMaxJ_i; /* i index at which a tile with max j is realized */ + int nMaxJ_j; /* j index at which a tile with max j is realized */ + int nMaxI_i; /* i index at which a tile with max i is realized */ + int nMaxI_j; /* j index at which a tile with max i is realized */ + char szExtJ[4]; /* extension of tile at which max j is realized */ + char szExtI[4]; /* extension of tile at which max i is realized */ +}; + +class KmlSingleDocRasterDataset: public GDALDataset +{ + friend class KmlSingleDocRasterRasterBand; + CPLString osDirname; + CPLString osNominalExt; + GDALDataset* poCurTileDS; + double adfGlobalExtents[4]; + double adfGeoTransform[6]; + std::vector apoOverviews; + std::vector aosDescs; + int nLevel; + int nTileSize; + int bHasBuiltOverviews; + int bLockOtherBands; + + protected: + virtual int CloseDependentDatasets(); + + public: + KmlSingleDocRasterDataset(); + ~KmlSingleDocRasterDataset(); + + virtual CPLErr GetGeoTransform( double * padfGeoTransform ) + { + memcpy(padfGeoTransform, adfGeoTransform, 6 * sizeof(double)); + return CE_None; + } + + virtual const char *GetProjectionRef() { return SRS_WKT_WGS84; } + + void BuildOverviews(); + + static GDALDataset* Open(const char* pszFilename, + const CPLString& osFilename, + CPLXMLNode* psNode); +}; + +/************************************************************************/ +/* KmlSingleDocRasterRasterBand */ +/************************************************************************/ + +class KmlSingleDocRasterRasterBand: public GDALRasterBand +{ + public: + KmlSingleDocRasterRasterBand(KmlSingleDocRasterDataset* poDS, + int nBand); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); + + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); +}; + +/************************************************************************/ +/* KmlSingleDocRasterDataset() */ +/************************************************************************/ + +KmlSingleDocRasterDataset::KmlSingleDocRasterDataset() +{ + poCurTileDS = NULL; + nLevel = 0; + nTileSize = 0; + bHasBuiltOverviews = FALSE; + bLockOtherBands = FALSE; +} + +/************************************************************************/ +/* ~KmlSingleDocRasterDataset() */ +/************************************************************************/ + +KmlSingleDocRasterDataset::~KmlSingleDocRasterDataset() +{ + CloseDependentDatasets(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int KmlSingleDocRasterDataset::CloseDependentDatasets() +{ + int bRet = FALSE; + + if( poCurTileDS != NULL ) + { + bRet = TRUE; + GDALClose((GDALDatasetH) poCurTileDS); + poCurTileDS = NULL; + } + if( apoOverviews.size() > 0 ) + { + bRet = TRUE; + for(size_t i = 0; i < apoOverviews.size(); i++) + delete apoOverviews[i]; + apoOverviews.resize(0); + } + + return bRet; +} + +/************************************************************************/ +/* KmlSingleDocGetDimensions() */ +/************************************************************************/ + +static int KmlSingleDocGetDimensions(const CPLString& osDirname, + const KmlSingleDocRasterTilesDesc& oDesc, + int nLevel, + int nTileSize, + int& nXSize, + int& nYSize, + int& nBands, + int& bHasCT) +{ + const char* pszImageFilename = CPLFormFilename( osDirname, + CPLSPrintf("kml_image_L%d_%d_%d", nLevel, + oDesc.nMaxJ_j, + oDesc.nMaxJ_i), + oDesc.szExtJ ); + GDALDataset* poImageDS = (GDALDataset*) GDALOpen(pszImageFilename, GA_ReadOnly); + if( poImageDS == NULL ) + { + return FALSE; + } + int nRightXSize; + int nBottomYSize = poImageDS->GetRasterYSize(); + nBands = poImageDS->GetRasterCount(); + bHasCT = (nBands == 1 && poImageDS->GetRasterBand(1)->GetColorTable() != NULL); + if( oDesc.nMaxJ_j == oDesc.nMaxI_j && + oDesc.nMaxJ_i == oDesc.nMaxI_i) + { + nRightXSize = poImageDS->GetRasterXSize(); + } + else + { + GDALClose( (GDALDatasetH) poImageDS) ; + pszImageFilename = CPLFormFilename( osDirname, + CPLSPrintf("kml_image_L%d_%d_%d", nLevel, + oDesc.nMaxI_j, + oDesc.nMaxI_i), + oDesc.szExtI ); + poImageDS = (GDALDataset*) GDALOpen(pszImageFilename, GA_ReadOnly); + if( poImageDS == NULL ) + { + return FALSE; + } + nRightXSize = poImageDS->GetRasterXSize(); + } + GDALClose( (GDALDatasetH) poImageDS) ; + + nXSize = nRightXSize + oDesc.nMaxI_i * nTileSize; + nYSize = nBottomYSize + oDesc.nMaxJ_j * nTileSize; + return (nXSize > 0 && nYSize > 0); +} + +/************************************************************************/ +/* BuildOverviews() */ +/************************************************************************/ + +void KmlSingleDocRasterDataset::BuildOverviews() +{ + if( bHasBuiltOverviews ) + return; + bHasBuiltOverviews = TRUE; + + for(int k = 2; k <= (int)aosDescs.size(); k++) + { + const KmlSingleDocRasterTilesDesc& oDesc = aosDescs[aosDescs.size()-k]; + int nXSize = 0, nYSize = 0, nTileBands = 0, bHasCT = FALSE; + if( !KmlSingleDocGetDimensions(osDirname, oDesc, (int)aosDescs.size() - k + 1, + nTileSize, + nXSize, nYSize, nTileBands, bHasCT) ) + { + break; + } + + KmlSingleDocRasterDataset* poOvrDS = new KmlSingleDocRasterDataset(); + poOvrDS->nRasterXSize = nXSize; + poOvrDS->nRasterYSize = nYSize; + poOvrDS->nLevel = (int)aosDescs.size() - k + 1; + poOvrDS->nTileSize = nTileSize; + poOvrDS->osDirname = osDirname; + poOvrDS->osNominalExt = oDesc.szExtI; + poOvrDS->adfGeoTransform[0] = adfGlobalExtents[0]; + poOvrDS->adfGeoTransform[1] = (adfGlobalExtents[2] - adfGlobalExtents[0]) / poOvrDS->nRasterXSize; + poOvrDS->adfGeoTransform[2] = 0.0; + poOvrDS->adfGeoTransform[3] = adfGlobalExtents[3]; + poOvrDS->adfGeoTransform[4] = 0.0; + poOvrDS->adfGeoTransform[5] = -(adfGlobalExtents[3] - adfGlobalExtents[1]) / poOvrDS->nRasterXSize; + for(int iBand = 1; iBand <= nBands; iBand ++ ) + poOvrDS->SetBand(iBand, new KmlSingleDocRasterRasterBand(poOvrDS, iBand)); + poOvrDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + + apoOverviews.push_back(poOvrDS); + } +} + +/************************************************************************/ +/* KmlSingleDocRasterRasterBand() */ +/************************************************************************/ + +KmlSingleDocRasterRasterBand::KmlSingleDocRasterRasterBand(KmlSingleDocRasterDataset* poDS, + int nBand) +{ + this->poDS = poDS; + this->nBand = nBand; + nBlockXSize = poDS->nTileSize; + nBlockYSize = poDS->nTileSize; + eDataType = GDT_Byte; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr KmlSingleDocRasterRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + KmlSingleDocRasterDataset* poGDS = (KmlSingleDocRasterDataset*) poDS; + const char* pszImageFilename = CPLFormFilename( poGDS->osDirname, + CPLSPrintf("kml_image_L%d_%d_%d", poGDS->nLevel, nBlockYOff, nBlockXOff), poGDS->osNominalExt ); + if( poGDS->poCurTileDS == NULL || + strcmp(CPLGetFilename(poGDS->poCurTileDS->GetDescription()), + CPLGetFilename(pszImageFilename)) != 0 ) + { + if( poGDS->poCurTileDS != NULL ) GDALClose((GDALDatasetH) poGDS->poCurTileDS); + CPLPushErrorHandler(CPLQuietErrorHandler); + poGDS->poCurTileDS = (GDALDataset*) GDALOpen(pszImageFilename, GA_ReadOnly); + CPLPopErrorHandler(); + } + GDALDataset* poImageDS = poGDS->poCurTileDS; + if( poImageDS == NULL ) + { + memset( pImage, 0, nBlockXSize * nBlockYSize ); + return CE_None; + } + int nXSize = poImageDS->GetRasterXSize(); + int nYSize = poImageDS->GetRasterYSize(); + + int nReqXSize = nBlockXSize; + if( nBlockXOff * nBlockXSize + nReqXSize > nRasterXSize ) + nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize; + int nReqYSize = nBlockYSize; + if( nBlockYOff * nBlockYSize + nReqYSize > nRasterYSize ) + nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize; + + if( nXSize != nReqXSize || nYSize != nReqYSize ) + { + CPLDebug("KMLSUPEROVERLAY", "Tile %s, dimensions %dx%d, expected %dx%d", + pszImageFilename, nXSize, nYSize, nReqXSize, nReqYSize); + return CE_Failure; + } + + CPLErr eErr = CE_Failure; + if( poImageDS->GetRasterCount() == 1 ) + { + GDALColorTable* poColorTable = poImageDS->GetRasterBand(1)->GetColorTable(); + if( nBand == 4 && poColorTable == NULL ) + { + /* Add fake alpha band */ + memset( pImage, 255, nBlockXSize * nBlockYSize ); + eErr = CE_None; + } + else + { + eErr = poImageDS->GetRasterBand(1)->RasterIO(GF_Read, + 0, 0, nXSize, nYSize, + pImage, + nXSize, nYSize, + GDT_Byte, 1, nBlockXSize, NULL); + + /* Expand color table */ + if( eErr == CE_None && poColorTable != NULL ) + { + int j, i; + for(j = 0; j < nReqYSize; j++ ) + { + for(i = 0; i < nReqXSize; i++ ) + { + GByte nVal = ((GByte*) pImage)[j * nBlockXSize + i]; + const GDALColorEntry * poEntry = poColorTable->GetColorEntry(nVal); + if( poEntry != NULL ) + { + if( nBand == 1 ) + ((GByte*) pImage)[j * nBlockXSize + i] = poEntry->c1; + else if( nBand == 2 ) + ((GByte*) pImage)[j * nBlockXSize + i] = poEntry->c2; + else if( nBand == 3 ) + ((GByte*) pImage)[j * nBlockXSize + i] = poEntry->c3; + else + ((GByte*) pImage)[j * nBlockXSize + i] = poEntry->c4; + } + } + } + } + } + } + else if( nBand <= poImageDS->GetRasterCount() ) + { + eErr = poImageDS->GetRasterBand(nBand)->RasterIO(GF_Read, + 0, 0, nXSize, nYSize, + pImage, + nXSize, nYSize, + GDT_Byte, 1, nBlockXSize, NULL); + } + else if( nBand == 4 && poImageDS->GetRasterCount() == 3 ) + { + /* Add fake alpha band */ + memset( pImage, 255, nBlockXSize * nBlockYSize ); + eErr = CE_None; + } + + /* Cache other bands */ + if( !poGDS->bLockOtherBands ) + { + poGDS->bLockOtherBands = TRUE; + for(int iBand = 1; iBand <= poGDS->nBands; iBand ++ ) + { + if( iBand != nBand ) + { + KmlSingleDocRasterRasterBand* poOtherBand = + (KmlSingleDocRasterRasterBand*)poGDS->GetRasterBand(iBand); + GDALRasterBlock* poBlock = poOtherBand-> + GetLockedBlockRef(nBlockXOff, nBlockYOff); + if( poBlock == NULL ) + continue; + poBlock->DropLock(); + } + } + poGDS->bLockOtherBands = FALSE; + } + + return eErr; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp KmlSingleDocRasterRasterBand::GetColorInterpretation() +{ + return (GDALColorInterp)(GCI_RedBand + nBand - 1); +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int KmlSingleDocRasterRasterBand::GetOverviewCount() +{ + KmlSingleDocRasterDataset* poGDS = (KmlSingleDocRasterDataset*) poDS; + poGDS->BuildOverviews(); + + return (int)poGDS->apoOverviews.size(); +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *KmlSingleDocRasterRasterBand::GetOverview(int iOvr) +{ + KmlSingleDocRasterDataset* poGDS = (KmlSingleDocRasterDataset*) poDS; + poGDS->BuildOverviews(); + + if( iOvr < 0 || iOvr >= (int)poGDS->apoOverviews.size() ) + return NULL; + + return poGDS->apoOverviews[iOvr]->GetRasterBand(nBand); +} + +/************************************************************************/ +/* KmlSingleDocCollectTiles() */ +/************************************************************************/ + +static void KmlSingleDocCollectTiles(CPLXMLNode* psNode, + std::vector& aosDescs, + CPLString& osURLBase) +{ + if( strcmp(psNode->pszValue, "href") == 0 ) + { + int level, j, i; + char szExt[4]; + const char* pszHref = CPLGetXMLValue(psNode, "", ""); + if( strncmp(pszHref, "http", 4) == 0 ) + { + osURLBase = CPLGetPath(pszHref); + } + if( sscanf(CPLGetFilename(pszHref), "kml_image_L%d_%d_%d.%3s", + &level, &j, &i, szExt) == 4 ) + { + if( level > (int)aosDescs.size() ) + { + KmlSingleDocRasterTilesDesc sDesc; + while( level > (int)aosDescs.size() + 1 ) + { + sDesc.nMaxJ_i = -1; + sDesc.nMaxJ_j = -1; + sDesc.nMaxI_i = -1; + sDesc.nMaxI_j = -1; + strcpy(sDesc.szExtI, ""); + strcpy(sDesc.szExtJ, ""); + aosDescs.push_back(sDesc); + } + + sDesc.nMaxJ_j = j; + sDesc.nMaxJ_i = i; + strcpy(sDesc.szExtJ, szExt); + sDesc.nMaxI_j = j; + sDesc.nMaxI_i = i; + strcpy(sDesc.szExtI, szExt); + aosDescs.push_back(sDesc); + } + else + { + /* 2010_USACE_JALBTCX_Louisiana_Mississippi_Lidar.kmz has not a lower-right tile */ + /* so the right most tile and the bottom most tile might be different */ + if( (j > aosDescs[level-1].nMaxJ_j) || + (j == aosDescs[level-1].nMaxJ_j && + i > aosDescs[level-1].nMaxJ_i) ) + { + aosDescs[level-1].nMaxJ_j = j; + aosDescs[level-1].nMaxJ_i = i; + strcpy(aosDescs[level-1].szExtJ, szExt); + } + if( i > aosDescs[level-1].nMaxI_i || + (i == aosDescs[level-1].nMaxI_i && + j > aosDescs[level-1].nMaxI_j) ) + { + aosDescs[level-1].nMaxI_j = j; + aosDescs[level-1].nMaxI_i = i; + strcpy(aosDescs[level-1].szExtI, szExt); + } + } + } + } + else + { + CPLXMLNode* psIter = psNode->psChild; + while(psIter != NULL) + { + if( psIter->eType == CXT_Element ) + KmlSingleDocCollectTiles(psIter, aosDescs, osURLBase); + psIter = psIter->psNext; + } + } +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +/* Read raster with a structure like http://opentopo.sdsc.edu/files/Haiti/NGA_Haiti_LiDAR2.kmz */ +/* i.e. made of a doc.kml that list all tiles at all overview levels */ +/* The tile name pattern is "kml_image_L{level}_{j}_{i}.{png|jpg}" */ +GDALDataset* KmlSingleDocRasterDataset::Open(const char* pszFilename, + const CPLString& osFilename, + CPLXMLNode* psRoot) +{ + CPLXMLNode* psRootFolder = CPLGetXMLNode(psRoot, "=kml.Document.Folder"); + if( psRootFolder == NULL ) + return NULL; + const char* pszRootFolderName = + CPLGetXMLValue(psRootFolder, "name", ""); + if( strcmp(pszRootFolderName, "kml_image_L1_0_0") != 0 ) + return NULL; + + double adfGlobalExtents[4]; + CPLXMLNode* psRegion = CPLGetXMLNode(psRootFolder, "Region"); + if( psRegion == NULL ) + return NULL; + if( !KmlSuperOverlayGetBoundingBox(psRegion, adfGlobalExtents) ) + return NULL; + + std::vector aosDescs; + CPLString osDirname = CPLGetPath(osFilename); + KmlSingleDocCollectTiles(psRootFolder, aosDescs, osDirname); + if( aosDescs.size() == 0 ) + return NULL; + int k; + for(k = 0; k < (int)aosDescs.size(); k++) + { + if( aosDescs[k].nMaxJ_i < 0 ) + return NULL; + } + + const char* pszImageFilename = CPLFormFilename( osDirname, + CPLSPrintf("kml_image_L%d_%d_%d", (int)aosDescs.size(), 0, 0), aosDescs[aosDescs.size()-1].szExtI); + GDALDataset* poImageDS = (GDALDataset*) GDALOpen(pszImageFilename, GA_ReadOnly); + if( poImageDS == NULL ) + { + return NULL; + } + int nTileSize = poImageDS->GetRasterXSize(); + if( nTileSize != poImageDS->GetRasterYSize() ) + { + nTileSize = 1024; + } + GDALClose( (GDALDatasetH) poImageDS) ; + + const KmlSingleDocRasterTilesDesc& oDesc = aosDescs[aosDescs.size()-1]; + int nXSize = 0, nYSize = 0, nBands = 0, bHasCT = FALSE; + if( !KmlSingleDocGetDimensions(osDirname, oDesc, (int)aosDescs.size(), nTileSize, + nXSize, nYSize, nBands, bHasCT) ) + { + return NULL; + } + + KmlSingleDocRasterDataset* poDS = new KmlSingleDocRasterDataset(); + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->nLevel = (int)aosDescs.size(); + poDS->nTileSize = nTileSize; + poDS->osDirname = osDirname; + poDS->osNominalExt = oDesc.szExtI; + memcpy(poDS->adfGlobalExtents, adfGlobalExtents, 4 * sizeof(double)); + poDS->adfGeoTransform[0] = adfGlobalExtents[0]; + poDS->adfGeoTransform[1] = (adfGlobalExtents[2] - adfGlobalExtents[0]) / poDS->nRasterXSize; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = adfGlobalExtents[3]; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -(adfGlobalExtents[3] - adfGlobalExtents[1]) / poDS->nRasterYSize; + if( nBands == 1 && bHasCT ) nBands = 4; + for(int iBand = 1; iBand <= nBands; iBand ++ ) + poDS->SetBand(iBand, new KmlSingleDocRasterRasterBand(poDS, iBand)); + poDS->SetDescription(pszFilename); + poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + poDS->aosDescs = aosDescs; + + return poDS; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *KmlSuperOverlayReadDataset::Open(const char* pszFilename, + KmlSuperOverlayReadDataset* poParent, + int nRec) + +{ + if( nRec == 2 ) + return NULL; + CPLString osFilename(pszFilename); + const char* pszExt = CPLGetExtension(pszFilename); + if( EQUAL(pszExt, "kmz") ) + { + if( strncmp(pszFilename, "/vsizip/", 8) != 0 ) + osFilename = CPLSPrintf("/vsizip/%s", pszFilename); + char** papszFiles = CPLReadDir(osFilename); + if( papszFiles == NULL ) + return NULL; + char** papszIter = papszFiles; + for(; *papszIter != NULL; papszIter ++) + { + pszExt = CPLGetExtension(*papszIter); + if( EQUAL(pszExt, "kml") ) + { + osFilename = CPLFormFilename(osFilename, *papszIter, NULL); + osFilename = KMLRemoveSlash(osFilename); + break; + } + } + CSLDestroy(papszFiles); + } + VSILFILE* fp = VSIFOpenL(osFilename, "rb"); + if( fp == NULL ) + return NULL; + char* pszBuffer = (char*) CPLMalloc(BUFFER_SIZE+1); + int nRead = (int)VSIFReadL(pszBuffer, 1, BUFFER_SIZE, fp); + pszBuffer[nRead] = '\0'; + VSIFCloseL(fp); + if( nRead == BUFFER_SIZE ) + { + CPLFree(pszBuffer); + return NULL; + } + + CPLXMLNode* psNode = CPLParseXMLString(pszBuffer); + CPLFree(pszBuffer); + if( psNode == NULL ) + return NULL; + + CPLXMLNode* psRegion = NULL; + CPLXMLNode* psDocument = NULL; + CPLXMLNode* psGroundOverlay = NULL; + CPLXMLNode* psLink = NULL; + if( !KmlSuperOverlayFindRegionStart(psNode, &psRegion, + &psDocument, &psGroundOverlay, &psLink) ) + { + GDALDataset* psDS = KmlSingleDocRasterDataset::Open(pszFilename, + osFilename, + psNode); + CPLDestroyXMLNode(psNode); + return psDS; + } + + if( psLink != NULL ) + { + const char* pszHref = CPLGetXMLValue(psLink, "href", NULL); + if( pszHref == NULL || !EQUAL(CPLGetExtension(pszHref), "kml") ) + { + CPLDestroyXMLNode(psNode); + return NULL; + } + + CPLString osSubFilename; + if( strncmp(pszHref, "http", 4) == 0) + osSubFilename = CPLSPrintf("/vsicurl_streaming/%s", pszHref); + else + { + osSubFilename = CPLFormFilename(CPLGetPath(osFilename), pszHref, NULL); + osSubFilename = KMLRemoveSlash(osSubFilename); + } + + CPLString osOverlayName, osOverlayDescription; + psDocument = CPLGetXMLNode(psNode, "=kml.Document"); + if( psDocument ) + { + const char* pszOverlayName = CPLGetXMLValue(psDocument, "name", NULL); + if( pszOverlayName != NULL && + strcmp(pszOverlayName, CPLGetBasename(pszFilename)) != 0 ) + { + osOverlayName = pszOverlayName; + } + const char* pszOverlayDescription = CPLGetXMLValue(psDocument, "description", NULL); + if( pszOverlayDescription != NULL ) + { + osOverlayDescription = pszOverlayDescription; + } + } + + CPLDestroyXMLNode(psNode); + + // FIXME + GDALDataset* poDS = Open(osSubFilename, poParent, nRec + 1); + if( poDS != NULL ) + { + poDS->SetDescription(pszFilename); + + if( osOverlayName.size() ) + { + poDS->SetMetadataItem( "NAME", osOverlayName); + } + if( osOverlayDescription.size() ) + { + poDS->SetMetadataItem( "DESCRIPTION", osOverlayDescription); + } + } + + return poDS; + } + + CPLAssert(psDocument != NULL); + CPLAssert(psGroundOverlay != NULL); + CPLAssert(psRegion != NULL); + + double adfExtents[4]; + if( !KmlSuperOverlayGetBoundingBox(psGroundOverlay, adfExtents) ) + { + CPLDestroyXMLNode(psNode); + return NULL; + } + + const char* pszIcon = CPLGetXMLValue(psGroundOverlay, "Icon.href", NULL); + if( pszIcon == NULL ) + { + CPLDestroyXMLNode(psNode); + return NULL; + } + GDALDataset* poDSIcon = KmlSuperOverlayLoadIcon(pszFilename, pszIcon); + if( poDSIcon == NULL ) + { + CPLDestroyXMLNode(psNode); + return NULL; + } + + int nFactor; + if( poParent != NULL ) + nFactor = poParent->nFactor / 2; + else + { + int nDepth = 0; + KmlSuperOverlayComputeDepth(pszFilename, psDocument, nDepth); + nFactor = 1 << nDepth; + } + + KmlSuperOverlayReadDataset* poDS = new KmlSuperOverlayReadDataset(); + poDS->osFilename = pszFilename; + poDS->psRoot = psNode; + poDS->psDocument = psDocument; + poDS->poDSIcon = poDSIcon; + poDS->poParent = poParent; + poDS->nFactor = nFactor; + poDS->nRasterXSize = nFactor * poDSIcon->GetRasterXSize(); + poDS->nRasterYSize = nFactor * poDSIcon->GetRasterYSize(); + poDS->adfGeoTransform[0] = adfExtents[0]; + poDS->adfGeoTransform[1] = (adfExtents[2] - adfExtents[0]) / poDS->nRasterXSize; + poDS->adfGeoTransform[3] = adfExtents[3]; + poDS->adfGeoTransform[5] = -(adfExtents[3] - adfExtents[1]) / poDS->nRasterYSize; + poDS->nBands = 4; + for(int i=0;i<4;i++) + poDS->SetBand(i+1, new KmlSuperOverlayRasterBand(poDS, i+1)); + poDS->SetDescription(pszFilename); + poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + + while( poDS->poParent == NULL && nFactor > 1 ) + { + nFactor /= 2; + + KmlSuperOverlayReadDataset* poOvrDS = new KmlSuperOverlayReadDataset(); + + poDS->papoOverviewDS = (KmlSuperOverlayReadDataset**) CPLRealloc( + poDS->papoOverviewDS, (poDS->nOverviewCount + 1) * sizeof(KmlSuperOverlayReadDataset*)); + poDS->papoOverviewDS[poDS->nOverviewCount ++] = poOvrDS; + + poOvrDS->bIsOvr = TRUE; + poOvrDS->poParent = poDS; + poOvrDS->nFactor = nFactor; + poOvrDS->nRasterXSize = nFactor * poDSIcon->GetRasterXSize(); + poOvrDS->nRasterYSize = nFactor * poDSIcon->GetRasterYSize(); + poOvrDS->adfGeoTransform[0] = adfExtents[0]; + poOvrDS->adfGeoTransform[1] = (adfExtents[2] - adfExtents[0]) / poOvrDS->nRasterXSize; + poOvrDS->adfGeoTransform[3] = adfExtents[3]; + poOvrDS->adfGeoTransform[5] = -(adfExtents[3] - adfExtents[1]) / poOvrDS->nRasterYSize; + poOvrDS->nBands = 4; + for(int i=0;i<4;i++) + poOvrDS->SetBand(i+1, new KmlSuperOverlayRasterBand(poOvrDS, i+1)); + poOvrDS->SetDescription(pszFilename); + poOvrDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + } + + return poDS; +} + +/************************************************************************/ +/* KmlSuperOverlayDatasetDelete() */ +/************************************************************************/ + +static CPLErr KmlSuperOverlayDatasetDelete(CPL_UNUSED const char* fileName) +{ + /* Null implementation, so that people can Delete("MEM:::") */ + return CE_None; +} + +/************************************************************************/ +/* GDALRegister_KMLSUPEROVERLAY() */ +/************************************************************************/ + +void GDALRegister_KMLSUPEROVERLAY() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "KMLSUPEROVERLAY" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "KMLSUPEROVERLAY" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Kml Super Overlay" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 Float32 Float64 CInt16 CInt32 CFloat32 CFloat64" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" " +" " ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = KmlSuperOverlayReadDataset::Identify; + poDriver->pfnOpen = KmlSuperOverlayReadDataset::Open; + poDriver->pfnCreateCopy = KmlSuperOverlayCreateCopy; + poDriver->pfnDelete = KmlSuperOverlayDatasetDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/kmlsuperoverlay/kmlsuperoverlaydataset.h b/bazaar/plugin/gdal/frmts/kmlsuperoverlay/kmlsuperoverlaydataset.h new file mode 100644 index 000000000..724c8b851 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kmlsuperoverlay/kmlsuperoverlaydataset.h @@ -0,0 +1,126 @@ +/****************************************************************************** + * $Id: kmlsuperoverlaydataset.h + * + * Project: KmlSuperOverlay + * Purpose: Implements write support for KML superoverlay - KMZ. + * Author: Harsh Govind, harsh.govind@spadac.com + * + ****************************************************************************** + * Copyright (c) 2010, SPADAC Inc. + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef KMLSUPEROVERLAYDATASET_H_INCLUDED +#define KMLSUPEROVERLAYDATASET_H_INCLUDED + +#include "gdal_pam.h" +#include "gdal_priv.h" +#include "cpl_minixml.h" +#include + +CPL_C_START +void CPL_DLL GDALRegister_KMLSUPEROVERLAY(void); +CPL_C_END + +/************************************************************************/ +/* KmlSuperOverlayReadDataset */ +/************************************************************************/ +class KmlSuperOverlayRasterBand; +class KmlSuperOverlayReadDataset; + +class LinkedDataset; +class LinkedDataset +{ + public: + KmlSuperOverlayReadDataset* poDS; + LinkedDataset* psPrev; + LinkedDataset* psNext; + CPLString osSubFilename; +}; + +class KmlSuperOverlayReadDataset : public GDALDataset +{ + friend class KmlSuperOverlayRasterBand; + + int nFactor; + CPLString osFilename; + CPLXMLNode *psRoot; + CPLXMLNode *psDocument; + GDALDataset *poDSIcon; + double adfGeoTransform[6]; + + int nOverviewCount; + KmlSuperOverlayReadDataset** papoOverviewDS; + int bIsOvr; + + KmlSuperOverlayReadDataset* poParent; + + std::map oMapChildren; + LinkedDataset *psFirstLink; + LinkedDataset *psLastLink; + + protected: + virtual int CloseDependentDatasets(); + + public: + KmlSuperOverlayReadDataset(); + virtual ~KmlSuperOverlayReadDataset(); + + static int Identify(GDALOpenInfo *); + static GDALDataset *Open(const char* pszFilename, KmlSuperOverlayReadDataset* poParent = NULL, int nRec = 0); + static GDALDataset *Open(GDALOpenInfo *); + + virtual CPLErr GetGeoTransform( double * ); + virtual const char *GetProjectionRef(); + + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); +}; + +/************************************************************************/ +/* KmlSuperOverlayRasterBand */ +/************************************************************************/ + +class KmlSuperOverlayRasterBand: public GDALRasterBand +{ + public: + KmlSuperOverlayRasterBand(KmlSuperOverlayReadDataset* poDS, int nBand); + protected: + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); + virtual GDALColorInterp GetColorInterpretation(); + + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); +}; + +#endif /* ndef KMLSUPEROVERLAYDATASET_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/frmts/kmlsuperoverlay/makefile.vc b/bazaar/plugin/gdal/frmts/kmlsuperoverlay/makefile.vc new file mode 100644 index 000000000..0457b749a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/kmlsuperoverlay/makefile.vc @@ -0,0 +1,20 @@ +OBJ = kmlsuperoverlaydataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + $(INSTALL) *.obj ..\o + +all: default + +clean: + -del *.obj + -del *.dll + -del *.exp + -del *.lib + -del *.manifest + -del *.pdb + -del *.ilk + diff --git a/bazaar/plugin/gdal/frmts/l1b/GNUmakefile b/bazaar/plugin/gdal/frmts/l1b/GNUmakefile new file mode 100644 index 000000000..df52ea6fa --- /dev/null +++ b/bazaar/plugin/gdal/frmts/l1b/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = l1bdataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/l1b/frmt_l1b.html b/bazaar/plugin/gdal/frmts/l1b/frmt_l1b.html new file mode 100644 index 000000000..95da65ddf --- /dev/null +++ b/bazaar/plugin/gdal/frmts/l1b/frmt_l1b.html @@ -0,0 +1,161 @@ + + +L1B -- NOAA Polar Orbiter Level 1b Data Set (AVHRR) + + + + +

    L1B -- NOAA Polar Orbiter Level 1b Data Set (AVHRR)

    + +GDAL supports NOAA Polar Orbiter Level 1b Data Set format for reading. Now it can +read NOAA-9(F) --- NOAA-17(M) datasets. NOTE: only AVHRR instrument supported +now, if you want read data from other instruments, write to me (Andrey Kiselev, +dron@ak4719.spb.edu). AVHRR +LAC/HRPT (1 km resolution) and GAC (4 km resolution) should be processed +correctly. + +

    Georeference

    + +

    +Note, that GDAL simple affine georeference model completely unsuitable for +the NOAA data. So you should not rely on it. It is recommended to use the +thin plate spline warper (tps). Automatic image rectification can be done with +ground control points (GCPs) from the input file. +

    +

    +NOAA stores 51 GCPs per scanline both in the LAC and GAC datasets. In fact +you may get less than 51 GCPs, especially at end of scanlines. Another approach +to rectification is manual selection of the GCPs using external source of +georeference information. +

    +

    +Before GDAL 1.10.2, a maximum of 11 x 20 GCPs were reported. This might be +unsuitable for correct warping. Starting with GDAL 1.10.2, a much higher density +will be reported, unless the L1B_HIGH_GCP_DENSITY configuration option is set +to NO. +

    +

    +Precision of the GCPs determination depends from the satellite type. In the +NOAA-9 -- NOAA-14 datasets geographic coordinates of the GCPs stored in +integer values as a 128th of a degree. So we can't determine positions more +precise than 1/128=0.0078125 of degree (~28"). In NOAA-15 -- NOAA-17 datasets +we have much more precise positions, they are stored as 10000th of degree. +

    +

    +Starting with GDAL 1.11, the GCPs will also be reported as a +geolocation array, +with Lagrangian interpolation of the 51 GCPs per scanline to the number of pixels +per scanline width. +

    +

    +Image will be always returned with most northern scanline located at the top of +image. If you want determine actual direction of the satellite moving you +should look at LOCATION metadata record. +

    + +

    Data

    + +In case of NOAA-10 in channel 5 you will get repeated channel 4 data. +
    +AVHRR/3 instrument (NOAA-15 -- NOAA-17) is a six channel radiometer, but only +five channels are transmitted to the ground at any given time. Channels 3A and +3B cannot operate simultaneously. Look at channel description field reported +by gdalinfo to determine what kind of channel contained in processed file. + +

    Metadata

    + +Several parameters, obtained from the dataset stored as metadata records.

    + +Metadata records:

    + +

      + +
    • SATELLITE: Satellite name

      + +

    • DATA_TYPE: Type of the data, stored in the Level 1b dataset (AVHRR +HRPT/LAC/GAC).

      + +

    • REVOLUTION: Orbit number. Note that it can be 1 to 2 off the +correct orbit number (according to documentation).

      + +

    • SOURCE: Receiving station name.

      + +

    • PROCESSING_CENTER: Name of data processing center.

      + +

    • START: Time of first scanline acquisition (year, day of year, +millisecond of day).

      + +

    • STOP: Time of last scanline acquisition (year, day of year, +millisecond of day).

      + +

    • LOCATION: AVHRR Earth location indication. Will be Ascending +when satellite moves from low latitudes to high latitudes and Descending +in other case.

      +

    + +Starting with GDAL 1.11, most metadata records can be written to a .CSV file when +the L1B_FETCH_METADATA configuration file is set to YES. By default, the filename +will be called "[l1b_dataset_name]_metadata.csv", and located in the +same directory as the L1B dataset. By defining the L1B_METADATA_DIRECTORY configuration +option, it is possible to create that file in another directory. +The documentation to interpret those metadata is +PODUG 3.1 for NOAA <=14 and +KLM 8.3.1.3.3.1 for NOAA >=15. +

    + +

    Subdatasets

    + +(Starting with GDAL 1.11)

    + +NOAA <=14 datasets advertize a L1B_SOLAR_ZENITH_ANGLES:"l1b_dataset_name" +subdataset that contains a maximum of 51 solar zenith angles for each scanline ( +beginning at sample 5 with a step of 8 samples for GAC data, beginning at sample +25 with a step of 40 samples for HRPT/LAC/FRAC data).

    + +NOAA >=15 datasets advertize a L1B_ANGLES:"l1b_dataset_name" +subdataset that contains 3 bands (solar zenith angles, satellite zenith angles +and relative azimuth angles) with 51 values for each scanline ( +beginning at sample 5 with a step of 8 samples for GAC data, beginning at sample +25 with a step of 40 samples for HRPT/LAC/FRAC data).

    + +NOAA >=15 datasets advertize a L1B_CLOUDS:"l1b_dataset_name" +subdataset that contains a band of same dimensions as bands of the main L1B +dataset. The values of each pixel are 0 = unknown; 1 = clear; 2 = cloudy; 3 = partly cloudy.

    + +

    Nodata mask

    + +(Starting with GDAL 2.0)

    + +NOAA >=15 datasets that report in their header to have missing scan lines +will expose a per-dataset mask band (following +RFC 15: Band Masks) +to indicate such scan lines. + +

    See Also:

    + +
      +
    • Implemented as gdal/frmts/l1b/l1bdataset.cpp.

      + +

    • NOAA Polar Orbiter Level 1b Data Set documented in the ``POD User's +Guide'' (TIROS-N -- NOAA-14 satellites) and in the ``NOAA KLM User's Guide'' +(NOAA-15 -- NOAA-16 satellites). You can find this manuals at + +NOAA Technical Documentation Introduction Page +

      + +

    • Excellent and complete review contained in the printed book ``The +Advanced Very High Resolution Radiometer (AVHRR)'' by Arthur P. Cracknell, +Taylor and Francis Ltd., 1997, ISBN 0-7484-0209-8. +

      + +

    • NOAA data can be downloaded from the +Comprehensive Large Array-data Stewardship System (CLASS) +(former SAA). Actually it is only source of Level 1b datasets for me, so my implementation +tested with that files only.

      + +

    • NOAA spacecrafts status page + +
    + + + diff --git a/bazaar/plugin/gdal/frmts/l1b/l1bdataset.cpp b/bazaar/plugin/gdal/frmts/l1b/l1bdataset.cpp new file mode 100644 index 000000000..3988acb40 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/l1b/l1bdataset.cpp @@ -0,0 +1,3539 @@ +/****************************************************************************** + * $Id: l1bdataset.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: NOAA Polar Orbiter Level 1b Dataset Reader (AVHRR) + * Purpose: Can read NOAA-9(F)-NOAA-17(M) AVHRR datasets + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + * Some format info at: http://www.sat.dundee.ac.uk/noaa1b.html + * + ****************************************************************************** + * Copyright (c) 2002, Andrey Kiselev + * Copyright (c) 2008-2013, Even Rouault + * + * ---------------------------------------------------------------------------- + * Lagrange interpolation suitable for NOAA level 1B file formats. + * Submitted by Andrew Brooks + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: l1bdataset.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +CPL_C_START +void GDALRegister_L1B(void); +CPL_C_END + +typedef enum { // File formats + L1B_NONE, // Not a L1B format + L1B_NOAA9, // NOAA-9/14 + L1B_NOAA15, // NOAA-15/METOP-2 + L1B_NOAA15_NOHDR // NOAA-15/METOP-2 without ARS header +} L1BFileFormat; + +typedef enum { // Spacecrafts: + TIROSN, // TIROS-N + // NOAA are given a letter before launch and a number after launch + NOAA6, // NOAA-6(A) + NOAAB, // NOAA-B + NOAA7, // NOAA-7(C) + NOAA8, // NOAA-8(E) + NOAA9_UNKNOWN, // Some NOAA-18 and NOAA-19 HRPT are recognized like that... + NOAA9, // NOAA-9(F) + NOAA10, // NOAA-10(G) + NOAA11, // NOAA-11(H) + NOAA12, // NOAA-12(D) + NOAA13, // NOAA-13(I) + NOAA14, // NOAA-14(J) + NOAA15, // NOAA-15(K) + NOAA16, // NOAA-16(L) + NOAA17, // NOAA-17(M) + NOAA18, // NOAA-18(N) + NOAA19, // NOAA-19(N') + // MetOp are given a number before launch and a letter after launch + METOP2, // METOP-A(2) + METOP1, // METOP-B(1) + METOP3, // METOP-C(3) +} L1BSpaceCraftdID; + +typedef enum { // Product types + HRPT, + LAC, + GAC, + FRAC +} L1BProductType; + +typedef enum { // Data format + PACKED10BIT, + UNPACKED8BIT, + UNPACKED16BIT +} L1BDataFormat; + +typedef enum { // Receiving stations names: + DU, // Dundee, Scotland, UK + GC, // Fairbanks, Alaska, USA (formerly Gilmore Creek) + HO, // Honolulu, Hawaii, USA + MO, // Monterey, California, USA + WE, // Western Europe CDA, Lannion, France + SO, // SOCC (Satellite Operations Control Center), Suitland, Maryland, USA + WI, // Wallops Island, Virginia, USA + SV, // Svalbard, Norway + UNKNOWN_STATION +} L1BReceivingStation; + +typedef enum { // Data processing centers: + CMS, // Centre de Meteorologie Spatiale - Lannion, France + DSS, // Dundee Satellite Receiving Station - Dundee, Scotland, UK + NSS, // NOAA/NESDIS - Suitland, Maryland, USA + UKM, // United Kingdom Meteorological Office - Bracknell, England, UK + UNKNOWN_CENTER +} L1BProcessingCenter; + +typedef enum { // AVHRR Earth location indication + ASCEND, + DESCEND +} L1BAscendOrDescend; + +/************************************************************************/ +/* AVHRR band widths */ +/************************************************************************/ + +static const char *apszBandDesc[] = +{ + // NOAA-7 -- METOP-2 channels + "AVHRR Channel 1: 0.58 micrometers -- 0.68 micrometers", + "AVHRR Channel 2: 0.725 micrometers -- 1.10 micrometers", + "AVHRR Channel 3: 3.55 micrometers -- 3.93 micrometers", + "AVHRR Channel 4: 10.3 micrometers -- 11.3 micrometers", + "AVHRR Channel 5: 11.5 micrometers -- 12.5 micrometers", // not in NOAA-6,-8,-10 + // NOAA-13 + "AVHRR Channel 5: 11.4 micrometers -- 12.4 micrometers", + // NOAA-15 -- METOP-2 + "AVHRR Channel 3A: 1.58 micrometers -- 1.64 micrometers", + "AVHRR Channel 3B: 3.55 micrometers -- 3.93 micrometers" + }; + +/************************************************************************/ +/* L1B file format related constants */ +/************************************************************************/ + +#define L1B_DATASET_NAME_SIZE 42 // Length of the string containing + // dataset name +#define L1B_NOAA9_HEADER_SIZE 122 // Terabit memory (TBM) header length +#define L1B_NOAA9_HDR_NAME_OFF 30 // Dataset name offset +#define L1B_NOAA9_HDR_SRC_OFF 70 // Receiving station name offset +#define L1B_NOAA9_HDR_CHAN_OFF 97 // Selected channels map offset +#define L1B_NOAA9_HDR_CHAN_SIZE 20 // Length of selected channels map +#define L1B_NOAA9_HDR_WORD_OFF 117 // Sensor data word size offset + +#define L1B_NOAA15_HEADER_SIZE 512 // Archive Retrieval System (ARS) + // header +#define L1B_NOAA15_HDR_CHAN_OFF 97 // Selected channels map offset +#define L1B_NOAA15_HDR_CHAN_SIZE 20 // Length of selected channels map +#define L1B_NOAA15_HDR_WORD_OFF 117 // Sensor data word size offset + +#define L1B_NOAA9_HDR_REC_SIZE 146 // Length of header record + // filled with the data +#define L1B_NOAA9_HDR_REC_ID_OFF 0 // Spacecraft ID offset +#define L1B_NOAA9_HDR_REC_PROD_OFF 1 // Data type offset +#define L1B_NOAA9_HDR_REC_DSTAT_OFF 34 // DACS status offset + +/* See http://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/klm/html/c8/sec83132-2.htm */ +#define L1B_NOAA15_HDR_REC_SIZE 992 // Length of header record + // filled with the data +#define L1B_NOAA15_HDR_REC_SITE_OFF 0 // Dataset creation site ID offset +#define L1B_NOAA15_HDR_REC_FORMAT_VERSION_OFF 4 // NOAA Level 1b Format Version Number +#define L1B_NOAA15_HDR_REC_FORMAT_VERSION_YEAR_OFF 6 // Level 1b Format Version Year (e.g., 1999) +#define L1B_NOAA15_HDR_REC_FORMAT_VERSION_DAY_OFF 8 // Level 1b Format Version Day of Year (e.g., 365) +#define L1B_NOAA15_HDR_REC_LOGICAL_REC_LENGTH_OFF 10 // Logical Record Length of source Level 1b data set prior to processing +#define L1B_NOAA15_HDR_REC_BLOCK_SIZE_OFF 12 // Block Size of source Level 1b data set prior to processing +#define L1B_NOAA15_HDR_REC_HDR_REC_COUNT_OFF 14 // Count of Header Records in this Data Set +#define L1B_NOAA15_HDR_REC_NAME_OFF 22 // Dataset name +#define L1B_NOAA15_HDR_REC_ID_OFF 72 // Spacecraft ID offset +#define L1B_NOAA15_HDR_REC_PROD_OFF 76 // Data type offset +#define L1B_NOAA15_HDR_REC_STAT_OFF 116 // Instrument status offset +#define L1B_NOAA15_HDR_REC_DATA_RECORD_COUNT_OFF 128 +#define L1B_NOAA15_HDR_REC_CALIBRATED_SCANLINE_COUNT_OFF 130 +#define L1B_NOAA15_HDR_REC_MISSING_SCANLINE_COUNT_OFF 132 +#define L1B_NOAA15_HDR_REC_SRC_OFF 154 // Receiving station name offset +#define L1B_NOAA15_HDR_REC_ELLIPSOID_OFF 328 + +/* This only apply if L1B_HIGH_GCP_DENSITY is explicitly set to NO */ +/* otherwise we will report more GCPs */ +#define DESIRED_GCPS_PER_LINE 11 +#define DESIRED_LINES_OF_GCPS 20 + +// Fixed values used to scale GCPs coordinates in AVHRR records +#define L1B_NOAA9_GCP_SCALE 128.0 +#define L1B_NOAA15_GCP_SCALE 10000.0 + +/************************************************************************/ +/* ==================================================================== */ +/* TimeCode (helper class) */ +/* ==================================================================== */ +/************************************************************************/ + +#define L1B_TIMECODE_LENGTH 100 +class TimeCode { + long lYear; + long lDay; + long lMillisecond; + char pszString[L1B_TIMECODE_LENGTH]; + + public: + void SetYear(long year) + { + lYear = year; + } + void SetDay(long day) + { + lDay = day; + } + void SetMillisecond(long millisecond) + { + lMillisecond = millisecond; + } + long GetYear() { return lYear; } + long GetDay() { return lDay; } + long GetMillisecond() { return lMillisecond; } + char* PrintTime() + { + snprintf(pszString, L1B_TIMECODE_LENGTH, + "year: %ld, day: %ld, millisecond: %ld", + lYear, lDay, lMillisecond); + return pszString; + } +}; +#undef L1B_TIMECODE_LENGTH + +/************************************************************************/ +/* ==================================================================== */ +/* L1BDataset */ +/* ==================================================================== */ +/************************************************************************/ +class L1BGeolocDataset; +class L1BGeolocRasterBand; +class L1BSolarZenithAnglesDataset; +class L1BSolarZenithAnglesRasterBand; +class L1BNOAA15AnglesDataset; +class L1BNOAA15AnglesRasterBand; +class L1BCloudsDataset; +class L1BCloudsRasterBand; + +class L1BDataset : public GDALPamDataset +{ + friend class L1BRasterBand; + friend class L1BMaskBand; + friend class L1BGeolocDataset; + friend class L1BGeolocRasterBand; + friend class L1BSolarZenithAnglesDataset; + friend class L1BSolarZenithAnglesRasterBand; + friend class L1BNOAA15AnglesDataset; + friend class L1BNOAA15AnglesRasterBand; + friend class L1BCloudsDataset; + friend class L1BCloudsRasterBand; + + //char pszRevolution[6]; // Five-digit number identifying spacecraft revolution + L1BReceivingStation eSource; // Source of data (receiving station name) + L1BProcessingCenter eProcCenter; // Data processing center + TimeCode sStartTime; + TimeCode sStopTime; + + int bHighGCPDensityStrategy; + GDAL_GCP *pasGCPList; + int nGCPCount; + int iGCPOffset; + int iGCPCodeOffset; + int iCLAVRStart; + int nGCPsPerLine; + int eLocationIndicator, iGCPStart, iGCPStep; + + L1BFileFormat eL1BFormat; + int nBufferSize; + L1BSpaceCraftdID eSpacecraftID; + L1BProductType eProductType; // LAC, GAC, HRPT, FRAC + L1BDataFormat iDataFormat; // 10-bit packed or 16-bit unpacked + int nRecordDataStart; + int nRecordDataEnd; + int nDataStartOffset; + int nRecordSize; + int nRecordSizeFromHeader; + GUInt32 iInstrumentStatus; + GUInt32 iChannelsMask; + + char *pszGCPProjection; + + VSILFILE *fp; + + int bFetchGeolocation; + int bGuessDataFormat; + + int bByteSwap; + + int bExposeMaskBand; + GDALRasterBand* poMaskBand; + + void ProcessRecordHeaders(); + int FetchGCPs( GDAL_GCP *, GByte *, int ); + void FetchNOAA9TimeCode(TimeCode *, const GByte *, int *); + void FetchNOAA15TimeCode(TimeCode *, const GByte *, int *); + void FetchTimeCode( TimeCode *psTime, const void *pRecordHeader, + int *peLocationIndicator ); + CPLErr ProcessDatasetHeader(const char* pszFilename); + int ComputeFileOffsets(); + + void FetchMetadata(); + void FetchMetadataNOAA15(); + + vsi_l_offset GetLineOffset(int nBlockYOff); + + GUInt16 GetUInt16(const void* pabyData); + GInt16 GetInt16(const void* pabyData); + GUInt32 GetUInt32(const void* pabyData); + GInt32 GetInt32(const void* pabyData); + + static L1BFileFormat DetectFormat( const char* pszFilename, + const GByte* pabyHeader, int nHeaderBytes ); + + public: + L1BDataset( L1BFileFormat ); + ~L1BDataset(); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + +}; + +/************************************************************************/ +/* ==================================================================== */ +/* L1BRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class L1BRasterBand : public GDALPamRasterBand +{ + friend class L1BDataset; + + public: + + L1BRasterBand( L1BDataset *, int ); + +// virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALRasterBand *GetMaskBand(); + virtual int GetMaskFlags(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* L1BMaskBand */ +/* ==================================================================== */ +/************************************************************************/ + +class L1BMaskBand: public GDALPamRasterBand +{ + friend class L1BDataset; + + public: + + L1BMaskBand( L1BDataset * ); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* L1BMaskBand() */ +/************************************************************************/ + +L1BMaskBand::L1BMaskBand( L1BDataset *poDS ) +{ + CPLAssert(poDS->eL1BFormat == L1B_NOAA15 || + poDS->eL1BFormat == L1B_NOAA15_NOHDR); + + this->poDS = poDS; + eDataType = GDT_Byte; + + nRasterXSize = poDS->GetRasterXSize(); + nRasterYSize = poDS->GetRasterYSize(); + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr L1BMaskBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + L1BDataset *poGDS = (L1BDataset *) poDS; + + VSIFSeekL( poGDS->fp, poGDS->GetLineOffset(nBlockYOff) + 24, SEEK_SET ); + + GByte abyData[4]; + VSIFReadL( abyData, 1, 4, poGDS->fp ); + GUInt32 n32 = poGDS->GetUInt32(abyData); + + if( (n32 >> 31) != 0 ) /* fatal flag */ + memset(pImage, 0, nBlockXSize); + else + memset(pImage, 255, nBlockXSize); + + return CE_None; +} + +/************************************************************************/ +/* L1BRasterBand() */ +/************************************************************************/ + +L1BRasterBand::L1BRasterBand( L1BDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + eDataType = GDT_UInt16; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* GetMaskBand() */ +/************************************************************************/ + +GDALRasterBand *L1BRasterBand::GetMaskBand() +{ + L1BDataset *poGDS = (L1BDataset *) poDS; + if( poGDS->poMaskBand ) + return poGDS->poMaskBand; + return GDALPamRasterBand::GetMaskBand(); +} + +/************************************************************************/ +/* GetMaskFlags() */ +/************************************************************************/ + +int L1BRasterBand::GetMaskFlags() +{ + L1BDataset *poGDS = (L1BDataset *) poDS; + if( poGDS->poMaskBand ) + return GMF_PER_DATASET; + return GDALPamRasterBand::GetMaskFlags(); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr L1BRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + L1BDataset *poGDS = (L1BDataset *) poDS; + +/* -------------------------------------------------------------------- */ +/* Seek to data. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( poGDS->fp, poGDS->GetLineOffset(nBlockYOff), SEEK_SET ); + +/* -------------------------------------------------------------------- */ +/* Read data into the buffer. */ +/* -------------------------------------------------------------------- */ + GUInt16 *iScan = NULL; // Unpacked 16-bit scanline buffer + int i, j; + + switch (poGDS->iDataFormat) + { + case PACKED10BIT: + { + // Read packed scanline + GUInt32 *iRawScan = (GUInt32 *)CPLMalloc(poGDS->nRecordSize); + VSIFReadL( iRawScan, 1, poGDS->nRecordSize, poGDS->fp ); + + iScan = (GUInt16 *)CPLMalloc(poGDS->nBufferSize); + j = 0; + for(i = poGDS->nRecordDataStart / (int)sizeof(iRawScan[0]); + i < poGDS->nRecordDataEnd / (int)sizeof(iRawScan[0]); i++) + { + GUInt32 iWord1 = poGDS->GetUInt32( &iRawScan[i] ); + GUInt32 iWord2 = iWord1 & 0x3FF00000; + + iScan[j++] = (GUInt16) (iWord2 >> 20); + iWord2 = iWord1 & 0x000FFC00; + iScan[j++] = (GUInt16) (iWord2 >> 10); + iScan[j++] = (GUInt16) (iWord1 & 0x000003FF); + } + CPLFree(iRawScan); + } + break; + case UNPACKED16BIT: + { + // Read unpacked scanline + GUInt16 *iRawScan = (GUInt16 *)CPLMalloc(poGDS->nRecordSize); + VSIFReadL( iRawScan, 1, poGDS->nRecordSize, poGDS->fp ); + + iScan = (GUInt16 *)CPLMalloc(poGDS->GetRasterXSize() + * poGDS->nBands * sizeof(GUInt16)); + for (i = 0; i < poGDS->GetRasterXSize() * poGDS->nBands; i++) + { + iScan[i] = poGDS->GetUInt16( &iRawScan[poGDS->nRecordDataStart + / (int)sizeof(iRawScan[0]) + i] ); + } + CPLFree(iRawScan); + } + break; + case UNPACKED8BIT: + { + // Read 8-bit unpacked scanline + GByte *byRawScan = (GByte *)CPLMalloc(poGDS->nRecordSize); + VSIFReadL( byRawScan, 1, poGDS->nRecordSize, poGDS->fp ); + + iScan = (GUInt16 *)CPLMalloc(poGDS->GetRasterXSize() + * poGDS->nBands * sizeof(GUInt16)); + for (i = 0; i < poGDS->GetRasterXSize() * poGDS->nBands; i++) + iScan[i] = byRawScan[poGDS->nRecordDataStart + / (int)sizeof(byRawScan[0]) + i]; + CPLFree(byRawScan); + } + break; + default: // NOTREACHED + break; + } + + int nBlockSize = nBlockXSize * nBlockYSize; + if (poGDS->eLocationIndicator == DESCEND) + { + for( i = 0, j = 0; i < nBlockSize; i++ ) + { + ((GUInt16 *) pImage)[i] = iScan[j + nBand - 1]; + j += poGDS->nBands; + } + } + else + { + for ( i = nBlockSize - 1, j = 0; i >= 0; i-- ) + { + ((GUInt16 *) pImage)[i] = iScan[j + nBand - 1]; + j += poGDS->nBands; + } + } + + CPLFree(iScan); + return CE_None; +} + +/************************************************************************/ +/* L1BDataset() */ +/************************************************************************/ + +L1BDataset::L1BDataset( L1BFileFormat eL1BFormat ) + +{ + eSource = UNKNOWN_STATION; + eProcCenter = UNKNOWN_CENTER; + // sStartTime + // sStopTime + bHighGCPDensityStrategy = CSLTestBoolean(CPLGetConfigOption("L1B_HIGH_GCP_DENSITY", "TRUE")); + pasGCPList = NULL; + nGCPCount = 0; + iGCPOffset = 0; + iGCPCodeOffset = 0; + iCLAVRStart = 0; + nGCPsPerLine = 0; + eLocationIndicator = DESCEND; // XXX: should be initialised + iGCPStart = 0; + iGCPStep = 0; + this->eL1BFormat = eL1BFormat; + nBufferSize = 0; + eSpacecraftID = TIROSN; + eProductType = HRPT; + iDataFormat = PACKED10BIT; + nRecordDataStart = 0; + nRecordDataEnd = 0; + nDataStartOffset = 0; + nRecordSize = 0; + nRecordSizeFromHeader = 0; + iInstrumentStatus = 0; + iChannelsMask = 0; + pszGCPProjection = CPLStrdup( "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\",SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",7043]],TOWGS84[0,0,4.5,0,0,0.554,0.2263],AUTHORITY[\"EPSG\",6322]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",8901]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",9108]],AUTHORITY[\"EPSG\",4322]]" ); + fp = NULL; + bFetchGeolocation = FALSE; + bGuessDataFormat = FALSE; + bByteSwap = CPL_IS_LSB; /* L1B is normally big-endian ordered, so byte-swap on little-endian CPU */ + bExposeMaskBand = FALSE; + poMaskBand = NULL; +} + +/************************************************************************/ +/* ~L1BDataset() */ +/************************************************************************/ + +L1BDataset::~L1BDataset() + +{ + FlushCache(); + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + if ( pszGCPProjection ) + CPLFree( pszGCPProjection ); + if( fp != NULL ) + VSIFCloseL( fp ); + delete poMaskBand; +} + +/************************************************************************/ +/* GetLineOffset() */ +/************************************************************************/ + +vsi_l_offset L1BDataset::GetLineOffset(int nBlockYOff) +{ + return (eLocationIndicator == DESCEND) ? + nDataStartOffset + (vsi_l_offset)nBlockYOff * nRecordSize : + nDataStartOffset + + (vsi_l_offset)(nRasterYSize - nBlockYOff - 1) * nRecordSize; +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int L1BDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *L1BDataset::GetGCPProjection() + +{ + if( nGCPCount > 0 ) + return pszGCPProjection; + else + return ""; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *L1BDataset::GetGCPs() +{ + return pasGCPList; +} + +/************************************************************************/ +/* Byte swapping helpers */ +/************************************************************************/ + +GUInt16 L1BDataset::GetUInt16(const void* pabyData) +{ + GUInt16 iTemp; + memcpy(&iTemp, pabyData, 2); + if( bByteSwap ) + return CPL_SWAP16(iTemp); + return iTemp; +} + +GInt16 L1BDataset::GetInt16(const void* pabyData) +{ + GInt16 iTemp; + memcpy(&iTemp, pabyData, 2); + if( bByteSwap ) + return CPL_SWAP16(iTemp); + return iTemp; +} + +GUInt32 L1BDataset::GetUInt32(const void* pabyData) +{ + GUInt32 lTemp; + memcpy(&lTemp, pabyData, 4); + if( bByteSwap ) + return CPL_SWAP32(lTemp); + return lTemp; +} + +GInt32 L1BDataset::GetInt32(const void* pabyData) +{ + GInt32 lTemp; + memcpy(&lTemp, pabyData, 4); + if( bByteSwap ) + return CPL_SWAP32(lTemp); + return lTemp; + +} + +/************************************************************************/ +/* Fetch timecode from the record header (NOAA9-NOAA14 version) */ +/************************************************************************/ + +void L1BDataset::FetchNOAA9TimeCode( TimeCode *psTime, + const GByte *piRecordHeader, + int *peLocationIndicator ) +{ + GUInt32 lTemp; + + lTemp = ((piRecordHeader[2] >> 1) & 0x7F); + psTime->SetYear((lTemp > 77) ? + (lTemp + 1900) : (lTemp + 2000)); // Avoid `Year 2000' problem + psTime->SetDay((GUInt32)(piRecordHeader[2] & 0x01) << 8 + | (GUInt32)piRecordHeader[3]); + psTime->SetMillisecond( ((GUInt32)(piRecordHeader[4] & 0x07) << 24) + | ((GUInt32)piRecordHeader[5] << 16) + | ((GUInt32)piRecordHeader[6] << 8) + | (GUInt32)piRecordHeader[7] ); + if ( peLocationIndicator ) + { + *peLocationIndicator = + ((piRecordHeader[8] & 0x02) == 0) ? ASCEND : DESCEND; + } +} + +/************************************************************************/ +/* Fetch timecode from the record header (NOAA15-METOP2 version) */ +/************************************************************************/ + +void L1BDataset::FetchNOAA15TimeCode( TimeCode *psTime, + const GByte *pabyRecordHeader, + int *peLocationIndicator ) +{ + psTime->SetYear(GetUInt16(pabyRecordHeader + 2)); + psTime->SetDay(GetUInt16(pabyRecordHeader + 4)); + psTime->SetMillisecond(GetUInt32(pabyRecordHeader+8)); + if ( peLocationIndicator ) + { + // FIXME: hemisphere + *peLocationIndicator = + ((GetUInt16(pabyRecordHeader + 12) & 0x8000) == 0) ? ASCEND : DESCEND; + } +} +/************************************************************************/ +/* FetchTimeCode() */ +/************************************************************************/ + +void L1BDataset::FetchTimeCode( TimeCode *psTime, + const void *pRecordHeader, + int *peLocationIndicator ) +{ + if (eSpacecraftID <= NOAA14) + { + FetchNOAA9TimeCode( psTime, (const GByte *) pRecordHeader, + peLocationIndicator ); + } + else + { + FetchNOAA15TimeCode( psTime, (const GByte *) pRecordHeader, + peLocationIndicator ); + } +} + +/************************************************************************/ +/* Fetch GCPs from the individual scanlines */ +/************************************************************************/ + +int L1BDataset::FetchGCPs( GDAL_GCP *pasGCPListRow, + GByte *pabyRecordHeader, int iLine ) +{ + // LAC and HRPT GCPs are tied to the center of pixel, + // GAC ones are slightly displaced. + double dfDelta = (eProductType == GAC) ? 0.9 : 0.5; + double dfPixel = (eLocationIndicator == DESCEND) ? + iGCPStart + dfDelta : (nRasterXSize - (iGCPStart + dfDelta)); + + int nGCPs; + if ( eSpacecraftID <= NOAA14 ) + { + // NOAA9-NOAA14 records have an indicator of number of working GCPs. + // Number of good GCPs may be smaller than the total amount of points. + nGCPs = (*(pabyRecordHeader + iGCPCodeOffset) < nGCPsPerLine) ? + *(pabyRecordHeader + iGCPCodeOffset) : nGCPsPerLine; +#ifdef DEBUG_VERBOSE + CPLDebug( "L1B", "iGCPCodeOffset=%d, nGCPsPerLine=%d, nGoodGCPs=%d", + iGCPCodeOffset, nGCPsPerLine, nGCPs ); +#endif + } + else + nGCPs = nGCPsPerLine; + + pabyRecordHeader += iGCPOffset; + + int nGCPCountRow = 0; + while ( nGCPs-- ) + { + if ( eSpacecraftID <= NOAA14 ) + { + GInt16 nRawY = GetInt16( pabyRecordHeader ); + pabyRecordHeader += sizeof(GInt16); + GInt16 nRawX = GetInt16( pabyRecordHeader ); + pabyRecordHeader += sizeof(GInt16); + + pasGCPListRow[nGCPCountRow].dfGCPY = nRawY / L1B_NOAA9_GCP_SCALE; + pasGCPListRow[nGCPCountRow].dfGCPX = nRawX / L1B_NOAA9_GCP_SCALE; + } + else + { + GInt32 nRawY = GetInt32( pabyRecordHeader ); + pabyRecordHeader += sizeof(GInt32); + GInt32 nRawX = GetInt32( pabyRecordHeader ); + pabyRecordHeader += sizeof(GInt32); + + pasGCPListRow[nGCPCountRow].dfGCPY = nRawY / L1B_NOAA15_GCP_SCALE; + pasGCPListRow[nGCPCountRow].dfGCPX = nRawX / L1B_NOAA15_GCP_SCALE; + } + + if ( pasGCPListRow[nGCPCountRow].dfGCPX < -180 + || pasGCPListRow[nGCPCountRow].dfGCPX > 180 + || pasGCPListRow[nGCPCountRow].dfGCPY < -90 + || pasGCPListRow[nGCPCountRow].dfGCPY > 90 ) + continue; + + pasGCPListRow[nGCPCountRow].dfGCPZ = 0.0; + pasGCPListRow[nGCPCountRow].dfGCPPixel = dfPixel; + dfPixel += (eLocationIndicator == DESCEND) ? iGCPStep : -iGCPStep; + pasGCPListRow[nGCPCountRow].dfGCPLine = + (double)( (eLocationIndicator == DESCEND) ? + iLine : nRasterYSize - iLine - 1 ) + 0.5; + nGCPCountRow++; + } + return nGCPCountRow; +} + +/************************************************************************/ +/* ProcessRecordHeaders() */ +/************************************************************************/ + +void L1BDataset::ProcessRecordHeaders() +{ + void *pRecordHeader = CPLMalloc( nRecordDataStart ); + + VSIFSeekL(fp, nDataStartOffset, SEEK_SET); + VSIFReadL(pRecordHeader, 1, nRecordDataStart, fp); + + FetchTimeCode( &sStartTime, pRecordHeader, &eLocationIndicator ); + + VSIFSeekL( fp, nDataStartOffset + (nRasterYSize - 1) * nRecordSize, + SEEK_SET); + VSIFReadL( pRecordHeader, 1, nRecordDataStart, fp ); + + FetchTimeCode( &sStopTime, pRecordHeader, NULL ); + +/* -------------------------------------------------------------------- */ +/* Pick a skip factor so that we will get roughly 20 lines */ +/* worth of GCPs. That should give respectible coverage on all */ +/* but the longest swaths. */ +/* -------------------------------------------------------------------- */ + int nTargetLines; + double dfLineStep; + + if( bHighGCPDensityStrategy ) + { + if (nRasterYSize < nGCPsPerLine) + { + nTargetLines = nRasterYSize; + } + else + { + int nColStep; + nColStep = nRasterXSize / nGCPsPerLine; + if (nRasterYSize >= nRasterXSize) + { + dfLineStep = nColStep; + } + else + { + dfLineStep = nRasterYSize / nGCPsPerLine; + } + nTargetLines = nRasterYSize / dfLineStep; + } + } + else + { + nTargetLines = MIN(DESIRED_LINES_OF_GCPS, nRasterYSize); + } + dfLineStep = 1.0 * (nRasterYSize - 1) / ( nTargetLines - 1 ); + +/* -------------------------------------------------------------------- */ +/* Initialize the GCP list. */ +/* -------------------------------------------------------------------- */ + pasGCPList = (GDAL_GCP *)VSICalloc( nTargetLines * nGCPsPerLine, + sizeof(GDAL_GCP) ); + if (pasGCPList == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + CPLFree( pRecordHeader ); + return; + } + GDALInitGCPs( nTargetLines * nGCPsPerLine, pasGCPList ); + +/* -------------------------------------------------------------------- */ +/* Fetch the GCPs for each selected line. We force the last */ +/* line sampled to be the last line in the dataset even if that */ +/* leaves a bigger than expected gap. */ +/* -------------------------------------------------------------------- */ + int iStep; + int iPrevLine = -1; + + for( iStep = 0; iStep < nTargetLines; iStep++ ) + { + int iLine; + + if( iStep == nTargetLines - 1 ) + iLine = nRasterYSize - 1; + else + iLine = (int)(dfLineStep * iStep); + if( iLine == iPrevLine ) + continue; + iPrevLine = iLine; + + VSIFSeekL( fp, nDataStartOffset + iLine * nRecordSize, SEEK_SET ); + VSIFReadL( pRecordHeader, 1, nRecordDataStart, fp ); + + int nGCPsOnThisLine = FetchGCPs( pasGCPList + nGCPCount, (GByte *)pRecordHeader, iLine ); + + if( !bHighGCPDensityStrategy ) + { +/* -------------------------------------------------------------------- */ +/* We don't really want too many GCPs per line. Downsample to */ +/* 11 per line. */ +/* -------------------------------------------------------------------- */ + + int iGCP; + int nDesiredGCPsPerLine = MIN(DESIRED_GCPS_PER_LINE,nGCPsOnThisLine); + int nGCPStep = ( nDesiredGCPsPerLine > 1 ) ? + ( nGCPsOnThisLine - 1 ) / ( nDesiredGCPsPerLine-1 ) : 1; + int iSrcGCP = nGCPCount; + int iDstGCP = nGCPCount; + + if( nGCPStep == 0 ) + nGCPStep = 1; + + for( iGCP = 0; iGCP < nDesiredGCPsPerLine; iGCP++ ) + { + if( iGCP == nDesiredGCPsPerLine - 1 ) + iSrcGCP = nGCPCount + nGCPsOnThisLine - 1; + else + iSrcGCP += nGCPStep; + iDstGCP ++; + + pasGCPList[iDstGCP].dfGCPX = pasGCPList[iSrcGCP].dfGCPX; + pasGCPList[iDstGCP].dfGCPY = pasGCPList[iSrcGCP].dfGCPY; + pasGCPList[iDstGCP].dfGCPPixel = pasGCPList[iSrcGCP].dfGCPPixel; + pasGCPList[iDstGCP].dfGCPLine = pasGCPList[iSrcGCP].dfGCPLine; + } + + nGCPCount += nDesiredGCPsPerLine; + } + else + { + nGCPCount += nGCPsOnThisLine; + } + } + + if( nGCPCount < nTargetLines * nGCPsPerLine ) + { + GDALDeinitGCPs( nTargetLines * nGCPsPerLine - nGCPCount, + pasGCPList + nGCPCount ); + } + + CPLFree( pRecordHeader ); + +/* -------------------------------------------------------------------- */ +/* Set fetched information as metadata records */ +/* -------------------------------------------------------------------- */ + // Time of first scanline + SetMetadataItem( "START", sStartTime.PrintTime() ); + // Time of last scanline + SetMetadataItem( "STOP", sStopTime.PrintTime() ); + // AVHRR Earth location indication + + switch( eLocationIndicator ) + { + case ASCEND: + SetMetadataItem( "LOCATION", "Ascending" ); + break; + case DESCEND: + default: + SetMetadataItem( "LOCATION", "Descending" ); + break; + } + +} + +/************************************************************************/ +/* FetchMetadata() */ +/************************************************************************/ + +void L1BDataset::FetchMetadata() +{ + if( eL1BFormat != L1B_NOAA9 ) + { + FetchMetadataNOAA15(); + return; + } + + const char* pszDir = CPLGetConfigOption("L1B_METADATA_DIRECTORY", NULL); + if( pszDir == NULL ) + { + pszDir = CPLGetPath(GetDescription()); + if( pszDir[0] == '\0' ) + pszDir = "."; + } + CPLString osMetadataFile(CPLSPrintf("%s/%s_metadata.csv", pszDir, CPLGetFilename(GetDescription()))); + VSILFILE* fpCSV = VSIFOpenL(osMetadataFile, "wb"); + if( fpCSV == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot create metadata file : %s", + osMetadataFile.c_str()); + return; + } + + VSIFPrintfL(fpCSV, "SCANLINE,NBLOCKYOFF,YEAR,DAY,MS_IN_DAY,"); + VSIFPrintfL(fpCSV, "FATAL_FLAG,TIME_ERROR,DATA_GAP,DATA_JITTER,INSUFFICIENT_DATA_FOR_CAL,NO_EARTH_LOCATION,DESCEND,P_N_STATUS,"); + VSIFPrintfL(fpCSV, "BIT_SYNC_STATUS,SYNC_ERROR,FRAME_SYNC_ERROR,FLYWHEELING,BIT_SLIPPAGE,C3_SBBC,C4_SBBC,C5_SBBC,"); + VSIFPrintfL(fpCSV, "TIP_PARITY_FRAME_1,TIP_PARITY_FRAME_2,TIP_PARITY_FRAME_3,TIP_PARITY_FRAME_4,TIP_PARITY_FRAME_5,"); + VSIFPrintfL(fpCSV, "SYNC_ERRORS,"); + VSIFPrintfL(fpCSV, "CAL_SLOPE_C1,CAL_INTERCEPT_C1,CAL_SLOPE_C2,CAL_INTERCEPT_C2,CAL_SLOPE_C3,CAL_INTERCEPT_C3,CAL_SLOPE_C4,CAL_INTERCEPT_C4,CAL_SLOPE_C5,CAL_INTERCEPT_C5,"); + VSIFPrintfL(fpCSV, "NUM_SOLZENANGLES_EARTHLOCPNTS"); + VSIFPrintfL(fpCSV, "\n"); + + GByte* pabyRecordHeader = (GByte*)CPLMalloc(nRecordDataStart); + + for( int nBlockYOff = 0; nBlockYOff < nRasterYSize; nBlockYOff ++ ) + { +/* -------------------------------------------------------------------- */ +/* Seek to data. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( fp, GetLineOffset(nBlockYOff), SEEK_SET ); + + VSIFReadL( pabyRecordHeader, 1, nRecordDataStart, fp ); + + GUInt16 nScanlineNumber = GetUInt16(pabyRecordHeader); + + TimeCode timeCode; + FetchTimeCode( &timeCode, pabyRecordHeader, NULL ); + + VSIFPrintfL(fpCSV, + "%d,%d,%d,%d,%d,", + nScanlineNumber, + nBlockYOff, + (int)timeCode.GetYear(), + (int)timeCode.GetDay(), + (int)timeCode.GetMillisecond()); + VSIFPrintfL(fpCSV, + "%d,%d,%d,%d,%d,%d,%d,%d,", + (pabyRecordHeader[8] >> 7) & 1, + (pabyRecordHeader[8] >> 6) & 1, + (pabyRecordHeader[8] >> 5) & 1, + (pabyRecordHeader[8] >> 4) & 1, + (pabyRecordHeader[8] >> 3) & 1, + (pabyRecordHeader[8] >> 2) & 1, + (pabyRecordHeader[8] >> 1) & 1, + (pabyRecordHeader[8] >> 0) & 1); + VSIFPrintfL(fpCSV, + "%d,%d,%d,%d,%d,%d,%d,%d,", + (pabyRecordHeader[9] >> 7) & 1, + (pabyRecordHeader[9] >> 6) & 1, + (pabyRecordHeader[9] >> 5) & 1, + (pabyRecordHeader[9] >> 4) & 1, + (pabyRecordHeader[9] >> 3) & 1, + (pabyRecordHeader[9] >> 2) & 1, + (pabyRecordHeader[9] >> 1) & 1, + (pabyRecordHeader[9] >> 0) & 1); + VSIFPrintfL(fpCSV, + "%d,%d,%d,%d,%d,", + (pabyRecordHeader[10] >> 7) & 1, + (pabyRecordHeader[10] >> 6) & 1, + (pabyRecordHeader[10] >> 5) & 1, + (pabyRecordHeader[10] >> 4) & 1, + (pabyRecordHeader[10] >> 3) & 1); + VSIFPrintfL(fpCSV, "%d,", pabyRecordHeader[11] >> 2); + GInt32 i32; + for(int i=0;i<10;i++) + { + i32 = GetInt32(pabyRecordHeader + 12 + 4 *i); + /* Scales : http://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/podug/html/c3/sec3-3.htm */ + if( (i % 2) == 0 ) + VSIFPrintfL(fpCSV, "%f,", i32 / pow(2.0, 30.0)); + else + VSIFPrintfL(fpCSV, "%f,", i32 / pow(2.0, 22.0)); + } + VSIFPrintfL(fpCSV, "%d", pabyRecordHeader[52]); + VSIFPrintfL(fpCSV, "\n"); + } + + CPLFree(pabyRecordHeader); + VSIFCloseL(fpCSV); +} + +/************************************************************************/ +/* FetchMetadataNOAA15() */ +/************************************************************************/ + +void L1BDataset::FetchMetadataNOAA15() +{ + int i,j; + const char* pszDir = CPLGetConfigOption("L1B_METADATA_DIRECTORY", NULL); + if( pszDir == NULL ) + { + pszDir = CPLGetPath(GetDescription()); + if( pszDir[0] == '\0' ) + pszDir = "."; + } + CPLString osMetadataFile(CPLSPrintf("%s/%s_metadata.csv", pszDir, CPLGetFilename(GetDescription()))); + VSILFILE* fpCSV = VSIFOpenL(osMetadataFile, "wb"); + if( fpCSV == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot create metadata file : %s", + osMetadataFile.c_str()); + return; + } + + VSIFPrintfL(fpCSV, "SCANLINE,NBLOCKYOFF,YEAR,DAY,MS_IN_DAY,SAT_CLOCK_DRIF_DELTA,SOUTHBOUND,SCANTIME_CORRECTED,C3_SELECT,"); + VSIFPrintfL(fpCSV, "FATAL_FLAG,TIME_ERROR,DATA_GAP,INSUFFICIENT_DATA_FOR_CAL," + "NO_EARTH_LOCATION,FIRST_GOOD_TIME_AFTER_CLOCK_UPDATE," + "INSTRUMENT_STATUS_CHANGED,SYNC_LOCK_DROPPED," + "FRAME_SYNC_ERROR,FRAME_SYNC_DROPPED_LOCK,FLYWHEELING," + "BIT_SLIPPAGE,TIP_PARITY_ERROR,REFLECTED_SUNLIGHT_C3B," + "REFLECTED_SUNLIGHT_C4,REFLECTED_SUNLIGHT_C5,RESYNC,P_N_STATUS,"); + VSIFPrintfL(fpCSV, "BAD_TIME_CAN_BE_INFERRED,BAD_TIME_CANNOT_BE_INFERRED," + "TIME_DISCONTINUITY,REPEAT_SCAN_TIME,"); + VSIFPrintfL(fpCSV, "UNCALIBRATED_BAD_TIME,CALIBRATED_FEWER_SCANLINES," + "UNCALIBRATED_BAD_PRT,CALIBRATED_MARGINAL_PRT," + "UNCALIBRATED_CHANNELS,"); + VSIFPrintfL(fpCSV, "NO_EARTH_LOC_BAD_TIME,EARTH_LOC_QUESTIONABLE_TIME," + "EARTH_LOC_QUESTIONABLE,EARTH_LOC_VERY_QUESTIONABLE,"); + VSIFPrintfL(fpCSV, "C3B_UNCALIBRATED,C3B_QUESTIONABLE,C3B_ALL_BLACKBODY," + "C3B_ALL_SPACEVIEW,C3B_MARGINAL_BLACKBODY,C3B_MARGINAL_SPACEVIEW,"); + VSIFPrintfL(fpCSV, "C4_UNCALIBRATED,C4_QUESTIONABLE,C4_ALL_BLACKBODY," + "C4_ALL_SPACEVIEW,C4_MARGINAL_BLACKBODY,C4_MARGINAL_SPACEVIEW,"); + VSIFPrintfL(fpCSV, "C5_UNCALIBRATED,C5_QUESTIONABLE,C5_ALL_BLACKBODY," + "C5_ALL_SPACEVIEW,C5_MARGINAL_BLACKBODY,C5_MARGINAL_SPACEVIEW,"); + VSIFPrintfL(fpCSV, "BIT_ERRORS,"); + for(i=0;i<3;i++) + { + const char* pszChannel = (i==0) ? "C1" : (i==1) ? "C2" : "C3A"; + for(j=0;j<3;j++) + { + const char* pszType = (j==0) ? "OP": (j==1) ? "TEST": "PRELAUNCH"; + VSIFPrintfL(fpCSV, "VIS_%s_CAL_%s_SLOPE_1,", pszType, pszChannel); + VSIFPrintfL(fpCSV, "VIS_%s_CAL_%s_INTERCEPT_1,", pszType, pszChannel); + VSIFPrintfL(fpCSV, "VIS_%s_CAL_%s_SLOPE_2,", pszType, pszChannel); + VSIFPrintfL(fpCSV, "VIS_%s_CAL_%s_INTERCEPT_2,", pszType, pszChannel); + VSIFPrintfL(fpCSV, "VIS_%s_CAL_%s_INTERSECTION,", pszType, pszChannel); + } + } + for(i=0;i<3;i++) + { + const char* pszChannel = (i==0) ? "C3B" : (i==1) ? "C4" : "C5"; + for(j=0;j<2;j++) + { + const char* pszType = (j==0) ? "OP": "TEST"; + VSIFPrintfL(fpCSV, "IR_%s_CAL_%s_COEFF_1,", pszType, pszChannel); + VSIFPrintfL(fpCSV, "IR_%s_CAL_%s_COEFF_2,", pszType, pszChannel); + VSIFPrintfL(fpCSV, "IR_%s_CAL_%s_COEFF_3,", pszType, pszChannel); + } + } + VSIFPrintfL(fpCSV, "EARTH_LOC_CORR_TIP_EULER,EARTH_LOC_IND," + "SPACECRAFT_ATT_CTRL,ATT_SMODE,ATT_PASSIVE_WHEEL_TEST," + "TIME_TIP_EULER,TIP_EULER_ROLL,TIP_EULER_PITCH,TIP_EULER_YAW," + "SPACECRAFT_ALT"); + VSIFPrintfL(fpCSV, "\n"); + + GByte* pabyRecordHeader = (GByte*)CPLMalloc(nRecordDataStart); + GInt16 i16; + GUInt16 n16; + GInt32 i32; + GUInt32 n32; + + for( int nBlockYOff = 0; nBlockYOff < nRasterYSize; nBlockYOff ++ ) + { +/* -------------------------------------------------------------------- */ +/* Seek to data. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( fp, GetLineOffset(nBlockYOff), SEEK_SET ); + + VSIFReadL( pabyRecordHeader, 1, nRecordDataStart, fp ); + + GUInt16 nScanlineNumber = GetUInt16(pabyRecordHeader); + + TimeCode timeCode; + FetchTimeCode( &timeCode, pabyRecordHeader, NULL ); + + /* Clock drift delta */ + i16 = GetInt16(pabyRecordHeader + 6); + /* Scanline bit field */ + n16 = GetInt16(pabyRecordHeader + 12); + + VSIFPrintfL(fpCSV, + "%d,%d,%d,%d,%d,%d,%d,%d,%d,", + nScanlineNumber, + nBlockYOff, + (int)timeCode.GetYear(), + (int)timeCode.GetDay(), + (int)timeCode.GetMillisecond(), + i16, + (n16 >> 15) & 1, + (n16 >> 14) & 1, + (n16) & 3); + + n32 = GetUInt32(pabyRecordHeader + 24); + VSIFPrintfL(fpCSV,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,", + (n32 >> 31) & 1, + (n32 >> 30) & 1, + (n32 >> 29) & 1, + (n32 >> 28) & 1, + (n32 >> 27) & 1, + (n32 >> 26) & 1, + (n32 >> 25) & 1, + (n32 >> 24) & 1, + (n32 >> 23) & 1, + (n32 >> 22) & 1, + (n32 >> 21) & 1, + (n32 >> 20) & 1, + (n32 >> 8) & 1, + (n32 >> 6) & 3, + (n32 >> 4) & 3, + (n32 >> 2) & 3, + (n32 >> 1) & 1, + (n32 >> 0) & 1); + + n32 = GetUInt32(pabyRecordHeader + 28); + VSIFPrintfL(fpCSV,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,", + (n32 >> 23) & 1, + (n32 >> 22) & 1, + (n32 >> 21) & 1, + (n32 >> 20) & 1, + (n32 >> 15) & 1, + (n32 >> 14) & 1, + (n32 >> 13) & 1, + (n32 >> 12) & 1, + (n32 >> 11) & 1, + (n32 >> 7) & 1, + (n32 >> 6) & 1, + (n32 >> 5) & 1, + (n32 >> 4) & 1); + + for(i=0;i<3;i++) + { + n16 = GetUInt16(pabyRecordHeader + 32 + 2 * i); + VSIFPrintfL(fpCSV,"%d,%d,%d,%d,%d,%d,", + (n32 >> 7) & 1, + (n32 >> 6) & 1, + (n32 >> 5) & 1, + (n32 >> 4) & 1, + (n32 >> 2) & 1, + (n32 >> 1) & 1); + } + + /* Bit errors */ + n16 = GetUInt16(pabyRecordHeader + 38); + VSIFPrintfL(fpCSV, "%d,", n16); + + int nOffset = 48; + for(i=0;i<3;i++) + { + for(j=0;j<3;j++) + { + i32 = GetInt32(pabyRecordHeader + nOffset); + nOffset += 4; + VSIFPrintfL(fpCSV, "%f,", i32 / pow(10.0, 7.0)); + i32 = GetInt32(pabyRecordHeader + nOffset); + nOffset += 4; + VSIFPrintfL(fpCSV, "%f,", i32 / pow(10.0, 6.0)); + i32 = GetInt32(pabyRecordHeader + nOffset); + nOffset += 4; + VSIFPrintfL(fpCSV, "%f,", i32 / pow(10.0, 7.0)); + i32 = GetInt32(pabyRecordHeader + nOffset); + nOffset += 4; + VSIFPrintfL(fpCSV, "%f,", i32 / pow(10.0, 6.0)); + i32 = GetInt32(pabyRecordHeader + nOffset); + nOffset += 4; + VSIFPrintfL(fpCSV, "%d,", i32); + } + } + for(i=0;i<18;i++) + { + i32 = GetInt32(pabyRecordHeader + nOffset); + nOffset += 4; + VSIFPrintfL(fpCSV, "%f,", i32 / pow(10.0, 6.0)); + } + + n32 = GetUInt32(pabyRecordHeader + 312); + VSIFPrintfL(fpCSV,"%d,%d,%d,%d,%d,", + (n32 >> 16) & 1, + (n32 >> 12) & 15, + (n32 >> 8) & 15, + (n32 >> 4) & 15, + (n32 >> 0) & 15); + + n32 = GetUInt32(pabyRecordHeader + 316); + VSIFPrintfL(fpCSV,"%d,",n32); + + for(i=0;i<3;i++) + { + i16 = GetUInt16(pabyRecordHeader + 320 + 2 * i); + VSIFPrintfL(fpCSV,"%f,",i16 / pow(10.0,3.0)); + } + + n16 = GetUInt16(pabyRecordHeader + 326); + VSIFPrintfL(fpCSV,"%f",n16 / pow(10.0,1.0)); + + VSIFPrintfL(fpCSV, "\n"); + } + + CPLFree(pabyRecordHeader); + VSIFCloseL(fpCSV); +} + +/************************************************************************/ +/* EBCDICToASCII */ +/************************************************************************/ + +static const GByte EBCDICToASCII[] = +{ +0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +0x10, 0x11, 0x12, 0x13, 0x9D, 0x85, 0x08, 0x87, 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F, +0x80, 0x81, 0x82, 0x83, 0x84, 0x0A, 0x17, 0x1B, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07, +0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, 0x98, 0x99, 0x9A, 0x9B, 0x14, 0x15, 0x9E, 0x1A, +0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA2, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, +0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAC, +0x2D, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, +0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x5C, 0x00, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, +}; + +/************************************************************************/ +/* ProcessDatasetHeader() */ +/************************************************************************/ + +CPLErr L1BDataset::ProcessDatasetHeader(const char* pszFilename) +{ + char szDatasetName[L1B_DATASET_NAME_SIZE + 1]; + + if ( eL1BFormat == L1B_NOAA9 ) + { + GByte abyTBMHeader[L1B_NOAA9_HEADER_SIZE]; + + if ( VSIFSeekL( fp, 0, SEEK_SET ) < 0 + || VSIFReadL( abyTBMHeader, 1, L1B_NOAA9_HEADER_SIZE, + fp ) < L1B_NOAA9_HEADER_SIZE ) + { + CPLDebug( "L1B", "Can't read NOAA-9/14 TBM header." ); + return CE_Failure; + } + + // If dataset name in EBCDIC, decode it in ASCII + if ( *(abyTBMHeader + 8 + 25) == 'K' + && *(abyTBMHeader + 8 + 30) == 'K' + && *(abyTBMHeader + 8 + 33) == 'K' + && *(abyTBMHeader + 8 + 40) == 'K' + && *(abyTBMHeader + 8 + 46) == 'K' + && *(abyTBMHeader + 8 + 52) == 'K' + && *(abyTBMHeader + 8 + 61) == 'K' ) + { + for(int i=0;i 5 ) + { + nBands = 5; + iChannelsMask = 0x1F; + } + + // Determine data format (10-bit packed or 8/16-bit unpacked) + if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF, + "10", 2) ) + iDataFormat = PACKED10BIT; + else if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF, + "16", 2) ) + iDataFormat = UNPACKED16BIT; + else if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF, + "08", 2) ) + iDataFormat = UNPACKED8BIT; + else if ( EQUALN((const char *)abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF, + " ", 2) + || abyTBMHeader[L1B_NOAA9_HDR_WORD_OFF] == '\0' ) + /* Empty string can be found in the following samples : + http://www2.ncdc.noaa.gov/docs/podug/data/avhrr/franh.1b (10 bit) + http://www2.ncdc.noaa.gov/docs/podug/data/avhrr/frang.1b (10 bit) + http://www2.ncdc.noaa.gov/docs/podug/data/avhrr/calfilel.1b (16 bit) + http://www2.ncdc.noaa.gov/docs/podug/data/avhrr/rapnzg.1b (16 bit) + ftp://ftp.sat.dundee.ac.uk/misc/testdata/noaa12/hrptnoaa1b.dat (10 bit) + */ + bGuessDataFormat = TRUE; + else + { +#ifdef DEBUG + CPLDebug( "L1B", "Unknown data format \"%.2s\".", + abyTBMHeader + L1B_NOAA9_HDR_WORD_OFF ); +#endif + return CE_Failure; + } + + // Now read the dataset header record + GByte abyRecHeader[L1B_NOAA9_HDR_REC_SIZE]; + if ( VSIFSeekL( fp, L1B_NOAA9_HEADER_SIZE, SEEK_SET ) < 0 + || VSIFReadL( abyRecHeader, 1, L1B_NOAA9_HDR_REC_SIZE, + fp ) < L1B_NOAA9_HDR_REC_SIZE ) + { + CPLDebug( "L1B", "Can't read NOAA-9/14 record header." ); + return CE_Failure; + } + + // Determine the spacecraft name + switch ( abyRecHeader[L1B_NOAA9_HDR_REC_ID_OFF] ) + { + case 4: + eSpacecraftID = NOAA7; + break; + case 6: + eSpacecraftID = NOAA8; + break; + case 7: + eSpacecraftID = NOAA9; + break; + case 8: + eSpacecraftID = NOAA10; + break; + case 1: + { + /* We could also use the time code to determine TIROS-N */ + if( strlen(pszFilename) == L1B_DATASET_NAME_SIZE && + strncmp(pszFilename + 8, ".TN.", 4) == 0 ) + eSpacecraftID = TIROSN; + else + eSpacecraftID = NOAA11; + break; + } + case 5: + eSpacecraftID = NOAA12; + break; + case 2: + { + /* We could also use the time code to determine NOAA6 */ + if( strlen(pszFilename) == L1B_DATASET_NAME_SIZE && + strncmp(pszFilename + 8, ".NA.", 4) == 0 ) + eSpacecraftID = NOAA6; + else + eSpacecraftID = NOAA13; + break; + } + case 3: + eSpacecraftID = NOAA14; + break; + default: + CPLError( CE_Warning, CPLE_AppDefined, + "Unknown spacecraft ID \"%d\".", + abyRecHeader[L1B_NOAA9_HDR_REC_ID_OFF] ); + + eSpacecraftID = NOAA9_UNKNOWN; + break; + } + + // Determine the product data type + int iWord = abyRecHeader[L1B_NOAA9_HDR_REC_PROD_OFF] >> 4; + switch ( iWord ) + { + case 1: + eProductType = LAC; + break; + case 2: + eProductType = GAC; + break; + case 3: + eProductType = HRPT; + break; + default: +#ifdef DEBUG + CPLDebug( "L1B", "Unknown product type \"%d\".", iWord ); +#endif + return CE_Failure; + } + + // Determine receiving station name + iWord = ( abyRecHeader[L1B_NOAA9_HDR_REC_DSTAT_OFF] & 0x60 ) >> 5; + switch( iWord ) + { + case 1: + eSource = GC; + break; + case 2: + eSource = WI; + break; + case 3: + eSource = SO; + break; + default: + eSource = UNKNOWN_STATION; + break; + } + } + + else if ( eL1BFormat == L1B_NOAA15 || eL1BFormat == L1B_NOAA15_NOHDR ) + { + if ( eL1BFormat == L1B_NOAA15 ) + { + GByte abyARSHeader[L1B_NOAA15_HEADER_SIZE]; + + if ( VSIFSeekL( fp, 0, SEEK_SET ) < 0 + || VSIFReadL( abyARSHeader, 1, L1B_NOAA15_HEADER_SIZE, + fp ) < L1B_NOAA15_HEADER_SIZE ) + { + CPLDebug( "L1B", "Can't read NOAA-15 ARS header." ); + return CE_Failure; + } + + // Determine number of bands + int i; + for ( i = 0; i < L1B_NOAA15_HDR_CHAN_SIZE; i++ ) + { + if ( abyARSHeader[L1B_NOAA15_HDR_CHAN_OFF + i] == 1 + || abyARSHeader[L1B_NOAA15_HDR_CHAN_OFF + i] == 'Y' ) + { + nBands++; + iChannelsMask |= (1 << i); + } + } + if ( nBands == 0 || nBands > 5 ) + { + nBands = 5; + iChannelsMask = 0x1F; + } + + // Determine data format (10-bit packed or 8/16-bit unpacked) + if ( EQUALN((const char *)abyARSHeader + L1B_NOAA15_HDR_WORD_OFF, + "10", 2) ) + iDataFormat = PACKED10BIT; + else if ( EQUALN((const char *)abyARSHeader + L1B_NOAA15_HDR_WORD_OFF, + "16", 2) ) + iDataFormat = UNPACKED16BIT; + else if ( EQUALN((const char *)abyARSHeader + L1B_NOAA15_HDR_WORD_OFF, + "08", 2) ) + iDataFormat = UNPACKED8BIT; + else + { +#ifdef DEBUG + CPLDebug( "L1B", "Unknown data format \"%.2s\".", + abyARSHeader + L1B_NOAA9_HDR_WORD_OFF ); +#endif + return CE_Failure; + } + } + else + { + nBands = 5; + iChannelsMask = 0x1F; + iDataFormat = PACKED10BIT; + } + + // Now read the dataset header record + GByte abyRecHeader[L1B_NOAA15_HDR_REC_SIZE]; + if ( VSIFSeekL( fp, + (eL1BFormat == L1B_NOAA15) ? L1B_NOAA15_HEADER_SIZE : 0, + SEEK_SET ) < 0 + || VSIFReadL( abyRecHeader, 1, L1B_NOAA15_HDR_REC_SIZE, + fp ) < L1B_NOAA15_HDR_REC_SIZE ) + { + CPLDebug( "L1B", "Can't read NOAA-9/14 record header." ); + return CE_Failure; + } + + // Fetch dataset name + memcpy( szDatasetName, abyRecHeader + L1B_NOAA15_HDR_REC_NAME_OFF, + L1B_DATASET_NAME_SIZE ); + szDatasetName[L1B_DATASET_NAME_SIZE] = '\0'; + + // Determine processing center where the dataset was created + if ( EQUALN((const char *)abyRecHeader + + L1B_NOAA15_HDR_REC_SITE_OFF, "CMS", 3) ) + eProcCenter = CMS; + else if ( EQUALN((const char *)abyRecHeader + + L1B_NOAA15_HDR_REC_SITE_OFF, "DSS", 3) ) + eProcCenter = DSS; + else if ( EQUALN((const char *)abyRecHeader + + L1B_NOAA15_HDR_REC_SITE_OFF, "NSS", 3) ) + eProcCenter = NSS; + else if ( EQUALN((const char *)abyRecHeader + + L1B_NOAA15_HDR_REC_SITE_OFF, "UKM", 3) ) + eProcCenter = UKM; + else + eProcCenter = UNKNOWN_CENTER; + + int nFormatVersionYear, nFormatVersionDayOfYear, nHeaderRecCount; + + /* Some products from NOAA-18 and NOAA-19 coming from 'ess' processing station */ + /* have little-endian ordering. Try to detect it with some consistency checks */ + for(int i=0;i<=2;i++) + { + nFormatVersionYear = GetUInt16(abyRecHeader + L1B_NOAA15_HDR_REC_FORMAT_VERSION_YEAR_OFF); + nFormatVersionDayOfYear = GetUInt16(abyRecHeader + L1B_NOAA15_HDR_REC_FORMAT_VERSION_DAY_OFF); + nHeaderRecCount = GetUInt16(abyRecHeader + L1B_NOAA15_HDR_REC_HDR_REC_COUNT_OFF); + if( i == 2 ) + break; + if( !(nFormatVersionYear >= 1980 && nFormatVersionYear <= 2100) && + !(nFormatVersionDayOfYear <= 366) && + !(nHeaderRecCount == 1) ) + { + if( i == 0 ) + CPLDebug("L1B", "Trying little-endian ordering"); + else + CPLDebug("L1B", "Not completely convincing... Returning to big-endian order"); + bByteSwap = !bByteSwap; + } + else + break; + } + nRecordSizeFromHeader = GetUInt16(abyRecHeader + L1B_NOAA15_HDR_REC_LOGICAL_REC_LENGTH_OFF); + int nFormatVersion = GetUInt16(abyRecHeader + L1B_NOAA15_HDR_REC_FORMAT_VERSION_OFF); + CPLDebug("L1B", "NOAA Level 1b Format Version Number = %d", nFormatVersion); + CPLDebug("L1B", "Level 1b Format Version Year = %d", nFormatVersionYear); + CPLDebug("L1B", "Level 1b Format Version Day of Year = %d", nFormatVersionDayOfYear); + CPLDebug("L1B", "Logical Record Length of source Level 1b data set prior to processing = %d", nRecordSizeFromHeader); + int nBlockSize = GetUInt16(abyRecHeader + L1B_NOAA15_HDR_REC_BLOCK_SIZE_OFF); + CPLDebug("L1B", "Block Size of source Level 1b data set prior to processing = %d", nBlockSize); + CPLDebug("L1B", "Count of Header Records in this Data Set = %d", nHeaderRecCount); + + int nDataRecordCount = GetUInt16(abyRecHeader + L1B_NOAA15_HDR_REC_DATA_RECORD_COUNT_OFF); + CPLDebug("L1B", "Count of Data Records = %d", nDataRecordCount); + + int nCalibratedScanlineCount = GetUInt16(abyRecHeader + L1B_NOAA15_HDR_REC_CALIBRATED_SCANLINE_COUNT_OFF); + CPLDebug("L1B", "Count of Calibrated, Earth Located Scan Lines = %d", nCalibratedScanlineCount); + + int nMissingScanlineCount = GetUInt16(abyRecHeader + L1B_NOAA15_HDR_REC_MISSING_SCANLINE_COUNT_OFF); + CPLDebug("L1B", "Count of Missing Scan Lines = %d", nMissingScanlineCount); + if( nMissingScanlineCount != 0 ) + bExposeMaskBand = TRUE; + + char szEllipsoid[8+1]; + memcpy(szEllipsoid, abyRecHeader + L1B_NOAA15_HDR_REC_ELLIPSOID_OFF, 8); + szEllipsoid[8] = '\0'; + CPLDebug("L1B", "Reference Ellipsoid Model ID = '%s'", szEllipsoid); + if( EQUAL(szEllipsoid, "WGS-84 ") ) + { + CPLFree(pszGCPProjection); + pszGCPProjection = CPLStrdup(SRS_WKT_WGS84); + } + else if( EQUAL(szEllipsoid, " GRS 80") ) + { + CPLFree(pszGCPProjection); + pszGCPProjection = CPLStrdup("GEOGCS[\"GRS 1980(IUGG, 1980)\",DATUM[\"unknown\",SPHEROID[\"GRS80\",6378137,298.257222101],TOWGS84[0,0,0,0,0,0,0]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]]"); + } + + // Determine the spacecraft name + // See http://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/klm/html/c8/sec83132-2.htm + int iWord = GetUInt16(abyRecHeader + L1B_NOAA15_HDR_REC_ID_OFF); + switch ( iWord ) + { + case 2: + eSpacecraftID = NOAA16; + break; + case 4: + eSpacecraftID = NOAA15; + break; + case 6: + eSpacecraftID = NOAA17; + break; + case 7: + eSpacecraftID = NOAA18; + break; + case 8: + eSpacecraftID = NOAA19; + break; + case 11: + eSpacecraftID = METOP1; + break; + case 12: + eSpacecraftID = METOP2; + break; + // METOP3 is not documented yet + case 13: + eSpacecraftID = METOP3; + break; + case 14: + eSpacecraftID = METOP3; + break; + default: +#ifdef DEBUG + CPLDebug( "L1B", "Unknown spacecraft ID \"%d\".", iWord ); +#endif + return CE_Failure; + } + + // Determine the product data type + iWord = GetUInt16(abyRecHeader + L1B_NOAA15_HDR_REC_PROD_OFF); + switch ( iWord ) + { + case 1: + eProductType = LAC; + break; + case 2: + eProductType = GAC; + break; + case 3: + eProductType = HRPT; + break; + case 4: // XXX: documentation specifies the code '4' + case 13: // for FRAC but real datasets contain '13 here.' + eProductType = FRAC; + break; + default: +#ifdef DEBUG + CPLDebug( "L1B", "Unknown product type \"%d\".", iWord ); +#endif + return CE_Failure; + } + + // Fetch hinstrument status. Helps to determine whether we have + // 3A or 3B channel in the dataset. + iInstrumentStatus = GetUInt32(abyRecHeader + L1B_NOAA15_HDR_REC_STAT_OFF); + + // Determine receiving station name + iWord = GetUInt16(abyRecHeader + L1B_NOAA15_HDR_REC_SRC_OFF); + switch( iWord ) + { + case 1: + eSource = GC; + break; + case 2: + eSource = WI; + break; + case 3: + eSource = SO; + break; + case 4: + eSource = SV; + break; + case 5: + eSource = MO; + break; + default: + eSource = UNKNOWN_STATION; + break; + } + } + else + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Set fetched information as metadata records */ +/* -------------------------------------------------------------------- */ + const char *pszText; + + SetMetadataItem( "DATASET_NAME", szDatasetName ); + + switch( eSpacecraftID ) + { + case TIROSN: + pszText = "TIROS-N"; + break; + case NOAA6: + pszText = "NOAA-6(A)"; + break; + case NOAAB: + pszText = "NOAA-B"; + break; + case NOAA7: + pszText = "NOAA-7(C)"; + break; + case NOAA8: + pszText = "NOAA-8(E)"; + break; + case NOAA9_UNKNOWN: + pszText = "UNKNOWN"; + break; + case NOAA9: + pszText = "NOAA-9(F)"; + break; + case NOAA10: + pszText = "NOAA-10(G)"; + break; + case NOAA11: + pszText = "NOAA-11(H)"; + break; + case NOAA12: + pszText = "NOAA-12(D)"; + break; + case NOAA13: + pszText = "NOAA-13(I)"; + break; + case NOAA14: + pszText = "NOAA-14(J)"; + break; + case NOAA15: + pszText = "NOAA-15(K)"; + break; + case NOAA16: + pszText = "NOAA-16(L)"; + break; + case NOAA17: + pszText = "NOAA-17(M)"; + break; + case NOAA18: + pszText = "NOAA-18(N)"; + break; + case NOAA19: + pszText = "NOAA-19(N')"; + break; + case METOP2: + pszText = "METOP-A(2)"; + break; + case METOP1: + pszText = "METOP-B(1)"; + break; + case METOP3: + pszText = "METOP-C(3)"; + break; + default: + pszText = "Unknown"; + break; + } + SetMetadataItem( "SATELLITE", pszText ); + + switch( eProductType ) + { + case LAC: + pszText = "AVHRR LAC"; + break; + case HRPT: + pszText = "AVHRR HRPT"; + break; + case GAC: + pszText = "AVHRR GAC"; + break; + case FRAC: + pszText = "AVHRR FRAC"; + break; + default: + pszText = "Unknown"; + break; + } + SetMetadataItem( "DATA_TYPE", pszText ); + + // Get revolution number as string, we don't need this value for processing + char szRevolution[6]; + memcpy( szRevolution, szDatasetName + 32, 5 ); + szRevolution[5] = '\0'; + SetMetadataItem( "REVOLUTION", szRevolution ); + + switch( eSource ) + { + case DU: + pszText = "Dundee, Scotland, UK"; + break; + case GC: + pszText = "Fairbanks, Alaska, USA (formerly Gilmore Creek)"; + break; + case HO: + pszText = "Honolulu, Hawaii, USA"; + break; + case MO: + pszText = "Monterey, California, USA"; + break; + case WE: + pszText = "Western Europe CDA, Lannion, France"; + break; + case SO: + pszText = "SOCC (Satellite Operations Control Center), Suitland, Maryland, USA"; + break; + case WI: + pszText = "Wallops Island, Virginia, USA"; + break; + default: + pszText = "Unknown receiving station"; + break; + } + SetMetadataItem( "SOURCE", pszText ); + + switch( eProcCenter ) + { + case CMS: + pszText = "Centre de Meteorologie Spatiale - Lannion, France"; + break; + case DSS: + pszText = "Dundee Satellite Receiving Station - Dundee, Scotland, UK"; + break; + case NSS: + pszText = "NOAA/NESDIS - Suitland, Maryland, USA"; + break; + case UKM: + pszText = "United Kingdom Meteorological Office - Bracknell, England, UK"; + break; + default: + pszText = "Unknown processing center"; + break; + } + SetMetadataItem( "PROCESSING_CENTER", pszText ); + + return CE_None; +} + +/************************************************************************/ +/* ComputeFileOffsets() */ +/************************************************************************/ + +int L1BDataset::ComputeFileOffsets() +{ + CPLDebug("L1B", "Data format = %s", + (iDataFormat == PACKED10BIT) ? "Packed 10 bit" : + (iDataFormat == UNPACKED16BIT) ? "Unpacked 16 bit" : + "Unpacked 8 bit"); + + switch( eProductType ) + { + case HRPT: + case LAC: + case FRAC: + nRasterXSize = 2048; + nBufferSize = 20484; + iGCPStart = 25 - 1; /* http://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/klm/html/c2/sec2-4.htm */ + iGCPStep = 40; + nGCPsPerLine = 51; + if ( eL1BFormat == L1B_NOAA9 ) + { + if (iDataFormat == PACKED10BIT) + { + nRecordSize = 14800; + nRecordDataEnd = 14104; + } + else if (iDataFormat == UNPACKED16BIT) + { + switch(nBands) + { + case 1: + nRecordSize = 4544; + nRecordDataEnd = 4544; + break; + case 2: + nRecordSize = 8640; + nRecordDataEnd = 8640; + break; + case 3: + nRecordSize = 12736; + nRecordDataEnd = 12736; + break; + case 4: + nRecordSize = 16832; + nRecordDataEnd = 16832; + break; + case 5: + nRecordSize = 20928; + nRecordDataEnd = 20928; + break; + } + } + else // UNPACKED8BIT + { + switch(nBands) + { + case 1: + nRecordSize = 2496; + nRecordDataEnd = 2496; + break; + case 2: + nRecordSize = 4544; + nRecordDataEnd = 4544; + break; + case 3: + nRecordSize = 6592; + nRecordDataEnd = 6592; + break; + case 4: + nRecordSize = 8640; + nRecordDataEnd = 8640; + break; + case 5: + nRecordSize = 10688; + nRecordDataEnd = 10688; + break; + } + } + nDataStartOffset = nRecordSize + L1B_NOAA9_HEADER_SIZE; + nRecordDataStart = 448; + iGCPCodeOffset = 52; + iGCPOffset = 104; + } + + else if ( eL1BFormat == L1B_NOAA15 + || eL1BFormat == L1B_NOAA15_NOHDR ) + { + if (iDataFormat == PACKED10BIT) + { + nRecordSize = 15872; + nRecordDataEnd = 14920; + iCLAVRStart = 14984; + } + else if (iDataFormat == UNPACKED16BIT) + { /* Table 8.3.1.3.3.1-3 */ + switch(nBands) + { + case 1: + nRecordSize = 6144; + nRecordDataEnd = 5360; + iCLAVRStart = 5368 + 56; /* guessed but not verified */ + break; + case 2: + nRecordSize = 10240; + nRecordDataEnd = 9456; + iCLAVRStart = 9464 + 56; /* guessed but not verified */ + break; + case 3: + nRecordSize = 14336; + nRecordDataEnd = 13552; + iCLAVRStart = 13560 + 56; /* guessed but not verified */ + break; + case 4: + nRecordSize = 18432; + nRecordDataEnd = 17648; + iCLAVRStart = 17656 + 56; /* guessed but not verified */ + break; + case 5: + nRecordSize = 22528; + nRecordDataEnd = 21744; + iCLAVRStart = 21752 + 56; + break; + } + } + else // UNPACKED8BIT + { /* Table 8.3.1.3.3.1-2 */ + switch(nBands) + { + case 1: + nRecordSize = 4096; + nRecordDataEnd = 3312; + iCLAVRStart = 3320 + 56; /* guessed but not verified */ + break; + case 2: + nRecordSize = 6144; + nRecordDataEnd = 5360; + iCLAVRStart = 5368 + 56; /* guessed but not verified */ + break; + case 3: + nRecordSize = 8192; + nRecordDataEnd = 7408; + iCLAVRStart = 7416 + 56; /* guessed but not verified */ + break; + case 4: + nRecordSize = 10240; + nRecordDataEnd = 9456; + iCLAVRStart = 9464 + 56; /* guessed but not verified */ + break; + case 5: + nRecordSize = 12288; + nRecordDataEnd = 11504; + iCLAVRStart = 11512 + 56; /* guessed but not verified */ + break; + } + } + nDataStartOffset = ( eL1BFormat == L1B_NOAA15_NOHDR ) ? + nRecordDataEnd : nRecordSize + L1B_NOAA15_HEADER_SIZE; + nRecordDataStart = 1264; + iGCPCodeOffset = 0; // XXX: not exist for NOAA15? + iGCPOffset = 640; + } + else + return 0; + break; + + case GAC: + nRasterXSize = 409; + nBufferSize = 4092; + iGCPStart = 5 - 1; // FIXME: depends of scan direction + iGCPStep = 8; + nGCPsPerLine = 51; + if ( eL1BFormat == L1B_NOAA9 ) + { + if (iDataFormat == PACKED10BIT) + { + nRecordSize = 3220; + nRecordDataEnd = 3176; + } + else if (iDataFormat == UNPACKED16BIT) + switch(nBands) + { + case 1: + nRecordSize = 1268; + nRecordDataEnd = 1266; + break; + case 2: + nRecordSize = 2084; + nRecordDataEnd = 2084; + break; + case 3: + nRecordSize = 2904; + nRecordDataEnd = 2902; + break; + case 4: + nRecordSize = 3720; + nRecordDataEnd = 3720; + break; + case 5: + nRecordSize = 4540; + nRecordDataEnd = 4538; + break; + } + else // UNPACKED8BIT + { + switch(nBands) + { + case 1: + nRecordSize = 860; + nRecordDataEnd = 858; + break; + case 2: + nRecordSize = 1268; + nRecordDataEnd = 1266; + break; + case 3: + nRecordSize = 1676; + nRecordDataEnd = 1676; + break; + case 4: + nRecordSize = 2084; + nRecordDataEnd = 2084; + break; + case 5: + nRecordSize = 2496; + nRecordDataEnd = 2494; + break; + } + } + nDataStartOffset = nRecordSize * 2 + L1B_NOAA9_HEADER_SIZE; + nRecordDataStart = 448; + iGCPCodeOffset = 52; + iGCPOffset = 104; + } + + else if ( eL1BFormat == L1B_NOAA15 + || eL1BFormat == L1B_NOAA15_NOHDR ) + { + if (iDataFormat == PACKED10BIT) + { + nRecordSize = 4608; + nRecordDataEnd = 3992; + iCLAVRStart = 4056; + } + else if (iDataFormat == UNPACKED16BIT) + { /* Table 8.3.1.4.3.1-3 */ + switch(nBands) + { + case 1: + nRecordSize = 2360; + nRecordDataEnd = 2082; + iCLAVRStart = 2088 + 56; /* guessed but not verified */ + break; + case 2: + nRecordSize = 3176; + nRecordDataEnd = 2900; + iCLAVRStart = 2904 + 56; /* guessed but not verified */ + break; + case 3: + nRecordSize = 3992; + nRecordDataEnd = 3718; + iCLAVRStart = 3720 + 56; /* guessed but not verified */ + break; + case 4: + nRecordSize = 4816; + nRecordDataEnd = 4536; + iCLAVRStart = 4544 + 56; /* guessed but not verified */ + break; + case 5: + nRecordSize = 5632; + nRecordDataEnd = 5354; + iCLAVRStart = 5360 + 56; + break; + } + } + else // UNPACKED8BIT + { /* Table 8.3.1.4.3.1-2 but record length is wrong in the table ! */ + switch(nBands) + { + case 1: + nRecordSize = 1952; + nRecordDataEnd = 1673; + iCLAVRStart = 1680 + 56; /* guessed but not verified */ + break; + case 2: + nRecordSize = 2360; + nRecordDataEnd = 2082; + iCLAVRStart = 2088 + 56; /* guessed but not verified */ + break; + case 3: + nRecordSize = 2768; + nRecordDataEnd = 2491; + iCLAVRStart = 2496 + 56; /* guessed but not verified */ + break; + case 4: + nRecordSize = 3176; + nRecordDataEnd = 2900; + iCLAVRStart = 2904 + 56; /* guessed but not verified */ + break; + case 5: + nRecordSize = 3584; + nRecordDataEnd = 3309; + iCLAVRStart = 3312 + 56; /* guessed but not verified */ + break; + } + } + nDataStartOffset = ( eL1BFormat == L1B_NOAA15_NOHDR ) ? + nRecordDataEnd : nRecordSize + L1B_NOAA15_HEADER_SIZE; + nRecordDataStart = 1264; + iGCPCodeOffset = 0; // XXX: not exist for NOAA15? + iGCPOffset = 640; + } + else + return 0; + break; + default: + return 0; + } + + return 1; +} + +/************************************************************************/ +/* L1BGeolocDataset */ +/************************************************************************/ + +class L1BGeolocDataset : public GDALDataset +{ + friend class L1BGeolocRasterBand; + + L1BDataset* poL1BDS; + int bInterpolGeolocationDS; + + public: + L1BGeolocDataset(L1BDataset* poMainDS, + int bInterpolGeolocationDS); + virtual ~L1BGeolocDataset(); + + static GDALDataset* CreateGeolocationDS(L1BDataset* poL1BDS, + int bInterpolGeolocationDS); +}; + +/************************************************************************/ +/* L1BGeolocRasterBand */ +/************************************************************************/ + +class L1BGeolocRasterBand: public GDALRasterBand +{ + public: + L1BGeolocRasterBand(L1BGeolocDataset* poDS, int nBand); + + virtual CPLErr IReadBlock(int, int, void*); + virtual double GetNoDataValue( int *pbSuccess = NULL ); +}; + +/************************************************************************/ +/* L1BGeolocDataset() */ +/************************************************************************/ + +L1BGeolocDataset::L1BGeolocDataset(L1BDataset* poL1BDS, + int bInterpolGeolocationDS) +{ + this->poL1BDS = poL1BDS; + this->bInterpolGeolocationDS = bInterpolGeolocationDS; + if( bInterpolGeolocationDS ) + nRasterXSize = poL1BDS->nRasterXSize; + else + nRasterXSize = poL1BDS->nGCPsPerLine; + nRasterYSize = poL1BDS->nRasterYSize; +} + +/************************************************************************/ +/* ~L1BGeolocDataset() */ +/************************************************************************/ + +L1BGeolocDataset::~L1BGeolocDataset() +{ + delete poL1BDS; +} + +/************************************************************************/ +/* L1BGeolocRasterBand() */ +/************************************************************************/ + +L1BGeolocRasterBand::L1BGeolocRasterBand(L1BGeolocDataset* poDS, int nBand) +{ + this->poDS = poDS; + this->nBand = nBand; + nRasterXSize = poDS->nRasterXSize; + nRasterYSize = poDS->nRasterYSize; + eDataType = GDT_Float64; + nBlockXSize = nRasterXSize; + nBlockYSize = 1; + if( nBand == 1 ) + SetDescription("GEOLOC X"); + else + SetDescription("GEOLOC Y"); +} + +/************************************************************************/ +/* LagrangeInterpol() */ +/************************************************************************/ + +/* ---------------------------------------------------------------------------- + * Perform a Lagrangian interpolation through the given x,y coordinates + * and return the interpolated y value for the given x value. + * The array size and thus the polynomial order is defined by numpt. + * Input: x[] and y[] are of size numpt, + * x0 is the x value for which we calculate the corresponding y + * Returns: y value calculated for given x0. + */ +static double LagrangeInterpol(const double x[], + const double y[], double x0, int numpt) +{ + int i, j; + double L; + double y0 = 0; + + for (i=0; i= numKnown) + startpt = numKnown-MIDDLE_INTERP_ORDER; + for (j=0; jpoL1BDS; + GDAL_GCP* pasGCPList = (GDAL_GCP *)CPLCalloc( poL1BDS->nGCPsPerLine, + sizeof(GDAL_GCP) ); + GDALInitGCPs( poL1BDS->nGCPsPerLine, pasGCPList ); + + + GByte* pabyRecordHeader = (GByte*)CPLMalloc(poL1BDS->nRecordSize); + +/* -------------------------------------------------------------------- */ +/* Seek to data. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( poL1BDS->fp, poL1BDS->GetLineOffset(nBlockYOff), SEEK_SET ); + + VSIFReadL( pabyRecordHeader, 1, poL1BDS->nRecordDataStart, poL1BDS->fp ); + + /* Fetch the GCPs for the row */ + int nGotGCPs = poL1BDS->FetchGCPs(pasGCPList, pabyRecordHeader, nBlockYOff ); + double* padfData = (double*)pData; + int i; + if( poGDS->bInterpolGeolocationDS ) + { + /* Fill the known position */ + for(i=0;iiGCPStart + i * poL1BDS->iGCPStep] = dfVal; + } + + if( nGotGCPs == poL1BDS->nGCPsPerLine ) + { + /* And do Lagangian interpolation to fill the holes */ + L1BInterpol(padfData, poL1BDS->nGCPsPerLine, + poL1BDS->iGCPStart, poL1BDS->iGCPStep, nRasterXSize); + } + else + { + int iFirstNonValid = 0; + if( nGotGCPs > 5 ) + iFirstNonValid = poL1BDS->iGCPStart + nGotGCPs * poL1BDS->iGCPStep + poL1BDS->iGCPStep / 2; + for(i=iFirstNonValid; i 0 ) + { + L1BInterpol(padfData, poL1BDS->nGCPsPerLine, + poL1BDS->iGCPStart, poL1BDS->iGCPStep, iFirstNonValid); + } + } + } + else + { + for(i=0;ieLocationIndicator == ASCEND ) + { + for(i=0;inGCPsPerLine, pasGCPList ); + CPLFree(pasGCPList); + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double L1BGeolocRasterBand::GetNoDataValue( int *pbSuccess ) +{ + if( pbSuccess ) + *pbSuccess = TRUE; + return -200.0; +} + +/************************************************************************/ +/* CreateGeolocationDS() */ +/************************************************************************/ + +GDALDataset* L1BGeolocDataset::CreateGeolocationDS(L1BDataset* poL1BDS, + int bInterpolGeolocationDS) +{ + L1BGeolocDataset* poGeolocDS = new L1BGeolocDataset(poL1BDS, bInterpolGeolocationDS); + for(int i=1;i<=2;i++) + { + poGeolocDS->SetBand(i, new L1BGeolocRasterBand(poGeolocDS, i)); + } + return poGeolocDS; +} + +/************************************************************************/ +/* L1BSolarZenithAnglesDataset */ +/************************************************************************/ + +class L1BSolarZenithAnglesDataset : public GDALDataset +{ + friend class L1BSolarZenithAnglesRasterBand; + + L1BDataset* poL1BDS; + + public: + L1BSolarZenithAnglesDataset(L1BDataset* poMainDS); + virtual ~L1BSolarZenithAnglesDataset(); + + static GDALDataset* CreateSolarZenithAnglesDS(L1BDataset* poL1BDS); +}; + +/************************************************************************/ +/* L1BSolarZenithAnglesRasterBand */ +/************************************************************************/ + +class L1BSolarZenithAnglesRasterBand: public GDALRasterBand +{ + public: + L1BSolarZenithAnglesRasterBand(L1BSolarZenithAnglesDataset* poDS, int nBand); + + virtual CPLErr IReadBlock(int, int, void*); + virtual double GetNoDataValue( int *pbSuccess = NULL ); +}; + +/************************************************************************/ +/* L1BSolarZenithAnglesDataset() */ +/************************************************************************/ + +L1BSolarZenithAnglesDataset::L1BSolarZenithAnglesDataset(L1BDataset* poL1BDS) +{ + this->poL1BDS = poL1BDS; + nRasterXSize = 51; + nRasterYSize = poL1BDS->nRasterYSize; +} + +/************************************************************************/ +/* ~L1BSolarZenithAnglesDataset() */ +/************************************************************************/ + +L1BSolarZenithAnglesDataset::~L1BSolarZenithAnglesDataset() +{ + delete poL1BDS; +} + +/************************************************************************/ +/* L1BSolarZenithAnglesRasterBand() */ +/************************************************************************/ + +L1BSolarZenithAnglesRasterBand::L1BSolarZenithAnglesRasterBand(L1BSolarZenithAnglesDataset* poDS, int nBand) +{ + this->poDS = poDS; + this->nBand = nBand; + nRasterXSize = poDS->nRasterXSize; + nRasterYSize = poDS->nRasterYSize; + eDataType = GDT_Float32; + nBlockXSize = nRasterXSize; + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr L1BSolarZenithAnglesRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void* pData) +{ + L1BSolarZenithAnglesDataset* poGDS = (L1BSolarZenithAnglesDataset*)poDS; + L1BDataset* poL1BDS = poGDS->poL1BDS; + int i; + + GByte* pabyRecordHeader = (GByte*)CPLMalloc(poL1BDS->nRecordSize); + +/* -------------------------------------------------------------------- */ +/* Seek to data. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( poL1BDS->fp, poL1BDS->GetLineOffset(nBlockYOff), SEEK_SET ); + + VSIFReadL( pabyRecordHeader, 1, poL1BDS->nRecordSize, poL1BDS->fp ); + + int nValidValues = MIN(nRasterXSize, pabyRecordHeader[poL1BDS->iGCPCodeOffset]); + float* pafData = (float*)pData; + + int bHasFractional = ( poL1BDS->nRecordDataEnd + 20 <= poL1BDS->nRecordSize ); + +#ifdef notdef + if( bHasFractional ) + { + for(i=0;i<20;i++) + { + GByte val = pabyRecordHeader[poL1BDS->nRecordDataEnd + i]; + for(int j=0;j<8;j++) + fprintf(stderr, "%c", ((val >> (7 -j)) & 1) ? '1' : '0'); + fprintf(stderr, " "); + } + fprintf(stderr, "\n"); + } +#endif + + for(i=0;iiGCPCodeOffset + 1 + i] / 2.0; + + if( bHasFractional ) + { + /* Cf http://www.ncdc.noaa.gov/oa/pod-guide/ncdc/docs/podug/html/l/app-l.htm#notl-2 */ + /* This is not very clear on if bits must be counted from MSB or LSB */ + /* but when testing on n12gac10bit.l1b, it appears that the 3 bits for i=0 are the 3 MSB bits */ + int nAddBitStart = i * 3; + int nFractional; + +#if 1 + if( (nAddBitStart % 8) + 3 <= 8 ) + { + nFractional = (pabyRecordHeader[poL1BDS->nRecordDataEnd + (nAddBitStart / 8)] >> (8 - ((nAddBitStart % 8)+3))) & 0x7; + } + else + { + nFractional = (((pabyRecordHeader[poL1BDS->nRecordDataEnd + (nAddBitStart / 8)] << 8) | + pabyRecordHeader[poL1BDS->nRecordDataEnd + (nAddBitStart / 8) + 1]) >> (16 - ((nAddBitStart % 8)+3))) & 0x7; + } +#else + nFractional = (pabyRecordHeader[poL1BDS->nRecordDataEnd + (nAddBitStart / 8)] >> (nAddBitStart % 8)) & 0x7; + if( (nAddBitStart % 8) + 3 > 8 ) + nFractional |= (pabyRecordHeader[poL1BDS->nRecordDataEnd + (nAddBitStart / 8) + 1] & ((1 << (((nAddBitStart % 8) + 3 - 8))) - 1)) << (3 - ((((nAddBitStart % 8) + 3 - 8))));*/ +#endif + if( nFractional > 4 ) + { + CPLDebug("L1B", "For nBlockYOff=%d, i=%d, wrong fractional value : %d", + nBlockYOff, i, nFractional); + } + + pafData[i] += nFractional / 10.0; + } + } + + for(;ieLocationIndicator == ASCEND ) + { + for(i=0;iSetBand(i, new L1BSolarZenithAnglesRasterBand(poGeolocDS, i)); + } + return poGeolocDS; +} + + +/************************************************************************/ +/* L1BNOAA15AnglesDataset */ +/************************************************************************/ + +class L1BNOAA15AnglesDataset : public GDALDataset +{ + friend class L1BNOAA15AnglesRasterBand; + + L1BDataset* poL1BDS; + + public: + L1BNOAA15AnglesDataset(L1BDataset* poMainDS); + virtual ~L1BNOAA15AnglesDataset(); + + static GDALDataset* CreateAnglesDS(L1BDataset* poL1BDS); +}; + +/************************************************************************/ +/* L1BNOAA15AnglesRasterBand */ +/************************************************************************/ + +class L1BNOAA15AnglesRasterBand: public GDALRasterBand +{ + public: + L1BNOAA15AnglesRasterBand(L1BNOAA15AnglesDataset* poDS, int nBand); + + virtual CPLErr IReadBlock(int, int, void*); +}; + +/************************************************************************/ +/* L1BNOAA15AnglesDataset() */ +/************************************************************************/ + +L1BNOAA15AnglesDataset::L1BNOAA15AnglesDataset(L1BDataset* poL1BDS) +{ + this->poL1BDS = poL1BDS; + nRasterXSize = 51; + nRasterYSize = poL1BDS->nRasterYSize; +} + +/************************************************************************/ +/* ~L1BNOAA15AnglesDataset() */ +/************************************************************************/ + +L1BNOAA15AnglesDataset::~L1BNOAA15AnglesDataset() +{ + delete poL1BDS; +} + +/************************************************************************/ +/* L1BNOAA15AnglesRasterBand() */ +/************************************************************************/ + +L1BNOAA15AnglesRasterBand::L1BNOAA15AnglesRasterBand(L1BNOAA15AnglesDataset* poDS, int nBand) +{ + this->poDS = poDS; + this->nBand = nBand; + nRasterXSize = poDS->nRasterXSize; + nRasterYSize = poDS->nRasterYSize; + eDataType = GDT_Float32; + nBlockXSize = nRasterXSize; + nBlockYSize = 1; + if( nBand == 1 ) + SetDescription("Solar zenith angles"); + else if( nBand == 2 ) + SetDescription("Satellite zenith angles"); + else + SetDescription("Relative azimuth angles"); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr L1BNOAA15AnglesRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void* pData) +{ + L1BNOAA15AnglesDataset* poGDS = (L1BNOAA15AnglesDataset*)poDS; + L1BDataset* poL1BDS = poGDS->poL1BDS; + int i; + + GByte* pabyRecordHeader = (GByte*)CPLMalloc(poL1BDS->nRecordSize); + +/* -------------------------------------------------------------------- */ +/* Seek to data. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( poL1BDS->fp, poL1BDS->GetLineOffset(nBlockYOff), SEEK_SET ); + + VSIFReadL( pabyRecordHeader, 1, poL1BDS->nRecordSize, poL1BDS->fp ); + + float* pafData = (float*)pData; + + for(i=0;iGetInt16(pabyRecordHeader + 328 + 6 * i + 2 * (nBand - 1)); + pafData[i] = i16 / 100.0; + } + + if( poL1BDS->eLocationIndicator == ASCEND ) + { + for(i=0;iSetBand(i, new L1BNOAA15AnglesRasterBand(poGeolocDS, i)); + } + return poGeolocDS; +} + +/************************************************************************/ +/* L1BCloudsDataset */ +/************************************************************************/ + +class L1BCloudsDataset : public GDALDataset +{ + friend class L1BCloudsRasterBand; + + L1BDataset* poL1BDS; + + public: + L1BCloudsDataset(L1BDataset* poMainDS); + virtual ~L1BCloudsDataset(); + + static GDALDataset* CreateCloudsDS(L1BDataset* poL1BDS); +}; + +/************************************************************************/ +/* L1BCloudsRasterBand */ +/************************************************************************/ + +class L1BCloudsRasterBand: public GDALRasterBand +{ + public: + L1BCloudsRasterBand(L1BCloudsDataset* poDS, int nBand); + + virtual CPLErr IReadBlock(int, int, void*); +}; + +/************************************************************************/ +/* L1BCloudsDataset() */ +/************************************************************************/ + +L1BCloudsDataset::L1BCloudsDataset(L1BDataset* poL1BDS) +{ + this->poL1BDS = poL1BDS; + nRasterXSize = poL1BDS->nRasterXSize; + nRasterYSize = poL1BDS->nRasterYSize; +} + +/************************************************************************/ +/* ~L1BCloudsDataset() */ +/************************************************************************/ + +L1BCloudsDataset::~L1BCloudsDataset() +{ + delete poL1BDS; +} + +/************************************************************************/ +/* L1BCloudsRasterBand() */ +/************************************************************************/ + +L1BCloudsRasterBand::L1BCloudsRasterBand(L1BCloudsDataset* poDS, int nBand) +{ + this->poDS = poDS; + this->nBand = nBand; + nRasterXSize = poDS->nRasterXSize; + nRasterYSize = poDS->nRasterYSize; + eDataType = GDT_Byte; + nBlockXSize = nRasterXSize; + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr L1BCloudsRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void* pData) +{ + L1BCloudsDataset* poGDS = (L1BCloudsDataset*)poDS; + L1BDataset* poL1BDS = poGDS->poL1BDS; + int i; + + GByte* pabyRecordHeader = (GByte*)CPLMalloc(poL1BDS->nRecordSize); + +/* -------------------------------------------------------------------- */ +/* Seek to data. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( poL1BDS->fp, poL1BDS->GetLineOffset(nBlockYOff), SEEK_SET ); + + VSIFReadL( pabyRecordHeader, 1, poL1BDS->nRecordSize, poL1BDS->fp ); + + GByte* pabyData = (GByte*)pData; + + for(i=0;iiCLAVRStart + (i / 4)] >> (8 - ((i%4)*2+2))) & 0x3); + } + + if( poL1BDS->eLocationIndicator == ASCEND ) + { + for(i=0;iSetBand(i, new L1BCloudsRasterBand(poGeolocDS, i)); + } + return poGeolocDS; +} + + +/************************************************************************/ +/* DetectFormat() */ +/************************************************************************/ + +L1BFileFormat L1BDataset::DetectFormat( const char* pszFilename, + const GByte* pabyHeader, int nHeaderBytes ) + +{ + if (pabyHeader == NULL || nHeaderBytes < L1B_NOAA9_HEADER_SIZE) + return L1B_NONE; + + // We will try the NOAA-15 and later formats first + if ( nHeaderBytes > L1B_NOAA15_HEADER_SIZE + 61 + && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 25) == '.' + && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 30) == '.' + && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 33) == '.' + && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 40) == '.' + && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 46) == '.' + && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 52) == '.' + && *(pabyHeader + L1B_NOAA15_HEADER_SIZE + 61) == '.' ) + return L1B_NOAA15; + + // Next try the NOAA-9/14 formats + if ( *(pabyHeader + 8 + 25) == '.' + && *(pabyHeader + 8 + 30) == '.' + && *(pabyHeader + 8 + 33) == '.' + && *(pabyHeader + 8 + 40) == '.' + && *(pabyHeader + 8 + 46) == '.' + && *(pabyHeader + 8 + 52) == '.' + && *(pabyHeader + 8 + 61) == '.' ) + return L1B_NOAA9; + + // Next try the NOAA-9/14 formats with dataset name in EBCDIC + if ( *(pabyHeader + 8 + 25) == 'K' + && *(pabyHeader + 8 + 30) == 'K' + && *(pabyHeader + 8 + 33) == 'K' + && *(pabyHeader + 8 + 40) == 'K' + && *(pabyHeader + 8 + 46) == 'K' + && *(pabyHeader + 8 + 52) == 'K' + && *(pabyHeader + 8 + 61) == 'K' ) + return L1B_NOAA9; + + // Finally try the AAPP formats + if ( *(pabyHeader + 25) == '.' + && *(pabyHeader + 30) == '.' + && *(pabyHeader + 33) == '.' + && *(pabyHeader + 40) == '.' + && *(pabyHeader + 46) == '.' + && *(pabyHeader + 52) == '.' + && *(pabyHeader + 61) == '.' ) + return L1B_NOAA15_NOHDR; + + // A few NOAA <= 9 datasets with no dataset name in TBM header + if( strlen(pszFilename) == L1B_DATASET_NAME_SIZE && + pszFilename[3] == '.' && + pszFilename[8] == '.' && + pszFilename[11] == '.' && + pszFilename[18] == '.' && + pszFilename[24] == '.' && + pszFilename[30] == '.' && + pszFilename[39] == '.' && + memcmp(pabyHeader + 30, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", L1B_DATASET_NAME_SIZE) == 0 && + (pabyHeader[75] == '+' || pabyHeader[75] == '-') && + (pabyHeader[78] == '+' || pabyHeader[78] == '-') && + (pabyHeader[81] == '+' || pabyHeader[81] == '-') && + (pabyHeader[85] == '+' || pabyHeader[85] == '-') ) + return L1B_NOAA9; + + return L1B_NONE; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int L1BDataset::Identify( GDALOpenInfo *poOpenInfo ) + +{ + if ( EQUALN( poOpenInfo->pszFilename, "L1BGCPS:", strlen("L1BGCPS:") ) ) + return TRUE; + if ( EQUALN( poOpenInfo->pszFilename, "L1BGCPS_INTERPOL:", strlen("L1BGCPS_INTERPOL:") ) ) + return TRUE; + if ( EQUALN( poOpenInfo->pszFilename, "L1B_SOLAR_ZENITH_ANGLES:", strlen("L1B_SOLAR_ZENITH_ANGLES:") ) ) + return TRUE; + if ( EQUALN( poOpenInfo->pszFilename, "L1B_ANGLES:", strlen("L1B_ANGLES:") ) ) + return TRUE; + if ( EQUALN( poOpenInfo->pszFilename, "L1B_CLOUDS:", strlen("L1B_CLOUDS:") ) ) + return TRUE; + + if ( DetectFormat(CPLGetFilename(poOpenInfo->pszFilename), + poOpenInfo->pabyHeader, + poOpenInfo->nHeaderBytes) == L1B_NONE ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *L1BDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + GDALDataset* poOutDS; + VSILFILE* fp = NULL; + CPLString osFilename = poOpenInfo->pszFilename; + int bAskGeolocationDS = FALSE; + int bInterpolGeolocationDS = FALSE; + int bAskSolarZenithAnglesDS = FALSE; + int bAskAnglesDS = FALSE; + int bAskCloudsDS = FALSE; + L1BFileFormat eL1BFormat; + + if ( EQUALN( poOpenInfo->pszFilename, "L1BGCPS:", strlen("L1BGCPS:") ) || + EQUALN( poOpenInfo->pszFilename, "L1BGCPS_INTERPOL:", strlen("L1BGCPS_INTERPOL:") ) || + EQUALN( poOpenInfo->pszFilename, "L1B_SOLAR_ZENITH_ANGLES:", strlen("L1B_SOLAR_ZENITH_ANGLES:") )|| + EQUALN( poOpenInfo->pszFilename, "L1B_ANGLES:", strlen("L1B_ANGLES:") )|| + EQUALN( poOpenInfo->pszFilename, "L1B_CLOUDS:", strlen("L1B_CLOUDS:") ) ) + { + GByte abyHeader[1024]; + const char* pszFilename; + if( EQUALN( poOpenInfo->pszFilename, "L1BGCPS_INTERPOL:", strlen("L1BGCPS_INTERPOL:")) ) + { + bAskGeolocationDS = TRUE; + bInterpolGeolocationDS = TRUE; + pszFilename = poOpenInfo->pszFilename + strlen("L1BGCPS_INTERPOL:"); + } + else if (EQUALN( poOpenInfo->pszFilename, "L1BGCPS:", strlen("L1BGCPS:") ) ) + { + bAskGeolocationDS = TRUE; + pszFilename = poOpenInfo->pszFilename + strlen("L1BGCPS:"); + } + else if (EQUALN( poOpenInfo->pszFilename, "L1B_SOLAR_ZENITH_ANGLES:", strlen("L1B_SOLAR_ZENITH_ANGLES:") ) ) + { + bAskSolarZenithAnglesDS = TRUE; + pszFilename = poOpenInfo->pszFilename + strlen("L1B_SOLAR_ZENITH_ANGLES:"); + } + else if (EQUALN( poOpenInfo->pszFilename, "L1B_ANGLES:", strlen("L1B_ANGLES:") ) ) + { + bAskAnglesDS = TRUE; + pszFilename = poOpenInfo->pszFilename + strlen("L1B_ANGLES:"); + } + else + { + bAskCloudsDS = TRUE; + pszFilename = poOpenInfo->pszFilename + strlen("L1B_CLOUDS:"); + } + if( pszFilename[0] == '"' ) + pszFilename ++; + osFilename = pszFilename; + if( osFilename.size() > 0 && osFilename[osFilename.size()-1] == '"' ) + osFilename.resize(osFilename.size()-1); + fp = VSIFOpenL( osFilename, "rb" ); + if ( !fp ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Can't open file \"%s\".", osFilename.c_str() ); + return NULL; + } + VSIFReadL( abyHeader, 1, sizeof(abyHeader)-1, fp); + abyHeader[sizeof(abyHeader)-1] = '\0'; + eL1BFormat = DetectFormat( CPLGetFilename(osFilename), + abyHeader, sizeof(abyHeader) ); + } + else + eL1BFormat = DetectFormat( CPLGetFilename(osFilename), + poOpenInfo->pabyHeader, + poOpenInfo->nHeaderBytes ); + + if ( eL1BFormat == L1B_NONE ) + { + if( fp != NULL ) + VSIFCloseL(fp); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The L1B driver does not support update access to existing" + " datasets.\n" ); + if( fp != NULL ) + VSIFCloseL(fp); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + L1BDataset *poDS; + VSIStatBufL sStat; + + poDS = new L1BDataset( eL1BFormat ); + + if( fp == NULL ) + fp = VSIFOpenL( osFilename, "rb" ); + poDS->fp = fp; + if ( !poDS->fp ) + { + CPLDebug( "L1B", "Can't open file \"%s\".", osFilename.c_str() ); + goto bad; + } + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + if ( poDS->ProcessDatasetHeader(CPLGetFilename(osFilename)) != CE_None ) + { + CPLDebug( "L1B", "Error reading L1B record header." ); + goto bad; + } + + VSIStatL(osFilename, &sStat); + + if( poDS->eL1BFormat == L1B_NOAA15_NOHDR && + poDS->nRecordSizeFromHeader == 22016 && + (sStat.st_size % poDS->nRecordSizeFromHeader) == 0 ) + { + poDS->iDataFormat = UNPACKED16BIT; + poDS->ComputeFileOffsets(); + poDS->nDataStartOffset = poDS->nRecordSizeFromHeader; + poDS->nRecordSize = poDS->nRecordSizeFromHeader; + poDS->iCLAVRStart = 0; + } + else if ( poDS->bGuessDataFormat ) + { + int nTempYSize; + GUInt16 nScanlineNumber; + int j; + + /* If the data format is unspecified, try each one of the 3 known data formats */ + /* It is considered valid when the spacing between the first 5 scanline numbers */ + /* is a constant */ + + for(j=0;j<3;j++) + { + poDS->iDataFormat = (L1BDataFormat) (PACKED10BIT + j); + if (!poDS->ComputeFileOffsets()) + goto bad; + + nTempYSize = (sStat.st_size - poDS->nDataStartOffset) / poDS->nRecordSize; + if (nTempYSize < 5) + continue; + + int nLastScanlineNumber = 0; + int nDiffLine = 0; + int i; + for (i=0;i<5;i++) + { + nScanlineNumber = 0; + + VSIFSeekL(poDS->fp, poDS->nDataStartOffset + i * poDS->nRecordSize, SEEK_SET); + VSIFReadL(&nScanlineNumber, 1, 2, poDS->fp); + nScanlineNumber = poDS->GetUInt16(&nScanlineNumber); + + if (i == 1) + { + nDiffLine = nScanlineNumber - nLastScanlineNumber; + if (nDiffLine == 0) + break; + } + else if (i > 1) + { + if (nDiffLine != nScanlineNumber - nLastScanlineNumber) + break; + } + + nLastScanlineNumber = nScanlineNumber; + } + + if (i == 5) + { + CPLDebug("L1B", "Guessed data format : %s", + (poDS->iDataFormat == PACKED10BIT) ? "10" : + (poDS->iDataFormat == UNPACKED8BIT) ? "08" : "16"); + break; + } + } + + if (j == 3) + { + CPLError(CE_Failure, CPLE_AppDefined, "Could not guess data format of L1B product"); + goto bad; + } + } + else + { + if (!poDS->ComputeFileOffsets()) + goto bad; + } + + CPLDebug("L1B", "nRecordDataStart = %d", poDS->nRecordDataStart); + CPLDebug("L1B", "nRecordDataEnd = %d", poDS->nRecordDataEnd); + CPLDebug("L1B", "nDataStartOffset = %d", poDS->nDataStartOffset); + CPLDebug("L1B", "iCLAVRStart = %d", poDS->iCLAVRStart); + CPLDebug("L1B", "nRecordSize = %d", poDS->nRecordSize); + + // Compute number of lines dinamycally, so we can read partially + // downloaded files + poDS->nRasterYSize = + (sStat.st_size - poDS->nDataStartOffset) / poDS->nRecordSize; + +/* -------------------------------------------------------------------- */ +/* Deal with GCPs */ +/* -------------------------------------------------------------------- */ + poDS->ProcessRecordHeaders(); + + if( bAskGeolocationDS ) + { + return L1BGeolocDataset::CreateGeolocationDS(poDS, bInterpolGeolocationDS); + } + else if( bAskSolarZenithAnglesDS ) + { + if( eL1BFormat == L1B_NOAA9 ) + return L1BSolarZenithAnglesDataset::CreateSolarZenithAnglesDS(poDS); + else + { + delete poDS; + return NULL; + } + } + else if( bAskAnglesDS ) + { + if( eL1BFormat != L1B_NOAA9 ) + return L1BNOAA15AnglesDataset::CreateAnglesDS(poDS); + else + { + delete poDS; + return NULL; + } + } + else if( bAskCloudsDS ) + { + if( poDS->iCLAVRStart > 0 ) + poOutDS = L1BCloudsDataset::CreateCloudsDS(poDS); + else + { + delete poDS; + return NULL; + } + } + else + { + poOutDS = poDS; + } + + { + CPLString osTMP; + int bInterpol = CSLTestBoolean(CPLGetConfigOption("L1B_INTERPOL_GCPS", "TRUE")); + + poOutDS->SetMetadataItem( "SRS", poDS->pszGCPProjection, "GEOLOCATION" ); /* unused by gdalgeoloc.cpp */ + + if( bInterpol ) + osTMP.Printf( "L1BGCPS_INTERPOL:\"%s\"", osFilename.c_str() ); + else + osTMP.Printf( "L1BGCPS:\"%s\"", osFilename.c_str() ); + poOutDS->SetMetadataItem( "X_DATASET", osTMP, "GEOLOCATION" ); + poOutDS->SetMetadataItem( "X_BAND", "1" , "GEOLOCATION" ); + poOutDS->SetMetadataItem( "Y_DATASET", osTMP, "GEOLOCATION" ); + poOutDS->SetMetadataItem( "Y_BAND", "2" , "GEOLOCATION" ); + + if( bInterpol ) + { + poOutDS->SetMetadataItem( "PIXEL_OFFSET", "0", "GEOLOCATION" ); + poOutDS->SetMetadataItem( "PIXEL_STEP", "1", "GEOLOCATION" ); + } + else + { + osTMP.Printf( "%d", poDS->iGCPStart); + poOutDS->SetMetadataItem( "PIXEL_OFFSET", osTMP, "GEOLOCATION" ); + osTMP.Printf( "%d", poDS->iGCPStep); + poOutDS->SetMetadataItem( "PIXEL_STEP", osTMP, "GEOLOCATION" ); + } + + poOutDS->SetMetadataItem( "LINE_OFFSET", "0", "GEOLOCATION" ); + poOutDS->SetMetadataItem( "LINE_STEP", "1", "GEOLOCATION" ); + } + + if( poOutDS != poDS ) + return poOutDS; + + if( eL1BFormat == L1B_NOAA9 ) + { + char** papszSubdatasets = NULL; + papszSubdatasets = CSLSetNameValue(papszSubdatasets, "SUBDATASET_1_NAME", + CPLSPrintf("L1B_SOLAR_ZENITH_ANGLES:\"%s\"", osFilename.c_str())); + papszSubdatasets = CSLSetNameValue(papszSubdatasets, "SUBDATASET_1_DESC", + "Solar zenith angles"); + poDS->SetMetadata(papszSubdatasets, "SUBDATASETS"); + CSLDestroy(papszSubdatasets); + } + else + { + char** papszSubdatasets = NULL; + papszSubdatasets = CSLSetNameValue(papszSubdatasets, "SUBDATASET_1_NAME", + CPLSPrintf("L1B_ANGLES:\"%s\"", osFilename.c_str())); + papszSubdatasets = CSLSetNameValue(papszSubdatasets, "SUBDATASET_1_DESC", + "Solar zenith angles, satellite zenith angles and relative azimuth angles"); + + if( poDS->iCLAVRStart > 0 ) + { + papszSubdatasets = CSLSetNameValue(papszSubdatasets, "SUBDATASET_2_NAME", + CPLSPrintf("L1B_CLOUDS:\"%s\"", osFilename.c_str())); + papszSubdatasets = CSLSetNameValue(papszSubdatasets, "SUBDATASET_2_DESC", + "Clouds from AVHRR (CLAVR)"); + } + + poDS->SetMetadata(papszSubdatasets, "SUBDATASETS"); + CSLDestroy(papszSubdatasets); + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int iBand, i; + + for( iBand = 1, i = 0; iBand <= poDS->nBands; iBand++ ) + { + poDS->SetBand( iBand, new L1BRasterBand( poDS, iBand )); + + // Channels descriptions + if ( poDS->eSpacecraftID >= NOAA6 && poDS->eSpacecraftID <= METOP3 ) + { + if ( !(i & 0x01) && poDS->iChannelsMask & 0x01 ) + { + poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[0] ); + i |= 0x01; + continue; + } + if ( !(i & 0x02) && poDS->iChannelsMask & 0x02 ) + { + poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[1] ); + i |= 0x02; + continue; + } + if ( !(i & 0x04) && poDS->iChannelsMask & 0x04 ) + { + if ( poDS->eSpacecraftID >= NOAA15 + && poDS->eSpacecraftID <= METOP3 ) + if ( poDS->iInstrumentStatus & 0x0400 ) + poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[7] ); + else + poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[6] ); + else + poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[2] ); + i |= 0x04; + continue; + } + if ( !(i & 0x08) && poDS->iChannelsMask & 0x08 ) + { + poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[3] ); + i |= 0x08; + continue; + } + if ( !(i & 0x10) && poDS->iChannelsMask & 0x10 ) + { + if (poDS->eSpacecraftID == NOAA13) // 5 NOAA-13 + poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[5] ); + else if (poDS->eSpacecraftID == NOAA6 || + poDS->eSpacecraftID == NOAA8 || + poDS->eSpacecraftID == NOAA10) // 4 NOAA-6,-8,-10 + poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[3] ); + else + poDS->GetRasterBand(iBand)->SetDescription( apszBandDesc[4] ); + i |= 0x10; + continue; + } + } + } + + if( poDS->bExposeMaskBand ) + poDS->poMaskBand = new L1BMaskBand(poDS); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + +/* -------------------------------------------------------------------- */ +/* Fetch metadata in CSV file */ +/* -------------------------------------------------------------------- */ + if( CSLTestBoolean(CPLGetConfigOption("L1B_FETCH_METADATA", "NO")) ) + { + poDS->FetchMetadata(); + } + + return( poDS ); + +bad: + delete poDS; + return NULL; +} + +/************************************************************************/ +/* GDALRegister_L1B() */ +/************************************************************************/ + +void GDALRegister_L1B() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "L1B" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "L1B" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "NOAA Polar Orbiter Level 1b Data Set" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_l1b.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + + poDriver->pfnOpen = L1BDataset::Open; + poDriver->pfnIdentify = L1BDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/l1b/makefile.vc b/bazaar/plugin/gdal/frmts/l1b/makefile.vc new file mode 100644 index 000000000..507b6019c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/l1b/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = l1bdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/leveller/GNUmakefile b/bazaar/plugin/gdal/frmts/leveller/GNUmakefile new file mode 100644 index 000000000..50cc6ae8f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/leveller/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = levellerdataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/leveller/frmt_leveller.html b/bazaar/plugin/gdal/frmts/leveller/frmt_leveller.html new file mode 100644 index 000000000..39a0aaec9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/leveller/frmt_leveller.html @@ -0,0 +1,73 @@ + + + Leveller --- Daylon Leveller heightfield + + + + +

    Leveller --- Daylon Leveller Heightfield

    + +Leveller heightfields store 32-bit elevation values. +Format versions 4 through 7 are supported with various caveats +(see below). The file extension for Leveller heightfields is "TER" (which +is the same as Terragen, but the driver only recognizes Leveller files).

    + +Blocks are organized as pixel-high scanlines (rows), with the first scanline +at the top (north) edge of the DEM, and adjacent pixels on each line +increasing from left to right (west to east).

    + +The band type is always Float32, even though format versions 4 and 5 +physically use 16.16 fixed-point. The driver autoconverts them to +floating-point.

    + + +

    Reading

    +dataset::GetProjectionRef() will return only a local coordinate system +for file versions 4 through 6.

    + +dataset::GetGeoTransform() returns a simple world scaling with a centered +origin for formats 4 through 6. For version 7, it returns a realworld transform +except for rotations. The identity transform is not considered an error condition; +Leveller documents often use them.

    + +band::GetUnitType() will report the measurement units used +by the file instead of converting unusual types to meters. +A list of unit types is in the levellerdataset.cpp module.

    + +band::GetScale() and band::GetOffset() will +return the physical-to-logical (i.e., raw to realworld) transform +for the elevation data.

    + + +

    Writing

    +The dataset::Create() call is supported, but for version 7 files only.

    + +band::SetUnitType() can be set to any of the unit types listed +in the levellerdataset.cpp module.

    + +dataset::SetGeoTransform() should not include rotation data.

    + +As with the Terragen driver, the MINUSERPIXELVALUE option must be +specified. This lets the driver correctly map from logical (realworld) elevations +to physical elevations.

    + +Header information is written out on the first call to band::IWriteBlock.

    + +

    History

    +v1.2 (Jul 17/07): Added v7 and Create support.

    +v1.1 (Oct 20/05): Fixed coordsys and elev scaling errors.

    + +

    See Also:

    + +
      +
    • Implemented as gdal/frmts/leveller/levellerdataset.cpp.

      + +

    • +Leveller SDK, which documents the Leveller format.

      + +

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/leveller/levellerdataset.cpp b/bazaar/plugin/gdal/frmts/leveller/levellerdataset.cpp new file mode 100644 index 000000000..98cbd3000 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/leveller/levellerdataset.cpp @@ -0,0 +1,1623 @@ +/****************************************************************************** + * levellerdataset.cpp,v 1.22 + * + * Project: Leveller TER Driver + * Purpose: Reader for Leveller TER documents + * Author: Ray Gardener, Daylon Graphics Ltd. + * + * Portions of this module derived from GDAL drivers by + * Frank Warmerdam, see http://www.gdal.org + * + ****************************************************************************** + * Copyright (c) 2005-2007 Daylon Graphics Ltd. + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + +#include "gdal_pam.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: levellerdataset.cpp 28435 2015-02-07 14:35:34Z rouault $"); + +CPL_C_START +void GDALRegister_Leveller(void); +CPL_C_END + +#if 1 + +#define str_equal(_s1, _s2) (0 == strcmp((_s1),(_s2))) +#define array_size(_a) (sizeof(_a) / sizeof(_a[0])) + +/*GDALDataset *LevellerCreateCopy( const char *, GDALDataset *, int, char **, + GDALProgressFunc pfnProgress, + void * pProgressData ); + +*/ + +/************************************************************************/ +/* ==================================================================== */ +/* LevellerDataset */ +/* ==================================================================== */ +/************************************************************************/ + +static const size_t kMaxTagNameLen = 63; + +enum +{ + // Leveller coordsys types. + LEV_COORDSYS_RASTER = 0, + LEV_COORDSYS_LOCAL, + LEV_COORDSYS_GEO +}; + +enum +{ + // Leveller digital axis extent styles. + LEV_DA_POSITIONED = 0, + LEV_DA_SIZED, + LEV_DA_PIXEL_SIZED +}; + +typedef enum +{ + // Measurement unit IDs, OEM version. + UNITLABEL_UNKNOWN = 0x00000000, + UNITLABEL_PIXEL = 0x70780000, + UNITLABEL_PERCENT = 0x25000000, + + UNITLABEL_RADIAN = 0x72616400, + UNITLABEL_DEGREE = 0x64656700, + UNITLABEL_ARCMINUTE = 0x6172636D, + UNITLABEL_ARCSECOND = 0x61726373, + + UNITLABEL_YM = 0x796D0000, + UNITLABEL_ZM = 0x7A6D0000, + UNITLABEL_AM = 0x616D0000, + UNITLABEL_FM = 0x666D0000, + UNITLABEL_PM = 0x706D0000, + UNITLABEL_A = 0x41000000, + UNITLABEL_NM = 0x6E6D0000, + UNITLABEL_U = 0x75000000, + UNITLABEL_UM = 0x756D0000, + UNITLABEL_PPT = 0x70707400, + UNITLABEL_PT = 0x70740000, + UNITLABEL_MM = 0x6D6D0000, + UNITLABEL_P = 0x70000000, + UNITLABEL_CM = 0x636D0000, + UNITLABEL_IN = 0x696E0000, + UNITLABEL_DFT = 0x64667400, + UNITLABEL_DM = 0x646D0000, + UNITLABEL_LI = 0x6C690000, + UNITLABEL_SLI = 0x736C6900, + UNITLABEL_SP = 0x73700000, + UNITLABEL_FT = 0x66740000, + UNITLABEL_SFT = 0x73667400, + UNITLABEL_YD = 0x79640000, + UNITLABEL_SYD = 0x73796400, + UNITLABEL_M = 0x6D000000, + UNITLABEL_FATH = 0x66617468, + UNITLABEL_R = 0x72000000, + UNITLABEL_RD = UNITLABEL_R, + UNITLABEL_DAM = 0x64416D00, + UNITLABEL_DKM = UNITLABEL_DAM, + UNITLABEL_CH = 0x63680000, + UNITLABEL_SCH = 0x73636800, + UNITLABEL_HM = 0x686D0000, + UNITLABEL_F = 0x66000000, + UNITLABEL_KM = 0x6B6D0000, + UNITLABEL_MI = 0x6D690000, + UNITLABEL_SMI = 0x736D6900, + UNITLABEL_NMI = 0x6E6D6900, + UNITLABEL_MEGAM = 0x4D6D0000, + UNITLABEL_LS = 0x6C730000, + UNITLABEL_GM = 0x476D0000, + UNITLABEL_LM = 0x6C6D0000, + UNITLABEL_AU = 0x41550000, + UNITLABEL_TM = 0x546D0000, + UNITLABEL_LHR = 0x6C687200, + UNITLABEL_LD = 0x6C640000, + UNITLABEL_PETAM = 0x506D0000, + UNITLABEL_LY = 0x6C790000, + UNITLABEL_PC = 0x70630000, + UNITLABEL_EXAM = 0x456D0000, + UNITLABEL_KLY = 0x6B6C7900, + UNITLABEL_KPC = 0x6B706300, + UNITLABEL_ZETTAM = 0x5A6D0000, + UNITLABEL_MLY = 0x4D6C7900, + UNITLABEL_MPC = 0x4D706300, + UNITLABEL_YOTTAM = 0x596D0000 +} UNITLABEL; + + +typedef struct +{ + const char* pszID; + double dScale; + UNITLABEL oemCode; +} measurement_unit; + +static const double kdays_per_year = 365.25; +static const double kdLStoM = 299792458.0; +static const double kdLYtoM = kdLStoM * kdays_per_year * 24 * 60 * 60; +static const double kdInch = 0.3048 / 12; +static const double kPI = 3.1415926535897932384626433832795; + +static const int kFirstLinearMeasureIdx = 9; + +static const measurement_unit kUnits[] = +{ + { "", 1.0, UNITLABEL_UNKNOWN }, + { "px", 1.0, UNITLABEL_PIXEL }, + { "%", 1.0, UNITLABEL_PERCENT }, // not actually used + + { "rad", 1.0, UNITLABEL_RADIAN }, + { "\xB0", kPI / 180.0, UNITLABEL_DEGREE }, // \xB0 is Unicode degree symbol + { "d", kPI / 180.0, UNITLABEL_DEGREE }, + { "deg", kPI / 180.0, UNITLABEL_DEGREE }, + { "'", kPI / (60.0 * 180.0), UNITLABEL_ARCMINUTE }, + { "\"", kPI / (3600.0 * 180.0), UNITLABEL_ARCSECOND }, + + { "ym", 1.0e-24, UNITLABEL_YM }, + { "zm", 1.0e-21, UNITLABEL_ZM }, + { "am", 1.0e-18, UNITLABEL_AM }, + { "fm", 1.0e-15, UNITLABEL_FM }, + { "pm", 1.0e-12, UNITLABEL_PM }, + { "A", 1.0e-10, UNITLABEL_A }, + { "nm", 1.0e-9, UNITLABEL_NM }, + { "u", 1.0e-6, UNITLABEL_U }, + { "um", 1.0e-6, UNITLABEL_UM }, + { "ppt", kdInch / 72.27, UNITLABEL_PPT }, + { "pt", kdInch / 72.0, UNITLABEL_PT }, + { "mm", 1.0e-3, UNITLABEL_MM }, + { "p", kdInch / 6.0, UNITLABEL_P }, + { "cm", 1.0e-2, UNITLABEL_CM }, + { "in", kdInch, UNITLABEL_IN }, + { "dft", 0.03048, UNITLABEL_DFT }, + { "dm", 0.1, UNITLABEL_DM }, + { "li", 0.2011684 /* GDAL 0.20116684023368047 ? */, UNITLABEL_LI }, + { "sli", 0.201168402336805, UNITLABEL_SLI }, + { "sp", 0.2286, UNITLABEL_SP }, + { "ft", 0.3048, UNITLABEL_FT }, + { "sft", 1200.0 / 3937.0, UNITLABEL_SFT }, + { "yd", 0.9144, UNITLABEL_YD }, + { "syd", 0.914401828803658, UNITLABEL_SYD }, + { "m", 1.0, UNITLABEL_M }, + { "fath", 1.8288, UNITLABEL_FATH }, + { "rd", 5.02921, UNITLABEL_RD }, + { "dam", 10.0, UNITLABEL_DAM }, + { "dkm", 10.0, UNITLABEL_DKM }, + { "ch", 20.1168 /* GDAL: 2.0116684023368047 ? */, UNITLABEL_CH }, + { "sch", 20.1168402336805, UNITLABEL_SCH }, + { "hm", 100.0, UNITLABEL_HM }, + { "f", 201.168, UNITLABEL_F }, + { "km", 1000.0, UNITLABEL_KM }, + { "mi", 1609.344, UNITLABEL_MI }, + { "smi", 1609.34721869444, UNITLABEL_SMI }, + { "nmi", 1853.0, UNITLABEL_NMI }, + { "Mm", 1.0e+6, UNITLABEL_MEGAM }, + { "ls", kdLStoM, UNITLABEL_LS }, + { "Gm", 1.0e+9, UNITLABEL_GM }, + { "lm", kdLStoM * 60, UNITLABEL_LM }, + { "AU", 8.317 * kdLStoM * 60, UNITLABEL_AU }, + { "Tm", 1.0e+12, UNITLABEL_TM }, + { "lhr", 60.0 * 60.0 * kdLStoM, UNITLABEL_LHR }, + { "ld", 24 * 60.0 * 60.0 * kdLStoM, UNITLABEL_LD }, + { "Pm", 1.0e+15, UNITLABEL_PETAM }, + { "ly", kdLYtoM, UNITLABEL_LY }, + { "pc", 3.2616 * kdLYtoM, UNITLABEL_PC }, + { "Em", 1.0e+18, UNITLABEL_EXAM }, + { "kly", 1.0e+3 * kdLYtoM, UNITLABEL_KLY }, + { "kpc", 3.2616 * 1.0e+3 * kdLYtoM, UNITLABEL_KPC }, + { "Zm", 1.0e+21, UNITLABEL_ZETTAM }, + { "Mly", 1.0e+6 * kdLYtoM, UNITLABEL_MLY }, + { "Mpc", 3.2616 * 1.0e+6 * kdLYtoM, UNITLABEL_MPC }, + { "Ym", 1.0e+24, UNITLABEL_YOTTAM } +}; + +// ---------------------------------------------------------------- + +static bool approx_equal(double a, double b) +{ + const double epsilon = 1e-5; + return (fabs(a-b) <= epsilon); +} + + +// ---------------------------------------------------------------- + +class LevellerRasterBand; + +class LevellerDataset : public GDALPamDataset +{ + friend class LevellerRasterBand; + friend class digital_axis; + + int m_version; + + char* m_pszFilename; + char* m_pszProjection; + + //char m_szUnits[8]; + char m_szElevUnits[8]; + double m_dElevScale; // physical-to-logical scaling. + double m_dElevBase; // logical offset. + double m_adfTransform[6]; + //double m_dMeasurePerPixel; + double m_dLogSpan[2]; + + VSILFILE* m_fp; + vsi_l_offset m_nDataOffset; + + bool load_from_file(VSILFILE*, const char*); + + + bool locate_data(vsi_l_offset&, size_t&, VSILFILE*, const char*); + bool get(int&, VSILFILE*, const char*); + bool get(size_t& n, VSILFILE* fp, const char* psz) + { return this->get((int&)n, fp, psz); } + bool get(double&, VSILFILE*, const char*); + bool get(char*, size_t, VSILFILE*, const char*); + + bool write_header(); + bool write_tag(const char*, int); + bool write_tag(const char*, size_t); + bool write_tag(const char*, double); + bool write_tag(const char*, const char*); + bool write_tag_start(const char*, size_t); + bool write(int); + bool write(size_t); + bool write(double); + bool write_byte(size_t); + + const measurement_unit* get_uom(const char*) const; + const measurement_unit* get_uom(UNITLABEL) const; + const measurement_unit* get_uom(double) const; + + bool convert_measure(double, double&, const char* pszUnitsFrom); + bool make_local_coordsys(const char* pszName, const char* pszUnits); + bool make_local_coordsys(const char* pszName, UNITLABEL); + const char* code_to_id(UNITLABEL) const; + UNITLABEL id_to_code(const char*) const; + UNITLABEL meter_measure_to_code(double) const; + bool compute_elev_scaling(const OGRSpatialReference&); + void raw_to_proj(double, double, double&, double&); + +public: + LevellerDataset(); + ~LevellerDataset(); + + static GDALDataset* Open( GDALOpenInfo* ); + static int Identify( GDALOpenInfo* ); + static GDALDataset* Create( const char* pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char** papszOptions ); + + virtual CPLErr GetGeoTransform( double* ); + virtual const char* GetProjectionRef(void); + + virtual CPLErr SetGeoTransform( double* ); + virtual CPLErr SetProjection(const char*); +}; + + +class digital_axis +{ + public: + digital_axis() : m_eStyle(LEV_DA_PIXEL_SIZED) {} + + bool get(LevellerDataset& ds, VSILFILE* fp, int n) + { + char szTag[32]; + sprintf(szTag, "coordsys_da%d_style", n); + if(!ds.get(m_eStyle, fp, szTag)) + return false; + sprintf(szTag, "coordsys_da%d_fixedend", n); + if(!ds.get(m_fixedEnd, fp, szTag)) + return false; + sprintf(szTag, "coordsys_da%d_v0", n); + if(!ds.get(m_d[0], fp, szTag)) + return false; + sprintf(szTag, "coordsys_da%d_v1", n); + if(!ds.get(m_d[1], fp, szTag)) + return false; + return true; + } + + double origin(size_t pixels) const + { + if(m_fixedEnd == 1) + { + switch(m_eStyle) + { + case LEV_DA_SIZED: + return m_d[1] + m_d[0]; + + case LEV_DA_PIXEL_SIZED: + return m_d[1] + (m_d[0] * (pixels-1)); + } + } + return m_d[0]; + } + + double scaling(size_t pixels) const + { + CPLAssert(pixels > 1); + if(m_eStyle == LEV_DA_PIXEL_SIZED) + return m_d[1 - m_fixedEnd]; + + return this->length(pixels) / (pixels - 1); + } + + double length(int pixels) const + { + // Return the signed length of the axis. + + switch(m_eStyle) + { + case LEV_DA_POSITIONED: + return m_d[1] - m_d[0]; + + case LEV_DA_SIZED: + return m_d[1 - m_fixedEnd]; + + case LEV_DA_PIXEL_SIZED: + return m_d[1 - m_fixedEnd] * (pixels-1); + + } + CPLAssert(FALSE); + return 0.0; + } + + + protected: + int m_eStyle; + size_t m_fixedEnd; + double m_d[2]; +}; + + +/************************************************************************/ +/* ==================================================================== */ +/* LevellerRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class LevellerRasterBand : public GDALPamRasterBand +{ + friend class LevellerDataset; + + float* m_pLine; + bool m_bFirstTime; + +public: + + LevellerRasterBand(LevellerDataset*); + ~LevellerRasterBand(); + + // Geomeasure support. + virtual const char* GetUnitType(); + virtual double GetScale(int* pbSuccess = NULL); + virtual double GetOffset(int* pbSuccess = NULL); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + virtual CPLErr SetUnitType( const char* ); +}; + + +/************************************************************************/ +/* LevellerRasterBand() */ +/************************************************************************/ + +LevellerRasterBand::LevellerRasterBand( LevellerDataset *poDS ) + : + m_pLine(NULL), + m_bFirstTime(true) +{ + this->poDS = poDS; + this->nBand = 1; + + eDataType = GDT_Float32; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1;//poDS->GetRasterYSize(); + + m_pLine = (float*)CPLMalloc(sizeof(float) * nBlockXSize); +} + + +LevellerRasterBand::~LevellerRasterBand() +{ + if(m_pLine != NULL) + CPLFree(m_pLine); +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr LevellerRasterBand::IWriteBlock +( + CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void* pImage +) +{ + CPLAssert( nBlockXOff == 0 ); + CPLAssert( pImage != NULL ); + CPLAssert( m_pLine != NULL ); + +/* #define sgn(_n) ((_n) < 0 ? -1 : ((_n) > 0 ? 1 : 0) ) + #define sround(_f) \ + (int)((_f) + (0.5 * sgn(_f))) +*/ + const size_t pixelsize = sizeof(float); + + LevellerDataset& ds = *(LevellerDataset*)poDS; + if(m_bFirstTime) + { + m_bFirstTime = false; + if(!ds.write_header()) + return CE_Failure; + ds.m_nDataOffset = VSIFTellL(ds.m_fp); + } + const size_t rowbytes = nBlockXSize * pixelsize; + const float* pfImage = (float*)pImage; + + if(0 == VSIFSeekL( + ds.m_fp, ds.m_nDataOffset + nBlockYOff * rowbytes, + SEEK_SET)) + { + for(size_t x = 0; x < (size_t)nBlockXSize; x++) + { + // Convert logical elevations to physical. + m_pLine[x] = (float) + ((pfImage[x] - ds.m_dElevBase) / ds.m_dElevScale); + } + +#ifdef CPL_MSB + GDALSwapWords( m_pLine, pixelsize, nBlockXSize, pixelsize ); +#endif + if(1 == VSIFWriteL(m_pLine, rowbytes, 1, ds.m_fp)) + return CE_None; + } + + return CE_Failure; +} + + +CPLErr LevellerRasterBand::SetUnitType( const char* psz ) +{ + LevellerDataset& ds = *(LevellerDataset*)poDS; + + if(strlen(psz) >= sizeof(ds.m_szElevUnits)) + return CE_Failure; + + strcpy(ds.m_szElevUnits, psz); + + return CE_None; +} + + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr LevellerRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void* pImage ) + +{ + CPLAssert( sizeof(float) == sizeof(GInt32) ); + CPLAssert( nBlockXOff == 0 ); + CPLAssert( pImage != NULL ); + + LevellerDataset *poGDS = (LevellerDataset *) poDS; + +/* -------------------------------------------------------------------- */ +/* Seek to scanline. */ +/* -------------------------------------------------------------------- */ + const size_t rowbytes = nBlockXSize * sizeof(float); + + if(0 != VSIFSeekL( + poGDS->m_fp, + poGDS->m_nDataOffset + nBlockYOff * rowbytes, + SEEK_SET)) + { + CPLError( CE_Failure, CPLE_FileIO, + ".bt Seek failed:%s", VSIStrerror( errno ) ); + return CE_Failure; + } + + +/* -------------------------------------------------------------------- */ +/* Read the scanline into the image buffer. */ +/* -------------------------------------------------------------------- */ + + if( VSIFReadL( pImage, rowbytes, 1, poGDS->m_fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Leveller read failed:%s", VSIStrerror( errno ) ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Swap on MSB platforms. */ +/* -------------------------------------------------------------------- */ +#ifdef CPL_MSB + GDALSwapWords( pImage, 4, nRasterXSize, 4 ); +#endif + +/* -------------------------------------------------------------------- */ +/* Convert from legacy-format fixed-point if necessary. */ +/* -------------------------------------------------------------------- */ + float* pf = (float*)pImage; + + if(poGDS->m_version < 6) + { + GInt32* pi = (int*)pImage; + for(size_t i = 0; i < (size_t)nBlockXSize; i++) + pf[i] = (float)pi[i] / 65536; + } + + +#if 0 +/* -------------------------------------------------------------------- */ +/* Convert raw elevations to realworld elevs. */ +/* -------------------------------------------------------------------- */ + for(size_t i = 0; i < nBlockXSize; i++) + pf[i] *= poGDS->m_dWorldscale; //this->GetScale(); +#endif + + return CE_None; +} + + + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ +const char *LevellerRasterBand::GetUnitType() +{ + // Return elevation units. + + LevellerDataset *poGDS = (LevellerDataset *) poDS; + + return poGDS->m_szElevUnits; +} + + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ + +double LevellerRasterBand::GetScale(int* pbSuccess) +{ + LevellerDataset *poGDS = (LevellerDataset *) poDS; + if(pbSuccess != NULL) + *pbSuccess = TRUE; + return poGDS->m_dElevScale; +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +double LevellerRasterBand::GetOffset(int* pbSuccess) +{ + LevellerDataset *poGDS = (LevellerDataset *) poDS; + if(pbSuccess != NULL) + *pbSuccess = TRUE; + return poGDS->m_dElevBase; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* LevellerDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* LevellerDataset() */ +/************************************************************************/ + +LevellerDataset::LevellerDataset() +{ + m_fp = NULL; + m_pszProjection = NULL; + m_pszFilename = NULL; +} + +/************************************************************************/ +/* ~LevellerDataset() */ +/************************************************************************/ + +LevellerDataset::~LevellerDataset() +{ + FlushCache(); + + CPLFree(m_pszProjection); + CPLFree(m_pszFilename); + + if( m_fp != NULL ) + VSIFCloseL( m_fp ); +} + +static double degrees_to_radians(double d) +{ + return (d * 0.017453292); +} + + +static double average(double a, double b) +{ + return 0.5 * (a + b); +} + + +void LevellerDataset::raw_to_proj(double x, double y, double& xp, double& yp) +{ + xp = x * m_adfTransform[1] + m_adfTransform[0]; + yp = y * m_adfTransform[5] + m_adfTransform[3]; +} + + +bool LevellerDataset::compute_elev_scaling +( + const OGRSpatialReference& sr +) +{ + const char* pszGroundUnits; + + if(!sr.IsGeographic()) + { + // For projected or local CS, the elev scale is + // the average ground scale. + m_dElevScale = average(m_adfTransform[1], m_adfTransform[5]); + + const double dfLinear = sr.GetLinearUnits(); + const measurement_unit* pu = this->get_uom(dfLinear); + if(pu == NULL) + return false; + + pszGroundUnits = pu->pszID; + } + else + { + pszGroundUnits = "m"; + + const double kdEarthCircumPolar = 40007849; + const double kdEarthCircumEquat = 40075004; + + double xr, yr; + xr = 0.5 * this->nRasterXSize; + yr = 0.5 * this->nRasterYSize; + + double xg[2], yg[2]; + this->raw_to_proj(xr, yr, xg[0], yg[0]); + this->raw_to_proj(xr+1, yr+1, xg[1], yg[1]); + + // The earths' circumference shrinks using a sin() + // curve as we go up in latitude. + const double dLatCircum = kdEarthCircumEquat + * sin(degrees_to_radians(90.0 - yg[0])); + + // Derive meter distance between geolongitudes + // in xg[0] and xg[1]. + double dx = fabs(xg[1] - xg[0]) / 360.0 * dLatCircum; + double dy = fabs(yg[1] - yg[0]) / 360.0 * kdEarthCircumPolar; + + m_dElevScale = average(dx, dy); + } + + m_dElevBase = m_dLogSpan[0]; + + // Convert from ground units to elev units. + const measurement_unit* puG = this->get_uom(pszGroundUnits); + const measurement_unit* puE = this->get_uom(m_szElevUnits); + + if(puG == NULL || puE == NULL) + return false; + + const double g_to_e = puG->dScale / puE->dScale; + + m_dElevScale *= g_to_e; + return true; +} + + +bool LevellerDataset::write_header() +{ + char szHeader[5]; + strcpy(szHeader, "trrn"); + szHeader[4] = 7; // TER v7 introduced w/ Lev 2.6. + + if(1 != VSIFWriteL(szHeader, 5, 1, m_fp) + || !this->write_tag("hf_w", (size_t)nRasterXSize) + || !this->write_tag("hf_b", (size_t)nRasterYSize)) + { + CPLError( CE_Failure, CPLE_FileIO, "Could not write header" ); + return false; + } + + m_dElevBase = 0.0; + m_dElevScale = 1.0; + + if(m_pszProjection == NULL || m_pszProjection[0] == 0) + { + this->write_tag("csclass", LEV_COORDSYS_RASTER); + } + else + { + this->write_tag("coordsys_wkt", m_pszProjection); + const UNITLABEL units_elev = this->id_to_code(m_szElevUnits); + + const int bHasECS = + (units_elev != UNITLABEL_PIXEL && units_elev != UNITLABEL_UNKNOWN); + + this->write_tag("coordsys_haselevm", bHasECS); + + OGRSpatialReference sr(m_pszProjection); + + if(bHasECS) + { + if(!this->compute_elev_scaling(sr)) + return false; + + // Raw-to-real units scaling. + this->write_tag("coordsys_em_scale", m_dElevScale); + + //elev offset, in real units. + this->write_tag("coordsys_em_base", m_dElevBase); + + this->write_tag("coordsys_em_units", units_elev); + } + + + if(sr.IsLocal()) + { + this->write_tag("csclass", LEV_COORDSYS_LOCAL); + + const double dfLinear = sr.GetLinearUnits(); + const int n = this->meter_measure_to_code(dfLinear); + this->write_tag("coordsys_units", n); + } + else + { + this->write_tag("csclass", LEV_COORDSYS_GEO); + } + + if( m_adfTransform[2] != 0.0 || m_adfTransform[4] != 0.0) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Cannot handle rotated geotransform" ); + return false; + } + + // todo: GDAL gridpost spacing is based on extent / rastersize + // instead of extent / (rastersize-1) like Leveller. + // We need to look into this and adjust accordingly. + + // Write north-south digital axis. + this->write_tag("coordsys_da0_style", LEV_DA_PIXEL_SIZED); + this->write_tag("coordsys_da0_fixedend", 0); + this->write_tag("coordsys_da0_v0", m_adfTransform[3]); + this->write_tag("coordsys_da0_v1", m_adfTransform[5]); + + // Write east-west digital axis. + this->write_tag("coordsys_da1_style", LEV_DA_PIXEL_SIZED); + this->write_tag("coordsys_da1_fixedend", 0); + this->write_tag("coordsys_da1_v0", m_adfTransform[0]); + this->write_tag("coordsys_da1_v1", m_adfTransform[1]); + } + + + this->write_tag_start("hf_data", + sizeof(float) * nRasterXSize * nRasterYSize); + + return true; +} + + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr LevellerDataset::SetGeoTransform( double *padfGeoTransform ) +{ + memcpy(m_adfTransform, padfGeoTransform, + sizeof(m_adfTransform)); + + return CE_None; +} + + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr LevellerDataset::SetProjection( const char * pszNewProjection ) +{ + if(m_pszProjection != NULL) + CPLFree(m_pszProjection); + + m_pszProjection = CPLStrdup(pszNewProjection); + + return CE_None; +} + + +/************************************************************************/ +/* Create() */ +/************************************************************************/ +GDALDataset* LevellerDataset::Create +( + const char* pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char** papszOptions +) +{ + if(nBands != 1) + { + CPLError( CE_Failure, CPLE_IllegalArg, "Band count must be 1" ); + return NULL; + } + + if(eType != GDT_Float32) + { + CPLError( CE_Failure, CPLE_IllegalArg, "Pixel type must be Float32" ); + return NULL; + } + + if(nXSize < 2 || nYSize < 2) + { + CPLError( CE_Failure, CPLE_IllegalArg, "One or more raster dimensions too small" ); + return NULL; + } + + + LevellerDataset* poDS = new LevellerDataset; + + poDS->eAccess = GA_Update; + + poDS->m_pszFilename = CPLStrdup(pszFilename); + + poDS->m_fp = VSIFOpenL( pszFilename, "wb+" ); + + if( poDS->m_fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.", + pszFilename ); + delete poDS; + return NULL; + } + + // Header will be written the first time IWriteBlock + // is called. + + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + + + const char* pszValue = CSLFetchNameValue( + papszOptions,"MINUSERPIXELVALUE"); + if( pszValue != NULL ) + poDS->m_dLogSpan[0] = CPLAtof( pszValue ); + else + { + delete poDS; + CPLError( CE_Failure, CPLE_IllegalArg, + "MINUSERPIXELVALUE must be specified." ); + return NULL; + } + + pszValue = CSLFetchNameValue( + papszOptions,"MAXUSERPIXELVALUE"); + if( pszValue != NULL ) + poDS->m_dLogSpan[1] = CPLAtof( pszValue ); + + if(poDS->m_dLogSpan[1] < poDS->m_dLogSpan[0]) + { + double t = poDS->m_dLogSpan[0]; + poDS->m_dLogSpan[0] = poDS->m_dLogSpan[1]; + poDS->m_dLogSpan[1] = t; + } + +// -------------------------------------------------------------------- +// Instance a band. +// -------------------------------------------------------------------- + poDS->SetBand( 1, new LevellerRasterBand( poDS ) ); + + return poDS; +} + + +bool LevellerDataset::write_byte(size_t n) +{ + unsigned char uch = (unsigned char)n; + return (1 == VSIFWriteL(&uch, 1, 1, m_fp)); +} + + +bool LevellerDataset::write(int n) +{ + CPL_LSBPTR32(&n); + return (1 == VSIFWriteL(&n, sizeof(n), 1, m_fp)); +} + + +bool LevellerDataset::write(size_t n) +{ + CPL_LSBPTR32(&n); + return (1 == VSIFWriteL(&n, sizeof(n), 1, m_fp)); +} + + +bool LevellerDataset::write(double d) +{ + CPL_LSBPTR64(&d); + return (1 == VSIFWriteL(&d, sizeof(d), 1, m_fp)); +} + + + +bool LevellerDataset::write_tag_start(const char* pszTag, size_t n) +{ + if(this->write_byte(strlen(pszTag))) + { + return (1 == VSIFWriteL(pszTag, strlen(pszTag), 1, m_fp) + && this->write(n)); + } + + return false; +} + + +bool LevellerDataset::write_tag(const char* pszTag, int n) +{ + return (this->write_tag_start(pszTag, sizeof(n)) + && this->write(n)); +} + + +bool LevellerDataset::write_tag(const char* pszTag, size_t n) +{ + return (this->write_tag_start(pszTag, sizeof(n)) + && this->write(n)); +} + + +bool LevellerDataset::write_tag(const char* pszTag, double d) +{ + return (this->write_tag_start(pszTag, sizeof(d)) + && this->write(d)); +} + + +bool LevellerDataset::write_tag(const char* pszTag, const char* psz) +{ + CPLAssert(strlen(pszTag) <= kMaxTagNameLen); + + char sz[kMaxTagNameLen + 1]; + sprintf(sz, "%sl", pszTag); + const size_t len = strlen(psz); + + if(len > 0 && this->write_tag(sz, len)) + { + sprintf(sz, "%sd", pszTag); + this->write_tag_start(sz, len); + return (1 == VSIFWriteL(psz, len, 1, m_fp)); + } + return false; +} + + +bool LevellerDataset::locate_data(vsi_l_offset& offset, size_t& len, VSILFILE* fp, const char* pszTag) +{ + // Locate the file offset of the desired tag's data. + // If it is not available, return false. + // If the tag is found, leave the filemark at the + // start of its data. + + if(0 != VSIFSeekL(fp, 5, SEEK_SET)) + return false; + + const int kMaxDescLen = 64; + for(;;) + { + unsigned char c; + if(1 != VSIFReadL(&c, sizeof(c), 1, fp)) + return false; + + const size_t descriptorLen = c; + if(descriptorLen == 0 || descriptorLen > (size_t)kMaxDescLen) + return false; + + char descriptor[kMaxDescLen+1]; + if(1 != VSIFReadL(descriptor, descriptorLen, 1, fp)) + return false; + + GUInt32 datalen; + if(1 != VSIFReadL(&datalen, sizeof(datalen), 1, fp)) + return false; + + datalen = CPL_LSBWORD32(datalen); + descriptor[descriptorLen] = 0; + if(str_equal(descriptor, pszTag)) + { + len = (size_t)datalen; + offset = VSIFTellL(fp); + return true; + } + else + { + // Seek to next tag. + if(0 != VSIFSeekL(fp, (vsi_l_offset)datalen, SEEK_CUR)) + return false; + } + } +} + +/************************************************************************/ +/* get() */ +/************************************************************************/ + +bool LevellerDataset::get(int& n, VSILFILE* fp, const char* psz) +{ + vsi_l_offset offset; + size_t len; + + if(this->locate_data(offset, len, fp, psz)) + { + GInt32 value; + if(1 == VSIFReadL(&value, sizeof(value), 1, fp)) + { + CPL_LSBPTR32(&value); + n = (int)value; + return true; + } + } + return false; +} + +/************************************************************************/ +/* get() */ +/************************************************************************/ + +bool LevellerDataset::get(double& d, VSILFILE* fp, const char* pszTag) +{ + vsi_l_offset offset; + size_t len; + + if(this->locate_data(offset, len, fp, pszTag)) + { + if(1 == VSIFReadL(&d, sizeof(d), 1, fp)) + { + CPL_LSBPTR64(&d); + return true; + } + } + return false; +} + + +/************************************************************************/ +/* get() */ +/************************************************************************/ +bool LevellerDataset::get(char* pszValue, size_t maxchars, VSILFILE* fp, const char* pszTag) +{ + char szTag[65]; + + // We can assume 8-bit encoding, so just go straight + // to the *_d tag. + sprintf(szTag, "%sd", pszTag); + + vsi_l_offset offset; + size_t len; + + if(this->locate_data(offset, len, fp, szTag)) + { + if(len > maxchars) + return false; + + if(1 == VSIFReadL(pszValue, len, 1, fp)) + { + pszValue[len] = 0; // terminate C-string + return true; + } + } + + return false; +} + + + +UNITLABEL LevellerDataset::meter_measure_to_code(double dM) const +{ + // Convert a meter conversion factor to its UOM OEM code. + // If the factor is close to the approximation margin, then + // require exact equality, otherwise be loose. + + const measurement_unit* pu = this->get_uom(dM); + return (pu != NULL ? pu->oemCode : UNITLABEL_UNKNOWN); +} + + +UNITLABEL LevellerDataset::id_to_code(const char* pszUnits) const +{ + // Convert a readable UOM to its OEM code. + + const measurement_unit* pu = this->get_uom(pszUnits); + return (pu != NULL ? pu->oemCode : UNITLABEL_UNKNOWN); +} + + +const char* LevellerDataset::code_to_id(UNITLABEL code) const +{ + // Convert a measurement unit's OEM ID to its readable ID. + + const measurement_unit* pu = this->get_uom(code); + return (pu != NULL ? pu->pszID : NULL); +} + + +const measurement_unit* LevellerDataset::get_uom(const char* pszUnits) const +{ + for(size_t i = 0; i < array_size(kUnits); i++) + { + if(strcmp(pszUnits, kUnits[i].pszID) == 0) + return &kUnits[i]; + } + CPLError( CE_Failure, CPLE_AppDefined, + "Unknown measurement units: %s", pszUnits ); + return NULL; +} + + +const measurement_unit* LevellerDataset::get_uom(UNITLABEL code) const +{ + for(size_t i = 0; i < array_size(kUnits); i++) + { + if(kUnits[i].oemCode == code) + return &kUnits[i]; + } + CPLError( CE_Failure, CPLE_AppDefined, + "Unknown measurement unit code: %08x", code ); + return NULL; +} + + +const measurement_unit* LevellerDataset::get_uom(double dM) const +{ + for(size_t i = kFirstLinearMeasureIdx; i < array_size(kUnits); i++) + { + if(dM >= 1.0e-4) + { + if(approx_equal(dM, kUnits[i].dScale)) + return &kUnits[i]; + } + else if(dM == kUnits[i].dScale) + return &kUnits[i]; + } + CPLError( CE_Failure, CPLE_AppDefined, + "Unknown measurement conversion factor: %f", dM ); + return NULL; +} + +/************************************************************************/ +/* convert_measure() */ +/************************************************************************/ + + +bool LevellerDataset::convert_measure +( + double d, + double& dResult, + const char* pszSpace +) +{ + // Convert a measure to meters. + + for(size_t i = kFirstLinearMeasureIdx; i < array_size(kUnits); i++) + { + if(str_equal(pszSpace, kUnits[i].pszID)) + { + dResult = d * kUnits[i].dScale; + return true; + } + } + CPLError( CE_Failure, CPLE_FileIO, + "Unknown linear measurement unit: '%s'", pszSpace ); + return false; +} + + +bool LevellerDataset::make_local_coordsys(const char* pszName, const char* pszUnits) +{ + OGRSpatialReference sr; + + sr.SetLocalCS(pszName); + double d; + return ( this->convert_measure(1.0, d, pszUnits) + && OGRERR_NONE == sr.SetLinearUnits(pszUnits, d) + && OGRERR_NONE == sr.exportToWkt(&m_pszProjection) ); +} + + +bool LevellerDataset::make_local_coordsys(const char* pszName, UNITLABEL code) +{ + const char* pszUnitID = this->code_to_id(code); + return ( pszUnitID != NULL + && this->make_local_coordsys(pszName, pszUnitID)); +} + + + +/************************************************************************/ +/* load_from_file() */ +/************************************************************************/ + +bool LevellerDataset::load_from_file(VSILFILE* file, const char* pszFilename) +{ + // get hf dimensions + if(!this->get(nRasterXSize, file, "hf_w")) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Cannot determine heightfield width." ); + return false; + } + + if(!this->get(nRasterYSize, file, "hf_b")) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Cannot determine heightfield breadth." ); + return false; + } + + if(nRasterXSize < 2 || nRasterYSize < 2) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Heightfield raster dimensions too small." ); + return false; + } + + // Record start of pixel data + size_t datalen; + if(!this->locate_data(m_nDataOffset, datalen, file, "hf_data")) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Cannot locate elevation data." ); + return false; + } + + // Sanity check: do we have enough pixels? + if(datalen != nRasterXSize * nRasterYSize * sizeof(float)) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "File does not have enough data." ); + return false; + } + + + // Defaults for raster coordsys. + m_adfTransform[0] = 0.0; + m_adfTransform[1] = 1.0; + m_adfTransform[2] = 0.0; + m_adfTransform[3] = 0.0; + m_adfTransform[4] = 0.0; + m_adfTransform[5] = 1.0; + + m_dElevScale = 1.0; + m_dElevBase = 0.0; + strcpy(m_szElevUnits, ""); + + if(m_version == 7) + { + // Read coordsys info. + int csclass = LEV_COORDSYS_RASTER; + (void)this->get(csclass, file, "csclass"); + + if(csclass != LEV_COORDSYS_RASTER) + { + // Get projection details and units. + + CPLAssert(m_pszProjection == NULL); + + if(csclass == LEV_COORDSYS_LOCAL) + { + UNITLABEL unitcode; + //char szLocalUnits[8]; + int unitcode_int; + if(!this->get(unitcode_int, file, "coordsys_units")) + unitcode_int = UNITLABEL_M; + unitcode = (UNITLABEL) unitcode_int; + + if(!this->make_local_coordsys("Leveller", unitcode)) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Cannot define local coordinate system." ); + return false; + } + } + else if(csclass == LEV_COORDSYS_GEO) + { + char szWKT[1024]; + if(!this->get(szWKT, 1023, file, "coordsys_wkt")) + return 0; + + m_pszProjection = (char*)CPLMalloc(strlen(szWKT) + 1); + strcpy(m_pszProjection, szWKT); + } + else + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unknown coordinate system type in %s.", + pszFilename ); + return false; + } + + // Get ground extents. + digital_axis axis_ns, axis_ew; + + if(axis_ns.get(*this, file, 0) + && axis_ew.get(*this, file, 1)) + { + m_adfTransform[0] = axis_ew.origin(nRasterXSize); + m_adfTransform[1] = axis_ew.scaling(nRasterXSize); + m_adfTransform[2] = 0.0; + + m_adfTransform[3] = axis_ns.origin(nRasterYSize); + m_adfTransform[4] = 0.0; + m_adfTransform[5] = axis_ns.scaling(nRasterYSize); + } + } + + // Get vertical (elev) coordsys. + int bHasVertCS = FALSE; + if(this->get(bHasVertCS, file, "coordsys_haselevm") && bHasVertCS) + { + this->get(m_dElevScale, file, "coordsys_em_scale"); + this->get(m_dElevBase, file, "coordsys_em_base"); + UNITLABEL unitcode; + int unitcode_int; + if(this->get(unitcode_int, file, "coordsys_em_units")) + { + unitcode = (UNITLABEL) unitcode_int; + const char* pszUnitID = this->code_to_id(unitcode); + if(pszUnitID != NULL) + strcpy(m_szElevUnits, pszUnitID); + else + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unknown OEM elevation unit of measure (%d)", + unitcode ); + return false; + } + } + // datum and localcs are currently unused. + } + } + else + { + // Legacy files use world units. + char szWorldUnits[32]; + strcpy(szWorldUnits, "m"); + + double dWorldscale = 1.0; + + if(this->get(dWorldscale, file, "hf_worldspacing")) + { + //m_bHasWorldscale = true; + if(this->get(szWorldUnits, sizeof(szWorldUnits)-1, file, + "hf_worldspacinglabel")) + { + // Drop long name, if present. + char* p = strchr(szWorldUnits, ' '); + if(p != NULL) + *p = 0; + } + +#if 0 + // If the units are something besides m/ft/sft, + // then convert them to meters. + + if(!str_equal("m", szWorldUnits) + && !str_equal("ft", szWorldUnits) + && !str_equal("sft", szWorldUnits)) + { + dWorldscale = this->convert_measure(dWorldscale, szWorldUnits); + strcpy(szWorldUnits, "m"); + } +#endif + + // Our extents are such that the origin is at the + // center of the heightfield. + m_adfTransform[0] = -0.5 * dWorldscale * (nRasterXSize-1); + m_adfTransform[3] = -0.5 * dWorldscale * (nRasterYSize-1); + m_adfTransform[1] = dWorldscale; + m_adfTransform[5] = dWorldscale; + } + m_dElevScale = dWorldscale; // this was 1.0 before because + // we were converting to real elevs ourselves, but + // some callers may want both the raw pixels and the + // transform to get real elevs. + + if(!this->make_local_coordsys("Leveller world space", szWorldUnits)) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Cannot define local coordinate system." ); + return false; + } + } + + return true; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* LevellerDataset::GetProjectionRef(void) +{ + return (m_pszProjection == NULL ? "" : m_pszProjection); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr LevellerDataset::GetGeoTransform(double* padfTransform) + +{ + memcpy(padfTransform, m_adfTransform, sizeof(m_adfTransform)); + return CE_None; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int LevellerDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + if( poOpenInfo->nHeaderBytes < 4 ) + return FALSE; + + return EQUALN((const char *) poOpenInfo->pabyHeader, "trrn", 4); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *LevellerDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + // The file should have at least 5 header bytes + // and hf_w, hf_b, and hf_data tags. +#ifdef DEBUG + +#endif + if( poOpenInfo->nHeaderBytes < 5+13+13+16 ) + return NULL; + + if( !LevellerDataset::Identify(poOpenInfo)) + return NULL; + + const int version = poOpenInfo->pabyHeader[4]; + if(version < 4 || version > 7) + return NULL; + + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + + LevellerDataset* poDS = new LevellerDataset(); + + poDS->m_version = version; + + // Reopen for large file access. + if( poOpenInfo->eAccess == GA_Update ) + poDS->m_fp = VSIFOpenL( poOpenInfo->pszFilename, "rb+" ); + else + poDS->m_fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + + if( poDS->m_fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to re-open %s within Leveller driver.", + poOpenInfo->pszFilename ); + return NULL; + } + poDS->eAccess = poOpenInfo->eAccess; + + +/* -------------------------------------------------------------------- */ +/* Read the file. */ +/* -------------------------------------------------------------------- */ + if( !poDS->load_from_file( poDS->m_fp, poOpenInfo->pszFilename ) ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->SetBand( 1, new LevellerRasterBand( poDS )); + + poDS->SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return( poDS ); +} + +#else +// stub so that module compiles +class LevellerDataset : public GDALPamDataset +{ + public: + static GDALDataset* Open( GDALOpenInfo* ) { return NULL; } +}; +#endif + +/************************************************************************/ +/* GDALRegister_Leveller() */ +/************************************************************************/ + +void GDALRegister_Leveller() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "Leveller" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "Leveller" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, + "ter" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Leveller heightfield" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_leveller.html" ); + +#if GDAL_VERSION_NUM >= 1500 + poDriver->pfnIdentify = LevellerDataset::Identify; +#endif + poDriver->pfnOpen = LevellerDataset::Open; + poDriver->pfnCreate = LevellerDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/leveller/makefile.vc b/bazaar/plugin/gdal/frmts/leveller/makefile.vc new file mode 100644 index 000000000..8f12eefbb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/leveller/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = levellerdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/makefile.vc b/bazaar/plugin/gdal/frmts/makefile.vc new file mode 100644 index 000000000..8d350381c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/makefile.vc @@ -0,0 +1,232 @@ + +GDAL_ROOT = .. + +EXTRAFLAGS = -DFRMT_ceos -DFRMT_aigrid -DFRMT_elas -DFRMT_hfa -DFRMT_gtiff\ + -DFRMT_sdts -DFRMT_raw -DFRMT_gxf -DFRMT_ceos2 -DFRMT_png \ + -DFRMT_dted -DFRMT_mem -DFRMT_jdem -DFRMT_gif \ + -DFRMT_envisat -DFRMT_aaigrid -DFRMT_usgsdem -DFRMT_l1b \ + -DFRMT_fit -DFRMT_vrt -DFRMT_xpm -DFRMT_bmp -DFRMT_rmf \ + -DFRMT_nitf -DFRMT_pcidsk -DFRMT_airsar -DFRMT_rs2 \ + -DFRMT_ilwis -DFRMT_msgn -DFRMT_rik -DFRMT_pcraster \ + -DFRMT_leveller -DFRMT_sgi -DFRMT_srtmhgt -DFRMT_idrisi \ + -DFRMT_jaxapalsar -DFRMT_ers -DFRMT_ingr -DFRMT_dimap \ + -DFRMT_gff -DFRMT_terragen -DFRMT_gsg -DFRMT_cosar -DFRMT_pds \ + -DFRMT_adrg -DFRMT_coasp -DFRMT_tsx -DFRMT_blx -DFRMT_til \ + -DFRMT_r -DFRMT_northwood -DFRMT_saga -DFRMT_xyz -DFRMT_hf2 \ + -DFRMT_kmlsuperoverlay -DFRMT_ozi -DFRMT_ctg -DFRMT_e00grid \ + -DFRMT_zmap -DFRMT_ngsgeoid -DFRMT_iris -DFRMT_map + +MOREEXTRA = + +DIRLIST = $(EXTRAFLAGS:-DFRMT_=) + +PLUGINFLAGS = + +PLUGINDIRLIST = $(PLUGINFLAGS:-DFRMT_=) + +!INCLUDE $(GDAL_ROOT)/nmake.opt + +!IFDEF PG_INC_DIR +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_postgisraster +!ENDIF + +!IFDEF JPEG_SUPPORTED +EXTRAFLAGS = -DFRMT_jpeg $(EXTRAFLAGS) +!ENDIF + +!IFDEF BSB_SUPPORTED +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_bsb +!ENDIF + +!IFDEF OGDIDIR +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_ogdi +!ELSE +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_zlib +!ENDIF + +!IFDEF JASPER_DIR +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_jpeg2000 +!ENDIF + +!IFDEF OPENJPEG_ENABLED +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_openjpeg +!ENDIF + +!IFDEF KAKDIR +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_jp2kak -DFRMT_jpipkak +!ENDIF + +!IFDEF ECWDIR +!IF "$(ECW_PLUGIN)" != "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_ecw +!ELSE +PLUGINFLAGS = $(PLUGINFLAGS) -DFRMT_ecw +!ENDIF +!ENDIF + +!IFDEF HDF4_DIR +!IF "$(HDF4_PLUGIN)" != "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_hdf4 +!ENDIF +!ENDIF + +!IFDEF HDF5_DIR +!IF "$(HDF5_PLUGIN)" != "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_hdf5 +!ELSE +PLUGINFLAGS = $(PLUGINFLAGS) -DFRMT_hdf5 +!ENDIF +!ENDIF + +!IF DEFINED(MRSID_DIR) || DEFINED(MRSID_RASTER_DIR) || DEFINED(MRSID_LIDAR_DIR) +!INCLUDE mrsid\nmake.opt +!ENDIF + +!IFDEF MRSID_RASTER_DIR +!IF "$(MRSID_PLUGIN)" != "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_mrsid +!ELSE +PLUGINFLAGS = $(PLUGINFLAGS) -DFRMT_mrsid +!ENDIF +!ENDIF + +!IFDEF MRSID_LIDAR_DIR +!IF "$(MRSID_LIDAR_PLUGIN)" != "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_mrsid_lidar +!ELSE +PLUGINFLAGS = $(PLUGINFLAGS) -DFRMT_mrsid_lidar +!ENDIF +!ENDIF + +!IFDEF FITS_INC_DIR +!IF "$(FITS_PLUGIN)" != "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_fits +!ELSE +PLUGINFLAGS = $(PLUGINFLAGS) -DFRMT_fits +!ENDIF +!ENDIF + +!IF DEFINED(POPPLER_LIBS) || DEFINED(PODOFO_LIBS) +!IF "$(PDF_PLUGIN)" != "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_pdf +!ELSE +PLUGINFLAGS = $(PLUGINFLAGS) -DFRMT_pdf +!ENDIF +!ENDIF + +!IFDEF DODS_DIR +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_dods +!ENDIF + +!IFDEF NETCDF_SETTING +!IF "$(NETCDF_PLUGIN)" != "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_netcdf +!ELSE +PLUGINFLAGS = $(PLUGINFLAGS) -DFRMT_netcdf +!ENDIF +!ENDIF + +!IFDEF GRIB_SETTING +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_grib +!ENDIF + +!IFDEF CURL_LIB +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_wcs -DFRMT_wms -DFRMT_plmosaic +!ENDIF + +!IFDEF SDE_ENABLED +!IF "$(SDE_PLUGIN)" != "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_sde +!ELSE +PLUGINFLAGS = $(PLUGINFLAGS) -DFRMT_sde +!ENDIF +!ENDIF + +!IFDEF RASDAMAN_ENABLED +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_rasdaman +!ENDIF + +!IFDEF OCI_LIB +!IF "$(OCI_PLUGIN)" != "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_georaster +!ELSE +PLUGINFLAGS = $(PLUGINFLAGS) -DFRMT_georaster +!ENDIF +!ENDIF + +!IFDEF INCLUDE_OGR_FRMTS +!IFDEF SQLITE_LIB +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_rasterlite -DFRMT_mbtiles +!ENDIF +!ENDIF + +!IFDEF WEBP_ENABLED +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_webp +!ENDIF + +!IFDEF GTA_CFLAGS +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_gta +!ENDIF + +!IFDEF INCLUDE_OGR_FRMTS +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_arg +!ENDIF + +!IFDEF KEA_CFLAGS +!IF "$(KEA_PLUGIN)" != "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DFRMT_kea +!ELSE +PLUGINFLAGS = $(PLUGINFLAGS) -DFRMT_kea +!ENDIF +!ENDIF + +default: o\gdalallregister.obj subdirs + +list: + echo $(DIRLIST) + echo $(PLUGINDIRLIST) + +subdirs: + for %d in ( $(DIRLIST) ) do \ + cd %d \ + && $(MAKE) /f makefile.vc \ + && cd .. \ + || exit 1 + +plugindirs: + -for %d in ( $(PLUGINDIRLIST) ) do \ + cd %d \ + && $(MAKE) /f makefile.vc plugin \ + && cd .. \ + || exit 1 + +o\gdalallregister.obj: gdalallregister.cpp ..\nmake.opt + $(CC) $(CFLAGS) $(MOREEXTRA) /c gdalallregister.cpp + copy gdalallregister.obj o + +clean: + -del o\*.obj *.obj + -for %d in ( $(DIRLIST) ) do \ + cd %d \ + && $(MAKE) /f makefile.vc clean \ + && cd .. \ + || exit 1 + -for %d in ( $(PLUGINDIRLIST) ) do \ + cd %d \ + && $(MAKE) /f makefile.vc clean \ + && cd .. \ + || exit 1 + cd iso8211 + $(MAKE) /f makefile.vc clean + +plugins-install: + -for %d in ( $(PLUGINDIRLIST) ) do \ + cd %d \ + && $(MAKE) /f makefile.vc plugin-install \ + && cd .. \ + || exit 1 + +html-install: + copy *.html $(HTMLDIR) + -for %d in ( $(DIRLIST) ) do \ + copy %d\frmt_*.html $(HTMLDIR) diff --git a/bazaar/plugin/gdal/frmts/map/GNUmakefile b/bazaar/plugin/gdal/frmts/map/GNUmakefile new file mode 100644 index 000000000..e85bf23fc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/map/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = mapdataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/map/frmt_map.html b/bazaar/plugin/gdal/frmts/map/frmt_map.html new file mode 100644 index 000000000..b2e829b1a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/map/frmt_map.html @@ -0,0 +1,29 @@ + + + MAP --- OziExplorer .MAP + + + + +

    MAP --- OziExplorer .MAP

    + +(GDAL >= 1.10)

    + +OziExplorer MAP files. +

    +This driver does not support file creation. + +

    See Also:

    + + + + + + diff --git a/bazaar/plugin/gdal/frmts/map/makefile.vc b/bazaar/plugin/gdal/frmts/map/makefile.vc new file mode 100644 index 000000000..24b7d0c26 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/map/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = mapdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/map/mapdataset.cpp b/bazaar/plugin/gdal/frmts/map/mapdataset.cpp new file mode 100644 index 000000000..726f4444a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/map/mapdataset.cpp @@ -0,0 +1,511 @@ +/****************************************************************************** + * + * Project: OziExplorer .MAP Driver + * Purpose: GDALDataset driver for OziExplorer .MAP files + * Author: Jean-Claude Repetto, + * + ****************************************************************************** + * Copyright (c) 2012, Jean-Claude Repetto + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "gdal_proxy.h" +#include "ogr_spatialref.h" +#include "ogr_geometry.h" + +CPL_CVSID("$Id: mapdataset.cpp 27559 2014-08-04 17:52:19Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* MAPDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class CPL_DLL MAPDataset : public GDALDataset +{ + GDALDataset *poImageDS; + + char *pszWKT; + int bGeoTransformValid; + double adfGeoTransform[6]; + int nGCPCount; + GDAL_GCP *pasGCPList; + OGRPolygon *poNeatLine; + CPLString osImgFilename; + + public: + MAPDataset(); + virtual ~MAPDataset(); + + virtual const char* GetProjectionRef(); + virtual CPLErr GetGeoTransform( double * ); + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual char **GetFileList(); + + virtual int CloseDependentDatasets(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo *poOpenInfo ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* MAPWrapperRasterBand */ +/* ==================================================================== */ +/************************************************************************/ +class MAPWrapperRasterBand : public GDALProxyRasterBand +{ + GDALRasterBand* poBaseBand; + + protected: + virtual GDALRasterBand* RefUnderlyingRasterBand() { return poBaseBand; } + + public: + MAPWrapperRasterBand( GDALRasterBand* poBaseBand ) + { + this->poBaseBand = poBaseBand; + eDataType = poBaseBand->GetRasterDataType(); + poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + } + ~MAPWrapperRasterBand() {} +}; + +/************************************************************************/ +/* ==================================================================== */ +/* MAPDataset */ +/* ==================================================================== */ +/************************************************************************/ + +MAPDataset::MAPDataset() + +{ + poImageDS = NULL; + pszWKT = NULL; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + nGCPCount = 0; + pasGCPList = NULL; + poNeatLine = NULL; + bGeoTransformValid = false; +} + +/************************************************************************/ +/* ~MAPDataset() */ +/************************************************************************/ + +MAPDataset::~MAPDataset() + +{ + if (poImageDS != NULL) + { + GDALClose( poImageDS ); + poImageDS = NULL; + } + if (pszWKT != NULL) + { + CPLFree(pszWKT); + pszWKT = NULL; + } + if (nGCPCount) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree(pasGCPList); + } + + if ( poNeatLine != NULL ) + { + delete poNeatLine; + poNeatLine = NULL; + } +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int MAPDataset::CloseDependentDatasets() +{ + int bRet = GDALDataset::CloseDependentDatasets(); + if (poImageDS != NULL) + { + GDALClose( poImageDS ); + poImageDS = NULL; + bRet = TRUE; + } + return bRet; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int MAPDataset::Identify( GDALOpenInfo *poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 200 + || !EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"MAP") ) + return FALSE; + + if( strstr((const char *) poOpenInfo->pabyHeader,"OziExplorer Map Data File") == NULL ) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +#define MAX_GCP 30 + +GDALDataset *MAPDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The MAP driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + + MAPDataset *poDS = new MAPDataset(); + +/* -------------------------------------------------------------------- */ +/* Try to load and parse the .MAP file. */ +/* -------------------------------------------------------------------- */ + + int bOziFileOK = + GDALLoadOziMapFile( poOpenInfo->pszFilename, + poDS->adfGeoTransform, + &poDS->pszWKT, + &poDS->nGCPCount, &poDS->pasGCPList ); + + if ( bOziFileOK && poDS->nGCPCount == 0 ) + poDS->bGeoTransformValid = TRUE; + + /* We need to read again the .map file because the GDALLoadOziMapFile function + does not returns all required data . An API change is necessary : maybe in GDAL 2.0 ? */ + + char **papszLines; + int iLine, nLines=0; + + papszLines = CSLLoad2( poOpenInfo->pszFilename, 200, 200, NULL ); + + if ( !papszLines ) + return NULL; + + nLines = CSLCount( papszLines ); + if( nLines < 2 ) + { + CSLDestroy(papszLines); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* We need to open the image in order to establish */ +/* details like the band count and types. */ +/* -------------------------------------------------------------------- */ + poDS->osImgFilename = papszLines[2]; + VSIStatBufL sStat; + if (VSIStatL(poDS->osImgFilename, &sStat) != 0) + { + CPLString osPath = CPLGetPath(poOpenInfo->pszFilename); + if (CPLIsFilenameRelative(poDS->osImgFilename)) + { + poDS->osImgFilename = CPLFormCIFilename(osPath, poDS->osImgFilename, NULL); + } + else + { + poDS->osImgFilename = CPLGetFilename(poDS->osImgFilename); + poDS->osImgFilename = CPLFormCIFilename(osPath, poDS->osImgFilename, NULL); + } + } + +/* -------------------------------------------------------------------- */ +/* Try and open the file. */ +/* -------------------------------------------------------------------- */ + poDS->poImageDS = (GDALDataset *) GDALOpen(poDS->osImgFilename, GA_ReadOnly ); + if( poDS->poImageDS == NULL || poDS->poImageDS->GetRasterCount() == 0) + { + CSLDestroy(papszLines); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Attach the bands. */ +/* -------------------------------------------------------------------- */ + int iBand; + + poDS->nRasterXSize = poDS->poImageDS->GetRasterXSize(); + poDS->nRasterYSize = poDS->poImageDS->GetRasterYSize(); + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + GDALClose( poDS->poImageDS ); + return NULL; + } + + for( iBand = 1; iBand <= poDS->poImageDS->GetRasterCount(); iBand++ ) + poDS->SetBand( iBand, + new MAPWrapperRasterBand( poDS->poImageDS->GetRasterBand( iBand )) ); + +/* -------------------------------------------------------------------- */ +/* Add the neatline/cutline, if required */ +/* -------------------------------------------------------------------- */ + + /* First, we need to check if it is necessary to define a neatline */ + bool bNeatLine = false; + char **papszTok; + for ( iLine = 10; iLine < nLines; iLine++ ) + { + if ( EQUALN(papszLines[iLine], "MMPXY,", 6) ) + { + papszTok = CSLTokenizeString2( papszLines[iLine], ",", + CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES ); + + if ( CSLCount(papszTok) != 4 ) + { + CSLDestroy(papszTok); + continue; + } + + int x = CPLAtofM(papszTok[2]); + int y = CPLAtofM(papszTok[3]); + if (( x != 0 && x != poDS->nRasterXSize) || (y != 0 && y != poDS->nRasterYSize) ) + { + bNeatLine = true; + CSLDestroy(papszTok); + break; + } + CSLDestroy(papszTok); + } + } + + /* Create and fill the neatline polygon */ + if (bNeatLine) + { + poDS->poNeatLine = new OGRPolygon(); /* Create a polygon to store the neatline */ + OGRLinearRing* poRing = new OGRLinearRing(); + + if ( poDS->bGeoTransformValid ) /* Compute the projected coordinates of the corners */ + { + for ( iLine = 10; iLine < nLines; iLine++ ) + { + if ( EQUALN(papszLines[iLine], "MMPXY,", 6) ) + { + papszTok = CSLTokenizeString2( papszLines[iLine], ",", + CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES ); + + if ( CSLCount(papszTok) != 4 ) + { + CSLDestroy(papszTok); + continue; + } + + double x = CPLAtofM(papszTok[2]); + double y = CPLAtofM(papszTok[3]); + double X = poDS->adfGeoTransform[0] + x * poDS->adfGeoTransform[1] + + y * poDS->adfGeoTransform[2]; + double Y = poDS->adfGeoTransform[3] + x * poDS->adfGeoTransform[4] + + y * poDS->adfGeoTransform[5]; + poRing->addPoint(X, Y); + CPLDebug( "CORNER MMPXY", "%f, %f, %f, %f", x, y, X, Y); + CSLDestroy(papszTok); + } + } + } + else /* Convert the geographic coordinates to projected coordinates */ + { + OGRSpatialReference oSRS; + OGRSpatialReference *poLatLong = NULL; + OGRCoordinateTransformation *poTransform = NULL; + OGRErr eErr; + char *pszWKT = poDS->pszWKT; + + if ( &poDS->pszWKT != NULL ) + { + eErr = oSRS.importFromWkt ( &pszWKT ); + if ( eErr == OGRERR_NONE ) + poLatLong = oSRS.CloneGeogCS(); + if ( poLatLong ) + poTransform = OGRCreateCoordinateTransformation( poLatLong, &oSRS ); + } + + for ( iLine = 10; iLine < nLines; iLine++ ) + { + if ( EQUALN(papszLines[iLine], "MMPLL,", 6) ) + { + CPLDebug( "MMPLL", "%s", papszLines[iLine] ); + char **papszTok = NULL; + + papszTok = CSLTokenizeString2( papszLines[iLine], ",", + CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES ); + + if ( CSLCount(papszTok) != 4 ) + { + CSLDestroy(papszTok); + continue; + } + + double dfLon = CPLAtofM(papszTok[2]); + double dfLat = CPLAtofM(papszTok[3]); + + if ( poTransform ) + poTransform->Transform( 1, &dfLon, &dfLat ); + poRing->addPoint(dfLon, dfLat); + CPLDebug( "CORNER MMPLL", "%f, %f", dfLon, dfLat); + CSLDestroy(papszTok); + } + } + if (poTransform) + delete poTransform; + if (poLatLong) + delete poLatLong; + } + + poRing->closeRings(); + poDS->poNeatLine->addRingDirectly(poRing); + + char* pszNeatLineWkt = NULL; + poDS->poNeatLine->exportToWkt(&pszNeatLineWkt); + CPLDebug( "NEATLINE", "%s", pszNeatLineWkt); + poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt); + CPLFree(pszNeatLineWkt); + } + + CSLDestroy(papszLines); + + return( poDS ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* MAPDataset::GetProjectionRef() +{ + return (pszWKT && nGCPCount == 0) ? pszWKT : ""; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr MAPDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double)); + + return( (nGCPCount == 0) ? CE_None : CE_Failure ); +} + + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int MAPDataset::GetGCPCount() +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char * MAPDataset::GetGCPProjection() +{ + return (pszWKT && nGCPCount != 0) ? pszWKT : ""; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP * MAPDataset::GetGCPs() +{ + return pasGCPList; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char** MAPDataset::GetFileList() +{ + char **papszFileList = GDALDataset::GetFileList(); + + papszFileList = CSLAddString( papszFileList, osImgFilename ); + + return papszFileList; +} + +/************************************************************************/ +/* GDALRegister_MAP() */ +/************************************************************************/ + +void GDALRegister_MAP() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "MAP" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "MAP" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "OziExplorer .MAP" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_map.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = MAPDataset::Open; + poDriver->pfnIdentify = MAPDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + + diff --git a/bazaar/plugin/gdal/frmts/mbtiles/GNUmakefile b/bazaar/plugin/gdal/frmts/mbtiles/GNUmakefile new file mode 100644 index 000000000..338aceade --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mbtiles/GNUmakefile @@ -0,0 +1,17 @@ + +include ../../GDALmake.opt + +OBJ = mbtilesdataset.o + +ifeq ($(LIBZ_SETTING),internal) + XTRA_OPT := $(XTRA_OPT) -I../zlib +endif + +CPPFLAGS := $(JSON_INCLUDE) $(XTRA_OPT) $(CPPFLAGS) -I../../ogr + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/mbtiles/frmt_mbtiles.html b/bazaar/plugin/gdal/frmts/mbtiles/frmt_mbtiles.html new file mode 100644 index 000000000..aecf85a1e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mbtiles/frmt_mbtiles.html @@ -0,0 +1,115 @@ + + + + +MBTiles + + + + +

    MBTiles

    + +

    Starting with GDAL 1.10, the MBTiles driver allows reading rasters in the MBTiles format, +which is a specification for storing tiled map data in SQLite databases.

    + +

    GDAL/OGR must be compiled with OGR SQLite driver support, and JPEG and PNG drivers.

    + +

    The SRS is always the Pseudo-Mercator (a.k.a Google Mercator) projection.

    + +

    The driver can retrieve pixel attributes encoded according to the UTFGrid specification +available in some MBTiles files. They can be obtained with the gdallocationinfo utility, or +with a GetMetadataItem("Pixel_iCol_iLine", "LocationInfo") call on a band object.

    + +

    Examples:

    + +
      + +
    • Accessing a remote MBTiles raster : +
      +$ gdalinfo /vsicurl/http://a.tiles.mapbox.com/v3/kkaefer.iceland.mbtiles
      +
      +Output: +
      +Driver: MBTiles/MBTiles
      +Files: /vsicurl/http://a.tiles.mapbox.com/v3/kkaefer.iceland.mbtiles
      +Size is 16384, 16384
      +Coordinate System is:
      +PROJCS["WGS 84 / Pseudo-Mercator",
      +    GEOGCS["WGS 84",
      +        DATUM["WGS_1984",
      +            SPHEROID["WGS 84",6378137,298.257223563,
      +                AUTHORITY["EPSG","7030"]],
      +            AUTHORITY["EPSG","6326"]],
      +        PRIMEM["Greenwich",0,
      +            AUTHORITY["EPSG","8901"]],
      +        UNIT["degree",0.0174532925199433,
      +            AUTHORITY["EPSG","9122"]],
      +        AUTHORITY["EPSG","4326"]],
      +    PROJECTION["Mercator_1SP"],
      +    PARAMETER["central_meridian",0],
      +    PARAMETER["scale_factor",1],
      +    PARAMETER["false_easting",0],
      +    PARAMETER["false_northing",0],
      +    UNIT["metre",1,
      +        AUTHORITY["EPSG","9001"]],
      +    AXIS["X",EAST],
      +    AXIS["Y",NORTH],
      +    EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext  +no_defs"],
      +    AUTHORITY["EPSG","3857"]]
      +Origin = (-3757031.250000000000000,11271093.750000000000000)
      +Pixel Size = (152.873992919921875,-152.873992919921875)
      +Image Structure Metadata:
      +  INTERLEAVE=PIXEL
      +Corner Coordinates:
      +Upper Left  (-3757031.250,11271093.750) ( 33d44'59.95"W, 70d36'45.36"N)
      +Lower Left  (-3757031.250, 8766406.250) ( 33d44'59.95"W, 61d36'22.97"N)
      +Upper Right (-1252343.750,11271093.750) ( 11d14'59.98"W, 70d36'45.36"N)
      +Lower Right (-1252343.750, 8766406.250) ( 11d14'59.98"W, 61d36'22.97"N)
      +Center      (-2504687.500,10018750.000) ( 22d29'59.97"W, 66d30'47.68"N)
      +Band 1 Block=256x256 Type=Byte, ColorInterp=Red
      +  Overviews: 8192x8192, 4096x4096, 2048x2048, 1024x1024, 512x512
      +  Mask Flags: PER_DATASET ALPHA
      +  Overviews of mask band: 8192x8192, 4096x4096, 2048x2048, 1024x1024, 512x512
      +Band 2 Block=256x256 Type=Byte, ColorInterp=Green
      +  Overviews: 8192x8192, 4096x4096, 2048x2048, 1024x1024, 512x512
      +  Mask Flags: PER_DATASET ALPHA
      +  Overviews of mask band: 8192x8192, 4096x4096, 2048x2048, 1024x1024, 512x512
      +Band 3 Block=256x256 Type=Byte, ColorInterp=Blue
      +  Overviews: 8192x8192, 4096x4096, 2048x2048, 1024x1024, 512x512
      +  Mask Flags: PER_DATASET ALPHA
      +  Overviews of mask band: 8192x8192, 4096x4096, 2048x2048, 1024x1024, 512x512
      +Band 4 Block=256x256 Type=Byte, ColorInterp=Alpha
      +  Overviews: 8192x8192, 4096x4096, 2048x2048, 1024x1024, 512x512
      +
      +
    • + + +
    • Reading pixel attributes encoded according to the UTFGrid specification : +
      +$ gdallocationinfo /vsicurl/http://a.tiles.mapbox.com/v3/mapbox.geography-class.mbtiles -wgs84 2 49 -b 1 -xml
      +
      +Output: +
      +<Report pixel="33132" line="22506">
      +  <BandReport band="1">
      +    <LocationInfo>
      +      <Key>74</Key>
      +      <JSon>{"admin":"France","flag_png":"iVBORw0KGgoAAAANSUhEUgAAAGQAAABDEAIAAAC1uevOAAAACXBIWXMAAABIAAAASABGyWs+AAAABmJLR0T///////8JWPfcAAABPklEQVR42u3cMRLBQBSA4Zc9CgqcALXC4bThBA5gNFyFM+wBVNFqjYTszpfi1Sm++bOv2ETEdNK2pc/T9ny977rCn+fx8rjtc7dMmybnxXy9KncGWGCBBRZYYIEFFlhggQUWWGCBBRZYYIE1/GzSLB0CLLAUCyywwAILLLDAAgsssGyFlcAqnJRiKRZYYIEFFlhggQUWWGDZCsFSLLDAAgsssP4DazQowVIssMACy1ZYG6wP30qxwFIssMACCyywwOr/HAYWWIplKwQLLLDAAgssZyywwAILLLDAqh6We4VgKZatECywFAsssMACCyywwAILLLBshWCBpVhggQUWWGCBBRZYYIFlKwQLLMUCCyywwAILLLBG+T8ZsMBSLFshWIoFFlhg/fp8BhZYigUWWGB9C+t9ggUWWGD5FA44XxBz7mcwZM9VAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDExLTA5LTAyVDIzOjI5OjIxLTA0OjAwcQbBWgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxMS0wMi0yOFQyMTo0ODozMS0wNTowMJkeu+wAAABSdEVYdHN2ZzpiYXNlLXVyaQBmaWxlOi8vL2hvbWUvYWovQ29kZS90bS1tYXN0ZXIvZXhhbXBsZXMvZ2VvZ3JhcGh5LWNsYXNzL2ZsYWdzL0ZSQS5zdmen2JoeAAAAAElFTkSuQmCC"}</JSon>
      +    </LocationInfo>
      +    <Value>238</Value>
      +  </BandReport>
      +</Report>
      +
      +
    • + +
    + +

    See Also:

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/mbtiles/makefile.vc b/bazaar/plugin/gdal/frmts/mbtiles/makefile.vc new file mode 100644 index 000000000..3687c7568 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mbtiles/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = mbtilesdataset.obj + +EXTRAFLAGS = -I../../ogr -I../../ogr/ogrsf_frmts/geojson -I../../ogr/ogrsf_frmts/geojson/libjson -I../zlib + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/mbtiles/mbtilesdataset.cpp b/bazaar/plugin/gdal/frmts/mbtiles/mbtilesdataset.cpp new file mode 100644 index 000000000..75cac223c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mbtiles/mbtilesdataset.cpp @@ -0,0 +1,1938 @@ +/****************************************************************************** + * $Id: mbtilesdataset.cpp 28884 2015-04-12 20:48:02Z rouault $ + * + * Project: GDAL MBTiles driver + * Purpose: Implement GDAL MBTiles support using OGR SQLite driver + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_frmts.h" +#include "gdal_pam.h" +#include "ogr_api.h" +#include "cpl_vsil_curl_priv.h" + +#include "zlib.h" +#include "json.h" + +#include + +extern "C" void GDALRegister_MBTiles(); + +CPL_CVSID("$Id: mbtilesdataset.cpp 28884 2015-04-12 20:48:02Z rouault $"); + +static const char * const apszAllowedDrivers[] = {"JPEG", "PNG", NULL}; + +class MBTilesBand; + +/************************************************************************/ +/* MBTILESOpenSQLiteDB() */ +/************************************************************************/ + +OGRDataSourceH MBTILESOpenSQLiteDB(const char* pszFilename, + GDALAccess eAccess) +{ + const char* apszAllowedDrivers[] = { "SQLITE", NULL }; + return (OGRDataSourceH)GDALOpenEx(pszFilename, + GDAL_OF_VECTOR | + ((eAccess == GA_Update) ? GDAL_OF_UPDATE : 0), + apszAllowedDrivers, NULL, NULL); +} + +/************************************************************************/ +/* ==================================================================== */ +/* MBTilesDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class MBTilesDataset : public GDALPamDataset +{ + friend class MBTilesBand; + + public: + MBTilesDataset(); + MBTilesDataset(MBTilesDataset* poMainDS, int nLevel); + + virtual ~MBTilesDataset(); + + virtual CPLErr GetGeoTransform(double* padfGeoTransform); + virtual const char* GetProjectionRef(); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain = "" ); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + + char* FindKey(int iPixel, int iLine, + int& nTileColumn, int& nTileRow, int& nZoomLevel); + void ComputeTileColTileRowZoomLevel( int nBlockXOff, + int nBlockYOff, + int &nTileColumn, + int &nTileRow, + int &nZoomLevel ); + int HasNonEmptyGrids(); + + protected: + virtual int CloseDependentDatasets(); + + private: + + int bMustFree; + MBTilesDataset* poMainDS; + int nLevel; + int nMinTileCol, nMinTileRow; + int nMinLevel; + + char** papszMetadata; + char** papszImageStructure; + + int nResolutions; + MBTilesDataset** papoOverviews; + + OGRDataSourceH hDS; + + int bFetchedMetadata; + CPLStringList aosList; + + int bHasNonEmptyGrids; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* MBTilesBand */ +/* ==================================================================== */ +/************************************************************************/ + +class MBTilesBand: public GDALPamRasterBand +{ + friend class MBTilesDataset; + + CPLString osLocationInfo; + + public: + MBTilesBand( MBTilesDataset* poDS, int nBand, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize); + + virtual GDALColorInterp GetColorInterpretation(); + + virtual int GetOverviewCount(); + virtual GDALRasterBand* GetOverview(int nLevel); + + virtual CPLErr IReadBlock( int, int, void * ); + + virtual char **GetMetadataDomainList(); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); +}; + +/************************************************************************/ +/* MBTilesBand() */ +/************************************************************************/ + +MBTilesBand::MBTilesBand(MBTilesDataset* poDS, int nBand, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize) +{ + this->poDS = poDS; + this->nBand = nBand; + this->eDataType = eDataType; + this->nBlockXSize = nBlockXSize; + this->nBlockYSize = nBlockYSize; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr MBTilesBand::IReadBlock( int nBlockXOff, int nBlockYOff, void * pImage) +{ + MBTilesDataset* poGDS = (MBTilesDataset*) poDS; + + int bGotTile = FALSE; + CPLAssert(eDataType == GDT_Byte); + + int nTileColumn, nTileRow, nZoomLevel; + poGDS->ComputeTileColTileRowZoomLevel(nBlockXOff, nBlockYOff, + nTileColumn, nTileRow, nZoomLevel); + + const char* pszSQL = CPLSPrintf("SELECT tile_data FROM tiles WHERE " + "tile_column = %d AND tile_row = %d AND zoom_level=%d", + nTileColumn, nTileRow, nZoomLevel); + CPLDebug("MBTILES", "nBand=%d, nBlockXOff=%d, nBlockYOff=%d, %s", + nBand, nBlockXOff, nBlockYOff, pszSQL); + OGRLayerH hSQLLyr = OGR_DS_ExecuteSQL(poGDS->hDS, pszSQL, NULL, NULL); + + OGRFeatureH hFeat = hSQLLyr ? OGR_L_GetNextFeature(hSQLLyr) : NULL; + if (hFeat != NULL) + { + CPLString osMemFileName; + osMemFileName.Printf("/vsimem/%p", this); + + int nDataSize = 0; + GByte* pabyData = OGR_F_GetFieldAsBinary(hFeat, 0, &nDataSize); + + VSILFILE * fp = VSIFileFromMemBuffer( osMemFileName.c_str(), pabyData, + nDataSize, FALSE); + VSIFCloseL(fp); + + GDALDatasetH hDSTile = GDALOpenEx(osMemFileName.c_str(), + GDAL_OF_RASTER | GDAL_OF_INTERNAL, + apszAllowedDrivers, NULL, NULL); + if (hDSTile != NULL) + { + int nTileBands = GDALGetRasterCount(hDSTile); + if (nTileBands == 4 && poGDS->nBands == 3) + nTileBands = 3; + + if (GDALGetRasterXSize(hDSTile) == nBlockXSize && + GDALGetRasterYSize(hDSTile) == nBlockYSize && + (nTileBands == poGDS->nBands || + (nTileBands == 1 && (poGDS->nBands == 3 || poGDS->nBands == 4)) || + (nTileBands == 3 && poGDS->nBands == 4))) + { + int iBand; + void* pSrcImage = NULL; + GByte abyTranslation[256][4]; + + bGotTile = TRUE; + + GDALColorTableH hCT = GDALGetRasterColorTable(GDALGetRasterBand(hDSTile, 1)); + if (nTileBands == 1 && (poGDS->nBands == 3 || poGDS->nBands == 4)) + { + if (hCT != NULL) + pSrcImage = CPLMalloc(nBlockXSize * nBlockYSize); + iBand = 1; + } + else + iBand = nBand; + + if (nTileBands == 3 && poGDS->nBands == 4 && iBand == 4) + memset(pImage, 255, nBlockXSize * nBlockYSize); + else + { + GDALRasterIO(GDALGetRasterBand(hDSTile, iBand), GF_Read, + 0, 0, nBlockXSize, nBlockYSize, + pImage, nBlockXSize, nBlockYSize, eDataType, 0, 0); + } + + if (pSrcImage != NULL && hCT != NULL) + { + int i; + memcpy(pSrcImage, pImage, nBlockXSize * nBlockYSize); + + int nEntryCount = GDALGetColorEntryCount( hCT ); + if (nEntryCount > 256) + nEntryCount = 256; + for(i = 0; i < nEntryCount; i++) + { + const GDALColorEntry* psEntry = GDALGetColorEntry( hCT, i ); + abyTranslation[i][0] = (GByte) psEntry->c1; + abyTranslation[i][1] = (GByte) psEntry->c2; + abyTranslation[i][2] = (GByte) psEntry->c3; + abyTranslation[i][3] = (GByte) psEntry->c4; + } + for(; i < 256; i++) + { + abyTranslation[i][0] = 0; + abyTranslation[i][1] = 0; + abyTranslation[i][2] = 0; + abyTranslation[i][3] = 0; + } + + for(i = 0; i < nBlockXSize * nBlockYSize; i++) + { + ((GByte*)pImage)[i] = abyTranslation[((GByte*)pSrcImage)[i]][nBand-1]; + } + } + + for(int iOtherBand=1;iOtherBand<=poGDS->nBands;iOtherBand++) + { + GDALRasterBlock *poBlock; + + if (iOtherBand == nBand) + continue; + + poBlock = ((MBTilesBand*)poGDS->GetRasterBand(iOtherBand))-> + TryGetLockedBlockRef(nBlockXOff,nBlockYOff); + + if (poBlock != NULL) + { + poBlock->DropLock(); + continue; + } + + poBlock = poGDS->GetRasterBand(iOtherBand)-> + GetLockedBlockRef(nBlockXOff,nBlockYOff, TRUE); + if (poBlock == NULL) + break; + + GByte* pabySrcBlock = (GByte *) poBlock->GetDataRef(); + if( pabySrcBlock == NULL ) + { + poBlock->DropLock(); + break; + } + + if (nTileBands == 3 && poGDS->nBands == 4 && iOtherBand == 4) + memset(pabySrcBlock, 255, nBlockXSize * nBlockYSize); + else if (nTileBands == 1 && (poGDS->nBands == 3 || poGDS->nBands == 4)) + { + int i; + if (pSrcImage) + { + for(i = 0; i < nBlockXSize * nBlockYSize; i++) + { + ((GByte*)pabySrcBlock)[i] = + abyTranslation[((GByte*)pSrcImage)[i]][iOtherBand-1]; + } + } + else + memcpy(pabySrcBlock, pImage, nBlockXSize * nBlockYSize); + } + else + { + GDALRasterIO(GDALGetRasterBand(hDSTile, iOtherBand), GF_Read, + 0, 0, nBlockXSize, nBlockYSize, + pabySrcBlock, nBlockXSize, nBlockYSize, eDataType, 0, 0); + } + + poBlock->DropLock(); + } + + CPLFree(pSrcImage); + } + else if (GDALGetRasterXSize(hDSTile) == nBlockXSize && + GDALGetRasterYSize(hDSTile) == nBlockYSize && + (nTileBands == 3 && poGDS->nBands == 1)) + { + bGotTile = TRUE; + + GByte* pabyRGBImage = (GByte*)CPLMalloc(3 * nBlockXSize * nBlockYSize); + GDALDatasetRasterIO(hDSTile, GF_Read, + 0, 0, nBlockXSize, nBlockYSize, + pabyRGBImage, nBlockXSize, nBlockYSize, eDataType, + 3, NULL, 3, 3 * nBlockXSize, 1); + for(int i=0;ihDS, hSQLLyr); + + if (!bGotTile) + { + memset(pImage, (nBand == 4) ? 0 : 255, nBlockXSize * nBlockYSize); + + for(int iOtherBand=1;iOtherBand<=poGDS->nBands;iOtherBand++) + { + GDALRasterBlock *poBlock; + + if (iOtherBand == nBand) + continue; + + poBlock = poGDS->GetRasterBand(iOtherBand)-> + GetLockedBlockRef(nBlockXOff,nBlockYOff, TRUE); + if (poBlock == NULL) + break; + + GByte* pabySrcBlock = (GByte *) poBlock->GetDataRef(); + if( pabySrcBlock == NULL ) + { + poBlock->DropLock(); + break; + } + + memset(pabySrcBlock, (iOtherBand == 4) ? 0 : 255, + nBlockXSize * nBlockYSize); + + poBlock->DropLock(); + } + } + + return CE_None; +} + +/************************************************************************/ +/* utf8decode() */ +/************************************************************************/ + +static unsigned utf8decode(const char* p, const char* end, int* len) +{ + unsigned char c = *(unsigned char*)p; + if (c < 0x80) { + *len = 1; + return c; + } else if (c < 0xc2) { + goto FAIL; + } + if (p+1 >= end || (p[1]&0xc0) != 0x80) goto FAIL; + if (c < 0xe0) { + *len = 2; + return + ((p[0] & 0x1f) << 6) + + ((p[1] & 0x3f)); + } else if (c == 0xe0) { + if (((unsigned char*)p)[1] < 0xa0) goto FAIL; + goto UTF8_3; +#if STRICT_RFC3629 + } else if (c == 0xed) { + // RFC 3629 says surrogate chars are illegal. + if (((unsigned char*)p)[1] >= 0xa0) goto FAIL; + goto UTF8_3; + } else if (c == 0xef) { + // 0xfffe and 0xffff are also illegal characters + if (((unsigned char*)p)[1]==0xbf && + ((unsigned char*)p)[2]>=0xbe) goto FAIL; + goto UTF8_3; +#endif + } else if (c < 0xf0) { + UTF8_3: + if (p+2 >= end || (p[2]&0xc0) != 0x80) goto FAIL; + *len = 3; + return + ((p[0] & 0x0f) << 12) + + ((p[1] & 0x3f) << 6) + + ((p[2] & 0x3f)); + } else if (c == 0xf0) { + if (((unsigned char*)p)[1] < 0x90) goto FAIL; + goto UTF8_4; + } else if (c < 0xf4) { + UTF8_4: + if (p+3 >= end || (p[2]&0xc0) != 0x80 || (p[3]&0xc0) != 0x80) goto FAIL; + *len = 4; +#if STRICT_RFC3629 + // RFC 3629 says all codes ending in fffe or ffff are illegal: + if ((p[1]&0xf)==0xf && + ((unsigned char*)p)[2] == 0xbf && + ((unsigned char*)p)[3] >= 0xbe) goto FAIL; +#endif + return + ((p[0] & 0x07) << 18) + + ((p[1] & 0x3f) << 12) + + ((p[2] & 0x3f) << 6) + + ((p[3] & 0x3f)); + } else if (c == 0xf4) { + if (((unsigned char*)p)[1] > 0x8f) goto FAIL; // after 0x10ffff + goto UTF8_4; + } else { + FAIL: + *len = 1; + return 0xfffd; // Unicode REPLACEMENT CHARACTER + } +} + + +/************************************************************************/ +/* ComputeTileColTileRowZoomLevel() */ +/************************************************************************/ + +void MBTilesDataset::ComputeTileColTileRowZoomLevel(int nBlockXOff, + int nBlockYOff, + int &nTileColumn, + int &nTileRow, + int &nZoomLevel) +{ + const int nBlockYSize = 256; + + int _nMinLevel = (poMainDS) ? poMainDS->nMinLevel : nMinLevel; + int _nMinTileCol = (poMainDS) ? poMainDS->nMinTileCol : nMinTileCol; + int _nMinTileRow = (poMainDS) ? poMainDS->nMinTileRow : nMinTileRow; + _nMinTileCol >>= nLevel; + + nTileColumn = nBlockXOff + _nMinTileCol; + nTileRow = (((nRasterYSize / nBlockYSize - 1 - nBlockYOff) << nLevel) + _nMinTileRow) >> nLevel; + nZoomLevel = ((poMainDS) ? poMainDS->nResolutions : nResolutions) - nLevel + _nMinLevel; +} + +/************************************************************************/ +/* HasNonEmptyGrids() */ +/************************************************************************/ + +int MBTilesDataset::HasNonEmptyGrids() +{ + OGRLayerH hSQLLyr; + OGRFeatureH hFeat; + const char* pszSQL; + + if (poMainDS) + return poMainDS->HasNonEmptyGrids(); + + if (bHasNonEmptyGrids >= 0) + return bHasNonEmptyGrids; + + bHasNonEmptyGrids = FALSE; + + if (OGR_DS_GetLayerByName(hDS, "grids") == NULL) + return FALSE; + + pszSQL = "SELECT type FROM sqlite_master WHERE name = 'grids'"; + CPLDebug("MBTILES", "%s", pszSQL); + hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, NULL, NULL); + if (hSQLLyr == NULL) + return FALSE; + + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat == NULL || !OGR_F_IsFieldSet(hFeat, 0)) + { + OGR_F_Destroy(hFeat); + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + return FALSE; + } + + int bGridsIsView = strcmp(OGR_F_GetFieldAsString(hFeat, 0), "view") == 0; + + OGR_F_Destroy(hFeat); + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + + bHasNonEmptyGrids = TRUE; + + /* In the case 'grids' is a view (and a join between the 'map' and 'grid_utfgrid' layers */ + /* the cost of evaluating a join is very long, even if grid_utfgrid is empty */ + /* so check it is not empty */ + if (bGridsIsView) + { + OGRLayerH hGridUTFGridLyr; + hGridUTFGridLyr = OGR_DS_GetLayerByName(hDS, "grid_utfgrid"); + if (hGridUTFGridLyr != NULL) + { + OGR_L_ResetReading(hGridUTFGridLyr); + hFeat = OGR_L_GetNextFeature(hGridUTFGridLyr); + OGR_F_Destroy(hFeat); + + bHasNonEmptyGrids = hFeat != NULL; + } + } + + return bHasNonEmptyGrids; +} + +/************************************************************************/ +/* FindKey() */ +/************************************************************************/ + +char* MBTilesDataset::FindKey(int iPixel, int iLine, + int& nTileColumn, int& nTileRow, int& nZoomLevel) +{ + const int nBlockXSize = 256, nBlockYSize = 256; + int nBlockXOff = iPixel / nBlockXSize; + int nBlockYOff = iLine / nBlockYSize; + + int nColInBlock = iPixel % nBlockXSize; + int nRowInBlock = iLine % nBlockXSize; + + ComputeTileColTileRowZoomLevel(nBlockXOff, nBlockYOff, + nTileColumn, nTileRow, nZoomLevel); + + char* pszKey = NULL; + + OGRLayerH hSQLLyr; + OGRFeatureH hFeat; + const char* pszSQL; + json_object* poGrid = NULL; + int i; + + /* See https://github.com/mapbox/utfgrid-spec/blob/master/1.0/utfgrid.md */ + /* for the explanation of the following processings */ + + pszSQL = CPLSPrintf("SELECT grid FROM grids WHERE " + "zoom_level = %d AND tile_column = %d AND tile_row = %d", + nZoomLevel, nTileColumn, nTileRow); + CPLDebug("MBTILES", "%s", pszSQL); + hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, NULL, NULL); + if (hSQLLyr == NULL) + return NULL; + + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat == NULL || !OGR_F_IsFieldSet(hFeat, 0)) + { + OGR_F_Destroy(hFeat); + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + return NULL; + } + + int nDataSize = 0; + GByte* pabyData = OGR_F_GetFieldAsBinary(hFeat, 0, &nDataSize); + + int nUncompressedSize = 256*256; + GByte* pabyUncompressed = (GByte*)CPLMalloc(nUncompressedSize + 1); + + z_stream sStream; + memset(&sStream, 0, sizeof(sStream)); + inflateInit(&sStream); + sStream.next_in = pabyData; + sStream.avail_in = nDataSize; + sStream.next_out = pabyUncompressed; + sStream.avail_out = nUncompressedSize; + int nStatus = inflate(&sStream, Z_FINISH); + inflateEnd(&sStream); + if (nStatus != Z_OK && nStatus != Z_STREAM_END) + { + CPLDebug("MBTILES", "Error unzipping grid"); + nUncompressedSize = 0; + pabyUncompressed[nUncompressedSize] = 0; + } + else + { + nUncompressedSize -= sStream.avail_out; + pabyUncompressed[nUncompressedSize] = 0; + //CPLDebug("MBTILES", "Grid size = %d", nUncompressedSize); + //CPLDebug("MBTILES", "Grid value = %s", (const char*)pabyUncompressed); + } + + struct json_tokener *jstok = NULL; + json_object* jsobj = NULL; + + if (nUncompressedSize == 0) + { + goto end; + } + + jstok = json_tokener_new(); + jsobj = json_tokener_parse_ex(jstok, (const char*)pabyUncompressed, -1); + if( jstok->err != json_tokener_success) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JSON parsing error: %s (at offset %d)", + json_tokener_error_desc(jstok->err), + jstok->char_offset); + json_tokener_free(jstok); + + goto end; + } + + json_tokener_free(jstok); + + if (json_object_is_type(jsobj, json_type_object)) + { + poGrid = json_object_object_get(jsobj, "grid"); + } + if (poGrid != NULL && json_object_is_type(poGrid, json_type_array)) + { + int nLines; + int nFactor; + json_object* poRow; + char* pszRow = NULL; + + nLines = json_object_array_length(poGrid); + if (nLines == 0) + goto end; + + nFactor = 256 / nLines; + nRowInBlock /= nFactor; + nColInBlock /= nFactor; + + poRow = json_object_array_get_idx(poGrid, nRowInBlock); + + /* Extract line of interest in grid */ + if (poRow != NULL && json_object_is_type(poRow, json_type_string)) + { + pszRow = CPLStrdup(json_object_get_string(poRow)); + } + + if (pszRow == NULL) + goto end; + + /* Unapply JSON encoding */ + for (i = 0; pszRow[i] != '\0'; i++) + { + unsigned char c = ((GByte*)pszRow)[i]; + if (c >= 93) c--; + if (c >= 35) c--; + if (c < 32) + { + CPLDebug("MBTILES", "Invalid character at byte %d", i); + break; + } + c -= 32; + ((GByte*)pszRow)[i] = c; + } + + if (pszRow[i] == '\0') + { + char* pszEnd = pszRow + i; + + int iCol = 0; + i = 0; + int nKey = -1; + while(pszRow + i < pszEnd) + { + int len = 0; + unsigned int res = utf8decode(pszRow + i, pszEnd, &len); + + /* Invalid UTF8 ? */ + if (res > 127 && len == 1) + break; + + if (iCol == nColInBlock) + { + nKey = (int)res; + //CPLDebug("MBTILES", "Key index = %d", nKey); + break; + } + i += len; + iCol ++; + } + + /* Find key */ + json_object* poKeys = json_object_object_get(jsobj, "keys"); + if (nKey >= 0 && poKeys != NULL && + json_object_is_type(poKeys, json_type_array) && + nKey < json_object_array_length(poKeys)) + { + json_object* poKey = json_object_array_get_idx(poKeys, nKey); + if (poKey != NULL && json_object_is_type(poKey, json_type_string)) + { + pszKey = CPLStrdup(json_object_get_string(poKey)); + } + } + } + + CPLFree(pszRow); + } + +end: + if (jsobj) + json_object_put(jsobj); + if (pabyUncompressed) + CPLFree(pabyUncompressed); + if (hFeat) + OGR_F_Destroy(hFeat); + if (hSQLLyr) + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + + return pszKey; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **MBTilesBand::GetMetadataDomainList() +{ + return CSLAddString(GDALPamRasterBand::GetMetadataDomainList(), "LocationInfo"); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *MBTilesBand::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + MBTilesDataset* poGDS = (MBTilesDataset*) poDS; + +/* ==================================================================== */ +/* LocationInfo handling. */ +/* ==================================================================== */ + if( pszDomain != NULL + && EQUAL(pszDomain,"LocationInfo") + && (EQUALN(pszName,"Pixel_",6) || EQUALN(pszName,"GeoPixel_",9)) ) + { + int iPixel, iLine; + + if (!poGDS->HasNonEmptyGrids()) + return NULL; + +/* -------------------------------------------------------------------- */ +/* What pixel are we aiming at? */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszName,"Pixel_",6) ) + { + if( sscanf( pszName+6, "%d_%d", &iPixel, &iLine ) != 2 ) + return NULL; + } + else if( EQUALN(pszName,"GeoPixel_",9) ) + { + double adfGeoTransform[6]; + double adfInvGeoTransform[6]; + double dfGeoX, dfGeoY; + + dfGeoX = CPLAtof(pszName + 9); + const char* pszUnderscore = strchr(pszName + 9, '_'); + if( !pszUnderscore ) + return NULL; + dfGeoY = CPLAtof(pszUnderscore + 1); + + if( GetDataset() == NULL ) + return NULL; + + if( GetDataset()->GetGeoTransform( adfGeoTransform ) != CE_None ) + return NULL; + + if( !GDALInvGeoTransform( adfGeoTransform, adfInvGeoTransform ) ) + return NULL; + + iPixel = (int) floor( + adfInvGeoTransform[0] + + adfInvGeoTransform[1] * dfGeoX + + adfInvGeoTransform[2] * dfGeoY ); + iLine = (int) floor( + adfInvGeoTransform[3] + + adfInvGeoTransform[4] * dfGeoX + + adfInvGeoTransform[5] * dfGeoY ); + } + else + return NULL; + + if( iPixel < 0 || iLine < 0 + || iPixel >= GetXSize() + || iLine >= GetYSize() ) + return NULL; + + int nTileColumn = -1, nTileRow = -1, nZoomLevel = -1; + char* pszKey = poGDS->FindKey(iPixel, iLine, nTileColumn, nTileRow, nZoomLevel); + + if (pszKey != NULL) + { + //CPLDebug("MBTILES", "Key = %s", pszKey); + + osLocationInfo = ""; + osLocationInfo += ""; + char* pszXMLEscaped = CPLEscapeString(pszKey, -1, CPLES_XML_BUT_QUOTES); + osLocationInfo += pszXMLEscaped; + CPLFree(pszXMLEscaped); + osLocationInfo += ""; + + if (OGR_DS_GetLayerByName(poGDS->hDS, "grid_data") != NULL && + strchr(pszKey, '\'') == NULL) + { + const char* pszSQL; + OGRLayerH hSQLLyr; + OGRFeatureH hFeat; + + pszSQL = CPLSPrintf("SELECT key_json FROM keymap WHERE " + "key_name = '%s'", + pszKey); + CPLDebug("MBTILES", "%s", pszSQL); + hSQLLyr = OGR_DS_ExecuteSQL(poGDS->hDS, pszSQL, NULL, NULL); + if (hSQLLyr) + { + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat != NULL && OGR_F_IsFieldSet(hFeat, 0)) + { + const char* pszJSon = OGR_F_GetFieldAsString(hFeat, 0); + //CPLDebug("MBTILES", "JSon = %s", pszJSon); + + osLocationInfo += ""; +#ifdef CPLES_XML_BUT_QUOTES + pszXMLEscaped = CPLEscapeString(pszJSon, -1, CPLES_XML_BUT_QUOTES); +#else + pszXMLEscaped = CPLEscapeString(pszJSon, -1, CPLES_XML); +#endif + osLocationInfo += pszXMLEscaped; + CPLFree(pszXMLEscaped); + osLocationInfo += ""; + } + OGR_F_Destroy(hFeat); + } + OGR_DS_ReleaseResultSet(poGDS->hDS, hSQLLyr); + } + + osLocationInfo += ""; + + CPLFree(pszKey); + + return osLocationInfo.c_str(); + } + + return NULL; + } + else + return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain); +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int MBTilesBand::GetOverviewCount() +{ + MBTilesDataset* poGDS = (MBTilesDataset*) poDS; + + if (poGDS->nResolutions >= 1) + return poGDS->nResolutions; + else + return GDALPamRasterBand::GetOverviewCount(); +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand* MBTilesBand::GetOverview(int nLevel) +{ + MBTilesDataset* poGDS = (MBTilesDataset*) poDS; + + if (poGDS->nResolutions == 0) + return GDALPamRasterBand::GetOverview(nLevel); + + if (nLevel < 0 || nLevel >= poGDS->nResolutions) + return NULL; + + GDALDataset* poOvrDS = poGDS->papoOverviews[nLevel]; + if (poOvrDS) + return poOvrDS->GetRasterBand(nBand); + else + return NULL; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp MBTilesBand::GetColorInterpretation() +{ + MBTilesDataset* poGDS = (MBTilesDataset*) poDS; + if (poGDS->nBands == 1) + { + return GCI_GrayIndex; + } + else if (poGDS->nBands == 3 || poGDS->nBands == 4) + { + if (nBand == 1) + return GCI_RedBand; + else if (nBand == 2) + return GCI_GreenBand; + else if (nBand == 3) + return GCI_BlueBand; + else if (nBand == 4) + return GCI_AlphaBand; + } + + return GCI_Undefined; +} + +/************************************************************************/ +/* MBTilesDataset() */ +/************************************************************************/ + +MBTilesDataset::MBTilesDataset() +{ + bMustFree = FALSE; + nLevel = 0; + poMainDS = NULL; + nResolutions = 0; + hDS = NULL; + papoOverviews = NULL; + papszMetadata = NULL; + papszImageStructure = + CSLAddString(NULL, "INTERLEAVE=PIXEL"); + nMinTileCol = nMinTileRow = 0; + nMinLevel = 0; + bFetchedMetadata = FALSE; + bHasNonEmptyGrids = -1; +} + +/************************************************************************/ +/* MBTilesDataset() */ +/************************************************************************/ + +MBTilesDataset::MBTilesDataset(MBTilesDataset* poMainDS, int nLevel) +{ + bMustFree = FALSE; + this->nLevel = nLevel; + this->poMainDS = poMainDS; + nResolutions = poMainDS->nResolutions - nLevel; + hDS = poMainDS->hDS; + papoOverviews = poMainDS->papoOverviews + nLevel; + papszMetadata = poMainDS->papszMetadata; + papszImageStructure = poMainDS->papszImageStructure; + + nRasterXSize = poMainDS->nRasterXSize / (1 << nLevel); + nRasterYSize = poMainDS->nRasterYSize / (1 << nLevel); + nMinTileCol = nMinTileRow = 0; + nMinLevel = 0; + bFetchedMetadata = FALSE; + bHasNonEmptyGrids = -1; +} + +/************************************************************************/ +/* ~MBTilesDataset() */ +/************************************************************************/ + +MBTilesDataset::~MBTilesDataset() +{ + CloseDependentDatasets(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int MBTilesDataset::CloseDependentDatasets() +{ + int bRet = GDALPamDataset::CloseDependentDatasets(); + + if (poMainDS == NULL && !bMustFree) + { + CSLDestroy(papszMetadata); + papszMetadata = NULL; + CSLDestroy(papszImageStructure); + papszImageStructure = NULL; + + int i; + + if (papoOverviews) + { + for(i=0;ibMustFree) + { + papoOverviews[i]->poMainDS = NULL; + } + delete papoOverviews[i]; + } + CPLFree(papoOverviews); + papoOverviews = NULL; + nResolutions = 0; + bRet = TRUE; + } + + if (hDS != NULL) + OGRReleaseDataSource(hDS); + hDS = NULL; + } + else if (poMainDS != NULL && bMustFree) + { + poMainDS->papoOverviews[nLevel-1] = NULL; + delete poMainDS; + poMainDS = NULL; + bRet = TRUE; + } + + return bRet; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +#define MAX_GM 20037508.34 + +CPLErr MBTilesDataset::GetGeoTransform(double* padfGeoTransform) +{ + int nMaxLevel = nMinLevel + nResolutions; + if (nMaxLevel == 0) + { + padfGeoTransform[0] = -MAX_GM; + padfGeoTransform[1] = 2 * MAX_GM / nRasterXSize; + padfGeoTransform[2] = 0; + padfGeoTransform[3] = MAX_GM; + padfGeoTransform[4] = 0; + padfGeoTransform[5] = -2 * MAX_GM / nRasterYSize; + } + else + { + int nMaxTileCol = nMinTileCol + nRasterXSize / 256; + int nMaxTileRow = nMinTileRow + nRasterYSize / 256; + int nMiddleTile = (1 << nMaxLevel) / 2; + padfGeoTransform[0] = 2 * MAX_GM * (nMinTileCol - nMiddleTile) / (1 << nMaxLevel); + padfGeoTransform[1] = 2 * MAX_GM * (nMaxTileCol - nMinTileCol) / (1 << nMaxLevel) / nRasterXSize; + padfGeoTransform[2] = 0; + padfGeoTransform[3] = 2 * MAX_GM * (nMaxTileRow - nMiddleTile) / (1 << nMaxLevel); + padfGeoTransform[4] = 0; + padfGeoTransform[5] = -2 * MAX_GM * (nMaxTileRow - nMinTileRow) / (1 << nMaxLevel) / nRasterYSize; + } + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* MBTilesDataset::GetProjectionRef() +{ + return "PROJCS[\"WGS 84 / Pseudo-Mercator\",GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]],PROJECTION[\"Mercator_1SP\"],PARAMETER[\"central_meridian\",0],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",0],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AXIS[\"X\",EAST],AXIS[\"Y\",NORTH],EXTENSION[\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs\"],AUTHORITY[\"EPSG\",\"3857\"]]"; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **MBTilesDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), + TRUE, + "", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char** MBTilesDataset::GetMetadata( const char * pszDomain ) +{ + if (pszDomain != NULL && !EQUAL(pszDomain, "")) + return GDALPamDataset::GetMetadata(pszDomain); + + if (bFetchedMetadata) + return aosList.List(); + + bFetchedMetadata = TRUE; + + OGRLayerH hSQLLyr = OGR_DS_ExecuteSQL(hDS, + "SELECT name, value FROM metadata", NULL, NULL); + if (hSQLLyr == NULL) + return NULL; + + if (OGR_FD_GetFieldCount(OGR_L_GetLayerDefn(hSQLLyr)) != 2) + { + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + return NULL; + } + + OGRFeatureH hFeat; + while( (hFeat = OGR_L_GetNextFeature(hSQLLyr)) != NULL ) + { + if (OGR_F_IsFieldSet(hFeat, 0) && OGR_F_IsFieldSet(hFeat, 1)) + { + const char* pszName = OGR_F_GetFieldAsString(hFeat, 0); + const char* pszValue = OGR_F_GetFieldAsString(hFeat, 1); + if (pszValue[0] != '\0' && + strncmp(pszValue, "function(",9) != 0 && + strstr(pszValue, "") == NULL && + strstr(pszValue, "

    ") == NULL && + strstr(pszValue, "pszFilename), "MBTILES") && + poOpenInfo->nHeaderBytes >= 1024 && + EQUALN((const char*)poOpenInfo->pabyHeader, "SQLite Format 3", 15)) + { + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* MBTilesGetMinMaxZoomLevel() */ +/************************************************************************/ + +static +int MBTilesGetMinMaxZoomLevel(OGRDataSourceH hDS, int bHasMap, + int &nMinLevel, int &nMaxLevel) +{ + const char* pszSQL; + OGRLayerH hSQLLyr; + OGRFeatureH hFeat; + int bHasMinMaxLevel = FALSE; + + pszSQL = "SELECT value FROM metadata WHERE name = 'minzoom' UNION ALL " + "SELECT value FROM metadata WHERE name = 'maxzoom'"; + CPLDebug("MBTILES", "%s", pszSQL); + hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, NULL, NULL); + if (hSQLLyr) + { + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat) + { + int bHasMinLevel = FALSE; + if (OGR_F_IsFieldSet(hFeat, 0)) + { + nMinLevel = OGR_F_GetFieldAsInteger(hFeat, 0); + bHasMinLevel = TRUE; + } + OGR_F_Destroy(hFeat); + + if (bHasMinLevel) + { + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat) + { + if (OGR_F_IsFieldSet(hFeat, 0)) + { + nMaxLevel = OGR_F_GetFieldAsInteger(hFeat, 0); + bHasMinMaxLevel = TRUE; + } + OGR_F_Destroy(hFeat); + } + } + } + + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + } + + if( !bHasMinMaxLevel ) + { +#define OPTIMIZED_FOR_VSICURL +#ifdef OPTIMIZED_FOR_VSICURL + int iLevel; + for(iLevel = 0; nMinLevel < 0 && iLevel < 16; iLevel ++) + { + pszSQL = CPLSPrintf( + "SELECT zoom_level FROM %s WHERE zoom_level = %d LIMIT 1", + (bHasMap) ? "map" : "tiles", iLevel); + CPLDebug("MBTILES", "%s", pszSQL); + hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, NULL, NULL); + if (hSQLLyr) + { + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat) + { + nMinLevel = iLevel; + OGR_F_Destroy(hFeat); + } + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + } + } + + if (nMinLevel < 0) + return FALSE; + + for(iLevel = 32; nMaxLevel < 0 && iLevel >= nMinLevel; iLevel --) + { + pszSQL = CPLSPrintf( + "SELECT zoom_level FROM %s WHERE zoom_level = %d LIMIT 1", + (bHasMap) ? "map" : "tiles", iLevel); + CPLDebug("MBTILES", "%s", pszSQL); + hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, NULL, NULL); + if (hSQLLyr) + { + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat) + { + nMaxLevel = iLevel; + bHasMinMaxLevel = TRUE; + OGR_F_Destroy(hFeat); + } + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + } + } +#else + pszSQL = "SELECT min(zoom_level), max(zoom_level) FROM tiles"; + CPLDebug("MBTILES", "%s", pszSQL); + hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, NULL, NULL); + if (hSQLLyr == NULL) + { + return FALSE; + } + + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat == NULL) + { + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + return FALSE; + } + + if (OGR_F_IsFieldSet(hFeat, 0) && OGR_F_IsFieldSet(hFeat, 1)) + { + nMinLevel = OGR_F_GetFieldAsInteger(hFeat, 0); + nMaxLevel = OGR_F_GetFieldAsInteger(hFeat, 1); + bHasMinMaxLevel = TRUE; + } + + OGR_F_Destroy(hFeat); + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); +#endif + } + + return bHasMinMaxLevel; +} + +/************************************************************************/ +/* MBTilesGetBounds() */ +/************************************************************************/ + +static +int MBTilesGetBounds(OGRDataSourceH hDS, + CPL_UNUSED int nMinLevel, + int nMaxLevel, + int& nMinTileRow, int& nMaxTileRow, + int& nMinTileCol, int &nMaxTileCol) +{ + const char* pszSQL; + int bHasBounds = FALSE; + OGRLayerH hSQLLyr; + OGRFeatureH hFeat; + + pszSQL = "SELECT value FROM metadata WHERE name = 'bounds'"; + CPLDebug("MBTILES", "%s", pszSQL); + hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, NULL, NULL); + if (hSQLLyr) + { + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat != NULL) + { + const char* pszBounds = OGR_F_GetFieldAsString(hFeat, 0); + char** papszTokens = CSLTokenizeString2(pszBounds, ",", 0); + if (CSLCount(papszTokens) != 4 || + fabs(CPLAtof(papszTokens[0])) > 180 || + fabs(CPLAtof(papszTokens[1])) > 86 || + fabs(CPLAtof(papszTokens[2])) > 180 || + fabs(CPLAtof(papszTokens[3])) > 86 || + CPLAtof(papszTokens[0]) > CPLAtof(papszTokens[2]) || + CPLAtof(papszTokens[1]) > CPLAtof(papszTokens[3])) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for 'bounds' metadata"); + CSLDestroy(papszTokens); + OGR_F_Destroy(hFeat); + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + return FALSE; + } + + #define FORTPI 0.78539816339744833 + /* Latitude to Google-mercator northing */ + #define LAT_TO_NORTHING(lat) \ + 6378137 * log(tan(FORTPI + .5 * (lat) / 180 * (4 * FORTPI))) + + nMinTileCol = (int)(((CPLAtof(papszTokens[0]) + 180) / 360) * (1 << nMaxLevel)); + nMaxTileCol = (int)(((CPLAtof(papszTokens[2]) + 180) / 360) * (1 << nMaxLevel)); + nMinTileRow = (int)(0.5 + ((LAT_TO_NORTHING(CPLAtof(papszTokens[1])) + MAX_GM) / (2* MAX_GM)) * (1 << nMaxLevel)); + nMaxTileRow = (int)(0.5 + ((LAT_TO_NORTHING(CPLAtof(papszTokens[3])) + MAX_GM) / (2* MAX_GM)) * (1 << nMaxLevel)); + + bHasBounds = TRUE; + + CSLDestroy(papszTokens); + + OGR_F_Destroy(hFeat); + } + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + } + + if (!bHasBounds) + { + pszSQL = CPLSPrintf("SELECT min(tile_column), max(tile_column), " + "min(tile_row), max(tile_row) FROM tiles " + "WHERE zoom_level = %d", nMaxLevel); + CPLDebug("MBTILES", "%s", pszSQL); + hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, NULL, NULL); + if (hSQLLyr == NULL) + { + return FALSE; + } + + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat == NULL) + { + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + return FALSE; + } + + if (OGR_F_IsFieldSet(hFeat, 0) && + OGR_F_IsFieldSet(hFeat, 1) && + OGR_F_IsFieldSet(hFeat, 2) && + OGR_F_IsFieldSet(hFeat, 3)) + { + nMinTileCol = OGR_F_GetFieldAsInteger(hFeat, 0); + nMaxTileCol = OGR_F_GetFieldAsInteger(hFeat, 1) + 1; + nMinTileRow = OGR_F_GetFieldAsInteger(hFeat, 2); + nMaxTileRow = OGR_F_GetFieldAsInteger(hFeat, 3) + 1; + bHasBounds = TRUE; + } + + OGR_F_Destroy(hFeat); + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + } + + return bHasBounds; +} + +/************************************************************************/ +/* MBTilesCurlReadCbk() */ +/************************************************************************/ + +/* We spy the data received by CURL for the initial request where we try */ +/* to get a first tile to see its characteristics. We just need the header */ +/* to determine that, so let's make VSICurl stop reading after we have found it */ + +static int MBTilesCurlReadCbk(CPL_UNUSED VSILFILE* fp, + void *pabyBuffer, size_t nBufferSize, + void* pfnUserData) +{ + const GByte abyPNGSig[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, /* PNG signature */ + 0x00, 0x00, 0x00, 0x0D, /* IHDR length */ + 0x49, 0x48, 0x44, 0x52 /* IHDR chunk */ }; + + /* JPEG SOF0 (Start Of Frame 0) marker */ + const GByte abyJPEG1CompSig[] = { 0xFF, 0xC0, /* marker */ + 0x00, 0x0B, /* data length = 8 + 1 * 3 */ + 0x08, /* depth : 8 bit */ + 0x01, 0x00, /* width : 256 */ + 0x01, 0x00, /* height : 256 */ + 0x01 /* components : 1 */ + }; + const GByte abyJPEG3CompSig[] = { 0xFF, 0xC0, /* marker */ + 0x00, 0x11, /* data length = 8 + 3 * 3 */ + 0x08, /* depth : 8 bit */ + 0x01, 0x00, /* width : 256 */ + 0x01, 0x00, /* width : 256 */ + 0x03 /* components : 3 */ + }; + + int i; + for(i = 0; i < (int)nBufferSize - (int)sizeof(abyPNGSig); i++) + { + if (memcmp(((GByte*)pabyBuffer) + i, abyPNGSig, sizeof(abyPNGSig)) == 0 && + i + sizeof(abyPNGSig) + 4 + 4 + 1 + 1 < nBufferSize) + { + GByte* ptr = ((GByte*)(pabyBuffer)) + i + (int)sizeof(abyPNGSig); + + int nWidth; + memcpy(&nWidth, ptr, 4); + CPL_MSBPTR32(&nWidth); + ptr += 4; + + int nHeight; + memcpy(&nHeight, ptr, 4); + CPL_MSBPTR32(&nHeight); + ptr += 4; + + GByte nDepth = *ptr; + ptr += 1; + + GByte nColorType = *ptr; + CPLDebug("MBTILES", "PNG: nWidth=%d nHeight=%d depth=%d nColorType=%d", + nWidth, nHeight, nDepth, nColorType); + + int* pnBands = (int*) pfnUserData; + *pnBands = -2; + if (nWidth == 256 && nHeight == 256 && nDepth == 8) + { + if (nColorType == 0) + *pnBands = 1; /* Gray */ + else if (nColorType == 2) + *pnBands = 3; /* RGB */ + else if (nColorType == 3) + { + /* This might also be a color table with transparency */ + /* but we cannot tell ! */ + *pnBands = -1; + return TRUE; + } + else if (nColorType == 4) + *pnBands = 2; /* Gray + alpha */ + else if (nColorType == 6) + *pnBands = 4; /* RGB + alpha */ + } + + return FALSE; + } + } + + for(i = 0; i < (int)nBufferSize - (int)sizeof(abyJPEG1CompSig); i++) + { + if (memcmp(((GByte*)pabyBuffer) + i, abyJPEG1CompSig, sizeof(abyJPEG1CompSig)) == 0) + { + CPLDebug("MBTILES", "JPEG: nWidth=%d nHeight=%d depth=%d nBands=%d", + 256, 256, 8, 1); + + int* pnBands = (int*) pfnUserData; + *pnBands = 1; + + return FALSE; + } + else if (memcmp(((GByte*)pabyBuffer) + i, abyJPEG3CompSig, sizeof(abyJPEG3CompSig)) == 0) + { + CPLDebug("MBTILES", "JPEG: nWidth=%d nHeight=%d depth=%d nBands=%d", + 256, 256, 8, 3); + + int* pnBands = (int*) pfnUserData; + *pnBands = 3; + + return FALSE; + } + } + + return TRUE; +} + +/************************************************************************/ +/* MBTilesGetBandCount() */ +/************************************************************************/ + +static +int MBTilesGetBandCount(OGRDataSourceH &hDS, + CPL_UNUSED int nMinLevel, + int nMaxLevel, + int nMinTileRow, int nMaxTileRow, + int nMinTileCol, int nMaxTileCol) +{ + OGRLayerH hSQLLyr; + OGRFeatureH hFeat; + const char* pszSQL; + VSILFILE* fpCURLOGR = NULL; + int bFirstSelect = TRUE; + + int nBands = -1; + + /* Small trick to get the VSILFILE associated with the OGR SQLite */ + /* DB */ + CPLString osDSName(OGR_DS_GetName(hDS)); + if (strncmp(osDSName.c_str(), "/vsicurl/", 9) == 0) + { + CPLErrorReset(); + CPLPushErrorHandler(CPLQuietErrorHandler); + hSQLLyr = OGR_DS_ExecuteSQL(hDS, "GetVSILFILE()", NULL, NULL); + CPLPopErrorHandler(); + CPLErrorReset(); + if (hSQLLyr != NULL) + { + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat) + { + if (OGR_F_IsFieldSet(hFeat, 0)) + { + const char* pszPointer = OGR_F_GetFieldAsString(hFeat, 0); + fpCURLOGR = (VSILFILE* )CPLScanPointer( pszPointer, strlen(pszPointer) ); + } + OGR_F_Destroy(hFeat); + } + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + } + } + + pszSQL = CPLSPrintf("SELECT tile_data FROM tiles WHERE " + "tile_column = %d AND tile_row = %d AND zoom_level = %d", + (nMinTileCol + nMaxTileCol) / 2, + (nMinTileRow + nMaxTileRow) / 2, + nMaxLevel); + CPLDebug("MBTILES", "%s", pszSQL); + + if (fpCURLOGR) + { + /* Install a spy on the file connexion that will intercept */ + /* PNG or JPEG headers, to interrupt their downloading */ + /* once the header is found. Speeds up dataset opening. */ + CPLErrorReset(); + VSICurlInstallReadCbk(fpCURLOGR, MBTilesCurlReadCbk, &nBands, TRUE); + + CPLErrorReset(); + CPLPushErrorHandler(CPLQuietErrorHandler); + hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, NULL, NULL); + CPLPopErrorHandler(); + + VSICurlUninstallReadCbk(fpCURLOGR); + + /* Did the spy intercept something interesting ? */ + if (nBands != -1) + { + CPLErrorReset(); + + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + hSQLLyr = NULL; + + /* Re-open OGR SQLite DB, because with our spy we have simulated an I/O error */ + /* that SQLite will have difficulies to recover within the existing connection */ + /* No worry ! This will be fast because the /vsicurl/ cache has cached the already */ + /* read blocks */ + OGRReleaseDataSource(hDS); + hDS = MBTILESOpenSQLiteDB(osDSName.c_str(), GA_ReadOnly); + if (hDS == NULL) + return -1; + + /* Unrecognized form of PNG. Error out */ + if (nBands <= 0) + return -1; + + return nBands; + } + else if (CPLGetLastErrorType() == CE_Failure) + { + CPLError(CE_Failure, CPLGetLastErrorNo(), "%s", CPLGetLastErrorMsg()); + } + } + else + { + hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, NULL, NULL); + } + + while( TRUE ) + { + if (hSQLLyr == NULL && bFirstSelect) + { + bFirstSelect = FALSE; + pszSQL = CPLSPrintf("SELECT tile_data FROM tiles WHERE " + "zoom_level = %d LIMIT 1", nMaxLevel); + CPLDebug("MBTILES", "%s", pszSQL); + hSQLLyr = OGR_DS_ExecuteSQL(hDS, pszSQL, NULL, NULL); + if (hSQLLyr == NULL) + return -1; + } + + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat == NULL) + { + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + hSQLLyr = NULL; + if( !bFirstSelect ) + return -1; + } + else + break; + } + + CPLString osMemFileName; + osMemFileName.Printf("/vsimem/%p", hSQLLyr); + + int nDataSize = 0; + GByte* pabyData = OGR_F_GetFieldAsBinary(hFeat, 0, &nDataSize); + + VSIFCloseL(VSIFileFromMemBuffer( osMemFileName.c_str(), pabyData, + nDataSize, FALSE)); + + GDALDatasetH hDSTile = GDALOpenEx(osMemFileName.c_str(), GDAL_OF_RASTER, + apszAllowedDrivers, NULL, NULL); + if (hDSTile == NULL) + { + VSIUnlink(osMemFileName.c_str()); + OGR_F_Destroy(hFeat); + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + return -1; + } + + nBands = GDALGetRasterCount(hDSTile); + + if ((nBands != 1 && nBands != 3 && nBands != 4) || + GDALGetRasterXSize(hDSTile) != 256 || + GDALGetRasterYSize(hDSTile) != 256 || + GDALGetRasterDataType(GDALGetRasterBand(hDSTile, 1)) != GDT_Byte) + { + CPLError(CE_Failure, CPLE_NotSupported, "Unsupported tile characteristics"); + GDALClose(hDSTile); + VSIUnlink(osMemFileName.c_str()); + OGR_F_Destroy(hFeat); + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + return -1; + } + + GDALColorTableH hCT = GDALGetRasterColorTable(GDALGetRasterBand(hDSTile, 1)); + if (nBands == 1 && hCT != NULL) + { + nBands = 3; + if( GDALGetColorEntryCount(hCT) > 0 ) + { + /* Typical of paletted PNG with transparency */ + const GDALColorEntry* psEntry = GDALGetColorEntry( hCT, 0 ); + if( psEntry->c4 == 0 ) + nBands = 4; + } + } + + GDALClose(hDSTile); + VSIUnlink(osMemFileName.c_str()); + OGR_F_Destroy(hFeat); + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + + return nBands; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset* MBTilesDataset::Open(GDALOpenInfo* poOpenInfo) +{ + CPLString osFileName; + CPLString osTableName; + + if (!Identify(poOpenInfo)) + return NULL; + + if (OGRGetDriverCount() == 0) + OGRRegisterAll(); + +/* -------------------------------------------------------------------- */ +/* Open underlying OGR DB */ +/* -------------------------------------------------------------------- */ + + OGRDataSourceH hDS = MBTILESOpenSQLiteDB(poOpenInfo->pszFilename, GA_ReadOnly); + + MBTilesDataset* poDS = NULL; + + if (hDS == NULL) + goto end; + +/* -------------------------------------------------------------------- */ +/* Build dataset */ +/* -------------------------------------------------------------------- */ + { + CPLString osMetadataTableName, osRasterTableName; + CPLString osSQL; + OGRLayerH hMetadataLyr, hRasterLyr; + OGRFeatureH hFeat; + int nResolutions; + int iBand, nBands, nBlockXSize, nBlockYSize; + GDALDataType eDataType; + OGRLayerH hSQLLyr = NULL; + int nMinLevel = -1, nMaxLevel = -1; + int nMinTileRow = 0, nMaxTileRow = 0, nMinTileCol = 0, nMaxTileCol = 0; + int bHasBounds = FALSE; + int bHasMinMaxLevel = FALSE; + int bHasMap; + const char* pszBandCount; + + osMetadataTableName = "metadata"; + + hMetadataLyr = OGR_DS_GetLayerByName(hDS, osMetadataTableName.c_str()); + if (hMetadataLyr == NULL) + goto end; + + osRasterTableName += "tiles"; + + hRasterLyr = OGR_DS_GetLayerByName(hDS, osRasterTableName.c_str()); + if (hRasterLyr == NULL) + goto end; + + bHasMap = OGR_DS_GetLayerByName(hDS, "map") != NULL; + if (bHasMap) + { + bHasMap = FALSE; + + hSQLLyr = OGR_DS_ExecuteSQL(hDS, "SELECT type FROM sqlite_master WHERE name = 'tiles'", NULL, NULL); + if (hSQLLyr != NULL) + { + hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat) + { + if (OGR_F_IsFieldSet(hFeat, 0)) + { + bHasMap = strcmp(OGR_F_GetFieldAsString(hFeat, 0), + "view") == 0; + if (!bHasMap) + { + CPLDebug("MBTILES", "Weird! 'tiles' is not a view, but 'map' exists"); + } + } + OGR_F_Destroy(hFeat); + } + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + } + } + +/* -------------------------------------------------------------------- */ +/* Get minimum and maximum zoom levels */ +/* -------------------------------------------------------------------- */ + + bHasMinMaxLevel = MBTilesGetMinMaxZoomLevel(hDS, bHasMap, + nMinLevel, nMaxLevel); + + if (bHasMinMaxLevel && (nMinLevel < 0 || nMinLevel > nMaxLevel)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Inconsistent values : min(zoom_level) = %d, max(zoom_level) = %d", + nMinLevel, nMaxLevel); + goto end; + } + + if (bHasMinMaxLevel && nMaxLevel > 22) + { + CPLError(CE_Failure, CPLE_NotSupported, + "zoom_level > 22 not supported"); + goto end; + } + + if (!bHasMinMaxLevel) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find min and max zoom_level"); + goto end; + } + +/* -------------------------------------------------------------------- */ +/* Get bounds */ +/* -------------------------------------------------------------------- */ + + bHasBounds = MBTilesGetBounds(hDS, nMinLevel, nMaxLevel, + nMinTileRow, nMaxTileRow, + nMinTileCol, nMaxTileCol); + if (!bHasBounds) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find min and max tile numbers"); + goto end; + } + +/* -------------------------------------------------------------------- */ +/* Get number of bands */ +/* -------------------------------------------------------------------- */ + + pszBandCount = CPLGetConfigOption("MBTILES_BAND_COUNT", "-1"); + nBands = atoi(pszBandCount); + + if( ! (nBands == 1 || nBands == 3 || nBands == 4) ) + { + nBands = MBTilesGetBandCount(hDS, nMinLevel, nMaxLevel, + nMinTileRow, nMaxTileRow, + nMinTileCol, nMaxTileCol); + if (nBands < 0) + goto end; + } + +/* -------------------------------------------------------------------- */ +/* Set dataset attributes */ +/* -------------------------------------------------------------------- */ + + poDS = new MBTilesDataset(); + poDS->eAccess = poOpenInfo->eAccess; + poDS->hDS = hDS; + + /* poDS will release it from now */ + hDS = NULL; + +/* -------------------------------------------------------------------- */ +/* Store resolutions */ +/* -------------------------------------------------------------------- */ + poDS->nMinLevel = nMinLevel; + poDS->nResolutions = nResolutions = nMaxLevel - nMinLevel; + +/* -------------------------------------------------------------------- */ +/* Round bounds to the lowest zoom level */ +/* -------------------------------------------------------------------- */ + + //CPLDebug("MBTILES", "%d %d %d %d", nMinTileCol, nMinTileRow, nMaxTileCol, nMaxTileRow); + nMinTileCol = (int)(1.0 * nMinTileCol / (1 << nResolutions)) * (1 << nResolutions); + nMinTileRow = (int)(1.0 * nMinTileRow / (1 << nResolutions)) * (1 << nResolutions); + nMaxTileCol = (int)ceil(1.0 * nMaxTileCol / (1 << nResolutions)) * (1 << nResolutions); + nMaxTileRow = (int)ceil(1.0 * nMaxTileRow / (1 << nResolutions)) * (1 << nResolutions); + +/* -------------------------------------------------------------------- */ +/* Compute raster size, geotransform and projection */ +/* -------------------------------------------------------------------- */ + poDS->nMinTileCol = nMinTileCol; + poDS->nMinTileRow = nMinTileRow; + poDS->nRasterXSize = (nMaxTileCol-nMinTileCol) * 256; + poDS->nRasterYSize = (nMaxTileRow-nMinTileRow) * 256; + + nBlockXSize = nBlockYSize = 256; + eDataType = GDT_Byte; + +/* -------------------------------------------------------------------- */ +/* Add bands */ +/* -------------------------------------------------------------------- */ + + for(iBand=0;iBandSetBand(iBand+1, new MBTilesBand(poDS, iBand+1, eDataType, + nBlockXSize, nBlockYSize)); + +/* -------------------------------------------------------------------- */ +/* Add overview levels as internal datasets */ +/* -------------------------------------------------------------------- */ + if (nResolutions >= 1) + { + poDS->papoOverviews = (MBTilesDataset**) + CPLCalloc(nResolutions, sizeof(MBTilesDataset*)); + int nLev; + for(nLev=1;nLev<=nResolutions;nLev++) + { + poDS->papoOverviews[nLev-1] = new MBTilesDataset(poDS, nLev); + + for(iBand=0;iBandpapoOverviews[nLev-1]->SetBand(iBand+1, + new MBTilesBand(poDS->papoOverviews[nLev-1], iBand+1, eDataType, + nBlockXSize, nBlockYSize)); + } + } + } + + poDS->SetMetadata(poDS->papszImageStructure, "IMAGE_STRUCTURE"); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + + if ( !EQUALN(poOpenInfo->pszFilename, "/vsicurl/", 9) ) + poDS->TryLoadXML(); + else + { + poDS->SetPamFlags(poDS->GetPamFlags() & ~GPF_DIRTY); + } + } + +end: + if (hDS) + OGRReleaseDataSource(hDS); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_MBTiles() */ +/************************************************************************/ + +void GDALRegister_MBTiles() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("MBTiles driver")) + return; + + if( GDALGetDriverByName( "MBTiles" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "MBTiles" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "MBTiles" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_mbtiles.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "mbtiles" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = MBTilesDataset::Open; + poDriver->pfnIdentify = MBTilesDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/mem/GNUmakefile b/bazaar/plugin/gdal/frmts/mem/GNUmakefile new file mode 100644 index 000000000..6a311918c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mem/GNUmakefile @@ -0,0 +1,17 @@ + + +include ../../GDALmake.opt + +OBJ = memdataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +install: + $(INSTALL_DATA) memdataset.h $(DESTDIR)$(INST_INCLUDE) diff --git a/bazaar/plugin/gdal/frmts/mem/frmt_mem.html b/bazaar/plugin/gdal/frmts/mem/frmt_mem.html new file mode 100644 index 000000000..65f10d09e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mem/frmt_mem.html @@ -0,0 +1,56 @@ + + +MEM -- In Memory Raster + + + + +

    MEM -- In Memory Raster

    + +GDAL supports the ability to hold rasters in a temporary in-memory format. +This is primarily useful for temporary datasets in scripts or internal +to applications. It is not generally of any use to application end-users.

    + +Memory datasets should support for most kinds of auxiliary information +including metadata, coordinate systems, georeferencing, GCPs, +color interpretation, nodata, color tables and all pixel data types.

    + +

    Dataset Name Format

    + +It is possible to open an existing array in memory. To do so, construct +a dataset name with the following format: + +
    +  MEM:::option=value[,option=value...]
    +
    + +For example: + +
    +  MEM:::DATAPOINTER=342343408,PIXELS=100,LINES=100,BANDS=3,DATATYPE=Byte,
    +       PIXELOFFSET=3,LINEOFFSET=300,BANDOFFSET=1
    +
    + +
      +
    • DATAPOINTER: pointer to the first pixel of the first band represented as +a long integer. NOTE! This may not work on platforms where a long is 32 bits and a pointer is 64 bits. (required) +
    • PIXELS: Width of raster in pixels. (required) +
    • LINES: Height of raster in lines. (required) +
    • BANDS: Number of bands, defaults to 1. (optional) +
    • DATATYPE: Name of the data type, as returned by GDALGetDataTypeName() (eg. Byte, Int16) Defaults to Byte. (optional) +
    • PIXELOFFSET: Offset in bytes between the start of one pixel and the next on the same scanline. (optional) +
    • LINEOFFSET: Offset in bytes between the start of one scanline and the next. (optional) +
    • BANDOFFSET: Offset in bytes between the start of one bands data and the next. +
    + +

    Creation Options

    + +There are no supported creation options.

    + +The MEM format is one of the few that supports the AddBand() method. +The AddBand() method supports DATAPOINTER, PIXELOFFSET and LINEOFFSET +options to reference an existing memory array.

    + + + + diff --git a/bazaar/plugin/gdal/frmts/mem/makefile.vc b/bazaar/plugin/gdal/frmts/mem/makefile.vc new file mode 100644 index 000000000..b73044019 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mem/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = memdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/mem/memdataset.cpp b/bazaar/plugin/gdal/frmts/mem/memdataset.cpp new file mode 100644 index 000000000..06bd387ce --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mem/memdataset.cpp @@ -0,0 +1,1210 @@ +/****************************************************************************** + * $Id: memdataset.cpp 28899 2015-04-14 09:27:00Z rouault $ + * + * Project: Memory Array Translator + * Purpose: Complete implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "memdataset.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: memdataset.cpp 28899 2015-04-14 09:27:00Z rouault $"); + +/************************************************************************/ +/* MEMCreateRasterBand() */ +/************************************************************************/ + +GDALRasterBandH MEMCreateRasterBand( GDALDataset *poDS, int nBand, + GByte *pabyData, GDALDataType eType, + int nPixelOffset, int nLineOffset, + int bAssumeOwnership ) + +{ + return (GDALRasterBandH) + new MEMRasterBand( poDS, nBand, pabyData, eType, nPixelOffset, + nLineOffset, bAssumeOwnership ); +} + +/************************************************************************/ +/* MEMRasterBand() */ +/************************************************************************/ + +MEMRasterBand::MEMRasterBand( GDALDataset *poDS, int nBand, + GByte *pabyDataIn, GDALDataType eTypeIn, + GSpacing nPixelOffsetIn, GSpacing nLineOffsetIn, + int bAssumeOwnership, const char * pszPixelType) + +{ + //CPLDebug( "MEM", "MEMRasterBand(%p)", this ); + + this->poDS = poDS; + this->nBand = nBand; + + this->eAccess = poDS->GetAccess(); + + eDataType = eTypeIn; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + if( nPixelOffsetIn == 0 ) + nPixelOffsetIn = GDALGetDataTypeSize(eTypeIn) / 8; + + if( nLineOffsetIn == 0 ) + nLineOffsetIn = nPixelOffsetIn * (size_t)nBlockXSize; + + nPixelOffset = nPixelOffsetIn; + nLineOffset = nLineOffsetIn; + bOwnData = bAssumeOwnership; + + pabyData = pabyDataIn; + + bNoDataSet = FALSE; + + poColorTable = NULL; + + eColorInterp = GCI_Undefined; + + papszCategoryNames = NULL; + dfOffset = 0.0; + dfScale = 1.0; + pszUnitType = NULL; + psSavedHistograms = NULL; + + if( pszPixelType && EQUAL(pszPixelType,"SIGNEDBYTE") ) + this->SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" ); +} + +/************************************************************************/ +/* ~MEMRasterBand() */ +/************************************************************************/ + +MEMRasterBand::~MEMRasterBand() + +{ + //CPLDebug( "MEM", "~MEMRasterBand(%p)", this ); + if( bOwnData ) + { + //CPLDebug( "MEM", "~MEMRasterBand() - free raw data." ); + VSIFree( pabyData ); + } + + if( poColorTable != NULL ) + delete poColorTable; + + CPLFree( pszUnitType ); + CSLDestroy( papszCategoryNames ); + + if (psSavedHistograms != NULL) + CPLDestroyXMLNode(psSavedHistograms); +} + + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr MEMRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + int nWordSize = GDALGetDataTypeSize( eDataType ) / 8; + CPLAssert( nBlockXOff == 0 ); + + if( nPixelOffset == nWordSize ) + { + memcpy( pImage, + pabyData + nLineOffset*(size_t)nBlockYOff, + nPixelOffset * nBlockXSize ); + } + else + { + GByte *pabyCur = pabyData + nLineOffset * (size_t)nBlockYOff; + + for( int iPixel = 0; iPixel < nBlockXSize; iPixel++ ) + { + memcpy( ((GByte *) pImage) + iPixel*nWordSize, + pabyCur + iPixel*nPixelOffset, + nWordSize ); + } + } + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr MEMRasterBand::IWriteBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + int nWordSize = GDALGetDataTypeSize( eDataType ) / 8; + CPLAssert( nBlockXOff == 0 ); + + if( nPixelOffset == nWordSize ) + { + memcpy( pabyData+nLineOffset*(size_t)nBlockYOff, + pImage, + nPixelOffset * nBlockXSize ); + } + else + { + GByte *pabyCur = pabyData + nLineOffset*(size_t)nBlockYOff; + + for( int iPixel = 0; iPixel < nBlockXSize; iPixel++ ) + { + memcpy( pabyCur + iPixel*nPixelOffset, + ((GByte *) pImage) + iPixel*nWordSize, + nWordSize ); + } + } + + return CE_None; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr MEMRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpaceBuf, + GSpacing nLineSpaceBuf, + GDALRasterIOExtraArg* psExtraArg ) +{ + if( nXSize != nBufXSize || nYSize != nBufYSize ) + { + return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpaceBuf, nLineSpaceBuf, + psExtraArg); + } + + // In case block based I/O has been done before + FlushCache(); + + if( eRWFlag == GF_Read ) + { + for(int iLine=0;iLine 1 && + nBandSpaceBuf == eBufTypeSize && + nPixelSpaceBuf == nBandSpaceBuf * nBands ) + { + GDALDataType eDT = GDT_Unknown; + GByte* pabyData = NULL; + GSpacing nPixelOffset = 0; + GSpacing nLineOffset = 0; + int eDTSize = 0; + int iBandIndex; + for( iBandIndex = 0; iBandIndex < nBandCount; iBandIndex++ ) + { + if( panBandMap[iBandIndex] != iBandIndex + 1 ) + break; + MEMRasterBand *poBand = (MEMRasterBand*) GetRasterBand(iBandIndex + 1); + if( iBandIndex == 0 ) + { + eDT = poBand->GetRasterDataType(); + pabyData = poBand->pabyData; + nPixelOffset = poBand->nPixelOffset; + nLineOffset = poBand->nLineOffset; + eDTSize = GDALGetDataTypeSize(eDT) / 8; + if( nPixelOffset != nBands * eDTSize ) + break; + } + else if( poBand->GetRasterDataType() != eDT || + nPixelOffset != poBand->nPixelOffset || + nLineOffset != poBand->nLineOffset || + poBand->pabyData != pabyData + iBandIndex * eDTSize ) + { + break; + } + } + if( iBandIndex == nBandCount ) + { + FlushCache(); + if( eRWFlag == GF_Read ) + { + for(int iLine=0;iLinepfnProgress; + void *pProgressDataGlobal = psExtraArg->pProgressData; + + CPLErr eErr = CE_None; + for( int iBandIndex = 0; + iBandIndex < nBandCount && eErr == CE_None; + iBandIndex++ ) + { + GDALRasterBand *poBand = GetRasterBand(panBandMap[iBandIndex]); + GByte *pabyBandData; + + if (poBand == NULL) + { + eErr = CE_Failure; + break; + } + + pabyBandData = ((GByte *) pData) + iBandIndex * nBandSpaceBuf; + + psExtraArg->pfnProgress = GDALScaledProgress; + psExtraArg->pProgressData = + GDALCreateScaledProgress( 1.0 * iBandIndex / nBandCount, + 1.0 * (iBandIndex + 1) / nBandCount, + pfnProgressGlobal, + pProgressDataGlobal ); + + eErr = poBand->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + (void *) pabyBandData, nBufXSize, nBufYSize, + eBufType, nPixelSpaceBuf, nLineSpaceBuf, + psExtraArg); + + GDALDestroyScaledProgress( psExtraArg->pProgressData ); + } + + psExtraArg->pfnProgress = pfnProgressGlobal; + psExtraArg->pProgressData = pProgressDataGlobal; + + return eErr; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ +double MEMRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bNoDataSet; + + if( bNoDataSet ) + return dfNoData; + else + return 0.0; +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ +CPLErr MEMRasterBand::SetNoDataValue( double dfNewValue ) +{ + dfNoData = dfNewValue; + bNoDataSet = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp MEMRasterBand::GetColorInterpretation() + +{ + if( poColorTable != NULL ) + return GCI_PaletteIndex; + else + return eColorInterp; +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +CPLErr MEMRasterBand::SetColorInterpretation( GDALColorInterp eGCI ) + +{ + eColorInterp = eGCI; + + return CE_None; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *MEMRasterBand::GetColorTable() + +{ + return poColorTable; +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr MEMRasterBand::SetColorTable( GDALColorTable *poCT ) + +{ + if( poColorTable != NULL ) + delete poColorTable; + + if( poCT == NULL ) + poColorTable = NULL; + else + poColorTable = poCT->Clone(); + + return CE_None; +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +const char *MEMRasterBand::GetUnitType() + +{ + if( pszUnitType == NULL ) + return ""; + else + return pszUnitType; +} + +/************************************************************************/ +/* SetUnitType() */ +/************************************************************************/ + +CPLErr MEMRasterBand::SetUnitType( const char *pszNewValue ) + +{ + CPLFree( pszUnitType ); + + if( pszNewValue == NULL ) + pszUnitType = NULL; + else + pszUnitType = CPLStrdup(pszNewValue); + + return CE_None; +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +double MEMRasterBand::GetOffset( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return dfOffset; +} + +/************************************************************************/ +/* SetOffset() */ +/************************************************************************/ + +CPLErr MEMRasterBand::SetOffset( double dfNewOffset ) + +{ + dfOffset = dfNewOffset; + return CE_None; +} + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ + +double MEMRasterBand::GetScale( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return dfScale; +} + +/************************************************************************/ +/* SetScale() */ +/************************************************************************/ + +CPLErr MEMRasterBand::SetScale( double dfNewScale ) + +{ + dfScale = dfNewScale; + return CE_None; +} + +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +char **MEMRasterBand::GetCategoryNames() + +{ + return papszCategoryNames; +} + +/************************************************************************/ +/* SetCategoryNames() */ +/************************************************************************/ + +CPLErr MEMRasterBand::SetCategoryNames( char ** papszNewNames ) + +{ + CSLDestroy( papszCategoryNames ); + papszCategoryNames = CSLDuplicate( papszNewNames ); + + return CE_None; +} + +/************************************************************************/ +/* SetDefaultHistogram() */ +/************************************************************************/ + +CPLErr MEMRasterBand::SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram) + +{ + CPLXMLNode *psNode; + +/* -------------------------------------------------------------------- */ +/* Do we have a matching histogram we should replace? */ +/* -------------------------------------------------------------------- */ + psNode = PamFindMatchingHistogram( psSavedHistograms, + dfMin, dfMax, nBuckets, + TRUE, TRUE ); + if( psNode != NULL ) + { + /* blow this one away */ + CPLRemoveXMLChild( psSavedHistograms, psNode ); + CPLDestroyXMLNode( psNode ); + } + +/* -------------------------------------------------------------------- */ +/* Translate into a histogram XML tree. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psHistItem; + + psHistItem = PamHistogramToXMLTree( dfMin, dfMax, nBuckets, + panHistogram, TRUE, FALSE ); + if( psHistItem == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Insert our new default histogram at the front of the */ +/* histogram list so that it will be the default histogram. */ +/* -------------------------------------------------------------------- */ + + if( psSavedHistograms == NULL ) + psSavedHistograms = CPLCreateXMLNode( NULL, CXT_Element, + "Histograms" ); + + psHistItem->psNext = psSavedHistograms->psChild; + psSavedHistograms->psChild = psHistItem; + + return CE_None; +} +/************************************************************************/ +/* GetDefaultHistogram() */ +/************************************************************************/ + +CPLErr +MEMRasterBand::GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig **ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + if( psSavedHistograms != NULL ) + { + CPLXMLNode *psXMLHist; + + for( psXMLHist = psSavedHistograms->psChild; + psXMLHist != NULL; psXMLHist = psXMLHist->psNext ) + { + int bApprox, bIncludeOutOfRange; + + if( psXMLHist->eType != CXT_Element + || !EQUAL(psXMLHist->pszValue,"HistItem") ) + continue; + + if( PamParseHistogram( psXMLHist, pdfMin, pdfMax, pnBuckets, + ppanHistogram, &bIncludeOutOfRange, + &bApprox ) ) + return CE_None; + else + return CE_Failure; + } + } + + return GDALRasterBand::GetDefaultHistogram( pdfMin, pdfMax, pnBuckets, + ppanHistogram, bForce, + pfnProgress,pProgressData); +} + +/************************************************************************/ +/* ==================================================================== */ +/* MEMDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* MEMDataset() */ +/************************************************************************/ + +MEMDataset::MEMDataset() + +{ + pszProjection = NULL; + bGeoTransformSet = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = -1.0; + + nGCPCount = 0; + pasGCPs = NULL; +} + +/************************************************************************/ +/* ~MEMDataset() */ +/************************************************************************/ + +MEMDataset::~MEMDataset() + +{ + FlushCache(); + CPLFree( pszProjection ); + + GDALDeinitGCPs( nGCPCount, pasGCPs ); + CPLFree( pasGCPs ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *MEMDataset::GetProjectionRef() + +{ + if( pszProjection == NULL ) + return ""; + else + return pszProjection; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr MEMDataset::SetProjection( const char *pszProjectionIn ) + +{ + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszProjectionIn ); + + return CE_None; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr MEMDataset::GetGeoTransform( double *padfGeoTransform ) + +{ + memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 ); + if( bGeoTransformSet ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr MEMDataset::SetGeoTransform( double *padfGeoTransform ) + +{ + memcpy( adfGeoTransform, padfGeoTransform, sizeof(double) * 6 ); + bGeoTransformSet = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GetInternalHandle() */ +/************************************************************************/ + +void *MEMDataset::GetInternalHandle( const char * pszRequest) + +{ + // check for MEMORYnnn string in pszRequest (nnnn can be up to 10 + // digits, or even omitted) + if( EQUALN(pszRequest,"MEMORY",6)) + { + if(int BandNumber = CPLScanLong(&pszRequest[6], 10)) + { + MEMRasterBand *RequestedRasterBand = + (MEMRasterBand *)GetRasterBand(BandNumber); + + // we're within a MEMDataset so the only thing a RasterBand + // could be is a MEMRasterBand + + if( RequestedRasterBand != NULL ) + { + // return the internal band data pointer + return(RequestedRasterBand->GetData()); + } + } + } + + return NULL; +} +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int MEMDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *MEMDataset::GetGCPProjection() + +{ + return osGCPProjection; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *MEMDataset::GetGCPs() + +{ + return pasGCPs; +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +CPLErr MEMDataset::SetGCPs( int nNewCount, const GDAL_GCP *pasNewGCPList, + const char *pszGCPProjection ) + +{ + GDALDeinitGCPs( nGCPCount, pasGCPs ); + CPLFree( pasGCPs ); + + if( pszGCPProjection == NULL ) + osGCPProjection = ""; + else + osGCPProjection = pszGCPProjection; + + nGCPCount = nNewCount; + pasGCPs = GDALDuplicateGCPs( nGCPCount, pasNewGCPList ); + + return CE_None; +} + +/************************************************************************/ +/* AddBand() */ +/* */ +/* Add a new band to the dataset, allowing creation options to */ +/* specify the existing memory to use, otherwise create new */ +/* memory. */ +/************************************************************************/ + +CPLErr MEMDataset::AddBand( GDALDataType eType, char **papszOptions ) + +{ + int nBandId = GetRasterCount() + 1; + GByte *pData; + int nPixelSize = (GDALGetDataTypeSize(eType) / 8); + +/* -------------------------------------------------------------------- */ +/* Do we need to allocate the memory ourselves? This is the */ +/* simple case. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue( papszOptions, "DATAPOINTER" ) == NULL ) + { + + pData = (GByte *) + VSICalloc(nPixelSize * GetRasterXSize(), GetRasterYSize() ); + + if( pData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to create band arrays ... out of memory." ); + return CE_Failure; + } + + SetBand( nBandId, + new MEMRasterBand( this, nBandId, pData, eType, nPixelSize, + nPixelSize * GetRasterXSize(), TRUE ) ); + + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Get layout of memory and other flags. */ +/* -------------------------------------------------------------------- */ + const char *pszOption; + GSpacing nPixelOffset, nLineOffset; + const char *pszDataPointer; + + pszDataPointer = CSLFetchNameValue(papszOptions,"DATAPOINTER"); + pData = (GByte *) CPLScanPointer(pszDataPointer, + strlen(pszDataPointer)); + + pszOption = CSLFetchNameValue(papszOptions,"PIXELOFFSET"); + if( pszOption == NULL ) + nPixelOffset = nPixelSize; + else + nPixelOffset = CPLScanUIntBig(pszOption, strlen(pszOption)); + + pszOption = CSLFetchNameValue(papszOptions,"LINEOFFSET"); + if( pszOption == NULL ) + nLineOffset = GetRasterXSize() * (size_t)nPixelOffset; + else + nLineOffset = CPLScanUIntBig(pszOption, strlen(pszOption)); + + SetBand( nBandId, + new MEMRasterBand( this, nBandId, pData, eType, + nPixelOffset, nLineOffset, FALSE ) ); + + return CE_None; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *MEMDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + char **papszOptions; + +/* -------------------------------------------------------------------- */ +/* Do we have the special filename signature for MEM format */ +/* description strings? */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(poOpenInfo->pszFilename,"MEM:::",6) + || poOpenInfo->fpL != NULL ) + return NULL; + + papszOptions = CSLTokenizeStringComplex(poOpenInfo->pszFilename+6, ",", + TRUE, FALSE ); + +/* -------------------------------------------------------------------- */ +/* Verify we have all required fields */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue( papszOptions, "PIXELS" ) == NULL + || CSLFetchNameValue( papszOptions, "LINES" ) == NULL + || CSLFetchNameValue( papszOptions, "DATAPOINTER" ) == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing required field (one of PIXELS, LINES or DATAPOINTER)\n" + "Unable to access in-memory array." ); + + CSLDestroy( papszOptions ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the new MEMDataset object. */ +/* -------------------------------------------------------------------- */ + MEMDataset *poDS; + + poDS = new MEMDataset(); + + poDS->nRasterXSize = atoi(CSLFetchNameValue(papszOptions,"PIXELS")); + poDS->nRasterYSize = atoi(CSLFetchNameValue(papszOptions,"LINES")); + poDS->eAccess = GA_Update; + +/* -------------------------------------------------------------------- */ +/* Extract other information. */ +/* -------------------------------------------------------------------- */ + const char *pszOption; + GDALDataType eType; + int nBands; + GSpacing nPixelOffset, nLineOffset, nBandOffset; + const char *pszDataPointer; + GByte *pabyData; + + pszOption = CSLFetchNameValue(papszOptions,"BANDS"); + if( pszOption == NULL ) + nBands = 1; + else + { + nBands = atoi(pszOption); + } + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || + !GDALCheckBandCount(nBands, TRUE)) + { + CSLDestroy( papszOptions ); + delete poDS; + return NULL; + } + + pszOption = CSLFetchNameValue(papszOptions,"DATATYPE"); + if( pszOption == NULL ) + eType = GDT_Byte; + else + { + if( atoi(pszOption) > 0 && atoi(pszOption) < GDT_TypeCount ) + eType = (GDALDataType) atoi(pszOption); + else + { + int iType; + + eType = GDT_Unknown; + for( iType = 0; iType < GDT_TypeCount; iType++ ) + { + if( EQUAL(GDALGetDataTypeName((GDALDataType) iType), + pszOption) ) + { + eType = (GDALDataType) iType; + break; + } + } + + if( eType == GDT_Unknown ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DATATYPE=%s not recognised.", + pszOption ); + CSLDestroy( papszOptions ); + delete poDS; + return NULL; + } + } + } + + pszOption = CSLFetchNameValue(papszOptions,"PIXELOFFSET"); + if( pszOption == NULL ) + nPixelOffset = GDALGetDataTypeSize(eType) / 8; + else + nPixelOffset = CPLScanUIntBig(pszOption, strlen(pszOption)); + + pszOption = CSLFetchNameValue(papszOptions,"LINEOFFSET"); + if( pszOption == NULL ) + nLineOffset = poDS->nRasterXSize * (size_t) nPixelOffset; + else + nLineOffset = CPLScanUIntBig(pszOption, strlen(pszOption)); + + pszOption = CSLFetchNameValue(papszOptions,"BANDOFFSET"); + if( pszOption == NULL ) + nBandOffset = nLineOffset * (size_t) poDS->nRasterYSize; + else + nBandOffset = CPLScanUIntBig(pszOption, strlen(pszOption)); + + pszDataPointer = CSLFetchNameValue(papszOptions,"DATAPOINTER"); + pabyData = (GByte *) CPLScanPointer( pszDataPointer, + strlen(pszDataPointer) ); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < nBands; iBand++ ) + { + poDS->SetBand( iBand+1, + new MEMRasterBand( poDS, iBand+1, + pabyData + iBand * nBandOffset, + eType, nPixelOffset, nLineOffset, + FALSE ) ); + } + +/* -------------------------------------------------------------------- */ +/* Try to return a regular handle on the file. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( papszOptions ); + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *MEMDataset::Create( CPL_UNUSED const char * pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + char **papszOptions ) +{ + +/* -------------------------------------------------------------------- */ +/* Do we want a pixel interleaved buffer? I mostly care about */ +/* this to test pixel interleaved io in other contexts, but it */ +/* could be useful to create a directly accessable buffer for */ +/* some apps. */ +/* -------------------------------------------------------------------- */ + int bPixelInterleaved = FALSE; + const char *pszOption = CSLFetchNameValue( papszOptions, "INTERLEAVE" ); + if( pszOption && EQUAL(pszOption,"PIXEL") ) + bPixelInterleaved = TRUE; + +/* -------------------------------------------------------------------- */ +/* First allocate band data, verifying that we can get enough */ +/* memory. */ +/* -------------------------------------------------------------------- */ + std::vector apbyBandData; + int iBand; + int nWordSize = GDALGetDataTypeSize(eType) / 8; + int bAllocOK = TRUE; + + GUIntBig nGlobalBigSize = (GUIntBig)nWordSize * nBands * nXSize * nYSize; + size_t nGlobalSize = (size_t)nGlobalBigSize; +#if SIZEOF_VOIDP == 4 + if( (GUIntBig)nGlobalSize != nGlobalBigSize ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Cannot allocate " CPL_FRMT_GUIB " bytes on this platform.", + nGlobalBigSize ); + return NULL; + } +#endif + + if( bPixelInterleaved ) + { + apbyBandData.push_back( + (GByte *) VSICalloc( 1, nGlobalSize ) ); + + if( apbyBandData[0] == NULL ) + bAllocOK = FALSE; + else + { + for( iBand = 1; iBand < nBands; iBand++ ) + apbyBandData.push_back( apbyBandData[0] + iBand * nWordSize ); + } + } + else + { + for( iBand = 0; iBand < nBands; iBand++ ) + { + apbyBandData.push_back( + (GByte *) VSICalloc( 1, ((size_t)nWordSize) * nXSize * nYSize ) ); + if( apbyBandData[iBand] == NULL ) + { + bAllocOK = FALSE; + break; + } + } + } + + if( !bAllocOK ) + { + for( iBand = 0; iBand < (int) apbyBandData.size(); iBand++ ) + { + if( apbyBandData[iBand] ) + VSIFree( apbyBandData[iBand] ); + } + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to create band arrays ... out of memory." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the new GTiffDataset object. */ +/* -------------------------------------------------------------------- */ + MEMDataset *poDS; + + poDS = new MEMDataset(); + + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->eAccess = GA_Update; + + const char *pszPixelType = CSLFetchNameValue( papszOptions, "PIXELTYPE" ); + if( pszPixelType && EQUAL(pszPixelType,"SIGNEDBYTE") ) + poDS->SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" ); + + if( bPixelInterleaved ) + poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( iBand = 0; iBand < nBands; iBand++ ) + { + MEMRasterBand *poNewBand; + + if( bPixelInterleaved ) + poNewBand = new MEMRasterBand( poDS, iBand+1, apbyBandData[iBand], + eType, nWordSize * nBands, 0, + iBand == 0 ); + else + poNewBand = new MEMRasterBand( poDS, iBand+1, apbyBandData[iBand], + eType, 0, 0, TRUE ); + + poDS->SetBand( iBand+1, poNewBand ); + } + +/* -------------------------------------------------------------------- */ +/* Try to return a regular handle on the file. */ +/* -------------------------------------------------------------------- */ + return poDS; +} + +/************************************************************************/ +/* MEMDatasetIdentify() */ +/************************************************************************/ + +static int MEMDatasetIdentify( GDALOpenInfo * poOpenInfo ) +{ + return (strncmp(poOpenInfo->pszFilename, "MEM:::", 6) == 0 && + poOpenInfo->fpL == NULL); +} + +/************************************************************************/ +/* MEMDatasetDelete() */ +/************************************************************************/ + +static CPLErr MEMDatasetDelete(CPL_UNUSED const char* fileName) +{ + /* Null implementation, so that people can Delete("MEM:::") */ + return CE_None; +} + +/************************************************************************/ +/* GDALRegister_MEM() */ +/************************************************************************/ + +void GDALRegister_MEM() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "MEM" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "MEM" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "In Memory Raster" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 Float32 Float64 CInt16 CInt32 CFloat32 CFloat64" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +"" ); + +/* Define GDAL_NO_OPEN_FOR_MEM_DRIVER macro to undefine Open() method for MEM driver. */ +/* Otherwise, bad user input can trigger easily a GDAL crash as random pointers can be passed as a string. */ +/* All code in GDAL tree using the MEM driver use the Create() method only, so Open() */ +/* is not needed, except for esoteric uses */ +#ifndef GDAL_NO_OPEN_FOR_MEM_DRIVER + poDriver->pfnOpen = MEMDataset::Open; + poDriver->pfnIdentify = MEMDatasetIdentify; +#endif + poDriver->pfnCreate = MEMDataset::Create; + poDriver->pfnDelete = MEMDatasetDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/mem/memdataset.h b/bazaar/plugin/gdal/frmts/mem/memdataset.h new file mode 100644 index 000000000..54b630118 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mem/memdataset.h @@ -0,0 +1,173 @@ +/****************************************************************************** + * $Id: memdataset.h 28899 2015-04-14 09:27:00Z rouault $ + * + * Project: Memory Array Translator + * Purpose: Declaration of MEMDataset, and MEMRasterBand. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef MEMDATASET_H_INCLUDED +#define MEMDATASET_H_INCLUDED + +#include "gdal_pam.h" +#include "gdal_priv.h" + +CPL_C_START +void GDALRegister_MEM(void); +/* Caution: if changing this prototype, also change in swig/include/gdal_python.i + where it is redefined */ +GDALRasterBandH CPL_DLL MEMCreateRasterBand( GDALDataset *, int, GByte *, + GDALDataType, int, int, int ); +CPL_C_END + +/************************************************************************/ +/* MEMDataset */ +/************************************************************************/ + +class MEMRasterBand; + +class CPL_DLL MEMDataset : public GDALDataset +{ + int bGeoTransformSet; + double adfGeoTransform[6]; + + char *pszProjection; + + int nGCPCount; + GDAL_GCP *pasGCPs; + CPLString osGCPProjection; + + public: + MEMDataset(); + virtual ~MEMDataset(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual void *GetInternalHandle( const char * ); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + virtual CPLErr AddBand( GDALDataType eType, + char **papszOptions=NULL ); + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpaceBuf, + GSpacing nLineSpaceBuf, + GSpacing nBandSpaceBuf, + GDALRasterIOExtraArg* psExtraArg); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); +}; + +/************************************************************************/ +/* MEMRasterBand */ +/************************************************************************/ + +class CPL_DLL MEMRasterBand : public GDALPamRasterBand +{ + protected: + friend class MEMDataset; + + GByte *pabyData; + GSpacing nPixelOffset; + GSpacing nLineOffset; + int bOwnData; + + int bNoDataSet; + double dfNoData; + + GDALColorTable *poColorTable; + GDALColorInterp eColorInterp; + + char *pszUnitType; + char **papszCategoryNames; + + double dfOffset; + double dfScale; + + CPLXMLNode *psSavedHistograms; + public: + + MEMRasterBand( GDALDataset *poDS, int nBand, + GByte *pabyData, GDALDataType eType, + GSpacing nPixelOffset, GSpacing nLineOffset, + int bAssumeOwnership, const char * pszPixelType = NULL); + virtual ~MEMRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpaceBuf, + GSpacing nLineSpaceBuf, + GDALRasterIOExtraArg* psExtraArg ); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual CPLErr SetNoDataValue( double ); + + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual CPLErr SetColorTable( GDALColorTable * ); + + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + + virtual const char *GetUnitType(); + CPLErr SetUnitType( const char * ); + + virtual char **GetCategoryNames(); + virtual CPLErr SetCategoryNames( char ** ); + + virtual double GetOffset( int *pbSuccess = NULL ); + CPLErr SetOffset( double ); + virtual double GetScale( int *pbSuccess = NULL ); + CPLErr SetScale( double ); + + virtual CPLErr SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram ); + virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig ** ppanHistogram, + int bForce, + GDALProgressFunc, void *pProgressData); + + // allow access to MEM driver's private internal memory buffer + GByte *GetData(void) const {return(pabyData);} +}; + +#endif /* ndef MEMDATASET_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/frmts/mrsid/GNUmakefile b/bazaar/plugin/gdal/frmts/mrsid/GNUmakefile new file mode 100644 index 000000000..69cfe8885 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid/GNUmakefile @@ -0,0 +1,33 @@ + +include ../../GDALmake.opt + +OBJ = mrsiddataset.o mrsidstream.o + +PLUGIN_DLL = gdal_MrSID.so + +ifeq ($(GEOTIFF_SETTING),internal) +GEOTIFF_INCLUDE = -I../../frmts/gtiff/libgeotiff +ifeq ($(RENAME_INTERNAL_LIBGEOTIFF_SYMBOLS),yes) +CPPFLAGS := -DRENAME_INTERNAL_LIBGEOTIFF_SYMBOLS $(CPPFLAGS) +endif +endif + +CPPFLAGS := $(MRSID_FLAGS) $(MRSID_INCLUDE) $(GEOTIFF_INCLUDE) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + $(LD_SHARED) $(OBJ) $(LDFLAGS) $(MRSID_LIBS) -o $(PLUGIN_DLL) + +#$(PLUGIN_DLL): $(OBJ:.o=.lo) +# $(LD) $(LDFLAGS) $(MRSID_LIBS) ../../$(LIBGDAL) -o $@ $(OBJ:.o=.lo) \ +# -rpath $(INST_LIB) \ +# -no-undefined -export-dynamic + diff --git a/bazaar/plugin/gdal/frmts/mrsid/frmt_jp2mrsid.html b/bazaar/plugin/gdal/frmts/mrsid/frmt_jp2mrsid.html new file mode 100644 index 000000000..dbb4327b9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid/frmt_jp2mrsid.html @@ -0,0 +1,86 @@ + + + JP2MrSID --- JPEG2000 via MrSID SDK + + + + +

    JP2MrSID --- JPEG2000 via MrSID SDK

    + +JPEG2000 file format is supported for reading with the MrSID DSDK. It +is also supported for writing with the MrSID ESDK.

    + +JPEG2000 MrSID support is only available with the version 5.x or newer +DSDK and ESDK.

    + +

    Creation Options

    + +If you have the MrSID ESDK (5.x or newer), it can be used to write JPEG2000 +files. The following creation options are supported. + +
      +
    • WORLDFILE=YES: to write an ESRI world file (with the extension + .j2w). +

      +

    • COMPRESSION=n: Indicates the desired compression ratio. Zero indicates +lossless compression. Twenty would indicate a 20:1 compression ratio (the +image would be compressed to 1/20 its original size). +

      + +

    • XMLPROFILE=[path to file]: Indicates a path to a LizardTech-specific + XML profile that can be used to set JPEG2000 encoding parameters. They can + be created + using +the MrSID ESDK, or with GeoExpress, or by hand using the following example as +a template: +

      +

      +<?xml version="1.0"?>
      +<Jp2Profile version="1.0">
      +  <Header>
      +    <name>Default</name> 
      +    <description>LizardTech preferred settings (20051216)</description>
      +  </Header>
      +  <Codestream>
      +    <layers>
      +      8
      +    </layers>
      +    <levels>
      +      99
      +    </levels>
      +    <tileSize>
      +      0 0
      +    </tileSize>
      +    <progressionOrder>
      +      RPCL
      +    </progressionOrder>
      +    <codeblockSize>
      +      64 64
      +    </codeblockSize>
      +    <pltMarkers>
      +      true
      +    </pltMarkers>
      +    <wavelet97>
      +      false
      +    </wavelet97>
      +    <precinctSize>
      +      256 256
      +    </precinctSize>
      +  </Codestream>
      +</Jp2Profile>
      +
      +
      +
    + +

    See Also:

    + + + + + + diff --git a/bazaar/plugin/gdal/frmts/mrsid/frmt_mrsid.html b/bazaar/plugin/gdal/frmts/mrsid/frmt_mrsid.html new file mode 100644 index 000000000..36125ddd8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid/frmt_mrsid.html @@ -0,0 +1,62 @@ + + + MrSID --- Multi-resolution Seamless Image Database + + + + +

    MrSID --- Multi-resolution Seamless Image Database

    + +MrSID is a wavelet-based image compression technology which can utilise both +lossy and lossless encoding. This technology was acquired in its original +form from Los Alamos National Laboratories (LANL), where it was developed +under the aegis of the U.S. government for storing fingerprints for the FBI. +Now is is developed and distributed by the LizardTech, Inc.

    + +This driver supports reading of MrSID image files using LizardTech's decoding +software development kit (DSDK). This DSDK is not free software, you should +contact LizardTech to obtain it (see link at end of this page). If you are +using GCC, please, ensure that you have the same compiler as was used +for DSDK compilation. It is C++ library, so you may get incompatibilities +in C++ name mangling between different GCC versions (2.95.x and 3.x).

    + +Latest versions of the DSDK also support decoding JPEG2000 file format, so this driver can be used for JPEG2000 too.

    + +

    Metadata

    + +MrSID metadata transparently translated into GDAL metadata strings. Files +in MrSID format contain a set of standard metadata tags such as: +IMAGE__WIDTH (contains the width of the image), IMAGE__HEIGHT (contains the +height of the image), IMAGE__XY_ORIGIN (contains the x and y coordinates +of the origin), IMAGE__INPUT_NAME (contains the name or names of the files +used to create the MrSID image) etc. GDAL's metadata keys cannot contain +characters `:' and `=', but standard MrSID tags always contain double colons +in tag names. These characters replaced in GDAL with `_' during translation. +So if you are using other software to work with MrSID be ready that names +of metadata keys will be shown differently in GDAL.

    + +Starting with GDAL 1.9.0, XMP metadata can be extracted from JPEG2000 files, and will be +stored as XML raw content in the xml:XMP metadata domain.

    + +

    Georeference

    + +MrSID images may contain georeference and coordinate system information in +form of GeoTIFF GeoKeys, translated in metadata records. All those GeoKeys +properly extracted and used by the driver. Unfortunately, there is one +caveat: old MrSID encoders has a bug which resulted in wrong GeoKeys, stored +in MrSID files. This bug was fixed in MrSID software version 1.5, but if you +have older encoders or files, created with older encoders, you cannot use +georeference information from them.

    + +

    See Also:

    + + + + + + diff --git a/bazaar/plugin/gdal/frmts/mrsid/makefile.vc b/bazaar/plugin/gdal/frmts/mrsid/makefile.vc new file mode 100644 index 000000000..0f0cffd5d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid/makefile.vc @@ -0,0 +1,47 @@ + +OBJ = mrsiddataset.obj mrsidstream.obj + +PLUGIN_DLL = gdal_MrSID.dll + +EXTRAFLAGS = $(MRSID_INCLUDE) $(GEOTIFF_INC) $(MRSID_FLAGS) + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt +!INCLUDE nmake.opt + +!IFNDEF GEOTIFF_INC +GEOTIFF_INC = -I..\gtiff\libgeotiff +!ENDIF + +!IF "$(MRSID_PLUGIN)" == "YES" && "$(MRSID_RDLLBUILD)" != "YES" +MRSID_FLAGS = -DMRSID_USE_TIFFSYMS_WORKAROUND $(MRSID_FLAGS) +!IFNDEF GEOTIFF_LIB +GEOTIFF_LIB = ..\gtiff\libgeotiff\geo_tiffp.obj +!ENDIF +!IFNDEF TIFF_LIB +TIFF_LIB = ..\gtiff\libtiff\tif_vsi.obj +!ENDIF +!ENDIF + +default: $(OBJ) + $(INSTALL) *.obj ..\o + +clean: + -del *.obj + -del *.dll + -del *.exp + -del *.lib + -del *.manifest + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) \ + $(GDAL_ROOT)/gdal_i.lib $(MRSID_LIB) $(GEOTIFF_LIB) $(TIFF_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) + diff --git a/bazaar/plugin/gdal/frmts/mrsid/mrsiddataset.cpp b/bazaar/plugin/gdal/frmts/mrsid/mrsiddataset.cpp new file mode 100644 index 000000000..2c5caa736 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid/mrsiddataset.cpp @@ -0,0 +1,3658 @@ +/****************************************************************************** + * $Id: mrsiddataset.cpp 28785 2015-03-26 20:46:45Z goatbar $ + * + * Project: Multi-resolution Seamless Image Database (MrSID) + * Purpose: Read/write LizardTech's MrSID file format - Version 4+ SDK. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2003, Andrey Kiselev + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#define NO_DELETE + +#include "gdaljp2abstractdataset.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include "gdaljp2metadata.h" +#include + +#include +#include + +CPL_CVSID("$Id: mrsiddataset.cpp 28785 2015-03-26 20:46:45Z goatbar $"); + +CPL_C_START +double GTIFAngleToDD( double dfAngle, int nUOMAngle ); +void CPL_DLL LibgeotiffOneTimeInit(); +CPL_C_END + +// Key Macros from Makefile: +// MRSID_ESDK: Means we have the encoding SDK (version 5 or newer required) +// MRSID_J2K: Means we are enabling MrSID SDK JPEG2000 support. + +#include "lt_types.h" +#include "lt_base.h" +#include "lt_fileSpec.h" +#include "lt_ioFileStream.h" +#include "lt_utilStatusStrings.h" +#include "lti_geoCoord.h" +#include "lti_pixel.h" +#include "lti_navigator.h" +#include "lti_sceneBuffer.h" +#include "lti_metadataDatabase.h" +#include "lti_metadataRecord.h" +#include "lti_utils.h" +#include "lti_delegates.h" +#include "lt_utilStatus.h" +#include "MrSIDImageReader.h" + +#ifdef MRSID_J2K +# include "J2KImageReader.h" +#endif + +// It seems that LT_STS_UTIL_TimeUnknown was added in version 6, also +// the first version with lti_version.h +#ifdef LT_STS_UTIL_TimeUnknown +# include "lti_version.h" +#endif + +// Are we using version 6 or newer? +#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 6 +# define MRSID_POST5 +#endif + +#ifdef MRSID_ESDK +# include "MG3ImageWriter.h" +# include "MG3WriterParams.h" +# include "MG2ImageWriter.h" +# include "MG2WriterParams.h" +# ifdef MRSID_HAVE_MG4WRITE +# include "MG4ImageWriter.h" +# include "MG4WriterParams.h" +# endif +# ifdef MRSID_J2K +# ifdef MRSID_POST5 +# include "JP2WriterManager.h" +# include "JPCWriterParams.h" +# else +# include "J2KImageWriter.h" +# include "J2KWriterParams.h" +# endif +# endif +#endif /* MRSID_ESDK */ + +#ifdef MRSID_POST5 +# define MRSID_HAVE_GETWKT +#endif + +#include "mrsidstream.h" + +LT_USE_NAMESPACE(LizardTech) + +/* -------------------------------------------------------------------- */ +/* Various wrapper templates used to force new/delete to happen */ +/* in the same heap. See bug 1213 and MSDN knowledgebase */ +/* article 122675. */ +/* -------------------------------------------------------------------- */ + +template +class LTIDLLPixel : public T +{ +public: + LTIDLLPixel(LTIColorSpace colorSpace, + lt_uint16 numBands, + LTIDataType dataType) : T(colorSpace,numBands,dataType) {} + virtual ~LTIDLLPixel() {}; +}; + +template +class LTIDLLReader : public T +{ +public: + LTIDLLReader(const LTFileSpec& fileSpec, + bool useWorldFile = false) : T(fileSpec, useWorldFile) {} + LTIDLLReader(LTIOStreamInf &oStream, + bool useWorldFile = false) : T(oStream, useWorldFile) {} + LTIDLLReader(LTIOStreamInf *poStream, + LTIOStreamInf *poWorldFile = NULL) : T(poStream, poWorldFile) {} + virtual ~LTIDLLReader() {}; +}; + +template +class LTIDLLNavigator : public T +{ +public: + LTIDLLNavigator(const LTIImage& image ) : T(image) {} + virtual ~LTIDLLNavigator() {}; +}; + +template +class LTIDLLBuffer : public T +{ +public: + LTIDLLBuffer(const LTIPixel& pixelProps, + lt_uint32 totalNumCols, + lt_uint32 totalNumRows, + void** data ) : T(pixelProps,totalNumCols,totalNumRows,data) {} + virtual ~LTIDLLBuffer() {}; +}; + +template +class LTIDLLCopy : public T +{ +public: + LTIDLLCopy(const T& original) : T(original) {} + virtual ~LTIDLLCopy() {}; +}; + +template +class LTIDLLWriter : public T +{ +public: + LTIDLLWriter(LTIImageStage *image) : T(image) {} + virtual ~LTIDLLWriter() {} +}; + +template +class LTIDLLDefault : public T +{ +public: + LTIDLLDefault() : T() {} + virtual ~LTIDLLDefault() {} +}; + +/* -------------------------------------------------------------------- */ +/* Interface to MrSID SDK progress reporting. */ +/* -------------------------------------------------------------------- */ + +class MrSIDProgress : public LTIProgressDelegate +{ +public: + MrSIDProgress(GDALProgressFunc f, void *arg) : m_f(f), m_arg(arg) {} + virtual ~MrSIDProgress() {} + virtual LT_STATUS setProgressStatus(float fraction) + { + if (!m_f) + return LT_STS_BadContext; + if( !m_f( fraction, NULL, m_arg ) ) + return LT_STS_Failure; + return LT_STS_Success; + } +private: + GDALProgressFunc m_f; + void *m_arg; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* MrSIDDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class MrSIDDataset : public GDALJP2AbstractDataset +{ + friend class MrSIDRasterBand; + + LTIOStreamInf *poStream; + LTIOFileStream oLTIStream; + LTIVSIStream oVSIStream; + +#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 7 + LTIImageFilter *poImageReader; +#else + LTIImageReader *poImageReader; +#endif + +#ifdef MRSID_ESDK + LTIGeoFileImageWriter *poImageWriter; +#endif + + LTIDLLNavigator *poLTINav; + LTIDLLCopy *poMetadata; + const LTIPixel *poNDPixel; + + LTIDLLBuffer *poBuffer; + int nBlockXSize, nBlockYSize; + int bPrevBlockRead; + int nPrevBlockXOff, nPrevBlockYOff; + + LTIDataType eSampleType; + GDALDataType eDataType; + LTIColorSpace eColorSpace; + + double dfCurrentMag; + + GTIFDefn *psDefn; + + MrSIDDataset *poParentDS; + int bIsOverview; + int nOverviewCount; + MrSIDDataset **papoOverviewDS; + + CPLString osMETFilename; + + CPLErr OpenZoomLevel( lt_int32 iZoom ); + char *SerializeMetadataRec( const LTIMetadataRecord* ); + int GetMetadataElement( const char *, void *, int=0 ); + void FetchProjParms(); + void GetGTIFDefn(); + char *GetOGISDefn( GTIFDefn * ); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, void *, + int, int, GDALDataType, int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + protected: + virtual int CloseDependentDatasets(); + + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + + public: + MrSIDDataset(int bIsJPEG2000); + ~MrSIDDataset(); + + static GDALDataset *Open( GDALOpenInfo * poOpenInfo, int bIsJP2 ); + + virtual char **GetFileList(); + +#ifdef MRSID_ESDK + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); + virtual void FlushCache( void ); +#endif +}; + +/************************************************************************/ +/* ==================================================================== */ +/* MrSIDRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class MrSIDRasterBand : public GDALPamRasterBand +{ + friend class MrSIDDataset; + + LTIPixel *poPixel; + + int nBlockSize; + + int bNoDataSet; + double dfNoDataValue; + + MrSIDDataset *poGDS; + + GDALColorInterp eBandInterp; + + public: + + MrSIDRasterBand( MrSIDDataset *, int ); + ~MrSIDRasterBand(); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); + CPLErr SetColorInterpretation( GDALColorInterp eNewInterp ); + virtual double GetNoDataValue( int * ); + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview( int ); + + virtual CPLErr GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev ); + +#ifdef MRSID_ESDK + virtual CPLErr IWriteBlock( int, int, void * ); +#endif +}; + +/************************************************************************/ +/* MrSIDRasterBand() */ +/************************************************************************/ + +MrSIDRasterBand::MrSIDRasterBand( MrSIDDataset *poDS, int nBand ) +{ + this->poDS = poDS; + poGDS = poDS; + this->nBand = nBand; + this->eDataType = poDS->eDataType; + +/* -------------------------------------------------------------------- */ +/* Set the block sizes and buffer parameters. */ +/* -------------------------------------------------------------------- */ + nBlockXSize = poDS->nBlockXSize; + nBlockYSize = poDS->nBlockYSize; +//#ifdef notdef + if( poDS->GetRasterXSize() > 2048 ) + nBlockXSize = 1024; + if( poDS->GetRasterYSize() > 128 ) + nBlockYSize = 128; + else + nBlockYSize = poDS->GetRasterYSize(); +//#endif + + nBlockSize = nBlockXSize * nBlockYSize; + poPixel = new LTIDLLPixel( poDS->eColorSpace, poDS->nBands, + poDS->eSampleType ); + + +/* -------------------------------------------------------------------- */ +/* Set NoData values. */ +/* */ +/* This logic is disabled for now since the MrSID nodata */ +/* semantics are different than GDAL. In MrSID all bands must */ +/* match the nodata value for that band in order for the pixel */ +/* to be considered nodata, otherwise all values are valid. */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + if ( poDS->poNDPixel ) + { + switch( poDS->eSampleType ) + { + case LTI_DATATYPE_UINT8: + case LTI_DATATYPE_SINT8: + dfNoDataValue = (double) + poDS->poNDPixel->getSampleValueUint8( nBand - 1 ); + break; + case LTI_DATATYPE_UINT16: + dfNoDataValue = (double) + poDS->poNDPixel->getSampleValueUint16( nBand - 1 ); + break; + case LTI_DATATYPE_FLOAT32: + dfNoDataValue = + poDS->poNDPixel->getSampleValueFloat32( nBand - 1 ); + break; + case LTI_DATATYPE_SINT16: + dfNoDataValue = (double) + *(GInt16 *)poDS->poNDPixel->getSampleValueAddr( nBand - 1 ); + break; + case LTI_DATATYPE_UINT32: + dfNoDataValue = (double) + *(GUInt32 *)poDS->poNDPixel->getSampleValueAddr( nBand - 1 ); + break; + case LTI_DATATYPE_SINT32: + dfNoDataValue = (double) + *(GInt32 *)poDS->poNDPixel->getSampleValueAddr( nBand - 1 ); + break; + case LTI_DATATYPE_FLOAT64: + dfNoDataValue = + *(double *)poDS->poNDPixel->getSampleValueAddr( nBand - 1 ); + break; + + case LTI_DATATYPE_INVALID: + CPLAssert( FALSE ); + break; + } + bNoDataSet = TRUE; + } + else +#endif + { + dfNoDataValue = 0.0; + bNoDataSet = FALSE; + } + + switch( poGDS->eColorSpace ) + { + case LTI_COLORSPACE_RGB: + if( nBand == 1 ) + eBandInterp = GCI_RedBand; + else if( nBand == 2 ) + eBandInterp = GCI_GreenBand; + else if( nBand == 3 ) + eBandInterp = GCI_BlueBand; + else + eBandInterp = GCI_Undefined; + break; + +#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 8 + case LTI_COLORSPACE_RGBA: + if( nBand == 1 ) + eBandInterp = GCI_RedBand; + else if( nBand == 2 ) + eBandInterp = GCI_GreenBand; + else if( nBand == 3 ) + eBandInterp = GCI_BlueBand; + else if( nBand == 4 ) + eBandInterp = GCI_AlphaBand; + else + eBandInterp = GCI_Undefined; + break; +#endif + + case LTI_COLORSPACE_CMYK: + if( nBand == 1 ) + eBandInterp = GCI_CyanBand; + else if( nBand == 2 ) + eBandInterp = GCI_MagentaBand; + else if( nBand == 3 ) + eBandInterp = GCI_YellowBand; + else if( nBand == 4 ) + eBandInterp = GCI_BlackBand; + else + eBandInterp = GCI_Undefined; + break; + + case LTI_COLORSPACE_GRAYSCALE: + eBandInterp = GCI_GrayIndex; + break; + + default: + eBandInterp = GCI_Undefined; + break; + } +} + +/************************************************************************/ +/* ~MrSIDRasterBand() */ +/************************************************************************/ + +MrSIDRasterBand::~MrSIDRasterBand() +{ + if ( poPixel ) + delete poPixel; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr MrSIDRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ +#ifdef MRSID_ESDK + if( poGDS->eAccess == GA_Update ) + { + CPLDebug( "MrSID", + "IReadBlock() - DSDK - read on updatable file fails." ); + memset( pImage, 0, nBlockSize * GDALGetDataTypeSize(eDataType) / 8 ); + return CE_None; + } +#endif /* MRSID_ESDK */ + + CPLDebug( "MrSID", "IReadBlock(%d,%d)", nBlockXOff, nBlockYOff ); + + if ( !poGDS->bPrevBlockRead + || poGDS->nPrevBlockXOff != nBlockXOff + || poGDS->nPrevBlockYOff != nBlockYOff ) + { + GInt32 nLine = nBlockYOff * nBlockYSize; + GInt32 nCol = nBlockXOff * nBlockXSize; + + // XXX: The scene, passed to LTIImageStage::read() call must be + // inside the image boundaries. So we should detect the last strip and + // form the scene properly. + CPLDebug( "MrSID", + "IReadBlock - read() %dx%d block at %d,%d.", + nBlockXSize, nBlockYSize, nCol, nLine ); + + if(!LT_SUCCESS( poGDS->poLTINav->setSceneAsULWH( + nCol, nLine, + (nCol+nBlockXSize>poGDS->GetRasterXSize())? + (poGDS->GetRasterXSize()-nCol):nBlockXSize, + (nLine+nBlockYSize>poGDS->GetRasterYSize())? + (poGDS->GetRasterYSize()-nLine):nBlockYSize, + poGDS->dfCurrentMag) )) + + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDRasterBand::IReadBlock(): Failed to set scene position." ); + return CE_Failure; + } + + if ( !poGDS->poBuffer ) + { + poGDS->poBuffer = + new LTIDLLBuffer( *poPixel, nBlockXSize, nBlockYSize, NULL ); +// poGDS->poBuffer = +// new LTISceneBuffer( *poPixel, nBlockXSize, nBlockYSize, NULL ); + } + + if(!LT_SUCCESS(poGDS->poImageReader->read(poGDS->poLTINav->getScene(), + *poGDS->poBuffer))) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDRasterBand::IReadBlock(): Failed to load image." ); + return CE_Failure; + } + + poGDS->bPrevBlockRead = TRUE; + poGDS->nPrevBlockXOff = nBlockXOff; + poGDS->nPrevBlockYOff = nBlockYOff; + } + + memcpy( pImage, poGDS->poBuffer->getTotalBandData(nBand - 1), + nBlockSize * (GDALGetDataTypeSize(poGDS->eDataType) / 8) ); + + return CE_None; +} + +#ifdef MRSID_ESDK + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr MrSIDRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + CPLAssert( poGDS != NULL + && nBlockXOff >= 0 + && nBlockYOff >= 0 + && pImage != NULL ); + +#ifdef DEBUG + CPLDebug( "MrSID", "IWriteBlock(): nBlockXOff=%d, nBlockYOff=%d", + nBlockXOff, nBlockYOff ); +#endif + + LTIScene oScene( nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + nBlockXSize, nBlockYSize, 1.0); + LTISceneBuffer oSceneBuf( *poPixel, poGDS->nBlockXSize, + poGDS->nBlockYSize, &pImage ); + + if( !LT_SUCCESS(poGDS->poImageWriter->writeBegin(oScene)) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDRasterBand::IWriteBlock(): writeBegin failed." ); + return CE_Failure; + } + + if( !LT_SUCCESS(poGDS->poImageWriter->writeStrip(oSceneBuf, oScene)) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDRasterBand::IWriteBlock(): writeStrip failed." ); + return CE_Failure; + } + + if( !LT_SUCCESS(poGDS->poImageWriter->writeEnd()) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDRasterBand::IWriteBlock(): writeEnd failed." ); + return CE_Failure; + } + + return CE_None; +} + +#endif /* MRSID_ESDK */ + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr MrSIDRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ +/* -------------------------------------------------------------------- */ +/* Fallback to default implementation if the whole scanline */ +/* without subsampling requested. */ +/* -------------------------------------------------------------------- */ + if ( nXSize == poGDS->GetRasterXSize() + && nXSize == nBufXSize + && nYSize == nBufYSize ) + { + return GDALRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, + nXSize, nYSize, pData, + nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + } + +/* -------------------------------------------------------------------- */ +/* Handle via the dataset level IRasterIO() */ +/* -------------------------------------------------------------------- */ + return poGDS->IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, + nBufXSize, nBufYSize, eBufType, + 1, &nBand, nPixelSpace, nLineSpace, 0, psExtraArg ); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp MrSIDRasterBand::GetColorInterpretation() + +{ + return eBandInterp; +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/* */ +/* This would normally just be used by folks using the MrSID code */ +/* to read JP2 streams in other formats (such as NITF) and */ +/* providing their own color interpretation regardless of what */ +/* MrSID might think the stream itself says. */ +/************************************************************************/ + +CPLErr MrSIDRasterBand::SetColorInterpretation( GDALColorInterp eNewInterp ) + +{ + eBandInterp = eNewInterp; + + return CE_None; +} + +/************************************************************************/ +/* GetStatistics() */ +/* */ +/* We override this method so that we can force generation of */ +/* statistics if approx ok is true since we know that a small */ +/* overview is always available, and that computing statistics */ +/* from it is very fast. */ +/************************************************************************/ + +CPLErr MrSIDRasterBand::GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev ) + +{ + if( bApproxOK ) + bForce = TRUE; + + return GDALPamRasterBand::GetStatistics( bApproxOK, bForce, + pdfMin, pdfMax, + pdfMean, pdfStdDev ); +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double MrSIDRasterBand::GetNoDataValue( int * pbSuccess ) + +{ + if( bNoDataSet ) + { + if( pbSuccess ) + *pbSuccess = bNoDataSet; + + return dfNoDataValue; + } + + return GDALPamRasterBand::GetNoDataValue( pbSuccess ); +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int MrSIDRasterBand::GetOverviewCount() + +{ + return poGDS->nOverviewCount; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *MrSIDRasterBand::GetOverview( int i ) + +{ + if( i < 0 || i >= poGDS->nOverviewCount ) + return NULL; + else + return poGDS->papoOverviewDS[i]->GetRasterBand( nBand ); +} + +/************************************************************************/ +/* MrSIDDataset() */ +/************************************************************************/ + +MrSIDDataset::MrSIDDataset(int bIsJPEG2000) +{ + poStream = NULL; + poImageReader = NULL; +#ifdef MRSID_ESDK + poImageWriter = NULL; +#endif + poLTINav = NULL; + poMetadata = NULL; + poNDPixel = NULL; + eSampleType = LTI_DATATYPE_UINT8; + nBands = 0; + eDataType = GDT_Byte; + + poBuffer = NULL; + bPrevBlockRead = FALSE; + nPrevBlockXOff = 0; + nPrevBlockYOff = 0; + + psDefn = NULL; + + dfCurrentMag = 1.0; + bIsOverview = FALSE; + poParentDS = this; + nOverviewCount = 0; + papoOverviewDS = NULL; + + poDriver = (GDALDriver*) GDALGetDriverByName( bIsJPEG2000 ? "JP2MrSID" : "MrSID" ); +} + +/************************************************************************/ +/* ~MrSIDDataset() */ +/************************************************************************/ + +MrSIDDataset::~MrSIDDataset() +{ + FlushCache(); + +#ifdef MRSID_ESDK + if ( poImageWriter ) + delete poImageWriter; +#endif + + if ( poBuffer ) + delete poBuffer; + if ( poMetadata ) + delete poMetadata; + if ( poLTINav ) + delete poLTINav; + if ( poImageReader && !bIsOverview ) +#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 7 + { + poImageReader->release(); + poImageReader = NULL; + } +#else + delete poImageReader; +#endif + // points to another member, don't delete + poStream = NULL; + + if ( psDefn ) + delete psDefn; + CloseDependentDatasets(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int MrSIDDataset::CloseDependentDatasets() +{ + int bRet = GDALPamDataset::CloseDependentDatasets(); + + if ( papoOverviewDS ) + { + for( int i = 0; i < nOverviewCount; i++ ) + delete papoOverviewDS[i]; + CPLFree( papoOverviewDS ); + papoOverviewDS = NULL; + bRet = TRUE; + } + return bRet; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr MrSIDDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ +/* -------------------------------------------------------------------- */ +/* We need various criteria to skip out to block based methods. */ +/* -------------------------------------------------------------------- */ + int bUseBlockedIO = bForceCachedIO; + + if( nYSize == 1 || nXSize * ((double) nYSize) < 100.0 ) + bUseBlockedIO = TRUE; + + if( nBufYSize == 1 || nBufXSize * ((double) nBufYSize) < 100.0 ) + bUseBlockedIO = TRUE; + + if( CSLTestBoolean( CPLGetConfigOption( "GDAL_ONE_BIG_READ", "NO") ) ) + bUseBlockedIO = FALSE; + + if( bUseBlockedIO ) + return GDALDataset::BlockBasedRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); + CPLDebug( "MrSID", "RasterIO() - using optimized dataset level IO." ); + +/* -------------------------------------------------------------------- */ +/* What is our requested window relative to the base dataset. */ +/* We want to operate from here on as if we were operating on */ +/* the full res band. */ +/* -------------------------------------------------------------------- */ + int nZoomMag = (int) ((1/dfCurrentMag) * 1.0000001); + + nXOff *= nZoomMag; + nYOff *= nZoomMag; + nXSize *= nZoomMag; + nYSize *= nZoomMag; + +/* -------------------------------------------------------------------- */ +/* We need to figure out the best zoom level to use for this */ +/* request. We apply a small fudge factor to make sure that */ +/* request just very, very slightly larger than a zoom level do */ +/* not force us to the next level. */ +/* -------------------------------------------------------------------- */ + int iOverview = 0; + double dfZoomMag = MIN((nXSize / (double)nBufXSize), + (nYSize / (double)nBufYSize)); + + for( nZoomMag = 1; + nZoomMag * 2 < (dfZoomMag + 0.1) + && iOverview < poParentDS->nOverviewCount; + nZoomMag *= 2, iOverview++ ) {} + +/* -------------------------------------------------------------------- */ +/* Work out the size of the temporary buffer and allocate it. */ +/* The temporary buffer will generally be at a moderately */ +/* higher resolution than the buffer of data requested. */ +/* -------------------------------------------------------------------- */ + int nTmpPixelSize; + LTIPixel oPixel( eColorSpace, nBands, eSampleType ); + + LT_STATUS eLTStatus; + unsigned int maxWidth; + unsigned int maxHeight; + + eLTStatus = poImageReader->getDimsAtMag(1.0/nZoomMag,maxWidth,maxHeight); + + if( !LT_SUCCESS(eLTStatus)) { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDDataset::IRasterIO(): Failed to get zoomed image dimensions.\n%s", + getLastStatusString( eLTStatus ) ); + return CE_Failure; + } + + int maxWidthAtL0 = bIsOverview?poParentDS->GetRasterXSize():this->GetRasterXSize(); + int maxHeightAtL0 = bIsOverview?poParentDS->GetRasterYSize():this->GetRasterYSize(); + + int sceneUlXOff = nXOff / nZoomMag; + int sceneUlYOff = nYOff / nZoomMag; + int sceneWidth = (int)(nXSize * (double) maxWidth / (double)maxWidthAtL0 + 0.99); + int sceneHeight = (int)(nYSize * (double) maxHeight / (double)maxHeightAtL0 + 0.99); + + if( (sceneUlXOff + sceneWidth) > (int) maxWidth ) + sceneWidth = maxWidth - sceneUlXOff; + + if( (sceneUlYOff + sceneHeight) > (int) maxHeight ) + sceneHeight = maxHeight - sceneUlYOff; + + LTISceneBuffer oLTIBuffer( oPixel, sceneWidth, sceneHeight, NULL ); + + nTmpPixelSize = GDALGetDataTypeSize( eDataType ) / 8; + +/* -------------------------------------------------------------------- */ +/* Create navigator, and move to the requested scene area. */ +/* -------------------------------------------------------------------- */ + LTINavigator oNav( *poImageReader ); + + if( !LT_SUCCESS(oNav.setSceneAsULWH( sceneUlXOff, sceneUlYOff, + sceneWidth, sceneHeight, + 1.0 / nZoomMag )) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDDataset::IRasterIO(): Failed to set scene position." ); + + return CE_Failure; + } + + CPLDebug( "MrSID", + "Dataset:IRasterIO(%d,%d %dx%d -> %dx%d -> %dx%d, zoom=%d)", + nXOff, nYOff, nXSize, nYSize, + sceneWidth, sceneHeight, + nBufXSize, nBufYSize, + nZoomMag ); + + if( !oNav.isSceneValid() ) + CPLDebug( "MrSID", "LTINavigator in invalid state." ); + +/* -------------------------------------------------------------------- */ +/* Read into the buffer. */ +/* -------------------------------------------------------------------- */ + + eLTStatus = poImageReader->read(oNav.getScene(),oLTIBuffer); + if(!LT_SUCCESS(eLTStatus) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDRasterBand::IRasterIO(): Failed to load image.\n%s", + getLastStatusString( eLTStatus ) ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* If we are pulling the data at a matching resolution, try to */ +/* do a more direct copy without subsampling. */ +/* -------------------------------------------------------------------- */ + int iBufLine, iBufPixel; + + if( nBufXSize == sceneWidth && nBufYSize == sceneHeight ) + { + for( int iBand = 0; iBand < nBandCount; iBand++ ) + { + GByte *pabySrcBand = (GByte *) + oLTIBuffer.getTotalBandData( panBandMap[iBand] - 1 ); + + for( int iLine = 0; iLine < nBufYSize; iLine++ ) + { + GDALCopyWords( pabySrcBand + iLine*nTmpPixelSize*sceneWidth, + eDataType, nTmpPixelSize, + ((GByte *)pData) + iLine*nLineSpace + + iBand * nBandSpace, + eBufType, nPixelSpace, + nBufXSize ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Manually resample to our target buffer. */ +/* -------------------------------------------------------------------- */ + else + { + for( iBufLine = 0; iBufLine < nBufYSize; iBufLine++ ) + { + int iTmpLine = (int) floor(((iBufLine+0.5) / nBufYSize)*sceneHeight); + + for( iBufPixel = 0; iBufPixel < nBufXSize; iBufPixel++ ) + { + int iTmpPixel = (int) + floor(((iBufPixel+0.5) / nBufXSize) * sceneWidth); + + for( int iBand = 0; iBand < nBandCount; iBand++ ) + { + GByte *pabySrc, *pabyDst; + + pabyDst = ((GByte *) pData) + + nPixelSpace * iBufPixel + + nLineSpace * iBufLine + + nBandSpace * iBand; + + pabySrc = (GByte *) oLTIBuffer.getTotalBandData( + panBandMap[iBand] - 1 ); + pabySrc += (iTmpLine * sceneWidth + iTmpPixel) * nTmpPixelSize; + + if( eDataType == eBufType ) + memcpy( pabyDst, pabySrc, nTmpPixelSize ); + else + GDALCopyWords( pabySrc, eDataType, 0, + pabyDst, eBufType, 0, 1 ); + } + } + } + } + + return CE_None; +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr MrSIDDataset::IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, + void * ) +{ + CPLError( CE_Warning, CPLE_AppDefined, + "MrSID overviews are built-in, so building external " + "overviews is unnecessary. Ignoring.\n" ); + + return CE_None; +} + +/************************************************************************/ +/* SerializeMetadataRec() */ +/************************************************************************/ + +char *MrSIDDataset::SerializeMetadataRec( const LTIMetadataRecord *poMetadataRec ) +{ + GUInt32 iNumDims = 0; + const GUInt32 *paiDims = NULL; + const void *pData = poMetadataRec->getArrayData( iNumDims, paiDims ); + GUInt32 i, j, k = 0, iLength; + char *pszMetadata = CPLStrdup( "" ); + + for ( i = 0; i < iNumDims; i++ ) + { + // stops on large binary data + if ( poMetadataRec->getDataType() == LTI_METADATA_DATATYPE_UINT8 + && paiDims[i] > 1024 ) + return pszMetadata; + + for ( j = 0; j < paiDims[i]; j++ ) + { + CPLString osTemp; + + switch( poMetadataRec->getDataType() ) + { + case LTI_METADATA_DATATYPE_UINT8: + case LTI_METADATA_DATATYPE_SINT8: + osTemp.Printf( "%d", ((GByte *)pData)[k++] ); + break; + case LTI_METADATA_DATATYPE_UINT16: + osTemp.Printf( "%u", ((GUInt16 *)pData)[k++] ); + break; + case LTI_METADATA_DATATYPE_SINT16: + osTemp.Printf( "%d", ((GInt16 *)pData)[k++] ); + break; + case LTI_METADATA_DATATYPE_UINT32: + osTemp.Printf( "%u", ((GUInt32 *)pData)[k++] ); + break; + case LTI_METADATA_DATATYPE_SINT32: + osTemp.Printf( "%d", ((GInt32 *)pData)[k++] ); + break; + case LTI_METADATA_DATATYPE_FLOAT32: + osTemp.Printf( "%f", ((float *)pData)[k++] ); + break; + case LTI_METADATA_DATATYPE_FLOAT64: + osTemp.Printf( "%f", ((double *)pData)[k++] ); + break; + case LTI_METADATA_DATATYPE_ASCII: + osTemp = ((const char **)pData)[k++]; + break; + default: + osTemp = ""; + break; + } + + iLength = strlen(pszMetadata) + strlen(osTemp) + 2; + + pszMetadata = (char *)CPLRealloc( pszMetadata, iLength ); + if ( !EQUAL( pszMetadata, "" ) ) + strncat( pszMetadata, ",", 1 ); + strncat( pszMetadata, osTemp, iLength ); + } + } + + return pszMetadata; +} + +/************************************************************************/ +/* GetMetadataElement() */ +/************************************************************************/ + +int MrSIDDataset::GetMetadataElement( const char *pszKey, void *pValue, + int iLength ) +{ + if ( !poMetadata->has( pszKey ) ) + return FALSE; + + const LTIMetadataRecord *poMetadataRec = NULL; + poMetadata->get( pszKey, poMetadataRec ); + + if ( !poMetadataRec->isScalar() ) + return FALSE; + + // XXX: return FALSE if we have more than one element in metadata record + int iSize; + switch( poMetadataRec->getDataType() ) + { + case LTI_METADATA_DATATYPE_UINT8: + case LTI_METADATA_DATATYPE_SINT8: + iSize = 1; + break; + case LTI_METADATA_DATATYPE_UINT16: + case LTI_METADATA_DATATYPE_SINT16: + iSize = 2; + break; + case LTI_METADATA_DATATYPE_UINT32: + case LTI_METADATA_DATATYPE_SINT32: + case LTI_METADATA_DATATYPE_FLOAT32: + iSize = 4; + break; + case LTI_METADATA_DATATYPE_FLOAT64: + iSize = 8; + break; + case LTI_METADATA_DATATYPE_ASCII: + iSize = iLength; + break; + default: + iSize = 0; + break; + } + + if ( poMetadataRec->getDataType() == LTI_METADATA_DATATYPE_ASCII ) + { + strncpy( (char *)pValue, + ((const char**)poMetadataRec->getScalarData())[0], iSize ); + ((char *)pValue)[iSize - 1] = '\0'; + } + else + memcpy( pValue, poMetadataRec->getScalarData(), iSize ); + + return TRUE; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char** MrSIDDataset::GetFileList() +{ + char** papszFileList = GDALPamDataset::GetFileList(); + + if (osMETFilename.size() != 0) + papszFileList = CSLAddString(papszFileList, osMETFilename.c_str()); + + return papszFileList; +} + +/************************************************************************/ +/* OpenZoomLevel() */ +/************************************************************************/ + +CPLErr MrSIDDataset::OpenZoomLevel( lt_int32 iZoom ) +{ +/* -------------------------------------------------------------------- */ +/* Get image geometry. */ +/* -------------------------------------------------------------------- */ + if ( iZoom != 0 ) + { + lt_uint32 iWidth, iHeight; + dfCurrentMag = LTIUtils::levelToMag( iZoom ); + poImageReader->getDimsAtMag( dfCurrentMag, iWidth, iHeight ); + nRasterXSize = iWidth; + nRasterYSize = iHeight; + } + else + { + dfCurrentMag = 1.0; + nRasterXSize = poImageReader->getWidth(); + nRasterYSize = poImageReader->getHeight(); + } + + nBands = poImageReader->getNumBands(); + nBlockXSize = nRasterXSize; + nBlockYSize = poImageReader->getStripHeight(); + + CPLDebug( "MrSID", "Opened zoom level %d with size %dx%d.", + iZoom, nRasterXSize, nRasterYSize ); + + try + { + poLTINav = new LTIDLLNavigator( *poImageReader ); + } + catch ( ... ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDDataset::OpenZoomLevel(): " + "Failed to create LTINavigator object." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Handle sample type and color space. */ +/* -------------------------------------------------------------------- */ + eColorSpace = poImageReader->getColorSpace(); + eSampleType = poImageReader->getDataType(); + switch ( eSampleType ) + { + case LTI_DATATYPE_UINT16: + eDataType = GDT_UInt16; + break; + case LTI_DATATYPE_SINT16: + eDataType = GDT_Int16; + break; + case LTI_DATATYPE_UINT32: + eDataType = GDT_UInt32; + break; + case LTI_DATATYPE_SINT32: + eDataType = GDT_Int32; + break; + case LTI_DATATYPE_FLOAT32: + eDataType = GDT_Float32; + break; + case LTI_DATATYPE_FLOAT64: + eDataType = GDT_Float64; + break; + case LTI_DATATYPE_UINT8: + case LTI_DATATYPE_SINT8: + default: + eDataType = GDT_Byte; + break; + } + +/* -------------------------------------------------------------------- */ +/* Read georeferencing. */ +/* -------------------------------------------------------------------- */ + if ( !poImageReader->isGeoCoordImplicit() ) + { + const LTIGeoCoord& oGeo = poImageReader->getGeoCoord(); + oGeo.get( adfGeoTransform[0], adfGeoTransform[3], + adfGeoTransform[1], adfGeoTransform[5], + adfGeoTransform[2], adfGeoTransform[4] ); + + adfGeoTransform[0] = adfGeoTransform[0] - adfGeoTransform[1] / 2; + adfGeoTransform[3] = adfGeoTransform[3] - adfGeoTransform[5] / 2; + bGeoTransformValid = TRUE; + } + else if( iZoom == 0 ) + { + bGeoTransformValid = + GDALReadWorldFile( GetDescription(), NULL, + adfGeoTransform ) + || GDALReadWorldFile( GetDescription(), ".wld", + adfGeoTransform ); + } + +/* -------------------------------------------------------------------- */ +/* Read wkt. */ +/* -------------------------------------------------------------------- */ +#ifdef MRSID_HAVE_GETWKT + if( !poImageReader->isGeoCoordImplicit() ) + { + const LTIGeoCoord& oGeo = poImageReader->getGeoCoord(); + + if( oGeo.getWKT() ) + { + /* Workaround probable issue with GeoDSK 7 on 64bit Linux */ + if (!(pszProjection != NULL && !EQUALN(pszProjection, "LOCAL_CS", 8) + && EQUALN( oGeo.getWKT(), "LOCAL_CS", 8))) + { + CPLFree( pszProjection ); + pszProjection = CPLStrdup( oGeo.getWKT() ); + } + } + } +#endif // HAVE_MRSID_GETWKT + +/* -------------------------------------------------------------------- */ +/* Special case for https://zulu.ssc.nasa.gov/mrsid/mrsid.pl */ +/* where LandSat .SID are accompanied by a .met file with the */ +/* projection */ +/* -------------------------------------------------------------------- */ + if (iZoom == 0 && (pszProjection == NULL || pszProjection[0] == '\0') && + EQUAL(CPLGetExtension(GetDescription()), "sid")) + { + const char* pszMETFilename = CPLResetExtension(GetDescription(), "met"); + VSILFILE* fp = VSIFOpenL(pszMETFilename, "rb"); + if (fp) + { + const char* pszLine; + int nCountLine = 0; + int nUTMZone = 0; + int bWGS84 = FALSE; + int bUnitsMeter = FALSE; + while ( (pszLine = CPLReadLine2L(fp, 200, NULL)) != NULL && + ++nCountLine < 1000 ) + { + if (nCountLine == 1 && strcmp(pszLine, "::MetadataFile") != 0) + break; + if (EQUALN(pszLine, "Projection UTM ", 15)) + nUTMZone = atoi(pszLine + 15); + else if (EQUAL(pszLine, "Datum WGS84")) + bWGS84 = TRUE; + else if (EQUAL(pszLine, "Units Meters")) + bUnitsMeter = TRUE; + } + VSIFCloseL(fp); + + /* Images in southern hemisphere have negative northings in the */ + /* .sdw file. A bit weird, but anyway we must use the northern */ + /* UTM SRS for consistency */ + if (nUTMZone >= 1 && nUTMZone <= 60 && bWGS84 && bUnitsMeter) + { + osMETFilename = pszMETFilename; + + OGRSpatialReference oSRS; + oSRS.importFromEPSG(32600 + nUTMZone); + CPLFree(pszProjection); + pszProjection = NULL; + oSRS.exportToWkt(&pszProjection); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Read NoData value. */ +/* -------------------------------------------------------------------- */ + poNDPixel = poImageReader->getNoDataPixel(); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 1; iBand <= nBands; iBand++ ) + SetBand( iBand, new MrSIDRasterBand( this, iBand ) ); + + return CE_None; +} + +/************************************************************************/ +/* MrSIDIdentify() */ +/* */ +/* Identify method that only supports MrSID files. */ +/************************************************************************/ + +static int MrSIDIdentify( GDALOpenInfo * poOpenInfo ) +{ + if( poOpenInfo->nHeaderBytes < 32 ) + return FALSE; + + if ( !EQUALN((const char *) poOpenInfo->pabyHeader, "msid", 4) ) + return FALSE; + +#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 8 + lt_uint8 gen; + bool raster; + LT_STATUS eStat = + MrSIDImageReaderInterface::getMrSIDGeneration(poOpenInfo->pabyHeader, gen, raster); + if (!LT_SUCCESS(eStat) || !raster) + return FALSE; +#endif + + return TRUE; +} + +/************************************************************************/ +/* MrSIDOpen() */ +/* */ +/* Open method that only supports MrSID files. */ +/************************************************************************/ + +static GDALDataset* MrSIDOpen( GDALOpenInfo *poOpenInfo ) +{ + if (!MrSIDIdentify(poOpenInfo)) + return NULL; + + return MrSIDDataset::Open( poOpenInfo, FALSE ); +} + + +#ifdef MRSID_J2K + +static const unsigned char jpc_header[] = +{0xff,0x4f}; + +/************************************************************************/ +/* JP2Identify() */ +/* */ +/* Identify method that only supports JPEG2000 files. */ +/************************************************************************/ + +static int JP2Identify( GDALOpenInfo *poOpenInfo ) +{ + if( poOpenInfo->nHeaderBytes < 32 ) + return FALSE; + + if( memcmp( poOpenInfo->pabyHeader, jpc_header, sizeof(jpc_header) ) == 0 ) + { + const char *pszExtension; + + pszExtension = CPLGetExtension( poOpenInfo->pszFilename ); + + if( !EQUAL(pszExtension,"jpc") && !EQUAL(pszExtension,"j2k") + && !EQUAL(pszExtension,"jp2") && !EQUAL(pszExtension,"jpx") + && !EQUAL(pszExtension,"j2c") && !EQUAL(pszExtension,"ntf")) + return FALSE; + } + else if( !EQUALN((const char *) poOpenInfo->pabyHeader + 4, "jP ", 4) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* JP2Open() */ +/* */ +/* Open method that only supports JPEG2000 files. */ +/************************************************************************/ + +static GDALDataset* JP2Open( GDALOpenInfo *poOpenInfo ) +{ + if (!JP2Identify(poOpenInfo)) + return NULL; + + return MrSIDDataset::Open( poOpenInfo, TRUE ); +} + +#endif // MRSID_J2K + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *MrSIDDataset::Open( GDALOpenInfo * poOpenInfo, int bIsJP2 ) +{ + if(poOpenInfo->fpL) + { + VSIFCloseL( poOpenInfo->fpL ); + poOpenInfo->fpL = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Make sure we have hooked CSV lookup for GDAL_DATA. */ +/* -------------------------------------------------------------------- */ + LibgeotiffOneTimeInit(); + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + MrSIDDataset *poDS; + LT_STATUS eStat; + + poDS = new MrSIDDataset(bIsJP2); + + // try the LTIOFileStream first, since it uses filesystem caching + eStat = poDS->oLTIStream.initialize( poOpenInfo->pszFilename, "rb" ); + if ( LT_SUCCESS(eStat) ) + { + eStat = poDS->oLTIStream.open(); + if ( LT_SUCCESS(eStat) ) + poDS->poStream = &(poDS->oLTIStream); + } + + // fall back on VSI for non-files + if ( !LT_SUCCESS(eStat) || !poDS->poStream ) + { + eStat = poDS->oVSIStream.initialize( poOpenInfo->pszFilename, "rb" ); + if ( !LT_SUCCESS(eStat) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "LTIVSIStream::initialize(): " + "failed to open file \"%s\".\n%s", + poOpenInfo->pszFilename, getLastStatusString( eStat ) ); + delete poDS; + return NULL; + } + + eStat = poDS->oVSIStream.open(); + if ( !LT_SUCCESS(eStat) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "LTIVSIStream::open(): " + "failed to open file \"%s\".\n%s", + poOpenInfo->pszFilename, getLastStatusString( eStat ) ); + delete poDS; + return NULL; + } + + poDS->poStream = &(poDS->oVSIStream); + } + +#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 7 + +#ifdef MRSID_J2K + if ( bIsJP2 ) + { + J2KImageReader *reader = J2KImageReader::create(); + eStat = reader->initialize( *(poDS->poStream) ); + poDS->poImageReader = reader; + } + else +#endif /* MRSID_J2K */ + { + MrSIDImageReader *reader = MrSIDImageReader::create(); + eStat = reader->initialize( poDS->poStream, NULL ); + poDS->poImageReader = reader; + } + +#else /* LTI_SDK_MAJOR < 7 */ + +#ifdef MRSID_J2K + if ( bIsJP2 ) + { + poDS->poImageReader = + new LTIDLLReader( *(poDS->poStream), true ); + eStat = poDS->poImageReader->initialize(); + } + else +#endif /* MRSID_J2K */ + { + poDS->poImageReader = + new LTIDLLReader( poDS->poStream, NULL ); + eStat = poDS->poImageReader->initialize(); + } + +#endif /* LTI_SDK_MAJOR >= 7 */ + + if ( !LT_SUCCESS(eStat) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "LTIImageReader::initialize(): " + "failed to initialize reader from the stream \"%s\".\n%s", + poOpenInfo->pszFilename, getLastStatusString( eStat ) ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read metadata. */ +/* -------------------------------------------------------------------- */ + poDS->poMetadata = new LTIDLLCopy( + poDS->poImageReader->getMetadata() ); + const GUInt32 iNumRecs = poDS->poMetadata->getIndexCount(); + GUInt32 i; + + for ( i = 0; i < iNumRecs; i++ ) + { + const LTIMetadataRecord *poMetadataRec = NULL; + if ( LT_SUCCESS(poDS->poMetadata->getDataByIndex(i, poMetadataRec)) ) + { + char *pszElement = poDS->SerializeMetadataRec( poMetadataRec ); + char *pszKey = CPLStrdup( poMetadataRec->getTagName() ); + char *pszTemp = pszKey; + + // GDAL metadata keys should not contain ':' and '=' characters. + // We will replace them with '_'. + do + { + if ( *pszTemp == ':' || *pszTemp == '=' ) + *pszTemp = '_'; + } + while ( *++pszTemp ); + + poDS->SetMetadataItem( pszKey, pszElement ); + + CPLFree( pszElement ); + CPLFree( pszKey ); + } + } + +/* -------------------------------------------------------------------- */ +/* Add MrSID version. */ +/* -------------------------------------------------------------------- */ +#ifdef MRSID_J2K + if( !bIsJP2 ) +#endif + { +#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 8 + lt_uint8 gen; + bool raster; + MrSIDImageReaderInterface::getMrSIDGeneration(poOpenInfo->pabyHeader, gen, raster); + poDS->SetMetadataItem( "VERSION", CPLString().Printf("MG%d%s", gen, raster ? "" : " LiDAR") ); +#else + lt_uint8 major; + lt_uint8 minor; + char letter; + MrSIDImageReader* poMrSIDImageReader = (MrSIDImageReader*)poDS->poImageReader; + poMrSIDImageReader->getVersion(major, minor, minor, letter); + if (major < 2) + major = 2; + poDS->SetMetadataItem( "VERSION", CPLString().Printf("MG%d", major) ); +#endif + } + + poDS->GetGTIFDefn(); + +/* -------------------------------------------------------------------- */ +/* Get number of resolution levels (we will use them as overviews).*/ +/* -------------------------------------------------------------------- */ +#ifdef MRSID_J2K + if( bIsJP2 ) + poDS->nOverviewCount + = ((J2KImageReader *) (poDS->poImageReader))->getNumLevels(); + else +#endif + poDS->nOverviewCount + = ((MrSIDImageReader *) (poDS->poImageReader))->getNumLevels(); + + if ( poDS->nOverviewCount > 0 ) + { + lt_int32 i; + + poDS->papoOverviewDS = (MrSIDDataset **) + CPLMalloc( poDS->nOverviewCount * (sizeof(void*)) ); + + for ( i = 0; i < poDS->nOverviewCount; i++ ) + { + poDS->papoOverviewDS[i] = new MrSIDDataset(bIsJP2); + poDS->papoOverviewDS[i]->poImageReader = poDS->poImageReader; + poDS->papoOverviewDS[i]->OpenZoomLevel( i + 1 ); + poDS->papoOverviewDS[i]->bIsOverview = TRUE; + poDS->papoOverviewDS[i]->poParentDS = poDS; + } + } + +/* -------------------------------------------------------------------- */ +/* Create object for the whole image. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->OpenZoomLevel( 0 ); + + CPLDebug( "MrSID", + "Opened image: width %d, height %d, bands %d", + poDS->nRasterXSize, poDS->nRasterYSize, poDS->nBands ); + + if( poDS->nBands > 1 ) + poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + + if (bIsJP2) + { + poDS->LoadJP2Metadata(poOpenInfo); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Initialize the overview manager for mask band support. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* EPSGProjMethodToCTProjMethod() */ +/* */ +/* Convert between the EPSG enumeration for projection methods, */ +/* and the GeoTIFF CT codes. */ +/* Explicitly copied from geo_normalize.c of the GeoTIFF package */ +/************************************************************************/ + +static int EPSGProjMethodToCTProjMethod( int nEPSG ) + +{ + /* see trf_method.csv for list of EPSG codes */ + + switch( nEPSG ) + { + case 9801: + return( CT_LambertConfConic_1SP ); + + case 9802: + return( CT_LambertConfConic_2SP ); + + case 9803: + return( CT_LambertConfConic_2SP ); /* Belgian variant not supported */ + + case 9804: + return( CT_Mercator ); /* 1SP and 2SP not differentiated */ + + case 9805: + return( CT_Mercator ); /* 1SP and 2SP not differentiated */ + + case 9806: + return( CT_CassiniSoldner ); + + case 9807: + return( CT_TransverseMercator ); + + case 9808: + return( CT_TransvMercator_SouthOriented ); + + case 9809: + return( CT_ObliqueStereographic ); + + case 9810: + return( CT_PolarStereographic ); + + case 9811: + return( CT_NewZealandMapGrid ); + + case 9812: + return( CT_ObliqueMercator ); /* is hotine actually different? */ + + case 9813: + return( CT_ObliqueMercator_Laborde ); + + case 9814: + return( CT_ObliqueMercator_Rosenmund ); /* swiss */ + + case 9815: + return( CT_ObliqueMercator ); + + case 9816: /* tunesia mining grid has no counterpart */ + return( KvUserDefined ); + } + + return( KvUserDefined ); +} + +/* EPSG Codes for projection parameters. Unfortunately, these bear no + relationship to the GeoTIFF codes even though the names are so similar. */ + +#define EPSGNatOriginLat 8801 +#define EPSGNatOriginLong 8802 +#define EPSGNatOriginScaleFactor 8805 +#define EPSGFalseEasting 8806 +#define EPSGFalseNorthing 8807 +#define EPSGProjCenterLat 8811 +#define EPSGProjCenterLong 8812 +#define EPSGAzimuth 8813 +#define EPSGAngleRectifiedToSkewedGrid 8814 +#define EPSGInitialLineScaleFactor 8815 +#define EPSGProjCenterEasting 8816 +#define EPSGProjCenterNorthing 8817 +#define EPSGPseudoStdParallelLat 8818 +#define EPSGPseudoStdParallelScaleFactor 8819 +#define EPSGFalseOriginLat 8821 +#define EPSGFalseOriginLong 8822 +#define EPSGStdParallel1Lat 8823 +#define EPSGStdParallel2Lat 8824 +#define EPSGFalseOriginEasting 8826 +#define EPSGFalseOriginNorthing 8827 +#define EPSGSphericalOriginLat 8828 +#define EPSGSphericalOriginLong 8829 +#define EPSGInitialLongitude 8830 +#define EPSGZoneWidth 8831 + +/************************************************************************/ +/* SetGTParmIds() */ +/* */ +/* This is hardcoded logic to set the GeoTIFF parameter */ +/* identifiers for all the EPSG supported projections. As the */ +/* trf_method.csv table grows with new projections, this code */ +/* will need to be updated. */ +/* Explicitly copied from geo_normalize.c of the GeoTIFF package. */ +/************************************************************************/ + +static int SetGTParmIds( int nCTProjection, + int *panProjParmId, + int *panEPSGCodes ) + +{ + int anWorkingDummy[7]; + + if( panEPSGCodes == NULL ) + panEPSGCodes = anWorkingDummy; + if( panProjParmId == NULL ) + panProjParmId = anWorkingDummy; + + memset( panEPSGCodes, 0, sizeof(int) * 7 ); + + /* psDefn->nParms = 7; */ + + switch( nCTProjection ) + { + case CT_CassiniSoldner: + case CT_NewZealandMapGrid: + panProjParmId[0] = ProjNatOriginLatGeoKey; + panProjParmId[1] = ProjNatOriginLongGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGNatOriginLat; + panEPSGCodes[1] = EPSGNatOriginLong; + panEPSGCodes[5] = EPSGFalseEasting; + panEPSGCodes[6] = EPSGFalseNorthing; + return TRUE; + + case CT_ObliqueMercator: + panProjParmId[0] = ProjCenterLatGeoKey; + panProjParmId[1] = ProjCenterLongGeoKey; + panProjParmId[2] = ProjAzimuthAngleGeoKey; + panProjParmId[3] = ProjRectifiedGridAngleGeoKey; + panProjParmId[4] = ProjScaleAtCenterGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGProjCenterLat; + panEPSGCodes[1] = EPSGProjCenterLong; + panEPSGCodes[2] = EPSGAzimuth; + panEPSGCodes[3] = EPSGAngleRectifiedToSkewedGrid; + panEPSGCodes[4] = EPSGInitialLineScaleFactor; + panEPSGCodes[5] = EPSGProjCenterEasting; + panEPSGCodes[6] = EPSGProjCenterNorthing; + return TRUE; + + case CT_ObliqueMercator_Laborde: + panProjParmId[0] = ProjCenterLatGeoKey; + panProjParmId[1] = ProjCenterLongGeoKey; + panProjParmId[2] = ProjAzimuthAngleGeoKey; + panProjParmId[4] = ProjScaleAtCenterGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGProjCenterLat; + panEPSGCodes[1] = EPSGProjCenterLong; + panEPSGCodes[2] = EPSGAzimuth; + panEPSGCodes[4] = EPSGInitialLineScaleFactor; + panEPSGCodes[5] = EPSGProjCenterEasting; + panEPSGCodes[6] = EPSGProjCenterNorthing; + return TRUE; + + case CT_LambertConfConic_1SP: + case CT_Mercator: + case CT_ObliqueStereographic: + case CT_PolarStereographic: + case CT_TransverseMercator: + case CT_TransvMercator_SouthOriented: + panProjParmId[0] = ProjNatOriginLatGeoKey; + panProjParmId[1] = ProjNatOriginLongGeoKey; + panProjParmId[4] = ProjScaleAtNatOriginGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGNatOriginLat; + panEPSGCodes[1] = EPSGNatOriginLong; + panEPSGCodes[4] = EPSGNatOriginScaleFactor; + panEPSGCodes[5] = EPSGFalseEasting; + panEPSGCodes[6] = EPSGFalseNorthing; + return TRUE; + + case CT_LambertConfConic_2SP: + panProjParmId[0] = ProjFalseOriginLatGeoKey; + panProjParmId[1] = ProjFalseOriginLongGeoKey; + panProjParmId[2] = ProjStdParallel1GeoKey; + panProjParmId[3] = ProjStdParallel2GeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + panEPSGCodes[0] = EPSGFalseOriginLat; + panEPSGCodes[1] = EPSGFalseOriginLong; + panEPSGCodes[2] = EPSGStdParallel1Lat; + panEPSGCodes[3] = EPSGStdParallel2Lat; + panEPSGCodes[5] = EPSGFalseOriginEasting; + panEPSGCodes[6] = EPSGFalseOriginNorthing; + return TRUE; + + case CT_SwissObliqueCylindrical: + panProjParmId[0] = ProjCenterLatGeoKey; + panProjParmId[1] = ProjCenterLongGeoKey; + panProjParmId[5] = ProjFalseEastingGeoKey; + panProjParmId[6] = ProjFalseNorthingGeoKey; + + /* EPSG codes? */ + return TRUE; + + default: + return( FALSE ); + } +} + +static const char *papszDatumEquiv[] = +{ + "Militar_Geographische_Institut", + "Militar_Geographische_Institute", + "World_Geodetic_System_1984", + "WGS_1984", + "WGS_72_Transit_Broadcast_Ephemeris", + "WGS_1972_Transit_Broadcast_Ephemeris", + "World_Geodetic_System_1972", + "WGS_1972", + "European_Terrestrial_Reference_System_89", + "European_Reference_System_1989", + NULL +}; + +/************************************************************************/ +/* WKTMassageDatum() */ +/* */ +/* Massage an EPSG datum name into WMT format. Also transform */ +/* specific exception cases into WKT versions. */ +/* Explicitly copied from the gt_wkt_srs.cpp. */ +/************************************************************************/ + +static void WKTMassageDatum( char ** ppszDatum ) + +{ + int i, j; + char *pszDatum = *ppszDatum; + + if (pszDatum[0] == '\0') + return; + +/* -------------------------------------------------------------------- */ +/* Translate non-alphanumeric values to underscores. */ +/* -------------------------------------------------------------------- */ + for( i = 0; pszDatum[i] != '\0'; i++ ) + { + if( !(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z') + && !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z') + && !(pszDatum[i] >= '0' && pszDatum[i] <= '9') ) + { + pszDatum[i] = '_'; + } + } + +/* -------------------------------------------------------------------- */ +/* Remove repeated and trailing underscores. */ +/* -------------------------------------------------------------------- */ + for( i = 1, j = 0; pszDatum[i] != '\0'; i++ ) + { + if( pszDatum[j] == '_' && pszDatum[i] == '_' ) + continue; + + pszDatum[++j] = pszDatum[i]; + } + if( pszDatum[j] == '_' ) + pszDatum[j] = '\0'; + else + pszDatum[j+1] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Search for datum equivelences. Specific massaged names get */ +/* mapped to OpenGIS specified names. */ +/* -------------------------------------------------------------------- */ + for( i = 0; papszDatumEquiv[i] != NULL; i += 2 ) + { + if( EQUAL(*ppszDatum,papszDatumEquiv[i]) ) + { + CPLFree( *ppszDatum ); + *ppszDatum = CPLStrdup( papszDatumEquiv[i+1] ); + return; + } + } +} + +/************************************************************************/ +/* FetchProjParms() */ +/* */ +/* Fetch the projection parameters for a particular projection */ +/* from MrSID metadata, and fill the GTIFDefn structure out */ +/* with them. */ +/* Copied from geo_normalize.c of the GeoTIFF package. */ +/************************************************************************/ + +void MrSIDDataset::FetchProjParms() +{ + double dfNatOriginLong = 0.0, dfNatOriginLat = 0.0, dfRectGridAngle = 0.0; + double dfFalseEasting = 0.0, dfFalseNorthing = 0.0, dfNatOriginScale = 1.0; + double dfStdParallel1 = 0.0, dfStdParallel2 = 0.0, dfAzimuth = 0.0; + +/* -------------------------------------------------------------------- */ +/* Get the false easting, and northing if available. */ +/* -------------------------------------------------------------------- */ + if( !GetMetadataElement( "GEOTIFF_NUM::3082::ProjFalseEastingGeoKey", + &dfFalseEasting ) + && !GetMetadataElement( "GEOTIFF_NUM::3090:ProjCenterEastingGeoKey", + &dfFalseEasting ) ) + dfFalseEasting = 0.0; + + if( !GetMetadataElement( "GEOTIFF_NUM::3083::ProjFalseNorthingGeoKey", + &dfFalseNorthing ) + && !GetMetadataElement( "GEOTIFF_NUM::3091::ProjCenterNorthingGeoKey", + &dfFalseNorthing ) ) + dfFalseNorthing = 0.0; + + switch( psDefn->CTProjection ) + { +/* -------------------------------------------------------------------- */ + case CT_Stereographic: +/* -------------------------------------------------------------------- */ + if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", + &dfNatOriginLong ) == 0 ) + dfNatOriginLong = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey", + &dfNatOriginLat ) == 0 ) + dfNatOriginLat = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3092::ProjScaleAtNatOriginGeoKey", + &dfNatOriginScale ) == 0 ) + dfNatOriginScale = 1.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjCenterLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjCenterLongGeoKey; + psDefn->ProjParm[4] = dfNatOriginScale; + psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_LambertConfConic_1SP: + case CT_Mercator: + case CT_ObliqueStereographic: + case CT_TransverseMercator: + case CT_TransvMercator_SouthOriented: +/* -------------------------------------------------------------------- */ + if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", + &dfNatOriginLong ) == 0 ) + dfNatOriginLong = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey", + &dfNatOriginLat ) == 0 ) + dfNatOriginLat = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3092::ProjScaleAtNatOriginGeoKey", + &dfNatOriginScale ) == 0 ) + dfNatOriginScale = 1.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; + psDefn->ProjParm[4] = dfNatOriginScale; + psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_ObliqueMercator: /* hotine */ +/* -------------------------------------------------------------------- */ + if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", + &dfNatOriginLong ) == 0 ) + dfNatOriginLong = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey", + &dfNatOriginLat ) == 0 ) + dfNatOriginLat = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3094::ProjAzimuthAngleGeoKey", + &dfAzimuth ) == 0 ) + dfAzimuth = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3096::ProjRectifiedGridAngleGeoKey", + &dfRectGridAngle ) == 0 ) + dfRectGridAngle = 90.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3092::ProjScaleAtNatOriginGeoKey", + &dfNatOriginScale ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3093::ProjScaleAtCenterGeoKey", + &dfNatOriginScale ) == 0 ) + dfNatOriginScale = 1.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjCenterLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjCenterLongGeoKey; + psDefn->ProjParm[2] = dfAzimuth; + psDefn->ProjParmId[2] = ProjAzimuthAngleGeoKey; + psDefn->ProjParm[3] = dfRectGridAngle; + psDefn->ProjParmId[3] = ProjRectifiedGridAngleGeoKey; + psDefn->ProjParm[4] = dfNatOriginScale; + psDefn->ProjParmId[4] = ProjScaleAtCenterGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_CassiniSoldner: + case CT_Polyconic: +/* -------------------------------------------------------------------- */ + if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", + &dfNatOriginLong ) == 0 ) + dfNatOriginLong = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey", + &dfNatOriginLat ) == 0 ) + dfNatOriginLat = 0.0; + + + if( GetMetadataElement( "GEOTIFF_NUM::3092::ProjScaleAtNatOriginGeoKey", + &dfNatOriginScale ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3093::ProjScaleAtCenterGeoKey", + &dfNatOriginScale ) == 0 ) + dfNatOriginScale = 1.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; + psDefn->ProjParm[4] = dfNatOriginScale; + psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_AzimuthalEquidistant: + case CT_MillerCylindrical: + case CT_Equirectangular: + case CT_Gnomonic: + case CT_LambertAzimEqualArea: + case CT_Orthographic: +/* -------------------------------------------------------------------- */ + if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", + &dfNatOriginLong ) == 0 ) + dfNatOriginLong = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey", + &dfNatOriginLat ) == 0 ) + dfNatOriginLat = 0.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjCenterLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjCenterLongGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_Robinson: + case CT_Sinusoidal: + case CT_VanDerGrinten: +/* -------------------------------------------------------------------- */ + if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", + &dfNatOriginLong ) == 0 ) + dfNatOriginLong = 0.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjCenterLongGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_PolarStereographic: +/* -------------------------------------------------------------------- */ + if( GetMetadataElement( "GEOTIFF_NUM::3095::ProjStraightVertPoleLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", + &dfNatOriginLong ) == 0 ) + dfNatOriginLong = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey", + &dfNatOriginLat ) == 0 ) + dfNatOriginLat = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3092::ProjScaleAtNatOriginGeoKey", + &dfNatOriginScale ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3093::ProjScaleAtCenterGeoKey", + &dfNatOriginScale ) == 0 ) + dfNatOriginScale = 1.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjStraightVertPoleLongGeoKey; + psDefn->ProjParm[4] = dfNatOriginScale; + psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_LambertConfConic_2SP: +/* -------------------------------------------------------------------- */ + if( GetMetadataElement( "GEOTIFF_NUM::3078::ProjStdParallel1GeoKey", + &dfStdParallel1 ) == 0 ) + dfStdParallel1 = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3079::ProjStdParallel2GeoKey", + &dfStdParallel2 ) == 0 ) + dfStdParallel1 = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", + &dfNatOriginLong ) == 0 ) + dfNatOriginLong = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey", + &dfNatOriginLat ) == 0 ) + dfNatOriginLat = 0.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfNatOriginLat; + psDefn->ProjParmId[0] = ProjFalseOriginLatGeoKey; + psDefn->ProjParm[1] = dfNatOriginLong; + psDefn->ProjParmId[1] = ProjFalseOriginLongGeoKey; + psDefn->ProjParm[2] = dfStdParallel1; + psDefn->ProjParmId[2] = ProjStdParallel1GeoKey; + psDefn->ProjParm[3] = dfStdParallel2; + psDefn->ProjParmId[3] = ProjStdParallel2GeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + +/* -------------------------------------------------------------------- */ + case CT_AlbersEqualArea: + case CT_EquidistantConic: +/* -------------------------------------------------------------------- */ + if( GetMetadataElement( "GEOTIFF_NUM::3078::ProjStdParallel1GeoKey", + &dfStdParallel1 ) == 0 ) + dfStdParallel1 = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3079::ProjStdParallel2GeoKey", + &dfStdParallel2 ) == 0 ) + dfStdParallel1 = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3080::ProjNatOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3084::ProjFalseOriginLongGeoKey", + &dfNatOriginLong ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3088::ProjCenterLongGeoKey", + &dfNatOriginLong ) == 0 ) + dfNatOriginLong = 0.0; + + if( GetMetadataElement( "GEOTIFF_NUM::3081::ProjNatOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3085::ProjFalseOriginLatGeoKey", + &dfNatOriginLat ) == 0 + && GetMetadataElement( "GEOTIFF_NUM::3089::ProjCenterLatGeoKey", + &dfNatOriginLat ) == 0 ) + dfNatOriginLat = 0.0; + + /* notdef: should transform to decimal degrees at this point */ + + psDefn->ProjParm[0] = dfStdParallel1; + psDefn->ProjParmId[0] = ProjStdParallel1GeoKey; + psDefn->ProjParm[1] = dfStdParallel2; + psDefn->ProjParmId[1] = ProjStdParallel2GeoKey; + psDefn->ProjParm[2] = dfNatOriginLat; + psDefn->ProjParmId[2] = ProjNatOriginLatGeoKey; + psDefn->ProjParm[3] = dfNatOriginLong; + psDefn->ProjParmId[3] = ProjNatOriginLongGeoKey; + psDefn->ProjParm[5] = dfFalseEasting; + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[6] = dfFalseNorthing; + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + psDefn->nParms = 7; + break; + } +} + +/************************************************************************/ +/* GetGTIFDefn() */ +/* This function borrowed from the GTIFGetDefn() function. */ +/* See geo_normalize.c from the GeoTIFF package. */ +/************************************************************************/ + +void MrSIDDataset::GetGTIFDefn() +{ + double dfInvFlattening; + +/* -------------------------------------------------------------------- */ +/* Make sure we have hooked CSV lookup for GDAL_DATA. */ +/* -------------------------------------------------------------------- */ + LibgeotiffOneTimeInit(); + +/* -------------------------------------------------------------------- */ +/* Initially we default all the information we can. */ +/* -------------------------------------------------------------------- */ + psDefn = new( GTIFDefn ); + psDefn->Model = KvUserDefined; + psDefn->PCS = KvUserDefined; + psDefn->GCS = KvUserDefined; + psDefn->UOMLength = KvUserDefined; + psDefn->UOMLengthInMeters = 1.0; + psDefn->UOMAngle = KvUserDefined; + psDefn->UOMAngleInDegrees = 1.0; + psDefn->Datum = KvUserDefined; + psDefn->Ellipsoid = KvUserDefined; + psDefn->SemiMajor = 0.0; + psDefn->SemiMinor = 0.0; + psDefn->PM = KvUserDefined; + psDefn->PMLongToGreenwich = 0.0; + + psDefn->ProjCode = KvUserDefined; + psDefn->Projection = KvUserDefined; + psDefn->CTProjection = KvUserDefined; + + psDefn->nParms = 0; + for( int i = 0; i < MAX_GTIF_PROJPARMS; i++ ) + { + psDefn->ProjParm[i] = 0.0; + psDefn->ProjParmId[i] = 0; + } + + psDefn->MapSys = KvUserDefined; + psDefn->Zone = 0; + +/* -------------------------------------------------------------------- */ +/* Try to get the overall model type. */ +/* -------------------------------------------------------------------- */ + GetMetadataElement( "GEOTIFF_NUM::1024::GTModelTypeGeoKey", + &(psDefn->Model) ); + +/* -------------------------------------------------------------------- */ +/* Try to get a PCS. */ +/* -------------------------------------------------------------------- */ + if( GetMetadataElement( "GEOTIFF_NUM::3072::ProjectedCSTypeGeoKey", + &(psDefn->PCS) ) + && psDefn->PCS != KvUserDefined ) + { + /* + * Translate this into useful information. + */ + GTIFGetPCSInfo( psDefn->PCS, NULL, &(psDefn->ProjCode), + &(psDefn->UOMLength), &(psDefn->GCS) ); + } + +/* -------------------------------------------------------------------- */ +/* If we have the PCS code, but didn't find it in the CSV files */ +/* (likely because we can't find them) we will try some ``jiffy */ +/* rules'' for UTM and state plane. */ +/* -------------------------------------------------------------------- */ + if( psDefn->PCS != KvUserDefined && psDefn->ProjCode == KvUserDefined ) + { + int nMapSys, nZone; + int nGCS = psDefn->GCS; + + nMapSys = GTIFPCSToMapSys( psDefn->PCS, &nGCS, &nZone ); + if( nMapSys != KvUserDefined ) + { + psDefn->ProjCode = (short) GTIFMapSysToProj( nMapSys, nZone ); + psDefn->GCS = (short) nGCS; + } + } + +/* -------------------------------------------------------------------- */ +/* If the Proj_ code is specified directly, use that. */ +/* -------------------------------------------------------------------- */ + if( psDefn->ProjCode == KvUserDefined ) + GetMetadataElement( "GEOTIFF_NUM::3074::ProjectionGeoKey", + &(psDefn->ProjCode) ); + + if( psDefn->ProjCode != KvUserDefined ) + { + /* + * We have an underlying projection transformation value. Look + * this up. For a PCS of ``WGS 84 / UTM 11'' the transformation + * would be Transverse Mercator, with a particular set of options. + * The nProjTRFCode itself would correspond to the name + * ``UTM zone 11N'', and doesn't include datum info. + */ + GTIFGetProjTRFInfo( psDefn->ProjCode, NULL, &(psDefn->Projection), + psDefn->ProjParm ); + + /* + * Set the GeoTIFF identity of the parameters. + */ + psDefn->CTProjection = (short) + EPSGProjMethodToCTProjMethod( psDefn->Projection ); + + SetGTParmIds( psDefn->CTProjection, psDefn->ProjParmId, NULL); + psDefn->nParms = 7; + } + +/* -------------------------------------------------------------------- */ +/* Try to get a GCS. If found, it will override any implied by */ +/* the PCS. */ +/* -------------------------------------------------------------------- */ + GetMetadataElement( "GEOTIFF_NUM::2048::GeographicTypeGeoKey", + &(psDefn->GCS) ); + +/* -------------------------------------------------------------------- */ +/* Derive the datum, and prime meridian from the GCS. */ +/* -------------------------------------------------------------------- */ + if( psDefn->GCS != KvUserDefined ) + { + GTIFGetGCSInfo( psDefn->GCS, NULL, &(psDefn->Datum), &(psDefn->PM), + &(psDefn->UOMAngle) ); + } + +/* -------------------------------------------------------------------- */ +/* Handle the GCS angular units. GeogAngularUnitsGeoKey */ +/* overrides the GCS or PCS setting. */ +/* -------------------------------------------------------------------- */ + GetMetadataElement( "GEOTIFF_NUM::2054::GeogAngularUnitsGeoKey", + &(psDefn->UOMAngle) ); + if( psDefn->UOMAngle != KvUserDefined ) + { + GTIFGetUOMAngleInfo( psDefn->UOMAngle, NULL, + &(psDefn->UOMAngleInDegrees) ); + } + +/* -------------------------------------------------------------------- */ +/* Check for a datum setting, and then use the datum to derive */ +/* an ellipsoid. */ +/* -------------------------------------------------------------------- */ + GetMetadataElement( "GEOTIFF_NUM::2050::GeogGeodeticDatumGeoKey", + &(psDefn->Datum) ); + + if( psDefn->Datum != KvUserDefined ) + { + GTIFGetDatumInfo( psDefn->Datum, NULL, &(psDefn->Ellipsoid) ); + } + +/* -------------------------------------------------------------------- */ +/* Check for an explicit ellipsoid. Use the ellipsoid to */ +/* derive the ellipsoid characteristics, if possible. */ +/* -------------------------------------------------------------------- */ + GetMetadataElement( "GEOTIFF_NUM::2056::GeogEllipsoidGeoKey", + &(psDefn->Ellipsoid) ); + + if( psDefn->Ellipsoid != KvUserDefined ) + { + GTIFGetEllipsoidInfo( psDefn->Ellipsoid, NULL, + &(psDefn->SemiMajor), &(psDefn->SemiMinor) ); + } + +/* -------------------------------------------------------------------- */ +/* Check for overridden ellipsoid parameters. It would be nice */ +/* to warn if they conflict with provided information, but for */ +/* now we just override. */ +/* -------------------------------------------------------------------- */ + GetMetadataElement( "GEOTIFF_NUM::2057::GeogSemiMajorAxisGeoKey", + &(psDefn->SemiMajor) ); + GetMetadataElement( "GEOTIFF_NUM::2058::GeogSemiMinorAxisGeoKey", + &(psDefn->SemiMinor) ); + + if( GetMetadataElement( "GEOTIFF_NUM::2059::GeogInvFlatteningGeoKey", + &dfInvFlattening ) == 1 ) + { + if( dfInvFlattening != 0.0 ) + psDefn->SemiMinor = OSRCalcSemiMinorFromInvFlattening(psDefn->SemiMajor, dfInvFlattening); + } + +/* -------------------------------------------------------------------- */ +/* Get the prime meridian info. */ +/* -------------------------------------------------------------------- */ + GetMetadataElement( "GEOTIFF_NUM::2051::GeogPrimeMeridianGeoKey", + &(psDefn->PM) ); + + if( psDefn->PM != KvUserDefined ) + { + GTIFGetPMInfo( psDefn->PM, NULL, &(psDefn->PMLongToGreenwich) ); + } + else + { + GetMetadataElement( "GEOTIFF_NUM::2061::GeogPrimeMeridianLongGeoKey", + &(psDefn->PMLongToGreenwich) ); + + psDefn->PMLongToGreenwich = + GTIFAngleToDD( psDefn->PMLongToGreenwich, + psDefn->UOMAngle ); + } + +/* -------------------------------------------------------------------- */ +/* Have the projection units of measure been overridden? We */ +/* should likely be doing something about angular units too, */ +/* but these are very rarely not decimal degrees for actual */ +/* file coordinates. */ +/* -------------------------------------------------------------------- */ + GetMetadataElement( "GEOTIFF_NUM::3076::ProjLinearUnitsGeoKey", + &(psDefn->UOMLength) ); + + if( psDefn->UOMLength != KvUserDefined ) + { + GTIFGetUOMLengthInfo( psDefn->UOMLength, NULL, + &(psDefn->UOMLengthInMeters) ); + } + +/* -------------------------------------------------------------------- */ +/* Handle a variety of user defined transform types. */ +/* -------------------------------------------------------------------- */ + if( GetMetadataElement( "GEOTIFF_NUM::3075::ProjCoordTransGeoKey", + &(psDefn->CTProjection) ) ) + { + FetchProjParms(); + } + +/* -------------------------------------------------------------------- */ +/* Try to set the zoned map system information. */ +/* -------------------------------------------------------------------- */ + psDefn->MapSys = GTIFProjToMapSys( psDefn->ProjCode, &(psDefn->Zone) ); + +/* -------------------------------------------------------------------- */ +/* If this is UTM, and we were unable to extract the projection */ +/* parameters from the CSV file, just set them directly now, */ +/* since it's pretty easy, and a common case. */ +/* -------------------------------------------------------------------- */ + if( (psDefn->MapSys == MapSys_UTM_North + || psDefn->MapSys == MapSys_UTM_South) + && psDefn->CTProjection == KvUserDefined ) + { + psDefn->CTProjection = CT_TransverseMercator; + psDefn->nParms = 7; + psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey; + psDefn->ProjParm[0] = 0.0; + + psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey; + psDefn->ProjParm[1] = psDefn->Zone*6 - 183.0; + + psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey; + psDefn->ProjParm[4] = 0.9996; + + psDefn->ProjParmId[5] = ProjFalseEastingGeoKey; + psDefn->ProjParm[5] = 500000.0; + + psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey; + + if( psDefn->MapSys == MapSys_UTM_North ) + psDefn->ProjParm[6] = 0.0; + else + psDefn->ProjParm[6] = 10000000.0; + } + + if ( pszProjection ) + CPLFree( pszProjection ); + pszProjection = GetOGISDefn( psDefn ); +} + + +/************************************************************************/ +/* GTIFToCPLRecyleString() */ +/* */ +/* This changes a string from the libgeotiff heap to the GDAL */ +/* heap. */ +/************************************************************************/ + +static void GTIFToCPLRecycleString( char **ppszTarget ) + +{ + if( *ppszTarget == NULL ) + return; + + char *pszTempString = CPLStrdup(*ppszTarget); + GTIFFreeMemory( *ppszTarget ); + *ppszTarget = pszTempString; +} + +/************************************************************************/ +/* GetOGISDefn() */ +/* Copied from the gt_wkt_srs.cpp. */ +/************************************************************************/ + +char *MrSIDDataset::GetOGISDefn( GTIFDefn *psDefn ) +{ + OGRSpatialReference oSRS; + + if( psDefn->Model != ModelTypeProjected + && psDefn->Model != ModelTypeGeographic ) + return CPLStrdup(""); + +/* -------------------------------------------------------------------- */ +/* If this is a projected SRS we set the PROJCS keyword first */ +/* to ensure that the GEOGCS will be a child. */ +/* -------------------------------------------------------------------- */ + if( psDefn->Model == ModelTypeProjected ) + { + char *pszPCSName; + int bPCSNameSet = FALSE; + + if( psDefn->PCS != KvUserDefined ) + { + + if( GTIFGetPCSInfo( psDefn->PCS, &pszPCSName, NULL, NULL, NULL ) ) + bPCSNameSet = TRUE; + + oSRS.SetNode( "PROJCS", bPCSNameSet ? pszPCSName : "unnamed" ); + if( bPCSNameSet ) + GTIFFreeMemory( pszPCSName ); + + oSRS.SetAuthority( "PROJCS", "EPSG", psDefn->PCS ); + } + else + { + char szPCSName[200]; + strcpy( szPCSName, "unnamed" ); + if ( GetMetadataElement( "GEOTIFF_NUM::1026::GTCitationGeoKey", + szPCSName, sizeof(szPCSName) ) ) + oSRS.SetNode( "PROJCS", szPCSName ); + } + } + +/* ==================================================================== */ +/* Setup the GeogCS */ +/* ==================================================================== */ + char *pszGeogName = NULL; + char *pszDatumName = NULL; + char *pszPMName = NULL; + char *pszSpheroidName = NULL; + char *pszAngularUnits = NULL; + double dfInvFlattening, dfSemiMajor; + char szGCSName[200]; + + if( GetMetadataElement( "GEOTIFF_NUM::2049::GeogCitationGeoKey", + szGCSName, sizeof(szGCSName) ) ) + pszGeogName = CPLStrdup(szGCSName); + else + { + GTIFGetGCSInfo( psDefn->GCS, &pszGeogName, NULL, NULL, NULL ); + GTIFToCPLRecycleString(&pszGeogName); + } + GTIFGetDatumInfo( psDefn->Datum, &pszDatumName, NULL ); + GTIFToCPLRecycleString(&pszDatumName); + GTIFGetPMInfo( psDefn->PM, &pszPMName, NULL ); + GTIFToCPLRecycleString(&pszPMName); + GTIFGetEllipsoidInfo( psDefn->Ellipsoid, &pszSpheroidName, NULL, NULL ); + GTIFToCPLRecycleString(&pszSpheroidName); + + GTIFGetUOMAngleInfo( psDefn->UOMAngle, &pszAngularUnits, NULL ); + GTIFToCPLRecycleString(&pszAngularUnits); + if( pszAngularUnits == NULL ) + pszAngularUnits = CPLStrdup("unknown"); + + if( pszDatumName != NULL ) + WKTMassageDatum( &pszDatumName ); + + dfSemiMajor = psDefn->SemiMajor; + if( psDefn->SemiMajor == 0.0 ) + { + pszSpheroidName = CPLStrdup("unretrievable - using WGS84"); + dfSemiMajor = SRS_WGS84_SEMIMAJOR; + dfInvFlattening = SRS_WGS84_INVFLATTENING; + } + else + dfInvFlattening = OSRCalcInvFlattening(psDefn->SemiMajor,psDefn->SemiMinor); + + oSRS.SetGeogCS( pszGeogName, pszDatumName, + pszSpheroidName, dfSemiMajor, dfInvFlattening, + pszPMName, + psDefn->PMLongToGreenwich / psDefn->UOMAngleInDegrees, + pszAngularUnits, + psDefn->UOMAngleInDegrees * 0.0174532925199433 ); + + if( psDefn->GCS != KvUserDefined ) + oSRS.SetAuthority( "GEOGCS", "EPSG", psDefn->GCS ); + + if( psDefn->Datum != KvUserDefined ) + oSRS.SetAuthority( "DATUM", "EPSG", psDefn->Datum ); + + if( psDefn->Ellipsoid != KvUserDefined ) + oSRS.SetAuthority( "SPHEROID", "EPSG", psDefn->Ellipsoid ); + + CPLFree( pszGeogName ); + CPLFree( pszDatumName ); + CPLFree( pszPMName ); + CPLFree( pszSpheroidName ); + CPLFree( pszAngularUnits ); + +/* ==================================================================== */ +/* Handle projection parameters. */ +/* ==================================================================== */ + if( psDefn->Model == ModelTypeProjected ) + { +/* -------------------------------------------------------------------- */ +/* Make a local copy of parms, and convert back into the */ +/* angular units of the GEOGCS and the linear units of the */ +/* projection. */ +/* -------------------------------------------------------------------- */ + double adfParm[10]; + int i; + + for( i = 0; i < MIN(10,psDefn->nParms); i++ ) + adfParm[i] = psDefn->ProjParm[i]; + + adfParm[0] /= psDefn->UOMAngleInDegrees; + adfParm[1] /= psDefn->UOMAngleInDegrees; + adfParm[2] /= psDefn->UOMAngleInDegrees; + adfParm[3] /= psDefn->UOMAngleInDegrees; + + adfParm[5] /= psDefn->UOMLengthInMeters; + adfParm[6] /= psDefn->UOMLengthInMeters; + +/* -------------------------------------------------------------------- */ +/* Translation the fundamental projection. */ +/* -------------------------------------------------------------------- */ + switch( psDefn->CTProjection ) + { + case CT_TransverseMercator: + oSRS.SetTM( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_TransvMercator_SouthOriented: + oSRS.SetTMSO( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_Mercator: + oSRS.SetMercator( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_ObliqueStereographic: + oSRS.SetOS( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_Stereographic: + oSRS.SetOS( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_ObliqueMercator: /* hotine */ + oSRS.SetHOM( adfParm[0], adfParm[1], + adfParm[2], adfParm[3], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_EquidistantConic: + oSRS.SetEC( adfParm[0], adfParm[1], + adfParm[2], adfParm[3], + adfParm[5], adfParm[6] ); + break; + + case CT_CassiniSoldner: + oSRS.SetCS( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_Polyconic: + oSRS.SetPolyconic( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_AzimuthalEquidistant: + oSRS.SetAE( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_MillerCylindrical: + oSRS.SetMC( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_Equirectangular: + oSRS.SetEquirectangular( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_Gnomonic: + oSRS.SetGnomonic( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_LambertAzimEqualArea: + oSRS.SetLAEA( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_Orthographic: + oSRS.SetOrthographic( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_Robinson: + oSRS.SetRobinson( adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_Sinusoidal: + oSRS.SetSinusoidal( adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_VanDerGrinten: + oSRS.SetVDG( adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_PolarStereographic: + oSRS.SetPS( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_LambertConfConic_2SP: + oSRS.SetLCC( adfParm[2], adfParm[3], + adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + + case CT_LambertConfConic_1SP: + oSRS.SetLCC1SP( adfParm[0], adfParm[1], + adfParm[4], + adfParm[5], adfParm[6] ); + break; + + case CT_AlbersEqualArea: + oSRS.SetACEA( adfParm[0], adfParm[1], + adfParm[2], adfParm[3], + adfParm[5], adfParm[6] ); + break; + + case CT_NewZealandMapGrid: + oSRS.SetNZMG( adfParm[0], adfParm[1], + adfParm[5], adfParm[6] ); + break; + } + +/* -------------------------------------------------------------------- */ +/* Set projection units. */ +/* -------------------------------------------------------------------- */ + char *pszUnitsName = NULL; + + GTIFGetUOMLengthInfo( psDefn->UOMLength, &pszUnitsName, NULL ); + + if( pszUnitsName != NULL && psDefn->UOMLength != KvUserDefined ) + { + oSRS.SetLinearUnits( pszUnitsName, psDefn->UOMLengthInMeters ); + oSRS.SetAuthority( "PROJCS|UNIT", "EPSG", psDefn->UOMLength ); + } + else + oSRS.SetLinearUnits( "unknown", psDefn->UOMLengthInMeters ); + + GTIFFreeMemory( pszUnitsName ); + } + +/* -------------------------------------------------------------------- */ +/* Return the WKT serialization of the object. */ +/* -------------------------------------------------------------------- */ + char *pszWKT; + + oSRS.FixupOrdering(); + + if( oSRS.exportToWkt( &pszWKT ) == OGRERR_NONE ) + return pszWKT; + else + return NULL; +} + +#ifdef MRSID_ESDK + +/************************************************************************/ +/* ==================================================================== */ +/* MrSIDDummyImageReader */ +/* */ +/* This is a helper class to wrap GDAL calls in MrSID interface. */ +/* ==================================================================== */ +/************************************************************************/ + +class MrSIDDummyImageReader : public LTIImageReader +{ + public: + + MrSIDDummyImageReader( GDALDataset *poSrcDS ); + ~MrSIDDummyImageReader(); + LT_STATUS initialize(); + lt_int64 getPhysicalFileSize(void) const { return 0; }; + + private: + GDALDataset *poDS; + GDALDataType eDataType; + LTIDataType eSampleType; + const LTIPixel *poPixel; + + double adfGeoTransform[6]; + + virtual LT_STATUS decodeStrip( LTISceneBuffer& stripBuffer, + const LTIScene& stripScene ); + virtual LT_STATUS decodeBegin( const LTIScene& ) + { return LT_STS_Success; }; + virtual LT_STATUS decodeEnd() { return LT_STS_Success; }; +}; + +/************************************************************************/ +/* MrSIDDummyImageReader() */ +/************************************************************************/ + +MrSIDDummyImageReader::MrSIDDummyImageReader( GDALDataset *poSrcDS ) : + LTIImageReader(), poDS(poSrcDS) +{ + poPixel = NULL; +} + +/************************************************************************/ +/* ~MrSIDDummyImageReader() */ +/************************************************************************/ + +MrSIDDummyImageReader::~MrSIDDummyImageReader() +{ + if ( poPixel ) + delete poPixel; +} + +/************************************************************************/ +/* initialize() */ +/************************************************************************/ + +LT_STATUS MrSIDDummyImageReader::initialize() +{ + LT_STATUS eStat = LT_STS_Uninit; +#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 6 + if ( !LT_SUCCESS(eStat = LTIImageReader::init()) ) + return eStat; +#else + if ( !LT_SUCCESS(eStat = LTIImageReader::initialize()) ) + return eStat; +#endif + + lt_uint16 nBands = (lt_uint16)poDS->GetRasterCount(); + LTIColorSpace eColorSpace = LTI_COLORSPACE_RGB; + switch ( nBands ) + { + case 1: + eColorSpace = LTI_COLORSPACE_GRAYSCALE; + break; + case 3: + eColorSpace = LTI_COLORSPACE_RGB; + break; + default: + eColorSpace = LTI_COLORSPACE_MULTISPECTRAL; + break; + } + + eDataType = poDS->GetRasterBand(1)->GetRasterDataType(); + switch ( eDataType ) + { + case GDT_UInt16: + eSampleType = LTI_DATATYPE_UINT16; + break; + case GDT_Int16: + eSampleType = LTI_DATATYPE_SINT16; + break; + case GDT_UInt32: + eSampleType = LTI_DATATYPE_UINT32; + break; + case GDT_Int32: + eSampleType = LTI_DATATYPE_SINT32; + break; + case GDT_Float32: + eSampleType = LTI_DATATYPE_FLOAT32; + break; + case GDT_Float64: + eSampleType = LTI_DATATYPE_FLOAT64; + break; + case GDT_Byte: + default: + eSampleType = LTI_DATATYPE_UINT8; + break; + } + + poPixel = new LTIDLLPixel( eColorSpace, nBands, eSampleType ); + if ( !LT_SUCCESS(setPixelProps(*poPixel)) ) + return LT_STS_Failure; + + if ( !LT_SUCCESS(setDimensions(poDS->GetRasterXSize(), + poDS->GetRasterYSize())) ) + return LT_STS_Failure; + + if ( poDS->GetGeoTransform( adfGeoTransform ) == CE_None ) + { +#ifdef MRSID_SDK_40 + LTIGeoCoord oGeo( adfGeoTransform[0] + adfGeoTransform[1] / 2, + adfGeoTransform[3] + adfGeoTransform[5] / 2, + adfGeoTransform[1], adfGeoTransform[5], + adfGeoTransform[2], adfGeoTransform[4], NULL, + poDS->GetProjectionRef() ); +#else + LTIGeoCoord oGeo( adfGeoTransform[0] + adfGeoTransform[1] / 2, + adfGeoTransform[3] + adfGeoTransform[5] / 2, + adfGeoTransform[1], adfGeoTransform[5], + adfGeoTransform[2], adfGeoTransform[4], + poDS->GetProjectionRef() ); +#endif + if ( !LT_SUCCESS(setGeoCoord( oGeo )) ) + return LT_STS_Failure; + } + + /*int bSuccess; + double dfNoDataValue = poDS->GetNoDataValue( &bSuccess ); + if ( bSuccess ) + { + LTIPixel oNoDataPixel( *poPixel ); + lt_uint16 iBand; + + for (iBand = 0; iBand < (lt_uint16)poDS->GetRasterCount(); iBand++) + oNoDataPixel.setSampleValueFloat32( iBand, dfNoDataValue ); + if ( !LT_SUCCESS(setNoDataPixel( &oNoDataPixel )) ) + return LT_STS_Failure; + }*/ + + setDefaultDynamicRange(); +#if !defined(LTI_SDK_MAJOR) || LTI_SDK_MAJOR < 8 + setClassicalMetadata(); +#endif + + return LT_STS_Success; +} + +/************************************************************************/ +/* decodeStrip() */ +/************************************************************************/ + +LT_STATUS MrSIDDummyImageReader::decodeStrip(LTISceneBuffer& stripData, + const LTIScene& stripScene) + +{ + const lt_int32 nXOff = stripScene.getUpperLeftCol(); + const lt_int32 nYOff = stripScene.getUpperLeftRow(); + const lt_int32 nBufXSize = stripScene.getNumCols(); + const lt_int32 nBufYSize = stripScene.getNumRows(); + const lt_int32 nDataBufXSize = stripData.getTotalNumCols(); + const lt_int32 nDataBufYSize = stripData.getTotalNumRows(); + const lt_uint16 nBands = poPixel->getNumBands(); + + void *pData = CPLMalloc(nDataBufXSize * nDataBufYSize * poPixel->getNumBytes()); + if ( !pData ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDDummyImageReader::decodeStrip(): " + "Cannot allocate enough space for scene buffer" ); + return LT_STS_Failure; + } + + poDS->RasterIO( GF_Read, nXOff, nYOff, nBufXSize, nBufYSize, + pData, nBufXSize, nBufYSize, eDataType, nBands, NULL, + 0, 0, 0, NULL ); + + stripData.importDataBSQ( pData ); + CPLFree( pData ); + return LT_STS_Success; +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void MrSIDDataset::FlushCache() + +{ + GDALDataset::FlushCache(); +} + +/************************************************************************/ +/* MrSIDCreateCopy() */ +/************************************************************************/ + +static GDALDataset * +MrSIDCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + const char* pszVersion = CSLFetchNameValue(papszOptions, "VERSION"); +#ifdef MRSID_HAVE_MG4WRITE + int iVersion = pszVersion ? atoi(pszVersion) : 4; +#else + int iVersion = pszVersion ? atoi(pszVersion) : 3; +#endif + LT_STATUS eStat = LT_STS_Uninit; + +#ifdef DEBUG + bool bMeter = false; +#else + bool bMeter = true; +#endif + + if (poSrcDS->GetRasterBand(1)->GetColorTable() != NULL) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "MrSID driver ignores color table. " + "The source raster band will be considered as grey level.\n" + "Consider using color table expansion (-expand option in gdal_translate)\n"); + if (bStrict) + return NULL; + } + + MrSIDProgress oProgressDelegate(pfnProgress, pProgressData); + if( LT_FAILURE( eStat = oProgressDelegate.setProgressStatus(0) ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDProgress.setProgressStatus failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } + + // Create the file. + MrSIDDummyImageReader oImageReader( poSrcDS ); + if( LT_FAILURE( eStat = oImageReader.initialize() ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDDummyImageReader.Initialize failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } + + LTIGeoFileImageWriter *poImageWriter = NULL; + switch (iVersion) + { + case 2: { + // Output Mrsid Version 2 file. +#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 8 + LTIDLLDefault *poMG2ImageWriter; + poMG2ImageWriter = new LTIDLLDefault; + eStat = poMG2ImageWriter->initialize(&oImageReader); +#else + LTIDLLWriter *poMG2ImageWriter; + poMG2ImageWriter = new LTIDLLWriter(&oImageReader); + eStat = poMG2ImageWriter->initialize(); +#endif + if( LT_FAILURE( eStat ) ) + { + delete poMG2ImageWriter; + CPLError( CE_Failure, CPLE_AppDefined, + "MG2ImageWriter.initialize() failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } + +#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 8 + eStat = poMG2ImageWriter->setEncodingApplication("MrSID Driver", + GDALVersionInfo("--version")); + if( LT_FAILURE( eStat ) ) + { + delete poMG2ImageWriter; + CPLError( CE_Failure, CPLE_AppDefined, + "MG2ImageWriter.setEncodingApplication() failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } +#endif + + poMG2ImageWriter->setUsageMeterEnabled(bMeter); + + poMG2ImageWriter->params().setBlockSize(poMG2ImageWriter->params().getBlockSize()); + + // check for compression option + const char* pszValue = CSLFetchNameValue(papszOptions, "COMPRESSION"); + if( pszValue != NULL ) + poMG2ImageWriter->params().setCompressionRatio( (float)CPLAtof(pszValue) ); + + poImageWriter = poMG2ImageWriter; + + break; } + case 3: { + // Output Mrsid Version 3 file. +#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 8 + LTIDLLDefault *poMG3ImageWriter; + poMG3ImageWriter = new LTIDLLDefault; + eStat = poMG3ImageWriter->initialize(&oImageReader); +#else + LTIDLLWriter *poMG3ImageWriter; + poMG3ImageWriter = new LTIDLLWriter(&oImageReader); + eStat = poMG3ImageWriter->initialize(); +#endif + if( LT_FAILURE( eStat ) ) + { + delete poMG3ImageWriter; + CPLError( CE_Failure, CPLE_AppDefined, + "MG3ImageWriter.initialize() failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } + +#if defined(LTI_SDK_MAJOR) && LTI_SDK_MAJOR >= 8 + eStat = poMG3ImageWriter->setEncodingApplication("MrSID Driver", + GDALVersionInfo("--version")); + if( LT_FAILURE( eStat ) ) + { + delete poMG3ImageWriter; + CPLError( CE_Failure, CPLE_AppDefined, + "MG3ImageWriter.setEncodingApplication() failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } +#endif + + // usage meter should only be disabled for debugging + poMG3ImageWriter->setUsageMeterEnabled(bMeter); + +#if !defined(LTI_SDK_MAJOR) || LTI_SDK_MAJOR < 8 + // Set 64-bit Interface for large files. + poMG3ImageWriter->setFileStream64(true); +#endif + + // set 2 pass optimizer option + if( CSLFetchNameValue(papszOptions, "TWOPASS") != NULL ) + poMG3ImageWriter->params().setTwoPassOptimizer( true ); + + // set filesize in KB + const char* pszValue = CSLFetchNameValue(papszOptions, "FILESIZE"); + if( pszValue != NULL ) + poMG3ImageWriter->params().setTargetFilesize( atoi(pszValue) ); + + poImageWriter = poMG3ImageWriter; + + break; } +#ifdef MRSID_HAVE_MG4WRITE + case 4: { + // Output Mrsid Version 4 file. + LTIDLLDefault *poMG4ImageWriter; + poMG4ImageWriter = new LTIDLLDefault; + eStat = poMG4ImageWriter->initialize(&oImageReader, NULL, NULL); + if( LT_FAILURE( eStat ) ) + { + delete poMG4ImageWriter; + CPLError( CE_Failure, CPLE_AppDefined, + "MG3ImageWriter.initialize() failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } + + eStat = poMG4ImageWriter->setEncodingApplication("MrSID Driver", + GDALVersionInfo("--version")); + if( LT_FAILURE( eStat ) ) + { + delete poMG4ImageWriter; + CPLError( CE_Failure, CPLE_AppDefined, + "MG3ImageWriter.setEncodingApplication() failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } + + // usage meter should only be disabled for debugging + poMG4ImageWriter->setUsageMeterEnabled(bMeter); + + // set 2 pass optimizer option + if( CSLFetchNameValue(papszOptions, "TWOPASS") != NULL ) + poMG4ImageWriter->params().setTwoPassOptimizer( true ); + + // set filesize in KB + const char* pszValue = CSLFetchNameValue(papszOptions, "FILESIZE"); + if( pszValue != NULL ) + poMG4ImageWriter->params().setTargetFilesize( atoi(pszValue) ); + + poImageWriter = poMG4ImageWriter; + + break; } +#endif /* MRSID_HAVE_MG4WRITE */ + default: + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid MrSID generation specified (VERSION=%s).", + pszVersion ); + return NULL; + } + + // set output filename + poImageWriter->setOutputFileSpec( pszFilename ); + + // set progress delegate + poImageWriter->setProgressDelegate(&oProgressDelegate); + + // set defaults + poImageWriter->setStripHeight(poImageWriter->getStripHeight()); + + // set MrSID world file + if( CSLFetchNameValue(papszOptions, "WORLDFILE") != NULL ) + poImageWriter->setWorldFileSupport( true ); + + // write the scene + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + const LTIScene oScene( 0, 0, nXSize, nYSize, 1.0 ); + if( LT_FAILURE( eStat = poImageWriter->write( oScene ) ) ) + { + delete poImageWriter; + CPLError( CE_Failure, CPLE_AppDefined, + "MG2ImageWriter.write() failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } + + delete poImageWriter; +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + GDALPamDataset *poDS = (GDALPamDataset *) + GDALOpen( pszFilename, GA_ReadOnly ); + + if( poDS ) + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} + +#ifdef MRSID_J2K +/************************************************************************/ +/* JP2CreateCopy() */ +/************************************************************************/ + +static GDALDataset * +JP2CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ +#ifdef DEBUG + bool bMeter = false; +#else + bool bMeter = true; +#endif + + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + LT_STATUS eStat; + + if (poSrcDS->GetRasterBand(1)->GetColorTable() != NULL) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "MrSID driver ignores color table. " + "The source raster band will be considered as grey level.\n" + "Consider using color table expansion (-expand option in gdal_translate)\n"); + if (bStrict) + return NULL; + } + + MrSIDProgress oProgressDelegate(pfnProgress, pProgressData); + if( LT_FAILURE( eStat = oProgressDelegate.setProgressStatus(0) ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDProgress.setProgressStatus failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } + + // Create the file. + MrSIDDummyImageReader oImageReader( poSrcDS ); + eStat = oImageReader.initialize(); + if( eStat != LT_STS_Success ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MrSIDDummyImageReader.Initialize failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } + +#if !defined(MRSID_POST5) + J2KImageWriter oImageWriter(&oImageReader); + eStat = oImageWriter.initialize(); +#elif !defined(LTI_SDK_MAJOR) || LTI_SDK_MAJOR < 8 + JP2WriterManager oImageWriter(&oImageReader); + eStat = oImageWriter.initialize(); +#else + JP2WriterManager oImageWriter; + eStat = oImageWriter.initialize(&oImageReader); +#endif + if( eStat != LT_STS_Success ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "J2KImageWriter.Initialize failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } + +#if !defined(LTI_SDK_MAJOR) || LTI_SDK_MAJOR < 8 + // Set 64-bit Interface for large files. + oImageWriter.setFileStream64(true); +#endif + + oImageWriter.setUsageMeterEnabled(bMeter); + + // set output filename + oImageWriter.setOutputFileSpec( pszFilename ); + + // set progress delegate + oImageWriter.setProgressDelegate(&oProgressDelegate); + + // Set defaults + //oImageWriter.setStripHeight(oImageWriter.getStripHeight()); + + // set MrSID world file + if( CSLFetchNameValue(papszOptions, "WORLDFILE") != NULL ) + oImageWriter.setWorldFileSupport( true ); + + // check for compression option + const char* pszValue = CSLFetchNameValue(papszOptions, "COMPRESSION"); + if( pszValue != NULL ) + oImageWriter.params().setCompressionRatio( (float)CPLAtof(pszValue) ); + + pszValue = CSLFetchNameValue(papszOptions, "XMLPROFILE"); + if( pszValue != NULL ) + { + LTFileSpec xmlprofile(pszValue); + eStat = oImageWriter.params().readProfile(xmlprofile); + if( eStat != LT_STS_Success ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JPCWriterParams.readProfile failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } + } + + // write the scene + const LTIScene oScene( 0, 0, nXSize, nYSize, 1.0 ); + eStat = oImageWriter.write( oScene ); + if( eStat != LT_STS_Success ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "J2KImageWriter.write() failed.\n%s", + getLastStatusString( eStat ) ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); + GDALPamDataset *poDS = (GDALPamDataset*) JP2Open(&oOpenInfo); + + if( poDS ) + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} +#endif /* MRSID_J2K */ +#endif /* MRSID_ESDK */ + +/************************************************************************/ +/* GDALRegister_MrSID() */ +/************************************************************************/ + +void GDALRegister_MrSID() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("MrSID driver")) + return; + +/* -------------------------------------------------------------------- */ +/* MrSID driver. */ +/* -------------------------------------------------------------------- */ + if( GDALGetDriverByName( "MrSID" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "MrSID" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Multi-resolution Seamless Image Database (MrSID)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_mrsid.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "sid" ); + +#ifdef MRSID_ESDK + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 Float32 Float64" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +// Version 2 Options +" " ); + + poDriver->pfnCreateCopy = MrSIDCreateCopy; + +#else + /* In read-only mode, we support VirtualIO. I don't think this is the case */ + /* for MrSIDCreateCopy() */ + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); +#endif + poDriver->pfnIdentify = MrSIDIdentify; + poDriver->pfnOpen = MrSIDOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } + +/* -------------------------------------------------------------------- */ +/* JP2MRSID driver. */ +/* -------------------------------------------------------------------- */ +#ifdef MRSID_J2K + if( GDALGetDriverByName( "JP2MrSID" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "JP2MrSID" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "MrSID JPEG2000" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_jp2mrsid.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jp2" ); + +#ifdef MRSID_ESDK + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); + + poDriver->pfnCreateCopy = JP2CreateCopy; +#else + /* In read-only mode, we support VirtualIO. I don't think this is the case */ + /* for JP2CreateCopy() */ + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); +#endif + poDriver->pfnIdentify = JP2Identify; + poDriver->pfnOpen = JP2Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +#endif /* def MRSID_J2K */ +} + +#if defined(MRSID_USE_TIFFSYMS_WORKAROUND) +extern "C" { + +/* This is not pretty but I am not sure how else to get the plugin to build + * against the ESDK. ESDK symbol dependencies bring in __TIFFmemcpy and + * __gtiff_size, which are not exported from gdal.dll. Rather than link these + * symbols from the ESDK distribution of GDAL, or link in the entire gdal.lib + * statically, it seemed safer and smaller to bring in just the objects that + * wouldsatisfy these symbols from the enclosing GDAL build. However, doing + * so pulls in a few more dependencies. /Gy and /OPT:REF did not seem to help + * things, so I have implemented no-op versions of these symbols since they + * do not actually get called. If the MrSID ESDK ever comes to require the + * actual versions of these functions, we'll hope duplicate symbol errors will + * bring attention back to this problem. + */ +void TIFFClientOpen() {} +void TIFFError() {} +void TIFFGetField() {} +void TIFFSetField() {} + +} +#endif diff --git a/bazaar/plugin/gdal/frmts/mrsid/mrsidstream.cpp b/bazaar/plugin/gdal/frmts/mrsid/mrsidstream.cpp new file mode 100644 index 000000000..2915d5944 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid/mrsidstream.cpp @@ -0,0 +1,279 @@ +/****************************************************************************** + * $Id: mrsidstream.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: Multi-resolution Seamless Image Database (MrSID) + * Purpose: Input/output stream wrapper for usage with LizardTech's + * MrSID SDK, implemenattion of the wrapper class methods. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2008, Andrey Kiselev + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_error.h" +#include "mrsidstream.h" + +CPL_CVSID("$Id: mrsidstream.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +LT_USE_NAMESPACE(LizardTech) + +/************************************************************************/ +/* ==================================================================== */ +/* LTIVSIStream */ +/* ==================================================================== */ +/************************************************************************/ + +LTIVSIStream::LTIVSIStream() : poFileHandle(NULL), nError(0), pnRefCount(NULL), +bIsOpen(FALSE) +{ +} + +/************************************************************************/ +/* ~LTIVSIStream() */ +/************************************************************************/ + +LTIVSIStream::~LTIVSIStream() +{ + if ( poFileHandle) + { + (*pnRefCount)--; + if (*pnRefCount == 0) + { + VSIFCloseL( (VSILFILE *)poFileHandle ); + nError = errno; + delete pnRefCount; + } + } +} + +/************************************************************************/ +/* initialize() */ +/************************************************************************/ + +LT_STATUS LTIVSIStream::initialize( const char *pszFilename, + const char *pszAccess ) +{ + CPLAssert(poFileHandle == NULL); + + errno = 0; + poFileHandle = (VSIVirtualHandle *)VSIFOpenL( pszFilename, pszAccess ); + if (poFileHandle) + { + pnRefCount = new int; + *pnRefCount = 1; + } + nError = errno; + + return poFileHandle ? LT_STS_Success : LT_STS_Failure; +} + +/************************************************************************/ +/* initialize() */ +/************************************************************************/ + +LT_STATUS LTIVSIStream::initialize( LTIVSIStream* ltiVSIStream ) +{ + CPLAssert(poFileHandle == NULL); + + poFileHandle = ltiVSIStream->poFileHandle; + if (poFileHandle) + { + pnRefCount = ltiVSIStream->pnRefCount; + (*pnRefCount) ++; + } + + return poFileHandle ? LT_STS_Success : LT_STS_Failure; +} + +/************************************************************************/ +/* isEOF() */ +/************************************************************************/ + +bool LTIVSIStream::isEOF() +{ + CPLAssert(poFileHandle); + + errno = 0; + bool bIsEOF = (0 != poFileHandle->Eof()); + nError = errno; + + return bIsEOF; +} + +/************************************************************************/ +/* isOpen() */ +/************************************************************************/ + +bool LTIVSIStream::isOpen() +{ + return poFileHandle != NULL && bIsOpen; +} + +/************************************************************************/ +/* open() */ +/************************************************************************/ + +LT_STATUS LTIVSIStream::open() +{ + bIsOpen = poFileHandle != NULL; + return poFileHandle ? LT_STS_Success : LT_STS_Failure; +} + +/************************************************************************/ +/* close() */ +/************************************************************************/ + +LT_STATUS LTIVSIStream::close() +{ + CPLAssert(poFileHandle); + + bIsOpen = FALSE; + errno = 0; + if ( poFileHandle->Seek( 0, SEEK_SET ) == 0 ) + return LT_STS_Success; + else + { + nError = errno; + return LT_STS_Failure; + } +} + +/************************************************************************/ +/* read() */ +/************************************************************************/ + +lt_uint32 LTIVSIStream::read( lt_uint8 *pDest, lt_uint32 nBytes ) +{ + CPLAssert(poFileHandle); + + errno = 0; + lt_uint32 nBytesRead = + (lt_uint32)poFileHandle->Read( pDest, 1, nBytes ); + nError = errno; + + return nBytesRead; +} + +/************************************************************************/ +/* write() */ +/************************************************************************/ + +lt_uint32 LTIVSIStream::write( const lt_uint8 *pSrc, lt_uint32 nBytes ) +{ + CPLAssert(poFileHandle); + + errno = 0; + lt_uint32 nBytesWritten = + (lt_uint32)poFileHandle->Write( pSrc, 1, nBytes ); + nError = errno; + + return nBytesWritten; +} + +/************************************************************************/ +/* seek() */ +/************************************************************************/ + +LT_STATUS LTIVSIStream::seek( lt_int64 nOffset, LTIOSeekDir nOrigin ) +{ + CPLAssert(poFileHandle); + + int nWhence; + switch (nOrigin) + { + case (LTIO_SEEK_DIR_BEG): + nWhence = SEEK_SET; + break; + + case (LTIO_SEEK_DIR_CUR): + { + nWhence = SEEK_CUR; + if( nOffset < 0 ) + { + nWhence = SEEK_SET; + nOffset += (lt_int64)poFileHandle->Tell(); + } + break; + } + + case (LTIO_SEEK_DIR_END): + nWhence = SEEK_END; + break; + + default: + return LT_STS_Failure; + } + + if ( poFileHandle->Seek( (vsi_l_offset)nOffset, nWhence ) == 0 ) + return LT_STS_Success; + else + { + nError = errno; + return LT_STS_Failure; + } +} + +/************************************************************************/ +/* tell() */ +/************************************************************************/ + +lt_int64 LTIVSIStream::tell() +{ + CPLAssert(poFileHandle); + + errno = 0; + lt_int64 nPos = (lt_int64)poFileHandle->Tell(); + nError = errno; + + return nPos; +} + +/************************************************************************/ +/* duplicate() */ +/************************************************************************/ + +LTIOStreamInf* LTIVSIStream::duplicate() +{ + LTIVSIStream *poNew = new LTIVSIStream; + poNew->initialize( this ); + + return poNew; +} + +/************************************************************************/ +/* getLastError() */ +/************************************************************************/ + +LT_STATUS LTIVSIStream::getLastError() const +{ + return nError; +} + +/************************************************************************/ +/* getID() */ +/************************************************************************/ + +const char *LTIVSIStream::getID() const +{ + return "LTIVSIStream:"; +} + diff --git a/bazaar/plugin/gdal/frmts/mrsid/mrsidstream.h b/bazaar/plugin/gdal/frmts/mrsid/mrsidstream.h new file mode 100644 index 000000000..d9fd91bcb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid/mrsidstream.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * $Id: mrsidstream.h 16078 2009-01-14 05:43:02Z warmerdam $ + * + * Project: Multi-resolution Seamless Image Database (MrSID) + * Purpose: Input/output stream wrapper for usage with LizardTech's + * MrSID SDK, wrapper class declaration. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2008, Andrey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef MRSIDSTREAM_H_INCLUDED +#define MRSIDSTREAM_H_INCLUDED + +#include "lt_base.h" +#include "lt_ioStreamInf.h" +#include "cpl_vsi_virtual.h" + +LT_USE_NAMESPACE(LizardTech) + +class LTIVSIStream : public LTIOStreamInf +{ + public: + LTIVSIStream(); + LT_STATUS initialize( const char *, const char * ); + LT_STATUS initialize( LTIVSIStream* ltiVSIStream ); + ~LTIVSIStream(); + + bool isEOF(); + bool isOpen(); + + LT_STATUS open(); + LT_STATUS close(); + + lt_uint32 read( lt_uint8 *, lt_uint32 ); + lt_uint32 write( const lt_uint8 *, lt_uint32 ); + + LT_STATUS seek( lt_int64, LTIOSeekDir ); + lt_int64 tell(); + + LTIOStreamInf* duplicate(); + + LT_STATUS getLastError() const; + + const char* getID() const; + + private: + VSIVirtualHandle *poFileHandle; + int nError; + int *pnRefCount; + int bIsOpen; +}; + +#endif /* MRSIDSTREAM_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/frmts/mrsid/nmake.opt b/bazaar/plugin/gdal/frmts/mrsid/nmake.opt new file mode 100644 index 000000000..b06c1a31d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid/nmake.opt @@ -0,0 +1,572 @@ +# $Id:$ +# +# nmake.opt - configuration file for mrsid raster and lidar drivers +# +# This file will get automatically included if any of the following macros are +# specified in the root nmake.opt file: +# MRSID_DIR - Should indicate the path to your MrSID SDK. This nmake script +# will guess the type of SDK it is (raster and/or lidar). +# MRSID_RASTER_DIR - Can indicate the path to a MrSID Raster SDK, if lidar +# support needs to be disabled or built against a separate SDK. +# MRSID_LIDAR_DIR - Can indicate the path to a MrSID Lidar SDK, if raster +# support needs to be disabled or built against a separate SDK. +# +# Other optional values that can affect mrsid driver configuration: +# MRSID_JP2=(YES|NO) - Register the MrSID driver to handle JPEG 2000 files. +# By default this option will be disabled. +# MRSID_PLUGIN=(YES|NO) - Build the mrsid raster driver as a plugin. The +# default value is NO. +# MRSID_LIDAR_PLUGIN=(YES|NO) - Build the mrsid lidar driver as a plugin. +# The default value is NO. +# MRSID_RDLLBUILD=(YES|NO) - Some versions of the MrSID SDK shipped with +# both static and dynamic libraries. Use this macro to choose between +# static and dynamic linkage for the Raster SDK, if both are available. +# By default, your SDK will be inspected and the appropriate library +# used, with a preference for the DLL if both are found. +# MRSID_LDLLBUILD=(YES|NO) - Some versions of the MrSID SDK shipped with +# both static and dynamic libraries. Use this macro to choose between +# static and dynamic linkage for the Lidar SDK, if both are available. +# By default, your SDK will be inspected and the appropriate library +# used, with a preference for the DLL if both are found. +# MRSID_CONFIG=(Release|Debug|Release_md|Debug_md) - When linking +# statically, the value of this macro should correspond to the run-time +# library linkage specified in OPTFLAGS, as follows: +# Release=/MT, Debug=/MTd, Release_md=/MD (default), Debug_md=/MDd +# MRSID_RVER=000 - Indicate the MrSID Raster SDK version you expect. This +# value will be validated against the actual SDK you are building to. By +# default the auto-detected version will be assumed correct. +# MRSID_LVER=000 - Indicate the MrSID Lidar SDK version you expect. This +# value will be validated against the actual SDK you are building to. By +# default the auto-detected version will be assumed correct. +# MRSID_ESDK=(YES|NO) - Include write support for MrSID raster files. By +# default this option will be enabled if you have a MrSID Raster ESDK. +# MRSID_SHOW_CONFIG=(YES|NO) - Display a report of all MRSID_* macro values +# after this script has run. +# +############################################################################### +# +# Initialize some default values. +# +!IFNDEF MRSID_CONFIG +MRSID_CONFIG = Release_md +!ENDIF +!IFNDEF MRSID_JP2 +MRSID_JP2 = NO +!ENDIF + +############################################################################### +# +# If MRSID_DIR was specified, then we will try to guess whether it points to a +# lidar or a raster SDK (or both). By the end of this block, we want to end +# up with independent macros MRSID_RASTER_DIR and/or MRSID_LIDAR_DIR. Thence +# MRSID_DIR will be ignored. +# +!IF DEFINED(MRSID_DIR) + +# Guard against conflicting configuration. +!IF DEFINED(MRSID_RASTER_DIR) && DEFINED(MRSID_LIDAR_DIR) +!ERROR The MRSID_DIR value is redundant or conflicts with the values of MRSID_RASTER_DIR and MRSID_LIDAR_DIR. Please remove one of these values from the build configuration. +!ENDIF + +# If a raster path was not explicitly set, see if MRSID_DIR is one. +!IF !DEFINED(MRSID_RASTER_DIR) +!IF EXIST("$(MRSID_DIR)\Raster_ESDK\include\lt_base.h") +MRSID_RASTER_DIR = $(MRSID_DIR)\Raster_ESDK +!ELSE IF EXIST("$(MRSID_DIR)\Raster_DSDK\include\lt_base.h") +MRSID_RASTER_DIR = $(MRSID_DIR)\Raster_DSDK +!ELSE IF EXIST("$(MRSID_DIR)\include\lt_base.h") || EXIST("$(MRSID_DIR)\include\support\lt_base.h") +MRSID_RASTER_DIR = $(MRSID_DIR) +!ENDIF +!ENDIF + +# If a lidar path was not explicitly set, see if MRSID_DIR is one. +!IF !DEFINED(MRSID_LIDAR_DIR) && DEFINED(MRSID_DIR) +!IF EXIST("$(MRSID_DIR)\Lidar_ESDK\include\lidar\Base.h") +MRSID_LIDAR_DIR = $(MRSID_DIR)\Lidar_ESDK +!ELSE IF EXIST("$(MRSID_DIR)\Lidar_DSDK\include\lidar\Base.h") +MRSID_LIDAR_DIR = $(MRSID_DIR)\Lidar_DSDK +!ELSE IF EXIST("$(MRSID_DIR)\include\lidar\Base.h") +MRSID_LIDAR_DIR = $(MRSID_DIR) +!ENDIF +!ENDIF + +!ENDIF + +# Verify we have at least one kind of SDK. Otherwise, there is nothing to do. +!IF !DEFINED(MRSID_RASTER_DIR) && !DEFINED(MRSID_LIDAR_DIR) +!IF DEFINED(MRSID_DIR) +!ERROR No MrSID SDK was found at $(MRSID_DIR). +!ELSE +!ERROR You did not specify a path to a MrSID SDK. +!ENDIF +!ENDIF + +# Henceforth, we can ignore MRSID_DIR. + + + + +############################################################################### +# +# Configure the MrSID Raster SDK. +# +!IF DEFINED(MRSID_RASTER_DIR) + +# Initialize some macros to help with paths that have evolved over time. +!IF EXIST("$(MRSID_RASTER_DIR)\include\base") +I_BASE = \base +!ENDIF +!IF EXIST("$(MRSID_RASTER_DIR)\include\support") +I_SUPP = \support +!ENDIF +!IF EXIST("$(MRSID_RASTER_DIR)\include\mrsid_readers") +I_SIDR = \mrsid_readers +!ENDIF +!IF EXIST("$(MRSID_RASTER_DIR)\include\j2k_readers") +I_JP2R = \j2k_readers +!ENDIF +!IF EXIST("$(MRSID_RASTER_DIR)\include\mrsid_writers") +I_SIDW = \mrsid_writers +!ENDIF +!IF EXIST("$(MRSID_RASTER_DIR)\include\c_api") +I_CAPI = \c_api +!ENDIF +!IF EXIST("$(MRSID_RASTER_DIR)\include\metadata") +I_META = \metadata +!ENDIF + + +# +# Determine the detailed Raster SDK version. +R800 = "$(MRSID_RASTER_DIR)\include$(I_BASE)\lti_types.h" +R701 = "$(MRSID_RASTER_DIR)\include$(I_NITF)\nitf_types.h" +R700 = "$(MRSID_RASTER_DIR)\include$(I_NONE)\lti_referenceCountedObject.h" +R607 = "$(MRSID_RASTER_DIR)\include$(I_CAPI)\ltic_api.h" +R605 = "$(MRSID_RASTER_DIR)\include$(I_META)\lti_metadataTypes.h" +R604 = "$(MRSID_RASTER_DIR)\include$(I_BASE)\lti_imageStage.h" +R506 = "$(MRSID_RASTER_DIR)\include$(I_CAPI)\ltic_api.h" +R415 = "$(MRSID_RASTER_DIR)\include$(I_BASE)\lti_coreStatus.h" +R412 = "$(MRSID_RASTER_DIR)\include$(I_BASE)\lti_types.h" +R411 = "$(MRSID_RASTER_DIR)\include$(I_BASE)\lti_utils.h" +R410 = "$(MRSID_RASTER_DIR)\include$(I_BASE)\lti_geoImageReader.h" +R409 = "$(MRSID_RASTER_DIR)\include$(I_CAPI)\ltic_api.h" +R408 = "$(MRSID_RASTER_DIR)\include$(I_BASE)\lti_coreStatus.h" +R407 = "$(MRSID_RASTER_DIR)\include$(I_BASE)\lti_types.h" +R406 = "$(MRSID_RASTER_DIR)\include$(I_BASE)\lti_pixelData.h" +R405 = "$(MRSID_RASTER_DIR)\include$(I_META)\lti_metadataStatus.h" +R404 = "$(MRSID_RASTER_DIR)\include$(I_SUPP)\lt_platform.h" +R403 = "$(MRSID_RASTER_DIR)\include$(I_BASE)\lti_coreStatus.h" +R402 = "$(MRSID_RASTER_DIR)\include$(I_BASE)\lti_image.h" +R400 = "$(MRSID_RASTER_DIR)\include$(I_BASE)\lti_image.h" + +!IF [find "LTIEncodingModification" $(R800) > NUL 2>&1] == 0 +MRSID_AUTORVER = 800 +!ELSE IF [find "NITFTRELocation" $(R701) > NUL 2>&1] == 0 +MRSID_AUTORVER = 701 +!ELSE IF EXIST($(R700)) +MRSID_AUTORVER = 700 +!ELSE IF [find "ltic_getDimsAtMag" $(R607) > NUL 2>&1] == 0 +MRSID_AUTORVER = 607 +!ELSE IF [find "ENCODING_APPLICATION" $(R605) > NUL 2>&1] == 0 +MRSID_AUTORVER = 605 +!ELSE IF [find "overrideGeoCoord" $(R604) > NUL 2>&1] == 0 +MRSID_AUTORVER = 604 +!ELSE IF [find "ltic_openNITFImageFile" $(R506) > NUL 2>&1] == 0 +MRSID_AUTORVER = 506 +!ELSE IF [find "AllocFailed" $(R415) > NUL 2>&1] == 0 +MRSID_AUTORVER = 415 +!ELSE IF [find "LTIResampleMethod" $(R412) > NUL 2>&1] == 0 +MRSID_AUTORVER = 412 +!ELSE IF [find "isSigned" $(R411) > NUL 2>&1] == 0 +MRSID_AUTORVER = 411 +!ELSE IF EXIST($(R410)) +MRSID_AUTORVER = 410 +!ELSE IF EXIST($(R409)) +MRSID_AUTORVER = 409 +!ELSE IF [find "BBB" $(R408) > NUL 2>&1] == 0 +MRSID_AUTORVER = 408 +!ELSE IF [find "BadFormatForTag" $(R407) > NUL 2>&1] == 0 +MRSID_AUTORVER = 407 +!ELSE IF EXIST($(R405)) && !EXIST($(R406)) +MRSID_AUTORVER = 406 +!ELSE IF [find "BadFormatForTag" $(R405) > NUL 2>&1] == 0 +MRSID_AUTORVER = 405 +!ELSE IF EXIST($(R404)) && [find "macintosh" $(R404) > NUL 2>&1] > 0 +MRSID_AUTORVER = 404 +!ELSE IF [find "DatatypeMismatch" $(R403) > NUL 2>&1] == 0 +MRSID_AUTORVER = 403 +!ELSE IF [find "getDimsAtMag" $(R402) > NUL 2>&1] == 0 +MRSID_AUTORVER = 402 +!ELSE IF [find "class LTIImage" $(R400) > NUL 2>&1] == 0 +MRSID_AUTORVER = 400 # earliest supported raster SDK (2003) +!ELSE +!ERROR $(MRSID_RASTER_DIR) is not a MrSID Raster SDK. +!ENDIF + + +# +# Check if this might be an encoding SDK. +!IF EXIST("$(MRSID_RASTER_DIR)\include$(I_SIDW)\lti_mrsidWritersStatus.h") +MRSID_AUTOESDK = YES +!ELSE +MRSID_AUTOESDK = NO +!ENDIF + +!IF "$(MRSID_ESDK)" == "YES" && "$(MRSID_AUTOESDK)" != "YES" +!ERROR MrSID Raster SDK found at $(MRSID_RASTER_DIR) is not an ESDK. +!ENDIF +!IF !DEFINED(MRSID_ESDK) +MRSID_ESDK = $(MRSID_AUTOESDK) +!ENDIF + + +# +# Check if this SDK can write MG4. +!IF EXIST("$(MRSID_RASTER_DIR)\include$(I_SIDW)\MG4ImageWriter.h") +MRSID_AUTOMG4W = YES +!ELSE +MRSID_AUTOMG4W = NO +!ENDIF + +!IF "$(MRSID_HAVE_MG4WRITE)" == "YES" && "$(MRSID_AUTOMG4W)" != "YES" +!ERROR MrSID Raster SDK found at $(MRSID_RASTER_DIR) is not an ESDK. +!ENDIF +!IF !DEFINED(MRSID_HAVE_MG4WRITE) +MRSID_HAVE_MG4WRITE = $(MRSID_AUTOMG4W) +!ENDIF + + +# +# Check if this SDK supports JP2. +!IF EXIST("$(MRSID_RASTER_DIR)\include$(I_JP2R)\J2KImageReader.h") || EXIST("$(MRSID_RASTER_DIR)\include$(I_SIDR)\J2KImageReader.h") +MRSID_AUTOJP2 = YES +!ELSE +MRSID_AUTOJP2 = NO +!ENDIF + +!IF "$(MRSID_JP2)" == "YES" && "$(MRSID_AUTOJP2)" != "YES" +!ERROR MrSID Raster SDK found at $(MRSID_RASTER_DIR) does not support JP2. +!ENDIF +!IF !DEFINED(MRSID_JP2) +MRSID_JP2 = $(MRSID_AUTOJP2) +!ENDIF + + +# +# Validate the Raster SDK version if one was indicated. +!IF DEFINED(MRSID_RVER) && "$(MRSID_RVER)" != "$(MRSID_AUTORVER)" +!ERROR MrSID Raster SDK at $(MRSID_RASTER_DIR) is version $(MRSID_AUTORVER), but you specified $(MRSID_RVER). +!ENDIF +MRSID_RVER = $(MRSID_AUTORVER) + + +# +# Guess linkage if it was not set explicitly. +!IF !DEFINED(MRSID_RDLLBUILD) +!IF "$(MRSID_ESDK)" == "YES" || $(MRSID_RVER)0 < 8000 +MRSID_RDLLBUILD = NO +!ELSE +MRSID_RDLLBUILD = YES +!ENDIF +!ENDIF + +!ELSE # MRSID_RASTER_DIR was not defined, so disallow other raster options + +!IF DEFINED(MRSID_RVER) +!ERROR MRSID_RVER specified, but no MrSID Raster SDK was found. +!ENDIF + +!IF "$(MRSID_ESDK)" == "YES" +!ERROR MRSID_ESDK specified, but no MrSID Raster SDK was found. +!ENDIF + +!IF "$(MRSID_JP2)" == "YES" +!ERROR MRSID_JP2 specified, but no MrSID Raster SDK was found. +!ENDIF + +!ENDIF # end configuration of Raster SDK + + + +############################################################################### +# +# Configure the MrSID Lidar SDK. +# +!IF DEFINED(MRSID_LIDAR_DIR) + +# Determine the detailed Lidar SDK version. +L111 = "$(MRSID_LIDAR_DIR)\include\lidar\Types.h" +L110 = "$(MRSID_LIDAR_DIR)\include\lidar\PointData.h" +L101 = "$(MRSID_LIDAR_DIR)\include\lidar\core_status.h" +L100 = "$(MRSID_LIDAR_DIR)\include\lidar\Mutex.h" +L090 = "$(MRSID_LIDAR_DIR)\include\lidar\IO.h" +L060 = "$(MRSID_LIDAR_DIR)\include\lidar\Base.h" + +!IF [find "&Huge(" $(L111) > NUL 2>&1] == 0 +MRSID_AUTOLVER = 111 +!ELSE IF [find "class PointInfo" $(L110) > NUL 2>&1] == 0 +MRSID_AUTOLVER = 110 +!ELSE IF [find "UNSUPPORTED_VERSION" $(L101) > NUL 2>&1] == 0 +MRSID_AUTOLVER = 101 +!ELSE IF EXIST($(L100)) +MRSID_AUTOLVER = 100 +!ELSE IF [find "struct Location" $(L090) > NUL 2>&1] == 0 +MRSID_AUTOLVER = 90 +!ELSE IF [find "LT_END_LIDAR_NAMESPACE" $(L060) > NUL 2>&1] == 0 +MRSID_AUTOLVER = 60 +!ENDIF + +# Validate the Lidar SDK version if one was indicated. +!IF DEFINED(MRSID_LVER) && "$(MRSID_LVER)" != "$(MRSID_AUTOLVER)" +!ERROR MrSID Lidar SDK at $(MRSID_LIDAR_DIR) is version $(MRSID_AUTOLVER), but you specified $(MRSID_LVER). +!ENDIF +MRSID_LVER = $(MRSID_AUTOLVER) + +# Guess linkage if it was not set explicitly. +!IF !DEFINED(MRSID_LDLLBUILD) +!IF $(MRSID_LVER)0 < 1110 +MRSID_LDLLBUILD = NO +!ELSE +MRSID_LDLLBUILD = YES +!ENDIF +!ENDIF + +!ELSE # MRSID_LIDAR_DIR was not defined, so disallow other lidar options + +!IF DEFINED(MRSID_LVER) +!ERROR MRSID_LVER specified, but no MrSID Lidar SDK was found. +!ENDIF + +!ENDIF + + + +############################################################################### +# +# Set up the build environment: MRSID_FLAGS, MRSID_INCLUDE, MRSID_LIB +# + +# +# MRSID_FLAGS +# +!IF DEFINED(MRSID_RASTER_DIR) + +!IF "$(MRSID_JP2)" == "YES" +MRSID_FLAGS = -DMRSID_J2K $(MRSID_FLAGS) +!ENDIF + +!IF "$(MRSID_ESDK)" == "YES" +MRSID_FLAGS = -DMRSID_ESDK $(MRSID_FLAGS) +!IF "$(MRSID_HAVE_MG4WRITE)" == "YES" +MRSID_FLAGS = -DMRSID_HAVE_MG4WRITE $(MRSID_FLAGS) +!ENDIF +!ENDIF + +MRSID_FLAGS = -DMRSID_RVER=$(MRSID_RVER) $(MRSID_FLAGS) + +!ENDIF # end raster flags + +!IF DEFINED(MRSID_LIDAR_DIR) + +MRSID_FLAGS = -DMRSID_LVER=$(MRSID_LVER) $(MRSID_FLAGS) + +!ENDIF # end lidar flags + + +# +# MRSID_INCLUDE +# +!IF DEFINED(MRSID_RASTER_DIR) + +!IF $(MRSID_RVER)0 >= 7000 +MRSID_INCLUDE = -I"$(MRSID_RASTER_DIR)\include" $(MRSID_INCLUDE) +!ENDIF +!IF EXIST("$(MRSID_RASTER_DIR)\include\base") +MRSID_INCLUDE = -I"$(MRSID_RASTER_DIR)\include\base" $(MRSID_INCLUDE) +!ENDIF +#!IF EXIST("$(MRSID_RASTER_DIR)\include\c_api") +#MRSID_INCLUDE = -I"$(MRSID_RASTER_DIR)\include\c_api" $(MRSID_INCLUDE) +#!ENDIF +!IF EXIST("$(MRSID_RASTER_DIR)\include\filters") +MRSID_INCLUDE = -I"$(MRSID_RASTER_DIR)\include\filters" $(MRSID_INCLUDE) +!ENDIF +!IF "$(MRSID_JP2)" == "YES" && EXIST("$(MRSID_RASTER_DIR)\include\j2k_readers") +MRSID_INCLUDE = -I"$(MRSID_RASTER_DIR)\include\j2k_readers" $(MRSID_INCLUDE) +!ENDIF +!IF "$(MRSID_JP2)" == "YES" && "$(MRSID_ESDK)" == "YES" && EXIST("$(MRSID_RASTER_DIR)\include\j2k_writers") +MRSID_INCLUDE = -I"$(MRSID_RASTER_DIR)\include\j2k_writers" $(MRSID_INCLUDE) +!ENDIF +!IF EXIST("$(MRSID_RASTER_DIR)\include\metadata") +MRSID_INCLUDE = -I"$(MRSID_RASTER_DIR)\include\metadata" $(MRSID_INCLUDE) +!ENDIF +!IF EXIST("$(MRSID_RASTER_DIR)\include\mrsid_readers") +MRSID_INCLUDE = -I"$(MRSID_RASTER_DIR)\include\mrsid_readers" $(MRSID_INCLUDE) +!ENDIF +!IF "$(MRSID_ESDK)" == "YES" && EXIST("$(MRSID_RASTER_DIR)\include\mrsid_writers") +MRSID_INCLUDE = -I"$(MRSID_RASTER_DIR)\include\mrsid_writers" $(MRSID_INCLUDE) +!ENDIF +#!IF EXIST("$(MRSID_RASTER_DIR)\include\readers") +#MRSID_INCLUDE = -I"$(MRSID_RASTER_DIR)\include\readers" $(MRSID_INCLUDE) +#!ENDIF +!IF EXIST("$(MRSID_RASTER_DIR)\include\support") +MRSID_INCLUDE = -I"$(MRSID_RASTER_DIR)\include\support" $(MRSID_INCLUDE) +!ENDIF +#!IF EXIST("$(MRSID_RASTER_DIR)\include\writers") +#MRSID_INCLUDE = -I"$(MRSID_RASTER_DIR)\include\writers" $(MRSID_INCLUDE) +#!ENDIF + +!ENDIF # end raster includes + +!IF DEFINED(MRSID_LIDAR_DIR) + +# lidar include paths +MRSID_INCLUDE = -I"$(MRSID_LIDAR_DIR)\include" $(MRSID_INCLUDE) + +!ENDIF # end lidar includes + + +# +# MRSID_LIB +# +!IF DEFINED(MRSID_RASTER_DIR) + +!IF "$(MRSID_RDLLBUILD)" == "YES" + +# +# Find and validate the specific DLL and add it to MRSID_LIB +!IF "$(MRSID_ESDK)" == "YES" +!ERROR Cannot link dynamically against the MrSID Raster ESDK. Try linking statically by setting MRSID_RDLLBUILD to NO. +!ELSE IF EXIST("$(MRSID_RASTER_DIR)\lib\lti_dsdk.dll") +MRSID_LIB = "$(MRSID_RASTER_DIR)\lib\lti_dsdk.lib" $(MRSID_LIB) +!ELSE IF EXIST("$(MRSID_RASTER_DIR)\lib\lti_dsdk_9.0.dll") +MRSID_LIB = "$(MRSID_RASTER_DIR)\lib\lti_dsdk.lib" $(MRSID_LIB) +!ELSE IF EXIST("$(MRSID_RASTER_DIR)\lib\lti_dsdk_9.1.dll") +MRSID_LIB = "$(MRSID_RASTER_DIR)\lib\lti_dsdk.lib" $(MRSID_LIB) +!ELSE IF EXIST("$(MRSID_RASTER_DIR)\lib\$(MRSID_CONFIG)\lti_dsdk_dll.dll") +MRSID_LIB = "$(MRSID_RASTER_DIR)\lib\$(MRSID_CONFIG)\lti_dsdk_dll.lib" $(MRSID_LIB) +!ELSE IF EXIST("$(MRSID_RASTER_DIR)\lib\Release_md\lti_dsdk_dll.dll") +# sdks 5 and older shipped only one configuration of the dll +!ERROR Found a MrSID Raster DLL under $(MRSID_RASTER_DIR)\lib\Release_md, but specified config was $(MRSID_CONFIG). Try setting MRSID_CONFIG to Release_md. +!ELSE IF $(MRSID_RVER)0 < 4090 +# mrsid sdks first had dlls in v4.0.9 +!ERROR This version of the MrSID Raster SDK ($(MRSID_RVER)) did not ship with DLLs. Try linking statically by setting MRSID_RDLLBUILD to NO. +!ELSE +!ERROR No suitable MrSID Raster DLL was found in $(MRSID_RASTER_DIR)\lib. +!ENDIF # end DLL search + +!ELSE # not building against the DLL; use the static lib(s) + +# +# Find and validate the specific DSDK static lib and add it to MRSID_LIB. +!IF $(MRSID_RVER)0 >= 8000 +!IF EXIST("$(MRSID_RASTER_DIR)\lib\$(MRSID_CONFIG)\lti_esdk.lib") +# v8+ esdk has the dsdk symbols folded into a single lib +MRSID_LIB = "$(MRSID_RASTER_DIR)\lib\$(MRSID_CONFIG)\lti_esdk.lib" $(MRSID_LIB) +!ELSE +# v8+ dsdk can only be linked dynamically +!ERROR Cannot link statically against this version of the MrSID Raster DSDK ($(MRSID_RVER)). Try linking dynamically by setting MRSID_RDLLBUILD to YES. +!ENDIF +!ELSE IF EXIST("$(MRSID_RASTER_DIR)\lib\$(MRSID_CONFIG)\lti_dsdk.lib") +MRSID_LIB = "$(MRSID_RASTER_DIR)\lib\$(MRSID_CONFIG)\lti_dsdk.lib" $(MRSID_LIB) +!ELSE IF EXIST("$(MRSID_RASTER_DIR)\lib\$(MRSID_CONFIG)\lti_sdk.lib") +# very old sdk indeed +MRSID_LIB = "$(MRSID_RASTER_DIR)\lib\$(MRSID_CONFIG)\lti_sdk.lib" $(MRSID_LIB) +!ELSE +!ERROR No suitable MrSID Raster libs were found in $(MRSID_RASTER_DIR)\lib. +!ENDIF # end DSDK lib search + +# +# Find and validate the specific JP2 static lib and add it to MRSID_LIB. +!IF "$(MRSID_JP2)" == "YES" +!IF EXIST("$(MRSID_RASTER_DIR)\3rd-party\lib\$(MRSID_CONFIG)\lt_lib_kakadu.lib") +MRSID_LIB = "$(MRSID_RASTER_DIR)\3rd-party\lib\$(MRSID_CONFIG)\lt_lib_kakadu.lib" $(MRSID_LIB) +!ELSE IF EXIST("$(MRSID_RASTER_DIR)\3rd-party\lib\$(MRSID_CONFIG)\ltikdu.lib") +MRSID_LIB = "$(MRSID_RASTER_DIR)\3rd-party\lib\$(MRSID_CONFIG)\ltikdu.lib" $(MRSID_LIB) +!ELSE IF $(MRSID_RVER)0 < 5000 +!ERROR This version of the MrSID Raster SDK ($(MRSID_RVER)) does not support JPEG 2000. +!ELSE +!ERROR The library needed for MrSID Raster SDK to support JPEG 2000 is missing. +!ENDIF +!ENDIF # end JP2 lib search + +# +# Find and validate the specific ESDK static lib(s) and add to MRSID_LIB. +!IF "$(MRSID_ESDK)" == "YES" +# if we still need to add lti_esdk.lib do that first +!IF $(MRSID_RVER)0 < 8000 && EXIST("$(MRSID_RASTER_DIR)\lib\$(MRSID_CONFIG)\lti_esdk.lib") +MRSID_LIB = "$(MRSID_RASTER_DIR)\lib\$(MRSID_CONFIG)\lti_esdk.lib" $(MRSID_LIB) +!ENDIF + +!IF EXISTS("$(MRSID_RASTER_DIR)\3rd-party\lib\$(MRSID_CONFIG)\cryptlib.lib") +MRSID_LIB = "$(MRSID_RASTER_DIR)\3rd-party\lib\$(MRSID_CONFIG)\cryptlib.lib" $(MRSID_LIB) +!ENDIF + +!IF EXISTS("$(MRSID_RASTER_DIR)\3rd-party\lib\$(MRSID_CONFIG)\xmlparse.lib") +MRSID_LIB = "$(MRSID_RASTER_DIR)\3rd-party\lib\$(MRSID_CONFIG)\xmlparse.lib" $(MRSID_LIB) +!ENDIF + +!ENDIF # end ESDK lib search + +MRSID_LIB = $(MRSID_LIB) advapi32.lib user32.lib + +!ENDIF # end static raster build environment + +!ENDIF # end raster libs + +# +# Add Lidar libs to MRSID_LIB +!IF DEFINED(MRSID_LIDAR_DIR) + +!IF "$(MRSID_LDLLBUILD)" == "YES" + +# +# Find and validate the specific DLL and add it to MRSID_LIB +!IF EXIST("$(MRSID_LIDAR_DIR)\lib\lti_lidar_dsdk.dll") +MRSID_LIB = "$(MRSID_LIDAR_DIR)\lib\lti_lidar_dsdk.lib" $(MRSID_LIB) +!ELSE IF EXIST("$(MRSID_LIDAR_DIR)\lib\lti_lidar_dsdk_1.1.dll") +MRSID_LIB = "$(MRSID_LIDAR_DIR)\lib\lti_lidar_dsdk.lib" $(MRSID_LIB) +!ELSE IF $(MRSID_LVER)0 < 1110 +!ERROR Cannot link dynamically against this version of the MrSID Lidar SDK ($(MRSID_LVER)). Try linking statically by setting MRSID_LDLLBUILD to NO. +!ELSE +!ERROR No suitable MrSID Lidar DLL was found in $(MRSID_LIDAR_DIR)\lib. +!ENDIF + +!ELSE # not building against the DLL; use the static lib(s) + +# +# Find and validate the specific lidar static lib and add it to MRSID_LIB +!IF EXIST("$(MRSID_LIDAR_DIR)\lib\$(MRSID_CONFIG)\lti_lidar_dsdk.lib") +MRSID_LIB = "$(MRSID_LIDAR_DIR)\lib\$(MRSID_CONFIG)\lti_lidar_dsdk.lib" $(MRSID_LIB) +!ELSE IF $(MRSID_LVER)0 >= 1110 +# v8+ dsdk can only be linked dynamically +!ERROR Cannot link statically against this version of the MrSID Lidar DSDK ($(MRSID_LVER)). Try linking dynamically by setting MRSID_LDLLBUILD to YES. +!ELSE +!ERROR No suitable MrSID Lidar libs were found under $(MRSID_LIDAR_DIR)\lib. +!ENDIF + +!ENDIF # end static lidar build environment + +!ENDIF # end lidar build environment + + +############################################################################### +# +# Report the configuration summary +# +!IF DEFINED(MRSID_SHOW_CONFIG) +!MESSAGE MRSID_RASTER_DIR = $(MRSID_RASTER_DIR) +!MESSAGE MRSID_RVER = $(MRSID_RVER) +!MESSAGE MRSID_JP2 = $(MRSID_JP2) +!MESSAGE MRSID_ESDK = $(MRSID_ESDK) +!MESSAGE MRSID_RDLLBUILD = $(MRSID_RDLLBUILD) +!MESSAGE MRSID_PLUGIN = $(MRSID_PLUGIN) +!MESSAGE MRSID_LIDAR_DIR = $(MRSID_LIDAR_DIR) +!MESSAGE MRSID_LVER = $(MRSID_LVER) +!MESSAGE MRSID_LDLLBUILD = $(MRSID_LDLLBUILD) +!MESSAGE MRSID_LIDAR_PLUGIN=$(MRSID_LIDAR_PLUGIN) +!MESSAGE MRSID_CONFIG = $(MRSID_CONFIG) +!MESSAGE MRSID_FLAGS = $(MRSID_FLAGS) +!MESSAGE MRSID_INCLUDE = $(MRSID_INCLUDE) +!MESSAGE MRSID_LIB = $(MRSID_LIB) +!ENDIF + diff --git a/bazaar/plugin/gdal/frmts/mrsid_lidar/GNUmakefile b/bazaar/plugin/gdal/frmts/mrsid_lidar/GNUmakefile new file mode 100644 index 000000000..26a6fd9c4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid_lidar/GNUmakefile @@ -0,0 +1,20 @@ +include ../../GDALmake.opt + +OBJ = gdal_MG4Lidar.o + +CPPFLAGS := $(XTRA_OPT) $(MRSID_LIDAR_INCLUDE) $(CPPFLAGS) + +PLUGIN_SO = gdal_MG4Lidar.so + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) *.so + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +plugin: $(PLUGIN_SO) + +$(PLUGIN_SO): $(OBJ) + gcc -shared $(LNK_FLAGS) $(OBJ) $(CONFIG_LIBS_INS) $(EXTRA_LIBS) \ + -o $(PLUGIN_SO) diff --git a/bazaar/plugin/gdal/frmts/mrsid_lidar/frmt_mrsid_lidar.html b/bazaar/plugin/gdal/frmts/mrsid_lidar/frmt_mrsid_lidar.html new file mode 100644 index 000000000..36ab0c70f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid_lidar/frmt_mrsid_lidar.html @@ -0,0 +1,147 @@ + + + + +MG4 --- MrSID Generation 4 for LiDAR + + + + +

    MrSID/MG4 LiDAR Compression / Point Cloud View files

    + +

    This driver provides a way to view MrSID/MG4 compressed LiDAR file as a +raster DEM. The specifics of the conversion depend on the desired cellsize, +filter criteria, aggregation methods and possibly several other parameters. +For this reason, the best way to read a MrSID/MG4 compressed LiDAR file +is by referencing it in a View (.view) file, which also parameterizes its +raster-conversion. The driver will read an MG4 file directly, however it +uses default rasterization parameters that may not produce a desirable +output. The contents of the View file are described in the specification +MrSID/MG4 LiDAR View Documents.

    + +

    MrSID/MG4 is a wavelet-based point-cloud compression technology. You may +think of it like a LAS file, only smaller and with a built in spatial index. +It is developed and distributed by LizardTech. This driver supports reading +of MG4 LiDAR files using LizardTech's decoding software development kit (DSDK). +This DSDK is freely distributed; but, it is not open source software. +You should contact LizardTech to obtain it (see link at end of this page). +

    + +

    Example View files (from View Document specification)

    + +

    Simplest possible .view file

    + +

    The simplest way to view an MG4 file is to wrap it in a View (.view) file +like this. Here, the relative reference to the MG4 file means that the file +must exist in the same directory as the .view file. Since we're not mapping +any bands explicitly, we get the default, which is elevation only. By default, +we aggregate based on mean. That is, if two (or more) points land on a single +cell, we will expose the average of the two. There's no filtering here so +we'll get all the points regardless of classification code or return number. +Since the native datatype of elevation is "Float64", that is the datatype of +the band we will expose.

    + +
    +<PointCloudView>
    +   <InputFile>Tetons.sid</InputFile>
    +</PointCloudView>
    +
    + +

    Crop the data

    + +

    This is similar to the example above but we are using the optional ClipBox +tag to select a 300 meter North-South swatch through the cloud. If we wanted +to crop in the East-West directions, we could have specified that explicitly +instead of using NOFITLER for those. Similarly, we could also have cropped in +the Z direction as well.

    + +
    +<PointCloudView>
    +   <InputFile>Tetons.sid</InputFile>
    +   <ClipBox>505500 505800 NOFILTER NOFILTER</ClipBox>
    +</PointCloudView>
    +
    + +

    Expose as a bare earth (Max) DEM

    + +

    Here, we expose a single band (elevation) but we want only those points that +have been classified as "Ground". The ClassificationFitler specifies a value +of 2 - the ASPRS Point Class code that stipulates "Ground" points. + Additionally, instead of the default "Mean" aggregation method, we specify +"Max". This means that if two (or more) points land on a single cell, we expose +the larger of the two elevation values.

    + +
    +<PointCloudView>
    +   <InputFile>E:\ESRIDevSummit2010\Tetons.sid</InputFile>
    +   <Band> <!-- Max Bare Earth-->
    +      <Channel>Z</Channel>
    +      <AggregationMethod>Max</AggregationMethod>
    +      <ClassificationFilter>2</ClassificationFilter>
    +   </Band>
    +</PointCloudView>
    +
    + +

    Intensity image

    + +

    Here we expose an intensity image from the point cloud.

    + +
    +<PointCloudView>
    +   <InputFile>Tetons.sid</InputFile>
    +   <Band>
    +      <!-- All intensities -->
    +      <Channel>Intensity</Channel>
    +   </Band>
    +</PointCloudView>
    +
    + +

    RGB image

    + +

    Some point cloud images include RGB data. If that's the case, you can use +a .view file like this to expose that data.

    + +
    +<PointCloudView>
    +   <InputFile>Grass Lake Small.xyzRGB.sid</InputFile>
    +   <Band>
    +      <Channel>Red</Channel>
    +   </Band>
    +   <Band>
    +      <Channel>Green</Channel>
    +   </Band>
    +   <Band>
    +      <Channel>Blue</Channel>
    +   </Band>
    +</PointCloudView> 
    +
    + +

    Writing not supported

    + +

    This driver does not support writing MG4 files.

    + +

    Limitations of current implementation

    + +

    Only one <InputFile> tag is supported. It must reference an MG4 +file.

    + +

    The only <InterpolationMethod> that is supported is +<None> (default). Use this to specify a NODATA value if the +default (maximum value of the datatype) is not what you want. See View +Specification for details.

    + +

    There is insufficient error checking for format errors and invalid +parameters. Many invalid entries will likely fail silently.

    + +

    See Also:

    + + + + + + diff --git a/bazaar/plugin/gdal/frmts/mrsid_lidar/frmt_mrsid_lidar_view_point_cloud.html b/bazaar/plugin/gdal/frmts/mrsid_lidar/frmt_mrsid_lidar_view_point_cloud.html new file mode 100644 index 000000000..6bfac2c37 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid_lidar/frmt_mrsid_lidar_view_point_cloud.html @@ -0,0 +1,1673 @@ + + + + + + + + + + + + + + + + + + + + + +
    + +
    + +

    Specification for

    + +

    MrSID/MG4 LiDAR +View Documents

    + +

    Version 1.0

    + +
    + +

    Michael Rosen, et al

    + +

    LizardTech

    + +

    March 2010

    + +

    1.  +Introduction

    + +

    This document specifies the contents of an XML document used +as a “view” into a LiDAR point cloud.  It is intended to be a rigorous definition of +an XML-based format for specifying rasterization of +point cloud data.  If you are looking for +“something to get me started very quickly,” please see the examples below.

    + +

    2.  Document +Structure (informative)

    + +

    The overall element structure of a View document is +informally shown below.  Indentation and regular expression syntax are +used to intuitively indicate parent-child nesting and the number of occurrences +of elements.

    + +

          +PointCloudView

    + +

                +InputFile +

    + +

                +Datatype ?

    + +

                +Band *

    + +

                      +Channel ?

    + +

                      +ClassificationFilter ?

    + +

                      +ReturnNumberFilter ?

    + +

                      +AggregationMethod ?

    + +

                      +InterpolationMethod ?

    + +

                +ClassificationFilter ?

    + +

                +ReturnNumberFilter ?

    + +

                +AggregationMethod ?

    + +

                +InterpolationMethod ?

    + +

                +ClipBox ?

    + +

                +CellSize ?

    + +

                +GeoReference ?

    + +

    3.  Elements

    + +

    Each element is specified as follows:

    + +

    ElementName

    + +

    ·        +Cardinality:  number of occurrences allowed

    + +

    ·        +Parents:  what element(s) may contain this element

    + +

    ·        +Contents: what may be placed inside the element

    + +

    ·        +Attributes:  what attributes are allowed, if any

    + +

    ·        +Notes:  additional usage information or restriction

    + +

    3.1.  PointClouldView

    + +

    Description:  the root element for the document

    + +

    Cardinality:  1

    + +

    Parents:  none (must be root element)

    + +

    Contents:  child elements as specified below:

    + +

    ·        +InputFile

    + +

    ·        +Datatype

    + +

    ·        +Band

    + +

    ·        +ClassificationFilter

    + +

    ·        +ReturnNumberFilter

    + +

    ·        +AggregationMethod

    + +

    ·        +InterpolationMethod

    + +

    ·        +ClipBox

    + +

    ·        +CellSize

    + +

    ·        +GeoReference

    + +

    Attributes:

    + +

    ·        +version +– this attribute must be present and set to the value 1.0

    + +

    Notes(none)

    + +

    3.2.  InputFile

    + +

    Description:  specifies an input file containing +point cloud data

    + +

    Cardinality:  1..n

    + +

    Parents:  PointClouldView

    + +

    Contents:  string (corresponding to a filename)

    + +

    Attributes:  (none)

    + +

    Notes:

    + +

    ·        +Typically the file will be a MrSID/MG4 LiDAR file, but may +also be a LAS file.

    + +

    ·        +The file name given may have a relative or +absolute path.  If relative, the path is to be expanded relative to the +directory containing this View document.

    + +

    3.3.  Datatype

    + +

    Description:  specifies the datatype +to which channel data should be coerced

    + +

    Cardinality:  0 or 1

    + +

    Parents:  PointClouldView

    + +

    Contents:  string (corresponding to a datatype name)

    + +

    Attributes:  (none)

    + +

    Notes:

    + +

    ·        +If this element is not present, the native datatype +of the channel is used.

    + +

    ·        +Legal values are derived from those returned by GDALGetDataTypeByName, as +follows:

    + +

    o   +Byte

    + +

    o   +UInt16

    + +

    o   +Int16

    + +

    o   +UInt32

    + +

    o   +Int32

    + +

    o   +Float32

    + +

    o   +Float64

    + +

    ·        +Channel data will be coerced via a c-style cast, truncating data as +necessary.

    + +

    3.4.  Band

    + +

    Description:  list of which band(s) to expose +and in what manner to process the band data

    + +

    Cardinality:  0, 1 or 3

    + +

    Parents:  PointClouldView

    + +

    Contents:  child elements as follows:

    + +

    ·        +0 or 1 Channel element

    + +

    ·        +0 or 1 ClassificationFilter +element

    + +

    ·        +0 or 1 ReturnNumberFilter +element

    + +

    ·        +0 or 1 InterpolationMethod +element

    + +

    ·        +0 or 1 AggregationMethod +element

    + +

    Attributes:  (none)

    + +

    Notes:

    + +

    ·        +Not specifying any bands is the same as +specifying only one with all default values.

    + +

    3.5.  Channel

    + +

    Description:  the name of the channel in the +input file

    + +

    Cardinality:  0 or 1 per Band element

    + +

    Parents:  Band

    + +

    Contents:  we use the following canonical names +of channels

    + +

    ·        +X

    + +

    ·        +Y

    + +

    ·        +Z

    + +

    ·        +Intensity

    + +

    ·        +ReturnNum

    + +

    ·        +NumReturns

    + +

    ·        +ScanDir

    + +

    ·        +EdgeFlightLine

    + +

    ·        +ClassId

    + +

    ·        +ScanAngle

    + +

    ·        +UserData

    + +

    ·        +SourceId

    + +

    ·        +GPSTime

    + +

    ·        +Red

    + +

    ·        +Green

    + +

    ·        +Blue

    + +

    Attributes:  (none)

    + +

    Notes

    + +

    ·        +Custom channels have non-canonical names, are supported, and may be +specified.

    + +

    ·        +If this element is omitted, the Channel for the Band shall default to Z.

    + +

    ·        +The channel names are derived from PointData.h +of the MG4 Decode SDK.

    + +

    3.6.  ClassificationFilter

    + +

    Description:  A filter for points whose +classification code is one of the specified values.

    + +

    Cardinality:  0 or 1 per Band element

    + +

    Parents:  Band +or PointCloudView

    + +

    Contents:  space-separated “Classification Values” (0-31) as defined by ASPRS +Standard LIDAR Point Classes in the LAS 1.3 +Specification.

    + +

    Attributes:  (none)

    + +

    Notes:

    + +

    ·        +If this element is omitted, the band shall have +no classification filter applied.

    + +

    ·        +If this element is a child of the PointCloudView +element, it applies to all bands (unless overridden for a specific band)

    + +

    ·        +If this element is a child of a Band +element, it applies to this band only and overrides any +other setting

    + +

    ·        +Note that numbers are used to represent the filters, rather than +strings.  This is because there is no canonical, simple naming convention +for them, and is also in keeping with existing practice in certain existing +applications.

    + +

    3.7.  ReturnNumberFilter

    + +

    Description:  A filter for points whose return +number is one of the specified values.

    + +

    Cardinality:  0 or 1 per Band element

    + +

    Parents:  Band +or PointCloudView

    + +

    Contents:  space-separated numbers (1, 2, …) or the string LAST

    + +

    Attributes:  (none)

    + +

    Notes:

    + +

    ·        +If this element is omitted, the band shall have +no return number filter applied

    + +

    ·        +If this element is a child of the PointCloudView +element, it applies to all bands (unless overridden for a specific band)

    + +

    ·        +If this element is a child of a Band +element, it applies to this band only and overrides any other setting

    + +

    3.8.  AggregationMethod

    + +

    Description:  Each cell (pixel) can expose a +single value.  When 2 or more points fall on a single cell, this method +determines what value to expose.

    + +

    Cardinality:  0 or 1 per Band element

    + +

    Parents:  Band +or PointCloudView

    + +

    Contents:  a string, one of Min, Max, +or Mean

    + +

    Attributes:  (none)

    + +

    Notes:

    + +

    ·        +If this element is omitted, the band shall have +the “Mean” aggregation method applied

    + +

    ·        +If this element is a child of the PointCloudView +element, it applies to all bands (unless overridden for a specific band)

    + +

    ·        +If this element is a child of a Band +element, it applies to this band only and overrides any other setting

    + +

    3.9.  InterpolationMethod

    + +

    Description:  Method and parameter to interpolate +NODATA values.  Also specifies what the NODATA value is.

    + +

    Cardinality:  0 or 1 per Band element

    + +

    Parents:  Band +or PointCloudView

    + +

    Contents:   exactly one of the following +elements:

    + +

    ·        +None

    + +

    ·        +InverseDistanceToAPower

    + +

    ·        +MovingAverage

    + +

    ·        +NearestNeighbor

    + +

    ·        +Minimum

    + +

    ·        +Maximum

    + +

    ·        +Range

    + +

    Attributes:  (none)

    + +

    Notes

    + +

    ·        +Each of the interpolation methods (MovingAverage, +etc.) is an element whose content is a text string corresponding to the +parameter(s) for that method.  See http://www.gdal.org/grid_tutorial.html +for a description of the methods and their parameter strings.

    + +

    ·        +In the parameter descriptions, MAX is used to indicate the value +defined by libc which is the largest supportable +value for the output datatype.  If you choose to +override this default be sure that the number you specify will fit in the datatype you specify.

    + +

    ·        +If this element is omitted, the band shall have +the “None” interpolation method applied.

    + +

    ·        +If this element is a child of the PointCloudView +element, it applies to all bands (unless overridden for a specific band)

    + +

    ·        +If this element is a child of a Band +element, it applies to this band only and overrides any other setting

    + +

    3.10.  ClipBox

    + +

    Description:  geographic extent of region to be +viewed

    + +

    Cardinality:  0 or 1

    + +

    Parents:  PointClouldView

    + +

    Contents:  4 or 6 doubles; the string NOFILTER may be specified in place of +a double value

    + +

    Attributes:  (none)

    + +

    Notes:

    + +

    ·        +The full 6 values are (in order): xmin, xmax, ymin, +ymax, zmin, zmax.

    + +

    ·        +The string NOFILTER +means to use the corresponding value of the Minimum Bounding Rectangle (MBR) of +the input files.  The point is not filtered by that value.

    + +

    ·        +If only 4 double are present, the zmin and zmax are assumed to be NOFILTER.

    + +

    ·        +If this element is not present, the clip box is +assumed to be the MBR of the input files.

    + +

    3.11.  CellSize

    + +

    Description:  Side length of a (square) pixel in +ground units

    + +

    Cardinality:  0 or 1

    + +

    Parents:  PointClouldView

    + +

    Contents:  1 double

    + +

    Attributes:  (none)

    + +

    Notes:

    + +

    ·        +This element is used to determine the size of +the resulting raster.

    + +

    ·        +If this element is omitted, the default cell +size is the average (linear) point spacing (assuming a uniform distribution +over the entire extent).

    + +

    3.12.  GeoReference

    + +

    Description:  the coordinate reference system of +the view

    + +

    Cardinality:  0 or 1

    + +

    Parents:  PointClouldView

    + +

    Contents:  a string (corresponding to a WKT)

    + +

    Attributes:  (none)

    + +

    Notes:

    + +

    ·        +If this element is omitted, the WKT of the input files is used.  If +two or more files have different WKTs, then no GeoReference +is defined.

    + +

    ·        +A typical use of this element is for when the +MG4 file was created without adequate GeoReference +information: cases where some combination of UOM, HorizCS +and VertCS are missing are quite common.

    + +

    4.  Additional +Requirements

    + +

    Any element not recognized should be treated as an error.

    + +

    Any attribute not recognized should be treated as an error.

    + +

    This specification does not mandate the lexical ordering of +the child elements within a given parent.

    + +

    5.  Examples

    + +

    5.1.  Simplest possible .view file

    + +

    The simplest way to view an MG4 file is to wrap it in a +View (.view) file like this.  Here, the relative +reference to the MG4 file means that the file must exist in the same directory +as the .view file.  Since we’re not +mapping any bands explicitly, we get the default, which is elevation only.  By default, we aggregate based on mean.  That is, if two (or more) points land on a +single cell, we will expose the average of the two.  There’s no filtering here so we’ll get all +the points regardless of classification code or return number.  Since the native datatype +of elevation is “Float64”, that is the datatype of the +band we will expose.

    + +

    <PointCloudView>

    + +

       +<InputFile>Tetons.sid</InputFile>

    + +

    </PointCloudView>

    + +

     

    + +

    5.2. Crop the data

    + +

    This is similar to the example above but we are using the optional +ClipBox tag to select a 300 meter North-South swatch +through the cloud.  If we wanted to crop +in the East-West directions, we could have specified that explicitly instead of +using NOFITLER for those.  Similarly, we +could also have cropped in the Z direction as well.

    + +

    <PointCloudView>

    + +

       +<InputFile>Tetons.sid</InputFile>

    + +

       +<ClipBox>505500 505800 NOFILTER +NOFILTER</ClipBox>

    + +

    </PointCloudView>

    + +

     

    + +

    5.3.  Expose as a bare earth (Max) DEM

    + +

    Here, we expose a single band (elevation) but we want only those points that have been classified as “Ground.”  The ClassificationFitler +specifies a value of 2 – the ASPRS Point Class code that stipulates “Ground” +points.  Additionally, instead of the +default “Mean” aggregation method, we specify “Max.”  This means that if two (or more) points land +on a single cell, we expose the larger of the two elevation values.

    + +

    <PointCloudView>

    + +

       +<InputFile>E:\ESRIDevSummit2010\Tetons.sid</InputFile>

    + +

       +<Band> <!-- Max Bare Earth-->

    + +

          +<Channel>Z</Channel>

    + +

          +<AggregationMethod>Max</AggregationMethod>

    + +

          +<ClassificationFilter>2</ClassificationFilter>

    + +

       +</Band>

    + +

    </PointCloudView>

    + +

    5.4.  Intensity image

    + +

    Here we +expose an intensity image from the point cloud.

    + +

    <PointCloudView>

    + +

       +<InputFile>Tetons.sid</InputFile>

    + +

       +<Band>

    + +

          +<!-- All intensities -->

    + +

          +<Channel>Intensity</Channel>

    + +

       +</Band>

    + +

    </PointCloudView>

    + +

    5.5  RGB image

    + +

    Some point +cloud images include RGB data.  If that’s +the case, you can use a .view file like this to expose that data.

    + +

    <PointCloudView>

    + +

       +<InputFile>Grass Lake +Small.xyzRGB.sid</InputFile>

    + +

       +<Band>

    + +

          +<Channel>Red</Channel>

    + +

       +</Band>

    + +

       +<Band>

    + +

          +<Channel>Green</Channel>

    + +

       +</Band>

    + +

       +<Band>

    + +

          +<Channel>Blue</Channel>

    + +

       +</Band>

    + +

    </PointCloudView>

    + +

     

    + +
    + + + + diff --git a/bazaar/plugin/gdal/frmts/mrsid_lidar/gdal_MG4Lidar.cpp b/bazaar/plugin/gdal/frmts/mrsid_lidar/gdal_MG4Lidar.cpp new file mode 100644 index 000000000..6b3f5db13 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid_lidar/gdal_MG4Lidar.cpp @@ -0,0 +1,942 @@ +/****************************************************************************** +* Project: MG4 Lidar GDAL Plugin +* Purpose: Provide an orthographic view of MG4-encoded Lidar dataset +* for use with LizardTech Lidar SDK version 1.1.0 +* Author: Michael Rosen, mrosen@lizardtech.com +* +****************************************************************************** +* Copyright (c) 2010, LizardTech +* All rights reserved. + +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* Neither the name of the LizardTech nor the names of its contributors may +* be used to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +****************************************************************************/ +#include "lidar/MG4PointReader.h" +#include "lidar/FileIO.h" +#include "lidar/Error.h" +#include "lidar/Version.h" +#include +LT_USE_LIDAR_NAMESPACE + +#include "gdal_pam.h" +// #include "gdal_alg.h" // 1.6 and later have gridding algorithms + +CPL_C_START +//void __declspec(dllexport) GDALRegister_MG4Lidar(void); +void CPL_DLL GDALRegister_MG4Lidar(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* MG4LidarDataset */ +/* ==================================================================== */ +/************************************************************************/ + +// Resolution Ratio between adjacent levels. +#define RESOLUTION_RATIO 2.0 + +class MG4LidarRasterBand; +class CropableMG4PointReader : public MG4PointReader +{ + CONCRETE_OBJECT(CropableMG4PointReader); + void init (IO *io, Bounds *bounds) + { + MG4PointReader::init(io); + if (bounds != NULL) + setBounds(*bounds); + } +}; +CropableMG4PointReader::CropableMG4PointReader() : MG4PointReader() {}; +CropableMG4PointReader::~CropableMG4PointReader() {}; + +IMPLEMENT_OBJECT_CREATE(CropableMG4PointReader); + +static double MaxRasterSize = 2048.0; +static double MaxBlockSideSize = 1024.0; +class MG4LidarDataset : public GDALPamDataset +{ +friend class MG4LidarRasterBand; +public: + MG4LidarDataset(); + ~MG4LidarDataset(); + static GDALDataset *Open( GDALOpenInfo * ); + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef(); +protected: + MG4PointReader *reader; + FileIO *fileIO; + CPLErr OpenZoomLevel( int Zoom ); + PointInfo requiredChannels; + int nOverviewCount; + MG4LidarDataset **papoOverviewDS; + CPLXMLNode *poXMLPCView; + bool ownsXML; + int nBlockXSize, nBlockYSize; + int iLevel; +}; + +/* ======================================== */ +/* MG4LidarRasterBand */ +/* ======================================== */ + +class MG4LidarRasterBand : public GDALPamRasterBand +{ + friend class MG4LidarDataset; + +public: + + MG4LidarRasterBand( MG4LidarDataset *, int, CPLXMLNode *, const char * ); + ~MG4LidarRasterBand(); + + virtual CPLErr GetStatistics( int bApproxOK, int bForce, double *pdfMin, double *pdfMax, double *pdfMean, double *padfStdDev ); + virtual int GetOverviewCount(); + virtual GDALRasterBand * GetOverview( int i ); + virtual CPLErr IReadBlock( int, int, void * ); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + + protected: + double getMaxValue(); + double nodatavalue; + virtual const bool ElementPassesFilter(const PointData &, size_t); + template + CPLErr doReadBlock(int, int, void *); + CPLXMLNode *poxmlBand; + char **papszFilterClassCodes; + char **papszFilterReturnNums; + const char * Aggregation; + CPLString ChannelName; +}; + + +/************************************************************************/ +/* MG4LidarRasterBand() */ +/************************************************************************/ + +MG4LidarRasterBand::MG4LidarRasterBand( MG4LidarDataset *pods, int nband, CPLXMLNode *xmlBand, const char * name ) +{ + this->poDS = pods; + this->nBand = nband; + this->poxmlBand = xmlBand; + this->ChannelName = name; + this->Aggregation = NULL; + nBlockXSize = pods->nBlockXSize; + nBlockYSize = pods->nBlockYSize; + + switch(pods->reader->getChannel(name)->getDataType()) + { +#define DO_CASE(ltdt, gdt) case (ltdt): eDataType = gdt; break; + DO_CASE(DATATYPE_FLOAT64, GDT_Float64); + DO_CASE(DATATYPE_FLOAT32, GDT_Float32); + DO_CASE(DATATYPE_SINT32, GDT_Int32); + DO_CASE(DATATYPE_UINT32, GDT_UInt32); + DO_CASE(DATATYPE_SINT16, GDT_Int16); + DO_CASE(DATATYPE_UINT16, GDT_UInt16); + DO_CASE(DATATYPE_SINT8, GDT_Byte); + DO_CASE(DATATYPE_UINT8, GDT_Byte); +default: + CPLError(CE_Failure, CPLE_AssertionFailed, + "Invalid datatype in MG4 file"); + break; +#undef DO_CASE + } + // Coerce datatypes as required. + const char * ForceDataType = CPLGetXMLValue(pods->poXMLPCView, "Datatype", NULL); + + if (ForceDataType != NULL) + { + GDALDataType dt = GDALGetDataTypeByName(ForceDataType); + if (dt != GDT_Unknown) + eDataType = dt; + } + + CPLXMLNode *poxmlFilter = CPLGetXMLNode(poxmlBand, "ClassificationFilter"); + if( poxmlFilter == NULL ) + poxmlFilter = CPLGetXMLNode(pods->poXMLPCView, "ClassificationFilter"); + if (poxmlFilter == NULL || poxmlFilter->psChild == NULL || + poxmlFilter->psChild->pszValue == NULL) + papszFilterClassCodes = NULL; + else + papszFilterClassCodes = CSLTokenizeString(poxmlFilter->psChild->pszValue); + + poxmlFilter = CPLGetXMLNode(poxmlBand, "ReturnNumberFilter"); + if( poxmlFilter == NULL ) + poxmlFilter = CPLGetXMLNode(pods->poXMLPCView, "ReturnNumberFilter"); + if (poxmlFilter == NULL || poxmlFilter->psChild == NULL || + poxmlFilter->psChild->pszValue == NULL) + papszFilterReturnNums = NULL; + else + papszFilterReturnNums = CSLTokenizeString(poxmlFilter->psChild->pszValue); + + + CPLXMLNode * poxmlAggregation = CPLGetXMLNode(poxmlBand, "AggregationMethod"); + if( poxmlAggregation == NULL ) + poxmlAggregation = CPLGetXMLNode(pods->poXMLPCView, "AggregationMethod"); + if (poxmlAggregation == NULL || poxmlAggregation->psChild == NULL || + poxmlAggregation->psChild->pszValue == NULL) + Aggregation = "Mean"; + else + Aggregation = poxmlAggregation->psChild->pszValue; + + nodatavalue = getMaxValue(); + + CPLXMLNode * poxmlIntepolation = CPLGetXMLNode(poxmlBand, "InterpolationMethod"); + if( poxmlIntepolation == NULL ) + poxmlIntepolation = CPLGetXMLNode(pods->poXMLPCView, "InterpolationMethod"); + if (poxmlIntepolation != NULL ) + { + CPLXMLNode * poxmlMethod= NULL; + char ** papszParams = NULL; + if (((poxmlMethod = CPLSearchXMLNode(poxmlIntepolation, "None")) != NULL) && + poxmlMethod->psChild != NULL && poxmlMethod->psChild->pszValue != NULL) + { + papszParams = CSLTokenizeString(poxmlMethod->psChild->pszValue); + if (!EQUAL(papszParams[0], "MAX")) + nodatavalue = CPLAtof(papszParams[0]); + } + // else if .... Add support for other interpolation methods here. + CSLDestroy(papszParams); + } + const char * filter = NULL; + if (papszFilterClassCodes != NULL && papszFilterReturnNums != NULL) + filter ="Classification and Return"; + if (papszFilterClassCodes != NULL) + filter = "Classification"; + else if (papszFilterReturnNums != NULL) + filter = "Return"; + CPLString osDesc; + if (filter) + osDesc.Printf("%s of %s (filtered by %s)", Aggregation, ChannelName.c_str(), filter); + else + osDesc.Printf("%s of %s", Aggregation, ChannelName.c_str()); + SetDescription(osDesc); +} + +/************************************************************************/ +/* ~MG4LidarRasterBand() */ +/************************************************************************/ +MG4LidarRasterBand::~MG4LidarRasterBand() +{ + CSLDestroy(papszFilterClassCodes); + CSLDestroy(papszFilterReturnNums); +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int MG4LidarRasterBand::GetOverviewCount() +{ + MG4LidarDataset *poGDS = (MG4LidarDataset *) poDS; + return poGDS->nOverviewCount; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ +GDALRasterBand *MG4LidarRasterBand::GetOverview( int i ) +{ + MG4LidarDataset *poGDS = (MG4LidarDataset *) poDS; + + if( i < 0 || i >= poGDS->nOverviewCount ) + return NULL; + else + return poGDS->papoOverviewDS[i]->GetRasterBand( nBand ); +} +template +const DTYPE GetChannelElement(const ChannelData &channel, size_t idx) +{ + DTYPE retval = static_cast(0); + switch (channel.getDataType()) + { + case (DATATYPE_FLOAT64): + retval = static_cast(static_cast(channel.getData())[idx]); + break; + case (DATATYPE_FLOAT32): + retval = static_cast(static_cast(channel.getData())[idx]); + break; + case (DATATYPE_SINT32): + retval = static_cast(static_cast(channel.getData())[idx]); + break; + case (DATATYPE_UINT32): + retval = static_cast(static_cast(channel.getData())[idx]); + break; + case (DATATYPE_SINT16): + retval = static_cast(static_cast(channel.getData())[idx]); + break; + case (DATATYPE_UINT16): + retval = static_cast(static_cast(channel.getData())[idx]); + break; + case (DATATYPE_SINT8): + retval = static_cast(static_cast(channel.getData())[idx]); + break; + case (DATATYPE_UINT8): + retval = static_cast(static_cast(channel.getData())[idx]); + break; + case (DATATYPE_SINT64): + retval = static_cast(static_cast(channel.getData())[idx]); + break; + case (DATATYPE_UINT64): + retval = static_cast(static_cast(channel.getData())[idx]); + break; + default: + break; + } + return retval; +} + + +const bool MG4LidarRasterBand::ElementPassesFilter(const PointData &pointdata, size_t i) +{ + bool bClassificationOK = true; + bool bReturnNumOK = true; + + // Check if classification code is ok: it was requested and it does match one of the requested codes + const int classcode = GetChannelElement(*pointdata.getChannel(CHANNEL_NAME_ClassId), i); + char bufCode[16]; + sprintf(bufCode, "%d", classcode); + bClassificationOK = (papszFilterClassCodes == NULL ? true : + (CSLFindString(papszFilterClassCodes,bufCode)!=-1)); + + if (bClassificationOK) + { + // Check if return num is ok: it was requested and it does match one of the requested return numbers + const long returnnum= static_cast(pointdata.getChannel(CHANNEL_NAME_ReturnNum)->getData())[i]; + sprintf(bufCode, "%d", (int)returnnum); + bReturnNumOK = (papszFilterReturnNums == NULL ? true : + (CSLFindString(papszFilterReturnNums, bufCode)!=-1)); + if (!bReturnNumOK && CSLFindString(papszFilterReturnNums, "Last")!=-1) + { // Didn't find an explicit match (e.g. return number "1") so we handle a request for "Last" returns + const long numreturns= GetChannelElement(*pointdata.getChannel(CHANNEL_NAME_NumReturns), i); + bReturnNumOK = (returnnum == numreturns); + } + } + + return bReturnNumOK && bClassificationOK; + +} + + +template +CPLErr MG4LidarRasterBand::doReadBlock(int nBlockXOff, int nBlockYOff, void * pImage) +{ + MG4LidarDataset * poGDS = (MG4LidarDataset *)poDS; + MG4PointReader *reader =poGDS->reader; + + struct Accumulator_t + { + DTYPE value; + int count; + } ; + Accumulator_t * Accumulator = NULL; + if (EQUAL(Aggregation, "Mean")) + { + Accumulator = new Accumulator_t[nBlockXSize*nBlockYSize]; + memset (Accumulator, 0, sizeof(Accumulator_t)*nBlockXSize*nBlockYSize); + } + for (int i = 0; i < nBlockXSize; i++) + { + for (int j = 0; j < nBlockYSize; j++) + { + static_cast(pImage)[i*nBlockYSize+j] = static_cast(nodatavalue); + } + } + + double geoTrans[6]; + poGDS->GetGeoTransform(geoTrans); + double xres = geoTrans[1]; + double yres = geoTrans[5]; + + // Get the extent of the requested block. + double xmin = geoTrans[0] + (nBlockXOff *nBlockXSize* xres); + double xmax = xmin + nBlockXSize* xres; + double ymax = reader->getBounds().y.max - (nBlockYOff * nBlockYSize* -yres); + double ymin = ymax - nBlockYSize* -yres; + Bounds bounds(xmin, xmax, ymin, ymax, -HUGE_VAL, +HUGE_VAL); + PointData pointdata; + pointdata.init(reader->getPointInfo(), 4096); + double fraction = 1.0/pow(RESOLUTION_RATIO, poGDS->iLevel); + CPLDebug( "MG4Lidar", "IReadBlock(x=%d y=%d, level=%d, fraction=%f)", nBlockXOff, nBlockYOff, poGDS->iLevel, fraction); + Scoped iter(reader->createIterator(bounds, fraction, reader->getPointInfo(), NULL)); + + const double * x = pointdata.getX(); + const double * y = pointdata.getY(); + size_t nPoints; + while ( (nPoints = iter->getNextPoints(pointdata)) != 0) + { + for( size_t i = 0; i < nPoints; i++ ) + { + const ChannelData * channel = pointdata.getChannel(ChannelName); + if (papszFilterClassCodes || papszFilterReturnNums) + { + if (!ElementPassesFilter(pointdata, i)) + continue; + } + double col = (x[i] - xmin) / xres; + double row = (ymax - y[i]) / -yres; + col = floor (col); + row = floor (row); + + if (row < 0) + row = 0; + else if (row >= nBlockYSize) + row = nBlockYSize - 1; + if (col < 0) + col = 0; + else if (col >= nBlockXSize ) + col = nBlockXSize - 1; + + int iCol = (int) (col); + int iRow = (int) (row); + const int offset =iRow* nBlockXSize + iCol; + if (EQUAL(Aggregation, "Max")) + { + DTYPE value = GetChannelElement(*channel, i); + if (static_cast(pImage)[offset] == static_cast(nodatavalue) || + static_cast(pImage)[offset] < value) + static_cast(pImage)[offset] = value; + } + else if (EQUAL(Aggregation, "Min")) + { + DTYPE value = GetChannelElement(*channel, i); + if (static_cast(pImage)[offset] == static_cast(nodatavalue) || + static_cast(pImage)[offset] > value) + static_cast(pImage)[offset] = value; + } + else if (EQUAL(Aggregation, "Mean")) + { + DTYPE value = GetChannelElement(*channel, i); + Accumulator[offset].count++; + Accumulator[offset].value += value; + static_cast(pImage)[offset] = static_cast(Accumulator[offset].value / Accumulator[offset].count); + } + } + } + + delete[] Accumulator; + return CE_None; +} + +double MG4LidarRasterBand::getMaxValue() +{ + double retval; + switch(eDataType) + { + #define DO_CASE(gdt, largestvalue) case (gdt):\ + retval = static_cast(largestvalue); \ + break; + + DO_CASE (GDT_Float64, DBL_MAX); + DO_CASE (GDT_Float32, FLT_MAX); + DO_CASE (GDT_Int32, INT_MAX); + DO_CASE (GDT_UInt32, UINT_MAX); + DO_CASE (GDT_Int16, SHRT_MAX); + DO_CASE (GDT_UInt16, USHRT_MAX); + DO_CASE (GDT_Byte, UCHAR_MAX); + #undef DO_CASE + default: + retval = 0; + break; + } + return retval; +} +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr MG4LidarRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + // CPLErr eErr; + switch(eDataType) + { +#define DO_CASE(gdt, nativedt) case (gdt):\ + /* eErr = */doReadBlock(nBlockXOff, nBlockYOff, pImage); \ + break; + + DO_CASE (GDT_Float64, double); + DO_CASE (GDT_Float32, float); + DO_CASE (GDT_Int32, long); + DO_CASE (GDT_UInt32, unsigned long); + DO_CASE (GDT_Int16, short); + DO_CASE (GDT_UInt16, unsigned short); + DO_CASE (GDT_Byte, char); +#undef DO_CASE + default: + return CE_Failure; + break; + + } + return CE_None; +} + +/************************************************************************/ +/* GetStatistics() */ +/************************************************************************/ + +CPLErr MG4LidarRasterBand::GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev ) + +{ + bApproxOK = TRUE; + bForce = TRUE; + + return GDALPamRasterBand::GetStatistics( bApproxOK, bForce, + pdfMin, pdfMax, + pdfMean, pdfStdDev ); + +} +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double MG4LidarRasterBand::GetNoDataValue( int *pbSuccess ) +{ + if (pbSuccess) + *pbSuccess = TRUE; + return nodatavalue; +} + + +/************************************************************************/ +/* MG4LidarDataset() */ +/************************************************************************/ +MG4LidarDataset::MG4LidarDataset() +{ + reader = NULL; + fileIO = NULL; + + poXMLPCView = NULL; + ownsXML = false; + nOverviewCount = 0; + papoOverviewDS = NULL; + +} +/************************************************************************/ +/* ~MG4LidarDataset() */ +/************************************************************************/ + +MG4LidarDataset::~MG4LidarDataset() + +{ + FlushCache(); + if ( papoOverviewDS ) + { + for( int i = 0; i < nOverviewCount; i++ ) + delete papoOverviewDS[i]; + CPLFree( papoOverviewDS ); + } + if (ownsXML) + CPLDestroyXMLNode(poXMLPCView); + + RELEASE(reader); + RELEASE(fileIO); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr MG4LidarDataset::GetGeoTransform( double * padfTransform ) + +{ + padfTransform[0] = reader->getBounds().x.min ;// Upper left X, Y + padfTransform[3] = reader->getBounds().y.max; // + padfTransform[1] = reader->getBounds().x.length()/GetRasterXSize(); //xRes + padfTransform[2] = 0.0; + + padfTransform[4] = 0.0; + padfTransform[5] = -1 * reader->getBounds().y.length()/GetRasterYSize(); //yRes + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *MG4LidarDataset::GetProjectionRef() + +{ + const char * wkt = CPLGetXMLValue(poXMLPCView, "GeoReference", NULL); + if (wkt == NULL) + wkt = reader->getWKT(); + return(wkt); +} + +/************************************************************************/ +/* OpenZoomLevel() */ +/************************************************************************/ + +CPLErr MG4LidarDataset::OpenZoomLevel( int iZoom ) +{ + /* -------------------------------------------------------------------- */ + /* Get image geometry. */ + /* -------------------------------------------------------------------- */ + iLevel = iZoom; + + // geo dimensions + const double gWidth = reader->getBounds().x.length() ; + const double gHeight = reader->getBounds().y.length() ; + + // geo res + double xRes = pow(RESOLUTION_RATIO, iZoom) * gWidth / MaxRasterSize ; + double yRes = pow(RESOLUTION_RATIO, iZoom) * gHeight / MaxRasterSize ; + xRes = yRes = MAX(xRes, yRes); + + // pixel dimensions + nRasterXSize = static_cast(gWidth / xRes + 0.5); + nRasterYSize = static_cast(gHeight / yRes + 0.5); + + nBlockXSize = static_cast(MIN(MaxBlockSideSize , GetRasterXSize())); + nBlockYSize = static_cast(MIN(MaxBlockSideSize , GetRasterYSize())); + + CPLDebug( "MG4Lidar", "Opened zoom level %d with size %dx%d.\n", + iZoom, nRasterXSize, nRasterYSize ); + + + + /* -------------------------------------------------------------------- */ + /* Handle sample type and color space. */ + /* -------------------------------------------------------------------- */ + //eColorSpace = poImageReader->getColorSpace(); + /* -------------------------------------------------------------------- */ + /* Create band information objects. */ + /* -------------------------------------------------------------------- */ + size_t BandCount = 0; + CPLXMLNode* xmlBand = poXMLPCView; + bool bClass = false; + bool bNumRets = false; + bool bRetNum = false; + while ((xmlBand = CPLSearchXMLNode(xmlBand, "Band")) != NULL) + { + CPLXMLNode * xmlChannel = CPLSearchXMLNode(xmlBand, "Channel"); + const char * name = "Z"; + if (xmlChannel && xmlChannel->psChild && xmlChannel->psChild->pszValue) + name = xmlChannel->psChild->pszValue; + + BandCount++; + MG4LidarRasterBand *band = new MG4LidarRasterBand(this, BandCount, xmlBand, name); + SetBand(BandCount, band); + if (band->papszFilterClassCodes) bClass = true; + if (band->papszFilterReturnNums) bNumRets = true; + if (bNumRets && CSLFindString(band->papszFilterReturnNums, "Last")) bRetNum = true; + xmlBand = xmlBand->psNext; + } + nBands = BandCount; + int nSDKChannels = BandCount + (bClass ? 1 : 0) + (bNumRets ? 1 : 0) + (bRetNum ? 1 : 0); + if (BandCount == 0) // default if no bands specified. + { + MG4LidarRasterBand *band = new MG4LidarRasterBand(this, 1, NULL, CHANNEL_NAME_Z); + SetBand(1, band); + nBands = 1; + nSDKChannels = 1; + } + requiredChannels.init(nSDKChannels); + const ChannelInfo *ci = NULL; + for (int i=0; igetChannel(dynamic_cast(papoBands[i])->ChannelName); + requiredChannels.getChannel(i).init(*ci); + } + int iSDKChannels = nBands; + if (bClass) + { + ci = reader->getChannel(CHANNEL_NAME_ClassId); + requiredChannels.getChannel(iSDKChannels++).init(*ci); + } + if (bRetNum) + { + ci = reader->getChannel(CHANNEL_NAME_ReturnNum); + requiredChannels.getChannel(iSDKChannels++).init(*ci); + } + if (bNumRets) + { + ci = reader->getChannel(CHANNEL_NAME_NumReturns); + requiredChannels.getChannel(iSDKChannels++).init(*ci); + } + + return CE_None; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *MG4LidarDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +#ifdef notdef + CPLPushErrorHandler( CPLLoggingErrorHandler ); + CPLSetConfigOption( "CPL_DEBUG", "ON" ); + CPLSetConfigOption( "CPL_LOG", "C:\\ArcGIS_GDAL\\jdem\\cpl.log" ); +#endif + + if( poOpenInfo->fpL == NULL || poOpenInfo->nHeaderBytes < 32 ) + return NULL; + + CPLXMLNode *pxmlPCView; + + // do something sensible for .sid files without a .view + if( EQUALN((const char *) poOpenInfo->pabyHeader, "msid", 4) ) + { + int gen; + bool raster; + if( !Version::getMrSIDFileVersion(poOpenInfo->pabyHeader, gen, raster) + || raster ) + return NULL; + + CPLString xmltmp( "" ); + xmltmp.append( poOpenInfo->pszFilename ); + xmltmp.append( "" ); + pxmlPCView = CPLParseXMLString( xmltmp ); + if (pxmlPCView == NULL) + return NULL; + } + else + { + // support .view xml + if( !EQUALN((const char *) poOpenInfo->pabyHeader, "pszFilename ); + if (pxmlPCView == NULL) + return NULL; + } + + CPLXMLNode *psInputFile = CPLGetXMLNode( pxmlPCView, "InputFile" ); + if( psInputFile == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to find in document." ); + CPLDestroyXMLNode(pxmlPCView); + return NULL; + } + CPLString sidInputName(psInputFile->psChild->pszValue); + if (CPLIsFilenameRelative(sidInputName)) + { + CPLString dirname(CPLGetDirname(poOpenInfo->pszFilename)); + sidInputName = CPLString(CPLFormFilename(dirname, sidInputName, NULL)); + } + GDALOpenInfo openinfo(sidInputName, GA_ReadOnly); + + /* -------------------------------------------------------------------- */ + /* Check that particular fields in the header are valid looking */ + /* dates. */ + /* -------------------------------------------------------------------- */ + if( openinfo.fpL == NULL || openinfo.nHeaderBytes < 50 ) + { + CPLDestroyXMLNode(pxmlPCView); + return NULL; + } + + /* check magic */ + // to do: SDK should provide an API for this. + if( !EQUALN((const char *) openinfo.pabyHeader, "msid", 4) + || (*(openinfo.pabyHeader+4) != 0x4 )) // Generation 4. ... is there more we can check? + { + CPLDestroyXMLNode(pxmlPCView); + return NULL; + } + + /* -------------------------------------------------------------------- */ + /* Create a corresponding GDALDataset. */ + /* -------------------------------------------------------------------- */ + MG4LidarDataset *poDS; + + poDS = new MG4LidarDataset(); + poDS->poXMLPCView = pxmlPCView; + poDS->ownsXML = true; + poDS->reader = CropableMG4PointReader::create(); + poDS->fileIO = FileIO::create(); + + const char * pszClipExtent = CPLGetXMLValue(pxmlPCView, "ClipBox", NULL); + MG4PointReader *r = MG4PointReader::create(); + FileIO* io = FileIO::create(); + +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + bool bIsUTF8 = CSLTestBoolean( CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ); + wchar_t *pwszFilename = NULL; + if (bIsUTF8) + { + pwszFilename = CPLRecodeToWChar(openinfo.pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2); + if (!pwszFilename) + { + RELEASE(r); + RELEASE(io); + return NULL; + } + io->init(pwszFilename, "r"); + } + else + io->init(openinfo.pszFilename, "r"); +#else + io->init(openinfo.pszFilename, "r"); +#endif + r->init(io); + Bounds bounds = r->getBounds(); + if (pszClipExtent) + { + char ** papszClipExtent = CSLTokenizeString(pszClipExtent); + int cslcount = CSLCount(papszClipExtent); + if (cslcount != 4 && cslcount != 6) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Invalid ClipBox. Must contain 4 or 6 floats." ); + CSLDestroy(papszClipExtent); + delete poDS; + RELEASE(r); + RELEASE(io); +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if ( pwszFilename ) + CPLFree( pwszFilename ); +#endif + return NULL; + } + if (!EQUAL(papszClipExtent[0], "NOFILTER")) + bounds.x.min = CPLAtof(papszClipExtent[0]); + if (!EQUAL(papszClipExtent[1], "NOFILTER")) + bounds.x.max = CPLAtof(papszClipExtent[1]); + if (!EQUAL(papszClipExtent[2], "NOFILTER")) + bounds.y.min = CPLAtof(papszClipExtent[2]); + if (!EQUAL(papszClipExtent[3], "NOFILTER")) + bounds.y.max = CPLAtof(papszClipExtent[3]); + if (cslcount == 6) + { + if (!EQUAL(papszClipExtent[4], "NOFILTER")) + bounds.z.min = CPLAtof(papszClipExtent[4]); + if (!EQUAL(papszClipExtent[5], "NOFILTER")) + bounds.z.max = CPLAtof(papszClipExtent[5]); + } + CSLDestroy(papszClipExtent); + } + +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if (bIsUTF8) + { + poDS->fileIO->init(pwszFilename, "r"); + CPLFree(pwszFilename); + } + else + poDS->fileIO->init( openinfo.pszFilename, "r" ); +#else + poDS->fileIO->init( openinfo.pszFilename, "r" ); +#endif + dynamic_cast(poDS->reader)->init(poDS->fileIO, &bounds); + poDS->SetDescription(poOpenInfo->pszFilename); + poDS->TryLoadXML(); + + double pts_per_area = ((double)r->getNumPoints())/(r->getBounds().x.length()*r->getBounds().y.length()); + double average_pt_spacing = sqrt(1.0 / pts_per_area) ; + double cell_side = average_pt_spacing; + const char * pszCellSize = CPLGetXMLValue(pxmlPCView, "CellSize", NULL); + if (pszCellSize) + cell_side = CPLAtof(pszCellSize); + MaxRasterSize = MAX(poDS->reader->getBounds().x.length()/cell_side, poDS->reader->getBounds().y.length()/cell_side); + + RELEASE(r); + RELEASE(io); + + // Calculate the number of levels to expose. The highest level correpsonds to a + // raster size of 256 on the longest side. + double blocksizefactor = MaxRasterSize/256.0; + poDS->nOverviewCount = MAX(0, (int)(log(blocksizefactor)/log(RESOLUTION_RATIO) + 0.5)); + if ( poDS->nOverviewCount > 0 ) + { + int i; + + poDS->papoOverviewDS = (MG4LidarDataset **) + CPLMalloc( poDS->nOverviewCount * (sizeof(void*)) ); + + for ( i = 0; i < poDS->nOverviewCount; i++ ) + { + poDS->papoOverviewDS[i] = new MG4LidarDataset (); + poDS->papoOverviewDS[i]->reader = RETAIN(poDS->reader); + poDS->papoOverviewDS[i]->SetMetadata(poDS->GetMetadata("MG4Lidar"), "MG4Lidar"); + poDS->papoOverviewDS[i]->poXMLPCView = pxmlPCView; + poDS->papoOverviewDS[i]->OpenZoomLevel( i+1 ); + } + } + + /* -------------------------------------------------------------------- */ + /* Create object for the whole image. */ + /* -------------------------------------------------------------------- */ + poDS->OpenZoomLevel( 0 ); + + CPLDebug( "MG4Lidar", + "Opened image: width %d, height %d, bands %d", + poDS->nRasterXSize, poDS->nRasterYSize, poDS->nBands ); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + + if (! ((poDS->nBands == 1) || (poDS->nBands == 3))) + { + CPLDebug( "MG4Lidar", + "Inappropriate number of bands (%d)", poDS->nBands ); + delete poDS; + return(NULL); + } + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_MG4Lidar() */ +/************************************************************************/ + +void GDALRegister_MG4Lidar() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("MG4Lidar driver")) + return; + + if( GDALGetDriverByName( "MG4Lidar" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "MG4Lidar" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "MrSID Generation 4 / Lidar (.sid)" ); + // To do: update this help file in gdal.org + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_mrsid_lidar.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "view" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Float64" ); + + poDriver->pfnOpen = MG4LidarDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/mrsid_lidar/makefile.vc b/bazaar/plugin/gdal/frmts/mrsid_lidar/makefile.vc new file mode 100644 index 000000000..5767f95ee --- /dev/null +++ b/bazaar/plugin/gdal/frmts/mrsid_lidar/makefile.vc @@ -0,0 +1,33 @@ + +OBJ = gdal_MG4Lidar.obj + +PLUGIN_DLL = gdal_MG4Lidar.dll + +EXTRAFLAGS = $(MRSID_INCLUDE) -D_CRT_SECURE_NO_WARNINGS /Zc:wchar_t- + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt +!INCLUDE $(GDAL_ROOT)\frmts\mrsid\nmake.opt + +default: $(OBJ) + $(INSTALL) *.obj ..\o + +clean: + -del *.obj + -del *.dll + -del *.exp + -del *.lib + -del *.manifest + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) \ + $(GDAL_ROOT)/gdal_i.lib $(MRSID_LIB) $(GEOTIFF_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) + diff --git a/bazaar/plugin/gdal/frmts/msg/GNUmakefile b/bazaar/plugin/gdal/frmts/msg/GNUmakefile new file mode 100644 index 000000000..fef39bc57 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/GNUmakefile @@ -0,0 +1,20 @@ + +VPATH = PublicDecompWT/DISE/:PublicDecompWT/COMP/Src/:PublicDecompWT/COMP/WT/Src/ + +include ../../GDALmake.opt + +OBJ1=$(patsubst %.cpp,%.o,$(wildcard PublicDecompWT/COMP/WT/Src/*.cpp)) +OBJ2=$(patsubst %.cpp,%.o,$(wildcard PublicDecompWT/COMP/Src/*.cpp)) +OBJ3=$(patsubst %.cpp,%.o,$(wildcard PublicDecompWT/DISE/*.cpp)) +WTOBJ=$(subst PublicDecompWT/COMP/WT/Src/, ,$(OBJ1)) $(subst PublicDecompWT/COMP/Src/, , $(OBJ2)) $(subst PublicDecompWT/DISE/, , $(OBJ3)) + +OBJ = msgdataset.o xritheaderparser.o prologue.o msgcommand.o reflectancecalculator.o $(WTOBJ) + +CPPFLAGS = -I PublicDecompWT/DISE -I PublicDecompWT/COMP/WT/Inc -I PublicDecompWT/COMP/Inc -I. + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/msg/PublicDecompWTMakefiles.zip b/bazaar/plugin/gdal/frmts/msg/PublicDecompWTMakefiles.zip new file mode 100644 index 000000000..63d40cd43 Binary files /dev/null and b/bazaar/plugin/gdal/frmts/msg/PublicDecompWTMakefiles.zip differ diff --git a/bazaar/plugin/gdal/frmts/msg/frmt_msg.html b/bazaar/plugin/gdal/frmts/msg/frmt_msg.html new file mode 100644 index 000000000..7dcf69607 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/frmt_msg.html @@ -0,0 +1,172 @@ + + + + MSG -- Meteosat Second Generation + + + + +

    MSG -- Meteosat Second Generation

    + +

    This driver implemets reading support for Meteosat Second +Generation files. These are files with names like +H-000-MSG1__-MSG1________-HRV______-000007___-200405311115-C_, commonly +distributed into a folder structure with dates (e.g. 2004\05\31 for the file mentioned).

    + +

    The MSG files are wavelet-compressed. A decompression library licensed from EUMETSAT is needed +(Public Wavelet Transform Decompression Library Software, +shorter Wavelet Transform Software). +The software is compilable on Microsoft Windows, Linux and Solaris Operating Systems, and it works on 32 bits and 64 bits as well as mixed architectures. +It is a licensed software and available free to download upon acceptance of the WaveLet Transform Software Licence during electronic registration process. +

    + + +

    Part of the source of the file xrithdr_extr.c from XRIT2PIC is used to +parse MSG headers. This source is licensed under the terms of the GNU +General Public License as published by the Free Software Foundation. +

    + +

    This driver is not "enabled" by default. See Build Instructions on how to include this driver in your GDAL library.
    + +

    Build Instructions

    + + +

    Download the Eumetsat library for wavelet decompression. This is a +file named PublicDecompWT.zip. Extract the content in a subdirectory with +the same name (under frmts/msg).

    + +

    If you are building with Visual Studio +6.0, extract the .vc makefiles for the PublicDecompWT from the file +PublicDecompWTMakefiles.zip.

    + +

    If you build using the GNUMakefile, use --with-msg option to enable MSG driver:

    + +
    ./configure --with-msg
    + +

    If find that some adjustments are needed in the makefile and/or the msg +source files, please "commit" them. The Eumetsat library promises to be +"platform indepentent", but as we are working with Microsoft Windows +and Visual Studio 6.0, we did not have the facilities to check if the +rest of the msg driver is. Furthermore, apply steps 4 to 7 from the GDAL Driver Implementation Tutorial, section "Adding Driver to GDAL Tree". +

    + +

    MSG Wiki page is available at http://trac.osgeo.org/gdal/wiki/MSG. +It's dedicated to document building and usage hints +

    + +

    Specification of Source Dataset

    + +

    It is possible to select individual files for opening. In this case, +the driver will gather the files that correspond to the other strips of +the same image, and correctly compose the image. +

    + +

    Example with gdal_translate.exe:

    +
    +gdal_translate
    + C:\hrit_a\2004\05\31\H-000-MSG1__-MSG1________-HRV______-000008___-200405311115-C_
    + C:\output\myimage.tif
    +
    + +

    It is also possible to use the following syntax for opening the MSG files:

    + +
      +
    • MSG(source_folder,timestamp,(channel,channel,...,channel),use_root_folder,data_conversion,nr_cycles,step)
    • +
    • +
        +
      • source_folder: a path to a folder structure that contains the files
      • +
      • timestamp: 12 digits representing a date/time that identifies the 114 files of the 12 images of that time, e.g. 200501181200
      • +
      • channel: a number between 1 and 12, representing each of the 12 available channels. When only specifying one channel, the brackets are optional.
      • +
      • use_root_folder: Y to indicate that the files reside directly into the source_folder specified. N to indicate that the files reside in date structured folders: source_folder/YYYY/MM/DD
      • +
      • data_conversion:
      • +
      • +
          +
        • N to keep the original 10 bits DN values. The result is UInt16.
        • +
        • B to convert to 8 bits (handy for GIF and JPEG images). The result is Byte.
        • +
        • R to perform radiometric calibration and get the result in mW/m2/sr/(cm-1)-1. The result is Float32.
        • +
        • L to perform radiometric calibration and get the result in W/m2/sr/um. The result is Float32.
        • +
        • T to get the reflectance for the visible bands (1, 2, 3 and 12) and the temprature in degrees Kelvin for the infrared bands (all other bands). The result is Float32.
        • +
        +
      • +
      • nr_cycles: a number that indicates the number of consecutive cycles to be included in the same file (time series). These are appended as additional bands.
      • +
      • step: a number that indicates what is the stepsize when multiple cycles are chosen. E.g. every 15 minutes: step = 1, every 30 minutes: step = 2 etc. Note that the cycles are exactly 15 minutes apart, so you can not get images from times in-between (the step is an + integer).
      • +
      +
    • +
    + + +

    Examples with gdal_translate utility:

    + +

    Example call to fetch an MSG image of 200501181200 with bands 1, 2 and 3 in IMG format:

    +
    +gdal_translate -of HFA MSG(\\pc2133-24002\RawData\,200501181200,(1,2,3),N,N,1,1) d:\output\outfile.img
    +
    + +

    In JPG format, and converting the 10 bits image to 8 bits by dividing all values by 4:

    +
    +gdal_translate -of JPEG MSG(\\pc2133-24002\RawData\,200501181200,(1,2,3),N,B,1,1) d:\output\outfile.jpg
    +
    + +

    The same, but reordering the bands in the JPEG image to resemble RGB:

    +
    +gdal_translate -of JPEG MSG(\\pc2133-24002\RawData\,200501181200,(3,2,1),N,B,1,1) d:\output\outfile.jpg
    +
    + +

    Geotiff output, only band 2, original 10 bits values:

    +
    +gdal_translate -of GTiff MSG(\\pc2133-24002\RawData\,200501181200,2,N,N,1,1) d:\output\outfile.tif
    +
    + +

    Band 12:

    +
    +gdal_translate -of GTiff MSG(\\pc2133-24002\RawData\,200501181200,12,N,N,1,1) d:\output\outfile.tif
    +
    + +

    The same band 12 with radiometric calibration in mW/m2/sr/(cm-1)-1:

    +
    +gdal_translate -of GTiff MSG(\\pc2133-24002\RawData\,200501181200,12,N,R,1,1) d:\output\outfile.tif
    +
    + +

    Retrieve data from c:\hrit-data\2005\01\18 instead of \\pc2133-24002\RawData\... :

    +
    +gdal_translate -of GTiff MSG(c:\hrit-data\2005\01\18,200501181200,12,Y,R,1,1) d:\output\outfile.tif
    +
    + +

    Another option to do the same (note the difference in the Y and the N for the “use_root_folder” parameter:

    +
    +gdal_translate -of GTiff MSG(c:\hrit-data\,200501181200,12,N,R,1,1) d:\output\outfile.tif
    +
    + +

    Without radiometric calibration, but for 10 consecutive cycles (thus from 1200 to 1415):

    +
    +gdal_translate -of GTiff MSG(c:\hrit-data\,200501181200,12,N,N,10,1) d:\output\outfile.tif
    +
    + +

    10 cycles, but every hour (thus from 1200 to 2100):

    +
    +gdal_translate -of GTiff MSG(c:\hrit-data\,200501181200,12,N,N,10,4) d:\output\outfile.tif
    +
    + +

    10 cycles, every hour, and bands 3, 2 and 1:

    +
    +gdal_translate -of GTiff MSG(c:\hrit-data\,200501181200,(3,2,1),N,N,10,4) d:\output\outfile.tif
    +
    + + +

    Georeference and Projection

    + +

    The images are using the Geostationary Satellite View projection. Most +GIS packages don't recognize this projection (we only know of ILWIS +that does have this projection), but gdalwarp.exe can be used to +re-project the images.

    + +

    See Also:

    + +
      +
    • Implemented as gdal/frmts/msg/msgdataset.cpp.
    • +
    • http://www.eumetsat.int - European Organisation for the Exploitation of Meteorological Satellites
    • +
    + + + diff --git a/bazaar/plugin/gdal/frmts/msg/makefile.vc b/bazaar/plugin/gdal/frmts/msg/makefile.vc new file mode 100644 index 000000000..e631257a4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/makefile.vc @@ -0,0 +1,26 @@ + +OBJ = msgdataset.obj xritheaderparser.obj prologue.obj msgcommand.obj reflectancecalculator.obj + +EXTRAFLAGS = -I PublicDecompWT\DISE -I PublicDecompWT\COMP\WT\Inc -I PublicDecompWT\COMP\Inc + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) waveletlib + xcopy /D /Y *.obj ..\o + +waveletlib: + cd PublicDecompWT\COMP\WT\Src \ + && $(MAKE) /f makefile.vc \ + && cd ..\..\..\.. + cd PublicDecompWT\COMP\Src \ + && $(MAKE) /f makefile.vc \ + && cd ..\..\.. + cd PublicDecompWT\DISE \ + && $(MAKE) /f makefile.vc \ + && cd ..\.. + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/msg/msgcommand.cpp b/bazaar/plugin/gdal/frmts/msg/msgcommand.cpp new file mode 100644 index 000000000..84640a9bc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/msgcommand.cpp @@ -0,0 +1,465 @@ +/****************************************************************************** + * $Id: msgcommand.cpp 27370 2014-05-21 09:34:50Z rouault $ + * + * Purpose: Implementation of MSGCommand class. Parse the src_dataset + * string that is meant for the MSG driver. + * Author: Bas Retsios, retsios@itc.nl + * + ****************************************************************************** + * Copyright (c) 2004, ITC + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#include "msgcommand.h" +#include +#include +using namespace std; + +#ifdef _WIN32 +#define PATH_SEP '\\' +#else +#define PATH_SEP '/' +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +#define min(a,b) (((a)<(b))?(a):(b)) + +MSGCommand::MSGCommand() : + cDataConversion('N'), + iNrCycles(1), + sRootFolder(""), + sTimeStamp(""), + iStep(1), + fUseTimestampFolder(true) +{ + for (int i = 0; i < 12; ++i) + channel[i] = 0; +} + +MSGCommand::~MSGCommand() +{ + +} + +std::string MSGCommand::sTrimSpaces(std::string const& str) +{ + std::string::size_type iStart = 0; + + while ((iStart < str.length()) && (str[iStart] == ' ')) + ++iStart; + + std::string::size_type iLength = str.length() - iStart; + + while ((iLength > 0) && (str[iStart + iLength - 1] == ' ')) + --iLength; + + return str.substr(iStart, iLength); +} + +std::string MSGCommand::sNextTerm(std::string const& str, int & iPos) +{ + std::string::size_type iOldPos = iPos; + iPos = str.find(',', iOldPos); + iPos = min(iPos, str.find(')', iOldPos)); + if (iPos > iOldPos) + { + std::string sRet = str.substr(iOldPos, iPos - iOldPos); + if (str[iPos] != ')') + ++iPos; + return sTrimSpaces(sRet); + } + else + return ""; +} + +bool fTimeStampCorrect(std::string const& sTimeStamp) +{ + if (sTimeStamp.length() != 12) + return false; + + for (int i = 0; i < 12; ++i) + { + if (sTimeStamp[i] < '0' || sTimeStamp[i] > '9') + return false; + } + + return true; +} + +std::string MSGCommand::parse(std::string const& command_line) +{ + // expected: + // MSG(folder,timestamp,channel,in_same_folder,data_conversion,nr_cycles,step) + // or + // MSG(folder,timestamp,(channel,channel,...,channel),in_same_folder,data_conversion,nr_cycles,step) + // or + // \H-000-MSG1__-MSG1________..... + + std::string sErr (""); + + std::string sID = command_line.substr(0, 4); + if (sID.compare("MSG(") == 0) + { + int iPos = 4; // after bracket open + sRootFolder = sNextTerm(command_line, iPos); + if (sRootFolder.length() > 0) + { + if (sRootFolder[sRootFolder.length() - 1] != PATH_SEP) + sRootFolder += PATH_SEP; + sTimeStamp = sNextTerm(command_line, iPos); + if (fTimeStampCorrect(sTimeStamp)) + { + try // for eventual exceptions + { + while ((iPos < command_line.length()) && (command_line[iPos] == ' ')) + ++iPos; + if (command_line[iPos] == '(') + { + ++iPos; // skip the ( bracket + int i = 1; + std::string sChannel = sNextTerm(command_line, iPos); + while (command_line[iPos] != ')') + { + int iChan = atoi(sChannel.c_str()); + if (iChan >= 1 && iChan <= 12) + channel[iChan - 1] = i; + else + sErr = "Channel numbers must be between 1 and 12"; + sChannel = sNextTerm(command_line, iPos); + ++i; + } + int iChan = atoi(sChannel.c_str()); + if (iChan >= 1 && iChan <= 12) + channel[iChan - 1] = i; + else + sErr = "Channel numbers must be between 1 and 12"; + ++iPos; // skip the ) bracket + while ((iPos < command_line.length()) && (command_line[iPos] == ' ')) + ++iPos; + if (command_line[iPos] == ',') + ++iPos; + } + else + { + std::string sChannel = sNextTerm(command_line, iPos); + int iChan = atoi(sChannel.c_str()); + if (iChan >= 1 && iChan <= 12) + channel[iChan - 1] = 1; + else + sErr = "Channel numbers must be between 1 and 12"; + } + std::string sInRootFolder = sNextTerm(command_line, iPos); + if ((sInRootFolder.compare("N") != 0) && (sInRootFolder.compare("Y") != 0)) + sErr = "Please specify N for data that is in a date dependent folder or Y for data that is in specified folder."; + else + fUseTimestampFolder = (sInRootFolder.compare("N") == 0); + std::string sDataConversion = sNextTerm(command_line, iPos); + cDataConversion = (sDataConversion.length()>0)?sDataConversion[0]:'N'; + std::string sNrCycles = sNextTerm(command_line, iPos); + iNrCycles = atoi(sNrCycles.c_str()); + if (iNrCycles < 1) + iNrCycles = 1; + std::string sStep = sNextTerm(command_line, iPos); + iStep = atoi(sStep.c_str()); + if (iStep < 1) + iStep = 1; + while ((iPos < command_line.length()) && (command_line[iPos] == ' ')) + ++iPos; + // additional correctness checks + if (command_line[iPos] != ')') + sErr = "Invalid syntax. Please review the MSG(...) statement."; + else if ((iNrChannels() > 1) && (channel[11] != 0)) + sErr = "It is not possible to combine channel 12 (HRV) with the other channels."; + else if (iNrChannels() == 0 && sErr.length() == 0) + sErr = "At least one channel should be specified."; + else if ((cDataConversion != 'N') && (cDataConversion != 'B') && (cDataConversion != 'R') && (cDataConversion != 'L') && (cDataConversion != 'T')) + sErr = "Please specify N(o change), B(yte conversion), R(adiometric calibration), L(radiometric using central wavelength) or T(reflectance or temperature) for data conversion."; + } + catch(...) + { + sErr = "Invalid syntax. Please review the MSG(...) statement."; + } + } + else + sErr = "Timestamp should be exactly 12 digits."; + } + else + sErr = "A folder must be filled in indicating the root of the image data folders."; + } + else if (command_line.find("H-000-MSG") >= 0) + { + int iPos = command_line.find("H-000-MSG"); + if ((command_line.length() - iPos) == 61) + { + fUseTimestampFolder = false; + sRootFolder = command_line.substr(0, iPos); + sTimeStamp = command_line.substr(iPos + 46, 12); + if (fTimeStampCorrect(sTimeStamp)) + { + int iChan = iChannel(command_line.substr(iPos + 26, 9)); + if (iChan >= 1 && iChan <= 12) + { + channel[iChan - 1] = 1; + cDataConversion = 'N'; + iNrCycles = 1; + iStep = 1; + } + else + sErr = "Channel numbers must be between 1 and 12"; + } + else + sErr = "Timestamp should be exactly 12 digits."; + } + else + sErr = "-"; // the source data set it is not for the MSG driver + } + else + sErr = "-"; // the source data set it is not for the MSG driver + return sErr; +} + +int MSGCommand::iNrChannels() +{ + int iRet = 0; + for (int i=0; i<12; ++i) + if (channel[i] != 0) + ++iRet; + + return iRet; +} + +int MSGCommand::iChannel(int iChannelNumber) +{ + // return the iChannelNumber-th channel + // iChannelNumber is a value between 1 and 12 + // note that channels are ordered. their order number is the value in the array + // As we can't combine channel 12 with channels 1 to 11, it does not make sense to inquire for iNr == 12 + int iRet = 0; + if (iChannelNumber <= iNrChannels()) + { + while ((iRet < 12) && (channel[iRet] != iChannelNumber)) + ++iRet; + } + + // will return a number between 1 and 12 + return (iRet + 1); +} + +int MSGCommand::iNrStrips(int iChannel) +{ + if (iChannel == 12) + return 24; + else if (iChannel >= 1 && iChannel <= 11) + return 8; + else + return 0; +} + +int MSGCommand::iChannel(std::string const& sChannel) +{ + if (sChannel.compare("VIS006___") == 0) + return 1; + else if (sChannel.compare("VIS008___") == 0) + return 2; + else if (sChannel.compare("IR_016___") == 0) + return 3; + else if (sChannel.compare("IR_039___") == 0) + return 4; + else if (sChannel.compare("WV_062___") == 0) + return 5; + else if (sChannel.compare("WV_073___") == 0) + return 6; + else if (sChannel.compare("IR_087___") == 0) + return 7; + else if (sChannel.compare("IR_097___") == 0) + return 8; + else if (sChannel.compare("IR_108___") == 0) + return 9; + else if (sChannel.compare("IR_120___") == 0) + return 10; + else if (sChannel.compare("IR_134___") == 0) + return 11; + else if (sChannel.compare("HRV______") == 0) + return 12; + else + return 0; +} + +std::string MSGCommand::sChannel(int iChannel) +{ + switch (iChannel) + { + case 1: + return "VIS006___"; + break; + case 2: + return "VIS008___"; + break; + case 3: + return "IR_016___"; + break; + case 4: + return "IR_039___"; + break; + case 5: + return "WV_062___"; + break; + case 6: + return "WV_073___"; + break; + case 7: + return "IR_087___"; + break; + case 8: + return "IR_097___"; + break; + case 9: + return "IR_108___"; + break; + case 10: + return "IR_120___"; + break; + case 11: + return "IR_134___"; + break; + case 12: + return "HRV______"; + break; + default: + return "_________"; + break; + } +} + +std::string MSGCommand::sTimeStampToFolder(std::string & sTimeStamp) +{ + std::string sYear (sTimeStamp.substr(0,4)); + std::string sMonth (sTimeStamp.substr(4, 2)); + std::string sDay (sTimeStamp.substr(6, 2)); + return (sYear + PATH_SEP + sMonth + PATH_SEP + sDay + PATH_SEP); +} + +int MSGCommand::iDaysInMonth(int iMonth, int iYear) +{ + int iDays; + + if ((iMonth == 4) || (iMonth == 6) || (iMonth == 9) || (iMonth == 11)) + iDays = 30; + else if (iMonth == 2) + { + iDays = 28; + if (iYear % 100 == 0) // century year + { + if (iYear % 400 == 0) // century leap year + ++iDays; + } + else + { + if (iYear % 4 == 0) // normal leap year + ++iDays; + } + } + else + iDays = 31; + + return iDays; +} + +std::string MSGCommand::sCycle(int iCycle) +{ + // find nth full quarter + // e.g. for n = 1, 200405311114 should result in 200405311115 + // 200405311115 should result in 200405311130 + // 200405311101 should result in 200405311115 + // 200412312345 should result in 200501010000 + + std::string sYear (sTimeStamp.substr(0, 4)); + std::string sMonth (sTimeStamp.substr(4, 2)); + std::string sDay (sTimeStamp.substr(6, 2)); + std::string sHours (sTimeStamp.substr(8, 2)); + std::string sMins (sTimeStamp.substr(10, 2)); + + int iYear = atoi(sYear.c_str()); + int iMonth = atoi(sMonth.c_str()); + int iDay = atoi(sDay.c_str()); + int iHours = atoi(sHours.c_str()); + int iMins = atoi(sMins.c_str()); + + iMins += (iCycle - 1)*15*iStep; + + // round off the mins found down to a multiple of 15 mins + iMins = ((int)(iMins / 15)) * 15; + // now handle the whole chain back to the year ... + while (iMins >= 60) + { + iMins -= 60; + ++iHours; + } + while (iHours >= 24) + { + iHours -= 24; + ++iDay; + } + while (iDay > iDaysInMonth(iMonth, iYear)) + { + iDay -= iDaysInMonth(iMonth, iYear); + ++iMonth; + } + while (iMonth > 12) + { + iMonth -= 12; + ++iYear; + } + + char sRet [100]; + sprintf(sRet, "%.4d%.2d%.2d%.2d%.2d", iYear, iMonth, iDay, iHours, iMins); + + return sRet; +} + +std::string MSGCommand::sFileName(int iSatellite, int iSequence, int iStrip) +{ + int iNr = iNrChannels(); + int iChannelNumber = 1 + (iSequence - 1) % iNr;; + int iCycle = 1 + (iSequence - 1) / iNr; + char sRet [4096]; + std::string siThCycle (sCycle(iCycle)); + if (fUseTimestampFolder) + sprintf(sRet, "%s%sH-000-MSG%d__-MSG%d________-%s-%.6d___-%s-C_", sRootFolder.c_str(), sTimeStampToFolder(siThCycle).c_str(), iSatellite, iSatellite, sChannel(iChannel(iChannelNumber)).c_str(), iStrip, siThCycle.c_str()); + else + sprintf(sRet, "%sH-000-MSG%d__-MSG%d________-%s-%.6d___-%s-C_", sRootFolder.c_str(), iSatellite, iSatellite, sChannel(iChannel(iChannelNumber)).c_str(), iStrip, siThCycle.c_str()); + return sRet; +} + +std::string MSGCommand::sPrologueFileName(int iSatellite, int iSequence) +{ + int iCycle = 1 + (iSequence - 1) / iNrChannels(); + char sRet [4096]; + std::string siThCycle (sCycle(iCycle)); + if (fUseTimestampFolder) + sprintf(sRet, "%s%sH-000-MSG%d__-MSG%d________-_________-PRO______-%s-__", sRootFolder.c_str(), sTimeStampToFolder(siThCycle).c_str(), iSatellite, iSatellite, siThCycle.c_str()); + else + sprintf(sRet, "%sH-000-MSG%d__-MSG%d________-_________-PRO______-%s-__", sRootFolder.c_str(), iSatellite, iSatellite, siThCycle.c_str()); + return sRet; +} + diff --git a/bazaar/plugin/gdal/frmts/msg/msgcommand.h b/bazaar/plugin/gdal/frmts/msg/msgcommand.h new file mode 100644 index 000000000..2df7ca0b4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/msgcommand.h @@ -0,0 +1,68 @@ +/****************************************************************************** + * $Id: msgcommand.h 15085 2008-07-31 13:41:31Z mloskot $ + * + * Purpose: Interface of MSGCommand class. Parse the src_dataset string + * that is meant for the MSG driver. + * Author: Bas Retsios, retsios@itc.nl + * + ****************************************************************************** + * Copyright (c) 2004, ITC + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef GDAL_MSG_MSGCOMMAND_H_INCLUDED +#define GDAL_MSG_MSGCOMMAND_H_INCLUDED + +#include + +class MSGCommand +{ +public: + MSGCommand(); + virtual ~MSGCommand(); + + std::string parse(std::string const& command_line); + std::string sFileName(int iSatellite, int iSequence, int iStrip); + std::string sPrologueFileName(int iSatellite, int iSequence); + std::string sCycle(int iCycle); + int iNrChannels(); + int iChannel(int iNr); + + static int iNrStrips(int iChannel); + + char cDataConversion; + int iNrCycles; + int channel[12]; + +private: + std::string sTrimSpaces(std::string const& str); + std::string sNextTerm(std::string const& str, int & iPos); + int iDaysInMonth(int iMonth, int iYear); + static std::string sChannel(int iChannel); + static int iChannel(std::string const& sChannel); + static std::string sTimeStampToFolder(std::string& sTimeStamp); + std::string sRootFolder; + std::string sTimeStamp; + int iStep; + bool fUseTimestampFolder; +}; + +#endif // GDAL_MSG_MSGCOMMAND_H_INCLUDED + diff --git a/bazaar/plugin/gdal/frmts/msg/msgdataset.cpp b/bazaar/plugin/gdal/frmts/msg/msgdataset.cpp new file mode 100644 index 000000000..0f0271e7f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/msgdataset.cpp @@ -0,0 +1,762 @@ +/****************************************************************************** + * $Id: msgdataset.cpp 27477 2014-06-28 15:23:40Z rouault $ + * + * Project: MSG Driver + * Purpose: GDALDataset driver for MSG translator for read support. + * Author: Bas Retsios, retsios@itc.nl + * + ****************************************************************************** + * Copyright (c) 2004, ITC + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#include "msgdataset.h" +#include "prologue.h" +#include "xritheaderparser.h" +#include "reflectancecalculator.h" + +#include "PublicDecompWT/COMP/WT/Inc/CWTDecoder.h" +#include "PublicDecompWT/DISE/CDataField.h" // Util namespace + +#include + +#if _MSC_VER > 1000 +#include +#else +#include +#endif + +const double MSGDataset::rCentralWvl[12] = {0.635, 0.810, 1.640, 3.900, 6.250, 7.350, 8.701, 9.660, 10.800, 12.000, 13.400, 0.750}; +const double MSGDataset::rVc[12] = {-1, -1, -1, 2569.094, 1598.566, 1362.142, 1149.083, 1034.345, 930.659, 839.661, 752.381, -1}; +const double MSGDataset::rA[12] = {-1, -1, -1, 0.9959, 0.9963, 0.9991, 0.9996, 0.9999, 0.9983, 0.9988, 0.9981, -1}; +const double MSGDataset::rB[12] = {-1, -1, -1, 3.471, 2.219, 0.485, 0.181, 0.060, 0.627, 0.397, 0.576, -1}; +int MSGDataset::iCurrentSatellite = 1; // satellite number 1,2,3,4 for MSG1, MSG2, MSG3 and MSG4 + +#define MAX_SATELLITES 4 + +/************************************************************************/ +/* MSGDataset() */ +/************************************************************************/ + +MSGDataset::MSGDataset() + +{ + poTransform = NULL; + pszProjection = CPLStrdup(""); + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~MSGDataset() */ +/************************************************************************/ + +MSGDataset::~MSGDataset() + +{ + if( poTransform != NULL ) + delete poTransform; + + CPLFree( pszProjection ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *MSGDataset::GetProjectionRef() + +{ + return ( pszProjection ); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr MSGDataset::SetProjection( const char * pszNewProjection ) +{ + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszNewProjection ); + + return CE_None; + +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr MSGDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return( CE_None ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *MSGDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Does this look like a MSG file */ +/* -------------------------------------------------------------------- */ + //if( poOpenInfo->fp == NULL) + // return NULL; + // Do not touch the fp .. it will close by itself if not null after we return (whether it is recognized as HRIT or not) + + std::string command_line (poOpenInfo->pszFilename); + + MSGCommand command; + std::string sErr = command.parse(command_line); + if (sErr.length() > 0) + { + if (sErr.compare("-") != 0) // this driver does not recognize this format .. be silent and return false so that another driver can try + CPLError( CE_Failure, CPLE_AppDefined, (sErr+"\n").c_str() ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Read the prologue. */ +/* -------------------------------------------------------------------- */ + Prologue pp; + + std::string sPrologueFileName = command.sPrologueFileName(iCurrentSatellite, 1); + bool fPrologueExists = (access(sPrologueFileName.c_str(), 0) == 0); + + // Make sure we're testing for MSG1,2,3 or 4 exactly once, start with the most recently used, and remember it in the static member for the next round. + if (!fPrologueExists) + { + iCurrentSatellite = 1 + iCurrentSatellite % MAX_SATELLITES; + sPrologueFileName = command.sPrologueFileName(iCurrentSatellite, 1); + fPrologueExists = (access(sPrologueFileName.c_str(), 0) == 0); + int iTries = 2; + while (!fPrologueExists && (iTries < MAX_SATELLITES)) + { + iCurrentSatellite = 1 + iCurrentSatellite % MAX_SATELLITES; + sPrologueFileName = command.sPrologueFileName(iCurrentSatellite, 1); + fPrologueExists = (access(sPrologueFileName.c_str(), 0) == 0); + ++iTries; + } + if (!fPrologueExists) // assume missing prologue file, keep original satellite number + { + iCurrentSatellite = 1 + iCurrentSatellite % MAX_SATELLITES; + sPrologueFileName = command.sPrologueFileName(iCurrentSatellite, 1); + } + } + + if (fPrologueExists) + { + std::ifstream p_file(sPrologueFileName.c_str(), std::ios::in|std::ios::binary); + XRITHeaderParser xhp (p_file); + if (xhp.isValid() && xhp.isPrologue()) + pp.read(p_file); + p_file.close(); + } + else + { + std::string sErr = "The prologue of the data set could not be found at the location specified:\n" + sPrologueFileName + "\n"; + CPLError( CE_Failure, CPLE_AppDefined, + sErr.c_str() ); + return FALSE; + } + + +// We're confident the string is formatted as an MSG command_line + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + + MSGDataset *poDS; + poDS = new MSGDataset(); + poDS->command = command; // copy it + +/* -------------------------------------------------------------------- */ +/* Capture raster size from MSG prologue and submit it to GDAL */ +/* -------------------------------------------------------------------- */ + + if (command.channel[11] != 0) // the HRV band + { + poDS->nRasterXSize = pp.idr()->ReferenceGridHRV->NumberOfColumns; + poDS->nRasterYSize = abs(pp.idr()->PlannedCoverageHRV->UpperNorthLinePlanned - pp.idr()->PlannedCoverageHRV->LowerSouthLinePlanned) + 1; + } + else + { + poDS->nRasterXSize = abs(pp.idr()->PlannedCoverageVIS_IR->WesternColumnPlanned - pp.idr()->PlannedCoverageVIS_IR->EasternColumnPlanned) + 1; + poDS->nRasterYSize = abs(pp.idr()->PlannedCoverageVIS_IR->NorthernLinePlanned - pp.idr()->PlannedCoverageVIS_IR->SouthernLinePlanned) + 1; + } + +/* -------------------------------------------------------------------- */ +/* Set Georeference Information */ +/* -------------------------------------------------------------------- */ + + double rPixelSizeX; + double rPixelSizeY; + double rMinX; + double rMaxY; + + if (command.channel[11] != 0) + { + rPixelSizeX = 1000 * pp.idr()->ReferenceGridHRV->ColumnDirGridStep; + rPixelSizeY = 1000 * pp.idr()->ReferenceGridHRV->LineDirGridStep; + rMinX = -rPixelSizeX * (pp.idr()->ReferenceGridHRV->NumberOfColumns / 2.0); // assumption: (0,0) falls in centre + rMaxY = rPixelSizeY * (pp.idr()->ReferenceGridHRV->NumberOfLines / 2.0); + } + else + { + rPixelSizeX = 1000 * pp.idr()->ReferenceGridVIS_IR->ColumnDirGridStep; + rPixelSizeY = 1000 * pp.idr()->ReferenceGridVIS_IR->LineDirGridStep; + rMinX = -rPixelSizeX * (pp.idr()->ReferenceGridVIS_IR->NumberOfColumns / 2.0); // assumption: (0,0) falls in centre + rMaxY = rPixelSizeY * (pp.idr()->ReferenceGridVIS_IR->NumberOfLines / 2.0); + } + poDS->adfGeoTransform[0] = rMinX; + poDS->adfGeoTransform[3] = rMaxY; + poDS->adfGeoTransform[1] = rPixelSizeX; + poDS->adfGeoTransform[5] = -rPixelSizeY; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + +/* -------------------------------------------------------------------- */ +/* Set Projection Information */ +/* -------------------------------------------------------------------- */ + + poDS->oSRS.SetGEOS( 0, 35785831, 0, 0 ); + poDS->oSRS.SetWellKnownGeogCS( "WGS84" ); // Temporary line to satisfy ERDAS (otherwise the ellips is "unnamed"). Eventually this should become the custom a and b ellips (CGMS). + CPLFree( poDS->pszProjection ); + poDS->oSRS.exportToWkt( &(poDS->pszProjection) ); + + // The following are 3 different try-outs for also setting the ellips a and b parameters. + // We leave them out for now however because this does not work. In gdalwarp, when choosing some + // specific target SRS, the result is an error message: + // + // ERROR 1: geocentric transformation missing z or ellps + // ERROR 1: GDALWarperOperation::ComputeSourceWindow() failed because + // the pfnTransformer failed. + // + // I can't explain the reason for the message at this time (could be a problem in the way the SRS is set here, + // but also a bug in Proj.4 or GDAL. + /* + oSRS.SetGeogCS( NULL, NULL, NULL, 6378169, 295.488065897, NULL, 0, NULL, 0 ); + + oSRS.SetGeogCS( "unnamed ellipse", "unknown", "unnamed", 6378169, 295.488065897, "Greenwich", 0.0); + + if( oSRS.importFromProj4("+proj=geos +h=35785831 +a=6378169 +b=6356583.8") == OGRERR_NONE ) + { + oSRS.exportToWkt( &(poDS->pszProjection) ); + } + */ + +/* -------------------------------------------------------------------- */ +/* Create a transformer to LatLon (only for Reflectance calculation) */ +/* -------------------------------------------------------------------- */ + + char *pszLLTemp; + char *pszLLTemp_bak; + + (poDS->oSRS.GetAttrNode("GEOGCS"))->exportToWkt(&pszLLTemp); + pszLLTemp_bak = pszLLTemp; // importFromWkt() changes the pointer + poDS->oLL.importFromWkt(&pszLLTemp); + CPLFree( pszLLTemp_bak ); + + poDS->poTransform = OGRCreateCoordinateTransformation( &(poDS->oSRS), &(poDS->oLL) ); + +/* -------------------------------------------------------------------- */ +/* Set the radiometric calibration parameters. */ +/* -------------------------------------------------------------------- */ + + memcpy( poDS->rCalibrationOffset, pp.rpr()->Cal_Offset, sizeof(double) * 12 ); + memcpy( poDS->rCalibrationSlope, pp.rpr()->Cal_Slope, sizeof(double) * 12 ); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = command.iNrChannels()*command.iNrCycles; + for( int iBand = 0; iBand < poDS->nBands; iBand++ ) + { + poDS->SetBand( iBand+1, new MSGRasterBand( poDS, iBand+1 ) ); + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + delete poDS; + CPLError( CE_Failure, CPLE_NotSupported, + "The MSG driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + return( poDS ); +} + +/************************************************************************/ +/* MSGRasterBand() */ +/************************************************************************/ + +const double MSGRasterBand::rRTOA[12] = {20.76, 23.24, 19.85, -1, -1, -1, -1, -1, -1, -1, -1, 25.11}; + +MSGRasterBand::MSGRasterBand( MSGDataset *poDS, int nBand ) +: fScanNorth(false) +, iLowerShift(0) +, iSplitLine(0) +, iLowerWestColumnPlanned(0) + +{ + this->poDS = poDS; + this->nBand = nBand; + + // Find if we're dealing with MSG1, MSG2, MSG3 or MSG4 + // Doing this per band is the only way to guarantee time-series when the satellite is changed + + std::string sPrologueFileName = poDS->command.sPrologueFileName(poDS->iCurrentSatellite, nBand); + bool fPrologueExists = (access(sPrologueFileName.c_str(), 0) == 0); + + // Make sure we're testing for MSG1,2,3 or 4 exactly once, start with the most recently used, and remember it in the static member for the next round. + if (!fPrologueExists) + { + poDS->iCurrentSatellite = 1 + poDS->iCurrentSatellite % MAX_SATELLITES; + sPrologueFileName = poDS->command.sPrologueFileName(poDS->iCurrentSatellite, nBand); + fPrologueExists = (access(sPrologueFileName.c_str(), 0) == 0); + int iTries = 2; + while (!fPrologueExists && (iTries < MAX_SATELLITES)) + { + poDS->iCurrentSatellite = 1 + poDS->iCurrentSatellite % MAX_SATELLITES; + sPrologueFileName = poDS->command.sPrologueFileName(poDS->iCurrentSatellite, nBand); + fPrologueExists = (access(sPrologueFileName.c_str(), 0) == 0); + ++iTries; + } + if (!fPrologueExists) // assume missing prologue file, keep original satellite number + { + poDS->iCurrentSatellite = 1 + poDS->iCurrentSatellite % MAX_SATELLITES; + sPrologueFileName = poDS->command.sPrologueFileName(poDS->iCurrentSatellite, nBand); + } + } + + iSatellite = poDS->iCurrentSatellite; // From here on, the satellite that corresponds to this band is settled to the current satellite + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = poDS->GetRasterYSize(); + +/* -------------------------------------------------------------------- */ +/* Open an input file and capture the header for the nr. of bits. */ +/* -------------------------------------------------------------------- */ + int iStrip = 1; + int iChannel = poDS->command.iChannel(1 + ((nBand - 1) % poDS->command.iNrChannels())); + std::string input_file = poDS->command.sFileName(iSatellite, nBand, iStrip); + while ((access(input_file.c_str(), 0) != 0) && (iStrip <= poDS->command.iNrStrips(iChannel))) // compensate for missing strips + input_file = poDS->command.sFileName(iSatellite, nBand, ++iStrip); + + if (iStrip <= poDS->command.iNrStrips(iChannel)) + { + std::ifstream i_file (input_file.c_str(), std::ios::in|std::ios::binary); + + if (i_file.good()) + { + XRITHeaderParser xhp (i_file); + + if (xhp.isValid()) + { + // Data type is either 8 or 16 bits .. we tell this to GDAL here + eDataType = GDT_Byte; // default .. always works + if (xhp.nrBitsPerPixel() > 8) + { + if (poDS->command.cDataConversion == 'N') + eDataType = GDT_UInt16; // normal case: MSG 10 bits data + else if (poDS->command.cDataConversion == 'B') + eDataType = GDT_Byte; // output data type Byte + else + eDataType = GDT_Float32; // Radiometric calibration + } + + // make IReadBlock be called once per file + nBlockYSize = xhp.nrRows(); + + // remember the scan direction + + fScanNorth = xhp.isScannedNorth(); + } + } + + i_file.close(); + } + else if (nBand > 1) + { + // missing entire band .. take data from first band + MSGRasterBand* pFirstRasterBand = (MSGRasterBand*)poDS->GetRasterBand(1); + eDataType = pFirstRasterBand->eDataType; + nBlockYSize = pFirstRasterBand->nBlockYSize; + fScanNorth = pFirstRasterBand->fScanNorth; + } + else // also first band is missing .. do something for fail-safety + { + eDataType = GDT_Byte; // default .. always works + if (poDS->command.cDataConversion == 'N') + eDataType = GDT_UInt16; // normal case: MSG 10 bits data + else if (poDS->command.cDataConversion == 'B') + eDataType = GDT_Byte; // output data type Byte + else + eDataType = GDT_Float32; // Radiometric calibration + + // nBlockYSize : default + // fScanNorth : default + + } +/* -------------------------------------------------------------------- */ +/* For the HRV band, read the prologue for shift and splitline. */ +/* -------------------------------------------------------------------- */ + + if (iChannel == 12) + { + if (fPrologueExists) + { + std::ifstream p_file(sPrologueFileName.c_str(), std::ios::in|std::ios::binary); + XRITHeaderParser xhp(p_file); + Prologue pp; + if (xhp.isValid() && xhp.isPrologue()) + pp.read(p_file); + p_file.close(); + + iLowerShift = pp.idr()->PlannedCoverageHRV->UpperWestColumnPlanned - pp.idr()->PlannedCoverageHRV->LowerWestColumnPlanned; + iSplitLine = abs(pp.idr()->PlannedCoverageHRV->UpperNorthLinePlanned - pp.idr()->PlannedCoverageHRV->LowerNorthLinePlanned) + 1; // without the "+ 1" the image of 1-Jan-2005 splits incorrectly + iLowerWestColumnPlanned = pp.idr()->PlannedCoverageHRV->LowerWestColumnPlanned; + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize the ReflectanceCalculator with the band-dependent info. */ +/* -------------------------------------------------------------------- */ + + int iCycle = 1 + (nBand - 1) / poDS->command.iNrChannels(); + std::string sTimeStamp = poDS->command.sCycle(iCycle); + + m_rc = new ReflectanceCalculator(sTimeStamp, rRTOA[iChannel-1]); +} + +/************************************************************************/ +/* ~MSGRasterBand() */ +/************************************************************************/ +MSGRasterBand::~MSGRasterBand() +{ + delete m_rc; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ +CPLErr MSGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + + MSGDataset *poGDS = (MSGDataset *) poDS; + + + int iBytesPerPixel = 1; + if (eDataType == GDT_UInt16) + iBytesPerPixel = 2; + else if (eDataType == GDT_Float32) + iBytesPerPixel = 4; +/* -------------------------------------------------------------------- */ +/* Calculate the correct input file name based on nBlockYOff */ +/* -------------------------------------------------------------------- */ + + int strip_number; + int iChannel = poGDS->command.iChannel(1 + ((nBand - 1) % poGDS->command.iNrChannels())); + + if (fScanNorth) + strip_number = nBlockYOff + 1; + else + strip_number = poGDS->command.iNrStrips(iChannel) - nBlockYOff; + + std::string strip_input_file = poGDS->command.sFileName(iSatellite, nBand, strip_number); + +/* -------------------------------------------------------------------- */ +/* Open the input file */ +/* -------------------------------------------------------------------- */ + if (access(strip_input_file.c_str(), 0) == 0) // does it exist? + { + std::ifstream i_file (strip_input_file.c_str(), std::ios::in|std::ios::binary); + + if (i_file.good()) + { + XRITHeaderParser xhp (i_file); + + if (xhp.isValid()) + { + std::vector QualityInfo; + unsigned short chunck_height = xhp.nrRows(); + unsigned short chunck_bpp = xhp.nrBitsPerPixel(); + unsigned short chunck_width = xhp.nrColumns(); + unsigned __int8 NR = (unsigned __int8)chunck_bpp; + unsigned int nb_ibytes = xhp.dataSize(); + int iShift = 0; + bool fSplitStrip = false; // in the split strip the "shift" only happens before the split "row" + int iSplitRow = 0; + if (iChannel == 12) + { + iSplitRow = iSplitLine % xhp.nrRows(); + int iSplitBlock = iSplitLine / xhp.nrRows(); + fSplitStrip = (nBlockYOff == iSplitBlock); // in the split strip the "shift" only happens before the split "row" + + // When iLowerShift > 0, the lower HRV image is shifted to the right + // When iLowerShift < 0, the lower HRV image is shifted to the left + // The available raster may be wider than needed, so that time series don't fall outside the raster. + + if (nBlockYOff <= iSplitBlock) + iShift = -iLowerShift; + // iShift < 0 means upper image moves to the left + // iShift > 0 means upper image moves to the right + } + + std::auto_ptr< unsigned char > ibuf( new unsigned char[nb_ibytes]); + + if (ibuf.get() == 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Not enough memory to perform wavelet decompression\n"); + return CE_Failure; + } + + i_file.read( (char *)(ibuf.get()), nb_ibytes); + + Util::CDataFieldCompressedImage img_compressed(ibuf.release(), + nb_ibytes*8, + (unsigned char)chunck_bpp, + chunck_width, + chunck_height ); + + Util::CDataFieldUncompressedImage img_uncompressed; + + //**************************************************** + //*** Here comes the wavelets decompression routine + COMP::DecompressWT(img_compressed, NR, img_uncompressed, QualityInfo); + //**************************************************** + + // convert: + // Depth: + // 8 bits -> 8 bits + // 10 bits -> 16 bits (img_uncompressed contains the 10 bits data in packed form) + // Geometry: + // chunck_width x chunck_height to nBlockXSize x nBlockYSize + + // cases: + // combination of the following: + // - scan direction can be north or south + // - eDataType can be GDT_Byte, GDT_UInt16 or GDT_Float32 + // - nBlockXSize == chunck_width or nBlockXSize > chunck_width + // - when nBlockXSize > chunck_width, fSplitStrip can be true or false + // we won't distinguish the following cases: + // - NR can be == 8 or != 8 + // - when nBlockXSize > chunck_width, iShift iMinCOff-iMaxCOff <= iShift <= 0 + + int nBlockSize = nBlockXSize * nBlockYSize; + int y = chunck_width * chunck_height; + int iStep = -1; + if (fScanNorth) // image is the other way around + { + y = -1; // See how y is used below: += happens first, the result is used in the [] + iStep = 1; + } + + COMP::CImage cimg (img_uncompressed); // unpack + if (eDataType == GDT_Byte) + { + if (nBlockXSize == chunck_width) // optimized version + { + if (poGDS->command.cDataConversion == 'B') + { + for( int i = 0; i < nBlockSize; ++i ) + ((GByte *)pImage)[i] = cimg.Get()[y+=iStep] / 4; + } + else + { + for( int i = 0; i < nBlockSize; ++i ) + ((GByte *)pImage)[i] = cimg.Get()[y+=iStep]; + } + } + else + { + // initialize to 0's (so that it does not have to be done in an 'else' statement ) + memset(pImage, 0, nBlockXSize * nBlockYSize * iBytesPerPixel); + if (poGDS->command.cDataConversion == 'B') + { + for( int j = 0; j < chunck_height; ++j ) // assumption: nBlockYSize == chunck_height + { + int iXOffset = j * nBlockXSize + iShift; + iXOffset += nBlockXSize - iLowerWestColumnPlanned - 1; // Position the HRV part in the frame; -1 to compensate the pre-increment in the for-loop + if (fSplitStrip && (j >= iSplitRow)) // In splitstrip, below splitline, thus do not shift!! + iXOffset -= iShift; + for (int i = 0; i < chunck_width; ++i) + ((GByte *)pImage)[++iXOffset] = cimg.Get()[y+=iStep] / 4; + } + } + else + { + for( int j = 0; j < chunck_height; ++j ) // assumption: nBlockYSize == chunck_height + { + int iXOffset = j * nBlockXSize + iShift; + iXOffset += nBlockXSize - iLowerWestColumnPlanned - 1; // Position the HRV part in the frame; -1 to compensate the pre-increment in the for-loop + if (fSplitStrip && (j >= iSplitRow)) // In splitstrip, below splitline, thus do not shift!! + iXOffset -= iShift; + for (int i = 0; i < chunck_width; ++i) + ((GByte *)pImage)[++iXOffset] = cimg.Get()[y+=iStep]; + } + } + } + } + else if (eDataType == GDT_UInt16) // this is our "normal case" if scan direction is South: 10 bit MSG data became 2 bytes per pixel + { + if (nBlockXSize == chunck_width) // optimized version + { + for( int i = 0; i < nBlockSize; ++i ) + ((GUInt16 *)pImage)[i] = cimg.Get()[y+=iStep]; + } + else + { + // initialize to 0's (so that it does not have to be done in an 'else' statement ) + memset(pImage, 0, nBlockXSize * nBlockYSize * iBytesPerPixel); + for( int j = 0; j < chunck_height; ++j ) // assumption: nBlockYSize == chunck_height + { + int iXOffset = j * nBlockXSize + iShift; + iXOffset += nBlockXSize - iLowerWestColumnPlanned - 1; // Position the HRV part in the frame; -1 to compensate the pre-increment in the for-loop + if (fSplitStrip && (j >= iSplitRow)) // In splitstrip, below splitline, thus do not shift!! + iXOffset -= iShift; + for (int i = 0; i < chunck_width; ++i) + ((GUInt16 *)pImage)[++iXOffset] = cimg.Get()[y+=iStep]; + } + } + } + else if (eDataType == GDT_Float32) // radiometric calibration is requested + { + if (nBlockXSize == chunck_width) // optimized version + { + for( int i = 0; i < nBlockSize; ++i ) + ((float *)pImage)[i] = (float)rRadiometricCorrection(cimg.Get()[y+=iStep], iChannel, nBlockYOff * nBlockYSize + i / nBlockXSize, i % nBlockXSize, poGDS); + } + else + { + // initialize to 0's (so that it does not have to be done in an 'else' statement ) + memset(pImage, 0, nBlockXSize * nBlockYSize * iBytesPerPixel); + for( int j = 0; j < chunck_height; ++j ) // assumption: nBlockYSize == chunck_height + { + int iXOffset = j * nBlockXSize + iShift; + iXOffset += nBlockXSize - iLowerWestColumnPlanned - 1; // Position the HRV part in the frame; -1 to compensate the pre-increment in the for-loop + if (fSplitStrip && (j >= iSplitRow)) // In splitstrip, below splitline, thus do not shift!! + iXOffset -= iShift; + int iXFrom = nBlockXSize - iLowerWestColumnPlanned + iShift; // i is used as the iCol parameter in rRadiometricCorrection + int iXTo = nBlockXSize - iLowerWestColumnPlanned + chunck_width + iShift; + for (int i = iXFrom; i < iXTo; ++i) // range always equal to chunck_width .. this is to utilize i to get iCol + ((float *)pImage)[++iXOffset] = (float)rRadiometricCorrection(cimg.Get()[y+=iStep], iChannel, nBlockYOff * nBlockYSize + j, (fSplitStrip && (j >= iSplitRow))?(i - iShift):i, poGDS); + } + } + } + } + else // header could not be opened .. make sure block contains 0's + memset(pImage, 0, nBlockXSize * nBlockYSize * iBytesPerPixel); + } + else // file could not be opened .. make sure block contains 0's + memset(pImage, 0, nBlockXSize * nBlockYSize * iBytesPerPixel); + + i_file.close(); + } + else // file does not exist .. make sure block contains 0's + memset(pImage, 0, nBlockXSize * nBlockYSize * iBytesPerPixel); + + return CE_None; +} + +double MSGRasterBand::rRadiometricCorrection(unsigned int iDN, int iChannel, int iRow, int iCol, MSGDataset* poGDS) +{ + int iIndex = iChannel - 1; // just for speed optimization + + double rSlope = poGDS->rCalibrationSlope[iIndex]; + double rOffset = poGDS->rCalibrationOffset[iIndex]; + + if (poGDS->command.cDataConversion == 'T') // reflectance for visual bands, temperatore for IR bands + { + double rRadiance = rOffset + (iDN * rSlope); + + if (iChannel >= 4 && iChannel <= 11) // Channels 4 to 11 (infrared): Temperature + { + const double rC1 = 1.19104e-5; + const double rC2 = 1.43877e+0; + + double cc2 = rC2 * poGDS->rVc[iIndex]; + double cc1 = rC1 * pow(poGDS->rVc[iIndex], 3) / rRadiance; + double rTemperature = ((cc2 / log(cc1 + 1)) - poGDS->rB[iIndex]) / poGDS->rA[iIndex]; + return rTemperature; + } + else // Channels 1,2,3 and 12 (visual): Reflectance + { + double rLon = poGDS->adfGeoTransform[0] + iCol * poGDS->adfGeoTransform[1]; // X, in "geos" meters + double rLat = poGDS->adfGeoTransform[3] + iRow * poGDS->adfGeoTransform[5]; // Y, in "geos" meters + if ((poGDS->poTransform != NULL) && poGDS->poTransform->Transform( 1, &rLon, &rLat )) // transform it to latlon + return m_rc->rGetReflectance(rRadiance, rLat, rLon); + else + return 0; + } + } + else // radiometric + { + if (poGDS->command.cDataConversion == 'R') + return rOffset + (iDN * rSlope); + else + { + double rFactor = 10 / pow(poGDS->rCentralWvl[iIndex], 2); + return rFactor * (rOffset + (iDN * rSlope)); + } + } +} + +/************************************************************************/ +/* GDALRegister_MSG() */ +/************************************************************************/ + +void GDALRegister_MSG() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "MSG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "MSG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "MSG HRIT Data" ); + + poDriver->pfnOpen = MSGDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/msg/msgdataset.h b/bazaar/plugin/gdal/frmts/msg/msgdataset.h new file mode 100644 index 000000000..2e45dbe3c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/msgdataset.h @@ -0,0 +1,100 @@ +/****************************************************************************** + * $Id: msgdataset.h 15064 2008-07-28 19:10:23Z mloskot $ + * + * Project: MSG Driver + * Purpose: GDALDataset driver for MSG translator for read support. + * Author: Bas Retsios, retsios@itc.nl + * + ****************************************************************************** + * Copyright (c) 2004, ITC + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_csv.h" +#include "ogr_spatialref.h" +#include "msgcommand.h" + +#include +#include + +CPL_C_START +void GDALRegister_MSG(void); +CPL_C_END + +/************************************************************************/ +/* MSGRasterBand */ +/************************************************************************/ + +class MSGDataset; +class ReflectanceCalculator; + +class MSGRasterBand : public GDALRasterBand +{ + friend class MSGDataset; + + public: + MSGRasterBand( MSGDataset *, int ); + virtual ~MSGRasterBand(); + virtual CPLErr IReadBlock( int, int, void * ); + + private: + double rRadiometricCorrection(unsigned int iDN, int iChannel, int iRow, int iCol, MSGDataset* poGDS); + bool fScanNorth; + int iLowerShift; // nr of pixels that lower HRV image is shifted compared to upper + int iSplitLine; // line from top where the HRV image splits + int iLowerWestColumnPlanned; + int iSatellite; // satellite number 1,2,3,4 for MSG1, MSG2, MSG3 and MSG4 + ReflectanceCalculator* m_rc; + static const double rRTOA[12]; +}; + +/************************************************************************/ +/* MSGDataset */ +/************************************************************************/ +class MSGDataset : public GDALDataset +{ + friend class MSGRasterBand; + + public: + MSGDataset(); + ~MSGDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + virtual CPLErr GetGeoTransform( double * padfTransform ); + + private: + MSGCommand command; + double adfGeoTransform[6]; // Calculate and store once as GetGeoTransform may be called multiple times + char *pszProjection; + OGRSpatialReference oSRS; + OGRSpatialReference oLL; + OGRCoordinateTransformation *poTransform; + double rCalibrationOffset[12]; + double rCalibrationSlope[12]; + static int iCurrentSatellite; // satellite number 1,2,3,4 for MSG1, MSG2, MSG3 and MSG4 + static const double rCentralWvl[12]; + static const double rVc[12]; + static const double rA[12]; + static const double rB[12]; +}; + diff --git a/bazaar/plugin/gdal/frmts/msg/prologue.cpp b/bazaar/plugin/gdal/frmts/msg/prologue.cpp new file mode 100644 index 000000000..7e64bdc1c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/prologue.cpp @@ -0,0 +1,235 @@ +/****************************************************************************** + * $Id: prologue.cpp 15064 2008-07-28 19:10:23Z mloskot $ + * + * Purpose: Implementation of Prologue class. Parse the prologue of one + * repeat cycle and keep the interesting info. + * Author: Bas Retsios, retsios@itc.nl + * + ****************************************************************************** + * Copyright (c) 2004, ITC + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#include "prologue.h" + +int size_SatelliteStatus() +{ + int iSizePrimary = 1+4+1+1+4+4+1+1+4+4+1;; + + int iSizeOrbitCoef = 4 + 4 + 8*8 + 8*8 + 8*8 + 8*8 + 8*8 + 8*8; + int iSizeOrbit = 4 + 4 + 100*iSizeOrbitCoef; + int iSizeAttitudeCoef = 4 + 4 + 8*8 + 8*8 + 8*8; + int iSizeAttitude = 4 + 4 + 8 + 100*iSizeAttitudeCoef; + int iSizeSpinRateatRCStart = 8; + int iSizeUTCCorrelation = 4 + 4 + 4*4*3 + 8 + 8 + 8 + 8 + 8; + + int iTotalSize = iSizePrimary + iSizeOrbit + iSizeAttitude + iSizeSpinRateatRCStart + iSizeUTCCorrelation; + + return iTotalSize; +} + +int size_ImageAcquisition() +{ + // up to DHSSSynchSelection + int iSize1 = 8 + 8 + 8 + 12 + 42 + 42*2 + 2 + 2 + 2 + 2 + 1; + // up to RefocusingDirection + int iSize2 = 42*2 + 42 + 42*2 + 42*2 + 42*2 + 27*2 + 15*2 + 6*2 + 1 + 2 + 1; + // to end + int iSize3 = 2 + 1 + 2 + 4 + 2 + 2 + 2 + 1 + 4 + 1 + 4 + 4 + 1 + 1 + 2 + 2 + 2 + 2; + + int iTotalSize = iSize1 + iSize2 + iSize3; + + return iTotalSize; +} + +int size_CelestialEvents() +{ + int iSizeCelestialBodies = 2 + 2 + 4 + 4 + 3*100*(2 + 2 + 8*8 + 8*8) + 100*(20*(2 + 2 + 2 + 8*8 + 8*8)); + + int iSizeRelationToImage = 1 + 2 + 2 + 1 + 1 + 1; + + int iTotalSize = iSizeCelestialBodies + iSizeRelationToImage; + + return iTotalSize; +} + +int size_Correction() +{ + return 19229; +} + +double iReadDouble(std::ifstream & ifile) +{ + // will use 8 bytes from the file to read a DOUBLE (according to the MSG definition of DOUBLE) + unsigned char buf [8]; + + ifile.read((char*)buf, 8); + double rVal; + ((char*)(&rVal))[0] = buf[7]; + ((char*)(&rVal))[1] = buf[6]; + ((char*)(&rVal))[2] = buf[5]; + ((char*)(&rVal))[3] = buf[4]; + ((char*)(&rVal))[4] = buf[3]; + ((char*)(&rVal))[5] = buf[2]; + ((char*)(&rVal))[6] = buf[1]; + ((char*)(&rVal))[7] = buf[0]; + + return rVal; +} + +double iReadReal(std::ifstream & ifile) +{ + // will use 4 bytes from the file to read a REAL (according to the MSG definition of REAL) + unsigned char buf [4]; + + ifile.read((char*)buf, 4); + float rVal; + ((char*)(&rVal))[0] = buf[3]; + ((char*)(&rVal))[1] = buf[2]; + ((char*)(&rVal))[2] = buf[1]; + ((char*)(&rVal))[3] = buf[0]; + + return rVal; +} + +int iReadInt(std::ifstream & ifile) +{ + // will use 4 bytes from the file to read an int (according to the MSG definition of int) + unsigned char buf [4]; + + ifile.read((char*)buf, 4); + int iResult = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3]; + + return iResult; +} + +unsigned char iReadByte (std::ifstream & ifile) +{ + // will read 1 byte from the file + char b; + + ifile.read(&b, 1); + + return b; +} + +ReferenceGridRecord::ReferenceGridRecord(std::ifstream & ifile) +{ + NumberOfLines = iReadInt(ifile); + NumberOfColumns = iReadInt(ifile); + LineDirGridStep = iReadReal(ifile); + ColumnDirGridStep = iReadReal(ifile); + GridOrigin = iReadByte(ifile); +} + +PlannedCoverageVIS_IRRecord::PlannedCoverageVIS_IRRecord(std::ifstream & ifile) +{ + SouthernLinePlanned = iReadInt(ifile); + NorthernLinePlanned = iReadInt(ifile); + EasternColumnPlanned = iReadInt(ifile); + WesternColumnPlanned = iReadInt(ifile); +} + +PlannedCoverageHRVRecord::PlannedCoverageHRVRecord(std::ifstream & ifile) +{ + LowerSouthLinePlanned = iReadInt(ifile); + LowerNorthLinePlanned = iReadInt(ifile); + LowerEastColumnPlanned = iReadInt(ifile); + LowerWestColumnPlanned = iReadInt(ifile); + UpperSouthLinePlanned = iReadInt(ifile); + UpperNorthLinePlanned = iReadInt(ifile); + UpperEastColumnPlanned = iReadInt(ifile); + UpperWestColumnPlanned = iReadInt(ifile); +} + + +ImageDescriptionRecord::ImageDescriptionRecord(std::ifstream & ifile) +{ + TypeOfProjection = iReadByte(ifile); + LongitudeOfSSP = iReadReal(ifile); + ReferenceGridVIS_IR = new ReferenceGridRecord(ifile); + ReferenceGridHRV = new ReferenceGridRecord(ifile); + PlannedCoverageVIS_IR = new PlannedCoverageVIS_IRRecord(ifile); + PlannedCoverageHRV = new PlannedCoverageHRVRecord(ifile); + ImageProcDirection = iReadByte(ifile); + PixelGenDirection = iReadByte(ifile); + for (int i=0; i < 12; ++i) + PlannedChannelProcessing[i] = iReadByte(ifile); +} + +ImageDescriptionRecord::~ImageDescriptionRecord() +{ + delete ReferenceGridVIS_IR; + delete ReferenceGridHRV; + delete PlannedCoverageVIS_IR; + delete PlannedCoverageHRV; +} + +RadiometricProcessingRecord::RadiometricProcessingRecord(std::ifstream & ifile) +{ + // skip a part that doesn't interest us + unsigned char dummy [12]; + int i; + for (i = 0; i < 6; ++i) + ifile.read((char*)dummy, 12); + + for (i = 0; i < 12; ++i) + { + Cal_Slope[i] = iReadDouble(ifile); + Cal_Offset[i] = iReadDouble(ifile); + } +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +Prologue::Prologue() +: m_idr(0) +, m_rpr(0) +{ + +} + +Prologue::~Prologue() +{ + if (m_idr) + delete m_idr; + if (m_rpr) + delete m_rpr; +} + +void Prologue::read(std::ifstream & ifile) +{ + unsigned char version = iReadByte(ifile); + + int iSkipHeadersSize = size_SatelliteStatus() + size_ImageAcquisition() + size_CelestialEvents() + size_Correction(); + +#if _MSC_VER > 1000 && _MSC_VER < 1300 + ifile.seekg(iSkipHeadersSize, std::ios_base::seekdir::cur); +#else + ifile.seekg(iSkipHeadersSize, std::ios_base::cur); +#endif + + m_idr = new ImageDescriptionRecord(ifile); + + m_rpr = new RadiometricProcessingRecord(ifile); + // TODO: file is not left at the end of the Radiometric Processing Record +} diff --git a/bazaar/plugin/gdal/frmts/msg/prologue.h b/bazaar/plugin/gdal/frmts/msg/prologue.h new file mode 100644 index 000000000..eed344dbb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/prologue.h @@ -0,0 +1,126 @@ +/****************************************************************************** + * $Id: prologue.h 15064 2008-07-28 19:10:23Z mloskot $ + * + * Purpose: Interface of Prologue class. Parse the prologue of one repeat + * cycle and keep the interesting info. + * Author: Bas Retsios, retsios@itc.nl + * + ****************************************************************************** + * Copyright (c) 2004, ITC + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#if !defined(AFX_PROLOGUE_H__777B5B86_04F4_4A01_86F6_24615DCD8446__INCLUDED_) +#define AFX_PROLOGUE_H__777B5B86_04F4_4A01_86F6_24615DCD8446__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include + +class ReferenceGridRecord +{ +public: + ReferenceGridRecord(std::ifstream & ifile); + + int NumberOfLines; + int NumberOfColumns; + double LineDirGridStep; + double ColumnDirGridStep; + unsigned char GridOrigin; // 0 == north-west corner; 1 == sw; 2 == se; 3 == ne; +}; + +class PlannedCoverageVIS_IRRecord +{ +public: + PlannedCoverageVIS_IRRecord(std::ifstream & ifile); + + int SouthernLinePlanned; + int NorthernLinePlanned; + int EasternColumnPlanned; + int WesternColumnPlanned; +}; + +class PlannedCoverageHRVRecord +{ +public: + PlannedCoverageHRVRecord(std::ifstream & ifile); + int LowerSouthLinePlanned; + int LowerNorthLinePlanned; + int LowerEastColumnPlanned; + int LowerWestColumnPlanned; + int UpperSouthLinePlanned; + int UpperNorthLinePlanned; + int UpperEastColumnPlanned; + int UpperWestColumnPlanned; +}; + +class ImageDescriptionRecord +{ +public: + ImageDescriptionRecord(std::ifstream & ifile); + virtual ~ImageDescriptionRecord(); + + unsigned char TypeOfProjection; // 1 == Geostationary, Earth centered in grid + double LongitudeOfSSP; + ReferenceGridRecord * ReferenceGridVIS_IR; + ReferenceGridRecord * ReferenceGridHRV; + PlannedCoverageVIS_IRRecord * PlannedCoverageVIS_IR; + PlannedCoverageHRVRecord * PlannedCoverageHRV; + unsigned char ImageProcDirection; // 0 == north-south; 1 == south-north + unsigned char PixelGenDirection; // 0 == east-west; 1 == west-east; + unsigned char PlannedChannelProcessing [12]; +}; + +class RadiometricProcessingRecord +{ +public: + RadiometricProcessingRecord(std::ifstream & ifile); + + double Cal_Slope [12]; + double Cal_Offset [12]; +}; + +class Prologue +{ +public: + Prologue(); + virtual ~Prologue(); + + void read(std::ifstream & ifile); + + const ImageDescriptionRecord * idr() + { + return m_idr; + }; + + const RadiometricProcessingRecord * rpr() + { + return m_rpr; + }; + +private: + ImageDescriptionRecord * m_idr; + RadiometricProcessingRecord * m_rpr; + +}; + +#endif // !defined(AFX_PROLOGUE_H__777B5B86_04F4_4A01_86F6_24615DCD8446__INCLUDED_) diff --git a/bazaar/plugin/gdal/frmts/msg/reflectancecalculator.cpp b/bazaar/plugin/gdal/frmts/msg/reflectancecalculator.cpp new file mode 100644 index 000000000..1de0cbaa7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/reflectancecalculator.cpp @@ -0,0 +1,162 @@ +/****************************************************************************** + * $Id: reflectancecalculator.cpp 15066 2008-07-28 20:21:59Z mloskot $ + * + * Purpose: Implementation of ReflectanceCalculator class. Calculate + * reflectance values from radiance, for visual bands. + * Author: Bas Retsios, retsios@itc.nl + * + ****************************************************************************** + * Copyright (c) 2004, ITC + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#include "reflectancecalculator.h" +#include +#include +using namespace std; + +#define M_PI 3.14159265358979323846 + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +ReflectanceCalculator::ReflectanceCalculator(std::string sTimeStamp, double rRTOA) +: m_rRTOA(rRTOA) +{ + std::string sYear (sTimeStamp.substr(0, 4)); + std::string sMonth (sTimeStamp.substr(4, 2)); + std::string sDay (sTimeStamp.substr(6, 2)); + std::string sHours (sTimeStamp.substr(8, 2)); + std::string sMins (sTimeStamp.substr(10, 2)); + + m_iYear = atoi(sYear.c_str()); + int iMonth = atoi(sMonth.c_str()); + m_iDay = atoi(sDay.c_str()); + for (int i = 1; i < iMonth; ++i) + m_iDay += iDaysInMonth(i, m_iYear); + int iHours = atoi(sHours.c_str()); + int iMins = atoi(sMins.c_str()); + + m_rHours = iHours + iMins / 60.0; +} + +ReflectanceCalculator::~ReflectanceCalculator() +{ + +} + +double ReflectanceCalculator::rGetReflectance(double rRadiance, double rLat, double rLon) const +{ + double phi = rLat * M_PI / 180; + double lam = rLon * M_PI / 180; + double rSunDist = rSunDistance(); + double ReflectanceNumerator = rRadiance*rSunDist*rSunDist; + double zenithAngle = rZenithAngle(phi, rDeclination(), rHourAngle(rLon)); + double ReflectanceDenominator = m_rRTOA*cos(zenithAngle*M_PI/180); + double Reflectance = ReflectanceNumerator / ReflectanceDenominator; + return Reflectance; +} + +double ReflectanceCalculator::rZenithAngle(double phi, double rDeclin, double rHourAngle) const +{ + double rCosZen = (sin(phi) * sin(rDeclin) + cos(phi) + * cos(rDeclin) * cos(rHourAngle)); + double zenithAngle = acos(rCosZen) * 180 / M_PI; + return zenithAngle; +} + +const double ReflectanceCalculator::rDeclination() const +{ + double rJulianDay = m_iDay - 1; + double yearFraction = (rJulianDay + m_rHours / 24) / iDaysInYear(m_iYear); + double T = 2 * M_PI * yearFraction; + + double declin = 0.006918 - 0.399912 * cos(T) + 0.070257 * sin(T) + - 0.006758 * cos(2 * T) + 0.000907 * sin(2 * T) + - 0.002697 * cos(3 * T) + 0.00148 * sin(3 * T); + return declin; +} + +double ReflectanceCalculator::rHourAngle(double rLon) const +{ + // In: rLon (in degrees) + // Out: hourAngle (in radians) + double rJulianDay = m_iDay - 1; + double yearFraction = (rJulianDay + m_rHours / 24) / iDaysInYear(m_iYear); + double T = 2 * M_PI * yearFraction; + + double EOT2 = 229.18 * (0.000075 + 0.001868 * cos(T)- 0.032077 * sin(T)); + double EOT3 = 229.18 * (- 0.014615 * cos(2 * T) - 0.040849 * sin(2 * T)); + double EOT = EOT2 + EOT3; + double TimeOffset = EOT + (4. * rLon); + // True solar time in minutes: + double TrueSolarTime = m_rHours * 60 + TimeOffset; + // Solar hour angle in degrees and in radians: + double HaDegr = (TrueSolarTime / 4. - 180.); + double hourAngle = HaDegr * M_PI / 180; + return hourAngle; +} + +const double ReflectanceCalculator::rSunDistance() const +{ + int iJulianDay = m_iDay - 1; + double theta = 2*M_PI *(iJulianDay - 3) / 365.25; + // rE0 is the inverse of the square of the sun-distance ratio + double rE0 = 1.000110 + 0.034221*cos(theta)+0.00128*sin(theta) + 0.000719*cos(2*theta)+0.000077*sin(2*theta); + // The calculated distance is expressed as a factor of the "average sun-distance" (on 1 Jan approx. 0.98, on 1 Jul approx. 1.01) + return 1 / sqrt(rE0); +} + +int ReflectanceCalculator::iDaysInYear(int iYear) const +{ + bool fLeapYear = iDaysInMonth(2, iYear) == 29; + + if (fLeapYear) + return 366; + else + return 365; +} + +int ReflectanceCalculator::iDaysInMonth(int iMonth, int iYear) const +{ + int iDays; + + if ((iMonth == 4) || (iMonth == 6) || (iMonth == 9) || (iMonth == 11)) + iDays = 30; + else if (iMonth == 2) + { + iDays = 28; + if (iYear % 100 == 0) // century year + { + if (iYear % 400 == 0) // century leap year + ++iDays; + } + else + { + if (iYear % 4 == 0) // normal leap year + ++iDays; + } + } + else + iDays = 31; + + return iDays; +} diff --git a/bazaar/plugin/gdal/frmts/msg/reflectancecalculator.h b/bazaar/plugin/gdal/frmts/msg/reflectancecalculator.h new file mode 100644 index 000000000..e1a9126b8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/reflectancecalculator.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * $Id: reflectancecalculator.h 15064 2008-07-28 19:10:23Z mloskot $ + * + * Purpose: Interface of ReflectanceCalculator class. Calculate reflectance + * values from radiance, for visual bands. + * Author: Bas Retsios, retsios@itc.nl + * + ****************************************************************************** + * Copyright (c) 2004, ITC + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#if !defined(AFX_REFLECTANCECALCULATOR_H__C9960E01_2A1B_41F0_B903_7957F11618D2__INCLUDED_) +#define AFX_REFLECTANCECALCULATOR_H__C9960E01_2A1B_41F0_B903_7957F11618D2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include + +class ReflectanceCalculator +{ +public: + ReflectanceCalculator(std::string sTimeStamp, double rRTOA); + virtual ~ReflectanceCalculator(); + double rGetReflectance(double rRadiance, double rLat, double rLon) const; +private: + double rZenithAngle(double phi, double rDeclin, double rHourAngle) const; + const double rDeclination() const; + double rHourAngle(double lam) const; + const double rSunDistance() const; + int iDaysInYear(int iYear) const; + int iDaysInMonth(int iMonth, int iYear) const; + + const double m_rRTOA; // solar irradiance on Top of Atmosphere + int m_iYear; // e.g. 2005 + int m_iDay; // 1-365/366 + double m_rHours; // 0-24 +}; + +#endif // !defined(AFX_REFLECTANCECALCULATOR_H__C9960E01_2A1B_41F0_B903_7957F11618D2__INCLUDED_) diff --git a/bazaar/plugin/gdal/frmts/msg/xritheaderparser.cpp b/bazaar/plugin/gdal/frmts/msg/xritheaderparser.cpp new file mode 100644 index 000000000..df16426fd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/xritheaderparser.cpp @@ -0,0 +1,154 @@ +/****************************************************************************** + * $Id: xritheaderparser.cpp 15066 2008-07-28 20:21:59Z mloskot $ + * + * Purpose: Implementation of XRITHeaderParser class. Parse the header + * of the combined XRIT header/data files. + * Author: Bas Retsios, retsios@itc.nl + * + ****************************************************************************** + * Copyright (c) 2007, ITC + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#include "xritheaderparser.h" +#include // malloc, free +#include // memcpy + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +// +// Upon successful parsing of a header in ifile, isValid() returns true +// and ifile is seeked to the beginning of the image data +////////////////////////////////////////////////////////////////////// + +XRITHeaderParser::XRITHeaderParser(std::ifstream & ifile) +: m_isValid(false) +, m_isPrologue(false) +, m_dataSize(0) +, m_nrBitsPerPixel(0) +, m_nrColumns(0) +, m_nrRows(0) +, m_scanNorth(false) +{ + const unsigned int probeSize = 8; + + unsigned char probeBuf[probeSize]; + ifile.read((char*)probeBuf, probeSize); // Probe file by reading first 8 bytes + + if (probeBuf[0] == 0 && probeBuf[1] == 0 && probeBuf[2] == 16) // Check for primary header record + { + long totalHeaderLength = parseInt32(&probeBuf[4]); + if ((totalHeaderLength >= 10) && (totalHeaderLength <= 10000)) // Check for valid header length + { + unsigned char * buf = (unsigned char*)std::malloc(totalHeaderLength); + std::memcpy(buf, probeBuf, probeSize); // save what we have already read when probing + ifile.read((char*)buf + probeSize, totalHeaderLength - probeSize); // read the rest of the header section + parseHeader(buf, totalHeaderLength); + std::free(buf); + + m_isValid = true; + } + } + + if (!m_isValid) // seek back to original position + { +#if _MSC_VER > 1000 && _MSC_VER < 1300 + ifile.seekg(-probeSize, std::ios_base::seekdir::cur); +#else + ifile.seekg(-probeSize, std::ios_base::cur); +#endif + } +} + +XRITHeaderParser::~XRITHeaderParser() +{ + +} + +int XRITHeaderParser::parseInt16(unsigned char * num) +{ + return (num[0]<<8) | num[1]; +} + +long XRITHeaderParser::parseInt32(unsigned char * num) +{ + return (num[0]<<24) | (num[1]<<16) | (num[2]<<8) | num[3]; +} + +void XRITHeaderParser::parseHeader(unsigned char * buf, long totalHeaderLength) +{ + int remainingHeaderLength = totalHeaderLength; + + while (remainingHeaderLength > 0) + { + int headerType = buf[0]; + int headerRecordLength = parseInt16(&buf[1]); + if (headerRecordLength > remainingHeaderLength) + break; + + switch(headerType) + { + case 0: // primary header + { + int fileTypeCode = buf[3]; // 0 = image data file, 128 = prologue + if (fileTypeCode == 128) + m_isPrologue = true; + + long dataFieldLengthH = parseInt32(&buf[8]); // length of data field in bits (High DWORD) + long dataFieldLengthL = parseInt32(&buf[12]); // length of data field in bits (Low DWORD) + m_dataSize = (dataFieldLengthH << 5) + (dataFieldLengthL >> 3); // combine and convert bits to bytes + } + break; + case 1: // image structure + m_nrBitsPerPixel = buf[3]; // NB, number of bits per pixel + m_nrColumns = parseInt16(&buf[4]); // NC, number of columns + m_nrRows = parseInt16(&buf[6]); // NL, number of lines + break; + case 2: // image navigation + { + long cfac = parseInt32(&buf[35]); // column scaling factor + long lfac = parseInt32(&buf[39]); // line scaling factor + long coff = parseInt32(&buf[43]); // column offset + long loff = parseInt32(&buf[47]); // line offset + if (lfac >= 0) + m_scanNorth = true; + else + m_scanNorth = false; + } + break; + case 3: // image data function + case 4: // annotation + case 5: // time stamp + case 6: // ancillary text + case 7: // key header + case 128: // image segment identification + case 129: // encryption key message header + case 130: // image compensation information header + case 131: // image observation time header + case 132: // image quality information header + break; + default: // ignore unknown header type + break; + } + + buf += headerRecordLength; + remainingHeaderLength -= headerRecordLength; + } +} diff --git a/bazaar/plugin/gdal/frmts/msg/xritheaderparser.h b/bazaar/plugin/gdal/frmts/msg/xritheaderparser.h new file mode 100644 index 000000000..18c5df5df --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msg/xritheaderparser.h @@ -0,0 +1,87 @@ +/****************************************************************************** + * + * Purpose: Interface of XRITHeaderParser class. Parse the header of + * the combined XRIT header/data files. + * Author: Bas Retsios, retsios@itc.nl + * + ****************************************************************************** + * Copyright (c) 2007, ITC + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#if !defined(AFX_XRITHEADERPARSER_H__D01CC599_96C2_4901_85B3_96169D757898__INCLUDED_) +#define AFX_XRITHEADERPARSER_H__D01CC599_96C2_4901_85B3_96169D757898__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include + +class XRITHeaderParser +{ +public: + XRITHeaderParser(std::ifstream & ifile); + + virtual ~XRITHeaderParser(); + + const bool isValid() { + return m_isValid; + } + + const bool isPrologue() { + return m_isPrologue; + } + + const long dataSize() { + return m_dataSize; + } + + const int nrRows() { + return m_nrRows; + } + + const int nrColumns() { + return m_nrColumns; + } + + const int nrBitsPerPixel() { + return m_nrBitsPerPixel; + } + + const bool isScannedNorth() { + return m_scanNorth; + } + +private: + int parseInt16(unsigned char * num); + long parseInt32(unsigned char * num); + void parseHeader(unsigned char * buf, long totalHeaderLength); + + bool m_isValid; + bool m_isPrologue; + long m_dataSize; + int m_nrBitsPerPixel; + int m_nrColumns; + int m_nrRows; + bool m_scanNorth; +}; + +#endif // !defined(AFX_XRITHEADERPARSER_H__D01CC599_96C2_4901_85B3_96169D757898__INCLUDED_) diff --git a/bazaar/plugin/gdal/frmts/msgn/GNUmakefile b/bazaar/plugin/gdal/frmts/msgn/GNUmakefile new file mode 100644 index 000000000..9118d2e49 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msgn/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = msgndataset.o msg_basic_types.o msg_reader_core.o + +CPPFLAGS := $(CPPFLAGS) -I. -DGDAL_SUPPORT + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/msgn/frmt_msgn.html b/bazaar/plugin/gdal/frmts/msgn/frmt_msgn.html new file mode 100644 index 000000000..6c52e312f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msgn/frmt_msgn.html @@ -0,0 +1,29 @@ + + + +MSGN -- Meteosat Second Generation (MSG) Native Archive Format (.nat) + + + + +

    MSGN -- Meteosat Second Generation (MSG) Native Archive Format (.nat)

    + +

    GDAL supports reading only of MSG native files. These files may have +anything from 1 to 12 bands, all at 10-bit resolution. + +

    Includes support for the 12th band (HRV - High Resolution Visible). This +is implemented as a subset, i.e., it is accessed by prefixing the filename +with the tag "HRV:". + +

    Similarly, it is possible to obtain floating point radiance values in +stead of the usual 10-bit digital numbers (DNs). This subset is accessed by +prefixing the filename with the tag "RAD:". + +

    Georeferencing is currently supported, but the results may not be +acceptable (accurate enough), depending on your requirements. The current +workaround is to implement the CGMS Geostationary projection directly, using +the code available from EUMETSAT. + + + + diff --git a/bazaar/plugin/gdal/frmts/msgn/makefile.vc b/bazaar/plugin/gdal/frmts/msgn/makefile.vc new file mode 100644 index 000000000..6771dcb6d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msgn/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = msgndataset.obj msg_basic_types.obj msg_reader_core.obj + +EXTRAFLAGS = -I..\iso8211 -I. -DGDAL_SUPPORT + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/msgn/msg_basic_types.cpp b/bazaar/plugin/gdal/frmts/msgn/msg_basic_types.cpp new file mode 100644 index 000000000..93e15ada9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msgn/msg_basic_types.cpp @@ -0,0 +1,223 @@ +/****************************************************************************** + * $Id: msg_basic_types.cpp 28435 2015-02-07 14:35:34Z rouault $ + * + * Project: MSG Native Reader + * Purpose: Basic types implementation. + * Author: Frans van den Bergh, fvdbergh@csir.co.za + * + ****************************************************************************** + * Copyright (c) 2005, Frans van den Bergh + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "msg_basic_types.h" +#include "cpl_port.h" + +CPL_CVSID("$Id: msg_basic_types.cpp 28435 2015-02-07 14:35:34Z rouault $"); + +#include + +namespace msg_native_format { + +#ifndef SQR +#define SQR(x) ((x)*(x)) +#endif + +// endian conversion routines +void to_native(GP_PK_HEADER& h) { + h.sourceSUId = CPL_MSBWORD32(h.sourceSUId); + h.sequenceCount = CPL_MSBWORD16(h.sequenceCount); + h.packetLength = CPL_MSBWORD32(h.packetLength); +} + +void to_native(GP_PK_SH1& h) { + h.spacecraftId = CPL_MSBWORD16(h.spacecraftId); +} + +void to_native(SUB_VISIRLINE& v) { + v.satelliteId = CPL_MSBWORD16(v.satelliteId); + v.lineNumberInVisirGrid = CPL_MSBWORD32(v.lineNumberInVisirGrid); +} + +static void swap_64_bits(unsigned char* b) { + for (int i=0; i < 4; i++) { + unsigned char t = b[i]; + b[i] = b[7-i]; + b[7-i] = t; + } +} + +void to_native(RADIOMETRIC_PROCCESSING_RECORD& r) { + for (int i=0; i < 12; i++) { + swap_64_bits((unsigned char*)&r.level1_5ImageCalibration[i].cal_slope); + swap_64_bits((unsigned char*)&r.level1_5ImageCalibration[i].cal_offset); + } +} + +void to_native(IMAGE_DESCRIPTION_RECORD& r) { + r.referencegrid_visir.numberOfLines = CPL_MSBWORD32(r.referencegrid_visir.numberOfLines); + r.referencegrid_visir.numberOfColumns = CPL_MSBWORD32(r.referencegrid_visir.numberOfColumns); + // should floats be swapped too? + float f; + + // convert float using CPL_MSBPTR32 + memcpy(&f, &r.referencegrid_visir.lineDirGridStep, sizeof(f)); + CPL_MSBPTR32(&f); + r.referencegrid_visir.lineDirGridStep = f; + + // convert float using CPL_MSBPTR32 + memcpy(&f, &r.referencegrid_visir.columnDirGridStep, sizeof(f)); + CPL_MSBPTR32(&f); + r.referencegrid_visir.columnDirGridStep = f; +} + +void to_string(PH_DATA& d) { + d.name[29] = 0; + d.value[49] = 0; +} + +// unit tests on structures +bool perform_type_size_check(void) { + bool success = true; + if (sizeof(MAIN_PROD_HEADER) != 3674) { + fprintf(stderr, "MAIN_PROD_HEADER size not 3674 (%lu)\n", (unsigned long)sizeof(MAIN_PROD_HEADER)); + success = false; + } + if (sizeof(SECONDARY_PROD_HEADER) != 1120) { + fprintf(stderr, "SECONDARY_PROD_HEADER size not 1120 (%lu)\n", (unsigned long)sizeof(SECONDARY_PROD_HEADER)); + success = false; + } + if (sizeof(SUB_VISIRLINE) != 27) { + fprintf(stderr, "SUB_VISIRLINE size not 17 (%lu)\n", (unsigned long)sizeof(SUB_VISIRLINE)); + success = false; + } + if (sizeof(GP_PK_HEADER) != 22) { + fprintf(stderr, "GP_PK_HEADER size not 22 (%lu)\n", (unsigned long)sizeof(GP_PK_HEADER)); + success = false; + } + if (sizeof(GP_PK_SH1) != 16) { + fprintf(stderr, "GP_PK_SH1 size not 16 (%lu)\n", (unsigned long)sizeof(GP_PK_SH1)); + success = false; + } + return success; +} + +const double Conversions::altitude = 42164; // from origin +const double Conversions::req = 6378.1690; // earthequatorial radius +const double Conversions::rpol = 6356.5838; // earth polar radius +const double Conversions::oblate = 1.0/298.257; // oblateness of earth +const double Conversions::deg_to_rad = M_PI/180.0; +const double Conversions::rad_to_deg = 180.0/M_PI; +const double Conversions::nlines = 3712; // number of lines in an image +const double Conversions::step = 17.83/nlines; // pixel / line step in degrees + +const int Conversions::CFAC = -781648343; +const int Conversions::LFAC = -781648343; +const int Conversions::COFF = 1856; +const int Conversions::LOFF = 1856; + +#define SQR(x) ((x)*(x)) + +void Conversions::convert_pixel_to_geo(double line, double column, double&longitude, double& latitude) { + double x = (column - COFF - 0.0) / double(CFAC >> 16); + double y = (line - LOFF - 0.0) / double(LFAC >> 16); + + double sd = sqrt(SQR(altitude*cos(x)*cos(y)) - (SQR(cos(y)) + 1.006803*SQR(sin(y)))*1737121856); + double sn = (altitude*cos(x)*cos(y) - sd)/(SQR(cos(y)) + 1.006803*SQR(sin(y))); + double s1 = altitude - sn*cos(x)*cos(y); + double s2 = sn*sin(x)*cos(y); + double s3 = -sn*sin(y); + double sxy = sqrt(s1*s1 + s2*s2); + + longitude = atan(s2/s1); + latitude = atan(1.006803*s3/sxy); + + longitude = longitude / M_PI * 180.0; + latitude = latitude / M_PI * 180.0; +} + +void Conversions::compute_pixel_xyz(double line, double column, double& x,double& y, double& z) { + double asamp = -(column - (nlines/2.0 + 0.5)) * step; + double aline = (line - (nlines/2.0 + 0.5)) * step; + + asamp *= deg_to_rad; + aline *= deg_to_rad; + + double tanal = tan(aline); + double tanas = tan(asamp); + + double p = -1; + double q = tanas; + double r = tanal * sqrt(1 + q*q); + + double a = q*q + (r*req/rpol)*(r*req/rpol) + p*p; + double b = 2 * altitude * p; + double c = altitude * altitude - req*req; + + double det = b*b - 4*a*c; + + if (det > 0) { + double k = (-b - sqrt(det))/(2*a); + x = altitude + k*p; + y = k * q; + z = k * r; + + } else { + fprintf(stderr, "Warning: pixel not visible\n"); + } +} + +double Conversions::compute_pixel_area_sqkm(double line, double column) { + double x1, x2; + double y1, y2; + double z1, z2; + + compute_pixel_xyz(line-0.5, column-0.5, x1, y1, z1); + compute_pixel_xyz(line+0.5, column-0.5, x2, y2, z2); + + double xlen = sqrt(SQR(x1 - x2) + SQR(y1 - y2) + SQR(z1 - z2)); + + compute_pixel_xyz(line-0.5, column+0.5, x2, y2, z2); + + double ylen = sqrt(SQR(x1 - x2) + SQR(y1 - y2) + SQR(z1 - z2)); + + return xlen*ylen; +} + +void Conversions::convert_geo_to_pixel(double longitude, double latitude,unsigned int& line, unsigned int& column) { + + latitude = latitude / 180.0 * M_PI; + longitude = longitude / 180.8 * M_PI; + + double c_lat = atan(0.993243 * tan(latitude)); + double r_l = rpol / sqrt(1 - 0.00675701*cos(c_lat)*cos(c_lat)); + double r1 = altitude - r_l*cos(c_lat)*cos(longitude); + double r2 = -r_l*cos(c_lat)*sin(longitude); + double r3 = r_l*sin(c_lat); + double rn = sqrt(r1*r1 + r2*r2 + r3*r3); + + double x = atan(-r2/r1) * (CFAC >> 16) + COFF; + double y = asin(-r3/rn) * (LFAC >> 16) + LOFF; + + line = (unsigned int)floor(x + 0.5); + column = (unsigned int)floor(y + 0.5); +} + +} // namespace msg_native_format diff --git a/bazaar/plugin/gdal/frmts/msgn/msg_basic_types.h b/bazaar/plugin/gdal/frmts/msgn/msg_basic_types.h new file mode 100644 index 000000000..c02b3a4b2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msgn/msg_basic_types.h @@ -0,0 +1,248 @@ +/****************************************************************************** + * $Id: msg_basic_types.h 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: MSG Native Reader + * Purpose: Basic types implementation. + * Author: Frans van den Bergh, fvdbergh@csir.co.za + * + ****************************************************************************** + * Copyright (c) 2005, Frans van den Bergh + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef MSG_BASIC_TYPES +#define MSG_BASIC_TYPES + +#include + +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795 +#endif + +namespace msg_native_format { + +const unsigned int SATELLITESTATUS_RECORD_LENGTH = 60134; +const unsigned int IMAGEACQUISITION_RECORD_LENGTH = 700; +const unsigned int CELESTIALEVENTS_RECORD_LENGTH = 326058; // should be 56258 according to ICD105 ?? +const unsigned int IMAGEDESCRIPTION_RECORD_LENGTH = 101; + +const unsigned int RADIOMETRICPROCESSING_RECORD_OFFSET = + SATELLITESTATUS_RECORD_LENGTH + + IMAGEACQUISITION_RECORD_LENGTH + + CELESTIALEVENTS_RECORD_LENGTH + + IMAGEDESCRIPTION_RECORD_LENGTH; + +typedef int INTEGER; // 32 bits +typedef unsigned int UNSIGNED; // 32 bits +typedef unsigned short USHORT; // 16 bits +typedef unsigned char TIME_CDS_SHORT[6]; +typedef unsigned char TIME_CDS_EXPANDED[10]; +typedef unsigned char EBYTE; // enumerated byte +typedef unsigned char UBYTE; // enumerated byte +typedef float REAL; + +typedef unsigned short int GP_SC_ID; // 16 bits, enumerated +typedef unsigned char GP_SC_CHAN_ID; // 8 bits, enumerated +typedef unsigned char GP_FAC_ID; // 8 bits, enumerated +typedef unsigned char GP_FAC_ENV; // 8 bits, enumerated +typedef unsigned int GP_SU_ID; // 32 bits, interval partition +typedef unsigned char GP_SVCE_TYPE; // 8 bits, enumerated + +// all structures must be packed on byte boundaries +#pragma pack(1) + +typedef struct { + unsigned char qualifier1; + unsigned char qualifier2; + unsigned char qualifier3; + unsigned char qualifier4; +} GP_CPU_ID; + +typedef struct { + char name[30]; + char value[50]; +} PH_DATA; + +typedef struct { + char name[30]; + char size[16]; + char address[16]; +} PH_DATA_ID; + +typedef struct { + PH_DATA formatName; + PH_DATA formatDocumentName; + PH_DATA formatDocumentMajorVersion; + PH_DATA formatDocumentMinorVersion; + PH_DATA creationDateTime; + PH_DATA creatingCentre; + PH_DATA_ID dataSetIdentification[5]; + UBYTE slack[1364]; // what is this? This is not in the documentation? + PH_DATA totalFileSize; + PH_DATA gort; + PH_DATA asti; + PH_DATA llos; + PH_DATA snit; + PH_DATA aiid; + PH_DATA ssbt; + PH_DATA ssst; + PH_DATA rrcc; + PH_DATA rrbt; + PH_DATA rrst; + PH_DATA pprc; + PH_DATA ppdt; + PH_DATA gplv; + PH_DATA apnm; + PH_DATA aarf; + PH_DATA uudt; + PH_DATA qqov; + PH_DATA udsp; +} MAIN_PROD_HEADER; + +typedef struct { + PH_DATA abid; + PH_DATA smod; + PH_DATA apxs; + PH_DATA avpa; + PH_DATA lscd; + PH_DATA lmap; + PH_DATA qdlc; + PH_DATA qdlp; + PH_DATA qqai; + PH_DATA selectedBandIds; + PH_DATA southLineSelectedRectangle; + PH_DATA northLineSelectedRectangle; + PH_DATA eastColumnSelectedRectangle; + PH_DATA westColumnSelectedRectangle; +} SECONDARY_PROD_HEADER; + +typedef struct { + UBYTE visirlineVersion; + GP_SC_ID satelliteId; + TIME_CDS_EXPANDED trueRepeatCycleStart; + INTEGER lineNumberInVisirGrid; + GP_SC_CHAN_ID channelId; + TIME_CDS_SHORT l10LineMeanAcquisitionTime; + EBYTE lineValidity; + EBYTE lineRadiometricQuality; + EBYTE lineGeometricQuality; + // actual line data not represented here +} SUB_VISIRLINE; + +typedef struct { + UBYTE headerVersionNo; + EBYTE packetType; // 2 = mission data + EBYTE subHeaderType; // 0 = no subheader, 1 = GP_PK_SH1, 2 = GP_PK_SH2 + GP_FAC_ID sourceFacilityId; + GP_FAC_ENV sourceEnvId; + UBYTE sourceInstanceId; + GP_SU_ID sourceSUId; + GP_CPU_ID sourceCPUId; + GP_FAC_ID destFacilityId; + GP_FAC_ENV destEnvId; + USHORT sequenceCount; + UNSIGNED packetLength; +} GP_PK_HEADER; + +typedef struct { + UBYTE subHeaderVersionNo; + EBYTE checksumFlag; + UBYTE acknowledgement[4]; + GP_SVCE_TYPE serviceType; + UBYTE serviceSubType; + TIME_CDS_SHORT packetTime; + GP_SC_ID spacecraftId; +} GP_PK_SH1; + +typedef struct { + double cal_slope; + double cal_offset; +} CALIBRATION; + +typedef struct { + EBYTE radianceLinearisation[12]; + EBYTE detectorEqualisation[12]; + EBYTE onboardCalibrationResult[12]; + EBYTE MPEFCalFeedback[12]; + EBYTE MTFAdaption[12]; + EBYTE straylightCorrectionFlag[12]; + CALIBRATION level1_5ImageCalibration[12]; + // rest of structure omitted for now +} RADIOMETRIC_PROCCESSING_RECORD; + +typedef struct { + INTEGER numberOfLines; + INTEGER numberOfColumns; + REAL lineDirGridStep; + REAL columnDirGridStep; + EBYTE gridOrigin; +} REFERENCEGRID_VISIR; + +typedef struct { + EBYTE typeOfProjection; + REAL longitudeOfSSP; + REFERENCEGRID_VISIR referencegrid_visir; + // rest of record omitted, for now +} IMAGE_DESCRIPTION_RECORD; + +// disable byte-packing +#pragma pack() + +// endian conversion routines +void to_native(GP_PK_HEADER& h); +void to_native(GP_PK_SH1& h); +void to_native(SUB_VISIRLINE& v); +void to_native(RADIOMETRIC_PROCCESSING_RECORD& r); +void to_native(IMAGE_DESCRIPTION_RECORD& r); + +// utility function, alters string fields permanently +void to_string(PH_DATA& d); + +// unit tests on structures, returns true on success +bool perform_type_size_check(void); + +class Conversions { +public: + static void convert_pixel_to_geo(double line, double column, double& longitude, double& latitude); + static void convert_geo_to_pixel(double longitude, double latitude, unsigned int& line, unsigned int& column); + + static void compute_pixel_xyz(double line, double column, double& x, double& y, double& z); + static double compute_pixel_area_sqkm(double line, double column); + + static const double altitude; // from origin + static const double req; // earth equatorial radius + static const double rpol; // earth polar radius + static const double oblate; // oblateness of earth + static const double deg_to_rad; + static const double rad_to_deg; + static const double step; // pixel / line step in degrees + static const double nlines; // number of lines in an image + + static const int CFAC; // Column scale factor + static const int LFAC; // Line scale factor + static const int COFF; // Column offset + static const int LOFF; // Line offset + +}; + +} // msg_native_format + +#endif + diff --git a/bazaar/plugin/gdal/frmts/msgn/msg_reader_core.cpp b/bazaar/plugin/gdal/frmts/msgn/msg_reader_core.cpp new file mode 100644 index 000000000..4a00ea889 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msgn/msg_reader_core.cpp @@ -0,0 +1,284 @@ +/****************************************************************************** + * $Id: msg_reader_core.cpp 24910 2012-09-05 17:52:18Z rouault $ + * + * Project: MSG Native Reader + * Purpose: Base class for reading in the headers of MSG native images + * Author: Frans van den Bergh, fvdbergh@csir.co.za + * + ****************************************************************************** + * Copyright (c) 2005, Frans van den Bergh + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "msg_reader_core.h" +#include "msg_basic_types.h" +#include +#include +#include + +#ifdef DEBUG +#ifdef GDAL_SUPPORT +#undef DEBUG +#endif +#endif + +#ifdef GDAL_SUPPORT +#include "cpl_vsi.h" + +CPL_CVSID("$Id: msg_reader_core.cpp 24910 2012-09-05 17:52:18Z rouault $"); + +#else +#define VSIFSeek(fp, pos, ref) fseek(fp, pos, ref) +#define VSIFRead(p, bs, nb, fp) fread(p, bs, nb, fp) +#endif + +namespace msg_native_format { + +const Blackbody_lut_type Msg_reader_core::Blackbody_LUT[MSG_NUM_CHANNELS+1] = { + {0,0,0}, // dummy channel + {0,0,0}, // N/A + {0,0,0}, // N/A + {0,0,0}, // N/A + {2569.094, 0.9959, 3.471}, + {1598.566, 0.9963, 2.219}, + {1362.142, 0.9991, 0.485}, + {1149.083, 0.9996, 0.181}, + {1034.345, 0.9999, 0.060}, + { 930.659, 0.9983, 0.627}, + { 839.661, 0.9988, 0.397}, + { 752.381, 0.9981, 0.576}, + {0,0,0} // N/A +}; + + +Msg_reader_core::Msg_reader_core(const char* fname) { + + FILE* fin = fopen(fname, "rb"); + if (!fin) { + fprintf(stderr, "Could not open file %s\n", fname); + return; + } + read_metadata_block(fin); +} + +Msg_reader_core::Msg_reader_core(FILE* fp) { + read_metadata_block(fp); +} + + +void Msg_reader_core::read_metadata_block(FILE* fin) { + _open_success = true; + + unsigned int i; + + VSIFRead(&_main_header, sizeof(_main_header), 1, fin); + VSIFRead(&_sec_header, sizeof(_sec_header), 1, fin); + +#ifdef DEBUG + // print out all the fields in the header + PH_DATA* hd = (PH_DATA*)&_main_header; + for (int i=0; i < 6; i++) { + to_string(*hd); + printf("[%02d] %s %s", i, hd->name, hd->value); + hd++; + } + PH_DATA_ID* hdi = (PH_DATA_ID*)&_main_header.dataSetIdentification; + + for (i=0; i < 5; i++) { + printf("%s %s %s", hdi->name, hdi->size, hdi->address); + hdi++; + } + hd = (PH_DATA*)(&_main_header.totalFileSize); + for (int i=0; i < 19; i++) { + to_string(*hd); + printf("[%02d] %s %s", i, hd->name, hd->value); + hd++; + } +#endif // DEBUG + + // extract data & header positions + + for (i=0; i < 5; i++) { + PH_DATA_ID* hdi = (PH_DATA_ID*)&_main_header.dataSetIdentification[i]; + if (strncmp(hdi->name, "15Header", strlen("15Header")) == 0) { + sscanf(hdi->size, "%d", &_f_header_size); + sscanf(hdi->address, "%d", &_f_header_offset); + } else + if (strncmp(hdi->name, "15Data", strlen("15Data")) == 0) { + sscanf(hdi->size, "%d", &_f_data_size); + sscanf(hdi->address, "%d", &_f_data_offset); + } + } +#ifdef DEBUG + printf("Data: %d %d\n", _f_data_offset, _f_data_size); + printf("Header: %d %d\n", _f_header_offset, _f_header_size); +#endif // DEBUG + + unsigned int lines; + sscanf(_sec_header.northLineSelectedRectangle.value, "%d", &_lines); + sscanf(_sec_header.southLineSelectedRectangle.value, "%d", &lines); + _line_start = lines; + _lines -= lines - 1; + + unsigned int cols; + sscanf(_sec_header.westColumnSelectedRectangle.value, "%d", &_columns); + sscanf(_sec_header.eastColumnSelectedRectangle.value, "%d", &cols); + _col_start = cols; + _columns -= cols - 1; + +#ifdef DEBUG + printf("lines = %d, cols = %d\n", _lines, _columns); +#endif // DEBUG + + int records_per_line = 0; + for (i=0; i < MSG_NUM_CHANNELS; i++) { + if (_sec_header.selectedBandIds.value[i] == 'X') { + _bands[i] = 1; + records_per_line += (i == (MSG_NUM_CHANNELS-1)) ? 3 : 1; + } else { + _bands[i] = 0; + } + } + +#ifdef DEBUG + printf("reading a total of %d records per line\n", records_per_line); +#endif // DEBUG + + // extract time fields, assume that SNIT is the correct field: + sscanf(_main_header.snit.value + 0, "%04d", &_year); + sscanf(_main_header.snit.value + 4, "%02d", &_month); + sscanf(_main_header.snit.value + 6, "%02d", &_day); + sscanf(_main_header.snit.value + 8, "%02d", &_hour); + sscanf(_main_header.snit.value + 10, "%02d", &_minute); + + // read radiometric block + RADIOMETRIC_PROCCESSING_RECORD rad; + off_t offset = RADIOMETRICPROCESSING_RECORD_OFFSET + _f_header_offset + sizeof(GP_PK_HEADER) + sizeof(GP_PK_SH1) + 1; + VSIFSeek(fin, offset, SEEK_SET); + VSIFRead(&rad, sizeof(RADIOMETRIC_PROCCESSING_RECORD), 1, fin); + to_native(rad); + memcpy((void*)_calibration, (void*)&rad.level1_5ImageCalibration,sizeof(_calibration)); + +#ifdef DEBUG + for (unsigned int i=0; i < MSG_NUM_CHANNELS; i++) { + if (_calibration[i].cal_slope < 0 || _calibration[i].cal_slope > 0.4) { + printf("Warning: calibration slope (%f) out of nominal range. MSG reader probably broken\n", _calibration[i].cal_slope); + + } + if (_calibration[i].cal_offset > 0 || _calibration[i].cal_offset < -20) { + printf("Warning: calibration offset (%f) out of nominal range. MSG reader probably broken\n", _calibration[i].cal_offset); + } + } +#endif + + // read image description block + IMAGE_DESCRIPTION_RECORD idr; + offset = RADIOMETRICPROCESSING_RECORD_OFFSET - IMAGEDESCRIPTION_RECORD_LENGTH + _f_header_offset + sizeof(GP_PK_HEADER) + sizeof(GP_PK_SH1) + 1; + VSIFSeek(fin, offset, SEEK_SET); + VSIFRead(&idr, sizeof(IMAGE_DESCRIPTION_RECORD), 1, fin); + to_native(idr); + _line_dir_step = idr.referencegrid_visir.lineDirGridStep; + _col_dir_step = idr.referencegrid_visir.columnDirGridStep; + + + // Rather convoluted, but this code is required to compute the real data block sizes + // It does this by reading in the first line of every band, to get to the packet size field + GP_PK_HEADER gp_header; + GP_PK_SH1 sub_header; + SUB_VISIRLINE visir_line; + + VSIFSeek(fin, _f_data_offset, SEEK_SET); + + _hrv_packet_size = 0; + _interline_spacing = 0; + visir_line.channelId = 0; + + int scanned_bands[MSG_NUM_CHANNELS]; + int band_count = 0; + for (i=0; i < MSG_NUM_CHANNELS; i++) { + scanned_bands[i] = _bands[i]; + band_count += _bands[i]; + } + + do { + VSIFRead(&gp_header, sizeof(GP_PK_HEADER), 1, fin); + VSIFRead(&sub_header, sizeof(GP_PK_SH1), 1, fin); + VSIFRead(&visir_line, sizeof(SUB_VISIRLINE), 1, fin); + to_native(visir_line); + to_native(gp_header); + + // skip over the actual line data + VSIFSeek(fin, + gp_header.packetLength - (sizeof(GP_PK_SH1) + sizeof(SUB_VISIRLINE) - 1), + SEEK_CUR + ); + + if (visir_line.channelId == 0 || visir_line.channelId > MSG_NUM_CHANNELS) { + _open_success = false; + break; + } + + if (scanned_bands[visir_line.channelId - 1]) { + scanned_bands[visir_line.channelId - 1] = 0; + band_count--; + + if (visir_line.channelId != 12) { // not the HRV channel + _visir_bytes_per_line = gp_header.packetLength - (sizeof(GP_PK_SH1) + sizeof(SUB_VISIRLINE) - 1); + _visir_packet_size = gp_header.packetLength + sizeof(GP_PK_HEADER) + 1; + _interline_spacing += _visir_packet_size; + } else { + _hrv_bytes_per_line = gp_header.packetLength - (sizeof(GP_PK_SH1) + sizeof(SUB_VISIRLINE) - 1); + _hrv_packet_size = gp_header.packetLength + sizeof(GP_PK_HEADER) + 1; + _interline_spacing += 3*_hrv_packet_size; + VSIFSeek(fin, 2*gp_header.packetLength, SEEK_CUR ); + } + } + } while (band_count > 0); +} + +#ifndef GDAL_SUPPORT + +int Msg_reader_core::_chan_to_idx(Msg_channel_names channel) { + unsigned int idx = 0; + while (idx < MSG_NUM_CHANNELS) { + if ( (1 << (idx+1)) == (int)channel ) { + return idx; + } + idx++; + } + return 0; +} + +void Msg_reader_core::get_pixel_geo_coordinates(unsigned int line, unsigned int column, double& longitude, double& latitude) { + Conversions::convert_pixel_to_geo((unsigned int)(line + _line_start), (unsigned int)(column + _col_start), longitude, latitude); +} + +void Msg_reader_core::get_pixel_geo_coordinates(double line, double column, double& longitude, double& latitude) { + Conversions::convert_pixel_to_geo(line + _line_start, column + _col_start, longitude, latitude); +} + +double Msg_reader_core::compute_pixel_area_sqkm(double line, double column) { + return Conversions::compute_pixel_area_sqkm(line + _line_start, column + _col_start); +} + +#endif // GDAL_SUPPORT + +} // namespace msg_native_format + diff --git a/bazaar/plugin/gdal/frmts/msgn/msg_reader_core.h b/bazaar/plugin/gdal/frmts/msgn/msg_reader_core.h new file mode 100644 index 000000000..d65a862f9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msgn/msg_reader_core.h @@ -0,0 +1,151 @@ +/****************************************************************************** + * $Id: msg_reader_core.h 11698 2007-06-25 16:33:06Z warmerdam $ + * + * Project: MSG Native Reader + * Purpose: Base class for reading in the headers of MSG native images + * Author: Frans van den Bergh, fvdbergh@csir.co.za + * + ****************************************************************************** + * Copyright (c) 2005, Frans van den Bergh + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef MSG_READER_CORE_H +#define MSG_READER_CORE_H + +#include "msg_basic_types.h" +#include + +namespace msg_native_format { + +const unsigned int MSG_NUM_CHANNELS = 12; + +typedef struct { + double vc; + double A; + double B; +} Blackbody_lut_type; + +typedef enum { + VIS0_6 = 2, + VIS0_8 = 4, + NIR1_6 = 8, + IR3_9 = 16, + IR6_2 = 32, + IR7_3 = 64, + IR8_7 = 128, + IR9_7 = 256, + IR10_8 = 512, + IR12_0 = 1024, + IR13_4 = 2048, + HRV = 4096 +} Msg_channel_names; + +class Msg_reader_core { +public: + Msg_reader_core(const char* fname); + Msg_reader_core(FILE* fp); + virtual ~Msg_reader_core(void) {}; + + bool get_open_success(void) { return _open_success; } + + #ifndef GDAL_SUPPORT + virtual void radiance_to_blackbody(int using_chan_no = 0) = 0; // can override which channel's parameters to use + virtual double* get_data(int chan_no=0) = 0; + #endif + + unsigned int get_lines(void) { return _lines; } + unsigned int get_columns(void) { return _columns; } + + void get_pixel_geo_coordinates(unsigned int line, unsigned int column, double& longitude, double& latitude); // x and y relative to this image, not full disc image + void get_pixel_geo_coordinates(double line, double column, double& longitude, double& latitude); // x and y relative to this image, not full disc image + double compute_pixel_area_sqkm(double line, double column); + + static const Blackbody_lut_type Blackbody_LUT[MSG_NUM_CHANNELS+1]; + + unsigned int get_year(void) { return _year; } + unsigned int get_month(void) { return _month; } + unsigned int get_day(void) { return _day; } + unsigned int get_hour(void) { return _hour; } + unsigned int get_minute(void) { return _minute; } + + unsigned int get_line_start(void) { return _line_start; } + unsigned int get_col_start(void) { return _col_start; } + + float get_col_dir_step(void) { return _col_dir_step; } + float get_line_dir_step(void) { return _line_dir_step; } + + unsigned int get_f_data_offset(void) { return _f_data_offset; } + unsigned int get_visir_bytes_per_line(void) { return _visir_bytes_per_line; } + unsigned int get_visir_packet_size(void) { return _visir_packet_size; } + unsigned int get_hrv_bytes_per_line(void) { return _hrv_bytes_per_line; } + unsigned int get_hrv_packet_size(void) { return _hrv_packet_size; } + unsigned int get_interline_spacing(void) { return _interline_spacing; } + + unsigned char* get_band_map(void) { return _bands; } + + CALIBRATION* get_calibration_parameters(void) { return _calibration; } + +private: + void read_metadata_block(FILE* fp); + +protected: + + int _chan_to_idx(Msg_channel_names channel); + + unsigned int _lines; + unsigned int _columns; + + unsigned int _line_start; + unsigned int _col_start; + + float _col_dir_step; + float _line_dir_step; + + MAIN_PROD_HEADER _main_header; + SECONDARY_PROD_HEADER _sec_header; + CALIBRATION _calibration[MSG_NUM_CHANNELS]; + + unsigned int _f_data_offset; + unsigned int _f_data_size; + unsigned int _f_header_offset; + unsigned int _f_header_size; + + unsigned int _visir_bytes_per_line; // packed length of a VISIR line, without headers + unsigned int _visir_packet_size; // effectively, the spacing between lines of consecutive bands in bytes + unsigned int _hrv_bytes_per_line; + unsigned int _hrv_packet_size; + unsigned int _interline_spacing; + + unsigned char _bands[MSG_NUM_CHANNELS]; + + unsigned int _year; + unsigned int _month; + unsigned int _day; + unsigned int _hour; + unsigned int _minute; + + bool _open_success; +}; + +}// namespace msg_native_format + +#endif + diff --git a/bazaar/plugin/gdal/frmts/msgn/msgndataset.cpp b/bazaar/plugin/gdal/frmts/msgn/msgndataset.cpp new file mode 100644 index 000000000..244652519 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/msgn/msgndataset.cpp @@ -0,0 +1,546 @@ +/****************************************************************************** + * $Id: msgndataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: MSG Native Reader + * Purpose: All code for EUMETSAT Archive format reader + * Author: Frans van den Bergh, fvdbergh@csir.co.za + * + ****************************************************************************** + * Copyright (c) 2005, Frans van den Bergh + * Copyright (c) 2008-2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "ogr_spatialref.h" + +#include "msg_reader_core.h" +using namespace msg_native_format; + +CPL_CVSID("$Id: msgndataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +CPL_C_START +void GDALRegister_MSGN(void); +CPL_C_END + +typedef enum { + MODE_VISIR, // Visible and Infrared bands (1 through 11) in 10-bit raw mode + MODE_HRV, // Pan band (band 11) only, in 10-bit raw mode + MODE_RAD // Black-body temperature (K) for thermal bands only (4-10), 64-bit float + } open_mode_type; + +class MSGNRasterBand; + +/************************************************************************/ +/* ==================================================================== */ +/* MSGNDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class MSGNDataset : public GDALDataset +{ + friend class MSGNRasterBand; + + FILE *fp; + + Msg_reader_core* msg_reader_core; + double adfGeoTransform[6]; + char *pszProjection; + + public: + MSGNDataset(); + ~MSGNDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef(); + +}; + +/************************************************************************/ +/* ==================================================================== */ +/* MSGNRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class MSGNRasterBand : public GDALRasterBand +{ + friend class MSGNDataset; + + unsigned int packet_size; + unsigned int bytes_per_line; + unsigned int interline_spacing; + unsigned int orig_band_no; // The name of the band + unsigned int band_in_file; // The effective index of the band in the file + open_mode_type open_mode; + + double GetNoDataValue (int *pbSuccess=NULL) { + if (pbSuccess) { + *pbSuccess = 1; + } + return MSGN_NODATA_VALUE; + } + + double MSGN_NODATA_VALUE; + + char band_description[30]; + + public: + + MSGNRasterBand( MSGNDataset *, int , open_mode_type mode, int orig_band_no, int band_in_file); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum(int *pbSuccess = NULL ); + virtual const char* GetDescription() const { return band_description; } +}; + + +/************************************************************************/ +/* MSGNRasterBand() */ +/************************************************************************/ + +MSGNRasterBand::MSGNRasterBand( MSGNDataset *poDS, int nBand , open_mode_type mode, int orig_band_no, int band_in_file) + +{ + this->poDS = poDS; + this->nBand = nBand; // GDAL's band number, i.e. always starts at 1 + this->open_mode = mode; + this->orig_band_no = orig_band_no; + this->band_in_file = band_in_file; + + sprintf(band_description, "band %02d", orig_band_no); + + if (mode != MODE_RAD) { + eDataType = GDT_UInt16; + MSGN_NODATA_VALUE = 0; + } else { + eDataType = GDT_Float64; + MSGN_NODATA_VALUE = -1000; + } + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + if (mode != MODE_HRV) { + packet_size = poDS->msg_reader_core->get_visir_packet_size(); + bytes_per_line = poDS->msg_reader_core->get_visir_bytes_per_line(); + } else { + packet_size = poDS->msg_reader_core->get_hrv_packet_size(); + bytes_per_line = poDS->msg_reader_core->get_hrv_bytes_per_line(); + } + + interline_spacing = poDS->msg_reader_core->get_interline_spacing(); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr MSGNRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) + +{ + MSGNDataset *poGDS = (MSGNDataset *) poDS; + + // invert y position + int i_nBlockYOff = poDS->GetRasterYSize() - 1 - nBlockYOff; + + char *pszRecord; + + unsigned int data_length = bytes_per_line + sizeof(SUB_VISIRLINE); + unsigned int data_offset = 0; + + if (open_mode != MODE_HRV) { + data_offset = poGDS->msg_reader_core->get_f_data_offset() + + interline_spacing*i_nBlockYOff + (band_in_file-1)*packet_size + + (packet_size - data_length); + } else { + data_offset = poGDS->msg_reader_core->get_f_data_offset() + + interline_spacing*(int(i_nBlockYOff/3) + 1) - + packet_size*(3 - (i_nBlockYOff % 3)) + (packet_size - data_length); + } + + VSIFSeek( poGDS->fp, data_offset, SEEK_SET ); + + pszRecord = (char *) CPLMalloc(data_length); + size_t nread = VSIFRead( pszRecord, 1, data_length, poGDS->fp ); + + SUB_VISIRLINE* p = (SUB_VISIRLINE*) pszRecord; + to_native(*p); + + if (p->lineValidity != 1) { + for (int c=0; c < nBlockXSize; c++) { + if (open_mode != MODE_RAD) { + ((GUInt16 *)pImage)[c] = (GUInt16)MSGN_NODATA_VALUE; + } else { + ((double *)pImage)[c] = MSGN_NODATA_VALUE; + } + } + } + + if ( nread != data_length || + ( open_mode != MODE_HRV && (p->lineNumberInVisirGrid - poGDS->msg_reader_core->get_line_start()) != (unsigned int)i_nBlockYOff ) + ) { // no sophisticated checking for HRV at the moment + CPLFree( pszRecord ); + + CPLError( CE_Failure, CPLE_AppDefined, "MSGN Scanline corrupt." ); + + return CE_Failure; + } + + // unpack the 10-bit values into 16-bit unsigned short ints + unsigned char *cptr = (unsigned char*)pszRecord + + (data_length - bytes_per_line); + int bitsLeft = 8; + unsigned short value = 0; + + if (open_mode != MODE_RAD) { + for (int c=0; c < nBlockXSize; c++) { + value = 0; + for (int bit=0; bit < 10; bit++) { + value <<= 1; + if (*cptr & 128) { + value |= 1; + } + *cptr <<= 1; + bitsLeft--; + if (bitsLeft == 0) { + cptr++; + bitsLeft = 8; + } + } + ((GUInt16 *)pImage)[nBlockXSize-1 - c] = value; + } + } else { + // radiance mode + for (int c=0; c < nBlockXSize; c++) { + value = 0; + for (int bit=0; bit < 10; bit++) { + value <<= 1; + if (*cptr & 128) { + value |= 1; + } + *cptr <<= 1; + bitsLeft--; + if (bitsLeft == 0) { + cptr++; + bitsLeft = 8; + } + } + double dvalue = double(value); + double bbvalue = MSGN_NODATA_VALUE; + bbvalue = dvalue * poGDS->msg_reader_core->get_calibration_parameters()[orig_band_no-1].cal_slope + + poGDS->msg_reader_core->get_calibration_parameters()[orig_band_no-1].cal_offset; + + ((double *)pImage)[nBlockXSize-1 -c] = bbvalue; + } + } + CPLFree( pszRecord ); + return CE_None; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ +double MSGNRasterBand::GetMinimum( int *pbSuccess ) { + if (pbSuccess) { + *pbSuccess = 1; + } + return open_mode != MODE_RAD ? 1 : GDALRasterBand::GetMinimum(pbSuccess); +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ +double MSGNRasterBand::GetMaximum(int *pbSuccess ) { + if (pbSuccess) { + *pbSuccess = 1; + } + return open_mode != MODE_RAD ? 1023 : GDALRasterBand::GetMaximum(pbSuccess); +} + +/************************************************************************/ +/* ==================================================================== */ +/* MSGNDataset */ +/* ==================================================================== */ +/************************************************************************/ + +MSGNDataset::MSGNDataset() { + pszProjection = CPLStrdup(""); + msg_reader_core = 0; +} + +/************************************************************************/ +/* ~MSGNDataset() */ +/************************************************************************/ + +MSGNDataset::~MSGNDataset() + +{ + if( fp != NULL ) + VSIFClose( fp ); + + if (msg_reader_core) { + delete msg_reader_core; + } + + CPLFree(pszProjection); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr MSGNDataset::GetGeoTransform( double * padfTransform ) + +{ + + for (int i=0; i < 6; i++) { + padfTransform[i] = adfGeoTransform[i]; + } + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *MSGNDataset::GetProjectionRef() + +{ + return ( pszProjection ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *MSGNDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + open_mode_type open_mode = MODE_VISIR; + GDALOpenInfo* open_info = poOpenInfo; + + if (!poOpenInfo->bStatOK) { + if ( EQUALN(poOpenInfo->pszFilename, "HRV:", 4) ) { + open_info = new GDALOpenInfo(&poOpenInfo->pszFilename[4], poOpenInfo->eAccess); + open_mode = MODE_HRV; + } else + if ( EQUALN(poOpenInfo->pszFilename, "RAD:", 4 ) ) { + open_info = new GDALOpenInfo(&poOpenInfo->pszFilename[4], poOpenInfo->eAccess); + open_mode = MODE_RAD; + } + } + +/* -------------------------------------------------------------------- */ +/* Before trying MSGNOpen() we first verify that there is at */ +/* least one "\n#keyword" type signature in the first chunk of */ +/* the file. */ +/* -------------------------------------------------------------------- */ + if( open_info->fpL == NULL || open_info->nHeaderBytes < 50 ) + return NULL; + + /* check if this is a "NATIVE" MSG format image */ + if( !EQUALN((char *)open_info->pabyHeader, + "FormatName : NATIVE", 36) ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The MSGN driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + MSGNDataset *poDS; + FILE* fp = VSIFOpen( open_info->pszFilename, "rb" ); + if( fp == NULL ) + return NULL; + + poDS = new MSGNDataset(); + + poDS->fp = fp; + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + // first reset the file pointer, then hand over to the msg_reader_core + VSIFSeek( poDS->fp, 0, SEEK_SET ); + + poDS->msg_reader_core = new Msg_reader_core(poDS->fp); + + if (!poDS->msg_reader_core->get_open_success()) { + delete poDS; + return NULL; + } + + poDS->nRasterXSize = poDS->msg_reader_core->get_columns(); + poDS->nRasterYSize = poDS->msg_reader_core->get_lines(); + + if (open_mode == MODE_HRV) { + poDS->nRasterXSize *= 3; + poDS->nRasterYSize *= 3; + } + + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + unsigned int i; + unsigned int band_count = 1; + unsigned int missing_band_count = 0; + unsigned char* bands = poDS->msg_reader_core->get_band_map(); + unsigned char band_map[MSG_NUM_CHANNELS+1]; // map GDAL band numbers to MSG channels + for (i=0; i < MSG_NUM_CHANNELS; i++) { + if (bands[i]) { + bool ok_to_add = false; + switch (open_mode) { + case MODE_VISIR: + ok_to_add = i < MSG_NUM_CHANNELS - 1; + break; + case MODE_RAD: + ok_to_add = (i <= 2) || (Msg_reader_core::Blackbody_LUT[i+1].B != 0); + break; + case MODE_HRV: + ok_to_add = i == MSG_NUM_CHANNELS - 1; + break; + } + if (ok_to_add) { + poDS->SetBand( band_count, new MSGNRasterBand( poDS, band_count, open_mode, i+1, i+1 - missing_band_count)); + band_map[band_count] = (unsigned char) (i+1); + band_count++; + } + } else { + missing_band_count++; + } + } + + double pixel_gsd_x; + double pixel_gsd_y; + double origin_x; + double origin_y; + + if (open_mode != MODE_HRV) { + pixel_gsd_x = 1000 * poDS->msg_reader_core->get_col_dir_step(); // convert from km to m + pixel_gsd_y = 1000 * poDS->msg_reader_core->get_line_dir_step(); // convert from km to m + origin_x = -pixel_gsd_x * (-(Conversions::nlines / 2.0) + poDS->msg_reader_core->get_col_start()); + origin_y = -pixel_gsd_y * ((Conversions::nlines / 2.0) - poDS->msg_reader_core->get_line_start()); + } else { + pixel_gsd_x = 1000 * poDS->msg_reader_core->get_col_dir_step() / 3.0; // convert from km to m, approximate for HRV + pixel_gsd_y = 1000 * poDS->msg_reader_core->get_line_dir_step() / 3.0; // convert from km to m, approximate for HRV + origin_x = -pixel_gsd_x * (-(3*Conversions::nlines / 2.0) + 3*poDS->msg_reader_core->get_col_start()); + origin_y = -pixel_gsd_y * ((3*Conversions::nlines / 2.0) - 3*poDS->msg_reader_core->get_line_start()); + } + + poDS->adfGeoTransform[0] = origin_x; + poDS->adfGeoTransform[1] = pixel_gsd_x; + poDS->adfGeoTransform[2] = 0.0; + + poDS->adfGeoTransform[3] = origin_y; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -pixel_gsd_y; + + OGRSpatialReference oSRS; + + oSRS.SetProjCS("Geostationary projection (MSG)"); + oSRS.SetGEOS( 0, 35785831, 0, 0 ); + oSRS.SetGeogCS( + "MSG Ellipsoid", + "MSG_DATUM", + "MSG_SPHEROID", + Conversions::rpol * 1000.0, + 1 / ( 1 - Conversions::rpol/Conversions::req) + ); + + oSRS.exportToWkt( &(poDS->pszProjection) ); + + CALIBRATION* cal = poDS->msg_reader_core->get_calibration_parameters(); + char tagname[30]; + char field[300]; + + poDS->SetMetadataItem("Radiometric parameters format", "offset slope"); + for (i=1; i < band_count; i++) { + sprintf(tagname, "ch%02d_cal", band_map[i]); + sprintf(field, "%.12e %.12e", cal[band_map[i]-1].cal_offset, cal[band_map[i]-1].cal_slope); + poDS->SetMetadataItem(tagname, field); + } + + sprintf(field, "%04d%02d%02d/%02d:%02d", + poDS->msg_reader_core->get_year(), + poDS->msg_reader_core->get_month(), + poDS->msg_reader_core->get_day(), + poDS->msg_reader_core->get_hour(), + poDS->msg_reader_core->get_minute() + ); + poDS->SetMetadataItem("Date/Time", field); + + sprintf(field, "%d %d", + poDS->msg_reader_core->get_line_start(), + poDS->msg_reader_core->get_col_start() + ); + poDS->SetMetadataItem("Origin", field); + + + if (open_info != poOpenInfo) { + delete open_info; + } + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_MSGN() */ +/************************************************************************/ + +void GDALRegister_MSGN() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "MSGN" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "MSGN" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "EUMETSAT Archive native (.nat)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_msgn.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "nat" ); + + poDriver->pfnOpen = MSGNDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/netcdf/GNUmakefile b/bazaar/plugin/gdal/frmts/netcdf/GNUmakefile new file mode 100644 index 000000000..e0690671d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/netcdf/GNUmakefile @@ -0,0 +1,27 @@ + +include ../../GDALmake.opt + +OBJ = netcdfdataset.o gmtdataset.o + +XTRA_OPT = +ifeq ($(NETCDF_HAS_NC4),yes) +XTRA_OPT := -DNETCDF_HAS_NC4 $(XTRA_OPT) +endif +ifeq ($(NETCDF_HAS_HDF4),yes) +XTRA_OPT := -DNETCDF_HAS_HDF4 $(XTRA_OPT) +endif +ifeq ($(HAVE_HDF4),yes) +XTRA_OPT := -DHAVE_HDF4 $(XTRA_OPT) +endif +ifeq ($(HAVE_HDF5),yes) +XTRA_OPT := -DHAVE_HDF5 $(XTRA_OPT) +endif + +CPPFLAGS := $(CPPFLAGS) $(XTRA_OPT) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/netcdf/frmt_netcdf.html b/bazaar/plugin/gdal/frmts/netcdf/frmt_netcdf.html new file mode 100644 index 000000000..b595e6acd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/netcdf/frmt_netcdf.html @@ -0,0 +1,355 @@ + + +NetCDF network Common Data Form + + + + +

    NetCDF: Network Common Data Form

    + + +This format is supported for read and write access. + +NetCDF is an interface for +array-oriented data access and is used for representing scientific data.

    + + +The fill value metadata or missing_value backward compatibililty is preserved +as NODATA value when available. +

    + +NOTE: Implemented as gdal/frmts/netcdf/netcdfdataset.cpp.

    + +

    Multiple Image Handling (Subdatasets)

    + +Nework Command Data Form is a container for several different arrays most +used for storing scientific dataset. One netCDF file may +contain several datasets. They may differ in size, number of dimensions and +may represent data for different regions. +

    +If the file contains only one netCDF array which appears to be an image, +it may be accessed directly, but if the file contains multiple images it may +be necessary to import the file via a two step process.

    +

    +The first step is to get a report of the components images (dataset) in the +file using gdalinfo, and then to import the desired images using +gdal_translate. The gdalinfo utility lists all multidimensional subdatasets +from the input netCDF file. +

    +The name of individual images are assigned to the SUBDATASET_n_NAME +metadata item. The description for each image is found in the +SUBDATASET_n_DESC metadata item. For netCDF images will follow this format: + +NETCDF:filename:variable_name

    + +where filename is the name of the input file, and +variable_name is the dataset selected withing the file.

    + +On the second step you provide this name for gdalinfo to get information +about the dataset or + +gdal_translate to read dataset.

    + +For example, we want to read data from a netCDF file:

    +

    +$ gdalinfo sst.nc
    +Driver: netCDF/Network Common Data Format
    +Size is 512, 512
    +Coordinate System is `'
    +Metadata:
    +  NC_GLOBAL#title=IPSL  model output prepared for IPCC Fourth Assessment SRES A2 experiment
    +  NC_GLOBAL#institution=IPSL (Institut Pierre Simon Laplace, Paris, France)
    +  NC_GLOBAL#source=IPSL-CM4_v1 (2003) : atmosphere : LMDZ (IPSL-CM4_IPCC, 96x71x19) ; ocean ORCA2 (ipsl_cm4_v1_8, 2x2L31); sea ice LIM (ipsl_cm4_v
    +  NC_GLOBAL#contact=Sebastien Denvil, sebastien.denvil@ipsl.jussieu.fr
    +  NC_GLOBAL#project_id=IPCC Fourth Assessment
    +  NC_GLOBAL#table_id=Table O1 (13 November 2004)
    +  NC_GLOBAL#experiment_id=SRES A2 experiment
    +  NC_GLOBAL#realization=1
    +  NC_GLOBAL#cmor_version=9.600000e-01
    +  NC_GLOBAL#Conventions=CF-1.0
    +  NC_GLOBAL#history=YYYY/MM/JJ: data generated; YYYY/MM/JJ+1 data transformed  At 16:37:23 on 01/11/2005, CMOR rewrote data to comply with CF standards and IPCC Fourth Assessment requirements
    +  NC_GLOBAL#references=Dufresne et al, Journal of Climate, 2015, vol XX, p 136
    +  NC_GLOBAL#comment=Test drive
    +Subdatasets:
    +  SUBDATASET_1_NAME=NETCDF:"sst.nc":lon_bnds
    +  SUBDATASET_1_DESC=[180x2] lon_bnds (64-bit floating-point)
    +  SUBDATASET_2_NAME=NETCDF:"sst.nc":lat_bnds
    +  SUBDATASET_2_DESC=[170x2] lat_bnds (64-bit floating-point)
    +  SUBDATASET_3_NAME=NETCDF:"sst.nc":time_bnds
    +  SUBDATASET_3_DESC=[24x2] time_bnds (64-bit floating-point)
    +  SUBDATASET_4_NAME=NETCDF:"sst.nc":tos
    +  SUBDATASET_4_DESC=[24x170x180] sea_surface_temperature (32-bit floating-point)Corner Coordinates:
    +Upper Left  (    0.0,    0.0)
    +Lower Left  (    0.0,  512.0)
    +Upper Right (  512.0,    0.0)
    +Lower Right (  512.0,  512.0)
    +Center      (  256.0,  256.0)
    +
    + +This netCDF files contain 4 datasets, lon_bnds, lat_bnds, tim_bnds and tos. + +Now select the subdataset, described as: + +

    NETCDF:"sst.nc":tos

    +[24x17x180] sea_surface_temperature (32-bit floating-point)

    +and get the information about the number of bands there is inside this +variable. + +

    +$ gdalinfo NETCDF:"sst.nc":tos
    +Driver: netCDF/Network Common Data Format
    +Size is 180, 170
    +Coordinate System is `'
    +Origin = (1.000000,-79.500000)
    +Pixel Size = (1.98888889,0.99411765)
    +Metadata:
    +  NC_GLOBAL#title=IPSL  model output prepared for IPCC Fourth Assessment SRES A2 experiment
    +  NC_GLOBAL#institution=IPSL (Institut Pierre Simon Laplace, Paris, France)
    +
    +.... More metadata +
    +  time#standard_name=time
    +  time#long_name=time
    +  time#units=days since 2001-1-1
    +  time#axis=T
    +  time#calendar=360_day
    +  time#bounds=time_bnds
    +  time#original_units=seconds since 2001-1-1
    +Corner Coordinates:
    +Upper Left  (   1.0000000, -79.5000000)
    +Lower Left  (   1.0000000,  89.5000000)
    +Upper Right (     359.000,     -79.500)
    +Lower Right (     359.000,      89.500)
    +Center      ( 180.0000000,   5.0000000)
    +Band 1 Block=180x1 Type=Float32, ColorInterp=Undefined
    +  NoData Value=1e+20
    +  Metadata:
    +    NETCDF_VARNAME=tos
    +    NETCDF_DIMENSION_time=15
    +    NETCDF_time_units=days since 2001-1-1
    +Band 2 Block=180x1 Type=Float32, ColorInterp=Undefined
    +  NoData Value=1e+20
    +  Metadata:
    +    NETCDF_VARNAME=tos
    +    NETCDF_DIMENSION_time=45
    +    NETCDF_time_units=days since 2001-1-1
    +
    +.... More Bands +
    +Band 22 Block=180x1 Type=Float32, ColorInterp=Undefined
    +  NoData Value=1e+20
    +  Metadata:
    +    NETCDF_VARNAME=tos
    +    NETCDF_DIMENSION_time=645
    +    NETCDF_time_units=days since 2001-1-1
    +Band 23 Block=180x1 Type=Float32, ColorInterp=Undefined
    +  NoData Value=1e+20
    +  Metadata:
    +    NETCDF_VARNAME=tos
    +    NETCDF_DIMENSION_time=675
    +    NETCDF_time_units=days since 2001-1-1
    +Band 24 Block=180x1 Type=Float32, ColorInterp=Undefined
    +  NoData Value=1e+20
    +  Metadata:
    +    NETCDF_VARNAME=tos
    +    NETCDF_DIMENSION_time=705
    +    NETCDF_time_units=days since 2001-1-1
    +
    + +gdalinfo display the number of bands into this subdataset. +There are metadata attached to each band. + +In this example, the metadata informs us +that each band correspond to an array of monthly sea surface temperature +from January 2001. There are 24 months of data in this subdataset. + +You may also use gdal_translate for reading the subdataset.

    + +Note that you should provide exactly the contents of the line marked +SUBDATASET_n_NAME to GDAL, including the NETCDF: prefix.

    + +The NETCDF prefix must be first. It triggers the subdataset netCDF driver. + +This driver is intended only for importing remote sensing and geospatial +datasets in form of raster images. If you want explore all data contained in +netCDF file you should use another tools. + +

    Dimension

    + +The netCDF driver assume that data follows the CF-1 convention from +UNIDATA + +The dimensions inside the NetCDF file use the following rules: (Z,Y,X). +If there are more than 3 +dimensions, the driver will merge them into bands. For example if you have +an 4 dimension arrays of the type (P, T, Y, X). The driver will multiply the +last 2 dimensions (P*T). The driver will display the bands in the following +order. It will first increment T and then P. Metadata +will be displayed on each band with its corresponding T and P values. + +

    Georeference

    + +There is no universal way of storing georeferencing in netCDF files. + +The driver first tries to follow the CF-1 Convention from UNIDATA looking for +the Metadata named "grid_mapping". + +If "grid_mapping" is not present, the driver will try to find an +lat/lon grid array to set geotransform array. The NetCDF driver verifies +that the Lat/Lon array is equally space.

    + +If those 2 mehtods fail, NetCDF driver will try to read the following +metadata directly and set up georefenrencing. +

      +
    • spatial_ref (Well Known Text) +
    +
      +
    • GeoTransform (GeoTransform array) +
    +or, +
      +
    • Northernmost_Northing +
    • Southernmost_Northing +
    • Easternmost_Easting +
    • Westernmost_Easting +
    + +

    Creation Issues

    + +This driver supports creation of netCDF file following the CF-1 convention. +You may create set of 2D datasets. Each variable array is named +Band1, Band2, ... BandN.

    + +Each band will have metadata tied to it giving a short description of +the data it contains. + + +

    GDAL NetCDF Metadata

    + +All netCDF attributes are transparently translated as GDAL metadata.

    +The translation follow these directives:

    +

      +
    • Global NetCDF metatadata have a NC_GLOBAL tag prefixed.

      +

    • Dataset metadata have their variable name prefixed.

      +

    • Each prefix is followed by a # sign.

      +

    • The NetCDF attribute follows the form: name=value.

      +

    + +Example:

    +

    +$ gdalinfo NETCDF:"sst.nc":tos
    +Driver: netCDF/Network Common Data Format
    +Size is 180, 170
    +Coordinate System is `'
    +Origin = (1.000000,-79.500000)
    +Pixel Size = (1.98888889,0.99411765)
    +Metadata:
    +
    +NetCDF global attributes

    +

    +  NC_GLOBAL#title=IPSL  model output prepared for IPCC Fourth Assessment SRES A2 experiment
    +
    +Variables attributes for: tos, lon, lat and time +
    +  tos#standard_name=sea_surface_temperature
    +  tos#long_name=Sea Surface Temperature
    +  tos#units=K
    +  tos#cell_methods=time: mean (interval: 30 minutes)
    +  tos#_FillValue=1.000000e+20
    +  tos#missing_value=1.000000e+20
    +  tos#original_name=sosstsst
    +  tos#original_units=degC
    +  tos#history= At   16:37:23 on 01/11/2005: CMOR altered the data in the following ways: added 2.73150E+02 to yield output units;  Cyclical dimension was output starting at a different lon;
    +  lon#standard_name=longitude
    +  lon#long_name=longitude
    +  lon#units=degrees_east
    +  lon#axis=X
    +  lon#bounds=lon_bnds
    +  lon#original_units=degrees_east
    +  lat#standard_name=latitude
    +  lat#long_name=latitude
    +  lat#units=degrees_north
    +  lat#axis=Y
    +  lat#bounds=lat_bnds
    +  lat#original_units=degrees_north
    +  time#standard_name=time
    +  time#long_name=time
    +  time#units=days since 2001-1-1
    +  time#axis=T
    +  time#calendar=360_day
    +  time#bounds=time_bnds
    +  time#original_units=seconds since 2001-1-1
    +
    + +

    Driver Improvements (GDAL >= 1.9.0)

    + +The driver has undergone significant changes in GDAL 1.9.0, see NEWS file and NetCDF Improvements. + +

    Important Changes

    + +
      + +
    • Added support for NC2, NC4 and NC4C file types for reading and writing, and HDF4 for reading. See NetCDF File Format for details. +

        +
      • NC : NetCDF Classic Format: The Original Binary Format.
      • +
      • NC2 : 64-bit Offset Format: Supporting Larger Variables
      • +
      • NC4 : NetCDF-4 Format: Uses HDF5
      • +
      • NC4C : NetCDF-4 Classic Model Format: HDF5 with NetCDF Limitations
      • +
      • HDF4 : HDF4 SD Format
      • +
      +

    • +
    • Improved support for CF-1.5 projected and geographic SRS reading and writing

    • +
    • Improvements to metadata (global and variable) handling

    • +
    • Added simple progress indicator

    • +
    • Added support for DEFLATE compression (reading and writing) and szip (reading) - requires NetCDF-4 support

    • +
    • Added support for valid_range/valid_min/valid_max

    • +
    • Proper handling of signed/unsigned byte data

    • +
    • Added support for Create() function - enables to use netcdf directly with gdalwarp

    • +
    • Added support for CF two-dimensional coordinate variables (see CF Conventions) via GDAL GEOLOCATION arrays (see RFC 4: Geolocation Arrays) (GDAL >= 1.10)

    • +
    + +

    Creation Options

    + +
      + +
    • FORMAT=[NC/NC2/NC4/NC4C]: Set the netcdf file format to use, NC is the default. NC2 is normally supported by recent netcdf installations, but NC4 and NC4C are available if netcdf was compiled with NetCDF-4 (and HDF5) support. +

    • COMPRESS=[NONE/DEFLATE]: Set the compression to use. DEFLATE is only available if netcdf has been compiled with NetCDF-4 support. NC4C format is the default if DEFLATE compression is used.

    • +
    • ZLEVEL=[1-9]: Set the level of compression when using DEFLATE compression. A value of 9 is best, and 1 is least compression. The default is 1, which offers the best time/compression ratio.

    • +
    • WRITE_BOTTOMUP=[YES/NO]: Set the y-axis order for export, overriding the order detected by the driver. NetCDF files are usually assumed "bottom-up", contrary to GDAL's model which is "north up". This normally does not create a problem in the y-axis order, unless there is no y axis geo-referencing. Files without geo-referencing information will be exported in the netcdf default "bottom-up" order, and the contrary for files with geo-referencing. For import see Configuration Option GDAL_NETCDF_BOTTOMUP below.

    • +
    • WRITE_GDAL_TAGS=[YES/NO]: Define if GDAL tags used for georeferencing (spatial_ref and GeoTransform) should be exported, in addition to CF tags. Not all information is stored in the CF tags (such as named datums and EPSG codes), therefore the driver exports these variables by default. In import the CF "grid_mapping" variable takes precedence and the GDAL tags are used if they do not conflict with CF metadata.

    • +
    • WRITE_LONLAT=[YES/NO/IF_NEEDED]: Define if CF lon/lat variables are written to file. Default is YES for geographic SRS and NO for projected SRS. This is normally not necessary for projected SRS as GDAL and many applications use the X/Y dimension variables and CF projection information. Use of IF_NEEDED option creates lon/lat variables if the projection is not part of the CF-1.5 standard.

    • +
    • TYPE_LONLAT=[float/double]: Set the variable type to use for lon/lat variables. Default is double for geographic SRS and float for projected SRS. If lon/lat variables are written for a projected SRS, the file is considerably large (each variable uses X*Y space), therefore TYPE_LONLAT=float and COMPRESS=DEFLATE are advisable in order to save space.

    • +
    • PIXELTYPE=[DEFAULT/SIGNEDBYTE]: By setting this to SIGNEDBYTE, a new Byte file can be forced to be written as signed byte.

    • + +
    + +

    Configuration Options

    + +
      +
    • GDAL_NETCDF_BOTTOMUP=[YES/NO] : Set the y-axis order for import, overriding the order detected by the driver. This option is usually not needed unless a specific dataset is causing problems (which should be reported in GDAL trac).
    • +
    + +

    Driver building

    + +This driver is compiled with the UNIDATA netCDF library.

    + +You need to download or compile the netCDF library before configuring GDAL with netCDF support.

    + +See NetCDF GDAL wiki for build instructions and information regarding HDF4, NetCDF-4 and HDF5. + + +

    See Also:

    + +
    +Full list of GDAL Raster Formats + + + + + + diff --git a/bazaar/plugin/gdal/frmts/netcdf/gmtdataset.cpp b/bazaar/plugin/gdal/frmts/netcdf/gmtdataset.cpp new file mode 100644 index 000000000..0148456d8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/netcdf/gmtdataset.cpp @@ -0,0 +1,623 @@ +/****************************************************************************** + * $Id: gmtdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $ + * + * Project: netCDF read/write Driver + * Purpose: GDAL bindings over netCDF library for GMT Grids. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "gdal_frmts.h" +#include "netcdf.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: gmtdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $"); + +extern CPLMutex *hNCMutex; /* shared with netcdf. See netcdfdataset.cpp */ + +/************************************************************************/ +/* ==================================================================== */ +/* GMTDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GMTRasterBand; + +class GMTDataset : public GDALPamDataset +{ + int z_id; + double adfGeoTransform[6]; + + public: + int cdfid; + + ~GMTDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + + CPLErr GetGeoTransform( double * padfTransform ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GMTRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GMTRasterBand : public GDALPamRasterBand +{ + nc_type nc_datatype; + int nZId; + + public: + + GMTRasterBand( GMTDataset *poDS, int nZId, int nBand ); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + + +/************************************************************************/ +/* GMTRasterBand() */ +/************************************************************************/ + +GMTRasterBand::GMTRasterBand( GMTDataset *poDS, int nZId, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + this->nZId = nZId; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + +/* -------------------------------------------------------------------- */ +/* Get the type of the "z" variable, our target raster array. */ +/* -------------------------------------------------------------------- */ + if( nc_inq_var( poDS->cdfid, nZId, NULL, &nc_datatype, NULL, NULL, + NULL ) != NC_NOERR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error in nc_var_inq() on 'z'." ); + return; + } + + if( nc_datatype == NC_BYTE ) + eDataType = GDT_Byte; + else if( nc_datatype == NC_SHORT ) + eDataType = GDT_Int16; + else if( nc_datatype == NC_INT ) + eDataType = GDT_Int32; + else if( nc_datatype == NC_FLOAT ) + eDataType = GDT_Float32; + else if( nc_datatype == NC_DOUBLE ) + eDataType = GDT_Float64; + else + { + if( nBand == 1 ) + CPLError( CE_Warning, CPLE_AppDefined, + "Unsupported GMT datatype (%d), treat as Float32.", + (int) nc_datatype ); + eDataType = GDT_Float32; + } +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GMTRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + size_t start[2], edge[2]; + int nErr = NC_NOERR; + int cdfid = ((GMTDataset *) poDS)->cdfid; + + CPLMutexHolderD(&hNCMutex); + + start[0] = nBlockYOff * nBlockXSize; + edge[0] = nBlockXSize; + + if( eDataType == GDT_Byte ) + nErr = nc_get_vara_uchar( cdfid, nZId, start, edge, + (unsigned char *) pImage ); + else if( eDataType == GDT_Int16 ) + nErr = nc_get_vara_short( cdfid, nZId, start, edge, + (short int *) pImage ); + else if( eDataType == GDT_Int32 ) + { + if( sizeof(long) == 4 ) + nErr = nc_get_vara_long( cdfid, nZId, start, edge, + (long *) pImage ); + else + nErr = nc_get_vara_int( cdfid, nZId, start, edge, + (int *) pImage ); + } + else if( eDataType == GDT_Float32 ) + nErr = nc_get_vara_float( cdfid, nZId, start, edge, + (float *) pImage ); + else if( eDataType == GDT_Float64 ) + nErr = nc_get_vara_double( cdfid, nZId, start, edge, + (double *) pImage ); + + if( nErr != NC_NOERR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GMT scanline fetch failed: %s", + nc_strerror( nErr ) ); + return CE_Failure; + } + else + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GMTDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ~GMTDataset() */ +/************************************************************************/ + +GMTDataset::~GMTDataset() + +{ + FlushCache(); + nc_close (cdfid); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GMTDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GMTDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Does this file have the GMT magic number? */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->fpL == NULL || poOpenInfo->nHeaderBytes < 50 ) + return NULL; + + if( poOpenInfo->pabyHeader[0] != 'C' + || poOpenInfo->pabyHeader[1] != 'D' + || poOpenInfo->pabyHeader[2] != 'F' + || poOpenInfo->pabyHeader[3] != 1 ) + return NULL; + + CPLMutexHolderD(&hNCMutex); + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ + int cdfid, nm_id, dim_count, z_id; + + if( nc_open( poOpenInfo->pszFilename, NC_NOWRITE, &cdfid ) != NC_NOERR ) + return NULL; + + if( nc_inq_varid( cdfid, "dimension", &nm_id ) != NC_NOERR + || nc_inq_varid( cdfid, "z", &z_id ) != NC_NOERR ) + { +#ifdef notdef + CPLError( CE_Warning, CPLE_AppDefined, + "%s is a GMT file, but not in GMT configuration.", + poOpenInfo->pszFilename ); +#endif + nc_close( cdfid ); + return NULL; + } + + if( nc_inq_ndims( cdfid, &dim_count ) != NC_NOERR || dim_count < 2 ) + { + nc_close( cdfid ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + nc_close( cdfid ); + CPLError( CE_Failure, CPLE_NotSupported, + "The GMT driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GMTDataset *poDS; + + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + poDS = new GMTDataset(); + CPLAcquireMutex(hNCMutex, 1000.0); + + poDS->cdfid = cdfid; + poDS->z_id = z_id; + +/* -------------------------------------------------------------------- */ +/* Get dimensions. If we can't find this, then this is a */ +/* GMT file, but not a normal grid product. */ +/* -------------------------------------------------------------------- */ + size_t start[2], edge[2]; + int nm[2]; + + start[0] = 0; + edge[0] = 2; + + nc_get_vara_int(cdfid, nm_id, start, edge, nm); + + poDS->nRasterXSize = nm[0]; + poDS->nRasterYSize = nm[1]; + +/* -------------------------------------------------------------------- */ +/* Fetch "z" attributes scale_factor, add_offset, and */ +/* node_offset. */ +/* -------------------------------------------------------------------- */ + double scale_factor=1.0, add_offset=0.0; + int node_offset = 1; + + nc_get_att_double( cdfid, z_id, "scale_factor", &scale_factor ); + nc_get_att_double( cdfid, z_id, "add_offset", &add_offset ); + nc_get_att_int( cdfid, z_id, "node_offset", &node_offset ); + +/* -------------------------------------------------------------------- */ +/* Get x/y range information. */ +/* -------------------------------------------------------------------- */ + int x_range_id, y_range_id; + + if( nc_inq_varid (cdfid, "x_range", &x_range_id) == NC_NOERR + && nc_inq_varid (cdfid, "y_range", &y_range_id) == NC_NOERR ) + { + double x_range[2], y_range[2]; + + nc_get_vara_double( cdfid, x_range_id, start, edge, x_range ); + nc_get_vara_double( cdfid, y_range_id, start, edge, y_range ); + + // Pixel is area + if( node_offset == 1 ) + { + poDS->adfGeoTransform[0] = x_range[0]; + poDS->adfGeoTransform[1] = + (x_range[1] - x_range[0]) / poDS->nRasterXSize; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = y_range[1]; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = + (y_range[0] - y_range[1]) / poDS->nRasterYSize; + } + + // Pixel is point - offset by half pixel. + else /* node_offset == 0 */ + { + poDS->adfGeoTransform[1] = + (x_range[1] - x_range[0]) / (poDS->nRasterXSize-1); + poDS->adfGeoTransform[0] = + x_range[0] - poDS->adfGeoTransform[1]*0.5; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = + (y_range[0] - y_range[1]) / (poDS->nRasterYSize-1); + poDS->adfGeoTransform[3] = + y_range[1] - poDS->adfGeoTransform[5]*0.5; + } + } + else + { + poDS->adfGeoTransform[0] = 0.0; + poDS->adfGeoTransform[1] = 1.0; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = 1.0; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = 1; + poDS->SetBand( 1, new GMTRasterBand( poDS, z_id, 1 )); + + if( scale_factor != 1.0 || add_offset != 0.0 ) + { + poDS->GetRasterBand(1)->SetOffset( add_offset ); + poDS->GetRasterBand(1)->SetScale( scale_factor ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + CPLAcquireMutex(hNCMutex, 1000.0); + + return( poDS ); +} + +/************************************************************************/ +/* GMTCreateCopy() */ +/* */ +/* This code mostly cribbed from GMT's "gmt_cdf.c" module. */ +/************************************************************************/ + +static GDALDataset * +GMTCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, CPL_UNUSED char ** papszOptions, + CPL_UNUSED GDALProgressFunc pfnProgress, + CPL_UNUSED void * pProgressData ) +{ +/* -------------------------------------------------------------------- */ +/* Figure out general characteristics. */ +/* -------------------------------------------------------------------- */ + nc_type nc_datatype; + GDALRasterBand *poBand; + int nXSize, nYSize; + + CPLMutexHolderD(&hNCMutex); + + if( poSrcDS->GetRasterCount() != 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Currently GMT export only supports 1 band datasets." ); + return NULL; + } + + poBand = poSrcDS->GetRasterBand(1); + + nXSize = poSrcDS->GetRasterXSize(); + nYSize = poSrcDS->GetRasterYSize(); + + if( poBand->GetRasterDataType() == GDT_Int16 ) + nc_datatype = NC_SHORT; + else if( poBand->GetRasterDataType() == GDT_Int32 ) + nc_datatype = NC_INT; + else if( poBand->GetRasterDataType() == GDT_Float32 ) + nc_datatype = NC_FLOAT; + else if( poBand->GetRasterDataType() == GDT_Float64 ) + nc_datatype = NC_DOUBLE; + else if( bStrict ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Band data type %s not supported in GMT, giving up.", + GDALGetDataTypeName( poBand->GetRasterDataType() ) ); + return NULL; + } + else if( poBand->GetRasterDataType() == GDT_Byte ) + nc_datatype = NC_SHORT; + else if( poBand->GetRasterDataType() == GDT_UInt16 ) + nc_datatype = NC_INT; + else if( poBand->GetRasterDataType() == GDT_UInt32 ) + nc_datatype = NC_INT; + else + nc_datatype = NC_FLOAT; + +/* -------------------------------------------------------------------- */ +/* Establish bounds from geotransform. */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + double dfXMax, dfYMin; + + poSrcDS->GetGeoTransform( adfGeoTransform ); + + if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 ) + { + CPLError( bStrict ? CE_Failure : CE_Warning, CPLE_AppDefined, + "Geotransform has rotational coefficients not supported in GMT." ); + if( bStrict ) + return NULL; + } + + dfXMax = adfGeoTransform[0] + adfGeoTransform[1] * nXSize; + dfYMin = adfGeoTransform[3] + adfGeoTransform[5] * nYSize; + +/* -------------------------------------------------------------------- */ +/* Create base file. */ +/* -------------------------------------------------------------------- */ + int cdfid, err; + + err = nc_create (pszFilename, NC_CLOBBER,&cdfid); + if( err != NC_NOERR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "nc_create(%s): %s", + pszFilename, nc_strerror( err ) ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Define the dimensions and so forth. */ +/* -------------------------------------------------------------------- */ + int side_dim, xysize_dim, dims[1]; + int x_range_id, y_range_id, z_range_id, inc_id, nm_id, z_id; + + nc_def_dim(cdfid, "side", 2, &side_dim); + nc_def_dim(cdfid, "xysize", (int) (nXSize * nYSize), &xysize_dim); + + dims[0] = side_dim; + nc_def_var (cdfid, "x_range", NC_DOUBLE, 1, dims, &x_range_id); + nc_def_var (cdfid, "y_range", NC_DOUBLE, 1, dims, &y_range_id); + nc_def_var (cdfid, "z_range", NC_DOUBLE, 1, dims, &z_range_id); + nc_def_var (cdfid, "spacing", NC_DOUBLE, 1, dims, &inc_id); + nc_def_var (cdfid, "dimension", NC_LONG, 1, dims, &nm_id); + + dims[0] = xysize_dim; + nc_def_var (cdfid, "z", nc_datatype, 1, dims, &z_id); + +/* -------------------------------------------------------------------- */ +/* Assign attributes. */ +/* -------------------------------------------------------------------- */ + double default_scale = 1.0; + double default_offset = 0.0; + int default_node_offset = 1; // pixel is area + + nc_put_att_text (cdfid, x_range_id, "units", 7, "meters"); + nc_put_att_text (cdfid, y_range_id, "units", 7, "meters"); + nc_put_att_text (cdfid, z_range_id, "units", 7, "meters"); + + nc_put_att_double (cdfid, z_id, "scale_factor", NC_DOUBLE, 1, + &default_scale ); + nc_put_att_double (cdfid, z_id, "add_offset", NC_DOUBLE, 1, + &default_offset ); + + nc_put_att_int (cdfid, z_id, "node_offset", NC_LONG, 1, + &default_node_offset ); + nc_put_att_text (cdfid, NC_GLOBAL, "title", 1, ""); + nc_put_att_text (cdfid, NC_GLOBAL, "source", 1, ""); + + /* leave define mode */ + nc_enddef (cdfid); + +/* -------------------------------------------------------------------- */ +/* Get raster min/max. */ +/* -------------------------------------------------------------------- */ + double adfMinMax[2]; + GDALComputeRasterMinMax( (GDALRasterBandH) poBand, FALSE, adfMinMax ); + +/* -------------------------------------------------------------------- */ +/* Set range variables. */ +/* -------------------------------------------------------------------- */ + size_t start[2], edge[2]; + double dummy[2]; + int nm[2]; + + start[0] = 0; + edge[0] = 2; + dummy[0] = adfGeoTransform[0]; + dummy[1] = dfXMax; + nc_put_vara_double(cdfid, x_range_id, start, edge, dummy); + + dummy[0] = dfYMin; + dummy[1] = adfGeoTransform[3]; + nc_put_vara_double(cdfid, y_range_id, start, edge, dummy); + + dummy[0] = adfGeoTransform[1]; + dummy[1] = -adfGeoTransform[5]; + nc_put_vara_double(cdfid, inc_id, start, edge, dummy); + + nm[0] = nXSize; + nm[1] = nYSize; + nc_put_vara_int(cdfid, nm_id, start, edge, nm); + + nc_put_vara_double(cdfid, z_range_id, start, edge, adfMinMax); + +/* -------------------------------------------------------------------- */ +/* Write out the image one scanline at a time. */ +/* -------------------------------------------------------------------- */ + double *padfData; + int iLine; + + padfData = (double *) CPLMalloc( sizeof(double) * nXSize ); + + edge[0] = nXSize; + for( iLine = 0; iLine < nYSize; iLine++ ) + { + start[0] = iLine * nXSize; + poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + padfData, nXSize, 1, GDT_Float64, 0, 0, NULL ); + err = nc_put_vara_double( cdfid, z_id, start, edge, padfData ); + if( err != NC_NOERR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "nc_put_vara_double(%s): %s", + pszFilename, nc_strerror( err ) ); + nc_close (cdfid); + return( NULL ); + } + } + + CPLFree( padfData ); + +/* -------------------------------------------------------------------- */ +/* Close file, and reopen. */ +/* -------------------------------------------------------------------- */ + nc_close (cdfid); + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + GDALPamDataset *poDS = (GDALPamDataset *) + GDALOpen( pszFilename, GA_ReadOnly ); + + if( poDS ) + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_GMT() */ +/************************************************************************/ + +void GDALRegister_GMT() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("GMT driver")) + return; + + if( GDALGetDriverByName( "GMT" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GMT" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GMT NetCDF Grid Format" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#GMT" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "nc" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Int16 Int32 Float32 Float64" ); + + poDriver->pfnOpen = GMTDataset::Open; + poDriver->pfnCreateCopy = GMTCreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/netcdf/makefile.vc b/bazaar/plugin/gdal/frmts/netcdf/makefile.vc new file mode 100644 index 000000000..cd3ccd701 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/netcdf/makefile.vc @@ -0,0 +1,48 @@ + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +OBJ = netcdfdataset.obj gmtdataset.obj + +PLUGIN_DLL = gdal_netCDF.dll + +EXTRAFLAGS = /I$(NETCDF_INC_DIR) + +!IF "$(NETCDF_PLUGIN)" == "YES" +EXTRAFLAGS = $(EXTRAFLAGS) -DNETCDF_PLUGIN +!ENDIF + +!IFDEF NETCDF_HAS_NC4 +EXTRAFLAGS = $(EXTRAFLAGS) -DNETCDF_HAS_NC4 +!ENDIF +!IFDEF NETCDF_HAS_HDF4 +EXTRAFLAGS = $(EXTRAFLAGS) -DNETCDF_HAS_HDF4 +!ENDIF +!IFDEF HDF4_DIR +EXTRAFLAGS = $(EXTRAFLAGS) -DHAVE_HDF4 +!ENDIF +!IFDEF HDF5_DIR +EXTRAFLAGS = $(EXTRAFLAGS) -DHAVE_HDF5 +!ENDIF + +default: $(OBJ) + $(INSTALL) *.obj ..\o + +clean: + -del *.obj + -del *.dll + -del *.exp + -del *.lib + -del *.manifest + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(GDALLIB) $(NETCDF_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) + diff --git a/bazaar/plugin/gdal/frmts/netcdf/netcdfdataset.cpp b/bazaar/plugin/gdal/frmts/netcdf/netcdfdataset.cpp new file mode 100644 index 000000000..faf51df31 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/netcdf/netcdfdataset.cpp @@ -0,0 +1,7084 @@ +/****************************************************************************** + * $Id: netcdfdataset.cpp 29200 2015-05-15 18:04:02Z rouault $ + * + * Project: netCDF read/write Driver + * Purpose: GDAL bindings over netCDF library. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * Copyright (c) 2010, Kyle Shannon + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "netcdfdataset.h" +#include "cpl_error.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: netcdfdataset.cpp 29200 2015-05-15 18:04:02Z rouault $"); + +#include //for NCDFWriteProjAttribs() + +/* Internal function declarations */ + +int NCDFIsGDALVersionGTE(const char* pszVersion, int nTarget); + +void NCDFAddGDALHistory( int fpImage, + const char * pszFilename, const char *pszOldHist, + const char * pszFunctionName ); + +void NCDFAddHistory(int fpImage, const char *pszAddHist, const char *pszOldHist); + +int NCDFIsCfProjection( const char* pszProjection ); + +void NCDFWriteProjAttribs(const OGR_SRSNode *poPROJCS, + const char* pszProjection, + const int fpImage, const int NCDFVarID); + +CPLErr NCDFSafeStrcat(char** ppszDest, char* pszSrc, size_t* nDestSize); +CPLErr NCDFSafeStrcpy(char** ppszDest, char* pszSrc, size_t* nDestSize); + +/* var / attribute helper functions */ +CPLErr NCDFGetAttr( int nCdfId, int nVarId, const char *pszAttrName, + double *pdfValue ); +CPLErr NCDFGetAttr( int nCdfId, int nVarId, const char *pszAttrName, + char **pszValue ); +CPLErr NCDFPutAttr( int nCdfId, int nVarId, + const char *pszAttrName, const char *pszValue ); +CPLErr NCDFGet1DVar( int nCdfId, int nVarId, char **pszValue );//replace this where used +CPLErr NCDFPut1DVar( int nCdfId, int nVarId, const char *pszValue ); + +double NCDFGetDefaultNoDataValue( int nVarType ); + +/* dimension check functions */ +int NCDFIsVarLongitude(int nCdfId, int nVarId=-1, const char * nVarName=NULL ); +int NCDFIsVarLatitude(int nCdfId, int nVarId=-1, const char * nVarName=NULL ); +int NCDFIsVarProjectionX( int nCdfId, int nVarId=-1, const char * pszVarName=NULL ); +int NCDFIsVarProjectionY( int nCdfId, int nVarId=-1, const char * pszVarName=NULL ); +int NCDFIsVarVerticalCoord(int nCdfId, int nVarId=-1, const char * nVarName=NULL ); +int NCDFIsVarTimeCoord(int nCdfId, int nVarId=-1, const char * nVarName=NULL ); + +char **NCDFTokenizeArray( const char *pszValue );//replace this where used +void CopyMetadata( void *poDS, int fpImage, int CDFVarID, + const char *pszMatchPrefix=NULL, int bIsBand=TRUE ); + +// uncomment this for more debug ouput +// #define NCDF_DEBUG 1 + +CPLMutex *hNCMutex = NULL; + +/************************************************************************/ +/* ==================================================================== */ +/* netCDFRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class netCDFRasterBand : public GDALPamRasterBand +{ + friend class netCDFDataset; + + nc_type nc_datatype; + int cdfid; + int nZId; + int nZDim; + int nLevel; + int nBandXPos; + int nBandYPos; + int *panBandZPos; + int *panBandZLev; + int bNoDataSet; + double dfNoDataValue; + double adfValidRange[2]; + double dfScale; + double dfOffset; + int bSignedData; + int status; + int bCheckLongitude; + + CPLErr CreateBandMetadata( int *paDimIds ); + template void CheckData ( void * pImage, + int nTmpBlockXSize, int nTmpBlockYSize, + int bCheckIsNan=FALSE ) ; + + protected: + + CPLXMLNode *SerializeToXML( const char *pszVRTPath ); + + public: + + netCDFRasterBand( netCDFDataset *poDS, + int nZId, + int nZDim, + int nLevel, + int *panBandZLen, + int *panBandPos, + int *paDimIds, + int nBand ); + netCDFRasterBand( netCDFDataset *poDS, + GDALDataType eType, + int nBand, + int bSigned=TRUE, + char *pszBandName=NULL, + char *pszLongName=NULL, + int nZId=-1, + int nZDim=2, + int nLevel=0, + int *panBandZLev=NULL, + int *panBandZPos=NULL, + int *paDimIds=NULL ); + ~netCDFRasterBand( ); + + virtual double GetNoDataValue( int * ); + virtual CPLErr SetNoDataValue( double ); + virtual double GetOffset( int * ); + virtual CPLErr SetOffset( double ); + virtual double GetScale( int * ); + virtual CPLErr SetScale( double ); + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + +}; + +/************************************************************************/ +/* netCDFRasterBand() */ +/************************************************************************/ + +netCDFRasterBand::netCDFRasterBand( netCDFDataset *poNCDFDS, + int nZId, + int nZDim, + int nLevel, + int *panBandZLev, + int *panBandZPos, + int *paDimIds, + int nBand ) + +{ + double dfNoData = 0.0; + int bGotNoData = FALSE; + nc_type vartype=NC_NAT; + nc_type atttype=NC_NAT; + size_t attlen; + char szNoValueName[NCDF_MAX_STR_LEN]; + + this->poDS = poNCDFDS; + this->panBandZPos = NULL; + this->panBandZLev = NULL; + this->nBand = nBand; + this->nZId = nZId; + this->nZDim = nZDim; + this->nLevel = nLevel; + this->nBandXPos = panBandZPos[0]; + this->nBandYPos = panBandZPos[1]; + this->bSignedData = TRUE; //default signed, except for Byte + this->cdfid = poNCDFDS->GetCDFID(); + this->status = NC_NOERR; + this->bCheckLongitude = FALSE; + +/* -------------------------------------------------------------------- */ +/* Take care of all other dimmensions */ +/* ------------------------------------------------------------------ */ + if( nZDim > 2 ) { + this->panBandZPos = + (int *) CPLCalloc( nZDim-1, sizeof( int ) ); + this->panBandZLev = + (int *) CPLCalloc( nZDim-1, sizeof( int ) ); + + for ( int i=0; i < nZDim - 2; i++ ){ + this->panBandZPos[i] = panBandZPos[i+2]; + this->panBandZLev[i] = panBandZLev[i]; + } + } + + this->dfNoDataValue = 0.0; + this->bNoDataSet = FALSE; + + nRasterXSize = poDS->GetRasterXSize( ); + nRasterYSize = poDS->GetRasterYSize( ); + nBlockXSize = poDS->GetRasterXSize( ); + nBlockYSize = 1; + +/* -------------------------------------------------------------------- */ +/* Get the type of the "z" variable, our target raster array. */ +/* -------------------------------------------------------------------- */ + if( nc_inq_var( cdfid, nZId, NULL, &nc_datatype, NULL, NULL, + NULL ) != NC_NOERR ){ + CPLError( CE_Failure, CPLE_AppDefined, + "Error in nc_var_inq() on 'z'." ); + return; + } + + if( nc_datatype == NC_BYTE ) + eDataType = GDT_Byte; +#ifdef NETCDF_HAS_NC4 + /* NC_UBYTE (unsigned byte) is only available for NC4 */ + else if( nc_datatype == NC_UBYTE ) + eDataType = GDT_Byte; +#endif + else if( nc_datatype == NC_CHAR ) + eDataType = GDT_Byte; + else if( nc_datatype == NC_SHORT ) + eDataType = GDT_Int16; + else if( nc_datatype == NC_INT ) + eDataType = GDT_Int32; + else if( nc_datatype == NC_FLOAT ) + eDataType = GDT_Float32; + else if( nc_datatype == NC_DOUBLE ) + eDataType = GDT_Float64; + else + { + if( nBand == 1 ) + CPLError( CE_Warning, CPLE_AppDefined, + "Unsupported netCDF datatype (%d), treat as Float32.", + (int) nc_datatype ); + eDataType = GDT_Float32; + } + +/* -------------------------------------------------------------------- */ +/* Find and set No Data for this variable */ +/* -------------------------------------------------------------------- */ + + /* find attribute name, either _FillValue or missing_value */ + status = nc_inq_att( cdfid, nZId, + _FillValue, &atttype, &attlen); + if( status == NC_NOERR ) { + strcpy(szNoValueName, _FillValue ); + } + else { + status = nc_inq_att( cdfid, nZId, + "missing_value", &atttype, &attlen ); + if( status == NC_NOERR ) { + strcpy( szNoValueName, "missing_value" ); + } + } + + /* fetch missing value */ + if( status == NC_NOERR ) { + if ( NCDFGetAttr( cdfid, nZId, szNoValueName, + &dfNoData ) == CE_None ) + bGotNoData = TRUE; + } + + /* if NoData was not found, use the default value */ + if ( ! bGotNoData ) { + nc_inq_vartype( cdfid, nZId, &vartype ); + dfNoData = NCDFGetDefaultNoDataValue( vartype ); + bGotNoData = TRUE; + CPLDebug( "GDAL_netCDF", + "did not get nodata value for variable #%d, using default %f", + nZId, dfNoData ); + } + + /* set value */ +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "SetNoDataValue(%f) read", dfNoData ); +#endif + SetNoDataValue( dfNoData ); + +/* -------------------------------------------------------------------- */ +/* Look for valid_range or valid_min/valid_max */ +/* -------------------------------------------------------------------- */ + /* set valid_range to nodata, then check for actual values */ + adfValidRange[0] = dfNoData; + adfValidRange[1] = dfNoData; + /* first look for valid_range */ + int bGotValidRange = FALSE; + status = nc_inq_att( cdfid, nZId, + "valid_range", &atttype, &attlen); + if( (status == NC_NOERR) && (attlen == 2)) { + int vrange[2]; + int vmin, vmax; + status = nc_get_att_int( cdfid, nZId, + "valid_range", vrange ); + if( status == NC_NOERR ) { + bGotValidRange = TRUE; + adfValidRange[0] = vrange[0]; + adfValidRange[1] = vrange[1]; + } + /* if not found look for valid_min and valid_max */ + else { + status = nc_get_att_int( cdfid, nZId, + "valid_min", &vmin ); + if( status == NC_NOERR ) { + adfValidRange[0] = vmin; + status = nc_get_att_int( cdfid, nZId, + "valid_max", &vmax ); + if( status == NC_NOERR ) { + adfValidRange[1] = vmax; + bGotValidRange = TRUE; + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Special For Byte Bands: check for signed/unsigned byte */ +/* -------------------------------------------------------------------- */ + if ( nc_datatype == NC_BYTE ) { + + /* netcdf uses signed byte by default, but GDAL uses unsigned by default */ + /* This may cause unexpected results, but is needed for back-compat */ + if ( poNCDFDS->bIsGdalFile ) + this->bSignedData = FALSE; + else + this->bSignedData = TRUE; + + /* For NC4 format NC_BYTE is signed, NC_UBYTE is unsigned */ + if ( poNCDFDS->nFormat == NCDF_FORMAT_NC4 ) { + this->bSignedData = TRUE; + } + else { + /* if we got valid_range, test for signed/unsigned range */ + /* http://www.unidata.ucar.edu/software/netcdf/docs/netcdf/Attribute-Conventions.html */ + if ( bGotValidRange == TRUE ) { + /* If we got valid_range={0,255}, treat as unsigned */ + if ( (adfValidRange[0] == 0) && (adfValidRange[1] == 255) ) { + bSignedData = FALSE; + /* reset valid_range */ + adfValidRange[0] = dfNoData; + adfValidRange[1] = dfNoData; + } + /* If we got valid_range={-128,127}, treat as signed */ + else if ( (adfValidRange[0] == -128) && (adfValidRange[1] == 127) ) { + bSignedData = TRUE; + /* reset valid_range */ + adfValidRange[0] = dfNoData; + adfValidRange[1] = dfNoData; + } + } + /* else test for _Unsigned */ + /* http://www.unidata.ucar.edu/software/netcdf/docs/BestPractices.html */ + else { + char *pszTemp = NULL; + if ( NCDFGetAttr( cdfid, nZId, "_Unsigned", &pszTemp ) + + == CE_None ) { + if ( EQUAL(pszTemp,"true")) + bSignedData = FALSE; + else if ( EQUAL(pszTemp,"false")) + bSignedData = TRUE; + CPLFree( pszTemp ); + } + } + } + + if ( bSignedData ) + { + /* set PIXELTYPE=SIGNEDBYTE */ + /* See http://trac.osgeo.org/gdal/wiki/rfc14_imagestructure */ + SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" ); + } + + } + +#ifdef NETCDF_HAS_NC4 + if ( nc_datatype == NC_UBYTE ) + this->bSignedData = FALSE; +#endif + + CPLDebug( "GDAL_netCDF", "netcdf type=%d gdal type=%d signedByte=%d", + nc_datatype, eDataType, bSignedData ); + +/* -------------------------------------------------------------------- */ +/* Create Band Metadata */ +/* -------------------------------------------------------------------- */ + CreateBandMetadata( paDimIds ); + +/* -------------------------------------------------------------------- */ +/* Attempt to fetch the scale_factor and add_offset attributes for the */ +/* variable and set them. If these values are not available, set */ +/* offset to 0 and scale to 1 */ +/* -------------------------------------------------------------------- */ + double dfOff = 0.0; + double dfScale = 1.0; + + if ( nc_inq_attid ( cdfid, nZId, CF_ADD_OFFSET, NULL) == NC_NOERR ) { + status = nc_get_att_double( cdfid, nZId, CF_ADD_OFFSET, &dfOff ); + CPLDebug( "GDAL_netCDF", "got add_offset=%.16g, status=%d", dfOff, status ); + } + if ( nc_inq_attid ( cdfid, nZId, + CF_SCALE_FACTOR, NULL) == NC_NOERR ) { + status = nc_get_att_double( cdfid, nZId, CF_SCALE_FACTOR, &dfScale ); + CPLDebug( "GDAL_netCDF", "got scale_factor=%.16g, status=%d", dfScale, status ); + } + SetOffset( dfOff ); + SetScale( dfScale ); + + /* should we check for longitude values > 360 ? */ + this->bCheckLongitude = + CSLTestBoolean(CPLGetConfigOption("GDAL_NETCDF_CENTERLONG_180", "YES")) + && NCDFIsVarLongitude( cdfid, nZId, NULL ); + +/* -------------------------------------------------------------------- */ +/* Check for variable chunking (netcdf-4 only) */ +/* GDAL block size should be set to hdf5 chunk size */ +/* -------------------------------------------------------------------- */ +#ifdef NETCDF_HAS_NC4 + int nTmpFormat = 0; + size_t chunksize[ MAX_NC_DIMS ]; + status = nc_inq_format( cdfid, &nTmpFormat); + if( ( status == NC_NOERR ) && ( ( nTmpFormat == NCDF_FORMAT_NC4 ) || + ( nTmpFormat == NCDF_FORMAT_NC4C ) ) ) { + /* check for chunksize and set it as the blocksize (optimizes read) */ + status = nc_inq_var_chunking( cdfid, nZId, &nTmpFormat, chunksize ); + if( ( status == NC_NOERR ) && ( nTmpFormat == NC_CHUNKED ) ) { + CPLDebug( "GDAL_netCDF", + "setting block size to chunk size : %ld x %ld\n", + chunksize[nZDim-1], chunksize[nZDim-2]); + nBlockXSize = (int) chunksize[nZDim-1]; + nBlockYSize = (int) chunksize[nZDim-2]; + } + } +#endif + +/* -------------------------------------------------------------------- */ +/* Force block size to 1 scanline for bottom-up datasets if */ +/* nBlockYSize != 1 */ +/* -------------------------------------------------------------------- */ + if( poNCDFDS->bBottomUp && nBlockYSize != 1 ) { + nBlockXSize = nRasterXSize; + nBlockYSize = 1; + } +} + +/* constructor in create mode */ +/* if nZId and following variables are not passed, the band will have 2 dimensions */ +/* TODO get metadata, missing val from band #1 if nZDim>2 */ +netCDFRasterBand::netCDFRasterBand( netCDFDataset *poNCDFDS, + GDALDataType eType, + int nBand, + int bSigned, + char *pszBandName, + char *pszLongName, + int nZId, + int nZDim, + int nLevel, + int *panBandZLev, + int *panBandZPos, + int *paDimIds ) +{ + int status; + double dfNoData = 0.0; + char szTemp[NCDF_MAX_STR_LEN]; + int bDefineVar = FALSE; + + this->poDS = poNCDFDS; + this->nBand = nBand; + this->nZId = nZId; + this->nZDim = nZDim; + this->nLevel = nLevel; + this->panBandZPos = NULL; + this->panBandZLev = NULL; + this->nBandXPos = 1; + this->nBandYPos = 0; + this->bSignedData = bSigned; + + this->status = NC_NOERR; + this->cdfid = poNCDFDS->GetCDFID(); + this->bCheckLongitude = FALSE; + + this->bNoDataSet = FALSE; + this->dfNoDataValue = 0.0; + + nRasterXSize = poDS->GetRasterXSize( ); + nRasterYSize = poDS->GetRasterYSize( ); + nBlockXSize = poDS->GetRasterXSize( ); + nBlockYSize = 1; + + if ( poDS->GetAccess() != GA_Update ) { + CPLError( CE_Failure, CPLE_NotSupported, + "Dataset is not in update mode, wrong netCDFRasterBand constructor" ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Take care of all other dimmensions */ +/* ------------------------------------------------------------------ */ + if ( nZDim > 2 && paDimIds != NULL ) { + + this->nBandXPos = panBandZPos[0]; + this->nBandYPos = panBandZPos[1]; + + this->panBandZPos = + (int *) CPLCalloc( nZDim-1, sizeof( int ) ); + this->panBandZLev = + (int *) CPLCalloc( nZDim-1, sizeof( int ) ); + + for ( int i=0; i < nZDim - 2; i++ ){ + this->panBandZPos[i] = panBandZPos[i+2]; + this->panBandZLev[i] = panBandZLev[i]; + } + } + +/* -------------------------------------------------------------------- */ +/* Get the type of the "z" variable, our target raster array. */ +/* -------------------------------------------------------------------- */ + eDataType = eType; + + switch ( eDataType ) + { + case GDT_Byte: + nc_datatype = NC_BYTE; +#ifdef NETCDF_HAS_NC4 + /* NC_UBYTE (unsigned byte) is only available for NC4 */ + if ( ! bSignedData && (poNCDFDS->nFormat == NCDF_FORMAT_NC4) ) + nc_datatype = NC_UBYTE; +#endif + break; + case GDT_Int16: + nc_datatype = NC_SHORT; + break; + case GDT_Int32: + nc_datatype = NC_INT; + break; + case GDT_Float32: + nc_datatype = NC_FLOAT; + break; + case GDT_Float64: + nc_datatype = NC_DOUBLE; + break; + default: + if( nBand == 1 ) + CPLError( CE_Warning, CPLE_AppDefined, + "Unsupported GDAL datatype (%d), treat as NC_FLOAT.", + (int) eDataType ); + nc_datatype = NC_FLOAT; + break; + } + +/* -------------------------------------------------------------------- */ +/* Define the variable if necessary (if nZId==-1) */ +/* -------------------------------------------------------------------- */ + if ( nZId == -1 ) { + + bDefineVar = TRUE; + + /* make sure we are in define mode */ + ( ( netCDFDataset * ) poDS )->SetDefineMode( TRUE ); + + if ( !pszBandName || EQUAL(pszBandName,"") ) + sprintf( szTemp, "Band%d", nBand ); + else + strcpy( szTemp, pszBandName ); + + if ( nZDim > 2 && paDimIds != NULL ) { + status = nc_def_var( cdfid, szTemp, nc_datatype, + nZDim, paDimIds, &nZId ); + } + else { + int anBandDims[ 2 ]; + anBandDims[0] = poNCDFDS->nYDimID; + anBandDims[1] = poNCDFDS->nXDimID; + status = nc_def_var( cdfid, szTemp, nc_datatype, + 2, anBandDims, &nZId ); + } + NCDF_ERR(status); + CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d) id=%d", + cdfid, szTemp, nc_datatype, nZId ); + this->nZId = nZId; + + if ( !pszLongName || EQUAL(pszLongName,"") ) + sprintf( szTemp, "GDAL Band Number %d", nBand ); + else + strcpy( szTemp, pszLongName ); + status = nc_put_att_text( cdfid, nZId, CF_LNG_NAME, + strlen( szTemp ), szTemp ); + NCDF_ERR(status); + + poNCDFDS->DefVarDeflate(nZId, TRUE); + } + + /* for Byte data add signed/unsigned info */ + if ( eDataType == GDT_Byte ) { + + if ( bDefineVar ) { //only add attributes if creating variable + CPLDebug( "GDAL_netCDF", "adding valid_range attributes for Byte Band" ); + /* For unsigned NC_BYTE (except NC4 format) */ + /* add valid_range and _Unsigned ( defined in CF-1 and NUG ) */ + if ( (nc_datatype == NC_BYTE) && (poNCDFDS->nFormat != NCDF_FORMAT_NC4) ) { + short int adfValidRange[2]; + if ( bSignedData ) { + adfValidRange[0] = -128; + adfValidRange[1] = 127; + status = nc_put_att_text( cdfid,nZId, + "_Unsigned", 5, "false" ); + } + else { + adfValidRange[0] = 0; + adfValidRange[1] = 255; + status = nc_put_att_text( cdfid,nZId, + "_Unsigned", 4, "true" ); + } + status=nc_put_att_short( cdfid,nZId, "valid_range", + NC_SHORT, 2, adfValidRange ); + } + } + /* for unsigned byte set PIXELTYPE=SIGNEDBYTE */ + /* See http://trac.osgeo.org/gdal/wiki/rfc14_imagestructure */ + if ( bSignedData ) + SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" ); + + } + + /* set default nodata */ +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "SetNoDataValue(%f) default", dfNoData ); +#endif + dfNoData = NCDFGetDefaultNoDataValue( nc_datatype ); + SetNoDataValue( dfNoData ); +} + +/************************************************************************/ +/* ~netCDFRasterBand() */ +/************************************************************************/ + +netCDFRasterBand::~netCDFRasterBand() +{ + FlushCache(); + if( panBandZPos ) + CPLFree( panBandZPos ); + if( panBandZLev ) + CPLFree( panBandZLev ); +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ +double netCDFRasterBand::GetOffset( int *pbSuccess ) +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return dfOffset; +} + +/************************************************************************/ +/* SetOffset() */ +/************************************************************************/ +CPLErr netCDFRasterBand::SetOffset( double dfNewOffset ) +{ + CPLMutexHolderD(&hNCMutex); + + dfOffset = dfNewOffset; + + /* write value if in update mode */ + if ( poDS->GetAccess() == GA_Update ) { + + /* make sure we are in define mode */ + ( ( netCDFDataset * ) poDS )->SetDefineMode( TRUE ); + + status = nc_put_att_double( cdfid, nZId, CF_ADD_OFFSET, + NC_DOUBLE, 1, &dfOffset ); + + NCDF_ERR(status); + if ( status == NC_NOERR ) + return CE_None; + else + return CE_Failure; + + } + + return CE_None; +} + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ +double netCDFRasterBand::GetScale( int *pbSuccess ) +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + return dfScale; +} + +/************************************************************************/ +/* SetScale() */ +/************************************************************************/ +CPLErr netCDFRasterBand::SetScale( double dfNewScale ) +{ + CPLMutexHolderD(&hNCMutex); + + dfScale = dfNewScale; + + /* write value if in update mode */ + if ( poDS->GetAccess() == GA_Update ) { + + /* make sure we are in define mode */ + ( ( netCDFDataset * ) poDS )->SetDefineMode( TRUE ); + + status = nc_put_att_double( cdfid, nZId, CF_SCALE_FACTOR, + NC_DOUBLE, 1, &dfScale ); + + NCDF_ERR(status); + if ( status == NC_NOERR ) + return CE_None; + else + return CE_Failure; + + } + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double netCDFRasterBand::GetNoDataValue( int * pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bNoDataSet; + + if( bNoDataSet ) + return dfNoDataValue; + else + return GDALPamRasterBand::GetNoDataValue( pbSuccess ); +} + + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr netCDFRasterBand::SetNoDataValue( double dfNoData ) + +{ + CPLMutexHolderD(&hNCMutex); + + /* If already set to new value, don't do anything */ + if ( bNoDataSet && CPLIsEqual( dfNoData, dfNoDataValue ) ) + return CE_None; + + /* write value if in update mode */ + if ( poDS->GetAccess() == GA_Update ) { + + /* netcdf-4 does not allow to set _FillValue after leaving define mode */ + /* but it's ok if variable has not been written to, so only print debug */ + /* see bug #4484 */ + if ( bNoDataSet && + ( ((netCDFDataset *)poDS)->GetDefineMode() == FALSE ) ) { + CPLDebug( "GDAL_netCDF", + "Setting NoDataValue to %.18g (previously set to %.18g) " + "but file is no longer in define mode (id #%d, band #%d)", + dfNoData, dfNoDataValue, cdfid, nBand ); + } +#ifdef NCDF_DEBUG + else { + CPLDebug( "GDAL_netCDF", "Setting NoDataValue to %.18g (id #%d, band #%d)", + dfNoData, cdfid, nBand ); + } +#endif + /* make sure we are in define mode */ + ( ( netCDFDataset * ) poDS )->SetDefineMode( TRUE ); + + if ( eDataType == GDT_Byte) { + if ( bSignedData ) { + signed char cNoDataValue = (signed char) dfNoData; + status = nc_put_att_schar( cdfid, nZId, _FillValue, + nc_datatype, 1, &cNoDataValue ); + } + else { + unsigned char ucNoDataValue = (unsigned char) dfNoData; + status = nc_put_att_uchar( cdfid, nZId, _FillValue, + nc_datatype, 1, &ucNoDataValue ); + } + } + else if ( eDataType == GDT_Int16 ) { + short int nsNoDataValue = (short int) dfNoData; + status = nc_put_att_short( cdfid, nZId, _FillValue, + nc_datatype, 1, &nsNoDataValue ); + } + else if ( eDataType == GDT_Int32) { + int nNoDataValue = (int) dfNoData; + status = nc_put_att_int( cdfid, nZId, _FillValue, + nc_datatype, 1, &nNoDataValue ); + } + else if ( eDataType == GDT_Float32) { + float fNoDataValue = (float) dfNoData; + status = nc_put_att_float( cdfid, nZId, _FillValue, + nc_datatype, 1, &fNoDataValue ); + } + else + status = nc_put_att_double( cdfid, nZId, _FillValue, + nc_datatype, 1, &dfNoData ); + + NCDF_ERR(status); + + /* update status if write worked */ + if ( status == NC_NOERR ) { + dfNoDataValue = dfNoData; + bNoDataSet = TRUE; + return CE_None; + } + else + return CE_Failure; + + } + + dfNoDataValue = dfNoData; + bNoDataSet = TRUE; + return CE_None; +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *netCDFRasterBand::SerializeToXML( CPL_UNUSED const char *pszUnused ) +{ +/* -------------------------------------------------------------------- */ +/* Overriden from GDALPamDataset to add only band histogram */ +/* and statistics. See bug #4244. */ +/* -------------------------------------------------------------------- */ + + if( psPam == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Setup root node and attributes. */ +/* -------------------------------------------------------------------- */ + CPLString oFmt; + + CPLXMLNode *psTree; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMRasterBand" ); + + if( GetBand() > 0 ) + CPLSetXMLValue( psTree, "#band", oFmt.Printf( "%d", GetBand() ) ); + +/* -------------------------------------------------------------------- */ +/* Histograms. */ +/* -------------------------------------------------------------------- */ + if( psPam->psSavedHistograms != NULL ) + CPLAddXMLChild( psTree, CPLCloneXMLTree( psPam->psSavedHistograms ) ); + +/* -------------------------------------------------------------------- */ +/* Metadata (statistics only). */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psMD; + + GDALMultiDomainMetadata oMDMDStats; + const char* papszMDStats[] = { "STATISTICS_MINIMUM", "STATISTICS_MAXIMUM", + "STATISTICS_MEAN", "STATISTICS_STDDEV", + NULL }; + for ( int i=0; ipsChild == NULL ) + CPLDestroyXMLNode( psMD ); + else + CPLAddXMLChild( psTree, psMD ); + } + +/* -------------------------------------------------------------------- */ +/* We don't want to return anything if we had no metadata to */ +/* attach. */ +/* -------------------------------------------------------------------- */ + if( psTree->psChild == NULL || psTree->psChild->psNext == NULL ) + { + CPLDestroyXMLNode( psTree ); + psTree = NULL; + } + + return psTree; +} + +/************************************************************************/ +/* CreateBandMetadata() */ +/************************************************************************/ + +CPLErr netCDFRasterBand::CreateBandMetadata( int *paDimIds ) + +{ + char szVarName[NC_MAX_NAME]; + char szMetaName[NC_MAX_NAME]; + char szMetaTemp[NCDF_MAX_STR_LEN]; + char *pszMetaValue = NULL; + char szTemp[NC_MAX_NAME]; + + int nd; + int i,j; + int Sum = 1; + int Taken = 0; + int result = 0; + int status; + int nVarID = -1; + int nDims; + size_t start[1]; + size_t count[1]; + nc_type nVarType = NC_NAT; + int nAtt=0; + + netCDFDataset *poDS = (netCDFDataset *) this->poDS; + +/* -------------------------------------------------------------------- */ +/* Compute all dimensions from Band number and save in Metadata */ +/* -------------------------------------------------------------------- */ + nc_inq_varname( cdfid, nZId, szVarName ); + nc_inq_varndims( cdfid, nZId, &nd ); +/* -------------------------------------------------------------------- */ +/* Compute multidimention band position */ +/* */ +/* BandPosition = (Total - sum(PastBandLevels) - 1)/sum(remainingLevels)*/ +/* if Data[2,3,4,x,y] */ +/* */ +/* BandPos0 = (nBand ) / (3*4) */ +/* BandPos1 = (nBand - BandPos0*(3*4) ) / (4) */ +/* BandPos2 = (nBand - BandPos0*(3*4) ) % (4) */ +/* -------------------------------------------------------------------- */ + + sprintf( szMetaName,"NETCDF_VARNAME"); + sprintf( szMetaTemp,"%s",szVarName); + SetMetadataItem( szMetaName, szMetaTemp ); + if( nd == 3 ) { + Sum *= panBandZLev[0]; + } + +/* -------------------------------------------------------------------- */ +/* Loop over non-spatial dimensions */ +/* -------------------------------------------------------------------- */ + for( i=0; i < nd-2 ; i++ ) { + + if( i != nd - 2 -1 ) { + Sum = 1; + for( j=i+1; j < nd-2; j++ ) { + Sum *= panBandZLev[j]; + } + result = (int) ( ( nLevel-Taken) / Sum ); + } + else { + result = (int) ( ( nLevel-Taken) % Sum ); + } + + strcpy(szVarName, + poDS->papszDimName[paDimIds[panBandZPos[i]]] ); + + status=nc_inq_varid( cdfid, szVarName, &nVarID ); + if( status != NC_NOERR ) { + /* Try to uppercase the first letter of the variable */ + /* Note: why is this needed? leaving for safety */ + szVarName[0]=(char) toupper(szVarName[0]); + status=nc_inq_varid( cdfid, szVarName, &nVarID ); + } + + status = nc_inq_vartype( cdfid, nVarID, &nVarType ); + + nDims = 0; + status = nc_inq_varndims( cdfid, nVarID, &nDims ); + + if( nDims == 1 ) { + count[0]=1; + start[0]=result; + switch( nVarType ) { + case NC_SHORT: + short sData; + status = nc_get_vara_short( cdfid, nVarID, + start, + count, &sData ); + sprintf( szMetaTemp,"%d", sData ); + break; + case NC_INT: + int nData; + status = nc_get_vara_int( cdfid, nVarID, + start, + count, &nData ); + sprintf( szMetaTemp,"%d", nData ); + break; + case NC_FLOAT: + float fData; + status = nc_get_vara_float( cdfid, nVarID, + start, + count, &fData ); + CPLsprintf( szMetaTemp,"%.8g", fData ); + break; + case NC_DOUBLE: + double dfData; + status = nc_get_vara_double( cdfid, nVarID, + start, + count, &dfData); + CPLsprintf( szMetaTemp,"%.16g", dfData ); + break; + default: + CPLDebug( "GDAL_netCDF", "invalid dim %s, type=%d", + szMetaTemp, nVarType); + break; + } + } + else + sprintf( szMetaTemp,"%d", result+1); + +/* -------------------------------------------------------------------- */ +/* Save dimension value */ +/* -------------------------------------------------------------------- */ + /* NOTE: removed #original_units as not part of CF-1 */ + sprintf( szMetaName,"NETCDF_DIM_%s", szVarName ); + SetMetadataItem( szMetaName, szMetaTemp ); + + Taken += result * Sum; + + } // end loop non-spatial dimensions + +/* -------------------------------------------------------------------- */ +/* Get all other metadata */ +/* -------------------------------------------------------------------- */ + nc_inq_varnatts( cdfid, nZId, &nAtt ); + + for( i=0; i < nAtt ; i++ ) { + + status = nc_inq_attname( cdfid, nZId, i, szTemp); + // if(strcmp(szTemp,_FillValue) ==0) continue; + sprintf( szMetaName,"%s",szTemp); + + if ( NCDFGetAttr( cdfid, nZId, szMetaName, &pszMetaValue) + == CE_None ) { + SetMetadataItem( szMetaName, pszMetaValue ); + } + else { + CPLDebug( "GDAL_netCDF", "invalid Band metadata %s", szMetaName ); + } + + if ( pszMetaValue ) { + CPLFree( pszMetaValue ); + pszMetaValue = NULL; + } + + } + + return CE_None; +} + +/************************************************************************/ +/* CheckData() */ +/************************************************************************/ +template +void netCDFRasterBand::CheckData ( void * pImage, + int nTmpBlockXSize, int nTmpBlockYSize, + int bCheckIsNan ) +{ + int i, j, k; + + CPLAssert( pImage != NULL ); + + /* if this block is not a full block (in the x axis), we need to re-arrange the data + this is because partial blocks are not arranged the same way in netcdf and gdal */ + if ( nTmpBlockXSize != nBlockXSize ) { + T* ptr = (T *) CPLCalloc( nTmpBlockXSize*nTmpBlockYSize, sizeof( T ) ); + memcpy( ptr, pImage, nTmpBlockXSize*nTmpBlockYSize*sizeof( T ) ); + for( j=0; j (T)adfValidRange[1] ) ) ) { + ( (T *)pImage )[k] = (T)dfNoDataValue; + } + } + } + } + + /* if mininum longitude is > 180, subtract 360 from all + if not, disable checking for further calls (check just once) + only check first and last block elements since lon must be monotonic */ + if ( bCheckLongitude && + MIN( ((T *)pImage)[0], ((T *)pImage)[nTmpBlockXSize-1] ) > 180.0 ) { + for( j=0; jbBottomUp ) { +#ifdef NCDF_DEBUG + if ( (nBlockYOff == 0) || (nBlockYOff == nRasterYSize-1) ) + CPLDebug( "GDAL_netCDF", + "reading bottom-up dataset, nBlockYSize=%d nRasterYSize=%d", + nBlockYSize, nRasterYSize ); +#endif + // check block size - return error if not 1 + // reading upside-down rasters with nBlockYSize!=1 needs further development + // perhaps a simple solution is to invert geotransform and not use bottom-up + if ( nBlockYSize == 1 ) { + start[nBandYPos] = nRasterYSize - 1 - nBlockYOff; + } + else { + CPLError( CE_Failure, CPLE_AppDefined, + "nBlockYSize = %d, only 1 supported when reading bottom-up dataset", nBlockYSize ); + return CE_Failure; + } + } else { + start[nBandYPos] = nBlockYOff * nBlockYSize; + } + + edge[nBandXPos] = nBlockXSize; + if ( ( start[nBandXPos] + edge[nBandXPos] ) > (size_t)nRasterXSize ) + edge[nBandXPos] = nRasterXSize - start[nBandXPos]; + edge[nBandYPos] = nBlockYSize; + if ( ( start[nBandYPos] + edge[nBandYPos] ) > (size_t)nRasterYSize ) + edge[nBandYPos] = nRasterYSize - start[nBandYPos]; + +#ifdef NCDF_DEBUG + if ( (nBlockYOff == 0) || (nBlockYOff == nRasterYSize-1) ) + CPLDebug( "GDAL_netCDF", "start={%ld,%ld} edge={%ld,%ld} bBottomUp=%d", + start[nBandXPos], start[nBandYPos], edge[nBandXPos], edge[nBandYPos], + ( ( netCDFDataset *) poDS )->bBottomUp ); +#endif + + if( nd == 3 ) { + start[panBandZPos[0]] = nLevel; // z + edge [panBandZPos[0]] = 1; + } + +/* -------------------------------------------------------------------- */ +/* Compute multidimention band position */ +/* */ +/* BandPosition = (Total - sum(PastBandLevels) - 1)/sum(remainingLevels)*/ +/* if Data[2,3,4,x,y] */ +/* */ +/* BandPos0 = (nBand ) / (3*4) */ +/* BandPos1 = (nBand - (3*4) ) / (4) */ +/* BandPos2 = (nBand - (3*4) ) % (4) */ +/* -------------------------------------------------------------------- */ + if (nd > 3) + { + Taken = 0; + for( i=0; i < nd-2 ; i++ ) + { + if( i != nd - 2 -1 ) { + Sum = 1; + for( j=i+1; j < nd-2; j++ ) { + Sum *= panBandZLev[j]; + } + start[panBandZPos[i]] = (int) ( ( nLevel-Taken) / Sum ); + edge[panBandZPos[i]] = 1; + } else { + start[panBandZPos[i]] = (int) ( ( nLevel-Taken) % Sum ); + edge[panBandZPos[i]] = 1; + } + Taken += start[panBandZPos[i]] * Sum; + } + } + + /* make sure we are in data mode */ + ( ( netCDFDataset * ) poDS )->SetDefineMode( FALSE ); + + /* read data according to type */ + if( eDataType == GDT_Byte ) + { + if (this->bSignedData) + { + status = nc_get_vara_schar( cdfid, nZId, start, edge, + (signed char *) pImage ); + if ( status == NC_NOERR ) + CheckData( pImage, edge[nBandXPos], edge[nBandYPos], + FALSE ); + } + else { + status = nc_get_vara_uchar( cdfid, nZId, start, edge, + (unsigned char *) pImage ); + if ( status == NC_NOERR ) + CheckData( pImage, edge[nBandXPos], edge[nBandYPos], + FALSE ); + } + } + + else if( eDataType == GDT_Int16 ) + { + status = nc_get_vara_short( cdfid, nZId, start, edge, + (short int *) pImage ); + if ( status == NC_NOERR ) + CheckData( pImage, edge[nBandXPos], edge[nBandYPos], + FALSE ); + } + else if( eDataType == GDT_Int32 ) + { + if( sizeof(long) == 4 ) + { + status = nc_get_vara_long( cdfid, nZId, start, edge, + (long int *) pImage ); + if ( status == NC_NOERR ) + CheckData( pImage, edge[nBandXPos], edge[nBandYPos], + FALSE ); + } + else + { + status = nc_get_vara_int( cdfid, nZId, start, edge, + (int *) pImage ); + if ( status == NC_NOERR ) + CheckData( pImage, edge[nBandXPos], edge[nBandYPos], + FALSE ); + } + } + else if( eDataType == GDT_Float32 ) + { + status = nc_get_vara_float( cdfid, nZId, start, edge, + (float *) pImage ); + if ( status == NC_NOERR ) + CheckData( pImage, edge[nBandXPos], edge[nBandYPos], + TRUE ); + } + else if( eDataType == GDT_Float64 ) + { + status = nc_get_vara_double( cdfid, nZId, start, edge, + (double *) pImage ); + if ( status == NC_NOERR ) + CheckData( pImage, edge[nBandXPos], edge[nBandYPos], + TRUE ); + } + else + status = NC_EBADTYPE; + + if( status != NC_NOERR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "netCDF scanline fetch failed: #%d (%s)", + status, nc_strerror( status ) ); + return CE_Failure; + } + else + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr netCDFRasterBand::IWriteBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + size_t start[ MAX_NC_DIMS]; + size_t edge[ MAX_NC_DIMS ]; + char pszName[ NCDF_MAX_STR_LEN ]; + int i,j; + int Sum=-1; + int Taken=-1; + int nd; + + CPLMutexHolderD(&hNCMutex); + +#ifdef NCDF_DEBUG + if ( (nBlockYOff == 0) || (nBlockYOff == nRasterYSize-1) ) + CPLDebug( "GDAL_netCDF", "netCDFRasterBand::IWriteBlock( %d, %d, ... ) nBand=%d", + nBlockXOff, nBlockYOff, nBand ); +#endif + + *pszName='\0'; + memset( start, 0, sizeof( start ) ); + memset( edge, 0, sizeof( edge ) ); + nc_inq_varndims ( cdfid, nZId, &nd ); + +/* -------------------------------------------------------------------- */ +/* Locate X, Y and Z position in the array */ +/* -------------------------------------------------------------------- */ + + start[nBandXPos] = 0; // x dim can move arround in array + // check y order + if( ( ( netCDFDataset *) poDS )->bBottomUp ) { + start[nBandYPos] = nRasterYSize - 1 - nBlockYOff; + } else { + start[nBandYPos] = nBlockYOff; // y + } + + edge[nBandXPos] = nBlockXSize; + edge[nBandYPos] = 1; + + if( nd == 3 ) { + start[panBandZPos[0]] = nLevel; // z + edge [panBandZPos[0]] = 1; + } + +/* -------------------------------------------------------------------- */ +/* Compute multidimention band position */ +/* */ +/* BandPosition = (Total - sum(PastBandLevels) - 1)/sum(remainingLevels)*/ +/* if Data[2,3,4,x,y] */ +/* */ +/* BandPos0 = (nBand ) / (3*4) */ +/* BandPos1 = (nBand - (3*4) ) / (4) */ +/* BandPos2 = (nBand - (3*4) ) % (4) */ +/* -------------------------------------------------------------------- */ + if (nd > 3) + { + Taken = 0; + for( i=0; i < nd-2 ; i++ ) + { + if( i != nd - 2 -1 ) { + Sum = 1; + for( j=i+1; j < nd-2; j++ ) { + Sum *= panBandZLev[j]; + } + start[panBandZPos[i]] = (int) ( ( nLevel-Taken) / Sum ); + edge[panBandZPos[i]] = 1; + } else { + start[panBandZPos[i]] = (int) ( ( nLevel-Taken) % Sum ); + edge[panBandZPos[i]] = 1; + } + Taken += start[panBandZPos[i]] * Sum; + } + } + + /* make sure we are in data mode */ + ( ( netCDFDataset * ) poDS )->SetDefineMode( FALSE ); + + /* copy data according to type */ + if( eDataType == GDT_Byte ) { + if ( this->bSignedData ) + status = nc_put_vara_schar( cdfid, nZId, start, edge, + (signed char*) pImage); + else + status = nc_put_vara_uchar( cdfid, nZId, start, edge, + (unsigned char*) pImage); + } + else if( ( eDataType == GDT_UInt16 ) || ( eDataType == GDT_Int16 ) ) { + status = nc_put_vara_short( cdfid, nZId, start, edge, + (short int *) pImage); + } + else if( eDataType == GDT_Int32 ) { + status = nc_put_vara_int( cdfid, nZId, start, edge, + (int *) pImage); + } + else if( eDataType == GDT_Float32 ) { + status = nc_put_vara_float( cdfid, nZId, start, edge, + (float *) pImage); + } + else if( eDataType == GDT_Float64 ) { + status = nc_put_vara_double( cdfid, nZId, start, edge, + (double *) pImage); + } + else { + CPLError( CE_Failure, CPLE_NotSupported, + "The NetCDF driver does not support GDAL data type %d", + eDataType ); + status = NC_EBADTYPE; + } + NCDF_ERR(status); + + if( status != NC_NOERR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "netCDF scanline write failed: %s", + nc_strerror( status ) ); + return CE_Failure; + } + else + return CE_None; + +} + +/************************************************************************/ +/* ==================================================================== */ +/* netCDFDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* netCDFDataset() */ +/************************************************************************/ + +netCDFDataset::netCDFDataset() + +{ + /* basic dataset vars */ + cdfid = -1; + papszSubDatasets = NULL; + papszMetadata = NULL; + bBottomUp = TRUE; + nFormat = NCDF_FORMAT_NONE; + bIsGdalFile = FALSE; + bIsGdalCfFile = FALSE; + + /* projection/GT */ + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + pszProjection = NULL; + nXDimID = -1; + nYDimID = -1; + bIsProjected = FALSE; + bIsGeographic = FALSE; /* can be not projected, and also not geographic */ + pszCFProjection = NULL; + pszCFCoordinates = NULL; + + /* state vars */ + status = NC_NOERR; + bDefineMode = TRUE; + bSetProjection = FALSE; + bSetGeoTransform = FALSE; + bAddedProjectionVars = FALSE; + bAddedGridMappingRef = FALSE; + + /* create vars */ + papszCreationOptions = NULL; + nCompress = NCDF_COMPRESS_NONE; + nZLevel = NCDF_DEFLATE_LEVEL; + nCreateMode = NC_CLOBBER; + bSignedData = TRUE; +} + + +/************************************************************************/ +/* ~netCDFDataset() */ +/************************************************************************/ + +netCDFDataset::~netCDFDataset() + +{ + CPLMutexHolderD(&hNCMutex); + + #ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "netCDFDataset::~netCDFDataset(), cdfid=%d filename=%s", + cdfid, osFilename.c_str() ); + #endif + /* make sure projection is written if GeoTransform OR Projection are missing */ + if( (GetAccess() == GA_Update) && (! bAddedProjectionVars) ) { + if ( bSetProjection && ! bSetGeoTransform ) + AddProjectionVars(); + else if ( bSetGeoTransform && ! bSetProjection ) + AddProjectionVars(); + // CPLError( CE_Warning, CPLE_AppDefined, + // "netCDFDataset::~netCDFDataset() Projection was not defined, projection will be missing" ); + } + + FlushCache(); + + /* make sure projection variable is written to band variable */ + if( (GetAccess() == GA_Update) && ! bAddedGridMappingRef ) + AddGridMappingRef(); + + CSLDestroy( papszMetadata ); + CSLDestroy( papszSubDatasets ); + CSLDestroy( papszCreationOptions ); + + if( pszProjection ) + CPLFree( pszProjection ); + if( pszCFProjection ) + CPLFree( pszCFProjection ); + if( pszCFCoordinates ) + CPLFree( pszCFCoordinates ); + + if( cdfid > 0 ) { +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "calling nc_close( %d )", cdfid ); +#endif + status = nc_close( cdfid ); + NCDF_ERR(status); + } + +} + +/************************************************************************/ +/* SetDefineMode() */ +/************************************************************************/ +int netCDFDataset::SetDefineMode( int bNewDefineMode ) +{ + /* do nothing if already in new define mode + or if dataset is in read-only mode */ + if ( ( bDefineMode == bNewDefineMode ) || + ( GetAccess() == GA_ReadOnly ) ) + return CE_None; + + CPLDebug( "GDAL_netCDF", "SetDefineMode(%d) old=%d", + bNewDefineMode, bDefineMode ); + + bDefineMode = bNewDefineMode; + + if ( bDefineMode == TRUE ) + status = nc_redef( cdfid ); + else + status = nc_enddef( cdfid ); + + NCDF_ERR(status); + return status; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **netCDFDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), + TRUE, + "SUBDATASETS", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ +char **netCDFDataset::GetMetadata( const char *pszDomain ) +{ + if( pszDomain != NULL && EQUALN( pszDomain, "SUBDATASETS", 11 ) ) + return papszSubDatasets; + else + return GDALDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char * netCDFDataset::GetProjectionRef() +{ + if( bSetProjection ) + return pszProjection; + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *netCDFDataset::SerializeToXML( const char *pszUnused ) + +{ +/* -------------------------------------------------------------------- */ +/* Overriden from GDALPamDataset to add only band histogram */ +/* and statistics. See bug #4244. */ +/* -------------------------------------------------------------------- */ + + CPLString oFmt; + + if( psPam == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Setup root node and attributes. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psDSTree; + + psDSTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMDataset" ); + +/* -------------------------------------------------------------------- */ +/* Process bands. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 0; iBand < GetRasterCount(); iBand++ ) + { + CPLXMLNode *psBandTree; + + netCDFRasterBand *poBand = (netCDFRasterBand *) + GetRasterBand(iBand+1); + + if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) ) + continue; + + psBandTree = poBand->SerializeToXML( pszUnused ); + + if( psBandTree != NULL ) + CPLAddXMLChild( psDSTree, psBandTree ); + } + +/* -------------------------------------------------------------------- */ +/* We don't want to return anything if we had no metadata to */ +/* attach. */ +/* -------------------------------------------------------------------- */ + if( psDSTree->psChild == NULL ) + { + CPLDestroyXMLNode( psDSTree ); + psDSTree = NULL; + } + + return psDSTree; +} + +/************************************************************************/ +/* FetchCopyParm() */ +/************************************************************************/ + +double netCDFDataset::FetchCopyParm( const char *pszGridMappingValue, + const char *pszParm, double dfDefault ) + +{ + char szTemp[ MAX_NC_NAME ]; + const char *pszValue; + + strcpy(szTemp,pszGridMappingValue); + strcat( szTemp, "#" ); + strcat( szTemp, pszParm ); + pszValue = CSLFetchNameValue(papszMetadata, szTemp); + + if( pszValue ) + { + return CPLAtofM(pszValue); + } + else + return dfDefault; +} + +/************************************************************************/ +/* FetchStandardParallels() */ +/************************************************************************/ + +char** netCDFDataset::FetchStandardParallels( const char *pszGridMappingValue ) +{ + char szTemp[ MAX_NC_NAME ]; + const char *pszValue; + char **papszValues = NULL; + //cf-1.0 tags + strcpy( szTemp,pszGridMappingValue ); + strcat( szTemp, "#" ); + strcat( szTemp, CF_PP_STD_PARALLEL ); + pszValue = CSLFetchNameValue( papszMetadata, szTemp ); + if( pszValue != NULL ) { + papszValues = NCDFTokenizeArray( pszValue ); + } + //try gdal tags + else + { + strcpy( szTemp, pszGridMappingValue ); + strcat( szTemp, "#" ); + strcat( szTemp, CF_PP_STD_PARALLEL_1 ); + + pszValue = CSLFetchNameValue( papszMetadata, szTemp ); + + if ( pszValue != NULL ) + papszValues = CSLAddString( papszValues, pszValue ); + + strcpy( szTemp,pszGridMappingValue ); + strcat( szTemp, "#" ); + strcat( szTemp, CF_PP_STD_PARALLEL_2 ); + + pszValue = CSLFetchNameValue( papszMetadata, szTemp ); + + if( pszValue != NULL ) + papszValues = CSLAddString( papszValues, pszValue ); + } + + return papszValues; +} + +/************************************************************************/ +/* SetProjectionFromVar() */ +/************************************************************************/ +void netCDFDataset::SetProjectionFromVar( int nVarId ) +{ + size_t start[2], edge[2]; + unsigned int i=0; + const char *pszValue = NULL; + int nVarProjectionID = -1; + char szVarName[ MAX_NC_NAME ]; + char szTemp[ MAX_NC_NAME ]; + char szGridMappingName[ MAX_NC_NAME ]; + char szGridMappingValue[ MAX_NC_NAME ]; + + double dfStdP1=0.0; + double dfStdP2=0.0; + double dfCenterLat=0.0; + double dfCenterLon=0.0; + double dfScale=1.0; + double dfFalseEasting=0.0; + double dfFalseNorthing=0.0; + double dfCentralMeridian=0.0; + double dfEarthRadius=0.0; + double dfInverseFlattening=0.0; + double dfLonPrimeMeridian=0.0; + const char *pszPMName=NULL; + double dfSemiMajorAxis=0.0; + double dfSemiMinorAxis=0.0; + + int bGotGeogCS = FALSE; + int bGotCfSRS = FALSE; + int bGotGdalSRS = FALSE; + int bGotCfGT = FALSE; + int bGotGdalGT = FALSE; + int bLookForWellKnownGCS = FALSE; //this could be a Config Option + + /* These values from CF metadata */ + OGRSpatialReference oSRS; + int nVarDimXID = -1; + int nVarDimYID = -1; + double *pdfXCoord = NULL; + double *pdfYCoord = NULL; + char szDimNameX[ MAX_NC_NAME ]; + char szDimNameY[ MAX_NC_NAME ]; + int nSpacingBegin=0; + int nSpacingMiddle=0; + int nSpacingLast=0; + int bLatSpacingOK=FALSE; + int bLonSpacingOK=FALSE; + size_t xdim = nRasterXSize; + size_t ydim = nRasterYSize; + + const char *pszUnits = NULL; + + /* These values from GDAL metadata */ + const char *pszWKT = NULL; + const char *pszGeoTransform = NULL; + char **papszGeoTransform = NULL; + + netCDFDataset * poDS = this; /* perhaps this should be removed for clarity */ + + /* temp variables to use in SetGeoTransform() and SetProjection() */ + double adfTempGeoTransform[6]; + char *pszTempProjection; + + CPLDebug( "GDAL_netCDF", "\n=====\nSetProjectionFromVar( %d )\n", nVarId ); + +/* -------------------------------------------------------------------- */ +/* Get x/y range information. */ +/* -------------------------------------------------------------------- */ + + adfTempGeoTransform[0] = 0.0; + adfTempGeoTransform[1] = 1.0; + adfTempGeoTransform[2] = 0.0; + adfTempGeoTransform[3] = 0.0; + adfTempGeoTransform[4] = 0.0; + adfTempGeoTransform[5] = 1.0; + pszTempProjection = NULL; + + if ( xdim == 1 || ydim == 1 ) { + CPLError( CE_Warning, CPLE_AppDefined, + "1-pixel width/height files not supported, xdim: %ld ydim: %ld", + (long)xdim, (long)ydim ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Look for grid_mapping metadata */ +/* -------------------------------------------------------------------- */ + + strcpy( szGridMappingValue, "" ); + strcpy( szGridMappingName, "" ); + + nc_inq_varname( cdfid, nVarId, szVarName ); + strcpy(szTemp,szVarName); + strcat(szTemp,"#"); + strcat(szTemp,CF_GRD_MAPPING); + + pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp); + if( pszValue ) { + strcpy(szGridMappingName,szTemp); + strcpy(szGridMappingValue,pszValue); + } + + if( !EQUAL( szGridMappingValue, "" ) ) { + + /* Read grid_mapping metadata */ + nc_inq_varid( cdfid, szGridMappingValue, &nVarProjectionID ); + poDS->ReadAttributes( cdfid, nVarProjectionID ); + +/* -------------------------------------------------------------------- */ +/* Look for GDAL spatial_ref and GeoTransform within grid_mapping */ +/* -------------------------------------------------------------------- */ + CPLDebug( "GDAL_netCDF", "got grid_mapping %s", szGridMappingValue ); + strcpy( szTemp,szGridMappingValue); + strcat( szTemp, "#" ); + strcat( szTemp, NCDF_SPATIAL_REF); + + pszWKT = CSLFetchNameValue(poDS->papszMetadata, szTemp); + + if( pszWKT != NULL ) { + strcpy( szTemp,szGridMappingValue); + strcat( szTemp, "#" ); + strcat( szTemp, NCDF_GEOTRANSFORM); + pszGeoTransform = CSLFetchNameValue(poDS->papszMetadata, szTemp); + } + } + +/* -------------------------------------------------------------------- */ +/* Get information about the file. */ +/* -------------------------------------------------------------------- */ +/* Was this file created by the GDAL netcdf driver? */ +/* Was this file created by the newer (CF-conformant) driver? */ +/* -------------------------------------------------------------------- */ +/* 1) If GDAL netcdf metadata is set, and version >= 1.9, */ +/* it was created with the new driver */ +/* 2) Else, if spatial_ref and GeoTransform are present in the */ +/* grid_mapping variable, it was created by the old driver */ +/* -------------------------------------------------------------------- */ + pszValue = CSLFetchNameValue(poDS->papszMetadata, "NC_GLOBAL#GDAL"); + + if( pszValue && NCDFIsGDALVersionGTE(pszValue, 1900)) { + bIsGdalFile = TRUE; + bIsGdalCfFile = TRUE; + } + else if( pszWKT != NULL && pszGeoTransform != NULL ) { + bIsGdalFile = TRUE; + bIsGdalCfFile = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Set default bottom-up default value */ +/* Y axis dimension and absence of GT can modify this value */ +/* Override with Config option GDAL_NETCDF_BOTTOMUP */ +/* -------------------------------------------------------------------- */ + /* new driver is bottom-up by default */ + if ( bIsGdalFile && ! bIsGdalCfFile ) + poDS->bBottomUp = FALSE; + else + poDS->bBottomUp = TRUE; + + CPLDebug( "GDAL_netCDF", + "bIsGdalFile=%d bIsGdalCfFile=%d bBottomUp=%d", + bIsGdalFile, bIsGdalCfFile, bBottomUp ); + +/* -------------------------------------------------------------------- */ +/* Look for dimension: lon */ +/* -------------------------------------------------------------------- */ + + memset( szDimNameX, '\0', sizeof( char ) * MAX_NC_NAME ); + memset( szDimNameY, '\0', sizeof( char ) * MAX_NC_NAME ); + + for( i = 0; (i < strlen( poDS->papszDimName[ poDS->nXDimID ] ) && + i < 3 ); i++ ) { + szDimNameX[i]=(char)tolower( ( poDS->papszDimName[poDS->nXDimID] )[i] ); + } + szDimNameX[3] = '\0'; + for( i = 0; (i < strlen( poDS->papszDimName[ poDS->nYDimID ] ) && + i < 3 ); i++ ) { + szDimNameY[i]=(char)tolower( ( poDS->papszDimName[poDS->nYDimID] )[i] ); + } + szDimNameY[3] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Read grid_mapping information and set projections */ +/* -------------------------------------------------------------------- */ + + if( !( EQUAL(szGridMappingName,"" ) ) ) { + + strcpy( szTemp, szGridMappingValue ); + strcat( szTemp, "#" ); + strcat( szTemp, CF_GRD_MAPPING_NAME ); + pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp); + + if( pszValue != NULL ) { + +/* -------------------------------------------------------------------- */ +/* Check for datum/spheroid information */ +/* -------------------------------------------------------------------- */ + dfEarthRadius = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_EARTH_RADIUS, + -1.0 ); + + dfLonPrimeMeridian = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LONG_PRIME_MERIDIAN, + 0.0 ); + // should try to find PM name from its value if not Greenwich + if ( ! CPLIsEqual(dfLonPrimeMeridian,0.0) ) + pszPMName = "unknown"; + + dfInverseFlattening = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_INVERSE_FLATTENING, + -1.0 ); + + dfSemiMajorAxis = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_SEMI_MAJOR_AXIS, + -1.0 ); + + dfSemiMinorAxis = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_SEMI_MINOR_AXIS, + -1.0 ); + + //see if semi-major exists if radius doesn't + if( dfEarthRadius < 0.0 ) + dfEarthRadius = dfSemiMajorAxis; + + //if still no radius, check old tag + if( dfEarthRadius < 0.0 ) + dfEarthRadius = poDS->FetchCopyParm( szGridMappingValue, + CF_PP_EARTH_RADIUS_OLD, + -1.0 ); + + //has radius value + if( dfEarthRadius > 0.0 ) { + //check for inv_flat tag + if( dfInverseFlattening < 0.0 ) { + //no inv_flat tag, check for semi_minor + if( dfSemiMinorAxis < 0.0 ) { + //no way to get inv_flat, use sphere + oSRS.SetGeogCS( "unknown", + NULL, + "Sphere", + dfEarthRadius, 0.0, + pszPMName, dfLonPrimeMeridian ); + bGotGeogCS = TRUE; + } + else { + if( dfSemiMajorAxis < 0.0 ) + dfSemiMajorAxis = dfEarthRadius; + //set inv_flat using semi_minor/major + dfInverseFlattening = OSRCalcInvFlattening(dfSemiMajorAxis, dfSemiMinorAxis); + + oSRS.SetGeogCS( "unknown", + NULL, + "Spheroid", + dfEarthRadius, dfInverseFlattening, + pszPMName, dfLonPrimeMeridian ); + bGotGeogCS = TRUE; + } + } + else { + oSRS.SetGeogCS( "unknown", + NULL, + "Spheroid", + dfEarthRadius, dfInverseFlattening, + pszPMName, dfLonPrimeMeridian ); + bGotGeogCS = TRUE; + } + + if ( bGotGeogCS ) + CPLDebug( "GDAL_netCDF", "got spheroid from CF: (%f , %f)", dfEarthRadius, dfInverseFlattening ); + + } + //no radius, set as wgs84 as default? + else { + // This would be too indiscrimant. But we should set + // it if we know the data is geographic. + //oSRS.SetWellKnownGeogCS( "WGS84" ); + } + +/* -------------------------------------------------------------------- */ +/* Transverse Mercator */ +/* -------------------------------------------------------------------- */ + + if( EQUAL( pszValue, CF_PT_TM ) ) { + + dfScale = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_SCALE_FACTOR_MERIDIAN, 1.0 ); + + dfCenterLon = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LONG_CENTRAL_MERIDIAN, 0.0 ); + + dfCenterLat = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LAT_PROJ_ORIGIN, 0.0 ); + + dfFalseEasting = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_EASTING, 0.0 ); + + dfFalseNorthing = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_NORTHING, 0.0 ); + + bGotCfSRS = TRUE; + oSRS.SetTM( dfCenterLat, + dfCenterLon, + dfScale, + dfFalseEasting, + dfFalseNorthing ); + + if( !bGotGeogCS ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + } + +/* -------------------------------------------------------------------- */ +/* Albers Equal Area */ +/* -------------------------------------------------------------------- */ + + if( EQUAL( pszValue, CF_PT_AEA ) ) { + + char **papszStdParallels = NULL; + + dfCenterLon = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LONG_CENTRAL_MERIDIAN, 0.0 ); + + dfCenterLat = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LAT_PROJ_ORIGIN, 0.0 ); + + dfFalseEasting = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_EASTING, 0.0 ); + + dfFalseNorthing = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_NORTHING, 0.0 ); + + papszStdParallels = + FetchStandardParallels( szGridMappingValue ); + + if( papszStdParallels != NULL ) { + + if ( CSLCount( papszStdParallels ) == 1 ) { + /* TODO CF-1 standard says it allows AEA to be encoded with only 1 standard parallel */ + /* how should this actually map to a 2StdP OGC WKT version? */ + CPLError( CE_Warning, CPLE_NotSupported, + "NetCDF driver import of AEA-1SP is not tested, using identical std. parallels\n" ); + dfStdP1 = CPLAtofM( papszStdParallels[0] ); + dfStdP2 = dfStdP1; + + } + + else if( CSLCount( papszStdParallels ) == 2 ) { + dfStdP1 = CPLAtofM( papszStdParallels[0] ); + dfStdP2 = CPLAtofM( papszStdParallels[1] ); + } + } + //old default + else { + dfStdP1 = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_STD_PARALLEL_1, 0.0 ); + + dfStdP2 = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_STD_PARALLEL_2, 0.0 ); + } + + dfCenterLat = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LAT_PROJ_ORIGIN, 0.0 ); + + bGotCfSRS = TRUE; + oSRS.SetACEA( dfStdP1, dfStdP2, dfCenterLat, dfCenterLon, + dfFalseEasting, dfFalseNorthing ); + + if( !bGotGeogCS ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + + CSLDestroy( papszStdParallels ); + } + +/* -------------------------------------------------------------------- */ +/* Cylindrical Equal Area */ +/* -------------------------------------------------------------------- */ + + else if( EQUAL( pszValue, CF_PT_CEA ) || EQUAL( pszValue, CF_PT_LCEA ) ) { + + char **papszStdParallels = NULL; + + papszStdParallels = + FetchStandardParallels( szGridMappingValue ); + + if( papszStdParallels != NULL ) { + dfStdP1 = CPLAtofM( papszStdParallels[0] ); + } + else { + //TODO: add support for 'scale_factor_at_projection_origin' variant to standard parallel + //Probably then need to calc a std parallel equivalent + CPLError( CE_Failure, CPLE_NotSupported, + "NetCDF driver does not support import of CF-1 LCEA " + "'scale_factor_at_projection_origin' variant yet.\n" ); + } + + dfCentralMeridian = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LONG_CENTRAL_MERIDIAN, 0.0 ); + + dfFalseEasting = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_EASTING, 0.0 ); + + dfFalseNorthing = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_NORTHING, 0.0 ); + + bGotCfSRS = TRUE; + oSRS.SetCEA( dfStdP1, dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); + + if( !bGotGeogCS ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + + CSLDestroy( papszStdParallels ); + } + +/* -------------------------------------------------------------------- */ +/* lambert_azimuthal_equal_area */ +/* -------------------------------------------------------------------- */ + else if( EQUAL( pszValue, CF_PT_LAEA ) ) { + dfCenterLon = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LON_PROJ_ORIGIN, 0.0 ); + + dfCenterLat = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LAT_PROJ_ORIGIN, 0.0 ); + + dfFalseEasting = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_EASTING, 0.0 ); + + dfFalseNorthing = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_NORTHING, 0.0 ); + + oSRS.SetProjCS( "LAEA (WGS84) " ); + + bGotCfSRS = TRUE; + oSRS.SetLAEA( dfCenterLat, dfCenterLon, + dfFalseEasting, dfFalseNorthing ); + + if( !bGotGeogCS ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + + } + +/* -------------------------------------------------------------------- */ +/* Azimuthal Equidistant */ +/* -------------------------------------------------------------------- */ + else if( EQUAL( pszValue, CF_PT_AE ) ) { + dfCenterLon = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LON_PROJ_ORIGIN, 0.0 ); + + dfCenterLat = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LAT_PROJ_ORIGIN, 0.0 ); + + dfFalseEasting = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_EASTING, 0.0 ); + + dfFalseNorthing = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_NORTHING, 0.0 ); + + bGotCfSRS = TRUE; + oSRS.SetAE( dfCenterLat, dfCenterLon, + dfFalseEasting, dfFalseNorthing ); + + if( !bGotGeogCS ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + + } + +/* -------------------------------------------------------------------- */ +/* Lambert conformal conic */ +/* -------------------------------------------------------------------- */ + else if( EQUAL( pszValue, CF_PT_LCC ) ) { + + char **papszStdParallels = NULL; + + dfCenterLon = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LONG_CENTRAL_MERIDIAN, 0.0 ); + + dfCenterLat = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LAT_PROJ_ORIGIN, 0.0 ); + + dfFalseEasting = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_EASTING, 0.0 ); + + dfFalseNorthing = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_NORTHING, 0.0 ); + + papszStdParallels = + FetchStandardParallels( szGridMappingValue ); + + /* 2SP variant */ + if( CSLCount( papszStdParallels ) == 2 ) { + dfStdP1 = CPLAtofM( papszStdParallels[0] ); + dfStdP2 = CPLAtofM( papszStdParallels[1] ); + oSRS.SetLCC( dfStdP1, dfStdP2, dfCenterLat, dfCenterLon, + dfFalseEasting, dfFalseNorthing ); + } + /* 1SP variant (with standard_parallel or center lon) */ + /* See comments in netcdfdataset.h for this projection. */ + else { + + dfScale = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_SCALE_FACTOR_ORIGIN, -1.0 ); + + /* CF definition, without scale factor */ + if( CPLIsEqual(dfScale, -1.0) ) { + + /* with standard_parallel */ + if( CSLCount( papszStdParallels ) == 1 ) + dfStdP1 = CPLAtofM( papszStdParallels[0] ); + /* with center lon instead */ + else + dfStdP1 = dfCenterLat; + dfStdP2 = dfStdP1; + + /* test if we should actually compute scale factor */ + if ( ! CPLIsEqual( dfStdP1, dfCenterLat ) ) { + CPLError( CE_Warning, CPLE_NotSupported, + "NetCDF driver import of LCC-1SP with standard_parallel1 != latitude_of_projection_origin\n" + "(which forces a computation of scale_factor) is experimental (bug #3324)\n" ); + /* use Snyder eq. 15-4 to compute dfScale from dfStdP1 and dfCenterLat */ + /* only tested for dfStdP1=dfCenterLat and (25,26), needs more data for testing */ + /* other option: use the 2SP variant - how to compute new standard parallels? */ + dfScale = ( cos(dfStdP1) * pow( tan(NCDF_PI/4 + dfStdP1/2), sin(dfStdP1) ) ) / + ( cos(dfCenterLat) * pow( tan(NCDF_PI/4 + dfCenterLat/2), sin(dfCenterLat) ) ); + } + /* default is 1.0 */ + else + dfScale = 1.0; + + oSRS.SetLCC1SP( dfCenterLat, dfCenterLon, dfScale, + dfFalseEasting, dfFalseNorthing ); + /* store dfStdP1 so we can output it to CF later */ + oSRS.SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdP1 ); + } + /* OGC/PROJ.4 definition with scale factor */ + else { + oSRS.SetLCC1SP( dfCenterLat, dfCenterLon, dfScale, + dfFalseEasting, dfFalseNorthing ); + } + } + + + bGotCfSRS = TRUE; + if( !bGotGeogCS ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + + CSLDestroy( papszStdParallels ); + } + +/* -------------------------------------------------------------------- */ +/* Is this Latitude/Longitude Grid explicitly */ +/* -------------------------------------------------------------------- */ + + else if ( EQUAL ( pszValue, CF_PT_LATITUDE_LONGITUDE ) ) { + bGotCfSRS = TRUE; + if( !bGotGeogCS ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + } +/* -------------------------------------------------------------------- */ +/* Mercator */ +/* -------------------------------------------------------------------- */ + + else if ( EQUAL ( pszValue, CF_PT_MERCATOR ) ) { + + char **papszStdParallels = NULL; + + /* If there is a standard_parallel, know it is Mercator 2SP */ + papszStdParallels = + FetchStandardParallels( szGridMappingValue ); + + if (NULL != papszStdParallels) { + /* CF-1 Mercator 2SP always has lat centered at equator */ + dfStdP1 = CPLAtofM( papszStdParallels[0] ); + + dfCenterLat = 0.0; + + dfCenterLon = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LON_PROJ_ORIGIN, 0.0 ); + + dfFalseEasting = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_EASTING, 0.0 ); + + dfFalseNorthing = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_NORTHING, 0.0 ); + + oSRS.SetMercator2SP( dfStdP1, dfCenterLat, dfCenterLon, + dfFalseEasting, dfFalseNorthing ); + } + else { + dfCenterLon = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LON_PROJ_ORIGIN, 0.0 ); + + dfCenterLat = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LAT_PROJ_ORIGIN, 0.0 ); + + dfScale = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_SCALE_FACTOR_ORIGIN, + 1.0 ); + + dfFalseEasting = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_EASTING, 0.0 ); + + dfFalseNorthing = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_NORTHING, 0.0 ); + + oSRS.SetMercator( dfCenterLat, dfCenterLon, dfScale, + dfFalseEasting, dfFalseNorthing ); + } + + bGotCfSRS = TRUE; + + if( !bGotGeogCS ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + + CSLDestroy( papszStdParallels ); + } + +/* -------------------------------------------------------------------- */ +/* Orthographic */ +/* -------------------------------------------------------------------- */ + + + else if ( EQUAL ( pszValue, CF_PT_ORTHOGRAPHIC ) ) { + dfCenterLon = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LON_PROJ_ORIGIN, 0.0 ); + + dfCenterLat = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LAT_PROJ_ORIGIN, 0.0 ); + + dfFalseEasting = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_EASTING, 0.0 ); + + dfFalseNorthing = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_NORTHING, 0.0 ); + + bGotCfSRS = TRUE; + + oSRS.SetOrthographic( dfCenterLat, dfCenterLon, + dfFalseEasting, dfFalseNorthing ); + + if( !bGotGeogCS ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + } + +/* -------------------------------------------------------------------- */ +/* Polar Stereographic */ +/* -------------------------------------------------------------------- */ + + else if ( EQUAL ( pszValue, CF_PT_POLAR_STEREO ) ) { + + char **papszStdParallels = NULL; + + dfScale = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_SCALE_FACTOR_ORIGIN, + -1.0 ); + + papszStdParallels = + FetchStandardParallels( szGridMappingValue ); + + /* CF allows the use of standard_parallel (lat_ts) OR scale_factor (k0), + make sure we have standard_parallel, using Snyder eq. 22-7 + with k=1 and lat=standard_parallel */ + if ( papszStdParallels != NULL ) { + dfStdP1 = CPLAtofM( papszStdParallels[0] ); + /* compute scale_factor from standard_parallel */ + /* this creates WKT that is inconsistent, don't write for now + also proj4 does not seem to use this parameter */ + // dfScale = ( 1.0 + fabs( sin( dfStdP1 * NCDF_PI / 180.0 ) ) ) / 2.0; + } + else { + if ( ! CPLIsEqual(dfScale,-1.0) ) { + /* compute standard_parallel from scale_factor */ + dfStdP1 = asin( 2*dfScale - 1 ) * 180.0 / NCDF_PI; + + /* fetch latitude_of_projection_origin (+90/-90) + used here for the sign of standard_parallel */ + double dfLatProjOrigin = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LAT_PROJ_ORIGIN, + 0.0 ); + if ( ! CPLIsEqual(dfLatProjOrigin,90.0) && + ! CPLIsEqual(dfLatProjOrigin,-90.0) ) { + CPLError( CE_Failure, CPLE_NotSupported, + "Polar Stereographic must have a %s parameter equal to +90 or -90\n.", + CF_PP_LAT_PROJ_ORIGIN ); + dfLatProjOrigin = 90.0; + } + if ( CPLIsEqual(dfLatProjOrigin,-90.0) ) + dfStdP1 = - dfStdP1; + } + else { + dfStdP1 = 0.0; //just to avoid warning at compilation + CPLError( CE_Failure, CPLE_NotSupported, + "The NetCDF driver does not support import of CF-1 Polar stereographic " + "without standard_parallel and scale_factor_at_projection_origin parameters.\n" ); + } + } + + /* set scale to default value 1.0 if it was not set */ + if ( CPLIsEqual(dfScale,-1.0) ) + dfScale = 1.0; + + dfCenterLon = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_VERT_LONG_FROM_POLE, 0.0 ); + + dfFalseEasting = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_EASTING, 0.0 ); + + dfFalseNorthing = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_NORTHING, 0.0 ); + + bGotCfSRS = TRUE; + /* map CF CF_PP_STD_PARALLEL_1 to WKT SRS_PP_LATITUDE_OF_ORIGIN */ + oSRS.SetPS( dfStdP1, dfCenterLon, dfScale, + dfFalseEasting, dfFalseNorthing ); + + if( !bGotGeogCS ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + + CSLDestroy( papszStdParallels ); + } + +/* -------------------------------------------------------------------- */ +/* Stereographic */ +/* -------------------------------------------------------------------- */ + + else if ( EQUAL ( pszValue, CF_PT_STEREO ) ) { + + dfCenterLon = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LON_PROJ_ORIGIN, 0.0 ); + + dfCenterLat = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_LAT_PROJ_ORIGIN, 0.0 ); + + dfScale = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_SCALE_FACTOR_ORIGIN, + 1.0 ); + + dfFalseEasting = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_EASTING, 0.0 ); + + dfFalseNorthing = + poDS->FetchCopyParm( szGridMappingValue, + CF_PP_FALSE_NORTHING, 0.0 ); + + bGotCfSRS = TRUE; + oSRS.SetStereographic( dfCenterLat, dfCenterLon, dfScale, + dfFalseEasting, dfFalseNorthing ); + + if( !bGotGeogCS ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + } + +/* -------------------------------------------------------------------- */ +/* Is this Latitude/Longitude Grid, default */ +/* -------------------------------------------------------------------- */ + + } else if( EQUAL( szDimNameX,"lon" ) ) { + oSRS.SetWellKnownGeogCS( "WGS84" ); + + } else { + // This would be too indiscrimant. But we should set + // it if we know the data is geographic. + //oSRS.SetWellKnownGeogCS( "WGS84" ); + } + } +/* -------------------------------------------------------------------- */ +/* Read projection coordinates */ +/* -------------------------------------------------------------------- */ + + nc_inq_varid( cdfid, poDS->papszDimName[nXDimID], &nVarDimXID ); + nc_inq_varid( cdfid, poDS->papszDimName[nYDimID], &nVarDimYID ); + + if( ( nVarDimXID != -1 ) && ( nVarDimYID != -1 ) ) { + pdfXCoord = (double *) CPLCalloc( xdim, sizeof(double) ); + pdfYCoord = (double *) CPLCalloc( ydim, sizeof(double) ); + + start[0] = 0; + edge[0] = xdim; + status = nc_get_vara_double( cdfid, nVarDimXID, + start, edge, pdfXCoord); + + edge[0] = ydim; + status = nc_get_vara_double( cdfid, nVarDimYID, + start, edge, pdfYCoord); + +/* -------------------------------------------------------------------- */ +/* Check for bottom-up from the Y-axis order */ +/* see bugs #4284 and #4251 */ +/* -------------------------------------------------------------------- */ + + if ( pdfYCoord[0] > pdfYCoord[1] ) + poDS->bBottomUp = FALSE; + else + poDS->bBottomUp = TRUE; + + CPLDebug( "GDAL_netCDF", "set bBottomUp = %d from Y axis", poDS->bBottomUp ); + +/* -------------------------------------------------------------------- */ +/* convert ]180,360] longitude values to [-180,180] */ +/* -------------------------------------------------------------------- */ + + if ( NCDFIsVarLongitude( cdfid, nVarDimXID, NULL ) && + CSLTestBoolean(CPLGetConfigOption("GDAL_NETCDF_CENTERLONG_180", "YES")) ) { + /* if mininum longitude is > 180, subtract 360 from all */ + if ( MIN( pdfXCoord[0], pdfXCoord[xdim-1] ) > 180.0 ) { + for ( size_t i=0; ipapszDimName[nXDimID] ); + strcat( szTemp, "#units" ); + pszValue = CSLFetchNameValue( poDS->papszMetadata, + szTemp ); + if( pszValue != NULL ) + pszUnitsX = pszValue; + + strcpy( szTemp, poDS->papszDimName[nYDimID] ); + strcat( szTemp, "#units" ); + pszValue = CSLFetchNameValue( poDS->papszMetadata, + szTemp ); + if( pszValue != NULL ) + pszUnitsY = pszValue; + + /* TODO: what to do if units are not equal in X and Y */ + if ( (pszUnitsX != NULL) && (pszUnitsY != NULL) && + EQUAL(pszUnitsX,pszUnitsY) ) + pszUnits = pszUnitsX; + + /* add units to PROJCS */ + if ( pszUnits != NULL && ! EQUAL(pszUnits,"") ) { + CPLDebug( "GDAL_netCDF", + "units=%s", pszUnits ); + if ( EQUAL(pszUnits,"m") ) { + oSRS.SetLinearUnits( "metre", 1.0 ); + oSRS.SetAuthority( "PROJCS|UNIT", "EPSG", 9001 ); + } + else if ( EQUAL(pszUnits,"km") ) { + oSRS.SetLinearUnits( "kilometre", 1000.0 ); + oSRS.SetAuthority( "PROJCS|UNIT", "EPSG", 9036 ); + } + /* TODO check for other values */ + // else + // oSRS.SetLinearUnits(pszUnits, 1.0); + } + } + else if ( oSRS.IsGeographic() ) { + oSRS.SetAngularUnits( CF_UNITS_D, CPLAtof(SRS_UA_DEGREE_CONV) ); + oSRS.SetAuthority( "GEOGCS|UNIT", "EPSG", 9122 ); + } + + /* Set Projection */ + oSRS.exportToWkt( &(pszTempProjection) ); + CPLDebug( "GDAL_netCDF", "setting WKT from CF" ); + SetProjection( pszTempProjection ); + CPLFree( pszTempProjection ); + + if ( !bGotCfGT ) + CPLDebug( "GDAL_netCDF", "got SRS but no geotransform from CF!"); + } + +/* -------------------------------------------------------------------- */ +/* Is pixel spacing uniform accross the map? */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +/* Check Longitude */ +/* -------------------------------------------------------------------- */ + + if( xdim == 2 ) { + bLonSpacingOK = TRUE; + } + else + { + nSpacingBegin = (int) poDS->rint((pdfXCoord[1] - pdfXCoord[0]) * 1000); + + nSpacingMiddle = (int) poDS->rint((pdfXCoord[xdim/2+1] - + pdfXCoord[xdim/2]) * 1000); + + nSpacingLast = (int) poDS->rint((pdfXCoord[xdim-1] - + pdfXCoord[xdim-2]) * 1000); + + CPLDebug("GDAL_netCDF", + "xdim: %ld nSpacingBegin: %d nSpacingMiddle: %d nSpacingLast: %d", + (long)xdim, nSpacingBegin, nSpacingMiddle, nSpacingLast ); +#ifdef NCDF_DEBUG + CPLDebug("GDAL_netCDF", + "xcoords: %f %f %f %f %f %f", + pdfXCoord[0], pdfXCoord[1], pdfXCoord[xdim / 2], pdfXCoord[(xdim / 2) + 1], + pdfXCoord[xdim - 2], pdfXCoord[xdim-1]); +#endif + + if( ( abs( abs( nSpacingBegin ) - abs( nSpacingLast ) ) <= 1 ) && + ( abs( abs( nSpacingBegin ) - abs( nSpacingMiddle ) ) <= 1 ) && + ( abs( abs( nSpacingMiddle ) - abs( nSpacingLast ) ) <= 1 ) ) { + bLonSpacingOK = TRUE; + } + } + + if ( bLonSpacingOK == FALSE ) { + CPLDebug( "GDAL_netCDF", + "Longitude is not equally spaced." ); + } + +/* -------------------------------------------------------------------- */ +/* Check Latitude */ +/* -------------------------------------------------------------------- */ + if( ydim == 2 ) { + bLatSpacingOK = TRUE; + } + else + { + nSpacingBegin = (int) poDS->rint((pdfYCoord[1] - pdfYCoord[0]) * + 1000); + + nSpacingMiddle = (int) poDS->rint((pdfYCoord[ydim/2+1] - + pdfYCoord[ydim/2]) * + 1000); + + nSpacingLast = (int) poDS->rint((pdfYCoord[ydim-1] - + pdfYCoord[ydim-2]) * + 1000); + + CPLDebug("GDAL_netCDF", + "ydim: %ld nSpacingBegin: %d nSpacingMiddle: %d nSpacingLast: %d", + (long)ydim, nSpacingBegin, nSpacingMiddle, nSpacingLast ); +#ifdef NCDF_DEBUG + CPLDebug("GDAL_netCDF", + "ycoords: %f %f %f %f %f %f", + pdfYCoord[0], pdfYCoord[1], pdfYCoord[ydim / 2], pdfYCoord[(ydim / 2) + 1], + pdfYCoord[ydim - 2], pdfYCoord[ydim-1]); +#endif + +/* -------------------------------------------------------------------- */ +/* For Latitude we allow an error of 0.1 degrees for gaussian */ +/* gridding (only if this is not a projected SRS) */ +/* -------------------------------------------------------------------- */ + + if( ( abs( abs( nSpacingBegin ) - abs( nSpacingLast ) ) <= 1 ) && + ( abs( abs( nSpacingBegin ) - abs( nSpacingMiddle ) ) <= 1 ) && + ( abs( abs( nSpacingMiddle ) - abs( nSpacingLast ) ) <= 1 ) ) { + bLatSpacingOK = TRUE; + } + else if( !oSRS.IsProjected() && + ( (( abs( abs(nSpacingBegin) - abs(nSpacingLast) ) ) <= 100 ) && + (( abs( abs(nSpacingBegin) - abs(nSpacingMiddle) ) ) <= 100 ) && + (( abs( abs(nSpacingMiddle) - abs(nSpacingLast) ) ) <= 100 ) ) ) { + bLatSpacingOK = TRUE; + CPLError(CE_Warning, 1,"Latitude grid not spaced evenly.\nSeting projection for grid spacing is within 0.1 degrees threshold.\n"); + + CPLDebug("GDAL_netCDF", + "Latitude grid not spaced evenly, but within 0.1 degree threshold (probably a Gaussian grid).\n" + "Saving original latitude values in Y_VALUES geolocation metadata" ); + Set1DGeolocation( nVarDimYID, "Y" ); + } + + if ( bLatSpacingOK == FALSE ) { + CPLDebug( "GDAL_netCDF", + "Latitude is not equally spaced." ); + } + } + if ( ( bLonSpacingOK == TRUE ) && ( bLatSpacingOK == TRUE ) ) { + +/* -------------------------------------------------------------------- */ +/* We have gridded data so we can set the Gereferencing info. */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +/* Enable GeoTransform */ +/* -------------------------------------------------------------------- */ + /* ----------------------------------------------------------*/ + /* In the following "actual_range" and "node_offset" */ + /* are attributes used by netCDF files created by GMT. */ + /* If we find them we know how to proceed. Else, use */ + /* the original algorithm. */ + /* --------------------------------------------------------- */ + double dummy[2], xMinMax[2], yMinMax[2]; + int node_offset = 0; + + bGotCfGT = TRUE; + + nc_get_att_int (cdfid, NC_GLOBAL, "node_offset", &node_offset); + + if (!nc_get_att_double (cdfid, nVarDimXID, "actual_range", dummy)) { + xMinMax[0] = dummy[0]; + xMinMax[1] = dummy[1]; + } + else { + xMinMax[0] = pdfXCoord[0]; + xMinMax[1] = pdfXCoord[xdim-1]; + node_offset = 0; + } + + if (!nc_get_att_double (cdfid, nVarDimYID, "actual_range", dummy)) { + yMinMax[0] = dummy[0]; + yMinMax[1] = dummy[1]; + } + else { + yMinMax[0] = pdfYCoord[0]; + yMinMax[1] = pdfYCoord[ydim-1]; + node_offset = 0; + } + + /* Check for reverse order of y-coordinate */ + if ( yMinMax[0] > yMinMax[1] ) { + dummy[0] = yMinMax[1]; + dummy[1] = yMinMax[0]; + yMinMax[0] = dummy[0]; + yMinMax[1] = dummy[1]; + } + + adfTempGeoTransform[0] = xMinMax[0]; + adfTempGeoTransform[2] = 0; + adfTempGeoTransform[3] = yMinMax[1]; + adfTempGeoTransform[4] = 0; + adfTempGeoTransform[1] = ( xMinMax[1] - xMinMax[0] ) / + ( poDS->nRasterXSize + (node_offset - 1) ); + adfTempGeoTransform[5] = ( yMinMax[0] - yMinMax[1] ) / + ( poDS->nRasterYSize + (node_offset - 1) ); + +/* -------------------------------------------------------------------- */ +/* Compute the center of the pixel */ +/* -------------------------------------------------------------------- */ + if ( !node_offset ) { // Otherwise its already the pixel center + adfTempGeoTransform[0] -= (adfTempGeoTransform[1] / 2); + adfTempGeoTransform[3] -= (adfTempGeoTransform[5] / 2); + } + + } + + CPLFree( pdfXCoord ); + CPLFree( pdfYCoord ); + }// end if (has dims) + +/* -------------------------------------------------------------------- */ +/* Process custom GDAL values (spatial_ref, GeoTransform) */ +/* -------------------------------------------------------------------- */ + if( !EQUAL( szGridMappingValue, "" ) ) { + + if( pszWKT != NULL ) { + +/* -------------------------------------------------------------------- */ +/* Compare SRS obtained from CF attributes and GDAL WKT */ +/* If possible use the more complete GDAL WKT */ +/* -------------------------------------------------------------------- */ + /* Set the SRS to the one written by GDAL */ + if ( ! bGotCfSRS || poDS->pszProjection == NULL || ! bIsGdalCfFile ) { + bGotGdalSRS = TRUE; + CPLDebug( "GDAL_netCDF", "setting WKT from GDAL" ); + SetProjection( pszWKT ); + } + else { /* use the SRS from GDAL if it doesn't conflict with the one from CF */ + char *pszProjectionGDAL = (char*) pszWKT ; + OGRSpatialReference oSRSGDAL; + oSRSGDAL.importFromWkt( &pszProjectionGDAL ); + /* set datum to unknown or else datums will not match, see bug #4281 */ + if ( oSRSGDAL.GetAttrNode( "DATUM" ) ) + oSRSGDAL.GetAttrNode( "DATUM" )->GetChild(0)->SetValue( "unknown" ); + /* need this for setprojection autotest */ + if ( oSRSGDAL.GetAttrNode( "PROJCS" ) ) + oSRSGDAL.GetAttrNode( "PROJCS" )->GetChild(0)->SetValue( "unnamed" ); + if ( oSRSGDAL.GetAttrNode( "GEOGCS" ) ) + oSRSGDAL.GetAttrNode( "GEOGCS" )->GetChild(0)->SetValue( "unknown" ); + oSRSGDAL.GetRoot()->StripNodes( "UNIT" ); + if ( oSRS.IsSame(&oSRSGDAL) ) { + // printf("ARE SAME, using GDAL WKT\n"); + bGotGdalSRS = TRUE; + CPLDebug( "GDAL_netCDF", "setting WKT from GDAL" ); + SetProjection( pszWKT ); + } + else { + CPLDebug( "GDAL_netCDF", + "got WKT from GDAL \n[%s]\nbut not using it because conflicts with CF\n[%s]\n", + pszWKT, poDS->pszProjection ); + } + } + +/* -------------------------------------------------------------------- */ +/* Look for GeoTransform Array, if not found in CF */ +/* -------------------------------------------------------------------- */ + if ( !bGotCfGT ) { + + /* TODO read the GT values and detect for conflict with CF */ + /* this could resolve the GT precision loss issue */ + + if( pszGeoTransform != NULL ) { + + bGotGdalGT = TRUE; + + papszGeoTransform = CSLTokenizeString2( pszGeoTransform, + " ", + CSLT_HONOURSTRINGS ); + adfTempGeoTransform[0] = CPLAtof( papszGeoTransform[0] ); + adfTempGeoTransform[1] = CPLAtof( papszGeoTransform[1] ); + adfTempGeoTransform[2] = CPLAtof( papszGeoTransform[2] ); + adfTempGeoTransform[3] = CPLAtof( papszGeoTransform[3] ); + adfTempGeoTransform[4] = CPLAtof( papszGeoTransform[4] ); + adfTempGeoTransform[5] = CPLAtof( papszGeoTransform[5] ); + +/* -------------------------------------------------------------------- */ +/* Look for corner array values */ +/* -------------------------------------------------------------------- */ + } else { + double dfNN=0.0, dfSN=0.0, dfEE=0.0, dfWE=0.0; + int bGotNN=FALSE, bGotSN=FALSE, bGotEE=FALSE, bGotWE=FALSE; + // CPLDebug( "GDAL_netCDF", "looking for geotransform corners\n" ); + + strcpy(szTemp,szGridMappingValue); + strcat( szTemp, "#" ); + strcat( szTemp, "Northernmost_Northing"); + pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp); + if( pszValue != NULL ) { + dfNN = CPLAtof( pszValue ); + bGotNN = TRUE; + } + + strcpy(szTemp,szGridMappingValue); + strcat( szTemp, "#" ); + strcat( szTemp, "Southernmost_Northing"); + pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp); + if( pszValue != NULL ) { + dfSN = CPLAtof( pszValue ); + bGotSN = TRUE; + } + + strcpy(szTemp,szGridMappingValue); + strcat( szTemp, "#" ); + strcat( szTemp, "Easternmost_Easting"); + pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp); + if( pszValue != NULL ) { + dfEE = CPLAtof( pszValue ); + bGotEE = TRUE; + } + + strcpy(szTemp,szGridMappingValue); + strcat( szTemp, "#" ); + strcat( szTemp, "Westernmost_Easting"); + pszValue = CSLFetchNameValue(poDS->papszMetadata, szTemp); + if( pszValue != NULL ) { + dfWE = CPLAtof( pszValue ); + bGotWE = TRUE; + } + + /* Only set the GeoTransform if we got all the values */ + if ( bGotNN && bGotSN && bGotEE && bGotWE ) { + + bGotGdalGT = TRUE; + + adfTempGeoTransform[0] = dfWE; + adfTempGeoTransform[1] = (dfEE - dfWE) / + ( poDS->GetRasterXSize() - 1 ); + adfTempGeoTransform[2] = 0.0; + adfTempGeoTransform[3] = dfNN; + adfTempGeoTransform[4] = 0.0; + adfTempGeoTransform[5] = (dfSN - dfNN) / + ( poDS->GetRasterYSize() - 1 ); + /* compute the center of the pixel */ + adfTempGeoTransform[0] = dfWE + - (adfTempGeoTransform[1] / 2); + adfTempGeoTransform[3] = dfNN + - (adfTempGeoTransform[5] / 2); + } + } // (pszGeoTransform != NULL) + CSLDestroy( papszGeoTransform ); + + if ( bGotGdalSRS && ! bGotGdalGT ) + CPLDebug( "GDAL_netCDF", "got SRS but not geotransform from GDAL!"); + + } // if ( !bGotCfGT ) + + } + } + + /* Set GeoTransform if we got a complete one - after projection has been set */ + if ( bGotCfGT || bGotGdalGT ) { + SetGeoTransform( adfTempGeoTransform ); + } + + /* Process geolocation arrays from CF "coordinates" attribute */ + /* perhaps we should only add if is not a (supported) CF projection (bIsCfProjection */ + ProcessCFGeolocation( nVarId ); + + /* debuging reports */ + CPLDebug( "GDAL_netCDF", + "bGotGeogCS=%d bGotCfSRS=%d bGotCfGT=%d bGotGdalSRS=%d bGotGdalGT=%d", + bGotGeogCS, bGotCfSRS, bGotCfGT, bGotGdalSRS, bGotGdalGT ); + + if ( !bGotCfGT && !bGotGdalGT ) + CPLDebug( "GDAL_netCDF", "did not get geotransform from CF nor GDAL!"); + + if ( !bGotGeogCS && !bGotCfSRS && !bGotGdalSRS && !bGotCfGT) + CPLDebug( "GDAL_netCDF", "did not get projection from CF nor GDAL!"); + +/* -------------------------------------------------------------------- */ +/* Search for Well-known GeogCS if got only CF WKT */ +/* Disabled for now, as a named datum also include control points */ +/* (see mailing list and bug#4281 */ +/* For example, WGS84 vs. GDA94 (EPSG:3577) - AEA in netcdf_cf.py */ +/* -------------------------------------------------------------------- */ + /* disabled for now, but could be set in a config option */ + bLookForWellKnownGCS = FALSE; + if ( bLookForWellKnownGCS && bGotCfSRS && ! bGotGdalSRS ) { + /* ET - could use a more exhaustive method by scanning all EPSG codes in data/gcs.csv */ + /* as proposed by Even in the gdal-dev mailing list "help for comparing two WKT" */ + /* this code could be contributed to a new function */ + /* OGRSpatialReference * OGRSpatialReference::FindMatchingGeogCS( const OGRSpatialReference *poOther ) */ + CPLDebug( "GDAL_netCDF", "Searching for Well-known GeogCS" ); + const char *pszWKGCSList[] = { "WGS84", "WGS72", "NAD27", "NAD83" }; + char *pszWKGCS = NULL; + oSRS.exportToPrettyWkt( &pszWKGCS ); + for( size_t i=0; iGetChild(0)->SetValue( "unknown" ); + /* could use OGRSpatialReference::StripCTParms() but let's keep TOWGS84 */ + oSRSTmp.GetRoot()->StripNodes( "AXIS" ); + oSRSTmp.GetRoot()->StripNodes( "AUTHORITY" ); + oSRSTmp.GetRoot()->StripNodes( "EXTENSION" ); + + oSRSTmp.exportToPrettyWkt( &pszWKGCS ); + if ( oSRS.IsSameGeogCS(&oSRSTmp) ) { + oSRS.SetWellKnownGeogCS( pszWKGCSList[i] ); + oSRS.exportToWkt( &(pszTempProjection) ); + SetProjection( pszTempProjection ); + CPLFree( pszTempProjection ); + } + } + } +} + + +int netCDFDataset::ProcessCFGeolocation( int nVarId ) +{ + int bAddGeoloc = FALSE; + char *pszTemp = NULL; + char **papszTokens = NULL; + CPLString osTMP; + char szGeolocXName[NC_MAX_NAME]; + char szGeolocYName[NC_MAX_NAME]; + szGeolocXName[0] = '\0'; + szGeolocYName[0] = '\0'; + + if ( NCDFGetAttr( cdfid, nVarId, "coordinates", &pszTemp ) == CE_None ) { + /* get X and Y geolocation names from coordinates attribute */ + papszTokens = CSLTokenizeString2( pszTemp, " ", 0 ); + if ( CSLCount(papszTokens) >= 2 ) { + /* test that each variable is longitude/latitude */ + for ( int i=0; i 0 ) { + // nc_put_att_text( cdfid, NCDFVarID, "proj4", + // strlen( pszProj4Defn ), pszProj4Defn ); + // } + nc_put_att_text( cdfid, NCDFVarID, NCDF_SPATIAL_REF, + strlen( pszProjection ), pszProjection ); + /* for now write the geotransform for back-compat or else + the old (1.8.1) driver overrides the CF geotransform with + empty values from dfNN, dfSN, dfEE, dfWE; */ + /* TODO: fix this in 1.8 branch, and then remove this here */ + if ( bWriteGeoTransform && bSetGeoTransform ) { + nc_put_att_text( cdfid, NCDFVarID, NCDF_GEOTRANSFORM, + strlen( szGeoTransform ), + szGeoTransform ); + } + } + + /* write projection variable to band variable */ + /* need to call later if there are no bands */ + AddGridMappingRef(); + + } /* end if( bWriteGridMapping ) */ + + pfnProgress( 0.10, NULL, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Write CF Projection vars */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +/* Write X/Y attributes */ +/* -------------------------------------------------------------------- */ + if( bIsProjected ) + { + pszUnits = oSRS.GetAttrValue("PROJCS|UNIT",1); + if ( pszUnits == NULL || EQUAL(pszUnits,"1") ) + strcpy(szUnits,"m"); + else if ( EQUAL(pszUnits,"1000") ) + strcpy(szUnits,"km"); + + /* X */ + int anXDims[1]; + anXDims[0] = nXDimID; + CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)", + cdfid, NCDF_DIMNAME_X, NC_DOUBLE ); + status = nc_def_var( cdfid, NCDF_DIMNAME_X, NC_DOUBLE, + 1, anXDims, &NCDFVarID ); + NCDF_ERR(status); + nVarXID=NCDFVarID; + nc_put_att_text( cdfid, NCDFVarID, CF_STD_NAME, + strlen(CF_PROJ_X_COORD), + CF_PROJ_X_COORD ); + nc_put_att_text( cdfid, NCDFVarID, CF_LNG_NAME, + strlen(CF_PROJ_X_COORD_LONG_NAME), + CF_PROJ_X_COORD_LONG_NAME ); + nc_put_att_text( cdfid, NCDFVarID, CF_UNITS, strlen(szUnits), szUnits ); + + /* Y */ + int anYDims[1]; + anYDims[0] = nYDimID; + CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d)", + cdfid, NCDF_DIMNAME_Y, NC_DOUBLE ); + status = nc_def_var( cdfid, NCDF_DIMNAME_Y, NC_DOUBLE, + 1, anYDims, &NCDFVarID ); + NCDF_ERR(status); + nVarYID=NCDFVarID; + nc_put_att_text( cdfid, NCDFVarID, CF_STD_NAME, + strlen(CF_PROJ_Y_COORD), + CF_PROJ_Y_COORD ); + nc_put_att_text( cdfid, NCDFVarID, CF_LNG_NAME, + strlen(CF_PROJ_Y_COORD_LONG_NAME), + CF_PROJ_Y_COORD_LONG_NAME ); + nc_put_att_text( cdfid, NCDFVarID, CF_UNITS, strlen(szUnits), szUnits ); + } + +/* -------------------------------------------------------------------- */ +/* Write lat/lon attributes if needed */ +/* -------------------------------------------------------------------- */ + if ( bWriteLonLat ) { + int *panLatDims=NULL; + int *panLonDims=NULL; + int nLatDims=-1; + int nLonDims=-1; + + /* get information */ + if ( bHasGeoloc ) { /* geoloc */ + nLatDims = 2; + panLatDims = (int *) CPLCalloc( nLatDims, sizeof( int ) ); + panLatDims[0] = nYDimID; + panLatDims[1] = nXDimID; + nLonDims = 2; + panLonDims = (int *) CPLCalloc( nLonDims, sizeof( int ) ); + panLonDims[0] = nYDimID; + panLonDims[1] = nXDimID; + } + else if ( bIsProjected ) { /* projected */ + nLatDims = 2; + panLatDims = (int *) CPLCalloc( nLatDims, sizeof( int ) ); + panLatDims[0] = nYDimID; + panLatDims[1] = nXDimID; + nLonDims = 2; + panLonDims = (int *) CPLCalloc( nLonDims, sizeof( int ) ); + panLonDims[0] = nYDimID; + panLonDims[1] = nXDimID; + } + else { /* geographic */ + nLatDims = 1; + panLatDims = (int *) CPLCalloc( nLatDims, sizeof( int ) ); + panLatDims[0] = nYDimID; + nLonDims = 1; + panLonDims = (int *) CPLCalloc( nLonDims, sizeof( int ) ); + panLonDims[0] = nXDimID; + } + + /* def vars and attributes */ + status = nc_def_var( cdfid, NCDF_DIMNAME_LAT, eLonLatType, + nLatDims, panLatDims, &NCDFVarID ); + CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d,%d,-,-) got id %d", + cdfid, NCDF_DIMNAME_LAT, eLonLatType, nLatDims, NCDFVarID ); + NCDF_ERR(status); + DefVarDeflate( NCDFVarID, FALSE ); // don't set chunking + nVarLatID = NCDFVarID; + nc_put_att_text( cdfid, NCDFVarID, CF_STD_NAME, + 8,"latitude" ); + nc_put_att_text( cdfid, NCDFVarID, CF_LNG_NAME, + 8, "latitude" ); + nc_put_att_text( cdfid, NCDFVarID, CF_UNITS, + 13, "degrees_north" ); + + status = nc_def_var( cdfid, NCDF_DIMNAME_LON, eLonLatType, + nLonDims, panLonDims, &NCDFVarID ); + CPLDebug( "GDAL_netCDF", "nc_def_var(%d,%s,%d,%d,-,-) got id %d", + cdfid, NCDF_DIMNAME_LON, eLonLatType, nLatDims, NCDFVarID ); + NCDF_ERR(status); + DefVarDeflate( NCDFVarID, FALSE ); // don't set chunking + nVarLonID = NCDFVarID; + nc_put_att_text( cdfid, NCDFVarID, CF_STD_NAME, + 9, "longitude" ); + nc_put_att_text( cdfid, NCDFVarID, CF_LNG_NAME, + 9, "longitude" ); + nc_put_att_text( cdfid, NCDFVarID, CF_UNITS, + 12, "degrees_east" ); + /* free data */ + CPLFree( panLatDims ); + CPLFree( panLonDims ); + + } + + pfnProgress( 0.50, NULL, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Get projection values */ +/* -------------------------------------------------------------------- */ + + double dfX0, dfDX, dfY0, dfDY; + dfX0=0.0, dfDX=0.0, dfY0=0.0, dfDY=0.0; + double *padLonVal = NULL; + double *padLatVal = NULL; /* should use float for projected, save space */ + + if( bIsProjected ) + { + // const char *pszProjection; + OGRSpatialReference oSRS; + OGRSpatialReference *poLatLonSRS = NULL; + OGRCoordinateTransformation *poTransform = NULL; + + char *pszWKT = (char *) pszProjection; + oSRS.importFromWkt( &pszWKT ); + + double *padYVal = NULL; + double *padXVal = NULL; + size_t startX[1]; + size_t countX[1]; + size_t startY[1]; + size_t countY[1]; + + CPLDebug("GDAL_netCDF", "Getting (X,Y) values" ); + + padXVal = (double *) CPLMalloc( nRasterXSize * sizeof( double ) ); + padYVal = (double *) CPLMalloc( nRasterYSize * sizeof( double ) ); + +/* -------------------------------------------------------------------- */ +/* Get Y values */ +/* -------------------------------------------------------------------- */ + if ( ! bBottomUp ) + dfY0 = adfGeoTransform[3]; + else /* invert latitude values */ + dfY0 = adfGeoTransform[3] + ( adfGeoTransform[5] * nRasterYSize ); + dfDY = adfGeoTransform[5]; + + for( int j=0; j(lon,lat)" ); + else + CPLDebug("GDAL_netCDF", "writing (lon,lat) from GEOLOCATION arrays" ); + + int bOK = TRUE; + double dfProgress = 0.2; + int i,j; + + size_t start[]={ 0, 0 }; + size_t count[]={ 1, (size_t)nRasterXSize }; + padLatVal = (double *) CPLMalloc( nRasterXSize * sizeof( double ) ); + padLonVal = (double *) CPLMalloc( nRasterXSize * sizeof( double ) ); + + for( j = 0; (j < nRasterYSize) && bOK && (status == NC_NOERR); j++ ) { + + start[0] = j; + + /* get values from geotransform */ + if ( ! bHasGeoloc ) { + /* fill values to transform */ + for( i=0; iTransform( nRasterXSize, + padLonVal, padLatVal, NULL ); + if ( ! bOK ) { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to Transform (X,Y) to (lon,lat).\n" ); + } + } + /* get values from geoloc arrays */ + else { + eErr = GDALRasterIO( hBand_Y, GF_Read, + 0, j, nRasterXSize, 1, + padLatVal, nRasterXSize, 1, + GDT_Float64, 0, 0 ); + if ( eErr == CE_None ) { + eErr = GDALRasterIO( hBand_X, GF_Read, + 0, j, nRasterXSize, 1, + padLonVal, nRasterXSize, 1, + GDT_Float64, 0, 0 ); + } + + if ( eErr == CE_None ) + bOK = TRUE; + else { + bOK = FALSE; + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to get scanline %d\n",j ); + } + } + + /* write data */ + if ( bOK ) { + status = nc_put_vara_double( cdfid, nVarLatID, start, + count, padLatVal); + NCDF_ERR(status); + status = nc_put_vara_double( cdfid, nVarLonID, start, + count, padLonVal); + NCDF_ERR(status); + } + + if ( j % (nRasterYSize/10) == 0 ) { + dfProgress += 0.08; + pfnProgress( dfProgress , NULL, pProgressData ); + } + } + + } + + /* Free the srs and transform objects */ + if ( poLatLonSRS != NULL ) CPLFree( poLatLonSRS ); + if ( poTransform != NULL ) CPLFree( poTransform ); + + /* Free data */ + CPLFree( padXVal ); + CPLFree( padYVal ); + CPLFree( padLonVal ); + CPLFree( padLatVal); + + } // projected + + /* If not Projected assume Geographic to catch grids without Datum */ + else if ( bWriteLonLat == TRUE ) { + +/* -------------------------------------------------------------------- */ +/* Get latitude values */ +/* -------------------------------------------------------------------- */ + if ( ! bBottomUp ) + dfY0 = adfGeoTransform[3]; + else /* invert latitude values */ + dfY0 = adfGeoTransform[3] + ( adfGeoTransform[5] * nRasterYSize ); + dfDY = adfGeoTransform[5]; + + /* override lat values with the ones in GEOLOCATION/Y_VALUES */ + if ( GetMetadataItem( "Y_VALUES", "GEOLOCATION" ) != NULL ) { + int nTemp = 0; + padLatVal = Get1DGeolocation( "Y_VALUES", nTemp ); + /* make sure we got the correct amount, if not fallback to GT */ + /* could add test fabs( fabs(padLatVal[0]) - fabs(dfY0) ) <= 0.1 ) ) */ + if ( nTemp == nRasterYSize ) { + CPLDebug("GDAL_netCDF", "Using Y_VALUES geolocation metadata for lat values" ); + } + else { + CPLDebug("GDAL_netCDF", + "Got %d elements from Y_VALUES geolocation metadata, need %d", + nTemp, nRasterYSize ); + if ( padLatVal ) { + CPLFree( padLatVal ); + padLatVal = NULL; + } + } + } + + if ( padLatVal == NULL ) { + padLatVal = (double *) CPLMalloc( nRasterYSize * sizeof( double ) ); + for( int i=0; i= 1) && (GetRasterBand( 1 )) && + pszCFProjection != NULL && ! EQUAL( pszCFProjection, "" ) ) { + + nVarId = ( (netCDFRasterBand *) GetRasterBand( 1 ) )->nZId; + bAddedGridMappingRef = TRUE; + + /* make sure we are in define mode */ + SetDefineMode( TRUE ); + status = nc_put_att_text( cdfid, nVarId, + CF_GRD_MAPPING, + strlen( pszCFProjection ), + pszCFProjection ); + NCDF_ERR(status); + if ( pszCFCoordinates != NULL && ! EQUAL( pszCFCoordinates, "" ) ) { + status = nc_put_att_text( cdfid, nVarId, + CF_COORDINATES, + strlen( pszCFCoordinates ), + pszCFCoordinates ); + NCDF_ERR(status); + } + + /* go back to previous define mode */ + SetDefineMode( bOldDefineMode ); + } +} + + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr netCDFDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + if( bSetGeoTransform ) + return CE_None; + else + return GDALPamDataset::GetGeoTransform( padfTransform ); +} + +/************************************************************************/ +/* rint() */ +/************************************************************************/ + +double netCDFDataset::rint( double dfX) +{ + if( dfX > 0 ) { + int nX = (int) (dfX+0.5); + if( nX % 2 ) { + double dfDiff = dfX - (double)nX; + if( dfDiff == -0.5 ) + return double( nX-1 ); + } + return double( nX ); + } else { + int nX= (int) (dfX-0.5); + if( nX % 2 ) { + double dfDiff = dfX - (double)nX; + if( dfDiff == 0.5 ) + return double(nX+1); + } + return double(nX); + } +} + +/************************************************************************/ +/* ReadAttributes() */ +/************************************************************************/ +CPLErr netCDFDataset::ReadAttributes( int cdfid, int var) + +{ + char szAttrName[ NC_MAX_NAME ]; + char szVarName [ NC_MAX_NAME ]; + char szMetaName[ NC_MAX_NAME * 2 ]; + char *pszMetaTemp = NULL; + int nbAttr; + + nc_inq_varnatts( cdfid, var, &nbAttr ); + if( var == NC_GLOBAL ) { + strcpy( szVarName,"NC_GLOBAL" ); + } + else { + nc_inq_varname( cdfid, var, szVarName ); + } + + for( int l=0; l < nbAttr; l++) { + + nc_inq_attname( cdfid, var, l, szAttrName); + sprintf( szMetaName, "%s#%s", szVarName, szAttrName ); + + if ( NCDFGetAttr( cdfid, var, szAttrName, &pszMetaTemp ) + == CE_None ) { + papszMetadata = CSLSetNameValue(papszMetadata, + szMetaName, + pszMetaTemp); + CPLFree(pszMetaTemp); + pszMetaTemp = NULL; + } + else { + CPLDebug( "GDAL_netCDF", "invalid global metadata %s", szMetaName ); + } + + } + + return CE_None; + +} + + +/************************************************************************/ +/* netCDFDataset::CreateSubDatasetList() */ +/************************************************************************/ +void netCDFDataset::CreateSubDatasetList( ) +{ + + char szDim[ MAX_NC_NAME ]; + char szTemp[ MAX_NC_NAME ]; + char szType[ MAX_NC_NAME ]; + char szName[ MAX_NC_NAME ]; + char szVarStdName[ MAX_NC_NAME ]; + int nDims; + int nVar; + int nVarCount; + int i; + nc_type nVarType; + int *ponDimIds; + size_t nDimLen; + int nSub; + nc_type nAttype; + size_t nAttlen; + + netCDFDataset *poDS; + poDS = this; + + nSub=1; + nc_inq_nvars ( cdfid, &nVarCount ); + + for ( nVar = 0; nVar < nVarCount; nVar++ ) { + + nc_inq_varndims ( cdfid, nVar, &nDims ); + + if( nDims >= 2 ) { + ponDimIds = (int *) CPLCalloc( nDims, sizeof( int ) ); + nc_inq_vardimid ( cdfid, nVar, ponDimIds ); + +/* -------------------------------------------------------------------- */ +/* Create Sub dataset list */ +/* -------------------------------------------------------------------- */ + szDim[0]='\0'; + for( i = 0; i < nDims; i++ ) { + nc_inq_dimlen ( cdfid, ponDimIds[i], &nDimLen ); + sprintf(szTemp, "%d", (int) nDimLen); + strcat(szTemp, "x" ); + strcat(szDim, szTemp); + } + + nc_inq_vartype( cdfid, nVar, &nVarType ); +/* -------------------------------------------------------------------- */ +/* Get rid of the last "x" character */ +/* -------------------------------------------------------------------- */ + szDim[strlen(szDim) - 1] = '\0'; + switch( nVarType ) { + + case NC_BYTE: + strcpy(szType, "8-bit integer"); + break; + case NC_CHAR: + strcpy(szType, "8-bit character"); + break; + case NC_SHORT: + strcpy(szType, "16-bit integer"); + break; + case NC_INT: + strcpy(szType, "32-bit integer"); + break; + case NC_FLOAT: + strcpy(szType, "32-bit floating-point"); + break; + case NC_DOUBLE: + strcpy(szType, "64-bit floating-point"); + break; +#ifdef NETCDF_HAS_NC4 + case NC_UBYTE: + strcpy(szType, "8-bit unsigned integer"); + break; + case NC_USHORT: + strcpy(szType, "16-bit unsigned integer"); + break; + case NC_UINT: + strcpy(szType, "32-bit unsigned integer"); + break; + case NC_INT64: + strcpy(szType, "64-bit integer"); + break; + case NC_UINT64: + strcpy(szType, "64-bit unsigned integer"); + break; +#endif + default: + break; + } + nc_inq_varname( cdfid, nVar, szName); + nc_inq_att( cdfid, nVar, CF_STD_NAME, &nAttype, &nAttlen); + if( nc_get_att_text ( cdfid, nVar, CF_STD_NAME, + szVarStdName ) == NC_NOERR ) { + szVarStdName[nAttlen] = '\0'; + } + else { + strcpy( szVarStdName, szName ); + } + + sprintf( szTemp, "SUBDATASET_%d_NAME", nSub); + + poDS->papszSubDatasets = + CSLSetNameValue( poDS->papszSubDatasets, szTemp, + CPLSPrintf( "NETCDF:\"%s\":%s", + poDS->osFilename.c_str(), + szName) ) ; + + sprintf( szTemp, "SUBDATASET_%d_DESC", nSub++ ); + + poDS->papszSubDatasets = + CSLSetNameValue( poDS->papszSubDatasets, szTemp, + CPLSPrintf( "[%s] %s (%s)", + szDim, + szVarStdName, + szType ) ); + + CPLFree(ponDimIds); + } + } + +} + +/************************************************************************/ +/* IdentifyFormat() */ +/************************************************************************/ + +int netCDFDataset::IdentifyFormat( GDALOpenInfo * poOpenInfo, +#ifndef HAVE_HDF5 +CPL_UNUSED +#endif + bool bCheckExt = TRUE ) +{ +/* -------------------------------------------------------------------- */ +/* Does this appear to be a netcdf file? If so, which format? */ +/* http://www.unidata.ucar.edu/software/netcdf/docs/faq.html#fv1_5 */ +/* -------------------------------------------------------------------- */ + + if( EQUALN(poOpenInfo->pszFilename,"NETCDF:",7) ) + return NCDF_FORMAT_UNKNOWN; + if ( poOpenInfo->nHeaderBytes < 4 ) + return NCDF_FORMAT_NONE; + if ( EQUALN((char*)poOpenInfo->pabyHeader,"CDF\001",4) ) + { + /* In case the netCDF driver is registered before the GMT driver, */ + /* avoid opening GMT files */ + if( GDALGetDriverByName("GMT") != NULL ) + { + int bFoundZ = FALSE, bFoundDimension = FALSE; + for(int i=0;inHeaderBytes - 11;i++) + { + if( poOpenInfo->pabyHeader[i] == 1 && + poOpenInfo->pabyHeader[i+1] == 'z' && + poOpenInfo->pabyHeader[i+2] == 0 ) + bFoundZ = TRUE; + else if( poOpenInfo->pabyHeader[i] == 9 && + memcmp((const char*)poOpenInfo->pabyHeader + i + 1, "dimension", 9) == 0 && + poOpenInfo->pabyHeader[i+10] == 0 ) + bFoundDimension = TRUE; + } + if( bFoundZ && bFoundDimension ) + return NCDF_FORMAT_UNKNOWN; + } + + return NCDF_FORMAT_NC; + } + else if ( EQUALN((char*)poOpenInfo->pabyHeader,"CDF\002",4) ) + return NCDF_FORMAT_NC2; + else if ( EQUALN((char*)poOpenInfo->pabyHeader,"\211HDF\r\n\032\n",8) ) { + /* Requires netCDF-4/HDF5 support in libnetcdf (not just libnetcdf-v4). + If HDF5 is not supported in GDAL, this driver will try to open the file + Else, make sure this driver does not try to open HDF5 files + If user really wants to open with this driver, use NETCDF:file.h5 format. + This check should be relaxed, but there is no clear way to make a difference. + */ + + /* Check for HDF5 support in GDAL */ +#ifdef HAVE_HDF5 + if ( bCheckExt ) { /* Check by default */ + const char* pszExtension = CPLGetExtension( poOpenInfo->pszFilename ); + if ( ! ( EQUAL( pszExtension, "nc") || EQUAL( pszExtension, "cdf") + || EQUAL( pszExtension, "nc2") || EQUAL( pszExtension, "nc4") + || EQUAL( pszExtension, "nc3") || EQUAL( pszExtension, "grd") ) ) + return NCDF_FORMAT_HDF5; + } +#endif + + /* Check for netcdf-4 support in libnetcdf */ +#ifdef NETCDF_HAS_NC4 + return NCDF_FORMAT_NC4; +#else + return NCDF_FORMAT_HDF5; +#endif + + } + else if ( EQUALN((char*)poOpenInfo->pabyHeader,"\016\003\023\001",4) ) { + /* Requires HDF4 support in libnetcdf, but if HF4 is supported by GDAL don't try to open. */ + /* If user really wants to open with this driver, use NETCDF:file.hdf syntax. */ + + /* Check for HDF4 support in GDAL */ +#ifdef HAVE_HDF4 + if ( bCheckExt ) { /* Check by default */ + /* Always treat as HDF4 file */ + return NCDF_FORMAT_HDF4; + } +#endif + + /* Check for HDF4 support in libnetcdf */ +#ifdef NETCDF_HAS_HDF4 + return NCDF_FORMAT_NC4; +#else + return NCDF_FORMAT_HDF4; +#endif + } + + return NCDF_FORMAT_NONE; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int netCDFDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( EQUALN(poOpenInfo->pszFilename,"NETCDF:",7) ) { + return TRUE; + } + int nTmpFormat = IdentifyFormat( poOpenInfo ); + if( NCDF_FORMAT_NC == nTmpFormat || + NCDF_FORMAT_NC2 == nTmpFormat || + NCDF_FORMAT_NC4 == nTmpFormat || + NCDF_FORMAT_NC4C == nTmpFormat ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *netCDFDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int j; + unsigned int k; + int nd; + int cdfid, dim_count, var, var_count; + int i = 0; + size_t lev_count; + size_t nTotLevCount = 1; + int nDim = 2; + int status; + int nDimID; + char szConventions[NC_MAX_NAME]; + int ndims, nvars, ngatts, unlimdimid; + int nCount=0; + int nVarID=-1; + + int nTmpFormat=NCDF_FORMAT_NONE; + int *panBandDimPos=NULL; // X, Y, Z postion in array + int *panBandZLev=NULL; + int *paDimIds=NULL; + size_t xdim, ydim; + char szTemp[NC_MAX_NAME]; + + CPLString osSubdatasetName; + int bTreatAsSubdataset; + + char **papszIgnoreVars = NULL; + char *pszTemp = NULL; + int nIgnoredVars = 0; + + char szDimName[NC_MAX_NAME]; + char szExtraDimNames[NC_MAX_NAME]; + char szExtraDimDef[NC_MAX_NAME]; + nc_type nType=NC_NAT; + +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "\n=====\nOpen(), filename=[%s]", poOpenInfo->pszFilename ); +#endif + +/* -------------------------------------------------------------------- */ +/* Does this appear to be a netcdf file? */ +/* -------------------------------------------------------------------- */ + if( ! EQUALN(poOpenInfo->pszFilename,"NETCDF:",7) ) { + nTmpFormat = IdentifyFormat( poOpenInfo ); +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "identified format %d", nTmpFormat ); +#endif + /* Note: not calling Identify() directly, because we want the file type */ + /* Only support NCDF_FORMAT* formats */ + if( ! ( NCDF_FORMAT_NC == nTmpFormat || + NCDF_FORMAT_NC2 == nTmpFormat || + NCDF_FORMAT_NC4 == nTmpFormat || + NCDF_FORMAT_NC4C == nTmpFormat ) ) + return NULL; + } + + CPLMutexHolderD(&hNCMutex); + + netCDFDataset *poDS; + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + poDS = new netCDFDataset(); + CPLAcquireMutex(hNCMutex, 1000.0); + + poDS->SetDescription( poOpenInfo->pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Check if filename start with NETCDF: tag */ +/* -------------------------------------------------------------------- */ + if( EQUALN( poOpenInfo->pszFilename,"NETCDF:",7) ) + { + char **papszName = + CSLTokenizeString2( poOpenInfo->pszFilename, + ":", CSLT_HONOURSTRINGS|CSLT_PRESERVEESCAPES ); + + /* -------------------------------------------------------------------- */ + /* Check for drive name in windows NETCDF:"D:\... */ + /* -------------------------------------------------------------------- */ + if ( CSLCount(papszName) == 4 && + strlen(papszName[1]) == 1 && + (papszName[2][0] == '/' || papszName[2][0] == '\\') ) + { + poDS->osFilename = papszName[1]; + poDS->osFilename += ':'; + poDS->osFilename += papszName[2]; + osSubdatasetName = papszName[3]; + bTreatAsSubdataset = TRUE; + CSLDestroy( papszName ); + } + else if( CSLCount(papszName) == 3 ) + { + poDS->osFilename = papszName[1]; + osSubdatasetName = papszName[2]; + bTreatAsSubdataset = TRUE; + CSLDestroy( papszName ); + } + else if( CSLCount(papszName) == 2 ) + { + poDS->osFilename = papszName[1]; + osSubdatasetName = ""; + bTreatAsSubdataset = FALSE; + CSLDestroy( papszName ); + } + else + { + CSLDestroy( papszName ); + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hNCMutex, 1000.0); + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to parse NETCDF: prefix string into expected 2, 3 or 4 fields." ); + return NULL; + } + /* Identify Format from real file, with bCheckExt=FALSE */ + GDALOpenInfo* poOpenInfo2 = new GDALOpenInfo(poDS->osFilename.c_str(), GA_ReadOnly ); + poDS->nFormat = IdentifyFormat( poOpenInfo2, FALSE ); + delete poOpenInfo2; + if( NCDF_FORMAT_NONE == poDS->nFormat || + NCDF_FORMAT_UNKNOWN == poDS->nFormat ) { + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hNCMutex, 1000.0); + return NULL; + } + } + else + { + poDS->osFilename = poOpenInfo->pszFilename; + bTreatAsSubdataset = FALSE; + poDS->nFormat = nTmpFormat; + } + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "calling nc_open( %s )", poDS->osFilename.c_str() ); +#endif + if( nc_open( poDS->osFilename, NC_NOWRITE, &cdfid ) != NC_NOERR ) { +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "error opening" ); +#endif + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hNCMutex, 1000.0); + return NULL; + } +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "got cdfid=%d\n", cdfid ); +#endif + +/* -------------------------------------------------------------------- */ +/* Is this a real netCDF file? */ +/* -------------------------------------------------------------------- */ + status = nc_inq(cdfid, &ndims, &nvars, &ngatts, &unlimdimid); + if( status != NC_NOERR ) { + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hNCMutex, 1000.0); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Get file type from netcdf */ +/* -------------------------------------------------------------------- */ + status = nc_inq_format (cdfid, &nTmpFormat); + if ( status != NC_NOERR ) { + NCDF_ERR(status); + } + else { + CPLDebug( "GDAL_netCDF", + "driver detected file type=%d, libnetcdf detected type=%d", + poDS->nFormat, nTmpFormat ); + if ( nTmpFormat != poDS->nFormat ) { + /* warn if file detection conflicts with that from libnetcdf */ + /* except for NC4C, which we have no way of detecting initially */ + if ( nTmpFormat != NCDF_FORMAT_NC4C ) { + CPLError( CE_Warning, CPLE_AppDefined, + "NetCDF driver detected file type=%d, but libnetcdf detected type=%d", + poDS->nFormat, nTmpFormat ); + } + CPLDebug( "GDAL_netCDF", "seting file type to %d, was %d", + nTmpFormat, poDS->nFormat ); + poDS->nFormat = nTmpFormat; + } + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The NETCDF driver does not support update access to existing" + " datasets.\n" ); + nc_close( cdfid ); + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hNCMutex, 1000.0); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Does the request variable exist? */ +/* -------------------------------------------------------------------- */ + if( bTreatAsSubdataset ) + { + status = nc_inq_varid( cdfid, osSubdatasetName, &var); + if( status != NC_NOERR ) { + CPLError( CE_Warning, CPLE_AppDefined, + "%s is a netCDF file, but %s is not a variable.", + poOpenInfo->pszFilename, + osSubdatasetName.c_str() ); + + nc_close( cdfid ); + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hNCMutex, 1000.0); + return NULL; + } + } + + if( nc_inq_ndims( cdfid, &dim_count ) != NC_NOERR || dim_count < 2 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "%s is a netCDF file, but not in GMT configuration.", + poOpenInfo->pszFilename ); + + nc_close( cdfid ); + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hNCMutex, 1000.0); + return NULL; + } + + CPLDebug( "GDAL_netCDF", "dim_count = %d", dim_count ); + + szConventions[0] = '\0'; + if( (status = nc_get_att_text( cdfid, NC_GLOBAL, "Conventions", + szConventions )) != NC_NOERR ) { + CPLError( CE_Warning, CPLE_AppDefined, + "No UNIDATA NC_GLOBAL:Conventions attribute"); + /* note that 'Conventions' is always capital 'C' in CF spec*/ + } + + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + if ( nc_inq_nvars ( cdfid, &var_count) != NC_NOERR ) + { + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hNCMutex, 1000.0); + return NULL; + } + + CPLDebug( "GDAL_netCDF", "var_count = %d", var_count ); + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* Create Netcdf Subdataset if filename as NETCDF tag */ +/* -------------------------------------------------------------------- */ + poDS->cdfid = cdfid; + + poDS->ReadAttributes( cdfid, NC_GLOBAL ); + +/* -------------------------------------------------------------------- */ +/* Identify variables that we should ignore as Raster Bands. */ +/* Variables that are identified in other variable's "coordinate" and */ +/* "bounds" attribute should not be treated as Raster Bands. */ +/* See CF sections 5.2, 5.6 and 7.1 */ +/* -------------------------------------------------------------------- */ + for ( j = 0; j < nvars; j++ ) { + char **papszTokens = NULL; + if ( NCDFGetAttr( cdfid, j, "coordinates", &pszTemp ) == CE_None ) { + papszTokens = CSLTokenizeString2( pszTemp, " ", 0 ); + for ( i=0; i= 2 ) { + nVarID=j; + nCount++; + } + } + + if ( papszIgnoreVars ) + CSLDestroy( papszIgnoreVars ); + +/* -------------------------------------------------------------------- */ +/* We have more than one variable with 2 dimensions in the */ +/* file, then treat this as a subdataset container dataset. */ +/* -------------------------------------------------------------------- */ + if( (nCount > 1) && !bTreatAsSubdataset ) + { + poDS->CreateSubDatasetList(); + poDS->SetMetadata( poDS->papszMetadata ); + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + poDS->TryLoadXML(); + CPLAcquireMutex(hNCMutex, 1000.0); + return( poDS ); + } + +/* -------------------------------------------------------------------- */ +/* If we are not treating things as a subdataset, then capture */ +/* the name of the single available variable as the subdataset. */ +/* -------------------------------------------------------------------- */ + if( !bTreatAsSubdataset ) // nCount must be 1! + { + char szVarName[NC_MAX_NAME]; + szVarName[0] = '\0'; + nc_inq_varname( cdfid, nVarID, szVarName); + osSubdatasetName = szVarName; + } + +/* -------------------------------------------------------------------- */ +/* We have ignored at least one variable, so we should report them */ +/* as subdatasets for reference. */ +/* -------------------------------------------------------------------- */ + if( (nIgnoredVars > 0) && !bTreatAsSubdataset ) + { + CPLDebug( "GDAL_netCDF", + "As %d variables were ignored, creating subdataset list " + "for reference. Variable #%d [%s] is the main variable", + nIgnoredVars, nVarID, osSubdatasetName.c_str() ); + poDS->CreateSubDatasetList(); + } + +/* -------------------------------------------------------------------- */ +/* Open the NETCDF subdataset NETCDF:"filename":subdataset */ +/* -------------------------------------------------------------------- */ + var=-1; + nc_inq_varid( cdfid, osSubdatasetName, &var); + nd = 0; + nc_inq_varndims ( cdfid, var, &nd ); + + paDimIds = (int *)CPLCalloc(nd, sizeof( int ) ); + panBandDimPos = ( int * ) CPLCalloc( nd, sizeof( int ) ); + + nc_inq_vardimid( cdfid, var, paDimIds ); + +/* -------------------------------------------------------------------- */ +/* Check if somebody tried to pass a variable with less than 2D */ +/* -------------------------------------------------------------------- */ + if ( nd < 2 ) { + CPLError( CE_Warning, CPLE_AppDefined, + "Variable has %d dimension(s) - not supported.", nd ); + CPLFree( paDimIds ); + CPLFree( panBandDimPos ); + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hNCMutex, 1000.0); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* CF-1 Convention */ +/* dimensions to appear in the relative order T, then Z, then Y, */ +/* then X to the file. All other dimensions should, whenever */ +/* possible, be placed to the left of the spatiotemporal */ +/* dimensions. */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +/* Verify that dimensions are in the {T,Z,Y,X} or {T,Z,Y,X} order */ +/* Ideally we should detect for other ordering and act accordingly */ +/* Only done if file has Conventions=CF-* and only prints warning */ +/* To disable set GDAL_NETCDF_VERIFY_DIMS=NO and to use only */ +/* attributes (not varnames) set GDAL_NETCDF_VERIFY_DIMS=STRICT */ +/* -------------------------------------------------------------------- */ + + int bCheckDims = FALSE; + bCheckDims = + ( CSLTestBoolean( CPLGetConfigOption( "GDAL_NETCDF_VERIFY_DIMS", "YES" ) ) + != FALSE ) && EQUALN( szConventions, "CF", 2 ); + + if ( bCheckDims ) { + char szDimName1[NC_MAX_NAME], szDimName2[NC_MAX_NAME], + szDimName3[NC_MAX_NAME], szDimName4[NC_MAX_NAME]; + szDimName1[0]='\0'; + szDimName2[0]='\0'; + szDimName3[0]='\0'; + szDimName4[0]='\0'; + nc_inq_dimname( cdfid, paDimIds[nd-1], szDimName1 ); + nc_inq_dimname( cdfid, paDimIds[nd-2], szDimName2 ); + if ( NCDFIsVarLongitude( cdfid, -1, szDimName1 )==FALSE && + NCDFIsVarProjectionX( cdfid, -1, szDimName1 )==FALSE ) { + CPLError( CE_Warning, CPLE_AppDefined, + "dimension #%d (%s) is not a Longitude/X dimension.", + nd-1, szDimName1 ); + } + if ( NCDFIsVarLatitude( cdfid, -1, szDimName2 )==FALSE && + NCDFIsVarProjectionY( cdfid, -1, szDimName2 )==FALSE ) { + CPLError( CE_Warning, CPLE_AppDefined, + "dimension #%d (%s) is not a Latitude/Y dimension.", + nd-2, szDimName2 ); + } + if ( nd >= 3 ) { + nc_inq_dimname( cdfid, paDimIds[nd-3], szDimName3 ); + if ( nd >= 4 ) { + nc_inq_dimname( cdfid, paDimIds[nd-4], szDimName4 ); + if ( NCDFIsVarVerticalCoord( cdfid, -1, szDimName3 )==FALSE ) { + CPLError( CE_Warning, CPLE_AppDefined, + "dimension #%d (%s) is not a Time dimension.", + nd-3, szDimName3 ); + } + if ( NCDFIsVarTimeCoord( cdfid, -1, szDimName4 )==FALSE ) { + CPLError( CE_Warning, CPLE_AppDefined, + "dimension #%d (%s) is not a Time dimension.", + nd-4, szDimName4 ); + } + } + else { + if ( NCDFIsVarVerticalCoord( cdfid, -1, szDimName3 )==FALSE && + NCDFIsVarTimeCoord( cdfid, -1, szDimName3 )==FALSE ) { + CPLError( CE_Warning, CPLE_AppDefined, + "dimension #%d (%s) is not a Time or Vertical dimension.", + nd-3, szDimName3 ); + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Get X dimensions information */ +/* -------------------------------------------------------------------- */ + poDS->nXDimID = paDimIds[nd-1]; + nc_inq_dimlen ( cdfid, poDS->nXDimID, &xdim ); + poDS->nRasterXSize = xdim; + +/* -------------------------------------------------------------------- */ +/* Get Y dimension information */ +/* -------------------------------------------------------------------- */ + poDS->nYDimID = paDimIds[nd-2]; + nc_inq_dimlen ( cdfid, poDS->nYDimID, &ydim ); + poDS->nRasterYSize = ydim; + + + for( j=0,k=0; j < nd; j++ ){ + if( paDimIds[j] == poDS->nXDimID ){ + panBandDimPos[0] = j; // Save Position of XDim + k++; + } + if( paDimIds[j] == poDS->nYDimID ){ + panBandDimPos[1] = j; // Save Position of YDim + k++; + } + } +/* -------------------------------------------------------------------- */ +/* X and Y Dimension Ids were not found! */ +/* -------------------------------------------------------------------- */ + if( k != 2 ) { + CPLFree( paDimIds ); + CPLFree( panBandDimPos ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read Metadata for this variable */ +/* -------------------------------------------------------------------- */ +/* should disable as is also done at band level, except driver needs the + variables as metadata (e.g. projection) */ + poDS->ReadAttributes( cdfid, var ); + +/* -------------------------------------------------------------------- */ +/* Read Metadata for each dimension */ +/* -------------------------------------------------------------------- */ + + for( j=0; j < dim_count; j++ ){ + nc_inq_dimname( cdfid, j, szTemp ); + poDS->papszDimName.AddString( szTemp ); + status = nc_inq_varid( cdfid, poDS->papszDimName[j], &nDimID ); + if( status == NC_NOERR ) { + poDS->ReadAttributes( cdfid, nDimID ); + } + } + +/* -------------------------------------------------------------------- */ +/* Set projection info */ +/* -------------------------------------------------------------------- */ + poDS->SetProjectionFromVar( var ); + + /* override bottom-up with GDAL_NETCDF_BOTTOMUP config option */ + const char *pszValue = CPLGetConfigOption( "GDAL_NETCDF_BOTTOMUP", NULL ); + if ( pszValue ) { + poDS->bBottomUp = CSLTestBoolean( pszValue ) != FALSE; + CPLDebug( "GDAL_netCDF", + "set bBottomUp=%d because GDAL_NETCDF_BOTTOMUP=%s", + poDS->bBottomUp, pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Save non-spatial dimension info */ +/* -------------------------------------------------------------------- */ + + nTotLevCount = 1; + if ( nd > 2 ) { + nDim=2; + panBandZLev = (int *)CPLCalloc( nd-2, sizeof( int ) ); + + strcpy( szExtraDimNames, (char*)"{"); + + for( j=0; j < nd; j++ ){ + if( ( paDimIds[j] != poDS->nXDimID ) && + ( paDimIds[j] != poDS->nYDimID ) ){ + nc_inq_dimlen ( cdfid, paDimIds[j], &lev_count ); + nTotLevCount *= lev_count; + panBandZLev[ nDim-2 ] = lev_count; + panBandDimPos[ nDim++ ] = j; //Save Position of ZDim + //Save non-spatial dimension names + if ( nc_inq_dimname( cdfid, paDimIds[j], szDimName ) + == NC_NOERR ) { + strcat( szExtraDimNames, szDimName ); + if ( j < nd-3 ) { + strcat( szExtraDimNames, (char *)"," ); + } + nc_inq_varid( cdfid, szDimName, &nVarID ); + nc_inq_vartype( cdfid, nVarID, &nType ); + sprintf( szExtraDimDef, "{%ld,%d}", (long)lev_count, nType ); + sprintf( szTemp, "NETCDF_DIM_%s_DEF", szDimName ); + poDS->papszMetadata = CSLSetNameValue( poDS->papszMetadata, + szTemp, szExtraDimDef ); + if ( NCDFGet1DVar( cdfid, nVarID, &pszTemp ) == CE_None ) { + sprintf( szTemp, "NETCDF_DIM_%s_VALUES", szDimName ); + poDS->papszMetadata = CSLSetNameValue( poDS->papszMetadata, + szTemp, pszTemp ); + CPLFree( pszTemp ); + } + } + } + } + strcat( szExtraDimNames, (char *)"}" ); + poDS->papszMetadata = CSLSetNameValue( poDS->papszMetadata, + "NETCDF_DIM_EXTRA", szExtraDimNames ); + } + i=0; + +/* -------------------------------------------------------------------- */ +/* Store Metadata */ +/* -------------------------------------------------------------------- */ + poDS->SetMetadata( poDS->papszMetadata ); + +/* -------------------------------------------------------------------- */ +/* Create bands */ +/* -------------------------------------------------------------------- */ + for ( unsigned int lev = 0; lev < nTotLevCount ; lev++ ) { + netCDFRasterBand *poBand = + new netCDFRasterBand(poDS, var, nDim, lev, + panBandZLev, panBandDimPos, + paDimIds, i+1 ); + poDS->SetBand( i+1, poBand ); + i++; + } + + CPLFree( paDimIds ); + CPLFree( panBandDimPos ); + if ( panBandZLev ) + CPLFree( panBandZLev ); + + poDS->nBands = i; + + // Handle angular geographic coordinates here + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + if( bTreatAsSubdataset ) + { + poDS->SetPhysicalFilename( poDS->osFilename ); + poDS->SetSubdatasetName( osSubdatasetName ); + } + + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + poDS->TryLoadXML(); + + if( bTreatAsSubdataset ) + poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" ); + else + poDS->oOvManager.Initialize( poDS, poDS->osFilename ); + + CPLAcquireMutex(hNCMutex, 1000.0); + + return( poDS ); +} + + +/************************************************************************/ +/* CopyMetadata() */ +/* */ +/* Create a copy of metadata for NC_GLOBAL or a variable */ +/************************************************************************/ + +void CopyMetadata( void *poDS, int fpImage, int CDFVarID, + const char *pszPrefix, int bIsBand ) { + + char **papszMetadata=NULL; + char **papszFieldData=NULL; + const char *pszField; + char szMetaName[ NCDF_MAX_STR_LEN ]; + size_t nAttrValueSize = NCDF_MAX_STR_LEN; + char *pszMetaValue = (char *) CPLMalloc(sizeof(char) * nAttrValueSize); + *pszMetaValue = '\0'; + char szTemp[ NCDF_MAX_STR_LEN ]; + int nItems; + + /* Remove the following band meta but set them later from band data */ + const char *papszIgnoreBand[] = { CF_ADD_OFFSET, CF_SCALE_FACTOR, + "valid_range", "_Unsigned", + _FillValue, "coordinates", + NULL }; + const char *papszIgnoreGlobal[] = { "NETCDF_DIM_EXTRA", NULL }; + + if( CDFVarID == NC_GLOBAL ) { + papszMetadata = GDALGetMetadata( (GDALDataset *) poDS,""); + } else { + papszMetadata = GDALGetMetadata( (GDALRasterBandH) poDS, NULL ); + } + + nItems = CSLCount( papszMetadata ); + + for(int k=0; k < nItems; k++ ) { + pszField = CSLGetField( papszMetadata, k ); + if ( papszFieldData ) CSLDestroy( papszFieldData ); + papszFieldData = CSLTokenizeString2 (pszField, "=", + CSLT_HONOURSTRINGS ); + if( papszFieldData[1] != NULL ) { + +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "copy metadata [%s]=[%s]", + papszFieldData[ 0 ], papszFieldData[ 1 ] ); +#endif + + strcpy( szMetaName, papszFieldData[ 0 ] ); + NCDFSafeStrcpy(&pszMetaValue, papszFieldData[ 1 ], &nAttrValueSize); + + /* check for items that match pszPrefix if applicable */ + if ( ( pszPrefix != NULL ) && ( !EQUAL( pszPrefix, "" ) ) ) { + /* remove prefix */ + if ( EQUALN( szMetaName, pszPrefix, strlen(pszPrefix) ) ) { + strcpy( szTemp, szMetaName+strlen(pszPrefix) ); + strcpy( szMetaName, szTemp ); + } + /* only copy items that match prefix */ + else + continue; + } + + /* Fix various issues with metadata translation */ + if( CDFVarID == NC_GLOBAL ) { + /* Do not copy items in papszIgnoreGlobal and NETCDF_DIM_* */ + if ( ( CSLFindString( (char **)papszIgnoreGlobal, szMetaName ) != -1 ) || + ( strncmp( szMetaName, "NETCDF_DIM_", 11 ) == 0 ) ) + continue; + /* Remove NC_GLOBAL prefix for netcdf global Metadata */ + else if( strncmp( szMetaName, "NC_GLOBAL#", 10 ) == 0 ) { + strcpy( szTemp, szMetaName+10 ); + strcpy( szMetaName, szTemp ); + } + /* GDAL Metadata renamed as GDAL-[meta] */ + else if ( strstr( szMetaName, "#" ) == NULL ) { + strcpy( szTemp, "GDAL_" ); + strcat( szTemp, szMetaName ); + strcpy( szMetaName, szTemp ); + } + /* Keep time, lev and depth information for safe-keeping */ + /* Time and vertical coordinate handling need improvements */ + /* + else if( strncmp( szMetaName, "time#", 5 ) == 0 ) { + szMetaName[4] = '-'; + } + else if( strncmp( szMetaName, "lev#", 4 ) == 0 ) { + szMetaName[3] = '-'; + } + else if( strncmp( szMetaName, "depth#", 6 ) == 0 ) { + szMetaName[5] = '-'; + } + */ + /* Only copy data without # (previously all data was copied) */ + if ( strstr( szMetaName, "#" ) != NULL ) + continue; + // /* netCDF attributes do not like the '#' character. */ + // for( unsigned int h=0; h < strlen( szMetaName ) -1 ; h++ ) { + // if( szMetaName[h] == '#' ) szMetaName[h] = '-'; + // } + } + else { + /* Do not copy varname, stats, NETCDF_DIM_*, nodata + and items in papszIgnoreBand */ + if ( ( strncmp( szMetaName, "NETCDF_VARNAME", 14) == 0 ) || + ( strncmp( szMetaName, "STATISTICS_", 11) == 0 ) || + ( strncmp( szMetaName, "NETCDF_DIM_", 11 ) == 0 ) || + ( strncmp( szMetaName, "missing_value", 13 ) == 0 ) || + ( strncmp( szMetaName, "_FillValue", 10 ) == 0 ) || + ( CSLFindString( (char **)papszIgnoreBand, szMetaName ) != -1 ) ) + continue; + } + + +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "copy name=[%s] value=[%s]", + szMetaName, pszMetaValue ); +#endif + if ( NCDFPutAttr( fpImage, CDFVarID,szMetaName, + pszMetaValue ) != CE_None ) + CPLDebug( "GDAL_netCDF", "NCDFPutAttr(%d, %d, %s, %s) failed", + fpImage, CDFVarID,szMetaName, pszMetaValue ); + } + } + + if ( papszFieldData ) CSLDestroy( papszFieldData ); + CPLFree( pszMetaValue ); + + /* Set add_offset and scale_factor here if present */ + if( ( CDFVarID != NC_GLOBAL ) && ( bIsBand ) ) { + + int bGotAddOffset, bGotScale; + GDALRasterBandH poRB = (GDALRasterBandH) poDS; + double dfAddOffset = GDALGetRasterOffset( poRB , &bGotAddOffset ); + double dfScale = GDALGetRasterScale( poRB, &bGotScale ); + + if ( bGotAddOffset && dfAddOffset != 0.0 && bGotScale && dfScale != 1.0 ) { + GDALSetRasterOffset( poRB, dfAddOffset ); + GDALSetRasterScale( poRB, dfScale ); + } + + } + +} + + +/************************************************************************/ +/* CreateLL() */ +/* */ +/* Shared functionality between netCDFDataset::Create() and */ +/* netCDF::CreateCopy() for creating netcdf file based on a set of */ +/* options and a configuration. */ +/************************************************************************/ + +netCDFDataset * +netCDFDataset::CreateLL( const char * pszFilename, + int nXSize, int nYSize, CPL_UNUSED int nBands, + char ** papszOptions ) +{ + int status = NC_NOERR; + netCDFDataset *poDS; + + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + poDS = new netCDFDataset(); + CPLAcquireMutex(hNCMutex, 1000.0); + + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->eAccess = GA_Update; + poDS->osFilename = pszFilename; + + /* from gtiff driver, is this ok? */ + /* + poDS->nBlockXSize = nXSize; + poDS->nBlockYSize = 1; + poDS->nBlocksPerBand = + ((nYSize + poDS->nBlockYSize - 1) / poDS->nBlockYSize) + * ((nXSize + poDS->nBlockXSize - 1) / poDS->nBlockXSize); + */ + + /* process options */ + poDS->papszCreationOptions = CSLDuplicate( papszOptions ); + poDS->ProcessCreationOptions( ); + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + status = nc_create( pszFilename, poDS->nCreateMode, &(poDS->cdfid) ); + + /* put into define mode */ + poDS->SetDefineMode(TRUE); + + if( status != NC_NOERR ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create netCDF file %s (Error code %d): %s .\n", + pszFilename, status, nc_strerror(status) ); + CPLReleaseMutex(hNCMutex); // Release mutex otherwise we'll deadlock with GDALDataset own mutex + delete poDS; + CPLAcquireMutex(hNCMutex, 1000.0); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Define dimensions */ +/* -------------------------------------------------------------------- */ + poDS->papszDimName.AddString( NCDF_DIMNAME_X ); + status = nc_def_dim( poDS->cdfid, NCDF_DIMNAME_X, nXSize, + &(poDS->nXDimID) ); + NCDF_ERR(status); + CPLDebug( "GDAL_netCDF", "status nc_def_dim( %d, %s, %d, -) got id %d", + poDS->cdfid, NCDF_DIMNAME_X, nXSize, poDS->nXDimID ); + + poDS->papszDimName.AddString( NCDF_DIMNAME_Y ); + status = nc_def_dim( poDS->cdfid, NCDF_DIMNAME_Y, nYSize, + &(poDS->nYDimID) ); + NCDF_ERR(status); + CPLDebug( "GDAL_netCDF", "status nc_def_dim( %d, %s, %d, -) got id %d", + poDS->cdfid, NCDF_DIMNAME_Y, nYSize, poDS->nYDimID ); + + return poDS; + +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset * +netCDFDataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ) +{ + netCDFDataset *poDS; + + CPLDebug( "GDAL_netCDF", + "\n=====\nnetCDFDataset::Create( %s, ... )\n", + pszFilename ); + + CPLMutexHolderD(&hNCMutex); + + poDS = netCDFDataset::CreateLL( pszFilename, + nXSize, nYSize, nBands, + papszOptions ); + + if ( ! poDS ) + return NULL; + + /* should we write signed or unsigned byte? */ + /* TODO should this only be done in Create() */ + poDS->bSignedData = TRUE; + const char *pszValue = + CSLFetchNameValue( papszOptions, "PIXELTYPE" ); + if( pszValue == NULL ) + pszValue = ""; + if( eType == GDT_Byte && ( ! EQUAL(pszValue,"SIGNEDBYTE") ) ) + poDS->bSignedData = FALSE; + +/* -------------------------------------------------------------------- */ +/* Add Conventions, GDAL info and history */ +/* -------------------------------------------------------------------- */ + NCDFAddGDALHistory( poDS->cdfid, pszFilename, "", "Create" ); + +/* -------------------------------------------------------------------- */ +/* Define bands */ +/* -------------------------------------------------------------------- */ + for( int iBand = 1; iBand <= nBands; iBand++ ) + { + poDS->SetBand( iBand, new netCDFRasterBand( poDS, eType, iBand, + poDS->bSignedData ) ); + } + + CPLDebug( "GDAL_netCDF", + "netCDFDataset::Create( %s, ... ) done", + pszFilename ); +/* -------------------------------------------------------------------- */ +/* Return same dataset */ +/* -------------------------------------------------------------------- */ + return( poDS ); + +} + + +template +CPLErr NCDFCopyBand( GDALRasterBand *poSrcBand, GDALRasterBand *poDstBand, + int nXSize, int nYSize, + GDALProgressFunc pfnProgress, void * pProgressData ) +{ + GDALDataType eDT = poSrcBand->GetRasterDataType(); + CPLErr eErr = CE_None; + T *patScanline = (T *) CPLMalloc( nXSize * sizeof(T) ); + + for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ ) { + eErr = poSrcBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + patScanline, nXSize, 1, eDT, + 0,0, NULL); + if ( eErr != CE_None ) + CPLDebug( "GDAL_netCDF", + "NCDFCopyBand(), poSrcBand->RasterIO() returned error code %d", + eErr ); + else { + eErr = poDstBand->RasterIO( GF_Write, 0, iLine, nXSize, 1, + patScanline, nXSize, 1, eDT, + 0,0, NULL); + if ( eErr != CE_None ) + CPLDebug( "GDAL_netCDF", + "NCDFCopyBand(), poDstBand->RasterIO() returned error code %d", + eErr ); + } + + if ( ( nYSize>10 ) && ( iLine % (nYSize/10) == 1 ) ) { + if( !pfnProgress( 1.0*iLine/nYSize , NULL, pProgressData ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + } + + CPLFree( patScanline ); + + pfnProgress( 1.0, NULL, pProgressData ); + + return eErr; +} + + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset* +netCDFDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + CPL_UNUSED int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) +{ + netCDFDataset *poDS; + void *pScaledProgress; + GDALDataType eDT; + CPLErr eErr = CE_None; + int nBands, nXSize, nYSize; + double adfGeoTransform[6]; + const char *pszWKT; + int iBand; + int status = NC_NOERR; + + int nDim = 2; + int *panBandDimPos=NULL; // X, Y, Z postion in array + int *panBandZLev=NULL; + int *panDimIds=NULL; + int *panDimVarIds=NULL; + nc_type nVarType; + char szTemp[ NCDF_MAX_STR_LEN ]; + double dfTemp,dfTemp2; + netCDFRasterBand *poBand = NULL; + GDALRasterBand *poSrcBand = NULL; + GDALRasterBand *poDstBand = NULL; + int nBandID = -1; + + CPLMutexHolderD(&hNCMutex); + + CPLDebug( "GDAL_netCDF", + "\n=====\nnetCDFDataset::CreateCopy( %s, ... )\n", + pszFilename ); + + nBands = poSrcDS->GetRasterCount(); + nXSize = poSrcDS->GetRasterXSize(); + nYSize = poSrcDS->GetRasterYSize(); + pszWKT = poSrcDS->GetProjectionRef(); + +/* -------------------------------------------------------------------- */ +/* Check input bands for errors */ +/* -------------------------------------------------------------------- */ + + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "NetCDF driver does not support source dataset with zero band.\n"); + return NULL; + } + + for( iBand=1; iBand <= nBands; iBand++ ) + { + poSrcBand = poSrcDS->GetRasterBand( iBand ); + eDT = poSrcBand->GetRasterDataType(); + if (eDT == GDT_Unknown || GDALDataTypeIsComplex(eDT)) + { + CPLError( CE_Failure, CPLE_NotSupported, + "NetCDF driver does not support source dataset with band of complex type."); + return NULL; + } + } + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + + /* same as in Create() */ + poDS = netCDFDataset::CreateLL( pszFilename, + nXSize, nYSize, nBands, + papszOptions ); + if ( ! poDS ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Copy global metadata */ +/* Add Conventions, GDAL info and history */ +/* -------------------------------------------------------------------- */ + CopyMetadata((void *) poSrcDS, poDS->cdfid, NC_GLOBAL, NULL, FALSE ); + NCDFAddGDALHistory( poDS->cdfid, pszFilename, + poSrcDS->GetMetadataItem("NC_GLOBAL#history",""), + "CreateCopy" ); + + pfnProgress( 0.1, NULL, pProgressData ); + + +/* -------------------------------------------------------------------- */ +/* Check for extra dimensions */ +/* -------------------------------------------------------------------- */ + char **papszExtraDimNames = + NCDFTokenizeArray( poSrcDS->GetMetadataItem("NETCDF_DIM_EXTRA","") ); + char **papszExtraDimValues = NULL; + size_t nDimSize = -1; + size_t nDimSizeTot = 1; + if ( papszExtraDimNames != NULL && ( CSLCount( papszExtraDimNames )> 0 ) ) { + // first make sure dimensions lengths compatible with band count + // for ( int i=0; i=0; i-- ) { + sprintf( szTemp, "NETCDF_DIM_%s_DEF", papszExtraDimNames[i] ); + papszExtraDimValues = NCDFTokenizeArray( poSrcDS->GetMetadataItem(szTemp,"") ); + nDimSize = atol( papszExtraDimValues[0] ); + CSLDestroy( papszExtraDimValues ); + nDimSizeTot *= nDimSize; + } + if ( nDimSizeTot == (size_t)nBands ) { + nDim = 2 + CSLCount( papszExtraDimNames ); + } + else { + // if nBands != #bands computed raise a warning + // just issue a debug message, because it was probably intentional + CPLDebug( "GDAL_netCDF", + "Warning: Number of bands (%d) is not compatible with dimensions " + "(total=%ld names=%s)", nBands, (long)nDimSizeTot, + poSrcDS->GetMetadataItem("NETCDF_DIM_EXTRA","") ); + CSLDestroy( papszExtraDimNames ); + papszExtraDimNames = NULL; + } + } + + panDimIds = (int *)CPLCalloc( nDim, sizeof( int ) ); + panBandDimPos = (int *) CPLCalloc( nDim, sizeof( int ) ); + + if ( nDim > 2 ) { + panBandZLev = (int *)CPLCalloc( nDim-2, sizeof( int ) ); + panDimVarIds = (int *)CPLCalloc( nDim-2, sizeof( int ) ); + + /* define all dims */ + for ( int i=CSLCount( papszExtraDimNames )-1; i>=0; i-- ) { + poDS->papszDimName.AddString( papszExtraDimNames[i] ); + sprintf( szTemp, "NETCDF_DIM_%s_DEF", papszExtraDimNames[i] ); + papszExtraDimValues = NCDFTokenizeArray( poSrcDS->GetMetadataItem(szTemp,"") ); + nDimSize = atol( papszExtraDimValues[0] ); + /* nc_type is an enum in netcdf-3, needs casting */ + nVarType = (nc_type) atol( papszExtraDimValues[1] ); + CSLDestroy( papszExtraDimValues ); + panBandZLev[ i ] = nDimSize; + panBandDimPos[ i+2 ] = i; //Save Position of ZDim + + /* define dim */ + status = nc_def_dim( poDS->cdfid, papszExtraDimNames[i], nDimSize, + &(panDimIds[i]) ); + NCDF_ERR(status); + + /* define dim var */ + int anDim[1]; + anDim[0] = panDimIds[i]; + status = nc_def_var( poDS->cdfid, papszExtraDimNames[i], + nVarType, 1, anDim, + &(panDimVarIds[i]) ); + NCDF_ERR(status); + + /* add dim metadata, using global var# items */ + sprintf( szTemp, "%s#", papszExtraDimNames[i] ); + CopyMetadata((void *) poSrcDS, poDS->cdfid, panDimVarIds[i], szTemp, FALSE ); + } + } + +/* -------------------------------------------------------------------- */ +/* Copy GeoTransform and Projection */ +/* -------------------------------------------------------------------- */ + /* copy geolocation info */ + if ( poSrcDS->GetMetadata("GEOLOCATION") != NULL ) + poDS->SetMetadata( poSrcDS->GetMetadata("GEOLOCATION"), "GEOLOCATION" ); + + /* copy geotransform */ + int bGotGeoTransform = FALSE; + eErr = poSrcDS->GetGeoTransform( adfGeoTransform ); + if ( eErr == CE_None ) { + poDS->SetGeoTransform( adfGeoTransform ); + /* disable AddProjectionVars() from being called */ + bGotGeoTransform = TRUE; + poDS->bSetGeoTransform = FALSE; + } + + /* copy projection */ + if ( pszWKT ) { + poDS->SetProjection( pszWKT ); + /* now we can call AddProjectionVars() directly */ + poDS->bSetGeoTransform = bGotGeoTransform; + pScaledProgress = GDALCreateScaledProgress( 0.20, 0.50, pfnProgress, + pProgressData ); + poDS->AddProjectionVars( GDALScaledProgress, pScaledProgress ); + /* save X,Y dim positions */ + panDimIds[nDim-1] = poDS->nXDimID; + panBandDimPos[0] = nDim-1; + panDimIds[nDim-2] = poDS->nYDimID; + panBandDimPos[1] = nDim-2; + GDALDestroyScaledProgress( pScaledProgress ); + + } + + /* write extra dim values - after projection for optimization */ + if ( nDim > 2 ) { + /* make sure we are in data mode */ + ( ( netCDFDataset * ) poDS )->SetDefineMode( FALSE ); + for ( int i=CSLCount( papszExtraDimNames )-1; i>=0; i-- ) { + sprintf( szTemp, "NETCDF_DIM_%s_VALUES", papszExtraDimNames[i] ); + if ( poSrcDS->GetMetadataItem( szTemp ) != NULL ) { + NCDFPut1DVar( poDS->cdfid, panDimVarIds[i], + poSrcDS->GetMetadataItem( szTemp ) ); + } + } + } + + pfnProgress( 0.25, NULL, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Define Bands */ +/* -------------------------------------------------------------------- */ + + for( iBand=1; iBand <= nBands; iBand++ ) { + CPLDebug( "GDAL_netCDF", "creating band # %d/%d nDim = %d", + iBand, nBands, nDim ); + + char szBandName[ NC_MAX_NAME ]; + char szLongName[ NC_MAX_NAME ]; + const char *tmpMetadata; + int bSignedData = TRUE; + int bNoDataSet; + double dfNoDataValue; + + poSrcBand = poSrcDS->GetRasterBand( iBand ); + eDT = poSrcBand->GetRasterDataType(); + + /* Get var name from NETCDF_VARNAME */ + tmpMetadata = poSrcBand->GetMetadataItem("NETCDF_VARNAME"); + if( tmpMetadata != NULL) { + if( nBands > 1 && papszExtraDimNames == NULL ) + sprintf(szBandName,"%s%d",tmpMetadata,iBand); + else strcpy( szBandName, tmpMetadata ); + } + else + szBandName[0]='\0'; + + /* Get long_name from #long_name */ + sprintf(szLongName,"%s#%s", + poSrcBand->GetMetadataItem("NETCDF_VARNAME"), + CF_LNG_NAME); + tmpMetadata = poSrcDS->GetMetadataItem(szLongName); + if( tmpMetadata != NULL) + strcpy( szLongName, tmpMetadata); + else + szLongName[0]='\0'; + + if ( eDT == GDT_Byte ) { + /* GDAL defaults to unsigned bytes, but check if metadata says its + signed, as NetCDF can support this for certain formats. */ + bSignedData = FALSE; + tmpMetadata = poSrcBand->GetMetadataItem("PIXELTYPE", + "IMAGE_STRUCTURE"); + if ( tmpMetadata && EQUAL(tmpMetadata,"SIGNEDBYTE") ) + bSignedData = TRUE; + } + + if ( nDim > 2 ) + poBand = new netCDFRasterBand( poDS, eDT, iBand, + bSignedData, + szBandName, szLongName, + nBandID, nDim, iBand-1, + panBandZLev, panBandDimPos, + panDimIds ); + else + poBand = new netCDFRasterBand( poDS, eDT, iBand, + bSignedData, + szBandName, szLongName ); + + poDS->SetBand( iBand, poBand ); + + /* set nodata value, if any */ + // poBand->SetNoDataValue( poSrcBand->GetNoDataValue(0) ); + dfNoDataValue = poSrcBand->GetNoDataValue( &bNoDataSet ); + if ( bNoDataSet ) { + CPLDebug( "GDAL_netCDF", "SetNoDataValue(%f) source", dfNoDataValue ); + poBand->SetNoDataValue( dfNoDataValue ); + } + + /* Copy Metadata for band */ + CopyMetadata( (void *) GDALGetRasterBand( poSrcDS, iBand ), + poDS->cdfid, poBand->nZId ); + + /* if more than 2D pass the first band's netcdf var ID to subsequent bands */ + if ( nDim > 2 ) + nBandID = poBand->nZId; + } + + /* write projection variable to band variable */ + poDS->AddGridMappingRef(); + + pfnProgress( 0.5, NULL, pProgressData ); + + +/* -------------------------------------------------------------------- */ +/* Write Bands */ +/* -------------------------------------------------------------------- */ + /* make sure we are in data mode */ + poDS->SetDefineMode( FALSE ); + + dfTemp = dfTemp2 = 0.5; + + eErr = CE_None; + + for( iBand=1; iBand <= nBands && eErr == CE_None; iBand++ ) { + + dfTemp2 = dfTemp + 0.4/nBands; + pScaledProgress = + GDALCreateScaledProgress( dfTemp, dfTemp2, + pfnProgress, pProgressData ); + dfTemp = dfTemp2; + + CPLDebug( "GDAL_netCDF", "copying band data # %d/%d ", + iBand,nBands ); + + poSrcBand = poSrcDS->GetRasterBand( iBand ); + eDT = poSrcBand->GetRasterDataType(); + + poDstBand = poDS->GetRasterBand( iBand ); + + +/* -------------------------------------------------------------------- */ +/* Copy Band data */ +/* -------------------------------------------------------------------- */ + if( eDT == GDT_Byte ) { + CPLDebug( "GDAL_netCDF", "GByte Band#%d", iBand ); + eErr = NCDFCopyBand( poSrcBand, poDstBand, nXSize, nYSize, + GDALScaledProgress, pScaledProgress ); + } + else if( ( eDT == GDT_UInt16 ) || ( eDT == GDT_Int16 ) ) { + CPLDebug( "GDAL_netCDF", "GInt16 Band#%d", iBand ); + eErr = NCDFCopyBand( poSrcBand, poDstBand, nXSize, nYSize, + GDALScaledProgress, pScaledProgress ); + } + else if( (eDT == GDT_UInt32) || (eDT == GDT_Int32) ) { + CPLDebug( "GDAL_netCDF", "GInt16 Band#%d", iBand ); + eErr = NCDFCopyBand( poSrcBand, poDstBand, nXSize, nYSize, + GDALScaledProgress, pScaledProgress ); + } + else if( eDT == GDT_Float32 ) { + CPLDebug( "GDAL_netCDF", "float Band#%d", iBand); + eErr = NCDFCopyBand( poSrcBand, poDstBand, nXSize, nYSize, + GDALScaledProgress, pScaledProgress ); + } + else if( eDT == GDT_Float64 ) { + CPLDebug( "GDAL_netCDF", "double Band#%d", iBand); + eErr = NCDFCopyBand( poSrcBand, poDstBand, nXSize, nYSize, + GDALScaledProgress, pScaledProgress ); + } + else { + CPLError( CE_Failure, CPLE_NotSupported, + "The NetCDF driver does not support GDAL data type %d", + eDT ); + } + + GDALDestroyScaledProgress( pScaledProgress ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup and close. */ +/* -------------------------------------------------------------------- */ + delete( poDS ); +// CPLFree(pszProj4Defn ); + + if ( panDimIds ) + CPLFree( panDimIds ); + if( panBandDimPos ) + CPLFree( panBandDimPos ); + if ( panBandZLev ) + CPLFree( panBandZLev ); + if( panDimVarIds ) + CPLFree( panDimVarIds ); + if ( papszExtraDimNames ) + CSLDestroy( papszExtraDimNames ); + + if (eErr != CE_None) + return NULL; + + pfnProgress( 0.95, NULL, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Re-open dataset so we can return it. */ +/* -------------------------------------------------------------------- */ + poDS = (netCDFDataset *) GDALOpen( pszFilename, GA_ReadOnly ); + +/* -------------------------------------------------------------------- */ +/* PAM cloning is disabled. See bug #4244. */ +/* -------------------------------------------------------------------- */ + // if( poDS ) + // poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + pfnProgress( 1.0, NULL, pProgressData ); + + return poDS; +} + +/* note: some logic depends on bIsProjected and bIsGeoGraphic */ +/* which may not be known when Create() is called, see AddProjectionVars() */ +void +netCDFDataset::ProcessCreationOptions( ) +{ + const char *pszValue; + + /* File format */ + nFormat = NCDF_FORMAT_NC; + pszValue = CSLFetchNameValue( papszCreationOptions, "FORMAT" ); + if ( pszValue != NULL ) { + if ( EQUAL( pszValue, "NC" ) ) { + nFormat = NCDF_FORMAT_NC; + } +#ifdef NETCDF_HAS_NC2 + else if ( EQUAL( pszValue, "NC2" ) ) { + nFormat = NCDF_FORMAT_NC2; + } +#endif +#ifdef NETCDF_HAS_NC4 + else if ( EQUAL( pszValue, "NC4" ) ) { + nFormat = NCDF_FORMAT_NC4; + } + else if ( EQUAL( pszValue, "NC4C" ) ) { + nFormat = NCDF_FORMAT_NC4C; + } +#endif + else { + CPLError( CE_Failure, CPLE_NotSupported, + "FORMAT=%s in not supported, using the default NC format.", pszValue ); + } + } + + /* compression only available for NC4 */ +#ifdef NETCDF_HAS_NC4 + + /* COMPRESS option */ + pszValue = CSLFetchNameValue( papszCreationOptions, "COMPRESS" ); + if ( pszValue != NULL ) { + if ( EQUAL( pszValue, "NONE" ) ) { + nCompress = NCDF_COMPRESS_NONE; + } + else if ( EQUAL( pszValue, "DEFLATE" ) ) { + nCompress = NCDF_COMPRESS_DEFLATE; + if ( !((nFormat == NCDF_FORMAT_NC4) || (nFormat == NCDF_FORMAT_NC4C)) ) { + CPLError( CE_Warning, CPLE_IllegalArg, + "NOTICE: Format set to NC4C because compression is set to DEFLATE." ); + nFormat = NCDF_FORMAT_NC4C; + } + } + else { + CPLError( CE_Failure, CPLE_NotSupported, + "COMPRESS=%s is not supported.", pszValue ); + } + } + + /* ZLEVEL option */ + pszValue = CSLFetchNameValue( papszCreationOptions, "ZLEVEL" ); + if( pszValue != NULL ) + { + nZLevel = atoi( pszValue ); + if (!(nZLevel >= 1 && nZLevel <= 9)) + { + CPLError( CE_Warning, CPLE_IllegalArg, + "ZLEVEL=%s value not recognised, ignoring.", + pszValue ); + nZLevel = NCDF_DEFLATE_LEVEL; + } + } + + /* CHUNKING option */ + bChunking = CSLFetchBoolean( papszCreationOptions, "CHUNKING", TRUE ); + +#endif + + /* set nCreateMode based on nFormat */ + switch ( nFormat ) { +#ifdef NETCDF_HAS_NC2 + case NCDF_FORMAT_NC2: + nCreateMode = NC_CLOBBER|NC_64BIT_OFFSET; + break; +#endif +#ifdef NETCDF_HAS_NC4 + case NCDF_FORMAT_NC4: + nCreateMode = NC_CLOBBER|NC_NETCDF4; + break; + case NCDF_FORMAT_NC4C: + nCreateMode = NC_CLOBBER|NC_NETCDF4|NC_CLASSIC_MODEL; + break; +#endif + case NCDF_FORMAT_NC: + default: + nCreateMode = NC_CLOBBER; + break; + } + + CPLDebug( "GDAL_netCDF", + "file options: format=%d compress=%d zlevel=%d", + nFormat, nCompress, nZLevel ); + +} + +int netCDFDataset::DefVarDeflate( +#ifdef NETCDF_HAS_NC4 + int nVarId, int bChunkingArg +#else + CPL_UNUSED int nVarId, CPL_UNUSED int bChunkingArg +#endif + ) +{ +#ifdef NETCDF_HAS_NC4 + if ( nCompress == NCDF_COMPRESS_DEFLATE ) { + // must set chunk size to avoid huge performace hit (set bChunkingArg=TRUE) + // perhaps another solution it to change the chunk cache? + // http://www.unidata.ucar.edu/software/netcdf/docs/netcdf.html#Chunk-Cache + // TODO make sure this is ok + CPLDebug( "GDAL_netCDF", + "DefVarDeflate( %d, %d ) nZlevel=%d", + nVarId, bChunkingArg, nZLevel ); + + status = nc_def_var_deflate(cdfid,nVarId,1,1,nZLevel); + NCDF_ERR(status); + + if ( (status == NC_NOERR) && bChunkingArg && bChunking ) { + + // set chunking to be 1 for all dims, except X dim + // size_t chunksize[] = { 1, (size_t)nRasterXSize }; + size_t chunksize[ MAX_NC_DIMS ]; + int nd; + nc_inq_varndims( cdfid, nVarId, &nd ); + for( int i=0; i" +" " +#ifdef NETCDF_HAS_NC4 +" " +" " +" " +" " +" " +" " +" " +"" ); + + +/* -------------------------------------------------------------------- */ +/* Set the driver details. */ +/* -------------------------------------------------------------------- */ + poDriver->SetDescription( "netCDF" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Network Common Data Format" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_netcdf.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "nc" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, + szCreateOptions ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + + /* make driver config and capabilities available */ + poDriver->SetMetadataItem( "NETCDF_VERSION", nc_inq_libvers() ); + poDriver->SetMetadataItem( "NETCDF_CONVENTIONS", NCDF_CONVENTIONS_CF ); +#ifdef NETCDF_HAS_NC2 + poDriver->SetMetadataItem( "NETCDF_HAS_NC2", "YES" ); +#endif +#ifdef NETCDF_HAS_NC4 + poDriver->SetMetadataItem( "NETCDF_HAS_NC4", "YES" ); +#endif +#ifdef NETCDF_HAS_HDF4 + poDriver->SetMetadataItem( "NETCDF_HAS_HDF4", "YES" ); +#endif +#ifdef HAVE_HDF4 + poDriver->SetMetadataItem( "GDAL_HAS_HDF4", "YES" ); +#endif +#ifdef HAVE_HDF5 + poDriver->SetMetadataItem( "GDAL_HAS_HDF5", "YES" ); +#endif + + /* set pfns and register driver */ + poDriver->pfnOpen = netCDFDataset::Open; + poDriver->pfnCreateCopy = netCDFDataset::CreateCopy; + poDriver->pfnCreate = netCDFDataset::Create; + poDriver->pfnIdentify = netCDFDataset::Identify; + poDriver->pfnUnloadDriver = NCDFUnloadDriver; + + GetGDALDriverManager( )->RegisterDriver( poDriver ); + } + +#ifdef NETCDF_PLUGIN + GDALRegister_GMT(); +#endif +} + +/************************************************************************/ +/* New functions */ +/************************************************************************/ + +/* Test for GDAL version string >= target */ +int NCDFIsGDALVersionGTE(const char* pszVersion, int nTarget) +{ + int nVersion = 0; + int nVersions [] = {0,0,0,0}; + char **papszTokens; + + /* Valid strings are "GDAL 1.9dev, released 2011/01/18" and "GDAL 1.8.1 " */ + if ( pszVersion == NULL || EQUAL( pszVersion, "" ) ) + return FALSE; + else if ( ! EQUALN("GDAL ", pszVersion, 5) ) + return FALSE; + /* 2.0dev of 2011/12/29 has been later renamed as 1.10dev */ + else if ( EQUAL("GDAL 2.0dev, released 2011/12/29", pszVersion) ) + return nTarget <= GDAL_COMPUTE_VERSION(1,10,0); + else if ( EQUALN("GDAL 1.9dev", pszVersion,11 ) ) + return nTarget <= 1900; + else if ( EQUALN("GDAL 1.8dev", pszVersion,11 ) ) + return nTarget <= 1800; + + papszTokens = CSLTokenizeString2( pszVersion+5, ".", 0 ); + + for ( int iToken = 0; papszTokens && papszTokens[iToken]; iToken++ ) { + nVersions[iToken] = atoi( papszTokens[iToken] ); + } + if( nVersions[0] > 1 || nVersions[1] >= 10 ) + nVersion = GDAL_COMPUTE_VERSION( nVersions[0], nVersions[1], nVersions[2] ); + else + nVersion = nVersions[0]*1000 + nVersions[1]*100 + + nVersions[2]*10 + nVersions[3]; + + CSLDestroy( papszTokens ); + return nTarget <= nVersion; +} + +/* Add Conventions, GDAL version and history */ +void NCDFAddGDALHistory( int fpImage, + const char * pszFilename, const char *pszOldHist, + const char * pszFunctionName) +{ + char szTemp[NC_MAX_NAME]; + + nc_put_att_text( fpImage, NC_GLOBAL, "Conventions", + strlen(NCDF_CONVENTIONS_CF), + NCDF_CONVENTIONS_CF ); + + const char* pszNCDF_GDAL = GDALVersionInfo("--version"); + nc_put_att_text( fpImage, NC_GLOBAL, "GDAL", + strlen(pszNCDF_GDAL), pszNCDF_GDAL ); + + /* Add history */ +#ifdef GDAL_SET_CMD_LINE_DEFINED_TMP + if ( ! EQUAL(GDALGetCmdLine(), "" ) ) + strcpy( szTemp, GDALGetCmdLine() ); + else + sprintf( szTemp, "GDAL %s( %s, ... )",pszFunctionName,pszFilename ); +#else + sprintf( szTemp, "GDAL %s( %s, ... )",pszFunctionName,pszFilename ); +#endif + + NCDFAddHistory( fpImage, szTemp, pszOldHist ); + +} + +/* code taken from cdo and libcdi, used for writing the history attribute */ +//void cdoDefHistory(int fileID, char *histstring) +void NCDFAddHistory(int fpImage, const char *pszAddHist, const char *pszOldHist) +{ + char strtime[32]; + time_t tp; + struct tm *ltime; + + char *pszNewHist = NULL; + size_t nNewHistSize = 0; + int disableHistory = FALSE; + int status; + + /* Check pszOldHist - as if there was no previous history, it will be + a null pointer - if so set as empty. */ + if (NULL == pszOldHist) { + pszOldHist = ""; + } + + tp = time(NULL); + if ( tp != -1 ) + { + ltime = localtime(&tp); + (void) strftime(strtime, sizeof(strtime), "%a %b %d %H:%M:%S %Y: ", ltime); + } + + // status = nc_get_att_text( fpImage, NC_GLOBAL, + // "history", pszOldHist ); + // printf("status: %d pszOldHist: [%s]\n",status,pszOldHist); + + nNewHistSize = strlen(pszOldHist)+strlen(strtime)+strlen(pszAddHist)+1+1; + pszNewHist = (char *) CPLMalloc(nNewHistSize * sizeof(char)); + + strcpy(pszNewHist, strtime); + strcat(pszNewHist, pszAddHist); + + if ( disableHistory == FALSE && pszNewHist ) + { + if ( ! EQUAL(pszOldHist,"") ) + strcat(pszNewHist, "\n"); + strcat(pszNewHist, pszOldHist); + } + + status = nc_put_att_text( fpImage, NC_GLOBAL, + "history", strlen(pszNewHist), + pszNewHist ); + NCDF_ERR(status); + + CPLFree(pszNewHist); +} + + +int NCDFIsCfProjection( const char* pszProjection ) +{ + /* Find the appropriate mapping */ + for (int iMap = 0; poNetcdfSRS_PT[iMap].WKT_SRS != NULL; iMap++ ) { + // printf("now at %d, proj=%s\n",i, poNetcdfSRS_PT[i].GDAL_SRS); + if ( EQUAL( pszProjection, poNetcdfSRS_PT[iMap].WKT_SRS ) ) { + if ( poNetcdfSRS_PT[iMap].mappings != NULL ) + return TRUE; + else + return FALSE; + } + } + return FALSE; +} + + +/* Write any needed projection attributes * + * poPROJCS: ptr to proj crd system + * pszProjection: name of projection system in GDAL WKT + * fpImage: open NetCDF file in writing mode + * NCDFVarID: NetCDF Var Id of proj system we're writing in to + * + * The function first looks for the oNetcdfSRS_PP mapping object + * that corresponds to the input projection name. If none is found + * the generic mapping is used. In the case of specific mappings, + * the driver looks for each attribute listed in the mapping object + * and then looks up the value within the OGR_SRSNode. In the case + * of the generic mapping, the lookup is reversed (projection params, + * then mapping). For more generic code, GDAL->NETCDF + * mappings and the associated value are saved in std::map objects. + */ + +/* NOTE modifications by ET to combine the specific and generic mappings */ + +void NCDFWriteProjAttribs( const OGR_SRSNode *poPROJCS, + const char* pszProjection, + const int fpImage, const int NCDFVarID ) +{ + double dfStdP[2]; + int bFoundStdP1=FALSE,bFoundStdP2=FALSE; + double dfValue=0.0; + const char *pszParamStr, *pszParamVal; + const std::string *pszNCDFAtt, *pszGDALAtt; + static const oNetcdfSRS_PP *poMap = NULL; + int nMapIndex = -1; + int bWriteVal = FALSE; + + //Attribute and Value mappings + std::map< std::string, std::string > oAttMap; + std::map< std::string, std::string >::iterator oAttIter; + std::map< std::string, double > oValMap; + std::map< std::string, double >::iterator oValIter, oValIter2; + //results to write + std::vector< std::pair > oOutList; + + /* Find the appropriate mapping */ + for (int iMap = 0; poNetcdfSRS_PT[iMap].WKT_SRS != NULL; iMap++ ) { + if ( EQUAL( pszProjection, poNetcdfSRS_PT[iMap].WKT_SRS ) ) { + nMapIndex = iMap; + poMap = poNetcdfSRS_PT[iMap].mappings; + break; + } + } + + //ET TODO if projection name is not found, should we do something special? + if ( nMapIndex == -1 ) { + CPLError( CE_Warning, CPLE_AppDefined, + "projection name %s not found in the lookup tables!!!", + pszProjection); + } + /* if no mapping was found or assigned, set the generic one */ + if ( !poMap ) { + CPLError( CE_Warning, CPLE_AppDefined, + "projection name %s in not part of the CF standard, will not be supported by CF!", + pszProjection); + poMap = poGenericMappings; + } + + /* initialize local map objects */ + for ( int iMap = 0; poMap[iMap].WKT_ATT != NULL; iMap++ ) { + oAttMap[poMap[iMap].WKT_ATT] = poMap[iMap].CF_ATT; + } + + for( int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ ) { + + const OGR_SRSNode *poNode; + + poNode = poPROJCS->GetChild( iChild ); + if( !EQUAL(poNode->GetValue(),"PARAMETER") + || poNode->GetChildCount() != 2 ) + continue; + pszParamStr = poNode->GetChild(0)->GetValue(); + pszParamVal = poNode->GetChild(1)->GetValue(); + + oValMap[pszParamStr] = CPLAtof(pszParamVal); + } + + /* Lookup mappings and fill output vector */ + if ( poMap != poGenericMappings ) { /* specific mapping, loop over mapping values */ + + for ( oAttIter = oAttMap.begin(); oAttIter != oAttMap.end(); oAttIter++ ) { + + pszGDALAtt = &(oAttIter->first); + pszNCDFAtt = &(oAttIter->second); + oValIter = oValMap.find( *pszGDALAtt ); + + if ( oValIter != oValMap.end() ) { + + dfValue = oValIter->second; + bWriteVal = TRUE; + + /* special case for PS (Polar Stereographic) grid + See comments in netcdfdataset.h for this projection. */ + if ( EQUAL( SRS_PP_LATITUDE_OF_ORIGIN, pszGDALAtt->c_str() ) && + EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC) ) { + double dfLatPole = 0.0; + if ( dfValue > 0.0) dfLatPole = 90.0; + else dfLatPole = -90.0; + oOutList.push_back( std::make_pair( std::string(CF_PP_LAT_PROJ_ORIGIN), + dfLatPole ) ); + } + + /* special case for LCC-1SP + See comments in netcdfdataset.h for this projection. */ + else if ( EQUAL( SRS_PP_SCALE_FACTOR, pszGDALAtt->c_str() ) && + EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) ) { + /* default is to not write as it is not CF-1 */ + bWriteVal = FALSE; + /* test if there is no standard_parallel1 */ + if ( oValMap.find( std::string(CF_PP_STD_PARALLEL_1) ) == oValMap.end() ) { + /* if scale factor != 1.0 write value for GDAL, but this is not supported by CF-1 */ + if ( !CPLIsEqual(dfValue,1.0) ) { + CPLError( CE_Failure, CPLE_NotSupported, + "NetCDF driver export of LCC-1SP with scale factor != 1.0 " + "and no standard_parallel1 is not CF-1 (bug #3324).\n" + "Use the 2SP variant which is supported by CF." ); + bWriteVal = TRUE; + } + /* else copy standard_parallel1 from latitude_of_origin, because scale_factor=1.0 */ + else { + oValIter2 = oValMap.find( std::string(SRS_PP_LATITUDE_OF_ORIGIN) ); + if (oValIter2 != oValMap.end() ) { + oOutList.push_back( std::make_pair( std::string(CF_PP_STD_PARALLEL_1), + oValIter2->second) ); + } + else { + CPLError( CE_Failure, CPLE_NotSupported, + "NetCDF driver export of LCC-1SP with no standard_parallel1 " + "and no latitude_of_origin is not suported (bug #3324)."); + } + } + } + } + if ( bWriteVal ) + oOutList.push_back( std::make_pair( *pszNCDFAtt, dfValue ) ); + + } + // else printf("NOT FOUND!!!\n"); + } + + } + else { /* generic mapping, loop over projected values */ + + for ( oValIter = oValMap.begin(); oValIter != oValMap.end(); oValIter++ ) { + + pszGDALAtt = &(oValIter->first); + dfValue = oValIter->second; + + oAttIter = oAttMap.find( *pszGDALAtt ); + + if ( oAttIter != oAttMap.end() ) { + oOutList.push_back( std::make_pair( oAttIter->second, dfValue ) ); + } + /* for SRS_PP_SCALE_FACTOR write 2 mappings */ + else if ( EQUAL(pszGDALAtt->c_str(), SRS_PP_SCALE_FACTOR) ) { + oOutList.push_back( std::make_pair( std::string(CF_PP_SCALE_FACTOR_MERIDIAN), + dfValue ) ); + oOutList.push_back( std::make_pair( std::string(CF_PP_SCALE_FACTOR_ORIGIN), + dfValue ) ); + } + /* if not found insert the GDAL name */ + else { + oOutList.push_back( std::make_pair( *pszGDALAtt, dfValue ) ); + } + } + } + + /* Write all the values that were found */ + // std::vector< std::pair >::reverse_iterator it; + // for (it = oOutList.rbegin(); it != oOutList.rend(); it++ ) { + std::vector< std::pair >::iterator it; + for (it = oOutList.begin(); it != oOutList.end(); it++ ) { + pszParamVal = (it->first).c_str(); + dfValue = it->second; + /* Handle the STD_PARALLEL attrib */ + if( EQUAL( pszParamVal, CF_PP_STD_PARALLEL_1 ) ) { + bFoundStdP1 = TRUE; + dfStdP[0] = dfValue; + } + else if( EQUAL( pszParamVal, CF_PP_STD_PARALLEL_2 ) ) { + bFoundStdP2 = TRUE; + dfStdP[1] = dfValue; + } + else { + nc_put_att_double( fpImage, NCDFVarID, pszParamVal, + NC_DOUBLE, 1,&dfValue ); + } + } + /* Now write the STD_PARALLEL attrib */ + if ( bFoundStdP1 ) { + /* one value or equal values */ + if ( !bFoundStdP2 || dfStdP[0] == dfStdP[1] ) { + nc_put_att_double( fpImage, NCDFVarID, CF_PP_STD_PARALLEL, + NC_DOUBLE, 1, &dfStdP[0] ); + } + else { /* two values */ + nc_put_att_double( fpImage, NCDFVarID, CF_PP_STD_PARALLEL, + NC_DOUBLE, 2, dfStdP ); + } + } +} + +CPLErr NCDFSafeStrcat(char** ppszDest, char* pszSrc, size_t* nDestSize) +{ + /* Reallocate the data string until the content fits */ + while(*nDestSize < (strlen(*ppszDest) + strlen(pszSrc) + 1)) { + (*nDestSize) *= 2; + *ppszDest = (char*) CPLRealloc((void*) *ppszDest, *nDestSize); +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "NCDFSafeStrcat() resized str from %ld to %ld", (*nDestSize)/2, *nDestSize ); +#endif + } + strcat(*ppszDest, pszSrc); + + return CE_None; +} + +CPLErr NCDFSafeStrcpy(char** ppszDest, char* pszSrc, size_t* nDestSize) +{ + /* Reallocate the data string until the content fits */ + while(*nDestSize < (strlen(*ppszDest) + strlen(pszSrc) + 1)) { + (*nDestSize) *= 2; + *ppszDest = (char*) CPLRealloc((void*) *ppszDest, *nDestSize); +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "NCDFSafeStrcpy() resized str from %ld to %ld", (*nDestSize)/2, *nDestSize ); +#endif + } + strcpy(*ppszDest, pszSrc); + + return CE_None; +} + +/* helper function for NCDFGetAttr() */ +/* sets pdfValue to first value returned */ +/* and if bSetPszValue=True sets pszValue with all attribute values */ +/* pszValue is the responsibility of the caller and must be freed */ +CPLErr NCDFGetAttr1( int nCdfId, int nVarId, const char *pszAttrName, + double *pdfValue, char **pszValue, int bSetPszValue ) +{ + nc_type nAttrType = NC_NAT; + size_t nAttrLen = 0; + size_t nAttrValueSize; + int status = 0; /*rename this */ + size_t m; + char szTemp[ NCDF_MAX_STR_LEN ]; + char *pszAttrValue = NULL; + double dfValue = 0.0; + + status = nc_inq_att( nCdfId, nVarId, pszAttrName, &nAttrType, &nAttrLen); + if ( status != NC_NOERR ) + return CE_Failure; + +#ifdef NCDF_DEBUG + CPLDebug( "GDAL_netCDF", "NCDFGetAttr1(%s) len=%ld type=%d", pszAttrName, nAttrLen, nAttrType ); +#endif + + /* Allocate guaranteed minimum size (use 10 or 20 if not a string) */ + nAttrValueSize = nAttrLen + 1; + if ( nAttrType != NC_CHAR && nAttrValueSize < 10 ) + nAttrValueSize = 10; + if ( nAttrType == NC_DOUBLE && nAttrValueSize < 20 ) + nAttrValueSize = 20; + pszAttrValue = (char *) CPLCalloc( nAttrValueSize, sizeof( char )); + *pszAttrValue = '\0'; + + if ( nAttrLen > 1 && nAttrType != NC_CHAR ) + NCDFSafeStrcat(&pszAttrValue, (char *)"{", &nAttrValueSize); + + switch (nAttrType) { + case NC_CHAR: + nc_get_att_text( nCdfId, nVarId, pszAttrName, pszAttrValue ); + pszAttrValue[nAttrLen]='\0'; + dfValue = 0.0; + break; + case NC_BYTE: + signed char *pscTemp; + pscTemp = (signed char *) CPLCalloc( nAttrLen, sizeof( signed char ) ); + nc_get_att_schar( nCdfId, nVarId, pszAttrName, pscTemp ); + dfValue = (double)pscTemp[0]; + for(m=0; m < nAttrLen-1; m++) { + sprintf( szTemp, "%d,", pscTemp[m] ); + NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize); + } + sprintf( szTemp, "%d", pscTemp[m] ); + NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize); + CPLFree(pscTemp); + break; +#ifdef NETCDF_HAS_NC4 + case NC_UBYTE: + unsigned char *pucTemp; + pucTemp = (unsigned char *) CPLCalloc( nAttrLen, sizeof( unsigned char ) ); + nc_get_att_uchar( nCdfId, nVarId, pszAttrName, pucTemp ); + dfValue = (double)pucTemp[0]; + for(m=0; m < nAttrLen-1; m++) { + sprintf( szTemp, "%d,", pucTemp[m] ); + NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize); + } + sprintf( szTemp, "%d", pucTemp[m] ); + NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize); + CPLFree(pucTemp); + break; +#endif + case NC_SHORT: + short *psTemp; + psTemp = (short *) CPLCalloc( nAttrLen, sizeof( short ) ); + nc_get_att_short( nCdfId, nVarId, pszAttrName, psTemp ); + dfValue = (double)psTemp[0]; + for(m=0; m < nAttrLen-1; m++) { + sprintf( szTemp, "%hd,", psTemp[m] ); + NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize); + } + sprintf( szTemp, "%hd", psTemp[m] ); + NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize); + CPLFree(psTemp); + break; + case NC_INT: + int *pnTemp; + pnTemp = (int *) CPLCalloc( nAttrLen, sizeof( int ) ); + nc_get_att_int( nCdfId, nVarId, pszAttrName, pnTemp ); + dfValue = (double)pnTemp[0]; + for(m=0; m < nAttrLen-1; m++) { + sprintf( szTemp, "%d,", pnTemp[m] ); + NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize); + } + sprintf( szTemp, "%d", pnTemp[m] ); + NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize); + CPLFree(pnTemp); + break; + case NC_FLOAT: + float *pfTemp; + pfTemp = (float *) CPLCalloc( nAttrLen, sizeof( float ) ); + nc_get_att_float( nCdfId, nVarId, pszAttrName, pfTemp ); + dfValue = (double)pfTemp[0]; + for(m=0; m < nAttrLen-1; m++) { + CPLsprintf( szTemp, "%.8g,", pfTemp[m] ); + NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize); + } + CPLsprintf( szTemp, "%.8g", pfTemp[m] ); + NCDFSafeStrcat(&pszAttrValue,szTemp, &nAttrValueSize); + CPLFree(pfTemp); + break; + case NC_DOUBLE: + double *pdfTemp; + pdfTemp = (double *) CPLCalloc(nAttrLen, sizeof(double)); + nc_get_att_double( nCdfId, nVarId, pszAttrName, pdfTemp ); + dfValue = pdfTemp[0]; + for(m=0; m < nAttrLen-1; m++) { + CPLsprintf( szTemp, "%.16g,", pdfTemp[m] ); + NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize); + } + CPLsprintf( szTemp, "%.16g", pdfTemp[m] ); + NCDFSafeStrcat(&pszAttrValue, szTemp, &nAttrValueSize); + CPLFree(pdfTemp); + break; + default: + CPLDebug( "GDAL_netCDF", "NCDFGetAttr unsupported type %d for attribute %s", + nAttrType,pszAttrName); + CPLFree( pszAttrValue ); + pszAttrValue = NULL; + break; + } + + if ( nAttrLen > 1 && nAttrType!= NC_CHAR ) + NCDFSafeStrcat(&pszAttrValue, (char *)"}", &nAttrValueSize); + + /* set return values */ + if ( bSetPszValue == TRUE ) *pszValue = pszAttrValue; + else CPLFree ( pszAttrValue ); + if ( pdfValue ) *pdfValue = dfValue; + + return CE_None; +} + + +/* sets pdfValue to first value found */ +CPLErr NCDFGetAttr( int nCdfId, int nVarId, const char *pszAttrName, + double *pdfValue ) +{ + return NCDFGetAttr1( nCdfId, nVarId, pszAttrName, pdfValue, NULL, FALSE ); +} + + +/* pszValue is the responsibility of the caller and must be freed */ +CPLErr NCDFGetAttr( int nCdfId, int nVarId, const char *pszAttrName, + char **pszValue ) +{ + return NCDFGetAttr1( nCdfId, nVarId, pszAttrName, FALSE, pszValue, TRUE ); +} + + +/* By default write NC_CHAR, but detect for int/float/double */ +CPLErr NCDFPutAttr( int nCdfId, int nVarId, + const char *pszAttrName, const char *pszValue ) +{ + nc_type nAttrType = NC_CHAR; + nc_type nTmpAttrType = NC_CHAR; + size_t nAttrLen = 0; + int status = 0; + size_t i; + char szTemp[ NCDF_MAX_STR_LEN ]; + char *pszTemp = NULL; + char **papszValues = NULL; + + int nValue = 0; + float fValue = 0.0f; + double dfValue = 0.0; + + /* get the attribute values as tokens */ + papszValues = NCDFTokenizeArray( pszValue ); + if ( papszValues == NULL ) + return CE_Failure; + + nAttrLen = CSLCount(papszValues); + + /* first detect type */ + nAttrType = NC_CHAR; + for ( i=0; i nAttrType ) + nAttrType = nTmpAttrType; + } + + /* now write the data */ + if ( nAttrType == NC_CHAR ) { + status = nc_put_att_text( nCdfId, nVarId, pszAttrName, + strlen( pszValue ), pszValue ); + NCDF_ERR(status); + } + else { + + switch( nAttrType ) { + case NC_INT: + int *pnTemp; + pnTemp = (int *) CPLCalloc( nAttrLen, sizeof( int ) ); + for(i=0; i < nAttrLen; i++) { + pnTemp[i] = strtol( papszValues[i], &pszTemp, 10 ); + } + status = nc_put_att_int( nCdfId, nVarId, pszAttrName, + NC_INT, nAttrLen, pnTemp ); + NCDF_ERR(status); + CPLFree(pnTemp); + break; + case NC_FLOAT: + float *pfTemp; + pfTemp = (float *) CPLCalloc( nAttrLen, sizeof( float ) ); + for(i=0; i < nAttrLen; i++) { + pfTemp[i] = (float)CPLStrtod( papszValues[i], &pszTemp ); + } + status = nc_put_att_float( nCdfId, nVarId, pszAttrName, + NC_FLOAT, nAttrLen, pfTemp ); + NCDF_ERR(status); + CPLFree(pfTemp); + break; + case NC_DOUBLE: + double *pdfTemp; + pdfTemp = (double *) CPLCalloc( nAttrLen, sizeof( double ) ); + for(i=0; i < nAttrLen; i++) { + pdfTemp[i] = CPLStrtod( papszValues[i], &pszTemp ); + } + status = nc_put_att_double( nCdfId, nVarId, pszAttrName, + NC_DOUBLE, nAttrLen, pdfTemp ); + NCDF_ERR(status); + CPLFree(pdfTemp); + break; + default: + if ( papszValues ) CSLDestroy( papszValues ); + return CE_Failure; + break; + } + } + + if ( papszValues ) CSLDestroy( papszValues ); + + return CE_None; +} + +CPLErr NCDFGet1DVar( int nCdfId, int nVarId, char **pszValue ) +{ + nc_type nVarType = NC_NAT; + size_t nVarLen = 0; + int status = 0; + size_t m; + char szTemp[ NCDF_MAX_STR_LEN ]; + char *pszVarValue = NULL; + size_t nVarValueSize; + int nVarDimId=-1; + size_t start[1], count[1]; + + /* get var information */ + status = nc_inq_varndims( nCdfId, nVarId, &nVarDimId ); + if ( status != NC_NOERR || nVarDimId != 1) + return CE_Failure; + status = nc_inq_vardimid( nCdfId, nVarId, &nVarDimId ); + if ( status != NC_NOERR ) + return CE_Failure; + status = nc_inq_vartype( nCdfId, nVarId, &nVarType ); + if ( status != NC_NOERR ) + return CE_Failure; + status = nc_inq_dimlen( nCdfId, nVarDimId, &nVarLen ); + if ( status != NC_NOERR ) + return CE_Failure; + start[0] = 0; + count[0] = nVarLen; + + /* Allocate guaranteed minimum size */ + nVarValueSize = NCDF_MAX_STR_LEN; + pszVarValue = (char *) CPLCalloc( nVarValueSize, sizeof( char )); + *pszVarValue = '\0'; + + if ( nVarLen > 1 && nVarType != NC_CHAR ) + NCDFSafeStrcat(&pszVarValue, (char *)"{", &nVarValueSize); + + switch (nVarType) { + case NC_CHAR: + nc_get_vara_text( nCdfId, nVarId, start, count, pszVarValue ); + pszVarValue[nVarLen]='\0'; + break; + /* TODO support NC_UBYTE */ + case NC_BYTE: + signed char *pscTemp; + pscTemp = (signed char *) CPLCalloc( nVarLen, sizeof( signed char ) ); + nc_get_vara_schar( nCdfId, nVarId, start, count, pscTemp ); + for(m=0; m < nVarLen-1; m++) { + sprintf( szTemp, "%d,", pscTemp[m] ); + NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize); + } + sprintf( szTemp, "%d", pscTemp[m] ); + NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize); + CPLFree(pscTemp); + break; + case NC_SHORT: + short *psTemp; + psTemp = (short *) CPLCalloc( nVarLen, sizeof( short ) ); + nc_get_vara_short( nCdfId, nVarId, start, count, psTemp ); + for(m=0; m < nVarLen-1; m++) { + sprintf( szTemp, "%hd,", psTemp[m] ); + NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize); + } + sprintf( szTemp, "%hd", psTemp[m] ); + NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize); + CPLFree(psTemp); + break; + case NC_INT: + int *pnTemp; + pnTemp = (int *) CPLCalloc( nVarLen, sizeof( int ) ); + nc_get_vara_int( nCdfId, nVarId, start, count, pnTemp ); + for(m=0; m < nVarLen-1; m++) { + sprintf( szTemp, "%d,", pnTemp[m] ); + NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize); + } + sprintf( szTemp, "%d", pnTemp[m] ); + NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize); + CPLFree(pnTemp); + break; + case NC_FLOAT: + float *pfTemp; + pfTemp = (float *) CPLCalloc( nVarLen, sizeof( float ) ); + nc_get_vara_float( nCdfId, nVarId, start, count, pfTemp ); + for(m=0; m < nVarLen-1; m++) { + CPLsprintf( szTemp, "%.8g,", pfTemp[m] ); + NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize); + } + CPLsprintf( szTemp, "%.8g", pfTemp[m] ); + NCDFSafeStrcat(&pszVarValue,szTemp, &nVarValueSize); + CPLFree(pfTemp); + break; + case NC_DOUBLE: + double *pdfTemp; + pdfTemp = (double *) CPLCalloc(nVarLen, sizeof(double)); + nc_get_vara_double( nCdfId, nVarId, start, count, pdfTemp ); + for(m=0; m < nVarLen-1; m++) { + CPLsprintf( szTemp, "%.16g,", pdfTemp[m] ); + NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize); + } + CPLsprintf( szTemp, "%.16g", pdfTemp[m] ); + NCDFSafeStrcat(&pszVarValue, szTemp, &nVarValueSize); + CPLFree(pdfTemp); + break; + default: + CPLDebug( "GDAL_netCDF", "NCDFGetVar1D unsupported type %d", + nVarType ); + CPLFree( pszVarValue ); + pszVarValue = NULL; + break; + } + + if ( nVarLen > 1 && nVarType!= NC_CHAR ) + NCDFSafeStrcat(&pszVarValue, (char *)"}", &nVarValueSize); + + /* set return values */ + *pszValue = pszVarValue; + + return CE_None; +} + +CPLErr NCDFPut1DVar( int nCdfId, int nVarId, const char *pszValue ) +{ + nc_type nVarType = NC_CHAR; + size_t nVarLen = 0; + int status = 0; + size_t i; + char *pszTemp = NULL; + char **papszValues = NULL; + + int nVarDimId=-1; + size_t start[1], count[1]; + + if ( EQUAL( pszValue, "" ) ) + return CE_Failure; + + /* get var information */ + status = nc_inq_varndims( nCdfId, nVarId, &nVarDimId ); + if ( status != NC_NOERR || nVarDimId != 1) + return CE_Failure; + status = nc_inq_vardimid( nCdfId, nVarId, &nVarDimId ); + if ( status != NC_NOERR ) + return CE_Failure; + status = nc_inq_vartype( nCdfId, nVarId, &nVarType ); + if ( status != NC_NOERR ) + return CE_Failure; + status = nc_inq_dimlen( nCdfId, nVarDimId, &nVarLen ); + if ( status != NC_NOERR ) + return CE_Failure; + start[0] = 0; + count[0] = nVarLen; + + /* get the values as tokens */ + papszValues = NCDFTokenizeArray( pszValue ); + if ( papszValues == NULL ) + return CE_Failure; + + nVarLen = CSLCount(papszValues); + + /* now write the data */ + if ( nVarType == NC_CHAR ) { + status = nc_put_vara_text( nCdfId, nVarId, start, count, + pszValue ); + NCDF_ERR(status); + } + else { + + switch( nVarType ) { + /* TODO add other types */ + case NC_INT: + int *pnTemp; + pnTemp = (int *) CPLCalloc( nVarLen, sizeof( int ) ); + for(i=0; i < nVarLen; i++) { + pnTemp[i] = strtol( papszValues[i], &pszTemp, 10 ); + } + status = nc_put_vara_int( nCdfId, nVarId, start, count, pnTemp ); + NCDF_ERR(status); + CPLFree(pnTemp); + break; + case NC_FLOAT: + float *pfTemp; + pfTemp = (float *) CPLCalloc( nVarLen, sizeof( float ) ); + for(i=0; i < nVarLen; i++) { + pfTemp[i] = (float)CPLStrtod( papszValues[i], &pszTemp ); + } + status = nc_put_vara_float( nCdfId, nVarId, start, count, + pfTemp ); + NCDF_ERR(status); + CPLFree(pfTemp); + break; + case NC_DOUBLE: + double *pdfTemp; + pdfTemp = (double *) CPLCalloc( nVarLen, sizeof( double ) ); + for(i=0; i < nVarLen; i++) { + pdfTemp[i] = CPLStrtod( papszValues[i], &pszTemp ); + } + status = nc_put_vara_double( nCdfId, nVarId, start, count, + pdfTemp ); + NCDF_ERR(status); + CPLFree(pdfTemp); + break; + default: + if ( papszValues ) CSLDestroy( papszValues ); + return CE_Failure; + break; + } + } + + if ( papszValues ) CSLDestroy( papszValues ); + + return CE_None; +} + + +/************************************************************************/ +/* GetDefaultNoDataValue() */ +/************************************************************************/ + +double NCDFGetDefaultNoDataValue( int nVarType ) + +{ + double dfNoData = 0.0; + + switch( nVarType ) { + case NC_BYTE: +#ifdef NETCDF_HAS_NC4 + case NC_UBYTE: +#endif + /* don't do default fill-values for bytes, too risky */ + dfNoData = 0.0; + break; + case NC_CHAR: + dfNoData = NC_FILL_CHAR; + break; + case NC_SHORT: + dfNoData = NC_FILL_SHORT; + break; + case NC_INT: + dfNoData = NC_FILL_INT; + break; + case NC_FLOAT: + dfNoData = NC_FILL_FLOAT; + break; + case NC_DOUBLE: + dfNoData = NC_FILL_DOUBLE; + break; + default: + dfNoData = 0.0; + break; + } + + return dfNoData; +} + + +int NCDFDoesVarContainAttribVal( int nCdfId, + const char ** papszAttribNames, + const char ** papszAttribValues, + int nVarId, + const char * pszVarName, + int bStrict=TRUE ) +{ + char *pszTemp = NULL; + int bFound = FALSE; + + if ( (nVarId == -1) && (pszVarName != NULL) ) + nc_inq_varid( nCdfId, pszVarName, &nVarId ); + + if ( nVarId == -1 ) return -1; + + for( int i=0; !bFound && i +#include "gdal_pam.h" +#include "gdal_priv.h" +#include "gdal_frmts.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" +#include "netcdf.h" + + +/************************************************************************/ +/* ==================================================================== */ +/* defines */ +/* ==================================================================== */ +/************************************************************************/ + +/* -------------------------------------------------------------------- */ +/* Creation and Configuration Options */ +/* -------------------------------------------------------------------- */ + +/* Creation options + + FORMAT=NC/NC2/NC4/NC4C (COMPRESS=DEFLATE sets FORMAT=NC4C) + COMPRESS=NONE/DEFLATE (default: NONE) + ZLEVEL=[1-9] (default: 1) + WRITE_BOTTOMUP=YES/NO (default: YES) + WRITE_GDAL_TAGS=YES/NO (default: YES) + WRITE_LONLAT=YES/NO/IF_NEEDED (default: YES for geographic, NO for projected) + TYPE_LONLAT=float/double (default: double for geographic, float for projected) + PIXELTYPE=DEFAULT/SIGNEDBYTE (use SIGNEDBYTE to get a signed Byte Band) +*/ + +/* Config Options + + GDAL_NETCDF_BOTTOMUP=YES/NO overrides bottom-up value on import + GDAL_NETCDF_CONVERT_LAT_180=YES/NO convert longitude values from ]180,360] to [-180,180] +*/ + +/* -------------------------------------------------------------------- */ +/* Driver-specific defines */ +/* -------------------------------------------------------------------- */ + +/* NETCDF driver defs */ +#define NCDF_MAX_STR_LEN 8192 +#define NCDF_CONVENTIONS_CF "CF-1.5" +#define NCDF_SPATIAL_REF "spatial_ref" +#define NCDF_GEOTRANSFORM "GeoTransform" +#define NCDF_DIMNAME_X "x" +#define NCDF_DIMNAME_Y "y" +#define NCDF_DIMNAME_LON "lon" +#define NCDF_DIMNAME_LAT "lat" +#define NCDF_LONLAT "lon lat" +#define NCDF_PI 3.14159265358979323846 + +/* netcdf file types, as in libcdi/cdo and compat w/netcdf.h */ +#define NCDF_FORMAT_NONE 0 /* Not a netCDF file */ +#define NCDF_FORMAT_NC 1 /* netCDF classic format */ +#define NCDF_FORMAT_NC2 2 /* netCDF version 2 (64-bit) */ +#define NCDF_FORMAT_NC4 3 /* netCDF version 4 */ +#define NCDF_FORMAT_NC4C 4 /* netCDF version 4 (classic) */ +#define NCDF_FORMAT_UNKNOWN 10 /* Format not determined (yet) */ +/* HDF files (HDF5 or HDF4) not supported because of lack of support */ +/* in libnetcdf installation or conflict with other drivers */ +#define NCDF_FORMAT_HDF5 5 /* HDF4 file, not supported */ +#define NCDF_FORMAT_HDF4 6 /* HDF4 file, not supported */ + +/* compression parameters */ +#define NCDF_COMPRESS_NONE 0 +/* TODO */ +/* http://www.unidata.ucar.edu/software/netcdf/docs/BestPractices.html#Packed%20Data%20Values */ +#define NCDF_COMPRESS_PACKED 1 +#define NCDF_COMPRESS_DEFLATE 2 +#define NCDF_DEFLATE_LEVEL 1 /* best time/size ratio */ +#define NCDF_COMPRESS_SZIP 3 /* no support for writting */ + +/* helper for libnetcdf errors */ +#define NCDF_ERR(status) if ( status != NC_NOERR ){ \ +CPLError( CE_Failure,CPLE_AppDefined, \ +"netcdf error #%d : %s .\nat (%s,%s,%d)\n",status, nc_strerror(status), \ +__FILE__, __FUNCTION__, __LINE__ ); } + +/* check for NC2 support in case it wasn't enabled at compile time */ +/* NC4 has to be detected at compile as it requires a special build of netcdf-4 */ +#ifndef NETCDF_HAS_NC2 +#ifdef NC_64BIT_OFFSET +#define NETCDF_HAS_NC2 1 +#endif +#endif + + +/* -------------------------------------------------------------------- */ +/* CF-1 or NUG (NetCDF User's Guide) defs */ +/* -------------------------------------------------------------------- */ + +/* CF: http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.5/cf-conventions.html */ +/* NUG: http://www.unidata.ucar.edu/software/netcdf/docs/netcdf.html#Variables */ +#define CF_STD_NAME "standard_name" +#define CF_LNG_NAME "long_name" +#define CF_UNITS "units" +#define CF_ADD_OFFSET "add_offset" +#define CF_SCALE_FACTOR "scale_factor" +/* should be SRS_UL_METER but use meter now for compat with gtiff files */ +#define CF_UNITS_M "metre" +#define CF_UNITS_D SRS_UA_DEGREE +#define CF_PROJ_X_COORD "projection_x_coordinate" +#define CF_PROJ_Y_COORD "projection_y_coordinate" +#define CF_PROJ_X_COORD_LONG_NAME "x coordinate of projection" +#define CF_PROJ_Y_COORD_LONG_NAME "y coordinate of projection" +#define CF_GRD_MAPPING_NAME "grid_mapping_name" +#define CF_GRD_MAPPING "grid_mapping" +#define CF_COORDINATES "coordinates" +/* #define CF_AXIS "axis" */ +/* #define CF_BOUNDS "bounds" */ +/* #define CF_ORIG_UNITS "original_units" */ + + +/* -------------------------------------------------------------------- */ +/* CF-1 convention standard variables related to */ +/* mapping & projection - see http://cf-pcmdi.llnl.gov/ */ +/* -------------------------------------------------------------------- */ + +/* projection types */ +#define CF_PT_AEA "albers_conical_equal_area" +#define CF_PT_AE "azimuthal_equidistant" +#define CF_PT_CEA "cylindrical_equal_area" +#define CF_PT_LAEA "lambert_azimuthal_equal_area" +#define CF_PT_LCEA "lambert_cylindrical_equal_area" +#define CF_PT_LCC "lambert_conformal_conic" +#define CF_PT_TM "transverse_mercator" +#define CF_PT_LATITUDE_LONGITUDE "latitude_longitude" +#define CF_PT_MERCATOR "mercator" +#define CF_PT_ORTHOGRAPHIC "orthographic" +#define CF_PT_POLAR_STEREO "polar_stereographic" +#define CF_PT_STEREO "stereographic" + +/* projection parameters */ +#define CF_PP_STD_PARALLEL "standard_parallel" +/* CF uses only "standard_parallel" */ +#define CF_PP_STD_PARALLEL_1 "standard_parallel_1" +#define CF_PP_STD_PARALLEL_2 "standard_parallel_2" +#define CF_PP_CENTRAL_MERIDIAN "central_meridian" +#define CF_PP_LONG_CENTRAL_MERIDIAN "longitude_of_central_meridian" +#define CF_PP_LON_PROJ_ORIGIN "longitude_of_projection_origin" +#define CF_PP_LAT_PROJ_ORIGIN "latitude_of_projection_origin" +/* #define PROJ_X_ORIGIN "projection_x_coordinate_origin" */ +/* #define PROJ_Y_ORIGIN "projection_y_coordinate_origin" */ +#define CF_PP_EARTH_SHAPE "GRIB_earth_shape" +#define CF_PP_EARTH_SHAPE_CODE "GRIB_earth_shape_code" +/* scale_factor is not CF, there are two possible translations */ +/* for WKT scale_factor : SCALE_FACTOR_MERIDIAN and SCALE_FACTOR_ORIGIN */ +#define CF_PP_SCALE_FACTOR_MERIDIAN "scale_factor_at_central_meridian" +#define CF_PP_SCALE_FACTOR_ORIGIN "scale_factor_at_projection_origin" +#define CF_PP_VERT_LONG_FROM_POLE "straight_vertical_longitude_from_pole" +#define CF_PP_FALSE_EASTING "false_easting" +#define CF_PP_FALSE_NORTHING "false_northing" +#define CF_PP_EARTH_RADIUS "earth_radius" +#define CF_PP_EARTH_RADIUS_OLD "spherical_earth_radius_meters" +#define CF_PP_INVERSE_FLATTENING "inverse_flattening" +#define CF_PP_LONG_PRIME_MERIDIAN "longitude_of_prime_meridian" +#define CF_PP_SEMI_MAJOR_AXIS "semi_major_axis" +#define CF_PP_SEMI_MINOR_AXIS "semi_minor_axis" +#define CF_PP_VERT_PERSP "vertical_perspective" /*not used yet */ + + +/* -------------------------------------------------------------------- */ +/* CF-1 Coordinate Type Naming (Chapter 4. Coordinate Types ) */ +/* -------------------------------------------------------------------- */ +static const char* papszCFLongitudeVarNames[] = { "lon", "longitude", NULL }; +static const char* papszCFLongitudeAttribNames[] = { "units", CF_STD_NAME, "axis", NULL }; +static const char* papszCFLongitudeAttribValues[] = { "degrees_east", "longitude", "X", NULL }; +static const char* papszCFLatitudeVarNames[] = { "lat", "latitude", NULL }; +static const char* papszCFLatitudeAttribNames[] = { "units", CF_STD_NAME, "axis", NULL }; +static const char* papszCFLatitudeAttribValues[] = { "degrees_north", "latitude", "Y", NULL }; + +static const char* papszCFProjectionXVarNames[] = { "x", "xc", NULL }; +static const char* papszCFProjectionXAttribNames[] = { CF_STD_NAME, NULL }; +static const char* papszCFProjectionXAttribValues[] = { CF_PROJ_X_COORD, NULL }; +static const char* papszCFProjectionYVarNames[] = { "y", "yc", NULL }; +static const char* papszCFProjectionYAttribNames[] = { CF_STD_NAME, NULL }; +static const char* papszCFProjectionYAttribValues[] = { CF_PROJ_Y_COORD, NULL }; + +static const char* papszCFVerticalAttribNames[] = { "axis", "positive", "positive", NULL }; +static const char* papszCFVerticalAttribValues[] = { "Z", "up", "down", NULL }; +static const char* papszCFVerticalUnitsValues[] = { + /* units of pressure */ + "bar", "bars", "millibar", "millibars", "decibar", "decibars", + "atmosphere", "atmospheres", "atm", "pascal", "pascals", "Pa", "hPa", + /* units of length */ + "meter", "meters", "m", "kilometer", "kilometers", "km", + /* dimensionless vertical coordinates */ + "level", "layer", "sigma_level", + NULL }; +/* dimensionless vertical coordinates */ +static const char* papszCFVerticalStandardNameValues[] = { + "atmosphere_ln_pressure_coordinate", "atmosphere_sigma_coordinate", + "atmosphere_hybrid_sigma_pressure_coordinate", + "atmosphere_hybrid_height_coordinate", + "atmosphere_sleve_coordinate", "ocean_sigma_coordinate", + "ocean_s_coordinate", "ocean_sigma_z_coordinate", + "ocean_double_sigma_coordinate", "atmosphere_ln_pressure_coordinate", + "atmosphere_sigma_coordinate", + "atmosphere_hybrid_sigma_pressure_coordinate", + "atmosphere_hybrid_height_coordinate", + "atmosphere_sleve_coordinate", "ocean_sigma_coordinate", + "ocean_s_coordinate", "ocean_sigma_z_coordinate", + "ocean_double_sigma_coordinate", NULL }; + +static const char* papszCFTimeAttribNames[] = { "axis", NULL }; +static const char* papszCFTimeAttribValues[] = { "T", NULL }; +static const char* papszCFTimeUnitsValues[] = { + "days since", "day since", "d since", + "hours since", "hour since", "h since", "hr since", + "minutes since", "minute since", "min since", + "seconds since", "second since", "sec since", "s since", + NULL }; + + +/* -------------------------------------------------------------------- */ +/* CF-1 to GDAL mappings */ +/* -------------------------------------------------------------------- */ + +/* Following are a series of mappings from CF-1 convention parameters + * for each projection, to the equivalent in OGC WKT used internally by GDAL. + * See: http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.5/apf.html + */ + +/* A struct allowing us to map between GDAL(OGC WKT) and CF-1 attributes */ +typedef struct { + const char *CF_ATT; + const char *WKT_ATT; + // TODO: mappings may need default values, like scale factor? + //double defval; +} oNetcdfSRS_PP; + +// default mappings, for the generic case +/* These 'generic' mappings are based on what was previously in the + poNetCDFSRS struct. They will be used as a fallback in case none + of the others match (ie you are exporting a projection that has + no CF-1 equivalent). + They are not used for known CF-1 projections since there is not a + unique 2-way projection-independent + mapping between OGC WKT params and CF-1 ones: it varies per-projection. +*/ + +static const oNetcdfSRS_PP poGenericMappings[] = { + /* scale_factor is handled as a special case, write 2 values */ + {CF_PP_STD_PARALLEL_1, SRS_PP_STANDARD_PARALLEL_1 }, + {CF_PP_STD_PARALLEL_2, SRS_PP_STANDARD_PARALLEL_2 }, + {CF_PP_LONG_CENTRAL_MERIDIAN, SRS_PP_CENTRAL_MERIDIAN }, + {CF_PP_LONG_CENTRAL_MERIDIAN, SRS_PP_LONGITUDE_OF_CENTER }, + {CF_PP_LON_PROJ_ORIGIN, SRS_PP_LONGITUDE_OF_ORIGIN }, + //Multiple mappings to LAT_PROJ_ORIGIN + {CF_PP_LAT_PROJ_ORIGIN, SRS_PP_LATITUDE_OF_ORIGIN }, + {CF_PP_LAT_PROJ_ORIGIN, SRS_PP_LATITUDE_OF_CENTER }, + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL }, +}; + +// Albers equal area +// +// grid_mapping_name = albers_conical_equal_area +// WKT: Albers_Conic_Equal_Area +// ESPG:9822 +// +// Map parameters: +// +// * standard_parallel - There may be 1 or 2 values. +// * longitude_of_central_meridian +// * latitude_of_projection_origin +// * false_easting +// * false_northing +// +static const oNetcdfSRS_PP poAEAMappings[] = { + {CF_PP_STD_PARALLEL_1, SRS_PP_STANDARD_PARALLEL_1}, + {CF_PP_STD_PARALLEL_2, SRS_PP_STANDARD_PARALLEL_2}, + {CF_PP_LAT_PROJ_ORIGIN, SRS_PP_LATITUDE_OF_CENTER}, + {CF_PP_LONG_CENTRAL_MERIDIAN, SRS_PP_LONGITUDE_OF_CENTER}, + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL} + }; + +// Azimuthal equidistant +// +// grid_mapping_name = azimuthal_equidistant +// WKT: Azimuthal_Equidistant +// +// Map parameters: +// +// * longitude_of_projection_origin +// * latitude_of_projection_origin +// * false_easting +// * false_northing +// +static const oNetcdfSRS_PP poAEMappings[] = { + {CF_PP_LAT_PROJ_ORIGIN, SRS_PP_LATITUDE_OF_CENTER}, + {CF_PP_LON_PROJ_ORIGIN, SRS_PP_LONGITUDE_OF_CENTER}, + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL} + }; + +// Lambert azimuthal equal area +// +// grid_mapping_name = lambert_azimuthal_equal_area +// WKT: Lambert_Azimuthal_Equal_Area +// +// Map parameters: +// +// * longitude_of_projection_origin +// * latitude_of_projection_origin +// * false_easting +// * false_northing +// +static const oNetcdfSRS_PP poLAEAMappings[] = { + {CF_PP_LAT_PROJ_ORIGIN, SRS_PP_LATITUDE_OF_CENTER}, + {CF_PP_LON_PROJ_ORIGIN, SRS_PP_LONGITUDE_OF_CENTER}, + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL} + }; + +// Lambert conformal +// +// grid_mapping_name = lambert_conformal_conic +// WKT: Lambert_Conformal_Conic_1SP / Lambert_Conformal_Conic_2SP +// +// Map parameters: +// +// * standard_parallel - There may be 1 or 2 values. +// * longitude_of_central_meridian +// * latitude_of_projection_origin +// * false_easting +// * false_northing +// +// See http://www.remotesensing.org/geotiff/proj_list/lambert_conic_conformal_1sp.html + +// Lambert conformal conic - 1SP +/* See bug # 3324 + It seems that the missing scale factor can be computed from standard_parallel1 and latitude_of_projection_origin. + If both are equal (the common case) then scale factor=1, else use Snyder eq. 15-4. + We save in the WKT standard_parallel1 for export to CF, but do not export scale factor. + If a WKT has a scale factor != 1 and no standard_parallel1 then export is not CF, but we output scale factor for compat. + is there a formula for that? +*/ +static const oNetcdfSRS_PP poLCC1SPMappings[] = { + {CF_PP_STD_PARALLEL_1, SRS_PP_STANDARD_PARALLEL_1}, + {CF_PP_LAT_PROJ_ORIGIN, SRS_PP_LATITUDE_OF_ORIGIN}, + {CF_PP_LONG_CENTRAL_MERIDIAN, SRS_PP_CENTRAL_MERIDIAN}, + {CF_PP_SCALE_FACTOR_ORIGIN, SRS_PP_SCALE_FACTOR}, /* special case */ + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL} + }; + +// Lambert conformal conic - 2SP +static const oNetcdfSRS_PP poLCC2SPMappings[] = { + {CF_PP_STD_PARALLEL_1, SRS_PP_STANDARD_PARALLEL_1}, + {CF_PP_STD_PARALLEL_2, SRS_PP_STANDARD_PARALLEL_2}, + {CF_PP_LAT_PROJ_ORIGIN, SRS_PP_LATITUDE_OF_ORIGIN}, + {CF_PP_LONG_CENTRAL_MERIDIAN, SRS_PP_CENTRAL_MERIDIAN}, + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL} + }; + +// Lambert cylindrical equal area +// +// grid_mapping_name = lambert_cylindrical_equal_area +// WKT: Cylindrical_Equal_Area +// EPSG:9834 (Spherical) and EPSG:9835 +// +// Map parameters: +// +// * longitude_of_central_meridian +// * either standard_parallel or scale_factor_at_projection_origin +// * false_easting +// * false_northing +// +// NB: CF-1 specifies a 'scale_factor_at_projection' alternative +// to std_parallel ... but no reference to this in EPSG/remotesensing.org +// ignore for now. +// +static const oNetcdfSRS_PP poLCEAMappings[] = { + {CF_PP_STD_PARALLEL_1, SRS_PP_STANDARD_PARALLEL_1}, + {CF_PP_LONG_CENTRAL_MERIDIAN, SRS_PP_CENTRAL_MERIDIAN}, + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL} + }; + +// Latitude-Longitude +// +// grid_mapping_name = latitude_longitude +// +// Map parameters: +// +// * None +// +// NB: handled as a special case - !isProjected() + + +// Mercator +// +// grid_mapping_name = mercator +// WKT: Mercator_1SP / Mercator_2SP +// +// Map parameters: +// +// * longitude_of_projection_origin +// * either standard_parallel or scale_factor_at_projection_origin +// * false_easting +// * false_northing + +// Mercator 1 Standard Parallel (EPSG:9804) +static const oNetcdfSRS_PP poM1SPMappings[] = { + {CF_PP_LON_PROJ_ORIGIN, SRS_PP_CENTRAL_MERIDIAN}, + //LAT_PROJ_ORIGIN is always equator (0) in CF-1 + {CF_PP_SCALE_FACTOR_ORIGIN, SRS_PP_SCALE_FACTOR}, + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL} + }; + +// Mercator 2 Standard Parallel +static const oNetcdfSRS_PP poM2SPMappings[] = { + {CF_PP_LON_PROJ_ORIGIN, SRS_PP_CENTRAL_MERIDIAN}, + {CF_PP_STD_PARALLEL_1, SRS_PP_STANDARD_PARALLEL_1}, + //From best understanding of this projection, only + // actually specify one SP - it is the same N/S of equator. + //{CF_PP_STD_PARALLEL_2, SRS_PP_LATITUDE_OF_ORIGIN}, + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL} + }; + +// Orthographic +// grid_mapping_name = orthographic +// WKT: Orthographic +// +// Map parameters: +// +// * longitude_of_projection_origin +// * latitude_of_projection_origin +// * false_easting +// * false_northing +// +static const oNetcdfSRS_PP poOrthoMappings[] = { + {CF_PP_LAT_PROJ_ORIGIN, SRS_PP_LATITUDE_OF_ORIGIN}, + {CF_PP_LON_PROJ_ORIGIN, SRS_PP_CENTRAL_MERIDIAN}, + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL} + }; + +// Polar stereographic +// +// grid_mapping_name = polar_stereographic +// WKT: Polar_Stereographic +// +// Map parameters: +// +// * straight_vertical_longitude_from_pole +// * latitude_of_projection_origin - Either +90. or -90. +// * Either standard_parallel or scale_factor_at_projection_origin +// * false_easting +// * false_northing + +/* + (http://www.remotesensing.org/geotiff/proj_list/polar_stereographic.html) + + Note: Projection parameters for this projection are quite different in CF-1 from + OGC WKT/GeoTiff (for the latter, see above). + From our best understanding, this projection requires more than a straight mapping: + - As defined below, 'latitude_of_origin' (WKT) -> 'standard_parallel' (CF-1) + and 'central_meridian' (WKT) -> 'straight_vertical_longitude_from_pole' (CF-1) + - Then the 'latitude_of_projection_origin' in CF-1 must be set to either +90 or -90, + depending on the sign of 'latitude_of_origin' in WKT. + CF allows the use of standard_parallel (lat_ts in proj4) OR scale_factor (k0 in proj4). + This is analogous to the B and A variants (resp.) in EPSG guidelines. + When importing a CF file with scale_factor, we compute standard_parallel using + Snyder eq. 22-7 with k=1 and lat=standard_parallel. + Currently OGR does NOT relate the scale factor with the standard parallel, so we + use the default. It seems that proj4 uses lat_ts (standard_parallel) and not k0. +*/ +static const oNetcdfSRS_PP poPSmappings[] = { + {CF_PP_STD_PARALLEL_1, SRS_PP_LATITUDE_OF_ORIGIN}, + /* {CF_PP_SCALE_FACTOR_ORIGIN, SRS_PP_SCALE_FACTOR}, */ + {CF_PP_VERT_LONG_FROM_POLE, SRS_PP_CENTRAL_MERIDIAN}, + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL} +}; + +// Rotated Pole +// +// grid_mapping_name = rotated_latitude_longitude +// WKT: N/A +// +// Map parameters: +// +// * grid_north_pole_latitude +// * grid_north_pole_longitude +// * north_pole_grid_longitude - This parameter is optional (default is 0.). + +/* TODO: No GDAL equivalent of rotated pole? Doesn't seem to have an EPSG + code or WKT ... so unless some advanced proj4 features can be used + seems to rule out. + see GDAL bug #4285 for a possible fix or workaround +*/ + +// Stereographic +// +// grid_mapping_name = stereographic +// WKT: Stereographic (and/or Oblique_Stereographic??) +// +// Map parameters: +// +// * longitude_of_projection_origin +// * latitude_of_projection_origin +// * scale_factor_at_projection_origin +// * false_easting +// * false_northing +// +// NB: see bug#4267 Stereographic vs. Oblique_Stereographic +// +static const oNetcdfSRS_PP poStMappings[] = { + {CF_PP_LAT_PROJ_ORIGIN, SRS_PP_LATITUDE_OF_ORIGIN}, + {CF_PP_LON_PROJ_ORIGIN, SRS_PP_CENTRAL_MERIDIAN}, + {CF_PP_SCALE_FACTOR_ORIGIN, SRS_PP_SCALE_FACTOR}, + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL} + }; + +// Transverse Mercator +// +// grid_mapping_name = transverse_mercator +// WKT: Transverse_Mercator +// +// Map parameters: +// +// * scale_factor_at_central_meridian +// * longitude_of_central_meridian +// * latitude_of_projection_origin +// * false_easting +// * false_northing +// +static const oNetcdfSRS_PP poTMMappings[] = { + {CF_PP_SCALE_FACTOR_MERIDIAN, SRS_PP_SCALE_FACTOR}, + {CF_PP_LONG_CENTRAL_MERIDIAN, SRS_PP_CENTRAL_MERIDIAN}, + {CF_PP_LAT_PROJ_ORIGIN, SRS_PP_LATITUDE_OF_ORIGIN}, + {CF_PP_FALSE_EASTING, SRS_PP_FALSE_EASTING }, + {CF_PP_FALSE_NORTHING, SRS_PP_FALSE_NORTHING }, + {NULL, NULL} + }; + +// Vertical perspective +// +// grid_mapping_name = vertical_perspective +// WKT: ??? +// +// Map parameters: +// +// * latitude_of_projection_origin +// * longitude_of_projection_origin +// * perspective_point_height +// * false_easting +// * false_northing +// +// TODO: see how to map this to OGR + + +/* Mappings for various projections, including netcdf and GDAL projection names + and corresponding oNetcdfSRS_PP mapping struct. + A NULL mappings value means that the projection is not included in the CF + standard and the generic mapping (poGenericMappings) will be used. */ +typedef struct { + const char *CF_SRS; + const char *WKT_SRS; + const oNetcdfSRS_PP* mappings; +} oNetcdfSRS_PT; + +static const oNetcdfSRS_PT poNetcdfSRS_PT[] = { + {CF_PT_AEA, SRS_PT_ALBERS_CONIC_EQUAL_AREA, poAEAMappings }, + {CF_PT_AE, SRS_PT_AZIMUTHAL_EQUIDISTANT, poAEMappings }, + {"cassini_soldner", SRS_PT_CASSINI_SOLDNER, NULL }, + {CF_PT_LCEA, SRS_PT_CYLINDRICAL_EQUAL_AREA, poLCEAMappings }, + {"eckert_iv", SRS_PT_ECKERT_IV, NULL }, + {"eckert_vi", SRS_PT_ECKERT_VI, NULL }, + {"equidistant_conic", SRS_PT_EQUIDISTANT_CONIC, NULL }, + {"equirectangular", SRS_PT_EQUIRECTANGULAR, NULL }, + {"gall_stereographic", SRS_PT_GALL_STEREOGRAPHIC, NULL }, + {"geostationary_satellite", SRS_PT_GEOSTATIONARY_SATELLITE, NULL }, + {"goode_homolosine", SRS_PT_GOODE_HOMOLOSINE, NULL }, + {"gnomonic", SRS_PT_GNOMONIC, NULL }, + {"hotine_oblique_mercator", SRS_PT_HOTINE_OBLIQUE_MERCATOR, NULL }, + {"hotine_oblique_mercator_2P", + SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN, NULL }, + {"laborde_oblique_mercator", SRS_PT_LABORDE_OBLIQUE_MERCATOR, NULL }, + {CF_PT_LCC, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP, poLCC1SPMappings }, + {CF_PT_LCC, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP, poLCC2SPMappings }, + {CF_PT_LAEA, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA, poLAEAMappings }, + {CF_PT_MERCATOR, SRS_PT_MERCATOR_1SP, poM1SPMappings }, + {CF_PT_MERCATOR, SRS_PT_MERCATOR_2SP, poM2SPMappings }, + {"miller_cylindrical", SRS_PT_MILLER_CYLINDRICAL, NULL }, + {"mollweide", SRS_PT_MOLLWEIDE, NULL }, + {"new_zealand_map_grid", SRS_PT_NEW_ZEALAND_MAP_GRID, NULL }, + /* for now map to STEREO, see bug #4267 */ + {"oblique_stereographic", SRS_PT_OBLIQUE_STEREOGRAPHIC, NULL }, + /* {STEREO, SRS_PT_OBLIQUE_STEREOGRAPHIC, poStMappings }, */ + {CF_PT_ORTHOGRAPHIC, SRS_PT_ORTHOGRAPHIC, poOrthoMappings }, + {CF_PT_POLAR_STEREO, SRS_PT_POLAR_STEREOGRAPHIC, poPSmappings }, + {"polyconic", SRS_PT_POLYCONIC, NULL }, + {"robinson", SRS_PT_ROBINSON, NULL }, + {"sinusoidal", SRS_PT_SINUSOIDAL, NULL }, + {CF_PT_STEREO, SRS_PT_STEREOGRAPHIC, poStMappings }, + {"swiss_oblique_cylindrical", SRS_PT_SWISS_OBLIQUE_CYLINDRICAL, NULL }, + {CF_PT_TM, SRS_PT_TRANSVERSE_MERCATOR, poTMMappings }, + {"TM_south_oriented", SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED, NULL }, + {NULL, NULL, NULL }, +}; + +/************************************************************************/ +/* ==================================================================== */ +/* netCDFDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class netCDFRasterBand; + +class netCDFDataset : public GDALPamDataset +{ + friend class netCDFRasterBand; //TMP + + /* basic dataset vars */ + CPLString osFilename; + int cdfid; + char **papszSubDatasets; + char **papszMetadata; + CPLStringList papszDimName; + bool bBottomUp; + int nFormat; + int bIsGdalFile; /* was this file created by GDAL? */ + int bIsGdalCfFile; /* was this file created by the (new) CF-compliant driver? */ + char *pszCFProjection; + char *pszCFCoordinates; + + /* projection/GT */ + double adfGeoTransform[6]; + char *pszProjection; + int nXDimID; + int nYDimID; + int bIsProjected; + int bIsGeographic; + + /* state vars */ + int status; + int bDefineMode; + int bSetProjection; + int bSetGeoTransform; + int bAddedProjectionVars; + int bAddedGridMappingRef; + + /* create vars */ + char **papszCreationOptions; + int nCompress; + int nZLevel; + int bChunking; + int nCreateMode; + int bSignedData; + + double rint( double ); + + double FetchCopyParm( const char *pszGridMappingValue, + const char *pszParm, double dfDefault ); + + char ** FetchStandardParallels( const char *pszGridMappingValue ); + + void ProcessCreationOptions( ); + int DefVarDeflate( int nVarId, int bChunkingArg=TRUE ); + CPLErr AddProjectionVars( GDALProgressFunc pfnProgress=GDALDummyProgress, + void * pProgressData=NULL ); + void AddGridMappingRef(); + + int GetDefineMode() { return bDefineMode; } + int SetDefineMode( int bNewDefineMode ); + + CPLErr ReadAttributes( int, int ); + + void CreateSubDatasetList( ); + + void SetProjectionFromVar( int ); + + int ProcessCFGeolocation( int ); + CPLErr Set1DGeolocation( int nVarId, const char *szDimName ); + double * Get1DGeolocation( const char *szDimName, int &nVarLen ); + + protected: + + CPLXMLNode *SerializeToXML( const char *pszVRTPath ); + + public: + + netCDFDataset( ); + ~netCDFDataset( ); + + /* Projection/GT */ + CPLErr GetGeoTransform( double * ); + CPLErr SetGeoTransform (double *); + const char * GetProjectionRef(); + CPLErr SetProjection (const char *); + + virtual char **GetMetadataDomainList(); + char ** GetMetadata( const char * ); + + int GetCDFID() { return cdfid; } + + /* static functions */ + static int Identify( GDALOpenInfo * ); + static int IdentifyFormat( GDALOpenInfo *, bool ); + static GDALDataset *Open( GDALOpenInfo * ); + + static netCDFDataset *CreateLL( const char * pszFilename, + int nXSize, int nYSize, int nBands, + char ** papszOptions ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ); + static GDALDataset* CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); + +}; + +#endif diff --git a/bazaar/plugin/gdal/frmts/ngsgeoid/GNUmakefile b/bazaar/plugin/gdal/frmts/ngsgeoid/GNUmakefile new file mode 100644 index 000000000..c0273bf26 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ngsgeoid/GNUmakefile @@ -0,0 +1,13 @@ + +OBJ = ngsgeoiddataset.o + +include ../../GDALmake.opt + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/ngsgeoid/frmt_ngsgeoid.html b/bazaar/plugin/gdal/frmts/ngsgeoid/frmt_ngsgeoid.html new file mode 100644 index 000000000..f30ea7dae --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ngsgeoid/frmt_ngsgeoid.html @@ -0,0 +1,25 @@ + + +NGSGEOID - NOAA NGS Geoid Height Grids + + + + +

    NGSGEOID - NOAA NGS Geoid Height Grids

    + +(Available for GDAL >= 1.9.0) +

    +GDAL supports reading NOAA NGS geoid height grids in binary format (.bin files). Those files +can be used for vertical datum transformations. +

    + +

    See also

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/ngsgeoid/makefile.vc b/bazaar/plugin/gdal/frmts/ngsgeoid/makefile.vc new file mode 100644 index 000000000..72cd3b7cc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ngsgeoid/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = ngsgeoiddataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/ngsgeoid/ngsgeoiddataset.cpp b/bazaar/plugin/gdal/frmts/ngsgeoid/ngsgeoiddataset.cpp new file mode 100644 index 000000000..2ca4d68c8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ngsgeoid/ngsgeoiddataset.cpp @@ -0,0 +1,429 @@ +/****************************************************************************** + * $Id: ngsgeoiddataset.cpp 28202 2014-12-23 20:12:45Z rouault $ + * + * Project: NGSGEOID driver + * Purpose: GDALDataset driver for NGSGEOID dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "gdal_pam.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: ngsgeoiddataset.cpp 28202 2014-12-23 20:12:45Z rouault $"); + +CPL_C_START +void GDALRegister_NGSGEOID(void); +CPL_C_END + +#define HEADER_SIZE (4 * 8 + 3 * 4) + +/************************************************************************/ +/* ==================================================================== */ +/* NGSGEOIDDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class NGSGEOIDRasterBand; + +class NGSGEOIDDataset : public GDALPamDataset +{ + friend class NGSGEOIDRasterBand; + + VSILFILE *fp; + double adfGeoTransform[6]; + int bIsLittleEndian; + + static int GetHeaderInfo( const GByte* pBuffer, + double* padfGeoTransform, + int* pnRows, + int* pnCols, + int* pbIsLittleEndian ); + + public: + NGSGEOIDDataset(); + virtual ~NGSGEOIDDataset(); + + virtual CPLErr GetGeoTransform( double * ); + virtual const char* GetProjectionRef(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* NGSGEOIDRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class NGSGEOIDRasterBand : public GDALPamRasterBand +{ + friend class NGSGEOIDDataset; + + public: + + NGSGEOIDRasterBand( NGSGEOIDDataset * ); + + virtual CPLErr IReadBlock( int, int, void * ); + + virtual const char* GetUnitType() { return "m"; } +}; + + +/************************************************************************/ +/* NGSGEOIDRasterBand() */ +/************************************************************************/ + +NGSGEOIDRasterBand::NGSGEOIDRasterBand( NGSGEOIDDataset *poDS ) + +{ + this->poDS = poDS; + this->nBand = 1; + + eDataType = GDT_Float32; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr NGSGEOIDRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) + +{ + NGSGEOIDDataset *poGDS = (NGSGEOIDDataset *) poDS; + + /* First values in the file corresponds to the south-most line of the imagery */ + VSIFSeekL(poGDS->fp, + HEADER_SIZE + (nRasterYSize - 1 - nBlockYOff) * nRasterXSize * 4, + SEEK_SET); + + if ((int)VSIFReadL(pImage, 4, nRasterXSize, poGDS->fp) != nRasterXSize) + return CE_Failure; + + if (poGDS->bIsLittleEndian) + { +#ifdef CPL_MSB + GDALSwapWords( pImage, 4, nRasterXSize, 4 ); +#endif + } + else + { +#ifdef CPL_LSB + GDALSwapWords( pImage, 4, nRasterXSize, 4 ); +#endif + } + + return CE_None; +} + +/************************************************************************/ +/* ~NGSGEOIDDataset() */ +/************************************************************************/ + +NGSGEOIDDataset::NGSGEOIDDataset() +{ + fp = NULL; + adfGeoTransform[0] = 0; + adfGeoTransform[1] = 1; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = 0; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = 1; + bIsLittleEndian = TRUE; +} + +/************************************************************************/ +/* ~NGSGEOIDDataset() */ +/************************************************************************/ + +NGSGEOIDDataset::~NGSGEOIDDataset() + +{ + FlushCache(); + if (fp) + VSIFCloseL(fp); +} + +/************************************************************************/ +/* GetHeaderInfo() */ +/************************************************************************/ + +int NGSGEOIDDataset::GetHeaderInfo( const GByte* pBuffer, + double* padfGeoTransform, + int* pnRows, + int* pnCols, + int* pbIsLittleEndian ) +{ + double dfSLAT; + double dfWLON; + double dfDLAT; + double dfDLON; + int nNLAT; + int nNLON; + int nIKIND; + + /* First check IKIND marker to determine if the file */ + /* is in little or big-endian order, and if it is a valid */ + /* NGSGEOID dataset */ + memcpy(&nIKIND, pBuffer + HEADER_SIZE - 4, 4); + CPL_LSBPTR32(&nIKIND); + if (nIKIND == 1) + { + *pbIsLittleEndian = TRUE; + } + else + { + memcpy(&nIKIND, pBuffer + HEADER_SIZE - 4, 4); + CPL_MSBPTR32(&nIKIND); + if (nIKIND == 1) + { + *pbIsLittleEndian = FALSE; + } + else + { + return FALSE; + } + } + + memcpy(&dfSLAT, pBuffer, 8); + if (*pbIsLittleEndian) + { + CPL_LSBPTR64(&dfSLAT); + } + else + { + CPL_MSBPTR64(&dfSLAT); + } + pBuffer += 8; + memcpy(&dfWLON, pBuffer, 8); + if (*pbIsLittleEndian) + { + CPL_LSBPTR64(&dfWLON); + } + else + { + CPL_MSBPTR64(&dfWLON); + } + pBuffer += 8; + memcpy(&dfDLAT, pBuffer, 8); + if (*pbIsLittleEndian) + { + CPL_LSBPTR64(&dfDLAT); + } + else + { + CPL_MSBPTR64(&dfDLAT); + } + pBuffer += 8; + memcpy(&dfDLON, pBuffer, 8); + if (*pbIsLittleEndian) + { + CPL_LSBPTR64(&dfDLON); + } + else + { + CPL_MSBPTR64(&dfDLON); + } + pBuffer += 8; + memcpy(&nNLAT, pBuffer, 4); + if (*pbIsLittleEndian) + { + CPL_LSBPTR32(&nNLAT); + } + else + { + CPL_MSBPTR32(&nNLAT); + } + pBuffer += 4; + memcpy(&nNLON, pBuffer, 4); + if (*pbIsLittleEndian) + { + CPL_LSBPTR32(&nNLON); + } + else + { + CPL_MSBPTR32(&nNLON); + } + pBuffer += 4; + + /*CPLDebug("NGSGEOID", "SLAT=%f, WLON=%f, DLAT=%f, DLON=%f, NLAT=%d, NLON=%d, IKIND=%d", + dfSLAT, dfWLON, dfDLAT, dfDLON, nNLAT, nNLON, nIKIND);*/ + + if (nNLAT <= 0 || nNLON <= 0 || dfDLAT <= 1e-15 || dfDLON <= 1e-15) + return FALSE; + + /* Grids go over +180 in longitude */ + if (dfSLAT < -90.0 || dfSLAT + nNLAT * dfDLAT > 90.0 || + dfWLON < -180.0 || dfWLON + nNLON * dfDLON > 360.0) + return FALSE; + + padfGeoTransform[0] = dfWLON - dfDLON / 2; + padfGeoTransform[1] = dfDLON; + padfGeoTransform[2] = 0.0; + padfGeoTransform[3] = dfSLAT + nNLAT * dfDLAT - dfDLAT / 2; + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = -dfDLAT; + + *pnRows = nNLAT; + *pnCols = nNLON; + + return TRUE; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int NGSGEOIDDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + if (poOpenInfo->nHeaderBytes < HEADER_SIZE) + return FALSE; + + double adfGeoTransform[6]; + int nRows, nCols; + int bIsLittleEndian; + if ( !GetHeaderInfo( poOpenInfo->pabyHeader, + adfGeoTransform, + &nRows, &nCols, &bIsLittleEndian ) ) + return FALSE; + + return TRUE; +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *NGSGEOIDDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if (!Identify(poOpenInfo)) + return NULL; + + if (poOpenInfo->eAccess == GA_Update) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The NGSGEOID driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + VSILFILE* fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + if (fp == NULL) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + NGSGEOIDDataset *poDS; + + poDS = new NGSGEOIDDataset(); + poDS->fp = fp; + + int nRows, nCols; + GetHeaderInfo( poOpenInfo->pabyHeader, + poDS->adfGeoTransform, + &nRows, + &nCols, + &poDS->bIsLittleEndian ); + poDS->nRasterXSize = nCols; + poDS->nRasterYSize = nRows; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = 1; + poDS->SetBand( 1, new NGSGEOIDRasterBand( poDS ) ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr NGSGEOIDDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double)); + + return( CE_None ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* NGSGEOIDDataset::GetProjectionRef() +{ + return SRS_WKT_WGS84; +} + +/************************************************************************/ +/* GDALRegister_NGSGEOID() */ +/************************************************************************/ + +void GDALRegister_NGSGEOID() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "NGSGEOID" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "NGSGEOID" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "NOAA NGS Geoid Height Grids" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_ngsgeoid.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "bin" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = NGSGEOIDDataset::Open; + poDriver->pfnIdentify = NGSGEOIDDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/nitf/GNUmakefile b/bazaar/plugin/gdal/frmts/nitf/GNUmakefile new file mode 100644 index 000000000..411f933e8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/nitf/GNUmakefile @@ -0,0 +1,47 @@ + + +include ../../GDALmake.opt + +GDAL_OBJ = nitfdataset.o rpftocdataset.o nitfwritejpeg.o \ + nitfwritejpeg_12.o nitfrasterband.o ecrgtocdataset.o +NITFLIB_OBJ = nitffile.o nitfimage.o mgrs.o nitfaridpcm.o \ + nitfbilevel.o rpftocfile.o nitfdes.o nitf_gcprpc.o + +CPPFLAGS := -I../vrt -I../gtiff $(CPPFLAGS) + +ifeq ($(TIFF_SETTING),internal) +ifeq ($(RENAME_INTERNAL_LIBTIFF_SYMBOLS),yes) +CPPFLAGS := $(CPPFLAGS) -DRENAME_INTERNAL_LIBTIFF_SYMBOLS +endif +CPPFLAGS := -I../gtiff/libtiff $(CPPFLAGS) +endif + +ifneq ($(JPEG_SETTING),no) +CPPFLAGS := $(CPPFLAGS) -DJPEG_SUPPORTED +endif + +ifeq ($(JPEG_SETTING),internal) +CPPFLAGS := -I../jpeg/libjpeg $(CPPFLAGS) +endif + +ifeq ($(JPEG12_ENABLED),yes) +CPPFLAGS := $(CPPFLAGS) -DJPEG_DUAL_MODE_8_12 +PREDEPEND = libjpeg12 +endif + +OBJ = $(GDAL_OBJ) $(NITFLIB_OBJ) + +default: $(PREDEPEND) $(OBJ:.o=.$(OBJ_EXT)) + +$(OBJ) $(O_OBJ): ../../gcore/gdal_proxy.h ../vrt/vrtdataset.h nitflib.h nitfwritejpeg.cpp + +clean: + rm -f *.o $(O_OBJ) nitfdump$(EXE) + +nitfdump$(EXE): nitfdump.$(OBJ_EXT) + $(LD) $(LDFLAGS) nitfdump.$(OBJ_EXT) $(CONFIG_LIBS) -o nitfdump$(EXE) + +install-obj: $(PREDEPEND) $(O_OBJ:.o=.$(OBJ_EXT)) + +libjpeg12: + (cd ../jpeg; $(MAKE) libjpeg12/jcapimin12.c) diff --git a/bazaar/plugin/gdal/frmts/nitf/ecrgtocdataset.cpp b/bazaar/plugin/gdal/frmts/nitf/ecrgtocdataset.cpp new file mode 100644 index 000000000..01c1de8d9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/nitf/ecrgtocdataset.cpp @@ -0,0 +1,1057 @@ +/****************************************************************************** + * $Id: ecrgtocdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: ECRG TOC read Translator + * Purpose: Implementation of ECRGTOCDataset and ECRGTOCSubDataset. + * Author: Even Rouault, even.rouault at mines-paris.org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +// g++ -g -Wall -fPIC frmts/nitf/ecrgtocdataset.cpp -shared -o gdal_ECRGTOC.so -Iport -Igcore -Iogr -Ifrmts/vrt -L. -lgdal + +#include "gdal_proxy.h" +#include "ogr_srs_api.h" +#include "vrtdataset.h" +#include "cpl_minixml.h" +#include + +CPL_CVSID("$Id: ecrgtocdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/** Overview of used classes : + - ECRGTOCDataset : lists the different subdatasets, listed in the .xml, + as subdatasets + - ECRGTOCSubDataset : one of these subdatasets, implemented as a VRT, of + the relevant NITF tiles + - ECRGTOCProxyRasterDataSet : a "proxy" dataset that maps to a NITF tile +*/ + +typedef struct +{ + const char* pszName; + const char* pszPath; + int nScale; + int nZone; +} FrameDesc; + +/************************************************************************/ +/* ==================================================================== */ +/* ECRGTOCDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class ECRGTOCDataset : public GDALPamDataset +{ + char **papszSubDatasets; + double adfGeoTransform[6]; + + char **papszFileList; + + public: + ECRGTOCDataset() + { + papszSubDatasets = NULL; + papszFileList = NULL; + } + + ~ECRGTOCDataset() + { + CSLDestroy( papszSubDatasets ); + CSLDestroy(papszFileList); + } + + virtual char **GetMetadata( const char * pszDomain = "" ); + + virtual char **GetFileList() { return CSLDuplicate(papszFileList); } + + void AddSubDataset(const char* pszFilename, + const char* pszProductTitle, + const char* pszDiscId); + + virtual CPLErr GetGeoTransform( double * padfGeoTransform) + { + memcpy(padfGeoTransform, adfGeoTransform, 6 * sizeof(double)); + return CE_None; + } + + virtual const char *GetProjectionRef(void) + { + return SRS_WKT_WGS84; + } + + static GDALDataset* Build( const char* pszTOCFilename, + CPLXMLNode* psXML, + CPLString osProduct, + CPLString osDiscId, + const char* pszFilename); + + static int Identify( GDALOpenInfo * poOpenInfo ); + static GDALDataset* Open( GDALOpenInfo * poOpenInfo ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* ECRGTOCSubDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class ECRGTOCSubDataset : public VRTDataset +{ + char** papszFileList; + + public: + ECRGTOCSubDataset(int nXSize, int nYSize) : VRTDataset(nXSize, nYSize) + { + /* Don't try to write a VRT file */ + SetWritable(FALSE); + + /* The driver is set to VRT in VRTDataset constructor. */ + /* We have to set it to the expected value ! */ + poDriver = (GDALDriver *) GDALGetDriverByName( "ECRGTOC" ); + + papszFileList = NULL; + } + + ~ECRGTOCSubDataset() + { + CSLDestroy(papszFileList); + } + + virtual char **GetFileList() { return CSLDuplicate(papszFileList); } + + static GDALDataset* Build( const char* pszProductTitle, + const char* pszDiscId, + int nScale, + int nCountSubDataset, + const char* pszTOCFilename, + const std::vector& aosFrameDesc, + double dfGlobalMinX, + double dfGlobalMinY, + double dfGlobalMaxX, + double dfGlobalMaxY, + double dfGlobalPixelXSize, + double dfGlobalPixelYSize); +}; + +/************************************************************************/ +/* AddSubDataset() */ +/************************************************************************/ + +void ECRGTOCDataset::AddSubDataset( const char* pszFilename, + const char* pszProductTitle, + const char* pszDiscId ) + +{ + char szName[80]; + int nCount = CSLCount(papszSubDatasets ) / 2; + + sprintf( szName, "SUBDATASET_%d_NAME", nCount+1 ); + papszSubDatasets = + CSLSetNameValue( papszSubDatasets, szName, + CPLSPrintf( "ECRG_TOC_ENTRY:%s:%s:%s", + pszProductTitle, pszDiscId, pszFilename ) ); + + sprintf( szName, "SUBDATASET_%d_DESC", nCount+1 ); + papszSubDatasets = + CSLSetNameValue( papszSubDatasets, szName, + CPLSPrintf( "%s:%s", pszProductTitle, pszDiscId)); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **ECRGTOCDataset::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain != NULL && EQUAL(pszDomain,"SUBDATASETS") ) + return papszSubDatasets; + + return GDALPamDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* GetScaleFromString() */ +/************************************************************************/ + +static int GetScaleFromString(const char* pszScale) +{ + const char* pszPtr = strstr(pszScale, "1:"); + if (pszPtr) + pszPtr = pszPtr + 2; + else + pszPtr = pszScale; + + int nScale = 0; + char ch; + while((ch = *pszPtr) != '\0') + { + if (ch >= '0' && ch <= '9') + nScale = nScale * 10 + ch - '0'; + else if (ch == ' ') + ; + else if (ch == 'k' || ch == 'K') + return nScale * 1000; + else if (ch == 'm' || ch == 'M') + return nScale * 1000000; + else + return 0; + pszPtr ++; + } + return nScale; +} + +/************************************************************************/ +/* GetFromBase34() */ +/************************************************************************/ + +static GIntBig GetFromBase34(const char* pszVal, int nMaxSize) +{ + int i; + GIntBig nFrameNumber = 0; + for(i=0;i= 'A' && ch <= 'Z') + ch += 'a' - 'A'; + /* i and o letters are excluded, */ + if (ch >= '0' && ch <= '9') + chVal = ch - '0'; + else if (ch >= 'a' && ch <= 'h') + chVal = ch - 'a' + 10; + else if (ch >= 'j' && ch < 'n') + chVal = ch - 'a' + 10 - 1; + else if (ch > 'p' && ch <= 'z') + chVal = ch - 'a' + 10 - 2; + else + { + CPLDebug("ECRG", "Invalid base34 value : %s", pszVal); + break; + } + nFrameNumber = nFrameNumber * 34 + chVal; + } + + return nFrameNumber; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +/* MIL-PRF-32283 - Table II. ECRG zone limits. */ +/* starting with a fake zone 0 for conveniency */ +static const int anZoneUpperLat[] = { 0, 32, 48, 56, 64, 68, 72, 76, 80 }; + +/* APPENDIX 70, TABLE III of MIL-A-89007 */ +static const int anACst_ADRG[] = + { 369664, 302592, 245760, 199168, 163328, 137216, 110080, 82432 }; +static const int nBCst_ADRG = 400384; + +#define CEIL_ROUND(a, b) (int)(ceil((double)(a)/(b))*(b)) +#define NEAR_ROUND(a, b) (int)(floor((double)(a)/(b) + 0.5)*(b)) + +#define ECRG_PIXELS 2304 + +static +int GetExtent(const char* pszFrameName, int nScale, int nZone, + double& dfMinX, double& dfMaxX, double& dfMinY, double& dfMaxY, + double& dfPixelXSize, double& dfPixelYSize) +{ + int nAbsZone = abs(nZone); + +/************************************************************************/ +/* Compute east-west constant */ +/************************************************************************/ + /* MIL-PRF-89038 - 60.1.2 - East-west pixel constant. */ + int nEW_ADRG = CEIL_ROUND(anACst_ADRG[nAbsZone-1] * (1e6 / nScale), 512); + int nEW_CADRG = NEAR_ROUND(nEW_ADRG / (150. / 100.), 256); + /* MIL-PRF-32283 - D.2.1.2 - East-west pixel constant. */ + int nEW = nEW_CADRG / 256 * 384; + +/************************************************************************/ +/* Compute number of longitudinal frames */ +/************************************************************************/ + /* MIL-PRF-32283 - D.2.1.7 - Longitudinal frames and subframes */ + int nCols = (int)ceil((double)nEW / ECRG_PIXELS); + +/************************************************************************/ +/* Compute north-south constant */ +/************************************************************************/ + /* MIL-PRF-89038 - 60.1.1 - North-south. pixel constant */ + int nNS_ADRG = CEIL_ROUND(nBCst_ADRG * (1e6 / nScale), 512) / 4; + int nNS_CADRG = NEAR_ROUND(nNS_ADRG / (150. / 100.), 256); + /* MIL-PRF-32283 - D.2.1.1 - North-south. pixel constant and Frame Width/Height */ + int nNS = nNS_CADRG / 256 * 384; + +/************************************************************************/ +/* Compute number of latitudinal frames and latitude of top of zone */ +/************************************************************************/ + dfPixelYSize = 90.0 / nNS; + + double dfFrameLatHeight = dfPixelYSize * ECRG_PIXELS; + + /* MIL-PRF-32283 - D.2.1.5 - Equatorward and poleward zone extents. */ + int nUpperZoneFrames = (int)ceil(anZoneUpperLat[nAbsZone] / dfFrameLatHeight); + int nBottomZoneFrames = (int)floor(anZoneUpperLat[nAbsZone-1] / dfFrameLatHeight); + int nRows = nUpperZoneFrames - nBottomZoneFrames; + + /* Not sure to really understand D.2.1.5.a. Testing needed */ + if (nZone < 0) + { + nUpperZoneFrames = -nBottomZoneFrames; + nBottomZoneFrames = nUpperZoneFrames - nRows; + } + + double dfUpperZoneTopLat = dfFrameLatHeight * nUpperZoneFrames; + +/************************************************************************/ +/* Compute coordinates of the frame in the zone */ +/************************************************************************/ + + /* Converts the first 10 characters into a number from base 34 */ + GIntBig nFrameNumber = GetFromBase34(pszFrameName, 10); + + /* MIL-PRF-32283 - A.2.6.1 */ + GIntBig nY = nFrameNumber / nCols; + GIntBig nX = nFrameNumber % nCols; + +/************************************************************************/ +/* Compute extent of the frame */ +/************************************************************************/ + + /* The nY is counted from the bottom of the zone... Pfff */ + dfMaxY = dfUpperZoneTopLat - (nRows - 1 - nY) * dfFrameLatHeight; + dfMinY = dfMaxY - dfFrameLatHeight; + + dfPixelXSize = 360.0 / nEW; + + double dfFrameLongWidth = dfPixelXSize * ECRG_PIXELS; + dfMinX = -180.0 + nX * dfFrameLongWidth; + dfMaxX = dfMinX + dfFrameLongWidth; + + //CPLDebug("ECRG", "Frame %s : minx=%.16g, maxy=%.16g, maxx=%.16g, miny=%.16g", + // pszFrameName, dfMinX, dfMaxY, dfMaxX, dfMinY); + + return TRUE; +} + +/************************************************************************/ +/* ==================================================================== */ +/* ECRGTOCProxyRasterDataSet */ +/* ==================================================================== */ +/************************************************************************/ + +class ECRGTOCProxyRasterDataSet : public GDALProxyPoolDataset +{ + /* The following parameters are only for sanity checking */ + int checkDone; + int checkOK; + double dfMinX; + double dfMaxY; + double dfPixelXSize; + double dfPixelYSize; + ECRGTOCSubDataset* poSubDataset; + + public: + ECRGTOCProxyRasterDataSet(ECRGTOCSubDataset* poSubDataset, + const char* fileName, + int nXSize, int nYSize, + double dfMinX, double dfMaxY, + double dfPixelXSize, double dfPixelYSize); + + GDALDataset* RefUnderlyingDataset() + { + GDALDataset* poSourceDS = GDALProxyPoolDataset::RefUnderlyingDataset(); + if (poSourceDS) + { + if (!checkDone) + SanityCheckOK(poSourceDS); + if (!checkOK) + { + GDALProxyPoolDataset::UnrefUnderlyingDataset(poSourceDS); + poSourceDS = NULL; + } + } + return poSourceDS; + } + + void UnrefUnderlyingDataset(GDALDataset* poUnderlyingDataset) + { + GDALProxyPoolDataset::UnrefUnderlyingDataset(poUnderlyingDataset); + } + + int SanityCheckOK(GDALDataset* poSourceDS); +}; + +/************************************************************************/ +/* ECRGTOCProxyRasterDataSet() */ +/************************************************************************/ + +ECRGTOCProxyRasterDataSet::ECRGTOCProxyRasterDataSet + (ECRGTOCSubDataset* poSubDataset, + const char* fileName, + int nXSize, int nYSize, + double dfMinX, double dfMaxY, + double dfPixelXSize, double dfPixelYSize) : + /* Mark as shared since the VRT will take several references if we are in RGBA mode (4 bands for this dataset) */ + GDALProxyPoolDataset(fileName, nXSize, nYSize, GA_ReadOnly, TRUE, SRS_WKT_WGS84) +{ + int i; + this->poSubDataset = poSubDataset; + this->dfMinX = dfMinX; + this->dfMaxY = dfMaxY; + this->dfPixelXSize = dfPixelXSize; + this->dfPixelYSize = dfPixelYSize; + + checkDone = FALSE; + checkOK = FALSE; + + for(i=0;i<3;i++) + { + SetBand(i + 1, new GDALProxyPoolRasterBand(this, i+1, GDT_Byte, nXSize, 1)); + } +} + +/************************************************************************/ +/* SanityCheckOK() */ +/************************************************************************/ + +#define WARN_CHECK_DS(x) do { if (!(x)) { CPLError(CE_Warning, CPLE_AppDefined,\ + "For %s, assert '" #x "' failed", GetDescription()); checkOK = FALSE; } } while(0) + +int ECRGTOCProxyRasterDataSet::SanityCheckOK(GDALDataset* poSourceDS) +{ + /*int nSrcBlockXSize, nSrcBlockYSize; + int nBlockXSize, nBlockYSize;*/ + double adfGeoTransform[6]; + if (checkDone) + return checkOK; + + checkOK = TRUE; + checkDone = TRUE; + + poSourceDS->GetGeoTransform(adfGeoTransform); + WARN_CHECK_DS(fabs(adfGeoTransform[0] - dfMinX) < 1e-10); + WARN_CHECK_DS(fabs(adfGeoTransform[3] - dfMaxY) < 1e-10); + WARN_CHECK_DS(fabs(adfGeoTransform[1] - dfPixelXSize) < 1e-10); + WARN_CHECK_DS(fabs(adfGeoTransform[5] - (-dfPixelYSize)) < 1e-10); + WARN_CHECK_DS(adfGeoTransform[2] == 0 && + adfGeoTransform[4] == 0); /* No rotation */ + WARN_CHECK_DS(poSourceDS->GetRasterCount() == 3); + WARN_CHECK_DS(poSourceDS->GetRasterXSize() == nRasterXSize); + WARN_CHECK_DS(poSourceDS->GetRasterYSize() == nRasterYSize); + WARN_CHECK_DS(EQUAL(poSourceDS->GetProjectionRef(), SRS_WKT_WGS84)); + /*poSourceDS->GetRasterBand(1)->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize); + GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize); + WARN_CHECK_DS(nSrcBlockXSize == nBlockXSize); + WARN_CHECK_DS(nSrcBlockYSize == nBlockYSize);*/ + WARN_CHECK_DS(poSourceDS->GetRasterBand(1)->GetRasterDataType() == GDT_Byte); + + return checkOK; +} + +/************************************************************************/ +/* BuildFullName() */ +/************************************************************************/ + +static const char* BuildFullName(const char* pszTOCFilename, + const char* pszFramePath, + const char* pszFrameName) +{ + char* pszPath; + if (pszFramePath[0] == '.' && + (pszFramePath[1] == '/' ||pszFramePath[1] == '\\')) + pszPath = CPLStrdup(pszFramePath + 2); + else + pszPath = CPLStrdup(pszFramePath); + for(int i=0;pszPath[i] != '\0';i++) + { + if (pszPath[i] == '\\') + pszPath[i] = '/'; + } + const char* pszName = CPLFormFilename(pszPath, pszFrameName, NULL); + CPLFree(pszPath); + pszPath = NULL; + const char* pszTOCPath = CPLGetDirname(pszTOCFilename); + const char* pszFirstSlashInName = strchr(pszName, '/'); + if (pszFirstSlashInName != NULL) + { + int nFirstDirLen = pszFirstSlashInName - pszName; + if ((int)strlen(pszTOCPath) >= nFirstDirLen + 1 && + (pszTOCPath[strlen(pszTOCPath) - (nFirstDirLen + 1)] == '/' || + pszTOCPath[strlen(pszTOCPath) - (nFirstDirLen + 1)] == '\\') && + strncmp(pszTOCPath + strlen(pszTOCPath) - nFirstDirLen, pszName, nFirstDirLen) == 0) + { + pszTOCPath = CPLGetDirname(pszTOCPath); + } + } + return CPLProjectRelativeFilename(pszTOCPath, pszName); +} + +/************************************************************************/ +/* Build() */ +/************************************************************************/ + +/* Builds a ECRGTOCSubDataset from the set of files of the toc entry */ +GDALDataset* ECRGTOCSubDataset::Build( const char* pszProductTitle, + const char* pszDiscId, + int nScale, + int nCountSubDataset, + const char* pszTOCFilename, + const std::vector& aosFrameDesc, + double dfGlobalMinX, + double dfGlobalMinY, + double dfGlobalMaxX, + double dfGlobalMaxY, + double dfGlobalPixelXSize, + double dfGlobalPixelYSize) + { + int i, j; + GDALDriver *poDriver; + ECRGTOCSubDataset *poVirtualDS; + int nSizeX, nSizeY; + double adfGeoTransform[6]; + + poDriver = GetGDALDriverManager()->GetDriverByName("VRT"); + if( poDriver == NULL ) + return NULL; + + nSizeX = (int)((dfGlobalMaxX - dfGlobalMinX) / dfGlobalPixelXSize + 0.5); + nSizeY = (int)((dfGlobalMaxY - dfGlobalMinY) / dfGlobalPixelYSize + 0.5); + + /* ------------------------------------ */ + /* Create the VRT with the overall size */ + /* ------------------------------------ */ + poVirtualDS = new ECRGTOCSubDataset( nSizeX, nSizeY ); + + poVirtualDS->SetProjection(SRS_WKT_WGS84); + + adfGeoTransform[0] = dfGlobalMinX; + adfGeoTransform[1] = dfGlobalPixelXSize; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = dfGlobalMaxY; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = -dfGlobalPixelYSize; + poVirtualDS->SetGeoTransform(adfGeoTransform); + + for (i=0;i<3;i++) + { + poVirtualDS->AddBand(GDT_Byte, NULL); + GDALRasterBand *poBand = poVirtualDS->GetRasterBand( i + 1 ); + poBand->SetColorInterpretation((GDALColorInterp)(GCI_RedBand+i)); + } + + poVirtualDS->SetDescription(pszTOCFilename); + + poVirtualDS->SetMetadataItem("PRODUCT_TITLE", pszProductTitle); + poVirtualDS->SetMetadataItem("DISC_ID", pszDiscId); + if (nScale != -1) + poVirtualDS->SetMetadataItem("SCALE", CPLString().Printf("%d", nScale)); + + /* -------------------------------------------------------------------- */ + /* Check for overviews. */ + /* -------------------------------------------------------------------- */ + + poVirtualDS->oOvManager.Initialize( poVirtualDS, + CPLString().Printf("%s.%d", pszTOCFilename, nCountSubDataset)); + + poVirtualDS->papszFileList = poVirtualDS->GDALDataset::GetFileList(); + + for(i=0;i<(int)aosFrameDesc.size(); i++) + { + const char* pszName = BuildFullName(pszTOCFilename, + aosFrameDesc[i].pszPath, + aosFrameDesc[i].pszName); + + double dfMinX = 0, dfMaxX = 0, dfMinY = 0, dfMaxY = 0, + dfPixelXSize = 0, dfPixelYSize = 0; + GetExtent(aosFrameDesc[i].pszName, + aosFrameDesc[i].nScale, aosFrameDesc[i].nZone, + dfMinX, dfMaxX, dfMinY, dfMaxY, dfPixelXSize, dfPixelYSize); + + int nFrameXSize = (int)((dfMaxX - dfMinX) / dfPixelXSize + 0.5); + int nFrameYSize = (int)((dfMaxY - dfMinY) / dfPixelYSize + 0.5); + + poVirtualDS->papszFileList = CSLAddString(poVirtualDS->papszFileList, pszName); + + /* We create proxy datasets and raster bands */ + /* Using real datasets and raster bands is possible in theory */ + /* However for large datasets, a TOC entry can include several hundreds of files */ + /* and we finally reach the limit of maximum file descriptors open at the same time ! */ + /* So the idea is to warp the datasets into a proxy and open the underlying dataset only when it is */ + /* needed (IRasterIO operation). To improve a bit efficiency, we have a cache of opened */ + /* underlying datasets */ + ECRGTOCProxyRasterDataSet* poDS = new ECRGTOCProxyRasterDataSet( + (ECRGTOCSubDataset*)poVirtualDS, pszName, nFrameXSize, nFrameYSize, + dfMinX, dfMaxY, dfPixelXSize, dfPixelYSize); + + for(j=0;j<3;j++) + { + VRTSourcedRasterBand *poBand = (VRTSourcedRasterBand*) + poVirtualDS->GetRasterBand( j + 1 ); + /* Place the raster band at the right position in the VRT */ + poBand->AddSimpleSource(poDS->GetRasterBand(j + 1), + 0, 0, nFrameXSize, nFrameYSize, + (int)((dfMinX - dfGlobalMinX) / dfGlobalPixelXSize + 0.5), + (int)((dfGlobalMaxY - dfMaxY) / dfGlobalPixelYSize + 0.5), + (int)((dfMaxX - dfMinX) / dfGlobalPixelXSize + 0.5), + (int)((dfMaxY - dfMinY) / dfGlobalPixelYSize + 0.5)); + } + + /* The ECRGTOCProxyRasterDataSet will be destroyed when its last raster band will be */ + /* destroyed */ + poDS->Dereference(); + } + + poVirtualDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); + + return poVirtualDS; +} + +/************************************************************************/ +/* Build() */ +/************************************************************************/ + +GDALDataset* ECRGTOCDataset::Build(const char* pszTOCFilename, + CPLXMLNode* psXML, + CPLString osProduct, + CPLString osDiscId, + const char* pszOpenInfoFilename) +{ + CPLXMLNode* psTOC = CPLGetXMLNode(psXML, "=Table_of_Contents"); + if (psTOC == NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot find Table_of_Contents element"); + return NULL; + } + + double dfGlobalMinX = 0, dfGlobalMinY = 0, dfGlobalMaxX = 0, dfGlobalMaxY= 0; + double dfGlobalPixelXSize = 0, dfGlobalPixelYSize = 0; + int bGlobalExtentValid = FALSE; + + ECRGTOCDataset* poDS = new ECRGTOCDataset(); + int nSubDatasets = 0; + + int bLookForSubDataset = osProduct.size() != 0 && osDiscId.size() != 0; + + int nCountSubDataset = 0; + + poDS->SetDescription( pszOpenInfoFilename ); + poDS->papszFileList = poDS->GDALDataset::GetFileList(); + + for(CPLXMLNode* psIter1 = psTOC->psChild; + psIter1 != NULL; + psIter1 = psIter1->psNext) + { + if (!(psIter1->eType == CXT_Element && psIter1->pszValue != NULL && + strcmp(psIter1->pszValue, "product") == 0)) + continue; + + const char* pszProductTitle = + CPLGetXMLValue(psIter1, "product_title", NULL); + if (pszProductTitle == NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot find product_title attribute"); + continue; + } + + if (bLookForSubDataset && strcmp(pszProductTitle, osProduct.c_str()) != 0) + continue; + + for(CPLXMLNode* psIter2 = psIter1->psChild; + psIter2 != NULL; + psIter2 = psIter2->psNext) + { + if (!(psIter2->eType == CXT_Element && psIter2->pszValue != NULL && + strcmp(psIter2->pszValue, "disc") == 0)) + continue; + + const char* pszDiscId = CPLGetXMLValue(psIter2, "id", NULL); + if (pszDiscId == NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot find id attribute"); + continue; + } + + if (bLookForSubDataset && strcmp(pszDiscId, osDiscId.c_str()) != 0) + continue; + + nCountSubDataset ++; + + CPLXMLNode* psFrameList = CPLGetXMLNode(psIter2, "frame_list"); + if (psFrameList == NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot find frame_list element"); + continue; + } + + int nValidFrames = 0; + + std::vector aosFrameDesc; + + int nSubDatasetScale = -1; + + for(CPLXMLNode* psIter3 = psFrameList->psChild; + psIter3 != NULL; + psIter3 = psIter3->psNext) + { + if (!(psIter3->eType == CXT_Element && + psIter3->pszValue != NULL && + strcmp(psIter3->pszValue, "scale") == 0)) + continue; + + const char* pszSize = CPLGetXMLValue(psIter3, "size", NULL); + if (pszSize == NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot find size attribute"); + continue; + } + + int nScale = GetScaleFromString(pszSize); + if (nScale <= 0) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid scale %s", pszSize); + continue; + } + + if (nValidFrames == 0) + nSubDatasetScale = nScale; + else + nSubDatasetScale = -1; + + for(CPLXMLNode* psIter4 = psIter3->psChild; + psIter4 != NULL; + psIter4 = psIter4->psNext) + { + if (!(psIter4->eType == CXT_Element && + psIter4->pszValue != NULL && + strcmp(psIter4->pszValue, "frame") == 0)) + continue; + + const char* pszFrameName = + CPLGetXMLValue(psIter4, "name", NULL); + if (pszFrameName == NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot find name element"); + continue; + } + + if (strlen(pszFrameName) != 18) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid value for name element : %s", + pszFrameName); + continue; + } + + const char* pszFramePath = + CPLGetXMLValue(psIter4, "frame_path", NULL); + if (pszFramePath == NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot find frame_path element"); + continue; + } + + const char* pszFrameZone = + CPLGetXMLValue(psIter4, "frame_zone", NULL); + if (pszFrameZone == NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot find frame_zone element"); + continue; + } + if (strlen(pszFrameZone) != 1) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid value for frame_zone element : %s", + pszFrameZone); + continue; + } + char chZone = pszFrameZone[0]; + int nZone = 0; + if (chZone >= '1' && chZone <= '9') + nZone = chZone - '0'; + else if (chZone >= 'a' && chZone <= 'h') + nZone = -(chZone - 'a' + 1); + else if (chZone >= 'A' && chZone <= 'H') + nZone = -(chZone - 'A' + 1); + else if (chZone == 'j' || chZone == 'J') + nZone = -9; + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid value for frame_zone element : %s", + pszFrameZone); + continue; + } + if (nZone == 9 || nZone == -9) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Polar zones unhandled by current implementation"); + continue; + } + + double dfMinX = 0, dfMaxX = 0, + dfMinY = 0, dfMaxY = 0, + dfPixelXSize = 0, dfPixelYSize = 0; + if (!GetExtent(pszFrameName, nScale, nZone, + dfMinX, dfMaxX, dfMinY, dfMaxY, + dfPixelXSize, dfPixelYSize)) + { + continue; + } + + nValidFrames ++; + + const char* pszFullName = BuildFullName(pszTOCFilename, + pszFramePath, + pszFrameName); + poDS->papszFileList = CSLAddString(poDS->papszFileList, pszFullName); + + if (!bGlobalExtentValid) + { + dfGlobalMinX = dfMinX; + dfGlobalMinY = dfMinY; + dfGlobalMaxX = dfMaxX; + dfGlobalMaxY = dfMaxY; + dfGlobalPixelXSize = dfPixelXSize; + dfGlobalPixelYSize = dfPixelYSize; + bGlobalExtentValid = TRUE; + } + else + { + if (dfMinX < dfGlobalMinX) dfGlobalMinX = dfMinX; + if (dfMinY < dfGlobalMinY) dfGlobalMinY = dfMinY; + if (dfMaxX > dfGlobalMaxX) dfGlobalMaxX = dfMaxX; + if (dfMaxY > dfGlobalMaxY) dfGlobalMaxY = dfMaxY; + if (dfPixelXSize < dfGlobalPixelXSize) + dfGlobalPixelXSize = dfPixelXSize; + if (dfPixelYSize < dfGlobalPixelYSize) + dfGlobalPixelYSize = dfPixelYSize; + } + + if (bLookForSubDataset) + { + FrameDesc frameDesc; + frameDesc.pszName = pszFrameName; + frameDesc.pszPath = pszFramePath; + frameDesc.nScale = nScale; + frameDesc.nZone = nZone; + aosFrameDesc.push_back(frameDesc); + } + } + } + + if (bLookForSubDataset) + { + delete poDS; + if (nValidFrames == 0) + return NULL; + return ECRGTOCSubDataset::Build(pszProductTitle, + pszDiscId, + nSubDatasetScale, + nCountSubDataset, + pszTOCFilename, + aosFrameDesc, + dfGlobalMinX, + dfGlobalMinY, + dfGlobalMaxX, + dfGlobalMaxY, + dfGlobalPixelXSize, + dfGlobalPixelYSize); + } + + if (nValidFrames) + { + poDS->AddSubDataset(pszOpenInfoFilename, + pszProductTitle, pszDiscId); + nSubDatasets ++; + } + } + } + + if (!bGlobalExtentValid) + { + delete poDS; + return NULL; + } + + if (nSubDatasets == 1) + { + const char* pszSubDatasetName = CSLFetchNameValue( + poDS->GetMetadata("SUBDATASETS"), "SUBDATASET_1_NAME"); + GDALOpenInfo oOpenInfo(pszSubDatasetName, GA_ReadOnly); + delete poDS; + GDALDataset* poRetDS = Open(&oOpenInfo); + if (poRetDS) + poRetDS->SetDescription(pszOpenInfoFilename); + return poRetDS; + } + + poDS->adfGeoTransform[0] = dfGlobalMinX; + poDS->adfGeoTransform[1] = dfGlobalPixelXSize; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = dfGlobalMaxY; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = - dfGlobalPixelYSize; + + poDS->nRasterXSize = (int)(0.5 + (dfGlobalMaxX - dfGlobalMinX) / dfGlobalPixelXSize); + poDS->nRasterYSize = (int)(0.5 + (dfGlobalMaxY - dfGlobalMinY) / dfGlobalPixelYSize); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->TryLoadXML(); + + return poDS; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int ECRGTOCDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + const char *pszFilename = poOpenInfo->pszFilename; + const char *pabyHeader = (const char *) poOpenInfo->pabyHeader; + +/* -------------------------------------------------------------------- */ +/* Is this a sub-dataset selector? If so, it is obviously ECRGTOC. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszFilename, "ECRG_TOC_ENTRY:",strlen("ECRG_TOC_ENTRY:"))) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* First we check to see if the file has the expected header */ +/* bytes. */ +/* -------------------------------------------------------------------- */ + if( pabyHeader == NULL ) + return FALSE; + + if ( strstr(pabyHeader, "") != NULL && + strstr(pabyHeader, "pszFilename; + CPLString osProduct, osDiscId; + + if( !Identify( poOpenInfo ) ) + return NULL; + + if( EQUALN(pszFilename, "ECRG_TOC_ENTRY:",strlen("ECRG_TOC_ENTRY:"))) + { + pszFilename += strlen("ECRG_TOC_ENTRY:"); + osProduct = pszFilename; + size_t iPos = osProduct.find(":"); + if (iPos == std::string::npos) + return NULL; + osProduct.resize(iPos); + + pszFilename += iPos + 1; + osDiscId = pszFilename; + iPos = osDiscId.find(":"); + if (iPos == std::string::npos) + return NULL; + osDiscId.resize(iPos); + + pszFilename += iPos + 1; + } + +/* -------------------------------------------------------------------- */ +/* Parse the XML file */ +/* -------------------------------------------------------------------- */ + CPLXMLNode* psXML = CPLParseXMLFile(pszFilename); + if (psXML == NULL) + { + return NULL; + } + + GDALDataset* poDS = Build( pszFilename, psXML, osProduct, osDiscId, + poOpenInfo->pszFilename); + CPLDestroyXMLNode(psXML); + + if (poDS && poOpenInfo->eAccess == GA_Update) + { + CPLError(CE_Failure, CPLE_NotSupported, + "ECRGTOC driver does not support update mode"); + delete poDS; + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_ECRGTOC() */ +/************************************************************************/ + +void GDALRegister_ECRGTOC() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ECRGTOC" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ECRGTOC" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ECRG TOC format" ); + + poDriver->pfnIdentify = ECRGTOCDataset::Identify; + poDriver->pfnOpen = ECRGTOCDataset::Open; + + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#ECRGTOC" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "xml" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/nitf/frmt_nitf.html b/bazaar/plugin/gdal/frmts/nitf/frmt_nitf.html new file mode 100644 index 000000000..45af36610 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/nitf/frmt_nitf.html @@ -0,0 +1,167 @@ + + +NITF -- National Imagery Transmission Format + + + + +

    NITF -- National Imagery Transmission Format

    + +GDAL supports reading of several subtypes of NITF image files, and writing +simple NITF 2.1 files. NITF 1.1, NITF 2.0, NITF 2.1 and NSIF 1.0 +files with uncompressed, ARIDPCM, JPEG compressed, JPEG2000 (with Kakadu, ECW SDKs or other JPEG2000 capable driver) +or VQ compressed images should be readable.

    + +The read support test has been tested on various products, +including CIB and CADRG frames from RPF products, ECRG frames, HRE products.

    + +Color tables for pseudocolored images are read. In some cases nodata values +may be identified.

    + +Lat/Long extents are read from the IGEOLO information in +the image header if available. If high precision lat/long georeferencing +information is available in RPF auxiliary data it will be used in preference +to the low precision IGEOLO information. In case a BLOCKA instance is found, +the higher precision coordinates of BLOCKA are used if the block data covers +the complete image - that is the L_LINES field with the row count for that +block is equal to the row count of the image. Additionally, all BLOCKA +instances are returned as metadata. If GeoSDE TRE are available, they will be +used to provide higher precision coordinates. +

    + +Most file header and image header fields are returned as dataset level +metadata.

    + +

    Creation Issues

    + +On export NITF files are always written as NITF 2.1 with one image +and no other auxiliary layers. Images are uncompressed by default, but JPEG +and JPEG2000 compression are also available. Georeferencing can only be written for +images using a geographic coordinate system or a UTM WGS84 projection. Coordinates are implicitly +treated as WGS84 even if they are actually in a different geographic coordinate +system. Pseudo-color tables may be written for 8bit images.

    + +In addition to the export oriented CreateCopy() API, it is also possible to +create a blank NITF file using Create() and write imagery on demand. However, +using this methology writing of pseudocolor tables and georeferencing is +not supported unless appropriate IREP and ICORDS creation options are supplied. +

    + +Creation Options:

    + +

      + +
    • Most file header, imagery header metadata and security fields can +be set with appropriate creation options (although they are reported as metadata +item, but must not be set as metadata). For instance setting +"FTITLE=Image of abandoned missle silo south west of Karsk" in the +creation option list would result in setting of the FTITLE field in the NITF +file header. Use the official field names from the NITF specification +document; do not put the "NITF_" prefix that is reported when asking the metadata +list.

      + +

    • IC=NC/C3/M3/C8 : Set the compression method. +
        +
      • NC is the default value, and means no compression. +
      • C3 means JPEG compression and is only available for the +CreateCopy() method. The QUALITY and PROGRESSIVE JPEG-specific +creation options can be used. See the JPEG driver +documentation. Starting with GDAL 1.7.0, multi-block images can be written. +
      • M3 is a variation of C3. The only difference is that a block map is written, +which allow for fast seeking to any block. (Starting with GDAL 1.7.0.) +
      • C8 means JPEG2000 compression (one block) and is available for +CreateCopy() and/or Create() methods. JPEG2000 compression is only available +if the JP2ECW, JP2KAK or Jasper driver is available : +
          +
        • JP2ECW : The TARGET and PROFILE JP2ECW-specific creation options can be used. +Both CreateCopy() and/or Create() methods are available. +See the JP2ECW driver documentation. +
        • JP2KAK : The general JP2KAK-specific creation options can be used (QUALITY, BLOCKXSIZE, BLOCKYSIZE, GMLPJ2, GeoJP2, LAYERS, ROI). +Only CreateCopy() method is available. +See the JP2KAK driver documentation. +
        • Starting with GDAL 1.7.0, if JP2ECW and JP2KAK drivers are not available, Jasper JPEG2000 driver can be used in the CreateCopy() case. +
        +
      +
    • +

      + +

    • NUMI=n : (Starting with GDAL 1.7.0) Number of images. Default = 1. +This option is only compatible with IC=NC (uncompressed images). +

      + +

    • ICORDS=G/D/N/S: Set to "G" to ensure that space will be reserved for +geographic corner coordinates (in DMS) to be set later via SetGeoTransform(), set to "D" +for geographic coordinates in decimal degrees, set to "N" +for UTM WGS84 projection in Northern hemisphere or to "S" for UTM WGS84 projection in +southern hemisphere (Only needed for Create() method, not CreateCopy()). +If you Create() a new NITF file and have specified "N" or "S" for ICORDS, +you need to call later the SetProjection method with a consistent +UTM SRS to set the UTM zone number (otherwise it will default to zone 0).

      + +

    • FHDR: File version can be selected though currently the only +two variations supported are "NITF02.10" (the default), and "NSIF01.00".

      + +

    • IREP: Set to "RGB/LUT" to reserve space for a color table for +each output band. (Only needed for +Create() method, not CreateCopy()).

      + +

    • IREPBAND: (GDAL >= 1.9.0) Comma separated list of band IREPBANDs in band order.

      + +

    • ISUBCAT: (GDAL >= 1.9.0) Comma separated list of band ISUBCATs in band order.

      + +

    • LUT_SIZE: Set to control the size of pseudocolor tables for +RGB/LUT bands. A value of 256 assumed if not present. (Only needed for +Create() method, not CreateCopy()).

      + + + +

    • BLOCKXSIZE=n: Set the block width.

      + +

    • BLOCKYSIZE=n: Set the block height.

      + +

    • BLOCKA_*=: If a complete set of BLOCKA options is provided with +exactly the same organization as the NITF_BLOCKA metadata reported when +reading an NITF file with BLOCKA TREs then a file will be created with BLOCKA +TREs.

      + +

    • TRE=tre-name=tre-contents: One or more TRE creation options may +be used provided to write arbitrary user defined TREs to the image header. The +tre-name should be at most six characters, and the tre-contents should be +"backslash escaped" if it contains blackslashes or zero bytes. The argument +is the same format as returned in the TRE metadata domain when reading.

      + +

    • FILE_TRE=tre-name=tre-contents: (GDAL >= 1.8.0) Similar to above options, +except that the TREs are written in the file header, instead of the image header.

      + +

    • SDE_TRE=YES/NO: (GDAL >= 1.8.0) Write GEOLOB and GEOPSB TREs to get +more precise georeferencing. This is limited to geographic SRS, and to CreateCopy() for now.

      + +

    + + +

    Links

    + + +

    Credit

    + +The author wishes to thank AUG +Signals and the GeoConnections +program for supporting development of this driver, and to thank +Steve Rawlinson (JPEG), Reiner Beck (BLOCKA) for assistance adding features.

    + + + + + + diff --git a/bazaar/plugin/gdal/frmts/nitf/frmt_nitf_advanced.html b/bazaar/plugin/gdal/frmts/nitf/frmt_nitf_advanced.html new file mode 100644 index 000000000..2d8b901e8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/nitf/frmt_nitf_advanced.html @@ -0,0 +1,233 @@ + + +NITF -- Advanced Driver Information + + + + +

    NITF -- Advanced Driver Information

    + +The NITF (National Imagery Transmission Format) driver in GDAL includes a +number of advanced, and somewhat esoteric options not suitable for the +general end user documentation for the driver. +This information is collected here, and is primarily aimed at developers and +advanced users. + +

    CGM Segments

    + +NITF files that have CGM data (that is segment type GR - graphics, or SY with +an STYPE value of 'C') will make that information available as metadata in the +CGM domain. The returned metadata will look something like: + +
    +  SEGMENT_COUNT=1
    +  SEGMENT_0_SLOC_ROW=25
    +  SEGMENT_0_SLOC_COL=25
    +  SEGMENT_0_SDLVL=2
    +  SEGMENT_0_SALVL=1
    +  SEGMENT_0_CCS_ROW=00025
    +  SEGMENT_0_CCS_COL=00025
    +  SEGMENT_0_DATA=\0!\0...
    +
    + +The SLOC_ROW and SLOC_COL values are the placement of the CGM object relative +to the base (SALVL) image. The CCS_ROW/COL values are relative to the common +coordinate system. The _SDLVL is the display level. +The DATA is the raw CGM data with "backslash quotable" escaping +applied. All occurances of ASCII zero will be translated to '\0', and all +backslashes and double quotes will be backslashed escaped. The +CPLUnescapeString() function can be used to unescape the data into binary +format using scheme CPLES_BackslashQuotable.

    + +Since GDAL 1.8.0, to add CGM data to a NITF image, you can pass creation options in the following format: + +

    +  CGM=SEGMENT_COUNT=1
    +  CGM=SEGMENT_0_SLOC_ROW=25
    +  CGM=SEGMENT_0_SLOC_COL=25
    +  CGM=SEGMENT_0_SDLVL=2
    +  CGM=SEGMENT_0_SALVL=1
    +  CGM=SEGMENT_0_DATA=\0!\0...
    +
    + +Notice that passing CGM as creation options will overwrite existing CGM segment read in the CGM metadata domain. + +

    + + +While GDAL does not support parsing or rendering CGM data, at least one user +has found the UniConverter +library useful for this purpose. + +

    Multi-Image NITF Files

    + +NITF files with more than one image segment (IM) will present the image +segments as subdatasets. Opening a multiple NITF file by filename will provide +access to the first image segment. The subdataset metadata for a 3 image +NITF file might look like:

    + +

    +Subdatasets:
    +  SUBDATASET_1_NAME=NITF_IM:0:multi_image_jpeg_2.0.ntf
    +  SUBDATASET_1_DESC=Image 1 of multi_image_jpeg_2.0.ntf
    +  SUBDATASET_2_NAME=NITF_IM:1:multi_image_jpeg_2.0.ntf
    +  SUBDATASET_2_DESC=Image 2 of multi_image_jpeg_2.0.ntf
    +  SUBDATASET_3_NAME=NITF_IM:2:multi_image_jpeg_2.0.ntf
    +  SUBDATASET_3_DESC=Image 3 of multi_image_jpeg_2.0.ntf
    +
    + +In this case opening "multi_image_jpeg_2.0.ntf" directly will give +access to "NITF_IM:0:multi_image_jpeg_2.0.ntf". To open the others +use the corresponding subdataset names. The Subdataset mechanism is +generic GDAL concept discussed in the +GDAL Data Model +document.

    + +

    Text Segments

    + +NITF files that have text segments (that is segment type TX) will make that +information available as metadata in the TEXT domain. +The returned metadata will look something like: + +
    +  HEADER_0=TE       00020021216151629xxxxxxxxxxxxxxxxxxxxxxxxxxx
    +  DATA_0=This is test text file 01.
    +
    +  HEADER_1=TE       00020021216151629xxxxxxxxxxxxxxxxxxxxxxxxxxx
    +  DATA_1=This is test text file 02.
    +
    +  HEADER_2=TE       00020021216151629xxxxxxxxxxxxxxxxxxxxxxxxxxx
    +  DATA_2=This is test text file 03.
    +
    +  HEADER_3=TE       00020021216151629xxxxxxxxxxxxxxxxxxxxxxxxxxx
    +  DATA_3=This is test text file 04.
    +
    +  HEADER_4=TE       00020021216151629xxxxxxxxxxxxxxxxxxxxxxxxxxx
    +  DATA_4=This is test text file 05.
    +
    + +The argument to DATA_n +is the raw text of the n'th (zero based) text segment with no escaping of any +kind applied.

    + +Since GDAL 1.8.0, the TEXT segment header data is preserved in HEADER_n metdata item. + +The CreateCopy() method on the NITF driver also supports creating +text segments on the output file as long as the input file has metadata +in the TEXT domain as defined above.

    + +Since GDAL 1.8.0, to add TEXT data to a NITF image, you can also pass creation options in the following format: + +

    +  TEXT=HEADER_0=TE       00020021216151629xxxxxxxxxxxxxxxxxxxxxxxxxxx
    +  TEXT=DATA_0=This is test text file 01.
    +  TEXT=HEADER_1=TE       00020021216151629xxxxxxxxxxxxxxxxxxxxxxxxxxx
    +  TEXT=DATA_1=This is test text file 02.
    +
    + +Notice that passing TEXT as creation options will overwrite existing text segment read in the TEXT metadata domain. + +

    TREs

    + +NITF files with registered (or unregistered?) extensions on the file header, +or the referenced image header will make them available in a raw form in +metadata via the TRE domain. The TRE domain will hold one metadata item per +TRE which will have the name of the TRE as the name, and the data of the TRE +as the contents. The data contents will be "backslash escaped" like CGM +data above.

    + +In case of multiple occurences of the same TRE, the second occurence will be +named "TRENAME_2", the third "TRENAME_3" where TRENAME is the TRE name.

    + +

    +Metadata (TRE):
    +  GEOPSB=MAPM  World Geodetic System 1984                                       
    +               WGE World Geodetic System 1984                                   
    +                   WE Geodetic                                                  
    +                      GEODMean Sea                                              
    +                          MSL 000000000000000                                   
    +                                                0000
    +  PRJPSB=Polar Stereographic                                                    
    +         PG2-00090.00000250000039.99999884000000000000000000000000000000
    +  MAPLOB=M  0598005958-000003067885.8-000002163353.8
    +
    + +

    TREs as xml:TRE

    + +Starting with GDAL 1.9.0, all TREs found in file and matching one of the +TRE description of the nitf_spec.xml in GDAL data directory will be reported +as XML content in the xml:TRE metadata domain.

    + +

    +Metadata (xml:TRE):
    +<tres>
    +  <tre name="RSMDCA" location="des TRE_OVERFLOW">
    +    <field name="IID" value="2_8" />
    +    <field name="EDITION" value="1101222272-2" />
    +    <field name="TID" value="1101222272-1" />
    +    <field name="NPAR" value="06" />
    +    <field name="NIMGE" value="001" />
    +    <field name="NPART" value="00006" />
    +    <repeated name="IMAGE" number="1">
    +      <group index="0">
    +        <field name="IID" value="2_8" />
    +        <field name="NPARI" value="06" />
    +      </group>
    +    </repeated>
    +    <field name="XUOL" value="-2.42965895449297E+06" />
    +    <field name="YUOL" value="-4.76049894293300E+06" />
    +    <field name="ZUOL" value="+3.46898407315533E+06" />
    +    <field name="XUXL" value="+8.90698769551156E-01" />
    +    <field name="XUYL" value="+2.48664813021570E-01" />
    +    <field name="XUZL" value="-3.80554217799520E-01" />
    +    <field name="YUXL" value="-4.54593996792805E-01" />
    +    <field name="YUYL" value="+4.87215943350720E-01" />
    +    <field name="YUZL" value="-7.45630553709282E-01" />
    +    <field name="ZUXL" value="+0.00000000000000E+00" />
    +    <field name="ZUYL" value="+8.37129879594448E-01" />
    +    <field name="ZUZL" value="+5.47004172461403E-01" />
    +[...]
    +    <repeated name="DERCOV" number="21">
    +      <group index="0">
    +        <field name="DERCOV" value="+5.77388827727787E+04" />
    +      </group>
    +[...]
    +      <group index="20">
    +        <field name="DERCOV" value="+1.14369570920252E-02" />
    +      </group>
    +    </repeated>
    +  </tre>
    +  <tre name="RSMECA" location="des TRE_OVERFLOW">
    +[...]
    +  </tre>
    +  <tre name="RSMIDA" location="des TRE_OVERFLOW">
    +[...]
    +  </tre>
    +  <tre name="RSMPCA" location="des TRE_OVERFLOW">
    +[...]
    +  </tre>
    +</tres>
    +
    + +

    Raw File / Image Headers

    + +In some cases application may need to recover very specific information +from the image or file headers that isn't normally available as metadata. +In this case it is possible to query the "NITF_METADATA" metadata domain. +The complete file and image headers will be returned as metadata in base64 +encoded format. Something like:

    + +

    +Metadata (NITF_METADATA):
    +  NITFFileHeader=002213 TklURjAyLjAwMDEgICAgVTIxN0cwSjA...
    +  NITFImageSubheader=439 SU1NaXNzaW5nIElEMjUxNTI1NTlaTU...
    +
    + +Note that the ascii encoded numeric values prefixing the base64 encoded +header is the length (decoded) in bytes, followed by one space.

    + + + + + + diff --git a/bazaar/plugin/gdal/frmts/nitf/makefile.vc b/bazaar/plugin/gdal/frmts/nitf/makefile.vc new file mode 100644 index 000000000..90da63dd4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/nitf/makefile.vc @@ -0,0 +1,37 @@ + +GDAL_OBJ = nitfdataset.obj rpftocdataset.obj nitfwritejpeg.obj \ + nitfwritejpeg_12.obj nitfrasterband.obj ecrgtocdataset.obj + +NITFLIB_OBJ = nitffile.obj nitfimage.obj mgrs.obj nitfaridpcm.obj \ + nitfbilevel.obj rpftocfile.obj nitfdes.obj nitf_gcprpc.obj + +OBJ = $(GDAL_OBJ) $(NITFLIB_OBJ) + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF JPEG_SUPPORTED +!IFDEF JPEG_EXTERNAL_LIB +JPEG_FLAGS = -I$(JPEGDIR) -DJPEG_SUPPORTED +!ELSE +JPEG_FLAGS = -I..\jpeg\libjpeg -DJPEG_SUPPORTED +!ENDIF +!ENDIF + +!IFDEF JPEG12_SUPPORTED +JPEG12_FLAGS = -DJPEG_DUAL_MODE_8_12 +!ENDIF + +EXTRAFLAGS = -I..\gtiff -I..\gtiff\libtiff -I..\vrt $(JPEG_FLAGS) $(JPEG12_FLAGS) + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + +nitfdump.exe: nitfdump.c $(NITFLIB_OBJ) + $(CC) $(CFLAGS) nitfdump.c $(GDALLIB) + if exist $@.manifest mt -manifest $@.manifest -outputresource:$@;1 + del nitfdump.obj diff --git a/bazaar/plugin/gdal/frmts/nitf/mgrs.c b/bazaar/plugin/gdal/frmts/nitf/mgrs.c new file mode 100644 index 000000000..c4bd57cae --- /dev/null +++ b/bazaar/plugin/gdal/frmts/nitf/mgrs.c @@ -0,0 +1,1105 @@ +/*************************************************************************** + * $Id: mgrs.c 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: MGRS Converter + * Purpose: Geotrans code for MGRS translation (slightly adapted) + * Author: Unknown (NIMA) + * + *************************************************************************** + *************************************************************************** + * RSC IDENTIFIER: MGRS + * + * ABSTRACT + * + * This component converts between geodetic coordinates (latitude and + * longitude) and Military Grid Reference System (MGRS) coordinates. + * + * ERROR HANDLING + * + * This component checks parameters for valid values. If an invalid value + * is found, the error code is combined with the current error code using + * the bitwise or. This combining allows multiple error codes to be + * returned. The possible error codes are: + * + * MGRS_NO_ERROR : No errors occurred in function + * MGRS_LAT_ERROR : Latitude outside of valid range + * (-90 to 90 degrees) + * MGRS_LON_ERROR : Longitude outside of valid range + * (-180 to 360 degrees) + * MGRS_STR_ERROR : An MGRS string error: string too long, + * too short, or badly formed + * MGRS_PRECISION_ERROR : The precision must be between 0 and 5 + * inclusive. + * MGRS_A_ERROR : Semi-major axis less than or equal to zero + * MGRS_INV_F_ERROR : Inverse flattening outside of valid range + * (250 to 350) + * MGRS_EASTING_ERROR : Easting outside of valid range + * (100,000 to 900,000 meters for UTM) + * (0 to 4,000,000 meters for UPS) + * MGRS_NORTHING_ERROR : Northing outside of valid range + * (0 to 10,000,000 meters for UTM) + * (0 to 4,000,000 meters for UPS) + * MGRS_ZONE_ERROR : Zone outside of valid range (1 to 60) + * MGRS_HEMISPHERE_ERROR : Invalid hemisphere ('N' or 'S') + * + * REUSE NOTES + * + * MGRS is intended for reuse by any application that does conversions + * between geodetic coordinates and MGRS coordinates. + * + * REFERENCES + * + * Further information on MGRS can be found in the Reuse Manual. + * + * MGRS originated from : U.S. Army Topographic Engineering Center + * Geospatial Information Division + * 7701 Telegraph Road + * Alexandria, VA 22310-3864 + * + * LICENSES + * + * None apply to this component. + * + * RESTRICTIONS + * + * + * ENVIRONMENT + * + * MGRS was tested and certified in the following environments: + * + * 1. Solaris 2.5 with GCC version 2.8.1 + * 2. Windows 95 with MS Visual C++ version 6 + * + * MODIFICATIONS + * + * Date Description + * ---- ----------- + * 16-11-94 Original Code + * 15-09-99 Reengineered upper layers + * 02-05-03 Corrected latitude band bug in GRID_UTM + * 08-20-03 Reengineered lower layers + */ + + +/***************************************************************************/ +/* + * INCLUDES + */ +#include +#include +#include +#include +#include "mgrs.h" + +/* + * ctype.h - Standard C character handling library + * math.h - Standard C math library + * stdio.h - Standard C input/output library + * string.h - Standard C string handling library + * ups.h - Universal Polar Stereographic (UPS) projection + * utm.h - Universal Transverse Mercator (UTM) projection + * mgrs.h - function prototype error checking + */ + + +/***************************************************************************/ +/* + * GLOBAL DECLARATIONS + */ +#define DEG_TO_RAD 0.017453292519943295 /* PI/180 */ +#define RAD_TO_DEG 57.29577951308232087 /* 180/PI */ +#define LETTER_A 0 /* ARRAY INDEX FOR LETTER A */ +#define LETTER_B 1 /* ARRAY INDEX FOR LETTER B */ +#define LETTER_C 2 /* ARRAY INDEX FOR LETTER C */ +#define LETTER_D 3 /* ARRAY INDEX FOR LETTER D */ +#define LETTER_E 4 /* ARRAY INDEX FOR LETTER E */ +#define LETTER_F 5 /* ARRAY INDEX FOR LETTER E */ +#define LETTER_G 6 /* ARRAY INDEX FOR LETTER H */ +#define LETTER_H 7 /* ARRAY INDEX FOR LETTER H */ +#define LETTER_I 8 /* ARRAY INDEX FOR LETTER I */ +#define LETTER_J 9 /* ARRAY INDEX FOR LETTER J */ +#define LETTER_K 10 /* ARRAY INDEX FOR LETTER J */ +#define LETTER_L 11 /* ARRAY INDEX FOR LETTER L */ +#define LETTER_M 12 /* ARRAY INDEX FOR LETTER M */ +#define LETTER_N 13 /* ARRAY INDEX FOR LETTER N */ +#define LETTER_O 14 /* ARRAY INDEX FOR LETTER O */ +#define LETTER_P 15 /* ARRAY INDEX FOR LETTER P */ +#define LETTER_Q 16 /* ARRAY INDEX FOR LETTER Q */ +#define LETTER_R 17 /* ARRAY INDEX FOR LETTER R */ +#define LETTER_S 18 /* ARRAY INDEX FOR LETTER S */ +#define LETTER_T 19 /* ARRAY INDEX FOR LETTER S */ +#define LETTER_U 20 /* ARRAY INDEX FOR LETTER U */ +#define LETTER_V 21 /* ARRAY INDEX FOR LETTER V */ +#define LETTER_W 22 /* ARRAY INDEX FOR LETTER W */ +#define LETTER_X 23 /* ARRAY INDEX FOR LETTER X */ +#define LETTER_Y 24 /* ARRAY INDEX FOR LETTER Y */ +#define LETTER_Z 25 /* ARRAY INDEX FOR LETTER Z */ +#define MGRS_LETTERS 3 /* NUMBER OF LETTERS IN MGRS */ +#define ONEHT 100000.e0 /* ONE HUNDRED THOUSAND */ +#define TWOMIL 2000000.e0 /* TWO MILLION */ +#define TRUE 1 /* CONSTANT VALUE FOR TRUE VALUE */ +#define FALSE 0 /* CONSTANT VALUE FOR FALSE VALUE */ +#define PI 3.14159265358979323e0 /* PI */ +#define PI_OVER_2 (PI / 2.0e0) + +#define MIN_EASTING 100000 +#define MAX_EASTING 900000 +#define MIN_NORTHING 0 +#define MAX_NORTHING 10000000 +#define MAX_PRECISION 5 /* Maximum precision of easting & northing */ +#define MIN_UTM_LAT ( (-80 * PI) / 180.0 ) /* -80 degrees in radians */ +#define MAX_UTM_LAT ( (84 * PI) / 180.0 ) /* 84 degrees in radians */ + +#define MIN_EAST_NORTH 0 +#define MAX_EAST_NORTH 4000000 + + +/* Ellipsoid parameters, default to WGS 84 */ +double MGRS_a = 6378137.0; /* Semi-major axis of ellipsoid in meters */ +double MGRS_f = 1 / 298.257223563; /* Flattening of ellipsoid */ +double MGRS_recpf = 298.257223563; +char MGRS_Ellipsoid_Code[3] = {'W','E',0}; + + +/* + * CLARKE_1866 : Ellipsoid code for CLARKE_1866 + * CLARKE_1880 : Ellipsoid code for CLARKE_1880 + * BESSEL_1841 : Ellipsoid code for BESSEL_1841 + * BESSEL_1841_NAMIBIA : Ellipsoid code for BESSEL 1841 (NAMIBIA) + */ +const char* CLARKE_1866 = "CC"; +const char* CLARKE_1880 = "CD"; +const char* BESSEL_1841 = "BR"; +const char* BESSEL_1841_NAMIBIA = "BN"; + + +typedef struct Latitude_Band_Value +{ + long letter; /* letter representing latitude band */ + double min_northing; /* minimum northing for latitude band */ + double north; /* upper latitude for latitude band */ + double south; /* lower latitude for latitude band */ +} Latitude_Band; + +static const Latitude_Band Latitude_Band_Table[20] = + {{LETTER_C, 1100000.0, -72.0, -80.5}, + {LETTER_D, 2000000.0, -64.0, -72.0}, + {LETTER_E, 2800000.0, -56.0, -64.0}, + {LETTER_F, 3700000.0, -48.0, -56.0}, + {LETTER_G, 4600000.0, -40.0, -48.0}, + {LETTER_H, 5500000.0, -32.0, -40.0}, + {LETTER_J, 6400000.0, -24.0, -32.0}, + {LETTER_K, 7300000.0, -16.0, -24.0}, + {LETTER_L, 8200000.0, -8.0, -16.0}, + {LETTER_M, 9100000.0, 0.0, -8.0}, + {LETTER_N, 0.0, 8.0, 0.0}, + {LETTER_P, 800000.0, 16.0, 8.0}, + {LETTER_Q, 1700000.0, 24.0, 16.0}, + {LETTER_R, 2600000.0, 32.0, 24.0}, + {LETTER_S, 3500000.0, 40.0, 32.0}, + {LETTER_T, 4400000.0, 48.0, 40.0}, + {LETTER_U, 5300000.0, 56.0, 48.0}, + {LETTER_V, 6200000.0, 64.0, 56.0}, + {LETTER_W, 7000000.0, 72.0, 64.0}, + {LETTER_X, 7900000.0, 84.5, 72.0}}; + + +typedef struct UPS_Constant_Value +{ + long letter; /* letter representing latitude band */ + long ltr2_low_value; /* 2nd letter range - high number */ + long ltr2_high_value; /* 2nd letter range - low number */ + long ltr3_high_value; /* 3rd letter range - high number (UPS) */ + double false_easting; /* False easting based on 2nd letter */ + double false_northing; /* False northing based on 3rd letter */ +} UPS_Constant; + +static const UPS_Constant UPS_Constant_Table[4] = + {{LETTER_A, LETTER_J, LETTER_Z, LETTER_Z, 800000.0, 800000.0}, + {LETTER_B, LETTER_A, LETTER_R, LETTER_Z, 2000000.0, 800000.0}, + {LETTER_Y, LETTER_J, LETTER_Z, LETTER_P, 800000.0, 1300000.0}, + {LETTER_Z, LETTER_A, LETTER_J, LETTER_P, 2000000.0, 1300000.0}}; + +/***************************************************************************/ +/* + * FUNCTIONS + */ + +long Get_Latitude_Band_Min_Northing(long letter, double* min_northing) +/* + * The function Get_Latitude_Band_Min_Northing receives a latitude band letter + * and uses the Latitude_Band_Table to determine the minimum northing for that + * latitude band letter. + * + * letter : Latitude band letter (input) + * min_northing : Minimum northing for that letter (output) + */ +{ /* Get_Latitude_Band_Min_Northing */ + long error_code = MGRS_NO_ERROR; + + if ((letter >= LETTER_C) && (letter <= LETTER_H)) + *min_northing = Latitude_Band_Table[letter-2].min_northing; + else if ((letter >= LETTER_J) && (letter <= LETTER_N)) + *min_northing = Latitude_Band_Table[letter-3].min_northing; + else if ((letter >= LETTER_P) && (letter <= LETTER_X)) + *min_northing = Latitude_Band_Table[letter-4].min_northing; + else + error_code |= MGRS_STRING_ERROR; + + return error_code; +} /* Get_Latitude_Band_Min_Northing */ + + +long Get_Latitude_Range(long letter, double* north, double* south) +/* + * The function Get_Latitude_Range receives a latitude band letter + * and uses the Latitude_Band_Table to determine the latitude band + * boundaries for that latitude band letter. + * + * letter : Latitude band letter (input) + * north : Northern latitude boundary for that letter (output) + * north : Southern latitude boundary for that letter (output) + */ +{ /* Get_Latitude_Range */ + long error_code = MGRS_NO_ERROR; + + if ((letter >= LETTER_C) && (letter <= LETTER_H)) + { + *north = Latitude_Band_Table[letter-2].north * DEG_TO_RAD; + *south = Latitude_Band_Table[letter-2].south * DEG_TO_RAD; + } + else if ((letter >= LETTER_J) && (letter <= LETTER_N)) + { + *north = Latitude_Band_Table[letter-3].north * DEG_TO_RAD; + *south = Latitude_Band_Table[letter-3].south * DEG_TO_RAD; + } + else if ((letter >= LETTER_P) && (letter <= LETTER_X)) + { + *north = Latitude_Band_Table[letter-4].north * DEG_TO_RAD; + *south = Latitude_Band_Table[letter-4].south * DEG_TO_RAD; + } + else + error_code |= MGRS_STRING_ERROR; + + return error_code; +} /* Get_Latitude_Range */ + + +long Get_Latitude_Letter(double latitude, int* letter) +/* + * The function Get_Latitude_Letter receives a latitude value + * and uses the Latitude_Band_Table to determine the latitude band + * letter for that latitude. + * + * latitude : Latitude (input) + * letter : Latitude band letter (output) + */ +{ /* Get_Latitude_Letter */ + double temp = 0.0; + long error_code = MGRS_NO_ERROR; + double lat_deg = latitude * RAD_TO_DEG; + + if (lat_deg >= 72 && lat_deg < 84.5) + *letter = LETTER_X; + else if (lat_deg > -80.5 && lat_deg < 72) + { + temp = ((latitude + (80.0 * DEG_TO_RAD)) / (8.0 * DEG_TO_RAD)) + 1.0e-12; + *letter = Latitude_Band_Table[(int)temp].letter; + } + else + error_code |= MGRS_LAT_ERROR; + + return error_code; +} /* Get_Latitude_Letter */ + + +long Check_Zone(char* MGRS, long* zone_exists) +/* + * The function Check_Zone receives an MGRS coordinate string. + * If a zone is given, TRUE is returned. Otherwise, FALSE + * is returned. + * + * MGRS : MGRS coordinate string (input) + * zone_exists : TRUE if a zone is given, + * FALSE if a zone is not given (output) + */ +{ /* Check_Zone */ + int i = 0; + int j = 0; + int num_digits = 0; + long error_code = MGRS_NO_ERROR; + + /* skip any leading blanks */ + while (MGRS[i] == ' ') + i++; + j = i; + while (isdigit(MGRS[i])) + i++; + num_digits = i - j; + if (num_digits <= 2) + if (num_digits > 0) + *zone_exists = TRUE; + else + *zone_exists = FALSE; + else + error_code |= MGRS_STRING_ERROR; + + return error_code; +} /* Check_Zone */ + + +long Round_MGRS (double value) +/* + * The function Round_MGRS rounds the input value to the + * nearest integer, using the standard engineering rule. + * The rounded integer value is then returned. + * + * value : Value to be rounded (input) + */ +{ /* Round_MGRS */ + double ivalue; + long ival; + double fraction = modf (value, &ivalue); + ival = (long)(ivalue); + if ((fraction > 0.5) || ((fraction == 0.5) && (ival%2 == 1))) + ival++; + return (ival); +} /* Round_MGRS */ + + +long Make_MGRS_String (char* MGRS, + long Zone, + int Letters[MGRS_LETTERS], + double Easting, + double Northing, + long Precision) +/* + * The function Make_MGRS_String constructs an MGRS string + * from its component parts. + * + * MGRS : MGRS coordinate string (output) + * Zone : UTM Zone (input) + * Letters : MGRS coordinate string letters (input) + * Easting : Easting value (input) + * Northing : Northing value (input) + * Precision : Precision level of MGRS string (input) + */ +{ /* Make_MGRS_String */ + long i; + long j; + double divisor; + long east; + long north; + char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + long error_code = MGRS_NO_ERROR; + + i = 0; + if (Zone) + i = sprintf (MGRS+i,"%2.2ld",Zone); + else + strncpy(MGRS, " ", 2); // 2 spaces + + for (j=0;j<3;j++) + MGRS[i++] = alphabet[Letters[j]]; + divisor = pow (10.0, (5 - Precision)); + Easting = fmod (Easting, 100000.0); + if (Easting >= 99999.5) + Easting = 99999.0; + east = (long)(Easting/divisor); + i += sprintf (MGRS+i, "%*.*ld", (int) Precision, (int) Precision, east); + Northing = fmod (Northing, 100000.0); + if (Northing >= 99999.5) + Northing = 99999.0; + north = (long)(Northing/divisor); + i += sprintf (MGRS+i, "%*.*ld", (int) Precision, (int) Precision, north); + return (error_code); +} /* Make_MGRS_String */ + + +long Break_MGRS_String (char* MGRS, + long* Zone, + long Letters[MGRS_LETTERS], + double* Easting, + double* Northing, + long* Precision) +/* + * The function Break_MGRS_String breaks down an MGRS + * coordinate string into its component parts. + * + * MGRS : MGRS coordinate string (input) + * Zone : UTM Zone (output) + * Letters : MGRS coordinate string letters (output) + * Easting : Easting value (output) + * Northing : Northing value (output) + * Precision : Precision level of MGRS string (output) + */ +{ /* Break_MGRS_String */ + long num_digits; + long num_letters; + long i = 0; + long j = 0; + long error_code = MGRS_NO_ERROR; + + while (MGRS[i] == ' ') + i++; /* skip any leading blanks */ + j = i; + while (isdigit(MGRS[i])) + i++; + num_digits = i - j; + if (num_digits <= 2) + if (num_digits > 0) + { + char zone_string[3]; + /* get zone */ + strncpy (zone_string, MGRS+j, 2); + zone_string[2] = 0; + sscanf (zone_string, "%ld", Zone); + if ((*Zone < 1) || (*Zone > 60)) + error_code |= MGRS_STRING_ERROR; + } + else + *Zone = 0; + else + error_code |= MGRS_STRING_ERROR; + j = i; + + while (isalpha(MGRS[i])) + i++; + num_letters = i - j; + if (num_letters == 3) + { + /* get letters */ + Letters[0] = (toupper(MGRS[j]) - (long)'A'); + if ((Letters[0] == LETTER_I) || (Letters[0] == LETTER_O)) + error_code |= MGRS_STRING_ERROR; + Letters[1] = (toupper(MGRS[j+1]) - (long)'A'); + if ((Letters[1] == LETTER_I) || (Letters[1] == LETTER_O)) + error_code |= MGRS_STRING_ERROR; + Letters[2] = (toupper(MGRS[j+2]) - (long)'A'); + if ((Letters[2] == LETTER_I) || (Letters[2] == LETTER_O)) + error_code |= MGRS_STRING_ERROR; + } + else + error_code |= MGRS_STRING_ERROR; + j = i; + while (isdigit(MGRS[i])) + i++; + num_digits = i - j; + if ((num_digits <= 10) && (num_digits%2 == 0)) + { + long n; + char east_string[6]; + char north_string[6]; + long east; + long north; + double multiplier; + /* get easting & northing */ + n = num_digits/2; + *Precision = n; + if (n > 0) + { + strncpy (east_string, MGRS+j, n); + east_string[n] = 0; + sscanf (east_string, "%ld", &east); + strncpy (north_string, MGRS+j+n, n); + north_string[n] = 0; + sscanf (north_string, "%ld", &north); + multiplier = pow (10.0, 5 - n); + *Easting = east * multiplier; + *Northing = north * multiplier; + } + else + { + *Easting = 0.0; + *Northing = 0.0; + } + } + else + error_code |= MGRS_STRING_ERROR; + + return (error_code); +} /* Break_MGRS_String */ + + +void Get_Grid_Values (long zone, + long* ltr2_low_value, + long* ltr2_high_value, + double *false_northing) +/* + * The function Get_Grid_Values sets the letter range used for + * the 2nd letter in the MGRS coordinate string, based on the set + * number of the utm zone. It also sets the false northing using a + * value of A for the second letter of the grid square, based on + * the grid pattern and set number of the utm zone. + * + * zone : Zone number (input) + * ltr2_low_value : 2nd letter low number (output) + * ltr2_high_value : 2nd letter high number (output) + * false_northing : False northing (output) + */ +{ /* BEGIN Get_Grid_Values */ + long set_number; /* Set number (1-6) based on UTM zone number */ + long aa_pattern; /* Pattern based on ellipsoid code */ + + set_number = zone % 6; + + if (!set_number) + set_number = 6; + + if (!strcmp(MGRS_Ellipsoid_Code,CLARKE_1866) || !strcmp(MGRS_Ellipsoid_Code, CLARKE_1880) || + !strcmp(MGRS_Ellipsoid_Code,BESSEL_1841) || !strcmp(MGRS_Ellipsoid_Code,BESSEL_1841_NAMIBIA)) + aa_pattern = FALSE; + else + aa_pattern = TRUE; + + if ((set_number == 1) || (set_number == 4)) + { + *ltr2_low_value = LETTER_A; + *ltr2_high_value = LETTER_H; + } + else if ((set_number == 2) || (set_number == 5)) + { + *ltr2_low_value = LETTER_J; + *ltr2_high_value = LETTER_R; + } + else if ((set_number == 3) || (set_number == 6)) + { + *ltr2_low_value = LETTER_S; + *ltr2_high_value = LETTER_Z; + } + + /* False northing at A for second letter of grid square */ + if (aa_pattern) + { + if ((set_number % 2) == 0) + *false_northing = 1500000.0; + else + *false_northing = 0.0; + } + else + { + if ((set_number % 2) == 0) + *false_northing = 500000.0; + else + *false_northing = 1000000.00; + } +} /* END OF Get_Grid_Values */ + + +long UTM_To_MGRS (long Zone, + double Latitude, + double Easting, + double Northing, + long Precision, + char *MGRS) +/* + * The function UTM_To_MGRS calculates an MGRS coordinate string + * based on the zone, latitude, easting and northing. + * + * Zone : Zone number (input) + * Latitude : Latitude in radians (input) + * Easting : Easting (input) + * Northing : Northing (input) + * Precision : Precision (input) + * MGRS : MGRS coordinate string (output) + */ +{ /* BEGIN UTM_To_MGRS */ + double false_northing; /* False northing for 3rd letter */ + double grid_easting; /* Easting used to derive 2nd letter of MGRS */ + double grid_northing; /* Northing used to derive 3rd letter of MGRS */ + long ltr2_low_value; /* 2nd letter range - low number */ + long ltr2_high_value; /* 2nd letter range - high number */ + int letters[MGRS_LETTERS]; /* Number location of 3 letters in alphabet */ + double divisor; + long error_code = MGRS_NO_ERROR; + + /* Round easting and northing values */ + divisor = pow (10.0, (5 - Precision)); + Easting = Round_MGRS (Easting/divisor) * divisor; + Northing = Round_MGRS (Northing/divisor) * divisor; + + Get_Grid_Values(Zone, <r2_low_value, <r2_high_value, &false_northing); + + error_code = Get_Latitude_Letter(Latitude, &letters[0]); + + if (!error_code) + { + grid_northing = Northing; + if (grid_northing == 1.e7) + grid_northing = grid_northing - 1.0; + + while (grid_northing >= TWOMIL) + { + grid_northing = grid_northing - TWOMIL; + } + grid_northing = grid_northing - false_northing; + + if (grid_northing < 0.0) + grid_northing = grid_northing + TWOMIL; + + letters[2] = (long)(grid_northing / ONEHT); + if (letters[2] > LETTER_H) + letters[2] = letters[2] + 1; + + if (letters[2] > LETTER_N) + letters[2] = letters[2] + 1; + + grid_easting = Easting; + if (((letters[0] == LETTER_V) && (Zone == 31)) && (grid_easting == 500000.0)) + grid_easting = grid_easting - 1.0; /* SUBTRACT 1 METER */ + + letters[1] = ltr2_low_value + ((long)(grid_easting / ONEHT) -1); + if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_N)) + letters[1] = letters[1] + 1; + + Make_MGRS_String (MGRS, Zone, letters, Easting, Northing, Precision); + } + return error_code; +} /* END UTM_To_MGRS */ + + +long Set_MGRS_Parameters (double a, + double f, + char *Ellipsoid_Code) +/* + * The function SET_MGRS_PARAMETERS receives the ellipsoid parameters and sets + * the corresponding state variables. If any errors occur, the error code(s) + * are returned by the function, otherwise MGRS_NO_ERROR is returned. + * + * a : Semi-major axis of ellipsoid in meters (input) + * f : Flattening of ellipsoid (input) + * Ellipsoid_Code : 2-letter code for ellipsoid (input) + */ +{ /* Set_MGRS_Parameters */ + + double inv_f = 1 / f; + long Error_Code = MGRS_NO_ERROR; + + if (a <= 0.0) + { /* Semi-major axis must be greater than zero */ + Error_Code |= MGRS_A_ERROR; + } + if ((inv_f < 250) || (inv_f > 350)) + { /* Inverse flattening must be between 250 and 350 */ + Error_Code |= MGRS_INV_F_ERROR; + } + if (!Error_Code) + { /* no errors */ + MGRS_a = a; + MGRS_f = f; + MGRS_recpf = inv_f; + strcpy (MGRS_Ellipsoid_Code, Ellipsoid_Code); + } + return (Error_Code); +} /* Set_MGRS_Parameters */ + + +void Get_MGRS_Parameters (double *a, + double *f, + char* Ellipsoid_Code) +/* + * The function Get_MGRS_Parameters returns the current ellipsoid + * parameters. + * + * a : Semi-major axis of ellipsoid, in meters (output) + * f : Flattening of ellipsoid (output) + * Ellipsoid_Code : 2-letter code for ellipsoid (output) + */ +{ /* Get_MGRS_Parameters */ + *a = MGRS_a; + *f = MGRS_f; + strcpy (Ellipsoid_Code, MGRS_Ellipsoid_Code); + return; +} /* Get_MGRS_Parameters */ + +#ifdef notdef +long Convert_UTM_To_MGRS (long Zone, + char Hemisphere, + double Easting, + double Northing, + long Precision, + char* MGRS) +/* + * The function Convert_UTM_To_MGRS converts UTM (zone, easting, and + * northing) coordinates to an MGRS coordinate string, according to the + * current ellipsoid parameters. If any errors occur, the error code(s) + * are returned by the function, otherwise MGRS_NO_ERROR is returned. + * + * Zone : UTM zone (input) + * Hemisphere : North or South hemisphere (input) + * Easting : Easting (X) in meters (input) + * Northing : Northing (Y) in meters (input) + * Precision : Precision level of MGRS string (input) + * MGRS : MGRS coordinate string (output) + */ +{ /* Convert_UTM_To_MGRS */ + double latitude; /* Latitude of UTM point */ + double longitude; /* Longitude of UTM point */ + long temp_error = MGRS_NO_ERROR; + long error_code = MGRS_NO_ERROR; + + if ((Zone < 1) || (Zone > 60)) + error_code |= MGRS_ZONE_ERROR; + if ((Hemisphere != 'S') && (Hemisphere != 'N')) + error_code |= MGRS_HEMISPHERE_ERROR; + if ((Easting < MIN_EASTING) || (Easting > MAX_EASTING)) + error_code |= MGRS_EASTING_ERROR; + if ((Northing < MIN_NORTHING) || (Northing > MAX_NORTHING)) + error_code |= MGRS_NORTHING_ERROR; + if ((Precision < 0) || (Precision > MAX_PRECISION)) + error_code |= MGRS_PRECISION_ERROR; + if (!error_code) + { + Set_UTM_Parameters (MGRS_a, MGRS_f, 0); + temp_error = Convert_UTM_To_Geodetic (Zone, Hemisphere, Easting, Northing, &latitude, &longitude); + + /* Special check for rounding to (truncated) eastern edge of zone 31V */ + if ((Zone == 31) && (latitude >= 56.0 * DEG_TO_RAD) && (latitude < 64.0 * DEG_TO_RAD) && + (longitude >= 3.0 * DEG_TO_RAD)) + { /* Reconvert to UTM zone 32 */ + Set_UTM_Parameters (MGRS_a, MGRS_f, 32); + temp_error = Convert_Geodetic_To_UTM (latitude, longitude, &Zone, &Hemisphere, &Easting, &Northing); + } + + error_code = UTM_To_MGRS (Zone, latitude, Easting, Northing, Precision, MGRS); + } + return (error_code); +} /* Convert_UTM_To_MGRS */ +#endif + +long Convert_MGRS_To_UTM (char *MGRS, + long *Zone, + char *Hemisphere, + double *Easting, + double *Northing) +/* + * The function Convert_MGRS_To_UTM converts an MGRS coordinate string + * to UTM projection (zone, hemisphere, easting and northing) coordinates + * according to the current ellipsoid parameters. If any errors occur, + * the error code(s) are returned by the function, otherwise UTM_NO_ERROR + * is returned. + * + * MGRS : MGRS coordinate string (input) + * Zone : UTM zone (output) + * Hemisphere : North or South hemisphere (output) + * Easting : Easting (X) in meters (output) + * Northing : Northing (Y) in meters (output) + */ +{ /* Convert_MGRS_To_UTM */ + double scaled_min_northing; + double min_northing; + long ltr2_low_value; + long ltr2_high_value; + double false_northing; + double grid_easting; /* Easting for 100,000 meter grid square */ + double grid_northing; /* Northing for 100,000 meter grid square */ + long letters[MGRS_LETTERS]; + long in_precision; +#ifdef notdef + double upper_lat_limit; /* North latitude limits based on 1st letter */ + double lower_lat_limit; /* South latitude limits based on 1st letter */ + double latitude = 0.0; + double longitude = 0.0; + double divisor = 1.0; +#endif + long error_code = MGRS_NO_ERROR; + + error_code = Break_MGRS_String (MGRS, Zone, letters, Easting, Northing, &in_precision); + if (!*Zone) + error_code |= MGRS_STRING_ERROR; + else + { + if (!error_code) + { + if ((letters[0] == LETTER_X) && ((*Zone == 32) || (*Zone == 34) || (*Zone == 36))) + error_code |= MGRS_STRING_ERROR; + else + { + if (letters[0] < LETTER_N) + *Hemisphere = 'S'; + else + *Hemisphere = 'N'; + + Get_Grid_Values(*Zone, <r2_low_value, <r2_high_value, &false_northing); + + /* Check that the second letter of the MGRS string is within + * the range of valid second letter values + * Also check that the third letter is valid */ + if ((letters[1] < ltr2_low_value) || (letters[1] > ltr2_high_value) || (letters[2] > LETTER_V)) + error_code |= MGRS_STRING_ERROR; + + if (!error_code) + { + grid_northing = (double)(letters[2]) * ONEHT + false_northing; + grid_easting = (double)((letters[1]) - ltr2_low_value + 1) * ONEHT; + if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_O)) + grid_easting = grid_easting - ONEHT; + + if (letters[2] > LETTER_O) + grid_northing = grid_northing - ONEHT; + + if (letters[2] > LETTER_I) + grid_northing = grid_northing - ONEHT; + + if (grid_northing >= TWOMIL) + grid_northing = grid_northing - TWOMIL; + + error_code = Get_Latitude_Band_Min_Northing(letters[0], &min_northing); + if (!error_code) + { + scaled_min_northing = min_northing; + while (scaled_min_northing >= TWOMIL) + { + scaled_min_northing = scaled_min_northing - TWOMIL; + } + + grid_northing = grid_northing - scaled_min_northing; + if (grid_northing < 0.0) + grid_northing = grid_northing + TWOMIL; + + grid_northing = min_northing + grid_northing; + + *Easting = grid_easting + *Easting; + *Northing = grid_northing + *Northing; +#ifdef notdef + /* check that point is within Zone Letter bounds */ + error_code = Set_UTM_Parameters(MGRS_a,MGRS_f,*Zone); + if (!error_code) + { + error_code = Convert_UTM_To_Geodetic(*Zone,*Hemisphere,*Easting,*Northing,&latitude,&longitude); + if (!error_code) + { + divisor = pow (10.0, in_precision); + error_code = Get_Latitude_Range(letters[0], &upper_lat_limit, &lower_lat_limit); + if (!error_code) + { + if (!(((lower_lat_limit - DEG_TO_RAD/divisor) <= latitude) && (latitude <= (upper_lat_limit + DEG_TO_RAD/divisor)))) + error_code |= MGRS_LAT_ERROR; + } + } + } +#endif /* notdef */ + } + } + } + } + } + return (error_code); +} /* Convert_MGRS_To_UTM */ + + +long Convert_UPS_To_MGRS (char Hemisphere, + double Easting, + double Northing, + long Precision, + char* MGRS) +/* + * The function Convert_UPS_To_MGRS converts UPS (hemisphere, easting, + * and northing) coordinates to an MGRS coordinate string according to + * the current ellipsoid parameters. If any errors occur, the error + * code(s) are returned by the function, otherwise UPS_NO_ERROR is + * returned. + * + * Hemisphere : Hemisphere either 'N' or 'S' (input) + * Easting : Easting/X in meters (input) + * Northing : Northing/Y in meters (input) + * Precision : Precision level of MGRS string (input) + * MGRS : MGRS coordinate string (output) + */ +{ /* Convert_UPS_To_MGRS */ + double false_easting; /* False easting for 2nd letter */ + double false_northing; /* False northing for 3rd letter */ + double grid_easting; /* Easting used to derive 2nd letter of MGRS */ + double grid_northing; /* Northing used to derive 3rd letter of MGRS */ + long ltr2_low_value; /* 2nd letter range - low number */ + int letters[MGRS_LETTERS]; /* Number location of 3 letters in alphabet */ + double divisor; + int index = 0; + long error_code = MGRS_NO_ERROR; + + if ((Hemisphere != 'N') && (Hemisphere != 'S')) + error_code |= MGRS_HEMISPHERE_ERROR; + if ((Easting < MIN_EAST_NORTH) || (Easting > MAX_EAST_NORTH)) + error_code |= MGRS_EASTING_ERROR; + if ((Northing < MIN_EAST_NORTH) || (Northing > MAX_EAST_NORTH)) + error_code |= MGRS_NORTHING_ERROR; + if ((Precision < 0) || (Precision > MAX_PRECISION)) + error_code |= MGRS_PRECISION_ERROR; + if (!error_code) + { + divisor = pow (10.0, (5 - Precision)); + Easting = Round_MGRS (Easting/divisor) * divisor; + Northing = Round_MGRS (Northing/divisor) * divisor; + + if (Hemisphere == 'N') + { + if (Easting >= TWOMIL) + letters[0] = LETTER_Z; + else + letters[0] = LETTER_Y; + + index = letters[0] - 22; + ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value; + false_easting = UPS_Constant_Table[index].false_easting; + false_northing = UPS_Constant_Table[index].false_northing; + } + else + { + if (Easting >= TWOMIL) + letters[0] = LETTER_B; + else + letters[0] = LETTER_A; + + ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value; + false_easting = UPS_Constant_Table[letters[0]].false_easting; + false_northing = UPS_Constant_Table[letters[0]].false_northing; + } + + grid_northing = Northing; + grid_northing = grid_northing - false_northing; + letters[2] = (long)(grid_northing / ONEHT); + + if (letters[2] > LETTER_H) + letters[2] = letters[2] + 1; + + if (letters[2] > LETTER_N) + letters[2] = letters[2] + 1; + + grid_easting = Easting; + grid_easting = grid_easting - false_easting; + letters[1] = ltr2_low_value + ((long)(grid_easting / ONEHT)); + + if (Easting < TWOMIL) + { + if (letters[1] > LETTER_L) + letters[1] = letters[1] + 3; + + if (letters[1] > LETTER_U) + letters[1] = letters[1] + 2; + } + else + { + if (letters[1] > LETTER_C) + letters[1] = letters[1] + 2; + + if (letters[1] > LETTER_H) + letters[1] = letters[1] + 1; + + if (letters[1] > LETTER_L) + letters[1] = letters[1] + 3; + } + + Make_MGRS_String (MGRS, 0, letters, Easting, Northing, Precision); + } + return (error_code); +} /* Convert_UPS_To_MGRS */ + + +long Convert_MGRS_To_UPS ( char *MGRS, + char *Hemisphere, + double *Easting, + double *Northing) +/* + * The function Convert_MGRS_To_UPS converts an MGRS coordinate string + * to UPS (hemisphere, easting, and northing) coordinates, according + * to the current ellipsoid parameters. If any errors occur, the error + * code(s) are returned by the function, otherwide UPS_NO_ERROR is returned. + * + * MGRS : MGRS coordinate string (input) + * Hemisphere : Hemisphere either 'N' or 'S' (output) + * Easting : Easting/X in meters (output) + * Northing : Northing/Y in meters (output) + */ +{ /* Convert_MGRS_To_UPS */ + long ltr2_high_value; /* 2nd letter range - high number */ + long ltr3_high_value; /* 3rd letter range - high number (UPS) */ + long ltr2_low_value; /* 2nd letter range - low number */ + double false_easting; /* False easting for 2nd letter */ + double false_northing; /* False northing for 3rd letter */ + double grid_easting; /* easting for 100,000 meter grid square */ + double grid_northing; /* northing for 100,000 meter grid square */ + long zone; + long letters[MGRS_LETTERS]; + long in_precision; + int index = 0; + long error_code = MGRS_NO_ERROR; + + error_code = Break_MGRS_String (MGRS, &zone, letters, Easting, Northing, &in_precision); + if (zone) + error_code |= MGRS_STRING_ERROR; + else + { + if (!error_code) + { + if (letters[0] >= LETTER_Y) + { + *Hemisphere = 'N'; + + index = letters[0] - 22; + ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value; + ltr2_high_value = UPS_Constant_Table[index].ltr2_high_value; + ltr3_high_value = UPS_Constant_Table[index].ltr3_high_value; + false_easting = UPS_Constant_Table[index].false_easting; + false_northing = UPS_Constant_Table[index].false_northing; + } + else + { + *Hemisphere = 'S'; + + ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value; + ltr2_high_value = UPS_Constant_Table[letters[0]].ltr2_high_value; + ltr3_high_value = UPS_Constant_Table[letters[0]].ltr3_high_value; + false_easting = UPS_Constant_Table[letters[0]].false_easting; + false_northing = UPS_Constant_Table[letters[0]].false_northing; + } + + /* Check that the second letter of the MGRS string is within + * the range of valid second letter values + * Also check that the third letter is valid */ + if ((letters[1] < ltr2_low_value) || (letters[1] > ltr2_high_value) || + ((letters[1] == LETTER_D) || (letters[1] == LETTER_E) || + (letters[1] == LETTER_M) || (letters[1] == LETTER_N) || + (letters[1] == LETTER_V) || (letters[1] == LETTER_W)) || + (letters[2] > ltr3_high_value)) + error_code = MGRS_STRING_ERROR; + + if (!error_code) + { + grid_northing = (double)letters[2] * ONEHT + false_northing; + if (letters[2] > LETTER_I) + grid_northing = grid_northing - ONEHT; + + if (letters[2] > LETTER_O) + grid_northing = grid_northing - ONEHT; + + grid_easting = (double)((letters[1]) - ltr2_low_value) * ONEHT + false_easting; + if (ltr2_low_value != LETTER_A) + { + if (letters[1] > LETTER_L) + grid_easting = grid_easting - 300000.0; + + if (letters[1] > LETTER_U) + grid_easting = grid_easting - 200000.0; + } + else + { + if (letters[1] > LETTER_C) + grid_easting = grid_easting - 200000.0; + + if (letters[1] > LETTER_I) + grid_easting = grid_easting - ONEHT; + + if (letters[1] > LETTER_L) + grid_easting = grid_easting - 300000.0; + } + + *Easting = grid_easting + *Easting; + *Northing = grid_northing + *Northing; + } + } + } + return (error_code); +} /* Convert_MGRS_To_UPS */ + + + diff --git a/bazaar/plugin/gdal/frmts/nitf/mgrs.h b/bazaar/plugin/gdal/frmts/nitf/mgrs.h new file mode 100644 index 000000000..4ffd1c48a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/nitf/mgrs.h @@ -0,0 +1,260 @@ +#ifndef MGRS_H + #define MGRS_H + +/*************************************************************************** + * $Id: mgrs.h 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: MGRS Converter + * Purpose: Geotrans declarations for MGRS translation (slightly adapted) + * Author: Unknown (NIMA) + * + *************************************************************************** + *************************************************************************** + * RSC IDENTIFIER: MGRS + * + * ABSTRACT + * + * This component converts between geodetic coordinates (latitude and + * longitude) and Military Grid Reference System (MGRS) coordinates. + * + * ERROR HANDLING + * + * This component checks parameters for valid values. If an invalid value + * is found, the error code is combined with the current error code using + * the bitwise or. This combining allows multiple error codes to be + * returned. The possible error codes are: + * + * MGRS_NO_ERROR : No errors occurred in function + * MGRS_LAT_ERROR : Latitude outside of valid range + * (-90 to 90 degrees) + * MGRS_LON_ERROR : Longitude outside of valid range + * (-180 to 360 degrees) + * MGRS_STR_ERROR : An MGRS string error: string too long, + * too short, or badly formed + * MGRS_PRECISION_ERROR : The precision must be between 0 and 5 + * inclusive. + * MGRS_A_ERROR : Semi-major axis less than or equal to zero + * MGRS_INV_F_ERROR : Inverse flattening outside of valid range + * (250 to 350) + * MGRS_EASTING_ERROR : Easting outside of valid range + * (100,000 to 900,000 meters for UTM) + * (0 to 4,000,000 meters for UPS) + * MGRS_NORTHING_ERROR : Northing outside of valid range + * (0 to 10,000,000 meters for UTM) + * (0 to 4,000,000 meters for UPS) + * MGRS_ZONE_ERROR : Zone outside of valid range (1 to 60) + * MGRS_HEMISPHERE_ERROR : Invalid hemisphere ('N' or 'S') + * + * REUSE NOTES + * + * MGRS is intended for reuse by any application that does conversions + * between geodetic coordinates and MGRS coordinates. + * + * REFERENCES + * + * Further information on MGRS can be found in the Reuse Manual. + * + * MGRS originated from : U.S. Army Topographic Engineering Center + * Geospatial Information Division + * 7701 Telegraph Road + * Alexandria, VA 22310-3864 + * + * LICENSES + * + * None apply to this component. + * + * RESTRICTIONS + * + * + * ENVIRONMENT + * + * MGRS was tested and certified in the following environments: + * + * 1. Solaris 2.5 with GCC version 2.8.1 + * 2. Windows 95 with MS Visual C++ version 6 + * + * MODIFICATIONS + * + * Date Description + * ---- ----------- + * 16-11-94 Original Code + * 15-09-99 Reengineered upper layers + * + */ + + +/***************************************************************************/ +/* + * DEFINES + */ + + #define MGRS_NO_ERROR 0x0000 + #define MGRS_LAT_ERROR 0x0001 + #define MGRS_LON_ERROR 0x0002 + #define MGRS_STRING_ERROR 0x0004 + #define MGRS_PRECISION_ERROR 0x0008 + #define MGRS_A_ERROR 0x0010 + #define MGRS_INV_F_ERROR 0x0020 + #define MGRS_EASTING_ERROR 0x0040 + #define MGRS_NORTHING_ERROR 0x0080 + #define MGRS_ZONE_ERROR 0x0100 + #define MGRS_HEMISPHERE_ERROR 0x0200 + + +/***************************************************************************/ +/* + * FUNCTION PROTOTYPES + */ + +/* ensure proper linkage to c++ programs */ + #ifdef __cplusplus +extern "C" { + #endif + + + long Set_MGRS_Parameters(double a, + double f, + char *Ellipsoid_Code); +/* + * The function Set_MGRS_Parameters receives the ellipsoid parameters and sets + * the corresponding state variables. If any errors occur, the error code(s) + * are returned by the function, otherwise MGRS_NO_ERROR is returned. + * + * a : Semi-major axis of ellipsoid in meters (input) + * f : Flattening of ellipsoid (input) + * Ellipsoid_Code : 2-letter code for ellipsoid (input) + */ + + + void Get_MGRS_Parameters(double *a, + double *f, + char *Ellipsoid_Code); +/* + * The function Get_MGRS_Parameters returns the current ellipsoid + * parameters. + * + * a : Semi-major axis of ellipsoid, in meters (output) + * f : Flattening of ellipsoid (output) + * Ellipsoid_Code : 2-letter code for ellipsoid (output) + */ + + + long Convert_Geodetic_To_MGRS (double Latitude, + double Longitude, + long Precision, + char *MGRS); +/* + * The function Convert_Geodetic_To_MGRS converts geodetic (latitude and + * longitude) coordinates to an MGRS coordinate string, according to the + * current ellipsoid parameters. If any errors occur, the error code(s) + * are returned by the function, otherwise MGRS_NO_ERROR is returned. + * + * Latitude : Latitude in radians (input) + * Longitude : Longitude in radians (input) + * Precision : Precision level of MGRS string (input) + * MGRS : MGRS coordinate string (output) + * + */ + + + long Convert_MGRS_To_Geodetic (char *MGRS, + double *Latitude, + double *Longitude); +/* + * This function converts an MGRS coordinate string to Geodetic (latitude + * and longitude in radians) coordinates. If any errors occur, the error + * code(s) are returned by the function, otherwise MGRS_NO_ERROR is returned. + * + * MGRS : MGRS coordinate string (input) + * Latitude : Latitude in radians (output) + * Longitude : Longitude in radians (output) + * + */ + + + long Convert_UTM_To_MGRS (long Zone, + char Hemisphere, + double Easting, + double Northing, + long Precision, + char *MGRS); +/* + * The function Convert_UTM_To_MGRS converts UTM (zone, easting, and + * northing) coordinates to an MGRS coordinate string, according to the + * current ellipsoid parameters. If any errors occur, the error code(s) + * are returned by the function, otherwise MGRS_NO_ERROR is returned. + * + * Zone : UTM zone (input) + * Hemisphere : North or South hemisphere (input) + * Easting : Easting (X) in meters (input) + * Northing : Northing (Y) in meters (input) + * Precision : Precision level of MGRS string (input) + * MGRS : MGRS coordinate string (output) + */ + + + long Convert_MGRS_To_UTM (char *MGRS, + long *Zone, + char *Hemisphere, + double *Easting, + double *Northing); +/* + * The function Convert_MGRS_To_UTM converts an MGRS coordinate string + * to UTM projection (zone, hemisphere, easting and northing) coordinates + * according to the current ellipsoid parameters. If any errors occur, + * the error code(s) are returned by the function, otherwise UTM_NO_ERROR + * is returned. + * + * MGRS : MGRS coordinate string (input) + * Zone : UTM zone (output) + * Hemisphere : North or South hemisphere (output) + * Easting : Easting (X) in meters (output) + * Northing : Northing (Y) in meters (output) + */ + + + + long Convert_UPS_To_MGRS ( char Hemisphere, + double Easting, + double Northing, + long Precision, + char *MGRS); + +/* + * The function Convert_UPS_To_MGRS converts UPS (hemisphere, easting, + * and northing) coordinates to an MGRS coordinate string according to + * the current ellipsoid parameters. If any errors occur, the error + * code(s) are returned by the function, otherwise UPS_NO_ERROR is + * returned. + * + * Hemisphere : Hemisphere either 'N' or 'S' (input) + * Easting : Easting/X in meters (input) + * Northing : Northing/Y in meters (input) + * Precision : Precision level of MGRS string (input) + * MGRS : MGRS coordinate string (output) + */ + + + long Convert_MGRS_To_UPS ( char *MGRS, + char *Hemisphere, + double *Easting, + double *Northing); +/* + * The function Convert_MGRS_To_UPS converts an MGRS coordinate string + * to UPS (hemisphere, easting, and northing) coordinates, according + * to the current ellipsoid parameters. If any errors occur, the error + * code(s) are returned by the function, otherwide UPS_NO_ERROR is returned. + * + * MGRS : MGRS coordinate string (input) + * Hemisphere : Hemisphere either 'N' or 'S' (output) + * Easting : Easting/X in meters (output) + * Northing : Northing/Y in meters (output) + */ + + + + #ifdef __cplusplus +} + #endif + +#endif /* MGRS_H */ diff --git a/bazaar/plugin/gdal/frmts/nitf/nitf_gcprpc.cpp b/bazaar/plugin/gdal/frmts/nitf/nitf_gcprpc.cpp new file mode 100644 index 000000000..3c393868c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/nitf/nitf_gcprpc.cpp @@ -0,0 +1,249 @@ +/****************************************************************************** + * $Id: nitf_gcprpc.cpp 22701 2011-07-11 18:37:31Z rouault $ + * + * Project: NITF Read/Write Translator + * Purpose: GCP / RPC Georeferencing Model (custom by/for ESRI) + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, ESRI + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "nitflib.h" + +CPL_CVSID("$Id"); + +/* Unused in normal builds. Caller code in nitfdataset.cpp is protected by #ifdef ESRI_BUILD */ +#ifdef ESRI_BUILD + +/************************************************************************/ +/* Apply() */ +/************************************************************************/ +static double Apply( double *C, double P, double L, double H ) +{ + // Polynomial equation for RPC00B. + + double H2 = H * H; + double L2 = L * L; + double P2 = P * P; + + return C[0] + + C[1]*L + C[2]*P + C[3]*H + C[4]*L*P + C[5]*L*H + + C[6]*P*H + C[7]*L2 + C[8]*P2 + C[9]*H2 + C[10]*P*L*H + + C[11]*L*L2 + C[12]*L*P2 + C[13]*L*H2 + C[14]*L2*P + C[15]*P*P2 + + C[16]*P*H2 + C[17]*L2*H + C[18]*P2*H + C[19]*H*H2; +} + +/************************************************************************/ +/* NITFDensifyGCPs() */ +/************************************************************************/ +void NITFDensifyGCPs( GDAL_GCP **psGCPs, int *pnGCPCount ) +{ + // Given the four corner points of an extent (UL, UR, LR, LL), this method + // will add three points to each line segment and return a total of 16 points + // including the four original corner points. + + if ( (*pnGCPCount != 4) || (psGCPs == NULL) ) return; + + const int nDensifiedGCPs = 16; + GDAL_GCP *psDensifiedGCPs = (GDAL_GCP*) CPLMalloc(sizeof(GDAL_GCP)*nDensifiedGCPs); + + GDALInitGCPs( nDensifiedGCPs, psDensifiedGCPs ); + + bool ok = true; + double xLeftPt = 0.0; + double xMidPt = 0.0; + double xRightPt = 0.0; + double yLeftPt = 0.0; + double yMidPt = 0.0; + double yRightPt = 0.0; + int count = 0; + int idx = 0; + + for( int ii = 0; ( (ii < *pnGCPCount) && (ok) ) ; ++ii ) + { + idx = ( ii != 3 ) ? ii+1 : 0; + + try + { + psDensifiedGCPs[count].dfGCPX = (*psGCPs)[ii].dfGCPX; + psDensifiedGCPs[count].dfGCPY = (*psGCPs)[ii].dfGCPY; + + xMidPt = ((*psGCPs)[ii].dfGCPX+(*psGCPs)[idx].dfGCPX) * 0.5; + yMidPt = ((*psGCPs)[ii].dfGCPY+(*psGCPs)[idx].dfGCPY) * 0.5; + + xLeftPt = ((*psGCPs)[ii].dfGCPX+xMidPt) * 0.5; + yLeftPt = ((*psGCPs)[ii].dfGCPY+yMidPt) * 0.5; + + xRightPt = (xMidPt+(*psGCPs)[idx].dfGCPX) * 0.5; + yRightPt = (yMidPt+(*psGCPs)[idx].dfGCPY) * 0.5; + + psDensifiedGCPs[count+1].dfGCPX = xLeftPt; + psDensifiedGCPs[count+1].dfGCPY = yLeftPt; + psDensifiedGCPs[count+2].dfGCPX = xMidPt; + psDensifiedGCPs[count+2].dfGCPY = yMidPt; + psDensifiedGCPs[count+3].dfGCPX = xRightPt; + psDensifiedGCPs[count+3].dfGCPY = yRightPt; + + count += *pnGCPCount; + + } + catch (...) + { + ok = false; + } + } + + if( !ok ) + { + GDALDeinitGCPs( nDensifiedGCPs, psDensifiedGCPs ); + CPLFree( psDensifiedGCPs ); + psDensifiedGCPs = NULL; + + return; + } + + GDALDeinitGCPs( *pnGCPCount, *psGCPs ); + CPLFree( *psGCPs ); + + *psGCPs = psDensifiedGCPs; + *pnGCPCount = nDensifiedGCPs; + psDensifiedGCPs = NULL; +} + +/************************************************************************/ +/* RPCTransform() */ +/************************************************************************/ + +static bool RPCTransform( NITFRPC00BInfo *psRPCInfo, + double *pGCPXCoord, + double *pGCPYCoord, + int nGCPCount ) +{ + if( (psRPCInfo == NULL) || (pGCPXCoord == NULL) || + (pGCPYCoord == NULL) || (nGCPCount <= 0) ) return (false); + + bool ok = true; + double H = 0.0; + double L = 0.0; + double P = 0.0; + double u = 0.0; + double v = 0.0; + double z = psRPCInfo->HEIGHT_OFF; + + double heightScaleInv = 1.0/psRPCInfo->HEIGHT_SCALE; + double latScaleInv = 1.0/psRPCInfo->LAT_SCALE; + double longScaleInv = 1.0/psRPCInfo->LONG_SCALE; + + for( int ii = 0; ( (ii < nGCPCount) && (ok) ); ++ii ) + { + try + { + P = ( pGCPYCoord[ii] - psRPCInfo->LAT_OFF ) * latScaleInv; + L = ( pGCPXCoord[ii] - psRPCInfo->LONG_OFF) * longScaleInv; + H = ( z - psRPCInfo->HEIGHT_OFF ) * heightScaleInv; + + u = Apply( psRPCInfo->SAMP_NUM_COEFF, P, L, H )/Apply( psRPCInfo->SAMP_DEN_COEFF, P, L, H ); + v = Apply( psRPCInfo->LINE_NUM_COEFF, P, L, H )/Apply( psRPCInfo->LINE_DEN_COEFF, P, L, H ); + + pGCPXCoord[ii] = u*psRPCInfo->SAMP_SCALE + psRPCInfo->SAMP_OFF; + pGCPYCoord[ii] = v*psRPCInfo->LINE_SCALE + psRPCInfo->LINE_OFF; + } + catch (...) + { + ok = false; + } + } + + return (ok); +} + +/************************************************************************/ +/* NITFUpdateGCPsWithRPC() */ +/************************************************************************/ + +void NITFUpdateGCPsWithRPC( NITFRPC00BInfo *psRPCInfo, + GDAL_GCP *psGCPs, + int *pnGCPCount ) +{ + if( (psRPCInfo == NULL) || (!psRPCInfo->SUCCESS) || + (psGCPs == NULL) || (*pnGCPCount < 4) ) return; + + double *pGCPXCoord = NULL; + double *pGCPYCoord = NULL; + + try + { + pGCPXCoord = new double[*pnGCPCount]; + pGCPYCoord = new double[*pnGCPCount]; + } + catch (...) + { + if( pGCPXCoord != NULL ) + { + delete [] (pGCPXCoord); + pGCPXCoord = NULL; + } + + if( pGCPYCoord != NULL ) + { + delete [] (pGCPYCoord); + pGCPYCoord = NULL; + } + } + + if( (pGCPXCoord == NULL) || (pGCPYCoord == NULL) ) return; + + bool ok = true; + + for( int ii = 0; ( (ii < *pnGCPCount) && (ok) ); ++ii ) + { + try + { + pGCPXCoord[ii] = psGCPs[ii].dfGCPX; + pGCPYCoord[ii] = psGCPs[ii].dfGCPY; + } + catch (...) + { + ok = false; + } + } + + if( (ok) && (RPCTransform( psRPCInfo, pGCPXCoord, pGCPYCoord, *pnGCPCount )) ) + { + // Replace the image coordinates of the input GCPs. + + for( int jj = 0; jj < *pnGCPCount; ++jj ) + { + psGCPs[jj].dfGCPPixel = pGCPXCoord[jj]; + psGCPs[jj].dfGCPLine = pGCPYCoord[jj]; + } + } + + delete [] (pGCPXCoord); + delete [] (pGCPYCoord); + + pGCPXCoord = NULL; + pGCPYCoord = NULL; +} + +#endif diff --git a/bazaar/plugin/gdal/frmts/nitf/nitfaridpcm.cpp b/bazaar/plugin/gdal/frmts/nitf/nitfaridpcm.cpp new file mode 100644 index 000000000..eea2f16d5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/nitf/nitfaridpcm.cpp @@ -0,0 +1,521 @@ +/****************************************************************************** + * $Id: nitfaridpcm.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: NITF Read/Write Library + * Purpose: ARIDPCM reading code. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal.h" +#include "nitflib.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: nitfaridpcm.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +static const int neighbourhood_size_75[4] = { 23, 47, 74, 173 }; +static const int bits_per_level_by_busycode_75[4/*busy code*/][4/*level*/] = { + { 8, 5, 0, 0 }, // BC = 00 + { 8, 5, 2, 0 }, // BC = 01 + { 8, 6, 4, 0 }, // BC = 10 + { 8, 7, 4, 2 }};// BC = 11 + +#define CR075 1 + +// Level for each index value. +static const int level_index_table[64] = +{ 0, + 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; + +// List of i,j to linear index macros mappings. +// Note that i is vertical and j is horizontal and progression is +// right to left, bottom to top. + +#define IND(i,j) (ij_index[i+j*8]-1) + +static const int ij_index[64] = { + + 1, // 0, 0 + 18, // 1, 0 + 6, // 2, 0 + 30, // 3, 0 + 3, // 4, 0 + 42, // 5, 0 + 12, // 6, 0 + 54, // 7, 0 + + 17, // 0, 1 + 19, // 1, 1 + 29, // 2, 1 + 31, // 3, 1 + 41, // 4, 1 + 43, // 5, 1 + 53, // 6, 1 + 55, // 7, 1 + + 5, // 0, 2 + 21, // 1, 2 + 7, // 2, 2 + 33, // 3, 2 + 11, // 4, 2 + 45, // 5, 2 + 13, // 6, 2 + 57, // 7, 2 + + 20, // 0, 3 + 22, // 1, 3 + 32, // 2, 3 + 34, // 3, 3 + 44, // 4, 3 + 46, // 5, 3 + 56, // 6, 3 + 58, // 7, 3 + + 2, // 0, 4 + 24, // 1, 4 + 9, // 2, 4 + 36, // 3, 4 + 4, // 4, 4 + 48, // 5, 4 + 15, // 6, 4 + 60, // 7, 4 + + 23, // 0, 5 + 25, // 1, 5 + 35, // 2, 5 + 37, // 3, 5 + 47, // 4, 5 + 49, // 5, 5 + 59, // 6, 5 + 61, // 7, 5 + + 8, // 0, 6 + 27, // 1, 6 + 10, // 2, 6 + 39, // 3, 6 + 14, // 4, 6 + 51, // 5, 6 + 16, // 6, 6 + 63, // 7, 6 + + 26, // 0, 7 + 28, // 1, 7 + 38, // 2, 7 + 40, // 3, 7 + 50, // 4, 7 + 52, // 5, 7 + 62, // 6, 7 + 64};// 7, 7 + +static const int delta_075_level_2_bc_0[32] = +{-71, -49, -38, -32, -27, -23, -20, -17, -14, -12, -10, -8, -6, -4, -3, -1, + 1, 2, 4, 6, 8, 12, 14, 16, 19, 22, 26, 31, 37, 46, 72 }; +static const int delta_075_level_2_bc_1[32] = +{-71, -49, -38, -32, -27, -23, -20, -17, -14, -12, -10, -8, -6, -4, -3, -1, + 1, 2, 4, 6, 8, 12, 14, 16, 19, 22, 26, 31, 37, 46, 72 }; +static const int delta_075_level_2_bc_2[64] = +{ -109, -82, -68, -59, -52, -46, -41, -37, -33, -30, -27, -25, -22, -20, + -18, -16, -15, -13, -11, -10, -9, -8, -7, -6, -5, + -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12, + 13,14,15,16,17,18,19,20,21,24,26,28,31,35,38, + 42,47,52,60,69,85,118}; +static const int delta_075_level_2_bc_3[128] = +{-159,-134,-122,-113,-106,-100,-94,-88,-83,-79,-76,-72,-69,-66,-63,-61, + -58,-56,-54,-52,-50,-48,-47,-45,-43,-42,-40,-39,-37,-36,-35,-33,-32,-31, + -30,-29,-28,-27,-25,-24,-23,-22,-21,-20,-19,-18,-17,-16,-15,-14, + -13,-12,-11,-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,10,11, + 12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34, + 35,36,37,38,39,40,41,42,43,45,48,52,56,60,64,68,73,79,85,92,100,109, + 118,130,144,159,177,196,217,236}; +static const int *delta_075_level_2[4] = +{ delta_075_level_2_bc_0, delta_075_level_2_bc_1, + delta_075_level_2_bc_2, delta_075_level_2_bc_3 }; + + +static const int delta_075_level_3_bc_1[4] = { -24, -6, 6, 24 }; +static const int delta_075_level_3_bc_2[16] = +{-68,-37,-23,-15, -9, -6, -3, -1, 1, 4, 7,10,16,24,37,70 }; +static const int delta_075_level_3_bc_3[16] = +{-117,-72, -50, -36, -25, -17, -10, -5,-1, 3, 7,14,25,45,82,166}; +static const int *delta_075_level_3[4] = +{ NULL, delta_075_level_3_bc_1, + delta_075_level_3_bc_2, delta_075_level_3_bc_3 }; + +static const int delta_075_level_4_bc_3[4] = {-47,-8,4,43}; +static const int *delta_075_level_4[4] = { NULL, NULL, NULL, delta_075_level_4_bc_3 }; + +static const int **delta_075_by_level_by_bc[4] = +{ NULL, delta_075_level_2, delta_075_level_3, delta_075_level_4 }; + +/************************************************************************/ +/* get_bits() */ +/************************************************************************/ + +static int +get_bits( unsigned char *buffer, int first_bit, int num_bits ) + +{ + int i; + int total =0; + + for( i = first_bit; i < first_bit+num_bits; i++ ) + { + total = total * 2; + if( buffer[i>>3] & (0x80 >> (i&7)) ) + total++; + } + + return total; +} + +/************************************************************************/ +/* get_delta() */ +/* */ +/* Compute the delta value for a particular (i,j) location. */ +/************************************************************************/ +static int +get_delta( unsigned char *srcdata, + int nInputBytes, + int busy_code, + CPL_UNUSED int comrat, + int block_offset, + CPL_UNUSED int block_size, + int i, + int j, + int *pbError ) + +{ + CPLAssert( comrat == CR075 ); + int pixel_index = IND(i,j); + int level_index = level_index_table[pixel_index]; + const int *bits_per_level = bits_per_level_by_busycode_75[busy_code]; + int delta_bits = bits_per_level[level_index]; + int delta_offset = 0; + + *pbError = FALSE; + + if( delta_bits == 0 ) + return 0; + + if( level_index == 3 ) + delta_offset = bits_per_level[0] + bits_per_level[1] * 3 + + bits_per_level[2] * 12 + (pixel_index - 16) * bits_per_level[3]; + else if( level_index == 2 ) + delta_offset = bits_per_level[0] + bits_per_level[1] * 3 + + (pixel_index - 4) * bits_per_level[2]; + else if( level_index == 1 ) + delta_offset = bits_per_level[0] + (pixel_index-1)*bits_per_level[1]; + + if (nInputBytes * 8 < block_offset+delta_offset + delta_bits) + { + CPLError( CE_Failure, CPLE_AppDefined, "Input buffer too small"); + *pbError = TRUE; + return 0; + } + + int delta_raw = get_bits( srcdata, block_offset+delta_offset, delta_bits ); + int delta = delta_raw; + + /* Should not happen as delta_075_by_level_by_bc[level_index] == NULL if and + only if level_index == 0, which means that pixel_index == 0, which means + (i, j) = (0, 0). That cannot happen as we are never called with those + values + */ + CPLAssert( delta_075_by_level_by_bc[level_index] != NULL ); + const int *lookup_table = delta_075_by_level_by_bc[level_index][busy_code]; + + CPLAssert( lookup_table != NULL ); + delta = lookup_table[delta_raw]; + + return delta; +} + +/************************************************************************/ +/* decode_block() */ +/* */ +/* Decode one 8x8 block. The 9x9 L buffer is pre-loaded with */ +/* the left and top values from previous blocks. */ +/************************************************************************/ +static int +decode_block( unsigned char *srcdata, int nInputBytes, + int busy_code, int comrat, + int block_offset, int block_size, + int left_side, int top_side, int L[9][9] ) + +{ + int i, j; + int bError; + + // Level 2 + L[0][4] = (L[0][0] + L[0][8])/2 + + get_delta(srcdata,nInputBytes,busy_code,comrat,block_offset,block_size,0,4,&bError); + if (bError) return FALSE; + L[4][0] = (L[0][0] + L[8][0])/2 + + get_delta(srcdata,nInputBytes,busy_code,comrat,block_offset,block_size,4,0,&bError); + if (bError) return FALSE; + L[4][4] = (L[0][0] + L[8][0] + L[0][8] + L[8][8])/4 + + get_delta(srcdata,nInputBytes,busy_code,comrat,block_offset,block_size,4,4,&bError); + if (bError) return FALSE; + + if( left_side ) + L[4][8] = L[4][0]; + if( top_side ) + L[8][4] = L[0][4]; + + // Level 3 + for( i = 0; i < 8; i += 4 ) + { + for( j = 0; j < 8; j += 4 ) + { + // above + L[i+2][j] = (L[i][j]+L[i+4][j])/2 + + get_delta(srcdata,nInputBytes,busy_code,comrat, + block_offset,block_size,i+2,j,&bError); + if (bError) return FALSE; + // left + L[i][j+2] = (L[i][j]+L[i][j+4])/2 + + get_delta(srcdata,nInputBytes,busy_code,comrat, + block_offset,block_size,i,j+2,&bError); + if (bError) return FALSE; + // up-left + L[i+2][j+2] = (L[i][j]+L[i][j+4]+L[i+4][j]+L[i+4][j+4])/4 + + get_delta(srcdata,nInputBytes,busy_code,comrat, + block_offset,block_size,i+2,j+2,&bError); + if (bError) return FALSE; + } + } + + if( left_side ) + { + L[2][8] = L[2][0]; + L[6][8] = L[6][0]; + } + if( top_side ) + { + L[8][2] = L[0][2]; + L[8][6] = L[0][6]; + } + + // Level 4 + for( i = 0; i < 8; i += 2 ) + { + for( j = 0; j < 8; j += 2 ) + { + // above + L[i+1][j] = (L[i][j]+L[i+2][j])/2 + + get_delta(srcdata,nInputBytes,busy_code,comrat, + block_offset,block_size,i+1,j,&bError); + if (bError) return FALSE; + // left + L[i][j+1] = (L[i][j]+L[i][j+2])/2 + + get_delta(srcdata,nInputBytes,busy_code,comrat, + block_offset,block_size,i,j+1,&bError); + if (bError) return FALSE; + // up-left + L[i+1][j+1] = (L[i][j]+L[i][j+2]+L[i+2][j]+L[i+2][j+2])/4 + + get_delta(srcdata,nInputBytes,busy_code,comrat, + block_offset,block_size,i+1,j+1,&bError); + if (bError) return FALSE; + } + } + + return TRUE; +} + +/************************************************************************/ +/* NITFUncompressARIDPCM() */ +/************************************************************************/ + +int NITFUncompressARIDPCM( NITFImage *psImage, + GByte *pabyInputData, + int nInputBytes, + GByte *pabyOutputImage ) + +{ + +/* -------------------------------------------------------------------- */ +/* First, verify that we are a COMRAT 0.75 image, which is all */ +/* we currently support. */ +/* -------------------------------------------------------------------- */ + if( !EQUAL(psImage->szCOMRAT,"0.75") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "COMRAT=%s ARIDPCM is not supported.\n" + "Currently only 0.75 is supported.", + psImage->szCOMRAT ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Setup up the various info we need for each 8x8 neighbourhood */ +/* (which we call blocks in this context). */ +/* -------------------------------------------------------------------- */ + int blocks_x = (psImage->nBlockWidth + 7) / 8; + int blocks_y = (psImage->nBlockHeight + 7) / 8; + int block_count = blocks_x * blocks_y; + int rowlen = blocks_x * 8; + + if( psImage->nBlockWidth > 1000 || /* to detect int overflow above */ + psImage->nBlockHeight > 1000 || + block_count > 1000 ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Block too large to be decoded"); + return FALSE; + } + + int block_offset[1000]; + int block_size[1000]; + int busy_code[1000]; + int busy_code_table_size = blocks_x * blocks_y * 2; + unsigned char L00[1000]; + +/* -------------------------------------------------------------------- */ +/* We allocate a working copy of the full image that may be a */ +/* bit larger than the output buffer if the width or height is */ +/* not divisible by 8. */ +/* -------------------------------------------------------------------- */ + GByte *full_image = (GByte *) CPLMalloc(blocks_x * blocks_y * 8 * 8 ); + +/* -------------------------------------------------------------------- */ +/* Scan through all the neighbourhoods determining the busyness */ +/* code, and the offset to each's data as well as the L00 value. */ +/* -------------------------------------------------------------------- */ + int i, j, total = busy_code_table_size; + + for( i = 0; i < blocks_x * blocks_y; i++ ) + { + if (nInputBytes * 8 < i * 2 + 2) + { + CPLError( CE_Failure, CPLE_AppDefined, "Input buffer too small"); + CPLFree(full_image); + return FALSE; + } + busy_code[i] = get_bits( pabyInputData, i*2, 2 ); + + block_offset[i] = total; + block_size[i] = neighbourhood_size_75[busy_code[i]]; + + if (nInputBytes * 8 < block_offset[i] + 8) + { + CPLError( CE_Failure, CPLE_AppDefined, "Input buffer too small"); + CPLFree(full_image); + return FALSE; + } + L00[i] = (unsigned char) get_bits( pabyInputData, block_offset[i], 8 ); + + total += block_size[i]; + } + +/* -------------------------------------------------------------------- */ +/* Process all the blocks, forming into a final image. */ +/* -------------------------------------------------------------------- */ + int iX, iY; + + for( iY = 0; iY < blocks_y; iY++ ) + { + for( iX = 0; iX < blocks_x; iX++ ) + { + int iBlock = iX + iY * blocks_x; + int L[9][9]; + unsigned char *full_tl = full_image + iX * 8 + iY * 8 * rowlen; + + L[0][0] = L00[iBlock]; + if( iX > 0 ) + { + L[0][8] = full_tl[rowlen * 7 - 1]; + L[2][8] = full_tl[rowlen * 5 - 1]; + L[4][8] = full_tl[rowlen * 3 - 1]; + L[6][8] = full_tl[rowlen * 1 - 1]; + } + else + { + L[0][8] = L[0][0]; + L[2][8] = L[0][8]; // need to reconstruct the rest! + L[4][8] = L[0][8]; + L[6][8] = L[0][8]; + } + + if( iY > 0 ) + { + L[8][0] = full_tl[7 - rowlen]; + L[8][2] = full_tl[5 - rowlen]; + L[8][4] = full_tl[3 - rowlen]; + L[8][6] = full_tl[1 - rowlen]; + } + else + { + L[8][0] = L[0][0]; + L[8][2] = L[0][0]; // Need to reconstruct the rest! + L[8][4] = L[0][0]; + L[8][5] = L[0][0]; + } + + if( iX == 0 || iY == 0 ) + L[8][8] = L[0][0]; + else + L[8][8] = full_tl[-1-rowlen]; + + if (!(decode_block( pabyInputData, nInputBytes, busy_code[iBlock], CR075, + block_offset[iBlock], block_size[iBlock], + iX == 0, iY == 0, L ))) + { + CPLFree( full_image ); + return FALSE; + } + + // Assign to output matrix. + for( i = 0; i < 8; i++ ) + { + for( j = 0; j < 8; j++ ) + { + int value = L[i][j]; + if( value < 0 ) + value = 0; + if( value > 255 ) + value = 255; + + full_tl[8-j-1 + (8-i-1) * rowlen] = (unsigned char) value; + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Copy full image back into target buffer, and free. */ +/* -------------------------------------------------------------------- */ + for( iY = 0; iY < psImage->nBlockHeight; iY++ ) + { + memcpy( pabyOutputImage + iY * psImage->nBlockWidth, + full_image + iY * rowlen, + psImage->nBlockWidth ); + } + + CPLFree( full_image ); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/frmts/nitf/nitfbilevel.cpp b/bazaar/plugin/gdal/frmts/nitf/nitfbilevel.cpp new file mode 100644 index 000000000..115e7649b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/nitf/nitfbilevel.cpp @@ -0,0 +1,118 @@ +/****************************************************************************** + * $Id: nitfbilevel.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: NITF Read/Write Library + * Purpose: Module implement BILEVEL (C1) compressed image reading. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal.h" +#include "nitflib.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" + +CPL_C_START +#include "tiffio.h" +CPL_C_END + +#include "tifvsi.h" + +CPL_CVSID("$Id: nitfbilevel.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* NITFUncompressBILEVEL() */ +/************************************************************************/ + +int NITFUncompressBILEVEL( NITFImage *psImage, + GByte *pabyInputData, int nInputBytes, + GByte *pabyOutputImage ) + +{ + int nOutputBytes= (psImage->nBlockWidth * psImage->nBlockHeight + 7)/8; + +/* -------------------------------------------------------------------- */ +/* Write memory TIFF with the bilevel data. */ +/* -------------------------------------------------------------------- */ + CPLString osFilename; + + osFilename.Printf( "/vsimem/nitf-wrk-%ld.tif", (long) CPLGetPID() ); + + VSILFILE* fpL = VSIFOpenL(osFilename, "w+"); + if( fpL == NULL ) + return FALSE; + TIFF *hTIFF = VSI_TIFFOpen( osFilename, "w+", fpL ); + if (hTIFF == NULL) + { + VSIFCloseL(fpL); + return FALSE; + } + + TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, psImage->nBlockWidth ); + TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, psImage->nBlockHeight ); + TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, 1 ); + TIFFSetField( hTIFF, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT ); + TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG ); + TIFFSetField( hTIFF, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB ); + + TIFFSetField( hTIFF, TIFFTAG_ROWSPERSTRIP, psImage->nBlockHeight ); + TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, 1 ); + TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK ); + TIFFSetField( hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_CCITTFAX3 ); + + if( psImage->szCOMRAT[0] == '2' ) + TIFFSetField( hTIFF, TIFFTAG_GROUP3OPTIONS, GROUP3OPT_2DENCODING ); + + TIFFWriteRawStrip( hTIFF, 0, pabyInputData, nInputBytes ); + TIFFWriteDirectory( hTIFF ); + + TIFFClose( hTIFF ); + +/* -------------------------------------------------------------------- */ +/* Now open and read it back. */ +/* -------------------------------------------------------------------- */ + int bResult = TRUE; + + hTIFF = VSI_TIFFOpen( osFilename, "r", fpL ); + if (hTIFF == NULL) + { + VSIFCloseL(fpL); + return FALSE; + } + + + if( TIFFReadEncodedStrip( hTIFF, 0, pabyOutputImage, nOutputBytes ) == -1 ) + { + memset( pabyOutputImage, 0, nOutputBytes ); + bResult = FALSE; + } + + TIFFClose( hTIFF ); + VSIFCloseL(fpL); + + VSIUnlink( osFilename ); + + return bResult; +} diff --git a/bazaar/plugin/gdal/frmts/nitf/nitfdataset.cpp b/bazaar/plugin/gdal/frmts/nitf/nitfdataset.cpp new file mode 100644 index 000000000..3e3a0f502 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/nitf/nitfdataset.cpp @@ -0,0 +1,5758 @@ +/****************************************************************************** + * $Id: nitfdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: NITF Read/Write Translator + * Purpose: NITFDataset and driver related implementations. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Portions Copyright (c) Her majesty the Queen in right of Canada as + * represented by the Minister of National Defence, 2006. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "nitfdataset.h" +#include "cpl_string.h" +#include "cpl_csv.h" + +CPL_CVSID("$Id: nitfdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +static void NITFPatchImageLength( const char *pszFilename, + GUIntBig nImageOffset, + GIntBig nPixelCount, const char *pszIC ); +static int NITFWriteCGMSegments( const char *pszFilename, char **papszList ); +static void NITFWriteTextSegments( const char *pszFilename, char **papszList ); + +#ifdef JPEG_SUPPORTED +static int NITFWriteJPEGImage( GDALDataset *, VSILFILE *, vsi_l_offset, char **, + GDALProgressFunc pfnProgress, + void * pProgressData ); +#endif + +#ifdef ESRI_BUILD +static void SetBandMetadata( NITFImage *psImage, GDALRasterBand *poBand, int nBand ); +#endif + +/************************************************************************/ +/* ==================================================================== */ +/* NITFDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* NITFDataset() */ +/************************************************************************/ + +NITFDataset::NITFDataset() + +{ + psFile = NULL; + psImage = NULL; + bGotGeoTransform = FALSE; + pszProjection = CPLStrdup(""); + poJ2KDataset = NULL; + bJP2Writing = FALSE; + poJPEGDataset = NULL; + + panJPEGBlockOffset = NULL; + pabyJPEGBlock = NULL; + nQLevel = 0; + + nGCPCount = 0; + pasGCPList = NULL; + pszGCPProjection = NULL; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + poDriver = (GDALDriver*) GDALGetDriverByName("NITF"); + + papszTextMDToWrite = NULL; + papszCgmMDToWrite = NULL; + + bInLoadXML = FALSE; + bExposeUnderlyingJPEGDatasetOverviews = FALSE; +} + +/************************************************************************/ +/* ~NITFDataset() */ +/************************************************************************/ + +NITFDataset::~NITFDataset() + +{ + CloseDependentDatasets(); + +/* -------------------------------------------------------------------- */ +/* Free datastructures. */ +/* -------------------------------------------------------------------- */ + CPLFree( pszProjection ); + + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + CPLFree( pszGCPProjection ); + + CPLFree( panJPEGBlockOffset ); + CPLFree( pabyJPEGBlock ); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int NITFDataset::CloseDependentDatasets() +{ + FlushCache(); + + int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets(); + +/* -------------------------------------------------------------------- */ +/* If we have been writing to a JPEG2000 file, check if the */ +/* color interpretations were set. If so, apply the settings */ +/* to the NITF file. */ +/* -------------------------------------------------------------------- */ + if( poJ2KDataset != NULL && bJP2Writing ) + { + int i; + + for( i = 0; i < nBands && papoBands != NULL; i++ ) + { + if( papoBands[i]->GetColorInterpretation() != GCI_Undefined ) + NITFSetColorInterpretation( psImage, i+1, + papoBands[i]->GetColorInterpretation() ); + } + } + +/* -------------------------------------------------------------------- */ +/* Close the underlying NITF file. */ +/* -------------------------------------------------------------------- */ + GUIntBig nImageStart = 0; + if( psFile != NULL ) + { + if (psFile->nSegmentCount > 0) + nImageStart = psFile->pasSegmentInfo[0].nSegmentStart; + + NITFClose( psFile ); + psFile = NULL; + } + +/* -------------------------------------------------------------------- */ +/* If we have a jpeg2000 output file, make sure it gets closed */ +/* and flushed out. */ +/* -------------------------------------------------------------------- */ + if( poJ2KDataset != NULL ) + { + GDALClose( (GDALDatasetH) poJ2KDataset ); + poJ2KDataset = NULL; + bHasDroppedRef = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Update file length, and COMRAT for JPEG2000 files we are */ +/* writing to. */ +/* -------------------------------------------------------------------- */ + if( bJP2Writing ) + { + GIntBig nPixelCount = nRasterXSize * ((GIntBig) nRasterYSize) * + nBands; + + NITFPatchImageLength( GetDescription(), nImageStart, nPixelCount, + "C8" ); + } + + bJP2Writing = FALSE; + +/* -------------------------------------------------------------------- */ +/* If we have a jpeg output file, make sure it gets closed */ +/* and flushed out. */ +/* -------------------------------------------------------------------- */ + if( poJPEGDataset != NULL ) + { + GDALClose( (GDALDatasetH) poJPEGDataset ); + poJPEGDataset = NULL; + bHasDroppedRef = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* If the dataset was opened by Create(), we may need to write */ +/* the CGM and TEXT segments */ +/* -------------------------------------------------------------------- */ + NITFWriteCGMSegments( GetDescription(), papszCgmMDToWrite ); + NITFWriteTextSegments( GetDescription(), papszTextMDToWrite ); + + CSLDestroy(papszTextMDToWrite); + papszTextMDToWrite = NULL; + CSLDestroy(papszCgmMDToWrite); + papszCgmMDToWrite = NULL; + +/* -------------------------------------------------------------------- */ +/* Destroy the raster bands if they exist. */ +/* We must do it now since the rasterbands can be NITFWrapperRasterBand */ +/* that derive from the GDALProxyRasterBand object, which keeps */ +/* a reference on the JPEG/JP2K dataset, so any later call to */ +/* FlushCache() would result in FlushCache() being called on a */ +/* already destroyed object */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < nBands; iBand++ ) + { + delete papoBands[iBand]; + } + nBands = 0; + + return bHasDroppedRef; +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void NITFDataset::FlushCache() + +{ + // If the JPEG/JP2K dataset has dirty pam info, then we should consider + // ourselves to as well. + if( poJPEGDataset != NULL + && (poJPEGDataset->GetPamFlags() & GPF_DIRTY) ) + MarkPamDirty(); + if( poJ2KDataset != NULL + && (poJ2KDataset->GetPamFlags() & GPF_DIRTY) ) + MarkPamDirty(); + + if( poJ2KDataset != NULL && bJP2Writing) + poJ2KDataset->FlushCache(); + + GDALPamDataset::FlushCache(); +} + +#ifdef ESRI_BUILD + +/************************************************************************/ +/* ExtractEsriMD() */ +/* */ +/* Extracts ESRI-specific required meta data from metadata */ +/* string list papszStrList. */ +/************************************************************************/ + +static char **ExtractEsriMD( char **papszMD ) +{ + char **papszEsriMD = NULL; + + if( papszMD ) + { + // These are the current generic ESRI metadata. + const char *const pEsriMDAcquisitionDate = "ESRI_MD_ACQUISITION_DATE"; + const char *const pEsriMDAngleToNorth = "ESRI_MD_ANGLE_TO_NORTH"; + const char *const pEsriMDCircularError = "ESRI_MD_CE"; + const char *const pEsriMDDataType = "ESRI_MD_DATA_TYPE"; + const char *const pEsriMDIsCloudCover = "ESRI_MD_ISCLOUDCOVER"; + const char *const pEsriMDLinearError = "ESRI_MD_LE"; + const char *const pEsriMDOffNaDir = "ESRI_MD_OFF_NADIR"; + const char *const pEsriMDPercentCloudCover = "ESRI_MD_PERCENT_CLOUD_COVER"; + const char *const pEsriMDProductName = "ESRI_MD_PRODUCT_NAME"; + const char *const pEsriMDSensorAzimuth = "ESRI_MD_SENSOR_AZIMUTH"; + const char *const pEsriMDSensorElevation = "ESRI_MD_SENSOR_ELEVATION"; + const char *const pEsriMDSensorName = "ESRI_MD_SENSOR_NAME"; + const char *const pEsriMDSunAzimuth = "ESRI_MD_SUN_AZIMUTH"; + const char *const pEsriMDSunElevation = "ESRI_MD_SUN_ELEVATION"; + + char szField[11]; + const char *pCCImageSegment = CSLFetchNameValue( papszMD, "NITF_IID1" ); + std::string ccSegment("false"); + + if( ( pCCImageSegment != NULL ) && ( strlen(pCCImageSegment) <= 10 ) ) + { + szField[0] = '\0'; + strncpy( szField, pCCImageSegment, strlen(pCCImageSegment) ); + szField[strlen(pCCImageSegment)] = '\0'; + + // Trim white off tag. + while( ( strlen(szField) > 0 ) && ( szField[strlen(szField)-1] == ' ' ) ) + szField[strlen(szField)-1] = '\0'; + + if ((strlen(szField) == 2) && (EQUALN(szField, "CC", 2))) ccSegment.assign("true"); + } + + const char *pAcquisitionDate = CSLFetchNameValue( papszMD, "NITF_FDT" ); + const char *pAngleToNorth = CSLFetchNameValue( papszMD, "NITF_CSEXRA_ANGLE_TO_NORTH" ); + const char *pCircularError = CSLFetchNameValue( papszMD, "NITF_CSEXRA_CIRCL_ERR" ); // Unit in feet. + const char *pLinearError = CSLFetchNameValue( papszMD, "NITF_CSEXRA_LINEAR_ERR" ); // Unit in feet. + const char *pPercentCloudCover = CSLFetchNameValue( papszMD, "NITF_PIAIMC_CLOUDCVR" ); + const char *pProductName = CSLFetchNameValue( papszMD, "NITF_CSDIDA_PRODUCT_ID" ); + const char *pSensorName = CSLFetchNameValue( papszMD, "NITF_PIAIMC_SENSNAME" ); + const char *pSunAzimuth = CSLFetchNameValue( papszMD, "NITF_CSEXRA_SUN_AZIMUTH" ); + const char *pSunElevation = CSLFetchNameValue( papszMD, "NITF_CSEXRA_SUN_ELEVATION" ); + + // Get ESRI_MD_DATA_TYPE. + const char *pDataType = NULL; + const char *pImgSegFieldICAT = CSLFetchNameValue( papszMD, "NITF_ICAT" ); + + if( ( pImgSegFieldICAT != NULL ) && ( EQUALN(pImgSegFieldICAT, "DTEM", 4) ) ) + pDataType = "Elevation"; + else + pDataType = "Generic"; + + if( pAngleToNorth == NULL ) + pAngleToNorth = CSLFetchNameValue( papszMD, "NITF_USE00A_ANGLE_TO_NORTH" ); + + // Percent cloud cover == 999 means that the information is not available. + if( (pPercentCloudCover != NULL) && (EQUALN(pPercentCloudCover, "999", 3)) ) + pPercentCloudCover = NULL; + + pAngleToNorth = CSLFetchNameValue( papszMD, "NITF_USE00A_ANGLE_TO_NORTH" ); + + if( pSunAzimuth == NULL ) + pSunAzimuth = CSLFetchNameValue( papszMD, "NITF_USE00A_SUN_AZ" ); + + if( pSunElevation == NULL ) + pSunElevation = CSLFetchNameValue( papszMD, "NITF_USE00A_SUN_EL" ); + + // CSLAddNameValue will not add the key/value pair if the value is NULL. + papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDAcquisitionDate, pAcquisitionDate ); + papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDAngleToNorth, pAngleToNorth ); + papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDCircularError, pCircularError ); + papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDDataType, pDataType ); + papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDIsCloudCover, ccSegment.c_str() ); + papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDLinearError, pLinearError ); + papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDProductName, pProductName ); + papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDPercentCloudCover, pPercentCloudCover ); + papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDSensorName, pSensorName ); + papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDSunAzimuth, pSunAzimuth ); + papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDSunElevation, pSunElevation ); + } + + return (papszEsriMD); +} + +/************************************************************************/ +/* SetBandMetadata() */ +/************************************************************************/ + +static void SetBandMetadata( NITFImage *psImage, GDALRasterBand *poBand, int nBand ) +{ + if( (psImage != NULL) && (poBand != NULL) && (nBand > 0) ) + { + NITFBandInfo *psBandInfo = psImage->pasBandInfo + nBand - 1; + + if( psBandInfo != NULL ) + { + // Set metadata BandName, WavelengthMax and WavelengthMin. + + if ( psBandInfo->szIREPBAND != NULL ) + { + if( EQUAL(psBandInfo->szIREPBAND,"B") ) + { + poBand->SetMetadataItem( "BandName", "Blue" ); + poBand->SetMetadataItem( "WavelengthMax", psBandInfo->szISUBCAT ); + poBand->SetMetadataItem( "WavelengthMin", psBandInfo->szISUBCAT ); + } + else if( EQUAL(psBandInfo->szIREPBAND,"G") ) + { + poBand->SetMetadataItem( "BandName", "Green" ); + poBand->SetMetadataItem( "WavelengthMax", psBandInfo->szISUBCAT ); + poBand->SetMetadataItem( "WavelengthMin", psBandInfo->szISUBCAT ); + } + else if( EQUAL(psBandInfo->szIREPBAND,"R") ) + { + poBand->SetMetadataItem( "BandName", "Red" ); + poBand->SetMetadataItem( "WavelengthMax", psBandInfo->szISUBCAT ); + poBand->SetMetadataItem( "WavelengthMin", psBandInfo->szISUBCAT ); + } + else if( EQUAL(psBandInfo->szIREPBAND,"N") ) + { + poBand->SetMetadataItem( "BandName", "NearInfrared" ); + poBand->SetMetadataItem( "WavelengthMax", psBandInfo->szISUBCAT ); + poBand->SetMetadataItem( "WavelengthMin", psBandInfo->szISUBCAT ); + } + else if( ( EQUAL(psBandInfo->szIREPBAND,"M") ) || ( ( psImage->szIREP != NULL ) && ( EQUAL(psImage->szIREP,"MONO") ) ) ) + { + poBand->SetMetadataItem( "BandName", "Panchromatic" ); + } + else + { + if( ( psImage->szICAT != NULL ) && ( EQUAL(psImage->szICAT,"IR") ) ) + { + poBand->SetMetadataItem( "BandName", "Infrared" ); + poBand->SetMetadataItem( "WavelengthMax", psBandInfo->szISUBCAT ); + poBand->SetMetadataItem( "WavelengthMin", psBandInfo->szISUBCAT ); + } + } + } + } + } +} + +#endif /* def ESRI_BUILD */ + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int NITFDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + const char *pszFilename = poOpenInfo->pszFilename; + +/* -------------------------------------------------------------------- */ +/* Is this a dataset selector? If so, it is obviously NITF. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszFilename, "NITF_IM:",8) ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Avoid that on Windows, JPEG_SUBFILE:x,y,z,data/../tmp/foo.ntf */ +/* to be recognized by the NITF driver, because */ +/* 'JPEG_SUBFILE:x,y,z,data' is considered as a (valid) directory */ +/* and thus the whole filename is evaluated as tmp/foo.ntf */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszFilename,"JPEG_SUBFILE:",13) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* First we check to see if the file has the expected header */ +/* bytes. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 4 ) + return FALSE; + + if( !EQUALN((char *) poOpenInfo->pabyHeader,"NITF",4) + && !EQUALN((char *) poOpenInfo->pabyHeader,"NSIF",4) + && !EQUALN((char *) poOpenInfo->pabyHeader,"NITF",4) ) + return FALSE; + + int i; + /* Check that it's not in fact a NITF A.TOC file, which is handled by the RPFTOC driver */ + for(i=0;i<(int)poOpenInfo->nHeaderBytes-(int)strlen("A.TOC");i++) + { + if (EQUALN((const char*)poOpenInfo->pabyHeader + i, "A.TOC", strlen("A.TOC"))) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *NITFDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + return OpenInternal(poOpenInfo, NULL, FALSE); +} + +GDALDataset *NITFDataset::OpenInternal( GDALOpenInfo * poOpenInfo, + GDALDataset *poWritableJ2KDataset, + int bOpenForCreate) + +{ + int nIMIndex = -1; + const char *pszFilename = poOpenInfo->pszFilename; + + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Select a specific subdataset. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszFilename, "NITF_IM:",8) ) + { + pszFilename += 8; + nIMIndex = atoi(pszFilename); + + while( *pszFilename != '\0' && *pszFilename != ':' ) + pszFilename++; + + if( *pszFilename == ':' ) + pszFilename++; + } + +/* -------------------------------------------------------------------- */ +/* Open the file with library. */ +/* -------------------------------------------------------------------- */ + NITFFile *psFile; + + if( poOpenInfo->fpL ) + { + VSILFILE* fpL = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + psFile = NITFOpenEx( fpL, pszFilename ); + } + else + psFile = NITFOpen( pszFilename, poOpenInfo->eAccess == GA_Update ); + if( psFile == NULL ) + { + return NULL; + } + + if (!bOpenForCreate) + { + NITFCollectAttachments( psFile ); + NITFReconcileAttachments( psFile ); + } + +/* -------------------------------------------------------------------- */ +/* Is there an image to operate on? */ +/* -------------------------------------------------------------------- */ + int iSegment, nThisIM = 0; + NITFImage *psImage = NULL; + + for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ ) + { + if( EQUAL(psFile->pasSegmentInfo[iSegment].szSegmentType,"IM") + && (nThisIM++ == nIMIndex || nIMIndex == -1) ) + { + psImage = NITFImageAccess( psFile, iSegment ); + if( psImage == NULL ) + { + NITFClose( psFile ); + return NULL; + } + break; + } + } + +/* -------------------------------------------------------------------- */ +/* If no image segments found report this to the user. */ +/* -------------------------------------------------------------------- */ + if( psImage == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "The file %s appears to be an NITF file, but no image\n" + "blocks were found on it.", + poOpenInfo->pszFilename ); + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + NITFDataset *poDS; + + poDS = new NITFDataset(); + + poDS->psFile = psFile; + poDS->psImage = psImage; + poDS->eAccess = poOpenInfo->eAccess; + poDS->osNITFFilename = pszFilename; + poDS->nIMIndex = nIMIndex; + + if( psImage ) + { + if (psImage->nCols <= 0 || psImage->nRows <= 0 || + psImage->nBlockWidth <= 0 || psImage->nBlockHeight <= 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Bad values in NITF image : nCols=%d, nRows=%d, nBlockWidth=%d, nBlockHeight=%d", + psImage->nCols, psImage->nRows, psImage->nBlockWidth, psImage->nBlockHeight); + delete poDS; + return NULL; + } + + poDS->nRasterXSize = psImage->nCols; + poDS->nRasterYSize = psImage->nRows; + } + else + { + poDS->nRasterXSize = 1; + poDS->nRasterYSize = 1; + } + + /* Can be set to NO to avoid opening the underlying JPEG2000/JPEG */ + /* stream. Might speed up operations when just metadata is needed */ + int bOpenUnderlyingDS = CSLTestBoolean( + CPLGetConfigOption("NITF_OPEN_UNDERLYING_DS", "YES")); + +/* -------------------------------------------------------------------- */ +/* If the image is JPEG2000 (C8) compressed, we will need to */ +/* open the image data as a JPEG2000 dataset. */ +/* -------------------------------------------------------------------- */ + int nUsableBands = 0; + int iBand; + int bSetColorInterpretation = TRUE; + int bSetColorTable = FALSE; + + if( psImage ) + nUsableBands = psImage->nBands; + + if( bOpenUnderlyingDS && psImage != NULL && EQUAL(psImage->szIC,"C8") ) + { + CPLString osDSName; + + osDSName.Printf( "/vsisubfile/" CPL_FRMT_GUIB "_" CPL_FRMT_GUIB ",%s", + psFile->pasSegmentInfo[iSegment].nSegmentStart, + psFile->pasSegmentInfo[iSegment].nSegmentSize, + pszFilename ); + + if( poWritableJ2KDataset != NULL ) + { + poDS->poJ2KDataset = (GDALPamDataset *) poWritableJ2KDataset; + poDS->bJP2Writing = TRUE; + poWritableJ2KDataset = NULL; + } + else + { + /* We explicitly list the allowed drivers to avoid hostile content */ + /* to be opened by a random driver, and also to make sure that */ + /* a future new JPEG2000 compatible driver derives from GDALPamDataset */ + static const char * const apszDrivers[] = { "JP2KAK", "JP2ECW", "JP2MRSID", + "JPEG2000", "JP2OPENJPEG", NULL }; + poDS->poJ2KDataset = (GDALPamDataset *) + GDALOpenEx( osDSName, GDAL_OF_RASTER, apszDrivers, NULL, NULL); + + if( poDS->poJ2KDataset == NULL ) + { + int bFoundJPEG2000Driver = FALSE; + for(int iDriver=0;apszDrivers[iDriver]!=NULL;iDriver++) + { + if (GDALGetDriverByName(apszDrivers[iDriver]) != NULL) + bFoundJPEG2000Driver = TRUE; + } + + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open JPEG2000 image within NITF file.\n%s\n%s", + (!bFoundJPEG2000Driver) ? + "No JPEG2000 capable driver (JP2KAK, JP2ECW, JP2MRSID, JP2OPENJPEG, etc...) is available." : + "One or several JPEG2000 capable drivers are available but the datastream could not be opened successfully.", + "You can define the NITF_OPEN_UNDERLYING_DS configuration option to NO, in order to just get the metadata."); + delete poDS; + return NULL; + } + + poDS->poJ2KDataset->SetPamFlags( + poDS->poJ2KDataset->GetPamFlags() | GPF_NOSAVE ); + } + + if( poDS->GetRasterXSize() != poDS->poJ2KDataset->GetRasterXSize() + || poDS->GetRasterYSize() != poDS->poJ2KDataset->GetRasterYSize()) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JPEG2000 data stream has not the same dimensions as the NITF file."); + delete poDS; + return NULL; + } + + if ( nUsableBands == 1) + { + const char* pszIREP = CSLFetchNameValue(psImage->papszMetadata, "NITF_IREP"); + if (pszIREP != NULL && EQUAL(pszIREP, "RGB/LUT")) + { + if (poDS->poJ2KDataset->GetRasterCount() == 3) + { +/* Test case : http://www.gwg.nga.mil/ntb/baseline/software/testfile/Jpeg2000/jp2_09/file9_jp2_2places.ntf */ +/* 256-entry palette/LUT in both JP2 Header and image Subheader */ +/* In this case, the JPEG2000 driver will probably do the RGB expension */ + nUsableBands = 3; + bSetColorInterpretation = FALSE; + } + else if (poDS->poJ2KDataset->GetRasterCount() == 1 && + psImage->pasBandInfo[0].nSignificantLUTEntries > 0) + { +/* Test case : http://www.gwg.nga.mil/ntb/baseline/software/testfile/Jpeg2000/jp2_09/file9_j2c.ntf */ +/* 256-entry/LUT in Image Subheader, JP2 header completely removed */ +/* The JPEG2000 driver will decode it as a grey band */ +/* So we must set the color table on the wrapper band */ +/* or for file9_jp2_2places.ntf as well if the J2K driver does do RGB expension */ + bSetColorTable = TRUE; + } + } + } + + if( poDS->poJ2KDataset->GetRasterCount() < nUsableBands ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "JPEG2000 data stream has less useful bands than expected, likely\n" + "because some channels have differing resolutions." ); + + nUsableBands = poDS->poJ2KDataset->GetRasterCount(); + } + } + +/* -------------------------------------------------------------------- */ +/* If the image is JPEG (C3) compressed, we will need to open */ +/* the image data as a JPEG dataset. */ +/* -------------------------------------------------------------------- */ + else if( bOpenUnderlyingDS && psImage != NULL + && EQUAL(psImage->szIC,"C3") + && psImage->nBlocksPerRow == 1 + && psImage->nBlocksPerColumn == 1 ) + { + GUIntBig nJPEGStart = psFile->pasSegmentInfo[iSegment].nSegmentStart; + + poDS->nQLevel = poDS->ScanJPEGQLevel( &nJPEGStart ); + + CPLString osDSName; + + osDSName.Printf( "JPEG_SUBFILE:Q%d," CPL_FRMT_GUIB "," CPL_FRMT_GUIB ",%s", + poDS->nQLevel, nJPEGStart, + psFile->pasSegmentInfo[iSegment].nSegmentSize + - (nJPEGStart - psFile->pasSegmentInfo[iSegment].nSegmentStart), + pszFilename ); + + CPLDebug( "GDAL", + "NITFDataset::Open() as IC=C3 (JPEG compressed)\n"); + + poDS->poJPEGDataset = (GDALPamDataset*) GDALOpen(osDSName,GA_ReadOnly); + if( poDS->poJPEGDataset == NULL ) + { + int bFoundJPEGDriver = GDALGetDriverByName("JPEG") != NULL; + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open JPEG image within NITF file.\n%s\n%s", + (!bFoundJPEGDriver) ? + "The JPEG driver is not available." : + "The JPEG driver is available but the datastream could not be opened successfully.", + "You can define the NITF_OPEN_UNDERLYING_DS configuration option to NO, in order to just get the metadata."); + delete poDS; + return NULL; + } + + /* In some circumstances, the JPEG image can be larger than the NITF */ + /* (NCOLS, NROWS) dimensions (#5001), so accept it as a valid case */ + /* But reject when it is smaller than the NITF dimensions. */ + if( poDS->GetRasterXSize() > poDS->poJPEGDataset->GetRasterXSize() + || poDS->GetRasterYSize() > poDS->poJPEGDataset->GetRasterYSize()) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JPEG data stream has smaller dimensions than the NITF file."); + delete poDS; + return NULL; + } + + poDS->poJPEGDataset->SetPamFlags( + poDS->poJPEGDataset->GetPamFlags() | GPF_NOSAVE ); + + if( poDS->poJPEGDataset->GetRasterCount() < nUsableBands ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "JPEG data stream has less useful bands than expected, likely\n" + "because some channels have differing resolutions." ); + + nUsableBands = poDS->poJPEGDataset->GetRasterCount(); + } + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + + GDALDataset* poBaseDS = NULL; + if (poDS->poJ2KDataset != NULL) + poBaseDS = poDS->poJ2KDataset; + else if (poDS->poJPEGDataset != NULL) + poBaseDS = poDS->poJPEGDataset; + + for( iBand = 0; iBand < nUsableBands; iBand++ ) + { + if( poBaseDS != NULL) + { + GDALRasterBand* poBaseBand = + poBaseDS->GetRasterBand(iBand+1); + +#ifdef ESRI_BUILD + SetBandMetadata( psImage, poBaseBand, iBand+1 ); +#endif + + NITFWrapperRasterBand* poBand = + new NITFWrapperRasterBand(poDS, poBaseBand, iBand+1 ); + + NITFBandInfo *psBandInfo = psImage->pasBandInfo + iBand; + if (bSetColorInterpretation) + { + /* FIXME? Does it make sense if the JPEG/JPEG2000 driver decodes */ + /* YCbCr data as RGB. We probably don't want to set */ + /* the color interpretation as Y, Cb, Cr */ + if( EQUAL(psBandInfo->szIREPBAND,"R") ) + poBand->SetColorInterpretation( GCI_RedBand ); + if( EQUAL(psBandInfo->szIREPBAND,"G") ) + poBand->SetColorInterpretation( GCI_GreenBand ); + if( EQUAL(psBandInfo->szIREPBAND,"B") ) + poBand->SetColorInterpretation( GCI_BlueBand ); + if( EQUAL(psBandInfo->szIREPBAND,"M") ) + poBand->SetColorInterpretation( GCI_GrayIndex ); + if( EQUAL(psBandInfo->szIREPBAND,"Y") ) + poBand->SetColorInterpretation( GCI_YCbCr_YBand ); + if( EQUAL(psBandInfo->szIREPBAND,"Cb") ) + poBand->SetColorInterpretation( GCI_YCbCr_CbBand ); + if( EQUAL(psBandInfo->szIREPBAND,"Cr") ) + poBand->SetColorInterpretation( GCI_YCbCr_CrBand ); + } + if (bSetColorTable) + { + poBand->SetColorTableFromNITFBandInfo(); + poBand->SetColorInterpretation( GCI_PaletteIndex ); + } + + poDS->SetBand( iBand+1, poBand ); + } + else + { + GDALRasterBand* poBand = new NITFRasterBand( poDS, iBand+1 ); + if (poBand->GetRasterDataType() == GDT_Unknown) + { + delete poBand; + delete poDS; + return NULL; + } + +#ifdef ESRI_BUILD + SetBandMetadata( psImage, poBand, iBand+1 ); +#endif + + poDS->SetBand( iBand+1, poBand ); + } + } + +/* -------------------------------------------------------------------- */ +/* Report problems with odd bit sizes. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update && + psImage != NULL + && (psImage->nBitsPerSample % 8 != 0) + && poDS->poJPEGDataset == NULL + && poDS->poJ2KDataset == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Image with %d bits per sample cannot be opened in update mode.", + psImage->nBitsPerSample ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Process the projection from the ICORDS. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRSWork; + + if( psImage == NULL ) + { + /* nothing */ + } + else if( psImage->chICORDS == 'G' || psImage->chICORDS == 'D' ) + { + CPLFree( poDS->pszProjection ); + poDS->pszProjection = NULL; + + oSRSWork.SetWellKnownGeogCS( "WGS84" ); + oSRSWork.exportToWkt( &(poDS->pszProjection) ); + } + else if( psImage->chICORDS == 'C' ) + { + CPLFree( poDS->pszProjection ); + poDS->pszProjection = NULL; + + oSRSWork.SetWellKnownGeogCS( "WGS84" ); + oSRSWork.exportToWkt( &(poDS->pszProjection) ); + + /* convert latitudes from geocentric to geodetic form. */ + + psImage->dfULY = + NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude( + psImage->dfULY ); + psImage->dfLLY = + NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude( + psImage->dfLLY ); + psImage->dfURY = + NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude( + psImage->dfURY ); + psImage->dfLRY = + NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude( + psImage->dfLRY ); + } + else if( psImage->chICORDS == 'S' || psImage->chICORDS == 'N' ) + { + CPLFree( poDS->pszProjection ); + poDS->pszProjection = NULL; + + oSRSWork.SetUTM( psImage->nZone, psImage->chICORDS == 'N' ); + oSRSWork.SetWellKnownGeogCS( "WGS84" ); + oSRSWork.exportToWkt( &(poDS->pszProjection) ); + } + else if( psImage->chICORDS == 'U' && psImage->nZone != 0 ) + { + CPLFree( poDS->pszProjection ); + poDS->pszProjection = NULL; + + oSRSWork.SetUTM( ABS(psImage->nZone), psImage->nZone > 0 ); + oSRSWork.SetWellKnownGeogCS( "WGS84" ); + oSRSWork.exportToWkt( &(poDS->pszProjection) ); + } + + +/* -------------------------------------------------------------------- */ +/* Try looking for a .nfw file. */ +/* -------------------------------------------------------------------- */ + if( psImage + && GDALReadWorldFile2( pszFilename, "nfw", + poDS->adfGeoTransform, poOpenInfo->GetSiblingFiles(), NULL ) ) + { + const char *pszHDR; + VSILFILE *fpHDR; + char **papszLines; + int isNorth; + int zone; + + poDS->bGotGeoTransform = TRUE; + + /* If nfw found, try looking for a header with projection info */ + /* in space imaging style format */ + pszHDR = CPLResetExtension( pszFilename, "hdr" ); + + fpHDR = VSIFOpenL( pszHDR, "rt" ); + + if( fpHDR == NULL && VSIIsCaseSensitiveFS(pszHDR) ) + { + pszHDR = CPLResetExtension( pszFilename, "HDR" ); + fpHDR = VSIFOpenL( pszHDR, "rt" ); + } + + if( fpHDR != NULL ) + { + VSIFCloseL( fpHDR ); + papszLines=CSLLoad2(pszHDR, 16, 200, NULL); + if (CSLCount(papszLines) == 16) + { + + if (psImage->chICORDS == 'N') + isNorth=1; + else if (psImage->chICORDS =='S') + isNorth=0; + else if (psImage->chICORDS == 'G' || psImage->chICORDS == 'D' || psImage->chICORDS == 'C') + { + if (psImage->dfLLY+psImage->dfLRY+psImage->dfULY+psImage->dfURY < 0) + isNorth=0; + else + isNorth=1; + } + else if (psImage->chICORDS == 'U') + { + isNorth = psImage->nZone >= 0; + } + else + { + isNorth = 1; /* arbitrarly suppose we are in northern hemisphere */ + + /* unless we have other information to determine the hemisphere */ + char** papszUSE00A_MD = NITFReadSTDIDC( psImage ); + if( papszUSE00A_MD != NULL ) + { + const char* pszLocation = CSLFetchNameValue(papszUSE00A_MD, "NITF_STDIDC_LOCATION"); + if (pszLocation && strlen(pszLocation) == 11) + { + isNorth = (pszLocation[4] == 'N'); + } + CSLDestroy( papszUSE00A_MD ); + } + else + { + NITFRPC00BInfo sRPCInfo; + if( NITFReadRPC00B( psImage, &sRPCInfo ) && sRPCInfo.SUCCESS ) + { + isNorth = (sRPCInfo.LAT_OFF >= 0); + } + } + } + + if( (EQUALN(papszLines[7], + "Selected Projection: Universal Transverse Mercator",50)) && + (EQUALN(papszLines[8],"Zone: ",6)) && + (strlen(papszLines[8]) >= 7)) + { + CPLFree( poDS->pszProjection ); + poDS->pszProjection = NULL; + zone=atoi(&(papszLines[8][6])); + oSRSWork.Clear(); + oSRSWork.SetUTM( zone, isNorth ); + oSRSWork.SetWellKnownGeogCS( "WGS84" ); + oSRSWork.exportToWkt( &(poDS->pszProjection) ); + } + else + { + /* Couldn't find associated projection info. + Go back to original file for geotransform. + */ + poDS->bGotGeoTransform = FALSE; + } + } + else + poDS->bGotGeoTransform = FALSE; + CSLDestroy(papszLines); + } + else + poDS->bGotGeoTransform = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Does this look like a CADRG polar tile ? (#2940) */ +/* -------------------------------------------------------------------- */ + const char* pszIID1 = (psImage) ? CSLFetchNameValue(psImage->papszMetadata, "NITF_IID1") : NULL; + const char* pszITITLE = (psImage) ? CSLFetchNameValue(psImage->papszMetadata, "NITF_ITITLE") : NULL; + if( psImage != NULL && !poDS->bGotGeoTransform && + (psImage->chICORDS == 'G' || psImage->chICORDS == 'D') && + pszIID1 != NULL && EQUAL(pszIID1, "CADRG") && + pszITITLE != NULL && strlen(pszITITLE) >= 12 + && (pszITITLE[strlen(pszITITLE) - 1] == '9' + || pszITITLE[strlen(pszITITLE) - 1] == 'J') ) + { + /* To get a perfect rectangle in Azimuthal Equidistant projection, we must use */ + /* the sphere and not WGS84 ellipsoid. That's a bit strange... */ + const char* pszNorthPolarProjection = "+proj=aeqd +lat_0=90 +lon_0=0 +x_0=0 +y_0=0 +a=6378137 +b=6378137 +units=m +no_defs"; + const char* pszSouthPolarProjection = "+proj=aeqd +lat_0=-90 +lon_0=0 +x_0=0 +y_0=0 +a=6378137 +b=6378137 +units=m +no_defs"; + + OGRSpatialReference oSRS_AEQD, oSRS_WGS84; + + const char *pszPolarProjection = (psImage->dfULY > 0) ? pszNorthPolarProjection : pszSouthPolarProjection; + oSRS_AEQD.importFromProj4(pszPolarProjection); + + oSRS_WGS84.SetWellKnownGeogCS( "WGS84" ); + + CPLPushErrorHandler( CPLQuietErrorHandler ); + OGRCoordinateTransformationH hCT = + (OGRCoordinateTransformationH)OGRCreateCoordinateTransformation(&oSRS_WGS84, &oSRS_AEQD); + CPLPopErrorHandler(); + if (hCT) + { + double dfULX_AEQD = psImage->dfULX; + double dfULY_AEQD = psImage->dfULY; + double dfURX_AEQD = psImage->dfURX; + double dfURY_AEQD = psImage->dfURY; + double dfLLX_AEQD = psImage->dfLLX; + double dfLLY_AEQD = psImage->dfLLY; + double dfLRX_AEQD = psImage->dfLRX; + double dfLRY_AEQD = psImage->dfLRY; + double z = 0; + int bSuccess = TRUE; + bSuccess &= OCTTransform(hCT, 1, &dfULX_AEQD, &dfULY_AEQD, &z); + bSuccess &= OCTTransform(hCT, 1, &dfURX_AEQD, &dfURY_AEQD, &z); + bSuccess &= OCTTransform(hCT, 1, &dfLLX_AEQD, &dfLLY_AEQD, &z); + bSuccess &= OCTTransform(hCT, 1, &dfLRX_AEQD, &dfLRY_AEQD, &z); + if (bSuccess) + { + /* Check that the coordinates of the 4 corners in Azimuthal Equidistant projection */ + /* are a rectangle */ + if (fabs((dfULX_AEQD - dfLLX_AEQD) / dfLLX_AEQD) < 1e-6 && + fabs((dfURX_AEQD - dfLRX_AEQD) / dfLRX_AEQD) < 1e-6 && + fabs((dfULY_AEQD - dfURY_AEQD) / dfURY_AEQD) < 1e-6 && + fabs((dfLLY_AEQD - dfLRY_AEQD) / dfLRY_AEQD) < 1e-6) + { + CPLFree(poDS->pszProjection); + oSRS_AEQD.exportToWkt( &(poDS->pszProjection) ); + + poDS->bGotGeoTransform = TRUE; + poDS->adfGeoTransform[0] = dfULX_AEQD; + poDS->adfGeoTransform[1] = (dfURX_AEQD - dfULX_AEQD) / poDS->nRasterXSize; + poDS->adfGeoTransform[2] = 0; + poDS->adfGeoTransform[3] = dfULY_AEQD; + poDS->adfGeoTransform[4] = 0; + poDS->adfGeoTransform[5] = (dfLLY_AEQD - dfULY_AEQD) / poDS->nRasterYSize; + } + } + OCTDestroyCoordinateTransformation(hCT); + } + else + { + // if we cannot instantiate the transformer, then we + // will at least attempt to record what we believe the + // natural coordinate system of the image is. This is + // primarily used by ArcGIS (#3337) + + CPLErrorReset(); + + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to instantiate coordinate system transformer, likely PROJ.DLL/libproj.so is not available. Returning image corners as lat/long GCPs as a fallback." ); + + char *pszAEQD = NULL; + oSRS_AEQD.exportToWkt( &(pszAEQD) ); + poDS->SetMetadataItem( "GCPPROJECTIONX", pszAEQD, "IMAGE_STRUCTURE" ); + CPLFree( pszAEQD ); + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have RPCs? */ +/* -------------------------------------------------------------------- */ + int bHasRPC00 = FALSE; + NITFRPC00BInfo sRPCInfo; + memset(&sRPCInfo, 0, sizeof(sRPCInfo)); /* To avoid warnings from not clever compilers */ + + if( psImage && NITFReadRPC00B( psImage, &sRPCInfo ) && sRPCInfo.SUCCESS ) + bHasRPC00 = TRUE; + +/* -------------------------------------------------------------------- */ +/* Do we have IGEOLO data that can be treated as a */ +/* geotransform? Our approach should support images in an */ +/* affine rotated frame of reference. */ +/* -------------------------------------------------------------------- */ + int nGCPCount = 0; + GDAL_GCP *psGCPs = NULL; + + if( psImage && !poDS->bGotGeoTransform && psImage->chICORDS != ' ' ) + { + nGCPCount = 4; + + psGCPs = (GDAL_GCP *) CPLMalloc(sizeof(GDAL_GCP) * nGCPCount); + GDALInitGCPs( nGCPCount, psGCPs ); + + if( psImage->bIsBoxCenterOfPixel ) + { + psGCPs[0].dfGCPPixel = 0.5; + psGCPs[0].dfGCPLine = 0.5; + psGCPs[1].dfGCPPixel = poDS->nRasterXSize-0.5; + psGCPs[1].dfGCPLine = 0.5; + psGCPs[2].dfGCPPixel = poDS->nRasterXSize-0.5; + psGCPs[2].dfGCPLine = poDS->nRasterYSize-0.5; + psGCPs[3].dfGCPPixel = 0.5; + psGCPs[3].dfGCPLine = poDS->nRasterYSize-0.5; + } + else + { + psGCPs[0].dfGCPPixel = 0.0; + psGCPs[0].dfGCPLine = 0.0; + psGCPs[1].dfGCPPixel = poDS->nRasterXSize; + psGCPs[1].dfGCPLine = 0.0; + psGCPs[2].dfGCPPixel = poDS->nRasterXSize; + psGCPs[2].dfGCPLine = poDS->nRasterYSize; + psGCPs[3].dfGCPPixel = 0.0; + psGCPs[3].dfGCPLine = poDS->nRasterYSize; + } + + psGCPs[0].dfGCPX = psImage->dfULX; + psGCPs[0].dfGCPY = psImage->dfULY; + + psGCPs[1].dfGCPX = psImage->dfURX; + psGCPs[1].dfGCPY = psImage->dfURY; + + psGCPs[2].dfGCPX = psImage->dfLRX; + psGCPs[2].dfGCPY = psImage->dfLRY; + + psGCPs[3].dfGCPX = psImage->dfLLX; + psGCPs[3].dfGCPY = psImage->dfLLY; + +/* -------------------------------------------------------------------- */ +/* ESRI desires to use the RPCs to produce a denser and more */ +/* accurate set of GCPs in this case. Details are unclear at */ +/* this time. */ +/* -------------------------------------------------------------------- */ +#ifdef ESRI_BUILD + if( bHasRPC00 + && ( (psImage->chICORDS == 'G') || (psImage->chICORDS == 'C') ) ) + { + if( nGCPCount == 4 ) + NITFDensifyGCPs( &psGCPs, &nGCPCount ); + + NITFUpdateGCPsWithRPC( &sRPCInfo, psGCPs, &nGCPCount ); + } +#endif /* def ESRI_BUILD */ + } + +/* -------------------------------------------------------------------- */ +/* Convert the GCPs into a geotransform definition, if possible. */ +/* -------------------------------------------------------------------- */ + if( !psImage ) + { + /* nothing */ + } + else if( poDS->bGotGeoTransform == FALSE + && nGCPCount > 0 + && GDALGCPsToGeoTransform( nGCPCount, psGCPs, + poDS->adfGeoTransform, FALSE ) ) + { + poDS->bGotGeoTransform = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* If we have IGEOLO that isn't north up, return it as GCPs. */ +/* -------------------------------------------------------------------- */ + else if( (psImage->dfULX != 0 || psImage->dfURX != 0 + || psImage->dfLRX != 0 || psImage->dfLLX != 0) + && psImage->chICORDS != ' ' && + ( poDS->bGotGeoTransform == FALSE ) && + nGCPCount >= 4 ) + { + CPLDebug( "GDAL", + "NITFDataset::Open() wasn't able to derive a first order\n" + "geotransform. It will be returned as GCPs."); + + poDS->nGCPCount = nGCPCount; + poDS->pasGCPList = psGCPs; + + psGCPs = NULL; + nGCPCount = 0; + + CPLFree( poDS->pasGCPList[0].pszId ); + poDS->pasGCPList[0].pszId = CPLStrdup( "UpperLeft" ); + + CPLFree( poDS->pasGCPList[1].pszId ); + poDS->pasGCPList[1].pszId = CPLStrdup( "UpperRight" ); + + CPLFree( poDS->pasGCPList[2].pszId ); + poDS->pasGCPList[2].pszId = CPLStrdup( "LowerRight" ); + + CPLFree( poDS->pasGCPList[3].pszId ); + poDS->pasGCPList[3].pszId = CPLStrdup( "LowerLeft" ); + + poDS->pszGCPProjection = CPLStrdup( poDS->pszProjection ); + } + + // This cleans up the original copy of the GCPs used to test if + // this IGEOLO could be used for a geotransform if we did not + // steal the to use as primary gcps. + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, psGCPs ); + CPLFree( psGCPs ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have PRJPSB and MAPLOB TREs to get better */ +/* georeferencing from? */ +/* -------------------------------------------------------------------- */ + if (psImage) + poDS->CheckGeoSDEInfo(); + +/* -------------------------------------------------------------------- */ +/* Do we have metadata. */ +/* -------------------------------------------------------------------- */ + char **papszMergedMD; + char **papszTRE_MD; + + // File and Image level metadata. + papszMergedMD = CSLDuplicate( poDS->psFile->papszMetadata ); + + if( psImage ) + { + papszMergedMD = CSLInsertStrings( papszMergedMD, + CSLCount( papszMergedMD ), + psImage->papszMetadata ); + + // Comments. + if( psImage->pszComments != NULL && strlen(psImage->pszComments) != 0 ) + papszMergedMD = CSLSetNameValue( + papszMergedMD, "NITF_IMAGE_COMMENTS", psImage->pszComments ); + + // Compression code. + papszMergedMD = CSLSetNameValue( papszMergedMD, "NITF_IC", + psImage->szIC ); + + // IMODE + char szIMODE[2]; + szIMODE[0] = psImage->chIMODE; + szIMODE[1] = '\0'; + papszMergedMD = CSLSetNameValue( papszMergedMD, "NITF_IMODE", szIMODE ); + + // ILOC/Attachment info + if( psImage->nIDLVL != 0 ) + { + NITFSegmentInfo *psSegInfo + = psFile->pasSegmentInfo + psImage->iSegment; + + papszMergedMD = + CSLSetNameValue( papszMergedMD, "NITF_IDLVL", + CPLString().Printf("%d",psImage->nIDLVL) ); + papszMergedMD = + CSLSetNameValue( papszMergedMD, "NITF_IALVL", + CPLString().Printf("%d",psImage->nIALVL) ); + papszMergedMD = + CSLSetNameValue( papszMergedMD, "NITF_ILOC_ROW", + CPLString().Printf("%d",psImage->nILOCRow) ); + papszMergedMD = + CSLSetNameValue( papszMergedMD, "NITF_ILOC_COLUMN", + CPLString().Printf("%d",psImage->nILOCColumn)); + papszMergedMD = + CSLSetNameValue( papszMergedMD, "NITF_CCS_ROW", + CPLString().Printf("%d",psSegInfo->nCCS_R) ); + papszMergedMD = + CSLSetNameValue( papszMergedMD, "NITF_CCS_COLUMN", + CPLString().Printf("%d", psSegInfo->nCCS_C)); + papszMergedMD = + CSLSetNameValue( papszMergedMD, "NITF_IMAG", + psImage->szIMAG ); + } + + papszMergedMD = NITFGenericMetadataRead(papszMergedMD, psFile, psImage, NULL); + + // BLOCKA + papszTRE_MD = NITFReadBLOCKA( psImage ); + if( papszTRE_MD != NULL ) + { + papszMergedMD = CSLInsertStrings( papszMergedMD, + CSLCount( papszTRE_MD ), + papszTRE_MD ); + CSLDestroy( papszTRE_MD ); + } + } + +#ifdef ESRI_BUILD + // Extract ESRI generic metadata. + char **papszESRI_MD = ExtractEsriMD( papszMergedMD ); + if( papszESRI_MD != NULL ) + { + papszMergedMD = CSLInsertStrings( papszMergedMD, + CSLCount( papszESRI_MD ), + papszESRI_MD ); + CSLDestroy( papszESRI_MD ); + } +#endif + + poDS->SetMetadata( papszMergedMD ); + CSLDestroy( papszMergedMD ); + +/* -------------------------------------------------------------------- */ +/* Image structure metadata. */ +/* -------------------------------------------------------------------- */ + if( psImage == NULL ) + /* do nothing */; + else if( psImage->szIC[1] == '1' ) + poDS->SetMetadataItem( "COMPRESSION", "BILEVEL", + "IMAGE_STRUCTURE" ); + else if( psImage->szIC[1] == '2' ) + poDS->SetMetadataItem( "COMPRESSION", "ARIDPCM", + "IMAGE_STRUCTURE" ); + else if( psImage->szIC[1] == '3' ) + poDS->SetMetadataItem( "COMPRESSION", "JPEG", + "IMAGE_STRUCTURE" ); + else if( psImage->szIC[1] == '4' ) + poDS->SetMetadataItem( "COMPRESSION", "VECTOR QUANTIZATION", + "IMAGE_STRUCTURE" ); + else if( psImage->szIC[1] == '5' ) + poDS->SetMetadataItem( "COMPRESSION", "LOSSLESS JPEG", + "IMAGE_STRUCTURE" ); + else if( psImage->szIC[1] == '8' ) + poDS->SetMetadataItem( "COMPRESSION", "JPEG2000", + "IMAGE_STRUCTURE" ); + +/* -------------------------------------------------------------------- */ +/* Do we have RPC info. */ +/* -------------------------------------------------------------------- */ + if( psImage && bHasRPC00 ) + { + char szValue[1280]; + int i; + + CPLsprintf( szValue, "%.16g", sRPCInfo.LINE_OFF ); + poDS->SetMetadataItem( "LINE_OFF", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", sRPCInfo.LINE_SCALE ); + poDS->SetMetadataItem( "LINE_SCALE", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", sRPCInfo.SAMP_OFF ); + poDS->SetMetadataItem( "SAMP_OFF", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", sRPCInfo.SAMP_SCALE ); + poDS->SetMetadataItem( "SAMP_SCALE", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", sRPCInfo.LONG_OFF ); + poDS->SetMetadataItem( "LONG_OFF", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", sRPCInfo.LONG_SCALE ); + poDS->SetMetadataItem( "LONG_SCALE", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", sRPCInfo.LAT_OFF ); + poDS->SetMetadataItem( "LAT_OFF", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", sRPCInfo.LAT_SCALE ); + poDS->SetMetadataItem( "LAT_SCALE", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", sRPCInfo.HEIGHT_OFF ); + poDS->SetMetadataItem( "HEIGHT_OFF", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", sRPCInfo.HEIGHT_SCALE ); + poDS->SetMetadataItem( "HEIGHT_SCALE", szValue, "RPC" ); + + szValue[0] = '\0'; + for( i = 0; i < 20; i++ ) + CPLsprintf( szValue+strlen(szValue), "%.16g ", + sRPCInfo.LINE_NUM_COEFF[i] ); + poDS->SetMetadataItem( "LINE_NUM_COEFF", szValue, "RPC" ); + + szValue[0] = '\0'; + for( i = 0; i < 20; i++ ) + CPLsprintf( szValue+strlen(szValue), "%.16g ", + sRPCInfo.LINE_DEN_COEFF[i] ); + poDS->SetMetadataItem( "LINE_DEN_COEFF", szValue, "RPC" ); + + szValue[0] = '\0'; + for( i = 0; i < 20; i++ ) + CPLsprintf( szValue+strlen(szValue), "%.16g ", + sRPCInfo.SAMP_NUM_COEFF[i] ); + poDS->SetMetadataItem( "SAMP_NUM_COEFF", szValue, "RPC" ); + + szValue[0] = '\0'; + for( i = 0; i < 20; i++ ) + CPLsprintf( szValue+strlen(szValue), "%.16g ", + sRPCInfo.SAMP_DEN_COEFF[i] ); + poDS->SetMetadataItem( "SAMP_DEN_COEFF", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", + sRPCInfo.LONG_OFF - ( sRPCInfo.LONG_SCALE / 2.0 ) ); + poDS->SetMetadataItem( "MIN_LONG", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", + sRPCInfo.LONG_OFF + ( sRPCInfo.LONG_SCALE / 2.0 ) ); + poDS->SetMetadataItem( "MAX_LONG", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", + sRPCInfo.LAT_OFF - ( sRPCInfo.LAT_SCALE / 2.0 ) ); + poDS->SetMetadataItem( "MIN_LAT", szValue, "RPC" ); + + CPLsprintf( szValue, "%.16g", + sRPCInfo.LAT_OFF + ( sRPCInfo.LAT_SCALE / 2.0 ) ); + poDS->SetMetadataItem( "MAX_LAT", szValue, "RPC" ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have Chip info? */ +/* -------------------------------------------------------------------- */ + NITFICHIPBInfo sChipInfo; + + if( psImage + && NITFReadICHIPB( psImage, &sChipInfo ) && sChipInfo.XFRM_FLAG == 0 ) + { + char szValue[1280]; + + CPLsprintf( szValue, "%.16g", sChipInfo.SCALE_FACTOR ); + poDS->SetMetadataItem( "ICHIP_SCALE_FACTOR", szValue ); + + sprintf( szValue, "%d", sChipInfo.ANAMORPH_CORR ); + poDS->SetMetadataItem( "ICHIP_ANAMORPH_CORR", szValue ); + + sprintf( szValue, "%d", sChipInfo.SCANBLK_NUM ); + poDS->SetMetadataItem( "ICHIP_SCANBLK_NUM", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.OP_ROW_11 ); + poDS->SetMetadataItem( "ICHIP_OP_ROW_11", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.OP_COL_11 ); + poDS->SetMetadataItem( "ICHIP_OP_COL_11", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.OP_ROW_12 ); + poDS->SetMetadataItem( "ICHIP_OP_ROW_12", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.OP_COL_12 ); + poDS->SetMetadataItem( "ICHIP_OP_COL_12", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.OP_ROW_21 ); + poDS->SetMetadataItem( "ICHIP_OP_ROW_21", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.OP_COL_21 ); + poDS->SetMetadataItem( "ICHIP_OP_COL_21", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.OP_ROW_22 ); + poDS->SetMetadataItem( "ICHIP_OP_ROW_22", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.OP_COL_22 ); + poDS->SetMetadataItem( "ICHIP_OP_COL_22", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.FI_ROW_11 ); + poDS->SetMetadataItem( "ICHIP_FI_ROW_11", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.FI_COL_11 ); + poDS->SetMetadataItem( "ICHIP_FI_COL_11", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.FI_ROW_12 ); + poDS->SetMetadataItem( "ICHIP_FI_ROW_12", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.FI_COL_12 ); + poDS->SetMetadataItem( "ICHIP_FI_COL_12", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.FI_ROW_21 ); + poDS->SetMetadataItem( "ICHIP_FI_ROW_21", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.FI_COL_21 ); + poDS->SetMetadataItem( "ICHIP_FI_COL_21", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.FI_ROW_22 ); + poDS->SetMetadataItem( "ICHIP_FI_ROW_22", szValue ); + + CPLsprintf( szValue, "%.16g", sChipInfo.FI_COL_22 ); + poDS->SetMetadataItem( "ICHIP_FI_COL_22", szValue ); + + sprintf( szValue, "%d", sChipInfo.FI_ROW ); + poDS->SetMetadataItem( "ICHIP_FI_ROW", szValue ); + + sprintf( szValue, "%d", sChipInfo.FI_COL ); + poDS->SetMetadataItem( "ICHIP_FI_COL", szValue ); + + } + + const NITFSeries* series = NITFGetSeriesInfo(pszFilename); + if (series) + { + poDS->SetMetadataItem("NITF_SERIES_ABBREVIATION", + (series->abbreviation) ? series->abbreviation : "Unknown"); + poDS->SetMetadataItem("NITF_SERIES_NAME", + (series->name) ? series->name : "Unknown"); + } + +/* -------------------------------------------------------------------- */ +/* If there are multiple image segments, and we are the zeroth, */ +/* then setup the subdataset metadata. */ +/* -------------------------------------------------------------------- */ + int nSubDSCount = 0; + + if( nIMIndex == -1 ) + { + char **papszSubdatasets = NULL; + int nIMCounter = 0; + + for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ ) + { + if( EQUAL(psFile->pasSegmentInfo[iSegment].szSegmentType,"IM") ) + { + CPLString oName; + CPLString oValue; + + oName.Printf( "SUBDATASET_%d_NAME", nIMCounter+1 ); + oValue.Printf( "NITF_IM:%d:%s", nIMCounter, pszFilename ); + papszSubdatasets = CSLSetNameValue( papszSubdatasets, + oName, oValue ); + + oName.Printf( "SUBDATASET_%d_DESC", nIMCounter+1 ); + oValue.Printf( "Image %d of %s", nIMCounter+1, pszFilename ); + papszSubdatasets = CSLSetNameValue( papszSubdatasets, + oName, oValue ); + + nIMCounter++; + } + } + + nSubDSCount = CSLCount(papszSubdatasets) / 2; + if( nSubDSCount > 1 ) + poDS->GDALMajorObject::SetMetadata( papszSubdatasets, + "SUBDATASETS" ); + + CSLDestroy( papszSubdatasets ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + + if( nSubDSCount > 1 || nIMIndex != -1 ) + { + if( nIMIndex == -1 ) + nIMIndex = 0; + + poDS->SetSubdatasetName( CPLString().Printf("%d",nIMIndex) ); + poDS->SetPhysicalFilename( pszFilename ); + } + + poDS->bInLoadXML = TRUE; + poDS->TryLoadXML(poOpenInfo->GetSiblingFiles()); + poDS->bInLoadXML = FALSE; + +/* -------------------------------------------------------------------- */ +/* Do we have a special overview file? If not, do we have */ +/* RSets that should be treated as an overview file? */ +/* -------------------------------------------------------------------- */ + const char *pszOverviewFile = + poDS->GetMetadataItem( "OVERVIEW_FILE", "OVERVIEWS" ); + + if( pszOverviewFile == NULL ) + { + if( poDS->CheckForRSets(pszFilename, poOpenInfo->GetSiblingFiles()) ) + pszOverviewFile = poDS->osRSetVRT; + } + +/* -------------------------------------------------------------------- */ +/* If we have jpeg or jpeg2000 bands we may need to set the */ +/* overview file on their dataset. (#3276) */ +/* -------------------------------------------------------------------- */ + GDALDataset *poSubDS = poDS->poJ2KDataset; + if( poDS->poJPEGDataset ) + poSubDS = poDS->poJPEGDataset; + + if( poSubDS && pszOverviewFile != NULL ) + { + poSubDS->SetMetadataItem( "OVERVIEW_FILE", + pszOverviewFile, + "OVERVIEWS" ); + } + +/* -------------------------------------------------------------------- */ +/* If we have jpeg, or jpeg2000 bands we may need to clear */ +/* their PAM dirty flag too. */ +/* -------------------------------------------------------------------- */ + if( poDS->poJ2KDataset != NULL ) + poDS->poJ2KDataset->SetPamFlags( + poDS->poJ2KDataset->GetPamFlags() & ~GPF_DIRTY ); + if( poDS->poJPEGDataset != NULL ) + poDS->poJPEGDataset->SetPamFlags( + poDS->poJPEGDataset->GetPamFlags() & ~GPF_DIRTY ); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + if( !EQUAL(poOpenInfo->pszFilename,pszFilename) ) + poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" ); + else + poDS->oOvManager.Initialize( poDS, pszFilename, poOpenInfo->GetSiblingFiles() ); + + /* If there are PAM overviews, don't expose the underlying JPEG dataset */ + /* overviews (in case of monoblock C3) */ + if( poDS->GetRasterCount() > 0 && poDS->GetRasterBand(1) != NULL ) + poDS->bExposeUnderlyingJPEGDatasetOverviews = + ((GDALPamRasterBand*)poDS->GetRasterBand(1))-> + GDALPamRasterBand::GetOverviewCount() == 0; + + return( poDS ); +} + +/************************************************************************/ +/* LoadDODDatum() */ +/* */ +/* Try to turn a US military datum name into a datum definition. */ +/************************************************************************/ + +static OGRErr LoadDODDatum( OGRSpatialReference *poSRS, + const char *pszDatumName ) + +{ +/* -------------------------------------------------------------------- */ +/* The most common case... */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszDatumName,"WGE ",4) ) + { + poSRS->SetWellKnownGeogCS( "WGS84" ); + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* All the rest we will try and load from gt_datum.csv */ +/* (Geotrans datum file). */ +/* -------------------------------------------------------------------- */ + char szExpanded[6]; + const char *pszGTDatum = CSVFilename( "gt_datum.csv" ); + + strncpy( szExpanded, pszDatumName, 3 ); + szExpanded[3] = '\0'; + if( pszDatumName[3] != ' ' ) + { + int nLen; + strcat( szExpanded, "-" ); + nLen = strlen(szExpanded); + szExpanded[nLen] = pszDatumName[3]; + szExpanded[nLen + 1] = '\0'; + } + + CPLString osDName = CSVGetField( pszGTDatum, "CODE", szExpanded, + CC_ApproxString, "NAME" ); + if( strlen(osDName) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find datum %s/%s in gt_datum.csv.", + pszDatumName, szExpanded ); + return OGRERR_FAILURE; + } + + CPLString osEllipseCode = CSVGetField( pszGTDatum, "CODE", szExpanded, + CC_ApproxString, "ELLIPSOID" ); + double dfDeltaX = CPLAtof(CSVGetField( pszGTDatum, "CODE", szExpanded, + CC_ApproxString, "DELTAX" ) ); + double dfDeltaY = CPLAtof(CSVGetField( pszGTDatum, "CODE", szExpanded, + CC_ApproxString, "DELTAY" ) ); + double dfDeltaZ = CPLAtof(CSVGetField( pszGTDatum, "CODE", szExpanded, + CC_ApproxString, "DELTAZ" ) ); + +/* -------------------------------------------------------------------- */ +/* Lookup the ellipse code. */ +/* -------------------------------------------------------------------- */ + const char *pszGTEllipse = CSVFilename( "gt_ellips.csv" ); + + CPLString osEName = CSVGetField( pszGTEllipse, "CODE", osEllipseCode, + CC_ApproxString, "NAME" ); + if( strlen(osEName) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find datum %s in gt_ellips.csv.", + osEllipseCode.c_str() ); + return OGRERR_FAILURE; + } + + double dfA = CPLAtof(CSVGetField( pszGTEllipse, "CODE", osEllipseCode, + CC_ApproxString, "A" )); + double dfInvF = CPLAtof(CSVGetField( pszGTEllipse, "CODE", osEllipseCode, + CC_ApproxString, "RF" )); + +/* -------------------------------------------------------------------- */ +/* Create geographic coordinate system. */ +/* -------------------------------------------------------------------- */ + poSRS->SetGeogCS( osDName, osDName, osEName, dfA, dfInvF ); + + poSRS->SetTOWGS84( dfDeltaX, dfDeltaY, dfDeltaZ ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CheckGeoSDEInfo() */ +/* */ +/* Check for GeoSDE TREs (GEOPSB/PRJPSB and MAPLOB). If we */ +/* have them, use them to override our coordinate system and */ +/* geotransform info. */ +/************************************************************************/ + +void NITFDataset::CheckGeoSDEInfo() + +{ + if( !psImage ) + return; + +/* -------------------------------------------------------------------- */ +/* Do we have the required TREs? */ +/* -------------------------------------------------------------------- */ + const char *pszGEOPSB , *pszPRJPSB, *pszMAPLOB; + OGRSpatialReference oSRS; + char szName[81]; + int nGEOPSBSize, nPRJPSBSize, nMAPLOBSize; + + pszGEOPSB = NITFFindTRE( psFile->pachTRE, psFile->nTREBytes,"GEOPSB",&nGEOPSBSize); + pszPRJPSB = NITFFindTRE( psFile->pachTRE, psFile->nTREBytes,"PRJPSB",&nPRJPSBSize); + pszMAPLOB = NITFFindTRE(psImage->pachTRE,psImage->nTREBytes,"MAPLOB",&nMAPLOBSize); + + if( pszGEOPSB == NULL || pszPRJPSB == NULL || pszMAPLOB == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Collect projection parameters. */ +/* -------------------------------------------------------------------- */ + + char szParm[16]; + if (nPRJPSBSize < 82 + 1) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read PRJPSB TRE. Not enough bytes"); + return; + } + int nParmCount = atoi(NITFGetField(szParm,pszPRJPSB,82,1)); + int i; + double adfParm[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + double dfFN; + double dfFE; + if (nPRJPSBSize < 83+15*nParmCount+15+15) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read PRJPSB TRE. Not enough bytes"); + return; + } + for( i = 0; i < nParmCount; i++ ) + adfParm[i] = CPLAtof(NITFGetField(szParm,pszPRJPSB,83+15*i,15)); + + dfFE = CPLAtof(NITFGetField(szParm,pszPRJPSB,83+15*nParmCount,15)); + dfFN = CPLAtof(NITFGetField(szParm,pszPRJPSB,83+15*nParmCount+15,15)); + +/* -------------------------------------------------------------------- */ +/* Try to handle the projection. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszPRJPSB+80,"AC",2) ) + oSRS.SetACEA( adfParm[1], adfParm[2], adfParm[3], adfParm[0], + dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"AK",2) ) + oSRS.SetLAEA( adfParm[1], adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"AL",2) ) + oSRS.SetAE( adfParm[1], adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"BF",2) ) + oSRS.SetBonne( adfParm[1], adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"CP",2) ) + oSRS.SetEquirectangular( adfParm[1], adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"CS",2) ) + oSRS.SetCS( adfParm[1], adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"EF",2) ) + oSRS.SetEckertIV( adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"ED",2) ) + oSRS.SetEckertVI( adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"GN",2) ) + oSRS.SetGnomonic( adfParm[1], adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"HX",2) ) + oSRS.SetHOM2PNO( adfParm[1], + adfParm[3], adfParm[2], + adfParm[5], adfParm[4], + adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"KA",2) ) + oSRS.SetEC( adfParm[1], adfParm[2], adfParm[3], adfParm[0], + dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"LE",2) ) + oSRS.SetLCC( adfParm[1], adfParm[2], adfParm[3], adfParm[0], + dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"LI",2) ) + oSRS.SetCEA( adfParm[1], adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"MC",2) ) + oSRS.SetMercator( adfParm[2], adfParm[1], 1.0, dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"MH",2) ) + oSRS.SetMC( 0.0, adfParm[1], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"MP",2) ) + oSRS.SetMollweide( adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"NT",2) ) + oSRS.SetNZMG( adfParm[1], adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"OD",2) ) + oSRS.SetOrthographic( adfParm[1], adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"PC",2) ) + oSRS.SetPolyconic( adfParm[1], adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"PG",2) ) + oSRS.SetPS( adfParm[1], adfParm[0], 1.0, dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"RX",2) ) + oSRS.SetRobinson( adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"SA",2) ) + oSRS.SetSinusoidal( adfParm[0], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"TC",2) ) + oSRS.SetTM( adfParm[2], adfParm[0], adfParm[1], dfFE, dfFN ); + + else if( EQUALN(pszPRJPSB+80,"VA",2) ) + oSRS.SetVDG( adfParm[0], dfFE, dfFN ); + + else + oSRS.SetLocalCS( NITFGetField(szName,pszPRJPSB,0,80) ); + +/* -------------------------------------------------------------------- */ +/* Try to apply the datum. */ +/* -------------------------------------------------------------------- */ + if (nGEOPSBSize < 86 + 4) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read GEOPSB TRE. Not enough bytes"); + return; + } + LoadDODDatum( &oSRS, NITFGetField(szParm,pszGEOPSB,86,4) ); + +/* -------------------------------------------------------------------- */ +/* Get the geotransform */ +/* -------------------------------------------------------------------- */ + double adfGT[6]; + double dfMeterPerUnit = 1.0; + + if (nMAPLOBSize < 28 + 15) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read MAPLOB TRE. Not enough bytes"); + return; + } + + if( EQUALN(pszMAPLOB+0,"DM ",3) ) + dfMeterPerUnit = 0.1; + else if( EQUALN(pszMAPLOB+0,"CM ",3) ) + dfMeterPerUnit = 0.01; + else if( EQUALN(pszMAPLOB+0,"MM ",3) ) + dfMeterPerUnit = 0.001; + else if( EQUALN(pszMAPLOB+0,"UM ",3) ) + dfMeterPerUnit = 0.000001; + else if( EQUALN(pszMAPLOB+0,"KM ",3) ) + dfMeterPerUnit = 1000.0; + else if( EQUALN(pszMAPLOB+0,"M ",3) ) + dfMeterPerUnit = 1.0; + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "MAPLOB Unit=%3.3s not regonised, geolocation may be wrong.", + pszMAPLOB+0 ); + } + + adfGT[0] = CPLAtof(NITFGetField(szParm,pszMAPLOB,13,15)); + adfGT[1] = CPLAtof(NITFGetField(szParm,pszMAPLOB,3,5)) * dfMeterPerUnit; + adfGT[2] = 0.0; + adfGT[3] = CPLAtof(NITFGetField(szParm,pszMAPLOB,28,15)); + adfGT[4] = 0.0; + adfGT[5] = -CPLAtof(NITFGetField(szParm,pszMAPLOB,8,5)) * dfMeterPerUnit; + +/* -------------------------------------------------------------------- */ +/* Apply back to dataset. */ +/* -------------------------------------------------------------------- */ + CPLFree( pszProjection ); + pszProjection = NULL; + + oSRS.exportToWkt( &pszProjection ); + + memcpy( adfGeoTransform, adfGT, sizeof(double)*6 ); + bGotGeoTransform = TRUE; +} + +/************************************************************************/ +/* AdviseRead() */ +/************************************************************************/ + +CPLErr NITFDataset::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + char **papszOptions ) + +{ + if( poJ2KDataset == NULL ) + return GDALDataset::AdviseRead( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eDT, + nBandCount, panBandList, + papszOptions); + else if( poJPEGDataset != NULL ) + return poJPEGDataset->AdviseRead( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eDT, + nBandCount, panBandList, + papszOptions); + else + return poJ2KDataset->AdviseRead( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eDT, + nBandCount, panBandList, + papszOptions); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr NITFDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ + if( poJ2KDataset != NULL ) + return poJ2KDataset->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); + else if( poJPEGDataset != NULL ) + return poJPEGDataset->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); + else + return GDALDataset::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); +} + + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr NITFDataset::GetGeoTransform( double *padfGeoTransform ) + +{ + memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 ); + + if( bGotGeoTransform ) + return CE_None; + else + return GDALPamDataset::GetGeoTransform( padfGeoTransform ); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr NITFDataset::SetGeoTransform( double *padfGeoTransform ) + +{ + double dfIGEOLOULX, dfIGEOLOULY, dfIGEOLOURX, dfIGEOLOURY, + dfIGEOLOLRX, dfIGEOLOLRY, dfIGEOLOLLX, dfIGEOLOLLY; + + bGotGeoTransform = TRUE; + /* Valgrind would complain because SetGeoTransform() is called */ + /* from SetProjection() with adfGeoTransform as argument */ + if (adfGeoTransform != padfGeoTransform) + memcpy( adfGeoTransform, padfGeoTransform, sizeof(double) * 6 ); + + dfIGEOLOULX = padfGeoTransform[0] + 0.5 * padfGeoTransform[1] + + 0.5 * padfGeoTransform[2]; + dfIGEOLOULY = padfGeoTransform[3] + 0.5 * padfGeoTransform[4] + + 0.5 * padfGeoTransform[5]; + dfIGEOLOURX = dfIGEOLOULX + padfGeoTransform[1] * (nRasterXSize - 1); + dfIGEOLOURY = dfIGEOLOULY + padfGeoTransform[4] * (nRasterXSize - 1); + dfIGEOLOLRX = dfIGEOLOULX + padfGeoTransform[1] * (nRasterXSize - 1) + + padfGeoTransform[2] * (nRasterYSize - 1); + dfIGEOLOLRY = dfIGEOLOULY + padfGeoTransform[4] * (nRasterXSize - 1) + + padfGeoTransform[5] * (nRasterYSize - 1); + dfIGEOLOLLX = dfIGEOLOULX + padfGeoTransform[2] * (nRasterYSize - 1); + dfIGEOLOLLY = dfIGEOLOULY + padfGeoTransform[5] * (nRasterYSize - 1); + + if( NITFWriteIGEOLO( psImage, psImage->chICORDS, + psImage->nZone, + dfIGEOLOULX, dfIGEOLOULY, dfIGEOLOURX, dfIGEOLOURY, + dfIGEOLOLRX, dfIGEOLOLRY, dfIGEOLOLLX, dfIGEOLOLLY ) ) + return CE_None; + else + return GDALPamDataset::SetGeoTransform( padfGeoTransform ); +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +CPLErr NITFDataset::SetGCPs( int nGCPCountIn, const GDAL_GCP *pasGCPListIn, + const char *pszGCPProjectionIn ) +{ + if( nGCPCountIn != 4 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "NITF only supports writing 4 GCPs."); + return CE_Failure; + } + + /* Free previous GCPs */ + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + + /* Duplicate in GCPs */ + nGCPCount = nGCPCountIn; + pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn); + + CPLFree(pszGCPProjection); + pszGCPProjection = CPLStrdup(pszGCPProjectionIn); + + int iUL = -1, iUR = -1, iLR = -1, iLL = -1; + +#define EPS_GCP 1e-5 + for(int i = 0; i < 4; i++ ) + { + if (fabs(pasGCPList[i].dfGCPPixel - 0.5) < EPS_GCP && + fabs(pasGCPList[i].dfGCPLine - 0.5) < EPS_GCP) + iUL = i; + + else if (fabs(pasGCPList[i].dfGCPPixel - (nRasterXSize - 0.5)) < EPS_GCP && + fabs(pasGCPList[i].dfGCPLine - 0.5) < EPS_GCP) + iUR = i; + + else if (fabs(pasGCPList[i].dfGCPPixel - (nRasterXSize - 0.5)) < EPS_GCP && + fabs(pasGCPList[i].dfGCPLine - (nRasterYSize - 0.5)) < EPS_GCP ) + iLR = i; + + else if (fabs(pasGCPList[i].dfGCPPixel - 0.5) < EPS_GCP && + fabs(pasGCPList[i].dfGCPLine - (nRasterYSize - 0.5)) < EPS_GCP) + iLL = i; + } + + if (iUL < 0 || iUR < 0 || iLR < 0 || iLL < 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "The 4 GCPs image coordinates must be exactly " + "at the *center* of the 4 corners of the image " + "( (%.1f, %.1f), (%.1f %.1f), (%.1f %.1f), (%.1f %.1f) ).", + 0.5, 0.5, + nRasterYSize - 0.5, 0.5, + nRasterXSize - 0.5, nRasterYSize - 0.5, + nRasterXSize - 0.5, 0.5); + return CE_Failure; + } + + double dfIGEOLOULX = pasGCPList[iUL].dfGCPX; + double dfIGEOLOULY = pasGCPList[iUL].dfGCPY; + double dfIGEOLOURX = pasGCPList[iUR].dfGCPX; + double dfIGEOLOURY = pasGCPList[iUR].dfGCPY; + double dfIGEOLOLRX = pasGCPList[iLR].dfGCPX; + double dfIGEOLOLRY = pasGCPList[iLR].dfGCPY; + double dfIGEOLOLLX = pasGCPList[iLL].dfGCPX; + double dfIGEOLOLLY = pasGCPList[iLL].dfGCPY; + + /* To recompute the zone */ + char* pszProjectionBack = pszProjection ? CPLStrdup(pszProjection) : NULL; + CPLErr eErr = SetProjection(pszGCPProjection); + CPLFree(pszProjection); + pszProjection = pszProjectionBack; + + if (eErr != CE_None) + return eErr; + + if( NITFWriteIGEOLO( psImage, psImage->chICORDS, + psImage->nZone, + dfIGEOLOULX, dfIGEOLOULY, dfIGEOLOURX, dfIGEOLOURY, + dfIGEOLOLRX, dfIGEOLOLRY, dfIGEOLOLLX, dfIGEOLOLLY ) ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *NITFDataset::GetProjectionRef() + +{ + if( bGotGeoTransform ) + return pszProjection; + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr NITFDataset::SetProjection(const char* _pszProjection) + +{ + int bNorth; + OGRSpatialReference oSRS, oSRS_WGS84; + char *pszWKT = (char *) _pszProjection; + + if( pszWKT != NULL ) + oSRS.importFromWkt( &pszWKT ); + else + return CE_Failure; + + oSRS_WGS84.SetWellKnownGeogCS( "WGS84" ); + if ( oSRS.IsSameGeogCS(&oSRS_WGS84) == FALSE) + { + CPLError(CE_Failure, CPLE_NotSupported, + "NITF only supports WGS84 geographic and UTM projections.\n"); + return CE_Failure; + } + + if( oSRS.IsGeographic() && oSRS.GetPrimeMeridian() == 0.0) + { + if (psImage->chICORDS != 'G' && psImage->chICORDS != 'D') + { + CPLError(CE_Failure, CPLE_NotSupported, + "NITF file should have been created with creation option 'ICORDS=G' (or 'ICORDS=D').\n"); + return CE_Failure; + } + } + else if( oSRS.GetUTMZone( &bNorth ) > 0) + { + if (bNorth && psImage->chICORDS != 'N') + { + CPLError(CE_Failure, CPLE_NotSupported, + "NITF file should have been created with creation option 'ICORDS=N'.\n"); + return CE_Failure; + } + else if (!bNorth && psImage->chICORDS != 'S') + { + CPLError(CE_Failure, CPLE_NotSupported, + "NITF file should have been created with creation option 'ICORDS=S'.\n"); + return CE_Failure; + } + + psImage->nZone = oSRS.GetUTMZone( NULL ); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "NITF only supports WGS84 geographic and UTM projections.\n"); + return CE_Failure; + } + + CPLFree(pszProjection); + pszProjection = CPLStrdup(_pszProjection); + + if (bGotGeoTransform) + SetGeoTransform(adfGeoTransform); + + return CE_None; +} + +#ifdef ESRI_BUILD +/************************************************************************/ +/* InitializeNITFDESMetadata() */ +/************************************************************************/ + +void NITFDataset::InitializeNITFDESMetadata() +{ + static const char *pszDESMetadataDomain = "NITF_DES_METADATA"; + static const char *pszDESsDomain = "NITF_DES"; + static const char *pszMDXmlDataContentDESDATA = "NITF_DES_XML_DATA_CONTENT_DESDATA"; + static const char *pszXmlDataContent = "XML_DATA_CONTENT"; + static const int idxXmlDataContentDESDATA = 973; + static const int sizeXmlDataContent = (int)strlen(pszXmlDataContent); + + char **ppszDESMetadataList = oSpecialMD.GetMetadata( pszDESMetadataDomain ); + + if( ppszDESMetadataList != NULL ) return; + + char **ppszDESsList = this->GetMetadata( pszDESsDomain ); + + if( ppszDESsList == NULL ) return; + + bool foundXmlDataContent = false; + char *pachNITFDES = NULL; + + // Set metadata "NITF_DES_XML_DATA_CONTENT_DESDATA". + // NOTE: There should only be one instance of XML_DATA_CONTENT DES. + + while( ((pachNITFDES = *ppszDESsList) != NULL) && (!foundXmlDataContent) ) + { + // The data stream has been Base64 encoded, need to decode it. + // NOTE: The actual length of the DES data stream is appended at the beginning of the encoded + // data and is separated by a space. + + const char* pszSpace = strchr(pachNITFDES, ' '); + + char* pszData = NULL; + int nDataLen = 0; + if( pszSpace ) + { + pszData = CPLStrdup( pszSpace+1 ); + nDataLen = CPLBase64DecodeInPlace((GByte*)pszData); + pszData[nDataLen] = 0; + } + + if ( nDataLen > 2 + sizeXmlDataContent && EQUALN(pszData, "DE", 2) ) + { + // Check to see if this is a XML_DATA_CONTENT DES. + if ( EQUALN(pszData + 2, pszXmlDataContent, sizeXmlDataContent) && + nDataLen > idxXmlDataContentDESDATA ) + { + foundXmlDataContent = true; + + // Get the value of the DESDATA field and set metadata "NITF_DES_XML_DATA_CONTENT_DESDATA". + const char* pszXML = pszData + idxXmlDataContentDESDATA; + + // Set the metadata. + oSpecialMD.SetMetadataItem( pszMDXmlDataContentDESDATA, pszXML, pszDESMetadataDomain ); + } + } + + CPLFree(pszData); + + pachNITFDES = NULL; + ppszDESsList += 1; + } +} + + +/************************************************************************/ +/* InitializeNITFDESs() */ +/************************************************************************/ + +void NITFDataset::InitializeNITFDESs() +{ + static const char *pszDESsDomain = "NITF_DES"; + + char **ppszDESsList = oSpecialMD.GetMetadata( pszDESsDomain ); + + if( ppszDESsList != NULL ) return; + +/* -------------------------------------------------------------------- */ +/* Go through all the segments and process all DES segments. */ +/* -------------------------------------------------------------------- */ + + char *pachDESData = NULL; + int nDESDataSize = 0; + std::string encodedDESData(""); + CPLStringList aosList; + + for( int iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ ) + { + NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment; + + if( EQUAL(psSegInfo->szSegmentType,"DE") ) + { + nDESDataSize = psSegInfo->nSegmentHeaderSize + psSegInfo->nSegmentSize; + pachDESData = (char*) VSIMalloc( nDESDataSize + 1 ); + + if (pachDESData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Cannot allocate memory for DES segment" ); + return; + } + + if( VSIFSeekL( psFile->fp, psSegInfo->nSegmentHeaderStart, + SEEK_SET ) != 0 + || (int)VSIFReadL( pachDESData, 1, nDESDataSize, + psFile->fp ) != nDESDataSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read %d byte DES subheader from " CPL_FRMT_GUIB ".", + nDESDataSize, + psSegInfo->nSegmentHeaderStart ); + CPLFree( pachDESData ); + return; + } + + pachDESData[nDESDataSize] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Accumulate all the DES segments. */ +/* -------------------------------------------------------------------- */ + + char* pszBase64 = CPLBase64Encode( nDESDataSize, (const GByte *)pachDESData ); + encodedDESData = pszBase64; + CPLFree(pszBase64); + + CPLFree( pachDESData ); + pachDESData = NULL; + + if( encodedDESData.empty() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Failed to encode DES subheader data!"); + return; + } + + // The length of the DES subheader data plus a space is append to the beginning of the encoded + // string so that we can recover the actual length of the image subheader when we decode it. + + char buffer[20]; + + sprintf(buffer, "%d", nDESDataSize); + + std::string desSubheaderStr(buffer); + desSubheaderStr.append(" "); + desSubheaderStr.append(encodedDESData); + + aosList.AddString(desSubheaderStr.c_str() ); + } + } + + if (aosList.size() > 0) + oSpecialMD.SetMetadata( aosList.List(), pszDESsDomain ); +} + +/************************************************************************/ +/* InitializeNITFTREs() */ +/************************************************************************/ + +void NITFDataset::InitializeNITFTREs() +{ + static const char *pszFileHeaderTREsDomain = "NITF_FILE_HEADER_TRES"; + static const char *pszImageSegmentTREsDomain = "NITF_IMAGE_SEGMENT_TRES"; + + char **ppszFileHeaderTREsList = oSpecialMD.GetMetadata( pszFileHeaderTREsDomain ); + char **ppszImageSegmentTREsList = oSpecialMD.GetMetadata( pszImageSegmentTREsDomain ); + + if( (ppszFileHeaderTREsList != NULL) && (ppszImageSegmentTREsList != NULL ) ) return; + +/* -------------------------------------------------------------------- */ +/* Loop over TRE sources (file and image). */ +/* -------------------------------------------------------------------- */ + + for( int nTRESrc = 0; nTRESrc < 2; nTRESrc++ ) + { + int nTREBytes = 0; + char *pszTREData = NULL; + const char *pszTREsDomain = NULL; + CPLStringList aosList; + +/* -------------------------------------------------------------------- */ +/* Extract file header or image segment TREs. */ +/* -------------------------------------------------------------------- */ + + if( nTRESrc == 0 ) + { + if( ppszFileHeaderTREsList != NULL ) continue; + + nTREBytes = psFile->nTREBytes; + pszTREData = psFile->pachTRE; + pszTREsDomain = pszFileHeaderTREsDomain; + } + else + { + if( ppszImageSegmentTREsList != NULL ) continue; + + if( psImage ) + { + nTREBytes = psImage->nTREBytes; + pszTREData = psImage->pachTRE; + pszTREsDomain = pszImageSegmentTREsDomain; + } + else + { + nTREBytes = 0; + pszTREData = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Loop over TREs. */ +/* -------------------------------------------------------------------- */ + + while( nTREBytes >= 11 ) + { + char szTemp[100]; + char szTag[7]; + char *pszEscapedData = NULL; + int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5 )); + + if (nThisTRESize < 0) + { + NITFGetField(szTemp, pszTREData, 0, 6 ); + CPLError(CE_Failure, CPLE_AppDefined, "Invalid size (%d) for TRE %s", + nThisTRESize, szTemp); + return; + } + + if (nThisTRESize > nTREBytes - 11) + { + CPLError(CE_Failure, CPLE_AppDefined, "Not enough bytes in TRE"); + return; + } + + strncpy( szTag, pszTREData, 6 ); + szTag[6] = '\0'; + + // trim white off tag. + while( strlen(szTag) > 0 && szTag[strlen(szTag)-1] == ' ' ) + szTag[strlen(szTag)-1] = '\0'; + + // escape data. + pszEscapedData = CPLEscapeString( pszTREData + 6, + nThisTRESize + 5, + CPLES_BackslashQuotable ); + + char * pszLine = (char *) CPLMalloc( strlen(szTag)+strlen(pszEscapedData)+2 ); + sprintf( pszLine, "%s=%s", szTag, pszEscapedData ); + aosList.AddString(pszLine); + CPLFree(pszLine); + pszLine = NULL; + + CPLFree( pszEscapedData ); + pszEscapedData = NULL; + + nTREBytes -= (nThisTRESize + 11); + pszTREData += (nThisTRESize + 11); + } + + if (aosList.size() > 0) + oSpecialMD.SetMetadata( aosList.List(), pszTREsDomain ); + } +} +#endif + +/************************************************************************/ +/* InitializeNITFMetadata() */ +/************************************************************************/ + +void NITFDataset::InitializeNITFMetadata() + +{ + static const char *pszDomainName = "NITF_METADATA"; + static const char *pszTagNITFFileHeader = "NITFFileHeader"; + static const char *pszTagNITFImageSubheader = "NITFImageSubheader"; + + if( oSpecialMD.GetMetadata( pszDomainName ) != NULL ) + return; + + // nHeaderLenOffset is the number of bytes to skip from the beginning of the NITF file header + // in order to get to the field HL (NITF file header length). + + int nHeaderLen = 0; + int nHeaderLenOffset = 0; + + // Get the NITF file header length. + + if( psFile->pachHeader != NULL ) + { + if ( (strncmp(psFile->pachHeader, "NITF02.10", 9) == 0) || (strncmp(psFile->pachHeader, "NSIF01.00", 9) == 0) ) + nHeaderLenOffset = 354; + else if ( (strncmp(psFile->pachHeader, "NITF01.10", 9) == 0) || (strncmp(psFile->pachHeader, "NITF02.00", 9) == 0) ) + nHeaderLenOffset = ( strncmp((psFile->pachHeader+280), "999998", 6 ) == 0 ) ? 394 : 354; + } + + char fieldHL[7]; + + if( nHeaderLenOffset > 0 ) + { + char *pszFieldHL = psFile->pachHeader + nHeaderLenOffset; + + memcpy(fieldHL, pszFieldHL, 6); + fieldHL[6] = '\0'; + nHeaderLen = atoi(fieldHL); + } + + if( nHeaderLen <= 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Zero length NITF file header!"); + return; + } + + char *encodedHeader = CPLBase64Encode(nHeaderLen, + (GByte*)psFile->pachHeader); + + if (encodedHeader == NULL || strlen(encodedHeader) == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Failed to encode NITF file header!"); + return; + } + + // The length of the NITF file header plus a space is append to the beginning of the encoded string so + // that we can recover the length of the NITF file header when we decode it without having to pull it + // out the HL field again. + + std::string nitfFileheaderStr(fieldHL); + nitfFileheaderStr.append(" "); + nitfFileheaderStr.append(encodedHeader); + + CPLFree( encodedHeader ); + + oSpecialMD.SetMetadataItem( pszTagNITFFileHeader, nitfFileheaderStr.c_str(), pszDomainName ); + + // Get the image subheader length. + + int nImageSubheaderLen = 0; + + for( int i = 0; i < psFile->nSegmentCount; ++i ) + { + if (strncmp(psFile->pasSegmentInfo[i].szSegmentType, "IM", 2) == 0) + { + nImageSubheaderLen = psFile->pasSegmentInfo[i].nSegmentHeaderSize; + break; + } + } + + if( nImageSubheaderLen < 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid length NITF image subheader!"); + return; + } + + if( nImageSubheaderLen > 0 ) + { + char *encodedImageSubheader = CPLBase64Encode(nImageSubheaderLen,(GByte*) psImage->pachHeader); + + if( encodedImageSubheader == NULL || strlen(encodedImageSubheader) ==0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Failed to encode image subheader!"); + return; + } + + // The length of the image subheader plus a space is append to the beginning of the encoded string so + // that we can recover the actual length of the image subheader when we decode it. + + char buffer[20]; + + sprintf(buffer, "%d", nImageSubheaderLen); + + std::string imageSubheaderStr(buffer); + imageSubheaderStr.append(" "); + imageSubheaderStr.append(encodedImageSubheader); + + CPLFree( encodedImageSubheader ); + + oSpecialMD.SetMetadataItem( pszTagNITFImageSubheader, imageSubheaderStr.c_str(), pszDomainName ); + } +} + +/************************************************************************/ +/* InitializeCGMMetadata() */ +/************************************************************************/ + +void NITFDataset::InitializeCGMMetadata() + +{ + if( oSpecialMD.GetMetadataItem( "SEGMENT_COUNT", "CGM" ) != NULL ) + return; + + int iSegment; + int iCGM = 0; + char **papszCGMMetadata = NULL; + + papszCGMMetadata = + CSLSetNameValue( papszCGMMetadata, "SEGMENT_COUNT", "0" ); + +/* ==================================================================== */ +/* Process all graphics segments. */ +/* ==================================================================== */ + for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ ) + { + NITFSegmentInfo *psSegment = psFile->pasSegmentInfo + iSegment; + + if( !EQUAL(psSegment->szSegmentType,"GR") + && !EQUAL(psSegment->szSegmentType,"SY") ) + continue; + + papszCGMMetadata = + CSLSetNameValue( papszCGMMetadata, + CPLString().Printf("SEGMENT_%d_SLOC_ROW", iCGM), + CPLString().Printf("%d",psSegment->nLOC_R) ); + papszCGMMetadata = + CSLSetNameValue( papszCGMMetadata, + CPLString().Printf("SEGMENT_%d_SLOC_COL", iCGM), + CPLString().Printf("%d",psSegment->nLOC_C) ); + + papszCGMMetadata = + CSLSetNameValue( papszCGMMetadata, + CPLString().Printf("SEGMENT_%d_CCS_ROW", iCGM), + CPLString().Printf("%d",psSegment->nCCS_R) ); + papszCGMMetadata = + CSLSetNameValue( papszCGMMetadata, + CPLString().Printf("SEGMENT_%d_CCS_COL", iCGM), + CPLString().Printf("%d",psSegment->nCCS_C) ); + + papszCGMMetadata = + CSLSetNameValue( papszCGMMetadata, + CPLString().Printf("SEGMENT_%d_SDLVL", iCGM), + CPLString().Printf("%d",psSegment->nDLVL) ); + papszCGMMetadata = + CSLSetNameValue( papszCGMMetadata, + CPLString().Printf("SEGMENT_%d_SALVL", iCGM), + CPLString().Printf("%d",psSegment->nALVL) ); + +/* -------------------------------------------------------------------- */ +/* Load the raw CGM data itself. */ +/* -------------------------------------------------------------------- */ + char *pabyCGMData, *pszEscapedCGMData; + + pabyCGMData = (char *) VSICalloc(1,(size_t)psSegment->nSegmentSize); + if (pabyCGMData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + CSLDestroy( papszCGMMetadata ); + return; + } + if( VSIFSeekL( psFile->fp, psSegment->nSegmentStart, + SEEK_SET ) != 0 + || VSIFReadL( pabyCGMData, 1, (size_t)psSegment->nSegmentSize, + psFile->fp ) != psSegment->nSegmentSize ) + { + CPLError( CE_Warning, CPLE_FileIO, + "Failed to read " CPL_FRMT_GUIB " bytes of graphic data at " CPL_FRMT_GUIB ".", + psSegment->nSegmentSize, + psSegment->nSegmentStart ); + CPLFree(pabyCGMData); + CSLDestroy( papszCGMMetadata ); + return; + } + + pszEscapedCGMData = CPLEscapeString( pabyCGMData, + (int)psSegment->nSegmentSize, + CPLES_BackslashQuotable ); + if (pszEscapedCGMData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + CPLFree(pabyCGMData); + CSLDestroy( papszCGMMetadata ); + return; + } + + papszCGMMetadata = + CSLSetNameValue( papszCGMMetadata, + CPLString().Printf("SEGMENT_%d_DATA", iCGM), + pszEscapedCGMData ); + CPLFree( pszEscapedCGMData ); + CPLFree( pabyCGMData ); + + iCGM++; + } + +/* -------------------------------------------------------------------- */ +/* Record the CGM segment count. */ +/* -------------------------------------------------------------------- */ + papszCGMMetadata = + CSLSetNameValue( papszCGMMetadata, + "SEGMENT_COUNT", + CPLString().Printf( "%d", iCGM ) ); + + oSpecialMD.SetMetadata( papszCGMMetadata, "CGM" ); + + CSLDestroy( papszCGMMetadata ); +} + +/************************************************************************/ +/* InitializeTextMetadata() */ +/************************************************************************/ + +void NITFDataset::InitializeTextMetadata() + +{ + if( oSpecialMD.GetMetadata( "TEXT" ) != NULL ) + return; + + int iSegment; + int iText = 0; + +/* ==================================================================== */ +/* Process all text segments. */ +/* ==================================================================== */ + for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ ) + { + NITFSegmentInfo *psSegment = psFile->pasSegmentInfo + iSegment; + + if( !EQUAL(psSegment->szSegmentType,"TX") ) + continue; + +/* -------------------------------------------------------------------- */ +/* Load the text header */ +/* -------------------------------------------------------------------- */ + + /* Allocate one extra byte for the NULL terminating character */ + char *pabyHeaderData = (char *) CPLCalloc(1, + (size_t) psSegment->nSegmentHeaderSize + 1); + if (VSIFSeekL(psFile->fp, psSegment->nSegmentHeaderStart, + SEEK_SET) != 0 || + VSIFReadL(pabyHeaderData, 1, (size_t) psSegment->nSegmentHeaderSize, + psFile->fp) != psSegment->nSegmentHeaderSize) + { + CPLError( CE_Warning, CPLE_FileIO, + "Failed to read %d bytes of text header data at " CPL_FRMT_GUIB ".", + psSegment->nSegmentHeaderSize, + psSegment->nSegmentHeaderStart); + CPLFree(pabyHeaderData); + return; + } + + oSpecialMD.SetMetadataItem( CPLString().Printf("HEADER_%d", iText), + pabyHeaderData, "TEXT"); + CPLFree(pabyHeaderData); + +/* -------------------------------------------------------------------- */ +/* Load the raw TEXT data itself. */ +/* -------------------------------------------------------------------- */ + char *pabyTextData; + + /* Allocate one extra byte for the NULL terminating character */ + pabyTextData = (char *) VSICalloc(1,(size_t)psSegment->nSegmentSize+1); + if (pabyTextData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + return; + } + if( VSIFSeekL( psFile->fp, psSegment->nSegmentStart, + SEEK_SET ) != 0 + || VSIFReadL( pabyTextData, 1, (size_t)psSegment->nSegmentSize, + psFile->fp ) != psSegment->nSegmentSize ) + { + CPLError( CE_Warning, CPLE_FileIO, + "Failed to read " CPL_FRMT_GUIB " bytes of text data at " CPL_FRMT_GUIB ".", + psSegment->nSegmentSize, + psSegment->nSegmentStart ); + CPLFree( pabyTextData ); + return; + } + + oSpecialMD.SetMetadataItem( CPLString().Printf( "DATA_%d", iText), + pabyTextData, "TEXT" ); + CPLFree( pabyTextData ); + + iText++; + } +} + +/************************************************************************/ +/* InitializeTREMetadata() */ +/************************************************************************/ + +void NITFDataset::InitializeTREMetadata() + +{ + if( oSpecialMD.GetMetadata( "TRE" ) != NULL ) + return; + + CPLXMLNode* psTresNode = CPLCreateXMLNode(NULL, CXT_Element, "tres"); + +/* -------------------------------------------------------------------- */ +/* Loop over TRE sources (file and image). */ +/* -------------------------------------------------------------------- */ + int nTRESrc; + + for( nTRESrc = 0; nTRESrc < 2; nTRESrc++ ) + { + int nTREBytes; + char *pszTREData; + + if( nTRESrc == 0 ) + { + nTREBytes = psFile->nTREBytes; + pszTREData = psFile->pachTRE; + } + else + { + if( psImage ) + { + nTREBytes = psImage->nTREBytes; + pszTREData = psImage->pachTRE; + } + else + { + nTREBytes = 0; + pszTREData = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Loop over TREs. */ +/* -------------------------------------------------------------------- */ + + while( nTREBytes >= 11 ) + { + char szTemp[100]; + char szTag[7]; + char *pszEscapedData; + int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5 )); + + if (nThisTRESize < 0) + { + NITFGetField(szTemp, pszTREData, 0, 6 ); + CPLError(CE_Failure, CPLE_AppDefined, "Invalid size (%d) for TRE %s", + nThisTRESize, szTemp); + return; + } + if (nThisTRESize > nTREBytes - 11) + { + CPLError(CE_Failure, CPLE_AppDefined, "Not enough bytes in TRE"); + return; + } + + strncpy( szTag, pszTREData, 6 ); + szTag[6] = '\0'; + + // trim white off tag. + while( strlen(szTag) > 0 && szTag[strlen(szTag)-1] == ' ' ) + szTag[strlen(szTag)-1] = '\0'; + + CPLXMLNode* psTreNode = NITFCreateXMLTre(psFile, szTag, pszTREData + 11,nThisTRESize); + if (psTreNode) + { + CPLCreateXMLNode(CPLCreateXMLNode(psTreNode, CXT_Attribute, "location"), + CXT_Text, nTRESrc == 0 ? "file" : "image"); + CPLAddXMLChild(psTresNode, psTreNode); + } + + // escape data. + pszEscapedData = CPLEscapeString( pszTREData + 11, + nThisTRESize, + CPLES_BackslashQuotable ); + if (pszEscapedData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + return; + } + + char szUniqueTag[32]; + strcpy(szUniqueTag, szTag); + int nCountUnique = 2; + while(oSpecialMD.GetMetadataItem( szUniqueTag, "TRE") != NULL) + { + sprintf(szUniqueTag, "%s_%d", szTag, nCountUnique); + nCountUnique ++; + } + oSpecialMD.SetMetadataItem( szUniqueTag, pszEscapedData, "TRE" ); + CPLFree( pszEscapedData ); + + nTREBytes -= (nThisTRESize + 11); + pszTREData += (nThisTRESize + 11); + } + } + +/* -------------------------------------------------------------------- */ +/* Loop over TRE in DES */ +/* -------------------------------------------------------------------- */ + int iSegment; + for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ ) + { + NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment; + NITFDES *psDES; + int nOffset = 0; + char szTREName[7]; + int nThisTRESize; + + if( !EQUAL(psSegInfo->szSegmentType,"DE") ) + continue; + + psDES = NITFDESAccess( psFile, iSegment ); + if( psDES == NULL ) + continue; + + char* pabyTREData = NULL; + nOffset = 0; + while (NITFDESGetTRE( psDES, nOffset, szTREName, &pabyTREData, &nThisTRESize)) + { + char* pszEscapedData = CPLEscapeString( pabyTREData, nThisTRESize, + CPLES_BackslashQuotable ); + if (pszEscapedData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + NITFDESFreeTREData(pabyTREData); + NITFDESDeaccess(psDES); + return; + } + + // trim white off tag. + while( strlen(szTREName) > 0 && szTREName[strlen(szTREName)-1] == ' ' ) + szTREName[strlen(szTREName)-1] = '\0'; + + CPLXMLNode* psTreNode = NITFCreateXMLTre(psFile, szTREName, pabyTREData,nThisTRESize); + if (psTreNode) + { + const char* pszDESID = CSLFetchNameValue(psDES->papszMetadata, "NITF_DESID"); + CPLCreateXMLNode(CPLCreateXMLNode(psTreNode, CXT_Attribute, "location"), + CXT_Text, pszDESID ? CPLSPrintf("des %s", pszDESID) : "des"); + CPLAddXMLChild(psTresNode, psTreNode); + } + + char szUniqueTag[32]; + strcpy(szUniqueTag, szTREName); + int nCountUnique = 2; + while(oSpecialMD.GetMetadataItem( szUniqueTag, "TRE") != NULL) + { + sprintf(szUniqueTag, "%s_%d", szTREName, nCountUnique); + nCountUnique ++; + } + oSpecialMD.SetMetadataItem( szUniqueTag, pszEscapedData, "TRE" ); + + CPLFree(pszEscapedData); + + nOffset += 11 + nThisTRESize; + + NITFDESFreeTREData(pabyTREData); + } + + NITFDESDeaccess(psDES); + } + + if (psTresNode->psChild != NULL) + { + char* pszXML = CPLSerializeXMLTree(psTresNode); + char* apszMD[2]; + apszMD[0] = pszXML; + apszMD[1] = NULL; + oSpecialMD.SetMetadata( apszMD, "xml:TRE" ); + CPLFree(pszXML); + } + CPLDestroyXMLNode(psTresNode); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **NITFDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "NITF_METADATA", "NITF_DES", "NITF_DES_METADATA", + "NITF_FILE_HEADER_TRES", "NITF_IMAGE_SEGMENT_TRES", + "CGM", "TEXT", "TRE", "xml:TRE", "OVERVIEWS", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **NITFDataset::GetMetadata( const char * pszDomain ) + +{ + if( pszDomain != NULL && EQUAL(pszDomain,"NITF_METADATA") ) + { + // InitializeNITFMetadata retrieves the NITF file header and all image segment file headers. (NOTE: The returned strings are base64-encoded). + + InitializeNITFMetadata(); + return oSpecialMD.GetMetadata( pszDomain ); + } + +#ifdef ESRI_BUILD + if( pszDomain != NULL && EQUAL(pszDomain,"NITF_DES") ) + { + // InitializeNITFDESs retrieves all the DES file headers (NOTE: The returned strings are base64-encoded). + + InitializeNITFDESs(); + return oSpecialMD.GetMetadata( pszDomain ); + } + + if( pszDomain != NULL && EQUAL(pszDomain,"NITF_DES_METADATA") ) + { + // InitializeNITFDESs retrieves all the DES file headers (NOTE: The returned strings are base64-encoded). + + InitializeNITFDESMetadata(); + return oSpecialMD.GetMetadata( pszDomain ); + } + + if( pszDomain != NULL && EQUAL(pszDomain,"NITF_FILE_HEADER_TRES") ) + { + // InitializeNITFTREs retrieves all the TREs that are resides in the NITF file header and all the + // TREs that are resides in the current image segment. + // NOTE: the returned strings are backslash-escaped + + InitializeNITFTREs(); + return oSpecialMD.GetMetadata( pszDomain ); + } + + if( pszDomain != NULL && EQUAL(pszDomain,"NITF_IMAGE_SEGMENT_TRES") ) + { + // InitializeNITFTREs retrieves all the TREs that are resides in the NITF file header and all the + // TREs that are resides in the current image segment. + // NOTE: the returned strings are backslash-escaped + + InitializeNITFTREs(); + return oSpecialMD.GetMetadata( pszDomain ); + } +#endif + + if( pszDomain != NULL && EQUAL(pszDomain,"CGM") ) + { + InitializeCGMMetadata(); + return oSpecialMD.GetMetadata( pszDomain ); + } + + if( pszDomain != NULL && EQUAL(pszDomain,"TEXT") ) + { + InitializeTextMetadata(); + return oSpecialMD.GetMetadata( pszDomain ); + } + + if( pszDomain != NULL && EQUAL(pszDomain,"TRE") ) + { + InitializeTREMetadata(); + return oSpecialMD.GetMetadata( pszDomain ); + } + + if( pszDomain != NULL && EQUAL(pszDomain,"xml:TRE") ) + { + InitializeTREMetadata(); + return oSpecialMD.GetMetadata( pszDomain ); + } + + return GDALPamDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *NITFDataset::GetMetadataItem(const char * pszName, + const char * pszDomain ) + +{ + if( pszDomain != NULL && EQUAL(pszDomain,"NITF_METADATA") ) + { + // InitializeNITFMetadata retrieves the NITF file header and all image segment file headers. (NOTE: The returned strings are base64-encoded). + + InitializeNITFMetadata(); + return oSpecialMD.GetMetadataItem( pszName, pszDomain ); + } + +#ifdef ESRI_BUILD + if( pszDomain != NULL && EQUAL(pszDomain,"NITF_DES_METADATA") ) + { + // InitializeNITFDESs retrieves all the DES file headers (NOTE: The returned strings are base64-encoded). + + InitializeNITFDESMetadata(); + return oSpecialMD.GetMetadataItem( pszName, pszDomain ); + } + + if( pszDomain != NULL && EQUAL(pszDomain,"NITF_FILE_HEADER_TRES") ) + { + // InitializeNITFTREs retrieves all the TREs that are resides in the NITF file header and all the + // TREs that are resides in the current image segment. + // NOTE: the returned strings are backslash-escaped + + InitializeNITFTREs(); + return oSpecialMD.GetMetadataItem( pszName, pszDomain ); + } + + if( pszDomain != NULL && EQUAL(pszDomain,"NITF_IMAGE_SEGMENT_TRES") ) + { + // InitializeNITFTREs retrieves all the TREs that are resides in the NITF file header and all the + // TREs that are resides in the current image segment. + // NOTE: the returned strings are backslash-escaped + + InitializeNITFTREs(); + return oSpecialMD.GetMetadataItem( pszName, pszDomain ); + } +#endif + + if( pszDomain != NULL && EQUAL(pszDomain,"CGM") ) + { + InitializeCGMMetadata(); + return oSpecialMD.GetMetadataItem( pszName, pszDomain ); + } + + if( pszDomain != NULL && EQUAL(pszDomain,"TEXT") ) + { + InitializeTextMetadata(); + return oSpecialMD.GetMetadataItem( pszName, pszDomain ); + } + + if( pszDomain != NULL && EQUAL(pszDomain,"TRE") ) + { + InitializeTREMetadata(); + return oSpecialMD.GetMetadataItem( pszName, pszDomain ); + } + + if( pszDomain != NULL && EQUAL(pszDomain,"OVERVIEWS") + && osRSetVRT.size() > 0 ) + return osRSetVRT; + + return GDALPamDataset::GetMetadataItem( pszName, pszDomain ); +} + + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int NITFDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *NITFDataset::GetGCPProjection() + +{ + if( nGCPCount > 0 && pszGCPProjection != NULL ) + return pszGCPProjection; + else + return ""; +} + +/************************************************************************/ +/* GetGCP() */ +/************************************************************************/ + +const GDAL_GCP *NITFDataset::GetGCPs() + +{ + return pasGCPList; +} + +/************************************************************************/ +/* CheckForRSets() */ +/* */ +/* Check for reduced resolution images in .r files and if */ +/* found return filename for a virtual file wrapping them as an */ +/* overview file. (#3457) */ +/************************************************************************/ + +int NITFDataset::CheckForRSets( const char *pszNITFFilename, + char** papszSiblingFiles ) + +{ + bool isR0File = EQUAL(CPLGetExtension(pszNITFFilename),"r0"); + +/* -------------------------------------------------------------------- */ +/* Check to see if we have RSets. */ +/* -------------------------------------------------------------------- */ + std::vector aosRSetFilenames; + int i; + + for( i = 1; i <= 5; i++ ) + { + CPLString osTarget; + VSIStatBufL sStat; + + if ( isR0File ) + { + osTarget = pszNITFFilename; + osTarget[osTarget.size()-1] = (char) ('0' + i ); + } + else + osTarget.Printf( "%s.r%d", pszNITFFilename, i ); + + if( papszSiblingFiles == NULL ) + { + if( VSIStatL( osTarget, &sStat ) != 0 ) + break; + } + else + { + if( CSLFindStringCaseSensitive(papszSiblingFiles, + CPLGetFilename( osTarget )) < 0 ) + break; + } + + aosRSetFilenames.push_back( osTarget ); + } + + if( aosRSetFilenames.size() == 0 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* We do, so try to create a wrapping VRT file. */ +/* -------------------------------------------------------------------- */ + CPLString osFragment; + int iBand; + + osRSetVRT.Printf( "\n", + GetRasterXSize()/2, GetRasterYSize()/2 ); + + for( iBand = 0; iBand < GetRasterCount(); iBand++ ) + { + GDALRasterBand *poBand = GetRasterBand(iBand+1); + + osRSetVRT += osFragment. + Printf( " \n", + GDALGetDataTypeName( poBand->GetRasterDataType() ), + iBand+1 ); + + for( i = 0; i < (int) aosRSetFilenames.size(); i++ ) + { + char* pszEscaped = CPLEscapeString(aosRSetFilenames[i].c_str(), -1, CPLES_XML); + if( i == 0 ) + osRSetVRT += osFragment.Printf( + " %s%d\n", + pszEscaped, iBand+1 ); + else + osRSetVRT += osFragment.Printf( + " %s%d\n", + pszEscaped, iBand+1 ); + CPLFree(pszEscaped); + } + osRSetVRT += osFragment. + Printf( " \n" ); + } + + osRSetVRT += "\n"; + + return TRUE; +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr NITFDataset::IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ +/* -------------------------------------------------------------------- */ +/* If we have been using RSets we will need to clear them first. */ +/* -------------------------------------------------------------------- */ + if( osRSetVRT.size() > 0 ) + { + oOvManager.CleanOverviews(); + osRSetVRT = ""; + } + + bExposeUnderlyingJPEGDatasetOverviews = FALSE; + +/* -------------------------------------------------------------------- */ +/* If we have an underlying JPEG2000 dataset (hopefully via */ +/* JP2KAK) we will try and build zero overviews as a way of */ +/* tricking it into clearing existing overviews-from-jpeg2000. */ +/* -------------------------------------------------------------------- */ + if( poJ2KDataset != NULL + && !poJ2KDataset->GetMetadataItem( "OVERVIEW_FILE", "OVERVIEWS" ) ) + poJ2KDataset->IBuildOverviews( pszResampling, 0, NULL, + nListBands, panBandList, + GDALDummyProgress, NULL ); + +/* -------------------------------------------------------------------- */ +/* Use the overview manager to build requested overviews. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = GDALPamDataset::IBuildOverviews( pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* If we are working with jpeg or jpeg2000, let the underlying */ +/* dataset know about the overview file. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poSubDS = poJ2KDataset; + if( poJPEGDataset ) + poSubDS = poJPEGDataset; + + const char *pszOverviewFile = + GetMetadataItem( "OVERVIEW_FILE", "OVERVIEWS" ); + + if( poSubDS && pszOverviewFile != NULL && eErr == CE_None + && poSubDS->GetMetadataItem( "OVERVIEW_FILE", "OVERVIEWS") == NULL ) + { + poSubDS->SetMetadataItem( "OVERVIEW_FILE", + pszOverviewFile, + "OVERVIEWS" ); + } + + return eErr; +} + +/************************************************************************/ +/* ScanJPEGQLevel() */ +/* */ +/* Search the NITF APP header in the jpeg data stream to find */ +/* out what predefined Q level tables should be used (or -1 if */ +/* they are inline). */ +/************************************************************************/ + +int NITFDataset::ScanJPEGQLevel( GUIntBig *pnDataStart ) + +{ + GByte abyHeader[100]; + + if( VSIFSeekL( psFile->fp, *pnDataStart, + SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Seek error to jpeg data stream." ); + return 0; + } + + if( VSIFReadL( abyHeader, 1, sizeof(abyHeader), psFile->fp ) + < sizeof(abyHeader) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Read error to jpeg data stream." ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* Scan ahead for jpeg magic code. In some files (eg. NSIF) */ +/* there seems to be some extra junk before the image data stream. */ +/* -------------------------------------------------------------------- */ + GUInt32 nOffset = 0; + while( nOffset < sizeof(abyHeader) - 23 + && (abyHeader[nOffset+0] != 0xff + || abyHeader[nOffset+1] != 0xd8 + || abyHeader[nOffset+2] != 0xff) ) + nOffset++; + + if( nOffset >= sizeof(abyHeader) - 23 ) + return 0; + + *pnDataStart += nOffset; + + if( nOffset > 0 ) + CPLDebug( "NITF", + "JPEG data stream at offset %d from start of data segement, NSIF?", + nOffset ); + +/* -------------------------------------------------------------------- */ +/* Do we have an NITF app tag? If so, pull out the Q level. */ +/* -------------------------------------------------------------------- */ + if( !EQUAL((char *)abyHeader+nOffset+6,"NITF") ) + return 0; + + return abyHeader[22+nOffset]; +} + +/************************************************************************/ +/* ScanJPEGBlocks() */ +/************************************************************************/ + +CPLErr NITFDataset::ScanJPEGBlocks() + +{ + int iBlock; + GUIntBig nJPEGStart = + psFile->pasSegmentInfo[psImage->iSegment].nSegmentStart; + + nQLevel = ScanJPEGQLevel( &nJPEGStart ); + +/* -------------------------------------------------------------------- */ +/* Allocate offset array */ +/* -------------------------------------------------------------------- */ + panJPEGBlockOffset = (GIntBig *) + VSICalloc(sizeof(GIntBig), + psImage->nBlocksPerRow*psImage->nBlocksPerColumn); + if (panJPEGBlockOffset == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + return CE_Failure; + } + panJPEGBlockOffset[0] = nJPEGStart; + + if ( psImage->nBlocksPerRow * psImage->nBlocksPerColumn == 1) + return CE_None; + + for( iBlock = psImage->nBlocksPerRow * psImage->nBlocksPerColumn - 1; + iBlock > 0; iBlock-- ) + panJPEGBlockOffset[iBlock] = -1; + +/* -------------------------------------------------------------------- */ +/* Scan through the whole image data stream identifying all */ +/* block boundaries. Each block starts with 0xFFD8 (SOI). */ +/* They also end with 0xFFD9, but we don't currently look for */ +/* that. */ +/* -------------------------------------------------------------------- */ + int iNextBlock = 1; + GIntBig iSegOffset = 2; + GIntBig iSegSize = psFile->pasSegmentInfo[psImage->iSegment].nSegmentSize + - (nJPEGStart - psFile->pasSegmentInfo[psImage->iSegment].nSegmentStart); + GByte abyBlock[512]; + int ignoreBytes = 0; + + while( iSegOffset < iSegSize-1 ) + { + size_t nReadSize = MIN((size_t)sizeof(abyBlock),(size_t)(iSegSize - iSegOffset)); + size_t i; + + if( VSIFSeekL( psFile->fp, panJPEGBlockOffset[0] + iSegOffset, + SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Seek error to jpeg data stream." ); + return CE_Failure; + } + + if( VSIFReadL( abyBlock, 1, nReadSize, psFile->fp ) < (size_t)nReadSize) + { + CPLError( CE_Failure, CPLE_FileIO, + "Read error to jpeg data stream." ); + return CE_Failure; + } + + for( i = 0; i < nReadSize-1; i++ ) + { + if (ignoreBytes == 0) + { + if( abyBlock[i] == 0xff ) + { + /* start-of-image marker */ + if ( abyBlock[i+1] == 0xd8 ) + { + panJPEGBlockOffset[iNextBlock++] + = panJPEGBlockOffset[0] + iSegOffset + i; + + if( iNextBlock == psImage->nBlocksPerRow*psImage->nBlocksPerColumn) + { + return CE_None; + } + } + /* Skip application-specific data to avoid false positive while detecting */ + /* start-of-image markers (#2927). The size of the application data is */ + /* found in the two following bytes */ + /* We need this complex mechanism of ignoreBytes for dealing with */ + /* application data crossing several abyBlock ... */ + else if ( abyBlock[i+1] >= 0xe0 && abyBlock[i+1] < 0xf0 ) + { + ignoreBytes = -2; + } + } + } + else if (ignoreBytes < 0) + { + if (ignoreBytes == -1) + { + /* Size of the application data */ + ignoreBytes = abyBlock[i]*256 + abyBlock[i+1]; + } + else + ignoreBytes++; + } + else + { + ignoreBytes--; + } + } + + iSegOffset += nReadSize - 1; + } + + return CE_None; +} + +/************************************************************************/ +/* ReadJPEGBlock() */ +/************************************************************************/ + +CPLErr NITFDataset::ReadJPEGBlock( int iBlockX, int iBlockY ) + +{ + CPLErr eErr; + +/* -------------------------------------------------------------------- */ +/* If this is our first request, do a scan for block boundaries. */ +/* -------------------------------------------------------------------- */ + if( panJPEGBlockOffset == NULL ) + { + if (EQUAL(psImage->szIC,"M3")) + { +/* -------------------------------------------------------------------- */ +/* When a data mask subheader is present, we don't need to scan */ +/* the whole file. We just use the psImage->panBlockStart table */ +/* -------------------------------------------------------------------- */ + panJPEGBlockOffset = (GIntBig *) + VSICalloc(sizeof(GIntBig), + psImage->nBlocksPerRow*psImage->nBlocksPerColumn); + if (panJPEGBlockOffset == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + return CE_Failure; + } + int i; + for (i=0;i< psImage->nBlocksPerRow*psImage->nBlocksPerColumn;i++) + { + panJPEGBlockOffset[i] = psImage->panBlockStart[i]; + if (panJPEGBlockOffset[i] != -1 && panJPEGBlockOffset[i] != 0xffffffff) + { + GUIntBig nOffset = panJPEGBlockOffset[i]; + nQLevel = ScanJPEGQLevel(&nOffset); + /* The beginning of the JPEG stream should be the offset */ + /* from the panBlockStart table */ + if (nOffset != (GUIntBig)panJPEGBlockOffset[i]) + { + CPLError(CE_Failure, CPLE_AppDefined, + "JPEG block doesn't start at expected offset"); + return CE_Failure; + } + } + } + } + else /* 'C3' case */ + { +/* -------------------------------------------------------------------- */ +/* Scan through the whole image data stream identifying all */ +/* block boundaries. */ +/* -------------------------------------------------------------------- */ + eErr = ScanJPEGBlocks(); + if( eErr != CE_None ) + return eErr; + } + } + +/* -------------------------------------------------------------------- */ +/* Allocate image data block (where the uncompressed image will go) */ +/* -------------------------------------------------------------------- */ + if( pabyJPEGBlock == NULL ) + { + /* Allocate enough memory to hold 12bit JPEG data */ + pabyJPEGBlock = (GByte *) + VSICalloc(psImage->nBands, + psImage->nBlockWidth * psImage->nBlockHeight * 2); + if (pabyJPEGBlock == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + return CE_Failure; + } + } + + +/* -------------------------------------------------------------------- */ +/* Read JPEG Chunk. */ +/* -------------------------------------------------------------------- */ + CPLString osFilename; + int iBlock = iBlockX + iBlockY * psImage->nBlocksPerRow; + GDALDataset *poDS; + int anBands[3] = { 1, 2, 3 }; + + if (panJPEGBlockOffset[iBlock] == -1 || panJPEGBlockOffset[iBlock] == 0xffffffff) + { + memset(pabyJPEGBlock, 0, psImage->nBands*psImage->nBlockWidth*psImage->nBlockHeight*2); + return CE_None; + } + + osFilename.Printf( "JPEG_SUBFILE:Q%d," CPL_FRMT_GIB ",%d,%s", + nQLevel, + panJPEGBlockOffset[iBlock], 0, + osNITFFilename.c_str() ); + + poDS = (GDALDataset *) GDALOpen( osFilename, GA_ReadOnly ); + if( poDS == NULL ) + return CE_Failure; + + if( poDS->GetRasterXSize() != psImage->nBlockWidth + || poDS->GetRasterYSize() != psImage->nBlockHeight ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JPEG block %d not same size as NITF blocksize.", + iBlock ); + delete poDS; + return CE_Failure; + } + + if( poDS->GetRasterCount() < psImage->nBands ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JPEG block %d has not enough bands.", + iBlock ); + delete poDS; + return CE_Failure; + } + + if( poDS->GetRasterBand(1)->GetRasterDataType() != GetRasterBand(1)->GetRasterDataType()) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JPEG block %d data type (%s) not consistent with band data type (%s).", + iBlock, GDALGetDataTypeName(poDS->GetRasterBand(1)->GetRasterDataType()), + GDALGetDataTypeName(GetRasterBand(1)->GetRasterDataType()) ); + delete poDS; + return CE_Failure; + } + + eErr = poDS->RasterIO( GF_Read, + 0, 0, + psImage->nBlockWidth, psImage->nBlockHeight, + pabyJPEGBlock, + psImage->nBlockWidth, psImage->nBlockHeight, + GetRasterBand(1)->GetRasterDataType(), psImage->nBands, anBands, + 0, 0, 0, NULL ); + + delete poDS; + + return eErr; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **NITFDataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + +/* -------------------------------------------------------------------- */ +/* Check for .imd file. */ +/* -------------------------------------------------------------------- */ + papszFileList = AddFile( papszFileList, "IMD", "imd" ); + +/* -------------------------------------------------------------------- */ +/* Check for .rpb file. */ +/* -------------------------------------------------------------------- */ + papszFileList = AddFile( papszFileList, "RPB", "rpb" ); + +/* -------------------------------------------------------------------- */ +/* Check for other files. */ +/* -------------------------------------------------------------------- */ + papszFileList = AddFile( papszFileList, "ATT", "att" ); + papszFileList = AddFile( papszFileList, "EPH", "eph" ); + papszFileList = AddFile( papszFileList, "GEO", "geo" ); + papszFileList = AddFile( papszFileList, "XML", "xml" ); + + return papszFileList; +} + +/************************************************************************/ +/* AddFile() */ +/* */ +/* Helper method for GetFileList() */ +/************************************************************************/ +char **NITFDataset::AddFile(char **papszFileList, const char* EXTENSION, const char* extension) +{ + VSIStatBufL sStatBuf; + CPLString osTarget = CPLResetExtension( osNITFFilename, EXTENSION ); + if( oOvManager.GetSiblingFiles() != NULL ) + { + if( CSLFindStringCaseSensitive( oOvManager.GetSiblingFiles(), + CPLGetFilename(osTarget) ) >= 0 ) + papszFileList = CSLAddString( papszFileList, osTarget ); + else + { + osTarget = CPLResetExtension( osNITFFilename, extension ); + if( CSLFindStringCaseSensitive( oOvManager.GetSiblingFiles(), + CPLGetFilename(osTarget) ) >= 0 ) + papszFileList = CSLAddString( papszFileList, osTarget ); + } + } + else + { + if( VSIStatL( osTarget, &sStatBuf ) == 0 ) + papszFileList = CSLAddString( papszFileList, osTarget ); + else + { + osTarget = CPLResetExtension( osNITFFilename, extension ); + if( VSIStatL( osTarget, &sStatBuf ) == 0 ) + papszFileList = CSLAddString( papszFileList, osTarget ); + } + } + + return papszFileList; +} + +/************************************************************************/ +/* GDALToNITFDataType() */ +/************************************************************************/ + +static const char *GDALToNITFDataType( GDALDataType eType ) + +{ + const char *pszPVType; + + switch( eType ) + { + case GDT_Byte: + case GDT_UInt16: + case GDT_UInt32: + pszPVType = "INT"; + break; + + case GDT_Int16: + case GDT_Int32: + pszPVType = "SI"; + break; + + case GDT_Float32: + case GDT_Float64: + pszPVType = "R"; + break; + + case GDT_CInt16: + case GDT_CInt32: + CPLError( CE_Failure, CPLE_AppDefined, + "NITF format does not support complex integer data." ); + return NULL; + + case GDT_CFloat32: + pszPVType = "C"; + break; + + default: + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported raster pixel type (%s).", + GDALGetDataTypeName(eType) ); + return NULL; + } + + return pszPVType; +} + +/************************************************************************/ +/* NITFJP2ECWOptions() */ +/* */ +/* Prepare JP2-in-NITF creation options based in part of the */ +/* NITF creation options. */ +/************************************************************************/ + +static char **NITFJP2ECWOptions( char **papszOptions ) + +{ + int i; + char** papszJP2Options = NULL; + + papszJP2Options = CSLAddString(papszJP2Options, "PROFILE=NPJE"); + papszJP2Options = CSLAddString(papszJP2Options, "CODESTREAM_ONLY=TRUE"); + + for( i = 0; papszOptions != NULL && papszOptions[i] != NULL; i++ ) + { + if( EQUALN(papszOptions[i],"PROFILE=",8) ) + { + CPLFree(papszJP2Options[0]); + papszJP2Options[0] = CPLStrdup(papszOptions[i]); + } + else if( EQUALN(papszOptions[i],"TARGET=",7) ) + papszJP2Options = CSLAddString(papszJP2Options, papszOptions[i]); + } + + return papszJP2Options; +} +/************************************************************************/ +/* NITFJP2KAKOptions() */ +/* */ +/* Prepare JP2-in-NITF creation options based in part of the */ +/* NITF creation options. */ +/************************************************************************/ + +static char **NITFJP2KAKOptions( char **papszOptions ) + +{ + int i; + char** papszKAKOptions = NULL; + + for( i = 0; papszOptions != NULL && papszOptions[i] != NULL; i++ ) + { + if( EQUALN(papszOptions[i],"QUALITY=", 8) ) + papszKAKOptions = CSLAddString(papszKAKOptions, papszOptions[i]); + else if (EQUALN(papszOptions[i],"BLOCKXSIZE=", 11) ) + papszKAKOptions = CSLAddString(papszKAKOptions, papszOptions[i]); + else if (EQUALN(papszOptions[i],"BLOCKYSIZE=", 11) ) + papszKAKOptions = CSLAddString(papszKAKOptions, papszOptions[i]); + else if (EQUALN(papszOptions[i],"GMLPJ2=", 7) ) + papszKAKOptions = CSLAddString(papszKAKOptions, papszOptions[i]); + else if (EQUALN(papszOptions[i],"GeoJP2=", 7) ) + papszKAKOptions = CSLAddString(papszKAKOptions, papszOptions[i]); + else if (EQUALN(papszOptions[i],"LAYERS=", 7) ) + papszKAKOptions = CSLAddString(papszKAKOptions, papszOptions[i]); + else if (EQUALN(papszOptions[i],"ROI=", 4) ) + papszKAKOptions = CSLAddString(papszKAKOptions, papszOptions[i]); + } + + return papszKAKOptions; +} + + + +/************************************************************************/ +/* NITFExtractTEXTAndCGMCreationOption() */ +/************************************************************************/ + +static char** NITFExtractTEXTAndCGMCreationOption( GDALDataset* poSrcDS, + char **papszOptions, + char ***ppapszTextMD, + char ***ppapszCgmMD ) +{ + char** papszFullOptions = CSLDuplicate(papszOptions); + +/* -------------------------------------------------------------------- */ +/* Prepare for text segments. */ +/* -------------------------------------------------------------------- */ + int iOpt, nNUMT = 0; + char **papszTextMD = CSLFetchNameValueMultiple (papszOptions, "TEXT"); + // Notice: CSLFetchNameValueMultiple remove the leading "TEXT=" when + // returning the list, which is what we want. + + // Use TEXT information from original image if no creation option is passed in. + if (poSrcDS != NULL && papszTextMD == NULL) + { + // Read CGM adata from original image, duplicate the list becuase + // we frees papszCgmMD at end of the function. + papszTextMD = CSLDuplicate( poSrcDS->GetMetadata( "TEXT" )); + } + + for( iOpt = 0; + papszTextMD != NULL && papszTextMD[iOpt] != NULL; + iOpt++ ) + { + if( !EQUALN(papszTextMD[iOpt],"DATA_",5) ) + continue; + + nNUMT++; + } + + if( nNUMT > 0 ) + { + papszFullOptions = CSLAddString( papszFullOptions, + CPLString().Printf( "NUMT=%d", + nNUMT ) ); + } + +/* -------------------------------------------------------------------- */ +/* Prepare for CGM segments. */ +/* -------------------------------------------------------------------- */ + const char *pszNUMS; // graphic segment option string + int nNUMS = 0; + + char **papszCgmMD = CSLFetchNameValueMultiple (papszOptions, "CGM"); + // Notice: CSLFetchNameValueMultiple remove the leading "CGM=" when + // returning the list, which is what we want. + + // Use CGM information from original image if no creation option is passed in. + if (poSrcDS != NULL && papszCgmMD == NULL) + { + // Read CGM adata from original image, duplicate the list becuase + // we frees papszCgmMD at end of the function. + papszCgmMD = CSLDuplicate( poSrcDS->GetMetadata( "CGM" )); + } + + // Set NUMS based on the number of segments + if (papszCgmMD != NULL) + { + pszNUMS = CSLFetchNameValue(papszCgmMD, "SEGMENT_COUNT"); + + if (pszNUMS != NULL) { + nNUMS = atoi(pszNUMS); + } + papszFullOptions = CSLAddString(papszFullOptions, + CPLString().Printf("NUMS=%d", nNUMS)); + } + + *ppapszTextMD = papszTextMD; + *ppapszCgmMD = papszCgmMD; + + return papszFullOptions; +} + +/************************************************************************/ +/* NITFDatasetCreate() */ +/************************************************************************/ + +GDALDataset * +NITFDataset::NITFDatasetCreate( const char *pszFilename, int nXSize, int nYSize, int nBands, + GDALDataType eType, char **papszOptions ) + +{ + const char *pszPVType = GDALToNITFDataType( eType ); + const char *pszIC = CSLFetchNameValue( papszOptions, "IC" ); + + if( pszPVType == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* We disallow any IC value except NC when creating this way. */ +/* -------------------------------------------------------------------- */ + GDALDriver *poJ2KDriver = NULL; + + if( pszIC != NULL && EQUAL(pszIC,"C8") ) + { + int bHasCreate = FALSE; + + poJ2KDriver = GetGDALDriverManager()->GetDriverByName( "JP2ECW" ); + if( poJ2KDriver != NULL ) + bHasCreate = poJ2KDriver->GetMetadataItem( GDAL_DCAP_CREATE, + NULL ) != NULL; + if( !bHasCreate ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create JPEG2000 encoded NITF files. The\n" + "JP2ECW driver is unavailable, or missing Create support." ); + return NULL; + } + } + + else if( pszIC != NULL && !EQUAL(pszIC,"NC") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported compression (IC=%s) used in direct\n" + "NITF File creation", + pszIC ); + return NULL; + } + + const char* pszSDE_TRE = CSLFetchNameValue(papszOptions, "SDE_TRE"); + if (pszSDE_TRE != NULL) + { + CPLError( CE_Warning, CPLE_AppDefined, + "SDE_TRE creation option ignored by Create() method (only valid in CreateCopy())" ); + } + + +/* -------------------------------------------------------------------- */ +/* Prepare for text and CGM segments. */ +/* -------------------------------------------------------------------- */ + char **papszTextMD = NULL; + char **papszCgmMD = NULL; + char **papszFullOptions = NITFExtractTEXTAndCGMCreationOption( NULL, + papszOptions, + &papszTextMD, + &papszCgmMD ); + +/* -------------------------------------------------------------------- */ +/* Create the file. */ +/* -------------------------------------------------------------------- */ + + if( !NITFCreate( pszFilename, nXSize, nYSize, nBands, + GDALGetDataTypeSize( eType ), pszPVType, + papszFullOptions ) ) + { + CSLDestroy(papszTextMD); + CSLDestroy(papszCgmMD); + CSLDestroy(papszFullOptions); + return NULL; + } + + CSLDestroy(papszFullOptions); + papszFullOptions = NULL; + +/* -------------------------------------------------------------------- */ +/* Various special hacks related to JPEG2000 encoded files. */ +/* -------------------------------------------------------------------- */ + GDALDataset* poWritableJ2KDataset = NULL; + if( poJ2KDriver ) + { + NITFFile *psFile = NITFOpen( pszFilename, TRUE ); + if (psFile == NULL) + { + CSLDestroy(papszTextMD); + CSLDestroy(papszCgmMD); + return NULL; + } + GUIntBig nImageOffset = psFile->pasSegmentInfo[0].nSegmentStart; + + CPLString osDSName; + + osDSName.Printf("/vsisubfile/" CPL_FRMT_GUIB "_%d,%s", nImageOffset, -1, pszFilename); + + NITFClose( psFile ); + + char** papszJP2Options = NITFJP2ECWOptions(papszOptions); + poWritableJ2KDataset = + poJ2KDriver->Create( osDSName, nXSize, nYSize, nBands, eType, + papszJP2Options ); + CSLDestroy(papszJP2Options); + + if( poWritableJ2KDataset == NULL ) + { + CSLDestroy(papszTextMD); + CSLDestroy(papszCgmMD); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Open the dataset in update mode. */ +/* -------------------------------------------------------------------- */ + GDALOpenInfo oOpenInfo( pszFilename, GA_Update ); + NITFDataset* poDS = (NITFDataset*) + NITFDataset::OpenInternal(&oOpenInfo, poWritableJ2KDataset, TRUE); + if (poDS) + { + poDS->papszTextMDToWrite = papszTextMD; + poDS->papszCgmMDToWrite = papszCgmMD; + } + else + { + CSLDestroy(papszTextMD); + CSLDestroy(papszCgmMD); + } + return poDS; +} + +/************************************************************************/ +/* NITFCreateCopy() */ +/************************************************************************/ + +GDALDataset * +NITFDataset::NITFCreateCopy( + const char *pszFilename, GDALDataset *poSrcDS, + int bStrict, char **papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + GDALDataType eType; + GDALRasterBand *poBand1; + int bJPEG2000 = FALSE; + int bJPEG = FALSE; + NITFDataset *poDstDS = NULL; + GDALDriver *poJ2KDriver = NULL; + + int nBands = poSrcDS->GetRasterCount(); + if( nBands == 0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to export files with zero bands." ); + return NULL; + } + + poBand1 = poSrcDS->GetRasterBand(1); + if( poBand1 == NULL ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Only allow supported compression values. */ +/* -------------------------------------------------------------------- */ + const char* pszIC = CSLFetchNameValue( papszOptions, "IC" ); + if( pszIC != NULL ) + { + if( EQUAL(pszIC,"NC") ) + /* ok */; + else if( EQUAL(pszIC,"C8") ) + { + poJ2KDriver = + GetGDALDriverManager()->GetDriverByName( "JP2ECW" ); + if( poJ2KDriver == NULL || + poJ2KDriver->GetMetadataItem( GDAL_DCAP_CREATECOPY, NULL ) == NULL ) + { + /* Try with JP2KAK as an alternate driver */ + poJ2KDriver = + GetGDALDriverManager()->GetDriverByName( "JP2KAK" ); + } + if( poJ2KDriver == NULL ) + { + /* Try with Jasper as an alternate driver */ + poJ2KDriver = + GetGDALDriverManager()->GetDriverByName( "JPEG2000" ); + } + if( poJ2KDriver == NULL ) + { + CPLError( + CE_Failure, CPLE_AppDefined, + "Unable to write JPEG2000 compressed NITF file.\n" + "No 'subfile' JPEG2000 write supporting drivers are\n" + "configured." ); + return NULL; + } + bJPEG2000 = TRUE; + } + else if( EQUAL(pszIC,"C3") || EQUAL(pszIC,"M3") ) + { + bJPEG = TRUE; +#ifndef JPEG_SUPPORTED + CPLError( + CE_Failure, CPLE_AppDefined, + "Unable to write JPEG compressed NITF file.\n" + "Libjpeg is not configured into build." ); + return NULL; +#endif + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Only IC=NC (uncompressed), IC=C3/M3 (JPEG) and IC=C8 (JPEG2000)\n" + "allowed with NITF CreateCopy method." ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Get the data type. Complex integers isn't supported by */ +/* NITF, so map that to complex float if we aren't in strict */ +/* mode. */ +/* -------------------------------------------------------------------- */ + eType = poBand1->GetRasterDataType(); + if( !bStrict && (eType == GDT_CInt16 || eType == GDT_CInt32) ) + eType = GDT_CFloat32; + +/* -------------------------------------------------------------------- */ +/* Prepare for text and CGM segments. */ +/* -------------------------------------------------------------------- */ + char **papszTextMD = NULL; + char **papszCgmMD = NULL; + char **papszFullOptions = NITFExtractTEXTAndCGMCreationOption( poSrcDS, + papszOptions, + &papszTextMD, + &papszCgmMD ); + +/* -------------------------------------------------------------------- */ +/* Copy over other source metadata items as creation options */ +/* that seem useful. */ +/* -------------------------------------------------------------------- */ + char **papszSrcMD = poSrcDS->GetMetadata(); + int iMD; + + for( iMD = 0; papszSrcMD && papszSrcMD[iMD]; iMD++ ) + { + if( EQUALN(papszSrcMD[iMD],"NITF_BLOCKA",11) + || EQUALN(papszSrcMD[iMD],"NITF_FHDR",9) ) + { + char *pszName = NULL; + const char *pszValue = CPLParseNameValue( papszSrcMD[iMD], + &pszName ); + if( pszName != NULL && + CSLFetchNameValue( papszFullOptions, pszName+5 ) == NULL ) + papszFullOptions = + CSLSetNameValue( papszFullOptions, pszName+5, pszValue ); + CPLFree(pszName); + } + } + +/* -------------------------------------------------------------------- */ +/* Copy TRE definitions as creation options. */ +/* -------------------------------------------------------------------- */ + papszSrcMD = poSrcDS->GetMetadata( "TRE" ); + + for( iMD = 0; papszSrcMD && papszSrcMD[iMD]; iMD++ ) + { + CPLString osTRE; + + if (EQUALN(papszSrcMD[iMD], "RPFHDR", 6) || + EQUALN(papszSrcMD[iMD], "RPFIMG", 6) || + EQUALN(papszSrcMD[iMD], "RPFDES", 6)) + { + /* Do not copy RPF TRE. They contain absolute offsets */ + /* No chance that they make sense in the new NITF file */ + continue; + } + + osTRE = "TRE="; + osTRE += papszSrcMD[iMD]; + + papszFullOptions = CSLAddString( papszFullOptions, osTRE ); + } + +/* -------------------------------------------------------------------- */ +/* Set if we can set IREP. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue(papszFullOptions,"IREP") == NULL ) + { + if ( ((poSrcDS->GetRasterCount() == 3 && bJPEG) || + (poSrcDS->GetRasterCount() >= 3 && !bJPEG)) && eType == GDT_Byte && + poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_RedBand && + poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_GreenBand && + poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_BlueBand) + { + if( bJPEG ) + papszFullOptions = + CSLSetNameValue( papszFullOptions, "IREP", "YCbCr601" ); + else + papszFullOptions = + CSLSetNameValue( papszFullOptions, "IREP", "RGB" ); + } + else if( poSrcDS->GetRasterCount() == 1 && eType == GDT_Byte + && poBand1->GetColorTable() != NULL ) + { + papszFullOptions = + CSLSetNameValue( papszFullOptions, "IREP", "RGB/LUT" ); + papszFullOptions = + CSLSetNameValue( papszFullOptions, "LUT_SIZE", + CPLString().Printf( + "%d", poBand1->GetColorTable()->GetColorEntryCount()) ); + } + else if( GDALDataTypeIsComplex(eType) ) + papszFullOptions = + CSLSetNameValue( papszFullOptions, "IREP", "NODISPLY" ); + + else + papszFullOptions = + CSLSetNameValue( papszFullOptions, "IREP", "MONO" ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have lat/long georeferencing information? */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + int bWriteGeoTransform = FALSE; + int bWriteGCPs = FALSE; + int bNorth, nZone = 0; + OGRSpatialReference oSRS, oSRS_WGS84; + char *pszWKT = (char *) poSrcDS->GetProjectionRef(); + if( pszWKT == NULL || pszWKT[0] == '\0' ) + pszWKT = (char *) poSrcDS->GetGCPProjection(); + + if( pszWKT != NULL && pszWKT[0] != '\0' ) + { + oSRS.importFromWkt( &pszWKT ); + + /* NITF is only WGS84 */ + oSRS_WGS84.SetWellKnownGeogCS( "WGS84" ); + if ( oSRS.IsSameGeogCS(&oSRS_WGS84) == FALSE) + { + CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "NITF only supports WGS84 geographic and UTM projections.\n"); + if (bStrict) + { + CSLDestroy(papszFullOptions); + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } + } + + const char* pszICORDS = CSLFetchNameValue(papszFullOptions, "ICORDS"); + +/* -------------------------------------------------------------------- */ +/* Should we write DIGEST Spatial Data Extension TRE ? */ +/* -------------------------------------------------------------------- */ + const char* pszSDE_TRE = CSLFetchNameValue(papszFullOptions, "SDE_TRE"); + int bSDE_TRE = pszSDE_TRE && CSLTestBoolean(pszSDE_TRE); + if (bSDE_TRE) + { + if( oSRS.IsGeographic() && oSRS.GetPrimeMeridian() == 0.0 + && poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None && + adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0 && + adfGeoTransform[5] < 0.0) + { + /* Override ICORDS to G if necessary */ + if (pszICORDS != NULL && EQUAL(pszICORDS, "D")) + { + papszFullOptions = + CSLSetNameValue( papszFullOptions, "ICORDS", "G" ); + CPLError(CE_Warning, CPLE_AppDefined, + "Forcing ICORDS=G when writing GEOLOB"); + } + else + { + /* Code a bit below will complain with other ICORDS value */ + } + + if (CSLPartialFindString(papszFullOptions, "TRE=GEOLOB=") != - 1) + { + CPLDebug("NITF", "GEOLOB TRE was explicitly defined before. " + "Overriding it with current georefencing info."); + } + + /* Structure of SDE TRE documented here */ + /*http://www.gwg.nga.mil/ntb/baseline/docs/digest/part2_annex_d.pdf */ + +/* -------------------------------------------------------------------- */ +/* Write GEOLOB TRE */ +/* -------------------------------------------------------------------- */ + char szGEOLOB[48+1]; + char* pszGEOLOB = szGEOLOB; + double dfARV = 360.0 / adfGeoTransform[1]; + double dfBRV = 360.0 / -adfGeoTransform[5]; + double dfLSO = adfGeoTransform[0]; + double dfPSO = adfGeoTransform[3]; + sprintf(pszGEOLOB, "%09d", (int)(dfARV + 0.5)); pszGEOLOB += 9; + sprintf(pszGEOLOB, "%09d", (int)(dfBRV + 0.5)); pszGEOLOB += 9; + sprintf(pszGEOLOB, "%#+015.10f", dfLSO); pszGEOLOB += 15; + sprintf(pszGEOLOB, "%#+015.10f", dfPSO); pszGEOLOB += 15; + CPLAssert(pszGEOLOB == szGEOLOB + 48); + + CPLString osGEOLOB("TRE=GEOLOB="); + osGEOLOB += szGEOLOB; + papszFullOptions = CSLAddString( papszFullOptions, osGEOLOB ) ; + +/* -------------------------------------------------------------------- */ +/* Write GEOPSB TRE if not already explicitly provided */ +/* -------------------------------------------------------------------- */ + if (CSLPartialFindString(papszFullOptions, "FILE_TRE=GEOPSB=") == -1 && + CSLPartialFindString(papszFullOptions, "TRE=GEOPSB=") == -1) + { + char szGEOPSB[443+1]; + memset(szGEOPSB, ' ', 443); + szGEOPSB[443] = 0; + #define WRITE_STR_NOSZ(dst, src) memcpy(dst, src, strlen(src)) + char* pszGEOPSB = szGEOPSB; + WRITE_STR_NOSZ(pszGEOPSB, "GEO"); pszGEOPSB += 3; + WRITE_STR_NOSZ(pszGEOPSB, "DEG"); pszGEOPSB += 3; + WRITE_STR_NOSZ(pszGEOPSB, "World Geodetic System 1984"); pszGEOPSB += 80; + WRITE_STR_NOSZ(pszGEOPSB, "WGE"); pszGEOPSB += 4; + WRITE_STR_NOSZ(pszGEOPSB, "World Geodetic System 1984"); pszGEOPSB += 80; + WRITE_STR_NOSZ(pszGEOPSB, "WE"); pszGEOPSB += 3; + WRITE_STR_NOSZ(pszGEOPSB, "Geodetic"); pszGEOPSB += 80; /* DVR */ + WRITE_STR_NOSZ(pszGEOPSB, "GEOD"); pszGEOPSB += 4; /* VDCDVR */ + WRITE_STR_NOSZ(pszGEOPSB, "Mean Sea"); pszGEOPSB += 80; /* SDA */ + WRITE_STR_NOSZ(pszGEOPSB, "MSL"); pszGEOPSB += 4; /* VDCSDA */ + WRITE_STR_NOSZ(pszGEOPSB, "000000000000000"); pszGEOPSB += 15; /* ZOR */ + pszGEOPSB += 3; /* GRD */ + pszGEOPSB += 80; /* GRN */ + WRITE_STR_NOSZ(pszGEOPSB, "0000"); pszGEOPSB += 4; /* ZNA */ + CPLAssert(pszGEOPSB == szGEOPSB + 443); + + CPLString osGEOPSB("FILE_TRE=GEOPSB="); + osGEOPSB += szGEOPSB; + papszFullOptions = CSLAddString( papszFullOptions, osGEOPSB ) ; + } + else + { + CPLDebug("NITF", "GEOPSB TRE was explicitly defined before. Keeping it."); + } + + } + else + { + CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "Georeferencing info isn't compatible with writing a GEOLOB TRE (only geographic SRS handled for now)"); + if (bStrict) + { + CSLDestroy(papszFullOptions); + return NULL; + } + } + } + + bWriteGeoTransform = ( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None ); + bWriteGCPs = ( !bWriteGeoTransform && poSrcDS->GetGCPCount() == 4 ); + + if( oSRS.IsGeographic() && oSRS.GetPrimeMeridian() == 0.0 ) + { + if (pszICORDS == NULL) + { + papszFullOptions = + CSLSetNameValue( papszFullOptions, "ICORDS", "G" ); + } + else if (EQUAL(pszICORDS, "G") || EQUAL(pszICORDS, "D")) + { + /* Do nothing */ + } + else + { + CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "Inconsistent ICORDS value with SRS : %s%s.\n", pszICORDS, + (!bStrict) ? ". Setting it to G instead" : ""); + if (bStrict) + { + CSLDestroy(papszFullOptions); + return NULL; + } + papszFullOptions = + CSLSetNameValue( papszFullOptions, "ICORDS", "G" ); + } + } + + else if( oSRS.GetUTMZone( &bNorth ) > 0 ) + { + if( bNorth ) + papszFullOptions = + CSLSetNameValue( papszFullOptions, "ICORDS", "N" ); + else + papszFullOptions = + CSLSetNameValue( papszFullOptions, "ICORDS", "S" ); + + nZone = oSRS.GetUTMZone( NULL ); + } + else + { + CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "NITF only supports WGS84 geographic and UTM projections.\n"); + if (bStrict) + { + CSLDestroy(papszFullOptions); + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create the output file. */ +/* -------------------------------------------------------------------- */ + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + const char *pszPVType = GDALToNITFDataType( eType ); + + if( pszPVType == NULL ) + { + CSLDestroy(papszFullOptions); + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } + + if (!NITFCreate( pszFilename, nXSize, nYSize, poSrcDS->GetRasterCount(), + GDALGetDataTypeSize( eType ), pszPVType, + papszFullOptions )) + { + CSLDestroy( papszFullOptions ); + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } + + CSLDestroy( papszFullOptions ); + papszFullOptions = NULL; + +/* ==================================================================== */ +/* JPEG2000 case. We need to write the data through a J2K */ +/* driver in pixel interleaved form. */ +/* ==================================================================== */ + if( bJPEG2000 ) + { + NITFFile *psFile = NITFOpen( pszFilename, TRUE ); + if (psFile == NULL) + { + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } + + GDALDataset *poJ2KDataset = NULL; + GUIntBig nImageOffset = psFile->pasSegmentInfo[0].nSegmentStart; + CPLString osDSName; + + NITFClose( psFile ); + + osDSName.Printf( "/vsisubfile/" CPL_FRMT_GUIB "_%d,%s", + nImageOffset, -1, + pszFilename ); + + if (EQUAL(poJ2KDriver->GetDescription(), "JP2ECW")) + { + char** papszJP2Options = NITFJP2ECWOptions(papszOptions); + poJ2KDataset = + poJ2KDriver->CreateCopy( osDSName, poSrcDS, FALSE, + papszJP2Options, + pfnProgress, pProgressData ); + CSLDestroy(papszJP2Options); + } + else if (EQUAL(poJ2KDriver->GetDescription(), "JP2KAK")) + { + char** papszKAKOptions = NITFJP2KAKOptions(papszOptions); + poJ2KDataset = + poJ2KDriver->CreateCopy( osDSName, poSrcDS, FALSE, + papszKAKOptions, + pfnProgress, pProgressData ); + CSLDestroy(papszKAKOptions); + + } + else + { + /* Jasper case */ + const char* apszOptions[] = { "FORMAT=JPC", NULL }; + poJ2KDataset = + poJ2KDriver->CreateCopy( osDSName, poSrcDS, FALSE, + (char **)apszOptions, + pfnProgress, pProgressData ); + } + if( poJ2KDataset == NULL ) + { + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } + + delete poJ2KDataset; + + // Now we need to figure out the actual length of the file + // and correct the image segment size information. + GIntBig nPixelCount = nXSize * ((GIntBig) nYSize) * + poSrcDS->GetRasterCount(); + + NITFPatchImageLength( pszFilename, nImageOffset, nPixelCount, "C8" ); + NITFWriteCGMSegments( pszFilename, papszCgmMD ); + NITFWriteTextSegments( pszFilename, papszTextMD ); + + GDALOpenInfo oOpenInfo( pszFilename, GA_Update ); + poDstDS = (NITFDataset *) Open( &oOpenInfo ); + + if( poDstDS == NULL ) + { + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } + } + +/* ==================================================================== */ +/* Loop copying bands to an uncompressed file. */ +/* ==================================================================== */ + else if( bJPEG ) + { +#ifdef JPEG_SUPPORTED + NITFFile *psFile = NITFOpen( pszFilename, TRUE ); + if (psFile == NULL) + { + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } + GUIntBig nImageOffset = psFile->pasSegmentInfo[0].nSegmentStart; + int bSuccess; + + bSuccess = + NITFWriteJPEGImage( poSrcDS, psFile->fp, nImageOffset, + papszOptions, + pfnProgress, pProgressData ); + + if( !bSuccess ) + { + NITFClose( psFile ); + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } + + // Now we need to figure out the actual length of the file + // and correct the image segment size information. + GIntBig nPixelCount = nXSize * ((GIntBig) nYSize) * + poSrcDS->GetRasterCount(); + + NITFClose( psFile ); + + NITFPatchImageLength( pszFilename, nImageOffset, + nPixelCount, pszIC ); + + NITFWriteCGMSegments( pszFilename, papszCgmMD ); + NITFWriteTextSegments( pszFilename, papszTextMD ); + + GDALOpenInfo oOpenInfo( pszFilename, GA_Update ); + poDstDS = (NITFDataset *) Open( &oOpenInfo ); + + if( poDstDS == NULL ) + { + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } +#endif /* def JPEG_SUPPORTED */ + } + +/* ==================================================================== */ +/* Loop copying bands to an uncompressed file. */ +/* ==================================================================== */ + else + { + NITFWriteCGMSegments( pszFilename, papszCgmMD ); + NITFWriteTextSegments( pszFilename, papszTextMD ); + + GDALOpenInfo oOpenInfo( pszFilename, GA_Update ); + poDstDS = (NITFDataset *) Open( &oOpenInfo ); + if( poDstDS == NULL ) + { + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } + + void *pData = VSIMalloc2(nXSize, (GDALGetDataTypeSize(eType) / 8)); + if (pData == NULL) + { + delete poDstDS; + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } + + CPLErr eErr = CE_None; + + for( int iBand = 0; eErr == CE_None && iBand < poSrcDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + GDALRasterBand *poDstBand = poDstDS->GetRasterBand( iBand+1 ); + +/* -------------------------------------------------------------------- */ +/* Do we need to copy a colortable or other metadata? */ +/* -------------------------------------------------------------------- */ + GDALColorTable *poCT; + + poCT = poSrcBand->GetColorTable(); + if( poCT != NULL ) + poDstBand->SetColorTable( poCT ); + +/* -------------------------------------------------------------------- */ +/* Copy image data. */ +/* -------------------------------------------------------------------- */ + for( int iLine = 0; iLine < nYSize; iLine++ ) + { + eErr = poSrcBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + pData, nXSize, 1, eType, 0, 0, NULL ); + if( eErr != CE_None ) + break; + + eErr = poDstBand->RasterIO( GF_Write, 0, iLine, nXSize, 1, + pData, nXSize, 1, eType, 0, 0, NULL ); + + if( eErr != CE_None ) + break; + + if( !pfnProgress( (iBand + (iLine+1) / (double) nYSize) + / (double) poSrcDS->GetRasterCount(), + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + break; + } + } + } + + CPLFree( pData ); + + if ( eErr != CE_None ) + { + delete poDstDS; + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Set the georeferencing. */ +/* -------------------------------------------------------------------- */ + if( bWriteGeoTransform ) + { + poDstDS->psImage->nZone = nZone; + poDstDS->SetGeoTransform( adfGeoTransform ); + } + else if( bWriteGCPs ) + { + poDstDS->psImage->nZone = nZone; + poDstDS->SetGCPs( poSrcDS->GetGCPCount(), + poSrcDS->GetGCPs(), + poSrcDS->GetGCPProjection() ); + } + + poDstDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + CSLDestroy(papszCgmMD); + CSLDestroy(papszTextMD); + + return poDstDS; +} + +/************************************************************************/ +/* NITFPatchImageLength() */ +/* */ +/* Fixup various stuff we don't know till we have written the */ +/* imagery. In particular the file length, image data length */ +/* and the compression ratio achieved. */ +/************************************************************************/ + +static void NITFPatchImageLength( const char *pszFilename, + GUIntBig nImageOffset, + GIntBig nPixelCount, + const char *pszIC ) + +{ + VSILFILE *fpVSIL = VSIFOpenL( pszFilename, "r+b" ); + if( fpVSIL == NULL ) + return; + + VSIFSeekL( fpVSIL, 0, SEEK_END ); + GUIntBig nFileLen = VSIFTellL( fpVSIL ); + +/* -------------------------------------------------------------------- */ +/* Update total file length. */ +/* -------------------------------------------------------------------- */ + if (nFileLen >= (GUIntBig)(1e12 - 1)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too big file : " CPL_FRMT_GUIB ". Truncating to 999999999998", + nFileLen); + nFileLen = (GUIntBig)(1e12 - 2); + } + VSIFSeekL( fpVSIL, 342, SEEK_SET ); + CPLString osLen = CPLString().Printf("%012" CPL_FRMT_GB_WITHOUT_PREFIX "u",nFileLen); + VSIFWriteL( (void *) osLen.c_str(), 1, 12, fpVSIL ); + +/* -------------------------------------------------------------------- */ +/* Update the image data length. */ +/* -------------------------------------------------------------------- */ + GUIntBig nImageSize = nFileLen-nImageOffset; + if (GUINTBIG_TO_DOUBLE(nImageSize) >= 1e10 - 1) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too big image size : " CPL_FRMT_GUIB". Truncating to 9999999998", + nImageSize); + nImageSize = (GUIntBig)(1e10 - 2); + } + VSIFSeekL( fpVSIL, 369, SEEK_SET ); + osLen = CPLString().Printf("%010" CPL_FRMT_GB_WITHOUT_PREFIX "u",nImageSize); + VSIFWriteL( (void *) osLen.c_str(), 1, 10, fpVSIL ); + +/* -------------------------------------------------------------------- */ +/* Update COMRAT, the compression rate variable. We have to */ +/* take into account the presence of graphic and text segments, */ +/* the optional presence of IGEOLO and ICOM to find its position. */ +/* -------------------------------------------------------------------- */ + char szICBuf[2]; + char achNUM[4]; // buffer for segment size. 3 digits plus null character + achNUM[3] = '\0'; + + // get number of graphic and text segment so we can calculate offset for + // image IC. + int nNumIOffset = 360; + VSIFSeekL( fpVSIL, nNumIOffset, SEEK_SET ); + VSIFReadL( achNUM, 1, 3, fpVSIL ); + int nIM = atoi(achNUM); // number of image segment + + int nNumSOffset = nNumIOffset + 3 + nIM * 16; + VSIFSeekL( fpVSIL, nNumSOffset, SEEK_SET ); + VSIFReadL( achNUM, 1, 3, fpVSIL ); + int nGS = atoi(achNUM); // number of graphic segment + + int nNumTOffset = nNumSOffset + 3 + 10 * nGS + 3; + VSIFSeekL( fpVSIL, nNumTOffset, SEEK_SET ); + VSIFReadL( achNUM, 1, 3, fpVSIL ); + int nTS = atoi(achNUM); // number of text segment + + int nAdditionalOffset = nGS * 10 + nTS * 9; + + /* Read ICORDS */ + VSIFSeekL( fpVSIL, 775 + nAdditionalOffset , SEEK_SET ); + char chICORDS; + VSIFReadL( &chICORDS, 1, 1, fpVSIL ); + if (chICORDS != ' ') + VSIFSeekL( fpVSIL, 60, SEEK_CUR); /* skip IGEOLO */ + + /* Read NICOM */ + char achNICOM[2]; + VSIFReadL( achNICOM, 1, 1, fpVSIL ); + achNICOM[1] = 0; + int nNICOM = atoi(achNICOM); + VSIFSeekL( fpVSIL, nNICOM * 80, SEEK_CUR); /* skip comments */ + + /* Read IC */ + VSIFReadL( szICBuf, 2, 1, fpVSIL ); + + /* The following line works around a "feature" of *BSD libc (at least PC-BSD 7.1) */ + /* that makes the position of the file offset unreliable when executing a */ + /* "seek, read and write" sequence. After the read(), the file offset seen by */ + /* the write() is approximatively the size of a block further... */ + VSIFSeekL( fpVSIL, VSIFTellL( fpVSIL ), SEEK_SET ); + + if( !EQUALN(szICBuf,pszIC,2) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to locate COMRAT to update in NITF header." ); + } + else + { + char szCOMRAT[5]; + + if( EQUAL(pszIC,"C8") ) /* jpeg2000 */ + { + double dfRate = (GIntBig)(nFileLen-nImageOffset) * 8 / (double) nPixelCount; + dfRate = MAX(0.01,MIN(99.99,dfRate)); + + // We emit in wxyz format with an implicit decimal place + // between wx and yz as per spec for lossy compression. + // We really should have a special case for lossless compression. + sprintf( szCOMRAT, "%04d", (int) (dfRate * 100)); + } + else if( EQUAL(pszIC, "C3") || EQUAL(pszIC, "M3") ) /* jpeg */ + { + strcpy( szCOMRAT, "00.0" ); + } + + VSIFWriteL( szCOMRAT, 4, 1, fpVSIL ); + } + + VSIFCloseL( fpVSIL ); +} + +/************************************************************************/ +/* NITFWriteCGMSegments() */ +/************************************************************************/ +static int NITFWriteCGMSegments( const char *pszFilename, char **papszList) +{ + char errorMessage[255] = ""; + + // size of each Cgm header entry (LS (4) + LSSH (6)) + const int nCgmHdrEntrySz = 10; + + if (papszList == NULL) + return TRUE; + + int nNUMS = 0; + const char *pszNUMS; + pszNUMS = CSLFetchNameValue(papszList, "SEGMENT_COUNT"); + if (pszNUMS != NULL) + { + nNUMS = atoi(pszNUMS); + } + + /* -------------------------------------------------------------------- */ + /* Open the target file. */ + /* -------------------------------------------------------------------- */ + VSILFILE *fpVSIL = VSIFOpenL(pszFilename, "r+b"); + + if (fpVSIL == NULL) + return FALSE; + + // Calculates the offset for NUMS so we can update header data + char achNUMI[4]; // 3 digits plus null character + achNUMI[3] = '\0'; + + // NUMI offset is at a fixed offset 363 + int nNumIOffset = 360; + VSIFSeekL(fpVSIL, nNumIOffset, SEEK_SET ); + VSIFReadL(achNUMI, 1, 3, fpVSIL); + int nIM = atoi(achNUMI); + + // 6 for size of LISH and 10 for size of LI + // NUMS offset is NumI offset plus the size of NumI + size taken up each + // the header data multiply by the number of data + + int nNumSOffset = nNumIOffset + 3+ nIM * (6 + 10); + + /* -------------------------------------------------------------------- */ + /* Confirm that the NUMS in the file header already matches the */ + /* number of graphic segments we want to write */ + /* -------------------------------------------------------------------- */ + char achNUMS[4]; + + VSIFSeekL( fpVSIL, nNumSOffset, SEEK_SET ); + VSIFReadL( achNUMS, 1, 3, fpVSIL ); + achNUMS[3] = '\0'; + + if( atoi(achNUMS) != nNUMS ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "It appears an attempt was made to add or update graphic\n" + "segments on an NITF file with existing segments. This\n" + "is not currently supported by the GDAL NITF driver." ); + + VSIFCloseL( fpVSIL ); + return FALSE; + } + + + // allocate space for graphic header. + // Size of LS = 4, size of LSSH = 6, and 1 for null character + char *pachLS = (char *) CPLCalloc(nNUMS * nCgmHdrEntrySz + 1, 1); + + /* -------------------------------------------------------------------- */ + /* Assume no extended data such as SXSHDL, SXSHD */ + /* -------------------------------------------------------------------- */ + + /* ==================================================================== */ + /* Write the Graphics segments at the end of the file. */ + /* ==================================================================== */ + + #define PLACE(location,name,text) strncpy(location,text,strlen(text)) + + for (int i = 0; i < nNUMS; i++) + { + + // Get all the fields for current CGM segment + const char *pszSlocRow = CSLFetchNameValue(papszList, + CPLString().Printf("SEGMENT_%d_SLOC_ROW", i)); + const char *pszSlocCol = CSLFetchNameValue(papszList, + CPLString().Printf("SEGMENT_%d_SLOC_COL", i)); + const char *pszSdlvl = CSLFetchNameValue(papszList, + CPLString().Printf("SEGMENT_%d_SDLVL", i)); + const char *pszSalvl = CSLFetchNameValue(papszList, + CPLString().Printf("SEGMENT_%d_SALVL", i)); + const char *pszData = CSLFetchNameValue(papszList, + CPLString().Printf("SEGMENT_%d_DATA", i)); + + // Error checking + if (pszSlocRow == NULL) + { + sprintf(errorMessage, "NITF graphic segment writing error: SLOC_ROW for segment %d is not defined",i); + break; + } + if (pszSlocCol == NULL) + { + sprintf(errorMessage, "NITF graphic segment writing error: SLOC_COL for segment %d is not defined",i); + break; + } + if (pszSdlvl == NULL) + { + sprintf(errorMessage, "NITF graphic segment writing error: SDLVL for segment %d is not defined", i); + break; + } + if (pszSalvl == NULL) + { + sprintf(errorMessage, "NITF graphic segment writing error: SALVLfor segment %d is not defined", i); + break; + } + if (pszData == NULL) + { + sprintf(errorMessage, "NITF graphic segment writing error: DATA for segment %d is not defined", i); + break; + } + + int nSlocCol = atoi(pszSlocRow); + int nSlocRow = atoi(pszSlocCol); + int nSdlvl = atoi(pszSdlvl); + int nSalvl = atoi(pszSalvl); + + // Create a buffer for graphics segment header, 258 is the size of + // the header that we will be writing. + char achGSH[258]; + + memset(achGSH, ' ', sizeof(achGSH)); + + + PLACE( achGSH+ 0, SY , "SY" ); + PLACE( achGSH+ 2, SID ,CPLSPrintf("%010d", i) ); + PLACE( achGSH+ 12, SNAME , "DEFAULT NAME " ); + PLACE( achGSH+32, SSCLAS , "U" ); + PLACE( achGSH+33, SSCLASY , "0" ); + PLACE( achGSH+199, ENCRYP , "0" ); + PLACE( achGSH+200, SFMT , "C" ); + PLACE( achGSH+201, SSTRUCT , "0000000000000" ); + PLACE( achGSH+214, SDLVL , CPLSPrintf("%03d",nSdlvl)); // size3 + PLACE( achGSH+217, SALVL , CPLSPrintf("%03d",nSalvl)); // size3 + PLACE( achGSH+220, SLOC , CPLSPrintf("%05d%05d",nSlocRow,nSlocCol) ); // size 10 + PLACE( achGSH+230, SBAND1 , "0000000000" ); + PLACE( achGSH+240, SCOLOR, "C" ); + PLACE( achGSH+241, SBAND2, "0000000000" ); + PLACE( achGSH+251, SRES2, "00" ); + PLACE( achGSH+253, SXSHDL, "00000" ); + + // Move to the end of the file + VSIFSeekL(fpVSIL, 0, SEEK_END ); + VSIFWriteL(achGSH, 1, sizeof(achGSH), fpVSIL); + + /* -------------------------------------- ------------------------------ */ + /* Prepare and write CGM segment data. */ + /* -------------------------------------------------------------------- */ + int nCGMSize = 0; + char *pszCgmToWrite = CPLUnescapeString(pszData, &nCGMSize, + CPLES_BackslashQuotable); + + if (nCGMSize > 999998) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Length of SEGMENT_%d_DATA is %d, which is greater than 999998. Truncating...", + i + 1, nCGMSize); + nCGMSize = 999998; + } + + VSIFWriteL(pszCgmToWrite, 1, nCGMSize, fpVSIL); + + /* -------------------------------------------------------------------- */ + /* Update the subheader and data size info in the file header. */ + /* -------------------------------------------------------------------- */ + sprintf( pachLS + nCgmHdrEntrySz * i, "%04d%06d",(int) sizeof(achGSH), nCGMSize ); + + CPLFree(pszCgmToWrite); + + } // End For + + + /* -------------------------------------------------------------------- */ + /* Write out the graphic segment info. */ + /* -------------------------------------------------------------------- */ + + VSIFSeekL(fpVSIL, nNumSOffset + 3, SEEK_SET ); + VSIFWriteL(pachLS, 1, nNUMS * nCgmHdrEntrySz, fpVSIL); + + /* -------------------------------------------------------------------- */ + /* Update total file length. */ + /* -------------------------------------------------------------------- */ + VSIFSeekL(fpVSIL, 0, SEEK_END ); + GUIntBig nFileLen = VSIFTellL(fpVSIL); + // Offset to file length entry + VSIFSeekL(fpVSIL, 342, SEEK_SET ); + if (GUINTBIG_TO_DOUBLE(nFileLen) >= 1e12 - 1) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too big file : " CPL_FRMT_GUIB ". Truncating to 999999999998", + nFileLen); + nFileLen = (GUIntBig) (1e12 - 2); + } + CPLString osLen = CPLString().Printf("%012" CPL_FRMT_GB_WITHOUT_PREFIX "u", + nFileLen); + VSIFWriteL((void *) osLen.c_str(), 1, 12, fpVSIL); + + VSIFCloseL(fpVSIL); + + CPLFree(pachLS); + + if (strlen(errorMessage) != 0) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s", errorMessage); + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* NITFWriteTextSegments() */ +/************************************************************************/ + +static void NITFWriteTextSegments( const char *pszFilename, + char **papszList ) + +{ +/* -------------------------------------------------------------------- */ +/* Count the number of apparent text segments to write. There */ +/* is nothing at all to do if there are none to write. */ +/* -------------------------------------------------------------------- */ + int iOpt, nNUMT = 0; + + for( iOpt = 0; papszList != NULL && papszList[iOpt] != NULL; iOpt++ ) + { + if( EQUALN(papszList[iOpt],"DATA_",5) ) + nNUMT++; + } + + if( nNUMT == 0 ) + return; + +/* -------------------------------------------------------------------- */ +/* Open the target file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpVSIL = VSIFOpenL( pszFilename, "r+b" ); + + if( fpVSIL == NULL ) + return; + + // Get number of text field. Since there there could be multiple images + // or graphic segment, the offset need to be calculated dynamically. + + char achNUMI[4]; // 3 digits plus null character + achNUMI[3] = '\0'; + // NUMI offset is at a fixed offset 363 + int nNumIOffset = 360; + VSIFSeekL( fpVSIL, nNumIOffset, SEEK_SET ); + VSIFReadL( achNUMI, 1, 3, fpVSIL ); + int nIM = atoi(achNUMI); + + char achNUMG[4]; // 3 digits plus null character + achNUMG[3] = '\0'; + + // 3 for size of NUMI. 6 and 10 are the field size for LISH and LI + int nNumGOffset = nNumIOffset + 3 + nIM * (6 + 10); + VSIFSeekL( fpVSIL, nNumGOffset, SEEK_SET ); + VSIFReadL( achNUMG, 1, 3, fpVSIL ); + int nGS = atoi(achNUMG); + + // NUMT offset + // 3 for size of NUMG. 4 and 6 are filed size of LSSH and LS. + // the last + 3 is for NUMX field, which is not used + int nNumTOffset = nNumGOffset + 3 + nGS * (4 + 6) + 3; + + /* -------------------------------------------------------------------- */ + /* Confirm that the NUMT in the file header already matches the */ + /* number of text segements we want to write, and that the */ + /* segment header/data size info is blank. */ + /* -------------------------------------------------------------------- */ + char achNUMT[4]; + char *pachLT = (char *) CPLCalloc(nNUMT * 9 + 1, 1); + + VSIFSeekL( fpVSIL, nNumTOffset, SEEK_SET ); + VSIFReadL( achNUMT, 1, 3, fpVSIL ); + achNUMT[3] = '\0'; + + VSIFReadL( pachLT, 1, nNUMT * 9, fpVSIL ); + + if( atoi(achNUMT) != nNUMT ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "It appears an attempt was made to add or update text\n" + "segments on an NITF file with existing segments. This\n" + "is not currently supported by the GDAL NITF driver." ); + + VSIFCloseL( fpVSIL ); + CPLFree( pachLT ); + return; + } + + if( !EQUALN(pachLT," ",9) ) + { + CPLFree( pachLT ); + // presumably the text segments are already written, do nothing. + VSIFCloseL( fpVSIL ); + return; + } + +/* -------------------------------------------------------------------- */ +/* At this point we likely ought to confirm NUMDES, NUMRES, */ +/* UDHDL and XHDL are zero. Consider adding later... */ +/* -------------------------------------------------------------------- */ + +/* ==================================================================== */ +/* Write the text segments at the end of the file. */ +/* ==================================================================== */ +#define PLACE(location,name,text) strncpy(location,text,strlen(text)) + int iTextSeg = 0; + + for( iOpt = 0; papszList != NULL && papszList[iOpt] != NULL; iOpt++ ) + { + const char *pszTextToWrite; + + if( !EQUALN(papszList[iOpt],"DATA_",5) ) + continue; + + const char *pszHeaderBuffer = NULL; + + pszTextToWrite = CPLParseNameValue( papszList[iOpt], NULL ); + if( pszTextToWrite == NULL ) + continue; + +/* -------------------------------------------------------------------- */ +/* Locate corresponding header data in the buffer */ +/* -------------------------------------------------------------------- */ + + for( int iOpt2 = 0; papszList != NULL && papszList[iOpt2] != NULL; iOpt2++ ) { + if( !EQUALN(papszList[iOpt2],"HEADER_",7) ) + continue; + + char *pszHeaderKey = NULL, *pszDataKey = NULL; + CPLParseNameValue( papszList[iOpt2], &pszHeaderKey ); + CPLParseNameValue( papszList[iOpt], &pszDataKey ); + if( pszHeaderKey == NULL || pszDataKey == NULL ) + { + CPLFree(pszHeaderKey); + CPLFree(pszDataKey); + continue; + } + + char *pszHeaderId, *pszDataId; //point to header and data number + pszHeaderId = pszHeaderKey + 7; + pszDataId = pszDataKey + 5; + + bool bIsSameId = strcmp(pszHeaderId, pszDataId) == 0; + CPLFree(pszHeaderKey); + CPLFree(pszDataKey); + + // if ID matches, read the header information and exit the loop + if (bIsSameId) { + pszHeaderBuffer = CPLParseNameValue( papszList[iOpt2], NULL); + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Prepare and write text header. */ +/* -------------------------------------------------------------------- */ + char achTSH[282]; + memset( achTSH, ' ', sizeof(achTSH) ); + VSIFSeekL( fpVSIL, 0, SEEK_END ); + + if (pszHeaderBuffer!= NULL) { + memcpy( achTSH, pszHeaderBuffer, MIN(strlen(pszHeaderBuffer), sizeof(achTSH)) ); + + // Take care NITF2.0 date format changes + char chTimeZone = achTSH[20]; + + // Check for Zulu time zone character. IpachLTf that exist, then + // it's NITF2.0 format. + if (chTimeZone == 'Z') { + char *achOrigDate=achTSH+12; // original date string + + // The date value taken from default NITF file date + char achNewDate[]="20021216151629"; + char achYear[3]; + int nYear; + + // Offset to the year + strncpy(achYear,achOrigDate+12, 2); + achYear[2] = '\0'; + nYear = atoi(achYear); + + // Set century. + // Since NITF2.0 does not track the century, we are going to + // assume any year number greater then 94 (the year NITF2.0 + // spec published), will be 1900s, otherwise, it's 2000s. + if (nYear > 94) strncpy(achNewDate,"19",2); + else strncpy(achNewDate,"20",2); + + strncpy(achNewDate+6, achOrigDate,8); // copy cover DDhhmmss + strncpy(achNewDate+2, achOrigDate+12,2); // copy over years + + // Perform month conversion + char *pszOrigMonth = achOrigDate+9; + char *pszNewMonth = achNewDate+4; + + if (strncmp(pszOrigMonth,"JAN",3) == 0) strncpy(pszNewMonth,"01",2); + else if (strncmp(pszOrigMonth,"FEB",3) == 0) strncpy(pszNewMonth,"02",2); + else if (strncmp(pszOrigMonth,"MAR",3) == 0) strncpy(pszNewMonth,"03",2); + else if (strncmp(pszOrigMonth,"APR",3) == 0) strncpy(pszNewMonth,"04",2); + else if (strncmp(pszOrigMonth,"MAY",3) == 0) strncpy(pszNewMonth,"05",2); + else if (strncmp(pszOrigMonth,"JUN",3) == 0) strncpy(pszNewMonth,"07",2); + else if (strncmp(pszOrigMonth,"AUG",3) == 0) strncpy(pszNewMonth,"08",2); + else if (strncmp(pszOrigMonth,"SEP",3) == 0) strncpy(pszNewMonth,"09",2); + else if (strncmp(pszOrigMonth,"OCT",3) == 0) strncpy(pszNewMonth,"10",2); + else if (strncmp(pszOrigMonth,"NOV",3) == 0) strncpy(pszNewMonth,"11",2); + else if (strncmp(pszOrigMonth,"DEC",3) == 0) strncpy(pszNewMonth,"12",2); + + PLACE( achTSH+ 12, TXTDT , achNewDate ); + + } + } else { // Use default value if header information is not found + PLACE( achTSH+ 0, TE , "TE" ); + PLACE( achTSH+ 9, TXTALVL , "000" ); + PLACE( achTSH+ 12, TXTDT , "20021216151629" ); + PLACE( achTSH+106, TSCLAS , "U" ); + PLACE( achTSH+273, ENCRYP , "0" ); + PLACE( achTSH+274, TXTFMT , "STA" ); + PLACE( achTSH+277, TXSHDL , "00000" ); + } + + + VSIFWriteL( achTSH, 1, sizeof(achTSH), fpVSIL ); + +/* -------------------------------------------------------------------- */ +/* Prepare and write text segment data. */ +/* -------------------------------------------------------------------- */ + + int nTextLength = (int) strlen(pszTextToWrite); + if (nTextLength > 99998) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Length of DATA_%d is %d, which is greater than 99998. Truncating...", + iTextSeg + 1, nTextLength); + nTextLength = 99998; + } + + VSIFWriteL( pszTextToWrite, 1, nTextLength, fpVSIL ); + +/* -------------------------------------------------------------------- */ +/* Update the subheader and data size info in the file header. */ +/* -------------------------------------------------------------------- */ + sprintf( pachLT + 9*iTextSeg+0, "%04d%05d", + (int) sizeof(achTSH), nTextLength ); + + iTextSeg++; + } + +/* -------------------------------------------------------------------- */ +/* Write out the text segment info. */ +/* -------------------------------------------------------------------- */ + + VSIFSeekL( fpVSIL, nNumTOffset + 3, SEEK_SET ); + VSIFWriteL( pachLT, 1, nNUMT * 9, fpVSIL ); + +/* -------------------------------------------------------------------- */ +/* Update total file length. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( fpVSIL, 0, SEEK_END ); + GUIntBig nFileLen = VSIFTellL( fpVSIL ); + + VSIFSeekL( fpVSIL, 342, SEEK_SET ); + if (GUINTBIG_TO_DOUBLE(nFileLen) >= 1e12 - 1) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too big file : " CPL_FRMT_GUIB ". Truncating to 999999999998", + nFileLen); + nFileLen = (GUIntBig)(1e12 - 2); + } + CPLString osLen = CPLString().Printf("%012" CPL_FRMT_GB_WITHOUT_PREFIX "u",nFileLen); + VSIFWriteL( (void *) osLen.c_str(), 1, 12, fpVSIL ); + + VSIFCloseL( fpVSIL ); + CPLFree( pachLT ); +} + +/************************************************************************/ +/* NITFWriteJPEGImage() */ +/************************************************************************/ + +#ifdef JPEG_SUPPORTED + +int +NITFWriteJPEGBlock( GDALDataset *poSrcDS, VSILFILE *fp, + int nBlockXOff, int nBlockYOff, + int nBlockXSize, int nBlockYSize, + int bProgressive, int nQuality, + const GByte* pabyAPP6, int nRestartInterval, + GDALProgressFunc pfnProgress, void * pProgressData ); + +static int +NITFWriteJPEGImage( GDALDataset *poSrcDS, VSILFILE *fp, vsi_l_offset nStartOffset, + char **papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int nQuality = 75; + int bProgressive = FALSE; + int nRestartInterval = -1; + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + if( nBands != 1 && nBands != 3 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "JPEG driver doesn't support %d bands. Must be 1 (grey) " + "or 3 (RGB) bands.\n", nBands ); + + return FALSE; + } + + GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + +#if defined(JPEG_LIB_MK1) || defined(JPEG_DUAL_MODE_8_12) + if( eDT != GDT_Byte && eDT != GDT_UInt16 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "JPEG driver doesn't support data type %s. " + "Only eight and twelve bit bands supported (Mk1 libjpeg).\n", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); + + return FALSE; + } + + if( eDT == GDT_UInt16 || eDT == GDT_Int16 ) + eDT = GDT_UInt16; + else + eDT = GDT_Byte; + +#else + if( eDT != GDT_Byte ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "JPEG driver doesn't support data type %s. " + "Only eight bit byte bands supported.\n", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); + + return FALSE; + } + + eDT = GDT_Byte; // force to 8bit. +#endif + +/* -------------------------------------------------------------------- */ +/* What options has the user selected? */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue(papszOptions,"QUALITY") != NULL ) + { + nQuality = atoi(CSLFetchNameValue(papszOptions,"QUALITY")); + if( nQuality < 10 || nQuality > 100 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "QUALITY=%s is not a legal value in the range 10-100.", + CSLFetchNameValue(papszOptions,"QUALITY") ); + return FALSE; + } + } + + if( CSLFetchNameValue(papszOptions,"RESTART_INTERVAL") != NULL ) + { + nRestartInterval = atoi(CSLFetchNameValue(papszOptions,"RESTART_INTERVAL")); + } + + bProgressive = CSLFetchBoolean( papszOptions, "PROGRESSIVE", FALSE ); + +/* -------------------------------------------------------------------- */ +/* Compute blocking factors */ +/* -------------------------------------------------------------------- */ + int nNPPBH = nXSize; + int nNPPBV = nYSize; + + if( CSLFetchNameValue( papszOptions, "BLOCKSIZE" ) != NULL ) + nNPPBH = nNPPBV = atoi(CSLFetchNameValue( papszOptions, "BLOCKSIZE" )); + + if( CSLFetchNameValue( papszOptions, "BLOCKXSIZE" ) != NULL ) + nNPPBH = atoi(CSLFetchNameValue( papszOptions, "BLOCKXSIZE" )); + + if( CSLFetchNameValue( papszOptions, "BLOCKYSIZE" ) != NULL ) + nNPPBV = atoi(CSLFetchNameValue( papszOptions, "BLOCKYSIZE" )); + + if( CSLFetchNameValue( papszOptions, "NPPBH" ) != NULL ) + nNPPBH = atoi(CSLFetchNameValue( papszOptions, "NPPBH" )); + + if( CSLFetchNameValue( papszOptions, "NPPBV" ) != NULL ) + nNPPBV = atoi(CSLFetchNameValue( papszOptions, "NPPBV" )); + + if( nNPPBH <= 0 || nNPPBV <= 0 || + nNPPBH > 9999 || nNPPBV > 9999 ) + nNPPBH = nNPPBV = 256; + + int nNBPR = (nXSize + nNPPBH - 1) / nNPPBH; + int nNBPC = (nYSize + nNPPBV - 1) / nNPPBV; + +/* -------------------------------------------------------------------- */ +/* Creates APP6 NITF application segment (required by MIL-STD-188-198) */ +/* see #3345 */ +/* -------------------------------------------------------------------- */ + GByte abyAPP6[23]; + GUInt16 nUInt16; + int nOffset = 0; + + memcpy(abyAPP6, "NITF", 4); + abyAPP6[4] = 0; + nOffset += 5; + + /* Version : 2.0 */ + nUInt16 = 0x0200; + CPL_MSBPTR16(&nUInt16); + memcpy(abyAPP6 + nOffset, &nUInt16, sizeof(nUInt16)); + nOffset += sizeof(nUInt16); + + /* IMODE */ + abyAPP6[nOffset] = (nBands == 1) ? 'B' : 'P'; + nOffset ++; + + /* Number of image blocks per row */ + nUInt16 = (GUInt16) nNBPR; + CPL_MSBPTR16(&nUInt16); + memcpy(abyAPP6 + nOffset, &nUInt16, sizeof(nUInt16)); + nOffset += sizeof(nUInt16); + + /* Number of image blocks per column */ + nUInt16 = (GUInt16) nNBPC; + CPL_MSBPTR16(&nUInt16); + memcpy(abyAPP6 + nOffset, &nUInt16, sizeof(nUInt16)); + nOffset += sizeof(nUInt16); + + /* Image color */ + abyAPP6[nOffset] = (nBands == 1) ? 0 : 1; + nOffset ++; + + /* Original sample precision */ + abyAPP6[nOffset] = (eDT == GDT_UInt16) ? 12 : 8; + nOffset ++; + + /* Image class */ + abyAPP6[nOffset] = 0; + nOffset ++; + + /* JPEG coding process */ + abyAPP6[nOffset] = (eDT == GDT_UInt16) ? 4 : 1; + nOffset ++; + + /* Quality */ + abyAPP6[nOffset] = 0; + nOffset ++; + + /* Stream color */ + abyAPP6[nOffset] = (nBands == 1) ? 0 /* Monochrome */ : 2 /* YCbCr*/ ; + nOffset ++; + + /* Stream bits */ + abyAPP6[nOffset] = (eDT == GDT_UInt16) ? 12 : 8; + nOffset ++; + + /* Horizontal filtering */ + abyAPP6[nOffset] = 1; + nOffset ++; + + /* Vertical filtering */ + abyAPP6[nOffset] = 1; + nOffset ++; + + /* Reserved */ + abyAPP6[nOffset] = 0; + nOffset ++; + abyAPP6[nOffset] = 0; + nOffset ++; + + CPLAssert(nOffset == sizeof(abyAPP6)); + +/* -------------------------------------------------------------------- */ +/* Prepare block map if necessary */ +/* -------------------------------------------------------------------- */ + + VSIFSeekL( fp, nStartOffset, SEEK_SET ); + + const char* pszIC = CSLFetchNameValue( papszOptions, "IC" ); + GUInt32 nIMDATOFF = 0; + if (EQUAL(pszIC, "M3")) + { + GUInt32 nIMDATOFF_MSB; + GUInt16 nBMRLNTH, nTMRLNTH, nTPXCDLNTH; + + /* Prepare the block map */ +#define BLOCKMAP_HEADER_SIZE (4 + 2 + 2 + 2) + nIMDATOFF_MSB = nIMDATOFF = BLOCKMAP_HEADER_SIZE + nNBPC * nNBPR * 4; + nBMRLNTH = 4; + nTMRLNTH = 0; + nTPXCDLNTH = 0; + + CPL_MSBPTR32( &nIMDATOFF_MSB ); + CPL_MSBPTR16( &nBMRLNTH ); + CPL_MSBPTR16( &nTMRLNTH ); + CPL_MSBPTR16( &nTPXCDLNTH ); + + VSIFWriteL( &nIMDATOFF_MSB, 1, 4, fp ); + VSIFWriteL( &nBMRLNTH, 1, 2, fp ); + VSIFWriteL( &nTMRLNTH, 1, 2, fp ); + VSIFWriteL( &nTPXCDLNTH, 1, 2, fp ); + + /* Reserve space for the table itself */ + VSIFSeekL( fp, nNBPC * nNBPR * 4, SEEK_CUR ); + } + +/* -------------------------------------------------------------------- */ +/* Copy each block */ +/* -------------------------------------------------------------------- */ + int nBlockXOff, nBlockYOff; + for(nBlockYOff=0;nBlockYOff" +" " +#ifdef JPEG_SUPPORTED +" " +" " +" " +"

    OGDI -- OGDI Bridge

    + +Note : From GDAL >= 1.5.0, there should be little reason to use the +OGDI raster bridge, as ADRG, +DTED and RPF +(CADRG/CIB) formats are natively supported by GDAL.

    + +OGDI raster data sources are supported by GDAL for reading. Both Matrix +and Image families should be fully supported, as well as reading of colormap +and projection metadata. The GDAL reader is intended +to be used with OGDI 3.1 drivers, but OGDI 3.0 drivers should also work.

    + +OGDI datasets are opened within GDAL by selecting the GLTP url. For +instance, gltp://gdal.velocet.ca/adrg/usr4/mpp1/adrg/TPSUS0101 would open +the ADRG dataset stored at /usr4/mpp1/adrg/TPSUS0101 on the machine +gdal.velocet.ca (assuming it has an OGDI server running) using the 'adrg' +driver. This default access to a whole datastore will attempt to represent +all layers (and all supported family types) as bands all at the resolution +and region reported by the datastore when initially accessed.

    + +It is also possible to select a particular layer and access family +from an OGDI datastore by indicating the layer name family in the name. +The GDAL dataset name +gltp:/adrg/usr4/mpp1/adrg/TPUS0101:"TPUS0102.IMG":Matrix would select +the layer named TPUS0102.IMG from the dataset /usr4/mpp1/adrg/TPUS0101 +on the local system using the ADRG driver, and access the Matrix family. +When a specific layer is accessed in this manner GDAL will attempt to determine +the region and resolution from the OGDI 3.1 capabilities document. Note that +OGDI 3.0 datastores must have the layer and family specified in the +dataset name since they cannot be determined automatically.

    + +

    +eg.
    +  gltp://gdal.velocet.ca/adrg/usr4/mpp1/adrg/TPUS0101
    +  gltp:/adrg/usr4/mpp1/adrg/TPUS0101
    +  gltp:/adrg/usr4/mpp1/adrg/TPUS0101:"TPUS0102.IMG":Matrix
    +
    + +OGDI Matrix family layers (pseudocolored integer layers) are represented +as a single band of raster data with a color table. Though the Matrix layers +contain 32bit integer values, they are represented through GDAL as eight +layers. All values over 255 are truncated to 255, and only 256 colormap +entries are captured. While this works well for most Matrix layers, it is +anticipated that at some point in the future Matrix layers with a larger +dynamic range will be represented as other data types.

    + +OGDI Image family layers may internally have a type of RGB (1) which is +represented as three 8bit bands in GDAL, or Byte (2), UInt16 (3), Int16 (4) +or Int32 (5). There is no support for floating points bands in OGDI 3.1.

    + +The GDAL OGDI driver will represent OGDI datasources as having arbitrary +overviews. Any GDAL raster read requests at a reduced resolution will be +passed on to the OGDI driver at that reduced resolution; potentially allowing +efficient reading of overview information from OGDI datastores.

    + +If an OGDI datastore is opened without selecting a layer name in the dataset +name, and if the datastore has OGDI 3.1 style capabilities, the list of layers +will be made available as SUBDATASETS metadata. For instance, the +gdalinfo command might report the following. This information can be +used to establish available layers for direct access.

    + +

    +Subdatasets:
    +  SUBDATASET_1_NAME=gltp:/adrg/usr4/mpp1/adrg/TPUS0101:"TPUS0101.IMG":Matrix
    +  SUBDATASET_1_DESC=TPUS0101.IMG as Matrix
    +  SUBDATASET_2_NAME=gltp:/adrg/usr4/mpp1/adrg/TPUS0101:"TPUS0102.IMG":Matrix
    +  SUBDATASET_2_DESC=TPUS0102.IMG as Matrix
    +  SUBDATASET_3_NAME=gltp:/adrg/usr4/mpp1/adrg/TPUS0101:"TPUS0101.IMG":Image
    +  SUBDATASET_3_DESC=TPUS0101.IMG as Image
    +  SUBDATASET_4_NAME=gltp:/adrg/usr4/mpp1/adrg/TPUS0101:"TPUS0102.IMG":Image
    +  SUBDATASET_4_DESC=TPUS0102.IMG as Image
    +
    + +See Also:

    + +

    + + + diff --git a/bazaar/plugin/gdal/frmts/ogdi/makefile.vc b/bazaar/plugin/gdal/frmts/ogdi/makefile.vc new file mode 100644 index 000000000..bc3d909c3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ogdi/makefile.vc @@ -0,0 +1,16 @@ + +OBJ = ogdidataset.obj + +GDAL_ROOT = ..\.. + +EXTRAFLAGS = -I$(OGDI_INCLUDE) -I$(OGDIDIR)/include/win32 \ + -I$(OGDIDIR)/proj -DWIN32 -D_WINDOWS + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/ogdi/ogdidataset.cpp b/bazaar/plugin/gdal/frmts/ogdi/ogdidataset.cpp new file mode 100644 index 000000000..ae0590f1a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ogdi/ogdidataset.cpp @@ -0,0 +1,981 @@ +/****************************************************************************** + * $Id: ogdidataset.cpp 28216 2014-12-25 18:02:53Z goatbar $ + * + * Name: ogdidataset.cpp + * Project: OGDI Bridge + * Purpose: Main driver for OGDI. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ecs.h" +#include "gdal_priv.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: ogdidataset.cpp 28216 2014-12-25 18:02:53Z goatbar $"); + +CPL_C_START +void GDALRegister_OGDI(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* OGDIDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class OGDIRasterBand; + +class CPL_DLL OGDIDataset : public GDALDataset +{ + friend class OGDIRasterBand; + + int nClientID; + + ecs_Region sGlobalBounds; + ecs_Region sCurrentBounds; + int nCurrentBand; + int nCurrentIndex; + + char *pszProjection; + + static CPLErr CollectLayers(int, char***,char***); + static CPLErr OverrideGlobalInfo(OGDIDataset*,const char *); + + void AddSubDataset( const char *pszType, const char *pszLayer ); + char **papszSubDatasets; + + public: + OGDIDataset(); + ~OGDIDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + + int GetClientID() { return nClientID; } + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * ); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain = "" ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* OGDIRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class OGDIRasterBand : public GDALRasterBand +{ + friend class OGDIDataset; + + int nOGDIImageType; /* ie. 1 for RGB */ + + char *pszLayerName; + ecs_Family eFamily; + + int nComponent; /* varies only for RGB layers */ + + GDALColorTable *poCT; + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + CPLErr EstablishAccess( int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize ); + + public: + + OGDIRasterBand( OGDIDataset *, int, const char *, + ecs_Family, int ); + ~OGDIRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual int HasArbitraryOverviews(); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ); + +}; + +/************************************************************************/ +/* OGDIRasterBand() */ +/************************************************************************/ + +OGDIRasterBand::OGDIRasterBand( OGDIDataset *poDS, int nBand, + const char * pszName, ecs_Family eFamily, + int nComponent ) + +{ + ecs_Result *psResult; + + this->poDS = poDS; + this->nBand = nBand; + this->eFamily = eFamily; + this->pszLayerName = CPLStrdup(pszName); + this->nComponent = nComponent; + poCT = NULL; + +/* -------------------------------------------------------------------- */ +/* Make this layer current. */ +/* -------------------------------------------------------------------- */ + EstablishAccess( 0, 0, + poDS->GetRasterXSize(), poDS->GetRasterYSize(), + poDS->GetRasterXSize(), poDS->GetRasterYSize() ); + +/* -------------------------------------------------------------------- */ +/* Get the raster info. */ +/* -------------------------------------------------------------------- */ + psResult = cln_GetRasterInfo( poDS->nClientID ); + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + } + +/* -------------------------------------------------------------------- */ +/* Establish if we have meaningful colortable information. */ +/* -------------------------------------------------------------------- */ + if( eFamily == Matrix ) + { + int i; + + poCT = new GDALColorTable(); + + for( i = 0; i < (int) ECSRASTERINFO(psResult).cat.cat_len; i++ ) { + GDALColorEntry sEntry; + + sEntry.c1 = ECSRASTERINFO(psResult).cat.cat_val[i].r; + sEntry.c2 = ECSRASTERINFO(psResult).cat.cat_val[i].g; + sEntry.c3 = ECSRASTERINFO(psResult).cat.cat_val[i].b; + sEntry.c4 = 255; + + poCT->SetColorEntry( ECSRASTERINFO(psResult).cat.cat_val[i].no_cat, + &sEntry ); + } + } + +/* -------------------------------------------------------------------- */ +/* Get the GDAL data type. Eventually we might use the */ +/* category info to establish what to do here. */ +/* -------------------------------------------------------------------- */ + if( eFamily == Matrix ) + eDataType = GDT_Byte; + else if( ECSRASTERINFO(psResult).width == 1 ) + eDataType = GDT_Byte; + else if( ECSRASTERINFO(psResult).width == 2 ) + eDataType = GDT_Byte; + else if( ECSRASTERINFO(psResult).width == 3 ) + eDataType = GDT_UInt16; + else if( ECSRASTERINFO(psResult).width == 4 ) + eDataType = GDT_Int16; + else if( ECSRASTERINFO(psResult).width == 5 ) + eDataType = GDT_Int32; + else + eDataType = GDT_UInt32; + + nOGDIImageType = ECSRASTERINFO(psResult).width; + +/* -------------------------------------------------------------------- */ +/* Currently only works for strips */ +/* -------------------------------------------------------------------- */ + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* ~OGDIRasterBand() */ +/************************************************************************/ + +OGDIRasterBand::~OGDIRasterBand() + +{ + FlushCache(); + CPLFree( pszLayerName ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr OGDIRasterBand::IReadBlock( int, int nBlockYOff, void * pImage ) + +{ + GDALRasterIOExtraArg sExtraArg; + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + + return IRasterIO( GF_Read, 0, nBlockYOff, nBlockXSize, 1, + pImage, nBlockXSize, 1, eDataType, + GDALGetDataTypeSize(eDataType)/8, 0, &sExtraArg ); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr OGDIRasterBand::IRasterIO( CPL_UNUSED GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + CPL_UNUSED GDALRasterIOExtraArg* psExtraArg ) +{ + OGDIDataset *poODS = (OGDIDataset *) poDS; + CPLErr eErr; +#ifdef notdef + CPLDebug( "OGDIRasterBand", + "RasterIO(%d,%d,%d,%d -> %dx%d)", + nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize ); +#endif + +/* -------------------------------------------------------------------- */ +/* Establish access at the desired resolution. */ +/* -------------------------------------------------------------------- */ + eErr = EstablishAccess( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize ); + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Read back one scanline at a time, till request is satisfied. */ +/* -------------------------------------------------------------------- */ + int iScanline; + + for( iScanline = 0; iScanline < nBufYSize; iScanline++ ) + { + ecs_Result *psResult; + void *pLineData; + pLineData = ((unsigned char *) pData) + iScanline * nLineSpace; + + poODS->nCurrentIndex++; + psResult = cln_GetNextObject( poODS->nClientID ); + + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + return( CE_Failure ); + } + + if( eFamily == Matrix ) + { + GDALCopyWords( ECSRASTER(psResult), GDT_UInt32, 4, + pLineData, eBufType, nPixelSpace, + nBufXSize ); + } + else if( nOGDIImageType == 1 ) + { + GDALCopyWords( ((GByte *) ECSRASTER(psResult)) + nComponent, + GDT_Byte, 4, + pLineData, eBufType, nPixelSpace, nBufXSize ); + + if( nComponent == 3 ) + { + int i; + + for( i = 0; i < nBufXSize; i++ ) + { + if( ((GByte *) pLineData)[i] != 0 ) + ((GByte *) pLineData)[i] = 255; + else + ((GByte *) pLineData)[i] = 0; + + } + } + } + else if( nOGDIImageType == 2 ) + { + GDALCopyWords( ECSRASTER(psResult), GDT_Byte, 1, + pLineData, eBufType, nPixelSpace, + nBufXSize ); + } + else if( nOGDIImageType == 3 ) + { + GDALCopyWords( ECSRASTER(psResult), GDT_UInt16, 2, + pLineData, eBufType, nPixelSpace, + nBufXSize ); + } + else if( nOGDIImageType == 4 ) + { + GDALCopyWords( ECSRASTER(psResult), GDT_Int16, 2, + pLineData, eBufType, nPixelSpace, + nBufXSize ); + } + else if( nOGDIImageType == 5 ) + { + GDALCopyWords( ECSRASTER(psResult), GDT_Int32, 4, + pLineData, eBufType, nPixelSpace, + nBufXSize ); + } + } + + return CE_None; +} + +/************************************************************************/ +/* HasArbitraryOverviews() */ +/************************************************************************/ + +int OGDIRasterBand::HasArbitraryOverviews() + +{ + return TRUE; +} + +/************************************************************************/ +/* EstablishAccess() */ +/************************************************************************/ + +CPLErr OGDIRasterBand::EstablishAccess( int nXOff, int nYOff, + int nWinXSize, int nWinYSize, + int nBufXSize, int nBufYSize ) + +{ + ecs_Result *psResult; + OGDIDataset *poODS = (OGDIDataset *) poDS; + +/* -------------------------------------------------------------------- */ +/* Is this already the current band? If not, make it so now. */ +/* -------------------------------------------------------------------- */ + if( poODS->nCurrentBand != nBand ) + { + ecs_LayerSelection sSelection; + + sSelection.Select = pszLayerName; + sSelection.F = eFamily; + + CPLDebug( "OGDIRasterBand", "", + pszLayerName ); + psResult = cln_SelectLayer( poODS->nClientID, &sSelection ); + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + return CE_Failure; + } + + poODS->nCurrentBand = nBand; + poODS->nCurrentIndex = -1; + } + +/* -------------------------------------------------------------------- */ +/* What region would represent this resolution and window? */ +/* -------------------------------------------------------------------- */ + ecs_Region sWin; + double dfNSTolerance = 0.0000001; + + sWin.west = nXOff * poODS->sGlobalBounds.ew_res + + poODS->sGlobalBounds.west; + sWin.east = (nXOff+nWinXSize) * poODS->sGlobalBounds.ew_res + + poODS->sGlobalBounds.west; + sWin.ew_res = poODS->sGlobalBounds.ew_res*(nWinXSize/(double)nBufXSize); + + sWin.north = poODS->sGlobalBounds.north + - nYOff*poODS->sGlobalBounds.ns_res; + if( nBufYSize == 1 && nWinYSize == 1 ) + { + sWin.ns_res = sWin.ew_res + * (poODS->sGlobalBounds.ns_res / poODS->sGlobalBounds.ew_res); + nWinYSize = (int) ((sWin.north - poODS->sGlobalBounds.south + sWin.ns_res*0.9) + / sWin.ns_res); + + sWin.south = sWin.north - nWinYSize * sWin.ns_res; + dfNSTolerance = MAX(poODS->sCurrentBounds.ns_res,sWin.ns_res); + } + else if( nBufYSize == 1 ) + { + sWin.ns_res = poODS->sGlobalBounds.ns_res + *(nWinYSize/(double)nBufYSize); + nWinYSize = (int) ((sWin.north - poODS->sGlobalBounds.south + sWin.ns_res*0.9) + / sWin.ns_res); + + sWin.south = sWin.north - nWinYSize * sWin.ns_res; + dfNSTolerance = MAX(poODS->sCurrentBounds.ns_res,sWin.ns_res); + } + else + { + sWin.ns_res = poODS->sGlobalBounds.ns_res + *(nWinYSize/(double)nBufYSize); + sWin.south = sWin.north - nWinYSize * sWin.ns_res; + dfNSTolerance = sWin.ns_res * 0.001; + } + + if( poODS->nCurrentIndex != 0 + || ABS(sWin.west - poODS->sCurrentBounds.west) > 0.0001 + || ABS(sWin.east - poODS->sCurrentBounds.east) > 0.0001 + || ABS(sWin.north - (poODS->sCurrentBounds.north - poODS->nCurrentIndex * poODS->sCurrentBounds.ns_res)) > dfNSTolerance + || ABS(sWin.ew_res/poODS->sCurrentBounds.ew_res - 1.0) > 0.0001 + || ABS(sWin.ns_res - poODS->sCurrentBounds.ns_res) > dfNSTolerance ) + { + CPLDebug( "OGDIRasterBand", + "", + nXOff, nYOff, nWinXSize, nWinYSize, nBufXSize, nBufYSize ); + + psResult = cln_SelectRegion( poODS->nClientID, &sWin ); + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + return CE_Failure; + } + + poODS->sCurrentBounds = sWin; + poODS->nCurrentIndex = 0; + } + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp OGDIRasterBand::GetColorInterpretation() + +{ + if( poCT != NULL ) + return GCI_PaletteIndex; + else if( nOGDIImageType == 1 && eFamily == Image && nComponent == 0 ) + return GCI_RedBand; + else if( nOGDIImageType == 1 && eFamily == Image && nComponent == 1 ) + return GCI_GreenBand; + else if( nOGDIImageType == 1 && eFamily == Image && nComponent == 2 ) + return GCI_BlueBand; + else if( nOGDIImageType == 1 && eFamily == Image && nComponent == 3 ) + return GCI_AlphaBand; + else + return GCI_Undefined; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *OGDIRasterBand::GetColorTable() + +{ + return poCT; +} + +/************************************************************************/ +/* AdviseRead() */ +/* */ +/* Allow the application to give us a hint in advance how they */ +/* want the data. */ +/************************************************************************/ + +CPLErr OGDIRasterBand::AdviseRead( int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + CPL_UNUSED GDALDataType eDT, + CPL_UNUSED char **papszOptions ) + +{ + return EstablishAccess( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize ); +} + +/************************************************************************/ +/* ==================================================================== */ +/* OGDIDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* OGDIDataset() */ +/************************************************************************/ + +OGDIDataset::OGDIDataset() + +{ + nClientID = -1; + nCurrentBand = -1; + nCurrentIndex = -1; + papszSubDatasets = NULL; +} + +/************************************************************************/ +/* ~OGDIDataset() */ +/************************************************************************/ + +OGDIDataset::~OGDIDataset() + +{ + cln_DestroyClient( nClientID ); + CSLDestroy( papszSubDatasets ); + CPLFree( pszProjection ); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **OGDIDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), + TRUE, + "SUBDATASETS", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **OGDIDataset::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain != NULL && EQUAL(pszDomain,"SUBDATASETS") ) + return papszSubDatasets; + else + return GDALDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *OGDIDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + ecs_Result *psResult; + int nClientID; + char **papszImages=NULL, **papszMatrices=NULL; + + if( !EQUALN(poOpenInfo->pszFilename,"gltp:",5) ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The OGDI driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Has the user hardcoded a layer and family in the URL? */ +/* Honour quoted strings for the layer name, since some layers */ +/* (ie. RPF/CADRG) have embedded colons. */ +/* -------------------------------------------------------------------- */ + int nC1=-1, nC2=-1, i, bInQuotes = FALSE; + char *pszURL = CPLStrdup(poOpenInfo->pszFilename); + + for( i = strlen(pszURL)-1; i > 0; i-- ) + { + if( pszURL[i] == '/' ) + break; + + if( pszURL[i] == '"' && pszURL[i-1] != '\\' ) + bInQuotes = !bInQuotes; + + else if( pszURL[i] == ':' && !bInQuotes ) + { + if( nC1 == -1 ) + { + nC1 = i; + pszURL[nC1] = '\0'; + } + else if( nC2 == -1 ) + { + nC2 = i; + pszURL[nC2] = '\0'; + } + } + } + +/* -------------------------------------------------------------------- */ +/* If we got a "family", and it is a vector family then return */ +/* quietly. */ +/* -------------------------------------------------------------------- */ + if( nC2 != -1 + && !EQUAL(pszURL+nC1+1,"Matrix") + && !EQUAL(pszURL+nC1+1,"Image") ) + { + CPLFree( pszURL ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open the client interface. */ +/* -------------------------------------------------------------------- */ + psResult = cln_CreateClient( &nClientID, pszURL ); + + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + CPLFree(pszURL); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* collect the list of images and matrices available. */ +/* -------------------------------------------------------------------- */ + if( nC2 == -1 ) + { + CollectLayers( nClientID, &papszImages, &papszMatrices ); + } + else + { + char *pszLayerName = CPLStrdup( pszURL+nC2+1 ); + + if( pszLayerName[0] == '"' ) + { + int nOut = 0; + + for( i = 1; pszLayerName[i] != '\0'; i++ ) + { + if( pszLayerName[i+1] == '"' && pszLayerName[i] == '\\' ) + pszLayerName[nOut++] = pszLayerName[++i]; + else if( pszLayerName[i] != '"' ) + pszLayerName[nOut++] = pszLayerName[i]; + else + break; + } + pszLayerName[nOut] = '\0'; + } + + if( EQUAL(pszURL+nC1+1,"Image") ) + papszImages = CSLAddString( papszImages, pszLayerName ); + else + papszMatrices = CSLAddString( papszMatrices, pszLayerName ); + + CPLFree( pszLayerName ); + } + + CPLFree( pszURL ); + +/* -------------------------------------------------------------------- */ +/* If this is a 3.1 server (ie, it support */ +/* cln_GetLayerCapabilities()) and it has no raster layers then */ +/* we can assume it must be a vector datastore. End without an */ +/* error in case the application wants to try this through */ +/* OGR. */ +/* -------------------------------------------------------------------- */ + psResult = cln_GetVersion(nClientID); + + if( (ECSERROR(psResult) || CPLAtof(ECSTEXT(psResult)) >= 3.1) + && CSLCount(papszMatrices) == 0 + && CSLCount(papszImages) == 0 ) + { + CPLDebug( "OGDIDataset", + "While this is an OGDI datastore, it does not appear to\n" + "have any identifiable raster layers. Perhaps it is a\n" + "vector datastore?" ); + cln_DestroyClient( nClientID ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + OGDIDataset *poDS; + + poDS = new OGDIDataset(); + + poDS->nClientID = nClientID; + poDS->SetDescription( poOpenInfo->pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + psResult = cln_GetGlobalBound( nClientID ); + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + return NULL; + } + + poDS->sGlobalBounds = ECSREGION(psResult); + + psResult = cln_GetServerProjection(nClientID); + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + return NULL; + } + + OGRSpatialReference oSRS; + + if( oSRS.importFromProj4( ECSTEXT(psResult) ) == OGRERR_NONE ) + { + poDS->pszProjection = NULL; + oSRS.exportToWkt( &(poDS->pszProjection) ); + } + else + { + CPLError( CE_Warning, CPLE_NotSupported, + "untranslatable PROJ.4 projection: %s\n", + ECSTEXT(psResult) ); + poDS->pszProjection = CPLStrdup(""); + } + +/* -------------------------------------------------------------------- */ +/* Select the global region. */ +/* -------------------------------------------------------------------- */ + psResult = cln_SelectRegion( nClientID, &(poDS->sGlobalBounds) ); + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + return NULL; + } + + poDS->sCurrentBounds = poDS->sGlobalBounds; + +/* -------------------------------------------------------------------- */ +/* If we have only one layer try to find the corresponding */ +/* capabilities, and override the global bounds and resolution */ +/* based on it. */ +/* -------------------------------------------------------------------- */ + if( CSLCount(papszMatrices) + CSLCount(papszImages) == 1 ) + { + if( CSLCount(papszMatrices) == 1 ) + OverrideGlobalInfo( poDS, papszMatrices[0] ); + else + OverrideGlobalInfo( poDS, papszImages[0] ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise setup a subdataset list. */ +/* -------------------------------------------------------------------- */ + else + { + int i; + + for( i = 0; papszMatrices != NULL && papszMatrices[i] != NULL; i++ ) + poDS->AddSubDataset( "Matrix", papszMatrices[i] ); + + for( i = 0; papszImages != NULL && papszImages[i] != NULL; i++ ) + poDS->AddSubDataset( "Image", papszImages[i] ); + } + +/* -------------------------------------------------------------------- */ +/* Establish raster info. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = (int) + (((poDS->sGlobalBounds.east - poDS->sGlobalBounds.west) + / poDS->sGlobalBounds.ew_res) + 0.5); + + poDS->nRasterYSize = (int) + (((poDS->sGlobalBounds.north - poDS->sGlobalBounds.south) + / poDS->sGlobalBounds.ns_res) + 0.5); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( i=0; papszMatrices != NULL && papszMatrices[i] != NULL; i++) + { + if( CSLFindString( papszImages, papszMatrices[i] ) == -1 ) + poDS->SetBand( poDS->GetRasterCount()+1, + new OGDIRasterBand( poDS, poDS->GetRasterCount()+1, + papszMatrices[i], Matrix, 0 ) ); + } + + for( i=0; papszImages != NULL && papszImages[i] != NULL; i++) + { + OGDIRasterBand *poBand; + + poBand = new OGDIRasterBand( poDS, poDS->GetRasterCount()+1, + papszImages[i], Image, 0 ); + + poDS->SetBand( poDS->GetRasterCount()+1, poBand ); + + /* special case for RGBt Layers */ + if( poBand->nOGDIImageType == 1 ) + { + poDS->SetBand( poDS->GetRasterCount()+1, + new OGDIRasterBand( poDS, poDS->GetRasterCount()+1, + papszImages[i], Image, 1 )); + poDS->SetBand( poDS->GetRasterCount()+1, + new OGDIRasterBand( poDS, poDS->GetRasterCount()+1, + papszImages[i], Image, 2 )); + poDS->SetBand( poDS->GetRasterCount()+1, + new OGDIRasterBand( poDS, poDS->GetRasterCount()+1, + papszImages[i], Image, 3 )); + } + } + + CSLDestroy( papszMatrices ); + CSLDestroy( papszImages ); + + return( poDS ); +} + +/************************************************************************/ +/* AddSubDataset() */ +/************************************************************************/ + +void OGDIDataset::AddSubDataset( const char *pszType, const char *pszLayer ) + +{ + char szName[80]; + int nCount = CSLCount( papszSubDatasets ) / 2; + + sprintf( szName, "SUBDATASET_%d_NAME", nCount+1 ); + papszSubDatasets = + CSLSetNameValue( papszSubDatasets, szName, + CPLSPrintf( "%s:\"%s\":%s", GetDescription(), pszLayer, pszType ) ); + + sprintf( szName, "SUBDATASET_%d_DESC", nCount+1 ); + papszSubDatasets = + CSLSetNameValue( papszSubDatasets, szName, + CPLSPrintf( "%s as %s", pszLayer, pszType ) ); +} + +/************************************************************************/ +/* CollectLayers() */ +/************************************************************************/ + +CPLErr OGDIDataset::CollectLayers( int nClientID, + char ***ppapszImages, + char ***ppapszMatrices ) + +{ + const ecs_LayerCapabilities *psLayer; + int iLayer; + + for( iLayer = 0; + (psLayer = cln_GetLayerCapabilities(nClientID,iLayer)) != NULL; + iLayer++ ) + { + if( psLayer->families[Image] ) + { + *ppapszImages = CSLAddString( *ppapszImages, psLayer->name ); + } + if( psLayer->families[Matrix] ) + { + *ppapszMatrices = CSLAddString( *ppapszMatrices, psLayer->name ); + } + } + + return CE_None; +} + +/************************************************************************/ +/* OverrideGlobalInfo() */ +/* */ +/* Override the global bounds and resolution based on a layers */ +/* capabilities, if possible. */ +/************************************************************************/ + +CPLErr OGDIDataset::OverrideGlobalInfo( OGDIDataset *poDS, + const char *pszLayer ) + +{ + const ecs_LayerCapabilities *psLayer; + int iLayer; + + for( iLayer = 0; + (psLayer = cln_GetLayerCapabilities(poDS->nClientID,iLayer)) != NULL; + iLayer++ ) + { + if( EQUAL(psLayer->name, pszLayer) ) + { + poDS->sGlobalBounds.north = psLayer->srs_north; + poDS->sGlobalBounds.south = psLayer->srs_south; + poDS->sGlobalBounds.east = psLayer->srs_east; + poDS->sGlobalBounds.west = psLayer->srs_west; + poDS->sGlobalBounds.ew_res = psLayer->srs_ewres; + poDS->sGlobalBounds.ns_res = psLayer->srs_nsres; + } + } + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *OGDIDataset::GetProjectionRef() + +{ + return( pszProjection ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr OGDIDataset::GetGeoTransform( double * padfTransform ) + +{ + padfTransform[0] = sGlobalBounds.west; + padfTransform[1] = sGlobalBounds.ew_res; + padfTransform[2] = 0.0; + + padfTransform[3] = sGlobalBounds.north; + padfTransform[4] = 0.0; + padfTransform[5] = -sGlobalBounds.ns_res; + + return( CE_None ); +} + +/************************************************************************/ +/* GDALRegister_OGDI() */ +/************************************************************************/ + +void GDALRegister_OGDI() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("GDAL/OGDI driver")) + return; + + if( GDALGetDriverByName( "OGDI" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "OGDI" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "OGDI Bridge" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_ogdi.html" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + + poDriver->pfnOpen = OGDIDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/openjpeg/GNUmakefile b/bazaar/plugin/gdal/frmts/openjpeg/GNUmakefile new file mode 100644 index 000000000..76f7e262d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/openjpeg/GNUmakefile @@ -0,0 +1,20 @@ + +include ../../GDALmake.opt + +OBJ = openjpegdataset.o + + + +ifneq ($(OPENJPEG_VERSION),) +CPPFLAGS := $(CPPFLAGS) -DOPENJPEG_VERSION=$(OPENJPEG_VERSION) +endif + +CPPFLAGS := -I.. $(CPPFLAGS) + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f $(OBJ) $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/openjpeg/eoptemplate_pleiades.xml b/bazaar/plugin/gdal/frmts/openjpeg/eoptemplate_pleiades.xml new file mode 100644 index 000000000..75ce8c97c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/openjpeg/eoptemplate_pleiades.xml @@ -0,0 +1,46 @@ + + + + {{{XPATH(//PRODUCTION_DATE/text())}}} + + + + + + + + {{{XPATH(//PRODUCT_CODE/text())}}} + + + + + {{{XPATH(//PRODUCT_CODE/text())}}} + + + + + {{{XPATH(//DATASET_TYPE/text())}}} + {{{XPATH(if(//PAN_RESTORATION/text() = 'true','P',if(//MS_RESTORATION/text() = 'true','Multi','Unknown')))}}} + {{{XPATH(//RESAMPLING_SPACING/text())}}} + + + + + + + + + + {{{XPATH(uuid())}}} + NOMINAL + ACQUIRED + + + + diff --git a/bazaar/plugin/gdal/frmts/openjpeg/eoptemplate_worldviewgeoeye.xml b/bazaar/plugin/gdal/frmts/openjpeg/eoptemplate_worldviewgeoeye.xml new file mode 100644 index 000000000..19ecfc2a1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/openjpeg/eoptemplate_worldviewgeoeye.xml @@ -0,0 +1,46 @@ + + + + {{{XPATH(//GENERATIONTIME/text())}}} + + + + + + + + {{{XPATH(//SATID/text())}}} + + + + + {{{XPATH(//SATID/text())}}} + + + + + OPTICAL + {{{XPATH((//BANDID/text())[1])}}} + {{{XPATH(//PRODUCTGSD/text())}}} + + + + + + + + + + {{{XPATH(uuid())}}} + NOMINAL + ACQUIRED + + + + diff --git a/bazaar/plugin/gdal/frmts/openjpeg/frmt_jp2openjpeg.html b/bazaar/plugin/gdal/frmts/openjpeg/frmt_jp2openjpeg.html new file mode 100644 index 000000000..d90c431d7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/openjpeg/frmt_jp2openjpeg.html @@ -0,0 +1,434 @@ + + +JP2OpenJPEG --- JPEG2000 driver based on OpenJPEG library + + + + +

    JP2OpenJPEG --- JPEG2000 driver based on OpenJPEG library

    + +

    (GDAL >= 1.8.0)

    + +

    This driver is an implementation of a JPEG2000 reader/writer based on OpenJPEG library v2.

    + +

    For GDAL 1.10 or later, use openjpeg 2.X.

    +

    For GDAL 1.9.x or before, use the v2 branch from its Subversion repository : http://openjpeg.googlecode.com/svn/branches/v2 (before r2230 when it was deleted).

    + +

    The driver uses the VSI Virtual File API, so it can read JPEG2000 compressed NITF files.

    + +

    Starting with GDAL 1.9.0, XMP metadata can be extracted from JPEG2000 files, and will be +stored as XML raw content in the xml:XMP metadata domain.

    + +

    Starting with GDAL 1.10, the driver supports writing georeferencing information as GeoJP2 and GMLJP2 boxes.

    + +

    Starting with GDAL 2.0, the driver supports creating files with transparency, +arbitrary band count, and adding/reading metadata. Update of georeferencing or +metadata of existing file is also supported. Optional intellectual property +metdata can be read/written in the xml:IPR box.

    + +

    Option Options

    + +(GDAL >= 2.0 ) + +The following open option is available: +
      +
    • 1BIT_ALPHA_PROMOTION=YES/NO: Whether a 1-bit alpha channel should be promoted to 8-bit. +Defaults to YES.

    • +
    + +

    Creation Options

    + +
      +
    • CODEC=JP2/J2K : JP2 will add JP2 boxes around the codestream data. +The value is determined automatically from the file extension. If it's neither +JP2 nor J2K, J2K codec is used.

    • + +
    • GMLJP2=YES/NO: (Starting with GDAL 1.10) Indicates whether a GML box + conforming to the OGC GML in JPEG2000 specification should be included in the +file. Unless GMLJP2V2_DEF is used, the version of the GMLJP2 box will be +version 1. Defaults to YES.

      + +

    • GMLJP2V2_DEF=filename: (Starting with GDAL 2.0) Indicates whether a GML box +conforming to the +OGC GML in JPEG2000, version 2 specification should be included in the +file. filename must point to a file with a JSon content that defines how +the GMLJP2 v2 box should be built. See below section for the syntax of the JSon configuration file. +It is also possible to directly pass the JSon content inlined as a string. +If filename is just set to YES, a minimal instance will be built.

      + +

    • GeoJP2=YES/NO: (Starting with GDAL 1.10) Indicates whether a +UUID/GeoTIFF box conforming to the GeoJP2 (GeoTIFF in JPEG2000) specification +should be included in the file. Defaults to YES.

      + +

    • QUALITY=float_value,float_value,... : Percentage between 0 and 100. +A value of 50 means the file will be half-size in comparison to uncompressed data, +33 means 1/3, etc.. Defaults to 25 (unless the dataset is made of a single band +with color table, in which case the default quality is 100). +Starting with GDAL 2.0, it is possible to specify several quality values +(comma separated) to ask for several quality layers. Quality values should be +increasing. +

    • + +
    • REVERSIBLE=YES/NO : YES means use of reversible 5x3 integer-only +filter, NO use of the irreversible DWT 9-7. Defaults to NO (unless the dataset +is made of a single band with color table, in which case reversible filter is +used).

    • + +
    • RESOLUTIONS=int_value : Number of resolution levels. Default value +is selected such the smallest overview of a tile is no bigger than 128x128.

    • + +
    • BLOCKXSIZE=int_value : Tile width. Defaults to 1024.

    • + +
    • BLOCKYSIZE=int_value : Tile height. Defaults to 1024.

    • + +
    • PROGRESSION=LRCP/RLCP/RPCL/PCRL/CPRL : Progession order. Defaults +to LRCP.

    • + +
    • SOP=YES/NO : YES means generate SOP (Start Of Packet) marker +segments. Defaults to NO.

    • + +
    • EPH=YES/NO : YES means generate EPH (End of Packet Header) marker +segments. Defaults to NO.

    • + +
    • YCBCR420=YES/NO : (GDAL >= 1.11) YES if RGB must be resampled +to YCbCr 4:2:0. Defaults to NO.

    • + +
    • YCC=YES/NO : (GDAL >= 2.0) YES if RGB must be transformed to +YCC color space ("MCT transform", i.e. internal transform, without visual +degration). Defaults to YES.

    • + +
    • NBITS=int_value : (GDAL >= 2.0) Bits (precision) for sub-byte +files (1-7), sub-uint16 (9-15), sub-uint32 (17-31).

    • + +
    • 1BIT_ALPHA=YES/NO: (GDAL >= 2.0) Whether to encode the alpha +channel as a 1-bit channel (when there's an alpha channel). Defaults to NO, +unless INSPIRE_TG=YES. Enabling this option might cause compatibility problems +with some readers. At the time of writing, those based on the MrSID JPEG2000 SDK +are unable to open such files. And regarding the ECW JPEG2000 SDK, decoding of +1-bit alpha channel with lossy/irreversible compression gives visual artifacts +(OK with lossless encoding). +

    • + +
    • ALPHA=YES/NO: (GDAL >= 2.0) Whether to force encoding last +channel as alpha channel. Only useful if the color interpretation of that channel +is not already Alpha. Defaults to NO.

    • + +
    • PROFILE=AUTO/UNRESTRICTED/PROFILE_1: (GDAL >= 2.0) Determine +which codestream profile to use. UNRESTRICTED corresponds to the +"Unrestricted JPEG 2000 Part 1 codestream" (RSIZ=0). PROFILE_1 corresponds +to the "JPEG 2000 Part 1 Profile 1 codestream" (RSIZ=2), which add constraints +on tile dimensions and number of resolutions. In AUTO mode, the driver +will determine if the BLOCKXSIZE, BLOCKYSIZE, RESOLUTIONS, CODEBLOCK_WIDTH and +CODEBLOCK_HEIGHT values are compatible with PROFILE_1 and advertize it in the +relevant case. Note that the default values of those options are compatible with +PROFILE_1. Otherwise UNRESTRICTED is advertized. Defaults to AUTO.

    • + +
    • INSPIRE_TG=YES/NO: (GDAL >= 2.0) Whether to use JPEG2000 features that +comply with +Inspire Orthoimagery Technical Guidelines. Defaults to NO. +When set to YES, implies PROFILE=PROFILE_1, 1BIT_ALPHA=YES, GEOBOXES_AFTER_JP2C=YES. +The CODEC, BLOCKXSIZE, BLOCKYSIZE, RESOLUTIONS, NBITS, PROFILE, CODEBLOCK_WIDTH +and CODEBLOCK_HEIGHT options will be checked against the requirements and recommendations of the +Technical Guidelines. +

    • + +
    • JPX=YES/NO: (GDAL >= 2.0) Whether to advertize JPX features, and +add a Reader requirement box, when a GMLJP2 box is written. Defaults to YES. +This option should not be used unless compatibility problems with a reader +occur.

    • + +
    • GEOBOXES_AFTER_JP2C=YES/NO: (GDAL >= 2.0) Whether to place +GeoJP2/GMLJP2 boxes after the code-stream. Defaults to NO, unless INSPIRE_TG=YES. +This option should not be used unless compatibility problems with a reader occur. +

    • + +
    • PRECINCTS={prec_w,prec_h},{prec_w,prec_h},...: (GDAL >= 2.0) +A list of {precincts width,precincts height} tuples to specify precincts size. +Each value should be a multiple of 2. The maximum number of tuples used will be +the number of resolutions. The first tuple corresponds to the higher resolution +level, and the following ones to the lower resolution levels. +If less tuples are specified, the last one is used by +dividing its values by 2 for each extra lower resolution level. +The default value used is {512,512},{256,512},{128,512},{64,512},{32,512},{16,512},{8,512},{4,512},{2,512}. +An empty string may be used to disable precincts ( +i.e. the default {32767,32767},{32767,32767}, ... will then be used). +

    • + +
    • TILEPARTS=DISABLED/RESOLUTIONS/LAYERS/COMPONENTS: (GDAL >= 2.0) +Whether to generate tile-parts and according to which criterion. Defaults to +DISABLED. +

    • + +
    • CODEBLOCK_WIDTH=int_value: (GDAL >= 2.0) +Codeblock width: power of two value between 4 and 1024. Defaults to 64. +Note that CODEBLOCK_WIDTH * CODEBLOCK_HEIGHT must not be greater than 4096. +For PROFILE_1 compatibility, CODEBLOCK_WIDTH must not be greater than 64. +

    • + +
    • CODEBLOCK_HEIGHT=int_value: (GDAL >= 2.0) +Codeblock height: power of two value between 4 and 1024. Defaults to 64. +Note that CODEBLOCK_WIDTH * CODEBLOCK_HEIGHT must not be greater than 4096. +For PROFILE_1 compatibility, CODEBLOCK_HEIGHT must not be greater than 64. +

    • + +
    • WRITE_METADATA=YES/NO: (GDAL >= 2.0) Whether metadata should be +written, in a dedicated JP2 'xml ' box. Defaults to NO. +The content of the 'xml ' box will be like: +

      +<GDALMultiDomainMetadata>
      +  <Metadata>
      +    <MDI key="foo">bar</MDI>
      +  </Metadata>
      +  <Metadata domain='aux_domain'>
      +    <MDI key="foo">bar</MDI>
      +  </Metadata>
      +  <Metadata domain='a_xml_domain' format='xml'>
      +    <arbitrary_xml_content>
      +    </arbitrary_xml_content>
      +  </Metadata>
      +</GDALMultiDomainMetadata>
      +
      +If there are metadata domain whose name starts with "xml:BOX_", they will be +written each as separate JP2 'xml ' box.

      +If there is a metadata domain whose name is "xml:XMP", its content will be +written as a JP2 'uuid' XMP box.

      +If there is a metadata domain whose name is "xml:IPR", its content will be +written as a JP2 'jp2i' box. +

    • + +
    • MAIN_MD_DOMAIN_ONLY=YES/NO: (GDAL >= 2.0) +(Only if WRITE_METADATA=YES) Whether only metadata from the main domain should +be written. Defaults to NO. +

    • + +
    • USE_SRC_CODESTREAM=YES/NO: (GDAL >= 2.0) +(EXPERIMENTAL!) When source dataset is JPEG2000, whether to reuse the codestream +of the source dataset unmodified. Defaults to NO. Note that enabling that feature might result +in inconsistent content of the JP2 boxes w.r.t. to the content of the source codestream. +Most other creation options will be ignored in that mode. Can be useful in some +use cases when adding/correcting georeferencing, metadata, ... INSPIRE_TG and +PROFILE options will be ignored, and the profile of the codestream will be overriden +with the one specified/implied by the options (which may be inconsistent with +the characteristics of the codestream). +

    • + +
    + +

    Lossless compression

    + +Lossless compression can be achieved if ALL the following creation options are defined : +
      +
    • QUALITY=100
    • +
    • REVERSIBLE=YES
    • +
    • YCBCR420=NO (which is the default)
    • +
    + +

    GMLJP2v2 definition file

    + +A GMLJP2v2 box typically contains a GMLJP2RectifiedGridCoverage with the +SRS information and geotransformation matrix. It is also possible to add metadata, +vector features (GML feature collections), annotations (KML), styles (typically +SLD, or other XML format) or any XML content as an extension. + +The value of the GMLJP2V2_DEF creation option should be a file that conforms with +the below syntax (elements starting with "#" are documentation, and can be omitted): + +
    +{
    +    "#doc" : "Unless otherwise specified, all elements are optional",
    +
    +    "#root_instance_doc": "Describe content of the GMLJP2CoverageCollection",
    +    "root_instance": {
    +        "#gml_id_doc": "Specify GMLJP2CoverageCollection gml:id. Default is ID_GMLJP2_0",
    +        "gml_id": "some_gml_id",
    +
    +        "#grid_coverage_file_doc": [
    +            "External XML file, whose root might be a GMLJP2GridCoverage, ",
    +            "GMLJP2RectifiedGridCoverage or a GMLJP2ReferenceableGridCoverage.",
    +            "If not specified, GDAL will auto-generate a GMLJP2RectifiedGridCoverage" ],
    +        "grid_coverage_file": "gmljp2gridcoverage.xml",
    +
    +        "#crs_url_doc": [
    +            "true for http://www.opengis.net/def/crs/EPSG/0/XXXX CRS URL.",
    +            "If false, use CRS URN. Default value is true",
    +            "Only taken into account for a auto-generated GMLJP2RectifiedGridCoverage"],
    +        "crs_url": true,
    +
    +        "#metadata_doc": [ "An array of metadata items. Can be either strings, with ",
    +                           "a filename or directly inline XML content, or either ",
    +                           "a more complete description." ],
    +        "metadata": [
    +
    +            "dcmetadata.xml",
    +
    +            {
    +                "#file_doc": "Can use relative or absolute paths. Exclusive of content, gdal_metadata and generated_metadata.",
    +                "file": "dcmetadata.xml",
    +
    +                "#gdal_metadata_doc": "Whether to serialize GDAL metadata as GDALMultiDomainMetadata",
    +                "gdal_metadata": false,
    +
    +                "#dynamic_metadata_doc":
    +                    [ "The metadata file will be generated from a template and a source file.",
    +                      "The template is a valid GMLJP2 metadata XML tree with placeholders like",
    +                      "{{{XPATH(some_xpath_expression)}}}",
    +                      "that are evalated from the source XML file. Typical use case",
    +                      "is to generate a gmljp2:eopMetadata from the XML metadata",
    +                      "provided by the image provider in their own particular format." ],
    +                "dynamic_metadata" :
    +                {
    +                    "template": "my_template.xml",
    +                    "source": "my_source.xml"
    +                },
    +
    +                "#content": "Exclusive of file. Inline XML metadata content",
    +                "content": "<gmljp2:metadata>Some simple textual metadata</gmljp2:metadata>",
    +
    +                "#parent_node": ["Where to put the metadata.",
    +                                 "Under CoverageCollection (default) or GridCoverage" ],
    +                "parent_node": "CoverageCollection"
    +            }
    +        ],
    +
    +        "#annotations_doc": [ "An array of filenames, either directly KML files",
    +                              "or other vector files recognized by GDAL that ",
    +                              "will be translated on-the-fly as KML" ],
    +        "annotations": [
    +            "my.kml"
    +        ],
    +
    +        "#gml_filelist_doc" :[
    +            "An array of GML files. Can be either GML filenames, ",
    +            "or a more complete description" ],
    +        "gml_filelist": [
    +
    +            "my.gml",
    +
    +            {
    +                "#file_doc": "Can use relative or absolute paths. Exclusive of remote_resource",
    +                "file": "converted/test_0.gml",
    +
    +                "#remote_resource_doc": "URL of a feature collection that must be referenced through a xlink:href",
    +                "remote_resource": "http://svn.osgeo.org/gdal/trunk/autotest/ogr/data/expected_gml_gml32.gml",
    +
    +                "#namespace_doc": ["The namespace in schemaLocation for which to substitute",
    +                                  "its original schemaLocation with the one provided below.",
    +                                  "Ignored for a remote_resource"],
    +                "namespace": "http://example.com",
    +
    +                "#schema_location_doc": ["Value of the substitued schemaLocation. ",
    +                                         "Typically a schema box label (link)",
    +                                         "Ignored for a remote_resource"],
    +                "schema_location": "gmljp2://xml/schema_0.xsd",
    +
    +                "#inline_doc": [
    +                    "Whether to inline the content, or put it in a separate xml box. Default is true",
    +                    "Ignored for a remote_resource." ],
    +                "inline": true,
    +
    +                "#parent_node": ["Where to put the FeatureCollection.",
    +                                 "Under CoverageCollection (default) or GridCoverage" ],
    +                "parent_node": "CoverageCollection"
    +            }
    +        ],
    +
    +
    +        "#styles_doc: [ "An array of styles. For example SLD files" ],
    +        "styles" : [
    +            {
    +                "#file_doc": "Can use relative or absolute paths.",
    +                "file": "my.sld",
    +
    +                "#parent_node": ["Where to put the FeatureCollection.",
    +                                 "Under CoverageCollection (default) or GridCoverage" ],
    +                "parent_node": "CoverageCollection"
    +            }
    +        ],
    +
    +        "#extensions_doc: [ "An array of extensions." ],
    +        "extensions" : [
    +            {
    +                "#file_doc": "Can use relative or absolute paths.",
    +                "file": "my.xml",
    +
    +                "#parent_node": ["Where to put the FeatureCollection.",
    +                                 "Under CoverageCollection (default) or GridCoverage" ],
    +                "parent_node": "CoverageCollection"
    +            }
    +        ]
    +    },
    +
    +    "#boxes_doc": "An array to describe the content of XML asoc boxes",
    +    "boxes": [
    +        {
    +            "#file_doc": "can use relative or absolute paths. Required",
    +            "file": "converted/test_0.xsd",
    +
    +            "#label_doc": ["the label of the XML box. If not specified, will be the ",
    +                          "filename without the directory part." ],
    +            "label": "schema_0.xsd"
    +        }
    +    ]
    +}
    +
    + +

    +Metadata can be dynamically generated from a template file (in that context, +with a XML structure) and a XML source file. The template file is processed +by searching for patterns like {{{XPATH(xpath_expr)}}} and replacing them by their evaluation +against the content of the source file. xpath_expr must be a XPath 1.0 compatible +expression, with the addition of the following functions : + +

      +
    • if(cond_expr,expr_if_true,expr_if_false): if cond_expr evalutes to +true, returns expr_if_true. Otherwise returns expr_if_false
    • +
    • uuid(): evaluates to a random UUID
    • +
    +

    + +

    A template file to process XML metadata of Pleiades imagery can be found +here, and +a template file to process XML metadata of GeoEye/WorldView imagery can be found +here.

    + +

    Vector information

    + +Starting with GDAL 2.0, a JPEG2000 file containing a GMLJP2 v2 box with GML feature +collections and/or KML annotations embedded can be opened as a vector file with +the OGR API. + +For example: +
    +ogrinfo -ro my.jp2 
    +
    +INFO: Open of my.jp2'
    +      using driver `JP2OpenJPEG' successful.
    +1: FC_GridCoverage_1_rivers (LineString)
    +2: FC_GridCoverage_1_borders (LineString)
    +3: Annotation_1_poly
    +
    + +

    Feature collections can be linked from the GMLJP2 v2 box to a remote location. +By default, the link is not followed. It will be followed if the open option +OPEN_REMOTE_GML is set to YES.

    + +

    See Also:

    + + + +Other JPEG2000 GDAL drivers : + + + + diff --git a/bazaar/plugin/gdal/frmts/openjpeg/makefile.vc b/bazaar/plugin/gdal/frmts/openjpeg/makefile.vc new file mode 100644 index 000000000..e591b25ee --- /dev/null +++ b/bazaar/plugin/gdal/frmts/openjpeg/makefile.vc @@ -0,0 +1,19 @@ + +OBJ = openjpegdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. $(OPENJPEG_CFLAGS) $(OPENJPEG_VERSION_CFLAGS) + +!IFDEF OPENJPEG_VERSION +OPENJPEG_VERSION_CFLAGS = -DOPENJPEG_VERSION=$(OPENJPEG_VERSION) +!ENDIF + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/openjpeg/openjpegdataset.cpp b/bazaar/plugin/gdal/frmts/openjpeg/openjpegdataset.cpp new file mode 100644 index 000000000..d608b912f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/openjpeg/openjpegdataset.cpp @@ -0,0 +1,3579 @@ +/****************************************************************************** + * $Id: openjpegdataset.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: JPEG2000 driver based on OpenJPEG library + * Purpose: JPEG2000 driver based on OpenJPEG library + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2014, Even Rouault + * Copyright (c) 2015, European Union (European Environment Agency) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* This file is to be used with openjpeg 2.0 */ + +#if defined(OPENJPEG_VERSION) && OPENJPEG_VERSION >= 20100 +#include +#else +#include /* openjpeg.h needs FILE* */ +#include +#endif +#include + +#include "gdaljp2abstractdataset.h" +#include "cpl_string.h" +#include "gdaljp2metadata.h" +#include "cpl_multiproc.h" +#include "cpl_atomic_ops.h" +#include "vrt/vrtdataset.h" + +CPL_CVSID("$Id: openjpegdataset.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +/************************************************************************/ +/* JP2OpenJPEGDataset_ErrorCallback() */ +/************************************************************************/ + +static void JP2OpenJPEGDataset_ErrorCallback(const char *pszMsg, CPL_UNUSED void *unused) +{ + CPLError(CE_Failure, CPLE_AppDefined, "%s", pszMsg); +} + +/************************************************************************/ +/* JP2OpenJPEGDataset_WarningCallback() */ +/************************************************************************/ + +static void JP2OpenJPEGDataset_WarningCallback(const char *pszMsg, CPL_UNUSED void *unused) +{ + if( strcmp(pszMsg, "Empty SOT marker detected: Psot=12.\n") == 0 ) + { + static int bWarningEmitted = FALSE; + if( bWarningEmitted ) + return; + bWarningEmitted = TRUE; + } + if( strcmp(pszMsg, "JP2 box which are after the codestream will not be read by this function.\n") != 0 ) + CPLError(CE_Warning, CPLE_AppDefined, "%s", pszMsg); +} + +/************************************************************************/ +/* JP2OpenJPEGDataset_InfoCallback() */ +/************************************************************************/ + +static void JP2OpenJPEGDataset_InfoCallback(const char *pszMsg, CPL_UNUSED void *unused) +{ + char* pszMsgTmp = CPLStrdup(pszMsg); + int nLen = (int)strlen(pszMsgTmp); + while( nLen > 0 && pszMsgTmp[nLen-1] == '\n' ) + { + pszMsgTmp[nLen-1] = '\0'; + nLen --; + } + CPLDebug("OPENJPEG", "info: %s", pszMsgTmp); + CPLFree(pszMsgTmp); +} + +typedef struct +{ + VSILFILE* fp; + vsi_l_offset nBaseOffset; +} JP2OpenJPEGFile; + +/************************************************************************/ +/* JP2OpenJPEGDataset_Read() */ +/************************************************************************/ + +static OPJ_SIZE_T JP2OpenJPEGDataset_Read(void* pBuffer, OPJ_SIZE_T nBytes, + void *pUserData) +{ + JP2OpenJPEGFile* psJP2OpenJPEGFile = (JP2OpenJPEGFile* )pUserData; + int nRet = VSIFReadL(pBuffer, 1, nBytes, psJP2OpenJPEGFile->fp); +#ifdef DEBUG_IO + CPLDebug("OPENJPEG", "JP2OpenJPEGDataset_Read(%d) = %d", (int)nBytes, nRet); +#endif + if (nRet == 0) + nRet = -1; + + return nRet; +} + +/************************************************************************/ +/* JP2OpenJPEGDataset_Write() */ +/************************************************************************/ + +static OPJ_SIZE_T JP2OpenJPEGDataset_Write(void* pBuffer, OPJ_SIZE_T nBytes, + void *pUserData) +{ + JP2OpenJPEGFile* psJP2OpenJPEGFile = (JP2OpenJPEGFile* )pUserData; + int nRet = VSIFWriteL(pBuffer, 1, nBytes, psJP2OpenJPEGFile->fp); +#ifdef DEBUG_IO + CPLDebug("OPENJPEG", "JP2OpenJPEGDataset_Write(%d) = %d", (int)nBytes, nRet); +#endif + return nRet; +} + +/************************************************************************/ +/* JP2OpenJPEGDataset_Seek() */ +/************************************************************************/ + +static OPJ_BOOL JP2OpenJPEGDataset_Seek(OPJ_OFF_T nBytes, void * pUserData) +{ + JP2OpenJPEGFile* psJP2OpenJPEGFile = (JP2OpenJPEGFile* )pUserData; +#ifdef DEBUG_IO + CPLDebug("OPENJPEG", "JP2OpenJPEGDataset_Seek(%d)", (int)nBytes); +#endif + return VSIFSeekL(psJP2OpenJPEGFile->fp, psJP2OpenJPEGFile->nBaseOffset +nBytes, + SEEK_SET) == 0; +} + +/************************************************************************/ +/* JP2OpenJPEGDataset_Skip() */ +/************************************************************************/ + +static OPJ_OFF_T JP2OpenJPEGDataset_Skip(OPJ_OFF_T nBytes, void * pUserData) +{ + JP2OpenJPEGFile* psJP2OpenJPEGFile = (JP2OpenJPEGFile* )pUserData; + vsi_l_offset nOffset = VSIFTellL(psJP2OpenJPEGFile->fp); + nOffset += nBytes; +#ifdef DEBUG_IO + CPLDebug("OPENJPEG", "JP2OpenJPEGDataset_Skip(%d -> " CPL_FRMT_GUIB ")", + (int)nBytes, (GUIntBig)nOffset); +#endif + VSIFSeekL(psJP2OpenJPEGFile->fp, nOffset, SEEK_SET); + return nBytes; +} + +/************************************************************************/ +/* ==================================================================== */ +/* JP2OpenJPEGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class JP2OpenJPEGRasterBand; + +class JP2OpenJPEGDataset : public GDALJP2AbstractDataset +{ + friend class JP2OpenJPEGRasterBand; + + VSILFILE *fp; /* Large FILE API */ + vsi_l_offset nCodeStreamStart; + vsi_l_offset nCodeStreamLength; + + OPJ_COLOR_SPACE eColorSpace; + int nRedIndex; + int nGreenIndex; + int nBlueIndex; + int nAlphaIndex; + + int bIs420; + + int iLevel; + int nOverviewCount; + JP2OpenJPEGDataset** papoOverviewDS; + int bUseSetDecodeArea; + + int nThreads; + int GetNumThreads(); + int bEnoughMemoryToLoadOtherBands; + int bRewrite; + int bHasGeoreferencingAtOpening; + + protected: + virtual int CloseDependentDatasets(); + + public: + JP2OpenJPEGDataset(); + ~JP2OpenJPEGDataset(); + + static int Identify( GDALOpenInfo * poOpenInfo ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + + virtual CPLErr SetProjection( const char * ); + virtual CPLErr SetGeoTransform( double* ); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + static void WriteBox(VSILFILE* fp, GDALJP2Box* poBox); + static void WriteGDALMetadataBox( VSILFILE* fp, GDALDataset* poSrcDS, + char** papszOptions ); + static void WriteXMLBoxes( VSILFILE* fp, GDALDataset* poSrcDS, + char** papszOptions ); + static void WriteXMPBox( VSILFILE* fp, GDALDataset* poSrcDS, + char** papszOptions ); + static void WriteIPRBox( VSILFILE* fp, GDALDataset* poSrcDS, + char** papszOptions ); + + CPLErr ReadBlock( int nBand, VSILFILE* fp, + int nBlockXOff, int nBlockYOff, void * pImage, + int nBandCount, int *panBandMap ); + + int PreloadBlocks( JP2OpenJPEGRasterBand* poBand, + int nXOff, int nYOff, int nXSize, int nYSize, + int nBandCount, int *panBandMap ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* JP2OpenJPEGRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class JP2OpenJPEGRasterBand : public GDALPamRasterBand +{ + friend class JP2OpenJPEGDataset; + int bPromoteTo8Bit; + GDALColorTable* poCT; + + public: + + JP2OpenJPEGRasterBand( JP2OpenJPEGDataset * poDS, int nBand, + GDALDataType eDataType, int nBits, + int bPromoteTo8Bit, + int nBlockXSize, int nBlockYSize ); + ~JP2OpenJPEGRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); + + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable* GetColorTable() { return poCT; } + + virtual int GetOverviewCount(); + virtual GDALRasterBand* GetOverview(int iOvrLevel); + + virtual int HasArbitraryOverviews() { return poCT == NULL; } +}; + + +/************************************************************************/ +/* JP2OpenJPEGRasterBand() */ +/************************************************************************/ + +JP2OpenJPEGRasterBand::JP2OpenJPEGRasterBand( JP2OpenJPEGDataset *poDS, int nBand, + GDALDataType eDataType, int nBits, + int bPromoteTo8Bit, + int nBlockXSize, int nBlockYSize ) + +{ + this->eDataType = eDataType; + this->nBlockXSize = nBlockXSize; + this->nBlockYSize = nBlockYSize; + this->bPromoteTo8Bit = bPromoteTo8Bit; + poCT = NULL; + + if( (nBits % 8) != 0 ) + GDALRasterBand::SetMetadataItem("NBITS", + CPLString().Printf("%d",nBits), + "IMAGE_STRUCTURE" ); + GDALRasterBand::SetMetadataItem("COMPRESSION", "JPEG2000", + "IMAGE_STRUCTURE" ); + this->poDS = poDS; + this->nBand = nBand; +} + +/************************************************************************/ +/* ~JP2OpenJPEGRasterBand() */ +/************************************************************************/ + +JP2OpenJPEGRasterBand::~JP2OpenJPEGRasterBand() +{ + delete poCT; +} + +/************************************************************************/ +/* CLAMP_0_255() */ +/************************************************************************/ + +static CPL_INLINE GByte CLAMP_0_255(int val) +{ + if (val < 0) + return 0; + else if (val > 255) + return 255; + else + return (GByte)val; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr JP2OpenJPEGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + JP2OpenJPEGDataset *poGDS = (JP2OpenJPEGDataset *) poDS; + if ( poGDS->bEnoughMemoryToLoadOtherBands ) + return poGDS->ReadBlock(nBand, poGDS->fp, nBlockXOff, nBlockYOff, pImage, + poGDS->nBands, NULL); + else + return poGDS->ReadBlock(nBand, poGDS->fp, nBlockXOff, nBlockYOff, pImage, + 1, &nBand); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr JP2OpenJPEGRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ + JP2OpenJPEGDataset *poGDS = (JP2OpenJPEGDataset *) poDS; + + if( eRWFlag != GF_Read ) + return CE_Failure; + +/* ==================================================================== */ +/* Do we have overviews that would be appropriate to satisfy */ +/* this request? */ +/* ==================================================================== */ + if( (nBufXSize < nXSize || nBufYSize < nYSize) + && GetOverviewCount() > 0 && eRWFlag == GF_Read ) + { + int nOverview; + GDALRasterIOExtraArg sExtraArg; + + GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg); + + nOverview = + GDALBandGetBestOverviewLevel2(this, nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, &sExtraArg); + if (nOverview >= 0) + { + GDALRasterBand* poOverviewBand = GetOverview(nOverview); + if (poOverviewBand == NULL) + return CE_Failure; + + return poOverviewBand->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, &sExtraArg ); + } + } + + poGDS->bEnoughMemoryToLoadOtherBands = poGDS->PreloadBlocks(this, nXOff, nYOff, nXSize, nYSize, 0, NULL); + + CPLErr eErr = GDALPamRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + + poGDS->bEnoughMemoryToLoadOtherBands = TRUE; + return eErr; +} + +/************************************************************************/ +/* GetNumThreads() */ +/************************************************************************/ + +int JP2OpenJPEGDataset::GetNumThreads() +{ + if( nThreads >= 1 ) + return nThreads; + + const char* pszThreads = CPLGetConfigOption("GDAL_NUM_THREADS", "ALL_CPUS"); + if (EQUAL(pszThreads, "ALL_CPUS")) + nThreads = CPLGetNumCPUs(); + else + nThreads = atoi(pszThreads); + if (nThreads > 128) + nThreads = 128; + if (nThreads <= 0) + nThreads = 1; + return nThreads; +} + +/************************************************************************/ +/* JP2OpenJPEGReadBlockInThread() */ +/************************************************************************/ + +class JobStruct +{ +public: + + JP2OpenJPEGDataset* poGDS; + int nBand; + std::vector< std::pair > oPairs; + volatile int nCurPair; + int nBandCount; + int *panBandMap; +}; + +static void JP2OpenJPEGReadBlockInThread(void* userdata) +{ + int nPair; + JobStruct* poJob = (JobStruct*) userdata; + JP2OpenJPEGDataset* poGDS = poJob->poGDS; + int nBand = poJob->nBand; + int nPairs = (int)poJob->oPairs.size(); + int nBandCount = poJob->nBandCount; + int* panBandMap = poJob->panBandMap; + VSILFILE* fp = VSIFOpenL(poGDS->GetDescription(), "rb"); + if( fp == NULL ) + { + CPLDebug("OPENJPEG", "Cannot open %s", poGDS->GetDescription()); + return; + } + + while( (nPair = CPLAtomicInc(&(poJob->nCurPair))) < nPairs ) + { + int nBlockXOff = poJob->oPairs[nPair].first; + int nBlockYOff = poJob->oPairs[nPair].second; + GDALRasterBlock* poBlock = poGDS->GetRasterBand(nBand)-> + GetLockedBlockRef(nBlockXOff,nBlockYOff, TRUE); + if (poBlock == NULL) + break; + + void* pDstBuffer = poBlock->GetDataRef(); + if (!pDstBuffer) + { + poBlock->DropLock(); + break; + } + + poGDS->ReadBlock(nBand, fp, nBlockXOff, nBlockYOff, pDstBuffer, + nBandCount, panBandMap); + + poBlock->DropLock(); + } + + VSIFCloseL(fp); +} + +/************************************************************************/ +/* PreloadBlocks() */ +/************************************************************************/ + +int JP2OpenJPEGDataset::PreloadBlocks(JP2OpenJPEGRasterBand* poBand, + int nXOff, int nYOff, int nXSize, int nYSize, + int nBandCount, int *panBandMap) +{ + int bRet = TRUE; + int nXStart = nXOff / poBand->nBlockXSize; + int nXEnd = (nXOff + nXSize - 1) / poBand->nBlockXSize; + int nYStart = nYOff / poBand->nBlockYSize; + int nYEnd = (nYOff + nYSize - 1) / poBand->nBlockYSize; + GIntBig nReqMem = (GIntBig)(nXEnd - nXStart + 1) * (nYEnd - nYStart + 1) * + poBand->nBlockXSize * poBand->nBlockYSize * (GDALGetDataTypeSize(poBand->eDataType) / 8); + + int nMaxThreads = GetNumThreads(); + if( !bUseSetDecodeArea && nMaxThreads > 1 ) + { + if( nReqMem > GDALGetCacheMax64() / (nBandCount == 0 ? 1 : nBandCount) ) + return FALSE; + + int nBlocksToLoad = 0; + std::vector< std::pair > oPairs; + for(int nBlockXOff = nXStart; nBlockXOff <= nXEnd; ++nBlockXOff) + { + for(int nBlockYOff = nYStart; nBlockYOff <= nYEnd; ++nBlockYOff) + { + GDALRasterBlock* poBlock = poBand->TryGetLockedBlockRef(nBlockXOff,nBlockYOff); + if (poBlock != NULL) + { + poBlock->DropLock(); + continue; + } + oPairs.push_back( std::pair(nBlockXOff, nBlockYOff) ); + nBlocksToLoad ++; + } + } + + if( nBlocksToLoad > 1 ) + { + int nThreads = MIN(nBlocksToLoad, nMaxThreads); + CPLJoinableThread** pahThreads = (CPLJoinableThread**) CPLMalloc( sizeof(CPLJoinableThread*) * nThreads ); + int i; + + CPLDebug("OPENJPEG", "%d blocks to load", nBlocksToLoad); + + JobStruct oJob; + oJob.poGDS = this; + oJob.nBand = poBand->GetBand(); + oJob.oPairs = oPairs; + oJob.nCurPair = -1; + if( nBandCount > 0 ) + { + oJob.nBandCount = nBandCount; + oJob.panBandMap = panBandMap; + } + else + { + if( nReqMem <= GDALGetCacheMax64() / nBands ) + { + oJob.nBandCount = nBands; + oJob.panBandMap = NULL; + } + else + { + bRet = FALSE; + oJob.nBandCount = 1; + oJob.panBandMap = &oJob.nBand; + } + } + + /* Flushes all dirty blocks from cache to disk to avoid them */ + /* to be flushed randomly, and simultaneously, from our worker threads, */ + /* which might cause races in the output driver. */ + /* This is a workaround to a design defect of the block cache */ + GDALRasterBlock::FlushDirtyBlocks(); + + for(i=0;iGetOverviewCount() > 0 && eRWFlag == GF_Read ) + { + int nOverview; + GDALRasterIOExtraArg sExtraArg; + + GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg); + + nOverview = + GDALBandGetBestOverviewLevel2(poBand, nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, &sExtraArg); + if (nOverview >= 0) + { + return papoOverviewDS[nOverview]->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + &sExtraArg); + } + } + + bEnoughMemoryToLoadOtherBands = PreloadBlocks(poBand, nXOff, nYOff, nXSize, nYSize, nBandCount, panBandMap); + + CPLErr eErr = GDALPamDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + psExtraArg ); + + bEnoughMemoryToLoadOtherBands = TRUE; + return eErr; +} + +/************************************************************************/ +/* JP2OpenJPEGCreateReadStream() */ +/************************************************************************/ + +static opj_stream_t* JP2OpenJPEGCreateReadStream(JP2OpenJPEGFile* psJP2OpenJPEGFile, + vsi_l_offset nSize) +{ + opj_stream_t *pStream = opj_stream_create(1024, TRUE); // Default 1MB is way too big for some datasets + + VSIFSeekL(psJP2OpenJPEGFile->fp, psJP2OpenJPEGFile->nBaseOffset, SEEK_SET); + opj_stream_set_user_data_length(pStream, nSize); + + opj_stream_set_read_function(pStream, JP2OpenJPEGDataset_Read); + opj_stream_set_seek_function(pStream, JP2OpenJPEGDataset_Seek); + opj_stream_set_skip_function(pStream, JP2OpenJPEGDataset_Skip); +#if defined(OPENJPEG_VERSION) && OPENJPEG_VERSION >= 20100 + opj_stream_set_user_data(pStream, psJP2OpenJPEGFile, NULL); +#else + opj_stream_set_user_data(pStream, psJP2OpenJPEGFile); +#endif + + return pStream; +} + +/************************************************************************/ +/* ReadBlock() */ +/************************************************************************/ + +CPLErr JP2OpenJPEGDataset::ReadBlock( int nBand, VSILFILE* fp, + int nBlockXOff, int nBlockYOff, void * pImage, + int nBandCount, int* panBandMap ) +{ + CPLErr eErr = CE_None; + opj_codec_t* pCodec; + opj_stream_t * pStream; + opj_image_t * psImage; + + JP2OpenJPEGRasterBand* poBand = (JP2OpenJPEGRasterBand*) GetRasterBand(nBand); + int nBlockXSize = poBand->nBlockXSize; + int nBlockYSize = poBand->nBlockYSize; + GDALDataType eDataType = poBand->eDataType; + + int nDataTypeSize = (GDALGetDataTypeSize(eDataType) / 8); + + int nTileNumber = nBlockXOff + nBlockYOff * poBand->nBlocksPerRow; + int nWidthToRead = MIN(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize); + int nHeightToRead = MIN(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize); + + pCodec = opj_create_decompress(OPJ_CODEC_J2K); + + opj_set_info_handler(pCodec, JP2OpenJPEGDataset_InfoCallback,NULL); + opj_set_warning_handler(pCodec, JP2OpenJPEGDataset_WarningCallback, NULL); + opj_set_error_handler(pCodec, JP2OpenJPEGDataset_ErrorCallback,NULL); + + opj_dparameters_t parameters; + opj_set_default_decoder_parameters(¶meters); + + if (! opj_setup_decoder(pCodec,¶meters)) + { + CPLError(CE_Failure, CPLE_AppDefined, "opj_setup_decoder() failed"); + return CE_Failure; + } + + JP2OpenJPEGFile sJP2OpenJPEGFile; + sJP2OpenJPEGFile.fp = fp; + sJP2OpenJPEGFile.nBaseOffset = nCodeStreamStart; + pStream = JP2OpenJPEGCreateReadStream(&sJP2OpenJPEGFile, nCodeStreamLength); + + if(!opj_read_header(pStream,pCodec,&psImage)) + { + CPLError(CE_Failure, CPLE_AppDefined, "opj_read_header() failed"); + return CE_Failure; + } + + if (!opj_set_decoded_resolution_factor( pCodec, iLevel )) + { + CPLError(CE_Failure, CPLE_AppDefined, "opj_set_decoded_resolution_factor() failed"); + eErr = CE_Failure; + goto end; + } + + if (bUseSetDecodeArea) + { + if (!opj_set_decode_area(pCodec,psImage, + nBlockXOff*nBlockXSize, + nBlockYOff*nBlockYSize, + nBlockXOff*nBlockXSize+nWidthToRead, + nBlockYOff*nBlockYSize+nHeightToRead)) + { + CPLError(CE_Failure, CPLE_AppDefined, "opj_set_decode_area() failed"); + eErr = CE_Failure; + goto end; + } + if (!opj_decode(pCodec,pStream, psImage)) + { + CPLError(CE_Failure, CPLE_AppDefined, "opj_decode() failed"); + eErr = CE_Failure; + goto end; + } + } + else + { + if (!opj_get_decoded_tile( pCodec, pStream, psImage, nTileNumber )) + { + CPLError(CE_Failure, CPLE_AppDefined, "opj_get_decoded_tile() failed"); + eErr = CE_Failure; + goto end; + } + } + + for(int xBand = 0; xBand < nBandCount; xBand ++) + { + void* pDstBuffer; + GDALRasterBlock *poBlock = NULL; + int iBand = (panBandMap) ? panBandMap[xBand] : xBand + 1; + int bPromoteTo8Bit = ((JP2OpenJPEGRasterBand*)GetRasterBand(iBand))->bPromoteTo8Bit; + + if (iBand == nBand) + pDstBuffer = pImage; + else + { + poBlock = ((JP2OpenJPEGRasterBand*)GetRasterBand(iBand))-> + TryGetLockedBlockRef(nBlockXOff,nBlockYOff); + if (poBlock != NULL) + { + poBlock->DropLock(); + continue; + } + + poBlock = GetRasterBand(iBand)-> + GetLockedBlockRef(nBlockXOff,nBlockYOff, TRUE); + if (poBlock == NULL) + { + continue; + } + + pDstBuffer = poBlock->GetDataRef(); + if (!pDstBuffer) + { + poBlock->DropLock(); + continue; + } + } + + if (bIs420) + { + CPLAssert((int)psImage->comps[0].w >= nWidthToRead); + CPLAssert((int)psImage->comps[0].h >= nHeightToRead); + CPLAssert(psImage->comps[1].w == (psImage->comps[0].w + 1) / 2); + CPLAssert(psImage->comps[1].h == (psImage->comps[0].h + 1) / 2); + CPLAssert(psImage->comps[2].w == (psImage->comps[0].w + 1) / 2); + CPLAssert(psImage->comps[2].h == (psImage->comps[0].h + 1) / 2); + if( nBands == 4 ) + { + CPLAssert((int)psImage->comps[3].w >= nWidthToRead); + CPLAssert((int)psImage->comps[3].h >= nHeightToRead); + } + + OPJ_INT32* pSrcY = psImage->comps[0].data; + OPJ_INT32* pSrcCb = psImage->comps[1].data; + OPJ_INT32* pSrcCr = psImage->comps[2].data; + OPJ_INT32* pSrcA = (nBands == 4) ? psImage->comps[3].data : NULL; + GByte* pDst = (GByte*)pDstBuffer; + for(int j=0;jcomps[0].w + i]; + int Cb = pSrcCb[(j/2) * psImage->comps[1].w + (i/2)]; + int Cr = pSrcCr[(j/2) * psImage->comps[2].w + (i/2)]; + if (iBand == 1) + pDst[j * nBlockXSize + i] = CLAMP_0_255((int)(Y + 1.402 * (Cr - 128))); + else if (iBand == 2) + pDst[j * nBlockXSize + i] = CLAMP_0_255((int)(Y - 0.34414 * (Cb - 128) - 0.71414 * (Cr - 128))); + else if (iBand == 3) + pDst[j * nBlockXSize + i] = CLAMP_0_255((int)(Y + 1.772 * (Cb - 128))); + else if (iBand == 4) + pDst[j * nBlockXSize + i] = pSrcA[j * psImage->comps[0].w + i]; + } + } + + if( bPromoteTo8Bit ) + { + for(int j=0;jcomps[iBand-1].w >= nWidthToRead); + CPLAssert((int)psImage->comps[iBand-1].h >= nHeightToRead); + + if( bPromoteTo8Bit ) + { + for(int j=0;jcomps[iBand-1].data[j * psImage->comps[iBand-1].w + i] *= 255; + } + } + } + + if ((int)psImage->comps[iBand-1].w == nBlockXSize && + (int)psImage->comps[iBand-1].h == nBlockYSize) + { + GDALCopyWords(psImage->comps[iBand-1].data, GDT_Int32, 4, + pDstBuffer, eDataType, nDataTypeSize, nBlockXSize * nBlockYSize); + } + else + { + for(int j=0;jcomps[iBand-1].data + j * psImage->comps[iBand-1].w, GDT_Int32, 4, + (GByte*)pDstBuffer + j * nBlockXSize * nDataTypeSize, eDataType, nDataTypeSize, + nWidthToRead); + } + } + } + + if (poBlock != NULL) + poBlock->DropLock(); + } + +end: + opj_end_decompress(pCodec,pStream); + opj_stream_destroy(pStream); + opj_destroy_codec(pCodec); + opj_image_destroy(psImage); + + return eErr; +} + + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int JP2OpenJPEGRasterBand::GetOverviewCount() +{ + JP2OpenJPEGDataset *poGDS = (JP2OpenJPEGDataset *) poDS; + return poGDS->nOverviewCount; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand* JP2OpenJPEGRasterBand::GetOverview(int iOvrLevel) +{ + JP2OpenJPEGDataset *poGDS = (JP2OpenJPEGDataset *) poDS; + if (iOvrLevel < 0 || iOvrLevel >= poGDS->nOverviewCount) + return NULL; + + return poGDS->papoOverviewDS[iOvrLevel]->GetRasterBand(nBand); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp JP2OpenJPEGRasterBand::GetColorInterpretation() +{ + JP2OpenJPEGDataset *poGDS = (JP2OpenJPEGDataset *) poDS; + + if( poCT ) + return GCI_PaletteIndex; + + if( nBand == poGDS->nAlphaIndex + 1 ) + return GCI_AlphaBand; + + if (poGDS->nBands <= 2 && poGDS->eColorSpace == OPJ_CLRSPC_GRAY) + return GCI_GrayIndex; + else if (poGDS->eColorSpace == OPJ_CLRSPC_SRGB || + poGDS->eColorSpace == OPJ_CLRSPC_SYCC) + { + if( nBand == poGDS->nRedIndex + 1 ) + return GCI_RedBand; + if( nBand == poGDS->nGreenIndex + 1 ) + return GCI_GreenBand; + if( nBand == poGDS->nBlueIndex + 1 ) + return GCI_BlueBand; + } + + return GCI_Undefined; +} + +/************************************************************************/ +/* ==================================================================== */ +/* JP2OpenJPEGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* JP2OpenJPEGDataset() */ +/************************************************************************/ + +JP2OpenJPEGDataset::JP2OpenJPEGDataset() +{ + fp = NULL; + nCodeStreamStart = 0; + nCodeStreamLength = 0; + nBands = 0; + eColorSpace = OPJ_CLRSPC_UNKNOWN; + nRedIndex = 0; + nGreenIndex = 1; + nBlueIndex = 2; + nAlphaIndex = -1; + bIs420 = FALSE; + iLevel = 0; + nOverviewCount = 0; + papoOverviewDS = NULL; + bUseSetDecodeArea = FALSE; + nThreads = -1; + bEnoughMemoryToLoadOtherBands = TRUE; + bRewrite = FALSE; + bHasGeoreferencingAtOpening = FALSE; +} + +/************************************************************************/ +/* ~JP2OpenJPEGDataset() */ +/************************************************************************/ + +JP2OpenJPEGDataset::~JP2OpenJPEGDataset() + +{ + FlushCache(); + + if( iLevel == 0 && fp != NULL ) + { + if( bRewrite ) + { + GDALJP2Box oBox( fp ); + vsi_l_offset nOffsetJP2C = 0, nLengthJP2C = 0, + nOffsetXML = 0, nOffsetASOC = 0, nOffsetUUID = 0, + nOffsetIHDR = 0, nLengthIHDR = 0; + int bMSIBox = FALSE, bGMLData = FALSE; + int bUnsupportedConfiguration = FALSE; + if( oBox.ReadFirst() ) + { + while( strlen(oBox.GetType()) > 0 ) + { + if( EQUAL(oBox.GetType(),"jp2c") ) + { + if( nOffsetJP2C == 0 ) + { + nOffsetJP2C = VSIFTellL(fp); + nLengthJP2C = oBox.GetDataLength(); + } + else + bUnsupportedConfiguration = TRUE; + } + else if( EQUAL(oBox.GetType(),"jp2h") ) + { + GDALJP2Box oSubBox( fp ); + if( oSubBox.ReadFirstChild( &oBox ) && + EQUAL(oSubBox.GetType(),"ihdr") ) + { + nOffsetIHDR = VSIFTellL(fp); + nLengthIHDR = oSubBox.GetDataLength(); + } + } + else if( EQUAL(oBox.GetType(),"xml ") ) + { + if( nOffsetXML == 0 ) + nOffsetXML = VSIFTellL(fp); + } + else if( EQUAL(oBox.GetType(),"asoc") ) + { + if( nOffsetASOC == 0 ) + nOffsetASOC = VSIFTellL(fp); + + GDALJP2Box oSubBox( fp ); + if( oSubBox.ReadFirstChild( &oBox ) && + EQUAL(oSubBox.GetType(),"lbl ") ) + { + char *pszLabel = (char *) oSubBox.ReadBoxData(); + if( pszLabel != NULL && EQUAL(pszLabel,"gml.data") ) + { + bGMLData = TRUE; + } + else + bUnsupportedConfiguration = TRUE; + CPLFree( pszLabel ); + } + else + bUnsupportedConfiguration = TRUE; + } + else if( EQUAL(oBox.GetType(),"uuid") ) + { + if( nOffsetUUID == 0 ) + nOffsetUUID = VSIFTellL(fp); + if( GDALJP2Metadata::IsUUID_MSI(oBox.GetUUID()) ) + bMSIBox = TRUE; + else if( !GDALJP2Metadata::IsUUID_XMP(oBox.GetUUID()) ) + bUnsupportedConfiguration = TRUE; + } + else if( !EQUAL(oBox.GetType(),"jP ") && + !EQUAL(oBox.GetType(),"ftyp") && + !EQUAL(oBox.GetType(),"rreq") && + !EQUAL(oBox.GetType(),"jp2h") && + !EQUAL(oBox.GetType(),"jp2i") ) + { + bUnsupportedConfiguration = TRUE; + } + + if (bUnsupportedConfiguration || !oBox.ReadNext()) + break; + } + } + + const char* pszGMLJP2; + int bGeoreferencingCompatOfGMLJP2 = + ((pszProjection != NULL && pszProjection[0] != '\0' ) && + bGeoTransformValid && nGCPCount == 0); + if( bGeoreferencingCompatOfGMLJP2 && + ((bHasGeoreferencingAtOpening && bGMLData) || + (!bHasGeoreferencingAtOpening)) ) + pszGMLJP2 = "GMLJP2=YES"; + else + pszGMLJP2 = "GMLJP2=NO"; + + const char* pszGeoJP2; + int bGeoreferencingCompatOfGeoJP2 = + ((pszProjection != NULL && pszProjection[0] != '\0' ) || + nGCPCount != 0 || bGeoTransformValid); + if( bGeoreferencingCompatOfGeoJP2 && + ((bHasGeoreferencingAtOpening && bMSIBox) || + (!bHasGeoreferencingAtOpening) || nGCPCount > 0) ) + pszGeoJP2 = "GeoJP2=YES"; + else + pszGeoJP2 = "GeoJP2=NO"; + + /* Test that the length of the JP2C box is not 0 */ + int bJP2CBoxOKForRewriteInPlace = TRUE; + if( nOffsetJP2C > 16 && !bUnsupportedConfiguration ) + { + VSIFSeekL(fp, nOffsetJP2C - 8, SEEK_SET); + GByte abyBuffer[8]; + VSIFReadL(abyBuffer, 1, 8, fp); + if( EQUALN((const char*)abyBuffer + 4, "jp2c", 4) && + abyBuffer[0] == 0 && abyBuffer[1] == 0 && + abyBuffer[2] == 0 && abyBuffer[3] == 0 ) + { + if( (vsi_l_offset)(GUInt32)(nLengthJP2C + 8) == (nLengthJP2C + 8) ) + { + CPLDebug("OPENJPEG", "Patching length of JP2C box with real length"); + VSIFSeekL(fp, nOffsetJP2C - 8, SEEK_SET); + GUInt32 nLength = (GUInt32)nLengthJP2C + 8; + CPL_MSBPTR32(&nLength); + VSIFWriteL(&nLength, 1, 4, fp); + } + else + bJP2CBoxOKForRewriteInPlace = FALSE; + } + } + + if( nOffsetJP2C == 0 || bUnsupportedConfiguration ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot rewrite file due to unsupported JP2 box configuration"); + VSIFCloseL( fp ); + } + else if( bJP2CBoxOKForRewriteInPlace && + (nOffsetXML == 0 || nOffsetXML > nOffsetJP2C) && + (nOffsetASOC == 0 || nOffsetASOC > nOffsetJP2C) && + (nOffsetUUID == 0 || nOffsetUUID > nOffsetJP2C) ) + { + CPLDebug("OPENJPEG", "Rewriting boxes after codestream"); + + /* Update IPR flag */ + if( nLengthIHDR == 14 ) + { + VSIFSeekL( fp, nOffsetIHDR + nLengthIHDR - 1, SEEK_SET ); + GByte bIPR = GetMetadata("xml:IPR") != NULL; + VSIFWriteL( &bIPR, 1, 1, fp ); + } + + VSIFSeekL( fp, nOffsetJP2C + nLengthJP2C, SEEK_SET ); + + GDALJP2Metadata oJP2MD; + if( GetGCPCount() > 0 ) + { + oJP2MD.SetGCPs( GetGCPCount(), + GetGCPs() ); + oJP2MD.SetProjection( GetGCPProjection() ); + } + else + { + const char* pszWKT = GetProjectionRef(); + if( pszWKT != NULL && pszWKT[0] != '\0' ) + { + oJP2MD.SetProjection( pszWKT ); + } + if( bGeoTransformValid ) + { + oJP2MD.SetGeoTransform( adfGeoTransform ); + } + } + + const char* pszAreaOrPoint = GetMetadataItem(GDALMD_AREA_OR_POINT); + oJP2MD.bPixelIsPoint = pszAreaOrPoint != NULL && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT); + + WriteIPRBox(fp, this, NULL); + + if( bGeoreferencingCompatOfGMLJP2 && EQUAL(pszGMLJP2, "GMLJP2=YES") ) + { + GDALJP2Box* poBox = oJP2MD.CreateGMLJP2(nRasterXSize,nRasterYSize); + WriteBox(fp, poBox); + delete poBox; + } + + WriteXMLBoxes(fp, this, NULL); + WriteGDALMetadataBox(fp, this, NULL); + + if( bGeoreferencingCompatOfGeoJP2 && EQUAL(pszGeoJP2, "GeoJP2=YES") ) + { + GDALJP2Box* poBox = oJP2MD.CreateJP2GeoTIFF(); + WriteBox(fp, poBox); + delete poBox; + } + + WriteXMPBox(fp, this, NULL); + + VSIFTruncateL( fp, VSIFTellL(fp) ); + + VSIFCloseL( fp ); + } + else + { + VSIFCloseL( fp ); + + CPLDebug("OPENJPEG", "Rewriting whole file"); + + const char* apszOptions[] = { + "USE_SRC_CODESTREAM=YES", "CODEC=JP2", "WRITE_METADATA=YES", + NULL, NULL, NULL }; + apszOptions[3] = pszGMLJP2; + apszOptions[4] = pszGeoJP2; + CPLString osTmpFilename(CPLSPrintf("%s.tmp", GetDescription())); + GDALDataset* poOutDS = CreateCopy( osTmpFilename, this, FALSE, + (char**)apszOptions, GDALDummyProgress, NULL ); + if( poOutDS ) + { + GDALClose(poOutDS); + VSIRename(osTmpFilename, GetDescription()); + } + else + VSIUnlink(osTmpFilename); + VSIUnlink(CPLSPrintf("%s.tmp.aux.xml", GetDescription())); + } + } + else + VSIFCloseL( fp ); + } + + CloseDependentDatasets(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int JP2OpenJPEGDataset::CloseDependentDatasets() +{ + int bRet = GDALJP2AbstractDataset::CloseDependentDatasets(); + if ( papoOverviewDS ) + { + for( int i = 0; i < nOverviewCount; i++ ) + delete papoOverviewDS[i]; + CPLFree( papoOverviewDS ); + papoOverviewDS = NULL; + bRet = TRUE; + } + return bRet; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr JP2OpenJPEGDataset::SetProjection( const char * pszProjectionIn ) +{ + if( eAccess == GA_Update ) + { + bRewrite = TRUE; + CPLFree(pszProjection); + pszProjection = (pszProjectionIn) ? CPLStrdup(pszProjectionIn) : CPLStrdup(""); + return CE_None; + } + else + return GDALJP2AbstractDataset::SetProjection(pszProjectionIn); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr JP2OpenJPEGDataset::SetGeoTransform( double *padfGeoTransform ) +{ + if( eAccess == GA_Update ) + { + bRewrite = TRUE; + memcpy(adfGeoTransform, padfGeoTransform, 6* sizeof(double)); + bGeoTransformValid = !( + adfGeoTransform[0] == 0.0 && adfGeoTransform[1] == 1.0 && + adfGeoTransform[2] == 0.0 && adfGeoTransform[3] == 0.0 && + adfGeoTransform[4] == 0.0 && adfGeoTransform[5] == 1.0); + return CE_None; + } + else + return GDALJP2AbstractDataset::SetGeoTransform(padfGeoTransform); +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +CPLErr JP2OpenJPEGDataset::SetGCPs( int nGCPCountIn, const GDAL_GCP *pasGCPListIn, + const char *pszGCPProjectionIn ) +{ + if( eAccess == GA_Update ) + { + bRewrite = TRUE; + CPLFree( pszProjection ); + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + pszProjection = (pszGCPProjectionIn) ? CPLStrdup(pszGCPProjectionIn) : CPLStrdup(""); + nGCPCount = nGCPCountIn; + pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPListIn ); + + return CE_None; + } + else + return GDALJP2AbstractDataset::SetGCPs(nGCPCountIn, pasGCPListIn, + pszGCPProjectionIn); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr JP2OpenJPEGDataset::SetMetadata( char ** papszMetadata, + const char * pszDomain ) +{ + if( eAccess == GA_Update ) + { + bRewrite = TRUE; + return GDALDataset::SetMetadata(papszMetadata, pszDomain); + } + return GDALJP2AbstractDataset::SetMetadata(papszMetadata, pszDomain); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr JP2OpenJPEGDataset::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) +{ + if( eAccess == GA_Update ) + { + bRewrite = TRUE; + return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain); + } + return GDALJP2AbstractDataset::SetMetadataItem(pszName, pszValue, pszDomain); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +static const unsigned char jpc_header[] = {0xff,0x4f}; +static const unsigned char jp2_box_jp[] = {0x6a,0x50,0x20,0x20}; /* 'jP ' */ + +int JP2OpenJPEGDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes >= 16 + && (memcmp( poOpenInfo->pabyHeader, jpc_header, + sizeof(jpc_header) ) == 0 + || memcmp( poOpenInfo->pabyHeader + 4, jp2_box_jp, + sizeof(jp2_box_jp) ) == 0 + ) ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* JP2OpenJPEGFindCodeStream() */ +/************************************************************************/ + +static vsi_l_offset JP2OpenJPEGFindCodeStream( VSILFILE* fp, + vsi_l_offset* pnLength ) +{ + vsi_l_offset nCodeStreamStart = 0; + vsi_l_offset nCodeStreamLength = 0; + + VSIFSeekL(fp, 0, SEEK_SET); + GByte abyHeader[16]; + VSIFReadL(abyHeader, 1, 16, fp); + + if (memcmp( abyHeader, jpc_header, sizeof(jpc_header) ) == 0) + { + VSIFSeekL(fp, 0, SEEK_END); + nCodeStreamLength = VSIFTellL(fp); + } + else if (memcmp( abyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp) ) == 0) + { + /* Find offset of first jp2c box */ + GDALJP2Box oBox( fp ); + if( oBox.ReadFirst() ) + { + while( strlen(oBox.GetType()) > 0 ) + { + if( EQUAL(oBox.GetType(),"jp2c") ) + { + nCodeStreamStart = VSIFTellL(fp); + nCodeStreamLength = oBox.GetDataLength(); + break; + } + + if (!oBox.ReadNext()) + break; + } + } + } + *pnLength = nCodeStreamLength; + return nCodeStreamStart; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *JP2OpenJPEGDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if (!Identify(poOpenInfo) || poOpenInfo->fpL == NULL) + return NULL; + + /* Detect which codec to use : J2K or JP2 ? */ + vsi_l_offset nCodeStreamLength = 0; + vsi_l_offset nCodeStreamStart = JP2OpenJPEGFindCodeStream(poOpenInfo->fpL, + &nCodeStreamLength); + + if( nCodeStreamStart == 0 && nCodeStreamLength == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "No code-stream in JP2 file"); + return NULL; + } + + OPJ_CODEC_FORMAT eCodecFormat = (nCodeStreamStart == 0) ? OPJ_CODEC_J2K : OPJ_CODEC_JP2; + + + opj_codec_t* pCodec; + + pCodec = opj_create_decompress(OPJ_CODEC_J2K); + + opj_set_info_handler(pCodec, JP2OpenJPEGDataset_InfoCallback,NULL); + opj_set_warning_handler(pCodec, JP2OpenJPEGDataset_WarningCallback, NULL); + opj_set_error_handler(pCodec, JP2OpenJPEGDataset_ErrorCallback,NULL); + + opj_dparameters_t parameters; + opj_set_default_decoder_parameters(¶meters); + + if (! opj_setup_decoder(pCodec,¶meters)) + { + return NULL; + } + + JP2OpenJPEGFile sJP2OpenJPEGFile; + sJP2OpenJPEGFile.fp = poOpenInfo->fpL; + sJP2OpenJPEGFile.nBaseOffset = nCodeStreamStart; + opj_stream_t * pStream = JP2OpenJPEGCreateReadStream(&sJP2OpenJPEGFile, + nCodeStreamLength); + + opj_image_t * psImage = NULL; + + if(!opj_read_header(pStream,pCodec,&psImage)) + { + CPLError(CE_Failure, CPLE_AppDefined, "opj_read_header() failed"); + opj_destroy_codec(pCodec); + opj_stream_destroy(pStream); + opj_image_destroy(psImage); + return NULL; + } + + opj_codestream_info_v2_t* pCodeStreamInfo = opj_get_cstr_info(pCodec); + OPJ_UINT32 nTileW,nTileH; + nTileW = pCodeStreamInfo->tdx; + nTileH = pCodeStreamInfo->tdy; +#ifdef DEBUG + OPJ_UINT32 nX0,nY0; + OPJ_UINT32 nTilesX,nTilesY; + nX0 = pCodeStreamInfo->tx0; + nY0 = pCodeStreamInfo->ty0; + nTilesX = pCodeStreamInfo->tw; + nTilesY = pCodeStreamInfo->th; + int mct = pCodeStreamInfo->m_default_tile_info.mct; +#endif + int numResolutions = pCodeStreamInfo->m_default_tile_info.tccp_info[0].numresolutions; + opj_destroy_cstr_info(&pCodeStreamInfo); + + if (psImage == NULL) + { + opj_destroy_codec(pCodec); + opj_stream_destroy(pStream); + opj_image_destroy(psImage); + return NULL; + } + +#ifdef DEBUG + int i; + CPLDebug("OPENJPEG", "nX0 = %u", nX0); + CPLDebug("OPENJPEG", "nY0 = %u", nY0); + CPLDebug("OPENJPEG", "nTileW = %u", nTileW); + CPLDebug("OPENJPEG", "nTileH = %u", nTileH); + CPLDebug("OPENJPEG", "nTilesX = %u", nTilesX); + CPLDebug("OPENJPEG", "nTilesY = %u", nTilesY); + CPLDebug("OPENJPEG", "mct = %d", mct); + CPLDebug("OPENJPEG", "psImage->x0 = %u", psImage->x0); + CPLDebug("OPENJPEG", "psImage->y0 = %u", psImage->y0); + CPLDebug("OPENJPEG", "psImage->x1 = %u", psImage->x1); + CPLDebug("OPENJPEG", "psImage->y1 = %u", psImage->y1); + CPLDebug("OPENJPEG", "psImage->numcomps = %d", psImage->numcomps); + //CPLDebug("OPENJPEG", "psImage->color_space = %d", psImage->color_space); + CPLDebug("OPENJPEG", "numResolutions = %d", numResolutions); + for(i=0;i<(int)psImage->numcomps;i++) + { + CPLDebug("OPENJPEG", "psImage->comps[%d].dx = %u", i, psImage->comps[i].dx); + CPLDebug("OPENJPEG", "psImage->comps[%d].dy = %u", i, psImage->comps[i].dy); + CPLDebug("OPENJPEG", "psImage->comps[%d].x0 = %u", i, psImage->comps[i].x0); + CPLDebug("OPENJPEG", "psImage->comps[%d].y0 = %u", i, psImage->comps[i].y0); + CPLDebug("OPENJPEG", "psImage->comps[%d].w = %u", i, psImage->comps[i].w); + CPLDebug("OPENJPEG", "psImage->comps[%d].h = %u", i, psImage->comps[i].h); + CPLDebug("OPENJPEG", "psImage->comps[%d].resno_decoded = %d", i, psImage->comps[i].resno_decoded); + CPLDebug("OPENJPEG", "psImage->comps[%d].factor = %d", i, psImage->comps[i].factor); + CPLDebug("OPENJPEG", "psImage->comps[%d].prec = %d", i, psImage->comps[i].prec); + CPLDebug("OPENJPEG", "psImage->comps[%d].sgnd = %d", i, psImage->comps[i].sgnd); + } +#endif + + if (psImage->x1 <= psImage->x0 || + psImage->y1 <= psImage->y0 || + psImage->numcomps == 0 || + (psImage->comps[0].w >> 31) != 0 || + (psImage->comps[0].h >> 31) != 0 || + (nTileW >> 31) != 0 || + (nTileH >> 31) != 0 || + psImage->comps[0].w != psImage->x1 - psImage->x0 || + psImage->comps[0].h != psImage->y1 - psImage->y0) + { + CPLDebug("OPENJPEG", "Unable to handle that image (1)"); + opj_destroy_codec(pCodec); + opj_stream_destroy(pStream); + opj_image_destroy(psImage); + return NULL; + } + + GDALDataType eDataType = GDT_Byte; + if (psImage->comps[0].prec > 16) + { + if (psImage->comps[0].sgnd) + eDataType = GDT_Int32; + else + eDataType = GDT_UInt32; + } + else if (psImage->comps[0].prec > 8) + { + if (psImage->comps[0].sgnd) + eDataType = GDT_Int16; + else + eDataType = GDT_UInt16; + } + + int bIs420 = (psImage->color_space != OPJ_CLRSPC_SRGB && + eDataType == GDT_Byte && + (psImage->numcomps == 3 || psImage->numcomps == 4) && + psImage->comps[1].w == psImage->comps[0].w / 2 && + psImage->comps[1].h == psImage->comps[0].h / 2 && + psImage->comps[2].w == psImage->comps[0].w / 2 && + psImage->comps[2].h == psImage->comps[0].h / 2) && + (psImage->numcomps == 3 || + (psImage->numcomps == 4 && + psImage->comps[3].w == psImage->comps[0].w && + psImage->comps[3].h == psImage->comps[0].h)); + + if (bIs420) + { + CPLDebug("OPENJPEG", "420 format"); + } + else + { + int iBand; + for(iBand = 2; iBand <= (int)psImage->numcomps; iBand ++) + { + if( psImage->comps[iBand-1].w != psImage->comps[0].w || + psImage->comps[iBand-1].h != psImage->comps[0].h ) + { + CPLDebug("OPENJPEG", "Unable to handle that image (2)"); + opj_destroy_codec(pCodec); + opj_stream_destroy(pStream); + opj_image_destroy(psImage); + return NULL; + } + } + } + + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + JP2OpenJPEGDataset *poDS; + int iBand; + + poDS = new JP2OpenJPEGDataset(); + if( eCodecFormat == OPJ_CODEC_JP2 ) + poDS->eAccess = poOpenInfo->eAccess; + poDS->eColorSpace = psImage->color_space; + poDS->nRasterXSize = psImage->x1 - psImage->x0; + poDS->nRasterYSize = psImage->y1 - psImage->y0; + poDS->nBands = psImage->numcomps; + poDS->fp = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + poDS->nCodeStreamStart = nCodeStreamStart; + poDS->nCodeStreamLength = nCodeStreamLength; + poDS->bIs420 = bIs420; + + poDS->bUseSetDecodeArea = + (poDS->nRasterXSize == (int)nTileW && + poDS->nRasterYSize == (int)nTileH && + (poDS->nRasterXSize > 1024 || + poDS->nRasterYSize > 1024)); + + if (poDS->bUseSetDecodeArea) + { + if (nTileW > 1024) nTileW = 1024; + if (nTileH > 1024) nTileH = 1024; + } + + GDALColorTable* poCT = NULL; + +/* -------------------------------------------------------------------- */ +/* Look for color table or cdef box */ +/* -------------------------------------------------------------------- */ + if( eCodecFormat == OPJ_CODEC_JP2 ) + { + GDALJP2Box oBox( poDS->fp ); + if( oBox.ReadFirst() ) + { + while( strlen(oBox.GetType()) > 0 ) + { + if( EQUAL(oBox.GetType(),"jp2h") ) + { + GDALJP2Box oSubBox( poDS->fp ); + + for( oSubBox.ReadFirstChild( &oBox ); + strlen(oSubBox.GetType()) > 0; + oSubBox.ReadNextChild( &oBox ) ) + { + GIntBig nDataLength = oSubBox.GetDataLength(); + if( poCT == NULL && + EQUAL(oSubBox.GetType(),"pclr") && + nDataLength >= 3 && + nDataLength <= 2 + 1 + 4 + 4 * 256 ) + { + GByte* pabyCT = oSubBox.ReadBoxData(); + if( pabyCT != NULL ) + { + int nEntries = (pabyCT[0] << 8) | pabyCT[1]; + int nComponents = pabyCT[2]; + /* CPLDebug("OPENJPEG", "Color table found"); */ + if( nEntries <= 256 && nComponents == 3 ) + { + /*CPLDebug("OPENJPEG", "resol[0] = %d", pabyCT[3]); + CPLDebug("OPENJPEG", "resol[1] = %d", pabyCT[4]); + CPLDebug("OPENJPEG", "resol[2] = %d", pabyCT[5]);*/ + if( pabyCT[3] == 7 && pabyCT[4] == 7 && pabyCT[5] == 7 && + nDataLength == 2 + 1 + 3 + 3 * nEntries ) + { + poCT = new GDALColorTable(); + for(int i=0;iSetColorEntry(i, &sEntry); + } + } + } + else if ( nEntries <= 256 && nComponents == 4 ) + { + if( pabyCT[3] == 7 && pabyCT[4] == 7 && + pabyCT[5] == 7 && pabyCT[6] == 7 && + nDataLength == 2 + 1 + 4 + 4 * nEntries ) + { + poCT = new GDALColorTable(); + for(int i=0;iSetColorEntry(i, &sEntry); + } + } + } + CPLFree(pabyCT); + } + } + /* There's a bug/misfeature in openjpeg: the color_space + only gets set at read tile time */ + else if( EQUAL(oSubBox.GetType(),"colr") && + nDataLength == 7 ) + { + GByte* pabyContent = oSubBox.ReadBoxData(); + if( pabyContent != NULL ) + { + if( pabyContent[0] == 1 /* enumerated colourspace */ ) + { + GUInt32 enumcs = (pabyContent[3] << 24) | + (pabyContent[4] << 16) | + (pabyContent[5] << 8) | + (pabyContent[6]); + if( enumcs == 16 ) + { + poDS->eColorSpace = OPJ_CLRSPC_SRGB; + CPLDebug("OPENJPEG", "SRGB color space"); + } + else if( enumcs == 17 ) + { + poDS->eColorSpace = OPJ_CLRSPC_GRAY; + CPLDebug("OPENJPEG", "Grayscale color space"); + } + else if( enumcs == 18 ) + { + poDS->eColorSpace = OPJ_CLRSPC_SYCC; + CPLDebug("OPENJPEG", "SYCC color space"); + } + else if( enumcs == 20 ) + { + /* Used by J2KP4files/testfiles_jp2/file7.jp2 */ + poDS->eColorSpace = OPJ_CLRSPC_SRGB; + CPLDebug("OPENJPEG", "e-sRGB color space"); + } + else if( enumcs == 21 ) + { + /* Used by J2KP4files/testfiles_jp2/file5.jp2 */ + poDS->eColorSpace = OPJ_CLRSPC_SRGB; + CPLDebug("OPENJPEG", "ROMM-RGB color space"); + } + else + { + poDS->eColorSpace = OPJ_CLRSPC_UNKNOWN; + CPLDebug("OPENJPEG", "Unknown color space"); + } + } + CPLFree(pabyContent); + } + } + /* Check if there's an alpha channel or odd channel attribution */ + else if( EQUAL(oSubBox.GetType(),"cdef") && + nDataLength == 2 + poDS->nBands * 6 ) + { + GByte* pabyContent = oSubBox.ReadBoxData(); + if( pabyContent != NULL ) + { + int nEntries = (pabyContent[0] << 8) | pabyContent[1]; + if( nEntries == poDS->nBands ) + { + poDS->nRedIndex = -1; + poDS->nGreenIndex = -1; + poDS->nBlueIndex = -1; + for(int i=0;inBands;i++) + { + int CNi = (pabyContent[2+6*i] << 8) | pabyContent[2+6*i+1]; + int Typi = (pabyContent[2+6*i+2] << 8) | pabyContent[2+6*i+3]; + int Asoci = (pabyContent[2+6*i+4] << 8) | pabyContent[2+6*i+5]; + if( CNi < 0 || CNi >= poDS->nBands ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Wrong value of CN%d=%d", i, CNi); + break; + } + if( Typi == 0 ) + { + if( Asoci == 1 ) + poDS->nRedIndex = CNi; + else if( Asoci == 2 ) + poDS->nGreenIndex = CNi; + else if( Asoci == 3 ) + poDS->nBlueIndex = CNi; + else if( Asoci < 0 || (Asoci > poDS->nBands && Asoci != 65535) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Wrong value of Asoc%d=%d", i, Asoci); + break; + } + } + else if( Typi == 1 ) + { + poDS->nAlphaIndex = CNi; + } + } + } + else + { + CPLDebug("OPENJPEG", "Unsupported cdef content"); + } + CPLFree(pabyContent); + } + } + } + } + + if (!oBox.ReadNext()) + break; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + { + int bPromoteTo8Bit = ( + iBand == poDS->nAlphaIndex + 1 && + psImage->comps[(poDS->nAlphaIndex==0 && poDS->nBands > 1) ? 1 : 0].prec == 8 && + psImage->comps[poDS->nAlphaIndex ].prec == 1 && + CSLFetchBoolean(poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION", + CSLTestBoolean(CPLGetConfigOption("JP2OPENJPEG_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES"))) ); + if( bPromoteTo8Bit ) + CPLDebug("JP2OpenJPEG", "Alpha band is promoted from 1 bit to 8 bit"); + + JP2OpenJPEGRasterBand* poBand = + new JP2OpenJPEGRasterBand( poDS, iBand, eDataType, + bPromoteTo8Bit ? 8: psImage->comps[iBand-1].prec, + bPromoteTo8Bit, + nTileW, nTileH); + if( iBand == 1 && poCT != NULL ) + poBand->poCT = poCT; + poDS->SetBand( iBand, poBand ); + } + +/* -------------------------------------------------------------------- */ +/* Create overview datasets. */ +/* -------------------------------------------------------------------- */ + int nW = poDS->nRasterXSize; + int nH = poDS->nRasterYSize; + + /* Lower resolutions are not compatible with a color-table */ + if( poCT != NULL ) + numResolutions = 0; + + while (poDS->nOverviewCount+1 < numResolutions && + (nW > 128 || nH > 128) && + (poDS->bUseSetDecodeArea || ((nTileW % 2) == 0 && (nTileH % 2) == 0))) + { + nW /= 2; + nH /= 2; + + poDS->papoOverviewDS = (JP2OpenJPEGDataset**) CPLRealloc( + poDS->papoOverviewDS, + (poDS->nOverviewCount + 1) * sizeof(JP2OpenJPEGDataset*)); + JP2OpenJPEGDataset* poODS = new JP2OpenJPEGDataset(); + poODS->SetDescription( poOpenInfo->pszFilename ); + poODS->iLevel = poDS->nOverviewCount + 1; + poODS->bUseSetDecodeArea = poDS->bUseSetDecodeArea; + poODS->nRedIndex = poDS->nRedIndex; + poODS->nGreenIndex = poDS->nGreenIndex; + poODS->nBlueIndex = poDS->nBlueIndex; + poODS->nAlphaIndex = poDS->nAlphaIndex; + if (!poDS->bUseSetDecodeArea) + { + nTileW /= 2; + nTileH /= 2; + } + else + { + if (nW < (int)nTileW || nH < (int)nTileH) + { + nTileW = nW; + nTileH = nH; + poODS->bUseSetDecodeArea = FALSE; + } + } + + poODS->eColorSpace = poDS->eColorSpace; + poODS->nRasterXSize = nW; + poODS->nRasterYSize = nH; + poODS->nBands = poDS->nBands; + poODS->fp = poDS->fp; + poODS->nCodeStreamStart = nCodeStreamStart; + poODS->nCodeStreamLength = nCodeStreamLength; + poODS->bIs420 = bIs420; + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + { + int bPromoteTo8Bit = ( + iBand == poDS->nAlphaIndex + 1 && + psImage->comps[(poDS->nAlphaIndex==0 && poDS->nBands > 1) ? 1 : 0].prec == 8 && + psImage->comps[poDS->nAlphaIndex].prec == 1 && + CSLFetchBoolean(poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION", + CSLTestBoolean(CPLGetConfigOption("JP2OPENJPEG_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES"))) ); + + poODS->SetBand( iBand, new JP2OpenJPEGRasterBand( poODS, iBand, eDataType, + bPromoteTo8Bit ? 8: psImage->comps[iBand-1].prec, + bPromoteTo8Bit, + nTileW, nTileH ) ); + } + + poDS->papoOverviewDS[poDS->nOverviewCount ++] = poODS; + + } + + opj_destroy_codec(pCodec); + opj_stream_destroy(pStream); + opj_image_destroy(psImage); + pCodec = NULL; + pStream = NULL; + psImage = NULL; + +/* -------------------------------------------------------------------- */ +/* More metadata. */ +/* -------------------------------------------------------------------- */ + if( poDS->nBands > 1 ) + { + poDS->GDALDataset::SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + } + + poOpenInfo->fpL = poDS->fp; + poDS->LoadJP2Metadata(poOpenInfo); + poOpenInfo->fpL = NULL; + + poDS->bHasGeoreferencingAtOpening = + ((poDS->pszProjection != NULL && poDS->pszProjection[0] != '\0' )|| + poDS->nGCPCount != 0 || poDS->bGeoTransformValid); + +/* -------------------------------------------------------------------- */ +/* Vector layers */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nOpenFlags & GDAL_OF_VECTOR ) + { + poDS->LoadVectorLayers( + CSLFetchBoolean(poOpenInfo->papszOpenOptions, "OPEN_REMOTE_GML", FALSE)); + + // If file opened in vector-only mode and there's no vector, + // return + if( (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 && + poDS->GetLayerCount() == 0 ) + { + delete poDS; + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + //poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* WriteBox() */ +/************************************************************************/ + +void JP2OpenJPEGDataset::WriteBox(VSILFILE* fp, GDALJP2Box* poBox) +{ + GUInt32 nLBox; + GUInt32 nTBox; + + if( poBox == NULL ) + return; + + nLBox = (int) poBox->GetDataLength() + 8; + nLBox = CPL_MSBWORD32( nLBox ); + + memcpy(&nTBox, poBox->GetType(), 4); + + VSIFWriteL( &nLBox, 4, 1, fp ); + VSIFWriteL( &nTBox, 4, 1, fp ); + VSIFWriteL(poBox->GetWritableData(), 1, (int) poBox->GetDataLength(), fp); +} + +/************************************************************************/ +/* WriteGDALMetadataBox() */ +/************************************************************************/ + +void JP2OpenJPEGDataset::WriteGDALMetadataBox( VSILFILE* fp, + GDALDataset* poSrcDS, + char** papszOptions ) +{ + GDALJP2Box* poBox = GDALJP2Metadata::CreateGDALMultiDomainMetadataXMLBox( + poSrcDS, CSLFetchBoolean(papszOptions, "MAIN_MD_DOMAIN_ONLY", FALSE)); + if( poBox ) + WriteBox(fp, poBox); + delete poBox; +} + +/************************************************************************/ +/* WriteXMLBoxes() */ +/************************************************************************/ + +void JP2OpenJPEGDataset::WriteXMLBoxes( VSILFILE* fp, GDALDataset* poSrcDS, + CPL_UNUSED char** papszOptions ) +{ + int nBoxes = 0; + GDALJP2Box** papoBoxes = GDALJP2Metadata::CreateXMLBoxes(poSrcDS, &nBoxes); + for(int i=0;i 1 ) + { + nBits ++; + nVal >>= 1; + } + return 1 << nBits; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * JP2OpenJPEGDataset::CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + + if( nBands == 0 || nBands > 16384 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to export files with %d bands. Must be >= 1 and <= 16384", nBands ); + return NULL; + } + + GDALColorTable* poCT = poSrcDS->GetRasterBand(1)->GetColorTable(); + if (poCT != NULL && nBands != 1) + { + CPLError( CE_Failure, CPLE_NotSupported, + "JP2OpenJPEG driver only supports a color table for a single-band dataset"); + return NULL; + } + + GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + int nDataTypeSize = (GDALGetDataTypeSize(eDataType) / 8); + if (eDataType != GDT_Byte && eDataType != GDT_Int16 && eDataType != GDT_UInt16 + && eDataType != GDT_Int32 && eDataType != GDT_UInt32) + { + CPLError( CE_Failure, CPLE_NotSupported, + "JP2OpenJPEG driver only supports creating Byte, GDT_Int16, GDT_UInt16, GDT_Int32, GDT_UInt32"); + return NULL; + } + + int bInspireTG = CSLFetchBoolean(papszOptions, "INSPIRE_TG", FALSE); + +/* -------------------------------------------------------------------- */ +/* Analyze creation options. */ +/* -------------------------------------------------------------------- */ + OPJ_CODEC_FORMAT eCodecFormat = OPJ_CODEC_J2K; + const char* pszCodec = CSLFetchNameValueDef(papszOptions, "CODEC", NULL); + if (pszCodec) + { + if (EQUAL(pszCodec, "JP2")) + eCodecFormat = OPJ_CODEC_JP2; + else if (EQUAL(pszCodec, "J2K")) + eCodecFormat = OPJ_CODEC_J2K; + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unsupported value for CODEC : %s. Defaulting to J2K", + pszCodec); + } + } + else + { + if (strlen(pszFilename) > 4 && + EQUAL(pszFilename + strlen(pszFilename) - 4, ".JP2")) + { + eCodecFormat = OPJ_CODEC_JP2; + } + } + if( eCodecFormat != OPJ_CODEC_JP2 && bInspireTG ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "INSPIRE_TG=YES mandates CODEC=JP2 (TG requirement 21)"); + return NULL; + } + + int nBlockXSize = + atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "1024")); + int nBlockYSize = + atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "1024")); + if (nBlockXSize < 32 || nBlockYSize < 32) + { + CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size"); + return NULL; + } + + if (nXSize < nBlockXSize) + nBlockXSize = nXSize; + if (nYSize < nBlockYSize) + nBlockYSize = nYSize; + + OPJ_PROG_ORDER eProgOrder = OPJ_LRCP; + const char* pszPROGORDER = + CSLFetchNameValueDef(papszOptions, "PROGRESSION", "LRCP"); + if (EQUAL(pszPROGORDER, "LRCP")) + eProgOrder = OPJ_LRCP; + else if (EQUAL(pszPROGORDER, "RLCP")) + eProgOrder = OPJ_RLCP; + else if (EQUAL(pszPROGORDER, "RPCL")) + eProgOrder = OPJ_RPCL; + else if (EQUAL(pszPROGORDER, "PCRL")) + eProgOrder = OPJ_PCRL; + else if (EQUAL(pszPROGORDER, "CPRL")) + eProgOrder = OPJ_CPRL; + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unsupported value for PROGRESSION : %s. Defaulting to LRCP", + pszPROGORDER); + } + + int bIsIrreversible = + ! (CSLFetchBoolean(papszOptions, "REVERSIBLE", poCT != NULL)); + + std::vector adfRates; + const char* pszQuality = CSLFetchNameValueDef(papszOptions, "QUALITY", NULL); + double dfDefaultQuality = ( poCT != NULL ) ? 100.0 : 25.0; + if (pszQuality) + { + char **papszTokens = CSLTokenizeStringComplex( pszQuality, ",", FALSE, FALSE ); + for(int i=0; papszTokens[i] != NULL; i++ ) + { + double dfQuality = CPLAtof(papszTokens[i]); + if (dfQuality > 0 && dfQuality <= 100) + { + double dfRate = 100 / dfQuality; + adfRates.push_back(dfRate); + } + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unsupported value for QUALITY: %s. Defaulting to single-layer, with quality=%.0f", + papszTokens[i], dfDefaultQuality); + adfRates.resize(0); + break; + } + } + if( papszTokens[0] == NULL ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unsupported value for QUALITY: %s. Defaulting to single-layer, with quality=%.0f", + pszQuality, dfDefaultQuality); + } + CSLDestroy(papszTokens); + } + if( adfRates.size() == 0 ) + { + adfRates.push_back(100. / dfDefaultQuality); + } + + if( poCT != NULL && (bIsIrreversible || adfRates[adfRates.size()-1] != 100.0 / 100.0) ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Encoding a dataset with a color table with REVERSIBLE != YES " + "or QUALITY != 100 will likely lead to bad visual results"); + } + + int nMaxTileDim = MAX(nBlockXSize, nBlockYSize); + int nNumResolutions = 1; + /* Pickup a reasonable value compatible with PROFILE_1 requirements */ + while( (nMaxTileDim >> (nNumResolutions-1)) > 128 ) + nNumResolutions ++; + int nMinProfile1Resolutions = nNumResolutions; + const char* pszResolutions = CSLFetchNameValueDef(papszOptions, "RESOLUTIONS", NULL); + if (pszResolutions) + { + nNumResolutions = atoi(pszResolutions); + if (nNumResolutions <= 0 || nNumResolutions >= 32 || + (nMaxTileDim >> nNumResolutions) == 0 ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unsupported value for RESOLUTIONS : %s. Defaulting to %d", + pszResolutions, nMinProfile1Resolutions); + nNumResolutions = nMinProfile1Resolutions; + } + } + + int bSOP = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SOP", "FALSE")); + int bEPH = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "EPH", "FALSE")); + + int nRedBandIndex = -1, nGreenBandIndex = -1, nBlueBandIndex = -1; + int nAlphaBandIndex = -1; + for(int i=0;iGetRasterBand(i+1)->GetColorInterpretation(); + if( eInterp == GCI_RedBand ) + nRedBandIndex = i; + else if( eInterp == GCI_GreenBand ) + nGreenBandIndex = i; + else if( eInterp == GCI_BlueBand ) + nBlueBandIndex = i; + else if( eInterp == GCI_AlphaBand ) + nAlphaBandIndex = i; + } + const char* pszAlpha = CSLFetchNameValue(papszOptions, "ALPHA"); + if( nAlphaBandIndex < 0 && nBands > 1 && pszAlpha != NULL && CSLTestBoolean(pszAlpha) ) + { + nAlphaBandIndex = nBands - 1; + } + + const char* pszYCBCR420 = CSLFetchNameValue(papszOptions, "YCBCR420"); + int bYCBCR420 = FALSE; + if( pszYCBCR420 && CSLTestBoolean(pszYCBCR420) ) + { + if ((nBands == 3 || nBands == 4) && eDataType == GDT_Byte && + nRedBandIndex == 0 && nGreenBandIndex == 1 && nBlueBandIndex == 2) + { + if( ((nXSize % 2) == 0 && (nYSize % 2) == 0 && (nBlockXSize % 2) == 0 && (nBlockYSize % 2) == 0) ) + { + bYCBCR420 = TRUE; + } + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "YCBCR420 unsupported when image size and/or tile size are not multiple of 2"); + } + } + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "YCBCR420 unsupported with this image band count and/or data byte"); + } + } + + const char* pszYCC = CSLFetchNameValue(papszOptions, "YCC"); + int bYCC = ((nBands == 3 || nBands == 4) && eDataType == GDT_Byte && + CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "YCC", "TRUE"))); + + /* TODO: when OpenJPEG 2.2 is released, make this conditional */ + /* Depending on the way OpenJPEG <= r2950 is built, YCC with 4 bands might work on + * Debug mode, but this relies on unreliable stack buffer overflows, so + * better err on the safe side */ + if( bYCC && nBands > 3 ) + { + if( pszYCC != NULL ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "OpenJPEG r2950 and below can generate invalid output with " + "MCT YCC transform and more than 3 bands. Disabling YCC"); + } + bYCC = FALSE; + } + + if( bYCBCR420 && bYCC ) + { + if( pszYCC != NULL ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "YCC unsupported when YCbCr requesting"); + } + bYCC = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Deal with codeblocks size */ +/* -------------------------------------------------------------------- */ + + int nCblockW = atoi(CSLFetchNameValueDef( papszOptions, "CODEBLOCK_WIDTH", "64" )); + int nCblockH = atoi(CSLFetchNameValueDef( papszOptions, "CODEBLOCK_HEIGHT", "64" )); + if( nCblockW < 4 || nCblockW > 1024 || nCblockH < 4 || nCblockH > 1024 ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Invalid values for codeblock size. Defaulting to 64x64"); + nCblockW = 64; + nCblockH = 64; + } + else if( nCblockW * nCblockH > 4096 ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Invalid values for codeblock size. " + "CODEBLOCK_WIDTH * CODEBLOCK_HEIGHT should be <= 4096. " + "Defaulting to 64x64"); + nCblockW = 64; + nCblockH = 64; + } + int nCblockW_po2 = FloorPowerOfTwo(nCblockW); + int nCblockH_po2 = FloorPowerOfTwo(nCblockH); + if( nCblockW_po2 != nCblockW || nCblockH_po2 != nCblockH ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Non power of two values used for codeblock size. " + "Using to %dx%d", + nCblockW_po2, nCblockH_po2); + } + nCblockW = nCblockW_po2; + nCblockH = nCblockH_po2; + +/* -------------------------------------------------------------------- */ +/* Deal with codestream PROFILE */ +/* -------------------------------------------------------------------- */ + const char* pszProfile = CSLFetchNameValueDef( papszOptions, "PROFILE", "AUTO" ); + int bProfile1 = FALSE; + if( EQUAL(pszProfile, "UNRESTRICTED") ) + { + bProfile1 = FALSE; + if( bInspireTG ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "INSPIRE_TG=YES mandates PROFILE=PROFILE_1 (TG requirement 21)"); + return NULL; + } + } + else if( EQUAL(pszProfile, "UNRESTRICTED_FORCED") ) + { + bProfile1 = FALSE; + } + else if( EQUAL(pszProfile, "PROFILE_1_FORCED") ) /* For debug only: can produce inconsistent codestream */ + { + bProfile1 = TRUE; + } + else + { + if( !(EQUAL(pszProfile, "PROFILE_1") || EQUAL(pszProfile, "AUTO")) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unsupported value for PROFILE : %s. Defaulting to AUTO", + pszProfile); + pszProfile = "AUTO"; + } + + bProfile1 = TRUE; + const char* pszReq21OrEmpty = (bInspireTG) ? " (TG requirement 21)" : ""; + if( (nBlockXSize != nXSize || nBlockYSize != nYSize) && + (nBlockXSize != nBlockYSize || nBlockXSize > 1024 || nBlockYSize > 1024 ) ) + { + bProfile1 = FALSE; + if( bInspireTG || EQUAL(pszProfile, "PROFILE_1") ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Tile dimensions incompatible with PROFILE_1%s. " + "Should be whole image or square with dimension <= 1024.", + pszReq21OrEmpty); + return NULL; + } + } + if( (nMaxTileDim >> (nNumResolutions-1)) > 128 ) + { + bProfile1 = FALSE; + if( bInspireTG || EQUAL(pszProfile, "PROFILE_1") ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Number of resolutions incompatible with PROFILE_1%s. " + "Should be at least %d.", + pszReq21OrEmpty, + nMinProfile1Resolutions); + return NULL; + } + } + if( nCblockW > 64 || nCblockH > 64 ) + { + bProfile1 = FALSE; + if( bInspireTG || EQUAL(pszProfile, "PROFILE_1") ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Codeblock width incompatible with PROFILE_1%s. " + "Codeblock width or height should be <= 64.", + pszReq21OrEmpty); + return NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Work out the precision. */ +/* -------------------------------------------------------------------- */ + int nBits; + if( CSLFetchNameValue( papszOptions, "NBITS" ) != NULL ) + { + nBits = atoi(CSLFetchNameValue(papszOptions,"NBITS")); + if( bInspireTG && + !(nBits == 1 || nBits == 8 || nBits == 16 || nBits == 32) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "INSPIRE_TG=YES mandates NBITS=1,8,16 or 32 (TG requirement 24)"); + return NULL; + } + } + else if( poSrcDS->GetRasterBand(1)->GetMetadataItem( "NBITS", "IMAGE_STRUCTURE" ) + != NULL ) + { + nBits = atoi(poSrcDS->GetRasterBand(1)->GetMetadataItem( "NBITS", + "IMAGE_STRUCTURE" )); + if( bInspireTG && + !(nBits == 1 || nBits == 8 || nBits == 16 || nBits == 32) ) + { + /* Implements "NOTE If the original data do not satisfy this " + "requirement, they will be converted in a representation using " + "the next higher power of 2" */ + nBits = GDALGetDataTypeSize(eDataType); + } + } + else + { + nBits = GDALGetDataTypeSize(eDataType); + } + + if( (GDALGetDataTypeSize(eDataType) == 8 && nBits > 8) || + (GDALGetDataTypeSize(eDataType) == 16 && (nBits <= 8 || nBits > 16)) || + (GDALGetDataTypeSize(eDataType) == 32 && (nBits <= 16 || nBits > 32)) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Inconsistent NBITS value with data type. Using %d", + GDALGetDataTypeSize(eDataType)); + } + +/* -------------------------------------------------------------------- */ +/* Georeferencing options */ +/* -------------------------------------------------------------------- */ + + int bGMLJP2Option = CSLFetchBoolean( papszOptions, "GMLJP2", TRUE ); + int nGMLJP2Version = 1; + const char* pszGMLJP2V2Def = CSLFetchNameValue( papszOptions, "GMLJP2V2_DEF" ); + if( pszGMLJP2V2Def != NULL ) + { + bGMLJP2Option = TRUE; + nGMLJP2Version = 2; + if( bInspireTG ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "INSPIRE_TG=YES is only compatible with GMLJP2 v1"); + return NULL; + } + } + int bGeoJP2Option = CSLFetchBoolean( papszOptions, "GeoJP2", TRUE ); + + GDALJP2Metadata oJP2MD; + + int bGeoreferencingCompatOfGeoJP2 = FALSE; + int bGeoreferencingCompatOfGMLJP2 = FALSE; + if( eCodecFormat == OPJ_CODEC_JP2 && (bGMLJP2Option || bGeoJP2Option) ) + { + if( poSrcDS->GetGCPCount() > 0 ) + { + bGeoreferencingCompatOfGeoJP2 = TRUE; + oJP2MD.SetGCPs( poSrcDS->GetGCPCount(), + poSrcDS->GetGCPs() ); + oJP2MD.SetProjection( poSrcDS->GetGCPProjection() ); + } + else + { + const char* pszWKT = poSrcDS->GetProjectionRef(); + if( pszWKT != NULL && pszWKT[0] != '\0' ) + { + bGeoreferencingCompatOfGeoJP2 = TRUE; + oJP2MD.SetProjection( pszWKT ); + } + double adfGeoTransform[6]; + if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None ) + { + bGeoreferencingCompatOfGeoJP2 = TRUE; + oJP2MD.SetGeoTransform( adfGeoTransform ); + } + bGeoreferencingCompatOfGMLJP2 = + ( pszWKT != NULL && pszWKT[0] != '\0' ) && + poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None; + } + if( poSrcDS->GetMetadata("RPC") != NULL ) + { + oJP2MD.SetRPCMD( poSrcDS->GetMetadata("RPC") ); + bGeoreferencingCompatOfGeoJP2 = TRUE; + } + + const char* pszAreaOrPoint = poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT); + oJP2MD.bPixelIsPoint = pszAreaOrPoint != NULL && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT); + + if( bGMLJP2Option && CPLGetConfigOption("GMLJP2OVERRIDE", NULL) != NULL ) + bGeoreferencingCompatOfGMLJP2 = TRUE; + } + + if( CSLFetchNameValue( papszOptions, "GMLJP2" ) != NULL && bGMLJP2Option && + !bGeoreferencingCompatOfGMLJP2 && nGMLJP2Version == 1 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "GMLJP2 box was explicitly required but cannot be written due " + "to lack of georeferencing and/or unsupported georeferencing for GMLJP2"); + } + + if( CSLFetchNameValue( papszOptions, "GeoJP2" ) != NULL && bGeoJP2Option && + !bGeoreferencingCompatOfGeoJP2 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "GeoJP2 box was explicitly required but cannot be written due " + "to lack of georeferencing"); + } + int bGeoBoxesAfter = CSLFetchBoolean(papszOptions, "GEOBOXES_AFTER_JP2C", + bInspireTG); + GDALJP2Box* poGMLJP2Box = NULL; + if( eCodecFormat == OPJ_CODEC_JP2 && bGMLJP2Option && bGeoreferencingCompatOfGMLJP2 ) + { + if( nGMLJP2Version == 1) + poGMLJP2Box = oJP2MD.CreateGMLJP2(nXSize,nYSize); + else + poGMLJP2Box = oJP2MD.CreateGMLJP2V2(nXSize,nYSize,pszGMLJP2V2Def,poSrcDS); + if( poGMLJP2Box == NULL ) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Setup encoder */ +/* -------------------------------------------------------------------- */ + + opj_cparameters_t parameters; + opj_set_default_encoder_parameters(¶meters); + if (bSOP) + parameters.csty |= 0x02; + if (bEPH) + parameters.csty |= 0x04; + parameters.cp_disto_alloc = 1; + parameters.tcp_numlayers = (int)adfRates.size(); + for(int i=0;i<(int)adfRates.size();i++) + parameters.tcp_rates[i] = (float) adfRates[i]; + parameters.cp_tx0 = 0; + parameters.cp_ty0 = 0; + parameters.tile_size_on = TRUE; + parameters.cp_tdx = nBlockXSize; + parameters.cp_tdy = nBlockYSize; + parameters.irreversible = bIsIrreversible; + parameters.numresolution = nNumResolutions; + parameters.prog_order = eProgOrder; + parameters.tcp_mct = bYCC; + parameters.cblockw_init = nCblockW; + parameters.cblockh_init = nCblockH; + + /* Add precincts */ + const char* pszPrecincts = CSLFetchNameValueDef(papszOptions, "PRECINCTS", + "{512,512},{256,512},{128,512},{64,512},{32,512},{16,512},{8,512},{4,512},{2,512}"); + char **papszTokens = CSLTokenizeStringComplex( pszPrecincts, "{},", FALSE, FALSE ); + int nPrecincts = CSLCount(papszTokens) / 2; + for(int i=0;i= 20100 + parameters.rsiz = OPJ_PROFILE_1; +#else + /* This is a hack but this works */ + parameters.cp_rsiz = (OPJ_RSIZ_CAPABILITIES) 2; /* Profile 1 */ +#endif + } + + opj_image_cmptparm_t* pasBandParams = + (opj_image_cmptparm_t*)CPLMalloc(nBands * sizeof(opj_image_cmptparm_t)); + int iBand; + int bSamePrecision = TRUE; + int b1BitAlpha = FALSE; + for(iBand=0;iBandGetRasterBand(iBand+1)->GetMetadataItem( + "NBITS", "IMAGE_STRUCTURE"); + /* Recommendation 38 In the case of an opacity channel, the bit depth should be 1-bit. */ + if( iBand == nAlphaBandIndex && + ((pszNBits != NULL && EQUAL(pszNBits, "1")) || + CSLFetchBoolean(papszOptions, "1BIT_ALPHA", bInspireTG)) ) + { + if( iBand != nBands - 1 && nBits != 1 ) + { + /* Might be a bug in openjpeg, but it seems that if the alpha */ + /* band is the first one, it would select 1-bit for all channels... */ + CPLError(CE_Warning, CPLE_NotSupported, + "Cannot output 1-bit alpha channel if it is not the last one"); + } + else + { + CPLDebug("OPENJPEG", "Using 1-bit alpha channel"); + pasBandParams[iBand].sgnd = 0; + pasBandParams[iBand].prec = 1; + bSamePrecision = FALSE; + b1BitAlpha = TRUE; + } + } + } + + if( bInspireTG && nAlphaBandIndex >= 0 && !b1BitAlpha ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "INSPIRE_TG=YES recommends 1BIT_ALPHA=YES (Recommendation 38)"); + } + + /* Always ask OpenJPEG to do codestream only. We will take care */ + /* of JP2 boxes */ + opj_codec_t* pCodec = opj_create_compress(OPJ_CODEC_J2K); + if (pCodec == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "opj_create_compress() failed"); + CPLFree(pasBandParams); + delete poGMLJP2Box; + return NULL; + } + + opj_set_info_handler(pCodec, JP2OpenJPEGDataset_InfoCallback,NULL); + opj_set_warning_handler(pCodec, JP2OpenJPEGDataset_WarningCallback,NULL); + opj_set_error_handler(pCodec, JP2OpenJPEGDataset_ErrorCallback,NULL); + + OPJ_COLOR_SPACE eColorSpace = OPJ_CLRSPC_GRAY; + + if( bYCBCR420 ) + { + eColorSpace = OPJ_CLRSPC_SYCC; + } + else if( (nBands == 3 || nBands == 4) && + nRedBandIndex >= 0 && nGreenBandIndex >= 0 && nBlueBandIndex >= 0 ) + { + eColorSpace = OPJ_CLRSPC_SRGB; + } + else if (poCT != NULL) + { + eColorSpace = OPJ_CLRSPC_SRGB; + } + + opj_image_t* psImage = opj_image_tile_create(nBands,pasBandParams, + eColorSpace); + + if (psImage == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "opj_image_tile_create() failed"); + opj_destroy_codec(pCodec); + CPLFree(pasBandParams); + pasBandParams = NULL; + delete poGMLJP2Box; + return NULL; + } + + psImage->x0 = 0; + psImage->y0 = 0; + psImage->x1 = nXSize; + psImage->y1 = nYSize; + psImage->color_space = eColorSpace; + psImage->numcomps = nBands; + + if (!opj_setup_encoder(pCodec,¶meters,psImage)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "opj_setup_encoder() failed"); + opj_image_destroy(psImage); + opj_destroy_codec(pCodec); + CPLFree(pasBandParams); + pasBandParams = NULL; + delete poGMLJP2Box; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + + const char* pszAccess = EQUALN(pszFilename, "/vsisubfile/", 12) ? "r+b" : "w+b"; + VSILFILE* fp = VSIFOpenL(pszFilename, pszAccess); + if (fp == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot create file"); + opj_image_destroy(psImage); + opj_destroy_codec(pCodec); + CPLFree(pasBandParams); + pasBandParams = NULL; + delete poGMLJP2Box; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Add JP2 boxes. */ +/* -------------------------------------------------------------------- */ + vsi_l_offset nStartJP2C = 0; + int bUseXLBoxes = FALSE; + + if( eCodecFormat == OPJ_CODEC_JP2 ) + { + GDALJP2Box jPBox(fp); + jPBox.SetType("jP "); + jPBox.AppendWritableData(4, "\x0D\x0A\x87\x0A"); + WriteBox(fp, &jPBox); + + GDALJP2Box ftypBox(fp); + ftypBox.SetType("ftyp"); + ftypBox.AppendWritableData(4, "jp2 "); /* Branding */ + ftypBox.AppendUInt32(0); /* minimum version */ + ftypBox.AppendWritableData(4, "jp2 "); /* Compatibility list: first value */ + + int bJPXOption = CSLFetchBoolean( papszOptions, "JPX", TRUE ); + if( bInspireTG && poGMLJP2Box != NULL && !bJPXOption ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "INSPIRE_TG=YES implies following GMLJP2 specification which " + "recommends advertize reader requirement 67 feature, and thus JPX capability"); + } + else if( poGMLJP2Box != NULL && bJPXOption ) + { + /* GMLJP2 uses lbl and asoc boxes, which are JPEG2000 Part II spec */ + /* advertizing jpx is required per 8.1 of 05-047r3 GMLJP2 */ + ftypBox.AppendWritableData(4, "jpx "); /* Compatibility list: second value */ + } + WriteBox(fp, &ftypBox); + + int bIPR = poSrcDS->GetMetadata("xml:IPR") != NULL && + CSLFetchBoolean(papszOptions, "WRITE_METADATA", FALSE); + + /* Reader requirement box */ + if( poGMLJP2Box != NULL && bJPXOption ) + { + GDALJP2Box rreqBox(fp); + rreqBox.SetType("rreq"); + rreqBox.AppendUInt8(1); /* ML = 1 byte for mask length */ + + rreqBox.AppendUInt8(0x80 | 0x40 | (bIPR ? 0x20 : 0)); /* FUAM */ + rreqBox.AppendUInt8(0x80); /* DCM */ + + rreqBox.AppendUInt16(2 + bIPR); /* NSF: Number of standard features */ + + rreqBox.AppendUInt16((bProfile1) ? 4 : 5); /* SF0 : PROFILE 1 or PROFILE 2 */ + rreqBox.AppendUInt8(0x80); /* SM0 */ + + rreqBox.AppendUInt16(67); /* SF1 : GMLJP2 box */ + rreqBox.AppendUInt8(0x40); /* SM1 */ + + if( bIPR ) + { + rreqBox.AppendUInt16(35); /* SF2 : IPR metadata */ + rreqBox.AppendUInt8(0x20); /* SM2 */ + } + rreqBox.AppendUInt16(0); /* NVF */ + WriteBox(fp, &rreqBox); + } + + GDALJP2Box ihdrBox(fp); + ihdrBox.SetType("ihdr"); + ihdrBox.AppendUInt32(nYSize); + ihdrBox.AppendUInt32(nXSize); + ihdrBox.AppendUInt16(nBands); + GByte BPC; + if( bSamePrecision ) + BPC = (pasBandParams[0].prec-1) | (pasBandParams[0].sgnd << 7); + else + BPC = 255; + ihdrBox.AppendUInt8(BPC); + ihdrBox.AppendUInt8(7); /* C=Compression type: fixed value */ + ihdrBox.AppendUInt8(0); /* UnkC: 0= colourspace of the image is known */ + /*and correctly specified in the Colourspace Specification boxes within the file */ + ihdrBox.AppendUInt8(bIPR); /* IPR: 0=no intellectual property, 1=IPR box */ + + GDALJP2Box bpccBox(fp); + if( !bSamePrecision ) + { + bpccBox.SetType("bpcc"); + for(int i=0;iGetColorEntryCount()); + nCTComponentCount = atoi(CSLFetchNameValueDef(papszOptions, "CT_COMPONENTS", "0")); + if( bInspireTG ) + { + if( nCTComponentCount != 0 && nCTComponentCount != 3 ) + CPLError(CE_Warning, CPLE_AppDefined, "Inspire TG mandates 3 components for color table"); + else + nCTComponentCount = 3; + } + else if( nCTComponentCount != 3 && nCTComponentCount != 4 ) + { + nCTComponentCount = 3; + for(int i=0;iGetColorEntry(i); + if( psEntry->c4 != 255 ) + { + CPLDebug("OPENJPEG", "Color table has at least one non-opaque value. " + "This may cause compatibility problems with some readers. " + "In which case use CT_COMPONENTS=3 creation option"); + nCTComponentCount = 4; + break; + } + } + } + nRedBandIndex = 0; + nGreenBandIndex = 1; + nBlueBandIndex = 2; + nAlphaBandIndex = (nCTComponentCount == 4) ? 3 : -1; + + pclrBox.AppendUInt16(nEntries); + pclrBox.AppendUInt8(nCTComponentCount); /* NPC: Number of components */ + for(int i=0;iGetColorEntry(i); + pclrBox.AppendUInt8((GByte)psEntry->c1); + pclrBox.AppendUInt8((GByte)psEntry->c2); + pclrBox.AppendUInt8((GByte)psEntry->c3); + if( nCTComponentCount == 4 ) + pclrBox.AppendUInt8((GByte)psEntry->c4); + } + + cmapBox.SetType("cmap"); + for(int i=0;i= 0) + { + cdefBox.SetType("cdef"); + int nComponents = (nCTComponentCount == 4) ? 4 : nBands; + cdefBox.AppendUInt16(nComponents); + for(int i=0;iGetMetadataItem("TIFFTAG_XRESOLUTION") != NULL + && poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION") != NULL + && poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT") != NULL ) + { + dfXRes = + CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION")); + dfYRes = + CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION")); + nResUnit = atoi(poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT")); +#define PIXELS_PER_INCH 2 +#define PIXELS_PER_CM 3 + + if( nResUnit == PIXELS_PER_INCH ) + { + // convert pixels per inch to pixels per cm. + dfXRes = dfXRes * 39.37 / 100.0; + dfYRes = dfYRes * 39.37 / 100.0; + nResUnit = PIXELS_PER_CM; + } + + if( nResUnit == PIXELS_PER_CM && + dfXRes > 0 && dfYRes > 0 && + dfXRes < 65535 && dfYRes < 65535 ) + { + /* Format a resd box and embed it inside a res box */ + GDALJP2Box oResd; + oResd.SetType("resd"); + + int nYDenom = 1; + while (nYDenom < 32767 && dfYRes < 32767) + { + dfYRes *= 2; + nYDenom *= 2; + } + int nXDenom = 1; + while (nXDenom < 32767 && dfXRes < 32767) + { + dfXRes *= 2; + nXDenom *= 2; + } + + oResd.AppendUInt16((GUInt16)dfYRes); + oResd.AppendUInt16((GUInt16)nYDenom); + oResd.AppendUInt16((GUInt16)dfXRes); + oResd.AppendUInt16((GUInt16)nXDenom); + oResd.AppendUInt8(2); /* vertical exponent */ + oResd.AppendUInt8(2); /* horizontal exponent */ + + GDALJP2Box* poResd = &oResd; + poRes = GDALJP2Box::CreateAsocBox( 1, &poResd ); + poRes->SetType("res "); + } + } + + /* Build and write jp2h super box now */ + GDALJP2Box* apoBoxes[7]; + int nBoxes = 1; + apoBoxes[0] = &ihdrBox; + if( bpccBox.GetDataLength() ) + apoBoxes[nBoxes++] = &bpccBox; + apoBoxes[nBoxes++] = &colrBox; + if( pclrBox.GetDataLength() ) + apoBoxes[nBoxes++] = &pclrBox; + if( cmapBox.GetDataLength() ) + apoBoxes[nBoxes++] = &cmapBox; + if( cdefBox.GetDataLength() ) + apoBoxes[nBoxes++] = &cdefBox; + if( poRes ) + apoBoxes[nBoxes++] = poRes; + GDALJP2Box* psJP2HBox = GDALJP2Box::CreateSuperBox( "jp2h", + nBoxes, + apoBoxes ); + WriteBox(fp, psJP2HBox); + delete psJP2HBox; + delete poRes; + + if( !bGeoBoxesAfter ) + { + if( bGeoJP2Option && bGeoreferencingCompatOfGeoJP2 ) + { + GDALJP2Box* poBox = oJP2MD.CreateJP2GeoTIFF(); + WriteBox(fp, poBox); + delete poBox; + } + + if( CSLFetchBoolean(papszOptions, "WRITE_METADATA", FALSE) && + !CSLFetchBoolean(papszOptions, "MAIN_MD_DOMAIN_ONLY", FALSE) ) + { + WriteXMPBox(fp, poSrcDS, papszOptions); + } + + if( CSLFetchBoolean(papszOptions, "WRITE_METADATA", FALSE) ) + { + if( !CSLFetchBoolean(papszOptions, "MAIN_MD_DOMAIN_ONLY", FALSE) ) + WriteXMLBoxes(fp, poSrcDS, papszOptions); + WriteGDALMetadataBox(fp, poSrcDS, papszOptions); + } + + if( poGMLJP2Box != NULL ) + { + WriteBox(fp, poGMLJP2Box); + } + } + } + CPLFree(pasBandParams); + pasBandParams = NULL; + +/* -------------------------------------------------------------------- */ +/* Try lossless reuse of an existing JPEG2000 codestream */ +/* -------------------------------------------------------------------- */ + vsi_l_offset nCodeStreamLength = 0; + vsi_l_offset nCodeStreamStart = 0; + VSILFILE* fpSrc = NULL; + if( CSLFetchBoolean(papszOptions, "USE_SRC_CODESTREAM", FALSE) ) + { + CPLString osSrcFilename( poSrcDS->GetDescription() ); + if( poSrcDS->GetDriver() != NULL && + poSrcDS->GetDriver() == GDALGetDriverByName("VRT") ) + { + VRTDataset* poVRTDS = (VRTDataset* )poSrcDS; + GDALDataset* poSimpleSourceDS = poVRTDS->GetSingleSimpleSource(); + if( poSimpleSourceDS ) + osSrcFilename = poSimpleSourceDS->GetDescription(); + } + + fpSrc = VSIFOpenL( osSrcFilename, "rb" ); + if( fpSrc ) + { + nCodeStreamStart = JP2OpenJPEGFindCodeStream(fpSrc, + &nCodeStreamLength); + } + if( nCodeStreamLength == 0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "USE_SRC_CODESTREAM=YES specified, but no codestream found"); + } + } + + if( eCodecFormat == OPJ_CODEC_JP2 ) + { + // Start codestream box + nStartJP2C = VSIFTellL(fp); + if( nCodeStreamLength ) + bUseXLBoxes = ((vsi_l_offset)(GUInt32)nCodeStreamLength != nCodeStreamLength); + else + bUseXLBoxes = CSLFetchBoolean(papszOptions, "JP2C_XLBOX", FALSE) || /* For debugging */ + (GIntBig)nXSize * nYSize * nBands * nDataTypeSize / adfRates[adfRates.size()-1] > 4e9; + GUInt32 nLBox = (bUseXLBoxes) ? 1 : 0; + CPL_MSBPTR32(&nLBox); + VSIFWriteL(&nLBox, 1, 4, fp); + VSIFWriteL("jp2c", 1, 4, fp); + if( bUseXLBoxes ) + { + GUIntBig nXLBox = 0; + VSIFWriteL(&nXLBox, 1, 8, fp); + } + } + +/* -------------------------------------------------------------------- */ +/* Do lossless reuse of an existing JPEG2000 codestream */ +/* -------------------------------------------------------------------- */ + if( fpSrc ) + { + const char* apszIgnoredOptions[] = { + "BLOCKXSIZE", "BLOCKYSIZE", "QUALITY", "REVERSIBLE", + "RESOLUTIONS", "PROGRESSION", "SOP", "EPH", + "YCBCR420", "YCC", "NBITS", "1BIT_ALPHA", "PRECINCTS", + "TILEPARTS", "CODEBLOCK_WIDTH", "CODEBLOCK_HEIGHT", NULL }; + for( int i = 0; apszIgnoredOptions[i]; i ++) + { + if( CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Option %s ignored when USE_SRC_CODESTREAM=YES", + apszIgnoredOptions[i]); + } + } + GByte abyBuffer[4096]; + VSIFSeekL( fpSrc, nCodeStreamStart, SEEK_SET ); + vsi_l_offset nRead = 0; + while( nRead < nCodeStreamLength ) + { + int nToRead = ( nCodeStreamLength-nRead > 4096 ) ? 4049 : + (int)(nCodeStreamLength-nRead); + if( (int)VSIFReadL(abyBuffer, 1, nToRead, fpSrc) != nToRead ) + { + VSIFCloseL(fp); + VSIFCloseL(fpSrc); + opj_image_destroy(psImage); + opj_destroy_codec(pCodec); + delete poGMLJP2Box; + return NULL; + } + if( nRead == 0 && (pszProfile || bInspireTG) && + abyBuffer[2] == 0xFF && abyBuffer[3] == 0x51 ) + { + if( EQUAL(pszProfile, "UNRESTRICTED") ) + { + abyBuffer[6] = 0; + abyBuffer[7] = 0; + } + else if( EQUAL(pszProfile, "PROFILE_1") || bInspireTG ) + { + // TODO: ultimately we should check that we can really set Profile 1 + abyBuffer[6] = 0; + abyBuffer[7] = 2; + } + } + if( (int)VSIFWriteL(abyBuffer, 1, nToRead, fp) != nToRead || + !pfnProgress( (nRead + nToRead) * 1.0 / nCodeStreamLength, + NULL, pProgressData ) ) + { + VSIFCloseL(fp); + VSIFCloseL(fpSrc); + opj_image_destroy(psImage); + opj_destroy_codec(pCodec); + delete poGMLJP2Box; + return NULL; + } + nRead += nToRead; + } + + VSIFCloseL(fpSrc); + } + else + { + opj_stream_t * pStream; + JP2OpenJPEGFile sJP2OpenJPEGFile; + sJP2OpenJPEGFile.fp = fp; + sJP2OpenJPEGFile.nBaseOffset = VSIFTellL(fp); + pStream = opj_stream_create(1024*1024, FALSE); + opj_stream_set_write_function(pStream, JP2OpenJPEGDataset_Write); + opj_stream_set_seek_function(pStream, JP2OpenJPEGDataset_Seek); + opj_stream_set_skip_function(pStream, JP2OpenJPEGDataset_Skip); +#if defined(OPENJPEG_VERSION) && OPENJPEG_VERSION >= 20100 + opj_stream_set_user_data(pStream, &sJP2OpenJPEGFile, NULL); +#else + opj_stream_set_user_data(pStream, &sJP2OpenJPEGFile); +#endif + + if (!opj_start_compress(pCodec,psImage,pStream)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "opj_start_compress() failed"); + opj_stream_destroy(pStream); + opj_image_destroy(psImage); + opj_destroy_codec(pCodec); + VSIFCloseL(fp); + delete poGMLJP2Box; + return NULL; + } + + int nTilesX = (nXSize + nBlockXSize - 1) / nBlockXSize; + int nTilesY = (nYSize + nBlockYSize - 1) / nBlockYSize; + + GUIntBig nTileSize = (GUIntBig)nBlockXSize * nBlockYSize * nBands * nDataTypeSize; + GByte* pTempBuffer; + if( nTileSize != (GUIntBig)(GUInt32)nTileSize ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Tile size exceeds 4GB"); + pTempBuffer = NULL; + } + else + { + pTempBuffer = (GByte*)VSIMalloc((size_t)nTileSize); + } + if (pTempBuffer == NULL) + { + opj_stream_destroy(pStream); + opj_image_destroy(psImage); + opj_destroy_codec(pCodec); + VSIFCloseL(fp); + delete poGMLJP2Box; + return NULL; + } + + GByte* pYUV420Buffer = NULL; + if (bYCBCR420) + { + pYUV420Buffer =(GByte*)VSIMalloc(3 * nBlockXSize * nBlockYSize / 2 + + ((nBands == 4) ? nBlockXSize * nBlockYSize : 0)); + if (pYUV420Buffer == NULL) + { + opj_stream_destroy(pStream); + opj_image_destroy(psImage); + opj_destroy_codec(pCodec); + CPLFree(pTempBuffer); + VSIFCloseL(fp); + delete poGMLJP2Box; + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Iterate over the tiles */ +/* -------------------------------------------------------------------- */ + pfnProgress( 0.0, NULL, pProgressData ); + + CPLErr eErr = CE_None; + int nBlockXOff, nBlockYOff; + int iTile = 0; + for(nBlockYOff=0;eErr == CE_None && nBlockYOffRasterIO(GF_Read, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + nWidthToRead, nHeightToRead, + pTempBuffer, nWidthToRead, nHeightToRead, + eDataType, + nBands, NULL, + 0,0,0,NULL); + if( b1BitAlpha ) + { + for(int i=0;i 4GB"); + bGeoreferencingCompatOfGeoJP2 = FALSE; + delete poGMLJP2Box; + poGMLJP2Box = NULL; + } + } + else + { + VSIFSeekL(fp, nStartJP2C, SEEK_SET); + CPL_MSBPTR32(&nBoxSize32); + VSIFWriteL(&nBoxSize32, 4, 1, fp); + } + } + VSIFSeekL(fp, 0, SEEK_END); + + if( CSLFetchBoolean(papszOptions, "WRITE_METADATA", FALSE) ) + { + WriteIPRBox(fp, poSrcDS, papszOptions); + } + + if( bGeoBoxesAfter ) + { + if( poGMLJP2Box != NULL ) + { + WriteBox(fp, poGMLJP2Box); + } + + if( CSLFetchBoolean(papszOptions, "WRITE_METADATA", FALSE) ) + { + if( !CSLFetchBoolean(papszOptions, "MAIN_MD_DOMAIN_ONLY", FALSE) ) + WriteXMLBoxes(fp, poSrcDS, papszOptions); + WriteGDALMetadataBox(fp, poSrcDS, papszOptions); + } + + if( bGeoJP2Option && bGeoreferencingCompatOfGeoJP2 ) + { + GDALJP2Box* poBox = oJP2MD.CreateJP2GeoTIFF(); + WriteBox(fp, poBox); + delete poBox; + } + + if( CSLFetchBoolean(papszOptions, "WRITE_METADATA", FALSE) && + !CSLFetchBoolean(papszOptions, "MAIN_MD_DOMAIN_ONLY", FALSE) ) + { + WriteXMPBox(fp, poSrcDS, papszOptions); + } + } + } + + VSIFCloseL(fp); + delete poGMLJP2Box; + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + + GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); + JP2OpenJPEGDataset *poDS = (JP2OpenJPEGDataset*) JP2OpenJPEGDataset::Open(&oOpenInfo); + + if( poDS ) + { + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT & (~GCIF_METADATA) ); + + /* Only write relevant metadata to PAM, and if needed */ + if( !CSLFetchBoolean(papszOptions, "WRITE_METADATA", FALSE) ) + { + char** papszSrcMD = CSLDuplicate(poSrcDS->GetMetadata()); + papszSrcMD = CSLSetNameValue(papszSrcMD, GDALMD_AREA_OR_POINT, NULL); + papszSrcMD = CSLSetNameValue(papszSrcMD, "Corder", NULL); + for(char** papszSrcMDIter = papszSrcMD; + papszSrcMDIter && *papszSrcMDIter; ) + { + /* Remove entries like KEY= (without value) */ + if( (*papszSrcMDIter)[0] && + (*papszSrcMDIter)[strlen((*papszSrcMDIter))-1] == '=' ) + { + CPLFree(*papszSrcMDIter); + memmove(papszSrcMDIter, papszSrcMDIter + 1, + sizeof(char*) * (CSLCount(papszSrcMDIter + 1) + 1)); + } + else + ++papszSrcMDIter; + } + char** papszMD = CSLDuplicate(poDS->GetMetadata()); + papszMD = CSLSetNameValue(papszMD, GDALMD_AREA_OR_POINT, NULL); + if( papszSrcMD && papszSrcMD[0] != NULL && + CSLCount(papszSrcMD) != CSLCount(papszMD) ) + { + poDS->SetMetadata(papszSrcMD); + } + CSLDestroy(papszSrcMD); + CSLDestroy(papszMD); + } + } + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_JP2OpenJPEG() */ +/************************************************************************/ + +void GDALRegister_JP2OpenJPEG() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("JP2OpenJPEG driver")) + return; + + if( GDALGetDriverByName( "JP2OpenJPEG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "JP2OpenJPEG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "JPEG-2000 driver based on OpenJPEG library" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_jp2openjpeg.html" ); + poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/jp2" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jp2" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "jp2 j2k" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" " +" " +" " +" " ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = JP2OpenJPEGDataset::Identify; + poDriver->pfnOpen = JP2OpenJPEGDataset::Open; + poDriver->pfnCreateCopy = JP2OpenJPEGDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/ozi/GNUmakefile b/bazaar/plugin/gdal/frmts/ozi/GNUmakefile new file mode 100644 index 000000000..f773ebfee --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ozi/GNUmakefile @@ -0,0 +1,19 @@ + +OBJ = ozidataset.o + +include ../../GDALmake.opt + +ifeq ($(LIBZ_SETTING),internal) +XTRA_OPT = -I../zlib +else +XTRA_OPT = +endif + +CPPFLAGS := $(CPPFLAGS) $(XTRA_OPT) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/ozi/frmt_ozi.html b/bazaar/plugin/gdal/frmts/ozi/frmt_ozi.html new file mode 100644 index 000000000..f34c6d473 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ozi/frmt_ozi.html @@ -0,0 +1,23 @@ + + +OZI -- OZF2/OZFX3 raster + + + + +

    OZI -- OZF2/OZFX3 raster

    + +(Available for GDAL >= 1.8.0) +

    +GDAL supports reading OZF2/OZFX3 raster datasets. +

    Either the image file or the .map file can be passed to GDAL. To retrieve georeferencing, +you need to specify the .map file. + +

    See also

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/ozi/makefile.vc b/bazaar/plugin/gdal/frmts/ozi/makefile.vc new file mode 100644 index 000000000..633fd14ea --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ozi/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ozidataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I..\zlib + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/ozi/ozidataset.cpp b/bazaar/plugin/gdal/frmts/ozi/ozidataset.cpp new file mode 100644 index 000000000..f85be7723 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/ozi/ozidataset.cpp @@ -0,0 +1,696 @@ +/****************************************************************************** + * $Id: ozidataset.cpp 28039 2014-11-30 18:24:59Z rouault $ + * + * Project: OZF2 and OZFx3 binary files driver + * Purpose: GDALDataset driver for OZF2 and OZFx3 binary files. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "zlib.h" + +/* g++ -fPIC -g -Wall frmts/ozi/ozidataset.cpp -shared -o gdal_OZI.so -Iport -Igcore -Iogr -L. -lgdal */ + +CPL_CVSID("$Id: ozidataset.cpp 28039 2014-11-30 18:24:59Z rouault $"); + +CPL_C_START +void GDALRegister_OZI(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* OZIDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class OZIRasterBand; + +class OZIDataset : public GDALPamDataset +{ + friend class OZIRasterBand; + + VSILFILE* fp; + int nZoomLevelCount; + int* panZoomLevelOffsets; + OZIRasterBand** papoOvrBands; + vsi_l_offset nFileSize; + + int bOzi3; + GByte nKeyInit; + + public: + OZIDataset(); + virtual ~OZIDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* OZIRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class OZIRasterBand : public GDALPamRasterBand +{ + friend class OZIDataset; + + int nXBlocks; + int nZoomLevel; + GDALColorTable* poColorTable; + GByte* pabyTranslationTable; + + public: + + OZIRasterBand( OZIDataset *, int nZoomLevel, + int nRasterXSize, int nRasterYSize, + int nXBlocks, + GDALColorTable* poColorTable); + virtual ~OZIRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + + virtual int GetOverviewCount(); + virtual GDALRasterBand* GetOverview(int nLevel); +}; + + +/************************************************************************/ +/* I/O functions */ +/************************************************************************/ + +static const GByte abyKey[] = +{ + 0x2D, 0x4A, 0x43, 0xF1, 0x27, 0x9B, 0x69, 0x4F, + 0x36, 0x52, 0x87, 0xEC, 0x5F, 0x42, 0x53, 0x22, + 0x9E, 0x8B, 0x2D, 0x83, 0x3D, 0xD2, 0x84, 0xBA, + 0xD8, 0x5B +}; + +static void OZIDecrypt(void *pabyVal, int n, GByte nKeyInit) +{ + int i; + for(i = 0; i < n; i++) + { + ((GByte*)pabyVal)[i] ^= abyKey[i % sizeof(abyKey)] + nKeyInit; + } +} + +static int ReadInt(GByte**pptr) +{ + int nVal; + memcpy(&nVal, *pptr, 4); + *pptr += 4; + CPL_LSBPTR32(&nVal); + return nVal; +} + +static short ReadShort(GByte**pptr) +{ + short nVal; + memcpy(&nVal, *pptr, 2); + *pptr += 2; + CPL_LSBPTR16(&nVal); + return nVal; +} + +static int ReadInt(VSILFILE* fp, int bOzi3 = FALSE, int nKeyInit = 0) +{ + int nVal; + VSIFReadL(&nVal, 1, 4, fp); + if (bOzi3) OZIDecrypt(&nVal, 4, (GByte) nKeyInit); + CPL_LSBPTR32(&nVal); + return nVal; +} + +static short ReadShort(VSILFILE* fp, int bOzi3 = FALSE, int nKeyInit = 0) +{ + short nVal; + VSIFReadL(&nVal, 1, 2, fp); + if (bOzi3) OZIDecrypt(&nVal, 2, (GByte) nKeyInit); + CPL_LSBPTR16(&nVal); + return nVal; +} + +/************************************************************************/ +/* OZIRasterBand() */ +/************************************************************************/ + +OZIRasterBand::OZIRasterBand( OZIDataset *poDS, int nZoomLevel, + int nRasterXSize, int nRasterYSize, + int nXBlocks, + GDALColorTable* poColorTable ) + +{ + this->poDS = poDS; + this->nBand = 1; + + eDataType = GDT_Byte; + + nBlockXSize = 64; + nBlockYSize = 64; + + this->nZoomLevel = nZoomLevel; + this->nRasterXSize = nRasterXSize; + this->nRasterYSize = nRasterYSize; + this->poColorTable = poColorTable; + this->nXBlocks = nXBlocks; + + pabyTranslationTable = NULL; +} + +/************************************************************************/ +/* ~OZIRasterBand() */ +/************************************************************************/ + +OZIRasterBand::~OZIRasterBand() +{ + delete poColorTable; + CPLFree(pabyTranslationTable); +} + + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp OZIRasterBand::GetColorInterpretation() +{ + return GCI_PaletteIndex; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable* OZIRasterBand::GetColorTable() +{ + return poColorTable; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr OZIRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + OZIDataset *poGDS = (OZIDataset *) poDS; + + int nBlock = nBlockYOff * nXBlocks + nBlockXOff; + + VSIFSeekL(poGDS->fp, poGDS->panZoomLevelOffsets[nZoomLevel] + + 12 + 1024 + 4 * nBlock, SEEK_SET); + int nPointer = ReadInt(poGDS->fp, poGDS->bOzi3, poGDS->nKeyInit); + if (nPointer < 0 || (vsi_l_offset)nPointer >= poGDS->nFileSize) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid offset for block (%d, %d) : %d", + nBlockXOff, nBlockYOff, nPointer); + return CE_Failure; + } + int nNextPointer = ReadInt(poGDS->fp, poGDS->bOzi3, poGDS->nKeyInit); + if (nNextPointer <= nPointer + 16 || + (vsi_l_offset)nNextPointer >= poGDS->nFileSize || + nNextPointer - nPointer > 10 * 64 * 64) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid next offset for block (%d, %d) : %d", + nBlockXOff, nBlockYOff, nNextPointer); + return CE_Failure; + } + + VSIFSeekL(poGDS->fp, nPointer, SEEK_SET); + + int nToRead = nNextPointer - nPointer; + GByte* pabyZlibBuffer = (GByte*)CPLMalloc(nToRead); + if (VSIFReadL(pabyZlibBuffer, nToRead, 1, poGDS->fp) != 1) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Not enough byte read for block (%d, %d)", + nBlockXOff, nBlockYOff); + CPLFree(pabyZlibBuffer); + return CE_Failure; + } + + if (poGDS->bOzi3) + OZIDecrypt(pabyZlibBuffer, 16, poGDS->nKeyInit); + + if (pabyZlibBuffer[0] != 0x78 || + pabyZlibBuffer[1] != 0xDA) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Bad ZLIB signature for block (%d, %d) : 0x%02X 0x%02X", + nBlockXOff, nBlockYOff, pabyZlibBuffer[0], pabyZlibBuffer[1]); + CPLFree(pabyZlibBuffer); + return CE_Failure; + } + + z_stream stream; + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + stream.next_in = pabyZlibBuffer + 2; + stream.avail_in = nToRead - 2; + + int err = inflateInit2(&(stream), -MAX_WBITS); + + int i; + for(i=0;i<64 && err == Z_OK;i++) + { + stream.next_out = (Bytef*)pImage + (63 - i) * 64; + stream.avail_out = 64; + err = inflate(& (stream), Z_NO_FLUSH); + if (err != Z_OK && err != Z_STREAM_END) + break; + + if (pabyTranslationTable) + { + int j; + GByte* ptr = ((GByte*)pImage) + (63 - i) * 64; + for(j=0;j<64;j++) + { + *ptr = pabyTranslationTable[*ptr]; + ptr ++; + } + } + } + + inflateEnd(&stream); + + CPLFree(pabyZlibBuffer); + + return (err == Z_OK || err == Z_STREAM_END) ? CE_None : CE_Failure; +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int OZIRasterBand::GetOverviewCount() +{ + if (nZoomLevel != 0) + return 0; + + OZIDataset *poGDS = (OZIDataset *) poDS; + return poGDS->nZoomLevelCount - 1; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand* OZIRasterBand::GetOverview(int nLevel) +{ + if (nZoomLevel != 0) + return NULL; + + OZIDataset *poGDS = (OZIDataset *) poDS; + if (nLevel < 0 || nLevel >= poGDS->nZoomLevelCount - 1) + return NULL; + + return poGDS->papoOvrBands[nLevel + 1]; +} + +/************************************************************************/ +/* ~OZIDataset() */ +/************************************************************************/ + +OZIDataset::OZIDataset() +{ + fp = NULL; + nZoomLevelCount = 0; + panZoomLevelOffsets = NULL; + papoOvrBands = NULL; + bOzi3 = FALSE; + nKeyInit = 0; +} + +/************************************************************************/ +/* ~OZIDataset() */ +/************************************************************************/ + +OZIDataset::~OZIDataset() +{ + if (fp) + VSIFCloseL(fp); + if (papoOvrBands != NULL ) + { + /* start at 1: do not destroy the base band ! */ + for(int i=1;inHeaderBytes < 14) + return FALSE; + + if (poOpenInfo->pabyHeader[0] == 0x80 && + poOpenInfo->pabyHeader[1] == 0x77) + return TRUE; + + return poOpenInfo->pabyHeader[0] == 0x78 && + poOpenInfo->pabyHeader[1] == 0x77 && + poOpenInfo->pabyHeader[6] == 0x40 && + poOpenInfo->pabyHeader[7] == 0x00 && + poOpenInfo->pabyHeader[8] == 0x01 && + poOpenInfo->pabyHeader[9] == 0x00 && + poOpenInfo->pabyHeader[10] == 0x36 && + poOpenInfo->pabyHeader[11] == 0x04 && + poOpenInfo->pabyHeader[12] == 0x00 && + poOpenInfo->pabyHeader[13] == 0x00; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *OZIDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if (!Identify(poOpenInfo)) + return NULL; + + GByte abyHeader[14]; + CPLString osImgFilename = poOpenInfo->pszFilename; + memcpy(abyHeader, poOpenInfo->pabyHeader, 14); + + int bOzi3 = (abyHeader[0] == 0x80 && + abyHeader[1] == 0x77); + + VSILFILE* fp = VSIFOpenL(osImgFilename.c_str(), "rb"); + if (fp == NULL) + return NULL; + + OZIDataset* poDS = new OZIDataset(); + poDS->fp = fp; + + GByte nRandomNumber = 0; + GByte nKeyInit = 0; + if (bOzi3) + { + VSIFSeekL(fp, 14, SEEK_SET); + VSIFReadL(&nRandomNumber, 1, 1, fp); + //printf("nRandomNumber = %d\n", nRandomNumber); + if (nRandomNumber < 0x94) + { + delete poDS; + return NULL; + } + VSIFSeekL(fp, 0x93, SEEK_CUR); + VSIFReadL(&nKeyInit, 1, 1, fp); + + VSIFSeekL(fp, 0, SEEK_SET); + VSIFReadL(abyHeader, 1, 14, fp); + OZIDecrypt(abyHeader, 14, nKeyInit); + if (!(abyHeader[6] == 0x40 && + abyHeader[7] == 0x00 && + abyHeader[8] == 0x01 && + abyHeader[9] == 0x00 && + abyHeader[10] == 0x36 && + abyHeader[11] == 0x04 && + abyHeader[12] == 0x00 && + abyHeader[13] == 0x00)) + { + delete poDS; + return NULL; + } + + VSIFSeekL(fp, 14 + 1 + nRandomNumber, SEEK_SET); + int nMagic = ReadInt(fp, bOzi3, nKeyInit); + CPLDebug("OZI", "OZI version code : 0x%08X", nMagic); + + poDS->bOzi3 = bOzi3; + } + else + { + VSIFSeekL(fp, 14, SEEK_SET); + } + + GByte abyHeader2[40], abyHeader2_Backup[40]; + VSIFReadL(abyHeader2, 40, 1, fp); + memcpy(abyHeader2_Backup, abyHeader2, 40); + + /* There's apparently a relationship between the nMagic number */ + /* and the nKeyInit, but I'm too lazy to add switch/cases that might */ + /* be not exhaustive, so let's try the 'brute force' attack !!! */ + /* It is much so funny to be able to run one in a few microseconds :-) */ + int i; + for(i = 0; i < 256; i ++) + { + nKeyInit = (GByte)i; + GByte* pabyHeader2 = abyHeader2; + if (bOzi3) + OZIDecrypt(abyHeader2, 40, nKeyInit); + + int nHeaderSize = ReadInt(&pabyHeader2); /* should be 40 */ + poDS->nRasterXSize = ReadInt(&pabyHeader2); + poDS->nRasterYSize = ReadInt(&pabyHeader2); + int nDepth = ReadShort(&pabyHeader2); /* should be 1 */ + int nBPP = ReadShort(&pabyHeader2); /* should be 8 */ + ReadInt(&pabyHeader2); /* reserved */ + ReadInt(&pabyHeader2); /* pixel number (height * width) : unused */ + ReadInt(&pabyHeader2); /* reserved */ + ReadInt(&pabyHeader2); /* reserved */ + ReadInt(&pabyHeader2); /* ?? 0x100 */ + ReadInt(&pabyHeader2); /* ?? 0x100 */ + + if (nHeaderSize != 40 || nDepth != 1 || nBPP != 8) + { + if (bOzi3) + { + if (nKeyInit != 255) + { + memcpy(abyHeader2, abyHeader2_Backup,40); + continue; + } + else + { + CPLDebug("OZI", "Cannot decypher 2nd header. Sorry..."); + delete poDS; + return NULL; + } + } + else + { + CPLDebug("OZI", "nHeaderSize = %d, nDepth = %d, nBPP = %d", + nHeaderSize, nDepth, nBPP); + delete poDS; + return NULL; + } + } + else + break; + } + poDS->nKeyInit = nKeyInit; + + int nSeparator = ReadInt(fp); + if (!bOzi3 && nSeparator != 0x77777777) + { + CPLDebug("OZI", "didn't get end of header2 marker"); + delete poDS; + return NULL; + } + + poDS->nZoomLevelCount = ReadShort(fp); + //CPLDebug("OZI", "nZoomLevelCount = %d", poDS->nZoomLevelCount); + if (poDS->nZoomLevelCount < 0 || poDS->nZoomLevelCount >= 256) + { + CPLDebug("OZI", "nZoomLevelCount = %d", poDS->nZoomLevelCount); + delete poDS; + return NULL; + } + + /* Skip array of zoom level percentage. We don't need it for GDAL */ + VSIFSeekL(fp, sizeof(float) * poDS->nZoomLevelCount, SEEK_CUR); + + nSeparator = ReadInt(fp); + if (!bOzi3 && nSeparator != 0x77777777) + { + /* Some files have 8 extra bytes before the marker. I'm not sure */ + /* what they are used for. So just skeep them and hope that */ + /* we'll find the marker */ + nSeparator = ReadInt(fp); + nSeparator = ReadInt(fp); + if (nSeparator != 0x77777777) + { + CPLDebug("OZI", "didn't get end of zoom levels marker"); + delete poDS; + return NULL; + } + } + + VSIFSeekL(fp, 0, SEEK_END); + vsi_l_offset nFileSize = VSIFTellL(fp); + poDS->nFileSize = nFileSize; + VSIFSeekL(fp, nFileSize - 4, SEEK_SET); + int nZoomLevelTableOffset = ReadInt(fp, bOzi3, nKeyInit); + if (nZoomLevelTableOffset < 0 || + (vsi_l_offset)nZoomLevelTableOffset >= nFileSize) + { + CPLDebug("OZI", "nZoomLevelTableOffset = %d", + nZoomLevelTableOffset); + delete poDS; + return NULL; + } + + VSIFSeekL(fp, nZoomLevelTableOffset, SEEK_SET); + + poDS->panZoomLevelOffsets = + (int*)CPLMalloc(sizeof(int) * poDS->nZoomLevelCount); + + for(i=0;inZoomLevelCount;i++) + { + poDS->panZoomLevelOffsets[i] = ReadInt(fp, bOzi3, nKeyInit); + if (poDS->panZoomLevelOffsets[i] < 0 || + (vsi_l_offset)poDS->panZoomLevelOffsets[i] >= nFileSize) + { + CPLDebug("OZI", "panZoomLevelOffsets[%d] = %d", + i, poDS->panZoomLevelOffsets[i]); + delete poDS; + return NULL; + } + } + + poDS->papoOvrBands = + (OZIRasterBand**)CPLCalloc(sizeof(OZIRasterBand*), poDS->nZoomLevelCount); + + for(i=0;inZoomLevelCount;i++) + { + VSIFSeekL(fp, poDS->panZoomLevelOffsets[i], SEEK_SET); + int nW = ReadInt(fp, bOzi3, nKeyInit); + int nH = ReadInt(fp, bOzi3, nKeyInit); + short nTileX = ReadShort(fp, bOzi3, nKeyInit); + short nTileY = ReadShort(fp, bOzi3, nKeyInit); + if (i == 0 && (nW != poDS->nRasterXSize || nH != poDS->nRasterYSize)) + { + CPLDebug("OZI", "zoom[%d] inconsistent dimensions for zoom level 0 : nW=%d, nH=%d, nTileX=%d, nTileY=%d, nRasterXSize=%d, nRasterYSize=%d", + i, nW, nH, nTileX, nTileY, poDS->nRasterXSize, poDS->nRasterYSize); + delete poDS; + return NULL; + } + /* Note (#3895): some files such as world.ozf2 provided with OziExplorer */ + /* expose nTileY=33, but have nH=2048, so only require 32 tiles in vertical dimension. */ + /* So there's apparently one extra and useless tile that will be ignored */ + /* without causing apparent issues */ + /* Some other files have more tile in horizontal direction than needed, so let's */ + /* accept that. But in that case we really need to keep the nTileX value for IReadBlock() */ + /* to work properly */ + if ((nW + 63) / 64 > nTileX || (nH + 63) / 64 > nTileY) + { + CPLDebug("OZI", "zoom[%d] unexpected number of tiles : nW=%d, nH=%d, nTileX=%d, nTileY=%d", + i, nW, nH, nTileX, nTileY); + delete poDS; + return NULL; + } + + GDALColorTable* poColorTable = new GDALColorTable(); + GByte abyColorTable[256*4]; + VSIFReadL(abyColorTable, 1, 1024, fp); + if (bOzi3) + OZIDecrypt(abyColorTable, 1024, nKeyInit); + int j; + for(j=0;j<256;j++) + { + GDALColorEntry sEntry; + sEntry.c1 = abyColorTable[4*j + 2]; + sEntry.c2 = abyColorTable[4*j + 1]; + sEntry.c3 = abyColorTable[4*j + 0]; + sEntry.c4 = 255; + poColorTable->SetColorEntry(j, &sEntry); + } + + poDS->papoOvrBands[i] = new OZIRasterBand(poDS, i, nW, nH, nTileX, poColorTable); + + if (i > 0) + { + GByte* pabyTranslationTable = + poDS->papoOvrBands[i]->GetIndexColorTranslationTo(poDS->papoOvrBands[0], NULL, NULL); + + delete poDS->papoOvrBands[i]->poColorTable; + poDS->papoOvrBands[i]->poColorTable = poDS->papoOvrBands[0]->poColorTable->Clone(); + poDS->papoOvrBands[i]->pabyTranslationTable = pabyTranslationTable; + } + + } + + poDS->SetBand(1, poDS->papoOvrBands[0]); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_OZI() */ +/************************************************************************/ + +void GDALRegister_OZI() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("OZI driver")) + return; + + if( GDALGetDriverByName( "OZI" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "OZI" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "OziExplorer Image File" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_ozi.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OZIDataset::Open; + poDriver->pfnIdentify = OZIDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/GNUmakefile b/bazaar/plugin/gdal/frmts/pcidsk/GNUmakefile new file mode 100644 index 000000000..644f54ddd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/GNUmakefile @@ -0,0 +1,111 @@ + +include ../../GDALmake.opt + +ifeq ($(JPEG_SETTING),internal) +JPEG_INCLUDE = -I../jpeg/libjpeg +endif + +ifneq ($(JPEG_SETTING),no) +JPEG_FLAGS = -DHAVE_LIBJPEG +endif + +ifeq ($(PCIDSK_SETTING),old) +OBJ = pcidskdataset.o pcidsktiledrasterband.o +CPPFLAGS := -I../raw $(CPPFLAGS) +$(O_OBJ): ../raw/rawdataset.h gdal_pcidsk.h +else +OBJ = pcidskdataset2.o ogrpcidsklayer.o vsi_pcidsk_io.o gdal_edb.o $(SDKOBJ) +ifeq ($(PCIDSK_SETTING),internal) +CPPFLAGS := $(JPEG_FLAGS) $(JPEG_INCLUDE) -DPCIDSK_INTERNAL \ + -Isdk $(CPPFLAGS) + +SDKOBJ = \ + cbandinterleavedchannel.o \ + cpcidskchannel.o \ + cpixelinterleavedchannel.o \ + ctiledchannel.o \ + cexternalchannel.o \ + cpcidskfile.o \ + libjpeg_io.o \ + edb_pcidsk.o \ + metadataset_p.o \ + pcidskbuffer.o \ + pcidskcreate.o \ + pcidskexception.o \ + pcidskinterfaces.o \ + pcidskopen.o \ + pcidsk_pubutils.o \ + pcidsk_utils.o \ + sysvirtualfile.o \ + cpcidskgeoref.o \ + cpcidsksegment.o \ + cpcidskvectorsegment.o \ + cpcidskvectorsegment_consistencycheck.o \ + vecsegheader.o \ + vecsegdataindex.o \ + metadatasegment_p.o \ + sysblockmap.o \ + cpcidskpct.o \ + cpcidskrpcmodel.o \ + cpcidskgcp2segment.o \ + cpcidskbitmap.o \ + cpcidsk_tex.o \ + cpcidskapmodel.o \ + clinksegment.o \ + cpcidsktoutinmodel.o \ + cpcidskbinarysegment.o \ + cpcidsk_array.o \ + cpcidskephemerissegment.o \ + cpcidskads40model.o + +else +CPPFLAGS := $(PCIDSK_INCLUDE) $(CPPFLAGS) +endif +endif + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +../o/%.$(OBJ_EXT): sdk/%.cpp + $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +../o/%.$(OBJ_EXT): sdk/channel/%.cpp + $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +../o/%.$(OBJ_EXT): sdk/core/%.cpp + $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +../o/%.$(OBJ_EXT): sdk/port/%.cpp + $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +../o/%.$(OBJ_EXT): sdk/segment/%.cpp + $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +%.$(OBJ_EXT): sdk/%.cpp + $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +%.$(OBJ_EXT): sdk/channel/%.cpp + $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +%.$(OBJ_EXT): sdk/core/%.cpp + $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +%.$(OBJ_EXT): sdk/port/%.cpp + $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +%.$(OBJ_EXT): sdk/segment/%.cpp + $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@ + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +import: + cp ~/pcidsk/src/*.h sdk + cp ~/pcidsk/src/channel/*.cpp sdk/channel + cp ~/pcidsk/src/channel/*.h sdk/channel + cp ~/pcidsk/src/segment/*.cpp sdk/segment + cp ~/pcidsk/src/segment/*.h sdk/segment + cp ~/pcidsk/src/core/*.cpp sdk/core + cp ~/pcidsk/src/core/*.h sdk/core + cp ~/pcidsk/src/port/*.cpp sdk/port diff --git a/bazaar/plugin/gdal/frmts/pcidsk/frmt_pcidsk.html b/bazaar/plugin/gdal/frmts/pcidsk/frmt_pcidsk.html new file mode 100644 index 000000000..9f87d1777 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/frmt_pcidsk.html @@ -0,0 +1,57 @@ + + + PCIDSK --- PCI Geomatics Database File + + + + +

    PCIDSK --- PCI Geomatics Database File

    + +PCIDSK database file used by PCI EASI/PACE software for image analysis. +It is supported for reading, and writing by GDAL. All pixel data types, and +data organizations (pixel interleaved, band interleaved, file interleaved +and tiled) should be supported. +Currently LUT segments are ignored, but PCT segments should be treated +as associated with the bands. Overall file, +and band specific metadata should be correctly associated with the image +or bands.

    + +Georeferencing is supported though there may be some limitations +in support of datums and ellipsoids. GCP segments are ignored. +RPC segments will be returned as GDAL style RPC metadata.

    + +Internal overview (pyramid) images will also be correctly read though newly +requested overviews will be built externally as an .ovr file.

    + +Starting with GDAL 2.0, vector segments are also supported by the driver.

    + +

    Creation Options

    + +Note that PCIDSK files are always produced pixel interleaved, even though +other organizations are supported for read.

    + +

      + +
    • INTERLEAVING=PIXEL/BAND/FILE/TILED: sets the interleaving for +the file raster data.

      + +

    • COMPRESSION=NONE/RLE/JPEG: Sets the compression to use. Values +other than NONE (the default) may only be used with TILED interleaving. +If JPEG is select it may include a quality value between 1 and 100 - eg. COMPRESSION=JPEG40.

      + +

    • TILESIZE=n: When INTERLEAVING is TILED, the tilesize may be selected +with this parameter - the default is 127 for 127x127.

      +

    + +

    See Also:

    + +
      +
    • Implemented as gdal/frmts/pcidsk/pcidskdataset2.cpp.

      + +

    • PCIDSK SDK

      + +

    + + + + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/gdal_edb.cpp b/bazaar/plugin/gdal/frmts/pcidsk/gdal_edb.cpp new file mode 100644 index 000000000..214b0ec7b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/gdal_edb.cpp @@ -0,0 +1,302 @@ +/****************************************************************************** + * $Id: gdal_edb.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: PCIDSK Database File + * Purpose: External Database access interface implementation (EDBFile). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_multiproc.h" +#include "gdal_priv.h" +#include "pcidsk.h" + +CPL_CVSID("$Id: gdal_edb.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +using namespace PCIDSK; + +/************************************************************************/ +/* ==================================================================== */ +/* GDAL_EDBFile */ +/* ==================================================================== */ +/************************************************************************/ + +class GDAL_EDBFile : public EDBFile +{ + GDALDataset *poDS; + +public: + + GDAL_EDBFile( GDALDataset *poDSIn ) { poDS = poDSIn; } + ~GDAL_EDBFile() { if( poDS ) Close(); } + + int Close() const; + int GetWidth() const; + int GetHeight() const; + int GetChannels() const; + int GetBlockWidth(int channel ) const; + int GetBlockHeight(int channel ) const; + eChanType GetType(int channel ) const; + int ReadBlock(int channel, + int block_index, void *buffer, + int win_xoff, int win_yoff, + int win_xsize, int win_ysize ); + int WriteBlock( int channel, int block_index, void *buffer); +}; + +/************************************************************************/ +/* GDAL_EDBOpen() */ +/************************************************************************/ + +EDBFile *GDAL_EDBOpen( std::string osFilename, std::string osAccess ) + +{ + GDALDataset *poDS; + + if( osAccess == "r" ) + poDS = (GDALDataset *) GDALOpen( osFilename.c_str(), GA_ReadOnly ); + else + poDS = (GDALDataset *) GDALOpen( osFilename.c_str(), GA_Update ); + + if( poDS == NULL ) + ThrowPCIDSKException( "%s", CPLGetLastErrorMsg() ); + + return new GDAL_EDBFile( poDS ); +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int GDAL_EDBFile::Close() const + +{ + if( poDS != NULL ) + { + delete poDS; + ((GDAL_EDBFile*)this)->poDS = NULL; + } + + return 1; +} + +/************************************************************************/ +/* GetWidth() */ +/************************************************************************/ + +int GDAL_EDBFile::GetWidth() const + +{ + return poDS->GetRasterXSize(); +} + +/************************************************************************/ +/* GetHeight() */ +/************************************************************************/ + +int GDAL_EDBFile::GetHeight() const + +{ + return poDS->GetRasterYSize(); +} + +/************************************************************************/ +/* GetChannels() */ +/************************************************************************/ + +int GDAL_EDBFile::GetChannels() const + +{ + return poDS->GetRasterCount(); +} + +/************************************************************************/ +/* GetBlockWidth() */ +/************************************************************************/ + +int GDAL_EDBFile::GetBlockWidth( int nChannel ) const + +{ + int nWidth, nHeight; + + poDS->GetRasterBand(nChannel)->GetBlockSize( &nWidth, &nHeight ); + + return nWidth; +} + +/************************************************************************/ +/* GetBlockHeight() */ +/************************************************************************/ + +int GDAL_EDBFile::GetBlockHeight( int nChannel ) const + +{ + int nWidth, nHeight; + + poDS->GetRasterBand(nChannel)->GetBlockSize( &nWidth, &nHeight ); + + return nHeight; +} + +/************************************************************************/ +/* GetType() */ +/************************************************************************/ + +eChanType GDAL_EDBFile::GetType( int nChannel ) const +{ + switch( poDS->GetRasterBand(nChannel)->GetRasterDataType() ) + { + case GDT_Byte: + return CHN_8U; + + case GDT_Int16: + return CHN_16S; + + case GDT_UInt16: + return CHN_16U; + + case GDT_Float32: + return CHN_32R; + + case GDT_CInt16: + return CHN_C16S; + + default: + return CHN_UNKNOWN; + } +} + +/************************************************************************/ +/* ReadBlock() */ +/************************************************************************/ + +int GDAL_EDBFile::ReadBlock( int channel, + int block_index, void *buffer, + int win_xoff, int win_yoff, + int win_xsize, int win_ysize ) + +{ + GDALRasterBand *poBand = poDS->GetRasterBand(channel); + int nBlockXSize, nBlockYSize; + int nBlockX, nBlockY; + int nWidthInBlocks; + int nPixelOffset; + int nLineOffset; + + if( GetType(channel) == CHN_UNKNOWN ) + { + ThrowPCIDSKException("%s channel type not supported for PCIDSK access.", + GDALGetDataTypeName(poBand->GetRasterDataType()) ); + } + + poBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + nWidthInBlocks = (poBand->GetXSize() + nBlockXSize - 1) / nBlockXSize; + + nBlockX = block_index % nWidthInBlocks; + nBlockY = block_index / nWidthInBlocks; + + nPixelOffset = GDALGetDataTypeSize(poBand->GetRasterDataType()) / 8; + nLineOffset = win_xsize * nPixelOffset; + +/* -------------------------------------------------------------------- */ +/* Are we reading a partial block at the edge of the database? */ +/* If so, ensure we don't read off the database. */ +/* -------------------------------------------------------------------- */ + if( nBlockX * nBlockXSize + win_xoff + win_xsize > poBand->GetXSize() ) + win_xsize = poBand->GetXSize() - nBlockX * nBlockXSize - win_xoff; + + if( nBlockY * nBlockYSize + win_yoff + win_ysize > poBand->GetYSize() ) + win_ysize = poBand->GetYSize() - nBlockY * nBlockYSize - win_yoff; + + CPLErr eErr = poBand->RasterIO( GF_Read, + nBlockX * nBlockXSize + win_xoff, + nBlockY * nBlockYSize + win_yoff, + win_xsize, win_ysize, + buffer, win_xsize, win_ysize, + poBand->GetRasterDataType(), + nPixelOffset, nLineOffset, NULL ); + + if( eErr != CE_None ) + { + ThrowPCIDSKException( "%s", CPLGetLastErrorMsg() ); + } + + return 1; +} + +/************************************************************************/ +/* WriteBlock() */ +/************************************************************************/ + +int GDAL_EDBFile::WriteBlock( int channel, int block_index, void *buffer) + +{ + GDALRasterBand *poBand = poDS->GetRasterBand(channel); + int nBlockXSize, nBlockYSize; + int nBlockX, nBlockY; + int nWinXSize, nWinYSize; + int nWidthInBlocks; + + if( GetType(channel) == CHN_UNKNOWN ) + { + ThrowPCIDSKException("%s channel type not supported for PCIDSK access.", + GDALGetDataTypeName(poBand->GetRasterDataType()) ); + } + + poBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + nWidthInBlocks = (poBand->GetXSize() + nBlockXSize - 1) / nBlockXSize; + + nBlockX = block_index % nWidthInBlocks; + nBlockY = block_index / nWidthInBlocks; + +/* -------------------------------------------------------------------- */ +/* Are we reading a partial block at the edge of the database? */ +/* If so, ensure we don't read off the database. */ +/* -------------------------------------------------------------------- */ + if( nBlockX * nBlockXSize + nBlockXSize > poBand->GetXSize() ) + nWinXSize = poBand->GetXSize() - nBlockX * nBlockXSize; + else + nWinXSize = nBlockXSize; + + if( nBlockY * nBlockYSize + nBlockYSize > poBand->GetYSize() ) + nWinYSize = poBand->GetYSize() - nBlockY * nBlockYSize; + else + nWinYSize = nBlockYSize; + + CPLErr eErr = poBand->RasterIO( GF_Write, + nBlockX * nBlockXSize, + nBlockY * nBlockYSize, + nWinXSize, nWinYSize, + buffer, nWinXSize, nWinYSize, + poBand->GetRasterDataType(), 0, 0, NULL ); + + if( eErr != CE_None ) + { + ThrowPCIDSKException( "%s", CPLGetLastErrorMsg() ); + } + + return 1; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/gdal_pcidsk.h b/bazaar/plugin/gdal/frmts/pcidsk/gdal_pcidsk.h new file mode 100644 index 000000000..099385ca8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/gdal_pcidsk.h @@ -0,0 +1,221 @@ +/****************************************************************************** + * $Id: gdal_pcidsk.h 20996 2010-10-28 18:38:15Z rouault $ + * + * Project: PCIDSK Database File + * Purpose: PCIDSK driver declarations. + * Author: Andrey Kiselev, dron@remotesensing.org + * + ****************************************************************************** + * Copyright (c) 2003, Andrey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" +#include "rawdataset.h" + +typedef enum +{ + PDI_PIXEL, + PDI_BAND, + PDI_FILE +} PCIDSKInterleaving; + +/************************************************************************/ +/* PCIDSKDataset */ +/************************************************************************/ + +class PCIDSKDataset : public RawDataset +{ + friend class PCIDSKRawRasterBand; + friend class PCIDSKTiledRasterBand; + + const char *pszFilename; + VSILFILE *fp; + + vsi_l_offset nFileSize; + + char *pszCreatTime; // Date/time of the database creation + + vsi_l_offset nGeoPtrOffset; // Offset in bytes to the pointer + // to GEO segment + vsi_l_offset nGeoOffset; // Offset in bytes to the GEO segment + vsi_l_offset nGcpPtrOffset; // Offset in bytes to the pointer + // to GCP segment + vsi_l_offset nGcpOffset; // Offset in bytes to the GCP segment + + int bGeoSegmentDirty; + int bGeoTransformValid; + + int nBlockMapSeg; + + GDAL_GCP *pasGCPList; + long nGCPCount; + + double adfGeoTransform[6]; + char *pszProjection; + char *pszGCPProjection; + + GDALDataType PCIDSKTypeToGDAL( const char *); + void WriteGeoSegment(); + + void CollectPCIDSKMetadata( int nSegment ); + + // Segment map + int nSegCount; + int *panSegType; + char **papszSegName; + vsi_l_offset *panSegOffset; + vsi_l_offset *panSegSize; + + int nBandFileCount; + VSILFILE **pafpBandFiles; + + public: + PCIDSKDataset(); + ~PCIDSKDataset(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char **papszParmList ); + static GDALDataset *CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + + virtual void FlushCache( void ); + + CPLErr GetGeoTransform( double * padfTransform ); + virtual CPLErr SetGeoTransform( double * ); + const char *GetProjectionRef(); + virtual CPLErr SetProjection( const char * ); + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + // pcidsk specific + int SegRead( int nSegment, + vsi_l_offset nOffset, + int nSize, + void *pBuffer ); +}; + +/************************************************************************/ +/* PCIDSKTiledRasterBand */ +/************************************************************************/ + +class PCIDSKTiledRasterBand : public GDALPamRasterBand +{ + friend class PCIDSKDataset; + + PCIDSKDataset *poPDS; + + int nImage; + + int nBlocks; + vsi_l_offset *panBlockOffset;// offset in physical file. + + int nTileCount; + vsi_l_offset *panTileOffset; // offset in "image" virtual file. + int *panTileSize; + + int nOverviewCount; + GDALRasterBand **papoOverviews; + + char szCompression[9]; + + void AttachOverview( GDALRasterBand *poOvBand ) { + + nOverviewCount++; + papoOverviews = (GDALRasterBand **) + CPLRealloc(papoOverviews,sizeof(void*) * nOverviewCount); + papoOverviews[nOverviewCount-1] = poOvBand; + } + + int BuildBlockMap(); + int BuildTileMap(); + + public: + PCIDSKTiledRasterBand( PCIDSKDataset *, int, int ); + ~PCIDSKTiledRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + + int SysRead( vsi_l_offset nOffset, int nSize, void * ); + + virtual int GetOverviewCount() { return nOverviewCount; } + virtual GDALRasterBand *GetOverview(int iOverview) + { return papoOverviews[iOverview]; } +}; + +/************************************************************************/ +/* PCIDSKRawRasterBand */ +/************************************************************************/ + +class PCIDSKRawRasterBand : public RawRasterBand +{ + friend class PCIDSKDataset; + + int nOverviewCount; + GDALRasterBand **papoOverviews; + + void AttachOverview( GDALRasterBand *poOvBand ) { + nOverviewCount++; + papoOverviews = (GDALRasterBand **) + CPLRealloc(papoOverviews,sizeof(void*) * nOverviewCount); + papoOverviews[nOverviewCount-1] = poOvBand; + } + + public: + PCIDSKRawRasterBand( GDALDataset *poDS, int nBand, VSILFILE * fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder ) + : RawRasterBand( poDS, nBand, fpRaw, nImgOffset, nPixelOffset, + nLineOffset, eDataType, bNativeOrder, TRUE ) { + nOverviewCount = 0; + papoOverviews = NULL; + } + ~PCIDSKRawRasterBand() { + FlushCache(); + for( int i = 0; i < nOverviewCount; i++ ) + delete papoOverviews[i]; + CPLFree( papoOverviews ); + } + + virtual int GetOverviewCount() { + if (nOverviewCount > 0) + return nOverviewCount; + + return RawRasterBand::GetOverviewCount(); + } + virtual GDALRasterBand *GetOverview(int iOverview) { + if (iOverview < nOverviewCount) + return papoOverviews[iOverview]; + + return RawRasterBand::GetOverview(iOverview); + } +}; + + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/makefile.vc b/bazaar/plugin/gdal/frmts/pcidsk/makefile.vc new file mode 100644 index 000000000..1035661db --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/makefile.vc @@ -0,0 +1,74 @@ + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IF "$(PCIDSK_SETTING)" == "OLD" +PCIDSKFLAGS = -I..\raw +OBJ = pcidskdataset.obj pcidsktiledrasterband.obj +!ENDIF + +!IF "$(PCIDSK_SETTING)" == "EXTERNAL" +PCIDSKFLAGS = $(PCIDSK_INCLUDE) +OBJ = pcidskdataset2.obj ogrpcidsklayer.obj vsi_pcidsk_io.obj gdal_edb.obj +!ENDIF + +!IF "$(PCIDSK_SETTING)" == "INTERNAL" +PCIDSKFLAGS = -Isdk -DPCIDSK_INTERNAL -DHAVE_LIBJPEG + +OBJ = pcidskdataset2.obj ogrpcidsklayer.obj vsi_pcidsk_io.obj gdal_edb.obj \ + sdk\channel\cbandinterleavedchannel.obj \ + sdk\channel\cpcidskchannel.obj \ + sdk\channel\cpixelinterleavedchannel.obj \ + sdk\channel\ctiledchannel.obj \ + sdk\channel\cexternalchannel.obj \ + sdk\core\cpcidskfile.obj \ + sdk\core\libjpeg_io.obj \ + sdk\core\edb_pcidsk.obj \ + sdk\core\metadataset_p.obj \ + sdk\core\pcidskbuffer.obj \ + sdk\core\pcidskcreate.obj \ + sdk\core\pcidskexception.obj \ + sdk\core\pcidskinterfaces.obj \ + sdk\core\pcidskopen.obj \ + sdk\core\pcidsk_pubutils.obj \ + sdk\core\pcidsk_utils.obj \ + sdk\core\sysvirtualfile.obj \ + sdk\core\clinksegment.obj \ + sdk\segment\cpcidskgeoref.obj \ + sdk\segment\cpcidskpct.obj \ + sdk\segment\cpcidsksegment.obj \ + sdk\segment\cpcidskvectorsegment.obj \ + sdk\segment\cpcidskvectorsegment_consistencycheck.obj \ + sdk\segment\vecsegheader.obj \ + sdk\segment\vecsegdataindex.obj \ + sdk\segment\metadatasegment_p.obj \ + sdk\segment\sysblockmap.obj \ + sdk\segment\cpcidskrpcmodel.obj \ + sdk\segment\cpcidskgcp2segment.obj \ + sdk\segment\cpcidskbitmap.obj \ + sdk\segment\cpcidsk_tex.obj \ + sdk\segment\cpcidskapmodel.obj \ + sdk\segment\cpcidsktoutinmodel.obj \ + sdk\segment\cpcidskbinarysegment.obj \ + sdk\segment\cpcidsk_array.obj \ + sdk\segment\cpcidskephemerissegment.obj \ + sdk\segment\cpcidskads40model.obj + +!ENDIF + +!IFDEF JPEG_EXTERNAL_LIB +JPEGFLAGS = -I$(JPEGDIR) +!ELSE +JPEGFLAGS = -I..\jpeg\libjpeg +!ENDIF + +.cpp.obj: + $(CC) $(CFLAGS) $(PCIDSKFLAGS) $(JPEGFLAGS) /c $*.cpp /Fo$*.obj + xcopy /D /Y $*.obj ..\o + +default: $(OBJ) + +clean: + -del $(OBJ) + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/notes.txt b/bazaar/plugin/gdal/frmts/pcidsk/notes.txt new file mode 100644 index 000000000..ddcdbada7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/notes.txt @@ -0,0 +1,44 @@ +SysBMap: + +512 byte header block: + - "VERSION 1" + - 8 bytes: # of images. + - 5 bytes: # of absolute blocks. + - 8 bytes: # of blocks actually used. + +block map entries: + - each is 28 bytes + - 4 bytes for SysBData Segment - often 1023. + - 8 bytes for absolute block index (zero based, within SysBData). + - 8 bytes for "image" number (-1 means unallocated) + - 8 bytes for the next absolute block in this images block chain + (-1 means end of chain) + +each block in block map is 8K on disk (16 512byte blocks). + + +After the block map is the layer information. For each layer: + + - 4 bytes: Layer type # : IDBSYSLT_DEAD(1) or IDBSYSLT_IMAGE(2) currently + + +Virtual tiled images: + +128 byte header: + - 8 bytes: image width + - 8 bytes: image height + - 8 bytes: tile width + - 8 bytes: tile height + - 3 bytes: tile pixel data type. + - some spaces + - compression type (normally NONE) + - some spaces. + +Tilemap: + - 12 bytes per tile containing offset within virtual file. + +Tile Size map: + - 8 bytes per tile containing size of tile data. + +Tile Data: + - Absolute data. diff --git a/bazaar/plugin/gdal/frmts/pcidsk/ogrpcidsklayer.cpp b/bazaar/plugin/gdal/frmts/pcidsk/ogrpcidsklayer.cpp new file mode 100644 index 000000000..16a090f58 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/ogrpcidsklayer.cpp @@ -0,0 +1,834 @@ +/****************************************************************************** + * $Id: ogrcsvlayer.cpp 17496 2009-08-02 11:54:23Z rouault $ + * + * Project: PCIDSK Translator + * Purpose: Implements OGRPCIDSKLayer class. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidskdataset2.h" + +CPL_CVSID("$Id$"); + +/************************************************************************/ +/* OGRPCIDSKLayer() */ +/************************************************************************/ + +OGRPCIDSKLayer::OGRPCIDSKLayer( PCIDSK::PCIDSKSegment *poSegIn, + bool bUpdate ) + +{ + poSRS = NULL; + bUpdateAccess = bUpdate; + poSeg = poSegIn; + poVecSeg = dynamic_cast( poSeg ); + + poFeatureDefn = new OGRFeatureDefn( poSeg->GetName().c_str() ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + hLastShapeId = PCIDSK::NullShapeId; + +/* -------------------------------------------------------------------- */ +/* Attempt to assign a geometry type. */ +/* -------------------------------------------------------------------- */ + try { + std::string osLayerType = poSeg->GetMetadataValue( "LAYER_TYPE" ); + + if( osLayerType == "WHOLE_POLYGONS" ) + poFeatureDefn->SetGeomType( wkbPolygon25D ); + else if( osLayerType == "ARCS" || osLayerType == "TOPO_ARCS" ) + poFeatureDefn->SetGeomType( wkbLineString25D ); + else if( osLayerType == "POINTS" || osLayerType == "TOPO_NODES" ) + poFeatureDefn->SetGeomType( wkbPoint25D ); + else if( osLayerType == "TABLE" ) + poFeatureDefn->SetGeomType( wkbNone ); + } catch(...) {} + + +/* -------------------------------------------------------------------- */ +/* Build field definitions. */ +/* -------------------------------------------------------------------- */ + try + { + iRingStartField = -1; + + for( int iField = 0; iField < poVecSeg->GetFieldCount(); iField++ ) + { + OGRFieldDefn oField( poVecSeg->GetFieldName(iField).c_str(), OFTString); + + switch( poVecSeg->GetFieldType(iField) ) + { + case PCIDSK::FieldTypeFloat: + case PCIDSK::FieldTypeDouble: + oField.SetType( OFTReal ); + break; + + case PCIDSK::FieldTypeInteger: + oField.SetType( OFTInteger ); + break; + + case PCIDSK::FieldTypeString: + oField.SetType( OFTString ); + break; + + case PCIDSK::FieldTypeCountedInt: + oField.SetType( OFTIntegerList ); + break; + + default: + CPLAssert( FALSE ); + break; + } + + // we ought to try and extract some width/precision information + // from the format string at some point. + + // If the last field is named RingStart we treat it specially. + if( EQUAL(oField.GetNameRef(),"RingStart") + && oField.GetType() == OFTIntegerList + && iField == poVecSeg->GetFieldCount()-1 ) + iRingStartField = iField; + else + poFeatureDefn->AddFieldDefn( &oField ); + } + +/* -------------------------------------------------------------------- */ +/* Look for a coordinate system. */ +/* -------------------------------------------------------------------- */ + CPLString osGeosys; + const char *pszUnits = NULL; + std::vector adfParameters; + + adfParameters = poVecSeg->GetProjection( osGeosys ); + + if( ((PCIDSK::UnitCode)(int)adfParameters[16]) + == PCIDSK::UNIT_DEGREE ) + pszUnits = "DEGREE"; + else if( ((PCIDSK::UnitCode)(int)adfParameters[16]) + == PCIDSK::UNIT_METER ) + pszUnits = "METER"; + else if( ((PCIDSK::UnitCode)(int)adfParameters[16]) + == PCIDSK::UNIT_US_FOOT ) + pszUnits = "FOOT"; + else if( ((PCIDSK::UnitCode)(int)adfParameters[16]) + == PCIDSK::UNIT_INTL_FOOT ) + pszUnits = "INTL FOOT"; + + poSRS = new OGRSpatialReference(); + + if( poSRS->importFromPCI( osGeosys, pszUnits, + &(adfParameters[0]) ) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Trap pcidsk exceptions. */ +/* -------------------------------------------------------------------- */ + catch( PCIDSK::PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "PCIDSK Exception while initializing layer, operation likely impaired.\n%s", ex.what() ); + } + catch(...) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Non-PCIDSK exception trapped while initializing layer, operation likely impaired." ); + } + + if( poFeatureDefn->GetGeomFieldCount() > 0 ) + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); +} + +/************************************************************************/ +/* ~OGRPCIDSKLayer() */ +/************************************************************************/ + +OGRPCIDSKLayer::~OGRPCIDSKLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "PCIDSK", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + poFeatureDefn->Release(); + + if (poSRS) + poSRS->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRPCIDSKLayer::ResetReading() + +{ + hLastShapeId = PCIDSK::NullShapeId; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRPCIDSKLayer::GetNextFeature() + +{ + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* Read features till we find one that satisfies our current */ +/* spatial criteria. */ +/* -------------------------------------------------------------------- */ + while( TRUE ) + { + poFeature = GetNextUnfilteredFeature(); + if( poFeature == NULL ) + break; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + break; + + delete poFeature; + } + + return poFeature; +} + +/************************************************************************/ +/* GetNextUnfilteredFeature() */ +/************************************************************************/ + +OGRFeature * OGRPCIDSKLayer::GetNextUnfilteredFeature() + +{ +/* -------------------------------------------------------------------- */ +/* Get the next shapeid. */ +/* -------------------------------------------------------------------- */ + if( hLastShapeId == PCIDSK::NullShapeId ) + hLastShapeId = poVecSeg->FindFirst(); + else + hLastShapeId = poVecSeg->FindNext( hLastShapeId ); + + if( hLastShapeId == PCIDSK::NullShapeId ) + return NULL; + + return GetFeature( hLastShapeId ); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRPCIDSKLayer::GetFeature( GIntBig nFID ) + +{ +/* -------------------------------------------------------------------- */ +/* Create the OGR feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poFeature = new OGRFeature( poFeatureDefn ); + poFeature->SetFID( (int) nFID ); + +/* -------------------------------------------------------------------- */ +/* Set attributes for any indicated attribute records. */ +/* -------------------------------------------------------------------- */ + try { + std::vector aoFields; + unsigned int i; + + poVecSeg->GetFields( (int) nFID, aoFields ); + for( i=0; i < aoFields.size(); i++ ) + { + if( (int) i == iRingStartField ) + continue; + + switch( aoFields[i].GetType() ) + { + case PCIDSK::FieldTypeNone: + // null field value. + break; + + case PCIDSK::FieldTypeInteger: + poFeature->SetField( i, aoFields[i].GetValueInteger() ); + break; + + case PCIDSK::FieldTypeFloat: + poFeature->SetField( i, aoFields[i].GetValueFloat() ); + break; + + case PCIDSK::FieldTypeDouble: + poFeature->SetField( i, aoFields[i].GetValueDouble() ); + break; + + case PCIDSK::FieldTypeString: + poFeature->SetField( i, aoFields[i].GetValueString().c_str() ); + break; + + case PCIDSK::FieldTypeCountedInt: + std::vector list = aoFields[i].GetValueCountedInt(); + + poFeature->SetField( i, list.size(), &(list[0]) ); + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Translate the geometry. */ +/* -------------------------------------------------------------------- */ + std::vector aoVertices; + + poVecSeg->GetVertices( (int) nFID, aoVertices ); + +/* -------------------------------------------------------------------- */ +/* Point */ +/* -------------------------------------------------------------------- */ + if( poFeatureDefn->GetGeomType() == wkbPoint25D + || (wkbFlatten(poFeatureDefn->GetGeomType()) == wkbUnknown + && aoVertices.size() == 1) ) + { + if( aoVertices.size() == 1 ) + { + OGRPoint* poPoint = + new OGRPoint( aoVertices[0].x, + aoVertices[0].y, + aoVertices[0].z ); + if (poSRS) + poPoint->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poPoint); + } + else + { + // report issue? + } + } + +/* -------------------------------------------------------------------- */ +/* LineString */ +/* -------------------------------------------------------------------- */ + else if( poFeatureDefn->GetGeomType() == wkbLineString25D + || (wkbFlatten(poFeatureDefn->GetGeomType()) == wkbUnknown + && aoVertices.size() > 1) ) + { + // We should likely be applying ringstart to break things into + // a multilinestring in some cases. + if( aoVertices.size() > 1 ) + { + OGRLineString *poLS = new OGRLineString(); + + poLS->setNumPoints( aoVertices.size() ); + + for( i = 0; i < aoVertices.size(); i++ ) + poLS->setPoint( i, + aoVertices[i].x, + aoVertices[i].y, + aoVertices[i].z ); + if (poSRS) + poLS->assignSpatialReference(poSRS); + + poFeature->SetGeometryDirectly( poLS ); + } + else + { + // report issue? + } + } + +/* -------------------------------------------------------------------- */ +/* Polygon - Currently we have no way to recognise if we are */ +/* dealing with a multipolygon when we have more than one */ +/* ring. Also, PCIDSK allows the rings to be in arbitrary */ +/* order, not necessarily outside first which we are not yet */ +/* ready to address in the following code. */ +/* -------------------------------------------------------------------- */ + else if( poFeatureDefn->GetGeomType() == wkbPolygon25D ) + { + std::vector anRingStart; + OGRPolygon *poPoly = new OGRPolygon(); + unsigned int iRing; + + if( iRingStartField != -1 ) + anRingStart = aoFields[iRingStartField].GetValueCountedInt(); + + for( iRing = 0; iRing < anRingStart.size()+1; iRing++ ) + { + int iStartVertex, iEndVertex, iVertex; + OGRLinearRing *poRing = new OGRLinearRing(); + + if( iRing == 0 ) + iStartVertex = 0; + else + iStartVertex = anRingStart[iRing-1]; + + if( iRing == anRingStart.size() ) + iEndVertex = aoVertices.size() - 1; + else + iEndVertex = anRingStart[iRing] - 1; + + poRing->setNumPoints( iEndVertex - iStartVertex + 1 ); + for( iVertex = iStartVertex; iVertex <= iEndVertex; iVertex++ ) + { + poRing->setPoint( iVertex - iStartVertex, + aoVertices[iVertex].x, + aoVertices[iVertex].y, + aoVertices[iVertex].z ); + } + + poPoly->addRingDirectly( poRing ); + } + + if (poSRS) + poPoly->assignSpatialReference(poSRS); + + poFeature->SetGeometryDirectly( poPoly ); + } + } + +/* -------------------------------------------------------------------- */ +/* Trap exceptions and report as CPL errors. */ +/* -------------------------------------------------------------------- */ + catch( PCIDSK::PCIDSKException ex ) + { + delete poFeature; + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return NULL; + } + catch(...) + { + delete poFeature; + CPLError( CE_Failure, CPLE_AppDefined, + "Non-PCIDSK exception trapped." ); + return NULL; + } + + m_nFeaturesRead++; + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPCIDSKLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return m_poFilterGeom == NULL && m_poAttrQuery == NULL; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCDeleteFeature) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCCreateField) ) + return bUpdateAccess; + + else + return FALSE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRPCIDSKLayer::GetFeatureCount( int bForce ) + +{ + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL ) + return OGRLayer::GetFeatureCount( bForce ); + else + { + try { + return poVecSeg->GetShapeCount(); + } catch(...) { + return 0; + } + } +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRPCIDSKLayer::GetExtent (OGREnvelope *psExtent, int bForce) + +{ + if( !bForce ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Loop over all features, but just read the geometry. This is */ +/* a fair amount quicker than actually processing all the */ +/* attributes, forming features and then exaimining the */ +/* geometries as the default implemntation would do. */ +/* -------------------------------------------------------------------- */ + try + { + bool bHaveExtent = FALSE; + + std::vector asVertices; + + for( PCIDSK::ShapeIterator oIt = poVecSeg->begin(); + oIt != poVecSeg->end(); + oIt++ ) + { + unsigned int i; + + poVecSeg->GetVertices( *oIt, asVertices ); + + for( i = 0; i < asVertices.size(); i++ ) + { + if( !bHaveExtent ) + { + psExtent->MinX = psExtent->MaxX = asVertices[i].x; + psExtent->MinY = psExtent->MaxY = asVertices[i].y; + bHaveExtent = true; + } + else + { + psExtent->MinX = MIN(psExtent->MinX,asVertices[i].x); + psExtent->MaxX = MAX(psExtent->MaxX,asVertices[i].x); + psExtent->MinY = MIN(psExtent->MinY,asVertices[i].y); + psExtent->MaxY = MAX(psExtent->MaxY,asVertices[i].y); + } + } + } + + if( bHaveExtent ) + return OGRERR_NONE; + else + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Trap pcidsk exceptions. */ +/* -------------------------------------------------------------------- */ + catch( PCIDSK::PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "PCIDSK Exception while initializing layer, operation likely impaired.\n%s", ex.what() ); + return OGRERR_FAILURE; + } + catch(...) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Non-PCIDSK exception trapped while initializing layer, operation likely impaired." ); + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRPCIDSKLayer::DeleteFeature( GIntBig nFID ) + +{ + try { + + poVecSeg->DeleteShape( (PCIDSK::ShapeId) nFID ); + + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Trap exceptions and report as CPL errors. */ +/* -------------------------------------------------------------------- */ + catch( PCIDSK::PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return OGRERR_FAILURE; + } + catch(...) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Non-PCIDSK exception trapped." ); + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRPCIDSKLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + try { + + PCIDSK::ShapeId id = poVecSeg->CreateShape( + (PCIDSK::ShapeId) poFeature->GetFID() ); + + poFeature->SetFID( (long) id ); + + return SetFeature( poFeature ); + } +/* -------------------------------------------------------------------- */ +/* Trap exceptions and report as CPL errors. */ +/* -------------------------------------------------------------------- */ + catch( PCIDSK::PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return OGRERR_FAILURE; + } + catch(...) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Non-PCIDSK exception trapped." ); + return OGRERR_FAILURE; + } + +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRPCIDSKLayer::ISetFeature( OGRFeature *poFeature ) + +{ + PCIDSK::ShapeId id = (PCIDSK::ShapeId) poFeature->GetFID(); + +/* -------------------------------------------------------------------- */ +/* Translate attribute fields. */ +/* -------------------------------------------------------------------- */ + try { + + int iPCI; + std::vector aoPCIFields; + + aoPCIFields.resize(poVecSeg->GetFieldCount()); + + for( iPCI = 0; iPCI < poVecSeg->GetFieldCount(); iPCI++ ) + { + int iOGR; + + iOGR = poFeatureDefn->GetFieldIndex( + poVecSeg->GetFieldName(iPCI).c_str() ); + + if( iOGR == -1 ) + continue; + + switch( poVecSeg->GetFieldType(iPCI) ) + { + case PCIDSK::FieldTypeInteger: + aoPCIFields[iPCI].SetValue( + poFeature->GetFieldAsInteger( iOGR ) ); + break; + + case PCIDSK::FieldTypeFloat: + aoPCIFields[iPCI].SetValue( + (float) poFeature->GetFieldAsDouble( iOGR ) ); + break; + + case PCIDSK::FieldTypeDouble: + aoPCIFields[iPCI].SetValue( + (double) poFeature->GetFieldAsDouble( iOGR ) ); + break; + + case PCIDSK::FieldTypeString: + aoPCIFields[iPCI].SetValue( + poFeature->GetFieldAsString( iOGR ) ); + break; + + case PCIDSK::FieldTypeCountedInt: + { + int nCount; + const int *panList = + poFeature->GetFieldAsIntegerList( iOGR, &nCount ); + std::vector anList; + + anList.resize( nCount ); + memcpy( &(anList[0]), panList, 4 * anList.size() ); + aoPCIFields[iPCI].SetValue( anList ); + } + break; + + default: + CPLAssert( FALSE ); + break; + } + } + + if( poVecSeg->GetFieldCount() > 0 ) + poVecSeg->SetFields( id, aoPCIFields ); + +/* -------------------------------------------------------------------- */ +/* Translate the geometry. */ +/* -------------------------------------------------------------------- */ + std::vector aoVertices; + OGRGeometry *poGeometry = poFeature->GetGeometryRef(); + + if( poGeometry == NULL ) + { + } + + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + aoVertices.resize(1); + aoVertices[0].x = poPoint->getX(); + aoVertices[0].y = poPoint->getY(); + aoVertices[0].z = poPoint->getZ(); + } + + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbLineString ) + { + OGRLineString *poLS = (OGRLineString *) poGeometry; + unsigned int i; + + aoVertices.resize(poLS->getNumPoints()); + + for( i = 0; i < aoVertices.size(); i++ ) + { + aoVertices[i].x = poLS->getX(i); + aoVertices[i].y = poLS->getY(i); + aoVertices[i].z = poLS->getZ(i); + } + } + + else + { + CPLDebug( "PCIDSK", "Unsupported geometry type in SetFeature(): %s", + poGeometry->getGeometryName() ); + } + + poVecSeg->SetVertices( id, aoVertices ); + + } /* try */ + +/* -------------------------------------------------------------------- */ +/* Trap exceptions and report as CPL errors. */ +/* -------------------------------------------------------------------- */ + catch( PCIDSK::PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return OGRERR_FAILURE; + } + catch(...) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Non-PCIDSK exception trapped." ); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRPCIDSKLayer::CreateField( OGRFieldDefn *poFieldDefn, + int bApproxOK ) + +{ + try { + + if( poFieldDefn->GetType() == OFTInteger ) + { + poVecSeg->AddField( poFieldDefn->GetNameRef(), + PCIDSK::FieldTypeInteger, + "", "" ); + poFeatureDefn->AddFieldDefn( poFieldDefn ); + } + else if( poFieldDefn->GetType() == OFTReal ) + { + poVecSeg->AddField( poFieldDefn->GetNameRef(), + PCIDSK::FieldTypeDouble, + "", "" ); + poFeatureDefn->AddFieldDefn( poFieldDefn ); + } + else if( poFieldDefn->GetType() == OFTString ) + { + poVecSeg->AddField( poFieldDefn->GetNameRef(), + PCIDSK::FieldTypeString, + "", "" ); + poFeatureDefn->AddFieldDefn( poFieldDefn ); + } + else if( poFieldDefn->GetType() == OFTIntegerList ) + { + poVecSeg->AddField( poFieldDefn->GetNameRef(), + PCIDSK::FieldTypeCountedInt, + "", "" ); + poFeatureDefn->AddFieldDefn( poFieldDefn ); + } + else if( bApproxOK ) + { + // Fallback to treating everything else as a string field. + OGRFieldDefn oModFieldDefn(poFieldDefn); + oModFieldDefn.SetType( OFTString ); + poVecSeg->AddField( poFieldDefn->GetNameRef(), + PCIDSK::FieldTypeString, + "", "" ); + poFeatureDefn->AddFieldDefn( &oModFieldDefn ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create field '%s' of unsupported data type.", + poFieldDefn->GetNameRef() ); + } + } + +/* -------------------------------------------------------------------- */ +/* Trap exceptions and report as CPL errors. */ +/* -------------------------------------------------------------------- */ + catch( PCIDSK::PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return OGRERR_FAILURE; + } + catch(...) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Non-PCIDSK exception trapped." ); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/pcidskdataset.cpp b/bazaar/plugin/gdal/frmts/pcidsk/pcidskdataset.cpp new file mode 100644 index 000000000..85d24dfb2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/pcidskdataset.cpp @@ -0,0 +1,1644 @@ +/****************************************************************************** + * $Id: pcidskdataset.cpp 28373 2015-01-30 00:14:24Z rouault $ + * + * Project: PCIDSK Database File + * Purpose: Read/write PCIDSK Database File used by the PCI software + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2003, Andrey Kiselev + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pcidsk.h" + +CPL_CVSID("$Id: pcidskdataset.cpp 28373 2015-01-30 00:14:24Z rouault $"); + +CPL_C_START +void GDALRegister_PCIDSK(void); +CPL_C_END + +const int nSegBlocks = 64; // Number of blocks of Segment Pointers +const int nGeoSegBlocks = 8; // Number of blocks in GEO Segment + +/************************************************************************/ +/* PCIDSKDataset() */ +/************************************************************************/ + +PCIDSKDataset::PCIDSKDataset() +{ + pszFilename = NULL; + fp = NULL; + nFileSize = 0; + nBands = 0; + pszCreatTime = NULL; + nGeoOffset = 0; + bGeoSegmentDirty = FALSE; + + nBlockMapSeg = 0; + + nSegCount = 0; + panSegType = NULL; + papszSegName = NULL; + panSegOffset = NULL; + panSegSize = NULL; + + pszProjection = CPLStrdup( "" ); + pszGCPProjection = CPLStrdup( "" ); + nGCPCount = 0; + pasGCPList = NULL; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + bGeoTransformValid = FALSE; + + nBandFileCount = 0; + pafpBandFiles = NULL; +} + +/************************************************************************/ +/* ~PCIDSKDataset() */ +/************************************************************************/ + +PCIDSKDataset::~PCIDSKDataset() +{ + int i; + + FlushCache(); + + if ( pszProjection ) + CPLFree( pszProjection ); + if ( pszGCPProjection ) + CPLFree( pszGCPProjection ); + if( fp != NULL ) + VSIFCloseL( fp ); + if( pszCreatTime ) + CPLFree( pszCreatTime ); + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + CPLFree( panSegOffset ); + CPLFree( panSegSize ); + CPLFree( panSegType ); + + for( i = 0; i < nSegCount; i++ ) + if( papszSegName[i] != NULL ) + CPLFree( papszSegName[i] ); + CPLFree( papszSegName ); + + for( i = 0; i < nBandFileCount; i++ ) + VSIFCloseL( pafpBandFiles[i] ); + CPLFree( pafpBandFiles ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr PCIDSKDataset::GetGeoTransform( double * padfTransform ) +{ + if( !bGeoTransformValid ) + return GDALPamDataset::GetGeoTransform( padfTransform ); + else + { + memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0])*6 ); + return CE_None; + } +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr PCIDSKDataset::SetGeoTransform( double * padfTransform ) +{ + memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 ); + bGeoSegmentDirty = TRUE; + bGeoTransformValid = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *PCIDSKDataset::GetProjectionRef() +{ + if( pszProjection ) + return pszProjection; + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr PCIDSKDataset::SetProjection( const char *pszNewProjection ) + +{ + if( pszProjection ) + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszNewProjection ); + bGeoSegmentDirty = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int PCIDSKDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *PCIDSKDataset::GetGCPProjection() + +{ + if( nGCPCount > 0 ) + return pszGCPProjection; + else + return GDALPamDataset::GetGCPProjection(); +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *PCIDSKDataset::GetGCPs() +{ + return pasGCPList; +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void PCIDSKDataset::FlushCache() +{ + GDALPamDataset::FlushCache(); + + if( GetAccess() == GA_Update ) + { + char szTemp[64]; + +/* -------------------------------------------------------------------- */ +/* Write out pixel size. */ +/* -------------------------------------------------------------------- */ + CPLPrintDouble( szTemp, "%16.9E", ABS(adfGeoTransform[1]), "C" ); + CPLPrintDouble( szTemp + 16, "%16.9E", ABS(adfGeoTransform[5]), "C" ); + + VSIFSeekL( fp, 408, SEEK_SET ); + VSIFWriteL( (void *)szTemp, 1, 32, fp ); + +/* -------------------------------------------------------------------- */ +/* Write out Georeferencing segment. */ +/* -------------------------------------------------------------------- */ + if ( nGeoOffset && bGeoSegmentDirty ) + { + WriteGeoSegment(); + bGeoSegmentDirty = FALSE; + } + } +} + +/************************************************************************/ +/* WriteGeoSegment() */ +/************************************************************************/ + +void PCIDSKDataset::WriteGeoSegment( ) +{ + char szTemp[3072]; + struct tm oUpdateTime; + time_t nTime = VSITime(NULL); + char *pszP = pszProjection; + OGRSpatialReference oSRS; + int i; + +#ifdef DEBUG + CPLDebug( "PCIDSK", "Writing out georeferencing segment." ); +#endif + + VSILocalTime( &nTime, &oUpdateTime ); + + CPLPrintStringFill( szTemp, "Master Georeferencing Segment for File", 64 ); + CPLPrintStringFill( szTemp + 64, "", 64 ); + if ( pszCreatTime ) + CPLPrintStringFill( szTemp + 128, pszCreatTime, 16 ); + else + CPLPrintTime( szTemp + 128, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" ); + CPLPrintTime( szTemp + 144, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" ); + CPLPrintStringFill( szTemp + 160, "", 224 ); + // Write the history line + CPLPrintStringFill( szTemp + 384, + "GDAL: Master Georeferencing Segment for File", 64 ); + CPLPrintTime( szTemp + 448, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" ); + // Fill other lines with spaces + CPLPrintStringFill( szTemp + 464, "", 80 * 7 ); + + // More history lines may be used, if needed. + // CPLPrintStringFill( szTemp + 464, "History2", 80 ); + // CPLPrintStringFill( szTemp + 544, "History3", 80 ); + // CPLPrintStringFill( szTemp + 624, "History4", 80 ); + // CPLPrintStringFill( szTemp + 704, "History5", 80 ); + // CPLPrintStringFill( szTemp + 784, "History6", 80 ); + // CPLPrintStringFill( szTemp + 864, "History7", 80 ); + // CPLPrintStringFill( szTemp + 944, "History8", 80 ); + + VSIFSeekL( fp, nGeoOffset, SEEK_SET ); + VSIFWriteL( (void *)szTemp, 1, 1024, fp ); + + CPLPrintStringFill( szTemp, "PROJECTION", 16 ); + CPLPrintStringFill( szTemp + 16, "PIXEL", 16 ); + + if( pszProjection != NULL && !EQUAL( pszProjection, "" ) + && oSRS.importFromWkt( &pszP ) == OGRERR_NONE ) + { + char *pszProj = NULL; + char *pszUnits = NULL; + double *padfPrjParms = NULL; + + oSRS.exportToPCI( &pszProj, &pszUnits, &padfPrjParms ); + CPLPrintStringFill( szTemp + 32, pszProj, 16 ); + + CPLPrintInt32( szTemp + 48, 3, 8 ); + CPLPrintInt32( szTemp + 56, 3, 8 ); + + CPLPrintStringFill( szTemp + 64, pszUnits, 16 ); + + for ( i = 0; i < 17; i++ ) + { + CPLPrintDouble( szTemp + 80 + 26 * i, + "%26.18E", padfPrjParms[i], "C" ); + } + + CPLPrintStringFill( szTemp + 522, "", 936 ); + + if ( pszProj ) + CPLFree( pszProj ); + if ( pszUnits ) + CPLFree(pszUnits ); + if ( padfPrjParms ) + CPLFree( padfPrjParms ); + } + else + { + if( adfGeoTransform[0] == 0.0 + && adfGeoTransform[1] == 1.0 + && adfGeoTransform[2] == 0.0 + && adfGeoTransform[3] == 0.0 + && adfGeoTransform[4] == 0.0 + && ABS(adfGeoTransform[5]) == 1.0 ) + { + // no georeferencing at all. + CPLPrintStringFill( szTemp + 32, "PIXEL", 16 ); + } + else + { + // georeferenced but not a known coordinate system. + CPLPrintStringFill( szTemp + 32, "METER", 16 ); + } + CPLPrintInt32( szTemp + 48, 3, 8 ); + CPLPrintInt32( szTemp + 56, 3, 8 ); + CPLPrintStringFill( szTemp + 64, "METER", 16 ); + CPLPrintStringFill( szTemp + 80, "", 1378 ); + } + + /* TODO: USGS format */ + CPLPrintStringFill( szTemp + 1458, "", 1614 ); + + for ( i = 0; i < 3; i++ ) + { + CPLPrintDouble( szTemp + 1980 + 26 * i, + "%26.18E", adfGeoTransform[i], "C" ); + } + for ( i = 0; i < 3; i++ ) + { + CPLPrintDouble( szTemp + 2526 + 26 * i, + "%26.18E", adfGeoTransform[i + 3], "C" ); + } + + VSIFWriteL( (void *)szTemp, 1, 3072, fp ); + + // Now make the segment active + szTemp[0] = 'A'; + VSIFSeekL( fp, nGeoPtrOffset, SEEK_SET ); + VSIFWriteL( (void *)szTemp, 1, 1, fp ); +} + +/************************************************************************/ +/* PCIDSKTypeToGDAL() */ +/************************************************************************/ + +GDALDataType PCIDSKDataset::PCIDSKTypeToGDAL( const char *pszType ) +{ + if ( EQUALN( pszType, "8U", 2 ) ) + return GDT_Byte; + if ( EQUALN( pszType, "16S", 3 ) ) + return GDT_Int16; + if ( EQUALN( pszType, "16U", 3 ) ) + return GDT_UInt16; + if ( EQUALN( pszType, "32R", 3 ) ) + return GDT_Float32; + + return GDT_Unknown; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int PCIDSKDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + if( poOpenInfo->nHeaderBytes < 512 + || !EQUALN((const char *) poOpenInfo->pabyHeader, "PCIDSK ", 8) ) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *PCIDSKDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + PCIDSKDataset *poDS; + + poDS = new PCIDSKDataset(); + + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" ); + if ( !poDS->fp ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to re-open %s within PCIDSK driver.\n", + poOpenInfo->pszFilename ); + delete poDS; + return NULL; + } + poDS->eAccess = poOpenInfo->eAccess; + +/* ==================================================================== */ +/* Read PCIDSK File Header. */ +/* ==================================================================== */ + char szTemp[1024]; + char *pszString; + + VSIFSeekL( poDS->fp, 0, SEEK_END ); + poDS->nFileSize = VSIFTellL( poDS->fp ); + +/* -------------------------------------------------------------------- */ +/* Read File Identification. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( poDS->fp, 0, SEEK_SET ); + VSIFReadL( szTemp, 1, 512, poDS->fp ); + + pszString = CPLScanString( szTemp + 8, 8, TRUE, TRUE ); + poDS->SetMetadataItem( "SOFTWARE", pszString ); + CPLFree( pszString ); + + pszString = CPLScanString( szTemp + 48, 64, TRUE, TRUE ); + poDS->SetMetadataItem( "FILE_ID", pszString ); + CPLFree( pszString ); + + pszString = CPLScanString( szTemp + 112, 32, TRUE, TRUE ); + poDS->SetMetadataItem( "GENERATING_FACILITY", pszString ); + CPLFree( pszString ); + + pszString = CPLScanString( szTemp + 144, 64, TRUE, TRUE ); + poDS->SetMetadataItem( "DESCRIPTION1", pszString ); + CPLFree( pszString ); + + pszString = CPLScanString( szTemp + 208, 64, TRUE, TRUE ); + poDS->SetMetadataItem( "DESCRIPTION2", pszString ); + CPLFree( pszString ); + + pszString = CPLScanString( szTemp + 272, 16, TRUE, TRUE ); + poDS->SetMetadataItem( "DATE_OF_CREATION", pszString ); + CPLFree( pszString ); + // Store original creation time string for later use + poDS->pszCreatTime = CPLScanString( szTemp + 272, 16, TRUE, FALSE ); + + pszString = CPLScanString( szTemp + 288, 16, TRUE, TRUE ); + poDS->SetMetadataItem( "DATE_OF_UPDATE", pszString ); + CPLFree( pszString ); + +/* ==================================================================== */ +/* Read Segment Pointers. */ +/* ==================================================================== */ + vsi_l_offset nSegPointersStart; // Start block of Segment Pointers + vsi_l_offset nSegPointersOffset; // Offset in bytes to Pointers + int nSegBlocks; // Number of blocks of Segment Pointers + + { + VSIFSeekL( poDS->fp, 440, SEEK_SET ); + VSIFReadL( szTemp, 1, 16, poDS->fp ); + szTemp[16] = '\0'; + nSegPointersStart = atol( szTemp ); // XXX: should be atoll() + nSegPointersOffset = ( nSegPointersStart - 1 ) * 512; + + VSIFSeekL( poDS->fp, 456, SEEK_SET ); + VSIFReadL( szTemp, 1, 8, poDS->fp ); + nSegBlocks = CPLScanLong( szTemp, 8 ); + poDS->nSegCount = ( nSegBlocks * 512 ) / 32; + + if ( poDS->nSegCount < 0 || + nSegPointersOffset + poDS->nSegCount * 32 >= poDS->nFileSize ) + { + CPLDebug("PCIDSK", "nSegCount=%d", poDS->nSegCount); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Allocate segment info structures. */ +/* -------------------------------------------------------------------- */ + poDS->panSegType = + (int *) VSICalloc( sizeof(int), poDS->nSegCount ); + poDS->papszSegName = + (char **) VSICalloc( sizeof(char*), poDS->nSegCount ); + poDS->panSegOffset = (vsi_l_offset *) + VSICalloc( sizeof(vsi_l_offset), poDS->nSegCount ); + poDS->panSegSize = (vsi_l_offset *) + VSICalloc( sizeof(vsi_l_offset), poDS->nSegCount ); + + if (poDS->panSegType == NULL || + poDS->papszSegName == NULL || + poDS->panSegOffset == NULL || + poDS->panSegSize == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Not enough memory to hold segment description of %s", + poOpenInfo->pszFilename ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Parse each segment pointer. */ +/* -------------------------------------------------------------------- */ + int iSeg; + + for( iSeg = 0; iSeg < poDS->nSegCount; iSeg++ ) + { + int bActive, nSegStartBlock, nSegSize; + char szSegName[9]; + + VSIFSeekL( poDS->fp, nSegPointersOffset + iSeg * 32, SEEK_SET ); + if (VSIFReadL( szTemp, 1, 32, poDS->fp ) != 32) + { + delete poDS; + return NULL; + } + szTemp[32] = '\0'; + + strncpy( szSegName, szTemp+4, 8 ); + szSegName[8] = '\0'; + + if ( szTemp[0] == 'A' || szTemp[0] == 'L' ) + bActive = TRUE; + else + bActive = FALSE; + + if( !bActive ) + continue; + + poDS->panSegType[iSeg] = CPLScanLong( szTemp + 1, 3 ); + nSegStartBlock = CPLScanLong( szTemp+12, 11 ); + nSegSize = CPLScanLong( szTemp+23, 9 ); + + poDS->papszSegName[iSeg] = CPLStrdup( szSegName ); + poDS->panSegOffset[iSeg] = 512 * ((vsi_l_offset) (nSegStartBlock-1)); + poDS->panSegSize[iSeg] = 512 * ((vsi_l_offset) nSegSize); + + CPLDebug( "PCIDSK", + "Seg=%d, Type=%d, Start=%9d, Size=%7d, Name=%s", + iSeg+1, poDS->panSegType[iSeg], + nSegStartBlock, nSegSize, szSegName ); + + // Some segments will be needed sooner, rather than later. + + if( poDS->panSegType[iSeg] == 182 && EQUAL(szSegName,"SysBMDir")) + poDS->nBlockMapSeg = iSeg+1; + } + } + +/* -------------------------------------------------------------------- */ +/* Read Image Data. */ +/* -------------------------------------------------------------------- */ + vsi_l_offset nImageStart; // Start block of image data + vsi_l_offset nImgHdrsStart; // Start block of image headers + vsi_l_offset nImageOffset; // Offset to the first byte of the image + int nByteBands, nInt16Bands, nUInt16Bands, nFloat32Bands; + + pszString = CPLScanString( szTemp + 304, 16, TRUE, FALSE ); + if ( !EQUAL( pszString, "" ) ) + nImageStart = atol( pszString );// XXX: should be atoll() + else + nImageStart = 1; + CPLFree( pszString ); + nImageOffset = (nImageStart - 1) * 512; + + pszString = CPLScanString( szTemp + 336, 16, TRUE, FALSE ); + nImgHdrsStart = atol( pszString ); // XXX: should be atoll() + CPLFree( pszString ); + + poDS->nBands = CPLScanLong( szTemp + 376, 8 ); + poDS->nRasterXSize = CPLScanLong( szTemp + 384, 8 ); + poDS->nRasterYSize = CPLScanLong( szTemp + 392, 8 ); + + if ( poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid dimensions : %d x %d", + poDS->nRasterXSize, poDS->nRasterYSize ); + delete poDS; + return NULL; + } + + nByteBands = CPLScanLong( szTemp + 464, 4 ); + nInt16Bands = CPLScanLong( szTemp + 468, 4 ); + nUInt16Bands = CPLScanLong( szTemp + 472, 4 ); + nFloat32Bands = CPLScanLong( szTemp + 476, 4 ); + + // If these fields are blank, then it is assumed that all channels are 8-bit + if ( nByteBands == 0 && nInt16Bands == 0 + && nUInt16Bands == 0 && nFloat32Bands == 0 ) + nByteBands = poDS->nBands; + +/* ==================================================================== */ +/* Read Image Headers and create band information objects. */ +/* ==================================================================== */ + int iBand; + PCIDSKInterleaving eInterleaving; + +/* -------------------------------------------------------------------- */ +/* Read type of interleaving and set up image parameters. */ +/* -------------------------------------------------------------------- */ + pszString = CPLScanString( szTemp + 360, 8, TRUE, FALSE ); + if ( EQUALN( pszString, "PIXEL", 5 ) ) + eInterleaving = PDI_PIXEL; + else if ( EQUALN( pszString, "BAND", 4 ) ) + eInterleaving = PDI_BAND; + else if ( EQUALN( pszString, "FILE", 4 ) ) + eInterleaving = PDI_FILE; + else + { + CPLDebug( "PCIDSK", + "PCIDSK interleaving type %s is not supported by GDAL", + pszString ); + delete poDS; + return NULL; + } + CPLFree( pszString ); + + for( iBand = 0; iBand < poDS->nBands; iBand++ ) + { + GDALDataType eType; + GDALRasterBand *poBand = NULL; + vsi_l_offset nImgHdrOffset = (nImgHdrsStart - 1 + iBand * 2) * 512; + vsi_l_offset nPixelOffset = 0, nLineOffset = 0, nLineSize = 0; + int bNativeOrder; + int i; + VSILFILE *fp = poDS->fp; + + VSIFSeekL( poDS->fp, nImgHdrOffset, SEEK_SET ); + if ( VSIFReadL( szTemp, 1, 1024, poDS->fp ) != 1024 ) + { + delete poDS; + return NULL; + } + + pszString = CPLScanString( szTemp + 160, 8, TRUE, FALSE ); + eType = poDS->PCIDSKTypeToGDAL( pszString ); + + // Old files computed type based on list. + if( eType == GDT_Unknown && pszString[0] == '\0' ) + { + if( iBand < nByteBands ) + eType = GDT_Byte; + else if( iBand < nByteBands + nInt16Bands ) + eType = GDT_Int16; + else if( iBand < nByteBands + nInt16Bands + nUInt16Bands ) + eType = GDT_UInt16; + else if( iBand < nByteBands + nInt16Bands + nUInt16Bands + + nFloat32Bands ) + eType = GDT_Float32; + } + + if ( eType == GDT_Unknown ) + { + CPLDebug( "PCIDSK", + "PCIDSK data type %s is not supported by GDAL", + pszString ); + delete poDS; + return NULL; + } + CPLFree( pszString ); + + switch ( eInterleaving ) + { + case PDI_PIXEL: + nPixelOffset = nByteBands + 2 * (nInt16Bands + nUInt16Bands) + + 4 * nFloat32Bands; + nLineSize = nPixelOffset * poDS->nRasterXSize; + nLineOffset = ((int)((nLineSize + 511)/512)) * 512; + break; + case PDI_BAND: + nPixelOffset = GDALGetDataTypeSize( eType ) / 8; + nLineOffset = nPixelOffset * poDS->nRasterXSize; + break; + case PDI_FILE: + { + char *pszFilename; + + // Read the filename + pszFilename = CPLScanString( szTemp + 64, 64, TRUE, FALSE ); + + // /SIS=n is special case for internal tiled file. + if( EQUALN(pszFilename,"/SIS=",5) ) + { + int nImage = atoi(pszFilename+5); + poBand = new PCIDSKTiledRasterBand( poDS, iBand+1, nImage ); + if ( poBand->GetXSize() == 0 ) + { + CPLFree( pszFilename ); + delete poBand; + delete poDS; + return NULL; + } + } + + // Non-empty filename means we have data stored in + // external raw file + else if ( !EQUAL(pszFilename, "") ) + { + CPLDebug( "PCIDSK", "pszFilename=%s", pszFilename ); + + if( poOpenInfo->eAccess == GA_ReadOnly ) + fp = VSIFOpenL( pszFilename, "rb" ); + else + fp = VSIFOpenL( pszFilename, "r+b" ); + + if ( !fp ) + { + CPLDebug( "PCIDSK", + "Cannot open external raw file %s", + pszFilename ); + iBand--; + poDS->nBands--; + CPLFree( pszFilename ); + continue; + } + + poDS->nBandFileCount++; + poDS->pafpBandFiles = (VSILFILE **) + CPLRealloc( poDS->pafpBandFiles, + poDS->nBandFileCount * sizeof(VSILFILE*) ); + poDS->pafpBandFiles[poDS->nBandFileCount-1] = fp; + } + + pszString = CPLScanString( szTemp + 168, 16, TRUE, FALSE ); + nImageOffset = atol( pszString ); // XXX: should be atoll() + CPLFree( pszString ); + + nPixelOffset = CPLScanLong( szTemp + 184, 8 ); + nLineOffset = CPLScanLong( szTemp + + 192, 8 ); + + CPLFree( pszFilename ); + } + break; + default: /* NOTREACHED */ + break; + } + +/* -------------------------------------------------------------------- */ +/* Create raw band, only if we didn't already get a tiled band. */ +/* -------------------------------------------------------------------- */ + if( poBand == NULL ) + { +#ifdef CPL_MSB + bNativeOrder = ( szTemp[201] == 'S')?FALSE:TRUE; +#else + bNativeOrder = ( szTemp[201] == 'S')?TRUE:FALSE; +#endif + +#ifdef DEBUG + CPLDebug( "PCIDSK", + "Band %d: nImageOffset=" CPL_FRMT_GIB ", nPixelOffset=" CPL_FRMT_GIB ", " + "nLineOffset=" CPL_FRMT_GIB ", nLineSize=" CPL_FRMT_GIB, + iBand + 1, (GIntBig)nImageOffset, (GIntBig)nPixelOffset, + (GIntBig)nLineOffset, (GIntBig)nLineSize ); +#endif + + poBand = new PCIDSKRawRasterBand( poDS, iBand + 1, fp, + nImageOffset, + (int) nPixelOffset, + (int) nLineOffset, + eType, bNativeOrder); + + switch ( eInterleaving ) + { + case PDI_PIXEL: + nImageOffset += GDALGetDataTypeSize( eType ) / 8; + break; + case PDI_BAND: + nImageOffset += nLineOffset * poDS->nRasterYSize; + break; + default: + break; + } + } + + poDS->SetBand( iBand + 1, poBand ); + +/* -------------------------------------------------------------------- */ +/* Read and assign few metadata parameters to each image band. */ +/* -------------------------------------------------------------------- */ + pszString = CPLScanString( szTemp, 64, TRUE, TRUE ); + poBand->SetDescription( pszString ); + CPLFree( pszString ); + + pszString = CPLScanString( szTemp + 128, 16, TRUE, TRUE ); + poBand->SetMetadataItem( "DATE_OF_CREATION", pszString ); + CPLFree( pszString ); + + pszString = CPLScanString( szTemp + 144, 16, TRUE, TRUE ); + poBand->SetMetadataItem( "DATE_OF_UPDATE", pszString ); + CPLFree( pszString ); + + pszString = CPLScanString( szTemp + 202, 16, TRUE, TRUE ); + if ( !EQUAL( szTemp, "" ) ) + poBand->SetMetadataItem( "UNITS", pszString ); + CPLFree( pszString ); + + for ( i = 0; i < 8; i++ ) + { + pszString = CPLScanString( szTemp + 384 + i * 80, 80, TRUE, TRUE ); + if ( !EQUAL( pszString, "" ) ) + poBand->SetMetadataItem( CPLSPrintf("HISTORY%d", i + 1), + pszString ); + CPLFree( pszString ); + } + + } + + if (!poDS->GetRasterCount()) + CPLError(CE_Warning, CPLE_None, "Dataset contain no raster bands."); + +/* ==================================================================== */ +/* Process some segments of interest. */ +/* ==================================================================== */ + int iSeg; + + for( iSeg = 0; iSeg < poDS->nSegCount; iSeg++ ) + { + switch( poDS->panSegType[iSeg] ) + { +/* -------------------------------------------------------------------- */ +/* Georeferencing segment. */ +/* -------------------------------------------------------------------- */ + case 150: // GEO segment + { + vsi_l_offset nGeoDataOffset; + int j, nXCoeffs, nYCoeffs; + OGRSpatialReference oSRS; + + poDS->nGeoPtrOffset = nSegPointersOffset + iSeg * 32; + poDS->nGeoOffset = poDS->panSegOffset[iSeg]; + nGeoDataOffset = poDS->nGeoOffset + 1024; + + VSIFSeekL( poDS->fp, nGeoDataOffset, SEEK_SET ); + VSIFReadL( szTemp, 1, 16, poDS->fp ); + szTemp[16] = '\0'; + if ( EQUALN( szTemp, "POLYNOMIAL", 10 ) ) + { + char szProj[17]; + + // Read projection definition + VSIFSeekL( poDS->fp, nGeoDataOffset + 32, SEEK_SET ); + VSIFReadL( szProj, 1, 16, poDS->fp ); + szProj[16] = '\0'; + if ( EQUALN( szProj, "PIXEL", 5 ) ) + break; + + // Read number of transform coefficients + VSIFSeekL( poDS->fp, nGeoDataOffset + 48, SEEK_SET ); + VSIFReadL( szTemp, 1, 16, poDS->fp ); + nXCoeffs = CPLScanLong( szTemp, 8 ); + if ( nXCoeffs > 3 ) + nXCoeffs = 3; + nYCoeffs = CPLScanLong( szTemp + 8, 8 ); + if ( nYCoeffs > 3 ) + nYCoeffs = 3; + + // Read geotransform coefficients + VSIFSeekL( poDS->fp, nGeoDataOffset + 212, SEEK_SET ); + VSIFReadL( szTemp, 1, nXCoeffs * 26, poDS->fp ); + for ( j = 0; j < nXCoeffs; j++ ) + { + poDS->adfGeoTransform[j] = + CPLScanDouble( szTemp + 26 * j, 26 ); + } + VSIFSeekL( poDS->fp, nGeoDataOffset + 1642, SEEK_SET ); + VSIFReadL( szTemp, 1, nYCoeffs * 26, poDS->fp ); + for ( j = 0; j < nYCoeffs; j++ ) + { + poDS->adfGeoTransform[j + 3] = + CPLScanDouble( szTemp + 26 * j, 26 ); + } + + poDS->bGeoTransformValid = TRUE; + + oSRS.importFromPCI( szProj, NULL, NULL ); + if ( poDS->pszProjection ) + CPLFree( poDS->pszProjection ); + oSRS.exportToWkt( &poDS->pszProjection ); + } + else if ( EQUALN( szTemp, "PROJECTION", 10 ) ) + { + char szProj[17], szUnits[17]; + double adfProjParms[17]; + + // Read projection definition + VSIFSeekL( poDS->fp, nGeoDataOffset + 32, SEEK_SET ); + VSIFReadL( szProj, 1, 16, poDS->fp ); + szProj[16] = '\0'; + if ( EQUALN( szProj, "PIXEL", 5 ) + || EQUALN( szProj, "METRE", 5 ) ) + break; + + // Read number of transform coefficients + VSIFSeekL( poDS->fp, nGeoDataOffset + 48, SEEK_SET ); + VSIFReadL( szTemp, 1, 16, poDS->fp ); + nXCoeffs = CPLScanLong( szTemp, 8 ); + if ( nXCoeffs > 3 ) + nXCoeffs = 3; + nYCoeffs = CPLScanLong( szTemp + 8, 8 ); + if ( nYCoeffs > 3 ) + nYCoeffs = 3; + + // Read grid units definition + VSIFSeekL( poDS->fp, nGeoDataOffset + 64, SEEK_SET ); + VSIFReadL( szUnits, 1, 16, poDS->fp ); + szUnits[16] = '\0'; + + // Read 16 projection parameters + VSIFSeekL( poDS->fp, nGeoDataOffset + 80, SEEK_SET ); + VSIFReadL( szTemp, 1, 26 * 16, poDS->fp ); + for ( j = 0; j < 17; j++ ) + { + adfProjParms[j] = + CPLScanDouble( szTemp + 26 * j, 26 ); + } + + // Read geotransform coefficients + VSIFSeekL( poDS->fp, nGeoDataOffset + 1980, SEEK_SET ); + VSIFReadL( szTemp, 1, nXCoeffs * 26, poDS->fp ); + for ( j = 0; j < nXCoeffs; j++ ) + { + poDS->adfGeoTransform[j] = + CPLScanDouble( szTemp + 26 * j, 26 );; + } + VSIFSeekL( poDS->fp, nGeoDataOffset + 2526, SEEK_SET ); + VSIFReadL( szTemp, 1, nYCoeffs * 26, poDS->fp ); + for ( j = 0; j < nYCoeffs; j++ ) + { + poDS->adfGeoTransform[j + 3] = + CPLScanDouble( szTemp + 26 * j, 26 ); + } + + poDS->bGeoTransformValid = TRUE; + + oSRS.importFromPCI( szProj, szUnits, adfProjParms ); + if ( poDS->pszProjection ) + CPLFree( poDS->pszProjection ); + oSRS.exportToWkt( &poDS->pszProjection ); + } + } + break; + +/* -------------------------------------------------------------------- */ +/* GCP Segment */ +/* -------------------------------------------------------------------- */ + case 214: // GCP segment + { + vsi_l_offset nGcpDataOffset; + int j; + OGRSpatialReference oSRS; + + poDS->nGcpPtrOffset = nSegPointersOffset + iSeg * 32; + poDS->nGcpOffset = poDS->panSegOffset[iSeg]; + nGcpDataOffset = poDS->nGcpOffset + 1024; + + if( !poDS->nGCPCount ) // XXX: We will read the + // first GCP segment only + { + VSIFSeekL( poDS->fp, nGcpDataOffset, SEEK_SET ); + VSIFReadL( szTemp, 1, 80, poDS->fp ); + poDS->nGCPCount = CPLScanLong( szTemp, 16 ); + if ( poDS->nGCPCount > 0 && + nGcpDataOffset + poDS->nGCPCount * 128 + 512 < poDS->nFileSize ) + { + double dfUnitConv = 1.0; + char szProj[17]; + + memcpy( szProj, szTemp + 32, 16 ); + szProj[16] = '\0'; + oSRS.importFromPCI( szProj, NULL, NULL ); + if ( poDS->pszGCPProjection ) + CPLFree( poDS->pszGCPProjection ); + oSRS.exportToWkt( &poDS->pszGCPProjection ); + poDS->pasGCPList = (GDAL_GCP *) + CPLCalloc( poDS->nGCPCount, sizeof(GDAL_GCP) ); + GDALInitGCPs( poDS->nGCPCount, poDS->pasGCPList ); + if ( EQUALN( szTemp + 64, "FEET ", 9 ) ) + dfUnitConv = CPLAtof(SRS_UL_FOOT_CONV); + for ( j = 0; j < poDS->nGCPCount; j++ ) + { + VSIFSeekL( poDS->fp, nGcpDataOffset + j * 128 + 512, + SEEK_SET ); + VSIFReadL( szTemp, 1, 128, poDS->fp ); + poDS->pasGCPList[j].dfGCPPixel = + CPLScanDouble( szTemp + 6, 18 ); + poDS->pasGCPList[j].dfGCPLine = + CPLScanDouble( szTemp + 24, 18 ); + poDS->pasGCPList[j].dfGCPX = + CPLScanDouble( szTemp + 60, 18 ); + poDS->pasGCPList[j].dfGCPY = + CPLScanDouble( szTemp + 78, 18 ); + poDS->pasGCPList[j].dfGCPZ = + CPLScanDouble( szTemp + 96, 18 ) / dfUnitConv; + } + } + + poDS->bGeoTransformValid = TRUE; + } + } + break; + +/* -------------------------------------------------------------------- */ +/* SYS Segments. Process metadata immediately. */ +/* -------------------------------------------------------------------- */ + case 182: // SYS segment. + { + if( EQUAL(poDS->papszSegName[iSeg],"METADATA") ) + poDS->CollectPCIDSKMetadata( iSeg+1 ); + } + break; + + default: + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Check for band overviews. */ +/* -------------------------------------------------------------------- */ + for( iBand = 0; iBand < poDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand *poBand = poDS->GetRasterBand( iBand+1 ); + char **papszMD = poBand->GetMetadata( "PCISYS" ); + int iMD; + + for( iMD = 0; papszMD != NULL && papszMD[iMD] != NULL; iMD++ ) + { + if( EQUALN(papszMD[iMD],"Overview_",9) ) + { + int nBlockXSize, nBlockYSize; + int nImage = atoi(CPLParseNameValue( papszMD[iMD], NULL )); + PCIDSKTiledRasterBand *poOvBand; + + poOvBand = new PCIDSKTiledRasterBand( poDS, 0, nImage ); + if ( poOvBand->GetXSize() == 0 ) + { + delete poOvBand; + delete poDS; + return NULL; + } + + poBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + if( nBlockYSize == 1 ) + ((PCIDSKRawRasterBand *) poBand)-> + AttachOverview( poOvBand ); + else + ((PCIDSKTiledRasterBand *) poBand)-> + AttachOverview( poOvBand ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Check for worldfile if we have no other georeferencing. */ +/* -------------------------------------------------------------------- */ + if( !poDS->bGeoTransformValid ) + poDS->bGeoTransformValid = + GDALReadWorldFile( poOpenInfo->pszFilename, "pxw", + poDS->adfGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Open overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* SegRead() */ +/************************************************************************/ + +int PCIDSKDataset::SegRead( int nSegment, vsi_l_offset nOffset, + int nSize, void *pBuffer ) + +{ + if( nSegment < 1 || nSegment > nSegCount || panSegType[nSegment-1] == 0 ) + return 0; + + if( nOffset + nSize > panSegSize[nSegment-1] ) + { + return 0; + } + else + { + if( VSIFSeekL( fp, panSegOffset[nSegment-1]+nOffset+1024, + SEEK_SET ) != 0 ) + return 0; + + return VSIFReadL( pBuffer, 1, nSize, fp ); + } +} + + +/************************************************************************/ +/* CollectPCIDSKMetadata() */ +/************************************************************************/ + +void PCIDSKDataset::CollectPCIDSKMetadata( int nSegment ) + +{ + int nSegSize = (int) panSegSize[nSegment-1]; + +/* -------------------------------------------------------------------- */ +/* Read all metadata in one gulp. */ +/* -------------------------------------------------------------------- */ + char *pszMetadataBuf = (char *) VSICalloc( 1, nSegSize + 1 ); + if ( pszMetadataBuf == NULL ) + return; + + if( !SegRead( nSegment, 0, nSegSize, pszMetadataBuf ) ) + { + CPLFree( pszMetadataBuf ); + CPLError( CE_Warning, CPLE_FileIO, + "IO error reading metadata, ignoring." ); + return; + } + +/* ==================================================================== */ +/* Parse out domain/name/value sets. */ +/* ==================================================================== */ + char *pszNext = pszMetadataBuf; + + while( *pszNext != '\0' ) + { + char *pszName, *pszValue; + + pszName = pszNext; + +/* -------------------------------------------------------------------- */ +/* Identify the end of this line, and zero terminate it. */ +/* -------------------------------------------------------------------- */ + while( *pszNext != 10 && *pszNext != 12 && *pszNext != 0 ) + pszNext++; + + if( *pszNext != 0 ) + { + *(pszNext++) = '\0'; + while( *pszNext == 10 || *pszNext == 12 ) + pszNext++; + } + +/* -------------------------------------------------------------------- */ +/* Split off the value from the name. */ +/* -------------------------------------------------------------------- */ + pszValue = pszName; + while( *pszValue != 0 && *pszValue != ':' ) + pszValue++; + + if( *pszValue != 0 ) + *(pszValue++) = '\0'; + + while( *pszValue == ' ' ) + pszValue++; + +/* -------------------------------------------------------------------- */ +/* Handle METADATA_IMG values by assigning to the appropriate */ +/* band object. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszName,"METADATA_IMG_",13) ) + { + int nBand = atoi(pszName+13); + pszName += 13; + while( *pszName && *pszName != '_' ) + pszName++; + + if( *pszName == '_' ) + pszName++; + + if( nBand > 0 && nBand <= GetRasterCount() ) + { + GDALRasterBand *poBand = GetRasterBand( nBand ); + + if( *pszName == '_' ) + poBand->SetMetadataItem( pszName+1, pszValue, "PCISYS" ); + else + poBand->SetMetadataItem( pszName, pszValue ); + } + } + + else if( EQUALN(pszName,"METADATA_FIL",13) ) + { + pszName += 13; + if( *pszName == '_' ) + pszName++; + + if( *pszName == '_' ) + SetMetadataItem( pszName+1, pszValue, "PCISYS" ); + else + SetMetadataItem( pszName, pszValue ); + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( pszMetadataBuf ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *PCIDSKDataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char **papszOptions ) + +{ + if ( eType != GDT_Byte + && eType != GDT_Int16 + && eType != GDT_UInt16 + && eType != GDT_Float32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create PCIDSK dataset with an illegal data type (%s),\n" + "only Byte, Int16, UInt16 and Float32 supported by the format.\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to create the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFOpenL( pszFilename, "wb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create file %s.\n", pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Get current time to fill appropriate fields. */ +/* -------------------------------------------------------------------- */ + struct tm oUpdateTime; + time_t nTime = VSITime(NULL); + + VSILocalTime( &nTime, &oUpdateTime ); + +/* ==================================================================== */ +/* Fill the PCIDSK File Header. */ +/* ==================================================================== */ + const char *pszDesc; + char szTemp[1024]; + vsi_l_offset nImageStart; // Start block of image data + vsi_l_offset nSegPointersStart; // Start block of Segment Pointers + vsi_l_offset nImageBlocks; // Number of blocks of image data + int nImgHdrBlocks; // Number of blocks of image header data + +/* -------------------------------------------------------------------- */ +/* Calculate offsets. */ +/* -------------------------------------------------------------------- */ + nImgHdrBlocks = nBands * 2; + nSegPointersStart = 2 + nImgHdrBlocks; + nImageStart = nSegPointersStart + nSegBlocks; + nImageBlocks = + (nXSize * ((vsi_l_offset)nYSize) * nBands * (GDALGetDataTypeSize(eType)/8) + 512) / 512; + +/* -------------------------------------------------------------------- */ +/* Fill the File Identification. */ +/* -------------------------------------------------------------------- */ + CPLPrintStringFill( szTemp, "PCIDSK ", 8 ); + CPLPrintStringFill( szTemp + 8, "GDAL", 4 ); + CPLPrintStringFill( szTemp + 12, GDALVersionInfo( "VERSION_NUM" ), 4 ); + CPLPrintUIntBig( szTemp + 16, + nImageStart + nImageBlocks + nGeoSegBlocks - 1, 16 ); + CPLPrintStringFill( szTemp + 32, "", 16 ); + CPLPrintStringFill( szTemp + 48, CPLGetFilename(pszFilename), 64 ); + CPLPrintStringFill( szTemp + 112, "Created with GDAL", 32 ); + + pszDesc = CSLFetchNameValue( papszOptions, "FILEDESC1" ); + if ( !pszDesc ) + pszDesc = ""; + CPLPrintStringFill( szTemp + 144, pszDesc, 64 ); + + pszDesc = CSLFetchNameValue( papszOptions, "FILEDESC2" ); + if ( !pszDesc ) + pszDesc = ""; + CPLPrintStringFill( szTemp + 208, pszDesc, 64 ); + + CPLPrintTime( szTemp + 272, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" ); + CPLPrintTime( szTemp + 288, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" ); + +/* -------------------------------------------------------------------- */ +/* Fill the Image Data and Segment Pointers. */ +/* -------------------------------------------------------------------- */ + CPLPrintUIntBig( szTemp + 304, nImageStart, 16 ); + CPLPrintUIntBig( szTemp + 320, nImageBlocks, 16 ); + sprintf( szTemp + 336, "%16d", 2 ); + sprintf( szTemp + 352, "%8d", nImgHdrBlocks ); + CPLPrintStringFill( szTemp + 360, "BAND", 8 ); + CPLPrintStringFill( szTemp + 368, "", 8 ); + sprintf( szTemp + 376, "%8d", nBands ); + sprintf( szTemp + 384, "%8d", nXSize ); + sprintf( szTemp + 392, "%8d", nYSize ); + CPLPrintStringFill( szTemp + 400, "METRE", 8 ); + // Two following parameters will be filled with real values in FlushCache() + CPLPrintStringFill( szTemp + 408, "", 16 ); // X size of pixel + CPLPrintStringFill( szTemp + 424, "", 16 ); // Y size of pixel + + CPLPrintUIntBig( szTemp + 440, nSegPointersStart, 16 ); + sprintf( szTemp + 456, "%8d", nSegBlocks ); + if ( eType == GDT_Byte ) + sprintf( szTemp + 464, "%4d", nBands ); + else + CPLPrintStringFill( szTemp + 464, " 0", 4 ); + if ( eType == GDT_Int16 ) + sprintf( szTemp + 468, "%4d", nBands ); + else + CPLPrintStringFill( szTemp + 468, " 0", 4 ); + if ( eType == GDT_UInt16 ) + sprintf( szTemp + 472, "%4d", nBands ); + else + CPLPrintStringFill( szTemp + 472, " 0", 4 ); + if ( eType == GDT_Float32 ) + sprintf( szTemp + 476, "%4d", nBands ); + else + CPLPrintStringFill( szTemp + 476, " 0", 4 ); + CPLPrintStringFill( szTemp + 480, "", 32 ); + + VSIFSeekL( fp, 0, SEEK_SET ); + VSIFWriteL( (void *)szTemp, 1, 512, fp ); + +/* ==================================================================== */ +/* Fill the Image Headers. */ +/* ==================================================================== */ + int i; + + for ( i = 0; i < nBands; i++ ) + { + pszDesc = + CSLFetchNameValue( papszOptions, CPLSPrintf("BANDDESC%d", i + 1) ); + + if ( !pszDesc ) + pszDesc = CPLSPrintf( "Image band %d", i + 1 ); + + CPLPrintStringFill( szTemp, pszDesc, 64 ); + CPLPrintStringFill( szTemp + 64, "", 64 ); + CPLPrintTime( szTemp + 128, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" ); + CPLPrintTime( szTemp + 144, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" ); + switch ( eType ) + { + case GDT_Byte: + CPLPrintStringFill( szTemp + 160, "8U", 8 ); + break; + case GDT_Int16: + CPLPrintStringFill( szTemp + 160, "16S", 8 ); + break; + case GDT_UInt16: + CPLPrintStringFill( szTemp + 160, "16U", 8 ); + break; + case GDT_Float32: + CPLPrintStringFill( szTemp + 160, "32R", 8 ); + break; + default: + break; + } + CPLPrintStringFill( szTemp + 168, "", 16 ); + CPLPrintStringFill( szTemp + 184, "", 8 ); + CPLPrintStringFill( szTemp + 192, "", 8 ); + CPLPrintStringFill( szTemp + 200, " ", 1 ); + CPLPrintStringFill( szTemp + 201, "N", 1 ); // only N is supported! + CPLPrintStringFill( szTemp + 202, "", 48 ); + CPLPrintStringFill( szTemp + 250, "", 32 ); + CPLPrintStringFill( szTemp + 282, "", 8 ); + CPLPrintStringFill( szTemp + 290, "", 94 ); + + // Write the history line + CPLPrintStringFill( szTemp + 384, + "GDAL: Image band created with GDAL", 64 ); + CPLPrintTime( szTemp + 448, 16, "%H:%M %d-%b-%y ", &oUpdateTime, "C" ); + // Fill other lines with spaces + CPLPrintStringFill( szTemp + 464, "", 80 * 7 ); + + // More history lines may be used if needed. + // CPLPrintStringFill( szTemp + 464, "HistoryLine2", 80 ); + // CPLPrintStringFill( szTemp + 544, "HistoryLine3", 80 ); + // CPLPrintStringFill( szTemp + 624, "HistoryLine4", 80 ); + // CPLPrintStringFill( szTemp + 704, "HistoryLine5", 80 ); + // CPLPrintStringFill( szTemp + 784, "HistoryLine6", 80 ); + // CPLPrintStringFill( szTemp + 864, "HistoryLine7", 80 ); + // CPLPrintStringFill( szTemp + 944, "HistoryLine8", 80 ); + + VSIFWriteL( (void *)szTemp, 1, 1024, fp ); + } + +/* ==================================================================== */ +/* Fill the Segment Pointers. */ +/* ==================================================================== */ + int nSegments = ( nSegBlocks * 512 ) / 32; + +/* -------------------------------------------------------------------- */ +/* Write out pointer to the Georeferencing segment. */ +/* -------------------------------------------------------------------- */ + CPLPrintStringFill( szTemp, "A150GEOref", 12 ); + CPLPrintUIntBig( szTemp + 12, nImageStart + nImageBlocks, 11 ); + sprintf( szTemp + 23, "%9d", nGeoSegBlocks ); + VSIFWriteL( (void *)szTemp, 1, 32, fp ); + +/* -------------------------------------------------------------------- */ +/* Blank all other segment pointers */ +/* -------------------------------------------------------------------- */ + CPLPrintStringFill( szTemp, "", 32 ); + for ( i = 1; i < nSegments; i++ ) + VSIFWriteL( (void *)szTemp, 1, 32, fp ); + +/* -------------------------------------------------------------------- */ +/* Write out default georef segment. */ +/* -------------------------------------------------------------------- */ + static const char *apszGeoref[] = { + "Master Georeferencing Segment for File 17:27 11Nov2003 17:27 11Nov2003 "," POLYNOMIAL PIXEL PIXEL 3 3 0.000000000000000000D+00 1.000000000000000000D+00 0.000000000000000000D+00 "," 0.000000000000000000D+00 0.000000000000000000D+00 1.000000000000000000D+00 "," "," ", NULL}; + + for( i = 0; apszGeoref[i] != NULL; i++ ) + VSIFWriteL( (void *) apszGeoref[i], 1, strlen(apszGeoref[i]), fp ); + + VSIFCloseL( fp ); + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * +PCIDSKDataset::CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + CPL_UNUSED int bStrict, + char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + PCIDSKDataset *poDS; + GDALDataType eType; + int iBand; + + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "PCIDSK driver does not support source dataset with zero band.\n"); + return NULL; + } + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + + eType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + + /* check that other bands match type- sets type */ + /* to unknown if they differ. */ + for( iBand = 1; iBand < nBands; iBand++ ) + { + GDALRasterBand *poBand = poSrcDS->GetRasterBand( iBand+1 ); + eType = GDALDataTypeUnion( eType, poBand->GetRasterDataType() ); + } + + poDS = (PCIDSKDataset *) Create( pszFilename, + poSrcDS->GetRasterXSize(), + poSrcDS->GetRasterYSize(), + poSrcDS->GetRasterCount(), + eType, papszOptions ); + + /* Check that Create worked- return Null if it didn't */ + if (poDS == NULL) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Copy the image data. */ +/* -------------------------------------------------------------------- */ + int nXSize = poDS->GetRasterXSize(); + int nYSize = poDS->GetRasterYSize(); + int nBlockXSize, nBlockYSize, nBlockTotal, nBlocksDone; + + poDS->GetRasterBand(1)->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + nBlockTotal = ((nXSize + nBlockXSize - 1) / nBlockXSize) + * ((nYSize + nBlockYSize - 1) / nBlockYSize) + * poSrcDS->GetRasterCount(); + + nBlocksDone = 0; + for( iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + GDALRasterBand *poDstBand = poDS->GetRasterBand( iBand+1 ); + int iYOffset, iXOffset; + void *pData; + CPLErr eErr; + + + pData = CPLMalloc(nBlockXSize * nBlockYSize + * GDALGetDataTypeSize(eType) / 8); + + for( iYOffset = 0; iYOffset < nYSize; iYOffset += nBlockYSize ) + { + for( iXOffset = 0; iXOffset < nXSize; iXOffset += nBlockXSize ) + { + int nTBXSize, nTBYSize; + + if( !pfnProgress( (nBlocksDone++) / (float) nBlockTotal, + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated" ); + delete poDS; + + GDALDriver *poPCIDSKDriver = + (GDALDriver *) GDALGetDriverByName( "PCIDSK" ); + poPCIDSKDriver->Delete( pszFilename ); + return NULL; + } + + nTBXSize = MIN(nBlockXSize,nXSize-iXOffset); + nTBYSize = MIN(nBlockYSize,nYSize-iYOffset); + + eErr = poSrcBand->RasterIO( GF_Read, + iXOffset, iYOffset, + nTBXSize, nTBYSize, + pData, nTBXSize, nTBYSize, + eType, 0, 0, NULL ); + if( eErr != CE_None ) + { + return NULL; + } + + eErr = poDstBand->RasterIO( GF_Write, + iXOffset, iYOffset, + nTBXSize, nTBYSize, + pData, nTBXSize, nTBYSize, + eType, 0, 0, NULL ); + + if( eErr != CE_None ) + { + return NULL; + } + } + } + + CPLFree( pData ); + } + +/* -------------------------------------------------------------------- */ +/* Copy georeferencing information, if enough is available. */ +/* -------------------------------------------------------------------- */ + + double *tempGeoTransform=NULL; + + tempGeoTransform = (double *) CPLMalloc(6*sizeof(double)); + + if (( poSrcDS->GetGeoTransform( tempGeoTransform ) == CE_None) + && (tempGeoTransform[0] != 0.0 || tempGeoTransform[1] != 1.0 + || tempGeoTransform[2] != 0.0 || tempGeoTransform[3] != 0.0 + || tempGeoTransform[4] != 0.0 || ABS(tempGeoTransform[5]) != 1.0 )) + { + poDS->SetProjection(poSrcDS->GetProjectionRef()); + poDS->SetGeoTransform(tempGeoTransform); + } + CPLFree(tempGeoTransform); + + + poDS->FlushCache(); + + if( !pfnProgress( 1.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated" ); + delete poDS; + + GDALDriver *poPCIDSKDriver = + (GDALDriver *) GDALGetDriverByName( "PCIDSK" ); + poPCIDSKDriver->Delete( pszFilename ); + return NULL; + } + + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} +/************************************************************************/ +/* GDALRegister_PCIDSK() */ +/************************************************************************/ + +void GDALRegister_PCIDSK() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "PCIDSK" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "PCIDSK" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "PCIDSK Database File" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_pcidsk.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "pix" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte UInt16 Int16 Float32" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); + + poDriver->pfnIdentify = PCIDSKDataset::Identify; + poDriver->pfnOpen = PCIDSKDataset::Open; + poDriver->pfnCreate = PCIDSKDataset::Create; + poDriver->pfnCreateCopy = PCIDSKDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/pcidskdataset2.cpp b/bazaar/plugin/gdal/frmts/pcidsk/pcidskdataset2.cpp new file mode 100644 index 000000000..4c83a624f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/pcidskdataset2.cpp @@ -0,0 +1,2151 @@ +/****************************************************************************** + * $Id: pcidskdataset2.cpp 28292 2015-01-05 19:35:55Z rouault $ + * + * Project: PCIDSK Database File + * Purpose: Read/write PCIDSK Database File used by the PCI software, using + * the external PCIDSK library. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidskdataset2.h" + +CPL_CVSID("$Id: pcidskdataset2.cpp 28292 2015-01-05 19:35:55Z rouault $"); + +const PCIDSK::PCIDSKInterfaces *PCIDSK2GetInterfaces(void); + +/************************************************************************/ +/* ==================================================================== */ +/* PCIDSK2Band */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* PCIDSK2Band() */ +/* */ +/* This constructor is used for main file channels. */ +/************************************************************************/ + +PCIDSK2Band::PCIDSK2Band( PCIDSK2Dataset *poDS, + PCIDSKFile *poFile, + int nBand ) + +{ + Initialize(); + + this->poDS = poDS; + this->poFile = poFile; + this->nBand = nBand; + + poChannel = poFile->GetChannel( nBand ); + + nBlockXSize = (int) poChannel->GetBlockWidth(); + nBlockYSize = (int) poChannel->GetBlockHeight(); + + eDataType = PCIDSK2Dataset::PCIDSKTypeToGDAL( poChannel->GetType() ); + + if( !EQUALN(poChannel->GetDescription().c_str(), + "Contents Not Specified",20) ) + GDALMajorObject::SetDescription( poChannel->GetDescription().c_str() ); + +/* -------------------------------------------------------------------- */ +/* Do we have overviews? */ +/* -------------------------------------------------------------------- */ + RefreshOverviewList(); +} + +/************************************************************************/ +/* PCIDSK2Band() */ +/* */ +/* This constructor is used for overviews and bitmap segments */ +/* as bands. */ +/************************************************************************/ + +PCIDSK2Band::PCIDSK2Band( PCIDSKChannel *poChannel ) + +{ + Initialize(); + + this->poChannel = poChannel; + + nBand = 1; + + nBlockXSize = (int) poChannel->GetBlockWidth(); + nBlockYSize = (int) poChannel->GetBlockHeight(); + + nRasterXSize = (int) poChannel->GetWidth(); + nRasterYSize = (int) poChannel->GetHeight(); + + eDataType = PCIDSK2Dataset::PCIDSKTypeToGDAL( poChannel->GetType() ); + + if( poChannel->GetType() == CHN_BIT ) + { + SetMetadataItem( "NBITS", "1", "IMAGE_STRUCTURE" ); + + if( !EQUALN(poChannel->GetDescription().c_str(), + "Contents Not Specified",20) ) + GDALMajorObject::SetDescription( poChannel->GetDescription().c_str() ); + } +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void PCIDSK2Band::Initialize() + +{ + papszLastMDListValue = NULL; + + poChannel = NULL; + poFile = NULL; + poDS = NULL; + + bCheckedForColorTable = false; + poColorTable = NULL; + nPCTSegNumber = -1; + + papszCategoryNames = NULL; +} + +/************************************************************************/ +/* ~PCIDSK2Band() */ +/************************************************************************/ + +PCIDSK2Band::~PCIDSK2Band() + +{ + while( apoOverviews.size() > 0 ) + { + delete apoOverviews[apoOverviews.size()-1]; + apoOverviews.pop_back(); + } + CSLDestroy( papszLastMDListValue ); + CSLDestroy( papszCategoryNames ); + + delete poColorTable; +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +void PCIDSK2Band::SetDescription( const char *pszDescription ) + +{ + try + { + poChannel->SetDescription( pszDescription ); + + if( !EQUALN(poChannel->GetDescription().c_str(), + "Contents Not Specified",20) ) + GDALMajorObject::SetDescription( poChannel->GetDescription().c_str() ); + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + } +} + +/************************************************************************/ +/* GetCategoryNames() */ +/* */ +/* Offer category names from Class_*_ metadata. */ +/************************************************************************/ + +char **PCIDSK2Band::GetCategoryNames() + +{ + // already scanned? + if( papszCategoryNames != NULL ) + return papszCategoryNames; + + try + { + std::vector aosMDKeys = poChannel->GetMetadataKeys(); + size_t i; + int nClassCount = 0; + static const int nMaxClasses = 10000; + papszCategoryNames = (char **) CPLCalloc(nMaxClasses+1, sizeof(char*)); + + for( i=0; i < aosMDKeys.size(); i++ ) + { + CPLString osKey = aosMDKeys[i]; + + // is this a "Class_n_name" keyword? + + if( !EQUALN(osKey,"Class_",6) ) + continue; + + if( !EQUAL(osKey.c_str() + osKey.size() - 5, "_name") ) + continue; + + // Ignore unreasonable class values. + int iClass = atoi(osKey.c_str() + 6); + + if( iClass < 0 || iClass > 10000 ) + continue; + + // Fetch the name. + CPLString osName = poChannel->GetMetadataValue(osKey); + + // do we need to put in place dummy class names for missing values? + if( iClass >= nClassCount ) + { + while( iClass >= nClassCount ) + { + papszCategoryNames[nClassCount++] = CPLStrdup(""); + papszCategoryNames[nClassCount] = NULL; + } + } + + // Replace target category name. + CPLFree( papszCategoryNames[iClass] ); + papszCategoryNames[iClass] = NULL; + + papszCategoryNames[iClass] = CPLStrdup(osName); + } + + if( nClassCount == 0 ) + return GDALPamRasterBand::GetCategoryNames(); + else + return papszCategoryNames; + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return NULL; + } +} + +/************************************************************************/ +/* CheckForColorTable() */ +/************************************************************************/ + +bool PCIDSK2Band::CheckForColorTable() + +{ + if( bCheckedForColorTable || poFile == NULL ) + return true; + + bCheckedForColorTable = true; + + try + { +/* -------------------------------------------------------------------- */ +/* Try to find an appropriate PCT segment to use. */ +/* -------------------------------------------------------------------- */ + std::string osDefaultPCT = poChannel->GetMetadataValue("DEFAULT_PCT_REF"); + PCIDSKSegment *poPCTSeg = NULL; + + // If there is no metadata, assume a single PCT in a file with only + // one raster band must be intended for it. + if( osDefaultPCT.size() == 0 + && poDS != NULL + && poDS->GetRasterCount() == 1 ) + { + poPCTSeg = poFile->GetSegment( SEG_PCT, "" ); + if( poPCTSeg != NULL + && poFile->GetSegment( SEG_PCT, "", + poPCTSeg->GetSegmentNumber() ) != NULL ) + poPCTSeg = NULL; + } + // Parse default PCT ref assuming an in file reference. + else if( osDefaultPCT.size() != 0 + && strstr(osDefaultPCT.c_str(),"PCT:") != NULL ) + { + poPCTSeg = poFile->GetSegment( + atoi(strstr(osDefaultPCT.c_str(),"PCT:") + 4) ); + } + + if( poPCTSeg != NULL ) + { + PCIDSK_PCT *poPCT = dynamic_cast( poPCTSeg ); + poColorTable = new GDALColorTable(); + int i; + unsigned char abyPCT[768]; + + nPCTSegNumber = poPCTSeg->GetSegmentNumber(); + + poPCT->ReadPCT( abyPCT ); + + for( i = 0; i < 256; i++ ) + { + GDALColorEntry sEntry; + + sEntry.c1 = abyPCT[256 * 0 + i]; + sEntry.c2 = abyPCT[256 * 1 + i]; + sEntry.c3 = abyPCT[256 * 2 + i]; + sEntry.c4 = 255; + poColorTable->SetColorEntry( i, &sEntry ); + } + } + +/* -------------------------------------------------------------------- */ +/* If we did not find an appropriate PCT segment, check for */ +/* Class_n color data from which to construct a color table. */ +/* -------------------------------------------------------------------- */ + std::vector aosMDKeys = poChannel->GetMetadataKeys(); + size_t i; + + for( i=0; i < aosMDKeys.size(); i++ ) + { + CPLString osKey = aosMDKeys[i]; + + // is this a "Class_n_name" keyword? + + if( !EQUALN(osKey,"Class_",6) ) + continue; + + if( !EQUAL(osKey.c_str() + osKey.size() - 6, "_Color") ) + continue; + + // Ignore unreasonable class values. + int iClass = atoi(osKey.c_str() + 6); + + if( iClass < 0 || iClass > 10000 ) + continue; + + // Fetch and parse the RGB value "(RGB:red green blue)" + CPLString osRGB = poChannel->GetMetadataValue(osKey); + int nRed, nGreen, nBlue; + + if( !EQUALN(osRGB,"(RGB:",5) ) + continue; + + if( sscanf( osRGB.c_str() + 5, "%d %d %d", + &nRed, &nGreen, &nBlue ) != 3 ) + continue; + + // we have an entry - apply to the color table. + GDALColorEntry sEntry; + + sEntry.c1 = (short) nRed; + sEntry.c2 = (short) nGreen; + sEntry.c3 = (short) nBlue; + sEntry.c4 = 255; + + if( poColorTable == NULL ) + { + CPLDebug( "PCIDSK", "Using Class_n_Color metadata for color table." ); + poColorTable = new GDALColorTable(); + } + + poColorTable->SetColorEntry( iClass, &sEntry ); + } + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return false; + } + + return true; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *PCIDSK2Band::GetColorTable() + +{ + CheckForColorTable(); + + if( poColorTable ) + return poColorTable; + else + return GDALPamRasterBand::GetColorTable(); + +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr PCIDSK2Band::SetColorTable( GDALColorTable *poCT ) + +{ + if( !CheckForColorTable() ) + return CE_Failure; + + // no color tables on overviews. + if( poFile == NULL ) + return CE_Failure; + + try + { +/* -------------------------------------------------------------------- */ +/* Are we trying to delete the color table? */ +/* -------------------------------------------------------------------- */ + if( poCT == NULL ) + { + delete poColorTable; + poColorTable = NULL; + + if( nPCTSegNumber != -1 ) + poFile->DeleteSegment( nPCTSegNumber ); + poChannel->SetMetadataValue( "DEFAULT_PCT_REF", "" ); + nPCTSegNumber = -1; + + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Do we need to create the segment? If so, also set the */ +/* default pct metadata. */ +/* -------------------------------------------------------------------- */ + if( nPCTSegNumber == -1 ) + { + nPCTSegNumber = poFile->CreateSegment( "PCTTable", + "Default Pseudo-Color Table", + SEG_PCT, 0 ); + + CPLString osRef; + + osRef.Printf( "gdb:/{PCT:%d}", nPCTSegNumber ); + poChannel->SetMetadataValue( "DEFAULT_PCT_REF", osRef ); + } + +/* -------------------------------------------------------------------- */ +/* Write out the PCT. */ +/* -------------------------------------------------------------------- */ + unsigned char abyPCT[768]; + int i, nColorCount = MIN(256,poCT->GetColorEntryCount()); + + memset( abyPCT, 0, 768 ); + + for( i = 0; i < nColorCount; i++ ) + { + GDALColorEntry sEntry; + + poCT->GetColorEntryAsRGB( i, &sEntry ); + abyPCT[256 * 0 + i] = (unsigned char) sEntry.c1; + abyPCT[256 * 1 + i] = (unsigned char) sEntry.c2; + abyPCT[256 * 2 + i] = (unsigned char) sEntry.c3; + } + + PCIDSK_PCT *poPCT = dynamic_cast( + poFile->GetSegment( nPCTSegNumber ) ); + + poPCT->WritePCT( abyPCT ); + + delete poColorTable; + poColorTable = poCT->Clone(); + } + +/* -------------------------------------------------------------------- */ +/* Trap exceptions. */ +/* -------------------------------------------------------------------- */ + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp PCIDSK2Band::GetColorInterpretation() + +{ + CheckForColorTable(); + + if( poColorTable != NULL ) + return GCI_PaletteIndex; + else + return GDALPamRasterBand::GetColorInterpretation(); +} + +/************************************************************************/ +/* RefreshOverviewList() */ +/************************************************************************/ + +void PCIDSK2Band::RefreshOverviewList() + +{ +/* -------------------------------------------------------------------- */ +/* Clear existing overviews. */ +/* -------------------------------------------------------------------- */ + while( apoOverviews.size() > 0 ) + { + delete apoOverviews[apoOverviews.size()-1]; + apoOverviews.pop_back(); + } + +/* -------------------------------------------------------------------- */ +/* Fetch overviews. */ +/* -------------------------------------------------------------------- */ + for( int iOver = 0; iOver < poChannel->GetOverviewCount(); iOver++ ) + { + apoOverviews.push_back( + new PCIDSK2Band( poChannel->GetOverview(iOver) ) ); + } +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr PCIDSK2Band::IReadBlock( int iBlockX, int iBlockY, void *pData ) + +{ + try + { + poChannel->ReadBlock( iBlockX + iBlockY * nBlocksPerRow, + pData ); + + // Do we need to upsample 1bit to 8bit? + if( poChannel->GetType() == CHN_BIT ) + { + GByte *pabyData = (GByte *) pData; + + for( int ii = nBlockXSize * nBlockYSize - 1; ii >= 0; ii-- ) + { + if( (pabyData[ii>>3] & (0x80 >> (ii & 0x7))) ) + pabyData[ii] = 1; + else + pabyData[ii] = 0; + } + } + + return CE_None; + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return CE_Failure; + } +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr PCIDSK2Band::IWriteBlock( int iBlockX, int iBlockY, void *pData ) + +{ + try + { + poChannel->WriteBlock( iBlockX + iBlockY * nBlocksPerRow, + pData ); + return CE_None; + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return CE_Failure; + } +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int PCIDSK2Band::GetOverviewCount() + +{ + if( apoOverviews.size() > 0 ) + return (int) apoOverviews.size(); + else + return GDALPamRasterBand::GetOverviewCount(); +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *PCIDSK2Band::GetOverview(int iOverview) + +{ + if( iOverview < 0 || iOverview >= (int) apoOverviews.size() ) + return GDALPamRasterBand::GetOverview( iOverview ); + else + return apoOverviews[iOverview]; +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr PCIDSK2Band::SetMetadata( char **papszMD, + const char *pszDomain ) + +{ +/* -------------------------------------------------------------------- */ +/* PCIDSK only supports metadata in the default domain. */ +/* -------------------------------------------------------------------- */ + if( pszDomain != NULL && strlen(pszDomain) > 0 ) + return GDALPamRasterBand::SetMetadata( papszMD, pszDomain ); + +/* -------------------------------------------------------------------- */ +/* Set each item individually. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( papszLastMDListValue ); + papszLastMDListValue = NULL; + + try + { + int iItem; + + for( iItem = 0; papszMD && papszMD[iItem]; iItem++ ) + { + const char *pszItemValue; + char *pszItemName = NULL; + + pszItemValue = CPLParseNameValue( papszMD[iItem], &pszItemName); + if( pszItemName != NULL ) + { + poChannel->SetMetadataValue( pszItemName, pszItemValue ); + CPLFree( pszItemName ); + } + } + return CE_None; + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return CE_Failure; + } +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr PCIDSK2Band::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ +/* -------------------------------------------------------------------- */ +/* PCIDSK only supports metadata in the default domain. */ +/* -------------------------------------------------------------------- */ + if( pszDomain != NULL && strlen(pszDomain) > 0 ) + return GDALPamRasterBand::SetMetadataItem(pszName,pszValue,pszDomain); + +/* -------------------------------------------------------------------- */ +/* Set on the file. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( papszLastMDListValue ); + papszLastMDListValue = NULL; + + try + { + if( !pszValue ) + pszValue = ""; + poChannel->SetMetadataValue( pszName, pszValue ); + return CE_None; + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return CE_Failure; + } +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **PCIDSK2Band::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamRasterBand::GetMetadataDomainList(), + TRUE, + "", NULL); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *PCIDSK2Band::GetMetadataItem( const char *pszName, + const char *pszDomain ) + +{ +/* -------------------------------------------------------------------- */ +/* PCIDSK only supports metadata in the default domain. */ +/* -------------------------------------------------------------------- */ + if( pszDomain != NULL && strlen(pszDomain) > 0 ) + return GDALPamRasterBand::GetMetadataItem( pszName, pszDomain ); + +/* -------------------------------------------------------------------- */ +/* Try and fetch. */ +/* -------------------------------------------------------------------- */ + try + { + osLastMDValue = poChannel->GetMetadataValue( pszName ); + + if( osLastMDValue == "" ) + return NULL; + else + return osLastMDValue.c_str(); + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return NULL; + } +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **PCIDSK2Band::GetMetadata( const char *pszDomain ) + +{ +/* -------------------------------------------------------------------- */ +/* PCIDSK only supports metadata in the default domain. */ +/* -------------------------------------------------------------------- */ + if( pszDomain != NULL && strlen(pszDomain) > 0 ) + return GDALPamRasterBand::GetMetadata( pszDomain ); + +/* -------------------------------------------------------------------- */ +/* If we have a cached result, just use that. */ +/* -------------------------------------------------------------------- */ + if( papszLastMDListValue != NULL ) + return papszLastMDListValue; + +/* -------------------------------------------------------------------- */ +/* Fetch and build the list. */ +/* -------------------------------------------------------------------- */ + try + { + std::vector aosKeys = poChannel->GetMetadataKeys(); + unsigned int i; + + for( i = 0; i < aosKeys.size(); i++ ) + { + if( aosKeys[i].c_str()[0] == '_' ) + continue; + + papszLastMDListValue = + CSLSetNameValue( papszLastMDListValue, + aosKeys[i].c_str(), + poChannel->GetMetadataValue(aosKeys[i]).c_str() ); + } + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return NULL; + } + + return papszLastMDListValue; +} + +/************************************************************************/ +/* ==================================================================== */ +/* PCIDSK2Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* PCIDSK2Dataset() */ +/************************************************************************/ + +PCIDSK2Dataset::PCIDSK2Dataset() +{ + poFile = NULL; + papszLastMDListValue = NULL; +} + +/************************************************************************/ +/* ~PCIDSK2Dataset() */ +/************************************************************************/ + +PCIDSK2Dataset::~PCIDSK2Dataset() +{ + FlushCache(); + + while( apoLayers.size() > 0 ) + { + delete apoLayers.back(); + apoLayers.pop_back(); + } + + try { + delete poFile; + poFile = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Trap exceptions. */ +/* -------------------------------------------------------------------- */ + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + } + catch( ... ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "PCIDSK SDK Failure in Close(), unexpected exception." ); + } + + CSLDestroy( papszLastMDListValue ); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **PCIDSK2Dataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + CPLString osBaseDir = CPLGetPath( GetDescription() ); + + try + { + for( int nChan = 1; nChan <= poFile->GetChannels(); nChan++ ) + { + PCIDSKChannel *poChannel = poFile->GetChannel( nChan ); + CPLString osChanFilename; + uint64 image_offset, pixel_offset, line_offset; + bool little_endian; + + poChannel->GetChanInfo( osChanFilename, image_offset, + pixel_offset, line_offset, little_endian ); + + if( osChanFilename != "" ) + { + papszFileList = + CSLAddString( papszFileList, + CPLProjectRelativeFilename( osBaseDir, + osChanFilename ) ); + } + } + + return papszFileList; + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return papszFileList; + } +} + +/************************************************************************/ +/* ProcessRPC() */ +/************************************************************************/ + +void PCIDSK2Dataset::ProcessRPC() + +{ +/* -------------------------------------------------------------------- */ +/* Search all BIN segments looking for an RPC segment. */ +/* -------------------------------------------------------------------- */ + PCIDSKSegment *poSeg = poFile->GetSegment( SEG_BIN, "" ); + PCIDSKRPCSegment *poRPCSeg = NULL; + + while( poSeg != NULL + && (poRPCSeg = dynamic_cast( poSeg )) == NULL ) + + { + poSeg = poFile->GetSegment( SEG_BIN, "", + poSeg->GetSegmentNumber() ); + } + + if( poRPCSeg == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Turn RPC segment into GDAL RFC 22 style metadata. */ +/* -------------------------------------------------------------------- */ + try + { + CPLString osValue; + double dfLineOffset, dfLineScale, dfSampOffset, dfSampScale; + double dfLatOffset, dfLatScale, + dfLongOffset, dfLongScale, + dfHeightOffset, dfHeightScale; + + poRPCSeg->GetRPCTranslationCoeffs( + dfLongOffset, dfLongScale, + dfLatOffset, dfLatScale, + dfHeightOffset, dfHeightScale, + dfSampOffset, dfSampScale, + dfLineOffset, dfLineScale ); + + osValue.Printf( "%.16g", dfLineOffset ); + GDALPamDataset::SetMetadataItem( "LINE_OFF", osValue, "RPC" ); + + osValue.Printf( "%.16g", dfLineScale ); + GDALPamDataset::SetMetadataItem( "LINE_SCALE", osValue, "RPC" ); + + osValue.Printf( "%.16g", dfSampOffset ); + GDALPamDataset::SetMetadataItem( "SAMP_OFF", osValue, "RPC" ); + + osValue.Printf( "%.16g", dfSampScale ); + GDALPamDataset::SetMetadataItem( "SAMP_SCALE", osValue, "RPC" ); + + osValue.Printf( "%.16g", dfLongOffset ); + GDALPamDataset::SetMetadataItem( "LONG_OFF", osValue, "RPC" ); + + osValue.Printf( "%.16g", dfLongScale ); + GDALPamDataset::SetMetadataItem( "LONG_SCALE", osValue, "RPC" ); + + osValue.Printf( "%.16g", dfLatOffset ); + GDALPamDataset::SetMetadataItem( "LAT_OFF", osValue, "RPC" ); + + osValue.Printf( "%.16g", dfLatScale ); + GDALPamDataset::SetMetadataItem( "LAT_SCALE", osValue, "RPC" ); + + osValue.Printf( "%.16g", dfHeightOffset ); + GDALPamDataset::SetMetadataItem( "HEIGHT_OFF", osValue, "RPC" ); + + osValue.Printf( "%.16g", dfHeightScale ); + GDALPamDataset::SetMetadataItem( "HEIGHT_SCALE", osValue, "RPC" ); + + CPLString osCoefList; + std::vector adfCoef; + int i; + + if( poRPCSeg->GetXNumerator().size() != 20 + || poRPCSeg->GetXDenominator().size() != 20 + || poRPCSeg->GetYNumerator().size() != 20 + || poRPCSeg->GetYDenominator().size() != 20 ) + { + GDALPamDataset::SetMetadata( NULL, "RPC" ); + CPLError( CE_Failure, CPLE_AppDefined, + "Did not get 20 values in the RPC coefficients lists." ); + return; + } + + adfCoef = poRPCSeg->GetYNumerator(); + osCoefList = ""; + for( i = 0; i < 20; i++ ) + { + osValue.Printf( "%.16g ", adfCoef[i] ); + osCoefList += osValue; + } + GDALPamDataset::SetMetadataItem( "LINE_NUM_COEFF", osCoefList, "RPC" ); + + adfCoef = poRPCSeg->GetYDenominator(); + osCoefList = ""; + for( i = 0; i < 20; i++ ) + { + osValue.Printf( "%.16g ", adfCoef[i] ); + osCoefList += osValue; + } + GDALPamDataset::SetMetadataItem( "LINE_DEN_COEFF", osCoefList, "RPC" ); + + adfCoef = poRPCSeg->GetXNumerator(); + osCoefList = ""; + for( i = 0; i < 20; i++ ) + { + osValue.Printf( "%.16g ", adfCoef[i] ); + osCoefList += osValue; + } + GDALPamDataset::SetMetadataItem( "SAMP_NUM_COEFF", osCoefList, "RPC" ); + + adfCoef = poRPCSeg->GetXDenominator(); + osCoefList = ""; + for( i = 0; i < 20; i++ ) + { + osValue.Printf( "%.16g ", adfCoef[i] ); + osCoefList += osValue; + } + GDALPamDataset::SetMetadataItem( "SAMP_DEN_COEFF", osCoefList, "RPC" ); + } + catch( PCIDSKException ex ) + { + GDALPamDataset::SetMetadata( NULL, "RPC" ); + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + } +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void PCIDSK2Dataset::FlushCache() + +{ + GDALPamDataset::FlushCache(); + + if( poFile ) + { + try { + poFile->Synchronize(); + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + } + } +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr PCIDSK2Dataset::SetMetadata( char **papszMD, + const char *pszDomain ) + +{ +/* -------------------------------------------------------------------- */ +/* PCIDSK only supports metadata in the default domain. */ +/* -------------------------------------------------------------------- */ + if( pszDomain != NULL && strlen(pszDomain) > 0 ) + return GDALPamDataset::SetMetadata( papszMD, pszDomain ); + +/* -------------------------------------------------------------------- */ +/* Set each item individually. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( papszLastMDListValue ); + papszLastMDListValue = NULL; + + try + { + int iItem; + + for( iItem = 0; papszMD && papszMD[iItem]; iItem++ ) + { + const char *pszItemValue; + char *pszItemName = NULL; + + pszItemValue = CPLParseNameValue( papszMD[iItem], &pszItemName); + if( pszItemName != NULL ) + { + poFile->SetMetadataValue( pszItemName, pszItemValue ); + CPLFree( pszItemName ); + } + } + return CE_None; + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return CE_Failure; + } +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr PCIDSK2Dataset::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ +/* -------------------------------------------------------------------- */ +/* PCIDSK only supports metadata in the default domain. */ +/* -------------------------------------------------------------------- */ + if( pszDomain != NULL && strlen(pszDomain) > 0 ) + return GDALPamDataset::SetMetadataItem( pszName, pszValue, pszDomain ); + +/* -------------------------------------------------------------------- */ +/* Set on the file. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( papszLastMDListValue ); + papszLastMDListValue = NULL; + + try + { + poFile->SetMetadataValue( pszName, pszValue ); + return CE_None; + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return CE_Failure; + } +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **PCIDSK2Dataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "", NULL); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *PCIDSK2Dataset::GetMetadataItem( const char *pszName, + const char *pszDomain ) + +{ +/* -------------------------------------------------------------------- */ +/* PCIDSK only supports metadata in the default domain. */ +/* -------------------------------------------------------------------- */ + if( pszDomain != NULL && strlen(pszDomain) > 0 ) + return GDALPamDataset::GetMetadataItem( pszName, pszDomain ); + +/* -------------------------------------------------------------------- */ +/* Try and fetch. */ +/* -------------------------------------------------------------------- */ + try + { + osLastMDValue = poFile->GetMetadataValue( pszName ); + + if( osLastMDValue == "" ) + return NULL; + else + return osLastMDValue.c_str(); + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return NULL; + } +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **PCIDSK2Dataset::GetMetadata( const char *pszDomain ) + +{ +/* -------------------------------------------------------------------- */ +/* PCIDSK only supports metadata in the default domain. */ +/* -------------------------------------------------------------------- */ + if( pszDomain != NULL && strlen(pszDomain) > 0 ) + return GDALPamDataset::GetMetadata( pszDomain ); + +/* -------------------------------------------------------------------- */ +/* If we have a cached result, just use that. */ +/* -------------------------------------------------------------------- */ + if( papszLastMDListValue != NULL ) + return papszLastMDListValue; + +/* -------------------------------------------------------------------- */ +/* Fetch and build the list. */ +/* -------------------------------------------------------------------- */ + try + { + std::vector aosKeys = poFile->GetMetadataKeys(); + unsigned int i; + + for( i = 0; i < aosKeys.size(); i++ ) + { + if( aosKeys[i].c_str()[0] == '_' ) + continue; + + papszLastMDListValue = + CSLSetNameValue( papszLastMDListValue, + aosKeys[i].c_str(), + poFile->GetMetadataValue(aosKeys[i]).c_str() ); + } + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return NULL; + } + + return papszLastMDListValue; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr PCIDSK2Dataset::SetGeoTransform( double * padfTransform ) +{ + PCIDSKGeoref *poGeoref = NULL; + try + { + PCIDSKSegment *poGeoSeg = poFile->GetSegment(1); + poGeoref = dynamic_cast( poGeoSeg ); + } + catch( PCIDSKException ex ) + { + // I should really check whether this is an expected issue. + } + + if( poGeoref == NULL ) + return GDALPamDataset::SetGeoTransform( padfTransform ); + else + { + try + { + poGeoref->WriteSimple( poGeoref->GetGeosys(), + padfTransform[0], + padfTransform[1], + padfTransform[2], + padfTransform[3], + padfTransform[4], + padfTransform[5] ); + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return CE_Failure; + } + + return CE_None; + } +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr PCIDSK2Dataset::GetGeoTransform( double * padfTransform ) +{ + PCIDSKGeoref *poGeoref = NULL; + try + { + PCIDSKSegment *poGeoSeg = poFile->GetSegment(1); + poGeoref = dynamic_cast( poGeoSeg ); + } + catch( PCIDSKException ex ) + { + // I should really check whether this is an expected issue. + } + + if( poGeoref != NULL ) + { + try + { + poGeoref->GetTransform( padfTransform[0], + padfTransform[1], + padfTransform[2], + padfTransform[3], + padfTransform[4], + padfTransform[5] ); + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return CE_Failure; + } + + // If we got anything non-default return it. + if( padfTransform[0] != 0.0 + || padfTransform[1] != 1.0 + || padfTransform[2] != 0.0 + || padfTransform[3] != 0.0 + || padfTransform[4] != 0.0 + || padfTransform[5] != 1.0 ) + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Check for worldfile if we have no other georeferencing. */ +/* -------------------------------------------------------------------- */ + if( GDALReadWorldFile( GetDescription(), "pxw", + padfTransform ) ) + return CE_None; + else + return GDALPamDataset::GetGeoTransform( padfTransform ); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr PCIDSK2Dataset::SetProjection( const char *pszWKT ) + +{ + osSRS = ""; + + PCIDSKGeoref *poGeoref = NULL; + + try + { + PCIDSKSegment *poGeoSeg = poFile->GetSegment(1); + poGeoref = dynamic_cast( poGeoSeg ); + } + catch( PCIDSKException ex ) + { + // I should really check whether this is an expected issue. + } + + if( poGeoref == NULL ) + { + return GDALPamDataset::SetProjection( pszWKT ); + } + else + { + char *pszGeosys = NULL; + char *pszUnits = NULL; + double *padfPrjParams = NULL; + + OGRSpatialReference oSRS; + char *pszWKTWork = (char *) pszWKT; + + if( oSRS.importFromWkt( &pszWKTWork ) == OGRERR_NONE + && oSRS.exportToPCI( &pszGeosys, &pszUnits, + &padfPrjParams ) == OGRERR_NONE ) + { + try + { + double adfGT[6]; + std::vector adfPCIParameters; + unsigned int i; + + poGeoref->GetTransform( adfGT[0], adfGT[1], adfGT[2], + adfGT[3], adfGT[4], adfGT[5] ); + + poGeoref->WriteSimple( pszGeosys, + adfGT[0], adfGT[1], adfGT[2], + adfGT[3], adfGT[4], adfGT[5] ); + + for( i = 0; i < 17; i++ ) + adfPCIParameters.push_back( padfPrjParams[i] ); + + if( EQUALN(pszUnits,"FOOT",4) ) + adfPCIParameters.push_back( + (double)(int) PCIDSK::UNIT_US_FOOT ); + else if( EQUALN(pszUnits,"INTL FOOT",9) ) + adfPCIParameters.push_back( + (double)(int) PCIDSK::UNIT_INTL_FOOT ); + else if( EQUALN(pszUnits,"DEGREE",6) ) + adfPCIParameters.push_back( + (double)(int) PCIDSK::UNIT_DEGREE ); + else + adfPCIParameters.push_back( + (double)(int) PCIDSK::UNIT_METER ); + + poGeoref->WriteParameters( adfPCIParameters ); + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return CE_Failure; + } + + CPLFree( pszGeosys ); + CPLFree( pszUnits ); + CPLFree( padfPrjParams ); + + return CE_None; + } + else + return GDALPamDataset::SetProjection( pszWKT ); + } +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *PCIDSK2Dataset::GetProjectionRef() +{ + if( osSRS != "" ) + return osSRS.c_str(); + + PCIDSKGeoref *poGeoref = NULL; + + try + { + PCIDSKSegment *poGeoSeg = poFile->GetSegment(1); + poGeoref = dynamic_cast( poGeoSeg ); + } + catch( PCIDSKException ex ) + { + // I should really check whether this is an expected issue. + } + + if( poGeoref == NULL ) + { + osSRS = GDALPamDataset::GetProjectionRef(); + } + else + { + CPLString osGeosys; + const char *pszUnits = NULL; + OGRSpatialReference oSRS; + char *pszWKT = NULL; + std::vector adfParameters; + + adfParameters.resize(18); + try + { + if( poGeoref ) + { + osGeosys = poGeoref->GetGeosys(); + adfParameters = poGeoref->GetParameters(); + if( ((UnitCode)(int)adfParameters[16]) + == PCIDSK::UNIT_DEGREE ) + pszUnits = "DEGREE"; + else if( ((UnitCode)(int)adfParameters[16]) + == PCIDSK::UNIT_METER ) + pszUnits = "METER"; + else if( ((UnitCode)(int)adfParameters[16]) + == PCIDSK::UNIT_US_FOOT ) + pszUnits = "FOOT"; + else if( ((UnitCode)(int)adfParameters[16]) + == PCIDSK::UNIT_INTL_FOOT ) + pszUnits = "INTL FOOT"; + } + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + } + + if( oSRS.importFromPCI( osGeosys, pszUnits, + &(adfParameters[0]) ) == OGRERR_NONE ) + { + oSRS.exportToWkt( &pszWKT ); + osSRS = pszWKT; + CPLFree( pszWKT ); + } + else + { + osSRS = GDALPamDataset::GetProjectionRef(); + } + } + + return osSRS.c_str(); +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr PCIDSK2Dataset::IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + if( nListBands == 0 ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Currently no support for clearing overviews. */ +/* -------------------------------------------------------------------- */ + if( nOverviews == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "PCIDSK2 driver does not currently support clearing existing overviews. " ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Establish which of the overview levels we already have, and */ +/* which are new. We assume that band 1 of the file is */ +/* representative. */ +/* -------------------------------------------------------------------- */ + int i, nNewOverviews, *panNewOverviewList = NULL; + PCIDSK2Band *poBand = (PCIDSK2Band*) GetRasterBand( panBandList[0] ); + + nNewOverviews = 0; + panNewOverviewList = (int *) CPLCalloc(sizeof(int),nOverviews); + for( i = 0; i < nOverviews && poBand != NULL; i++ ) + { + int j; + + for( j = 0; j < poBand->GetOverviewCount(); j++ ) + { + int nOvFactor; + GDALRasterBand * poOverview = poBand->GetOverview( j ); + + nOvFactor = GDALComputeOvFactor(poOverview->GetXSize(), + poBand->GetXSize(), + poOverview->GetYSize(), + poBand->GetYSize()); + + if( nOvFactor == panOverviewList[i] + || nOvFactor == GDALOvLevelAdjust2( panOverviewList[i], + poBand->GetXSize(), + poBand->GetYSize() ) ) + panOverviewList[i] *= -1; + } + + if( panOverviewList[i] > 0 ) + panNewOverviewList[nNewOverviews++] = panOverviewList[i]; + else + panOverviewList[i] *= -1; + } + +/* -------------------------------------------------------------------- */ +/* Create the overviews that are missing. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nNewOverviews; i++ ) + { + try + { + // conveniently our resampling values mostly match PCIDSK. + poFile->CreateOverviews( nListBands, panBandList, + panNewOverviewList[i], pszResampling ); + } + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + CPLFree( panNewOverviewList ); + return CE_Failure; + } + } + + CPLFree( panNewOverviewList ); + panNewOverviewList = NULL; + + int iBand; + for( iBand = 0; iBand < nListBands; iBand++ ) + { + poBand = (PCIDSK2Band *) GetRasterBand( panBandList[iBand] ); + ((PCIDSK2Band *) poBand)->RefreshOverviewList(); + } + +/* -------------------------------------------------------------------- */ +/* Actually generate the overview imagery. */ +/* -------------------------------------------------------------------- */ + GDALRasterBand **papoOverviewBands; + CPLErr eErr = CE_None; + std::vector anRegenLevels; + + papoOverviewBands = (GDALRasterBand **) + CPLCalloc(sizeof(void*),nOverviews); + + for( iBand = 0; iBand < nListBands && eErr == CE_None; iBand++ ) + { + nNewOverviews = 0; + + poBand = (PCIDSK2Band*) GetRasterBand( panBandList[iBand] ); + + for( i = 0; i < nOverviews && poBand != NULL; i++ ) + { + int j; + + for( j = 0; j < poBand->GetOverviewCount(); j++ ) + { + int nOvFactor; + GDALRasterBand * poOverview = poBand->GetOverview( j ); + + nOvFactor = GDALComputeOvFactor(poOverview->GetXSize(), + poBand->GetXSize(), + poOverview->GetYSize(), + poBand->GetYSize()); + + if( nOvFactor == panOverviewList[i] + || nOvFactor == GDALOvLevelAdjust2( panOverviewList[i], + poBand->GetXSize(), + poBand->GetYSize() ) ) + { + papoOverviewBands[nNewOverviews++] = poOverview; + anRegenLevels.push_back( j ); + break; + } + } + } + + if( nNewOverviews > 0 ) + { + eErr = GDALRegenerateOverviews( (GDALRasterBandH) poBand, + nNewOverviews, + (GDALRasterBandH*)papoOverviewBands, + pszResampling, + pfnProgress, pProgressData ); + + // Mark the regenerated overviews as valid. + for( i = 0; i < (int) anRegenLevels.size(); i++ ) + poBand->poChannel->SetOverviewValidity( anRegenLevels[i], + true ); + } + } + + CPLFree(papoOverviewBands); + + return eErr; +} + +/************************************************************************/ +/* PCIDSKTypeToGDAL() */ +/************************************************************************/ + +GDALDataType PCIDSK2Dataset::PCIDSKTypeToGDAL( eChanType eType ) +{ + switch( eType ) + { + case CHN_8U: + return GDT_Byte; + + case CHN_16U: + return GDT_UInt16; + + case CHN_16S: + return GDT_Int16; + + case CHN_32R: + return GDT_Float32; + + case CHN_BIT: + return GDT_Byte; + + case CHN_C16U: + return GDT_CInt16; + + case CHN_C16S: + return GDT_CInt16; + + case CHN_C32R: + return GDT_CFloat32; + + default: + return GDT_Unknown; + } +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int PCIDSK2Dataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + if( poOpenInfo->nHeaderBytes < 512 + || !EQUALN((const char *) poOpenInfo->pabyHeader, "PCIDSK ", 8) ) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *PCIDSK2Dataset::Open( GDALOpenInfo * poOpenInfo ) +{ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Try opening the file. */ +/* -------------------------------------------------------------------- */ + try { + PCIDSKFile *poFile = + PCIDSK::Open( poOpenInfo->pszFilename, + poOpenInfo->eAccess == GA_ReadOnly ? "r" : "r+", + PCIDSK2GetInterfaces() ); + if( poFile == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to re-open %s within PCIDSK driver.\n", + poOpenInfo->pszFilename ); + return NULL; + } + + /* Check if this is a vector-only PCIDSK file and that we are */ + /* opened in raster-only mode */ + if( poOpenInfo->eAccess == GA_ReadOnly && + (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 && + (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) == 0 && + poFile->GetChannels() == 0 && + poFile->GetSegment( PCIDSK::SEG_VEC, "" ) != NULL ) + { + CPLDebug("PCIDSK", "This is a vector-only PCIDSK dataset, " + "but it has been opened in read-only in raster-only mode"); + delete poFile; + return NULL; + } + /* Reverse test */ + if( poOpenInfo->eAccess == GA_ReadOnly && + (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 && + (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 && + poFile->GetChannels() != 0 && + poFile->GetSegment( PCIDSK::SEG_VEC, "" ) == NULL ) + { + CPLDebug("PCIDSK", "This is a raster-only PCIDSK dataset, " + "but it has been opened in read-only in vector-only mode"); + delete poFile; + return NULL; + } + + return LLOpen( poOpenInfo->pszFilename, poFile, poOpenInfo->eAccess, + poOpenInfo->GetSiblingFiles() ); + } +/* -------------------------------------------------------------------- */ +/* Trap exceptions. */ +/* -------------------------------------------------------------------- */ + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return NULL; + } + catch( ... ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "PCIDSK::Create() failed, unexpected exception." ); + return NULL; + } +} + +/************************************************************************/ +/* LLOpen() */ +/* */ +/* Low level variant of open that takes the preexisting */ +/* PCIDSKFile. */ +/************************************************************************/ + +GDALDataset *PCIDSK2Dataset::LLOpen( const char *pszFilename, + PCIDSK::PCIDSKFile *poFile, + GDALAccess eAccess, + char** papszSiblingFiles ) + +{ + PCIDSK2Dataset *poDS = NULL; + try { +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + + poDS = new PCIDSK2Dataset(); + + poDS->poFile = poFile; + poDS->eAccess = eAccess; + poDS->nRasterXSize = poFile->GetWidth(); + poDS->nRasterYSize = poFile->GetHeight(); + +/* -------------------------------------------------------------------- */ +/* Are we specifically PIXEL or BAND interleaving? */ +/* */ +/* We don't set anything for FILE since it is harder to know if */ +/* this is tiled or what the on disk interleaving is. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(poFile->GetInterleaving().c_str(),"PIXEL") ) + poDS->SetMetadataItem( "IMAGE_STRUCTURE", "PIXEL", + "IMAGE_STRUCTURE" ); + else if( EQUAL(poFile->GetInterleaving().c_str(),"BAND") ) + poDS->SetMetadataItem( "IMAGE_STRUCTURE", "BAND", + "IMAGE_STRUCTURE" ); + +/* -------------------------------------------------------------------- */ +/* Create band objects. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 0; iBand < poFile->GetChannels(); iBand++ ) + { + PCIDSKChannel* poChannel = poFile->GetChannel( iBand + 1 ); + if (poChannel->GetBlockWidth() <= 0 || + poChannel->GetBlockHeight() <= 0) + { + delete poDS; + return NULL; + } + + poDS->SetBand( iBand+1, new PCIDSK2Band( poDS, poFile, iBand+1 )); + } + +/* -------------------------------------------------------------------- */ +/* Create band objects for bitmap segments. */ +/* -------------------------------------------------------------------- */ + int nLastBitmapSegment = 0; + PCIDSKSegment *poBitSeg; + + while( (poBitSeg = poFile->GetSegment( SEG_BIT, "", + nLastBitmapSegment)) != NULL ) + { + PCIDSKChannel *poChannel = + dynamic_cast( poBitSeg ); + if (poChannel->GetBlockWidth() <= 0 || + poChannel->GetBlockHeight() <= 0) + { + delete poDS; + return NULL; + } + + poDS->SetBand( poDS->GetRasterCount()+1, + new PCIDSK2Band( poChannel ) ); + + nLastBitmapSegment = poBitSeg->GetSegmentNumber(); + } + +/* -------------------------------------------------------------------- */ +/* Create vector layers from vector segments. */ +/* -------------------------------------------------------------------- */ + PCIDSK::PCIDSKSegment *segobj; + for( segobj = poFile->GetSegment( PCIDSK::SEG_VEC, "" ); + segobj != NULL; + segobj = poFile->GetSegment( PCIDSK::SEG_VEC, "", + segobj->GetSegmentNumber() ) ) + { + poDS->apoLayers.push_back( new OGRPCIDSKLayer( segobj, eAccess == GA_Update ) ); + } + +/* -------------------------------------------------------------------- */ +/* Process RPC segment, if there is one. */ +/* -------------------------------------------------------------------- */ + poDS->ProcessRPC(); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( pszFilename ); + poDS->TryLoadXML( papszSiblingFiles ); + +/* -------------------------------------------------------------------- */ +/* Open overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, pszFilename, papszSiblingFiles ); + + return( poDS ); + } + +/* -------------------------------------------------------------------- */ +/* Trap exceptions. */ +/* -------------------------------------------------------------------- */ + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + } + catch( ... ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "PCIDSK SDK Failure in Open(), unexpected exception." ); + } + +/* -------------------------------------------------------------------- */ +/* In case of exception, close dataset */ +/* -------------------------------------------------------------------- */ + delete poDS; + + return NULL; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *PCIDSK2Dataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char **papszParmList ) + +{ + PCIDSKFile *poFile; + +/* -------------------------------------------------------------------- */ +/* Prepare channel type list. */ +/* -------------------------------------------------------------------- */ + std::vector aeChanTypes; + + if( eType == GDT_Float32 ) + aeChanTypes.resize( MAX(1,nBands), CHN_32R ); + else if( eType == GDT_Int16 ) + aeChanTypes.resize( MAX(1,nBands), CHN_16S ); + else if( eType == GDT_UInt16 ) + aeChanTypes.resize( MAX(1,nBands), CHN_16U ); + else if( eType == GDT_CInt16 ) + aeChanTypes.resize( MAX(1, nBands), CHN_C16S ); + else if( eType == GDT_CFloat32 ) + aeChanTypes.resize( MAX(1, nBands), CHN_C32R ); + else + aeChanTypes.resize( MAX(1,nBands), CHN_8U ); + +/* -------------------------------------------------------------------- */ +/* Reformat options. Currently no support for jpeg compression */ +/* quality. */ +/* -------------------------------------------------------------------- */ + CPLString osOptions; + const char *pszValue; + + pszValue = CSLFetchNameValue( papszParmList, "INTERLEAVING" ); + if( pszValue == NULL ) + pszValue = "BAND"; + + osOptions = pszValue; + + if( osOptions == "TILED" ) + { + pszValue = CSLFetchNameValue( papszParmList, "TILESIZE" ); + if( pszValue != NULL ) + osOptions += pszValue; + + pszValue = CSLFetchNameValue( papszParmList, "COMPRESSION" ); + if( pszValue != NULL ) + { + osOptions += " "; + osOptions += pszValue; + } + } + +/* -------------------------------------------------------------------- */ +/* Try creation. */ +/* -------------------------------------------------------------------- */ + try { + if( nBands == 0 ) + nXSize = nYSize = 512; + poFile = PCIDSK::Create( pszFilename, nXSize, nYSize, nBands, + &(aeChanTypes[0]), osOptions, + PCIDSK2GetInterfaces() ); + +/* -------------------------------------------------------------------- */ +/* Apply band descriptions, if provided as creation options. */ +/* -------------------------------------------------------------------- */ + size_t i; + + for( i = 0; papszParmList != NULL && papszParmList[i] != NULL; i++ ) + { + if( EQUALN(papszParmList[i],"BANDDESC",8) ) + { + int nBand = atoi(papszParmList[i] + 8 ); + const char *pszDescription = strstr(papszParmList[i],"="); + if( pszDescription && nBand > 0 && nBand <= nBands ) + { + poFile->GetChannel(nBand)->SetDescription( pszDescription+1 ); + } + } + } + + return LLOpen( pszFilename, poFile, GA_Update ); + } +/* -------------------------------------------------------------------- */ +/* Trap exceptions. */ +/* -------------------------------------------------------------------- */ + catch( PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + return NULL; + } + catch( ... ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "PCIDSK::Create() failed, unexpected exception." ); + return NULL; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int PCIDSK2Dataset::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return eAccess == GA_Update; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *PCIDSK2Dataset::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= (int) apoLayers.size() ) + return NULL; + else + return apoLayers[iLayer]; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +PCIDSK2Dataset::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + CPL_UNUSED char ** papszOptions ) +{ +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( eAccess != GA_Update ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened read-only.\n" + "New layer %s cannot be created.\n", + GetDescription(), pszLayerName ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Figure out what type of layer we need. */ +/* -------------------------------------------------------------------- */ + std::string osLayerType; + + switch( wkbFlatten(eType) ) + { + case wkbPoint: + osLayerType = "POINTS"; + break; + + case wkbLineString: + osLayerType = "ARCS"; + break; + + case wkbPolygon: + osLayerType = "WHOLE_POLYGONS"; + break; + + case wkbNone: + osLayerType = "TABLE"; + break; + + default: + break; + } + +/* -------------------------------------------------------------------- */ +/* Create the segment. */ +/* -------------------------------------------------------------------- */ + int nSegNum = poFile->CreateSegment( pszLayerName, "", + PCIDSK::SEG_VEC, 0L ); + PCIDSK::PCIDSKSegment *poSeg = poFile->GetSegment( nSegNum ); + PCIDSK::PCIDSKVectorSegment *poVecSeg = + dynamic_cast( poSeg ); + + if( osLayerType != "" ) + poSeg->SetMetadataValue( "LAYER_TYPE", osLayerType ); + +/* -------------------------------------------------------------------- */ +/* Do we need to apply a coordinate system? */ +/* -------------------------------------------------------------------- */ + char *pszGeosys = NULL; + char *pszUnits = NULL; + double *padfPrjParams = NULL; + + if( poSRS != NULL + && poSRS->exportToPCI( &pszGeosys, &pszUnits, + &padfPrjParams ) == OGRERR_NONE ) + { + try + { + std::vector adfPCIParameters; + int i; + + for( i = 0; i < 17; i++ ) + adfPCIParameters.push_back( padfPrjParams[i] ); + + if( EQUALN(pszUnits,"FOOT",4) ) + adfPCIParameters.push_back( + (double)(int) PCIDSK::UNIT_US_FOOT ); + else if( EQUALN(pszUnits,"INTL FOOT",9) ) + adfPCIParameters.push_back( + (double)(int) PCIDSK::UNIT_INTL_FOOT ); + else if( EQUALN(pszUnits,"DEGREE",6) ) + adfPCIParameters.push_back( + (double)(int) PCIDSK::UNIT_DEGREE ); + else + adfPCIParameters.push_back( + (double)(int) PCIDSK::UNIT_METER ); + + poVecSeg->SetProjection( pszGeosys, adfPCIParameters ); + } + catch( PCIDSK::PCIDSKException ex ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", ex.what() ); + } + + CPLFree( pszGeosys ); + CPLFree( pszUnits ); + CPLFree( padfPrjParams ); + } + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + apoLayers.push_back( new OGRPCIDSKLayer( poSeg, TRUE ) ); + + return apoLayers[apoLayers.size()-1]; +} + +/************************************************************************/ +/* GDALRegister_PCIDSK() */ +/************************************************************************/ + +void GDALRegister_PCIDSK() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "PCIDSK" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "PCIDSK" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "PCIDSK Database File" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_pcidsk.html" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "pix" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte UInt16 Int16 Float32 CInt16 CFloat32" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" " +" " ); + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, "" ); + + poDriver->pfnIdentify = PCIDSK2Dataset::Identify; + poDriver->pfnOpen = PCIDSK2Dataset::Open; + poDriver->pfnCreate = PCIDSK2Dataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/pcidskdataset2.h b/bazaar/plugin/gdal/frmts/pcidsk/pcidskdataset2.h new file mode 100644 index 000000000..7d599270e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/pcidskdataset2.h @@ -0,0 +1,202 @@ +/****************************************************************************** + * $Id$ + * + * Project: PCIDSK Database File + * Purpose: Read/write PCIDSK Database File used by the PCI software, using + * the external PCIDSK library. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef PCIDSKDATASET2_H_INCLUDED +#define PCIDSKDATASET2_H_INCLUDED + +#define GDAL_PCIDSK_DRIVER + +#include "pcidsk.h" +#include "pcidsk_pct.h" +#include "ogrsf_frmts.h" +#include "pcidsk_vectorsegment.h" +#include "gdal_pam.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +using namespace PCIDSK; + +class OGRPCIDSKLayer; + +/************************************************************************/ +/* PCIDSK2Dataset */ +/************************************************************************/ + +class PCIDSK2Dataset : public GDALPamDataset +{ + friend class PCIDSK2Band; + + CPLString osSRS; + CPLString osLastMDValue; + char **papszLastMDListValue; + + PCIDSKFile *poFile; + + std::vector apoLayers; + + static GDALDataType PCIDSKTypeToGDAL( eChanType eType ); + void ProcessRPC(); + + public: + PCIDSK2Dataset(); + ~PCIDSK2Dataset(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *LLOpen( const char *pszFilename, PCIDSK::PCIDSKFile *, + GDALAccess eAccess, + char** papszSiblingFiles = NULL ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char **papszParmList ); + + char **GetFileList(void); + CPLErr GetGeoTransform( double * padfTransform ); + CPLErr SetGeoTransform( double * ); + const char *GetProjectionRef(); + CPLErr SetProjection( const char * ); + + virtual char **GetMetadataDomainList(); + CPLErr SetMetadata( char **, const char * ); + char **GetMetadata( const char* ); + CPLErr SetMetadataItem(const char*,const char*,const char*); + const char *GetMetadataItem( const char*, const char*); + + virtual void FlushCache(void); + + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + + virtual int GetLayerCount() { return (int) apoLayers.size(); } + virtual OGRLayer *GetLayer( int ); + + virtual int TestCapability( const char * ); + + virtual OGRLayer *ICreateLayer( const char *, OGRSpatialReference *, + OGRwkbGeometryType, char ** ); +}; + +/************************************************************************/ +/* PCIDSK2Band */ +/************************************************************************/ + +class PCIDSK2Band : public GDALPamRasterBand +{ + friend class PCIDSK2Dataset; + + PCIDSKChannel *poChannel; + PCIDSKFile *poFile; + + void RefreshOverviewList(); + std::vector apoOverviews; + + CPLString osLastMDValue; + char **papszLastMDListValue; + + bool CheckForColorTable(); + GDALColorTable *poColorTable; + bool bCheckedForColorTable; + int nPCTSegNumber; + + char **papszCategoryNames; + + void Initialize(); + + public: + PCIDSK2Band( PCIDSK2Dataset *, PCIDSKFile *, int ); + PCIDSK2Band( PCIDSKChannel * ); + ~PCIDSK2Band(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); + + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual CPLErr SetColorTable( GDALColorTable * ); + + virtual void SetDescription( const char * ); + + virtual char **GetMetadataDomainList(); + CPLErr SetMetadata( char **, const char * ); + char **GetMetadata( const char* ); + CPLErr SetMetadataItem(const char*,const char*,const char*); + const char *GetMetadataItem( const char*, const char*); + + virtual char **GetCategoryNames(); +}; + +/************************************************************************/ +/* OGRPCIDSKLayer */ +/************************************************************************/ + +class OGRPCIDSKLayer : public OGRLayer +{ + PCIDSK::PCIDSKVectorSegment *poVecSeg; + PCIDSK::PCIDSKSegment *poSeg; + + OGRFeatureDefn *poFeatureDefn; + + OGRFeature * GetNextUnfilteredFeature(); + + int iRingStartField; + PCIDSK::ShapeId hLastShapeId; + + bool bUpdateAccess; + + OGRSpatialReference *poSRS; + + public: + OGRPCIDSKLayer( PCIDSK::PCIDSKSegment*, bool bUpdate ); + ~OGRPCIDSKLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + OGRFeature *GetFeature( GIntBig nFeatureId ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ); + + OGRErr DeleteFeature( GIntBig nFID ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + + GIntBig GetFeatureCount( int ); + OGRErr GetExtent( OGREnvelope *psExtent, int bForce ); +}; + +#endif /* PCIDSKDATASET2_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/pcidsk/pcidsktiledrasterband.cpp b/bazaar/plugin/gdal/frmts/pcidsk/pcidsktiledrasterband.cpp new file mode 100644 index 000000000..5af012f98 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/pcidsktiledrasterband.cpp @@ -0,0 +1,365 @@ +/****************************************************************************** + * $Id: pcidsktiledrasterband.cpp 17688 2009-09-25 16:01:19Z dron $ + * + * Project: PCIDSK Database File + * Purpose: Implementation of PCIDSKTiledRasterBand + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pcidsk.h" + +CPL_CVSID("$Id: pcidsktiledrasterband.cpp 17688 2009-09-25 16:01:19Z dron $"); + +/************************************************************************/ +/* PCIDSKRasterBand() */ +/************************************************************************/ + +PCIDSKTiledRasterBand::PCIDSKTiledRasterBand( PCIDSKDataset *poDS, + int nBand, int nImage ) + +{ + poPDS = poDS; + this->poDS = poDS; + + this->nBand = nBand; + this->nImage = nImage; + + nOverviewCount = 0; + papoOverviews = NULL; + + nBlocks = 0; + panBlockOffset = NULL; + + if( !BuildBlockMap() ) + return; + +/* -------------------------------------------------------------------- */ +/* Load and parse image header. This is the image header */ +/* within the tiled image data. */ +/* -------------------------------------------------------------------- */ + char achBData[128]; + + SysRead( 0, 128, achBData ); + + nRasterXSize = (int) CPLScanLong(achBData + 0, 8); + nRasterYSize = (int) CPLScanLong(achBData + 8, 8); + nBlockXSize = (int) CPLScanLong(achBData + 16, 8); + nBlockYSize = (int) CPLScanLong(achBData + 24, 8); + + int nBPR = (nBlockXSize) ? (nRasterXSize + nBlockXSize - 1) / nBlockXSize : 0; + int nBPC = (nBlockYSize) ? (nRasterYSize + nBlockYSize - 1) / nBlockYSize : 0; + + /* nBPR * nBPC * 20 must fit on an int. See BuildTileMap() */ + if ( nRasterXSize <= 0 || nRasterYSize <= 0 || nBlockXSize <= 0 || nBlockYSize <= 0 + || nRasterXSize > INT_MAX - (nBlockXSize - 1) + || nRasterYSize > INT_MAX - (nBlockYSize - 1) + || (double)nBPR * (double)nBPC * 20 > INT_MAX ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid raster or block dimensions" ); + nRasterXSize = 0; + nRasterYSize = 0; + nBlockXSize = 0; + nBlockYSize = 0; + } + + eDataType = poPDS->PCIDSKTypeToGDAL( achBData + 32 ); + + szCompression[8] = '\0'; + memcpy( szCompression, achBData+54, 8 ); +} + +/************************************************************************/ +/* ~PCIDSKTiledRasterBand() */ +/************************************************************************/ + +PCIDSKTiledRasterBand::~PCIDSKTiledRasterBand() +{ + FlushCache(); + + int i; + + for( i = 0; i < nOverviewCount; i++ ) + delete papoOverviews[i]; + CPLFree( papoOverviews ); + + CPLFree( panBlockOffset ); + CPLFree( panTileOffset ); + CPLFree( panTileSize ); +} + +/************************************************************************/ +/* BuildBlockMap() */ +/************************************************************************/ + +int PCIDSKTiledRasterBand::BuildBlockMap() + +{ + nBlocks = 0; + panBlockOffset = NULL; + + nTileCount = 0; + panTileOffset = NULL; + panTileSize = NULL; + +/* -------------------------------------------------------------------- */ +/* Read the whole block map segment. */ +/* -------------------------------------------------------------------- */ + int nBMapSize; + char *pachBMap; + + if( poPDS->nBlockMapSeg < 1 ) + return FALSE; + + nBMapSize = (int) poPDS->panSegSize[poPDS->nBlockMapSeg-1]; + pachBMap = (char *) CPLCalloc(nBMapSize+1,1); + + if( !poPDS->SegRead( poPDS->nBlockMapSeg, 0, nBMapSize, pachBMap ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Parse the header. */ +/* -------------------------------------------------------------------- */ + int nMaxBlocks = (int) CPLScanLong(pachBMap + 18,8); + + if( !EQUALN(pachBMap,"VERSION",7) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Build a "back link" map for this image's blocks. We need */ +/* this to positively identify the first block in the chain. */ +/* -------------------------------------------------------------------- */ + int *panBackLink; + int i, nLastBlock = -1; + + panBackLink = (int *) VSICalloc( sizeof(int), nMaxBlocks ); + if ( panBackLink == NULL ) + { + VSIFree( pachBMap ); + VSIFree( panBackLink ); + return FALSE; + } + for( i = 0; i < nMaxBlocks; i++ ) + panBackLink[i] = -1; + + for( i = 0; i < nMaxBlocks; i++ ) + { + char *pachEntry = pachBMap + i * 28 + 512; + int nThisImage = (int) CPLScanLong(pachEntry + 12,8); + int nNextBlock = (int) CPLScanLong(pachEntry + 20,8); + + if ( nThisImage != nImage || nNextBlock >= nMaxBlocks ) + continue; + + if( nNextBlock == -1 ) + nLastBlock = i; + else + panBackLink[nNextBlock] = i; + } + +/* -------------------------------------------------------------------- */ +/* Track back through chain to identify the first entry (while */ +/* counting). */ +/* -------------------------------------------------------------------- */ + int iBlock = nLastBlock; + + nBlocks = 1; + while( panBackLink[iBlock] != -1 ) + { + nBlocks++; + iBlock = panBackLink[iBlock]; + } + + CPLFree( panBackLink ); + panBlockOffset = (vsi_l_offset *) VSIMalloc(sizeof(vsi_l_offset)*nBlocks); + if ( panBlockOffset == NULL ) + { + CPLFree( pachBMap ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Process blocks, transforming to absolute offsets in the */ +/* PCIDSK file. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBlocks; i++ ) + { + char *pachEntry = pachBMap + iBlock * 28 + 512; + int nBDataSeg = CPLScanLong( pachEntry + 0, 4 ); + int nBDataBlock = CPLScanLong( pachEntry + 4, 8 ); + + if ( nBDataSeg <= 0 || nBDataSeg > poPDS->nSegCount + || poPDS->panSegType[nBDataSeg-1] != 182) + { + CPLFree( pachBMap ); + return FALSE; + } + + panBlockOffset[i] = + ((vsi_l_offset) nBDataBlock) * 8192 + + poPDS->panSegOffset[nBDataSeg-1] + 1024; + + iBlock = (int) CPLScanLong( pachEntry + 20, 8 ); + } + + CPLFree( pachBMap ); + + return TRUE; +} + +/************************************************************************/ +/* BuildTileMap() */ +/************************************************************************/ + +int PCIDSKTiledRasterBand::BuildTileMap() + +{ + if ( nTileCount < 0 ) + return FALSE; + if( nTileCount > 0) + return TRUE; + + int nBPR = (nRasterXSize + nBlockXSize - 1) / nBlockXSize; + int nBPC = (nRasterYSize + nBlockYSize - 1) / nBlockYSize; + + nTileCount = nBPR * nBPC; + panTileOffset = (vsi_l_offset *) + VSICalloc( sizeof(vsi_l_offset), nTileCount ); + panTileSize = (int *) VSICalloc( sizeof(int), nTileCount ); + + char *pachTileInfo = (char *) VSIMalloc( 20 * nTileCount ); + if ( panTileOffset == NULL || panTileSize == NULL || pachTileInfo == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory" ); + nTileCount = -1; + CPLFree( pachTileInfo ); + return FALSE; + } + + if( !SysRead( 128, 20 * nTileCount, pachTileInfo ) ) + { + nTileCount = -1; + CPLFree( pachTileInfo ); + return FALSE; + } + + for( int iTile = 0; iTile < nTileCount; iTile++ ) + { + panTileOffset[iTile] = (vsi_l_offset) + CPLScanUIntBig( pachTileInfo+12*iTile, 12 ); + panTileSize[iTile] = (int) + CPLScanLong( pachTileInfo+12*nTileCount+8*iTile, 8 ); + } + + CPLFree( pachTileInfo ); + + return TRUE; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr PCIDSKTiledRasterBand::IReadBlock( int nBlockX, int nBlockY, + void *pData ) + +{ + int iTile; + + if( !EQUALN(szCompression,"NONE",4) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Compression '%s' not supported by GDAL.", + szCompression ); + return CE_Failure; + } + + if( !BuildTileMap() ) + return CE_Failure; + + int nBPR = (nRasterXSize + nBlockXSize - 1) / nBlockXSize; + + iTile = nBlockX + nBlockY * nBPR; + + if( panTileOffset[iTile] == (vsi_l_offset) -1 ) + { + int nWordSize = GDALGetDataTypeSize( eDataType ) / 8; + memset( pData, 0, nBlockXSize*nBlockYSize*nWordSize ); + } + else + { + if( !SysRead( panTileOffset[iTile], panTileSize[iTile], pData ) ) + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* PCIDSK multibyte data is always big endian. Swap if needed. */ +/* -------------------------------------------------------------------- */ +#ifdef CPL_LSB + int nWordSize = GDALGetDataTypeSize( eDataType ) / 8; + GDALSwapWords( pData, nWordSize, nBlockXSize * nBlockYSize, nWordSize ); +#endif + + return CE_None; +} + +/************************************************************************/ +/* SysRead() */ +/************************************************************************/ + +int PCIDSKTiledRasterBand::SysRead( vsi_l_offset nOffset, + int nSize, + void *pData ) + +{ + int iReadSoFar = 0; + + while( iReadSoFar < nSize ) + { + int iBlock; + vsi_l_offset nNextOffset = nOffset + iReadSoFar; + vsi_l_offset nRealOffset; + int nOffsetInBlock, nThisReadBytes; + + iBlock = (int) (nNextOffset >> 13); + nOffsetInBlock = (int) (nNextOffset & 0x1fff); + + if ( iBlock >= nBlocks ) + return 0; + + nRealOffset = panBlockOffset[iBlock] + nOffsetInBlock; + + nThisReadBytes = MIN(nSize - iReadSoFar,8192 - nOffsetInBlock); + + if( VSIFSeekL( poPDS->fp, nRealOffset, SEEK_SET ) != 0 ) + return 0; + + if( VSIFReadL( ((char *) pData) + iReadSoFar, 1, nThisReadBytes, + poPDS->fp ) != (size_t) nThisReadBytes ) + return 0; + + iReadSoFar += nThisReadBytes; + } + + return nSize; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/Doxyfile b/bazaar/plugin/gdal/frmts/pcidsk/sdk/Doxyfile new file mode 100644 index 000000000..113d33c70 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/Doxyfile @@ -0,0 +1,1258 @@ +# Doxyfile 1.5.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = libpcidsk + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, +# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, +# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, +# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . \ + core/pcidskopen.cpp core/pcidskcreate.cpp \ + core/pcidskexception.cpp core/pcidsk_pubutils.cpp \ + core/pcidskinterfaces.cpp + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = io_win32.cpp win32_mutex.cpp sysblockmap.cpp \ + pcidsk_config.h pcidsk_p.h pcidskbuffer.cpp \ + sysvirtualfile.cpp + + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = cpcidsk*.cpp c*channel.cpp *_p.cpp + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = PCIDSK_DLL + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a caller dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/Makefile b/bazaar/plugin/gdal/frmts/pcidsk/sdk/Makefile new file mode 100644 index 000000000..0ace8a1e7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/Makefile @@ -0,0 +1,68 @@ + +# Comment out to skip use of libjpeg +JPEG_FLAGS = -DHAVE_LIBJPEG +JPEG_LDFLAGS = -ljpeg + +OBJ = \ + channel/cbandinterleavedchannel.o \ + channel/cpcidskchannel.o \ + channel/cpixelinterleavedchannel.o \ + channel/ctiledchannel.o \ + core/cpcidskfile.o \ + core/libjpeg_io.o \ + core/metadataset_p.o \ + core/pcidskbuffer.o \ + core/pcidskcreate.o \ + core/pcidskexception.o \ + core/pcidskinterfaces.o \ + core/pcidskopen.o \ + core/pcidsk_pubutils.o \ + core/pcidsk_utils.o \ + core/sysvirtualfile.o \ + port/io_stdio.o \ + port/pthread_mutex.o \ + segment/cpcidskgeoref.o \ + segment/cpcidskpct.o \ + segment/cpcidsksegment.o \ + segment/cpcidskvectorsegment.o \ + segment/metadatasegment_p.o \ + segment/sysblockmap.o \ + segment/cpcidskrpcmodel.o \ + segment/cpcidskgcp2segment.o \ + segment/cpcidskbitmap.o \ + segment/cpcidsk_tex.o + + +CXXFLAGS = -g -O0 -Wall -fPIC -I. $(JPEG_FLAGS) + +default: pcidsk.a libpcidsk.so + +pcidsk.a: $(OBJ) + ar r pcidsk.a $(OBJ) + +libpcidsk.so: $(OBJ) + g++ -fPIC $(JPEG_LDFLAGS) -ldl -shared -o libpcidsk.so $(OBJ) + +clean: + rm -f $(OBJ) + -rm -f *.o + -rm -f *.a + -rm -f *.so + +core_comp: + (cd core; $(MAKE)) + (cd channel; $(MAKE)) + (cd segment; $(MAKE)) + (cd port; $(MAKE)) + +check: default + (cd ../tests; $(MAKE) check) + +$(OBJ): pcidsk.h pcidsk_config.h + +%.o: %.cpp + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< + +docs: + doxygen + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/Makefile.vc b/bazaar/plugin/gdal/frmts/pcidsk/sdk/Makefile.vc new file mode 100644 index 000000000..d03e2442b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/Makefile.vc @@ -0,0 +1,60 @@ +#OPTFLAGS = /Zi +OPTFLAGS = /Ox +CXXFLAGS = /nologo /MD /GR /EHsc $(OPTFLAGS) /W3 /I. \ + /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE + +# Add this if producing a DLL: +# /DLIBPCIDSK_EXPORTS + +OBJ = \ + channel\cbandinterleavedchannel.obj \ + channel\cpcidskchannel.obj \ + channel\cpixelinterleavedchannel.obj \ + channel\ctiledchannel.obj \ + core\cpcidskfile.obj \ + core\libjpeg_io.obj \ + core\metadataset_p.obj \ + core\pcidskbuffer.obj \ + core\pcidskcreate.obj \ + core\pcidskexception.obj \ + core\pcidskinterfaces.obj \ + core\pcidskopen.obj \ + core\pcidsk_pubutils.obj \ + core\pcidsk_utils.obj \ + core\sysvirtualfile.obj \ + port\io_win32.obj \ + port\win32_mutex.obj \ + segment\cpcidskgeoref.obj \ + segment\cpcidsksegment.obj \ + segment\cpcidskvectorsegment.obj \ + segment\cpcidskrpcmodel.obj \ + segment\metadatasegment_p.obj \ + segment\sysblockmap.obj \ + segment\cpcidskbitmap.obj \ + segment\cpcidsk_tex.obj + +default: pcidsk.lib + +pcidsk.lib: $(OBJ) + lib /nologo /out:pcidsk.lib $(OBJ) + +clean: + -del $(OBJ) + -del *.lib + -del *.pdb + -del *.ilk + +components: + (cd core; $(MAKE)) + (cd channel; $(MAKE)) + (cd port; $(MAKE)) + (cd segment; $(MAKE)) + +check: default + (cd ../tests; $(MAKE) check) + +$(OBJ): pcidsk.h pcidsk_config.h + +.cpp.obj: + $(CC) $(CXXFLAGS) /c $*.cpp /Fo$*.obj + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/Makefile b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/Makefile new file mode 100644 index 000000000..a954348d5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/Makefile @@ -0,0 +1,8 @@ +default: + (cd .. ; $(MAKE)) + +clean: + (cd .. ; $(MAKE) clean) + +check: + (cd .. ; $(MAKE) check) diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cbandinterleavedchannel.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cbandinterleavedchannel.cpp new file mode 100644 index 000000000..bf018853a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cbandinterleavedchannel.cpp @@ -0,0 +1,476 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CBandInterleavedChannel class. + * + * This class is used to implement band interleaved channels within a + * PCIDSK file (which are always packed, and FILE interleaved data from + * external raw files which may not be packed. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_channel.h" +#include "pcidsk_buffer.h" +#include "pcidsk_exception.h" +#include "pcidsk_file.h" +#include "core/pcidsk_utils.h" +#include "core/cpcidskfile.h" +#include "core/clinksegment.h" +#include "channel/cbandinterleavedchannel.h" +#include +#include +#include +#include + +#include "cpl_port.h" + +using namespace PCIDSK; + +/************************************************************************/ +/* CBandInterleavedChannel() */ +/************************************************************************/ + +CBandInterleavedChannel::CBandInterleavedChannel( PCIDSKBuffer &image_header, + uint64 ih_offset, + CPL_UNUSED PCIDSKBuffer &file_header, + int channelnum, + CPCIDSKFile *file, + uint64 image_offset, + eChanType pixel_type ) + : CPCIDSKChannel( image_header, ih_offset, file, pixel_type, channelnum) + +{ + io_handle_p = NULL; + io_mutex_p = NULL; + +/* -------------------------------------------------------------------- */ +/* Establish the data layout. */ +/* -------------------------------------------------------------------- */ + if( strcmp(file->GetInterleaving().c_str(),"FILE") == 0 ) + { + start_byte = atouint64(image_header.Get( 168, 16 )); + pixel_offset = atouint64(image_header.Get( 184, 8 )); + line_offset = atouint64(image_header.Get( 192, 8 )); + } + else + { + start_byte = image_offset; + pixel_offset = DataTypeSize(pixel_type); + line_offset = pixel_offset * width; + } + +/* -------------------------------------------------------------------- */ +/* Establish the file we will be accessing. */ +/* -------------------------------------------------------------------- */ + image_header.Get(64,64,filename); + + filename = MassageLink( filename ); + + if( filename.length() == 0 ) + file->GetIODetails( &io_handle_p, &io_mutex_p ); + + else + filename = MergeRelativePath( file->GetInterfaces()->io, + file->GetFilename(), + filename ); +} + +/************************************************************************/ +/* ~CBandInterleavedChannel() */ +/************************************************************************/ + +CBandInterleavedChannel::~CBandInterleavedChannel() + +{ +} + +/************************************************************************/ +/* ReadBlock() */ +/************************************************************************/ + +int CBandInterleavedChannel::ReadBlock( int block_index, void *buffer, + int xoff, int yoff, + int xsize, int ysize ) + +{ + PCIDSKInterfaces *interfaces = file->GetInterfaces(); + +/* -------------------------------------------------------------------- */ +/* Default window if needed. */ +/* -------------------------------------------------------------------- */ + if( xoff == -1 && yoff == -1 && xsize == -1 && ysize == -1 ) + { + xoff = 0; + yoff = 0; + xsize = GetBlockWidth(); + ysize = GetBlockHeight(); + } + +/* -------------------------------------------------------------------- */ +/* Validate Window */ +/* -------------------------------------------------------------------- */ + if( xoff < 0 || xoff + xsize > GetBlockWidth() + || yoff < 0 || yoff + ysize > GetBlockHeight() ) + { + ThrowPCIDSKException( + "Invalid window in ReadBloc(): xoff=%d,yoff=%d,xsize=%d,ysize=%d", + xoff, yoff, xsize, ysize ); + } + +/* -------------------------------------------------------------------- */ +/* Establish region to read. */ +/* -------------------------------------------------------------------- */ + int pixel_size = DataTypeSize( pixel_type ); + uint64 offset = start_byte + line_offset * block_index + + pixel_offset * xoff; + int window_size = (int) (pixel_offset*(xsize-1) + pixel_size); + +/* -------------------------------------------------------------------- */ +/* Get file access handles if we don't already have them. */ +/* -------------------------------------------------------------------- */ + if( io_handle_p == NULL ) + file->GetIODetails( &io_handle_p, &io_mutex_p, filename.c_str(), + file->GetUpdatable() ); + +/* -------------------------------------------------------------------- */ +/* If the imagery is packed, we can read directly into the */ +/* target buffer. */ +/* -------------------------------------------------------------------- */ + if( pixel_size == (int) pixel_offset ) + { + MutexHolder holder( *io_mutex_p ); + + interfaces->io->Seek( *io_handle_p, offset, SEEK_SET ); + interfaces->io->Read( buffer, 1, window_size, *io_handle_p ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise we allocate a working buffer that holds the whole */ +/* line, read into that, and pick out our data of interest. */ +/* -------------------------------------------------------------------- */ + else + { + int i; + PCIDSKBuffer line_from_disk( window_size ); + char *this_pixel; + + MutexHolder holder( *io_mutex_p ); + + interfaces->io->Seek( *io_handle_p, offset, SEEK_SET ); + interfaces->io->Read( line_from_disk.buffer, + 1, line_from_disk.buffer_size, + *io_handle_p ); + + for( i = 0, this_pixel = line_from_disk.buffer; i < xsize; i++ ) + { + memcpy( ((char *) buffer) + pixel_size * i, + this_pixel, pixel_size ); + this_pixel += pixel_size; + } + } + +/* -------------------------------------------------------------------- */ +/* Do byte swapping if needed. */ +/* -------------------------------------------------------------------- */ + if( needs_swap ) + SwapPixels( buffer, pixel_type, xsize ); + + return 1; +} + +/************************************************************************/ +/* WriteBlock() */ +/************************************************************************/ + +int CBandInterleavedChannel::WriteBlock( int block_index, void *buffer ) + +{ + PCIDSKInterfaces *interfaces = file->GetInterfaces(); + + if( !file->GetUpdatable() ) + throw PCIDSKException( "File not open for update in WriteBlock()" ); + + InvalidateOverviews(); + +/* -------------------------------------------------------------------- */ +/* Establish region to read. */ +/* -------------------------------------------------------------------- */ + int pixel_size = DataTypeSize( pixel_type ); + uint64 offset = start_byte + line_offset * block_index; + int window_size = (int) (pixel_offset*(width-1) + pixel_size); + +/* -------------------------------------------------------------------- */ +/* Get file access handles if we don't already have them. */ +/* -------------------------------------------------------------------- */ + if( io_handle_p == NULL ) + file->GetIODetails( &io_handle_p, &io_mutex_p, filename.c_str(), + file->GetUpdatable() ); + +/* -------------------------------------------------------------------- */ +/* If the imagery is packed, we can read directly into the */ +/* target buffer. */ +/* -------------------------------------------------------------------- */ + if( pixel_size == (int) pixel_offset ) + { + MutexHolder holder( *io_mutex_p ); + + if( needs_swap ) // swap before write. + SwapPixels( buffer, pixel_type, width ); + + interfaces->io->Seek( *io_handle_p, offset, SEEK_SET ); + interfaces->io->Write( buffer, 1, window_size, *io_handle_p ); + + if( needs_swap ) // restore to original order. + SwapPixels( buffer, pixel_type, width ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise we allocate a working buffer that holds the whole */ +/* line, read into that, and pick out our data of interest. */ +/* -------------------------------------------------------------------- */ + else + { + int i; + PCIDSKBuffer line_from_disk( window_size ); + char *this_pixel; + + MutexHolder holder( *io_mutex_p ); + + interfaces->io->Seek( *io_handle_p, offset, SEEK_SET ); + interfaces->io->Read( buffer, 1, line_from_disk.buffer_size, + *io_handle_p ); + + for( i = 0, this_pixel = line_from_disk.buffer; i < width; i++ ) + { + memcpy( this_pixel, ((char *) buffer) + pixel_size * i, + pixel_size ); + + if( needs_swap ) // swap before write. + SwapPixels( this_pixel, pixel_type, 1 ); + + this_pixel += pixel_size; + } + + interfaces->io->Seek( *io_handle_p, offset, SEEK_SET ); + interfaces->io->Write( buffer, 1, line_from_disk.buffer_size, + *io_handle_p ); + } + +/* -------------------------------------------------------------------- */ +/* Do byte swapping if needed. */ +/* -------------------------------------------------------------------- */ + + return 1; +} + +/************************************************************************/ +/* GetChanInfo() */ +/************************************************************************/ +void CBandInterleavedChannel +::GetChanInfo( std::string &filename_ret, uint64 &image_offset, + uint64 &pixel_offset, uint64 &line_offset, + bool &little_endian ) const + +{ + image_offset = start_byte; + pixel_offset = this->pixel_offset; + line_offset = this->line_offset; + little_endian = (byte_order == 'S'); + +/* -------------------------------------------------------------------- */ +/* We fetch the filename from the header since it will be the */ +/* "clean" version without any paths. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer ih(64); + file->ReadFromFile( ih.buffer, ih_offset+64, 64 ); + + ih.Get(0,64,filename_ret); + filename_ret = MassageLink( filename_ret ); +} + +/************************************************************************/ +/* SetChanInfo() */ +/************************************************************************/ + +void CBandInterleavedChannel +::SetChanInfo( std::string filename, uint64 image_offset, + uint64 pixel_offset, uint64 line_offset, + bool little_endian ) + +{ + if( ih_offset == 0 ) + ThrowPCIDSKException( "No Image Header available for this channel." ); + +/* -------------------------------------------------------------------- */ +/* Fetch the existing image header. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer ih(1024); + + file->ReadFromFile( ih.buffer, ih_offset, 1024 ); + +/* -------------------------------------------------------------------- */ +/* If the linked filename is too long to fit in the 64 */ +/* character IHi.2 field, then we need to use a link segment to */ +/* store the filename. */ +/* -------------------------------------------------------------------- */ + std::string IHi2_filename; + + if( filename.size() > 64 ) + { + int link_segment; + + ih.Get( 64, 64, IHi2_filename ); + + if( IHi2_filename.substr(0,3) == "LNK" ) + { + link_segment = std::atoi( IHi2_filename.c_str() + 4 ); + } + else + { + char link_filename[64]; + + link_segment = + file->CreateSegment( "Link ", + "Long external channel filename link.", + SEG_SYS, 1 ); + + sprintf( link_filename, "LNK %4d", link_segment ); + IHi2_filename = link_filename; + } + + CLinkSegment *link = + dynamic_cast( file->GetSegment( link_segment ) ); + + if( link != NULL ) + { + link->SetPath( filename ); + link->Synchronize(); + } + } + +/* -------------------------------------------------------------------- */ +/* If we used to have a link segment but no longer need it, we */ +/* need to delete the link segment. */ +/* -------------------------------------------------------------------- */ + else + { + ih.Get( 64, 64, IHi2_filename ); + + if( IHi2_filename.substr(0,3) == "LNK" ) + { + int link_segment = std::atoi( IHi2_filename.c_str() + 4 ); + + file->DeleteSegment( link_segment ); + } + + IHi2_filename = filename; + } + +/* -------------------------------------------------------------------- */ +/* Update the image header. */ +/* -------------------------------------------------------------------- */ + // IHi.2 + ih.Put( IHi2_filename.c_str(), 64, 64 ); + + // IHi.6.1 + ih.Put( image_offset, 168, 16 ); + + // IHi.6.2 + ih.Put( pixel_offset, 184, 8 ); + + // IHi.6.3 + ih.Put( line_offset, 192, 8 ); + + // IHi.6.5 + if( little_endian ) + ih.Put( "S", 201, 1 ); + else + ih.Put( "N", 201, 1 ); + + file->WriteToFile( ih.buffer, ih_offset, 1024 ); + +/* -------------------------------------------------------------------- */ +/* Update local configuration. */ +/* -------------------------------------------------------------------- */ + this->filename = MergeRelativePath( file->GetInterfaces()->io, + file->GetFilename(), + filename ); + + start_byte = image_offset; + this->pixel_offset = pixel_offset; + this->line_offset = line_offset; + + if( little_endian ) + byte_order = 'S'; + else + byte_order = 'N'; + +/* -------------------------------------------------------------------- */ +/* Determine if we need byte swapping. */ +/* -------------------------------------------------------------------- */ + unsigned short test_value = 1; + + if( ((uint8 *) &test_value)[0] == 1 ) + needs_swap = (byte_order != 'S'); + else + needs_swap = (byte_order == 'S'); + + if( pixel_type == CHN_8U ) + needs_swap = 0; +} + +/************************************************************************/ +/* MassageLink() */ +/* */ +/* Return the filename after applying translation of long */ +/* linked filenames using a link segment. */ +/************************************************************************/ + +std::string CBandInterleavedChannel::MassageLink( std::string filename_in ) const + +{ + if (filename_in.find("LNK") == 0) + { + std::string seg_str(filename_in, 4, 4); + unsigned int seg_num = std::atoi(seg_str.c_str()); + + if (seg_num == 0) + { + throw PCIDSKException("Unable to find link segment. Link name: %s", + filename_in.c_str()); + } + + CLinkSegment* link_seg = + dynamic_cast(file->GetSegment(seg_num)); + if (link_seg == NULL) + { + throw PCIDSKException("Failed to get Link Information Segment."); + } + + filename_in = link_seg->GetPath(); + } + + return filename_in; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cbandinterleavedchannel.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cbandinterleavedchannel.h new file mode 100644 index 000000000..92d4efb17 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cbandinterleavedchannel.h @@ -0,0 +1,89 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CBandInterleavedChannel class. + * + * This class is used to implement band interleaved channels within a + * PCIDSK file (which are always packed, and FILE interleaved data from + * external raw files which may not be packed. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_CHANNEL_CBANDINTERLEAVEDCHANNEL_H +#define __INCLUDE_CHANNEL_CBANDINTERLEAVEDCHANNEL_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_buffer.h" +#include "channel/cpcidskchannel.h" +#include + +namespace PCIDSK +{ + class CPCIDSKFile; + class Mutex; +/************************************************************************/ +/* CBandInterleavedChannel */ +/* */ +/* Also used for FILE interleaved raw files. */ +/************************************************************************/ + + class CBandInterleavedChannel : public CPCIDSKChannel + { + public: + CBandInterleavedChannel( PCIDSKBuffer &image_header, + uint64 ih_offset, + PCIDSKBuffer &file_header, + int channelnum, + CPCIDSKFile *file, + uint64 image_offset, + eChanType pixel_type ); + virtual ~CBandInterleavedChannel(); + + virtual int ReadBlock( int block_index, void *buffer, + int xoff=-1, int yoff=-1, + int xsize=-1, int ysize=-1 ); + virtual int WriteBlock( int block_index, void *buffer ); + + virtual void GetChanInfo( std::string &filename, uint64 &image_offset, + uint64 &pixel_offset, uint64 &line_offset, + bool &little_endian ) const; + virtual void SetChanInfo( std::string filename, uint64 image_offset, + uint64 pixel_offset, uint64 line_offset, + bool little_endian ); + + private: + // raw file layout - internal or external + uint64 start_byte; + uint64 pixel_offset; + uint64 line_offset; + + std::string filename; + + void **io_handle_p; + Mutex **io_mutex_p; + + std::string MassageLink( std::string ) const; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_CHANNEL_CBANDINTERLEAVEDCHANNEL_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cexternalchannel.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cexternalchannel.cpp new file mode 100644 index 000000000..9ba29f351 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cexternalchannel.cpp @@ -0,0 +1,786 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CExternalChannel class. + * + * This class is used to implement band interleaved channels that are + * references to an external image database that is not just a raw file. + * It uses the application supplied EDB interface to access non-PCIDSK files. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_channel.h" +#include "pcidsk_buffer.h" +#include "pcidsk_exception.h" +#include "pcidsk_file.h" +#include "core/pcidsk_utils.h" +#include "core/cpcidskfile.h" +#include "channel/cexternalchannel.h" +#include "core/clinksegment.h" +#include +#include +#include +#include + +#include "cpl_port.h" + +using namespace PCIDSK; + +/************************************************************************/ +/* CExternalChannel() */ +/************************************************************************/ + +CExternalChannel::CExternalChannel( PCIDSKBuffer &image_header, + uint64 ih_offset, + CPL_UNUSED PCIDSKBuffer &file_header, + std::string filename, + int channelnum, + CPCIDSKFile *file, + eChanType pixel_type ) + : CPCIDSKChannel( image_header, ih_offset, file, pixel_type, channelnum) + +{ + db = NULL; + mutex = NULL; + +/* -------------------------------------------------------------------- */ +/* Establish the data window. */ +/* -------------------------------------------------------------------- */ + exoff = atoi(image_header.Get( 250, 8 )); + eyoff = atoi(image_header.Get( 258, 8 )); + exsize = atoi(image_header.Get( 266, 8 )); + eysize = atoi(image_header.Get( 274, 8 )); + + echannel = atoi(image_header.Get( 282, 8 )); + + if (echannel == 0) { + echannel = channelnum; + } + +/* -------------------------------------------------------------------- */ +/* Establish the file we will be accessing. */ +/* -------------------------------------------------------------------- */ + if( filename != "" ) + this->filename = filename; + else + image_header.Get(64,64,this->filename); +} + +/************************************************************************/ +/* ~CExternalChannel() */ +/************************************************************************/ + +CExternalChannel::~CExternalChannel() + +{ + // no need to deaccess the EDBFile - the file is responsible for that. +} + +/************************************************************************/ +/* AccessDB() */ +/************************************************************************/ + +void CExternalChannel::AccessDB() const + +{ + if( db != NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* open, or fetch an already open file handle. */ +/* -------------------------------------------------------------------- */ + writable = file->GetEDBFileDetails( &db, &mutex, filename ); + +/* -------------------------------------------------------------------- */ +/* Capture the block size. */ +/* -------------------------------------------------------------------- */ + block_width = db->GetBlockWidth( echannel ); + if( block_width > width ) + block_width = width; + block_height = db->GetBlockHeight( echannel ); + if( block_height > height ) + block_height = height; + + blocks_per_row = (GetWidth() + block_width - 1) / block_width; +} + +/************************************************************************/ +/* GetBlockWidth() */ +/************************************************************************/ + +int CExternalChannel::GetBlockWidth() const + +{ + AccessDB(); + + return block_width; +} + +/************************************************************************/ +/* GetBlockHeight() */ +/************************************************************************/ + +int CExternalChannel::GetBlockHeight() const + +{ + AccessDB(); + + return block_height; +} + +/************************************************************************/ +/* ReadBlock() */ +/************************************************************************/ + +int CExternalChannel::ReadBlock( int block_index, void *buffer, + int xoff, int yoff, + int xsize, int ysize ) + +{ + AccessDB(); + +/* -------------------------------------------------------------------- */ +/* Default window if needed. */ +/* -------------------------------------------------------------------- */ + if( xoff == -1 && yoff == -1 && xsize == -1 && ysize == -1 ) + { + xoff = 0; + yoff = 0; + xsize = GetBlockWidth(); + ysize = GetBlockHeight(); + } + +/* -------------------------------------------------------------------- */ +/* Validate Window */ +/* -------------------------------------------------------------------- */ + if( xoff < 0 || xoff + xsize > GetBlockWidth() + || yoff < 0 || yoff + ysize > GetBlockHeight() ) + { + ThrowPCIDSKException( + "Invalid window in ReadBlock(): xoff=%d,yoff=%d,xsize=%d,ysize=%d", + xoff, yoff, xsize, ysize ); + } + +/* -------------------------------------------------------------------- */ +/* Do a direct call for the simpliest case of 1:1 block mapping. */ +/* -------------------------------------------------------------------- */ + if( exoff == 0 && eyoff == 0 + && exsize == db->GetWidth() + && eysize == db->GetHeight() ) + { + MutexHolder oHolder( mutex ); + return db->ReadBlock( echannel, block_index, buffer, + xoff, yoff, xsize, ysize ); + } + +/* ==================================================================== */ +/* Otherwise we need to break this down into potentially up to */ +/* four requests against the source file. */ +/* ==================================================================== */ + int src_block_width = db->GetBlockWidth( echannel ); + int src_block_height = db->GetBlockHeight( echannel ); + int src_blocks_per_row = (db->GetWidth() + src_block_width - 1) + / src_block_width; + int pixel_size = DataTypeSize(GetType()); + uint8 *temp_buffer = (uint8 *) calloc(src_block_width*src_block_height, + pixel_size); + int txoff, tyoff, txsize, tysize; + int dst_blockx, dst_blocky; + + if( temp_buffer == NULL ) + ThrowPCIDSKException( "Failed to allocate temporary block buffer." ); + + dst_blockx = block_index % blocks_per_row; + dst_blocky = block_index / blocks_per_row; + + // what is the region of our desired data on the destination file? + + txoff = dst_blockx * block_width + exoff + xoff; + tyoff = dst_blocky * block_height + eyoff + yoff; + txsize = xsize; + tysize = ysize; + +/* -------------------------------------------------------------------- */ +/* read external block for top left corner of target block. */ +/* -------------------------------------------------------------------- */ + int ablock_x, ablock_y, i_line; + int axoff, ayoff, axsize, aysize; + int block1_xsize=0, block1_ysize=0; + int ttxoff, ttyoff, ttxsize, ttysize; + + ttxoff = txoff; + ttyoff = tyoff; + ttxsize = txsize; + ttysize = tysize; + + ablock_x = ttxoff / src_block_width; + ablock_y = ttyoff / src_block_height; + + axoff = ttxoff - ablock_x * src_block_width; + ayoff = ttyoff - ablock_y * src_block_height; + + if( axoff + ttxsize > src_block_width ) + axsize = src_block_width - axoff; + else + axsize = ttxsize; + + if( ayoff + ttysize > src_block_height ) + aysize = src_block_height - ayoff; + else + aysize = ttysize; + + if( axsize > 0 ) + block1_xsize = axsize; + else + block1_xsize = 0; + + if( aysize > 0 ) + block1_ysize = aysize; + else + block1_ysize = 0; + + if( axsize > 0 && aysize > 0 ) + { + MutexHolder oHolder( mutex ); + db->ReadBlock( echannel, ablock_x + ablock_y * src_blocks_per_row, + temp_buffer, axoff, ayoff, axsize, aysize ); + + for( i_line = 0; i_line < aysize; i_line++ ) + { + memcpy( ((uint8*) buffer) + i_line * xsize * pixel_size, + temp_buffer + i_line * axsize * pixel_size, + axsize * pixel_size ); + } + } + +/* -------------------------------------------------------------------- */ +/* read external block for top right corner of target block. */ +/* -------------------------------------------------------------------- */ + ttxoff = txoff + block1_xsize; + ttyoff = tyoff; + ttxsize = txsize - block1_xsize; + ttysize = tysize; + + ablock_x = ttxoff / src_block_width; + ablock_y = ttyoff / src_block_height; + + axoff = ttxoff - ablock_x * src_block_width; + ayoff = ttyoff - ablock_y * src_block_height; + + if( axoff + ttxsize > src_block_width ) + axsize = src_block_width - axoff; + else + axsize = ttxsize; + + if( ayoff + ttysize > src_block_height ) + aysize = src_block_height - ayoff; + else + aysize = ttysize; + + if( axsize > 0 && aysize > 0 ) + { + MutexHolder oHolder( mutex ); + db->ReadBlock( echannel, ablock_x + ablock_y * src_blocks_per_row, + temp_buffer, axoff, ayoff, axsize, aysize ); + + for( i_line = 0; i_line < aysize; i_line++ ) + { + memcpy( ((uint8*) buffer) + + (block1_xsize + i_line * xsize) * pixel_size, + temp_buffer + i_line * axsize * pixel_size, + axsize * pixel_size ); + } + } + +/* -------------------------------------------------------------------- */ +/* read external block for bottom left corner of target block. */ +/* -------------------------------------------------------------------- */ + ttxoff = txoff; + ttyoff = tyoff + block1_ysize; + ttxsize = txsize; + ttysize = tysize - block1_ysize; + + ablock_x = ttxoff / src_block_width; + ablock_y = ttyoff / src_block_height; + + axoff = ttxoff - ablock_x * src_block_width; + ayoff = ttyoff - ablock_y * src_block_height; + + if( axoff + ttxsize > src_block_width ) + axsize = src_block_width - axoff; + else + axsize = ttxsize; + + if( ayoff + ttysize > src_block_height ) + aysize = src_block_height - ayoff; + else + aysize = ttysize; + + if( axsize > 0 && aysize > 0 ) + { + MutexHolder oHolder( mutex ); + db->ReadBlock( echannel, ablock_x + ablock_y * src_blocks_per_row, + temp_buffer, axoff, ayoff, axsize, aysize ); + + for( i_line = 0; i_line < aysize; i_line++ ) + { + memcpy( ((uint8*) buffer) + + (i_line + block1_ysize) * xsize * pixel_size, + temp_buffer + i_line * axsize * pixel_size, + axsize * pixel_size ); + } + } + +/* -------------------------------------------------------------------- */ +/* read external block for bottom left corner of target block. */ +/* -------------------------------------------------------------------- */ + ttxoff = txoff + block1_xsize; + ttyoff = tyoff + block1_ysize; + ttxsize = txsize - block1_xsize; + ttysize = tysize - block1_ysize; + + ablock_x = ttxoff / src_block_width; + ablock_y = ttyoff / src_block_height; + + axoff = ttxoff - ablock_x * src_block_width; + ayoff = ttyoff - ablock_y * src_block_height; + + if( axoff + ttxsize > src_block_width ) + axsize = src_block_width - axoff; + else + axsize = ttxsize; + + if( ayoff + ttysize > src_block_height ) + aysize = src_block_height - ayoff; + else + aysize = ttysize; + + if( axsize > 0 && aysize > 0 ) + { + MutexHolder oHolder( mutex ); + db->ReadBlock( echannel, ablock_x + ablock_y * src_blocks_per_row, + temp_buffer, axoff, ayoff, axsize, aysize ); + + for( i_line = 0; i_line < aysize; i_line++ ) + { + memcpy( ((uint8*) buffer) + + (block1_xsize + (i_line + block1_ysize) * xsize) * pixel_size, + temp_buffer + i_line * axsize * pixel_size, + axsize * pixel_size ); + } + } + + free( temp_buffer ); + + return 1; +} + +/************************************************************************/ +/* WriteBlock() */ +/************************************************************************/ + +int CExternalChannel::WriteBlock( int block_index, void *buffer ) + +{ + AccessDB(); + + if( !file->GetUpdatable() || !writable ) + throw PCIDSKException( "File not open for update in WriteBlock()" ); + +/* -------------------------------------------------------------------- */ +/* Pass the request on directly in the simple case. */ +/* -------------------------------------------------------------------- */ + if( exoff == 0 && eyoff == 0 + && exsize == db->GetWidth() + && eysize == db->GetHeight() ) + { + MutexHolder oHolder( mutex ); + return db->WriteBlock( echannel, block_index, buffer ); + } + +/* ==================================================================== */ +/* Otherwise we need to break this down into potentially up to */ +/* four requests against the source file. */ +/* ==================================================================== */ + int src_block_width = db->GetBlockWidth( echannel ); + int src_block_height = db->GetBlockHeight( echannel ); + int src_blocks_per_row = (db->GetWidth() + src_block_width - 1) + / src_block_width; + int pixel_size = DataTypeSize(GetType()); + uint8 *temp_buffer = (uint8 *) calloc(src_block_width*src_block_height, + pixel_size); + int txoff, tyoff, txsize, tysize; + int dst_blockx, dst_blocky; + + if( temp_buffer == NULL ) + ThrowPCIDSKException( "Failed to allocate temporary block buffer." ); + + dst_blockx = block_index % blocks_per_row; + dst_blocky = block_index / blocks_per_row; + + // what is the region of our desired data on the destination file? + + txoff = dst_blockx * block_width + exoff; + tyoff = dst_blocky * block_height + eyoff; + txsize = block_width; + tysize = block_height; + +/* -------------------------------------------------------------------- */ +/* process external block for top left corner of target block. */ +/* -------------------------------------------------------------------- */ + int ablock_x, ablock_y, i_line; + int axoff, ayoff, axsize, aysize; + int block1_xsize=0, block1_ysize=0; + int ttxoff, ttyoff, ttxsize, ttysize; + + ttxoff = txoff; + ttyoff = tyoff; + ttxsize = txsize; + ttysize = tysize; + + ablock_x = ttxoff / src_block_width; + ablock_y = ttyoff / src_block_height; + + axoff = ttxoff - ablock_x * src_block_width; + ayoff = ttyoff - ablock_y * src_block_height; + + if( axoff + ttxsize > src_block_width ) + axsize = src_block_width - axoff; + else + axsize = ttxsize; + + if( ayoff + ttysize > src_block_height ) + aysize = src_block_height - ayoff; + else + aysize = ttysize; + + if( axsize > 0 ) + block1_xsize = axsize; + else + block1_xsize = 0; + + if( aysize > 0 ) + block1_ysize = aysize; + else + block1_ysize = 0; + + if( axsize > 0 && aysize > 0 ) + { + MutexHolder oHolder( mutex ); + db->ReadBlock( echannel, ablock_x + ablock_y * src_blocks_per_row, + temp_buffer ); + + for( i_line = 0; i_line < aysize; i_line++ ) + { + memcpy( temp_buffer + + (i_line+ayoff) * src_block_width * pixel_size + + axoff * pixel_size, + ((uint8*) buffer) + i_line * block_width * pixel_size, + axsize * pixel_size ); + } + + db->WriteBlock( echannel, ablock_x + ablock_y * src_blocks_per_row, + temp_buffer ); + } + +/* -------------------------------------------------------------------- */ +/* read external block for top right corner of target block. */ +/* -------------------------------------------------------------------- */ + ttxoff = txoff + block1_xsize; + ttyoff = tyoff; + ttxsize = txsize - block1_xsize; + ttysize = tysize; + + ablock_x = ttxoff / src_block_width; + ablock_y = ttyoff / src_block_height; + + axoff = ttxoff - ablock_x * src_block_width; + ayoff = ttyoff - ablock_y * src_block_height; + + if( axoff + ttxsize > src_block_width ) + axsize = src_block_width - axoff; + else + axsize = ttxsize; + + if( ayoff + ttysize > src_block_height ) + aysize = src_block_height - ayoff; + else + aysize = ttysize; + + if( axsize > 0 && aysize > 0 ) + { + MutexHolder oHolder( mutex ); + db->ReadBlock( echannel, ablock_x + ablock_y * src_blocks_per_row, + temp_buffer ); + + for( i_line = 0; i_line < aysize; i_line++ ) + { + memcpy( temp_buffer + + (i_line+ayoff) * src_block_width * pixel_size + + axoff * pixel_size, + ((uint8*) buffer) + i_line * block_width * pixel_size + + block1_xsize * pixel_size, + axsize * pixel_size ); + } + + db->WriteBlock( echannel, ablock_x + ablock_y * src_blocks_per_row, + temp_buffer ); + } + +/* -------------------------------------------------------------------- */ +/* read external block for bottom left corner of target block. */ +/* -------------------------------------------------------------------- */ + ttxoff = txoff; + ttyoff = tyoff + block1_ysize; + ttxsize = txsize; + ttysize = tysize - block1_ysize; + + ablock_x = ttxoff / src_block_width; + ablock_y = ttyoff / src_block_height; + + axoff = ttxoff - ablock_x * src_block_width; + ayoff = ttyoff - ablock_y * src_block_height; + + if( axoff + ttxsize > src_block_width ) + axsize = src_block_width - axoff; + else + axsize = ttxsize; + + if( ayoff + ttysize > src_block_height ) + aysize = src_block_height - ayoff; + else + aysize = ttysize; + + if( axsize > 0 && aysize > 0 ) + { + MutexHolder oHolder( mutex ); + db->ReadBlock( echannel, ablock_x + ablock_y * src_blocks_per_row, + temp_buffer ); + + for( i_line = 0; i_line < aysize; i_line++ ) + { + memcpy( temp_buffer + + (i_line+ayoff) * src_block_width * pixel_size + + axoff * pixel_size, + ((uint8*) buffer) + + (i_line+block1_ysize) * block_width * pixel_size, + axsize * pixel_size ); + } + + db->WriteBlock( echannel, ablock_x + ablock_y * src_blocks_per_row, + temp_buffer ); + } + +/* -------------------------------------------------------------------- */ +/* read external block for bottom left corner of target block. */ +/* -------------------------------------------------------------------- */ + ttxoff = txoff + block1_xsize; + ttyoff = tyoff + block1_ysize; + ttxsize = txsize - block1_xsize; + ttysize = tysize - block1_ysize; + + ablock_x = ttxoff / src_block_width; + ablock_y = ttyoff / src_block_height; + + axoff = ttxoff - ablock_x * src_block_width; + ayoff = ttyoff - ablock_y * src_block_height; + + if( axoff + ttxsize > src_block_width ) + axsize = src_block_width - axoff; + else + axsize = ttxsize; + + if( ayoff + ttysize > src_block_height ) + aysize = src_block_height - ayoff; + else + aysize = ttysize; + + if( axsize > 0 && aysize > 0 ) + { + MutexHolder oHolder( mutex ); + db->ReadBlock( echannel, ablock_x + ablock_y * src_blocks_per_row, + temp_buffer ); + + for( i_line = 0; i_line < aysize; i_line++ ) + { + memcpy( temp_buffer + + (i_line+ayoff) * src_block_width * pixel_size + + axoff * pixel_size, + ((uint8*) buffer) + + (i_line+block1_ysize) * block_width * pixel_size + + block1_xsize * pixel_size, + axsize * pixel_size ); + } + + db->WriteBlock( echannel, ablock_x + ablock_y * src_blocks_per_row, + temp_buffer ); + } + + free( temp_buffer ); + + return 1; +} + +/************************************************************************/ +/* GetEChanInfo() */ +/************************************************************************/ +void CExternalChannel::GetEChanInfo( std::string &filename, int &echannel, + int &exoff, int &eyoff, + int &exsize, int &eysize ) const + +{ + echannel = this->echannel; + exoff = this->exoff; + eyoff = this->eyoff; + exsize = this->exsize; + eysize = this->eysize; + filename = this->filename; +} + +/************************************************************************/ +/* SetEChanInfo() */ +/************************************************************************/ + +void CExternalChannel::SetEChanInfo( std::string filename, int echannel, + int exoff, int eyoff, + int exsize, int eysize ) + +{ + if( ih_offset == 0 ) + ThrowPCIDSKException( "No Image Header available for this channel." ); + +/* -------------------------------------------------------------------- */ +/* Fetch the existing image header. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer ih(1024); + + file->ReadFromFile( ih.buffer, ih_offset, 1024 ); + +/* -------------------------------------------------------------------- */ +/* If the linked filename is too long to fit in the 64 */ +/* character IHi.2 field, then we need to use a link segment to */ +/* store the filename. */ +/* -------------------------------------------------------------------- */ + std::string IHi2_filename; + + if( filename.size() > 64 ) + { + int link_segment; + + ih.Get( 64, 64, IHi2_filename ); + + if( IHi2_filename.substr(0,3) == "LNK" ) + { + link_segment = std::atoi( IHi2_filename.c_str() + 4 ); + } + else + { + char link_filename[64]; + + link_segment = + file->CreateSegment( "Link ", + "Long external channel filename link.", + SEG_SYS, 1 ); + + sprintf( link_filename, "LNK %4d", link_segment ); + IHi2_filename = link_filename; + } + + CLinkSegment *link = + dynamic_cast( file->GetSegment( link_segment ) ); + + if( link != NULL ) + { + link->SetPath( filename ); + link->Synchronize(); + } + } + +/* -------------------------------------------------------------------- */ +/* If we used to have a link segment but no longer need it, we */ +/* need to delete the link segment. */ +/* -------------------------------------------------------------------- */ + else + { + ih.Get( 64, 64, IHi2_filename ); + + if( IHi2_filename.substr(0,3) == "LNK" ) + { + int link_segment = std::atoi( IHi2_filename.c_str() + 4 ); + + file->DeleteSegment( link_segment ); + } + + IHi2_filename = filename; + } + +/* -------------------------------------------------------------------- */ +/* Update the image header. */ +/* -------------------------------------------------------------------- */ + // IHi.2 + ih.Put( IHi2_filename.c_str(), 64, 64 ); + + // IHi.6.1 + ih.Put( "", 168, 16 ); + + // IHi.6.2 + ih.Put( "", 184, 8 ); + + // IHi.6.3 + ih.Put( "", 192, 8 ); + + // IHi.6.5 + ih.Put( "", 201, 1 ); + + // IHi.6.7 + ih.Put( exoff, 250, 8 ); + + // IHi.6.8 + ih.Put( eyoff, 258, 8 ); + + // IHi.6.9 + ih.Put( exsize, 266, 8 ); + + // IHi.6.10 + ih.Put( eysize, 274, 8 ); + + // IHi.6.11 + ih.Put( echannel, 282, 8 ); + + file->WriteToFile( ih.buffer, ih_offset, 1024 ); + +/* -------------------------------------------------------------------- */ +/* Update local configuration. */ +/* -------------------------------------------------------------------- */ + this->filename = MergeRelativePath( file->GetInterfaces()->io, + file->GetFilename(), + filename ); + + this->exoff = exoff; + this->eyoff = eyoff; + this->exsize = exsize; + this->eysize = eysize; + this->echannel = echannel; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cexternalchannel.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cexternalchannel.h new file mode 100644 index 000000000..00ab9e64f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cexternalchannel.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CExternalChannel class. + * + * This class is used to implement band interleaved channels that are + * references to an external image database that is not just a raw file. + * It uses the application supplied EDB interface to access non-PCIDSK files. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __INCLUDE_CHANNEL_CEXTERNALCHANNEL_H +#define __INCLUDE_CHANNEL_CEXTERNALCHANNEL_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_buffer.h" +#include "channel/cpcidskchannel.h" +#include + +namespace PCIDSK +{ + class CPCIDSKFile; + +/************************************************************************/ +/* CExternalChannel */ +/************************************************************************/ + + class CExternalChannel : public CPCIDSKChannel + { + public: + CExternalChannel( PCIDSKBuffer &image_header, + uint64 ih_offset, + PCIDSKBuffer &file_header, + std::string filename, + int channelnum, + CPCIDSKFile *file, + eChanType pixel_type ); + virtual ~CExternalChannel(); + + virtual int GetBlockWidth() const; + virtual int GetBlockHeight() const; + virtual int ReadBlock( int block_index, void *buffer, + int xoff=-1, int yoff=-1, + int xsize=-1, int ysize=-1 ); + virtual int WriteBlock( int block_index, void *buffer ); + + virtual void GetEChanInfo( std::string &filename, int &echannel, + int &exoff, int &eyoff, + int &exsize, int &eysize ) const; + virtual void SetEChanInfo( std::string filename, int echannel, + int exoff, int eyoff, + int exsize, int eysize ); + private: + int exoff; + int eyoff; + int exsize; + int eysize; + + int echannel; + + mutable int blocks_per_row; + + mutable EDBFile *db; + mutable Mutex *mutex; + mutable bool writable; + + void AccessDB() const; + + mutable std::string filename; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_CHANNEL_CEXTERNALCHANNEL_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cpcidskchannel.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cpcidskchannel.cpp new file mode 100644 index 000000000..5ffb2fea6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cpcidskchannel.cpp @@ -0,0 +1,522 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSKChannel Abstract class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "core/pcidsk_utils.h" +#include "pcidsk_exception.h" +#include "pcidsk_channel.h" +#include "core/cpcidskfile.h" +#include "channel/cpcidskchannel.h" +#include "channel/ctiledchannel.h" +#include +#include +#include +#include +#include + +#include "cpl_port.h" + +using namespace PCIDSK; + +/************************************************************************/ +/* CPCIDSKChannel() */ +/************************************************************************/ + +CPCIDSKChannel::CPCIDSKChannel( PCIDSKBuffer &image_header, + uint64 ih_offset, + CPCIDSKFile *file, + eChanType pixel_type, + int channel_number ) + +{ + this->pixel_type = pixel_type; + this->file = file; + this->channel_number = channel_number; + this->ih_offset = ih_offset; + + width = file->GetWidth(); + height = file->GetHeight(); + + block_width = width; + block_height = 1; + +/* -------------------------------------------------------------------- */ +/* Establish if we need to byte swap the data on load/store. */ +/* -------------------------------------------------------------------- */ + if( channel_number != -1 ) + { + unsigned short test_value = 1; + + byte_order = image_header.buffer[201]; + if( ((uint8 *) &test_value)[0] == 1 ) + needs_swap = (byte_order != 'S'); + else + needs_swap = (byte_order == 'S'); + + if( pixel_type == CHN_8U ) + needs_swap = 0; + + LoadHistory( image_header ); + +/* -------------------------------------------------------------------- */ +/* Initialize the metadata object, but do not try to load till */ +/* needed. We avoid doing this for unassociated channels such */ +/* as overviews. */ +/* -------------------------------------------------------------------- */ + metadata.Initialize( file, "IMG", channel_number ); + } + +/* -------------------------------------------------------------------- */ +/* No overviews for unassociated files, so just mark them as */ +/* initialized. */ +/* -------------------------------------------------------------------- */ + overviews_initialized = (channel_number == -1); +} + +/************************************************************************/ +/* ~CPCIDSKChannel() */ +/************************************************************************/ + +CPCIDSKChannel::~CPCIDSKChannel() + +{ + InvalidateOverviewInfo(); +} + +/************************************************************************/ +/* InvalidateOverviewInfo() */ +/* */ +/* This is called when CreateOverviews() creates overviews - we */ +/* invalidate our loaded info and re-establish on a next request. */ +/************************************************************************/ + +void CPCIDSKChannel::InvalidateOverviewInfo() + +{ + for( size_t io=0; io < overview_bands.size(); io++ ) + { + if( overview_bands[io] != NULL ) + { + delete overview_bands[io]; + overview_bands[io] = NULL; + } + } + + overview_infos.clear(); + overview_bands.clear(); + overview_decimations.clear(); + + overviews_initialized = false; +} + +/************************************************************************/ +/* EstablishOverviewInfo() */ +/************************************************************************/ +void CPCIDSKChannel::EstablishOverviewInfo() const + +{ + if( overviews_initialized ) + return; + + overviews_initialized = true; + + std::vector keys = GetMetadataKeys(); + size_t i; + + for( i = 0; i < keys.size(); i++ ) + { + if( strncmp(keys[i].c_str(),"_Overview_",10) != 0 ) + continue; + + std::string value = GetMetadataValue( keys[i] ); + + overview_infos.push_back( value ); + overview_bands.push_back( NULL ); + overview_decimations.push_back( atoi(keys[i].c_str()+10) ); + } +} + +/************************************************************************/ +/* GetBlockCount() */ +/************************************************************************/ + +int CPCIDSKChannel::GetBlockCount() const + +{ + // We deliberately call GetBlockWidth() and GetWidth() to trigger + // computation of the values for tiled layers. At some point it would + // be good to cache the block count as this computation is a bit expensive + + int x_block_count = (GetWidth() + GetBlockWidth() - 1) / GetBlockWidth(); + int y_block_count = (GetHeight() + GetBlockHeight() - 1) / GetBlockHeight(); + + return x_block_count * y_block_count; +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int CPCIDSKChannel::GetOverviewCount() + +{ + EstablishOverviewInfo(); + + return overview_infos.size(); +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +PCIDSKChannel *CPCIDSKChannel::GetOverview( int overview_index ) + +{ + EstablishOverviewInfo(); + + if( overview_index < 0 || overview_index >= (int) overview_infos.size() ) + ThrowPCIDSKException( "Non existant overview (%d) requested.", + overview_index ); + + if( overview_bands[overview_index] == NULL ) + { + PCIDSKBuffer image_header(1024), file_header(1024); + char pseudo_filename[65]; + + sprintf( pseudo_filename, "/SIS=%d", + atoi(overview_infos[overview_index].c_str()) ); + + image_header.Put( pseudo_filename, 64, 64 ); + + overview_bands[overview_index] = + new CTiledChannel( image_header, 0, file_header, -1, file, + CHN_UNKNOWN ); + } + + return overview_bands[overview_index]; +} + +/************************************************************************/ +/* IsOverviewValid() */ +/************************************************************************/ + +bool CPCIDSKChannel::IsOverviewValid( int overview_index ) + +{ + EstablishOverviewInfo(); + + if( overview_index < 0 || overview_index >= (int) overview_infos.size() ) + ThrowPCIDSKException( "Non existant overview (%d) requested.", + overview_index ); + + int sis_id, validity=0; + + sscanf( overview_infos[overview_index].c_str(), "%d %d", + &sis_id, &validity ); + + return validity != 0; +} + +/************************************************************************/ +/* GetOverviewResampling() */ +/************************************************************************/ + +std::string CPCIDSKChannel::GetOverviewResampling( int overview_index ) + +{ + EstablishOverviewInfo(); + + if( overview_index < 0 || overview_index >= (int) overview_infos.size() ) + ThrowPCIDSKException( "Non existant overview (%d) requested.", + overview_index ); + + int sis_id, validity=0; + char resampling[17]; + + sscanf( overview_infos[overview_index].c_str(), "%d %d %16s", + &sis_id, &validity, &(resampling[0]) ); + + return resampling; +} + +/************************************************************************/ +/* SetOverviewValidity() */ +/************************************************************************/ + +void CPCIDSKChannel::SetOverviewValidity( int overview_index, + bool new_validity ) + +{ + EstablishOverviewInfo(); + + if( overview_index < 0 || overview_index >= (int) overview_infos.size() ) + ThrowPCIDSKException( "Non existant overview (%d) requested.", + overview_index ); + + int sis_id, validity=0; + char resampling[17]; + + sscanf( overview_infos[overview_index].c_str(), "%d %d %16s", + &sis_id, &validity, &(resampling[0]) ); + + // are we already set to this value? + if( new_validity == (validity != 0) ) + return; + + char new_info[48]; + + sprintf( new_info, "%d %d %s", + sis_id, (new_validity ? 1 : 0 ), resampling ); + + overview_infos[overview_index] = new_info; + + // write back to metadata. + char key[20]; + sprintf( key, "_Overview_%d", overview_decimations[overview_index] ); + + SetMetadataValue( key, new_info ); +} + +/************************************************************************/ +/* InvalidateOverviews() */ +/* */ +/* Whenever a write is done on this band, we will invalidate */ +/* any previously valid overviews. */ +/************************************************************************/ + +void CPCIDSKChannel::InvalidateOverviews() + +{ + EstablishOverviewInfo(); + + for( int i = 0; i < GetOverviewCount(); i++ ) + SetOverviewValidity( i, false ); +} + +/************************************************************************/ +/* GetOverviewLevelMapping() */ +/************************************************************************/ + +std::vector CPCIDSKChannel::GetOverviewLevelMapping() const +{ + EstablishOverviewInfo(); + + return overview_decimations; +} + +/************************************************************************/ +/* GetDescription() */ +/************************************************************************/ + +std::string CPCIDSKChannel::GetDescription() + +{ + if( ih_offset == 0 ) + return ""; + + PCIDSKBuffer ih_1(64); + std::string ret; + + file->ReadFromFile( ih_1.buffer, ih_offset, 64 ); + ih_1.Get(0,64,ret); + + return ret; +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +void CPCIDSKChannel::SetDescription( const std::string &description ) + +{ + if( ih_offset == 0 ) + ThrowPCIDSKException( "Description cannot be set on overviews." ); + + PCIDSKBuffer ih_1(64); + ih_1.Put( description.c_str(), 0, 64 ); + file->WriteToFile( ih_1.buffer, ih_offset, 64 ); +} + +/************************************************************************/ +/* LoadHistory() */ +/************************************************************************/ + +void CPCIDSKChannel::LoadHistory( const PCIDSKBuffer &image_header ) + +{ + // Read the history from the image header. PCIDSK supports + // 8 history entries per channel. + + std::string hist_msg; + history_.clear(); + for (unsigned int i = 0; i < 8; i++) + { + image_header.Get(384 + i * 80, 80, hist_msg); + + // Some programs seem to push history records with a trailing '\0' + // so do some extra processing to cleanup. FUN records on segment + // 3 of eltoro.pix are an example of this. + size_t size = hist_msg.size(); + while( size > 0 + && (hist_msg[size-1] == ' ' || hist_msg[size-1] == '\0') ) + size--; + + hist_msg.resize(size); + + history_.push_back(hist_msg); + } +} + +/************************************************************************/ +/* GetHistoryEntries() */ +/************************************************************************/ + +std::vector CPCIDSKChannel::GetHistoryEntries() const +{ + return history_; +} + +/************************************************************************/ +/* SetHistoryEntries() */ +/************************************************************************/ + +void CPCIDSKChannel::SetHistoryEntries(const std::vector &entries) + +{ + if( ih_offset == 0 ) + ThrowPCIDSKException( "Attempt to update history on a raster that is not\na conventional band with an image header." ); + + PCIDSKBuffer image_header(1024); + + file->ReadFromFile( image_header.buffer, ih_offset, 1024 ); + + for( unsigned int i = 0; i < 8; i++ ) + { + const char *msg = ""; + if( entries.size() > i ) + msg = entries[i].c_str(); + + image_header.Put( msg, 384 + i * 80, 80 ); + } + + file->WriteToFile( image_header.buffer, ih_offset, 1024 ); + + // Force reloading of history_ + LoadHistory( image_header ); +} + +/************************************************************************/ +/* PushHistory() */ +/************************************************************************/ + +void CPCIDSKChannel::PushHistory( const std::string &app, + const std::string &message ) + +{ +#define MY_MIN(a,b) ((a history_entries = GetHistoryEntries(); + + history_entries.insert( history_entries.begin(), history ); + history_entries.resize(8); + + SetHistoryEntries( history_entries ); +} + +/************************************************************************/ +/* GetChanInfo() */ +/************************************************************************/ +void CPCIDSKChannel::GetChanInfo( std::string &filename, uint64 &image_offset, + uint64 &pixel_offset, uint64 &line_offset, + bool &little_endian ) const + +{ + image_offset = 0; + pixel_offset = 0; + line_offset = 0; + little_endian = true; + filename = ""; +} + +/************************************************************************/ +/* SetChanInfo() */ +/************************************************************************/ + +void CPCIDSKChannel::SetChanInfo( CPL_UNUSED std::string filename, + CPL_UNUSED uint64 image_offset, + CPL_UNUSED uint64 pixel_offset, + CPL_UNUSED uint64 line_offset, + CPL_UNUSED bool little_endian ) +{ + ThrowPCIDSKException( "Attempt to SetChanInfo() on a channel that is not FILE interleaved." ); +} + +/************************************************************************/ +/* GetEChanInfo() */ +/************************************************************************/ +void CPCIDSKChannel::GetEChanInfo( std::string &filename, int &echannel, + int &exoff, int &eyoff, + int &exsize, int &eysize ) const + +{ + echannel = 0; + exoff = 0; + eyoff = 0; + exsize = 0; + eysize = 0; + filename = ""; +} + +/************************************************************************/ +/* SetEChanInfo() */ +/************************************************************************/ + +void CPCIDSKChannel::SetEChanInfo( CPL_UNUSED std::string filename, + CPL_UNUSED int echannel, + CPL_UNUSED int exoff, + CPL_UNUSED int eyoff, + CPL_UNUSED int exsize, + CPL_UNUSED int eysize ) +{ + ThrowPCIDSKException( "Attempt to SetEChanInfo() on a channel that is not FILE interleaved." ); +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cpcidskchannel.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cpcidskchannel.h new file mode 100644 index 000000000..308ca772b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cpcidskchannel.h @@ -0,0 +1,142 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CPCIDSKChannel Abstract class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_CHANNEL_CPCIDSKCHANNEL_H +#define __INCLUDE_CHANNEL_CPCIDSKCHANNEL_H + +#include "pcidsk_config.h" +#include "pcidsk_buffer.h" +#include "pcidsk_channel.h" +#include "core/metadataset.h" +#include "core/mutexholder.h" +#include +#include + +namespace PCIDSK +{ + class CPCIDSKFile; + class CTiledChannel; +/************************************************************************/ +/* CPCIDSKChannel */ +/* */ +/* Abstract class that helps implement some of the more mundane details */ +/* required for when implementing an imagery channel I/O strategy. If */ +/* you are using this to implement those details, use virtual */ +/* inheritance to attempt to avoid the fragile base class problem and */ +/* then implement the Imagery I/O functions. */ +/************************************************************************/ + class CPCIDSKChannel : public PCIDSKChannel + { + friend class PCIDSKFile; + + public: + CPCIDSKChannel( PCIDSKBuffer &image_header, uint64 ih_offset, + CPCIDSKFile *file, eChanType pixel_type, + int channel_number ); + virtual ~CPCIDSKChannel(); + + virtual int GetBlockWidth() const { return block_width; } + virtual int GetBlockHeight() const { return block_height; } + virtual int GetBlockCount() const; + + virtual int GetWidth() const { return width; } + virtual int GetHeight() const { return height; } + virtual eChanType GetType() const { return pixel_type; } + + int GetOverviewCount(); + PCIDSKChannel *GetOverview( int i ); + bool IsOverviewValid( int i ); + void SetOverviewValidity( int i, bool validity ); + std::string GetOverviewResampling( int i ); + std::vector GetOverviewLevelMapping() const; + + int GetChannelNumber() { return channel_number; } + + std::string GetMetadataValue( const std::string &key ) const + { return metadata.GetMetadataValue(key); } + void SetMetadataValue( const std::string &key, const std::string &value ) + { metadata.SetMetadataValue(key,value); } + std::vector GetMetadataKeys() const + { return metadata.GetMetadataKeys(); } + + virtual void Synchronize() {} + + std::string GetDescription(); + void SetDescription( const std::string &description ); + + virtual std::vector GetHistoryEntries() const; + virtual void SetHistoryEntries( const std::vector &entries ); + virtual void PushHistory(const std::string &app, + const std::string &message); + + virtual void GetChanInfo( std::string &filename, uint64 &image_offset, + uint64 &pixel_offset, uint64 &line_offset, + bool &little_endian ) const; + virtual void SetChanInfo( std::string filename, uint64 image_offset, + uint64 pixel_offset, uint64 line_offset, + bool little_endian ); + virtual void GetEChanInfo( std::string &filename, int &echannel, + int &exoff, int &eyoff, + int &exsize, int &eysize ) const; + virtual void SetEChanInfo( std::string filename, int echannel, + int exoff, int eyoff, + int exsize, int eysize ); + + // Just for CPCIDSKFile. + void InvalidateOverviewInfo(); + + protected: + CPCIDSKFile *file; + mutable MetadataSet metadata; + + void LoadHistory( const PCIDSKBuffer &image_header ); + std::vector history_; + + int channel_number; + uint64 ih_offset; + mutable eChanType pixel_type; + char byte_order; // 'S': littleendian, 'N': bigendian + mutable int needs_swap; + + // width/height, and block size. + mutable int width; + mutable int height; + mutable int block_width; + mutable int block_height; + + // info about overviews; + void EstablishOverviewInfo() const; + + mutable bool overviews_initialized; + mutable std::vector overview_infos; + mutable std::vector overview_bands; + mutable std::vector overview_decimations; + + void InvalidateOverviews(); + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_CHANNEL_CPCIDSKCHANNEL_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cpixelinterleavedchannel.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cpixelinterleavedchannel.cpp new file mode 100644 index 000000000..c1122e692 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cpixelinterleavedchannel.cpp @@ -0,0 +1,261 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPixelInterleavedChannel class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_exception.h" +#include "core/pcidsk_utils.h" +#include "core/cpcidskfile.h" +#include "channel/cpixelinterleavedchannel.h" +#include +#include + +#include "cpl_port.h" + +using namespace PCIDSK; + +/************************************************************************/ +/* CPixelInterleavedChannel() */ +/************************************************************************/ + +CPixelInterleavedChannel::CPixelInterleavedChannel( PCIDSKBuffer &image_header, + uint64 ih_offset, + CPL_UNUSED PCIDSKBuffer &file_header, + int channelnum, + CPCIDSKFile *file, + int image_offset, + eChanType pixel_type ) + : CPCIDSKChannel( image_header, ih_offset, file, pixel_type, channelnum) + +{ + this->image_offset = image_offset; +} + +/************************************************************************/ +/* ~CPixelInterleavedChannel() */ +/************************************************************************/ + +CPixelInterleavedChannel::~CPixelInterleavedChannel() + +{ +} + +/************************************************************************/ +/* ReadBlock() */ +/************************************************************************/ + +int CPixelInterleavedChannel::ReadBlock( int block_index, void *buffer, + int win_xoff, int win_yoff, + int win_xsize, int win_ysize ) + +{ +/* -------------------------------------------------------------------- */ +/* Default window if needed. */ +/* -------------------------------------------------------------------- */ + if( win_xoff == -1 && win_yoff == -1 && win_xsize == -1 && win_ysize == -1 ) + { + win_xoff = 0; + win_yoff = 0; + win_xsize = GetBlockWidth(); + win_ysize = GetBlockHeight(); + } + +/* -------------------------------------------------------------------- */ +/* Validate Window */ +/* -------------------------------------------------------------------- */ + if( win_xoff < 0 || win_xoff + win_xsize > GetBlockWidth() + || win_yoff < 0 || win_yoff + win_ysize > GetBlockHeight() ) + { + ThrowPCIDSKException( + "Invalid window in ReadBloc(): win_xoff=%d,win_yoff=%d,xsize=%d,ysize=%d", + win_xoff, win_yoff, win_xsize, win_ysize ); + } + +/* -------------------------------------------------------------------- */ +/* Work out sizes and offsets. */ +/* -------------------------------------------------------------------- */ + int pixel_group = file->GetPixelGroupSize(); + int pixel_size = DataTypeSize(GetType()); + +/* -------------------------------------------------------------------- */ +/* Read and lock the scanline. */ +/* -------------------------------------------------------------------- */ + uint8 *pixel_buffer = (uint8 *) + file->ReadAndLockBlock( block_index, win_xoff, win_xsize); + +/* -------------------------------------------------------------------- */ +/* Copy the data into our callers buffer. Try to do this */ +/* reasonably efficiently. We might consider adding faster */ +/* cases for 16/32bit data that is word aligned. */ +/* -------------------------------------------------------------------- */ + if( pixel_size == pixel_group ) + memcpy( buffer, pixel_buffer, pixel_size * win_xsize ); + else + { + int i; + uint8 *src = ((uint8 *)pixel_buffer) + image_offset; + uint8 *dst = (uint8 *) buffer; + + if( pixel_size == 1 ) + { + for( i = win_xsize; i != 0; i-- ) + { + *dst = *src; + dst++; + src += pixel_group; + } + } + else if( pixel_size == 2 ) + { + for( i = win_xsize; i != 0; i-- ) + { + *(dst++) = *(src++); + *(dst++) = *(src++); + src += pixel_group-2; + } + } + else if( pixel_size == 4 ) + { + for( i = win_xsize; i != 0; i-- ) + { + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + src += pixel_group-4; + } + } + else + ThrowPCIDSKException( "Unsupported pixel type..." ); + } + + file->UnlockBlock( 0 ); + +/* -------------------------------------------------------------------- */ +/* Do byte swapping if needed. */ +/* -------------------------------------------------------------------- */ + if( needs_swap ) + SwapPixels( buffer, pixel_type, win_xsize ); + + return 1; +} + +template +void CopyPixels(const T* const src, T* const dst, + std::size_t offset, std::size_t count) +{ + for (std::size_t i = 0; i < count; i++) + { + dst[i] = src[(i + 1) * offset]; + } +} + +/************************************************************************/ +/* WriteBlock() */ +/************************************************************************/ + +int CPixelInterleavedChannel::WriteBlock( int block_index, void *buffer ) + +{ + if( !file->GetUpdatable() ) + throw PCIDSKException( "File not open for update in WriteBlock()" ); + + InvalidateOverviews(); + +/* -------------------------------------------------------------------- */ +/* Work out sizes and offsets. */ +/* -------------------------------------------------------------------- */ + int pixel_group = file->GetPixelGroupSize(); + int pixel_size = DataTypeSize(GetType()); + +/* -------------------------------------------------------------------- */ +/* Read and lock the scanline. */ +/* -------------------------------------------------------------------- */ + uint8 *pixel_buffer = (uint8 *) file->ReadAndLockBlock( block_index ); + +/* -------------------------------------------------------------------- */ +/* Copy the data into our callers buffer. Try to do this */ +/* reasonably efficiently. We might consider adding faster */ +/* cases for 16/32bit data that is word aligned. */ +/* -------------------------------------------------------------------- */ + // TODO: fixup for the complex case + if( pixel_size == pixel_group ) + memcpy( pixel_buffer, buffer, pixel_size * width ); + else + { + int i; + uint8 *dst = ((uint8 *)pixel_buffer) + image_offset; + uint8 *src = (uint8 *) buffer; + + if( pixel_size == 1 ) + { + for( i = width; i != 0; i-- ) + { + *dst = *src; + src++; + dst += pixel_group; + } + } + else if( pixel_size == 2 ) + { + for( i = width; i != 0; i-- ) + { + *(dst++) = *(src++); + *(dst++) = *(src++); + + if( needs_swap ) + SwapData( dst-2, 2, 1 ); + + dst += pixel_group-2; + } + } + else if( pixel_size == 4 ) + { + for( i = width; i != 0; i-- ) + { + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + + if( needs_swap ) + SwapData( dst-4, 4, 1); + + dst += pixel_group-4; + + } + } + else + ThrowPCIDSKException( "Unsupported pixel type..." ); + } + + file->UnlockBlock( 1 ); + +/* -------------------------------------------------------------------- */ +/* Do byte swapping if needed. */ +/* -------------------------------------------------------------------- */ + + return 1; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cpixelinterleavedchannel.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cpixelinterleavedchannel.h new file mode 100644 index 000000000..1592111bc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/cpixelinterleavedchannel.h @@ -0,0 +1,66 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CPixelInterleavedChannel class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_CHANNEL_CPIXELINTERLEAVEDCHANNEL_H +#define __INCLUDE_CHANNEL_CPIXELINTERLEAVEDCHANNEL_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_buffer.h" +#include "channel/cpcidskchannel.h" + +namespace PCIDSK +{ + class CPCIDSKFile; + +/************************************************************************/ +/* CPixelInterleavedChannel */ +/************************************************************************/ + class CPixelInterleavedChannel : public CPCIDSKChannel + { + + + public: + CPixelInterleavedChannel( PCIDSKBuffer &image_header, + uint64 ih_offset, + PCIDSKBuffer &file_header, + int channelnum, + CPCIDSKFile *file, + int image_offset, + eChanType pixel_type ); + virtual ~CPixelInterleavedChannel(); + + virtual int ReadBlock( int block_index, void *buffer, + int xoff=-1, int yoff=-1, + int xsize=-1, int ysize=-1 ); + + virtual int WriteBlock( int block_index, void *buffer ); + private: + int image_offset; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_CHANNEL_CPIXELINTERLEAVEDCHANNEL_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/ctiledchannel.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/ctiledchannel.cpp new file mode 100644 index 000000000..f4996d8cd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/ctiledchannel.cpp @@ -0,0 +1,926 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CTiledChannel class. + * + * This class is used to implement band interleaved channels within a + * PCIDSK file (which are always packed, and FILE interleaved data from + * external raw files which may not be packed. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_exception.h" +#include "channel/ctiledchannel.h" +#include "segment/sysblockmap.h" +#include "core/sysvirtualfile.h" +#include "core/cpcidskfile.h" +#include "core/pcidsk_utils.h" +#include +#include +#include + +#include "cpl_port.h" + +using namespace PCIDSK; + +/************************************************************************/ +/* CTiledChannel() */ +/************************************************************************/ + +CTiledChannel::CTiledChannel( PCIDSKBuffer &image_header, + uint64 ih_offset, + CPL_UNUSED PCIDSKBuffer &file_header, + int channelnum, + CPCIDSKFile *file, + eChanType pixel_type ) + : CPCIDSKChannel( image_header, ih_offset, file, pixel_type, channelnum) +{ +/* -------------------------------------------------------------------- */ +/* Establish the virtual file we will be accessing. */ +/* -------------------------------------------------------------------- */ + std::string filename; + + image_header.Get(64,64,filename); + + assert( strstr(filename.c_str(),"SIS=") != NULL ); + + image = atoi(strstr(filename.c_str(),"SIS=") + 4); + + vfile = NULL; + +/* -------------------------------------------------------------------- */ +/* If this is an unassociated channel (ie. an overview), we */ +/* will set the size and blocksize values to something */ +/* unreasonable and set them properly in EstablishAccess() */ +/* -------------------------------------------------------------------- */ + if( channelnum == -1 ) + { + width = -1; + height = -1; + block_width = -1; + block_height = -1; + } +} + +/************************************************************************/ +/* ~CTiledChannel() */ +/************************************************************************/ + +CTiledChannel::~CTiledChannel() + +{ + Synchronize(); +} + +/************************************************************************/ +/* EstablishAccess() */ +/************************************************************************/ + +void CTiledChannel::EstablishAccess() const + +{ + if( vfile != NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Establish the virtual file to access this image. */ +/* -------------------------------------------------------------------- */ + SysBlockMap *bmap = dynamic_cast( + file->GetSegment( SEG_SYS, "SysBMDir" )); + + if( bmap == NULL ) + ThrowPCIDSKException( "Unable to find SysBMDir segment." ); + + vfile = bmap->GetVirtualFile( image ); + +/* -------------------------------------------------------------------- */ +/* Parse the header. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer theader(128); + std::string data_type; + + vfile->ReadFromFile( theader.buffer, 0, 128 ); + + width = theader.GetInt(0,8); + height = theader.GetInt(8,8); + block_width = theader.GetInt(16,8); + block_height = theader.GetInt(24,8); + + theader.Get(32,4,data_type); + theader.Get(54, 8, compression); + + pixel_type = GetDataTypeFromName(data_type); + if (pixel_type == CHN_UNKNOWN) + { + ThrowPCIDSKException( "Unknown channel type: %s", + data_type.c_str() ); + } + +/* -------------------------------------------------------------------- */ +/* Compute information on the tiles. */ +/* -------------------------------------------------------------------- */ + tiles_per_row = (width + block_width - 1) / block_width; + tiles_per_col = (height + block_height - 1) / block_height; + tile_count = tiles_per_row * tiles_per_col; + +/* -------------------------------------------------------------------- */ +/* Resize our tile info cache. */ +/* -------------------------------------------------------------------- */ + int tile_block_info_count = + (tile_count + tile_block_size - 1) / tile_block_size; + + tile_offsets.resize( tile_block_info_count ); + tile_sizes.resize( tile_block_info_count ); + tile_info_dirty.resize( tile_block_info_count, false ); + +/* -------------------------------------------------------------------- */ +/* Establish byte swapping. Tiled data files are always big */ +/* endian, regardless of what the headers might imply. */ +/* -------------------------------------------------------------------- */ + unsigned short test_value = 1; + + if( ((uint8 *) &test_value)[0] == 1 ) + needs_swap = pixel_type != CHN_8U; + else + needs_swap = false; +} + +/************************************************************************/ +/* LoadTileInfoBlock() */ +/************************************************************************/ + +void CTiledChannel::LoadTileInfoBlock( int block ) + +{ + assert( tile_offsets[block].size() == 0 ); + +/* -------------------------------------------------------------------- */ +/* How many tiles in this block? */ +/* -------------------------------------------------------------------- */ + int tiles_in_block = tile_block_size; + + if( block * tile_block_size + tiles_in_block > tile_count ) + tiles_in_block = tile_count - block * tile_block_size; + +/* -------------------------------------------------------------------- */ +/* Resize the vectors for this block. */ +/* -------------------------------------------------------------------- */ + tile_offsets[block].resize( tiles_in_block ); + tile_sizes[block].resize( tiles_in_block ); + +/* -------------------------------------------------------------------- */ +/* Read the offset and size data from disk. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer offset_map( tiles_in_block * 12 + 1 ); + PCIDSKBuffer size_map( tiles_in_block * 8 + 1 ); + + vfile->ReadFromFile( offset_map.buffer, + 128 + block * tile_block_size * 12, + tiles_in_block * 12 ); + vfile->ReadFromFile( size_map.buffer, + 128 + tile_count * 12 + block * tile_block_size * 8, + tiles_in_block * 8 ); + + for( int i = 0; i < tiles_in_block; i++ ) + { + char chSaved; + char *target = offset_map.buffer + i*12; + + chSaved = target[12]; + target[12] = '\0'; + tile_offsets[block][i] = atouint64(target); + target[12] = chSaved; + + target = size_map.buffer + i*8; + chSaved = target[8]; + target[8] = '\0'; + tile_sizes[block][i] = atoi(target); + target[8] = chSaved; + } +} + +/************************************************************************/ +/* SaveTileInfoBlock() */ +/************************************************************************/ + +void CTiledChannel::SaveTileInfoBlock( int block ) + +{ + assert( tile_offsets[block].size() != 0 ); + int tiles_in_block = tile_offsets[block].size(); + +/* -------------------------------------------------------------------- */ +/* Write the offset and size data to disk. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer offset_map( tiles_in_block * 12 + 1 ); + PCIDSKBuffer size_map( tiles_in_block * 8 + 1 ); + + for( int i = 0; i < tiles_in_block; i++ ) + { + if( tile_offsets[block][i] == (uint64) -1 + || tile_offsets[block][i] == 0 ) + offset_map.Put( -1, i*12, 12 ); + else + offset_map.Put( tile_offsets[block][i], i*12, 12 ); + + size_map.Put( tile_sizes[block][i], i*8, 8 ); + } + + vfile->WriteToFile( offset_map.buffer, + 128 + block * tile_block_size * 12, + tiles_in_block * 12 ); + vfile->WriteToFile( size_map.buffer, + 128 + tile_count * 12 + block * tile_block_size * 8, + tiles_in_block * 8 ); + + tile_info_dirty[block] = false; +} + +/************************************************************************/ +/* GetTileInfo() */ +/* */ +/* Fetch the tile offset and size for the indicated tile. */ +/************************************************************************/ + +void CTiledChannel::GetTileInfo( int tile_index, uint64 &offset, int &size ) + +{ + int block = tile_index / tile_block_size; + int index_within_block = tile_index - block * tile_block_size; + + if( tile_offsets[block].size() == 0 ) + LoadTileInfoBlock( block ); + + offset = tile_offsets[block][index_within_block]; + size = tile_sizes[block][index_within_block]; +} + +/************************************************************************/ +/* SetTileInfo() */ +/************************************************************************/ + +void CTiledChannel::SetTileInfo( int tile_index, uint64 offset, int size ) + +{ + int block = tile_index / tile_block_size; + int index_within_block = tile_index - block * tile_block_size; + + if( tile_offsets[block].size() == 0 ) + LoadTileInfoBlock( block ); + + if( offset != tile_offsets[block][index_within_block] + || size != tile_sizes[block][index_within_block] ) + { + tile_offsets[block][index_within_block] = offset; + tile_sizes[block][index_within_block] = size; + + tile_info_dirty[block] = true; + } +} + +/************************************************************************/ +/* Synchronize() */ +/* */ +/* Flush updated blockmap to disk if it is dirty. */ +/************************************************************************/ + +void CTiledChannel::Synchronize() + +{ + if( tile_info_dirty.size() == 0 ) + return; + + int i; + + for( i = 0; i < (int) tile_info_dirty.size(); i++ ) + { + if( tile_info_dirty[i] ) + SaveTileInfoBlock( i ); + } + + vfile->Synchronize(); +} + +/************************************************************************/ +/* ReadBlock() */ +/************************************************************************/ + +int CTiledChannel::ReadBlock( int block_index, void *buffer, + int xoff, int yoff, + int xsize, int ysize ) + +{ + int pixel_size = DataTypeSize(GetType()); + +/* -------------------------------------------------------------------- */ +/* Default window if needed. */ +/* -------------------------------------------------------------------- */ + if( xoff == -1 && yoff == -1 && xsize == -1 && ysize == -1 ) + { + xoff = 0; + yoff = 0; + xsize = GetBlockWidth(); + ysize = GetBlockHeight(); + } + +/* -------------------------------------------------------------------- */ +/* Validate Window */ +/* -------------------------------------------------------------------- */ + if( xoff < 0 || xoff + xsize > GetBlockWidth() + || yoff < 0 || yoff + ysize > GetBlockHeight() ) + { + ThrowPCIDSKException( + "Invalid window in ReadBloc(): xoff=%d,yoff=%d,xsize=%d,ysize=%d", + xoff, yoff, xsize, ysize ); + } + + if( block_index < 0 || block_index >= tile_count ) + { + ThrowPCIDSKException( "Requested non-existant block (%d)", + block_index ); + } + +/* -------------------------------------------------------------------- */ +/* Does this tile exist? If not return a zeroed buffer. */ +/* -------------------------------------------------------------------- */ + uint64 tile_offset; + int tile_size; + + GetTileInfo( block_index, tile_offset, tile_size ); + + if( tile_size == 0 ) + { + memset( buffer, 0, GetBlockWidth() * GetBlockHeight() * pixel_size ); + return 1; + } + +/* -------------------------------------------------------------------- */ +/* The simpliest case it an uncompressed direct and complete */ +/* tile read into the destination buffer. */ +/* -------------------------------------------------------------------- */ + if( xoff == 0 && xsize == GetBlockWidth() + && yoff == 0 && ysize == GetBlockHeight() + && tile_size == xsize * ysize * pixel_size + && compression == "NONE" ) + { + vfile->ReadFromFile( buffer, tile_offset, tile_size ); + + // Do byte swapping if needed. + if( needs_swap ) + SwapPixels( buffer, pixel_type, xsize * ysize ); + + return 1; + } + +/* -------------------------------------------------------------------- */ +/* Load uncompressed data, one scanline at a time, into the */ +/* target buffer. */ +/* -------------------------------------------------------------------- */ + if( compression == "NONE" ) + { + int iy; + + for( iy = 0; iy < ysize; iy++ ) + { + vfile->ReadFromFile( ((uint8 *) buffer) + + iy * xsize * pixel_size, + tile_offset + + ((iy+yoff)*block_width + xoff) * pixel_size, + xsize * pixel_size ); + } + + // Do byte swapping if needed. + if( needs_swap ) + SwapPixels( buffer, pixel_type, xsize * ysize ); + + return 1; + } + +/* -------------------------------------------------------------------- */ +/* Load the whole compressed data into a working buffer. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer oCompressedData( tile_size ); + PCIDSKBuffer oUncompressedData( pixel_size * block_width * block_height ); + + vfile->ReadFromFile( oCompressedData.buffer, tile_offset, tile_size ); + +/* -------------------------------------------------------------------- */ +/* Handle decompression. */ +/* -------------------------------------------------------------------- */ + if( compression == "RLE" ) + { + RLEDecompressBlock( oCompressedData, oUncompressedData ); + } + else if( strncmp(compression.c_str(),"JPEG",4) == 0 ) + { + JPEGDecompressBlock( oCompressedData, oUncompressedData ); + } + else + { + ThrowPCIDSKException( + "Unable to read tile of unsupported compression type: %s", + compression.c_str() ); + } + +/* -------------------------------------------------------------------- */ +/* Swap if necessary. TODO: there is some reason to doubt that */ +/* the old implementation properly byte swapped compressed */ +/* data. Perhaps this should be conditional? */ +/* -------------------------------------------------------------------- */ + if( needs_swap ) + SwapPixels( oUncompressedData.buffer, pixel_type, + GetBlockWidth() * GetBlockHeight() ); + +/* -------------------------------------------------------------------- */ +/* Copy out the desired subwindow. */ +/* -------------------------------------------------------------------- */ + int iy; + + for( iy = 0; iy < ysize; iy++ ) + { + memcpy( ((uint8 *) buffer) + iy * xsize * pixel_size, + oUncompressedData.buffer + + ((iy+yoff)*block_width + xoff) * pixel_size, + xsize * pixel_size ); + } + + return 1; +} + +/************************************************************************/ +/* IsTileEmpty() */ +/************************************************************************/ +bool CTiledChannel::IsTileEmpty(void *buffer) const +{ + assert(sizeof(int32) == 4); // just to be on the safe side... + + unsigned int num_dword = + (block_width * block_height * DataTypeSize(pixel_type)) / 4; + unsigned int rem = + (block_width * block_height * DataTypeSize(pixel_type)) % 4; + + int32* int_buf = reinterpret_cast(buffer); + + if (num_dword > 0) { + for (unsigned int n = 0; n < num_dword; n++) { + if (int_buf[n]) return false; + } + } + + char* char_buf = reinterpret_cast(int_buf + num_dword); + if (rem > 0) { + for (unsigned int n = 0; n < rem; n++) { + if (char_buf[n]) return false; + } + } + + return true; +} + +/************************************************************************/ +/* WriteBlock() */ +/************************************************************************/ + +int CTiledChannel::WriteBlock( int block_index, void *buffer ) + +{ + if( !file->GetUpdatable() ) + throw PCIDSKException( "File not open for update in WriteBlock()" ); + + InvalidateOverviews(); + + int pixel_size = DataTypeSize(GetType()); + int pixel_count = GetBlockWidth() * GetBlockHeight(); + + if( block_index < 0 || block_index >= tile_count ) + { + ThrowPCIDSKException( "Requested non-existant block (%d)", + block_index ); + } + +/* -------------------------------------------------------------------- */ +/* Fetch existing tile offset and size. */ +/* -------------------------------------------------------------------- */ + uint64 tile_offset; + int tile_size; + + GetTileInfo( block_index, tile_offset, tile_size ); + +/* -------------------------------------------------------------------- */ +/* The simpliest case it an uncompressed direct and complete */ +/* tile read into the destination buffer. */ +/* -------------------------------------------------------------------- */ + if( compression == "NONE" + && tile_size == pixel_count * pixel_size ) + { + // Do byte swapping if needed. + if( needs_swap ) + SwapPixels( buffer, pixel_type, pixel_count ); + + vfile->WriteToFile( buffer, tile_offset, tile_size ); + + if( needs_swap ) + SwapPixels( buffer, pixel_type, pixel_count ); + + return 1; + } + + if ((int64)tile_offset == -1) + { + // Check if the tile is empty. If it is, we can skip writing it, + // unless the tile is already dirty. + bool is_empty = IsTileEmpty(buffer); + + if (is_empty) return 1; // we don't need to do anything else + } + +/* -------------------------------------------------------------------- */ +/* Copy the uncompressed data into a PCIDSKBuffer, and byte */ +/* swap if needed. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer oUncompressedData( pixel_size * block_width * block_height ); + + memcpy( oUncompressedData.buffer, buffer, + oUncompressedData.buffer_size ); + + if( needs_swap ) + SwapPixels( oUncompressedData.buffer, pixel_type, pixel_count ); + +/* -------------------------------------------------------------------- */ +/* Compress the imagery. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer oCompressedData; + + if( compression == "NONE" ) + { + oCompressedData = oUncompressedData; + } + else if( compression == "RLE" ) + { + RLECompressBlock( oUncompressedData, oCompressedData ); + } + else if( strncmp(compression.c_str(),"JPEG",4) == 0 ) + { + JPEGCompressBlock( oUncompressedData, oCompressedData ); + } + else + { + ThrowPCIDSKException( + "Unable to write tile of unsupported compression type: %s", + compression.c_str() ); + } + +/* -------------------------------------------------------------------- */ +/* If this fits in the existing space, just write it directly. */ +/* -------------------------------------------------------------------- */ + if( oCompressedData.buffer_size <= tile_size ) + { + vfile->WriteToFile( oCompressedData.buffer, tile_offset, tile_size ); + + tile_size = oCompressedData.buffer_size; + SetTileInfo( block_index, tile_offset, tile_size ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise we try and write it at the end of the virtual file. */ +/* -------------------------------------------------------------------- */ + else + { + uint64 new_offset = vfile->GetLength(); + + vfile->WriteToFile( oCompressedData.buffer, + new_offset, oCompressedData.buffer_size ); + + SetTileInfo( block_index, new_offset, oCompressedData.buffer_size ); + } + + return 1; +} + +/************************************************************************/ +/* GetBlockWidth() */ +/************************************************************************/ + +int CTiledChannel::GetBlockWidth() const + +{ + EstablishAccess(); + return CPCIDSKChannel::GetBlockWidth(); +} + +/************************************************************************/ +/* GetBlockHeight() */ +/************************************************************************/ + +int CTiledChannel::GetBlockHeight() const + +{ + EstablishAccess(); + return CPCIDSKChannel::GetBlockHeight(); +} + +/************************************************************************/ +/* GetWidth() */ +/************************************************************************/ + +int CTiledChannel::GetWidth() const + +{ + if( width == -1 ) + EstablishAccess(); + + return CPCIDSKChannel::GetWidth(); +} + +/************************************************************************/ +/* GetHeight() */ +/************************************************************************/ + +int CTiledChannel::GetHeight() const + +{ + if( height == -1 ) + EstablishAccess(); + + return CPCIDSKChannel::GetHeight(); +} + +/************************************************************************/ +/* GetType() */ +/************************************************************************/ + +eChanType CTiledChannel::GetType() const + +{ + if( pixel_type == CHN_UNKNOWN ) + EstablishAccess(); + + return CPCIDSKChannel::GetType(); +} + +/************************************************************************/ +/* RLEDecompressBlock() */ +/************************************************************************/ + +void CTiledChannel::RLEDecompressBlock( PCIDSKBuffer &oCompressedData, + PCIDSKBuffer &oDecompressedData ) + + +{ + int src_offset=0, dst_offset=0; + uint8 *src = (uint8 *) oCompressedData.buffer; + uint8 *dst = (uint8 *) oDecompressedData.buffer; + int pixel_size = DataTypeSize(GetType()); + +/* -------------------------------------------------------------------- */ +/* Process till we are out of source data, or our destination */ +/* buffer is full. These conditions should be satisified at */ +/* the same time! */ +/* -------------------------------------------------------------------- */ + while( src_offset + 1 + pixel_size <= oCompressedData.buffer_size + && dst_offset < oDecompressedData.buffer_size ) + { +/* -------------------------------------------------------------------- */ +/* Extract a repeat run */ +/* -------------------------------------------------------------------- */ + if( src[src_offset] > 127 ) + { + int count = src[src_offset++] - 128; + int i; + + if( dst_offset + count * pixel_size > oDecompressedData.buffer_size) + { + ThrowPCIDSKException( "RLE compressed tile corrupt, overrun avoided." ); + } + + while( count-- > 0 ) + { + for( i = 0; i < pixel_size; i++ ) + dst[dst_offset++] = src[src_offset+i]; + } + src_offset += pixel_size; + } + +/* -------------------------------------------------------------------- */ +/* Extract a literal run. */ +/* -------------------------------------------------------------------- */ + else + { + int count = src[src_offset++]; + + if( dst_offset + count*pixel_size > oDecompressedData.buffer_size + || src_offset + count*pixel_size > oCompressedData.buffer_size) + { + ThrowPCIDSKException( "RLE compressed tile corrupt, overrun avoided." ); + } + + memcpy( dst + dst_offset, src + src_offset, + pixel_size * count ); + src_offset += pixel_size * count; + dst_offset += pixel_size * count; + } + + } + +/* -------------------------------------------------------------------- */ +/* Final validation. */ +/* -------------------------------------------------------------------- */ + if( src_offset != oCompressedData.buffer_size + || dst_offset != oDecompressedData.buffer_size ) + { + ThrowPCIDSKException( "RLE compressed tile corrupt, result incomplete." ); + } +} + +/************************************************************************/ +/* RLECompressBlock() */ +/* */ +/* TODO: There does not seem to be any byte order logic in here! */ +/************************************************************************/ + +void CTiledChannel::RLECompressBlock( PCIDSKBuffer &oUncompressedData, + PCIDSKBuffer &oCompressedData ) + + +{ + int src_bytes = oUncompressedData.buffer_size; + int pixel_size = DataTypeSize(GetType()); + int src_offset = 0, dst_offset = 0; + int i; + uint8 *src = (uint8 *) oUncompressedData.buffer; + +/* -------------------------------------------------------------------- */ +/* Loop till input exausted. */ +/* -------------------------------------------------------------------- */ + while( src_offset < src_bytes ) + { + bool bGotARun = false; + +/* -------------------------------------------------------------------- */ +/* Establish the run length, and emit if greater than 3. */ +/* -------------------------------------------------------------------- */ + if( src_offset + 3*pixel_size < src_bytes ) + { + int count = 1; + + while( count < 127 + && src_offset + count*pixel_size < src_bytes ) + { + bool bWordMatch = true; + + for( i = 0; i < pixel_size; i++ ) + { + if( src[src_offset+i] + != src[src_offset+i+count*pixel_size] ) + bWordMatch = false; + } + + if( !bWordMatch ) + break; + + count++; + } + + if( count >= 3 ) + { + if( oCompressedData.buffer_size < dst_offset + pixel_size+1 ) + oCompressedData.SetSize( oCompressedData.buffer_size*2+100); + + oCompressedData.buffer[dst_offset++] = (char) (count+128); + + for( i = 0; i < pixel_size; i++ ) + oCompressedData.buffer[dst_offset++] = src[src_offset+i]; + + src_offset += count * pixel_size; + + bGotARun = true; + } + else + bGotARun = false; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise emit a literal till we encounter at least a three */ +/* word series. */ +/* -------------------------------------------------------------------- */ + if( !bGotARun ) + { + int count = 1; + int match_count = 0; + + while( count < 127 + && src_offset + count*pixel_size < src_bytes ) + { + bool bWordMatch = true; + + for( i = 0; i < pixel_size; i++ ) + { + if( src[src_offset+i] + != src[src_offset+i+count*pixel_size] ) + bWordMatch = false; + } + + if( bWordMatch ) + match_count++; + else + match_count = 0; + + if( match_count > 2 ) + break; + + count++; + } + + assert( src_offset + count*pixel_size <= src_bytes ); + + while( oCompressedData.buffer_size + < dst_offset + count*pixel_size+1 ) + oCompressedData.SetSize( oCompressedData.buffer_size*2+100 ); + + oCompressedData.buffer[dst_offset++] = (char) count; + memcpy( oCompressedData.buffer + dst_offset, + src + src_offset, + count * pixel_size ); + src_offset += count * pixel_size; + dst_offset += count * pixel_size; + } + } + + oCompressedData.buffer_size = dst_offset; +} + +/************************************************************************/ +/* JPEGDecompressBlock() */ +/************************************************************************/ + +void CTiledChannel::JPEGDecompressBlock( PCIDSKBuffer &oCompressedData, + PCIDSKBuffer &oDecompressedData ) + + +{ + if( file->GetInterfaces()->JPEGDecompressBlock == NULL ) + ThrowPCIDSKException( "JPEG decompression not enabled in the PCIDSKInterfaces of this build." ); + + file->GetInterfaces()->JPEGDecompressBlock( + (uint8 *) oCompressedData.buffer, oCompressedData.buffer_size, + (uint8 *) oDecompressedData.buffer, oDecompressedData.buffer_size, + GetBlockWidth(), GetBlockHeight(), GetType() ); +} + +/************************************************************************/ +/* JPEGCompressBlock() */ +/************************************************************************/ + +void CTiledChannel::JPEGCompressBlock( PCIDSKBuffer &oDecompressedData, + PCIDSKBuffer &oCompressedData ) +{ + if( file->GetInterfaces()->JPEGCompressBlock == NULL ) + ThrowPCIDSKException( "JPEG compression not enabled in the PCIDSKInterfaces of this build." ); + +/* -------------------------------------------------------------------- */ +/* What quality should we be using? */ +/* -------------------------------------------------------------------- */ +#if 0 + int quality = 75; + + if( compression.c_str()[4] >= '1' + && compression.c_str()[4] <= '0' ) + quality = atoi(compression.c_str() + 4); +#endif + +/* -------------------------------------------------------------------- */ +/* Make the output buffer plent big to hold any conceivable */ +/* result. */ +/* -------------------------------------------------------------------- */ + oCompressedData.SetSize( oDecompressedData.buffer_size * 2 + 1000 ); + +/* -------------------------------------------------------------------- */ +/* invoke. */ +/* -------------------------------------------------------------------- */ + file->GetInterfaces()->JPEGCompressBlock( + (uint8 *) oDecompressedData.buffer, oDecompressedData.buffer_size, + (uint8 *) oCompressedData.buffer, oCompressedData.buffer_size, + GetBlockWidth(), GetBlockHeight(), GetType(), 75 ); +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/ctiledchannel.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/ctiledchannel.h new file mode 100644 index 000000000..8263ffcae --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/channel/ctiledchannel.h @@ -0,0 +1,110 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CTiledChannel raster access strategy + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_CHANNEL_CTILEDCHANNEL_H +#define __INCLUDE_CHANNEL_CTILEDCHANNEL_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_buffer.h" +#include "channel/cpcidskchannel.h" + +#include +#include + +namespace PCIDSK +{ + class SysVirtualFile; +/************************************************************************/ +/* CTiledChannel */ +/* */ +/* Internal tiled data stored in special tiled imagery */ +/* segments. Imagery may be compressed. */ +/************************************************************************/ + + class CTiledChannel : public CPCIDSKChannel + { + + public: + CTiledChannel( PCIDSKBuffer &image_header, + uint64 ih_offset, + PCIDSKBuffer &file_header, + int channelnum, + CPCIDSKFile *file, + eChanType pixel_type ); + virtual ~CTiledChannel(); + + virtual int GetBlockWidth() const; + virtual int GetBlockHeight() const; + virtual int GetWidth() const; + virtual int GetHeight() const; + virtual eChanType GetType() const; + + virtual int ReadBlock( int block_index, void *buffer, + int xoff=-1, int yoff=-1, + int xsize=-1, int ysize=-1 ); + virtual int WriteBlock( int block_index, void *buffer ); + + virtual void Synchronize(); + + + + private: + int image; + mutable int tile_count; + mutable int tiles_per_row; + mutable int tiles_per_col; + mutable SysVirtualFile *vfile; + mutable std::string compression; + + void EstablishAccess() const; + void RLEDecompressBlock( PCIDSKBuffer &oCompressed, + PCIDSKBuffer &oDecompressed ); + void RLECompressBlock( PCIDSKBuffer &oUncompressed, + PCIDSKBuffer &oCompressed ); + void JPEGDecompressBlock( PCIDSKBuffer &oCompressed, + PCIDSKBuffer &oDecompressed ); + void JPEGCompressBlock( PCIDSKBuffer &oDecompressed, + PCIDSKBuffer &oCompressed ); + + bool IsTileEmpty(void* buffer) const; + + // managed paged list of tile offsets and sizes. + void GetTileInfo( int tile_index, + uint64 &offset, int &size ); + void SetTileInfo( int tile_index, + uint64 offset, int size ); + void LoadTileInfoBlock( int tile_info_block ); + void SaveTileInfoBlock( int tile_info_block ); + + static const int tile_block_size = 4096; + mutable std::vector< std::vector > tile_offsets; + mutable std::vector< std::vector > tile_sizes; + mutable std::vector tile_info_dirty; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_CHANNEL_CTILEDCHANNEL_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/Makefile b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/Makefile new file mode 100644 index 000000000..a954348d5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/Makefile @@ -0,0 +1,8 @@ +default: + (cd .. ; $(MAKE)) + +clean: + (cd .. ; $(MAKE) clean) + +check: + (cd .. ; $(MAKE) check) diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/clinksegment.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/clinksegment.cpp new file mode 100644 index 000000000..02627e5eb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/clinksegment.cpp @@ -0,0 +1,130 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CLinkSegment class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "core/clinksegment.h" +#include "segment/cpcidsksegment.h" +#include "core/pcidsk_utils.h" +#include "pcidsk_exception.h" + +#include +#include +#include +#include +#include +#include + +using namespace PCIDSK; + +CLinkSegment::CLinkSegment(PCIDSKFile *file, + int segment, + const char *segment_pointer) : + CPCIDSKSegment(file, segment, segment_pointer), + loaded_(false), modified_(false) +{ + Load(); +} + + +CLinkSegment::~CLinkSegment() +{ +} + +// Load the contents of the segment +void CLinkSegment::Load() +{ + // Check if we've already loaded the segment into memory + if (loaded_) { + return; + } + + assert(data_size - 1024 == 1 * 512); + + seg_data.SetSize(data_size - 1024); // should be 1 * 512 + + ReadFromFile(seg_data.buffer, 0, data_size - 1024); + + if (std::strncmp(seg_data.buffer, "SysLinkF", 8)) + { + seg_data.Put("SysLinkF",0,8); + return; + } + + path = std::string(&seg_data.buffer[8]); + std::string::reverse_iterator first_non_space = + std::find_if(path.rbegin(), path.rend(), + std::bind2nd(std::not_equal_to(), ' ')); + + *(--first_non_space) = '\0'; + + + // We've now loaded the structure up with data. Mark it as being loaded + // properly. + loaded_ = true; + +} + +void CLinkSegment::Write(void) +{ + //We are not writing if nothing was loaded. + if (!modified_) { + return; + } + + seg_data.Put("SysLinkF",0,8); + seg_data.Put(path.c_str(), 8, path.size(), true); + + WriteToFile(seg_data.buffer, 0, data_size-1024); + modified_ = false; +} + +std::string CLinkSegment::GetPath(void) const +{ + return path; +} + +void CLinkSegment::SetPath(const std::string& oPath) +{ + if(oPath.size() < 504) + { + path = oPath; + modified_ = true; + } + else + { + throw PCIDSKException("The size of the path cannot be" + " bigger than 504 characters."); + } +} + +void CLinkSegment::Synchronize() +{ + if(modified_) + { + this->Write(); + } +} + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/clinksegment.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/clinksegment.h new file mode 100644 index 000000000..03448de86 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/clinksegment.h @@ -0,0 +1,61 @@ +/****************************************************************************** + * + * Purpose: Support for reading and manipulating PCIDSK link info Segments + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_SEGMENT_CLINKSEGMENT_H +#define __INCLUDE_PCIDSK_SEGMENT_CLINKSEGMENT_H + +#include "segment/cpcidsksegment.h" +#include "pcidsk_buffer.h" + +namespace PCIDSK { + class PCIDSKFile; + + class CLinkSegment : public CPCIDSKSegment + { + public: + CLinkSegment(PCIDSKFile *file, int segment,const char *segment_pointer); + ~CLinkSegment(); + + // Get path + std::string GetPath(void) const; + // Set path + void SetPath(const std::string& oPath); + + //synchronize the segment on disk. + void Synchronize(); + private: + // Helper housekeeping functions + void Load(); + void Write(); + + bool loaded_; + bool modified_; + PCIDSKBuffer seg_data; + std::string path; + }; +} + +#endif // __INCLUDE_PCIDSK_SEGMENT_CLINKSEGMENT_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/cpcidskfile.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/cpcidskfile.cpp new file mode 100644 index 000000000..28ab9a28b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/cpcidskfile.cpp @@ -0,0 +1,1312 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSKFile class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "pcidsk_file.h" +#include "pcidsk_exception.h" +#include "pcidsk_channel.h" +#include "pcidsk_segment.h" +#include "core/mutexholder.h" +#include "core/pcidsk_utils.h" +#include "core/cpcidskfile.h" + +// Channel types +#include "channel/cbandinterleavedchannel.h" +#include "channel/cpixelinterleavedchannel.h" +#include "channel/ctiledchannel.h" +#include "channel/cexternalchannel.h" + +// Segment types +#include "segment/cpcidskgeoref.h" +#include "segment/cpcidskpct.h" +#include "segment/cpcidskvectorsegment.h" +#include "segment/metadatasegment.h" +#include "segment/sysblockmap.h" +#include "segment/cpcidskrpcmodel.h" +#include "segment/cpcidskgcp2segment.h" +#include "segment/cpcidskbitmap.h" +#include "segment/cpcidsk_tex.h" +#include "segment/cpcidsk_array.h" +#include "segment/cpcidskapmodel.h" +#include "segment/cpcidskads40model.h" +#include "segment/cpcidsktoutinmodel.h" +#include "segment/cpcidskpolymodel.h" +#include "segment/cpcidskbinarysegment.h" +#include "core/clinksegment.h" + +#include +#include +#include +#include +#include + +#include + +using namespace PCIDSK; + +/************************************************************************/ +/* CPCIDSKFile() */ +/************************************************************************/ + +CPCIDSKFile::CPCIDSKFile( std::string filename ) + +{ + io_handle = NULL; + io_mutex = NULL; + updatable = false; + base_filename = filename; + +/* -------------------------------------------------------------------- */ +/* Initialize the metadata object, but do not try to load till */ +/* needed. */ +/* -------------------------------------------------------------------- */ + metadata.Initialize( this, "FIL", 0 ); +} + +/************************************************************************/ +/* ~CPCIDSKFile() */ +/************************************************************************/ + +CPCIDSKFile::~CPCIDSKFile() + +{ + Synchronize(); + +/* -------------------------------------------------------------------- */ +/* Cleanup last block buffer. */ +/* -------------------------------------------------------------------- */ + if( last_block_data != NULL ) + { + last_block_index = -1; + free( last_block_data ); + last_block_data = NULL; + delete last_block_mutex; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup channels and segments. */ +/* -------------------------------------------------------------------- */ + size_t i; + for( i = 0; i < channels.size(); i++ ) + { + delete channels[i]; + channels[i] = NULL; + } + + for( i = 0; i < segments.size(); i++ ) + { + delete segments[i]; + segments[i] = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Close and cleanup IO stuff. */ +/* -------------------------------------------------------------------- */ + { + MutexHolder oHolder( io_mutex ); + + if( io_handle ) + { + interfaces.io->Close( io_handle ); + io_handle = NULL; + } + } + + size_t i_file; + + for( i_file=0; i_file < file_list.size(); i_file++ ) + { + delete file_list[i_file].io_mutex; + file_list[i_file].io_mutex = NULL; + + interfaces.io->Close( file_list[i_file].io_handle ); + file_list[i_file].io_handle = NULL; + } + + for( i_file=0; i_file < edb_file_list.size(); i_file++ ) + { + delete edb_file_list[i_file].io_mutex; + edb_file_list[i_file].io_mutex = NULL; + + delete edb_file_list[i_file].file; + edb_file_list[i_file].file = NULL; + } + + delete io_mutex; +} + +/************************************************************************/ +/* Synchronize() */ +/************************************************************************/ + +void CPCIDSKFile::Synchronize() + +{ + if( !GetUpdatable() ) + return; + +/* -------------------------------------------------------------------- */ +/* Flush out last line caching stuff for pixel interleaved data. */ +/* -------------------------------------------------------------------- */ + FlushBlock(); + +/* -------------------------------------------------------------------- */ +/* Synchronize all channels. */ +/* -------------------------------------------------------------------- */ + size_t i; + for( i = 0; i < channels.size(); i++ ) + channels[i]->Synchronize(); + +/* -------------------------------------------------------------------- */ +/* Synchronize all segments we have instantiated. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < segments.size(); i++ ) + { + if( segments[i] != NULL ) + segments[i]->Synchronize(); + } + +/* -------------------------------------------------------------------- */ +/* Ensure the file is synhronized to disk. */ +/* -------------------------------------------------------------------- */ + MutexHolder oHolder( io_mutex ); + + interfaces.io->Flush( io_handle ); +} + +/************************************************************************/ +/* GetChannel() */ +/************************************************************************/ + +PCIDSKChannel *CPCIDSKFile::GetChannel( int band ) + +{ + if( band < 1 || band > channel_count ) + ThrowPCIDSKException( "Out of range band (%d) requested.", + band ); + + return channels[band-1]; +} + +/************************************************************************/ +/* GetSegment() */ +/************************************************************************/ + +PCIDSK::PCIDSKSegment *CPCIDSKFile::GetSegment( int segment ) + +{ +/* -------------------------------------------------------------------- */ +/* Is this a valid segment? */ +/* -------------------------------------------------------------------- */ + if( segment < 1 || segment > segment_count ) + return NULL; + + const char *segment_pointer = segment_pointers.buffer + (segment-1) * 32; + if( segment_pointer[0] != 'A' && segment_pointer[0] != 'L' ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Do we already have a corresponding object? */ +/* -------------------------------------------------------------------- */ + if( segments[segment] != NULL ) + return segments[segment]; + +/* -------------------------------------------------------------------- */ +/* Instantiate per the type. */ +/* -------------------------------------------------------------------- */ + int segment_type = segment_pointers.GetInt((segment-1)*32+1,3); + PCIDSKSegment *segobj = NULL; + + switch( segment_type ) + { + case SEG_GEO: + segobj = new CPCIDSKGeoref( this, segment, segment_pointer ); + break; + + case SEG_PCT: + segobj = new CPCIDSK_PCT( this, segment, segment_pointer ); + break; + + case SEG_VEC: + segobj = new CPCIDSKVectorSegment( this, segment, segment_pointer ); + break; + + case SEG_BIT: + segobj = new CPCIDSKBitmap( this, segment, segment_pointer ); + break; + + case SEG_TEX: + segobj = new CPCIDSK_TEX( this, segment, segment_pointer ); + break; + + case SEG_SYS: + if( strncmp(segment_pointer + 4, "SysBMDir",8) == 0 ) + segobj = new SysBlockMap( this, segment, segment_pointer ); + else if( strncmp(segment_pointer + 4, "METADATA",8) == 0 ) + segobj = new MetadataSegment( this, segment, segment_pointer ); + else if (strncmp(segment_pointer + 4, "Link ", 8) == 0) + segobj = new CLinkSegment(this, segment, segment_pointer); + else + segobj = new CPCIDSKSegment( this, segment, segment_pointer ); + + break; + + case SEG_GCP2: + segobj = new CPCIDSKGCP2Segment(this, segment, segment_pointer); + break; + + case SEG_ORB: + segobj = new CPCIDSKEphemerisSegment(this, segment, segment_pointer); + break; + + case SEG_ARR: + segobj = new CPCIDSK_ARRAY(this, segment, segment_pointer); + break; + + case SEG_BIN: + if (!strncmp(segment_pointer + 4, "RFMODEL ", 8)) + { + segobj = new CPCIDSKRPCModelSegment( this, segment, segment_pointer ); + } + else if (!strncmp(segment_pointer + 4, "APMODEL ", 8)) + { + segobj = new CPCIDSKAPModelSegment(this, segment, segment_pointer); + } + else if (!strncmp(segment_pointer + 4, "ADSMODEL", 8)) + { + segobj = new CPCIDSKADS40ModelSegment(this, segment, segment_pointer); + } + else if (!strncmp(segment_pointer + 4, "POLYMDL ", 8)) + { + segobj = new CPCIDSKBinarySegment(this, segment, segment_pointer); + } + else if (!strncmp(segment_pointer + 4, "TPSMODEL", 8)) + { + segobj = new CPCIDSKGCP2Segment(this, segment, segment_pointer); + } + else if (!strncmp(segment_pointer + 4, "MODEL ", 8)) + { + segobj = new CPCIDSKToutinModelSegment(this, segment, segment_pointer); + } + else if (!strncmp(segment_pointer + 4, "MMSPB ", 8)) + { + segobj = new CPCIDSKBinarySegment(this, segment, segment_pointer); + } + else if (!strncmp(segment_pointer + 4, "MMADS ", 8)) + { + segobj = new CPCIDSKBinarySegment(this, segment, segment_pointer); + } + break; + } + + if (segobj == NULL) + segobj = new CPCIDSKSegment( this, segment, segment_pointer ); + + segments[segment] = segobj; + + return segobj; +} + +/************************************************************************/ +/* GetSegment() */ +/* */ +/* Find segment by type/name. */ +/************************************************************************/ + +PCIDSK::PCIDSKSegment *CPCIDSKFile::GetSegment( int type, std::string name, + int previous ) + +{ + int i; + char type_str[4]; + + name += " "; // white space pad name. + + //we want the 3 less significant digit only in case type is too big + // Note : that happen with SEG_VEC_TABLE that is equal to 65652 in GDB. + //see function BuildChildrenLayer in jtfile.cpp, the call on GDBSegNext + //in the loop on gasTypeTable can create issue in PCIDSKSegNext + //(in pcic/gdbfrtms/pcidskopen.cpp) + sprintf( type_str, "%03d", (type % 1000) ); + + for( i = previous; i < segment_count; i++ ) + { + if( type != SEG_UNKNOWN + && strncmp(segment_pointers.buffer+i*32+1,type_str,3) != 0 ) + continue; + + if( name != " " + && strncmp(segment_pointers.buffer+i*32+4,name.c_str(),8) != 0 ) + continue; + + // Ignore deleted segments. + if (*(segment_pointers.buffer + i * 32 + 0) == 'D') continue; + + return GetSegment(i+1); + } + + return NULL; +} + + +/************************************************************************/ +/* GetSegments() */ +/************************************************************************/ + +std::vector CPCIDSKFile::GetSegments() + +{ + PCIDSK::ThrowPCIDSKException( "Objects list access not implemented yet." ); + + std::vector list; + return list; +} + +/************************************************************************/ +/* InitializeFromHeader() */ +/************************************************************************/ + +void CPCIDSKFile::InitializeFromHeader() + +{ +/* -------------------------------------------------------------------- */ +/* Process the file header. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer fh(512); + + ReadFromFile( fh.buffer, 0, 512 ); + + width = atoi(fh.Get(384,8)); + height = atoi(fh.Get(392,8)); + channel_count = atoi(fh.Get(376,8)); + file_size = fh.GetUInt64(16,16); + + uint64 ih_start_block = atouint64(fh.Get(336,16)); + uint64 image_start_block = atouint64(fh.Get(304,16)); + fh.Get(360,8,interleaving); + + uint64 image_offset = (image_start_block-1) * 512; + + block_size = 0; + last_block_index = -1; + last_block_dirty = 0; + last_block_data = NULL; + last_block_mutex = NULL; + +/* -------------------------------------------------------------------- */ +/* Load the segment pointers into a PCIDSKBuffer. For now we */ +/* try to avoid doing too much other processing on them. */ +/* -------------------------------------------------------------------- */ + int segment_block_count = atoi(fh.Get(456,8)); + + segment_count = (segment_block_count * 512) / 32; + segment_pointers.SetSize( segment_block_count * 512 ); + segment_pointers_offset = atouint64(fh.Get(440,16)) * 512 - 512; + ReadFromFile( segment_pointers.buffer, segment_pointers_offset, + segment_block_count * 512 ); + + segments.resize( segment_count + 1 ); + +/* -------------------------------------------------------------------- */ +/* Get the number of each channel type - only used for some */ +/* interleaving cases. */ +/* -------------------------------------------------------------------- */ + int count_8u = 0, count_16s = 0, count_16u = 0, count_32r = 0; + int count_c16u = 0, count_c16s = 0, count_c32r = 0; + + if (strcmp(fh.Get(464,4), " ") == 0) + { + count_8u = channel_count; + } + else + { + count_8u = atoi(fh.Get(464,4)); + count_16s = atoi(fh.Get(468,4)); + count_16u = atoi(fh.Get(472,4)); + count_32r = atoi(fh.Get(476,4)); + count_c16u = atoi(fh.Get(480,4)); + count_c16s = atoi(fh.Get(484,4)); + count_c32r = atoi(fh.Get(488,4)); + } +/* -------------------------------------------------------------------- */ +/* for pixel interleaved files we need to compute the length of */ +/* a scanline padded out to a 512 byte boundary. */ +/* -------------------------------------------------------------------- */ + if( interleaving == "PIXEL" ) + { + first_line_offset = image_offset; + pixel_group_size = count_8u + count_16s*2 + count_16u*2 + count_32r*4; + + block_size = pixel_group_size * width; + if( block_size % 512 != 0 ) + block_size += 512 - (block_size % 512); + + last_block_data = malloc((size_t) block_size); + if( last_block_data == NULL ) + ThrowPCIDSKException( "Allocating %d bytes for scanline buffer failed.", + (int) block_size ); + + last_block_mutex = interfaces.CreateMutex(); + image_offset = 0; + } + +/* -------------------------------------------------------------------- */ +/* Initialize the list of channels. */ +/* -------------------------------------------------------------------- */ + int channelnum; + + for( channelnum = 1; channelnum <= channel_count; channelnum++ ) + { + PCIDSKBuffer ih(1024); + PCIDSKChannel *channel = NULL; + uint64 ih_offset = (ih_start_block-1)*512 + (channelnum-1)*1024; + + ReadFromFile( ih.buffer, ih_offset, 1024 ); + + // fetch the filename, if there is one. + std::string filename; + ih.Get(64,64,filename); + + // adjust it relative to the path of the pcidsk file. + filename = MergeRelativePath( interfaces.io, + base_filename, filename ); + + // work out channel type from header + eChanType pixel_type; + const char *pixel_type_string = ih.Get( 160, 8 ); + + pixel_type = GetDataTypeFromName(pixel_type_string); + + // if we didn't get channel type in header, work out from counts (old). + // Check this only if we don't have complex channels: + + if (strncmp(pixel_type_string," ",8) == 0 ) + { + assert( count_c32r == 0 && count_c16u == 0 && count_c16s == 0 ); + if( channelnum <= count_8u ) + pixel_type = CHN_8U; + else if( channelnum <= count_8u + count_16s ) + pixel_type = CHN_16S; + else if( channelnum <= count_8u + count_16s + count_16u ) + pixel_type = CHN_16U; + else + pixel_type = CHN_32R; + } + + if( interleaving == "BAND" ) + { + channel = new CBandInterleavedChannel( ih, ih_offset, fh, + channelnum, this, + image_offset, pixel_type ); + + + image_offset += (int64)DataTypeSize(channel->GetType()) + * (int64)width * (int64)height; + } + + else if( interleaving == "PIXEL" ) + { + channel = new CPixelInterleavedChannel( ih, ih_offset, fh, + channelnum, this, + (int) image_offset, + pixel_type ); + image_offset += DataTypeSize(pixel_type); + } + + else if( interleaving == "FILE" + && strncmp(filename.c_str(),"/SIS=",5) == 0 ) + { + channel = new CTiledChannel( ih, ih_offset, fh, + channelnum, this, pixel_type ); + } + + else if( interleaving == "FILE" + && filename != "" + && strncmp(((const char*)ih.buffer)+250, " ", 8 ) != 0 ) + { + channel = new CExternalChannel( ih, ih_offset, fh, filename, + channelnum, this, pixel_type ); + } + + else if( interleaving == "FILE" ) + { + channel = new CBandInterleavedChannel( ih, ih_offset, fh, + channelnum, this, + 0, pixel_type ); + } + + else + ThrowPCIDSKException( "Unsupported interleaving:%s", + interleaving.c_str() ); + + channels.push_back( channel ); + } +} + +/************************************************************************/ +/* ReadFromFile() */ +/************************************************************************/ + +void CPCIDSKFile::ReadFromFile( void *buffer, uint64 offset, uint64 size ) + +{ + MutexHolder oHolder( io_mutex ); + + interfaces.io->Seek( io_handle, offset, SEEK_SET ); + if( interfaces.io->Read( buffer, 1, size, io_handle ) != size ) + ThrowPCIDSKException( "PCIDSKFile:Failed to read %d bytes at %d.", + (int) size, (int) offset ); +} + +/************************************************************************/ +/* WriteToFile() */ +/************************************************************************/ + +void CPCIDSKFile::WriteToFile( const void *buffer, uint64 offset, uint64 size ) + +{ + if( !GetUpdatable() ) + throw PCIDSKException( "File not open for update in WriteToFile()" ); + + MutexHolder oHolder( io_mutex ); + + interfaces.io->Seek( io_handle, offset, SEEK_SET ); + if( interfaces.io->Write( buffer, 1, size, io_handle ) != size ) + ThrowPCIDSKException( "PCIDSKFile:Failed to write %d bytes at %d.", + (int) size, (int) offset ); +} + +/************************************************************************/ +/* ReadAndLockBlock() */ +/************************************************************************/ + +void *CPCIDSKFile::ReadAndLockBlock( int block_index, + int win_xoff, int win_xsize ) + +{ + if( last_block_data == NULL ) + ThrowPCIDSKException( "ReadAndLockBlock() called on a file that is not pixel interleaved." ); + +/* -------------------------------------------------------------------- */ +/* Default, and validate windowing. */ +/* -------------------------------------------------------------------- */ + if( win_xoff == -1 && win_xsize == -1 ) + { + win_xoff = 0; + win_xsize = GetWidth(); + } + + if( win_xoff < 0 || win_xoff+win_xsize > GetWidth() ) + { + ThrowPCIDSKException( "CPCIDSKFile::ReadAndLockBlock(): Illegal window - xoff=%d, xsize=%d", + win_xoff, win_xsize ); + } + + if( block_index == last_block_index + && win_xoff == last_block_xoff + && win_xsize == last_block_xsize ) + { + last_block_mutex->Acquire(); + return last_block_data; + } + +/* -------------------------------------------------------------------- */ +/* Flush any dirty writable data. */ +/* -------------------------------------------------------------------- */ + FlushBlock(); + +/* -------------------------------------------------------------------- */ +/* Read the requested window. */ +/* -------------------------------------------------------------------- */ + last_block_mutex->Acquire(); + + ReadFromFile( last_block_data, + first_line_offset + block_index*block_size + + win_xoff * pixel_group_size, + pixel_group_size * win_xsize ); + last_block_index = block_index; + last_block_xoff = win_xoff; + last_block_xsize = win_xsize; + + return last_block_data; +} + +/************************************************************************/ +/* UnlockBlock() */ +/************************************************************************/ + +void CPCIDSKFile::UnlockBlock( bool mark_dirty ) + +{ + if( last_block_mutex == NULL ) + return; + + last_block_dirty |= mark_dirty; + last_block_mutex->Release(); +} + +/************************************************************************/ +/* WriteBlock() */ +/************************************************************************/ + +void CPCIDSKFile::WriteBlock( int block_index, void *buffer ) + +{ + if( !GetUpdatable() ) + throw PCIDSKException( "File not open for update in WriteBlock()" ); + + if( last_block_data == NULL ) + ThrowPCIDSKException( "WriteBlock() called on a file that is not pixel interleaved." ); + + WriteToFile( buffer, + first_line_offset + block_index*block_size, + block_size ); +} + +/************************************************************************/ +/* FlushBlock() */ +/************************************************************************/ + +void CPCIDSKFile::FlushBlock() + +{ + if( last_block_dirty ) + { + last_block_mutex->Acquire(); + if( last_block_dirty ) // is it still dirty? + { + WriteBlock( last_block_index, last_block_data ); + last_block_dirty = 0; + } + last_block_mutex->Release(); + } +} + +/************************************************************************/ +/* GetEDBFileDetails() */ +/************************************************************************/ + +bool CPCIDSKFile::GetEDBFileDetails( EDBFile** file_p, + Mutex **io_mutex_p, + std::string filename ) + +{ + *file_p = NULL; + *io_mutex_p = NULL; + +/* -------------------------------------------------------------------- */ +/* Does the file exist already in our file list? */ +/* -------------------------------------------------------------------- */ + unsigned int i; + + for( i = 0; i < edb_file_list.size(); i++ ) + { + if( edb_file_list[i].filename == filename ) + { + *file_p = edb_file_list[i].file; + *io_mutex_p = edb_file_list[i].io_mutex; + return edb_file_list[i].writable; + } + } + +/* -------------------------------------------------------------------- */ +/* If not, we need to try and open the file. Eventually we */ +/* will need better rules about read or update access. */ +/* -------------------------------------------------------------------- */ + ProtectedEDBFile new_file; + + new_file.file = NULL; + new_file.writable = false; + + if( GetUpdatable() ) + { + try { + new_file.file = interfaces.OpenEDB( filename, "r+" ); + new_file.writable = true; + } + catch( PCIDSK::PCIDSKException ex ) {} + catch( std::exception ex ) {} + } + + if( new_file.file == NULL ) + new_file.file = interfaces.OpenEDB( filename, "r" ); + + if( new_file.file == NULL ) + ThrowPCIDSKException( "Unable to open file '%s'.", + filename.c_str() ); + +/* -------------------------------------------------------------------- */ +/* Push the new file into the list of files managed for this */ +/* PCIDSK file. */ +/* -------------------------------------------------------------------- */ + new_file.io_mutex = interfaces.CreateMutex(); + new_file.filename = filename; + + edb_file_list.push_back( new_file ); + + *file_p = edb_file_list[edb_file_list.size()-1].file; + *io_mutex_p = edb_file_list[edb_file_list.size()-1].io_mutex; + + return new_file.writable; +} + +/************************************************************************/ +/* GetIODetails() */ +/************************************************************************/ + +void CPCIDSKFile::GetIODetails( void ***io_handle_pp, + Mutex ***io_mutex_pp, + std::string filename, + bool writable ) + +{ + *io_handle_pp = NULL; + *io_mutex_pp = NULL; + +/* -------------------------------------------------------------------- */ +/* Does this reference the PCIDSK file itself? */ +/* -------------------------------------------------------------------- */ + if( filename.size() == 0 ) + { + *io_handle_pp = &io_handle; + *io_mutex_pp = &io_mutex; + return; + } + +/* -------------------------------------------------------------------- */ +/* Does the file exist already in our file list? */ +/* -------------------------------------------------------------------- */ + unsigned int i; + + for( i = 0; i < file_list.size(); i++ ) + { + if( file_list[i].filename == filename + && (!writable || file_list[i].writable) ) + { + *io_handle_pp = &(file_list[i].io_handle); + *io_mutex_pp = &(file_list[i].io_mutex); + return; + } + } + +/* -------------------------------------------------------------------- */ +/* If not, we need to try and open the file. Eventually we */ +/* will need better rules about read or update access. */ +/* -------------------------------------------------------------------- */ + ProtectedFile new_file; + + if( writable ) + new_file.io_handle = interfaces.io->Open( filename, "r+" ); + else + new_file.io_handle = interfaces.io->Open( filename, "r" ); + + if( new_file.io_handle == NULL ) + ThrowPCIDSKException( "Unable to open file '%s'.", + filename.c_str() ); + +/* -------------------------------------------------------------------- */ +/* Push the new file into the list of files managed for this */ +/* PCIDSK file. */ +/* -------------------------------------------------------------------- */ + new_file.io_mutex = interfaces.CreateMutex(); + new_file.filename = filename; + new_file.writable = writable; + + file_list.push_back( new_file ); + + *io_handle_pp = &(file_list[file_list.size()-1].io_handle); + *io_mutex_pp = &(file_list[file_list.size()-1].io_mutex); +} + +/************************************************************************/ +/* DeleteSegment() */ +/************************************************************************/ + +void CPCIDSKFile::DeleteSegment( int segment ) + +{ +/* -------------------------------------------------------------------- */ +/* Is this an existing segment? */ +/* -------------------------------------------------------------------- */ + PCIDSKSegment *poSeg = GetSegment( segment ); + + if( poSeg == NULL ) + ThrowPCIDSKException( "DeleteSegment(%d) failed, segment does not exist.", segment ); + +/* -------------------------------------------------------------------- */ +/* Wipe associated metadata. */ +/* -------------------------------------------------------------------- */ + std::vector md_keys = poSeg->GetMetadataKeys(); + unsigned int i; + + for( i = 0; i < md_keys.size(); i++ ) + poSeg->SetMetadataValue( md_keys[i], "" ); + +/* -------------------------------------------------------------------- */ +/* Remove the segment object from the segment object cache. I */ +/* hope the application is not retaining any references to this */ +/* segment! */ +/* -------------------------------------------------------------------- */ + segments[segment] = NULL; + delete poSeg; + +/* -------------------------------------------------------------------- */ +/* Mark the segment pointer as deleted. */ +/* -------------------------------------------------------------------- */ + segment_pointers.buffer[(segment-1)*32] = 'D'; + + // write the updated segment pointer back to the file. + WriteToFile( segment_pointers.buffer + (segment-1)*32, + segment_pointers_offset + (segment-1)*32, + 32 ); +} + +/************************************************************************/ +/* CreateSegment() */ +/************************************************************************/ + +int CPCIDSKFile::CreateSegment( std::string name, std::string description, + eSegType seg_type, int data_blocks ) + +{ +/* -------------------------------------------------------------------- */ +/* Set the size of fixed length segments. */ +/* -------------------------------------------------------------------- */ + int expected_data_blocks = 0; + bool prezero = false; + + switch( seg_type ) + { + case SEG_LUT: + expected_data_blocks = 2; + break; + + case SEG_PCT: + expected_data_blocks = 6; + break; + + case SEG_SIG: + expected_data_blocks = 12; + break; + + case SEG_GCP2: + // expected_data_blocks = 67; + // Change seg type to new GCP segment type + expected_data_blocks = 129; + break; + + case SEG_GEO: + expected_data_blocks = 6; + break; + + case SEG_TEX: + expected_data_blocks = 64; + prezero = true; + break; + + case SEG_BIT: + { + uint64 bytes = ((width * (uint64) height) + 7) / 8; + expected_data_blocks = (int) ((bytes + 511) / 512); + prezero = true; + } + break; + + default: + break; + } + + if( data_blocks == 0 && expected_data_blocks != 0 ) + data_blocks = expected_data_blocks; + +/* -------------------------------------------------------------------- */ +/* Find an empty Segment Pointer. For System segments we start */ +/* at the end, instead of the beginning to avoid using up */ +/* segment numbers that the user would notice. */ +/* -------------------------------------------------------------------- */ + int segment = 1; + int64 seg_start = -1; + PCIDSKBuffer segptr( 32 ); + + if( seg_type == SEG_SYS ) + { + for( segment=segment_count; segment >= 1; segment-- ) + { + memcpy( segptr.buffer, segment_pointers.buffer+(segment-1)*32, 32); + + uint64 this_seg_size = segptr.GetUInt64(23,9); + char flag = (char) segptr.buffer[0]; + + if( flag == 'D' + && (uint64) data_blocks+2 == this_seg_size + && this_seg_size > 0 ) + seg_start = segptr.GetUInt64(12,11) - 1; + else if( flag == ' ' ) + seg_start = 0; + else if( flag && this_seg_size == 0 ) + seg_start = 0; + + if( seg_start != -1 ) + break; + } + } + else + { + for( segment=1; segment <= segment_count; segment++ ) + { + memcpy( segptr.buffer, segment_pointers.buffer+(segment-1)*32, 32); + + uint64 this_seg_size = segptr.GetUInt64(23,9); + char flag = (char) segptr.buffer[0]; + + if( flag == 'D' + && (uint64) data_blocks+2 == this_seg_size + && this_seg_size > 0 ) + seg_start = segptr.GetUInt64(12,11) - 1; + else if( flag == ' ' ) + seg_start = 0; + else if( flag && this_seg_size == 0 ) + seg_start = 0; + + if( seg_start != -1 ) + break; + } + } + + if( segment > segment_count ) + ThrowPCIDSKException( "All %d segment pointers in use.", segment_count); + +/* -------------------------------------------------------------------- */ +/* If the segment does not have a data area already, identify */ +/* it's location at the end of the file, and extend the file to */ +/* the desired length. */ +/* -------------------------------------------------------------------- */ + if( seg_start == 0 ) + { + seg_start = GetFileSize(); + ExtendFile( data_blocks + 2, prezero ); + } + +/* -------------------------------------------------------------------- */ +/* Update the segment pointer information. */ +/* -------------------------------------------------------------------- */ + // SP1.1 - Flag + segptr.Put( "A", 0, 1 ); + + // SP1.2 - Type + segptr.Put( (int) seg_type, 1, 3 ); + + // SP1.3 - Name + segptr.Put( name.c_str(), 4, 8 ); + + // SP1.4 - start block + segptr.Put( (uint64) (seg_start + 1), 12, 11 ); + + // SP1.5 - data blocks. + segptr.Put( data_blocks+2, 23, 9 ); + + // Update in memory copy of segment pointers. + memcpy( segment_pointers.buffer+(segment-1)*32, segptr.buffer, 32); + + // Update on disk. + WriteToFile( segptr.buffer, + segment_pointers_offset + (segment-1)*32, 32 ); + +/* -------------------------------------------------------------------- */ +/* Prepare segment header. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer sh(1024); + + char current_time[17]; + + GetCurrentDateTime( current_time ); + + sh.Put( " ", 0, 1024 ); + + // SH1 - segment content description + sh.Put( description.c_str(), 0, 64 ); + + // SH3 - Creation time/date + sh.Put( current_time, 128, 16 ); + + // SH4 - Last Update time/date + sh.Put( current_time, 144, 16 ); + +/* -------------------------------------------------------------------- */ +/* Write segment header. */ +/* -------------------------------------------------------------------- */ + WriteToFile( sh.buffer, seg_start * 512, 1024 ); + +/* -------------------------------------------------------------------- */ +/* Initialize the newly created segment. */ +/* -------------------------------------------------------------------- */ + PCIDSKSegment *seg_obj = GetSegment( segment ); + + seg_obj->Initialize(); + + return segment; +} + +/************************************************************************/ +/* ExtendFile() */ +/************************************************************************/ + +void CPCIDSKFile::ExtendFile( uint64 blocks_requested, bool prezero ) + +{ + if( prezero ) + { + std::vector zeros; + uint64 blocks_to_zero = blocks_requested; + + zeros.resize( 512 * 32 ); + + while( blocks_to_zero > 0 ) + { + uint64 this_time = blocks_to_zero; + if( this_time > 32 ) + this_time = 32; + + WriteToFile( &(zeros[0]), file_size * 512, this_time*512 ); + blocks_to_zero -= this_time; + file_size += this_time; + } + } + else + { + WriteToFile( "\0", (file_size + blocks_requested) * 512 - 1, 1 ); + file_size += blocks_requested; + } + + PCIDSKBuffer fh3( 16 ); + fh3.Put( file_size, 0, 16 ); + WriteToFile( fh3.buffer, 16, 16 ); +} + +/************************************************************************/ +/* ExtendSegment() */ +/************************************************************************/ + +void CPCIDSKFile::ExtendSegment( int segment, uint64 blocks_requested, + bool prezero ) + +{ + // for now we take it for granted that the segment is valid and at th + // end of the file - later we should support moving it. + + ExtendFile( blocks_requested, prezero ); + + // Update the block count. + segment_pointers.Put( + segment_pointers.GetUInt64((segment-1)*32+23,9) + blocks_requested, + (segment-1)*32+23, 9 ); + + // write the updated segment pointer back to the file. + WriteToFile( segment_pointers.buffer + (segment-1)*32, + segment_pointers_offset + (segment-1)*32, + 32 ); +} + +/************************************************************************/ +/* MoveSegmentToEOF() */ +/************************************************************************/ + +void CPCIDSKFile::MoveSegmentToEOF( int segment ) + +{ + int segptr_off = (segment - 1) * 32; + uint64 seg_start, seg_size; + uint64 new_seg_start; + + seg_start = segment_pointers.GetUInt64( segptr_off + 12, 11 ); + seg_size = segment_pointers.GetUInt64( segptr_off + 23, 9 ); + + // Are we already at the end of the file? + if( (seg_start + seg_size - 1) == file_size ) + return; + + new_seg_start = file_size + 1; + + // Grow the file to hold the segment at the end. + ExtendFile( seg_size, false ); + + // Move the segment data to the new location. + uint8 copy_buf[16384]; + uint64 srcoff, dstoff, bytes_to_go; + + bytes_to_go = seg_size * 512; + srcoff = (seg_start - 1) * 512; + dstoff = (new_seg_start - 1) * 512; + + while( bytes_to_go > 0 ) + { + uint64 bytes_this_chunk = sizeof(copy_buf); + if( bytes_to_go < bytes_this_chunk ) + bytes_this_chunk = bytes_to_go; + + ReadFromFile( copy_buf, srcoff, bytes_this_chunk ); + WriteToFile( copy_buf, dstoff, bytes_this_chunk ); + + srcoff += bytes_this_chunk; + dstoff += bytes_this_chunk; + bytes_to_go -= bytes_this_chunk; + } + + // Update segment pointer in memory and on disk. + segment_pointers.Put( new_seg_start, segptr_off + 12, 11 ); + + WriteToFile( segment_pointers.buffer + segptr_off, + segment_pointers_offset + segptr_off, + 32 ); + + // Update the segments own information. + if( segments[segment] != NULL ) + { + CPCIDSKSegment *seg = + dynamic_cast( segments[segment] ); + + seg->LoadSegmentPointer( segment_pointers.buffer + segptr_off ); + } +} + +/************************************************************************/ +/* CreateOverviews() */ +/************************************************************************/ +/* + const char *pszResampling; + Can be "NEAREST" for Nearest Neighbour resampling (the fastest), + "AVERAGE" for block averaging or "MODE" for block mode. This + establishing the type of resampling to be applied when preparing + the decimated overviews. Other methods can be set as well, but + not all applications might support a given overview generation + method. +*/ + +void CPCIDSKFile::CreateOverviews( int chan_count, int *chan_list, + int factor, std::string resampling ) + +{ + std::vector default_chan_list; + +/* -------------------------------------------------------------------- */ +/* Default to processing all bands. */ +/* -------------------------------------------------------------------- */ + if( chan_count == 0 ) + { + chan_count = channel_count; + default_chan_list.resize( chan_count ); + + for( int i = 0; i < chan_count; i++ ) + default_chan_list[i] = i+1; + + chan_list = &(default_chan_list[0]); + } + +/* -------------------------------------------------------------------- */ +/* Work out the creation options that should apply for the */ +/* overview. */ +/* -------------------------------------------------------------------- */ + std::string layout = GetMetadataValue( "_DBLayout" ); + int blocksize = 127; + std::string compression = "NONE"; + + if( strncmp( layout.c_str(), "TILED", 5 ) == 0 ) + { + ParseTileFormat( layout, blocksize, compression ); + } + +/* -------------------------------------------------------------------- */ +/* Make sure we have a blockmap segment for managing the tiled */ +/* layers. */ +/* -------------------------------------------------------------------- */ + PCIDSKSegment *bm_seg = GetSegment( SEG_SYS, "SysBMDir" ); + SysBlockMap *bm; + + if( bm_seg == NULL ) + { + CreateSegment( "SysBMDir", + "System Block Map Directory - Do not modify.", + SEG_SYS, 0 ); + bm_seg = GetSegment( SEG_SYS, "SysBMDir" ); + bm = dynamic_cast(bm_seg); + bm->Initialize(); + } + else + bm = dynamic_cast(bm_seg); + +/* ==================================================================== */ +/* Loop over the channels. */ +/* ==================================================================== */ + for( int chan_index = 0; chan_index < chan_count; chan_index++ ) + { + int channel_number = chan_list[chan_index]; + PCIDSKChannel *channel = GetChannel( channel_number ); + +/* -------------------------------------------------------------------- */ +/* Figure out if the given overview level already exists */ +/* for a given channel; if it does, skip creating it. */ +/* -------------------------------------------------------------------- */ + bool overview_exists = false; + for( int i = channel->GetOverviewCount()-1; i >= 0; i-- ) + { + PCIDSKChannel *overview = channel->GetOverview( i ); + + if( overview->GetWidth() == channel->GetWidth() / factor + && overview->GetHeight() == channel->GetHeight() / factor ) + { + overview_exists = true; + } + } + + if (overview_exists == false) + { +/* -------------------------------------------------------------------- */ +/* Create the overview as a tiled image layer. */ +/* -------------------------------------------------------------------- */ + int virtual_image = + bm->CreateVirtualImageFile( channel->GetWidth() / factor, + channel->GetHeight() / factor, + blocksize, blocksize, + channel->GetType(), compression ); + +/* -------------------------------------------------------------------- */ +/* Attach reference to this overview as metadata. */ +/* -------------------------------------------------------------------- */ + char overview_md_value[128]; + char overview_md_key[128]; + + sprintf( overview_md_key, "_Overview_%d", factor ); + sprintf( overview_md_value, "%d 0 %s",virtual_image,resampling.c_str()); + + channel->SetMetadataValue( overview_md_key, overview_md_value ); + } + +/* -------------------------------------------------------------------- */ +/* Force channel to invalidate it's loaded overview list. */ +/* -------------------------------------------------------------------- */ + dynamic_cast(channel)->InvalidateOverviewInfo(); + } +} + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/cpcidskfile.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/cpcidskfile.h new file mode 100644 index 000000000..af5af91ae --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/cpcidskfile.h @@ -0,0 +1,161 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CPCIDSKFile class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PRIV_CPCIDSKFILE_H +#define __INCLUDE_PRIV_CPCIDSKFILE_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_buffer.h" +#include "pcidsk_file.h" +#include "pcidsk_mutex.h" +#include "pcidsk_interfaces.h" +#include "core/metadataset.h" +#include "core/protectedfile.h" + +#include +#include + +namespace PCIDSK +{ + class PCIDSKChannel; + class PCIDSKSegment; + class PCIDSKInterfaces; +/************************************************************************/ +/* CPCIDSKFile */ +/************************************************************************/ + class CPCIDSKFile : public PCIDSKFile + { + friend PCIDSKFile PCIDSK_DLL *Open( std::string filename, + std::string access, const PCIDSKInterfaces *interfaces ); + public: + + CPCIDSKFile( std::string filename ); + virtual ~CPCIDSKFile(); + + virtual PCIDSKInterfaces *GetInterfaces() { return &interfaces; } + + PCIDSKChannel *GetChannel( int band ); + PCIDSKSegment *GetSegment( int segment ); + std::vector GetSegments(); + + PCIDSKSegment *GetSegment( int type, std::string name, + int previous = 0 ); + int CreateSegment( std::string name, std::string description, + eSegType seg_type, int data_blocks ); + void DeleteSegment( int segment ); + void CreateOverviews( int chan_count, int *chan_list, + int factor, std::string resampling ); + + int GetWidth() const { return width; } + int GetHeight() const { return height; } + int GetChannels() const { return channel_count; } + std::string GetInterleaving() const { return interleaving; } + bool GetUpdatable() const { return updatable; } + uint64 GetFileSize() const { return file_size; } + + // the following are only for pixel interleaved IO + int GetPixelGroupSize() const { return pixel_group_size; } + void *ReadAndLockBlock( int block_index, int xoff=-1, int xsize=-1 ); + void UnlockBlock( bool mark_dirty = false ); + void WriteBlock( int block_index, void *buffer ); + void FlushBlock(); + + void WriteToFile( const void *buffer, uint64 offset, uint64 size ); + void ReadFromFile( void *buffer, uint64 offset, uint64 size ); + + std::string GetFilename() const { return base_filename; } + + void GetIODetails( void ***io_handle_pp, Mutex ***io_mutex_pp, + std::string filename="", bool writable=false ); + + bool GetEDBFileDetails( EDBFile** file_p, Mutex **io_mutex_p, + std::string filename ); + + std::string GetMetadataValue( const std::string& key ) + { return metadata.GetMetadataValue(key); } + void SetMetadataValue( const std::string& key, const std::string& value ) + { metadata.SetMetadataValue(key,value); } + std::vector GetMetadataKeys() + { return metadata.GetMetadataKeys(); } + + void Synchronize(); + + // not exposed to applications. + void ExtendFile( uint64 blocks_requested, bool prezero = false ); + void ExtendSegment( int segment, uint64 blocks_to_add, + bool prezero = false ); + void MoveSegmentToEOF( int segment ); + + private: + PCIDSKInterfaces interfaces; + + void InitializeFromHeader(); + + std::string base_filename; + + int width; + int height; + int channel_count; + std::string interleaving; + + std::vector channels; + + int segment_count; + uint64 segment_pointers_offset; + PCIDSKBuffer segment_pointers; + + std::vector segments; + + // pixel interleaved info. + uint64 block_size; // pixel interleaved scanline size. + int pixel_group_size; // pixel interleaved pixel_offset value. + uint64 first_line_offset; + + int last_block_index; + bool last_block_dirty; + int last_block_xoff; + int last_block_xsize; + void *last_block_data; + Mutex *last_block_mutex; + + void *io_handle; + Mutex *io_mutex; + bool updatable; + + uint64 file_size; // in blocks. + + // register of open external raw files. + std::vector file_list; + + // register of open external databasefiles + std::vector edb_file_list; + + MetadataSet metadata; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_PRIV_CPCIDSKFILE_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/edb_pcidsk.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/edb_pcidsk.cpp new file mode 100644 index 000000000..50a82ad50 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/edb_pcidsk.cpp @@ -0,0 +1,183 @@ +/****************************************************************************** + * + * Purpose: Implementation of the EDB interface that works only for + * links to another PCIDSK database. This is mostly useful + * for testing - practical use is minimal. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "core/pcidsk_utils.h" +#include "pcidsk_exception.h" +#include "pcidsk_edb.h" +#include "pcidsk.h" +#include +#include + +using namespace PCIDSK; + +/************************************************************************/ +/* ==================================================================== */ +/* PCIDSK_EDBFile */ +/* ==================================================================== */ +/************************************************************************/ + +class PCIDSK_EDBFile : public EDBFile +{ + mutable PCIDSKFile *file; + +public: + + PCIDSK_EDBFile( PCIDSKFile *file_in ) { file = file_in; } + ~PCIDSK_EDBFile() { Close(); } + + int Close() const; + int GetWidth() const; + int GetHeight() const; + int GetChannels() const; + int GetBlockWidth(int channel ) const; + int GetBlockHeight(int channel ) const; + eChanType GetType(int channel ) const; + int ReadBlock(int channel, + int block_index, void *buffer, + int win_xoff, int win_yoff, + int win_xsize, int win_ysize ); + int WriteBlock( int channel, int block_index, void *buffer); +}; + +/************************************************************************/ +/* DefaultOpenEDB() */ +/************************************************************************/ + +EDBFile *PCIDSK::DefaultOpenEDB( std::string filename, std::string access ) + +{ + // it would be nice to be able to pass in an appropriate PCIDSKInterface! + + PCIDSKFile *file = PCIDSK::Open( filename, access, NULL ); + + return new PCIDSK_EDBFile( file ); +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int PCIDSK_EDBFile::Close() const + +{ + if( file != NULL ) + { + delete file; + file = NULL; + } + + return 1; +} + +/************************************************************************/ +/* GetWidth() */ +/************************************************************************/ + +int PCIDSK_EDBFile::GetWidth() const + +{ + return file->GetWidth(); +} + +/************************************************************************/ +/* GetHeight() */ +/************************************************************************/ + +int PCIDSK_EDBFile::GetHeight() const + +{ + return file->GetHeight(); +} + +/************************************************************************/ +/* GetChannels() */ +/************************************************************************/ + +int PCIDSK_EDBFile::GetChannels() const + +{ + return file->GetChannels(); +} + +/************************************************************************/ +/* GetBlockWidth() */ +/************************************************************************/ + +int PCIDSK_EDBFile::GetBlockWidth( int channel ) const + +{ + return file->GetChannel(channel)->GetBlockWidth(); +} + +/************************************************************************/ +/* GetBlockHeight() */ +/************************************************************************/ + +int PCIDSK_EDBFile::GetBlockHeight( int channel ) const + +{ + return file->GetChannel(channel)->GetBlockHeight(); +} + +/************************************************************************/ +/* GetType() */ +/************************************************************************/ + +eChanType PCIDSK_EDBFile::GetType( int channel ) const +{ + return file->GetChannel(channel)->GetType(); +} + +/************************************************************************/ +/* ReadBlock() */ +/************************************************************************/ + +int PCIDSK_EDBFile::ReadBlock( int channel, + int block_index, void *buffer, + int win_xoff, int win_yoff, + int win_xsize, int win_ysize ) + +{ + return + file->GetChannel(channel)->ReadBlock( block_index, buffer, + win_xoff, win_yoff, + win_xsize, win_ysize ); +} + +/************************************************************************/ +/* WriteBlock() */ +/************************************************************************/ + +int PCIDSK_EDBFile::WriteBlock( int channel, int block_index, void *buffer) + +{ + return file->GetChannel(channel)->WriteBlock( block_index, buffer ); +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/libjpeg_io.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/libjpeg_io.cpp new file mode 100644 index 000000000..91cc2d6d7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/libjpeg_io.cpp @@ -0,0 +1,199 @@ +/****************************************************************************** + * + * Purpose: Implementation of the JPEG compression/decompression based + * on libjpeg. This implements functions suitable for use + * as jpeg interfaces in the PCIDSKInterfaces class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "core/pcidsk_utils.h" +#include "pcidsk_exception.h" +#include +#include + +#include "cpl_port.h" + +using namespace PCIDSK; + +#if defined(HAVE_LIBJPEG) + +extern "C" { +#include "jpeglib.h" +} + +static void _DummyMgrMethod( j_compress_ptr /*pUnused*/ ) {} +static void _DummySrcMgrMethod( j_decompress_ptr /*pUnused*/ ) {} + +/************************************************************************/ +/* JpegError() */ +/* */ +/* Handle errors generated by the IJG library. We treat all */ +/* errors as fatal at this point. Future handling may be */ +/* improved by overriding other methods. */ +/************************************************************************/ + +static void JpegError(j_common_ptr cinfo) +{ + char buf[256]; + + cinfo->err->format_message(cinfo, buf); + ThrowPCIDSKException( "%s", buf ); +} + +/************************************************************************/ +/* LibJPEG_DecompressBlock() */ +/************************************************************************/ + +void PCIDSK::LibJPEG_DecompressBlock( + uint8 *src_data, int src_bytes, uint8 *dst_data, CPL_UNUSED int dst_bytes, + int xsize, int ysize, eChanType CPL_UNUSED pixel_type ) +{ + struct jpeg_decompress_struct sJCompInfo; + struct jpeg_source_mgr sSrcMgr; + struct jpeg_error_mgr sErrMgr; + + int i; + +/* -------------------------------------------------------------------- */ +/* Setup the buffer we will compress into. We make it pretty */ +/* big to ensure there is space. The calling function will */ +/* free it as soon as it is done so this shouldn't hurt much. */ +/* -------------------------------------------------------------------- */ + sSrcMgr.init_source = _DummySrcMgrMethod; + sSrcMgr.fill_input_buffer = (boolean (*)(j_decompress_ptr)) + _DummyMgrMethod; + sSrcMgr.skip_input_data = (void (*)(j_decompress_ptr, long)) + _DummyMgrMethod; + sSrcMgr.resync_to_restart = jpeg_resync_to_restart; + sSrcMgr.term_source = _DummySrcMgrMethod; + + sSrcMgr.next_input_byte = src_data; + sSrcMgr.bytes_in_buffer = src_bytes; + +/* -------------------------------------------------------------------- */ +/* Setup JPEG Decompression */ +/* -------------------------------------------------------------------- */ + jpeg_create_decompress(&sJCompInfo); + + sJCompInfo.src = &sSrcMgr; + sJCompInfo.err = jpeg_std_error(&sErrMgr); + sJCompInfo.err->output_message = JpegError; + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + jpeg_read_header( &sJCompInfo, TRUE ); + if (sJCompInfo.image_width != (unsigned int)xsize || + sJCompInfo.image_height != (unsigned int)ysize) + { + ThrowPCIDSKException("Tile Size wrong in LibJPEG_DecompressTile(), got %dx%d, expected %dx%d.", + sJCompInfo.image_width, + sJCompInfo.image_height, + xsize, ysize ); + } + + sJCompInfo.out_color_space = JCS_GRAYSCALE; + jpeg_start_decompress(&sJCompInfo); + +/* -------------------------------------------------------------------- */ +/* Read each of the scanlines. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < ysize; i++ ) + { + uint8 *line_data = dst_data + i*xsize; + jpeg_read_scanlines( &sJCompInfo, (JSAMPARRAY) &line_data, 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + jpeg_finish_decompress( &sJCompInfo ); + jpeg_destroy_decompress( &sJCompInfo ); +} + +/************************************************************************/ +/* LibJPEG_CompressBlock() */ +/************************************************************************/ + +void PCIDSK::LibJPEG_CompressBlock( + uint8 *src_data, CPL_UNUSED int src_bytes, uint8 *dst_data, int &dst_bytes, + int xsize, int ysize, CPL_UNUSED eChanType pixel_type, int quality ) +{ + struct jpeg_compress_struct sJCompInfo; + struct jpeg_destination_mgr sDstMgr; + struct jpeg_error_mgr sErrMgr; + + int i; + +/* -------------------------------------------------------------------- */ +/* Setup the buffer we will compress into. */ +/* -------------------------------------------------------------------- */ + sDstMgr.next_output_byte = dst_data; + sDstMgr.free_in_buffer = dst_bytes; + sDstMgr.init_destination = _DummyMgrMethod; + sDstMgr.empty_output_buffer = (boolean (*)(j_compress_ptr)) + _DummyMgrMethod; + sDstMgr.term_destination = _DummyMgrMethod; + +/* -------------------------------------------------------------------- */ +/* Setup JPEG Compression */ +/* -------------------------------------------------------------------- */ + jpeg_create_compress(&sJCompInfo); + + sJCompInfo.dest = &sDstMgr; + sJCompInfo.err = jpeg_std_error(&sErrMgr); + sJCompInfo.err->output_message = JpegError; + + sJCompInfo.image_width = xsize; + sJCompInfo.image_height = ysize; + sJCompInfo.input_components = 1; + sJCompInfo.in_color_space = JCS_GRAYSCALE; + + jpeg_set_defaults(&sJCompInfo); + jpeg_set_quality(&sJCompInfo, quality, TRUE ); + jpeg_start_compress(&sJCompInfo, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Write all the scanlines at once. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < ysize; i++ ) + { + uint8 *pabyLine = src_data + i*xsize; + + jpeg_write_scanlines( &sJCompInfo, (JSAMPARRAY)&pabyLine, 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + jpeg_finish_compress( &sJCompInfo ); + + dst_bytes = dst_bytes - sDstMgr.free_in_buffer; + + jpeg_destroy_compress( &sJCompInfo ); +} + +#endif /* defined(HAVE_LIBJPEG) */ diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/metadataset.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/metadataset.h new file mode 100644 index 000000000..2c2f63bd0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/metadataset.h @@ -0,0 +1,67 @@ +/****************************************************************************** + * + * Purpose: Declaration of the MetadataSet class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PRIV_METADATASET_H +#define __INCLUDE_PRIV_METADATASET_H + +#include "pcidsk_config.h" +#include "pcidsk_file.h" +#include +#include +#include + +namespace PCIDSK +{ + class CPCIDSKFile; + /************************************************************************/ + /* MetadataSet */ + /************************************************************************/ + + class MetadataSet + { + public: + MetadataSet(); + ~MetadataSet(); + + void Initialize( PCIDSKFile *file, const std::string& group, int id ); + std::string GetMetadataValue( const std::string& key ); + void SetMetadataValue( const std::string& key, const std::string& value ); + std::vector GetMetadataKeys(); + + private: + PCIDSKFile *file; + + bool loaded; + std::map md_set; + + std::string group; + int id; + + void Load(); + }; + +} // end namespace PCIDSK +#endif // __INCLUDE_PRIV_METADATASET_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/metadataset_p.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/metadataset_p.cpp new file mode 100644 index 000000000..31da995fd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/metadataset_p.cpp @@ -0,0 +1,171 @@ +/****************************************************************************** + * + * Purpose: Implementation of the MetadataSet class. This is a container + * for a set of metadata, and used by the file, channel and segment + * classes to manage metadata for themselves. It is not public + * to SDK users. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_exception.h" +#include "core/metadataset.h" + +#include "segment/metadatasegment.h" + +#include + +using namespace PCIDSK; + +/************************************************************************/ +/* MetadataSet() */ +/************************************************************************/ + +MetadataSet::MetadataSet() + + +{ + this->file = NULL; + id = -1; + loaded = false; +} + +/************************************************************************/ +/* ~MetadataSet() */ +/************************************************************************/ + +MetadataSet::~MetadataSet() + +{ +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void MetadataSet::Initialize( PCIDSKFile *file, const std::string& group, int id ) + +{ + this->file = file; + this->group = group; + this->id = id; +} + +/************************************************************************/ +/* Load() */ +/************************************************************************/ + +void MetadataSet::Load() + +{ + if( loaded ) + return; + + // This legitimately occurs in some situations, such for overview channel + // objects. + if( file == NULL ) + { + loaded = true; + return; + } + + PCIDSKSegment *seg = file->GetSegment( SEG_SYS , "METADATA"); + + if( seg == NULL ) + { + loaded = true; + return; + } + + MetadataSegment *md_seg = dynamic_cast( seg ); + + md_seg->FetchGroupMetadata( group.c_str(), id, md_set ); + loaded = true; +} + +/************************************************************************/ +/* GetMetadataValue() */ +/************************************************************************/ + +std::string MetadataSet::GetMetadataValue( const std::string& key ) + +{ + if( !loaded ) + Load(); + + if( md_set.count(key) == 0 ) + return ""; + else + return md_set[key]; +} + +/************************************************************************/ +/* SetMetadataValue() */ +/************************************************************************/ + +void MetadataSet::SetMetadataValue( const std::string& key, const std::string& value ) +{ + if( !loaded ) + Load(); + + if( file == NULL ) + { + ThrowPCIDSKException( "Attempt to set metadata on an unassociated MetadataSet, likely an overview channel." ); + } + + md_set[key] = value; + + PCIDSKSegment *seg = file->GetSegment( SEG_SYS , "METADATA"); + + if( seg == NULL ) + { + file->CreateSegment( "METADATA", + "Please do not modify this metadata segment.", + SEG_SYS, 0 ); + seg = file->GetSegment( SEG_SYS , "METADATA"); + } + + MetadataSegment *md_seg = dynamic_cast( seg ); + + md_seg->SetGroupMetadataValue( group.c_str(), id, key, value ); +} + +/************************************************************************/ +/* GetMetadataKeys() */ +/************************************************************************/ + +std::vector MetadataSet::GetMetadataKeys() +{ + if( !loaded ) + Load(); + + std::vector keys; + std::map::iterator it; + + for( it = md_set.begin(); it != md_set.end(); it++ ) + { + keys.push_back( (*it).first ); + } + + return keys; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/mutexholder.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/mutexholder.h new file mode 100644 index 000000000..707c31962 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/mutexholder.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * + * Purpose: MutexHolder class. Helper class for controlling the acquisition + * and release of a Mutex based on current context. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_CORE_MUTEXHOLDER_H +#define __INCLUDE_CORE_MUTEXHOLDER_H + +#include "pcidsk_mutex.h" + +namespace PCIDSK +{ + /************************************************************************/ + /* MutexHolder */ + /************************************************************************/ + class PCIDSK_DLL MutexHolder + { + public: + MutexHolder( Mutex *mutex ) + { + this->mutex = mutex; + if( mutex != NULL ) + mutex->Acquire(); + } + ~MutexHolder() + { + if( mutex ) + mutex->Release(); + } + + private: + Mutex *mutex; + + }; + +} //end namespace PCIDSK + +#endif // __INCLUDE_CORE_MUTEXHOLDER_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidsk_pubutils.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidsk_pubutils.cpp new file mode 100644 index 000000000..1d6aa1254 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidsk_pubutils.cpp @@ -0,0 +1,234 @@ +/****************************************************************************** + * + * Purpose: Various public (documented) utility functions. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_exception.h" +#include "core/pcidsk_utils.h" +#include +#include + +using namespace PCIDSK; + +/************************************************************************/ +/* DataTypeSize() */ +/************************************************************************/ + +/** + * Return size of data type. + * + * Note that type CHN_BIT exists to represent one bit backed data from + * bitmap segments, but because the return of this functions is measured + * in bytes, the size of a CHN_BIT pixel cannot be properly returned (one + * eighth of a byte), so "1" is returned instead. + * + * @param chan_type the channel type enumeration value. + * + * @return the size of the passed data type in bytes, or zero for unknown + * values. + */ + +int PCIDSK::DataTypeSize( eChanType chan_type ) + +{ + switch( chan_type ) + { + case CHN_8U: + return 1; + case CHN_16S: + return 2; + case CHN_16U: + return 2; + case CHN_32R: + return 4; + case CHN_C16U: + return 4; + case CHN_C16S: + return 4; + case CHN_C32R: + return 8; + case CHN_BIT: + return 1; // not really accurate! + default: + return 0; + } +} + +/************************************************************************/ +/* DataTypeName() */ +/************************************************************************/ + +/** + * Return name for the data type. + * + * The returned values are suitable for display to people, and matches + * the portion of the name after the underscore (ie. "8U" for CHN_8U. + * + * @param chan_type the channel type enumeration value to be translated. + * + * @return a string representing the data type. + */ + +std::string PCIDSK::DataTypeName( eChanType chan_type ) + +{ + switch( chan_type ) + { + case CHN_8U: + return "8U"; + case CHN_16S: + return "16S"; + case CHN_16U: + return "16U"; + case CHN_32R: + return "32R"; + case CHN_C16U: + return "C16U"; + case CHN_C16S: + return "C16S"; + case CHN_C32R: + return "C32R"; + case CHN_BIT: + return "BIT"; + default: + return "UNK"; + } +} + +/************************************************************************/ +/* GetDataTypeFromName() */ +/************************************************************************/ + +/** + * @brief Return the segment type code based on the contents of type_name + * + * @param the type name, as a string + * + * @return the channel type code + */ +eChanType PCIDSK::GetDataTypeFromName(std::string const& type_name) +{ + if (type_name.find("8U") != std::string::npos) { + return CHN_8U; + } else if (type_name.find("C16U") != std::string::npos) { + return CHN_C16U; + } else if (type_name.find("C16S") != std::string::npos) { + return CHN_C16S; + } else if (type_name.find("C32R") != std::string::npos) { + return CHN_C32R; + } else if (type_name.find("16U") != std::string::npos) { + return CHN_16U; + } else if (type_name.find("16S") != std::string::npos) { + return CHN_16S; + } else if (type_name.find("32R") != std::string::npos) { + return CHN_32R; + } else if (type_name.find("BIT") != std::string::npos) { + return CHN_BIT; + } else { + return CHN_UNKNOWN; + } +} + +/************************************************************************/ +/* IsDataTypeComplex() */ +/************************************************************************/ + +/** + * @brief Return whether or not the data type is complex + * + * @param the type + * + * @return true if the data type is complex, false otherwise + */ +bool PCIDSK::IsDataTypeComplex(eChanType type) +{ + switch(type) + { + case CHN_C32R: + case CHN_C16U: + case CHN_C16S: + return true; + default: + return false; + } +} + +/************************************************************************/ +/* SegmentTypeName() */ +/************************************************************************/ + +/** + * Return name for segment type. + * + * Returns a short name for the segment type code passed in. This is normally + * the portion of the enumeration name that comes after the underscore - ie. + * "BIT" for SEG_BIT. + * + * @param type the segment type code. + * + * @return the string for the segment type. + */ + +std::string PCIDSK::SegmentTypeName( eSegType type ) + +{ + switch( type ) + { + case SEG_BIT: + return "BIT"; + case SEG_VEC: + return "VEC"; + case SEG_SIG: + return "SIG"; + case SEG_TEX: + return "TEX"; + case SEG_GEO: + return "GEO"; + case SEG_ORB: + return "ORB"; + case SEG_LUT: + return "LUT"; + case SEG_PCT: + return "PCT"; + case SEG_BLUT: + return "BLUT"; + case SEG_BPCT: + return "BPCT"; + case SEG_BIN: + return "BIN"; + case SEG_ARR: + return "ARR"; + case SEG_SYS: + return "SYS"; + case SEG_GCPOLD: + return "GCPOLD"; + case SEG_GCP2: + return "GCP2"; + default: + return "UNKNOWN"; + } +} + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidsk_utils.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidsk_utils.cpp new file mode 100644 index 000000000..a3f5231e3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidsk_utils.cpp @@ -0,0 +1,688 @@ +/****************************************************************************** + * + * Purpose: Various private (undocumented) utility functions. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_exception.h" +#include "pcidsk_georef.h" +#include "pcidsk_io.h" +#include "core/pcidsk_utils.h" +#include +#include +#include +#include +#include +#include + +using namespace PCIDSK; + +#if defined(_MSC_VER) && (_MSC_VER < 1500) +# define vsnprintf _vsnprintf +#endif + +/************************************************************************/ +/* GetCurrentDateTime() */ +/************************************************************************/ + +// format we want: "HH:MM DDMMMYYYY \0" + +#include +#include + +void PCIDSK::GetCurrentDateTime( char *out_time ) + +{ + time_t clock; + char ctime_out[25]; + + time( &clock ); + strncpy( ctime_out, ctime(&clock), 24 ); // TODO: reentrance issue? + + // ctime() products: "Wed Jun 30 21:49:08 1993\n" + + ctime_out[24] = '\0'; + + out_time[0] = ctime_out[11]; + out_time[1] = ctime_out[12]; + out_time[2] = ':'; + out_time[3] = ctime_out[14]; + out_time[4] = ctime_out[15]; + out_time[5] = ' '; + out_time[6] = ctime_out[8]; + out_time[7] = ctime_out[9]; + out_time[8] = ctime_out[4]; + out_time[9] = ctime_out[5]; + out_time[10] = ctime_out[6]; + out_time[11] = ctime_out[20]; + out_time[12] = ctime_out[21]; + out_time[13] = ctime_out[22]; + out_time[14] = ctime_out[23]; + out_time[15] = ' '; + out_time[16] = '\0'; +} + +/************************************************************************/ +/* UCaseStr() */ +/* */ +/* Force a string into upper case "in place". */ +/************************************************************************/ + +std::string &PCIDSK::UCaseStr( std::string &target ) + +{ + for( unsigned int i = 0; i < target.size(); i++ ) + { + if( islower(target[i]) ) + target[i] = (char) toupper(target[i]); + } + + return target; +} + +/************************************************************************/ +/* atouint64() */ +/************************************************************************/ + +uint64 PCIDSK::atouint64( const char *str_value ) + +{ +#if defined(__MSVCRT__) || defined(_MSC_VER) + return (uint64) _atoi64( str_value ); +#else + return (uint64) atoll( str_value ); +#endif +} + +/************************************************************************/ +/* atoint64() */ +/************************************************************************/ + +int64 PCIDSK::atoint64( const char *str_value ) + +{ +#if defined(__MSVCRT__) || defined(_MSC_VER) + return (int64) _atoi64( str_value ); +#else + return (int64) atoll( str_value ); +#endif +} + +/************************************************************************/ +/* SwapPixels() */ +/************************************************************************/ +/** + * @brief Perform an endianess swap for a given buffer of pixels + * + * Baed on the provided data type, do an appropriate endianess swap for + * a buffer of pixels. Deals with the Complex case specially, in + * particular. + * + * @param data the pixels to be swapped + * @param type the data type of the pixels + * @param count the count of pixels (not bytes, words, etc.) + */ +void PCIDSK::SwapPixels(void* const data, + const eChanType type, + const std::size_t count) +{ + switch(type) { + case CHN_8U: + case CHN_16U: + case CHN_16S: + case CHN_32R: + SwapData(data, DataTypeSize(type), count); + break; + case CHN_C16U: + case CHN_C16S: + case CHN_C32R: + SwapData(data, DataTypeSize(type) / 2, count * 2); + break; + default: + ThrowPCIDSKException("Unknown data type passed to SwapPixels." + "This is a software bug. Please contact your vendor."); + } +} + +/************************************************************************/ +/* SwapData() */ +/************************************************************************/ + +void PCIDSK::SwapData( void* const data, const int size, const int wcount ) + +{ + uint8* data8 = reinterpret_cast(data); + std::size_t count = wcount; + + if( size == 2 ) + { + uint8 t; + + for( ; count; count-- ) + { + t = data8[0]; + data8[0] = data8[1]; + data8[1] = t; + + data8 += 2; + } + } + else if( size == 1 ) + /* do nothing */; + else if( size == 4 ) + { + uint8 t; + + for( ; count; count-- ) + { + t = data8[0]; + data8[0] = data8[3]; + data8[3] = t; + + t = data8[1]; + data8[1] = data8[2]; + data8[2] = t; + + data8 += 4; + } + } + else if( size == 8 ) + { + uint8 t; + + for( ; count; count-- ) + { + t = data8[0]; + data8[0] = data8[7]; + data8[7] = t; + + t = data8[1]; + data8[1] = data8[6]; + data8[6] = t; + + t = data8[2]; + data8[2] = data8[5]; + data8[5] = t; + + t = data8[3]; + data8[3] = data8[4]; + data8[4] = t; + + data8 += 8; + } + } + else + ThrowPCIDSKException( "Unsupported data size in SwapData()" ); +} + +/************************************************************************/ +/* BigEndianSystem() */ +/************************************************************************/ + +bool PCIDSK::BigEndianSystem() + +{ + unsigned short test_value = 1; + char test_char_value[2]; + + memcpy( test_char_value, &test_value, 2 ); + + return test_char_value[0] == 0; +} + + +/************************************************************************/ +/* ParseTileFormat() */ +/* */ +/* Parse blocksize and compression out of a TILED interleaving */ +/* string as passed to the Create() function or stored in */ +/* _DBLayout metadata. */ +/************************************************************************/ + +void PCIDSK::ParseTileFormat( std::string full_text, + int &block_size, std::string &compression ) + +{ + compression = "NONE"; + block_size = 127; + + UCaseStr( full_text ); + +/* -------------------------------------------------------------------- */ +/* Only operate on tiled stuff. */ +/* -------------------------------------------------------------------- */ + if( strncmp(full_text.c_str(),"TILED",5) != 0 ) + return; + +/* -------------------------------------------------------------------- */ +/* Do we have a block size? */ +/* -------------------------------------------------------------------- */ + const char *next_text = full_text.c_str() + 5; + + if( isdigit(*next_text) ) + { + block_size = atoi(next_text); + while( isdigit(*next_text) ) + next_text++; + } + + while( *next_text == ' ' ) + next_text++; + +/* -------------------------------------------------------------------- */ +/* Do we have a compression type? */ +/* -------------------------------------------------------------------- */ + if( *next_text != '\0' ) + { + compression = next_text; + if (compression == "NO_WARNINGS") + compression = ""; + else if( compression != "RLE" + && strncmp(compression.c_str(),"JPEG",4) != 0 + && compression != "NONE" + && compression != "QUADTREE" ) + { + ThrowPCIDSKException( "Unsupported tile compression scheme '%s' requested.", + compression.c_str() ); + } + } +} + +/************************************************************************/ +/* pci_strcasecmp() */ +/************************************************************************/ + +int PCIDSK::pci_strcasecmp( const char *string1, const char *string2 ) + +{ + int i; + + for( i = 0; string1[i] != '\0' && string2[i] != '\0'; i++ ) + { + char c1 = string1[i]; + char c2 = string2[i]; + + if( islower(c1) ) + c1 = (char) toupper(c1); + if( islower(c2) ) + c2 = (char) toupper(c2); + + if( c1 < c2 ) + return -1; + else if( c1 > c2 ) + return 1; + } + + if( string1[i] == '\0' && string2[i] == '\0' ) + return 0; + else if( string1[i] == '\0' ) + return 1; + else + return -1; +} + +/************************************************************************/ +/* pci_strncasecmp() */ +/************************************************************************/ + +int PCIDSK::pci_strncasecmp( const char *string1, const char *string2, int len ) + +{ + int i; + + for( i = 0; i < len; i++ ) + { + if( string1[i] == '\0' && string2[i] == '\0' ) + return 0; + else if( string1[i] == '\0' ) + return 1; + else if( string2[i] == '\0' ) + return -1; + + char c1 = string1[i]; + char c2 = string2[i]; + + if( islower(c1) ) + c1 = (char) toupper(c1); + if( islower(c2) ) + c2 = (char) toupper(c2); + + if( c1 < c2 ) + return -1; + else if( c1 > c2 ) + return 1; + } + + return 0; +} + +/************************************************************************/ +/* ProjParmsFromText() */ +/* */ +/* function to turn a ProjParms string (17 floating point */ +/* numbers) into an array, as well as attaching the units code */ +/* derived from the geosys string. */ +/************************************************************************/ + +std::vector PCIDSK::ProjParmsFromText( std::string geosys, + std::string sparms ) + +{ + std::vector dparms; + const char *next = sparms.c_str(); + + for( next = sparms.c_str(); *next != '\0'; ) + { + dparms.push_back( CPLAtof(next) ); + + // move past this token + while( *next != '\0' && *next != ' ' ) + next++; + + // move past white space. + while( *next != '\0' && *next == ' ' ) + next++; + } + + dparms.resize(18); + + // This is rather iffy! + if( EQUALN(geosys.c_str(),"DEGREE",3) ) + dparms[17] = (double) (int) UNIT_DEGREE; + else if( EQUALN(geosys.c_str(),"MET",3) ) + dparms[17] = (double) (int) UNIT_METER; + else if( EQUALN(geosys.c_str(),"FOOT",4) ) + dparms[17] = (double) (int) UNIT_US_FOOT; + else if( EQUALN(geosys.c_str(),"FEET",4) ) + dparms[17] = (double) (int) UNIT_US_FOOT; + else if( EQUALN(geosys.c_str(),"INTL FOOT",5) ) + dparms[17] = (double) (int) UNIT_INTL_FOOT; + else if( EQUALN(geosys.c_str(),"SPCS",4) ) + dparms[17] = (double) (int) UNIT_METER; + else if( EQUALN(geosys.c_str(),"SPIF",4) ) + dparms[17] = (double) (int) UNIT_INTL_FOOT; + else if( EQUALN(geosys.c_str(),"SPAF",4) ) + dparms[17] = (double) (int) UNIT_US_FOOT; + else + dparms[17] = -1.0; /* unknown */ + + return dparms; +} + +/************************************************************************/ +/* ProjParmsToText() */ +/************************************************************************/ + +std::string PCIDSK::ProjParmsToText( std::vector dparms ) + +{ + unsigned int i; + std::string sparms; + + for( i = 0; i < 17; i++ ) + { + char value[64]; + double dvalue; + + if( i < dparms.size() ) + dvalue = dparms[i]; + else + dvalue = 0.0; + + if( dvalue == floor(dvalue) ) + sprintf( value, "%d", (int) dvalue ); + else + CPLsprintf( value, "%.15g", dvalue ); + + if( i > 0 ) + sparms += " "; + + sparms += value; + } + + return sparms; +} + +/************************************************************************/ +/* ExtractPath() */ +/* */ +/* Extract the directory path portion of the passed filename. */ +/* It assumes the last component is a filename and should not */ +/* be passed a bare path. The trailing directory delimeter is */ +/* removed from the result. The return result is an empty */ +/* string for a simple filename passed in with no directory */ +/* component. */ +/************************************************************************/ + +std::string PCIDSK::ExtractPath( std::string filename ) + +{ + int i; + + for( i = filename.size()-1; i >= 0; i-- ) + { + if( filename[i] == '\\' || filename[i] == '/' ) + break; + } + + if( i > 0 ) + return filename.substr(0,i); + else + return ""; +} + +/************************************************************************/ +/* MergeRelativePath() */ +/* */ +/* This attempts to take src_filename and make it relative to */ +/* the base of the file "base", if this evaluates to a new file */ +/* in the filesystem. It will not make any change if */ +/* src_filename appears to be absolute or if the altered path */ +/* does not resolve to a file in the filesystem. */ +/************************************************************************/ + +std::string PCIDSK::MergeRelativePath( const PCIDSK::IOInterfaces *io_interfaces, + std::string base, + std::string src_filename ) + +{ +/* -------------------------------------------------------------------- */ +/* Does src_filename appear to be absolute? */ +/* -------------------------------------------------------------------- */ + if( src_filename.size() == 0 ) + return src_filename; // we can't do anything with a blank. + else if( src_filename.size() > 2 && src_filename[1] == ':' ) + return src_filename; // has a drive letter? + else if( src_filename[0] == '/' || src_filename[0] == '\\' ) + return src_filename; // has a leading dir marker. + +/* -------------------------------------------------------------------- */ +/* Figure out what path split char we want to use. */ +/* -------------------------------------------------------------------- */ +#if defined(__MSVCRT__) || defined(_MSC_VER) + const static char path_split = '\\'; +#else + const static char path_split = '/'; +#endif + +/* -------------------------------------------------------------------- */ +/* Merge paths. */ +/* -------------------------------------------------------------------- */ + std::string base_path = ExtractPath( base ); + std::string result; + + if( base_path == "" ) + return src_filename; + + result = base_path; + result += path_split; + result += src_filename; + +/* -------------------------------------------------------------------- */ +/* Check if the target exists by this name. */ +/* -------------------------------------------------------------------- */ + try + { + void *hFile = io_interfaces->Open( result, "r" ); + // should throw an exception on failure. + io_interfaces->Close( hFile ); + return result; + } + catch( ... ) + { + return src_filename; + } +} + + +/************************************************************************/ +/* DefaultDebug() */ +/* */ +/* Default implementation of the Debug() output interface. */ +/************************************************************************/ + +void PCIDSK::DefaultDebug( const char * message ) + +{ + static bool initialized = false; + static bool enabled = false; + + if( !initialized ) + { + if( getenv( "PCIDSK_DEBUG" ) != NULL ) + enabled = true; + + initialized = true; + } + + if( enabled ) + std::cerr << message; +} + +/************************************************************************/ +/* vDebug() */ +/* */ +/* Helper function for Debug(). */ +/************************************************************************/ + +static void vDebug( void (*pfnDebug)(const char *), + const char *fmt, std::va_list args ) + +{ + std::string message; + +/* -------------------------------------------------------------------- */ +/* This implementation for platforms without vsnprintf() will */ +/* just plain fail if the formatted contents are too large. */ +/* -------------------------------------------------------------------- */ +#if defined(MISSING_VSNPRINTF) + char *pszBuffer = (char *) malloc(30000); + if( vsprintf( pszBuffer, fmt, args) > 29998 ) + { + message = "PCIDSK::Debug() ... buffer overrun."; + } + else + message = pszBuffer; + + free( pszBuffer ); + +/* -------------------------------------------------------------------- */ +/* This should grow a big enough buffer to hold any formatted */ +/* result. */ +/* -------------------------------------------------------------------- */ +#else + char szModestBuffer[500]; + int nPR; + va_list wrk_args; + +#ifdef va_copy + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + + nPR = vsnprintf( szModestBuffer, sizeof(szModestBuffer), fmt, + wrk_args ); + if( nPR == -1 || nPR >= (int) sizeof(szModestBuffer)-1 ) + { + int nWorkBufferSize = 2000; + char *pszWorkBuffer = (char *) malloc(nWorkBufferSize); + +#ifdef va_copy + va_end( wrk_args ); + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + while( (nPR=vsnprintf( pszWorkBuffer, nWorkBufferSize, fmt, wrk_args)) + >= nWorkBufferSize-1 + || nPR == -1 ) + { + nWorkBufferSize *= 4; + pszWorkBuffer = (char *) realloc(pszWorkBuffer, + nWorkBufferSize ); +#ifdef va_copy + va_end( wrk_args ); + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + } + message = pszWorkBuffer; + free( pszWorkBuffer ); + } + else + { + message = szModestBuffer; + } + va_end( wrk_args ); +#endif + +/* -------------------------------------------------------------------- */ +/* Forward the message. */ +/* -------------------------------------------------------------------- */ + pfnDebug( message.c_str() ); +} + +/************************************************************************/ +/* Debug() */ +/* */ +/* Function to write output to a debug stream if one is */ +/* enabled. This is intended to be widely called in the */ +/* library. */ +/************************************************************************/ + +void PCIDSK::Debug( void (*pfnDebug)(const char *), const char *fmt, ... ) + +{ + if( pfnDebug == NULL ) + return; + + std::va_list args; + + va_start( args, fmt ); + vDebug( pfnDebug, fmt, args ); + va_end( args ); +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidsk_utils.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidsk_utils.h new file mode 100644 index 000000000..2f553dbc2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidsk_utils.h @@ -0,0 +1,83 @@ +/****************************************************************************** + * + * Purpose: PCIDSK library utility functions - private + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_CORE_PCIDSK_UTILS_H +#define __INCLUDE_CORE_PCIDSK_UTILS_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include +#include + +namespace PCIDSK +{ + class IOInterfaces; + + /************************************************************************/ + /* Utility functions. */ + /************************************************************************/ + + std::string &UCaseStr( std::string & ); + uint64 atouint64( const char *); + int64 atoint64( const char *); + int pci_strcasecmp( const char *, const char * ); + int pci_strncasecmp( const char *, const char *, int ); + +#define EQUAL(x,y) (pci_strcasecmp(x,y) == 0) +#define EQUALN(x,y,n) (pci_strncasecmp(x,y,n) == 0) + + void SwapData( void* const data, const int size, const int wcount ); + bool BigEndianSystem(void); + void GetCurrentDateTime( char *out_datetime ); + + void ParseTileFormat( std::string full_text, int &block_size, + std::string &compression ); + void SwapPixels(void* const data, + const eChanType type, + const std::size_t count); + + std::vector ProjParmsFromText( std::string geosys, + std::string parms ); + std::string ProjParmsToText( std::vector ); + + std::string MergeRelativePath( const PCIDSK::IOInterfaces *, + std::string base, + std::string src_filename ); + std::string ExtractPath( std::string ); + + void LibJPEG_DecompressBlock( + uint8 *src_data, int src_bytes, uint8 *dst_data, int dst_bytes, + int xsize, int ysize, eChanType pixel_type ); + void LibJPEG_CompressBlock( + uint8 *src_data, int src_bytes, uint8 *dst_data, int &dst_bytes, + int xsize, int ysize, eChanType pixel_type, int quality ); + + void DefaultDebug( const char * ); + void Debug( void (*)(const char *), const char *fmt, ... ); + +} // end namespace PCIDSK + +#endif // __INCLUDE_CORE_PCIDSK_UTILS_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskbuffer.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskbuffer.cpp new file mode 100644 index 000000000..cffd4bb88 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskbuffer.cpp @@ -0,0 +1,291 @@ +/****************************************************************************** + * + * Purpose: Implementation of the PCIDSKBuffer class. This class is for + * convenient parsing and formatting of PCIDSK ASCII headers. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_buffer.h" +#include "pcidsk_exception.h" +#include "core/pcidsk_utils.h" + +#include +#include +#include +#include + +using namespace PCIDSK; + +#ifdef _MSC_VER +#ifndef snprintf +#define snprintf _snprintf +#endif // !defined(snprintf) +#endif + +/************************************************************************/ +/* PCIDSKBuffer() */ +/************************************************************************/ + +PCIDSKBuffer::PCIDSKBuffer( int size ) + +{ + buffer_size = 0; + buffer = NULL; + + if( size > 0 ) + SetSize( size ); +} + +/************************************************************************/ +/* PCIDSKBuffer() */ +/************************************************************************/ + +PCIDSKBuffer::PCIDSKBuffer( const char *src, int size ) + +{ + buffer_size = 0; + buffer = NULL; + + SetSize( size ); + memcpy( buffer, src, size ); +} + +/************************************************************************/ +/* ~PCIDSKBuffer() */ +/************************************************************************/ + +PCIDSKBuffer::~PCIDSKBuffer() + +{ + free( buffer ); +} + +/************************************************************************/ +/* SetSize() */ +/************************************************************************/ + +void PCIDSKBuffer::SetSize( int size ) + +{ + buffer_size = size; + if( buffer == NULL ) + buffer = (char *) malloc(size+1); + else + buffer = (char *) realloc(buffer,size+1); + + if( buffer == NULL ) + { + buffer_size = 0; + ThrowPCIDSKException( "Out of memory allocating %d byte PCIDSKBuffer.", + size ); + } + + buffer[size] = '\0'; +} + +/************************************************************************/ +/* Get() */ +/************************************************************************/ + +const char *PCIDSKBuffer::Get( int offset, int size ) const + +{ + Get( offset, size, work_field, 0 ); + return work_field.c_str(); +} + +/************************************************************************/ +/* Get() */ +/************************************************************************/ + +void PCIDSKBuffer::Get( int offset, int size, std::string &target, int unpad ) const + +{ + if( offset + size > buffer_size ) + ThrowPCIDSKException( "Get() past end of PCIDSKBuffer." ); + + if( unpad ) + { + while( size > 0 && buffer[offset+size-1] == ' ' ) + size--; + } + + target.assign( buffer + offset, size ); +} + +/************************************************************************/ +/* GetUInt64() */ +/************************************************************************/ + +uint64 PCIDSKBuffer::GetUInt64( int offset, int size ) const + +{ + std::string value_str; + + if( offset + size > buffer_size ) + ThrowPCIDSKException( "GetUInt64() past end of PCIDSKBuffer." ); + + value_str.assign( buffer + offset, size ); + + return atouint64(value_str.c_str()); +} + +/************************************************************************/ +/* GetInt() */ +/************************************************************************/ + +int PCIDSKBuffer::GetInt( int offset, int size ) const + +{ + std::string value_str; + + if( offset + size > buffer_size ) + ThrowPCIDSKException( "GetInt() past end of PCIDSKBuffer." ); + + value_str.assign( buffer + offset, size ); + + return atoi(value_str.c_str()); +} + +/************************************************************************/ +/* GetDouble() */ +/************************************************************************/ + +double PCIDSKBuffer::GetDouble( int offset, int size ) const + +{ + std::string value_str; + + if( offset + size > buffer_size ) + ThrowPCIDSKException( "GetDouble() past end of PCIDSKBuffer." ); + + value_str.assign( buffer + offset, size ); + +/* -------------------------------------------------------------------- */ +/* PCIDSK uses FORTRAN 'D' format for doubles - convert to 'E' */ +/* (C style) before calling CPLAtof. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < size; i++ ) + { + if( value_str[i] == 'D' ) + value_str[i] = 'E'; + } +#ifdef PCIDSK_INTERNAL + return CPLAtof(value_str.c_str()); +#else + std::stringstream oStream; + oStream << value_str; + double dValue = 0.0; + oStream >> dValue; + + return dValue; +#endif +} + +/************************************************************************/ +/* Put() */ +/************************************************************************/ + +void PCIDSKBuffer::Put( const char *value, int offset, int size, bool null_term ) + +{ + if( offset + size > buffer_size ) + ThrowPCIDSKException( "Put() past end of PCIDSKBuffer." ); + + int v_size = strlen(value); + if( v_size > size ) + v_size = size; + + if( v_size < size ) + memset( buffer + offset, ' ', size ); + + memcpy( buffer + offset, value, v_size ); + + if (null_term) + { + *(buffer + offset + v_size) = '\0'; + } +} + +/************************************************************************/ +/* PutBin(double) */ +/************************************************************************/ + +void PCIDSKBuffer::PutBin(double value, int offset) +{ + const char* pszValue = (const char*)&value; + memcpy( buffer + offset, pszValue, 8 ); +} + +/************************************************************************/ +/* Put(uint64) */ +/************************************************************************/ + +void PCIDSKBuffer::Put( uint64 value, int offset, int size ) + +{ + char fmt[64]; + char wrk[128]; + + sprintf( fmt, "%%%d%sd", size, PCIDSK_FRMT_64_WITHOUT_PREFIX ); + sprintf( wrk, fmt, value ); + + Put( wrk, offset, size ); +} + +/************************************************************************/ +/* Put(double) */ +/************************************************************************/ + +void PCIDSKBuffer::Put( double value, int offset, int size, + const char *fmt ) + +{ + if( fmt == NULL ) + fmt = "%g"; + + char wrk[128]; + CPLsnprintf( wrk, 127, fmt, value ); + + char *exponent = strstr(wrk,"E"); + if( exponent != NULL ) + *exponent = 'D'; + + Put( wrk, offset, size ); +} + +/************************************************************************/ +/* operator=() */ +/************************************************************************/ + +PCIDSKBuffer &PCIDSKBuffer::operator=( const PCIDSKBuffer &src ) + +{ + SetSize( src.buffer_size ); + memcpy( buffer, src.buffer, buffer_size ); + + return *this; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskcreate.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskcreate.cpp new file mode 100644 index 000000000..ea574d992 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskcreate.cpp @@ -0,0 +1,528 @@ +/****************************************************************************** + * + * Purpose: Implementation of the Create() function to create new PCIDSK files. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "pcidsk.h" +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_exception.h" +#include "pcidsk_file.h" +#include "pcidsk_georef.h" +#include "core/pcidsk_utils.h" +#include "segment/sysblockmap.h" +#include +#include +#include +#include + +using namespace PCIDSK; + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +/** + * Create a PCIDSK (.pix) file. + * + * @param filename the name of the PCIDSK file to create. + * @param pixels the width of the new file in pixels. + * @param lines the height of the new file in scanlines. + * @param channel_count the number of channels to create. + * @param channel_types an array of types for all the channels, or NULL for + * all CHN_8U channels. + * @param options creation options (interleaving, etc) + * @param interfaces Either NULL to use default interfaces, or a pointer + * to a populated interfaces object. + * + * @return a pointer to a file object for accessing the PCIDSK file. + */ + +PCIDSKFile PCIDSK_DLL * +PCIDSK::Create( std::string filename, int pixels, int lines, + int channel_count, eChanType *channel_types, + std::string options, const PCIDSKInterfaces *interfaces ) + +{ +/* -------------------------------------------------------------------- */ +/* Use default interfaces if none are passed in. */ +/* -------------------------------------------------------------------- */ + PCIDSKInterfaces default_interfaces; + if( interfaces == NULL ) + interfaces = &default_interfaces; + +/* -------------------------------------------------------------------- */ +/* Default the channel types to all 8U if not provided. */ +/* -------------------------------------------------------------------- */ + std::vector default_channel_types; + + if( channel_types == NULL ) + { + default_channel_types.resize( channel_count+1, CHN_8U ); + channel_types = &(default_channel_types[0]); + } + +/* -------------------------------------------------------------------- */ +/* Validate parameters. */ +/* -------------------------------------------------------------------- */ + const char *interleaving = NULL; + std::string compression = "NONE"; + /* bool nozero = false; */ + bool nocreate = false; + bool externallink = false; + int blocksize = 127; + + UCaseStr( options ); + + if(strncmp(options.c_str(),"PIXEL",5) == 0 ) + interleaving = "PIXEL"; + else if( strncmp(options.c_str(),"BAND",4) == 0 ) + interleaving = "BAND"; + else if( strncmp(options.c_str(),"TILED",5) == 0 ) + { + interleaving = "FILE"; + ParseTileFormat( options, blocksize, compression ); + } + else if( strncmp(options.c_str(),"FILE",4) == 0 ) + { + if( strncmp(options.c_str(),"FILENOCREATE",12) == 0 ) + nocreate = true; + else if( strncmp(options.c_str(),"FILELINK",8) == 0 ) + { + nocreate = true; + externallink = true; + } + interleaving = "FILE"; + } + else + ThrowPCIDSKException( "PCIDSK::Create() options '%s' not recognised.", + options.c_str() ); +#if 0 + if( strstr(options.c_str(),"NOZERO") != NULL ) + nozero = true; +#endif + +/* -------------------------------------------------------------------- */ +/* Validate the channel types. */ +/* -------------------------------------------------------------------- */ + int channels[7] = {0,0,0,0,0,0,0}; + int chan_index; + bool regular = true; + + for( chan_index=0; chan_index < channel_count; chan_index++ ) + { + if( chan_index > 0 + && ((int) channel_types[chan_index]) + < ((int) channel_types[chan_index-1]) ) + regular = false; + + channels[((int) channel_types[chan_index])]++; + } + + if( !regular && strcmp(interleaving,"FILE") != 0 ) + { + ThrowPCIDSKException( + "Requested mixture of band types not supported for interleaving=%s.", + interleaving ); + } + +/* -------------------------------------------------------------------- */ +/* Create the file. */ +/* -------------------------------------------------------------------- */ + void *io_handle = interfaces->io->Open( filename, "w+" ); + + assert( io_handle != NULL ); + +/* ==================================================================== */ +/* Establish some key file layout information. */ +/* ==================================================================== */ + int image_header_start = 1; // in blocks + uint64 image_data_start, image_data_size=0; // in blocks + uint64 segment_ptr_start, segment_ptr_size=64; // in blocks + int pixel_group_size, line_size; // in bytes + int image_header_count = channel_count; + +/* -------------------------------------------------------------------- */ +/* Pixel interleaved. */ +/* -------------------------------------------------------------------- */ + if( strcmp(interleaving,"PIXEL") == 0 ) + { + pixel_group_size = + channels[0] + // CHN_8U + channels[1] * DataTypeSize(CHN_16U) + + channels[2] * DataTypeSize(CHN_16S) + + channels[3] * DataTypeSize(CHN_32R) + + channels[4] * DataTypeSize(CHN_C16U) + + channels[5] * DataTypeSize(CHN_C16S) + + channels[6] * DataTypeSize(CHN_C32R); + //channels[0] + channels[1]*2 + channels[2]*2 + channels[3]*4; + line_size = ((pixel_group_size * pixels + 511) / 512) * 512; + image_data_size = (((uint64)line_size) * lines) / 512; + + // TODO: Old code enforces a 1TB limit for some reason. + } + +/* -------------------------------------------------------------------- */ +/* Band interleaved. */ +/* -------------------------------------------------------------------- */ + else if( strcmp(interleaving,"BAND") == 0 ) + { + pixel_group_size = + channels[0] + // CHN_8U + channels[1] * DataTypeSize(CHN_16U) + + channels[2] * DataTypeSize(CHN_16S) + + channels[3] * DataTypeSize(CHN_32R) + + channels[4] * DataTypeSize(CHN_C16U) + + channels[5] * DataTypeSize(CHN_C16S) + + channels[6] * DataTypeSize(CHN_C32R); + // BAND interleaved bands are tightly packed. + image_data_size = + (((uint64)pixel_group_size) * pixels * lines + 511) / 512; + + // TODO: Old code enforces a 1TB limit for some reason. + } + +/* -------------------------------------------------------------------- */ +/* FILE/Tiled. */ +/* -------------------------------------------------------------------- */ + else if( strcmp(interleaving,"FILE") == 0 ) + { + // For some reason we reserve extra space, but only for FILE. + if( channel_count < 64 ) + image_header_count = 64; + + image_data_size = 0; + + // TODO: Old code enforces a 1TB limit on the fattest band. + } + +/* -------------------------------------------------------------------- */ +/* Place components. */ +/* -------------------------------------------------------------------- */ + segment_ptr_start = image_header_start + image_header_count*2; + image_data_start = segment_ptr_start + segment_ptr_size; + +/* ==================================================================== */ +/* Prepare the file header. */ +/* ==================================================================== */ + PCIDSKBuffer fh(512); + + char current_time[17]; + GetCurrentDateTime( current_time ); + + // Initialize everything to spaces. + fh.Put( "", 0, 512 ); + +/* -------------------------------------------------------------------- */ +/* File Type, Version, and Size */ +/* Notice: we get the first 4 characters from PCIVERSIONAME. */ +/* -------------------------------------------------------------------- */ + // FH1 - magic format string. + fh.Put( "PCIDSK", 0, 8 ); + + // FH2 - TODO: Allow caller to pass this in. + fh.Put( "SDK V1.0", 8, 8 ); + + // FH3 - file size later. + fh.Put( (image_data_start + image_data_size), 16, 16 ); + + // FH4 - 16 characters reserved - spaces. + + // FH5 - Description + fh.Put( filename.c_str(), 48, 64 ); + + // FH6 - Facility + fh.Put( "PCI Inc., Richmond Hill, Canada", 112, 32 ); + + // FH7.1 / FH7.2 - left blank (64+64 bytes @ 144) + + // FH8 Creation date/time + fh.Put( current_time, 272, 16 ); + + // FH9 Update date/time + fh.Put( current_time, 288, 16 ); + +/* -------------------------------------------------------------------- */ +/* Image Data */ +/* -------------------------------------------------------------------- */ + // FH10 - start block of image data + fh.Put( image_data_start+1, 304, 16 ); + + // FH11 - number of blocks of image data. + fh.Put( image_data_size, 320, 16 ); + + // FH12 - start block of image headers. + fh.Put( image_header_start+1, 336, 16 ); + + // FH13 - number of blocks of image headers. + fh.Put( image_header_count*2, 352, 8); + + // FH14 - interleaving. + fh.Put( interleaving, 360, 8); + + // FH15 - reserved - MIXED is for some ancient backwards compatability. + fh.Put( "MIXED", 368, 8); + + // FH16 - number of image bands. + fh.Put( channel_count, 376, 8 ); + + // FH17 - width of image in pixels. + fh.Put( pixels, 384, 8 ); + + // FH18 - height of image in pixels. + fh.Put( lines, 392, 8 ); + + // FH19 - pixel ground size interpretation. + fh.Put( "METRE", 400, 8 ); + + // TODO: + //PrintDouble( fh->XPixelSize, "%16.9f", 1.0 ); + //PrintDouble( fh->YPixelSize, "%16.9f", 1.0 ); + fh.Put( "1.0", 408, 16 ); + fh.Put( "1.0", 424, 16 ); + +/* -------------------------------------------------------------------- */ +/* Segment Pointers */ +/* -------------------------------------------------------------------- */ + // FH22 - start block of segment pointers. + fh.Put( segment_ptr_start+1, 440, 16 ); + + // fH23 - number of blocks of segment pointers. + fh.Put( segment_ptr_size, 456, 8 ); + +/* -------------------------------------------------------------------- */ +/* Number of different types of Channels */ +/* -------------------------------------------------------------------- */ + // FH24.1 - 8U bands. + fh.Put( channels[0], 464, 4 ); + + // FH24.2 - 16S bands. + fh.Put( channels[1], 468, 4 ); + + // FH24.3 - 16U bands. + fh.Put( channels[2], 472, 4 ); + + // FH24.4 - 32R bands. + fh.Put( channels[3], 476, 4 ); + + // FH24.5 - C16U bands + fh.Put( channels[4], 480, 4 ); + + // FH24.6 - C16S bands + fh.Put( channels[5], 484, 4 ); + + // FH24.7 - C32R bands + fh.Put( channels[6], 488, 4 ); + +/* -------------------------------------------------------------------- */ +/* Write out the file header. */ +/* -------------------------------------------------------------------- */ + interfaces->io->Write( fh.buffer, 512, 1, io_handle ); + +/* ==================================================================== */ +/* Write out the image headers. */ +/* ==================================================================== */ + PCIDSKBuffer ih( 1024 ); + + ih.Put( " ", 0, 1024 ); + + // IHi.1 - Text describing Channel Contents + ih.Put( "Contents Not Specified", 0, 64 ); + + // IHi.2 - Filename storing image. + if( strncmp(interleaving,"FILE",4) == 0 ) + ih.Put( "", 64, 64 ); + + if( externallink ) + { + // IHi.6.7 - IHi.6.10 + ih.Put( 0, 250, 8 ); + ih.Put( 0, 258, 8 ); + ih.Put( pixels, 266, 8 ); + ih.Put( lines, 274, 8 ); + } + + // IHi.3 - Creation time and date. + ih.Put( current_time, 128, 16 ); + + // IHi.4 - Creation time and date. + ih.Put( current_time, 144, 16 ); + + interfaces->io->Seek( io_handle, image_header_start*512, SEEK_SET ); + + for( chan_index = 0; chan_index < channel_count; chan_index++ ) + { + ih.Put(DataTypeName(channel_types[chan_index]).c_str(), 160, 8); + + if( strncmp("TILED",options.c_str(),5) == 0 ) + { + char sis_filename[65]; + sprintf( sis_filename, "/SIS=%d", chan_index ); + ih.Put( sis_filename, 64, 64 ); + + // IHi.6.7 - IHi.6.10 + ih.Put( 0, 250, 8 ); + ih.Put( 0, 258, 8 ); + ih.Put( pixels, 266, 8 ); + ih.Put( lines, 274, 8 ); + + // IHi.6.11 + ih.Put( 1, 282, 8 ); + } + + interfaces->io->Write( ih.buffer, 1024, 1, io_handle ); + } + + for( chan_index = channel_count; + chan_index < image_header_count; + chan_index++ ) + { + ih.Put( "", 160, 8 ); + ih.Put( "", 64, 64 ); + ih.Put( "", 250, 40 ); + + interfaces->io->Write( ih.buffer, 1024, 1, io_handle ); + } + +/* ==================================================================== */ +/* Write out the segment pointers, all spaces. */ +/* ==================================================================== */ + PCIDSKBuffer segment_pointers( (int) (segment_ptr_size*512) ); + segment_pointers.Put( " ", 0, (int) (segment_ptr_size*512) ); + + interfaces->io->Seek( io_handle, segment_ptr_start*512, SEEK_SET ); + interfaces->io->Write( segment_pointers.buffer, segment_ptr_size, 512, + io_handle ); + +/* -------------------------------------------------------------------- */ +/* Ensure we write out something at the end of the image data */ +/* to force the file size. */ +/* -------------------------------------------------------------------- */ + if( image_data_size > 0 ) + { + interfaces->io->Seek( io_handle, (image_data_start + image_data_size)*512-1, + SEEK_SET ); + interfaces->io->Write( "\0", 1, 1, io_handle ); + } + +/* -------------------------------------------------------------------- */ +/* Close the raw file, and reopen as a pcidsk file. */ +/* -------------------------------------------------------------------- */ + interfaces->io->Close( io_handle ); + + PCIDSKFile *file = Open( filename, "r+", interfaces ); + +/* -------------------------------------------------------------------- */ +/* Create a default georeferencing segment. */ +/* -------------------------------------------------------------------- */ + file->CreateSegment( "GEOref", + "Master Georeferencing Segment for File", + SEG_GEO, 6 ); + +/* -------------------------------------------------------------------- */ +/* If the dataset is tiled, create the file band data. */ +/* -------------------------------------------------------------------- */ + if( strncmp(options.c_str(),"TILED",5) == 0 ) + { + file->SetMetadataValue( "_DBLayout", options ); + + // For sizing the SysBMDir we want an approximate size of the + // the imagery. + uint64 rough_image_size = + (channels[0] + // CHN_8U + channels[1] * DataTypeSize(CHN_16U) + + channels[2] * DataTypeSize(CHN_16S) + + channels[3] * DataTypeSize(CHN_32R) + + channels[4] * DataTypeSize(CHN_C16U) + + channels[5] * DataTypeSize(CHN_C16S) + + channels[6] * DataTypeSize(CHN_C32R)) + * (pixels * (uint64) lines); + uint64 sysbmdir_size = ((rough_image_size / 8192) * 28) / 512; + + sysbmdir_size = (int) (sysbmdir_size * 1.1 + 100); + int segment = file->CreateSegment( "SysBMDir", + "System Block Map Directory - Do not modify.", + SEG_SYS, sysbmdir_size ); + + SysBlockMap *bm = + dynamic_cast(file->GetSegment( segment )); + + for( chan_index = 0; chan_index < channel_count; chan_index++ ) + { + bm->CreateVirtualImageFile( pixels, lines, blocksize, blocksize, + channel_types[chan_index], + compression ); + } + } + +/* -------------------------------------------------------------------- */ +/* If we have a non-tiled FILE interleaved file, should we */ +/* create external band files now? */ +/* -------------------------------------------------------------------- */ + if( strncmp(interleaving,"FILE",4) == 0 + && strncmp(options.c_str(),"TILED",5) != 0 + && !nocreate ) + { + for( chan_index = 0; chan_index < channel_count; chan_index++ ) + { + PCIDSKChannel *channel = file->GetChannel( chan_index + 1 ); + int pixel_size = DataTypeSize(channel->GetType()); + + // build a band filename that uses the basename of the PCIDSK + // file, and adds ".nnn" based on the band. + std::string band_filename = filename; + char ext[5]; + sprintf( ext, ".%03d", chan_index+1 ); + + size_t last_dot = band_filename.find_last_of("."); + if( last_dot != std::string::npos + && (band_filename.find_last_of("/\\:") == std::string::npos + || band_filename.find_last_of("/\\:") < last_dot) ) + { + band_filename.resize( last_dot ); + } + + band_filename += ext; + + // Now build a version without a path. + std::string relative_band_filename; + size_t path_div = band_filename.find_last_of( "/\\:" ); + if( path_div == std::string::npos ) + relative_band_filename = band_filename; + else + relative_band_filename = band_filename.c_str() + path_div + 1; + + // create the file - ought we write the whole file? + void *band_io_handle = interfaces->io->Open( band_filename, "w" ); + interfaces->io->Write( "\0", 1, 1, band_io_handle ); + interfaces->io->Close( band_io_handle ); + + // Set the channel header information. + channel->SetChanInfo( relative_band_filename, 0, pixel_size, + pixel_size * pixels, true ); + } + } + + return file; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskexception.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskexception.cpp new file mode 100644 index 000000000..ac74dd3a2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskexception.cpp @@ -0,0 +1,220 @@ +/****************************************************************************** + * + * Purpose: Implementation of the PCIDSKException class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_exception.h" +#include +#include +#include + +using PCIDSK::PCIDSKException; + +#if defined(_MSC_VER) && (_MSC_VER < 1500) +# define vsnprintf _vsnprintf +#endif + +/** + +\class PCIDSK::PCIDSKException + +\brief Generic SDK Exception + +The PCIDSKException class is used for all errors thrown by the PCIDSK +library. It includes a formatted message and is derived from std::exception. +The PCIDSK library throws all exceptions as pointers, and library exceptions +should be caught like this: + +@code + try + { + PCIDSKFile *file = PCIDSK::Open( "irvine.pix, "r", NULL ); + } + catch( PCIDSK::PCIDSKException &ex ) + { + fprintf( stderr, "PCIDSKException:\n%s\n", ex.what() ); + exit( 1 ); + } +@endcode + +*/ + +/************************************************************************/ +/* PCIDSKException() */ +/************************************************************************/ + +/** + * Create exception with formatted message. + * + * This constructor supports formatting of an exception message + * using printf style format and additional arguments. + * + * @param fmt the printf style format (eg. "Illegal value:%d") + * @param ... additional arguments as required by the format string. + */ + +PCIDSKException::PCIDSKException( const char *fmt, ... ) + +{ + std::va_list args; + + va_start( args, fmt ); + vPrintf( fmt, args ); + va_end( args ); +} + +/************************************************************************/ +/* ~PCIDSKException() */ +/************************************************************************/ + +/** + * Destructor. + */ + +PCIDSKException::~PCIDSKException() throw() + +{ +} + +/************************************************************************/ +/* vPrintf() */ +/************************************************************************/ + +/** + * Format a message. + * + * Assigns a message to an exception using printf style formatting + * and va_list arguments (similar to vfprintf(). + * + * @param fmt printf style format string. + * @param args additional arguments as required. + */ + + +void PCIDSKException::vPrintf( const char *fmt, std::va_list args ) + +{ +/* -------------------------------------------------------------------- */ +/* This implementation for platforms without vsnprintf() will */ +/* just plain fail if the formatted contents are too large. */ +/* -------------------------------------------------------------------- */ + +#if defined(MISSING_VSNPRINTF) + char *pszBuffer = (char *) malloc(30000); + if( vsprintf( pszBuffer, fmt, args) > 29998 ) + { + message = "PCIDSKException::vPrintf() ... buffer overrun."; + } + else + message = pszBuffer; + + free( pszBuffer ); + +/* -------------------------------------------------------------------- */ +/* This should grow a big enough buffer to hold any formatted */ +/* result. */ +/* -------------------------------------------------------------------- */ +#else + char szModestBuffer[500]; + int nPR; + va_list wrk_args; + +#ifdef va_copy + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + + nPR = vsnprintf( szModestBuffer, sizeof(szModestBuffer), fmt, + wrk_args ); + if( nPR == -1 || nPR >= (int) sizeof(szModestBuffer)-1 ) + { + int nWorkBufferSize = 2000; + char *pszWorkBuffer = (char *) malloc(nWorkBufferSize); + +#ifdef va_copy + va_end( wrk_args ); + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + while( (nPR=vsnprintf( pszWorkBuffer, nWorkBufferSize, fmt, wrk_args)) + >= nWorkBufferSize-1 + || nPR == -1 ) + { + nWorkBufferSize *= 4; + pszWorkBuffer = (char *) realloc(pszWorkBuffer, + nWorkBufferSize ); +#ifdef va_copy + va_end( wrk_args ); + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + } + message = pszWorkBuffer; + free( pszWorkBuffer ); + } + else + { + message = szModestBuffer; + } + va_end( wrk_args ); +#endif +} + +/** + * \fn const char *PCIDSKException::what() const throw(); + * + * \brief fetch exception message. + * + * @return a pointer to the internal message associated with the exception. + */ + +/** + * \brief throw a formatted exception. + * + * This function throws a PCIDSK Exception by reference after formatting + * the message using the given printf style format and arguments. This + * function exists primarily so that throwing an exception can be done in + * one line of code, instead of declaring an exception and then throwing it. + * + * @param fmt the printf style format (eg. "Illegal value:%d") + * @param ... additional arguments as required by the format string. + */ +void PCIDSK::ThrowPCIDSKException( const char *fmt, ... ) + +{ + std::va_list args; + PCIDSKException ex(""); + + va_start( args, fmt ); + ex.vPrintf( fmt, args ); + va_end( args ); + + throw ex; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskinterfaces.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskinterfaces.cpp new file mode 100644 index 000000000..a55702576 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskinterfaces.cpp @@ -0,0 +1,115 @@ +/****************************************************************************** + * + * Purpose: Implementation of the PCIDSKInterfaces class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_utils.h" +#include "pcidsk_interfaces.h" +#include "pcidsk_mutex.h" + +using namespace PCIDSK; + +/************************************************************************/ +/* PCIDSKInterfaces() */ +/* */ +/* This constructor just defaults all the interfaces and */ +/* functions to the default implementation. */ +/************************************************************************/ + +PCIDSKInterfaces::PCIDSKInterfaces() + +{ + io = GetDefaultIOInterfaces(); + OpenEDB = DefaultOpenEDB; + CreateMutex = DefaultCreateMutex; + Debug = DefaultDebug; + +#if defined(HAVE_LIBJPEG) + JPEGDecompressBlock = LibJPEG_DecompressBlock; + JPEGCompressBlock = LibJPEG_CompressBlock; +#else + JPEGDecompressBlock = NULL; + JPEGCompressBlock = NULL; +#endif + +} + +/** + +\var const IOInterfaces *PCIDSKInterfaces::io; + +\brief Pointer to IO Interfaces. + +***************************************************************************/ + +/** + +\var Mutex *(*PCIDSKInterfaces::CreateMutex)(void); + +\brief Function to create a mutex + +***************************************************************************/ + +/** + +\var void (*PCIDSKInterfaces::JPEGDecompressBlock)(uint8 *src_data, int src_bytes, uint8 *dst_data, int dst_bytes, int xsize, int ysize, eChanType pixel_type); + +\brief Function to decompress a jpeg block + +This function may be NULL if there is no jpeg interface available. + +The default implementation is implemented using libjpeg. + +The function decodes the jpeg compressed image in src_data (src_bytes long) +into dst_data (dst_bytes long) as image data. The result should be exactly +dst_bytes long, and will be an image of xsize x ysize of type pixel_type +(currently on CHN_8U is allowed). + +Errors should be thrown as exceptions. + +***************************************************************************/ + +/** + +\var void (*PCIDSKInterfaces::JPEGCompressBlock)(uint8 *src_data, int src_bytes, uint8 *dst_data, int &dst_bytes, int xsize, int ysize, eChanType pixel_type); + +\brief Function to compress a jpeg block + +This function may be NULL if there is no jpeg interface available. + +The default implementation is implemented using libjpeg. + +The function encodes the image in src_data (src_bytes long) +into dst_data as compressed jpeg data. The passed in value of dst_bytes is the +size of the passed in dst_data array (it should be large enough to hold +any compressed result0 and dst_bytes will be returned with the resulting +actual number of bytes used. + +Errors should be thrown as exceptions. + +***************************************************************************/ + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskopen.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskopen.cpp new file mode 100644 index 000000000..eb7c8c6ba --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/pcidskopen.cpp @@ -0,0 +1,113 @@ +/****************************************************************************** + * + * Purpose: Implementation of the Open() function. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "pcidsk.h" +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_file.h" +#include "pcidsk_interfaces.h" +#include "core/cpcidskfile.h" +#include +#include +#include + +using namespace PCIDSK; + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +/** + * Open a PCIDSK (.pix) file. + * + * This function attempts to open the named file, with the indicated + * access and the provided set of system interface methods. + * + * @param filename the name of the PCIDSK file to access. + * @param access either "r" for read-only, or "r+" for read-write access. + * @param interfaces Either NULL to use default interfaces, or a pointer + * to a populated interfaces object. + * + * @return a pointer to a file object for accessing the PCIDSK file. + */ + +PCIDSKFile *PCIDSK::Open( std::string filename, std::string access, + const PCIDSKInterfaces *interfaces ) + +{ +/* -------------------------------------------------------------------- */ +/* Use default interfaces if none are passed in. */ +/* -------------------------------------------------------------------- */ + PCIDSKInterfaces default_interfaces; + if( interfaces == NULL ) + interfaces = &default_interfaces; + +/* -------------------------------------------------------------------- */ +/* First open the file, and confirm that it is PCIDSK before */ +/* going further. */ +/* -------------------------------------------------------------------- */ + void *io_handle = interfaces->io->Open( filename, access ); + + assert( io_handle != NULL ); + + char header_check[6]; + + if( interfaces->io->Read( header_check, 1, 6, io_handle ) != 6 + || memcmp(header_check,"PCIDSK",6) != 0 ) + { + interfaces->io->Close( io_handle ); + ThrowPCIDSKException( "File %s does not appear to be PCIDSK format.", + filename.c_str() ); + } + +/* -------------------------------------------------------------------- */ +/* Create the PCIDSKFile object. */ +/* -------------------------------------------------------------------- */ + + CPCIDSKFile *file = new CPCIDSKFile( filename ); + + file->interfaces = *interfaces; + file->io_handle = io_handle; + file->io_mutex = interfaces->CreateMutex(); + + if( strstr(access.c_str(),"+") != NULL ) + file->updatable = true; + +/* -------------------------------------------------------------------- */ +/* Initialize it from the header. */ +/* -------------------------------------------------------------------- */ + try + { + file->InitializeFromHeader(); + } + catch(...) + { + delete file; + throw; + } + + return file; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/protectedfile.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/protectedfile.h new file mode 100644 index 000000000..c8c44062c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/protectedfile.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * + * Purpose: Declaration of the Protected File structure + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_CORE_PROTECTEDFILE_H +#define __INCLUDE_CORE_PROTECTEDFILE_H + +namespace PCIDSK +{ + + /************************************************************************/ + /* ProtectedFile */ + /************************************************************************/ + struct ProtectedFile + { + std::string filename; + bool writable; + void *io_handle; + Mutex *io_mutex; + }; + + /************************************************************************/ + /* ProtectedEDBFile */ + /************************************************************************/ + struct ProtectedEDBFile + { + EDBFile *file; + std::string filename; + bool writable; + Mutex *io_mutex; + }; + +} // end namespace PCIDSK + +#endif // __INCLUDE_CORE_PROTECTEDFILE_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/sysvirtualfile.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/sysvirtualfile.cpp new file mode 100644 index 000000000..a14901906 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/sysvirtualfile.cpp @@ -0,0 +1,589 @@ +/****************************************************************************** + * + * Purpose: Implementation of the SysVirtualFile class. + * + * This class is used to manage access to a single virtual file stored in + * SysBData segments based on a block map stored in the SysBMDir segment + * (and managed by SysBlockMap class). + * + * The virtual files are allocated in 8K chunks (block_size) in segments. + * To minimize IO requests, other overhead, we keep one such 8K block in + * our working cache for the virtual file stream. + * + * This class is primarily used by the CTiledChannel class for access to + * tiled images. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_buffer.h" +#include "pcidsk_exception.h" +#include "pcidsk_utils.h" +#include "core/sysvirtualfile.h" +#include "core/cpcidskfile.h" +#include "core/mutexholder.h" +#include "segment/sysblockmap.h" +#include +#include +#if 0 +#include +#endif + +using namespace PCIDSK; + + +const int SysVirtualFile::block_size = SYSVIRTUALFILE_BLOCKSIZE; + +/************************************************************************/ +/* SysVirtualFile() */ +/************************************************************************/ + +SysVirtualFile::SysVirtualFile( CPCIDSKFile *file, int start_block, + uint64 image_length, + SysBlockMap *sysblockmap, + int image_index ) + +{ + io_handle = NULL; + io_mutex = NULL; + + file_length = image_length; + this->file = file; + this->sysblockmap = sysblockmap; + this->image_index = image_index; + + loaded_block = -1; + loaded_block_dirty = false; + + last_bm_index = -1; + + next_bm_entry_to_load = start_block; + + regular_blocks = false; + blocks_loaded = 0; +} + +/************************************************************************/ +/* ~SysVirtualFile() */ +/************************************************************************/ + +SysVirtualFile::~SysVirtualFile() + +{ + Synchronize(); +} + +/************************************************************************/ +/* Synchronize() */ +/************************************************************************/ + +void SysVirtualFile::Synchronize() + +{ + FlushDirtyBlock(); +} + +/************************************************************************/ +/* GetBlockSegment() */ +/************************************************************************/ + +uint16 SysVirtualFile::GetBlockSegment( int requested_block ) + +{ + if( requested_block < 0 ) + ThrowPCIDSKException( "SysVirtualFile::GetBlockSegment(%d) - illegal request.", + requested_block ); + + if( requested_block >= blocks_loaded ) + LoadBMEntrysTo( requested_block ); + + if( regular_blocks ) + // regular blocks are all in one segment. + return xblock_segment[0]; + else + return xblock_segment[requested_block]; +} + +/************************************************************************/ +/* GetBlockIndexInSegment() */ +/************************************************************************/ + +int SysVirtualFile::GetBlockIndexInSegment( int requested_block ) + +{ + if( requested_block < 0 ) + ThrowPCIDSKException( "SysVirtualFile::GetBlockIndexInSegment(%d) - illegal request.", + requested_block ); + + if( requested_block >= blocks_loaded ) + LoadBMEntrysTo( requested_block ); + + if( regular_blocks ) + // regular blocks all follow the first block in order. + return xblock_index[0] + requested_block; + else + return xblock_index[requested_block]; +} + + +/************************************************************************/ +/* SetBlockInfo() */ +/************************************************************************/ + +void SysVirtualFile::SetBlockInfo( int requested_block, + uint16 new_block_segment, + int new_block_index ) + +{ + if( requested_block < 0 ) + ThrowPCIDSKException( "SysVirtualFile::SetBlockSegment(%d) - illegal request.", + requested_block ); + + // this should always be the case. + assert( requested_block == blocks_loaded ); + + // Initialization case. + if( requested_block == 0 && blocks_loaded == 0 ) + { + xblock_segment.push_back( new_block_segment ); + xblock_index.push_back( new_block_index ); + blocks_loaded = 1; + return; + } + + if( !regular_blocks ) + { + xblock_segment.push_back( new_block_segment ); + xblock_index.push_back( new_block_index ); + blocks_loaded++; + return; + } + + // Are things still regular? + if( new_block_segment == xblock_segment[0] + && new_block_index == xblock_index[0] + requested_block ) + { + blocks_loaded++; + return; + } + + // Ah, we see they are now irregular. We need to build up the + // segment/index arrays and proceed to populate them. + Debug( file->GetInterfaces()->Debug, + "SysVirtualFile - Discovered stream is irregulr. %d/%d follows %d/%d at block %d.\n", + new_block_segment, new_block_index, + xblock_segment[0], xblock_index[0], + requested_block ); + + regular_blocks = false; + while( (int) xblock_segment.size() < blocks_loaded ) + { + xblock_segment.push_back( xblock_segment[0] ); + xblock_index.push_back( xblock_index[xblock_index.size()-1]+1 ); + } + + xblock_segment.push_back( new_block_segment ); + xblock_index.push_back( new_block_index ); + blocks_loaded++; +} + +/************************************************************************/ +/* WriteToFile() */ +/************************************************************************/ + +void +SysVirtualFile::WriteToFile( const void *buffer, uint64 offset, uint64 size ) + +{ + uint64 buffer_offset = 0; + + if(io_handle == NULL || io_mutex == NULL) + file->GetIODetails( &io_handle, &io_mutex ); + + MutexHolder oMutex(*io_mutex); + + while( buffer_offset < size ) + { + int request_block = (int) ((offset + buffer_offset) / block_size); + int offset_in_block = (int) ((offset + buffer_offset) % block_size); + int amount_to_copy = block_size - offset_in_block; + + if (offset_in_block != 0 || (size - buffer_offset) < (uint64)block_size) { + // we need to read in the block for update + LoadBlock( request_block ); + if( amount_to_copy > (int) (size - buffer_offset) ) + amount_to_copy = (int) (size - buffer_offset); + + // fill in the block + memcpy( block_data + offset_in_block, + ((uint8 *) buffer) + buffer_offset, + amount_to_copy ); + + loaded_block_dirty = true; + } else { + int num_full_blocks = (int) ((size - buffer_offset) / block_size); + + WriteBlocks(request_block, num_full_blocks, (uint8*)buffer + buffer_offset); + + amount_to_copy = num_full_blocks * block_size; + } + + buffer_offset += amount_to_copy; + } + + if( offset+size > file_length ) + { + file_length = offset+size; + sysblockmap->SetVirtualFileSize( image_index, file_length ); + } +} + +/************************************************************************/ +/* ReadFromFile() */ +/************************************************************************/ + +void SysVirtualFile::ReadFromFile( void *buffer, uint64 offset, uint64 size ) + +{ + if(io_handle == NULL || io_mutex == NULL) + file->GetIODetails( &io_handle, &io_mutex ); + + MutexHolder oMutex(*io_mutex); + + uint64 buffer_offset = 0; +#if 0 + printf("Requesting region at %llu of size %llu\n", offset, size); +#endif + while( buffer_offset < size ) + { + int request_block = (int) ((offset + buffer_offset) / block_size); + int offset_in_block = (int) ((offset + buffer_offset) % block_size); + int amount_to_copy = block_size - offset_in_block; + + + + if (offset_in_block != 0 || (size - buffer_offset) < (uint64)block_size) { + // Deal with the case where we need to load a partial block. Hopefully + // this doesn't happen often + LoadBlock( request_block ); + if( amount_to_copy > (int) (size - buffer_offset) ) + amount_to_copy = (int) (size - buffer_offset); + memcpy( ((uint8 *) buffer) + buffer_offset, + block_data + offset_in_block, amount_to_copy ); + } else { + // Use the bulk loading of blocks. First, compute the range + // of full blocks we need to load + int num_full_blocks = (int) ((size - buffer_offset)/block_size); + + LoadBlocks(request_block, num_full_blocks, ((uint8*)buffer) + buffer_offset); + amount_to_copy = num_full_blocks * block_size; + } + + + buffer_offset += amount_to_copy; + } +} + +/************************************************************************/ +/* LoadBlock() */ +/************************************************************************/ +/** + * Loads the requested_block block from the system virtual file. Extends + * the file if necessary + */ +void SysVirtualFile::LoadBlock( int requested_block ) + +{ +/* -------------------------------------------------------------------- */ +/* Do we already have this block? */ +/* -------------------------------------------------------------------- */ + if( requested_block == loaded_block ) + return; + +/* -------------------------------------------------------------------- */ +/* Do we need to grow the virtual file by one block? */ +/* -------------------------------------------------------------------- */ + GrowVirtualFile(requested_block); + +/* -------------------------------------------------------------------- */ +/* Does this block exist in the virtual file? */ +/* -------------------------------------------------------------------- */ + if( requested_block < 0 || requested_block >= blocks_loaded ) + ThrowPCIDSKException( "SysVirtualFile::LoadBlock(%d) - block out of range.", + requested_block ); + +/* -------------------------------------------------------------------- */ +/* Do we have a dirty block loaded that needs to be saved? */ +/* -------------------------------------------------------------------- */ + FlushDirtyBlock(); + +/* -------------------------------------------------------------------- */ +/* Load the requested block. */ +/* -------------------------------------------------------------------- */ + LoadBMEntrysTo( requested_block ); + PCIDSKSegment *data_seg_obj = + file->GetSegment( GetBlockSegment( requested_block ) ); + + data_seg_obj->ReadFromFile( block_data, + block_size * (uint64) GetBlockIndexInSegment( requested_block ), + block_size ); + + loaded_block = requested_block; + loaded_block_dirty = false; +} + +/************************************************************************/ +/* FlushDirtyBlock() */ +/************************************************************************/ +/** + * If the block currently loaded is dirty, flush it to the file + */ +void SysVirtualFile::FlushDirtyBlock(void) +{ + if (loaded_block_dirty) { + if(io_handle == NULL || io_mutex == NULL) + file->GetIODetails( &io_handle, &io_mutex ); + + MutexHolder oMutex(*io_mutex); + + PCIDSKSegment *data_seg_obj = + file->GetSegment( GetBlockSegment( loaded_block ) ); + + data_seg_obj->WriteToFile( block_data, + block_size * (uint64) GetBlockIndexInSegment( loaded_block ), + block_size ); + loaded_block_dirty = false; + } +} + +/************************************************************************/ +/* GrowVirtualFile() */ +/************************************************************************/ +void SysVirtualFile::GrowVirtualFile(std::ptrdiff_t requested_block) +{ + LoadBMEntrysTo( requested_block ); + + if( requested_block == blocks_loaded ) + { + if(io_handle == NULL || io_mutex == NULL) + file->GetIODetails( &io_handle, &io_mutex ); + + MutexHolder oMutex(*io_mutex); + + int new_seg; + int offset; + + offset = + sysblockmap->GrowVirtualFile( image_index, last_bm_index, new_seg); + SetBlockInfo( requested_block, (uint16) new_seg, offset ); + } +} + +/************************************************************************/ +/* WriteBlocks() */ +/************************************************************************/ +/** + * \brief Writes a group of blocks + * Attempts to create a group of blocks (if the SysVirtualFile + * is not already large enough to hold them) and then write them + * out contiguously + */ +void SysVirtualFile::WriteBlocks(int first_block, + int block_count, + void* const buffer) +{ + if(io_handle == NULL || io_mutex == NULL) + file->GetIODetails( &io_handle, &io_mutex ); + + MutexHolder oMutex(*io_mutex); + + FlushDirtyBlock(); + // Iterate through all the blocks to be written, first, then + // grow the virtual file + for (unsigned int i = 0; i <= (unsigned int) block_count; i++) { + GrowVirtualFile(first_block + i); + } + + // Using a similar algorithm to the LoadBlocks() function, + // attempt to coalesce writes + std::size_t buffer_off = 0; + std::size_t blocks_written = 0; + std::size_t current_first_block = first_block; + while (blocks_written < (std::size_t) block_count) { + LoadBMEntrysTo( current_first_block+1 ); + + unsigned int cur_segment = GetBlockSegment( current_first_block ); + unsigned int cur_block = current_first_block; + while (cur_block < (unsigned int)block_count + first_block && + (unsigned int) GetBlockSegment(cur_block + 1) == cur_segment) + { + cur_block++; + LoadBMEntrysTo( current_first_block+1 ); + } + + // Find largest span of contiguous blocks we can write + uint64 write_start = GetBlockIndexInSegment(current_first_block); + uint64 write_cur = write_start * block_size; + unsigned int count_to_write = 1; + while (write_cur + block_size == + (uint64)GetBlockIndexInSegment(count_to_write + current_first_block - 1) * block_size && + count_to_write < (cur_block - current_first_block)) + { + write_cur += block_size; + count_to_write++; + } + + PCIDSKSegment *data_seg_obj = + file->GetSegment( cur_segment ); + + std::size_t bytes_to_write = count_to_write * block_size; + + data_seg_obj->WriteToFile((uint8*)buffer + buffer_off, + block_size * write_start, + bytes_to_write); + + buffer_off += bytes_to_write; + blocks_written += count_to_write; + current_first_block += count_to_write; + } +} + +/************************************************************************/ +/* LoadBlocks() */ +/************************************************************************/ +/** + * \brief Load a group of blocks + * Attempts to coalesce reading of groups of blocks into a single + * filesystem I/O operation. Does not cache the loaded block, nor + * does it modify the state of the SysVirtualFile, other than to + * flush the loaded block if it is dirty. + */ +void SysVirtualFile::LoadBlocks(int requested_block_start, + int requested_block_count, + void* const buffer) +{ + if(io_handle == NULL || io_mutex == NULL) + file->GetIODetails( &io_handle, &io_mutex ); + + MutexHolder oMutex(*io_mutex); + + FlushDirtyBlock(); + + unsigned int blocks_read = 0; + unsigned int current_start = requested_block_start; + + std::size_t buffer_off = 0; + + while (blocks_read < (unsigned int)requested_block_count) { + // Coalesce blocks that are in the same segment + LoadBMEntrysTo( current_start+1 ); + unsigned int cur_segment = GetBlockSegment(current_start); // segment of current + // first block + unsigned int cur_block = current_start; // starting block ID + while (cur_block < (unsigned int)requested_block_count + requested_block_start && + GetBlockSegment(cur_block + 1) == cur_segment) { + // this block is in the same segment as the previous one we + // wanted to read. + cur_block++; + LoadBMEntrysTo( cur_block+1 ); + } + + // now attempt to determine if the region of blocks (from current_start + // to cur_block are contiguous + uint64 read_start = GetBlockIndexInSegment(current_start); + uint64 read_cur = read_start * block_size; + unsigned int count_to_read = 1; // we'll read at least one block + while (read_cur + block_size == + (uint64)GetBlockIndexInSegment(count_to_read + current_start) * block_size && // compare count of blocks * offset with stored offset + count_to_read < (cur_block - current_start) ) // make sure we don't try to read more blocks than we determined fell in + // this segment + { + read_cur += block_size; + count_to_read++; + } + +#if 0 + // Check if we need to grow the virtual file for each of these blocks + for (unsigned int i = 0 ; i < count_to_read; i++) { + GrowVirtualFile(i + current_start); + } + + printf("Coalescing the read of %d blocks\n", count_to_read); +#endif + + // Perform the actual read + PCIDSKSegment *data_seg_obj = + file->GetSegment( cur_segment ); + + std::size_t data_size = block_size * count_to_read; + +#if 0 + printf("Reading %d bytes at offset %d in buffer\n", data_size, buffer_off); +#endif + + data_seg_obj->ReadFromFile( ((uint8*)buffer) + buffer_off, + block_size * read_start, + data_size ); + + buffer_off += data_size; // increase buffer offset + + // Increment the current start by the number of blocks we jsut read + current_start += count_to_read; + blocks_read += count_to_read; + } +} + +/************************************************************************/ +/* LoadBMEntryTo() */ +/* */ +/* We load the blockmap "as needed". This method fills in */ +/* blockmap entries up to the target block, if not already */ +/* loaded. Passing in a target block_index of -1 loads the */ +/* whole blockmap. It is harmless to request more blocks than */ +/* are available. */ +/************************************************************************/ + +void SysVirtualFile::LoadBMEntrysTo( int target_index ) + +{ + if( target_index > 0 ) + { + target_index += 200 - (target_index%200); + } + + while( (target_index == -1 || blocks_loaded <= target_index ) + && next_bm_entry_to_load != -1 ) + { + uint16 segment; + int block; + + last_bm_index = next_bm_entry_to_load; + next_bm_entry_to_load = + sysblockmap->GetNextBlockMapEntry( + next_bm_entry_to_load, segment, block ); + + SetBlockInfo( blocks_loaded, segment, block ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/sysvirtualfile.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/sysvirtualfile.h new file mode 100644 index 000000000..5a0e5e91e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/core/sysvirtualfile.h @@ -0,0 +1,113 @@ +/****************************************************************************** + * + * Purpose: Declaration of the SysVirtualFile class. + * + * This class is used to manage access to a single virtual file stored in + * SysBData segments based on a block map stored in the SysBMDir segment + * (and managed by SysBlockMap class). + * + * The virtual files are allocated in 8K chunks (block_size) in segments. + * To minimize IO requests, other overhead, we keep one such 8K block in + * our working cache for the virtual file stream. + * + * This class is primarily used by the CTiledChannel class for access to + * tiled images. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_CORE_SYSVIRTUALFILE_H +#define __INCLUDE_CORE_SYSVIRTUALFILE_H + +#include "pcidsk_buffer.h" +#include "pcidsk_mutex.h" + +#include + +#define SYSVIRTUALFILE_BLOCKSIZE 8192 + +namespace PCIDSK +{ + class CPCIDSKFile; + class SysBlockMap; + /************************************************************************/ + /* SysVirtualFile */ + /************************************************************************/ + + class SysVirtualFile + { + public: + SysVirtualFile( CPCIDSKFile *file, int start_block, uint64 image_length, + SysBlockMap *sysblockmap, int image_index ); + ~SysVirtualFile(); + + void Synchronize(); + + void WriteToFile( const void *buffer, uint64 offset, uint64 size ); + void ReadFromFile( void *buffer, uint64 offset, uint64 size ); + + uint64 GetLength() { return file_length; } + + static const int block_size; + + private: + CPCIDSKFile *file; + void **io_handle; + Mutex **io_mutex; + + SysBlockMap *sysblockmap; + int image_index; + + uint64 file_length; + + bool regular_blocks; + int blocks_loaded; + std::vector xblock_segment; + std::vector xblock_index; + int next_bm_entry_to_load; + + int loaded_block; + uint8 block_data[SYSVIRTUALFILE_BLOCKSIZE]; + bool loaded_block_dirty; + + int last_bm_index; + + uint16 GetBlockSegment( int requested_block ); + int GetBlockIndexInSegment( int requested_block ); + + void SetBlockInfo( int requested_block, + uint16 new_block_segment, + int new_block_index ); + + void LoadBlock( int requested_block ); + void LoadBlocks( int requested_block_start, + int requested_block_count, + void* const buffer); + void GrowVirtualFile(std::ptrdiff_t requested_block); + void FlushDirtyBlock(); + void WriteBlocks(int first_block, int block_count, + void* const buffer); + void LoadBMEntrysTo( int block_index ); + }; +} + +#endif // __INCLUDE_CORE_SYSVIRTUALFILE_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk.h new file mode 100644 index 000000000..ff60eb58a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk.h @@ -0,0 +1,66 @@ +/****************************************************************************** + * + * Purpose: Primary public include file for PCIDSK SDK. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/** + * \file pcidsk.h + * + * Public PCIDSK library classes and functions. + */ + +#ifndef PCIDSK_H_INCLUDED +#define PCIDSK_H_INCLUDED + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_file.h" +#include "pcidsk_channel.h" +#include "pcidsk_buffer.h" +#include "pcidsk_mutex.h" +#include "pcidsk_exception.h" +#include "pcidsk_interfaces.h" +#include "pcidsk_segment.h" +#include "pcidsk_io.h" +#include "pcidsk_georef.h" +#include "pcidsk_rpc.h" + +//! Namespace for all PCIDSK Library classes and functions. + +namespace PCIDSK { +/************************************************************************/ +/* PCIDSK Access Functions */ +/************************************************************************/ +PCIDSKFile PCIDSK_DLL *Open( std::string filename, std::string access, + const PCIDSKInterfaces *interfaces = NULL ); +PCIDSKFile PCIDSK_DLL *Create( std::string filename, int pixels, int lines, + int channel_count, eChanType *channel_types, + std::string options, + const PCIDSKInterfaces *interfaces = NULL ); + + +} // end of PCIDSK namespace + +#endif // PCIDSK_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_ads40.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_ads40.h new file mode 100644 index 000000000..ea5ef1287 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_ads40.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * + * Purpose: Interface representing access to a PCIDSK ADS40 Segment + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_PCIDSK_ADS40_H +#define __INCLUDE_PCIDSK_PCIDSK_ADS40_H + +#include +#include + +namespace PCIDSK { +//! Interface to PCIDSK RPC segment. + class PCIDSKADS40Segment + { + public: + // Get path + virtual std::string GetPath(void) const = 0; + // Set path + virtual void SetPath(const std::string& oPath) = 0; + + // Virtual destructor + virtual ~PCIDSKADS40Segment() {} + }; +} + +#endif // __INCLUDE_PCIDSK_PCIDSK_ADS40_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_airphoto.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_airphoto.h new file mode 100644 index 000000000..740f984cb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_airphoto.h @@ -0,0 +1,162 @@ +/****************************************************************************** + * + * Purpose: Declaration of the Airphoto segment interface and the helper + * storage objects. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_SRC_PCIDSK_AIRPHOTO_H +#define __INCLUDE_PCIDSK_SRC_PCIDSK_AIRPHOTO_H + +#include "pcidsk_config.h" + +#include +#include +#include + +namespace PCIDSK { + /** + * Structure for storing interior orientation parameters associated + * with the APModel + */ + class PCIDSK_DLL PCIDSKAPModelIOParams + { + public: + PCIDSKAPModelIOParams(std::vector const& imgtofocalx, + std::vector const& imgtofocaly, + std::vector const& focaltocolumn, + std::vector const& focaltorow, + double focal_len, + std::pair const& prin_pt, + std::vector const& radial_dist); + std::vector const& GetImageToFocalPlaneXCoeffs(void) const; + std::vector const& GetImageToFocalPlaneYCoeffs(void) const; + std::vector const& GetFocalPlaneToColumnCoeffs(void) const; + std::vector const& GetFocalPlaneToRowCoeffs(void) const; + + double GetFocalLength(void) const; + std::pair const& GetPrincipalPoint(void) const; + std::vector const& GetRadialDistortionCoeffs(void) const; + private: + std::vector imgtofocalx_; + std::vector imgtofocaly_; + std::vector focaltocolumn_; + std::vector focaltorow_; + double focal_len_; + std::pair prin_point_; + std::vector rad_dist_coeff_; + }; + + /** + * Structure for storing exterior orientation parameters associated + * with the APModel + */ + class PCIDSK_DLL PCIDSKAPModelEOParams + { + public: + PCIDSKAPModelEOParams(std::string const& rotation_type, + std::vector const& earth_to_body, + std::vector const& perspect_cen, + unsigned int epsg_code = 0); + std::string GetEarthToBodyRotationType(void) const; + std::vector const& GetEarthToBodyRotation(void) const; + std::vector const& GetPerspectiveCentrePosition(void) const; + unsigned int GetEPSGCode(void) const; + private: + std::string rot_type_; + std::vector earth_to_body_; + std::vector perspective_centre_pos_; + unsigned int epsg_code_; + }; + + class PCIDSK_DLL PCIDSKAPModelMiscParams + { + public: + PCIDSKAPModelMiscParams(std::vector const& decentering_coeffs, + std::vector const& x3dcoord, + std::vector const& y3dcoord, + std::vector const& z3dcoord, + double radius, + double rff, + double min_gcp_hgt, + double max_gcp_hgt, + bool is_prin_pt_off, + bool has_dist, + bool has_decent, + bool has_radius); + std::vector const& GetDecenteringDistortionCoeffs(void) const; + std::vector const& GetX3DCoord(void) const; + std::vector const& GetY3DCoord(void) const; + std::vector const& GetZ3DCoord(void) const; + double GetRadius(void) const; + double GetRFF(void) const; // radius * focal * focal + double GetGCPMinHeight(void) const; + double GetGCPMaxHeight(void) const; + bool IsPrincipalPointOffset(void) const; + bool HasDistortion(void) const; + bool HasDecentering(void) const; + bool HasRadius(void) const; + private: + std::vector decentering_coeffs_; + std::vector x3dcoord_; + std::vector y3dcoord_; + std::vector z3dcoord_; + double radius_, rff_, min_gcp_hgt_, max_gcp_hgt_; + bool is_prin_pt_off_; + bool has_dist_; + bool has_decent_; + bool has_radius_; + }; + + /** + * Interface for accessing the contents of the Airphoto Model + * segment. + */ + class PCIDSKAPModelSegment + { + public: + virtual ~PCIDSKAPModelSegment() {} + + virtual unsigned int GetWidth(void) const = 0; + virtual unsigned int GetHeight(void) const = 0; + virtual unsigned int GetDownsampleFactor(void) const = 0; + + // Interior Orientation Parameters + virtual PCIDSKAPModelIOParams const& GetInteriorOrientationParams(void) const = 0; + + // Exterior Orientation Parameters + virtual PCIDSKAPModelEOParams const& GetExteriorOrientationParams(void) const = 0; + + // ProjInfo + virtual PCIDSKAPModelMiscParams const& GetAdditionalParams(void) const = 0; + + virtual std::string GetMapUnitsString(void) const = 0; + virtual std::string GetUTMUnitsString(void) const = 0; + virtual std::vector const& GetProjParams(void) const = 0; + + }; + +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_SRC_PCIDSK_AIRPHOTO_H + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_array.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_array.h new file mode 100644 index 000000000..abc6428a8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_array.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * + * Purpose: PCIDSK ARRAY segment interface class. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_ARRAY_H +#define __INCLUDE_PCIDSK_ARRAY_H + +#include +#include + +namespace PCIDSK +{ +/************************************************************************/ +/* PCIDSK_ARRAY */ +/************************************************************************/ + +//! Interface to PCIDSK text segment. + + class PCIDSK_DLL PCIDSK_ARRAY + { + public: + virtual ~PCIDSK_ARRAY() {} + + //ARRAY functions + virtual unsigned char GetDimensionCount() const =0; + virtual void SetDimensionCount(unsigned char nDim) =0; + virtual const std::vector& GetSizes() const =0; + virtual void SetSizes(const std::vector& oSizes) =0; + virtual const std::vector& GetArray() const =0; + virtual void SetArray(const std::vector& oArray) =0; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_ARRAY_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_binary.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_binary.h new file mode 100644 index 000000000..8646a1473 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_binary.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * + * Purpose: Interface representing access to a PCIDSK Binary Segment + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_PCIDSK_BINARY_H +#define __INCLUDE_PCIDSK_PCIDSK_BINARY_H + +namespace PCIDSK { +//! Interface to PCIDSK Binary segment. + class PCIDSKBinarySegment + { + public: + virtual const char* GetBuffer(void) const = 0; + virtual unsigned int GetBufferSize(void) const = 0; + virtual void SetBuffer(const char* pabyBuf, + unsigned int nBufSize) = 0; + + // Virtual destructor + virtual ~PCIDSKBinarySegment() {} + }; +} + +#endif // __INCLUDE_PCIDSK_PCIDSK_BINARY_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_buffer.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_buffer.h new file mode 100644 index 000000000..58c200b3a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_buffer.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * + * Purpose: Declaration of the PCIDSKBuffer class + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSKBUFFER_H +#define __INCLUDE_PCIDSKBUFFER_H + +#include "pcidsk_config.h" + +#include + +namespace PCIDSK +{ +/************************************************************************/ +/* PCIDSKBuffer */ +/* */ +/* Convenience class for managing ascii headers of various */ +/* sorts. Primarily for internal use. */ +/************************************************************************/ + + class PCIDSKBuffer + { + friend class MetadataSegment; // ? + public: + PCIDSKBuffer( int size = 0 ); + PCIDSKBuffer( const char *src, int size ); + ~PCIDSKBuffer(); + + char *buffer; + int buffer_size; + + PCIDSKBuffer &operator=(const PCIDSKBuffer& src); + + const char *Get( int offset, int size ) const; + void Get( int offset, int size, std::string &target, int unpad=1 ) const; + + double GetDouble( int offset, int size ) const; + int GetInt( int offset, int size ) const; + int64 GetInt64( int offset, int size ) const; + uint64 GetUInt64( int offset, int size ) const; + + void Put( const char *value, int offset, int size, bool null_term = false ); + void Put( uint64 value, int offset, int size ); + void Put( double value, int offset, int size, const char *fmt=NULL ); + void Put( int value, int offset, int size ) + { Put( (uint64) value, offset, size ); } + void Put( unsigned int value, int offset, int size ) + { Put( (uint64) value, offset, size ); } + + void PutBin(double value, int offset); + + void SetSize( int size ); + + private: + mutable std::string work_field; + }; +} // end namespace PCIDSK +#endif // __INCLUDE_PCIDSKBUFFER_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_channel.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_channel.h new file mode 100644 index 000000000..dd841b80b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_channel.h @@ -0,0 +1,95 @@ +/****************************************************************************** + * + * Purpose: Declaration of the PCIDSKChannel interface. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_CHANNEL_H +#define __INCLUDE_PCIDSK_CHANNEL_H + +#include "pcidsk_types.h" +#include +#include + +namespace PCIDSK +{ +/************************************************************************/ +/* PCIDSKChannel */ +/************************************************************************/ + +//! Interface to one PCIDSK channel (band) or bitmap segment. + + class PCIDSK_DLL PCIDSKChannel + { + public: + virtual ~PCIDSKChannel() {}; + virtual int GetBlockWidth() const = 0; + virtual int GetBlockHeight() const = 0; + virtual int GetBlockCount() const = 0; + virtual int GetWidth() const = 0; + virtual int GetHeight() const = 0; + virtual eChanType GetType() const = 0; + virtual int ReadBlock( int block_index, void *buffer, + int win_xoff=-1, int win_yoff=-1, + int win_xsize=-1, int win_ysize=-1 ) = 0; + virtual int WriteBlock( int block_index, void *buffer ) = 0; + virtual int GetOverviewCount() = 0; + virtual PCIDSKChannel *GetOverview( int i ) = 0; + virtual bool IsOverviewValid( int i ) = 0; + virtual std::string GetOverviewResampling( int i ) = 0; + virtual void SetOverviewValidity( int i, bool validity ) = 0; + virtual std::vector GetOverviewLevelMapping() const = 0; + + virtual std::string GetMetadataValue( const std::string &key ) const = 0; + virtual void SetMetadataValue( const std::string &key, const std::string &value ) = 0; + virtual std::vector GetMetadataKeys() const = 0; + + virtual void Synchronize() = 0; + + virtual std::string GetDescription() = 0; + virtual void SetDescription( const std::string &description ) = 0; + + virtual std::vector GetHistoryEntries() const = 0; + virtual void SetHistoryEntries( const std::vector &entries ) = 0; + virtual void PushHistory(const std::string &app, + const std::string &message) = 0; + + // Only applicable to FILE interleaved raw channels. + virtual void GetChanInfo( std::string &filename, uint64 &image_offset, + uint64 &pixel_offset, uint64 &line_offset, + bool &little_endian ) const = 0; + virtual void SetChanInfo( std::string filename, uint64 image_offset, + uint64 pixel_offset, uint64 line_offset, + bool little_endian ) = 0; + + // Only applicable to CExternalChannels + virtual void GetEChanInfo( std::string &filename, int &echannel, + int &exoff, int &eyoff, + int &exsize, int &eysize ) const = 0; + virtual void SetEChanInfo( std::string filename, int echannel, + int exoff, int eyoff, + int exsize, int eysize ) = 0; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_CHANNEL_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_config.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_config.h new file mode 100644 index 000000000..52606d4aa --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_config.h @@ -0,0 +1,98 @@ +/****************************************************************************** + * + * Purpose: Primary include file for PCIDSK SDK. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef PCIDSK_CONFIG_H_INCLUDED +#define PCIDSK_CONFIG_H_INCLUDED + +namespace PCIDSK { + + typedef unsigned char uint8; + +#ifndef _PCI_TYPES + typedef int int32; + typedef unsigned int uint32; + typedef short int16; + typedef unsigned short uint16; + +#if defined(_MSC_VER) + typedef __int64 int64; + typedef unsigned __int64 uint64; +#else + typedef long long int64; + typedef unsigned long long uint64; +#endif + +#endif // _PCI_TYPES + +} + +#ifdef _MSC_VER +# ifdef LIBPCIDSK_EXPORTS +# define PCIDSK_DLL __declspec(dllexport) +# else +# define PCIDSK_DLL +# endif +#else +# define PCIDSK_DLL +#endif + +#if defined(__MSVCRT__) || defined(_MSC_VER) + #define PCIDSK_FRMT_64_WITHOUT_PREFIX "I64" +#else + #define PCIDSK_FRMT_64_WITHOUT_PREFIX "ll" +#endif + +// #define MISSING_VSNPRINTF + +/** + * Versioning in the PCIDSK SDK + * The version number for the PCIDSK SDK is to be used as follows: + *
      + *
    • If minor changes to the underlying fundamental classes are made, + * but no linkage-breaking changes are made, increment the minor + * number. + *
    • If major changes are made to the underlying interfaces that will + * break linkage, increment the major number. + *
    + */ +#define PCIDSK_SDK_MAJOR_VERSION 0 +#define PCIDSK_SDK_MINOR_VERSION 1 + +#ifndef GDAL_PCIDSK_DRIVER +#ifdef PCIDSK_INTERNAL +#include +extern "C" double CPLAtof(const char*); +extern "C" int CPLsprintf(char *str, const char* fmt, ...); +extern "C" int CPLsnprintf(char *str, size_t size, const char* fmt, ...); +#else +#define CPLAtof atof +#define CPLsprintf sprintf +#define CPLsnprintf snprintf +#endif +#endif + +#endif // PCIDSK_CONFIG_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_edb.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_edb.h new file mode 100644 index 000000000..f6ac3aaa1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_edb.h @@ -0,0 +1,66 @@ +/****************************************************************************** + * + * Purpose: PCIDSK External Database Interface declaration. This provides + * mechanisms for access to external linked image file formats. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_EDB_H +#define __INCLUDE_PCIDSK_EDB_H + +#include "pcidsk_config.h" + +#include + +namespace PCIDSK +{ +/************************************************************************/ +/* EDBFile */ +/************************************************************************/ + +//! External Database Interface class. + + class EDBFile + { + public: + virtual ~EDBFile() {} + virtual int Close() const = 0; + + virtual int GetWidth() const = 0; + virtual int GetHeight() const = 0; + virtual int GetChannels() const = 0; + virtual int GetBlockWidth(int channel ) const = 0; + virtual int GetBlockHeight(int channel ) const = 0; + virtual eChanType GetType(int channel ) const = 0; + virtual int ReadBlock(int channel, + int block_index, void *buffer, + int win_xoff=-1, int win_yoff=-1, + int win_xsize=-1, int win_ysize=-1 ) = 0; + virtual int WriteBlock( int channel, int block_index, void *buffer) = 0; + }; + + EDBFile PCIDSK_DLL *DefaultOpenEDB(std::string filename, + std::string access); +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_EDB_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_ephemeris.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_ephemeris.h new file mode 100644 index 000000000..7f11c22f0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_ephemeris.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * + * Purpose: Interface representing access to a PCIDSK Ephemeris segment + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_PCIDSK_EPHEMERIS_H +#define __INCLUDE_PCIDSK_PCIDSK_EPHEMERIS_H + +#include +#include +#include "segment/orbitstructures.h" + +namespace PCIDSK { +//! Interface to PCIDSK RPC segment. + class PCIDSKEphemerisSegment + { + public: + + // Virtual destructor + virtual ~PCIDSKEphemerisSegment() {} + + virtual const EphemerisSeg_t& GetEphemeris() const=0; + virtual void SetEphemeris(const EphemerisSeg_t& oEph) =0; + }; +} + +#endif // __INCLUDE_PCIDSK_PCIDSK_EPHEMERIS_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_exception.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_exception.h new file mode 100644 index 000000000..5a35f0686 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_exception.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * + * Purpose: Declaration of the PCIDSKException class. All exceptions thrown + * by the PCIDSK library will be of this type. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_EXCEPTION_H +#define __INCLUDE_PCIDSK_EXCEPTION_H + +#include "pcidsk_config.h" + +#include +#include +#include + +namespace PCIDSK +{ +/************************************************************************/ +/* Exception */ +/************************************************************************/ + + class PCIDSKException : public std::exception + { + public: + PCIDSKException(const char *fmt, ... ); + virtual ~PCIDSKException() throw(); + + void vPrintf( const char *fmt, std::va_list list ); + virtual const char *what() const throw() { return message.c_str(); } + private: + std::string message; + }; + + void PCIDSK_DLL ThrowPCIDSKException( const char *fmt, ... ); + +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_EXCEPTION_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_file.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_file.h new file mode 100644 index 000000000..e4e3ad7dc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_file.h @@ -0,0 +1,93 @@ +/****************************************************************************** + * + * Purpose: Declaration of the PCIDSKFile Interface + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_FILE_H +#define __INCLUDE_PCIDSK_FILE_H + +#include "pcidsk_segment.h" +#include +#include + +namespace PCIDSK +{ +/************************************************************************/ +/* PCIDSKFile */ +/************************************************************************/ + + class PCIDSKChannel; + class PCIDSKSegment; + class PCIDSKInterfaces; + class Mutex; + +//! Top interface to PCIDSK (.pix) files. + class PCIDSK_DLL PCIDSKFile + { + public: + virtual ~PCIDSKFile() {}; + + virtual PCIDSKInterfaces *GetInterfaces() = 0; + + virtual PCIDSKChannel *GetChannel( int band ) = 0; + virtual PCIDSKSegment *GetSegment( int segment ) = 0; + virtual std::vector GetSegments() = 0; + + virtual PCIDSK::PCIDSKSegment *GetSegment( int type, + std::string name, int previous = 0 ) = 0; + + virtual int GetWidth() const = 0; + virtual int GetHeight() const = 0; + virtual int GetChannels() const = 0; + virtual std::string GetInterleaving() const = 0; + virtual bool GetUpdatable() const = 0; + virtual uint64 GetFileSize() const = 0; + + virtual int CreateSegment( std::string name, std::string description, + eSegType seg_type, int data_blocks ) = 0; + virtual void DeleteSegment( int segment ) = 0; + virtual void CreateOverviews( int chan_count, int *chan_list, + int factor, std::string resampling ) = 0; + + // the following are only for pixel interleaved IO + virtual int GetPixelGroupSize() const = 0; + virtual void *ReadAndLockBlock( int block_index, int xoff=-1, int xsize=-1) = 0; + virtual void UnlockBlock( bool mark_dirty = false ) = 0; + + // low level io, primarily internal. + virtual void WriteToFile( const void *buffer, uint64 offset, uint64 size)=0; + virtual void ReadFromFile( void *buffer, uint64 offset, uint64 size ) = 0; + + virtual void GetIODetails( void ***io_handle_pp, Mutex ***io_mutex_pp, + std::string filename="", bool writable=false ) = 0; + + virtual std::string GetMetadataValue( const std::string& key ) = 0; + virtual void SetMetadataValue( const std::string& key, const std::string& value ) = 0; + virtual std::vector GetMetadataKeys() = 0; + + virtual void Synchronize() = 0; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_FILE_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_gcp.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_gcp.h new file mode 100644 index 000000000..8e9b9c038 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_gcp.h @@ -0,0 +1,204 @@ +/****************************************************************************** + * + * Purpose: Declaration of the PCIDSK::GCP class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __INCLUDE_PCIDSK_SRC_GCP_H +#define __INCLUDE_PCIDSK_SRC_GCP_H + +#include "pcidsk_config.h" + +#include +#include + +namespace PCIDSK { + /** + * \brief PCIDSK Generic GCP Structure + * + * The PCIDSK::GCP class encompases all the possible field + * combinations in the last two revisions of PCI's GCP segment + * type. + * + * If a legacy GCP type is used, the additional information fields + * will return empty values. + */ + class PCIDSK_DLL GCP { + public: + GCP(double x, double y, double z, + double line, double pix, + std::string const& gcp_id, + std::string const& map_units, + std::string const& proj_parms = "", + double xerr = 0.0, double yerr = 0.0, double zerr = 0.0, + double line_err = 0.0, double pix_err = 0.0) + { + ground_point_[0] = x; + ground_point_[1] = y; + ground_point_[2] = z; + + ground_error_[0] = xerr; + ground_error_[1] = yerr; + ground_error_[2] = zerr; + + raster_point_[1] = line; + raster_point_[0] = pix; + + raster_error_[1] = line_err; + raster_error_[0] = pix_err; + + std::memset(gcp_id_, ' ', 64); + + std::strncpy(gcp_id_, gcp_id.c_str(), + gcp_id.size() > 64 ? 64 : gcp_id.size()); + gcp_id_[gcp_id.size() > 64 ? 64 : gcp_id.size()] = '\0'; + + this->map_units_ = map_units; + this->proj_parms_ = proj_parms; + + elevation_unit_ = EMetres; + elevation_datum_ = EEllipsoidal; + iscp_ = false; // default to GCPs + } + + GCP(GCP const& gcp) + { + Copy(gcp); + } + + GCP& operator=(GCP const& gcp) + { + Copy(gcp); + return *this; + } + + enum EElevationDatum + { + EMeanSeaLevel = 0, + EEllipsoidal + }; + + enum EElevationUnit + { + EMetres = 0, + EAmericanFeet, + EInternationalFeet, + EUnknown + }; + + void SetElevationUnit(EElevationUnit unit) + { + elevation_unit_ = unit; + } + + void SetElevationDatum(EElevationDatum datum) + { + elevation_datum_ = datum; + } + + void GetElevationInfo(EElevationDatum& datum, EElevationUnit& unit) const + { + unit = elevation_unit_; + datum = elevation_datum_; + } + + void SetCheckpoint(bool is_checkpoint) + { + iscp_ = is_checkpoint; + } + + bool IsCheckPoint(void) const + { + return iscp_; + } + + double GetX() const { return ground_point_[0]; } + double GetXErr() const { return ground_error_[0]; } + double GetY() const { return ground_point_[1]; } + double GetYErr() const { return ground_error_[1]; } + double GetZ() const { return ground_point_[2]; } + double GetZErr() const { return ground_error_[2]; } + + double GetPixel() const { return raster_point_[0]; } + double GetPixelErr() const { return raster_error_[0]; } + double GetLine() const { return raster_point_[1]; } + double GetLineErr() const { return raster_error_[1]; } + + void GetMapUnits(std::string& map_units, std::string& proj_parms) const + { map_units = map_units_; proj_parms = proj_parms_;} + void SetMapUnits(std::string const& map_units, + std::string const& proj_parms) { map_units_ = map_units; + proj_parms_ = proj_parms;} + + const char* GetIDString(void) const { return gcp_id_; } + private: + void Copy(GCP const& gcp) + { + ground_point_[0] = gcp.ground_point_[0]; + ground_point_[1] = gcp.ground_point_[1]; + ground_point_[2] = gcp.ground_point_[2]; + + ground_error_[0] = gcp.ground_error_[0]; + ground_error_[1] = gcp.ground_error_[1]; + ground_error_[2] = gcp.ground_error_[2]; + + raster_point_[0] = gcp.raster_point_[0]; + raster_point_[1] = gcp.raster_point_[1]; + + raster_error_[0] = gcp.raster_error_[0]; + raster_error_[1] = gcp.raster_error_[1]; + + this->map_units_ = gcp.map_units_; + this->proj_parms_ = gcp.proj_parms_; + this->iscp_ = gcp.iscp_; + + std::strncpy(this->gcp_id_, gcp.gcp_id_, 64); + + this->gcp_id_[64] = '\0'; + + this->elevation_unit_ = gcp.elevation_unit_; + this->elevation_datum_ = gcp.elevation_datum_; + } + + bool iscp_; // true = checkpoint, false = GCP + + EElevationUnit elevation_unit_; + EElevationDatum elevation_datum_; + + // Point information + double ground_point_[3]; + double ground_error_[3]; // variances + + double raster_point_[2]; + double raster_error_[2]; + + char gcp_id_[65]; + + std::string map_units_; ///< PCI mapunits string + std::string proj_parms_; ///< PCI projection parameters string + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_SRC_GCP_H + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_gcpsegment.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_gcpsegment.h new file mode 100644 index 000000000..b8e1ff801 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_gcpsegment.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * + * Purpose: Interface through which a PCIDSK GCP Segment would be accessed + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_PCIDSK_GCPSEGMENT_H +#define __INCLUDE_PCIDSK_PCIDSK_GCPSEGMENT_H + +#include "pcidsk_gcp.h" + +#include + +namespace PCIDSK { + +//! Interface to PCIDSK GCP segment. + class PCIDSKGCPSegment + { + public: + //! Return all GCPs in the segment + virtual std::vector const& GetGCPs(void) const = 0; + + //! Write the given GCPs to the segment. If the segment already exists, it will be replaced with this one. + virtual void SetGCPs(std::vector const& gcps) = 0; + + //! Return the count of GCPs in the segment + virtual unsigned int GetGCPCount(void) const = 0; + + //! Clear a GCP Segment + virtual void ClearGCPs(void) = 0; + + //! Virtual Destructor + virtual ~PCIDSKGCPSegment(void) {} + }; +} // end namespace PCIDSK + + +#endif // __INCLUDE_PCIDSK_PCIDSK_GCPSEGMENT_H + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_georef.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_georef.h new file mode 100644 index 000000000..57a285251 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_georef.h @@ -0,0 +1,161 @@ +/****************************************************************************** + * + * Purpose: PCIDSK Georeferencing information storage class. Declaration. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_GEOREF_H +#define __INCLUDE_PCIDSK_GEOREF_H + +#include +#include + +namespace PCIDSK +{ + typedef enum { + UNIT_US_FOOT = 1, + UNIT_METER = 2, + UNIT_DEGREE = 4, + UNIT_INTL_FOOT = 5 + } UnitCode; + +/************************************************************************/ +/* PCIDSKGeoref */ +/************************************************************************/ + +//! Interface to PCIDSK georeferencing segment. + + class PCIDSK_DLL PCIDSKGeoref + { + public: + virtual ~PCIDSKGeoref() {} + +/** +\brief Get georeferencing transformation. + +Returns the affine georeferencing transform coefficients for this image. +Used to map from pixel/line coordinates to georeferenced coordinates using +the transformation: + + Xgeo = a1 + a2 * Xpix + xrot * Ypix + + Ygeo = b1 + yrot * Xpix + b2 * Ypix + +where Xpix and Ypix are pixel line locations with (0,0) being the top left +corner of the top left pixel, and (0.5,0.5) being the center of the top left +pixel. For an ungeoreferenced image the values will be +(0.0,1.0,0.0,0.0,0.0,1.0). + +@param a1 returns easting of top left corner. +@param a2 returns easting pixel size. +@param xrot returns rotational coefficient, normally zero. +@param b1 returns northing of the top left corner. +@param yrot returns rotational coefficient, normally zero. +@param b3 returns northing pixel size, normally negative indicating north-up. + +*/ + virtual void GetTransform( double &a1, double &a2, double &xrot, + double &b1, double &yrot, double &b3 ) = 0; + +/** +\brief Fetch georeferencing string. + +Returns the short, 16 character, georeferncing string. This string is +sufficient to document the coordinate system of simple coordinate +systems (like "UTM 17 S D000"), while other coordinate systems are +only fully defined with additional projection parameters. + +@return the georeferencing string. + +*/ + virtual std::string GetGeosys() = 0; + +/** +\brief Fetch projection parameters. + +Fetches the list of detailed projection parameters used for projection +methods not fully described by the Geosys string. The projection +parameters are as shown below, though in the future more items might +be added to the array. The first 15 are the classic USGS GCTP parameters. + +
      +
    • Parm[0]: diameter of earth - major axis (meters). +
    • Parm[1]: diameter of earth - minor axis (meters). +
    • Parm[2]: Reference Longitude (degrees) +
    • Parm[3]: Reference Latitude (degrees) +
    • Parm[4]: Standard Parallel 1 (degrees) +
    • Parm[5]: Standard Parallel 2 (degrees) +
    • Parm[6]: False Easting (meters?) +
    • Parm[7]: False Northing (meters?) +
    • Parm[8]: Scale (unitless) +
    • Parm[9]: Height (meters?) +
    • Parm[10]: Longitude 1 (degrees) +
    • Parm[11]: Latitude 1 (degrees) +
    • Parm[12]: Longitude 2 (degrees) +
    • Parm[13]: Latitude 2 (degrees) +
    • Parm[14]: Azimuth (degrees) +
    • Parm[15]: Landsat Number +
    • Parm[16]: Landsat Path +
    • Parm[17]: Unit Code (1=US Foot, 2=Meter, 4=Degree, 5=Intl Foot). +
    + +Review the PCIDSK Database Reference Manual to understand which parameters +apply to which projection methods. + +@return an array of values, at least 18. +*/ + + virtual std::vector GetParameters() = 0; + +/** +\brief Write simple georeferencing information + +Writes out a georeferencing string and geotransform to the segment. + +@param geosys 16 character coordinate system, like "UTM 17 S D000". +@param a1 easting of top left corner. +@param a2 easting pixel size. +@param xrot rotational coefficient, normally zero. +@param b1 northing of the top left corner. +@param yrot rotational coefficient, normally zero. +@param b3 northing pixel size, normally negative indicating north-up. + +*/ + virtual void WriteSimple( std::string const& geosys, + double a1, double a2, double xrot, + double b1, double yrot, double b3 ) = 0; + +/** +\brief Write complex projection parameters. + +See GetParameters() for the description of the parameters list. + +@param parameters A list of at least 17 projection parameters. + +*/ + + virtual void WriteParameters( std::vector const& parameters ) = 0; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_GEOREF_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_interfaces.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_interfaces.h new file mode 100644 index 000000000..843adecd0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_interfaces.h @@ -0,0 +1,65 @@ +/****************************************************************************** + * + * Purpose: Declaration of hookable interfaces for the library + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __INCLUDE_PCIDSK_INTERFACES_H +#define __INCLUDE_PCIDSK_INTERFACES_H + +#include "pcidsk_io.h" +#include "pcidsk_mutex.h" +#include "pcidsk_edb.h" + +namespace PCIDSK +{ + /************************************************************************/ + /* PCIDSKInterfaces */ + /************************************************************************/ + + //! Collection of PCIDSK hookable interfaces. + + class PCIDSK_DLL PCIDSKInterfaces + { + public: + PCIDSKInterfaces(); + + const IOInterfaces *io; + + EDBFile *(*OpenEDB)(std::string filename, std::string access); + + Mutex *(*CreateMutex)(void); + + void (*JPEGDecompressBlock) + ( uint8 *src_data, int src_bytes, uint8 *dst_data, int dst_bytes, + int xsize, int ysize, eChanType pixel_type ); + void (*JPEGCompressBlock) + ( uint8 *src_data, int src_bytes, uint8 *dst_data, int &dst_bytes, + int xsize, int ysize, eChanType pixel_type, int quality ); + + void (*Debug)( const char * ); + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_INTERFACES_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_io.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_io.h new file mode 100644 index 000000000..24e336c8e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_io.h @@ -0,0 +1,61 @@ +/****************************************************************************** + * + * Purpose: PCIDSK I/O Interface declaration. The I/O interfaces for the + * library can be overridden by an object implementing this class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_IO_H +#define __INCLUDE_PCIDSK_IO_H + +#include "pcidsk_config.h" + +#include + +namespace PCIDSK +{ +/************************************************************************/ +/* IOInterfaces */ +/************************************************************************/ + +//! IO Interface class. + + class IOInterfaces + { + public: + virtual ~IOInterfaces() {} + virtual void *Open( std::string filename, std::string access ) const = 0; + virtual uint64 Seek( void *io_handle, uint64 offset, int whence ) const = 0; + virtual uint64 Tell( void *io_handle ) const = 0; + virtual uint64 Read( void *buffer, uint64 size, uint64 nmemb, void *io_handle ) const = 0; + virtual uint64 Write( const void *buffer, uint64 size, uint64 nmemb, void *io_handle ) const = 0; + virtual int Eof( void *io_handle ) const = 0; + virtual int Flush( void *io_handle ) const = 0; + virtual int Close( void *io_handle ) const = 0; + }; + + const IOInterfaces PCIDSK_DLL *GetDefaultIOInterfaces(); + +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_IO_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_mutex.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_mutex.h new file mode 100644 index 000000000..97cae0c33 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_mutex.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * + * Purpose: Interface. PCIDSK Mutex Class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_MUTEX_H +#define __INCLUDE_PCIDSK_MUTEX_H + +#include "pcidsk_config.h" + +namespace PCIDSK +{ +/************************************************************************/ +/* Mutex */ +/************************************************************************/ + + class Mutex + { + public: + virtual ~Mutex() {} + + virtual int Acquire() = 0; + virtual int Release() = 0; + }; + + Mutex PCIDSK_DLL *DefaultCreateMutex(void); +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_MUTEX_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_pct.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_pct.h new file mode 100644 index 000000000..3867aa539 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_pct.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * + * Purpose: PCIDSK PCT segment interface class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_PCT_H +#define __INCLUDE_PCIDSK_PCT_H + +#include +#include + +namespace PCIDSK +{ +/************************************************************************/ +/* PCIDSK_PCT */ +/************************************************************************/ + +//! Interface to PCIDSK pseudo-color segment. + + class PCIDSK_DLL PCIDSK_PCT + { + public: + virtual ~PCIDSK_PCT() {} + +/** +\brief Read a PCT Segment (SEG_PCT). + +@param pct Pseudo-Color Table buffer (768 entries) into which the +pseudo-color table is read. It consists of the red gun output +values (pct[0-255]), followed by the green gun output values (pct[256-511]) +and ends with the blue gun output values (pct[512-767]). + +*/ + virtual void ReadPCT( unsigned char pct[768] ) = 0; + +/** +\brief Write a PCT Segment. + +@param pct Pseudo-Color Table buffer (768 entries) from which the +pseudo-color table is written. It consists of the red gun output +values (pct[0-255]), followed by the green gun output values (pct[256-511]) +and ends with the blue gun output values (pct[512-767]). + +*/ + virtual void WritePCT( unsigned char pct[768] ) = 0; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_PCT_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_poly.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_poly.h new file mode 100644 index 000000000..a0d579aa7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_poly.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * + * Purpose: Interface representing access to a PCIDSK Polynomial Segment + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_PCIDSK_POLY_H +#define __INCLUDE_PCIDSK_PCIDSK_POLY_H + +#include +#include + +namespace PCIDSK { +//! Interface to PCIDSK Polynomial segment. + class PCIDSKPolySegment + { + public: + //Get the coefficients + virtual std::vector GetXForwardCoefficients() const=0; + virtual std::vector GetYForwardCoefficients() const=0; + virtual std::vector GetXBackwardCoefficients() const=0; + virtual std::vector GetYBackwardCoefficients() const=0; + + //Set the coefficients + virtual void SetCoefficients(const std::vector& oXForward, + const std::vector& oYForward, + const std::vector& oXBackward, + const std::vector& oYBackward) =0; + + // Get the number of lines + virtual unsigned int GetLines() const=0; + // Get the number of pixels + virtual unsigned int GetPixels() const=0; + // Set the number of lines/pixels + virtual void SetRasterSize(unsigned int nLines,unsigned int nPixels) =0; + + // Get the Geosys String + virtual std::string GetGeosysString() const=0; + // Set the Geosys string + virtual void SetGeosysString(const std::string& oGeosys) =0; + + //Get the projection informations + virtual std::vector GetProjParmInfo() const=0; + //Set the projection informations + virtual void SetProjParmInfo(const std::vector& oInfo) =0; + + // Virtual destructor + virtual ~PCIDSKPolySegment() {} + }; +} + +#endif // __INCLUDE_PCIDSK_PCIDSK_POLY_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_rpc.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_rpc.h new file mode 100644 index 000000000..4be951477 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_rpc.h @@ -0,0 +1,112 @@ +/****************************************************************************** + * + * Purpose: Interface representing access to a PCIDSK RPC Segment + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_PCIDSK_RPC_H +#define __INCLUDE_PCIDSK_PCIDSK_RPC_H + +#include +#include + +namespace PCIDSK { +//! Interface to PCIDSK RPC segment. + class PCIDSKRPCSegment + { + public: + + // Get the X and Y RPC coefficients + virtual std::vector GetXNumerator(void) const = 0; + virtual std::vector GetXDenominator(void) const = 0; + virtual std::vector GetYNumerator(void) const = 0; + virtual std::vector GetYDenominator(void) const = 0; + + // Set the X and Y RPC Coefficients + virtual void SetCoefficients(const std::vector& xnum, + const std::vector& xdenom, const std::vector& ynum, + const std::vector& ydenom) = 0; + + // Get the RPC offset/scale Coefficients + virtual void GetRPCTranslationCoeffs(double& xoffset, double& xscale, + double& yoffset, double& yscale, double& zoffset, double& zscale, + double& pixoffset, double& pixscale, double& lineoffset, double& linescale) const = 0; + + // Set the RPC offset/scale Coefficients + virtual void SetRPCTranslationCoeffs(const double xoffset, const double xscale, + const double yoffset, const double yscale, + const double zoffset, const double zscale, + const double pixoffset, const double pixscale, + const double lineoffset, const double linescale) = 0; + + // Get the adjusted X values + virtual std::vector GetAdjXValues(void) const = 0; + // Get the adjusted Y values + virtual std::vector GetAdjYValues(void) const = 0; + + // Set the adjusted X/Y values + virtual void SetAdjCoordValues(const std::vector& xcoord, + const std::vector& ycoord) = 0; + + // Get whether or not this is a user-generated RPC model + virtual bool IsUserGenerated(void) const = 0; + // Set whether or not this is a user-generated RPC model + virtual void SetUserGenerated(bool usergen) = 0; + + // Get whether the model has been adjusted + virtual bool IsNominalModel(void) const = 0; + // Set whether the model has been adjusted + virtual void SetIsNominalModel(bool nominal) = 0; + + // Get sensor name + virtual std::string GetSensorName(void) const = 0; + // Set sensor name + virtual void SetSensorName(const std::string& name) = 0; + + // Output projection information of RPC Model + // Get the Geosys String + virtual std::string GetGeosysString(void) const = 0; + // Set the Geosys string + virtual void SetGeosysString(const std::string& geosys) = 0; + + // Get the number of lines + virtual unsigned int GetLines(void) const = 0; + + // Get the number of pixels + virtual unsigned int GetPixels(void) const = 0; + + // Set the number of lines/pixels + virtual void SetRasterSize(const unsigned int lines, const unsigned int pixels) = 0; + + // Set/get the downsample factor + virtual void SetDownsample(const unsigned int downsample) = 0; + virtual unsigned int GetDownsample(void) const = 0; + + // TODO: Setting/getting detailed projection params (just GCTP params?) + + // Virtual destructor + virtual ~PCIDSKRPCSegment() {} + }; +} + +#endif // __INCLUDE_PCIDSK_PCIDSK_RPC_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_segment.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_segment.h new file mode 100644 index 000000000..9aa500eb5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_segment.h @@ -0,0 +1,79 @@ +/****************************************************************************** + * + * Purpose: Primary public include file for PCIDSK SDK. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __INCLUDE_SEGMENT_PCIDSKSEGMENT_H +#define __INCLUDE_SEGMENT_PCIDSKSEGMENT_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include +#include + +namespace PCIDSK +{ +/************************************************************************/ +/* PCIDSKSegment */ +/************************************************************************/ + +//! Public interface for the PCIDSK Segment Type + + class PCIDSKSegment + { + public: + virtual ~PCIDSKSegment() {} + + virtual void Initialize() {} + + virtual void WriteToFile( const void *buffer, uint64 offset, uint64 size)=0; + virtual void ReadFromFile( void *buffer, uint64 offset, uint64 size ) = 0; + + virtual eSegType GetSegmentType() = 0; + virtual std::string GetName() = 0; + virtual std::string GetDescription() = 0; + virtual int GetSegmentNumber() = 0; + virtual uint64 GetContentSize() = 0; + virtual bool IsAtEOF() = 0; + + virtual void SetDescription( const std::string &description) = 0; + + virtual std::string GetMetadataValue( const std::string &key ) const = 0; + virtual void SetMetadataValue( const std::string &key, const std::string &value ) = 0; + virtual std::vector GetMetadataKeys() const = 0; + + virtual std::vector GetHistoryEntries() const = 0; + virtual void SetHistoryEntries( const std::vector &entries ) = 0; + virtual void PushHistory(const std::string &app, + const std::string &message) = 0; + + virtual void Synchronize() = 0; + + virtual std::string ConsistencyCheck() = 0; + }; + +} // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_PCIDSKSEGMENT_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_shape.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_shape.h new file mode 100644 index 000000000..0dd3c7c8c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_shape.h @@ -0,0 +1,272 @@ +/****************************************************************************** + * + * Purpose: PCIDSK Vector Shape interface. Declaration. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __INCLUDE_PCIDSK_SHAPE_H +#define __INCLUDE_PCIDSK_SHAPE_H + +#include +#include +#include +#include + +namespace PCIDSK +{ + + //! Type used for shape identifier, use constant NullShapeId as a NULL value + typedef int32 ShapeId; + + static const ShapeId NullShapeId = -1; + + //! Structure for an x,y,z point. + typedef struct + { + double x; + double y; + double z; + } ShapeVertex; + +/************************************************************************/ +/* ShapeFieldType */ +/************************************************************************/ + //! Attribute field types. + typedef enum // These deliberately match GDBFieldType values. + { + FieldTypeNone = 0, + FieldTypeFloat = 1, + FieldTypeDouble = 2, + FieldTypeString = 3, + FieldTypeInteger = 4, + FieldTypeCountedInt = 5 + } ShapeFieldType; + +/************************************************************************/ +/* ShapeFieldTypeName() */ +/************************************************************************/ + /** + \brief Translate field type into a textual description. + @param type the type enumeration value to translate. + @return name for field type. + */ + inline std::string ShapeFieldTypeName( ShapeFieldType type ) + { + switch( type ) { + case FieldTypeNone: return "None"; + case FieldTypeFloat: return "Float"; + case FieldTypeDouble: return "Double"; + case FieldTypeString: return "String"; + case FieldTypeInteger: return "Integer"; + case FieldTypeCountedInt: return "CountedInt"; + } + return ""; + } + + +/************************************************************************/ +/* ShapeField */ +/************************************************************************/ + /** + \brief Attribute field value. + + This class encapsulates any of the supported vector attribute field + types in a convenient way that avoids memory leaks or ownership confusion. + The object has a field type (initially FieldTypeNone on construction) + and a value of the specified type. Note that the appropriate value + accessor (ie. GetValueInteger()) must be used that corresponds to the + fields type. No attempt is made to automatically convert (ie. float to + double) if the wrong accessor is used. + + */ + + class ShapeField + { + private: + ShapeFieldType type; // use FieldTypeNone for NULL fields. + + union + { + float float_val; + double double_val; + char *string_val; + int32 integer_val; + int32 *integer_list_val; + } v; + + public: + //! Simple constructor. + ShapeField() + { v.string_val = NULL; type = FieldTypeNone; } + + //! Copy constructor. + ShapeField( const ShapeField &src ) + { v.string_val = NULL; type = FieldTypeNone; *this = src; } + + ~ShapeField() + { Clear(); } + + //! Assignment operator. + ShapeField &operator=( const ShapeField &src ) + { + switch( src.GetType() ) + { + case FieldTypeFloat: + SetValue( src.GetValueFloat() ); + break; + case FieldTypeDouble: + SetValue( src.GetValueDouble() ); + break; + case FieldTypeInteger: + SetValue( src.GetValueInteger() ); + break; + case FieldTypeCountedInt: + SetValue( src.GetValueCountedInt() ); + break; + case FieldTypeString: + SetValue( src.GetValueString() ); + break; + case FieldTypeNone: + Clear(); + break; + } + return *this; + } + + //! Assignment operator. + bool operator==( const ShapeField &other ) + { + if( GetType() != other.GetType() ) + return false; + + switch( other.GetType() ) + { + case FieldTypeFloat: + return GetValueFloat() == other.GetValueFloat(); + case FieldTypeDouble: + return GetValueDouble() == other.GetValueDouble(); + case FieldTypeInteger: + return GetValueInteger() == other.GetValueInteger(); + case FieldTypeString: + return GetValueString() == other.GetValueString(); + case FieldTypeCountedInt: + return GetValueCountedInt() == other.GetValueCountedInt(); + case FieldTypeNone: + return false; + default: + return false; + } + } + + //! Clear field value. + void Clear() + { + if( (type == FieldTypeString || type == FieldTypeCountedInt) + && v.string_val != NULL ) + { + free( v.string_val ); + v.string_val = NULL; + } + type = FieldTypeNone; + } + + //! Fetch field type + ShapeFieldType GetType() const + { return type; } + + //! Set integer value on field. + void SetValue( int32 val ) + { + Clear(); + type = FieldTypeInteger; + v.integer_val = val; + } + + //! Set integer list value on field. + void SetValue( const std::vector &val ) + { + Clear(); + type = FieldTypeCountedInt; + v.integer_list_val = (int32*) + malloc(sizeof(int32) * (val.size()+1) ); + v.integer_list_val[0] = val.size(); + if( val.size() > 0 ) + memcpy( v.integer_list_val+1, &(val[0]), + sizeof(int32) * val.size() ); + } + + //! Set string value on field. + void SetValue( const std::string &val ) + { + Clear(); + type = FieldTypeString; + v.string_val = strdup(val.c_str()); + } + + //! Set double precision floating point value on field. + void SetValue( double val ) + { + Clear(); + type = FieldTypeDouble; + v.double_val = val; + } + + //! Set single precision floating point value on field. + void SetValue( float val ) + { + Clear(); + type = FieldTypeFloat; + v.float_val = val; + } + + //! Fetch value as integer or zero if field not of appropriate type. + int32 GetValueInteger() const + { if( type == FieldTypeInteger ) return v.integer_val; else return 0; } + //! Fetch value as integer list or empty list if field not of appropriate type. + std::vector GetValueCountedInt() const + { + std::vector result; + if( type == FieldTypeCountedInt ) + { + result.resize( v.integer_list_val[0] ); + if( v.integer_list_val[0] > 0 ) + memcpy( &(result[0]), &(v.integer_list_val[1]), + (v.integer_list_val[0]) * sizeof(int32) ); + } + return result; + } + //! Fetch value as string or "" if field not of appropriate type. + std::string GetValueString() const + { if( type == FieldTypeString ) return v.string_val; else return ""; } + //! Fetch value as float or 0.0 if field not of appropriate type. + float GetValueFloat() const + { if( type == FieldTypeFloat ) return v.float_val; else return 0.0; } + //! Fetch value as double or 0.0 if field not of appropriate type. + double GetValueDouble() const + { if( type == FieldTypeDouble ) return v.double_val; else return 0.0; } + }; + +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_SHAPE_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_tex.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_tex.h new file mode 100644 index 000000000..cf9cb9581 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_tex.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * + * Purpose: PCIDSK TEXt segment interface class. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_TEX_H +#define __INCLUDE_PCIDSK_TEX_H + +#include +#include + +namespace PCIDSK +{ +/************************************************************************/ +/* PCIDSK_TEX */ +/************************************************************************/ + +//! Interface to PCIDSK text segment. + + class PCIDSK_DLL PCIDSK_TEX + { + public: + virtual ~PCIDSK_TEX() {} + +/** +\brief Read a text segment (SEG_TEX). + +All carriage returns in the file are converted to newlines during reading. No other processing is done. + +@return a string containing the entire contents of the text segment. + +*/ + virtual std::string ReadText() = 0; + +/** +\brief Write a text segment. + +Writes the text to the text segment. All newlines will be converted to +carriage controls for storage in the text segment per the normal text segment +conventions, and if missing a carriage return will be added to the end of the +file. + +@param text the text to write to the segment. May contain newlines, and other special characters but no embedded \0 characters. + +*/ + virtual void WriteText( const std::string &text ) = 0; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_TEX_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_toutin.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_toutin.h new file mode 100644 index 000000000..b9cc20054 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_toutin.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * + * Purpose: Interface representing access to a PCIDSK Toutin Segment + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_PCIDSK_TOUTIN_H +#define __INCLUDE_PCIDSK_PCIDSK_TOUTIN_H + +#include +#include +#include "segment/toutinstructures.h" + +namespace PCIDSK { +//! Interface to PCIDSK RPC segment. + class PCIDSKToutinSegment + { + public: + + // Virtual destructor + virtual ~PCIDSKToutinSegment() {} + + virtual SRITInfo_t GetInfo() const =0; + virtual void SetInfo(const SRITInfo_t& poInfo) =0; + }; +} + +#endif // __INCLUDE_PCIDSK_PCIDSK_TOUTIN_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_types.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_types.h new file mode 100644 index 000000000..2bb937835 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_types.h @@ -0,0 +1,79 @@ +/****************************************************************************** + * + * Purpose: Enumerations, data types and related helpers for the PCIDSK SDK + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_TYPES_H +#define __INCLUDE_PCIDSK_TYPES_H + +#include "pcidsk_config.h" + +#include + +namespace PCIDSK +{ + //! Channel pixel data types. + typedef enum { + CHN_8U=0, /*!< 8 bit unsigned byte */ + CHN_16S=1, /*!< 16 bit signed integer */ + CHN_16U=2, /*!< 16 bit unsigned integer */ + CHN_32R=3, /*!< 32 bit ieee floating point */ + CHN_C16U=4, /*!< 16-bit unsigned integer, complex */ + CHN_C16S=5, /*!< 16-bit signed integer, complex */ + CHN_C32R=6, /*!< 32-bit IEEE-754 Float, complex */ + CHN_BIT=7, /*!< 1bit unsigned (packed bitmap) */ + CHN_UNKNOWN=99 /*!< unknown channel type */ + } eChanType; + + //! Segment types. + typedef enum { + SEG_UNKNOWN = -1, + + SEG_BIT = 101, + SEG_VEC = 116, + SEG_SIG = 121, + SEG_TEX = 140, + SEG_GEO = 150, + SEG_ORB = 160, + SEG_LUT = 170, + SEG_PCT = 171, + SEG_BLUT = 172, + SEG_BPCT = 173, + SEG_BIN = 180, + SEG_ARR = 181, + SEG_SYS = 182, + SEG_GCPOLD = 214, + SEG_GCP2 = 215 + } eSegType; + + // Helper functions for working with segments and data types + int PCIDSK_DLL DataTypeSize( eChanType ); + std::string PCIDSK_DLL DataTypeName( eChanType ); + std::string PCIDSK_DLL SegmentTypeName( eSegType ); + eChanType PCIDSK_DLL GetDataTypeFromName(std::string const& type_name); + bool PCIDSK_DLL IsDataTypeComplex(eChanType type); + +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_TYPES_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_vectorsegment.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_vectorsegment.h new file mode 100644 index 000000000..19443339b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/pcidsk_vectorsegment.h @@ -0,0 +1,309 @@ +/****************************************************************************** + * + * Purpose: PCIDSK Vector Segment public interface. Declaration. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __INCLUDE_PCIDSK_VECTORSEGMENT_H +#define __INCLUDE_PCIDSK_VECTORSEGMENT_H + +#include +#include +#include +#include "pcidsk_shape.h" + +namespace PCIDSK +{ + class ShapeIterator; + +/************************************************************************/ +/* PCIDSKVectorSegment */ +/************************************************************************/ + +/** +\brief Interface to PCIDSK vector segment. + +The vector segment contains a set of vector features with a common set +of attribute data (fields). Each feature has a numeric identifier (ShapeId), +a set of field values, and a set of geometric vertices. The layer as a +whole has a description of the attribute fields, and an RST (Representation +Style Table). + +The geometry and attribute fields of shapes can be fetched with the +GetVertices() and GetFields() methods by giving the ShapeId of the desired +feature. The set of shapeid's can be identified using the FindFirst(), +and FindNext() methods or the STL compatible ShapeIterator (begin() and +end() methods). + +The PCIDSKSegment interface for the segment can be used to fetch the +LAYER_TYPE metadata describing how the vertices should be interpreted +as a geometry. Some layers will also have a RingStart attribute field +which is used in conjunction with the LAYER_TYPE to interprete the +geometry. Some vector segments may have no LAYER_TYPE metadata in which +case single vertices are interpreted as points, and multiple vertices +as linestrings. + +More details are available in the GDB.HLP description of the GDB vector +data model. + +Note that there are no mechanisms for fast spatial or attribute searches +in a PCIDSK vector segment. Accessing features randomly (rather than +in the order shapeids are returned by FindFirst()/FindNext() or ShapeIterator +) may result in reduced performance, and the use of large amounts of memory +for large vector segments. + +*/ + + class PCIDSK_DLL PCIDSKVectorSegment + { + public: + virtual ~PCIDSKVectorSegment() {} + +/** +\brief Fetch RST. + +No attempt is made to parse the RST, it is up to the caller to decode it. + +NOTE: There is some header info on RST format that may be needed to do this +for older RSTs. + +@return RST as a string. +*/ + virtual std::string GetRst() = 0; + + +/** +\brief Fetch Projection + +The returned values are the projection parameters in the same form returned +by PCIDSKGeoref::GetParameters() and the passed in geosys argument is +updated with the coordinate system string. + +@return Projection parameters as a vector. +*/ + virtual std::vector GetProjection( std::string &geosys ) = 0; + +/** +\brief Get field count. + +Note that this includes any system attributes, like RingStart, that would +not normally be shown to the user. + +@return the number of attribute fields defined on this layer. +*/ + + virtual int GetFieldCount() = 0; + +/** +\brief Get field name. + +@param field_index index of the field requested from zero to GetFieldCount()-1. +@return the field name. +*/ + virtual std::string GetFieldName(int field_index) = 0; + +/** +\brief Get field description. + +@param field_index index of the field requested from zero to GetFieldCount()-1. +@return the field description, often empty. +*/ + virtual std::string GetFieldDescription(int field_index) = 0; + +/** +\brief Get field type. + +@param field_index index of the field requested from zero to GetFieldCount()-1. +@return the field type. +*/ + virtual ShapeFieldType GetFieldType(int field_index) = 0; + +/** +\brief Get field format. + +@param field_index index of the field requested from zero to GetFieldCount()-1. +@return the field format as a C style format string suitable for use with printf. +*/ + virtual std::string GetFieldFormat(int field_index) = 0; + +/** +\brief Get field default. + +@param field_index index of the field requested from zero to GetFieldCount()-1. +@return the field default value. +*/ + virtual ShapeField GetFieldDefault(int field_index) = 0; + +/** +\brief Get iterator to first shape. +@return iterator. +*/ + virtual ShapeIterator begin() = 0; + +/** +\brief Get iterator to end of shape lib (a wrapper for NullShapeId). +@return iterator. +*/ + virtual ShapeIterator end() = 0; + +/** +\brief Fetch first shapeid in the layer. +@return first shape's shapeid. +*/ + virtual ShapeId FindFirst() = 0; + +/** +\brief Fetch the next shape id after the indicated shape id. +@param id the previous shapes id. +@return next shape's shapeid. +*/ + virtual ShapeId FindNext(ShapeId id) = 0; + + +/** +\brief Fetch the number of shapes in this segment. +@return the shape count. +*/ + + virtual int GetShapeCount() = 0; + +/** +\brief Fetch the vertices for the indicated shape. +@param id the shape to fetch +@param list the list is updated with the vertices for this shape. +*/ + virtual void GetVertices( ShapeId id, + std::vector& list ) = 0; + +/** +\brief Fetch the fields for the indicated shape. +@param id the shape to fetch +@param list the field list is updated with the field values for this shape. +*/ + virtual void GetFields( ShapeId id, + std::vector& list ) = 0; + + +/** +\brief Set the projection for the segment. + +For details on the geosys and parms values see the PCIDSKGeoref class. + +@param geosys the usual 16 character coordinate system string. +@param parms additional parameters needed for user parametrized projection. +*/ + virtual void SetProjection(std::string geosys, + std::vector parms ) = 0; + +/** +\brief Create new attribute field. + +@param name the field name, should be unique in layer. +@param type the field type. +@param description the field description. +@param format the C style format string or "" for default formatting. +@param default_value the default value for this field or NULL for system default. +*/ + + virtual void AddField( std::string name, ShapeFieldType type, + std::string description, + std::string format, + ShapeField *default_value=NULL ) = 0; + +/** +\brief Create a new shape. + +Newly created shapes have no geometry or attribute values. + +@param id The ShapeId to assign to the new shape, or default to assign the next available shapeid. + +@return the shapeid assigned to the newly created shape. +*/ + + virtual ShapeId CreateShape( ShapeId id = NullShapeId ) = 0; + +/** +\brief Delete a shape. + +An exception is thrown if the shape does not exist. + +@param id the shapeid to delete. + +*/ + virtual void DeleteShape( ShapeId id ) = 0; + +/** +\brief Assign vertices to shape. + +@param id the shape to assign vertices to. +@param list the list of vertices to assign. +*/ + + virtual void SetVertices( ShapeId id, + const std::vector &list ) = 0; + + +/** +\brief Assign attribute value to a shape. + +The list of fields should match the types and length from the schema +(GetFieldCount(), GetFieldType()). + +@param id the shape to update. +@param list the list of field value to assign. +*/ + virtual void SetFields( ShapeId id, + const std::vector& list) = 0; + +// Methods needed + // DeleteField + }; + +/************************************************************************/ +/* ShapeIterator */ +/************************************************************************/ + +//! Iterator over shapeids in a vector segment. + + class ShapeIterator : public std::iterator + { + ShapeId id; + PCIDSKVectorSegment *seg; + + public: + ShapeIterator(PCIDSKVectorSegment *seg_in) + : seg(seg_in) { id = seg->FindFirst(); } + ShapeIterator(PCIDSKVectorSegment *seg_in, ShapeId id_in ) + : id(id_in), seg(seg_in) {} + ShapeIterator(const ShapeIterator& mit) : id(mit.id), seg(mit.seg) {} + ShapeIterator& operator++() { id=seg->FindNext(id); return *this;} + ShapeIterator& operator++(int) { id=seg->FindNext(id); return *this;} + bool operator==(const ShapeIterator& rhs) {return id == rhs.id;} + bool operator!=(const ShapeIterator& rhs) {return id != rhs.id;} + ShapeId& operator*() {return id;} + }; + +} // end namespace PCIDSK + +#endif // __INCLUDE_PCIDSK_VECTORSEGMENT_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/Makefile b/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/Makefile new file mode 100644 index 000000000..a954348d5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/Makefile @@ -0,0 +1,8 @@ +default: + (cd .. ; $(MAKE)) + +clean: + (cd .. ; $(MAKE) clean) + +check: + (cd .. ; $(MAKE) check) diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/io_stdio.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/io_stdio.cpp new file mode 100644 index 000000000..6e8a11313 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/io_stdio.cpp @@ -0,0 +1,258 @@ +/****************************************************************************** + * + * Purpose: Implementation of a stdio based IO layer. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_io.h" +#include "pcidsk_exception.h" +#include +#include +#include +#include + +using namespace PCIDSK; + +class StdioIOInterface : public IOInterfaces +{ + virtual void *Open( std::string filename, std::string access ) const; + virtual uint64 Seek( void *io_handle, uint64 offset, int whence ) const; + virtual uint64 Tell( void *io_handle ) const; + virtual uint64 Read( void *buffer, uint64 size, uint64 nmemb, void *io_hanle ) const; + virtual uint64 Write( const void *buffer, uint64 size, uint64 nmemb, void *io_handle ) const; + virtual int Eof( void *io_handle ) const; + virtual int Flush( void *io_handle ) const; + virtual int Close( void *io_handle ) const; + + const char *LastError() const; +}; + +typedef struct { + FILE *fp; + uint64 offset; + bool last_op_write; +} FileInfo; + +/************************************************************************/ +/* GetDefaultIOInterfaces() */ +/************************************************************************/ + +/** + * Fetch default IO interfaces. + * + * Returns the default IO interfaces implemented in the PCIDSK library. + * These are suitable for use in a PCIDSK::PCIDSKInterfaces object. + * + * @return pointer to internal IO interfaces class. + */ +const IOInterfaces *PCIDSK::GetDefaultIOInterfaces() +{ + static StdioIOInterface singleton_stdio_interface; + + return &singleton_stdio_interface; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +void * +StdioIOInterface::Open( std::string filename, std::string access ) const + +{ + std::string adjusted_access = access; + + adjusted_access += "b"; + + FILE *fp = fopen( filename.c_str(), adjusted_access.c_str() ); + + if( fp == NULL ) + ThrowPCIDSKException( "Failed to open %s: %s", + filename.c_str(), LastError() ); + + FileInfo *fi = new FileInfo(); + fi->fp = fp; + fi->offset = 0; + fi->last_op_write = false; + + return fi; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +uint64 +StdioIOInterface::Seek( void *io_handle, uint64 offset, int whence ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + + // seeks that do nothing are still surprisingly expensive with MSVCRT. + // try and short circuit if possible. + if( whence == SEEK_SET && offset == fi->offset ) + return 0; + + uint64 result = fseek( fi->fp, offset, whence ); + + if( result == (uint64) -1 ) + ThrowPCIDSKException( "Seek(%d,%d): %s", + (int) offset, whence, + LastError() ); + + if( whence == SEEK_SET ) + fi->offset = offset; + else if( whence == SEEK_END ) + fi->offset = ftell( fi->fp ); + else if( whence == SEEK_CUR ) + fi->offset += offset; + + fi->last_op_write = false; + + return result; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +uint64 StdioIOInterface::Tell( void *io_handle ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + + return ftell( fi->fp ); +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +uint64 StdioIOInterface::Read( void *buffer, uint64 size, uint64 nmemb, + void *io_handle ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + + errno = 0; + +/* -------------------------------------------------------------------- */ +/* If a fwrite() is followed by an fread(), the POSIX rules are */ +/* that some of the write may still be buffered and lost. We */ +/* are required to do a seek between to force flushing. So we */ +/* keep careful track of what happened last to know if we */ +/* skipped a flushing seek that we may need to do now. */ +/* -------------------------------------------------------------------- */ + if( fi->last_op_write ) + fseek( fi->fp, fi->offset, SEEK_SET ); + +/* -------------------------------------------------------------------- */ +/* Do the read. */ +/* -------------------------------------------------------------------- */ + uint64 result = fread( buffer, size, nmemb, fi->fp ); + + if( errno != 0 && result == 0 && nmemb != 0 ) + ThrowPCIDSKException( "Read(%d): %s", + (int) size * nmemb, + LastError() ); + + fi->offset += size*result; + fi->last_op_write = false; + + return result; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +uint64 StdioIOInterface::Write( const void *buffer, uint64 size, uint64 nmemb, + void *io_handle ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + + errno = 0; + + uint64 result = fwrite( buffer, size, nmemb, fi->fp ); + + if( errno != 0 && result == 0 && nmemb != 0 ) + ThrowPCIDSKException( "Write(%d): %s", + (int) size * nmemb, + LastError() ); + + fi->offset += size*result; + fi->last_op_write = true; + + return result; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int StdioIOInterface::Eof( void *io_handle ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + return feof( fi->fp ); +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int StdioIOInterface::Flush( void *io_handle ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + return fflush( fi->fp ); +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int StdioIOInterface::Close( void *io_handle ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + int result = fclose( fi->fp ); + + delete fi; + + return result; +} + +/************************************************************************/ +/* LastError() */ +/* */ +/* Return a string representation of the last error. */ +/************************************************************************/ + +const char *StdioIOInterface::LastError() const + +{ + return strerror( errno ); +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/io_win32.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/io_win32.cpp new file mode 100644 index 000000000..d24c93de8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/io_win32.cpp @@ -0,0 +1,329 @@ +/****************************************************************************** + * + * Purpose: Implementation of IO interface using Win32 API. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_io.h" +#include "pcidsk_exception.h" +#include +#include +#include +#include +#include +#include + +using namespace PCIDSK; + +class Win32IOInterface : public IOInterfaces +{ + virtual void *Open( std::string filename, std::string access ) const; + virtual uint64 Seek( void *io_handle, uint64 offset, int whence ) const; + virtual uint64 Tell( void *io_handle ) const; + virtual uint64 Read( void *buffer, uint64 size, uint64 nmemb, void *io_handle ) const; + virtual uint64 Write( const void *buffer, uint64 size, uint64 nmemb, void *io_handle ) const; + virtual int Eof( void *io_handle ) const; + virtual int Flush( void *io_handle ) const; + virtual int Close( void *io_handle ) const; + + const char *LastError() const; +}; + +typedef struct { + HANDLE hFile; + uint64 offset; +} FileInfo; + +/************************************************************************/ +/* GetDefaultIOInterfaces() */ +/************************************************************************/ + +const IOInterfaces *PCIDSK::GetDefaultIOInterfaces() +{ + static Win32IOInterface singleton_win32_interface; + + return &singleton_win32_interface; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +void * +Win32IOInterface::Open( std::string filename, std::string access ) const + +{ + DWORD dwDesiredAccess, dwCreationDisposition, dwFlagsAndAttributes; + HANDLE hFile; + + if( strchr(access.c_str(),'+') != NULL || strchr(access.c_str(),'w') != 0 ) + dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; + else + dwDesiredAccess = GENERIC_READ; + + if( strstr(access.c_str(), "w") != NULL ) + dwCreationDisposition = CREATE_ALWAYS; + else + dwCreationDisposition = OPEN_EXISTING; + + dwFlagsAndAttributes = (dwDesiredAccess == GENERIC_READ) ? + FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL, + + hFile = CreateFileA(filename.c_str(), dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + if( hFile == INVALID_HANDLE_VALUE ) + { + ThrowPCIDSKException( "Open(%s,%s) failed:\n%s", + filename.c_str(), access.c_str(), LastError() ); + } + + FileInfo *fi = new FileInfo(); + fi->hFile = hFile; + fi->offset = 0; + + return fi; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +uint64 +Win32IOInterface::Seek( void *io_handle, uint64 offset, int whence ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + uint32 dwMoveMethod, dwMoveHigh; + uint32 nMoveLow; + LARGE_INTEGER li; + + // seeks that do nothing are still surprisingly expensive with MSVCRT. + // try and short circuit if possible. + if( whence == SEEK_SET && offset == fi->offset ) + return 0; + + switch(whence) + { + case SEEK_CUR: + dwMoveMethod = FILE_CURRENT; + break; + case SEEK_END: + dwMoveMethod = FILE_END; + break; + case SEEK_SET: + default: + dwMoveMethod = FILE_BEGIN; + break; + } + + li.QuadPart = offset; + nMoveLow = li.LowPart; + dwMoveHigh = li.HighPart; + + SetLastError( 0 ); + SetFilePointer(fi->hFile, (LONG) nMoveLow, (PLONG)&dwMoveHigh, + dwMoveMethod); + + if( GetLastError() != NO_ERROR ) + { +#ifdef notdef + LPVOID lpMsgBuf = NULL; + + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, 0, NULL ); + + printf( "[ERROR %d]\n %s\n", GetLastError(), (char *) lpMsgBuf ); + printf( "nOffset=%u, nMoveLow=%u, dwMoveHigh=%u\n", + (GUInt32) nOffset, nMoveLow, dwMoveHigh ); +#endif + ThrowPCIDSKException( "Seek(%d,%d): %s (%d)", + (int) offset, whence, + LastError(), GetLastError() ); + return -1; + } + +/* -------------------------------------------------------------------- */ +/* Update our offset. */ +/* -------------------------------------------------------------------- */ + if( whence == SEEK_SET ) + fi->offset = offset; + else if( whence == SEEK_END ) + { + LARGE_INTEGER li; + + li.HighPart = 0; + li.LowPart = SetFilePointer( fi->hFile, 0, (PLONG) &(li.HighPart), + FILE_CURRENT ); + fi->offset = li.QuadPart; + } + else if( whence == SEEK_CUR ) + fi->offset += offset; + + return 0; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +uint64 Win32IOInterface::Tell( void *io_handle ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + + return fi->offset; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +uint64 Win32IOInterface::Read( void *buffer, uint64 size, uint64 nmemb, + void *io_handle ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + + errno = 0; + + DWORD dwSizeRead; + size_t result; + + if( !ReadFile(fi->hFile, buffer, (DWORD)(size*nmemb), &dwSizeRead, NULL) ) + { + result = 0; + } + else if( size == 0 ) + result = 0; + else + result = (size_t) (dwSizeRead / size); + + if( errno != 0 && result == 0 && nmemb != 0 ) + ThrowPCIDSKException( "Read(%d): %s", + (int) size * nmemb, + LastError() ); + + fi->offset += size*result; + + return result; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +uint64 Win32IOInterface::Write( const void *buffer, uint64 size, uint64 nmemb, + void *io_handle ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + + errno = 0; + + DWORD dwSizeRead; + size_t result; + + if( !WriteFile(fi->hFile, buffer, (DWORD)(size*nmemb), &dwSizeRead, NULL) ) + { + result = 0; + } + else if( size == 0 ) + result = 0; + else + result = (size_t) (dwSizeRead / size); + + if( errno != 0 && result == 0 && nmemb != 0 ) + ThrowPCIDSKException( "Write(%d): %s", + (int) size * nmemb, + LastError() ); + + fi->offset += size*result; + + return result; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int Win32IOInterface::Eof( void *io_handle ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + uint64 nCur, nEnd; + + nCur = Tell( io_handle ); + Seek( io_handle, 0, SEEK_END ); + nEnd = Tell( io_handle ); + Seek( io_handle, nCur, SEEK_SET ); + + return (nCur == nEnd); +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int Win32IOInterface::Flush( void *io_handle ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + + FlushFileBuffers( fi->hFile ); + + return 0; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int Win32IOInterface::Close( void *io_handle ) const + +{ + FileInfo *fi = (FileInfo *) io_handle; + + int result = CloseHandle( fi->hFile ) ? 0 : -1; + delete fi; + + return result; +} + +/************************************************************************/ +/* LastError() */ +/* */ +/* Return a string representation of the last error. */ +/************************************************************************/ + +const char *Win32IOInterface::LastError() const + +{ + return ""; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/pthread_mutex.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/pthread_mutex.cpp new file mode 100644 index 000000000..d034cba8d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/pthread_mutex.cpp @@ -0,0 +1,127 @@ +/****************************************************************************** + * + * Purpose: Implementation of pthreads based mutex. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_mutex.h" +#include + +#include + + +/************************************************************************/ +/* PThreadMutex */ +/************************************************************************/ + +class PThreadMutex : public PCIDSK::Mutex + +{ +private: + pthread_mutex_t *hMutex; + +public: + PThreadMutex(); + ~PThreadMutex(); + + int Acquire(void); + int Release(void); +}; + +/************************************************************************/ +/* PThreadMutex() */ +/************************************************************************/ + +PThreadMutex::PThreadMutex() + +{ + hMutex = (pthread_mutex_t *) malloc(sizeof(pthread_mutex_t)); + +#if defined(PTHREAD_MUTEX_RECURSIVE) + { + pthread_mutexattr_t attr; + pthread_mutexattr_init( &attr ); + pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); + pthread_mutex_init( hMutex, &attr ); + } +#elif defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) + pthread_mutex_t tmp_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + *hMutex = tmp_mutex; +#else +#error "Recursive mutexes apparently unsupported, configure --without-threads" +#endif +} + +/************************************************************************/ +/* ~PThreadMutex() */ +/************************************************************************/ + +PThreadMutex::~PThreadMutex() + +{ + pthread_mutex_destroy( hMutex ); + free( hMutex ); +} + +/************************************************************************/ +/* Release() */ +/************************************************************************/ + +int PThreadMutex::Release() + +{ + pthread_mutex_unlock( hMutex ); + return 1; +} + +/************************************************************************/ +/* Acquire() */ +/************************************************************************/ + +int PThreadMutex::Acquire() + +{ + return pthread_mutex_lock( hMutex ) == 0; +} + +/************************************************************************/ +/* DefaultCreateMutex() */ +/************************************************************************/ + +/** + * Create a mutex. + * + * This function creates the default style of mutex for the currently + * PCIDSK library build. The mutex should be destroyed with delete when + * no longer required. + * + * @return a new mutex object pointer. + */ + + +PCIDSK::Mutex *PCIDSK::DefaultCreateMutex(void) + +{ + return new PThreadMutex(); +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/win32_mutex.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/win32_mutex.cpp new file mode 100644 index 000000000..52c0a4a2a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/port/win32_mutex.cpp @@ -0,0 +1,110 @@ +/****************************************************************************** + * + * Purpose: Implementation of pthreads based mutex. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_mutex.h" +#include "pcidsk_exception.h" + +#include + + +/************************************************************************/ +/* Win32Mutex */ +/************************************************************************/ + +class Win32Mutex : public PCIDSK::Mutex + +{ +private: + HANDLE hMutex; + +public: + Win32Mutex(); + ~Win32Mutex(); + + int Acquire(void); + int Release(void); +}; + +/************************************************************************/ +/* Win32Mutex() */ +/************************************************************************/ + +Win32Mutex::Win32Mutex() + +{ + hMutex = CreateMutex( NULL, 1, NULL ); + Release(); // it is created acquired, but we want it free. +} + +/************************************************************************/ +/* ~PThreadMutex() */ +/************************************************************************/ + +Win32Mutex::~Win32Mutex() + +{ + if( hMutex ) + CloseHandle( hMutex ); +} + +/************************************************************************/ +/* Release() */ +/************************************************************************/ + +int Win32Mutex::Release() + +{ + ReleaseMutex( hMutex ); + return 1; +} + +/************************************************************************/ +/* Acquire() */ +/************************************************************************/ + +int Win32Mutex::Acquire() + +{ + DWORD hr; + + hr = WaitForSingleObject( hMutex, (int) (3600 * 1000) ); + + if( hr != 0 ) + PCIDSK::ThrowPCIDSKException( "Failed to acquire mutex in 3600s." ); + + return 1; +} + +/************************************************************************/ +/* DefaultCreateMutex() */ +/************************************************************************/ + +PCIDSK::Mutex *PCIDSK::DefaultCreateMutex(void) + +{ + return new Win32Mutex(); +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/Makefile b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/Makefile new file mode 100644 index 000000000..a954348d5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/Makefile @@ -0,0 +1,8 @@ +default: + (cd .. ; $(MAKE)) + +clean: + (cd .. ; $(MAKE) clean) + +check: + (cd .. ; $(MAKE) check) diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsk_array.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsk_array.cpp new file mode 100644 index 000000000..73a989dc1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsk_array.cpp @@ -0,0 +1,334 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSK_TEX class. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_exception.h" +#include "segment/cpcidsk_array.h" +#include "core/cpcidskfile.h" +#include +#include +#include +#include "core/pcidsk_utils.h" + +using namespace PCIDSK; + +/************************************************************************/ +/* CPCIDSK_ARRAY() */ +/************************************************************************/ + +CPCIDSK_ARRAY::CPCIDSK_ARRAY( PCIDSKFile *file, int segment, + const char *segment_pointer ) + : CPCIDSKSegment( file, segment, segment_pointer ), + loaded_(false),mbModified(false) +{ + MAX_DIMENSIONS = 8; + Load(); +} + +/************************************************************************/ +/* ~CPCIDSK_ARRAY */ +/************************************************************************/ + +CPCIDSK_ARRAY::~CPCIDSK_ARRAY() + +{ +} + +/** + * Load the contents of the segment + */ +void CPCIDSK_ARRAY::Load() +{ + // Check if we've already loaded the segment into memory + if (loaded_) { + return; + } + + PCIDSKBuffer& seg_header = this->GetHeader(); + seg_data.SetSize(GetContentSize()); + ReadFromFile(seg_data.buffer, 0, seg_data.buffer_size); + + if(std::strncmp(seg_header.buffer+160, "64R ", 8)) + { + seg_header.Put("64R ",160,8); + loaded_ = true; + return ; + } + + int nDimension = seg_header.GetInt(160+8,8); + if(nDimension < 1 || nDimension > MAX_DIMENSIONS) + { + std::stringstream oStream; + oStream << "Invalid array dimension " << nDimension; + oStream << " stored in the segment."; + std::string oMsg = oStream.str(); + throw PCIDSKException(oMsg.c_str()); + } + mnDimension = static_cast(nDimension); + + moSizes.clear(); + for( int i = 0; i < mnDimension; i++ ) + { + int nSize = seg_header.GetInt(160+24 + i*8,8); + if(nSize < 1) + { + std::stringstream oStream; + oStream << "Invalid size " << nSize << " for dimension " << i+1; + std::string oMsg = oStream.str(); + throw PCIDSKException(oMsg.c_str()); + } + moSizes.push_back( nSize ); + } + + //calculate the total number of elements in the array. + unsigned int nElements = 1; + for(unsigned int i=0 ; i < moSizes.size() ; i++) + { + nElements *= moSizes[i]; + } + + for( unsigned int i = 0; i < nElements; i++ ) + { + const double* pdValue = (const double*)seg_data.Get(i*8,8); + char uValue[8]; + std::memcpy(uValue,pdValue,8); + SwapData(uValue,8,1); + double dValue; + memcpy(&dValue, uValue, 8); + moArray.push_back(dValue); + } + + //PCIDSK doesn't have support for headers. + + // We've now loaded the structure up with data. Mark it as being loaded + // properly. + loaded_ = true; + +} + +/** + * Write the segment on disk + */ +void CPCIDSK_ARRAY::Write(void) +{ + //We are not writing if nothing was loaded. + if (!loaded_) { + return; + } + + PCIDSKBuffer& seg_header = this->GetHeader(); + int nBlocks = (moArray.size()*8 + 511)/512 ; + unsigned int nSizeBuffer = (nBlocks)*512 ; + //64 values can be put into 512 bytes. + unsigned int nRest = nBlocks*64 - moArray.size(); + + seg_data.SetSize(nSizeBuffer); + + seg_header.Put("64R ",160,8); + seg_header.Put((int)mnDimension,160+8,8); + + for( int i = 0; i < mnDimension; i++ ) + { + int nSize = static_cast(moSizes[i]); + seg_header.Put(nSize,160+24 + i*8,8); + } + + for( unsigned int i = 0; i < moArray.size(); i++ ) + { + double dValue = moArray[i]; + SwapData(&dValue,8,1); + seg_data.PutBin(dValue,i*8); + } + + //set the end of the buffer to 0. + for( unsigned int i=0 ; i < nRest ; i++) + { + seg_data.Put(0.0,(moArray.size()+i)*8,8,"%22.14f"); + } + + WriteToFile(seg_data.buffer,0,seg_data.buffer_size); + + mbModified = false; +} + +/** + * Synchronize the segement, if it was modified then + * write it into disk. + */ +void CPCIDSK_ARRAY::Synchronize() +{ + if(mbModified) + { + this->Write(); + //write the modified header + file->WriteToFile( header.buffer, data_offset, 1024 ); + } +} + +/** + * This function returns the number of dimension in the array. + * an array segment can have minimum 1 dimension and maximum + * 8 dimension. + * + * @return the dimension of the array in [1,8] + */ +unsigned char CPCIDSK_ARRAY::GetDimensionCount() const +{ + return mnDimension; +} + +/** + * This function set the dimension of the array. the dimension + * must be in [1,8] or a pci::Exception is thrown. + * + * @param nDim number of dimension, should be in [1,8] + */ +void CPCIDSK_ARRAY::SetDimensionCount(unsigned char nDim) +{ + if(nDim < 1 || nDim > 8) + { + throw PCIDSKException("An array cannot have a " + "dimension bigger than 8 or smaller than 1."); + } + mnDimension = nDim; + mbModified = true; +} + +/** + * Get the number of element that can be put in each of the dimension + * of the array. the size of the return vector is GetDimensionCount(). + * + * @return the size of each dimension. + */ +const std::vector& CPCIDSK_ARRAY::GetSizes() const +{ + return moSizes; +} + +/** + * Set the size of each dimension. If the size of the array is bigger + * or smaller than GetDimensionCount(), then a pci::Exception is thrown + * if one of the sizes is 0, then a pci::Exception is thrown. + * + * @param oSizes the size of each dimension + */ +void CPCIDSK_ARRAY::SetSizes(const std::vector& oSizes) +{ + if(oSizes.size() != GetDimensionCount()) + { + throw PCIDSKException("You need to specify the sizes" + " for each dimension of the array"); + } + + for( unsigned int i=0 ; i < oSizes.size() ; i++) + { + if(oSizes[i] == 0) + { + throw PCIDSKException("You cannot define the size of a dimension to 0."); + } + } + moSizes = oSizes; + mbModified = true; +} + +/** + * Get the array in a vector. the size of this vector is + * GetSize()[0]*GetSize()[2]*...*GetSize()[GetDimensionCount()-1]. + * value are stored in the following order inside this vector: + * ViDj = Value i of Dimension j + * n = size of dimension 1 + * p = size of dimension 2 + * h = size of dimension k + * + * V1D1 ... VnD1 V1D2 ... VpD2 ... V1Dk ... VhDk + * + * @return the array. + */ +const std::vector& CPCIDSK_ARRAY::GetArray() const +{ + return moArray; +} + +/** + * Set the array in the segment. the size of this vector is + * GetSize()[0]*GetSize()[2]*...*GetSize()[GetDimensionCount()-1]. + * value are stored in the following order inside this vector: + * ViDj = Value i of Dimension j + * n = size of dimension 1 + * p = size of dimension 2 + * h = size of dimension k + * + * V1D1 ... VnD1 V1D2 ... VpD2 ... V1Dk ... VhDk + * + * If the size of oArray doesn't match the sizes and dimensions + * then a pci::Exception is thrown. + * + * @param oArray the array. + */ +void CPCIDSK_ARRAY::SetArray(const std::vector& oArray) +{ + unsigned int nLength = 1; + for( unsigned int i=0 ; i < moSizes.size() ; i++) + { + nLength *= moSizes[i]; + } + + if(nLength != oArray.size()) + { + throw PCIDSKException("the size of this array doesn't match " + "the size specified in GetSizes(). See documentation for" + " more information."); + } + moArray = oArray; + mbModified = true; +} + +/** + * Get the headers of this array. If no headers has be specified, then + * this function return an empty vector. + * the size of this vector should be equal to the size of the first dimension + * returned by GetSize()[0] + * + * @return the headers. + */ +const std::vector& CPCIDSK_ARRAY::GetHeaders() const +{ + return moHeaders; +} + +/** + * Set the headers of this array. An empty vector can be specified to clear + * the headers in the segment. + * the size of this vector should be equal to the size of the first dimension + * returned by GetSize()[0]. If it is not the case, a pci::Exception is thrown. + * + * @param oHeaders the headers. + */ +void CPCIDSK_ARRAY::SetHeaders(const std::vector& oHeaders) +{ + moHeaders = oHeaders; + mbModified = true; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsk_array.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsk_array.h new file mode 100644 index 000000000..06180662f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsk_array.h @@ -0,0 +1,90 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CPCIDSK_ARRAY class. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_SEGMENT_PCIDSK_ARRAY_H +#define __INCLUDE_SEGMENT_PCIDSK_ARRAY_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_array.h" +#include "pcidsk_buffer.h" +#include "segment/cpcidsksegment.h" + +#include + +namespace PCIDSK +{ + class PCIDSKFile; + + /************************************************************************/ + /* CPCIDSK_ARRAY */ + /************************************************************************/ + + class CPCIDSK_ARRAY : public CPCIDSKSegment, + public PCIDSK_ARRAY + { + public: + CPCIDSK_ARRAY( PCIDSKFile *file, int segment,const char *segment_pointer); + + virtual ~CPCIDSK_ARRAY(); + + // CPCIDSK_ARRAY + unsigned char GetDimensionCount() const ; + void SetDimensionCount(unsigned char nDim) ; + const std::vector& GetSizes() const ; + void SetSizes(const std::vector& oSizes) ; + const std::vector& GetArray() const ; + void SetArray(const std::vector& oArray) ; + + //synchronize the segment on disk. + void Synchronize(); + private: + + //Headers are not supported by PCIDSK, we keep the function + //private here in case we want to add the feature + //in the future. + const std::vector& GetHeaders() const ; + void SetHeaders(const std::vector& oHeaders) ; + + // Helper housekeeping functions + void Load(); + void Write(); + + //members + PCIDSKBuffer seg_data; + bool loaded_; + bool mbModified; + unsigned char MAX_DIMENSIONS; + + //Array information + std::vector moHeaders; + unsigned char mnDimension; + std::vector moSizes; + std::vector moArray; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_PCIDSK_ARRAY_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsk_tex.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsk_tex.cpp new file mode 100644 index 000000000..5a107b873 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsk_tex.cpp @@ -0,0 +1,133 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSK_TEX class. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_exception.h" +#include "segment/cpcidsk_tex.h" +#include +#include + +using namespace PCIDSK; + +/************************************************************************/ +/* CPCIDSK_TEX() */ +/************************************************************************/ + +CPCIDSK_TEX::CPCIDSK_TEX( PCIDSKFile *file, int segment, + const char *segment_pointer ) + : CPCIDSKSegment( file, segment, segment_pointer ) + +{ +} + +/************************************************************************/ +/* ~CPCIDSK_TEX() */ +/************************************************************************/ + +CPCIDSK_TEX::~CPCIDSK_TEX() + +{ +} + +/************************************************************************/ +/* ReadText() */ +/************************************************************************/ + +std::string CPCIDSK_TEX::ReadText() + +{ + PCIDSKBuffer seg_data; + + seg_data.SetSize( (int) GetContentSize() ); + + ReadFromFile( seg_data.buffer, 0, seg_data.buffer_size ); + + int i; + char *tbuffer = (char *) seg_data.buffer; + + for( i = 0; i < seg_data.buffer_size; i++ ) + { + if( tbuffer[i] == '\r' ) + tbuffer[i] = '\n'; + + if( tbuffer[i] == '\0' ) + break; + } + + return std::string( (const char *) seg_data.buffer, i ); +} + +/************************************************************************/ +/* WriteText() */ +/************************************************************************/ + +void CPCIDSK_TEX::WriteText( const std::string &text_in ) + +{ + // Transform all \n's to \r's (chr(10) to char(13)). + unsigned int i, i_out = 0; + std::string text = text_in; + + for( i = 0; i < text.size(); i++ ) + { + if( text[i] == '\0' ) + { + text.resize( i ); + break; + } + + if( text[i] == '\n' && text[i+1] == '\r' ) + { + text[i_out++] = '\r'; + i++; + } + else if( text[i] == '\r' && text[i+1] == '\n' ) + { + text[i_out++] = '\r'; + i++; + } + else if( text[i] == '\n' ) + text[i_out++] = '\r'; + else + text[i_out++] = text[i]; + } + + text.resize( i_out ); + + // make sure we have a newline at the end. + + if( i_out > 0 && text[i_out-1] != '\r' ) + text += "\r"; + + // We really *ought* to ensure the rest of the segment + // is zeroed out to properly adhere to the specification. + // It might also be prudent to ensure the segment grows + // in 32K increments to avoid "move to end of file churn" + // if several text segments are growing a bit at a time + // though this is uncommon. + + WriteToFile( text.c_str(), 0, text.size() + 1 ); +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsk_tex.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsk_tex.h new file mode 100644 index 000000000..7e67a45ae --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsk_tex.h @@ -0,0 +1,61 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CPCIDSK_TEX class. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_SEGMENT_PCIDSK_TEX_H +#define __INCLUDE_SEGMENT_PCIDSK_TEX_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_tex.h" +#include "pcidsk_buffer.h" +#include "segment/cpcidsksegment.h" + +#include + +namespace PCIDSK +{ + class PCIDSKFile; + + /************************************************************************/ + /* CPCIDSK_TEX */ + /************************************************************************/ + + class CPCIDSK_TEX : virtual public CPCIDSKSegment, + public PCIDSK_TEX + { + public: + CPCIDSK_TEX( PCIDSKFile *file, int segment,const char *segment_pointer); + + virtual ~CPCIDSK_TEX(); + + // PCIDSK_TEX + + std::string ReadText(); + void WriteText( const std::string &text ); + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_PCIDSK_TEX_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskads40model.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskads40model.cpp new file mode 100644 index 000000000..5c9218923 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskads40model.cpp @@ -0,0 +1,147 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSKADS40ModelSegment class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_ads40.h" +#include "segment/cpcidsksegment.h" +#include "core/pcidsk_utils.h" +#include "pcidsk_exception.h" +#include "segment/cpcidskads40model.h" + +#include +#include +#include +#include + +using namespace PCIDSK; + +// Struct to store details of the RPC model +struct CPCIDSKADS40ModelSegment::PCIDSKADS40Info +{ + std::string path; + + // The raw segment data + PCIDSKBuffer seg_data; +}; + +CPCIDSKADS40ModelSegment::CPCIDSKADS40ModelSegment(PCIDSKFile *file, + int segment, + const char *segment_pointer) : + CPCIDSKSegment(file, segment, segment_pointer), + pimpl_(new CPCIDSKADS40ModelSegment::PCIDSKADS40Info), + loaded_(false),mbModified(false) +{ + Load(); +} + + +CPCIDSKADS40ModelSegment::~CPCIDSKADS40ModelSegment() +{ + delete pimpl_; +} + +// Load the contents of the segment +void CPCIDSKADS40ModelSegment::Load() +{ + // Check if we've already loaded the segment into memory + if (loaded_) { + return; + } + + assert(data_size - 1024 == 1 * 512); + + pimpl_->seg_data.SetSize(data_size - 1024); // should be 1 * 512 + + ReadFromFile(pimpl_->seg_data.buffer, 0, data_size - 1024); + + // The ADS40 Model Segment is defined as follows: + // ADs40 Segment: 1 512-byte blocks + + // Block 1: + // Bytes 0-7: 'ADS40 ' + // Byte 8-512: the path + + if (std::strncmp(pimpl_->seg_data.buffer, "ADS40 ", 8)) + { + pimpl_->seg_data.Put("ADS40 ",0,8); + return; + // Something has gone terribly wrong! + /*throw PCIDSKException("A segment that was previously identified as an RFMODEL " + "segment does not contain the appropriate data. Found: [%s]", + std::string(pimpl_->seg_data.buffer, 8).c_str());*/ + } + + pimpl_->path = std::string(&pimpl_->seg_data.buffer[8]); + + // We've now loaded the structure up with data. Mark it as being loaded + // properly. + loaded_ = true; + +} + +void CPCIDSKADS40ModelSegment::Write(void) +{ + //We are not writing if nothing was loaded. + if (!loaded_) { + return; + } + + pimpl_->seg_data.Put("ADS40 ",0,8); + pimpl_->seg_data.Put(pimpl_->path.c_str(),8,pimpl_->path.size()); + + WriteToFile(pimpl_->seg_data.buffer,0,data_size-1024); + mbModified = false; +} + +// Get sensor name +std::string CPCIDSKADS40ModelSegment::GetPath(void) const +{ + return pimpl_->path; +} + +// Set sensor name +void CPCIDSKADS40ModelSegment::SetPath(const std::string& oPath) +{ + if(oPath.size() < 504) + { + pimpl_->path = oPath; + mbModified = true; + } + else + { + throw PCIDSKException("The size of the path cannot be" + " bigger than 504 characters."); + } +} + +void CPCIDSKADS40ModelSegment::Synchronize() +{ + if(mbModified) + { + this->Write(); + } +} + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskads40model.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskads40model.h new file mode 100644 index 000000000..5d2987ce6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskads40model.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * + * Purpose: Support for reading and manipulating PCIDSK ADS40 Segments + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_SEGMENT_PCIDSKADS40MODEL_H +#define __INCLUDE_PCIDSK_SEGMENT_PCIDSKADS40MODEL_H + +#include "pcidsk_ads40.h" +#include "segment/cpcidsksegment.h" + +namespace PCIDSK { + class PCIDSKFile; + + class CPCIDSKADS40ModelSegment : public PCIDSKADS40Segment, + public CPCIDSKSegment + { + public: + CPCIDSKADS40ModelSegment(PCIDSKFile *file, int segment,const char *segment_pointer); + ~CPCIDSKADS40ModelSegment(); + + // Get path + std::string GetPath(void) const; + // Set path + void SetPath(const std::string& oPath); + + //synchronize the segment on disk. + void Synchronize(); + private: + // Helper housekeeping functions + void Load(); + void Write(); + + struct PCIDSKADS40Info; + PCIDSKADS40Info *pimpl_; + bool loaded_; + bool mbModified; + }; +} + +#endif // __INCLUDE_PCIDSK_SEGMENT_PCIDSKADS40MODEL_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskapmodel.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskapmodel.cpp new file mode 100644 index 000000000..e31f6ea2b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskapmodel.cpp @@ -0,0 +1,526 @@ +/****************************************************************************** + * + * Purpose: Implementation of the APMODEL segment and storage objects. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "pcidsk_airphoto.h" +#include "pcidsk_exception.h" +#include "segment/cpcidskapmodel.h" + +#include +#include +#include +#include +#include +#include + +using namespace PCIDSK; + +/** + * Construct a new PCIDSK Airphoto Model Interior Orientation + * parameters store. + */ +PCIDSKAPModelIOParams::PCIDSKAPModelIOParams(std::vector const& imgtofocalx, + std::vector const& imgtofocaly, + std::vector const& focaltocolumn, + std::vector const& focaltorow, + double focal_len, + std::pair const& prin_pt, + std::vector const& radial_dist) : + imgtofocalx_(imgtofocalx), imgtofocaly_(imgtofocaly), focaltocolumn_(focaltocolumn), + focaltorow_(focaltorow), focal_len_(focal_len), prin_point_(prin_pt), + rad_dist_coeff_(radial_dist) +{ +} + +std::vector const& PCIDSKAPModelIOParams::GetImageToFocalPlaneXCoeffs(void) const +{ + return imgtofocalx_; +} + +std::vector const& PCIDSKAPModelIOParams::GetImageToFocalPlaneYCoeffs(void) const +{ + return imgtofocaly_; +} + +std::vector const& PCIDSKAPModelIOParams::GetFocalPlaneToColumnCoeffs(void) const +{ + return focaltocolumn_; +} + +std::vector const& PCIDSKAPModelIOParams::GetFocalPlaneToRowCoeffs(void) const +{ + return focaltorow_; +} + +double PCIDSKAPModelIOParams::GetFocalLength(void) const +{ + return focal_len_; +} + +std::pair const& PCIDSKAPModelIOParams::GetPrincipalPoint(void) const +{ + return prin_point_; +} + +std::vector const& PCIDSKAPModelIOParams::GetRadialDistortionCoeffs(void) const +{ + return rad_dist_coeff_; +} + +/** + * Construct a new PCIDSK Airphoto Model Exterior Orientation parameters + * storage object. + */ +PCIDSKAPModelEOParams::PCIDSKAPModelEOParams(std::string const& rotation_type, + std::vector const& earth_to_body, + std::vector const& perspect_cen, + unsigned int epsg_code) : + rot_type_(rotation_type), earth_to_body_(earth_to_body), + perspective_centre_pos_(perspect_cen), epsg_code_(epsg_code) +{ +} + +std::string PCIDSKAPModelEOParams::GetEarthToBodyRotationType(void) const +{ + return rot_type_; +} + +std::vector const& PCIDSKAPModelEOParams::GetEarthToBodyRotation(void) const +{ + return earth_to_body_; +} + +std::vector const& PCIDSKAPModelEOParams::GetPerspectiveCentrePosition(void) const +{ + return perspective_centre_pos_; +} + +unsigned int PCIDSKAPModelEOParams::GetEPSGCode(void) const +{ + return epsg_code_; +} + +/** + * Miscellaneous camera parameters for the AP Model + */ +PCIDSKAPModelMiscParams::PCIDSKAPModelMiscParams(std::vector const& decentering_coeffs, + std::vector const& x3dcoord, + std::vector const& y3dcoord, + std::vector const& z3dcoord, + double radius, + double rff, + double min_gcp_hgt, + double max_gcp_hgt, + bool is_prin_pt_off, + bool has_dist, + bool has_decent, + bool has_radius) : + decentering_coeffs_(decentering_coeffs), x3dcoord_(x3dcoord), y3dcoord_(y3dcoord), + z3dcoord_(z3dcoord), radius_(radius), rff_(rff), min_gcp_hgt_(min_gcp_hgt), + max_gcp_hgt_(max_gcp_hgt), is_prin_pt_off_(is_prin_pt_off), + has_dist_(has_dist), has_decent_(has_decent), has_radius_(has_radius) +{ +} + +std::vector const& PCIDSKAPModelMiscParams::GetDecenteringDistortionCoeffs(void) const +{ + return decentering_coeffs_; +} + +std::vector const& PCIDSKAPModelMiscParams::GetX3DCoord(void) const +{ + return x3dcoord_; +} + +std::vector const& PCIDSKAPModelMiscParams::GetY3DCoord(void) const +{ + return y3dcoord_; +} + +std::vector const& PCIDSKAPModelMiscParams::GetZ3DCoord(void) const +{ + return z3dcoord_; +} + +double PCIDSKAPModelMiscParams::GetRadius(void) const +{ + return radius_; +} + +double PCIDSKAPModelMiscParams::GetRFF(void) const +{ + return rff_; +} + +double PCIDSKAPModelMiscParams::GetGCPMinHeight(void) const +{ + return min_gcp_hgt_; +} + +double PCIDSKAPModelMiscParams::GetGCPMaxHeight(void) const +{ + return max_gcp_hgt_; +} + +bool PCIDSKAPModelMiscParams::IsPrincipalPointOffset(void) const +{ + return is_prin_pt_off_; +} + +bool PCIDSKAPModelMiscParams::HasDistortion(void) const +{ + return has_dist_; +} + +bool PCIDSKAPModelMiscParams::HasDecentering(void) const +{ + return has_decent_; +} + +bool PCIDSKAPModelMiscParams::HasRadius(void) const +{ + return has_radius_; +} + +/** + * Create a new PCIDSK APMODEL segment + */ +CPCIDSKAPModelSegment::CPCIDSKAPModelSegment(PCIDSKFile *file, int segment, const char *segment_pointer) : + CPCIDSKSegment(file, segment, segment_pointer) +{ + filled_ = false; + io_params_ = NULL; + eo_params_ = NULL; + misc_params_ = NULL; + UpdateFromDisk(); +} + +CPCIDSKAPModelSegment::~CPCIDSKAPModelSegment() +{ + delete io_params_; + delete eo_params_; + delete misc_params_; +} + +unsigned int CPCIDSKAPModelSegment::GetWidth(void) const +{ + if (!filled_) { + ThrowPCIDSKException("Failed to determine width from APModel."); + } + return width_; +} + +unsigned int CPCIDSKAPModelSegment::GetHeight(void) const +{ + if (!filled_) { + ThrowPCIDSKException("Failed to determine height from APModel."); + } + return height_; +} + +unsigned int CPCIDSKAPModelSegment::GetDownsampleFactor(void) const +{ + if (!filled_) { + ThrowPCIDSKException("Failed to determine APModel downsample factor."); + } + return downsample_; +} + +// Interior Orientation Parameters +PCIDSKAPModelIOParams const& CPCIDSKAPModelSegment::GetInteriorOrientationParams(void) const +{ + if (io_params_ == NULL) { + ThrowPCIDSKException("There was a failure in reading the APModel IO params."); + } + return *io_params_; +} + +// Exterior Orientation Parameters +PCIDSKAPModelEOParams const& CPCIDSKAPModelSegment::GetExteriorOrientationParams(void) const +{ + if (eo_params_ == NULL) { + ThrowPCIDSKException("There was a failure in reading the APModel EO params."); + } + return *eo_params_; +} + +PCIDSKAPModelMiscParams const& CPCIDSKAPModelSegment::GetAdditionalParams(void) const +{ + if (misc_params_ == NULL) { + ThrowPCIDSKException("There was a failure in reading the APModel camera params."); + } + return *misc_params_; +} + +std::string CPCIDSKAPModelSegment::GetMapUnitsString(void) const +{ + return map_units_; +} + +std::string CPCIDSKAPModelSegment::GetUTMUnitsString(void) const +{ + return map_units_; +} + +std::vector const& CPCIDSKAPModelSegment::GetProjParams(void) const +{ + return proj_parms_; +} + +/************************************************************************/ +/* BinaryToAPInfo() */ +/************************************************************************/ +/** + * Convert the contents of the PCIDSKBuffer buf to a set of APModel + * params + * + * @param buf A reference pointer to a PCIDSKBuffer + * @param eo_params A pointer to EO params to be populated + * @param io_params A pointer to IO params to be populated + * @param misc_params A pointer to camera params to be populated + * @param pixels The number of pixels in the image + * @param lines The number of lines in the image + * @param downsample The downsampling factor applied + * @param map_units the map units/geosys string + * @param utm_units the UTM units string + */ +namespace { + void BinaryToAPInfo(PCIDSKBuffer& buf, + PCIDSKAPModelEOParams*& eo_params, + PCIDSKAPModelIOParams*& io_params, + PCIDSKAPModelMiscParams*& misc_params, + unsigned int& pixels, + unsigned int& lines, + unsigned int& downsample, + std::string& map_units, + std::vector& proj_parms, + std::string& utm_units) + + { + proj_parms.clear(); + map_units.clear(); + utm_units.clear(); + /* -------------------------------------------------------------------- */ + /* Read the header block */ + /* -------------------------------------------------------------------- */ + + if(strncmp(buf.buffer,"APMODEL ",8)) + { + std::string magic(buf.buffer, 8); + ThrowPCIDSKException("Bad segment magic found. Found: [%s] expecting [APMODEL ]", + magic.c_str()); + } + + /* -------------------------------------------------------------------- */ + /* Allocate the APModel. */ + /* -------------------------------------------------------------------- */ + + downsample = buf.GetInt(24, 3); + if (0 >= downsample) downsample = 0; + + /* -------------------------------------------------------------------- */ + /* Read the values */ + /* -------------------------------------------------------------------- */ + pixels = buf.GetInt(0 * 22 + 512, 22); + lines = buf.GetInt(1 * 22 + 512, 22); + double focal_length = buf.GetDouble(2 * 22 + 512, 22); + std::vector perspective_centre(3); + perspective_centre[0] = buf.GetDouble(3 * 22 + 512, 22); + perspective_centre[1] = buf.GetDouble(4 * 22 + 512, 22); + perspective_centre[2] = buf.GetDouble(5 * 22 + 512, 22); + + std::vector earth_to_body(3); + earth_to_body[0] = buf.GetDouble(6 * 22 + 512, 22); + earth_to_body[1] = buf.GetDouble(7 * 22 + 512, 22); + earth_to_body[2] = buf.GetDouble(8 * 22 + 512, 22); + + // NOTE: PCIDSK itself doesn't support storing information + // about the rotation type, nor the EPSG code for the + // transformation. However, in the (not so distant) + // future, we will likely want to add this support to + // the APMODEL segment (or perhaps a future means of + // storing airphoto information). + eo_params = new PCIDSKAPModelEOParams("", + earth_to_body, + perspective_centre, + -1); + + std::vector x3d(3); + std::vector y3d(3); + std::vector z3d(3); + + x3d[0] = buf.GetDouble(9 * 22 + 512, 22); + x3d[1] = buf.GetDouble(10 * 22 + 512, 22); + x3d[2] = buf.GetDouble(11 * 22 + 512, 22); + y3d[0] = buf.GetDouble(12 * 22 + 512, 22); + y3d[1] = buf.GetDouble(13 * 22 + 512, 22); + y3d[2] = buf.GetDouble(14 * 22 + 512, 22); + z3d[0] = buf.GetDouble(15 * 22 + 512, 22); + z3d[1] = buf.GetDouble(16 * 22 + 512, 22); + z3d[2] = buf.GetDouble(17 * 22 + 512, 22); + + std::vector img_to_focal_plane_x(4); + std::vector img_to_focal_plane_y(4); + img_to_focal_plane_x[0] = buf.GetDouble(18 * 22 + 512, 22); + img_to_focal_plane_x[1] = buf.GetDouble(19 * 22 + 512, 22); + img_to_focal_plane_x[2] = buf.GetDouble(20 * 22 + 512, 22); + img_to_focal_plane_x[3] = buf.GetDouble(21 * 22 + 512, 22); + + img_to_focal_plane_y[0] = buf.GetDouble(0 * 22 + 512 * 2, 22); + img_to_focal_plane_y[1] = buf.GetDouble(1 * 22 + 512 * 2, 22); + img_to_focal_plane_y[2] = buf.GetDouble(2 * 22 + 512 * 2, 22); + img_to_focal_plane_y[3] = buf.GetDouble(3 * 22 + 512 * 2, 22); + + std::vector focal_to_cols(4); + std::vector focal_to_lines(4); + focal_to_cols[0] = buf.GetDouble(4 * 22 + 512 * 2, 22); + focal_to_cols[1] = buf.GetDouble(5 * 22 + 512 * 2, 22); + focal_to_cols[2] = buf.GetDouble(6 * 22 + 512 * 2, 22); + focal_to_cols[3] = buf.GetDouble(7 * 22 + 512 * 2, 22); + + focal_to_lines[0] = buf.GetDouble(8 * 22 + 512 * 2, 22); + focal_to_lines[1] = buf.GetDouble(9 * 22 + 512 * 2, 22); + focal_to_lines[2] = buf.GetDouble(10 * 22 + 512 * 2, 22); + focal_to_lines[3] = buf.GetDouble(11 * 22 + 512 * 2, 22); + + std::pair principal_point; + + principal_point.first = buf.GetDouble(12 * 22 + 512 * 2, 22); + principal_point.second = buf.GetDouble(13 * 22 + 512 * 2, 22); + + std::vector radial_distortion(8); + radial_distortion[0] = buf.GetDouble(14 * 22 + 512 * 2, 22); + radial_distortion[1] = buf.GetDouble(15 * 22 + 512 * 2, 22); + radial_distortion[2] = buf.GetDouble(16 * 22 + 512 * 2, 22); + radial_distortion[3] = buf.GetDouble(17 * 22 + 512 * 2, 22); + radial_distortion[4] = buf.GetDouble(18 * 22 + 512 * 2, 22); + radial_distortion[5] = buf.GetDouble(19 * 22 + 512 * 2, 22); + radial_distortion[6] = buf.GetDouble(20 * 22 + 512 * 2, 22); + radial_distortion[7] = buf.GetDouble(21 * 22 + 512 * 2, 22); + + // We have enough information now to construct the interior + // orientation parameters + io_params = new PCIDSKAPModelIOParams(img_to_focal_plane_x, + img_to_focal_plane_y, + focal_to_cols, + focal_to_lines, + focal_length, + principal_point, + radial_distortion); + + std::vector decentering(4); + decentering[0] = buf.GetDouble(0 * 22 + 512 * 3, 22); + decentering[1] = buf.GetDouble(1 * 22 + 512 * 3, 22); + decentering[2] = buf.GetDouble(2 * 22 + 512 * 3, 22); + decentering[3] = buf.GetDouble(3 * 22 + 512 * 3, 22); + + double radius = buf.GetDouble(4 * 22 + 512 * 3, 22); + double rff = buf.GetDouble(5 * 22 + 512 * 3, 22); + double gcp_min_height = buf.GetDouble(6 * 22 + 512 * 3, 22); + double gcp_max_height = buf.GetDouble(7 * 22 + 512 * 3, 22); + bool prin_off = buf.GetInt(8 * 22 + 512 * 3, 22) != 0; + bool distort_true = buf.GetInt(9 * 22 + 512 * 3, 22) != 0; + bool has_decentering = buf.GetInt(10 * 22 + 512 * 3, 22) != 0; + bool has_radius = buf.GetInt(11 * 22 + 512 * 3, 22) != 0; + + // Fill in the camera parameters + misc_params = new PCIDSKAPModelMiscParams(decentering, + x3d, + y3d, + z3d, + radius, + rff, + gcp_min_height, + gcp_max_height, + prin_off, + distort_true, + has_decentering, + has_radius); + + + /* -------------------------------------------------------------------- */ + /* Read the projection required */ + /* -------------------------------------------------------------------- */ + buf.Get(512 * 4, 16, map_units); + + if (!std::strncmp(buf.Get(512 * 4 + 16, 3), "UTM", 3)) + { + buf.Get(512 * 4, 3, utm_units); + } + + // Parse the Proj Params + proj_parms.clear(); + + if (*buf.Get(512 * 4 + 256, 1) == '\0') + { + for (std::size_t i = 0; i < 18; i++) + { + proj_parms.push_back(0.0); + } + } + else + { + std::stringstream proj_stream(std::string(buf.Get(512 * 4 + 256, 256))); + + for (std::size_t i = 0; i < 18; i++) + { + double parm; + proj_stream >> parm; + proj_parms.push_back(parm); + } + } + } +} // end anonymous namespace + +void CPCIDSKAPModelSegment::UpdateFromDisk(void) +{ + if (filled_) { + return; + } + + // Start reading in the APModel segment. APModel segments should be + // 7 blocks long. + if (data_size < (1024 + 7 * 512)) { + ThrowPCIDSKException("APMODEL segment is smaller than expected. A " + "segment of size %d was found", data_size); + } + buf.SetSize( (int) (data_size - 1024) ); + ReadFromFile(buf.buffer, 0, data_size - 1024); + + // Expand it using an analogue to a method pulled from GDB + BinaryToAPInfo(buf, + eo_params_, + io_params_, + misc_params_, + width_, + height_, + downsample_, + map_units_, + proj_parms_, + utm_units_); + + // Done, mark ourselves as having been properly filled + filled_ = true; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskapmodel.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskapmodel.h new file mode 100644 index 000000000..d57e85572 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskapmodel.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * + * Purpose: Declaration of the APMODEL segment. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_SEGMENT_CPCIDSKAPMODEL_H +#define __INCLUDE_SEGMENT_CPCIDSKAPMODEL_H + +#include "pcidsk_airphoto.h" +#include "segment/cpcidsksegment.h" + +#include +#include + +namespace PCIDSK { + + class CPCIDSKAPModelSegment : virtual public CPCIDSKSegment, + public PCIDSKAPModelSegment + { + public: + CPCIDSKAPModelSegment(PCIDSKFile *file, int segment, + const char *segment_pointer); + + ~CPCIDSKAPModelSegment(); + + unsigned int GetWidth(void) const; + unsigned int GetHeight(void) const; + unsigned int GetDownsampleFactor(void) const; + + // Interior Orientation Parameters + PCIDSKAPModelIOParams const& GetInteriorOrientationParams(void) const; + + // Exterior Orientation Parameters + PCIDSKAPModelEOParams const& GetExteriorOrientationParams(void) const; + + // ProjInfo + PCIDSKAPModelMiscParams const& GetAdditionalParams(void) const; + + std::string GetMapUnitsString(void) const; + std::string GetUTMUnitsString(void) const; + std::vector const& GetProjParams(void) const; + + private: + void UpdateFromDisk(); + + PCIDSKBuffer buf; + std::string map_units_, utm_units_; + std::vector proj_parms_; + PCIDSKAPModelIOParams* io_params_; + PCIDSKAPModelEOParams* eo_params_; + PCIDSKAPModelMiscParams* misc_params_; + unsigned int width_, height_, downsample_; + bool filled_; + }; + +} // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_CPCIDSKAPMODEL_H + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskbinarysegment.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskbinarysegment.cpp new file mode 100644 index 000000000..567eaedce --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskbinarysegment.cpp @@ -0,0 +1,135 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSKBinarySegment class. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "segment/cpcidskbinarysegment.h" +#include "segment/cpcidsksegment.h" +#include "core/pcidsk_utils.h" +#include "pcidsk_exception.h" +#include "core/pcidsk_utils.h" + +#include +#include +#include +#include + +using namespace PCIDSK; + +/** + * Binary Segment constructor + * @param[in,out] file the PCIDSK file + * @param[in] segment the segment index + * @param[in] segment_pointer the segement pointer + * @param[in] bLoad true to load the segment, else false (default true) + */ +CPCIDSKBinarySegment::CPCIDSKBinarySegment(PCIDSKFile *file, + int segment, + const char *segment_pointer, + bool bLoad) : + CPCIDSKSegment(file, segment, segment_pointer), + loaded_(false),mbModified(false) +{ + if (true == bLoad) + { + Load(); + } + return; +}// Initializer constructor + + +CPCIDSKBinarySegment::~CPCIDSKBinarySegment() +{ +} + +/** + * Load the contents of the segment + */ +void CPCIDSKBinarySegment::Load() +{ + // Check if we've already loaded the segment into memory + if (loaded_) { + return; + } + + seg_data.SetSize((int)data_size - 1024); + + ReadFromFile(seg_data.buffer, 0, data_size - 1024); + + // Mark it as being loaded properly. + loaded_ = true; + +} + +/** + * Write the segment on disk + */ +void CPCIDSKBinarySegment::Write(void) +{ + //We are not writing if nothing was loaded. + if (!loaded_) { + return; + } + + WriteToFile(seg_data.buffer, 0, seg_data.buffer_size); + + mbModified = false; +} + +/** + * Synchronize the segement, if it was modified then + * write it into disk. + */ +void CPCIDSKBinarySegment::Synchronize() +{ + if(mbModified) + { + this->Write(); + } +} + +void +CPCIDSKBinarySegment::SetBuffer(const char* pabyBuf, + unsigned int nBufSize) +{ + // Round the buffer size up to the next multiple of 512. + int nNumBlocks = nBufSize / 512 + ((0 == nBufSize % 512) ? 0 : 1); + unsigned int nAllocBufSize = 512 * nNumBlocks; + + seg_data.SetSize((int)nAllocBufSize); + data_size = nAllocBufSize + 1024; // Incl. header + + memcpy(seg_data.buffer, pabyBuf, nBufSize); + + // Fill unused data at end with zeroes. + if (nBufSize < nAllocBufSize) + { + memset(seg_data.buffer + nBufSize, 0, + nAllocBufSize - nBufSize); + } + mbModified = true; + + return; +}// SetBuffer diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskbinarysegment.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskbinarysegment.h new file mode 100644 index 000000000..f3d9ce5c7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskbinarysegment.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * + * Purpose: Support for reading and manipulating general PCIDSK Binary Segments + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_SEGMENT_PCIDSKBINARY_SEG_H +#define __INCLUDE_PCIDSK_SEGMENT_PCIDSKBINARY_SEG_H + +#include "pcidsk_binary.h" +#include "segment/cpcidsksegment.h" + +namespace PCIDSK { + class PCIDSKFile; + + class CPCIDSKBinarySegment : public PCIDSKBinarySegment, + public CPCIDSKSegment + { + public: + CPCIDSKBinarySegment(PCIDSKFile *file, int segment, + const char *segment_pointer, bool bLoad=true); + ~CPCIDSKBinarySegment(); + + const char* GetBuffer(void) const + { + return seg_data.buffer; + } + + unsigned int GetBufferSize(void) const + { + return seg_data.buffer_size; + } + void SetBuffer(const char* pabyBuf, + unsigned int nBufSize); + + //synchronize the segment on disk. + void Synchronize(); + private: + + // Helper housekeeping functions + void Load(); + void Write(); + + //functions to read/write binary information + protected: + // The raw segment data + PCIDSKBuffer seg_data; + bool loaded_; + bool mbModified; + }; +} + +#endif // __INCLUDE_PCIDSK_SEGMENT_PCIDSKBINARY_SEG_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskbitmap.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskbitmap.cpp new file mode 100644 index 000000000..e30ebc313 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskbitmap.cpp @@ -0,0 +1,530 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSKBitmap class. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_exception.h" +#include "segment/cpcidskbitmap.h" +#include "pcidsk_file.h" +#include "core/pcidsk_utils.h" +#include +#include +#include +#include +#include + +#include "cpl_port.h" + +using namespace PCIDSK; + +/************************************************************************/ +/* CPCIDSKBitmap() */ +/************************************************************************/ + +CPCIDSKBitmap::CPCIDSKBitmap( PCIDSKFile *file, int segment, + const char *segment_pointer ) + : CPCIDSKSegment( file, segment, segment_pointer ) + +{ + loaded = false; +} + +/************************************************************************/ +/* ~CPCIDSKBitmap() */ +/************************************************************************/ + +CPCIDSKBitmap::~CPCIDSKBitmap() + +{ +} + +/************************************************************************/ +/* Initialize() */ +/* */ +/* Set up a newly created bitmap segment. We just need to */ +/* write some stuff into the segment header. */ +/************************************************************************/ + +void CPCIDSKBitmap::Initialize() + +{ + loaded = false; + + CPCIDSKBitmap *pThis = (CPCIDSKBitmap *) this; + + PCIDSKBuffer &bheader = pThis->GetHeader(); + + bheader.Put( 0, 160 , 16 ); + bheader.Put( 0, 160+16*1, 16 ); + bheader.Put( file->GetWidth(), 160+16*2, 16 ); + bheader.Put( file->GetHeight(), 160+16*3, 16 ); + bheader.Put( -1, 160+16*4, 16 ); + + file->WriteToFile( bheader.buffer, data_offset, 1024 ); +} + +/************************************************************************/ +/* Load() */ +/************************************************************************/ + +void CPCIDSKBitmap::Load() const + +{ + if( loaded ) + return; + + // We don't really mean the internals are const, just a lie to + // keep the const interfaces happy. + + CPCIDSKBitmap *pThis = (CPCIDSKBitmap *) this; + + PCIDSKBuffer &bheader = pThis->GetHeader(); + + pThis->width = bheader.GetInt( 192, 16 ); + pThis->height = bheader.GetInt( 192+16, 16 ); + + // Choosing 8 lines per block ensures that each block + // starts on a byte boundary. + pThis->block_width = pThis->width; + pThis->block_height = 8; + + pThis->loaded = true; +} + +/************************************************************************/ +/* GetBlockWidth() */ +/************************************************************************/ + +int CPCIDSKBitmap::GetBlockWidth() const + +{ + if( !loaded ) + Load(); + + return block_width; +} + +/************************************************************************/ +/* GetBlockHeight() */ +/************************************************************************/ + +int CPCIDSKBitmap::GetBlockHeight() const + +{ + if( !loaded ) + Load(); + + return block_height; +} + +/************************************************************************/ +/* GetBlockCount() */ +/************************************************************************/ + +int CPCIDSKBitmap::GetBlockCount() const + +{ + if( !loaded ) + Load(); + + return ((width + block_width - 1) / block_width) + * ((height + block_height - 1) / block_height); +} + +/************************************************************************/ +/* GetWidth() */ +/************************************************************************/ + +int CPCIDSKBitmap::GetWidth() const + +{ + if( !loaded ) + Load(); + + return width; +} + +/************************************************************************/ +/* GetHeight() */ +/************************************************************************/ + +int CPCIDSKBitmap::GetHeight() const + +{ + if( !loaded ) + Load(); + + return height; +} + +/************************************************************************/ +/* GetType() */ +/************************************************************************/ + +eChanType CPCIDSKBitmap::GetType() const + +{ + return CHN_BIT; +} + +/************************************************************************/ +/* PCIDSK_CopyBits() */ +/* */ +/* Copy bit strings - adapted from GDAL. */ +/************************************************************************/ + +static void +PCIDSK_CopyBits( const uint8 *pabySrcData, int nSrcOffset, int nSrcStep, + uint8 *pabyDstData, int nDstOffset, int nDstStep, + int nBitCount, int nStepCount ) + +{ + int iStep; + int iBit; + + for( iStep = 0; iStep < nStepCount; iStep++ ) + { + for( iBit = 0; iBit < nBitCount; iBit++ ) + { + if( pabySrcData[nSrcOffset>>3] + & (0x80 >>(nSrcOffset & 7)) ) + pabyDstData[nDstOffset>>3] |= (0x80 >> (nDstOffset & 7)); + else + pabyDstData[nDstOffset>>3] &= ~(0x80 >> (nDstOffset & 7)); + + + nSrcOffset++; + nDstOffset++; + } + + nSrcOffset += (nSrcStep - nBitCount); + nDstOffset += (nDstStep - nBitCount); + } +} + +/************************************************************************/ +/* ReadBlock() */ +/************************************************************************/ + +int CPCIDSKBitmap::ReadBlock( int block_index, void *buffer, + int win_xoff, int win_yoff, + int win_xsize, int win_ysize ) + +{ + uint64 block_size = (block_width * block_height + 7) / 8; + uint8 *wrk_buffer = (uint8 *) buffer; + + if( block_index < 0 || block_index >= GetBlockCount() ) + { + ThrowPCIDSKException( "Requested non-existant block (%d)", + block_index ); + } +/* -------------------------------------------------------------------- */ +/* If we are doing subwindowing, we will need to create a */ +/* temporary bitmap to load into. If we are concerned about */ +/* high performance access to small windows in big bitmaps we */ +/* will eventually want to reimplement this to avoid reading */ +/* the whole block to subwindow from. */ +/* -------------------------------------------------------------------- */ + if( win_ysize != -1 ) + { + if( win_xoff < 0 || win_xoff + win_xsize > GetBlockWidth() + || win_yoff < 0 || win_yoff + win_ysize > GetBlockHeight() ) + { + ThrowPCIDSKException( + "Invalid window in CPCIDSKBitmap::ReadBlock(): xoff=%d,yoff=%d,xsize=%d,ysize=%d", + win_xoff, win_yoff, win_xsize, win_ysize ); + } + + wrk_buffer = (uint8 *) malloc((size_t) block_size); + if( wrk_buffer == NULL ) + ThrowPCIDSKException( "Out of memory allocating %d bytes in CPCIDSKBitmap::ReadBlock()", + (int) block_size ); + } + +/* -------------------------------------------------------------------- */ +/* Read the block, taking care in the case of partial blocks at */ +/* the bottom of the image. */ +/* -------------------------------------------------------------------- */ + if( (block_index+1) * block_height <= height ) + ReadFromFile( wrk_buffer, block_size * block_index, block_size ); + else + { + uint64 short_block_size; + + memset( buffer, 0, (size_t) block_size ); + + short_block_size = + ((height - block_index*block_height) * block_width + 7) / 8; + + ReadFromFile( wrk_buffer, block_size * block_index, short_block_size ); + } + +/* -------------------------------------------------------------------- */ +/* Perform subwindowing if needed. */ +/* -------------------------------------------------------------------- */ + if( win_ysize != -1 ) + { + int y_out; + + for( y_out = 0; y_out < win_ysize; y_out++ ) + { + PCIDSK_CopyBits( wrk_buffer, + win_xoff + (y_out+win_yoff)*block_width, 0, + (uint8*) buffer, y_out * win_xsize, 0, + win_xsize, 1 ); + } + + free( wrk_buffer ); + } + + return 0; +} + +/************************************************************************/ +/* WriteBlock() */ +/************************************************************************/ + +int CPCIDSKBitmap::WriteBlock( int block_index, void *buffer ) + +{ + uint64 block_size = (block_width * block_height) / 8; + + if( (block_index+1) * block_height <= height ) + WriteToFile( buffer, block_size * block_index, block_size ); + else + { + uint64 short_block_size; + + short_block_size = + ((height - block_index*block_height) * block_width + 7) / 8; + + WriteToFile( buffer, block_size * block_index, short_block_size ); + } + + return 1; +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int CPCIDSKBitmap::GetOverviewCount() +{ + return 0; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +PCIDSKChannel *CPCIDSKBitmap::GetOverview( CPL_UNUSED int i ) +{ + // The %d is ignored in the exception. + ThrowPCIDSKException("Non-existant overview %d requested on bitmap segment."); + return NULL; +} + +/************************************************************************/ +/* IsOverviewValid() */ +/************************************************************************/ + +bool CPCIDSKBitmap::IsOverviewValid( CPL_UNUSED int i ) +{ + return false; +} + +/************************************************************************/ +/* GetOverviewResampling() */ +/************************************************************************/ + +std::string CPCIDSKBitmap::GetOverviewResampling( CPL_UNUSED int i ) +{ + return ""; +} + +/************************************************************************/ +/* SetOverviewValidity() */ +/************************************************************************/ + +void CPCIDSKBitmap::SetOverviewValidity( CPL_UNUSED int i, CPL_UNUSED bool validity ) +{ +} + +/************************************************************************/ +/* GetMetadataValue() */ +/************************************************************************/ + +std::string CPCIDSKBitmap::GetMetadataValue( const std::string &key ) const + +{ + return CPCIDSKSegment::GetMetadataValue( key ); +} + +/************************************************************************/ +/* SetMetadataValue() */ +/************************************************************************/ + +void CPCIDSKBitmap::SetMetadataValue( const std::string &key, + const std::string &value ) + +{ + CPCIDSKSegment::SetMetadataValue( key, value ); +} + +/************************************************************************/ +/* GetOverviewLevelMapping() */ +/************************************************************************/ +std::vector CPCIDSKBitmap::GetOverviewLevelMapping() const +{ + std::vector ov; + + return ov; +} + +/************************************************************************/ +/* GetMetadataKeys() */ +/************************************************************************/ + +std::vector CPCIDSKBitmap::GetMetadataKeys() const + +{ + return CPCIDSKSegment::GetMetadataKeys(); +} + +/************************************************************************/ +/* Synchronize() */ +/************************************************************************/ + +void CPCIDSKBitmap::Synchronize() + +{ + // TODO + + CPCIDSKSegment::Synchronize(); +} + +/************************************************************************/ +/* GetDescription() */ +/************************************************************************/ + +std::string CPCIDSKBitmap::GetDescription() + +{ + return CPCIDSKSegment::GetDescription(); +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +void CPCIDSKBitmap::SetDescription( const std::string &description ) + +{ + CPCIDSKSegment::SetDescription( description ); +} + +/************************************************************************/ +/* GetHistoryEntries() */ +/************************************************************************/ + +std::vector CPCIDSKBitmap::GetHistoryEntries() const + +{ + return CPCIDSKSegment::GetHistoryEntries(); +} + +/************************************************************************/ +/* SetHistoryEntries() */ +/************************************************************************/ + +void CPCIDSKBitmap::SetHistoryEntries( const std::vector &entries ) + +{ + CPCIDSKSegment::SetHistoryEntries( entries ); +} + +/************************************************************************/ +/* PushHistory() */ +/************************************************************************/ + +void CPCIDSKBitmap::PushHistory( const std::string &app, + const std::string &message ) + +{ + CPCIDSKSegment::PushHistory( app, message ); +} + +/************************************************************************/ +/* GetChanInfo() */ +/************************************************************************/ +void CPCIDSKBitmap::GetChanInfo( std::string &filename, uint64 &image_offset, + uint64 &pixel_offset, uint64 &line_offset, + bool &little_endian ) const + +{ + image_offset = 0; + pixel_offset = 0; + line_offset = 0; + little_endian = true; + filename = ""; +} + +/************************************************************************/ +/* SetChanInfo() */ +/************************************************************************/ + +void CPCIDSKBitmap::SetChanInfo( CPL_UNUSED std::string filename, CPL_UNUSED uint64 image_offset, + CPL_UNUSED uint64 pixel_offset, CPL_UNUSED uint64 line_offset, + CPL_UNUSED bool little_endian ) +{ + ThrowPCIDSKException( "Attempt to SetChanInfo() on a bitmap." ); +} + +/************************************************************************/ +/* GetEChanInfo() */ +/************************************************************************/ +void CPCIDSKBitmap::GetEChanInfo( std::string &filename, int &echannel, + int &exoff, int &eyoff, + int &exsize, int &eysize ) const +{ + echannel = 0; + exoff = 0; + eyoff = 0; + exsize = 0; + eysize = 0; + filename = ""; +} + +/************************************************************************/ +/* SetEChanInfo() */ +/************************************************************************/ + +void CPCIDSKBitmap::SetEChanInfo( CPL_UNUSED std::string filename, CPL_UNUSED int echannel, + CPL_UNUSED int exoff, CPL_UNUSED int eyoff, + CPL_UNUSED int exsize, CPL_UNUSED int eysize ) +{ + ThrowPCIDSKException( "Attempt to SetEChanInfo() on a bitmap." ); +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskbitmap.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskbitmap.h new file mode 100644 index 000000000..826125b94 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskbitmap.h @@ -0,0 +1,113 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CPCIDSKBitmap class. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __INCLUDE_SEGMENT_PCIDSKBITMAP_H +#define __INCLUDE_SEGMENT_PCIDSKBITMAP_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_buffer.h" +#include "segment/cpcidsksegment.h" +#include "pcidsk_channel.h" + +#include + +namespace PCIDSK +{ + class PCIDSKFile; + + /************************************************************************/ + /* CPCIDSKGeoref */ + /************************************************************************/ + + class CPCIDSKBitmap : virtual public CPCIDSKSegment, + public PCIDSKChannel + { + public: + CPCIDSKBitmap(PCIDSKFile *file,int segment,const char*segment_pointer); + virtual ~CPCIDSKBitmap(); + + virtual void Initialize(); + + // Channel interface + virtual int GetBlockWidth() const; + virtual int GetBlockHeight() const; + virtual int GetBlockCount() const; + virtual int GetWidth() const; + virtual int GetHeight() const; + virtual eChanType GetType() const; + virtual int ReadBlock( int block_index, void *buffer, + int win_xoff=-1, int win_yoff=-1, + int win_xsize=-1, int win_ysize=-1 ); + virtual int WriteBlock( int block_index, void *buffer ); + virtual int GetOverviewCount(); + virtual PCIDSKChannel *GetOverview( int i ); + virtual bool IsOverviewValid( int i ); + virtual std::string GetOverviewResampling( int i ); + virtual void SetOverviewValidity( int i, bool validity ); + virtual std::vector GetOverviewLevelMapping() const; + + virtual std::string GetMetadataValue( const std::string &key ) const; + virtual void SetMetadataValue( const std::string &key, const std::string &value ); + virtual std::vector GetMetadataKeys() const; + + virtual void Synchronize(); + + virtual std::string GetDescription(); + virtual void SetDescription( const std::string &description ); + + virtual std::vector GetHistoryEntries() const; + virtual void SetHistoryEntries( const std::vector &entries ); + virtual void PushHistory(const std::string &app, + const std::string &message); + + virtual void GetChanInfo( std::string &filename, uint64 &image_offset, + uint64 &pixel_offset, uint64 &line_offset, + bool &little_endian ) const; + virtual void SetChanInfo( std::string filename, uint64 image_offset, + uint64 pixel_offset, uint64 line_offset, + bool little_endian ); + virtual void GetEChanInfo( std::string &filename, int &echannel, + int &exoff, int &eyoff, + int &exsize, int &eysize ) const; + virtual void SetEChanInfo( std::string filename, int echannel, + int exoff, int eyoff, + int exsize, int eysize ); + + private: + bool loaded; + + int width; + int height; + int block_width; + int block_height; + + void Load() const; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_PCIDSKBITMAP_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskephemerissegment.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskephemerissegment.cpp new file mode 100644 index 000000000..5b0e3dabe --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskephemerissegment.cpp @@ -0,0 +1,1398 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSKEphemerisSegment class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "segment/cpcidsksegment.h" +#include "core/pcidsk_utils.h" +#include "segment/cpcidskephemerissegment.h" +#include "pcidsk_exception.h" +#include "core/pcidsk_utils.h" + +#include +#include +#include +#include + +using namespace PCIDSK; + +namespace +{ + /** + * Function to get the minimum value of two values. + * + * @param a The first value. + * @param b The second value. + * + * @return The minimum value of the two specified values. + */ + int MinFunction(int a,int b) + { + return (aWrite(); + } +} + +/************************************************************************/ +/* ConvertDeg() */ +/************************************************************************/ +/** + * if mode is 0, convert angle from 0 to 360 to 0 to 180 and 0 to -180 + * if mode is 1, convert angle from 0 to 180 and 0 to -180 to 0 to 360 + * + * @param degree the degree + * @param mode the mode + */ +double CPCIDSKEphemerisSegment::ConvertDeg(double degree, int mode) +{ + double result; + + if (mode == 0) + { +/* -------------------------------------------------------------------- */ +/* degree is in range of 0 to 360 */ +/* -------------------------------------------------------------------- */ + if (degree > 180) + result = degree - 360; + else + result = degree; + } + else + { +/* -------------------------------------------------------------------- */ +/* degree is in range of 0 to 180 and 0 to -180 */ +/* -------------------------------------------------------------------- */ + if (degree < 0) + result = 360 + degree; + else + result = degree; + + } + return (result); +} + +/************************************************************************/ +/* ReadAvhrrEphemerisSegment() */ +/************************************************************************/ +/** + * Read the contents of blocks 9, 11, and onwards from the orbit + * segment into the EphemerisSeg_t structure. + * @param nStartBlock where to start to read in the buffer + * @param psEphSegRec the structure to populate with information. + */ +void +CPCIDSKEphemerisSegment::ReadAvhrrEphemerisSegment(int nStartBlock, + EphemerisSeg_t *psEphSegRec) +{ + int nBlock = 0, nLine = 0; + int nPos = 0; + AvhrrSeg_t *as = NULL; + + int nDataLength = seg_data.buffer_size; +/* -------------------------------------------------------------------- */ +/* Allocate the AVHRR segment portion of EphemerisSeg_t. */ +/* -------------------------------------------------------------------- */ + psEphSegRec->AvhrrSeg = new AvhrrSeg_t(); + as = psEphSegRec->AvhrrSeg; + +/* -------------------------------------------------------------------- */ +/* Read in the Nineth Block which contains general info + ephemeris */ +/* info as well. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 8*512; + + as->szImageFormat = seg_data.Get(nPos, 16); + as->nImageXSize = seg_data.GetInt(nPos+16, 16); + as->nImageYSize = seg_data.GetInt(nPos+32, 16); + + + if ( std::strncmp(seg_data.Get(nPos+48,9), "ASCENDING", 9)==0 ) + as->bIsAscending = true; + else + as->bIsAscending = false; + if ( std::strncmp(seg_data.Get(nPos+64,7), "ROTATED", 7)==0 ) + as->bIsImageRotated = true; + else + as->bIsImageRotated = false; + + as->szOrbitNumber = seg_data.Get(nPos+80, 16); + as->szAscendDescendNodeFlag = seg_data.Get(nPos+96,16); + as->szEpochYearAndDay = seg_data.Get(nPos+112,16); + as->szEpochTimeWithinDay = seg_data.Get(nPos+128,16); + as->szTimeDiffStationSatelliteMsec = seg_data.Get(nPos+144,16); + as->szActualSensorScanRate = seg_data.Get(nPos+160,16); + as->szIdentOfOrbitInfoSource = seg_data.Get(nPos+176,16); + as->szInternationalDesignator = seg_data.Get(nPos+192,16); + as->szOrbitNumAtEpoch = seg_data.Get(nPos+208,16); + as->szJulianDayAscendNode = seg_data.Get(nPos+224,16); + as->szEpochYear = seg_data.Get(nPos+240,16); + as->szEpochMonth = seg_data.Get(nPos+256,16); + as->szEpochDay = seg_data.Get(nPos+272,16); + as->szEpochHour = seg_data.Get(nPos+288,16); + as->szEpochMinute = seg_data.Get(nPos+304,16); + as->szEpochSecond = seg_data.Get(nPos+320,16); + as->szPointOfAriesDegrees = seg_data.Get(nPos+336,16); + as->szAnomalisticPeriod = seg_data.Get(nPos+352,16); + as->szNodalPeriod = seg_data.Get(nPos+368,16); + as->szEccentricity = seg_data.Get(nPos+384,16); + as->szArgumentOfPerigee = seg_data.Get(nPos+400,16); + as->szRAAN = seg_data.Get(nPos+416,16); + as->szInclination = seg_data.Get(nPos+432,16); + as->szMeanAnomaly = seg_data.Get(nPos+448,16); + as->szSemiMajorAxis = seg_data.Get(nPos+464,16); + +/* -------------------------------------------------------------------- */ +/* Skip the 10th block which is reserved for future use. */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +/* Read in the 11th block, which contains indexing info. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*10; + + as->nRecordSize = seg_data.GetInt(nPos, 16); + as->nBlockSize = seg_data.GetInt(nPos+16, 16); + as->nNumRecordsPerBlock = seg_data.GetInt(nPos+32, 16); + as->nNumBlocks = seg_data.GetInt(nPos+48, 16); + as->nNumScanlineRecords = seg_data.GetInt(nPos+64, 16); + +/* -------------------------------------------------------------------- */ +/* Allocate the scanline records. */ +/* -------------------------------------------------------------------- */ + if ( as->nNumScanlineRecords == 0 ) + return; + +/* -------------------------------------------------------------------- */ +/* Now read the 12th block and onward. */ +/* -------------------------------------------------------------------- */ + nBlock = 12; + + if ( as->nNumRecordsPerBlock == 0 ) + return; + + for(nLine = 0; nLine < as->nNumScanlineRecords; + nLine += as->nNumRecordsPerBlock) + { + int nNumRecords = MinFunction(as->nNumRecordsPerBlock, + as->nNumScanlineRecords - nLine); + nPos = nStartBlock + 512*(nBlock-1); + if( nDataLength < 512*nBlock ) + { + break; + } + + for(int i = 0; i < nNumRecords; ++i) + { + AvhrrLine_t sLine; + ReadAvhrrScanlineRecord(nPos+i*80, &sLine); + as->Line.push_back(sLine); + } + + ++nBlock; + } +} + +/************************************************************************/ +/* ReadAvhrrScanlineRecord() */ +/************************************************************************/ +/** + * Read from a byte buffer in order to set a scanline record. + * @param pbyBuf the buffer that contains the record to read. + * @param psScanlineRecord the record to read. + */ +void +CPCIDSKEphemerisSegment::ReadAvhrrScanlineRecord(int nPos, + AvhrrLine_t *psScanlineRecord) +{ + int i; + AvhrrLine_t *sr = psScanlineRecord; + + sr->nScanLineNum = ReadAvhrrInt32((unsigned char*)seg_data.Get(nPos,4)); + sr->nStartScanTimeGMTMsec = ReadAvhrrInt32((unsigned char*)seg_data.Get(nPos+4,4)); + + for(i = 0; i < 10; ++i) + sr->abyScanLineQuality[i] = seg_data.GetInt(nPos+8+i,1); + + for(i = 0; i < 5; ++i) + { + sr->aabyBadBandIndicators[i][0] = seg_data.GetInt(nPos+18+2*i,1); + sr->aabyBadBandIndicators[i][1] = seg_data.GetInt(nPos+18+2*i+1,1); + } + + for(i = 0; i < 8; ++i) + sr->abySatelliteTimeCode[i] = seg_data.GetInt(nPos+28+i,1); + + for(i = 0; i < 3; ++i) + sr->anTargetTempData[i] = ReadAvhrrInt32((unsigned char*)seg_data.Get(nPos+36+i*4,4)); + for(i = 0; i < 3; ++i) + sr->anTargetScanData[i] = ReadAvhrrInt32((unsigned char*)seg_data.Get(nPos+48+i*4,4)); + for(i = 0; i < 5; ++i) + sr->anSpaceScanData[i] = ReadAvhrrInt32((unsigned char*)seg_data.Get(nPos+60+i*4,4)); +} + +/************************************************************************/ +/* ReadAvhrrInt32() */ +/************************************************************************/ +/** + * Read an integer from a given buffer of at least 4 bytes. + * @param pbyBuf the buffer that contains the value. + * @return the value + */ +int +CPCIDSKEphemerisSegment::ReadAvhrrInt32(unsigned char* pbyBuf) +{ + int nValue = 0; + unsigned char* b = pbyBuf; + nValue = (int)((b[0]<<24) | (b[1]<<16) | (b[2]<<8) | b[3]); + + return( nValue ); +} + +/************************************************************************/ +/* WriteAvhrrEphemerisSegment() */ +/************************************************************************/ +/** + * Write the contents of blocks 9, 10, and onwards to the orbit + * segment from fields in the EphemerisSeg_t structure. + * @param nStartBlock where to start to write the information in the buffer + * @param psEphSegRec the information to write. + */ +void +CPCIDSKEphemerisSegment::WriteAvhrrEphemerisSegment(int nStartBlock, + EphemerisSeg_t *psEphSegRec) +{ + int nBlock = 0, nLine = 0; + int nPos = 0; +/* -------------------------------------------------------------------- */ +/* Check that the AvhrrSeg is not NULL. */ +/* -------------------------------------------------------------------- */ + AvhrrSeg_t *as = NULL; + as = psEphSegRec->AvhrrSeg; + + if ( as == NULL) + { + throw PCIDSKException("The AvhrrSeg is NULL."); + } + +/* -------------------------------------------------------------------- */ +/* Realloc the data buffer large enough to hold all the AVHRR */ +/* information, and zero it. */ +/* -------------------------------------------------------------------- */ + int nToAdd = 512 * + (((as->nNumScanlineRecords + as->nNumRecordsPerBlock-1) / + as->nNumRecordsPerBlock) + +4); + seg_data.SetSize(seg_data.buffer_size + nToAdd); + + nPos = nStartBlock; + memset(seg_data.buffer+nPos,' ',nToAdd); + +/* -------------------------------------------------------------------- */ +/* Write the first avhrr Block. */ +/* -------------------------------------------------------------------- */ + + seg_data.Put(as->szImageFormat.c_str(),nPos,16); + + seg_data.Put(as->nImageXSize,nPos+16,16); + seg_data.Put(as->nImageYSize,nPos+32,16); + + if ( as->bIsAscending ) + seg_data.Put("ASCENDING",nPos+48,9); + else + seg_data.Put("DESCENDING",nPos+48,10); + + if ( as->bIsImageRotated ) + seg_data.Put("ROTATED",nPos+64,7); + else + seg_data.Put("NOT ROTATED",nPos+64,11); + + seg_data.Put(as->szOrbitNumber.c_str(),nPos+80,16); + seg_data.Put(as->szAscendDescendNodeFlag.c_str(),nPos+96,16,true); + seg_data.Put(as->szEpochYearAndDay.c_str(),nPos+112,16,true); + seg_data.Put(as->szEpochTimeWithinDay.c_str(),nPos+128,16,true); + seg_data.Put(as->szTimeDiffStationSatelliteMsec.c_str(),nPos+144,16,true); + seg_data.Put(as->szActualSensorScanRate.c_str(),nPos+160,16,true); + seg_data.Put(as->szIdentOfOrbitInfoSource.c_str(),nPos+176,16,true); + seg_data.Put(as->szInternationalDesignator.c_str(),nPos+192,16,true); + seg_data.Put(as->szOrbitNumAtEpoch.c_str(),nPos+208,16,true); + seg_data.Put(as->szJulianDayAscendNode.c_str(),nPos+224,16,true); + seg_data.Put(as->szEpochYear.c_str(),nPos+240,16,true); + seg_data.Put(as->szEpochMonth.c_str(),nPos+256,16,true); + seg_data.Put(as->szEpochDay.c_str(),nPos+272,16,true); + seg_data.Put(as->szEpochHour.c_str(),nPos+288,16,true); + seg_data.Put(as->szEpochMinute.c_str(),nPos+304,16,true); + seg_data.Put(as->szEpochSecond.c_str(),nPos+320,16,true); + seg_data.Put(as->szPointOfAriesDegrees.c_str(),nPos+336,16,true); + seg_data.Put(as->szAnomalisticPeriod.c_str(),nPos+352,16,true); + seg_data.Put(as->szNodalPeriod.c_str(),nPos+368,16,true); + seg_data.Put(as->szEccentricity.c_str(), nPos+384,16,true); + seg_data.Put(as->szArgumentOfPerigee.c_str(),nPos+400,16,true); + seg_data.Put(as->szRAAN.c_str(),nPos+416,16,true); + seg_data.Put(as->szInclination.c_str(),nPos+432,16,true); + seg_data.Put(as->szMeanAnomaly.c_str(),nPos+448,16,true); + seg_data.Put(as->szSemiMajorAxis.c_str(),nPos+464,16,true); + +/* -------------------------------------------------------------------- */ +/* second avhrr block is all zeros. */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +/* Write the 3rd avhrr Block. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*2; + + seg_data.Put(as->nRecordSize,nPos,16); + seg_data.Put(as->nBlockSize,nPos+16,16); + seg_data.Put(as->nNumRecordsPerBlock,nPos+32,16); + seg_data.Put(as->nNumBlocks,nPos+48,16); + seg_data.Put(as->nNumScanlineRecords,nPos+64,16); + +/* -------------------------------------------------------------------- */ +/* Write the fourth avhrr block onwards. */ +/* -------------------------------------------------------------------- */ + if ( as->Line.size() == 0 ) + return; + + nBlock = 4; + + if ( as->nNumRecordsPerBlock == 0 ) + return; + + for(nLine = 0; nLine < as->nNumScanlineRecords; + nLine += as->nNumRecordsPerBlock) + { + int nNumRecords = MinFunction(as->nNumRecordsPerBlock, + as->nNumScanlineRecords - nLine); + nPos = nStartBlock + (nBlock-1) * 512; + + for(int i = 0; i < nNumRecords; ++i) + { + WriteAvhrrScanlineRecord(&(as->Line[nLine+i]), nPos + i*80); + } + + ++nBlock; + } +} + +/************************************************************************/ +/* WriteAvhrrScanlineRecord() */ +/************************************************************************/ +/** + * Write a scanline record to a byte buffer. + * @param psScanlineRecord the record to write + * @param pbyBuf the buffer to write. + */ +void +CPCIDSKEphemerisSegment::WriteAvhrrScanlineRecord( + AvhrrLine_t *psScanlineRecord, + int nPos) +{ + int i; + AvhrrLine_t *sr = psScanlineRecord; + unsigned char* b = (unsigned char*)&(seg_data.buffer[nPos]); + + WriteAvhrrInt32(sr->nScanLineNum, b); + WriteAvhrrInt32(sr->nStartScanTimeGMTMsec, b+4); + + for(i=0 ; i < 10 ; i++) + seg_data.Put(sr->abyScanLineQuality[i],nPos+8+i,1); + + for(i = 0; i < 5; ++i) + { + seg_data.Put(sr->aabyBadBandIndicators[i][0],nPos+18+i*2,1); + seg_data.Put(sr->aabyBadBandIndicators[i][1],nPos+18+i*2+1,1); + } + + for(i=0 ; i < 8 ; i++) + seg_data.Put(sr->abySatelliteTimeCode[i],nPos+28+i,1); + + for(i = 0; i < 3; ++i) + WriteAvhrrInt32(sr->anTargetTempData[i], b+(36+i*4)); + for(i = 0; i < 3; ++i) + WriteAvhrrInt32(sr->anTargetScanData[i], b+(48+i*4)); + for(i = 0; i < 5; ++i) + WriteAvhrrInt32(sr->anSpaceScanData[i], b+(60+i*4)); + +} + +/************************************************************************/ +/* WriteAvhrrInt32() */ +/************************************************************************/ +/** + * Write an integer into a given buffer of at least 4 bytes. + * @param nValue the value to write + * @param pbyBuf the buffer to write into. + */ +void CPCIDSKEphemerisSegment::WriteAvhrrInt32(int nValue, + unsigned char* pbyBuf) +{ + pbyBuf[0] = ((nValue & 0xff000000) >> 24); + pbyBuf[1] = ((nValue & 0x00ff0000) >> 16); + pbyBuf[2] = ((nValue & 0x0000ff00) >> 8); + pbyBuf[3] = (nValue & 0x000000ff); +} + + +/************************************************************************/ +/* BinaryToEphemeris() */ +/************************************************************************/ +/** + * Read binary information from a binary buffer to create an + * EphemerisSeg_t structure. The caller is responsible to free the memory + * of the returned structure with delete. + * + * @param nStartBlock where to start read the orbit info into the buffer. + * @return the orbbit information + */ +EphemerisSeg_t * +CPCIDSKEphemerisSegment::BinaryToEphemeris( int nStartBlock ) + +{ + EphemerisSeg_t *segment; + int i; + int nPos = nStartBlock; + + segment = new EphemerisSeg_t(); + +/* -------------------------------------------------------------------- */ +/* Process first block. */ +/* -------------------------------------------------------------------- */ + + segment->SatelliteDesc = seg_data.Get(nPos+8,32); + segment->SceneID = seg_data.Get(nPos+40, 32); + +/* -------------------------------------------------------------------- */ +/* Process the second block. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512; + + segment->SatelliteSensor = seg_data.Get(nPos, 16); + for (i=0; i<16; i++) + { + if (segment->SatelliteSensor[i] == ' ') + { + segment->SatelliteSensor = segment->SatelliteSensor.substr(0,i); + break; + } + } + + segment->SensorNo = seg_data.Get(nPos+22, 2); + segment->DateImageTaken = seg_data.Get(nPos+44, 22); + + if (seg_data.buffer[nPos+66] == 'Y' || + seg_data.buffer[nPos+66] == 'y') + segment->SupSegExist = true; + else + segment->SupSegExist = false; + segment->FieldOfView = seg_data.GetDouble(nPos+88, 22); + segment->ViewAngle = seg_data.GetDouble(nPos+110, 22); + segment->NumColCentre = seg_data.GetDouble(nPos+132, 22); + segment->RadialSpeed = seg_data.GetDouble(nPos+154, 22); + segment->Eccentricity = seg_data.GetDouble(nPos+176, 22); + segment->Height = seg_data.GetDouble(nPos+198, 22); + segment->Inclination = seg_data.GetDouble(nPos+220, 22); + segment->TimeInterval = seg_data.GetDouble(nPos+242, 22); + segment->NumLineCentre = seg_data.GetDouble(nPos+264, 22); + segment->LongCentre = seg_data.GetDouble(nPos+286, 22); + segment->AngularSpd = seg_data.GetDouble(nPos+308, 22); + segment->AscNodeLong = seg_data.GetDouble(nPos+330, 22); + segment->ArgPerigee = seg_data.GetDouble(nPos+352, 22); + segment->LatCentre = seg_data.GetDouble(nPos+374, 22); + segment->EarthSatelliteDist = seg_data.GetDouble(nPos+396, 22); + segment->NominalPitch = seg_data.GetDouble(nPos+418, 22); + segment->TimeAtCentre = seg_data.GetDouble(nPos+440, 22); + segment->SatelliteArg = seg_data.GetDouble(nPos+462, 22); + segment->bDescending = true; + if (seg_data.buffer[nPos+484] == 'A') + segment->bDescending = false; + +/* -------------------------------------------------------------------- */ +/* Process the third block. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 2*512; + + segment->XCentre = seg_data.GetDouble(nPos, 22); + segment->YCentre = seg_data.GetDouble(nPos+22, 22); + segment->UtmXCentre = seg_data.GetDouble(nPos+44, 22); + segment->UtmYCentre = seg_data.GetDouble(nPos+66, 22); + segment->PixelRes = seg_data.GetDouble(nPos+88, 22); + segment->LineRes = seg_data.GetDouble(nPos+110, 22); + if (seg_data.buffer[nPos+132] == 'Y' || + seg_data.buffer[nPos+132] == 'y') + segment->CornerAvail = true; + else + segment->CornerAvail = false; + segment->MapUnit = seg_data.Get(nPos+133, 16); + + segment->XUL = seg_data.GetDouble(nPos+149, 22); + segment->YUL = seg_data.GetDouble(nPos+171, 22); + segment->XUR = seg_data.GetDouble(nPos+193, 22); + segment->YUR = seg_data.GetDouble(nPos+215, 22); + segment->XLR = seg_data.GetDouble(nPos+237, 22); + segment->YLR = seg_data.GetDouble(nPos+259, 22); + segment->XLL = seg_data.GetDouble(nPos+281, 22); + segment->YLL = seg_data.GetDouble(nPos+303, 22); + segment->UtmXUL = seg_data.GetDouble(nPos+325, 22); + segment->UtmYUL = seg_data.GetDouble(nPos+347, 22); + segment->UtmXUR = seg_data.GetDouble(nPos+369, 22); + segment->UtmYUR = seg_data.GetDouble(nPos+391, 22); + segment->UtmXLR = seg_data.GetDouble(nPos+413, 22); + segment->UtmYLR = seg_data.GetDouble(nPos+435, 22); + segment->UtmXLL = seg_data.GetDouble(nPos+457, 22); + segment->UtmYLL = seg_data.GetDouble(nPos+479, 22); + +/* -------------------------------------------------------------------- */ +/* Process the 4th block (Corner lat/long coordinates) */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 3*512; + + segment->LongCentreDeg = seg_data.GetDouble(nPos, 16); + segment->LatCentreDeg = seg_data.GetDouble(nPos+16, 16); + segment->LongUL = seg_data.GetDouble(nPos+32, 16); + segment->LatUL = seg_data.GetDouble(nPos+48, 16); + segment->LongUR = seg_data.GetDouble(nPos+64, 16); + segment->LatUR = seg_data.GetDouble(nPos+80, 16); + segment->LongLR = seg_data.GetDouble(nPos+96, 16); + segment->LatLR = seg_data.GetDouble(nPos+112, 16); + segment->LongLL = seg_data.GetDouble(nPos+128, 16); + segment->LatLL = seg_data.GetDouble(nPos+144, 16); + segment->HtCentre = seg_data.GetDouble(nPos+160, 16); + segment->HtUL = seg_data.GetDouble(nPos+176, 16); + segment->HtUR = seg_data.GetDouble(nPos+192, 16); + segment->HtLR = seg_data.GetDouble(nPos+208, 16); + segment->HtLL = seg_data.GetDouble(nPos+224, 16); + +/* -------------------------------------------------------------------- */ +/* Process the 5th block. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*4; + + segment->ImageRecordLength = seg_data.GetInt(nPos, 16); + segment->NumberImageLine = seg_data.GetInt(nPos+16, 16); + segment->NumberBytePerPixel = seg_data.GetInt(nPos+32, 16); + segment->NumberSamplePerLine = seg_data.GetInt(nPos+48, 16); + segment->NumberPrefixBytes = seg_data.GetInt(nPos+64, 16); + segment->NumberSuffixBytes = seg_data.GetInt(nPos+80, 16); + +/* -------------------------------------------------------------------- */ +/* Process the 6th and 7th block. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 5*512; + + segment->SPNCoeff = 0; + + if(std::strncmp(seg_data.Get(nPos,8), "SPOT1BOD", 8)==0 || + std::strncmp(seg_data.Get(nPos,8), "SPOT1BNW", 8)==0) + { + segment->SPNCoeff = seg_data.GetInt(nPos+22, 22); + for (i=0; i<20; i++) + { + segment->SPCoeff1B[i] = + seg_data.GetDouble(nPos+(i+2)*22, 22); + } + + if (std::strncmp(seg_data.Get(nPos,8), "SPOT1BNW", 8)==0) + { + nPos = nStartBlock + 6*512; + + for (i=0; i<19; i++) + { + segment->SPCoeff1B[i+20] = + seg_data.GetDouble(nPos+i*22, 22); + } + segment->SPCoeffSg[0] = seg_data.GetInt(nPos+418, 8); + segment->SPCoeffSg[1] = seg_data.GetInt(nPos+426, 8); + segment->SPCoeffSg[2] = seg_data.GetInt(nPos+434, 8); + segment->SPCoeffSg[3] = seg_data.GetInt(nPos+442, 8); + } + } + +/* -------------------------------------------------------------------- */ +/* 6th and 7th block of ORBIT segment are blank. */ +/* Read in the 8th block. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 7*512; + + if (std::strncmp(seg_data.Get(nPos,8), "ATTITUDE", 8)==0) + segment->Type = OrbAttitude; + else if (std::strncmp(seg_data.Get(nPos,8), "RADAR ", 8)==0) + segment->Type = OrbLatLong; + else if (std::strncmp(seg_data.Get(nPos,8), "AVHRR ", 8)==0) + segment->Type = OrbAvhrr; + else if (std::strncmp(seg_data.Get(nPos,8), "NO_DATA ", 8)==0) + segment->Type = OrbNone; + else + throw PCIDSKException("Invalid Orbit type found: [%s]", + seg_data.Get(nPos,8)); + +/* -------------------------------------------------------------------- */ +/* Orbit segment is a Satellite Attitude Segment(ATTITUDE) only */ +/* for SPOT 1A. */ +/* -------------------------------------------------------------------- */ + if (segment->Type == OrbAttitude) + { + AttitudeSeg_t *AttitudeSeg; + int nBlock, nData; + + AttitudeSeg = segment->AttitudeSeg = new AttitudeSeg_t(); + +/* -------------------------------------------------------------------- */ +/* Read in the 9th block. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*8; + + AttitudeSeg->Roll = seg_data.GetDouble(nPos, 22); + AttitudeSeg->Pitch = seg_data.GetDouble(nPos+22, 22); + AttitudeSeg->Yaw = seg_data.GetDouble(nPos+44, 22); + AttitudeSeg->NumberOfLine = seg_data.GetInt(nPos+88, 22); + if (AttitudeSeg->NumberOfLine % ATT_SEG_LINE_PER_BLOCK != 0) + AttitudeSeg->NumberBlockData = 1 + + AttitudeSeg->NumberOfLine / ATT_SEG_LINE_PER_BLOCK; + else + AttitudeSeg->NumberBlockData = + AttitudeSeg->NumberOfLine / ATT_SEG_LINE_PER_BLOCK; + +/* -------------------------------------------------------------------- */ +/* Read in the line required. */ +/* -------------------------------------------------------------------- */ + for (nBlock=0, nData=0; nBlockNumberBlockData; + nBlock++) + { +/* -------------------------------------------------------------------- */ +/* Read in 10+nBlock th block as required. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*(9+nBlock); + +/* -------------------------------------------------------------------- */ +/* Fill in the lines as required. */ +/* -------------------------------------------------------------------- */ + for (i=0; + iNumberOfLine; + i++, nData++) + { + AttitudeLine_t oAttitudeLine; + oAttitudeLine.ChangeInAttitude + = seg_data.GetDouble(nPos+i*44, 22); + oAttitudeLine.ChangeEarthSatelliteDist + = seg_data.GetDouble(nPos+i*44+22, 22); + AttitudeSeg->Line.push_back(oAttitudeLine); + } + } + + if (nData != AttitudeSeg->NumberOfLine) + { + throw PCIDSKException("Number of data line read (%d) " + "does not matches with what is specified in " + "the segment (%d).\n", nData, + AttitudeSeg->NumberOfLine); + } + } +/* -------------------------------------------------------------------- */ +/* Radar segment (LATLONG) */ +/* -------------------------------------------------------------------- */ + else if (segment->Type == OrbLatLong) + { + RadarSeg_t *RadarSeg; + int nBlock, nData; + + RadarSeg = segment->RadarSeg = new RadarSeg_t(); +/* -------------------------------------------------------------------- */ +/* Read in the 9th block. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*8; + + RadarSeg->Identifier = seg_data.Get(nPos, 16); + RadarSeg->Facility = seg_data.Get(nPos+16, 16); + RadarSeg->Ellipsoid = seg_data.Get(nPos+32, 16); + + RadarSeg->EquatorialRadius = seg_data.GetDouble(nPos+48, 16); + RadarSeg->PolarRadius = seg_data.GetDouble(nPos+64, 16); + RadarSeg->IncidenceAngle = seg_data.GetDouble(nPos+80, 16); + RadarSeg->LineSpacing = seg_data.GetDouble(nPos+96, 16); + RadarSeg->PixelSpacing = seg_data.GetDouble(nPos+112, 16); + RadarSeg->ClockAngle = seg_data.GetDouble(nPos+128, 16); + +/* -------------------------------------------------------------------- */ +/* Read in the 10th block. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 9*512; + + RadarSeg->NumberBlockData = seg_data.GetInt(nPos, 8); + RadarSeg->NumberData = seg_data.GetInt(nPos+8, 8); + +/* -------------------------------------------------------------------- */ +/* Read in the 11-th through 11+RadarSeg->NumberBlockData th block */ +/* for the ancillary data present. */ +/* -------------------------------------------------------------------- */ + for (nBlock = 0, nData = 0; + nBlock < RadarSeg->NumberBlockData; nBlock++) + { +/* -------------------------------------------------------------------- */ +/* Read in one block of data. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*(10+nBlock); + + for (i=0; + iNumberData; + i++, nData++) + { + int offset; + char *currentindex; + void *currentptr; + double tmp; + int32 tmpInt; + const double million = 1000000.0; + +/* -------------------------------------------------------------------- */ +/* Reading in one ancillary data at a time. */ +/* -------------------------------------------------------------------- */ + AncillaryData_t oData; + offset = i*ANC_DATA_SIZE; + + currentindex = (char *)seg_data.Get(nPos+offset,4); + currentptr = (char *) currentindex; + SwapData(currentptr,4,1); + tmpInt = *((int32 *) currentptr); + oData.SlantRangeFstPixel = tmpInt; + + currentindex = (char *)seg_data.Get(nPos+offset+4,4); + currentptr = (char *) currentindex; + SwapData(currentptr,4,1); + tmpInt = *((int32 *) currentptr); + oData.SlantRangeLastPixel = tmpInt; + + currentindex = (char *)seg_data.Get(nPos+offset+8,4); + currentptr = (char *) currentindex; + SwapData(currentptr,4,1); + tmpInt = *((int32 *) currentptr); + tmp = (double) tmpInt / million; + oData.FstPixelLat + = (float) ConvertDeg(tmp, 0); + + currentindex = (char *)seg_data.Get(nPos+offset+12,4); + currentptr = (char *) currentindex; + SwapData(currentptr,4,1); + tmpInt = *((int32 *) currentptr); + tmp = (double) tmpInt / million; + oData.MidPixelLat + = (float) ConvertDeg(tmp, 0); + + currentindex = (char *)seg_data.Get(nPos+offset+16,4); + currentptr = (char *) currentindex; + SwapData(currentptr,4,1); + tmpInt = *((int32 *) currentptr); + tmp = (double) tmpInt / million; + oData.LstPixelLat + = (float) ConvertDeg(tmp, 0); + + currentindex = (char *)seg_data.Get(nPos+offset+20,4); + currentptr = (char *) currentindex; + SwapData(currentptr,4,1); + tmpInt = *((int32 *) currentptr); + tmp = (double) tmpInt / million; + oData.FstPixelLong + = (float) ConvertDeg(tmp, 0); + + currentindex = (char *)seg_data.Get(nPos+offset+24,4); + currentptr = (char *) currentindex; + SwapData(currentptr,4,1); + tmpInt = *((int32 *) currentptr); + tmp = (double) tmpInt / million; + oData.MidPixelLong + = (float) ConvertDeg(tmp, 0); + + currentindex = (char *)seg_data.Get(nPos+offset+28,4); + currentptr = (char *) currentindex; + SwapData(currentptr,4,1); + tmpInt = *((int32 *) currentptr); + tmp = (double) tmpInt / million; + oData.LstPixelLong + = (float) ConvertDeg(tmp, 0); + + RadarSeg->Line.push_back(oData); + } + } + + if (RadarSeg->NumberData != nData) + { + throw PCIDSKException("Number " + "of data lines read (%d) does not match with" + "\nwhat is specified in segment (%d).\n", nData, + RadarSeg->NumberData); + } + } +/* -------------------------------------------------------------------- */ +/* AVHRR segment */ +/* -------------------------------------------------------------------- */ + else if (segment->Type == OrbAvhrr) + { + ReadAvhrrEphemerisSegment( nStartBlock, segment); + } + + return segment; +} + +/************************************************************************/ +/* EphemerisToBinary() */ +/************************************************************************/ +/** + * Write an Orbit segment information into a binary buffer of size 4096. + * The caller is responsible to free this memory with delete []. + * + * @param psOrbit the orbit information to write into the binary + * @param nStartBlock where to start writing in the buffer. + */ +void +CPCIDSKEphemerisSegment::EphemerisToBinary( EphemerisSeg_t * psOrbit, + int nStartBlock ) + +{ + int i,j; + +/* -------------------------------------------------------------------- */ +/* The binary data must be at least 8 blocks (4096 bytes) long */ +/* for the common information. */ +/* -------------------------------------------------------------------- */ + seg_data.SetSize(nStartBlock+4096); + memset(seg_data.buffer+nStartBlock,' ',4096); + + int nPos = nStartBlock; + +/* -------------------------------------------------------------------- */ +/* Write the first block */ +/* -------------------------------------------------------------------- */ + + seg_data.Put("ORBIT ",nPos,8); + seg_data.Put(psOrbit->SatelliteDesc.c_str(), nPos+8,32,true); + seg_data.Put(psOrbit->SceneID.c_str(), nPos+40,32,true); + +/* -------------------------------------------------------------------- */ +/* Write the second block */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 1*512; + + seg_data.Put(psOrbit->SatelliteSensor.c_str(), nPos,16); + seg_data.Put(psOrbit->SensorNo.c_str(),nPos+22,2,true); + seg_data.Put(psOrbit->DateImageTaken.c_str(), nPos+44,22,true); + + if (psOrbit->SupSegExist) + seg_data.Put("Y",nPos+66,1); + else + seg_data.Put("N",nPos+66,1); + + seg_data.Put(psOrbit->FieldOfView,nPos+88,22,"%22.14f"); + seg_data.Put(psOrbit->ViewAngle,nPos+110,22,"%22.14f"); + seg_data.Put(psOrbit->NumColCentre,nPos+132,22,"%22.14f"); + seg_data.Put(psOrbit->RadialSpeed,nPos+154,22,"%22.14f"); + seg_data.Put(psOrbit->Eccentricity,nPos+176,22,"%22.14f"); + seg_data.Put(psOrbit->Height,nPos+198,22,"%22.14f"); + seg_data.Put(psOrbit->Inclination,nPos+220,22,"%22.14f"); + seg_data.Put(psOrbit->TimeInterval,nPos+242,22,"%22.14f"); + seg_data.Put(psOrbit->NumLineCentre,nPos+264,22,"%22.14f"); + seg_data.Put(psOrbit->LongCentre,nPos+286,22,"%22.14f"); + seg_data.Put(psOrbit->AngularSpd,nPos+308,22,"%22.14f"); + seg_data.Put(psOrbit->AscNodeLong,nPos+330,22,"%22.14f"); + seg_data.Put(psOrbit->ArgPerigee,nPos+352,22,"%22.14f"); + seg_data.Put(psOrbit->LatCentre,nPos+374,22,"%22.14f"); + seg_data.Put(psOrbit->EarthSatelliteDist,nPos+396,22,"%22.14f"); + seg_data.Put(psOrbit->NominalPitch,nPos+418,22,"%22.14f"); + seg_data.Put(psOrbit->TimeAtCentre,nPos+440,22,"%22.14f"); + seg_data.Put(psOrbit->SatelliteArg,nPos+462,22,"%22.14f"); + + if (psOrbit->bDescending) + seg_data.Put("DESCENDING",nPos+484,10); + else + seg_data.Put("ASCENDING ",nPos+484,10); + +/* -------------------------------------------------------------------- */ +/* Write the third block */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*2; + + seg_data.Put(psOrbit->XCentre,nPos,22,"%22.14f"); + seg_data.Put(psOrbit->YCentre,nPos+22,22,"%22.14f"); + seg_data.Put(psOrbit->UtmXCentre,nPos+44,22,"%22.14f"); + seg_data.Put(psOrbit->UtmYCentre,nPos+66,22,"%22.14f"); + seg_data.Put(psOrbit->PixelRes,nPos+88,22,"%22.14f"); + seg_data.Put(psOrbit->LineRes,nPos+110,22,"%22.14f"); + + if (psOrbit->CornerAvail == true) + seg_data.Put("Y",nPos+132,1); + else + seg_data.Put("N",nPos+132,1); + + seg_data.Put(psOrbit->MapUnit.c_str(),nPos+133,16,true); + + seg_data.Put(psOrbit->XUL,nPos+149,22,"%22.14f"); + seg_data.Put(psOrbit->YUL,nPos+171,22,"%22.14f"); + seg_data.Put(psOrbit->XUR,nPos+193,22,"%22.14f"); + seg_data.Put(psOrbit->YUR,nPos+215,22,"%22.14f"); + seg_data.Put(psOrbit->XLR,nPos+237,22,"%22.14f"); + seg_data.Put(psOrbit->YLR,nPos+259,22,"%22.14f"); + seg_data.Put(psOrbit->XLL,nPos+281,22,"%22.14f"); + seg_data.Put(psOrbit->YLL,nPos+303,22,"%22.14f"); + seg_data.Put(psOrbit->UtmXUL,nPos+325,22,"%22.14f"); + seg_data.Put(psOrbit->UtmYUL,nPos+347,22,"%22.14f"); + seg_data.Put(psOrbit->UtmXUR,nPos+369,22,"%22.14f"); + seg_data.Put(psOrbit->UtmYUR,nPos+391,22,"%22.14f"); + seg_data.Put(psOrbit->UtmXLR,nPos+413,22,"%22.14f"); + seg_data.Put(psOrbit->UtmYLR,nPos+435,22,"%22.14f"); + seg_data.Put(psOrbit->UtmXLL,nPos+457,22,"%22.14f"); + seg_data.Put(psOrbit->UtmYLL,nPos+479,22,"%22.14f"); + +/* -------------------------------------------------------------------- */ +/* Write the fourth block */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*3; + + seg_data.Put(psOrbit->LongCentreDeg,nPos,22,"%16.7f"); + seg_data.Put(psOrbit->LatCentreDeg,nPos+16,22,"%16.7f"); + seg_data.Put(psOrbit->LongUL,nPos+32,22,"%16.7f"); + seg_data.Put(psOrbit->LatUL,nPos+48,22,"%16.7f"); + seg_data.Put(psOrbit->LongUR,nPos+64,22,"%16.7f"); + seg_data.Put(psOrbit->LatUR,nPos+80,22,"%16.7f"); + seg_data.Put(psOrbit->LongLR,nPos+96,22,"%16.7f"); + seg_data.Put(psOrbit->LatLR,nPos+112,22,"%16.7f"); + seg_data.Put(psOrbit->LongLL,nPos+128,22,"%16.7f"); + seg_data.Put(psOrbit->LatLL,nPos+144,22,"%16.7f"); + seg_data.Put(psOrbit->HtCentre,nPos+160,22,"%16.7f"); + seg_data.Put(psOrbit->HtUL,nPos+176,22,"%16.7f"); + seg_data.Put(psOrbit->HtUR,nPos+192,22,"%16.7f"); + seg_data.Put(psOrbit->HtLR,nPos+208,22,"%16.7f"); + seg_data.Put(psOrbit->HtLL,nPos+224,22,"%16.7f"); + +/* -------------------------------------------------------------------- */ +/* Write the fifth block */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*4; + + seg_data.Put(psOrbit->ImageRecordLength,nPos,16); + seg_data.Put(psOrbit->NumberImageLine,nPos+16,16); + seg_data.Put(psOrbit->NumberBytePerPixel,nPos+32,16); + seg_data.Put(psOrbit->NumberSamplePerLine,nPos+48,16); + seg_data.Put(psOrbit->NumberPrefixBytes,nPos+64,16); + seg_data.Put(psOrbit->NumberSuffixBytes,nPos+80,16); + +/* -------------------------------------------------------------------- */ +/* Write the sixth and seventh block (blanks) */ +/* For SPOT it is not blank */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*5; + + if (psOrbit->SPNCoeff > 0) + { + if (psOrbit->SPNCoeff == 20) + { + seg_data.Put("SPOT1BOD",nPos,8); + seg_data.Put(psOrbit->SPNCoeff,nPos+22,22); + + j = 44; + for (i=0; i<20; i++) + { + seg_data.Put(psOrbit->SPCoeff1B[i], + nPos+j,22,"%22.14f"); + j += 22; + } + } + else + { + seg_data.Put("SPOT1BNW",nPos,8); + seg_data.Put(psOrbit->SPNCoeff,nPos+22,22); + + j = 44; + for (i=0; i<20; i++) + { + seg_data.Put(psOrbit->SPCoeff1B[i], + nPos+j,22,"%22.14f"); + j += 22; + } + + nPos = nStartBlock + 512*6; + + j = 0; + for (i=20; i<39; i++) + { + seg_data.Put(psOrbit->SPCoeff1B[i], + nPos+j,22,"%22.14f"); + j += 22; + } + + seg_data.Put(psOrbit->SPCoeffSg[0],nPos+418,8); + seg_data.Put(psOrbit->SPCoeffSg[1],nPos+426,8); + seg_data.Put(psOrbit->SPCoeffSg[2],nPos+434,8); + seg_data.Put(psOrbit->SPCoeffSg[3],nPos+442,8); + } + } + +/* -------------------------------------------------------------------- */ +/* Write the eighth block. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*7; + + if (psOrbit->Type == OrbAttitude) + seg_data.Put("ATTITUDE",nPos,8); + else if (psOrbit->Type == OrbLatLong) + seg_data.Put("RADAR ",nPos,8); + else if (psOrbit->Type == OrbAvhrr) + seg_data.Put("AVHRR ",nPos,8); + else if (psOrbit->Type == OrbNone) + seg_data.Put("NO_DATA ",nPos,8); + else + { + throw PCIDSKException("Invalid Orbit type."); + } + +/* ==================================================================== */ +/* Orbit segment is a Satellite Attitude Segment(ATTITUDE) only */ +/* for SPOT 1A. */ +/* ==================================================================== */ + if (psOrbit->Type == OrbAttitude) + { + AttitudeSeg_t *AttitudeSeg; + int nBlock, nData; + + AttitudeSeg = psOrbit->AttitudeSeg; + + if (AttitudeSeg == NULL) + { + throw PCIDSKException("The AttitudeSeg is NULL."); + } + +/* -------------------------------------------------------------------- */ +/* Add one block */ +/* -------------------------------------------------------------------- */ + seg_data.SetSize(seg_data.buffer_size + 512); + + nPos = nStartBlock + 512*8; + memset(seg_data.buffer+nPos,' ',512); + +/* -------------------------------------------------------------------- */ +/* Write the nineth block. */ +/* -------------------------------------------------------------------- */ + + seg_data.Put(AttitudeSeg->Roll,nPos,22,"%22.14f"); + seg_data.Put(AttitudeSeg->Pitch,nPos+22,22,"%22.14f"); + seg_data.Put(AttitudeSeg->Yaw,nPos+44,22,"%22.14f"); + + if (AttitudeSeg->NumberOfLine % ATT_SEG_LINE_PER_BLOCK != 0) + AttitudeSeg->NumberBlockData = 1 + + AttitudeSeg->NumberOfLine / ATT_SEG_LINE_PER_BLOCK; + else + AttitudeSeg->NumberBlockData = + AttitudeSeg->NumberOfLine / ATT_SEG_LINE_PER_BLOCK; + + seg_data.Put(AttitudeSeg->NumberBlockData,nPos+66,22); + seg_data.Put(AttitudeSeg->NumberOfLine,nPos+88,22); + +/* -------------------------------------------------------------------- */ +/* Add NumberBlockData blocks to array. */ +/* -------------------------------------------------------------------- */ + seg_data.SetSize(seg_data.buffer_size + + 512 * AttitudeSeg->NumberBlockData); + + nPos = nStartBlock + 512*9; + memset(seg_data.buffer+nPos,' ', + 512 * AttitudeSeg->NumberBlockData); + +/* -------------------------------------------------------------------- */ +/* Write out the line required. */ +/* -------------------------------------------------------------------- */ + for (nBlock=0, nData=0; nBlockNumberBlockData; + nBlock++) + { + int i; + nPos = nStartBlock + 512*(nBlock + 9); + +/* -------------------------------------------------------------------- */ +/* Fill in buffer as required. */ +/* -------------------------------------------------------------------- */ + for (i=0; + iNumberOfLine; + i++, nData++) + { + seg_data.Put( + AttitudeSeg->Line[nData].ChangeInAttitude, + nPos+i*44,22,"%22.14f"); + seg_data.Put( + AttitudeSeg->Line[nData].ChangeEarthSatelliteDist, + nPos+i*44+22,22,"%22.14f"); + } + } + + if (nData != AttitudeSeg->NumberOfLine) + { + throw PCIDSKException("Number of data line written" + " (%d) does not match with\nwhat is specified " + " in the segment (%d).\n", + nData, AttitudeSeg->NumberOfLine); + } + } + +/* ==================================================================== */ +/* Radar segment (LATLONG) */ +/* ==================================================================== */ + else if (psOrbit->Type == OrbLatLong) + { + RadarSeg_t *RadarSeg; + int i, nBlock, nData; + + RadarSeg = psOrbit->RadarSeg; + + if (RadarSeg == NULL) + { + throw PCIDSKException("The RadarSeg is NULL."); + } + +/* -------------------------------------------------------------------- */ +/* Add two blocks. */ +/* -------------------------------------------------------------------- */ + seg_data.SetSize(seg_data.buffer_size + 512*2); + + nPos = nStartBlock + 512*8; + memset(seg_data.buffer+nPos,' ', 512*2); + +/* -------------------------------------------------------------------- */ +/* Write out the nineth block. */ +/* -------------------------------------------------------------------- */ + + seg_data.Put(RadarSeg->Identifier.c_str(), nPos,16); + seg_data.Put(RadarSeg->Facility.c_str(), nPos+16,16); + seg_data.Put(RadarSeg->Ellipsoid.c_str(), nPos+32,16); + + seg_data.Put(RadarSeg->EquatorialRadius,nPos+48,16,"%16.7f"); + seg_data.Put(RadarSeg->PolarRadius,nPos+64,16,"%16.7f"); + seg_data.Put(RadarSeg->IncidenceAngle,nPos+80,16,"%16.7f"); + seg_data.Put(RadarSeg->LineSpacing,nPos+96,16,"%16.7f"); + seg_data.Put(RadarSeg->PixelSpacing,nPos+112,16,"%16.7f"); + seg_data.Put(RadarSeg->ClockAngle,nPos+128,16,"%16.7f"); + +/* -------------------------------------------------------------------- */ +/* Write out the tenth block. */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*9; + + seg_data.Put(RadarSeg->NumberBlockData,nPos,8); + seg_data.Put(RadarSeg->NumberData,nPos+8,8); + +/* -------------------------------------------------------------------- */ +/* Make room for all the following per-line data. */ +/* -------------------------------------------------------------------- */ + seg_data.SetSize(seg_data.buffer_size + + 512 * RadarSeg->NumberBlockData); + + nPos = nStartBlock + 512*10; + memset(seg_data.buffer+nPos,' ', + 512 * RadarSeg->NumberBlockData); + +/* -------------------------------------------------------------------- */ +/* Write out the 11-th through 11+psOrbit->NumberBlockData block */ +/* for the ancillary data present. */ +/* -------------------------------------------------------------------- */ + for (nBlock = 0, nData = 0; + nBlock < RadarSeg->NumberBlockData; nBlock++) + { + for (i=0; + iNumberData; + i++, nData++) + { + int offset; + char *currentptr, *currentindex; + double tmp, tmpDouble; + const double million = 1000000.0; + int32 tmpInt; + +/* -------------------------------------------------------------------- */ +/* Point to correct block */ +/* -------------------------------------------------------------------- */ + nPos = nStartBlock + 512*(10+nBlock); + +/* -------------------------------------------------------------------- */ +/* Writing out one ancillary data at a time. */ +/* -------------------------------------------------------------------- */ + offset = i*ANC_DATA_SIZE; + + currentptr = + (char *) &(RadarSeg->Line[nData].SlantRangeFstPixel); + SwapData(currentptr,4,1); + currentindex = &(seg_data.buffer[nPos+offset]); + std::memcpy((void *) currentindex,currentptr, 4); + + currentptr = + (char *) &(RadarSeg->Line[nData].SlantRangeLastPixel); + SwapData(currentptr,4,1); + currentindex += 4; + std::memcpy((void *) currentindex,currentptr, 4); + + tmp = ConvertDeg(RadarSeg->Line[nData].FstPixelLat, 1); + tmpDouble = tmp * million; + tmpInt = (int32) tmpDouble; + currentptr = (char *) &tmpInt; + SwapData(currentptr,4,1); + currentindex += 4; + std::memcpy((void *) currentindex,currentptr, 4); + + tmp = ConvertDeg(RadarSeg->Line[nData].MidPixelLat, 1); + tmpDouble = tmp * million; + tmpInt = (int32) tmpDouble; + currentptr = (char *) &tmpInt; + SwapData(currentptr,4,1); + currentindex += 4; + std::memcpy((void *) currentindex,currentptr, 4); + + tmp = ConvertDeg(RadarSeg->Line[nData].LstPixelLat, 1); + tmpDouble = tmp * million; + tmpInt = (int32) tmpDouble; + currentptr = (char *) &tmpInt; + SwapData(currentptr,4,1); + currentindex += 4; + std::memcpy((void *) currentindex,currentptr, 4); + + tmp = ConvertDeg(RadarSeg->Line[nData].FstPixelLong, 1); + tmpDouble = tmp * million; + tmpInt = (int32) tmpDouble; + currentptr = (char *) &tmpInt; + SwapData(currentptr,4,1); + currentindex += 4; + std::memcpy((void *) currentindex,currentptr, 4); + + tmp = ConvertDeg(RadarSeg->Line[nData].MidPixelLong, 1); + tmpDouble = tmp * million; + tmpInt = (int32) tmpDouble; + currentptr = (char *) &tmpInt; + SwapData(currentptr,4,1); + currentindex += 4; + std::memcpy((void *) currentindex,currentptr, 4); + + tmp = ConvertDeg(RadarSeg->Line[nData].LstPixelLong, 1); + tmpDouble = tmp * million; + tmpInt = (int32) tmpDouble; + currentptr = (char *) &tmpInt; + SwapData(currentptr,4,1); + currentindex += 4; + std::memcpy((void *) currentindex,currentptr, 4); + } + } + } + +/* ==================================================================== */ +/* AVHRR segment */ +/* ==================================================================== */ + else if ( psOrbit->Type == OrbAvhrr && + psOrbit->AvhrrSeg->nNumRecordsPerBlock > 0 ) + { + WriteAvhrrEphemerisSegment(nStartBlock + 8*512 , psOrbit); + } +} + + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskephemerissegment.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskephemerissegment.h new file mode 100644 index 000000000..bd9bacfc0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskephemerissegment.h @@ -0,0 +1,87 @@ +/****************************************************************************** + * + * Purpose: Support for reading and manipulating PCIDSK Ephemeris Segments + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_SEGMENT_PCIDSKEPHEMERIS_SEG_H +#define __INCLUDE_PCIDSK_SEGMENT_PCIDSKEPHEMERIS_SEG_H + +#include "pcidsk_ephemeris.h" +#include "segment/cpcidsksegment.h" + +namespace PCIDSK { + class PCIDSKFile; + + class CPCIDSKEphemerisSegment : public PCIDSKEphemerisSegment, + public CPCIDSKSegment + { + public: + CPCIDSKEphemerisSegment(PCIDSKFile *file, int segment,const char *segment_pointer,bool bLoad=true); + ~CPCIDSKEphemerisSegment(); + + const EphemerisSeg_t& GetEphemeris() const + { + return *mpoEphemeris; + }; + void SetEphemeris(const EphemerisSeg_t& oEph) + { + if(mpoEphemeris) + { + delete mpoEphemeris; + } + mpoEphemeris = new EphemerisSeg_t(oEph); + mbModified = true; + }; + + //synchronize the segment on disk. + void Synchronize(); + private: + + // Helper housekeeping functions + void Load(); + void Write(); + + EphemerisSeg_t* mpoEphemeris; + //functions to read/write binary information + protected: + // The raw segment data + PCIDSKBuffer seg_data; + bool loaded_; + bool mbModified; + void ReadAvhrrEphemerisSegment(int, + EphemerisSeg_t *); + void ReadAvhrrScanlineRecord(int nPos, + AvhrrLine_t *psScanlineRecord); + int ReadAvhrrInt32(unsigned char* pbyBuf); + void WriteAvhrrEphemerisSegment(int , EphemerisSeg_t *); + void WriteAvhrrScanlineRecord(AvhrrLine_t *psScanlineRecord, + int nPos); + void WriteAvhrrInt32(int nValue, unsigned char* pbyBuf); + EphemerisSeg_t *BinaryToEphemeris( int nStartBlock ); + void EphemerisToBinary( EphemerisSeg_t *, int ); + double ConvertDeg(double degree, int mode); + }; +} + +#endif // __INCLUDE_PCIDSK_SEGMENT_PCIDSKEPHEMERIS_SEG_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskgcp2segment.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskgcp2segment.cpp new file mode 100644 index 000000000..2b4720e10 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskgcp2segment.cpp @@ -0,0 +1,295 @@ +/****************************************************************************** + * + * Purpose: Implementation of access to a PCIDSK GCP2 Segment + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "segment/cpcidskgcp2segment.h" + +#include "pcidsk_gcp.h" +#include "pcidsk_exception.h" + +#include +#include +#include +#include + +using namespace PCIDSK; + +struct CPCIDSKGCP2Segment::PCIDSKGCP2SegInfo +{ + std::vector gcps; + unsigned int num_gcps; + PCIDSKBuffer seg_data; + + std::string map_units; ///< PCI mapunits string + std::string proj_parms; ///< Additional projection parameters + unsigned int num_proj; + bool changed; +}; + +CPCIDSKGCP2Segment::CPCIDSKGCP2Segment(PCIDSKFile *file, int segment, const char *segment_pointer) + : CPCIDSKSegment(file, segment, segment_pointer), loaded_(false) +{ + pimpl_ = new PCIDSKGCP2SegInfo; + pimpl_->gcps.clear(); + pimpl_->changed = false; + Load(); +} + +CPCIDSKGCP2Segment::~CPCIDSKGCP2Segment() +{ + RebuildSegmentData(); + delete pimpl_; +} + +void CPCIDSKGCP2Segment::Load() +{ + if (loaded_) { + return; + } + + // Read the the segment in. The first block has information about + // the structure of the GCP segment (how many, the projection, etc.) + pimpl_->seg_data.SetSize(data_size - 1024); + ReadFromFile(pimpl_->seg_data.buffer, 0, data_size - 1024); + + // check for 'GCP2 ' in the first 8 bytes + if (std::strncmp(pimpl_->seg_data.buffer, "GCP2 ", 8) != 0) { + // Assume it's an empty segment, so we can mark loaded_ = true, + // write it out and return + pimpl_->changed = true; + pimpl_->map_units = "LAT/LONG D000"; + pimpl_->proj_parms = ""; + pimpl_->num_gcps = 0; + loaded_ = true; + return; + } + + // Check the number of blocks field's validity + unsigned int num_blocks = pimpl_->seg_data.GetInt(8, 8); + + if (((data_size - 1024 - 512) / 512) != num_blocks) { + //ThrowPCIDSKException("Calculated number of blocks (%d) does not match " + // "the value encoded in the GCP2 segment (%d).", ((data_size - 1024 - 512)/512), + // num_blocks); + // Something is messed up with how GDB generates these segments... nice. + } + + pimpl_->num_gcps = pimpl_->seg_data.GetInt(16, 8); + + // Extract the map units string: + pimpl_->map_units = std::string(pimpl_->seg_data.buffer + 24, 16); + + // Extract the projection parameters string + pimpl_->proj_parms = std::string(pimpl_->seg_data.buffer + 256, 256); + + // Get the number of alternative projections (should be 0!) + pimpl_->num_proj = pimpl_->seg_data.GetInt(40, 8); + if (pimpl_->num_proj != 0) { + ThrowPCIDSKException("There are alternative projections contained in this " + "GCP2 segment. This functionality is not supported in libpcidsk."); + } + + // Load the GCPs into the vector of PCIDSK::GCPs + for (unsigned int i = 0; i < pimpl_->num_gcps; i++) + { + unsigned int offset = 512 + i * 256; + bool is_cp = pimpl_->seg_data.buffer[offset] == 'C'; + double pixel = pimpl_->seg_data.GetDouble(offset + 6, 14); + double line = pimpl_->seg_data.GetDouble(offset + 20, 14); + + double elev = pimpl_->seg_data.GetDouble(offset + 34, 12); + double x = pimpl_->seg_data.GetDouble(offset + 48, 22); + double y = pimpl_->seg_data.GetDouble(offset + 70, 22); + + PCIDSK::GCP::EElevationDatum elev_datum = pimpl_->seg_data.buffer[offset + 47] != 'M' ? + GCP::EEllipsoidal : GCP::EMeanSeaLevel; + + char elev_unit_c = pimpl_->seg_data.buffer[offset + 46]; + PCIDSK::GCP::EElevationUnit elev_unit = elev_unit_c == 'M' ? GCP::EMetres : + elev_unit_c == 'F' ? GCP::EInternationalFeet : + elev_unit_c == 'A' ? GCP::EAmericanFeet : GCP::EUnknown; + + double pix_err = pimpl_->seg_data.GetDouble(offset + 92, 10); + double line_err = pimpl_->seg_data.GetDouble(offset + 102, 10); + double elev_err = pimpl_->seg_data.GetDouble(offset + 112, 10); + + double x_err = pimpl_->seg_data.GetDouble(offset + 122, 14); + double y_err = pimpl_->seg_data.GetDouble(offset + 136, 14); + + std::string gcp_id(pimpl_->seg_data.buffer + offset + 192, 64); + + PCIDSK::GCP gcp(x, y, elev, + line, pixel, gcp_id, pimpl_->map_units, + pimpl_->proj_parms, + x_err, y_err, elev_err, + line_err, pix_err); + gcp.SetElevationUnit(elev_unit); + gcp.SetElevationDatum(elev_datum); + gcp.SetCheckpoint(is_cp); + + pimpl_->gcps.push_back(gcp); + } + + loaded_ = true; +} + + // Return all GCPs in the segment +std::vector const& CPCIDSKGCP2Segment::GetGCPs(void) const +{ + return pimpl_->gcps; +} + +// Write the given GCPs to the segment. If the segment already +// exists, it will be replaced with this one. +void CPCIDSKGCP2Segment::SetGCPs(std::vector const& gcps) +{ + pimpl_->num_gcps = gcps.size(); + pimpl_->gcps = gcps; // copy them in + pimpl_->changed = true; + + RebuildSegmentData(); +} + +// Return the count of GCPs in the segment +unsigned int CPCIDSKGCP2Segment::GetGCPCount(void) const +{ + return pimpl_->num_gcps; +} + +void CPCIDSKGCP2Segment::RebuildSegmentData(void) +{ + if (pimpl_->changed == false) { + return; + } + + // Rebuild the segment data based on the contents of the struct + int num_blocks = (pimpl_->num_gcps + 1) / 2; + + // This will have to change when we have proper projections support + + if (pimpl_->gcps.size() > 0) + { + pimpl_->gcps[0].GetMapUnits(pimpl_->map_units, + pimpl_->proj_parms); + } + + pimpl_->seg_data.SetSize(num_blocks * 512 + 512); + + // Write out the first few fields + pimpl_->seg_data.Put("GCP2 ", 0, 8); + pimpl_->seg_data.Put(num_blocks, 8, 8); + pimpl_->seg_data.Put((int)pimpl_->gcps.size(), 16, 8); + pimpl_->seg_data.Put(pimpl_->map_units.c_str(), 24, 16); + pimpl_->seg_data.Put((int)0, 40, 8); + pimpl_->seg_data.Put(pimpl_->proj_parms.c_str(), 256, 256); + + // Time to write GCPs out: + std::vector::const_iterator iter = + pimpl_->gcps.begin(); + + unsigned int id = 0; + while (iter != pimpl_->gcps.end()) { + std::size_t offset = 512 + id * 256; + + if ((*iter).IsCheckPoint()) { + pimpl_->seg_data.Put("C", offset, 1); + } else { + pimpl_->seg_data.Put("G", offset, 1); + } + + pimpl_->seg_data.Put("0", offset + 1, 5); + + // Start writing out the GCP values + pimpl_->seg_data.Put((*iter).GetPixel(), offset + 6, 14, "%14.4f"); + pimpl_->seg_data.Put((*iter).GetLine(), offset + 20, 14, "%14.4f"); + pimpl_->seg_data.Put((*iter).GetZ(), offset + 34, 12, "%12.4f"); + + GCP::EElevationUnit unit; + GCP::EElevationDatum datum; + (*iter).GetElevationInfo(datum, unit); + + char unit_c[2]; + + switch (unit) + { + case GCP::EMetres: + case GCP::EUnknown: + unit_c[0] = 'M'; + break; + case GCP::EAmericanFeet: + unit_c[0] = 'A'; + break; + case GCP::EInternationalFeet: + unit_c[0] = 'F'; + break; + } + + char datum_c[2]; + + switch(datum) + { + case GCP::EEllipsoidal: + datum_c[0] = 'E'; + break; + case GCP::EMeanSeaLevel: + datum_c[0] = 'M'; + break; + } + + unit_c[1] = '\0'; + datum_c[1] = '\0'; + + // Write out elevation information + pimpl_->seg_data.Put(unit_c, offset + 46, 1); + pimpl_->seg_data.Put(datum_c, offset + 47, 1); + + pimpl_->seg_data.Put((*iter).GetX(), offset + 48, 22, "%22.14e"); + pimpl_->seg_data.Put((*iter).GetY(), offset + 70, 22, "%22.14e"); + pimpl_->seg_data.Put((*iter).GetPixelErr(), offset + 92, 10, "%10.4f"); + pimpl_->seg_data.Put((*iter).GetLineErr(), offset + 102, 10, "%10.4f"); + pimpl_->seg_data.Put((*iter).GetZErr(), offset + 112, 10, "%10.4f"); + pimpl_->seg_data.Put((*iter).GetXErr(), offset + 122, 14, "%14.4e"); + pimpl_->seg_data.Put((*iter).GetYErr(), offset + 136, 14, "%14.4e"); + pimpl_->seg_data.Put((*iter).GetIDString(), offset + 192, 64, true ); + + id++; + iter++; + } + + WriteToFile(pimpl_->seg_data.buffer, 0, pimpl_->seg_data.buffer_size); + + pimpl_->changed = false; +} + +// Clear a GCP Segment +void CPCIDSKGCP2Segment::ClearGCPs(void) +{ + pimpl_->num_gcps = 0; + pimpl_->gcps.clear(); + pimpl_->changed = true; + + RebuildSegmentData(); +} + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskgcp2segment.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskgcp2segment.h new file mode 100644 index 000000000..24fed850d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskgcp2segment.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * + * Purpose: Declaration of access to a PCIDSK GCP2 Segment + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_SEGMENT_CPCIDSKGCP2SEGMENT_H +#define __INCLUDE_SEGMENT_CPCIDSKGCP2SEGMENT_H + +#include "pcidsk_gcp.h" +#include "pcidsk_gcpsegment.h" +#include "segment/cpcidsksegment.h" + +namespace PCIDSK { + class CPCIDSKGCP2Segment : virtual public PCIDSKGCPSegment, + public CPCIDSKSegment + { + public: + CPCIDSKGCP2Segment(PCIDSKFile *file, int segment,const char *segment_pointer); + ~CPCIDSKGCP2Segment(); + + // Return all GCPs in the segment + std::vector const& GetGCPs(void) const; + + // Write the given GCPs to the segment. If the segment already + // exists, it will be replaced with this one. + void SetGCPs(std::vector const& gcps); + + // Return the count of GCPs in the segment + unsigned int GetGCPCount(void) const; + + // Clear a GCP Segment + void ClearGCPs(void); + private: + void Load(); + void RebuildSegmentData(void); + bool loaded_; + struct PCIDSKGCP2SegInfo; + PCIDSKGCP2SegInfo* pimpl_; + }; +} + +#endif // __INCLUDE_SEGMENT_CPCIDSKGCP2SEGMENT_H + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskgeoref.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskgeoref.cpp new file mode 100644 index 000000000..bab64ad82 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskgeoref.cpp @@ -0,0 +1,1460 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSKGeoref class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_exception.h" +#include "segment/cpcidskgeoref.h" +#include "core/pcidsk_utils.h" +#include +#include +#include +#include +#include + +using namespace PCIDSK; + +static double PAK2PCI( double deg, int function ); + +#ifndef ABS +# define ABS(x) ((x<0) ? (-1*(x)) : x) +#endif + +/************************************************************************/ +/* CPCIDSKGeoref() */ +/************************************************************************/ + +CPCIDSKGeoref::CPCIDSKGeoref( PCIDSKFile *file, int segment, + const char *segment_pointer ) + : CPCIDSKSegment( file, segment, segment_pointer ) + +{ + loaded = false; +} + +/************************************************************************/ +/* ~CPCIDSKGeoref() */ +/************************************************************************/ + +CPCIDSKGeoref::~CPCIDSKGeoref() + +{ +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void CPCIDSKGeoref::Initialize() + +{ + // Note: we depend on Load() reacting gracefully to an uninitialized + // georeferencing segment. + + WriteSimple( "PIXEL", 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ); +} + +/************************************************************************/ +/* Load() */ +/************************************************************************/ + +void CPCIDSKGeoref::Load() + +{ + if( loaded ) + return; + + // TODO: this should likely be protected by a mutex. + +/* -------------------------------------------------------------------- */ +/* Load the segment contents into a buffer. */ +/* -------------------------------------------------------------------- */ + seg_data.SetSize( (int) (data_size - 1024) ); + + ReadFromFile( seg_data.buffer, 0, data_size - 1024 ); + +/* -------------------------------------------------------------------- */ +/* Handle simple case of a POLYNOMIAL. */ +/* -------------------------------------------------------------------- */ + if( strncmp(seg_data.buffer,"POLYNOMIAL",10) == 0 ) + { + seg_data.Get(32,16,geosys); + + if( seg_data.GetInt(48,8) != 3 || seg_data.GetInt(56,8) != 3 ) + ThrowPCIDSKException( "Unexpected number of coefficients in POLYNOMIAL GEO segment." ); + + a1 = seg_data.GetDouble(212+26*0,26); + a2 = seg_data.GetDouble(212+26*1,26); + xrot = seg_data.GetDouble(212+26*2,26); + + b1 = seg_data.GetDouble(1642+26*0,26); + yrot = seg_data.GetDouble(1642+26*1,26); + b3 = seg_data.GetDouble(1642+26*2,26); + } + +/* -------------------------------------------------------------------- */ +/* Handle the case of a PROJECTION segment - for now we ignore */ +/* the actual projection parameters. */ +/* -------------------------------------------------------------------- */ + else if( strncmp(seg_data.buffer,"PROJECTION",10) == 0 ) + { + seg_data.Get(32,16,geosys); + + if( seg_data.GetInt(48,8) != 3 || seg_data.GetInt(56,8) != 3 ) + ThrowPCIDSKException( "Unexpected number of coefficients in POLYNOMIAL GEO segment." ); + + a1 = seg_data.GetDouble(1980+26*0,26); + a2 = seg_data.GetDouble(1980+26*1,26); + xrot = seg_data.GetDouble(1980+26*2,26); + + b1 = seg_data.GetDouble(2526+26*0,26); + yrot = seg_data.GetDouble(2526+26*1,26); + b3 = seg_data.GetDouble(2526+26*2,26); + } + +/* -------------------------------------------------------------------- */ +/* Blank segment, just created and we just initialize things a bit.*/ +/* -------------------------------------------------------------------- */ + else if( memcmp(seg_data.buffer, + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16) == 0 ) + { + geosys = ""; + + a1 = 0.0; + a2 = 1.0; + xrot = 0.0; + b1 = 0.0; + yrot = 0.0; + b3 = 1.0; + } + + else + { + ThrowPCIDSKException( "Unexpected GEO segment type: %s", + seg_data.Get(0,16) ); + } + + loaded = true; +} + +/************************************************************************/ +/* GetGeosys() */ +/************************************************************************/ + +std::string CPCIDSKGeoref::GetGeosys() + +{ + Load(); + return geosys; +} + +/************************************************************************/ +/* GetTransform() */ +/************************************************************************/ + +void CPCIDSKGeoref::GetTransform( double &a1, double &a2, double &xrot, + double &b1, double &yrot, double &b3 ) + +{ + Load(); + + a1 = this->a1; + a2 = this->a2; + xrot = this->xrot; + b1 = this->b1; + yrot = this->yrot; + b3 = this->b3; +} + +/************************************************************************/ +/* GetParameters() */ +/************************************************************************/ + +std::vector CPCIDSKGeoref::GetParameters() + +{ + unsigned int i; + std::vector parms; + + Load(); + + parms.resize(18); + + if( strncmp(seg_data.buffer,"PROJECTION",10) != 0 ) + { + for( i = 0; i < 17; i++ ) + parms[i] = 0.0; + parms[17] = -1.0; + } + else + { + for( i = 0; i < 17; i++ ) + parms[i] = seg_data.GetDouble(80+26*i,26); + + std::string grid_units; + seg_data.Get(64,16,grid_units); + + if( EQUALN(grid_units.c_str(),"DEGREE",3) ) + parms[17] = (double) (int) UNIT_DEGREE; + else if( EQUALN(grid_units.c_str(),"MET",3) ) + parms[17] = (double) (int) UNIT_METER; + else if( EQUALN(grid_units.c_str(),"FOOT",4) ) + parms[17] = (double) (int) UNIT_US_FOOT; + else if( EQUALN(grid_units.c_str(),"FEET",4) ) + parms[17] = (double) (int) UNIT_US_FOOT; + else if( EQUALN(grid_units.c_str(),"INTL FOOT",5) ) + parms[17] = (double) (int) UNIT_INTL_FOOT; + else + parms[17] = -1.0; /* unknown */ + } + + return parms; +} + +/************************************************************************/ +/* WriteSimple() */ +/************************************************************************/ + +void CPCIDSKGeoref::WriteSimple( std::string const& geosys, + double a1, double a2, double xrot, + double b1, double yrot, double b3 ) + +{ + Load(); + + std::string geosys_clean(ReformatGeosys( geosys )); + +/* -------------------------------------------------------------------- */ +/* Establish the appropriate units code when possible. */ +/* -------------------------------------------------------------------- */ + std::string units_code = "METER"; + + if( EQUALN(geosys_clean.c_str(),"FOOT",4) ) + units_code = "FOOT"; + else if( EQUALN(geosys_clean.c_str(),"SPAF",4) ) + units_code = "FOOT"; + else if( EQUALN(geosys_clean.c_str(),"SPIF",4) ) + units_code = "INTL FOOT"; + else if( EQUALN(geosys_clean.c_str(),"LONG",4) ) + units_code = "DEEGREE"; + +/* -------------------------------------------------------------------- */ +/* Write a fairly simple PROJECTION segment. */ +/* -------------------------------------------------------------------- */ + seg_data.SetSize( 6 * 512 ); + + seg_data.Put( " ", 0, seg_data.buffer_size ); + + // SD.PRO.P1 + seg_data.Put( "PROJECTION", 0, 16 ); + + // SD.PRO.P2 + seg_data.Put( "PIXEL", 16, 16 ); + + // SD.PRO.P3 + seg_data.Put( geosys_clean.c_str(), 32, 16 ); + + // SD.PRO.P4 + seg_data.Put( 3, 48, 8 ); + + // SD.PRO.P5 + seg_data.Put( 3, 56, 8 ); + + // SD.PRO.P6 + seg_data.Put( units_code.c_str(), 64, 16 ); + + // SD.PRO.P7 - P22 + for( int i = 0; i < 17; i++ ) + seg_data.Put( 0.0, 80 + i*26, 26, "%26.18E" ); + + // SD.PRO.P24 + PrepareGCTPFields(); + + // SD.PRO.P26 + seg_data.Put( a1, 1980 + 0*26, 26, "%26.18E" ); + seg_data.Put( a2, 1980 + 1*26, 26, "%26.18E" ); + seg_data.Put( xrot,1980 + 2*26, 26, "%26.18E" ); + + // SD.PRO.P27 + seg_data.Put( b1, 2526 + 0*26, 26, "%26.18E" ); + seg_data.Put( yrot, 2526 + 1*26, 26, "%26.18E" ); + seg_data.Put( b3, 2526 + 2*26, 26, "%26.18E" ); + + WriteToFile( seg_data.buffer, 0, seg_data.buffer_size ); + + loaded = false; +} + +/************************************************************************/ +/* WriteParameters() */ +/************************************************************************/ + +void CPCIDSKGeoref::WriteParameters( std::vector const& parms ) + +{ + Load(); + + if( parms.size() < 17 ) + ThrowPCIDSKException( "Did not get expected number of parameters in WriteParameters()" ); + + unsigned int i; + + for( i = 0; i < 17; i++ ) + seg_data.Put(parms[i],80+26*i,26,"%26.16f"); + + if( parms.size() >= 18 ) + { + switch( (UnitCode) (int) parms[17] ) + { + case UNIT_DEGREE: + seg_data.Put( "DEGREE", 64, 16 ); + break; + + case UNIT_METER: + seg_data.Put( "METER", 64, 16 ); + break; + + case UNIT_US_FOOT: + seg_data.Put( "FOOT", 64, 16 ); + break; + + case UNIT_INTL_FOOT: + seg_data.Put( "INTL FOOT", 64, 16 ); + break; + } + } + + PrepareGCTPFields(); + + WriteToFile( seg_data.buffer, 0, seg_data.buffer_size ); + + // no need to mark loaded false, since we don't cache these parameters. +} + +/************************************************************************/ +/* GetUSGSParameters() */ +/************************************************************************/ + +std::vector CPCIDSKGeoref::GetUSGSParameters() + +{ + unsigned int i; + std::vector parms; + + Load(); + + parms.resize(19); + if( strncmp(seg_data.buffer,"PROJECTION",10) != 0 ) + { + for( i = 0; i < 19; i++ ) + parms[i] = 0.0; + } + else + { + for( i = 0; i < 19; i++ ) + parms[i] = seg_data.GetDouble(1458+26*i,26); + } + + return parms; +} + +/************************************************************************/ +/* ReformatGeosys() */ +/* */ +/* Put a geosys string into standard form. Similar to what the */ +/* DecodeGeosys() function in the PCI SDK does. */ +/************************************************************************/ + +std::string CPCIDSKGeoref::ReformatGeosys( std::string const& geosys ) + +{ +/* -------------------------------------------------------------------- */ +/* Put into a local buffer and pad out to 16 characters with */ +/* spaces. */ +/* -------------------------------------------------------------------- */ + char local_buf[33]; + + strncpy( local_buf, geosys.c_str(), 16 ); + local_buf[16] = '\0'; + strcat( local_buf, " " ); + local_buf[16] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Extract the earth model from the geosys string. */ +/* -------------------------------------------------------------------- */ + char earthmodel[5]; + const char *cp; + int i; + char last; + + cp = local_buf; + while( cp < local_buf + 16 && cp[1] != '\0' ) + cp++; + + while( cp > local_buf && isspace(*cp) ) + cp--; + + last = '\0'; + while( cp > local_buf + && (isdigit((unsigned char)*cp) + || *cp == '-' || *cp == '+' ) ) + { + if( last == '\0' ) last = *cp; + cp--; + } + + if( isdigit( (unsigned char)last ) && + ( *cp == 'D' || *cp == 'd' || + *cp == 'E' || *cp == 'e' ) ) + { + i = atoi( cp+1 ); + if( i > -100 && i < 1000 + && (cp == local_buf + || ( cp > local_buf && isspace( *(cp-1) ) ) + ) + ) + { + if( *cp == 'D' || *cp == 'd' ) + sprintf( earthmodel, "D%03d", i ); + else + sprintf( earthmodel, "E%03d", i ); + } + else + { + sprintf( earthmodel, " " ); + } + } + else + { + sprintf( earthmodel, " " ); + } + +/* -------------------------------------------------------------------- */ +/* Identify by geosys string. */ +/* -------------------------------------------------------------------- */ + const char *ptr; + int zone, ups_zone; + char zone_code = ' '; + + if( EQUALN(local_buf,"PIX",3) ) + { + strcpy( local_buf, "PIXEL " ); + } + else if( EQUALN(local_buf,"UTM",3) ) + { + /* Attempt to find a zone and ellipsoid */ + for( ptr=local_buf+3; isspace(*ptr); ptr++ ) {} + if( isdigit( (unsigned char)*ptr ) || *ptr == '-' ) + { + zone = atoi(ptr); + for( ; isdigit((unsigned char)*ptr) || *ptr == '-'; ptr++ ) {} + for( ; isspace(*ptr); ptr++ ) {} + if( isalpha(*ptr) + && !isdigit((unsigned char)*(ptr+1)) + && ptr[1] != '-' ) + zone_code = *(ptr++); + } + else + zone = -100; + + if( zone >= -60 && zone <= 60 && zone != 0 ) + { + if( zone_code >= 'a' && zone_code <= 'z' ) + zone_code = zone_code - 'a' + 'A'; + + if( zone_code == ' ' && zone < 0 ) + zone_code = 'C'; + + zone = ABS(zone); + + sprintf( local_buf, + "UTM %3d %c %4s", + zone, zone_code, earthmodel ); + } + else + { + sprintf( local_buf, + "UTM %4s", + earthmodel ); + } + if( local_buf[14] == ' ' ) + local_buf[14] = '0'; + if( local_buf[13] == ' ' ) + local_buf[13] = '0'; + } + else if( EQUALN(local_buf,"MET",3) ) + { + sprintf( local_buf, "METRE %4s", earthmodel ); + } + else if( EQUALN(local_buf,"FEET",4) || EQUALN(local_buf,"FOOT",4)) + { + sprintf( local_buf, "FOOT %4s", earthmodel ); + } + else if( EQUALN(local_buf,"LAT",3) || + EQUALN(local_buf,"LON",3) ) + { + sprintf( local_buf, + "LONG/LAT %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"SPCS ",5) || + EQUALN(local_buf,"SPAF ",5) || + EQUALN(local_buf,"SPIF ",5) ) + { + int nSPZone = 0; + + for( ptr=local_buf+4; isspace(*ptr); ptr++ ) {} + nSPZone = atoi(ptr); + + if ( EQUALN(local_buf,"SPCS ",5) ) + strcpy( local_buf, "SPCS " ); + else if ( EQUALN(local_buf,"SPAF ",5) ) + strcpy( local_buf, "SPAF " ); + else + strcpy( local_buf, "SPIF " ); + + if( nSPZone != 0 ) + sprintf( local_buf + 5, "%4d %4s",nSPZone,earthmodel); + else + sprintf( local_buf + 5, " %4s",earthmodel); + + } + else if( EQUALN(local_buf,"ACEA ",5) ) + { + sprintf( local_buf, + "ACEA %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"AE ",3) ) + { + sprintf( local_buf, + "AE %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"EC ",3) ) + { + sprintf( local_buf, + "EC %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"ER ",3) ) + { + sprintf( local_buf, + "ER %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"GNO ",4) ) + { + sprintf( local_buf, + "GNO %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"GVNP",4) ) + { + sprintf( local_buf, + "GVNP %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"LAEA_ELL",8) ) + { + sprintf( local_buf, + "LAEA_ELL %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"LAEA",4) ) + { + sprintf( local_buf, + "LAEA %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"LCC_1SP",7) ) + { + sprintf( local_buf, + "LCC_1SP %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"LCC ",4) ) + { + sprintf( local_buf, + "LCC %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"MC ",3) ) + { + sprintf( local_buf, + "MC %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"MER ",4) ) + { + sprintf( local_buf, + "MER %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"MSC ",4) ) + { + sprintf( local_buf, + "MSC %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"OG ",3) ) + { + sprintf( local_buf, + "OG %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"OM ",3) ) + { + sprintf( local_buf, + "OM %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"PC ",3) ) + { + sprintf( local_buf, + "PC %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"PS ",3) ) + { + sprintf( local_buf, + "PS %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"ROB ",4) ) + { + sprintf( local_buf, + "ROB %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"SG ",3) ) + { + sprintf( local_buf, + "SG %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"SIN ",4) ) + { + sprintf( local_buf, + "SIN %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"SOM ",4) ) + { + sprintf( local_buf, + "SOM %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"TM ",3) ) + { + sprintf( local_buf, + "TM %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"VDG ",4) ) + { + sprintf( local_buf, + "VDG %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"UPSA",4) ) + { + sprintf( local_buf, + "UPSA %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"UPS ",4) ) + { + /* Attempt to find UPS zone */ + for( ptr=local_buf+3; isspace(*ptr); ptr++ ) {} + if( *ptr == 'A' || *ptr == 'B' || *ptr == 'Y' || *ptr == 'Z' ) + ups_zone = *ptr; + else if( *ptr == 'a' || *ptr == 'b' || *ptr == 'y' || *ptr == 'z' ) + ups_zone = toupper( *ptr ); + else + ups_zone = ' '; + + sprintf( local_buf, + "UPS %c %4s", + ups_zone, earthmodel ); + } + else if( EQUALN(local_buf,"GOOD",4) ) + { + sprintf( local_buf, + "GOOD %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"NZMG",4) ) + { + sprintf( local_buf, + "NZMG %4s", + earthmodel ); + } + else if( EQUALN(local_buf,"CASS",4) ) + { + if( EQUALN( earthmodel, "D000", 4 ) ) + sprintf( local_buf, "CASS %4s", "E010" ); + else + sprintf( local_buf, "CASS %4s", earthmodel ); + } + else if( EQUALN(local_buf,"RSO ",4) ) + { + if( EQUALN( earthmodel, "D000", 4 ) ) + sprintf( local_buf, "RSO %4s", "E010" ); + else + sprintf( local_buf, "RSO %4s", earthmodel ); + } + else if( EQUALN(local_buf,"KROV",4) ) + { + if( EQUALN( earthmodel, "D000", 4 ) ) + sprintf( local_buf, "KROV %4s", "E002" ); + else + sprintf( local_buf, "KROV %4s", earthmodel ); + } + else if( EQUALN(local_buf,"KRON",4) ) + { + if( EQUALN( earthmodel, "D000", 4 ) ) + sprintf( local_buf, "KRON %4s", "E002" ); + else + sprintf( local_buf, "KRON %4s", earthmodel ); + } + else if( EQUALN(local_buf,"SGDO",4) ) + { + if( EQUALN( earthmodel, "D000", 4 ) ) + sprintf( local_buf, "SGDO %4s", "E910" ); + else + sprintf( local_buf, "SGDO %4s", earthmodel ); + } + else if( EQUALN(local_buf,"LBSG",4) ) + { + if( EQUALN( earthmodel, "D000", 4 ) ) + sprintf( local_buf, "LBSG %4s", "E202" ); + else + sprintf( local_buf, "LBSG %4s", earthmodel ); + } + else if( EQUALN(local_buf,"ISIN",4) ) + { + if( EQUALN( earthmodel, "D000", 4 ) ) + sprintf( local_buf, "ISIN %4s", "E700" ); + else + sprintf( local_buf, "ISIN %4s", earthmodel ); + } +/* -------------------------------------------------------------------- */ +/* This may be a user projection. Just reformat the earth model */ +/* portion. */ +/* -------------------------------------------------------------------- */ + else + { + sprintf( local_buf, "%-11.11s %4s", geosys.c_str(), earthmodel ); + } + + return local_buf; +} + +/* +C PAK2PCI converts a Latitude or Longitude value held in decimal +C form to or from the standard packed DMS format DDDMMMSSS.SSS. +C The standard packed DMS format is the required format for any +C Latitude or Longitude value in the projection parameter array +C (TPARIN and/or TPARIO) in calling the U.S.G.S. GCTP package, +C but is not required for the actual coordinates to be converted. +C This routine has been coverted from the PAKPCI fortran routine. +C +C When function is 1, the value returned is made up as follows: +C +C PACK2PCI = (DDD * 1000000) + (MMM * 1000) + SSS.SSS +C +C When function is 0, the value returned is made up as follows: +C +C PACK2PCI = DDD + MMM/60 + SSS/3600 +C +C where: DDD are the degrees +C MMM are the minutes +C SSS.SSS are the seconds +C +C The sign of the input value is retained and will denote the +C hemisphere (For longitude, (-) is West and (+) is East of +C Greenwich; For latitude, (-) is South and (+) is North of +C the equator). +C +C +C CALL SEQUENCE +C +C double = PACK2PCI (degrees, function) +C +C degrees - (double) Latitude or Longitude value in decimal +C degrees. +C +C function - (Int) Function to perform +C 1, convert decimal degrees to DDDMMMSSS.SSS +C 0, convert DDDMMMSSS.SSS to decimal degrees +C +C +C EXAMPLE +C +C double degrees, packed, unpack +C +C degrees = -125.425 ! Same as 125d 25' 30" W +C packed = PACK2PCI (degrees, 1) ! PACKED will equal -125025030.000 +C unpack = PACK2PCI (degrees, 0) ! UNPACK will equal -125.425 +*/ + +/************************************************************************/ +/* PAK2PCI() */ +/************************************************************************/ + +static double PAK2PCI( double deg, int function ) +{ + double new_deg; + int sign; + double result; + + double degrees; + double temp1, temp2, temp3; + int dd, mm; + double ss; + + sign = (int)(1.0); + degrees = deg; + + if ( degrees < 0 ) + { + sign = (int)(-1.0); + degrees = degrees * sign; + } + +/* -------------------------------------------------------------------- */ +/* Unpack the value. */ +/* -------------------------------------------------------------------- */ + if ( function == 0 ) + { + new_deg = (double) ABS( degrees ); + + dd = (int)( new_deg / 1000000.0); + + new_deg = ( new_deg - (dd * 1000000) ); + mm = (int)(new_deg/(1000)); + + new_deg = ( new_deg - (mm * 1000) ); + + ss = new_deg; + + result = (double) sign * ( dd + mm/60.0 + ss/3600.0 ); + } + else + { +/* -------------------------------------------------------------------- */ +/* Procduce DDDMMMSSS.SSS from decimal degrees. */ +/* -------------------------------------------------------------------- */ + new_deg = (double) ((int)degrees % 360); + temp1 = degrees - new_deg; + + temp2 = temp1 * 60; + + mm = (int)((temp2 * 60) / 60); + + temp3 = temp2 - mm; + ss = temp3 * 60; + + result = (double) sign * + ( (new_deg * 1000000) + (mm * 1000) + ss); + } + return result; +} + +/************************************************************************/ +/* PrepareGCTPFields() */ +/* */ +/* Fill the GCTP fields in the seg_data image based on the */ +/* non-GCTP values. */ +/************************************************************************/ + +void CPCIDSKGeoref::PrepareGCTPFields() + +{ + enum GCTP_UNIT_CODES { + GCTP_UNIT_UNKNOWN = -1, /* Default, NOT a valid code */ + GCTP_UNIT_RADIAN = 0, /* 0, NOT used at present */ + GCTP_UNIT_US_FOOT, /* 1, Used for GEO_SPAF */ + GCTP_UNIT_METRE, /* 2, Used for most map projections */ + GCTP_UNIT_SECOND, /* 3, NOT used at present */ + GCTP_UNIT_DEGREE, /* 4, Used for GEO_LONG */ + GCTP_UNIT_INTL_FOOT, /* 5, Used for GEO_SPIF */ + GCTP_UNIT_TABLE /* 6, NOT used at present */ + }; + + seg_data.Get(32,16,geosys); + std::string geosys_clean(ReformatGeosys( geosys )); + +/* -------------------------------------------------------------------- */ +/* Establish the GCTP units code. */ +/* -------------------------------------------------------------------- */ + double IOmultiply = 1.0; + int UnitsCode = GCTP_UNIT_METRE; + + std::string grid_units; + seg_data.Get(64,16,grid_units); + + if( EQUALN(grid_units.c_str(),"MET",3) ) + UnitsCode = GCTP_UNIT_METRE; + else if( EQUALN( grid_units.c_str(), "FOOT", 4 ) ) + { + UnitsCode = GCTP_UNIT_US_FOOT; + IOmultiply = 1.0 / 0.3048006096012192; + } + else if( EQUALN( grid_units.c_str(), "INTL FOOT", 9 ) ) + { + UnitsCode = GCTP_UNIT_INTL_FOOT; + IOmultiply = 1.0 / 0.3048; + } + else if( EQUALN( grid_units.c_str(), "DEGREE", 6 ) ) + UnitsCode = GCTP_UNIT_DEGREE; + +/* -------------------------------------------------------------------- */ +/* Extract the non-GCTP style parameters. */ +/* -------------------------------------------------------------------- */ + double pci_parms[17]; + int i; + + for( i = 0; i < 17; i++ ) + pci_parms[i] = seg_data.GetDouble(80+26*i,26); + +#define Dearth0 pci_parms[0] +#define Dearth1 pci_parms[1] +#define RefLong pci_parms[2] +#define RefLat pci_parms[3] +#define StdParallel1 pci_parms[4] +#define StdParallel2 pci_parms[5] +#define FalseEasting pci_parms[6] +#define FalseNorthing pci_parms[7] +#define Scale pci_parms[8] +#define Height pci_parms[9] +#define Long1 pci_parms[10] +#define Lat1 pci_parms[11] +#define Long2 pci_parms[12] +#define Lat2 pci_parms[13] +#define Azimuth pci_parms[14] +#define LandsatNum pci_parms[15] +#define LandsatPath pci_parms[16] + +/* -------------------------------------------------------------------- */ +/* Get the zone code. */ +/* -------------------------------------------------------------------- */ + int ProjectionZone = 0; + + if( strncmp(geosys_clean.c_str(),"UTM ",4) == 0 + || strncmp(geosys_clean.c_str(),"SPCS ",5) == 0 + || strncmp(geosys_clean.c_str(),"SPAF ",5) == 0 + || strncmp(geosys_clean.c_str(),"SPIF ",5) == 0 ) + { + ProjectionZone = atoi(geosys_clean.c_str() + 5); + } + +/* -------------------------------------------------------------------- */ +/* Handle the ellipsoid. We depend on applications properly */ +/* setting proj_parms[0], and proj_parms[1] with the semi-major */ +/* and semi-minor axes in all other cases. */ +/* */ +/* I wish we could lookup datum codes to find their GCTP */ +/* ellipsoid values here! */ +/* -------------------------------------------------------------------- */ + int Spheroid = -1; + if( geosys_clean[12] == 'E' ) + Spheroid = atoi(geosys_clean.c_str() + 13); + + if( Spheroid < 0 || Spheroid > 19 ) + Spheroid = -1; + +/* -------------------------------------------------------------------- */ +/* Initialize the USGS Parameters. */ +/* -------------------------------------------------------------------- */ + double USGSParms[15]; + int gsys; + + for ( i = 0; i < 15; i++ ) + USGSParms[i] = 0; + +/* -------------------------------------------------------------------- */ +/* Projection 0: Geographic (no projection) */ +/* -------------------------------------------------------------------- */ + if( strncmp(geosys_clean.c_str(),"LON",3) == 0 + || strncmp(geosys_clean.c_str(),"LAT",3) == 0 ) + { + gsys = 0; + UnitsCode = GCTP_UNIT_DEGREE; + } + +/* -------------------------------------------------------------------- */ +/* Projection 1: Universal Transverse Mercator */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"UTM ",4) == 0 ) + { + char row_char = geosys_clean[10]; + gsys = 1; + + // Southern hemisphere? + if( (row_char >= 'C') && (row_char <= 'M') && ProjectionZone > 0 ) + { + ProjectionZone *= -1; + } + +/* -------------------------------------------------------------------- */ +/* Process UTM as TM. The reason for this is the GCTP software */ +/* does not provide for input of an Earth Model for UTM, but does */ +/* for TM. */ +/* -------------------------------------------------------------------- */ + gsys = 9; + USGSParms[0] = Dearth0; + USGSParms[1] = Dearth1; + USGSParms[2] = 0.9996; + + USGSParms[4] = PAK2PCI( + ( ABS(ProjectionZone) * 6.0 - 183.0 ), 1 ); + USGSParms[5] = PAK2PCI( 0.0, 1 ); + USGSParms[6] = 500000.0; + USGSParms[7] = ( ProjectionZone < 0 ) ? 10000000.0 : 0.0; + } + +/* -------------------------------------------------------------------- */ +/* Projection 2: State Plane Coordinate System */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"SPCS ",5) == 0 ) + { + gsys = 2; + if( UnitsCode != GCTP_UNIT_METRE + && UnitsCode != GCTP_UNIT_US_FOOT + && UnitsCode != GCTP_UNIT_INTL_FOOT ) + UnitsCode = GCTP_UNIT_METRE; + } + + else if( strncmp(geosys_clean.c_str(),"SPAF ",5) == 0 ) + { + gsys = 2; + if( UnitsCode != GCTP_UNIT_METRE + && UnitsCode != GCTP_UNIT_US_FOOT + && UnitsCode != GCTP_UNIT_INTL_FOOT ) + UnitsCode = GCTP_UNIT_US_FOOT; + } + + else if( strncmp(geosys_clean.c_str(),"SPIF ",5) == 0 ) + { + gsys = 2; + if( UnitsCode != GCTP_UNIT_METRE + && UnitsCode != GCTP_UNIT_US_FOOT + && UnitsCode != GCTP_UNIT_INTL_FOOT ) + UnitsCode = GCTP_UNIT_INTL_FOOT; + } + +/* -------------------------------------------------------------------- */ +/* Projection 3: Albers Conical Equal-Area */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"ACEA ",5) == 0 ) + { + gsys = 3; + USGSParms[0] = Dearth0; + USGSParms[1] = Dearth1; + USGSParms[2] = PAK2PCI(StdParallel1, 1); + USGSParms[3] = PAK2PCI(StdParallel2, 1); + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 4: Lambert Conformal Conic */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"LCC ",5) == 0 ) + { + gsys = 4; + USGSParms[0] = Dearth0; + USGSParms[1] = Dearth1; + USGSParms[2] = PAK2PCI(StdParallel1, 1); + USGSParms[3] = PAK2PCI(StdParallel2, 1); + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 5: Mercator */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"MER ",5) == 0 ) + { + gsys = 5; + USGSParms[0] = Dearth0; + USGSParms[1] = Dearth1; + + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 6: Polar Stereographic */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"PS ",5) == 0 ) + { + gsys = 6; + USGSParms[0] = Dearth0; + USGSParms[1] = Dearth1; + + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 7: Polyconic */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"PC ",5) == 0 ) + { + gsys = 7; + USGSParms[0] = Dearth0; + USGSParms[1] = Dearth1; + + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 8: Equidistant Conic */ +/* Format A, one standard parallel, usgs_params[8] = 0 */ +/* Format B, two standard parallels, usgs_params[8] = not 0 */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"EC ",5) == 0 ) + { + gsys = 8; + USGSParms[0] = Dearth0; + USGSParms[1] = Dearth1; + USGSParms[2] = PAK2PCI(StdParallel1, 1); + USGSParms[3] = PAK2PCI(StdParallel2, 1); + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + + if ( StdParallel2 != 0 ) + { + USGSParms[8] = 1; + } + } + +/* -------------------------------------------------------------------- */ +/* Projection 9: Transverse Mercator */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"TM ",5) == 0 ) + { + gsys = 9; + USGSParms[0] = Dearth0; + USGSParms[1] = Dearth1; + USGSParms[2] = Scale; + + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 10: Stereographic */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"SG ",5) == 0 ) + { + gsys = 10; + USGSParms[0] = Dearth0; + + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 11: Lambert Azimuthal Equal-Area */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"LAEA ",5) == 0 ) + { + gsys = 11; + + USGSParms[0] = Dearth0; + + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 12: Azimuthal Equidistant */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"AE ",5) == 0 ) + { + gsys = 12; + USGSParms[0] = Dearth0; + + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 13: Gnomonic */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"GNO ",5) == 0 ) + { + gsys = 13; + USGSParms[0] = Dearth0; + + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 14: Orthographic */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"OG ",5) == 0 ) + { + gsys = 14; + USGSParms[0] = Dearth0; + + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 15: General Vertical Near-Side Perspective */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"GVNP ",5) == 0 ) + { + gsys = 15; + USGSParms[0] = Dearth0; + + USGSParms[2] = Height; + + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 16: Sinusoidal */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"SIN ",5) == 0 ) + { + gsys = 16; + USGSParms[0] = Dearth0; + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 17: Equirectangular */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"ER ",5) == 0 ) + { + gsys = 17; + USGSParms[0] = Dearth0; + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } +/* -------------------------------------------------------------------- */ +/* Projection 18: Miller Cylindrical */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"MC ",5) == 0 ) + { + gsys = 18; + USGSParms[0] = Dearth0; + + USGSParms[4] = PAK2PCI(RefLong, 1); + + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 19: Van der Grinten */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"VDG ",5) == 0 ) + { + gsys = 19; + USGSParms[0] = Dearth0; + + USGSParms[4] = PAK2PCI(RefLong, 1); + + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 20: Oblique Mercator (Hotine) */ +/* Format A, Azimuth and RefLong defined (Long1, Lat1, */ +/* Long2, Lat2 not defined), usgs_params[12] = 0 */ +/* Format B, Long1, Lat1, Long2, Lat2 defined (Azimuth */ +/* and RefLong not defined), usgs_params[12] = not 0 */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"OM ",5) == 0 ) + { + gsys = 20; + USGSParms[0] = Dearth0; + USGSParms[1] = Dearth1; + USGSParms[2] = Scale; + USGSParms[3] = PAK2PCI(Azimuth ,1); + + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting * IOmultiply; + USGSParms[7] = FalseNorthing * IOmultiply; + + USGSParms[8] = PAK2PCI(Long1, 1); + USGSParms[9] = PAK2PCI(Lat1, 1); + USGSParms[10] = PAK2PCI(Long2, 1); + USGSParms[11] = PAK2PCI(Lat2, 1); + if ( (Long1 != 0) || (Lat1 != 0) || + (Long2 != 0) || (Lat2 != 0) ) + USGSParms[12] = 0.0; + else + USGSParms[12] = 1.0; + } +/* -------------------------------------------------------------------- */ +/* Projection 21: Robinson */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"ROB ",5) == 0 ) + { + gsys = 21; + USGSParms[0] = Dearth0; + + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[6] = FalseEasting + * IOmultiply; + USGSParms[7] = FalseNorthing + * IOmultiply; + + } +/* -------------------------------------------------------------------- */ +/* Projection 22: Space Oblique Mercator */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"SOM ",5) == 0 ) + { + gsys = 22; + USGSParms[0] = Dearth0; + USGSParms[1] = Dearth1; + USGSParms[2] = LandsatNum; + USGSParms[3] = LandsatPath; + USGSParms[6] = FalseEasting + * IOmultiply; + USGSParms[7] = FalseNorthing + * IOmultiply; + } +/* -------------------------------------------------------------------- */ +/* Projection 23: Modified Stereographic Conformal (Alaska) */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"MSC ",5) == 0 ) + { + gsys = 23; + USGSParms[0] = Dearth0; + USGSParms[1] = Dearth1; + USGSParms[6] = FalseEasting + * IOmultiply; + USGSParms[7] = FalseNorthing + * IOmultiply; + } + +/* -------------------------------------------------------------------- */ +/* Projection 6: Universal Polar Stereographic is just Polar */ +/* Stereographic with certain assumptions. */ +/* -------------------------------------------------------------------- */ + else if( strncmp(geosys_clean.c_str(),"UPS ",5) == 0 ) + { + gsys = 6; + + USGSParms[0] = Dearth0; + USGSParms[1] = Dearth1; + + USGSParms[4] = PAK2PCI(0.0, 1); + + USGSParms[6] = 2000000.0; + USGSParms[7] = 2000000.0; + + double dwork = 81.0 + 6.0/60.0 + 52.3/3600.0; + + if( geosys_clean[10] == 'A' || geosys_clean[10] == 'B' ) + { + USGSParms[5] = PAK2PCI(-dwork,1); + } + else if( geosys_clean[10] == 'Y' || geosys_clean[10]=='Z') + { + USGSParms[5] = PAK2PCI(dwork,1); + } + else + { + USGSParms[4] = PAK2PCI(RefLong, 1); + USGSParms[5] = PAK2PCI(RefLat, 1); + USGSParms[6] = FalseEasting + * IOmultiply; + USGSParms[7] = FalseNorthing + * IOmultiply; + } + } + +/* -------------------------------------------------------------------- */ +/* Unknown code. */ +/* -------------------------------------------------------------------- */ + else + { + gsys = -1; + } + + if( ProjectionZone == 0 ) + ProjectionZone = 10000 + gsys; + +/* -------------------------------------------------------------------- */ +/* Place USGS values in the formatted segment. */ +/* -------------------------------------------------------------------- */ + seg_data.Put( (double) gsys, 1458 , 26, "%26.18lE" ); + seg_data.Put( (double) ProjectionZone, 1458+26, 26, "%26.18lE" ); + + for( i = 0; i < 15; i++ ) + seg_data.Put( USGSParms[i], 1458+26*(2+i), 26, "%26.18lE" ); + + seg_data.Put( (double) UnitsCode, 1458+26*17, 26, "%26.18lE" ); + seg_data.Put( (double) Spheroid, 1458+26*18, 26, "%26.18lE" ); +} + + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskgeoref.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskgeoref.h new file mode 100644 index 000000000..7607345b4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskgeoref.h @@ -0,0 +1,88 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CPCIDSKGeoref class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_SEGMENT_PCIDSKGEOREF_H +#define __INCLUDE_SEGMENT_PCIDSKGEOREF_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_georef.h" +#include "pcidsk_buffer.h" +#include "segment/cpcidsksegment.h" + +#include + +namespace PCIDSK +{ + class PCIDSKFile; + + /************************************************************************/ + /* CPCIDSKGeoref */ + /************************************************************************/ + + class CPCIDSKGeoref : virtual public CPCIDSKSegment, + public PCIDSKGeoref + { + public: + CPCIDSKGeoref( PCIDSKFile *file, int segment,const char *segment_pointer ); + + virtual ~CPCIDSKGeoref(); + + // PCIDSKSegment + + void Initialize(); + + // PCIDSKGeoref + + void GetTransform( double &a1, double &a2, double &xrot, + double &b1, double &yrot, double &b3 ); + std::string GetGeosys(); + + std::vector GetParameters(); + + void WriteSimple( std::string const& geosys, + double a1, double a2, double xrot, + double b1, double yrot, double b3 ); + void WriteParameters( std::vector const& parameters ); + + // special interface just for testing. + std::vector GetUSGSParameters(); + + private: + bool loaded; + + std::string geosys; + double a1, a2, xrot, b1, yrot, b3; + + void Load(); + void PrepareGCTPFields(); + std::string ReformatGeosys( std::string const& geosys ); + + PCIDSKBuffer seg_data; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_PCIDSKGEOREF_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskpct.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskpct.cpp new file mode 100644 index 000000000..722fff012 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskpct.cpp @@ -0,0 +1,99 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSK_PCT class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_exception.h" +#include "segment/cpcidskpct.h" +#include +#include + +using namespace PCIDSK; + +/************************************************************************/ +/* CPCIDSK_PCT() */ +/************************************************************************/ + +CPCIDSK_PCT::CPCIDSK_PCT( PCIDSKFile *file, int segment, + const char *segment_pointer ) + : CPCIDSKSegment( file, segment, segment_pointer ) + +{ +} + +/************************************************************************/ +/* ~CPCIDSKGeoref() */ +/************************************************************************/ + +CPCIDSK_PCT::~CPCIDSK_PCT() + +{ +} + +/************************************************************************/ +/* ReadPCT() */ +/************************************************************************/ + +void CPCIDSK_PCT::ReadPCT( unsigned char pct[768] ) + +{ + PCIDSKBuffer seg_data; + + seg_data.SetSize( 768*4 ); + + ReadFromFile( seg_data.buffer, 0, 768*4 ); + + int i; + for( i = 0; i < 256; i++ ) + { + pct[ 0+i] = (unsigned char) seg_data.GetInt( 0+i*4, 4 ); + pct[256+i] = (unsigned char) seg_data.GetInt(1024+i*4, 4 ); + pct[512+i] = (unsigned char) seg_data.GetInt(2048+i*4, 4 ); + } +} + +/************************************************************************/ +/* WritePCT() */ +/************************************************************************/ + +void CPCIDSK_PCT::WritePCT( unsigned char pct[768] ) + +{ + PCIDSKBuffer seg_data; + + seg_data.SetSize( 768*4 ); + + ReadFromFile( seg_data.buffer, 0, 768*4 ); + + int i; + for( i = 0; i < 256; i++ ) + { + seg_data.Put( (int) pct[ 0+i], 0+i*4, 4 ); + seg_data.Put( (int) pct[256+i],1024+i*4, 4 ); + seg_data.Put( (int) pct[512+i],2048+i*4, 4 ); + } + + WriteToFile( seg_data.buffer, 0, 768*4 ); +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskpct.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskpct.h new file mode 100644 index 000000000..dec13f394 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskpct.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CPCIDSK_PCT class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_SEGMENT_PCIDSK_PCT_H +#define __INCLUDE_SEGMENT_PCIDSK_PCT_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_pct.h" +#include "pcidsk_buffer.h" +#include "segment/cpcidsksegment.h" + +#include + +namespace PCIDSK +{ + class PCIDSKFile; + + /************************************************************************/ + /* CPCIDSK_PCT */ + /************************************************************************/ + + class CPCIDSK_PCT : virtual public CPCIDSKSegment, + public PCIDSK_PCT + { + public: + CPCIDSK_PCT( PCIDSKFile *file, int segment,const char *segment_pointer); + + virtual ~CPCIDSK_PCT(); + + virtual void ReadPCT( unsigned char pct[768] ); + virtual void WritePCT( unsigned char pct[768] ); + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_PCIDSKGEOREF_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskpolymodel.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskpolymodel.h new file mode 100644 index 000000000..f68230c53 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskpolymodel.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * + * Purpose: Support for reading and manipulating PCIDSK Polynomial Segments + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_SEGMENT_PCIDSKPOLYMODEL_H +#define __INCLUDE_PCIDSK_SEGMENT_PCIDSKPOLYMODEL_H + +#include "pcidsk_poly.h" +#include "segment/cpcidsksegment.h" + +namespace PCIDSK { + class PCIDSKFile; + + class CPCIDSKPolyModelSegment : public PCIDSKPolySegment, + public CPCIDSKSegment + { + public: + CPCIDSKPolyModelSegment(PCIDSKFile *file, int segment,const char *segment_pointer); + ~CPCIDSKPolyModelSegment(); + + std::vector GetXForwardCoefficients() const; + std::vector GetYForwardCoefficients() const; + std::vector GetXBackwardCoefficients() const; + std::vector GetYBackwardCoefficients() const; + + void SetCoefficients(const std::vector& oXForward, + const std::vector& oYForward, + const std::vector& oXBackward, + const std::vector& oYBackward) ; + + unsigned int GetLines() const; + unsigned int GetPixels() const; + void SetRasterSize(unsigned int nLines,unsigned int nPixels) ; + + std::string GetGeosysString() const; + void SetGeosysString(const std::string& oGeosys) ; + + std::vector GetProjParmInfo() const; + void SetProjParmInfo(const std::vector& oInfo) ; + + //synchronize the segment on disk. + void Synchronize(); + private: + // Helper housekeeping functions + void Load(); + void Write(); + + struct PCIDSKPolyInfo; + PCIDSKPolyInfo *pimpl_; + bool loaded_; + bool mbModified; + }; +} + +#endif // __INCLUDE_PCIDSK_SEGMENT_PCIDSKPOLYMODEL_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskrpcmodel.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskrpcmodel.cpp new file mode 100644 index 000000000..9b7f669e7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskrpcmodel.cpp @@ -0,0 +1,680 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSKRPCModelSegment class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_rpc.h" +#include "segment/cpcidsksegment.h" +#include "core/pcidsk_utils.h" +#include "pcidsk_exception.h" +#include "segment/cpcidskrpcmodel.h" + +#include +#include +#include +#include + +using namespace PCIDSK; + +// Struct to store details of the RPC model +struct CPCIDSKRPCModelSegment::PCIDSKRPCInfo +{ + bool userrpc; // whether or not the RPC was generated from GCPs + bool adjusted; // Whether or not the RPC has been adjusted + int downsample; // Epipolar Downsample factor + + unsigned int pixels; // pixels in the image + unsigned int lines; // lines in the image + + unsigned int num_coeffs; // number of coefficientsg + + std::vector pixel_num; // numerator, pixel direction + std::vector pixel_denom; // denominator, pixel direction + std::vector line_num; // numerator, line direction + std::vector line_denom; // denominator, line direction + + // Scale/offset coefficients in the ground domain + double x_off; + double x_scale; + + double y_off; + double y_scale; + + double z_off; + double z_scale; + + // Scale/offset coefficients in the raster domain + double pix_off; + double pix_scale; + + double line_off; + double line_scale; + + std::vector x_adj; // adjusted X values + std::vector y_adj; // adjusted Y values + + std::string sensor_name; // the name of the sensor + + std::string map_units; // the map units string + + // TODO: Projection Info + + // The raw segment data + PCIDSKBuffer seg_data; +}; + +CPCIDSKRPCModelSegment::CPCIDSKRPCModelSegment(PCIDSKFile *file, int segment,const char *segment_pointer) : + CPCIDSKSegment(file, segment, segment_pointer), pimpl_(new CPCIDSKRPCModelSegment::PCIDSKRPCInfo), + loaded_(false),mbModified(false) +{ + Load(); +} + + +CPCIDSKRPCModelSegment::~CPCIDSKRPCModelSegment() +{ + delete pimpl_; +} + +// Load the contents of the segment +void CPCIDSKRPCModelSegment::Load() +{ + // Check if we've already loaded the segment into memory + if (loaded_) { + return; + } + + assert(data_size - 1024 == 7 * 512); + + pimpl_->seg_data.SetSize((int) (data_size - 1024)); // should be 7 * 512 + + ReadFromFile(pimpl_->seg_data.buffer, 0, data_size - 1024); + + // The RPC Model Segment is defined as follows: + // RFMODEL Segment: 7 512-byte blocks + + // Block 1: + // Bytes 0-7: 'RFMODEL ' + // Byte 8: User Provided RPC (1: user-provided, 0: computed from GCPs) + // Bytes 22-23: 'DS' + // Bytes 24-26: Downsample factor used during Epipolar Generation + // Bytes 27-29: '2ND' -- no clue what this means + // Bytes 30-35: 'SENSOR' + // Bytes 36: Sensor Name (NULL terminated) + + if (std::strncmp(pimpl_->seg_data.buffer, "RFMODEL ", 8)) + { + pimpl_->seg_data.Put("RFMODEL",0,8); + pimpl_->userrpc = false; + pimpl_->adjusted = false; + pimpl_->seg_data.Put("DS",22,2); + pimpl_->downsample = 1; + pimpl_->seg_data.Put("SENSOR",30,6); + pimpl_->num_coeffs = 20; + loaded_ = true; + return; + // Something has gone terribly wrong! + /*throw PCIDSKException("A segment that was previously identified as an RFMODEL " + "segment does not contain the appropriate data. Found: [%s]", + std::string(pimpl_->seg_data.buffer, 8).c_str());*/ + } + + // Determine if this is user-provided + pimpl_->userrpc = pimpl_->seg_data.buffer[8] == '1' ? true : false; + + // Check for the DS characters + pimpl_->downsample = 1; + if (!std::strncmp(&pimpl_->seg_data.buffer[22], "DS", 2)) + { + // Read the downsample factor + pimpl_->downsample = pimpl_->seg_data.GetInt(24, 3); + } + + //This is requiered if writting with PCIDSKIO + //and reading with GDBIO (probably because of legacy issue) + // see Bugzilla 255 and 254. + bool bSecond = false; + if (!std::strncmp(&pimpl_->seg_data.buffer[27], "2ND", 3)) + { + bSecond = true; + } + + // Sensor name: + if (!std::strncmp(&pimpl_->seg_data.buffer[30], "SENSOR", 6)) { + pimpl_->sensor_name = std::string(&pimpl_->seg_data.buffer[36]); + } else { + pimpl_->sensor_name = ""; + } + + // Block 2: + // Bytes 0-3: Number of coefficients + // Bytes 4-13: Number of pixels + // Bytes 14-23: Number of lines + // Bytes 24-45: Longitude offset + // Bytes 46-67: Longitude scale + // Bytes 68-89: Latitude Offset + // Bytes 90-111: Latitude Scale + // Bytes 112-133: Height offset + // Bytes 134-155: Height scale + // Bytes 156-177: Sample offset + // Bytes 178-199: Sample scale + // Bytes 200-221: Line offset + // Bytes 222-243: line scale + // Bytes 244-375: Adjusted X coefficients (5 * 22 bytes) + // Bytes 376-507: Adjusted Y coefficients (5 * 22 bytes) + // if bSecond is false, then the coefficient are stored + // at others positions + // every value takes 22 bytes. + + if(bSecond) + { + pimpl_->num_coeffs = pimpl_->seg_data.GetInt(512, 4); + + if (pimpl_->num_coeffs * 22 > 512) { + // this segment is malformed. Throw an exception. + throw PCIDSKException("RFMODEL segment coefficient count requires more " + "than one block to store. There is an error in this segment. The " + "number of coefficients according to the segment is %d.", pimpl_->num_coeffs); + } + + pimpl_->lines = pimpl_->seg_data.GetInt(512 + 4, 10); + pimpl_->pixels = pimpl_->seg_data.GetInt(512 + 14, 10); + pimpl_->x_off = pimpl_->seg_data.GetDouble(512 + 24, 22); + pimpl_->x_scale = pimpl_->seg_data.GetDouble(512 + 46, 22); + pimpl_->y_off = pimpl_->seg_data.GetDouble(512 + 68, 22); + pimpl_->y_scale = pimpl_->seg_data.GetDouble(512 + 90, 22); + pimpl_->z_off = pimpl_->seg_data.GetDouble(512 + 112, 22); + pimpl_->z_scale = pimpl_->seg_data.GetDouble(512 + 134, 22); + pimpl_->pix_off = pimpl_->seg_data.GetDouble(512 + 156, 22); + pimpl_->pix_scale = pimpl_->seg_data.GetDouble(512 + 178, 22); + pimpl_->line_off = pimpl_->seg_data.GetDouble(512 + 200, 22); + pimpl_->line_scale = pimpl_->seg_data.GetDouble(512 + 222, 22); + + pimpl_->adjusted = false; + // Read in adjusted X coefficients + for (unsigned int i = 0; i <= 5; i++) + { + double tmp = pimpl_->seg_data.GetDouble(512 + 244 + (i * 22), 22); + pimpl_->x_adj.push_back(tmp); + if (0.0 != tmp) + { + pimpl_->adjusted = true; + } + } + + // Read in adjusted Y coefficients + for (unsigned int i = 0; i <= 5; i++) + { + double tmp = pimpl_->seg_data.GetDouble(512 + 376 + (i * 22), 22); + pimpl_->y_adj.push_back(tmp); + if (0.0 != tmp) + { + pimpl_->adjusted = true; + } + } + } + else + { + pimpl_->num_coeffs = pimpl_->seg_data.GetInt(512, 22); + + if (pimpl_->num_coeffs * 22 > 512) { + // this segment is malformed. Throw an exception. + throw PCIDSKException("RFMODEL segment coefficient count requires more " + "than one block to store. There is an error in this segment. The " + "number of coefficients according to the segment is %d.", pimpl_->num_coeffs); + } + + pimpl_->lines = pimpl_->seg_data.GetInt(512 + 22, 22); + pimpl_->pixels = pimpl_->seg_data.GetInt(512 + 2*22,22); + pimpl_->x_off = pimpl_->seg_data.GetDouble(512 + 3*22, 22); + pimpl_->x_scale = pimpl_->seg_data.GetDouble(512 + 4*22, 22); + pimpl_->y_off = pimpl_->seg_data.GetDouble(512 + 5*22, 22); + pimpl_->y_scale = pimpl_->seg_data.GetDouble(512 + 6*22, 22); + pimpl_->z_off = pimpl_->seg_data.GetDouble(512 + 7*22, 22); + pimpl_->z_scale = pimpl_->seg_data.GetDouble(512 + 8*22, 22); + pimpl_->pix_off = pimpl_->seg_data.GetDouble(512 + 9*22, 22); + pimpl_->pix_scale = pimpl_->seg_data.GetDouble(512 + 10*22, 22); + pimpl_->line_off = pimpl_->seg_data.GetDouble(512 + 11*22, 22); + pimpl_->line_scale = pimpl_->seg_data.GetDouble(512 + 12*22, 22); + + pimpl_->adjusted = false; + // Read in adjusted X coefficients + for (unsigned int i = 0; i <= 3; i++) + { + double tmp = pimpl_->seg_data.GetDouble(512 + 12*22 + (i * 22), 22); + pimpl_->x_adj.push_back(tmp); + if (0.0 != tmp) + { + pimpl_->adjusted = true; + } + } + pimpl_->x_adj.push_back(0.0); + pimpl_->x_adj.push_back(0.0); + pimpl_->x_adj.push_back(0.0); + + // Read in adjusted Y coefficients + for (unsigned int i = 0; i <= 3; i++) + { + double tmp = pimpl_->seg_data.GetDouble(512 + 16*22 + (i * 22), 22); + pimpl_->y_adj.push_back(tmp); + if (0.0 != tmp) + { + pimpl_->adjusted = true; + } + } + pimpl_->y_adj.push_back(0.0); + pimpl_->y_adj.push_back(0.0); + pimpl_->y_adj.push_back(0.0); + } + + // Block 3: + // Block 3 contains the numerator coefficients for the pixel rational polynomial + // Number of Coefficients * 22 bytes + for (unsigned int i = 0; i < pimpl_->num_coeffs; i++) { + pimpl_->pixel_num.push_back(pimpl_->seg_data.GetDouble(2 * 512 + (i * 22), 22)); + } + + // Block 4: + // Block 4 contains the denominator coefficients for the pixel rational polynomial + // Number of Coefficients * 22 bytes + for (unsigned int i = 0; i < pimpl_->num_coeffs; i++) { + pimpl_->pixel_denom.push_back(pimpl_->seg_data.GetDouble(3 * 512 + (i * 22), 22)); + } + + // Block 5: + // Block 5 contains the numerator coefficients for the line rational polynomial + // Number of Coefficients * 22 bytes + for (unsigned int i = 0; i < pimpl_->num_coeffs; i++) { + pimpl_->line_num.push_back(pimpl_->seg_data.GetDouble(4 * 512 + (i * 22), 22)); + } + + // Block 6: + // Block 6 contains the denominator coefficients for the line rational polynomial + // Number of Coefficients * 22 bytes + for (unsigned int i = 0; i < pimpl_->num_coeffs; i++) { + pimpl_->line_denom.push_back(pimpl_->seg_data.GetDouble(5 * 512 + (i * 22), 22)); + } + + // Block 7: + // Bytes 0-15: MapUnits string + // Bytes 256-511: ProjInfo_t, serialized + pimpl_->map_units = std::string(&pimpl_->seg_data.buffer[6 * 512], 16); + + // We've now loaded the structure up with data. Mark it as being loaded + // properly. + loaded_ = true; + +} + +void CPCIDSKRPCModelSegment::Write(void) +{ + //We are not writing if nothing was loaded. + if (!loaded_) { + return; + } + + // The RPC Model Segment is defined as follows: + // RFMODEL Segment: 7 512-byte blocks + + // Block 1: + // Bytes 0-7: 'RFMODEL ' + // Byte 8: User Provided RPC (1: user-provided, 0: computed from GCPs) + // Bytes 22-23: 'DS' + // Bytes 24-26: Downsample factor used during Epipolar Generation + // Bytes 27-29: '2ND' -- no clue what this means + // Bytes 30-35: 'SENSOR' + // Bytes 36: Sensor Name (NULL terminated) + pimpl_->seg_data.Put("RFMODEL",0,8); + + // Determine if this is user-provided + pimpl_->seg_data.buffer[8] = pimpl_->userrpc ? '1' : '0'; + + // Check for the DS characters + pimpl_->seg_data.Put("DS",22,2); + pimpl_->seg_data.Put(pimpl_->downsample,24,3); + + //This is requiered if writting with PCIDSKIO + //and reading with GDBIO (probably because of legacy issue) + // see Bugzilla 255 and 254. + pimpl_->seg_data.Put("2ND",27,3); + + // Sensor name: + pimpl_->seg_data.Put("SENSOR",30,6); + pimpl_->seg_data.Put(pimpl_->sensor_name.c_str(),36,pimpl_->sensor_name.size()); + + // Block 2: + // Bytes 0-3: Number of coefficients + // Bytes 4-13: Number of pixels + // Bytes 14-23: Number of lines + // Bytes 24-45: Longitude offset + // Bytes 46-67: Longitude scale + // Bytes 68-89: Latitude Offset + // Bytes 90-111: Latitude Scale + // Bytes 112-133: Height offset + // Bytes 134-155: Height scale + // Bytes 156-177: Sample offset + // Bytes 178-199: Sample scale + // Bytes 200-221: Line offset + // Bytes 222-243: line scale + // Bytes 244-375: Adjusted X coefficients (5 * 22 bytes) + // Bytes 376-507: Adjusted Y coefficients (5 * 22 bytes) + + if (pimpl_->num_coeffs * 22 > 512) { + // this segment is malformed. Throw an exception. + throw PCIDSKException("RFMODEL segment coefficient count requires more " + "than one block to store. There is an error in this segment. The " + "number of coefficients according to the segment is %d.", pimpl_->num_coeffs); + } + + pimpl_->seg_data.Put(pimpl_->num_coeffs,512, 4); + + pimpl_->seg_data.Put(pimpl_->lines,512 + 4, 10); + pimpl_->seg_data.Put(pimpl_->pixels,512 + 14, 10); + pimpl_->seg_data.Put(pimpl_->x_off,512 + 24, 22,"%22.14f"); + pimpl_->seg_data.Put(pimpl_->x_scale,512 + 46, 22,"%22.14f"); + pimpl_->seg_data.Put(pimpl_->y_off,512 + 68, 22,"%22.14f"); + pimpl_->seg_data.Put(pimpl_->y_scale,512 + 90, 22,"%22.14f"); + pimpl_->seg_data.Put(pimpl_->z_off,512 + 112, 22,"%22.14f"); + pimpl_->seg_data.Put(pimpl_->z_scale,512 + 134, 22,"%22.14f"); + pimpl_->seg_data.Put(pimpl_->pix_off,512 + 156, 22,"%22.14f"); + pimpl_->seg_data.Put(pimpl_->pix_scale,512 + 178, 22,"%22.14f"); + pimpl_->seg_data.Put(pimpl_->line_off,512 + 200, 22,"%22.14f"); + pimpl_->seg_data.Put(pimpl_->line_scale,512 + 222, 22,"%22.14f"); + + // Read in adjusted X coefficients + for (unsigned int i = 0; i <= 5; i++) + { + pimpl_->seg_data.Put(pimpl_->x_adj[i],512 + 244 + (i * 22), 22,"%22.14f"); + if(pimpl_->x_adj[i] != 0.0) + { + pimpl_->adjusted = true; + } + } + + // Read in adjusted Y coefficients + for (unsigned int i = 0; i <= 5; i++) + { + pimpl_->seg_data.Put(pimpl_->y_adj[i],512 + 376 + (i * 22), 22,"%22.14f"); + if(pimpl_->y_adj[i] != 0.0) + { + pimpl_->adjusted = true; + } + } + + // Block 3: + // Block 3 contains the numerator coefficients for the pixel rational polynomial + // Number of Coefficients * 22 bytes + for (unsigned int i = 0; i < pimpl_->num_coeffs; i++) + { + pimpl_->seg_data.Put(pimpl_->pixel_num[i],2 * 512 + (i * 22), 22,"%22.14f"); + } + + // Block 4: + // Block 4 contains the denominator coefficients for the pixel rational polynomial + // Number of Coefficients * 22 bytes + for (unsigned int i = 0; i < pimpl_->num_coeffs; i++) + { + pimpl_->seg_data.Put(pimpl_->pixel_denom[i],3 * 512 + (i * 22), 22,"%22.14f"); + } + + // Block 5: + // Block 5 contains the numerator coefficients for the line rational polynomial + // Number of Coefficients * 22 bytes + for (unsigned int i = 0; i < pimpl_->num_coeffs; i++) + { + pimpl_->seg_data.Put(pimpl_->line_num[i],4 * 512 + (i * 22), 22,"%22.14f"); + } + + // Block 6: + // Block 6 contains the denominator coefficients for the line rational polynomial + // Number of Coefficients * 22 bytes + for (unsigned int i = 0; i < pimpl_->num_coeffs; i++) + { + pimpl_->seg_data.Put(pimpl_->line_denom[i],5 * 512 + (i * 22), 22,"%22.14f"); + } + + // Block 7: + // Bytes 0-15: MapUnits string + // Bytes 256-511: ProjInfo_t, serialized + pimpl_->seg_data.Put(pimpl_->map_units.c_str(),6 * 512, 16); + + WriteToFile(pimpl_->seg_data.buffer,0,data_size-1024); + mbModified = false; +} + +std::vector CPCIDSKRPCModelSegment::GetXNumerator(void) const +{ + return pimpl_->pixel_num; +} + +std::vector CPCIDSKRPCModelSegment::GetXDenominator(void) const +{ + return pimpl_->pixel_denom; +} + +std::vector CPCIDSKRPCModelSegment::GetYNumerator(void) const +{ + return pimpl_->line_num; +} + +std::vector CPCIDSKRPCModelSegment::GetYDenominator(void) const +{ + return pimpl_->line_denom; +} + +// Set the RPC Coefficients +void CPCIDSKRPCModelSegment::SetCoefficients( + const std::vector& xnum, const std::vector& xdenom, + const std::vector& ynum, const std::vector& ydenom) +{ + if (xnum.size() != xdenom.size() || ynum.size() != ydenom.size() || + xnum.size() != ynum.size() || xdenom.size() != ydenom.size()) { + throw PCIDSKException("All RPC coefficient vectors must be the " + "same size."); + } + + pimpl_->pixel_num = xnum; + pimpl_->pixel_denom = xdenom; + pimpl_->line_num = ynum; + pimpl_->line_denom = ydenom; + mbModified = true; +} + +// Get the RPC offset/scale Coefficients +void CPCIDSKRPCModelSegment::GetRPCTranslationCoeffs(double& xoffset, double& xscale, + double& yoffset, double& yscale, double& zoffset, double& zscale, + double& pixoffset, double& pixscale, double& lineoffset, double& linescale) const +{ + xoffset = pimpl_->x_off; + xscale = pimpl_->x_scale; + + yoffset = pimpl_->y_off; + yscale = pimpl_->y_scale; + + zoffset = pimpl_->z_off; + zscale = pimpl_->z_scale; + + pixoffset = pimpl_->pix_off; + pixscale = pimpl_->pix_scale; + + lineoffset = pimpl_->line_off; + linescale = pimpl_->line_scale; +} + +// Set the RPC offset/scale Coefficients +void CPCIDSKRPCModelSegment::SetRPCTranslationCoeffs( + const double xoffset, const double xscale, + const double yoffset, const double yscale, + const double zoffset, const double zscale, + const double pixoffset, const double pixscale, + const double lineoffset, const double linescale) +{ + pimpl_->x_off = xoffset; + pimpl_->x_scale = xscale; + + pimpl_->y_off = yoffset; + pimpl_->y_scale = yscale; + + pimpl_->z_off = zoffset; + pimpl_->z_scale = zscale; + + pimpl_->pix_off = pixoffset; + pimpl_->pix_scale = pixscale; + + pimpl_->line_off = lineoffset; + pimpl_->line_scale = linescale; + + mbModified = true; +} + +// Get the adjusted X values +std::vector CPCIDSKRPCModelSegment::GetAdjXValues(void) const +{ + return pimpl_->x_adj; +} + +// Get the adjusted Y values +std::vector CPCIDSKRPCModelSegment::GetAdjYValues(void) const +{ + return pimpl_->y_adj; +} + +// Set the adjusted X/Y values +void CPCIDSKRPCModelSegment::SetAdjCoordValues(const std::vector& xcoord, + const std::vector& ycoord) +{ + if (xcoord.size() != 6 || ycoord.size() != 6) { + throw PCIDSKException("X and Y adjusted coordinates must have " + "length 6."); + } + + pimpl_->x_adj = xcoord; + pimpl_->y_adj = ycoord; + + mbModified = true; +} + +// Get whether or not this is a user-generated RPC model +bool CPCIDSKRPCModelSegment::IsUserGenerated(void) const +{ + return pimpl_->userrpc; +} + +// Set whether or not this is a user-generated RPC model +void CPCIDSKRPCModelSegment::SetUserGenerated(bool usergen) +{ + pimpl_->userrpc = usergen; + mbModified = true; +} + +// Get whether the model has been adjusted +bool CPCIDSKRPCModelSegment::IsNominalModel(void) const +{ + return !pimpl_->adjusted; +} + +// Set whether the model has been adjusted +void CPCIDSKRPCModelSegment::SetIsNominalModel(bool nominal) +{ + pimpl_->adjusted = !nominal; + mbModified = true; +} + +// Get sensor name +std::string CPCIDSKRPCModelSegment::GetSensorName(void) const +{ + return pimpl_->sensor_name; +} + +// Set sensor name +void CPCIDSKRPCModelSegment::SetSensorName(const std::string& name) +{ + pimpl_->sensor_name = name; + mbModified = true; +} + +// Output projection information of RPC Model +// Get the Geosys String +std::string CPCIDSKRPCModelSegment::GetGeosysString(void) const +{ + return pimpl_->map_units; +} + +// Set the Geosys string +void CPCIDSKRPCModelSegment::SetGeosysString(const std::string& geosys) +{ + if (geosys.size() > 16) { + throw PCIDSKException("GeoSys/MapUnits string must be no more than " + "16 characters to be valid."); + } + pimpl_->map_units = geosys; + mbModified = true; +} + +// Get number of lines +unsigned int CPCIDSKRPCModelSegment::GetLines(void) const +{ + return pimpl_->lines; +} + +unsigned int CPCIDSKRPCModelSegment::GetPixels(void) const +{ + return pimpl_->pixels; +} + +void CPCIDSKRPCModelSegment::SetRasterSize(const unsigned int lines, const unsigned int pixels) +{ + if (lines == 0 || pixels == 0) { + throw PCIDSKException("Non-sensical raster dimensions provided: %ux%u", lines, pixels); + } + + pimpl_->lines = lines; + pimpl_->pixels = pixels; + mbModified = true; +} + +void CPCIDSKRPCModelSegment::SetDownsample(const unsigned int downsample) +{ + if (downsample == 0) { + throw PCIDSKException("Invalid downsample factor provided: %u", downsample); + } + + pimpl_->downsample = downsample; + mbModified = true; +} + +unsigned int CPCIDSKRPCModelSegment::GetDownsample(void) const +{ + return pimpl_->downsample; +} + +void CPCIDSKRPCModelSegment::Synchronize() +{ + if(mbModified) + { + this->Write(); + } +} + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskrpcmodel.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskrpcmodel.h new file mode 100644 index 000000000..224eb4b1a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskrpcmodel.h @@ -0,0 +1,127 @@ +/****************************************************************************** + * + * Purpose: Support for reading and manipulating PCIDSK RPC Segments + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_SEGMENT_PCIDSKRPCMODEL_H +#define __INCLUDE_PCIDSK_SEGMENT_PCIDSKRPCMODEL_H + +#include "pcidsk_rpc.h" +#include "segment/cpcidsksegment.h" + +namespace PCIDSK { + class PCIDSKFile; + + class CPCIDSKRPCModelSegment : virtual public PCIDSKRPCSegment, + public CPCIDSKSegment + { + public: + CPCIDSKRPCModelSegment(PCIDSKFile *file, int segment,const char *segment_pointer); + ~CPCIDSKRPCModelSegment(); + + // Implementation of PCIDSKRPCSegment + // Get the X and Y RPC coefficients + std::vector GetXNumerator(void) const; + std::vector GetXDenominator(void) const; + std::vector GetYNumerator(void) const; + std::vector GetYDenominator(void) const; + + // Set the X and Y RPC Coefficients + void SetCoefficients(const std::vector& xnum, + const std::vector& xdenom, const std::vector& ynum, + const std::vector& ydenom); + + // Get the RPC offset/scale Coefficients + void GetRPCTranslationCoeffs(double& xoffset, double& xscale, + double& yoffset, double& yscale, double& zoffset, double& zscale, + double& pixoffset, double& pixscale, double& lineoffset, double& linescale) const; + + // Set the RPC offset/scale Coefficients + void SetRPCTranslationCoeffs( + const double xoffset, const double xscale, + const double yoffset, const double yscale, + const double zoffset, const double zscale, + const double pixoffset, const double pixscale, + const double lineoffset, const double linescale); + + // Get the adjusted X values + std::vector GetAdjXValues(void) const; + // Get the adjusted Y values + std::vector GetAdjYValues(void) const; + + // Set the adjusted X/Y values + void SetAdjCoordValues(const std::vector& xcoord, + const std::vector& ycoord); + + // Get whether or not this is a user-generated RPC model + bool IsUserGenerated(void) const; + // Set whether or not this is a user-generated RPC model + void SetUserGenerated(bool usergen); + + // Get whether the model has been adjusted + bool IsNominalModel(void) const; + // Set whether the model has been adjusted + void SetIsNominalModel(bool nominal); + + // Get sensor name + std::string GetSensorName(void) const; + // Set sensor name + void SetSensorName(const std::string& name); + + // Output projection information of RPC Model + // Get the Geosys String + std::string GetGeosysString(void) const; + // Set the Geosys string + void SetGeosysString(const std::string& geosys); + + // Get the number of lines + unsigned int GetLines(void) const; + + // Get the number of pixels + unsigned int GetPixels(void) const; + + // Set the number of lines/pixels + void SetRasterSize(const unsigned int lines, const unsigned int pixels); + + // Set the downsample factor + void SetDownsample(const unsigned int downsample); + + // Get the downsample factor + unsigned int GetDownsample(void) const; + + //synchronize the segment on disk. + void Synchronize(); + private: + // Helper housekeeping functions + void Load(); + void Write(); + + struct PCIDSKRPCInfo; + PCIDSKRPCInfo *pimpl_; + bool loaded_; + bool mbModified; + }; +} + +#endif // __INCLUDE_PCIDSK_SEGMENT_PCIDSKRPCMODEL_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsksegment.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsksegment.cpp new file mode 100644 index 000000000..577eef75a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsksegment.cpp @@ -0,0 +1,359 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSKSegment class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "segment/cpcidsksegment.h" +#include "core/metadataset.h" +#include "core/cpcidskfile.h" +#include "core/pcidsk_utils.h" +#include "pcidsk_buffer.h" +#include "pcidsk_exception.h" +#include +#include +#include +#include +#include + +using namespace PCIDSK; + +/************************************************************************/ +/* PCIDSKSegment() */ +/************************************************************************/ + +CPCIDSKSegment::CPCIDSKSegment( PCIDSKFile *file, int segment, + const char *segment_pointer ) + +{ + this->file = file; + this->segment = segment; + + LoadSegmentPointer( segment_pointer ); + LoadSegmentHeader(); // eventually we might want to defer this. + +/* -------------------------------------------------------------------- */ +/* Initialize the metadata object, but do not try to load till */ +/* needed. */ +/* -------------------------------------------------------------------- */ + metadata = new MetadataSet; + metadata->Initialize( file, SegmentTypeName(segment_type), segment ); +} + +/************************************************************************/ +/* ~PCIDSKSegment() */ +/************************************************************************/ + +CPCIDSKSegment::~CPCIDSKSegment() + +{ + delete metadata; +} + +/************************************************************************/ +/* GetMetadataValue() */ +/************************************************************************/ +std::string CPCIDSKSegment::GetMetadataValue( const std::string &key ) const +{ + return metadata->GetMetadataValue(key); +} + +/************************************************************************/ +/* SetMetadataValue() */ +/************************************************************************/ +void CPCIDSKSegment::SetMetadataValue( const std::string &key, const std::string &value ) +{ + metadata->SetMetadataValue(key,value); +} + +/************************************************************************/ +/* GetMetdataKeys() */ +/************************************************************************/ +std::vector CPCIDSKSegment::GetMetadataKeys() const +{ + return metadata->GetMetadataKeys(); +} + +/************************************************************************/ +/* LoadSegmentPointer() */ +/************************************************************************/ + +void CPCIDSKSegment::LoadSegmentPointer( const char *segment_pointer ) + +{ + PCIDSKBuffer segptr( segment_pointer, 32 ); + + segment_flag = segptr.buffer[0]; + segment_type = (eSegType) (atoi(segptr.Get(1,3))); + data_offset = (atouint64(segptr.Get(12,11))-1) * 512; + data_size = atouint64(segptr.Get(23,9)) * 512; + + segptr.Get(4,8,segment_name); +} + +/************************************************************************/ +/* LoadSegmentHeader() */ +/************************************************************************/ +#include +void CPCIDSKSegment::LoadSegmentHeader() + +{ + header.SetSize(1024); + + file->ReadFromFile( header.buffer, data_offset, 1024 ); + + // Read the history from the segment header. PCIDSK supports + // 8 history entries per segment. + std::string hist_msg; + history_.clear(); + for (unsigned int i = 0; i < 8; i++) + { + header.Get(384 + i * 80, 80, hist_msg); + + // Some programs seem to push history records with a trailing '\0' + // so do some extra processing to cleanup. FUN records on segment + // 3 of eltoro.pix are an example of this. + size_t size = hist_msg.size(); + while( size > 0 + && (hist_msg[size-1] == ' ' || hist_msg[size-1] == '\0') ) + size--; + + hist_msg.resize(size); + + history_.push_back(hist_msg); + } +} + +/************************************************************************/ +/* FlushHeader() */ +/* */ +/* This is used primarily after this class or subclasses have */ +/* modified the header buffer and need it pushed back to disk. */ +/************************************************************************/ + +void CPCIDSKSegment::FlushHeader() + +{ + file->WriteToFile( header.buffer, data_offset, 1024 ); +} + +/************************************************************************/ +/* ReadFromFile() */ +/************************************************************************/ + +void CPCIDSKSegment::ReadFromFile( void *buffer, uint64 offset, uint64 size ) + +{ + if( offset+size+1024 > data_size ) + ThrowPCIDSKException( + "Attempt to read past end of segment %d (%d bytes at offset %d)", + segment, (int) offset, (int) size ); + file->ReadFromFile( buffer, offset + data_offset + 1024, size ); +} + +/************************************************************************/ +/* WriteToFile() */ +/************************************************************************/ + +void CPCIDSKSegment::WriteToFile( const void *buffer, uint64 offset, uint64 size ) +{ + if( offset+size > data_size-1024 ) + { + CPCIDSKFile *poFile = dynamic_cast(file); + + if (poFile == NULL) { + ThrowPCIDSKException("Attempt to dynamic_cast the file interface " + "to a CPCIDSKFile failed. This is a programmer error, and should " + "be reported to your software provider."); + } + + if( !IsAtEOF() ) + poFile->MoveSegmentToEOF( segment ); + + uint64 blocks_to_add = + ((offset+size+511) - (data_size - 1024)) / 512; + + // prezero if we aren't directly writing all the new blocks. + poFile->ExtendSegment( segment, blocks_to_add, + !(offset == data_size - 1024 + && size == blocks_to_add * 512) ); + data_size += blocks_to_add * 512; + } + + file->WriteToFile( buffer, offset + data_offset + 1024, size ); +} + +/************************************************************************/ +/* GetDescription() */ +/************************************************************************/ + +std::string CPCIDSKSegment::GetDescription() +{ + std::string target; + + header.Get( 0, 64, target ); + + return target; +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +void CPCIDSKSegment::SetDescription( const std::string &description ) +{ + header.Put( description.c_str(), 0, 64); + + file->WriteToFile( header.buffer, data_offset, 1024 ); +} + +/************************************************************************/ +/* IsAtEOF() */ +/************************************************************************/ + +bool CPCIDSKSegment::IsAtEOF() +{ + if( 512 * file->GetFileSize() == data_offset + data_size ) + return true; + else + return false; +} + +/************************************************************************/ +/* GetHistoryEntries() */ +/************************************************************************/ + +std::vector CPCIDSKSegment::GetHistoryEntries() const +{ + return history_; +} + +/************************************************************************/ +/* SetHistoryEntries() */ +/************************************************************************/ + +void CPCIDSKSegment::SetHistoryEntries(const std::vector &entries) + +{ + for( unsigned int i = 0; i < 8; i++ ) + { + const char *msg = ""; + if( entries.size() > i ) + msg = entries[i].c_str(); + + header.Put( msg, 384 + i * 80, 80 ); + } + + FlushHeader(); + + // Force reloading of history_ + LoadSegmentHeader(); +} + +/************************************************************************/ +/* PushHistory() */ +/************************************************************************/ + +void CPCIDSKSegment::PushHistory( const std::string &app, + const std::string &message ) + +{ +#define MY_MIN(a,b) ((a history_entries = GetHistoryEntries(); + + history_entries.insert( history_entries.begin(), history ); + history_entries.resize(8); + + SetHistoryEntries( history_entries ); +} + + +/************************************************************************/ +/* MoveData() */ +/* */ +/* Move a chunk of data within a segment. Overlapping source */ +/* and destination are permitted. */ +/************************************************************************/ + +void CPCIDSKSegment::MoveData( uint64 src_offset, uint64 dst_offset, + uint64 size_in_bytes ) + +{ + bool copy_backwards = false; + + // We move things backwards if the areas overlap and the destination + // is further on in the segment. + if( dst_offset > src_offset + && src_offset + size_in_bytes > dst_offset ) + copy_backwards = true; + + + // Move the segment data to the new location. + uint8 copy_buf[16384]; + uint64 bytes_to_go; + + bytes_to_go = size_in_bytes; + + while( bytes_to_go > 0 ) + { + uint64 bytes_this_chunk = sizeof(copy_buf); + if( bytes_to_go < bytes_this_chunk ) + bytes_this_chunk = bytes_to_go; + + if( copy_backwards ) + { + ReadFromFile( copy_buf, + src_offset + bytes_to_go - bytes_this_chunk, + bytes_this_chunk ); + WriteToFile( copy_buf, + dst_offset + bytes_to_go - bytes_this_chunk, + bytes_this_chunk ); + } + else + { + ReadFromFile( copy_buf, src_offset, bytes_this_chunk ); + WriteToFile( copy_buf, dst_offset, bytes_this_chunk ); + + src_offset += bytes_this_chunk; + dst_offset += bytes_this_chunk; + } + + bytes_to_go -= bytes_this_chunk; + } +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsksegment.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsksegment.h new file mode 100644 index 000000000..aed417650 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsksegment.h @@ -0,0 +1,113 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CPCIDSKSegment class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _INCLUDE_SEGMENT_PCIDSKSEGMENT_H +#define _INCLUDE_SEGMENT_PCIDSKSEGMENT_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_buffer.h" +#include "pcidsk_segment.h" + +#include +#include + +namespace PCIDSK +{ + class PCIDSKFile; + class MetadataSet; + +/************************************************************************/ +/* CPCIDSKSegment */ +/* */ +/* Base class for accessing all segments. Provides core */ +/* PCIDSKObject implementation for segments with raw segment io */ +/* options. */ +/************************************************************************/ + + class CPCIDSKSegment : virtual public PCIDSKSegment + { + public: + CPCIDSKSegment( PCIDSKFile *file, int segment, + const char *segment_pointer ); + virtual ~CPCIDSKSegment(); + + void LoadSegmentPointer( const char *segment_pointer ); + void LoadSegmentHeader(); + + PCIDSKBuffer &GetHeader() { return header; } + void FlushHeader(); + + void WriteToFile( const void *buffer, uint64 offset, uint64 size ); + void ReadFromFile( void *buffer, uint64 offset, uint64 size ); + + eSegType GetSegmentType() { return segment_type; } + std::string GetName() { return segment_name; } + std::string GetDescription(); + int GetSegmentNumber() { return segment; } + uint64 GetContentSize() { return data_size - 1024; } + bool IsAtEOF(); + + void SetDescription( const std::string &description); + + std::string GetMetadataValue( const std::string &key ) const; + void SetMetadataValue( const std::string &key, const std::string &value ); + std::vector GetMetadataKeys() const; + + virtual void Synchronize() {} + + std::vector GetHistoryEntries() const; + void SetHistoryEntries( const std::vector &entries ); + void PushHistory(const std::string &app, + const std::string &message); + + virtual std::string ConsistencyCheck() { return ""; } + + protected: + PCIDSKFile *file; + + int segment; + + eSegType segment_type; + char segment_flag; + std::string segment_name; + + uint64 data_offset; // includes 1024 byte segment header. + uint64 data_size; + + PCIDSKBuffer header; + + mutable MetadataSet *metadata; + + std::vector history_; + + void MoveData( uint64 src_offset, uint64 dst_offset, + uint64 size_in_bytes ); + }; + +} // end namespace PCIDSK +#endif // _INCLUDE_SEGMENT_PCIDSKSEGMENT_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsktoutinmodel.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsktoutinmodel.cpp new file mode 100644 index 000000000..7ce2f5d04 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsktoutinmodel.cpp @@ -0,0 +1,894 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSKToutinModelSegment class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "segment/cpcidsksegment.h" +#include "core/pcidsk_utils.h" +#include "segment/cpcidsktoutinmodel.h" +#include "pcidsk_exception.h" +#include "core/pcidsk_utils.h" + +#include +#include +#include +#include + +using namespace PCIDSK; + +namespace +{ + /** + * Function to get the minimum value of two values. + * + * @param a The first value. + * @param b The second value. + * + * @return The minimum value of the two specified values. + */ +#if 0 /* Unused */ + int MinFunction(int a,int b) + { + return (aWrite(); + } +} + +/************************************************************************/ +/* BinaryToSRITInfo() */ +/************************************************************************/ +/** + * Translate a block of binary data into a SRIT segment. the caller is + * responsible to free the returned memory with delete. + * + * @return Rational Satellite Model structure. + */ +SRITInfo_t * +CPCIDSKToutinModelSegment::BinaryToSRITInfo() +{ + int i,j,k,l; + SRITInfo_t *SRITModel; + bool bVersion9; + +/* -------------------------------------------------------------------- */ +/* Read the header block */ +/* -------------------------------------------------------------------- */ + // We test the name of the binary segment before starting to read + // the buffer. + if (std::strncmp(seg_data.buffer, "MODEL ", 8)) + { + seg_data.Put("MODEL ",0,8); + return NULL; + // Something has gone terribly wrong! + /*throw PCIDSKException("A segment that was previously " + "identified as an RFMODEL " + "segment does not contain the appropriate data. Found: [%s]", + std::string(seg_data.buffer, 8).c_str());*/ + } + + bVersion9 = false; + int nVersion = seg_data.GetInt(8,1); + if (nVersion == 9) + { + bVersion9 = true; + } + +/* -------------------------------------------------------------------- */ +/* Allocate the SRITModel. */ +/* -------------------------------------------------------------------- */ + SRITModel = new SRITInfo_t(); + + SRITModel->GCPMeanHtFlag = 0; + SRITModel->nDownSample = 1; + if(std::strncmp(seg_data.Get(22,2) , "DS", 2)==0) + { + SRITModel->nDownSample = seg_data.GetInt(24,3); + } + +/* -------------------------------------------------------------------- */ +/* Read the Block 1 */ +/* -------------------------------------------------------------------- */ + + SRITModel->N0x2 = seg_data.GetDouble(512,22); + SRITModel->aa = seg_data.GetDouble(512+22,22); + SRITModel->SmALPHA = seg_data.GetDouble(512+44,22); + SRITModel->bb = seg_data.GetDouble(512+66,22); + SRITModel->C0 = seg_data.GetDouble(512+88,22); + SRITModel->cc = seg_data.GetDouble(512+110,22); + SRITModel->COS_KHI = seg_data.GetDouble(512+132,22); + SRITModel->DELTA_GAMMA = seg_data.GetDouble(512+154,22); + SRITModel->GAMMA = seg_data.GetDouble(512+176,22); + SRITModel->K_1 = seg_data.GetDouble(512+198,22); + SRITModel->L0 = seg_data.GetDouble(512+220,22); + SRITModel->P = seg_data.GetDouble(512+242,22); + SRITModel->Q = seg_data.GetDouble(512+264,22); + SRITModel->TAU = seg_data.GetDouble(512+286,22); + SRITModel->THETA = seg_data.GetDouble(512+308,22); + SRITModel->THETA_SEC = seg_data.GetDouble(512+330,22); + SRITModel->X0 = seg_data.GetDouble(512+352,22); + SRITModel->Y0 = seg_data.GetDouble(512+374,22); + SRITModel->delh = seg_data.GetDouble(512+396,22); + SRITModel->COEF_Y2 = seg_data.GetDouble(512+418,22); + + if (bVersion9) + { + SRITModel->delT = seg_data.GetDouble(512+440,22); + SRITModel->delL = seg_data.GetDouble(512+462,22); + SRITModel->delTau = seg_data.GetDouble(512+484,22); + } + else + { + SRITModel->delT = 0.0; + SRITModel->delL = 0.0; + SRITModel->delTau = 0.0; + } + +/* -------------------------------------------------------------------- */ +/* Read the GCP information in Block 2 */ +/* -------------------------------------------------------------------- */ + + SRITModel->nGCPCount = seg_data.GetInt(2*512,10); + SRITModel->nEphemerisSegNo = seg_data.GetInt(2*512+10,10); + SRITModel->nAttitudeFlag = seg_data.GetInt(2*512+20,10); + SRITModel->GCPUnit = seg_data.Get(2*512+30,16); + + SRITModel->dfGCPMeanHt = seg_data.GetDouble(2*512+50,22); + SRITModel->dfGCPMinHt = seg_data.GetDouble(2*512+72,22); + SRITModel->dfGCPMaxHt = seg_data.GetDouble(2*512+94,22); + +/* -------------------------------------------------------------------- */ +/* Initialize a simple GeoTransform. */ +/* -------------------------------------------------------------------- */ + + SRITModel->utmunit = seg_data.Get(2*512+225,16); + + if (std::strcmp(seg_data.Get(2*512+245,8),"ProjInfo")==0) + { + SRITModel->oProjectionInfo = seg_data.Get(2*512+255,256); + } + +/* -------------------------------------------------------------------- */ +/* Read the GCPs */ +/* -------------------------------------------------------------------- */ + l = 0; + k = 4; + for (j=0; jnGCPCount; j++) + { + SRITModel->nGCPIds[j] = + seg_data.GetInt((k-1)*512+10*l,5); + SRITModel->nPixel[j] = + seg_data.GetInt((k-1)*512+10*(l+1),5); + SRITModel->nLine[j] = + seg_data.GetInt((k-1)*512+10*(l+1)+5,5); + SRITModel->dfElev[j] = + seg_data.GetInt((k-1)*512+10*(l+2),10); + l+=3; + + if (l<50) + continue; + + k++; + l = 0; + } + +/* -------------------------------------------------------------------- */ +/* Call BinaryToEphemeris to get the orbital data */ +/* -------------------------------------------------------------------- */ + SRITModel->OrbitPtr = + BinaryToEphemeris( 512*21 ); + +/* -------------------------------------------------------------------- */ +/* Pass the sensor back to SRITModel */ +/* -------------------------------------------------------------------- */ + SRITModel->Sensor = SRITModel->OrbitPtr->SatelliteSensor; + +/* -------------------------------------------------------------------- */ +/* Assign nSensor value */ +/* -------------------------------------------------------------------- */ + + SRITModel->nSensor = GetSensor (SRITModel->OrbitPtr); + SRITModel->nModel = GetModel (SRITModel->nSensor); + + if( SRITModel->nSensor == -999) + { + throw PCIDSKException("Invalid Sensor : %s.", + SRITModel->OrbitPtr->SatelliteSensor.c_str()); + } + if( SRITModel->nModel == -999) + { + throw PCIDSKException("Invalid Model from sensor number: %d.", + SRITModel->nSensor); + } + +/* -------------------------------------------------------------------- */ +/* Get the attitude data for SPOT */ +/* -------------------------------------------------------------------- */ + if (SRITModel->OrbitPtr->AttitudeSeg != NULL || + SRITModel->OrbitPtr->RadarSeg != NULL) + { + if (SRITModel->OrbitPtr->Type == OrbAttitude) + { + int ndata; + AttitudeSeg_t *attitudeSeg + = SRITModel->OrbitPtr->AttitudeSeg; + + ndata = SRITModel->OrbitPtr->AttitudeSeg->NumberOfLine; + + for (i=0; iHdeltat.push_back( + attitudeSeg->Line[i].ChangeInAttitude); + SRITModel->Qdeltar.push_back( + attitudeSeg->Line[i].ChangeEarthSatelliteDist); + } + } + } + else + { + SRITModel->Qdeltar.clear(); + SRITModel->Hdeltat.clear(); + } + + return SRITModel; +} + +/************************************************************************/ +/* SRITInfoToBinary() */ +/************************************************************************/ +/** + * Translate a SRITInfo_t into binary data. + * Translate a SRITInfo_t into the corresponding block of + * binary data. This function is expected to be used by + * ranslators such as iisopen.c (VISTA) so that our satellite + * models can be converted into some opaque serialized form. + * Translate a RFInfo_t into the corresponding block of binary data. + * + * @param SRITModel Satellite Model structure. + * @param pnBinaryLength Length of binary data. + * @return Binary data for a Satellite Model structure. + */ +void +CPCIDSKToutinModelSegment::SRITInfoToBinary( SRITInfo_t *SRITModel ) + +{ + int i,j,k,l; + double dfminht,dfmaxht,dfmeanht; + int nPos = 0; + +/* -------------------------------------------------------------------- */ +/* Create the data array. */ +/* -------------------------------------------------------------------- */ + seg_data.SetSize(512 * 21); + + //clean the buffer + memset( seg_data.buffer , ' ', 512 * 21 ); + +/* -------------------------------------------------------------------- */ +/* Initialize the header. */ +/* -------------------------------------------------------------------- */ + nPos = 512*0; + seg_data.Put("MODEL 9.0",0,nPos+11); + + seg_data.Put("DS",nPos+22,2); + seg_data.Put(SRITModel->nDownSample,nPos+24,3); + +/* -------------------------------------------------------------------- */ +/* Write the model results to second segment */ +/* -------------------------------------------------------------------- */ + nPos = 512*1; + + seg_data.Put(SRITModel->N0x2,nPos,22,"%22.14f"); + seg_data.Put(SRITModel->aa,nPos+22,22,"%22.14f"); + seg_data.Put(SRITModel->SmALPHA,nPos+22*2,22,"%22.14f"); + seg_data.Put(SRITModel->bb,nPos+22*3,22,"%22.14f"); + seg_data.Put(SRITModel->C0,nPos+22*4,22,"%22.14f"); + seg_data.Put(SRITModel->cc,nPos+22*5,22,"%22.14f"); + seg_data.Put(SRITModel->COS_KHI,nPos+22*6,22,"%22.14f"); + seg_data.Put(SRITModel->DELTA_GAMMA,nPos+22*7,22,"%22.14f"); + seg_data.Put(SRITModel->GAMMA,nPos+22*8,22,"%22.14f"); + seg_data.Put(SRITModel->K_1,nPos+22*9,22,"%22.14f"); + seg_data.Put(SRITModel->L0,nPos+22*10,22,"%22.14f"); + seg_data.Put(SRITModel->P,nPos+22*11,22,"%22.14f"); + seg_data.Put(SRITModel->Q,nPos+22*12,22,"%22.14f"); + seg_data.Put(SRITModel->TAU,nPos+22*13,22,"%22.14f"); + seg_data.Put(SRITModel->THETA,nPos+22*14,22,"%22.14f"); + seg_data.Put(SRITModel->THETA_SEC,nPos+22*15,22,"%22.14f"); + seg_data.Put(SRITModel->X0,nPos+22*16,22,"%22.14f"); + seg_data.Put(SRITModel->Y0,nPos+22*17,22,"%22.14f"); + seg_data.Put(SRITModel->delh,nPos+22*18,22,"%22.14f"); + seg_data.Put(SRITModel->COEF_Y2,nPos+22*19,22,"%22.14f"); + seg_data.Put(SRITModel->delT,nPos+22*20,22,"%22.14f"); + seg_data.Put(SRITModel->delL,nPos+22*21,22,"%22.14f"); + seg_data.Put(SRITModel->delTau,nPos+22*22,22,"%22.14f"); + +/* -------------------------------------------------------------------- */ +/* Find the min and max height */ +/* -------------------------------------------------------------------- */ + nPos = 2*512; + + if (SRITModel->nGCPCount != 0) + { + dfminht = 1.e38; + dfmaxht = -1.e38; + for (i=0; inGCPCount; i++) + { + if (SRITModel->dfElev[i] > dfmaxht) + dfmaxht = SRITModel->dfElev[i]; + if (SRITModel->dfElev[i] < dfminht) + dfminht = SRITModel->dfElev[i]; + } + } + else + { + dfminht = SRITModel->dfGCPMinHt; + dfmaxht = 0; + } + + dfmeanht = (dfminht + dfmaxht)/2.; + + seg_data.Put(SRITModel->nGCPCount,nPos,10); + seg_data.Put("2",nPos+10,1); + seg_data.Put("0",nPos+20,1); + + if (SRITModel->OrbitPtr->AttitudeSeg != NULL || + SRITModel->OrbitPtr->RadarSeg != NULL || + SRITModel->OrbitPtr->AvhrrSeg != NULL ) + { + if (SRITModel->OrbitPtr->Type == OrbAttitude) + { + if (SRITModel->OrbitPtr->AttitudeSeg->NumberOfLine != 0) + seg_data.Put("3",nPos+20,1); + } + } + + seg_data.Put(SRITModel->GCPUnit.c_str(),nPos+30,16); + seg_data.Put("M",nPos+49,1); + + seg_data.Put(dfmeanht,nPos+50,22,"%22.14f"); + seg_data.Put(dfminht,nPos+72,22,"%22.14f"); + seg_data.Put(dfmaxht,nPos+94,22,"%22.14f"); + + seg_data.Put("NEWGCP",nPos+116,6); + +/* -------------------------------------------------------------------- */ +/* Write the projection parameter if necessary */ +/* -------------------------------------------------------------------- */ + + seg_data.Put(SRITModel->utmunit.c_str(),nPos+225,16); + + if(SRITModel->oProjectionInfo.size() > 0) + { + seg_data.Put("ProjInfo: ",nPos+245,10); + seg_data.Put(SRITModel->oProjectionInfo.c_str(), + nPos+255,256); + } + +/* -------------------------------------------------------------------- */ +/* Write the GCP to third segment */ +/* -------------------------------------------------------------------- */ + nPos = 3*512; + + l = 0; + k = 3; + for (j=0; jnGCPCount; j++) + { + if (j > 255) + break; + + seg_data.Put(SRITModel->nGCPIds[j],nPos+10*l,5); + seg_data.Put((int)(SRITModel->nPixel[j]+0.5), + nPos+10*(l+1),5); + seg_data.Put((int)(SRITModel->nLine[j]+0.5), + nPos+10*(l+1)+5,5); + seg_data.Put((int)SRITModel->dfElev[j],nPos+10*(l+2),10); + + l+=3; + + if (l<50) + continue; + + k++; + nPos = 512*k; + l = 0; + } + +/* -------------------------------------------------------------------- */ +/* Add the serialized form of the EphemerisSeg_t. */ +/* -------------------------------------------------------------------- */ + EphemerisToBinary( SRITModel->OrbitPtr , 512*21 ); +} + +/** + * Get the sensor enum from the orbit segment. + * @param OrbitPtr the orbit segment + * @return the sensor type. + */ +int CPCIDSKToutinModelSegment::GetSensor( EphemerisSeg_t *OrbitPtr) +{ + int nSensor; + + nSensor = -999; + + if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"AVHRR",5)) + nSensor = AVHRR; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"PLA",3)) + nSensor = PLA_1; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"MLA",3)) + nSensor = MLA_1; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"ASTER",5)) + nSensor = ASTER; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"SAR",3)) + { + nSensor = SAR; + if (OrbitPtr->PixelRes == 6.25) + nSensor = RSAT_FIN; + } + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-1",6)) + nSensor = LISS_1; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-2",6)) + nSensor = LISS_2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-3",6)) + nSensor = LISS_3; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-L3-L2",10)) + nSensor = LISS_L3_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-L3",7)) + nSensor = LISS_L3; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-L4-L2",10)) + nSensor = LISS_L4_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-L4",7)) + nSensor = LISS_L4; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-P3-L2",10)) + nSensor = LISS_P3_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-P3",7)) + nSensor = LISS_P3; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-W3-L2",10)) + nSensor = LISS_W3_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-W3",7)) + nSensor = LISS_W3; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-M3",7)) + nSensor = LISS_M3; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-AWF-L2",11)) + nSensor = LISS_AWF_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"LISS-AWF",8)) + nSensor = LISS_AWF; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"EOC",3)) + nSensor = EOC; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"IRS",3)) + nSensor = IRS_1; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"TM",2)) + { + nSensor = TM; + if (OrbitPtr->PixelRes == 15) + nSensor = ETM; + } + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"ETM",3)) + nSensor = ETM; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"IKO",3)) + { + nSensor = IKO_PAN; + if (OrbitPtr->PixelRes == 4) + nSensor = IKO_MULTI; + } + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"ORBVIEW",7)) + { + nSensor = ORBVIEW_PAN; + if (OrbitPtr->PixelRes == 4) + nSensor = ORBVIEW_MULTI; + } + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"OV",2)) + { + if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"OV3_PAN_BASIC",13)) + nSensor = OV3_PAN_BASIC; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"OV3_PAN_GEO",11)) + nSensor = OV3_PAN_GEO; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"OV3_MULTI_BASIC",15)) + nSensor = OV3_MULTI_BASIC; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"OV3_MULTI_GEO",13)) + nSensor = OV3_MULTI_GEO; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"OV5_PAN_BASIC",13)) + nSensor = OV5_PAN_BASIC; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"OV5_PAN_GEO",11)) + nSensor = OV5_PAN_GEO; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"OV5_MULTI_BASIC",15)) + nSensor = OV5_MULTI_BASIC; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"OV5_MULTI_GEO",13)) + nSensor = OV5_MULTI_GEO; + } + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"QBIRD_PAN_STD",13)) + nSensor = QBIRD_PAN_STD; // this checking must go first + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"QBIRD_PAN_STH",13)) + nSensor = QBIRD_PAN_STH; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"QBIRD_PAN",9)) + nSensor = QBIRD_PAN; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"QBIRD_MULTI_STD",15)) + nSensor = QBIRD_MULTI_STD; // this checking must go first + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"QBIRD_MULTI_STH",15)) + nSensor = QBIRD_MULTI_STH; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"QBIRD_MULTI",11)) + nSensor = QBIRD_MULTI; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"WVIEW1_PAN_STD",14) || + EQUALN(OrbitPtr->SatelliteSensor.c_str(),"WVIEW_PAN_STD",13)) + nSensor = WVIEW_PAN_STD; // this checking must go first + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"WVIEW1_PAN",10) || + EQUALN(OrbitPtr->SatelliteSensor.c_str(),"WVIEW_PAN",9)) + nSensor = WVIEW_PAN; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"WVIEW_MULTI_STD",15)) + nSensor = WVIEW_MULTI_STD; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"WVIEW_MULTI",11)) + nSensor = WVIEW_MULTI; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"FORMOSAT",8)) + { + if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"FORMOSAT_PAN_L2",15)) + nSensor = FORMOSAT_PAN_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"FORMOSAT_MULTIL2",16)) + nSensor = FORMOSAT_MULTIL2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"FORMOSAT_PAN",12)) + nSensor = FORMOSAT_PAN; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"FORMOSAT_MULTI",14)) + nSensor = FORMOSAT_MULTI; + } + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"SPOT5_PAN_2_5",13)) + nSensor = SPOT5_PAN_2_5; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"SPOT5_PAN_5",11)) + nSensor = SPOT5_PAN_5; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"SPOT5_HRS",9)) + nSensor = SPOT5_HRS; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"SPOT5_MULTI",11)) + nSensor = SPOT5_MULTI; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"MERIS_FR",8)) + nSensor = MERIS_FR; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"MERIS_RR",8)) + nSensor = MERIS_RR; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"MERIS_LR",8)) + nSensor = MERIS_LR; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"ASAR",4)) + nSensor = ASAR; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"EROS",4)) + nSensor = EROS; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"MODIS_1000",10)) + nSensor = MODIS_1000; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"MODIS_500",9)) + nSensor = MODIS_500; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"MODIS_250",9)) + nSensor = MODIS_250; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"CBERS_HRC_L2",12)) + nSensor = CBERS_HRC_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"CBERS_HRC",9)) + nSensor = CBERS_HRC; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"CBERS_CCD_L2",12)) + nSensor = CBERS_CCD_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"CBERS_CCD",9)) + nSensor = CBERS_CCD; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"CBERS_IRM_80_L2",15)) + nSensor = CBERS_IRM_80_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"CBERS_IRM_80",12)) + nSensor = CBERS_IRM_80; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"CBERS_IRM_160_L2",16)) + nSensor = CBERS_IRM_160_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"CBERS_IRM_160",13)) + nSensor = CBERS_IRM_160; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"CBERS_WFI_L2",12)) + nSensor = CBERS_WFI_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"CBERS_WFI",9)) + nSensor = CBERS_WFI; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"CARTOSAT1_L1",12)) + nSensor = CARTOSAT1_L1; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"CARTOSAT1_L2",12)) + nSensor = CARTOSAT1_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"DMC_1R",6)) + nSensor = DMC_1R; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"DMC_1T",6)) + nSensor = DMC_1T; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"ALOS_PRISM_L1",13)) + nSensor = ALOS_PRISM_L1; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"ALOS_PRISM_L2",13)) + nSensor = ALOS_PRISM_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"ALOS_AVNIR_L1",13)) + nSensor = ALOS_AVNIR_L1; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"ALOS_AVNIR_L2",13)) + nSensor = ALOS_AVNIR_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"PALSAR",6)) + nSensor = PALSAR; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"KOMPSAT2_PAN",12)) + nSensor = KOMPSAT2_PAN; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"KOMPSAT2_MULTI",14)) + nSensor = KOMPSAT2_MULTI; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"TERRASAR",8)) + nSensor = TERRASAR; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"RAPIDEYE",8)) + nSensor = RAPIDEYE_L1B; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"THEOS_PAN_L1",12)) + nSensor = THEOS_PAN_L1; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"THEOS_PAN_L2",12)) + nSensor = THEOS_PAN_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"THEOS_MS_L1",11)) + nSensor = THEOS_MS_L1; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"THEOS_MS_L2",11)) + nSensor = THEOS_MS_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"GOSAT_500_L1",12)) + nSensor = GOSAT_500_L1; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"GOSAT_500_L2",12)) + nSensor = GOSAT_500_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"GOSAT_1500_L1",13)) + nSensor = GOSAT_1500_L1; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"GOSAT_1500_L2",13)) + nSensor = GOSAT_1500_L2; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"HJ_CCD_1A",9)) + nSensor = HJ_CCD_1A; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"HJ_CCD_1B",5)) + nSensor = HJ_CCD_1B; + else if (EQUALN(OrbitPtr->SatelliteSensor.c_str(),"NEW",3)) + nSensor = NEW; + else + { + throw PCIDSKException("Invalid Sensor %s", + OrbitPtr->SatelliteSensor.c_str()); + } + + return (nSensor); +} +/** + * Get the model of a sensor + * @param nSensor the sensor + * @return the model + */ +int CPCIDSKToutinModelSegment::GetModel( int nSensor ) +{ + int nModel; + + nModel = -999; + + switch (nSensor) + { + case PLA_1: + case PLA_2: + case PLA_3: + case PLA_4: + case MLA_1: + case MLA_2: + case MLA_3: + case MLA_4: + case NEW: + nModel = SRITModele; + break; + + case ASTER: + case CBERS_CCD: + case CBERS_IRM_80: + case CBERS_IRM_160: + case CBERS_WFI: + case IRS_1: + case LISS_AWF: + case LISS_1: + case LISS_2: + case LISS_3: + case LISS_L3: + case LISS_L4: + case LISS_P3: + case LISS_W3: + case LISS_M3: + case EOC: + case SPOT5_PAN_5: + case SPOT5_HRS: + case SPOT5_MULTI: + case MERIS_FR: + case MERIS_RR: + case MERIS_LR: + case MODIS_1000: + case MODIS_500: + case MODIS_250: + case ALOS_AVNIR_L1: + case ALOS_AVNIR_L2: + case RAPIDEYE_L1B: + case THEOS_PAN_L1: + case THEOS_MS_L1: + case GOSAT_500_L1: + case GOSAT_1500_L1: + case HJ_CCD_1A: + nModel = SRITModele1A; + break; + + case TM: + case ETM: + case LISS_P3_L2: + case LISS_L3_L2: + case LISS_W3_L2: + case LISS_L4_L2: + case LISS_AWF_L2: + case CBERS_IRM_80_L2: + case CBERS_IRM_160_L2: + case CBERS_WFI_L2: + case CBERS_CCD_L2: + case CBERS_HRC_L2: + case DMC_1R: + case DMC_1T: + case ALOS_PRISM_L2: + case THEOS_PAN_L2: + case THEOS_MS_L2: + case GOSAT_500_L2: + case GOSAT_1500_L2: + case HJ_CCD_1B: + nModel = SRITModele1B; + break; + + case SAR: + case RSAT_FIN: + case RSAT_STD: + case ERS_1: + case ERS_2: + case ASAR: + case QBIRD_PAN_STD: + case QBIRD_MULTI_STD: + case WVIEW_PAN_STD: + case WVIEW_MULTI_STD: + case IKO_PAN: + case IKO_MULTI: + case CARTOSAT1_L2: + case PALSAR: + case FORMOSAT_PAN_L2: + case FORMOSAT_MULTIL2: + case TERRASAR: + case OV3_PAN_GEO: + case OV3_MULTI_GEO: + case OV5_PAN_GEO: + case OV5_MULTI_GEO: + nModel = SRITModeleSAR; + break; + + case ORBVIEW_PAN: + case ORBVIEW_MULTI: + case QBIRD_PAN: + case QBIRD_MULTI: + case WVIEW_PAN: + case WVIEW_MULTI: + case SPOT5_PAN_2_5: + case CARTOSAT1_L1: + case ALOS_PRISM_L1: + case KOMPSAT2_PAN: + case KOMPSAT2_MULTI: + case CBERS_HRC: + case OV3_PAN_BASIC: + case OV3_MULTI_BASIC: + case OV5_PAN_BASIC: + case OV5_MULTI_BASIC: + nModel = SRITModele1AHR; + break; + + case EROS: + case QBIRD_PAN_STH: + case QBIRD_MULTI_STH: + case FORMOSAT_PAN: + case FORMOSAT_MULTI: + nModel = SRITModeleEros; + break; + + default: + throw PCIDSKException("Invalid sensor type."); + break; + } + + return (nModel); +} + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsktoutinmodel.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsktoutinmodel.h new file mode 100644 index 000000000..436d8378c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidsktoutinmodel.h @@ -0,0 +1,68 @@ +/****************************************************************************** + * + * Purpose: Support for reading and manipulating PCIDSK Toutin Segments + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_SEGMENT_PCIDSKTOUTINMODEL_H +#define __INCLUDE_PCIDSK_SEGMENT_PCIDSKTOUTINMODEL_H + +#include "pcidsk_toutin.h" +#include "segment/cpcidsksegment.h" +#include "segment/cpcidskephemerissegment.h" + +namespace PCIDSK { + class PCIDSKFile; + + class CPCIDSKToutinModelSegment : public PCIDSKToutinSegment, + public CPCIDSKEphemerisSegment + { + public: + CPCIDSKToutinModelSegment(PCIDSKFile *file, int segment,const char *segment_pointer); + ~CPCIDSKToutinModelSegment(); + + SRITInfo_t GetInfo() const; + void SetInfo(const SRITInfo_t& poInfo); + + //synchronize the segment on disk. + void Synchronize(); + private: + + // Helper housekeeping functions + void Load(); + void Write(); + + //functions to read/write binary information + private: + //Toutin informations. + SRITInfo_t* mpoInfo; + + SRITInfo_t *BinaryToSRITInfo(); + void SRITInfoToBinary( SRITInfo_t *SRITModel); + + int GetSensor( EphemerisSeg_t *OrbitPtr); + int GetModel( int nSensor ); + }; +} + +#endif // __INCLUDE_PCIDSK_SEGMENT_PCIDSKTOUTINMODEL_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskvectorsegment.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskvectorsegment.cpp new file mode 100644 index 000000000..8c292890c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskvectorsegment.cpp @@ -0,0 +1,1522 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSKVectorSegment class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_file.h" +#include "pcidsk_exception.h" +#include "core/pcidsk_utils.h" +#include "segment/cpcidskvectorsegment.h" +#include +#include +#include + +using namespace PCIDSK; + +/* -------------------------------------------------------------------- */ +/* Size of a block in the record/vertice block tables. This is */ +/* determined by the PCIDSK format and may not be changed. */ +/* -------------------------------------------------------------------- */ +static const int block_page_size = 8192; + + +/* -------------------------------------------------------------------- */ +/* Size of one page of loaded shapeids. This is not related to */ +/* the file format, and may be changed to alter the number of */ +/* shapeid pointers kept in RAM at one time from the shape */ +/* index. */ +/* -------------------------------------------------------------------- */ +static const int shapeid_page_size = 1024; + +/************************************************************************/ +/* CPCIDSKVectorSegment() */ +/************************************************************************/ + +CPCIDSKVectorSegment::CPCIDSKVectorSegment( PCIDSKFile *file, int segment, + const char *segment_pointer ) + : CPCIDSKSegment( file, segment, segment_pointer ) + +{ + base_initialized = false; + + last_shapes_id = NullShapeId; + last_shapes_index = -1; + + raw_loaded_data_offset = 0; + vert_loaded_data_offset = 0; + record_loaded_data_offset = 0; + raw_loaded_data_dirty = false; + vert_loaded_data_dirty = false; + record_loaded_data_dirty = false; + + shape_index_start = 0; + shape_index_page_dirty = false; + + shapeid_map_active = false; + shapeid_pages_certainly_mapped = -1; + + vh.vs = this; + + highest_shapeid_used = NullShapeId; +} + +/************************************************************************/ +/* ~CPCIDSKVectorSegment() */ +/************************************************************************/ + +CPCIDSKVectorSegment::~CPCIDSKVectorSegment() + +{ + Synchronize(); +} + +/************************************************************************/ +/* Synchronize() */ +/************************************************************************/ + +void CPCIDSKVectorSegment::Synchronize() +{ + if( base_initialized ) + { + FlushDataBuffer( sec_vert ); + FlushDataBuffer( sec_record ); + + di[sec_vert].Flush(); + di[sec_record].Flush(); + + FlushLoadedShapeIndex(); + + if( GetHeader().GetInt( 192, 16 ) != shape_count + && file->GetUpdatable() ) + { + GetHeader().Put( shape_count, 192, 16 ); + FlushHeader(); + } + } +} + +/************************************************************************/ +/* Initialize() */ +/* */ +/* Initialize the header of a new vector segment in a */ +/* consistent state for an empty segment. */ +/************************************************************************/ + +void CPCIDSKVectorSegment::Initialize() + +{ + needs_swap = !BigEndianSystem(); + +/* -------------------------------------------------------------------- */ +/* Initialize the header that occurs within the regular segment */ +/* data. */ +/* -------------------------------------------------------------------- */ + vh.InitializeNew(); + +/* -------------------------------------------------------------------- */ +/* Initialize the values in the generic segment header. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer &head = GetHeader(); + + head.Put( "METRE", 160, 16 ); + head.Put( 1.0, 176, 16 ); + head.Put( 0, 192, 16 ); + head.Put( 0, 208, 16 ); + head.Put( 0, 224, 16 ); + head.Put( "", 240, 16 ); + head.Put( 0, 256, 16 ); + head.Put( 0, 272, 16 ); + + FlushHeader(); +} + +/************************************************************************/ +/* LoadHeader() */ +/* */ +/* Initialize minimum information from the vector segment */ +/* header. We defer this till an actual vector related action */ +/* is taken. */ +/************************************************************************/ + +void CPCIDSKVectorSegment::LoadHeader() + +{ + if( base_initialized ) + return; + + base_initialized = true; + + needs_swap = !BigEndianSystem(); + + vh.InitializeExisting(); +} + +/************************************************************************/ +/* ReadField() */ +/* */ +/* Read a value from the indicated offset in a section of the */ +/* vector segment, and place the value into a ShapeField */ +/* structure based on the passed in field type. */ +/************************************************************************/ + +uint32 CPCIDSKVectorSegment::ReadField( uint32 offset, ShapeField& field, + ShapeFieldType field_type, + int section ) + +{ + switch( field_type ) + { + case FieldTypeInteger: + { + int32 value; + memcpy( &value, GetData( section, offset, NULL, 4), 4 ); + if( needs_swap ) + SwapData( &value, 4, 1 ); + field.SetValue( value ); + return offset + 4; + } + + case FieldTypeFloat: + { + float value; + memcpy( &value, GetData( section, offset, NULL, 4), 4 ); + if( needs_swap ) + SwapData( &value, 4, 1 ); + field.SetValue( value ); + return offset + 4; + } + + case FieldTypeDouble: + { + double value; + memcpy( &value, GetData( section, offset, NULL, 8), 8 ); + if( needs_swap ) + SwapData( &value, 8, 1 ); + field.SetValue( value ); + return offset + 8; + } + + case FieldTypeString: + { + int available; + char *srcdata = GetData( section, offset, &available, 1 ); + + // Simple case -- all initially available. + int string_len = 0; + + while( srcdata[string_len] != '\0' && available - string_len > 0 ) + string_len++; + + if( string_len < available && srcdata[string_len] == '\0' ) + { + std::string value( srcdata, string_len ); + field.SetValue( value ); + return offset + string_len + 1; + } + + std::string value; + + while( *srcdata != '\0' ) + { + value += *(srcdata++); + offset++; + available--; + if( available == 0 ) + srcdata = GetData( section, offset, &available, 1 ); + } + + field.SetValue( value ); + return offset+1; + } + + case FieldTypeCountedInt: + { + std::vector value; + int32 count; + char *srcdata = GetData( section, offset, NULL, 4 ); + memcpy( &count, srcdata, 4 ); + if( needs_swap ) + SwapData( &count, 4, 1 ); + + value.resize( count ); + if( count > 0 ) + { + memcpy( &(value[0]), GetData(section,offset+4,NULL,4*count), 4*count ); + if( needs_swap ) + SwapData( &(value[0]), 4, count ); + } + + field.SetValue( value ); + return offset + 4 + 4*count; + } + + default: + assert( 0 ); + return offset; + } +} + +/************************************************************************/ +/* WriteField() */ +/* */ +/* Write a field value into a buffer, growing the buffer if */ +/* needed to hold the value. */ +/************************************************************************/ + +uint32 CPCIDSKVectorSegment::WriteField( uint32 offset, + const ShapeField& field, + PCIDSKBuffer& buffer ) + +{ +/* -------------------------------------------------------------------- */ +/* How much space do we need for this value? */ +/* -------------------------------------------------------------------- */ + uint32 item_size = 0; + + switch( field.GetType() ) + { + case FieldTypeInteger: + item_size = 4; + break; + + case FieldTypeFloat: + item_size = 4; + break; + + case FieldTypeDouble: + item_size = 8; + break; + + case FieldTypeString: + item_size = field.GetValueString().size() + 1; + break; + + case FieldTypeCountedInt: + item_size = field.GetValueCountedInt().size() * 4 + 4; + break; + + default: + assert( 0 ); + item_size = 0; + break; + } + +/* -------------------------------------------------------------------- */ +/* Do we need to grow the buffer to hold this? Try to make it */ +/* plenty larger. */ +/* -------------------------------------------------------------------- */ + if( item_size + offset > (uint32) buffer.buffer_size ) + buffer.SetSize( buffer.buffer_size*2 + item_size ); + +/* -------------------------------------------------------------------- */ +/* Write to the buffer, and byte swap if needed. */ +/* -------------------------------------------------------------------- */ + switch( field.GetType() ) + { + case FieldTypeInteger: + { + int32 value = field.GetValueInteger(); + if( needs_swap ) + SwapData( &value, 4, 1 ); + memcpy( buffer.buffer+offset, &value, 4 ); + break; + } + + case FieldTypeFloat: + { + float value = field.GetValueFloat(); + if( needs_swap ) + SwapData( &value, 4, 1 ); + memcpy( buffer.buffer+offset, &value, 4 ); + break; + } + + case FieldTypeDouble: + { + double value = field.GetValueDouble(); + if( needs_swap ) + SwapData( &value, 8, 1 ); + memcpy( buffer.buffer+offset, &value, 8 ); + break; + } + + case FieldTypeString: + { + std::string value = field.GetValueString(); + memcpy( buffer.buffer+offset, value.c_str(), item_size ); + break; + } + + case FieldTypeCountedInt: + { + std::vector value = field.GetValueCountedInt(); + uint32 count = value.size(); + memcpy( buffer.buffer+offset, &count, 4 ); + if( count > 0 ) + { + memcpy( buffer.buffer+offset+4, &(value[0]), count * 4 ); + if( needs_swap ) + SwapData( buffer.buffer+offset, 4, count+1 ); + } + break; + } + + default: + assert( 0 ); + break; + } + + return offset + item_size; +} + +/************************************************************************/ +/* GetData() */ +/************************************************************************/ + +char *CPCIDSKVectorSegment::GetData( int section, uint32 offset, + int *bytes_available, int min_bytes, + bool update ) + +{ + if( min_bytes == 0 ) + min_bytes = 1; + +/* -------------------------------------------------------------------- */ +/* Select the section to act on. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer *pbuf = NULL; + uint32 *pbuf_offset = NULL; + bool *pbuf_dirty = NULL; + + if( section == sec_raw ) + { + pbuf = &raw_loaded_data; + pbuf_offset = &raw_loaded_data_offset; + pbuf_dirty = &raw_loaded_data_dirty; + } + else if( section == sec_vert ) + { + pbuf = &vert_loaded_data; + pbuf_offset = &vert_loaded_data_offset; + pbuf_dirty = &vert_loaded_data_dirty; + } + else if( section == sec_record ) + { + pbuf = &record_loaded_data; + pbuf_offset = &record_loaded_data_offset; + pbuf_dirty = &record_loaded_data_dirty; + } + +/* -------------------------------------------------------------------- */ +/* If the desired data is not within our loaded section, reload */ +/* one or more blocks around the request. */ +/* -------------------------------------------------------------------- */ + if( offset < *pbuf_offset + || offset+min_bytes > *pbuf_offset + pbuf->buffer_size ) + { + if( *pbuf_dirty ) + FlushDataBuffer( section ); + + // we want whole 8K blocks around the target region. + uint32 load_offset = offset - (offset % block_page_size); + int size = (offset + min_bytes - load_offset + block_page_size - 1); + + size -= (size % block_page_size); + + // If the request goes beyond the end of the file, and we are + // in update mode, grow the segment by writing at the end of + // the requested section. This will throw an exception if we + // are unable to grow the file. + if( section != sec_raw + && load_offset + size > di[section].GetIndex()->size() * block_page_size + && update ) + { + PCIDSKBuffer zerobuf(block_page_size); + + memset( zerobuf.buffer, 0, block_page_size ); + WriteSecToFile( section, zerobuf.buffer, + (load_offset + size) / block_page_size - 1, 1 ); + } + + *pbuf_offset = load_offset; + pbuf->SetSize( size ); + + ReadSecFromFile( section, pbuf->buffer, + load_offset / block_page_size, size / block_page_size ); + } + +/* -------------------------------------------------------------------- */ +/* If an update request goes beyond the end of the last data */ +/* byte in a data section, then update the bytes used. Now */ +/* read into our buffer. */ +/* -------------------------------------------------------------------- */ + if( section != sec_raw + && offset + min_bytes > di[section].GetSectionEnd() ) + di[section].SetSectionEnd( offset + min_bytes ); + +/* -------------------------------------------------------------------- */ +/* Return desired info. */ +/* -------------------------------------------------------------------- */ + if( bytes_available != NULL ) + *bytes_available = *pbuf_offset + pbuf->buffer_size - offset; + + if( update ) + *pbuf_dirty = true; + + return pbuf->buffer + offset - *pbuf_offset; +} + +/************************************************************************/ +/* ReadSecFromFile() */ +/* */ +/* Read one or more blocks from the desired "section" of the */ +/* segment data, going through the block pointer map for */ +/* vect/record sections. */ +/************************************************************************/ + +void CPCIDSKVectorSegment::ReadSecFromFile( int section, char *buffer, + int block_offset, + int block_count ) + +{ +/* -------------------------------------------------------------------- */ +/* Raw is a simple case, directly gulp. */ +/* -------------------------------------------------------------------- */ + if( section == sec_raw ) + { + ReadFromFile( buffer, block_offset*block_page_size, block_count*block_page_size ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Process one 8K block at a time in case they are discontigous */ +/* which they often are. */ +/* -------------------------------------------------------------------- */ + int i; + const std::vector *block_map = di[section].GetIndex(); + + assert( block_count + block_offset <= (int) block_map->size() ); + + for( i = 0; i < block_count; i++ ) + { + ReadFromFile( buffer + i * block_page_size, + block_page_size * (*block_map)[block_offset+i], + block_page_size ); + } +} + +/************************************************************************/ +/* FlushDataBuffer() */ +/* */ +/* Flush the indicated data buffer to disk if it is marked */ +/* dirty. */ +/************************************************************************/ + +void CPCIDSKVectorSegment::FlushDataBuffer( int section ) + +{ +/* -------------------------------------------------------------------- */ +/* Select the section to act on. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer *pbuf = NULL; + uint32 *pbuf_offset = NULL; + bool *pbuf_dirty = NULL; + + if( section == sec_raw ) + { + pbuf = &raw_loaded_data; + pbuf_offset = &raw_loaded_data_offset; + pbuf_dirty = &raw_loaded_data_dirty; + } + else if( section == sec_vert ) + { + pbuf = &vert_loaded_data; + pbuf_offset = &vert_loaded_data_offset; + pbuf_dirty = &vert_loaded_data_dirty; + } + else if( section == sec_record ) + { + pbuf = &record_loaded_data; + pbuf_offset = &record_loaded_data_offset; + pbuf_dirty = &record_loaded_data_dirty; + } + + if( ! *pbuf_dirty || pbuf->buffer_size == 0 ) + return; + +/* -------------------------------------------------------------------- */ +/* We need to write something. */ +/* -------------------------------------------------------------------- */ + assert( (pbuf->buffer_size % block_page_size) == 0 ); + assert( (*pbuf_offset % block_page_size) == 0 ); + + WriteSecToFile( section, pbuf->buffer, + *pbuf_offset / block_page_size, + pbuf->buffer_size / block_page_size ); + + *pbuf_dirty = false; +} + +/************************************************************************/ +/* WriteSecToFile() */ +/* */ +/* Read one or more blocks from the desired "section" of the */ +/* segment data, going through the block pointer map for */ +/* vect/record sections. */ +/************************************************************************/ + +void CPCIDSKVectorSegment::WriteSecToFile( int section, char *buffer, + int block_offset, + int block_count ) + +{ +/* -------------------------------------------------------------------- */ +/* Raw is a simple case, directly gulp. */ +/* -------------------------------------------------------------------- */ + if( section == sec_raw ) + { + WriteToFile( buffer, block_offset*block_page_size, + block_count*block_page_size ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Do we need to grow this data section to be able to do the */ +/* write? */ +/* -------------------------------------------------------------------- */ + const std::vector *block_map = di[section].GetIndex(); + + if( block_count + block_offset > (int) block_map->size() ) + { + vh.GrowBlockIndex( section, + block_count + block_offset - block_map->size() ); + } + +/* -------------------------------------------------------------------- */ +/* Process one 8K block at a time in case they are discontigous */ +/* which they often are. */ +/* -------------------------------------------------------------------- */ + int i; + for( i = 0; i < block_count; i++ ) + { + WriteToFile( buffer + i * block_page_size, + block_page_size * (*block_map)[block_offset+i], + block_page_size ); + } +} + +/************************************************************************/ +/* GetProjection() */ +/************************************************************************/ + +std::vector CPCIDSKVectorSegment::GetProjection( std::string &geosys ) + +{ + LoadHeader(); + +/* -------------------------------------------------------------------- */ +/* Fetch the projparms string from the proj section of the */ +/* vector segment header. */ +/* -------------------------------------------------------------------- */ + ShapeField projparms; + + ReadField( vh.section_offsets[hsec_proj]+32, projparms, + FieldTypeString, sec_raw ); + +/* -------------------------------------------------------------------- */ +/* Read the geosys (units) string from SDH5.VEC1 in the segment */ +/* header. */ +/* -------------------------------------------------------------------- */ + GetHeader().Get( 160, 16, geosys, 0 ); // do not unpad! + + return ProjParmsFromText( geosys, projparms.GetValueString() ); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +void CPCIDSKVectorSegment::SetProjection( std::string geosys, + std::vector parms ) + +{ + LoadHeader(); + +/* -------------------------------------------------------------------- */ +/* Apply parameters in the vector segment "proj" header section. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer proj(32); + uint32 proj_size; + ShapeField value; + + value.SetValue( ProjParmsToText( parms ) ); + + ReadFromFile( proj.buffer, vh.section_offsets[hsec_proj], 32 ); + proj_size = WriteField( 32, value, proj ); + + vh.GrowSection( hsec_proj, proj_size ); + WriteToFile( proj.buffer, vh.section_offsets[hsec_proj], proj_size ); + +/* -------------------------------------------------------------------- */ +/* Write the geosys string to the generic segment header. */ +/* -------------------------------------------------------------------- */ + GetHeader().Put( geosys.c_str(), 160, 16 ); + FlushHeader(); +} + +/************************************************************************/ +/* IndexFromShapeId() */ +/* */ +/* Translate a shapeid into a shape index. Several mechanisms */ +/* are used to accelerate this when possible. */ +/************************************************************************/ + +int CPCIDSKVectorSegment::IndexFromShapeId( ShapeId id ) + +{ + if( id == NullShapeId ) + return -1; + + LoadHeader(); + +/* -------------------------------------------------------------------- */ +/* Does this match our last lookup? */ +/* -------------------------------------------------------------------- */ + if( id == last_shapes_id ) + return last_shapes_index; + +/* -------------------------------------------------------------------- */ +/* Is this the next shapeid in sequence, and is it in our */ +/* loaded index cache? */ +/* -------------------------------------------------------------------- */ + if( id == last_shapes_id + 1 + && last_shapes_index + 1 >= shape_index_start + && last_shapes_index + 1 < shape_index_start + (int) shape_index_ids.size() ) + { + last_shapes_index++; + last_shapes_id++; + return last_shapes_index; + } + +/* -------------------------------------------------------------------- */ +/* Activate the shapeid map, if it is not already active. */ +/* -------------------------------------------------------------------- */ + if( !shapeid_map_active ) + { + PopulateShapeIdMap(); + } + +/* -------------------------------------------------------------------- */ +/* Is this already in our shapeid map? */ +/* -------------------------------------------------------------------- */ + if( shapeid_map.count( id ) == 1 ) + return shapeid_map[id]; + + return -1; +} + +/************************************************************************/ +/* LoadShapeIdPage() */ +/************************************************************************/ + +void CPCIDSKVectorSegment::LoadShapeIdPage( int page ) + +{ +/* -------------------------------------------------------------------- */ +/* Load a chunk of shape index information into a */ +/* PCIDSKBuffer. */ +/* -------------------------------------------------------------------- */ + uint32 shape_index_byte_offset = + vh.section_offsets[hsec_shape] + + di[sec_record].offset_on_disk_within_section + + di[sec_record].size_on_disk + 4; + + int entries_to_load = shapeid_page_size; + + shape_index_start = page * shapeid_page_size; + if( shape_index_start + entries_to_load > shape_count ) + entries_to_load = shape_count - shape_index_start; + + PCIDSKBuffer wrk_index; + wrk_index.SetSize( entries_to_load * 12 ); + + ReadFromFile( wrk_index.buffer, + shape_index_byte_offset + shape_index_start*12, + wrk_index.buffer_size ); + +/* -------------------------------------------------------------------- */ +/* Parse into the vectors for easier use. */ +/* -------------------------------------------------------------------- */ + int i; + + shape_index_ids.resize( entries_to_load ); + shape_index_vertex_off.resize( entries_to_load ); + shape_index_record_off.resize( entries_to_load ); + + for( i = 0; i < entries_to_load; i++ ) + { + memcpy( &(shape_index_ids[i]), wrk_index.buffer + i*12, 4 ); + memcpy( &(shape_index_vertex_off[i]), wrk_index.buffer + i*12+4, 4 ); + memcpy( &(shape_index_record_off[i]), wrk_index.buffer + i*12+8, 4 ); + } + + if( needs_swap && entries_to_load > 0 ) + { + SwapData( &(shape_index_ids[0]), 4, entries_to_load ); + SwapData( &(shape_index_vertex_off[0]), 4, entries_to_load ); + SwapData( &(shape_index_record_off[0]), 4, entries_to_load ); + } + + PushLoadedIndexIntoMap(); +} + +/************************************************************************/ +/* AccessShapeByIndex() */ +/* */ +/* This method is responsible for loading the set of */ +/* information for shape "shape_index" into the shape_index data */ +/* structures if it is not already there. */ +/************************************************************************/ + +void CPCIDSKVectorSegment::AccessShapeByIndex( int shape_index ) + +{ + LoadHeader(); + +/* -------------------------------------------------------------------- */ +/* Is the requested index already loaded? */ +/* -------------------------------------------------------------------- */ + if( shape_index >= shape_index_start + && shape_index < shape_index_start + (int) shape_index_ids.size() ) + return; + + // this is for requesting the next shapeindex after shapecount on + // a partial page. + if( shape_index == shape_count + && (int) shape_index_ids.size() < shapeid_page_size + && shape_count == (int) shape_index_ids.size() + shape_index_start ) + return; + +/* -------------------------------------------------------------------- */ +/* If the currently loaded shapeindex is dirty, we should write */ +/* it now. */ +/* -------------------------------------------------------------------- */ + FlushLoadedShapeIndex(); + +/* -------------------------------------------------------------------- */ +/* Load the page of shapeid information for this shape index. */ +/* -------------------------------------------------------------------- */ + LoadShapeIdPage( shape_index / shapeid_page_size ); +} + +/************************************************************************/ +/* PushLoadedIndexIntoMap() */ +/************************************************************************/ + +void CPCIDSKVectorSegment::PushLoadedIndexIntoMap() + +{ +/* -------------------------------------------------------------------- */ +/* If the shapeid map is active, apply the current pages */ +/* shapeids if it does not already appear to have been */ +/* applied. */ +/* -------------------------------------------------------------------- */ + int loaded_page = shape_index_start / shapeid_page_size; + + if( shapeid_map_active && shape_index_ids.size() > 0 ) + { + unsigned int i; + + for( i = 0; i < shape_index_ids.size(); i++ ) + { + if( shape_index_ids[i] != NullShapeId ) + shapeid_map[shape_index_ids[i]] = i+shape_index_start; + } + + if( loaded_page == shapeid_pages_certainly_mapped+1 ) + shapeid_pages_certainly_mapped++; + } +} + +/************************************************************************/ +/* PopulateShapeIdMap() */ +/* */ +/* Completely populate the shapeid->index map. */ +/************************************************************************/ + +void CPCIDSKVectorSegment::PopulateShapeIdMap() + +{ +/* -------------------------------------------------------------------- */ +/* Enable shapeid_map mode, and load the current page. */ +/* -------------------------------------------------------------------- */ + if( !shapeid_map_active ) + { + shapeid_map_active = true; + PushLoadedIndexIntoMap(); + } + +/* -------------------------------------------------------------------- */ +/* Load all outstanding pages. */ +/* -------------------------------------------------------------------- */ + int shapeid_pages = (shape_count+shapeid_page_size-1) / shapeid_page_size; + + while( shapeid_pages_certainly_mapped+1 < shapeid_pages ) + { + LoadShapeIdPage( shapeid_pages_certainly_mapped+1 ); + } +} + +/************************************************************************/ +/* FindFirst() */ +/************************************************************************/ + +ShapeId CPCIDSKVectorSegment::FindFirst() +{ + LoadHeader(); + + if( shape_count == 0 ) + return NullShapeId; + + AccessShapeByIndex( 0 ); + + last_shapes_id = shape_index_ids[0]; + last_shapes_index = 0; + + return last_shapes_id; +} + +/************************************************************************/ +/* FindNext() */ +/************************************************************************/ + +ShapeId CPCIDSKVectorSegment::FindNext( ShapeId previous_id ) +{ + if( previous_id == NullShapeId ) + return FindFirst(); + + int previous_index = IndexFromShapeId( previous_id ); + + if( previous_index == shape_count - 1 ) + return NullShapeId; + + AccessShapeByIndex( previous_index+1 ); + + last_shapes_index = previous_index+1; + last_shapes_id = shape_index_ids[last_shapes_index - shape_index_start]; + + return last_shapes_id; +} + +/************************************************************************/ +/* GetShapeCount() */ +/************************************************************************/ + +int CPCIDSKVectorSegment::GetShapeCount() + +{ + LoadHeader(); + + return shape_count; +} + +/************************************************************************/ +/* GetVertices() */ +/************************************************************************/ + +void CPCIDSKVectorSegment::GetVertices( ShapeId shape_id, + std::vector &vertices ) + +{ + int shape_index = IndexFromShapeId( shape_id ); + + if( shape_index == -1 ) + ThrowPCIDSKException( "Attempt to call GetVertices() on non-existing shape id '%d'.", + (int) shape_id ); + + AccessShapeByIndex( shape_index ); + + uint32 vert_off = shape_index_vertex_off[shape_index - shape_index_start]; + uint32 vertex_count; + + if( vert_off == 0xffffffff ) + { + vertices.resize(0); + return; + } + + memcpy( &vertex_count, GetData( sec_vert, vert_off+4, NULL, 4 ), 4 ); + if( needs_swap ) + SwapData( &vertex_count, 4, 1 ); + + vertices.resize( vertex_count ); + + // We ought to change this to process the available data and + // then request more. + if( vertex_count > 0 ) + { + memcpy( &(vertices[0]), + GetData( sec_vert, vert_off+8, NULL, vertex_count*24), + vertex_count * 24 ); + if( needs_swap ) + SwapData( &(vertices[0]), 8, vertex_count*3 ); + } +} + +/************************************************************************/ +/* GetFieldCount() */ +/************************************************************************/ + +int CPCIDSKVectorSegment::GetFieldCount() + +{ + LoadHeader(); + + return vh.field_names.size(); +} + +/************************************************************************/ +/* GetFieldName() */ +/************************************************************************/ + +std::string CPCIDSKVectorSegment::GetFieldName( int field_index ) + +{ + LoadHeader(); + + return vh.field_names[field_index]; +} + +/************************************************************************/ +/* GetFieldDescription() */ +/************************************************************************/ + +std::string CPCIDSKVectorSegment::GetFieldDescription( int field_index ) + +{ + LoadHeader(); + + return vh.field_descriptions[field_index]; +} + +/************************************************************************/ +/* GetFieldType() */ +/************************************************************************/ + +ShapeFieldType CPCIDSKVectorSegment::GetFieldType( int field_index ) + +{ + LoadHeader(); + + return vh.field_types[field_index]; +} + +/************************************************************************/ +/* GetFieldFormat() */ +/************************************************************************/ + +std::string CPCIDSKVectorSegment::GetFieldFormat( int field_index ) + +{ + LoadHeader(); + + return vh.field_formats[field_index]; +} + +/************************************************************************/ +/* GetFieldDefault() */ +/************************************************************************/ + +ShapeField CPCIDSKVectorSegment::GetFieldDefault( int field_index ) + +{ + LoadHeader(); + + return vh.field_defaults[field_index]; +} + +/************************************************************************/ +/* GetFields() */ +/************************************************************************/ + +void CPCIDSKVectorSegment::GetFields( ShapeId id, + std::vector& list ) + +{ + unsigned int i; + int shape_index = IndexFromShapeId( id ); + + if( shape_index == -1 ) + ThrowPCIDSKException( "Attempt to call GetFields() on non-existing shape id '%d'.", + (int) id ); + + AccessShapeByIndex( shape_index ); + + uint32 offset = shape_index_record_off[shape_index - shape_index_start]; + + list.resize(vh.field_names.size()); + + if( offset == 0xffffffff ) + { + for( i = 0; i < vh.field_names.size(); i++ ) + list[i] = vh.field_defaults[i]; + } + else + { + offset += 4; // skip size + + for( i = 0; i < vh.field_names.size(); i++ ) + offset = ReadField( offset, list[i], vh.field_types[i], sec_record ); + } +} + +/************************************************************************/ +/* AddField() */ +/************************************************************************/ + +void CPCIDSKVectorSegment::AddField( std::string name, ShapeFieldType type, + std::string description, + std::string format, + ShapeField *default_value ) + +{ + ShapeField fallback_default; + + LoadHeader(); + +/* -------------------------------------------------------------------- */ +/* If no default is provided, use the obvious value. */ +/* -------------------------------------------------------------------- */ + if( default_value == NULL ) + { + switch( type ) + { + case FieldTypeFloat: + fallback_default.SetValue( (float) 0.0 ); + break; + case FieldTypeDouble: + fallback_default.SetValue( (double) 0.0 ); + break; + case FieldTypeInteger: + fallback_default.SetValue( (int32) 0 ); + break; + case FieldTypeCountedInt: + { + std::vector empty_list; + fallback_default.SetValue( empty_list ); + break; + } + case FieldTypeString: + fallback_default.SetValue( "" ); + break; + + case FieldTypeNone: + break; + } + + default_value = &fallback_default; + } + +/* -------------------------------------------------------------------- */ +/* Make sure the default field is of the correct type. */ +/* -------------------------------------------------------------------- */ + if( default_value->GetType() != type ) + { + ThrowPCIDSKException( "Attempt to add field with a default value of " + "a different type than the field." ); + } + + if( type == FieldTypeNone ) + { + ThrowPCIDSKException( "Creating fields of type None not supported." ); + } + +/* -------------------------------------------------------------------- */ +/* Add the field to the definition list. */ +/* -------------------------------------------------------------------- */ + vh.field_names.push_back( name ); + vh.field_types.push_back( type ); + vh.field_descriptions.push_back( description ); + vh.field_formats.push_back( format ); + vh.field_defaults.push_back( *default_value ); + + vh.WriteFieldDefinitions(); + +/* -------------------------------------------------------------------- */ +/* If we have existing features, we should go through adding */ +/* this new field. */ +/* -------------------------------------------------------------------- */ + if( shape_count > 0 ) + { + ThrowPCIDSKException( "Support for adding fields in populated layers " + "has not yet been implemented." ); + } +} + +/************************************************************************/ +/* CreateShape() */ +/************************************************************************/ + +ShapeId CPCIDSKVectorSegment::CreateShape( ShapeId id ) + +{ + LoadHeader(); + +/* -------------------------------------------------------------------- */ +/* Make sure we have the last shapeid index page loaded. */ +/* -------------------------------------------------------------------- */ + AccessShapeByIndex( shape_count ); + +/* -------------------------------------------------------------------- */ +/* Do we need to assign a shapeid? */ +/* -------------------------------------------------------------------- */ + if( id == NullShapeId ) + { + if( highest_shapeid_used == NullShapeId ) + id = 0; + else + id = highest_shapeid_used + 1; + } + if( id > highest_shapeid_used ) + highest_shapeid_used = id; + else + { + PopulateShapeIdMap(); + if( shapeid_map.count(id) > 0 ) + { + ThrowPCIDSKException( "Attempt to create a shape with id '%d', but that already exists.", id ); + } + } + +/* -------------------------------------------------------------------- */ +/* Push this new shape on to our list of shapeids in the */ +/* current page, and mark the page as dirty. */ +/* -------------------------------------------------------------------- */ + shape_index_ids.push_back( id ); + shape_index_record_off.push_back( 0xffffffff ); + shape_index_vertex_off.push_back( 0xffffffff ); + shape_index_page_dirty = true; + + if( shapeid_map_active ) + shapeid_map[id] = shape_count; + + shape_count++; + + return id; +} + +/************************************************************************/ +/* DeleteShape() */ +/* */ +/* Delete a shape by shapeid. */ +/************************************************************************/ + +void CPCIDSKVectorSegment::DeleteShape( ShapeId id ) + +{ + int shape_index = IndexFromShapeId( id ); + + if( shape_index == -1 ) + ThrowPCIDSKException( "Attempt to call DeleteShape() on non-existing shape '%d'.", + (int) id ); + +/* ==================================================================== */ +/* Our strategy is to move the last shape in our index down to */ +/* replace the shape that we are deleting. Unfortunately this */ +/* will result in an out of sequence shapeid, but it is hard to */ +/* avoid that without potentially rewriting much of the shape */ +/* index. */ +/* */ +/* Note that the following sequence *does* work for special */ +/* cases like deleting the last shape in the list, or deleting */ +/* a shape on the same page as the last shape. At worst a wee */ +/* bit of extra work is done. */ +/* ==================================================================== */ + +/* -------------------------------------------------------------------- */ +/* Load the page of shapeids containing the last shape in our */ +/* index, capture the last shape's details, and remove it. */ +/* -------------------------------------------------------------------- */ + + uint32 vert_off, rec_off; + ShapeId last_id; + + AccessShapeByIndex( shape_count-1 ); + + last_id = shape_index_ids[shape_count-1-shape_index_start]; + vert_off = shape_index_vertex_off[shape_count-1-shape_index_start]; + rec_off = shape_index_record_off[shape_count-1-shape_index_start]; + + // We don't actually have to modify this area of the index on disk. + // Some of the stuff at the end just becomes unreferenced when we + // decrement shape_count. + +/* -------------------------------------------------------------------- */ +/* Load the page with the shape we are deleting, and put last */ +/* the shapes information over it. */ +/* -------------------------------------------------------------------- */ + AccessShapeByIndex( shape_index ); + + shape_index_ids[shape_index-shape_index_start] = last_id; + shape_index_vertex_off[shape_index-shape_index_start] = vert_off; + shape_index_record_off[shape_index-shape_index_start] = rec_off; + + shape_index_page_dirty = true; + + if( shapeid_map_active ) + shapeid_map.erase( id ); + + shape_count--; +} + +/************************************************************************/ +/* SetVertices() */ +/************************************************************************/ + +void CPCIDSKVectorSegment::SetVertices( ShapeId id, + const std::vector& list ) + +{ + int shape_index = IndexFromShapeId( id ); + + if( shape_index == -1 ) + ThrowPCIDSKException( "Attempt to call SetVertices() on non-existing shape '%d'.", + (int) id ); + + PCIDSKBuffer vbuf( list.size() * 24 + 8 ); + + AccessShapeByIndex( shape_index ); + +/* -------------------------------------------------------------------- */ +/* Is the current space big enough to hold the new vertex set? */ +/* -------------------------------------------------------------------- */ + uint32 vert_off = shape_index_vertex_off[shape_index - shape_index_start]; + uint32 chunk_size; + + if( vert_off != 0xffffffff ) + { + memcpy( &chunk_size, GetData( sec_vert, vert_off, NULL, 4 ), 4 ); + if( needs_swap ) + SwapData( &chunk_size, 4, 1 ); + + if( chunk_size < (uint32) vbuf.buffer_size ) + { + vert_off = 0xffffffff; + } + } + +/* -------------------------------------------------------------------- */ +/* Do we need to put this at the end of the section? */ +/* -------------------------------------------------------------------- */ + if( vert_off == 0xffffffff ) + { + vert_off = di[sec_vert].GetSectionEnd(); + chunk_size = vbuf.buffer_size; + } + +/* -------------------------------------------------------------------- */ +/* Format the vertices in a buffer. */ +/* -------------------------------------------------------------------- */ + uint32 vert_count = list.size(); + unsigned int i; + + memcpy( vbuf.buffer, &chunk_size, 4 ); + memcpy( vbuf.buffer+4, &vert_count, 4 ); + if( needs_swap ) + SwapData( vbuf.buffer, 4, 2 ); + + for( i = 0; i < vert_count; i++ ) + { + memcpy( vbuf.buffer + 8 + i*24 + 0, &(list[i].x), 8 ); + memcpy( vbuf.buffer + 8 + i*24 + 8, &(list[i].y), 8 ); + memcpy( vbuf.buffer + 8 + i*24 + 16, &(list[i].z), 8 ); + } + + if( needs_swap ) + SwapData( vbuf.buffer + 8, 8, 3*vert_count ); + +/* -------------------------------------------------------------------- */ +/* Write the data into the working buffer. */ +/* -------------------------------------------------------------------- */ + memcpy( GetData( sec_vert, vert_off, NULL, vbuf.buffer_size, true ), + vbuf.buffer, vbuf.buffer_size ); + +/* -------------------------------------------------------------------- */ +/* Record the offset */ +/* -------------------------------------------------------------------- */ + if( shape_index_vertex_off[shape_index - shape_index_start] != vert_off ) + { + shape_index_vertex_off[shape_index - shape_index_start] = vert_off; + shape_index_page_dirty = true; + } +} + +/************************************************************************/ +/* SetFields() */ +/************************************************************************/ + +void CPCIDSKVectorSegment::SetFields( ShapeId id, + const std::vector& list_in ) + +{ + uint32 i; + int shape_index = IndexFromShapeId( id ); + std::vector full_list; + const std::vector *listp = NULL; + + if( shape_index == -1 ) + ThrowPCIDSKException( "Attempt to call SetFields() on non-existing shape id '%d'.", + (int) id ); + + if( list_in.size() > vh.field_names.size() ) + { + ThrowPCIDSKException( + "Attempt to write %d fields to a layer with only %d fields.", + list_in.size(), vh.field_names.size() ); + } + + if( list_in.size() < vh.field_names.size() ) + { + full_list = list_in; + + // fill out missing fields in list with defaults. + for( i = list_in.size(); i < vh.field_names.size(); i++ ) + full_list[i] = vh.field_defaults[i]; + + listp = &full_list; + } + else + listp = &list_in; + + AccessShapeByIndex( shape_index ); + +/* -------------------------------------------------------------------- */ +/* Format the fields in the buffer. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer fbuf(4); + uint32 offset = 4; + + for( i = 0; i < listp->size(); i++ ) + offset = WriteField( offset, (*listp)[i], fbuf ); + + fbuf.SetSize( offset ); + +/* -------------------------------------------------------------------- */ +/* Is the current space big enough to hold the new field set? */ +/* -------------------------------------------------------------------- */ + uint32 rec_off = shape_index_record_off[shape_index - shape_index_start]; + uint32 chunk_size = offset; + + if( rec_off != 0xffffffff ) + { + memcpy( &chunk_size, GetData( sec_record, rec_off, NULL, 4 ), 4 ); + if( needs_swap ) + SwapData( &chunk_size, 4, 1 ); + + if( chunk_size < (uint32) fbuf.buffer_size ) + { + rec_off = 0xffffffff; + } + } + +/* -------------------------------------------------------------------- */ +/* Do we need to put this at the end of the section? */ +/* -------------------------------------------------------------------- */ + if( rec_off == 0xffffffff ) + { + rec_off = di[sec_record].GetSectionEnd(); + chunk_size = fbuf.buffer_size; + } + +/* -------------------------------------------------------------------- */ +/* Set the chunk size, and number of fields. */ +/* -------------------------------------------------------------------- */ + memcpy( fbuf.buffer + 0, &chunk_size, 4 ); + + if( needs_swap ) + SwapData( fbuf.buffer, 4, 1 ); + +/* -------------------------------------------------------------------- */ +/* Write the data into the working buffer. */ +/* -------------------------------------------------------------------- */ + memcpy( GetData( sec_record, rec_off, NULL, fbuf.buffer_size, true ), + fbuf.buffer, fbuf.buffer_size ); + +/* -------------------------------------------------------------------- */ +/* Record the offset */ +/* -------------------------------------------------------------------- */ + if( shape_index_record_off[shape_index - shape_index_start] != rec_off ) + { + shape_index_record_off[shape_index - shape_index_start] = rec_off; + shape_index_page_dirty = true; + } +} + +/************************************************************************/ +/* FlushLoadedShapeIndex() */ +/************************************************************************/ + +void CPCIDSKVectorSegment::FlushLoadedShapeIndex() + +{ + if( !shape_index_page_dirty ) + return; + + uint32 offset = vh.ShapeIndexPrepare( shape_count * 12 + 4 ); + + PCIDSKBuffer write_buffer( shapeid_page_size * 12 ); + + // Update the count field. + memcpy( write_buffer.buffer, &shape_count, 4 ); + if( needs_swap ) + SwapData( write_buffer.buffer, 4, 1 ); + WriteToFile( write_buffer.buffer, offset, 4 ); + + // Write out the page of shapeid information. + unsigned int i; + for( i = 0; i < shape_index_ids.size(); i++ ) + { + memcpy( write_buffer.buffer + 12*i, + &(shape_index_ids[i]), 4 ); + memcpy( write_buffer.buffer + 12*i + 4, + &(shape_index_vertex_off[i]), 4 ); + memcpy( write_buffer.buffer + 12*i + 8, + &(shape_index_record_off[i]), 4 ); + } + + if( needs_swap ) + SwapData( write_buffer.buffer, 4, shape_index_ids.size() * 3 ); + + WriteToFile( write_buffer.buffer, + offset + 4 + shape_index_start * 12, + 12 * shape_index_ids.size() ); + + // invalidate the raw buffer. + raw_loaded_data.buffer_size = 0; + + + shape_index_page_dirty = false; +} + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskvectorsegment.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskvectorsegment.h new file mode 100644 index 000000000..aeb6df782 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskvectorsegment.h @@ -0,0 +1,179 @@ +/****************************************************************************** + * + * Purpose: Declaration of the CPCIDSKVectorSegment class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_SEGMENT_PCIDSKVECTORSEGMENT_H +#define __INCLUDE_SEGMENT_PCIDSKVECTORSEGMENT_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_vectorsegment.h" +#include "pcidsk_buffer.h" +#include "segment/cpcidsksegment.h" +#include "segment/vecsegheader.h" +#include "segment/vecsegdataindex.h" + +#include +#include + +namespace PCIDSK +{ + class PCIDSKFile; + + const int sec_vert = 0; + const int sec_record = 1; + const int sec_raw = 2; + + /************************************************************************/ + /* CPCIDSKVectorSegment */ + /************************************************************************/ + + class CPCIDSKVectorSegment : virtual public CPCIDSKSegment, + public PCIDSKVectorSegment + { + friend class VecSegHeader; + friend class VecSegDataIndex; + + public: + CPCIDSKVectorSegment( PCIDSKFile *file, int segment, + const char *segment_pointer ); + + virtual ~CPCIDSKVectorSegment(); + + void Initialize(); + void Synchronize(); + + std::string GetRst() { return ""; } + std::vector GetProjection( std::string &geosys ); + void SetProjection(std::string geosys, + std::vector parms); + + int GetFieldCount(); + std::string GetFieldName(int); + std::string GetFieldDescription(int); + ShapeFieldType GetFieldType(int); + std::string GetFieldFormat(int); + ShapeField GetFieldDefault(int); + + ShapeIterator begin() { return ShapeIterator(this); } + ShapeIterator end() { return ShapeIterator(this,NullShapeId); } + + ShapeId FindFirst(); + ShapeId FindNext(ShapeId); + + int GetShapeCount(); + + void GetVertices( ShapeId, std::vector& ); + void GetFields( ShapeId, std::vector& ); + + void AddField( std::string name, ShapeFieldType type, + std::string description, + std::string format, + ShapeField *default_value ); + + ShapeId CreateShape( ShapeId id ); + void DeleteShape( ShapeId id ); + void SetVertices( ShapeId id, + const std::vector& list ); + void SetFields( ShapeId id, + const std::vector& list ); + + std::string ConsistencyCheck(); + + // Essentially internal stuff. + char *GetData( int section, uint32 offset, + int *bytes_available = NULL, + int min_bytes = 0, + bool update = false ); + uint32 ReadField( uint32 offset, + ShapeField& field, + ShapeFieldType field_type, + int section = sec_record ); + + uint32 WriteField( uint32 offset, + const ShapeField& field, + PCIDSKBuffer &buffer ); + void ReadSecFromFile( int section, char *buffer, + int block_offset, + int block_count ); + void WriteSecToFile( int section, char *buffer, + int block_offset, + int block_count ); + + private: + bool base_initialized; + bool needs_swap; + + VecSegHeader vh; + VecSegDataIndex di[2]; + + int32 shape_count; + ShapeId highest_shapeid_used; + //ShapeId first_shape_id; + //ShapeId last_shape_id; + + int32 shape_index_start; // index of first shape + std::vector shape_index_ids; // loaded shape ids. + std::vector shape_index_vertex_off; // loaded vertex offsets + std::vector shape_index_record_off; // loaded record offsets. + bool shape_index_page_dirty; + + ShapeId last_shapes_id; + int last_shapes_index; + + bool shapeid_map_active; + std::map shapeid_map; + int shapeid_pages_certainly_mapped; + + void AccessShapeByIndex( int iIndex ); + int IndexFromShapeId( ShapeId id ); + void LoadShapeIdPage( int page ); + void FlushLoadedShapeIndex(); + void PushLoadedIndexIntoMap(); + void PopulateShapeIdMap(); + + // Cached buffers for GetData(); + PCIDSKBuffer raw_loaded_data; + uint32 raw_loaded_data_offset; + bool raw_loaded_data_dirty; + + PCIDSKBuffer vert_loaded_data; + uint32 vert_loaded_data_offset; + bool vert_loaded_data_dirty; + + PCIDSKBuffer record_loaded_data; + uint32 record_loaded_data_offset; + bool record_loaded_data_dirty; + + void FlushDataBuffer( int section ); + void LoadHeader(); + + std::string ConsistencyCheck_Header(); + std::string ConsistencyCheck_DataIndices(); + std::string ConsistencyCheck_ShapeIndices(); + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_VECTORSEGMENT_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskvectorsegment_consistencycheck.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskvectorsegment_consistencycheck.cpp new file mode 100644 index 000000000..f4f7c9c6e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/cpcidskvectorsegment_consistencycheck.cpp @@ -0,0 +1,344 @@ +/****************************************************************************** + * + * Purpose: Implementation of the CPCIDSKVectorSegment class's + * ConsistencyCheck() method. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_file.h" +#include "pcidsk_exception.h" +#include "core/pcidsk_utils.h" +#include "segment/cpcidskvectorsegment.h" +#include +#include + +using namespace PCIDSK; + +/* -------------------------------------------------------------------- */ +/* Size of a block in the record/vertice block tables. This is */ +/* determined by the PCIDSK format and may not be changed. */ +/* -------------------------------------------------------------------- */ +static const int block_page_size = 8192; + +/************************************************************************/ +/* ==================================================================== */ +/* SpaceMap */ +/* */ +/* Helper class to track space allocations. */ +/* ==================================================================== */ +/************************************************************************/ + +class SpaceMap +{ +public: + std::vector offsets; + std::vector sizes; + + // binary search for the offset closes to our target or earlier. + uint32 FindPreceding( uint32 offset ) + { + if( offsets.size() == 0 ) + return 0; + + uint32 start=0, end=offsets.size()-1; + + while( end > start ) + { + uint32 middle = (start+end+1) / 2; + if( offsets[middle] > offset ) + end = middle-1; + else if( offsets[middle] < offset ) + start = middle; + else + return middle; + } + + return start; + } + + bool AddChunk( uint32 offset, uint32 size ) + { + uint32 preceding = FindPreceding( offset ); + + // special case for empty + if( offsets.size() == 0 ) + { + offsets.push_back( offset ); + sizes.push_back( size ); + return false; + } + + // special case for before first. + if( offsets.size() > 0 && offset < offsets[0] ) + { + if( offset+size > offsets[0] ) + return true; + + if( offset+size == offsets[0] ) + { + offsets[0] = offset; + sizes[0] += size; + } + else + { + offsets.insert( offsets.begin(), offset ); + sizes.insert( sizes.begin(), size ); + } + return false; + } + + if( offsets[preceding] + sizes[preceding] > offset ) + { + // conflict! + return true; + } + + if( preceding+1 < offsets.size() + && offsets[preceding+1] < offset+size ) + { + // conflict! + return true; + } + + // can we merge into preceding entry? + if( offsets[preceding] + sizes[preceding] == offset ) + { + sizes[preceding] += size; + return false; + } + + // can we merge into following entry? + if( preceding+1 < offsets.size() + && offsets[preceding+1] == offset+size ) + { + offsets[preceding+1] = offset; + sizes[preceding+1] += size; + return false; + } + + // Insert after preceding. + offsets.insert( offsets.begin() + (preceding + 1), offset ); + sizes.insert( sizes.begin() + (preceding + 1), size ); + + return false; + } +}; + + + +/************************************************************************/ +/* ConsistencyCheck() */ +/************************************************************************/ + +std::string CPCIDSKVectorSegment::ConsistencyCheck() + +{ + Synchronize(); + + std::string report = CPCIDSKSegment::ConsistencyCheck(); + + report += ConsistencyCheck_Header(); + report += ConsistencyCheck_DataIndices(); + report += ConsistencyCheck_ShapeIndices(); + + if( report != "" ) + fprintf( stderr, "ConsistencyCheck() Report:\n%s", report.c_str() ); + + return report; +} + +/************************************************************************/ +/* ConsistencyCheck_Header() */ +/* */ +/* Check that the header sections are non-overlapping and fit */ +/* in the blocks indicated. */ +/* */ +/* Verify some "fixed" values. */ +/************************************************************************/ + +std::string CPCIDSKVectorSegment::ConsistencyCheck_Header() + +{ + std::string report; + + LoadHeader(); + + if( vh.header_blocks < 1 ) + report += "less than one header_blocks\n"; + + if( vh.header_blocks * block_page_size > GetContentSize() ) + report += "header blocks larger than segment size!"; + + + SpaceMap smap; + int i; + + for( i = 0; i < 4; i++ ) + { + if( smap.AddChunk( vh.section_offsets[i], vh.section_sizes[i] ) ) + report += "A header section overlaps another header section!\n"; + + if( vh.section_offsets[i] + vh.section_sizes[i] + > vh.header_blocks * block_page_size ) + report += "A header section goes past end of header.\n"; + } + + return report; +} + +/************************************************************************/ +/* ConsistencyCheck_DataIndices() */ +/************************************************************************/ + +std::string CPCIDSKVectorSegment::ConsistencyCheck_DataIndices() + +{ + std::string report; + unsigned int section; + + SpaceMap smap; + + smap.AddChunk( 0, vh.header_blocks ); + + for( section = 0; section < 2; section++ ) + { + const std::vector *map = di[section].GetIndex(); + unsigned int i; + + for( i = 0; i < map->size(); i++ ) + { + if( smap.AddChunk( (*map)[i], 1 ) ) + { + char msg[100]; + + sprintf( msg, "Conflict for block %d, held by at least data index '%d'.\n", + (*map)[i], section ); + + report += msg; + } + } + + if( di[section].bytes > di[section].block_count * block_page_size ) + { + report += "bytes for data index to large for block count.\n"; + } + } + + return report; +} + +/************************************************************************/ +/* ConsistencyCheck_ShapeIndices() */ +/************************************************************************/ + +std::string CPCIDSKVectorSegment::ConsistencyCheck_ShapeIndices() + +{ + std::string report; + SpaceMap vmap, rmap; + std::map id_map; + int iShape; + + for( iShape = 0; iShape < shape_count; iShape++ ) + { + AccessShapeByIndex( iShape ); + + unsigned int toff = iShape - shape_index_start; + + if( id_map.count(shape_index_ids[toff]) > 0 ) + { + char msg[100]; + + sprintf( msg, "ShapeID %d is used for shape %d and %d!\n", + shape_index_ids[toff], + toff, id_map[shape_index_ids[toff]]); + report += msg; + } + + id_map[shape_index_ids[toff]] = toff; + + + if( shape_index_vertex_off[toff] != 0xffffffff ) + { + uint32 vertex_count; + uint32 vertex_size; + uint32 vert_off = shape_index_vertex_off[toff]; + + memcpy( &vertex_size, GetData( sec_vert, vert_off, NULL, 4 ), 4 ); + memcpy( &vertex_count, GetData( sec_vert, vert_off+4, NULL, 4 ), 4 ); + if( needs_swap ) + { + SwapData( &vertex_count, 4, 1 ); + SwapData( &vertex_size, 4, 1 ); + } + + if( vertex_size < vertex_count * 24 + 8 ) + { + report += "vertices for shape index seem larger than space allocated.\n"; + } + + if( vert_off + vertex_size > di[sec_vert].GetSectionEnd() ) + { + report += "record overruns data index bytes.\n"; + } + + if( vmap.AddChunk( vert_off, vertex_size ) ) + { + report += "vertex overlap detected!\n"; + } + } + + if( shape_index_record_off[toff] != 0xffffffff ) + { + uint32 rec_off = shape_index_record_off[toff]; + uint32 offset = rec_off; + uint32 record_size, i; + ShapeField wfld; + + memcpy( &record_size, GetData( sec_record, rec_off, NULL, 4 ), 4 ); + if( needs_swap ) + SwapData( &record_size, 4, 1 ); + + offset += 4; + for( i = 0; i < vh.field_names.size(); i++ ) + offset = ReadField( offset, wfld, vh.field_types[i], + sec_record ); + + if( offset - rec_off > record_size ) + report += "record actually larger than declared record size.\n"; + + if( rec_off + record_size > di[sec_record].GetSectionEnd() ) + { + report += "record overruns data index bytes.\n"; + } + + if( rmap.AddChunk( rec_off, record_size ) ) + { + report += "record overlap detected!\n"; + } + } + } + + return report; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/metadatasegment.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/metadatasegment.h new file mode 100644 index 000000000..a857a6da1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/metadatasegment.h @@ -0,0 +1,75 @@ +/****************************************************************************** + * + * Purpose: Declaration of the MetadataSegment class. + * + * This class is used to manage access to the SYS METADATA segment. This + * segment holds all the metadata for objects in the PCIDSK file. + * + * This class is closely partnered with the MetadataSet class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __INCLUDE_SEGMENT_METADATASEGMENT_H +#define __INCLUDE_SEGMENT_METADATASEGMENT_H + +#include "pcidsk_buffer.h" +#include "segment/cpcidsksegment.h" + +#include +#include + +namespace PCIDSK +{ + /************************************************************************/ + /* MetadataSegment */ + /************************************************************************/ + + class MetadataSegment : virtual public CPCIDSKSegment + { + + public: + MetadataSegment( PCIDSKFile *file, int segment, + const char *segment_pointer ); + virtual ~MetadataSegment(); + + void FetchGroupMetadata( const char *group, int id, + std::map &md_set ); + void SetGroupMetadataValue( const char *group, int id, + const std::string& key, const std::string& value ); + + void Synchronize(); + + private: + bool loaded; + + void Load(); + void Save(); + + PCIDSKBuffer seg_data; + + std::map update_list; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_METADATASEGMENT_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/metadatasegment_p.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/metadatasegment_p.cpp new file mode 100644 index 000000000..a0d901219 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/metadatasegment_p.cpp @@ -0,0 +1,281 @@ +/****************************************************************************** + * + * Purpose: Implementation of the MetadataSegment class. + * + * This class is used to manage access to the SYS METADATA segment. This + * segment holds all the metadata for objects in the PCIDSK file. + * + * This class is closely partnered with the MetadataSet class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_file.h" +#include "segment/metadatasegment.h" +#include +#include +#include +#include + +using namespace PCIDSK; + +/************************************************************************/ +/* MetadataSegment() */ +/************************************************************************/ + +MetadataSegment::MetadataSegment( PCIDSKFile *file, int segment, + const char *segment_pointer ) + : CPCIDSKSegment( file, segment, segment_pointer ) + +{ + loaded = false; +} + +/************************************************************************/ +/* ~MetadataSegment() */ +/************************************************************************/ + +MetadataSegment::~MetadataSegment() + +{ + Synchronize(); +} + +/************************************************************************/ +/* Synchronize() */ +/************************************************************************/ + +void MetadataSegment::Synchronize() +{ + if( loaded && update_list.size() > 0 ) + Save(); +} + +/************************************************************************/ +/* Load() */ +/************************************************************************/ + +void MetadataSegment::Load() + +{ + if( loaded ) + return; + + // TODO: this should likely be protected by a mutex. + +/* -------------------------------------------------------------------- */ +/* Load the segment contents into a buffer. */ +/* -------------------------------------------------------------------- */ + seg_data.SetSize( (int) (data_size - 1024) ); + + ReadFromFile( seg_data.buffer, 0, data_size - 1024 ); + + loaded = true; +} + +/************************************************************************/ +/* FetchGroupMetadata() */ +/************************************************************************/ + +void MetadataSegment::FetchGroupMetadata( const char *group, int id, + std::map &md_set) +{ +/* -------------------------------------------------------------------- */ +/* Load the metadata segment if not already loaded. */ +/* -------------------------------------------------------------------- */ + Load(); + +/* -------------------------------------------------------------------- */ +/* Establish the key prefix we are searching for. */ +/* -------------------------------------------------------------------- */ + char key_prefix[200]; + int prefix_len; + + std::sprintf( key_prefix, "METADATA_%s_%d_", group, id ); + prefix_len = std::strlen(key_prefix); + +/* -------------------------------------------------------------------- */ +/* Process all the metadata entries in this segment, searching */ +/* for those that match our prefix. */ +/* -------------------------------------------------------------------- */ + const char *pszNext; + + for( pszNext = (const char *) seg_data.buffer; *pszNext != '\0'; ) + { +/* -------------------------------------------------------------------- */ +/* Identify the end of this line, and the split character (:). */ +/* -------------------------------------------------------------------- */ + int i_split = -1, i; + + for( i=0; + pszNext[i] != 10 && pszNext[i] != 12 && pszNext[i] != 0; + i++) + { + if( i_split == -1 && pszNext[i] == ':' ) + i_split = i; + } + + if( pszNext[i] == '\0' ) + break; + +/* -------------------------------------------------------------------- */ +/* If this matches our prefix, capture the key and value. */ +/* -------------------------------------------------------------------- */ + if( i_split != -1 && std::strncmp(pszNext,key_prefix,prefix_len) == 0 ) + { + std::string key, value; + + key.assign( pszNext+prefix_len, i_split-prefix_len ); + + if( pszNext[i_split+1] == ' ' ) + value.assign( pszNext+i_split+2, i-i_split-2 ); + else + value.assign( pszNext+i_split+1, i-i_split-1 ); + + md_set[key] = value; + } + +/* -------------------------------------------------------------------- */ +/* Advance to start of next line. */ +/* -------------------------------------------------------------------- */ + pszNext = pszNext + i; + while( *pszNext == 10 || *pszNext == 12 ) + pszNext++; + } +} + +/************************************************************************/ +/* SetGroupMetadataValue() */ +/************************************************************************/ + +void MetadataSegment::SetGroupMetadataValue( const char *group, int id, + const std::string& key, const std::string& value ) +{ + Load(); + + char key_prefix[200]; + + std::sprintf( key_prefix, "METADATA_%s_%d_", group, id ); + + std::string full_key; + + full_key = key_prefix; + full_key += key; + + update_list[full_key] = value; +} + +/************************************************************************/ +/* Save() */ +/* */ +/* When saving we first need to merge in any updates. We put */ +/* this off since scanning and updating the metadata doc could */ +/* be epxensive if done for each item. */ +/************************************************************************/ + +void MetadataSegment::Save() + +{ + std::string new_data; + +/* -------------------------------------------------------------------- */ +/* Process all the metadata entries in this segment, searching */ +/* for those that match our prefix. */ +/* -------------------------------------------------------------------- */ + const char *pszNext; + + for( pszNext = (const char *) seg_data.buffer; *pszNext != '\0'; ) + { +/* -------------------------------------------------------------------- */ +/* Identify the end of this line, and the split character (:). */ +/* -------------------------------------------------------------------- */ + int i_split = -1, i; + + for( i=0; + pszNext[i] != 10 && pszNext[i] != 12 && pszNext[i] != 0; + i++) + { + if( i_split == -1 && pszNext[i] == ':' ) + i_split = i; + } + + if( pszNext[i] == '\0' ) + break; + +/* -------------------------------------------------------------------- */ +/* If we have a new value for this key, do not copy over the */ +/* old value. Otherwise append the old value to our new image. */ +/* -------------------------------------------------------------------- */ + std::string full_key; + + full_key.assign( pszNext, i_split ); + + if( update_list.count(full_key) == 1 ) + /* do not transfer - we will append later */; + else + new_data.append( pszNext, i+1 ); + +/* -------------------------------------------------------------------- */ +/* Advance to start of next line. */ +/* -------------------------------------------------------------------- */ + pszNext = pszNext + i; + while( *pszNext == 10 || *pszNext == 12 ) + pszNext++; + } + +/* -------------------------------------------------------------------- */ +/* Append all the update items with non-empty values. */ +/* -------------------------------------------------------------------- */ + std::map::iterator it; + + for( it = update_list.begin(); it != update_list.end(); it++ ) + { + if( it->second.size() == 0 ) + continue; + + std::string line; + + line = it->first; + line += ": "; + line += it->second; + line += "\n"; + + new_data += line; + } + + update_list.clear(); + +/* -------------------------------------------------------------------- */ +/* Move the new value into our buffer, and write to disk. */ +/* -------------------------------------------------------------------- */ + if( new_data.size() % 512 != 0 ) // zero fill the last block. + { + new_data.resize( new_data.size() + (512 - (new_data.size() % 512)), + '\0' ); + } + + seg_data.SetSize( new_data.size() ); + std::memcpy( seg_data.buffer, new_data.c_str(), new_data.size() ); + + WriteToFile( seg_data.buffer, 0, seg_data.buffer_size ); +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/orbitstructures.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/orbitstructures.h new file mode 100644 index 000000000..3bdf8ac00 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/orbitstructures.h @@ -0,0 +1,845 @@ +/****************************************************************************** + * + * Purpose: Support for storing and manipulating Orbit information + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_ORBIT_INFORMATION_H +#define __INCLUDE_PCIDSK_ORBIT_INFORMATION_H + +#include +#include + +namespace PCIDSK +{ +/* -------------------------------------------------------------------- */ +/* Structure for ephemeris segment (ORBIT segment, type 160). */ +/* -------------------------------------------------------------------- */ +#define EPHEMERIS_BLK 8 +#define EPHEMERIS_RADAR_BLK 10 +#define EPHEMERIS_ATT_BLK 9 +/* -------------------------------------------------------------------- */ +/* Structure for Satellite Radar segment. */ +/* -------------------------------------------------------------------- */ +#define ANC_DATA_PER_BLK 16 +#define ANC_DATA_SIZE 32 + /** + * Ancillary data structure. + */ + struct AncillaryData_t + { + /** + * Default constrcutor + */ + AncillaryData_t() + { + } + /** + * Copy constructor + * @param oAD the ancillary data to copy + */ + AncillaryData_t(const AncillaryData_t& oAD) + { + Copy(oAD); + } + + /** + * assignement operator + * @param oAD the ancillary data to assign + */ + AncillaryData_t& operator=(const AncillaryData_t& oAD) + { + Copy(oAD); + return *this; + } + + /** + * Copy function + * @param oAD the ancillary data to copy + */ + void Copy(const AncillaryData_t& oAD) + { + if(this == &oAD) + { + return; + } + SlantRangeFstPixel = oAD.SlantRangeFstPixel; + SlantRangeLastPixel = oAD.SlantRangeLastPixel; + FstPixelLat = oAD.FstPixelLat; + MidPixelLat = oAD.MidPixelLat; + LstPixelLat = oAD.LstPixelLat; + FstPixelLong = oAD.FstPixelLong; + MidPixelLong = oAD.MidPixelLong; + LstPixelLong = oAD.LstPixelLong; + } + + int SlantRangeFstPixel; /* Slant Range to First Pixel (m) */ + int SlantRangeLastPixel; /* Slant Range to Last Pixel (m) */ + float FstPixelLat; /* First Pixel Latitude (millionths degrees) */ + float MidPixelLat; /* Mid Pixel Latitude (millionths degrees) */ + float LstPixelLat; /* Last Pixel Latitude (millionths degrees) */ + float FstPixelLong; /* First Pixel Longitude (millionths degrees)*/ + float MidPixelLong; /* Mid Pixel Longitude (millionths degrees) */ + float LstPixelLong; /* Last Pixel Longitude (millionths degrees) */ + } ; + + /** + * Radar segment information + */ + struct RadarSeg_t + { + /** + * Default constrcutor + */ + RadarSeg_t() + { + } + /** + * Copy constructor + * @param oRS the radar segment to copy + */ + RadarSeg_t(const RadarSeg_t& oRS) + { + Copy(oRS); + } + + /** + * assignement operator + * @param oRS the radar segment to assign + */ + RadarSeg_t& operator=(const RadarSeg_t& oRS) + { + Copy(oRS); + return *this; + } + + /** + * Copy function + * @param oRS the radar segment to copy + */ + void Copy(const RadarSeg_t& oRS) + { + if(this == &oRS) + { + return; + } + Identifier = oRS.Identifier; + Facility = oRS.Facility; + Ellipsoid = oRS.Ellipsoid; + EquatorialRadius = oRS.EquatorialRadius; + PolarRadius = oRS.PolarRadius; + IncidenceAngle = oRS.IncidenceAngle; + PixelSpacing = oRS.PixelSpacing; + LineSpacing = oRS.LineSpacing; + ClockAngle = oRS.ClockAngle; + + NumberBlockData = oRS.NumberBlockData; + NumberData = oRS.NumberData; + + Line = oRS.Line; + } + + std::string Identifier; /* Product identifier */ + std::string Facility; /* Processing facility */ + std::string Ellipsoid; /* Ellipsoid designator */ + double EquatorialRadius; /* Equatorial radius of earth */ + double PolarRadius; /* Polar radius of earth */ + double IncidenceAngle; /* Incidence angle */ + double PixelSpacing; /* Nominal pixel spacing in metre */ + double LineSpacing; /* Nominal line spacing in metre */ + double ClockAngle; /* Clock angle in degree */ + + int NumberBlockData; /* Number of blocks of ancillary data */ + int NumberData; /* Number of ancillary data */ + + std::vector Line; /* Pointer to ancillary line */ + } ; + +/* -------------------------------------------------------------------- */ +/* Structure for Satellite attitude segment. */ +/* -------------------------------------------------------------------- */ +#define ATT_SEG_BLK 604 +#define ATT_SEG_MAX_LINE 6000 +#define ATT_SEG_LINE_PER_BLOCK 10 + + /** + * Attitude line information + */ + struct AttitudeLine_t + { + /** + * Default constrcutor + */ + AttitudeLine_t() + { + } + /** + * Copy constructor + * @param oAL the attitude line to copy + */ + AttitudeLine_t(const AttitudeLine_t& oAL) + { + Copy(oAL); + } + + /** + * assignement operator + * @param oAL the attitude line to assign + */ + AttitudeLine_t& operator=(const AttitudeLine_t& oAL) + { + Copy(oAL); + return *this; + } + + /** + * Copy function + * @param oAL the attitude line to copy + */ + void Copy(const AttitudeLine_t& oAL) + { + if(this == &oAL) + { + return; + } + ChangeInAttitude = oAL.ChangeInAttitude; + ChangeEarthSatelliteDist = oAL.ChangeEarthSatelliteDist; + } + + double ChangeInAttitude; /* Change in satellite attiutde (D22.16) */ + double ChangeEarthSatelliteDist; /* Change in earth-satellite distance + (D22.16) */ + } ; + + /** + * Attitude segment information + */ + struct AttitudeSeg_t + { + /** + * Default constrcutor + */ + AttitudeSeg_t() + { + } + /** + * Copy constructor + * @param oAS the attitude segment to copy + */ + AttitudeSeg_t(const AttitudeSeg_t& oAS) + { + Copy(oAS); + } + + /** + * assignement operator + * @param oAS the avhrr segment to assign + */ + AttitudeSeg_t& operator=(const AttitudeSeg_t& oAS) + { + Copy(oAS); + return *this; + } + + /** + * Copy function + * @param oAS the avhrr segment to copy + */ + void Copy(const AttitudeSeg_t& oAS) + { + if(this == &oAS) + { + return; + } + Roll = oAS.Roll; + Pitch = oAS.Pitch; + Yaw = oAS.Yaw; + NumberOfLine = oAS.NumberOfLine; + NumberBlockData = oAS.NumberBlockData; + Line = oAS.Line; + } + + double Roll; /* Roll (D22.16) */ + double Pitch; /* Pitch (D22.16) */ + double Yaw; /* Yaw (D22.16) */ + int NumberOfLine; /* No. of Lines (I22) */ + + int NumberBlockData; /* No. of block of data. */ + std::vector Line; + + } ; + +/* -------------------------------------------------------------------- */ +/* AVHRR orbit segment. Composed of 11 blocks plus extra blocks */ +/* for holding per-scanline information. */ +/* -------------------------------------------------------------------- */ +#define AVH_SEG_BASE_NUM_BLK 11 + + /** + * Avhrr line information + */ + struct AvhrrLine_t + { + /** + * Default constrcutor + */ + AvhrrLine_t() + { + } + /** + * Copy constructor + * @param oAL the avhrr line to copy + */ + AvhrrLine_t(const AvhrrLine_t& oAL) + { + Copy(oAL); + } + + /** + * assignement operator + * @param oAL the avhrr line to assign + */ + AvhrrLine_t& operator=(const AvhrrLine_t& oAL) + { + Copy(oAL); + return *this; + } + + /** + * Copy function + * @param oAL the avhrr line to copy + */ + void Copy(const AvhrrLine_t& oAL) + { + if(this == &oAL) + { + return; + } + nScanLineNum = oAL.nScanLineNum; + nStartScanTimeGMTMsec = oAL.nStartScanTimeGMTMsec; + for(int i=0 ; i < 10 ; i++) + abyScanLineQuality[i] = oAL.abyScanLineQuality[i]; + for(int i=0 ; i < 5 ; i++) + { + aabyBadBandIndicators[i][0] = oAL.aabyBadBandIndicators[i][0]; + aabyBadBandIndicators[i][1] = oAL.aabyBadBandIndicators[i][1]; + anSpaceScanData[i] = oAL.anSpaceScanData[i]; + } + for(int i=0 ; i < 8 ; i++) + abySatelliteTimeCode[i] = oAL.abySatelliteTimeCode[i]; + for(int i=0 ; i < 3 ; i++) + { + anTargetTempData[i] = oAL.anTargetTempData[i]; + anTargetScanData[i] = oAL.anTargetScanData[i]; + } + } + + /* For geocoding */ + int nScanLineNum; + int nStartScanTimeGMTMsec; + unsigned char abyScanLineQuality[10]; + unsigned char aabyBadBandIndicators[5][2]; + unsigned char abySatelliteTimeCode[8]; + + /* For thermal/IR calibration */ + int anTargetTempData[3]; + int anTargetScanData[3]; + int anSpaceScanData[5]; + + } ; + + /** + * Avhrr segment information. + */ + struct AvhrrSeg_t + { + /** + * Default constrcutor + */ + AvhrrSeg_t() + { + } + /** + * Copy constructor + * @param oAS the avhrr segment to copy + */ + AvhrrSeg_t(const AvhrrSeg_t& oAS) + { + Copy(oAS); + } + + /** + * assignement operator + * @param oAS the avhrr segment to assign + */ + AvhrrSeg_t& operator=(const AvhrrSeg_t& oAS) + { + Copy(oAS); + return *this; + } + + /** + * Copy function + * @param oAS the avhrr segment to copy + */ + void Copy(const AvhrrSeg_t& oAS) + { + if(this == &oAS) + { + return; + } + szImageFormat = oAS.szImageFormat; + nImageXSize = oAS.nImageXSize; + nImageYSize = oAS.nImageYSize; + bIsAscending = oAS.bIsAscending; + bIsImageRotated = oAS.bIsImageRotated; + szOrbitNumber = oAS.szOrbitNumber; + szAscendDescendNodeFlag = oAS.szAscendDescendNodeFlag; + szEpochYearAndDay = oAS.szEpochYearAndDay; + szEpochTimeWithinDay = oAS.szEpochTimeWithinDay; + szTimeDiffStationSatelliteMsec = oAS.szTimeDiffStationSatelliteMsec; + szActualSensorScanRate = oAS.szActualSensorScanRate; + szIdentOfOrbitInfoSource = oAS.szIdentOfOrbitInfoSource; + szInternationalDesignator = oAS.szInternationalDesignator; + szOrbitNumAtEpoch = oAS.szOrbitNumAtEpoch; + szJulianDayAscendNode = oAS.szJulianDayAscendNode; + szEpochYear = oAS.szEpochYear; + szEpochMonth = oAS.szEpochMonth; + szEpochDay = oAS.szEpochDay; + szEpochHour = oAS.szEpochHour; + szEpochMinute = oAS.szEpochMinute; + szEpochSecond = oAS.szEpochSecond; + szPointOfAriesDegrees = oAS.szPointOfAriesDegrees; + szAnomalisticPeriod = oAS.szAnomalisticPeriod; + szNodalPeriod = oAS.szNodalPeriod; + szEccentricity = oAS.szEccentricity; + szArgumentOfPerigee = oAS.szArgumentOfPerigee; + szRAAN = oAS.szRAAN; + szInclination = oAS.szInclination; + szMeanAnomaly = oAS.szMeanAnomaly; + szSemiMajorAxis = oAS.szSemiMajorAxis; + nRecordSize = oAS.nRecordSize; + nBlockSize = oAS.nBlockSize; + nNumRecordsPerBlock = oAS.nNumRecordsPerBlock; + nNumBlocks = oAS.nNumBlocks; + nNumScanlineRecords = oAS.nNumScanlineRecords; + Line = oAS.Line; + } + + /* Nineth Block Part 1 - General/header information */ + std::string szImageFormat; + int nImageXSize; + int nImageYSize; + bool bIsAscending; + bool bIsImageRotated; + + /* Nineth Block Part 2 - Ephemeris information */ + std::string szOrbitNumber; + std::string szAscendDescendNodeFlag; + std::string szEpochYearAndDay; + std::string szEpochTimeWithinDay; + std::string szTimeDiffStationSatelliteMsec; + std::string szActualSensorScanRate; + std::string szIdentOfOrbitInfoSource; + std::string szInternationalDesignator; + std::string szOrbitNumAtEpoch; + std::string szJulianDayAscendNode; + std::string szEpochYear; + std::string szEpochMonth; + std::string szEpochDay; + std::string szEpochHour; + std::string szEpochMinute; + std::string szEpochSecond; + std::string szPointOfAriesDegrees; + std::string szAnomalisticPeriod; + std::string szNodalPeriod; + std::string szEccentricity; + std::string szArgumentOfPerigee; + std::string szRAAN; + std::string szInclination; + std::string szMeanAnomaly; + std::string szSemiMajorAxis; + + /* 10th Block - Empty, reserved for future use */ + + /* 11th Block - Needed for indexing 12th block onwards */ + int nRecordSize; + int nBlockSize; + int nNumRecordsPerBlock; + int nNumBlocks; + int nNumScanlineRecords; + + /* 12th Block and onwards - Per-scanline records */ + std::vector Line; + + } ; + + /** + * Possible orbit types. + */ + typedef enum + { + OrbNone, + OrbAttitude, + OrbLatLong, + OrbAvhrr + } OrbitType; + + /** + * Ephemeris segment structure + */ + struct EphemerisSeg_t + { + /** + * Default constrcutor + */ + EphemerisSeg_t() + { + AttitudeSeg = NULL; + RadarSeg = NULL; + AvhrrSeg = NULL; + } + + /** + * Destructor + */ + ~EphemerisSeg_t() + { + delete AttitudeSeg; + delete RadarSeg; + delete AvhrrSeg; + } + + /** + * Copy constructor + * @param oES the ephemeris segment to copy + */ + EphemerisSeg_t(const EphemerisSeg_t& oES) + { + AttitudeSeg = NULL; + RadarSeg = NULL; + AvhrrSeg = NULL; + Copy(oES); + } + + /** + * assignement operator + * @param oES the ephemeris segment to assign + */ + EphemerisSeg_t& operator=(const EphemerisSeg_t& oES) + { + Copy(oES); + return *this; + } + + /** + * Copy function + * @param oES the ephemeris segment to copy + */ + void Copy(const EphemerisSeg_t& oES) + { + if(this == &oES) + { + return; + } + delete AttitudeSeg; + delete RadarSeg; + delete AvhrrSeg; + AttitudeSeg = NULL; + RadarSeg = NULL; + AvhrrSeg = NULL; + if(oES.AttitudeSeg) + AttitudeSeg = new AttitudeSeg_t(*oES.AttitudeSeg); + if(oES.RadarSeg) + RadarSeg = new RadarSeg_t(*oES.RadarSeg); + if(oES.AvhrrSeg) + AvhrrSeg = new AvhrrSeg_t(*oES.AvhrrSeg); + + for(int i =0 ; i <39 ; i++) + SPCoeff1B[i] = oES.SPCoeff1B[i]; + for(int i =0 ; i <4 ; i++) + SPCoeffSg[i] = oES.SPCoeffSg[i]; + + SatelliteDesc = oES.SatelliteDesc; + SceneID = oES.SceneID; + SatelliteSensor = oES.SatelliteSensor; + SensorNo = oES.SensorNo; + DateImageTaken = oES.DateImageTaken; + SupSegExist = oES.SupSegExist; + FieldOfView = oES.FieldOfView; + ViewAngle = oES.ViewAngle; + NumColCentre = oES.NumColCentre; + RadialSpeed = oES.RadialSpeed; + Eccentricity = oES.Eccentricity; + Height = oES.Height; + Inclination = oES.Inclination; + TimeInterval = oES.TimeInterval; + NumLineCentre = oES.NumLineCentre; + LongCentre = oES.LongCentre; + AngularSpd = oES.AngularSpd; + AscNodeLong = oES.AscNodeLong; + ArgPerigee = oES.ArgPerigee; + LatCentre = oES.LatCentre; + EarthSatelliteDist = oES.EarthSatelliteDist; + NominalPitch = oES.NominalPitch; + TimeAtCentre = oES.TimeAtCentre; + SatelliteArg = oES.SatelliteArg; + XCentre = oES.XCentre; + YCentre = oES.YCentre; + UtmYCentre = oES.UtmYCentre; + UtmXCentre = oES.UtmXCentre; + PixelRes = oES.PixelRes; + LineRes = oES.LineRes; + CornerAvail = oES.CornerAvail; + MapUnit = oES.MapUnit; + XUL = oES.XUL; + YUL = oES.YUL; + XUR = oES.XUR; + YUR = oES.YUR; + XLR = oES.XLR; + YLR = oES.YLR; + XLL = oES.XLL; + YLL = oES.YLL; + UtmYUL = oES.UtmYUL; + UtmXUL = oES.UtmXUL; + UtmYUR = oES.UtmYUR; + UtmXUR = oES.UtmXUR; + UtmYLR = oES.UtmYLR; + UtmXLR = oES.UtmXLR; + UtmYLL = oES.UtmYLL; + UtmXLL = oES.UtmXLL; + LatCentreDeg = oES.LatCentreDeg; + LongCentreDeg = oES.LongCentreDeg; + LatUL = oES.LatUL; + LongUL = oES.LongUL; + LatUR = oES.LatUR; + LongUR = oES.LongUR; + LatLR = oES.LatLR; + LongLR = oES.LongLR; + LatLL = oES.LatLL; + LongLL = oES.LongLL; + HtCentre = oES.HtCentre; + HtUL = oES.HtUL; + HtUR = oES.HtUR; + HtLR = oES.HtLR; + HtLL = oES.HtLL; + ImageRecordLength = oES.ImageRecordLength; + NumberImageLine = oES.NumberImageLine; + NumberBytePerPixel = oES.NumberBytePerPixel; + NumberSamplePerLine = oES.NumberSamplePerLine; + NumberPrefixBytes = oES.NumberPrefixBytes; + NumberSuffixBytes = oES.NumberSuffixBytes; + SPNCoeff = oES.SPNCoeff; + bDescending = oES.bDescending; + Type = oES.Type; + } + + /// Satellite description + std::string SatelliteDesc; + /// Scene ID + std::string SceneID; + + /// Satellite sensor + std::string SatelliteSensor; + /// Satellite sensor no. + std::string SensorNo; + /// Date of image taken + std::string DateImageTaken; + /// Flag to indicate supplemental segment + bool SupSegExist; + /// Scanner field of view (ALPHA) + double FieldOfView; + /// Viewing angle (BETA) + double ViewAngle; + /// Number of column at center (C0) + double NumColCentre; + /// Radial speed (DELIRO) + double RadialSpeed; + /// Eccentricity (ES) + double Eccentricity; + /// Height (H0) + double Height; + /// Inclination (I) + double Inclination; + /// Time interval (K) + double TimeInterval; + /// Number of line at center (L0) + double NumLineCentre; + /// Longitude of center (LAMBDA) + double LongCentre; + /// Angular speed (N) + double AngularSpd; + /// Ascending node Longitude (OMEGA-MAJ) + double AscNodeLong; + /// Argument Perigee (OMEGA-MIN) + double ArgPerigee; + /// Latitude of center (PHI) + double LatCentre; + /// Earth Satellite distance (RHO) + double EarthSatelliteDist; + /// Nominal pitch (T) + double NominalPitch; + /// Time at centre (T0) + double TimeAtCentre; + /// Satellite argument (WP) + double SatelliteArg; + + /// Scene center pixel coordinate + double XCentre; + /// Scene center line coordinate + double YCentre; + /// Scene centre UTM northing + double UtmYCentre; + /// Scene centre UTM easting + double UtmXCentre; + /// Pixel resolution in x direction + double PixelRes; + /// Pixel resolution in y direction + double LineRes; + /// Flag to tell corner coordinate available + bool CornerAvail; + /// Map units + std::string MapUnit; + /// Pixel coordinate of upper left corner + double XUL; + /// Line coordinate of upper left corner + double YUL; + /// Pixel coordinate of upper right corner + double XUR; + /// Line coordinate of upper right corner + double YUR; + /// Pixel coordinate of lower right corner + double XLR; + /// Line coordinate of lower right corner + double YLR; + /// Pixel coordinate of lower left corner + double XLL; + /// Line coordinate of lower left corner + double YLL; + /// UTM Northing of upper left corner + double UtmYUL; + /// UTM Easting of upper left corner + double UtmXUL; + /// UTM Northing of upper right corner + double UtmYUR; + /// UTM Easting of upper right corner + double UtmXUR; + /// UTM Northing of lower right corner + double UtmYLR; + /// UTM Easting of lower right corner + double UtmXLR; + /// Utm Northing of lower left corner + double UtmYLL; + /// Utm Easting of lower left corner + double UtmXLL; + + /// Scene centre latitude (deg) + double LatCentreDeg; + /// Scene centre longitude (deg) + double LongCentreDeg; + /// Upper left latitude (deg) + double LatUL; + /// Upper left longitude (deg) + double LongUL; + /// Upper right latitude (deg) + double LatUR; + /// Upper right longitude (deg) + double LongUR; + /// Lower right latitude (deg) + double LatLR; + /// Lower right longitude (deg) + double LongLR; + /// Lower left latitude (deg) + double LatLL; + /// Lower left longitude (deg) + double LongLL; + /// Centre Height (m) + double HtCentre; + /// UL Height (m) + double HtUL; + /// UR Height (m) + double HtUR; + /// LR Height (m) + double HtLR; + /// LL Height (m) + double HtLL; + + /// SPOT 1B coefficients + double SPCoeff1B[39]; + /// SPOT 1B segment coefficients + int SPCoeffSg[4]; + + /// Image record length + int ImageRecordLength; + /// Number of image line + int NumberImageLine; + /// Number of bytes per pixel + int NumberBytePerPixel; + /// Number of samples per line + int NumberSamplePerLine; + /// Number of prefix bytes + int NumberPrefixBytes; + /// Number of suffix bytes + int NumberSuffixBytes; + /// Number of coefficients for SPOT 1B + int SPNCoeff; + + /// Flag to indicate ascending or descending + bool bDescending; + + /// Orbit type: None, LatLong, Attitude, Avhrr + OrbitType Type; + AttitudeSeg_t *AttitudeSeg; + RadarSeg_t *RadarSeg; + AvhrrSeg_t *AvhrrSeg; + }; + + /** + * List of sensor type + */ + typedef enum {PLA_1, MLA_1, PLA_2, MLA_2, PLA_3, MLA_3, PLA_4, MLA_4, + ASTER, SAR, LISS_1, LISS_2, LISS_3, LISS_L3, LISS_L3_L2, + LISS_L4, LISS_L4_L2, LISS_P3, LISS_P3_L2, LISS_W3, LISS_W3_L2, + LISS_AWF, LISS_AWF_L2, LISS_M3, EOC, IRS_1, RSAT_FIN, + RSAT_STD, ERS_1, ERS_2, TM, ETM, IKO_PAN, IKO_MULTI, + ORBVIEW_PAN, ORBVIEW_MULTI, OV3_PAN_BASIC, OV3_PAN_GEO, + OV3_MULTI_BASIC, OV3_MULTI_GEO, OV5_PAN_BASIC, OV5_PAN_GEO, + OV5_MULTI_BASIC, OV5_MULTI_GEO, QBIRD_PAN, QBIRD_PAN_STD, + QBIRD_PAN_STH, QBIRD_MULTI, QBIRD_MULTI_STD, QBIRD_MULTI_STH, + FORMOSAT_PAN, FORMOSAT_MULTI, FORMOSAT_PAN_L2, + FORMOSAT_MULTIL2, SPOT5_PAN_2_5, SPOT5_PAN_5, SPOT5_HRS, + SPOT5_MULTI, MERIS_FR, MERIS_RR, MERIS_LR, ASAR, EROS, + MODIS_250, MODIS_500, MODIS_1000, CBERS_HRC, CBERS_HRC_L2, + CBERS_CCD, CBERS_CCD_L2, CBERS_IRM_80, CBERS_IRM_80_L2, + CBERS_IRM_160, CBERS_IRM_160_L2, CBERS_WFI, CBERS_WFI_L2, + CARTOSAT1_L1, CARTOSAT1_L2, ALOS_PRISM_L1, ALOS_PRISM_L2, + ALOS_AVNIR_L1, ALOS_AVNIR_L2, PALSAR, DMC_1R, DMC_1T, + KOMPSAT2_PAN, KOMPSAT2_MULTI, TERRASAR, WVIEW_PAN, + WVIEW_PAN_STD, WVIEW_MULTI, WVIEW_MULTI_STD, + RAPIDEYE_L1B, THEOS_PAN_L1, THEOS_PAN_L2, + THEOS_MS_L1, THEOS_MS_L2, + GOSAT_500_L1, GOSAT_500_L2, GOSAT_1500_L1, GOSAT_1500_L2, + HJ_CCD_1A, HJ_CCD_1B, NEW, AVHRR} TypeDeCapteur; +} + +#endif // __INCLUDE_PCIDSK_ORBIT_INFORMATION_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/pcidsksegmentbuilder.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/pcidsksegmentbuilder.h new file mode 100644 index 000000000..360e9c705 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/pcidsksegmentbuilder.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * + * Purpose: Interface. Builder class for constructing a related PCIDSK segment + * class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __INCLUDE_SEGMENT_PCIDSKSEGMENTBUILDER_H +#define __INCLUDE_SEGMENT_PCIDSKSEGMENTBUILDER_H + +namespace PCIDSK +{ + class PCIDSKSegment; + class PCIDSKFile; + + /** + * PCIDSK Abstract Builder class. Given a segment pointer, constructs + * an instance of a given PCIDSKSegment implementor. Typically an instance + * of this will be registered with the PCIDSK Segment Factory. + */ + struct IPCIDSKSegmentBuilder + { + // TODO: Determine required arguments for a segment to be constructed + virtual PCIDSKSegment *BuildSegment(PCIDSKFile *poFile, + unsigned int nSegmentID, const char *psSegmentPointer) = 0; + + // Get a list of segments that this builder can handle + virtual std::vector GetSupportedSegments(void) const = 0; + + // Get a copyright string + virtual std::string GetVendorString(void) const = 0; + + // Get a name string + virtual std::string GetSegmentBuilderName(void) const = 0; + + // Virtual destructor + virtual ~IPCIDSKSegmentBuilder() {} + }; + +}; // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_PCIDSKSEGMENTBUILDER_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/sysblockmap.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/sysblockmap.cpp new file mode 100644 index 000000000..9fa1b433b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/sysblockmap.cpp @@ -0,0 +1,551 @@ +/****************************************************************************** + * + * Purpose: Implementation of the SysBlockMap class. + * + * This class is used to manage access to the SYS virtual block map segment + * (named SysBMDir). This segment is used to keep track of one or more + * virtual files stored in SysBData segments. These virtual files are normally + * used to hold tiled images for primary bands or overviews. + * + * This class is closely partnered with the SysVirtualFile class, and the + * primary client is the CTiledChannel class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk_exception.h" +#include "pcidsk_file.h" +#include "core/sysvirtualfile.h" +#include "segment/sysblockmap.h" +#include "core/cpcidskfile.h" + +#include +#include +#include +#include + +using namespace PCIDSK; + +/************************************************************************/ +/* SysBlockMap() */ +/************************************************************************/ + +SysBlockMap::SysBlockMap( PCIDSKFile *file, int segment, + const char *segment_pointer ) + : CPCIDSKSegment( file, segment, segment_pointer ) + +{ + partial_loaded = false; + full_loaded = false; + dirty = false; + growing_segment = 0; +} + +/************************************************************************/ +/* ~SysBlockMap() */ +/************************************************************************/ + +SysBlockMap::~SysBlockMap() + +{ + size_t i; + + for( i = 0; i < virtual_files.size(); i++ ) + { + delete virtual_files[i]; + virtual_files[i] = NULL; + } + + Synchronize(); +} + +/************************************************************************/ +/* Initialize() */ +/* */ +/* This method is used after creation of the SysBMDir segment */ +/* to fill in valid contents. Prepares bare minimum contents. */ +/************************************************************************/ + +void SysBlockMap::Initialize() + +{ + PCIDSKBuffer init_data(512); + + init_data.Put( "VERSION 1", 0, 10 ); + init_data.Put( 0, 10, 8 ); + init_data.Put( 0, 18, 8 ); + init_data.Put( -1, 26, 8 ); + init_data.Put( "", 34, 512-34 ); + + WriteToFile( init_data.buffer, 0, init_data.buffer_size ); +#ifdef notdef + // arbitrarily grow the segment a bit to avoid having to move it too soon. + WriteToFile( "\0", 8191, 1 ); +#endif +} + +/************************************************************************/ +/* PartialLoad() */ +/* */ +/* Load the header and some per-layer information. */ +/************************************************************************/ + +void SysBlockMap::PartialLoad() + +{ + if( partial_loaded ) + return; + +// printf( "" ); +// fflush( stdout ); + +/* -------------------------------------------------------------------- */ +/* Load the 512 byte count section of the blockmap. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer count_data; + + count_data.SetSize( 512 ); + ReadFromFile( count_data.buffer, 0, 512 ); + + if( strncmp(count_data.buffer,"VERSION",7) != 0 ) + ThrowPCIDSKException( "SysBlockMap::PartialLoad() - block map corrupt." ); + + if( count_data.GetInt( 7, 3 ) != 1 ) + ThrowPCIDSKException( "SysBlockMap::PartialLoad() - unsupported version." ); + +/* -------------------------------------------------------------------- */ +/* Establish our SysVirtualFile array based on the number of */ +/* images listed in the image list. */ +/* -------------------------------------------------------------------- */ + int layer_count = count_data.GetInt( 10, 8 ); + + virtual_files.resize( layer_count ); + + block_count = count_data.GetInt( 18, 8 ); + first_free_block = count_data.GetInt( 26, 8 ); + +/* -------------------------------------------------------------------- */ +/* Load the layer list definitions. These are fairly small. */ +/* -------------------------------------------------------------------- */ + layer_data.SetSize( 24 * layer_count ); + ReadFromFile( layer_data.buffer, + 512 + 28 * block_count, + layer_data.buffer_size); + + partial_loaded = true; + +// FullLoad(); +} + +/************************************************************************/ +/* FullLoad() */ +/* */ +/* Load the blockmap data (can be large) into blockmap_data. */ +/************************************************************************/ + +void SysBlockMap::FullLoad() + +{ + PartialLoad(); + + if( full_loaded ) + return; + +// printf( "" ); +// fflush( stdout ); + + // TODO: this should likely be protected by a mutex. + +/* -------------------------------------------------------------------- */ +/* Load the segment contents into a buffer. */ +/* -------------------------------------------------------------------- */ + blockmap_data.SetSize( block_count * 28 ); + ReadFromFile( blockmap_data.buffer, 512, blockmap_data.buffer_size ); + + full_loaded = true; +} + +/************************************************************************/ +/* Synchronize() */ +/************************************************************************/ + +void SysBlockMap::Synchronize() + +{ + if( !full_loaded || !dirty ) + return; + + PCIDSKBuffer init_data(512); + + init_data.Put( "VERSION 1", 0, 10 ); + init_data.Put( (int) virtual_files.size(), 10, 8 ); + init_data.Put( block_count, 18, 8 ); + init_data.Put( first_free_block, 26, 8 ); + init_data.Put( "", 34, 512-34 ); + + WriteToFile( init_data.buffer, 0, init_data.buffer_size ); + + WriteToFile( blockmap_data.buffer, 512, blockmap_data.buffer_size ); + WriteToFile( layer_data.buffer, 512 + blockmap_data.buffer_size, + layer_data.buffer_size ); + + dirty = false; +} + +/************************************************************************/ +/* AllocateBlocks() */ +/* */ +/* Allocate a bunch of new blocks and attach to the free list. */ +/************************************************************************/ + +void SysBlockMap::AllocateBlocks() + +{ + FullLoad(); + +/* -------------------------------------------------------------------- */ +/* Find a segment we can extend. We consider any SYS segments */ +/* with a name of SysBData. */ +/* -------------------------------------------------------------------- */ + PCIDSKSegment *seg; + + if( growing_segment > 0 ) + { + seg = file->GetSegment( growing_segment ); + if( !seg->IsAtEOF() ) + growing_segment = 0; + } + + if( growing_segment == 0 ) + { + PCIDSKSegment *seg; + int previous = 0; + + while( (seg=file->GetSegment( SEG_SYS, "SysBData", previous )) != NULL ) + { + previous = seg->GetSegmentNumber(); + + if( seg->IsAtEOF() ) + { + growing_segment = previous; + break; + } + } + } + +/* -------------------------------------------------------------------- */ +/* If we didn't find one, then create a new segment. */ +/* -------------------------------------------------------------------- */ + if( growing_segment == 0 ) + { + growing_segment = + file->CreateSegment( "SysBData", + "System Block Data for Tiles and Overviews " + "- Do not modify", + SEG_SYS, 0L ); + } + +/* -------------------------------------------------------------------- */ +/* Allocate another set of space. */ +/* -------------------------------------------------------------------- */ + uint64 new_big_blocks = 16; + uint64 new_bytes = new_big_blocks * SysVirtualFile::block_size; + seg = file->GetSegment( growing_segment ); + int block_index_in_segment = (int) + (seg->GetContentSize() / SysVirtualFile::block_size); + + seg->WriteToFile( "\0", seg->GetContentSize() + new_bytes - 1, 1 ); + +/* -------------------------------------------------------------------- */ +/* Resize the memory image of the blockmap. */ +/* -------------------------------------------------------------------- */ + if( 28 * (block_count + new_big_blocks) + > (unsigned int) blockmap_data.buffer_size ) + blockmap_data.SetSize( (int) (28 * (block_count + new_big_blocks)) ); + +/* -------------------------------------------------------------------- */ +/* Fill in info on the new blocks. */ +/* -------------------------------------------------------------------- */ + uint64 block_index; + + for( block_index = block_count; + block_index < block_count + new_big_blocks; + block_index++ ) + { + int bi_offset = (int) (block_index * 28); + + blockmap_data.Put( growing_segment, bi_offset, 4 ); + blockmap_data.Put( block_index_in_segment++, bi_offset+4, 8 ); + blockmap_data.Put( -1, bi_offset+12, 8 ); + + if( block_index == block_count + new_big_blocks - 1 ) + blockmap_data.Put( -1, bi_offset+20, 8 ); + else + blockmap_data.Put( block_index+1, bi_offset+20, 8 ); + } + + first_free_block = block_count; + + block_count += (int) new_big_blocks; + + dirty = true; +} + +/************************************************************************/ +/* GrowVirtualFile() */ +/* */ +/* Get one more block for this virtual file. */ +/************************************************************************/ + +int SysBlockMap::GrowVirtualFile( int image, int &last_block, + int &block_segment_ret ) + +{ + FullLoad(); + +/* -------------------------------------------------------------------- */ +/* Do we need to create new free blocks? */ +/* -------------------------------------------------------------------- */ + if( first_free_block == -1 ) + AllocateBlocks(); + +/* -------------------------------------------------------------------- */ +/* Return the first free block, and update the next pointer of */ +/* the previous block. */ +/* -------------------------------------------------------------------- */ + int alloc_block = first_free_block; + + // update first free block to point to the next free block. + first_free_block = blockmap_data.GetInt( alloc_block*28+20, 8); + + // mark block as owned by this layer/image. + blockmap_data.Put( image, alloc_block*28 + 12, 8 ); + + // clear next free block on allocated block - it is the last in the chain + blockmap_data.Put( -1, alloc_block*28 + 20, 8 ); + + // point the previous "last block" for this virtual file to this new block + if( last_block != -1 ) + blockmap_data.Put( alloc_block, last_block*28 + 20, 8 ); + else + layer_data.Put( alloc_block, image*24 + 4, 8 ); + + dirty = true; + + block_segment_ret = blockmap_data.GetInt( alloc_block*28, 4 ); + last_block = alloc_block; + + return blockmap_data.GetInt( alloc_block*28+4, 8 ); +} + +/************************************************************************/ +/* SetVirtualFileSize() */ +/************************************************************************/ + +void SysBlockMap::SetVirtualFileSize( int image_index, uint64 file_length ) + +{ + FullLoad(); + + layer_data.Put( file_length, 24*image_index + 12, 12 ); + dirty = true; +} + +/************************************************************************/ +/* GetVirtualFile() */ +/************************************************************************/ + +SysVirtualFile *SysBlockMap::GetVirtualFile( int image ) + +{ + PartialLoad(); + + if( image < 0 || image >= (int) virtual_files.size() ) + ThrowPCIDSKException( "GetImageSysFile(%d): invalid image index", + image ); + + if( virtual_files[image] != NULL ) + return virtual_files[image]; + + uint64 vfile_length = layer_data.GetUInt64( 24*image + 12, 12 ); + int start_block = layer_data.GetInt( 24*image + 4, 8 ); + + virtual_files[image] = + new SysVirtualFile( dynamic_cast(file), + start_block, vfile_length, + this, image ); + + return virtual_files[image]; +} + +/************************************************************************/ +/* CreateVirtualFile() */ +/************************************************************************/ + +int SysBlockMap::CreateVirtualFile() + +{ + FullLoad(); + +/* -------------------------------------------------------------------- */ +/* Is there an existing dead layer we can reuse? */ +/* -------------------------------------------------------------------- */ + unsigned int layer_index; + + for( layer_index = 0; layer_index < virtual_files.size(); layer_index++ ) + { + if( layer_data.GetInt( 24*layer_index + 0, 4 ) == 1 /* dead */ ) + { + break; + } + } + +/* -------------------------------------------------------------------- */ +/* If not, extend the layer table. */ +/* -------------------------------------------------------------------- */ + if( layer_index == virtual_files.size() ) + { + layer_index = virtual_files.size(); + layer_data.SetSize( (layer_index+1) * 24 ); + virtual_files.push_back( NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Set all the entries for this layer. */ +/* -------------------------------------------------------------------- */ + dirty = true; + + layer_data.Put( 2, 24*layer_index + 0, 4 ); + layer_data.Put( -1, 24*layer_index + 4, 8 ); + layer_data.Put( 0, 24*layer_index + 12, 12 ); + + return layer_index; +} + +/************************************************************************/ +/* CreateVirtualImageFile() */ +/************************************************************************/ + +int SysBlockMap::CreateVirtualImageFile( int width, int height, + int block_width, int block_height, + eChanType chan_type, + std::string compression ) + +{ + if( compression == "" ) + compression = "NONE"; + +/* -------------------------------------------------------------------- */ +/* Create the underlying virtual file. */ +/* -------------------------------------------------------------------- */ + int img_index = CreateVirtualFile(); + SysVirtualFile *vfile = GetVirtualFile( img_index ); + +/* -------------------------------------------------------------------- */ +/* Set up the image header. */ +/* -------------------------------------------------------------------- */ + PCIDSKBuffer theader(128); + + theader.Put( "", 0, 128 ); + + theader.Put( width, 0, 8 ); + theader.Put( height, 8, 8 ); + theader.Put( block_width, 16, 8 ); + theader.Put( block_height, 24, 8 ); + theader.Put( DataTypeName(chan_type).c_str(), 32, 4 ); + theader.Put( compression.c_str(), 54, 8 ); + + vfile->WriteToFile( theader.buffer, 0, 128 ); + +/* -------------------------------------------------------------------- */ +/* Setup the tile map - initially with no tiles referenced. */ +/* -------------------------------------------------------------------- */ + int tiles_per_row = (width + block_width - 1) / block_width; + int tiles_per_col = (height + block_height - 1) / block_height; + int tile_count = tiles_per_row * tiles_per_col; + int i; + + PCIDSKBuffer tmap( tile_count * 20 ); + + for( i = 0; i < tile_count; i++ ) + { + tmap.Put( -1, i*12, 12 ); + tmap.Put( 0, tile_count*12 + i*8, 8 ); + } + + vfile->WriteToFile( tmap.buffer, 128, tile_count*20 ); + + return img_index; +} + +/************************************************************************/ +/* GetNextBlockMapEntry() */ +/* */ +/* SysVirtualFile's call this method to find the next block in */ +/* the blockmap which belongs to them. This allows them to */ +/* fill their blockmap "as needed" without necessarily forcing */ +/* a full load of the blockmap. */ +/************************************************************************/ + +int SysBlockMap::GetNextBlockMapEntry( int bm_index, + uint16 &segment, + int &block_in_segment ) + +{ + if( !partial_loaded ) + PartialLoad(); + +/* -------------------------------------------------------------------- */ +/* If the full blockmap is already loaded, just fetch it from */ +/* there to avoid extra IO or confusion between what is disk */ +/* and what is in memory. */ +/* */ +/* Otherwise we read from disk and hope the io level buffering */ +/* is pretty good. */ +/* -------------------------------------------------------------------- */ + char bm_entry[29]; + + if( full_loaded ) + { + memcpy( bm_entry, blockmap_data.buffer + bm_index * 28, 28 ); + } + else + { + ReadFromFile( bm_entry, bm_index * 28 + 512, 28 ); + } + +/* -------------------------------------------------------------------- */ +/* Parse the values as efficiently as we can. */ +/* -------------------------------------------------------------------- */ + bm_entry[28] = '\0'; + + int next_block = atoi( bm_entry+20 ); + + bm_entry[12] = '\0'; + block_in_segment = atoi(bm_entry+4); + + bm_entry[4] = '\0'; + segment = atoi(bm_entry); + + return next_block; +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/sysblockmap.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/sysblockmap.h new file mode 100644 index 000000000..3a9ec973c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/sysblockmap.h @@ -0,0 +1,97 @@ +/****************************************************************************** + * + * Purpose: Declaration of the SysBlockMap class. + * + * This class is used to manage access to the SYS virtual block map segment + * (named SysBMDir). This segment is used to keep track of one or more + * virtual files stored in SysBData segments. These virtual files are normally + * used to hold tiled images for primary bands or overviews. + * + * This class is closely partnered with the SysVirtualFile class, and the + * primary client is the CTiledChannel class. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_SEGMENT_SYSBLOCKMAP_H +#define __INCLUDE_SEGMENT_SYSBLOCKMAP_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_segment.h" +#include "segment/cpcidsksegment.h" + +namespace PCIDSK +{ + class SysVirtualFile; + class PCIDSKFile; + + /************************************************************************/ + /* SysBlockMap */ + /************************************************************************/ + + class SysBlockMap : virtual public CPCIDSKSegment + { + public: + SysBlockMap( PCIDSKFile *file, int segment,const char *segment_pointer ); + + virtual ~SysBlockMap(); + + virtual void Synchronize(); + virtual void Initialize(); + + SysVirtualFile *GetVirtualFile( int image ); + int CreateVirtualFile(); + int CreateVirtualImageFile( int width, int height, + int block_width, int block_height, + eChanType chan_type, + std::string compression ); + int GrowVirtualFile( int image, int &last_block, + int &block_segment_ret ); + void SetVirtualFileSize( int image, uint64 file_length ); + + int GetNextBlockMapEntry( int bm_index, + uint16 &segment, + int &block_in_segment ); + + private: + bool partial_loaded; + bool full_loaded; + bool dirty; + + void PartialLoad(); + void FullLoad(); + void AllocateBlocks(); + + PCIDSKBuffer layer_data; // only if partial_loaded + PCIDSKBuffer blockmap_data; // only if full_loaded. + + int block_count; + int first_free_block; + + int growing_segment; + + std::vector virtual_files; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_SYSBLOCKMAP_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/toutinstructures.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/toutinstructures.h new file mode 100644 index 000000000..35778a9ec --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/toutinstructures.h @@ -0,0 +1,206 @@ +/****************************************************************************** + * + * Purpose: Support for storing and manipulating Toutin information + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_PCIDSK_TOUTIN_INFORMATION_H +#define __INCLUDE_PCIDSK_TOUTIN_INFORMATION_H + +#include "segment/orbitstructures.h" + +namespace PCIDSK +{ +/* -------------------------------------------------------------------- */ +/* SRITInfo_t - Satellite Model structure. */ +/* -------------------------------------------------------------------- */ +#define AP_MDL 1 +#define SRIT_MDL 2 +#define RF_MDL 6 +#define RTCS_MDL 7 +#define ADS_MDL 9 +#define SRITModele 0 +#define SRITModele1A 1 +#define SRITModele1B 2 +#define SRITModeleSAR 3 +#define SRITModele1AHR 4 +#define SRITModeleEros 5 + +#define MAX_SPOT_LINES 30000 + + /** + * the SRITInfo_t struct contains all information + * for the Toutin Math Model. + */ + struct SRITInfo_t + { + /** + * default constructor + */ + SRITInfo_t() + { + OrbitPtr = NULL; + } + /** + * destructor + */ + ~SRITInfo_t() + { + delete OrbitPtr; + } + + /** + * Copy constructor. + * @param oSI the SRITInfo_t to copy + */ + SRITInfo_t(const SRITInfo_t& oSI) + { + OrbitPtr = NULL; + Copy(oSI); + } + + /** + * Assignment operator + * @param oSI the SRITInfo_t to assign + */ + SRITInfo_t& operator=(const SRITInfo_t& oSI) + { + Copy(oSI); + return *this; + } + + /** + * Copy function + * @param oSI the SRITInfo_t to copy + */ + void Copy(const SRITInfo_t& oSI) + { + if(this == &oSI) + { + return; + } + delete OrbitPtr; + OrbitPtr = NULL; + if(oSI.OrbitPtr) + { + OrbitPtr = new EphemerisSeg_t(*oSI.OrbitPtr); + } + + for(int i=0 ; i<256 ; i++) + { + nGCPIds[i] = oSI.nGCPIds[i]; + nPixel[i] = oSI.nPixel[i]; + nLine[i] = oSI.nLine[i]; + dfElev[i] = oSI.dfElev[i]; + } + + N0x2 = oSI.N0x2; + aa = oSI.aa; + SmALPHA = oSI.SmALPHA; + bb = oSI.bb; + C0 = oSI.C0; + cc = oSI.cc; + COS_KHI = oSI.COS_KHI; + DELTA_GAMMA = oSI.DELTA_GAMMA; + GAMMA = oSI.GAMMA; + K_1 = oSI.K_1; + L0 = oSI.L0; + P = oSI.P; + Q = oSI.Q; + TAU = oSI.TAU; + THETA = oSI.THETA; + THETA_SEC = oSI.THETA_SEC; + X0 = oSI.X0; + Y0 = oSI.Y0; + delh = oSI.delh; + COEF_Y2 = oSI.COEF_Y2; + delT = oSI.delT; + delL = oSI.delL; + delTau = oSI.delTau; + nDownSample = oSI.nDownSample; + nGCPCount = oSI.nGCPCount; + nEphemerisSegNo = oSI.nEphemerisSegNo; + nAttitudeFlag = oSI.nAttitudeFlag; + utmunit = oSI.utmunit; + GCPUnit = oSI.GCPUnit; + GCPMeanHtFlag = oSI.GCPMeanHtFlag; + dfGCPMeanHt = oSI.dfGCPMeanHt; + dfGCPMinHt = oSI.dfGCPMinHt; + dfGCPMaxHt = oSI.dfGCPMaxHt; + Qdeltar = oSI.Qdeltar; + Hdeltat = oSI.Hdeltat; + Sensor = oSI.Sensor; + nSensor = oSI.nSensor; + nModel = oSI.nModel; + RawToGeo = oSI.RawToGeo; + oProjectionInfo = oSI.oProjectionInfo; + } + + double N0x2; + double aa; + double SmALPHA; + double bb; + double C0; + double cc; + double COS_KHI; + double DELTA_GAMMA; + double GAMMA; + double K_1; + double L0; + double P; + double Q; + double TAU; + double THETA; + double THETA_SEC; + double X0; + double Y0; + double delh; + double COEF_Y2; + double delT; + double delL; + double delTau; + int nDownSample; + int nGCPCount; + int nEphemerisSegNo; + int nAttitudeFlag; + std::string utmunit; + std::string GCPUnit; + char GCPMeanHtFlag; + double dfGCPMeanHt; + double dfGCPMinHt; + double dfGCPMaxHt; + int nGCPIds[256]; + int nPixel[256],nLine[256]; + double dfElev[256]; + std::vector Qdeltar; + std::vector Hdeltat; + std::string Sensor; + int nSensor; + int nModel; + EphemerisSeg_t *OrbitPtr; + bool RawToGeo; + std::string oProjectionInfo; + } ; +} + +#endif // __INCLUDE_PCIDSK_TOUTIN_INFORMATION_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/vecsegdataindex.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/vecsegdataindex.cpp new file mode 100644 index 000000000..c07a78a75 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/vecsegdataindex.cpp @@ -0,0 +1,291 @@ +/****************************************************************************** + * + * Purpose: Implementation of the VecSegIndex class. + * + * This class is used to manage a vector segment data block index. There + * will be two instances created, one for the record data (sec_record) and + * one for the vertices (sec_vert). This class is exclusively a private + * helper class for VecSegHeader. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk.h" +#include "core/pcidsk_utils.h" +#include "segment/cpcidskvectorsegment.h" +#include +#include +#include + +using namespace PCIDSK; + +/* -------------------------------------------------------------------- */ +/* Size of a block in the record/vertice block tables. This is */ +/* determined by the PCIDSK format and may not be changed. */ +/* -------------------------------------------------------------------- */ +static const int block_page_size = 8192; + +/************************************************************************/ +/* VecSegDataIndex() */ +/************************************************************************/ + +VecSegDataIndex::VecSegDataIndex() + +{ + block_initialized = false; + vs = NULL; + dirty = false; +} + +/************************************************************************/ +/* ~VecSegDataIndex() */ +/************************************************************************/ + +VecSegDataIndex::~VecSegDataIndex() + +{ +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void VecSegDataIndex::Initialize( CPCIDSKVectorSegment *vs, int section ) + +{ + this->section = section; + this->vs = vs; + + if( section == sec_vert ) + offset_on_disk_within_section = 0; + else + offset_on_disk_within_section = vs->di[sec_vert].SerializedSize(); + + uint32 offset = offset_on_disk_within_section + + vs->vh.section_offsets[hsec_shape]; + + memcpy( &block_count, vs->GetData(sec_raw,offset,NULL,4), 4); + memcpy( &bytes, vs->GetData(sec_raw,offset+4,NULL,4), 4); + + bool needs_swap = !BigEndianSystem(); + + if( needs_swap ) + { + SwapData( &block_count, 4, 1 ); + SwapData( &bytes, 4, 1 ); + } + + size_on_disk = block_count * 4 + 8; +} + +/************************************************************************/ +/* SerializedSize() */ +/************************************************************************/ + +uint32 VecSegDataIndex::SerializedSize() + +{ + return 8 + 4 * block_count; +} + +/************************************************************************/ +/* GetBlockIndex() */ +/************************************************************************/ + +const std::vector *VecSegDataIndex::GetIndex() + +{ +/* -------------------------------------------------------------------- */ +/* Load block map if needed. */ +/* -------------------------------------------------------------------- */ + if( !block_initialized ) + { + bool needs_swap = !BigEndianSystem(); + + block_index.resize( block_count ); + if( block_count > 0 ) + { + vs->ReadFromFile( &(block_index[0]), + offset_on_disk_within_section + + vs->vh.section_offsets[hsec_shape] + 8, + 4 * block_count ); + + if( needs_swap ) + SwapData( &(block_index[0]), 4, block_count ); + } + + block_initialized = true; + } + + return &block_index; +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +void VecSegDataIndex::Flush() + +{ + if( !dirty ) + return; + + GetIndex(); // force loading if not already loaded! + + PCIDSKBuffer wbuf( SerializedSize() ); + + memcpy( wbuf.buffer + 0, &block_count, 4 ); + memcpy( wbuf.buffer + 4, &bytes, 4 ); + memcpy( wbuf.buffer + 8, &(block_index[0]), 4*block_count ); + + bool needs_swap = !BigEndianSystem(); + + if( needs_swap ) + SwapData( wbuf.buffer, 4, block_count+2 ); + + // Make sure this section of the header is large enough. + int32 shift = (int32) wbuf.buffer_size - (int32) size_on_disk; + + if( shift != 0 ) + { + uint32 old_section_size = vs->vh.section_sizes[hsec_shape]; + +// fprintf( stderr, "Shifting section %d by %d bytes.\n", +// section, shift ); + + vs->vh.GrowSection( hsec_shape, old_section_size + shift ); + + if( section == sec_vert ) + { + // move record block index and shape index. + vs->MoveData( vs->vh.section_offsets[hsec_shape] + + vs->di[sec_vert].size_on_disk, + vs->vh.section_offsets[hsec_shape] + + vs->di[sec_vert].size_on_disk + shift, + old_section_size - size_on_disk ); + } + else + { + // only move shape index. + vs->MoveData( vs->vh.section_offsets[hsec_shape] + + vs->di[sec_vert].size_on_disk + + vs->di[sec_record].size_on_disk, + vs->vh.section_offsets[hsec_shape] + + vs->di[sec_vert].size_on_disk + + vs->di[sec_record].size_on_disk + + shift, + old_section_size + - vs->di[sec_vert].size_on_disk + - vs->di[sec_record].size_on_disk ); + } + + if( section == sec_vert ) + vs->di[sec_record].offset_on_disk_within_section += shift; + } + + // Actually write to disk. + vs->WriteToFile( wbuf.buffer, + offset_on_disk_within_section + + vs->vh.section_offsets[hsec_shape], + wbuf.buffer_size ); + + size_on_disk = wbuf.buffer_size; + dirty = false; +} + +/************************************************************************/ +/* GetSectionEnd() */ +/************************************************************************/ + +uint32 VecSegDataIndex::GetSectionEnd() + +{ + return bytes; +} + +/************************************************************************/ +/* SetSectionEnd() */ +/************************************************************************/ + +void VecSegDataIndex::SetSectionEnd( uint32 new_end ) + +{ + // should we keep track of the need to write this back to disk? + bytes = new_end; +} + +/************************************************************************/ +/* AddBlockToIndex() */ +/************************************************************************/ + +void VecSegDataIndex::AddBlockToIndex( uint32 block ) + +{ + GetIndex(); // force loading. + + block_index.push_back( block ); + block_count++; + dirty = true; +} + +/************************************************************************/ +/* SetDirty() */ +/* */ +/* This method is primarily used to mark the need to write the */ +/* index when the location changes. */ +/************************************************************************/ + +void VecSegDataIndex::SetDirty() + +{ + dirty = true; +} + +/************************************************************************/ +/* VacateBlockRange() */ +/* */ +/* Move any blocks in the indicated block range to the end of */ +/* the segment to make space for a growing header. */ +/************************************************************************/ + +void VecSegDataIndex::VacateBlockRange( uint32 start, uint32 count ) + +{ + GetIndex(); // make sure loaded. + + unsigned int i; + uint32 next_block = (uint32) (vs->GetContentSize() / block_page_size); + + for( i = 0; i < block_count; i++ ) + { + if( block_index[i] >= start && block_index[i] < start+count ) + { + vs->MoveData( block_index[i] * block_page_size, + next_block * block_page_size, + block_page_size ); + block_index[i] = next_block; + dirty = true; + next_block++; + } + } +} diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/vecsegdataindex.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/vecsegdataindex.h new file mode 100644 index 000000000..aa2221750 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/vecsegdataindex.h @@ -0,0 +1,85 @@ +/****************************************************************************** + * + * Purpose: Declaration of the VecSegIndex class. + * + * This class is used to manage a vector segment data block index. There + * will be two instances created, one for the record data (sec_record) and + * one for the vertices (sec_vert). This class is exclusively a private + * helper class for VecSegHeader. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef __INCLUDE_SEGMENT_VECSEGDATAINDEX_H +#define __INCLUDE_SEGMENT_VECSEGDATAINDEX_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_vectorsegment.h" + +#include +#include + +namespace PCIDSK +{ + class VecSegDataIndex + { + friend class CPCIDSKVectorSegment; + friend class VecSegHeader; + + public: + VecSegDataIndex(); + ~VecSegDataIndex(); + + void Initialize( CPCIDSKVectorSegment *seg, + int section ); + + uint32 SerializedSize(); + + void SetDirty(); + void Flush(); + + const std::vector *GetIndex(); + void AddBlockToIndex( uint32 block ); + void VacateBlockRange( uint32 start, uint32 count ); + + uint32 GetSectionEnd(); + void SetSectionEnd( uint32 new_size ); + + private: + CPCIDSKVectorSegment *vs; + + int section; + + uint32 offset_on_disk_within_section; + uint32 size_on_disk; + + bool block_initialized; + uint32 block_count; + uint32 bytes; + std::vector block_index; + bool dirty; + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_VECSEGDATAINDEX_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/vecsegheader.cpp b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/vecsegheader.cpp new file mode 100644 index 000000000..f27e5769e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/vecsegheader.cpp @@ -0,0 +1,505 @@ +/****************************************************************************** + * + * Purpose: Implementation of the VecSegHeader class. + * + * This class is used to manage reading and writing of the vector segment + * header section, growing them as needed. It is exclusively a private + * helper class for the CPCIDSKVectorSegment. + * + ****************************************************************************** + * Copyright (c) 2010 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pcidsk.h" +#include "core/pcidsk_utils.h" +#include "segment/vecsegheader.h" +#include "segment/cpcidskvectorsegment.h" +#include +#include +#include + +using namespace PCIDSK; + +/* -------------------------------------------------------------------- */ +/* Size of a block in the record/vertice block tables. This is */ +/* determined by the PCIDSK format and may not be changed. */ +/* -------------------------------------------------------------------- */ +static const int block_page_size = 8192; + +/************************************************************************/ +/* VecSegHeader() */ +/************************************************************************/ + +VecSegHeader::VecSegHeader() + +{ + vs = NULL; + initialized = false; + needs_swap = !BigEndianSystem(); +} + +/************************************************************************/ +/* ~VecSegHeader() */ +/************************************************************************/ + +VecSegHeader::~VecSegHeader() + +{ +} + +/************************************************************************/ +/* InitializeNew() */ +/* */ +/* Initialize the header of a new vector segment in a */ +/* consistent state for an empty segment. */ +/************************************************************************/ + +void VecSegHeader::InitializeNew() + +{ + PCIDSKBuffer header( 8 * 1024 ); + uint32 ivalue, hoffset; + + memset( header.buffer, 0, header.buffer_size ); + + // magic cookie + ivalue = 0xffffffff; + memcpy( header.buffer + 0, &ivalue, 4 ); + memcpy( header.buffer + 4, &ivalue, 4 ); + + ivalue = 21; + memcpy( header.buffer + 8, &ivalue, 4 ); + ivalue = 4; + memcpy( header.buffer + 12, &ivalue, 4 ); + ivalue = 19; + memcpy( header.buffer + 16, &ivalue, 4 ); + ivalue = 69; + memcpy( header.buffer + 20, &ivalue, 4 ); + ivalue = 1; + memcpy( header.buffer + 24, &ivalue, 4 ); + + // blocks in header. + ivalue = 1; + memcpy( header.buffer + 68, &ivalue, 4 ); + + // offset to Projection + hoffset = 88; + memcpy( header.buffer + 72, &hoffset, 4 ); + + // Project segment + double dvalue; + dvalue = 0.0; + memcpy( header.buffer + hoffset, &dvalue, 8 ); + memcpy( header.buffer + hoffset+8, &dvalue, 8 ); + dvalue = 1.0; + memcpy( header.buffer + hoffset+16, &dvalue, 8 ); + memcpy( header.buffer + hoffset+24, &dvalue, 8 ); + if( needs_swap ) + SwapData( header.buffer + hoffset, 8, 4 ); + hoffset += 33; + + // offset to RST + memcpy( header.buffer + 76, &hoffset, 4 ); + + // RST - two zeros means no rst + empty string. + hoffset += 9; + + // offset to Records + memcpy( header.buffer + 80, &hoffset, 4 ); + + // Records - zeros means no fields. + hoffset += 4; + + // offset to Shapes + memcpy( header.buffer + 84, &hoffset, 4 ); + + // Shapes - zero means no shapes. + hoffset += 4; + + if( needs_swap ) + SwapData( header.buffer, 4, 22 ); + + vs->WriteToFile( header.buffer, 0, header.buffer_size ); +} + +/************************************************************************/ +/* InitializeExisting() */ +/* */ +/* Establish the location and sizes of the various header */ +/* sections. */ +/************************************************************************/ + +void VecSegHeader::InitializeExisting() + +{ + if( initialized ) + return; + + initialized = true; + +/* -------------------------------------------------------------------- */ +/* Check fixed portion of the header to ensure this is a V6 */ +/* style vector segment. */ +/* -------------------------------------------------------------------- */ + static const unsigned char magic[24] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0, 0, 0, 21, 0, 0, 0, 4, 0, 0, 0, 19, 0, 0, 0, 69 }; + + if( memcmp( vs->GetData( sec_raw, 0, NULL, 24 ), magic, 24 ) != 0 ) + { + ThrowPCIDSKException( "Unexpected vector header values, possibly it is not a V6 vector segment?" ); + } + +/* -------------------------------------------------------------------- */ +/* Establish how big the header is currently. */ +/* -------------------------------------------------------------------- */ + memcpy( &header_blocks, vs->GetData( sec_raw, 68, NULL, 4 ), 4 ); + if( needs_swap ) + SwapData( &header_blocks, 4, 1 ); + +/* -------------------------------------------------------------------- */ +/* Load section offsets. */ +/* -------------------------------------------------------------------- */ + memcpy( section_offsets, vs->GetData( sec_raw, 72, NULL, 16 ), 16 ); + if( needs_swap ) + SwapData( section_offsets, 4, 4 ); + +/* -------------------------------------------------------------------- */ +/* Determine the size of the projection section. */ +/* -------------------------------------------------------------------- */ + ShapeField work_value; + uint32 next_off = section_offsets[hsec_proj]; + + next_off += 32; // xoff/yoff/xsize/ysize values. + + next_off = vs->ReadField( next_off, work_value, FieldTypeString, sec_raw ); + section_sizes[hsec_proj] = next_off - section_offsets[hsec_proj]; + +/* -------------------------------------------------------------------- */ +/* Determine the size of the RST. */ +/* -------------------------------------------------------------------- */ + // yikes, not too sure! for now assume it is empty. + section_sizes[hsec_rst] = 8; + +/* -------------------------------------------------------------------- */ +/* Load the field definitions. */ +/* -------------------------------------------------------------------- */ + int field_count, i; + + next_off = section_offsets[hsec_record]; + + next_off = vs->ReadField( next_off, work_value, FieldTypeInteger, sec_raw ); + field_count = work_value.GetValueInteger(); + + for( i = 0; i < field_count; i++ ) + { + next_off = vs->ReadField( next_off, work_value, FieldTypeString, sec_raw ); + field_names.push_back( work_value.GetValueString() ); + + next_off = vs->ReadField( next_off, work_value, FieldTypeString, sec_raw ); + field_descriptions.push_back( work_value.GetValueString() ); + + next_off = vs->ReadField( next_off, work_value, FieldTypeInteger, sec_raw ); + field_types.push_back( (ShapeFieldType) work_value.GetValueInteger() ); + + next_off = vs->ReadField( next_off, work_value, FieldTypeString, sec_raw ); + field_formats.push_back( work_value.GetValueString() ); + + next_off = vs->ReadField( next_off, work_value, field_types[i], sec_raw ); + field_defaults.push_back( work_value ); + } + + section_sizes[hsec_record] = next_off - section_offsets[hsec_record]; + +/* -------------------------------------------------------------------- */ +/* Fetch the vertex block basics. */ +/* -------------------------------------------------------------------- */ + next_off = section_offsets[hsec_shape]; + + vs->di[sec_vert].Initialize( vs, sec_vert ); + next_off += vs->di[sec_vert].SerializedSize(); + +/* -------------------------------------------------------------------- */ +/* Fetch the record block basics. */ +/* -------------------------------------------------------------------- */ + vs->di[sec_record].Initialize( vs, sec_record ); + next_off += vs->di[sec_record].SerializedSize(); + +/* -------------------------------------------------------------------- */ +/* Fetch the shapeid basics. */ +/* -------------------------------------------------------------------- */ + memcpy( &(vs->shape_count), vs->GetData(sec_raw,next_off,NULL,4), 4); + if( needs_swap ) + SwapData( &(vs->shape_count), 4, 1 ); + + next_off += 4; + vs->shape_index_start = 0; + + section_sizes[hsec_shape] = next_off - section_offsets[hsec_shape] + + vs->shape_count * 12; +} + +/************************************************************************/ +/* WriteFieldDefinitions() */ +/************************************************************************/ + +void VecSegHeader::WriteFieldDefinitions() + +{ + PCIDSKBuffer hbuf( 1000 ); + uint32 offset = 0, i; + ShapeField wrkfield; + + wrkfield.SetValue( (int32) field_names.size() ); + offset = vs->WriteField( offset, wrkfield, hbuf ); + + for( i = 0; i < field_names.size(); i++ ) + { + wrkfield.SetValue( field_names[i] ); + offset = vs->WriteField( offset, wrkfield, hbuf ); + + wrkfield.SetValue( field_descriptions[i] ); + offset = vs->WriteField( offset, wrkfield, hbuf ); + + wrkfield.SetValue( (int32) field_types[i] ); + offset = vs->WriteField( offset, wrkfield, hbuf ); + + wrkfield.SetValue( field_formats[i] ); + offset = vs->WriteField( offset, wrkfield, hbuf ); + + offset = vs->WriteField( offset, field_defaults[i], hbuf ); + } + + hbuf.SetSize( offset ); + + GrowSection( hsec_record, hbuf.buffer_size ); + vs->WriteToFile( hbuf.buffer, section_offsets[hsec_record], + hbuf.buffer_size ); + + // invalidate the raw buffer. + vs->raw_loaded_data.buffer_size = 0; +} + +/************************************************************************/ +/* GrowSection() */ +/* */ +/* If necessary grow/move the header section specified to have */ +/* the desired amount of room. Returns true if the header */ +/* section has moved. */ +/************************************************************************/ + +bool VecSegHeader::GrowSection( int hsec, uint32 new_size ) + +{ +/* -------------------------------------------------------------------- */ +/* Trivial case. */ +/* -------------------------------------------------------------------- */ + if( section_sizes[hsec] >= new_size ) + { + section_sizes[hsec] = new_size; + return false; + } + +/* -------------------------------------------------------------------- */ +/* Can we grow the section in it's currently location without */ +/* overlapping anything else? */ +/* -------------------------------------------------------------------- */ + int ihsec; + bool grow_ok = true; + uint32 last_used = 0; + + for( ihsec = 0; ihsec < 4; ihsec++ ) + { + if( ihsec == hsec ) + continue; + + if( section_offsets[ihsec] + section_sizes[ihsec] > last_used ) + last_used = section_offsets[ihsec] + section_sizes[ihsec]; + + if( section_offsets[hsec] >= + section_offsets[ihsec] + section_sizes[ihsec] ) + continue; + + if( section_offsets[ihsec] >= section_offsets[hsec] + new_size ) + continue; + + // apparent overlap + grow_ok = false; + } + +/* -------------------------------------------------------------------- */ +/* If we can grow in place and have space there is nothing to do. */ +/* -------------------------------------------------------------------- */ + if( grow_ok + && section_offsets[hsec] + new_size + < header_blocks * block_page_size ) + { + section_sizes[hsec] = new_size; + return false; + } + +/* -------------------------------------------------------------------- */ +/* Where will the section be positioned after grow? It might */ +/* be nice to search for a big enough hole in the existing area */ +/* to fit the section. */ +/* -------------------------------------------------------------------- */ + uint32 new_base; + + if( grow_ok ) + new_base = section_offsets[hsec]; + else + new_base = last_used; + +/* -------------------------------------------------------------------- */ +/* Does the header need to grow? */ +/* -------------------------------------------------------------------- */ + if( new_base + new_size > header_blocks * block_page_size ) + { + GrowHeader( (new_base+new_size+block_page_size-1) / block_page_size + - header_blocks ); + } + +/* -------------------------------------------------------------------- */ +/* Move the old section to the new location. */ +/* -------------------------------------------------------------------- */ + bool actual_move = false; + + if( new_base != section_offsets[hsec] ) + { + vs->MoveData( section_offsets[hsec], new_base, section_sizes[hsec] ); + actual_move = true; + } + + section_sizes[hsec] = new_size; + section_offsets[hsec] = new_base; + +/* -------------------------------------------------------------------- */ +/* Update the section offsets list. */ +/* -------------------------------------------------------------------- */ + if( actual_move ) + { + uint32 new_offset = section_offsets[hsec]; + if( needs_swap ) + SwapData( &new_offset, 4, 1 ); + vs->WriteToFile( &new_offset, 72 + hsec * 4, 4 ); + } + + return true; +} + +/************************************************************************/ +/* GrowBlockIndex() */ +/* */ +/* Allocate the requested number of additional blocks to the */ +/* data block index. */ +/************************************************************************/ + +void VecSegHeader::GrowBlockIndex( int section, int new_blocks ) + +{ + if( new_blocks == 0 ) + return; + + uint32 next_block = (uint32) (vs->GetContentSize() / block_page_size); + + while( new_blocks > 0 ) + { + vs->di[section].AddBlockToIndex( next_block++ ); + new_blocks--; + } + + if( GrowSection( hsec_shape, section_sizes[hsec_shape] + 4*new_blocks ) ) + { + vs->di[sec_vert].SetDirty(); + vs->di[sec_record].SetDirty(); + vs->shape_index_page_dirty = true; // we need to rewrite at new location + } +} + +/************************************************************************/ +/* ShapeIndexPrepare() */ +/* */ +/* When CPCIDSKVectorSegment::FlushLoadedShapeIndex() needs to */ +/* write out all the shapeid's and offsets, it calls this */ +/* method to find the offset from the start of the segment at */ +/* which it should do the writing. */ +/* */ +/* We use this opportunity to flush out the vertex, and record */ +/* block offsets if necessary, and to grow the header if needed */ +/* to hold the proposed shapeindex size. The passed in size */ +/* is the size in bytes from "Number of Shapes" on in the */ +/* "Shape section" of the header. */ +/************************************************************************/ + +uint32 VecSegHeader::ShapeIndexPrepare( uint32 size ) + +{ + GrowSection( hsec_shape, + size + + vs->di[sec_vert].size_on_disk + + vs->di[sec_record].size_on_disk ); + + return section_offsets[hsec_shape] + + vs->di[sec_vert].size_on_disk + + vs->di[sec_record].size_on_disk; +} + +/************************************************************************/ +/* GrowHeader() */ +/* */ +/* Grow the header by the requested number of blocks. This */ +/* will often involve migrating existing vector or record */ +/* section blocks on to make space since the header must be */ +/* contiguous. */ +/************************************************************************/ + +void VecSegHeader::GrowHeader( uint32 new_blocks ) + +{ +// fprintf( stderr, "GrowHeader(%d) to %d\n", +// new_blocks, header_blocks + new_blocks ); + +/* -------------------------------------------------------------------- */ +/* Process the two existing block maps, moving stuff on if */ +/* needed. */ +/* -------------------------------------------------------------------- */ + vs->di[sec_vert].VacateBlockRange( header_blocks, new_blocks ); + vs->di[sec_record].VacateBlockRange( header_blocks, new_blocks ); + +/* -------------------------------------------------------------------- */ +/* Write to ensure the segment is the new size. */ +/* -------------------------------------------------------------------- */ + vs->WriteToFile( "\0", (header_blocks+new_blocks) * block_page_size - 1, 1); + +/* -------------------------------------------------------------------- */ +/* Update to new header size. */ +/* -------------------------------------------------------------------- */ + header_blocks += new_blocks; + + uint32 header_block_buf = header_blocks; + + if( needs_swap ) + SwapData( &header_block_buf, 4, 1 ); + + vs->WriteToFile( &header_block_buf, 68, 4 ); +} + diff --git a/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/vecsegheader.h b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/vecsegheader.h new file mode 100644 index 000000000..509365c90 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/sdk/segment/vecsegheader.h @@ -0,0 +1,96 @@ +/****************************************************************************** + * + * Purpose: Declaration of the VecSegHeader class. + * + * This class is used to manage reading and writing of the vector segment + * header section, growing them as needed. It is exclusively a private + * helper class for the CPCIDSKVectorSegment. + * + ****************************************************************************** + * Copyright (c) 2009 + * PCI Geomatics, 50 West Wilmot Street, Richmond Hill, Ont, Canada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef __INCLUDE_SEGMENT_VECSEGHEADER_H +#define __INCLUDE_SEGMENT_VECSEGHEADER_H + +#include "pcidsk_config.h" +#include "pcidsk_types.h" +#include "pcidsk_buffer.h" +#include "pcidsk_vectorsegment.h" + +#include +#include + +namespace PCIDSK +{ + class CPCIDSKVectorSegment; + + /************************************************************************/ + /* VecSegHeader */ + /************************************************************************/ + + const int hsec_proj = 0; + const int hsec_rst = 1; + const int hsec_record = 2; + const int hsec_shape = 3; + + class VecSegHeader + { + public: + VecSegHeader(); + + ~VecSegHeader(); + + void InitializeNew(); + void InitializeExisting(); + + void GrowHeader( uint32 new_blocks ); + bool GrowSection( int hsec, uint32 new_size ); + void WriteHeaderSection( int hsec, PCIDSKBuffer &buffer ); + + void GrowBlockIndex( int section, int new_blocks ); + + uint32 section_offsets[4]; + uint32 section_sizes[4]; + + // Field Definitions + std::vector field_names; + std::vector field_descriptions; + std::vector field_types; + std::vector field_formats; + std::vector field_defaults; + + void WriteFieldDefinitions(); + uint32 ShapeIndexPrepare( uint32 byte_size ); + + CPCIDSKVectorSegment *vs; + + uint32 header_blocks; + + private: + + bool initialized; + bool needs_swap; + + }; +} // end namespace PCIDSK + +#endif // __INCLUDE_SEGMENT_VECTORSEGMENT_H diff --git a/bazaar/plugin/gdal/frmts/pcidsk/vsi_pcidsk_io.cpp b/bazaar/plugin/gdal/frmts/pcidsk/vsi_pcidsk_io.cpp new file mode 100644 index 000000000..397ae98bd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcidsk/vsi_pcidsk_io.cpp @@ -0,0 +1,290 @@ +/****************************************************************************** + * $Id: vsi_pcidsk_io.cpp 28459 2015-02-12 13:48:21Z rouault $ + * + * Project: PCIDSK Database File + * Purpose: PCIDSK SDK compatiable io interface built on VSI. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_multiproc.h" +#include "pcidsk.h" + +CPL_CVSID("$Id: vsi_pcidsk_io.cpp 28459 2015-02-12 13:48:21Z rouault $"); + +using namespace PCIDSK; + +EDBFile *GDAL_EDBOpen( std::string osFilename, std::string osAccess ); + +class VSI_IOInterface : public IOInterfaces +{ + virtual void *Open( std::string filename, std::string access ) const; + virtual uint64 Seek( void *io_handle, uint64 offset, int whence ) const; + virtual uint64 Tell( void *io_handle ) const; + virtual uint64 Read( void *buffer, uint64 size, uint64 nmemb, void *io_hanle ) const; + virtual uint64 Write( const void *buffer, uint64 size, uint64 nmemb, void *io_handle ) const; + virtual int Eof( void *io_handle ) const; + virtual int Flush( void *io_handle ) const; + virtual int Close( void *io_handle ) const; + + const char *LastError() const; +}; + +/************************************************************************/ +/* PCIDSK2GetIOInterfaces() */ +/************************************************************************/ + +const PCIDSK::PCIDSKInterfaces *PCIDSK2GetInterfaces() +{ + static VSI_IOInterface singleton_vsi_interface; + static PCIDSKInterfaces singleton_pcidsk2_interfaces; + + singleton_pcidsk2_interfaces.io = &singleton_vsi_interface; + singleton_pcidsk2_interfaces.OpenEDB = GDAL_EDBOpen; + + return &singleton_pcidsk2_interfaces; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +void * +VSI_IOInterface::Open( std::string filename, std::string access ) const + +{ + VSILFILE *fp = VSIFOpenL( filename.c_str(), access.c_str() ); + + if( fp == NULL ) + ThrowPCIDSKException( "Failed to open %s: %s", + filename.c_str(), LastError() ); + + return fp; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +uint64 +VSI_IOInterface::Seek( void *io_handle, uint64 offset, int whence ) const + +{ + VSILFILE *fp = (VSILFILE *) io_handle; + + uint64 result = VSIFSeekL( fp, offset, whence ); + + if( result == (uint64) -1 ) + ThrowPCIDSKException( "Seek(%d,%d): %s", + (int) offset, whence, + LastError() ); + + return result; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +uint64 VSI_IOInterface::Tell( void *io_handle ) const + +{ + VSILFILE *fp = (VSILFILE *) io_handle; + + return VSIFTellL( fp ); +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +uint64 VSI_IOInterface::Read( void *buffer, uint64 size, uint64 nmemb, + void *io_handle ) const + +{ + VSILFILE *fp = (VSILFILE *) io_handle; + + errno = 0; + + uint64 result = VSIFReadL( buffer, (size_t) size, (size_t) nmemb, fp ); + + if( errno != 0 && result == 0 && nmemb != 0 ) + ThrowPCIDSKException( "Read(%d): %s", + (int) size * nmemb, + LastError() ); + + return result; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +uint64 VSI_IOInterface::Write( const void *buffer, uint64 size, uint64 nmemb, + void *io_handle ) const + +{ + VSILFILE *fp = (VSILFILE *) io_handle; + + errno = 0; + + uint64 result = VSIFWriteL( buffer, (size_t) size, (size_t) nmemb, fp ); + + if( errno != 0 && result == 0 && nmemb != 0 ) + ThrowPCIDSKException( "Write(%d): %s", + (int) size * nmemb, + LastError() ); + + return result; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSI_IOInterface::Eof( void *io_handle ) const + +{ + return VSIFEofL( (VSILFILE *) io_handle ); +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSI_IOInterface::Flush( void *io_handle ) const + +{ + return VSIFFlushL( (VSILFILE *) io_handle ); +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSI_IOInterface::Close( void *io_handle ) const + +{ + return VSIFCloseL( (VSILFILE *) io_handle ); +} + +/************************************************************************/ +/* LastError() */ +/* */ +/* Return a string representation of the last error. */ +/************************************************************************/ + +const char *VSI_IOInterface::LastError() const + +{ + return strerror( errno ); +} + +/************************************************************************/ +/* If we are using the internal copy of the PCIDSK SDK we need */ +/* to provide stub implementations of GetDefaultIOInterfaces() */ +/* and GetDefaultMutex() */ +/************************************************************************/ + +#ifdef PCIDSK_INTERNAL + +const IOInterfaces *PCIDSK::GetDefaultIOInterfaces() +{ + static VSI_IOInterface singleton_vsi_interface; + + return &singleton_vsi_interface; +} + +/************************************************************************/ +/* CPLThreadMutex */ +/************************************************************************/ + +class CPLThreadMutex : public PCIDSK::Mutex + +{ +private: + CPLMutex *hMutex; + +public: + CPLThreadMutex(); + ~CPLThreadMutex(); + + int Acquire(void); + int Release(void); +}; + +/************************************************************************/ +/* CPLThreadMutex() */ +/************************************************************************/ + +CPLThreadMutex::CPLThreadMutex() + +{ + hMutex = CPLCreateMutex(); + CPLReleaseMutex( hMutex ); // it is created acquired, but we want it free. +} + +/************************************************************************/ +/* ~CPLThreadMutex() */ +/************************************************************************/ + +CPLThreadMutex::~CPLThreadMutex() + +{ + CPLDestroyMutex( hMutex ); +} + +/************************************************************************/ +/* Release() */ +/************************************************************************/ + +int CPLThreadMutex::Release() + +{ + CPLReleaseMutex( hMutex ); + return 1; +} + +/************************************************************************/ +/* Acquire() */ +/************************************************************************/ + +int CPLThreadMutex::Acquire() + +{ + return CPLAcquireMutex( hMutex, 100.0 ); +} + +/************************************************************************/ +/* DefaultCreateMutex() */ +/************************************************************************/ + +PCIDSK::Mutex *PCIDSK::DefaultCreateMutex(void) + +{ + return new CPLThreadMutex(); +} + +#endif /* def PCIDSK_INTERNAL */ diff --git a/bazaar/plugin/gdal/frmts/pcraster/GNUmakefile b/bazaar/plugin/gdal/frmts/pcraster/GNUmakefile new file mode 100644 index 000000000..9f8079fe1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/GNUmakefile @@ -0,0 +1,28 @@ + +include ../../GDALmake.opt + +CPPFLAGS := $(XTRA_OPT) $(CPPFLAGS) + +ifeq ($(PCRASTER_SETTING),internal) +CPPFLAGS += -DUSE_IN_GDAL -Ilibcsf +OBJ = _getcell.o _getrow.o _gsomece.o _putcell.o _rputrow.o angle.o attravai.o attrsize.o cellsize.o create2.o csfglob.o csfsup.o delattr.o dumconv.o endian.o filename.o gattrblk.o gattridx.o gcellrep.o gdattype.o getattr.o getx0.o gety0.o ggisfid.o gmaxval.o gminval.o gnrcols.o gnrrows.o gproj.o gputproj.o gvalscal.o gvartype.o gversion.o ismv.o kernlcsf.o legend.o mclose.o mopen.o moreattr.o mperror.o pgisfid.o pmaxval.o pminval.o putallmv.o putattr.o putsomec.o putx0.o puty0.o pvalscal.o rattrblk.o rcomp.o rcoords.o rdup2.o reseterr.o rextend.o rmalloc.o rrowcol.o ruseas.o setangle.o setmv.o setvtmv.o strconst.o strpad.o swapio.o trackmm.o vs2.o vsdef.o vsis.o vsvers.o wattrblk.o +endif + +OBJ += pcrasterdataset.o pcrastermisc.o pcrasterrasterband.o pcrasterutil.o + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o libcsf/*.o $(O_OBJ) + rm -fR html + +../o/%.$(OBJ_EXT): libcsf/%.c + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +docs: + doxygen doxygen.cfg + +ctags: + ctags *cpp *h diff --git a/bazaar/plugin/gdal/frmts/pcraster/doxygen.cfg b/bazaar/plugin/gdal/frmts/pcraster/doxygen.cfg new file mode 100644 index 000000000..df6c96147 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/doxygen.cfg @@ -0,0 +1,1161 @@ +# Doxyfile 1.3.9.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of source +# files, where putting all generated files in the same directory would otherwise +# cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is used +# as the annotated text. Otherwise, the brief description is used as-is. If left +# blank, the following values are used ("$name" is automatically replaced with the +# name of the entity): "The $name class" "The $name widget" "The $name file" +# "is" "provides" "specifies" "contains" "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. + +SHOW_DIRECTORIES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 4 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse the +# parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superseded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes that +# lay further from the root node will be omitted. Note that setting this option to +# 1 or 2 may greatly reduce the computation time needed for large code bases. Also +# note that a graph may be further truncated if the graph's image dimensions are +# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). +# If 0 is used for the depth value (the default), the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/AUTHORS b/bazaar/plugin/gdal/frmts/pcraster/libcsf/AUTHORS new file mode 100644 index 000000000..26bdb674a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/AUTHORS @@ -0,0 +1 @@ +PCRaster Research and Development team, Utrecht University diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/COPYING b/bazaar/plugin/gdal/frmts/pcraster/libcsf/COPYING new file mode 100644 index 000000000..ede206a54 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/COPYING @@ -0,0 +1,35 @@ +The following copyright and license applies to all files in +this directory (gdal/frmts/pcraster/libcsf). + +------------------------------------------------------------------------ + +Copyright (c) PCRaster owners +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +* Neither the name of Utrecht University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/README b/bazaar/plugin/gdal/frmts/pcraster/libcsf/README new file mode 100644 index 000000000..a6ba323ed --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/README @@ -0,0 +1,11 @@ +This library reads and writes the native PCRaster format, formerly known as CSF. + +More information about PCRaster can be found at: +http://www.pcraster.eu +http://pcraster.geo.uu.nl + +Questions can be directed to the PCRaster mailing list: +http://pcraster.geo.uu.nl/support/questions-2/ + +Bug reports can be submitted to the PCRaster SourceForge page: +http://sourceforge.net/p/pcraster/bugs-and-feature-requests/milestone/PCRaster/ diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/_getcell.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/_getcell.c new file mode 100644 index 000000000..4e5132d7f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/_getcell.c @@ -0,0 +1,26 @@ +#include "csf.h" +#include "csfimpl.h" + +/* read one cell from a CSF raster file + * RgetCell reads one cell value from a + * file. + * returns + * 1 if cell is successfully read, + * 0 if not + * + * example + * .so examples/csfdump1.tr + */ +size_t RgetCell( + MAP *map, /* map handle */ + size_t rowNr, /* row number of cell */ + size_t colNr, /* column number of cell */ + void *cellValue) /* write-only. buffer, large enough to hold + * the value of the cell in the file and app + * cell representation + */ +{ + return RgetSomeCells(map, + ( (map->raster.nrCols) * rowNr) + colNr, + (size_t)1, cellValue); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/_getrow.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/_getrow.c new file mode 100644 index 000000000..abc81eec9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/_getrow.c @@ -0,0 +1,25 @@ +#include "csf.h" +#include "csfimpl.h" + +/* read one row from a CSF raster file + * RgetRow reads one row of cells from a + * file. + * returns + * Number of cells successfully read + * + * example + * .so examples/_row.tr + */ +size_t RgetRow( + MAP *map, /* map handle */ + size_t rowNr, /* row number to be read */ + void *buf) /* write-only. buffer large enough to hold + * cell values of one row in both the file + * and in-app cell representation + */ +{ + return RgetSomeCells(map, + map->raster.nrCols*rowNr, + (size_t)map->raster.nrCols, + buf) ; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/_gsomece.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/_gsomece.c new file mode 100644 index 000000000..a9779b672 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/_gsomece.c @@ -0,0 +1,38 @@ +#include "csf.h" +#include "csfimpl.h" + +/* read a stream of cells + * RgetSomeCells views a raster as one linear stream of + * cells, with row i+1 placed after row i. + * In this stream any sequence can be read by specifying an + * offset and the number of cells to be read + * returns the number of cells read, just as fread + * + * example + * .so examples/somecell.tr + */ +size_t RgetSomeCells( + MAP *map, /* map handle */ + size_t offset, /* offset from pixel (row,col) = (0,0) */ + size_t nrCells, /* number of cells to be read */ + void *buf)/* write-only. Buffer large enough to + * hold nrCells cells in the in-file cell representation + * or the in-app cell representation. + */ +{ + + CSF_FADDR readAt; + size_t cellsRead; + UINT2 inFileCR = RgetCellRepr(map); + + offset <<= LOG_CELLSIZE(inFileCR); + readAt = ADDR_DATA + (CSF_FADDR)offset; + fseek(map->fp, (long)readAt, SEEK_SET); + cellsRead = map->read(buf, (size_t)CELLSIZE(inFileCR), + (size_t)nrCells, map->fp); + + PRECOND(map->file2app != NULL); + map->file2app(nrCells, buf); + + return(cellsRead); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/_putcell.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/_putcell.c new file mode 100644 index 000000000..26493d62b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/_putcell.c @@ -0,0 +1,28 @@ +#include "csf.h" +#include "csfimpl.h" + +/* write one cell to a CSF raster file + * RputCell writes one cell value to a + * file. + * returns + * 1 if cell is successfully written, not 1 if not. + * + * example + * .so examples/rawbin.tr + */ +size_t RputCell( +MAP *map, /* map handle */ +size_t rowNr, /* Row number of cell */ +size_t colNr, /* Column number of cell */ +void *cellValue) /* read-write. Buffer large enough to + * hold one cell in the in-file cell representation + * or the in-app cell representation. + * If these types are not equal then the buffer is + * converted from the in-app to the in-file + * cell representation. + */ +{ + return RputSomeCells(map, + (map->raster.nrCols * rowNr) + colNr, + (size_t)1, cellValue) ; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/_rputrow.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/_rputrow.c new file mode 100644 index 000000000..86ce5e10d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/_rputrow.c @@ -0,0 +1,28 @@ +#include "csf.h" +#include "csfimpl.h" + + +/* write one row to a CSF raster file + * RputRow writes one row of cell values to a + * file. + * returns + * number of cells successfully written. Should be equal + * to the number of columns if everything is OK. + * + * example + * .so examples/_row.tr + */ +size_t RputRow( +MAP *map, /* map handle */ +size_t rowNr, /* Row number of row */ +void *buf) /* read-write. Buffer large enough to + * hold one row in the in-file cell representation + * or the in-app cell representation. + * If these types are not equal then the buffer is + * converted from the in-app to the in-file + * cell representation. + */ +{ + return RputSomeCells(map, (map->raster.nrCols)*rowNr, + (size_t)map->raster.nrCols, buf) ; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/angle.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/angle.c new file mode 100644 index 000000000..f5cf32382 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/angle.c @@ -0,0 +1,50 @@ +#include "csf.h" +#include "csfimpl.h" + +/* M_PI */ +#include + +#ifndef M_PI +# define M_PI ((double)3.14159265358979323846) +#endif + +/* put new angle + * RputAngle changes the angle + * of the map. + * returns new angle or -1 in case of an error. + * + * Merrno + * NOACCESS + * BAD_ANGLE + */ +REAL8 RputAngle( + MAP *map, /* map handle */ + REAL8 angle) /* new angle */ +{ + CHECKHANDLE_GOTO(map, error); + if(! WRITE_ENABLE(map)) + { + M_ERROR(NOACCESS); + goto error; + } + if ((0.5*-M_PI) >= angle || angle >= (0.5*M_PI)) + { + M_ERROR(BAD_ANGLE); + goto error; + } + map->raster.angle = angle; + + return(angle); +error: return(-1.0); +} + +/* get new angle + * RgetAngle returns the angle + * of the map. + * returns angle of the map + */ +REAL8 RgetAngle( + const MAP *map) /* map handle */ +{ + return map->raster.angle; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/attravai.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/attravai.c new file mode 100644 index 000000000..f3dde6de2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/attravai.c @@ -0,0 +1,23 @@ +#include "csf.h" +#include "csfimpl.h" + +/* check if an attribute is available + * MattributeAvail search for the given id in the map. + * + * returns + * 0 if the attribute is not available, + * nonzero if the attribute is available + * + * Merrno + * ILLHANDLE + */ +int MattributeAvail( + MAP *m, /* map handle */ + CSF_ATTR_ID id) /* identification of attribute */ +{ + ATTR_CNTRL_BLOCK b; + + if (! CsfIsValidMap(m)) + return 0; + return(CsfGetAttrBlock(m, id, &b) != 0); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/attrsize.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/attrsize.c new file mode 100644 index 000000000..a707a6d6b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/attrsize.c @@ -0,0 +1,18 @@ +#include "csf.h" +#include "csfimpl.h" + +/* get the size of an attribute (LIBRARY_INTERNAL) + * returns + * 0 if the attribute is not available, + * or the nonzero size if the attribute is available. + */ +size_t CsfAttributeSize( + MAP *m, /* map handle */ + CSF_ATTR_ID id) /* identification of attribute */ +{ + ATTR_CNTRL_BLOCK b; + + if (CsfGetAttrBlock(m, id, &b) != 0) + return b.attrs[CsfGetAttrIndex(id, &b)].attrSize; + return 0; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/cellsize.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/cellsize.c new file mode 100644 index 000000000..cd69285d3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/cellsize.c @@ -0,0 +1,74 @@ +#include "csf.h" +#include "csfimpl.h" + +/* global header (opt.) and cellsize's prototypes "" */ + + +/* headers of this app. modules called */ + +/***************/ +/* EXTERNALS */ +/***************/ + +/**********************/ +/* LOCAL DECLARATIONS */ +/**********************/ + +/*********************/ +/* LOCAL DEFINITIONS */ +/*********************/ + +/******************/ +/* IMPLEMENTATION */ +/******************/ + + +/* get cell size + * returns the cell size or -1 in case of an error + * + * Merrno + * ILL_CELLSIZE + * ILLHANDLE + */ +REAL8 RgetCellSize( + const MAP *map) /* map handle */ +{ + CHECKHANDLE(map); + if ( map->raster.cellSize != map->raster.cellSizeDupl) + { + M_ERROR(ILL_CELLSIZE); + return -1; + } + + return(map->raster.cellSize); +} + +/* put cell size + * returns the new cell size or -1 + * in case of an error. + * + * Merrno + * ILLHANDLE + * NOACCESS + * ILL_CELLSIZE + */ +REAL8 RputCellSize( + MAP *map, /* map handle */ + REAL8 cellSize) /* new cell size */ +{ + CHECKHANDLE_GOTO(map, error); + if(! WRITE_ENABLE(map)) + { + M_ERROR(NOACCESS); + goto error; + } + if (cellSize <= 0.0) + { + M_ERROR(ILL_CELLSIZE); + goto error; + } + map->raster.cellSize = cellSize; + map->raster.cellSizeDupl = cellSize; + return(cellSize); +error: return(-1.0); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/create2.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/create2.c new file mode 100644 index 000000000..a2017f180 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/create2.c @@ -0,0 +1,222 @@ +#include "csf.h" +#include "csfimpl.h" + +#include +#include + +/* M_PI */ +#include +#ifndef M_PI +# define M_PI ((double)3.14159265358979323846) +#endif + +/* + * Create a new CSF-Raster-file + * The Rcreate function + * creates a new CSF-Raster-file of nrRows by nrCols where each + * cell is of type cellRepr. If the file already exists its + * contents is destroyed. The value of + * the pixels is undefined. MinMaxStatus is MM_KEEPTRACK. The + * access mode is M_READ_WRITE. + * It is not + * known if a file is created after a NOSPACE message. + * Returns + * if the file is created successfully, Rcreate returns + * a map handle. In case of an error Rcreate returns NULL. + * + * Merrno + * NOCORE, BAD_CELLREPR, BAD_PROJECTION, OPENFAILED, NOSPACE. + * CONFL_CELLREPR and BAD_VALUESCALE will generate a failed assertion in DEBUG mode. + * + * Example: + * .so examples/create2.tr + * + */ + +MAP *Rcreate( + const char *fileName, /* name of the file to be created */ + size_t nrRows, /* the number of rows */ + size_t nrCols, /* the number of columns */ + CSF_CR cellRepr, /* the cell representation must be complaint with the data type + */ + CSF_VS dataType, /* a.k.a. the value scale. + */ + CSF_PT projection, /* + */ + REAL8 xUL, /* x co-ordinate of upper left */ + REAL8 yUL, /* y co-ordinate of upper left */ + REAL8 angle, /* counter clockwise rotation angle + * of the grid top compared to the + * x-axis in radians. Legal value are + * between -0.5 pi and 0.5 pi + */ + REAL8 cellSize) /* cell size of pixel */ +{ + MAP *newMap; + size_t fileSize; + char crap = 0; + + if (! CsfIsBootedCsfKernel()) + CsfBootCsfKernel(); + + newMap = (MAP *)CSF_MALLOC(sizeof(MAP)); + if (newMap == NULL) + { + M_ERROR(NOCORE); + goto errorMapAlloc; + } + newMap->fileName = (char *)CSF_MALLOC(strlen(fileName)+1); + if (newMap->fileName == NULL) + { + M_ERROR(NOCORE); + goto errorNameAlloc; + } + + if (!( + cellRepr == CR_INT4 || + cellRepr == CR_UINT1 || + cellRepr == CR_REAL4 || + cellRepr == CR_REAL8 )) + { + M_ERROR(BAD_CELLREPR); + goto error_notOpen; + } + + switch(dataType) { + case VS_BOOLEAN: + case VS_LDD: + if (cellRepr != CR_UINT1) + { + PROG_ERROR(CONFL_CELLREPR); + goto error_notOpen; + } + break; + case VS_NOMINAL: + case VS_ORDINAL: + if (IS_REAL(cellRepr)) + { + PROG_ERROR(CONFL_CELLREPR); + goto error_notOpen; + } + break; + case VS_SCALAR: + case VS_DIRECTION: + if (!IS_REAL(cellRepr)) + { + PROG_ERROR(CONFL_CELLREPR); + goto error_notOpen; + } + break; + default: + PROG_ERROR(BAD_VALUESCALE); + goto error_notOpen; + } + + if (cellSize <= 0.0) + { + M_ERROR(ILL_CELLSIZE); + goto error_notOpen; + } + + if ((0.5*-M_PI) >= angle || angle >= (0.5*M_PI)) + { + M_ERROR(BAD_ANGLE); + goto error_notOpen; + } + + newMap->fileAccessMode = M_READ_WRITE; + (void)strcpy(newMap->fileName, fileName); + + newMap->fp = fopen (fileName, S_CREATE); + if(newMap->fp == NULL) + { + /* we should analyse the errno parameter + * here to get the reason + */ + M_ERROR(OPENFAILED); + goto error_notOpen; + } + /* + fflush(newMap->fp); WHY? + */ + + (void)memset(&(newMap->main),0, sizeof(CSF_MAIN_HEADER)); + (void)memset(&(newMap->raster),0, sizeof(CSF_RASTER_HEADER)); + /* put defaults values */ + + /* assure signature is padded with 0x0 */ + (void)memset(newMap->main.signature, 0x0, (size_t)CSF_SIG_SPACE); + (void)strcpy(newMap->main.signature, CSF_SIG); + newMap->main.version = CSF_VERSION_2; + newMap->main.gisFileId = 0; + newMap->main.projection = PROJ_DEC_T2B(projection); + newMap->main.attrTable = 0; /* initially no attributes */ + newMap->main.mapType = T_RASTER; + + /* write endian mode current machine: */ + newMap->main.byteOrder= ORD_OK; + +#ifdef DEBUG + newMap->read = (CSF_READ_FUNC)CsfReadPlain; + newMap->write = (CSF_READ_FUNC)CsfWritePlain; +#else + newMap->read = (CSF_READ_FUNC)fread; + newMap->write = (CSF_READ_FUNC)fwrite; +#endif + + newMap->raster.valueScale = dataType; + newMap->raster.cellRepr = cellRepr; + CsfSetVarTypeMV( &(newMap->raster.minVal), cellRepr); + CsfSetVarTypeMV( &(newMap->raster.maxVal), cellRepr); + newMap->raster.xUL = xUL; + newMap->raster.yUL = yUL; + newMap->raster.nrRows = nrRows; + newMap->raster.nrCols = nrCols; + newMap->raster.cellSize = cellSize; + newMap->raster.cellSizeDupl = cellSize; + newMap->raster.angle = angle; + CsfFinishMapInit(newMap); + + /* set types to value cellRepr + newMap->types[STORED_AS]= (UINT1)newMap->raster.cellRepr; + newMap->types[READ_AS] = (UINT1)newMap->raster.cellRepr; + */ + newMap->appCR = (UINT1)newMap->raster.cellRepr; + newMap->app2file = CsfDummyConversion; + newMap->file2app = CsfDummyConversion; + + /* make file the size of the header and data */ + fileSize = nrRows*nrCols; + fileSize <<= LOG_CELLSIZE(cellRepr); + fileSize += ADDR_DATA; + + /* enlarge the file to the length needed by seeking and writing + one byte of crap */ + + if ( fseek(newMap->fp, (long)(fileSize-1),SEEK_SET) || /* fsetpos() is better */ + newMap->write(&crap, (size_t)1, (size_t)1, newMap->fp) != 1 ) + { + M_ERROR(NOSPACE); + goto error_open; + } + (void)fflush(newMap->fp); + if ( ftell(newMap->fp) != (long)fileSize) + { + M_ERROR(NOSPACE); + goto error_open; + } + + newMap->minMaxStatus = MM_KEEPTRACK; + + CsfRegisterMap(newMap); + + return(newMap); +error_open: + (void)fclose(newMap->fp); +error_notOpen: + CSF_FREE(newMap->fileName); +errorNameAlloc: + CSF_FREE(newMap); +errorMapAlloc: + return(NULL); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/csf.h b/bazaar/plugin/gdal/frmts/pcraster/libcsf/csf.h new file mode 100644 index 000000000..c9cb3fe6a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/csf.h @@ -0,0 +1,361 @@ +#ifndef INCLUDED_CSF +#define INCLUDED_CSF + +#ifdef CSF_V1 +# error new include file used while CSF_V1 is defined +#endif + +#ifndef INCLUDED_CSFTYPES +#include "csftypes.h" +#define INCLUDED_CSFTYPES +#endif + +#ifdef __cplusplus + extern "C" { +#endif + + + +#include +#include "csfattr.h" + + +/*****************************************************************/ +/* */ +/* RUU CROSS SYSTEM MAP FORMAT */ +/* VERSION 2 */ +/*****************************************************************/ + +extern int Merrno; /* declared in csfglob.c */ + +/* CSF_VAR_TYPE can hold every possible */ +/* data type */ +typedef REAL8 CSF_VAR_TYPE; + +/* values for CSF_MAIN_HEADER.mapType : */ +#define T_RASTER 1 + +/* CSF_FADDR can hold any location in the file + * CSF_FADDR is always an offset from the begin + * (0) of the file + */ +typedef UINT4 CSF_FADDR32; +typedef long CSF_FADDR; + +/* value for first 27 bytes of MAIN_HEADER.signature */ +#define CSF_SIG "RUU CROSS SYSTEM MAP FORMAT" +#define CSF_SIZE_SIG (sizeof(CSF_SIG)-1) + +#define CSF_SIG_SPACE ((size_t)32) + +typedef struct CSF_MAIN_HEADER +{ + char signature[CSF_SIG_SPACE]; + UINT2 version; + UINT4 gisFileId; + UINT2 projection; + CSF_FADDR32 attrTable; + UINT2 mapType; + UINT4 byteOrder; +} CSF_MAIN_HEADER; + +/******************************************************************/ +/* Definition of the second header */ +/******************************************************************/ +/* CSF_MAIN_HEADER.mapType decides which structure is */ +/* used as second header */ +/******************************************************************/ +/******************************************************************/ +/* Definition of the raster header */ +/******************************************************************/ +typedef struct CSF_RASTER_HEADER +{ + /* see #def's of VS_* + */ + UINT2 valueScale; + /* see #def's of CR_* + */ + UINT2 cellRepr; + + /* minVal holds a value equal or less than the + * minimum value in the cell matrix + */ + CSF_VAR_TYPE minVal; + + /* maxVal holds a value equal or greater than the + * maximum value in the cell matrix + */ + CSF_VAR_TYPE maxVal; + + /* co-ordinate of upper left corner + */ + REAL8 xUL; + REAL8 yUL; + + UINT4 nrRows; + UINT4 nrCols; + + /* CSF version 1 problem: X and Y cellsize + * could differ, no longer the case + * even though cellSizeX and cellSizeY + * are stored separate, they should be equal + * all apps. are working with square pixels + */ + REAL8 cellSize; /* was cellSizeX */ + REAL8 cellSizeDupl; /* was cellSizeY */ + + /* new in version 2 + * rotation angle of grid + */ + REAL8 angle; + + /* remainder is not part of + * file header + */ + /* cosine and sine of + * the angle are computed + * when opening or creating + * the file + */ + REAL8 angleCos; + REAL8 angleSin; + CSF_PT projection; /* copy of main header */ +} CSF_RASTER_HEADER; + +/*******************************************************************/ +/* mode values */ +/*******************************************************************/ + +/* bit-mapped values: */ +enum MOPEN_PERM { + M_READ=1, /* open read only */ + M_WRITE=2, /* open write only */ + M_READ_WRITE=3 /* open for both reading and writing */ +}; + + + +/****************************************************************/ +/* Error listing return messages */ +/****************************************************************/ + +/* values for errolist */ +/* happens frequently + * assure 0 value + * bogs on mingw + * # if NOERROR != 0 + * # error EXPECT NOERROR TO BE 0 + */ +# define NOERROR 0 + +#define OPENFAILED 1 +#define NOT_CSF 2 +#define BAD_VERSION 3 +#define BAD_BYTEORDER 4 +#define NOCORE 5 +#define BAD_CELLREPR 6 +#define NOACCESS 7 +#define ROWNR2BIG 8 +#define COLNR2BIG 9 +#define NOT_RASTER 10 +#define BAD_CONVERSION 11 +#define NOSPACE 12 +#define WRITE_ERROR 13 +#define ILLHANDLE 14 +#define READ_ERROR 15 +#define BADACCESMODE 16 +#define ATTRNOTFOUND 17 +#define ATTRDUPL 18 +#define ILL_CELLSIZE 19 +#define CONFL_CELLREPR 20 +#define BAD_VALUESCALE 21 +#define XXXXXXXXXXXX 22 +#define BAD_ANGLE 23 +#define CANT_USE_AS_BOOLEAN 24 +#define CANT_USE_WRITE_BOOLEAN 25 +#define CANT_USE_WRITE_LDD 26 +#define CANT_USE_AS_LDD 27 +#define CANT_USE_WRITE_OLDCR 28 +#define ILLEGAL_USE_TYPE 29 +/* number of errors */ +#define ERRORNO 30 + +typedef void (*CSF_CONV_FUNC)(size_t, void *); +/* conversion function for reading + * and writing + */ +typedef size_t (*CSF_WRITE_FUNC)(void *buf, size_t size, size_t n, FILE *f); +typedef size_t (*CSF_READ_FUNC)(void *buf, size_t size, size_t n, FILE *f); + +typedef struct MAP +{ + CSF_CONV_FUNC file2app; + CSF_CONV_FUNC app2file; + UINT2 appCR; + CSF_MAIN_HEADER main; + CSF_RASTER_HEADER raster; + char *fileName; + FILE *fp; + int fileAccessMode; + int mapListId; + UINT2 minMaxStatus; + + CSF_WRITE_FUNC write; + CSF_READ_FUNC read; +}MAP; + +typedef CSF_RASTER_HEADER CSF_RASTER_LOCATION_ATTRIBUTES; + + +/************************************************************/ +/* */ +/* PROTOTYPES OF RUU CSF */ +/* */ +/************************************************************/ + +MAP *Rcreate(const char *fileName, + size_t nrRows, size_t nrCols, + CSF_CR cellRepr, CSF_VS dataType, + CSF_PT projection, REAL8 xUL, REAL8 yUL, REAL8 angle, REAL8 cellSize); +MAP *Rdup(const char *toFile , const MAP *from, + CSF_CR cellRepr, CSF_VS dataType); +void *Rmalloc(const MAP *m, size_t nrOfCells); +int RuseAs(MAP *m, CSF_CR useType); + +MAP *Mopen(const char *fname, enum MOPEN_PERM mode); +enum MOPEN_PERM MopenPerm(const MAP *m); + +int Rcompare(const MAP *m1,const MAP *m2); +int RgetLocationAttributes( + CSF_RASTER_LOCATION_ATTRIBUTES *l, /* fill in this struct */ + const MAP *m); /* map handle to copy from */ +int RcompareLocationAttributes( + const CSF_RASTER_LOCATION_ATTRIBUTES *m1, /* */ + const CSF_RASTER_LOCATION_ATTRIBUTES *m2); /* */ + +int Mclose(MAP *map); +void Merror(int nr); +void Mperror(const char *userString); +void MperrorExit(const char *userString, int exitCode); +const char *MstrError(void); +const char *MgetFileName(const MAP *m); +void ResetMerrno(void); + +int RputAllMV(MAP *newMap); +size_t RputRow(MAP *map, size_t rowNr, void *buf); +size_t RputSomeCells (MAP *map, size_t somePlace, size_t nrCells, void *buf); +size_t RputCell(MAP *map, size_t rowNr, size_t colNr, void *cellValue); +size_t RgetRow(MAP *map, size_t rowNr, void *buf); +size_t RgetSomeCells (MAP *map, size_t somePlace, size_t nrCells, void *buf); +size_t RgetCell(MAP *map, size_t rowNr, size_t colNr, void *cellValue); + +int RputDoNotChangeValues(const MAP *map); +int MnativeEndian(const MAP *map); + + +UINT4 MgetMapDataType(const MAP *map); +UINT4 MgetVersion(const MAP *map); +UINT4 MgetGisFileId(const MAP *map); +UINT4 MputGisFileId(MAP *map,UINT4 gisFileId); +int IsMVcellRepr(CSF_CR cellRepr, const void *cellValue); +int IsMV(const MAP *map, const void *cellValue); +CSF_VS RgetValueScale(const MAP *map); +CSF_VS RputValueScale(MAP *map, CSF_VS valueScale); +int RvalueScaleIs(const MAP *m, CSF_VS vs); +int RvalueScale2(CSF_VS vs); +CSF_CR RdefaultCellRepr(CSF_VS vs); +CSF_CR RgetCellRepr(const MAP *map); +CSF_CR RgetUseCellRepr(const MAP *map); + +int RgetMinVal(const MAP *map, void *minVal); +int RgetMaxVal(const MAP *map, void *maxVal); +void RputMinVal(MAP *map, const void *minVal); +void RputMaxVal(MAP *map, const void *maxVal); +void RdontTrackMinMax(MAP *m); + +REAL8 RgetXUL(const MAP *map); +REAL8 RgetYUL(const MAP *map); +REAL8 RputXUL(MAP *map, REAL8 xUL); +REAL8 RputYUL(MAP *map, REAL8 yUL); + +/* old names: + */ +#define RgetX0 RgetXUL +#define RgetY0 RgetYUL +#define RputX0 RputXUL +#define RputY0 RputYUL + + +REAL8 RgetAngle(const MAP *map); +REAL8 RputAngle(MAP *map, REAL8 Angle); + +size_t RgetNrCols(const MAP *map); +size_t RgetNrRows(const MAP *map); + +REAL8 RgetCellSize(const MAP *map); +REAL8 RputCellSize(MAP *map, REAL8 newCellSize); + +int RgetCoords( const MAP *m, int inCelPos, size_t row, size_t col, double *x, double *y); +int RrowCol2Coords(const MAP *m, double row, double col, double *x, double *y); +void RasterRowCol2Coords(const CSF_RASTER_LOCATION_ATTRIBUTES *m, +double row, double col, double *x, double *y); + +CSF_PT MgetProjection(const MAP *map); +CSF_PT MputProjection(MAP *map, CSF_PT p); + +void SetMV(const MAP *m, void *cell); +void SetMVcellRepr(CSF_CR cellRepr, void *cell); +void SetMemMV(void *dest,size_t nrElements,CSF_CR type); +/* historical error, implemented twice + * SetArrayMV => SetMemMV + */ + +int MattributeAvail(MAP *m, CSF_ATTR_ID id); +CSF_ATTR_ID MdelAttribute(MAP *m, CSF_ATTR_ID id); + + +size_t MgetNrLegendEntries(MAP *m); +int MgetLegend(MAP *m, CSF_LEGEND *l); +int MputLegend(MAP *m, CSF_LEGEND *l, size_t nrEntries); + +size_t MgetHistorySize(MAP *m); +size_t MgetDescriptionSize(MAP *m); +size_t MgetNrColourPaletteEntries(MAP *m); +size_t MgetNrGreyPaletteEntries(MAP *m); +int MgetDescription(MAP *m, char *des); +int MgetHistory(MAP *m, char *history); +int MgetColourPalette(MAP *m, UINT2 *pal); +int MgetGreyPalette(MAP *m, UINT2 *pal); +int MputDescription(MAP *m, char *des); +int MputHistory(MAP *m, char *history); +int MputColourPalette(MAP *m, UINT2 *pal, size_t nrTupels); +int MputGreyPalette(MAP *m, UINT2 *pal, size_t nrTupels); + +int Rcoords2RowCol( const MAP *m, + double x, double y, + double *row, double *col); +void RasterCoords2RowCol( const CSF_RASTER_LOCATION_ATTRIBUTES *m, + double x, double y, + double *row, double *col); +int RasterCoords2RowColChecked( const CSF_RASTER_LOCATION_ATTRIBUTES *m, + double x, double y, + double *row, double *col); + +int RgetRowCol(const MAP *m, + double x, double y, + size_t *row, size_t *col); + +const char *RstrCellRepr(CSF_CR cr); +const char *RstrValueScale(CSF_VS vs); +const char *MstrProjection(CSF_PT p); +int RgetValueScaleVersion(const MAP *m); + +void RcomputeExtend(REAL8 *xUL, REAL8 *yUL, size_t *nrRows, size_t *nrCols, + double x_1, double y_1, double x_2, double y_2, CSF_PT projection, REAL8 cellSize, double rounding); + +#ifdef __cplusplus + } +#endif + +/* INCLUDED_CSF */ +#endif diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/csfattr.h b/bazaar/plugin/gdal/frmts/pcraster/libcsf/csfattr.h new file mode 100644 index 000000000..115550ab6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/csfattr.h @@ -0,0 +1,29 @@ +#ifndef CSF__ATTR_H +#define CSF__ATTR_H + +#ifdef __cplusplus + extern "C" { +#endif + +typedef enum CSF_ATTR_ID { + ATTR_ID_LEGEND_V1=1, /* version 1 legend */ + ATTR_ID_HISTORY=2, /* history fields */ + ATTR_ID_COLOUR_PAL=3, /* colour palette */ + ATTR_ID_GREY_PAL=4, /* grey palette */ + ATTR_ID_DESCRIPTION=5, /* description */ + ATTR_ID_LEGEND_V2=6 /* version 2 legend */ +} CSF_ATTR_ID; + +#define CSF_LEGEND_ENTRY_SIZE 64 +#define CSF_LEGEND_DESCR_SIZE 60 + +typedef struct CSF_LEGEND { + INT4 nr; + char descr[60]; +} CSF_LEGEND; + +#ifdef __cplusplus + } +#endif + +#endif /* CSF__ATTR_H */ diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/csfglob.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/csfglob.c new file mode 100644 index 000000000..246280f51 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/csfglob.c @@ -0,0 +1,7 @@ +#include "csf.h" +#include "csfimpl.h" + +/* global variable set on the last error condition + * Most functions sets this variable in case of an error condition. + */ +int Merrno = NOERROR; diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/csfimpl.h b/bazaar/plugin/gdal/frmts/pcraster/libcsf/csfimpl.h new file mode 100644 index 000000000..61d91efb0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/csfimpl.h @@ -0,0 +1,238 @@ +#ifndef CSF__IMPL_H +#define CSF__IMPL_H + +/******************************************************************/ +/******************************************************************/ +/** */ +/** RUU CROSS SYSTEM MAP FORMAT */ +/** */ +/******************************************************************/ +/* number of maps that can be open at one time + * FOPEN_MAX should be there in Ansi-C in + * stdio.h is included in csf.h, check if csf.h is included first + */ +#ifndef INCLUDED_CSF +# error csfimpl.h included before csf.h +#endif + +/******************************************************************/ +/* CSFIMPL.H */ +/******************************************************************/ + +/******************************************************************/ +/* Starting Addresses */ +/******************************************************************/ +/* Constants of type CSF_FADDR */ + +#define ADDR_MAIN_HEADER ((CSF_FADDR)0) +#define ADDR_SECOND_HEADER ((CSF_FADDR)64) +#define ADDR_DATA ((CSF_FADDR)256) + +/* Padding of headers + */ +#define RASTER_HEADER_FILL_SIZE ((size_t)124) +#define MAIN_HEADER_FILL_SIZE ((size_t)14) +/* Used in mclose.c + */ +#define MAX_HEADER_FILL_SIZE (RASTER_HEADER_FILL_SIZE) + +/* values for MAIN_HEADER.byteOrder */ +#define ORD_OK 0x00000001L +#define ORD_SWAB 0x01000000L + +/* INTERFACE with PCRaster software + */ +#ifdef USE_IN_PCR +# include "stddefx.h" +# include "misc.h" /* malloc, free */ +# define CSF_MALLOC ChkMalloc +# define CSF_FREE Free +#else +# include /* malloc, free,abs */ +# include +# define CSF_MALLOC malloc +# define CSF_FREE free +# ifdef DEBUG +# define PRECOND(x) assert(x) +# define POSTCOND(x) assert(x) +# else +# define PRECOND(x) +# define POSTCOND(x) +# endif +#ifndef USE_IN_GDAL +# define ABS(x) abs(x) +#endif +# define USED_UNINIT_ZERO 0 +#endif + +/******************************************************************/ +/* Definition of the main header */ +/******************************************************************/ + +/* value for MAIN_HEADER.version */ +#define CSF_VERSION_1 1 +#define CSF_VERSION_2 2 + + +#define IS_UNSIGNED(type) (!((type) & CSF_FLOAT_SIGN_MASK)) +#define IS_SIGNED(type) ((type) & CSF_SIGN_MASK) +#define IS_REAL(type) ((type) & CSF_FLOAT_MASK) + +/******************************************************************/ +/* Compiler conditions */ +/******************************************************************/ +/* + sizeof(INT1) == 1 + sizeof(INT2) == 2 + sizeof(INT4) == 4 + sizeof(UINT1) == 1 + sizeof(UINT2) == 2 + sizeof(UINT4) == 4 + sizeof(REAL4) == 4 + sizeof(REAL8) == 8 +*/ + +/******************************************************************/ +/* Definition of an attribute control block */ +/******************************************************************/ + +#define NR_ATTR_IN_BLOCK 10 +#define LAST_ATTR_IN_BLOCK (NR_ATTR_IN_BLOCK-1) + + +typedef struct ATTR_REC +{ + UINT2 attrId; /* attribute identifier */ + CSF_FADDR32 attrOffset; /* file-offset of attribute */ + UINT4 attrSize; /* size of attribute in bytes */ +} ATTR_REC; + +typedef struct ATTR_CNTRL_BLOCK +{ + ATTR_REC attrs[NR_ATTR_IN_BLOCK]; + CSF_FADDR32 next; /* file-offset of next block */ +} ATTR_CNTRL_BLOCK; + +#define SIZE_OF_ATTR_CNTRL_BLOCK \ + ((NR_ATTR_IN_BLOCK * (sizeof(UINT2) + sizeof(CSF_FADDR32) + sizeof(UINT4))) \ + + sizeof(CSF_FADDR32) ) + +/* Note that two empty holes in the attribute area are never merged */ + + +#define ATTR_NOT_USED 0x0 + /* value of attrId field if an attribute is deleted */ + /* attrOffset and attrSize must remain valid; so a new + * attribute can be inserted if it's size is equal or + * smaller then attrSize + */ + +#define END_OF_ATTRS 0xFFFF + /* value of attrId field if there are no more attributes */ + /* INDEED: A BUG we wanted to use the highest value (0xFFFFFFFF) + * but we made a mistake. Don't change, 1023 is just as + * good as (2^16)-1 + */ + +/* does y decrements from + * top to bottom in this projection type? + * this will also hold for the old types + * since only PT_XY was increments from + * top to bottom, like PT_YINCT2B + * PT_XY and PT_YINCT2B are the only one that are + * 0, the others all have a nonzero value + */ +#define PROJ_DEC_T2B(x) (x != 0) + +#define MM_KEEPTRACK 0 +#define MM_DONTKEEPTRACK 1 +#define MM_WRONGVALUE 2 + +#define M_ERROR(errorCode) Merrno = errorCode +#define PROG_ERROR(errorCode) Merrno = errorCode + +#define S_READ "rb" /* Open for read only */ +#define S_WRITE "r+b" /* Open for write only I don't know an */ + /* appropriate mode "r+b" seems most app. */ +#define S_READ_WRITE "r+b" /* Open for reading and writing */ +#define S_CREATE "w+b" /* Create new file for reading and writing */ + +#define WRITE_ENABLE(m) (m->fileAccessMode & M_WRITE) +#define READ_ENABLE(m) (m->fileAccessMode & M_READ) +#define IS_BAD_ACCESS_MODE(mode) \ + (mode >> 2) /* use only 2 bits for modes */ + +#define READ_AS 0 /* note that READ_AS is also used on procedures + that implies write access, under the condition of + write access both type bytes are equal, and the + READ_AS byte is 0-alligned in the record, so this + byte is quicker accessible */ + /* we will call READ_AS the ONLY_AS if write access is implied */ +#define ONLY_AS 0 +#define STORED_AS 1 + +/* Typed zero values to keep lint happy + * mainly used in conversion macro's + */ +#define ZERO_UINT1 ((UINT1)0) +#define ZERO_UINT2 ((UINT2)0) +#define ZERO_UINT4 ((UINT4)0) +#define ZERO_INT1 ((INT1) 0) +#define ZERO_INT2 ((INT2) 0) +#define ZERO_INT4 ((INT4) 0) +#define ZERO_REAL4 ((REAL4)0) +#define ZERO_REAL8 ((REAL8)0) + +/* LIBRARY_INTERNAL's: */ +/* OLD STUFF +void TransForm(const MAP *map, UINT4 nrCells, void *buf); + */ + +void CsfFinishMapInit(MAP *m); +void CsfDummyConversion(size_t n, void *buf); +int CsfIsValidMap(const MAP *m); +void CsfUnloadMap(MAP *m); +void CsfRegisterMap(MAP *m); +int CsfIsBootedCsfKernel(void); +void CsfBootCsfKernel(void); +void CsfSetVarTypeMV( CSF_VAR_TYPE *var, CSF_CR cellRepr); +void CsfGetVarType(void *dest, const CSF_VAR_TYPE *src, CSF_CR cellRepr); +void CsfReadAttrBlock( MAP *m, CSF_FADDR32 pos, ATTR_CNTRL_BLOCK *b); +int CsfWriteAttrBlock(MAP *m, CSF_FADDR32 pos, ATTR_CNTRL_BLOCK *b); +int CsfGetAttrIndex(CSF_ATTR_ID id, const ATTR_CNTRL_BLOCK *b); +CSF_FADDR32 CsfGetAttrBlock(MAP *m, CSF_ATTR_ID id, ATTR_CNTRL_BLOCK *b); +CSF_FADDR32 CsfGetAttrPosSize(MAP *m, CSF_ATTR_ID id, size_t *size); +size_t CsfWriteSwapped(void *buf, size_t size, size_t n, FILE *f); +size_t CsfReadSwapped(void *buf, size_t size, size_t n, FILE *f); +size_t CsfWritePlain(void *buf, size_t size, size_t n, FILE *f); +size_t CsfReadPlain(void *buf, size_t size, size_t n, FILE *f); +void CsfSwap(void *buf, size_t size, size_t n); +char *CsfStringPad(char *s, size_t reqSize); + +CSF_FADDR32 CsfSeekAttrSpace(MAP *m, CSF_ATTR_ID id, size_t size); +CSF_ATTR_ID CsfPutAttribute( MAP *m, CSF_ATTR_ID id, size_t size, size_t nitems, void *attr); +CSF_ATTR_ID CsfGetAttribute(MAP *m, CSF_ATTR_ID id, size_t elSize, size_t *nmemb, void *attr); +size_t CsfAttributeSize(MAP *m, CSF_ATTR_ID id); +CSF_ATTR_ID CsfUpdateAttribute(MAP *m, CSF_ATTR_ID id, size_t itemSize, size_t nitems, void *attr); + +int CsfValidSize(size_t size); + +#define CHECKHANDLE_GOTO(m, label) \ + if (! CsfIsValidMap(m)) \ + { \ + M_ERROR(ILLHANDLE); \ + goto label; \ + } +#define CHECKHANDLE_RETURN(m, value) \ + if (! CsfIsValidMap(m)) \ + { \ + M_ERROR(ILLHANDLE); \ + return value; \ + } +#define CHECKHANDLE(m) \ + if (! CsfIsValidMap(m)) \ + { \ + M_ERROR(ILLHANDLE); \ + } + +#endif /* CSF__IMPL_H */ diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/csfsup.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/csfsup.c new file mode 100644 index 000000000..0276e63be --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/csfsup.c @@ -0,0 +1,25 @@ +#include "csf.h" +#include "csftypes.h" +#include /* memset */ + +/* set an array of cells to missing value + * SetMemMV sets an array of cells to missing value + */ +void SetMemMV( + void *buf, /* write-only buffer with cells */ + size_t nrElements, /* number of cells */ + CSF_CR cellRepr) /* cell representation */ +{ +size_t index; + + switch (cellRepr) { + case CR_INT1: (void)memset(buf,MV_INT1,nrElements);break; + case CR_INT2: for (index=0;index 224 + * VS_NOMINAL 0xE2 => 226 + * VS_ORDINAL 0xF2 => 242 + * VS_SCALAR 0xEB => 235 + * VS_DIRECTION 0xFB => 251 + * VS_LDD 0xF0 => 240 + * VS_VECTOR 0xEC => 236 + * VS_VECTOR 0xEC vector, not used yet + */ + +typedef enum CSF_VS { + + /* * version 1 datatypes, + * these can be returned by BUT NOT passed to a csf2 function + */ + VS_NOTDETERMINED=0, /* version 1 */ + VS_CLASSIFIED =1, /* version 1 */ + VS_CONTINUOUS =2, /* version 1 */ + + /* * version 2 datatypes + * these two can be returned by or passed to a csf2 function + */ + VS_BOOLEAN =0xE0,/* boolean, always UINT1, values: 0,1 or MV_UINT1 */ + VS_NOMINAL =0xE2,/* nominal, UINT1 or INT4 */ + VS_ORDINAL =0xF2,/* ordinal, UINT1 or INT4 */ + VS_SCALAR =0xEB,/* scalar, REAL4 or (maybe) REAL8 */ + VS_DIRECTION =0xFB,/* directional REAL4 or (maybe) REAL8, -1 means no direction */ + VS_LDD =0xF0,/* local drain direction, always UINT1, values: 1-9 or MV_UINT1 */ + + /* * this one CANNOT be returned by NOR passed to a csf2 function */ + VS_UNDEFINED =100 /* just some value different from the rest */ + +} CSF_VS; + + +/* CELL REPRESENTATION + CR_UINT1 0x00 => 0 + CR_INT4 0x26 => 38 + CR_REAL4 0x5A => 90 + CR_INT1 0x04 => 4 + CR_INT2 0x15 => 21 + CR_UINT2 0x11 => 17 + CR_UINT4 0x22 => 34 + CR_REAL8 0xDB => 219 + for vector types + CR_VECT4 0x?? + CR_VECT8 0x?? + */ + +typedef enum CSF_CR { + + /* * preferred version 2 cell representations + */ + + CR_UINT1 =0x00, /* boolean, ldd and small nominal and small ordinal */ + CR_INT4 =0x26, /* large nominal and large ordinal */ + CR_REAL4 =0x5A, /* single scalar and single directional */ + + /* * other version 2 cell representations + */ + + CR_REAL8 =0xDB, /* double scalar or directional, and also the only type that + * can hold all + * cell representation without loss of precision + */ + + /* * version 1 cell representations + * these can be returned by BUT NOT passed to a csf2 function + */ + + CR_INT1 =0x04, /* . */ + CR_INT2 =0x15, /* . */ + CR_UINT2 =0x11, /* . */ + CR_UINT4 =0x22, /* . */ + + /* * this one CANNOT be returned by NOR passed to a csf2 function */ + + CR_UNDEFINED =100 /* just some value different from the rest */ +} CSF_CR; + + + +/* how to get the cellsize from these type identifiers */ +#define CSF_SIZE_MASK ((size_t)0x03) +#define CSF_SIGN_MASK ((size_t)0x04) +#define CSF_FLOAT_MASK ((size_t)0x08) +#define CSF_FLOAT_SIGN_MASK ((size_t)0x0C) +#define CSF_SIZE_MV_MASK ((size_t)0x30) +#define CSF_SKIP_MASK ((size_t)0xC0) +/* low nibble is uniq for every CR_VALUE: */ +#define CSF_UNIQ_MASK ((size_t)0x0F) +#define CSF_POS_SIZE_MV_MASK ((size_t)4) +#define CSF_POS_SKIP_MASK ((size_t)6) +#define CSF_UNIQ_CR_MASK(type) ((size_t)((type) & CSF_UNIQ_MASK)) +#define LOG_CELLSIZE(type) ((size_t)((type) & CSF_SIZE_MASK)) +#define CELLSIZE(type) ((size_t)(1 << LOG_CELLSIZE(type))) +#define CSFSIZEOF(nr, type) ((size_t)(((size_t)nr) << LOG_CELLSIZE(type))) + +#include /* FLT_MIN, DBL_MAX, DBL_MAX, FLT_MAX */ + +#define MV_INT1 ((CSF_IN_GLOBAL_NS INT1)-128) +#define MV_INT2 ((CSF_IN_GLOBAL_NS INT2)-32768) +/* cl C4146 has it right + #define MV_INT4 ((CSF_IN_GLOBAL_NS INT4)-2147483648) + is dangerous + */ +#define MV_INT4 ((CSF_IN_GLOBAL_NS INT4)0x80000000L) + +#define MV_UINT1 ((CSF_IN_GLOBAL_NS UINT1)0xFF) +#define MV_UINT2 ((CSF_IN_GLOBAL_NS UINT2)0xFFFF) +#define MV_UINT4 ((CSF_IN_GLOBAL_NS UINT4)0xFFFFFFFFL) + +#define INT2_MIN ((INT2)(MV_INT2+1)) +#define INT2_MAX ((INT2)0x7FFF) + +#define UINT1_MIN ((UINT1)0) +#define UINT1_MAX ((UINT1)(MV_UINT1-1)) + +#define INT4_MIN ((INT4)(MV_INT4+1)) +#define INT4_MAX ((INT4)0x7FFFFFFFL) + +#define REAL4_MIN ((REAL4)(FLT_MIN)) +#define REAL4_MAX ((REAL4)(FLT_MAX)) + +#define REAL8_MIN ((REAL8)(DBL_MIN)) +#define REAL8_MAX ((REAL8)(DBL_MAX)) + + +/* x is a pointer to a value */ +#define IS_MV_UINT1(x) ((*((const CSF_IN_GLOBAL_NS UINT1 *)x)) == MV_UINT1) +#define IS_MV_UINT2(x) ((*((const CSF_IN_GLOBAL_NS UINT2 *)x)) == MV_UINT2) +#define IS_MV_UINT4(x) ((*((const CSF_IN_GLOBAL_NS UINT4 *)x)) == MV_UINT4) +#define IS_MV_INT1(x) ((*((const CSF_IN_GLOBAL_NS INT1 *)x)) == MV_INT1) +#define IS_MV_INT2(x) ((*((const CSF_IN_GLOBAL_NS INT2 *)x)) == MV_INT2) +#define IS_MV_INT4(x) ((*((const CSF_IN_GLOBAL_NS INT4 *)x)) == MV_INT4) + +/* MV_REAL4 and MV_REAL8 are bitpatterns with all 1's that + * are NAN's + * MV_REAL4 has the same bitpattern as a MV_UINT4 + * MV_REAL8 has the same bitpattern as two MV_UINT4's + * only the first 32 bits already identify a NAN, + * so that's what we test + */ +#ifdef CPU_LITTLE_ENDIAN +# ifdef CPU_BIG_ENDIAN +# error CPU_BIG_ENDIAN and CPU_LITTLE_ENDIAN are both defined +# endif +# ifdef INTEL16 +# define IS_MV_REAL4(x) (((const UINT2 *)(x))[1] == MV_UINT2) +# define IS_MV_REAL8(x) (((const UINT2 *)(x))[3] == MV_UINT2) +# else +# define IS_MV_REAL4(x) ((*((const CSF_IN_GLOBAL_NS UINT4_ALIASING *)(x))) == MV_UINT4) +# define IS_MV_REAL8(x) (((const CSF_IN_GLOBAL_NS UINT4_ALIASING *)(x))[1] == MV_UINT4) +# endif +#else +# ifdef CPU_BIG_ENDIAN +# ifdef CPU_LITTLE_ENDIAN +# error CPU_BIG_ENDIAN and CPU_LITTLE_ENDIAN are both defined +# endif +# define IS_MV_REAL4(x) (((const UINT4 *)(x))[0] == MV_UINT4) +# define IS_MV_REAL8(x) (((const UINT4 *)(x))[0] == MV_UINT4) +# else +# error BYTE ORDER NOT SPECIFIED (CPU_LITTLE_ENDIAN or CPU_BIG_ENDIAN) +# endif +#endif + + +/* some special values + */ +#define LDD_PIT 5 +#define DIR_NODIRECTION -1 + +/* some special macro's + * x is a pointer + */ +#define SET_MV_UINT1(x) ( (*((UINT1 *)(x))) = MV_UINT1) +#define SET_MV_UINT2(x) ( (*((UINT2 *)(x))) = MV_UINT2) +#define SET_MV_UINT4(x) ( (*((UINT4 *)(x))) = MV_UINT4) +#define SET_MV_INT1(x) ( (*(( INT1 *)(x))) = MV_INT1) +#define SET_MV_INT2(x) ( (*(( INT2 *)(x))) = MV_INT2) +#define SET_MV_INT4(x) ( (*(( INT4 *)(x))) = MV_INT4) +#define SET_MV_REAL4(x) ((*(CSF_IN_GLOBAL_NS UINT4_ALIASING *)(x)) = MV_UINT4) +#define SET_MV_REAL8(x) SET_MV_REAL4((x)),SET_MV_REAL4((((CSF_IN_GLOBAL_NS UINT4 *)(x))+1)) + +/* copy of floats by typecasting to + * an integer since MV_REAL? is a NAN + */ +#define COPY_REAL4(dest,src) ( (*(UINT4_ALIASING *)(dest)) = (*(const UINT4_ALIASING *)(src)) ) +#define COPY_REAL8(dest,src) COPY_REAL4((dest),(src)),\ + COPY_REAL4( (((UINT4 *)(dest))+1),(((const UINT4 *)(src))+1) ) + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/delattr.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/delattr.c new file mode 100644 index 000000000..840262c30 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/delattr.c @@ -0,0 +1,42 @@ +#include "csf.h" +#include "csfimpl.h" + +/* delete attribute from map + * MdelAttribute deletes an attribute + * from a map, if the attribute is available. + * returns + * the id argument if the attribute is succesfully deleted, + * or 0 in case of error or if the attribute is not found. + * + * Merrno + * NOACCESS + * WRITE_ERROR + */ +CSF_ATTR_ID MdelAttribute( + MAP *m, /* map handle */ + CSF_ATTR_ID id) /* identification of attribute */ +{ + ATTR_CNTRL_BLOCK b; + CSF_FADDR32 pos; + + if (! WRITE_ENABLE(m)) + { + M_ERROR(NOACCESS); + goto error; + } + + pos = CsfGetAttrBlock(m, id, &b); + if (pos == 0) + goto error; + + b.attrs[CsfGetAttrIndex(id, &b)].attrId = ATTR_NOT_USED; + if (CsfWriteAttrBlock(m, pos, &b)) + { + M_ERROR(WRITE_ERROR); + goto error; + } + + return id ; + +error: return 0 ; /* not found or an error */ +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/dumconv.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/dumconv.c new file mode 100644 index 000000000..92abf38e2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/dumconv.c @@ -0,0 +1,37 @@ +#include "csf.h" +#include "csfimpl.h" + +/* global header (opt.) and dumconv's prototypes "" */ + +/* headers of this app. modules called */ + +/***************/ +/* EXTERNALS */ +/***************/ + +/**********************/ +/* LOCAL DECLARATIONS */ +/**********************/ + +/*********************/ +/* LOCAL DEFINITIONS */ +/*********************/ + +/******************/ +/* IMPLEMENTATION */ +/******************/ + +/* ARGSUSED */ + +/* dummy conversion (LIBRARY_INTERNAL) + * does nothing + */ +void CsfDummyConversion( + size_t nrCells, + void *buf) +{ + /* nothing */ + /* Shut up the C compiler */ + (void)nrCells; + (void)buf; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/endian.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/endian.c new file mode 100644 index 000000000..1bcd1895c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/endian.c @@ -0,0 +1,20 @@ +#include "csf.h" +#include "csfimpl.h" + +/* test if map is in native endian mode + * test if map is in native endian mode + * returns nonzero if native, 0 if not native + */ +int MnativeEndian(const MAP *map) +{ + return map->main.byteOrder == ORD_OK; +} + +/* test if the Rput functions alter their cell buffers + * test if the Rput functions alter their cell buffers + * returns nonzero if they do not alter, 0 if they alter their buffers + */ +int RputDoNotChangeValues(const MAP *map) +{ + return MnativeEndian(map) && map->appCR == map->raster.cellRepr; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/filename.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/filename.c new file mode 100644 index 000000000..185e654cc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/filename.c @@ -0,0 +1,11 @@ +#include "csf.h" +#include "csfimpl.h" + +/* file name associated with map + * returns the file name associated with map + */ +const char *MgetFileName( + const MAP *m) /* map handle */ +{ + return(m->fileName); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gattrblk.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gattrblk.c new file mode 100644 index 000000000..a6604aea2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gattrblk.c @@ -0,0 +1,53 @@ +#include "csf.h" +#include "csfimpl.h" + +/* get the attribute control block (LIBRARY_INTERNAL) + * GetAttrBlock searches for the attribute control block + * that keeps the information for the given id. + * returns + * 0 if attribute is not found, + * or if found, the file position of the attribute + * control block. + */ +CSF_FADDR32 CsfGetAttrBlock( + MAP *m, /* map handle */ + CSF_ATTR_ID id, /* identification of the attribute */ + ATTR_CNTRL_BLOCK *b) /* write-only, attribute control block containing + * the id information. + */ +{ + CSF_FADDR32 next; + + next = m->main.attrTable; + while (next != 0 ) + { + CsfReadAttrBlock(m, next, b); + if (CsfGetAttrIndex(id, b) != NR_ATTR_IN_BLOCK) + break; + next = b->next; + } + return(next); +} + +/* get the attribute position and size (LIBRARY_INTERNAL) + * CsfGetAttrPosSize searches the attribute control block list + * that keeps the information for the given id. + * returns + * 0 if attribute is not found, + * or if found, the file position of the attribute. + */ +CSF_FADDR32 CsfGetAttrPosSize( + MAP *m, /* map handle */ + CSF_ATTR_ID id, /* identification of the attribute */ + size_t *size) /* write-only the size of the attribute */ +{ + ATTR_CNTRL_BLOCK b; + int i; + + if (CsfGetAttrBlock(m,id, &b) == 0) + return 0; + + i = CsfGetAttrIndex(id, &b); + *size = b.attrs[i].attrSize; + return b.attrs[i].attrOffset; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gattridx.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gattridx.c new file mode 100644 index 000000000..7ac67f70e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gattridx.c @@ -0,0 +1,20 @@ +#include "csf.h" +#include "csfimpl.h" + +/* search attribute index in block (LIBRARY_INTERNAL) + * returns index in block where id is found, NR_ATTR_IN_BLOCK if not found + */ +int CsfGetAttrIndex( + CSF_ATTR_ID id, /* id to be found */ + const ATTR_CNTRL_BLOCK *b) /* block to inspect */ +{ + int i = 0; + + while(i < NR_ATTR_IN_BLOCK) + { + if (b->attrs[i].attrId == id) + break; + i++; + } + return(i); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gcellrep.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gcellrep.c new file mode 100644 index 000000000..b9307d6b9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gcellrep.c @@ -0,0 +1,23 @@ +#include "csf.h" +#include "csfimpl.h" + +/* get cell representation + * RgetCellRepr returns the in-file cell representation. + * returns the cell representation + */ +CSF_CR RgetCellRepr( + const MAP *map) /* map handle */ +{ + return(map->raster.cellRepr); +} + +/* get cell representation as set by RuseAs + * RgetUseCellRepr returns the cell representation as set by RuseAs + * returns the cell representation as set by RuseAs + */ + +CSF_CR RgetUseCellRepr( + const MAP *map) /* map handle */ +{ + return(map->appCR); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gdattype.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gdattype.c new file mode 100644 index 000000000..a3b2e89aa --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gdattype.c @@ -0,0 +1,14 @@ +#include "csf.h" +#include "csfimpl.h" + +/* data type of the map + * MgetDataType returns the type of the map, which is always + * a raster until now. + * returns + * T_RASTER + */ +UINT4 MgetMapDataType( + const MAP *map) /* map handle */ +{ + return (UINT4)(map->main.mapType); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/getattr.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/getattr.c new file mode 100644 index 000000000..fd6246f71 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/getattr.c @@ -0,0 +1,47 @@ +#include "csf.h" +#include "csfimpl.h" + +/* read an attribute (LIBRARY_INTERNAL) + * MgetAttribute reads an attribute if it is available. + * Be aware that you can't pass a simple pointer to some + * (array of) structure(s) due to allignment en endian problems. + * At some time there will be a seperate get function for each attribute + * returns 0 if the attribute is not found, arg id if + * the attribute is found. + */ +CSF_ATTR_ID CsfGetAttribute( + MAP *m, /* map handle */ + CSF_ATTR_ID id, /* id of attribute to be read */ + size_t elSize, /* size of each data-element */ + size_t *nmemb, /* write-only. how many elSize mebers are read */ + void *attr) /* write-only. buffer where attribute is read in. + * Must be big enough to hold buffer. + */ +{ + ATTR_CNTRL_BLOCK b; + CSF_FADDR pos; + PRECOND(CsfValidSize(elSize)); + CHECKHANDLE_GOTO(m, error); + + if (! READ_ENABLE(m)) + { + M_ERROR(NOACCESS); + goto error; + } + + if (CsfGetAttrBlock(m, id, &b) != 0) + { + int i = CsfGetAttrIndex(id, &b); + *nmemb = b.attrs[i].attrSize; + POSTCOND( ((*nmemb) % elSize) == 0); + *nmemb /= elSize; + POSTCOND( (*nmemb) > 0); + pos = b.attrs[i].attrOffset; + (void)fseek(m->fp, (long)pos, SEEK_SET); + m->read(attr,elSize, (size_t)(*nmemb),m->fp); + return(id); + } + else + *nmemb = 0; +error: return(0); /* not available or an error */ +} /* MgetAttribute */ diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/getx0.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/getx0.c new file mode 100644 index 000000000..b488f75a4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/getx0.c @@ -0,0 +1,12 @@ +#include "csf.h" +#include "csfimpl.h" + +/* x value, upper left co-ordinate of map + * returns the x value, upper left co-ordinate of map + */ +REAL8 RgetXUL( + const MAP *map) /* map handle */ +{ + CHECKHANDLE(map); + return(map->raster.xUL); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gety0.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gety0.c new file mode 100644 index 000000000..0d6994f5d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gety0.c @@ -0,0 +1,15 @@ +#include "csf.h" +#include "csfimpl.h" + +/* y value, upper left co-ordinate of map + * RgetYUL returns the y value of the upper left co-ordinate of map. + * Whether this is the largest or smallest y value depends on the + * projection (See MgetProjection()). + * returns the y value, upper left co-ordinate of map + */ +REAL8 RgetYUL( + const MAP *map) /* map handle */ +{ + CHECKHANDLE(map); + return(map->raster.yUL); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/ggisfid.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/ggisfid.c new file mode 100644 index 000000000..f2fef42c5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/ggisfid.c @@ -0,0 +1,13 @@ +#include "csf.h" +#include "csfimpl.h" + +/* gis file id + * returns + * the "gis file id" field + */ +UINT4 MgetGisFileId( + const MAP *map) /* map handle */ +{ + CHECKHANDLE(map); + return(map->main.gisFileId); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gmaxval.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gmaxval.c new file mode 100644 index 000000000..481494833 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gmaxval.c @@ -0,0 +1,37 @@ +#include "csf.h" +#include "csfimpl.h" + +/* get maximum cell value + * RgetMaxVal returns the value stored in + * the header as the maximum value. + * If the minMaxStatus is MM_WRONGVALUE + * then a missing value is returned. + * returns 0 if argument maxVal is returned with a missing + * value, nonzero if not. + * + * example + * .so examples/csfstat.tr + */ +int RgetMaxVal( + const MAP *map, /* map handle */ + void *maxVal) /* write-only. Maximum value or missing value */ +{ + /* use buffer that can hold largest + * cell representation + */ + CSF_VAR_TYPE buf_1; + void *buf = (void *)(&buf_1); + + CHECKHANDLE(map); + CsfGetVarType(buf, &(map->raster.maxVal), RgetCellRepr(map)); + + map->file2app((size_t)1, buf); + + if (map->minMaxStatus == MM_WRONGVALUE) + SetMV(map, buf); + + CsfGetVarType(maxVal, buf, map->appCR); + + return((!IsMV(map,maxVal)) && + map->minMaxStatus!=MM_WRONGVALUE); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gminval.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gminval.c new file mode 100644 index 000000000..b7d624fb7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gminval.c @@ -0,0 +1,38 @@ +#include "csf.h" +#include "csfimpl.h" + +/* get minimum cell value + * RgetMinVal returns the value stored in + * the header as the minimum value. + * If the minMaxStatus is MM_WRONGVALUE + * then a missing value is returned. + * returns 0 if argument minVal is returned with a missing + * value, nonzero if not. + * + * example + * .so examples/csfstat.tr + */ + +int RgetMinVal( + const MAP *map, /* map handle */ + void *minVal) /* write-only. Minimum value or missing value */ +{ + /* use buffer that can hold largest + * cell representation + */ + CSF_VAR_TYPE buf_1; + void *buf = (void *)(&buf_1); + + CHECKHANDLE(map); + CsfGetVarType(buf, &(map->raster.minVal), RgetCellRepr(map)); + + map->file2app((size_t)1, buf); + + if (map->minMaxStatus == MM_WRONGVALUE) + SetMV(map, buf); + + CsfGetVarType(minVal, buf, map->appCR); + + return((!IsMV(map,minVal)) && + map->minMaxStatus!=MM_WRONGVALUE); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gnrcols.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gnrcols.c new file mode 100644 index 000000000..de13dfcb5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gnrcols.c @@ -0,0 +1,15 @@ +#include "csf.h" +#include "csfimpl.h" + +/* number of columns in a map + * RgetNrCols returns the number of columns in a map + * returns the number of columns in a map + * + * example + * .so examples/csfdump1.tr + */ +size_t RgetNrCols( + const MAP *map) /* map handle */ +{ + return(map->raster.nrCols); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gnrrows.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gnrrows.c new file mode 100644 index 000000000..91db0df5b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gnrrows.c @@ -0,0 +1,15 @@ +#include "csf.h" +#include "csfimpl.h" + +/* number of rows in a map + * RgetNrCols returns the number of rows in a map + * returns the number of rows in a map + * + * example + * .so examples/csfdump1.tr + */ +size_t RgetNrRows( + const MAP *map) /* map handle */ +{ + return(map->raster.nrRows); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gproj.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gproj.c new file mode 100644 index 000000000..af364b5e5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gproj.c @@ -0,0 +1,16 @@ +#include "csf.h" +#include "csfimpl.h" + +/* get projection type + * MgetProjection returns the projection type of the map. + * In version 2, projections are simplified. We only discern between + * a projection with y increasing (PT_YINCT2B) and decreasing (PT_YDECT2B) + * from top to bottom. + * The old constants are mapped to these two. + * returns PT_YINCT2B or PT_YDECT2B + */ +CSF_PT MgetProjection( + const MAP *map) /* map handle */ +{ + return (map->main.projection) ? PT_YDECT2B : PT_YINCT2B; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gputproj.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gputproj.c new file mode 100644 index 000000000..48f997f8e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gputproj.c @@ -0,0 +1,32 @@ +#include "csf.h" +#include "csfimpl.h" + +/* change projection type of map + * MputProjection type changes the projection type of a map. + * In version 2, projections are simplified. We only discern between + * a projection with y increasing (PT_YINCT2B=0) and decreasing (PT_YDECT2B=1) + * from top to bottom. + * All old constants that denote a projection with y decreasing are nonzero. + * And the old constant that denote a projection with y decreasing (PT_XY) is 0. + * returns the new projection (PT_YINCT2B or PT_YDECT2B) or MV_UINT2 if an + * error occurred. + * + * Merrno + * NOACCESS + */ +CSF_PT MputProjection( + MAP *map, /* map handle */ + CSF_PT p) /* projection type, all nonzero values are mapped to + * 1 (PT_YDECT2B) + */ +{ + CHECKHANDLE_GOTO(map, error); + if(! WRITE_ENABLE(map)) + { + M_ERROR(NOACCESS); + goto error; + } + map->main.projection = (p) ? PT_YDECT2B : PT_YINCT2B; + return map->main.projection; +error: return(MV_UINT2); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gvalscal.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gvalscal.c new file mode 100644 index 000000000..c6b1ebadc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gvalscal.c @@ -0,0 +1,13 @@ +#include "csf.h" +#include "csfimpl.h" + +/* get value scale of map + * returns the value scale of a map which is one of the + * constants prefixed by "VS_". + * + */ +CSF_VS RgetValueScale( + const MAP *map) /* map handle */ +{ + return(map->raster.valueScale); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gvartype.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gvartype.c new file mode 100644 index 000000000..8ec0dc638 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gvartype.c @@ -0,0 +1,20 @@ +#include "csf.h" +#include "csfimpl.h" + +/* perform a simple byte-copy of 8,4,2 or 1 byte (LIBRARY_INTERNAL) + */ +void CsfGetVarType( void *dest, const CSF_VAR_TYPE *src, CSF_CR cellRepr) +{ + switch (LOG_CELLSIZE(cellRepr)) /* 2log size */ + { + case 3 : ((UINT4 *)dest)[1] = ((const UINT4 *)src)[1]; + ((UINT4 *)dest)[0] = ((const UINT4 *)src)[0]; + break; + case 2 : (*(UINT4 *)dest) = (*(const UINT4 *)src); + break; + case 1 : (*(UINT2 *)dest) = (*(const UINT2 *)src); + break; + default: POSTCOND(LOG_CELLSIZE(cellRepr) == 0); + (*(UINT1 *)dest) = (*(const UINT1 *)src); + } +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/gversion.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gversion.c new file mode 100644 index 000000000..4221811df --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/gversion.c @@ -0,0 +1,12 @@ +#include "csf.h" +#include "csfimpl.h" + +/* get CSF version + * returns CSF version + */ +UINT4 MgetVersion( + const MAP *map) /* map handle */ +{ + CHECKHANDLE(map); + return (UINT4) (map->main.version); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/ismv.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/ismv.c new file mode 100644 index 000000000..babe55a74 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/ismv.c @@ -0,0 +1,48 @@ +#include "csf.h" +#include "csfimpl.h" + +/* test if a value is missing value + * returns 0 if not, nonzero if it is a missing value + */ +int IsMV( + const MAP *map, /* map handle */ + const void *cellValue) /* value to be tested */ +{ + return(IsMVcellRepr(map->appCR, cellValue)); +} + +/* test if a value is missing value + * returns 0 if not, nonzero if it is a missing value + */ +int IsMVcellRepr( + CSF_CR cellRepr, /* cell representation of argument cellValue. + * That is one of the constants prefixed by CR_. + */ + const void *cellValue) /* value to be tested */ +{ + + if (IS_SIGNED(cellRepr)) + switch ( (cellRepr & CSF_SIZE_MV_MASK ) >> CSF_POS_SIZE_MV_MASK) + { + case 0: return(*((const INT1 *)cellValue) == MV_INT1); + case 1: return(*((const INT2 *)cellValue) == MV_INT2); + default:return(*((const INT4 *)cellValue) == MV_INT4); + } + else + if (IS_REAL(cellRepr)) + { + if (cellRepr == CR_REAL4) + return(IS_MV_REAL4(cellValue)); + else + return(IS_MV_REAL8(cellValue)); + } + else + { + switch ( (cellRepr & CSF_SIZE_MV_MASK ) >> CSF_POS_SIZE_MV_MASK) + { + case 0: return(*((const UINT1 *)cellValue) == MV_UINT1); + case 1: return(*((const UINT2 *)cellValue) == MV_UINT2); + default: return(*((const UINT4 *)cellValue) == MV_UINT4); + } + } +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/kernlcsf.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/kernlcsf.c new file mode 100644 index 000000000..3d3fe8ec4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/kernlcsf.c @@ -0,0 +1,132 @@ +#include "csf.h" +#include "csfimpl.h" + +#include + +/***************/ +/* EXTERNALS */ +/***************/ + +static MAP **mapList = NULL; +static size_t mapListLen = 4; + +/*********************/ +/* LOCAL DEFINITIONS */ +/*********************/ + +/**********************/ +/* LOCAL DECLARATIONS */ +/**********************/ + +/******************/ +/* IMPLEMENTATION */ +/******************/ + + +/* close all open maps at exit (LIBRARY_INTERNAL) + * passed through atexit to c-library + * exit code + */ +static void CsfCloseCsfKernel(void) +{ + size_t i; + + for(i = 0; i < mapListLen; i++) + if(mapList[i] != NULL) + if(Mclose(mapList[i])) + (void)fprintf(stderr,"CSF_INTERNAL_ERROR: unable to close %s at exit\n", + mapList[i]->fileName); + CSF_FREE(mapList); + mapList = NULL; +} + +/* boot the CSF runtime library (LIBRARY_INTERNAL) + * CsfBootCsfKernel creates the mapList and adds the function to + * close all files at a system exit + * + * NOTE + * Note that CsfBootCsfKernel never returns if there isn't enough + * memory to allocate an array of mapListLen pointers, or if + * the atexit() call fails + */ +void CsfBootCsfKernel(void) +{ + POSTCOND(mapList == NULL); + + mapList = (MAP **)calloc(mapListLen,sizeof(MAP *)); + if (mapList == NULL) { + (void)fprintf(stderr,"CSF_INTERNAL_ERROR: Not enough memory to use CSF-files\n"); + exit(1); + } + + if (atexit(CsfCloseCsfKernel)) { + (void)fprintf(stderr,"CSF_INTERNAL_ERROR: Impossible to close CSF-files automatically at exit\n"); + exit(1); + } +} + +/* check if the kernel is booted (LIBRARY_INTERNAL) + * returns 0 if not, nonzero if already booted + */ +int CsfIsBootedCsfKernel(void) +{ + return(mapList != NULL); +} + +/* put map in run time structure (LIBRARY_INTERNAL) + * Every map opened or created is + * registered in a list for verification + * if functions get a valid map handle + * passed and for automatic closing + * at exit if the application forgets it. + */ +void CsfRegisterMap( + MAP *m) /* map handle to be registered, the field m->mapListId is + * initialized + */ +{ + size_t i=0; + + while (mapList[i] != NULL && i < mapListLen) + i++; + + if(i == mapListLen) + { + size_t j; + /* double size */ + mapListLen *=2; + mapList=realloc(mapList,sizeof(MAP *)*mapListLen); + if (mapList == NULL) { + (void)fprintf(stderr,"CSF_INTERNAL_ERROR: Not enough memory to use CSF-files\n"); + exit(1); + } + /* initialize new part, i at begin */ + for(j=i; j < mapListLen; ++j) + mapList[j]=0; + } + + mapList[i] = m; + m->mapListId = i; +} + +/* remove map from run time structure (LIBRARY_INTERNAL) + * The map handle will become invalid. + */ +void CsfUnloadMap( + MAP *m) /* map handle */ +{ + POSTCOND(CsfIsValidMap(m)); + + mapList[m->mapListId] = NULL; + m->mapListId = -1; +} + +/* check if the map handle is created via the csf kernel (LIBRARY_INTERNAL) + */ +int CsfIsValidMap( + const MAP *m) /* map handle */ +{ + return(CsfIsBootedCsfKernel() && m != NULL + && m->mapListId >= 0 && ((size_t)m->mapListId) < mapListLen + && mapList[m->mapListId] == m); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/legend.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/legend.c new file mode 100644 index 000000000..3f3e336b9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/legend.c @@ -0,0 +1,130 @@ +#include "csf.h" +#include "csfimpl.h" + +/* get the number of entries with a negative + * number for a type 1 legend + */ +static int NrLegendEntries(MAP *m) +{ + int size = CsfAttributeSize(m, ATTR_ID_LEGEND_V2); + if (size == 0) + { + if ( (size = -(int)CsfAttributeSize(m, ATTR_ID_LEGEND_V1)) != 0 ) + size -= CSF_LEGEND_ENTRY_SIZE; + } + return size/CSF_LEGEND_ENTRY_SIZE; +} +static int CmpEntries( + CSF_LEGEND *e1, + CSF_LEGEND *e2) +{ + return (int) ((e1->nr) - (e2->nr)); +} + +static void SortEntries( + CSF_LEGEND *l, /* version 2 legend */ + size_t nr) /* nr entries + name */ +{ +#ifndef USE_IN_PCR + typedef int (*QSORT_CMP)(const void *e1, const void *e2); +#endif + PRECOND(nr >= 1); + qsort(l+1, (size_t)nr-1, sizeof(CSF_LEGEND), (QSORT_CMP)CmpEntries); +} + +/* get the number of legend entries + * MgetNrLegendEntries tries to find a version 2 or version 1 + * legend. The return number can be used to allocate the appropriate + * array for legend. + * returns the number of entries in a legend plus 1 (for the name of the legend) + * or 0 if there is no legend + */ +size_t MgetNrLegendEntries( + MAP *m) /* the map pointer */ +{ + return (size_t)ABS(NrLegendEntries(m)); +} + +/* read a legend + * MgetLegend reads a version 2 and 1 legend. + * Version 1 legend are converted to version 2: the first + * array entry holds an empty string in the description field. + * returns + * 0 if no legend is available or in case of an error, + * nonzero otherwise + */ +int MgetLegend( + MAP *m, /* Map handle */ + CSF_LEGEND *l) /* array large enough to hold name and all entries, + * the entries are sorted + * struct CSF_LEGEND is typedef'ed in csfattr.h + */ +{ + CSF_ATTR_ID id = NrLegendEntries(m) < 0 ? ATTR_ID_LEGEND_V1 : ATTR_ID_LEGEND_V2; + size_t size; + CSF_FADDR pos = CsfGetAttrPosSize(m, id, &size); + size_t i,nr,start = 0; + if (pos == 0) + return 0; + fseek(m->fp, (long)pos, SEEK_SET); + if (id == ATTR_ID_LEGEND_V1) + { + /* empty name */ + l[0].nr = 0; + l[0].descr[0] = '\0'; + start = 1; /* don't read in name */ + } + nr = size/CSF_LEGEND_ENTRY_SIZE; + for(i = start; i < nr+start; i++) + { + m->read(&(l[i].nr), sizeof(INT4), (size_t)1, m->fp); + m->read(l[i].descr, sizeof(char), (size_t)CSF_LEGEND_DESCR_SIZE, m->fp); + } + SortEntries(l, nr+start); + return 1; +} + +/* write a legend + * MputLegend writes a (version 2) legend to a map replacing + * the old one if existent. + * See csfattr.h for the legend structure. + * + * returns + * 0 in case of an error, + * nonzero otherwise + * + * Merrno + * NOACCESS + * WRITE_ERROR + */ +int MputLegend( + MAP *m, /* Map handle */ + CSF_LEGEND *l, /* read-write, array with name and entries, the entries + * are sorted before writing to the file. + * Strings are padded with zeros. + */ + size_t nrEntries) /* number of array elements. That is name plus real legend entries */ +{ + int i = NrLegendEntries(m); + CSF_ATTR_ID id = i < 0 ? ATTR_ID_LEGEND_V1 : ATTR_ID_LEGEND_V2; + if (i) + if (! MdelAttribute(m, id)) + return 0; + SortEntries(l, nrEntries); + if (CsfSeekAttrSpace(m, ATTR_ID_LEGEND_V2, (size_t)(nrEntries*CSF_LEGEND_ENTRY_SIZE)) == 0) + return 0; + for(i = 0; i < (int)nrEntries; i++) + { + if( + m->write(&(l[i].nr), sizeof(INT4), (size_t)1, m->fp) != 1 || + m->write( + CsfStringPad(l[i].descr,(size_t)CSF_LEGEND_DESCR_SIZE), + sizeof(char), (size_t)CSF_LEGEND_DESCR_SIZE, m->fp) + != CSF_LEGEND_DESCR_SIZE ) + { + M_ERROR(WRITE_ERROR); + return 0; + } + } + return 1; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/makefile.vc b/bazaar/plugin/gdal/frmts/pcraster/libcsf/makefile.vc new file mode 100644 index 000000000..0656d5530 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/makefile.vc @@ -0,0 +1,29 @@ +GDAL_ROOT = ..\..\.. +EXTRAFLAGS=-DUSE_IN_GDAL $(SOFTWARNFLAGS) +!INCLUDE $(GDAL_ROOT)\nmake.opt + + +OBJ = endian.obj swapio.obj vs2.obj vsdef.obj rextend.obj\ +vsvers.obj legend.obj strpad.obj strconst.obj vsis.obj ruseas.obj rmalloc.obj create2.obj \ +_getcell.obj _getrow.obj _gsomece.obj _putcell.obj _rputrow.obj\ +attravai.obj attrsize.obj csfglob.obj csfsup.obj delattr.obj\ +filename.obj gattrblk.obj gattridx.obj gcellrep.obj \ + cellsize.obj gdattype.obj getattr.obj getx0.obj\ +gety0.obj ggisfid.obj gmaxval.obj gminval.obj gnrcols.obj gnrrows.obj\ +gproj.obj gputproj.obj gvalscal.obj gvartype.obj gversion.obj ismv.obj\ +kernlcsf.obj mclose.obj mopen.obj mperror.obj \ +pgisfid.obj pmaxval.obj pminval.obj putallmv.obj putattr.obj\ +putsomec.obj putx0.obj puty0.obj pvalscal.obj rattrblk.obj rcomp.obj\ +rcoords.obj rdup2.obj reseterr.obj \ +rrowcol.obj setmv.obj setvtmv.obj moreattr.obj\ +wattrblk.obj setangle.obj angle.obj dumconv.obj trackmm.obj + +GDAL_ROOT = ..\..\.. + + +default: $(OBJ) + xcopy /D /Y *.obj ..\..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/mclose.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/mclose.c new file mode 100644 index 000000000..e1dcef172 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/mclose.c @@ -0,0 +1,89 @@ +#include "csf.h" +#include "csfimpl.h" + +#include /* memset() */ + +/* close a map + * the Mclose function closes a map + * if the map is being used for output + * all header data is rewritten first + * returns Upon succesful completion 0 is returned. + * Otherwise, a non-zero value is returned + * + * Merrno + * WRITE_ERROR (map descriptor still in tact if this happens) + */ +int Mclose( + MAP *m) /* map to close, map descriptor + * is deallocated + */ +{ + CHECKHANDLE_GOTO(m, error); + + if (m->minMaxStatus == MM_WRONGVALUE) + { + CsfSetVarTypeMV( &(m->raster.minVal), m->raster.cellRepr); + CsfSetVarTypeMV( &(m->raster.maxVal), m->raster.cellRepr); + } + + /* if write permission , write all header data to file */ + if (WRITE_ENABLE(m)) + { + char filler[MAX_HEADER_FILL_SIZE]; + (void)memset(filler, 0x0, (size_t)MAX_HEADER_FILL_SIZE); + + if (m->main.byteOrder != ORD_OK) { + CsfSwap((void*)&(m->raster.minVal), CELLSIZE(m->raster.cellRepr),(size_t)1); + CsfSwap((void*)&(m->raster.maxVal), CELLSIZE(m->raster.cellRepr),(size_t)1); + } + + fseek(m->fp,(long)ADDR_MAIN_HEADER,SEEK_SET); + if(m->write((void*)&(m->main.signature),sizeof(char), CSF_SIG_SPACE,m->fp) + != CSF_SIG_SPACE || + m->write((void*)&(m->main.version),sizeof(UINT2),(size_t)1,m->fp)!=1 || + m->write((void*)&(m->main.gisFileId),sizeof(UINT4),(size_t)1,m->fp)!=1 || + m->write((void*)&(m->main.projection),sizeof(UINT2),(size_t)1,m->fp)!=1 || + m->write((void*)&(m->main.attrTable),sizeof(UINT4),(size_t)1,m->fp)!=1 || + m->write((void*)&(m->main.mapType),sizeof(UINT2),(size_t)1,m->fp)!=1 || + fwrite((void*)&(m->main.byteOrder),sizeof(UINT4),(size_t)1,m->fp)!=1 || + m->write((void*)filler, sizeof(char), MAIN_HEADER_FILL_SIZE ,m->fp) + != MAIN_HEADER_FILL_SIZE ) + { + M_ERROR(WRITE_ERROR); + goto error; + } + fseek(m->fp,ADDR_SECOND_HEADER, SEEK_SET); + + if ( m->write((void*)&(m->raster.valueScale),sizeof(UINT2),(size_t)1,m->fp) !=1 || + m->write((void*)&(m->raster.cellRepr), sizeof(UINT2),(size_t)1,m->fp) !=1 || + fwrite((void*)&(m->raster.minVal), sizeof(CSF_VAR_TYPE),(size_t)1,m->fp) !=1 || + fwrite((void*)&(m->raster.maxVal), sizeof(CSF_VAR_TYPE),(size_t)1,m->fp) !=1 || + m->write((void*)&(m->raster.xUL), sizeof(REAL8),(size_t)1,m->fp) !=1 || + m->write((void*)&(m->raster.yUL), sizeof(REAL8),(size_t)1,m->fp) !=1 || + m->write((void*)&(m->raster.nrRows), sizeof(UINT4),(size_t)1,m->fp) !=1 || + m->write((void*)&(m->raster.nrCols), sizeof(UINT4),(size_t)1,m->fp) !=1 || + m->write((void*)&(m->raster.cellSize), sizeof(REAL8),(size_t)1,m->fp) !=1 || + m->write((void*)&(m->raster.cellSizeDupl), sizeof(REAL8),(size_t)1,m->fp) !=1 || + m->write((void*)&(m->raster.angle), sizeof(REAL8),(size_t)1,m->fp) !=1 || + m->write((void*)filler, sizeof(char), RASTER_HEADER_FILL_SIZE ,m->fp) + != RASTER_HEADER_FILL_SIZE ) + { + M_ERROR(WRITE_ERROR); + goto error; + } + } + + (void)fclose(m->fp); + CsfUnloadMap(m); + + /* clear the space, to avoid typical errors such as + accessing the map after Mclose */ + (void)memset((void *)m->fileName, 0x0, strlen(m->fileName)); + CSF_FREE(m->fileName); + + (void)memset((void *)m, 0x0, sizeof(MAP)); + CSF_FREE(m); + + return(0); +error: return(1); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/mopen.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/mopen.c new file mode 100644 index 000000000..56af168bd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/mopen.c @@ -0,0 +1,204 @@ +#include + +#include "csf.h" +#include "csfimpl.h" + + +static const char *openModes[3] = { + S_READ, + S_WRITE, + S_READ_WRITE + }; + +/* Return the access mode of m + * MopenPerm returns the permission. + * Note that M_WRITE is deprecated + */ +enum MOPEN_PERM MopenPerm( + const MAP *m) +{ + return m->fileAccessMode; +} + +/* open an existing CSF file + * Mopen opens a CSF file. It allocates space for + * the MAP runtime-structure, reads the header file + * and performs test to determine if it is a CSF file. + * The MinMaxStatus is set to MM_KEEPTRACK if the min/max + * header fields are not MV or MM_WRONGVALUE if one of them + * contains a MV. + * returns a pointer the MAP runtime structure if the file is + * successfully opened as a CSF file, NULL if not. + * + * Merrno + * NOCORE BADACCESMODE OPENFAILED NOT_CSF BAD_VERSION + * + * EXAMPLE + * .so examples/testcsf.tr + */ +MAP *Mopen( + const char *fileName, /* file name */ + enum MOPEN_PERM mode) /* file permission */ +{ + MAP *m; + UINT4 s; /* swap detection field */ + + if (! CsfIsBootedCsfKernel()) + CsfBootCsfKernel(); + + m = (MAP *)CSF_MALLOC(sizeof(MAP)); + + if (m == NULL) + { + M_ERROR(NOCORE); + goto error_mapMalloc; + } + + m->fileName = (char *)CSF_MALLOC(strlen(fileName)+1); + if (m->fileName == NULL) + { + M_ERROR(NOCORE); + goto error_fnameMalloc; + } + (void)strcpy(m->fileName,fileName); + + /* check file mode validation */ + if ( IS_BAD_ACCESS_MODE(mode)) + { + M_ERROR(BADACCESMODE); + goto error_notOpen; + } + m->fileAccessMode = mode; + + + /* check if file can be opened or exists */ + m->fp = fopen(fileName, openModes[mode-1]); + if (m->fp == NULL) + { + M_ERROR(OPENFAILED); + goto error_notOpen; + } + + /* check if file could be C.S.F.-file + * (at least 256 bytes long) + * otherwise the signature comparison will + * fail + */ + + (void)fseek(m->fp,0L, SEEK_END); + if (ftell(m->fp) < (long)ADDR_DATA) + { + M_ERROR(NOT_CSF); + goto error_open; + } + + (void)fseek(m->fp, 14+CSF_SIG_SPACE, SEEK_SET); + if (1 != fread((void *)&s, sizeof(UINT4),(size_t)1,m->fp)) + { + fprintf(stderr, "WARNING: Unable to read ORD_OK in CSF.\n"); + } + if (s != ORD_OK) { + m->write = CsfWriteSwapped; + m->read = CsfReadSwapped; + } + else { +#ifdef DEBUG + m->read = (CSF_READ_FUNC)CsfReadPlain; + m->write = (CSF_READ_FUNC)CsfWritePlain; +#else + m->read = (CSF_READ_FUNC)fread; + m->write = (CSF_READ_FUNC)fwrite; +#endif + } + + (void)fseek(m->fp, ADDR_MAIN_HEADER, SEEK_SET); + m->read((void *)&(m->main.signature), sizeof(char), CSF_SIG_SPACE,m->fp); + m->read((void *)&(m->main.version), sizeof(UINT2),(size_t)1,m->fp); + m->read((void *)&(m->main.gisFileId), sizeof(UINT4),(size_t)1,m->fp); + m->read((void *)&(m->main.projection),sizeof(UINT2),(size_t)1,m->fp); + m->read((void *)&(m->main.attrTable), sizeof(UINT4),(size_t)1,m->fp); + m->read((void *)&(m->main.mapType), sizeof(UINT2),(size_t)1,m->fp); + m->read((void *)&(m->main.byteOrder), sizeof(UINT4),(size_t)1,m->fp); + /* 14+CSF_SIG_SPACE + */ + + (void)fseek(m->fp, ADDR_SECOND_HEADER, SEEK_SET); + m->read((void *)&(m->raster.valueScale), sizeof(UINT2),(size_t)1,m->fp); + m->read((void *)&(m->raster.cellRepr), sizeof(UINT2),(size_t)1,m->fp); + + if (1 != fread((void *)&(m->raster.minVal), sizeof(CSF_VAR_TYPE),(size_t)1,m->fp)) + { + fprintf(stderr, "WARNING: Unable to read min val in CSF.\n"); + } + if (1 != fread((void *)&(m->raster.maxVal), sizeof(CSF_VAR_TYPE),(size_t)1,m->fp)) + { + fprintf(stderr, "WARNING: Unable to read max val in CSF.\n"); + } + if (s != ORD_OK) { + CsfSwap((void *)&(m->raster.minVal), CELLSIZE(m->raster.cellRepr),(size_t)1); + CsfSwap((void *)&(m->raster.maxVal), CELLSIZE(m->raster.cellRepr),(size_t)1); + } + + m->read((void *)&(m->raster.xUL), sizeof(REAL8),(size_t)1,m->fp); + m->read((void *)&(m->raster.yUL), sizeof(REAL8),(size_t)1,m->fp); + m->read((void *)&(m->raster.nrRows), sizeof(UINT4),(size_t)1,m->fp); + m->read((void *)&(m->raster.nrCols), sizeof(UINT4),(size_t)1,m->fp); + m->read((void *)&(m->raster.cellSize), sizeof(REAL8),(size_t)1,m->fp); + m->read((void *)&(m->raster.cellSizeDupl), sizeof(REAL8),(size_t)1,m->fp); + + m->read((void *)&(m->raster.angle), sizeof(REAL8),(size_t)1,m->fp); + + + /* check signature C.S.F.file + */ + if(strncmp(m->main.signature,CSF_SIG,CSF_SIZE_SIG)!=0) + { + M_ERROR(NOT_CSF); + goto error_open; + } + /* should be read right + */ + POSTCOND(m->main.byteOrder == ORD_OK); + /* restore byteOrder C.S.F.file (Intel or Motorola) */ + m->main.byteOrder=s; + + /* check version C.S.F.file + */ + if (m->main.version != CSF_VERSION_1 + && (m->main.version != CSF_VERSION_2)) + { + M_ERROR(BAD_VERSION); + goto error_open; + } + + if (m->main.version == CSF_VERSION_1) + m->raster.angle = 0.0; + + CsfFinishMapInit(m); + + CsfRegisterMap(m); + + /* install cell value converters: (app2file,file2app) + */ + m->app2file = CsfDummyConversion; + m->file2app = CsfDummyConversion; + m->appCR = m->raster.cellRepr; + + if (IsMV(m,&(m->raster.minVal)) || + IsMV(m,&(m->raster.maxVal)) ) + m->minMaxStatus = MM_WRONGVALUE; + else + m->minMaxStatus = MM_KEEPTRACK; + + return(m); + + error_open: + PRECOND(m->fp != NULL); + (void)fclose(m->fp); + error_notOpen: + CSF_FREE(m->fileName); + error_fnameMalloc: + CSF_FREE(m); +error_mapMalloc: + return(NULL); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/moreattr.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/moreattr.c new file mode 100644 index 000000000..d83485820 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/moreattr.c @@ -0,0 +1,178 @@ +#include "csf.h" +#include "csfimpl.h" + +#include /* strlen */ + +/* get the size of the history attribute + * returns + * the size of history buffer INCLUDING the termimating `\0`, + * or 0 if not available or in case of error + */ +size_t MgetHistorySize(MAP *m) /* the map to get it from */ +{ + return (size_t)CsfAttributeSize(m, ATTR_ID_HISTORY); +} + +/* get the size of the description attribute + * returns + * the size of description buffer INCLUDING the termimating `\0`, + * or 0 if not available or in case of error + */ +size_t MgetDescriptionSize(MAP *m) /* the map to get it from */ +{ + return (size_t)CsfAttributeSize(m, ATTR_ID_DESCRIPTION); +} + +/* get the number of colour palette entries + * MgetNrColourPaletteEntries returns the number of rgb tupels + * of the colour palette. Each tupel is a sequence of 3 UINT2 + * words describing red, green and blue. + * returns + * the number of rgb tupels, + * or 0 if not available or in case of error + */ +size_t MgetNrColourPaletteEntries(MAP *m) /* the map to get it from */ +{ + size_t s = (size_t)CsfAttributeSize(m, ATTR_ID_COLOUR_PAL); + POSTCOND( (s % (3*sizeof(UINT2))) == 0); + return s / (3*sizeof(UINT2)); +} + +/* get the number of grey palette entries + * MgetNrGreyPaletteEntries returns the number of grey tupels + * of the grey palette. Each tupel is one UINT2 + * words describing the intensity: low, 0 is black, high is white. + * returns + * the number of grey tupels, + * or 0 if not available or in case of error + */ +size_t MgetNrGreyPaletteEntries(MAP *m) /* the map to get it from */ +{ + size_t s = (size_t)CsfAttributeSize(m, ATTR_ID_GREY_PAL); + POSTCOND( (s % (sizeof(UINT2))) == 0); + return s / (sizeof(UINT2)); +} + +/* get the description attribute + * returns + * 0 if not available or in case of error, + * nonzero otherwise + */ +int MgetDescription(MAP *m, /* the map to get it from */ + char *des) /* the resulting description string */ +{ + size_t size; + return CsfGetAttribute(m, ATTR_ID_DESCRIPTION, sizeof(char), &size, des); +} + +/* get the history attribute + * returns + * 0 if not available or in case of error, + * nonzero otherwise + */ +int MgetHistory(MAP *m, /* the map to get it from */ + char *history) /* the resulting history string */ +{ + size_t size; + return CsfGetAttribute(m, ATTR_ID_HISTORY, sizeof(char), &size, history); +} + +/* get the colour palette + * MgetColourPalette fills the pal argument with a number of rgb tupels + * of the colour palette. Each tupel is a sequence of 3 UINT2 + * words describing red, green and blue. Thus if the map has 8 + * colour palette entries it puts 24 UINT2 values in pal. + * returns + * 0 if not available or in case of error, + * nonzero otherwise + */ +int MgetColourPalette(MAP *m, /* the map to get it from */ + UINT2 *pal) /* the resulting palette */ +{ + size_t size; + return CsfGetAttribute(m, ATTR_ID_COLOUR_PAL, sizeof(UINT2), &size, pal); +} + +/* get the grey palette + * MgetGreyPalette fills the pal argument with a number of grey tupels + * of the grey palette. Each tupel is one UINT2 + * words describing the intensity: low, 0 is black, high is white. + * returns + * 0 if not available or in case of error, + * nonzero otherwise + */ +int MgetGreyPalette(MAP *m, /* the map to get it from */ + UINT2 *pal) /* the resulting palette */ +{ + size_t size; + return CsfGetAttribute(m, ATTR_ID_GREY_PAL, sizeof(UINT2), &size, pal); +} + +/* put the description attribute + * MputDescription writes the description string to a map. + * An existing description is overwritten. + * returns + * 0 if not available or in case of error, + * nonzero otherwise + */ +int MputDescription(MAP *m, /* the map to get it from */ + char *des) /* the new description string. + * Is a C-string, `\0`-terminated + */ +{ + return CsfUpdateAttribute(m, ATTR_ID_DESCRIPTION, sizeof(char), + strlen(des)+1, des); +} + +/* put the history attribute + * MputHistory writes the history string to a map. + * An existing history is overwritten. + * returns + * 0 if not available or in case of error, + * nonzero otherwise + */ +int MputHistory(MAP *m, /* the map to get it from */ + char *history) /* the new history string + * Is a C-string, `\0`-terminated + */ +{ + return CsfUpdateAttribute(m, ATTR_ID_HISTORY, sizeof(char), + strlen(history)+1, history); +} + +/* put the colour palette + * MputColourPalette writes the pal argument that is filled + * with a number of rgb tupels + * of the colour palette to the map. Each tupel is a sequence of 3 UINT2 + * words describing red, green and blue. Thus if the map has 8 + * colour palette entries it puts 24 UINT2 values in map palette. + * An existing colour palette is overwritten. + * returns + * 0 if not available or in case of error, + * nonzero otherwise + */ +int MputColourPalette(MAP *m, /* the map to get it from */ + UINT2 *pal, /* the new palette */ + size_t nrTupels) /* the number of 3 UINT2 words tupels of pal */ +{ + return CsfUpdateAttribute(m, ATTR_ID_COLOUR_PAL, sizeof(UINT2), + nrTupels*3, pal); +} + +/* put the grey palette + * MputColourPalette writes the pal argument that is filled + * with a number of grey tupels + * of the grey palette. Each tupel is one UINT2 + * words describing the intensity: low, 0 is black, high is white. + * An existing grey palette is overwritten. + * returns + * 0 if not available or in case of error, + * nonzero otherwise + */ +int MputGreyPalette(MAP *m, /* the map to get it from */ + UINT2 *pal, /* the new grey palette */ + size_t nrTupels) /* the number of UINT2 words tupels of pal */ +{ + return CsfUpdateAttribute(m, ATTR_ID_GREY_PAL, sizeof(UINT2), + nrTupels, pal); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/mperror.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/mperror.c new file mode 100644 index 000000000..f625219d6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/mperror.c @@ -0,0 +1,81 @@ +#include "csf.h" +#include "csfimpl.h" + + +static const char *errolist[ERRORNO]={ +"No error", +"File could not be opened or does not exist", +"File is not a PCRaster file", +"Wrong C.S.F.-version", +"Wrong byte order", +"Not enough memory", +"Illegal cell representation constant", +"Acces denied", +"Row number to big", +"Collumn number to big", +"Map is not a raster file", +"Illegal conversion", +"No space on device to write", +"A write error occurred", +"Illegal handle", +"A read error occurred", +"Illegal access mode constant", +"Attribute not found", +"Attribute already in file", +"Cell size <= 0", +"Conflict between cell representation and value scale", +"Illegal value scale", +"XXXXXXXXXXXXXXXXXXXX", +"Angle < -0.5 pi or > 0.5 pi", +"Can't read as a boolean map", +"Can't write as a boolean map", +"Can't write as a ldd map", +"Can't use as a ldd map", +"Can't write to version 1 cell representation", +"Usetype is not version 2 cell representation, VS_LDD or VS_BOOLEAN" +}; + +/* write error message to stderr + * Mperror writes the error message belonging to the current Merrno + * value to stderr, prefixed by a userString, separated by a semicolon. + * + * example + * .so examples/csfdump1.tr + */ +void Mperror( + const char *userString) /* prefix string */ +{ + (void)fprintf(stderr,"%s : %s\n", userString, errolist[Merrno]); +} + +/* write error message to stderr and exits + * Mperror first writes the error message belonging to the current Merrno + * value to stderr, prefixed by userString, separated by a semicolon. + * Then Mperror exits by calling exit() with the given exit code. + * + * returns + * NEVER RETURNS! + * + * example + * .so examples/csfdump2.tr + */ +void MperrorExit( + const char *userString, /* prefix string */ + int exitCode) /* exit code */ +{ + Mperror(userString); + exit(exitCode); +} + +/* error message + * MstrError returns the error message belonging to the current Merrno + * value. + * returns the error message belonging to the current Merrno + * + * example + * .so examples/testcsf.tr + */ +const char *MstrError(void) +{ + return(errolist[Merrno]); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/pcrtypes.h b/bazaar/plugin/gdal/frmts/pcraster/libcsf/pcrtypes.h new file mode 100644 index 000000000..c1bf24491 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/pcrtypes.h @@ -0,0 +1,305 @@ +#ifndef INCLUDED_PCRTYPES +# define INCLUDED_PCRTYPES + +#ifndef INCLUDED_CSFTYPES +#include "csftypes.h" +#define INCLUDED_CSFTYPES +#endif + +#ifndef INCLUDED_STRING +#include +#define INCLUDED_STRING +#endif + +/* NOTE that this file is included in the PCRaster gdal driver + * it accounts for more compiler issues than we deal with + * normally in PCRaster, such as the VC6 compiler. + * 64 bit integer typedefs are only defined for the PCRaster + * source not for the Gdal driver. + * The Gdal PCRaster driver defines USE_IN_GDAL, PCRaster sources + * do not. + */ + +// memset +// use string.h not cstring +// VC6 does not have memset in std +// better use Ansi-C string.h to be safe +#ifndef INCLUDED_C_STRING +#include +#define INCLUDED_C_STRING +#endif + +#ifndef USE_IN_GDAL + // exclude Gdal driver, only for PCRaster sources + +# ifdef _MSC_VER + typedef __int64 PCR_INT8; + typedef unsigned __int64 PCR_UINT8; +# else + // assume gcc FTTB + typedef long long PCR_INT8; + typedef unsigned long long PCR_UINT8; +# endif +/* from gcc manual: + ISO C99 supports data types for integers that are at least 64 bits wide, + and as an extension GCC supports them in C89 mode and in C++. Simply + write long long int for a signed integer, or unsigned long long int for + an unsigned integer. To make an integer constant of type long long int, + add the suffix `LL' to the integer. To make an integer constant of type + unsigned long long int, add the suffix `ULL' to the integer. + + WARNING: macros below are not typesafe, use pcr::isMV and pcr::setMV instead + */ +#define MV_INT8 ((CSF_IN_GLOBAL_NS PCR_INT8)0x8000000000000000LL) +#define MV_UINT8 ((CSF_IN_GLOBAL_NS PCR_UINT8)0xFFFFFFFFFFFFFFFFULL) +#define IS_MV_INT8(x) ((*((const CSF_IN_GLOBAL_NS PCR_INT8 *)x)) == MV_INT8) +#define IS_MV_UINT8(x) ((*((const CSF_IN_GLOBAL_NS PCR_UINT8 *)x)) == MV_UINT8) +#define SET_MV_INT8(x) ( (*((PCR_INT8 *)(x))) = MV_INT8) +#define SET_MV_UINT8(x) ( (*((PCR_UINT8 *)(x))) = MV_UINT8) + +#endif + +namespace pcr { +/*! + \brief Tests if the value v is a missing value. + \param v the value to be tested. + \return True if value \a v is a missing value. + + the generic isMV(const T& v) is not implemented, only the specializations + + \todo Zet alle dingen met een bepaald type,isMV, setMv, isType in + een zgn. struct trait + zie cast drama als isMV mist voor INT2 in BandMapTest::Open2 + Zie numeric_limit discussie in Josuttis +*/ + template bool isMV(const T& v); +/*! + \brief Tests if the value pointed to by v is a missing value. + \param v Pointer to the value to be tested. + \return True if the value pointed to by v is a missing value. +*/ + template bool isMV(T* v) { + return isMV(*v); + } + +# define PCR_DEF_ISMV(type) \ + template<> \ + inline bool isMV(const type& v) \ + { return v == MV_##type; } + PCR_DEF_ISMV(UINT1) + PCR_DEF_ISMV(UINT2) + PCR_DEF_ISMV(UINT4) + PCR_DEF_ISMV(INT1) + PCR_DEF_ISMV(INT2) + PCR_DEF_ISMV(INT4) +# ifndef USE_IN_GDAL + template<> + inline bool isMV(const PCR_UINT8& v) + { return v == MV_UINT8; } + template<> + inline bool isMV(const PCR_INT8& v) + { return v == MV_INT8; } +# endif +# undef PCR_DEF_ISMV + template<> inline bool isMV(const REAL4& v) + { return IS_MV_REAL4(&v); } + template<> inline bool isMV(const REAL8& v) + { return IS_MV_REAL8(&v); } + +template<> inline bool isMV(std::string const& string) +{ + return string.empty(); +} + + /*! + \brief Sets the value v to a missing value. + \param v value to be set. + the generic setMV(T& v) is not implemented, only the specializations + */ + template void setMV(T& v); + /*! + \brief Sets the value pointed to by v to a missing value. + \param v Pointer to the value to be set. + */ + template void setMV(T *v) { + setMV(*v); + } + +# define PCR_DEF_SETMV(type) \ + template<> \ + inline void setMV(type& v) \ + { v = MV_##type; } + PCR_DEF_SETMV(UINT1) + PCR_DEF_SETMV(UINT2) + PCR_DEF_SETMV(UINT4) + PCR_DEF_SETMV(INT1) + PCR_DEF_SETMV(INT2) + PCR_DEF_SETMV(INT4) +# ifndef USE_IN_GDAL + template<> + inline void setMV(PCR_UINT8& v) + { v = MV_UINT8; } + template<> + inline void setMV(PCR_INT8& v) + { v = MV_INT8; } +# endif +# undef PCR_DEF_SETMV + + template<> + inline void setMV(REAL4& v) + { +# ifndef __i386__ + SET_MV_REAL4((&v)); +# else + // this fixes an optimization problem (release mode), if is v is a single + // element variable in function scope (stack-based) + // constraint the setting to memory (m) + // for correct alignment + asm ("movl $-1, %0" : "=m" (v)); +# endif + } + template<> + inline void setMV(REAL8& v) + { +# ifndef __i386__ + SET_MV_REAL8((&v)); +# else + memset(&v,MV_UINT1,sizeof(REAL8)); + // constraint the setting to memory (m) + // this fixes the same optimization problem, as for REAL4 + // see com_mvoptest.cc, does not work: ! + // int *v2= (int *)&v; + // asm ("movl $-1, %[dest]" : [dest] "=m" (v2[0])); + // asm ("movl $-1, %[dest]" : [dest] "=m" (v2[1])); +# endif + } + +template<> +inline void setMV(std::string& string) +{ + // VC6 does not have clear + // string.clear(); + string=""; +} + +/*! \brief set array \a v of size \a n to all MV's + * the generic setMV(T& v) is implemented, the specializations + * are optimizations + * \todo + * check if stdlib has a 'wordsized' memset + * or optimize for I86, for gcc look into include/asm/string + */ +template + void setMV(T *v, size_t n) +{ + for(size_t i=0; i + void setMVMemSet(T *v, size_t n) { + memset(v,MV_UINT1,n*sizeof(T)); + } + } + +# define PCR_DEF_SETMV_MEMSET(type) \ + template<> \ + inline void setMV(type* v,size_t n) \ + { detail::setMVMemSet(v,n); } + PCR_DEF_SETMV_MEMSET(UINT1) + PCR_DEF_SETMV_MEMSET(UINT2) + PCR_DEF_SETMV_MEMSET(UINT4) +# ifndef USE_IN_GDAL + PCR_DEF_SETMV_MEMSET(PCR_UINT8) +# endif + PCR_DEF_SETMV_MEMSET(REAL4) + PCR_DEF_SETMV_MEMSET(REAL8) +# undef PCR_DEF_SETMV_MEMSET + template<> + inline void setMV(INT1 *v, size_t n) { + memset(v,MV_INT1,n); + } + +//! replace a value equal to \a nonStdMV with the standard MV +/*! + * \todo + * the isMV test is only needed for floats, to protect NAN evaluation + * what once happened on bcc/win32. Should reevaluate that. + */ +template + struct AlterToStdMV { + T d_nonStdMV; + AlterToStdMV(T nonStdMV): + d_nonStdMV(nonStdMV) {}; + + void operator()(T& v) { + if (!isMV(v) && v == d_nonStdMV) + setMV(v); + } + }; + +//! return the value or the standard missing value if value equal to \a nonStdMV +/*! + * \todo + * the isMV test is only needed for floats, to protect NAN evaluation + * what once happened on bcc/win32. Should reevaluate that. + */ +template + struct ToStdMV { + T d_nonStdMV; + T d_mv; + ToStdMV(T nonStdMV): + d_nonStdMV(nonStdMV) { + setMV(d_mv); + } + + T operator()(T const& v) { + if (!isMV(v) && v == d_nonStdMV) { + return d_mv; + } + return v; + } + }; + +//! replace the standard MV with a value equal to \a otherMV +/*! + * \todo + * the isMV test is only needed for floats, to protect NAN evaluation + * what once happened on bcc/win32. Should reevaluate that. + */ +template + struct AlterFromStdMV { + T d_otherMV; + AlterFromStdMV(T otherMV): + d_otherMV(otherMV) {}; + + void operator()(T& v) { + if (isMV(v)) + v = d_otherMV; + } + }; + +//! return the value or \a otherMV if value equal to standard MV +/*! + * \todo + * the isMV test is only needed for floats, to protect NAN evaluation + * what once happened on bcc/win32. Should reevaluate that. + */ +template + struct FromStdMV { + T d_otherMV; + FromStdMV(T otherMV): + d_otherMV(otherMV) {}; + + T operator()(const T& v) { + if (isMV(v)) + return d_otherMV; + return v; + } + }; + +} + + +#endif diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/pgisfid.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/pgisfid.c new file mode 100644 index 000000000..98814b8f1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/pgisfid.c @@ -0,0 +1,26 @@ +#include "csf.h" +#include "csfimpl.h" + +/* put the gis file id + * returns + * the "gis file id" field or MV_UINT4 + * in case of an error + * + * Merrno + * NOACCESS + */ +UINT4 MputGisFileId( + MAP *map, /* map handle */ + UINT4 gisFileId) /* new gis file id */ +{ + + CHECKHANDLE_GOTO(map, error); + if(! WRITE_ENABLE(map)) + { + M_ERROR(NOACCESS); + goto error; + } + map->main.gisFileId = gisFileId; + return(gisFileId); +error: return(MV_UINT4); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/pmaxval.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/pmaxval.c new file mode 100644 index 000000000..ed5a2e506 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/pmaxval.c @@ -0,0 +1,38 @@ +#include "csf.h" +#include "csfimpl.h" + +/* set new maximum cell value + * RputMaxVal set a new value stored in + * the header as the maximum value. + * minMaxStatus is set to MM_DONTKEEPTRACK + * + * NOTE + * Note that the header maximum set must be equal or + * larger than the maximum value in the map. + * + * example + * .so examples/set_min.tr + */ +void RputMaxVal( + MAP *map, /* map handle */ + const void *maxVal) /* New maximum value */ +{ + /* use buffer that can hold largest + * cell representation + */ + CSF_VAR_TYPE buf_1; + void *buf = (void *)(&buf_1); + + CHECKHANDLE(map); + + /* make a copy */ + CsfGetVarType(buf, maxVal, map->appCR); + + /* convert */ + map->app2file((size_t)1, buf); + + /* set */ + CsfGetVarType( (void *)&(map->raster.maxVal), buf, RgetCellRepr(map)); + + map->minMaxStatus = MM_DONTKEEPTRACK; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/pminval.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/pminval.c new file mode 100644 index 000000000..4b498ebbd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/pminval.c @@ -0,0 +1,39 @@ +#include "csf.h" +#include "csfimpl.h" + +/* set new minimum cell value + * RputMinVal set a new value stored in + * the header as the minimum value. + * minMaxStatus is set to MM_DONTKEEPTRACK + * + * NOTE + * Note that the header minimum set must be equal or + * smaller than the minimum value in the map. + * + * example + * .so examples/set_min.tr + */ + +void RputMinVal( + MAP *map, /* map handle */ + const void *minVal) /* New minimum value */ +{ + /* use buffer that can hold largest + * cell representation + */ + CSF_VAR_TYPE buf_1; + void *buf = (void *)(&buf_1); + + CHECKHANDLE(map); + + /* make a copy */ + CsfGetVarType(buf, minVal, map->appCR); + + /* convert */ + map->app2file((size_t)1,buf); + + /* set */ + CsfGetVarType( (void *)&(map->raster.minVal), buf, RgetCellRepr(map)); + + map->minMaxStatus = MM_DONTKEEPTRACK; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/putallmv.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/putallmv.c new file mode 100644 index 000000000..6904f2876 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/putallmv.c @@ -0,0 +1,56 @@ +#include + +#include "csf.h" +#include "csfimpl.h" + +/* make all cells missing value in map + * RputAllMV writes a missing values to all the cells in a + * map. For this is allocates a buffer to hold one row at a + * time. + * returns 1 if succesfull, 0 in case of an error + */ +int RputAllMV( + MAP *m) +{ + size_t i,nc,nr; + void *buffer; + CSF_CR cr; + + CHECKHANDLE_GOTO(m, error); + if(! WRITE_ENABLE(m)) + { + M_ERROR(NOACCESS); + goto error; + } + + cr = RgetCellRepr(m); + nc = RgetNrCols(m); + + buffer = Rmalloc(m,nc); + if(buffer == NULL) + { + M_ERROR(NOCORE); + goto error; + } + + /* Fill buffer with determined Missingvalue*/ + SetMemMV(buffer, nc, cr); + + nr = RgetNrRows(m); + for(i = 0 ; i < nr; i++) + if (RputRow(m, i, buffer) != nc) + { + M_ERROR(WRITE_ERROR); + goto error_f; + } + CSF_FREE(buffer); + + CsfSetVarTypeMV( &(m->raster.minVal), cr); + CsfSetVarTypeMV( &(m->raster.maxVal), cr); + + return(1); +error_f: + CSF_FREE(buffer); +error: + return(0); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/putattr.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/putattr.c new file mode 100644 index 000000000..7dba3fd37 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/putattr.c @@ -0,0 +1,213 @@ +#include "csf.h" +#include "csfimpl.h" + +/* make block empty + */ +static void InitBlock( + ATTR_CNTRL_BLOCK *b) /* write-only */ +{ + int i; + for (i = 0 ; i < NR_ATTR_IN_BLOCK; i++) + { + b->attrs[i].attrId = END_OF_ATTRS; + b->attrs[i].attrSize = 0; + b->attrs[i].attrOffset = 0; + } + b->next = 0; +} + +/* replace an attribute (LIBRARY_INTERNAL) + * + */ +CSF_ATTR_ID CsfUpdateAttribute( + MAP *m, /* map handle */ + CSF_ATTR_ID id, /* attribute identification */ + size_t itemSize, /* size of each attribute element. + * 1 or sizeof(char) in case of a + * string + */ + size_t nitems, /* number of attribute elements or + * strlen+1 in case of a variable character + * string field. Don't forget to pad a + * non-variable field with '\0'! + */ + void *attr) /* buffer containing attribute */ +{ + PRECOND(CsfValidSize(itemSize)); + if (CsfAttributeSize(m,id)) + if (! MdelAttribute(m,id)) + return 0; + return CsfPutAttribute(m,id,itemSize,nitems, attr); +} + + + +/* write an attribute to a map (LIBRARY_INTERNAL) + * MputAttribute writes exactly the number of bytes specified + * by the size argument starting at the address of argument + * attr. Which means that you can't simply pass a structure or an + * array of structures as argument attr, due to the alignment + * of fields within a structure and internal swapping. You can + * only pass an array of elementary types (UINT1, REAL4, etc.) + * or character string. + * If one wants to refresh an attribute, one should first + * call MdelAttribute to delete the attribute and then use + * MputAttribute to write the new value. + * returns argument id or 0 in case of error. + * + * Merrno + * ATTRDUPL + * NOACCESS + * WRITE_ERROR + */ +CSF_ATTR_ID CsfPutAttribute( + MAP *m, /* map handle */ + CSF_ATTR_ID id, /* attribute identification */ + size_t itemSize, /* size of each attribute element. + * 1 or sizeof(char) in case of a + * string + */ + size_t nitems, /* number of attribute elements or + * strlen+1 in case of a variable character + * string field. Don't forget to pad a + * non-variable field with '\0'! + */ + void *attr) /* buffer containing attribute */ +{ + size_t size = nitems * itemSize; + + PRECOND(CsfValidSize(itemSize)); + PRECOND(size > 0); + + if (CsfSeekAttrSpace(m,id,size) == 0) + goto error; + + if (m->write(attr, itemSize, nitems, m->fp) != nitems) + { + M_ERROR(WRITE_ERROR); + goto error; + } + return(id); /* succes */ +error: return(0); /* failure */ +} + +/* seek to space for attribute (LIBRARY_INTERNAL) + * CsfSeekAttrSpace seeks to the a point in the file where + * the attribute must be stored and update the attribute control + * blocks accordingly. + * Writing can still fail since there is no check if that space is really + * avalaible on the device. After this call returns the file is already + * seeked to the point the functions returns. + * returns the file position or 0 in case of error. + * + * Merrno + * ATTRDUPL + * NOACCESS + * WRITE_ERROR + */ +CSF_FADDR32 CsfSeekAttrSpace( + MAP *m, /* map handle */ + CSF_ATTR_ID id, /* attribute identification only for check if avalaible */ + size_t size) /* size to be seeked to */ +{ + ATTR_CNTRL_BLOCK b; + CSF_FADDR32 currBlockPos, prevBlockPos=USED_UNINIT_ZERO, newPos, endBlock, resultPos=0; + int noPosFound; + int i; + + if (MattributeAvail(m ,id)) + { + M_ERROR(ATTRDUPL); + goto error; + } + + if (! WRITE_ENABLE(m)) + { + M_ERROR(NOACCESS); + goto error; + } + + currBlockPos = m->main.attrTable; + noPosFound = 1; + while (noPosFound) + { + if (currBlockPos == 0) + { + if (m->main.attrTable == 0) + { /* FIRST BLOCK */ + newPos =( (CSF_FADDR)(m->raster.nrRows)* + (CSF_FADDR)(m->raster.nrCols)* + (CSF_FADDR)(CELLSIZE(RgetCellRepr(m)))) + + ADDR_DATA; + m->main.attrTable = newPos; + } + else + { /* NEW/NEXT BLOCK */ + newPos = b.attrs[LAST_ATTR_IN_BLOCK].attrOffset + + + b.attrs[LAST_ATTR_IN_BLOCK].attrSize; + b.next = newPos; + if (CsfWriteAttrBlock(m, prevBlockPos, &b)) + { + M_ERROR(WRITE_ERROR); + resultPos = 0; + } + } + InitBlock(&b); + b.attrs[0].attrOffset = + newPos + SIZE_OF_ATTR_CNTRL_BLOCK; + currBlockPos = newPos; + noPosFound = 0; + } + else + CsfReadAttrBlock(m, currBlockPos, &b); + i = 0; /* this is also the right index if a new block + is added ! */ + while (noPosFound && i < NR_ATTR_IN_BLOCK) + switch (b.attrs[i].attrId) + { + case END_OF_ATTRS: + POSTCOND(i >= 1); + /* i >= 1 , no block otherwise */ + b.attrs[i].attrOffset = + b.attrs[i-1].attrOffset + + b.attrs[i-1].attrSize; + noPosFound = 0; + break; + case ATTR_NOT_USED: + if (i == NR_ATTR_IN_BLOCK) + endBlock = b.next; + else + endBlock = b.attrs[i+1].attrOffset; + if ( (size_t)( endBlock - b.attrs[i].attrOffset) >= size) + /* this position can + hold the attr */ + noPosFound = 0; + else + i++; + break; + default: + i++; + } /* switch */ +/* if (b.next == 0) + ? When did I change this CW + remember this block position since it may be have + to rewritten +*/ + prevBlockPos = currBlockPos; + if (noPosFound) + currBlockPos = b.next; + } /* while */ + + b.attrs[i].attrSize = size; + b.attrs[i].attrId = id; + resultPos = b.attrs[i].attrOffset; + + if (CsfWriteAttrBlock(m, currBlockPos, &b)) + { + M_ERROR(WRITE_ERROR); + resultPos = 0; + } + fseek(m->fp, (long)resultPos, SEEK_SET); /* fsetpos() is better */ +error: return resultPos; +} /* CsfSeekAttrSpace */ diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/putsomec.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/putsomec.c new file mode 100644 index 000000000..0f427924f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/putsomec.c @@ -0,0 +1,262 @@ +#include "csf.h" +#include "csfimpl.h" + +typedef void (*DF)(void *min, void *max, size_t n, const void *buf); + +/* DET: + * while (min is MV) && (i != nrCells) + * min = max = buf[i++]; + * while (i != nrCells) + * if (buf[i] is not MV) + * if (buf[i] < min) min = buf[i]; + * if (buf[i] > max) max = buf[i]; + * i++ + */ +#define DET(min, max, nrCells, buf, type) \ + {\ + size_t i=0;\ + while (((*(type *)min) == MV_##type) && (i != nrCells))\ + (*(type *)max) = (*(type *)min) =\ + ((const type *)buf)[i++];\ + while (i != nrCells) \ + {\ + if (((const type *)buf)[i] != MV_##type)\ + {\ + if (((const type *)buf)[i] < (*(type *)min) )\ + (*(type *)min) = ((const type *)buf)[i];\ + if (((const type *)buf)[i] > (*(type *)max) )\ + (*(type *)max) = ((const type *)buf)[i];\ + }\ + i++;\ + }\ + } + + +/* determines new minimum and new maximum + * DetMinMax({U}INT[124]|REAL[48]) analyzes + * an array of cells and adjust the min and max argument + * if necessary. If min and max are not yet set then they + * must be MV both. The function + * assumes that both min and max are MV if min is MV. + */ +static void DetMinMaxINT1( +INT1 *min, /* read-write. adjusted minimum */ +INT1 *max, /* read-write. adjusted maximum */ +size_t nrCells, /* number of cells in buf */ +const INT1 *buf) /* cell values to be examined */ +{ + DET(min, max, nrCells, buf, INT1); +} + +/* determines new minimum and new maximum + * DetMinMax* (* = all cell representation) analyzes + * an array of cells and adjust the min and max argument + * if necessary. If min and max are not yet set then they + * must be MV both. The function + * assumes that both min and max are MV if min is MV. + */ + static void DetMinMaxINT2( +INT2 *min, /* read-write. adjusted minimum */ +INT2 *max, /* read-write. adjusted maximum */ +size_t nrCells,/* number of cells in buf */ +const INT2 *buf) /* cell values to be examined */ +{ + DET(min, max, nrCells, buf, INT2); +} + +/* determines new minimum and new maximum + * DetMinMax* (* = all cell representation) analyzes + * an array of cells and adjust the min and max argument + * if necessary. If min and max are not yet set then they + * must be MV both. The function + * assumes that both min and max are MV if min is MV. + */ + static void DetMinMaxINT4( +INT4 *min, /* read-write. adjusted minimum */ +INT4 *max, /* read-write. adjusted maximum */ +size_t nrCells,/* number of cells in buf */ +const INT4 *buf) /* cell values to be examined */ +{ + DET(min, max, nrCells, buf, INT4); +} + + +/* determines new minimum and new maximum + * DetMinMax* (* = all cell representation) analyzes + * an array of cells and adjust the min and max argument + * if necessary. If min and max are not yet set then they + * must be MV both. The function + * assumes that both min and max are MV if min is MV. + */ + static void DetMinMaxUINT1( +UINT1 *min, /* read-write. adjusted minimum */ +UINT1 *max, /* read-write. adjusted maximum */ +size_t nrCells,/* number of cells in buf */ +const UINT1 *buf) /* cell values to be examined */ +{ + DET(min, max, nrCells, buf, UINT1); +} + +/* determines new minimum and new maximum + * DetMinMax* (* = all cell representation) analyzes + * an array of cells and adjust the min and max argument + * if necessary. If min and max are not yet set then they + * must be MV both. The function + * assumes that both min and max are MV if min is MV. + */ + static void DetMinMaxUINT2( +UINT2 *min, /* read-write. adjusted minimum */ +UINT2 *max, /* read-write. adjusted maximum */ +size_t nrCells,/* number of cells in buf */ +const UINT2 *buf) /* cell values to be examined */ +{ + DET(min, max, nrCells, buf, UINT2); +} + +/* determines new minimum and new maximum + * DetMinMax* (* = all cell representation) analyzes + * an array of cells and adjust the min and max argument + * if necessary. If min and max are not yet set then they + * must be MV both. The function + * assumes that both min and max are MV if min is MV. + */ + static void DetMinMaxUINT4( +UINT4 *min, /* read-write. adjusted minimum */ +UINT4 *max, /* read-write. adjusted maximum */ +size_t nrCells,/* number of cells in buf */ +const UINT4 *buf) /* cell values to be examined */ +{ + DET(min, max, nrCells, buf, UINT4); +} + + +/* determines new minimum and new maximum + * DetMinMax* (* = all cell representation) analyzes + * an array of cells and adjust the min and max argument + * if necessary. If min and max are not yet set then they + * must be MV both. The function + * assumes that both min and max are MV if min is MV. + */ +static void DetMinMaxREAL4( +REAL4 *min, /* read-write. adjusted minimum */ +REAL4 *max, /* read-write. adjusted maximum */ +size_t nrCells,/* number of cells in buf */ +const REAL4 *buf) /* cell values to be examined */ +{ + size_t i = 0; + + if ( IS_MV_REAL4(min)) + { + while ( IS_MV_REAL4(min) && (i != nrCells)) + *((UINT4 *)min) = ((const UINT4 *)buf)[i++]; + *max = *min; + } + while (i != nrCells) + { + if (! IS_MV_REAL4(buf+i)) + { + if (buf[i] < *min ) + *min = buf[i]; + if (buf[i] > *max) + *max = buf[i]; + } + i++; + } +} + + + +/* determines new minimum and new maximum + * DetMinMax* (* = all cell representation) analyzes + * an array of cells and adjust the min and max argument + * if necessary. If min and max are not yet set then they + * must be MV both. The function + * assumes that both min and max are MV if min is MV. + */ +static void DetMinMaxREAL8( +REAL8 *min, /* read-write. adjusted minimum */ +REAL8 *max, /* read-write. adjusted maximum */ +size_t nrCells,/* number of cells in buf */ +const REAL8 *buf) /* cell values to be examined */ +{ + size_t i = 0; + + if ( IS_MV_REAL8(min)) + { + while ( IS_MV_REAL8(min) && (i != nrCells)) + { + ((UINT4 *)min)[0] = ((const UINT4 *)buf)[2*i]; + ((UINT4 *)min)[1] = ((const UINT4 *)buf)[(2*i++)+1]; + } + *max = *min; + } + while (i != nrCells) + { + if (! IS_MV_REAL8(buf+i)) + { + if (buf[i] < *min ) + *min = buf[i]; + if (buf[i] > *max) + *max = buf[i]; + } + i++; + } +} + + +/* write a stream of cells + * RputSomeCells views a raster as one linear stream of + * cells, with row i+1 placed after row i. + * In this stream any sequence can be written by specifying an + * offset and the number of cells to be written + * returns the number of cells written, just as fwrite + * + * example + * .so examples/somecell.tr + */ +size_t RputSomeCells( + MAP *map, /* map handle */ + size_t offset, /* offset from pixel (row,col) = (0,0) */ + size_t nrCells, /* number of cells to be read */ + void *buf)/* read-write. Buffer large enough to + * hold nrCells cells in the in-file cell representation + * or the in-app cell representation. + * If these types are not equal then the buffer is + * converted from the in-app to the in-file + * cell representation. + */ +{ + CSF_FADDR writeAt; + CSF_CR cr = map->raster.cellRepr; + + /* convert */ + map->app2file(nrCells, buf); + + + if (map->minMaxStatus == MM_KEEPTRACK) + { + const DF detMinMaxFunc[12] = { + (DF)DetMinMaxUINT1, (DF)DetMinMaxUINT2, + (DF)DetMinMaxUINT4, NULL /* 0x03 */ , + (DF)DetMinMaxINT1 , (DF)DetMinMaxINT2 , + (DF)DetMinMaxINT4 , NULL /* 0x07 */ , + NULL /* 0x08 */ , NULL /* 0x09 */ , + (DF)DetMinMaxREAL4, (DF)DetMinMaxREAL8 }; + + void *min = &(map->raster.minVal); + void *max = &(map->raster.maxVal); + + PRECOND(CSF_UNIQ_CR_MASK(cr) < 12); + PRECOND(detMinMaxFunc[CSF_UNIQ_CR_MASK(cr)] != NULL); + + detMinMaxFunc[CSF_UNIQ_CR_MASK(cr)](min, max, nrCells, buf); + + } + else + map->minMaxStatus = MM_WRONGVALUE; + + writeAt = ((CSF_FADDR)offset) << LOG_CELLSIZE(cr); + writeAt += ADDR_DATA; + fseek(map->fp, (long)writeAt, SEEK_SET); + return(map->write(buf, (size_t)CELLSIZE(cr), (size_t)nrCells, map->fp)); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/putx0.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/putx0.c new file mode 100644 index 000000000..6524d28f3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/putx0.c @@ -0,0 +1,26 @@ +#include "csf.h" +#include "csfimpl.h" + + +/* change the x value of upper left co-ordinate + * RputXUL changes the x value of upper left co-ordinate. + * returns the new x value of upper left co-ordinate or 0 + * case of an error. + * + * Merrno + * NOACCESS + */ +REAL8 RputXUL( + MAP *map, /* map handle */ + REAL8 xUL) /* new x value of top left co-ordinate */ +{ + CHECKHANDLE_GOTO(map, error); + if(! WRITE_ENABLE(map)) + { + M_ERROR(NOACCESS); + goto error; + } + map->raster.xUL = xUL; + return(xUL); +error: return(0); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/puty0.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/puty0.c new file mode 100644 index 000000000..38b62de03 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/puty0.c @@ -0,0 +1,27 @@ +#include "csf.h" +#include "csfimpl.h" + +/* change the y value of upper left co-ordinate + * RputYUL changes the y value of upper left co-ordinate. + * Whether this is the largest or smallest y value depends on the + * projection (See MgetProjection()). + * returns the new y value of upper left co-ordinate or 0 + * case of an error. + * + * Merrno + * NOACCESS + */ +REAL8 RputYUL( + MAP *map, /* map handle */ + REAL8 yUL) /* new y value of upper left co-ordinate */ +{ + CHECKHANDLE_GOTO(map, error); + if(! WRITE_ENABLE(map)) + { + M_ERROR(NOACCESS); + goto error; + } + map->raster.yUL = yUL; + return(yUL); +error: return(0); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/pvalscal.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/pvalscal.c new file mode 100644 index 000000000..d9a6dd1aa --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/pvalscal.c @@ -0,0 +1,31 @@ +#include "csf.h" +#include "csfimpl.h" + +/* put new value scale + * RputValueScale changes the value scale + * of the map. + * + * returns + * new value scale or VS_UNDEFINED in case of an error. + * + * NOTE + * Note that there is no check if the cell representation + * is complaint. + * + * Merrno + * NOACCESS + */ +CSF_VS RputValueScale( + MAP *map, /* map handle */ + CSF_VS valueScale) /* new value scale */ +{ + CHECKHANDLE_GOTO(map, error); + if(! WRITE_ENABLE(map)) + { + M_ERROR(NOACCESS); + goto error; + } + map->raster.valueScale = valueScale; + return(valueScale); +error: return(VS_UNDEFINED); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/rattrblk.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rattrblk.c new file mode 100644 index 000000000..c545269c1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rattrblk.c @@ -0,0 +1,20 @@ +#include "csf.h" +#include "csfimpl.h" + +/* read attribute control block (LIBRARY_INTERNAL) + */ +void CsfReadAttrBlock( + MAP *m, /* map handle */ + CSF_FADDR32 pos, /* file position of block to be read */ + ATTR_CNTRL_BLOCK *b) /* write-only. attribute control block read */ +{ + int i; + fseek(m->fp, (long)pos, SEEK_SET); + for(i=0; i < NR_ATTR_IN_BLOCK; i++) + { + m->read((void *)&(b->attrs[i].attrId), sizeof(UINT2),(size_t)1,m->fp); + m->read((void *)&(b->attrs[i].attrOffset), sizeof(CSF_FADDR32),(size_t)1,m->fp); + m->read((void *)&(b->attrs[i].attrSize), sizeof(UINT4),(size_t)1,m->fp); + } + m->read((void *)&(b->next), sizeof(CSF_FADDR32),(size_t)1,m->fp); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/rcomp.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rcomp.c new file mode 100644 index 000000000..0b3010a89 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rcomp.c @@ -0,0 +1,70 @@ +#include "csf.h" +#include "csfimpl.h" + +/* compare 2 maps for their location attributes + * Rcompare compares 2 maps for all location attributes: + * + * projection, + * + * xUL, yUL, angle, + * + * cell size and + * + * number of rows and columns + * + * returns 0 if one of these attributes differ or in case of an error, 1 + * if they are all equal. + * + * Merrno + * NOT_RASTER + */ +int Rcompare( + const MAP *m1, /* map handle 1 */ + const MAP *m2) /* map handle 2 */ +{ + CHECKHANDLE_GOTO(m1, error); + + /* check if mapType is T_RASTER */ + if ((m1->main.mapType != T_RASTER) + || (m2->main.mapType != T_RASTER)) + { + M_ERROR(NOT_RASTER); + goto error; + } + + if ( + MgetProjection(m1) == MgetProjection(m2) && + m1->raster.xUL == m2->raster.xUL && + m1->raster.yUL == m2->raster.yUL && + m1->raster.cellSize == m2->raster.cellSize && + m1->raster.cellSizeDupl == m2->raster.cellSizeDupl && + m1->raster.angle == m2->raster.angle && + m1->raster.nrRows == m2->raster.nrRows && + m1->raster.nrCols == m2->raster.nrCols + ) return(1); +error: + return(0); +} + +int RgetLocationAttributes( + CSF_RASTER_LOCATION_ATTRIBUTES *l, /* fill in this struct */ + const MAP *m) /* map handle to copy from */ +{ + CHECKHANDLE_GOTO(m, error); + *l = m->raster; + return 1; +error: + return 0; +} + +int RcompareLocationAttributes( + const CSF_RASTER_LOCATION_ATTRIBUTES *m1, /* */ + const CSF_RASTER_LOCATION_ATTRIBUTES *m2) /* */ +{ + return ( + m1->projection == m2->projection && + m1->xUL == m2->xUL && m1->yUL == m2->yUL && + m1->cellSize == m2->cellSize && + m1->angle == m2->angle && + m1->nrRows == m2->nrRows && m1->nrCols == m2->nrCols ); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/rcoords.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rcoords.c new file mode 100644 index 000000000..41004383c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rcoords.c @@ -0,0 +1,101 @@ +#include "csf.h" +#include "csfimpl.h" + +/* compute true world co-ordinate of a pixel + * RrowCol2Coords computes the true world co-ordinate from a + * row, column index. + * The row, column co-ordinate + * don't have to be on the map. They are just relative to upper left position. + * For example (row,col) = (-1,0) computes the (x,y) co-ordinate of + * the pixel that is right above upper left pixel. + * + * returns + * 0 if the co-ordinate is outside the map. + * 1 if inside. + * -1 in case of an error. + * + * Merrno + * ILL_CELLSIZE + */ +int RgetCoords( + const MAP *m, /* map handle */ + int inCellPos, /* nonzero if you want the co-ordinate + * at the centre of the cell, 0 if you + * want the upper left co-ordinate of the cell + */ + size_t row, /* Row number (relates to y position). */ + size_t col, /* Column number (relates to x position). */ + double *x, /* write-only. Returns x of true co-ordinate */ + double *y) /* write-only. Returns y of true co-ordinate */ +{ + return RrowCol2Coords(m, + (double)row+(inCellPos ? 0.5 : 0.0), + (double)col+(inCellPos ? 0.5 : 0.0), + x,y); +} + +/* compute true world co-ordinate from row, column index + * RrowCol2Coords computes the true world co-ordinate from a + * row, column index. The row,column co-ordinate can be fractions. + * For example (row,col) = (0.5,0.5) computes the (x,y) co-ordinate of + * the centre of the upper left pixel. Secondly, the row and column co-ordinate + * don't have to be on the map. They are just relative to upper left position. + * For example (row,col) = (-0.5,0.5) computes the (x,y) co-ordinate of + * the centre of the pixel that is right above upper left pixel. + */ +void RasterRowCol2Coords( + const CSF_RASTER_LOCATION_ATTRIBUTES *m, /* raster handle */ + double row, /* Row number (relates to y position). */ + double col, /* Column number (relates to x position). */ + double *x, /* write-only. x co-ordinate */ + double *y) /* write-only. y co-ordinate */ +{ + double cs = m->cellSize; + double c = m->angleCos; + double s = m->angleSin; + double yRow = cs * row; + double xCol = cs * col; + double xCol_t = xCol * c - yRow * s; + double yRow_t = xCol * s + yRow * c; + + *x = m->xUL + xCol_t; + if (m->projection == PT_YINCT2B) + *y = m->yUL + yRow_t; + else /* all other projections */ + *y = m->yUL - yRow_t; +} + +/* compute true world co-ordinate from row, column index + * RasterRowCol2Coords computes the true world co-ordinate from a + * row, column index. The row,column co-ordinate can be fractions. + * For example (row,col) = (0.5,0.5) computes the (x,y) co-ordinate of + * the centre of the upper left pixel. Secondly, the row and column co-ordinate + * don't have to be on the map. They are just relative to upper left position. + * For example (row,col) = (-0.5,0.5) computes the (x,y) co-ordinate of + * the centre of the pixel that is right above upper left pixel. + * + * returns + * 0 if the co-ordinate is outside the map. + * 1 if inside. + * -1 in case of an error. + * + * Merrno + * ILL_CELLSIZE + */ +int RrowCol2Coords(const MAP *m, /* map handle */ + double row, /* Row number (relates to y position). */ + double col, /* Column number (relates to x position). */ + double *x, /* write-only. x co-ordinate */ + double *y) /* write-only. y co-ordinate */ +{ + if (m->raster.cellSize <= 0 + || m->raster.cellSize != m->raster.cellSizeDupl ) + { + M_ERROR(ILL_CELLSIZE); + goto error; + } + RasterRowCol2Coords(&(m->raster),row,col,x,y); + return( (m->raster.nrRows > row) && (m->raster.nrCols > col) && + (row >= 0) && (col >= 0)); +error: return(-1); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/rdup2.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rdup2.c new file mode 100644 index 000000000..a8de2a9c8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rdup2.c @@ -0,0 +1,48 @@ +#include "csf.h" +#include "csfimpl.h" + + +/* create a new map by cloning another one + * Rdup creates a new empty map from the specifications of another map. + * No cell values are copied. It uses a call to Rcreate to create the + * map. See Rcreate for legal values of the args cellRepr and valueScale. + * returns the map handle of the newly created map or NULL in case of an + * error + * + * Merrno + * NOT_RASTER plus the Merrno codes of Rcreate + * + * EXAMPLE + * .so examples/dupbool.tr + */ +MAP *Rdup( + const char *toFile, /* file name of map to be created */ + const MAP *from, /* map to clone from */ + CSF_CR cellRepr, /* cell representation of new map */ + CSF_VS dataType) /* datatype/valuescale of new map */ +{ + MAP *newMap = NULL; /* set NULL for goto error */ + + CHECKHANDLE_GOTO(from, error); + + /* check if mapType is T_RASTER */ + if (from->main.mapType != T_RASTER) + { + M_ERROR(NOT_RASTER); + goto error; + } + + newMap = Rcreate(toFile, + (size_t)from->raster.nrRows, + (size_t)from->raster.nrCols, + cellRepr, + dataType, + from->main.projection, + from->raster.xUL, + from->raster.yUL, + from->raster.angle, + from->raster.cellSize); + +error: + return newMap ; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/reseterr.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/reseterr.c new file mode 100644 index 000000000..3b3f02368 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/reseterr.c @@ -0,0 +1,13 @@ +#include "csf.h" +#include "csfimpl.h" + +/* reset Merrno variable + * ResetMerrno sets the Merrno variable to NOERROR (0). + * + * example + * .so examples/testcsf.tr + */ +void ResetMerrno(void) +{ + Merrno = NOERROR; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/rextend.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rextend.c new file mode 100644 index 000000000..f159fcee5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rextend.c @@ -0,0 +1,101 @@ +#include "csf.h" +#include "csfimpl.h" +#include + +/* round up and down come up with a number such + * we have rounded to integer multiplication + * anyway this should hold: + POSTCOND(RoundUp( 5.3, 4) == 8 ); + POSTCOND(RoundUp( 4 , 4) == 8 ); + POSTCOND(RoundUp(-5.3, 4) == -4 ); + POSTCOND(RoundUp(-4, 4) == 0 ); + POSTCOND(RoundDown( 5.3, 4) == 4 ); + POSTCOND(RoundDown( 4 , 4) == 0 ); + POSTCOND(RoundDown(-5.3, 4) == -8 ); + POSTCOND(RoundDown(-4 , 4) == -8 ); + */ + +static double RoundDown( + double v, + double round) +{ + double rVal = fmod(v, round); + double x; + if(rVal == 0) + return v-round; + if (v < 0) + x = v-round-rVal; + else + x = v-rVal; + return x; +} + +static double RoundUp( + double v, + double round) +{ + double rVal = fmod(v, round); + if(rVal == 0) + return v+round; + if (v < 0) + return v-rVal; + else + return v+round-rVal; +} + +/* compute (xUL,yUL) and nrRows, nrCols from some coordinates + * RcomputeExtend computes parameters to create a raster maps + * from minimum and maximum x and y coordinates, projection information, + * cellsize and units. The resulting parameters are computed that the + * smallest raster map can be created that will include the two + * coordinates given, assuming a default angle of 0. + * Which coordinates are the maximum or minimum are + * determined by the function itself. + */ +void RcomputeExtend( + REAL8 *xUL, /* write-only, resulting xUL */ + REAL8 *yUL, /* write-only, resulting yUL */ + size_t *nrRows, /* write-only, resulting nrRows */ + size_t *nrCols, /* write-only, resulting nrCols */ + double x_1, /* first x-coordinate */ + double y_1, /* first y-coordinate */ + double x_2, /* second x-coordinate */ + double y_2, /* second y-coordinate */ + CSF_PT projection, /* required projection */ + REAL8 cellSize, /* required cellsize, > 0 */ + double rounding) /* assure that (xUL/rounding), (yUL/rouding) + * (xLL/rounding) and (yLL/rounding) will + * will all be an integers values > 0 + */ +{ + /* + * xUL ______ + | | + | | + | | + ------ + + */ + double yLL,xUR = x_1 > x_2 ? x_1 : x_2; + *xUL = x_1 < x_2 ? x_1 : x_2; + *xUL = RoundDown(*xUL, rounding); /* Round down */ + xUR = RoundUp( xUR, rounding); /* Round up */ + POSTCOND(*xUL <= xUR); + *nrCols = (size_t)ceil((xUR - *xUL)/cellSize); + if (projection == PT_YINCT2B) + { + yLL = y_1 > y_2 ? y_1 : y_2; /* highest value at bottom */ + *yUL = y_1 < y_2 ? y_1 : y_2; /* lowest value at top */ + *yUL = RoundDown(*yUL, rounding); + yLL = RoundUp( yLL, rounding); + } + else + { + yLL = y_1 < y_2 ? y_1 : y_2; /* lowest value at bottom */ + *yUL = y_1 > y_2 ? y_1 : y_2; /* highest value at top */ + *yUL = RoundUp( *yUL, rounding); + yLL = RoundDown( yLL, rounding); + } + *nrRows = (size_t)ceil(fabs(yLL - *yUL)/cellSize); +} + diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/rmalloc.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rmalloc.c new file mode 100644 index 000000000..15f778b90 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rmalloc.c @@ -0,0 +1,31 @@ +#include "csf.h" +#include "csfimpl.h" + +/* allocate dynamic memory large enough to hold in-file and app cells + * Rmalloc allocates memory to hold nrOfCells + * cells in both the in-file and app cell representation. Allocation + * is done by malloc for other users. Our own (utrecht university) applications + * calls ChkMalloc. Freeing memory allocated by Rmalloc is done by free (or Free). + * + * NOTE + * Note that a possible RuseAs call must be done BEFORE Rmalloc. + * + * returns + * a pointer the allocated memory or + * NULL + * if the request fails + * + * example + * .so examples/_row.tr + */ +void *Rmalloc( + const MAP *m, /* map handle */ + size_t nrOfCells) /* number of cells allocated memory must hold */ +{ + CSF_CR inFileCR = RgetCellRepr(m); + CSF_CR largestCellRepr = + LOG_CELLSIZE(m->appCR) > LOG_CELLSIZE(inFileCR) + ? m->appCR : inFileCR; + + return CSF_MALLOC((size_t)CSFSIZEOF(nrOfCells, largestCellRepr)); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/rrowcol.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rrowcol.c new file mode 100644 index 000000000..f3525d9de --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/rrowcol.c @@ -0,0 +1,139 @@ +#include "csf.h" +#include "csfimpl.h" + +#include /* floor */ + +/* compute (fractional) row, column index from true world co-ordinate. + * RasterCoords2RowCol computes row, column index from true world co-ordinate. + * The row and column co-ordinate are returned as fractions (See parameters + * section). + * The x,y co-ordinate + * don't have to be on the map. They are just relative to upper left position. + * + * returns + * 0 if the co-ordinate is outside the map, + * 1 if inside, + * -1 in case of an error + * + * Merrno + * ILL_CELLSIZE + */ +void RasterCoords2RowCol( + const CSF_RASTER_LOCATION_ATTRIBUTES *m, + double x, /* x of true co-ordinate */ + double y, /* y of true co-ordinate */ + double *row, /* write-only. Row index (y-pos). floor(row) is row number, + * if (row >= 0) then fmod(row, 1) is in-pixel displacement from pixel-top, + * if (row <0) then fmod(row, 1) is in-pixel displacement from pixel-bottom. + */ + double *col) /* write-only. Column index (x-pos). floor(col) is column number, + * if (col >= 0) then fmod(col, 1) is in-pixel displacement from pixel-left, + * if (col <0) then fmod(col, 1) is in-pixel displacement from pixel-right. + */ +{ + double cs = m->cellSize; + double xCol = (x - m->xUL) / cs; + double yRow = (((m->projection == PT_YINCT2B) + ? (y - m->yUL) + : (m->yUL - y)) / cs); + /* rotate clockwise: */ + double c = m->angleCos; /* cos(t) == cos(-t) */ + double s = -(m->angleSin); /* -sin(t) == sin(-t) */ + *col = xCol * c - yRow * s; + *row = xCol * s + yRow * c; +} + +int RasterCoords2RowColChecked( + const CSF_RASTER_LOCATION_ATTRIBUTES *m, + double x, /* x of true co-ordinate */ + double y, /* y of true co-ordinate */ + double *row, + double *col) +{ + double row_,col_; /* use copies, func( , , ,&dummy, &dummy) will fail */ + RasterCoords2RowCol(m,x,y,&row_,&col_); + *row = row_; + *col = col_; + return( row_ >= 0 && col_ >= 0 && + (m->nrRows > row_) && (m->nrCols > col_) ); +} + +/* compute (fractional) row, column index from true world co-ordinate. + * Rcoord2RowCol computes row, column index from true world co-ordinate. + * The row and column co-ordinate are returned as fractions (See parameters + * section). + * The x,y co-ordinate + * don't have to be on the map. They are just relative to upper left position. + * + * returns + * 0 if the co-ordinate is outside the map, + * 1 if inside, + * -1 in case of an error + * + * Merrno + * ILL_CELLSIZE + */ +int Rcoords2RowCol( + const MAP *m, /* map handle */ + double x, /* x of true co-ordinate */ + double y, /* y of true co-ordinate */ + double *row, /* write-only. Row index (y-pos). floor(row) is row number, + * if (row >= 0) then fmod(row, 1) is in-pixel displacement from pixel-top, + * if (row <0) then fmod(row, 1) is in-pixel displacement from pixel-bottom. + */ + double *col) /* write-only. Column index (x-pos). floor(col) is column number, + * if (col >= 0) then fmod(col, 1) is in-pixel displacement from pixel-left, + * if (col <0) then fmod(col, 1) is in-pixel displacement from pixel-right. + */ +{ + double row_,col_; /* use copies, func( , , ,&dummy, &dummy) will fail + * otherwise + */ + if (m->raster.cellSize <= 0 + || (m->raster.cellSize != m->raster.cellSizeDupl ) ) + { /* CW we should put this in Mopen */ + M_ERROR(ILL_CELLSIZE); + goto error; + } + + RasterCoords2RowCol(&(m->raster),x,y,&row_,&col_); + *row = row_; + *col = col_; + return( row_ >= 0 && col_ >= 0 && + (m->raster.nrRows > row_) && (m->raster.nrCols > col_) ); +error: return(-1); +} + + +/* compute row, column number of true world co-ordinate + * RgetRowCol computes row, column number of true world co-ordinate. + * + * returns + * 0 if the co-ordinate is outside the map, + * 1 if inside, + * -1 in case of an error + * + * Merrno + * ILL_CELLSIZE + */ +int RgetRowCol( + const MAP *m, /* map handle */ + double x, /* x of true co-ordinate */ + double y, /* y of true co-ordinate */ + size_t *row, /* write-only. Row number (y-pos). + * Undefined if (x,y) is outside of map + */ + size_t *col) /* write-only. Column number (x-pos). + * Undefined if (x,y) is outside of map + */ +{ + double row_d,col_d; + int result; + result = Rcoords2RowCol(m,x,y,&row_d,&col_d); + if (result > 0) + { + *row = (size_t)floor(row_d); + *col = (size_t)floor(col_d); + } + return(result); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/ruseas.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/ruseas.c new file mode 100644 index 000000000..b272d3076 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/ruseas.c @@ -0,0 +1,537 @@ +#include "csf.h" +#include "csfimpl.h" + +static void UINT1tLdd(size_t nrCells, void *Buf) +{ + size_t i; + UINT1 *buf = (UINT1 *)Buf; + + for(i=0; i < (size_t)nrCells; i++) + if (buf[i] != MV_UINT1) + { + buf[i] %= (UINT1)10; + if (buf[i] == 0) + buf[i] = MV_UINT1; + } +} + +static void INT2tLdd(size_t nrCells, void *Buf) +{ + size_t i; + INT2 *inBuf = (INT2 *)Buf; + UINT1 *outBuf = (UINT1 *)Buf; + for(i=0; i < (size_t)nrCells; i++) + if (inBuf[i] != MV_INT2) + { + outBuf[i] = (UINT1)(ABS(inBuf[i]) % 10); + if (outBuf[i] == 0) + outBuf[i] = MV_UINT1; + } + else + outBuf[i] = MV_UINT1; +} + +#define TOBOOL(nr, buf, srcType)\ +{\ + size_t i;\ +/* loop must be upward to prevent overwriting of values \ + * not yet converted \ + */ \ + PRECOND(sizeof(srcType) >= sizeof(UINT1));\ + for(i=0; i < (size_t)nr; i++)\ + if (IS_MV_##srcType( ((srcType *)buf)+i) )\ + ((UINT1 *)buf)[i] = MV_UINT1;\ + else\ + ((UINT1 *)buf)[i] = ((srcType *)buf)[i] != ZERO_##srcType;\ +} + +static void INT1tBoolean(size_t nrCells, void *buf) +{ TOBOOL(nrCells, buf, INT1); } + +static void INT2tBoolean(size_t nrCells, void *buf) +{ TOBOOL(nrCells, buf, INT2); } + +static void INT4tBoolean(size_t nrCells, void *buf) +{ TOBOOL(nrCells, buf, INT4); } + +static void UINT1tBoolean(size_t nrCells, void *buf) +{ TOBOOL(nrCells, buf, UINT1); } + +static void UINT2tBoolean(size_t nrCells, void *buf) +{ TOBOOL(nrCells, buf, UINT2); } + +static void UINT4tBoolean(size_t nrCells, void *buf) +{ TOBOOL(nrCells, buf, UINT4); } + +static void REAL4tBoolean(size_t nrCells, void *buf) +{ TOBOOL(nrCells, buf, REAL4); } + +static void REAL8tBoolean(size_t nrCells, void *buf) +{ TOBOOL(nrCells, buf, REAL8); } + +#define CONV_BIG_TO_SMALL(nr, buf, destType, srcType)\ +{\ + size_t i;\ +/* loop must be upward to prevent overwriting of values \ + * not yet converted: \ + */ \ + PRECOND(sizeof(srcType) >= sizeof(destType)); /* upward loop OK */ \ + for(i=0; i < (size_t)nr; i++)\ + if (IS_MV_##srcType( ((srcType *)buf)+i) )\ + SET_MV_##destType( ((destType *)buf)+i);\ + else\ + ((destType *)buf)[i] = (destType)(((srcType *)buf)[i]);\ +} + +#define CONV_SMALL_TO_BIG(nr, buf, destType, srcType)\ +{\ + size_t i = (size_t)nr;\ +/* loop must be downward to prevent overwriting of values \ + * not yet converted: \ + */ \ + PRECOND(sizeof(srcType) <= sizeof(destType)); /* downward loop OK */ \ + do { i--;\ + if (IS_MV_##srcType( ((srcType *)buf)+i) )\ + SET_MV_##destType( ((destType *)buf)+i);\ + else\ + ((destType *)buf)[i] = (destType)(((srcType *)buf)[i]);\ + }while ( i != 0);\ +} + +static void UINT1tINT4(size_t nrCells, void *buf) +{ CONV_SMALL_TO_BIG(nrCells, buf, INT4, UINT1);} + +static void UINT1tREAL4(size_t nrCells, void *buf) +{ CONV_SMALL_TO_BIG(nrCells, buf, REAL4, UINT1);} + +static void UINT1tREAL8(size_t nrCells, void *buf) +{ CONV_SMALL_TO_BIG(nrCells, buf, REAL8, UINT1);} + +static void INT4tUINT1(size_t nrCells, void *buf) +{ CONV_BIG_TO_SMALL(nrCells, buf, UINT1, INT4);} + +static void INT2tUINT1(size_t nrCells, void *buf) +{ CONV_BIG_TO_SMALL(nrCells, buf, UINT1, INT2);} + +static void UINT2tUINT1(size_t nrCells, void *buf) +{ CONV_BIG_TO_SMALL(nrCells, buf, UINT1, UINT2);} + +static void INT4tREAL4(size_t nrCells, void *buf) +{ CONV_BIG_TO_SMALL(nrCells, buf, REAL4, INT4);} + +static void INT4tREAL8(size_t nrCells, void *buf) +{ CONV_SMALL_TO_BIG(nrCells, buf, REAL8, INT4);} + +static void REAL4tUINT1(size_t nrCells, void *buf) +{ CONV_BIG_TO_SMALL(nrCells, buf, UINT1, REAL4);} + +static void REAL4tINT4(size_t nrCells, void *buf) +{ CONV_BIG_TO_SMALL(nrCells, buf, INT4, REAL4);} + +static void REAL4tREAL8(size_t nrCells, void *buf) +{ CONV_SMALL_TO_BIG(nrCells, buf, REAL8, REAL4);} + +static void REAL8tUINT1(size_t nrCells, void *buf) +{ CONV_BIG_TO_SMALL(nrCells, buf, UINT1, REAL8);} + +static void REAL8tINT4(size_t nrCells, void *buf) +{ CONV_BIG_TO_SMALL(nrCells, buf, INT4, REAL8);} + +static void REAL8tREAL4(size_t nrCells, void *buf) +{ CONV_BIG_TO_SMALL(nrCells, buf, REAL4, REAL8);} + +static void Transform2( size_t nrCells, void *buf, CSF_CR destCellRepr, CSF_CR currCellRepr); + +static void INT1tINT4(size_t nrCells, void *buf) +{ Transform2(nrCells, buf, CR_INT4, CR_INT1);} + +static void INT1tREAL4(size_t nrCells, void *buf) +{ Transform2(nrCells, buf, CR_REAL4, CR_INT1);} + +static void INT1tREAL8(size_t nrCells, void *buf) +{ Transform2(nrCells, buf, CR_REAL8, CR_INT1);} + +static void INT2tINT4(size_t nrCells, void *buf) +{ Transform2(nrCells, buf, CR_INT4, CR_INT2);} + +static void INT2tREAL4(size_t nrCells, void *buf) +{ Transform2(nrCells, buf, CR_REAL4, CR_INT2);} + +static void INT2tREAL8(size_t nrCells, void *buf) +{ Transform2(nrCells, buf, CR_REAL8, CR_INT2);} + +static void UINT2tINT4(size_t nrCells, void *buf) +{ Transform2(nrCells, buf, CR_INT4, CR_UINT2);} + +static void UINT2tREAL4(size_t nrCells, void *buf) +{ Transform2(nrCells, buf, CR_REAL4, CR_UINT2);} + +static void UINT2tREAL8(size_t nrCells, void *buf) +{ Transform2(nrCells, buf, CR_REAL8, CR_UINT2);} + +static void UINT4tREAL4(size_t nrCells, void *buf) +{ Transform2(nrCells, buf, CR_REAL4, CR_UINT4);} + +static void UINT4tREAL8(size_t nrCells, void *buf) +{ Transform2(nrCells, buf, CR_REAL8, CR_UINT4);} + + +static void ConvertToINT2( size_t nrCells, void *buf, CSF_CR src) +{ + if (IS_SIGNED(src)) + { + POSTCOND(src == CR_INT1); + CONV_SMALL_TO_BIG(nrCells,buf, INT2, INT1); + } + else + { + POSTCOND(src == CR_UINT1); + CONV_SMALL_TO_BIG(nrCells,buf, INT2, UINT1); + } +} + +static void ConvertToINT4( size_t nrCells, void *buf, CSF_CR src) +{ + if (IS_SIGNED(src)) + { + POSTCOND(src == CR_INT2); + CONV_SMALL_TO_BIG(nrCells,buf, INT4, INT2); + } + else + { + POSTCOND(src == CR_UINT2); + CONV_SMALL_TO_BIG(nrCells,buf, INT4, UINT2); + } +} + + +static void UINT1tUINT2( +size_t nrCells, +void *buf) +{ + CONV_SMALL_TO_BIG(nrCells, buf, UINT2, UINT1); +} + +static void UINT2tUINT4( +size_t nrCells, +void *buf) +{ + CONV_SMALL_TO_BIG(nrCells, buf, UINT4, UINT2); +} + +static void ConvertToREAL4( size_t nrCells, void *buf, CSF_CR src) +{ + size_t i; + + i = (size_t)nrCells; + + if (IS_SIGNED(src)) + { + POSTCOND(src == CR_INT4); + INT4tREAL4(nrCells, buf); + } + else + { + POSTCOND(src == CR_UINT4); + { + do { + i--; + if ( ((UINT4 *)buf)[i] == MV_UINT4 ) + ((UINT4 *)buf)[i] = MV_UINT4; + else + ((REAL4 *)buf)[i] = (REAL4)((UINT4 *)buf)[i]; + } + while(i != 0); + } + } +} + +static void Transform2( + size_t nrCells, + void *buf, + CSF_CR destCellRepr, /* the output representation */ + CSF_CR currCellRepr) /* at start of while this is the representation + read in the MAP-file */ +{ + /* subsequent looping changes the to the new + * converted type + */ + while(currCellRepr != destCellRepr) + { + switch(currCellRepr) + { + case CR_INT1: ConvertToINT2(nrCells, buf, + currCellRepr); + currCellRepr = CR_INT2; + break; + case CR_INT2: ConvertToINT4(nrCells, buf, + currCellRepr); + currCellRepr = CR_INT4; + break; + case CR_INT4: ConvertToREAL4(nrCells, buf, + currCellRepr); + currCellRepr = CR_REAL4; + break; + case CR_UINT1: if (IS_SIGNED(destCellRepr)) + { + ConvertToINT2(nrCells, buf, + currCellRepr); + currCellRepr = CR_INT2; + } + else + { + UINT1tUINT2(nrCells, buf); + currCellRepr = CR_UINT2; + } + break; + case CR_UINT2: if (destCellRepr == CR_INT4) + { + ConvertToINT4(nrCells, buf, + currCellRepr); + currCellRepr = CR_INT4; + } + else + { + UINT2tUINT4(nrCells, buf); + currCellRepr = CR_UINT4; + } + break; + case CR_UINT4: ConvertToREAL4(nrCells, buf, + currCellRepr); + currCellRepr = CR_REAL4; + break; + default : POSTCOND(currCellRepr == CR_REAL4); + REAL4tREAL8(nrCells, buf); + currCellRepr = CR_REAL8; + } + } +} + +/* OLD STUFF +void TransForm( + const MAP *map, + size_t nrCells, + void *buf) +{ + Transform2(nrCells, buf, map->types[READ_AS], map->types[STORED_AS]); +} +*/ + +#define illegal NULL +#define same CsfDummyConversion + +static const CSF_CONV_FUNC ConvTable[8][8] = { +/* ConvTable[source][destination] */ +/* INT1 , INT2 , INT4 , UINT1 , UINT2 , UINT4 , REAL4 , REAL8 */ +/* 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ind */ +/* 0x04 , 0x05 , 0x06 , 0x00 , 0x01 , 0x02 , 0x0A , 0x0B low-nib */ +{ same , illegal , INT1tINT4 , illegal , illegal , illegal , INT1tREAL4, INT1tREAL8}, /* INT1 */ +{ illegal , same , INT2tINT4 ,INT2tUINT1 , illegal , illegal , INT2tREAL4, INT2tREAL8}, /* INT2 */ +{ illegal , illegal , same ,INT4tUINT1 , illegal , illegal , INT4tREAL4, INT4tREAL8}, /* INT4 */ +{ illegal , illegal ,UINT1tINT4 , same ,UINT1tUINT2, illegal ,UINT1tREAL4,UINT1tREAL8}, /* UINT1 */ +{ illegal , illegal ,UINT2tINT4 , UINT2tUINT1 , same ,UINT2tUINT4,UINT2tREAL4,UINT2tREAL8}, /* UINT2 */ +{ illegal , illegal , illegal , illegal , illegal , same ,UINT4tREAL4,UINT4tREAL8}, /* UINT4 */ +{ illegal , illegal ,REAL4tINT4 ,REAL4tUINT1, illegal , illegal , same ,REAL4tREAL8}, /* REAL4 */ +{ illegal , illegal ,REAL8tINT4 ,REAL8tUINT1, illegal , illegal ,REAL8tREAL4, same } /* REAL8 */ +}; + +static const CSF_CONV_FUNC boolConvTable[8] = { +INT1tBoolean, INT2tBoolean, INT4tBoolean, +UINT1tBoolean, UINT2tBoolean, UINT4tBoolean, +REAL4tBoolean, REAL8tBoolean }; + + +static const char convTableIndex[12] = { + 3 /* UINT1 */, 4 /* UINT2 */, 5 /* UINT4 */, -1 /* 0x03 */, + 0 /* INT1 */, 1 /* INT2 */, 2 /* INT4 */, -1 /* 0x07 */, + -1 /* 0x08 */, -1 /* 0x09 */, 6 /* REAL4 */, 7 /* REAL8 */ +}; + +static CSF_CONV_FUNC ConvFuncBool(CSF_CR cr) +{ + PRECOND(CSF_UNIQ_CR_MASK(cr) < 12); + PRECOND(convTableIndex[CSF_UNIQ_CR_MASK(cr)] != -1); + + return boolConvTable[(int)convTableIndex[CSF_UNIQ_CR_MASK(cr)]]; +} + +static CSF_CONV_FUNC ConvFunc(CSF_CR destType, CSF_CR srcType) +{ + + PRECOND(CSF_UNIQ_CR_MASK(destType) < 12); + PRECOND(CSF_UNIQ_CR_MASK(srcType) < 12); + PRECOND(convTableIndex[CSF_UNIQ_CR_MASK(srcType)] != -1); + PRECOND(convTableIndex[CSF_UNIQ_CR_MASK(destType)] != -1); + /* don't complain on illegal, it can be attached + * to a app2file while there's no WRITE_MODE + * if it's an error then it's catched in RputSomeCells + */ + return + ConvTable[(int)convTableIndex[CSF_UNIQ_CR_MASK(srcType)]] + [(int)convTableIndex[CSF_UNIQ_CR_MASK(destType)]]; +} + +static int HasInFileCellReprType2(CSF_CR cr) +{ + char type2[12] = { + 1 /* UINT1 */, 0 /* UINT2 */, 0 /* UINT4 */, 0 /* 0x03 */, + 0 /* INT1 */, 0 /* INT2 */, 1 /* INT4 */, 0 /* 0x07 */, + 0 /* 0x08 */, 0 /* 0x09 */, 1 /* REAL4 */, 1 /* REAL8 */}; + + PRECOND(CSF_UNIQ_CR_MASK(cr) < 12); + + return (int)type2[CSF_UNIQ_CR_MASK(cr)]; +} + +/* set the cell representation the application will use + * RuseAs enables an application to use cell values + * in a different format than they are stored in the map. + * Cell values are converted when getting (Rget*-functions) and + * putting (Rput*-functions) cells if necessary. + * Thus no conversions are applied if cell representation and/or + * value scale already match. + * Any conversions between the version 2 cell representations, + * (CR_UINT1, CR_INT4, CR_REAL4 and CR_REAL8) is possible. + * Conversion from a non version 2 cell representation to a version + * 2 cell representation is only possible when you don't + * have write access to the cells. + * Conversion rules are exactly as described in K&R 2nd edition section A6. + * + * Two special conversions are possible if you don't + * have write access to the cells or if the in-file cell representation is + * UINT1: + * (1) VS_BOOLEAN: successive calls to the Rget*-functions returns the result of + * value != 0 + * , that is 0 or 1 in UINT1 format. The in-file cell representation can be + * anything, except if the value scale is VS_DIRECTION or VS_LDD. + * (2) VS_LDD: successive calls to the Rget*-functions returns the result of + * value % 10 + * , that is 1 to 9 in UINT1 format (0's are set to MV_UINT1). + * The in-file cell representation must be CR_UINT1 or CR_INT2 and + * the value scale must be VS_LDD, VS_CLASSIFIED or VS_NOTDETERMINED. + * + * NOTE + * that you must use Rmalloc() to get enough space for both the in-file and + * app cell representation. + * + * returns + * 0 if conversion obeys rules given here. 1 if not (conversions + * will not take place). + * + * Merrno + * CANT_USE_AS_BOOLEAN CANT_USE_WRITE_BOOLEAN + * CANT_USE_WRITE_LDD + * CANT_USE_AS_LDD + * CANT_USE_WRITE_OLDCR + * ILLEGAL_USE_TYPE + * + * EXAMPLE + * .so examples/maskdump.tr + */ + +int RuseAs( + MAP *m, /* map handle */ + CSF_CR useType) /* CR_UINT1,CR_INT4, CR_REAL4, CR_REAL8, VS_BOOLEAN or VS_LDD */ +{ + + CSF_CR inFileCR = RgetCellRepr(m); + CSF_VS inFileVS = RgetValueScale(m); + int hasInFileCellReprType2 = HasInFileCellReprType2(inFileCR); + + /* it is very unconvenient that both, VS and CR are taken as arguments + * for this function, and previously were used in the switch statement + * now, at least 'special conversions' handled first + */ + if((int)useType == VS_BOOLEAN){ + switch(inFileVS) { + case VS_LDD: + case VS_DIRECTION: { + M_ERROR(CANT_USE_AS_BOOLEAN); + return 1; + } + case VS_BOOLEAN: { + POSTCOND(inFileCR == CR_UINT1); + m->appCR = CR_UINT1; + m->file2app = same; + m->app2file = same; + return 0; + } + default: { + if((!hasInFileCellReprType2) && WRITE_ENABLE(m)) { + /* cellrepr is old one, we can't write that */ + M_ERROR(CANT_USE_WRITE_BOOLEAN); + return 1; + } + m->appCR = CR_UINT1; + m->file2app = ConvFuncBool(inFileCR); + m->app2file = ConvFunc(inFileCR, CR_UINT1); + return 0; + } + } + } + else if ((int)useType == VS_LDD){ + switch(inFileVS) { + case VS_LDD: { + POSTCOND(inFileCR == CR_UINT1); + m->appCR = CR_UINT1; + m->file2app = same; + m->app2file = same; + return 0; + } + case VS_CLASSIFIED: + case VS_NOTDETERMINED: { + switch(inFileCR) { + case CR_UINT1: { + m->appCR = CR_UINT1; + m->file2app = UINT1tLdd; + m->app2file = same; + return 0; + } + case CR_INT2: { + if(WRITE_ENABLE(m)) { + M_ERROR(CANT_USE_WRITE_LDD); + return 1; + } + m->appCR = CR_UINT1; + m->file2app = INT2tLdd; + m->app2file = illegal; + return 0; + } + default: { + /* This should never happen. + * Shut up compiler. + */ + assert(0); + } + } + } + default: { + M_ERROR(CANT_USE_AS_LDD); + return 1; + } + } + } + + switch(useType) { + case CR_UINT1: + case CR_INT4 : + case CR_REAL4: + case CR_REAL8: { + if((!hasInFileCellReprType2) && WRITE_ENABLE(m)) { + /* cellrepr is old one, we can't write that */ + M_ERROR(CANT_USE_WRITE_OLDCR); + return 1; + } + m->appCR = useType; + m->file2app = ConvFunc(useType, inFileCR); + m->app2file = ConvFunc(inFileCR, useType); + POSTCOND(m->file2app != NULL); + return 0; + } + default: { + M_ERROR(ILLEGAL_USE_TYPE); + return 1; + } + } + /* NOTREACHED */ +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/setangle.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/setangle.c new file mode 100644 index 000000000..d20278b38 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/setangle.c @@ -0,0 +1,43 @@ +#include +#include "csf.h" +#include "csfimpl.h" + +/* global header (opt.) and setangle's prototypes "" */ + + +/* headers of this app. modules called */ + +/***************/ +/* EXTERNALS */ +/***************/ + +/**********************/ +/* LOCAL DECLARATIONS */ +/**********************/ + +/*********************/ +/* LOCAL DEFINITIONS */ +/*********************/ + +/******************/ +/* IMPLEMENTATION */ +/******************/ + +/* Set the stuff in the header after header initialization (LIBRARY_INTERNAL) + * Implements some common code for Mopen, Rcreate and family: + * + * set the map angle cosine and sin in header + * these values are only used in the co-ordinate conversion + * routines. And since they do a counter clockwise rotation we + * take the sine and cosine of the negative angle. + * + * copy projection field into raster, so raster can act as an + * indepent structure, for transformations + */ +void CsfFinishMapInit( + MAP *m) /* map handle */ +{ + m->raster.angleCos = cos(-(m->raster.angle)); + m->raster.angleSin = sin(-(m->raster.angle)); + m->raster.projection = MgetProjection(m); +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/setmv.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/setmv.c new file mode 100644 index 000000000..d5e2a98ba --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/setmv.c @@ -0,0 +1,54 @@ +#include "csf.h" +#include "csfimpl.h" + +/* set a memory location to a missing value + * SetMV sets a memory location to a missing value + * (using the application cell representation). + * SetMV is quite slow but handy as in the example + * below. In general one should use assignment for + * integers (e.g. v = MV_UINT1) or the macro's + * SET_MV_REAL4 and SET_MV_REAL8 + * + * EXAMPLE + * .so examples/border.tr + */ +void SetMV( + const MAP *m, /* map handle */ + void *c) /* write-only. location set to missing value */ +{ + SetMVcellRepr(m->appCR, c); +} + +/* set a memory location to a missing value + * SetMVcellRepr sets a memory location to a missing value + * (using the application cell representation). + * In general one should use assignment for + * integers (e.g. v = MV_UINT1) or the macro's + * SET_MV_REAL4 and SET_MV_REAL8 + * + */ +void SetMVcellRepr( + CSF_CR cellRepr, /* cell representation, one of the CR_* constants */ + void *c) /* write-only. location set to missing value */ +{ + switch (cellRepr) + { + case CR_INT1 : *((INT1 *)c) = MV_INT1; + break; + case CR_INT2 : *((INT2 *)c) = MV_INT2; + break; + case CR_INT4 : *((INT4 *)c) = MV_INT4; + break; + case CR_UINT1 : *((UINT1 *)c) = MV_UINT1; + break; + case CR_UINT2 : *((UINT2 *)c) = MV_UINT2; + break; + case CR_REAL8 : + ((UINT4 *)c)[1] = MV_UINT4; + default : POSTCOND( + cellRepr == CR_REAL8 || + cellRepr == CR_REAL4 || + cellRepr == CR_UINT4 ); + *((UINT4 *)c) = MV_UINT4; + } +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/setvtmv.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/setvtmv.c new file mode 100644 index 000000000..7f71baa98 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/setvtmv.c @@ -0,0 +1,24 @@ +#include "csf.h" +#include "csfimpl.h" + +/* (LIBRARY_INTERNAL) + */ +void CsfSetVarTypeMV( CSF_VAR_TYPE *var, CSF_CR cellRepr) +{ +/* assuming unions are left-alligned */ + if(IS_SIGNED(cellRepr)) + switch(LOG_CELLSIZE(cellRepr)) + { + case 2 : *(INT4 *)var = MV_INT4; + break; + case 1 : *(INT2 *)var = MV_INT2; + break; + default: POSTCOND(LOG_CELLSIZE(cellRepr) == 0); + *(INT1 *)var = MV_INT1; + } + else + { + ((UINT4 *)var)[0] = MV_UINT4; + ((UINT4 *)var)[1] = MV_UINT4; + } +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/strconst.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/strconst.c new file mode 100644 index 000000000..6575338c2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/strconst.c @@ -0,0 +1,88 @@ +#include "csf.h" + +/* global header (opt.) and strconst's prototypes "" */ + + +/* headers of this app. modules called */ + +/***************/ +/* EXTERNALS */ +/***************/ + +/**********************/ +/* LOCAL DECLARATIONS */ +/**********************/ + +/*********************/ +/* LOCAL DEFINITIONS */ +/*********************/ +static char errorBuf[64]; +/******************/ +/* IMPLEMENTATION */ +/******************/ + +/* string with cell representation in plain english or acronym + * The string is in lower case except for INT1,INT2,UINT2 and UINT4 + *, they return an acronym. If cr is not + * a valid constant, for example 999, then the string is + * "999 is no CR constant". + * The "no constant" message is stored in a static buffer + * used by both RstrCellRepr and RstrValueScale. + * returns + * string with cell representation + */ +const char *RstrCellRepr(CSF_CR cr) /* cell representation constant */ +{ + switch(cr) { + case CR_INT1 : return "INT1"; + case CR_INT2 : return "INT2"; + case CR_INT4 : return "large integer"; + case CR_UINT1 : return "small integer"; + case CR_UINT2 : return "UINT2"; + case CR_UINT4 : return "UINT4"; + case CR_REAL4 : return "small real"; + case CR_REAL8 : return "large real"; + default : (void)sprintf(errorBuf,"%u is no CR constant", (unsigned)cr); + return errorBuf; + } +} + +/* string with value scale + * The string is in lower case. If cr is not + * a valid constant, for example 999, then the string is + * "999 is no VS constant". + * The "no constant" message is stored in a static buffer + * used by both RstrCellRepr and RstrValueScale. + * returns + * string with value scale in lower case + */ +const char *RstrValueScale(CSF_VS vs) /* value scale constant */ +{ + switch(vs) { + case VS_NOTDETERMINED : return "notdetermined"; + case VS_CLASSIFIED : return "classified"; + case VS_CONTINUOUS : return "continuous"; + case VS_BOOLEAN : return "boolean"; + case VS_NOMINAL : return "nominal"; + case VS_ORDINAL : return "ordinal"; + case VS_SCALAR : return "scalar"; + case VS_DIRECTION : return "directional"; + case VS_LDD : return "ldd"; + default : (void)sprintf(errorBuf,"%u is no VS constant", (unsigned)vs); + return errorBuf; + } +} + +/* string with projection + * The string is in lower case. + * string with name of projection + */ +const char *MstrProjection(CSF_PT p) /* projection constant, 0 is + * top to bottom. non-0 is bottom + * to top + */ +{ + return (p) ? + "y increases from bottom to top" + :"y increases from top to bottom"; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/strpad.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/strpad.c new file mode 100644 index 000000000..31aee5514 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/strpad.c @@ -0,0 +1,32 @@ +#include + +/* global header (opt.) and strpad's prototypes "" */ +#include "csf.h" +#include "csfimpl.h" + +/* headers of this app. modules called */ + +/***************/ +/* EXTERNALS */ +/***************/ + +/**********************/ +/* LOCAL DECLARATIONS */ +/**********************/ + +/*********************/ +/* LOCAL DEFINITIONS */ +/*********************/ + +/******************/ +/* IMPLEMENTATION */ +/******************/ +/* pad a string attribute with zeros (LIBRARY_INTERNAL) + */ +char *CsfStringPad(char *s, size_t reqSize) +{ + size_t l = strlen(s); + PRECOND(l <= reqSize); + (void)memset(s+l, '\0', reqSize-l); + return s; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/swapio.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/swapio.c new file mode 100644 index 000000000..6b80a68a6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/swapio.c @@ -0,0 +1,119 @@ +#include "csf.h" + +/* global header (opt.) and swapio's prototypes "" */ +#include "csfimpl.h" + + +/* headers of this app. modules called */ + +/***************/ +/* EXTERNALS */ +/***************/ + +/**********************/ +/* LOCAL DECLARATIONS */ +/**********************/ + +/* typedef for swap functions (LIBRARY_INTERNAL) + * typedef for swap functions + */ +typedef void (*SWAP)(unsigned char *buf, size_t n); +/*********************/ +/* LOCAL DEFINITIONS */ +/*********************/ + +/******************/ +/* IMPLEMENTATION */ +/******************/ + +/* check valid size of element (LIBRARY_INTERNAL) + */ +int CsfValidSize(size_t size) +{ + return size == 1 || size == 2 || size == 4 || size == 8; +} + +#ifdef DEBUG + size_t CsfWritePlain(void *buf, size_t size, size_t n, FILE *f) + { + PRECOND(CsfValidSize(size)); + return fwrite(buf, size, n, f); + } + size_t CsfReadPlain(void *buf, size_t size, size_t n, FILE *f) + { + PRECOND(CsfValidSize(size)); + return fread(buf, size, n, f); + } +#endif + +/* ARGSUSED */ +static void Swap1(unsigned char *buf, size_t n) +{ + /* do nothing */ + /* Shut up C compiler. */ + (void)buf; + (void)n; +} + +static void Swap2(unsigned char *b, size_t n) +{ + unsigned char tmp; + size_t i; + for (i=0; i < n; i++) + { + /* 01 => 10 */ + tmp = b[0]; b[0] = b[1]; b[1] = tmp; + b += 2; + } +} + +static void Swap4(unsigned char *b, size_t n) +{ + unsigned char tmp; + size_t i; + for (i=0; i < n; i++) + { + /* 0123 => 3210 */ + tmp = b[0]; b[0] = b[3]; b[3] = tmp; + tmp = b[1]; b[1] = b[2]; b[2] = tmp; + b += 4; + } +} + +static void Swap8(unsigned char *b, size_t n) +{ + unsigned char tmp; + size_t i; + for (i=0; i < n; i++) + { + /* 01234567 => 76543210 */ + tmp = b[0]; b[0] = b[7]; b[7] = tmp; + tmp = b[1]; b[1] = b[6]; b[6] = tmp; + tmp = b[2]; b[2] = b[5]; b[5] = tmp; + tmp = b[3]; b[3] = b[4]; b[4] = tmp; + b += 8; + } +} + +void CsfSwap(void *buf, size_t size, size_t n) +{ + SWAP l[9] = { NULL, Swap1, Swap2, NULL, Swap4, + NULL, NULL, NULL, Swap8}; + PRECOND(CsfValidSize(size)); + PRECOND(l[size] != NULL); + + l[size]((unsigned char *)buf,n); +} + +size_t CsfWriteSwapped(void *buf, size_t size, size_t n, FILE *f) +{ + CsfSwap(buf,size, n); + return fwrite(buf, size, n,f); +} + +size_t CsfReadSwapped(void *buf, size_t size, size_t n, FILE *f) +{ + size_t r = fread(buf, size, n,f); + CsfSwap(buf,size, r); + return r; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/trackmm.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/trackmm.c new file mode 100644 index 000000000..92389e7b4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/trackmm.c @@ -0,0 +1,34 @@ +#include "csf.h" +#include "csfimpl.h" + +/* global header (opt.) and trackmm's prototypes "" */ + + +/* headers of this app. modules called */ + +/***************/ +/* EXTERNALS */ +/***************/ + +/**********************/ +/* LOCAL DECLARATIONS */ +/**********************/ + +/*********************/ +/* LOCAL DEFINITIONS */ +/*********************/ + +/******************/ +/* IMPLEMENTATION */ +/******************/ + +/* disable automatic tracking of minimum and maximum value + * A call to RdontTrackMinMax disables the automatic tracking + * of the min/max value in succesive cell writes. + * If used, one must always + * use RputMinVal and RputMaxVal to set the correct values. + */ +void RdontTrackMinMax(MAP *m) /* map handle */ +{ + m->minMaxStatus = MM_DONTKEEPTRACK; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/vs2.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/vs2.c new file mode 100644 index 000000000..247fda9dd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/vs2.c @@ -0,0 +1,42 @@ +#include "csf.h" +#include "csfimpl.h" + + +/* headers of this app. modules called */ + +/***************/ +/* EXTERNALS */ +/***************/ + +/**********************/ +/* LOCAL DECLARATIONS */ +/**********************/ + +/*********************/ +/* LOCAL DEFINITIONS */ +/*********************/ + +/******************/ +/* IMPLEMENTATION */ +/******************/ + +/* test if valuescale/datatype is a CSF version 2 value scale + * RvalueScale2 tests if the map's value scale is a version + * 2 valuescale/datatype. + * returns + * 0 if the value is not in the above list, 1 if it does. + * + */ +int RvalueScale2( + CSF_VS vs) /* value scale. ALL OF BELOW are accepted */ +{ + switch(vs) { + case VS_LDD: + case VS_BOOLEAN: + case VS_NOMINAL: + case VS_ORDINAL: + case VS_SCALAR: + case VS_DIRECTION: return 1; + default : return 0; + } +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/vsdef.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/vsdef.c new file mode 100644 index 000000000..4f58de98d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/vsdef.c @@ -0,0 +1,45 @@ +#include "csf.h" +#include "csfimpl.h" + + +/* headers of this app. modules called */ + +/***************/ +/* EXTERNALS */ +/***************/ + +/**********************/ +/* LOCAL DECLARATIONS */ +/**********************/ + +/*********************/ +/* LOCAL DEFINITIONS */ +/*********************/ + +/******************/ +/* IMPLEMENTATION */ +/******************/ + +/* returns default cell representation of a value scale/cellRepr + * returns + * the appropriate cell representation constant (CR_something) + * or CR_UNDEFINED if vs is not a csf2 datatype + * + */ +CSF_CR RdefaultCellRepr( + CSF_VS vs) /* value scale */ +{ + switch(vs) { + case VS_LDD: + case VS_BOOLEAN: return CR_UINT1; + case VS_NOMINAL: + case VS_ORDINAL: return CR_INT4; + case VS_SCALAR: + case VS_DIRECTION: return CR_REAL4; + case VS_CLASSIFIED: return CR_UINT1; + case VS_CONTINUOUS: return CR_REAL4; + case VS_NOTDETERMINED: + default: + return CR_UNDEFINED; + } +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/vsis.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/vsis.c new file mode 100644 index 000000000..51391a519 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/vsis.c @@ -0,0 +1,91 @@ +#include "csf.h" +#include "csfimpl.h" + + +/* headers of this app. modules called */ + +/***************/ +/* EXTERNALS */ +/***************/ + +/**********************/ +/* LOCAL DECLARATIONS */ +/**********************/ + +/*********************/ +/* LOCAL DEFINITIONS */ +/*********************/ + +/******************/ +/* IMPLEMENTATION */ +/******************/ + +/* test value scale on compatibility CSF version 1 and 2 + * RvalueScaleIs tests if the map's value scale is compatible + * with a certain value scale. Here is list of compatible but + * different value scales: + * + * VS_NOTDETERMINED: always returns 0 + * + * VS_CLASSIFIED: VS_NOTDETERMINED + * + * VS_CONTINUOUS: VS_NOTDETERMINED + * + * VS_BOOLEAN: VS_CLASSIFIED, VS_NOTDETERMINED + * + * VS_NOMINAL: VS_CLASSIFIED, VS_NOTDETERMINED + * + * VS_ORDINAL: VS_CLASSIFIED, VS_NOTDETERMINED + * + * VS_LDD: VS_CLASSIFIED, VS_NOTDETERMINED (only if cell representation is + * UINT1 or INT2) + * + * VS_SCALAR: VS_CONTINUOUS, VS_NOTDETERMINED + * + * VS_DIRECTION: none + * + * returns + * 0 if not compatible or if vs argument is VS_NOTDETERMINED or in case of + * error, nonzero if + * compatible. + * + * Merrno + * BAD_VALUESCALE + * + * EXAMPLE + * .so examples/maskdump.tr + */ +int RvalueScaleIs( + const MAP *m, /* a version 1 map handle */ + CSF_VS vs) /* a version 2 value scale that is compatible with map's value + * scale yes or no? + */ +{ + CSF_VS mapsVS = RgetValueScale(m); + + if (vs == VS_NOTDETERMINED) + return 0; + + if (vs == mapsVS) + return 1; + + switch(vs) { + case VS_CLASSIFIED: return mapsVS == VS_NOTDETERMINED; + case VS_CONTINUOUS: return mapsVS == VS_NOTDETERMINED; + case VS_LDD: + { CSF_CR cr = RgetCellRepr(m); + if (cr != CR_UINT1 && cr != CR_INT2) + return 0; + } /* fall through */ + case VS_BOOLEAN: + case VS_NOMINAL: + case VS_ORDINAL: return mapsVS == VS_CLASSIFIED + || mapsVS == VS_NOTDETERMINED; + case VS_SCALAR: return mapsVS == VS_CONTINUOUS + || mapsVS == VS_NOTDETERMINED; + /* direction isn't compatible with anything */ + case VS_DIRECTION: return 0; + default : M_ERROR(BAD_VALUESCALE); + return 0; + } +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/vsvers.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/vsvers.c new file mode 100644 index 000000000..c60747bf4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/vsvers.c @@ -0,0 +1,47 @@ +#include "csf.h" + +/* global header (opt.) and vsvers's prototypes "" */ + + +/* headers of this app. modules called */ + +/***************/ +/* EXTERNALS */ +/***************/ + +/**********************/ +/* LOCAL DECLARATIONS */ +/**********************/ + +/*********************/ +/* LOCAL DEFINITIONS */ +/*********************/ + +/******************/ +/* IMPLEMENTATION */ +/******************/ + +/* get version nr of value scale + * returns + * 0 for illegal value scale, + * 1 version 1 value scale, + * 2 version 2 value scale + */ +int RgetValueScaleVersion( + const MAP *m) /* map handle */ +{ + UINT2 vs = RgetValueScale(m); + + switch(vs) { + case VS_CLASSIFIED : + case VS_CONTINUOUS : + case VS_NOTDETERMINED: return 1; + case VS_LDD : + case VS_BOOLEAN : + case VS_NOMINAL : + case VS_ORDINAL : + case VS_SCALAR : + case VS_DIRECTION: return 2; + default : return 0; + } +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/libcsf/wattrblk.c b/bazaar/plugin/gdal/frmts/pcraster/libcsf/wattrblk.c new file mode 100644 index 000000000..53b58f2f2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/libcsf/wattrblk.c @@ -0,0 +1,26 @@ +#include "csf.h" +#include "csfimpl.h" + +/* write an attribute control block (LIBRARY_INTERNAL) + * returns 0 if successful, + * 1 if seeking or writing failed + */ +int CsfWriteAttrBlock( + MAP *m, /* map handle */ + CSF_FADDR32 pos, /* file position where the block is written */ + ATTR_CNTRL_BLOCK *b) /* attribute control block to be written */ +{ + int i; + + if ( fseek(m->fp,(long) pos, SEEK_SET) ) + return 1; + + for(i=0; i < NR_ATTR_IN_BLOCK; i++) + if ( m->write(&(b->attrs[i].attrId), sizeof(UINT2),(size_t)1,m->fp) != 1 || + m->write(&(b->attrs[i].attrOffset),sizeof(CSF_FADDR32),(size_t)1,m->fp) != 1 || + m->write(&(b->attrs[i].attrSize), sizeof(UINT4),(size_t)1,m->fp) != 1 + ) + return 1; + + return m->write(&(b->next), sizeof(CSF_FADDR32),(size_t)1,m->fp) != 1; +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/makefile.vc b/bazaar/plugin/gdal/frmts/pcraster/makefile.vc new file mode 100644 index 000000000..e6b3a95d7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/makefile.vc @@ -0,0 +1,30 @@ + +# nmake makefile for gdal +# should go as makefile.vc in frmts/pcraster + +OBJ = pcrasterdataset.obj pcrastermisc.obj pcrasterutil.obj \ + pcrasterrasterband.obj + +!IFNDEF PCRASTER_EXTERNAL_LIB +EXTRAFLAGS = -Ilibcsf -DUSE_IN_GDAL +!ENDIF + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj \ + ..\o +!IFNDEF PCRASTER_EXTERNAL_LIB + cd libcsf + $(MAKE) /f makefile.vc + cd .. +!ENDIF + +clean: + -del *.obj + cd libcsf + $(MAKE) /f makefile.vc clean + cd .. + diff --git a/bazaar/plugin/gdal/frmts/pcraster/pcrasterdataset.cpp b/bazaar/plugin/gdal/frmts/pcraster/pcrasterdataset.cpp new file mode 100644 index 000000000..ef34e8278 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/pcrasterdataset.cpp @@ -0,0 +1,569 @@ +/****************************************************************************** + * $Id: pcrasterdataset.cpp 29187 2015-05-13 14:20:13Z kdejong $ + * + * Project: PCRaster Integration + * Purpose: PCRaster CSF 2.0 raster file driver + * Author: Kor de Jong, Oliver Schmitz + * + ****************************************************************************** + * Copyright (c) PCRaster owners + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef INCLUDED_IOSTREAM +#include +#define INCLUDED_IOSTREAM +#endif + +#ifndef INCLUDED_GDAL_PAM +#include "gdal_pam.h" +#define INCLUDED_GDAL_PAM +#endif + +#ifndef INCLUDED_CPL_STRING +#include "cpl_string.h" +#define INCLUDED_CPL_STRING +#endif + +CPL_CVSID("$Id: pcrasterdataset.cpp 29187 2015-05-13 14:20:13Z kdejong $"); + +// PCRaster library headers. + +// Module headers. +#ifndef INCLUDED_PCRASTERRASTERBAND +#include "pcrasterrasterband.h" +#define INCLUDED_PCRASTERRASTERBAND +#endif + +#ifndef INCLUDED_PCRASTERDATASET +#include "pcrasterdataset.h" +#define INCLUDED_PCRASTERDATASET +#endif + +#ifndef INCLUDED_PCRASTERUTIL +#include "pcrasterutil.h" +#define INCLUDED_PCRASTERUTIL +#endif + + + +/*! + \file + This file contains the implementation of the PCRasterDataset class. +*/ + + + +//------------------------------------------------------------------------------ +// DEFINITION OF STATIC PCRDATASET MEMBERS +//------------------------------------------------------------------------------ + +//! Tries to open the file described by \a info. +/*! + \param info Object with information about the dataset to open. + \return Pointer to newly allocated GDALDataset or 0. + + Returns 0 if the file could not be opened. +*/ +GDALDataset* PCRasterDataset::open( + GDALOpenInfo* info) +{ + PCRasterDataset* dataset = 0; + + if(info->fpL && info->nHeaderBytes >= static_cast(CSF_SIZE_SIG) && + strncmp((char*)info->pabyHeader, CSF_SIG, CSF_SIZE_SIG) == 0) { + MOPEN_PERM mode = info->eAccess == GA_Update + ? M_READ_WRITE + : M_READ; + + MAP* map = mapOpen(info->pszFilename, mode); + + if(map) { + dataset = new PCRasterDataset(map); + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information and overviews. */ +/* -------------------------------------------------------------------- */ + if( dataset ) + { + dataset->SetDescription( info->pszFilename ); + dataset->TryLoadXML(); + + dataset->oOvManager.Initialize( dataset, info->pszFilename ); + } + + return dataset; +} + + + +//! Writes a raster to \a filename as a PCRaster raster file. +/*! + \warning The source raster must have only 1 band. Currently, the values in + the source raster must be stored in one of the supported cell + representations (CR_UINT1, CR_INT4, CR_REAL4, CR_REAL8). + + The meta data item PCRASTER_VALUESCALE will be checked to see what value + scale to use. Otherwise a value scale is determined using + GDALType2ValueScale(GDALDataType). + + This function always writes rasters using CR_UINT1, CR_INT4 or CR_REAL4 + cell representations. +*/ +GDALDataset* PCRasterDataset::createCopy( + char const* filename, + GDALDataset* source, + CPL_UNUSED int strict, + CPL_UNUSED char** options, + GDALProgressFunc progress, + void* progressData) +{ + // Checks. + int nrBands = source->GetRasterCount(); + if(nrBands != 1) { + CPLError(CE_Failure, CPLE_NotSupported, + "PCRaster driver: Too many bands ('%d'): must be 1 band", nrBands); + return 0; + } + + GDALRasterBand* raster = source->GetRasterBand(1); + + // Create PCRaster raster. Determine properties of raster to create. + size_t nrRows = raster->GetYSize(); + size_t nrCols = raster->GetXSize(); + std::string string; + + // The in-file type of the cells. + CSF_CR fileCellRepresentation = GDALType2CellRepresentation( + raster->GetRasterDataType(), false); + + if(fileCellRepresentation == CR_UNDEFINED) { + CPLError(CE_Failure, CPLE_NotSupported, + "PCRaster driver: Cannot determine a valid cell representation"); + return 0; + } + + // The value scale of the values. + CSF_VS valueScale = VS_UNDEFINED; + if(source->GetMetadataItem("PCRASTER_VALUESCALE")) { + string = source->GetMetadataItem("PCRASTER_VALUESCALE"); + } + + valueScale = !string.empty() + ? string2ValueScale(string) + : GDALType2ValueScale(raster->GetRasterDataType()); + + if(valueScale == VS_UNDEFINED) { + CPLError(CE_Failure, CPLE_NotSupported, + "PCRaster driver: Cannot determine a valid value scale"); + return 0; + } + + CSF_PT const projection = PT_YDECT2B; + REAL8 const angle = 0.0; + REAL8 west = 0.0; + REAL8 north = 0.0; + REAL8 cellSize = 1.0; + + double transform[6]; + if(source->GetGeoTransform(transform) == CE_None) { + if(transform[2] == 0.0 && transform[4] == 0.0) { + west = static_cast(transform[0]); + north = static_cast(transform[3]); + cellSize = static_cast(transform[1]); + } + } + + // The in-memory type of the cells. + CSF_CR appCellRepresentation = CR_UNDEFINED; + appCellRepresentation = GDALType2CellRepresentation( + raster->GetRasterDataType(), true); + + if(appCellRepresentation == CR_UNDEFINED) { + CPLError(CE_Failure, CPLE_NotSupported, + "PCRaster driver: Cannot determine a valid cell representation"); + return 0; + } + + // Check whether value scale fits the cell representation. Adjust when + // needed. + valueScale = fitValueScale(valueScale, appCellRepresentation); + + // Create a raster with the in file cell representation. + MAP* map = Rcreate(filename, nrRows, nrCols, fileCellRepresentation, + valueScale, projection, west, north, angle, cellSize); + + if(!map) { + CPLError(CE_Failure, CPLE_OpenFailed, + "PCRaster driver: Unable to create raster %s", filename); + return 0; + } + + // Try to convert in app cell representation to the cell representation + // of the file. + if(RuseAs(map, appCellRepresentation)) { + CPLError(CE_Failure, CPLE_NotSupported, + "PCRaster driver: Cannot convert cells: %s", MstrError()); + Mclose(map); + return 0; + } + + int hasMissingValue; + double missingValue = raster->GetNoDataValue(&hasMissingValue); + + // This is needed to get my (KDJ) unit tests running. + // I am still uncertain why this is needed. If the input raster has float32 + // values and the output int32, than the missing value in the dataset object + // is not updated like the values are. + if(missingValue == ::missingValue(CR_REAL4) && + fileCellRepresentation == CR_INT4) { + missingValue = ::missingValue(fileCellRepresentation); + } + + // TODO conversie van INT2 naar INT4 ondersteunen. zie ruseas.c regel 503. + // conversie op r 159. + + // Create buffer for one row of values. + void* buffer = Rmalloc(map, nrCols); + + // Copy values from source to target. + CPLErr errorCode = CE_None; + for(size_t row = 0; errorCode == CE_None && row < nrRows; ++row) { + + // Get row from source. + if(raster->RasterIO(GF_Read, 0, row, nrCols, 1, buffer, nrCols, 1, + raster->GetRasterDataType(), 0, 0, NULL) != CE_None) { + CPLError(CE_Failure, CPLE_FileIO, + "PCRaster driver: Error reading from source raster"); + errorCode = CE_Failure; + break; + } + + // Upon reading values are converted to the + // right data type. This includes the missing value. If the source + // value cannot be represented in the target data type it is set to a + // missing value. + + if(hasMissingValue) { + alterToStdMV(buffer, nrCols, appCellRepresentation, missingValue); + } + + if(valueScale == VS_BOOLEAN) { + castValuesToBooleanRange(buffer, nrCols, appCellRepresentation); + } + + // Write row in target. + RputRow(map, row, buffer); + + if(!progress((row + 1) / (static_cast(nrRows)), 0, progressData)) { + CPLError(CE_Failure, CPLE_UserInterrupt, + "PCRaster driver: User terminated CreateCopy()"); + errorCode = CE_Failure; + break; + } + } + + Mclose(map); + map = 0; + + free(buffer); + buffer = 0; + + if( errorCode != CE_None ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + GDALPamDataset *poDS = (GDALPamDataset *) + GDALOpen( filename, GA_Update ); + + if( poDS ) + poDS->CloneInfo( source, GCIF_PAM_DEFAULT ); + + return poDS; +} + + + +//------------------------------------------------------------------------------ +// DEFINITION OF PCRDATASET MEMBERS +//------------------------------------------------------------------------------ + +//! Constructor. +/*! + \param map PCRaster map handle. It is ours to close. +*/ +PCRasterDataset::PCRasterDataset( + MAP* map) + + : GDALPamDataset(), + d_map(map), d_west(0.0), d_north(0.0), d_cellSize(0.0) + +{ + // Read header info. + nRasterXSize = RgetNrCols(d_map); + nRasterYSize = RgetNrRows(d_map); + d_west = static_cast(RgetXUL(d_map)); + d_north = static_cast(RgetYUL(d_map)); + d_cellSize = static_cast(RgetCellSize(d_map)); + d_cellRepresentation = RgetUseCellRepr(d_map); + CPLAssert(d_cellRepresentation != CR_UNDEFINED); + d_valueScale = RgetValueScale(d_map); + CPLAssert(d_valueScale != VS_UNDEFINED); + d_defaultNoDataValue = ::missingValue(d_cellRepresentation); + d_location_changed = false; + + // Create band information objects. + nBands = 1; + SetBand(1, new PCRasterRasterBand(this)); + + SetMetadataItem("PCRASTER_VALUESCALE", valueScale2String( + d_valueScale).c_str()); +} + + + +//! Destructor. +/*! + \warning The map given in the constructor is closed. +*/ +PCRasterDataset::~PCRasterDataset() +{ + FlushCache(); + Mclose(d_map); +} + + + +//! Sets projections info. +/*! + \param transform Array to fill. + + CSF 2.0 supports the notion of y coordinates which increase from north to + south. Support for this has been dropped and applications reading PCRaster + rasters will treat or already treat y coordinates as increasing from south + to north only. +*/ +CPLErr PCRasterDataset::GetGeoTransform(double* transform) +{ + // x = west + nrCols * cellsize + transform[0] = d_west; + transform[1] = d_cellSize; + transform[2] = 0.0; + + // y = north + nrRows * -cellsize + transform[3] = d_north; + transform[4] = 0.0; + transform[5] = -1.0 * d_cellSize; + + return CE_None; +} + + + +//! Returns the map handle. +/*! + \return Map handle. +*/ +MAP* PCRasterDataset::map() const +{ + return d_map; +} + + + +//! Returns the in-app cell representation. +/*! + \return cell representation + \warning This might not be the same representation as use to store the values in the file. + \sa valueScale() +*/ +CSF_CR PCRasterDataset::cellRepresentation() const +{ + return d_cellRepresentation; +} + + + +//! Returns the value scale of the data. +/*! + \return Value scale + \sa cellRepresentation() +*/ +CSF_VS PCRasterDataset::valueScale() const +{ + return d_valueScale; +} + + + +//! Returns the value of the missing value. +/*! + \return Missing value +*/ +double PCRasterDataset::defaultNoDataValue() const +{ + return d_defaultNoDataValue; +} + + +GDALDataset* PCRasterDataset::create( + const char* filename, + int nr_cols, + int nr_rows, + int nrBands, + GDALDataType gdalType, + char** papszParmList) +{ + // Checks + if(nrBands != 1){ + CPLError(CE_Failure, CPLE_NotSupported, + "PCRaster driver : " + "attempt to create dataset with too many bands (%d); " + "must be 1 band.\n", nrBands); + return NULL; + } + + int row_col_max = INT4_MAX - 1; + if(nr_cols > row_col_max){ + CPLError(CE_Failure, CPLE_NotSupported, + "PCRaster driver : " + "attempt to create dataset with too many columns (%d); " + "must be smaller than %d.", nr_cols, row_col_max); + return NULL; + } + + if(nr_rows > row_col_max){ + CPLError(CE_Failure, CPLE_NotSupported, + "PCRaster driver : " + "attempt to create dataset with too many rows (%d); " + "must be smaller than %d.", nr_rows, row_col_max); + return NULL; + } + + if(gdalType != GDT_Byte && + gdalType != GDT_Int32 && + gdalType != GDT_Float32){ + CPLError( CE_Failure, CPLE_AppDefined, + "PCRaster driver: " + "attempt to create dataset with an illegal data type (%s); " + "use either Byte, Int32 or Float32.", + GDALGetDataTypeName(gdalType)); + return NULL; + } + + // value scale must be specified by the user, + // determines cell representation + const char *valueScale = CSLFetchNameValue( + papszParmList,"PCRASTER_VALUESCALE"); + + if(valueScale == NULL){ + CPLError(CE_Failure, CPLE_AppDefined, + "PCRaster driver: value scale can not be determined; " + "specify PCRASTER_VALUESCALE."); + return NULL; + } + + + CSF_VS csf_value_scale = string2ValueScale(valueScale); + + if(csf_value_scale == VS_UNDEFINED){ + CPLError( CE_Failure, CPLE_AppDefined, + "PCRaster driver: value scale can not be determined (%s); " + "use either VS_BOOLEAN, VS_NOMINAL, VS_ORDINAL, VS_SCALAR, " + "VS_DIRECTION, VS_LDD", + valueScale); + return NULL; + } + + CSF_CR csf_cell_representation = GDALType2CellRepresentation(gdalType, false); + + // default values + REAL8 west = 0.0; + REAL8 north = 0.0; + REAL8 length = 1.0; + REAL8 angle = 0.0; + CSF_PT projection = PT_YDECT2B; + + // Create a new raster + MAP* map = Rcreate(filename, nr_rows, nr_cols, csf_cell_representation, + csf_value_scale, projection, west, north, angle, length); + + if(!map){ + CPLError(CE_Failure, CPLE_OpenFailed, + "PCRaster driver: Unable to create raster %s", filename); + return NULL; + } + + Mclose(map); + map = NULL; + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + GDALPamDataset *poDS = (GDALPamDataset *) + GDALOpen(filename, GA_Update); + + return poDS; +} + + +CPLErr PCRasterDataset::SetGeoTransform(double* transform) +{ + if((transform[2] != 0.0) || (transform[4] != 0.0)) { + CPLError(CE_Failure, CPLE_NotSupported, + "PCRaster driver: " + "rotated geotransformations are not supported."); + return CE_Failure; + } + + if(transform[1] != transform[5] * -1.0 ) { + CPLError(CE_Failure, CPLE_NotSupported, + "PCRaster driver: " + "only the same width and height for cells is supported." ); + return CE_Failure; + } + + d_west = transform[0]; + d_north = transform[3]; + d_cellSize = transform[1]; + d_location_changed = true; + + return CE_None; +} + + +bool PCRasterDataset::location_changed() const { + return d_location_changed; +} + + +//------------------------------------------------------------------------------ +// DEFINITION OF FREE OPERATORS +//------------------------------------------------------------------------------ + + + +//------------------------------------------------------------------------------ +// DEFINITION OF FREE FUNCTIONS +//------------------------------------------------------------------------------ diff --git a/bazaar/plugin/gdal/frmts/pcraster/pcrasterdataset.h b/bazaar/plugin/gdal/frmts/pcraster/pcrasterdataset.h new file mode 100644 index 000000000..e6651d4c4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/pcrasterdataset.h @@ -0,0 +1,181 @@ +/****************************************************************************** + * $Id: pcrasterdataset.h 29187 2015-05-13 14:20:13Z kdejong $ + * + * Project: PCRaster Integration + * Purpose: PCRaster CSF 2.0 raster file driver declarations. + * Author: Kor de Jong, Oliver Schmitz + * + ****************************************************************************** + * Copyright (c) PCRaster owners + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef INCLUDED_PCRASTERDATASET +#define INCLUDED_PCRASTERDATASET + +// Library headers. +#ifndef INCLUDED_GDAL_PAM +#include "gdal_pam.h" +#define INCLUDED_GDAL_PAM +#endif + +// PCRaster library headers. +#ifndef INCLUDED_CSF +#include "csf.h" +#define INCLUDED_CSF +#endif + +// Module headers. + +// namespace { + // PCRasterDataset declarations. +// } +namespace gdal { + class PCRasterDatasetTest; +} + + + +// namespace { + + + +//! This class specialises the GDALDataset class for PCRaster datasets. +/*! + PCRaster raster datasets are currently formatted by the CSF 2.0 data format. + A PCRasterDataset consists of one band. + + More info about PCRaster can be found at http://www.pcraster.nl and + http://pcraster.geog.uu.nl + + Additional documentation about this driver can be found in + frmts/frmts_various.html of the GDAL source code distribution. +*/ +class PCRasterDataset: public GDALPamDataset +{ + + friend class gdal::PCRasterDatasetTest; + +public: + + static GDALDataset* open (GDALOpenInfo* info); + + static GDALDataset* create (const char* filename, + int nr_cols, + int nr_rows, + int nrBands, + GDALDataType gdalType, + char** papszParmList); + + static GDALDataset* createCopy (char const* filename, + GDALDataset* source, + int strict, + char** options, + GDALProgressFunc progress, + void* progressData); + +private: + + //! CSF map structure. + MAP* d_map; + + //! Left coordinate of raster. + double d_west; + + //! Top coordinate of raster. + double d_north; + + //! Cell size. + double d_cellSize; + + //! Cell representation. + CSF_CR d_cellRepresentation; + + //! Value scale. + CSF_VS d_valueScale; + + //! No data value. + double d_defaultNoDataValue; + + bool d_location_changed; + + //! Assignment operator. NOT IMPLEMENTED. + PCRasterDataset& operator= (const PCRasterDataset&); + + //! Copy constructor. NOT IMPLEMENTED. + PCRasterDataset (const PCRasterDataset&); + +public: + + //---------------------------------------------------------------------------- + // CREATORS + //---------------------------------------------------------------------------- + + PCRasterDataset (MAP* map); + + /* virtual */ ~PCRasterDataset (); + + //---------------------------------------------------------------------------- + // MANIPULATORS + //---------------------------------------------------------------------------- + + CPLErr SetGeoTransform (double* transform); + + //---------------------------------------------------------------------------- + // ACCESSORS + //---------------------------------------------------------------------------- + + MAP* map () const; + + CPLErr GetGeoTransform (double* transform); + + CSF_CR cellRepresentation () const; + + CSF_VS valueScale () const; + + double defaultNoDataValue () const; + + bool location_changed () const; + +}; + + + +//------------------------------------------------------------------------------ +// INLINE FUNCTIONS +//------------------------------------------------------------------------------ + + + +//------------------------------------------------------------------------------ +// FREE OPERATORS +//------------------------------------------------------------------------------ + + + +//------------------------------------------------------------------------------ +// FREE FUNCTIONS +//------------------------------------------------------------------------------ + + + +// } // namespace + +#endif diff --git a/bazaar/plugin/gdal/frmts/pcraster/pcrastermisc.cpp b/bazaar/plugin/gdal/frmts/pcraster/pcrastermisc.cpp new file mode 100644 index 000000000..71f23ac2f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/pcrastermisc.cpp @@ -0,0 +1,85 @@ +/****************************************************************************** + * $Id: pcrastermisc.cpp 28862 2015-04-07 10:10:25Z kdejong $ + * + * Project: PCRaster Integration + * Purpose: PCRaster driver support functions. + * Author: Kor de Jong, Oliver Schmitz + * + ****************************************************************************** + * Copyright (c) PCRaster owners + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +// Library headers. +#ifndef INCLUDED_IOSTREAM +#include +#define INCLUDED_IOSTREAM +#endif + +#ifndef INCLUDED_STRING +#include +#define INCLUDED_STRING +#endif + +#ifndef INCLUDED_GDAL_PAM +#include "gdal_pam.h" +#define INCLUDED_GDAL_PAM +#endif + +// PCRaster library headers. + +// Module headers. +#ifndef INCLUDED_PCRASTERDATASET +#include "pcrasterdataset.h" +#define INCLUDED_PCRASTERDATASET +#endif + + + +CPL_C_START +void GDALRegister_PCRaster(void); +CPL_C_END + + + +void GDALRegister_PCRaster() +{ + if (! GDAL_CHECK_VERSION("PCRaster driver")) + return; + + if(!GDALGetDriverByName("PCRaster")) { + + GDALDriver* poDriver = new GDALDriver(); + + poDriver->SetDescription("PCRaster"); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "PCRaster Raster File"); + poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte Int32 Float32"); + poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "frmt_various.html#PCRaster"); + poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "map" ); + + poDriver->pfnOpen = PCRasterDataset::open; + poDriver->pfnCreate = PCRasterDataset::create; + poDriver->pfnCreateCopy = PCRasterDataset::createCopy; + + GetGDALDriverManager()->RegisterDriver(poDriver); + } +} diff --git a/bazaar/plugin/gdal/frmts/pcraster/pcrasterrasterband.cpp b/bazaar/plugin/gdal/frmts/pcraster/pcrasterrasterband.cpp new file mode 100644 index 000000000..2bcf08402 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/pcrasterrasterband.cpp @@ -0,0 +1,422 @@ +/****************************************************************************** + * $Id: pcrasterrasterband.cpp 29187 2015-05-13 14:20:13Z kdejong $ + * + * Project: PCRaster Integration + * Purpose: PCRaster raster band implementation. + * Author: Kor de Jong, Oliver Schmitz + * + ****************************************************************************** + * Copyright (c) PCRaster owners + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef INCLUDED_PCRASTERRASTERBAND +#include "pcrasterrasterband.h" +#define INCLUDED_PCRASTERRASTERBAND +#endif + +// PCRaster library headers. +#ifndef INCLUDED_CSF +#include "csf.h" +#define INCLUDED_CSF +#endif + +#ifndef INCLUDED_CSFIMPL +#include "csfimpl.h" +#define INCLUDED_CSFIMPL +#endif + +// Module headers. +#ifndef INCLUDED_PCRASTERDATASET +#include "pcrasterdataset.h" +#define INCLUDED_PCRASTERDATASET +#endif + +#ifndef INCLUDED_PCRASTERUTIL +#include "pcrasterutil.h" +#define INCLUDED_PCRASTERUTIL +#endif + + + +/*! + \file + This file contains the implementation of the PCRasterRasterBand class. +*/ + + + +//------------------------------------------------------------------------------ +// DEFINITION OF STATIC PCRRASTERBAND MEMBERS +//------------------------------------------------------------------------------ + + + +//------------------------------------------------------------------------------ +// DEFINITION OF PCRRASTERBAND MEMBERS +//------------------------------------------------------------------------------ + +//! Constructor. +/*! + \param dataset The dataset we are a part of. +*/ +PCRasterRasterBand::PCRasterRasterBand( + PCRasterDataset* dataset) + + : GDALPamRasterBand(), + d_dataset(dataset), + d_noDataValue(), + d_defaultNoDataValueOverridden(false), + d_create_in(GDT_Unknown) + +{ + this->poDS = dataset; + this->nBand = 1; + this->eDataType = cellRepresentation2GDALType(dataset->cellRepresentation()); + this->nBlockXSize = dataset->GetRasterXSize(); + this->nBlockYSize = 1; +} + + + +//! Destructor. +/*! +*/ +PCRasterRasterBand::~PCRasterRasterBand() +{ +} + + + +double PCRasterRasterBand::GetNoDataValue( + int* success) +{ + if(success) { + *success = 1; + } + + return d_defaultNoDataValueOverridden + ? d_noDataValue : d_dataset->defaultNoDataValue(); +} + + + +double PCRasterRasterBand::GetMinimum( + int* success) +{ + double result; + int isValid; + + switch(d_dataset->cellRepresentation()) { + // CSF version 2. ---------------------------------------------------------- + case CR_UINT1: { + UINT1 min; + isValid = RgetMinVal(d_dataset->map(), &min); + result = static_cast(min); + break; + } + case CR_INT4: { + INT4 min; + isValid = RgetMinVal(d_dataset->map(), &min); + result = static_cast(min); + break; + } + case CR_REAL4: { + REAL4 min; + isValid = RgetMinVal(d_dataset->map(), &min); + result = static_cast(min); + break; + } + case CR_REAL8: { + REAL8 min; + isValid = RgetMinVal(d_dataset->map(), &min); + result = static_cast(min); + break; + } + // CSF version 1. ---------------------------------------------------------- + case CR_INT1: { + INT1 min; + isValid = RgetMinVal(d_dataset->map(), &min); + result = static_cast(min); + break; + } + case CR_INT2: { + INT2 min; + isValid = RgetMinVal(d_dataset->map(), &min); + result = static_cast(min); + break; + } + case CR_UINT2: { + UINT2 min; + isValid = RgetMinVal(d_dataset->map(), &min); + result = static_cast(min); + break; + } + case CR_UINT4: { + UINT4 min; + isValid = RgetMinVal(d_dataset->map(), &min); + result = static_cast(min); + break; + } + default: { + result = 0.0; + isValid = 0; + break; + } + } + + if(success) { + *success = isValid ? 1 : 0; + } + + return result; +} + + + +double PCRasterRasterBand::GetMaximum( + int* success) +{ + double result; + int isValid; + + switch(d_dataset->cellRepresentation()) { + case CR_UINT1: { + UINT1 max; + isValid = RgetMaxVal(d_dataset->map(), &max); + result = static_cast(max); + break; + } + case CR_INT4: { + INT4 max; + isValid = RgetMaxVal(d_dataset->map(), &max); + result = static_cast(max); + break; + } + case CR_REAL4: { + REAL4 max; + isValid = RgetMaxVal(d_dataset->map(), &max); + result = static_cast(max); + break; + } + // CSF version 1. ---------------------------------------------------------- + case CR_INT1: { + INT1 max; + isValid = RgetMaxVal(d_dataset->map(), &max); + result = static_cast(max); + break; + } + case CR_INT2: { + INT2 max; + isValid = RgetMaxVal(d_dataset->map(), &max); + result = static_cast(max); + break; + } + case CR_UINT2: { + UINT2 max; + isValid = RgetMaxVal(d_dataset->map(), &max); + result = static_cast(max); + break; + } + case CR_UINT4: { + UINT4 max; + isValid = RgetMaxVal(d_dataset->map(), &max); + result = static_cast(max); + break; + } + default: { + result = 0.0; + isValid = 0; + break; + } + } + + if(success) { + *success = isValid ? 1 : 0; + } + + return result; +} + + + +CPLErr PCRasterRasterBand::IReadBlock( + CPL_UNUSED int nBlockXoff, + int nBlockYoff, + void* buffer) +{ + size_t nrCellsRead = RgetRow(d_dataset->map(), nBlockYoff, buffer); + + // Now we have raw values, missing values are set according to the CSF + // conventions. This means that floating points should not be evaluated. + // Since this is done by the GDal library we replace these with valid + // values. Non-MV values are not touched. + + // Replace in-file MV with in-app MV which may be different. + alterFromStdMV(buffer, nrCellsRead, d_dataset->cellRepresentation(), + GetNoDataValue()); + + return CE_None; +} + + +CPLErr PCRasterRasterBand::IRasterIO(GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, + int nYSize, void * pData, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg) +{ + if (eRWFlag == GF_Read){ + // read should just be the default + return GDALRasterBand::IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg); + } + else{ + // the datatype of the incoming data can be of different type than the + // cell representation used in the raster + // 'remember' the GDAL type to distinguish it later on in iWriteBlock + d_create_in = eBufType; + return GDALRasterBand::IRasterIO(GF_Write, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg); + + } +} + + + +CPLErr PCRasterRasterBand::IWriteBlock( + CPL_UNUSED int nBlockXoff, + int nBlockYoff, + void* source) +{ + CSF_VS valuescale = d_dataset->valueScale(); + + if(valuescale == VS_LDD) { + if((d_create_in == GDT_Byte) || (d_create_in == GDT_Float32) || (d_create_in == GDT_Float64)) { + CPLError(CE_Failure, CPLE_NotSupported, + "PCRaster driver: " + "conversion from %s to LDD not supported", + GDALGetDataTypeName(d_create_in)); + return CE_Failure; + } + } + + // set new location attributes to the header + if (d_dataset->location_changed()){ + REAL8 west = 0.0; + REAL8 north = 0.0; + REAL8 cellSize = 1.0; + double transform[6]; + if(this->poDS->GetGeoTransform(transform) == CE_None) { + if(transform[2] == 0.0 && transform[4] == 0.0) { + west = static_cast(transform[0]); + north = static_cast(transform[3]); + cellSize = static_cast(transform[1]); + } + } + (void)RputXUL(d_dataset->map(), west); + (void)RputYUL(d_dataset->map(), north); + (void)RputCellSize(d_dataset->map(), cellSize); + } + + int nr_cols = this->poDS->GetRasterXSize(); + + // new maps from create() set min/max to MV + // in case of reopening that map the min/max + // value tracking is disabled (MM_WRONGVALUE) + // reactivate it again to ensure that the output will + // get the correct values when values are written to map + d_dataset->map()->minMaxStatus = MM_KEEPTRACK; + + // allocate memory for row + void* buffer = Rmalloc(d_dataset->map(), nr_cols); + memcpy(buffer, source, nr_cols * 4); + + // convert source no_data values to MV in dest + switch(valuescale) { + case VS_BOOLEAN: + case VS_LDD: { + alterToStdMV(buffer, nr_cols, CR_UINT1, GetNoDataValue()); + break; + } + case VS_NOMINAL: + case VS_ORDINAL: { + alterToStdMV(buffer, nr_cols, CR_INT4, GetNoDataValue()); + break; + } + case VS_SCALAR: + case VS_DIRECTION: { + alterToStdMV(buffer, nr_cols, CR_REAL4, GetNoDataValue()); + break; + } + default: { + break; + } + } + + // conversion of values according to value scale + switch(valuescale) { + case VS_BOOLEAN: { + castValuesToBooleanRange(buffer, nr_cols, CR_UINT1); + break; + } + case VS_LDD: { + castValuesToLddRange(buffer, nr_cols); + break; + } + case VS_DIRECTION: { + castValuesToDirectionRange(buffer, nr_cols); + break; + } + default: { + break; + } + } + + RputRow(d_dataset->map(), nBlockYoff, buffer); + free(buffer); + + return CE_None; +} + + +CPLErr PCRasterRasterBand::SetNoDataValue(double nodata) +{ + d_noDataValue = nodata; + d_defaultNoDataValueOverridden = true; + + return CE_None; +} + + +//------------------------------------------------------------------------------ +// DEFINITION OF FREE OPERATORS +//------------------------------------------------------------------------------ + + + +//------------------------------------------------------------------------------ +// DEFINITION OF FREE FUNCTIONS +//------------------------------------------------------------------------------ diff --git a/bazaar/plugin/gdal/frmts/pcraster/pcrasterrasterband.h b/bazaar/plugin/gdal/frmts/pcraster/pcrasterrasterband.h new file mode 100644 index 000000000..471e8a68a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/pcrasterrasterband.h @@ -0,0 +1,145 @@ +/****************************************************************************** + * $Id: pcrasterrasterband.h 29187 2015-05-13 14:20:13Z kdejong $ + * + * Project: PCRaster Integration + * Purpose: PCRaster raster band declaration. + * Author: Kor de Jong, Oliver Schmitz + * + ****************************************************************************** + * Copyright (c) PCRaster owners + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef INCLUDED_PCRASTERRASTERBAND +#define INCLUDED_PCRASTERRASTERBAND + +// Library headers. +#ifndef INCLUDED_GDAL_PAM +#include "gdal_pam.h" +#define INCLUDED_GDAL_PAM +#endif + +// PCRaster library headers. + +// Module headers. + + + +// namespace { + // PCRasterRasterBand declarations. +// } +class PCRasterDataset; + + + +// namespace { + + + +//! This class specialises the GDALRasterBand class for PCRaster rasters. +/*! +*/ +class PCRasterRasterBand: public GDALPamRasterBand +{ + +private: + + //! Dataset this band is part of. For use only. + PCRasterDataset const* d_dataset; + + double d_noDataValue; + + bool d_defaultNoDataValueOverridden; + + GDALDataType d_create_in; + + virtual CPLErr IRasterIO (GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); + + //! Assignment operator. NOT IMPLEMENTED. + PCRasterRasterBand& operator= (const PCRasterRasterBand&); + + //! Copy constructor. NOT IMPLEMENTED. + PCRasterRasterBand (const PCRasterRasterBand&); + +protected: + + double GetNoDataValue (int* success=NULL); + + double GetMinimum (int* success); + + double GetMaximum (int* success); + +public: + + //---------------------------------------------------------------------------- + // CREATORS + //---------------------------------------------------------------------------- + + PCRasterRasterBand (PCRasterDataset* dataset); + + /* virtual */ ~PCRasterRasterBand (); + + //---------------------------------------------------------------------------- + // MANIPULATORS + //---------------------------------------------------------------------------- + + CPLErr IWriteBlock (CPL_UNUSED int nBlockXoff, + int nBlockYoff, + void* buffer); + + CPLErr SetNoDataValue (double no_data); + + //---------------------------------------------------------------------------- + // ACCESSORS + //---------------------------------------------------------------------------- + + CPLErr IReadBlock (int nBlockXoff, + int nBlockYoff, + void* buffer); + +}; + + + +//------------------------------------------------------------------------------ +// INLINE FUNCTIONS +//------------------------------------------------------------------------------ + + + +//------------------------------------------------------------------------------ +// FREE OPERATORS +//------------------------------------------------------------------------------ + + + +//------------------------------------------------------------------------------ +// FREE FUNCTIONS +//------------------------------------------------------------------------------ + + + +// } // namespace + +#endif diff --git a/bazaar/plugin/gdal/frmts/pcraster/pcrasterutil.cpp b/bazaar/plugin/gdal/frmts/pcraster/pcrasterutil.cpp new file mode 100644 index 000000000..802c7117f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/pcrasterutil.cpp @@ -0,0 +1,735 @@ +/****************************************************************************** + * $Id: pcrasterutil.cpp 28862 2015-04-07 10:10:25Z kdejong $ + * + * Project: PCRaster Integration + * Purpose: PCRaster driver support functions. + * Author: Kor de Jong, Oliver Schmitz + * + ****************************************************************************** + * Copyright (c) PCRaster owners + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef INCLUDED_IOSTREAM +#include +#define INCLUDED_IOSTREAM +#endif + +#ifndef INCLUDED_ALGORITHM +#include +#define INCLUDED_ALGORITHM +#endif + +#ifndef INCLUDED_FLOAT +#include +#define INCLUDED_FLOAT +#endif + +#ifndef INCLUDED_PCRTYPES +#include "pcrtypes.h" +#define INCLUDED_PCRTYPES +#endif + +#ifndef INCLUDED_PCRASTERUTIL +#include "pcrasterutil.h" +#define INCLUDED_PCRASTERUTIL +#endif + + + +//! Converts PCRaster data type to GDAL data type. +/*! + \param cellRepresentation Cell representation. + \return GDAL data type, GDT_Uknown if conversion is not possible. +*/ +GDALDataType cellRepresentation2GDALType( + CSF_CR cellRepresentation) +{ + GDALDataType type = GDT_Unknown; + + switch(cellRepresentation) { + // CSF version 2. ---------------------------------------------------------- + case CR_UINT1: { + type = GDT_Byte; + break; + } + case CR_INT4: { + type = GDT_Int32; + break; + } + case CR_REAL4: { + type = GDT_Float32; + break; + } + case CR_REAL8: { + type = GDT_Float64; + break; + } + // CSF version 1. ---------------------------------------------------------- + case CR_INT1: { + type = GDT_Byte; + break; + } + case CR_INT2: { + type = GDT_Int16; + break; + } + case CR_UINT2: { + type = GDT_UInt16; + break; + } + case CR_UINT4: { + type = GDT_UInt32; + break; + } + default: { + break; + } + } + + return type; +} + + + +CSF_VS string2ValueScale( + std::string const& string) +{ + CSF_VS valueScale = VS_UNDEFINED; + + // CSF version 2. ------------------------------------------------------------ + if(string == "VS_BOOLEAN") { + valueScale = VS_BOOLEAN; + } + else if(string == "VS_NOMINAL") { + valueScale = VS_NOMINAL; + } + else if(string == "VS_ORDINAL") { + valueScale = VS_ORDINAL; + } + else if(string == "VS_SCALAR") { + valueScale = VS_SCALAR; + } + else if(string == "VS_DIRECTION") { + valueScale = VS_DIRECTION; + } + else if(string == "VS_LDD") { + valueScale = VS_LDD; + } + // CSF version1. ------------------------------------------------------------- + else if(string == "VS_CLASSIFIED") { + valueScale = VS_CLASSIFIED; + } + else if(string == "VS_CONTINUOUS") { + valueScale = VS_CONTINUOUS; + } + else if(string == "VS_NOTDETERMINED") { + valueScale = VS_NOTDETERMINED; + } + + return valueScale; +} + + + +std::string valueScale2String( + CSF_VS valueScale) +{ + std::string result = "VS_UNDEFINED"; + + switch(valueScale) { + // CSF version 2. ---------------------------------------------------------- + case VS_BOOLEAN: { + result = "VS_BOOLEAN"; + break; + } + case VS_NOMINAL: { + result = "VS_NOMINAL"; + break; + } + case VS_ORDINAL: { + result = "VS_ORDINAL"; + break; + } + case VS_SCALAR: { + result = "VS_SCALAR"; + break; + } + case VS_DIRECTION: { + result = "VS_DIRECTION"; + break; + } + case VS_LDD: { + result = "VS_LDD"; + break; + } + // CSF version 1. ---------------------------------------------------------- + case VS_CLASSIFIED: { + result = "VS_CLASSIFIED"; + break; + } + case VS_CONTINUOUS: { + result = "VS_CONTINUOUS"; + break; + } + case VS_NOTDETERMINED: { + result = "VS_NOTDETERMINED"; + break; + } + default: { + break; + } + } + + return result; +} + + + +std::string cellRepresentation2String( + CSF_CR cellRepresentation) +{ + std::string result = "CR_UNDEFINED"; + + switch(cellRepresentation) { + + // CSF version 2. ---------------------------------------------------------- + case CR_UINT1: { + result = "CR_UINT1"; + break; + } + case CR_INT4: { + result = "CR_INT4"; + break; + } + case CR_REAL4: { + result = "CR_REAL4"; + break; + } + case CR_REAL8: { + result = "CR_REAL8"; + break; + } + // CSF version 1. ---------------------------------------------------------- + case CR_INT1: { + result = "CR_INT1"; + break; + } + case CR_INT2: { + result = "CR_INT2"; + break; + } + case CR_UINT2: { + result = "CR_UINT2"; + break; + } + case CR_UINT4: { + result = "CR_UINT4"; + break; + } + default: { + break; + } + } + + return result; +} + + + +//! Converts GDAL data type to PCRaster value scale. +/*! + \param type GDAL data type. + \return Value scale. + \warning \a type must be one of the standard numerical types and not + complex. + + GDAL byte is regarded as PCRaster boolean, integral as nominal and float + as scalar. This function will never return VS_LDD, VS_ORDINAL or + VS_DIRECTION. +*/ +CSF_VS GDALType2ValueScale( + GDALDataType type) +{ + CSF_VS valueScale = VS_UNDEFINED; + + switch(type) { + case GDT_Byte: { + // A foreign dataset is unlikely to support our LDD's. + valueScale = VS_BOOLEAN; + break; + } + case GDT_UInt16: + case GDT_UInt32: + case GDT_Int16: + case GDT_Int32: { + valueScale = VS_NOMINAL; + break; + } + case GDT_Float32: { + // A foreign dataset is unlikely to support our directional. + valueScale = VS_SCALAR; + break; + } + case GDT_Float64: { + // A foreign dataset is unlikely to support our directional. + valueScale = VS_SCALAR; + break; + } + default: { + CPLAssert(false); + break; + } + } + + return valueScale; +} + + + +//! Converts a GDAL type to a PCRaster cell representation. +/*! + \param type GDAL type. + \param exact Whether an exact match or a CSF2.0 supported cell + representation should be returned. + \return Cell representation. + \warning \a type must be one of the standard numerical types and not + complex. + + If exact is false, conversion to CSF2.0 types will take place. This is + useful for in file cell representations. If exact is true, and exact match + is made. This is useful for in app cell representations. + + If exact is false, this function always returns one of CR_UINT1, CR_INT4 + or CR_REAL4. +*/ +CSF_CR GDALType2CellRepresentation( + GDALDataType type, + bool exact) +{ + CSF_CR cellRepresentation = CR_UNDEFINED; + + switch(type) { + case GDT_Byte: { + cellRepresentation = CR_UINT1; + break; + } + case GDT_UInt16: { + cellRepresentation = exact ? CR_UINT2: CR_UINT1; + break; + } + case GDT_UInt32: { + cellRepresentation = exact ? CR_UINT4: CR_UINT1; + break; + } + case GDT_Int16: { + cellRepresentation = exact ? CR_INT2: CR_INT4; + break; + } + case GDT_Int32: { + cellRepresentation = CR_INT4; + break; + } + case GDT_Float32: { + cellRepresentation = CR_REAL4; + break; + } + case GDT_Float64: { + cellRepresentation = exact ? CR_REAL8: CR_REAL4; + break; + } + default: { + break; + } + } + + return cellRepresentation; +} + + + +//! Determines a missing value to use for data of \a cellRepresentation. +/*! + \param cellRepresentation Cell representation of the data. + \return Missing value. + \exception . + \sa . +*/ +double missingValue( + CSF_CR cellRepresentation) +{ + // It turns out that the missing values set here should be equal to the ones + // used in gdal's code to do data type conversion. Otherwise missing values + // in the source raster will be lost in the destination raster. It seems that + // when assigning new missing values gdal uses its own nodata values instead + // of the value set in the dataset. + + double missingValue = 0.0; + + switch(cellRepresentation) { + // CSF version 2. ---------------------------------------------------------- + case CR_UINT1: { + // missingValue = static_cast(MV_UINT1); + missingValue = UINT1(255); + break; + } + case CR_INT4: { + // missingValue = static_cast(MV_INT4); + missingValue = INT4(-2147483647); + break; + } + case CR_REAL4: { + // using breaks on gcc 2.95 + // CPLAssert(std::numeric_limits::is_iec559); + // missingValue = -std::numeric_limits::max(); + missingValue = -FLT_MAX; + break; + } + // CSF version 1. ---------------------------------------------------------- + case CR_INT1: { + missingValue = static_cast(MV_INT1); + break; + } + case CR_INT2: { + missingValue = static_cast(MV_INT2); + break; + } + case CR_UINT2: { + missingValue = static_cast(MV_UINT2); + break; + } + case CR_UINT4: { + missingValue = static_cast(MV_UINT4); + break; + } + default: { + CPLAssert(false); + break; + } + } + + return missingValue; +} + + + +//! Opens the raster in \a filename using mode \a mode. +/*! + \param filename Filename of raster to open. + \return Pointer to CSF MAP structure. + \exception . + \warning . + \sa . +*/ +MAP* mapOpen( + std::string const& filename, + MOPEN_PERM mode) +{ + MAP* map = Mopen(filename.c_str(), mode); + + return map; +} + + + +void alterFromStdMV( + void* buffer, + size_t size, + CSF_CR cellRepresentation, + double missingValue) +{ + switch(cellRepresentation) { + // CSF version 2. ---------------------------------------------------------- + case(CR_UINT1): { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterFromStdMV(static_cast(missingValue))); + break; + } + case(CR_INT4): { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterFromStdMV(static_cast(missingValue))); + break; + } + case(CR_REAL4): { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterFromStdMV(static_cast(missingValue))); + break; + } + case(CR_REAL8): { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterFromStdMV(static_cast(missingValue))); + break; + } + // CSF version 1. ---------------------------------------------------------- + case CR_INT1: { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterFromStdMV(static_cast(missingValue))); + break; + } + case CR_INT2: { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterFromStdMV(static_cast(missingValue))); + break; + } + case CR_UINT2: { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterFromStdMV(static_cast(missingValue))); + break; + } + case CR_UINT4: { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterFromStdMV(static_cast(missingValue))); + break; + } + default: { + CPLAssert(false); + break; + } + } +} + + + +void alterToStdMV( + void* buffer, + size_t size, + CSF_CR cellRepresentation, + double missingValue) +{ + switch(cellRepresentation) { + // CSF version 2. ---------------------------------------------------------- + case(CR_UINT1): { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterToStdMV(static_cast(missingValue))); + break; + } + case(CR_INT4): { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterToStdMV(static_cast(missingValue))); + break; + } + case(CR_REAL4): { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterToStdMV(static_cast(missingValue))); + break; + } + case(CR_REAL8): { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterToStdMV(static_cast(missingValue))); + break; + } + // CSF version 1. ---------------------------------------------------------- + case CR_INT1: { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterToStdMV(static_cast(missingValue))); + break; + } + case CR_INT2: { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterToStdMV(static_cast(missingValue))); + break; + } + case CR_UINT2: { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterToStdMV(static_cast(missingValue))); + break; + } + case CR_UINT4: { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + pcr::AlterToStdMV(static_cast(missingValue))); + break; + } + default: { + CPLAssert(false); + break; + } + } +} + + + +CSF_VS fitValueScale( + CSF_VS valueScale, + CSF_CR cellRepresentation) +{ + CSF_VS result = valueScale; + + switch(cellRepresentation) { + case CR_UINT1: { + switch(valueScale) { + case VS_LDD: { + result = VS_LDD; + break; + } + default: { + result = VS_BOOLEAN; + break; + } + } + break; + } + case CR_INT4: { + switch(valueScale) { + case VS_BOOLEAN: { + result = VS_NOMINAL; + break; + } + case VS_SCALAR: { + result = VS_ORDINAL; + break; + } + case VS_DIRECTION: { + result = VS_ORDINAL; + break; + } + case VS_LDD: { + result = VS_NOMINAL; + break; + } + default: { + result = valueScale; + break; + } + } + break; + } + case CR_REAL4: { + switch(valueScale) { + case VS_DIRECTION: { + result = VS_DIRECTION; + break; + } + default: { + result = VS_SCALAR; + break; + } + } + break; + } + default: { + break; + } + } + + return result; +} + + + +void castValuesToBooleanRange( + void* buffer, + size_t size, + CSF_CR cellRepresentation) +{ + switch(cellRepresentation) { + // CSF version 2. ---------------------------------------------------------- + case(CR_UINT1): { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + CastToBooleanRange()); + break; + } + case(CR_INT4): { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + CastToBooleanRange()); + break; + } + case(CR_REAL4): { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + CastToBooleanRange()); + break; + } + case(CR_REAL8): { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + CastToBooleanRange()); + break; + } + // CSF version 1. ---------------------------------------------------------- + case CR_INT1: { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + CastToBooleanRange()); + break; + } + case CR_INT2: { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + CastToBooleanRange()); + break; + } + case CR_UINT2: { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + CastToBooleanRange()); + break; + } + case CR_UINT4: { + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + CastToBooleanRange()); + break; + } + default: { + CPLAssert(false); + break; + } + } +} + + + +void castValuesToDirectionRange( + void* buffer, + size_t size) +{ + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + CastToDirection()); +} + + + +void castValuesToLddRange( + void* buffer, + size_t size) +{ + std::for_each(static_cast(buffer), + static_cast(buffer) + size, + CastToLdd()); +} \ No newline at end of file diff --git a/bazaar/plugin/gdal/frmts/pcraster/pcrasterutil.h b/bazaar/plugin/gdal/frmts/pcraster/pcrasterutil.h new file mode 100644 index 000000000..6ad55e60d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pcraster/pcrasterutil.h @@ -0,0 +1,189 @@ +/****************************************************************************** + * $Id: pcrasterutil.h 28862 2015-04-07 10:10:25Z kdejong $ + * + * Project: PCRaster Integration + * Purpose: PCRaster driver support declarations. + * Author: Kor de Jong, Oliver Schmitz + * + ****************************************************************************** + * Copyright (c) PCRaster owners + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +// Library headers. +#ifndef INCLUDED_STRING +#include +#define INCLUDED_STRING +#endif + +// PCRaster library headers. +#ifndef INCLUDED_CSF +#include "csf.h" +#define INCLUDED_CSF +#endif + +#ifndef INCLUDED_PCRTYPES +#include "pcrtypes.h" +#define INCLUDED_PCRTYPES +#endif + +// Module headers. +#ifndef INCLUDED_GDAL_PRIV +#include "gdal_priv.h" +#define INCLUDED_GDAL_PRIV +#endif + + +GDALDataType cellRepresentation2GDALType(CSF_CR cellRepresentation); + +CSF_VS string2ValueScale (const std::string& string); + +std::string valueScale2String (CSF_VS valueScale); + +std::string cellRepresentation2String(CSF_CR cellRepresentation); + +CSF_VS GDALType2ValueScale (GDALDataType type); + +/* +CSF_CR string2PCRasterCellRepresentation( + const std::string& string); + */ + +CSF_CR GDALType2CellRepresentation( + GDALDataType type, + bool exact); + +void* createBuffer (size_t size, + CSF_CR type); + +void deleteBuffer (void* buffer, + CSF_CR type); + +bool isContinuous (CSF_VS valueScale); + +double missingValue (CSF_CR type); + +void alterFromStdMV (void* buffer, + size_t size, + CSF_CR cellRepresentation, + double missingValue); + +void alterToStdMV (void* buffer, + size_t size, + CSF_CR cellRepresentation, + double missingValue); + +MAP* mapOpen (std::string const& filename, + MOPEN_PERM mode); + +CSF_VS fitValueScale (CSF_VS valueScale, + CSF_CR cellRepresentation); + +void castValuesToBooleanRange( + void* buffer, + size_t size, + CSF_CR cellRepresentation); + +void castValuesToDirectionRange( + void* buffer, + size_t size); + +void castValuesToLddRange(void* buffer, + size_t size); + +template +struct CastToBooleanRange +{ + void operator()(T& value) { + if(!pcr::isMV(value)) { + if(value != 0) { + value = T(value > T(0)); + } + else { + pcr::setMV(value); + } + } + } +}; + + + +template<> +struct CastToBooleanRange +{ + void operator()(UINT1& value) { + if(!pcr::isMV(value)) { + value = UINT1(value > UINT1(0)); + } + } +}; + + + +template<> +struct CastToBooleanRange +{ + void operator()(UINT2& value) { + if(!pcr::isMV(value)) { + value = UINT2(value > UINT2(0)); + } + } +}; + + + +template<> +struct CastToBooleanRange +{ + void operator()(UINT4& value) { + if(!pcr::isMV(value)) { + value = UINT4(value > UINT4(0)); + } + } +}; + + +struct CastToDirection +{ + void operator()(REAL4& value) { + REAL4 factor = M_PI / 180.0; + if(!pcr::isMV(value)) { + value = REAL4(value * factor); + } + } +}; + + + +struct CastToLdd +{ + void operator()(UINT1& value) { + if(!pcr::isMV(value)) { + if((value < 1) || (value > 9)) { + CPLError(CE_Warning, CPLE_IllegalArg, + "PCRaster driver: incorrect LDD value used, assigned MV instead"); + pcr::setMV(value); + } + else { + value = UINT1(value); + } + } + } +}; \ No newline at end of file diff --git a/bazaar/plugin/gdal/frmts/pdf/GNUmakefile b/bazaar/plugin/gdal/frmts/pdf/GNUmakefile new file mode 100644 index 000000000..208632c28 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/GNUmakefile @@ -0,0 +1,38 @@ +include ../../GDALmake.opt + +OBJ = pdfdataset.o pdfio.o pdfobject.o pdfcreatecopy.o ogrpdflayer.o pdfwritabledataset.o pdfreadvectors.o + +ifeq ($(HAVE_POPPLER),yes) +CPPFLAGS += -DHAVE_POPPLER +endif + +ifeq ($(POPPLER_HAS_OPTCONTENT),yes) +CPPFLAGS += -DPOPPLER_HAS_OPTCONTENT +endif + +ifeq ($(POPPLER_BASE_STREAM_HAS_TWO_ARGS),yes) +CPPFLAGS += -DPOPPLER_BASE_STREAM_HAS_TWO_ARGS +endif + +ifeq ($(POPPLER_0_20_OR_LATER),yes) +CPPFLAGS += -DPOPPLER_0_20_OR_LATER +endif + +ifeq ($(POPPLER_0_23_OR_LATER),yes) +CPPFLAGS += -DPOPPLER_0_23_OR_LATER +endif + +ifeq ($(HAVE_PODOFO),yes) +CPPFLAGS += -DHAVE_PODOFO +endif + +$(O_OBJ): pdfobject.h pdfio.h pdfcreatecopy.h gdal_pdf.h + +CPPFLAGS := $(CPPFLAGS) -I../vrt -I../mem -I../../ogr/ogrsf_frmts/mem $(POPPLER_INC) $(PODOFO_INC) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/pdf/frmt_pdf.html b/bazaar/plugin/gdal/frmts/pdf/frmt_pdf.html new file mode 100644 index 000000000..70d3d9bd7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/frmt_pdf.html @@ -0,0 +1,363 @@ + + + +Geospatial PDF + + + + +

    Geospatial PDF

    + +(Available for GDAL >= 1.8.0) +

    +GDAL supports reading Geospatial PDF documents, by extracting georeferencing information and rasterizing the data. +Non-geospatial PDF documents will also be recognized by the driver. +

    +Starting with GDAL >= 1.10.0, PDF documents can be created from other GDAL raster datasets, and OGR datasources can +also optionally be drawn on top of the raster layer (see OGR_* creation options in the below section). +

    +GDAL must be compiled with libpoppler support (GPL-licensed), and libpoppler itself must have been configured with +--enable-xpdf-headers so that the xpdf C++ headers are available. Note: the poppler C++ API isn't +stable, so the driver compilation may fail with too old or too recent poppler versions. +Successfully tested versions are poppler >= 0.12.X and <= 0.26.0. +

    +Starting with GDAL 1.9.0, as an alternative, the PDF driver can be compiled against libpodofo (LGPL-licensed) +to avoid the libpoppler dependency. This is sufficient to get the georeferencing and vector information. However, for +getting the imagery, the pdftoppm utility that comes with the poppler distribution must be available in the system PATH. +A temporary file will be generated in a directory determined by the following configuration options : CPL_TMPDIR, +TMPDIR or TEMP (in that order). If none are defined, the current directory will be used. +Successfully tested versions are libpodofo 0.8.4 and 0.9.1. +

    +The driver supports reading georeferencing encoded in either of the 2 current existing ways : according to the OGC +encoding best practice, or according to the Adobe Supplement to ISO 32000. +

    +Multipage documents are exposed as subdatasets, one subdataset par page of the document. +

    +The neatline (for OGC best practice) or the bounding box (Adobe style) will be reported as a NEATLINE metadata item, +so that it can be later used as a cutline for the warping algorithm. +

    +Starting with GDAL 1.9.0, XMP metadata can be extracted from the file, and will be +stored as XML raw content in the xml:XMP metadata domain. +

    +Starting with GDAL 1.10.0, additional metadata, such as found in USGS Topo PDF can be extracted from the file, and will be +stored as XML raw content in the EMBEDDED_METADATA metadata domain. +

    + +

    Vector support

    + +

    +Starting with GDAL 1.10, this driver can read and write geospatial PDF with vector features. +Vector read support requires linking to podofo or poppler libraries, but write support does not. + +The driver can read vector features encoded according to PDF's logical structure facilities (as described by "§10.6 - Logical Structure" of PDF spec), +or retrieve only vector geometries for other vector PDF files.

    + +

    Feature style support

    + +For write support, the driver has partial support for the style information attached to features, +encoded according to the OGR Feature Style Specification.

    + +The following tools are recognized : +

      +
    • For points, LABEL and SYMBOL.
    • +
    • For lines, PEN.
    • +
    • For polygons, PEN and BRUSH.
    • +
    + +The supported attributes for each tool are summed up in the following table :

    + + + + + + + + + + + + + + +
    ToolSupported attributesExample
    PENcolor, width and dash patternPEN(c:#FF0000,w:5px)
    BRUSHforeground colorBRUSH(fc:#0000FF)
    LABELtext (limited to ASCII strings), foreground color, size, x and y offsets, and angleLABEL(c:#000000,t:"Hello World!",s:5)
    SYMBOLid (ogr-sym-0 to ogr-sym-9, and filenames for raster symbols), color and sizeSYMBOL(c:#00FF00,id:"ogr-sym-3",s:10)
    SYMBOL(c:#00000080,id:"a_symbol.png")
    +

    + +Alpha values are supported for colors to control the opacity. If not specified, for BRUSH, it is set at 50% opaque.

    + +For SYMBOL with a bitmap name, only the alpha value of the color specified with 'c' is taken into account.

    + +

    Configuration options

    + +
      +
    • GDAL_PDF_DPI : To control the dimensions of the raster by specifying the DPI of the +rasterization with the Its default value is 150. Starting with GDAL 1.10, the driver +will make some effort to guess the DPI value either from a specific metadata item contained in some PDF files, or +from the raster images inside the PDF (in simple cases).
    • +
    • GDAL_PDF_NEATLINE : (GDAL >= 1.10.0 ) The name of the neatline to select (only available for geospatial PDF, encoded +according to OGC Best Practice). This defaults to "Map Layers" for USGS Topo PDF. If not found, the neatline that +covers the largest area.
    • + +

      +Starting with GDAL >= 1.10.0 and when GDAL is compiled against libpoppler, the following options are also +available : + +

    • GDAL_PDF_RENDERING_OPTIONS : a combination of VECTOR, RASTER and TEXT separated +by comma, to select whether vector, raster or text features should be rendered. If the option is not +specified, all features are rendered.
    • +
    • GDAL_PDF_BANDS = 3 or 4 : whether the PDF should be rendered as a RGB (3) or RGBA (4) image. +Defaults to 3.
    • +
    • GDAL_PDF_LAYERS = list of layers (comma separated) to turn ON (or "ALL" to turn all layers ON). +The layer names can be obtained by querying the LAYERS metadata domain. When this option is specified, +layers not explicitly listed will be turned off.
    • +
    • GDAL_PDF_LAYERS_OFF = list of layers (comma separated) to turn OFF. The layer names can be obtained by +querying the LAYERS metadata domain.
    • +
    + +

    LAYERS Metadata domain

    + +Starting with GDAL >= 1.10.0 and when GDAL is compiled against libpoppler, the LAYERS metadata domain can be queried +to retrieve layer names that can be turned ON or OFF. This is useful to know which values to specify for the +GDAL_PDF_LAYERS or GDAL_PDF_LAYERS_OFF configuration options.

    + +For example : +

    +$ gdalinfo ../autotest/gdrivers/data/adobe_style_geospatial.pdf -mdd LAYERS
    +
    +Driver: PDF/Geospatial PDF
    +Files: ../autotest/gdrivers/data/adobe_style_geospatial.pdf
    +[...]
    +Metadata (LAYERS):
    +  LAYER_00_NAME=New_Data_Frame
    +  LAYER_01_NAME=New_Data_Frame.Graticule
    +  LAYER_02_NAME=Layers
    +  LAYER_03_NAME=Layers.Measured_Grid
    +  LAYER_04_NAME=Layers.Graticule
    +[...]
    +
    +$ gdal_translate ../autotest/gdrivers/data/adobe_style_geospatial.pdf out.tif --config GDAL_PDF_LAYERS_OFF "New_Data_Frame"
    +
    +
    + +

    Restrictions

    + +The opening of a PDF document (to get the georeferencing) is fast, but at the first access to a raster block, +the whole page will be rasterized, which can be a slow operation. +

    +Note: starting with GDAL 1.10, some raster-only PDF files (such as some USGS GeoPDF files), that are regularly +tiled are exposed as tiled dataset by the GDAL PDF driver, and can be rendered with either the Poppler or the +Podofo backends. +

    +Only a few of the possible Datums available in the OGC best practice spec have been currently mapped +in the driver. Unrecognized datums will be considered as being based on the WGS84 ellipsoid. +

    +For documents that contain several neatlines in a page (insets), the georeferencing will be +extracted from the inset that has the largest area (in term of screen points). +

    + +

    Creation Issues (GDAL >= 1.10.0)

    + +

    PDF documents can be created from other GDAL raster datasets, that +have 1 band (graylevel or with color table), 3 bands (RGB) or 4 bands (RGBA).

    + +

    Georeferencing information will be written by default according to +the ISO32000 specification. It is also possible to write it according to the +OGC Best Practice conventions (but limited to a few datum and projection types).

    + +

    Note: PDF write support does not require linking to poppler or podofo.

    + +

    Creation Options

    + +
      +
    • COMPRESS=[NONE/DEFLATE/JPEG/JPEG2000]: +Set the compression to use for raster data. DEFLATE is the default.

    • + +
    • STREAM_COMPRESS=[NONE/DEFLATE]: +Set the compression to use for stream objects. DEFLATE is the default.

    • + +
    • DPI=value: +Set the DPI to use. Default to 72. May be automatically adjusted to higher value +so that page dimension does not exceed the 14400 maximum value (in user units) +allowed by Acrobat.

    • + +
    • PREDICTOR=[1/2]: +Only for DEFLATE compression. Might be set to 2 to use horizontal predictor that can make files smaller (but not always!). +1 is the default.

    • + +
    • JPEG_QUALITY=[1-100]: Set the JPEG quality when using JPEG compression. +A value of 100 is best quality (least compression), and 1 is worst quality (best compression). The default is 75.

    • + +
    • JPEG2000_DRIVER=[JP2KAK/JP2ECW/JP2OpenJPEG/JPEG2000]: +Set the JPEG2000 driver to use. If not specified, it will be searched in the previous list.

    • + +
    • TILED=YES: By default monoblock files are created. This +option can be used to force creation of tiled PDF files.

    • + +
    • BLOCKXSIZE=n: Sets tile width, defaults to 256.

    • + +
    • BLOCKYSIZE=n: Set tile height, defaults to 256.

    • + +
    • CLIPPING_EXTENT=xmin,ymin,xmax,ymax: Set the clipping extent for the main source dataset and for the +optional extra rasters. The coordinates are expressed in the units of the SRS of the dataset. +If not specified, the clipping extent is set to the extent of the main source dataset.

    • + +
    • LAYER_NAME=name: Name for layer where the raster is placed. If specified, the raster will be be placed +into a layer that can be toggled/un-toggled in the "Layer tree" of the PDF reader.

    • + +
    • EXTRA_RASTERS=dataset_ids: Comma separated list of georeferenced rasters to insert into +the page. Those rasters are displayed on top of the main source raster. They must be georeferenced in the same projection, +and they will be clipped to CLIPPING_EXTENT if it is specified (otherwise to the extent of the main source raster).

    • + +
    • EXTRA_RASTERS_LAYER_NAME=dataset_names: Comma separated list of name for each raster +specified in EXTRA_RASTERS. If specified, each extra raster will be be placed +into a layer, named with the specified value, that can be toggled/un-toggled in the "Layer tree" of the PDF reader. If not +specified, all the extra rasters will be placed in the default layer.

    • + +
    • EXTRA_STREAM=content: A PDF content stream to draw after the imagery, typically to add some text. It may refer +the /FTimesRoman and /FTimesBold fonts.

    • + +
    • EXTRA_IMAGES=image_file_name,x,y,scale[,link=some_url] (possibly repeated): A list of (ungeoreferenced) images to insert into +the page as extra content. This is useful to insert logos, legends, etc... +x and y are in user units from the lower left corner of the page, and the anchor point is the lower left pixel of the image. +scale is a magnifying ratio (use 1 if unsure). If link=some_url is specified, the image will be selectable and its selection +will cause a web browser to be opened on the specified URL.

    • + +
    • EXTRA_LAYER_NAME=name: Name for layer where the extra content specified with EXTRA_STREAM or EXTRA_IMAGES is placed. +If specified, the extra content will be be placed into a layer that can be toggled/un-toggled in the "Layer tree" of the PDF reader.

    • + +
    • MARGIN/LEFT_MARGIN/RIGHT_MARGIN/TOP_MARGIN/BOTTOM_MARGIN=value: Margin around image in user units.

    • + +
    • GEO_ENCODING=[NONE/ISO32000/OGC_BP/BOTH]: +Set the Geo encoding method to use. ISO32000 is the default.

    • + +
    • NEATLINE=polygon_definition_in_wkt: +Set the NEATLINE to use.

    • + +
    • XMP=[NONE/xml_xmp_content]: +By default, if the source dataset has data in the 'xml:XMP' metadata domain, this +data will be copied to the output PDF, unless this option is set to NONE. The XMP +xml string can also be directly set to this option.

    • + +
    • WRITE_INFO=[YES/NO]: +By default, the AUTHOR, CREATOR, CREATION_DATE, KEYWORDS, PRODUCER, SUBJECT and TITLE information +will be written into the PDF Info block from the corresponding metadata item from the +source dataset, or if not set, from the corresponding creation option. +If this option is set to NO, no information will be written.

    • + +
    • AUTHOR, CREATOR, CREATION_DATE, KEYWORDS, PRODUCER, + SUBJECT, TITLE : metadata that can be written into the PDF Info block. +Note: the format of the value for CREATION_DATE must be D:YYYYMMDDHHmmSSOHH'mm' +(e.g. D:20121122132447+02'00' for 22 nov 2012 13:24:47 GMT+02) +(see PDF Reference, version 1.7, page 160)

    • + +
    • OGR_DATASOURCE=name : Name of the OGR datasource to display on top of the raster layer.

    • + +
    • OGR_DISPLAY_FIELD=name : Name of the field (matching the name of a field from the OGR layer definition) +to use to build the label of features that appear in the "Model Tree" UI component of a well-known PDF viewer. +For example, if the OGR layer has a field called "ID", this can be used as the value for that option : features +in the "Model Tree" will be labelled from their value for the "ID" field. +If not specified, sequential generic labels will be used ("feature1", "feature2", etc... ).

    • + +
    • OGR_DISPLAY_LAYER_NAMES=names : Comma separated list of names to display for the OGR layers in the "Model Tree". +This option is useful to provide custom names, instead of OGR layer name that are used when this option is not specified. +When specified, the number of names should be the same as the number of OGR layers in the datasource (and in the order they appear when +listed by ogrinfo for example).

    • + +
    • OGR_WRITE_ATTRIBUTES=YES/NO : Whether to write attributes of OGR features. Defaults to YES

    • + +
    • OGR_LINK_FIELD=name : Name of the field (matching the name of a field from the OGR layer definition) +to use to cause clicks on OGR features to open a web browser on the URL specified by the field value.

    • + +
    • OFF_LAYERS=names: Comma separated list of layer names that should be initially hidden. +By default, all layers are visible. The layer names can come from LAYER_NAME (main raster layer name), EXTRA_RASTERS_LAYER_NAME, +EXTRA_LAYER_NAME and OGR_DISPLAY_LAYER_NAMES.

    • + +
    • EXCLUSIVE_LAYERS=names: +Comma separated list of layer names, such that only one of those layers can be visible at a time. +This is the behaviour of radio-buttons in a graphical user interface. + The layer names can come from LAYER_NAME (main raster layer name), EXTRA_RASTERS_LAYER_NAME, +EXTRA_LAYER_NAME and OGR_DISPLAY_LAYER_NAMES. +

    • + +
    • JAVASCRIPT=script: Javascript content to run at document opening. See +Acrobat(R) JavaScript Scripting Reference.

    • + +
    • JAVASCRIPT_FILE=script_filename: Name of Javascript file to embed and run at document opening. See +Acrobat(R) JavaScript Scripting Reference.

    • + +
    + +

    Update of existing files (GDAL >= 1.10.0)

    + +Existing PDF files (created or not with GDAL) can be opened in update mode in order to set or +update the following elements : +
      +
    • Geotransform and associated projection (with SetGeoTransform() and SetProjection())
    • +
    • GCPs (with SetGCPs())
    • +
    • Neatline (with SetMetadataItem("NEATLINE", polygon_definition_in_wkt))
    • +
    • Content of Info object (with SetMetadataItem(key, value) where key is one of AUTHOR, CREATOR, CREATION_DATE, KEYWORDS, PRODUCER, SUBJECT and TITLE)
    • +
    • xml:XMP metadata (with SetMetadata(md, "xml:XMP"))
    • +
    + +For geotransform or GCPs, the Geo encoding method used by default is ISO32000. OGC_BP can be +selected by setting the GDAL_PDF_GEO_ENCODING configuration option to OGC_BP.

    + +Updated elements are written at the end of the file, following the incremental update method described in +the PDF specification.

    + +

    Examples

    + +
      +
    • +Create a PDF from 2 rasters (main_raster and another_raster), such that main_raster is initially displayed, and they are exclusively displayed : +
      +gdal_translate -of PDF main_raster.tif my.pdf -co LAYER_NAME=main_raster
      +               -co EXTRA_RASTERS=another_raster.tif -co EXTRA_RASTERS_LAYER_NAME=another_raster
      +               -co OFF_LAYERS=another_raster -co EXCLUSIVE_LAYERS=main_raster,another_raster
      +
      +
    • + +
    • +Create of PDF with some JavaScript : +
      +gdal_translate -of PDF my.tif my.pdf -co JAVASCRIPT_FILE=script.js
      +
      + +where script.js is : +
      +button = app.alert({cMsg: 'This file was generated by GDAL. Do you want to visit its website ?', cTitle: 'Question', nIcon:2, nType:2});
      +if (button == 4) app.launchURL('http://gdal.org/');
      +
      +
    • +
    + +

    See also

    + +Specifications : + + + +

    +Libraries : + +

    + +

    +Samples : + +

    + + + diff --git a/bazaar/plugin/gdal/frmts/pdf/gdal_pdf.h b/bazaar/plugin/gdal/frmts/pdf/gdal_pdf.h new file mode 100644 index 000000000..2599d4691 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/gdal_pdf.h @@ -0,0 +1,372 @@ +/****************************************************************************** + * $Id$ + * + * Project: PDF Translator + * Purpose: Definition of classes for OGR .pdf driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _GDAL_PDF_H_INCLUDED +#define _GDAL_PDF_H_INCLUDED + +#ifdef HAVE_POPPLER + +/* hack for PDF driver and poppler >= 0.15.0 that defines incompatible "typedef bool GBool" */ +/* in include/poppler/goo/gtypes.h with the one defined in cpl_port.h */ +#define CPL_GBOOL_DEFINED +#define OGR_FEATURESTYLE_INCLUDE + +#include +#endif + +#include "gdal_pam.h" +#include "ogrsf_frmts.h" + +#include "ogr_mem.h" +#include "pdfobject.h" + +#include +#include + +/************************************************************************/ +/* OGRPDFLayer */ +/************************************************************************/ + +#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) + +class PDFDataset; + +class OGRPDFLayer : public OGRMemLayer +{ + PDFDataset *poDS; + int bGeomTypeSet; + int bGeomTypeMixed; + +public: + OGRPDFLayer(PDFDataset* poDS, + const char * pszName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eGeomType); + + void Fill( GDALPDFArray* poArray ); + + virtual int TestCapability( const char * ); +}; + +#endif + +/************************************************************************/ +/* OGRPDFWritableLayer */ +/************************************************************************/ + +class PDFWritableVectorDataset; + +class OGRPDFWritableLayer : public OGRMemLayer +{ + PDFWritableVectorDataset *poDS; + +public: + OGRPDFWritableLayer(PDFWritableVectorDataset* poDS, + const char * pszName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eGeomType); + + virtual int TestCapability( const char * ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); +}; + +/************************************************************************/ +/* GDALPDFTileDesc */ +/************************************************************************/ + +typedef struct +{ + GDALPDFObject* poImage; + double adfCM[6]; + double dfWidth; + double dfHeight; + int nBands; +} GDALPDFTileDesc; + +/************************************************************************/ +/* ==================================================================== */ +/* PDFDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class PDFRasterBand; +class PDFImageRasterBand; + +#ifdef HAVE_POPPLER +class ObjectAutoFree; +#endif + +#define MAX_TOKEN_SIZE 256 +#define TOKEN_STACK_SIZE 8 + +#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) + +class PDFDataset : public GDALPamDataset +{ + friend class PDFRasterBand; + friend class PDFImageRasterBand; + + CPLString osFilename; + CPLString osUserPwd; + char *pszWKT; + double dfDPI; + int bHasCTM; + double adfCTM[6]; + double adfGeoTransform[6]; + int bGeoTransformValid; + int nGCPCount; + GDAL_GCP *pasGCPList; + int bProjDirty; + int bNeatLineDirty; + + GDALMultiDomainMetadata oMDMD; + int bInfoDirty; + int bXMPDirty; + + int bUsePoppler; +#ifdef HAVE_POPPLER + PDFDoc* poDocPoppler; +#endif +#ifdef HAVE_PODOFO + PoDoFo::PdfMemDocument* poDocPodofo; + int bPdfToPpmFailed; +#endif + GDALPDFObject* poPageObj; + + int iPage; + + GDALPDFObject *poImageObj; + + double dfMaxArea; + int ParseLGIDictObject(GDALPDFObject* poLGIDict); + int ParseLGIDictDictFirstPass(GDALPDFDictionary* poLGIDict, int* pbIsBestCandidate = NULL); + int ParseLGIDictDictSecondPass(GDALPDFDictionary* poLGIDict); + int ParseProjDict(GDALPDFDictionary* poProjDict); + int ParseVP(GDALPDFObject* poVP, double dfMediaBoxWidth, double dfMediaBoxHeight); + int ParseMeasure(GDALPDFObject* poMeasure, + double dfMediaBoxWidth, double dfMediaBoxHeight, + double dfULX, double dfULY, double dfLRX, double dfLRY); + + int bTried; + GByte *pabyCachedData; + int nLastBlockXOff, nLastBlockYOff; + + OGRPolygon* poNeatLine; + + std::vector asTiles; /* in the order of the PDF file */ + std::vector aiTiles; /* in the order of blocks */ + int nBlockXSize; + int nBlockYSize; + int CheckTiledRaster(); + + void GuessDPI(GDALPDFDictionary* poPageDict, int* pnBands); + void FindXMP(GDALPDFObject* poObj); + void ParseInfo(GDALPDFObject* poObj); + +#ifdef HAVE_POPPLER + ObjectAutoFree* poCatalogObjectPoppler; +#endif + GDALPDFObject* poCatalogObject; + GDALPDFObject* GetCatalog(); + +#ifdef HAVE_POPPLER + void AddLayer(const char* pszLayerName, OptionalContentGroup* ocg); + void ExploreLayers(GDALPDFArray* poArray, int nRecLevel, CPLString osTopLayer = ""); + void FindLayers(); + void TurnLayersOnOff(); + CPLStringList osLayerList; + std::map oLayerOCGMap; +#endif + + CPLStringList osLayerWithRefList; + CPLString FindLayerOCG(GDALPDFDictionary* poPageDict, + const char* pszLayerName); + void FindLayersGeneric(GDALPDFDictionary* poPageDict); + + int bUseOCG; + + char **papszOpenOptions; + static const char* GetOption(char** papszOpenOptions, + const char* pszOptionName, + const char* pszDefaultVal); + + int nLayers; + OGRLayer **papoLayers; + + double dfPageWidth; + double dfPageHeight; + void PDFCoordsToSRSCoords(double x, double y, + double& X, double &Y); + + std::map oMapMCID; + void CleanupIntermediateResources(); + + std::map oMapOperators; + void InitMapOperators(); + + int bSetStyle; + + void ExploreTree(GDALPDFObject* poObj, int nRecLevel); + void ExploreContents(GDALPDFObject* poObj, GDALPDFObject* poResources); + + void ExploreContentsNonStructuredInternal(GDALPDFObject* poContents, + GDALPDFObject* poResources, + std::map& oMapPropertyToLayer); + void ExploreContentsNonStructured(GDALPDFObject* poObj, GDALPDFObject* poResources); + + int UnstackTokens(const char* pszToken, + int nRequiredArgs, + char aszTokenStack[TOKEN_STACK_SIZE][MAX_TOKEN_SIZE], + int& nTokenStackSize, + double* adfCoords); + OGRGeometry* ParseContent(const char* pszContent, + GDALPDFObject* poResources, + int bInitBDCStack, + int bMatchQ, + std::map& oMapPropertyToLayer, + OGRPDFLayer* poCurLayer); + OGRGeometry* BuildGeometry(std::vector& oCoords, + int bHasFoundFill, + int bHasMultiPart); + + int OpenVectorLayers(GDALPDFDictionary* poPageDict); + + public: + PDFDataset(); + virtual ~PDFDataset(); + + virtual const char* GetProjectionRef(); + virtual CPLErr GetGeoTransform( double * ); + + virtual CPLErr SetProjection(const char* pszWKTIn); + virtual CPLErr SetGeoTransform(double* padfGeoTransform); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + CPLErr ReadPixels( int nReqXOff, int nReqYOff, + int nReqXSize, int nReqYSize, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GSpacing nBandSpace, + GByte* pabyData ); + + virtual int GetLayerCount(); + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); + + OGRGeometry *GetGeometryFromMCID(int nMCID); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* PDFRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class PDFRasterBand : public GDALPamRasterBand +{ + friend class PDFDataset; + + CPLErr IReadBlockFromTile( int, int, void * ); + + public: + + PDFRasterBand( PDFDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); +}; + +#endif /* defined(HAVE_POPPLER) || defined(HAVE_PODOFO) */ + +/************************************************************************/ +/* PDFWritableDataset */ +/************************************************************************/ + +class PDFWritableVectorDataset : public GDALDataset +{ + char** papszOptions; + + int nLayers; + OGRLayer **papoLayers; + + int bModified; + + public: + PDFWritableVectorDataset(); + ~PDFWritableVectorDataset(); + + virtual OGRLayer* ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ); + + virtual OGRErr SyncToDisk(); + + virtual int GetLayerCount(); + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); + + static GDALDataset* Create( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ); + + void SetModified() { bModified = TRUE; } +}; + +GDALDataset* GDALPDFOpen(const char* pszFilename, GDALAccess eAccess); +CPLString PDFSanitizeLayerName(const char* pszName); + +#endif /* ndef _GDAL_PDF_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/pdf/makefile.vc b/bazaar/plugin/gdal/frmts/pdf/makefile.vc new file mode 100644 index 000000000..07867feb2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/makefile.vc @@ -0,0 +1,55 @@ + +OBJ = pdfdataset.obj pdfio.obj pdfobject.obj pdfcreatecopy.obj ogrpdflayer.obj pdfwritabledataset.obj pdfreadvectors.obj + +PLUGIN_DLL = gdal_PDF.dll + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF PDF_PLUGIN +OBJ = $(OBJ) ..\..\ogr\ogrsf_frmts\mem\ogrmemdatasource.obj ..\..\ogr\ogrsf_frmts\mem\ogrmemdriver.obj ..\..\ogr\ogrsf_frmts\mem\ogrmemlayer.obj +!ENDIF + +EXTRAFLAGS = -I..\vrt -I..\mem -I..\..\ogr\ogrsf_frmts\mem $(POPPLER_EXTRAFLAGS) $(PODOFO_EXTRAFLAGS) + +!IFDEF POPPLER_ENABLED +POPPLER_EXTRAFLAGS = $(POPPLER_CFLAGS) $(POPPLER_HAS_OPTCONTENT_FLAGS) $(POPPLER_BASE_STREAM_HAS_TWO_ARGS_FLAGS) $(POPPLER_0_20_OR_LATER_FLAGS) $(POPPLER_0_23_OR_LATER_FLAGS) -DHAVE_POPPLER + +!IFDEF POPPLER_HAS_OPTCONTENT +POPPLER_HAS_OPTCONTENT_FLAGS = -DPOPPLER_HAS_OPTCONTENT +!ENDIF + +!IFDEF POPPLER_BASE_STREAM_HAS_TWO_ARGS +POPPLER_BASE_STREAM_HAS_TWO_ARGS_FLAGS = -DPOPPLER_BASE_STREAM_HAS_TWO_ARGS +!ENDIF + +!IFDEF POPPLER_0_20_OR_LATER +POPPLER_0_20_OR_LATER_FLAGS = -DPOPPLER_0_20_OR_LATER +!ENDIF + +!IFDEF POPPLER_0_23_OR_LATER +POPPLER_0_23_OR_LATER_FLAGS = -DPOPPLER_0_23_OR_LATER +!ENDIF + +!ENDIF + +!IFDEF PODOFO_ENABLED +PODOFO_EXTRAFLAGS = $(PODOFO_CFLAGS) -DHAVE_PODOFO +!ENDIF + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(GDALLIB) $(POPPLER_LIBS) $(PODOFO_LIBS) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) \ No newline at end of file diff --git a/bazaar/plugin/gdal/frmts/pdf/ogrpdflayer.cpp b/bazaar/plugin/gdal/frmts/pdf/ogrpdflayer.cpp new file mode 100644 index 000000000..263494ffb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/ogrpdflayer.cpp @@ -0,0 +1,209 @@ +/****************************************************************************** + * $Id$ + * + * Project: PDF Translator + * Purpose: Implements OGRPDFDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pdf.h" + +CPL_CVSID("$Id$"); + +#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) + +/************************************************************************/ +/* OGRPDFLayer() */ +/************************************************************************/ + +OGRPDFLayer::OGRPDFLayer( PDFDataset* poDS, + const char * pszName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eGeomType ) : + OGRMemLayer(pszName, poSRS, eGeomType ) +{ + this->poDS = poDS; + bGeomTypeSet = FALSE; + bGeomTypeMixed = FALSE; +} + +/************************************************************************/ +/* Fill() */ +/************************************************************************/ + +void OGRPDFLayer::Fill( GDALPDFArray* poArray ) +{ + for(int i=0;iGetLength();i++) + { + GDALPDFObject* poFeatureObj = poArray->Get(i); + if (poFeatureObj->GetType() != PDFObjectType_Dictionary) + continue; + + GDALPDFObject* poA = poFeatureObj->GetDictionary()->Get("A"); + if (!(poA != NULL && poA->GetType() == PDFObjectType_Dictionary)) + continue; + + GDALPDFObject* poP = poA->GetDictionary()->Get("P"); + if (!(poP != NULL && poP->GetType() == PDFObjectType_Array)) + continue; + + GDALPDFObject* poK = poFeatureObj->GetDictionary()->Get("K"); + int nK = -1; + if (poK != NULL && poK->GetType() == PDFObjectType_Int) + nK = poK->GetInt(); + + GDALPDFArray* poPArray = poP->GetArray(); + int j; + for(j = 0;jGetLength();j++) + { + GDALPDFObject* poKV = poPArray->Get(j); + if (poKV->GetType() == PDFObjectType_Dictionary) + { + GDALPDFObject* poN = poKV->GetDictionary()->Get("N"); + GDALPDFObject* poV = poKV->GetDictionary()->Get("V"); + if (poN != NULL && poN->GetType() == PDFObjectType_String && + poV != NULL) + { + int nIdx = GetLayerDefn()->GetFieldIndex( poN->GetString().c_str() ); + OGRFieldType eType = OFTString; + if (poV->GetType() == PDFObjectType_Int) + eType = OFTInteger; + else if (poV->GetType() == PDFObjectType_Real) + eType = OFTReal; + if (nIdx < 0) + { + OGRFieldDefn oField(poN->GetString().c_str(), eType); + CreateField(&oField); + } + else if (GetLayerDefn()->GetFieldDefn(nIdx)->GetType() != eType && + GetLayerDefn()->GetFieldDefn(nIdx)->GetType() != OFTString) + { + OGRFieldDefn oField(poN->GetString().c_str(), OFTString); + AlterFieldDefn( nIdx, &oField, ALTER_TYPE_FLAG ); + } + } + } + } + + OGRFeature* poFeature = new OGRFeature(GetLayerDefn()); + for(j = 0;jGetLength();j++) + { + GDALPDFObject* poKV = poPArray->Get(j); + if (poKV->GetType() == PDFObjectType_Dictionary) + { + GDALPDFObject* poN = poKV->GetDictionary()->Get("N"); + GDALPDFObject* poV = poKV->GetDictionary()->Get("V"); + if (poN != NULL && poN->GetType() == PDFObjectType_String && + poV != NULL) + { + if (poV->GetType() == PDFObjectType_String) + poFeature->SetField(poN->GetString().c_str(), poV->GetString().c_str()); + else if (poV->GetType() == PDFObjectType_Int) + poFeature->SetField(poN->GetString().c_str(), poV->GetInt()); + else if (poV->GetType() == PDFObjectType_Real) + poFeature->SetField(poN->GetString().c_str(), poV->GetReal()); + } + } + } + + if (nK >= 0) + { + OGRGeometry* poGeom = poDS->GetGeometryFromMCID(nK); + if (poGeom) + { + poGeom->assignSpatialReference(GetSpatialRef()); + poFeature->SetGeometry(poGeom); + } + } + + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if( !bGeomTypeMixed && poGeom != NULL ) + { + if (!bGeomTypeSet) + { + bGeomTypeSet = TRUE; + GetLayerDefn()->SetGeomType(poGeom->getGeometryType()); + } + else if (GetLayerDefn()->GetGeomType() != poGeom->getGeometryType()) + { + bGeomTypeMixed = TRUE; + GetLayerDefn()->SetGeomType(wkbUnknown); + } + } + ICreateFeature(poFeature); + + delete poFeature; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPDFLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + else + return OGRMemLayer::TestCapability(pszCap); +} + +#endif /* defined(HAVE_POPPLER) || defined(HAVE_PODOFO) */ + +/************************************************************************/ +/* OGRPDFWritableLayer() */ +/************************************************************************/ + +OGRPDFWritableLayer::OGRPDFWritableLayer( PDFWritableVectorDataset* poDS, + const char * pszName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eGeomType ) : + OGRMemLayer(pszName, poSRS, eGeomType ) +{ + this->poDS = poDS; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRPDFWritableLayer::ICreateFeature( OGRFeature *poFeature ) +{ + poDS->SetModified(); + return OGRMemLayer::ICreateFeature(poFeature); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPDFWritableLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + else + return OGRMemLayer::TestCapability(pszCap); +} diff --git a/bazaar/plugin/gdal/frmts/pdf/pdfcreatecopy.cpp b/bazaar/plugin/gdal/frmts/pdf/pdfcreatecopy.cpp new file mode 100644 index 000000000..773b8e9f6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/pdfcreatecopy.cpp @@ -0,0 +1,4740 @@ +/****************************************************************************** + * $Id: pdfcreatecopy.cpp 28780 2015-03-26 12:29:35Z rouault $ + * + * Project: PDF driver + * Purpose: GDALDataset driver for PDF dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pdf.h" +#include "pdfcreatecopy.h" + +#include "cpl_vsi_virtual.h" +#include "cpl_conv.h" +#include "cpl_error.h" +#include "ogr_spatialref.h" +#include "ogr_geometry.h" +#include "vrtdataset.h" + +#include "pdfobject.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/* Cf PDF reference v1.7, Appendix C, page 993 */ +#define MAXIMUM_SIZE_IN_UNITS 14400 + +CPL_CVSID("$Id: pdfcreatecopy.cpp 28780 2015-03-26 12:29:35Z rouault $"); + +#define PIXEL_TO_GEO_X(x,y) adfGeoTransform[0] + x * adfGeoTransform[1] + y * adfGeoTransform[2] +#define PIXEL_TO_GEO_Y(x,y) adfGeoTransform[3] + x * adfGeoTransform[4] + y * adfGeoTransform[5] + +class GDALFakePDFDataset : public GDALDataset +{ + public: + GDALFakePDFDataset() {} +}; + +/************************************************************************/ +/* Init() */ +/************************************************************************/ + +void GDALPDFWriter::Init() +{ + nPageResourceId = 0; + nStructTreeRootId = 0; + nCatalogId = nCatalogGen = 0; + bInWriteObj = FALSE; + nInfoId = nInfoGen = 0; + nXMPId = nXMPGen = 0; + nNamesId = 0; + + nLastStartXRef = 0; + nLastXRefSize = 0; + bCanUpdate = FALSE; +} + +/************************************************************************/ +/* GDALPDFWriter() */ +/************************************************************************/ + +GDALPDFWriter::GDALPDFWriter(VSILFILE* fpIn, int bAppend) : fp(fpIn) +{ + Init(); + + if (!bAppend) + { + VSIFPrintfL(fp, "%%PDF-1.6\n"); + + /* See PDF 1.7 reference, page 92. Write 4 non-ASCII bytes to indicate that the content will be binary */ + VSIFPrintfL(fp, "%%%c%c%c%c\n", 0xFF, 0xFF, 0xFF, 0xFF); + + nPageResourceId = AllocNewObject(); + nCatalogId = AllocNewObject(); + } +} + +/************************************************************************/ +/* ~GDALPDFWriter() */ +/************************************************************************/ + +GDALPDFWriter::~GDALPDFWriter() +{ + Close(); +} + +/************************************************************************/ +/* ParseIndirectRef() */ +/************************************************************************/ + +static int ParseIndirectRef(const char* pszStr, int& nNum, int &nGen) +{ + while(*pszStr == ' ') + pszStr ++; + + nNum = atoi(pszStr); + while(*pszStr >= '0' && *pszStr <= '9') + pszStr ++; + if (*pszStr != ' ') + return FALSE; + + while(*pszStr == ' ') + pszStr ++; + + nGen = atoi(pszStr); + while(*pszStr >= '0' && *pszStr <= '9') + pszStr ++; + if (*pszStr != ' ') + return FALSE; + + while(*pszStr == ' ') + pszStr ++; + + return *pszStr == 'R'; +} + +/************************************************************************/ +/* ParseTrailerAndXRef() */ +/************************************************************************/ + +int GDALPDFWriter::ParseTrailerAndXRef() +{ + VSIFSeekL(fp, 0, SEEK_END); + char szBuf[1024+1]; + vsi_l_offset nOffset = VSIFTellL(fp); + + if (nOffset > 128) + nOffset -= 128; + else + nOffset = 0; + + /* Find startxref section */ + VSIFSeekL(fp, nOffset, SEEK_SET); + int nRead = (int) VSIFReadL(szBuf, 1, 128, fp); + szBuf[nRead] = 0; + if (nRead < 9) + return FALSE; + + const char* pszStartXRef = NULL; + int i; + for(i = nRead - 9; i>= 0; i --) + { + if (strncmp(szBuf + i, "startxref", 9) == 0) + { + pszStartXRef = szBuf + i; + break; + } + } + if (pszStartXRef == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find startxref"); + return FALSE; + } + pszStartXRef += 9; + while(*pszStartXRef == '\r' || *pszStartXRef == '\n') + pszStartXRef ++; + if (*pszStartXRef == '\0') + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find startxref"); + return FALSE; + } + + nLastStartXRef = CPLScanUIntBig(pszStartXRef,16); + + /* Skip to beginning of xref section */ + VSIFSeekL(fp, nLastStartXRef, SEEK_SET); + + /* And skip to trailer */ + const char* pszLine; + while( (pszLine = CPLReadLineL(fp)) != NULL) + { + if (strncmp(pszLine, "trailer", 7) == 0) + break; + } + + if( pszLine == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find trailer"); + return FALSE; + } + + /* Read trailer content */ + nRead = (int) VSIFReadL(szBuf, 1, 1024, fp); + szBuf[nRead] = 0; + + /* Find XRef size */ + const char* pszSize = strstr(szBuf, "/Size"); + if (pszSize == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find trailer /Size"); + return FALSE; + } + pszSize += 5; + while(*pszSize == ' ') + pszSize ++; + nLastXRefSize = atoi(pszSize); + + /* Find Root object */ + const char* pszRoot = strstr(szBuf, "/Root"); + if (pszRoot == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find trailer /Root"); + return FALSE; + } + pszRoot += 5; + while(*pszRoot == ' ') + pszRoot ++; + + if (!ParseIndirectRef(pszRoot, nCatalogId, nCatalogGen)) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse trailer /Root"); + return FALSE; + } + + /* Find Info object */ + const char* pszInfo = strstr(szBuf, "/Info"); + if (pszInfo != NULL) + { + pszInfo += 5; + while(*pszInfo == ' ') + pszInfo ++; + + if (!ParseIndirectRef(pszInfo, nInfoId, nInfoGen)) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse trailer /Info"); + nInfoId = nInfoGen = 0; + } + } + + VSIFSeekL(fp, 0, SEEK_END); + + return TRUE; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +void GDALPDFWriter::Close() +{ + if (fp) + { + CPLAssert(!bInWriteObj); + if (nPageResourceId) + { + WritePages(); + WriteXRefTableAndTrailer(); + } + else if (bCanUpdate) + { + WriteXRefTableAndTrailer(); + } + VSIFCloseL(fp); + } + fp = NULL; +} + +/************************************************************************/ +/* UpdateProj() */ +/************************************************************************/ + +void GDALPDFWriter::UpdateProj(GDALDataset* poSrcDS, + double dfDPI, + GDALPDFDictionaryRW* poPageDict, + int nPageNum, int nPageGen) +{ + bCanUpdate = TRUE; + if ((int)asXRefEntries.size() < nLastXRefSize - 1) + asXRefEntries.resize(nLastXRefSize - 1); + + int nViewportId = 0; + int nLGIDictId = 0; + + CPLAssert(nPageNum != 0); + CPLAssert(poPageDict != NULL); + + PDFMargins sMargins = {0, 0, 0, 0}; + + const char* pszGEO_ENCODING = CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000"); + if (EQUAL(pszGEO_ENCODING, "ISO32000") || EQUAL(pszGEO_ENCODING, "BOTH")) + nViewportId = WriteSRS_ISO32000(poSrcDS, dfDPI * USER_UNIT_IN_INCH, NULL, &sMargins, TRUE); + if (EQUAL(pszGEO_ENCODING, "OGC_BP") || EQUAL(pszGEO_ENCODING, "BOTH")) + nLGIDictId = WriteSRS_OGC_BP(poSrcDS, dfDPI * USER_UNIT_IN_INCH, NULL, &sMargins); + +#ifdef invalidate_xref_entry + GDALPDFObject* poVP = poPageDict->Get("VP"); + if (poVP) + { + if (poVP->GetType() == PDFObjectType_Array && + poVP->GetArray()->GetLength() == 1) + poVP = poVP->GetArray()->Get(0); + + int nVPId = poVP->GetRefNum(); + if (nVPId) + { + asXRefEntries[nVPId - 1].bFree = TRUE; + asXRefEntries[nVPId - 1].nGen ++; + } + } +#endif + + poPageDict->Remove("VP"); + poPageDict->Remove("LGIDict"); + + if (nViewportId) + { + poPageDict->Add("VP", &((new GDALPDFArrayRW())-> + Add(nViewportId, 0))); + } + + if (nLGIDictId) + { + poPageDict->Add("LGIDict", nLGIDictId, 0); + } + + StartObj(nPageNum, nPageGen); + VSIFPrintfL(fp, "%s\n", poPageDict->Serialize().c_str()); + EndObj(); +} + +/************************************************************************/ +/* UpdateInfo() */ +/************************************************************************/ + +void GDALPDFWriter::UpdateInfo(GDALDataset* poSrcDS) +{ + bCanUpdate = TRUE; + if ((int)asXRefEntries.size() < nLastXRefSize - 1) + asXRefEntries.resize(nLastXRefSize - 1); + + int nNewInfoId = SetInfo(poSrcDS, NULL); + /* Write empty info, because podofo driver will find the dangling info instead */ + if (nNewInfoId == 0 && nInfoId != 0) + { +#ifdef invalidate_xref_entry + asXRefEntries[nInfoId - 1].bFree = TRUE; + asXRefEntries[nInfoId - 1].nGen ++; +#else + StartObj(nInfoId, nInfoGen); + VSIFPrintfL(fp, "<< >>\n"); + EndObj(); +#endif + } +} + +/************************************************************************/ +/* UpdateXMP() */ +/************************************************************************/ + +void GDALPDFWriter::UpdateXMP(GDALDataset* poSrcDS, + GDALPDFDictionaryRW* poCatalogDict) +{ + bCanUpdate = TRUE; + if ((int)asXRefEntries.size() < nLastXRefSize - 1) + asXRefEntries.resize(nLastXRefSize - 1); + + CPLAssert(nCatalogId != 0); + CPLAssert(poCatalogDict != NULL); + + GDALPDFObject* poMetadata = poCatalogDict->Get("Metadata"); + if (poMetadata) + { + nXMPId = poMetadata->GetRefNum(); + nXMPGen = poMetadata->GetRefGen(); + } + + poCatalogDict->Remove("Metadata"); + int nNewXMPId = SetXMP(poSrcDS, NULL); + + /* Write empty metadata, because podofo driver will find the dangling info instead */ + if (nNewXMPId == 0 && nXMPId != 0) + { + StartObj(nXMPId, nXMPGen); + VSIFPrintfL(fp, "<< >>\n"); + EndObj(); + } + + if (nXMPId) + poCatalogDict->Add("Metadata", nXMPId, 0); + + StartObj(nCatalogId, nCatalogGen); + VSIFPrintfL(fp, "%s\n", poCatalogDict->Serialize().c_str()); + EndObj(); +} + +/************************************************************************/ +/* AllocNewObject() */ +/************************************************************************/ + +int GDALPDFWriter::AllocNewObject() +{ + asXRefEntries.push_back(GDALXRefEntry()); + return (int)asXRefEntries.size(); +} + +/************************************************************************/ +/* WriteXRefTableAndTrailer() */ +/************************************************************************/ + +void GDALPDFWriter::WriteXRefTableAndTrailer() +{ + vsi_l_offset nOffsetXREF = VSIFTellL(fp); + VSIFPrintfL(fp, "xref\n"); + + char buffer[16]; + if (bCanUpdate) + { + VSIFPrintfL(fp, "0 1\n"); + VSIFPrintfL(fp, "0000000000 65535 f \n"); + for(size_t i=0;i dfMeanX && + pasGCPList[i].dfGCPLine < dfMeanY ) + iUR = i; + + else if (pasGCPList[i].dfGCPPixel > dfMeanX && + pasGCPList[i].dfGCPLine > dfMeanY ) + iLR = i; + + else if (pasGCPList[i].dfGCPPixel < dfMeanX && + pasGCPList[i].dfGCPLine > dfMeanY ) + iLL = i; + } +} + +/************************************************************************/ +/* WriteSRS_ISO32000() */ +/************************************************************************/ + +int GDALPDFWriter::WriteSRS_ISO32000(GDALDataset* poSrcDS, + double dfUserUnit, + const char* pszNEATLINE, + PDFMargins* psMargins, + int bWriteViewport) +{ + int nWidth = poSrcDS->GetRasterXSize(); + int nHeight = poSrcDS->GetRasterYSize(); + const char* pszWKT = poSrcDS->GetProjectionRef(); + double adfGeoTransform[6]; + + int bHasGT = (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None); + const GDAL_GCP* pasGCPList = (poSrcDS->GetGCPCount() == 4) ? poSrcDS->GetGCPs() : NULL; + if (pasGCPList != NULL) + pszWKT = poSrcDS->GetGCPProjection(); + + if( !bHasGT && pasGCPList == NULL ) + return 0; + + if( pszWKT == NULL || EQUAL(pszWKT, "") ) + return 0; + + double adfGPTS[8]; + + double dfULPixel = 0; + double dfULLine = 0; + double dfLRPixel = nWidth; + double dfLRLine = nHeight; + + GDAL_GCP asNeatLineGCPs[4]; + if (pszNEATLINE == NULL) + pszNEATLINE = poSrcDS->GetMetadataItem("NEATLINE"); + if( bHasGT && pszNEATLINE != NULL && pszNEATLINE[0] != '\0' ) + { + OGRGeometry* poGeom = NULL; + OGRGeometryFactory::createFromWkt( (char**)&pszNEATLINE, NULL, &poGeom ); + if ( poGeom != NULL && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) + { + OGRLineString* poLS = ((OGRPolygon*)poGeom)->getExteriorRing(); + double adfGeoTransformInv[6]; + if( poLS != NULL && poLS->getNumPoints() == 5 && + GDALInvGeoTransform(adfGeoTransform, adfGeoTransformInv) ) + { + for(int i=0;i<4;i++) + { + double X = asNeatLineGCPs[i].dfGCPX = poLS->getX(i); + double Y = asNeatLineGCPs[i].dfGCPY = poLS->getY(i); + double x = adfGeoTransformInv[0] + X * adfGeoTransformInv[1] + Y * adfGeoTransformInv[2]; + double y = adfGeoTransformInv[3] + X * adfGeoTransformInv[4] + Y * adfGeoTransformInv[5]; + asNeatLineGCPs[i].dfGCPPixel = x; + asNeatLineGCPs[i].dfGCPLine = y; + } + + int iUL = 0, iUR = 0, iLR = 0, iLL = 0; + GDALPDFFind4Corners(asNeatLineGCPs, + iUL,iUR, iLR, iLL); + + if (fabs(asNeatLineGCPs[iUL].dfGCPPixel - asNeatLineGCPs[iLL].dfGCPPixel) > .5 || + fabs(asNeatLineGCPs[iUR].dfGCPPixel - asNeatLineGCPs[iLR].dfGCPPixel) > .5 || + fabs(asNeatLineGCPs[iUL].dfGCPLine - asNeatLineGCPs[iUR].dfGCPLine) > .5 || + fabs(asNeatLineGCPs[iLL].dfGCPLine - asNeatLineGCPs[iLR].dfGCPLine) > .5) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Neatline coordinates should form a rectangle in pixel space. Ignoring it"); + for(int i=0;i<4;i++) + { + CPLDebug("PDF", "pixel[%d] = %.1f, line[%d] = %.1f", + i, asNeatLineGCPs[i].dfGCPPixel, + i, asNeatLineGCPs[i].dfGCPLine); + } + } + else + { + pasGCPList = asNeatLineGCPs; + } + } + } + delete poGeom; + } + + if( pasGCPList ) + { + int iUL = 0, iUR = 0, iLR = 0, iLL = 0; + GDALPDFFind4Corners(pasGCPList, + iUL,iUR, iLR, iLL); + + if (fabs(pasGCPList[iUL].dfGCPPixel - pasGCPList[iLL].dfGCPPixel) > .5 || + fabs(pasGCPList[iUR].dfGCPPixel - pasGCPList[iLR].dfGCPPixel) > .5 || + fabs(pasGCPList[iUL].dfGCPLine - pasGCPList[iUR].dfGCPLine) > .5 || + fabs(pasGCPList[iLL].dfGCPLine - pasGCPList[iLR].dfGCPLine) > .5) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GCPs should form a rectangle in pixel space"); + return 0; + } + + dfULPixel = pasGCPList[iUL].dfGCPPixel; + dfULLine = pasGCPList[iUL].dfGCPLine; + dfLRPixel = pasGCPList[iLR].dfGCPPixel; + dfLRLine = pasGCPList[iLR].dfGCPLine; + + /* Upper-left */ + adfGPTS[0] = pasGCPList[iUL].dfGCPX; + adfGPTS[1] = pasGCPList[iUL].dfGCPY; + + /* Lower-left */ + adfGPTS[2] = pasGCPList[iLL].dfGCPX; + adfGPTS[3] = pasGCPList[iLL].dfGCPY; + + /* Lower-right */ + adfGPTS[4] = pasGCPList[iLR].dfGCPX; + adfGPTS[5] = pasGCPList[iLR].dfGCPY; + + /* Upper-right */ + adfGPTS[6] = pasGCPList[iUR].dfGCPX; + adfGPTS[7] = pasGCPList[iUR].dfGCPY; + } + else + { + /* Upper-left */ + adfGPTS[0] = PIXEL_TO_GEO_X(0, 0); + adfGPTS[1] = PIXEL_TO_GEO_Y(0, 0); + + /* Lower-left */ + adfGPTS[2] = PIXEL_TO_GEO_X(0, nHeight); + adfGPTS[3] = PIXEL_TO_GEO_Y(0, nHeight); + + /* Lower-right */ + adfGPTS[4] = PIXEL_TO_GEO_X(nWidth, nHeight); + adfGPTS[5] = PIXEL_TO_GEO_Y(nWidth, nHeight); + + /* Upper-right */ + adfGPTS[6] = PIXEL_TO_GEO_X(nWidth, 0); + adfGPTS[7] = PIXEL_TO_GEO_Y(nWidth, 0); + } + + OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszWKT); + if( hSRS == NULL ) + return 0; + OGRSpatialReferenceH hSRSGeog = OSRCloneGeogCS(hSRS); + if( hSRSGeog == NULL ) + { + OSRDestroySpatialReference(hSRS); + return 0; + } + OGRCoordinateTransformationH hCT = OCTNewCoordinateTransformation( hSRS, hSRSGeog); + if( hCT == NULL ) + { + OSRDestroySpatialReference(hSRS); + OSRDestroySpatialReference(hSRSGeog); + return 0; + } + + int bSuccess = TRUE; + + bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 0, adfGPTS + 1, NULL ) == 1); + bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 2, adfGPTS + 3, NULL ) == 1); + bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 4, adfGPTS + 5, NULL ) == 1); + bSuccess &= (OCTTransform( hCT, 1, adfGPTS + 6, adfGPTS + 7, NULL ) == 1); + + if (!bSuccess) + { + OSRDestroySpatialReference(hSRS); + OSRDestroySpatialReference(hSRSGeog); + OCTDestroyCoordinateTransformation(hCT); + return 0; + } + + const char * pszAuthorityCode = OSRGetAuthorityCode( hSRS, NULL ); + const char * pszAuthorityName = OSRGetAuthorityName( hSRS, NULL ); + int nEPSGCode = 0; + if( pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG") && + pszAuthorityCode != NULL ) + nEPSGCode = atoi(pszAuthorityCode); + + int bIsGeographic = OSRIsGeographic(hSRS); + + OSRMorphToESRI(hSRS); + char* pszESRIWKT = NULL; + OSRExportToWkt(hSRS, &pszESRIWKT); + + OSRDestroySpatialReference(hSRS); + OSRDestroySpatialReference(hSRSGeog); + OCTDestroyCoordinateTransformation(hCT); + hSRS = NULL; + hSRSGeog = NULL; + hCT = NULL; + + if (pszESRIWKT == NULL) + return 0; + + int nViewportId = (bWriteViewport) ? AllocNewObject() : 0; + int nMeasureId = AllocNewObject(); + int nGCSId = AllocNewObject(); + + if (nViewportId) + { + StartObj(nViewportId); + GDALPDFDictionaryRW oViewPortDict; + oViewPortDict.Add("Type", GDALPDFObjectRW::CreateName("Viewport")) + .Add("Name", "Layer") + .Add("BBox", &((new GDALPDFArrayRW()) + ->Add(dfULPixel / dfUserUnit + psMargins->nLeft) + .Add((nHeight - dfLRLine) / dfUserUnit + psMargins->nBottom) + .Add(dfLRPixel / dfUserUnit + psMargins->nLeft) + .Add((nHeight - dfULLine) / dfUserUnit + psMargins->nBottom))) + .Add("Measure", nMeasureId, 0); + VSIFPrintfL(fp, "%s\n", oViewPortDict.Serialize().c_str()); + EndObj(); + } + + StartObj(nMeasureId); + GDALPDFDictionaryRW oMeasureDict; + oMeasureDict .Add("Type", GDALPDFObjectRW::CreateName("Measure")) + .Add("Subtype", GDALPDFObjectRW::CreateName("GEO")) + .Add("Bounds", &((new GDALPDFArrayRW()) + ->Add(0).Add(1). + Add(0).Add(0). + Add(1).Add(0). + Add(1).Add(1))) + .Add("GPTS", &((new GDALPDFArrayRW()) + ->Add(adfGPTS[1]).Add(adfGPTS[0]). + Add(adfGPTS[3]).Add(adfGPTS[2]). + Add(adfGPTS[5]).Add(adfGPTS[4]). + Add(adfGPTS[7]).Add(adfGPTS[6]))) + .Add("LPTS", &((new GDALPDFArrayRW()) + ->Add(0).Add(1). + Add(0).Add(0). + Add(1).Add(0). + Add(1).Add(1))) + .Add("GCS", nGCSId, 0); + VSIFPrintfL(fp, "%s\n", oMeasureDict.Serialize().c_str()); + EndObj(); + + + StartObj(nGCSId); + GDALPDFDictionaryRW oGCSDict; + oGCSDict.Add("Type", GDALPDFObjectRW::CreateName(bIsGeographic ? "GEOGCS" : "PROJCS")) + .Add("WKT", pszESRIWKT); + if (nEPSGCode) + oGCSDict.Add("EPSG", nEPSGCode); + VSIFPrintfL(fp, "%s\n", oGCSDict.Serialize().c_str()); + EndObj(); + + CPLFree(pszESRIWKT); + + return nViewportId ? nViewportId : nMeasureId; +} + +/************************************************************************/ +/* GDALPDFBuildOGC_BP_Datum() */ +/************************************************************************/ + +static GDALPDFObject* GDALPDFBuildOGC_BP_Datum(const OGRSpatialReference* poSRS) +{ + const OGR_SRSNode* poDatumNode = poSRS->GetAttrNode("DATUM"); + const char* pszDatumDescription = NULL; + if (poDatumNode && poDatumNode->GetChildCount() > 0) + pszDatumDescription = poDatumNode->GetChild(0)->GetValue(); + + GDALPDFObjectRW* poPDFDatum = NULL; + + if (pszDatumDescription) + { + double dfSemiMajor = poSRS->GetSemiMajor(); + double dfInvFlattening = poSRS->GetInvFlattening(); + int nEPSGDatum = -1; + const char *pszAuthority = poSRS->GetAuthorityName( "DATUM" ); + if( pszAuthority != NULL && EQUAL(pszAuthority,"EPSG") ) + nEPSGDatum = atoi(poSRS->GetAuthorityCode( "DATUM" )); + + if( EQUAL(pszDatumDescription,SRS_DN_WGS84) || nEPSGDatum == 6326 ) + poPDFDatum = GDALPDFObjectRW::CreateString("WGE"); + else if( EQUAL(pszDatumDescription, SRS_DN_NAD27) || nEPSGDatum == 6267 ) + poPDFDatum = GDALPDFObjectRW::CreateString("NAS"); + else if( EQUAL(pszDatumDescription, SRS_DN_NAD83) || nEPSGDatum == 6269 ) + poPDFDatum = GDALPDFObjectRW::CreateString("NAR"); + else if( nEPSGDatum == 6135 ) + poPDFDatum = GDALPDFObjectRW::CreateString("OHA-M"); + else + { + CPLDebug("PDF", + "Unhandled datum name (%s). Write datum parameters then.", + pszDatumDescription); + + GDALPDFDictionaryRW* poPDFDatumDict = new GDALPDFDictionaryRW(); + poPDFDatum = GDALPDFObjectRW::CreateDictionary(poPDFDatumDict); + + const OGR_SRSNode* poSpheroidNode = poSRS->GetAttrNode("SPHEROID"); + if (poSpheroidNode && poSpheroidNode->GetChildCount() >= 3) + { + poPDFDatumDict->Add("Description", pszDatumDescription); + + const char* pszEllipsoidCode = NULL; +#ifdef disabled_because_terrago_toolbar_does_not_like_it + if( ABS(dfSemiMajor-6378249.145) < 0.01 + && ABS(dfInvFlattening-293.465) < 0.0001 ) + { + pszEllipsoidCode = "CD"; /* Clark 1880 */ + } + else if( ABS(dfSemiMajor-6378245.0) < 0.01 + && ABS(dfInvFlattening-298.3) < 0.0001 ) + { + pszEllipsoidCode = "KA"; /* Krassovsky */ + } + else if( ABS(dfSemiMajor-6378388.0) < 0.01 + && ABS(dfInvFlattening-297.0) < 0.0001 ) + { + pszEllipsoidCode = "IN"; /* International 1924 */ + } + else if( ABS(dfSemiMajor-6378160.0) < 0.01 + && ABS(dfInvFlattening-298.25) < 0.0001 ) + { + pszEllipsoidCode = "AN"; /* Australian */ + } + else if( ABS(dfSemiMajor-6377397.155) < 0.01 + && ABS(dfInvFlattening-299.1528128) < 0.0001 ) + { + pszEllipsoidCode = "BR"; /* Bessel 1841 */ + } + else if( ABS(dfSemiMajor-6377483.865) < 0.01 + && ABS(dfInvFlattening-299.1528128) < 0.0001 ) + { + pszEllipsoidCode = "BN"; /* Bessel 1841 (Namibia / Schwarzeck)*/ + } +#if 0 + else if( ABS(dfSemiMajor-6378160.0) < 0.01 + && ABS(dfInvFlattening-298.247167427) < 0.0001 ) + { + pszEllipsoidCode = "GRS67"; /* GRS 1967 */ + } +#endif + else if( ABS(dfSemiMajor-6378137) < 0.01 + && ABS(dfInvFlattening-298.257222101) < 0.000001 ) + { + pszEllipsoidCode = "RF"; /* GRS 1980 */ + } + else if( ABS(dfSemiMajor-6378206.4) < 0.01 + && ABS(dfInvFlattening-294.9786982) < 0.0001 ) + { + pszEllipsoidCode = "CC"; /* Clarke 1866 */ + } + else if( ABS(dfSemiMajor-6377340.189) < 0.01 + && ABS(dfInvFlattening-299.3249646) < 0.0001 ) + { + pszEllipsoidCode = "AM"; /* Modified Airy */ + } + else if( ABS(dfSemiMajor-6377563.396) < 0.01 + && ABS(dfInvFlattening-299.3249646) < 0.0001 ) + { + pszEllipsoidCode = "AA"; /* Airy */ + } + else if( ABS(dfSemiMajor-6378200) < 0.01 + && ABS(dfInvFlattening-298.3) < 0.0001 ) + { + pszEllipsoidCode = "HE"; /* Helmert 1906 */ + } + else if( ABS(dfSemiMajor-6378155) < 0.01 + && ABS(dfInvFlattening-298.3) < 0.0001 ) + { + pszEllipsoidCode = "FA"; /* Modified Fischer 1960 */ + } +#if 0 + else if( ABS(dfSemiMajor-6377298.556) < 0.01 + && ABS(dfInvFlattening-300.8017) < 0.0001 ) + { + pszEllipsoidCode = "evrstSS"; /* Everest (Sabah & Sarawak) */ + } + else if( ABS(dfSemiMajor-6378165.0) < 0.01 + && ABS(dfInvFlattening-298.3) < 0.0001 ) + { + pszEllipsoidCode = "WGS60"; + } + else if( ABS(dfSemiMajor-6378145.0) < 0.01 + && ABS(dfInvFlattening-298.25) < 0.0001 ) + { + pszEllipsoidCode = "WGS66"; + } +#endif + else if( ABS(dfSemiMajor-6378135.0) < 0.01 + && ABS(dfInvFlattening-298.26) < 0.0001 ) + { + pszEllipsoidCode = "WD"; + } + else if( ABS(dfSemiMajor-6378137.0) < 0.01 + && ABS(dfInvFlattening-298.257223563) < 0.000001 ) + { + pszEllipsoidCode = "WE"; + } +#endif + + if( pszEllipsoidCode != NULL ) + { + poPDFDatumDict->Add("Ellipsoid", pszEllipsoidCode); + } + else + { + const char* pszEllipsoidDescription = + poSpheroidNode->GetChild(0)->GetValue(); + + CPLDebug("PDF", + "Unhandled ellipsoid name (%s). Write ellipsoid parameters then.", + pszEllipsoidDescription); + + poPDFDatumDict->Add("Ellipsoid", + &((new GDALPDFDictionaryRW()) + ->Add("Description", pszEllipsoidDescription) + .Add("SemiMajorAxis", dfSemiMajor, TRUE) + .Add("InvFlattening", dfInvFlattening, TRUE))); + } + + const OGR_SRSNode *poTOWGS84 = poSRS->GetAttrNode( "TOWGS84" ); + if( poTOWGS84 != NULL + && poTOWGS84->GetChildCount() >= 3 + && (poTOWGS84->GetChildCount() < 7 + || (EQUAL(poTOWGS84->GetChild(3)->GetValue(),"") + && EQUAL(poTOWGS84->GetChild(4)->GetValue(),"") + && EQUAL(poTOWGS84->GetChild(5)->GetValue(),"") + && EQUAL(poTOWGS84->GetChild(6)->GetValue(),""))) ) + { + poPDFDatumDict->Add("ToWGS84", + &((new GDALPDFDictionaryRW()) + ->Add("dx", poTOWGS84->GetChild(0)->GetValue()) + .Add("dy", poTOWGS84->GetChild(1)->GetValue()) + .Add("dz", poTOWGS84->GetChild(2)->GetValue())) ); + } + else if( poTOWGS84 != NULL && poTOWGS84->GetChildCount() >= 7) + { + poPDFDatumDict->Add("ToWGS84", + &((new GDALPDFDictionaryRW()) + ->Add("dx", poTOWGS84->GetChild(0)->GetValue()) + .Add("dy", poTOWGS84->GetChild(1)->GetValue()) + .Add("dz", poTOWGS84->GetChild(2)->GetValue()) + .Add("rx", poTOWGS84->GetChild(3)->GetValue()) + .Add("ry", poTOWGS84->GetChild(4)->GetValue()) + .Add("rz", poTOWGS84->GetChild(5)->GetValue()) + .Add("sf", poTOWGS84->GetChild(6)->GetValue())) ); + } + } + } + } + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "No datum name. Defaulting to WGS84."); + } + + if (poPDFDatum == NULL) + poPDFDatum = GDALPDFObjectRW::CreateString("WGE"); + + return poPDFDatum; +} + +/************************************************************************/ +/* GDALPDFBuildOGC_BP_Projection() */ +/************************************************************************/ + +static GDALPDFDictionaryRW* GDALPDFBuildOGC_BP_Projection(const OGRSpatialReference* poSRS) +{ + + const char* pszProjectionOGCBP = "GEOGRAPHIC"; + const char *pszProjection = poSRS->GetAttrValue("PROJECTION"); + + GDALPDFDictionaryRW* poProjectionDict = new GDALPDFDictionaryRW(); + poProjectionDict->Add("Type", GDALPDFObjectRW::CreateName("Projection")); + poProjectionDict->Add("Datum", GDALPDFBuildOGC_BP_Datum(poSRS)); + + if( pszProjection == NULL ) + { + if( poSRS->IsGeographic() ) + pszProjectionOGCBP = "GEOGRAPHIC"; + else if( poSRS->IsLocal() ) + pszProjectionOGCBP = "LOCAL CARTESIAN"; + else + { + CPLError(CE_Warning, CPLE_NotSupported, "Unsupported SRS type"); + delete poProjectionDict; + return NULL; + } + } + else if( EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR) ) + { + int bNorth; + int nZone = poSRS->GetUTMZone( &bNorth ); + + if( nZone != 0 ) + { + pszProjectionOGCBP = "UT"; + poProjectionDict->Add("Hemisphere", (bNorth) ? "N" : "S"); + poProjectionDict->Add("Zone", nZone); + } + else + { + double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,90.L); + double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + double dfScale = poSRS->GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0); + double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0); + double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0); + + /* OGC_BP supports representing numbers as strings for better precision */ + /* so use it */ + + pszProjectionOGCBP = "TC"; + poProjectionDict->Add("OriginLatitude", dfCenterLat, TRUE); + poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE); + poProjectionDict->Add("ScaleFactor", dfScale, TRUE); + poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE); + poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE); + } + } + else if( EQUAL(pszProjection,SRS_PT_POLAR_STEREOGRAPHIC) ) + { + double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + double dfScale = poSRS->GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0); + double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0); + double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0); + + if( fabs(dfCenterLat) == 90.0 && dfCenterLong == 0.0 && + dfScale == 0.994 && dfFalseEasting == 200000.0 && dfFalseNorthing == 200000.0) + { + pszProjectionOGCBP = "UP"; + poProjectionDict->Add("Hemisphere", (dfCenterLat > 0) ? "N" : "S"); + } + else + { + pszProjectionOGCBP = "PG"; + poProjectionDict->Add("LatitudeTrueScale", dfCenterLat, TRUE); + poProjectionDict->Add("LongitudeDownFromPole", dfCenterLong, TRUE); + poProjectionDict->Add("ScaleFactor", dfScale, TRUE); + poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE); + poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE); + } + } + + else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP)) + { + double dfStdP1 = poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); + double dfStdP2 = poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0); + double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0); + double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0); + + pszProjectionOGCBP = "LE"; + poProjectionDict->Add("StandardParallelOne", dfStdP1, TRUE); + poProjectionDict->Add("StandardParallelTwo", dfStdP2, TRUE); + poProjectionDict->Add("OriginLatitude", dfCenterLat, TRUE); + poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE); + poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE); + poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE); + } + + else if( EQUAL(pszProjection,SRS_PT_MERCATOR_1SP) ) + { + double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + double dfCenterLat = poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + double dfScale = poSRS->GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0); + double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0); + double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0); + + pszProjectionOGCBP = "MC"; + poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE); + poProjectionDict->Add("OriginLatitude", dfCenterLat, TRUE); + poProjectionDict->Add("ScaleFactor", dfScale, TRUE); + poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE); + poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE); + } + +#ifdef not_supported + else if( EQUAL(pszProjection,SRS_PT_MERCATOR_2SP) ) + { + double dfStdP1 = poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); + double dfCenterLong = poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + double dfFalseEasting = poSRS->GetNormProjParm(SRS_PP_FALSE_EASTING,0.0); + double dfFalseNorthing = poSRS->GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0); + + pszProjectionOGCBP = "MC"; + poProjectionDict->Add("StandardParallelOne", dfStdP1, TRUE); + poProjectionDict->Add("CentralMeridian", dfCenterLong, TRUE); + poProjectionDict->Add("FalseEasting", dfFalseEasting, TRUE); + poProjectionDict->Add("FalseNorthing", dfFalseNorthing, TRUE); + } +#endif + + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unhandled projection type (%s) for now", pszProjection); + } + + poProjectionDict->Add("ProjectionType", pszProjectionOGCBP); + + if( poSRS->IsProjected() ) + { + char* pszUnitName = NULL; + double dfLinearUnits = poSRS->GetLinearUnits(&pszUnitName); + if (dfLinearUnits == 1.0) + poProjectionDict->Add("Units", "M"); + else if (dfLinearUnits == 0.3048) + poProjectionDict->Add("Units", "FT"); + } + + return poProjectionDict; +} + +/************************************************************************/ +/* WriteSRS_OGC_BP() */ +/************************************************************************/ + +int GDALPDFWriter::WriteSRS_OGC_BP(GDALDataset* poSrcDS, + double dfUserUnit, + const char* pszNEATLINE, + PDFMargins* psMargins) +{ + int nWidth = poSrcDS->GetRasterXSize(); + int nHeight = poSrcDS->GetRasterYSize(); + const char* pszWKT = poSrcDS->GetProjectionRef(); + double adfGeoTransform[6]; + + int bHasGT = (poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None); + int nGCPCount = poSrcDS->GetGCPCount(); + const GDAL_GCP* pasGCPList = (nGCPCount >= 4) ? poSrcDS->GetGCPs() : NULL; + if (pasGCPList != NULL) + pszWKT = poSrcDS->GetGCPProjection(); + + if( !bHasGT && pasGCPList == NULL ) + return 0; + + if( pszWKT == NULL || EQUAL(pszWKT, "") ) + return 0; + + if( !bHasGT ) + { + if (!GDALGCPsToGeoTransform( nGCPCount, pasGCPList, + adfGeoTransform, FALSE )) + { + CPLDebug("PDF", "Could not compute GT with exact match. Writing Registration then"); + } + else + { + bHasGT = TRUE; + } + } + + OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszWKT); + if( hSRS == NULL ) + return 0; + + const OGRSpatialReference* poSRS = (const OGRSpatialReference*)hSRS; + GDALPDFDictionaryRW* poProjectionDict = GDALPDFBuildOGC_BP_Projection(poSRS); + if (poProjectionDict == NULL) + { + OSRDestroySpatialReference(hSRS); + return 0; + } + + GDALPDFArrayRW* poNeatLineArray = NULL; + + if (pszNEATLINE == NULL) + pszNEATLINE = poSrcDS->GetMetadataItem("NEATLINE"); + if( bHasGT && pszNEATLINE != NULL && !EQUAL(pszNEATLINE, "NO") && pszNEATLINE[0] != '\0' ) + { + OGRGeometry* poGeom = NULL; + OGRGeometryFactory::createFromWkt( (char**)&pszNEATLINE, NULL, &poGeom ); + if ( poGeom != NULL && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) + { + OGRLineString* poLS = ((OGRPolygon*)poGeom)->getExteriorRing(); + double adfGeoTransformInv[6]; + if( poLS != NULL && poLS->getNumPoints() >= 5 && + GDALInvGeoTransform(adfGeoTransform, adfGeoTransformInv) ) + { + poNeatLineArray = new GDALPDFArrayRW(); + + // FIXME : ensure that they are in clockwise order ? + for(int i=0;igetNumPoints() - 1;i++) + { + double X = poLS->getX(i); + double Y = poLS->getY(i); + double x = adfGeoTransformInv[0] + X * adfGeoTransformInv[1] + Y * adfGeoTransformInv[2]; + double y = adfGeoTransformInv[3] + X * adfGeoTransformInv[4] + Y * adfGeoTransformInv[5]; + poNeatLineArray->Add(x / dfUserUnit + psMargins->nLeft, TRUE); + poNeatLineArray->Add((nHeight - y) / dfUserUnit + psMargins->nBottom, TRUE); + } + } + } + delete poGeom; + } + + if( pszNEATLINE != NULL && EQUAL(pszNEATLINE, "NO") ) + { + // Do nothing + } + else if( pasGCPList && poNeatLineArray == NULL) + { + if (nGCPCount == 4) + { + int iUL = 0, iUR = 0, iLR = 0, iLL = 0; + GDALPDFFind4Corners(pasGCPList, + iUL,iUR, iLR, iLL); + + double adfNL[8]; + adfNL[0] = pasGCPList[iUL].dfGCPPixel / dfUserUnit + psMargins->nLeft; + adfNL[1] = (nHeight - pasGCPList[iUL].dfGCPLine) / dfUserUnit + psMargins->nBottom; + adfNL[2] = pasGCPList[iLL].dfGCPPixel / dfUserUnit + psMargins->nLeft; + adfNL[3] = (nHeight - pasGCPList[iLL].dfGCPLine) / dfUserUnit + psMargins->nBottom; + adfNL[4] = pasGCPList[iLR].dfGCPPixel / dfUserUnit + psMargins->nLeft; + adfNL[5] = (nHeight - pasGCPList[iLR].dfGCPLine) / dfUserUnit + psMargins->nBottom; + adfNL[6] = pasGCPList[iUR].dfGCPPixel / dfUserUnit + psMargins->nLeft; + adfNL[7] = (nHeight - pasGCPList[iUR].dfGCPLine) / dfUserUnit + psMargins->nBottom; + + poNeatLineArray = new GDALPDFArrayRW(); + poNeatLineArray->Add(adfNL, 8, TRUE); + } + else + { + poNeatLineArray = new GDALPDFArrayRW(); + + // FIXME : ensure that they are in clockwise order ? + int i; + for(i = 0; i < nGCPCount; i++) + { + poNeatLineArray->Add(pasGCPList[i].dfGCPPixel / dfUserUnit + psMargins->nLeft, TRUE); + poNeatLineArray->Add((nHeight - pasGCPList[i].dfGCPLine) / dfUserUnit + psMargins->nBottom, TRUE); + } + } + } + else if (poNeatLineArray == NULL) + { + poNeatLineArray = new GDALPDFArrayRW(); + + poNeatLineArray->Add(0 / dfUserUnit + psMargins->nLeft, TRUE); + poNeatLineArray->Add((nHeight - 0) / dfUserUnit + psMargins->nBottom, TRUE); + + poNeatLineArray->Add(0 / dfUserUnit + psMargins->nLeft, TRUE); + poNeatLineArray->Add((nHeight -nHeight) / dfUserUnit + psMargins->nBottom, TRUE); + + poNeatLineArray->Add(nWidth / dfUserUnit + psMargins->nLeft, TRUE); + poNeatLineArray->Add((nHeight -nHeight) / dfUserUnit + psMargins->nBottom, TRUE); + + poNeatLineArray->Add(nWidth / dfUserUnit + psMargins->nLeft, TRUE); + poNeatLineArray->Add((nHeight - 0) / dfUserUnit + psMargins->nBottom, TRUE); + } + + int nLGIDictId = AllocNewObject(); + StartObj(nLGIDictId); + GDALPDFDictionaryRW oLGIDict; + oLGIDict.Add("Type", GDALPDFObjectRW::CreateName("LGIDict")) + .Add("Version", "2.1"); + if( bHasGT ) + { + double adfCTM[6]; + double dfX1 = psMargins->nLeft; + double dfY2 = nHeight / dfUserUnit + psMargins->nBottom ; + + adfCTM[0] = adfGeoTransform[1] * dfUserUnit; + adfCTM[1] = adfGeoTransform[2] * dfUserUnit; + adfCTM[2] = - adfGeoTransform[4] * dfUserUnit; + adfCTM[3] = - adfGeoTransform[5] * dfUserUnit; + adfCTM[4] = adfGeoTransform[0] - (adfCTM[0] * dfX1 + adfCTM[2] * dfY2); + adfCTM[5] = adfGeoTransform[3] - (adfCTM[1] * dfX1 + adfCTM[3] * dfY2); + + oLGIDict.Add("CTM", &((new GDALPDFArrayRW())->Add(adfCTM, 6, TRUE))); + } + else + { + GDALPDFArrayRW* poRegistrationArray = new GDALPDFArrayRW(); + int i; + for(i = 0; i < nGCPCount; i++) + { + GDALPDFArrayRW* poPTArray = new GDALPDFArrayRW(); + poPTArray->Add(pasGCPList[i].dfGCPPixel / dfUserUnit + psMargins->nLeft, TRUE); + poPTArray->Add((nHeight - pasGCPList[i].dfGCPLine) / dfUserUnit + psMargins->nBottom, TRUE); + poPTArray->Add(pasGCPList[i].dfGCPX, TRUE); + poPTArray->Add(pasGCPList[i].dfGCPY, TRUE); + poRegistrationArray->Add(poPTArray); + } + oLGIDict.Add("Registration", poRegistrationArray); + } + if( poNeatLineArray ) + { + oLGIDict.Add("Neatline", poNeatLineArray); + } + + const OGR_SRSNode* poNode = poSRS->GetRoot(); + if( poNode != NULL ) + poNode = poNode->GetChild(0); + const char* pszDescription = NULL; + if( poNode != NULL ) + pszDescription = poNode->GetValue(); + if( pszDescription ) + { + oLGIDict.Add("Description", pszDescription); + } + + oLGIDict.Add("Projection", poProjectionDict); + + /* GDAL extension */ + if( CSLTestBoolean( CPLGetConfigOption("GDAL_PDF_OGC_BP_WRITE_WKT", "TRUE") ) ) + poProjectionDict->Add("WKT", pszWKT); + + VSIFPrintfL(fp, "%s\n", oLGIDict.Serialize().c_str()); + EndObj(); + + OSRDestroySpatialReference(hSRS); + + return nLGIDictId; +} + +/************************************************************************/ +/* GDALPDFGetValueFromDSOrOption() */ +/************************************************************************/ + +static const char* GDALPDFGetValueFromDSOrOption(GDALDataset* poSrcDS, + char** papszOptions, + const char* pszKey) +{ + const char* pszValue = CSLFetchNameValue(papszOptions, pszKey); + if (pszValue == NULL) + pszValue = poSrcDS->GetMetadataItem(pszKey); + if (pszValue != NULL && pszValue[0] == '\0') + return NULL; + else + return pszValue; +} + +/************************************************************************/ +/* SetInfo() */ +/************************************************************************/ + +int GDALPDFWriter::SetInfo(GDALDataset* poSrcDS, + char** papszOptions) +{ + const char* pszAUTHOR = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "AUTHOR"); + const char* pszPRODUCER = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "PRODUCER"); + const char* pszCREATOR = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "CREATOR"); + const char* pszCREATION_DATE = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "CREATION_DATE"); + const char* pszSUBJECT = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "SUBJECT"); + const char* pszTITLE = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "TITLE"); + const char* pszKEYWORDS = GDALPDFGetValueFromDSOrOption(poSrcDS, papszOptions, "KEYWORDS"); + + if (pszAUTHOR == NULL && pszPRODUCER == NULL && pszCREATOR == NULL && pszCREATION_DATE == NULL && + pszSUBJECT == NULL && pszTITLE == NULL && pszKEYWORDS == NULL) + return 0; + + if (nInfoId == 0) + nInfoId = AllocNewObject(); + StartObj(nInfoId, nInfoGen); + GDALPDFDictionaryRW oDict; + if (pszAUTHOR != NULL) + oDict.Add("Author", pszAUTHOR); + if (pszPRODUCER != NULL) + oDict.Add("Producer", pszPRODUCER); + if (pszCREATOR != NULL) + oDict.Add("Creator", pszCREATOR); + if (pszCREATION_DATE != NULL) + oDict.Add("CreationDate", pszCREATION_DATE); + if (pszSUBJECT != NULL) + oDict.Add("Subject", pszSUBJECT); + if (pszTITLE != NULL) + oDict.Add("Title", pszTITLE); + if (pszKEYWORDS != NULL) + oDict.Add("Keywords", pszKEYWORDS); + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + EndObj(); + + return nInfoId; +} + +/************************************************************************/ +/* SetXMP() */ +/************************************************************************/ + +int GDALPDFWriter::SetXMP(GDALDataset* poSrcDS, + const char* pszXMP) +{ + if (pszXMP != NULL && EQUALN(pszXMP, "NO", 2)) + return 0; + if (pszXMP != NULL && pszXMP[0] == '\0') + return 0; + + char** papszXMP = poSrcDS->GetMetadata("xml:XMP"); + if (pszXMP == NULL && papszXMP != NULL && papszXMP[0] != NULL) + pszXMP = papszXMP[0]; + + if (pszXMP == NULL) + return 0; + + CPLXMLNode* psNode = CPLParseXMLString(pszXMP); + if (psNode == NULL) + return 0; + CPLDestroyXMLNode(psNode); + + if(nXMPId == 0) + nXMPId = AllocNewObject(); + StartObj(nXMPId, nXMPGen); + GDALPDFDictionaryRW oDict; + oDict.Add("Type", GDALPDFObjectRW::CreateName("Metadata")) + .Add("Subtype", GDALPDFObjectRW::CreateName("XML")) + .Add("Length", (int)strlen(pszXMP)); + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + VSIFPrintfL(fp, "stream\n"); + VSIFPrintfL(fp, "%s\n", pszXMP); + VSIFPrintfL(fp, "endstream\n"); + EndObj(); + return nXMPId; +} + +/************************************************************************/ +/* WriteOCG() */ +/************************************************************************/ + +int GDALPDFWriter::WriteOCG(const char* pszLayerName, int nParentId) +{ + if (pszLayerName == NULL || pszLayerName[0] == '\0') + return 0; + + int nOGCId = AllocNewObject(); + + GDALPDFOCGDesc oOCGDesc; + oOCGDesc.nId = nOGCId; + oOCGDesc.nParentId = nParentId; + oOCGDesc.osLayerName = pszLayerName; + + asOCGs.push_back(oOCGDesc); + + StartObj(nOGCId); + { + GDALPDFDictionaryRW oDict; + oDict.Add("Type", GDALPDFObjectRW::CreateName("OCG")); + oDict.Add("Name", pszLayerName); + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + } + EndObj(); + + return nOGCId; +} + +/************************************************************************/ +/* StartPage() */ +/************************************************************************/ + +int GDALPDFWriter::StartPage(GDALDataset* poClippingDS, + double dfDPI, + const char* pszGEO_ENCODING, + const char* pszNEATLINE, + PDFMargins* psMargins, + PDFCompressMethod eStreamCompressMethod, + int bHasOGRData) +{ + int nWidth = poClippingDS->GetRasterXSize(); + int nHeight = poClippingDS->GetRasterYSize(); + int nBands = poClippingDS->GetRasterCount(); + + double dfUserUnit = dfDPI * USER_UNIT_IN_INCH; + double dfWidthInUserUnit = nWidth / dfUserUnit + psMargins->nLeft + psMargins->nRight; + double dfHeightInUserUnit = nHeight / dfUserUnit + psMargins->nBottom + psMargins->nTop; + + int nPageId = AllocNewObject(); + asPageId.push_back(nPageId); + + int nContentId = AllocNewObject(); + int nResourcesId = AllocNewObject(); + + int nAnnotsId = AllocNewObject(); + + int bISO32000 = EQUAL(pszGEO_ENCODING, "ISO32000") || + EQUAL(pszGEO_ENCODING, "BOTH"); + int bOGC_BP = EQUAL(pszGEO_ENCODING, "OGC_BP") || + EQUAL(pszGEO_ENCODING, "BOTH"); + + int nViewportId = 0; + if( bISO32000 ) + nViewportId = WriteSRS_ISO32000(poClippingDS, dfUserUnit, pszNEATLINE, psMargins, TRUE); + + int nLGIDictId = 0; + if( bOGC_BP ) + nLGIDictId = WriteSRS_OGC_BP(poClippingDS, dfUserUnit, pszNEATLINE, psMargins); + + StartObj(nPageId); + GDALPDFDictionaryRW oDictPage; + oDictPage.Add("Type", GDALPDFObjectRW::CreateName("Page")) + .Add("Parent", nPageResourceId, 0) + .Add("MediaBox", &((new GDALPDFArrayRW()) + ->Add(0).Add(0).Add(dfWidthInUserUnit).Add(dfHeightInUserUnit))) + .Add("UserUnit", dfUserUnit) + .Add("Contents", nContentId, 0) + .Add("Resources", nResourcesId, 0) + .Add("Annots", nAnnotsId, 0); + + if (nBands == 4) + { + oDictPage.Add("Group", + &((new GDALPDFDictionaryRW()) + ->Add("Type", GDALPDFObjectRW::CreateName("Group")) + .Add("S", GDALPDFObjectRW::CreateName("Transparency")) + .Add("CS", GDALPDFObjectRW::CreateName("DeviceRGB")))); + } + if (nViewportId) + { + oDictPage.Add("VP", &((new GDALPDFArrayRW()) + ->Add(nViewportId, 0))); + } + if (nLGIDictId) + { + oDictPage.Add("LGIDict", nLGIDictId, 0); + } + + if (bHasOGRData) + oDictPage.Add("StructParents", 0); + + VSIFPrintfL(fp, "%s\n", oDictPage.Serialize().c_str()); + EndObj(); + + oPageContext.poClippingDS = poClippingDS; + oPageContext.nPageId = nPageId; + oPageContext.nContentId = nContentId; + oPageContext.nResourcesId = nResourcesId; + oPageContext.nAnnotsId = nAnnotsId; + oPageContext.dfDPI = dfDPI; + oPageContext.sMargins = *psMargins; + oPageContext.eStreamCompressMethod = eStreamCompressMethod; + + return TRUE; +} + +/************************************************************************/ +/* WriteColorTable() */ +/************************************************************************/ + +int GDALPDFWriter::WriteColorTable(GDALDataset* poSrcDS) +{ + /* Does the source image has a color table ? */ + GDALColorTable* poCT = NULL; + if (poSrcDS->GetRasterCount() > 0) + poCT = poSrcDS->GetRasterBand(1)->GetColorTable(); + int nColorTableId = 0; + if (poCT != NULL && poCT->GetColorEntryCount() <= 256) + { + int nColors = poCT->GetColorEntryCount(); + nColorTableId = AllocNewObject(); + + int nLookupTableId = AllocNewObject(); + + /* Index object */ + StartObj(nColorTableId); + { + GDALPDFArrayRW oArray; + oArray.Add(GDALPDFObjectRW::CreateName("Indexed")) + .Add(&((new GDALPDFArrayRW())->Add(GDALPDFObjectRW::CreateName("DeviceRGB")))) + .Add(nColors-1) + .Add(nLookupTableId, 0); + VSIFPrintfL(fp, "%s\n", oArray.Serialize().c_str()); + } + EndObj(); + + /* Lookup table object */ + StartObj(nLookupTableId); + { + GDALPDFDictionaryRW oDict; + oDict.Add("Length", nColors * 3); + VSIFPrintfL(fp, "%s %% Lookup table\n", oDict.Serialize().c_str()); + } + VSIFPrintfL(fp, "stream\n"); + GByte pabyLookup[768]; + for(int i=0;iGetColorEntry(i); + pabyLookup[3 * i + 0] = (GByte)poEntry->c1; + pabyLookup[3 * i + 1] = (GByte)poEntry->c2; + pabyLookup[3 * i + 2] = (GByte)poEntry->c3; + } + VSIFWriteL(pabyLookup, 3 * nColors, 1, fp); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "endstream\n"); + EndObj(); + } + + return nColorTableId; +} + +/************************************************************************/ +/* WriteImagery() */ +/************************************************************************/ + +int GDALPDFWriter::WriteImagery(GDALDataset* poDS, + const char* pszLayerName, + PDFCompressMethod eCompressMethod, + int nPredictor, + int nJPEGQuality, + const char* pszJPEG2000_DRIVER, + int nBlockXSize, int nBlockYSize, + GDALProgressFunc pfnProgress, + void * pProgressData) +{ + int nWidth = poDS->GetRasterXSize(); + int nHeight = poDS->GetRasterYSize(); + double dfUserUnit = oPageContext.dfDPI * USER_UNIT_IN_INCH; + + GDALPDFRasterDesc oRasterDesc; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + oRasterDesc.nOCGRasterId = WriteOCG(pszLayerName); + + /* Does the source image has a color table ? */ + int nColorTableId = WriteColorTable(poDS); + + int nXBlocks = (nWidth + nBlockXSize - 1) / nBlockXSize; + int nYBlocks = (nHeight + nBlockYSize - 1) / nBlockYSize; + int nBlocks = nXBlocks * nYBlocks; + int nBlockXOff, nBlockYOff; + for(nBlockYOff = 0; nBlockYOff < nYBlocks; nBlockYOff ++) + { + for(nBlockXOff = 0; nBlockXOff < nXBlocks; nBlockXOff ++) + { + int nReqWidth = MIN(nBlockXSize, nWidth - nBlockXOff * nBlockXSize); + int nReqHeight = MIN(nBlockYSize, nHeight - nBlockYOff * nBlockYSize); + int iImage = nBlockYOff * nXBlocks + nBlockXOff; + + void* pScaledData = GDALCreateScaledProgress( iImage / (double)nBlocks, + (iImage + 1) / (double)nBlocks, + pfnProgress, pProgressData); + int nX = nBlockXOff * nBlockXSize; + int nY = nBlockYOff * nBlockYSize; + + int nImageId = WriteBlock(poDS, + nX, + nY, + nReqWidth, nReqHeight, + nColorTableId, + eCompressMethod, + nPredictor, + nJPEGQuality, + pszJPEG2000_DRIVER, + GDALScaledProgress, + pScaledData); + + GDALDestroyScaledProgress(pScaledData); + + if (nImageId == 0) + return FALSE; + + GDALPDFImageDesc oImageDesc; + oImageDesc.nImageId = nImageId; + oImageDesc.dfXOff = nX / dfUserUnit + oPageContext.sMargins.nLeft; + oImageDesc.dfYOff = (nHeight - nY - nReqHeight) / dfUserUnit + oPageContext.sMargins.nBottom; + oImageDesc.dfXSize = nReqWidth / dfUserUnit; + oImageDesc.dfYSize = nReqHeight / dfUserUnit; + + oRasterDesc.asImageDesc.push_back(oImageDesc); + } + } + + oPageContext.asRasterDesc.push_back(oRasterDesc); + + return TRUE; +} + +/************************************************************************/ +/* WriteClippedImagery() */ +/************************************************************************/ + +int GDALPDFWriter::WriteClippedImagery( + GDALDataset* poDS, + const char* pszLayerName, + PDFCompressMethod eCompressMethod, + int nPredictor, + int nJPEGQuality, + const char* pszJPEG2000_DRIVER, + int nBlockXSize, int nBlockYSize, + GDALProgressFunc pfnProgress, + void * pProgressData) +{ + double dfUserUnit = oPageContext.dfDPI * USER_UNIT_IN_INCH; + + GDALPDFRasterDesc oRasterDesc; + + /* Get clipping dataset bounding-box */ + double adfClippingGeoTransform[6]; + GDALDataset* poClippingDS = oPageContext.poClippingDS; + poClippingDS->GetGeoTransform(adfClippingGeoTransform); + int nClippingWidth = poClippingDS->GetRasterXSize(); + int nClippingHeight = poClippingDS->GetRasterYSize(); + double dfClippingMinX = adfClippingGeoTransform[0]; + double dfClippingMaxX = dfClippingMinX + nClippingWidth * adfClippingGeoTransform[1]; + double dfClippingMaxY = adfClippingGeoTransform[3]; + double dfClippingMinY = dfClippingMaxY + nClippingHeight * adfClippingGeoTransform[5]; + + if( dfClippingMaxY < dfClippingMinY ) + { + double dfTmp = dfClippingMinY; + dfClippingMinY = dfClippingMaxY; + dfClippingMaxY = dfTmp; + } + + /* Get current dataset dataset bounding-box */ + double adfGeoTransform[6]; + poDS->GetGeoTransform(adfGeoTransform); + int nWidth = poDS->GetRasterXSize(); + int nHeight = poDS->GetRasterYSize(); + double dfRasterMinX = adfGeoTransform[0]; + //double dfRasterMaxX = dfRasterMinX + nWidth * adfGeoTransform[1]; + double dfRasterMaxY = adfGeoTransform[3]; + double dfRasterMinY = dfRasterMaxY + nHeight * adfGeoTransform[5]; + + if( dfRasterMaxY < dfRasterMinY ) + { + double dfTmp = dfRasterMinY; + dfRasterMinY = dfRasterMaxY; + dfRasterMaxY = dfTmp; + } + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + oRasterDesc.nOCGRasterId = WriteOCG(pszLayerName); + + /* Does the source image has a color table ? */ + int nColorTableId = WriteColorTable(poDS); + + int nXBlocks = (nWidth + nBlockXSize - 1) / nBlockXSize; + int nYBlocks = (nHeight + nBlockYSize - 1) / nBlockYSize; + int nBlocks = nXBlocks * nYBlocks; + int nBlockXOff, nBlockYOff; + for(nBlockYOff = 0; nBlockYOff < nYBlocks; nBlockYOff ++) + { + for(nBlockXOff = 0; nBlockXOff < nXBlocks; nBlockXOff ++) + { + int nReqWidth = MIN(nBlockXSize, nWidth - nBlockXOff * nBlockXSize); + int nReqHeight = MIN(nBlockYSize, nHeight - nBlockYOff * nBlockYSize); + int iImage = nBlockYOff * nXBlocks + nBlockXOff; + + void* pScaledData = GDALCreateScaledProgress( iImage / (double)nBlocks, + (iImage + 1) / (double)nBlocks, + pfnProgress, pProgressData); + + int nX = nBlockXOff * nBlockXSize; + int nY = nBlockYOff * nBlockYSize; + + /* Compute extent of block to write */ + double dfBlockMinX = adfGeoTransform[0] + nX * adfGeoTransform[1]; + double dfBlockMaxX = adfGeoTransform[0] + (nX + nReqWidth) * adfGeoTransform[1]; + double dfBlockMinY = adfGeoTransform[3] + (nY + nReqHeight) * adfGeoTransform[5]; + double dfBlockMaxY = adfGeoTransform[3] + nY * adfGeoTransform[5]; + + if( dfBlockMaxY < dfBlockMinY ) + { + double dfTmp = dfBlockMinY; + dfBlockMinY = dfBlockMaxY; + dfBlockMaxY = dfTmp; + } + + /* Clip the extent of the block with the extent of the main raster */ + double dfIntersectMinX = MAX(dfBlockMinX, dfClippingMinX); + double dfIntersectMinY = MAX(dfBlockMinY, dfClippingMinY); + double dfIntersectMaxX = MIN(dfBlockMaxX, dfClippingMaxX); + double dfIntersectMaxY = MIN(dfBlockMaxY, dfClippingMaxY); + + if( dfIntersectMinX < dfIntersectMaxX && + dfIntersectMinY < dfIntersectMaxY ) + { + /* Re-compute (x,y,width,height) subwindow of current raster from */ + /* the extent of the clipped block */ + nX = (int)((dfIntersectMinX - dfRasterMinX) / adfGeoTransform[1] + 0.5); + if( adfGeoTransform[5] < 0 ) + nY = (int)((dfRasterMaxY - dfIntersectMaxY) / (-adfGeoTransform[5]) + 0.5); + else + nY = (int)((dfIntersectMinY - dfRasterMinY) / adfGeoTransform[5] + 0.5); + nReqWidth = (int)((dfIntersectMaxX - dfRasterMinX) / adfGeoTransform[1] + 0.5) - nX; + if( adfGeoTransform[5] < 0 ) + nReqHeight = (int)((dfRasterMaxY - dfIntersectMinY) / (-adfGeoTransform[5]) + 0.5) - nY; + else + nReqHeight = (int)((dfIntersectMaxY - dfRasterMinY) / adfGeoTransform[5] + 0.5) - nY; + + if( nReqWidth > 0 && nReqHeight > 0) + { + int nImageId = WriteBlock(poDS, + nX, + nY, + nReqWidth, nReqHeight, + nColorTableId, + eCompressMethod, + nPredictor, + nJPEGQuality, + pszJPEG2000_DRIVER, + GDALScaledProgress, + pScaledData); + + if (nImageId == 0) + { + GDALDestroyScaledProgress(pScaledData); + return FALSE; + } + + /* Compute the subwindow in image coordinates of the main raster corresponding */ + /* to the extent of the clipped block */ + double dfXInClippingUnits, dfYInClippingUnits, dfReqWidthInClippingUnits, dfReqHeightInClippingUnits; + + dfXInClippingUnits = (dfIntersectMinX - dfClippingMinX) / adfClippingGeoTransform[1]; + if( adfClippingGeoTransform[5] < 0 ) + dfYInClippingUnits = (dfClippingMaxY - dfIntersectMaxY) / (-adfClippingGeoTransform[5]); + else + dfYInClippingUnits = (dfIntersectMinY - dfClippingMinY) / adfClippingGeoTransform[5]; + dfReqWidthInClippingUnits = (dfIntersectMaxX - dfClippingMinX) / adfClippingGeoTransform[1] - dfXInClippingUnits; + if( adfClippingGeoTransform[5] < 0 ) + dfReqHeightInClippingUnits = (dfClippingMaxY - dfIntersectMinY) / (-adfClippingGeoTransform[5]) - dfYInClippingUnits; + else + dfReqHeightInClippingUnits = (dfIntersectMaxY - dfClippingMinY) / adfClippingGeoTransform[5] - dfYInClippingUnits; + + GDALPDFImageDesc oImageDesc; + oImageDesc.nImageId = nImageId; + oImageDesc.dfXOff = dfXInClippingUnits / dfUserUnit + oPageContext.sMargins.nLeft; + oImageDesc.dfYOff = (nClippingHeight - dfYInClippingUnits - dfReqHeightInClippingUnits) / dfUserUnit + oPageContext.sMargins.nBottom; + oImageDesc.dfXSize = dfReqWidthInClippingUnits / dfUserUnit; + oImageDesc.dfYSize = dfReqHeightInClippingUnits / dfUserUnit; + + oRasterDesc.asImageDesc.push_back(oImageDesc); + } + } + + GDALDestroyScaledProgress(pScaledData); + } + } + + oPageContext.asRasterDesc.push_back(oRasterDesc); + + return TRUE; +} + +#ifdef OGR_ENABLED + +/************************************************************************/ +/* WriteOGRDataSource() */ +/************************************************************************/ + +int GDALPDFWriter::WriteOGRDataSource(const char* pszOGRDataSource, + const char* pszOGRDisplayField, + const char* pszOGRDisplayLayerNames, + const char* pszOGRLinkField, + int bWriteOGRAttributes) +{ + if (OGRGetDriverCount() == 0) + OGRRegisterAll(); + + OGRDataSourceH hDS = OGROpen(pszOGRDataSource, 0, NULL); + if (hDS == NULL) + return FALSE; + + int iObj = 0; + + int nLayers = OGR_DS_GetLayerCount(hDS); + + char** papszLayerNames = CSLTokenizeString2(pszOGRDisplayLayerNames,",",0); + + for(int iLayer = 0; iLayer < nLayers; iLayer ++) + { + CPLString osLayerName; + if (CSLCount(papszLayerNames) < nLayers) + osLayerName = OGR_L_GetName(OGR_DS_GetLayer(hDS, iLayer)); + else + osLayerName = papszLayerNames[iLayer]; + + WriteOGRLayer(hDS, iLayer, + pszOGRDisplayField, + pszOGRLinkField, + osLayerName, + bWriteOGRAttributes, + iObj); + } + + OGRReleaseDataSource(hDS); + + CSLDestroy(papszLayerNames); + + return TRUE; +} + +/************************************************************************/ +/* StartOGRLayer() */ +/************************************************************************/ + +GDALPDFLayerDesc GDALPDFWriter::StartOGRLayer(CPLString osLayerName, + int bWriteOGRAttributes) +{ + GDALPDFLayerDesc osVectorDesc; + osVectorDesc.osLayerName = osLayerName; + osVectorDesc.bWriteOGRAttributes = bWriteOGRAttributes; + osVectorDesc.nOGCId = WriteOCG(osLayerName); + osVectorDesc.nFeatureLayerId = (bWriteOGRAttributes) ? AllocNewObject() : 0; + osVectorDesc.nOCGTextId = 0; + + return osVectorDesc; +} + +/************************************************************************/ +/* EndOGRLayer() */ +/************************************************************************/ + +void GDALPDFWriter::EndOGRLayer(GDALPDFLayerDesc& osVectorDesc) +{ + if (osVectorDesc.bWriteOGRAttributes) + { + StartObj(osVectorDesc.nFeatureLayerId); + + GDALPDFDictionaryRW oDict; + oDict.Add("A", &(new GDALPDFDictionaryRW())->Add("O", + GDALPDFObjectRW::CreateName("UserProperties"))); + + GDALPDFArrayRW* poArray = new GDALPDFArrayRW(); + oDict.Add("K", poArray); + + for(int i = 0; i < (int)osVectorDesc.aUserPropertiesIds.size(); i++) + { + poArray->Add(osVectorDesc.aUserPropertiesIds[i], 0); + } + + if (nStructTreeRootId == 0) + nStructTreeRootId = AllocNewObject(); + + oDict.Add("P", nStructTreeRootId, 0); + oDict.Add("S", GDALPDFObjectRW::CreateName("Feature")); + oDict.Add("T", osVectorDesc.osLayerName); + + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + + EndObj(); + } + + oPageContext.asVectorDesc.push_back(osVectorDesc); +} + +/************************************************************************/ +/* WriteOGRLayer() */ +/************************************************************************/ + +int GDALPDFWriter::WriteOGRLayer(OGRDataSourceH hDS, + int iLayer, + const char* pszOGRDisplayField, + const char* pszOGRLinkField, + CPLString osLayerName, + int bWriteOGRAttributes, + int& iObj) +{ + GDALDataset* poClippingDS = oPageContext.poClippingDS; + double adfGeoTransform[6]; + if (poClippingDS->GetGeoTransform(adfGeoTransform) != CE_None) + return FALSE; + + GDALPDFLayerDesc osVectorDesc = StartOGRLayer(osLayerName, + bWriteOGRAttributes); + OGRLayerH hLyr = OGR_DS_GetLayer(hDS, iLayer); + + const char* pszWKT = poClippingDS->GetProjectionRef(); + OGRSpatialReferenceH hGDAL_SRS = NULL; + if( pszWKT && pszWKT[0] != '\0' ) + hGDAL_SRS = OSRNewSpatialReference(pszWKT); + OGRSpatialReferenceH hOGR_SRS = OGR_L_GetSpatialRef(hLyr); + OGRCoordinateTransformationH hCT = NULL; + + if( hGDAL_SRS == NULL && hOGR_SRS != NULL ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Vector layer has a SRS set, but Raster layer has no SRS set. Assuming they are the same."); + } + else if( hGDAL_SRS != NULL && hOGR_SRS == NULL ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Vector layer has no SRS set, but Raster layer has a SRS set. Assuming they are the same."); + } + else if( hGDAL_SRS != NULL && hOGR_SRS != NULL ) + { + if (!OSRIsSame(hGDAL_SRS, hOGR_SRS)) + { + hCT = OCTNewCoordinateTransformation( hOGR_SRS, hGDAL_SRS ); + if( hCT == NULL ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot compute coordinate transformation from vector SRS to raster SRS"); + } + } + } + + if( hCT == NULL ) + { + double dfXMin = adfGeoTransform[0]; + double dfYMin = adfGeoTransform[3] + poClippingDS->GetRasterYSize() * adfGeoTransform[5]; + double dfXMax = adfGeoTransform[0] + poClippingDS->GetRasterXSize() * adfGeoTransform[1]; + double dfYMax = adfGeoTransform[3]; + OGR_L_SetSpatialFilterRect(hLyr, dfXMin, dfYMin, dfXMax, dfYMax); + } + + OGRFeatureH hFeat; + int iObjLayer = 0; + + while( (hFeat = OGR_L_GetNextFeature(hLyr)) != NULL) + { + WriteOGRFeature(osVectorDesc, + hFeat, + hCT, + pszOGRDisplayField, + pszOGRLinkField, + bWriteOGRAttributes, + iObj, + iObjLayer); + + OGR_F_Destroy(hFeat); + } + + EndOGRLayer(osVectorDesc); + + if( hCT != NULL ) + OCTDestroyCoordinateTransformation(hCT); + if( hGDAL_SRS != NULL ) + OSRDestroySpatialReference(hGDAL_SRS); + + return TRUE; +} + +/************************************************************************/ +/* DrawGeometry() */ +/************************************************************************/ + +static void DrawGeometry(VSILFILE* fp, OGRGeometryH hGeom, double adfMatrix[4], int bPaint = TRUE) +{ + switch(wkbFlatten(OGR_G_GetGeometryType(hGeom))) + { + case wkbLineString: + { + int nPoints = OGR_G_GetPointCount(hGeom); + for(int i=0;iGetRasterYSize(); + double dfUserUnit = oPageContext.dfDPI * USER_UNIT_IN_INCH; + double adfGeoTransform[6]; + poClippingDS->GetGeoTransform(adfGeoTransform); + + double adfMatrix[4]; + adfMatrix[0] = - adfGeoTransform[0] / (adfGeoTransform[1] * dfUserUnit) + oPageContext.sMargins.nLeft; + adfMatrix[1] = 1.0 / (adfGeoTransform[1] * dfUserUnit); + adfMatrix[2] = - (adfGeoTransform[3] + adfGeoTransform[5] * nHeight) / (-adfGeoTransform[5] * dfUserUnit) + oPageContext.sMargins.nBottom; + adfMatrix[3] = 1.0 / (-adfGeoTransform[5] * dfUserUnit); + + OGRGeometryH hGeom = OGR_F_GetGeometryRef(hFeat); + if (hGeom == NULL) + { + return TRUE; + } + + OGREnvelope sEnvelope; + + if( hCT != NULL ) + { + /* Reproject */ + if( OGR_G_Transform(hGeom, hCT) != OGRERR_NONE ) + { + return TRUE; + } + + OGREnvelope sRasterEnvelope; + sRasterEnvelope.MinX = adfGeoTransform[0]; + sRasterEnvelope.MinY = adfGeoTransform[3] + poClippingDS->GetRasterYSize() * adfGeoTransform[5]; + sRasterEnvelope.MaxX = adfGeoTransform[0] + poClippingDS->GetRasterXSize() * adfGeoTransform[1]; + sRasterEnvelope.MaxY = adfGeoTransform[3]; + + /* Check that the reprojected geometry interescts the raster envelope */ + OGR_G_GetEnvelope(hGeom, &sEnvelope); + if( !(sRasterEnvelope.Intersects(sEnvelope)) ) + { + return TRUE; + } + } + else + { + OGR_G_GetEnvelope(hGeom, &sEnvelope); + } + + /* -------------------------------------------------------------- */ + /* Get style */ + /* -------------------------------------------------------------- */ + int nPenR = 0, nPenG = 0, nPenB = 0, nPenA = 255; + int nBrushR = 127, nBrushG = 127, nBrushB = 127, nBrushA = 127; + int nTextR = 0, nTextG = 0, nTextB = 0, nTextA = 255; + int bSymbolColorDefined = FALSE; + int nSymbolR = 0, nSymbolG = 0, nSymbolB = 0, nSymbolA = 255; + double dfTextSize = 12, dfTextAngle = 0, dfTextDx = 0, dfTextDy = 0; + double dfPenWidth = 1; + double dfSymbolSize = 5; + CPLString osDashArray; + CPLString osLabelText; + CPLString osSymbolId; + int nImageSymbolId = 0, nImageWidth = 0, nImageHeight = 0; + + OGRStyleMgrH hSM = OGR_SM_Create(NULL); + OGR_SM_InitFromFeature(hSM, hFeat); + int nCount = OGR_SM_GetPartCount(hSM, NULL); + for(int iPart = 0; iPart < nCount; iPart++) + { + OGRStyleToolH hTool = OGR_SM_GetPart(hSM, iPart, NULL); + if (hTool) + { + if (OGR_ST_GetType(hTool) == OGRSTCPen) + { + int bIsNull = TRUE; + const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTPenColor, &bIsNull); + if (pszColor && !bIsNull) + { + int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255; + int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha); + if (nVals >= 3) + { + nPenR = nRed; + nPenG = nGreen; + nPenB = nBlue; + if (nVals == 4) + nPenA = nAlpha; + } + } + + const char* pszDash = OGR_ST_GetParamStr(hTool, OGRSTPenPattern, &bIsNull); + if (pszDash && !bIsNull) + { + char** papszTokens = CSLTokenizeString2(pszDash, " ", 0); + int nTokens = CSLCount(papszTokens); + if ((nTokens % 2) == 0) + { + for(int i=0;i= 3) + { + nBrushR = nRed; + nBrushG = nGreen; + nBrushB = nBlue; + if (nVals == 4) + nBrushA = nAlpha; + } + } + } + else if (OGR_ST_GetType(hTool) == OGRSTCLabel) + { + int bIsNull; + const char* pszStr = OGR_ST_GetParamStr(hTool, OGRSTLabelTextString, &bIsNull); + if (pszStr) + { + osLabelText = pszStr; + + /* If the text is of the form {stuff}, then it means we want to fetch */ + /* the value of the field "stuff" in the feature */ + if( osLabelText.size() && osLabelText[0] == '{' && + osLabelText[osLabelText.size() - 1] == '}' ) + { + osLabelText = pszStr + 1; + osLabelText.resize(osLabelText.size() - 1); + + int nIdxField = OGR_F_GetFieldIndex(hFeat, osLabelText); + if( nIdxField >= 0 ) + osLabelText = OGR_F_GetFieldAsString(hFeat, nIdxField); + else + osLabelText = ""; + } + } + + const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTLabelFColor, &bIsNull); + if (pszColor && !bIsNull) + { + int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255; + int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha); + if (nVals >= 3) + { + nTextR = nRed; + nTextG = nGreen; + nTextB = nBlue; + if (nVals == 4) + nTextA = nAlpha; + } + } + + double dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelSize, &bIsNull); + if (!bIsNull) + { + dfTextSize = dfVal; + } + + dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelAngle, &bIsNull); + if (!bIsNull) + { + dfTextAngle = dfVal; + } + + dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelDx, &bIsNull); + if (!bIsNull) + { + dfTextDx = dfVal; + } + + dfVal = OGR_ST_GetParamDbl(hTool, OGRSTLabelDy, &bIsNull); + if (!bIsNull) + { + dfTextDy = dfVal; + } + + } + else if (OGR_ST_GetType(hTool) == OGRSTCSymbol) + { + int bIsNull; + const char* pszSymbolId = OGR_ST_GetParamStr(hTool, OGRSTSymbolId, &bIsNull); + if (pszSymbolId && !bIsNull) + { + osSymbolId = pszSymbolId; + + if (strstr(pszSymbolId, "ogr-sym-") == NULL) + { + if (oMapSymbolFilenameToDesc.find(osSymbolId) == oMapSymbolFilenameToDesc.end()) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + GDALDatasetH hImageDS = GDALOpen(osSymbolId, GA_ReadOnly); + CPLPopErrorHandler(); + if (hImageDS != NULL) + { + nImageWidth = GDALGetRasterXSize(hImageDS); + nImageHeight = GDALGetRasterYSize(hImageDS); + + nImageSymbolId = WriteBlock((GDALDataset*) hImageDS, + 0, 0, + nImageWidth, + nImageHeight, + 0, + COMPRESS_DEFAULT, + 0, + -1, + NULL, + NULL, + NULL); + GDALClose(hImageDS); + } + + GDALPDFImageDesc oDesc; + oDesc.nImageId = nImageSymbolId; + oDesc.dfXOff = 0; + oDesc.dfYOff = 0; + oDesc.dfXSize = nImageWidth; + oDesc.dfYSize = nImageHeight; + oMapSymbolFilenameToDesc[osSymbolId] = oDesc; + } + else + { + GDALPDFImageDesc& oDesc = oMapSymbolFilenameToDesc[osSymbolId]; + nImageSymbolId = oDesc.nImageId; + nImageWidth = (int)oDesc.dfXSize; + nImageHeight = (int)oDesc.dfYSize; + } + } + } + + double dfVal = OGR_ST_GetParamDbl(hTool, OGRSTSymbolSize, &bIsNull); + if (!bIsNull) + { + dfSymbolSize = dfVal; + } + + const char* pszColor = OGR_ST_GetParamStr(hTool, OGRSTSymbolColor, &bIsNull); + if (pszColor && !bIsNull) + { + int nRed = 0, nGreen = 0, nBlue = 0, nAlpha = 255; + int nVals = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue,&nAlpha); + if (nVals >= 3) + { + bSymbolColorDefined = TRUE; + nSymbolR = nRed; + nSymbolG = nGreen; + nSymbolB = nBlue; + if (nVals == 4) + nSymbolA = nAlpha; + } + } + } + + OGR_ST_Destroy(hTool); + } + } + OGR_SM_Destroy(hSM); + + if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint && bSymbolColorDefined) + { + nPenR = nSymbolR; + nPenG = nSymbolG; + nPenB = nSymbolB; + nPenA = nSymbolA; + nBrushR = nSymbolR; + nBrushG = nSymbolG; + nBrushB = nSymbolB; + nBrushA = nSymbolA; + } + + double dfRadius = dfSymbolSize * dfUserUnit; + + /* -------------------------------------------------------------- */ + /* Write object dictionary */ + /* -------------------------------------------------------------- */ + int nObjectId = AllocNewObject(); + int nObjectLengthId = AllocNewObject(); + + osVectorDesc.aIds.push_back(nObjectId); + + int bboxXMin, bboxYMin, bboxXMax, bboxYMax; + if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint && nImageSymbolId != 0) + { + bboxXMin = (int)floor(sEnvelope.MinX * adfMatrix[1] + adfMatrix[0] - nImageWidth / 2); + bboxYMin = (int)floor(sEnvelope.MinY * adfMatrix[3] + adfMatrix[2] - nImageHeight / 2); + bboxXMax = (int)ceil(sEnvelope.MaxX * adfMatrix[1] + adfMatrix[0] + nImageWidth / 2); + bboxYMax = (int)ceil(sEnvelope.MaxY * adfMatrix[3] + adfMatrix[2] + nImageHeight / 2); + } + else + { + double dfMargin = dfPenWidth; + if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint ) + { + if (osSymbolId == "ogr-sym-6" || + osSymbolId == "ogr-sym-7") + { + const double dfSqrt3 = 1.73205080757; + dfMargin += dfRadius * 2 * dfSqrt3 / 3; + } + else + dfMargin += dfRadius; + } + bboxXMin = (int)floor(sEnvelope.MinX * adfMatrix[1] + adfMatrix[0] - dfMargin); + bboxYMin = (int)floor(sEnvelope.MinY * adfMatrix[3] + adfMatrix[2] - dfMargin); + bboxXMax = (int)ceil(sEnvelope.MaxX * adfMatrix[1] + adfMatrix[0] + dfMargin); + bboxYMax = (int)ceil(sEnvelope.MaxY * adfMatrix[3] + adfMatrix[2] + dfMargin); + } + + int iField = -1; + const char* pszLinkVal = NULL; + if (pszOGRLinkField != NULL && + (iField = OGR_FD_GetFieldIndex(OGR_F_GetDefnRef(hFeat), pszOGRLinkField)) >= 0 && + OGR_F_IsFieldSet(hFeat, iField) && + strcmp((pszLinkVal = OGR_F_GetFieldAsString(hFeat, iField)), "") != 0) + { + int nAnnotId = AllocNewObject(); + oPageContext.anAnnotationsId.push_back(nAnnotId); + StartObj(nAnnotId); + { + GDALPDFDictionaryRW oDict; + oDict.Add("Type", GDALPDFObjectRW::CreateName("Annot")); + oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Link")); + oDict.Add("Rect", &(new GDALPDFArrayRW())->Add(bboxXMin).Add(bboxYMin).Add(bboxXMax).Add(bboxYMax)); + oDict.Add("A", &(new GDALPDFDictionaryRW())-> + Add("S", GDALPDFObjectRW::CreateName("URI")). + Add("URI", pszLinkVal)); + oDict.Add("BS", &(new GDALPDFDictionaryRW())-> + Add("Type", GDALPDFObjectRW::CreateName("Border")). + Add("S", GDALPDFObjectRW::CreateName("S")). + Add("W", 0)); + oDict.Add("Border", &(new GDALPDFArrayRW())->Add(0).Add(0).Add(0)); + oDict.Add("H", GDALPDFObjectRW::CreateName("I")); + + if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPolygon && + OGR_G_GetGeometryCount(hGeom) == 1 ) + { + OGRGeometryH hSubGeom = OGR_G_GetGeometryRef(hGeom, 0); + int nPoints = OGR_G_GetPointCount(hSubGeom); + if( nPoints == 4 || nPoints == 5 ) + { + std::vector adfX, adfY; + for(int i=0;i + Add(adfX[0]).Add(adfY[0]). + Add(adfX[1]).Add(adfY[1]). + Add(adfX[2]).Add(adfY[2]). + Add(adfX[0]).Add(adfY[0])); + } + else if( nPoints == 5 ) + { + oDict.Add("QuadPoints", &(new GDALPDFArrayRW())-> + Add(adfX[0]).Add(adfY[0]). + Add(adfX[1]).Add(adfY[1]). + Add(adfX[2]).Add(adfY[2]). + Add(adfX[3]).Add(adfY[3])); + } + } + } + + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + } + EndObj(); + } + + StartObj(nObjectId); + { + GDALPDFDictionaryRW oDict; + GDALPDFArrayRW* poBBOX = new GDALPDFArrayRW(); + poBBOX->Add(bboxXMin).Add(bboxYMin).Add(bboxXMax). Add(bboxYMax); + oDict.Add("Length", nObjectLengthId, 0) + .Add("Type", GDALPDFObjectRW::CreateName("XObject")) + .Add("BBox", poBBOX) + .Add("Subtype", GDALPDFObjectRW::CreateName("Form")); + if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) + { + oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode")); + } + + GDALPDFDictionaryRW* poGS1 = new GDALPDFDictionaryRW(); + poGS1->Add("Type", GDALPDFObjectRW::CreateName("ExtGState")); + if (nPenA != 255) + poGS1->Add("CA", (nPenA == 127 || nPenA == 128) ? 0.5 : nPenA / 255.0); + if (nBrushA != 255) + poGS1->Add("ca", (nBrushA == 127 || nBrushA == 128) ? 0.5 : nBrushA / 255.0 ); + + GDALPDFDictionaryRW* poExtGState = new GDALPDFDictionaryRW(); + poExtGState->Add("GS1", poGS1); + + GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW(); + poResources->Add("ExtGState", poExtGState); + + if( nImageSymbolId != 0 ) + { + GDALPDFDictionaryRW* poDictXObject = new GDALPDFDictionaryRW(); + poResources->Add("XObject", poDictXObject); + + poDictXObject->Add(CPLSPrintf("SymImage%d", nImageSymbolId), nImageSymbolId, 0); + } + + oDict.Add("Resources", poResources); + + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + } + + /* -------------------------------------------------------------- */ + /* Write object stream */ + /* -------------------------------------------------------------- */ + VSIFPrintfL(fp, "stream\n"); + + vsi_l_offset nStreamStart = VSIFTellL(fp); + + VSILFILE* fpGZip = NULL; + VSILFILE* fpBack = fp; + if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) + { + fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE ); + fp = fpGZip; + } + + VSIFPrintfL(fp, "q\n"); + + VSIFPrintfL(fp, "/GS1 gs\n"); + + if (nImageSymbolId == 0) + { + VSIFPrintfL(fp, "%f w\n" + "0 J\n" + "0 j\n" + "10 M\n" + "[%s]0 d\n", + dfPenWidth, + osDashArray.c_str()); + + VSIFPrintfL(fp, "%f %f %f RG\n", nPenR / 255.0, nPenG / 255.0, nPenB / 255.0); + VSIFPrintfL(fp, "%f %f %f rg\n", nBrushR / 255.0, nBrushG / 255.0, nBrushB / 255.0); + } + + if (wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint) + { + double dfX = OGR_G_GetX(hGeom, 0) * adfMatrix[1] + adfMatrix[0]; + double dfY = OGR_G_GetY(hGeom, 0) * adfMatrix[3] + adfMatrix[2]; + + if (nImageSymbolId != 0) + { + VSIFPrintfL(fp, "%d 0 0 %d %f %f cm\n", + nImageWidth, nImageHeight, + dfX - nImageWidth / 2, dfY - nImageHeight / 2); + VSIFPrintfL(fp, "/SymImage%d Do\n", nImageSymbolId); + } + else if (osSymbolId == "") + osSymbolId = "ogr-sym-3"; /* symbol by default */ + else if ( !(osSymbolId == "ogr-sym-0" || + osSymbolId == "ogr-sym-1" || + osSymbolId == "ogr-sym-2" || + osSymbolId == "ogr-sym-3" || + osSymbolId == "ogr-sym-4" || + osSymbolId == "ogr-sym-5" || + osSymbolId == "ogr-sym-6" || + osSymbolId == "ogr-sym-7" || + osSymbolId == "ogr-sym-8" || + osSymbolId == "ogr-sym-9") ) + { + CPLDebug("PDF", "Unhandled symbol id : %s. Using ogr-sym-3 instead", osSymbolId.c_str()); + osSymbolId = "ogr-sym-3"; + } + + if (osSymbolId == "ogr-sym-0") /* cross (+) */ + { + VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY); + VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY); + VSIFPrintfL(fp, "%f %f m\n", dfX, dfY - dfRadius); + VSIFPrintfL(fp, "%f %f l\n", dfX, dfY + dfRadius); + VSIFPrintfL(fp, "S\n"); + } + else if (osSymbolId == "ogr-sym-1") /* diagcross (X) */ + { + VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY - dfRadius); + VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY + dfRadius); + VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY + dfRadius); + VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY - dfRadius); + VSIFPrintfL(fp, "S\n"); + } + else if (osSymbolId == "ogr-sym-2" || + osSymbolId == "ogr-sym-3") /* circle */ + { + /* See http://www.whizkidtech.redprince.net/bezier/circle/kappa/ */ + const double dfKappa = 0.5522847498; + + VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY); + VSIFPrintfL(fp, "%f %f %f %f %f %f c\n", + dfX - dfRadius, dfY - dfRadius * dfKappa, + dfX - dfRadius * dfKappa, dfY - dfRadius, + dfX, dfY - dfRadius); + VSIFPrintfL(fp, "%f %f %f %f %f %f c\n", + dfX + dfRadius * dfKappa, dfY - dfRadius, + dfX + dfRadius, dfY - dfRadius * dfKappa, + dfX + dfRadius, dfY); + VSIFPrintfL(fp, "%f %f %f %f %f %f c\n", + dfX + dfRadius, dfY + dfRadius * dfKappa, + dfX + dfRadius * dfKappa, dfY + dfRadius, + dfX, dfY + dfRadius); + VSIFPrintfL(fp, "%f %f %f %f %f %f c\n", + dfX - dfRadius * dfKappa, dfY + dfRadius, + dfX - dfRadius, dfY + dfRadius * dfKappa, + dfX - dfRadius, dfY); + if (osSymbolId == "ogr-sym-2") + VSIFPrintfL(fp, "s\n"); /* not filled */ + else + VSIFPrintfL(fp, "b*\n"); /* filled */ + } + else if (osSymbolId == "ogr-sym-4" || + osSymbolId == "ogr-sym-5") /* square */ + { + VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY + dfRadius); + VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY + dfRadius); + VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY - dfRadius); + VSIFPrintfL(fp, "%f %f l\n", dfX - dfRadius, dfY - dfRadius); + if (osSymbolId == "ogr-sym-4") + VSIFPrintfL(fp, "s\n"); /* not filled */ + else + VSIFPrintfL(fp, "b*\n"); /* filled */ + } + else if (osSymbolId == "ogr-sym-6" || + osSymbolId == "ogr-sym-7") /* triangle */ + { + const double dfSqrt3 = 1.73205080757; + VSIFPrintfL(fp, "%f %f m\n", dfX - dfRadius, dfY - dfRadius * dfSqrt3 / 3); + VSIFPrintfL(fp, "%f %f l\n", dfX, dfY + 2 * dfRadius * dfSqrt3 / 3); + VSIFPrintfL(fp, "%f %f l\n", dfX + dfRadius, dfY - dfRadius * dfSqrt3 / 3); + if (osSymbolId == "ogr-sym-6") + VSIFPrintfL(fp, "s\n"); /* not filled */ + else + VSIFPrintfL(fp, "b*\n"); /* filled */ + } + else if (osSymbolId == "ogr-sym-8" || + osSymbolId == "ogr-sym-9") /* star */ + { + const double dfSin18divSin126 = 0.38196601125; + VSIFPrintfL(fp, "%f %f m\n", dfX, dfY + dfRadius); + for(int i=1; i<10;i++) + { + double dfFactor = ((i % 2) == 1) ? dfSin18divSin126 : 1.0; + VSIFPrintfL(fp, "%f %f l\n", + dfX + cos(M_PI / 2 - i * M_PI * 36 / 180) * dfRadius * dfFactor, + dfY + sin(M_PI / 2 - i * M_PI * 36 / 180) * dfRadius * dfFactor); + } + if (osSymbolId == "ogr-sym-8") + VSIFPrintfL(fp, "s\n"); /* not filled */ + else + VSIFPrintfL(fp, "b*\n"); /* filled */ + } + } + else + { + DrawGeometry(fp, hGeom, adfMatrix); + } + + VSIFPrintfL(fp, "Q"); + + if (fpGZip) + VSIFCloseL(fpGZip); + fp = fpBack; + + vsi_l_offset nStreamEnd = VSIFTellL(fp); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "endstream\n"); + EndObj(); + + StartObj(nObjectLengthId); + VSIFPrintfL(fp, + " %ld\n", + (long)(nStreamEnd - nStreamStart)); + EndObj(); + + /* -------------------------------------------------------------- */ + /* Write label */ + /* -------------------------------------------------------------- */ + if (osLabelText.size() && wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPoint) + { + if (osVectorDesc.nOCGTextId == 0) + osVectorDesc.nOCGTextId = WriteOCG("Text", osVectorDesc.nOGCId); + + /* -------------------------------------------------------------- */ + /* Write object dictionary */ + /* -------------------------------------------------------------- */ + nObjectId = AllocNewObject(); + nObjectLengthId = AllocNewObject(); + + osVectorDesc.aIdsText.push_back(nObjectId); + + StartObj(nObjectId); + { + GDALPDFDictionaryRW oDict; + + GDALDataset* poClippingDS = oPageContext.poClippingDS; + int nWidth = poClippingDS->GetRasterXSize(); + int nHeight = poClippingDS->GetRasterYSize(); + double dfUserUnit = oPageContext.dfDPI * USER_UNIT_IN_INCH; + double dfWidthInUserUnit = nWidth / dfUserUnit + oPageContext.sMargins.nLeft + oPageContext.sMargins.nRight; + double dfHeightInUserUnit = nHeight / dfUserUnit + oPageContext.sMargins.nBottom + oPageContext.sMargins.nTop; + + oDict.Add("Length", nObjectLengthId, 0) + .Add("Type", GDALPDFObjectRW::CreateName("XObject")) + .Add("BBox", &((new GDALPDFArrayRW()) + ->Add(0).Add(0)).Add(dfWidthInUserUnit).Add(dfHeightInUserUnit)) + .Add("Subtype", GDALPDFObjectRW::CreateName("Form")); + if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) + { + oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode")); + } + + GDALPDFDictionaryRW* poResources = new GDALPDFDictionaryRW(); + + if (nTextA != 255) + { + GDALPDFDictionaryRW* poGS1 = new GDALPDFDictionaryRW(); + poGS1->Add("Type", GDALPDFObjectRW::CreateName("ExtGState")); + poGS1->Add("ca", (nTextA == 127 || nTextA == 128) ? 0.5 : nTextA / 255.0); + + GDALPDFDictionaryRW* poExtGState = new GDALPDFDictionaryRW(); + poExtGState->Add("GS1", poGS1); + + poResources->Add("ExtGState", poExtGState); + } + + GDALPDFDictionaryRW* poDictFTimesRoman = NULL; + poDictFTimesRoman = new GDALPDFDictionaryRW(); + poDictFTimesRoman->Add("Type", GDALPDFObjectRW::CreateName("Font")); + poDictFTimesRoman->Add("BaseFont", GDALPDFObjectRW::CreateName("Times-Roman")); + poDictFTimesRoman->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding")); + poDictFTimesRoman->Add("Subtype", GDALPDFObjectRW::CreateName("Type1")); + + GDALPDFDictionaryRW* poDictFont = new GDALPDFDictionaryRW(); + if (poDictFTimesRoman) + poDictFont->Add("FTimesRoman", poDictFTimesRoman); + poResources->Add("Font", poDictFont); + + oDict.Add("Resources", poResources); + + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + } + + /* -------------------------------------------------------------- */ + /* Write object stream */ + /* -------------------------------------------------------------- */ + VSIFPrintfL(fp, "stream\n"); + + vsi_l_offset nStreamStart = VSIFTellL(fp); + + VSILFILE* fpGZip = NULL; + VSILFILE* fpBack = fp; + if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) + { + fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE ); + fp = fpGZip; + } + + double dfX = OGR_G_GetX(hGeom, 0) * adfMatrix[1] + adfMatrix[0] + dfTextDx; + double dfY = OGR_G_GetY(hGeom, 0) * adfMatrix[3] + adfMatrix[2] + dfTextDy; + + VSIFPrintfL(fp, "q\n"); + VSIFPrintfL(fp, "BT\n"); + if (nTextA != 255) + { + VSIFPrintfL(fp, "/GS1 gs\n"); + } + if (dfTextAngle == 0) + { + VSIFPrintfL(fp, "%f %f Td\n", dfX, dfY); + } + else + { + dfTextAngle = - dfTextAngle * M_PI / 180.0; + VSIFPrintfL(fp, "%f %f %f %f %f %f Tm\n", + cos(dfTextAngle), -sin(dfTextAngle), + sin(dfTextAngle), cos(dfTextAngle), + dfX, dfY); + } + VSIFPrintfL(fp, "%f %f %f rg\n", nTextR / 255.0, nTextG / 255.0, nTextB / 255.0); + VSIFPrintfL(fp, "/FTimesRoman %f Tf\n", dfTextSize); + VSIFPrintfL(fp, "("); + for(size_t i=0;i= 32 && osLabelText[i] <= 127) { */ + VSIFPrintfL(fp, "%c", osLabelText[i]); + /* } else { + VSIFPrintfL(fp, "_"); + } */ + } + VSIFPrintfL(fp, ") Tj\n"); + VSIFPrintfL(fp, "ET\n"); + VSIFPrintfL(fp, "Q"); + + if (fpGZip) + VSIFCloseL(fpGZip); + fp = fpBack; + + vsi_l_offset nStreamEnd = VSIFTellL(fp); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "endstream\n"); + EndObj(); + + StartObj(nObjectLengthId); + VSIFPrintfL(fp, + " %ld\n", + (long)(nStreamEnd - nStreamStart)); + EndObj(); + } + else + { + osVectorDesc.aIdsText.push_back(0); + } + + /* -------------------------------------------------------------- */ + /* Write feature attributes */ + /* -------------------------------------------------------------- */ + int nFeatureUserProperties = 0; + + CPLString osFeatureName; + + if (bWriteOGRAttributes) + { + int iField = -1; + if (pszOGRDisplayField && + (iField = OGR_FD_GetFieldIndex(OGR_F_GetDefnRef(hFeat), pszOGRDisplayField)) >= 0) + osFeatureName = OGR_F_GetFieldAsString(hFeat, iField); + else + osFeatureName = CPLSPrintf("feature%d", iObjLayer + 1); + + nFeatureUserProperties = AllocNewObject(); + StartObj(nFeatureUserProperties); + + GDALPDFDictionaryRW oDict; + GDALPDFDictionaryRW* poDictA = new GDALPDFDictionaryRW(); + oDict.Add("A", poDictA); + poDictA->Add("O", GDALPDFObjectRW::CreateName("UserProperties")); + + int nFields = OGR_F_GetFieldCount(hFeat); + GDALPDFArrayRW* poArray = new GDALPDFArrayRW(); + for(int i = 0; i < nFields; i++) + { + if (OGR_F_IsFieldSet(hFeat, i)) + { + OGRFieldDefnH hFDefn = OGR_F_GetFieldDefnRef( hFeat, i ); + GDALPDFDictionaryRW* poKV = new GDALPDFDictionaryRW(); + poKV->Add("N", OGR_Fld_GetNameRef(hFDefn)); + if (OGR_Fld_GetType(hFDefn) == OFTInteger) + poKV->Add("V", OGR_F_GetFieldAsInteger(hFeat, i)); + else if (OGR_Fld_GetType(hFDefn) == OFTReal) + poKV->Add("V", OGR_F_GetFieldAsDouble(hFeat, i)); + else + poKV->Add("V", OGR_F_GetFieldAsString(hFeat, i)); + poArray->Add(poKV); + } + } + + poDictA->Add("P", poArray); + + oDict.Add("K", iObj); + oDict.Add("P", osVectorDesc.nFeatureLayerId, 0); + oDict.Add("Pg", oPageContext.nPageId, 0); + oDict.Add("S", GDALPDFObjectRW::CreateName("feature")); + oDict.Add("T", osFeatureName); + + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + + EndObj(); + } + + iObj ++; + iObjLayer ++; + + osVectorDesc.aUserPropertiesIds.push_back(nFeatureUserProperties); + osVectorDesc.aFeatureNames.push_back(osFeatureName); + + return TRUE; +} + +#endif + +/************************************************************************/ +/* EndPage() */ +/************************************************************************/ + +int GDALPDFWriter::EndPage(const char* pszExtraImages, + const char* pszExtraStream, + const char* pszExtraLayerName, + const char* pszOffLayers, + const char* pszExclusiveLayers) +{ + int nLayerExtraId = WriteOCG(pszExtraLayerName); + if( pszOffLayers ) + osOffLayers = pszOffLayers; + if( pszExclusiveLayers ) + osExclusiveLayers = pszExclusiveLayers; + + int bHasTimesRoman = pszExtraStream && strstr(pszExtraStream, "/FTimesRoman"); + int bHasTimesBold = pszExtraStream && strstr(pszExtraStream, "/FTimesBold"); + + /* -------------------------------------------------------------- */ + /* Write extra images */ + /* -------------------------------------------------------------- */ + std::vector asExtraImageDesc; + if (pszExtraImages) + { + if( GDALGetDriverCount() == 0 ) + GDALAllRegister(); + + char** papszExtraImagesTokens = CSLTokenizeString2(pszExtraImages, ",", 0); + double dfUserUnit = oPageContext.dfDPI * USER_UNIT_IN_INCH; + int nCount = CSLCount(papszExtraImagesTokens); + for(int i=0;i+4<=nCount; /* */) + { + const char* pszImageFilename = papszExtraImagesTokens[i+0]; + double dfX = CPLAtof(papszExtraImagesTokens[i+1]); + double dfY = CPLAtof(papszExtraImagesTokens[i+2]); + double dfScale = CPLAtof(papszExtraImagesTokens[i+3]); + const char* pszLinkVal = NULL; + i += 4; + if( i < nCount && EQUALN(papszExtraImagesTokens[i],"link=",5) ) + { + pszLinkVal = papszExtraImagesTokens[i] + 5; + i++; + } + GDALDataset* poImageDS = (GDALDataset* )GDALOpen(pszImageFilename, GA_ReadOnly); + if (poImageDS) + { + int nImageId = WriteBlock( poImageDS, + 0, 0, + poImageDS->GetRasterXSize(), + poImageDS->GetRasterYSize(), + 0, + COMPRESS_DEFAULT, + 0, + -1, + NULL, + NULL, + NULL ); + + if (nImageId) + { + GDALPDFImageDesc oImageDesc; + oImageDesc.nImageId = nImageId; + oImageDesc.dfXSize = poImageDS->GetRasterXSize() / dfUserUnit * dfScale; + oImageDesc.dfYSize = poImageDS->GetRasterYSize() / dfUserUnit * dfScale; + oImageDesc.dfXOff = dfX; + oImageDesc.dfYOff = dfY; + + asExtraImageDesc.push_back(oImageDesc); + + if( pszLinkVal != NULL ) + { + int nAnnotId = AllocNewObject(); + oPageContext.anAnnotationsId.push_back(nAnnotId); + StartObj(nAnnotId); + { + GDALPDFDictionaryRW oDict; + oDict.Add("Type", GDALPDFObjectRW::CreateName("Annot")); + oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Link")); + oDict.Add("Rect", &(new GDALPDFArrayRW())-> + Add(oImageDesc.dfXOff). + Add(oImageDesc.dfYOff). + Add(oImageDesc.dfXOff + oImageDesc.dfXSize). + Add(oImageDesc.dfYOff + oImageDesc.dfYSize)); + oDict.Add("A", &(new GDALPDFDictionaryRW())-> + Add("S", GDALPDFObjectRW::CreateName("URI")). + Add("URI", pszLinkVal)); + oDict.Add("BS", &(new GDALPDFDictionaryRW())-> + Add("Type", GDALPDFObjectRW::CreateName("Border")). + Add("S", GDALPDFObjectRW::CreateName("S")). + Add("W", 0)); + oDict.Add("Border", &(new GDALPDFArrayRW())->Add(0).Add(0).Add(0)); + oDict.Add("H", GDALPDFObjectRW::CreateName("I")); + + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + } + EndObj(); + } + } + + GDALClose(poImageDS); + } + } + CSLDestroy(papszExtraImagesTokens); + } + + /* -------------------------------------------------------------- */ + /* Write content dictionary */ + /* -------------------------------------------------------------- */ + int nContentLengthId = AllocNewObject(); + + StartObj(oPageContext.nContentId); + { + GDALPDFDictionaryRW oDict; + oDict.Add("Length", nContentLengthId, 0); + if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) + { + oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode")); + } + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + } + + /* -------------------------------------------------------------- */ + /* Write content stream */ + /* -------------------------------------------------------------- */ + VSIFPrintfL(fp, "stream\n"); + vsi_l_offset nStreamStart = VSIFTellL(fp); + + VSILFILE* fpGZip = NULL; + VSILFILE* fpBack = fp; + if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) + { + fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE ); + fp = fpGZip; + } + + /* -------------------------------------------------------------- */ + /* Write drawing instructions for raster blocks */ + /* -------------------------------------------------------------- */ + for(size_t iRaster = 0; iRaster < oPageContext.asRasterDesc.size(); iRaster++) + { + const GDALPDFRasterDesc& oDesc = oPageContext.asRasterDesc[iRaster]; + if (oDesc.nOCGRasterId) + VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oDesc.nOCGRasterId); + + for(size_t iImage = 0; iImage < oDesc.asImageDesc.size(); iImage ++) + { + VSIFPrintfL(fp, "q\n"); + GDALPDFObjectRW* poXSize = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfXSize); + GDALPDFObjectRW* poYSize = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfYSize); + GDALPDFObjectRW* poXOff = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfXOff); + GDALPDFObjectRW* poYOff = GDALPDFObjectRW::CreateReal(oDesc.asImageDesc[iImage].dfYOff); + VSIFPrintfL(fp, "%s 0 0 %s %s %s cm\n", + poXSize->Serialize().c_str(), + poYSize->Serialize().c_str(), + poXOff->Serialize().c_str(), + poYOff->Serialize().c_str()); + delete poXSize; + delete poYSize; + delete poXOff; + delete poYOff; + VSIFPrintfL(fp, "/Image%d Do\n", + oDesc.asImageDesc[iImage].nImageId); + VSIFPrintfL(fp, "Q\n"); + } + + if (oDesc.nOCGRasterId) + VSIFPrintfL(fp, "EMC\n"); + } + + /* -------------------------------------------------------------- */ + /* Write drawing instructions for vector features */ + /* -------------------------------------------------------------- */ + int iObj = 0; + for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++) + { + GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer]; + + VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOGCId); + + for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++) + { + CPLString osName = oLayerDesc.aFeatureNames[iVector]; + if (osName.size()) + { + VSIFPrintfL(fp, "/feature <> BDC\n", + iObj); + } + + iObj ++; + + VSIFPrintfL(fp, "/Vector%d Do\n", oLayerDesc.aIds[iVector]); + + if (osName.size()) + { + VSIFPrintfL(fp, "EMC\n"); + } + } + + VSIFPrintfL(fp, "EMC\n"); + } + + /* -------------------------------------------------------------- */ + /* Write drawing instructions for labels of vector features */ + /* -------------------------------------------------------------- */ + iObj = 0; + for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++) + { + GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer]; + if (oLayerDesc.nOCGTextId) + { + VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOGCId); + VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", oLayerDesc.nOCGTextId); + + for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++) + { + if (oLayerDesc.aIdsText[iVector]) + { + CPLString osName = oLayerDesc.aFeatureNames[iVector]; + if (osName.size()) + { + VSIFPrintfL(fp, "/feature <> BDC\n", + iObj); + } + + VSIFPrintfL(fp, "/Text%d Do\n", oLayerDesc.aIdsText[iVector]); + + if (osName.size()) + { + VSIFPrintfL(fp, "EMC\n"); + } + } + + iObj ++; + } + + VSIFPrintfL(fp, "EMC\n"); + VSIFPrintfL(fp, "EMC\n"); + } + else + iObj += (int) oLayerDesc.aIds.size(); + } + + /* -------------------------------------------------------------- */ + /* Write drawing instructions for extra content. */ + /* -------------------------------------------------------------- */ + if (pszExtraStream || asExtraImageDesc.size()) + { + if (nLayerExtraId) + VSIFPrintfL(fp, "/OC /Lyr%d BDC\n", nLayerExtraId); + + /* -------------------------------------------------------------- */ + /* Write drawing instructions for extra images. */ + /* -------------------------------------------------------------- */ + for(size_t iImage = 0; iImage < asExtraImageDesc.size(); iImage ++) + { + VSIFPrintfL(fp, "q\n"); + GDALPDFObjectRW* poXSize = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfXSize); + GDALPDFObjectRW* poYSize = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfYSize); + GDALPDFObjectRW* poXOff = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfXOff); + GDALPDFObjectRW* poYOff = GDALPDFObjectRW::CreateReal(asExtraImageDesc[iImage].dfYOff); + VSIFPrintfL(fp, "%s 0 0 %s %s %s cm\n", + poXSize->Serialize().c_str(), + poYSize->Serialize().c_str(), + poXOff->Serialize().c_str(), + poYOff->Serialize().c_str()); + delete poXSize; + delete poYSize; + delete poXOff; + delete poYOff; + VSIFPrintfL(fp, "/Image%d Do\n", + asExtraImageDesc[iImage].nImageId); + VSIFPrintfL(fp, "Q\n"); + } + + if (pszExtraStream) + VSIFPrintfL(fp, "%s\n", pszExtraStream); + + if (nLayerExtraId) + VSIFPrintfL(fp, "EMC\n"); + } + + if (fpGZip) + VSIFCloseL(fpGZip); + fp = fpBack; + + vsi_l_offset nStreamEnd = VSIFTellL(fp); + if (fpGZip) + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "endstream\n"); + EndObj(); + + StartObj(nContentLengthId); + VSIFPrintfL(fp, + " %ld\n", + (long)(nStreamEnd - nStreamStart)); + EndObj(); + + /* -------------------------------------------------------------- */ + /* Write objects for feature tree. */ + /* -------------------------------------------------------------- */ + if (nStructTreeRootId) + { + int nParentTreeId = AllocNewObject(); + StartObj(nParentTreeId); + VSIFPrintfL(fp, "<< /Nums [ 0 "); + VSIFPrintfL(fp, "[ "); + for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++) + { + GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer]; + for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++) + { + int nId = oLayerDesc.aUserPropertiesIds[iVector]; + if (nId) + VSIFPrintfL(fp, "%d 0 R ", nId); + } + } + VSIFPrintfL(fp, " ]\n"); + VSIFPrintfL(fp, " ] >> \n"); + EndObj(); + + StartObj(nStructTreeRootId); + VSIFPrintfL(fp, + "<< " + "/Type /StructTreeRoot " + "/ParentTree %d 0 R " + "/K [ ", nParentTreeId); + for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++) + { + VSIFPrintfL(fp, "%d 0 R ", oPageContext.asVectorDesc[iLayer]. nFeatureLayerId); + } + VSIFPrintfL(fp,"] >>\n"); + EndObj(); + } + + /* -------------------------------------------------------------- */ + /* Write page resource dictionary. */ + /* -------------------------------------------------------------- */ + StartObj(oPageContext.nResourcesId); + { + GDALPDFDictionaryRW oDict; + GDALPDFDictionaryRW* poDictXObject = new GDALPDFDictionaryRW(); + oDict.Add("XObject", poDictXObject); + size_t iImage; + for(size_t iRaster = 0; iRaster < oPageContext.asRasterDesc.size(); iRaster++) + { + const GDALPDFRasterDesc& oDesc = oPageContext.asRasterDesc[iRaster]; + for(iImage = 0; iImage < oDesc.asImageDesc.size(); iImage ++) + { + poDictXObject->Add(CPLSPrintf("Image%d", oDesc.asImageDesc[iImage].nImageId), + oDesc.asImageDesc[iImage].nImageId, 0); + } + } + for(iImage = 0; iImage < asExtraImageDesc.size(); iImage ++) + { + poDictXObject->Add(CPLSPrintf("Image%d", asExtraImageDesc[iImage].nImageId), + asExtraImageDesc[iImage].nImageId, 0); + } + for(size_t iLayer = 0; iLayer < oPageContext.asVectorDesc.size(); iLayer ++) + { + GDALPDFLayerDesc& oLayerDesc = oPageContext.asVectorDesc[iLayer]; + for(size_t iVector = 0; iVector < oLayerDesc.aIds.size(); iVector ++) + { + poDictXObject->Add(CPLSPrintf("Vector%d", oLayerDesc.aIds[iVector]), + oLayerDesc.aIds[iVector], 0); + if (oLayerDesc.aIdsText[iVector]) + poDictXObject->Add(CPLSPrintf("Text%d", oLayerDesc.aIdsText[iVector]), + oLayerDesc.aIdsText[iVector], 0); + } + } + + GDALPDFDictionaryRW* poDictFTimesRoman = NULL; + if (bHasTimesRoman) + { + poDictFTimesRoman = new GDALPDFDictionaryRW(); + poDictFTimesRoman->Add("Type", GDALPDFObjectRW::CreateName("Font")); + poDictFTimesRoman->Add("BaseFont", GDALPDFObjectRW::CreateName("Times-Roman")); + poDictFTimesRoman->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding")); + poDictFTimesRoman->Add("Subtype", GDALPDFObjectRW::CreateName("Type1")); + } + + GDALPDFDictionaryRW* poDictFTimesBold = NULL; + if (bHasTimesBold) + { + poDictFTimesBold = new GDALPDFDictionaryRW(); + poDictFTimesBold->Add("Type", GDALPDFObjectRW::CreateName("Font")); + poDictFTimesBold->Add("BaseFont", GDALPDFObjectRW::CreateName("Times-Bold")); + poDictFTimesBold->Add("Encoding", GDALPDFObjectRW::CreateName("WinAnsiEncoding")); + poDictFTimesBold->Add("Subtype", GDALPDFObjectRW::CreateName("Type1")); + } + + if (poDictFTimesRoman != NULL || poDictFTimesBold != NULL) + { + GDALPDFDictionaryRW* poDictFont = new GDALPDFDictionaryRW(); + if (poDictFTimesRoman) + poDictFont->Add("FTimesRoman", poDictFTimesRoman); + if (poDictFTimesBold) + poDictFont->Add("FTimesBold", poDictFTimesBold); + oDict.Add("Font", poDictFont); + } + + if (asOCGs.size()) + { + GDALPDFDictionaryRW* poDictProperties = new GDALPDFDictionaryRW(); + for(size_t i=0; iAdd(CPLSPrintf("Lyr%d", asOCGs[i].nId), + asOCGs[i].nId, 0); + oDict.Add("Properties", poDictProperties); + } + + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + } + EndObj(); + + /* -------------------------------------------------------------- */ + /* Write annotation arrays. */ + /* -------------------------------------------------------------- */ + StartObj(oPageContext.nAnnotsId); + { + GDALPDFArrayRW oArray; + for(size_t i = 0; i < oPageContext.anAnnotationsId.size(); i++) + { + oArray.Add(oPageContext.anAnnotationsId[i], 0); + } + VSIFPrintfL(fp, "%s\n", oArray.Serialize().c_str()); + } + EndObj(); + + return TRUE; +} + +/************************************************************************/ +/* WriteMask() */ +/************************************************************************/ + +int GDALPDFWriter::WriteMask(GDALDataset* poSrcDS, + int nXOff, int nYOff, int nReqXSize, int nReqYSize, + PDFCompressMethod eCompressMethod) +{ + int nMaskSize = nReqXSize * nReqYSize; + GByte* pabyMask = (GByte*)VSIMalloc(nMaskSize); + if (pabyMask == NULL) + return 0; + + CPLErr eErr; + eErr = poSrcDS->GetRasterBand(4)->RasterIO( + GF_Read, + nXOff, nYOff, + nReqXSize, nReqYSize, + pabyMask, nReqXSize, nReqYSize, GDT_Byte, + 0, 0, NULL); + if (eErr != CE_None) + { + VSIFree(pabyMask); + return 0; + } + + int bOnly0or255 = TRUE; + int bOnly255 = TRUE; + /* int bOnly0 = TRUE; */ + int i; + for(i=0;iGetRasterCount(); + if (nBands == 0) + return 0; + + if (nColorTableId == 0) + nColorTableId = WriteColorTable(poSrcDS); + + CPLErr eErr = CE_None; + GDALDataset* poBlockSrcDS = NULL; + GDALDatasetH hMemDS = NULL; + GByte* pabyMEMDSBuffer = NULL; + + if (eCompressMethod == COMPRESS_DEFAULT) + { + GDALDataset* poSrcDSToTest = poSrcDS; + + /* Test if we can directly copy original JPEG content */ + /* if available */ + if (poSrcDS->GetDriver() != NULL && + poSrcDS->GetDriver() == GDALGetDriverByName("VRT")) + { + VRTDataset* poVRTDS = (VRTDataset* )poSrcDS; + poSrcDSToTest = poVRTDS->GetSingleSimpleSource(); + } + + if (poSrcDSToTest != NULL && + poSrcDSToTest->GetDriver() != NULL && + EQUAL(poSrcDSToTest->GetDriver()->GetDescription(), "JPEG") && + nXOff == 0 && nYOff == 0 && + nReqXSize == poSrcDSToTest->GetRasterXSize() && + nReqYSize == poSrcDSToTest->GetRasterYSize() && + nJPEGQuality < 0) + { + VSILFILE* fpSrc = VSIFOpenL(poSrcDSToTest->GetDescription(), "rb"); + if (fpSrc != NULL) + { + CPLDebug("PDF", "Copying directly original JPEG file"); + + VSIFSeekL(fpSrc, 0, SEEK_END); + int nLength = (int)VSIFTellL(fpSrc); + VSIFSeekL(fpSrc, 0, SEEK_SET); + + int nImageId = AllocNewObject(); + + StartObj(nImageId); + + GDALPDFDictionaryRW oDict; + oDict.Add("Length", nLength) + .Add("Type", GDALPDFObjectRW::CreateName("XObject")) + .Add("Filter", GDALPDFObjectRW::CreateName("DCTDecode")) + .Add("Subtype", GDALPDFObjectRW::CreateName("Image")) + .Add("Width", nReqXSize) + .Add("Height", nReqYSize) + .Add("ColorSpace", + (nBands == 1) ? GDALPDFObjectRW::CreateName("DeviceGray") : + GDALPDFObjectRW::CreateName("DeviceRGB")) + .Add("BitsPerComponent", 8); + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + VSIFPrintfL(fp, "stream\n"); + + GByte abyBuffer[1024]; + for(int i=0;iGetRasterXSize() && + nReqYSize == poSrcDS->GetRasterYSize() && + nBands != 4) + { + poBlockSrcDS = poSrcDS; + } + else + { + if (nBands == 4) + nBands = 3; + + GDALDriverH hMemDriver = GDALGetDriverByName("MEM"); + if( hMemDriver == NULL ) + return 0; + + hMemDS = GDALCreate(hMemDriver, "MEM:::", + nReqXSize, nReqYSize, 0, + GDT_Byte, NULL); + if (hMemDS == NULL) + return 0; + + pabyMEMDSBuffer = + (GByte*)VSIMalloc3(nReqXSize, nReqYSize, nBands); + if (pabyMEMDSBuffer == NULL) + { + GDALClose(hMemDS); + return 0; + } + + eErr = poSrcDS->RasterIO(GF_Read, + nXOff, nYOff, + nReqXSize, nReqYSize, + pabyMEMDSBuffer, nReqXSize, nReqYSize, + GDT_Byte, nBands, NULL, + 0, 0, 0, NULL); + + if( eErr != CE_None ) + { + CPLFree(pabyMEMDSBuffer); + GDALClose(hMemDS); + return 0; + } + + int iBand; + for(iBand = 0; iBand < nBands; iBand ++) + { + char** papszMEMDSOptions = NULL; + char szTmp[64]; + memset(szTmp, 0, sizeof(szTmp)); + CPLPrintPointer(szTmp, + pabyMEMDSBuffer + iBand * nReqXSize * nReqYSize, sizeof(szTmp)); + papszMEMDSOptions = CSLSetNameValue(papszMEMDSOptions, "DATAPOINTER", szTmp); + GDALAddBand(hMemDS, GDT_Byte, papszMEMDSOptions); + CSLDestroy(papszMEMDSOptions); + } + + poBlockSrcDS = (GDALDataset*) hMemDS; + } + + int nImageId = AllocNewObject(); + int nImageLengthId = AllocNewObject(); + + int nMeasureId = 0; + if( CSLTestBoolean(CPLGetConfigOption("GDAL_PDF_WRITE_GEOREF_ON_IMAGE", "FALSE")) && + nReqXSize == poSrcDS->GetRasterXSize() && + nReqYSize == poSrcDS->GetRasterYSize() ) + { + PDFMargins sMargins = {0, 0, 0, 0}; + nMeasureId = WriteSRS_ISO32000(poSrcDS, 1, NULL, &sMargins, FALSE); + } + + StartObj(nImageId); + + GDALPDFDictionaryRW oDict; + oDict.Add("Length", nImageLengthId, 0) + .Add("Type", GDALPDFObjectRW::CreateName("XObject")); + + if( eCompressMethod == COMPRESS_DEFLATE ) + { + oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode")); + if( nPredictor == 2 ) + oDict.Add("DecodeParms", &((new GDALPDFDictionaryRW()) + ->Add("Predictor", 2) + .Add("Colors", nBands) + .Add("Columns", nReqXSize))); + } + else if( eCompressMethod == COMPRESS_JPEG ) + { + oDict.Add("Filter", GDALPDFObjectRW::CreateName("DCTDecode")); + } + else if( eCompressMethod == COMPRESS_JPEG2000 ) + { + oDict.Add("Filter", GDALPDFObjectRW::CreateName("JPXDecode")); + } + + oDict.Add("Subtype", GDALPDFObjectRW::CreateName("Image")) + .Add("Width", nReqXSize) + .Add("Height", nReqYSize) + .Add("ColorSpace", + (nColorTableId != 0) ? GDALPDFObjectRW::CreateIndirect(nColorTableId, 0) : + (nBands == 1) ? GDALPDFObjectRW::CreateName("DeviceGray") : + GDALPDFObjectRW::CreateName("DeviceRGB")) + .Add("BitsPerComponent", 8); + if( nMaskId ) + { + oDict.Add("SMask", nMaskId, 0); + } + if( nMeasureId ) + { + oDict.Add("Measure", nMeasureId, 0); + } + + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + VSIFPrintfL(fp, "stream\n"); + + vsi_l_offset nStreamStart = VSIFTellL(fp); + + if( eCompressMethod == COMPRESS_JPEG || + eCompressMethod == COMPRESS_JPEG2000 ) + { + GDALDriver* poJPEGDriver = NULL; + char szTmp[64]; + char** papszOptions = NULL; + + if( eCompressMethod == COMPRESS_JPEG ) + { + poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JPEG"); + if (poJPEGDriver != NULL && nJPEGQuality > 0) + papszOptions = CSLAddString(papszOptions, CPLSPrintf("QUALITY=%d", nJPEGQuality)); + sprintf(szTmp, "/vsimem/pdftemp/%p.jpg", this); + } + else + { + if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JP2KAK")) + poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JP2KAK"); + if (poJPEGDriver == NULL) + { + if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JP2ECW")) + { + poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JP2ECW"); + if( poJPEGDriver && + poJPEGDriver->GetMetadataItem(GDAL_DMD_CREATIONDATATYPES) == NULL ) + { + poJPEGDriver = NULL; + } + } + if (poJPEGDriver) + { + papszOptions = CSLAddString(papszOptions, "PROFILE=NPJE"); + papszOptions = CSLAddString(papszOptions, "LAYERS=1"); + papszOptions = CSLAddString(papszOptions, "GeoJP2=OFF"); + papszOptions = CSLAddString(papszOptions, "GMLJP2=OFF"); + } + } + if (poJPEGDriver == NULL) + { + if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JP2OpenJPEG")) + poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JP2OpenJPEG"); + if (poJPEGDriver) + { + papszOptions = CSLAddString(papszOptions, "GeoJP2=OFF"); + papszOptions = CSLAddString(papszOptions, "GMLJP2=OFF"); + } + } + if (poJPEGDriver == NULL) + { + if (pszJPEG2000_DRIVER == NULL || EQUAL(pszJPEG2000_DRIVER, "JPEG2000")) + poJPEGDriver = (GDALDriver*) GDALGetDriverByName("JPEG2000"); + } + sprintf(szTmp, "/vsimem/pdftemp/%p.jp2", this); + } + + if( poJPEGDriver == NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "No %s driver found", + ( eCompressMethod == COMPRESS_JPEG ) ? "JPEG" : "JPEG2000"); + eErr = CE_Failure; + goto end; + } + + GDALDataset* poJPEGDS = NULL; + + poJPEGDS = poJPEGDriver->CreateCopy(szTmp, poBlockSrcDS, + FALSE, papszOptions, + pfnProgress, pProgressData); + + CSLDestroy(papszOptions); + if( poJPEGDS == NULL ) + { + eErr = CE_Failure; + goto end; + } + + GDALClose(poJPEGDS); + + vsi_l_offset nJPEGDataSize = 0; + GByte* pabyJPEGData = VSIGetMemFileBuffer(szTmp, &nJPEGDataSize, TRUE); + VSIFWriteL(pabyJPEGData, nJPEGDataSize, 1, fp); + CPLFree(pabyJPEGData); + } + else + { + VSILFILE* fpGZip = NULL; + VSILFILE* fpBack = fp; + if( eCompressMethod == COMPRESS_DEFLATE ) + { + fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE ); + fp = fpGZip; + } + + GByte* pabyLine = (GByte*)CPLMalloc(nReqXSize * nBands); + for(int iLine = 0; iLine < nReqYSize; iLine ++) + { + /* Get pixel interleaved data */ + eErr = poBlockSrcDS->RasterIO(GF_Read, + 0, iLine, nReqXSize, 1, + pabyLine, nReqXSize, 1, GDT_Byte, + nBands, NULL, nBands, 0, 1, NULL); + if( eErr != CE_None ) + break; + + /* Apply predictor if needed */ + if( nPredictor == 2 ) + { + if( nBands == 1 ) + { + int nPrevValue = pabyLine[0]; + for(int iPixel = 1; iPixel < nReqXSize; iPixel ++) + { + int nCurValue = pabyLine[iPixel]; + pabyLine[iPixel] = (GByte) (nCurValue - nPrevValue); + nPrevValue = nCurValue; + } + } + else if( nBands == 3 ) + { + int nPrevValueR = pabyLine[0]; + int nPrevValueG = pabyLine[1]; + int nPrevValueB = pabyLine[2]; + for(int iPixel = 1; iPixel < nReqXSize; iPixel ++) + { + int nCurValueR = pabyLine[3 * iPixel + 0]; + int nCurValueG = pabyLine[3 * iPixel + 1]; + int nCurValueB = pabyLine[3 * iPixel + 2]; + pabyLine[3 * iPixel + 0] = (GByte) (nCurValueR - nPrevValueR); + pabyLine[3 * iPixel + 1] = (GByte) (nCurValueG - nPrevValueG); + pabyLine[3 * iPixel + 2] = (GByte) (nCurValueB - nPrevValueB); + nPrevValueR = nCurValueR; + nPrevValueG = nCurValueG; + nPrevValueB = nCurValueB; + } + } + } + + if( VSIFWriteL(pabyLine, nReqXSize * nBands, 1, fp) != 1 ) + { + eErr = CE_Failure; + break; + } + + if( eErr == CE_None && pfnProgress != NULL + && !pfnProgress( (iLine+1) / (double)nReqYSize, + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + eErr = CE_Failure; + break; + } + } + + CPLFree(pabyLine); + + if (fpGZip) + VSIFCloseL(fpGZip); + fp = fpBack; + } + +end: + CPLFree(pabyMEMDSBuffer); + pabyMEMDSBuffer = NULL; + if( hMemDS != NULL ) + { + GDALClose(hMemDS); + hMemDS = NULL; + } + + vsi_l_offset nStreamEnd = VSIFTellL(fp); + VSIFPrintfL(fp, + "\n" + "endstream\n"); + EndObj(); + + StartObj(nImageLengthId); + VSIFPrintfL(fp, + " %ld\n", + (long)(nStreamEnd - nStreamStart)); + EndObj(); + + return eErr == CE_None ? nImageId : 0; +} + +/************************************************************************/ +/* WriteJavascript() */ +/************************************************************************/ + +int GDALPDFWriter::WriteJavascript(const char* pszJavascript) +{ + int nJSId = AllocNewObject(); + int nJSLengthId = AllocNewObject(); + StartObj(nJSId); + { + GDALPDFDictionaryRW oDict; + oDict.Add("Length", nJSLengthId, 0); + if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) + { + oDict.Add("Filter", GDALPDFObjectRW::CreateName("FlateDecode")); + } + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + } + VSIFPrintfL(fp, "stream\n"); + vsi_l_offset nStreamStart = VSIFTellL(fp); + + VSILFILE* fpGZip = NULL; + VSILFILE* fpBack = fp; + if( oPageContext.eStreamCompressMethod != COMPRESS_NONE ) + { + fpGZip = (VSILFILE* )VSICreateGZipWritable( (VSIVirtualHandle*) fp, TRUE, FALSE ); + fp = fpGZip; + } + + VSIFWriteL(pszJavascript, strlen(pszJavascript), 1, fp); + + if (fpGZip) + VSIFCloseL(fpGZip); + fp = fpBack; + + vsi_l_offset nStreamEnd = VSIFTellL(fp); + VSIFPrintfL(fp, + "\n" + "endstream\n"); + EndObj(); + + StartObj(nJSLengthId); + VSIFPrintfL(fp, + " %ld\n", + (long)(nStreamEnd - nStreamStart)); + EndObj(); + + nNamesId = AllocNewObject(); + StartObj(nNamesId); + { + GDALPDFDictionaryRW oDict; + GDALPDFDictionaryRW* poJavaScriptDict = new GDALPDFDictionaryRW(); + oDict.Add("JavaScript", poJavaScriptDict); + + GDALPDFArrayRW* poNamesArray = new GDALPDFArrayRW(); + poJavaScriptDict->Add("Names", poNamesArray); + + poNamesArray->Add("GDAL"); + + GDALPDFDictionaryRW* poJSDict = new GDALPDFDictionaryRW(); + poNamesArray->Add(poJSDict); + + poJSDict->Add("JS", nJSId, 0); + poJSDict->Add("S", GDALPDFObjectRW::CreateName("JavaScript")); + + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + } + EndObj(); + + return nNamesId; +} + +/************************************************************************/ +/* WriteJavascriptFile() */ +/************************************************************************/ + +int GDALPDFWriter::WriteJavascriptFile(const char* pszJavascriptFile) +{ + int nRet = 0; + char* pszJavascriptToFree = (char*)CPLMalloc(65536); + VSILFILE* fpJS = VSIFOpenL(pszJavascriptFile, "rb"); + if( fpJS != NULL ) + { + int nRead = (int)VSIFReadL(pszJavascriptToFree, 1, 65536, fpJS); + if( nRead < 65536 ) + { + pszJavascriptToFree[nRead] = '\0'; + nRet = WriteJavascript(pszJavascriptToFree); + } + VSIFCloseL(fpJS); + } + CPLFree(pszJavascriptToFree); + return nRet; +} +/************************************************************************/ +/* WritePages() */ +/************************************************************************/ + +void GDALPDFWriter::WritePages() +{ + StartObj(nPageResourceId); + { + GDALPDFDictionaryRW oDict; + GDALPDFArrayRW* poKids = new GDALPDFArrayRW(); + oDict.Add("Type", GDALPDFObjectRW::CreateName("Pages")) + .Add("Count", (int)asPageId.size()) + .Add("Kids", poKids); + + for(size_t i=0;iAdd(asPageId[i], 0); + + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + } + EndObj(); + + StartObj(nCatalogId); + { + GDALPDFDictionaryRW oDict; + oDict.Add("Type", GDALPDFObjectRW::CreateName("Catalog")) + .Add("Pages", nPageResourceId, 0); + if (nXMPId) + oDict.Add("Metadata", nXMPId, 0); + if (asOCGs.size()) + { + GDALPDFDictionaryRW* poDictOCProperties = new GDALPDFDictionaryRW(); + oDict.Add("OCProperties", poDictOCProperties); + + GDALPDFDictionaryRW* poDictD = new GDALPDFDictionaryRW(); + poDictOCProperties->Add("D", poDictD); + + /* Build "Order" array of D dict */ + GDALPDFArrayRW* poArrayOrder = new GDALPDFArrayRW(); + size_t i; + for(i=0;iAdd(asOCGs[i].nId, 0); + if (i + 1 < asOCGs.size() && asOCGs[i+1].nParentId == asOCGs[i].nId) + { + GDALPDFArrayRW* poSubArrayOrder = new GDALPDFArrayRW(); + poSubArrayOrder->Add(asOCGs[i+1].nId, 0); + poArrayOrder->Add(poSubArrayOrder); + i ++; + } + } + poDictD->Add("Order", poArrayOrder); + + /* Build "OFF" array of D dict */ + if( osOffLayers.size() ) + { + GDALPDFArrayRW* poArrayOFF = new GDALPDFArrayRW(); + char** papszTokens = CSLTokenizeString2(osOffLayers, ",", 0); + for(int i=0; papszTokens[i] != NULL; i++) + { + size_t j; + int bFound = FALSE; + for(j=0;jAdd(asOCGs[j].nId, 0); + bFound = TRUE; + } + if (j + 1 < asOCGs.size() && asOCGs[j+1].nParentId == asOCGs[j].nId) + { + j ++; + } + } + if( !bFound ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Unknown layer name (%s) specified in OFF_LAYERS", + papszTokens[i]); + } + } + CSLDestroy(papszTokens); + + poDictD->Add("OFF", poArrayOFF); + } + + /* Build "RBGroups" array of D dict */ + if( osExclusiveLayers.size() ) + { + GDALPDFArrayRW* poArrayRBGroups = new GDALPDFArrayRW(); + char** papszTokens = CSLTokenizeString2(osExclusiveLayers, ",", 0); + for(int i=0; papszTokens[i] != NULL; i++) + { + size_t j; + int bFound = FALSE; + for(j=0;jAdd(asOCGs[j].nId, 0); + bFound = TRUE; + } + if (j + 1 < asOCGs.size() && asOCGs[j+1].nParentId == asOCGs[j].nId) + { + j ++; + } + } + if( !bFound ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Unknown layer name (%s) specified in EXCLUSIVE_LAYERS", + papszTokens[i]); + } + } + CSLDestroy(papszTokens); + + if( poArrayRBGroups->GetLength() ) + { + GDALPDFArrayRW* poMainArrayRBGroups = new GDALPDFArrayRW(); + poMainArrayRBGroups->Add(poArrayRBGroups); + poDictD->Add("RBGroups", poMainArrayRBGroups); + } + else + delete poArrayRBGroups; + } + + GDALPDFArrayRW* poArrayOGCs = new GDALPDFArrayRW(); + for(i=0;iAdd(asOCGs[i].nId, 0); + poDictOCProperties->Add("OCGs", poArrayOGCs); + } + + if (nStructTreeRootId) + { + GDALPDFDictionaryRW* poDictMarkInfo = new GDALPDFDictionaryRW(); + oDict.Add("MarkInfo", poDictMarkInfo); + poDictMarkInfo->Add("UserProperties", GDALPDFObjectRW::CreateBool(TRUE)); + + oDict.Add("StructTreeRoot", nStructTreeRootId, 0); + } + + if (nNamesId) + oDict.Add("Names", nNamesId, 0); + + VSIFPrintfL(fp, "%s\n", oDict.Serialize().c_str()); + } + EndObj(); +} + +/************************************************************************/ +/* GDALPDFGetJPEGQuality() */ +/************************************************************************/ + +static int GDALPDFGetJPEGQuality(char** papszOptions) +{ + int nJpegQuality = -1; + const char* pszValue = CSLFetchNameValue( papszOptions, "JPEG_QUALITY" ); + if( pszValue != NULL ) + { + nJpegQuality = atoi( pszValue ); + if (!(nJpegQuality >= 1 && nJpegQuality <= 100)) + { + CPLError( CE_Warning, CPLE_IllegalArg, + "JPEG_QUALITY=%s value not recognised, ignoring.", + pszValue ); + nJpegQuality = -1; + } + } + return nJpegQuality; +} + +/************************************************************************/ +/* GDALPDFClippingDataset */ +/************************************************************************/ + +class GDALPDFClippingDataset: public GDALDataset +{ + GDALDataset* poSrcDS; + double adfGeoTransform[6]; + + public: + GDALPDFClippingDataset(GDALDataset* poSrcDS, double adfClippingExtent[4]) : poSrcDS(poSrcDS) + { + double adfSrcGeoTransform[6]; + poSrcDS->GetGeoTransform(adfSrcGeoTransform); + adfGeoTransform[0] = adfClippingExtent[0]; + adfGeoTransform[1] = adfSrcGeoTransform[1]; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = adfSrcGeoTransform[5] < 0 ? adfClippingExtent[3] : adfClippingExtent[1]; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = adfSrcGeoTransform[5]; + nRasterXSize = (int)((adfClippingExtent[2] - adfClippingExtent[0]) / adfSrcGeoTransform[1]); + nRasterYSize = (int)((adfClippingExtent[3] - adfClippingExtent[1]) / fabs(adfSrcGeoTransform[5])); + } + + virtual CPLErr GetGeoTransform( double * padfGeoTransform ) + { + memcpy(padfGeoTransform, adfGeoTransform, 6 * sizeof(double)); + return CE_None; + } + + virtual const char* GetProjectionRef() + { + return poSrcDS->GetProjectionRef(); + } +}; + +/************************************************************************/ +/* GDALPDFCreateCopy() */ +/************************************************************************/ + +GDALDataset *GDALPDFCreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, + char **papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + int nBands = poSrcDS->GetRasterCount(); + int nWidth = poSrcDS->GetRasterXSize(); + int nHeight = poSrcDS->GetRasterYSize(); + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + if( nBands != 1 && nBands != 3 && nBands != 4 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "PDF driver doesn't support %d bands. Must be 1 (grey or with color table), " + "3 (RGB) or 4 bands.\n", nBands ); + + return NULL; + } + + GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + if( eDT != GDT_Byte ) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "PDF driver doesn't support data type %s. " + "Only eight bit byte bands supported.\n", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); + + if (bStrict) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read options. */ +/* -------------------------------------------------------------------- */ + PDFCompressMethod eCompressMethod = COMPRESS_DEFAULT; + const char* pszCompressMethod = CSLFetchNameValue(papszOptions, "COMPRESS"); + if (pszCompressMethod) + { + if( EQUAL(pszCompressMethod, "NONE") ) + eCompressMethod = COMPRESS_NONE; + else if( EQUAL(pszCompressMethod, "DEFLATE") ) + eCompressMethod = COMPRESS_DEFLATE; + else if( EQUAL(pszCompressMethod, "JPEG") ) + eCompressMethod = COMPRESS_JPEG; + else if( EQUAL(pszCompressMethod, "JPEG2000") ) + eCompressMethod = COMPRESS_JPEG2000; + else + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "Unsupported value for COMPRESS."); + + if (bStrict) + return NULL; + } + } + + PDFCompressMethod eStreamCompressMethod = COMPRESS_DEFLATE; + const char* pszStreamCompressMethod = CSLFetchNameValue(papszOptions, "STREAM_COMPRESS"); + if (pszStreamCompressMethod) + { + if( EQUAL(pszStreamCompressMethod, "NONE") ) + eStreamCompressMethod = COMPRESS_NONE; + else if( EQUAL(pszStreamCompressMethod, "DEFLATE") ) + eStreamCompressMethod = COMPRESS_DEFLATE; + else + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "Unsupported value for STREAM_COMPRESS."); + + if (bStrict) + return NULL; + } + } + + if (nBands == 1 && + poSrcDS->GetRasterBand(1)->GetColorTable() != NULL && + (eCompressMethod == COMPRESS_JPEG || eCompressMethod == COMPRESS_JPEG2000)) + { + CPLError( CE_Warning, CPLE_AppDefined, + "The source raster band has a color table, which is not appropriate with JPEG or JPEG2000 compression.\n" + "You should rather consider using color table expansion (-expand option in gdal_translate)"); + } + + + int nBlockXSize = nWidth; + int nBlockYSize = nHeight; + const char* pszValue; + + int bTiled = CSLFetchBoolean( papszOptions, "TILED", FALSE ); + if( bTiled ) + nBlockXSize = nBlockYSize = 256; + + pszValue = CSLFetchNameValue(papszOptions, "BLOCKXSIZE"); + if( pszValue != NULL ) + { + nBlockXSize = atoi( pszValue ); + if (nBlockXSize < 0 || nBlockXSize >= nWidth) + nBlockXSize = nWidth; + } + + pszValue = CSLFetchNameValue(papszOptions, "BLOCKYSIZE"); + if( pszValue != NULL ) + { + nBlockYSize = atoi( pszValue ); + if (nBlockYSize < 0 || nBlockYSize >= nHeight) + nBlockYSize = nHeight; + } + + int nJPEGQuality = GDALPDFGetJPEGQuality(papszOptions); + + const char* pszJPEG2000_DRIVER = CSLFetchNameValue(papszOptions, "JPEG2000_DRIVER"); + + const char* pszGEO_ENCODING = + CSLFetchNameValueDef(papszOptions, "GEO_ENCODING", "ISO32000"); + + const char* pszXMP = CSLFetchNameValue(papszOptions, "XMP"); + + const char* pszPredictor = CSLFetchNameValue(papszOptions, "PREDICTOR"); + int nPredictor = 1; + if (pszPredictor) + { + if (eCompressMethod == COMPRESS_DEFAULT) + eCompressMethod = COMPRESS_DEFLATE; + + if (eCompressMethod != COMPRESS_DEFLATE) + { + CPLError(CE_Warning, CPLE_NotSupported, + "PREDICTOR option is only taken into account for DEFLATE compression"); + } + else + { + nPredictor = atoi(pszPredictor); + if (nPredictor != 1 && nPredictor != 2) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Supported PREDICTOR values are 1 or 2"); + nPredictor = 1; + } + } + } + + const char* pszNEATLINE = CSLFetchNameValue(papszOptions, "NEATLINE"); + + int nMargin = atoi(CSLFetchNameValueDef(papszOptions, "MARGIN", "0")); + + PDFMargins sMargins; + sMargins.nLeft = nMargin; + sMargins.nRight = nMargin; + sMargins.nTop = nMargin; + sMargins.nBottom = nMargin; + + const char* pszLeftMargin = CSLFetchNameValue(papszOptions, "LEFT_MARGIN"); + if (pszLeftMargin) sMargins.nLeft = atoi(pszLeftMargin); + + const char* pszRightMargin = CSLFetchNameValue(papszOptions, "RIGHT_MARGIN"); + if (pszRightMargin) sMargins.nRight = atoi(pszRightMargin); + + const char* pszTopMargin = CSLFetchNameValue(papszOptions, "TOP_MARGIN"); + if (pszTopMargin) sMargins.nTop = atoi(pszTopMargin); + + const char* pszBottomMargin = CSLFetchNameValue(papszOptions, "BOTTOM_MARGIN"); + if (pszBottomMargin) sMargins.nBottom = atoi(pszBottomMargin); + + const char* pszDPI = CSLFetchNameValue(papszOptions, "DPI"); + double dfDPI = DEFAULT_DPI; + if( pszDPI != NULL ) + dfDPI = CPLAtof(pszDPI); + + double dfUserUnit = dfDPI * USER_UNIT_IN_INCH; + double dfWidthInUserUnit = nWidth / dfUserUnit + sMargins.nLeft + sMargins.nRight; + double dfHeightInUserUnit = nHeight / dfUserUnit + sMargins.nBottom + sMargins.nTop; + if( dfWidthInUserUnit > MAXIMUM_SIZE_IN_UNITS || + dfHeightInUserUnit > MAXIMUM_SIZE_IN_UNITS ) + { + if( pszDPI == NULL ) + { + if( sMargins.nLeft + sMargins.nRight >= MAXIMUM_SIZE_IN_UNITS || + sMargins.nBottom + sMargins.nTop >= MAXIMUM_SIZE_IN_UNITS ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Margins too big compared to maximum page dimension (%d) " + "in user units allowed by Acrobat", + MAXIMUM_SIZE_IN_UNITS); + } + else + { + if( dfWidthInUserUnit >= dfHeightInUserUnit ) + { + dfDPI = (int)(0.5 + (double)nWidth / (MAXIMUM_SIZE_IN_UNITS - + (sMargins.nLeft + sMargins.nRight)) / USER_UNIT_IN_INCH); + } + else + { + dfDPI = (int)(0.5 + (double)nHeight / (MAXIMUM_SIZE_IN_UNITS - + (sMargins.nBottom + sMargins.nTop)) / USER_UNIT_IN_INCH); + } + CPLDebug("PDF", "Adjusting DPI to %d so that page dimension in " + "user units remain in what is accepted by Acrobat", (int)dfDPI); + } + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "The page dimension in user units is %d x %d whereas the " + "maximum allowed by Acrobat is %d x %d", + (int)(dfWidthInUserUnit + 0.5), + (int)(dfHeightInUserUnit + 0.5), + MAXIMUM_SIZE_IN_UNITS, MAXIMUM_SIZE_IN_UNITS); + } + } + + if (dfDPI < DEFAULT_DPI) + dfDPI = DEFAULT_DPI; + + const char* pszClippingExtent = CSLFetchNameValue(papszOptions, "CLIPPING_EXTENT"); + int bUseClippingExtent = FALSE; + double adfClippingExtent[4] = { 0.0, 0.0, 0.0, 0.0 }; + if( pszClippingExtent != NULL ) + { + char** papszTokens = CSLTokenizeString2(pszClippingExtent, ",", 0); + if( CSLCount(papszTokens) == 4 ) + { + bUseClippingExtent = TRUE; + adfClippingExtent[0] = CPLAtof(papszTokens[0]); + adfClippingExtent[1] = CPLAtof(papszTokens[1]); + adfClippingExtent[2] = CPLAtof(papszTokens[2]); + adfClippingExtent[3] = CPLAtof(papszTokens[3]); + if( adfClippingExtent[0] > adfClippingExtent[2] || + adfClippingExtent[1] > adfClippingExtent[3] ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid value for CLIPPING_EXTENT. Should be xmin,ymin,xmax,ymax"); + bUseClippingExtent = TRUE; + } + + if( bUseClippingExtent ) + { + double adfGeoTransform[6]; + if( poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None ) + { + if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot use CLIPPING_EXTENT because main raster has a rotated geotransform"); + bUseClippingExtent = TRUE; + } + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot use CLIPPING_EXTENT because main raster has no geotransform"); + bUseClippingExtent = TRUE; + } + } + } + CSLDestroy(papszTokens); + } + + const char* pszLayerName = CSLFetchNameValue(papszOptions, "LAYER_NAME"); + + const char* pszExtraImages = CSLFetchNameValue(papszOptions, "EXTRA_IMAGES"); + const char* pszExtraStream = CSLFetchNameValue(papszOptions, "EXTRA_STREAM"); + const char* pszExtraLayerName = CSLFetchNameValue(papszOptions, "EXTRA_LAYER_NAME"); + + const char* pszOGRDataSource = CSLFetchNameValue(papszOptions, "OGR_DATASOURCE"); + const char* pszOGRDisplayField = CSLFetchNameValue(papszOptions, "OGR_DISPLAY_FIELD"); + const char* pszOGRDisplayLayerNames = CSLFetchNameValue(papszOptions, "OGR_DISPLAY_LAYER_NAMES"); + const char* pszOGRLinkField = CSLFetchNameValue(papszOptions, "OGR_LINK_FIELD"); + int bWriteOGRAttributes = CSLFetchBoolean(papszOptions, "OGR_WRITE_ATTRIBUTES", TRUE); + + const char* pszExtraRasters = CSLFetchNameValue(papszOptions, "EXTRA_RASTERS"); + const char* pszExtraRastersLayerName = CSLFetchNameValue(papszOptions, "EXTRA_RASTERS_LAYER_NAME"); + + const char* pszOffLayers = CSLFetchNameValue(papszOptions, "OFF_LAYERS"); + const char* pszExclusiveLayers = CSLFetchNameValue(papszOptions, "EXCLUSIVE_LAYERS"); + + const char* pszJavascript = CSLFetchNameValue(papszOptions, "JAVASCRIPT"); + const char* pszJavascriptFile = CSLFetchNameValue(papszOptions, "JAVASCRIPT_FILE"); + +/* -------------------------------------------------------------------- */ +/* Create file. */ +/* -------------------------------------------------------------------- */ + VSILFILE* fp = VSIFOpenL(pszFilename, "wb"); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create PDF file %s.\n", + pszFilename ); + return NULL; + } + + + GDALPDFWriter oWriter(fp); + + GDALDataset* poClippingDS = poSrcDS; + if( bUseClippingExtent ) + poClippingDS = new GDALPDFClippingDataset(poSrcDS, adfClippingExtent); + + if( CSLFetchBoolean(papszOptions, "WRITE_INFO", TRUE) ) + oWriter.SetInfo(poSrcDS, papszOptions); + oWriter.SetXMP(poClippingDS, pszXMP); + + oWriter.StartPage(poClippingDS, + dfDPI, + pszGEO_ENCODING, + pszNEATLINE, + &sMargins, + eStreamCompressMethod, + pszOGRDataSource != NULL && bWriteOGRAttributes); + + int bRet; + + if( !bUseClippingExtent ) + { + bRet = oWriter.WriteImagery(poSrcDS, + pszLayerName, + eCompressMethod, + nPredictor, + nJPEGQuality, + pszJPEG2000_DRIVER, + nBlockXSize, nBlockYSize, + pfnProgress, pProgressData); + } + else + { + bRet = oWriter.WriteClippedImagery(poSrcDS, + pszLayerName, + eCompressMethod, + nPredictor, + nJPEGQuality, + pszJPEG2000_DRIVER, + nBlockXSize, nBlockYSize, + pfnProgress, pProgressData); + } + + char** papszExtraRasters = CSLTokenizeString2( + pszExtraRasters ? pszExtraRasters : "", ",", 0); + char** papszExtraRastersLayerName = CSLTokenizeString2( + pszExtraRastersLayerName ? pszExtraRastersLayerName : "", ",", 0); + int bUseExtraRastersLayerName = (CSLCount(papszExtraRasters) == + CSLCount(papszExtraRastersLayerName)); + int bUseExtraRasters = TRUE; + + const char* pszClippingProjectionRef = poSrcDS->GetProjectionRef(); + if( CSLCount(papszExtraRasters) != 0 ) + { + double adfGeoTransform[6]; + if( poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None ) + { + if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot use EXTRA_RASTERS because main raster has a rotated geotransform"); + bUseExtraRasters = FALSE; + } + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot use EXTRA_RASTERS because main raster has no geotransform"); + bUseExtraRasters = FALSE; + } + if( bUseExtraRasters && + (pszClippingProjectionRef == NULL || + pszClippingProjectionRef[0] == '\0') ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot use EXTRA_RASTERS because main raster has no projection"); + bUseExtraRasters = FALSE; + } + } + + for(int i=0; bRet && bUseExtraRasters && papszExtraRasters[i] != NULL; i++) + { + GDALDataset* poDS = (GDALDataset*)GDALOpen(papszExtraRasters[i], GA_ReadOnly); + if( poDS != NULL ) + { + double adfGeoTransform[6]; + int bUseRaster = TRUE; + if( poDS->GetGeoTransform(adfGeoTransform) == CE_None ) + { + if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot use %s because it has a rotated geotransform", + papszExtraRasters[i]); + bUseRaster = FALSE; + } + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot use %s because it has no geotransform", + papszExtraRasters[i]); + bUseRaster = FALSE; + } + const char* pszProjectionRef = poDS->GetProjectionRef(); + if( bUseRaster && + (pszProjectionRef == NULL || pszProjectionRef[0] == '\0') ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot use %s because it has no projection", + papszExtraRasters[i]); + bUseRaster = FALSE; + } + if( bUseRaster ) + { + if( pszClippingProjectionRef != NULL && + pszProjectionRef != NULL && + !EQUAL(pszClippingProjectionRef, pszProjectionRef) ) + { + OGRSpatialReferenceH hClippingSRS = + OSRNewSpatialReference(pszClippingProjectionRef); + OGRSpatialReferenceH hSRS = + OSRNewSpatialReference(pszProjectionRef); + if (!OSRIsSame(hClippingSRS, hSRS)) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot use %s because it has a different projection than main dataset", + papszExtraRasters[i]); + bUseRaster = FALSE; + } + OSRDestroySpatialReference(hClippingSRS); + OSRDestroySpatialReference(hSRS); + } + } + if( bUseRaster ) + { + bRet = oWriter.WriteClippedImagery(poDS, + bUseExtraRastersLayerName ? + papszExtraRastersLayerName[i] : NULL, + eCompressMethod, + nPredictor, + nJPEGQuality, + pszJPEG2000_DRIVER, + nBlockXSize, nBlockYSize, + NULL, NULL); + } + + GDALClose(poDS); + } + } + + CSLDestroy(papszExtraRasters); + CSLDestroy(papszExtraRastersLayerName); + +#ifdef OGR_ENABLED + if (bRet && pszOGRDataSource != NULL) + oWriter.WriteOGRDataSource(pszOGRDataSource, + pszOGRDisplayField, + pszOGRDisplayLayerNames, + pszOGRLinkField, + bWriteOGRAttributes); +#endif + + if (bRet) + oWriter.EndPage(pszExtraImages, + pszExtraStream, + pszExtraLayerName, + pszOffLayers, + pszExclusiveLayers); + + if (pszJavascript) + oWriter.WriteJavascript(pszJavascript); + else if (pszJavascriptFile) + oWriter.WriteJavascriptFile(pszJavascriptFile); + + oWriter.Close(); + + if (poClippingDS != poSrcDS) + delete poClippingDS; + + if (!bRet) + { + VSIUnlink(pszFilename); + return NULL; + } + else + { +#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) + return GDALPDFOpen(pszFilename, GA_ReadOnly); +#else + return new GDALFakePDFDataset(); +#endif + } +} diff --git a/bazaar/plugin/gdal/frmts/pdf/pdfcreatecopy.h b/bazaar/plugin/gdal/frmts/pdf/pdfcreatecopy.h new file mode 100644 index 000000000..4bc0e17f3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/pdfcreatecopy.h @@ -0,0 +1,287 @@ +/****************************************************************************** + * $Id: pdfcreatecopy.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: PDF driver + * Purpose: GDALDataset driver for PDF dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef PDFCREATECOPY_H_INCLUDED +#define PDFCREATECOPY_H_INCLUDED + +#include "pdfobject.h" +#include "gdal_priv.h" +#include +#include + +#ifdef OGR_ENABLED +#include "ogr_api.h" +#endif + +typedef enum +{ + COMPRESS_NONE, + COMPRESS_DEFLATE, + COMPRESS_JPEG, + COMPRESS_JPEG2000, + COMPRESS_DEFAULT +} PDFCompressMethod; + +typedef struct +{ + int nLeft; + int nRight; + int nTop; + int nBottom; +} PDFMargins; + +/************************************************************************/ +/* GDALPDFWriter */ +/************************************************************************/ + +class GDALXRefEntry +{ + public: + vsi_l_offset nOffset; + int nGen; + int bFree; + + GDALXRefEntry() : nOffset(0), nGen(0), bFree(FALSE) {} + GDALXRefEntry(vsi_l_offset nOffsetIn, int nGenIn = 0) : nOffset(nOffsetIn), nGen(nGenIn), bFree(FALSE) {} + GDALXRefEntry(const GDALXRefEntry& oOther) : nOffset(oOther.nOffset), nGen(oOther.nGen), bFree(oOther.bFree) {} + GDALXRefEntry& operator= (const GDALXRefEntry& oOther) { nOffset = oOther.nOffset; nGen = oOther.nGen; bFree = oOther.bFree; return *this; } +}; + +class GDALPDFImageDesc +{ + public: + int nImageId; + double dfXOff; + double dfYOff; + double dfXSize; + double dfYSize; +}; + +class GDALPDFLayerDesc +{ + public: + int nOGCId; + int nOCGTextId; + int nFeatureLayerId; + CPLString osLayerName; + int bWriteOGRAttributes; + std::vector aIds; + std::vector aIdsText; + std::vector aUserPropertiesIds; + std::vector aFeatureNames; +}; + +class GDALPDFRasterDesc +{ + public: + int nOCGRasterId; + std::vector asImageDesc; +}; + +class GDALPDFPageContext +{ + public: + GDALDataset* poClippingDS; + PDFCompressMethod eStreamCompressMethod; + double dfDPI; + PDFMargins sMargins; + int nPageId; + int nContentId; + int nResourcesId; + std::vector asVectorDesc; + std::vector asRasterDesc; + int nAnnotsId; + std::vector anAnnotationsId; +}; + +class GDALPDFOCGDesc +{ + public: + int nId; + int nParentId; + CPLString osLayerName; +}; + +class GDALPDFWriter +{ + VSILFILE* fp; + std::vector asXRefEntries; + std::vector asPageId; + std::vector asOCGs; + std::map oMapSymbolFilenameToDesc; + + int nInfoId; + int nInfoGen; + int nPageResourceId; + int nStructTreeRootId; + int nCatalogId; + int nCatalogGen; + int nXMPId; + int nXMPGen; + int nNamesId; + int bInWriteObj; + + vsi_l_offset nLastStartXRef; + int nLastXRefSize; + int bCanUpdate; + + GDALPDFPageContext oPageContext; + + CPLString osOffLayers; + CPLString osExclusiveLayers; + + void Init(); + + void StartObj(int nObjectId, int nGen = 0); + void EndObj(); + void WriteXRefTableAndTrailer(); + void WritePages(); + int WriteBlock( GDALDataset* poSrcDS, + int nXOff, int nYOff, int nReqXSize, int nReqYSize, + int nColorTableId, + PDFCompressMethod eCompressMethod, + int nPredictor, + int nJPEGQuality, + const char* pszJPEG2000_DRIVER, + GDALProgressFunc pfnProgress, + void * pProgressData ); + int WriteMask(GDALDataset* poSrcDS, + int nXOff, int nYOff, int nReqXSize, int nReqYSize, + PDFCompressMethod eCompressMethod); + int WriteOCG(const char* pszLayerName, int nParentId = 0); + + int WriteColorTable(GDALDataset* poSrcDS); + + int AllocNewObject(); + + public: + GDALPDFWriter(VSILFILE* fpIn, int bAppend = FALSE); + ~GDALPDFWriter(); + + void Close(); + + int GetCatalogNum() { return nCatalogId; } + int GetCatalogGen() { return nCatalogGen; } + + int ParseTrailerAndXRef(); + void UpdateProj(GDALDataset* poSrcDS, + double dfDPI, + GDALPDFDictionaryRW* poPageDict, + int nPageNum, int nPageGen); + void UpdateInfo(GDALDataset* poSrcDS); + void UpdateXMP (GDALDataset* poSrcDS, + GDALPDFDictionaryRW* poCatalogDict); + + int WriteSRS_ISO32000(GDALDataset* poSrcDS, + double dfUserUnit, + const char* pszNEATLINE, + PDFMargins* psMargins, + int bWriteViewport); + int WriteSRS_OGC_BP(GDALDataset* poSrcDS, + double dfUserUnit, + const char* pszNEATLINE, + PDFMargins* psMargins); + + int StartPage(GDALDataset* poSrcDS, + double dfDPI, + const char* pszGEO_ENCODING, + const char* pszNEATLINE, + PDFMargins* psMargins, + PDFCompressMethod eStreamCompressMethod, + int bHasOGRData); + + int WriteImagery(GDALDataset* poDS, + const char* pszLayerName, + PDFCompressMethod eCompressMethod, + int nPredictor, + int nJPEGQuality, + const char* pszJPEG2000_DRIVER, + int nBlockXSize, int nBlockYSize, + GDALProgressFunc pfnProgress, + void * pProgressData); + + int WriteClippedImagery(GDALDataset* poDS, + const char* pszLayerName, + PDFCompressMethod eCompressMethod, + int nPredictor, + int nJPEGQuality, + const char* pszJPEG2000_DRIVER, + int nBlockXSize, int nBlockYSize, + GDALProgressFunc pfnProgress, + void * pProgressData); +#ifdef OGR_ENABLED + int WriteOGRDataSource(const char* pszOGRDataSource, + const char* pszOGRDisplayField, + const char* pszOGRDisplayLayerNames, + const char* pszOGRLinkField, + int bWriteOGRAttributes); + + GDALPDFLayerDesc StartOGRLayer(CPLString osLayerName, + int bWriteOGRAttributes); + void EndOGRLayer(GDALPDFLayerDesc& osVectorDesc); + + int WriteOGRLayer(OGRDataSourceH hDS, + int iLayer, + const char* pszOGRDisplayField, + const char* pszOGRLinkField, + CPLString osLayerName, + int bWriteOGRAttributes, + int& iObj); + + int WriteOGRFeature(GDALPDFLayerDesc& osVectorDesc, + OGRFeatureH hFeat, + OGRCoordinateTransformationH hCT, + const char* pszOGRDisplayField, + const char* pszOGRLinkField, + int bWriteOGRAttributes, + int& iObj, + int& iObjLayer); +#endif + + int WriteJavascript(const char* pszJavascript); + int WriteJavascriptFile(const char* pszJavascriptFile); + + int EndPage(const char* pszExtraImages, + const char* pszExtraStream, + const char* pszExtraLayerName, + const char* pszOffLayers, + const char* pszExclusiveLayers); + + int SetInfo(GDALDataset* poSrcDS, + char** papszOptions); + int SetXMP(GDALDataset* poSrcDS, + const char* pszXMP); +}; + +GDALDataset *GDALPDFCreateCopy( const char *, GDALDataset *, + int, char **, + GDALProgressFunc pfnProgress, + void * pProgressData ); + +#endif // PDFCREATECOPY_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/pdf/pdfdataset.cpp b/bazaar/plugin/gdal/frmts/pdf/pdfdataset.cpp new file mode 100644 index 000000000..4016a256d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/pdfdataset.cpp @@ -0,0 +1,5316 @@ +/****************************************************************************** + * $Id: pdfdataset.cpp 28978 2015-04-23 09:14:09Z rouault $ + * + * Project: PDF driver + * Purpose: GDALDataset driver for PDF dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pdf.h" + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" +#include "ogr_geometry.h" +#include "cpl_spawn.h" + +#ifdef HAVE_POPPLER +#include "cpl_multiproc.h" +#include "pdfio.h" +#include +#endif // HAVE_POPPLER + +#include "pdfcreatecopy.h" +#include + +#define GDAL_DEFAULT_DPI 150.0 + +/* g++ -fPIC -g -Wall frmts/pdf/pdfdataset.cpp -shared -o gdal_PDF.so -Iport -Igcore -Iogr -L. -lgdal -lpoppler -I/usr/include/poppler */ + +CPL_CVSID("$Id: pdfdataset.cpp 28978 2015-04-23 09:14:09Z rouault $"); + +CPL_C_START +void GDALRegister_PDF(void); +CPL_C_END + +#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) +static const char* pszOpenOptionList = +"" +" " +" " +#endif // HAVE_POPPLER && HAVE_PODOFO +" " +" "; + +static double Get(GDALPDFObject* poObj, int nIndice = -1); + +#ifdef HAVE_POPPLER + +static CPLMutex* hGlobalParamsMutex = NULL; + +/************************************************************************/ +/* ObjectAutoFree */ +/************************************************************************/ + +class ObjectAutoFree : public Object +{ +public: + ObjectAutoFree() {} + ~ObjectAutoFree() { free(); } +}; + + +/************************************************************************/ +/* GDALPDFOutputDev */ +/************************************************************************/ + +class GDALPDFOutputDev : public SplashOutputDev +{ + private: + int bEnableVector; + int bEnableText; + int bEnableBitmap; + + void skipBytes(Stream *str, + int width, int height, + int nComps, int nBits) + { + int nVals = width * nComps; + int nLineSize = (nVals * nBits + 7) >> 3; + int nBytes = nLineSize * height; + for (int i = 0; i < nBytes; i++) + { + if( str->getChar() == EOF) + break; + } + } + + public: + GDALPDFOutputDev(SplashColorMode colorModeA, int bitmapRowPadA, + GBool reverseVideoA, SplashColorPtr paperColorA) : + SplashOutputDev(colorModeA, bitmapRowPadA, + reverseVideoA, paperColorA), + bEnableVector(TRUE), + bEnableText(TRUE), + bEnableBitmap(TRUE) {} + + void SetEnableVector(int bFlag) { bEnableVector = bFlag; } + void SetEnableText(int bFlag) { bEnableText = bFlag; } + void SetEnableBitmap(int bFlag) { bEnableBitmap = bFlag; } + + virtual void startPage(int pageNum, GfxState *state +#ifdef POPPLER_0_23_OR_LATER + ,XRef* xref +#endif + ) + { + SplashOutputDev::startPage(pageNum, state +#ifdef POPPLER_0_23_OR_LATER + ,xref +#endif + ); + SplashBitmap* poBitmap = getBitmap(); + memset(poBitmap->getDataPtr(), 255, poBitmap->getRowSize() * poBitmap->getHeight()); + } + + virtual void stroke(GfxState * state) + { + if (bEnableVector) + SplashOutputDev::stroke(state); + } + + virtual void fill(GfxState * state) + { + if (bEnableVector) + SplashOutputDev::fill(state); + } + + virtual void eoFill(GfxState * state) + { + if (bEnableVector) + SplashOutputDev::eoFill(state); + } + + virtual void drawChar(GfxState *state, double x, double y, + double dx, double dy, + double originX, double originY, + CharCode code, int nBytes, Unicode *u, int uLen) + { + if (bEnableText) + SplashOutputDev::drawChar(state, x, y, dx, dy, + originX, originY, + code, nBytes, u, uLen); + } + + virtual void beginTextObject(GfxState *state) + { + if (bEnableText) + SplashOutputDev::beginTextObject(state); + } + +#ifndef POPPLER_0_23_OR_LATER + virtual GBool deviceHasTextClip(GfxState *state) + { + if (bEnableText) + return SplashOutputDev::deviceHasTextClip(state); + return gFalse; + } +#endif + + virtual void endTextObject(GfxState *state) + { + if (bEnableText) + SplashOutputDev::endTextObject(state); + } + + virtual void drawImageMask(GfxState *state, Object *ref, Stream *str, + int width, int height, GBool invert, + GBool interpolate, GBool inlineImg) + { + if (bEnableBitmap) + SplashOutputDev::drawImageMask(state, ref, str, + width, height, invert, + interpolate, inlineImg); + else + { + str->reset(); + if (inlineImg) + { + skipBytes(str, width, height, 1, 1); + } + str->close(); + } + } + +#ifdef POPPLER_0_20_OR_LATER + virtual void setSoftMaskFromImageMask(GfxState *state, + Object *ref, Stream *str, + int width, int height, GBool invert, + GBool inlineImg, double *baseMatrix) + { + if (bEnableBitmap) + SplashOutputDev::setSoftMaskFromImageMask(state, ref, str, + width, height, invert, + inlineImg, baseMatrix); + else + str->close(); + } + + virtual void unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) + { + if (bEnableBitmap) + SplashOutputDev::unsetSoftMaskFromImageMask(state, baseMatrix); + } +#endif + + virtual void drawImage(GfxState *state, Object *ref, Stream *str, + int width, int height, GfxImageColorMap *colorMap, + GBool interpolate, int *maskColors, GBool inlineImg) + { + if (bEnableBitmap) + SplashOutputDev::drawImage(state, ref, str, + width, height, colorMap, + interpolate, maskColors, inlineImg); + else + { + str->reset(); + if (inlineImg) + { + skipBytes(str, width, height, + colorMap->getNumPixelComps(), + colorMap->getBits()); + } + str->close(); + } + } + + virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str, + int width, int height, + GfxImageColorMap *colorMap, + GBool interpolate, + Stream *maskStr, int maskWidth, int maskHeight, + GBool maskInvert, GBool maskInterpolate) + { + if (bEnableBitmap) + SplashOutputDev::drawMaskedImage(state, ref, str, + width, height, colorMap, + interpolate, + maskStr, maskWidth, maskHeight, + maskInvert, maskInterpolate); + else + str->close(); + } + + virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, + int width, int height, + GfxImageColorMap *colorMap, + GBool interpolate, + Stream *maskStr, + int maskWidth, int maskHeight, + GfxImageColorMap *maskColorMap, + GBool maskInterpolate) + { + if (bEnableBitmap) + { + if( maskColorMap->getBits() <= 0 ) /* workaround poppler bug (robustness) */ + { + str->close(); + return; + } + SplashOutputDev::drawSoftMaskedImage(state, ref, str, + width, height, colorMap, + interpolate, + maskStr, maskWidth, maskHeight, + maskColorMap, maskInterpolate); + } + else + str->close(); + } +}; + +#endif + +/************************************************************************/ +/* Dump routines */ +/************************************************************************/ + +class GDALPDFDumper +{ + private: + FILE* f; + int nDepthLimit; + std::set< int > aoSetObjectExplored; + int bDumpParent; + + void DumpSimplified(GDALPDFObject* poObj); + + public: + GDALPDFDumper(FILE* fIn, int nDepthLimitIn = -1) : f(fIn), nDepthLimit(nDepthLimitIn) + { + bDumpParent = CSLTestBoolean(CPLGetConfigOption("PDF_DUMP_PARENT", "FALSE")); + } + + void Dump(GDALPDFObject* poObj, int nDepth = 0); + void Dump(GDALPDFDictionary* poDict, int nDepth = 0); + void Dump(GDALPDFArray* poArray, int nDepth = 0); +}; + +void GDALPDFDumper::Dump(GDALPDFArray* poArray, int nDepth) +{ + if (nDepthLimit >= 0 && nDepth > nDepthLimit) + return; + + int nLength = poArray->GetLength(); + int i; + CPLString osIndent; + for(i=0;iGet(i)) != NULL) + { + if (poObj->GetType() == PDFObjectType_String || + poObj->GetType() == PDFObjectType_Null || + poObj->GetType() == PDFObjectType_Bool || + poObj->GetType() == PDFObjectType_Int || + poObj->GetType() == PDFObjectType_Real || + poObj->GetType() == PDFObjectType_Name) + { + fprintf(f, " "); + DumpSimplified(poObj); + fprintf(f, "\n"); + } + else + { + fprintf(f, "\n"); + Dump( poObj, nDepth+1); + } + } + } +} + +void GDALPDFDumper::DumpSimplified(GDALPDFObject* poObj) +{ + switch(poObj->GetType()) + { + case PDFObjectType_String: + fprintf(f, "%s (string)", poObj->GetString().c_str()); + break; + + case PDFObjectType_Null: + fprintf(f, "null"); + break; + + case PDFObjectType_Bool: + fprintf(f, "%s (bool)", poObj->GetBool() ? "true" : "false"); + break; + + case PDFObjectType_Int: + fprintf(f, "%d (int)", poObj->GetInt()); + break; + + case PDFObjectType_Real: + fprintf(f, "%f (real)", poObj->GetReal()); + break; + + case PDFObjectType_Name: + fprintf(f, "%s (name)", poObj->GetName().c_str()); + break; + + default: + fprintf(f, "unknown !"); + break; + } +} + +void GDALPDFDumper::Dump(GDALPDFObject* poObj, int nDepth) +{ + if (nDepthLimit >= 0 && nDepth > nDepthLimit) + return; + + int i; + CPLString osIndent; + for(i=0;iGetTypeName()); + int nRefNum = poObj->GetRefNum(); + if (nRefNum != 0) + fprintf(f, ", Num = %d, Gen = %d", + nRefNum, poObj->GetRefGen()); + fprintf(f, "\n"); + + if (nRefNum != 0) + { + if (aoSetObjectExplored.find(nRefNum) != aoSetObjectExplored.end()) + return; + aoSetObjectExplored.insert(nRefNum); + } + + switch(poObj->GetType()) + { + case PDFObjectType_Array: + Dump(poObj->GetArray(), nDepth+1); + break; + + case PDFObjectType_Dictionary: + Dump(poObj->GetDictionary(), nDepth+1); + break; + + case PDFObjectType_String: + case PDFObjectType_Null: + case PDFObjectType_Bool: + case PDFObjectType_Int: + case PDFObjectType_Real: + case PDFObjectType_Name: + fprintf(f, "%s", osIndent.c_str()); + DumpSimplified(poObj); + fprintf(f, "\n"); + break; + + default: + fprintf(f, "%s", osIndent.c_str()); + fprintf(f, "unknown !\n"); + break; + } + + GDALPDFStream* poStream = poObj->GetStream(); + if (poStream != NULL) + { + fprintf(f, "%sHas stream (%d bytes)\n", osIndent.c_str(), poStream->GetLength()); + } +} + +void GDALPDFDumper::Dump(GDALPDFDictionary* poDict, int nDepth) +{ + if (nDepthLimit >= 0 && nDepth > nDepthLimit) + return; + + std::map& oMap = poDict->GetValues(); + std::map::iterator oIter = oMap.begin(); + std::map::iterator oEnd = oMap.end(); + int i; + CPLString osIndent; + for(i=0;ifirst.c_str(); + fprintf(f, "%sItem[%d] : %s", osIndent.c_str(), i, pszKey); + GDALPDFObject* poObj = oIter->second; + if (strcmp(pszKey, "Parent") == 0 && !bDumpParent) + { + if (poObj->GetRefNum()) + fprintf(f, ", Num = %d, Gen = %d", + poObj->GetRefNum(), poObj->GetRefGen()); + fprintf(f, "\n"); + continue; + } + if (poObj != NULL) + { + if (poObj->GetType() == PDFObjectType_String || + poObj->GetType() == PDFObjectType_Null || + poObj->GetType() == PDFObjectType_Bool || + poObj->GetType() == PDFObjectType_Int || + poObj->GetType() == PDFObjectType_Real || + poObj->GetType() == PDFObjectType_Name) + { + fprintf(f, " = "); + DumpSimplified(poObj); + fprintf(f, "\n"); + } + else + { + fprintf(f, "\n"); + Dump(poObj, nDepth+1); + } + } + } +} + +/************************************************************************/ +/* PDFRasterBand() */ +/************************************************************************/ + +PDFRasterBand::PDFRasterBand( PDFDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Byte; + + if( poDS->nBlockXSize ) + { + nBlockXSize = poDS->nBlockXSize; + nBlockYSize = poDS->nBlockYSize; + } + else if( poDS->GetRasterXSize() < 64 * 1024 * 1024 / poDS->GetRasterYSize() ) + { + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + } + else + { + nBlockXSize = MIN(1024, poDS->GetRasterXSize()); + nBlockYSize = MIN(1024, poDS->GetRasterYSize()); + poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + } +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp PDFRasterBand::GetColorInterpretation() +{ + PDFDataset *poGDS = (PDFDataset *) poDS; + if (poGDS->nBands == 1) + return GCI_GrayIndex; + else + return (GDALColorInterp)(GCI_RedBand + (nBand - 1)); +} + +/************************************************************************/ +/* IReadBlockFromTile() */ +/************************************************************************/ + +CPLErr PDFRasterBand::IReadBlockFromTile( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + PDFDataset *poGDS = (PDFDataset *) poDS; + + int nReqXSize = nBlockXSize; + int nReqYSize = nBlockYSize; + if( (nBlockXOff + 1) * nBlockXSize > nRasterXSize ) + nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize; + if( (nBlockYOff + 1) * nBlockYSize > nRasterYSize ) + nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize; + + int nXBlocks = DIV_ROUND_UP(nRasterXSize, nBlockXSize); + int iTile = poGDS->aiTiles[nBlockYOff * nXBlocks + nBlockXOff]; + GDALPDFTileDesc& sTile = poGDS->asTiles[iTile]; + GDALPDFObject* poImage = sTile.poImage; + + if( iTile < 0 ) + { + memset(pImage, 0, nBlockXSize * nBlockYSize); + return CE_None; + } + + if( nBand == 4 ) + { + GDALPDFDictionary* poImageDict = poImage->GetDictionary(); + GDALPDFObject* poSMask = poImageDict->Get("SMask"); + if( poSMask != NULL && poSMask->GetType() == PDFObjectType_Dictionary ) + { + GDALPDFDictionary* poSMaskDict = poSMask->GetDictionary(); + GDALPDFObject* poWidth = poSMaskDict->Get("Width"); + GDALPDFObject* poHeight = poSMaskDict->Get("Height"); + GDALPDFObject* poColorSpace = poSMaskDict->Get("ColorSpace"); + GDALPDFObject* poBitsPerComponent = poSMaskDict->Get("BitsPerComponent"); + int nBits = 0; + if( poBitsPerComponent ) + nBits = (int)Get(poBitsPerComponent); + if (poWidth && Get(poWidth) == nReqXSize && + poHeight && Get(poHeight) == nReqYSize && + poColorSpace && poColorSpace->GetType() == PDFObjectType_Name && + poColorSpace->GetName() == "DeviceGray" && + (nBits == 1 || nBits == 8) ) + { + GDALPDFStream* poStream = poSMask->GetStream(); + GByte* pabyStream = NULL; + + if( poStream == NULL ) + return CE_Failure; + + pabyStream = (GByte*) poStream->GetBytes(); + if( pabyStream == NULL ) + return CE_Failure; + + int nReqXSize1 = (nReqXSize + 7) / 8; + if( (nBits == 8 && poStream->GetLength() != nReqXSize * nReqYSize) || + (nBits == 1 && poStream->GetLength() != nReqXSize1 * nReqYSize) ) + { + VSIFree(pabyStream); + return CE_Failure; + } + + GByte* pabyData = (GByte*) pImage; + if( nReqXSize != nBlockXSize || nReqYSize != nBlockYSize ) + { + memset(pabyData, 0, nBlockXSize * nBlockYSize); + } + + if( nBits == 8 ) + { + for(int j = 0; j < nReqYSize; j++) + { + for(int i = 0; i < nReqXSize; i++) + { + pabyData[j * nBlockXSize + i] = pabyStream[j * nReqXSize + i]; + } + } + } + else + { + for(int j = 0; j < nReqYSize; j++) + { + for(int i = 0; i < nReqXSize; i++) + { + if( pabyStream[j * nReqXSize1 + i / 8] & (1 << (7 - (i % 8))) ) + pabyData[j * nBlockXSize + i] = 255; + else + pabyData[j * nBlockXSize + i] = 0; + } + } + } + + VSIFree(pabyStream); + return CE_None; + } + } + + memset(pImage, 255, nBlockXSize * nBlockYSize); + return CE_None; + } + + if( poGDS->nLastBlockXOff == nBlockXOff && + poGDS->nLastBlockYOff == nBlockYOff && + poGDS->pabyCachedData != NULL ) + { + CPLDebug("PDF", "Using cached block (%d, %d)", + nBlockXOff, nBlockYOff); + // do nothing + } + else + { + if (poGDS->bTried == FALSE) + { + poGDS->bTried = TRUE; + poGDS->pabyCachedData = (GByte*)VSIMalloc3(3, nBlockXSize, nBlockYSize); + } + if (poGDS->pabyCachedData == NULL) + return CE_Failure; + + GDALPDFStream* poStream = poImage->GetStream(); + GByte* pabyStream = NULL; + + if( poStream == NULL ) + return CE_Failure; + + pabyStream = (GByte*) poStream->GetBytes(); + if( pabyStream == NULL ) + return CE_Failure; + + if( poStream->GetLength() != sTile.nBands * nReqXSize * nReqYSize) + { + VSIFree(pabyStream); + return CE_Failure; + } + + memcpy(poGDS->pabyCachedData, pabyStream, poStream->GetLength()); + VSIFree(pabyStream); + poGDS->nLastBlockXOff = nBlockXOff; + poGDS->nLastBlockYOff = nBlockYOff; + } + + GByte* pabyData = (GByte*) pImage; + if( nBand != 4 && (nReqXSize != nBlockXSize || nReqYSize != nBlockYSize) ) + { + memset(pabyData, 0, nBlockXSize * nBlockYSize); + } + + if( poGDS->nBands >= 3 && sTile.nBands == 3 ) + { + for(int j = 0; j < nReqYSize; j++) + { + for(int i = 0; i < nReqXSize; i++) + { + pabyData[j * nBlockXSize + i] = poGDS->pabyCachedData[3 * (j * nReqXSize + i) + nBand - 1]; + } + } + } + else if( sTile.nBands == 1 ) + { + for(int j = 0; j < nReqYSize; j++) + { + for(int i = 0; i < nReqXSize; i++) + { + pabyData[j * nBlockXSize + i] = poGDS->pabyCachedData[j * nReqXSize + i]; + } + } + } + + return CE_None; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr PDFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + PDFDataset *poGDS = (PDFDataset *) poDS; + + if (poGDS->aiTiles.size() ) + { + if ( IReadBlockFromTile(nBlockXOff, nBlockYOff, + pImage) == CE_None ) + { + return CE_None; + } + else + { + poGDS->aiTiles.resize(0); + poGDS->bTried = FALSE; + CPLFree(poGDS->pabyCachedData); + poGDS->pabyCachedData = NULL; + poGDS->nLastBlockXOff = -1; + poGDS->nLastBlockYOff = -1; + } + } + + int nReqXSize = nBlockXSize; + int nReqYSize = nBlockYSize; + if( (nBlockXOff + 1) * nBlockXSize > nRasterXSize ) + nReqXSize = nRasterXSize - nBlockXOff * nBlockXSize; + if( nBlockYSize == 1 ) + nReqYSize = nRasterYSize; + else if( (nBlockYOff + 1) * nBlockYSize > nRasterYSize ) + nReqYSize = nRasterYSize - nBlockYOff * nBlockYSize; + + if (poGDS->bTried == FALSE) + { + poGDS->bTried = TRUE; + if( nBlockYSize == 1 ) + poGDS->pabyCachedData = (GByte*)VSIMalloc3(MAX(3, poGDS->nBands), nRasterXSize, nRasterYSize); + else + poGDS->pabyCachedData = (GByte*)VSIMalloc3(MAX(3, poGDS->nBands), nBlockXSize, nBlockYSize); + } + if (poGDS->pabyCachedData == NULL) + return CE_Failure; + + if( poGDS->nLastBlockXOff == nBlockXOff && + (nBlockYSize == 1 || poGDS->nLastBlockYOff == nBlockYOff) && + poGDS->pabyCachedData != NULL ) + { + /*CPLDebug("PDF", "Using cached block (%d, %d)", + nBlockXOff, nBlockYOff);*/ + // do nothing + } + else + { +#ifdef HAVE_PODOFO + if (!poGDS->bUsePoppler && nBand == 4) + { + memset(pImage, 255, nBlockXSize * nBlockYSize); + return CE_None; + } +#endif + + CPLErr eErr = poGDS->ReadPixels( nBlockXOff * nBlockXSize, + (nBlockYSize == 1) ? 0 : nBlockYOff * nBlockYSize, + nReqXSize, + nReqYSize, + 1, + nBlockXSize, + nBlockXSize * ((nBlockYSize == 1) ? nRasterYSize : nBlockYSize), + poGDS->pabyCachedData); + if( eErr == CE_None ) + { + poGDS->nLastBlockXOff = nBlockXOff; + poGDS->nLastBlockYOff = nBlockYOff; + } + else + { + CPLFree(poGDS->pabyCachedData); + poGDS->pabyCachedData = NULL; + } + + } + if (poGDS->pabyCachedData == NULL) + return CE_Failure; + + if( nBlockYSize == 1 ) + memcpy(pImage, + poGDS->pabyCachedData + (nBand - 1) * nBlockXSize * nRasterYSize + nBlockYOff * nBlockXSize, + nBlockXSize); + else + memcpy(pImage, + poGDS->pabyCachedData + (nBand - 1) * nBlockXSize * nBlockYSize, + nBlockXSize * nBlockYSize); + + return CE_None; +} + +/************************************************************************/ +/* GetOption() */ +/************************************************************************/ + +const char* PDFDataset::GetOption(char** papszOpenOptions, + const char* pszOptionName, + const char* pszDefaultVal) +{ + CPLErr eLastErrType = CPLGetLastErrorType(); + int nLastErrno = CPLGetLastErrorNo(); + CPLString osLastErrorMsg(CPLGetLastErrorMsg()); + CPLXMLNode* psNode = CPLParseXMLString(pszOpenOptionList); + CPLErrorSetState(eLastErrType, nLastErrno, osLastErrorMsg); + if( psNode == NULL ) return pszDefaultVal; + CPLXMLNode* psIter = psNode->psChild; + while( psIter != NULL ) + { + if( EQUAL(CPLGetXMLValue( psIter, "name", "" ), pszOptionName) ) + { + const char* pszVal = CSLFetchNameValue(papszOpenOptions, pszOptionName); + if( pszVal != NULL ) + { + CPLDestroyXMLNode(psNode); + return pszVal; + } + const char* pszAltConfigOption = CPLGetXMLValue( psIter, "alt_config_option", NULL ); + if( pszAltConfigOption != NULL ) + { + pszVal = CPLGetConfigOption(pszAltConfigOption, pszDefaultVal); + CPLDestroyXMLNode(psNode); + return pszVal; + } + CPLDestroyXMLNode(psNode); + return pszDefaultVal; + } + psIter = psIter->psNext; + } + CPLError(CE_Failure, CPLE_AppDefined, + "Requesting an undocumented open option '%s'", pszOptionName); + CPLDestroyXMLNode(psNode); + return pszDefaultVal; +} + +/************************************************************************/ +/* ReadPixels() */ +/************************************************************************/ + +CPLErr PDFDataset::ReadPixels( int nReqXOff, int nReqYOff, + int nReqXSize, int nReqYSize, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GByte* pabyData ) +{ + CPLErr eErr = CE_None; + const char* pszRenderingOptions = GetOption(papszOpenOptions, "RENDERING_OPTIONS", NULL); + +#ifdef HAVE_POPPLER + if(bUsePoppler) + { + SplashColor sColor; + sColor[0] = 255; + sColor[1] = 255; + sColor[2] = 255; + GDALPDFOutputDev *poSplashOut; + poSplashOut = new GDALPDFOutputDev((nBands < 4) ? splashModeRGB8 : splashModeXBGR8, + 4, gFalse, + (nBands < 4) ? sColor : NULL); + + if (pszRenderingOptions != NULL) + { + poSplashOut->SetEnableVector(FALSE); + poSplashOut->SetEnableText(FALSE); + poSplashOut->SetEnableBitmap(FALSE); + + char** papszTokens = CSLTokenizeString2( pszRenderingOptions, " ,", 0 ); + for(int i=0;papszTokens[i] != NULL;i++) + { + if (EQUAL(papszTokens[i], "VECTOR")) + poSplashOut->SetEnableVector(TRUE); + else if (EQUAL(papszTokens[i], "TEXT")) + poSplashOut->SetEnableText(TRUE); + else if (EQUAL(papszTokens[i], "RASTER") || + EQUAL(papszTokens[i], "BITMAP")) + poSplashOut->SetEnableBitmap(TRUE); + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "Value %s is not a valid value for GDAL_PDF_RENDERING_OPTIONS", + papszTokens[i]); + } + } + CSLDestroy(papszTokens); + } + + PDFDoc* poDoc = poDocPoppler; +#ifdef POPPLER_0_20_OR_LATER + poSplashOut->startDoc(poDoc); +#else + poSplashOut->startDoc(poDoc->getXRef()); +#endif + + /* EVIL: we modify a private member... */ + /* poppler (at least 0.12 and 0.14 versions) don't render correctly */ + /* some PDFs and display an error message 'Could not find a OCG with Ref' */ + /* in those cases. This processing of optional content is an addition of */ + /* poppler in comparison to original xpdf, which hasn't the issue. All in */ + /* all, nullifying optContent removes the error message and improves the rendering */ +#ifdef POPPLER_HAS_OPTCONTENT + Catalog* poCatalog = poDoc->getCatalog(); + OCGs* poOldOCGs = poCatalog->optContent; + if (!bUseOCG) + poCatalog->optContent = NULL; +#endif + poDoc->displayPageSlice(poSplashOut, + iPage, + dfDPI, dfDPI, + 0, + TRUE, gFalse, gFalse, + nReqXOff, nReqYOff, + nReqXSize, nReqYSize); + + /* Restore back */ +#ifdef POPPLER_HAS_OPTCONTENT + poCatalog->optContent = poOldOCGs; +#endif + + SplashBitmap* poBitmap = poSplashOut->getBitmap(); + if (poBitmap->getWidth() != nReqXSize || poBitmap->getHeight() != nReqYSize) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Bitmap decoded size (%dx%d) doesn't match raster size (%dx%d)" , + poBitmap->getWidth(), poBitmap->getHeight(), + nReqXSize, nReqYSize); + delete poSplashOut; + return CE_Failure; + } + + GByte* pabyDataR = pabyData; + GByte* pabyDataG = pabyData + nBandSpace; + GByte* pabyDataB = pabyData + 2 * nBandSpace; + GByte* pabyDataA = pabyData + 3 * nBandSpace; + GByte* pabySrc = poBitmap->getDataPtr(); + GByte* pabyAlphaSrc = (GByte*)poBitmap->getAlphaPtr(); + int i, j; + for(j=0;jgetAlphaRowSize(); + pabySrc += poBitmap->getRowSize(); + } + delete poSplashOut; + } +#endif // HAVE_POPPLER + +#ifdef HAVE_PODOFO + if (!bUsePoppler) + { + if( bPdfToPpmFailed ) + return CE_Failure; + + if (pszRenderingOptions != NULL) + { + CPLError(CE_Warning, CPLE_NotSupported, + "GDAL_PDF_RENDERING_OPTIONS only supported " + "when PDF driver is compiled against Poppler."); + } + + CPLString osTmpFilename; + int nRet; + +#ifdef notdef + int bUseSpawn = CSLTestBoolean(CPLGetConfigOption("GDAL_PDF_USE_SPAWN", "YES")); + if( !bUseSpawn ) + { + CPLString osCmd = CPLSPrintf("pdftoppm -r %f -x %d -y %d -W %d -H %d -f %d -l %d \"%s\"", + dfDPI, + nReqXOff, + nReqYOff, + nReqXSize, + nReqYSize, + iPage, iPage, + osFilename.c_str()); + + if (osUserPwd.size() != 0) + { + osCmd += " -upw \""; + osCmd += osUserPwd; + osCmd += "\""; + } + + CPLString osTmpFilenamePrefix = CPLGenerateTempFilename("pdf"); + osTmpFilename = CPLSPrintf("%s-%d.ppm", + osTmpFilenamePrefix.c_str(), + iPage); + osCmd += CPLSPrintf(" \"%s\"", osTmpFilenamePrefix.c_str()); + + CPLDebug("PDF", "Running '%s'", osCmd.c_str()); + nRet = CPLSystem(NULL, osCmd.c_str()); + } + else +#endif // notdef + { + char** papszArgs = NULL; + papszArgs = CSLAddString(papszArgs, "pdftoppm"); + papszArgs = CSLAddString(papszArgs, "-r"); + papszArgs = CSLAddString(papszArgs, CPLSPrintf("%f", dfDPI)); + papszArgs = CSLAddString(papszArgs, "-x"); + papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqXOff)); + papszArgs = CSLAddString(papszArgs, "-y"); + papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqYOff)); + papszArgs = CSLAddString(papszArgs, "-W"); + papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqXSize)); + papszArgs = CSLAddString(papszArgs, "-H"); + papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", nReqYSize)); + papszArgs = CSLAddString(papszArgs, "-f"); + papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", iPage)); + papszArgs = CSLAddString(papszArgs, "-l"); + papszArgs = CSLAddString(papszArgs, CPLSPrintf("%d", iPage)); + if (osUserPwd.size() != 0) + { + papszArgs = CSLAddString(papszArgs, "-upw"); + papszArgs = CSLAddString(papszArgs, osUserPwd.c_str()); + } + papszArgs = CSLAddString(papszArgs, osFilename.c_str()); + + osTmpFilename = CPLSPrintf("/vsimem/pdf/temp_%p.ppm", this); + VSILFILE* fpOut = VSIFOpenL(osTmpFilename, "wb"); + if( fpOut != NULL ) + { + nRet = CPLSpawn(papszArgs, NULL, fpOut, FALSE); + VSIFCloseL(fpOut); + } + else + nRet = -1; + + CSLDestroy(papszArgs); + } + + if (nRet == 0) + { + GDALDataset* poDS = (GDALDataset*) GDALOpen(osTmpFilename, GA_ReadOnly); + if (poDS) + { + if (poDS->GetRasterCount() == 3) + { + eErr = poDS->RasterIO(GF_Read, 0, 0, + nReqXSize, + nReqYSize, + pabyData, + nReqXSize, nReqYSize, + GDT_Byte, 3, NULL, + nPixelSpace, nLineSpace, nBandSpace, NULL); + } + delete poDS; + } + } + else + { + CPLDebug("PDF", "Ret code = %d", nRet); + bPdfToPpmFailed = TRUE; + eErr = CE_Failure; + } + VSIUnlink(osTmpFilename); + } +#endif + + return eErr; +} + +/************************************************************************/ +/* ==================================================================== */ +/* PDFImageRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class PDFImageRasterBand : public PDFRasterBand +{ + friend class PDFDataset; + + public: + + PDFImageRasterBand( PDFDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + + +/************************************************************************/ +/* PDFImageRasterBand() */ +/************************************************************************/ + +PDFImageRasterBand::PDFImageRasterBand( PDFDataset *poDS, int nBand ) : PDFRasterBand(poDS, nBand) + +{ +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr PDFImageRasterBand::IReadBlock( int CPL_UNUSED nBlockXOff, int nBlockYOff, + void * pImage ) +{ + PDFDataset *poGDS = (PDFDataset *) poDS; + CPLAssert(poGDS->poImageObj != NULL); + + if (poGDS->bTried == FALSE) + { + int nBands = (poGDS->nBands == 1) ? 1 : 3; + poGDS->bTried = TRUE; + if (nBands == 3) + { + poGDS->pabyCachedData = (GByte*)VSIMalloc3(nBands, nRasterXSize, nRasterYSize); + if (poGDS->pabyCachedData == NULL) + return CE_Failure; + } + + GDALPDFStream* poStream = poGDS->poImageObj->GetStream(); + GByte* pabyStream = NULL; + + if (poStream == NULL || + poStream->GetLength() != nBands * nRasterXSize * nRasterYSize || + (pabyStream = (GByte*) poStream->GetBytes()) == NULL) + { + VSIFree(poGDS->pabyCachedData); + poGDS->pabyCachedData = NULL; + return CE_Failure; + } + + if (nBands == 3) + { + /* pixel interleaved to band interleaved */ + for(int i = 0; i < nRasterXSize * nRasterYSize; i++) + { + poGDS->pabyCachedData[0 * nRasterXSize * nRasterYSize + i] = pabyStream[3 * i + 0]; + poGDS->pabyCachedData[1 * nRasterXSize * nRasterYSize + i] = pabyStream[3 * i + 1]; + poGDS->pabyCachedData[2 * nRasterXSize * nRasterYSize + i] = pabyStream[3 * i + 2]; + } + VSIFree(pabyStream); + } + else + poGDS->pabyCachedData = pabyStream; + } + + if (poGDS->pabyCachedData == NULL) + return CE_Failure; + + if (nBand == 4) + memset(pImage, 255, nRasterXSize); + else + memcpy(pImage, + poGDS->pabyCachedData + (nBand - 1) * nRasterXSize * nRasterYSize + nBlockYOff * nRasterXSize, + nRasterXSize); + + return CE_None; +} + +/************************************************************************/ +/* ~PDFDataset() */ +/************************************************************************/ + +PDFDataset::PDFDataset() +{ + bUsePoppler = FALSE; +#ifdef HAVE_POPPLER + poDocPoppler = NULL; +#endif +#ifdef HAVE_PODOFO + poDocPodofo = NULL; + bPdfToPpmFailed = FALSE; +#endif + poImageObj = NULL; + pszWKT = NULL; + dfDPI = GDAL_DEFAULT_DPI; + dfMaxArea = 0; + adfGeoTransform[0] = 0; + adfGeoTransform[1] = 1; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = 0; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = 1; + bHasCTM = FALSE; + bGeoTransformValid = FALSE; + nGCPCount = 0; + pasGCPList = NULL; + bProjDirty = FALSE; + bNeatLineDirty = FALSE; + bInfoDirty = FALSE; + bXMPDirty = FALSE; + bTried = FALSE; + pabyCachedData = NULL; + nLastBlockXOff = -1; + nLastBlockYOff = -1; + iPage = -1; + poNeatLine = NULL; + bUseOCG = FALSE; + poCatalogObject = NULL; +#ifdef HAVE_POPPLER + poCatalogObjectPoppler = NULL; +#endif + nBlockXSize = 0; + nBlockYSize = 0; + papszOpenOptions = NULL; + + nLayers = 0; + papoLayers = NULL; + + dfPageWidth = dfPageHeight = 0; + + bSetStyle = CSLTestBoolean(CPLGetConfigOption("OGR_PDF_SET_STYLE", "YES")); + + InitMapOperators(); +} + +/************************************************************************/ +/* PDFFreeDoc() */ +/************************************************************************/ + +#ifdef HAVE_POPPLER +static void PDFFreeDoc(PDFDoc* poDoc) +{ + if (poDoc) + { + /* hack to avoid potential cross heap issues on Win32 */ + /* str is the VSIPDFFileStream object passed in the constructor of PDFDoc */ + // NOTE: This is potentially very dangerous. See comment in VSIPDFFileStream::FillBuffer() */ + delete poDoc->str; + poDoc->str = NULL; + + delete poDoc; + } +} +#endif + +/************************************************************************/ +/* GetCatalog() */ +/************************************************************************/ + +GDALPDFObject* PDFDataset::GetCatalog() +{ + if (poCatalogObject) + return poCatalogObject; + +#ifdef HAVE_POPPLER + if (bUsePoppler) + { + poCatalogObjectPoppler = new ObjectAutoFree; + poDocPoppler->getXRef()->getCatalog(poCatalogObjectPoppler); + if (!poCatalogObjectPoppler->isNull()) + poCatalogObject = new GDALPDFObjectPoppler(poCatalogObjectPoppler, FALSE); + } +#endif + +#ifdef HAVE_PODOFO + if (!bUsePoppler) + { + int nCatalogNum = 0, nCatalogGen = 0; + VSILFILE* fp = VSIFOpenL(osFilename.c_str(), "rb"); + if (fp != NULL) + { + GDALPDFWriter oWriter(fp, TRUE); + if (oWriter.ParseTrailerAndXRef()) + { + nCatalogNum = oWriter.GetCatalogNum(); + nCatalogGen = oWriter.GetCatalogGen(); + } + oWriter.Close(); + } + + PoDoFo::PdfObject* poCatalogPodofo = + poDocPodofo->GetObjects().GetObject(PoDoFo::PdfReference(nCatalogNum, nCatalogGen)); + if (poCatalogPodofo) + poCatalogObject = new GDALPDFObjectPodofo(poCatalogPodofo, poDocPodofo->GetObjects()); + } +#endif + + return poCatalogObject; +} + +/************************************************************************/ +/* ~PDFDataset() */ +/************************************************************************/ + +PDFDataset::~PDFDataset() +{ + CPLFree(pabyCachedData); + pabyCachedData = NULL; + + delete poNeatLine; + poNeatLine = NULL; + + /* Collect data necessary to update */ + int nNum = poPageObj->GetRefNum(); + int nGen = poPageObj->GetRefGen(); + GDALPDFDictionaryRW* poPageDictCopy = NULL; + GDALPDFDictionaryRW* poCatalogDictCopy = NULL; + if (eAccess == GA_Update && + (bProjDirty || bNeatLineDirty || bInfoDirty || bXMPDirty) && + nNum != 0 && + poPageObj != NULL && + poPageObj->GetType() == PDFObjectType_Dictionary) + { + poPageDictCopy = poPageObj->GetDictionary()->Clone(); + + if (bXMPDirty) + { + /* We need the catalog because it points to the XMP Metadata object */ + GetCatalog(); + if (poCatalogObject && poCatalogObject->GetType() == PDFObjectType_Dictionary) + poCatalogDictCopy = poCatalogObject->GetDictionary()->Clone(); + } + } + + /* Close document (and file descriptor) to be able to open it */ + /* in read-write mode afterwards */ + delete poPageObj; + poPageObj = NULL; + delete poCatalogObject; + poCatalogObject = NULL; +#ifdef HAVE_POPPLER + delete poCatalogObjectPoppler; + PDFFreeDoc(poDocPoppler); + poDocPoppler = NULL; +#endif +#ifdef HAVE_PODOFO + delete poDocPodofo; + poDocPodofo = NULL; +#endif + + /* Now do the update */ + if (poPageDictCopy) + { + VSILFILE* fp = VSIFOpenL(osFilename, "rb+"); + if (fp != NULL) + { + GDALPDFWriter oWriter(fp, TRUE); + if (oWriter.ParseTrailerAndXRef()) + { + if ((bProjDirty || bNeatLineDirty) && poPageDictCopy != NULL) + oWriter.UpdateProj(this, dfDPI, + poPageDictCopy, nNum, nGen); + + if (bInfoDirty) + oWriter.UpdateInfo(this); + + if (bXMPDirty && poCatalogDictCopy != NULL) + oWriter.UpdateXMP(this, poCatalogDictCopy); + } + oWriter.Close(); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot open %s in update mode", osFilename.c_str()); + } + } + delete poPageDictCopy; + poPageDictCopy = NULL; + delete poCatalogDictCopy; + poCatalogDictCopy = NULL; + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + pasGCPList = NULL; + nGCPCount = 0; + } + CPLFree(pszWKT); + pszWKT = NULL; + CSLDestroy(papszOpenOptions); + + CleanupIntermediateResources(); + + for(int i=0;iGetBlockSize(&nBandBlockXSize, &nBandBlockYSize); + if( aiTiles.size() == 0 && + eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize && + (nBufXSize > nBandBlockXSize || nBufYSize > nBandBlockYSize) && + eBufType == GDT_Byte && nBandCount == nBands && + (nBands >= 3 && panBandMap[0] == 1 && panBandMap[1] == 2 && + panBandMap[2] == 3 && ( nBands == 3 || panBandMap[3] == 4)) ) + { + int bReadPixels = TRUE; +#ifdef HAVE_PODOFO + if (!bUsePoppler && nBands == 4) + { + bReadPixels = FALSE; + } +#endif + if( bReadPixels ) + return ReadPixels(nXOff, nYOff, nXSize, nYSize, + nPixelSpace, nLineSpace, nBandSpace, (GByte*)pData); + } + + return GDALPamDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int PDFDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + if (strncmp(poOpenInfo->pszFilename, "PDF:", 4) == 0) + return TRUE; + if (strncmp(poOpenInfo->pszFilename, "PDF_IMAGE:", 10) == 0) + return TRUE; + + if (poOpenInfo->nHeaderBytes < 128) + return FALSE; + + return strncmp((const char*)poOpenInfo->pabyHeader, "%PDF", 4) == 0; +} + +/************************************************************************/ +/* PDFDatasetErrorFunction() */ +/************************************************************************/ + +#ifdef HAVE_POPPLER + +static void PDFDatasetErrorFunctionCommon(const CPLString& osError) +{ + if (strcmp(osError.c_str(), "Incorrect password") == 0) + return; + /* Reported on newer USGS GeoPDF */ + if (strcmp(osError.c_str(), "Couldn't find group for reference to set OFF") == 0) + { + CPLDebug("PDF", "%s", osError.c_str()); + return; + } + + CPLError(CE_Failure, CPLE_AppDefined, "%s", osError.c_str()); +} + +#ifdef POPPLER_0_20_OR_LATER +static void PDFDatasetErrorFunction(CPL_UNUSED void* userData, CPL_UNUSED ErrorCategory eErrCatagory, +#ifdef POPPLER_0_23_OR_LATER + Goffset nPos, +#else + int nPos, +#endif + char *pszMsg) +{ + CPLString osError; + + if (nPos >= 0) + osError.Printf("Pos = %d, ", (int)nPos); + osError += pszMsg; + PDFDatasetErrorFunctionCommon(osError); +} +#else +static void PDFDatasetErrorFunction(int nPos, char *pszMsg, va_list args) +{ + CPLString osError; + + if (nPos >= 0) + osError.Printf("Pos = %d, ", nPos); + osError += CPLString().vPrintf(pszMsg, args); + PDFDatasetErrorFunctionCommon(osError); +} +#endif +#endif + +/************************************************************************/ +/* GDALPDFParseStreamContentOnlyDrawForm() */ +/************************************************************************/ + +static +CPLString GDALPDFParseStreamContentOnlyDrawForm(const char* pszContent) +{ + CPLString osToken; + char ch; + int nCurIdx = 0; + CPLString osCurrentForm; + + //CPLDebug("PDF", "content = %s", pszContent); + + while((ch = *pszContent) != '\0') + { + if (ch == '%') + { + /* Skip comments until end-of-line */ + while((ch = *pszContent) != '\0') + { + if (ch == '\r' || ch == '\n') + break; + pszContent ++; + } + if (ch == 0) + break; + } + else if (ch == ' ' || ch == '\r' || ch == '\n') + { + if (osToken.size()) + { + if (nCurIdx == 0 && osToken[0] == '/') + { + osCurrentForm = osToken.substr(1); + nCurIdx ++; + } + else if (nCurIdx == 1 && osToken == "Do") + { + nCurIdx ++; + } + else + { + return ""; + } + } + osToken = ""; + } + else + osToken += ch; + pszContent ++; + } + + return osCurrentForm; +} + +/************************************************************************/ +/* GDALPDFParseStreamContent() */ +/************************************************************************/ + +typedef enum +{ + STATE_INIT, + STATE_AFTER_q, + STATE_AFTER_cm, + STATE_AFTER_Do +} PDFStreamState; + +/* This parser is reduced to understanding sequences that draw rasters, such as : + q + scaleX 0 0 scaleY translateX translateY cm + /ImXXX Do + Q + + All other sequences will abort the parsing. + + Returns TRUE if the stream only contains images. +*/ + +static +int GDALPDFParseStreamContent(const char* pszContent, + GDALPDFDictionary* poXObjectDict, + double* pdfDPI, + int* pbDPISet, + int* pnBands, + std::vector& asTiles, + int bAcceptRotationTerms) +{ + CPLString osToken; + char ch; + PDFStreamState nState = STATE_INIT; + int nCurIdx = 0; + double adfVals[6]; + CPLString osCurrentImage; + + double dfDPI = DEFAULT_DPI; + *pbDPISet = FALSE; + + while((ch = *pszContent) != '\0') + { + if (ch == '%') + { + /* Skip comments until end-of-line */ + while((ch = *pszContent) != '\0') + { + if (ch == '\r' || ch == '\n') + break; + pszContent ++; + } + if (ch == 0) + break; + } + else if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') + { + if (osToken.size()) + { + if (nState == STATE_INIT) + { + if (osToken == "q") + { + nState = STATE_AFTER_q; + nCurIdx = 0; + } + else if (osToken != "Q") + return FALSE; + } + else if (nState == STATE_AFTER_q) + { + if (osToken == "q") + { + // ignore + } + else if (nCurIdx < 6) + { + adfVals[nCurIdx ++] = CPLAtof(osToken); + } + else if (nCurIdx == 6 && osToken == "cm") + { + nState = STATE_AFTER_cm; + nCurIdx = 0; + } + else + return FALSE; + } + else if (nState == STATE_AFTER_cm) + { + if (nCurIdx == 0 && osToken[0] == '/') + { + osCurrentImage = osToken.substr(1); + } + else if (osToken == "Do") + { + nState = STATE_AFTER_Do; + } + else + return FALSE; + } + else if (nState == STATE_AFTER_Do) + { + if (osToken == "Q") + { + GDALPDFObject* poImage = poXObjectDict->Get(osCurrentImage); + if (poImage != NULL && poImage->GetType() == PDFObjectType_Dictionary) + { + GDALPDFTileDesc sTile; + GDALPDFDictionary* poImageDict = poImage->GetDictionary(); + GDALPDFObject* poWidth = poImageDict->Get("Width"); + GDALPDFObject* poHeight = poImageDict->Get("Height"); + GDALPDFObject* poColorSpace = poImageDict->Get("ColorSpace"); + GDALPDFObject* poSMask = poImageDict->Get("SMask"); + if (poColorSpace && poColorSpace->GetType() == PDFObjectType_Name) + { + if (poColorSpace->GetName() == "DeviceRGB") + { + sTile.nBands = 3; + if ( *pnBands < 3) + *pnBands = 3; + } + else if (poColorSpace->GetName() == "DeviceGray") + { + sTile.nBands = 1; + if ( *pnBands < 1) + *pnBands = 1; + } + else + sTile.nBands = 0; + } + if ( poSMask != NULL ) + *pnBands = 4; + + if (poWidth && poHeight && ((bAcceptRotationTerms && adfVals[1] == -adfVals[2]) || + (!bAcceptRotationTerms && adfVals[1] == 0.0 && adfVals[2] == 0.0))) + { + double dfWidth = Get(poWidth); + double dfHeight = Get(poHeight); + double dfScaleX = adfVals[0]; + double dfScaleY = adfVals[3]; + double dfDPI_X = ROUND_TO_INT_IF_CLOSE(dfWidth / dfScaleX * DEFAULT_DPI, 1e-3); + double dfDPI_Y = ROUND_TO_INT_IF_CLOSE(dfHeight / dfScaleY * DEFAULT_DPI, 1e-3); + //CPLDebug("PDF", "Image %s, width = %.16g, height = %.16g, scaleX = %.16g, scaleY = %.16g --> DPI_X = %.16g, DPI_Y = %.16g", + // osCurrentImage.c_str(), dfWidth, dfHeight, dfScaleX, dfScaleY, dfDPI_X, dfDPI_Y); + if (dfDPI_X > dfDPI) dfDPI = dfDPI_X; + if (dfDPI_Y > dfDPI) dfDPI = dfDPI_Y; + + memcpy(&(sTile.adfCM), adfVals, 6 * sizeof(double)); + sTile.poImage = poImage; + sTile.dfWidth = dfWidth; + sTile.dfHeight = dfHeight; + asTiles.push_back(sTile); + + *pbDPISet = TRUE; + *pdfDPI = dfDPI; + } + } + nState = STATE_INIT; + } + else + return FALSE; + } + } + osToken = ""; + } + else + osToken += ch; + pszContent ++; + } + + return TRUE; +} + +/************************************************************************/ +/* CheckTiledRaster() */ +/************************************************************************/ + +int PDFDataset::CheckTiledRaster() +{ + size_t i; + int nBlockXSize = 0, nBlockYSize = 0; + const double dfUserUnit = dfDPI * USER_UNIT_IN_INCH; + + /* First pass : check that all tiles have same DPI, */ + /* are contained entirely in the raster size, */ + /* and determine the block size */ + for(i=0; iGetDictionary(); + GDALPDFObject* poBitsPerComponent = poImageDict->Get("BitsPerComponent"); + GDALPDFObject* poColorSpace = poImageDict->Get("ColorSpace"); + GDALPDFObject* poFilter = poImageDict->Get("Filter"); + + /* Podofo cannot uncompress JPEG2000 streams */ + if( !bUsePoppler && poFilter != NULL && + poFilter->GetType() == PDFObjectType_Name && + poFilter->GetName() == "JPXDecode" ) + { + CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading", + (int)i); + return FALSE; + } + + if( poBitsPerComponent == NULL || + Get(poBitsPerComponent) != 8 || + poColorSpace == NULL || + poColorSpace->GetType() != PDFObjectType_Name || + (poColorSpace->GetName() != "DeviceRGB" && + poColorSpace->GetName() != "DeviceGray") ) + { + CPLDebug("PDF", "Tile %d : Incompatible image for tiled reading", + (int)i); + return FALSE; + } + + if( fabs(dfDrawWidth - asTiles[i].dfWidth) > 1e-2 || + fabs(dfDrawHeight - asTiles[i].dfHeight) > 1e-2 || + fabs(nWidth - asTiles[i].dfWidth) > 1e-8 || + fabs(nHeight - asTiles[i].dfHeight) > 1e-8 || + fabs(nX - dfX) > 1e-1 || + fabs(nY - dfY) > 1e-1 || + nX < 0 || nY < 0 || nX + nWidth > nRasterXSize || + nY >= nRasterYSize ) + { + CPLDebug("PDF", "Tile %d : %f %f %f %f %f %f", + (int)i, dfX, dfY, dfDrawWidth, dfDrawHeight, + asTiles[i].dfWidth, asTiles[i].dfHeight); + return FALSE; + } + if( nBlockXSize == 0 && nBlockYSize == 0 && + nX == 0 && nY != 0 ) + { + nBlockXSize = nWidth; + nBlockYSize = nHeight; + } + } + if( nBlockXSize <= 0 || nBlockYSize <= 0 || nBlockXSize > 2048 || nBlockYSize > 2048 ) + return FALSE; + + int nXBlocks = DIV_ROUND_UP(nRasterXSize, nBlockXSize); + int nYBlocks = DIV_ROUND_UP(nRasterYSize, nBlockYSize); + + /* Second pass to determine that all tiles are properly aligned on block size */ + for(i=0; i 0 && nHeight != nBlockYSize ) + bOK = FALSE; + if( nY == 0 && nHeight != nRasterYSize - (nYBlocks - 1) * nBlockYSize) + bOK = FALSE; + + if( !bOK ) + { + CPLDebug("PDF", "Tile %d : %d %d %d %d", + (int)i, nX, nY, nWidth, nHeight); + return FALSE; + } + } + + /* Third pass to set the aiTiles array */ + aiTiles.resize(nXBlocks * nYBlocks, -1); + for(i=0; inBlockXSize = nBlockXSize; + this->nBlockYSize = nBlockYSize; + + return TRUE; +} + +/************************************************************************/ +/* GuessDPI() */ +/************************************************************************/ + +void PDFDataset::GuessDPI(GDALPDFDictionary* poPageDict, int* pnBands) +{ + const char* pszDPI = GetOption(papszOpenOptions, "DPI", NULL); + if (pszDPI != NULL) + { + dfDPI = CPLAtof(pszDPI); + } + else + { + /* Try to get a better value from the images that are drawn */ + /* Very simplistic logic. Will only work for raster only PDF */ + + GDALPDFObject* poContents = poPageDict->Get("Contents"); + if (poContents != NULL && poContents->GetType() == PDFObjectType_Array) + { + GDALPDFArray* poContentsArray = poContents->GetArray(); + if (poContentsArray->GetLength() == 1) + { + poContents = poContentsArray->Get(0); + } + } + + GDALPDFObject* poXObject = poPageDict->LookupObject("Resources.XObject"); + if (poContents != NULL && + poContents->GetType() == PDFObjectType_Dictionary && + poXObject != NULL && + poXObject->GetType() == PDFObjectType_Dictionary) + { + GDALPDFDictionary* poXObjectDict = poXObject->GetDictionary(); + GDALPDFDictionary* poContentDict = poXObjectDict; + GDALPDFStream* poPageStream = poContents->GetStream(); + if (poPageStream != NULL) + { + char* pszContent = NULL; + int nLength = poPageStream->GetLength(); + int bResetTiles = FALSE; + double dfScaleDPI = 1.0; + + if( nLength < 100000 ) + { + CPLString osForm; + pszContent = poPageStream->GetBytes(); + if( pszContent != NULL ) + { +#ifdef DEBUG + const char* pszDumpStream = CPLGetConfigOption("PDF_DUMP_STREAM", NULL); + if( pszDumpStream != NULL ) + { + VSILFILE* fpDump = VSIFOpenL(pszDumpStream, "wb"); + if( fpDump ) + { + VSIFWriteL(pszContent, 1, nLength, fpDump); + VSIFCloseL(fpDump); + } + } +#endif // DEBUG + osForm = GDALPDFParseStreamContentOnlyDrawForm(pszContent); + if (osForm.size() == 0) + { + /* Special case for USGS Topo PDF, like CA_Hollywood_20090811_OM_geo.pdf */ + const char* pszOGCDo = strstr(pszContent, " /XO1 Do"); + if( pszOGCDo ) + { + const char* pszcm = strstr(pszContent, " cm "); + if( pszcm != NULL && pszcm < pszOGCDo ) + { + const char* pszNextcm = strstr(pszcm + 2, "cm"); + if( pszNextcm == NULL || pszNextcm > pszOGCDo ) + { + const char* pszIter = pszcm; + while( pszIter > pszContent ) + { + if( (*pszIter >= '0' && *pszIter <= '9') || + *pszIter == '-' || + *pszIter == '.' || + *pszIter == ' ' ) + pszIter --; + else + { + pszIter ++; + break; + } + } + CPLString oscm(pszIter); + oscm.resize(pszcm - pszIter); + char** papszTokens = CSLTokenizeString(oscm); + double dfScaleX = -1, dfScaleY = -2; + if( CSLCount(papszTokens) == 6 ) + { + dfScaleX = CPLAtof(papszTokens[0]); + dfScaleY = CPLAtof(papszTokens[3]); + } + CSLDestroy(papszTokens); + if( dfScaleX == dfScaleY && dfScaleX > 0.0 ) + { + osForm = "XO1"; + bResetTiles = TRUE; + dfScaleDPI = 1.0 / dfScaleX; + } + } + } + else + { + osForm = "XO1"; + bResetTiles = TRUE; + } + } + /* Special case for USGS Topo PDF, like CA_Sacramento_East_20120308_TM_geo.pdf */ + else + { + CPLString osOCG = FindLayerOCG(poPageDict, "Orthoimage"); + if( osOCG.size() ) + { + const char* pszBDCLookup = CPLSPrintf("/OC /%s BDC", osOCG.c_str()); + const char* pszBDC = strstr(pszContent, pszBDCLookup); + if( pszBDC != NULL ) + { + const char* pszIter = pszBDC + strlen(pszBDCLookup); + while( *pszIter != '\0' ) + { + if( *pszIter == 13 || *pszIter == 10 || + *pszIter == ' '|| *pszIter == 'q' ) + pszIter ++; + else + break; + } + if( strncmp(pszIter, "1 0 0 1 0 0 cm\n", + strlen( "1 0 0 1 0 0 cm\n" )) == 0 ) + pszIter += strlen("1 0 0 1 0 0 cm\n"); + if( *pszIter == '/' ) + { + pszIter ++; + const char* pszDo = strstr(pszIter, " Do"); + if( pszDo != NULL ) + { + osForm = pszIter; + osForm.resize(pszDo - pszIter); + bResetTiles = TRUE; + } + } + } + } + } + } + } + + if (osForm.size()) + { + CPLFree(pszContent); + pszContent = NULL; + + GDALPDFObject* poObjForm = poXObjectDict->Get(osForm); + if (poObjForm != NULL && + poObjForm->GetType() == PDFObjectType_Dictionary && + (poPageStream = poObjForm->GetStream()) != NULL) + { + GDALPDFDictionary* poObjFormDict = poObjForm->GetDictionary(); + GDALPDFObject* poSubtype; + if ((poSubtype = poObjFormDict->Get("Subtype")) != NULL && + poSubtype->GetType() == PDFObjectType_Name && + poSubtype->GetName() == "Form") + { + int nLength = poPageStream->GetLength(); + if( nLength < 100000 ) + { + pszContent = poPageStream->GetBytes(); + + GDALPDFObject* poXObject2 = poObjFormDict->LookupObject("Resources.XObject"); + if( poXObject2 != NULL && + poXObject2->GetType() == PDFObjectType_Dictionary ) + poContentDict = poXObject2->GetDictionary(); + } + } + } + } + } + + if (pszContent != NULL) + { + int bDPISet = FALSE; + + const char* pszContentToParse = pszContent; + if( bResetTiles ) + { + while( *pszContentToParse != '\0' ) + { + if( *pszContentToParse == 13 || *pszContentToParse == 10 || + *pszContentToParse == ' ' || + (*pszContentToParse >= '0' && *pszContentToParse <= '9') || + *pszContentToParse == '.' || + *pszContentToParse == '-' || + *pszContentToParse == 'l' || + *pszContentToParse == 'm' || + *pszContentToParse == 'n' || + *pszContentToParse == 'W' ) + pszContentToParse ++; + else + break; + } + } + + GDALPDFParseStreamContent(pszContentToParse, + poContentDict, + &(dfDPI), + &bDPISet, + pnBands, + asTiles, + bResetTiles); + CPLFree(pszContent); + if (bDPISet) + { + dfDPI *= dfScaleDPI; + + CPLDebug("PDF", "DPI guessed from contents stream = %.16g", dfDPI); + SetMetadataItem("DPI", CPLSPrintf("%.16g", dfDPI)); + if( bResetTiles ) + asTiles.resize(0); + } + else + asTiles.resize(0); + } + } + } + + GDALPDFObject* poUserUnit = NULL; + if ( (poUserUnit = poPageDict->Get("UserUnit")) != NULL && + (poUserUnit->GetType() == PDFObjectType_Int || + poUserUnit->GetType() == PDFObjectType_Real) ) + { + dfDPI = ROUND_TO_INT_IF_CLOSE(Get(poUserUnit) * DEFAULT_DPI); + CPLDebug("PDF", "Found UserUnit in Page --> DPI = %.16g", dfDPI); + SetMetadataItem("DPI", CPLSPrintf("%.16g", dfDPI)); + } + } + + if (dfDPI < 1 || dfDPI > 7200) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid value for GDAL_PDF_DPI. Using default value instead"); + dfDPI = GDAL_DEFAULT_DPI; + } +} + +/************************************************************************/ +/* FindXMP() */ +/************************************************************************/ + +void PDFDataset::FindXMP(GDALPDFObject* poObj) +{ + if (poObj->GetType() != PDFObjectType_Dictionary) + return; + + GDALPDFDictionary* poDict = poObj->GetDictionary(); + GDALPDFObject* poType = poDict->Get("Type"); + GDALPDFObject* poSubtype = poDict->Get("Subtype"); + if (poType == NULL || + poType->GetType() != PDFObjectType_Name || + poType->GetName() != "Metadata" || + poSubtype == NULL || + poSubtype->GetType() != PDFObjectType_Name || + poSubtype->GetName() != "XML") + { + return; + } + + GDALPDFStream* poStream = poObj->GetStream(); + if (poStream == NULL) + return; + + char* pszContent = poStream->GetBytes(); + int nLength = (int)poStream->GetLength(); + if (pszContent != NULL && nLength > 15 && + strncmp(pszContent, "GetType() != PDFObjectType_Dictionary) + return; + + GDALPDFDictionary* poInfoObjDict = poInfoObj->GetDictionary(); + GDALPDFObject* poItem = NULL; + int bOneMDISet = FALSE; + if ((poItem = poInfoObjDict->Get("Author")) != NULL && + poItem->GetType() == PDFObjectType_String) + { + SetMetadataItem("AUTHOR", poItem->GetString().c_str()); + bOneMDISet = TRUE; + } + if ((poItem = poInfoObjDict->Get("Creator")) != NULL && + poItem->GetType() == PDFObjectType_String) + { + SetMetadataItem("CREATOR", poItem->GetString().c_str()); + bOneMDISet = TRUE; + } + if ((poItem = poInfoObjDict->Get("Keywords")) != NULL && + poItem->GetType() == PDFObjectType_String) + { + SetMetadataItem("KEYWORDS", poItem->GetString().c_str()); + bOneMDISet = TRUE; + } + if ((poItem = poInfoObjDict->Get("Subject")) != NULL && + poItem->GetType() == PDFObjectType_String) + { + SetMetadataItem("SUBJECT", poItem->GetString().c_str()); + bOneMDISet = TRUE; + } + if ((poItem = poInfoObjDict->Get("Title")) != NULL && + poItem->GetType() == PDFObjectType_String) + { + SetMetadataItem("TITLE", poItem->GetString().c_str()); + bOneMDISet = TRUE; + } + if ((poItem = poInfoObjDict->Get("Producer")) != NULL && + poItem->GetType() == PDFObjectType_String) + { + if (bOneMDISet || poItem->GetString() != "PoDoFo - http://podofo.sf.net") + { + SetMetadataItem("PRODUCER", poItem->GetString().c_str()); + bOneMDISet = TRUE; + } + } + if ((poItem = poInfoObjDict->Get("CreationDate")) != NULL && + poItem->GetType() == PDFObjectType_String) + { + if (bOneMDISet) + SetMetadataItem("CREATION_DATE", poItem->GetString().c_str()); + } +} + +#ifdef HAVE_POPPLER + +/************************************************************************/ +/* AddLayer() */ +/************************************************************************/ + +void PDFDataset::AddLayer(const char* pszLayerName, OptionalContentGroup* ocg) +{ + int nNewIndex = osLayerList.size() /*/ 2*/; + + if (nNewIndex == 100) + { + CPLStringList osNewLayerList; + for(int i=0;i<100;i++) + { + osNewLayerList.AddNameValue(CPLSPrintf("LAYER_%03d_NAME", i), + osLayerList[/*2 * */ i] + strlen("LAYER_00_NAME=")); + /*osNewLayerList.AddNameValue(CPLSPrintf("LAYER_%03d_INIT_STATE", i), + osLayerList[i * 2 + 1] + strlen("LAYER_00_INIT_STATE="));*/ + } + osLayerList = osNewLayerList; + } + + char szFormatName[64]; + /* char szFormatInitState[64]; */ + sprintf(szFormatName, "LAYER_%%0%dd_NAME", nNewIndex >= 100 ? 3 : 2); + /* sprintf(szFormatInitState, "LAYER_%%0%dd_INIT_STATE", nNewIndex >= 100 ? 3 : 2); */ + + osLayerList.AddNameValue(CPLSPrintf(szFormatName, nNewIndex), + pszLayerName); + /*osLayerList.AddNameValue(CPLSPrintf(szFormatInitState, nNewIndex), + (ocg == NULL || ocg->getState() == OptionalContentGroup::On) ? "ON" : "OFF");*/ + oLayerOCGMap[pszLayerName] = ocg; + + //if (ocg != NULL && ocg->getState() == OptionalContentGroup::Off) + // bUseOCG = TRUE; +} + +/************************************************************************/ +/* ExploreLayers() */ +/************************************************************************/ + +void PDFDataset::ExploreLayers(GDALPDFArray* poArray, + int nRecLevel, + CPLString osTopLayer) +{ + if( nRecLevel == 16 ) + return; + + int nLength = poArray->GetLength(); + CPLString osCurLayer; + for(int i=0;iGet(i); + if (i == 0 && poObj->GetType() == PDFObjectType_String) + { + CPLString osName = PDFSanitizeLayerName(poObj->GetString().c_str()); + if (osTopLayer.size()) + osTopLayer = osTopLayer + "." + osName; + else + osTopLayer = osName; + AddLayer(osTopLayer.c_str(), NULL); + } + else if (poObj->GetType() == PDFObjectType_Array) + { + ExploreLayers(poObj->GetArray(), nRecLevel + 1, osCurLayer); + osCurLayer = ""; + } + else if (poObj->GetType() == PDFObjectType_Dictionary) + { + GDALPDFDictionary* poDict = poObj->GetDictionary(); + GDALPDFObject* poName = poDict->Get("Name"); + if (poName != NULL && poName->GetType() == PDFObjectType_String) + { + CPLString osName = PDFSanitizeLayerName(poName->GetString().c_str()); + if (osTopLayer.size()) + osCurLayer = osTopLayer + "." + osName; + else + osCurLayer = osName; + //CPLDebug("PDF", "Layer %s", osCurLayer.c_str()); + + OCGs* optContentConfig = poDocPoppler->getOptContentConfig(); + struct Ref r; + r.num = poObj->GetRefNum(); + r.gen = poObj->GetRefGen(); + OptionalContentGroup* ocg = optContentConfig->findOcgByRef(r); + if (ocg) + { + AddLayer(osCurLayer.c_str(), ocg); + osLayerWithRefList.AddString( + CPLSPrintf("%s %d %d", osCurLayer.c_str(), r.num, r.gen)); + } + } + } + } +} + +/************************************************************************/ +/* FindLayers() */ +/************************************************************************/ + +void PDFDataset::FindLayers() +{ + OCGs* optContentConfig = poDocPoppler->getOptContentConfig(); + if (optContentConfig == NULL || !optContentConfig->isOk() ) + return; + + Array* array = optContentConfig->getOrderArray(); + if (array) + { + GDALPDFArray* poArray = GDALPDFCreateArray(array); + ExploreLayers(poArray, 0); + delete poArray; + } + else + { + GooList* ocgList = optContentConfig->getOCGs(); + for(int i=0;igetLength();i++) + { + OptionalContentGroup* ocg = (OptionalContentGroup*) ocgList->get(i); + if( ocg != NULL && ocg->getName() != NULL ) + AddLayer((const char*)ocg->getName()->getCString(), ocg); + } + } + + oMDMD.SetMetadata(osLayerList.List(), "LAYERS"); +} + +/************************************************************************/ +/* TurnLayersOnOff() */ +/************************************************************************/ + +void PDFDataset::TurnLayersOnOff() +{ + OCGs* optContentConfig = poDocPoppler->getOptContentConfig(); + if (optContentConfig == NULL || !optContentConfig->isOk() ) + return; + + // Which layers to turn ON ? + const char* pszLayers = GetOption(papszOpenOptions, "LAYERS", NULL); + if (pszLayers) + { + int i; + int bAll = EQUAL(pszLayers, "ALL"); + GooList* ocgList = optContentConfig->getOCGs(); + for(i=0;igetLength();i++) + { + OptionalContentGroup* ocg = (OptionalContentGroup*) ocgList->get(i); + ocg->setState( (bAll) ? OptionalContentGroup::On : OptionalContentGroup::Off ); + } + + char** papszLayers = CSLTokenizeString2(pszLayers, ",", 0); + for(i=0;!bAll && papszLayers[i] != NULL;i++) + { + std::map::iterator oIter = + oLayerOCGMap.find(papszLayers[i]); + if (oIter != oLayerOCGMap.end()) + { + if (oIter->second) + { + //CPLDebug("PDF", "Turn '%s' on", papszLayers[i]); + oIter->second->setState(OptionalContentGroup::On); + } + + // Turn child layers on, unless there's one of them explicitly listed + // in the list. + size_t nLen = strlen(papszLayers[i]); + int bFoundChildLayer = FALSE; + oIter = oLayerOCGMap.begin(); + for( ; oIter != oLayerOCGMap.end() && !bFoundChildLayer; oIter ++) + { + if (oIter->first.size() > nLen && + strncmp(oIter->first.c_str(), papszLayers[i], nLen) == 0 && + oIter->first[nLen] == '.') + { + for(int j=0;papszLayers[j] != NULL;j++) + { + if (strcmp(papszLayers[j], oIter->first.c_str()) == 0) + bFoundChildLayer = TRUE; + } + } + } + + if( !bFoundChildLayer ) + { + oIter = oLayerOCGMap.begin(); + for( ; oIter != oLayerOCGMap.end() && !bFoundChildLayer; oIter ++) + { + if (oIter->first.size() > nLen && + strncmp(oIter->first.c_str(), papszLayers[i], nLen) == 0 && + oIter->first[nLen] == '.') + { + if (oIter->second) + { + //CPLDebug("PDF", "Turn '%s' on too", oIter->first.c_str()); + oIter->second->setState(OptionalContentGroup::On); + } + } + } + } + + // Turn parent layers on too + char* pszLastDot; + while( (pszLastDot = strrchr(papszLayers[i], '.')) != NULL) + { + *pszLastDot = '\0'; + oIter = oLayerOCGMap.find(papszLayers[i]); + if (oIter != oLayerOCGMap.end()) + { + if (oIter->second) + { + //CPLDebug("PDF", "Turn '%s' on too", papszLayers[i]); + oIter->second->setState(OptionalContentGroup::On); + } + } + } + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", + papszLayers[i]); + } + } + CSLDestroy(papszLayers); + + bUseOCG = TRUE; + } + + // Which layers to turn OFF ? + const char* pszLayersOFF = GetOption(papszOpenOptions, "LAYERS_OFF", NULL); + if (pszLayersOFF) + { + char** papszLayersOFF = CSLTokenizeString2(pszLayersOFF, ",", 0); + for(int i=0;papszLayersOFF[i] != NULL;i++) + { + std::map::iterator oIter = + oLayerOCGMap.find(papszLayersOFF[i]); + if (oIter != oLayerOCGMap.end()) + { + if (oIter->second) + { + //CPLDebug("PDF", "Turn '%s' off", papszLayersOFF[i]); + oIter->second->setState(OptionalContentGroup::Off); + } + + // Turn child layers off too + size_t nLen = strlen(papszLayersOFF[i]); + oIter = oLayerOCGMap.begin(); + for( ; oIter != oLayerOCGMap.end(); oIter ++) + { + if (oIter->first.size() > nLen && + strncmp(oIter->first.c_str(), papszLayersOFF[i], nLen) == 0 && + oIter->first[nLen] == '.') + { + if (oIter->second) + { + //CPLDebug("PDF", "Turn '%s' off too", oIter->first.c_str()); + oIter->second->setState(OptionalContentGroup::Off); + } + } + } + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, "Unknown layer '%s'", + papszLayersOFF[i]); + } + } + CSLDestroy(papszLayersOFF); + + bUseOCG = TRUE; + } +} + +#endif + +/************************************************************************/ +/* FindLayerOCG() */ +/************************************************************************/ + +CPLString PDFDataset::FindLayerOCG(GDALPDFDictionary* poPageDict, + const char* pszLayerName) +{ + GDALPDFObject* poProperties = + poPageDict->LookupObject("Resources.Properties"); + if (poProperties != NULL && + poProperties->GetType() == PDFObjectType_Dictionary) + { + std::map& oMap = + poProperties->GetDictionary()->GetValues(); + std::map::iterator oIter = oMap.begin(); + std::map::iterator oEnd = oMap.end(); + + for(; oIter != oEnd; ++oIter) + { + GDALPDFObject* poObj = oIter->second; + if( poObj->GetRefNum() != 0 && poObj->GetType() == PDFObjectType_Dictionary ) + { + GDALPDFObject* poType = poObj->GetDictionary()->Get("Type"); + GDALPDFObject* poName = poObj->GetDictionary()->Get("Name"); + if( poType != NULL && + poType->GetType() == PDFObjectType_Name && + poType->GetName() == "OCG" && + poName != NULL && + poName->GetType() == PDFObjectType_String ) + { + if( strcmp(poName->GetString().c_str(), pszLayerName) == 0) + return oIter->first; + } + } + } + } + return ""; +} + +/************************************************************************/ +/* FindLayersGeneric() */ +/************************************************************************/ + +void PDFDataset::FindLayersGeneric(GDALPDFDictionary* poPageDict) +{ + GDALPDFObject* poProperties = + poPageDict->LookupObject("Resources.Properties"); + if (poProperties != NULL && + poProperties->GetType() == PDFObjectType_Dictionary) + { + std::map& oMap = + poProperties->GetDictionary()->GetValues(); + std::map::iterator oIter = oMap.begin(); + std::map::iterator oEnd = oMap.end(); + + for(; oIter != oEnd; ++oIter) + { + GDALPDFObject* poObj = oIter->second; + if( poObj->GetRefNum() != 0 && poObj->GetType() == PDFObjectType_Dictionary ) + { + GDALPDFObject* poType = poObj->GetDictionary()->Get("Type"); + GDALPDFObject* poName = poObj->GetDictionary()->Get("Name"); + if( poType != NULL && + poType->GetType() == PDFObjectType_Name && + poType->GetName() == "OCG" && + poName != NULL && + poName->GetType() == PDFObjectType_String ) + { + osLayerWithRefList.AddString( + CPLSPrintf("%s %d %d", + PDFSanitizeLayerName(poName->GetString()).c_str(), + poObj->GetRefNum(), + poObj->GetRefGen())); + } + } + } + } +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *PDFDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if (!Identify(poOpenInfo)) + return NULL; + + const char* pszUserPwd = GetOption(poOpenInfo->papszOpenOptions, "USER_PWD", NULL); + + int bOpenSubdataset = strncmp(poOpenInfo->pszFilename, "PDF:", 4) == 0; + int bOpenSubdatasetImage = strncmp(poOpenInfo->pszFilename, "PDF_IMAGE:", 10) == 0; + int iPage = -1; + int nImageNum = -1; + const char* pszFilename = poOpenInfo->pszFilename; + char szPassword[81]; + + if (bOpenSubdataset) + { + iPage = atoi(pszFilename + 4); + if (iPage <= 0) + return NULL; + pszFilename = strchr(pszFilename + 4, ':'); + if (pszFilename == NULL) + return NULL; + pszFilename ++; + } + else if (bOpenSubdatasetImage) + { + iPage = atoi(pszFilename + 10); + if (iPage <= 0) + return NULL; + const char* pszNext = strchr(pszFilename + 10, ':'); + if (pszNext == NULL) + return NULL; + nImageNum = atoi(pszNext + 1); + if (nImageNum <= 0) + return NULL; + pszFilename = strchr(pszNext + 1, ':'); + if (pszFilename == NULL) + return NULL; + pszFilename ++; + } + else + iPage = 1; + + int bUsePoppler; +#if defined(HAVE_POPPLER) && !defined(HAVE_PODOFO) + bUsePoppler = TRUE; +#elif !defined(HAVE_POPPLER) && defined(HAVE_PODOFO) + bUsePoppler = FALSE; +#elif defined(HAVE_POPPLER) && defined(HAVE_PODOFO) + const char* pszPDFLib = GetOption(poOpenInfo->papszOpenOptions, "PDF_LIB", "POPPLER"); + if (EQUAL(pszPDFLib, "POPPLER")) + bUsePoppler = TRUE; + else if (EQUAL(pszPDFLib, "PODOFO")) + bUsePoppler = FALSE; + else + { + CPLDebug("PDF", "Invalid value for GDAL_PDF_LIB config option"); + bUsePoppler = TRUE; + } +#else + return NULL; +#endif + + GDALPDFObject* poPageObj = NULL; +#ifdef HAVE_POPPLER + PDFDoc* poDocPoppler = NULL; + ObjectAutoFree oObj; + Page* poPagePoppler = NULL; + Catalog* poCatalogPoppler = NULL; +#endif +#ifdef HAVE_PODOFO + PoDoFo::PdfMemDocument* poDocPodofo = NULL; + PoDoFo::PdfPage* poPagePodofo = NULL; +#endif + int nPages = 0; + +#ifdef HAVE_POPPLER + if(bUsePoppler) + { + GooString* poUserPwd = NULL; + + /* Set custom error handler for poppler errors */ +#ifdef POPPLER_0_20_OR_LATER + setErrorCallback(PDFDatasetErrorFunction, NULL); +#else + setErrorFunction(PDFDatasetErrorFunction); +#endif + + { + CPLMutexHolderD(&hGlobalParamsMutex); + /* poppler global variable */ + if (globalParams == NULL) + globalParams = new GlobalParams(); + + globalParams->setPrintCommands(CSLTestBoolean( + CPLGetConfigOption("GDAL_PDF_PRINT_COMMANDS", "FALSE"))); + } + + while(TRUE) + { + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return NULL; + + fp = (VSILFILE*)VSICreateBufferedReaderHandle((VSIVirtualHandle*)fp); + + if (pszUserPwd) + poUserPwd = new GooString(pszUserPwd); + + oObj.initNull(); + poDocPoppler = new PDFDoc(new VSIPDFFileStream(fp, pszFilename, &oObj), NULL, poUserPwd); + delete poUserPwd; + + if ( !poDocPoppler->isOk() || poDocPoppler->getNumPages() == 0 ) + { + if (poDocPoppler->getErrorCode() == errEncrypted) + { + if (pszUserPwd && EQUAL(pszUserPwd, "ASK_INTERACTIVE")) + { + printf( "Enter password (will be echo'ed in the console): " ); + if (0 == fgets( szPassword, sizeof(szPassword), stdin )) + { + fprintf(stderr, "WARNING: Error getting password.\n"); + } + szPassword[sizeof(szPassword)-1] = 0; + char* sz10 = strchr(szPassword, '\n'); + if (sz10) + *sz10 = 0; + pszUserPwd = szPassword; + PDFFreeDoc(poDocPoppler); + continue; + } + else if (pszUserPwd == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "A password is needed. You can specify it through the PDF_USER_PWD " + "configuration option (that can be set to ASK_INTERACTIVE)"); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid password"); + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); + } + + PDFFreeDoc(poDocPoppler); + + return NULL; + } + else + break; + + /* Reset errors that could have been issued during opening and that */ + /* did not result in an invalid document */ + CPLErrorReset(); + } + + poCatalogPoppler = poDocPoppler->getCatalog(); + if ( poCatalogPoppler == NULL || !poCatalogPoppler->isOk() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid catalog"); + PDFFreeDoc(poDocPoppler); + return NULL; + } + +#ifdef dump_catalog + { + ObjectAutoFree oCatalog; + poDocPoppler->getXRef()->getCatalog(&oCatalog); + GDALPDFObjectPoppler oCatalogGDAL(&oCatalog, FALSE); + GDALPDFDumper oDumper(stderr); + oDumper.Dump(&oCatalogGDAL); + } +#endif + + nPages = poDocPoppler->getNumPages(); + if (iPage < 1 || iPage > nPages) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)", + iPage, nPages); + PDFFreeDoc(poDocPoppler); + return NULL; + } + + /* Sanity check to validate page count */ + if( iPage != nPages ) + { + poPagePoppler = poCatalogPoppler->getPage(nPages); + if ( poPagePoppler == NULL || !poPagePoppler->isOk() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page count"); + PDFFreeDoc(poDocPoppler); + return NULL; + } + } + + poPagePoppler = poCatalogPoppler->getPage(iPage); + if ( poPagePoppler == NULL || !poPagePoppler->isOk() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page"); + PDFFreeDoc(poDocPoppler); + return NULL; + } + + /* Here's the dirty part: this is a private member */ + /* so we had to #define private public to get it ! */ + Object& oPageObj = poPagePoppler->pageObj; + if ( !oPageObj.isDict() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : !oPageObj.isDict()"); + PDFFreeDoc(poDocPoppler); + return NULL; + } + + poPageObj = new GDALPDFObjectPoppler(&oPageObj, FALSE); + Ref* poPageRef = poCatalogPoppler->getPageRef(iPage); + if (poPageRef != NULL) + { + ((GDALPDFObjectPoppler*)poPageObj)->SetRefNumAndGen(poPageRef->num, poPageRef->gen); + } + } +#endif + +#ifdef HAVE_PODOFO + if (!bUsePoppler) + { + PoDoFo::PdfError::EnableDebug( false ); + PoDoFo::PdfError::EnableLogging( false ); + + poDocPodofo = new PoDoFo::PdfMemDocument(); + try + { + poDocPodofo->Load(pszFilename); + } + catch(PoDoFo::PdfError& oError) + { + if (oError.GetError() == PoDoFo::ePdfError_InvalidPassword) + { + if (pszUserPwd) + { + if (EQUAL(pszUserPwd, "ASK_INTERACTIVE")) + { + printf( "Enter password (will be echo'ed in the console): " ); + if (0 == fgets( szPassword, sizeof(szPassword), stdin )) + { + fprintf(stderr, "WARNING: Error getting password.\n"); + } + szPassword[sizeof(szPassword)-1] = 0; + char* sz10 = strchr(szPassword, '\n'); + if (sz10) + *sz10 = 0; + pszUserPwd = szPassword; + } + + try + { + poDocPodofo->SetPassword(pszUserPwd); + } + catch(PoDoFo::PdfError& oError) + { + if (oError.GetError() == PoDoFo::ePdfError_InvalidPassword) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid password"); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what()); + } + delete poDocPodofo; + return NULL; + } + catch(...) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); + delete poDocPodofo; + return NULL; + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "A password is needed. You can specify it through the PDF_USER_PWD " + "configuration option (that can be set to ASK_INTERACTIVE)"); + delete poDocPodofo; + return NULL; + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what()); + delete poDocPodofo; + return NULL; + } + } + catch(...) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); + delete poDocPodofo; + return NULL; + } + + nPages = poDocPodofo->GetPageCount(); + if (iPage < 1 || iPage > nPages) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid page number (%d/%d)", + iPage, nPages); + delete poDocPodofo; + return NULL; + } + + try + { + /* Sanity check to validate page count */ + if( iPage != nPages ) + poPagePodofo = poDocPodofo->GetPage(nPages - 1); + + poPagePodofo = poDocPodofo->GetPage(iPage - 1); + } + catch(PoDoFo::PdfError& oError) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what()); + delete poDocPodofo; + return NULL; + } + catch(...) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF"); + delete poDocPodofo; + return NULL; + } + + if ( poPagePodofo == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : invalid page"); + delete poDocPodofo; + return NULL; + } + + PoDoFo::PdfObject* pObj = poPagePodofo->GetObject(); + poPageObj = new GDALPDFObjectPodofo(pObj, poDocPodofo->GetObjects()); + } +#endif + + GDALPDFDictionary* poPageDict = poPageObj->GetDictionary(); + if ( poPageDict == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : poPageDict == NULL"); +#ifdef HAVE_POPPLER + PDFFreeDoc(poDocPoppler); +#endif +#ifdef HAVE_PODOFO + delete poDocPodofo; +#endif + return NULL; + } + + const char* pszDumpObject = CPLGetConfigOption("PDF_DUMP_OBJECT", NULL); + if (pszDumpObject != NULL) + { + FILE* f; + if (strcmp(pszDumpObject, "stderr") == 0) + f = stderr; + else if (EQUAL(pszDumpObject, "YES")) + f = fopen(CPLSPrintf("dump_%s.txt", CPLGetFilename(pszFilename)), "wt"); + else + f = fopen(pszDumpObject, "wt"); + if (f == NULL) + f = stderr; + + GDALPDFDumper oDumper(f); + oDumper.Dump(poPageObj); + if (f != stderr) + fclose(f); + } + + PDFDataset* poDS = new PDFDataset(); + poDS->papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions); + poDS->bUsePoppler = bUsePoppler; + poDS->osFilename = pszFilename; + poDS->eAccess = poOpenInfo->eAccess; + + if ( nPages > 1 && !bOpenSubdataset ) + { + int i; + CPLStringList aosList; + for(i=0;ipszFilename)); + sprintf( szKey, "SUBDATASET_%d_DESC", i+1 ); + aosList.AddNameValue(szKey, CPLSPrintf("Page %d of %s", i+1, poOpenInfo->pszFilename)); + } + poDS->SetMetadata( aosList.List(), "SUBDATASETS" ); + } + +#ifdef HAVE_POPPLER + poDS->poDocPoppler = poDocPoppler; +#endif +#ifdef HAVE_PODOFO + poDS->poDocPodofo = poDocPodofo; +#endif + poDS->poPageObj = poPageObj; + poDS->osUserPwd = pszUserPwd ? pszUserPwd : ""; + poDS->iPage = iPage; + + int nBandsGuessed = 0; + if (nImageNum < 0) + { + poDS->GuessDPI(poPageDict, &nBandsGuessed); + if( nBandsGuessed < 4 ) + nBandsGuessed = 0; + } + else + { + const char* pszDPI = GetOption(poOpenInfo->papszOpenOptions, "DPI", NULL); + if (pszDPI != NULL) + { + poDS->dfDPI = CPLAtof(pszDPI); + } + } + + double dfX1 = 0.0, dfY1 = 0.0, dfX2 = 0.0, dfY2 = 0.0; + +#ifdef HAVE_POPPLER + if (bUsePoppler) + { + PDFRectangle* psMediaBox = poPagePoppler->getMediaBox(); + dfX1 = psMediaBox->x1; + dfY1 = psMediaBox->y1; + dfX2 = psMediaBox->x2; + dfY2 = psMediaBox->y2; + } +#endif + +#ifdef HAVE_PODOFO + if (!bUsePoppler) + { + PoDoFo::PdfRect oMediaBox = poPagePodofo->GetMediaBox(); + dfX1 = oMediaBox.GetLeft(); + dfY1 = oMediaBox.GetBottom(); + dfX2 = dfX1 + oMediaBox.GetWidth(); + dfY2 = dfY1 + oMediaBox.GetHeight(); + } +#endif + + double dfUserUnit = poDS->dfDPI * USER_UNIT_IN_INCH; + poDS->dfPageWidth = dfX2 - dfX1; + poDS->dfPageHeight = dfY2 - dfY1; + poDS->nRasterXSize = (int) floor((dfX2 - dfX1) * dfUserUnit+0.5); + poDS->nRasterYSize = (int) floor((dfY2 - dfY1) * dfUserUnit+0.5); + + if( !GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ) + { + delete poDS; + return NULL; + } + + double dfRotation = 0; +#ifdef HAVE_POPPLER + if (bUsePoppler) + dfRotation = poDocPoppler->getPageRotate(iPage); +#endif + +#ifdef HAVE_PODOFO + if (!bUsePoppler) + dfRotation = poPagePodofo->GetRotation(); +#endif + if ( dfRotation == 90 || + dfRotation == -90 || + dfRotation == 270 ) + { +/* FIXME: the non poppler case should be implemented. This needs to rotate */ +/* the output of pdftoppm */ +#ifdef HAVE_POPPLER + if (bUsePoppler) + { + /* Wondering how it would work with a georeferenced image */ + /* Has only been tested with ungeoreferenced image */ + int nTmp = poDS->nRasterXSize; + poDS->nRasterXSize = poDS->nRasterYSize; + poDS->nRasterYSize = nTmp; + } +#endif + } + + /* Check if the PDF is only made of regularly tiled images */ + /* (like some USGS GeoPDF production) */ + if( dfRotation == 0.0 && poDS->asTiles.size() && + EQUAL(GetOption(poOpenInfo->papszOpenOptions, "LAYERS", "ALL"), "ALL") ) + { + poDS->CheckTiledRaster(); + if (poDS->aiTiles.size() ) + poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + } + + GDALPDFObject* poLGIDict = NULL; + GDALPDFObject* poVP = NULL; + int bIsOGCBP = FALSE; + if ( (poLGIDict = poPageDict->Get("LGIDict")) != NULL && nImageNum < 0 ) + { + /* Cf 08-139r3_GeoPDF_Encoding_Best_Practice_Version_2.2.pdf */ + CPLDebug("PDF", "OGC Encoding Best Practice style detected"); + if (poDS->ParseLGIDictObject(poLGIDict)) + { + if (poDS->bHasCTM ) + { + if ( dfRotation == 90 ) + { + poDS->adfGeoTransform[0] = poDS->adfCTM[4]; + poDS->adfGeoTransform[1] = poDS->adfCTM[2] / dfUserUnit; + poDS->adfGeoTransform[2] = poDS->adfCTM[0] / dfUserUnit; + poDS->adfGeoTransform[3] = poDS->adfCTM[5]; + poDS->adfGeoTransform[4] = poDS->adfCTM[3] / dfUserUnit; + poDS->adfGeoTransform[5] = poDS->adfCTM[1] / dfUserUnit; + } + else if ( dfRotation == -90 || dfRotation == 270 ) + { + poDS->adfGeoTransform[0] = poDS->adfCTM[4] + poDS->adfCTM[2] * poDS->dfPageHeight + poDS->adfCTM[0] * poDS->dfPageWidth; + poDS->adfGeoTransform[1] = -poDS->adfCTM[2] / dfUserUnit; + poDS->adfGeoTransform[2] = -poDS->adfCTM[0] / dfUserUnit; + poDS->adfGeoTransform[3] = poDS->adfCTM[5] + poDS->adfCTM[3] * poDS->dfPageHeight + poDS->adfCTM[1] * poDS->dfPageWidth; + poDS->adfGeoTransform[4] = -poDS->adfCTM[3] / dfUserUnit; + poDS->adfGeoTransform[5] = -poDS->adfCTM[1] / dfUserUnit; + } + else + { + poDS->adfGeoTransform[0] = poDS->adfCTM[4] + poDS->adfCTM[2] * poDS->dfPageHeight; + poDS->adfGeoTransform[1] = poDS->adfCTM[0] / dfUserUnit; + poDS->adfGeoTransform[2] = - poDS->adfCTM[2] / dfUserUnit; + poDS->adfGeoTransform[3] = poDS->adfCTM[5] + poDS->adfCTM[3] * poDS->dfPageHeight; + poDS->adfGeoTransform[4] = poDS->adfCTM[1] / dfUserUnit; + poDS->adfGeoTransform[5] = - poDS->adfCTM[3] / dfUserUnit; + } + + poDS->bGeoTransformValid = TRUE; + } + + bIsOGCBP = TRUE; + + int i; + for(i=0;inGCPCount;i++) + { + if ( dfRotation == 90 ) + { + double dfPixel = poDS->pasGCPList[i].dfGCPPixel * dfUserUnit; + double dfLine = poDS->pasGCPList[i].dfGCPLine * dfUserUnit; + poDS->pasGCPList[i].dfGCPPixel = dfLine; + poDS->pasGCPList[i].dfGCPLine = dfPixel; + } + else if ( dfRotation == -90 || dfRotation == 270 ) + { + double dfPixel = poDS->pasGCPList[i].dfGCPPixel * dfUserUnit; + double dfLine = poDS->pasGCPList[i].dfGCPLine * dfUserUnit; + poDS->pasGCPList[i].dfGCPPixel = poDS->nRasterXSize - dfLine; + poDS->pasGCPList[i].dfGCPLine = poDS->nRasterYSize - dfPixel; + } + else + { + poDS->pasGCPList[i].dfGCPPixel = poDS->pasGCPList[i].dfGCPPixel * dfUserUnit; + poDS->pasGCPList[i].dfGCPLine = poDS->nRasterYSize - poDS->pasGCPList[i].dfGCPLine * dfUserUnit; + } + } + } + } + else if ( (poVP = poPageDict->Get("VP")) != NULL && nImageNum < 0 ) + { + /* Cf adobe_supplement_iso32000.pdf */ + CPLDebug("PDF", "Adobe ISO32000 style Geospatial PDF perhaps ?"); + if (dfX1 != 0 || dfY1 != 0) + { + CPLDebug("PDF", "non null dfX1 or dfY1 values. untested case..."); + } + poDS->ParseVP(poVP, dfX2 - dfX1, dfY2 - dfY1); + } + else + { + GDALPDFObject* poXObject = poPageDict->LookupObject("Resources.XObject"); + + if (poXObject != NULL && + poXObject->GetType() == PDFObjectType_Dictionary) + { + GDALPDFDictionary* poXObjectDict = poXObject->GetDictionary(); + std::map& oMap = poXObjectDict->GetValues(); + std::map::iterator oMapIter = oMap.begin(); + int nSubDataset = 0; + while(oMapIter != oMap.end()) + { + GDALPDFObject* poObj = oMapIter->second; + if (poObj->GetType() == PDFObjectType_Dictionary) + { + GDALPDFDictionary* poDict = poObj->GetDictionary(); + GDALPDFObject* poSubtype = NULL; + GDALPDFObject* poMeasure = NULL; + GDALPDFObject* poWidth = NULL; + GDALPDFObject* poHeight = NULL; + int nW = 0; + int nH = 0; + if ((poSubtype = poDict->Get("Subtype")) != NULL && + poSubtype->GetType() == PDFObjectType_Name && + poSubtype->GetName() == "Image" && + (poMeasure = poDict->Get("Measure")) != NULL && + poMeasure->GetType() == PDFObjectType_Dictionary && + (poWidth = poDict->Get("Width")) != NULL && + poWidth->GetType() == PDFObjectType_Int && + (nW = poWidth->GetInt()) > 0 && + (poHeight = poDict->Get("Height")) != NULL && + poHeight->GetType() == PDFObjectType_Int && + (nH = poHeight->GetInt()) > 0 ) + { + if (nImageNum < 0) + CPLDebug("PDF", "Measure found on Image object (%d)", + poObj->GetRefNum()); + + GDALPDFObject* poColorSpace = poDict->Get("ColorSpace"); + GDALPDFObject* poBitsPerComponent = poDict->Get("BitsPerComponent"); + if (poObj->GetRefNum() != 0 && + poObj->GetRefGen() == 0 && + poColorSpace != NULL && + poColorSpace->GetType() == PDFObjectType_Name && + (poColorSpace->GetName() == "DeviceGray" || + poColorSpace->GetName() == "DeviceRGB") && + (poBitsPerComponent == NULL || + (poBitsPerComponent->GetType() == PDFObjectType_Int && + poBitsPerComponent->GetInt() == 8))) + { + if (nImageNum < 0) + { + nSubDataset ++; + poDS->SetMetadataItem(CPLSPrintf("SUBDATASET_%d_NAME", + nSubDataset), + CPLSPrintf("PDF_IMAGE:%d:%d:%s", + iPage, poObj->GetRefNum(), pszFilename), + "SUBDATASETS"); + poDS->SetMetadataItem(CPLSPrintf("SUBDATASET_%d_DESC", + nSubDataset), + CPLSPrintf("Georeferenced image of size %dx%d of page %d of %s", + nW, nH, iPage, pszFilename), + "SUBDATASETS"); + } + else if (poObj->GetRefNum() == nImageNum) + { + poDS->nRasterXSize = nW; + poDS->nRasterYSize = nH; + poDS->ParseMeasure(poMeasure, nW, nH, 0, nH, nW, 0); + poDS->poImageObj = poObj; + if (poColorSpace->GetName() == "DeviceGray") + nBandsGuessed = 1; + break; + } + } + } + } + ++ oMapIter; + } + } + + if (nImageNum >= 0 && poDS->poImageObj == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find image %d", nImageNum); + delete poDS; + return NULL; + } + + /* Not a geospatial PDF doc */ + } + + /* If pixel size or top left coordinates are very close to an int, round them to the int */ + double dfEps = ( fabs(poDS->adfGeoTransform[0]) > 1e5 && + fabs(poDS->adfGeoTransform[3]) > 1e5 ) ? 1e-5 : 1e-8; + poDS->adfGeoTransform[0] = ROUND_TO_INT_IF_CLOSE(poDS->adfGeoTransform[0], dfEps); + poDS->adfGeoTransform[1] = ROUND_TO_INT_IF_CLOSE(poDS->adfGeoTransform[1]); + poDS->adfGeoTransform[3] = ROUND_TO_INT_IF_CLOSE(poDS->adfGeoTransform[3], dfEps); + poDS->adfGeoTransform[5] = ROUND_TO_INT_IF_CLOSE(poDS->adfGeoTransform[5]); + + if (poDS->poNeatLine) + { + char* pszNeatLineWkt = NULL; + OGRLinearRing* poRing = poDS->poNeatLine->getExteriorRing(); + /* Adobe style is already in target SRS units */ + if (bIsOGCBP) + { + int nPoints = poRing->getNumPoints(); + int i; + + for(i=0;igetY(i) * dfUserUnit; + y = poRing->getX(i) * dfUserUnit; + } + else if( dfRotation == -90.0 || dfRotation == 270.0 ) + { + x = poDS->nRasterXSize - poRing->getY(i) * dfUserUnit; + y = poDS->nRasterYSize - poRing->getX(i) * dfUserUnit; + } + else + { + x = poRing->getX(i) * dfUserUnit; + y = poDS->nRasterYSize - poRing->getY(i) * dfUserUnit; + } + double X = poDS->adfGeoTransform[0] + x * poDS->adfGeoTransform[1] + + y * poDS->adfGeoTransform[2]; + double Y = poDS->adfGeoTransform[3] + x * poDS->adfGeoTransform[4] + + y * poDS->adfGeoTransform[5]; + poRing->setPoint(i, X, Y); + } + } + poRing->closeRings(); + + poDS->poNeatLine->exportToWkt(&pszNeatLineWkt); + if (nImageNum < 0) + poDS->SetMetadataItem("NEATLINE", pszNeatLineWkt); + CPLFree(pszNeatLineWkt); + } + + +#ifdef HAVE_POPPLER + if (bUsePoppler) + { + GooString* poMetadata = poCatalogPoppler->readMetadata(); + if (poMetadata) + { + char* pszContent = poMetadata->getCString(); + if (pszContent != NULL && + strncmp(pszContent, "SetMetadata(apszMDList, "xml:XMP"); + } + delete poMetadata; + } + + /* Read Info object */ + /* The test is necessary since with some corrupted PDFs poDocPoppler->getDocInfo() */ + /* might abort() */ + if( poDocPoppler->getXRef()->isOk() ) + { + Object oInfo; + poDocPoppler->getDocInfo(&oInfo); + GDALPDFObjectPoppler oInfoObjPoppler(&oInfo, FALSE); + poDS->ParseInfo(&oInfoObjPoppler); + oInfo.free(); + } + + /* Find layers */ + poDS->FindLayers(); + + /* Turn user specified layers on or off */ + poDS->TurnLayersOnOff(); + } +#endif + +#ifdef HAVE_PODOFO + if (!bUsePoppler) + { + PoDoFo::TIVecObjects it = poDocPodofo->GetObjects().begin(); + for( ; it != poDocPodofo->GetObjects().end(); ++it ) + { + GDALPDFObjectPodofo oObjPodofo((*it), poDocPodofo->GetObjects()); + poDS->FindXMP(&oObjPodofo); + } + + /* Find layers */ + poDS->FindLayersGeneric(poPageDict); + + /* Read Info object */ + PoDoFo::PdfInfo* poInfo = poDocPodofo->GetInfo(); + if (poInfo != NULL) + { + GDALPDFObjectPodofo oInfoObjPodofo(poInfo->GetObject(), poDocPodofo->GetObjects()); + poDS->ParseInfo(&oInfoObjPodofo); + } + } +#endif + + int nBands = 3; + if( nBandsGuessed ) + nBands = nBandsGuessed; + const char* pszPDFBands = GetOption(poOpenInfo->papszOpenOptions, "BANDS", NULL); + if( pszPDFBands ) + { + nBands = atoi(pszPDFBands); + if (nBands != 3 && nBands != 4) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Invalid value for GDAL_PDF_BANDS. Using 3 as a fallback"); + nBands = 3; + } + } +#ifdef HAVE_PODOFO + if (!bUsePoppler && nBands == 4 && poDS->aiTiles.size() == 0) + { + CPLError(CE_Warning, CPLE_NotSupported, + "GDAL_PDF_BANDS=4 only supported when PDF driver is compiled against Poppler. " + "Using 3 as a fallback"); + nBands = 3; + } +#endif + + int iBand; + for(iBand = 1; iBand <= nBands; iBand ++) + { + if (poDS->poImageObj != NULL) + poDS->SetBand(iBand, new PDFImageRasterBand(poDS, iBand)); + else + poDS->SetBand(iBand, new PDFRasterBand(poDS, iBand)); + } + + int bHasNonEmptyVectorLayers = poDS->OpenVectorLayers(poPageDict); + + /* Check if this is a raster-only PCIDSK file and that we are */ + /* opened in vector-only mode */ + if( (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 && + (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 && + !bHasNonEmptyVectorLayers ) + { + CPLDebug("PCIDSK", "This is a raster-only PDF dataset, " + "but it has been opened in vector-only mode"); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + /* Clear dirty flag */ + poDS->bProjDirty = FALSE; + poDS->bNeatLineDirty = FALSE; + poDS->bInfoDirty = FALSE; + poDS->bXMPDirty = FALSE; + + return( poDS ); +} + +/************************************************************************/ +/* ParseLGIDictObject() */ +/************************************************************************/ + +int PDFDataset::ParseLGIDictObject(GDALPDFObject* poLGIDict) +{ + int i; + int bOK = FALSE; + if (poLGIDict->GetType() == PDFObjectType_Array) + { + GDALPDFArray* poArray = poLGIDict->GetArray(); + int nArrayLength = poArray->GetLength(); + int iMax = -1; + GDALPDFObject* poArrayElt; + for (i=0; iGet(i)) == NULL || + poArrayElt->GetType() != PDFObjectType_Dictionary ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "LGIDict[%d] is not a dictionary", i); + return FALSE; + } + + int bIsBestCandidate = FALSE; + if (ParseLGIDictDictFirstPass(poArrayElt->GetDictionary(), &bIsBestCandidate)) + { + if (bIsBestCandidate || iMax < 0) + iMax = i; + } + } + + if (iMax < 0) + return FALSE; + + poArrayElt = poArray->Get(iMax); + bOK = ParseLGIDictDictSecondPass(poArrayElt->GetDictionary()); + } + else if (poLGIDict->GetType() == PDFObjectType_Dictionary) + { + bOK = ParseLGIDictDictFirstPass(poLGIDict->GetDictionary()) && + ParseLGIDictDictSecondPass(poLGIDict->GetDictionary()); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "LGIDict is of type %s", poLGIDict->GetTypeName()); + } + + return bOK; +} + +/************************************************************************/ +/* Get() */ +/************************************************************************/ + +static double Get(GDALPDFObject* poObj, int nIndice) +{ + if (poObj->GetType() == PDFObjectType_Array && nIndice >= 0) + { + poObj = poObj->GetArray()->Get(nIndice); + if (poObj == NULL) + return 0; + return Get(poObj); + } + else if (poObj->GetType() == PDFObjectType_Int) + return poObj->GetInt(); + else if (poObj->GetType() == PDFObjectType_Real) + return poObj->GetReal(); + else if (poObj->GetType() == PDFObjectType_String) + { + const char* pszStr = poObj->GetString().c_str(); + size_t nLen = strlen(pszStr); + /* cf Military_Installations_2008.pdf that has values like "96 0 0.0W" */ + char chLast = pszStr[nLen-1]; + if (chLast == 'W' || chLast == 'E' || chLast == 'N' || chLast == 'S') + { + double dfDeg = CPLAtof(pszStr); + double dfMin = 0, dfSec = 0; + const char* pszNext = strchr(pszStr, ' '); + if (pszNext) + pszNext ++; + if (pszNext) + dfMin = CPLAtof(pszNext); + if (pszNext) + pszNext = strchr(pszNext, ' '); + if (pszNext) + pszNext ++; + if (pszNext) + dfSec = CPLAtof(pszNext); + double dfVal = dfDeg + dfMin / 60 + dfSec / 3600; + if (chLast == 'W' || chLast == 'S') + return -dfVal; + else + return dfVal; + } + return CPLAtof(pszStr); + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, "Unexpected type : %s", + poObj->GetTypeName()); + return 0; + } +} + +/************************************************************************/ +/* Get() */ +/************************************************************************/ + +static double Get(GDALPDFDictionary* poDict, const char* pszName) +{ + GDALPDFObject* poObj; + if ( (poObj = poDict->Get(pszName)) != NULL ) + return Get(poObj); + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find parameter %s", pszName); + return 0; +} + +/************************************************************************/ +/* ParseLGIDictDictFirstPass() */ +/************************************************************************/ + +int PDFDataset::ParseLGIDictDictFirstPass(GDALPDFDictionary* poLGIDict, + int* pbIsBestCandidate) +{ + int i; + + if (pbIsBestCandidate) + *pbIsBestCandidate = FALSE; + + if (poLGIDict == NULL) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Extract Type attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poType; + if ((poType = poLGIDict->Get("Type")) == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find Type of LGIDict object"); + return FALSE; + } + + if ( poType->GetType() != PDFObjectType_Name ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid type for Type of LGIDict object"); + return FALSE; + } + + if ( strcmp(poType->GetName().c_str(), "LGIDict") != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid value for Type of LGIDict object : %s", + poType->GetName().c_str()); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Extract Version attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poVersion; + if ((poVersion = poLGIDict->Get("Version")) == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find Version of LGIDict object"); + return FALSE; + } + + if ( poVersion->GetType() == PDFObjectType_String ) + { + /* OGC best practice is 2.1 */ + CPLDebug("PDF", "LGIDict Version : %s", + poVersion->GetString().c_str()); + } + else if (poVersion->GetType() == PDFObjectType_Int) + { + /* Old TerraGo is 2 */ + CPLDebug("PDF", "LGIDict Version : %d", + poVersion->GetInt()); + } + + /* USGS PDF maps have several LGIDict. Keep the one whose description */ + /* is "Map Layers" by default */ + const char* pszNeatlineToSelect = + GetOption(papszOpenOptions, "NEATLINE", "Map Layers"); + +/* -------------------------------------------------------------------- */ +/* Extract Neatline attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poNeatline; + if ((poNeatline = poLGIDict->Get("Neatline")) != NULL && + poNeatline->GetType() == PDFObjectType_Array) + { + int nLength = poNeatline->GetArray()->GetLength(); + if ( (nLength % 2) != 0 || nLength < 4 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid length for Neatline"); + return FALSE; + } + + GDALPDFObject* poDescription; + int bIsAskedNeatline = FALSE; + if ( (poDescription = poLGIDict->Get("Description")) != NULL && + poDescription->GetType() == PDFObjectType_String ) + { + CPLDebug("PDF", "Description = %s", poDescription->GetString().c_str()); + + if( EQUAL(poDescription->GetString().c_str(), pszNeatlineToSelect) ) + { + dfMaxArea = 1e300; + bIsAskedNeatline = TRUE; + } + } + + if( !bIsAskedNeatline ) + { + double dfMinX = 0, dfMinY = 0, dfMaxX = 0, dfMaxY = 0; + for(i=0;i dfMaxX) dfMaxX = dfX; + if (i == 0 || dfY > dfMaxY) dfMaxY = dfY; + } + double dfArea = (dfMaxX - dfMinX) * (dfMaxY - dfMinY); + if (dfArea < dfMaxArea) + { + CPLDebug("PDF", "Not the largest neatline. Skipping it"); + return TRUE; + } + + CPLDebug("PDF", "This is the largest neatline for now"); + dfMaxArea = dfArea; + } + else + CPLDebug("PDF", "The \"%s\" registration will be selected", + pszNeatlineToSelect); + + if (pbIsBestCandidate) + *pbIsBestCandidate = TRUE; + + delete poNeatLine; + poNeatLine = new OGRPolygon(); + OGRLinearRing* poRing = new OGRLinearRing(); + if (nLength == 4) + { + /* 2 points only ? They are the bounding box */ + double dfX1 = Get(poNeatline, 0); + double dfY1 = Get(poNeatline, 1); + double dfX2 = Get(poNeatline, 2); + double dfY2 = Get(poNeatline, 3); + poRing->addPoint(dfX1, dfY1); + poRing->addPoint(dfX2, dfY1); + poRing->addPoint(dfX2, dfY2); + poRing->addPoint(dfX1, dfY2); + } + else + { + for(i=0;iaddPoint(dfX, dfY); + } + } + poNeatLine->addRingDirectly(poRing); + } + + return TRUE; +} + +/************************************************************************/ +/* ParseLGIDictDictSecondPass() */ +/************************************************************************/ + +int PDFDataset::ParseLGIDictDictSecondPass(GDALPDFDictionary* poLGIDict) +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Extract Description attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poDescription; + if ( (poDescription = poLGIDict->Get("Description")) != NULL && + poDescription->GetType() == PDFObjectType_String ) + { + CPLDebug("PDF", "Description = %s", poDescription->GetString().c_str()); + } + +/* -------------------------------------------------------------------- */ +/* Extract CTM attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poCTM; + bHasCTM = FALSE; + if ((poCTM = poLGIDict->Get("CTM")) != NULL && + poCTM->GetType() == PDFObjectType_Array) + { + int nLength = poCTM->GetArray()->GetLength(); + if ( nLength != 6 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid length for CTM"); + return FALSE; + } + + bHasCTM = TRUE; + for(i=0;iGet("Registration")) != NULL && + poRegistration->GetType() == PDFObjectType_Array) + { + GDALPDFArray* poRegistrationArray = poRegistration->GetArray(); + int nLength = poRegistrationArray->GetLength(); + if( nLength > 4 || (!bHasCTM && nLength >= 2) || + CSLTestBoolean(CPLGetConfigOption("PDF_REPORT_GCPS", "NO")) ) + { + nGCPCount = 0; + pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nLength); + + for(i=0;iGet(i); + if ( poGCP != NULL && + poGCP->GetType() == PDFObjectType_Array && + poGCP->GetArray()->GetLength() == 4 ) + { + double dfUserX = Get(poGCP, 0); + double dfUserY = Get(poGCP, 1); + double dfX = Get(poGCP, 2); + double dfY = Get(poGCP, 3); + CPLDebug("PDF", "GCP[%d].userX = %.16g", i, dfUserX); + CPLDebug("PDF", "GCP[%d].userY = %.16g", i, dfUserY); + CPLDebug("PDF", "GCP[%d].x = %.16g", i, dfX); + CPLDebug("PDF", "GCP[%d].y = %.16g", i, dfY); + + char szID[32]; + sprintf( szID, "%d", nGCPCount+1 ); + pasGCPList[nGCPCount].pszId = CPLStrdup( szID ); + pasGCPList[nGCPCount].pszInfo = CPLStrdup(""); + pasGCPList[nGCPCount].dfGCPPixel = dfUserX; + pasGCPList[nGCPCount].dfGCPLine = dfUserY; + pasGCPList[nGCPCount].dfGCPX = dfX; + pasGCPList[nGCPCount].dfGCPY = dfY; + nGCPCount ++; + } + } + } + } + + if (!bHasCTM && nGCPCount == 0) + { + CPLDebug("PDF", "Neither CTM nor Registration found"); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Extract Projection attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poProjection; + if ((poProjection = poLGIDict->Get("Projection")) == NULL || + poProjection->GetType() != PDFObjectType_Dictionary) + { + CPLError(CE_Failure, CPLE_AppDefined, "Could not find Projection"); + return FALSE; + } + + return ParseProjDict(poProjection->GetDictionary()); +} + +/************************************************************************/ +/* ParseProjDict() */ +/************************************************************************/ + +int PDFDataset::ParseProjDict(GDALPDFDictionary* poProjDict) +{ + if (poProjDict == NULL) + return FALSE; + OGRSpatialReference oSRS; + +/* -------------------------------------------------------------------- */ +/* Extract WKT attribute (GDAL extension) */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poWKT; + if ( (poWKT = poProjDict->Get("WKT")) != NULL && + poWKT->GetType() == PDFObjectType_String && + CSLTestBoolean( CPLGetConfigOption("GDAL_PDF_OGC_BP_READ_WKT", "TRUE") ) ) + { + CPLDebug("PDF", "Found WKT attribute (GDAL extension). Using it"); + const char* pszWKTRead = poWKT->GetString().c_str(); + CPLFree(pszWKT); + pszWKT = CPLStrdup(pszWKTRead); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Extract Type attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poType; + if ((poType = poProjDict->Get("Type")) == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find Type of Projection object"); + return FALSE; + } + + if ( poType->GetType() != PDFObjectType_Name ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid type for Type of Projection object"); + return FALSE; + } + + if ( strcmp(poType->GetName().c_str(), "Projection") != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid value for Type of Projection object : %s", + poType->GetName().c_str()); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Extract Datum attribute */ +/* -------------------------------------------------------------------- */ + int bIsWGS84 = FALSE; + int bIsNAD83 = FALSE; + /* int bIsNAD27 = FALSE; */ + + GDALPDFObject* poDatum; + if ((poDatum = poProjDict->Get("Datum")) != NULL) + { + if (poDatum->GetType() == PDFObjectType_String) + { + /* Using Annex A of http://portal.opengeospatial.org/files/?artifact_id=40537 */ + const char* pszDatum = poDatum->GetString().c_str(); + CPLDebug("PDF", "Datum = %s", pszDatum); + if (EQUAL(pszDatum, "WE") || EQUAL(pszDatum, "WGE")) + { + bIsWGS84 = TRUE; + oSRS.SetWellKnownGeogCS("WGS84"); + } + else if (EQUAL(pszDatum, "NAR") || EQUALN(pszDatum, "NAR-", 4)) + { + bIsNAD83 = TRUE; + oSRS.SetWellKnownGeogCS("NAD83"); + } + else if (EQUAL(pszDatum, "NAS") || EQUALN(pszDatum, "NAS-", 4)) + { + /* bIsNAD27 = TRUE; */ + oSRS.SetWellKnownGeogCS("NAD27"); + } + else if (EQUAL(pszDatum, "HEN")) /* HERAT North, Afghanistan */ + { + oSRS.SetGeogCS( "unknown" /*const char * pszGeogName*/, + "unknown" /*const char * pszDatumName */, + "International 1924", + 6378388,297); + oSRS.SetTOWGS84(-333,-222,114); + } + else if (EQUAL(pszDatum, "ING-A")) /* INDIAN 1960, Vietnam 16N */ + { + oSRS.importFromEPSG(4131); + } + else if (EQUAL(pszDatum, "GDS")) /* Geocentric Datum of Australia */ + { + oSRS.importFromEPSG(4283); + } + else if (EQUALN(pszDatum, "OHA-", 4)) /* Old Hawaiian */ + { + oSRS.importFromEPSG(4135); /* matches OHA-M (Mean) */ + if( !EQUAL(pszDatum, "OHA-M") ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Using OHA-M (Old Hawaiian Mean) definition for %s. Potential issue with datum shift parameters", + pszDatum); + OGR_SRSNode *poNode = oSRS.GetRoot(); + int iChild = poNode->FindChild( "AUTHORITY" ); + if( iChild != -1 ) + poNode->DestroyChild( iChild ); + iChild = poNode->FindChild( "DATUM" ); + if( iChild != -1 ) + { + poNode = poNode->GetChild(iChild); + iChild = poNode->FindChild( "AUTHORITY" ); + if( iChild != -1 ) + poNode->DestroyChild( iChild ); + } + } + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Unhandled (yet) value for Datum : %s. Defaulting to WGS84...", + pszDatum); + oSRS.SetGeogCS( "unknown" /*const char * pszGeogName*/, + "unknown" /*const char * pszDatumName */, + "unknown", + 6378137,298.257223563); + } + } + else if (poDatum->GetType() == PDFObjectType_Dictionary) + { + GDALPDFDictionary* poDatumDict = poDatum->GetDictionary(); + + GDALPDFObject* poDatumDescription = poDatumDict->Get("Description"); + const char* pszDatumDescription = "unknown"; + if (poDatumDescription != NULL && + poDatumDescription->GetType() == PDFObjectType_String) + pszDatumDescription = poDatumDescription->GetString().c_str(); + CPLDebug("PDF", "Datum.Description = %s", pszDatumDescription); + + GDALPDFObject* poEllipsoid = poDatumDict->Get("Ellipsoid"); + if (poEllipsoid == NULL || + !(poEllipsoid->GetType() == PDFObjectType_String || + poEllipsoid->GetType() == PDFObjectType_Dictionary)) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot find Ellipsoid in Datum. Defaulting to WGS84..."); + oSRS.SetGeogCS( "unknown", + pszDatumDescription, + "unknown", + 6378137,298.257223563); + } + else if (poEllipsoid->GetType() == PDFObjectType_String) + { + const char* pszEllipsoid = poEllipsoid->GetString().c_str(); + CPLDebug("PDF", "Datum.Ellipsoid = %s", pszEllipsoid); + if( EQUAL(pszEllipsoid, "WE") ) + { + oSRS.SetGeogCS( "unknown", + pszDatumDescription, + "WGS 84", + 6378137,298.257223563); + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Unhandled (yet) value for Ellipsoid : %s. Defaulting to WGS84...", + pszEllipsoid); + oSRS.SetGeogCS( "unknown", + pszDatumDescription, + pszEllipsoid, + 6378137,298.257223563); + } + } + else// if (poEllipsoid->GetType() == PDFObjectType_Dictionary) + { + GDALPDFDictionary* poEllipsoidDict = poEllipsoid->GetDictionary(); + + GDALPDFObject* poEllipsoidDescription = poEllipsoidDict->Get("Description"); + const char* pszEllipsoidDescription = "unknown"; + if (poEllipsoidDescription != NULL && + poEllipsoidDescription->GetType() == PDFObjectType_String) + pszEllipsoidDescription = poEllipsoidDescription->GetString().c_str(); + CPLDebug("PDF", "Datum.Ellipsoid.Description = %s", pszEllipsoidDescription); + + double dfSemiMajor = Get(poEllipsoidDict, "SemiMajorAxis"); + CPLDebug("PDF", "Datum.Ellipsoid.SemiMajorAxis = %.16g", dfSemiMajor); + double dfInvFlattening = -1.0; + + if( poEllipsoidDict->Get("InvFlattening") ) + { + dfInvFlattening = Get(poEllipsoidDict, "InvFlattening"); + CPLDebug("PDF", "Datum.Ellipsoid.InvFlattening = %.16g", dfInvFlattening); + } + else if( poEllipsoidDict->Get("SemiMinorAxis") ) + { + double dfSemiMinor = Get(poEllipsoidDict, "SemiMinorAxis"); + CPLDebug("PDF", "Datum.Ellipsoid.SemiMinorAxis = %.16g", dfSemiMinor); + dfInvFlattening = OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor); + } + + if( dfSemiMajor != 0.0 && dfInvFlattening != -1.0 ) + { + oSRS.SetGeogCS( "unknown", + pszDatumDescription, + pszEllipsoidDescription, + dfSemiMajor, dfInvFlattening); + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid Ellipsoid object. Defaulting to WGS84..."); + oSRS.SetGeogCS( "unknown", + pszDatumDescription, + pszEllipsoidDescription, + 6378137,298.257223563); + } + + + } + + GDALPDFObject* poTOWGS84 = poDatumDict->Get("ToWGS84"); + if( poTOWGS84 != NULL && poTOWGS84->GetType() == PDFObjectType_Dictionary ) + { + GDALPDFDictionary* poTOWGS84Dict = poTOWGS84->GetDictionary(); + double dx = Get(poTOWGS84Dict, "dx"); + double dy = Get(poTOWGS84Dict, "dy"); + double dz = Get(poTOWGS84Dict, "dz"); + if (poTOWGS84Dict->Get("rx") && poTOWGS84Dict->Get("ry") && + poTOWGS84Dict->Get("rz") && poTOWGS84Dict->Get("sf")) + { + double rx = Get(poTOWGS84Dict, "rx"); + double ry = Get(poTOWGS84Dict, "ry"); + double rz = Get(poTOWGS84Dict, "rz"); + double sf = Get(poTOWGS84Dict, "sf"); + oSRS.SetTOWGS84(dx, dy, dz, rx, ry, rz, sf); + } + else + { + oSRS.SetTOWGS84(dx, dy, dz); + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Extract Hemisphere attribute */ +/* -------------------------------------------------------------------- */ + CPLString osHemisphere; + GDALPDFObject* poHemisphere; + if ((poHemisphere = poProjDict->Get("Hemisphere")) != NULL && + poHemisphere->GetType() == PDFObjectType_String) + { + osHemisphere = poHemisphere->GetString(); + } + +/* -------------------------------------------------------------------- */ +/* Extract ProjectionType attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poProjectionType; + if ((poProjectionType = poProjDict->Get("ProjectionType")) == NULL || + poProjectionType->GetType() != PDFObjectType_String) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find ProjectionType of Projection object"); + return FALSE; + } + CPLString osProjectionType(poProjectionType->GetString()); + CPLDebug("PDF", "Projection.ProjectionType = %s", osProjectionType.c_str()); + + /* Unhandled: NONE, GEODETIC */ + + if (EQUAL(osProjectionType, "GEOGRAPHIC")) + { + /* Nothing to do */ + } + + /* Unhandled: LOCAL CARTESIAN, MG (MGRS) */ + + else if (EQUAL(osProjectionType, "UT")) /* UTM */ + { + int nZone = (int)Get(poProjDict, "Zone"); + int bNorth = EQUAL(osHemisphere, "N"); + if (bIsWGS84) + oSRS.importFromEPSG( ((bNorth) ? 32600 : 32700) + nZone ); + else + oSRS.SetUTM( nZone, bNorth ); + } + + else if (EQUAL(osProjectionType, "UP")) /* Universal Polar Stereographic (UPS) */ + { + int bNorth = EQUAL(osHemisphere, "N"); + if (bIsWGS84) + oSRS.importFromEPSG( (bNorth) ? 32661 : 32761 ); + else + oSRS.SetPS( (bNorth) ? 90 : -90, 0, + 0.994, 200000, 200000 ); + } + + else if (EQUAL(osProjectionType, "SPCS")) /* State Plane */ + { + int nZone = (int)Get(poProjDict, "Zone"); + oSRS.SetStatePlane( nZone, bIsNAD83 ); + } + + else if (EQUAL(osProjectionType, "AC")) /* Albers Equal Area Conic */ + { + double dfStdP1 = Get(poProjDict, "StandardParallelOne"); + double dfStdP2 = Get(poProjDict, "StandardParallelTwo"); + double dfCenterLat = Get(poProjDict, "OriginLatitude"); + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetACEA( dfStdP1, dfStdP2, + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "AL")) /* Azimuthal Equidistant */ + { + double dfCenterLat = Get(poProjDict, "OriginLatitude"); + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetAE( dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "BF")) /* Bonne */ + { + double dfStdP1 = Get(poProjDict, "OriginLatitude"); + double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetBonne( dfStdP1, dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "CS")) /* Cassini */ + { + double dfCenterLat = Get(poProjDict, "OriginLatitude"); + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetCS( dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "LI")) /* Cylindrical Equal Area */ + { + double dfStdP1 = Get(poProjDict, "OriginLatitude"); + double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetCEA( dfStdP1, dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "EF")) /* Eckert IV */ + { + double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetEckertIV( dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "ED")) /* Eckert VI */ + { + double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetEckertVI( dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "CP")) /* Equidistant Cylindrical */ + { + double dfCenterLat = Get(poProjDict, "StandardParallel"); + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetEquirectangular( dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "GN")) /* Gnomonic */ + { + double dfCenterLat = Get(poProjDict, "OriginLatitude"); + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetGnomonic(dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "LE")) /* Lambert Conformal Conic */ + { + double dfStdP1 = Get(poProjDict, "StandardParallelOne"); + double dfStdP2 = Get(poProjDict, "StandardParallelTwo"); + double dfCenterLat = Get(poProjDict, "OriginLatitude"); + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetLCC( dfStdP1, dfStdP2, + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "MC")) /* Mercator */ + { +#ifdef not_supported + if (poProjDict->Get("StandardParallelOne") == NULL) + { +#endif + double dfCenterLat = Get(poProjDict, "OriginLatitude"); + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfScale = Get(poProjDict, "ScaleFactor"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetMercator( dfCenterLat, dfCenterLong, + dfScale, + dfFalseEasting, dfFalseNorthing ); +#ifdef not_supported + } + else + { + double dfStdP1 = Get(poProjDict, "StandardParallelOne"); + double dfCenterLat = poProjDict->Get("OriginLatitude") ? Get(poProjDict, "OriginLatitude") : 0; + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetMercator2SP( dfStdP1, + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } +#endif + } + + else if (EQUAL(osProjectionType, "MH")) /* Miller Cylindrical */ + { + double dfCenterLat = 0 /* ? */; + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetMC( dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "MP")) /* Mollweide */ + { + double dfCentralMeridian = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetMollweide( dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); + } + + /* Unhandled: "NY" : Ney's (Modified Lambert Conformal Conic) */ + + else if (EQUAL(osProjectionType, "NT")) /* New Zealand Map Grid */ + { + /* No parameter specified in the PDF, so let's take the ones of EPSG:27200 */ + double dfCenterLat = -41; + double dfCenterLong = 173; + double dfFalseEasting = 2510000; + double dfFalseNorthing = 6023150; + oSRS.SetNZMG( dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "OC")) /* Oblique Mercator */ + { + double dfCenterLat = Get(poProjDict, "OriginLatitude"); + double dfLat1 = Get(poProjDict, "LatitudeOne"); + double dfLong1 = Get(poProjDict, "LongitudeOne"); + double dfLat2 = Get(poProjDict, "LatitudeTwo"); + double dfLong2 = Get(poProjDict, "LongitudeTwo"); + double dfScale = Get(poProjDict, "ScaleFactor"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetHOM2PNO( dfCenterLat, + dfLat1, dfLong1, + dfLat2, dfLong2, + dfScale, + dfFalseEasting, + dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "OD")) /* Orthographic */ + { + double dfCenterLat = Get(poProjDict, "OriginLatitude"); + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetOrthographic( dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "PG")) /* Polar Stereographic */ + { + double dfCenterLat = Get(poProjDict, "LatitudeTrueScale"); + double dfCenterLong = Get(poProjDict, "LongitudeDownFromPole"); + double dfScale = 1.0; + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetPS( dfCenterLat, dfCenterLong, + dfScale, + dfFalseEasting, dfFalseNorthing); + } + + else if (EQUAL(osProjectionType, "PH")) /* Polyconic */ + { + double dfCenterLat = Get(poProjDict, "OriginLatitude"); + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetPolyconic( dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "SA")) /* Sinusoidal */ + { + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetSinusoidal( dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } + + else if (EQUAL(osProjectionType, "SD")) /* Stereographic */ + { + double dfCenterLat = Get(poProjDict, "OriginLatitude"); + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfScale = 1.0; + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetStereographic( dfCenterLat, dfCenterLong, + dfScale, + dfFalseEasting, dfFalseNorthing); + } + + else if (EQUAL(osProjectionType, "TC")) /* Transverse Mercator */ + { + double dfCenterLat = Get(poProjDict, "OriginLatitude"); + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfScale = Get(poProjDict, "ScaleFactor"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + if (dfCenterLat == 0.0 && dfScale == 0.9996 && dfFalseEasting == 500000 && + (dfFalseNorthing == 0.0 || dfFalseNorthing == 10000000.0)) + { + int nZone = (int) floor( (dfCenterLong + 180.0) / 6.0 ) + 1; + int bNorth = dfFalseNorthing == 0; + if (bIsWGS84) + oSRS.importFromEPSG( ((bNorth) ? 32600 : 32700) + nZone ); + else if (bIsNAD83 && bNorth) + oSRS.importFromEPSG( 26900 + nZone ); + else + oSRS.SetUTM( nZone, bNorth ); + } + else + { + oSRS.SetTM( dfCenterLat, dfCenterLong, + dfScale, + dfFalseEasting, dfFalseNorthing ); + } + } + + /* Unhandled TX : Transverse Cylindrical Equal Area */ + + else if (EQUAL(osProjectionType, "VA")) /* Van der Grinten */ + { + double dfCenterLong = Get(poProjDict, "CentralMeridian"); + double dfFalseEasting = Get(poProjDict, "FalseEasting"); + double dfFalseNorthing = Get(poProjDict, "FalseNorthing"); + oSRS.SetVDG( dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + } + + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unhandled (yet) value for ProjectionType : %s", + osProjectionType.c_str()); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Extract Units attribute */ +/* -------------------------------------------------------------------- */ + CPLString osUnits; + GDALPDFObject* poUnits; + if ((poUnits = poProjDict->Get("Units")) != NULL && + poUnits->GetType() == PDFObjectType_String) + { + osUnits = poUnits->GetString(); + CPLDebug("PDF", "Projection.Units = %s", osUnits.c_str()); + + if (EQUAL(osUnits, "M")) + oSRS.SetLinearUnits( "Meter", 1.0 ); + else if (EQUAL(osUnits, "FT")) + oSRS.SetLinearUnits( "foot", 0.3048 ); + } + +/* -------------------------------------------------------------------- */ +/* Export SpatialRef */ +/* -------------------------------------------------------------------- */ + CPLFree(pszWKT); + pszWKT = NULL; + if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE) + { + CPLFree(pszWKT); + pszWKT = NULL; + } + + return TRUE; +} + +/************************************************************************/ +/* ParseVP() */ +/************************************************************************/ + +int PDFDataset::ParseVP(GDALPDFObject* poVP, double dfMediaBoxWidth, double dfMediaBoxHeight) +{ + int i; + + if (poVP->GetType() != PDFObjectType_Array) + return FALSE; + + GDALPDFArray* poVPArray = poVP->GetArray(); + + int nLength = poVPArray->GetLength(); + CPLDebug("PDF", "VP length = %d", nLength); + if (nLength < 1) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Find the largest BBox */ +/* -------------------------------------------------------------------- */ + int iLargest = 0; + double dfLargestArea = 0; + + for(i=0;iGet(i); + if (poVPElt == NULL || poVPElt->GetType() != PDFObjectType_Dictionary) + { + return FALSE; + } + + GDALPDFDictionary* poVPEltDict = poVPElt->GetDictionary(); + + GDALPDFObject* poBBox; + if( (poBBox = poVPEltDict->Get("BBox")) == NULL || + poBBox->GetType() != PDFObjectType_Array ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find Bbox object"); + return FALSE; + } + + int nBboxLength = poBBox->GetArray()->GetLength(); + if (nBboxLength != 4) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid length for Bbox object"); + return FALSE; + } + + double adfBBox[4]; + adfBBox[0] = Get(poBBox, 0); + adfBBox[1] = Get(poBBox, 1); + adfBBox[2] = Get(poBBox, 2); + adfBBox[3] = Get(poBBox, 3); + double dfArea = fabs(adfBBox[2] - adfBBox[0]) * fabs(adfBBox[3] - adfBBox[1]); + if (dfArea > dfLargestArea) + { + iLargest = i; + dfLargestArea = dfArea; + } + } + + if (nLength > 1) + { + CPLDebug("PDF", "Largest BBox in VP array is element %d", iLargest); + } + + + GDALPDFObject* poVPElt = poVPArray->Get(iLargest); + if (poVPElt == NULL || poVPElt->GetType() != PDFObjectType_Dictionary) + { + return FALSE; + } + + GDALPDFDictionary* poVPEltDict = poVPElt->GetDictionary(); + + GDALPDFObject* poBBox; + if( (poBBox = poVPEltDict->Get("BBox")) == NULL || + poBBox->GetType() != PDFObjectType_Array ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find Bbox object"); + return FALSE; + } + + int nBboxLength = poBBox->GetArray()->GetLength(); + if (nBboxLength != 4) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid length for Bbox object"); + return FALSE; + } + + double dfULX = Get(poBBox, 0); + double dfULY = dfMediaBoxHeight - Get(poBBox, 1); + double dfLRX = Get(poBBox, 2); + double dfLRY = dfMediaBoxHeight - Get(poBBox, 3); + +/* -------------------------------------------------------------------- */ +/* Extract Measure attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poMeasure; + if( (poMeasure = poVPEltDict->Get("Measure")) == NULL || + poMeasure->GetType() != PDFObjectType_Dictionary ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find Measure object"); + return FALSE; + } + + int bRet = ParseMeasure(poMeasure, dfMediaBoxWidth, dfMediaBoxHeight, + dfULX, dfULY, dfLRX, dfLRY); + +/* -------------------------------------------------------------------- */ +/* Extract PointData attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poPointData; + if( (poPointData = poVPEltDict->Get("PtData")) != NULL && + poPointData->GetType() == PDFObjectType_Dictionary ) + { + CPLDebug("PDF", "Found PointData"); + } + + return bRet; +} + +/************************************************************************/ +/* ParseMeasure() */ +/************************************************************************/ + +int PDFDataset::ParseMeasure(GDALPDFObject* poMeasure, + double dfMediaBoxWidth, double dfMediaBoxHeight, + double dfULX, double dfULY, double dfLRX, double dfLRY) +{ + int i; + GDALPDFDictionary* poMeasureDict = poMeasure->GetDictionary(); + +/* -------------------------------------------------------------------- */ +/* Extract Subtype attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poSubtype; + if( (poSubtype = poMeasureDict->Get("Subtype")) == NULL || + poSubtype->GetType() != PDFObjectType_Name ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find Subtype object"); + return FALSE; + } + + CPLDebug("PDF", "Subtype = %s", poSubtype->GetName().c_str()); + +/* -------------------------------------------------------------------- */ +/* Extract Bounds attribute (optional) */ +/* -------------------------------------------------------------------- */ + + /* http://acrobatusers.com/sites/default/files/gallery_pictures/SEVERODVINSK.pdf */ + /* has lgit:LPTS, lgit:GPTS and lgit:Bounds that have more precision than */ + /* LPTS, GPTS and Bounds. Use those ones */ + + GDALPDFObject* poBounds; + if( (poBounds = poMeasureDict->Get("lgit:Bounds")) != NULL && + poBounds->GetType() == PDFObjectType_Array ) + { + CPLDebug("PDF", "Using lgit:Bounds"); + } + else if( (poBounds = poMeasureDict->Get("Bounds")) == NULL || + poBounds->GetType() != PDFObjectType_Array ) + { + poBounds = NULL; + } + + if (poBounds != NULL) + { + int nBoundsLength = poBounds->GetArray()->GetLength(); + if (nBoundsLength == 8) + { + double adfBounds[8]; + for(i=0;i<8;i++) + { + adfBounds[i] = Get(poBounds, i); + CPLDebug("PDF", "Bounds[%d] = %f", i, adfBounds[i]); + } + + // TODO we should use it to restrict the neatline but + // I have yet to set a sample where bounds are not the four + // corners of the unit square. + } + } + +/* -------------------------------------------------------------------- */ +/* Extract GPTS attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poGPTS; + if( (poGPTS = poMeasureDict->Get("lgit:GPTS")) != NULL && + poGPTS->GetType() == PDFObjectType_Array ) + { + CPLDebug("PDF", "Using lgit:GPTS"); + } + else if( (poGPTS = poMeasureDict->Get("GPTS")) == NULL || + poGPTS->GetType() != PDFObjectType_Array ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find GPTS object"); + return FALSE; + } + + int nGPTSLength = poGPTS->GetArray()->GetLength(); + if (nGPTSLength != 8) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid length for GPTS object"); + return FALSE; + } + + double adfGPTS[8]; + for(i=0;i<8;i++) + { + adfGPTS[i] = Get(poGPTS, i); + CPLDebug("PDF", "GPTS[%d] = %.18f", i, adfGPTS[i]); + } + +/* -------------------------------------------------------------------- */ +/* Extract LPTS attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poLPTS; + if( (poLPTS = poMeasureDict->Get("lgit:LPTS")) != NULL && + poLPTS->GetType() == PDFObjectType_Array ) + { + CPLDebug("PDF", "Using lgit:LPTS"); + } + else if( (poLPTS = poMeasureDict->Get("LPTS")) == NULL || + poLPTS->GetType() != PDFObjectType_Array ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find LPTS object"); + return FALSE; + } + + int nLPTSLength = poLPTS->GetArray()->GetLength(); + if (nLPTSLength != 8) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid length for LPTS object"); + return FALSE; + } + + double adfLPTS[8]; + for(i=0;i<8;i++) + { + adfLPTS[i] = Get(poLPTS, i); + CPLDebug("PDF", "LPTS[%d] = %f", i, adfLPTS[i]); + } + +/* -------------------------------------------------------------------- */ +/* Extract GCS attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poGCS; + if( (poGCS = poMeasureDict->Get("GCS")) == NULL || + poGCS->GetType() != PDFObjectType_Dictionary ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find GCS object"); + return FALSE; + } + + GDALPDFDictionary* poGCSDict = poGCS->GetDictionary(); + +/* -------------------------------------------------------------------- */ +/* Extract GCS.Type attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poGCSType; + if( (poGCSType = poGCSDict->Get("Type")) == NULL || + poGCSType->GetType() != PDFObjectType_Name ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find GCS.Type object"); + return FALSE; + } + + CPLDebug("PDF", "GCS.Type = %s", poGCSType->GetName().c_str()); + +/* -------------------------------------------------------------------- */ +/* Extract EPSG attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poEPSG; + int nEPSGCode = 0; + if( (poEPSG = poGCSDict->Get("EPSG")) != NULL && + poEPSG->GetType() == PDFObjectType_Int ) + { + nEPSGCode = poEPSG->GetInt(); + CPLDebug("PDF", "GCS.EPSG = %d", nEPSGCode); + } + +/* -------------------------------------------------------------------- */ +/* Extract GCS.WKT attribute */ +/* -------------------------------------------------------------------- */ + GDALPDFObject* poGCSWKT = poGCSDict->Get("WKT"); + if( poGCSWKT != NULL && + poGCSWKT->GetType() != PDFObjectType_String ) + { + poGCSWKT = NULL; + } + + if (poGCSWKT != NULL) + CPLDebug("PDF", "GCS.WKT = %s", poGCSWKT->GetString().c_str()); + + if (nEPSGCode <= 0 && poGCSWKT == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find GCS.WKT or GCS.EPSG objects"); + return FALSE; + } + + OGRSpatialReference oSRS; + int bSRSOK = FALSE; + if (nEPSGCode != 0 && + oSRS.importFromEPSG(nEPSGCode) == OGRERR_NONE) + { + bSRSOK = TRUE; + CPLFree(pszWKT); + pszWKT = NULL; + oSRS.exportToWkt(&pszWKT); + } + else + { + if (poGCSWKT == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot resolve EPSG object, and GCS.WKT not found"); + return FALSE; + } + + CPLFree(pszWKT); + pszWKT = CPLStrdup(poGCSWKT->GetString().c_str()); + } + + if (!bSRSOK) + { + char* pszWktTemp = pszWKT; + if (oSRS.importFromWkt(&pszWktTemp) != OGRERR_NONE) + { + CPLFree(pszWKT); + pszWKT = NULL; + return FALSE; + } + } + + /* For http://www.avenza.com/sites/default/files/spatialpdf/US_County_Populations.pdf */ + /* or http://www.agmkt.state.ny.us/soilwater/aem/gis_mapping_tools/HUC12_Albany.pdf */ + const char* pszDatum = oSRS.GetAttrValue("Datum"); + if (pszDatum && strncmp(pszDatum, "D_", 2) == 0) + { + oSRS.morphFromESRI(); + + CPLFree(pszWKT); + pszWKT = NULL; + if (oSRS.exportToWkt(&pszWKT) != OGRERR_NONE) + { + CPLFree(pszWKT); + pszWKT = NULL; + } + else + { + CPLDebug("PDF", "WKT after morphFromESRI() = %s", pszWKT); + } + } + +/* -------------------------------------------------------------------- */ +/* Compute geotransform */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference* poSRSGeog = oSRS.CloneGeogCS(); + + /* Files found at http://carto.iict.ch/blog/publications-cartographiques-au-format-geospatial-pdf/ */ + /* are in a PROJCS. However the coordinates in GPTS array are not in (lat, long) as required by the */ + /* ISO 32000 supplement spec, but in (northing, easting). Adobe reader is able to understand that, */ + /* so let's also try to do it with a heuristics. */ + + int bReproject = TRUE; + if (oSRS.IsProjected() && + (fabs(adfGPTS[0]) > 91 || fabs(adfGPTS[2]) > 91 || fabs(adfGPTS[4]) > 91 || fabs(adfGPTS[6]) > 91 || + fabs(adfGPTS[1]) > 361 || fabs(adfGPTS[3]) > 361 || fabs(adfGPTS[5]) > 361 || fabs(adfGPTS[7]) > 361)) + { + CPLDebug("PDF", "GPTS coordinates seems to be in (northing, easting), which is non-standard"); + bReproject = FALSE; + } + + OGRCoordinateTransformation* poCT = NULL; + if (bReproject) + { + poCT = OGRCreateCoordinateTransformation( poSRSGeog, &oSRS); + if (poCT == NULL) + { + delete poSRSGeog; + CPLFree(pszWKT); + pszWKT = NULL; + return FALSE; + } + } + + GDAL_GCP asGCPS[4]; + + /* Create NEATLINE */ + poNeatLine = new OGRPolygon(); + OGRLinearRing* poRing = new OGRLinearRing(); + poNeatLine->addRingDirectly(poRing); + + for(i=0;i<4;i++) + { + /* We probably assume LPTS is 0 or 1 */ + asGCPS[i].dfGCPPixel = (dfULX * (1 - adfLPTS[2*i+0]) + dfLRX * adfLPTS[2*i+0]) / dfMediaBoxWidth * nRasterXSize; + asGCPS[i].dfGCPLine = (dfULY * (1 - adfLPTS[2*i+1]) + dfLRY * adfLPTS[2*i+1]) / dfMediaBoxHeight * nRasterYSize; + + double lat = adfGPTS[2*i], lon = adfGPTS[2*i+1]; + double x = lon, y = lat; + if (bReproject) + { + if (!poCT->Transform(1, &x, &y, NULL)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot reproject (%f, %f)", lon, lat); + delete poSRSGeog; + delete poCT; + CPLFree(pszWKT); + pszWKT = NULL; + return FALSE; + } + } + + x = ROUND_TO_INT_IF_CLOSE(x); + y = ROUND_TO_INT_IF_CLOSE(y); + + asGCPS[i].dfGCPX = x; + asGCPS[i].dfGCPY = y; + + poRing->addPoint(x, y); + } + + delete poSRSGeog; + delete poCT; + + if (!GDALGCPsToGeoTransform( 4, asGCPS, + adfGeoTransform, FALSE)) + { + CPLDebug("PDF", "Could not compute GT with exact match. Try with approximate"); + if (!GDALGCPsToGeoTransform( 4, asGCPS, + adfGeoTransform, TRUE)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Could not compute GT with approximate match."); + return FALSE; + } + } + bGeoTransformValid = TRUE; + + /* If the non scaling terms of the geotransform are significantly smaller than */ + /* the pixel size, then nullify them as being just artifacts of reprojection and */ + /* GDALGCPsToGeoTransform() numerical imprecisions */ + double dfPixelSize = MIN(fabs(adfGeoTransform[1]), fabs(adfGeoTransform[5])); + double dfRotationShearTerm = MAX(fabs(adfGeoTransform[2]), fabs(adfGeoTransform[4])); + if (dfRotationShearTerm < 1e-5 * dfPixelSize) + { + double dfLRX = adfGeoTransform[0] + nRasterXSize * adfGeoTransform[1] + nRasterYSize * adfGeoTransform[2]; + double dfLRY = adfGeoTransform[3] + nRasterXSize * adfGeoTransform[4] + nRasterYSize * adfGeoTransform[5]; + adfGeoTransform[1] = (dfLRX - adfGeoTransform[0]) / nRasterXSize; + adfGeoTransform[5] = (dfLRY - adfGeoTransform[3]) / nRasterYSize; + adfGeoTransform[2] = adfGeoTransform[4] = 0; + } + + return TRUE; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* PDFDataset::GetProjectionRef() +{ + if (pszWKT && bGeoTransformValid) + return pszWKT; + return ""; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr PDFDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double)); + + return( (bGeoTransformValid) ? CE_None : CE_Failure ); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr PDFDataset::SetProjection(const char* pszWKTIn) +{ + CPLFree(pszWKT); + pszWKT = pszWKTIn ? CPLStrdup(pszWKTIn) : CPLStrdup(""); + bProjDirty = TRUE; + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr PDFDataset::SetGeoTransform(double* padfGeoTransform) +{ + memcpy(adfGeoTransform, padfGeoTransform, 6 * sizeof(double)); + bGeoTransformValid = TRUE; + bProjDirty = TRUE; + + /* Reset NEATLINE if not explicitly set by the user */ + if (!bNeatLineDirty) + SetMetadataItem("NEATLINE", NULL); + return CE_None; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **PDFDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "xml:XMP", "LAYERS", "EMBEDDED_METADATA", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **PDFDataset::GetMetadata( const char * pszDomain ) +{ + if( pszDomain != NULL && EQUAL(pszDomain, "EMBEDDED_METADATA") ) + { + char** papszRet = oMDMD.GetMetadata(pszDomain); + if( papszRet ) + return papszRet; + + GDALPDFObject* poCatalog = GetCatalog(); + if( poCatalog == NULL ) + return NULL; + GDALPDFObject* poFirstElt = poCatalog->LookupObject("Names.EmbeddedFiles.Names[0]"); + GDALPDFObject* poF = poCatalog->LookupObject("Names.EmbeddedFiles.Names[1].EF.F"); + + if( poFirstElt == NULL || poFirstElt->GetType() != PDFObjectType_String || + poFirstElt->GetString() != "Metadata" ) + return NULL; + if( poF == NULL || poF->GetType() != PDFObjectType_Dictionary ) + return NULL; + GDALPDFStream* poStream = poF->GetStream(); + if( poStream == NULL ) + return NULL; + + char* apszMetadata[2] = { NULL, NULL }; + apszMetadata[0] = poStream->GetBytes(); + oMDMD.SetMetadata(apszMetadata, pszDomain); + VSIFree(apszMetadata[0]); + } + + return oMDMD.GetMetadata(pszDomain); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr PDFDataset::SetMetadata( char ** papszMetadata, + const char * pszDomain ) +{ + if (pszDomain == NULL || EQUAL(pszDomain, "")) + { + if (CSLFindString(papszMetadata, "NEATLINE") != -1) + { + bProjDirty = TRUE; + bNeatLineDirty = TRUE; + } + bInfoDirty = TRUE; + } + else if (EQUAL(pszDomain, "xml:XMP")) + bXMPDirty = TRUE; + return oMDMD.SetMetadata(papszMetadata, pszDomain); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *PDFDataset::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + return oMDMD.GetMetadataItem(pszName, pszDomain); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr PDFDataset::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) +{ + if (pszDomain == NULL || EQUAL(pszDomain, "")) + { + if (EQUAL(pszName, "NEATLINE")) + { + bProjDirty = TRUE; + bNeatLineDirty = TRUE; + } + else + { + if (pszValue == NULL) + pszValue = ""; + bInfoDirty = TRUE; + } + } + else if (EQUAL(pszDomain, "xml:XMP")) + bXMPDirty = TRUE; + return oMDMD.SetMetadataItem(pszName, pszValue, pszDomain); +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int PDFDataset::GetGCPCount() +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char * PDFDataset::GetGCPProjection() +{ + if (pszWKT != NULL && nGCPCount != 0) + return pszWKT; + return ""; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP * PDFDataset::GetGCPs() +{ + return pasGCPList; +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +CPLErr PDFDataset::SetGCPs( int nGCPCountIn, const GDAL_GCP *pasGCPListIn, + const char *pszGCPProjectionIn ) +{ + const char* pszGEO_ENCODING = + CPLGetConfigOption("GDAL_PDF_GEO_ENCODING", "ISO32000"); + if( nGCPCountIn != 4 && EQUAL(pszGEO_ENCODING, "ISO32000")) + { + CPLError(CE_Failure, CPLE_NotSupported, + "PDF driver only supports writing 4 GCPs when " + "GDAL_PDF_GEO_ENCODING=ISO32000."); + return CE_Failure; + } + + /* Free previous GCPs */ + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + + /* Duplicate in GCPs */ + nGCPCount = nGCPCountIn; + pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn); + + CPLFree(pszWKT); + pszWKT = CPLStrdup(pszGCPProjectionIn); + + bProjDirty = TRUE; + + /* Reset NEATLINE if not explicitly set by the user */ + if (!bNeatLineDirty) + SetMetadataItem("NEATLINE", NULL); + + return CE_None; +} + +#endif // #if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) + +/************************************************************************/ +/* GDALPDFOpen() */ +/************************************************************************/ + +GDALDataset* GDALPDFOpen( +#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) + const char* pszFilename, + GDALAccess eAccess +#else + CPL_UNUSED const char* pszFilename, + CPL_UNUSED GDALAccess eAccess +#endif + ) +{ +#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) + GDALOpenInfo oOpenInfo(pszFilename, eAccess); + return PDFDataset::Open(&oOpenInfo); +#else + return NULL; +#endif +} + +/************************************************************************/ +/* GDALPDFUnloadDriver() */ +/************************************************************************/ + +static void GDALPDFUnloadDriver(CPL_UNUSED GDALDriver * poDriver) +{ +#ifdef HAVE_POPPLER + if( hGlobalParamsMutex != NULL ) + CPLDestroyMutex(hGlobalParamsMutex); +#endif +} + +/************************************************************************/ +/* PDFSanitizeLayerName() */ +/************************************************************************/ + +CPLString PDFSanitizeLayerName(const char* pszName) +{ + CPLString osName; + for(int i=0; pszName[i] != '\0'; i++) + { + if (pszName[i] == ' ' || pszName[i] == '.' || pszName[i] == ',') + osName += "_"; + else if (pszName[i] != '"') + osName += pszName[i]; + } + return osName; +} + +/************************************************************************/ +/* GDALRegister_PDF() */ +/************************************************************************/ + +void GDALRegister_PDF() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("PDF driver")) + return; + + if( GDALGetDriverByName( "PDF" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "PDF" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Geospatial PDF" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_pdf.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "pdf" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte" ); +#ifdef HAVE_POPPLER + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( "HAVE_POPPLER", "YES" ); +#endif // HAVE_POPPLER +#ifdef HAVE_PODOFO + poDriver->SetMetadataItem( "HAVE_PODOFO", "YES" ); +#endif // HAVE_PODOFO + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"\n" +" \n" +" \n" +" \n" +" \n" ); + +#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, pszOpenOptionList ); + poDriver->pfnOpen = PDFDataset::Open; + poDriver->pfnIdentify = PDFDataset::Identify; + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); +#endif // HAVE_POPPLER || HAVE_PODOFO + + poDriver->pfnCreateCopy = GDALPDFCreateCopy; + poDriver->pfnCreate = PDFWritableVectorDataset::Create; + poDriver->pfnUnloadDriver = GDALPDFUnloadDriver; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/pdf/pdfio.cpp b/bazaar/plugin/gdal/frmts/pdf/pdfio.cpp new file mode 100644 index 000000000..03343112c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/pdfio.cpp @@ -0,0 +1,403 @@ +/****************************************************************************** + * $Id $ + * + * Project: PDF driver + * Purpose: GDALDataset driver for PDF dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pdf.h" + +#ifdef HAVE_POPPLER + +#include "pdfio.h" + +#include "cpl_vsi.h" + +CPL_CVSID("$Id: pdfio.cpp 28956 2015-04-20 16:17:55Z rouault $"); + + +#ifdef POPPLER_BASE_STREAM_HAS_TWO_ARGS +/* Poppler 0.31.0 is the first one that needs to know the file size */ +static vsi_l_offset VSIPDFFileStreamGetSize(VSILFILE* f) +{ + VSIFSeekL(f, 0, SEEK_END); + vsi_l_offset nSize = VSIFTellL(f); + VSIFSeekL(f, 0, SEEK_SET); + return nSize; +} +#endif + +/************************************************************************/ +/* VSIPDFFileStream() */ +/************************************************************************/ + +VSIPDFFileStream::VSIPDFFileStream(VSILFILE* f, const char* pszFilename, Object *dictA): +#ifdef POPPLER_BASE_STREAM_HAS_TWO_ARGS + BaseStream(dictA, (setPos_offset_type)VSIPDFFileStreamGetSize(f)) +#else + BaseStream(dictA) +#endif +{ + poParent = NULL; + poFilename = new GooString(pszFilename); + this->f = f; + nStart = 0; + bLimited = gFalse; + nLength = 0; + nCurrentPos = -1; + bHasSavedPos = FALSE; + nSavedPos = 0; + nPosInBuffer = nBufferLength = -1; +} + +/************************************************************************/ +/* VSIPDFFileStream() */ +/************************************************************************/ + +VSIPDFFileStream::VSIPDFFileStream(VSIPDFFileStream* poParent, + vsi_l_offset startA, GBool limitedA, + vsi_l_offset lengthA, Object *dictA): +#ifdef POPPLER_BASE_STREAM_HAS_TWO_ARGS + BaseStream(dictA, lengthA) +#else + BaseStream(dictA) +#endif +{ + this->poParent = poParent; + poFilename = poParent->poFilename; + f = poParent->f; + nStart = startA; + bLimited = limitedA; + nLength = lengthA; + nCurrentPos = -1; + bHasSavedPos = FALSE; + nSavedPos = 0; + nPosInBuffer = nBufferLength = -1; +} + +/************************************************************************/ +/* ~VSIPDFFileStream() */ +/************************************************************************/ + +VSIPDFFileStream::~VSIPDFFileStream() +{ + close(); + if (poParent == NULL) + { + delete poFilename; + if (f) + VSIFCloseL(f); + } +} + +/************************************************************************/ +/* copy() */ +/************************************************************************/ + +#ifdef POPPLER_0_23_OR_LATER +BaseStream* VSIPDFFileStream::copy() +{ + return new VSIPDFFileStream(poParent, nStart, bLimited, + nLength, &dict); +} +#endif + +/************************************************************************/ +/* makeSubStream() */ +/************************************************************************/ + +Stream *VSIPDFFileStream::makeSubStream(makeSubStream_offset_type startA, GBool limitedA, + makeSubStream_offset_type lengthA, Object *dictA) +{ + return new VSIPDFFileStream(this, + startA, limitedA, + lengthA, dictA); +} + +/************************************************************************/ +/* getPos() */ +/************************************************************************/ + +getPos_ret_type VSIPDFFileStream::getPos() +{ + return (getPos_ret_type) nCurrentPos; +} + +/************************************************************************/ +/* getStart() */ +/************************************************************************/ + + +getStart_ret_type VSIPDFFileStream::getStart() +{ + return (getStart_ret_type) nStart; +} + +/************************************************************************/ +/* getKind() */ +/************************************************************************/ + +StreamKind VSIPDFFileStream::getKind() +{ + return strFile; +} + +/************************************************************************/ +/* getFileName() */ +/************************************************************************/ + +GooString *VSIPDFFileStream::getFileName() +{ + return poFilename; +} + +/************************************************************************/ +/* FillBuffer() */ +/************************************************************************/ + +int VSIPDFFileStream::FillBuffer() +{ + if (nBufferLength == 0) + return FALSE; + if (nBufferLength != -1 && nBufferLength < BUFFER_SIZE) + return FALSE; + + nPosInBuffer = 0; + int nToRead; + if (!bLimited) + nToRead = BUFFER_SIZE; + else if (nCurrentPos + BUFFER_SIZE > nStart + nLength) + nToRead = (int)(nStart + nLength - nCurrentPos); + else + nToRead = BUFFER_SIZE; + if( nToRead < 0 ) + return FALSE; + nBufferLength = (int) VSIFReadL(abyBuffer, 1, nToRead, f); + if (nBufferLength == 0) + return FALSE; + + // Since we now report a non-zero length (as BaseStream::length member), + // PDFDoc::getPage() can go to the linearized mode if the file is linearized, + // and thus create a pageCache. If so, in PDFDoc::~PDFDoc(), + // if pageCache is not null, it would try to access the stream (str) through + // getPageCount(), but we have just freed and nullify str before in PDFFreeDoc(). + // So make as if the file is not linearized to avoid those issues... + // All this is due to our attempt of avoiding cross-heap issues with allocation + // and liberation of VSIPDFFileStream as PDFDoc::str member. + if( nCurrentPos <= 0 ) + { + for(int i=0;i= nStart + nLength) + return EOF; + if (VSIFReadL(&chRead, 1, 1, f) == 0) + return EOF; +#else + if (nPosInBuffer == nBufferLength) + { + if (!FillBuffer() || nPosInBuffer >= nBufferLength) + return EOF; + } + + GByte chRead = abyBuffer[nPosInBuffer]; + nPosInBuffer ++; +#endif + nCurrentPos ++; + return chRead; +} + +/************************************************************************/ +/* getUnfilteredChar() */ +/************************************************************************/ + +int VSIPDFFileStream::getUnfilteredChar () +{ + return getChar(); +} + +/************************************************************************/ +/* lookChar() */ +/************************************************************************/ + +int VSIPDFFileStream::lookChar() +{ +#ifdef unoptimized_version + int nPosBefore = nCurrentPos; + int chRead = getChar(); + if (chRead == EOF) + return EOF; + VSIFSeekL(f, nCurrentPos = nPosBefore, SEEK_SET); + return chRead; +#else + int chRead = getChar(); + if (chRead == EOF) + return EOF; + nPosInBuffer --; + nCurrentPos --; + return chRead; +#endif +} + +/************************************************************************/ +/* reset() */ +/************************************************************************/ + +void VSIPDFFileStream::reset() +{ + nSavedPos = VSIFTellL(f); + bHasSavedPos = TRUE; + VSIFSeekL(f, nCurrentPos = nStart, SEEK_SET); + nPosInBuffer = nBufferLength = -1; +} + +/************************************************************************/ +/* unfilteredReset() */ +/************************************************************************/ + +void VSIPDFFileStream::unfilteredReset () +{ + reset(); +} + +/************************************************************************/ +/* close() */ +/************************************************************************/ + +void VSIPDFFileStream::close() +{ + if (bHasSavedPos) + VSIFSeekL(f, nCurrentPos = nSavedPos, SEEK_SET); + bHasSavedPos = FALSE; + nSavedPos = 0; +} + +/************************************************************************/ +/* setPos() */ +/************************************************************************/ + +void VSIPDFFileStream::setPos(setPos_offset_type pos, int dir) +{ + if (dir >= 0) + { + VSIFSeekL(f, nCurrentPos = pos, SEEK_SET); + } + else + { + if (bLimited == gFalse) + { + VSIFSeekL(f, 0, SEEK_END); + } + else + { + VSIFSeekL(f, nStart + nLength, SEEK_SET); + } + vsi_l_offset size = VSIFTellL(f); + vsi_l_offset newpos = (vsi_l_offset) pos; + if (newpos > size) + newpos = size; + VSIFSeekL(f, nCurrentPos = size - newpos, SEEK_SET); + } + nPosInBuffer = nBufferLength = -1; +} + +/************************************************************************/ +/* moveStart() */ +/************************************************************************/ + +void VSIPDFFileStream::moveStart(moveStart_delta_type delta) +{ + nStart += delta; + VSIFSeekL(f, nCurrentPos = nStart, SEEK_SET); + nPosInBuffer = nBufferLength = -1; +} + +/************************************************************************/ +/* hasGetChars() */ +/************************************************************************/ + +GBool VSIPDFFileStream::hasGetChars() +{ + return true; +} + +/************************************************************************/ +/* getChars() */ +/************************************************************************/ + +int VSIPDFFileStream::getChars(int nChars, Guchar *buffer) +{ + int nRead = 0; + while (nRead < nChars) + { + int nToRead = nChars - nRead; + if (nPosInBuffer == nBufferLength) + { + if (!bLimited && nToRead > BUFFER_SIZE) + { + int nJustRead = (int) VSIFReadL(buffer + nRead, 1, nToRead, f); + nPosInBuffer = nBufferLength = -1; + nCurrentPos += nJustRead; + nRead += nJustRead; + break; + } + else if (!FillBuffer() || nPosInBuffer >= nBufferLength) + break; + } + if( nToRead > nBufferLength - nPosInBuffer ) + nToRead = nBufferLength - nPosInBuffer; + + memcpy( buffer + nRead, abyBuffer + nPosInBuffer, nToRead ); + nPosInBuffer += nToRead; + nCurrentPos += nToRead; + nRead += nToRead; + } + return nRead; +} + +#endif diff --git a/bazaar/plugin/gdal/frmts/pdf/pdfio.h b/bazaar/plugin/gdal/frmts/pdf/pdfio.h new file mode 100644 index 000000000..f649a1b54 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/pdfio.h @@ -0,0 +1,118 @@ +/****************************************************************************** + * $Id: pdfio.h 28438 2015-02-07 21:47:35Z rouault $ + * + * Project: PDF driver + * Purpose: GDALDataset driver for PDF dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef PDFIO_H_INCLUDED +#define PDFIO_H_INCLUDED + +#include "cpl_vsi_virtual.h" + + +/* begin of poppler xpdf includes */ +#include +#include +/* end of poppler xpdf includes */ + +/************************************************************************/ +/* VSIPDFFileStream */ +/************************************************************************/ + +#define BUFFER_SIZE 1024 + + +#ifdef POPPLER_0_23_OR_LATER +#define getPos_ret_type Goffset +#define getStart_ret_type Goffset +#define makeSubStream_offset_type Goffset +#define setPos_offset_type Goffset +#define moveStart_delta_type Goffset +#else +#define getPos_ret_type int +#define getStart_ret_type Guint +#define makeSubStream_offset_type Guint +#define setPos_offset_type Guint +#define moveStart_delta_type int +#endif + + +class VSIPDFFileStream: public BaseStream +{ + public: + VSIPDFFileStream(VSILFILE* f, const char* pszFilename, Object *dictA); + VSIPDFFileStream(VSIPDFFileStream* poParent, + vsi_l_offset startA, GBool limitedA, + vsi_l_offset lengthA, Object *dictA); + virtual ~VSIPDFFileStream(); + +#ifdef POPPLER_0_23_OR_LATER + virtual BaseStream* copy(); +#endif + + virtual Stream * makeSubStream(makeSubStream_offset_type startA, GBool limitedA, + makeSubStream_offset_type lengthA, Object *dictA); + virtual getPos_ret_type getPos(); + virtual getStart_ret_type getStart(); + + virtual void setPos(setPos_offset_type pos, int dir = 0); + virtual void moveStart(moveStart_delta_type delta); + + virtual StreamKind getKind(); + virtual GooString *getFileName(); + + virtual int getChar(); + virtual int getUnfilteredChar (); + virtual int lookChar(); + + virtual void reset(); + virtual void unfilteredReset (); + virtual void close(); + + private: + /* Added in poppler 0.15.0 */ + virtual GBool hasGetChars(); + virtual int getChars(int nChars, Guchar *buffer); + + VSIPDFFileStream *poParent; + GooString *poFilename; + VSILFILE *f; + vsi_l_offset nStart; + int bLimited; + vsi_l_offset nLength; + + vsi_l_offset nCurrentPos; + int bHasSavedPos; + vsi_l_offset nSavedPos; + + GByte abyBuffer[BUFFER_SIZE]; + int nPosInBuffer; + int nBufferLength; + + int FillBuffer(); +}; + +#endif // PDFIO_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/pdf/pdfobject.cpp b/bazaar/plugin/gdal/frmts/pdf/pdfobject.cpp new file mode 100644 index 000000000..58995a4a6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/pdfobject.cpp @@ -0,0 +1,1832 @@ +/****************************************************************************** + * $Id: pdfobject.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: PDF driver + * Purpose: GDALDataset driver for PDF dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pdf.h" + +#include +#include "pdfobject.h" + +CPL_CVSID("$Id: pdfobject.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* ROUND_TO_INT_IF_CLOSE() */ +/************************************************************************/ + +double ROUND_TO_INT_IF_CLOSE(double x, double eps) +{ + if( eps == 0.0 ) + eps = fabs(x) < 1 ? 1e-10 : 1e-8; + int nClosestInt = (int)floor(x + 0.5); + if ( fabs(x - nClosestInt) < eps ) + return nClosestInt; + else + return x; +} + +/************************************************************************/ +/* GDALPDFGetPDFString() */ +/************************************************************************/ + +static CPLString GDALPDFGetPDFString(const char* pszStr) +{ + GByte* pabyData = (GByte*)pszStr; + int i; + GByte ch; + for(i=0;(ch = pabyData[i]) != '\0';i++) + { + if (ch < 32 || ch > 127 || + ch == '(' || ch == ')' || + ch == '\\' || ch == '%' || ch == '#') + break; + } + CPLString osStr; + if (ch == 0) + { + osStr = "("; + osStr += pszStr; + osStr += ")"; + return osStr; + } + + wchar_t* pwszDest = CPLRecodeToWChar( pszStr, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + osStr = "= 0x10000 /* && pwszDest[i] <= 0x10FFFF */) + { + /* Generate UTF-16 surrogate pairs (on Windows, CPLRecodeToWChar does it for us) */ + int nHeadSurrogate = ((pwszDest[i] - 0x10000) >> 10) | 0xd800; + int nTrailSurrogate = ((pwszDest[i] - 0x10000) & 0x3ff) | 0xdc00; + osStr += CPLSPrintf("%02X", (nHeadSurrogate >> 8) & 0xff); + osStr += CPLSPrintf("%02X", (nHeadSurrogate) & 0xff); + osStr += CPLSPrintf("%02X", (nTrailSurrogate >> 8) & 0xff); + osStr += CPLSPrintf("%02X", (nTrailSurrogate) & 0xff); + } + else +#endif + { + osStr += CPLSPrintf("%02X", (int)(pwszDest[i] >> 8) & 0xff); + osStr += CPLSPrintf("%02X", (int)(pwszDest[i]) & 0xff); + } + } + osStr += ">"; + CPLFree(pwszDest); + return osStr; +} + +/************************************************************************/ +/* GDALPDFGetPDFName() */ +/************************************************************************/ + +static CPLString GDALPDFGetPDFName(const char* pszStr) +{ + GByte* pabyData = (GByte*)pszStr; + int i; + GByte ch; + CPLString osStr; + for(i=0;(ch = pabyData[i]) != '\0';i++) + { + if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '-')) + osStr += '_'; + else + osStr += ch; + } + return osStr; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFObject */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ~GDALPDFObject() */ +/************************************************************************/ + +GDALPDFObject::~GDALPDFObject() +{ +} + +/************************************************************************/ +/* LookupObject() */ +/************************************************************************/ + +GDALPDFObject* GDALPDFObject::LookupObject(const char* pszPath) +{ + if( GetType() != PDFObjectType_Dictionary ) + return NULL; + return GetDictionary()->LookupObject(pszPath); +} + +/************************************************************************/ +/* GetTypeName() */ +/************************************************************************/ + +const char* GDALPDFObject::GetTypeName() +{ + switch(GetType()) + { + case PDFObjectType_Unknown: return GetTypeNameNative(); + case PDFObjectType_Null: return "null"; + case PDFObjectType_Bool: return "bool"; + case PDFObjectType_Int: return "int"; + case PDFObjectType_Real: return "real"; + case PDFObjectType_String: return "string"; + case PDFObjectType_Name: return "name"; + case PDFObjectType_Array: return "array"; + case PDFObjectType_Dictionary: return "dictionary"; + default: return GetTypeNameNative(); + } +} + +/************************************************************************/ +/* Serialize() */ +/************************************************************************/ + +void GDALPDFObject::Serialize(CPLString& osStr) +{ + int nRefNum = GetRefNum(); + if( nRefNum ) + { + int nRefGen = GetRefGen(); + osStr.append(CPLSPrintf("%d %d R", nRefNum, nRefGen)); + return; + } + + switch(GetType()) + { + case PDFObjectType_Null: osStr.append("null"); return; + case PDFObjectType_Bool: osStr.append(GetBool() ? "true": "false"); return; + case PDFObjectType_Int: osStr.append(CPLSPrintf("%d", GetInt())); return; + case PDFObjectType_Real: + { + char szReal[512]; + double dfRealNonRounded = GetReal(); + double dfReal = ROUND_TO_INT_IF_CLOSE(dfRealNonRounded); + if (dfReal == (double)(GIntBig)dfReal) + sprintf(szReal, CPL_FRMT_GIB, (GIntBig)dfReal); + else if (CanRepresentRealAsString()) + { + /* Used for OGC BP numeric values */ + CPLsprintf(szReal, "(%.16g)", dfReal); + } + else + { + CPLsprintf(szReal, "%.16f", dfReal); + + /* Remove non significant trailing zeroes */ + char* pszDot = strchr(szReal, '.'); + if (pszDot) + { + int iDot = (int)(pszDot - szReal); + int nLen = (int)strlen(szReal); + for(int i=nLen-1; i > iDot; i --) + { + if (szReal[i] == '0') + szReal[i] = '\0'; + else + break; + } + } + } + osStr.append(szReal); + return; + } + case PDFObjectType_String: osStr.append(GDALPDFGetPDFString(GetString())); return; + case PDFObjectType_Name: osStr.append("/"); osStr.append(GDALPDFGetPDFName(GetName())); return; + case PDFObjectType_Array: GetArray()->Serialize(osStr); return; + case PDFObjectType_Dictionary: GetDictionary()->Serialize(osStr); return; + case PDFObjectType_Unknown: + default: fprintf(stderr, "Serializing unknown object !\n"); return; + } +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +GDALPDFObjectRW* GDALPDFObject::Clone() +{ + int nRefNum = GetRefNum(); + if( nRefNum ) + { + int nRefGen = GetRefGen(); + return GDALPDFObjectRW::CreateIndirect(nRefNum, nRefGen); + } + + switch(GetType()) + { + case PDFObjectType_Null: return GDALPDFObjectRW::CreateNull(); + case PDFObjectType_Bool: return GDALPDFObjectRW::CreateBool(GetBool()); + case PDFObjectType_Int: return GDALPDFObjectRW::CreateInt(GetInt()); + case PDFObjectType_Real: return GDALPDFObjectRW::CreateReal(GetReal()); + case PDFObjectType_String: return GDALPDFObjectRW::CreateString(GetString()); + case PDFObjectType_Name: return GDALPDFObjectRW::CreateName(GetName()); + case PDFObjectType_Array: return GDALPDFObjectRW::CreateArray(GetArray()->Clone()); + case PDFObjectType_Dictionary: return GDALPDFObjectRW::CreateDictionary(GetDictionary()->Clone()); + case PDFObjectType_Unknown: + default: fprintf(stderr, "Cloning unknown object !\n"); return NULL; + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFDictionary */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ~GDALPDFDictionary() */ +/************************************************************************/ + +GDALPDFDictionary::~GDALPDFDictionary() +{ +} + +/************************************************************************/ +/* LookupObject() */ +/************************************************************************/ + +GDALPDFObject* GDALPDFDictionary::LookupObject(const char* pszPath) +{ + GDALPDFObject* poCurObj = NULL; + char** papszTokens = CSLTokenizeString2(pszPath, ".", 0); + for(int i=0; papszTokens[i] != NULL; i++) + { + int iElt = -1; + char* pszBracket = strchr(papszTokens[i], '['); + if( pszBracket != NULL ) + { + iElt = atoi(pszBracket + 1); + *pszBracket = '\0'; + } + + if( i == 0 ) + { + poCurObj = Get(papszTokens[i]); + } + else + { + if( poCurObj->GetType() != PDFObjectType_Dictionary ) + { + poCurObj = NULL; + break; + } + poCurObj = poCurObj->GetDictionary()->Get(papszTokens[i]); + } + + if( poCurObj == NULL ) + { + poCurObj = NULL; + break; + } + + if( iElt >= 0 ) + { + if( poCurObj->GetType() != PDFObjectType_Array ) + { + poCurObj = NULL; + break; + } + poCurObj = poCurObj->GetArray()->Get(iElt); + } + } + CSLDestroy(papszTokens); + return poCurObj; +} + +/************************************************************************/ +/* Serialize() */ +/************************************************************************/ + +void GDALPDFDictionary::Serialize(CPLString& osStr) +{ + osStr.append("<< "); + std::map& oMap = GetValues(); + std::map::iterator oIter = oMap.begin(); + std::map::iterator oEnd = oMap.end(); + for(;oIter != oEnd;++oIter) + { + const char* pszKey = oIter->first.c_str(); + GDALPDFObject* poObj = oIter->second; + osStr.append("/"); + osStr.append(pszKey); + osStr.append(" "); + poObj->Serialize(osStr); + osStr.append(" "); + } + osStr.append(">>"); +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +GDALPDFDictionaryRW* GDALPDFDictionary::Clone() +{ + GDALPDFDictionaryRW* poDict = new GDALPDFDictionaryRW(); + std::map& oMap = GetValues(); + std::map::iterator oIter = oMap.begin(); + std::map::iterator oEnd = oMap.end(); + for(;oIter != oEnd;++oIter) + { + const char* pszKey = oIter->first.c_str(); + GDALPDFObject* poObj = oIter->second; + poDict->Add(pszKey, poObj->Clone()); + } + return poDict; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFArray */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ~GDALPDFArray() */ +/************************************************************************/ + +GDALPDFArray::~GDALPDFArray() +{ +} + +/************************************************************************/ +/* Serialize() */ +/************************************************************************/ + +void GDALPDFArray::Serialize(CPLString& osStr) +{ + int nLength = GetLength(); + int i; + + osStr.append("[ "); + for(i=0;iSerialize(osStr); + osStr.append(" "); + } + osStr.append("]"); +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +GDALPDFArrayRW* GDALPDFArray::Clone() +{ + GDALPDFArrayRW* poArray = new GDALPDFArrayRW(); + int nLength = GetLength(); + int i; + for(i=0;iAdd(Get(i)->Clone()); + } + return poArray; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFStream */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ~GDALPDFStream() */ +/************************************************************************/ + +GDALPDFStream::~GDALPDFStream() +{ +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFObjectRW */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALPDFObjectRW() */ +/************************************************************************/ + +GDALPDFObjectRW::GDALPDFObjectRW(GDALPDFObjectType eType) +{ + m_eType = eType; + m_nVal = 0; + m_dfVal = 0.0; + //m_osVal; + m_poDict = NULL; + m_poArray = NULL; + m_nNum = 0; + m_nGen = 0; + m_bCanRepresentRealAsString = FALSE; +} + +/************************************************************************/ +/* ~GDALPDFObjectRW() */ +/************************************************************************/ + +GDALPDFObjectRW::~GDALPDFObjectRW() +{ + delete m_poDict; + delete m_poArray; +} + +/************************************************************************/ +/* CreateIndirect() */ +/************************************************************************/ + +GDALPDFObjectRW* GDALPDFObjectRW::CreateIndirect(int nNum, int nGen) +{ + GDALPDFObjectRW* poObj = new GDALPDFObjectRW(PDFObjectType_Unknown); + poObj->m_nNum = nNum; + poObj->m_nGen = nGen; + return poObj; +} + +/************************************************************************/ +/* CreateNull() */ +/************************************************************************/ + +GDALPDFObjectRW* GDALPDFObjectRW::CreateNull() +{ + return new GDALPDFObjectRW(PDFObjectType_Null); +} + +/************************************************************************/ +/* CreateBool() */ +/************************************************************************/ + +GDALPDFObjectRW* GDALPDFObjectRW::CreateBool(int bVal) +{ + GDALPDFObjectRW* poObj = new GDALPDFObjectRW(PDFObjectType_Bool); + poObj->m_nVal = bVal; + return poObj; +} + +/************************************************************************/ +/* CreateInt() */ +/************************************************************************/ + +GDALPDFObjectRW* GDALPDFObjectRW::CreateInt(int nVal) +{ + GDALPDFObjectRW* poObj = new GDALPDFObjectRW(PDFObjectType_Int); + poObj->m_nVal = nVal; + return poObj; +} + +/************************************************************************/ +/* CreateReal() */ +/************************************************************************/ + +GDALPDFObjectRW* GDALPDFObjectRW::CreateReal(double dfVal, + int bCanRepresentRealAsString) +{ + GDALPDFObjectRW* poObj = new GDALPDFObjectRW(PDFObjectType_Real); + poObj->m_dfVal = dfVal; + poObj->m_bCanRepresentRealAsString = bCanRepresentRealAsString; + return poObj; +} + +/************************************************************************/ +/* CreateString() */ +/************************************************************************/ + +GDALPDFObjectRW* GDALPDFObjectRW::CreateString(const char* pszStr) +{ + GDALPDFObjectRW* poObj = new GDALPDFObjectRW(PDFObjectType_String); + poObj->m_osVal = pszStr; + return poObj; +} + +/************************************************************************/ +/* CreateName() */ +/************************************************************************/ + +GDALPDFObjectRW* GDALPDFObjectRW::CreateName(const char* pszName) +{ + GDALPDFObjectRW* poObj = new GDALPDFObjectRW(PDFObjectType_Name); + poObj->m_osVal = pszName; + return poObj; +} + +/************************************************************************/ +/* CreateDictionary() */ +/************************************************************************/ + +GDALPDFObjectRW* GDALPDFObjectRW::CreateDictionary(GDALPDFDictionaryRW* poDict) +{ + CPLAssert(poDict); + GDALPDFObjectRW* poObj = new GDALPDFObjectRW(PDFObjectType_Dictionary); + poObj->m_poDict = poDict; + return poObj; +} + +/************************************************************************/ +/* CreateArray() */ +/************************************************************************/ + +GDALPDFObjectRW* GDALPDFObjectRW::CreateArray(GDALPDFArrayRW* poArray) +{ + CPLAssert(poArray); + GDALPDFObjectRW* poObj = new GDALPDFObjectRW(PDFObjectType_Array); + poObj->m_poArray = poArray; + return poObj; +} + +/************************************************************************/ +/* GetTypeNameNative() */ +/************************************************************************/ + +const char* GDALPDFObjectRW::GetTypeNameNative() +{ + fprintf(stderr, "Should not go here"); + return ""; +} + +/************************************************************************/ +/* GetType() */ +/************************************************************************/ + +GDALPDFObjectType GDALPDFObjectRW::GetType() +{ + return m_eType; +} + +/************************************************************************/ +/* GetBool() */ +/************************************************************************/ + +int GDALPDFObjectRW::GetBool() +{ + if (m_eType == PDFObjectType_Bool) + return m_nVal; + + return FALSE; +} + +/************************************************************************/ +/* GetInt() */ +/************************************************************************/ + +int GDALPDFObjectRW::GetInt() +{ + if (m_eType == PDFObjectType_Int) + return m_nVal; + + return 0; +} + +/************************************************************************/ +/* GetReal() */ +/************************************************************************/ + +double GDALPDFObjectRW::GetReal() +{ + return m_dfVal; +} + +/************************************************************************/ +/* GetString() */ +/************************************************************************/ + +const CPLString& GDALPDFObjectRW::GetString() +{ + return m_osVal; +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const CPLString& GDALPDFObjectRW::GetName() +{ + return m_osVal; +} + +/************************************************************************/ +/* GetDictionary() */ +/************************************************************************/ + +GDALPDFDictionary* GDALPDFObjectRW::GetDictionary() +{ + return m_poDict; +} + +/************************************************************************/ +/* GetArray() */ +/************************************************************************/ + +GDALPDFArray* GDALPDFObjectRW::GetArray() +{ + return m_poArray; +} + +/************************************************************************/ +/* GetStream() */ +/************************************************************************/ + +GDALPDFStream* GDALPDFObjectRW::GetStream() +{ + return NULL; +} + +/************************************************************************/ +/* GetRefNum() */ +/************************************************************************/ + +int GDALPDFObjectRW::GetRefNum() +{ + return m_nNum; +} + +/************************************************************************/ +/* GetRefGen() */ +/************************************************************************/ + +int GDALPDFObjectRW::GetRefGen() +{ + return m_nGen; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFDictionaryRW */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALPDFDictionaryRW() */ +/************************************************************************/ + +GDALPDFDictionaryRW::GDALPDFDictionaryRW() +{ +} + +/************************************************************************/ +/* ~GDALPDFDictionaryRW() */ +/************************************************************************/ + +GDALPDFDictionaryRW::~GDALPDFDictionaryRW() +{ + std::map::iterator oIter = m_map.begin(); + std::map::iterator oEnd = m_map.end(); + for(; oIter != oEnd; ++oIter) + delete oIter->second; +} + +/************************************************************************/ +/* Get() */ +/************************************************************************/ + +GDALPDFObject* GDALPDFDictionaryRW::Get(const char* pszKey) +{ + std::map::iterator oIter = m_map.find(pszKey); + if (oIter != m_map.end()) + return oIter->second; + return NULL; +} + +/************************************************************************/ +/* GetValues() */ +/************************************************************************/ + +std::map& GDALPDFDictionaryRW::GetValues() +{ + return m_map; +} + +/************************************************************************/ +/* Add() */ +/************************************************************************/ + +GDALPDFDictionaryRW& GDALPDFDictionaryRW::Add(const char* pszKey, GDALPDFObject* poVal) +{ + std::map::iterator oIter = m_map.find(pszKey); + if (oIter != m_map.end()) + { + delete oIter->second; + oIter->second = poVal; + } + else + m_map[pszKey] = poVal; + + return *this; +} + +/************************************************************************/ +/* Remove() */ +/************************************************************************/ + +GDALPDFDictionaryRW& GDALPDFDictionaryRW::Remove(const char* pszKey) +{ + std::map::iterator oIter = m_map.find(pszKey); + if (oIter != m_map.end()) + { + delete oIter->second; + m_map.erase(pszKey); + } + + return *this; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFArrayRW */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALPDFArrayRW() */ +/************************************************************************/ + +GDALPDFArrayRW::GDALPDFArrayRW() +{ +} + +/************************************************************************/ +/* ~GDALPDFArrayRW() */ +/************************************************************************/ + +GDALPDFArrayRW::~GDALPDFArrayRW() +{ + for(int i=0; i < (int)m_array.size(); i++) + delete m_array[i]; +} + +/************************************************************************/ +/* GetLength() */ +/************************************************************************/ + +int GDALPDFArrayRW::GetLength() +{ + return (int)m_array.size(); +} + +/************************************************************************/ +/* Get() */ +/************************************************************************/ + +GDALPDFObject* GDALPDFArrayRW::Get(int nIndex) +{ + if (nIndex < 0 || nIndex >= GetLength()) + return NULL; + return m_array[nIndex]; +} + +/************************************************************************/ +/* Add() */ +/************************************************************************/ + +GDALPDFArrayRW& GDALPDFArrayRW::Add(GDALPDFObject* poObj) +{ + m_array.push_back(poObj); + return *this; +} + +/************************************************************************/ +/* Add() */ +/************************************************************************/ + +GDALPDFArrayRW& GDALPDFArrayRW::Add(double* padfVal, int nCount, + int bCanRepresentRealAsString) +{ + for(int i=0;i m_map; + + public: + GDALPDFDictionaryPoppler(Dict* poDict) : m_poDict(poDict) {} + virtual ~GDALPDFDictionaryPoppler(); + + virtual GDALPDFObject* Get(const char* pszKey); + virtual std::map& GetValues(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFArrayPoppler */ +/* ==================================================================== */ +/************************************************************************/ + +class GDALPDFArrayPoppler : public GDALPDFArray +{ + private: + Array* m_poArray; + std::vector m_v; + + public: + GDALPDFArrayPoppler(Array* poArray) : m_poArray(poArray) {} + virtual ~GDALPDFArrayPoppler(); + + virtual int GetLength(); + virtual GDALPDFObject* Get(int nIndex); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFStreamPoppler */ +/* ==================================================================== */ +/************************************************************************/ + +class GDALPDFStreamPoppler : public GDALPDFStream +{ + private: + int m_nLength; + Stream* m_poStream; + + public: + GDALPDFStreamPoppler(Stream* poStream) : m_nLength(-1), m_poStream(poStream) {} + virtual ~GDALPDFStreamPoppler() {} + + virtual int GetLength(); + virtual char* GetBytes(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFObjectPoppler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ~GDALPDFObjectPoppler() */ +/************************************************************************/ + +GDALPDFObjectPoppler::~GDALPDFObjectPoppler() +{ + m_po->free(); + if (m_bDestroy) + delete m_po; + delete m_poDict; + delete m_poArray; + delete m_poStream; +} + +/************************************************************************/ +/* GetType() */ +/************************************************************************/ + +GDALPDFObjectType GDALPDFObjectPoppler::GetType() +{ + switch(m_po->getType()) + { + case objNull: return PDFObjectType_Null; + case objBool: return PDFObjectType_Bool; + case objInt: return PDFObjectType_Int; + case objReal: return PDFObjectType_Real; + case objString: return PDFObjectType_String; + case objName: return PDFObjectType_Name; + case objArray: return PDFObjectType_Array; + case objDict: return PDFObjectType_Dictionary; + case objStream: return PDFObjectType_Dictionary; + default: return PDFObjectType_Unknown; + } +} + +/************************************************************************/ +/* GetTypeNameNative() */ +/************************************************************************/ + +const char* GDALPDFObjectPoppler::GetTypeNameNative() +{ + return m_po->getTypeName(); +} + +/************************************************************************/ +/* GetBool() */ +/************************************************************************/ + +int GDALPDFObjectPoppler::GetBool() +{ + if (GetType() == PDFObjectType_Bool) + return m_po->getBool(); + else + return 0; +} + +/************************************************************************/ +/* GetInt() */ +/************************************************************************/ + +int GDALPDFObjectPoppler::GetInt() +{ + if (GetType() == PDFObjectType_Int) + return m_po->getInt(); + else + return 0; +} + +/************************************************************************/ +/* GetReal() */ +/************************************************************************/ + +double GDALPDFObjectPoppler::GetReal() +{ + if (GetType() == PDFObjectType_Real) + return m_po->getReal(); + else + return 0.0; +} + +/************************************************************************/ +/* GDALPDFPopplerGetUTF8() */ +/************************************************************************/ + +static CPLString GDALPDFPopplerGetUTF8(GooString* poStr) +{ + GByte* pabySrc = (GByte*)poStr->getCString(); + int nLen = poStr->getLength(); + int bBEUnicodeMarker = nLen > 2 && pabySrc[0] == 0xFF && pabySrc[1] == 0xFE; + if (!poStr->hasUnicodeMarker() && !bBEUnicodeMarker) + { + const char* pszStr = poStr->getCString(); + if (CPLIsUTF8(pszStr, -1)) + return pszStr; + else + { + char* pszUTF8 = CPLRecode( pszStr, CPL_ENC_ISO8859_1, CPL_ENC_UTF8 ); + CPLString osRet = pszUTF8; + CPLFree(pszUTF8); + return osRet; + } + } + + /* This is UTF-16 content */ + pabySrc += 2; + nLen = (nLen - 2) / 2; + wchar_t *pwszSource = new wchar_t[nLen + 1]; + int j = 0; + for(int i=0; i= 0xD800 && pwszSource[j] <= 0xDBFF && i + 1 < nLen) + { + /* should be in the range 0xDC00... 0xDFFF */ + wchar_t nTrailSurrogate; + if (!bBEUnicodeMarker) + nTrailSurrogate = (pabySrc[2 * (i+1)] << 8) + pabySrc[2 * (i+1) + 1]; + else + nTrailSurrogate = (pabySrc[2 * (i+1) + 1] << 8) + pabySrc[2 * (i+1)]; + if (nTrailSurrogate >= 0xDC00 && nTrailSurrogate <= 0xDFFF) + { + pwszSource[j] = ((pwszSource[j] - 0xD800) << 10) + (nTrailSurrogate - 0xDC00) + 0x10000; + i++; + } + } +#endif + } + pwszSource[j] = 0; + + char* pszUTF8 = CPLRecodeFromWChar( pwszSource, CPL_ENC_UCS2, CPL_ENC_UTF8 ); + delete[] pwszSource; + CPLString osStrUTF8(pszUTF8); + CPLFree(pszUTF8); + return osStrUTF8; +} + +/************************************************************************/ +/* GetString() */ +/************************************************************************/ + +const CPLString& GDALPDFObjectPoppler::GetString() +{ + if (GetType() == PDFObjectType_String) + return (osStr = GDALPDFPopplerGetUTF8(m_po->getString())); + else + return (osStr = ""); +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const CPLString& GDALPDFObjectPoppler::GetName() +{ + if (GetType() == PDFObjectType_Name) + return (osStr = m_po->getName()); + else + return (osStr = ""); +} + +/************************************************************************/ +/* GetDictionary() */ +/************************************************************************/ + +GDALPDFDictionary* GDALPDFObjectPoppler::GetDictionary() +{ + if (GetType() != PDFObjectType_Dictionary) + return NULL; + + if (m_poDict) + return m_poDict; + + Dict* poDict = (m_po->getType() == objStream) ? m_po->getStream()->getDict() : m_po->getDict(); + if (poDict == NULL) + return NULL; + m_poDict = new GDALPDFDictionaryPoppler(poDict); + return m_poDict; +} + +/************************************************************************/ +/* GetArray() */ +/************************************************************************/ + +GDALPDFArray* GDALPDFObjectPoppler::GetArray() +{ + if (GetType() != PDFObjectType_Array) + return NULL; + + if (m_poArray) + return m_poArray; + + Array* poArray = m_po->getArray(); + if (poArray == NULL) + return NULL; + m_poArray = new GDALPDFArrayPoppler(poArray); + return m_poArray; +} + +/************************************************************************/ +/* GetStream() */ +/************************************************************************/ + +GDALPDFStream* GDALPDFObjectPoppler::GetStream() +{ + if (m_po->getType() != objStream) + return NULL; + + if (m_poStream) + return m_poStream; + m_poStream = new GDALPDFStreamPoppler(m_po->getStream()); + return m_poStream; +} + +/************************************************************************/ +/* SetRefNumAndGen() */ +/************************************************************************/ + +void GDALPDFObjectPoppler::SetRefNumAndGen(int nNum, int nGen) +{ + m_nRefNum = nNum; + m_nRefGen = nGen; +} + +/************************************************************************/ +/* GetRefNum() */ +/************************************************************************/ + +int GDALPDFObjectPoppler::GetRefNum() +{ + return m_nRefNum; +} + +/************************************************************************/ +/* GetRefGen() */ +/************************************************************************/ + +int GDALPDFObjectPoppler::GetRefGen() +{ + return m_nRefGen; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFDictionaryPoppler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ~GDALPDFDictionaryPoppler() */ +/************************************************************************/ + +GDALPDFDictionaryPoppler::~GDALPDFDictionaryPoppler() +{ + std::map::iterator oIter = m_map.begin(); + std::map::iterator oEnd = m_map.end(); + for(; oIter != oEnd; ++oIter) + delete oIter->second; +} + +/************************************************************************/ +/* Get() */ +/************************************************************************/ + +GDALPDFObject* GDALPDFDictionaryPoppler::Get(const char* pszKey) +{ + std::map::iterator oIter = m_map.find(pszKey); + if (oIter != m_map.end()) + return oIter->second; + + Object* po = new Object; + if (m_poDict->lookupNF((char*)pszKey, po) && !po->isNull()) + { + int nRefNum = 0, nRefGen = 0; + if( po->isRef()) + { + nRefNum = po->getRefNum(); + nRefGen = po->getRefGen(); + } + if( !po->isRef() || (m_poDict->lookup((char*)pszKey, po) && !po->isNull()) ) + { + GDALPDFObjectPoppler* poObj = new GDALPDFObjectPoppler(po, TRUE); + poObj->SetRefNumAndGen(nRefNum, nRefGen); + m_map[pszKey] = poObj; + return poObj; + } + else + { + delete po; + return NULL; + } + } + else + { + delete po; + return NULL; + } +} + +/************************************************************************/ +/* GetValues() */ +/************************************************************************/ + +std::map& GDALPDFDictionaryPoppler::GetValues() +{ + int i = 0; + int nLength = m_poDict->getLength(); + for(i=0;igetKey(i); + Get(pszKey); + } + return m_map; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFArrayPoppler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALPDFCreateArray() */ +/************************************************************************/ + +GDALPDFArray* GDALPDFCreateArray(Array* array) +{ + return new GDALPDFArrayPoppler(array); +} + +/************************************************************************/ +/* ~GDALPDFArrayPoppler() */ +/************************************************************************/ + +GDALPDFArrayPoppler::~GDALPDFArrayPoppler() +{ + for(int i=0;i<(int)m_v.size();i++) + { + delete m_v[i]; + } +} + +/************************************************************************/ +/* GetLength() */ +/************************************************************************/ + +int GDALPDFArrayPoppler::GetLength() +{ + return m_poArray->getLength(); +} + +/************************************************************************/ +/* Get() */ +/************************************************************************/ + +GDALPDFObject* GDALPDFArrayPoppler::Get(int nIndex) +{ + if (nIndex < 0 || nIndex >= GetLength()) + return NULL; + + int nOldSize = (int)m_v.size(); + if (nIndex >= nOldSize) + { + m_v.resize(nIndex+1); + for(int i=nOldSize;i<=nIndex;i++) + { + m_v[i] = NULL; + } + } + + if (m_v[nIndex] != NULL) + return m_v[nIndex]; + + Object* po = new Object; + if (m_poArray->getNF(nIndex, po)) + { + int nRefNum = 0, nRefGen = 0; + if( po->isRef()) + { + nRefNum = po->getRefNum(); + nRefGen = po->getRefGen(); + } + if( !po->isRef() || (m_poArray->get(nIndex, po)) ) + { + GDALPDFObjectPoppler* poObj = new GDALPDFObjectPoppler(po, TRUE); + poObj->SetRefNumAndGen(nRefNum, nRefGen); + m_v[nIndex] = poObj; + return poObj; + } + else + { + delete po; + return NULL; + } + } + else + { + delete po; + return NULL; + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFStreamPoppler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GetLength() */ +/************************************************************************/ + +int GDALPDFStreamPoppler::GetLength() +{ + if (m_nLength >= 0) + return m_nLength; + + m_poStream->reset(); + m_nLength = 0; + while(m_poStream->getChar() != EOF) + m_nLength ++; + return m_nLength; +} + +/************************************************************************/ +/* GetBytes() */ +/************************************************************************/ + +char* GDALPDFStreamPoppler::GetBytes() +{ + /* fillGooString() available in poppler >= 0.16.0 */ +#ifdef POPPLER_BASE_STREAM_HAS_TWO_ARGS + GooString* gstr = new GooString(); + m_poStream->fillGooString(gstr); + + if( gstr->getLength() ) + { + m_nLength = gstr->getLength(); + char* pszContent = (char*) VSIMalloc(m_nLength + 1); + if (!pszContent) + return NULL; + memcpy(pszContent, gstr->getCString(), m_nLength); + pszContent[m_nLength] = '\0'; + delete gstr; + return pszContent; + } + else + { + delete gstr; + return NULL; + } +#else + int i; + int nLengthAlloc = 0; + char* pszContent = NULL; + if( m_nLength >= 0 ) + { + pszContent = (char*) VSIMalloc(m_nLength + 1); + if (!pszContent) + return NULL; + nLengthAlloc = m_nLength; + } + m_poStream->reset(); + for(i = 0; ; ++i ) + { + int nVal = m_poStream->getChar(); + if (nVal == EOF) + break; + if( i >= nLengthAlloc ) + { + nLengthAlloc = 32 + nLengthAlloc + nLengthAlloc / 3; + char* pszContentNew = (char*) VSIRealloc(pszContent, nLengthAlloc + 1); + if( pszContentNew == NULL ) + { + CPLFree(pszContent); + m_nLength = 0; + return NULL; + } + pszContent = pszContentNew; + } + pszContent[i] = (GByte)nVal; + } + m_nLength = i; + pszContent[i] = '\0'; + return pszContent; +#endif +} + +#endif // HAVE_POPPLER + +#ifdef HAVE_PODOFO + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFDictionaryPodofo */ +/* ==================================================================== */ +/************************************************************************/ + +class GDALPDFDictionaryPodofo: public GDALPDFDictionary +{ + private: + PoDoFo::PdfDictionary* m_poDict; + PoDoFo::PdfVecObjects& m_poObjects; + std::map m_map; + + public: + GDALPDFDictionaryPodofo(PoDoFo::PdfDictionary* poDict, PoDoFo::PdfVecObjects& poObjects) : m_poDict(poDict), m_poObjects(poObjects) {} + virtual ~GDALPDFDictionaryPodofo(); + + virtual GDALPDFObject* Get(const char* pszKey); + virtual std::map& GetValues(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFArrayPodofo */ +/* ==================================================================== */ +/************************************************************************/ + +class GDALPDFArrayPodofo : public GDALPDFArray +{ + private: + PoDoFo::PdfArray* m_poArray; + PoDoFo::PdfVecObjects& m_poObjects; + std::vector m_v; + + public: + GDALPDFArrayPodofo(PoDoFo::PdfArray* poArray, PoDoFo::PdfVecObjects& poObjects) : m_poArray(poArray), m_poObjects(poObjects) {} + virtual ~GDALPDFArrayPodofo(); + + virtual int GetLength(); + virtual GDALPDFObject* Get(int nIndex); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFStreamPodofo */ +/* ==================================================================== */ +/************************************************************************/ + +class GDALPDFStreamPodofo : public GDALPDFStream +{ + private: + PoDoFo::PdfMemStream* m_pStream; + + public: + GDALPDFStreamPodofo(PoDoFo::PdfMemStream* pStream) : m_pStream(pStream) { } + virtual ~GDALPDFStreamPodofo() {} + + virtual int GetLength(); + virtual char* GetBytes(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFObjectPodofo */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALPDFObjectPodofo() */ +/************************************************************************/ + +GDALPDFObjectPodofo::GDALPDFObjectPodofo(PoDoFo::PdfObject* po, PoDoFo::PdfVecObjects& poObjects) : + m_po(po), m_poObjects(poObjects), m_poDict(NULL), m_poArray(NULL), m_poStream(NULL) +{ + try + { + if (m_po->GetDataType() == PoDoFo::ePdfDataType_Reference) + { + PoDoFo::PdfObject* poObj = m_poObjects.GetObject(m_po->GetReference()); + if (poObj) + m_po = poObj; + } + } + catch(PoDoFo::PdfError& oError) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what()); + } +} + +/************************************************************************/ +/* ~GDALPDFObjectPodofo() */ +/************************************************************************/ + +GDALPDFObjectPodofo::~GDALPDFObjectPodofo() +{ + delete m_poDict; + delete m_poArray; + delete m_poStream; +} + +/************************************************************************/ +/* GetType() */ +/************************************************************************/ + +GDALPDFObjectType GDALPDFObjectPodofo::GetType() +{ + try + { + switch(m_po->GetDataType()) + { + case PoDoFo::ePdfDataType_Null: return PDFObjectType_Null; + case PoDoFo::ePdfDataType_Bool: return PDFObjectType_Bool; + case PoDoFo::ePdfDataType_Number: return PDFObjectType_Int; + case PoDoFo::ePdfDataType_Real: return PDFObjectType_Real; + case PoDoFo::ePdfDataType_HexString: return PDFObjectType_String; + case PoDoFo::ePdfDataType_String: return PDFObjectType_String; + case PoDoFo::ePdfDataType_Name: return PDFObjectType_Name; + case PoDoFo::ePdfDataType_Array: return PDFObjectType_Array; + case PoDoFo::ePdfDataType_Dictionary: return PDFObjectType_Dictionary; + default: return PDFObjectType_Unknown; + } + } + catch(PoDoFo::PdfError& oError) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what()); + return PDFObjectType_Unknown; + } +} + +/************************************************************************/ +/* GetTypeNameNative() */ +/************************************************************************/ + +const char* GDALPDFObjectPodofo::GetTypeNameNative() +{ + try + { + return m_po->GetDataTypeString(); + } + catch(PoDoFo::PdfError& oError) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid PDF : %s", oError.what()); + return "unknown"; + } +} + +/************************************************************************/ +/* GetBool() */ +/************************************************************************/ + +int GDALPDFObjectPodofo::GetBool() +{ + if (m_po->GetDataType() == PoDoFo::ePdfDataType_Bool) + return m_po->GetBool(); + else + return 0; +} + +/************************************************************************/ +/* GetInt() */ +/************************************************************************/ + +int GDALPDFObjectPodofo::GetInt() +{ + if (m_po->GetDataType() == PoDoFo::ePdfDataType_Number) + return (int)m_po->GetNumber(); + else + return 0; +} + +/************************************************************************/ +/* GetReal() */ +/************************************************************************/ + +double GDALPDFObjectPodofo::GetReal() +{ + if (GetType() == PDFObjectType_Real) + return m_po->GetReal(); + else + return 0.0; +} + +/************************************************************************/ +/* GetString() */ +/************************************************************************/ + +const CPLString& GDALPDFObjectPodofo::GetString() +{ + if (GetType() == PDFObjectType_String) + return (osStr = m_po->GetString().GetStringUtf8()); + else + return (osStr = ""); +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const CPLString& GDALPDFObjectPodofo::GetName() +{ + if (GetType() == PDFObjectType_Name) + return (osStr = m_po->GetName().GetName()); + else + return (osStr = ""); +} + +/************************************************************************/ +/* GetDictionary() */ +/************************************************************************/ + +GDALPDFDictionary* GDALPDFObjectPodofo::GetDictionary() +{ + if (GetType() != PDFObjectType_Dictionary) + return NULL; + + if (m_poDict) + return m_poDict; + + m_poDict = new GDALPDFDictionaryPodofo(&m_po->GetDictionary(), m_poObjects); + return m_poDict; +} + +/************************************************************************/ +/* GetArray() */ +/************************************************************************/ + +GDALPDFArray* GDALPDFObjectPodofo::GetArray() +{ + if (GetType() != PDFObjectType_Array) + return NULL; + + if (m_poArray) + return m_poArray; + + m_poArray = new GDALPDFArrayPodofo(&m_po->GetArray(), m_poObjects); + return m_poArray; +} + +/************************************************************************/ +/* GetStream() */ +/************************************************************************/ + +GDALPDFStream* GDALPDFObjectPodofo::GetStream() +{ + try + { + if (!m_po->HasStream()) + return NULL; + } + catch(PoDoFo::PdfError& oError) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid object : %s", oError.what()); + return NULL; + } + catch(...) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid object"); + return NULL; + } + + if (m_poStream) + return m_poStream; + PoDoFo::PdfMemStream* pStream = NULL; + try + { + pStream = dynamic_cast(m_po->GetStream()); + pStream->Uncompress(); + } + catch( const PoDoFo::PdfError & e ) + { + e.PrintErrorMsg(); + pStream = NULL; + } + if (pStream) + { + m_poStream = new GDALPDFStreamPodofo(pStream); + return m_poStream; + } + else + return NULL; +} + +/************************************************************************/ +/* GetRefNum() */ +/************************************************************************/ + +int GDALPDFObjectPodofo::GetRefNum() +{ + return m_po->Reference().ObjectNumber(); +} + +/************************************************************************/ +/* GetRefGen() */ +/************************************************************************/ + +int GDALPDFObjectPodofo::GetRefGen() +{ + return m_po->Reference().GenerationNumber(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFDictionaryPodofo */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ~GDALPDFDictionaryPodofo() */ +/************************************************************************/ + +GDALPDFDictionaryPodofo::~GDALPDFDictionaryPodofo() +{ + std::map::iterator oIter = m_map.begin(); + std::map::iterator oEnd = m_map.end(); + for(; oIter != oEnd; ++oIter) + delete oIter->second; +} + +/************************************************************************/ +/* Get() */ +/************************************************************************/ + +GDALPDFObject* GDALPDFDictionaryPodofo::Get(const char* pszKey) +{ + std::map::iterator oIter = m_map.find(pszKey); + if (oIter != m_map.end()) + return oIter->second; + + PoDoFo::PdfObject* poVal = m_poDict->GetKey(PoDoFo::PdfName(pszKey)); + if (poVal) + { + GDALPDFObjectPodofo* poObj = new GDALPDFObjectPodofo(poVal, m_poObjects); + m_map[pszKey] = poObj; + return poObj; + } + else + { + return NULL; + } +} + +/************************************************************************/ +/* GetValues() */ +/************************************************************************/ + +std::map& GDALPDFDictionaryPodofo::GetValues() +{ + PoDoFo::TKeyMap::iterator oIter = m_poDict->GetKeys().begin(); + PoDoFo::TKeyMap::iterator oEnd = m_poDict->GetKeys().end(); + for( ; oIter != oEnd; ++oIter) + { + const char* pszKey = oIter->first.GetName().c_str(); + Get(pszKey); + } + + return m_map; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFArrayPodofo */ +/* ==================================================================== */ +/************************************************************************/ + +GDALPDFArrayPodofo::~GDALPDFArrayPodofo() +{ + for(int i=0;i<(int)m_v.size();i++) + { + delete m_v[i]; + } +} + +/************************************************************************/ +/* GetLength() */ +/************************************************************************/ + +int GDALPDFArrayPodofo::GetLength() +{ + return (int)m_poArray->GetSize(); +} + +/************************************************************************/ +/* Get() */ +/************************************************************************/ + +GDALPDFObject* GDALPDFArrayPodofo::Get(int nIndex) +{ + if (nIndex < 0 || nIndex >= GetLength()) + return NULL; + + int nOldSize = (int)m_v.size(); + if (nIndex >= nOldSize) + { + m_v.resize(nIndex+1); + for(int i=nOldSize;i<=nIndex;i++) + { + m_v[i] = NULL; + } + } + + if (m_v[nIndex] != NULL) + return m_v[nIndex]; + + PoDoFo::PdfObject& oVal = (*m_poArray)[nIndex]; + GDALPDFObjectPodofo* poObj = new GDALPDFObjectPodofo(&oVal, m_poObjects); + m_v[nIndex] = poObj; + return poObj; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPDFStreamPodofo */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GetLength() */ +/************************************************************************/ + +int GDALPDFStreamPodofo::GetLength() +{ + return (int)m_pStream->GetLength(); +} + +/************************************************************************/ +/* GetBytes() */ +/************************************************************************/ + +char* GDALPDFStreamPodofo::GetBytes() +{ + int nLength = GetLength(); + char* pszContent = (char*) VSIMalloc(nLength + 1); + if (!pszContent) + return NULL; + memcpy(pszContent, m_pStream->Get(), nLength); + pszContent[nLength] = '\0'; + return pszContent; +} + +#endif // HAVE_PODOFO diff --git a/bazaar/plugin/gdal/frmts/pdf/pdfobject.h b/bazaar/plugin/gdal/frmts/pdf/pdfobject.h new file mode 100644 index 000000000..448cab032 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/pdfobject.h @@ -0,0 +1,341 @@ +/****************************************************************************** + * $Id: pdfobject.h 27329 2014-05-14 16:24:17Z rouault $ + * + * Project: PDF driver + * Purpose: GDALDataset driver for PDF dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef PDFOBJECT_H_INCLUDED +#define PDFOBJECT_H_INCLUDED + +#include "cpl_string.h" +#include +#include + +#define DEFAULT_DPI (72.0) +#define USER_UNIT_IN_INCH (1.0 / DEFAULT_DPI) + +#ifdef HAVE_POPPLER + +/* begin of poppler xpdf includes */ +#include + +#define private public /* Ugly! Page::pageObj is private but we need it... */ +#include +#undef private + +#include + +#define private public /* Ugly! Catalog::optContent is private but we need it... */ +#include +#undef private + +#define private public /* Ugly! PDFDoc::str is private but we need it... */ +#include +#undef private + +#include +#include +#include +#include +#include +/* end of poppler xpdf includes */ + +#endif // HAVE_POPPLER + +#ifdef HAVE_PODOFO +/* + * Some Windows header defines a GetObject macro that + * shadows a GetObject() method in PoDoFo. This + * workaround is documented in the PoDoFo source. + */ +#ifdef GetObject +#undef GetObject +#endif + +#include "podofo.h" +#endif // HAVE_PODOFO + + +double ROUND_TO_INT_IF_CLOSE(double x, double eps = 0); + +typedef enum +{ + PDFObjectType_Unknown, + PDFObjectType_Null, + PDFObjectType_Bool, + PDFObjectType_Int, + PDFObjectType_Real, + PDFObjectType_String, + PDFObjectType_Name, + PDFObjectType_Array, + PDFObjectType_Dictionary +} GDALPDFObjectType; + +class GDALPDFDictionary; +class GDALPDFArray; +class GDALPDFStream; + +class GDALPDFObjectRW; +class GDALPDFDictionaryRW; +class GDALPDFArrayRW; + +class GDALPDFObject +{ + protected: + virtual const char* GetTypeNameNative() = 0; + + public: + virtual ~GDALPDFObject(); + + virtual GDALPDFObjectType GetType() = 0; + virtual const char* GetTypeName(); + virtual int GetBool() = 0; + virtual int GetInt() = 0; + virtual double GetReal() = 0; + virtual int CanRepresentRealAsString() { return FALSE; } + virtual const CPLString& GetString() = 0; + virtual const CPLString& GetName() = 0; + virtual GDALPDFDictionary* GetDictionary() = 0; + virtual GDALPDFArray* GetArray() = 0; + virtual GDALPDFStream* GetStream() = 0; + virtual int GetRefNum() = 0; + virtual int GetRefGen() = 0; + + GDALPDFObject* LookupObject(const char* pszPath); + + void Serialize(CPLString& osStr); + CPLString Serialize() { CPLString osStr; Serialize(osStr); return osStr; } + GDALPDFObjectRW* Clone(); +}; + +class GDALPDFDictionary +{ + public: + virtual ~GDALPDFDictionary(); + + virtual GDALPDFObject* Get(const char* pszKey) = 0; + virtual std::map& GetValues() = 0; + + GDALPDFObject* LookupObject(const char* pszPath); + + void Serialize(CPLString& osStr); + CPLString Serialize() { CPLString osStr; Serialize(osStr); return osStr; } + GDALPDFDictionaryRW* Clone(); +}; + +class GDALPDFArray +{ + public: + virtual ~GDALPDFArray(); + + virtual int GetLength() = 0; + virtual GDALPDFObject* Get(int nIndex) = 0; + + void Serialize(CPLString& osStr); + CPLString Serialize() { CPLString osStr; Serialize(osStr); return osStr; } + GDALPDFArrayRW* Clone(); +}; + +class GDALPDFStream +{ + public: + virtual ~GDALPDFStream(); + + virtual int GetLength() = 0; + virtual char* GetBytes() = 0; +}; + +class GDALPDFObjectRW : public GDALPDFObject +{ + private: + GDALPDFObjectType m_eType; + int m_nVal; + double m_dfVal; + CPLString m_osVal; + GDALPDFDictionaryRW *m_poDict; + GDALPDFArrayRW *m_poArray; + int m_nNum; + int m_nGen; + int m_bCanRepresentRealAsString; + + GDALPDFObjectRW(GDALPDFObjectType eType); + + protected: + virtual const char* GetTypeNameNative(); + + public: + + static GDALPDFObjectRW* CreateIndirect(int nNum, int nGen); + static GDALPDFObjectRW* CreateNull(); + static GDALPDFObjectRW* CreateBool(int bVal); + static GDALPDFObjectRW* CreateInt(int nVal); + static GDALPDFObjectRW* CreateReal(double dfVal, int bCanRepresentRealAsString = FALSE); + static GDALPDFObjectRW* CreateString(const char* pszStr); + static GDALPDFObjectRW* CreateName(const char* pszName); + static GDALPDFObjectRW* CreateDictionary(GDALPDFDictionaryRW* poDict); + static GDALPDFObjectRW* CreateArray(GDALPDFArrayRW* poArray); + virtual ~GDALPDFObjectRW(); + + virtual GDALPDFObjectType GetType(); + virtual int GetBool(); + virtual int GetInt(); + virtual double GetReal(); + virtual int CanRepresentRealAsString() { return m_bCanRepresentRealAsString; } + virtual const CPLString& GetString(); + virtual const CPLString& GetName(); + virtual GDALPDFDictionary* GetDictionary(); + virtual GDALPDFArray* GetArray(); + virtual GDALPDFStream* GetStream(); + virtual int GetRefNum(); + virtual int GetRefGen(); +}; + +class GDALPDFDictionaryRW : public GDALPDFDictionary +{ + private: + std::map m_map; + + public: + GDALPDFDictionaryRW(); + virtual ~GDALPDFDictionaryRW(); + + virtual GDALPDFObject* Get(const char* pszKey); + virtual std::map& GetValues(); + + GDALPDFDictionaryRW& Add(const char* pszKey, GDALPDFObject* poVal); + GDALPDFDictionaryRW& Remove(const char* pszKey); + + GDALPDFDictionaryRW& Add(const char* pszKey, GDALPDFArrayRW* poArray) { return Add(pszKey, GDALPDFObjectRW::CreateArray(poArray)); } + GDALPDFDictionaryRW& Add(const char* pszKey, GDALPDFDictionaryRW* poDict) { return Add(pszKey, GDALPDFObjectRW::CreateDictionary(poDict)); } + GDALPDFDictionaryRW& Add(const char* pszKey, const char* pszVal) { return Add(pszKey, GDALPDFObjectRW::CreateString(pszVal)); } + GDALPDFDictionaryRW& Add(const char* pszKey, int nVal) { return Add(pszKey, GDALPDFObjectRW::CreateInt(nVal)); } + GDALPDFDictionaryRW& Add(const char* pszKey, double dfVal, int bCanRepresentRealAsString = FALSE) { return Add(pszKey, GDALPDFObjectRW::CreateReal(dfVal, bCanRepresentRealAsString)); } + GDALPDFDictionaryRW& Add(const char* pszKey, int nNum, int nGen) { return Add(pszKey, GDALPDFObjectRW::CreateIndirect(nNum, nGen)); } +}; + +class GDALPDFArrayRW : public GDALPDFArray +{ + private: + std::vector m_array; + + public: + GDALPDFArrayRW(); + virtual ~GDALPDFArrayRW(); + + virtual int GetLength(); + virtual GDALPDFObject* Get(int nIndex); + + GDALPDFArrayRW& Add(GDALPDFObject* poObj); + + GDALPDFArrayRW& Add(GDALPDFArrayRW* poArray) { return Add(GDALPDFObjectRW::CreateArray(poArray)); } + GDALPDFArrayRW& Add(GDALPDFDictionaryRW* poDict) { return Add(GDALPDFObjectRW::CreateDictionary(poDict)); } + GDALPDFArrayRW& Add(const char* pszVal) { return Add(GDALPDFObjectRW::CreateString(pszVal)); } + GDALPDFArrayRW& Add(int nVal) { return Add(GDALPDFObjectRW::CreateInt(nVal)); } + GDALPDFArrayRW& Add(double dfVal, int bCanRepresentRealAsString = FALSE) { return Add(GDALPDFObjectRW::CreateReal(dfVal, bCanRepresentRealAsString)); } + GDALPDFArrayRW& Add(double* padfVal, int nCount, int bCanRepresentRealAsString = FALSE); + GDALPDFArrayRW& Add(int nNum, int nGen) { return Add(GDALPDFObjectRW::CreateIndirect(nNum, nGen)); } +}; + +#ifdef HAVE_POPPLER + +class GDALPDFObjectPoppler : public GDALPDFObject +{ + private: + Object* m_po; + int m_bDestroy; + GDALPDFDictionary* m_poDict; + GDALPDFArray* m_poArray; + GDALPDFStream* m_poStream; + CPLString osStr; + int m_nRefNum; + int m_nRefGen; + + protected: + virtual const char* GetTypeNameNative(); + + public: + GDALPDFObjectPoppler(Object* po, int bDestroy) : + m_po(po), m_bDestroy(bDestroy), + m_poDict(NULL), m_poArray(NULL), m_poStream(NULL), + m_nRefNum(0), m_nRefGen(0) {} + + void SetRefNumAndGen(int nNum, int nGen); + + virtual ~GDALPDFObjectPoppler(); + + virtual GDALPDFObjectType GetType(); + virtual int GetBool(); + virtual int GetInt(); + virtual double GetReal(); + virtual const CPLString& GetString(); + virtual const CPLString& GetName(); + virtual GDALPDFDictionary* GetDictionary(); + virtual GDALPDFArray* GetArray(); + virtual GDALPDFStream* GetStream(); + virtual int GetRefNum(); + virtual int GetRefGen(); +}; + +GDALPDFArray* GDALPDFCreateArray(Array* array); + +#endif // HAVE_POPPLER + +#ifdef HAVE_PODOFO + +class GDALPDFObjectPodofo : public GDALPDFObject +{ + private: + PoDoFo::PdfObject* m_po; + PoDoFo::PdfVecObjects& m_poObjects; + GDALPDFDictionary* m_poDict; + GDALPDFArray* m_poArray; + GDALPDFStream* m_poStream; + CPLString osStr; + + protected: + virtual const char* GetTypeNameNative(); + + public: + GDALPDFObjectPodofo(PoDoFo::PdfObject* po, PoDoFo::PdfVecObjects& poObjects); + + virtual ~GDALPDFObjectPodofo(); + + virtual GDALPDFObjectType GetType(); + virtual int GetBool(); + virtual int GetInt(); + virtual double GetReal(); + virtual const CPLString& GetString(); + virtual const CPLString& GetName(); + virtual GDALPDFDictionary* GetDictionary(); + virtual GDALPDFArray* GetArray(); + virtual GDALPDFStream* GetStream(); + virtual int GetRefNum(); + virtual int GetRefGen(); +}; + +#endif // HAVE_PODOFO + +#endif // PDFOBJECT_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/pdf/pdfreadvectors.cpp b/bazaar/plugin/gdal/frmts/pdf/pdfreadvectors.cpp new file mode 100644 index 000000000..37d8f1def --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/pdfreadvectors.cpp @@ -0,0 +1,1666 @@ +/****************************************************************************** + * $Id: pdfreadvectors.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: PDF driver + * Purpose: GDALDataset driver for PDF dataset (read vector features) + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pdf.h" + +#define SQUARE(x) ((x)*(x)) +#define EPSILON 1e-5 + +CPL_CVSID("$Id: pdfreadvectors.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +#if defined(HAVE_POPPLER) || defined(HAVE_PODOFO) + +/************************************************************************/ +/* OpenVectorLayers() */ +/************************************************************************/ + +int PDFDataset::OpenVectorLayers(GDALPDFDictionary* poPageDict) +{ + GetCatalog(); + if( poCatalogObject == NULL ) + return FALSE; + + GDALPDFObject* poContents = poPageDict->Get("Contents"); + if (poContents == NULL) + return FALSE; + + if (poContents->GetType() != PDFObjectType_Dictionary && + poContents->GetType() != PDFObjectType_Array) + return FALSE; + + GDALPDFObject* poResources = poPageDict->Get("Resources"); + if (poResources == NULL || poResources->GetType() != PDFObjectType_Dictionary) + return FALSE; + + GDALPDFObject* poStructTreeRoot = poCatalogObject->GetDictionary()->Get("StructTreeRoot"); + if (CSLTestBoolean(CPLGetConfigOption("OGR_PDF_READ_NON_STRUCTURED", "NO")) || + poStructTreeRoot == NULL || + poStructTreeRoot->GetType() != PDFObjectType_Dictionary) + { + ExploreContentsNonStructured(poContents, poResources); + } + else + { + ExploreContents(poContents, poResources); + ExploreTree(poStructTreeRoot, 0); + } + + CleanupIntermediateResources(); + + int bEmptyDS = TRUE; + for(int i=0;iGetFeatureCount() != 0) + { + bEmptyDS = FALSE; + break; + } + } + return !bEmptyDS; +} + +/************************************************************************/ +/* CleanupIntermediateResources() */ +/************************************************************************/ + +void PDFDataset::CleanupIntermediateResources() +{ + std::map::iterator oMapIter = oMapMCID.begin(); + for( ; oMapIter != oMapMCID.end(); ++oMapIter) + delete oMapIter->second; + oMapMCID.erase(oMapMCID.begin(), oMapMCID.end()); +} + +/************************************************************************/ +/* InitMapOperators() */ +/************************************************************************/ + +typedef struct +{ + char szOpName[4]; + int nArgs; +} PDFOperator; + +static const PDFOperator asPDFOperators [] = +{ + { "b", 0 }, + { "B", 0 }, + { "b*", 0 }, + { "B*", 0 }, + { "BDC", 2 }, + // BI + { "BMC", 1 }, + // BT + { "BX", 0 }, + { "c", 6 }, + { "cm", 6 }, + { "CS", 1 }, + { "cs", 1 }, + { "d", 1 }, /* we have ignored the first arg */ + // d0 + // d1 + { "Do", 1 }, + { "DP", 2 }, + // EI + { "EMC", 0 }, + // ET + { "EX", 0 }, + { "f", 0 }, + { "F", 0 }, + { "f*", 0 }, + { "G", 1 }, + { "g", 1 }, + { "gs", 1 }, + { "h", 0 }, + { "i", 1 }, + // ID + { "j", 1 }, + { "J", 1 }, + { "K", 4 }, + { "k", 4 }, + { "l", 2 }, + { "m", 2 }, + { "M", 1 }, + { "MP", 1 }, + { "n", 0 }, + { "q", 0 }, + { "Q", 0 }, + { "re", 4 }, + { "RG", 3 }, + { "rg", 3 }, + { "ri", 1 }, + { "s", 0 }, + { "S", 0 }, + { "SC", -1 }, + { "sc", -1 }, + { "SCN", -1 }, + { "scn", -1 }, + { "sh", 1 }, + // T* + { "Tc", 1}, + { "Td", 2}, + { "TD", 2}, + { "Tf", 1}, + { "Tj", 1}, + { "TJ", 1}, + { "TL", 1}, + { "Tm", 6}, + { "Tr", 1}, + { "Ts", 1}, + { "Tw", 1}, + { "Tz", 1}, + { "v", 4 }, + { "w", 1 }, + { "W", 0 }, + { "W*", 0 }, + { "y", 4 }, + // ' + // " +}; + +void PDFDataset::InitMapOperators() +{ + for(size_t i=0;i= nLayers) + return NULL; + + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int PDFDataset::GetLayerCount() +{ + return nLayers; +} + +/************************************************************************/ +/* ExploreTree() */ +/************************************************************************/ + +void PDFDataset::ExploreTree(GDALPDFObject* poObj, int nRecLevel) +{ + if (nRecLevel == 16) + return; + + if (poObj->GetType() != PDFObjectType_Dictionary) + return; + + GDALPDFDictionary* poDict = poObj->GetDictionary(); + + GDALPDFObject* poS = poDict->Get("S"); + CPLString osS; + if (poS != NULL && poS->GetType() == PDFObjectType_Name) + { + osS = poS->GetName(); + } + + GDALPDFObject* poT = poDict->Get("T"); + CPLString osT; + if (poT != NULL && poT->GetType() == PDFObjectType_String) + { + osT = poT->GetString(); + } + + GDALPDFObject* poK = poDict->Get("K"); + if (poK == NULL) + return; + + if (poK->GetType() == PDFObjectType_Array) + { + GDALPDFArray* poArray = poK->GetArray(); + if (poArray->GetLength() > 0 && + poArray->Get(0)->GetType() == PDFObjectType_Dictionary && + poArray->Get(0)->GetDictionary()->Get("K") != NULL && + poArray->Get(0)->GetDictionary()->Get("K")->GetType() == PDFObjectType_Int) + { + CPLString osLayerName; + if (osT.size()) + osLayerName = osT; + else + { + if (osS.size()) + osLayerName = osS; + else + osLayerName = CPLSPrintf("Layer%d", nLayers + 1); + } + + const char* pszWKT = GetProjectionRef(); + OGRSpatialReference* poSRS = NULL; + if (pszWKT && pszWKT[0] != '\0') + { + poSRS = new OGRSpatialReference(); + poSRS->importFromWkt((char**) &pszWKT); + } + + OGRPDFLayer* poLayer = + new OGRPDFLayer(this, osLayerName.c_str(), poSRS, wkbUnknown); + delete poSRS; + + poLayer->Fill(poArray); + + papoLayers = (OGRLayer**) + CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers] = poLayer; + nLayers ++; + } + else + { + for(int i=0;iGetLength();i++) + ExploreTree(poArray->Get(i), nRecLevel + 1); + } + } + else if (poK->GetType() == PDFObjectType_Dictionary) + { + ExploreTree(poK, nRecLevel + 1); + } +} + +/************************************************************************/ +/* GetGeometryFromMCID() */ +/************************************************************************/ + +OGRGeometry* PDFDataset::GetGeometryFromMCID(int nMCID) +{ + std::map::iterator oMapIter = oMapMCID.find(nMCID); + if (oMapIter != oMapMCID.end()) + return oMapIter->second; + else + return NULL; +} + +/************************************************************************/ +/* GraphicState */ +/************************************************************************/ + +class GraphicState +{ + public: + double adfCM[6]; + double adfStrokeColor[3]; + double adfFillColor[3]; + + GraphicState() + { + adfCM[0] = 1; + adfCM[1] = 0; + adfCM[2] = 0; + adfCM[3] = 1; + adfCM[4] = 0; + adfCM[5] = 0; + adfStrokeColor[0] = 0.0; + adfStrokeColor[1] = 0.0; + adfStrokeColor[2] = 0.0; + adfFillColor[0] = 1.0; + adfFillColor[1] = 1.0; + adfFillColor[2] = 1.0; + } + + void MultiplyBy(double adfMatrix[6]) + { + /* + [ a b 0 ] [ a' b' 0] [ aa' + bc' ab' + bd' 0 ] + [ c d 0 ] * [ c' d' 0] = [ ca' + dc' cb' + dd' 0 ] + [ e f 1 ] [ e' f' 1] [ ea' + fc' + e' eb' + fd' + f' 1 ] + */ + + double a = adfCM[0]; + double b = adfCM[1]; + double c = adfCM[2]; + double d = adfCM[3]; + double e = adfCM[4]; + double f = adfCM[5]; + double ap = adfMatrix[0]; + double bp = adfMatrix[1]; + double cp = adfMatrix[2]; + double dp = adfMatrix[3]; + double ep = adfMatrix[4]; + double fp = adfMatrix[5]; + adfCM[0] = a*ap + b*cp; + adfCM[1] = a*bp + b*dp; + adfCM[2] = c*ap + d*cp; + adfCM[3] = c*bp + d*dp; + adfCM[4] = e*ap + f*cp + ep; + adfCM[5] = e*bp + f*dp + fp; + } + + void ApplyMatrix(double adfCoords[2]) + { + double x = adfCoords[0]; + double y = adfCoords[1]; + + adfCoords[0] = x * adfCM[0] + y * adfCM[2] + adfCM[4]; + adfCoords[1] = x * adfCM[1] + y * adfCM[3] + adfCM[5]; + } +}; + +/************************************************************************/ +/* PDFCoordsToSRSCoords() */ +/************************************************************************/ + +void PDFDataset::PDFCoordsToSRSCoords(double x, double y, + double& X, double &Y) +{ + x = x / dfPageWidth * nRasterXSize; + y = (1 - y / dfPageHeight) * nRasterYSize; + + X = adfGeoTransform[0] + x * adfGeoTransform[1] + y * adfGeoTransform[2]; + Y = adfGeoTransform[3] + x * adfGeoTransform[4] + y * adfGeoTransform[5]; + + if( fabs(X - (int)floor(X + 0.5)) < 1e-8 ) + X = (int)floor(X + 0.5); + if( fabs(Y - (int)floor(Y + 0.5)) < 1e-8 ) + Y = (int)floor(Y + 0.5); +} + +/************************************************************************/ +/* PDFGetCircleCenter() */ +/************************************************************************/ + +/* Return the center of a circle, or NULL if it is not recognized */ + +static OGRPoint* PDFGetCircleCenter(OGRLineString* poLS) +{ + if (poLS == NULL || poLS->getNumPoints() != 5) + return NULL; + + if (poLS->getY(0) == poLS->getY(2) && + poLS->getX(1) == poLS->getX(3) && + fabs((poLS->getX(0) + poLS->getX(2)) / 2 - poLS->getX(1)) < EPSILON && + fabs((poLS->getY(1) + poLS->getY(3)) / 2 - poLS->getY(0)) < EPSILON) + { + return new OGRPoint((poLS->getX(0) + poLS->getX(2)) / 2, + (poLS->getY(1) + poLS->getY(3)) / 2); + } + return NULL; +} + +/************************************************************************/ +/* PDFGetSquareCenter() */ +/************************************************************************/ + +/* Return the center of a square, or NULL if it is not recognized */ + +static OGRPoint* PDFGetSquareCenter(OGRLineString* poLS) +{ + if (poLS == NULL || poLS->getNumPoints() < 4 || poLS->getNumPoints() > 5) + return NULL; + + if (poLS->getX(0) == poLS->getX(3) && + poLS->getY(0) == poLS->getY(1) && + poLS->getX(1) == poLS->getX(2) && + poLS->getY(2) == poLS->getY(3) && + fabs(fabs(poLS->getX(0) - poLS->getX(1)) - fabs(poLS->getY(0) - poLS->getY(3))) < EPSILON) + { + return new OGRPoint((poLS->getX(0) + poLS->getX(1)) / 2, + (poLS->getY(0) + poLS->getY(3)) / 2); + } + return NULL; +} + +/************************************************************************/ +/* PDFGetTriangleCenter() */ +/************************************************************************/ + +/* Return the center of a equilateral triangle, or NULL if it is not recognized */ + +static OGRPoint* PDFGetTriangleCenter(OGRLineString* poLS) +{ + if (poLS == NULL || poLS->getNumPoints() < 3 || poLS->getNumPoints() > 4) + return NULL; + + double dfSqD1 = SQUARE(poLS->getX(0) - poLS->getX(1)) + SQUARE(poLS->getY(0) - poLS->getY(1)); + double dfSqD2 = SQUARE(poLS->getX(1) - poLS->getX(2)) + SQUARE(poLS->getY(1) - poLS->getY(2)); + double dfSqD3 = SQUARE(poLS->getX(0) - poLS->getX(2)) + SQUARE(poLS->getY(0) - poLS->getY(2)); + if (fabs(dfSqD1 - dfSqD2) < EPSILON && fabs(dfSqD2 - dfSqD3) < EPSILON) + { + return new OGRPoint((poLS->getX(0) + poLS->getX(1) + poLS->getX(2)) / 3, + (poLS->getY(0) + poLS->getY(1) + poLS->getY(2)) / 3); + } + return NULL; +} + +/************************************************************************/ +/* PDFGetStarCenter() */ +/************************************************************************/ + +/* Return the center of a 5-point star, or NULL if it is not recognized */ + +static OGRPoint* PDFGetStarCenter(OGRLineString* poLS) +{ + if (poLS == NULL || poLS->getNumPoints() < 10 || poLS->getNumPoints() > 11) + return NULL; + + double dfSqD01 = SQUARE(poLS->getX(0) - poLS->getX(1)) + + SQUARE(poLS->getY(0) - poLS->getY(1)); + double dfSqD02 = SQUARE(poLS->getX(0) - poLS->getX(2)) + + SQUARE(poLS->getY(0) - poLS->getY(2)); + double dfSqD13 = SQUARE(poLS->getX(1) - poLS->getX(3)) + + SQUARE(poLS->getY(1) - poLS->getY(3)); + const double dfSin18divSin126 = 0.38196601125; + int bOK = fabs(dfSqD13 / dfSqD02 - SQUARE(dfSin18divSin126)) < EPSILON; + for(int i=1;i<10 && bOK;i++) + { + double dfSqDiip1 = SQUARE(poLS->getX(i) - poLS->getX((i+1)%10)) + + SQUARE(poLS->getY(i) - poLS->getY((i+1)%10)); + if (fabs(dfSqDiip1 - dfSqD01) > EPSILON) + { + bOK = FALSE; + } + double dfSqDiip2 = SQUARE(poLS->getX(i) - poLS->getX((i+2)%10)) + + SQUARE(poLS->getY(i) - poLS->getY((i+2)%10)); + if ( (i%2) == 1 && fabs(dfSqDiip2 - dfSqD13) > EPSILON ) + { + bOK = FALSE; + } + if ( (i%2) == 0 && fabs(dfSqDiip2 - dfSqD02) > EPSILON ) + { + bOK = FALSE; + } + } + if (bOK) + { + return new OGRPoint((poLS->getX(0) + poLS->getX(2) + poLS->getX(4) + + poLS->getX(6) + poLS->getX(8)) / 5, + (poLS->getY(0) + poLS->getY(2) + poLS->getY(4) + + poLS->getY(6) + poLS->getY(8)) / 5); + } + return NULL; +} + +/************************************************************************/ +/* UnstackTokens() */ +/************************************************************************/ + +int PDFDataset::UnstackTokens(const char* pszToken, + int nRequiredArgs, + char aszTokenStack[TOKEN_STACK_SIZE][MAX_TOKEN_SIZE], + int& nTokenStackSize, + double* adfCoords) +{ + if (nTokenStackSize < nRequiredArgs) + { + CPLDebug("PDF", "not enough arguments for %s", pszToken); + return FALSE; + } + nTokenStackSize -= nRequiredArgs; + for(int i=0;i& oMapPropertyToLayer, + OGRPDFLayer* poCurLayer) +{ + +#define PUSH(aszTokenStack, str, strlen) \ + do \ + { \ + if(nTokenStackSize < TOKEN_STACK_SIZE) \ + memcpy(aszTokenStack[nTokenStackSize ++], str, strlen + 1); \ + else \ + { \ + CPLError(CE_Failure, CPLE_AppDefined, "Max token stack size reached");\ + return NULL; \ + }; \ + } while(0) + +#define ADD_CHAR(szToken, c) \ + do \ + { \ + if(nTokenSize < MAX_TOKEN_SIZE-1) \ + { \ + szToken[nTokenSize ++ ] = c; \ + szToken[nTokenSize ] = '\0'; \ + } \ + else \ + { \ + CPLError(CE_Failure, CPLE_AppDefined, "Max token size reached");\ + return NULL; \ + }; \ + } while(0) + + char szToken[MAX_TOKEN_SIZE]; + int nTokenSize = 0; + char ch; + char aszTokenStack[TOKEN_STACK_SIZE][MAX_TOKEN_SIZE]; + int nTokenStackSize = 0; + int bInString = FALSE; + int nBDCLevel = 0; + int nParenthesisLevel = 0; + int nArrayLevel = 0; + int nBTLevel = 0; + + int bCollectAllObjects = poResources != NULL && !bInitBDCStack && !bMatchQ; + + GraphicState oGS; + std::stack oGSStack; + std::stack oLayerStack; + + std::vector oCoords; + int bHasFoundFill = FALSE; + int bHasMultiPart = FALSE; + + szToken[0] = '\0'; + + if (bInitBDCStack) + { + PUSH(aszTokenStack, "dummy", 5); + PUSH(aszTokenStack, "dummy", 5); + oLayerStack.push(NULL); + } + + while((ch = *pszContent) != '\0') + { + int bPushToken = FALSE; + + if (!bInString && ch == '%') + { + /* Skip comments until end-of-line */ + while((ch = *pszContent) != '\0') + { + if (ch == '\r' || ch == '\n') + break; + pszContent ++; + } + if (ch == 0) + break; + } + else if (!bInString && (ch == ' ' || ch == '\r' || ch == '\n')) + { + bPushToken = TRUE; + } + + /* Ignore arrays */ + else if (!bInString && nTokenSize == 0 && ch == '[') + { + nArrayLevel ++; + } + else if (!bInString && nArrayLevel && nTokenSize == 0 && ch == ']') + { + nArrayLevel --; + } + + else if (!bInString && nTokenSize == 0 && ch == '(') + { + bInString = TRUE; + nParenthesisLevel ++; + ADD_CHAR(szToken, ch); + } + else if (bInString && ch == '(') + { + nParenthesisLevel ++; + ADD_CHAR(szToken, ch); + } + else if (bInString && ch == ')') + { + nParenthesisLevel --; + ADD_CHAR(szToken, ch); + if (nParenthesisLevel == 0) + { + bInString = FALSE; + bPushToken = TRUE; + } + } + else if (ch == '<' && pszContent[1] == '<' && nTokenSize == 0) + { + int nDictDepth = 0; + + while(*pszContent != '\0') + { + if (pszContent[0] == '<' && pszContent[1] == '<') + { + ADD_CHAR(szToken, '<'); + ADD_CHAR(szToken, '<'); + nDictDepth ++; + pszContent += 2; + } + else if (pszContent[0] == '>' && pszContent[1] == '>') + { + ADD_CHAR(szToken, '>'); + ADD_CHAR(szToken, '>'); + nDictDepth --; + pszContent += 2; + if (nDictDepth == 0) + break; + } + else + { + ADD_CHAR(szToken, *pszContent); + pszContent ++; + } + } + if (nDictDepth == 0) + { + bPushToken = TRUE; + pszContent --; + } + else + break; + } + else + { + ADD_CHAR(szToken, ch); + } + + pszContent ++; + if (pszContent[0] == '\0') + bPushToken = TRUE; + +#define EQUAL1(szToken, s) (szToken[0] == s[0] && szToken[1] == '\0') +#define EQUAL2(szToken, s) (szToken[0] == s[0] && szToken[1] == s[1] && szToken[2] == '\0') +#define EQUAL3(szToken, s) (szToken[0] == s[0] && szToken[1] == s[1] && szToken[2] == s[2] && szToken[3] == '\0') + + if (bPushToken && nTokenSize) + { + if (EQUAL2(szToken, "BI")) + { + while(*pszContent != '\0') + { + if( pszContent[0] == 'E' && pszContent[1] == 'I' && pszContent[2] == ' ' ) + { + break; + } + pszContent ++; + } + if( pszContent[0] == 'E' ) + pszContent += 3; + else + return NULL; + } + else if (EQUAL3(szToken, "BDC")) + { + if (nTokenStackSize < 2) + { + CPLDebug("PDF", + "not enough arguments for %s", + szToken); + return NULL; + } + nTokenStackSize -= 2; + const char* pszOC = aszTokenStack[nTokenStackSize]; + const char* pszOCGName = aszTokenStack[nTokenStackSize+1]; + + nBDCLevel ++; + + if( EQUAL3(pszOC, "/OC") && pszOCGName[0] == '/' ) + { + std::map::iterator oIter = + oMapPropertyToLayer.find(pszOCGName + 1); + if( oIter != oMapPropertyToLayer.end() ) + { + poCurLayer = oIter->second; + //CPLDebug("PDF", "Cur layer : %s", poCurLayer->GetName()); + } + } + + oLayerStack.push(poCurLayer); + //CPLDebug("PDF", "%s %s BDC", osOC.c_str(), osOCGName.c_str()); + } + else if (EQUAL3(szToken, "EMC")) + { + //CPLDebug("PDF", "EMC"); + if( !oLayerStack.empty() ) + { + oLayerStack.pop(); + if( !oLayerStack.empty() ) + poCurLayer = oLayerStack.top(); + else + poCurLayer = NULL; + + /*if (poCurLayer) + { + CPLDebug("PDF", "Cur layer : %s", poCurLayer->GetName()); + }*/ + } + else + { + CPLDebug("PDF", "Should not happen at line %d", __LINE__); + poCurLayer = NULL; + //return NULL; + } + + nBDCLevel --; + if (nBDCLevel == 0 && bInitBDCStack) + break; + } + + /* Ignore any text stuff */ + else if (EQUAL2(szToken, "BT")) + nBTLevel ++; + else if (EQUAL2(szToken, "ET")) + { + nBTLevel --; + if (nBTLevel < 0) + { + CPLDebug("PDF", "Should not happen at line %d", __LINE__); + return NULL; + } + } + else if (!nArrayLevel && !nBTLevel) + { + int bEmitFeature = FALSE; + + if( szToken[0] < 'A' ) + { + PUSH(aszTokenStack, szToken, nTokenSize); + } + else if (EQUAL1(szToken, "q")) + { + oGSStack.push(oGS); + } + else if (EQUAL1(szToken, "Q")) + { + if (oGSStack.empty()) + { + CPLDebug("PDF", "not enough arguments for %s", szToken); + return NULL; + } + + oGS = oGSStack.top(); + oGSStack.pop(); + + if (oGSStack.empty() && bMatchQ) + break; + } + else if (EQUAL2(szToken, "cm")) + { + double adfMatrix[6]; + if (!UnstackTokens(szToken, 6, aszTokenStack, nTokenStackSize, adfMatrix)) + { + CPLDebug("PDF", "Should not happen at line %d", __LINE__); + return NULL; + } + + oGS.MultiplyBy(adfMatrix); + } + else if (EQUAL1(szToken, "b") || /* closepath, fill, stroke */ + EQUAL2(szToken, "b*") /* closepath, eofill, stroke */) + { + if (!(oCoords.size() > 0 && + oCoords[oCoords.size() - 2] == CLOSE_SUBPATH && + oCoords[oCoords.size() - 1] == CLOSE_SUBPATH)) + { + oCoords.push_back(CLOSE_SUBPATH); + oCoords.push_back(CLOSE_SUBPATH); + } + oCoords.push_back(FILL_SUBPATH); + oCoords.push_back(FILL_SUBPATH); + bHasFoundFill = TRUE; + + bEmitFeature = TRUE; + } + else if (EQUAL1(szToken, "B") || /* fill, stroke */ + EQUAL2(szToken, "B*") || /* eofill, stroke */ + EQUAL1(szToken, "f") || /* fill */ + EQUAL1(szToken, "F") || /* fill */ + EQUAL2(szToken, "f*") /* eofill */ ) + { + oCoords.push_back(FILL_SUBPATH); + oCoords.push_back(FILL_SUBPATH); + bHasFoundFill = TRUE; + + bEmitFeature = TRUE; + } + else if (EQUAL1(szToken, "h")) /* close subpath */ + { + if (!(oCoords.size() > 0 && + oCoords[oCoords.size() - 2] == CLOSE_SUBPATH && + oCoords[oCoords.size() - 1] == CLOSE_SUBPATH)) + { + oCoords.push_back(CLOSE_SUBPATH); + oCoords.push_back(CLOSE_SUBPATH); + } + } + else if (EQUAL1(szToken, "n")) /* new subpath without stroking or filling */ + { + oCoords.resize(0); + } + else if (EQUAL1(szToken, "s")) /* close and stroke */ + { + if (!(oCoords.size() > 0 && + oCoords[oCoords.size() - 2] == CLOSE_SUBPATH && + oCoords[oCoords.size() - 1] == CLOSE_SUBPATH)) + { + oCoords.push_back(CLOSE_SUBPATH); + oCoords.push_back(CLOSE_SUBPATH); + } + + bEmitFeature = TRUE; + } + else if (EQUAL1(szToken, "S")) /* stroke */ + { + bEmitFeature = TRUE; + } + else if (EQUAL1(szToken, "m") || EQUAL1(szToken, "l")) + { + double adfCoords[2]; + if (!UnstackTokens(szToken, 2, aszTokenStack, nTokenStackSize, adfCoords)) + { + CPLDebug("PDF", "Should not happen at line %d", __LINE__); + return NULL; + } + + if (EQUAL1(szToken, "m")) + { + if (oCoords.size() != 0) + bHasMultiPart = TRUE; + oCoords.push_back(NEW_SUBPATH); + oCoords.push_back(NEW_SUBPATH); + } + + oGS.ApplyMatrix(adfCoords); + oCoords.push_back(adfCoords[0]); + oCoords.push_back(adfCoords[1]); + } + else if (EQUAL1(szToken, "c")) /* Bezier curve */ + { + double adfCoords[6]; + if (!UnstackTokens(szToken, 6, aszTokenStack, nTokenStackSize, adfCoords)) + { + CPLDebug("PDF", "Should not happen at line %d", __LINE__); + return NULL; + } + + oGS.ApplyMatrix(adfCoords + 4); + oCoords.push_back(adfCoords[4]); + oCoords.push_back(adfCoords[5]); + } + else if (EQUAL1(szToken, "v") || EQUAL1(szToken, "y")) /* Bezier curve */ + { + double adfCoords[4]; + if (!UnstackTokens(szToken, 4, aszTokenStack, nTokenStackSize, adfCoords)) + { + CPLDebug("PDF", "Should not happen at line %d", __LINE__); + return NULL; + } + + oGS.ApplyMatrix(adfCoords + 2); + oCoords.push_back(adfCoords[2]); + oCoords.push_back(adfCoords[3]); + } + else if (EQUAL2(szToken, "re")) /* Rectangle */ + { + double adfCoords[4]; + if (!UnstackTokens(szToken, 4, aszTokenStack, nTokenStackSize, adfCoords)) + { + CPLDebug("PDF", "Should not happen at line %d", __LINE__); + return NULL; + } + + adfCoords[2] += adfCoords[0]; + adfCoords[3] += adfCoords[1]; + + oGS.ApplyMatrix(adfCoords); + oGS.ApplyMatrix(adfCoords + 2); + + if (oCoords.size() != 0) + bHasMultiPart = TRUE; + oCoords.push_back(NEW_SUBPATH); + oCoords.push_back(NEW_SUBPATH); + oCoords.push_back(adfCoords[0]); + oCoords.push_back(adfCoords[1]); + oCoords.push_back(adfCoords[2]); + oCoords.push_back(adfCoords[1]); + oCoords.push_back(adfCoords[2]); + oCoords.push_back(adfCoords[3]); + oCoords.push_back(adfCoords[0]); + oCoords.push_back(adfCoords[3]); + oCoords.push_back(CLOSE_SUBPATH); + oCoords.push_back(CLOSE_SUBPATH); + } + + else if (EQUAL2(szToken, "Do")) + { + if (nTokenStackSize == 0) + { + CPLDebug("PDF", + "not enough arguments for %s", + szToken); + return NULL; + } + + CPLString osObjectName = aszTokenStack[--nTokenStackSize]; + + if (osObjectName[0] != '/') + { + CPLDebug("PDF", "Should not happen at line %d", __LINE__); + return NULL; + } + + if (poResources == NULL) + { + if (osObjectName.find("/SymImage") == 0) + { + oCoords.push_back(oGS.adfCM[4] + oGS.adfCM[0] / 2); + oCoords.push_back(oGS.adfCM[5] + oGS.adfCM[3] / 2); + + szToken[0] = '\0'; + nTokenSize = 0; + + if( poCurLayer != NULL) + bEmitFeature = TRUE; + else + continue; + } + else + { + //CPLDebug("PDF", "Should not happen at line %d", __LINE__); + return NULL; + } + } + + if( !bEmitFeature ) + { + GDALPDFObject* poXObject = + poResources->GetDictionary()->Get("XObject"); + if (poXObject == NULL || + poXObject->GetType() != PDFObjectType_Dictionary) + { + CPLDebug("PDF", "Should not happen at line %d", __LINE__); + return NULL; + } + + GDALPDFObject* poObject = + poXObject->GetDictionary()->Get(osObjectName.c_str() + 1); + if (poObject == NULL) + { + CPLDebug("PDF", "Should not happen at line %d", __LINE__); + return NULL; + } + + int bParseStream = TRUE; + /* Check if the object is an image. If so, no need to try to parse */ + /* it. */ + if (poObject->GetType() == PDFObjectType_Dictionary) + { + GDALPDFObject* poSubtype = poObject->GetDictionary()->Get("Subtype"); + if (poSubtype != NULL && + poSubtype->GetType() == PDFObjectType_Name && + poSubtype->GetName() == "Image" ) + { + bParseStream = FALSE; + } + } + + if( bParseStream ) + { + GDALPDFStream* poStream = poObject->GetStream(); + if (!poStream) + { + CPLDebug("PDF", "Should not happen at line %d", __LINE__); + return NULL; + } + + char* pszStr = poStream->GetBytes(); + if( pszStr ) + { + OGRGeometry* poGeom = ParseContent(pszStr, NULL, FALSE, FALSE, + oMapPropertyToLayer, poCurLayer); + CPLFree(pszStr); + if (poGeom && !bCollectAllObjects) + return poGeom; + delete poGeom; + } + } + } + } + else if( EQUAL2(szToken, "RG") || EQUAL2(szToken, "rg") ) + { + double* padf = ( EQUAL2(szToken, "RG") ) ? oGS.adfStrokeColor : oGS.adfFillColor; + if (!UnstackTokens(szToken, 3, aszTokenStack, nTokenStackSize, padf)) + { + CPLDebug("PDF", "Should not happen at line %d", __LINE__); + return NULL; + } + } + else if (oMapOperators.find(szToken) != oMapOperators.end()) + { + int nArgs = oMapOperators[szToken]; + if (nArgs < 0) + { + while( nTokenStackSize != 0 ) + { + CPLString osTopToken = aszTokenStack[--nTokenStackSize]; + if (oMapOperators.find(osTopToken) != oMapOperators.end()) + break; + } + } + else + { + if( nArgs > nTokenStackSize ) + { + CPLDebug("PDF", + "not enough arguments for %s", + szToken); + return NULL; + } + nTokenStackSize -= nArgs; + } + } + else + { + PUSH(aszTokenStack, szToken, nTokenSize); + } + + if( bEmitFeature && poCurLayer != NULL) + { + OGRGeometry* poGeom = BuildGeometry(oCoords, bHasFoundFill, bHasMultiPart); + bHasFoundFill = bHasMultiPart = FALSE; + if (poGeom) + { + OGRFeature* poFeature = new OGRFeature(poCurLayer->GetLayerDefn()); + if( bSetStyle ) + { + OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType()); + if( eType == wkbLineString || eType == wkbMultiLineString ) + { + poFeature->SetStyleString(CPLSPrintf("PEN(c:#%02X%02X%02X)", + (int)(oGS.adfStrokeColor[0] * 255 + 0.5), + (int)(oGS.adfStrokeColor[1] * 255 + 0.5), + (int)(oGS.adfStrokeColor[2] * 255 + 0.5))); + } + else if( eType == wkbPolygon || eType == wkbMultiPolygon ) + { + poFeature->SetStyleString(CPLSPrintf("PEN(c:#%02X%02X%02X);BRUSH(fc:#%02X%02X%02X)", + (int)(oGS.adfStrokeColor[0] * 255 + 0.5), + (int)(oGS.adfStrokeColor[1] * 255 + 0.5), + (int)(oGS.adfStrokeColor[2] * 255 + 0.5), + (int)(oGS.adfFillColor[0] * 255 + 0.5), + (int)(oGS.adfFillColor[1] * 255 + 0.5), + (int)(oGS.adfFillColor[2] * 255 + 0.5))); + } + } + poGeom->assignSpatialReference(poCurLayer->GetSpatialRef()); + poFeature->SetGeometryDirectly(poGeom); + poCurLayer->CreateFeature(poFeature); + delete poFeature; + } + + oCoords.resize(0); + } + } + + szToken[0] = '\0'; + nTokenSize = 0; + } + } + + if (nTokenStackSize != 0) + { + while(nTokenStackSize != 0) + { + nTokenStackSize--; + CPLDebug("PDF", + "Remaing values in stack : %s", + aszTokenStack[nTokenStackSize]); + } + return NULL; + } + + if (bCollectAllObjects) + return NULL; + + return BuildGeometry(oCoords, bHasFoundFill, bHasMultiPart); +} + +/************************************************************************/ +/* BuildGeometry() */ +/************************************************************************/ + +OGRGeometry* PDFDataset::BuildGeometry(std::vector& oCoords, + int bHasFoundFill, + int bHasMultiPart) +{ + OGRGeometry* poGeom = NULL; + + if (!oCoords.size()) + return NULL; + + if (oCoords.size() == 2) + { + double X, Y; + PDFCoordsToSRSCoords(oCoords[0], oCoords[1], X, Y); + poGeom = new OGRPoint(X, Y); + } + else if (!bHasFoundFill) + { + OGRLineString* poLS = NULL; + OGRMultiLineString* poMLS = NULL; + if (bHasMultiPart) + { + poMLS = new OGRMultiLineString(); + poGeom = poMLS; + } + + for(size_t i=0;iaddGeometryDirectly(poLS); + else + poGeom = poLS; + } + else if (oCoords[i] == CLOSE_SUBPATH && oCoords[i+1] == CLOSE_SUBPATH) + { + if (poLS && poLS->getNumPoints() >= 2 && + !(poLS->getX(0) == poLS->getX(poLS->getNumPoints()-1) && + poLS->getY(0) == poLS->getY(poLS->getNumPoints()-1))) + { + poLS->addPoint(poLS->getX(0), poLS->getY(0)); + } + } + else if (oCoords[i] == FILL_SUBPATH && oCoords[i+1] == FILL_SUBPATH) + { + /* Should not happen */ + } + else + { + if (poLS) + { + double X, Y; + PDFCoordsToSRSCoords(oCoords[i], oCoords[i+1], X, Y); + + poLS->addPoint(X, Y); + } + } + } + + /* Recognize points as outputed by GDAL (ogr-sym-2 : circle (not filled)) */ + OGRGeometry* poCenter = NULL; + if (poCenter == NULL && poLS != NULL && poLS->getNumPoints() == 5) + { + poCenter = PDFGetCircleCenter(poLS); + } + + /* Recognize points as outputed by GDAL (ogr-sym-4: square (not filled)) */ + if (poCenter == NULL && poLS != NULL && (poLS->getNumPoints() == 4 || poLS->getNumPoints() == 5)) + { + poCenter = PDFGetSquareCenter(poLS); + } + + /* Recognize points as outputed by GDAL (ogr-sym-6: triangle (not filled)) */ + if (poCenter == NULL && poLS != NULL && (poLS->getNumPoints() == 3 || poLS->getNumPoints() == 4)) + { + poCenter = PDFGetTriangleCenter(poLS); + } + + /* Recognize points as outputed by GDAL (ogr-sym-8: star (not filled)) */ + if (poCenter == NULL && poLS != NULL && (poLS->getNumPoints() == 10 || poLS->getNumPoints() == 11)) + { + poCenter = PDFGetStarCenter(poLS); + } + + if (poCenter == NULL && poMLS != NULL && poMLS->getNumGeometries() == 2) + { + OGRLineString* poLS1 = (OGRLineString* )poMLS->getGeometryRef(0); + OGRLineString* poLS2 = (OGRLineString* )poMLS->getGeometryRef(1); + + /* Recognize points as outputed by GDAL (ogr-sym-0: cross (+) ) */ + if (poLS1->getNumPoints() == 2 && poLS2->getNumPoints() == 2 && + poLS1->getY(0) == poLS1->getY(1) && + poLS2->getX(0) == poLS2->getX(1) && + fabs(fabs(poLS1->getX(0) - poLS1->getX(1)) - fabs(poLS2->getY(0) - poLS2->getY(1))) < EPSILON && + fabs((poLS1->getX(0) + poLS1->getX(1)) / 2 - poLS2->getX(0)) < EPSILON && + fabs((poLS2->getY(0) + poLS2->getY(1)) / 2 - poLS1->getY(0)) < EPSILON) + { + poCenter = new OGRPoint(poLS2->getX(0), poLS1->getY(0)); + } + /* Recognize points as outputed by GDAL (ogr-sym-1: diagcross (X) ) */ + else if (poLS1->getNumPoints() == 2 && poLS2->getNumPoints() == 2 && + poLS1->getX(0) == poLS2->getX(0) && + poLS1->getY(0) == poLS2->getY(1) && + poLS1->getX(1) == poLS2->getX(1) && + poLS1->getY(1) == poLS2->getY(0) && + fabs(fabs(poLS1->getX(0) - poLS1->getX(1)) - fabs(poLS1->getY(0) - poLS1->getY(1))) < EPSILON) + { + poCenter = new OGRPoint((poLS1->getX(0) + poLS1->getX(1)) / 2, + (poLS1->getY(0) + poLS1->getY(1)) / 2); + } + } + + if (poCenter) + { + delete poGeom; + poGeom = poCenter; + } + } + else + { + OGRLinearRing* poLS = NULL; + int nPolys = 0; + OGRGeometry** papoPoly = NULL; + + for(size_t i=0;icloseRings(); + + OGRPoint* poCenter = NULL; + + if (nPolys == 0 && + poLS && + poLS->getNumPoints() == 5) + { + /* Recognize points as outputed by GDAL (ogr-sym-3 : circle (filled)) */ + poCenter = PDFGetCircleCenter(poLS); + + /* Recognize points as outputed by GDAL (ogr-sym-5: square (filled)) */ + if (poCenter == NULL) + poCenter = PDFGetSquareCenter(poLS); + + /* ESRI points */ + if (poCenter == NULL && + oCoords.size() == 14 && + poLS->getY(0) == poLS->getY(1) && + poLS->getX(1) == poLS->getX(2) && + poLS->getY(2) == poLS->getY(3) && + poLS->getX(3) == poLS->getX(0)) + { + poCenter = new OGRPoint((poLS->getX(0) + poLS->getX(1)) / 2, + (poLS->getY(0) + poLS->getY(2)) / 2); + } + } + /* Recognize points as outputed by GDAL (ogr-sym-7: triangle (filled)) */ + else if (nPolys == 0 && + poLS && + poLS->getNumPoints() == 4) + { + poCenter = PDFGetTriangleCenter(poLS); + } + /* Recognize points as outputed by GDAL (ogr-sym-9: star (filled)) */ + else if (nPolys == 0 && + poLS && + poLS->getNumPoints() == 11) + { + poCenter = PDFGetStarCenter(poLS); + } + + if (poCenter) + { + poGeom = poCenter; + break; + } + + if (poLS->getNumPoints() >= 3) + { + OGRPolygon* poPoly = new OGRPolygon(); + poPoly->addRingDirectly(poLS); + poLS = NULL; + + papoPoly = (OGRGeometry**) CPLRealloc(papoPoly, (nPolys + 1) * sizeof(OGRGeometry*)); + papoPoly[nPolys ++] = poPoly; + } + else + { + delete poLS; + poLS = NULL; + } + } + } + else + { + if (poLS) + { + double X, Y; + PDFCoordsToSRSCoords(oCoords[i], oCoords[i+1], X, Y); + + poLS->addPoint(X, Y); + } + } + } + + delete poLS; + + int bIsValidGeometry; + if (nPolys == 2 && + ((OGRPolygon*)papoPoly[0])->getNumInteriorRings() == 0 && + ((OGRPolygon*)papoPoly[1])->getNumInteriorRings() == 0) + { + OGRLinearRing* poRing0 = ((OGRPolygon*)papoPoly[0])->getExteriorRing(); + OGRLinearRing* poRing1 = ((OGRPolygon*)papoPoly[1])->getExteriorRing(); + if (poRing0->getNumPoints() == poRing1->getNumPoints()) + { + int bSameRing = TRUE; + for(int i=0;igetNumPoints();i++) + { + if (poRing0->getX(i) != poRing1->getX(i)) + { + bSameRing = FALSE; + break; + } + if (poRing0->getY(i) != poRing1->getY(i)) + { + bSameRing = FALSE; + break; + } + } + + /* Just keep on ring if they are identical */ + if (bSameRing) + { + delete papoPoly[1]; + nPolys = 1; + } + } + } + if (nPolys) + { + poGeom = OGRGeometryFactory::organizePolygons( + papoPoly, nPolys, &bIsValidGeometry, NULL); + } + CPLFree(papoPoly); + } + + return poGeom; +} + +/************************************************************************/ +/* ExploreContents() */ +/************************************************************************/ + +void PDFDataset::ExploreContents(GDALPDFObject* poObj, + GDALPDFObject* poResources) +{ + std::map oMapPropertyToLayer; + + if (poObj->GetType() == PDFObjectType_Array) + { + GDALPDFArray* poArray = poObj->GetArray(); + for(int i=0;iGetLength();i++) + ExploreContents(poArray->Get(i), poResources); + } + + if (poObj->GetType() != PDFObjectType_Dictionary) + return; + + GDALPDFStream* poStream = poObj->GetStream(); + if (!poStream) + return; + + char* pszStr = poStream->GetBytes(); + if (!pszStr) + return; + + const char* pszMCID = (const char*) pszStr; + while((pszMCID = strstr(pszMCID, "/MCID")) != NULL) + { + const char* pszBDC = strstr(pszMCID, "BDC"); + if (pszBDC) + { + /* Hack for http://www.avenza.com/sites/default/files/spatialpdf/US_County_Populations.pdf */ + /* FIXME: that logic is too fragile. */ + const char* pszStartParsing = pszBDC; + const char* pszAfterBDC = pszBDC + 3; + int bMatchQ = FALSE; + while (pszAfterBDC[0] == ' ' || pszAfterBDC[0] == '\r' || pszAfterBDC[0] == '\n') + pszAfterBDC ++; + if (strncmp(pszAfterBDC, "0 0 m", 5) == 0) + { + const char* pszLastq = pszBDC; + while(pszLastq > pszStr && *pszLastq != 'q') + pszLastq --; + + if (pszLastq > pszStr && *pszLastq == 'q' && + (pszLastq[-1] == ' ' || pszLastq[-1] == '\r' || pszLastq[-1] == '\n') && + (pszLastq[1] == ' ' || pszLastq[1] == '\r' || pszLastq[1] == '\n')) + { + pszStartParsing = pszLastq; + bMatchQ = TRUE; + } + } + + int nMCID = atoi(pszMCID + 6); + if (GetGeometryFromMCID(nMCID) == NULL) + { + OGRGeometry* poGeom = ParseContent(pszStartParsing, poResources, + !bMatchQ, bMatchQ, oMapPropertyToLayer, NULL); + if( poGeom != NULL ) + { + /* Save geometry in map */ + oMapMCID[nMCID] = poGeom; + } + } + } + pszMCID += 5; + } + CPLFree(pszStr); +} + +/************************************************************************/ +/* ExploreContentsNonStructured() */ +/************************************************************************/ + +void PDFDataset::ExploreContentsNonStructuredInternal(GDALPDFObject* poContents, + GDALPDFObject* poResources, + std::map& oMapPropertyToLayer) +{ + if (poContents->GetType() == PDFObjectType_Array) + { + GDALPDFArray* poArray = poContents->GetArray(); + char* pszConcatStr = NULL; + int nConcatLen = 0; + for(int i=0;iGetLength();i++) + { + GDALPDFObject* poObj = poArray->Get(i); + if( poObj->GetType() != PDFObjectType_Dictionary) + break; + GDALPDFStream* poStream = poObj->GetStream(); + if (!poStream) + break; + char* pszStr = poStream->GetBytes(); + if (!pszStr) + break; + int nLen = (int)strlen(pszStr); + char* pszConcatStrNew = (char*)CPLRealloc(pszConcatStr, nConcatLen + nLen + 1); + if( pszConcatStrNew == NULL ) + { + CPLFree(pszStr); + break; + } + pszConcatStr = pszConcatStrNew; + memcpy(pszConcatStr + nConcatLen, pszStr, nLen+1); + nConcatLen += nLen; + CPLFree(pszStr); + } + if( pszConcatStr ) + ParseContent(pszConcatStr, poResources, FALSE, FALSE, oMapPropertyToLayer, NULL); + CPLFree(pszConcatStr); + return; + } + + if (poContents->GetType() != PDFObjectType_Dictionary) + return; + + GDALPDFStream* poStream = poContents->GetStream(); + if (!poStream) + return; + + char* pszStr = poStream->GetBytes(); + if( !pszStr ) + return; + ParseContent(pszStr, poResources, FALSE, FALSE, oMapPropertyToLayer, NULL); + CPLFree(pszStr); +} + +void PDFDataset::ExploreContentsNonStructured(GDALPDFObject* poContents, + GDALPDFObject* poResources) +{ + std::map oMapPropertyToLayer; + if (poResources != NULL && + poResources->GetType() == PDFObjectType_Dictionary) + { + GDALPDFObject* poProperties = + poResources->GetDictionary()->Get("Properties"); + if (poProperties != NULL && + poProperties->GetType() == PDFObjectType_Dictionary) + { + char** papszLayersWithRef = osLayerWithRefList.List(); + char** papszIter = papszLayersWithRef; + std::map< std::pair, OGRPDFLayer *> oMapNumGenToLayer; + while(papszIter && *papszIter) + { + char** papszTokens = CSLTokenizeString(*papszIter); + + if( CSLCount(papszTokens) != 3 ) { + CSLDestroy(papszTokens); + CPLDebug("PDF", "Ignore '%s', unparsable.", *papszIter); + papszIter ++; + continue; + } + + const char* pszLayerName = papszTokens[0]; + int nNum = atoi(papszTokens[1]); + int nGen = atoi(papszTokens[2]); + + CPLString osSanitizedName(PDFSanitizeLayerName(pszLayerName)); + + OGRPDFLayer* poLayer = (OGRPDFLayer*) GetLayerByName(osSanitizedName.c_str()); + if (poLayer == NULL) + { + const char* pszWKT = GetProjectionRef(); + OGRSpatialReference* poSRS = NULL; + if (pszWKT && pszWKT[0] != '\0') + { + poSRS = new OGRSpatialReference(); + poSRS->importFromWkt((char**) &pszWKT); + } + + poLayer = + new OGRPDFLayer(this, osSanitizedName.c_str(), poSRS, wkbUnknown); + delete poSRS; + + papoLayers = (OGRLayer**) + CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers] = poLayer; + nLayers ++; + } + + oMapNumGenToLayer[ std::pair(nNum, nGen) ] = poLayer; + + CSLDestroy(papszTokens); + papszIter ++; + } + + std::map& oMap = + poProperties->GetDictionary()->GetValues(); + std::map::iterator oIter = oMap.begin(); + std::map::iterator oEnd = oMap.end(); + + for(; oIter != oEnd; ++oIter) + { + const char* pszKey = oIter->first.c_str(); + GDALPDFObject* poObj = oIter->second; + if( poObj->GetRefNum() != 0 ) + { + std::map< std::pair, OGRPDFLayer *>::iterator + oIterNumGenToLayer = oMapNumGenToLayer.find( + std::pair(poObj->GetRefNum(), poObj->GetRefGen()) ); + if( oIterNumGenToLayer != oMapNumGenToLayer.end() ) + { + oMapPropertyToLayer[pszKey] = oIterNumGenToLayer->second; + } + } + } + } + } + + if( nLayers == 0 ) + return; + + ExploreContentsNonStructuredInternal(poContents, + poResources, + oMapPropertyToLayer); + + /* Remove empty layers */ + int i = 0; + while(i < nLayers) + { + if (papoLayers[i]->GetFeatureCount() == 0) + { + delete papoLayers[i]; + if (i < nLayers - 1) + { + memmove(papoLayers + i, papoLayers + i + 1, + (nLayers - 1 - i) * sizeof(OGRPDFLayer*)); + } + nLayers --; + } + else + i ++; + } +} + +#endif /* defined(HAVE_POPPLER) || defined(HAVE_PODOFO) */ diff --git a/bazaar/plugin/gdal/frmts/pdf/pdfwritabledataset.cpp b/bazaar/plugin/gdal/frmts/pdf/pdfwritabledataset.cpp new file mode 100644 index 000000000..2471d5830 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pdf/pdfwritabledataset.cpp @@ -0,0 +1,334 @@ +/****************************************************************************** + * $Id$ + * + * Project: PDF driver + * Purpose: GDALDataset driver for PDF dataset (writable vector dataset) + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pdf.h" +#include "pdfcreatecopy.h" +#include "memdataset.h" + +CPL_CVSID("$Id$"); + +/************************************************************************/ +/* PDFWritableVectorDataset() */ +/************************************************************************/ + +PDFWritableVectorDataset::PDFWritableVectorDataset() +{ + papszOptions = NULL; + nLayers = 0; + papoLayers = NULL; + bModified = FALSE; +} + +/************************************************************************/ +/* ~PDFWritableVectorDataset() */ +/************************************************************************/ + +PDFWritableVectorDataset::~PDFWritableVectorDataset() +{ + SyncToDisk(); + + CSLDestroy(papszOptions); + for(int i=0;iSetDescription(pszName); + poDataset->papszOptions = CSLDuplicate(papszOptions); + + return poDataset; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +PDFWritableVectorDataset::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + CPL_UNUSED char ** papszOptions ) +{ +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRLayer* poLayer = new OGRPDFWritableLayer(this, pszLayerName, poSRS, eType); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers] = poLayer; + nLayers ++; + + return poLayer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int PDFWritableVectorDataset::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *PDFWritableVectorDataset::GetLayer( int iLayer ) + +{ + if (iLayer < 0 || iLayer >= nLayers) + return NULL; + + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int PDFWritableVectorDataset::GetLayerCount() +{ + return nLayers; +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr PDFWritableVectorDataset::SyncToDisk() +{ + if (nLayers == 0 || !bModified) + return OGRERR_NONE; + + bModified = FALSE; + + OGREnvelope sGlobalExtent; + int bHasExtent = FALSE; + for(int i=0;iGetExtent(&sExtent) == OGRERR_NONE) + { + bHasExtent = TRUE; + sGlobalExtent.Merge(sExtent); + } + } + if (!bHasExtent || + sGlobalExtent.MinX == sGlobalExtent.MaxX || + sGlobalExtent.MinY == sGlobalExtent.MaxY) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot compute spatial extent of features"); + return OGRERR_FAILURE; + } + + PDFCompressMethod eStreamCompressMethod = COMPRESS_DEFLATE; + const char* pszStreamCompressMethod = CSLFetchNameValue(papszOptions, "STREAM_COMPRESS"); + if (pszStreamCompressMethod) + { + if( EQUAL(pszStreamCompressMethod, "NONE") ) + eStreamCompressMethod = COMPRESS_NONE; + else if( EQUAL(pszStreamCompressMethod, "DEFLATE") ) + eStreamCompressMethod = COMPRESS_DEFLATE; + else + { + CPLError( CE_Warning, CPLE_NotSupported, + "Unsupported value for STREAM_COMPRESS."); + } + } + + const char* pszGEO_ENCODING = + CSLFetchNameValueDef(papszOptions, "GEO_ENCODING", "ISO32000"); + + double dfDPI = CPLAtof(CSLFetchNameValueDef(papszOptions, "DPI", "72")); + if (dfDPI < 72.0) + dfDPI = 72.0; + + const char* pszNEATLINE = CSLFetchNameValue(papszOptions, "NEATLINE"); + + int nMargin = atoi(CSLFetchNameValueDef(papszOptions, "MARGIN", "0")); + + PDFMargins sMargins; + sMargins.nLeft = nMargin; + sMargins.nRight = nMargin; + sMargins.nTop = nMargin; + sMargins.nBottom = nMargin; + + const char* pszLeftMargin = CSLFetchNameValue(papszOptions, "LEFT_MARGIN"); + if (pszLeftMargin) sMargins.nLeft = atoi(pszLeftMargin); + + const char* pszRightMargin = CSLFetchNameValue(papszOptions, "RIGHT_MARGIN"); + if (pszRightMargin) sMargins.nRight = atoi(pszRightMargin); + + const char* pszTopMargin = CSLFetchNameValue(papszOptions, "TOP_MARGIN"); + if (pszTopMargin) sMargins.nTop = atoi(pszTopMargin); + + const char* pszBottomMargin = CSLFetchNameValue(papszOptions, "BOTTOM_MARGIN"); + if (pszBottomMargin) sMargins.nBottom = atoi(pszBottomMargin); + + const char* pszExtraImages = CSLFetchNameValue(papszOptions, "EXTRA_IMAGES"); + const char* pszExtraStream = CSLFetchNameValue(papszOptions, "EXTRA_STREAM"); + const char* pszExtraLayerName = CSLFetchNameValue(papszOptions, "EXTRA_LAYER_NAME"); + + const char* pszOGRDisplayField = CSLFetchNameValue(papszOptions, "OGR_DISPLAY_FIELD"); + const char* pszOGRDisplayLayerNames = CSLFetchNameValue(papszOptions, "OGR_DISPLAY_LAYER_NAMES"); + int bWriteOGRAttributes = CSLFetchBoolean(papszOptions, "OGR_WRITE_ATTRIBUTES", TRUE); + const char* pszOGRLinkField = CSLFetchNameValue(papszOptions, "OGR_LINK_FIELD"); + + const char* pszOffLayers = CSLFetchNameValue(papszOptions, "OFF_LAYERS"); + const char* pszExclusiveLayers = CSLFetchNameValue(papszOptions, "EXCLUSIVE_LAYERS"); + + const char* pszJavascript = CSLFetchNameValue(papszOptions, "JAVASCRIPT"); + const char* pszJavascriptFile = CSLFetchNameValue(papszOptions, "JAVASCRIPT_FILE"); + +/* -------------------------------------------------------------------- */ +/* Create file. */ +/* -------------------------------------------------------------------- */ + VSILFILE* fp = VSIFOpenL(GetDescription(), "wb"); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create PDF file %s.\n", + GetDescription() ); + return OGRERR_FAILURE; + } + + GDALPDFWriter oWriter(fp); + + double dfRatio = (sGlobalExtent.MaxY - sGlobalExtent.MinY) / (sGlobalExtent.MaxX - sGlobalExtent.MinX); + + int nWidth, nHeight; + + if (dfRatio < 1) + { + nWidth = 1024; + nHeight = nWidth * dfRatio; + } + else + { + nHeight = 1024; + nWidth = nHeight / dfRatio; + } + + GDALDataset* poSrcDS = MEMDataset::Create( "MEM:::", nWidth, nHeight, 0, GDT_Byte, NULL ); + + double adfGeoTransform[6]; + adfGeoTransform[0] = sGlobalExtent.MinX; + adfGeoTransform[1] = (sGlobalExtent.MaxX - sGlobalExtent.MinX) / nWidth; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = sGlobalExtent.MaxY; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = - (sGlobalExtent.MaxY - sGlobalExtent.MinY) / nHeight; + + poSrcDS->SetGeoTransform(adfGeoTransform); + + OGRSpatialReference* poSRS = papoLayers[0]->GetSpatialRef(); + if (poSRS) + { + char* pszWKT = NULL; + poSRS->exportToWkt(&pszWKT); + poSrcDS->SetProjection(pszWKT); + CPLFree(pszWKT); + } + + oWriter.SetInfo(poSrcDS, papszOptions); + + oWriter.StartPage(poSrcDS, + dfDPI, + pszGEO_ENCODING, + pszNEATLINE, + &sMargins, + eStreamCompressMethod, + bWriteOGRAttributes); + + int iObj = 0; + + char** papszLayerNames = CSLTokenizeString2(pszOGRDisplayLayerNames,",",0); + + for(int i=0;iGetName(); + else + osLayerName = papszLayerNames[i]; + + oWriter.WriteOGRLayer((OGRDataSourceH)this, + i, + pszOGRDisplayField, + pszOGRLinkField, + osLayerName, + bWriteOGRAttributes, + iObj); + } + + CSLDestroy(papszLayerNames); + + oWriter.EndPage(pszExtraImages, + pszExtraStream, + pszExtraLayerName, + pszOffLayers, + pszExclusiveLayers); + + if (pszJavascript) + oWriter.WriteJavascript(pszJavascript); + else if (pszJavascriptFile) + oWriter.WriteJavascriptFile(pszJavascriptFile); + + oWriter.Close(); + + delete poSrcDS; + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/frmts/pds/GNUmakefile b/bazaar/plugin/gdal/frmts/pds/GNUmakefile new file mode 100644 index 000000000..a7e08b373 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/GNUmakefile @@ -0,0 +1,16 @@ + +include ../../GDALmake.opt + +OBJ = pdsdataset.o isis2dataset.o isis3dataset.o vicardataset.o nasakeywordhandler.o vicarkeywordhandler.o + +CPPFLAGS := -I../raw $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +$(O_OBJ): nasakeywordhandler.h vicarkeywordhandler.h ../raw/rawdataset.h + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + diff --git a/bazaar/plugin/gdal/frmts/pds/frmt_isis2.html b/bazaar/plugin/gdal/frmts/pds/frmt_isis2.html new file mode 100644 index 000000000..3d7a392c7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/frmt_isis2.html @@ -0,0 +1,53 @@ + + +ISIS2 -- USGS Astrogeology ISIS Cube (Version 2) + + + + +

    ISIS2 -- USGS Astrogeology ISIS Cube (Version 2)

    + +ISIS2 is a format used by the USGS Planetary Cartography group +to store and distribute planetary imagery data. GDAL provides read +and write access to ISIS2 formatted imagery data.

    + +ISIS2 files often have the extension .cub, sometimes with an associated +.lbl (label) file. When a .lbl file exists it should be used as +the dataset name rather than the .cub file.

    + +In addition to support for most ISIS2 imagery configurations, this +driver also reads georeferencing and coordinate system information +as well as selected other header metadata.

    + +Implementation of this driver was supported by the United States +Geological Survey.

    + +ISIS2 is part of a family of related formats including PDS and ISIS3.

    + +

    Creation Issues

    + +Currently the ISIS2 writer writes a very minimal header with only the +image structure information. No coordinate system, georeferencing or +other metadata is captured.

    + +

    Creation Options

    + +
      + +
    • LABELING_METHOD=ATTACHED/DETACHED: Determines whether the +header labelling should be in the same file as the imagery (the default - ATTACHED) +or in a separate file (DETACHED).

    • +
    • IMAGE_EXTENSION=extension: Set the extension used +for detached image files, defaults to "cub". Only used if LABELING_METHOD=DETACHED.

    • +
    + +

    See Also:

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/pds/frmt_isis3.html b/bazaar/plugin/gdal/frmts/pds/frmt_isis3.html new file mode 100644 index 000000000..e842aaec9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/frmt_isis3.html @@ -0,0 +1,37 @@ + + +ISIS3 -- USGS Astrogeology ISIS Cube (Version 3) + + + + +

    ISIS3 -- USGS Astrogeology ISIS Cube (Version 3)

    + +ISIS3 is a format used by the USGS Planetary Cartography group +to store and distribute planetary imagery data. GDAL provides +read-only access to ISIS3 formatted imagery data.

    + +ISIS3 files often have the extension .cub, sometimes with an associated +.lbl (label) file. When a .lbl file exists it should be used as +the dataset name rather than the .cub file.

    + +In addition to support for most ISIS3 imagery configurations, this +driver also reads georeferencing and coordinate system information +as well as selected other header metadata.

    + +Implementation of this driver was supported by the United States +Geological Survey.

    + +ISIS3 is part of a family of related formats including PDS and ISIS2.

    + + +

    See Also:

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/pds/frmt_pds.html b/bazaar/plugin/gdal/frmts/pds/frmt_pds.html new file mode 100644 index 000000000..973cef578 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/frmt_pds.html @@ -0,0 +1,44 @@ + + +PDS -- Planetary Data System + + + + +

    PDS -- Planetary Data System

    + +PDS is a format used primarily by NASA to store and distribute +solar, lunar and planetary imagery data. GDAL provides read-only +access to PDS formatted imagery data.

    + +PDS files often have the extension .img, sometimes with an associated +.lbl (label) file. When a .lbl file exists it should be used as +the dataset name rather than the .img file.

    + +In addition to support for most PDS imagery configurations, this +driver also reads georeferencing and coordinate system information +as well as selected other header metadata.

    + +Implementation of this driver was supported by the United States +Geological Survey.

    + +Due to abiguitities in the PDS specification, the georeferencing of +some products is subtly or grossly incorrect. There are configuration +variables which may be set for these products to correct the interpretation +of the georeferencing. Some details are available in +ticket #3940.

    + + +PDS is part of a family of related formats including ISIS2 and ISIS3.

    + +

    See Also:

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/pds/isis2dataset.cpp b/bazaar/plugin/gdal/frmts/pds/isis2dataset.cpp new file mode 100644 index 000000000..23fc03cee --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/isis2dataset.cpp @@ -0,0 +1,1221 @@ +/****************************************************************************** + * $Id: isis2dataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: ISIS Version 2 Driver + * Purpose: Implementation of ISIS2Dataset + * Author: Trent Hare (thare@usgs.gov), + * Robert Soricone (rsoricone@usgs.gov) + * Ludovic Mercier (ludovic.mercier@gmail.com) + * Frank Warmerdam (warmerdam@pobox.com) + * + * NOTE: Original code authored by Trent and Robert and placed in the public + * domain as per US government policy. I have (within my rights) appropriated + * it and placed it under the following license. This is not intended to + * diminish Trent and Roberts contribution. + ****************************************************************************** + * Copyright (c) 2006, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#define NULL1 0 +#define NULL2 -32768 +#define NULL3 -3.4028226550889044521e+38 + +#ifndef PI +# define PI 3.1415926535897932384626433832795 +#endif + +#define RECORD_SIZE 512 + +#include "rawdataset.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include "nasakeywordhandler.h" + +CPL_CVSID("$Id: isis2dataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_ISIS2(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* ISISDataset version2 */ +/* ==================================================================== */ +/************************************************************************/ + +class ISIS2Dataset : public RawDataset +{ + VSILFILE *fpImage; // image data file. + CPLString osExternalCube; + + NASAKeywordHandler oKeywords; + + int bGotTransform; + double adfGeoTransform[6]; + + CPLString osProjection; + + int parse_label(const char *file, char *keyword, char *value); + int strstrip(char instr[], char outstr[], int position); + + CPLString oTempResult; + + void CleanString( CPLString &osInput ); + + const char *GetKeyword( const char *pszPath, + const char *pszDefault = ""); + const char *GetKeywordSub( const char *pszPath, + int iSubscript, + const char *pszDefault = ""); + +public: + ISIS2Dataset(); + ~ISIS2Dataset(); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual const char *GetProjectionRef(void); + + virtual char **GetFileList(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); + + // Write related. + static int WriteRaster(CPLString osFilename, bool includeLabel, GUIntBig iRecord, GUIntBig iLabelRecords, GDALDataType eType, const char * pszInterleaving); + + static int WriteLabel(CPLString osFilename, CPLString osRasterFile, CPLString sObjectTag, unsigned int nXSize, unsigned int nYSize, unsigned int nBands, GDALDataType eType, + GUIntBig iRecords, const char * pszInterleaving, GUIntBig & iLabelRecords, bool bRelaunch=false); + static int WriteQUBE_Information(VSILFILE *fpLabel, unsigned int iLevel, unsigned int & nWritingBytes, + unsigned int nXSize, unsigned int nYSize, unsigned int nBands, GDALDataType eType, const char * pszInterleaving); + + static unsigned int WriteKeyword(VSILFILE *fpLabel, unsigned int iLevel, CPLString key, CPLString value); + static unsigned int WriteFormatting(VSILFILE *fpLabel, CPLString data); + static GUIntBig RecordSizeCalculation(unsigned int nXSize, unsigned int nYSize, unsigned int nBands, GDALDataType eType ); +}; + +/************************************************************************/ +/* ISIS2Dataset() */ +/************************************************************************/ + +ISIS2Dataset::ISIS2Dataset() +{ + fpImage = NULL; + bGotTransform = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~ISIS2Dataset() */ +/************************************************************************/ + +ISIS2Dataset::~ISIS2Dataset() + +{ + FlushCache(); + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **ISIS2Dataset::GetFileList() + +{ + char **papszFileList = NULL; + + papszFileList = GDALPamDataset::GetFileList(); + + if( strlen(osExternalCube) > 0 ) + papszFileList = CSLAddString( papszFileList, osExternalCube ); + + return papszFileList; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *ISIS2Dataset::GetProjectionRef() + +{ + if( strlen(osProjection) > 0 ) + return osProjection; + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr ISIS2Dataset::GetGeoTransform( double * padfTransform ) + +{ + if( bGotTransform ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; + } + else + { + return GDALPamDataset::GetGeoTransform( padfTransform ); + } +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + + +int ISIS2Dataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + if( poOpenInfo->pabyHeader == NULL ) + return FALSE; + + if( strstr((const char *)poOpenInfo->pabyHeader,"^QUBE") == NULL ) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *ISIS2Dataset::Open( GDALOpenInfo * poOpenInfo ) +{ +/* -------------------------------------------------------------------- */ +/* Does this look like a CUBE or an IMAGE Primary Data Object? */ +/* -------------------------------------------------------------------- */ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Open the file using the large file API. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpQube = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + + if( fpQube == NULL ) + return NULL; + + ISIS2Dataset *poDS; + + poDS = new ISIS2Dataset(); + + if( ! poDS->oKeywords.Ingest( fpQube, 0 ) ) + { + VSIFCloseL( fpQube ); + delete poDS; + return NULL; + } + + VSIFCloseL( fpQube ); + +/* -------------------------------------------------------------------- */ +/* We assume the user is pointing to the label (ie. .lab) file. */ +/* -------------------------------------------------------------------- */ + // QUBE can be inline or detached and point to an image name + // ^QUBE = 76 + // ^QUBE = ("ui31s015.img",6441) - has another label on the image + // ^QUBE = "ui31s015.img" - which implies no label or skip value + + const char *pszQube = poDS->GetKeyword( "^QUBE" ); + GUIntBig nQube = 0; + int bByteLocation = FALSE; + CPLString osTargetFile = poOpenInfo->pszFilename; + + if( pszQube[0] == '"' ) + { + CPLString osTPath = CPLGetPath(poOpenInfo->pszFilename); + CPLString osFilename = pszQube; + poDS->CleanString( osFilename ); + osTargetFile = CPLFormCIFilename( osTPath, osFilename, NULL ); + poDS->osExternalCube = osTargetFile; + } + else if( pszQube[0] == '(' ) + { + CPLString osTPath = CPLGetPath(poOpenInfo->pszFilename); + CPLString osFilename = poDS->GetKeywordSub("^QUBE",1,""); + poDS->CleanString( osFilename ); + osTargetFile = CPLFormCIFilename( osTPath, osFilename, NULL ); + poDS->osExternalCube = osTargetFile; + + nQube = atoi(poDS->GetKeywordSub("^QUBE",2,"1")); + if( strstr(poDS->GetKeywordSub("^QUBE",2,"1"),"") != NULL ) + bByteLocation = true; + } + else + { + nQube = atoi(pszQube); + if( strstr(pszQube,"") != NULL ) + bByteLocation = true; + } + +/* -------------------------------------------------------------------- */ +/* Check if file an ISIS2 header file? Read a few lines of text */ +/* searching for something starting with nrows or ncols. */ +/* -------------------------------------------------------------------- */ + GDALDataType eDataType = GDT_Byte; + OGRSpatialReference oSRS; + + //image parameters + int nRows, nCols, nBands = 1; + GUIntBig nSkipBytes = 0; + int itype; + int s_ix, s_iy, s_iz; // check SUFFIX_ITEMS params. + int record_bytes; + int bNoDataSet = FALSE; + char chByteOrder = 'M'; //default to MSB + + //Georef parameters + double dfULXMap=0.5; + double dfULYMap = 0.5; + double dfXDim = 1.0; + double dfYDim = 1.0; + double dfNoData = 0.0; + double xulcenter = 0.0; + double yulcenter = 0.0; + + //projection parameters + int bProjectionSet = TRUE; + double semi_major = 0.0; + double semi_minor = 0.0; + double iflattening = 0.0; + double center_lat = 0.0; + double center_lon = 0.0; + double first_std_parallel = 0.0; + double second_std_parallel = 0.0; + VSILFILE *fp; + + /* -------------------------------------------------------------------- */ + /* Checks to see if this is valid ISIS2 cube */ + /* SUFFIX_ITEM tag in .cub file should be (0,0,0); no side-planes */ + /* -------------------------------------------------------------------- */ + s_ix = atoi(poDS->GetKeywordSub( "QUBE.SUFFIX_ITEMS", 1 )); + s_iy = atoi(poDS->GetKeywordSub( "QUBE.SUFFIX_ITEMS", 2 )); + s_iz = atoi(poDS->GetKeywordSub( "QUBE.SUFFIX_ITEMS", 3 )); + + if( s_ix != 0 || s_iy != 0 || s_iz != 0 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "*** ISIS 2 cube file has invalid SUFFIX_ITEMS parameters:\n" + "*** gdal isis2 driver requires (0, 0, 0), thus no sideplanes or backplanes\n" + "found: (%i, %i, %i)\n\n", s_ix, s_iy, s_iz ); + delete poDS; + return NULL; + } + + /**************** end SUFFIX_ITEM check ***********************/ + + + /*********** Grab layout type (BSQ, BIP, BIL) ************/ + // AXIS_NAME = (SAMPLE,LINE,BAND) + /***********************************************************/ + const char *value; + + char szLayout[10] = "BSQ"; //default to band seq. + value = poDS->GetKeyword( "QUBE.AXIS_NAME", "" ); + if (EQUAL(value,"(SAMPLE,LINE,BAND)") ) + strcpy(szLayout,"BSQ"); + else if (EQUAL(value,"(BAND,LINE,SAMPLE)") ) + strcpy(szLayout,"BIP"); + else if (EQUAL(value,"(SAMPLE,BAND,LINE)") || EQUAL(value,"") ) + strcpy(szLayout,"BSQ"); + else { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s layout not supported. Abort\n\n", value); + delete poDS; + return NULL; + } + + /*********** Grab samples lines band ************/ + nCols = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS",1)); + nRows = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS",2)); + nBands = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS",3)); + + /*********** Grab Qube record bytes **********/ + record_bytes = atoi(poDS->GetKeyword("RECORD_BYTES")); + + if (nQube > 0 && bByteLocation ) + nSkipBytes = (nQube - 1); + else if( nQube > 0 ) + nSkipBytes = (nQube - 1) * record_bytes; + else + nSkipBytes = 0; + + /*********** Grab samples lines band ************/ + CPLString osCoreItemType = poDS->GetKeyword( "QUBE.CORE_ITEM_TYPE" ); + if( (EQUAL(osCoreItemType,"PC_INTEGER")) || + (EQUAL(osCoreItemType,"PC_UNSIGNED_INTEGER")) || + (EQUAL(osCoreItemType,"PC_REAL")) ) { + chByteOrder = 'I'; + } + + /******** Grab format type - isis2 only supports 8,16,32 *******/ + itype = atoi(poDS->GetKeyword("QUBE.CORE_ITEM_BYTES","")); + switch(itype) { + case 1 : + eDataType = GDT_Byte; + dfNoData = NULL1; + bNoDataSet = TRUE; + break; + case 2 : + if( strstr(osCoreItemType,"UNSIGNED") != NULL ) + { + dfNoData = 0; + eDataType = GDT_UInt16; + } + else + { + dfNoData = NULL2; + eDataType = GDT_Int16; + } + bNoDataSet = TRUE; + break; + case 4 : + eDataType = GDT_Float32; + dfNoData = NULL3; + bNoDataSet = TRUE; + break; + case 8 : + eDataType = GDT_Float64; + dfNoData = NULL3; + bNoDataSet = TRUE; + break; + default : + CPLError( CE_Failure, CPLE_AppDefined, + "Itype of %d is not supported in ISIS 2.", + itype); + delete poDS; + return NULL; + } + + /*********** Grab Cellsize ************/ + value = poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.MAP_SCALE"); + if (strlen(value) > 0 ) { + dfXDim = (float) CPLAtof(value) * 1000.0; /* convert from km to m */ + dfYDim = (float) CPLAtof(value) * 1000.0 * -1; + } + + /*********** Grab LINE_PROJECTION_OFFSET ************/ + value = poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.LINE_PROJECTION_OFFSET"); + if (strlen(value) > 0) { + yulcenter = (float) CPLAtof(value); + yulcenter = ((yulcenter) * dfYDim); + dfULYMap = yulcenter - (dfYDim/2); + } + + /*********** Grab SAMPLE_PROJECTION_OFFSET ************/ + value = poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.SAMPLE_PROJECTION_OFFSET"); + if( strlen(value) > 0 ) { + xulcenter = (float) CPLAtof(value); + xulcenter = ((xulcenter) * dfXDim); + dfULXMap = xulcenter - (dfXDim/2); + } + + /*********** Grab TARGET_NAME ************/ + /**** This is the planets name i.e. MARS ***/ + CPLString target_name = poDS->GetKeyword("QUBE.TARGET_NAME"); + + /*********** Grab MAP_PROJECTION_TYPE ************/ + CPLString map_proj_name = + poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE"); + poDS->CleanString( map_proj_name ); + + /*********** Grab SEMI-MAJOR ************/ + semi_major = + CPLAtof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.A_AXIS_RADIUS")) * 1000.0; + + /*********** Grab semi-minor ************/ + semi_minor = + CPLAtof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.C_AXIS_RADIUS")) * 1000.0; + + /*********** Grab CENTER_LAT ************/ + center_lat = + CPLAtof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.CENTER_LATITUDE")); + + /*********** Grab CENTER_LON ************/ + center_lon = + CPLAtof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.CENTER_LONGITUDE")); + + /*********** Grab 1st std parallel ************/ + first_std_parallel = + CPLAtof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.FIRST_STANDARD_PARALLEL")); + + /*********** Grab 2nd std parallel ************/ + second_std_parallel = + CPLAtof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.SECOND_STANDARD_PARALLEL")); + + /*** grab PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/ + // Need to further study how ocentric/ographic will effect the gdal library. + // So far we will use this fact to define a sphere or ellipse for some projections + // Frank - may need to talk this over + char bIsGeographic = TRUE; + value = poDS->GetKeyword("CUBE.IMAGE_MAP_PROJECTION.PROJECTION_LATITUDE_TYPE"); + if (EQUAL( value, "\"PLANETOCENTRIC\"" )) + bIsGeographic = FALSE; + + CPLDebug("ISIS2","using projection %s", map_proj_name.c_str() ); + + //Set oSRS projection and parameters + if ((EQUAL( map_proj_name, "EQUIRECTANGULAR_CYLINDRICAL" )) || + (EQUAL( map_proj_name, "EQUIRECTANGULAR" )) || + (EQUAL( map_proj_name, "SIMPLE_CYLINDRICAL" )) ) { + oSRS.OGRSpatialReference::SetEquirectangular2 ( 0.0, center_lon, center_lat, 0, 0 ); + } else if (EQUAL( map_proj_name, "ORTHOGRAPHIC" )) { + oSRS.OGRSpatialReference::SetOrthographic ( center_lat, center_lon, 0, 0 ); + } else if ((EQUAL( map_proj_name, "SINUSOIDAL" )) || + (EQUAL( map_proj_name, "SINUSOIDAL_EQUAL-AREA" ))) { + oSRS.OGRSpatialReference::SetSinusoidal ( center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "MERCATOR" )) { + oSRS.OGRSpatialReference::SetMercator ( center_lat, center_lon, 1, 0, 0 ); + } else if (EQUAL( map_proj_name, "POLAR_STEREOGRAPHIC" )) { + oSRS.OGRSpatialReference::SetPS ( center_lat, center_lon, 1, 0, 0 ); + } else if (EQUAL( map_proj_name, "TRANSVERSE_MERCATOR" )) { + oSRS.OGRSpatialReference::SetTM ( center_lat, center_lon, 1, 0, 0 ); + } else if (EQUAL( map_proj_name, "LAMBERT_CONFORMAL_CONIC" )) { + oSRS.OGRSpatialReference::SetLCC ( first_std_parallel, second_std_parallel, center_lat, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "") ) { + /* no projection */ + bProjectionSet = FALSE; + } else { + CPLDebug( "ISIS2", + "Dataset projection %s is not supported. Continuing...", + map_proj_name.c_str() ); + bProjectionSet = FALSE; + } + + if (bProjectionSet) { + //Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword + CPLString proj_target_name = map_proj_name + " " + target_name; + oSRS.SetProjCS(proj_target_name); //set ProjCS keyword + + //The geographic/geocentric name will be the same basic name as the body name + //'GCS' = Geographic/Geocentric Coordinate System + CPLString geog_name = "GCS_" + target_name; + + //The datum and sphere names will be the same basic name aas the planet + CPLString datum_name = "D_" + target_name; + CPLString sphere_name = target_name; // + "_IAU_IAG"); //Might not be IAU defined so don't add + + //calculate inverse flattening from major and minor axis: 1/f = a/(a-b) + if ((semi_major - semi_minor) < 0.0000001) + iflattening = 0; + else + iflattening = semi_major / (semi_major - semi_minor); + + //Set the body size but take into consideration which proj is being used to help w/ proj4 compatibility + //The use of a Sphere, polar radius or ellipse here is based on how ISIS does it internally + if ( ( (EQUAL( map_proj_name, "STEREOGRAPHIC" ) && (fabs(center_lat) == 90)) ) || + (EQUAL( map_proj_name, "POLAR_STEREOGRAPHIC" ))) + { + if (bIsGeographic) { + //Geograpraphic, so set an ellipse + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, iflattening, + "Reference_Meridian", 0.0 ); + } else { + //Geocentric, so force a sphere using the semi-minor axis. I hope... + sphere_name += "_polarRadius"; + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_minor, 0.0, + "Reference_Meridian", 0.0 ); + } + } + else if ( (EQUAL( map_proj_name, "SIMPLE_CYLINDRICAL" )) || + (EQUAL( map_proj_name, "ORTHOGRAPHIC" )) || + (EQUAL( map_proj_name, "STEREOGRAPHIC" )) || + (EQUAL( map_proj_name, "SINUSOIDAL_EQUAL-AREA" )) || + (EQUAL( map_proj_name, "SINUSOIDAL" )) ) { + //isis uses the sphereical equation for these projections so force a sphere + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, 0.0, + "Reference_Meridian", 0.0 ); + } + else if ((EQUAL( map_proj_name, "EQUIRECTANGULAR_CYLINDRICAL" )) || + (EQUAL( map_proj_name, "EQUIRECTANGULAR" )) ) { + //Calculate localRadius using ISIS3 simple elliptical method + // not the more standard Radius of Curvature method + //PI = 4 * atan(1); + double radLat, localRadius; + if (center_lon == 0) { //No need to calculate local radius + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, 0.0, + "Reference_Meridian", 0.0 ); + } else { + radLat = center_lat * PI / 180; // in radians + localRadius = semi_major * semi_minor / sqrt(pow(semi_minor*cos(radLat),2) + + pow(semi_major*sin(radLat),2) ); + sphere_name += "_localRadius"; + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + localRadius, 0.0, + "Reference_Meridian", 0.0 ); + CPLDebug( "ISIS2", "local radius: %f", localRadius); + } + } + else { + //All other projections: Mercator, Transverse Mercator, Lambert Conformal, etc. + //Geographic, so set an ellipse + if (bIsGeographic) { + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, iflattening, + "Reference_Meridian", 0.0 ); + } else { + //Geocentric, so force a sphere. I hope... + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, 0.0, + "Reference_Meridian", 0.0 ); + } + } + + + // translate back into a projection string. + char *pszResult = NULL; + oSRS.exportToWkt( &pszResult ); + poDS->osProjection = pszResult; + CPLFree( pszResult ); + } + +/* END ISIS2 Label Read */ +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/* -------------------------------------------------------------------- */ +/* Did we get the required keywords? If not we return with */ +/* this never having been considered to be a match. This isn't */ +/* an error! */ +/* -------------------------------------------------------------------- */ + if( nRows < 1 || nCols < 1 || nBands < 1 ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = nCols; + poDS->nRasterYSize = nRows; + +/* -------------------------------------------------------------------- */ +/* Open target binary file. */ +/* -------------------------------------------------------------------- */ + + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fpImage = VSIFOpenL( osTargetFile, "rb" ); + else + poDS->fpImage = VSIFOpenL( osTargetFile, "r+b" ); + + if( poDS->fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s with write permission.\n%s", + osTargetFile.c_str(), VSIStrerror( errno ) ); + delete poDS; + return NULL; + } + + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Compute the line offset. */ +/* -------------------------------------------------------------------- */ + int nItemSize = GDALGetDataTypeSize(eDataType)/8; + int nLineOffset, nPixelOffset, nBandOffset; + + if( EQUAL(szLayout,"BIP") ) + { + nPixelOffset = nItemSize * nBands; + nLineOffset = nPixelOffset * nCols; + nBandOffset = nItemSize; + } + else if( EQUAL(szLayout,"BSQ") ) + { + nPixelOffset = nItemSize; + nLineOffset = nPixelOffset * nCols; + nBandOffset = nLineOffset * nRows; + } + else /* assume BIL */ + { + nPixelOffset = nItemSize; + nLineOffset = nItemSize * nBands * nCols; + nBandOffset = nItemSize * nCols; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int i; + + poDS->nBands = nBands;; + for( i = 0; i < poDS->nBands; i++ ) + { + RawRasterBand *poBand; + + poBand = + new RawRasterBand( poDS, i+1, poDS->fpImage, + nSkipBytes + nBandOffset * i, + nPixelOffset, nLineOffset, eDataType, +#ifdef CPL_LSB + chByteOrder == 'I' || chByteOrder == 'L', +#else + chByteOrder == 'M', +#endif + TRUE ); + + if( bNoDataSet ) + poBand->SetNoDataValue( dfNoData ); + + poDS->SetBand( i+1, poBand ); + + // Set offset/scale values at the PAM level. + poBand->SetOffset( + CPLAtofM(poDS->GetKeyword("QUBE.CORE_BASE","0.0"))); + poBand->SetScale( + CPLAtofM(poDS->GetKeyword("QUBE.CORE_MULTIPLIER","1.0"))); + } + +/* -------------------------------------------------------------------- */ +/* Check for a .prj file. For isis2 I would like to keep this in */ +/* -------------------------------------------------------------------- */ + CPLString osPath, osName; + + osPath = CPLGetPath( poOpenInfo->pszFilename ); + osName = CPLGetBasename(poOpenInfo->pszFilename); + const char *pszPrjFile = CPLFormCIFilename( osPath, osName, "prj" ); + + fp = VSIFOpenL( pszPrjFile, "r" ); + if( fp != NULL ) + { + char **papszLines; + OGRSpatialReference oSRS; + + VSIFCloseL( fp ); + + papszLines = CSLLoad( pszPrjFile ); + + if( oSRS.importFromESRI( papszLines ) == OGRERR_NONE ) + { + char *pszResult = NULL; + oSRS.exportToWkt( &pszResult ); + poDS->osProjection = pszResult; + CPLFree( pszResult ); + } + + CSLDestroy( papszLines ); + } + + + if( dfULYMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0 ) + { + poDS->bGotTransform = TRUE; + poDS->adfGeoTransform[0] = dfULXMap; + poDS->adfGeoTransform[1] = dfXDim; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = dfULYMap; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = dfYDim; + } + + if( !poDS->bGotTransform ) + poDS->bGotTransform = + GDALReadWorldFile( poOpenInfo->pszFilename, "cbw", + poDS->adfGeoTransform ); + + if( !poDS->bGotTransform ) + poDS->bGotTransform = + GDALReadWorldFile( poOpenInfo->pszFilename, "wld", + poDS->adfGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GetKeyword() */ +/************************************************************************/ + +const char *ISIS2Dataset::GetKeyword( const char *pszPath, + const char *pszDefault ) + +{ + return oKeywords.GetKeyword( pszPath, pszDefault ); +} + +/************************************************************************/ +/* GetKeywordSub() */ +/************************************************************************/ + +const char *ISIS2Dataset::GetKeywordSub( const char *pszPath, + int iSubscript, + const char *pszDefault ) + +{ + const char *pszResult = oKeywords.GetKeyword( pszPath, NULL ); + + if( pszResult == NULL ) + return pszDefault; + + if( pszResult[0] != '(' ) + return pszDefault; + + char **papszTokens = CSLTokenizeString2( pszResult, "(,)", + CSLT_HONOURSTRINGS ); + + if( iSubscript <= CSLCount(papszTokens) ) + { + oTempResult = papszTokens[iSubscript-1]; + CSLDestroy( papszTokens ); + return oTempResult.c_str(); + } + else + { + CSLDestroy( papszTokens ); + return pszDefault; + } +} + +/************************************************************************/ +/* CleanString() */ +/* */ +/* Removes single or double quotes, and converts spaces to underscores. */ +/* The change is made in-place to CPLString. */ +/************************************************************************/ + +void ISIS2Dataset::CleanString( CPLString &osInput ) + +{ + if( ( osInput.size() < 2 ) || + ((osInput.at(0) != '"' || osInput.at(osInput.size()-1) != '"' ) && + ( osInput.at(0) != '\'' || osInput.at(osInput.size()-1) != '\'')) ) + return; + + char *pszWrk = CPLStrdup(osInput.c_str() + 1); + int i; + + pszWrk[strlen(pszWrk)-1] = '\0'; + + for( i = 0; pszWrk[i] != '\0'; i++ ) + { + if( pszWrk[i] == ' ' ) + pszWrk[i] = '_'; + } + + osInput = pszWrk; + CPLFree( pszWrk ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ +/** + * Hidden Creation Options: + * INTERLEAVE=BSQ/BIP/BIL: Force the generation specified type of interleaving. + * BSQ --- band sequental (default), + * BIP --- band interleaved by pixel, + * BIL --- band interleaved by line. + * OBJECT=QUBE/IMAGE/SPECTRAL_QUBE, if null default is QUBE + */ + +GDALDataset *ISIS2Dataset::Create(const char* pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char** papszParmList) { + + /* Verify settings. In Isis 2 core pixel values can be represented in + * three different ways : 1, 2 4, or 8 Bytes */ + if( eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_Float32 + && eType != GDT_UInt16 && eType != GDT_Float64 ){ + CPLError(CE_Failure, CPLE_AppDefined, + "The ISIS2 driver does not supporting creating files of type %s.", + GDALGetDataTypeName( eType ) ); + return NULL; + } + + /* (SAMPLE, LINE, BAND) - Band Sequential (BSQ) - default choice + (SAMPLE, BAND, LINE) - Band Interleaved by Line (BIL) + (BAND, SAMPLE, LINE) - Band Interleaved by Pixel (BIP) */ + const char *pszInterleaving = "(SAMPLE,LINE,BAND)"; + const char *pszInterleavingParam = CSLFetchNameValue( papszParmList, "INTERLEAVE" ); + if ( pszInterleavingParam ) { + if ( EQUALN( pszInterleavingParam, "bip", 3 ) ) + pszInterleaving = "(BAND,SAMPLE,LINE)"; + else if ( EQUALN( pszInterleavingParam, "bil", 3 ) ) + pszInterleaving = "(SAMPLE,BAND,LINE)"; + else + pszInterleaving = "(SAMPLE,LINE,BAND)"; + } + + /* default labeling method is attached */ + bool bAttachedLabelingMethod = true; + /* check if labeling method is set : check the all three first chars */ + const char *pszLabelingMethod = CSLFetchNameValue( papszParmList, "LABELING_METHOD" ); + if ( pszLabelingMethod ){ + if ( EQUALN( pszLabelingMethod, "detached", 3 ) ){ + bAttachedLabelingMethod = false; + } + if ( EQUALN( pszLabelingMethod, "attached", 3 ) ){ + bAttachedLabelingMethod = true; + } + } + + /* set the label and data files */ + CPLString osLabelFile, osRasterFile, osOutFile; + if( bAttachedLabelingMethod ) { + osLabelFile = ""; + osRasterFile = pszFilename; + osOutFile = osRasterFile; + } + else + { + CPLString sExtension = "cub"; + const char* pszExtension = CSLFetchNameValue( papszParmList, "IMAGE_EXTENSION" ); + if( pszExtension ){ + sExtension = pszExtension; + } + + if( EQUAL(CPLGetExtension( pszFilename ), sExtension) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "IMAGE_EXTENSION (%s) cannot match LABEL file extension.", + sExtension.c_str() ); + return NULL; + } + + osLabelFile = pszFilename; + osRasterFile = CPLResetExtension( osLabelFile, sExtension ); + osOutFile = osLabelFile; + } + + const char *pszObject = CSLFetchNameValue( papszParmList, "OBJECT" ); + CPLString sObject = "QUBE"; // default choice + if (pszObject) { + if ( EQUAL( pszObject, "IMAGE") ){ + sObject = "IMAGE"; + } + if ( EQUAL( pszObject, "SPECTRAL_QUBE")){ + sObject = "SPECTRAL_QUBE"; + } + } + + GUIntBig iRecords = ISIS2Dataset::RecordSizeCalculation(nXSize, nYSize, nBands, eType); + GUIntBig iLabelRecords(2); + + CPLDebug("ISIS2","irecord = %i",static_cast(iRecords)); + + if( bAttachedLabelingMethod ) + { + ISIS2Dataset::WriteLabel(osRasterFile, "", sObject, nXSize, nYSize, nBands, eType, iRecords, pszInterleaving, iLabelRecords, true); + } + else + { + ISIS2Dataset::WriteLabel(osLabelFile, osRasterFile, sObject, nXSize, nYSize, nBands, eType, iRecords, pszInterleaving, iLabelRecords); + } + + if( !ISIS2Dataset::WriteRaster(osRasterFile, bAttachedLabelingMethod, + iRecords, iLabelRecords, eType, + pszInterleaving) ) + return NULL; + + return (GDALDataset *) GDALOpen( osOutFile, GA_Update ); +} + + +/************************************************************************/ +/* WriteRaster() */ +/************************************************************************/ + +int ISIS2Dataset::WriteRaster(CPLString osFilename, + bool includeLabel, + GUIntBig iRecords, + GUIntBig iLabelRecords, + CPL_UNUSED GDALDataType eType, + CPL_UNUSED const char * pszInterleaving) { + GUIntBig nSize; + GByte byZero(0); + CPLString pszAccess("wb"); + if(includeLabel) + pszAccess = "ab"; + + VSILFILE *fpBin = VSIFOpenL( osFilename, pszAccess.c_str() ); + if( fpBin == NULL ) { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to create %s:\n%s", + osFilename.c_str(), VSIStrerror( errno ) ); + return FALSE; + } + + nSize = iRecords * RECORD_SIZE; + CPLDebug("ISIS2","nSize = %i", static_cast(nSize)); + + if(includeLabel) + nSize = iLabelRecords * RECORD_SIZE + nSize; + + // write last byte + if(VSIFSeekL( fpBin, nSize-1, SEEK_SET ) != 0 || + VSIFWriteL( &byZero, 1, 1, fpBin ) != 1){ + CPLError( CE_Failure, CPLE_FileIO, + "Failed to write %s:\n%s", + osFilename.c_str(), VSIStrerror( errno ) ); + VSIFCloseL( fpBin ); + return FALSE; + } + VSIFCloseL( fpBin ); + + return TRUE; +} + +/************************************************************************/ +/* RecordSizeCalculation() */ +/************************************************************************/ +GUIntBig ISIS2Dataset::RecordSizeCalculation(unsigned int nXSize, unsigned int nYSize, unsigned int nBands, GDALDataType eType ) + +{ + GUIntBig n = nXSize * nYSize * nBands * ( GDALGetDataTypeSize(eType) / 8); + // size of pds file is a multiple of RECORD_SIZE Bytes. + CPLDebug("ISIS2","n = %i", static_cast(n)); + CPLDebug("ISIS2","RECORD SIZE = %i", RECORD_SIZE); + CPLDebug("ISIS2","nXSize = %i", nXSize); + CPLDebug("ISIS2","nYSize = %i", nYSize); + CPLDebug("ISIS2","nBands = %i", nBands); + CPLDebug("ISIS2","DataTypeSize = %i", GDALGetDataTypeSize(eType)); + return (GUIntBig) ceil(static_cast(n)/RECORD_SIZE); +} + +/************************************************************************/ +/* WriteQUBE_Information() */ +/************************************************************************/ + +int ISIS2Dataset::WriteQUBE_Information( + VSILFILE *fpLabel, unsigned int iLevel, unsigned int & nWritingBytes, + unsigned int nXSize, unsigned int nYSize, unsigned int nBands, + GDALDataType eType, const char * pszInterleaving) + +{ + nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, ""); + nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, "/* Qube structure */"); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "OBJECT", "QUBE"); + iLevel++; + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "AXES", "3"); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "AXIS_NAME", pszInterleaving); + nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, "/* Core description */"); + + CPLDebug("ISIS2","%d,%d,%d",nXSize,nYSize,nBands); + + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEMS",CPLString().Printf("(%d,%d,%d)",nXSize,nYSize,nBands)); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_NAME", "\"RAW DATA NUMBER\""); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_UNIT", "\"N/A\""); + // TODO change for eType + + if( eType == GDT_Byte ) + { + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_UNSIGNED_INTEGER"); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_BYTES", "1"); + } + else if( eType == GDT_UInt16 ) + { + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_UNSIGNED_INTEGER"); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_BYTES", "2"); + } + else if( eType == GDT_Int16 ) + { + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_INTEGER"); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_BYTES", "2"); + } + else if( eType == GDT_Float32 ) + { + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_REAL"); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_BYTES", "4"); + } + else if( eType == GDT_Float64 ) + { + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_REAL"); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_BYTES", "8"); + } + + // TODO add core null value + + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_BASE", "0.0"); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_MULTIPLIER", "1.0"); + nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, "/* Suffix description */"); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "SUFFIX_BYTES", "4"); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "SUFFIX_ITEMS", "( 0, 0, 0)"); + iLevel--; + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "END_OBJECT", "QUBE"); + + return TRUE; +} + +/************************************************************************/ +/* WriteLabel() */ +/* */ +/* osRasterFile : name of raster file but if it is empty we */ +/* have only one file with an attached label */ +/* sObjectTag : QUBE, IMAGE or SPECTRAL_QUBE */ +/* bRelaunch : flag to allow recursiv call */ +/************************************************************************/ + +int ISIS2Dataset::WriteLabel( + CPLString osFilename, CPLString osRasterFile, CPLString sObjectTag, + unsigned int nXSize, unsigned int nYSize, unsigned int nBands, + GDALDataType eType, + GUIntBig iRecords, const char * pszInterleaving, + GUIntBig &iLabelRecords, + CPL_UNUSED bool bRelaunch) +{ + CPLDebug("ISIS2", "Write Label filename = %s, rasterfile = %s",osFilename.c_str(),osRasterFile.c_str()); + bool bAttachedLabel = EQUAL(osRasterFile, ""); + + VSILFILE *fpLabel = VSIFOpenL( osFilename, "w" ); + + if( fpLabel == NULL ){ + CPLError( CE_Failure, CPLE_FileIO, + "Failed to create %s:\n%s", + osFilename.c_str(), VSIStrerror( errno ) ); + return FALSE; + } + + unsigned int iLevel(0); + unsigned int nWritingBytes(0); + + /* write common header */ + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "PDS_VERSION_ID", "PDS3" ); + nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, ""); + nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, "/* File identification and structure */"); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "RECORD_TYPE", "FIXED_LENGTH" ); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "RECORD_BYTES", CPLString().Printf("%d",RECORD_SIZE)); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "FILE_RECORDS", CPLString().Printf(CPL_FRMT_GUIB,iRecords)); + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "LABEL_RECORDS", CPLString().Printf(CPL_FRMT_GUIB,iLabelRecords)); + if(!bAttachedLabel){ + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "FILE_NAME", CPLGetFilename(osRasterFile)); + } + nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, ""); + + nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, "/* Pointers to Data Objects */"); + + if(bAttachedLabel){ + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, CPLString().Printf("^%s",sObjectTag.c_str()), CPLString().Printf(CPL_FRMT_GUIB,iLabelRecords+1)); + }else{ + nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, CPLString().Printf("^%s",sObjectTag.c_str()), CPLString().Printf("(\"%s\",1)",CPLGetFilename(osRasterFile))); + } + + if(EQUAL(sObjectTag, "QUBE")){ + ISIS2Dataset::WriteQUBE_Information(fpLabel, iLevel, nWritingBytes, nXSize, nYSize, nBands, eType, pszInterleaving); + } + + nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, "END"); + + // check if file record is correct + unsigned int q = nWritingBytes/RECORD_SIZE; + if( q <= iLabelRecords){ + // correct we add space after the label end for complete from iLabelRecords + unsigned int nSpaceBytesToWrite = (unsigned int) (iLabelRecords * RECORD_SIZE - nWritingBytes); + VSIFPrintfL(fpLabel,"%*c", nSpaceBytesToWrite, ' '); + }else{ + iLabelRecords = q+1; + ISIS2Dataset::WriteLabel(osFilename, osRasterFile, sObjectTag, nXSize, nYSize, nBands, eType, iRecords, pszInterleaving, iLabelRecords); + } + VSIFCloseL( fpLabel ); + + return TRUE; +} + +/************************************************************************/ +/* WriteKeyword() */ +/************************************************************************/ + + unsigned int ISIS2Dataset::WriteKeyword( + VSILFILE *fpLabel, unsigned int iLevel, CPLString key, CPLString value) + + { + CPLString tab = ""; + iLevel *= 4; // each struct is idented by 4 spaces + int ret = VSIFPrintfL(fpLabel,"%*s%s=%s\n", iLevel, tab.c_str(), key.c_str(), value.c_str()); + return ret; + } + +/************************************************************************/ +/* WriteFormatting() */ +/************************************************************************/ + + unsigned int ISIS2Dataset::WriteFormatting(VSILFILE *fpLabel, CPLString data) + + { + int ret = VSIFPrintfL(fpLabel,"%s\n", data.c_str()); + return ret; + } + +/************************************************************************/ +/* GDALRegister_ISIS2() */ +/************************************************************************/ + + void GDALRegister_ISIS2() + + { + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ISIS2" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ISIS2" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "USGS Astrogeology ISIS cube (Version 2)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_isis2.html" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte Int16 UInt16 Float32 Float64"); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"\n" +" " +" \n" ); + + poDriver->pfnIdentify = ISIS2Dataset::Identify; + poDriver->pfnOpen = ISIS2Dataset::Open; + poDriver->pfnCreate = ISIS2Dataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } + } diff --git a/bazaar/plugin/gdal/frmts/pds/isis3dataset.cpp b/bazaar/plugin/gdal/frmts/pds/isis3dataset.cpp new file mode 100644 index 000000000..3fa0f6a6b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/isis3dataset.cpp @@ -0,0 +1,927 @@ +/****************************************************************************** + * $Id: isis3dataset.cpp 10646 2007-01-18 02:38:10Z warmerdam $ + * + * Project: ISIS Version 3 Driver + * Purpose: Implementation of ISIS3Dataset + * Author: Trent Hare (thare@usgs.gov) + * Frank Warmerdam (warmerdam@pobox.com) + * + * NOTE: Original code authored by Trent and placed in the public domain as + * per US government policy. I have (within my rights) appropriated it and + * placed it under the following license. This is not intended to diminish + * Trents contribution. + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#define NULL1 0 +#define NULL2 -32768 +//#define NULL3 0xFF7FFFFB //in hex +//Same as ESRI_GRID_FLOAT_NO_DATA +//#define NULL3 -340282346638528859811704183484516925440.0 +#define NULL3 -3.4028226550889044521e+38 + +#ifndef PI +# define PI 3.1415926535897932384626433832795 +#endif + +#include "rawdataset.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include "nasakeywordhandler.h" + +CPL_CVSID("$Id: isis3dataset.cpp 10646 2007-09-18 02:38:10Z xxxx $"); + +CPL_C_START +void GDALRegister_ISIS3(void); +CPL_C_END + +class ISIS3Dataset; + +/************************************************************************/ +/* ==================================================================== */ +/* ISISTiledBand */ +/* ==================================================================== */ +/************************************************************************/ + +class ISISTiledBand : public GDALPamRasterBand +{ + VSILFILE *fpVSIL; + GIntBig nFirstTileOffset; + GIntBig nXTileOffset; + GIntBig nYTileOffset; + int bNativeOrder; + + public: + + ISISTiledBand( GDALDataset *poDS, VSILFILE *fpVSIL, + int nBand, GDALDataType eDT, + int nTileXSize, int nTileYSize, + GIntBig nFirstTileOffset, + GIntBig nXTileOffset, + GIntBig nYTileOffset, + int bNativeOrder ); + virtual ~ISISTiledBand() {} + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* ISISTiledBand() */ +/************************************************************************/ + +ISISTiledBand::ISISTiledBand( GDALDataset *poDS, VSILFILE *fpVSIL, + int nBand, GDALDataType eDT, + int nTileXSize, int nTileYSize, + GIntBig nFirstTileOffset, + GIntBig nXTileOffset, + GIntBig nYTileOffset, + int bNativeOrder ) + +{ + this->poDS = poDS; + this->nBand = nBand; + this->fpVSIL = fpVSIL; + this->bNativeOrder = bNativeOrder; + eDataType = eDT; + nBlockXSize = nTileXSize; + nBlockYSize = nTileYSize; + + int nBlocksPerRow = + (poDS->GetRasterXSize() + nTileXSize - 1) / nTileXSize; + int nBlocksPerColumn = + (poDS->GetRasterYSize() + nTileYSize - 1) / nTileYSize; + + if( nXTileOffset == 0 && nYTileOffset == 0 ) + { + nXTileOffset = (GDALGetDataTypeSize(eDT)/8) * nTileXSize * nTileYSize; + nYTileOffset = nXTileOffset * nBlocksPerRow; + } + + this->nFirstTileOffset = nFirstTileOffset + + (nBand-1) * nYTileOffset * nBlocksPerColumn; + + this->nXTileOffset = nXTileOffset; + this->nYTileOffset = nYTileOffset; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr ISISTiledBand::IReadBlock( int nXBlock, int nYBlock, void *pImage ) + +{ + GIntBig nOffset = nFirstTileOffset + + nXBlock * nXTileOffset + nYBlock * nYTileOffset; + size_t nBlockSize = + (GDALGetDataTypeSize(eDataType)/8) * nBlockXSize * nBlockYSize; + + if( VSIFSeekL( fpVSIL, nOffset, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to offset %d to read tile %d,%d.", + (int) nOffset, nXBlock, nYBlock ); + return CE_Failure; + } + + if( VSIFReadL( pImage, 1, nBlockSize, fpVSIL ) != nBlockSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read %d bytes for tile %d,%d.", + (int) nBlockSize, nXBlock, nYBlock ); + return CE_Failure; + } + + if( !bNativeOrder ) + GDALSwapWords( pImage, GDALGetDataTypeSize(eDataType)/8, + nBlockXSize*nBlockYSize, + GDALGetDataTypeSize(eDataType)/8 ); + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* ISISDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class ISIS3Dataset : public RawDataset +{ + VSILFILE *fpImage; // image data file. + + CPLString osExternalCube; + + NASAKeywordHandler oKeywords; + + int bGotTransform; + double adfGeoTransform[6]; + + CPLString osProjection; + + int parse_label(const char *file, char *keyword, char *value); + int strstrip(char instr[], char outstr[], int position); + + CPLString oTempResult; + + const char *GetKeyword( const char *pszPath, + const char *pszDefault = ""); + const char *GetKeywordSub( const char *pszPath, + int iSubscript, + const char *pszDefault = ""); + +public: + ISIS3Dataset(); + ~ISIS3Dataset(); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual const char *GetProjectionRef(void); + + virtual char **GetFileList(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); +}; + + + +/************************************************************************/ +/* ISIS3Dataset() */ +/************************************************************************/ + +ISIS3Dataset::ISIS3Dataset() +{ + fpImage = NULL; + bGotTransform = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~ISIS3Dataset() */ +/************************************************************************/ + +ISIS3Dataset::~ISIS3Dataset() + +{ + FlushCache(); + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **ISIS3Dataset::GetFileList() + +{ + char **papszFileList = NULL; + + papszFileList = GDALPamDataset::GetFileList(); + + if( strlen(osExternalCube) > 0 ) + papszFileList = CSLAddString( papszFileList, osExternalCube ); + + return papszFileList; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *ISIS3Dataset::GetProjectionRef() + +{ + if( strlen(osProjection) > 0 ) + return osProjection; + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr ISIS3Dataset::GetGeoTransform( double * padfTransform ) + +{ + if( bGotTransform ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; + } + else + { + return GDALPamDataset::GetGeoTransform( padfTransform ); + } +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ +int ISIS3Dataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->pabyHeader != NULL + && strstr((const char *)poOpenInfo->pabyHeader,"IsisCube") != NULL ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *ISIS3Dataset::Open( GDALOpenInfo * poOpenInfo ) +{ +/* -------------------------------------------------------------------- */ +/* Does this look like a CUBE dataset? */ +/* -------------------------------------------------------------------- */ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Open the file using the large file API. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpQube = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + + if( fpQube == NULL ) + return NULL; + + ISIS3Dataset *poDS; + + poDS = new ISIS3Dataset(); + + if( ! poDS->oKeywords.Ingest( fpQube, 0 ) ) + { + VSIFCloseL( fpQube ); + delete poDS; + return NULL; + } + + VSIFCloseL( fpQube ); + +/* -------------------------------------------------------------------- */ +/* Assume user is pointing to label (ie .lbl) file for detached option */ +/* -------------------------------------------------------------------- */ + // Image can be inline or detached and point to an image name + // the Format can be Tiled or Raw + // Object = Core + // StartByte = 65537 + // Format = Tile + // TileSamples = 128 + // TileLines = 128 + //OR----- + // Object = Core + // StartByte = 1 + // ^Core = r0200357_detatched.cub + // Format = BandSequential + //OR----- + // Object = Core + // StartByte = 1 + // ^Core = r0200357_detached_tiled.cub + // Format = Tile + // TileSamples = 128 + // TileLines = 128 + +/* -------------------------------------------------------------------- */ +/* What file contains the actual data? */ +/* -------------------------------------------------------------------- */ + const char *pszCore = poDS->GetKeyword( "IsisCube.Core.^Core" ); + CPLString osQubeFile; + + if( EQUAL(pszCore,"") ) + osQubeFile = poOpenInfo->pszFilename; + else + { + CPLString osPath = CPLGetPath( poOpenInfo->pszFilename ); + osQubeFile = CPLFormFilename( osPath, pszCore, NULL ); + poDS->osExternalCube = osQubeFile; + } + +/* -------------------------------------------------------------------- */ +/* Check if file an ISIS3 header file? Read a few lines of text */ +/* searching for something starting with nrows or ncols. */ +/* -------------------------------------------------------------------- */ + GDALDataType eDataType = GDT_Byte; + OGRSpatialReference oSRS; + + int nRows = -1; + int nCols = -1; + int nBands = 1; + int nSkipBytes = 0; + int tileSizeX = 0; + int tileSizeY = 0; + double dfULXMap=0.5; + double dfULYMap = 0.5; + double dfXDim = 1.0; + double dfYDim = 1.0; + double scaleFactor = 1.0; + double dfNoData = 0.0; + int bNoDataSet = FALSE; + char chByteOrder = 'M'; //default to MSB + char szLayout[32] = "BandSequential"; //default to band seq. + const char *target_name; //planet name + //projection parameters + const char *map_proj_name; + int bProjectionSet = TRUE; + char proj_target_name[200]; + char geog_name[60]; + char datum_name[60]; + char sphere_name[60]; + char bIsGeographic = TRUE; + double semi_major = 0.0; + double semi_minor = 0.0; + double iflattening = 0.0; + double center_lat = 0.0; + double center_lon = 0.0; + double first_std_parallel = 0.0; + double second_std_parallel = 0.0; + double radLat, localRadius; + VSILFILE *fp; + + /************* Skipbytes *****************************/ + nSkipBytes = atoi(poDS->GetKeyword("IsisCube.Core.StartByte","")) - 1; + + /******* Grab format type (BandSequential, Tiled) *******/ + const char *value; + + value = poDS->GetKeyword( "IsisCube.Core.Format", "" ); + if (EQUAL(value,"Tile") ) { //Todo + strcpy(szLayout,"Tiled"); + /******* Get Tile Sizes *********/ + tileSizeX = atoi(poDS->GetKeyword("IsisCube.Core.TileSamples","")); + tileSizeY = atoi(poDS->GetKeyword("IsisCube.Core.TileLines","")); + if (tileSizeX <= 0 || tileSizeY <= 0) + { + CPLError( CE_Failure, CPLE_OpenFailed, "Wrong tile dimensions : %d x %d", + tileSizeX, tileSizeY); + delete poDS; + return NULL; + } + } + else if (EQUAL(value,"BandSequential") ) + strcpy(szLayout,"BSQ"); + else { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s layout not supported. Abort\n\n", value); + delete poDS; + return NULL; + } + + /*********** Grab samples lines band ************/ + nCols = atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Samples","")); + nRows = atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Lines","")); + nBands = atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Bands","")); + + /****** Grab format type - ISIS3 only supports 8,U16,S16,32 *****/ + const char *itype; + + itype = poDS->GetKeyword( "IsisCube.Core.Pixels.Type" ); + if (EQUAL(itype,"UnsignedByte") ) { + eDataType = GDT_Byte; + dfNoData = NULL1; + bNoDataSet = TRUE; + } + else if (EQUAL(itype,"UnsignedWord") ) { + eDataType = GDT_UInt16; + dfNoData = NULL1; + bNoDataSet = TRUE; + } + else if (EQUAL(itype,"SignedWord") ) { + eDataType = GDT_Int16; + dfNoData = NULL2; + bNoDataSet = TRUE; + } + else if (EQUAL(itype,"Real") || EQUAL(value,"") ) { + eDataType = GDT_Float32; + dfNoData = NULL3; + bNoDataSet = TRUE; + } + else { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s layout type not supported. Abort\n\n", itype); + delete poDS; + return NULL; + } + + /*********** Grab samples lines band ************/ + value = poDS->GetKeyword( "IsisCube.Core.Pixels.ByteOrder"); + if (EQUAL(value,"Lsb")) + chByteOrder = 'I'; + + /*********** Grab Cellsize ************/ + value = poDS->GetKeyword("IsisCube.Mapping.PixelResolution"); + if (strlen(value) > 0 ) { + dfXDim = CPLAtof(value); /* values are in meters */ + dfYDim = -CPLAtof(value); + } + + /*********** Grab UpperLeftCornerY ************/ + value = poDS->GetKeyword("IsisCube.Mapping.UpperLeftCornerY"); + if (strlen(value) > 0) { + dfULYMap = CPLAtof(value); + } + + /*********** Grab UpperLeftCornerX ************/ + value = poDS->GetKeyword("IsisCube.Mapping.UpperLeftCornerX"); + if( strlen(value) > 0 ) { + dfULXMap = CPLAtof(value); + } + + /*********** Grab TARGET_NAME ************/ + /**** This is the planets name i.e. Mars ***/ + target_name = poDS->GetKeyword("IsisCube.Mapping.TargetName"); + + /*********** Grab MAP_PROJECTION_TYPE ************/ + map_proj_name = + poDS->GetKeyword( "IsisCube.Mapping.ProjectionName"); + + /*********** Grab SEMI-MAJOR ************/ + semi_major = + CPLAtof(poDS->GetKeyword( "IsisCube.Mapping.EquatorialRadius")); + + /*********** Grab semi-minor ************/ + semi_minor = + CPLAtof(poDS->GetKeyword( "IsisCube.Mapping.PolarRadius")); + + /*********** Grab CENTER_LAT ************/ + center_lat = + CPLAtof(poDS->GetKeyword( "IsisCube.Mapping.CenterLatitude")); + + /*********** Grab CENTER_LON ************/ + center_lon = + CPLAtof(poDS->GetKeyword( "IsisCube.Mapping.CenterLongitude")); + + /*********** Grab 1st std parallel ************/ + first_std_parallel = + CPLAtof(poDS->GetKeyword( "IsisCube.Mapping.FirstStandardParallel")); + + /*********** Grab 2nd std parallel ************/ + second_std_parallel = + CPLAtof(poDS->GetKeyword( "IsisCube.Mapping.SecondStandardParallel")); + + /*********** Grab scaleFactor ************/ + scaleFactor = + CPLAtof(poDS->GetKeyword( "IsisCube.Mapping.scaleFactor", "1.0")); + + /*** grab LatitudeType = Planetographic ****/ + // Need to further study how ocentric/ographic will effect the gdal library + // So far we will use this fact to define a sphere or ellipse for some + // projections + + // Frank - may need to talk this over + value = poDS->GetKeyword("IsisCube.Mapping.LatitudeType"); + if (EQUAL( value, "Planetocentric" )) + bIsGeographic = FALSE; + + //Set oSRS projection and parameters + //############################################################ + //ISIS3 Projection types + // Equirectangular + // LambertConformal + // Mercator + // ObliqueCylindrical //Todo + // Orthographic + // PolarStereographic + // SimpleCylindrical + // Sinusoidal + // TransverseMercator + +#ifdef DEBUG + CPLDebug( "ISIS3", "using projection %s", map_proj_name); +#endif + + if ((EQUAL( map_proj_name, "Equirectangular" )) || + (EQUAL( map_proj_name, "SimpleCylindrical" )) ) { + oSRS.OGRSpatialReference::SetEquirectangular2 ( 0.0, center_lon, center_lat, 0, 0 ); + } else if (EQUAL( map_proj_name, "Orthographic" )) { + oSRS.OGRSpatialReference::SetOrthographic ( center_lat, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "Sinusoidal" )) { + oSRS.OGRSpatialReference::SetSinusoidal ( center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "Mercator" )) { + oSRS.OGRSpatialReference::SetMercator ( center_lat, center_lon, scaleFactor, 0, 0 ); + } else if (EQUAL( map_proj_name, "PolarStereographic" )) { + oSRS.OGRSpatialReference::SetPS ( center_lat, center_lon, scaleFactor, 0, 0 ); + } else if (EQUAL( map_proj_name, "TransverseMercator" )) { + oSRS.OGRSpatialReference::SetTM ( center_lat, center_lon, scaleFactor, 0, 0 ); + } else if (EQUAL( map_proj_name, "LambertConformal" )) { + oSRS.OGRSpatialReference::SetLCC ( first_std_parallel, second_std_parallel, center_lat, center_lon, 0, 0 ); + } else { + CPLDebug( "ISIS3", + "Dataset projection %s is not supported. Continuing...", + map_proj_name ); + bProjectionSet = FALSE; + } + + if (bProjectionSet) { + //Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword + strcpy(proj_target_name, map_proj_name); + strcat(proj_target_name, " "); + strcat(proj_target_name, target_name); + oSRS.SetProjCS(proj_target_name); //set ProjCS keyword + + //The geographic/geocentric name will be the same basic name as the body name + //'GCS' = Geographic/Geocentric Coordinate System + strcpy(geog_name, "GCS_"); + strcat(geog_name, target_name); + + //The datum name will be the same basic name as the planet + strcpy(datum_name, "D_"); + strcat(datum_name, target_name); + + strcpy(sphere_name, target_name); + //strcat(sphere_name, "_IAU_IAG"); //Might not be IAU defined so don't add + + //calculate inverse flattening from major and minor axis: 1/f = a/(a-b) + if ((semi_major - semi_minor) < 0.0000001) + iflattening = 0; + else + iflattening = semi_major / (semi_major - semi_minor); + + //Set the body size but take into consideration which proj is being used to help w/ proj4 compatibility + //The use of a Sphere, polar radius or ellipse here is based on how ISIS does it internally + if ( ( (EQUAL( map_proj_name, "Stereographic" ) && (fabs(center_lat) == 90)) ) || + (EQUAL( map_proj_name, "PolarStereographic" )) ) + { + if (bIsGeographic) { + //Geograpraphic, so set an ellipse + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, iflattening, + "Reference_Meridian", 0.0 ); + } else { + //Geocentric, so force a sphere using the semi-minor axis. I hope... + strcat(sphere_name, "_polarRadius"); + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_minor, 0.0, + "Reference_Meridian", 0.0 ); + } + } + else if ( (EQUAL( map_proj_name, "SimpleCylindrical" )) || + (EQUAL( map_proj_name, "Orthographic" )) || + (EQUAL( map_proj_name, "Stereographic" )) || + (EQUAL( map_proj_name, "Sinusoidal" )) ) { + //isis uses the sphereical equation for these projections so force a sphere + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, 0.0, + "Reference_Meridian", 0.0 ); + } + else if (EQUAL( map_proj_name, "Equirectangular" )) { + //Calculate localRadius using ISIS3 simple elliptical method + // not the more standard Radius of Curvature method + //PI = 4 * atan(1); + radLat = center_lat * PI / 180; // in radians + localRadius = semi_major * semi_minor / sqrt(pow(semi_minor*cos(radLat),2) + + pow(semi_major*sin(radLat),2) ); + strcat(sphere_name, "_localRadius"); + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + localRadius, 0.0, + "Reference_Meridian", 0.0 ); + } + else { + //All other projections: Mercator, Transverse Mercator, Lambert Conformal, etc. + //Geographic, so set an ellipse + if (bIsGeographic) { + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, iflattening, + "Reference_Meridian", 0.0 ); + } else { + //Geocentric, so force a sphere. I hope... + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, 0.0, + "Reference_Meridian", 0.0 ); + } + } + + // translate back into a projection string. + char *pszResult = NULL; + oSRS.exportToWkt( &pszResult ); + poDS->osProjection = pszResult; + CPLFree( pszResult ); + } + +/* END ISIS3 Label Read */ +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/* -------------------------------------------------------------------- */ +/* Is the CUB detached - if so, reset name to binary file? */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + // Frank - is this correct? + //The extension already added on so don't add another. But is this needed? + char *pszPath = CPLStrdup( CPLGetPath( poOpenInfo->pszFilename ) ); + char *pszName = CPLStrdup( CPLGetBasename( poOpenInfo->pszFilename ) ); + if (bIsDetached) + pszCUBFilename = CPLFormCIFilename( pszPath, detachedCub, "" ); +#endif + +/* -------------------------------------------------------------------- */ +/* Did we get the required keywords? If not we return with */ +/* this never having been considered to be a match. This isn't */ +/* an error! */ +/* -------------------------------------------------------------------- */ + if( nRows < 1 || nCols < 1 || nBands < 1 ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = nCols; + poDS->nRasterYSize = nRows; + +/* -------------------------------------------------------------------- */ +/* Open target binary file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fpImage = VSIFOpenL( osQubeFile, "r" ); + else + poDS->fpImage = VSIFOpenL( osQubeFile, "r+" ); + + if( poDS->fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s with write permission.\n%s", + osQubeFile.c_str(), + VSIStrerror( errno ) ); + delete poDS; + return NULL; + } + + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Compute the line offset. */ +/* -------------------------------------------------------------------- */ + int nItemSize = GDALGetDataTypeSize(eDataType)/8; + int nLineOffset=0, nPixelOffset=0, nBandOffset=0; + + if( EQUAL(szLayout,"BSQ") ) + { + nPixelOffset = nItemSize; + nLineOffset = nPixelOffset * nCols; + nBandOffset = nLineOffset * nRows; + } + else /* Tiled */ + { + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int i; + +#ifdef CPL_LSB + int bNativeOrder = !(chByteOrder == 'M'); +#else + int bNativeOrder = (chByteOrder == 'M'); +#endif + + + for( i = 0; i < nBands; i++ ) + { + GDALRasterBand *poBand; + + if( EQUAL(szLayout,"Tiled") ) + { + poBand = new ISISTiledBand( poDS, poDS->fpImage, i+1, eDataType, + tileSizeX, tileSizeY, + nSkipBytes, 0, 0, + bNativeOrder ); + } + else + { + poBand = + new RawRasterBand( poDS, i+1, poDS->fpImage, + nSkipBytes + nBandOffset * i, + nPixelOffset, nLineOffset, eDataType, +#ifdef CPL_LSB + chByteOrder == 'I' || chByteOrder == 'L', +#else + chByteOrder == 'M', +#endif + TRUE ); + } + + poDS->SetBand( i+1, poBand ); + + if( bNoDataSet ) + ((GDALPamRasterBand *) poBand)->SetNoDataValue( dfNoData ); + + // Set offset/scale values at the PAM level. + poBand->SetOffset( + CPLAtofM(poDS->GetKeyword("IsisCube.Core.Pixels.Base","0.0"))); + poBand->SetScale( + CPLAtofM(poDS->GetKeyword("IsisCube.Core.Pixels.Multiplier","1.0"))); + } + +/* -------------------------------------------------------------------- */ +/* Check for a .prj file. For ISIS3 I would like to keep this in */ +/* -------------------------------------------------------------------- */ + CPLString osPath, osName; + + osPath = CPLGetPath( poOpenInfo->pszFilename ); + osName = CPLGetBasename(poOpenInfo->pszFilename); + const char *pszPrjFile = CPLFormCIFilename( osPath, osName, "prj" ); + + fp = VSIFOpenL( pszPrjFile, "r" ); + if( fp != NULL ) + { + char **papszLines; + OGRSpatialReference oSRS; + + VSIFCloseL( fp ); + + papszLines = CSLLoad( pszPrjFile ); + + if( oSRS.importFromESRI( papszLines ) == OGRERR_NONE ) + { + char *pszResult = NULL; + oSRS.exportToWkt( &pszResult ); + poDS->osProjection = pszResult; + CPLFree( pszResult ); + } + + CSLDestroy( papszLines ); + } + + + if( dfULYMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0 ) + { + poDS->bGotTransform = TRUE; + poDS->adfGeoTransform[0] = dfULXMap; + poDS->adfGeoTransform[1] = dfXDim; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = dfULYMap; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = dfYDim; + } + + if( !poDS->bGotTransform ) + poDS->bGotTransform = + GDALReadWorldFile( poOpenInfo->pszFilename, "cbw", + poDS->adfGeoTransform ); + + if( !poDS->bGotTransform ) + poDS->bGotTransform = + GDALReadWorldFile( poOpenInfo->pszFilename, "wld", + poDS->adfGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GetKeyword() */ +/************************************************************************/ + +const char *ISIS3Dataset::GetKeyword( const char *pszPath, + const char *pszDefault ) + +{ + return oKeywords.GetKeyword( pszPath, pszDefault ); +} + +/************************************************************************/ +/* GetKeywordSub() */ +/************************************************************************/ + +const char *ISIS3Dataset::GetKeywordSub( const char *pszPath, + int iSubscript, + const char *pszDefault ) + +{ + const char *pszResult = oKeywords.GetKeyword( pszPath, NULL ); + + if( pszResult == NULL ) + return pszDefault; + + if( pszResult[0] != '(' ) + return pszDefault; + + char **papszTokens = CSLTokenizeString2( pszResult, "(,)", + CSLT_HONOURSTRINGS ); + + if( iSubscript <= CSLCount(papszTokens) ) + { + oTempResult = papszTokens[iSubscript-1]; + CSLDestroy( papszTokens ); + return oTempResult.c_str(); + } + else + { + CSLDestroy( papszTokens ); + return pszDefault; + } +} + + +/************************************************************************/ +/* GDALRegister_ISIS3() */ +/************************************************************************/ + +void GDALRegister_ISIS3() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ISIS3" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ISIS3" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "USGS Astrogeology ISIS cube (Version 3)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_isis3.html" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = ISIS3Dataset::Open; + poDriver->pfnIdentify = ISIS3Dataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/pds/makefile.vc b/bazaar/plugin/gdal/frmts/pds/makefile.vc new file mode 100644 index 000000000..0a34a1f5c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/makefile.vc @@ -0,0 +1,16 @@ + +OBJ = pdsdataset.obj isis2dataset.obj isis3dataset.obj \ + vicardataset.obj nasakeywordhandler.obj vicarkeywordhandler.obj + +GDAL_ROOT = ..\.. + +EXTRAFLAGS = -I..\raw + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/pds/nasakeywordhandler.cpp b/bazaar/plugin/gdal/frmts/pds/nasakeywordhandler.cpp new file mode 100644 index 000000000..baf1a071f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/nasakeywordhandler.cpp @@ -0,0 +1,441 @@ +/****************************************************************************** + * $Id: pdsdataset.cpp 12658 2007-11-07 23:14:33Z warmerdam $ + * + * Project: PDS Driver; Planetary Data System Format + * Purpose: Implementation of NASAKeywordHandler - a class to read + * keyword data from PDS, ISIS2 and ISIS3 data products. + * Author: Frank Warmerdam + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + **************************************************************************** + * Object Description Language (ODL) is used to encode data labels for PDS + * and other NASA data systems. Refer to Chapter 12 of "PDS Standards + * Reference" at http://pds.jpl.nasa.gov/tools/standards-reference.shtml for + * further details about ODL. + * + * This is also known as PVL (Parameter Value Language) which is written + * about at http://www.orrery.us/node/44 where it notes: + * + * The PVL syntax that the PDS uses is specified by the Consultative Committee + * for Space Data Systems in their Blue Book publication: "Parameter Value + * Language Specification (CCSD0006 and CCSD0008)", June 2000 + * [CCSDS 641.0-B-2], and Green Book publication: "Parameter Value Language - + * A Tutorial", June 2000 [CCSDS 641.0-G-2]. PVL has also been accepted by the + * International Standards Organization (ISO), as a Final Draft International + * Standard (ISO 14961:2002) keyword value type language for naming and + * expressing data values. + * -- + * also of interest, on PDS ODL: + * http://pds.jpl.nasa.gov/documents/sr/Chapter12.pdf + * + ****************************************************************************/ + +#include "cpl_string.h" +#include "nasakeywordhandler.h" + +/************************************************************************/ +/* ==================================================================== */ +/* NASAKeywordHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* NASAKeywordHandler() */ +/************************************************************************/ + +NASAKeywordHandler::NASAKeywordHandler() + +{ + papszKeywordList = NULL; +} + +/************************************************************************/ +/* ~NASAKeywordHandler() */ +/************************************************************************/ + +NASAKeywordHandler::~NASAKeywordHandler() + +{ + CSLDestroy( papszKeywordList ); + papszKeywordList = NULL; +} + +/************************************************************************/ +/* Ingest() */ +/************************************************************************/ + +int NASAKeywordHandler::Ingest( VSILFILE *fp, int nOffset ) + +{ +/* -------------------------------------------------------------------- */ +/* Read in buffer till we find END all on it's own line. */ +/* -------------------------------------------------------------------- */ + if( VSIFSeekL( fp, nOffset, SEEK_SET ) != 0 ) + return FALSE; + + for( ; TRUE; ) + { + const char *pszCheck; + char szChunk[513]; + + int nBytesRead = VSIFReadL( szChunk, 1, 512, fp ); + + szChunk[nBytesRead] = '\0'; + osHeaderText += szChunk; + + if( nBytesRead < 512 ) + break; + + if( osHeaderText.size() > 520 ) + pszCheck = osHeaderText.c_str() + (osHeaderText.size() - 520); + else + pszCheck = szChunk; + + if( strstr(pszCheck,"\r\nEND\r\n") != NULL + || strstr(pszCheck,"\nEND\n") != NULL + || strstr(pszCheck,"\r\nEnd\r\n") != NULL + || strstr(pszCheck,"\nEnd\n") != NULL ) + break; + } + + pszHeaderNext = osHeaderText.c_str(); + +/* -------------------------------------------------------------------- */ +/* Process name/value pairs, keeping track of a "path stack". */ +/* -------------------------------------------------------------------- */ + return ReadGroup( "" ); +} + +/************************************************************************/ +/* ReadGroup() */ +/************************************************************************/ + +int NASAKeywordHandler::ReadGroup( const char *pszPathPrefix ) + +{ + CPLString osName, osValue; + + for( ; TRUE; ) + { + if( !ReadPair( osName, osValue ) ) + return FALSE; + + if( EQUAL(osName,"OBJECT") || EQUAL(osName,"GROUP") ) + { + if( !ReadGroup( (CPLString(pszPathPrefix) + osValue + ".").c_str() ) ) + return FALSE; + } + else if( EQUAL(osName,"END") + || EQUAL(osName,"END_GROUP" ) + || EQUAL(osName,"END_OBJECT" ) ) + { + return TRUE; + } + else + { + osName = pszPathPrefix + osName; + papszKeywordList = CSLSetNameValue( papszKeywordList, + osName, osValue ); + } + } +} + +/************************************************************************/ +/* ReadPair() */ +/* */ +/* Read a name/value pair from the input stream. Strip off */ +/* white space, ignore comments, split on '='. */ +/* Returns TRUE on success. */ +/************************************************************************/ + +int NASAKeywordHandler::ReadPair( CPLString &osName, CPLString &osValue ) + +{ + osName = ""; + osValue = ""; + + if( !ReadWord( osName ) ) + return FALSE; + + SkipWhite(); + + if( EQUAL(osName,"END") ) + return TRUE; + + if( *pszHeaderNext != '=' ) + { + // ISIS3 does not have anything after the end group/object keyword. + if( EQUAL(osName,"End_Group") || EQUAL(osName,"End_Object") ) + return TRUE; + else + return FALSE; + } + + pszHeaderNext++; + + SkipWhite(); + + osValue = ""; + + // Handle value lists like: Name = (Red, Red) + if( *pszHeaderNext == '(' ) + { + CPLString osWord; + + while( ReadWord( osWord ) ) + { + SkipWhite(); + + osValue += osWord; + if( osWord[strlen(osWord)-1] == ')' ) + break; + } + } + + // Handle value lists like: Name = {Red, Red} + else if( *pszHeaderNext == '{' ) + { + CPLString osWord; + + while( ReadWord( osWord ) ) + { + SkipWhite(); + + osValue += osWord; + if( osWord[strlen(osWord)-1] == '}' ) + break; + } + } + + else // Handle more normal "single word" values. + { + if( !ReadWord( osValue ) ) + return FALSE; + + } + + SkipWhite(); + + // No units keyword? + if( *pszHeaderNext != '<' ) + return TRUE; + + // Append units keyword. For lines that like like this: + // MAP_RESOLUTION = 4.0 + + CPLString osWord; + + osValue += " "; + + while( ReadWord( osWord ) ) + { + SkipWhite(); + + osValue += osWord; + if( osWord[strlen(osWord)-1] == '>' ) + break; + } + + return TRUE; +} + +/************************************************************************/ +/* ReadWord() */ +/* Returns TRUE on success */ +/************************************************************************/ + +int NASAKeywordHandler::ReadWord( CPLString &osWord ) + +{ + osWord = ""; + + SkipWhite(); + + if( !(*pszHeaderNext != '\0' + && *pszHeaderNext != '=' + && !isspace((unsigned char)*pszHeaderNext)) ) + return FALSE; + + /* Extract a text string delimited by '\"' */ + /* Convert newlines (CR or LF) within quotes. While text strings + support them as per ODL, the keyword list doesn't want them */ + if( *pszHeaderNext == '"' ) + { + osWord += *(pszHeaderNext++); + while( *pszHeaderNext != '"' ) + { + if( *pszHeaderNext == '\0' ) + return FALSE; + if( *pszHeaderNext == '\n' ) + { + osWord += "\\n"; + pszHeaderNext++; + continue; + } + if( *pszHeaderNext == '\r' ) + { + osWord += "\\r"; + pszHeaderNext++; + continue; + } + osWord += *(pszHeaderNext++); + } + osWord += *(pszHeaderNext++); + return TRUE; + } + + /* Extract a symbol string */ + /* These are expected to not have + '\'' (delimiters), + format effectors (should fit on a single line) or + control characters. + */ + if( *pszHeaderNext == '\'' ) + { + osWord += *(pszHeaderNext++); + while( *pszHeaderNext != '\'' ) + { + if( *pszHeaderNext == '\0' ) + return FALSE; + + osWord += *(pszHeaderNext++); + } + osWord += *(pszHeaderNext++); + return TRUE; + } + + /* + * Extract normal text. Terminated by '=' or whitespace. + * + * A special exception is that a line may terminate with a '-' + * which is taken as a line extender, and we suck up white space to new + * text. + */ + while( *pszHeaderNext != '\0' + && *pszHeaderNext != '=' + && !isspace((unsigned char)*pszHeaderNext) ) + { + osWord += *pszHeaderNext; + pszHeaderNext++; + + if( *pszHeaderNext == '-' + && (pszHeaderNext[1] == 10 || pszHeaderNext[1] == 13) ) + { + pszHeaderNext += 2; + SkipWhite(); + } + } + + return TRUE; +} + +/************************************************************************/ +/* SkipWhite() */ +/* Skip white spaces and C style comments */ +/************************************************************************/ + +void NASAKeywordHandler::SkipWhite() + +{ + for( ; TRUE; ) + { + // Skip C style comments + if( *pszHeaderNext == '/' && pszHeaderNext[1] == '*' ) + { + pszHeaderNext += 2; + + while( *pszHeaderNext != '\0' + && (*pszHeaderNext != '*' + || pszHeaderNext[1] != '/' ) ) + { + pszHeaderNext++; + } + + pszHeaderNext += 2; + + // consume till end of line. + // reduce sensibility to a label error + while( *pszHeaderNext != '\0' + && *pszHeaderNext != 10 + && *pszHeaderNext != 13 ) + { + pszHeaderNext++; + } + continue; + } + + // Skip # style comments + if( (*pszHeaderNext == 10 || *pszHeaderNext == 13 || + *pszHeaderNext == ' ' || *pszHeaderNext == '\t' ) + && pszHeaderNext[1] == '#' ) + { + pszHeaderNext += 2; + + // consume till end of line. + while( *pszHeaderNext != '\0' + && *pszHeaderNext != 10 + && *pszHeaderNext != 13 ) + { + pszHeaderNext++; + } + continue; + } + + // Skip white space (newline, space, tab, etc ) + if( isspace( (unsigned char)*pszHeaderNext ) ) + { + pszHeaderNext++; + continue; + } + + // not white space, return. + return; + } +} + +/************************************************************************/ +/* GetKeyword() */ +/************************************************************************/ + +const char *NASAKeywordHandler::GetKeyword( const char *pszPath, + const char *pszDefault ) + +{ + const char *pszResult; + + pszResult = CSLFetchNameValue( papszKeywordList, pszPath ); + if( pszResult == NULL ) + return pszDefault; + else + return pszResult; +} + +/************************************************************************/ +/* GetKeywordList() */ +/************************************************************************/ + +char **NASAKeywordHandler::GetKeywordList() +{ + return papszKeywordList; +} + diff --git a/bazaar/plugin/gdal/frmts/pds/nasakeywordhandler.h b/bazaar/plugin/gdal/frmts/pds/nasakeywordhandler.h new file mode 100644 index 000000000..75af271b5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/nasakeywordhandler.h @@ -0,0 +1,27 @@ +/************************************************************************/ +/* ==================================================================== */ +/* NASAKeywordHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class NASAKeywordHandler +{ + char **papszKeywordList; + + CPLString osHeaderText; + const char *pszHeaderNext; + + void SkipWhite(); + int ReadWord( CPLString &osWord ); + int ReadPair( CPLString &osName, CPLString &osValue ); + int ReadGroup( const char *pszPathPrefix ); + +public: + NASAKeywordHandler(); + ~NASAKeywordHandler(); + + int Ingest( VSILFILE *fp, int nOffset ); + + const char *GetKeyword( const char *pszPath, const char *pszDefault ); + char **GetKeywordList(); +}; diff --git a/bazaar/plugin/gdal/frmts/pds/pdsdataset.cpp b/bazaar/plugin/gdal/frmts/pds/pdsdataset.cpp new file mode 100644 index 000000000..6473a027e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/pdsdataset.cpp @@ -0,0 +1,1426 @@ +/****************************************************************************** + * $Id: pdsdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: PDS Driver; Planetary Data System Format + * Purpose: Implementation of PDSDataset + * Author: Trent Hare (thare@usgs.gov), + * Robert Soricone (rsoricone@usgs.gov) + * + * NOTE: Original code authored by Trent and Robert and placed in the public + * domain as per US government policy. I have (within my rights) appropriated + * it and placed it under the following license. This is not intended to + * diminish Trent and Roberts contribution. + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +// Set up PDS NULL values +#define NULL1 0 +#define NULL2 -32768 +//#define NULL3 -0.3402822655089E+39 +//Same as ESRI_GRID_FLOAT_NO_DATA +//#define NULL3 -340282346638528859811704183484516925440.0 +#define NULL3 -3.4028226550889044521e+38 + +#include "rawdataset.h" +#include "gdal_proxy.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include "nasakeywordhandler.h" + +CPL_CVSID("$Id: pdsdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_PDS(void); +CPL_C_END + +enum PDSLayout +{ + PDS_BSQ, + PDS_BIP, + PDS_BIL +}; + +/************************************************************************/ +/* ==================================================================== */ +/* PDSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class PDSDataset : public RawDataset +{ + VSILFILE *fpImage; // image data file. + GDALDataset *poCompressedDS; + + NASAKeywordHandler oKeywords; + + int bGotTransform; + double adfGeoTransform[6]; + + CPLString osProjection; + + CPLString osTempResult; + + CPLString osExternalCube; + + void ParseSRS(); + int ParseCompressedImage(); + int ParseImage( CPLString osPrefix, CPLString osFilenamePrefix ); + static void CleanString( CPLString &osInput ); + + const char *GetKeyword( std::string osPath, + const char *pszDefault = ""); + const char *GetKeywordSub( std::string osPath, + int iSubscript, + const char *pszDefault = ""); + const char *GetKeywordUnit( const char *pszPath, + int iSubscript, + const char *pszDefault = ""); + + protected: + virtual int CloseDependentDatasets(); + +public: + PDSDataset(); + ~PDSDataset(); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual const char *GetProjectionRef(void); + + virtual char **GetFileList(void); + + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); +}; + +/************************************************************************/ +/* PDSDataset() */ +/************************************************************************/ + +PDSDataset::PDSDataset() +{ + fpImage = NULL; + bGotTransform = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + poCompressedDS = NULL; +} + +/************************************************************************/ +/* ~PDSDataset() */ +/************************************************************************/ + +PDSDataset::~PDSDataset() + +{ + FlushCache(); + if( fpImage != NULL ) + VSIFCloseL( fpImage ); + + CloseDependentDatasets(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int PDSDataset::CloseDependentDatasets() +{ + int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets(); + + if( poCompressedDS ) + { + bHasDroppedRef = TRUE; + delete poCompressedDS; + poCompressedDS = NULL; + } + + for( int iBand = 0; iBand < nBands; iBand++ ) + { + delete papoBands[iBand]; + } + nBands = 0; + + return bHasDroppedRef; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **PDSDataset::GetFileList() + +{ + char **papszFileList = RawDataset::GetFileList(); + + if( poCompressedDS != NULL ) + { + char **papszCFileList = poCompressedDS->GetFileList(); + + papszFileList = CSLInsertStrings( papszFileList, -1, + papszCFileList ); + CSLDestroy( papszCFileList ); + } + + if( !osExternalCube.empty() ) + { + papszFileList = CSLAddString( papszFileList, osExternalCube ); + } + + return papszFileList; +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr PDSDataset::IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + if( poCompressedDS != NULL ) + return poCompressedDS->BuildOverviews( pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); + else + return RawDataset::IBuildOverviews( pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr PDSDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ + if( poCompressedDS != NULL ) + return poCompressedDS->RasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + psExtraArg); + else + return RawDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *PDSDataset::GetProjectionRef() + +{ + if( strlen(osProjection) > 0 ) + return osProjection; + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr PDSDataset::GetGeoTransform( double * padfTransform ) + +{ + if( bGotTransform ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; + } + else + { + return GDALPamDataset::GetGeoTransform( padfTransform ); + } +} + +/************************************************************************/ +/* ParseSRS() */ +/************************************************************************/ + +void PDSDataset::ParseSRS() + +{ + const char *pszFilename = GetDescription(); + + CPLString osPrefix; + if( strlen(GetKeyword( "IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE")) == 0 && + strlen(GetKeyword( "UNCOMPRESSED_FILE.IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE")) != 0 ) + osPrefix = "UNCOMPRESSED_FILE."; + +/* ==================================================================== */ +/* Get the geotransform. */ +/* ==================================================================== */ + /*********** Grab Cellsize ************/ + //example: + //MAP_SCALE = 14.818 + //added search for unit (only checks for CM, KM - defaults to Meters) + const char *value; + //Georef parameters + double dfULXMap=0.5; + double dfULYMap = 0.5; + double dfXDim = 1.0; + double dfYDim = 1.0; + double xulcenter = 0.0; + double yulcenter = 0.0; + + value = GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.MAP_SCALE"); + if (strlen(value) > 0 ) { + dfXDim = CPLAtof(value); + dfYDim = CPLAtof(value) * -1; + + CPLString osKey(osPrefix + "IMAGE_MAP_PROJECTION.MAP_SCALE"); + CPLString unit = GetKeywordUnit(osKey,2); //KM + //value = GetKeywordUnit("IMAGE_MAP_PROJECTION.MAP_SCALE",3); //PIXEL + if((EQUAL(unit,"M")) || (EQUAL(unit,"METER")) || (EQUAL(unit,"METERS"))) { + // do nothing + } + else if (EQUAL(unit,"CM")) { + // convert from cm to m + dfXDim = dfXDim / 100.0; + dfYDim = dfYDim / 100.0; + } else { + //defaults to convert km to m + dfXDim = dfXDim * 1000.0; + dfYDim = dfYDim * 1000.0; + } + } + +/* -------------------------------------------------------------------- */ +/* Calculate upper left corner of pixel in meters from the */ +/* upper left center pixel sample/line offsets. It doesn't */ +/* mean the defaults will work for every PDS image, as these */ +/* values are used inconsistently. Thus we have included */ +/* conversion options to allow the user to override the */ +/* documented PDS3 default. Jan. 2011, for known mapping issues */ +/* see GDAL PDS page or mapping within ISIS3 source (USGS) */ +/* $ISIS3DATA/base/translations/pdsProjectionLineSampToXY.def */ +/* -------------------------------------------------------------------- */ + + // defaults should be correct for what is documented in the PDS3 standard + double dfSampleOffset_Shift; + double dfLineOffset_Shift; + double dfSampleOffset_Mult; + double dfLineOffset_Mult; + + dfSampleOffset_Shift = + CPLAtof(CPLGetConfigOption( "PDS_SampleProjOffset_Shift", "-0.5" )); + + dfLineOffset_Shift = + CPLAtof(CPLGetConfigOption( "PDS_LineProjOffset_Shift", "-0.5" )); + + dfSampleOffset_Mult = + CPLAtof(CPLGetConfigOption( "PDS_SampleProjOffset_Mult", "-1.0") ); + + dfLineOffset_Mult = + CPLAtof( CPLGetConfigOption( "PDS_LineProjOffset_Mult", "1.0") ); + + /*********** Grab LINE_PROJECTION_OFFSET ************/ + value = GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.LINE_PROJECTION_OFFSET"); + if (strlen(value) > 0) { + yulcenter = CPLAtof(value); + dfULYMap = ((yulcenter + dfLineOffset_Shift) * -dfYDim * dfLineOffset_Mult); + //notice dfYDim is negative here which is why it is again negated here + } + /*********** Grab SAMPLE_PROJECTION_OFFSET ************/ + value = GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.SAMPLE_PROJECTION_OFFSET"); + if( strlen(value) > 0 ) { + xulcenter = CPLAtof(value); + dfULXMap = ((xulcenter + dfSampleOffset_Shift) * dfXDim * dfSampleOffset_Mult); + } + +/* ==================================================================== */ +/* Get the coordinate system. */ +/* ==================================================================== */ + int bProjectionSet = TRUE; + double semi_major = 0.0; + double semi_minor = 0.0; + double iflattening = 0.0; + double center_lat = 0.0; + double center_lon = 0.0; + double first_std_parallel = 0.0; + double second_std_parallel = 0.0; + OGRSpatialReference oSRS; + + /*********** Grab TARGET_NAME ************/ + /**** This is the planets name i.e. MARS ***/ + CPLString target_name = GetKeyword("TARGET_NAME"); + CleanString( target_name ); + + /********** Grab MAP_PROJECTION_TYPE *****/ + CPLString map_proj_name = + GetKeyword( osPrefix + "IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE"); + CleanString( map_proj_name ); + + /****** Grab semi_major & convert to KM ******/ + semi_major = + CPLAtof(GetKeyword( osPrefix + "IMAGE_MAP_PROJECTION.A_AXIS_RADIUS")) * 1000.0; + + /****** Grab semi-minor & convert to KM ******/ + semi_minor = + CPLAtof(GetKeyword( osPrefix + "IMAGE_MAP_PROJECTION.C_AXIS_RADIUS")) * 1000.0; + + /*********** Grab CENTER_LAT ************/ + center_lat = + CPLAtof(GetKeyword( osPrefix + "IMAGE_MAP_PROJECTION.CENTER_LATITUDE")); + + /*********** Grab CENTER_LON ************/ + center_lon = + CPLAtof(GetKeyword( osPrefix + "IMAGE_MAP_PROJECTION.CENTER_LONGITUDE")); + + /********** Grab 1st std parallel *******/ + first_std_parallel = + CPLAtof(GetKeyword( osPrefix + "IMAGE_MAP_PROJECTION.FIRST_STANDARD_PARALLEL")); + + /********** Grab 2nd std parallel *******/ + second_std_parallel = + CPLAtof(GetKeyword( osPrefix + "IMAGE_MAP_PROJECTION.SECOND_STANDARD_PARALLEL")); + + /*** grab PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/ + // Need to further study how ocentric/ographic will effect the gdal library. + // So far we will use this fact to define a sphere or ellipse for some projections + // Frank - may need to talk this over + char bIsGeographic = TRUE; + value = GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.COORDINATE_SYSTEM_NAME"); + if (EQUAL( value, "PLANETOCENTRIC" )) + bIsGeographic = FALSE; + +/** Set oSRS projection and parameters --- all PDS supported types added if apparently supported in oSRS + "AITOFF", ** Not supported in GDAL?? + "ALBERS", + "BONNE", + "BRIESEMEISTER", ** Not supported in GDAL?? + "CYLINDRICAL EQUAL AREA", + "EQUIDISTANT", + "EQUIRECTANGULAR", + "GNOMONIC", + "HAMMER", ** Not supported in GDAL?? + "HENDU", ** Not supported in GDAL?? + "LAMBERT AZIMUTHAL EQUAL AREA", + "LAMBERT CONFORMAL", + "MERCATOR", + "MOLLWEIDE", + "OBLIQUE CYLINDRICAL", + "ORTHOGRAPHIC", + "SIMPLE CYLINDRICAL", + "SINUSOIDAL", + "STEREOGRAPHIC", + "TRANSVERSE MERCATOR", + "VAN DER GRINTEN", ** Not supported in GDAL?? + "WERNER" ** Not supported in GDAL?? +**/ + CPLDebug( "PDS","using projection %s\n\n", map_proj_name.c_str()); + + if ((EQUAL( map_proj_name, "EQUIRECTANGULAR" )) || + (EQUAL( map_proj_name, "SIMPLE_CYLINDRICAL" )) || + (EQUAL( map_proj_name, "EQUIDISTANT" )) ) { + oSRS.SetEquirectangular2 ( 0.0, center_lon, center_lat, 0, 0 ); + } else if (EQUAL( map_proj_name, "ORTHOGRAPHIC" )) { + oSRS.SetOrthographic ( center_lat, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "SINUSOIDAL" )) { + oSRS.SetSinusoidal ( center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "MERCATOR" )) { + oSRS.SetMercator ( center_lat, center_lon, 1, 0, 0 ); + } else if (EQUAL( map_proj_name, "STEREOGRAPHIC" )) { + oSRS.SetStereographic ( center_lat, center_lon, 1, 0, 0 ); + } else if (EQUAL( map_proj_name, "POLAR_STEREOGRAPHIC")) { + oSRS.SetPS ( center_lat, center_lon, 1, 0, 0 ); + } else if (EQUAL( map_proj_name, "TRANSVERSE_MERCATOR" )) { + oSRS.SetTM ( center_lat, center_lon, 1, 0, 0 ); + } else if (EQUAL( map_proj_name, "LAMBERT_CONFORMAL_CONIC" )) { + oSRS.SetLCC ( first_std_parallel, second_std_parallel, + center_lat, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "LAMBERT_AZIMUTHAL_EQUAL_AREA" )) { + oSRS.SetLAEA( center_lat, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "CYLINDRICAL_EQUAL_AREA" )) { + oSRS.SetCEA ( first_std_parallel, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "MOLLWEIDE" )) { + oSRS.SetMollweide ( center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "ALBERS" )) { + oSRS.SetACEA ( first_std_parallel, second_std_parallel, + center_lat, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "BONNE" )) { + oSRS.SetBonne ( first_std_parallel, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "GNOMONIC" )) { + oSRS.SetGnomonic ( center_lat, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "OBLIQUE_CYLINDRICAL" )) { + // hope Swiss Oblique Cylindrical is the same + oSRS.SetSOC ( center_lat, center_lon, 0, 0 ); + } else { + CPLDebug( "PDS", + "Dataset projection %s is not supported. Continuing...", + map_proj_name.c_str() ); + bProjectionSet = FALSE; + } + + if (bProjectionSet) { + //Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword + CPLString proj_target_name = map_proj_name + " " + target_name; + oSRS.SetProjCS(proj_target_name); //set ProjCS keyword + + //The geographic/geocentric name will be the same basic name as the body name + //'GCS' = Geographic/Geocentric Coordinate System + CPLString geog_name = "GCS_" + target_name; + + //The datum and sphere names will be the same basic name aas the planet + CPLString datum_name = "D_" + target_name; + CPLString sphere_name = target_name; // + "_IAU_IAG"); //Might not be IAU defined so don't add + + //calculate inverse flattening from major and minor axis: 1/f = a/(a-b) + if ((semi_major - semi_minor) < 0.0000001) + iflattening = 0; + else + iflattening = semi_major / (semi_major - semi_minor); + + //Set the body size but take into consideration which proj is being used to help w/ compatibility + //Notice that most PDS projections are spherical based on the fact that ISIS/PICS are spherical + //Set the body size but take into consideration which proj is being used to help w/ proj4 compatibility + //The use of a Sphere, polar radius or ellipse here is based on how ISIS does it internally + if ( ( (EQUAL( map_proj_name, "STEREOGRAPHIC" ) && (fabs(center_lat) == 90)) ) || + (EQUAL( map_proj_name, "POLAR_STEREOGRAPHIC" ))) + { + if (bIsGeographic) { + //Geograpraphic, so set an ellipse + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, iflattening, + "Reference_Meridian", 0.0 ); + } else { + //Geocentric, so force a sphere using the semi-minor axis. I hope... + sphere_name += "_polarRadius"; + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_minor, 0.0, + "Reference_Meridian", 0.0 ); + } + } + else if ( (EQUAL( map_proj_name, "SIMPLE_CYLINDRICAL" )) || + (EQUAL( map_proj_name, "EQUIDISTANT" )) || + (EQUAL( map_proj_name, "ORTHOGRAPHIC" )) || + (EQUAL( map_proj_name, "STEREOGRAPHIC" )) || + (EQUAL( map_proj_name, "SINUSOIDAL" )) ) { + //isis uses the spherical equation for these projections so force a sphere + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, 0.0, + "Reference_Meridian", 0.0 ); + } + else if (EQUAL( map_proj_name, "EQUIRECTANGULAR" )) { + //isis uses local radius as a sphere, which is pre-calculated in the PDS label as the semi-major + sphere_name += "_localRadius"; + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, 0.0, + "Reference_Meridian", 0.0 ); + } + else { + //All other projections: Mercator, Transverse Mercator, Lambert Conformal, etc. + //Geographic, so set an ellipse + if (bIsGeographic) { + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, iflattening, + "Reference_Meridian", 0.0 ); + } else { + //Geocentric, so force a sphere. I hope... + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, 0.0, + "Reference_Meridian", 0.0 ); + } + } + + // translate back into a projection string. + char *pszResult = NULL; + oSRS.exportToWkt( &pszResult ); + osProjection = pszResult; + CPLFree( pszResult ); + } + +/* ==================================================================== */ +/* Check for a .prj and world file to override the georeferencing. */ +/* ==================================================================== */ + { + CPLString osPath, osName; + VSILFILE *fp; + + osPath = CPLGetPath( pszFilename ); + osName = CPLGetBasename(pszFilename); + const char *pszPrjFile = CPLFormCIFilename( osPath, osName, "prj" ); + + fp = VSIFOpenL( pszPrjFile, "r" ); + if( fp != NULL ) + { + char **papszLines; + OGRSpatialReference oSRS; + + VSIFCloseL( fp ); + + papszLines = CSLLoad( pszPrjFile ); + + if( oSRS.importFromESRI( papszLines ) == OGRERR_NONE ) + { + char *pszResult = NULL; + oSRS.exportToWkt( &pszResult ); + osProjection = pszResult; + CPLFree( pszResult ); + } + + CSLDestroy( papszLines ); + } + } + + if( dfULYMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0 ) + { + bGotTransform = TRUE; + adfGeoTransform[0] = dfULXMap; + adfGeoTransform[1] = dfXDim; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = dfULYMap; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = dfYDim; + } + + if( !bGotTransform ) + bGotTransform = + GDALReadWorldFile( pszFilename, "psw", + adfGeoTransform ); + + if( !bGotTransform ) + bGotTransform = + GDALReadWorldFile( pszFilename, "wld", + adfGeoTransform ); + +} + +/************************************************************************/ +/* PDSConvertFromHex() */ +/************************************************************************/ + +static GUInt32 PDSConvertFromHex(const char* pszVal) +{ + if( !EQUALN(pszVal, "16#", 3) ) + return 0; + + pszVal += 3; + GUInt32 nVal = 0; + while( *pszVal != '#' && *pszVal != '\0' ) + { + nVal <<= 4; + if( *pszVal >= '0' && *pszVal <= '9' ) + nVal += *pszVal - '0'; + else if( *pszVal >= 'A' && *pszVal <= 'F' ) + nVal += *pszVal - 'A' + 10; + else + return 0; + pszVal ++; + } + return nVal; +} + +/************************************************************************/ +/* ParseImage() */ +/************************************************************************/ + +int PDSDataset::ParseImage( CPLString osPrefix, CPLString osFilenamePrefix ) +{ +/* ------------------------------------------------------------------- */ +/* We assume the user is pointing to the label (ie. .lbl) file. */ +/* ------------------------------------------------------------------- */ + // IMAGE can be inline or detached and point to an image name + // ^IMAGE = 3 + // ^IMAGE = "GLOBAL_ALBEDO_8PPD.IMG" + // ^IMAGE = "MEGT90N000CB.IMG" + // ^IMAGE = ("BLAH.IMG",1) -- start at record 1 (1 based) + // ^IMAGE = ("BLAH.IMG") -- still start at record 1 (equiv of "BLAH.IMG") + // ^IMAGE = ("BLAH.IMG", 5 ) -- start at byte 5 (the fifth byte in the file) + // ^IMAGE = 10851 + // ^SPECTRAL_QUBE = 5 for multi-band images + + CPLString osImageKeyword = "IMAGE"; + CPLString osQube = GetKeyword( osPrefix + "^" + osImageKeyword, "" ); + CPLString osTargetFile = GetDescription(); + + if (EQUAL(osQube,"")) { + osImageKeyword = "SPECTRAL_QUBE"; + osQube = GetKeyword( osPrefix + "^" + osImageKeyword ); + } + + int nQube = atoi(osQube); + int nDetachedOffset = 0; + int bDetachedOffsetInBytes = FALSE; + + if( osQube.size() && osQube[0] == '(' ) + { + osQube = "\""; + osQube += GetKeywordSub( osPrefix + "^" + osImageKeyword, 1 ); + osQube += "\""; + nDetachedOffset = atoi(GetKeywordSub( osPrefix + "^" + osImageKeyword, 2, "1")) - 1; + + // If this is not explicitly in bytes, then it is assumed to be in + // records, and we need to translate to bytes. + if (strstr(GetKeywordSub(osPrefix + "^" + osImageKeyword, 2),"") != NULL) + bDetachedOffsetInBytes = TRUE; + } + + if( osQube.size() && osQube[0] == '"' ) + { + CPLString osFilename = osQube; + CleanString( osFilename ); + if( osFilenamePrefix.size() ) + { + osTargetFile = osFilenamePrefix + osFilename; + } + else + { + CPLString osTPath = CPLGetPath(GetDescription()); + osTargetFile = CPLFormCIFilename( osTPath, osFilename, NULL ); + osExternalCube = osTargetFile; + } + } + + + GDALDataType eDataType = GDT_Byte; + + + //image parameters + int nRows, nCols, nBands = 1; + int nSkipBytes = 0; + int itype; + int record_bytes; + int nSuffixItems = 0, nSuffixLines = 0; + int nSuffixBytes = 4; // Default as per PDS specification + char chByteOrder = 'M'; //default to MSB + double dfNoData = 0.0, dfScale = 1.0, dfOffset = 0.0; + const char *pszUnit = NULL, *pszDesc = NULL; + + /* -------------------------------------------------------------------- */ + /* Checks to see if this is raw PDS image not compressed image */ + /* so ENCODING_TYPE either does not exist or it equals "N/A". */ + /* Compressed types will not be supported in this routine */ + /* -------------------------------------------------------------------- */ + CPLString value; + + CPLString osEncodingType = GetKeyword(osPrefix+"IMAGE.ENCODING_TYPE","N/A"); + CleanString(osEncodingType); + if ( !EQUAL(osEncodingType.c_str(),"N/A") ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "*** PDS image file has an ENCODING_TYPE parameter:\n" + "*** gdal pds driver does not support compressed image types\n" + "found: (%s)\n\n", osEncodingType.c_str() ); + return FALSE; + } + /**************** end ENCODING_TYPE check ***********************/ + + + /*********** Grab layout type (BSQ, BIP, BIL) ************/ + // AXIS_NAME = (SAMPLE,LINE,BAND) + /*********** Grab samples lines band **************/ + /** if AXIS_NAME = "" then Bands=1 and Sample and Lines **/ + /** are there own keywords "LINES" and "LINE_SAMPLES" **/ + /** if not NULL then CORE_ITEMS keyword i.e. (234,322,2) **/ + /***********************************************************/ + int eLayout = PDS_BSQ; //default to band seq. + value = GetKeyword( osPrefix+osImageKeyword+".AXIS_NAME", "" ); + if (EQUAL(value,"(SAMPLE,LINE,BAND)") ) { + eLayout = PDS_BSQ; + nCols = atoi(GetKeywordSub(osPrefix+osImageKeyword+".CORE_ITEMS",1)); + nRows = atoi(GetKeywordSub(osPrefix+osImageKeyword+".CORE_ITEMS",2)); + nBands = atoi(GetKeywordSub(osPrefix+osImageKeyword+".CORE_ITEMS",3)); + } + else if (EQUAL(value,"(BAND,LINE,SAMPLE)") ) { + eLayout = PDS_BIP; + nBands = atoi(GetKeywordSub(osPrefix+osImageKeyword+".CORE_ITEMS",1)); + nRows = atoi(GetKeywordSub(osPrefix+osImageKeyword+".CORE_ITEMS",2)); + nCols = atoi(GetKeywordSub(osPrefix+osImageKeyword+".CORE_ITEMS",3)); + } + else if (EQUAL(value,"(SAMPLE,BAND,LINE)") ) { + eLayout = PDS_BIL; + nCols = atoi(GetKeywordSub(osPrefix+osImageKeyword+".CORE_ITEMS",1)); + nBands = atoi(GetKeywordSub(osPrefix+osImageKeyword+".CORE_ITEMS",2)); + nRows = atoi(GetKeywordSub(osPrefix+osImageKeyword+".CORE_ITEMS",3)); + } + else if ( EQUAL(value,"") ) { + eLayout = PDS_BSQ; + nCols = atoi(GetKeyword(osPrefix+osImageKeyword+".LINE_SAMPLES","")); + nRows = atoi(GetKeyword(osPrefix+osImageKeyword+".LINES","")); + nBands = atoi(GetKeyword(osPrefix+osImageKeyword+".BANDS","1")); + } + else { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s layout not supported. Abort\n\n", value.c_str() ); + return FALSE; + } + + /*********** Grab Qube record bytes **********/ + record_bytes = atoi(GetKeyword(osPrefix+"IMAGE.RECORD_BYTES")); + if (record_bytes == 0) + record_bytes = atoi(GetKeyword(osPrefix+"RECORD_BYTES")); + + // this can happen with "record_type = undefined". + if( record_bytes == 0 ) + record_bytes = 1; + + if( nQube >0 && osQube.find("") != CPLString::npos ) + nSkipBytes = nQube - 1; + else if (nQube > 0 ) + nSkipBytes = (nQube - 1) * record_bytes; + else if( nDetachedOffset > 0 ) + { + if (bDetachedOffsetInBytes) + nSkipBytes = nDetachedOffset; + else + nSkipBytes = nDetachedOffset * record_bytes; + } + else + nSkipBytes = 0; + + nSkipBytes += atoi(GetKeyword(osPrefix+"IMAGE.LINE_PREFIX_BYTES","")); + + /*********** Grab SAMPLE_TYPE *****************/ + /** if keyword not found leave as "M" or "MSB" **/ + CPLString osST = GetKeyword( osPrefix+"IMAGE.SAMPLE_TYPE" ); + if( osST.size() >= 2 && osST[0] == '"' && osST[osST.size()-1] == '"' ) + osST = osST.substr( 1, osST.size() - 2 ); + + if( (EQUAL(osST,"LSB_INTEGER")) || + (EQUAL(osST,"LSB")) || // just incase + (EQUAL(osST,"LSB_UNSIGNED_INTEGER")) || + (EQUAL(osST,"LSB_SIGNED_INTEGER")) || + (EQUAL(osST,"UNSIGNED_INTEGER")) || + (EQUAL(osST,"VAX_REAL")) || + (EQUAL(osST,"VAX_INTEGER")) || + (EQUAL(osST,"PC_INTEGER")) || //just incase + (EQUAL(osST,"PC_REAL")) ) { + chByteOrder = 'I'; + } + + /**** Grab format type - pds supports 1,2,4,8,16,32,64 (in theory) **/ + /**** I have only seen 8, 16, 32 (float) in released datasets **/ + CPLString osSB = GetKeyword(osPrefix+"IMAGE.SAMPLE_BITS",""); + if ( osSB.size() > 0 ) + { + itype = atoi(osSB); + switch(itype) { + case 8 : + eDataType = GDT_Byte; + dfNoData = NULL1; + break; + case 16 : + if( strstr(osST,"UNSIGNED") != NULL ) + eDataType = GDT_UInt16; + else + eDataType = GDT_Int16; + dfNoData = NULL2; + break; + case 32 : + eDataType = GDT_Float32; + dfNoData = NULL3; + break; + case 64 : + eDataType = GDT_Float64; + dfNoData = NULL3; + break; + default : + CPLError( CE_Failure, CPLE_AppDefined, + "Sample_bits of %d is not supported in this gdal PDS reader.", + itype); + return FALSE; + } + + dfOffset = CPLAtofM(GetKeyword(osPrefix+"IMAGE.OFFSET","0.0")); + dfScale = CPLAtofM(GetKeyword(osPrefix+"IMAGE.SCALING_FACTOR","1.0")); + } + else /* No IMAGE object, search for the QUBE. */ + { + osSB = GetKeyword(osPrefix+"SPECTRAL_QUBE.CORE_ITEM_BYTES",""); + itype = atoi(osSB); + switch(itype) { + case 1 : + eDataType = GDT_Byte; + break; + case 2 : + if( strstr(osST,"UNSIGNED") != NULL ) + eDataType = GDT_UInt16; + else + eDataType = GDT_Int16; + break; + case 4 : + eDataType = GDT_Float32; + break; + default : + CPLError( CE_Failure, CPLE_AppDefined, + "CORE_ITEM_BYTES of %d is not supported in this gdal PDS reader.", + itype); + return FALSE; + } + + /* Parse suffix dimensions if defined. */ + value = GetKeyword( osPrefix + "SPECTRAL_QUBE.SUFFIX_ITEMS", "" ); + if ( value.size() > 0 ) + { + value = GetKeyword(osPrefix + "SPECTRAL_QUBE.SUFFIX_BYTES", ""); + if ( value.size() > 0 ) + nSuffixBytes = atoi( value ); + + nSuffixItems = atoi( + GetKeywordSub(osPrefix+"SPECTRAL_QUBE.SUFFIX_ITEMS",1)); + nSuffixLines = atoi( + GetKeywordSub(osPrefix+"SPECTRAL_QUBE.SUFFIX_ITEMS",2)); + } + + value = GetKeyword( osPrefix + "SPECTRAL_QUBE.CORE_NULL", "" ); + if ( value.size() > 0 ) + dfNoData = CPLAtofM( value ); + + dfOffset = CPLAtofM( + GetKeyword(osPrefix + "SPECTRAL_QUBE.CORE_BASE", "0.0") ); + dfScale = CPLAtofM( + GetKeyword(osPrefix + "SPECTRAL_QUBE.CORE_MULTIPLIER", "1.0") ); + pszUnit = GetKeyword(osPrefix + "SPECTRAL_QUBE.CORE_UNIT", NULL); + pszDesc = GetKeyword(osPrefix + "SPECTRAL_QUBE.CORE_NAME", NULL); + } + +/* -------------------------------------------------------------------- */ +/* Is there a specific nodata value in the file? Either the */ +/* MISSING or MISSING_CONSTANT keywords are nodata. */ +/* -------------------------------------------------------------------- */ + + const char* pszMissing = GetKeyword( osPrefix+"IMAGE.MISSING", NULL ); + if( pszMissing == NULL ) + pszMissing = GetKeyword( osPrefix+"IMAGE.MISSING_CONSTANT", NULL ); + + if( pszMissing != NULL ) + { + if( *pszMissing == '"' ) + pszMissing ++; + + /* For example : MISSING_CONSTANT = "16#FF7FFFFB#" */ + if( EQUALN(pszMissing, "16#", 3) && strlen(pszMissing) >= 3 + 8 + 1 && + pszMissing[3 + 8] == '#' && (eDataType == GDT_Float32 || eDataType == GDT_Float64) ) + { + GUInt32 nVal = PDSConvertFromHex(pszMissing); + float fVal; + memcpy(&fVal, &nVal, 4); + dfNoData = fVal; + } + else + dfNoData = CPLAtofM( pszMissing ); + } + +/* -------------------------------------------------------------------- */ +/* Did we get the required keywords? If not we return with */ +/* this never having been considered to be a match. This isn't */ +/* an error! */ +/* -------------------------------------------------------------------- */ + if( nRows < 1 || nCols < 1 || nBands < 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "File %s appears to be a PDS file, but failed to find some required keywords.", + GetDescription() ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + nRasterXSize = nCols; + nRasterYSize = nRows; + +/* -------------------------------------------------------------------- */ +/* Open target binary file. */ +/* -------------------------------------------------------------------- */ + + if( eAccess == GA_ReadOnly ) + { + fpImage = VSIFOpenL( osTargetFile, "rb" ); + if( fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s.\n%s", + osTargetFile.c_str(), + VSIStrerror( errno ) ); + return FALSE; + } + } + else + { + fpImage = VSIFOpenL( osTargetFile, "r+b" ); + if( fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s with write permission.\n%s", + osTargetFile.c_str(), + VSIStrerror( errno ) ); + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Compute the line offset. */ +/* -------------------------------------------------------------------- */ + int nItemSize = GDALGetDataTypeSize(eDataType)/8; + int nLineOffset = record_bytes; + int nPixelOffset, nBandOffset; + + if( eLayout == PDS_BIP ) + { + nPixelOffset = nItemSize * nBands; + nBandOffset = nItemSize; + nLineOffset = ((nPixelOffset * nCols + record_bytes - 1)/record_bytes) + * record_bytes; + } + else if( eLayout == PDS_BSQ ) + { + nPixelOffset = nItemSize; + nLineOffset = ((nPixelOffset * nCols + record_bytes - 1)/record_bytes) + * record_bytes; + nBandOffset = nLineOffset * nRows + + nSuffixLines * (nCols + nSuffixItems) * nSuffixBytes; + } + else /* assume BIL */ + { + nPixelOffset = nItemSize; + nBandOffset = nItemSize * nCols; + nLineOffset = ((nBandOffset * nCols + record_bytes - 1)/record_bytes) + * record_bytes; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nBands; i++ ) + { + RawRasterBand *poBand; + + poBand = + new RawRasterBand( this, i+1, fpImage, + nSkipBytes + nBandOffset * i, + nPixelOffset, nLineOffset, eDataType, +#ifdef CPL_LSB + chByteOrder == 'I' || chByteOrder == 'L', +#else + chByteOrder == 'M', +#endif + TRUE ); + + if( nBands == 1 ) + { + const char* pszMin = GetKeyword(osPrefix+"IMAGE.MINIMUM", NULL); + const char* pszMax = GetKeyword(osPrefix+"IMAGE.MAXIMUM", NULL); + const char* pszMean = GetKeyword(osPrefix+"IMAGE.MEAN", NULL); + const char* pszStdDev= GetKeyword(osPrefix+"IMAGE.STANDARD_DEVIATION", NULL); + if (pszMin != NULL && pszMax != NULL && + pszMean != NULL && pszStdDev != NULL) + { + poBand->SetStatistics( CPLAtofM(pszMin), + CPLAtofM(pszMax), + CPLAtofM(pszMean), + CPLAtofM(pszStdDev)); + } + } + + poBand->SetNoDataValue( dfNoData ); + + SetBand( i+1, poBand ); + + // Set offset/scale values at the PAM level. + poBand->SetOffset( dfOffset ); + poBand->SetScale( dfScale ); + if ( pszUnit ) + poBand->SetUnitType( pszUnit ); + if ( pszDesc ) + poBand->SetDescription( pszDesc ); + } + + return TRUE; +} + +/************************************************************************/ +/* ==================================================================== */ +/* PDSWrapperRasterBand */ +/* */ +/* proxy for the jp2 or other compressed bands. */ +/* ==================================================================== */ +/************************************************************************/ +class PDSWrapperRasterBand : public GDALProxyRasterBand +{ + GDALRasterBand* poBaseBand; + + protected: + virtual GDALRasterBand* RefUnderlyingRasterBand() { return poBaseBand; } + + public: + PDSWrapperRasterBand( GDALRasterBand* poBaseBand ) + { + this->poBaseBand = poBaseBand; + eDataType = poBaseBand->GetRasterDataType(); + poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + } + ~PDSWrapperRasterBand() {} +}; + +/************************************************************************/ +/* ParseCompressedImage() */ +/************************************************************************/ + +int PDSDataset::ParseCompressedImage() + +{ + CPLString osFileName = GetKeyword( "COMPRESSED_FILE.FILE_NAME", "" ); + CleanString( osFileName ); + + CPLString osPath = CPLGetPath(GetDescription()); + CPLString osFullFileName = CPLFormFilename( osPath, osFileName, NULL ); + int iBand; + + poCompressedDS = (GDALDataset*) GDALOpen( osFullFileName, GA_ReadOnly ); + + if( poCompressedDS == NULL ) + return FALSE; + + nRasterXSize = poCompressedDS->GetRasterXSize(); + nRasterYSize = poCompressedDS->GetRasterYSize(); + + for( iBand = 0; iBand < poCompressedDS->GetRasterCount(); iBand++ ) + { + SetBand( iBand+1, new PDSWrapperRasterBand( poCompressedDS->GetRasterBand( iBand+1 ) ) ); + } + + return TRUE; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int PDSDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->pabyHeader == NULL ) + return FALSE; + + return strstr((char*)poOpenInfo->pabyHeader,"PDS_VERSION_ID") != NULL; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *PDSDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + if( !Identify( poOpenInfo ) ) + return NULL; + + if( strstr((const char *)poOpenInfo->pabyHeader,"PDS3") == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "It appears this is an older PDS image type. Only PDS_VERSION_ID = PDS3 are currently supported by this gdal PDS reader."); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open and parse the keyword header. Sometimes there is stuff */ +/* before the PDS_VERSION_ID, which we want to ignore. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpQube = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + + if( fpQube == NULL ) + return NULL; + + PDSDataset *poDS; + + poDS = new PDSDataset(); + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->eAccess = poOpenInfo->eAccess; + + const char* pszPDSVersionID = strstr((const char *)poOpenInfo->pabyHeader,"PDS_VERSION_ID"); + int nOffset = 0; + if (pszPDSVersionID) + nOffset = pszPDSVersionID - (const char *)poOpenInfo->pabyHeader; + + if( ! poDS->oKeywords.Ingest( fpQube, nOffset ) ) + { + delete poDS; + VSIFCloseL( fpQube ); + return NULL; + } + VSIFCloseL( fpQube ); + +/* -------------------------------------------------------------------- */ +/* Is this a compressed image with COMPRESSED_FILE subdomain? */ +/* */ +/* The corresponding parse operations will read keywords, */ +/* establish bands and raster size. */ +/* -------------------------------------------------------------------- */ + CPLString osEncodingType = poDS->GetKeyword( "COMPRESSED_FILE.ENCODING_TYPE", "" ); + + CPLString osCompressedFilename = poDS->GetKeyword( "COMPRESSED_FILE.FILE_NAME", "" ); + CleanString( osCompressedFilename ); + + CPLString osUncompressedFilename = poDS->GetKeyword( "UNCOMPRESSED_FILE.IMAGE.NAME", ""); + if( osUncompressedFilename.size() == 0 ) + osUncompressedFilename = poDS->GetKeyword( "UNCOMPRESSED_FILE.FILE_NAME", ""); + CleanString( osUncompressedFilename ); + + VSIStatBufL sStat; + CPLString osFilenamePrefix; + + if( EQUAL(osEncodingType, "ZIP") && + osCompressedFilename.size() != 0 && + osUncompressedFilename.size() != 0 ) + { + CPLString osPath = CPLGetPath(poDS->GetDescription()); + osCompressedFilename = CPLFormFilename( osPath, osCompressedFilename, NULL ); + osUncompressedFilename = CPLFormFilename( osPath, osUncompressedFilename, NULL ); + if( VSIStatExL(osCompressedFilename, &sStat, VSI_STAT_EXISTS_FLAG) == 0 && + VSIStatExL(osUncompressedFilename, &sStat, VSI_STAT_EXISTS_FLAG) != 0 ) + { + osFilenamePrefix = "/vsizip/" + osCompressedFilename + "/"; + poDS->osExternalCube = osCompressedFilename; + } + osEncodingType = ""; + } + + if( osEncodingType.size() != 0 ) + { + if( !poDS->ParseCompressedImage() ) + { + delete poDS; + return NULL; + } + } + else + { + CPLString osPrefix; + + if( osUncompressedFilename != "" ) + osPrefix = "UNCOMPRESSED_FILE."; + + if( !poDS->ParseImage(osPrefix, osFilenamePrefix) ) + { + delete poDS; + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Set the coordinate system and geotransform. */ +/* -------------------------------------------------------------------- */ + poDS->ParseSRS(); + +/* -------------------------------------------------------------------- */ +/* Transfer a few interesting keywords as metadata. */ +/* -------------------------------------------------------------------- */ + int i; + static const char *apszKeywords[] = + { "FILTER_NAME", "DATA_SET_ID", "PRODUCT_ID", + "PRODUCER_INSTITUTION_NAME", "PRODUCT_TYPE", "MISSION_NAME", + "SPACECRAFT_NAME", "INSTRUMENT_NAME", "INSTRUMENT_ID", + "TARGET_NAME", "CENTER_FILTER_WAVELENGTH", "BANDWIDTH", + "PRODUCT_CREATION_TIME", "NOTE", + NULL }; + + for( i = 0; apszKeywords[i] != NULL; i++ ) + { + const char *pszKeywordValue = poDS->GetKeyword( apszKeywords[i] ); + + if( pszKeywordValue != NULL ) + poDS->SetMetadataItem( apszKeywords[i], pszKeywordValue ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GetKeyword() */ +/************************************************************************/ + +const char *PDSDataset::GetKeyword( std::string osPath, + const char *pszDefault ) + +{ + return oKeywords.GetKeyword( osPath.c_str(), pszDefault ); +} + +/************************************************************************/ +/* GetKeywordSub() */ +/************************************************************************/ + +const char *PDSDataset::GetKeywordSub( std::string osPath, + int iSubscript, + const char *pszDefault ) + +{ + const char *pszResult = oKeywords.GetKeyword( osPath.c_str(), NULL ); + + if( pszResult == NULL ) + return pszDefault; + + if( pszResult[0] != '(' ) + return pszDefault; + + char **papszTokens = CSLTokenizeString2( pszResult, "(,)", + CSLT_HONOURSTRINGS ); + + if( iSubscript <= CSLCount(papszTokens) ) + { + osTempResult = papszTokens[iSubscript-1]; + CSLDestroy( papszTokens ); + return osTempResult.c_str(); + } + else + { + CSLDestroy( papszTokens ); + return pszDefault; + } +} + +/************************************************************************/ +/* GetKeywordUnit() */ +/************************************************************************/ + +const char *PDSDataset::GetKeywordUnit( const char *pszPath, + int iSubscript, + const char *pszDefault ) + +{ + const char *pszResult = oKeywords.GetKeyword( pszPath, NULL ); + + if( pszResult == NULL ) + return pszDefault; + + char **papszTokens = CSLTokenizeString2( pszResult, "", + CSLT_HONOURSTRINGS ); + + if( iSubscript <= CSLCount(papszTokens) ) + { + osTempResult = papszTokens[iSubscript-1]; + CSLDestroy( papszTokens ); + return osTempResult.c_str(); + } + else + { + CSLDestroy( papszTokens ); + return pszDefault; + } +} + +/************************************************************************/ +/* CleanString() */ +/* */ +/* Removes single or double quotes, and converts spaces to underscores. */ +/* The change is made in-place to CPLString. */ +/************************************************************************/ + +void PDSDataset::CleanString( CPLString &osInput ) + +{ + if( ( osInput.size() < 2 ) || + ((osInput.at(0) != '"' || osInput.at(osInput.size()-1) != '"' ) && + ( osInput.at(0) != '\'' || osInput.at(osInput.size()-1) != '\'')) ) + return; + + char *pszWrk = CPLStrdup(osInput.c_str() + 1); + int i; + + pszWrk[strlen(pszWrk)-1] = '\0'; + + for( i = 0; pszWrk[i] != '\0'; i++ ) + { + if( pszWrk[i] == ' ' ) + pszWrk[i] = '_'; + } + + osInput = pszWrk; + CPLFree( pszWrk ); +} +/************************************************************************/ +/* GDALRegister_PDS() */ +/************************************************************************/ + +void GDALRegister_PDS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "PDS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "PDS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "NASA Planetary Data System" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_pds.html" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = PDSDataset::Open; + poDriver->pfnIdentify = PDSDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/pds/vicardataset.cpp b/bazaar/plugin/gdal/frmts/pds/vicardataset.cpp new file mode 100644 index 000000000..e110612cb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/vicardataset.cpp @@ -0,0 +1,835 @@ +/****************************************************************************** + * + * Project: VICAR Driver; JPL/MIPL VICAR Format + * Purpose: Implementation of VICARDataset + * Author: Sebastian Walter + * + * NOTE: This driver code is loosely based on the ISIS and PDS drivers. + * It is not intended to diminish the contribution of the original authors + ****************************************************************************** + * Copyright (c) 2014, Sebastian Walter + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#define NULL1 0 +#define NULL2 -32768 +#define NULL3 -32768. + +#ifndef PI +# define PI 3.1415926535897932384626433832795 +#endif + +#include "rawdataset.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include "vicarkeywordhandler.h" + +#include + +CPL_CVSID("$Id: vicardataset.cpp 28786 2015-03-26 21:13:18Z rouault $"); + +CPL_C_START +void GDALRegister_VICAR(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* VICARDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class VICARDataset : public RawDataset +{ + VSILFILE *fpImage; + + GByte abyHeader[10000]; + CPLString osExternalCube; + + VICARKeywordHandler oKeywords; + + int bGotTransform; + double adfGeoTransform[6]; + + CPLString osProjection; + + const char *GetKeyword( const char *pszPath, + const char *pszDefault = ""); + +public: + VICARDataset(); + ~VICARDataset(); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual const char *GetProjectionRef(void); + + virtual char **GetFileList(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); + +}; + + + +/************************************************************************/ +/* VICARDataset() */ +/************************************************************************/ + +VICARDataset::VICARDataset() +{ + fpImage = NULL; + bGotTransform = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~VICARDataset() */ +/************************************************************************/ + +VICARDataset::~VICARDataset() + +{ + FlushCache(); + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **VICARDataset::GetFileList() + +{ + char **papszFileList = NULL; + + papszFileList = GDALPamDataset::GetFileList(); + + if( strlen(osExternalCube) > 0 ) + papszFileList = CSLAddString( papszFileList, osExternalCube ); + + return papszFileList; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *VICARDataset::GetProjectionRef() + +{ + if( strlen(osProjection) > 0 ) + return osProjection; + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr VICARDataset::GetGeoTransform( double * padfTransform ) + +{ + if( bGotTransform ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; + } + else + { + return GDALPamDataset::GetGeoTransform( padfTransform ); + } +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int VICARDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->pabyHeader == NULL ) + return FALSE; + + return strstr((char*)poOpenInfo->pabyHeader,"LBLSIZE") != NULL && + strstr((char*)poOpenInfo->pabyHeader,"FORMAT") != NULL && + strstr((char*)poOpenInfo->pabyHeader,"NL") != NULL && + strstr((char*)poOpenInfo->pabyHeader,"NS") != NULL && + strstr((char*)poOpenInfo->pabyHeader,"NB") != NULL; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *VICARDataset::Open( GDALOpenInfo * poOpenInfo ) +{ +/* -------------------------------------------------------------------- */ +/* Does this look like a VICAR dataset? */ +/* -------------------------------------------------------------------- */ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Open the file using the large file API. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpQube = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + + if( fpQube == NULL ) + return NULL; + + VICARDataset *poDS; + + poDS = new VICARDataset(); + if( ! poDS->oKeywords.Ingest( fpQube, poOpenInfo->pabyHeader ) ) { + VSIFCloseL( fpQube ); + delete poDS; + return NULL; + } + + VSIFCloseL( fpQube ); + + CPLString osQubeFile; + osQubeFile = poOpenInfo->pszFilename; + GDALDataType eDataType = GDT_Byte; + + int nRows = -1; + int nCols = -1; + int nBands = 1; + int nSkipBytes = 0; + int bIsDTM = FALSE; + char chByteOrder = 'M'; + double dfNoData = 0.0; + const char *value; + + /***** CHECK ENDIANNESS **************/ + + value = poDS->GetKeyword( "INTFMT" ); + if (!EQUAL(value,"LOW") ) { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s layout not supported. Abort\n\n", value); + return FALSE; + } + value = poDS->GetKeyword( "REALFMT" ); + if (!EQUAL(value,"RIEEE") ) { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s layout not supported. Abort\n\n", value); + return FALSE; + } + value = poDS->GetKeyword( "BREALFMT" ); + if (EQUAL(value,"VAX") ) { + chByteOrder = 'I'; + } + + /************ CHECK INSTRUMENT *****************/ + /************ ONLY HRSC TESTED *****************/ + + value = poDS->GetKeyword( "DTM.DTM_OFFSET" ); + if (!EQUAL(value,"") ) { + bIsDTM = TRUE; + } + + + value = poDS->GetKeyword( "BLTYPE" ); + if (!EQUAL(value,"M94_HRSC") && bIsDTM==FALSE ) { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s instrument not tested. Continue with caution!\n\n", value); + } + + /*********** Grab layout type (BSQ, BIP, BIL) ************/ + + char szLayout[10] = "BSQ"; //default to band seq. + value = poDS->GetKeyword( "ORG" ); + if (EQUAL(value,"BSQ") ) { + strcpy(szLayout,"BSQ"); + nCols = atoi(poDS->GetKeyword("NS")); + nRows = atoi(poDS->GetKeyword("NL")); + nBands = atoi(poDS->GetKeyword("NB")); + } + else { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s layout not supported. Abort\n\n", value); + return FALSE; + } + + /*********** Grab record bytes **********/ + nSkipBytes = atoi(poDS->GetKeyword("NBB")); + + if (EQUAL( poDS->GetKeyword( "FORMAT" ), "BYTE" )) { + eDataType = GDT_Byte; + dfNoData = NULL1; + } + else if (EQUAL( poDS->GetKeyword( "FORMAT" ), "HALF" )) { + eDataType = GDT_Int16; + dfNoData = NULL2; + chByteOrder = 'I'; + } + else if (EQUAL( poDS->GetKeyword( "FORMAT" ), "FULL" )) { + eDataType = GDT_UInt32; + dfNoData = 0; + } + else if (EQUAL( poDS->GetKeyword( "FORMAT" ), "REAL" )) { + eDataType = GDT_Float32; + dfNoData = NULL3; + chByteOrder = 'I'; + } + else { + printf("Could not find known VICAR label entries!\n"); + delete poDS; + return NULL; + } + + if( nRows < 1 || nCols < 1 || nBands < 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "File %s appears to be a VICAR file, but failed to find some required keywords.", + poDS->GetDescription() ); + return FALSE; + } +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = nCols; + poDS->nRasterYSize = nRows; + + double dfULXMap=0.5; + double dfULYMap = 0.5; + double dfXDim = 1.0; + double dfYDim = 1.0; + double xulcenter = 0.0; + double yulcenter = 0.0; + + value = poDS->GetKeyword("MAP.MAP_SCALE"); + if (strlen(value) > 0 ) { + dfXDim = CPLAtof(value); + dfYDim = CPLAtof(value) * -1; + dfXDim = dfXDim * 1000.0; + dfYDim = dfYDim * 1000.0; + } + + double dfSampleOffset_Shift; + double dfLineOffset_Shift; + double dfSampleOffset_Mult; + double dfLineOffset_Mult; + + dfSampleOffset_Shift = + CPLAtof(CPLGetConfigOption( "PDS_SampleProjOffset_Shift", "-0.5" )); + + dfLineOffset_Shift = + CPLAtof(CPLGetConfigOption( "PDS_LineProjOffset_Shift", "-0.5" )); + + dfSampleOffset_Mult = + CPLAtof(CPLGetConfigOption( "PDS_SampleProjOffset_Mult", "-1.0") ); + + dfLineOffset_Mult = + CPLAtof( CPLGetConfigOption( "PDS_LineProjOffset_Mult", "1.0") ); + + /*********** Grab LINE_PROJECTION_OFFSET ************/ + value = poDS->GetKeyword("MAP.LINE_PROJECTION_OFFSET"); + if (strlen(value) > 0) { + yulcenter = CPLAtof(value); + dfULYMap = ((yulcenter + dfLineOffset_Shift) * -dfYDim * dfLineOffset_Mult); + } + /*********** Grab SAMPLE_PROJECTION_OFFSET ************/ + value = poDS->GetKeyword("MAP.SAMPLE_PROJECTION_OFFSET"); + if( strlen(value) > 0 ) { + xulcenter = CPLAtof(value); + dfULXMap = ((xulcenter + dfSampleOffset_Shift) * dfXDim * dfSampleOffset_Mult); + } + +/* ==================================================================== */ +/* Get the coordinate system. */ +/* ==================================================================== */ + int bProjectionSet = TRUE; + double semi_major = 0.0; + double semi_minor = 0.0; + double iflattening = 0.0; + double center_lat = 0.0; + double center_lon = 0.0; + double first_std_parallel = 0.0; + double second_std_parallel = 0.0; + OGRSpatialReference oSRS; + + /*********** Grab TARGET_NAME ************/ + /**** This is the planets name i.e. MARS ***/ + CPLString target_name = poDS->GetKeyword("MAP.TARGET_NAME"); + + /********** Grab MAP_PROJECTION_TYPE *****/ + CPLString map_proj_name = + poDS->GetKeyword( "MAP.MAP_PROJECTION_TYPE"); + + /****** Grab semi_major & convert to KM ******/ + semi_major = + CPLAtof(poDS->GetKeyword( "MAP.A_AXIS_RADIUS")) * 1000.0; + + /****** Grab semi-minor & convert to KM ******/ + semi_minor = + CPLAtof(poDS->GetKeyword( "MAP.C_AXIS_RADIUS")) * 1000.0; + + /*********** Grab CENTER_LAT ************/ + center_lat = + CPLAtof(poDS->GetKeyword( "MAP.CENTER_LATITUDE")); + + /*********** Grab CENTER_LON ************/ + center_lon = + CPLAtof(poDS->GetKeyword( "MAP.CENTER_LONGITUDE")); + + /********** Grab 1st std parallel *******/ + first_std_parallel = + CPLAtof(poDS->GetKeyword( "MAP.FIRST_STANDARD_PARALLEL")); + + /********** Grab 2nd std parallel *******/ + second_std_parallel = + CPLAtof(poDS->GetKeyword( "MAP.SECOND_STANDARD_PARALLEL")); + + /*** grab PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/ + // Need to further study how ocentric/ographic will effect the gdal library. + // So far we will use this fact to define a sphere or ellipse for some projections + // Frank - may need to talk this over + char bIsGeographic = TRUE; + value = poDS->GetKeyword("MAP.COORDINATE_SYSTEM_NAME"); + if (EQUAL( value, "PLANETOCENTRIC" )) + bIsGeographic = FALSE; + +/** Set oSRS projection and parameters --- all PDS supported types added if apparently supported in oSRS + "AITOFF", ** Not supported in GDAL?? + "ALBERS", + "BONNE", + "BRIESEMEISTER", ** Not supported in GDAL?? + "CYLINDRICAL EQUAL AREA", + "EQUIDISTANT", + "EQUIRECTANGULAR", + "GNOMONIC", + "HAMMER", ** Not supported in GDAL?? + "HENDU", ** Not supported in GDAL?? + "LAMBERT AZIMUTHAL EQUAL AREA", + "LAMBERT CONFORMAL", + "MERCATOR", + "MOLLWEIDE", + "OBLIQUE CYLINDRICAL", + "ORTHOGRAPHIC", + "SIMPLE CYLINDRICAL", + "SINUSOIDAL", + "STEREOGRAPHIC", + "TRANSVERSE MERCATOR", + "VAN DER GRINTEN", ** Not supported in GDAL?? + "WERNER" ** Not supported in GDAL?? +**/ + CPLDebug( "PDS","using projection %s\n\n", map_proj_name.c_str()); + + if ((EQUAL( map_proj_name, "EQUIRECTANGULAR" )) || + (EQUAL( map_proj_name, "SIMPLE_CYLINDRICAL" )) || + (EQUAL( map_proj_name, "EQUIDISTANT" )) ) { + oSRS.SetEquirectangular2 ( 0.0, center_lon, center_lat, 0, 0 ); + } else if (EQUAL( map_proj_name, "ORTHOGRAPHIC" )) { + oSRS.SetOrthographic ( center_lat, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "SINUSOIDAL" )) { + oSRS.SetSinusoidal ( center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "MERCATOR" )) { + oSRS.SetMercator ( center_lat, center_lon, 1, 0, 0 ); + } else if (EQUAL( map_proj_name, "STEREOGRAPHIC" )) { + oSRS.SetStereographic ( center_lat, center_lon, 1, 0, 0 ); + } else if (EQUAL( map_proj_name, "POLAR_STEREOGRAPHIC")) { + oSRS.SetPS ( center_lat, center_lon, 1, 0, 0 ); + } else if (EQUAL( map_proj_name, "TRANSVERSE_MERCATOR" )) { + oSRS.SetTM ( center_lat, center_lon, 1, 0, 0 ); + } else if (EQUAL( map_proj_name, "LAMBERT_CONFORMAL_CONIC" )) { + oSRS.SetLCC ( first_std_parallel, second_std_parallel, + center_lat, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "LAMBERT_AZIMUTHAL_EQUAL_AREA" )) { + oSRS.SetLAEA( center_lat, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "CYLINDRICAL_EQUAL_AREA" )) { + oSRS.SetCEA ( first_std_parallel, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "MOLLWEIDE" )) { + oSRS.SetMollweide ( center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "ALBERS" )) { + oSRS.SetACEA ( first_std_parallel, second_std_parallel, + center_lat, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "BONNE" )) { + oSRS.SetBonne ( first_std_parallel, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "GNOMONIC" )) { + oSRS.SetGnomonic ( center_lat, center_lon, 0, 0 ); + } else if (EQUAL( map_proj_name, "OBLIQUE_CYLINDRICAL" )) { + // hope Swiss Oblique Cylindrical is the same + oSRS.SetSOC ( center_lat, center_lon, 0, 0 ); + } else { + CPLDebug( "VICAR", + "Dataset projection %s is not supported. Continuing...", + map_proj_name.c_str() ); + bProjectionSet = FALSE; + } + + if (bProjectionSet) { + //Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword + CPLString proj_target_name = map_proj_name + " " + target_name; + oSRS.SetProjCS(proj_target_name); //set ProjCS keyword + + //The geographic/geocentric name will be the same basic name as the body name + //'GCS' = Geographic/Geocentric Coordinate System + CPLString geog_name = "GCS_" + target_name; + + //The datum and sphere names will be the same basic name aas the planet + CPLString datum_name = "D_" + target_name; + CPLString sphere_name = target_name; // + "_IAU_IAG"); //Might not be IAU defined so don't add + + //calculate inverse flattening from major and minor axis: 1/f = a/(a-b) + if ((semi_major - semi_minor) < 0.0000001) + iflattening = 0; + else + iflattening = semi_major / (semi_major - semi_minor); + + //Set the body size but take into consideration which proj is being used to help w/ compatibility + //Notice that most PDS projections are spherical based on the fact that ISIS/PICS are spherical + //Set the body size but take into consideration which proj is being used to help w/ proj4 compatibility + //The use of a Sphere, polar radius or ellipse here is based on how ISIS does it internally + if ( ( (EQUAL( map_proj_name, "STEREOGRAPHIC" ) && (fabs(center_lat) == 90)) ) || + (EQUAL( map_proj_name, "POLAR_STEREOGRAPHIC" ))) + { + if (bIsGeographic) { + //Geograpraphic, so set an ellipse + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, iflattening, + "Reference_Meridian", 0.0 ); + } else { + //Geocentric, so force a sphere using the semi-minor axis. I hope... + sphere_name += "_polarRadius"; + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_minor, 0.0, + "Reference_Meridian", 0.0 ); + } + } + else if ( (EQUAL( map_proj_name, "SIMPLE_CYLINDRICAL" )) || + (EQUAL( map_proj_name, "EQUIDISTANT" )) || + (EQUAL( map_proj_name, "ORTHOGRAPHIC" )) || + (EQUAL( map_proj_name, "STEREOGRAPHIC" )) || + (EQUAL( map_proj_name, "SINUSOIDAL" )) ) { + //isis uses the spherical equation for these projections so force a sphere + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, 0.0, + "Reference_Meridian", 0.0 ); + } + else if (EQUAL( map_proj_name, "EQUIRECTANGULAR" )) { + //isis uses local radius as a sphere, which is pre-calculated in the PDS label as the semi-major + sphere_name += "_localRadius"; + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, 0.0, + "Reference_Meridian", 0.0 ); + } + else { + //All other projections: Mercator, Transverse Mercator, Lambert Conformal, etc. + //Geographic, so set an ellipse + if (bIsGeographic) { + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, iflattening, + "Reference_Meridian", 0.0 ); + } else { + //Geocentric, so force a sphere. I hope... + oSRS.SetGeogCS( geog_name, datum_name, sphere_name, + semi_major, 0.0, + "Reference_Meridian", 0.0 ); + } + } + + // translate back into a projection string. + char *pszResult = NULL; + oSRS.exportToWkt( &pszResult ); + poDS->osProjection = pszResult; + CPLFree( pszResult ); + } + { + poDS->bGotTransform = TRUE; + poDS->adfGeoTransform[0] = dfULXMap; + poDS->adfGeoTransform[1] = dfXDim; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = dfULYMap; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = dfYDim; + } + + if( !poDS->bGotTransform ) + poDS->bGotTransform = + GDALReadWorldFile( osQubeFile, "psw", + poDS->adfGeoTransform ); + + if( !poDS->bGotTransform ) + poDS->bGotTransform = + GDALReadWorldFile( osQubeFile, "wld", + poDS->adfGeoTransform ); + + +/* -------------------------------------------------------------------- */ +/* Open target binary file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fpImage = VSIFOpenL( osQubeFile, "r" ); + else + poDS->fpImage = VSIFOpenL( osQubeFile, "r+" ); + + if( poDS->fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s with write permission.\n%s", + osQubeFile.c_str(), + VSIStrerror( errno ) ); + delete poDS; + return NULL; + } + + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Compute the line offsets. */ +/* -------------------------------------------------------------------- */ + + long int nItemSize = GDALGetDataTypeSize(eDataType)/8; + long int nLineOffset=0, nPixelOffset=0, nBandOffset=0; + nPixelOffset = nItemSize; + nLineOffset = nPixelOffset * nCols + atoi(poDS->GetKeyword("NBB")) ; + nBandOffset = nLineOffset * nRows; + + nSkipBytes = atoi(poDS->GetKeyword("LBLSIZE")); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nBands; i++ ) + { + GDALRasterBand *poBand; + + poBand = new RawRasterBand( poDS, i+1, poDS->fpImage, nSkipBytes + nBandOffset * i, + nPixelOffset, nLineOffset, eDataType, +#ifdef CPL_LSB + chByteOrder == 'I' || chByteOrder == 'L', +#else + chByteOrder == 'M', +#endif + TRUE ); + + poDS->SetBand( i+1, poBand ); + poBand->SetNoDataValue( dfNoData ); + if (bIsDTM==TRUE) { + poBand->SetScale( (double) CPLAtof(poDS->GetKeyword( "DTM.DTM_SCALING_FACTOR") ) ); + poBand->SetOffset( (double) CPLAtof(poDS->GetKeyword( "DTM.DTM_OFFSET") ) ); + const char* pszMin = poDS->GetKeyword( "DTM.DTM_MINIMUM_DN", NULL ); + const char* pszMax = poDS->GetKeyword( "DTM.DTM_MAXIMUM_DN", NULL ); + if (pszMin != NULL && pszMax != NULL ) + poBand->SetStatistics(CPLAtofM(pszMin),CPLAtofM(pszMax),0,0); + const char* pszNoData = poDS->GetKeyword( "DTM.DTM_MISSING_DN", NULL ); + if (pszNoData != NULL ) + poBand->SetNoDataValue( CPLAtofM(pszNoData) ); + } else if (EQUAL( poDS->GetKeyword( "BLTYPE"), "M94_HRSC" )) { + float scale=CPLAtof(poDS->GetKeyword("DLRTO8.REFLECTANCE_SCALING_FACTOR","-1.")); + if (scale < 0.) { + scale = CPLAtof(poDS->GetKeyword( "HRCAL.REFLECTANCE_SCALING_FACTOR","1.")); + } + poBand->SetScale( scale ); + float offset=CPLAtof(poDS->GetKeyword("DLRTO8.REFLECTANCE_OFFSET","-1.")); + if (offset < 0.) { + offset = CPLAtof(poDS->GetKeyword( "HRCAL.REFLECTANCE_OFFSET","0.")); + } + poBand->SetOffset( offset ); + } + const char* pszMin = poDS->GetKeyword( "STATISTICS.MINIMUM", NULL ); + const char* pszMax = poDS->GetKeyword( "STATISTICS.MAXIMUM", NULL ); + const char* pszMean = poDS->GetKeyword( "STATISTICS.MEAN", NULL ); + const char* pszStdDev = poDS->GetKeyword( "STATISTICS.STANDARD_DEVIATION", NULL ); + if (pszMin != NULL && pszMax != NULL && pszMean != NULL && pszStdDev != NULL ) + poBand->SetStatistics(CPLAtofM(pszMin),CPLAtofM(pszMax),CPLAtofM(pszMean),CPLAtofM(pszStdDev)); + } + + +/* -------------------------------------------------------------------- */ +/* Instrument-specific keywords as metadata. */ +/* -------------------------------------------------------------------- */ + +/****************** HRSC ******************************/ + + if (EQUAL( poDS->GetKeyword( "BLTYPE"), "M94_HRSC" ) ) { + poDS->SetMetadataItem( "SPACECRAFT_NAME", poDS->GetKeyword( "M94_INSTRUMENT.INSTRUMENT_HOST_NAME") ); + poDS->SetMetadataItem( "PRODUCT_TYPE", poDS->GetKeyword( "TYPE")); + + if (EQUAL( poDS->GetKeyword( "M94_INSTRUMENT.DETECTOR_ID"), "MEX_HRSC_SRC" )) { + static const char *apszKeywords[] = { + "M94_ORBIT.IMAGE_TIME", + "FILE.EVENT_TYPE", + "FILE.PROCESSING_LEVEL_ID", + "M94_INSTRUMENT.DETECTOR_ID", + "M94_CAMERAS.EXPOSURE_DURATION", + "HRCONVER.INSTRUMENT_TEMPERATURE", NULL + }; + for( i = 0; apszKeywords[i] != NULL; i++ ) { + const char *pszKeywordValue = poDS->GetKeyword( apszKeywords[i] ); + if( pszKeywordValue != NULL ) + poDS->SetMetadataItem( apszKeywords[i], pszKeywordValue ); + } + } else { + static const char *apszKeywords[] = { + "M94_ORBIT.START_TIME", "M94_ORBIT.STOP_TIME", + "M94_INSTRUMENT.DETECTOR_ID", + "M94_CAMERAS.MACROPIXEL_SIZE", + "FILE.EVENT_TYPE", + "M94_INSTRUMENT.MISSION_PHASE_NAME", + "HRORTHO.SPICE_FILE_NAME", + "HRCONVER.MISSING_FRAMES", "HRCONVER.OVERFLOW_FRAMES", "HRCONVER.ERROR_FRAMES", + "HRFOOT.BEST_GROUND_SAMPLING_DISTANCE", + "DLRTO8.RADIANCE_SCALING_FACTOR", "DLRTO8.RADIANCE_OFFSET", + "DLRTO8.REFLECTANCE_SCALING_FACTOR", "DLRTO8.REFLECTANCE_OFFSET", + "HRCAL.RADIANCE_SCALING_FACTOR", "HRCAL.RADIANCE_OFFSET", + "HRCAL.REFLECTANCE_SCALING_FACTOR", "HRCAL.REFLECTANCE_OFFSET", + "HRORTHO.DTM_NAME", "HRORTHO.EXTORI_FILE_NAME", "HRORTHO.GEOMETRIC_CALIB_FILE_NAME", + NULL + }; + for( i = 0; apszKeywords[i] != NULL; i++ ) { + const char *pszKeywordValue = poDS->GetKeyword( apszKeywords[i], NULL ); + if( pszKeywordValue != NULL ) + poDS->SetMetadataItem( apszKeywords[i], pszKeywordValue ); + } + } + } + if (bIsDTM==TRUE && EQUAL( poDS->GetKeyword( "MAP.TARGET_NAME"), "MARS" )) { + poDS->SetMetadataItem( "SPACECRAFT_NAME", "MARS_EXPRESS" ); + poDS->SetMetadataItem( "PRODUCT_TYPE", "DTM"); + static const char *apszKeywords[] = { + "DTM.DTM_MISSING_DN", "DTM.DTM_OFFSET", "DTM.DTM_SCALING_FACTOR", "DTM.DTM_A_AXIS_RADIUS", + "DTM.DTM_B_AXIS_RADIUS", "DTM.DTM_C_AXIS_RADIUS", "DTM.DTM_DESC", "DTM.DTM_MINIMUM_DN", + "DTM.DTM_MAXIMUM_DN", NULL }; + for( i = 0; apszKeywords[i] != NULL; i++ ) { + const char *pszKeywordValue = poDS->GetKeyword( apszKeywords[i] ); + if( pszKeywordValue != NULL ) + poDS->SetMetadataItem( apszKeywords[i], pszKeywordValue ); + } + } + + +/****************** DAWN ******************************/ + else if (EQUAL( poDS->GetKeyword( "INSTRUMENT_ID"), "FC2" )) { + poDS->SetMetadataItem( "SPACECRAFT_NAME", "DAWN" ); + static const char *apszKeywords[] = {"ORBIT_NUMBER","FILTER_NUMBER", + "FRONT_DOOR_STATUS", + "FIRST_LINE", + "FIRST_LINE_SAMPLE", + "PRODUCER_INSTITUTION_NAME", + "SOURCE_FILE_NAME", + "PROCESSING_LEVEL_ID", + "TARGET_NAME", + "LIMB_IN_IMAGE", + "POLE_IN_IMAGE", + "REFLECTANCE_SCALING_FACTOR", + "SPICE_FILE_NAME", + "SPACECRAFT_CENTRIC_LATITUDE", + "SPACECRAFT_EASTERN_LONGITUDE", + "FOOTPRINT_POSITIVE_LONGITUDE", + NULL }; + for( i = 0; apszKeywords[i] != NULL; i++ ) { + const char *pszKeywordValue = poDS->GetKeyword( apszKeywords[i] ); + if( pszKeywordValue != NULL ) + poDS->SetMetadataItem( apszKeywords[i], pszKeywordValue ); + } + + } + else if (bIsDTM==TRUE && EQUAL( poDS->GetKeyword( "TARGET_NAME"), "VESTA" )) { + poDS->SetMetadataItem( "SPACECRAFT_NAME", "DAWN" ); + poDS->SetMetadataItem( "PRODUCT_TYPE", "DTM"); + static const char *apszKeywords[] = { + "DTM_MISSING_DN", "DTM_OFFSET", "DTM_SCALING_FACTOR", "DTM_A_AXIS_RADIUS", + "DTM_B_AXIS_RADIUS", "DTM_C_AXIS_RADIUS", "DTM_MINIMUM_DN", + "DTM_MAXIMUM_DN", "MAP_PROJECTION_TYPE", "COORDINATE_SYSTEM_NAME", + "POSITIVE_LONGITUDE_DIRECTION", "MAP_SCALE", + "CENTER_LONGITUDE", "LINE_PROJECTION_OFFSET", "SAMPLE_PROJECTION_OFFSET", + NULL }; + for( i = 0; apszKeywords[i] != NULL; i++ ) { + const char *pszKeywordValue = poDS->GetKeyword( apszKeywords[i] ); + if( pszKeywordValue != NULL ) + poDS->SetMetadataItem( apszKeywords[i], pszKeywordValue ); + } + } + + +/* -------------------------------------------------------------------- */ +/* END Instrument-specific keywords as metadata. */ +/* -------------------------------------------------------------------- */ + + if (EQUAL(poDS->GetKeyword( "EOL"), "1" )) + poDS->SetMetadataItem( "END-OF-DATASET_LABEL", "PRESENT" ); + poDS->SetMetadataItem( "CONVERSION_DETAILS", "http://www.lpi.usra.edu/meetings/lpsc2014/pdf/1088.pdf" ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GetKeyword() */ +/************************************************************************/ + +const char *VICARDataset::GetKeyword( const char *pszPath, + const char *pszDefault ) + +{ + return oKeywords.GetKeyword( pszPath, pszDefault ); +} + + + +/************************************************************************/ +/* GDALRegister_VICAR() */ +/************************************************************************/ + +void GDALRegister_VICAR() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "VICAR" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "VICAR" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "MIPL VICAR file" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_vicar.html" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = VICARDataset::Open; + poDriver->pfnIdentify = VICARDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/pds/vicarkeywordhandler.cpp b/bazaar/plugin/gdal/frmts/pds/vicarkeywordhandler.cpp new file mode 100644 index 000000000..8cc110071 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/vicarkeywordhandler.cpp @@ -0,0 +1,355 @@ +/****************************************************************************** +* +* Project: VICAR Driver; JPL/MIPL VICAR Format +* Purpose: Implementation of VICARKeywordHandler - a class to read +* keyword data from VICAR data products. +* Author: Sebastian Walter +* +* NOTE: This driver code is loosely based on the ISIS and PDS drivers. +* It is not intended to diminish the contribution of the authors +****************************************************************************** +* Copyright (c) 2014, Sebastian Walter +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +****************************************************************************/ + + +#include "cpl_string.h" +#include "vicarkeywordhandler.h" + +/************************************************************************/ +/* ==================================================================== */ +/* VICARKeywordHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VICARKeywordHandler() */ +/************************************************************************/ + +VICARKeywordHandler::VICARKeywordHandler() + +{ + papszKeywordList = NULL; +} + +/************************************************************************/ +/* ~VICARKeywordHandler() */ +/************************************************************************/ + +VICARKeywordHandler::~VICARKeywordHandler() + +{ + CSLDestroy( papszKeywordList ); + papszKeywordList = NULL; +} + +/************************************************************************/ +/* Ingest() */ +/************************************************************************/ + +int VICARKeywordHandler::Ingest( VSILFILE *fp, GByte *pabyHeader ) + +{ +/* -------------------------------------------------------------------- */ +/* Read in buffer till we find END all on it's own line. */ +/* -------------------------------------------------------------------- */ + if( VSIFSeekL( fp, 0, SEEK_SET ) != 0 ) + return FALSE; + + char *pch1, *pch2; + char keyval[100]; + + // Find LBLSIZE Entry + char* pszLBLSIZE=strstr((char*)pabyHeader,"LBLSIZE"); + int nOffset = 0; + + if (pszLBLSIZE) + nOffset = pszLBLSIZE - (const char *)pabyHeader; + pch1=strstr((char*)pabyHeader+nOffset,"="); + if( pch1 == NULL ) return FALSE; + pch1 ++; + pch2=strstr((char*)pabyHeader+nOffset," "); + if( pch2 == NULL ) return FALSE; + strncpy(keyval,pch1,MAX(pch2-pch1, 99)); + keyval[MAX(pch2-pch1, 99)] = 0; + LabelSize=atoi(keyval); + if( LabelSize > 10 * 1024 * 124 ) + return FALSE; + + char* pszChunk = (char*) VSIMalloc(LabelSize+1); + if( pszChunk == NULL ) + return FALSE; + int nBytesRead = VSIFReadL( pszChunk, 1, LabelSize, fp ); + pszChunk[LabelSize] = 0; + + osHeaderText += pszChunk ; + VSIFree(pszChunk); + pszHeaderNext = osHeaderText.c_str(); + +/* -------------------------------------------------------------------- */ +/* Process name/value pairs, keeping track of a "path stack". */ +/* -------------------------------------------------------------------- */ + if( !ReadGroup("") ) + return FALSE; +/* -------------------------------------------------------------------- */ +/* Now check for the Vicar End-of-Dataset Label... */ +/* -------------------------------------------------------------------- */ + const char *pszResult; + + pszResult = CSLFetchNameValue( papszKeywordList, "EOL" ); + + if( pszResult == NULL ) + return FALSE; + + if( !EQUAL(pszResult,"1") ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* There is a EOL! e.G. h4231_0000.nd4.06 */ +/* -------------------------------------------------------------------- */ + + long int nLineOffset=0, nPixelOffset=0, nBandOffset=0; + if (EQUAL( CSLFetchNameValue(papszKeywordList,"FORMAT" ), "BYTE" )) { + nPixelOffset = 1; + } + else if (EQUAL( CSLFetchNameValue(papszKeywordList,"FORMAT" ), "HALF" )) { + nPixelOffset = 2; + } + else if (EQUAL( CSLFetchNameValue(papszKeywordList,"FORMAT" ), "FULL" )) { + nPixelOffset = 4; + } + else if (EQUAL( CSLFetchNameValue(papszKeywordList,"FORMAT" ), "REAL" )) { + nPixelOffset = 4; + } + long int nCols = atoi(CSLFetchNameValue(papszKeywordList,"NS")) ; + long int nRows = atoi(CSLFetchNameValue(papszKeywordList,"NL")) ; + int nBands = atoi(CSLFetchNameValue(papszKeywordList,"NB")) ; + int nBB = atoi(CSLFetchNameValue(papszKeywordList,"NBB")) ; + nLineOffset = nPixelOffset * nCols + nBB ; + nBandOffset = nLineOffset * nRows; + long int starteol = LabelSize + nBandOffset * nBands; + if( VSIFSeekL( fp, starteol, SEEK_SET ) != 0 ) { + printf("Error seeking to EOL!\n"); + return FALSE; + } + char szChunk[100]; + nBytesRead = VSIFReadL( szChunk, 1, 30, fp ); + szChunk[nBytesRead] = '\0'; + pszLBLSIZE=strstr(szChunk,"LBLSIZE"); + nOffset=0; + + if (pszLBLSIZE) + nOffset = pszLBLSIZE - (const char *)szChunk; + pch1=strstr((char*)szChunk+nOffset,"=")+1; + pch2=strstr((char*)szChunk+nOffset," "); + strncpy(keyval,pch1,pch2-pch1); + int EOLabelSize=atoi(keyval); + if( EOLabelSize > 99 ) + EOLabelSize = 99; + if( VSIFSeekL( fp, starteol, SEEK_SET ) != 0 ) { + printf("Error seeking again to EOL!\n"); + return FALSE; + } + nBytesRead = VSIFReadL( szChunk, 1, EOLabelSize, fp ); + szChunk[nBytesRead] = '\0'; + osHeaderText += szChunk ; + osHeaderText.append("END"); + pszHeaderNext = osHeaderText.c_str(); + return ReadGroup( "" ); + +} + +/************************************************************************/ +/* ReadGroup() */ +/************************************************************************/ + +int VICARKeywordHandler::ReadGroup( CPL_UNUSED const char *pszPathPrefix ) { + CPLString osName, osValue, osProperty; + + for( ; TRUE; ) { + if( !ReadPair( osName, osValue ) ) { + //printf("Could not... \n"); + return FALSE; + } + if( EQUAL(osName,"END") ) + return TRUE; + else if( EQUAL(osName,"PROPERTY") || EQUAL(osName,"HISTORY") || EQUAL(osName,"TASK")) + osProperty = osValue; + else { + if ( !EQUAL(osProperty,"") ) + osName = osProperty + "." + osName; + papszKeywordList = CSLSetNameValue( papszKeywordList, osName, osValue ); + } + } +} + + +/************************************************************************/ +/* ReadPair() */ +/* */ +/* Read a name/value pair from the input stream. Strip off */ +/* white space, ignore comments, split on '='. */ +/* Returns TRUE on success. */ +/************************************************************************/ + +int VICARKeywordHandler::ReadPair( CPLString &osName, CPLString &osValue ) { + + osName = ""; + osValue = ""; + + if( !ReadWord( osName ) ) { + return FALSE;} + + SkipWhite(); + + // VICAR has no NULL string termination + if( *pszHeaderNext == '\0') { + osName="END"; + return TRUE; + } + + pszHeaderNext++; + + SkipWhite(); + + osValue = ""; + + if( *pszHeaderNext == '(' && pszHeaderNext[1] == '\'' ) + { + CPLString osWord; + while( ReadWord( osWord ) ) + { + osValue += osWord; + if ( strlen(osWord) < 2 ) continue; + if( osWord[strlen(osWord)-1] == ')' && osWord[strlen(osWord)-2] == '\'' ) break; + } + } + + else if( *pszHeaderNext == '(' && *pszHeaderNext-1 != '\'' ) + { + CPLString osWord; + + while( ReadWord( osWord ) ) + { + SkipWhite(); + + osValue += osWord; + if( osWord[strlen(osWord)-1] == ')' ) break; + } + } + + else + { + if( !ReadWord( osValue ) ) return FALSE; + + } + + SkipWhite(); + + return TRUE; +} + +/************************************************************************/ +/* ReadWord() */ +/* Returns TRUE on success */ +/************************************************************************/ + +int VICARKeywordHandler::ReadWord( CPLString &osWord ) + +{ + osWord = ""; + + SkipWhite(); + + if( *pszHeaderNext == '\0') return TRUE; + + if( !( *pszHeaderNext != '=' && !isspace((unsigned char)*pszHeaderNext)) ) { + return FALSE; + } + + if( *pszHeaderNext == '\'' ) + { + pszHeaderNext++; + while( *pszHeaderNext != '\'' ) + { + //Skip Double Quotes + if( *pszHeaderNext+1 == '\'' ) continue; + osWord += *(pszHeaderNext++); + } + pszHeaderNext++; + return TRUE; + } + + while( *pszHeaderNext != '=' && !isspace((unsigned char)*pszHeaderNext) ) + { + osWord += *pszHeaderNext; + pszHeaderNext++; + + } + + return TRUE; +} + +/************************************************************************/ +/* SkipWhite() */ +/* Skip white spaces and C style comments */ +/************************************************************************/ + +void VICARKeywordHandler::SkipWhite() + +{ + for( ; TRUE; ) + { + if( isspace( (unsigned char)*pszHeaderNext ) ) + { + pszHeaderNext++; + continue; + } + + // not white space, return. + return; + } +} + +/************************************************************************/ +/* GetKeyword() */ +/************************************************************************/ + +const char *VICARKeywordHandler::GetKeyword( const char *pszPath, const char *pszDefault ) + +{ + const char *pszResult; + + pszResult = CSLFetchNameValue( papszKeywordList, pszPath ); + if( pszResult == NULL ){ + return pszDefault;} + else + return pszResult; +} + +/************************************************************************/ +/* GetKeywordList() */ +/************************************************************************/ + +char **VICARKeywordHandler::GetKeywordList() +{ + return papszKeywordList; +} + diff --git a/bazaar/plugin/gdal/frmts/pds/vicarkeywordhandler.h b/bazaar/plugin/gdal/frmts/pds/vicarkeywordhandler.h new file mode 100644 index 000000000..36564e43f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pds/vicarkeywordhandler.h @@ -0,0 +1,53 @@ +/****************************************************************************** + * + * Project: VICAR Driver; JPL/MIPL VICAR Format + * Purpose: Implementation of VICARKeywordHandler - a class to read + * keyword data from VICAR data products. + * Authors: Sebastian Walter + * + * NOTE: This driver code is loosely based on the ISIS and PDS drivers. + * It is not intended to diminish the contribution of the authors + ****************************************************************************** + * Copyright (c) 2014, Sebastian Walter + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +class VICARKeywordHandler +{ + char **papszKeywordList; + + CPLString osHeaderText; + const char *pszHeaderNext; + + void SkipWhite(); + int ReadWord( CPLString &osWord ); + int ReadPair( CPLString &osName, CPLString &osValue ); + int ReadGroup( const char *pszPathPrefix ); + int LabelSize; + int Eol; + +public: + VICARKeywordHandler(); + ~VICARKeywordHandler(); + + int Ingest( VSILFILE *fp, GByte *pabyHeader ); + + const char *GetKeyword( const char *pszPath, const char *pszDefault ); + char **GetKeywordList(); +}; diff --git a/bazaar/plugin/gdal/frmts/pgchip/GNUmakefile b/bazaar/plugin/gdal/frmts/pgchip/GNUmakefile new file mode 100644 index 000000000..43a2ef6cb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pgchip/GNUmakefile @@ -0,0 +1,24 @@ +# USER CONFIGURATION +# Set your postgis include path here : +POSTGIS_INC =-I/path/to/your/postgis/headers +# END OF USER CONFIGURATION + + +include ../../GDALmake.opt + +OBJ = pgchipdataset.o pgchiprasterband.o pgchiputilities.o + + +CPPFLAGS := -Wall -g $(XTRA_OPT) $(PG_INC) $(POSTGIS_INC) $(CPPFLAGS ) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +../o/%.$(OBJ_EXT): + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/pgchip/INSTALL b/bazaar/plugin/gdal/frmts/pgchip/INSTALL new file mode 100644 index 000000000..36da77ded --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pgchip/INSTALL @@ -0,0 +1,37 @@ +REQUIREMENTS : + +* Make sure you have a full PostgreSQL/Postgis clean source installation +* You need to have Proj4 installed and configured in Pstgis to get the driver work + + +INSTALL NOTES : + +1* Go to frmts directory under GDAL source tree + +2* Unpack pgchip archive in the frmts directory + +3* Edit GNUMakefile to set your Postgis include path + +4* Add registration entry point declaration : + - Open gdal/gcore/gdal_frmts.h + - Add "void CPL_DLL GDALRegister_PGCHIP(void);" between the CPL_C_START and CPL_C_END tags + +5* Add a call to the registration function in frmts/gdalallregister.c + In the GDALAllRegister() function add the followinf lines : + #ifdef FRMT_pgchip + GDALRegister_PGCHIP(); + #endif + +6* Add the format short name to the GDAL_FORMATS macro in GDALmake.opt.in (and to GDALmake.opt) : + - Locate the variable GDAL_FORMATS and add "pgchip" (lowercase) to the list of formats + +7* Add a format specific item to the EXTRAFLAGS macro in frmts/makefile.vc + +8* Recompile your GDAL library : + - make clean + -./configure + - make + - make install + +9* Test your pgchip installation : + execute gdalinfo --formats and search for pgchip \ No newline at end of file diff --git a/bazaar/plugin/gdal/frmts/pgchip/README b/bazaar/plugin/gdal/frmts/pgchip/README new file mode 100644 index 000000000..5fab1f71a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pgchip/README @@ -0,0 +1,29 @@ +Important Drivers Restrictions : + + * PGCHIP driver is currently under development which means it has NOT been fully tested and no stable release is downloadable. + + * The driver only supports GDT_Byte and GDT_UInt16 datatypes and deals with 1 or 4 bands (GREY_SCALE, PALETTE and RGBA) + + * The column name for the chip is not yet changeable and is "raster" by default + + * In order to specify the database you want to connect to, you have to give a connection string. The differents connection parameters (host,port,dbname) must be delimited with a "#" character. The name of the Postgis layer should be given at the end of the string after a "%layer=" argument. Example : + + $ gdalinfo PG:host=192.168.1.1#dbname=mydb%layer=myRasterTable + + +How can I test the driver : + + * You can choose to build your own application using the GDAL API or use the utility programs. + * Some examples with gdal_translate : + + Import BMP raster : + gdal_translate -of pgchip /DATA/myRaster.bmp PG:host=192.168.1.1#dbname=mydb#port=5432%layer=myRaster + + Then export to PNG : + gdal_translate -of png -ot UInt16 PG:host=192.168.1.1#dbname=mydb#port=5432%layer=myRaster /DATA/myRaster.png + + +Author information and bug report : + + website : http://simon.benjamin.free.fr/pgchip/ + email : noumayoss@gmail.com diff --git a/bazaar/plugin/gdal/frmts/pgchip/makefile.vc b/bazaar/plugin/gdal/frmts/pgchip/makefile.vc new file mode 100644 index 000000000..5f115e39c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pgchip/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = pgChipdataset.obj + +EXTRAFLAGS = -I..\iso8211 + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/pgchip/pgchip.h b/bazaar/plugin/gdal/frmts/pgchip/pgchip.h new file mode 100644 index 000000000..fbfb9d51d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pgchip/pgchip.h @@ -0,0 +1,119 @@ +/****************************************************************************** + * + * File : pgchip.h + * Project: PGCHIP Driver + * Purpose: Main header file for POSTGIS CHIP/GDAL Driver + * Author: Benjamin Simon, noumayoss@gmail.com + * + ****************************************************************************** + * Copyright (c) 2005, Benjamin Simon, noumayoss@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * Revision 1.1 2005/08/29 bsimon + * New + * + */ + +#include "gdal_priv.h" +#include "libpq-fe.h" +#include "liblwgeom.h" + +// External functions (what's again the reason for using explicit hex form ?) +extern void pgch_deparse_hex(unsigned char in, unsigned char *out); +extern void deparse_hex_string(unsigned char *strOut,char *strIn,int length); +extern void parse_hex_string(unsigned char *strOut,char *strIn,int length); + +/* color types */ +#define PGCHIP_COLOR_TYPE_GRAY 0 +#define PGCHIP_COLOR_TYPE_PALETTE 1 +#define PGCHIP_COLOR_TYPE_RGB_ALPHA 4 + +//pg_chip color struct +typedef struct pgchip_color_nohex_struct +{ + unsigned char red; + unsigned char green; + unsigned char blue; + unsigned char alpha; +} pgchip_color; + + +/************************************************************************/ +/* ==================================================================== */ +/* PGCHIPDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class PGCHIPRasterBand; + +class PGCHIPDataset : public GDALDataset{ + + friend class PGCHIPRasterBand; + + PGconn *hPGConn; + char *pszTableName; + char *pszDSName; + char *pszProjection; + + CHIP *PGCHIP; + int SRID; + int nBitDepth; + + int nColorType; /* PGHIP_COLOR_TYPE_* */ + GDALColorTable *poColorTable; + int bHaveNoData; + double dfNoDataValue; + + double adfGeoTransform[6]; + int bGeoTransformValid; + + public: + + PGCHIPDataset(); + ~PGCHIPDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + + static void printChipInfo(const CHIP& chip); + + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* PGCHIPRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class PGCHIPRasterBand : public GDALRasterBand{ + + friend class PGCHIPDataset; + + public: + + PGCHIPRasterBand( PGCHIPDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + +}; diff --git a/bazaar/plugin/gdal/frmts/pgchip/pgchipdataset.cpp b/bazaar/plugin/gdal/frmts/pgchip/pgchipdataset.cpp new file mode 100644 index 000000000..0712f7e2a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pgchip/pgchipdataset.cpp @@ -0,0 +1,995 @@ +/****************************************************************************** + * + * File : pgchipdataset.cpp + * Project: PGCHIP Driver + * Purpose: GDALDataset code for POSTGIS CHIP/GDAL Driver + * Author: Benjamin Simon, noumayoss@gmail.com + * + ****************************************************************************** + * Copyright (c) 2005, Benjamin Simon, noumayoss@gmail.com + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * Revision 1.1 2005/08/29 bsimon + * New + * + */ + +#include "pgchip.h" + +/* Define to enable debugging info */ +/*#define PGCHIP_DEBUG 1*/ + +CPL_C_START +void GDALRegister_PGCHIP(void); +CPL_C_END + + +/************************************************************************/ +/* ==================================================================== */ +/* PGCHIPDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* PGCHIPDataset() */ +/************************************************************************/ + +PGCHIPDataset::PGCHIPDataset(){ + + hPGConn = NULL; + pszTableName = NULL; + pszDSName = NULL; + PGCHIP = NULL; + + bGeoTransformValid = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + SRID = -1; + pszProjection = CPLStrdup(""); + + bHaveNoData = FALSE; + dfNoDataValue = -1; +} + +/************************************************************************/ +/* ~PGCHIPDataset() */ +/************************************************************************/ + +PGCHIPDataset::~PGCHIPDataset(){ + + if( hPGConn != NULL ) + { + /* XXX - mloskot: After the connection is closed, valgrind still + * reports 36 bytes definitely lost, somewhere in the libpq. + */ + PQfinish( hPGConn ); + hPGConn = NULL; + } + + CPLFree(pszProjection); + CPLFree(pszTableName); + CPLFree(pszDSName); + CPLFree(PGCHIP); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr PGCHIPDataset::GetGeoTransform( double * padfTransform ){ + + memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0]) * 6 ); + + if( bGeoTransformValid ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *PGCHIPDataset::GetProjectionRef(){ + + char szCommand[1024]; + PGresult *hResult; + + if(SRID == -1) + { + return ""; + } + +/* -------------------------------------------------------------------- */ +/* Reading proj */ +/* -------------------------------------------------------------------- */ + + sprintf( szCommand,"SELECT srtext FROM spatial_ref_sys where SRID=%d",SRID); + + hResult = PQexec(hPGConn,szCommand); + + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) > 0 ) + { + CPLFree(pszProjection); + pszProjection = CPLStrdup(PQgetvalue(hResult,0,0)); + } + + if( hResult ) + PQclear( hResult ); + + return pszProjection; +} + +/************************************************************************/ +/* PGChipOpenConnection() */ +/************************************************************************/ + +static +PGconn* PGChipOpenConnection(const char* pszFilename, char** ppszTableName, const char** ppszDSName, + int bExitOnMissingTable, int* pbExistTable, int *pbHasNameCol) +{ + char *pszConnectionString; + PGconn *hPGConn; + PGresult *hResult = NULL; + int i=0; + int bHavePostGIS; + char *pszTableName; + char szCommand[1024]; + + if( pszFilename == NULL || !EQUALN(pszFilename,"PG:",3)) + return NULL; + + pszConnectionString = CPLStrdup(pszFilename); + +/* -------------------------------------------------------------------- */ +/* Try to establish connection. */ +/* -------------------------------------------------------------------- */ + while(pszConnectionString[i] != '\0') + { + + if(pszConnectionString[i] == '#') + pszConnectionString[i] = ' '; + else if(pszConnectionString[i] == '%') + { + pszConnectionString[i] = '\0'; + break; + } + i++; + } + + hPGConn = PQconnectdb( pszConnectionString + 3 ); + if( hPGConn == NULL || PQstatus(hPGConn) == CONNECTION_BAD ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "PGconnectcb failed.\n%s", + PQerrorMessage(hPGConn) ); + PQfinish(hPGConn); + CPLFree(pszConnectionString); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Test to see if this database instance has support for the */ +/* PostGIS Geometry type. If so, disable sequential scanning */ +/* so we will get the value of the gist indexes. */ +/* -------------------------------------------------------------------- */ + + hResult = PQexec(hPGConn, + "SELECT oid FROM pg_type WHERE typname = 'geometry'" ); + + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) > 0 ) + { + bHavePostGIS = TRUE; + } + + if( hResult ) + PQclear( hResult ); + + if(!bHavePostGIS){ + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find geometry type, is Postgis correctly installed ?\n"); + PQfinish(hPGConn); + CPLFree(pszConnectionString); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try opening the layer */ +/* -------------------------------------------------------------------- */ + + if( strstr(pszFilename, "layer=") != NULL ) + { + pszTableName = CPLStrdup( strstr(pszFilename, "layer=") + 6 ); + } + else + { + pszTableName = CPLStrdup("unknown_layer"); + } + + char* pszDSName = strstr(pszTableName, "%name="); + if (pszDSName) + { + *pszDSName = '\0'; + pszDSName += 6; + } + else + pszDSName = "unknown_name"; + + sprintf( szCommand, + "select b.attname from pg_class a,pg_attribute b where a.oid=b.attrelid and a.relname='%s' and b.attname='raster';", + pszTableName); + + hResult = PQexec(hPGConn,szCommand); + + if( ! (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK && PQntuples(hResult) > 0) ) + { + if (pbExistTable) + *pbExistTable = FALSE; + + if (bExitOnMissingTable) + { + if (hResult) + PQclear( hResult ); + CPLFree(pszConnectionString); + CPLFree(pszTableName); + PQfinish(hPGConn); + return NULL; + } + } + else + { + if (pbExistTable) + *pbExistTable = TRUE; + + sprintf( szCommand, + "select b.attname from pg_class a,pg_attribute b where a.oid=b.attrelid and a.relname='%s' and b.attname='name';", + pszTableName); + + if (hResult) + PQclear( hResult ); + hResult = PQexec(hPGConn,szCommand); + if (PQresultStatus(hResult) == PGRES_TUPLES_OK && PQntuples(hResult) > 0) + { + if (pbHasNameCol) + *pbHasNameCol = TRUE; + } + else + { + if (pbHasNameCol) + *pbHasNameCol = FALSE; + } + } + + if (hResult) + PQclear( hResult ); + + if (ppszTableName) + *ppszTableName = pszTableName; + else + CPLFree(pszTableName); + + if (ppszDSName) + *ppszDSName = pszDSName; + + CPLFree(pszConnectionString); + + return hPGConn; +} + + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *PGCHIPDataset::Open( GDALOpenInfo * poOpenInfo ){ + + char szCommand[1024]; + PGresult *hResult = NULL; + PGCHIPDataset *poDS = NULL; + char *chipStringHex; + + unsigned char *chipdata; + int t; + char *pszTableName = NULL; + const char *pszDSName = NULL; + int bHasNameCol = FALSE; + + PGconn* hPGConn = PGChipOpenConnection(poOpenInfo->pszFilename, &pszTableName, &pszDSName, TRUE, NULL, &bHasNameCol); + + if( hPGConn == NULL) + return NULL; + + poDS = new PGCHIPDataset(); + poDS->hPGConn = hPGConn; + poDS->pszTableName = pszTableName; + poDS->pszDSName = CPLStrdup(pszDSName); + +/* -------------------------------------------------------------------- */ +/* Read the chip */ +/* -------------------------------------------------------------------- */ + + hResult = PQexec(poDS->hPGConn, "BEGIN"); + + if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK ) + { + PQclear( hResult ); + if (bHasNameCol) + sprintf( szCommand, + "SELECT raster FROM %s WHERE name = '%s' LIMIT 1 ", + poDS->pszTableName, poDS->pszDSName); + else + sprintf( szCommand, + "SELECT raster FROM %s LIMIT 1", + poDS->pszTableName); + + hResult = PQexec(poDS->hPGConn,szCommand); + } + + if( !hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage(poDS->hPGConn) ); + if (hResult) + PQclear( hResult ); + delete poDS; + return NULL; + } + + if (PQntuples(hResult) == 0) + { + PQclear( hResult ); + delete poDS; + return NULL; + } + + chipStringHex = PQgetvalue(hResult, 0, 0); + + if (chipStringHex == NULL) + { + PQclear( hResult ); + delete poDS; + return NULL; + } + + int stringlen = strlen((char *)chipStringHex); + + // Allocating memory for chip + chipdata = (unsigned char *) CPLMalloc(stringlen/2); + + for (t=0;tPGCHIP = (CHIP *)chipdata; + poDS->SRID = poDS->PGCHIP->SRID; + + if (poDS->PGCHIP->bvol.xmin != 0 && poDS->PGCHIP->bvol.ymin != 0 & + poDS->PGCHIP->bvol.xmax != 0 && poDS->PGCHIP->bvol.ymax != 0) + { + poDS->bGeoTransformValid = TRUE; + + poDS->adfGeoTransform[0] = poDS->PGCHIP->bvol.xmin; + poDS->adfGeoTransform[3] = poDS->PGCHIP->bvol.ymax; + poDS->adfGeoTransform[1] = (poDS->PGCHIP->bvol.xmax - poDS->PGCHIP->bvol.xmin) / poDS->PGCHIP->width; + poDS->adfGeoTransform[5] = - (poDS->PGCHIP->bvol.ymax - poDS->PGCHIP->bvol.ymin) / poDS->PGCHIP->height; + } + +#ifdef PGCHIP_DEBUG + poDS->printChipInfo(*(poDS->PGCHIP)); +#endif + + PQclear( hResult ); + + hResult = PQexec(poDS->hPGConn, "COMMIT"); + PQclear( hResult ); + +/* -------------------------------------------------------------------- */ +/* Verify that there's no unknown field set. */ +/* -------------------------------------------------------------------- */ + + if ( poDS->PGCHIP->future[0] != 0 || + poDS->PGCHIP->future[1] != 0 || + poDS->PGCHIP->future[2] != 0 || + poDS->PGCHIP->future[3] != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported CHIP format (future field bytes != 0)\n"); + delete poDS; + return NULL; + } + + if ( poDS->PGCHIP->compression != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Compressed CHIP unsupported\n"); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Set some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + + poDS->nRasterXSize = poDS->PGCHIP->width; + poDS->nRasterYSize = poDS->PGCHIP->height; + poDS->nBands = 1; // (int)poDS->PGCHIP->future[0]; + //poDS->nBitDepth = (int)poDS->PGCHIP->future[1]; + poDS->nColorType = PGCHIP_COLOR_TYPE_GRAY; //(int)poDS->PGCHIP->future[2]; + + switch (poDS->PGCHIP->datatype) + { + case 5: // 24bit integer + poDS->nBitDepth = 24; + break; + case 6: // 16bit integer + poDS->nBitDepth = 16; + break; + case 8: + poDS->nBitDepth = 8; + break; + case 1: // float32 + case 7: // 16bit ??? + case 101: // float32 (NDR) + case 105: // 24bit integer (NDR) + case 106: // 16bit integer (NDR) + case 107: // 16bit ??? (NDR) + case 108: // 8bit ??? (NDR) [ doesn't make sense ] + default : + CPLError( CE_Failure, CPLE_AppDefined, + "Under development : CHIP datatype %d unsupported.\n", + poDS->PGCHIP->datatype); + break; + } + + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < poDS->nBands; iBand++ ) + poDS->SetBand( iBand+1, new PGCHIPRasterBand( poDS, iBand+1 ) ); + + +/* -------------------------------------------------------------------- */ +/* Is there a palette? Note: we should also read back and */ +/* apply transparency values if available. */ +/* -------------------------------------------------------------------- */ + CPLAssert (poDS->nColorType != PGCHIP_COLOR_TYPE_PALETTE); + if( poDS->nColorType == PGCHIP_COLOR_TYPE_PALETTE ) + { + unsigned char *pPalette; + int nColorCount = 0; + int sizePalette = 0; + int offsetColor = -1; + GDALColorEntry oEntry; + + nColorCount = (int)poDS->PGCHIP->compression; + pPalette = (unsigned char *)chipdata + sizeof(CHIP); + sizePalette = nColorCount * sizeof(pgchip_color); + + poDS->poColorTable = new GDALColorTable(); + + for( int iColor = 0; iColor < nColorCount; iColor++ ) + { + oEntry.c1 = pPalette[offsetColor++]; + oEntry.c2 = pPalette[offsetColor++]; + oEntry.c3 = pPalette[offsetColor++]; + oEntry.c4 = pPalette[offsetColor++]; + + poDS->poColorTable->SetColorEntry( iColor, &oEntry ); + } + } + + return( poDS ); +} + + +/************************************************************************/ +/* PGCHIPCreateCopy() */ +/************************************************************************/ +static GDALDataset * PGCHIPCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ){ + + + PGconn *hPGConn; + char *pszTableName = NULL; + const char *pszDSName = NULL; + char* pszCommand; + PGresult *hResult; + char *pszProjection; + int SRID; + GDALColorTable *poCT= NULL; + int bTableExists = FALSE; + int bHasNameCol = FALSE; + + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int nBands = poSrcDS->GetRasterCount(); + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + + /* check number of bands */ + if( nBands != 1 && nBands != 4) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Under development : PGCHIP driver doesn't support %d bands. Must be 1 or 4\n", nBands ); + + return NULL; + } + + if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte + && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Under development : PGCHIP driver doesn't support data type %s. " + "Only eight bit (Byte) and sixteen bit (UInt16) bands supported.\n", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); + + return NULL; + } + + hPGConn = PGChipOpenConnection(pszFilename, &pszTableName, &pszDSName, FALSE, &bTableExists, &bHasNameCol); + + /* Check Postgis connection string */ + if( hPGConn == NULL){ + CPLError( CE_Failure, CPLE_NotSupported, + "Cannont connect to %s.\n", pszFilename); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Setup some parameters. */ +/* -------------------------------------------------------------------- */ + + int nBitDepth; + GDALDataType eType; + int storageChunk; + int nColorType=0; + + + if( nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() == NULL ){ + nColorType = PGCHIP_COLOR_TYPE_GRAY; + } + else if( nBands == 1 ){ + nColorType = PGCHIP_COLOR_TYPE_PALETTE; + } + else if( nBands == 4 ){ + nColorType = PGCHIP_COLOR_TYPE_RGB_ALPHA; + } + + if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16 ) + { + eType = GDT_Byte; + nBitDepth = 8; + } + else + { + eType = GDT_UInt16; + nBitDepth = 16; + } + + storageChunk = nBitDepth/8; + + //printf("nBands = %d, nBitDepth = %d\n",nBands,nBitDepth); + + pszCommand = (char*)CPLMalloc(1024); + + if(!bTableExists){ + sprintf( pszCommand, + "CREATE TABLE %s(raster chip, name varchar)", + pszTableName); + + hResult = PQexec(hPGConn,pszCommand); + bHasNameCol = TRUE; + } + else + { + if (bHasNameCol) + sprintf( pszCommand, + "DELETE FROM %s WHERE name = '%s'", pszTableName, pszDSName); + else + sprintf( pszCommand, + "DELETE FROM %s", pszTableName); + + hResult = PQexec(hPGConn,pszCommand); + } + + if( hResult && (PQresultStatus(hResult) == PGRES_COMMAND_OK || PQresultStatus(hResult) == PGRES_TUPLES_OK)){ + PQclear( hResult ); + } + else { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage(hPGConn) ); + PQfinish( hPGConn ); + hPGConn = NULL; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Projection, finding SRID */ +/* -------------------------------------------------------------------- */ + + pszProjection = (char *)poSrcDS->GetProjectionRef(); + SRID = -1; + + if( !EQUALN(pszProjection,"GEOGCS",6) + && !EQUALN(pszProjection,"PROJCS",6) + && !EQUALN(pszProjection,"+",6) + && !EQUAL(pszProjection,"") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Only OGC WKT Projections supported for writing to Postgis.\n" + "%s not supported.", + pszProjection ); + } + + + if( pszProjection[0]=='+') + sprintf( pszCommand,"SELECT SRID FROM spatial_ref_sys where proj4text=%s",pszProjection); + else + sprintf( pszCommand,"SELECT SRID FROM spatial_ref_sys where srtext=%s",pszProjection); + + hResult = PQexec(hPGConn,pszCommand); + + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) > 0 ){ + + SRID = atoi(PQgetvalue(hResult,0,0)); + + } + + // Try to find SRID via EPSG number + if (SRID == -1 && strcmp(pszProjection,"") != 0){ + + char *buf; + char epsg[16]; + memset(epsg,0,16); + const char *workingproj = pszProjection; + + while( (buf = strstr(workingproj,"EPSG")) != 0){ + workingproj = buf+4; + } + + int iChar = 0; + workingproj = workingproj + 3; + + + while(workingproj[iChar] != '"' && iChar < 15){ + epsg[iChar] = workingproj[iChar]; + iChar++; + } + + if(epsg[0] != 0){ + SRID = atoi(epsg); + } + } + else{ + CPLError( CE_Failure, CPLE_AppDefined, + "Projection %s not found in spatial_ref_sys table. SRID will be set to -1.\n", + pszProjection ); + + SRID = -1; + } + + if( hResult ) + PQclear( hResult ); + + +/* -------------------------------------------------------------------- */ +/* Write palette if there is one. Technically, I think it is */ +/* possible to write 16bit palettes for PNG, but we will omit */ +/* this for now. */ +/* -------------------------------------------------------------------- */ + + unsigned char *pPalette = NULL; + int bHaveNoData = FALSE; + double dfNoDataValue = -1; + int nbColors = 0,bFoundTrans = FALSE; + size_t sizePalette = 0; + + if( nColorType == PGCHIP_COLOR_TYPE_PALETTE ) + { + + GDALColorEntry sEntry; + int iColor; + int offsetColor = -1; + + poCT = poSrcDS->GetRasterBand(1)->GetColorTable(); + nbColors = poCT->GetColorEntryCount(); + + sizePalette += sizeof(pgchip_color) * poCT->GetColorEntryCount(); + + pPalette = (unsigned char *) CPLMalloc(sizePalette); + + + for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ ) + { + poCT->GetColorEntryAsRGB( iColor, &sEntry ); + if( sEntry.c4 != 255 ) + bFoundTrans = TRUE; + + pPalette[offsetColor++] = (unsigned char) sEntry.c1; + pPalette[offsetColor++] = (unsigned char) sEntry.c2; + pPalette[offsetColor++] = (unsigned char) sEntry.c3; + + + if( bHaveNoData && iColor == (int) dfNoDataValue ){ + pPalette[offsetColor++] = 0; + } + else{ + pPalette[offsetColor++] = (unsigned char) sEntry.c4; + } + } + } + + +/* -------------------------------------------------------------------- */ +/* Initialize CHIP Structure */ +/* -------------------------------------------------------------------- */ + + CHIP PGCHIP; + + memset(&PGCHIP,0,sizeof(PGCHIP)); + + PGCHIP.factor = 1.0; + PGCHIP.endian_hint = 1; + PGCHIP.compression = 0; // nbColors; // To cope with palette extra information :
    + PGCHIP.height = nYSize; + PGCHIP.width = nXSize; + PGCHIP.SRID = SRID; + PGCHIP.future[0] = 0; // nBands; //nBands is stored in future variable + PGCHIP.future[1] = 0; // nBitDepth; //nBitDepth is stored in future variable + PGCHIP.future[2] = 0; // nColorType; //nBitDepth is stored in future variable + PGCHIP.future[3] = 0; // nbColors; // Useless as we store nbColors in the "compression" integer + PGCHIP.data = NULL; // Serialized Form + + double adfGeoTransform[6]; + if (GDALGetGeoTransform(poSrcDS, adfGeoTransform) == CE_None) + { + if (adfGeoTransform[2] == 0 && + adfGeoTransform[4] == 0 && + adfGeoTransform[5] < 0) + { + PGCHIP.bvol.xmin = adfGeoTransform[0]; + PGCHIP.bvol.ymax = adfGeoTransform[3]; + PGCHIP.bvol.xmax = adfGeoTransform[0] + nXSize * adfGeoTransform[1]; + PGCHIP.bvol.ymin = adfGeoTransform[3] + nYSize * adfGeoTransform[5]; + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Could not write geotransform.\n" ); + } + } + + // PGCHIP.size changes if there is a palette. + // Is calculated by Postgis when inserting anyway + PGCHIP.size = sizeof(CHIP) - sizeof(void*) + (nYSize * nXSize * storageChunk * nBands) + sizePalette; + + switch(nBitDepth) + { + case 8: + PGCHIP.datatype = 8; // NDR|XDR ? + break; + case 16: + PGCHIP.datatype = 6; // NDR|XDR ? + break; + case 24: + PGCHIP.datatype = 5; // NDR|XDR ? + break; + default: + CPLError( CE_Failure, CPLE_AppDefined,"Under development : ERROR STORAGE CHUNK SIZE NOT SUPPORTED\n"); + break; + } + +#ifdef PGCHIP_DEBUG + PGCHIPDataset::printChipInfo(PGCHIP); +#endif + +/* -------------------------------------------------------------------- */ +/* Loop over image */ +/* -------------------------------------------------------------------- */ + + CPLErr eErr; + size_t lineSize = nXSize * storageChunk * nBands; + + // allocating data buffer + GByte *data = (GByte *) CPLMalloc( nYSize * lineSize); + + for( int iLine = 0; iLine < nYSize; iLine++ ){ + for( int iBand = 0; iBand < nBands; iBand++ ){ + + GDALRasterBand * poBand = poSrcDS->GetRasterBand( iBand+1 ); + + eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + data + (iBand*storageChunk) + iLine * lineSize, + nXSize, 1, eType, + nBands * storageChunk, + lineSize ); + } + } + + +/* -------------------------------------------------------------------- */ +/* Write Header, Palette and Data */ +/* -------------------------------------------------------------------- */ + + char *result; + size_t j=0; + + // Calculating result length (*2 -> Hex form, +1 -> end string) + size_t size_result = (PGCHIP.size * 2) + 1; + + // memory allocation + result = (char *) CPLMalloc( size_result * sizeof(char)); + + // Assign chip + GByte *header = (GByte *)&PGCHIP; + + // Copy header into result string + for(j=0; j0){ + for(j=0;jPGCHIP != NULL){ + printf("\n---< CHIP INFO >----\n"); + printf("CHIP.datatype = %d\n", c.datatype); + printf("CHIP.compression = %d\n", c.compression); + printf("CHIP.size = %d\n", c.size); + printf("CHIP.factor = %f\n", c.factor); + printf("CHIP.width = %d\n", c.width); + printf("CHIP.height = %d\n", c.height); + //printf("CHIP.future[0] (nBands?) = %d\n", (int)c.future[0]); + //printf("CHIP.future[1] (nBitDepth?) = %d\n", (int)c.future[1]); + printf("--------------------\n"); + //} +} + +/************************************************************************/ +/* PGCHIPDelete */ +/************************************************************************/ +static CPLErr PGCHIPDelete(const char* pszFilename) +{ + return CE_None; +} + +/************************************************************************/ +/* GDALRegister_PGCHIP() */ +/************************************************************************/ +void GDALRegister_PGCHIP(){ + + GDALDriver *poDriver; + + if( GDALGetDriverByName( "PGCHIP" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "PGCHIP" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Postgis CHIP raster" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16" ); + + poDriver->pfnOpen = PGCHIPDataset::Open; + poDriver->pfnCreateCopy = PGCHIPCreateCopy; + poDriver->pfnDelete = PGCHIPDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/pgchip/pgchiprasterband.cpp b/bazaar/plugin/gdal/frmts/pgchip/pgchiprasterband.cpp new file mode 100644 index 000000000..21abd35a4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pgchip/pgchiprasterband.cpp @@ -0,0 +1,191 @@ +/****************************************************************************** + * + * File : pgchiprasterband.cpp + * Project: PGCHIP Driver + * Purpose: GDALRasterBand code for POSTGIS CHIP/GDAL Driver + * Author: Benjamin Simon, noumayoss@gmail.com + * + ****************************************************************************** + * Copyright (c) 2005, Benjamin Simon, noumayoss@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * Revision 1.1 2005/08/29 bsimon + * New + * + */ + +/************************************************************************/ +/* ==================================================================== */ +/* PGCHIPRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +#include "pgchip.h" + + +/************************************************************************/ +/* PGCHIPRasterBand() */ +/************************************************************************/ + +PGCHIPRasterBand::PGCHIPRasterBand( PGCHIPDataset *poDS, int nBand ){ + + this->poDS = poDS; + this->nBand = nBand; + + if( poDS->nBitDepth == 16 ) + eDataType = GDT_UInt16; + else + eDataType = GDT_Byte; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr PGCHIPRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ){ + + PGCHIPDataset *poGDS = (PGCHIPDataset *) poDS; + + int chipDataSize; + int bandSize; + int nPixelSize; + int nPixelOffset; + int nXSize; + int i; + + // Must start on the very left + CPLAssert( nBlockXOff == 0 ); + + + if( poGDS->nBitDepth == 16 ) + nPixelSize = 2; + else + nPixelSize = 1; + + nXSize = GetXSize(); + bandSize = nPixelSize * nXSize; + int sizePalette = 0; + + if(poGDS->nColorType == PGCHIP_COLOR_TYPE_PALETTE){ + sizePalette = (int)poGDS->PGCHIP->compression * sizeof(pgchip_color); + } + + // Determine size of whole Image Data + chipDataSize = poGDS->PGCHIP->size - (sizeof(CHIP)-sizeof(void*)) - sizePalette; + +//printf("sizePalette: %d\n", sizePalette); + +/* -------------------------------------------------------------------- */ +/* Extracting band from pointer */ +/* -------------------------------------------------------------------- */ + + +//printf("About to IReadBlock nBlockXOff: %d nBlockYOff: %d, pixelSize: %d, pixelOffset:%d nXSize: %d, bandSize:%d\n", nBlockXOff, nBlockYOff, nPixelSize, nPixelOffset, nXSize, bandSize); + + char* dataptr = (char*)&(poGDS->PGCHIP->data); + size_t bandoffset = nBlockYOff * bandSize + nBlockXOff * nPixelSize; + + // This is for supporting nBands > 1 + nPixelOffset = poGDS->nBands * nPixelSize; + + if( nPixelSize == 1 ){ + + char *bufferData = (char*)(dataptr + bandoffset); + + for(i = 0; i < nXSize; i++ ){ + ((char *) pImage)[i] = bufferData[i*nPixelOffset]; + } + } + else { + + CPLAssert (nPixelSize==2); + + //GUInt16 *bufferData = (GUInt16 *)((char*)(dataptr + bandoffset)); + + for(i = 0; i < nXSize; i++ ) + { + // I'm not sure what we need this for + size_t offset = i*nPixelOffset; + +#if 0 + printf("Reading from ptr %p at offset %d (%d total offset - %d)\n", + bufferData, offset, + (char*)&(bufferData[offset])-dataptr, + bandoffset+offset); +#endif + + ((GUInt16 *) pImage)[i] = *(GUInt16*)&dataptr[bandoffset+offset]; + + } + } + + + return CE_None; +} + + + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ +GDALColorInterp PGCHIPRasterBand::GetColorInterpretation(){ + + PGCHIPDataset *poGDS = (PGCHIPDataset *) poDS; + + if( poGDS->nColorType == PGCHIP_COLOR_TYPE_GRAY ){ + return GCI_GrayIndex; + } + else if( poGDS->nColorType == PGCHIP_COLOR_TYPE_PALETTE ){ + return GCI_PaletteIndex; + } + else if(poGDS->nColorType == PGCHIP_COLOR_TYPE_RGB_ALPHA){ + if( nBand == 1 ) + return GCI_RedBand; + else if( nBand == 2 ) + return GCI_GreenBand; + else if( nBand == 3 ) + return GCI_BlueBand; + else + return GCI_AlphaBand; + } + + return GCI_GrayIndex; +} + + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *PGCHIPRasterBand::GetColorTable(){ + + PGCHIPDataset *poGDS = (PGCHIPDataset *) poDS; + + if( nBand == 1 ) + return poGDS->poColorTable; + else + return NULL; +} diff --git a/bazaar/plugin/gdal/frmts/pgchip/pgchiputilities.cpp b/bazaar/plugin/gdal/frmts/pgchip/pgchiputilities.cpp new file mode 100644 index 000000000..4c3802ebc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/pgchip/pgchiputilities.cpp @@ -0,0 +1,290 @@ +/****************************************************************************** + * + * File : pgchiputilities.cpp + * Project: PGCHIP Driver + * Purpose: Utility functions for POSTGIS CHIP/GDAL Driver + * Author: Benjamin Simon, noumayoss@gmail.com + * + ****************************************************************************** + * Copyright (c) 2005, Benjamin Simon, noumayoss@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * Revision 1.1 2005/08/29 bsimon + * New + * + */ + +#include "pgchip.h" + +/************************************************************************/ +/* ==================================================================== */ +/* Utility Hex Functions */ +/* ==================================================================== */ +/************************************************************************/ + +void pgch_deparse_hex(unsigned char str, unsigned char *result){ + + int input_high; + int input_low; + + input_high = (str>>4); + input_low = (str & 0x0F); + + switch (input_high) + { + case 0: + result[0] = '0'; + break; + case 1: + result[0] = '1'; + break; + case 2: + result[0] = '2'; + break; + case 3: + result[0] = '3'; + break; + case 4: + result[0] = '4'; + break; + case 5: + result[0] = '5'; + break; + case 6: + result[0] = '6'; + break; + case 7: + result[0] = '7'; + break; + case 8: + result[0] = '8'; + break; + case 9: + result[0] = '9'; + break; + case 10: + result[0] = 'A'; + break; + case 11: + result[0] = 'B'; + break; + case 12: + result[0] = 'C'; + break; + case 13: + result[0] = 'D'; + break; + case 14: + result[0] = 'E'; + break; + case 15: + result[0] = 'F'; + break; + } + + switch (input_low) + { + case 0: + result[1] = '0'; + break; + case 1: + result[1] = '1'; + break; + case 2: + result[1] = '2'; + break; + case 3: + result[1] = '3'; + break; + case 4: + result[1] = '4'; + break; + case 5: + result[1] = '5'; + break; + case 6: + result[1] = '6'; + break; + case 7: + result[1] = '7'; + break; + case 8: + result[1] = '8'; + break; + case 9: + result[1] = '9'; + break; + case 10: + result[1] = 'A'; + break; + case 11: + result[1] = 'B'; + break; + case 12: + result[1] = 'C'; + break; + case 13: + result[1] = 'D'; + break; + case 14: + result[1] = 'E'; + break; + case 15: + result[1] = 'F'; + break; + } +} + +//given a string with at least 2 chars in it, convert them to +// a byte value. No error checking done! +unsigned char parse_hex(char *str){ + + //do this a little brute force to make it faster + + unsigned char result_high = 0; + unsigned char result_low = 0; + + switch (str[0]) + { + case '0' : + result_high = 0; + break; + case '1' : + result_high = 1; + break; + case '2' : + result_high = 2; + break; + case '3' : + result_high = 3; + break; + case '4' : + result_high = 4; + break; + case '5' : + result_high = 5; + break; + case '6' : + result_high = 6; + break; + case '7' : + result_high = 7; + break; + case '8' : + result_high = 8; + break; + case '9' : + result_high = 9; + break; + case 'A' : + result_high = 10; + break; + case 'B' : + result_high = 11; + break; + case 'C' : + result_high = 12; + break; + case 'D' : + result_high = 13; + break; + case 'E' : + result_high = 14; + break; + case 'F' : + result_high = 15; + break; + } + switch (str[1]) + { + case '0' : + result_low = 0; + break; + case '1' : + result_low = 1; + break; + case '2' : + result_low = 2; + break; + case '3' : + result_low = 3; + break; + case '4' : + result_low = 4; + break; + case '5' : + result_low = 5; + break; + case '6' : + result_low = 6; + break; + case '7' : + result_low = 7; + break; + case '8' : + result_low = 8; + break; + case '9' : + result_low = 9; + break; + case 'A' : + result_low = 10; + break; + case 'B' : + result_low = 11; + break; + case 'C' : + result_low = 12; + break; + case 'D' : + result_low = 13; + break; + case 'E' : + result_low = 14; + break; + case 'F' : + result_low = 15; + break; + } + return (unsigned char) ((result_high<<4) + result_low); +} + +/* Parse an hex string */ +void parse_hex_string(unsigned char *strOut,char *strIn,int length){ + + int i; + for(i=0;i + +PLMosaic (Planet Labs Mosaics API) + + + + +

    PLMosaic (Planet Labs Mosaics API)

    + +(GDAL/OGR >= 2.0)

    + +This driver can connect to Planet Labs Mosaics API. +GDAL/OGR must be built with Curl support in order for the +PLMosaic driver to be compiled.

    + +The driver supports listing mosaics and reading them. +Mosaics are accessed at their highest resolution (Google Maps +level 15, i.e. 4.77 meters/pixel). Mosaics are typically composed of quads of +4096x4096 pixels.

    + +For mosaics of type Byte, overviews are available by using the tile API. For other +data types, there is no support for overviews, so requests that involve downsampling may +take a long time to complete.

    + +

    Dataset name syntax

    + +The minimal syntax to open a datasource is :
    PLMosaic:[options]

    + +Additionnal optional parameters can be specified after the ':' sign. +Currently the following one is supported :

    + +

      +
    • api_key=value: To specify the Planet API key. It is mandatory, unless +it is supplied through the open option API_KEY, or the configuration option PL_API_KEY.
    • +
    • mosaic=mosaic_name: To specify the mosaic name.
    • +
    • cache_path=path: To specify the path to a directory where cached +quads (and tiles) are stored. A plmosaic_cache/{mosaic_name} subdirectory will be created +under that path. The empty string can be used to disable any disk caching.
    • +
    • trust_cache=YES/NO: Whether already cached quads should be reused directly, +without prior checking if the server has a more recent version. Note: +this only applies to quads, and not tiles. Default is NO.
    • +
    • use_tiles=YES/NO: Whether to use the tile API to access full +resolution data, instead of downloading quads. Only apply for Byte mosaics. Default is NO.
    • +
    + +If several parameters are specified, they must be separated by a comma.

    + +

    If no mosaic parameter is supplied, the list of available mosaics will be +returned as subdatasets. If only one mosaic is available, it will be directly +opened.

    + +

    Open options

    + +

    The following open options are available : API_KEY, MOSAIC, CACHE_PATH, +TRUST_CACHE and USE_TILES. They have the same semantics as the above +describe parameters of same name.

    + +

    Configuration options

    + +The following configuration options are available : +
      +
    • PL_API_KEY=value: To specify the Planet API key.
    • +
    + +

    Location information

    + +

    +The special Pixel_{x}_{y} metadata item of the LocationInfo metadata domain, +where x is the column and y is the line in the mosaic, can be queried to get +information about the underneath quad and the scenes that compose it (only scenes +whose footprint intersect the point are listed). This is +the syntax used by the gdallocationinfo utility +(see RFC 32) +

    + +

    Below an example of the return : +

    +<LocationInfo>
    +    <Quad>
    +        <id>L15-0455E-1272N</id>
    +        <file_size>9205776</file_size>
    +        <links_full>https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0455E-1272N/full</links_full>
    +        <links_mosaic>https://api.planet.com/v0/mosaics/color_balance_mosaic</links_mosaic>
    +        <links_scenes>https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0455E-1272N/scenes/</links_scenes>
    +        <links_self>https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0455E-1272N</links_self>
    +        <links_thumbnail>https://api.planet.com/v0/mosaics/color_balance_mosaic/quads/L15-0455E-1272N/thumb</links_thumbnail>
    +        <num_input_scenes>5</num_input_scenes>
    +        <percent_covered>23.52</percent_covered>
    +        <geometry>POLYGON ((-100.019531236 40.04443758,-100.019531236 39.909736229899998,-99.843749986099994 39.909736229899998,-99.843749986099994 40.04443758,-100.019531236 40.04443758))</geometry>
    +    </Quad>
    +    <Scenes>
    +        <Scene>
    +            <id>20141014_165043_0908</id>
    +            <acquired>2014/10/14 16:50:43+00</acquired>
    +            <camera_bit_depth>8</camera_bit_depth>
    +            <camera_color_mode>RGB</camera_color_mode>
    +            <camera_exposure_time>1170</camera_exposure_time>
    +            <camera_gain>560</camera_gain>
    +            <camera_tdi_pulses>4</camera_tdi_pulses>
    +            <cloud_cover_estimated>0</cloud_cover_estimated>
    +            <data_products_analytic_full>https://api.planet.com/v0/scenes/ortho/20141014_165043_0908/full?product=analytic</data_products_analytic_full>
    +            <data_products_visual_full>https://api.planet.com/v0/scenes/ortho/20141014_165043_0908/full?product=visual</data_products_visual_full>
    +            <file_size>52363596</file_size>
    +            <image_statistics_gsd>4.22939278619</image_statistics_gsd>
    +            <image_statistics_image_quality>standard</image_statistics_image_quality>
    +            <image_statistics_snr>38.816414101138</image_statistics_snr>
    +            <links_full>https://api.planet.com/v0/scenes/ortho/20141014_165043_0908/full</links_full>
    +            <links_self>https://api.planet.com/v0/scenes/ortho/20141014_165043_0908</links_self>
    +            <links_square_thumbnail>https://api.planet.com/v0/scenes/ortho/20141014_165043_0908/square-thumb</links_square_thumbnail>
    +            <links_thumbnail>https://api.planet.com/v0/scenes/ortho/20141014_165043_0908/thumb</links_thumbnail>
    +            <quad_metadata_percentage_of_quad>1.72</quad_metadata_percentage_of_quad>
    +            <sat_alt>625.335328005</sat_alt>
    +            <sat_id>0908</sat_id>
    +            <sat_lat>39.929866991</sat_lat>
    +            <sat_lng>-99.7924758414</sat_lng>
    +            <sat_off_nadir>2.16630567839854</sat_off_nadir>
    +            <strip_id>1413305228.34946</strip_id>
    +            <sun_altitude>37.0861376836067</sun_altitude>
    +            <sun_azimuth>150.226745964007</sun_azimuth>
    +            <sun_local_time_of_day>10.1924460550178</sun_local_time_of_day>
    +            <geometry>POLYGON ((-99.972717127088671 39.955932287655287,-100.001449726838672 40.054504403484273,-100.15302892466832 40.028245144277392,-100.124005707005423 39.929632727481838,-99.972717127088671 39.955932287655287))</geometry>
    +        </Scene>
    +        <Scene>
    +            ...
    +        </Scene>
    +    </Scenes>
    +</LocationInfo>
    +
    +

    + +

    Examples

    + +
  • Listing all mosaics available (with the rights of the account) : +
    +gdalinfo "PLMosaic:" -oo API_KEY=some_value
    +
    +or +
    +gdalinfo "PLMosaic:api_key=some_value"
    +
    +or +
    +gdalinfo "PLMosaic:" --config PL_API_KEY some_value
    +
    + +returns (in case of multiple mosaics): + +
    +Driver: PLMOSAIC/Planet Labs Mosaics API
    +Files: none associated
    +Size is 512, 512
    +Coordinate System is `'
    +Image Structure Metadata:
    +  INTERLEAVE=PIXEL
    +Subdatasets:
    +  SUBDATASET_1_NAME=PLMOSAIC:mosaic=color_balance_mosaic
    +  SUBDATASET_1_DESC=Color-Balanced Mosaic
    +  ...
    +Corner Coordinates:
    +Upper Left  (    0.0,    0.0)
    +Lower Left  (    0.0,  512.0)
    +Upper Right (  512.0,    0.0)
    +Lower Right (  512.0,  512.0)
    +Center      (  256.0,  256.0)
    +
    + + +
  • Open a particular mosaic : +
    +gdalinfo "PLMosaic:mosaic=color_balance_mosaic" -oo API_KEY=some_value
    +
    + +returns: +
    +Driver: PLMOSAIC/Planet Labs Mosaics API
    +Files: none associated
    +Size is 8388608, 8388608
    +Coordinate System is:
    +PROJCS["WGS 84 / Pseudo-Mercator",
    +    GEOGCS["WGS 84",
    +        DATUM["WGS_1984",
    +            SPHEROID["WGS 84",6378137,298.257223563,
    +                AUTHORITY["EPSG","7030"]],
    +            AUTHORITY["EPSG","6326"]],
    +        PRIMEM["Greenwich",0,
    +            AUTHORITY["EPSG","8901"]],
    +        UNIT["degree",0.0174532925199433,
    +            AUTHORITY["EPSG","9122"]],
    +        AUTHORITY["EPSG","4326"]],
    +    PROJECTION["Mercator_1SP"],
    +    PARAMETER["central_meridian",0],
    +    PARAMETER["scale_factor",1],
    +    PARAMETER["false_easting",0],
    +    PARAMETER["false_northing",0],
    +    UNIT["metre",1,
    +        AUTHORITY["EPSG","9001"]],
    +    AXIS["X",EAST],
    +    AXIS["Y",NORTH],
    +    EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext  +no_defs"],
    +    AUTHORITY["EPSG","3857"]]
    +Origin = (-20037508.339999999850988,20037508.339999999850988)
    +Pixel Size = (4.777314267160000,-4.777314267160000)
    +Metadata:
    +  FIRST_ACQUIRED=2014-03-20T15:57:11+00:00
    +  LAST_ACQUIRED=2015-04-06T11:38:16.570663+00:00
    +  TITLE=Color-Balanced Mosaic
    +Image Structure Metadata:
    +  INTERLEAVE=PIXEL
    +Corner Coordinates:
    +Upper Left  (-20037508.340,20037508.340) (180d 0' 0.00"W, 85d 3' 4.06"N)
    +Lower Left  (-20037508.340,-20037508.340) (180d 0' 0.00"W, 85d 3' 4.06"S)
    +Upper Right (20037508.340,20037508.340) (180d 0' 0.00"E, 85d 3' 4.06"N)
    +Lower Right (20037508.340,-20037508.340) (180d 0' 0.00"E, 85d 3' 4.06"S)
    +Center      (   0.0000063,  -0.0000063) (  0d 0' 0.00"E,  0d 0' 0.00"S)
    +Band 1 Block=256x256 Type=Byte, ColorInterp=Red
    +  Overviews: 4194304x4194304, ..., 256x256
    +  Mask Flags: PER_DATASET ALPHA 
    +  Overviews of mask band: Overviews: 4194304x4194304, ..., 256x256
    +Band 2 Block=256x256 Type=Byte, ColorInterp=Green
    +  Overviews: 4194304x4194304, ..., 256x256
    +  Mask Flags: PER_DATASET ALPHA 
    +  Overviews of mask band: Overviews: 4194304x4194304, ..., 256x256
    +Band 3 Block=256x256 Type=Byte, ColorInterp=Blue
    +  Overviews: 4194304x4194304, ..., 256x256
    +  Mask Flags: PER_DATASET ALPHA 
    +  Overviews of mask band: Overviews: 4194304x4194304, ..., 256x256
    +Band 4 Block=256x256 Type=Byte, ColorInterp=Alpha
    +  Overviews: 4194304x4194304, ..., 256x256
    +
    + + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/plmosaic/makefile.vc b/bazaar/plugin/gdal/frmts/plmosaic/makefile.vc new file mode 100644 index 000000000..8bd163fbd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/plmosaic/makefile.vc @@ -0,0 +1,16 @@ + +OBJ = plmosaicdataset.obj + +EXTRAFLAGS = $(CURL_CFLAGS) $(CURL_INC) -I..\..\ogr\ogrsf_frmts\geojson -I../../ogr/ogrsf_frmts/geojson/libjson + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + -del *.dll diff --git a/bazaar/plugin/gdal/frmts/plmosaic/plmosaicdataset.cpp b/bazaar/plugin/gdal/frmts/plmosaic/plmosaicdataset.cpp new file mode 100644 index 000000000..6e4bc203a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/plmosaic/plmosaicdataset.cpp @@ -0,0 +1,1511 @@ +/****************************************************************************** + * $Id: plmosaicdataset.cpp 29019 2015-04-25 20:34:19Z rouault $ + * + * Project: PLMosaic driver + * Purpose: PLMosaic driver + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2015, Planet Labs + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_pam.h" +#include "ogr_spatialref.h" +#include "ogrsf_frmts.h" +#include "cpl_http.h" +#include "cpl_minixml.h" +#include + +CPL_CVSID("$Id: plmosaicdataset.cpp 29019 2015-04-25 20:34:19Z rouault $"); + +extern "C" void GDALRegister_PLMOSAIC(); + +// g++ -fPIC -g -Wall frmts/plmosaic/*.cpp -shared -o gdal_PLMOSAIC.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/geojson/libjson -L. -lgdal + +#define GM_ORIGIN -20037508.340 +#define GM_ZOOM_0 ((2 * -(GM_ORIGIN)) / 256) + +/************************************************************************/ +/* ==================================================================== */ +/* PLMosaicDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class PLLinkedDataset; +class PLLinkedDataset +{ +public: + CPLString osKey; + GDALDataset *poDS; + PLLinkedDataset *psPrev; + PLLinkedDataset *psNext; + + PLLinkedDataset() : poDS(NULL), psPrev(NULL), psNext(NULL) {} +}; + +class PLMosaicRasterBand; + +class PLMosaicDataset : public GDALPamDataset +{ + friend class PLMosaicRasterBand; + + int bMustCleanPersistant; + CPLString osCachePathRoot; + int bTrustCache; + CPLString osBaseURL; + CPLString osAPIKey; + CPLString osMosaic; + char *pszWKT; + int nQuadSize; + CPLString osQuadPattern; + CPLString osQuadsURL; + int bHasGeoTransform; + double adfGeoTransform[6]; + int nZoomLevel; + int bUseTMSForMain; + GDALDataset *poTMSDS; + + int nCacheMaxSize; + std::map oMapLinkedDatasets; + PLLinkedDataset *psHead; + PLLinkedDataset *psTail; + void FlushDatasetsCache(); + CPLString GetMosaicCachePath(); + void CreateMosaicCachePathIfNecessary(); + + int nLastMetaTileX; + int nLastMetaTileY; + CPLString osLastQuadInformation; + CPLString osLastQuadSceneInformation; + CPLString osLastRetGetLocationInfo; + const char *GetLocationInfo(int nPixel, int nLine); + + char **GetBaseHTTPOptions(); + CPLHTTPResult *Download(const char* pszURL, + int bQuiet404Error = FALSE); + json_object *RunRequest(const char* pszURL, + int bQuiet404Error = FALSE); + int OpenMosaic(); + int ListSubdatasets(); + + CPLString formatTileName(int tile_x, int tile_y); + void InsertNewDataset(CPLString osKey, GDALDataset* poDS); + GDALDataset* OpenAndInsertNewDataset(CPLString osTmpFilename, + CPLString osTilename); + + public: + PLMosaicDataset(); + ~PLMosaicDataset(); + + static int Identify( GDALOpenInfo * poOpenInfo ); + static GDALDataset *Open( GDALOpenInfo * ); + + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + virtual void FlushCache(void); + + virtual const char *GetProjectionRef(); + virtual CPLErr GetGeoTransform(double* padfGeoTransform); + + GDALDataset *GetMetaTile(int tile_x, int tile_y); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* PLMosaicRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class PLMosaicRasterBand : public GDALRasterBand +{ + friend class PLMosaicDataset; + + public: + + PLMosaicRasterBand( PLMosaicDataset * poDS, int nBand, + GDALDataType eDataType ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); + + virtual const char *GetMetadataItem( const char* pszName, + const char * pszDomain = "" ); + + virtual GDALColorInterp GetColorInterpretation(); + + virtual int GetOverviewCount(); + virtual GDALRasterBand* GetOverview(int iOvrLevel); +}; + + +/************************************************************************/ +/* PLMosaicRasterBand() */ +/************************************************************************/ + +PLMosaicRasterBand::PLMosaicRasterBand( PLMosaicDataset *poDS, int nBand, + GDALDataType eDataType ) + +{ + this->eDataType = eDataType; + this->nBlockXSize = 256; + this->nBlockYSize = 256; + + this->poDS = poDS; + this->nBand = nBand; + + if( eDataType == GDT_UInt16 ) + { + if( nBand <= 3 ) + SetMetadataItem("NBITS", "12", "IMAGE_STRUCTURE"); + } +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr PLMosaicRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + PLMosaicDataset* poMOSDS = (PLMosaicDataset*) poDS; + //CPLDebug("PLMOSAIC", "IReadBlock(band=%d, x=%d, y=%d)", nBand, xBlock, yBlock); + + if( poMOSDS->bUseTMSForMain && poMOSDS->poTMSDS ) + return poMOSDS->poTMSDS->GetRasterBand(nBand)->ReadBlock(nBlockXOff, nBlockYOff, + pImage); + + int bottom_yblock = (nRasterYSize - nBlockYOff * nBlockYSize) / nBlockYSize - 1; + + int meta_tile_x = (nBlockXOff * nBlockXSize) / poMOSDS->nQuadSize; + int meta_tile_y = (bottom_yblock * nBlockYSize) / poMOSDS->nQuadSize; + int sub_tile_x = nBlockXOff % (poMOSDS->nQuadSize / nBlockXSize); + int sub_tile_y = nBlockYOff % (poMOSDS->nQuadSize / nBlockYSize); + + GDALDataset *poMetaTileDS = poMOSDS->GetMetaTile(meta_tile_x, meta_tile_y); + if( poMetaTileDS == NULL ) + { + memset(pImage, 0, + nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType)/8)); + return CE_None; + } + + return poMetaTileDS->GetRasterBand(nBand)-> + RasterIO( GF_Read, + sub_tile_x * nBlockXSize, + sub_tile_y * nBlockYSize, + nBlockXSize, + nBlockYSize, + pImage, nBlockXSize, nBlockYSize, + eDataType, 0, 0, NULL); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr PLMosaicRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ + PLMosaicDataset* poMOSDS = (PLMosaicDataset*) poDS; + if( poMOSDS->bUseTMSForMain && poMOSDS->poTMSDS ) + return poMOSDS->poTMSDS->GetRasterBand(nBand)->RasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + + return GDALRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg ); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char* PLMosaicRasterBand::GetMetadataItem( const char* pszName, + const char* pszDomain ) +{ + int nPixel, nLine; + if( pszName != NULL && pszDomain != NULL && EQUAL(pszDomain, "LocationInfo") && + sscanf(pszName, "Pixel_%d_%d", &nPixel, &nLine) == 2 ) + { + PLMosaicDataset* poMOSDS = (PLMosaicDataset*) poDS; + return poMOSDS->GetLocationInfo(nPixel, nLine); + } + else + return GDALRasterBand::GetMetadataItem(pszName, pszDomain); +} + + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int PLMosaicRasterBand::GetOverviewCount() +{ + PLMosaicDataset *poGDS = (PLMosaicDataset *) poDS; + if( !poGDS->poTMSDS ) + return 0; + return poGDS->poTMSDS->GetRasterBand(1)->GetOverviewCount(); +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand* PLMosaicRasterBand::GetOverview(int iOvrLevel) +{ + PLMosaicDataset *poGDS = (PLMosaicDataset *) poDS; + if (iOvrLevel < 0 || iOvrLevel >= GetOverviewCount()) + return NULL; + + poGDS->CreateMosaicCachePathIfNecessary(); + + return poGDS->poTMSDS->GetRasterBand(nBand)->GetOverview(iOvrLevel); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp PLMosaicRasterBand::GetColorInterpretation() +{ + switch( nBand ) + { + case 1: + return GCI_RedBand; + case 2: + return GCI_GreenBand; + case 3: + return GCI_BlueBand; + case 4: + return GCI_AlphaBand; + default: + CPLAssert(FALSE); + return GCI_GrayIndex; + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* PLMosaicDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* PLMosaicDataset() */ +/************************************************************************/ + +PLMosaicDataset::PLMosaicDataset() +{ + bMustCleanPersistant = FALSE; + bTrustCache = FALSE; + pszWKT = NULL; + nQuadSize = 0; + bHasGeoTransform = FALSE; + adfGeoTransform[0] = 0; + adfGeoTransform[1] = 1; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = 0; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = 1; + nZoomLevel = 0; + psHead = NULL; + psTail = NULL; + SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); + osCachePathRoot = CPLGetPath(CPLGenerateTempFilename("")); + nCacheMaxSize = 10; + nLastMetaTileX = -1; + nLastMetaTileY = -1; + bUseTMSForMain = FALSE; + poTMSDS = NULL; +} + +/************************************************************************/ +/* ~PLMosaicDataset() */ +/************************************************************************/ + +PLMosaicDataset::~PLMosaicDataset() + +{ + FlushCache(); + CPLFree(pszWKT); + delete poTMSDS; + if (bMustCleanPersistant) + { + char** papszOptions = NULL; + papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT", CPLSPrintf("PLMOSAIC:%p", this)); + CPLHTTPFetch( osBaseURL, papszOptions); + CSLDestroy(papszOptions); + } + +} + +/************************************************************************/ +/* FlushDatasetsCache() */ +/************************************************************************/ + +void PLMosaicDataset::FlushDatasetsCache() +{ + for( PLLinkedDataset* psIter = psHead; psIter != NULL; ) + { + PLLinkedDataset* psNext = psIter->psNext; + if( psIter->poDS ) + GDALClose(psIter->poDS); + delete psIter; + psIter = psNext; + } + psHead = psTail = NULL; + oMapLinkedDatasets.clear(); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void PLMosaicDataset::FlushCache() +{ + FlushDatasetsCache(); + + nLastMetaTileX = -1; + nLastMetaTileY = -1; + osLastQuadInformation = ""; + osLastQuadSceneInformation = ""; + osLastRetGetLocationInfo = ""; + + GDALDataset::FlushCache(); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int PLMosaicDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + return EQUALN(poOpenInfo->pszFilename, "PLMOSAIC:", strlen("PLMOSAIC:")); +} + +/************************************************************************/ +/* GetBaseHTTPOptions() */ +/************************************************************************/ + +char** PLMosaicDataset::GetBaseHTTPOptions() +{ + bMustCleanPersistant = TRUE; + + char** papszOptions = NULL; + papszOptions = CSLAddString(papszOptions, CPLSPrintf("PERSISTENT=PLMOSAIC:%p", this)); + /* Use basic auth, rather than Authorization headers since curl would forward it to S3 */ + papszOptions = CSLAddString(papszOptions, CPLSPrintf("USERPWD=%s:", osAPIKey.c_str())); + + return papszOptions; +} + +/************************************************************************/ +/* Download() */ +/************************************************************************/ + +CPLHTTPResult* PLMosaicDataset::Download(const char* pszURL, + int bQuiet404Error) +{ + char** papszOptions = CSLAddString(GetBaseHTTPOptions(), NULL); + CPLHTTPResult * psResult; + if( strncmp(osBaseURL, "/vsimem/", strlen("/vsimem/")) == 0 && + strncmp(pszURL, "/vsimem/", strlen("/vsimem/")) == 0 ) + { + CPLDebug("PLSCENES", "Fetching %s", pszURL); + psResult = (CPLHTTPResult*) CPLCalloc(1, sizeof(CPLHTTPResult)); + vsi_l_offset nDataLength = 0; + CPLString osURL(pszURL); + if( osURL[osURL.size()-1 ] == '/' ) + osURL.resize(osURL.size()-1); + GByte* pabyBuf = VSIGetMemFileBuffer(osURL, &nDataLength, FALSE); + if( pabyBuf ) + { + psResult->pabyData = (GByte*) VSIMalloc(1 + (size_t)nDataLength); + if( psResult->pabyData ) + { + memcpy(psResult->pabyData, pabyBuf, (size_t)nDataLength); + psResult->pabyData[nDataLength] = 0; + psResult->nDataLen = (size_t)nDataLength; + } + } + else + { + psResult->pszErrBuf = + CPLStrdup(CPLSPrintf("Error 404. Cannot find %s", pszURL)); + } + } + else + { + if( bQuiet404Error ) + CPLPushErrorHandler(CPLQuietErrorHandler); + psResult = CPLHTTPFetch( pszURL, papszOptions); + if( bQuiet404Error ) + CPLPopErrorHandler(); + } + CSLDestroy(papszOptions); + + if( psResult->pszErrBuf != NULL ) + { + if( !(bQuiet404Error && strstr(psResult->pszErrBuf, "404")) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s", + psResult->pabyData ? (const char*) psResult->pabyData : + psResult->pszErrBuf); + } + CPLHTTPDestroyResult(psResult); + return NULL; + } + + if( psResult->pabyData == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Empty content returned by server"); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + return psResult; +} + +/************************************************************************/ +/* RunRequest() */ +/************************************************************************/ + +json_object* PLMosaicDataset::RunRequest(const char* pszURL, + int bQuiet404Error) +{ + CPLHTTPResult * psResult = Download(pszURL, bQuiet404Error); + if( psResult == NULL ) + { + return NULL; + } + + json_tokener* jstok = NULL; + json_object* poObj = NULL; + + jstok = json_tokener_new(); + poObj = json_tokener_parse_ex(jstok, (const char*) psResult->pabyData, -1); + if( jstok->err != json_tokener_success) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JSON parsing error: %s (at offset %d)", + json_tokener_error_desc(jstok->err), jstok->char_offset); + json_tokener_free(jstok); + CPLHTTPDestroyResult(psResult); + return NULL; + } + json_tokener_free(jstok); + + CPLHTTPDestroyResult(psResult); + + if( json_object_get_type(poObj) != json_type_object ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Return is not a JSON dictionary"); + json_object_put(poObj); + poObj = NULL; + } + + return poObj; +} + +/************************************************************************/ +/* PLMosaicGetParameter() */ +/************************************************************************/ + +static CPLString PLMosaicGetParameter( GDALOpenInfo * poOpenInfo, + char** papszOptions, + const char* pszName, + const char* pszDefaultVal ) +{ + return CSLFetchNameValueDef( papszOptions, pszName, + CSLFetchNameValueDef( poOpenInfo->papszOpenOptions, pszName, + pszDefaultVal )); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *PLMosaicDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if (!Identify(poOpenInfo) ) + return NULL; + + PLMosaicDataset* poDS = new PLMosaicDataset(); + + poDS->osBaseURL = CPLGetConfigOption("PL_URL", "https://api.planet.com/v0/mosaics/"); + + char** papszOptions = CSLTokenizeStringComplex( + poOpenInfo->pszFilename+strlen("PLMosaic:"), ",", TRUE, FALSE ); + for( char** papszIter = papszOptions; papszIter && *papszIter; papszIter ++ ) + { + char* pszKey; + const char* pszValue = CPLParseNameValue(*papszIter, &pszKey); + if( pszValue != NULL ) + { + if( !EQUAL(pszKey, "api_key") && + !EQUAL(pszKey, "mosaic") && + !EQUAL(pszKey, "cache_path") && + !EQUAL(pszKey, "trust_cache") && + !EQUAL(pszKey, "use_tiles") ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Unsupported option %s", pszKey); + CPLFree(pszKey); + delete poDS; + CSLDestroy(papszOptions); + return NULL; + } + CPLFree(pszKey); + } + } + + poDS->osAPIKey = PLMosaicGetParameter(poOpenInfo, papszOptions, "api_key", + CPLGetConfigOption("PL_API_KEY","")); + + if( poDS->osAPIKey.size() == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Missing PL_API_KEY configuration option or API_KEY open option"); + delete poDS; + CSLDestroy(papszOptions); + return NULL; + } + + poDS->osMosaic = PLMosaicGetParameter(poOpenInfo, papszOptions, "mosaic", ""); + + poDS->osCachePathRoot = PLMosaicGetParameter(poOpenInfo, papszOptions, "cache_path", + CPLGetConfigOption("PL_CACHE_PATH","")); + + poDS->bTrustCache = CSLTestBoolean(PLMosaicGetParameter( + poOpenInfo, papszOptions, "trust_cache", "FALSE")); + + poDS->bUseTMSForMain = CSLTestBoolean(PLMosaicGetParameter( + poOpenInfo, papszOptions, "use_tiles", "FALSE")); + + CSLDestroy(papszOptions); + papszOptions = NULL; + + if( poDS->osMosaic.size() ) + { + if( !poDS->OpenMosaic() ) + { + delete poDS; + poDS = NULL; + } + } + else + { + if( !poDS->ListSubdatasets() ) + { + delete poDS; + poDS = NULL; + } + else + { + char** papszMD = poDS->GetMetadata("SUBDATASETS"); + if( CSLCount(papszMD) == 2 ) + { + CPLString osOldFilename(poOpenInfo->pszFilename); + CPLString osMosaicConnectionString = CSLFetchNameValue(papszMD, "SUBDATASET_1_NAME"); + delete poDS; + GDALOpenInfo oOpenInfo(osMosaicConnectionString.c_str(), GA_ReadOnly); + oOpenInfo.papszOpenOptions = poOpenInfo->papszOpenOptions; + poDS = (PLMosaicDataset*) Open(&oOpenInfo); + if( poDS ) + poDS->SetDescription(osOldFilename); + } + } + } + + if( poDS ) + poDS->SetPamFlags(0); + + return( poDS ); +} + +/************************************************************************/ +/* ReplaceSubString() */ +/************************************************************************/ + +static void ReplaceSubString(CPLString &osTarget, + CPLString osPattern, + CPLString osReplacement) + +{ + // assumes only one occurance of osPattern + size_t pos = osTarget.find(osPattern); + if( pos == CPLString::npos ) + return; + + osTarget.replace(pos, osPattern.size(), osReplacement); +} + +/************************************************************************/ +/* GetMosaicCachePath() */ +/************************************************************************/ + +CPLString PLMosaicDataset::GetMosaicCachePath() +{ + if( osCachePathRoot.size() ) + { + CPLString osCachePath(CPLFormFilename(osCachePathRoot, "plmosaic_cache", NULL)); + CPLString osMosaicPath(CPLFormFilename(osCachePath, osMosaic, NULL)); + + return osMosaicPath; + } + return ""; +} + +/************************************************************************/ +/* CreateMosaicCachePathIfNecessary() */ +/************************************************************************/ + +void PLMosaicDataset::CreateMosaicCachePathIfNecessary() +{ + if( osCachePathRoot.size() ) + { + CPLString osCachePath(CPLFormFilename(osCachePathRoot, "plmosaic_cache", NULL)); + CPLString osMosaicPath(CPLFormFilename(osCachePath, osMosaic, NULL)); + + VSIStatBufL sStatBuf; + if( VSIStatL(osMosaicPath, &sStatBuf) != 0 ) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + VSIMkdir(osCachePathRoot, 0755); + VSIMkdir(osCachePath, 0755); + VSIMkdir(osMosaicPath, 0755); + CPLPopErrorHandler(); + } + } +} + +/************************************************************************/ +/* OpenMosaic() */ +/************************************************************************/ + +int PLMosaicDataset::OpenMosaic() +{ + CPLString osURL(osBaseURL); + if( osURL[osURL.size()-1] != '/' ) + osURL += '/'; + osURL += osMosaic; + json_object* poObj = RunRequest(osURL); + if( poObj == NULL ) + { + return FALSE; + } + + json_object* poCoordinateSystem = json_object_object_get(poObj, "coordinate_system"); + json_object* poDataType = json_object_object_get(poObj, "datatype"); + json_object* poQuadPattern = json_object_object_get(poObj, "quad_pattern"); + json_object* poQuadSize = json_object_object_get(poObj, "quad_size"); + json_object* poResolution = json_object_object_get(poObj, "resolution"); + json_object* poLinks = json_object_object_get(poObj, "links"); + json_object* poLinksQuads = NULL; + json_object* poLinksTiles = NULL; + if( poLinks != NULL && json_object_get_type(poLinks) == json_type_object ) + { + poLinksQuads = json_object_object_get(poLinks, "quads"); + poLinksTiles = json_object_object_get(poLinks, "tiles"); + } + if( poCoordinateSystem == NULL || json_object_get_type(poCoordinateSystem) != json_type_string || + poDataType == NULL || json_object_get_type(poDataType) != json_type_string || + poQuadPattern == NULL || json_object_get_type(poQuadPattern) != json_type_string || + poQuadSize == NULL || json_object_get_type(poQuadSize) != json_type_int || + poResolution == NULL || (json_object_get_type(poResolution) != json_type_int && + json_object_get_type(poResolution) != json_type_double) || + poLinksQuads == NULL || json_object_get_type(poLinksQuads) != json_type_string ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Missing required parameter"); + json_object_put(poObj); + return FALSE; + } + + const char* pszSRS = json_object_get_string(poCoordinateSystem); + if( !EQUAL(pszSRS, "EPSG:3857") ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Unsupported coordinate_system = %s", + pszSRS); + json_object_put(poObj); + return FALSE; + } + + OGRSpatialReference oSRS; + oSRS.SetFromUserInput(pszSRS); + oSRS.exportToWkt(&pszWKT); + + GDALDataType eDT = GDT_Unknown; + const char* pszDataType = json_object_get_string(poDataType); + if( EQUAL(pszDataType, "byte") ) + eDT = GDT_Byte; + else if( EQUAL(pszDataType, "uint16") ) + eDT = GDT_UInt16; + else if( EQUAL(pszDataType, "int16") ) + eDT = GDT_Int16; + else + { + CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data_type = %s", + pszDataType); + json_object_put(poObj); + return FALSE; + } + + if( bUseTMSForMain && eDT != GDT_Byte ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot use tile API for full resolution data on non Byte mosaic"); + bUseTMSForMain = FALSE; + } + + nQuadSize = json_object_get_int(poQuadSize); + if( nQuadSize <= 0 || (nQuadSize % 256) != 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Unsupported quad_size = %d", + nQuadSize); + json_object_put(poObj); + return FALSE; + } + + double dfResolution = json_object_get_double(poResolution); + if( EQUAL(pszSRS, "EPSG:3857") ) + { + double dfZoomLevel = log(GM_ZOOM_0 / dfResolution)/log(2.0); + nZoomLevel = (int)(dfZoomLevel + 0.1); + if( fabs(dfZoomLevel - nZoomLevel) > 1e-5 ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Unsupported resolution = %.12g", + dfResolution); + json_object_put(poObj); + return FALSE; + } + bHasGeoTransform = TRUE; + adfGeoTransform[0] = GM_ORIGIN; + adfGeoTransform[1] = dfResolution; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = -GM_ORIGIN; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = -dfResolution; + nRasterXSize = (int)(2 * -GM_ORIGIN / dfResolution + 0.5); + nRasterYSize = nRasterXSize; + } + + const char* pszQuadPattern = json_object_get_string(poQuadPattern); + if( strstr(pszQuadPattern, "{tilex:") == NULL || + strstr(pszQuadPattern, "{tiley:") == NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Invalid quad_pattern = %s", + pszQuadPattern); + json_object_put(poObj); + return FALSE; + } + osQuadPattern = pszQuadPattern; + osQuadsURL = json_object_get_string(poLinksQuads); + + // Use WMS/TMS driver for overviews (only for byte) + if( eDT == GDT_Byte && EQUAL(pszSRS, "EPSG:3857") && + poLinksTiles != NULL && json_object_get_type(poLinksTiles) == json_type_string ) + { + const char* pszLinksTiles = json_object_get_string(poLinksTiles); + if( strstr(pszLinksTiles, "{x}") == NULL || strstr(pszLinksTiles, "{y}") == NULL || + strstr(pszLinksTiles, "{z}") == NULL ) + { + CPLError(CE_Warning, CPLE_NotSupported, "Invalid links.tiles = %s", + pszLinksTiles); + } + else + { + CPLString osCacheStr; + if( osCachePathRoot.size() ) + { + osCacheStr = " "; + osCacheStr += GetMosaicCachePath(); + osCacheStr += "\n"; + } + + CPLString osTMSURL(pszLinksTiles); + if( strncmp(pszLinksTiles, "https://", strlen("https://")) == 0 ) + { + // Add API key as Basic auth + osTMSURL = "https://"; + osTMSURL += osAPIKey; + osTMSURL += ":@"; + osTMSURL += pszLinksTiles + strlen("https://"); + } + ReplaceSubString(osTMSURL, "{x}", "${x}"); + ReplaceSubString(osTMSURL, "{y}", "${y}"); + ReplaceSubString(osTMSURL, "{z}", "${z}"); + ReplaceSubString(osTMSURL, "{0-3}", "0"); + + CPLString osTMS = CPLSPrintf("\n" +" \n" +" %s\n" +" \n" +" \n" +" %.16g\n" +" %.16g\n" +" %.16g\n" +" %.16g\n" +" %d\n" +" 1\n" +" 1\n" +" top\n" +" \n" +" %s\n" +" 256\n" +" 256\n" +" 4\n" +"%s" +"", + osTMSURL.c_str(), + adfGeoTransform[0], + adfGeoTransform[3], + adfGeoTransform[0] + nRasterXSize * adfGeoTransform[1], + adfGeoTransform[3] + nRasterYSize * adfGeoTransform[5], + nZoomLevel, + pszSRS, + osCacheStr.c_str()); + //CPLDebug("PLMosaic", "TMS : %s", osTMS.c_str()); + + poTMSDS = (GDALDataset*)GDALOpenEx(osTMS, GDAL_OF_RASTER | GDAL_OF_INTERNAL, + NULL, NULL, NULL); + } + } + + if( bUseTMSForMain && poTMSDS == NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot find tile definition, so use_tiles will be ignored"); + bUseTMSForMain = FALSE; + } + + for(int i=0;i<4;i++) + SetBand(i + 1, new PLMosaicRasterBand(this, i + 1, eDT)); + + json_object* poFirstAcquired = json_object_object_get(poObj, "first_acquired"); + if( poFirstAcquired != NULL && json_object_get_type(poFirstAcquired) == json_type_string ) + { + SetMetadataItem("FIRST_ACQUIRED", + json_object_get_string(poFirstAcquired)); + } + json_object* poLastAcquired = json_object_object_get(poObj, "last_acquired"); + if( poLastAcquired != NULL && json_object_get_type(poLastAcquired) == json_type_string ) + { + SetMetadataItem("LAST_ACQUIRED", + json_object_get_string(poLastAcquired)); + } + json_object* poTitle = json_object_object_get(poObj, "title"); + if( poTitle != NULL && json_object_get_type(poTitle) == json_type_string ) + { + SetMetadataItem("TITLE", json_object_get_string(poTitle)); + } + + + json_object_put(poObj); + return TRUE; +} + +/************************************************************************/ +/* ListSubdatasets() */ +/************************************************************************/ + +int PLMosaicDataset::ListSubdatasets() +{ + CPLString osURL(osBaseURL); + CPLStringList aosSubdatasets; + while(osURL.size()) + { + json_object* poObj = RunRequest(osURL); + if( poObj == NULL ) + { + return FALSE; + } + + osURL = ""; + json_object* poLinks = json_object_object_get(poObj, "links"); + if( poLinks != NULL && json_object_get_type(poLinks) == json_type_object ) + { + json_object* poNext = json_object_object_get(poLinks, "next"); + if( poNext != NULL && json_object_get_type(poNext) == json_type_string ) + { + osURL = json_object_get_string(poNext); + } + } + + json_object* poMosaics = json_object_object_get(poObj, "mosaics"); + if( poMosaics == NULL || json_object_get_type(poMosaics) != json_type_array ) + { + json_object_put(poObj); + return FALSE; + } + + int nMosaics = json_object_array_length(poMosaics); + for(int i=0;i< nMosaics;i++) + { + const char* pszName = NULL; + const char* pszTitle = NULL; + const char* pszSelf = NULL; + const char* pszCoordinateSystem = NULL; + json_object* poMosaic = json_object_array_get_idx(poMosaics, i); + if( poMosaic && json_object_get_type(poMosaic) == json_type_object ) + { + json_object* poName = json_object_object_get(poMosaic, "name"); + if( poName != NULL && json_object_get_type(poName) == json_type_string ) + { + pszName = json_object_get_string(poName); + } + json_object* poTitle = json_object_object_get(poMosaic, "title"); + if( poTitle != NULL && json_object_get_type(poTitle) == json_type_string ) + { + pszTitle = json_object_get_string(poTitle); + } + poLinks = json_object_object_get(poMosaic, "links"); + if( poLinks != NULL && json_object_get_type(poLinks) == json_type_object ) + { + json_object* poSelf = json_object_object_get(poLinks, "self"); + if( poSelf != NULL && json_object_get_type(poSelf) == json_type_string ) + { + pszSelf = json_object_get_string(poSelf); + } + } + json_object* poCoordinateSystem = json_object_object_get(poMosaic, "coordinate_system"); + if( poCoordinateSystem && json_object_get_type(poCoordinateSystem) == json_type_string ) + { + pszCoordinateSystem = json_object_get_string(poCoordinateSystem); + } + } + + if( pszName && pszSelf && pszCoordinateSystem && + EQUAL(pszCoordinateSystem, "EPSG:3857") ) + { + int nDatasetIdx = aosSubdatasets.Count() / 2 + 1; + aosSubdatasets.AddNameValue( + CPLSPrintf("SUBDATASET_%d_NAME", nDatasetIdx), + CPLSPrintf("PLMOSAIC:mosaic=%s", pszName)); + if( pszTitle ) + { + aosSubdatasets.AddNameValue( + CPLSPrintf("SUBDATASET_%d_DESC", nDatasetIdx), + pszTitle); + } + else + { + aosSubdatasets.AddNameValue( + CPLSPrintf("SUBDATASET_%d_DESC", nDatasetIdx), + CPLSPrintf("Mosaic %s", pszName)); + } + } + } + + json_object_put(poObj); + } + SetMetadata(aosSubdatasets.List(), "SUBDATASETS"); + return TRUE; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* PLMosaicDataset::GetProjectionRef() +{ + return (pszWKT) ? pszWKT : ""; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr PLMosaicDataset::GetGeoTransform(double* padfGeoTransform) +{ + memcpy(padfGeoTransform, adfGeoTransform, 6 * sizeof(double)); + return ( bHasGeoTransform ) ? CE_None : CE_Failure; +} + +/************************************************************************/ +/* formatTileName() */ +/************************************************************************/ + +CPLString PLMosaicDataset::formatTileName(int tile_x, int tile_y) + +{ + CPLString result = osQuadPattern; + CPLString fragment; + size_t nPos; + int nDigits; + + nPos = osQuadPattern.find("{tilex:"); + nPos += strlen("{tilex:"); + if( sscanf(osQuadPattern.c_str() + nPos, "0%dd}", &nDigits) != 1 || + nDigits <= 0 || nDigits > 9 ) + return result; + fragment.Printf(CPLSPrintf("%%0%dd", nDigits), tile_x); + ReplaceSubString(result, CPLSPrintf("{tilex:0%dd}", nDigits), fragment); + + nPos = osQuadPattern.find("{tiley:"); + nPos += strlen("{tiley:"); + if( sscanf(osQuadPattern.c_str() + nPos, "0%dd}", &nDigits) != 1 || + nDigits <= 0 || nDigits > 9 ) + return result; + fragment.Printf(CPLSPrintf("%%0%dd", nDigits), tile_y); + ReplaceSubString(result, CPLSPrintf("{tiley:0%dd}", nDigits), fragment); + + fragment.Printf("%d", nZoomLevel); + ReplaceSubString(result, "{glevel:d}", fragment); + + return result; +} + +/************************************************************************/ +/* InsertNewDataset() */ +/************************************************************************/ + +void PLMosaicDataset::InsertNewDataset(CPLString osKey, GDALDataset* poDS) +{ + if( (int) oMapLinkedDatasets.size() == nCacheMaxSize ) + { + CPLDebug("PLMOSAIC", "Discarding older entry %s from cache", + psTail->osKey.c_str()); + oMapLinkedDatasets.erase(psTail->osKey); + PLLinkedDataset* psNewTail = psTail->psPrev; + psNewTail->psNext = NULL; + if( psTail->poDS ) + GDALClose( psTail->poDS ); + delete psTail; + psTail = psNewTail; + } + + PLLinkedDataset* psLinkedDataset = new PLLinkedDataset(); + if( psHead ) + psHead->psPrev = psLinkedDataset; + psLinkedDataset->osKey = osKey; + psLinkedDataset->psNext = psHead; + psLinkedDataset->poDS = poDS; + psHead = psLinkedDataset; + if( psTail == NULL ) + psTail = psHead; + oMapLinkedDatasets[osKey] = psLinkedDataset; +} + +/************************************************************************/ +/* OpenAndInsertNewDataset() */ +/************************************************************************/ + +GDALDataset* PLMosaicDataset::OpenAndInsertNewDataset(CPLString osTmpFilename, + CPLString osTilename) +{ + const char* const apszAllowedDrivers[2] = { "GTiff", NULL }; + GDALDataset* poDS = (GDALDataset*) + GDALOpenEx(osTmpFilename, GDAL_OF_RASTER | GDAL_OF_INTERNAL, + apszAllowedDrivers, NULL, NULL); + if( poDS != NULL ) + { + if( poDS->GetRasterXSize() != nQuadSize || + poDS->GetRasterYSize() != nQuadSize || + poDS->GetRasterCount() != 4 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Inconsistent metatile characteristics"); + GDALClose(poDS); + poDS = NULL; + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid GTiff dataset: %s", + osTilename.c_str()); + } + + InsertNewDataset(osTilename, poDS); + return poDS; +} + +/************************************************************************/ +/* GetMetaTile() */ +/************************************************************************/ + +GDALDataset* PLMosaicDataset::GetMetaTile(int tile_x, int tile_y) +{ + CPLString osTilename = formatTileName(tile_x, tile_y); + std::map::const_iterator it = + oMapLinkedDatasets.find(osTilename); + if( it == oMapLinkedDatasets.end() ) + { + CPLString osTmpFilename; + + CPLString osMosaicPath(GetMosaicCachePath()); + osTmpFilename = CPLFormFilename(osMosaicPath, + CPLSPrintf("%s_%s.tif", osMosaic.c_str(), CPLGetFilename(osTilename)), NULL); + VSIStatBufL sStatBuf; + if( osCachePathRoot.size() && VSIStatL(osTmpFilename, &sStatBuf) == 0 ) + { + if( bTrustCache ) + { + return OpenAndInsertNewDataset(osTmpFilename, osTilename); + } + + CPLDebug("PLMOSAIC", "File %s exists. Checking if it is up-to-date...", + osTmpFilename.c_str()); + // Fetch metatile metadata + json_object* poObj = RunRequest((osQuadsURL + osTilename).c_str()); + if( poObj == NULL ) + { + CPLDebug("PLMOSAIC", "Cannot get tile metadata"); + InsertNewDataset(osTilename, NULL); + return NULL; + } + + // Currently we only check by file size, which should be good enough + // as the metatiles are compressed, so a change in content is likely + // to cause a change in filesize. Use of a signature would be better + // though if available in the metadata + int nFileSize = 0; + json_object* poProperties = json_object_object_get(poObj, "properties"); + if( poProperties && json_object_get_type(poProperties) == json_type_object ) + { + json_object* poFileSize = json_object_object_get(poProperties, "file_size"); + nFileSize = json_object_get_int(poFileSize); + } + json_object_put(poObj); + if( (int)sStatBuf.st_size == nFileSize ) + { + CPLDebug("PLMOSAIC", "Cached tile is up-to-date"); + return OpenAndInsertNewDataset(osTmpFilename, osTilename); + } + else + { + CPLDebug("PLMOSAIC", "Cached tile is not up-to-date"); + VSIUnlink(osTmpFilename); + } + } + + // Fetch the GeoTIFF now + CPLString osURL = osQuadsURL; + osURL += osTilename; + osURL += "/full"; + + CPLHTTPResult* psResult = Download(osURL, TRUE); + if( psResult == NULL ) + { + InsertNewDataset(osTilename, NULL); + return NULL; + } + + CreateMosaicCachePathIfNecessary(); + + VSILFILE* fp = osCachePathRoot.size() ? VSIFOpenL(osTmpFilename, "wb") : NULL; + if( fp ) + { + VSIFWriteL(psResult->pabyData, 1, psResult->nDataLen, fp); + VSIFCloseL(fp); + } + else + { + // In case there's no temporary path or it is not writable + // use a in-memory dataset, and limit the cache to only one + if( osCachePathRoot.size() && nCacheMaxSize > 1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot write into %s. Using /vsimem and reduce cache to 1 entry", + osCachePathRoot.c_str()); + FlushDatasetsCache(); + nCacheMaxSize = 1; + } + osTmpFilename = + CPLSPrintf("/vsimem/single_tile_plmosaic_cache/%s/%d_%d.tif", + osMosaic.c_str(), tile_x, tile_y); + fp = VSIFOpenL(osTmpFilename, "wb"); + if( fp ) + { + VSIFWriteL(psResult->pabyData, 1, psResult->nDataLen, fp); + VSIFCloseL(fp); + } + } + CPLHTTPDestroyResult(psResult); + GDALDataset* poDS = OpenAndInsertNewDataset(osTmpFilename, osTilename); + + if( strncmp(osTmpFilename, "/vsimem/single_tile_plmosaic_cache/", + strlen("/vsimem/single_tile_plmosaic_cache/")) == 0 ) + VSIUnlink(osTilename); + + return poDS; + } + + // Move link to head of MRU list + PLLinkedDataset* psLinkedDataset = it->second; + GDALDataset* poDS = psLinkedDataset->poDS; + if( psLinkedDataset != psHead ) + { + if( psLinkedDataset == psTail ) + psTail = psLinkedDataset->psPrev; + if( psLinkedDataset->psPrev ) + psLinkedDataset->psPrev->psNext = psLinkedDataset->psNext; + if( psLinkedDataset->psNext ) + psLinkedDataset->psNext->psPrev = psLinkedDataset->psPrev; + psLinkedDataset->psNext = psHead; + psLinkedDataset->psPrev = NULL; + psHead->psPrev = psLinkedDataset; + psHead = psLinkedDataset; + } + + return poDS; +} + +/************************************************************************/ +/* GetLocationInfo() */ +/************************************************************************/ + +const char* PLMosaicDataset::GetLocationInfo(int nPixel, int nLine) +{ + int nBlockXSize, nBlockYSize; + GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize); + + int nBlockXOff = nPixel / nBlockXSize; + int nBlockYOff = nLine / nBlockYSize; + int bottom_yblock = (nRasterYSize - nBlockYOff * nBlockYSize) / nBlockYSize - 1; + + int meta_tile_x = (nBlockXOff * nBlockXSize) / nQuadSize; + int meta_tile_y = (bottom_yblock * nBlockYSize) / nQuadSize; + + CPLString osQuadURL = osQuadsURL; + CPLString osTilename = formatTileName(meta_tile_x, meta_tile_y); + osQuadURL += osTilename; + + if( meta_tile_x != nLastMetaTileX || meta_tile_y != nLastMetaTileY ) + { + CPLHTTPResult* psResult; + psResult = Download(osQuadURL, TRUE); + if( psResult ) + osLastQuadInformation = (const char*) psResult->pabyData; + else + osLastQuadInformation = ""; + CPLHTTPDestroyResult(psResult); + + CPLString osQuadScenesURL = osQuadURL + "/scenes/"; + + psResult = Download(osQuadScenesURL, TRUE); + if( psResult ) + osLastQuadSceneInformation = (const char*) psResult->pabyData; + else + osLastQuadSceneInformation = ""; + CPLHTTPDestroyResult(psResult); + + nLastMetaTileX = meta_tile_x; + nLastMetaTileY = meta_tile_y; + } + + osLastRetGetLocationInfo = ""; + + CPLXMLNode* psRoot = CPLCreateXMLNode(NULL, CXT_Element, "LocationInfo"); + + if( osLastQuadInformation.size() ) + { + const char* const apszAllowedDrivers[2] = { "GeoJSON", NULL }; + const char* const apszOptions[2] = { "FLATTEN_NESTED_ATTRIBUTES=YES", NULL }; + CPLString osTmpJSonFilename; + osTmpJSonFilename.Printf("/vsimem/plmosaic/%p/quad.json", this); + VSIFCloseL(VSIFileFromMemBuffer(osTmpJSonFilename, + (GByte*)osLastQuadInformation.c_str(), + osLastQuadInformation.size(), FALSE)); + GDALDataset* poDS = (GDALDataset*) GDALOpenEx(osTmpJSonFilename, GDAL_OF_VECTOR, + apszAllowedDrivers, apszOptions, NULL); + VSIUnlink(osTmpJSonFilename); + + if( poDS ) + { + CPLXMLNode* psQuad = CPLCreateXMLNode(psRoot, CXT_Element, "Quad"); + OGRLayer* poLayer = poDS->GetLayer(0); + OGRFeature* poFeat; + while( (poFeat = poLayer->GetNextFeature()) != NULL ) + { + for(int i=0;iGetFieldCount();i++) + { + if( poFeat->IsFieldSet(i) ) + { + CPLXMLNode* psItem = CPLCreateXMLNode(psQuad, + CXT_Element, poFeat->GetFieldDefnRef(i)->GetNameRef()); + CPLCreateXMLNode(psItem, CXT_Text, poFeat->GetFieldAsString(i)); + } + } + OGRGeometry* poGeom = poFeat->GetGeometryRef(); + if( poGeom ) + { + CPLXMLNode* psItem = CPLCreateXMLNode(psQuad, CXT_Element, "geometry"); + char* pszWKT = NULL; + poGeom->exportToWkt(&pszWKT); + CPLCreateXMLNode(psItem, CXT_Text, pszWKT); + CPLFree(pszWKT); + } + delete poFeat; + } + + GDALClose(poDS); + } + } + + if( osLastQuadSceneInformation.size() && pszWKT != NULL ) + { + const char* const apszAllowedDrivers[2] = { "GeoJSON", NULL }; + const char* const apszOptions[2] = { "FLATTEN_NESTED_ATTRIBUTES=YES", NULL }; + CPLString osTmpJSonFilename; + osTmpJSonFilename.Printf("/vsimem/plmosaic/%p/scenes.json", this); + VSIFCloseL(VSIFileFromMemBuffer(osTmpJSonFilename, + (GByte*)osLastQuadSceneInformation.c_str(), + osLastQuadSceneInformation.size(), FALSE)); + GDALDataset* poDS = (GDALDataset*) GDALOpenEx(osTmpJSonFilename, GDAL_OF_VECTOR, + apszAllowedDrivers, apszOptions, NULL); + VSIUnlink(osTmpJSonFilename); + + OGRSpatialReference oSRSSrc, oSRSDst; + oSRSSrc.SetFromUserInput(pszWKT); + oSRSDst.importFromEPSG(4326); + OGRCoordinateTransformation* poCT = OGRCreateCoordinateTransformation(&oSRSSrc, + &oSRSDst); + double x = adfGeoTransform[0] + nPixel * adfGeoTransform[1]; + double y = adfGeoTransform[3] + nLine * adfGeoTransform[5]; + if( poDS && poCT && poCT->Transform(1, &x, &y)) + { + CPLXMLNode* psScenes = NULL; + OGRLayer* poLayer = poDS->GetLayer(0); + poLayer->SetSpatialFilterRect(x,y,x,y); + OGRFeature* poFeat; + while( (poFeat = poLayer->GetNextFeature()) != NULL ) + { + OGRGeometry* poGeom = poFeat->GetGeometryRef(); + if( poGeom ) + { + if( psScenes == NULL ) + psScenes = CPLCreateXMLNode(psRoot, CXT_Element, "Scenes"); + CPLXMLNode* psScene = CPLCreateXMLNode(psScenes, CXT_Element, "Scene"); + for(int i=0;iGetFieldCount();i++) + { + if( poFeat->IsFieldSet(i) ) + { + CPLXMLNode* psItem = CPLCreateXMLNode(psScene, + CXT_Element, poFeat->GetFieldDefnRef(i)->GetNameRef()); + CPLCreateXMLNode(psItem, CXT_Text, poFeat->GetFieldAsString(i)); + } + } + CPLXMLNode* psItem = CPLCreateXMLNode(psScene, CXT_Element, "geometry"); + char* pszWKT = NULL; + poGeom->exportToWkt(&pszWKT); + CPLCreateXMLNode(psItem, CXT_Text, pszWKT); + CPLFree(pszWKT); + } + delete poFeat; + } + } + delete poCT; + if( poDS ) + GDALClose(poDS); + } + + char* pszXML = CPLSerializeXMLTree(psRoot); + CPLDestroyXMLNode(psRoot); + osLastRetGetLocationInfo = pszXML; + CPLFree(pszXML); + + return osLastRetGetLocationInfo.c_str(); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr PLMosaicDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) +{ + if( bUseTMSForMain && poTMSDS ) + return poTMSDS->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + psExtraArg ); + + return BlockBasedRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + psExtraArg ); +} + +/************************************************************************/ +/* GDALRegister_PLMOSAIC() */ +/************************************************************************/ + +void GDALRegister_PLMOSAIC() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "PLMOSAIC" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "PLMOSAIC" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Planet Labs Mosaics API" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_plmosaic.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CONNECTION_PREFIX, "PLMOSAIC:" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " ); + + poDriver->pfnIdentify = PLMosaicDataset::Identify; + poDriver->pfnOpen = PLMosaicDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/png/GNUmakefile b/bazaar/plugin/gdal/frmts/png/GNUmakefile new file mode 100644 index 000000000..f39c869ed --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/GNUmakefile @@ -0,0 +1,50 @@ +# $Id: GNUmakefile 27976 2014-11-17 13:13:44Z rouault $ +# +# Makefile to build libpng using GNU Make and GCC. +# + +include ../../GDALmake.opt + +ifeq ($(PNG_SETTING),internal) +XTRA_OPT = -DPNG_NO_GLOBAL_ARRAYS -Ilibpng +OBJ = \ + png.o \ + pngerror.o \ + pnggccrd.o \ + pngget.o \ + pngmem.o \ + pngpread.o \ + pngread.o \ + pngrio.o \ + pngrtran.o \ + pngrutil.o \ + pngset.o \ + pngtrans.o \ + pngvcrd.o \ + pngwio.o \ + pngwrite.o \ + pngwtran.o \ + pngwutil.o \ + pngdataset.o + +else +OBJ = pngdataset.o +endif + +ifeq ($(LIBZ_SETTING),internal) +XTRA_OPT := $(XTRA_OPT) -I../zlib +endif + +CPPFLAGS := $(XTRA_OPT) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o libpng/*.o $(O_OBJ) + +../o/%.$(OBJ_EXT): libpng/%.c + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/png/libpng/LICENSE b/bazaar/plugin/gdal/frmts/png/libpng/LICENSE new file mode 100644 index 000000000..a684fe5a8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/LICENSE @@ -0,0 +1,111 @@ + +This copy of the libpng notices is provided for your convenience. In case of +any discrepancy between this copy and the notices in the file png.h that is +included in the libpng distribution, the latter shall prevail. + +COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: + +If you modify libpng you may insert additional notices immediately following +this sentence. + +This code is released under the libpng license. + +libpng versions 1.2.6, August 15, 2004, through 1.2.50, July 10, 2012, are +Copyright (c) 2004, 2006-2009 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-1.2.5 +with the following individual added to the list of Contributing Authors + + Cosmin Truta + +libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are +Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-1.0.6 +with the following individuals added to the list of Contributing Authors + + Simon-Pierre Cadieux + Eric S. Raymond + Gilles Vollant + +and with the following additions to the disclaimer: + + There is no warranty against interference with your enjoyment of the + library or against infringement. There is no warranty that our + efforts or the library will fulfill any of your particular purposes + or needs. This library is provided with all faults, and the entire + risk of satisfactory quality, performance, accuracy, and effort is with + the user. + +libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are +Copyright (c) 1998, 1999 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-0.96, +with the following individuals added to the list of Contributing Authors: + + Tom Lane + Glenn Randers-Pehrson + Willem van Schaik + +libpng versions 0.89, June 1996, through 0.96, May 1997, are +Copyright (c) 1996, 1997 Andreas Dilger +Distributed according to the same disclaimer and license as libpng-0.88, +with the following individuals added to the list of Contributing Authors: + + John Bowler + Kevin Bracey + Sam Bushell + Magnus Holmgren + Greg Roelofs + Tom Tanner + +libpng versions 0.5, May 1995, through 0.88, January 1996, are +Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. + +For the purposes of this copyright and license, "Contributing Authors" +is defined as the following set of individuals: + + Andreas Dilger + Dave Martindale + Guy Eric Schalnat + Paul Schmidt + Tim Wegner + +The PNG Reference Library is supplied "AS IS". The Contributing Authors +and Group 42, Inc. disclaim all warranties, expressed or implied, +including, without limitation, the warranties of merchantability and of +fitness for any purpose. The Contributing Authors and Group 42, Inc. +assume no liability for direct, indirect, incidental, special, exemplary, +or consequential damages, which may result from the use of the PNG +Reference Library, even if advised of the possibility of such damage. + +Permission is hereby granted to use, copy, modify, and distribute this +source code, or portions hereof, for any purpose, without fee, subject +to the following restrictions: + +1. The origin of this source code must not be misrepresented. + +2. Altered versions must be plainly marked as such and must not + be misrepresented as being the original source. + +3. This Copyright notice may not be removed or altered from any + source or altered source distribution. + +The Contributing Authors and Group 42, Inc. specifically permit, without +fee, and encourage the use of this source code as a component to +supporting the PNG file format in commercial products. If you use this +source code in a product, acknowledgment is not required but would be +appreciated. + + +A "png_get_copyright" function is available, for convenient use in "about" +boxes and the like: + + printf("%s",png_get_copyright(NULL)); + +Also, the PNG logo (in PNG format, of course) is supplied in the +files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + +Libpng is OSI Certified Open Source Software. OSI Certified Open Source is a +certification mark of the Open Source Initiative. + +Glenn Randers-Pehrson +glennrp at users.sourceforge.net +July 10, 2012 diff --git a/bazaar/plugin/gdal/frmts/png/libpng/README b/bazaar/plugin/gdal/frmts/png/libpng/README new file mode 100644 index 000000000..771ca50c6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/README @@ -0,0 +1,17 @@ +$Id$ + +*** IMPORTANT *** +Updating libpng in GDAL tree should follow update of zlib library in frmts/zlib. +***************** + +libpng 1.2.52 is the official PNG reference library. +It supports almost all PNG features, is extensible, +and has been extensively tested for over 15 years. + +http://www.libpng.org + + +GDAL changes (stored in libpng_gdal.patch) +------------ + +* Make screwy MSPaint "zero chunks" only a warning, not error. See r18808 and #3416 diff --git a/bazaar/plugin/gdal/frmts/png/libpng/libpng_gdal.patch b/bazaar/plugin/gdal/frmts/png/libpng/libpng_gdal.patch new file mode 100644 index 000000000..cc1134f15 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/libpng_gdal.patch @@ -0,0 +1,22 @@ +--- /home/even/libpng-1.2.46/pngrutil.c 2011-07-09 12:30:23.000000000 +0200 ++++ pngrutil.c 2011-07-29 19:52:18.831921846 +0200 +@@ -2404,7 +2404,8 @@ + #endif + ) + #endif +- png_chunk_error(png_ptr, "unknown critical chunk"); ++ /* GDAL change : png_chunk_error -> png_chunk_warning */ ++ png_chunk_warning(png_ptr, "unknown critical chunk"); + } + + #ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +@@ -2488,7 +2489,8 @@ + if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) || + isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3])) + { +- png_chunk_error(png_ptr, "invalid chunk type"); ++ /* GDAL change : png_chunk_error -> png_chunk_warning */ ++ png_chunk_warning(png_ptr, "invalid chunk type"); + } + } + diff --git a/bazaar/plugin/gdal/frmts/png/libpng/makefile.vc b/bazaar/plugin/gdal/frmts/png/libpng/makefile.vc new file mode 100644 index 000000000..a6b3e15a7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/makefile.vc @@ -0,0 +1,38 @@ +# $Id: makefile.vc 10656 2007-01-19 01:31:01Z mloskot $ +# +# Makefile to build libpng using NMAKE and Visual C++ compiler. +# +OBJ = \ + png.obj \ + pngerror.obj \ + pnggccrd.obj \ + pngget.obj \ + pngmem.obj \ + pngpread.obj \ + pngread.obj \ + pngrio.obj \ + pngrtran.obj \ + pngrutil.obj \ + pngset.obj \ + pngtrans.obj \ + pngvcrd.obj \ + pngwio.obj \ + pngwrite.obj \ + pngwtran.obj \ + pngwutil.obj + +GDAL_ROOT = ..\..\.. + +EXTRAFLAGS = -I..\..\zlib + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\..\o + +libpng.lib: $(OBJ) + LIB /OUT:libpng.lib $(OBJ) + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/png/libpng/png.c b/bazaar/plugin/gdal/frmts/png/libpng/png.c new file mode 100644 index 000000000..2ebb6f752 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/png.c @@ -0,0 +1,1092 @@ + +/* png.c - location for general purpose libpng functions + * + * Last changed in libpng 1.2.51 [February 6, 2014] + * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_INTERNAL +#define PNG_NO_EXTERN +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef version_1_2_52 Your_png_h_is_not_version_1_2_52; + +/* Version information for C files. This had better match the version + * string defined in png.h. + */ + +#ifdef PNG_USE_GLOBAL_ARRAYS +/* png_libpng_ver was changed to a function in version 1.0.5c */ +PNG_CONST char png_libpng_ver[18] = PNG_LIBPNG_VER_STRING; + +#ifdef PNG_READ_SUPPORTED + +/* png_sig was changed to a function in version 1.0.5c */ +/* Place to hold the signature string for a PNG file. */ +PNG_CONST png_byte FARDATA png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; +#endif /* PNG_READ_SUPPORTED */ + +/* Invoke global declarations for constant strings for known chunk types */ +PNG_IHDR; +PNG_IDAT; +PNG_IEND; +PNG_PLTE; +PNG_bKGD; +PNG_cHRM; +PNG_gAMA; +PNG_hIST; +PNG_iCCP; +PNG_iTXt; +PNG_oFFs; +PNG_pCAL; +PNG_sCAL; +PNG_pHYs; +PNG_sBIT; +PNG_sPLT; +PNG_sRGB; +PNG_tEXt; +PNG_tIME; +PNG_tRNS; +PNG_zTXt; + +#ifdef PNG_READ_SUPPORTED +/* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + +/* Start of interlace block */ +PNG_CONST int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + +/* Offset to next interlace block */ +PNG_CONST int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + +/* Start of interlace block in the y direction */ +PNG_CONST int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + +/* Offset to next interlace block in the y direction */ +PNG_CONST int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + +/* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h +PNG_CONST int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; +*/ + +/* Mask to determine which pixels are valid in a pass */ +PNG_CONST int FARDATA png_pass_mask[] = + {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; + +/* Mask to determine which pixels to overwrite while displaying */ +PNG_CONST int FARDATA png_pass_dsp_mask[] + = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; + +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_USE_GLOBAL_ARRAYS */ + +/* Tells libpng that we have already handled the first "num_bytes" bytes + * of the PNG file signature. If the PNG data is embedded into another + * stream we can set num_bytes = 8 so that libpng will not attempt to read + * or write any of the magic bytes before it starts on the IHDR. + */ + +#ifdef PNG_READ_SUPPORTED +void PNGAPI +png_set_sig_bytes(png_structp png_ptr, int num_bytes) +{ + png_debug(1, "in png_set_sig_bytes"); + + if (png_ptr == NULL) + return; + + if (num_bytes > 8) + png_error(png_ptr, "Too many bytes for PNG signature."); + + png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes); +} + +/* Checks whether the supplied bytes match the PNG signature. We allow + * checking less than the full 8-byte signature so that those apps that + * already read the first few bytes of a file to determine the file type + * can simply check the remaining bytes for extra assurance. Returns + * an integer less than, equal to, or greater than zero if sig is found, + * respectively, to be less than, to match, or be greater than the correct + * PNG signature (this is the same behaviour as strcmp, memcmp, etc). + */ +int PNGAPI +png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + if (num_to_check > 8) + num_to_check = 8; + else if (num_to_check < 1) + return (-1); + + if (start > 7) + return (-1); + + if (start + num_to_check > 8) + num_to_check = 8 - start; + + return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check))); +} + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* (Obsolete) function to check signature bytes. It does not allow one + * to check a partial signature. This function might be removed in the + * future - use png_sig_cmp(). Returns true (nonzero) if the file is PNG. + */ +int PNGAPI +png_check_sig(png_bytep sig, int num) +{ + return ((int)!png_sig_cmp(sig, (png_size_t)0, (png_size_t)num)); +} +#endif +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Function to allocate memory for zlib and clear it to 0. */ +#ifdef PNG_1_0_X +voidpf PNGAPI +#else +voidpf /* PRIVATE */ +#endif +png_zalloc(voidpf png_ptr, uInt items, uInt size) +{ + png_voidp ptr; + png_structp p=(png_structp)png_ptr; + png_uint_32 save_flags=p->flags; + png_uint_32 num_bytes; + + if (png_ptr == NULL) + return (NULL); + if (items > PNG_UINT_32_MAX/size) + { + png_warning (p, "Potential overflow in png_zalloc()"); + return (NULL); + } + num_bytes = (png_uint_32)items * size; + + p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes); + p->flags=save_flags; + +#if defined(PNG_1_0_X) && !defined(PNG_NO_ZALLOC_ZERO) + if (ptr == NULL) + return ((voidpf)ptr); + + if (num_bytes > (png_uint_32)0x8000L) + { + png_memset(ptr, 0, (png_size_t)0x8000L); + png_memset((png_bytep)ptr + (png_size_t)0x8000L, 0, + (png_size_t)(num_bytes - (png_uint_32)0x8000L)); + } + else + { + png_memset(ptr, 0, (png_size_t)num_bytes); + } +#endif + return ((voidpf)ptr); +} + +/* Function to free memory for zlib */ +#ifdef PNG_1_0_X +void PNGAPI +#else +void /* PRIVATE */ +#endif +png_zfree(voidpf png_ptr, voidpf ptr) +{ + png_free((png_structp)png_ptr, (png_voidp)ptr); +} + +/* Reset the CRC variable to 32 bits of 1's. Care must be taken + * in case CRC is > 32 bits to leave the top bits 0. + */ +void /* PRIVATE */ +png_reset_crc(png_structp png_ptr) +{ + png_ptr->crc = crc32(0, Z_NULL, 0); +} + +/* Calculate the CRC over a section of data. We can only pass as + * much data to this routine as the largest single buffer size. We + * also check that this data will actually be used before going to the + * trouble of calculating it. + */ +void /* PRIVATE */ +png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length) +{ + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + if (need_crc) + png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length); +} + +/* Allocate the memory for an info_struct for the application. We don't + * really need the png_ptr, but it could potentially be useful in the + * future. This should be used in favour of malloc(png_sizeof(png_info)) + * and png_info_init() so that applications that want to use a shared + * libpng don't have to be recompiled if png_info changes size. + */ +png_infop PNGAPI +png_create_info_struct(png_structp png_ptr) +{ + png_infop info_ptr; + + png_debug(1, "in png_create_info_struct"); + + if (png_ptr == NULL) + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO, + png_ptr->malloc_fn, png_ptr->mem_ptr); +#else + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); +#endif + if (info_ptr != NULL) + png_info_init_3(&info_ptr, png_sizeof(png_info)); + + return (info_ptr); +} + +/* This function frees the memory associated with a single info struct. + * Normally, one would use either png_destroy_read_struct() or + * png_destroy_write_struct() to free an info struct, but this may be + * useful for some applications. + */ +void PNGAPI +png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) +{ + png_infop info_ptr = NULL; + + png_debug(1, "in png_destroy_info_struct"); + + if (png_ptr == NULL) + return; + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + png_info_destroy(png_ptr, info_ptr); + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn, + png_ptr->mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } +} + +/* Initialize the info structure. This is now an internal function (0.89) + * and applications using it are urged to use png_create_info_struct() + * instead. + */ +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +#undef png_info_init +void PNGAPI +png_info_init(png_infop info_ptr) +{ + /* We only come here via pre-1.0.12-compiled applications */ + png_info_init_3(&info_ptr, 0); +} +#endif + +void PNGAPI +png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) +{ + png_infop info_ptr = *ptr_ptr; + + png_debug(1, "in png_info_init_3"); + + if (info_ptr == NULL) + return; + + if (png_sizeof(png_info) > png_info_struct_size) + { + png_destroy_struct(info_ptr); + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); + *ptr_ptr = info_ptr; + } + + /* Set everything to 0 */ + png_memset(info_ptr, 0, png_sizeof(png_info)); +} + +#ifdef PNG_FREE_ME_SUPPORTED +void PNGAPI +png_data_freer(png_structp png_ptr, png_infop info_ptr, + int freer, png_uint_32 mask) +{ + png_debug(1, "in png_data_freer"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (freer == PNG_DESTROY_WILL_FREE_DATA) + info_ptr->free_me |= mask; + else if (freer == PNG_USER_WILL_FREE_DATA) + info_ptr->free_me &= ~mask; + else + png_warning(png_ptr, + "Unknown freer parameter in png_data_freer."); +} +#endif + +void PNGAPI +png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask, + int num) +{ + png_debug(1, "in png_free_data"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + +#ifdef PNG_TEXT_SUPPORTED + /* Free text item num or (if num == -1) all text items */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) +#else + if (mask & PNG_FREE_TEXT) +#endif + { + if (num != -1) + { + if (info_ptr->text && info_ptr->text[num].key) + { + png_free(png_ptr, info_ptr->text[num].key); + info_ptr->text[num].key = NULL; + } + } + else + { + int i; + for (i = 0; i < info_ptr->num_text; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); + png_free(png_ptr, info_ptr->text); + info_ptr->text = NULL; + info_ptr->num_text=0; + } + } +#endif + +#ifdef PNG_tRNS_SUPPORTED + /* Free any tRNS entry */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) +#else + if ((mask & PNG_FREE_TRNS) && (png_ptr->flags & PNG_FLAG_FREE_TRNS)) +#endif + { + png_free(png_ptr, info_ptr->trans); + info_ptr->trans = NULL; + info_ptr->valid &= ~PNG_INFO_tRNS; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif + } +#endif + +#ifdef PNG_sCAL_SUPPORTED + /* Free any sCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) +#else + if (mask & PNG_FREE_SCAL) +#endif + { +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, info_ptr->scal_s_width); + png_free(png_ptr, info_ptr->scal_s_height); + info_ptr->scal_s_width = NULL; + info_ptr->scal_s_height = NULL; +#endif + info_ptr->valid &= ~PNG_INFO_sCAL; + } +#endif + +#ifdef PNG_pCAL_SUPPORTED + /* Free any pCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) +#else + if (mask & PNG_FREE_PCAL) +#endif + { + png_free(png_ptr, info_ptr->pcal_purpose); + png_free(png_ptr, info_ptr->pcal_units); + info_ptr->pcal_purpose = NULL; + info_ptr->pcal_units = NULL; + if (info_ptr->pcal_params != NULL) + { + int i; + for (i = 0; i < (int)info_ptr->pcal_nparams; i++) + { + png_free(png_ptr, info_ptr->pcal_params[i]); + info_ptr->pcal_params[i] = NULL; + } + png_free(png_ptr, info_ptr->pcal_params); + info_ptr->pcal_params = NULL; + } + info_ptr->valid &= ~PNG_INFO_pCAL; + } +#endif + +#ifdef PNG_iCCP_SUPPORTED + /* Free any iCCP entry */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) +#else + if (mask & PNG_FREE_ICCP) +#endif + { + png_free(png_ptr, info_ptr->iccp_name); + png_free(png_ptr, info_ptr->iccp_profile); + info_ptr->iccp_name = NULL; + info_ptr->iccp_profile = NULL; + info_ptr->valid &= ~PNG_INFO_iCCP; + } +#endif + +#ifdef PNG_sPLT_SUPPORTED + /* Free a given sPLT entry, or (if num == -1) all sPLT entries */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) +#else + if (mask & PNG_FREE_SPLT) +#endif + { + if (num != -1) + { + if (info_ptr->splt_palettes) + { + png_free(png_ptr, info_ptr->splt_palettes[num].name); + png_free(png_ptr, info_ptr->splt_palettes[num].entries); + info_ptr->splt_palettes[num].name = NULL; + info_ptr->splt_palettes[num].entries = NULL; + } + } + else + { + if (info_ptr->splt_palettes_num) + { + int i; + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i); + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = NULL; + info_ptr->splt_palettes_num = 0; + } + info_ptr->valid &= ~PNG_INFO_sPLT; + } + } +#endif + +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED + if (png_ptr->unknown_chunk.data) + { + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) +#else + if (mask & PNG_FREE_UNKN) +#endif + { + if (num != -1) + { + if (info_ptr->unknown_chunks) + { + png_free(png_ptr, info_ptr->unknown_chunks[num].data); + info_ptr->unknown_chunks[num].data = NULL; + } + } + else + { + int i; + + if (info_ptr->unknown_chunks_num) + { + for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i); + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + info_ptr->unknown_chunks_num = 0; + } + } + } +#endif + +#ifdef PNG_hIST_SUPPORTED + /* Free any hIST entry */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_HIST) & info_ptr->free_me) +#else + if ((mask & PNG_FREE_HIST) && (png_ptr->flags & PNG_FLAG_FREE_HIST)) +#endif + { + png_free(png_ptr, info_ptr->hist); + info_ptr->hist = NULL; + info_ptr->valid &= ~PNG_INFO_hIST; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif + } +#endif + + /* Free any PLTE entry that was internally allocated */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) +#else + if ((mask & PNG_FREE_PLTE) && (png_ptr->flags & PNG_FLAG_FREE_PLTE)) +#endif + { + png_zfree(png_ptr, info_ptr->palette); + info_ptr->palette = NULL; + info_ptr->valid &= ~PNG_INFO_PLTE; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif + info_ptr->num_palette = 0; + } + +#ifdef PNG_INFO_IMAGE_SUPPORTED + /* Free any image bits attached to the info structure */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) +#else + if (mask & PNG_FREE_ROWS) +#endif + { + if (info_ptr->row_pointers) + { + int row; + for (row = 0; row < (int)info_ptr->height; row++) + { + png_free(png_ptr, info_ptr->row_pointers[row]); + info_ptr->row_pointers[row] = NULL; + } + png_free(png_ptr, info_ptr->row_pointers); + info_ptr->row_pointers = NULL; + } + info_ptr->valid &= ~PNG_INFO_IDAT; + } +#endif + +#ifdef PNG_FREE_ME_SUPPORTED + if (num == -1) + info_ptr->free_me &= ~mask; + else + info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL); +#endif +} + +/* This is an internal routine to free any memory that the info struct is + * pointing to before re-using it or freeing the struct itself. Recall + * that png_free() checks for NULL pointers for us. + */ +void /* PRIVATE */ +png_info_destroy(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_info_destroy"); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list = NULL; + png_ptr->num_chunk_list = 0; + } +#endif + + png_info_init_3(&info_ptr, png_sizeof(png_info)); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +/* This function returns a pointer to the io_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy() or png_read_destroy() are called. + */ +png_voidp PNGAPI +png_get_io_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + return (png_ptr->io_ptr); +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the default input/output functions for the PNG file. If you + * use your own read or write routines, you can call either png_set_read_fn() + * or png_set_write_fn() instead of png_init_io(). If you have defined + * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't + * necessarily available. + */ +void PNGAPI +png_init_io(png_structp png_ptr, png_FILE_p fp) +{ + png_debug(1, "in png_init_io"); + + if (png_ptr == NULL) + return; + + png_ptr->io_ptr = (png_voidp)fp; +} +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED +/* Convert the supplied time into an RFC 1123 string suitable for use in + * a "Creation Time" or other text-based time string. + */ +png_charp PNGAPI +png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime) +{ + static PNG_CONST char short_months[12][4] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (png_ptr == NULL) + return (NULL); + if (png_ptr->time_buffer == NULL) + { + png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29* + png_sizeof(char))); + } + +#ifdef _WIN32_WCE + { + wchar_t time_buf[29]; + wsprintf(time_buf, TEXT("%d %S %d %02d:%02d:%02d +0000"), + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + WideCharToMultiByte(CP_ACP, 0, time_buf, -1, png_ptr->time_buffer, + 29, NULL, NULL); + } +#else +#ifdef USE_FAR_KEYWORD + { + char near_time_buf[29]; + png_snprintf6(near_time_buf, 29, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + png_memcpy(png_ptr->time_buffer, near_time_buf, + 29*png_sizeof(char)); + } +#else + png_snprintf6(png_ptr->time_buffer, 29, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); +#endif +#endif /* _WIN32_WCE */ + return ((png_charp)png_ptr->time_buffer); +} +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +png_charp PNGAPI +png_get_copyright(png_structp png_ptr) +{ + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ +#ifdef PNG_STRING_COPYRIGHT + return PNG_STRING_COPYRIGHT +#else +#ifdef __STDC__ + return ((png_charp) PNG_STRING_NEWLINE \ + "libpng version 1.2.52 - November 20, 2014" PNG_STRING_NEWLINE \ + "Copyright (c) 1998-2014 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \ + "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ + "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ + PNG_STRING_NEWLINE); +#else + return ((png_charp) "libpng version 1.2.52 - November 20, 2014\ + Copyright (c) 1998-2014 Glenn Randers-Pehrson\ + Copyright (c) 1996-1997 Andreas Dilger\ + Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."); +#endif +#endif +} + +/* The following return the library version as a short string in the + * format 1.0.0 through 99.99.99zz. To get the version of *.h files + * used with your application, print out PNG_LIBPNG_VER_STRING, which + * is defined in png.h. + * Note: now there is no difference between png_get_libpng_ver() and + * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard, + * it is guaranteed that png.c uses the correct version of png.h. + */ +png_charp PNGAPI +png_get_libpng_ver(png_structp png_ptr) +{ + /* Version of *.c files used when building libpng */ + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); +} + +png_charp PNGAPI +png_get_header_ver(png_structp png_ptr) +{ + /* Version of *.h files used when building libpng */ + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); +} + +png_charp PNGAPI +png_get_header_version(png_structp png_ptr) +{ + /* Returns longer string containing both version and date */ + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ +#ifdef __STDC__ + return ((png_charp) PNG_HEADER_VERSION_STRING +#ifndef PNG_READ_SUPPORTED + " (NO READ SUPPORT)" +#endif + PNG_STRING_NEWLINE); +#else + return ((png_charp) PNG_HEADER_VERSION_STRING); +#endif +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +int PNGAPI +png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name) +{ + /* Check chunk_name and return "keep" value if it's on the list, else 0 */ + int i; + png_bytep p; + if (png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list<=0) + return 0; + p = png_ptr->chunk_list + png_ptr->num_chunk_list*5 - 5; + for (i = png_ptr->num_chunk_list; i; i--, p -= 5) + if (!png_memcmp(chunk_name, p, 4)) + return ((int)*(p + 4)); + return 0; +} +#endif + +/* This function, added to libpng-1.0.6g, is untested. */ +int PNGAPI +png_reset_zstream(png_structp png_ptr) +{ + if (png_ptr == NULL) + return Z_STREAM_ERROR; + return (inflateReset(&png_ptr->zstream)); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +/* This function was added to libpng-1.0.7 */ +png_uint_32 PNGAPI +png_access_version_number(void) +{ + /* Version of *.c files used when building libpng */ + return((png_uint_32) PNG_LIBPNG_VER); +} + + +#if defined(PNG_READ_SUPPORTED) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#ifndef PNG_1_0_X +/* This function was added to libpng 1.2.0 */ +int PNGAPI +png_mmx_support(void) +{ + /* Obsolete, to be removed from libpng-1.4.0 */ + return -1; +} +#endif /* PNG_1_0_X */ +#endif /* PNG_READ_SUPPORTED && PNG_ASSEMBLER_CODE_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_SIZE_T +/* Added at libpng version 1.2.6 */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +png_size_t PNGAPI +png_convert_size(size_t size) +{ + if (size > (png_size_t)-1) + PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */ + return ((png_size_t)size); +} +#endif /* PNG_SIZE_T */ + +/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */ +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_CHECK_cHRM_SUPPORTED + +/* + * Multiply two 32-bit numbers, V1 and V2, using 32-bit + * arithmetic, to produce a 64 bit result in the HI/LO words. + * + * A B + * x C D + * ------ + * AD || BD + * AC || CB || 0 + * + * where A and B are the high and low 16-bit words of V1, + * C and D are the 16-bit words of V2, AD is the product of + * A and D, and X || Y is (X << 16) + Y. +*/ + +void /* PRIVATE */ +png_64bit_product (long v1, long v2, unsigned long *hi_product, + unsigned long *lo_product) +{ + int a, b, c, d; + long lo, hi, x, y; + + a = (v1 >> 16) & 0xffff; + b = v1 & 0xffff; + c = (v2 >> 16) & 0xffff; + d = v2 & 0xffff; + + lo = b * d; /* BD */ + x = a * d + c * b; /* AD + CB */ + y = ((lo >> 16) & 0xffff) + x; + + lo = (lo & 0xffff) | ((y & 0xffff) << 16); + hi = (y >> 16) & 0xffff; + + hi += a * c; /* AC */ + + *hi_product = (unsigned long)hi; + *lo_product = (unsigned long)lo; +} + +int /* PRIVATE */ +png_check_cHRM_fixed(png_structp png_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + int ret = 1; + unsigned long xy_hi,xy_lo,yx_hi,yx_lo; + + png_debug(1, "in function png_check_cHRM_fixed"); + + if (png_ptr == NULL) + return 0; + + if (white_x < 0 || white_y <= 0 || + red_x < 0 || red_y < 0 || + green_x < 0 || green_y < 0 || + blue_x < 0 || blue_y < 0) + { + png_warning(png_ptr, + "Ignoring attempt to set negative chromaticity value"); + ret = 0; + } + if (white_x > (png_fixed_point) PNG_UINT_31_MAX || + white_y > (png_fixed_point) PNG_UINT_31_MAX || + red_x > (png_fixed_point) PNG_UINT_31_MAX || + red_y > (png_fixed_point) PNG_UINT_31_MAX || + green_x > (png_fixed_point) PNG_UINT_31_MAX || + green_y > (png_fixed_point) PNG_UINT_31_MAX || + blue_x > (png_fixed_point) PNG_UINT_31_MAX || + blue_y > (png_fixed_point) PNG_UINT_31_MAX ) + { + png_warning(png_ptr, + "Ignoring attempt to set chromaticity value exceeding 21474.83"); + ret = 0; + } + if (white_x > 100000L - white_y) + { + png_warning(png_ptr, "Invalid cHRM white point"); + ret = 0; + } + if (red_x > 100000L - red_y) + { + png_warning(png_ptr, "Invalid cHRM red point"); + ret = 0; + } + if (green_x > 100000L - green_y) + { + png_warning(png_ptr, "Invalid cHRM green point"); + ret = 0; + } + if (blue_x > 100000L - blue_y) + { + png_warning(png_ptr, "Invalid cHRM blue point"); + ret = 0; + } + + png_64bit_product(green_x - red_x, blue_y - red_y, &xy_hi, &xy_lo); + png_64bit_product(green_y - red_y, blue_x - red_x, &yx_hi, &yx_lo); + + if (xy_hi == yx_hi && xy_lo == yx_lo) + { + png_warning(png_ptr, + "Ignoring attempt to set cHRM RGB triangle with zero area"); + ret = 0; + } + + return ret; +} +#endif /* PNG_CHECK_cHRM_SUPPORTED */ +#endif /* PNG_cHRM_SUPPORTED */ + +void /* PRIVATE */ +png_check_IHDR(png_structp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + int error = 0; + + /* Check for width and height valid values */ + if (width == 0) + { + png_warning(png_ptr, "Image width is zero in IHDR"); + error = 1; + } + + if (height == 0) + { + png_warning(png_ptr, "Image height is zero in IHDR"); + error = 1; + } + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (width > png_ptr->user_width_max || width > PNG_USER_WIDTH_MAX) +#else + if (width > PNG_USER_WIDTH_MAX) +#endif + { + png_warning(png_ptr, "Image width exceeds user limit in IHDR"); + error = 1; + } + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (height > png_ptr->user_height_max || height > PNG_USER_HEIGHT_MAX) +#else + if (height > PNG_USER_HEIGHT_MAX) +#endif + { + png_warning(png_ptr, "Image height exceeds user limit in IHDR"); + error = 1; + } + + if (width > PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Invalid image width in IHDR"); + error = 1; + } + + if ( height > PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Invalid image height in IHDR"); + error = 1; + } + + /* Check other values */ + if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && + bit_depth != 8 && bit_depth != 16) + { + png_warning(png_ptr, "Invalid bit depth in IHDR"); + error = 1; + } + + if (color_type < 0 || color_type == 1 || + color_type == 5 || color_type > 6) + { + png_warning(png_ptr, "Invalid color type in IHDR"); + error = 1; + } + + if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || + ((color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) + { + png_warning(png_ptr, "Invalid color type/bit depth combination in IHDR"); + error = 1; + } + + if (interlace_type >= PNG_INTERLACE_LAST) + { + png_warning(png_ptr, "Unknown interlace method in IHDR"); + error = 1; + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Unknown compression method in IHDR"); + error = 1; + } + +#ifdef PNG_MNG_FEATURES_SUPPORTED + /* Accept filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not read a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) && + png_ptr->mng_features_permitted) + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + + if (filter_type != PNG_FILTER_TYPE_BASE) + { + if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && + ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + { + png_warning(png_ptr, "Unknown filter method in IHDR"); + error = 1; + } + + if (png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) + { + png_warning(png_ptr, "Invalid filter method in IHDR"); + error = 1; + } + } + +#else + if (filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Unknown filter method in IHDR"); + error = 1; + } +#endif + + if (error == 1) + png_error(png_ptr, "Invalid IHDR data"); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/png.h b/bazaar/plugin/gdal/frmts/png/libpng/png.h new file mode 100644 index 000000000..ed9e42911 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/png.h @@ -0,0 +1,3818 @@ +/* png.h - header file for PNG reference library + * + * libpng version 1.2.52 - November 20, 2014 + * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license (See LICENSE, below) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.2.52 - November 20, 2014: Glenn + * See also "Contributing Authors", below. + * + * Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + * 1.0.8rc1 1 10008 2.1.0.8rc1 + * 1.0.8 1 10008 2.1.0.8 + * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + * 1.0.9rc1 1 10009 2.1.0.9rc1 + * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + * 1.0.9rc2 1 10009 2.1.0.9rc2 + * 1.0.9 1 10009 2.1.0.9 + * 1.0.10beta1 1 10010 2.1.0.10beta1 + * 1.0.10rc1 1 10010 2.1.0.10rc1 + * 1.0.10 1 10010 2.1.0.10 + * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + * 1.0.11rc1 1 10011 2.1.0.11rc1 + * 1.0.11 1 10011 2.1.0.11 + * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + * 1.0.12rc1 2 10012 2.1.0.12rc1 + * 1.0.12 2 10012 2.1.0.12 + * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) + * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + * 1.2.0rc1 3 10200 3.1.2.0rc1 + * 1.2.0 3 10200 3.1.2.0 + * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 + * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + * 1.2.1 3 10201 3.1.2.1 + * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + * 1.0.13 10 10013 10.so.0.1.0.13 + * 1.2.2 12 10202 12.so.0.1.2.2 + * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + * 1.2.3 12 10203 12.so.0.1.2.3 + * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 + * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + * 1.0.14 10 10014 10.so.0.1.0.14 + * 1.2.4 13 10204 12.so.0.1.2.4 + * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 + * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 + * 1.0.15 10 10015 10.so.0.1.0.15 + * 1.2.5 13 10205 12.so.0.1.2.5 + * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + * 1.0.16 10 10016 10.so.0.1.0.16 + * 1.2.6 13 10206 12.so.0.1.2.6 + * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + * 1.0.17rc1 10 10017 10.so.0.1.0.17rc1 + * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + * 1.0.17 10 10017 10.so.0.1.0.17 + * 1.2.7 13 10207 12.so.0.1.2.7 + * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + * 1.0.18rc1-5 10 10018 10.so.0.1.0.18rc1-5 + * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + * 1.0.18 10 10018 10.so.0.1.0.18 + * 1.2.8 13 10208 12.so.0.1.2.8 + * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 + * 1.2.9beta4-11 13 10209 12.so.0.9[.0] + * 1.2.9rc1 13 10209 12.so.0.9[.0] + * 1.2.9 13 10209 12.so.0.9[.0] + * 1.2.10beta1-8 13 10210 12.so.0.10[.0] + * 1.2.10rc1-3 13 10210 12.so.0.10[.0] + * 1.2.10 13 10210 12.so.0.10[.0] + * 1.2.11beta1-4 13 10211 12.so.0.11[.0] + * 1.0.19rc1-5 10 10019 10.so.0.19[.0] + * 1.2.11rc1-5 13 10211 12.so.0.11[.0] + * 1.0.19 10 10019 10.so.0.19[.0] + * 1.2.11 13 10211 12.so.0.11[.0] + * 1.0.20 10 10020 10.so.0.20[.0] + * 1.2.12 13 10212 12.so.0.12[.0] + * 1.2.13beta1 13 10213 12.so.0.13[.0] + * 1.0.21 10 10021 10.so.0.21[.0] + * 1.2.13 13 10213 12.so.0.13[.0] + * 1.2.14beta1-2 13 10214 12.so.0.14[.0] + * 1.0.22rc1 10 10022 10.so.0.22[.0] + * 1.2.14rc1 13 10214 12.so.0.14[.0] + * 1.0.22 10 10022 10.so.0.22[.0] + * 1.2.14 13 10214 12.so.0.14[.0] + * 1.2.15beta1-6 13 10215 12.so.0.15[.0] + * 1.0.23rc1-5 10 10023 10.so.0.23[.0] + * 1.2.15rc1-5 13 10215 12.so.0.15[.0] + * 1.0.23 10 10023 10.so.0.23[.0] + * 1.2.15 13 10215 12.so.0.15[.0] + * 1.2.16beta1-2 13 10216 12.so.0.16[.0] + * 1.2.16rc1 13 10216 12.so.0.16[.0] + * 1.0.24 10 10024 10.so.0.24[.0] + * 1.2.16 13 10216 12.so.0.16[.0] + * 1.2.17beta1-2 13 10217 12.so.0.17[.0] + * 1.0.25rc1 10 10025 10.so.0.25[.0] + * 1.2.17rc1-3 13 10217 12.so.0.17[.0] + * 1.0.25 10 10025 10.so.0.25[.0] + * 1.2.17 13 10217 12.so.0.17[.0] + * 1.0.26 10 10026 10.so.0.26[.0] + * 1.2.18 13 10218 12.so.0.18[.0] + * 1.2.19beta1-31 13 10219 12.so.0.19[.0] + * 1.0.27rc1-6 10 10027 10.so.0.27[.0] + * 1.2.19rc1-6 13 10219 12.so.0.19[.0] + * 1.0.27 10 10027 10.so.0.27[.0] + * 1.2.19 13 10219 12.so.0.19[.0] + * 1.2.20beta01-04 13 10220 12.so.0.20[.0] + * 1.0.28rc1-6 10 10028 10.so.0.28[.0] + * 1.2.20rc1-6 13 10220 12.so.0.20[.0] + * 1.0.28 10 10028 10.so.0.28[.0] + * 1.2.20 13 10220 12.so.0.20[.0] + * 1.2.21beta1-2 13 10221 12.so.0.21[.0] + * 1.2.21rc1-3 13 10221 12.so.0.21[.0] + * 1.0.29 10 10029 10.so.0.29[.0] + * 1.2.21 13 10221 12.so.0.21[.0] + * 1.2.22beta1-4 13 10222 12.so.0.22[.0] + * 1.0.30rc1 10 10030 10.so.0.30[.0] + * 1.2.22rc1 13 10222 12.so.0.22[.0] + * 1.0.30 10 10030 10.so.0.30[.0] + * 1.2.22 13 10222 12.so.0.22[.0] + * 1.2.23beta01-05 13 10223 12.so.0.23[.0] + * 1.2.23rc01 13 10223 12.so.0.23[.0] + * 1.2.23 13 10223 12.so.0.23[.0] + * 1.2.24beta01-02 13 10224 12.so.0.24[.0] + * 1.2.24rc01 13 10224 12.so.0.24[.0] + * 1.2.24 13 10224 12.so.0.24[.0] + * 1.2.25beta01-06 13 10225 12.so.0.25[.0] + * 1.2.25rc01-02 13 10225 12.so.0.25[.0] + * 1.0.31 10 10031 10.so.0.31[.0] + * 1.2.25 13 10225 12.so.0.25[.0] + * 1.2.26beta01-06 13 10226 12.so.0.26[.0] + * 1.2.26rc01 13 10226 12.so.0.26[.0] + * 1.2.26 13 10226 12.so.0.26[.0] + * 1.0.32 10 10032 10.so.0.32[.0] + * 1.2.27beta01-06 13 10227 12.so.0.27[.0] + * 1.2.27rc01 13 10227 12.so.0.27[.0] + * 1.0.33 10 10033 10.so.0.33[.0] + * 1.2.27 13 10227 12.so.0.27[.0] + * 1.0.34 10 10034 10.so.0.34[.0] + * 1.2.28 13 10228 12.so.0.28[.0] + * 1.2.29beta01-03 13 10229 12.so.0.29[.0] + * 1.2.29rc01 13 10229 12.so.0.29[.0] + * 1.0.35 10 10035 10.so.0.35[.0] + * 1.2.29 13 10229 12.so.0.29[.0] + * 1.0.37 10 10037 10.so.0.37[.0] + * 1.2.30beta01-04 13 10230 12.so.0.30[.0] + * 1.0.38rc01-08 10 10038 10.so.0.38[.0] + * 1.2.30rc01-08 13 10230 12.so.0.30[.0] + * 1.0.38 10 10038 10.so.0.38[.0] + * 1.2.30 13 10230 12.so.0.30[.0] + * 1.0.39rc01-03 10 10039 10.so.0.39[.0] + * 1.2.31rc01-03 13 10231 12.so.0.31[.0] + * 1.0.39 10 10039 10.so.0.39[.0] + * 1.2.31 13 10231 12.so.0.31[.0] + * 1.2.32beta01-02 13 10232 12.so.0.32[.0] + * 1.0.40rc01 10 10040 10.so.0.40[.0] + * 1.2.32rc01 13 10232 12.so.0.32[.0] + * 1.0.40 10 10040 10.so.0.40[.0] + * 1.2.32 13 10232 12.so.0.32[.0] + * 1.2.33beta01-02 13 10233 12.so.0.33[.0] + * 1.2.33rc01-02 13 10233 12.so.0.33[.0] + * 1.0.41rc01 10 10041 10.so.0.41[.0] + * 1.2.33 13 10233 12.so.0.33[.0] + * 1.0.41 10 10041 10.so.0.41[.0] + * 1.2.34beta01-07 13 10234 12.so.0.34[.0] + * 1.0.42rc01 10 10042 10.so.0.42[.0] + * 1.2.34rc01 13 10234 12.so.0.34[.0] + * 1.0.42 10 10042 10.so.0.42[.0] + * 1.2.34 13 10234 12.so.0.34[.0] + * 1.2.35beta01-03 13 10235 12.so.0.35[.0] + * 1.0.43rc01-02 10 10043 10.so.0.43[.0] + * 1.2.35rc01-02 13 10235 12.so.0.35[.0] + * 1.0.43 10 10043 10.so.0.43[.0] + * 1.2.35 13 10235 12.so.0.35[.0] + * 1.2.36beta01-05 13 10236 12.so.0.36[.0] + * 1.2.36rc01 13 10236 12.so.0.36[.0] + * 1.0.44 10 10044 10.so.0.44[.0] + * 1.2.36 13 10236 12.so.0.36[.0] + * 1.2.37beta01-03 13 10237 12.so.0.37[.0] + * 1.2.37rc01 13 10237 12.so.0.37[.0] + * 1.2.37 13 10237 12.so.0.37[.0] + * 1.0.45 10 10045 12.so.0.45[.0] + * 1.0.46 10 10046 10.so.0.46[.0] + * 1.2.38beta01 13 10238 12.so.0.38[.0] + * 1.2.38rc01-03 13 10238 12.so.0.38[.0] + * 1.0.47 10 10047 10.so.0.47[.0] + * 1.2.38 13 10238 12.so.0.38[.0] + * 1.2.39beta01-05 13 10239 12.so.0.39[.0] + * 1.2.39rc01 13 10239 12.so.0.39[.0] + * 1.0.48 10 10048 10.so.0.48[.0] + * 1.2.39 13 10239 12.so.0.39[.0] + * 1.2.40beta01 13 10240 12.so.0.40[.0] + * 1.2.40rc01 13 10240 12.so.0.40[.0] + * 1.0.49 10 10049 10.so.0.49[.0] + * 1.2.40 13 10240 12.so.0.40[.0] + * 1.2.41beta01-18 13 10241 12.so.0.41[.0] + * 1.0.51rc01 10 10051 10.so.0.51[.0] + * 1.2.41rc01-03 13 10241 12.so.0.41[.0] + * 1.0.51 10 10051 10.so.0.51[.0] + * 1.2.41 13 10241 12.so.0.41[.0] + * 1.2.42beta01-02 13 10242 12.so.0.42[.0] + * 1.2.42rc01-05 13 10242 12.so.0.42[.0] + * 1.0.52 10 10052 10.so.0.52[.0] + * 1.2.42 13 10242 12.so.0.42[.0] + * 1.2.43beta01-05 13 10243 12.so.0.43[.0] + * 1.0.53rc01-02 10 10053 10.so.0.53[.0] + * 1.2.43rc01-02 13 10243 12.so.0.43[.0] + * 1.0.53 10 10053 10.so.0.53[.0] + * 1.2.43 13 10243 12.so.0.43[.0] + * 1.2.44beta01-03 13 10244 12.so.0.44[.0] + * 1.2.44rc01-03 13 10244 12.so.0.44[.0] + * 1.2.44 13 10244 12.so.0.44[.0] + * 1.2.45beta01-03 13 10245 12.so.0.45[.0] + * 1.0.55rc01 10 10055 10.so.0.55[.0] + * 1.2.45rc01 13 10245 12.so.0.45[.0] + * 1.0.55 10 10055 10.so.0.55[.0] + * 1.2.45 13 10245 12.so.0.45[.0] + * 1.2.46rc01-02 13 10246 12.so.0.46[.0] + * 1.0.56 10 10056 10.so.0.56[.0] + * 1.2.46 13 10246 12.so.0.46[.0] + * 1.2.47beta01 13 10247 12.so.0.47[.0] + * 1.2.47rc01 13 10247 12.so.0.47[.0] + * 1.0.57rc01 10 10057 10.so.0.57[.0] + * 1.2.47 13 10247 12.so.0.47[.0] + * 1.0.57 10 10057 10.so.0.57[.0] + * 1.2.48beta01 13 10248 12.so.0.48[.0] + * 1.2.48rc01-02 13 10248 12.so.0.48[.0] + * 1.0.58 10 10058 10.so.0.58[.0] + * 1.2.48 13 10248 12.so.0.48[.0] + * 1.2.49rc01 13 10249 12.so.0.49[.0] + * 1.0.59 10 10059 10.so.0.59[.0] + * 1.2.49 13 10249 12.so.0.49[.0] + * 1.0.60 10 10060 10.so.0.60[.0] + * 1.2.50 13 10250 12.so.0.50[.0] + * 1.2.51beta01-05 13 10251 12.so.0.51[.0] + * 1.2.51rc01-04 13 10251 12.so.0.51[.0] + * 1.0.61 10 10061 10.so.0.61[.0] + * 1.2.51 13 10251 12.so.0.51[.0] + * 1.2.52beta01 13 10252 12.so.0.52[.0] + * 1.2.52rc01-02 13 10252 12.so.0.52[.0] + * 1.0.62 10 10062 10.so.0.62[.0] + * 1.2.52 13 10252 12.so.0.52[.0] + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO Specification, + * defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001 +#define PNG_INFO_sBIT 0x0002 +#define PNG_INFO_cHRM 0x0004 +#define PNG_INFO_PLTE 0x0008 +#define PNG_INFO_tRNS 0x0010 +#define PNG_INFO_bKGD 0x0020 +#define PNG_INFO_hIST 0x0040 +#define PNG_INFO_pHYs 0x0080 +#define PNG_INFO_oFFs 0x0100 +#define PNG_INFO_tIME 0x0200 +#define PNG_INFO_pCAL 0x0400 +#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_uint_32 rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info FAR * png_row_infop; +typedef png_row_info FAR * FAR * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. + */ +typedef struct png_struct_def png_struct; +typedef png_struct FAR * png_structp; + +typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); +typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); +typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); +typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, + int)); +typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, + png_row_infop, png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only, deprecated */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE 0x0800 /* write only */ +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.2.41 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t)); +typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application, except to store + * the jmp_buf. + */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmpbuf; /* used in png_error */ +#endif + png_error_ptr error_fn PNG_DEPSTRUCT; /* function for printing errors and aborting */ + png_error_ptr warning_fn PNG_DEPSTRUCT; /* function for printing warnings */ + png_voidp error_ptr PNG_DEPSTRUCT; /* user supplied struct for error functions */ + png_rw_ptr write_data_fn PNG_DEPSTRUCT; /* function for writing output data */ + png_rw_ptr read_data_fn PNG_DEPSTRUCT; /* function for reading input data */ + png_voidp io_ptr PNG_DEPSTRUCT; /* ptr to application struct for I/O functions */ + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr read_user_transform_fn PNG_DEPSTRUCT; /* user read transform */ +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr write_user_transform_fn PNG_DEPSTRUCT; /* user write transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr PNG_DEPSTRUCT; /* user supplied struct for user transform */ + png_byte user_transform_depth PNG_DEPSTRUCT; /* bit depth of user transformed pixels */ + png_byte user_transform_channels PNG_DEPSTRUCT; /* channels in user transformed pixels */ +#endif +#endif + + png_uint_32 mode PNG_DEPSTRUCT; /* tells us where we are in the PNG file */ + png_uint_32 flags PNG_DEPSTRUCT; /* flags indicating various things to libpng */ + png_uint_32 transformations PNG_DEPSTRUCT; /* which transformations to perform */ + + z_stream zstream PNG_DEPSTRUCT; /* pointer to decompression structure (below) */ + png_bytep zbuf PNG_DEPSTRUCT; /* buffer for zlib */ + png_size_t zbuf_size PNG_DEPSTRUCT; /* size of zbuf */ + int zlib_level PNG_DEPSTRUCT; /* holds zlib compression level */ + int zlib_method PNG_DEPSTRUCT; /* holds zlib compression method */ + int zlib_window_bits PNG_DEPSTRUCT; /* holds zlib compression window bits */ + int zlib_mem_level PNG_DEPSTRUCT; /* holds zlib compression memory level */ + int zlib_strategy PNG_DEPSTRUCT; /* holds zlib compression strategy */ + + png_uint_32 width PNG_DEPSTRUCT; /* width of image in pixels */ + png_uint_32 height PNG_DEPSTRUCT; /* height of image in pixels */ + png_uint_32 num_rows PNG_DEPSTRUCT; /* number of rows in current pass */ + png_uint_32 usr_width PNG_DEPSTRUCT; /* width of row at start of write */ + png_uint_32 rowbytes PNG_DEPSTRUCT; /* size of row in bytes */ +#if 0 /* Replaced with the following in libpng-1.2.43 */ + png_size_t irowbytes PNG_DEPSTRUCT; +#endif +/* Added in libpng-1.2.43 */ +#ifdef PNG_USER_LIMITS_SUPPORTED + /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown + * chunks that can be stored (0 means unlimited). + */ + png_uint_32 user_chunk_cache_max PNG_DEPSTRUCT; +#endif + png_uint_32 iwidth PNG_DEPSTRUCT; /* width of current interlaced row in pixels */ + png_uint_32 row_number PNG_DEPSTRUCT; /* current row in interlace pass */ + png_bytep prev_row PNG_DEPSTRUCT; /* buffer to save previous (unfiltered) row */ + png_bytep row_buf PNG_DEPSTRUCT; /* buffer to save current (unfiltered) row */ +#ifndef PNG_NO_WRITE_FILTER + png_bytep sub_row PNG_DEPSTRUCT; /* buffer to save "sub" row when filtering */ + png_bytep up_row PNG_DEPSTRUCT; /* buffer to save "up" row when filtering */ + png_bytep avg_row PNG_DEPSTRUCT; /* buffer to save "avg" row when filtering */ + png_bytep paeth_row PNG_DEPSTRUCT; /* buffer to save "Paeth" row when filtering */ +#endif + png_row_info row_info PNG_DEPSTRUCT; /* used for transformation routines */ + + png_uint_32 idat_size PNG_DEPSTRUCT; /* current IDAT size for read */ + png_uint_32 crc PNG_DEPSTRUCT; /* current chunk CRC value */ + png_colorp palette PNG_DEPSTRUCT; /* palette from the input file */ + png_uint_16 num_palette PNG_DEPSTRUCT; /* number of color entries in palette */ + png_uint_16 num_trans PNG_DEPSTRUCT; /* number of transparency values */ + png_byte chunk_name[5] PNG_DEPSTRUCT; /* null-terminated name of current chunk */ + png_byte compression PNG_DEPSTRUCT; /* file compression type (always 0) */ + png_byte filter PNG_DEPSTRUCT; /* file filter type (always 0) */ + png_byte interlaced PNG_DEPSTRUCT; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + png_byte pass PNG_DEPSTRUCT; /* current interlace pass (0 - 6) */ + png_byte do_filter PNG_DEPSTRUCT; /* row filter flags (see PNG_FILTER_ below ) */ + png_byte color_type PNG_DEPSTRUCT; /* color type of file */ + png_byte bit_depth PNG_DEPSTRUCT; /* bit depth of file */ + png_byte usr_bit_depth PNG_DEPSTRUCT; /* bit depth of users row */ + png_byte pixel_depth PNG_DEPSTRUCT; /* number of bits per pixel */ + png_byte channels PNG_DEPSTRUCT; /* number of channels in file */ + png_byte usr_channels PNG_DEPSTRUCT; /* channels at start of write */ + png_byte sig_bytes PNG_DEPSTRUCT; /* magic bytes read/written from start of file */ + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +#ifdef PNG_LEGACY_SUPPORTED + png_byte filler PNG_DEPSTRUCT; /* filler byte for pixel expansion */ +#else + png_uint_16 filler PNG_DEPSTRUCT; /* filler bytes for pixel expansion */ +#endif +#endif + +#ifdef PNG_bKGD_SUPPORTED + png_byte background_gamma_type PNG_DEPSTRUCT; +# ifdef PNG_FLOATING_POINT_SUPPORTED + float background_gamma PNG_DEPSTRUCT; +# endif + png_color_16 background PNG_DEPSTRUCT; /* background color in screen gamma space */ +#ifdef PNG_READ_GAMMA_SUPPORTED + png_color_16 background_1 PNG_DEPSTRUCT; /* background normalized to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_flush_ptr output_flush_fn PNG_DEPSTRUCT; /* Function for flushing output */ + png_uint_32 flush_dist PNG_DEPSTRUCT; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows PNG_DEPSTRUCT; /* number of rows written since last flush */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + int gamma_shift PNG_DEPSTRUCT; /* number of "insignificant" bits 16-bit gamma */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float gamma PNG_DEPSTRUCT; /* file gamma value */ + float screen_gamma PNG_DEPSTRUCT; /* screen gamma value (display_exponent) */ +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep gamma_table PNG_DEPSTRUCT; /* gamma table for 8-bit depth files */ + png_bytep gamma_from_1 PNG_DEPSTRUCT; /* converts from 1.0 to screen */ + png_bytep gamma_to_1 PNG_DEPSTRUCT; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_table PNG_DEPSTRUCT; /* gamma table for 16-bit depth files */ + png_uint_16pp gamma_16_from_1 PNG_DEPSTRUCT; /* converts from 1.0 to screen */ + png_uint_16pp gamma_16_to_1 PNG_DEPSTRUCT; /* converts from file to 1.0 */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit PNG_DEPSTRUCT; /* significant bits in each available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift PNG_DEPSTRUCT; /* shift for significant bit tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans PNG_DEPSTRUCT; /* transparency values for paletted files */ + png_color_16 trans_values PNG_DEPSTRUCT; /* transparency values for non-paletted files */ +#endif + + png_read_status_ptr read_row_fn PNG_DEPSTRUCT; /* called after each row is decoded */ + png_write_status_ptr write_row_fn PNG_DEPSTRUCT; /* called after each row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn PNG_DEPSTRUCT; /* called after header data fully read */ + png_progressive_row_ptr row_fn PNG_DEPSTRUCT; /* called after each prog. row is decoded */ + png_progressive_end_ptr end_fn PNG_DEPSTRUCT; /* called after image is complete */ + png_bytep save_buffer_ptr PNG_DEPSTRUCT; /* current location in save_buffer */ + png_bytep save_buffer PNG_DEPSTRUCT; /* buffer for previously read data */ + png_bytep current_buffer_ptr PNG_DEPSTRUCT; /* current location in current_buffer */ + png_bytep current_buffer PNG_DEPSTRUCT; /* buffer for recently used data */ + png_uint_32 push_length PNG_DEPSTRUCT; /* size of current input chunk */ + png_uint_32 skip_length PNG_DEPSTRUCT; /* bytes to skip in input data */ + png_size_t save_buffer_size PNG_DEPSTRUCT; /* amount of data now in save_buffer */ + png_size_t save_buffer_max PNG_DEPSTRUCT; /* total size of save_buffer */ + png_size_t buffer_size PNG_DEPSTRUCT; /* total amount of available input data */ + png_size_t current_buffer_size PNG_DEPSTRUCT; /* amount of data now in current_buffer */ + int process_mode PNG_DEPSTRUCT; /* what push library is currently doing */ + int cur_palette PNG_DEPSTRUCT; /* current push library palette index */ + +# ifdef PNG_TEXT_SUPPORTED + png_size_t current_text_size PNG_DEPSTRUCT; /* current size of text input data */ + png_size_t current_text_left PNG_DEPSTRUCT; /* how much text left to read in input */ + png_charp current_text PNG_DEPSTRUCT; /* current text chunk buffer */ + png_charp current_text_ptr PNG_DEPSTRUCT; /* current location in current_text */ +# endif /* PNG_TEXT_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* for the Borland special 64K segment handler */ + png_bytepp offset_table_ptr PNG_DEPSTRUCT; + png_bytep offset_table PNG_DEPSTRUCT; + png_uint_16 offset_table_number PNG_DEPSTRUCT; + png_uint_16 offset_table_count PNG_DEPSTRUCT; + png_uint_16 offset_table_count_free PNG_DEPSTRUCT; +#endif + +#ifdef PNG_READ_DITHER_SUPPORTED + png_bytep palette_lookup PNG_DEPSTRUCT; /* lookup table for dithering */ + png_bytep dither_index PNG_DEPSTRUCT; /* index translation for palette files */ +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED) + png_uint_16p hist PNG_DEPSTRUCT; /* histogram */ +#endif + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_byte heuristic_method PNG_DEPSTRUCT; /* heuristic for row filter selection */ + png_byte num_prev_filters PNG_DEPSTRUCT; /* number of weights for previous rows */ + png_bytep prev_filters PNG_DEPSTRUCT; /* filter type(s) of previous row(s) */ + png_uint_16p filter_weights PNG_DEPSTRUCT; /* weight(s) for previous line(s) */ + png_uint_16p inv_filter_weights PNG_DEPSTRUCT; /* 1/weight(s) for previous line(s) */ + png_uint_16p filter_costs PNG_DEPSTRUCT; /* relative filter calculation cost */ + png_uint_16p inv_filter_costs PNG_DEPSTRUCT; /* 1/relative filter calculation cost */ +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + png_charp time_buffer PNG_DEPSTRUCT; /* String to hold RFC 1123 time text */ +#endif + +/* New members added in libpng-1.0.6 */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_uint_32 free_me PNG_DEPSTRUCT; /* flags items libpng is responsible for freeing */ +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED + png_voidp user_chunk_ptr PNG_DEPSTRUCT; + png_user_chunk_ptr read_user_chunk_fn PNG_DEPSTRUCT; /* user read chunk handler */ +#endif + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int num_chunk_list PNG_DEPSTRUCT; + png_bytep chunk_list PNG_DEPSTRUCT; +#endif + +/* New members added in libpng-1.0.3 */ +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_byte rgb_to_gray_status PNG_DEPSTRUCT; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff PNG_DEPSTRUCT; + png_uint_16 rgb_to_gray_green_coeff PNG_DEPSTRUCT; + png_uint_16 rgb_to_gray_blue_coeff PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ + defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Changed from png_byte to png_uint_32 at version 1.2.0 */ +#ifdef PNG_1_0_X + png_byte mng_features_permitted PNG_DEPSTRUCT; +#else + png_uint_32 mng_features_permitted PNG_DEPSTRUCT; +#endif /* PNG_1_0_X */ +#endif + +/* New member added in libpng-1.0.7 */ +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_fixed_point int_gamma PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_byte filter_type PNG_DEPSTRUCT; +#endif + +#ifdef PNG_1_0_X +/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */ + png_uint_32 row_buf_size PNG_DEPSTRUCT; +#endif + +/* New members added in libpng-1.2.0 */ +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +# ifndef PNG_1_0_X +# ifdef PNG_MMX_CODE_SUPPORTED + png_byte mmx_bitdepth_threshold PNG_DEPSTRUCT; + png_uint_32 mmx_rowbytes_threshold PNG_DEPSTRUCT; +# endif + png_uint_32 asm_flags PNG_DEPSTRUCT; +# endif +#endif + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr PNG_DEPSTRUCT; /* user supplied struct for mem functions */ + png_malloc_ptr malloc_fn PNG_DEPSTRUCT; /* function for allocating memory */ + png_free_ptr free_fn PNG_DEPSTRUCT; /* function for freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf PNG_DEPSTRUCT; /* buffer to save current (unfiltered) row */ + +#ifdef PNG_READ_DITHER_SUPPORTED +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep dither_sort PNG_DEPSTRUCT; /* working sort array */ + png_bytep index_to_palette PNG_DEPSTRUCT; /* where the original index currently is */ + /* in the palette */ + png_bytep palette_to_index PNG_DEPSTRUCT; /* which original index points to this */ + /* palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type PNG_DEPSTRUCT; + +#ifdef PNG_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max PNG_DEPSTRUCT; + png_uint_32 user_height_max PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.25 and 1.2.17 */ +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED + /* Storage for unknown chunk that the library doesn't recognize. */ + png_unknown_chunk unknown_chunk PNG_DEPSTRUCT; +#endif + +/* New members added in libpng-1.2.26 */ + png_uint_32 old_big_row_buf_size PNG_DEPSTRUCT; + png_uint_32 old_prev_row_size PNG_DEPSTRUCT; + +/* New member added in libpng-1.2.30 */ + png_charp chunkdata PNG_DEPSTRUCT; /* buffer for reading chunk data */ + + +}; + + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef png_structp version_1_2_52; + +typedef png_struct FAR * FAR * png_structpp; + +/* Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + */ + +/* Returns the version number of the library */ +extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, + int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num)) PNG_DEPRECATED; + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +extern PNG_EXPORT(png_structp,png_create_read_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)) PNG_ALLOCATED; + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +extern PNG_EXPORT(png_structp,png_create_write_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)) PNG_ALLOCATED; + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(void,png_set_compression_buffer_size) + PNGARG((png_structp png_ptr, png_uint_32 size)); +#endif + +/* Reset the compression stream */ +extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_structp,png_create_read_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)) PNG_ALLOCATED; +extern PNG_EXPORT(png_structp,png_create_write_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)) PNG_ALLOCATED; +#endif + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); + +/* Allocate and initialize the info structure */ +extern PNG_EXPORT(png_infop,png_create_info_struct) + PNGARG((png_structp png_ptr)) PNG_ALLOCATED; + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize the info structure (old interface - DEPRECATED) */ +extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr)) + PNG_DEPRECATED; +#undef png_info_init +#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\ + png_sizeof(png_info)); +#endif + +extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, + png_size_t png_info_struct_size)); + +/* Writes all the PNG information before the image. */ +extern PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED +extern PNG_EXPORT(png_charp,png_convert_to_rfc1123) + PNGARG((png_structp png_ptr, png_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, + struct tm FAR * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, + time_t ttime)); +#endif /* PNG_CONVERT_tIME_SUPPORTED */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); +#ifndef PNG_1_0_X +extern PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp + png_ptr)); +#endif +extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated */ +extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp + png_ptr)) PNG_DEPRECATED; +#endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, + int error_action, double red, double green )); +#endif +extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green )); +extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp + png_ptr)); +#endif + +extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, + png_colorp palette)); + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ +extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +#define PNG_FILLER_BEFORE 0 +#define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +#ifndef PNG_1_0_X +extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +#endif +#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, + png_color_8p true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. */ +extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)); +#endif +#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +#define PNG_BACKGROUND_GAMMA_SCREEN 1 +#define PNG_BACKGROUND_GAMMA_FILE 2 +#define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED +/* Strip the second byte of information from a 16-bit depth file. */ +extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_DITHER_SUPPORTED +/* Turn on dithering, and reduce the palette to the number of colors available. */ +extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_uint_16p histogram, int full_dither)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* Handle gamma correction. Screen_gamma=(display_exponent) */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, + double screen_gamma, double default_file_gamma)); +#endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */ +/* Deprecated and will be removed. Use png_permit_mng_features() instead. */ +extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr, + int empty_plte_permitted)) PNG_DEPRECATED; +#endif +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); + +/* Optional call to update the users info structure */ +extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, + png_bytep row, + png_bytep display_row)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, + png_bytepp image)); +#endif + +/* Write a row of image data */ +extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, + png_bytep row)); + +/* Write a few rows of image data */ +extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_uint_32 num_rows)); + +/* Write the image data */ +extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, + png_bytepp image)); + +/* Writes the end of the PNG file. */ +extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp + png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free all memory used by the read (old method - NOT DLL EXPORTED) */ +extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr, + png_infop end_info_ptr)) PNG_DEPRECATED; + +/* Free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_write_struct) + PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); + +/* Free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ +extern void png_write_destroy PNGARG((png_structp png_ptr)) PNG_DEPRECATED; + +/* Set the libpng method of handling chunk CRC errors */ +extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, + int crit_action, int ancil_action)); + +/* Values for png_set_crc_action() to say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, + int filters)); + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */ +/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ + * defines, either the default (minimum-sum-of-absolute-differences), or + * the experimental method (weighted-minimum-sum-of-absolute-differences). + * + * Weights are factors >= 1.0, indicating how important it is to keep the + * filter type consistent between rows. Larger numbers mean the current + * filter is that many times as likely to be the same as the "num_weights" + * previous filters. This is cumulative for each previous row with a weight. + * There needs to be "num_weights" values in "filter_weights", or it can be + * NULL if the weights aren't being specified. Weights have no influence on + * the selection of the first row filter. Well chosen weights can (in theory) + * improve the compression for a given image. + * + * Costs are factors >= 1.0 indicating the relative decoding costs of a + * filter type. Higher costs indicate more decoding expense, and are + * therefore less likely to be selected over a filter with lower computational + * costs. There needs to be a value in "filter_costs" for each valid filter + * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't + * setting the costs. Costs try to improve the speed of decompression without + * unduly increasing the compressed image size. + * + * A negative weight or cost indicates the default value is to be used, and + * values in the range [0.0, 1.0) indicate the value is to remain unchanged. + * The default values for both weights and costs are currently 1.0, but may + * change if good general weighting/cost heuristics can be found. If both + * the weights and costs are set to 1.0, this degenerates the WEIGHTED method + * to the UNWEIGHTED method, but with added encoding time/computation. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, + int heuristic_method, int num_weights, png_doublep filter_weights, + png_doublep filter_costs)); +#endif +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +/* Heuristic used for row filter selection. These defines should NOT be + * changed. + */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, + int level)); + +extern PNG_EXPORT(void,png_set_compression_mem_level) + PNGARG((png_structp png_ptr, int mem_level)); + +extern PNG_EXPORT(void,png_set_compression_strategy) + PNGARG((png_structp png_ptr, int strategy)); + +extern PNG_EXPORT(void,png_set_compression_window_bits) + PNGARG((png_structp png_ptr, int window_bits)); + +extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, + int method)); + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); + +extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, + png_read_status_ptr read_row_fn)); + +extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr read_user_transform_fn)); +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr write_user_transform_fn)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp + png_ptr, png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +extern PNG_EXPORT(png_voidp,png_get_user_transform_ptr) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp + png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, + png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +extern PNG_EXPORT(png_voidp,png_get_progressive_ptr) + PNGARG((png_structp png_ptr)); + +/* Function to be called when data becomes available */ +extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* Function that combines rows. Not very much different than the + * png_combine_row() call. Is this even used????? + */ +extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, + png_bytep old_row, png_bytep new_row)); +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, + png_uint_32 size)) PNG_ALLOCATED; + +#ifdef PNG_1_0_X +# define png_malloc_warn png_malloc +#else +/* Added at libpng version 1.2.4 */ +extern PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, + png_uint_32 size)) PNG_ALLOCATED; +#endif + +/* Frees a pointer allocated by png_malloc() */ +extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); + +#ifdef PNG_1_0_X +/* Function to allocate memory for zlib. */ +extern PNG_EXPORT(voidpf,png_zalloc) PNGARG((voidpf png_ptr, uInt items, + uInt size)); + +/* Function to free memory for zlib */ +extern PNG_EXPORT(void,png_zfree) PNGARG((voidpf png_ptr, voidpf ptr)); +#endif + +/* Free data that was allocated internally */ +extern PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 free_me, int num)); +#ifdef PNG_FREE_ME_SUPPORTED +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application + */ +extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, + png_infop info_ptr, int freer, png_uint_32 mask)); +#endif +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008 +#define PNG_FREE_ICCP 0x0010 +#define PNG_FREE_SPLT 0x0020 +#define PNG_FREE_ROWS 0x0040 +#define PNG_FREE_PCAL 0x0080 +#define PNG_FREE_SCAL 0x0100 +#define PNG_FREE_UNKN 0x0200 +#define PNG_FREE_LIST 0x0400 +#define PNG_FREE_PLTE 0x1000 +#define PNG_FREE_TRNS 0x2000 +#define PNG_FREE_TEXT 0x4000 +#define PNG_FREE_ALL 0x7fff +#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, + png_uint_32 size)) PNG_ALLOCATED; +extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, + png_voidp ptr)); +#endif + +extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr, + png_voidp s1, png_voidp s2, png_uint_32 size)) PNG_DEPRECATED; + +extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr, + png_voidp s1, int value, png_uint_32 size)) PNG_DEPRECATED; + +#if defined(USE_FAR_KEYWORD) /* memory model conversion function */ +extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, + int check)); +#endif /* USE_FAR_KEYWORD */ + +#ifndef PNG_NO_ERROR_TEXT +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)) PNG_NORETURN; + +/* The same, but the chunk name is prepended to the error string. */ +extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)) PNG_NORETURN; +#else +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_err) PNGARG((png_structp png_ptr)) PNG_NORETURN; +#endif + +#ifndef PNG_NO_WARNINGS +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Non-fatal error in libpng, chunk name is prepended to message. */ +extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_NO_WARNINGS */ + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +extern PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, +png_infop info_ptr)); +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +extern PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image height in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image bit_depth. */ +extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image color_type. */ +extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image filter_type. */ +extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image interlace_type. */ +extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image compression_type. */ +extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +#endif + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +/* Returns pointer to signature string read from PNG header */ +extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_bKGD_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point + *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point + *int_blue_x, png_fixed_point *int_blue_y)); +#endif +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double white_x, double white_y, double red_x, + double red_y, double green_x, double green_y, double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *file_gamma)); +#endif +extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_file_gamma)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double file_gamma)); +#endif +extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_file_gamma)); +#endif + +#ifdef PNG_hIST_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p *hist)); +#endif + +#ifdef PNG_hIST_SUPPORTED +extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p hist)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, + int *type, int *nparams, png_charp *units, png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp *palette, int *num_palette)); + +extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen)); + /* Note to maintainer: profile should be png_bytepp */ +#endif + +#ifdef PNG_iCCP_SUPPORTED +extern PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#ifdef PNG_sPLT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* + * Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep *trans, int *num_trans, + png_color_16p *trans_values)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep trans, int num_trans, + png_color_16p trans_values)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +#endif + +#ifdef PNG_sCAL_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, double *width, double *height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED */ + +#ifdef PNG_sCAL_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +/* Provide a list of chunks and how they are to be handled, if the built-in + handling or default unknown chunk handling is not desired. Any chunks not + listed will be handled in the default manner. The IHDR and IEND chunks + must not be listed. + keep = 0: follow default behaviour + = 1: do not keep + = 2: keep only if safe-to-copy + = 3: keep even if unsafe-to-copy +*/ +extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp + png_ptr, int keep, png_bytep chunk_list, int num_chunks)); +PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep + chunk_name)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); +extern PNG_EXPORT(void, png_set_unknown_chunk_location) + PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); +extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp + png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, + png_infop info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +#endif + +/* Define PNG_DEBUG at compile time for debugging information. Higher + * numbers for PNG_DEBUG mean more debugging information. This has + * only been added since version 0.95 so it is not implemented throughout + * libpng yet, but more support will be added as needed. + */ +#ifdef PNG_DEBUG +#if (PNG_DEBUG > 0) +#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) +#include +#if (PNG_DEBUG > 1) +#ifndef _DEBUG +# define _DEBUG +#endif +#ifndef png_debug +#define png_debug(l,m) _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE) +#endif +#ifndef png_debug1 +#define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1) +#endif +#ifndef png_debug2 +#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2) +#endif +#endif +#else /* PNG_DEBUG_FILE || !_MSC_VER */ +#ifndef PNG_DEBUG_FILE +#define PNG_DEBUG_FILE stderr +#endif /* PNG_DEBUG_FILE */ + +#if (PNG_DEBUG > 1) +/* Note: ["%s"m PNG_STRING_NEWLINE] probably does not work on non-ISO + * compilers. + */ +# ifdef __STDC__ +# ifndef png_debug +# define png_debug(l,m) \ + { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ + } +# endif +# ifndef png_debug1 +# define png_debug1(l,m,p1) \ + { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ + } +# endif +# ifndef png_debug2 +# define png_debug2(l,m,p1,p2) \ + { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s" m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ + } +# endif +# else /* __STDC __ */ +# ifndef png_debug +# define png_debug(l,m) \ + { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format); \ + } +# endif +# ifndef png_debug1 +# define png_debug1(l,m,p1) \ + { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format,p1); \ + } +# endif +# ifndef png_debug2 +# define png_debug2(l,m,p1,p2) \ + { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format,p1,p2); \ + } +# endif +# endif /* __STDC __ */ +#endif /* (PNG_DEBUG > 1) */ + +#endif /* _MSC_VER */ +#endif /* (PNG_DEBUG > 0) */ +#endif /* PNG_DEBUG */ +#ifndef png_debug +#define png_debug(l, m) +#endif +#ifndef png_debug1 +#define png_debug1(l, m, p1) +#endif +#ifndef png_debug2 +#define png_debug2(l, m, p1, p2) +#endif + +extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp + png_ptr, png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 + +/* Added to version 1.2.0 */ +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +#ifdef PNG_MMX_CODE_SUPPORTED +#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED 0x01 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU 0x02 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW 0x04 +#define PNG_ASM_FLAG_MMX_READ_INTERLACE 0x08 +#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB 0x10 +#define PNG_ASM_FLAG_MMX_READ_FILTER_UP 0x20 +#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG 0x40 +#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80 +#define PNG_ASM_FLAGS_INITIALIZED 0x80000000 /* not user-settable */ + +#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ) +#define PNG_MMX_WRITE_FLAGS ( 0 ) + +#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \ + | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU \ + | PNG_MMX_READ_FLAGS \ + | PNG_MMX_WRITE_FLAGS ) + +#define PNG_SELECT_READ 1 +#define PNG_SELECT_WRITE 2 +#endif /* PNG_MMX_CODE_SUPPORTED */ + +#ifndef PNG_1_0_X +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask) + PNGARG((int flag_select, int *compilerID)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask) + PNGARG((int flag_select)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flags) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold) + PNGARG((png_structp png_ptr)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_asm_flags) + PNGARG((png_structp png_ptr, png_uint_32 asm_flags)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_mmx_thresholds) + PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold)); + +#endif /* PNG_1_0_X */ + +#ifndef PNG_1_0_X +/* png.c, pnggccrd.c, or pngvcrd.c */ +extern PNG_EXPORT(int,png_mmx_support) PNGARG((void)); +#endif /* PNG_1_0_X */ +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp + png_ptr, png_uint_32 strip_mode)); +#endif + +/* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp + png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); +extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp + png_ptr)); +extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp + png_ptr)); +#endif +/* Maintainer: Put new public prototypes here ^, in libpng.3, and in + * project defs + */ + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 - \ + (png_uint_16)(alpha)) + (png_uint_16)128); \ + (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } + +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(png_uint_32)(65535L - \ + (png_uint_32)(alpha)) + (png_uint_32)32768L); \ + (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + (png_uint_16)127) / 255) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ + (png_uint_32)32767) / (png_uint_32)65535L) + +#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ + +/* Inline macros to do direct reads of bytes from the input buffer. These + * require that you are using an architecture that uses PNG byte ordering + * (MSB first) and supports unaligned data storage. I think that PowerPC + * in big-endian mode and 680x0 are the only ones that will support this. + * The x86 line of processors definitely do not. The png_get_int_32() + * routine also assumes we are using two's complement format for negative + * values, which is almost certainly true. + */ +#ifdef PNG_READ_BIG_ENDIAN_SUPPORTED +# define png_get_uint_32(buf) ( *((png_uint_32p) (buf))) +# define png_get_uint_16(buf) ( *((png_uint_16p) (buf))) +# define png_get_int_32(buf) ( *((png_int_32p) (buf))) +#else +extern PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf)); +#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */ +extern PNG_EXPORT(png_uint_32,png_get_uint_31) + PNGARG((png_structp png_ptr, png_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). + */ +extern PNG_EXPORT(void,png_save_uint_32) + PNGARG((png_bytep buf, png_uint_32 i)); +extern PNG_EXPORT(void,png_save_int_32) + PNGARG((png_bytep buf, png_int_32 i)); + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +extern PNG_EXPORT(void,png_save_uint_16) + PNGARG((png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ + +/* ************************************************************************* */ + +/* These next functions are used internally in the code. They generally + * shouldn't be used unless you are writing code to add or replace some + * functionality in libpng. More information about most functions can + * be found in the files where the functions are located. + */ + + +/* Various modes of operation, that are visible to applications because + * they are used for unknown chunk location. + */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_HAVE_IDAT 0x04 +#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ +#define PNG_HAVE_IEND 0x10 + +#ifdef PNG_INTERNAL + +/* More modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. + */ +#define PNG_HAVE_gAMA 0x20 +#define PNG_HAVE_cHRM 0x40 +#define PNG_HAVE_sRGB 0x80 +#define PNG_HAVE_CHUNK_HEADER 0x100 +#define PNG_WROTE_tIME 0x200 +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 +#define PNG_BACKGROUND_IS_GRAY 0x800 +#define PNG_HAVE_PNG_SIGNATURE 0x1000 +#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ + +/* Flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001 +#define PNG_INTERLACE 0x0002 +#define PNG_PACK 0x0004 +#define PNG_SHIFT 0x0008 +#define PNG_SWAP_BYTES 0x0010 +#define PNG_INVERT_MONO 0x0020 +#define PNG_DITHER 0x0040 +#define PNG_BACKGROUND 0x0080 +#define PNG_BACKGROUND_EXPAND 0x0100 + /* 0x0200 unused */ +#define PNG_16_TO_8 0x0400 +#define PNG_RGBA 0x0800 +#define PNG_EXPAND 0x1000 +#define PNG_GAMMA 0x2000 +#define PNG_GRAY_TO_RGB 0x4000 +#define PNG_FILLER 0x8000L +#define PNG_PACKSWAP 0x10000L +#define PNG_SWAP_ALPHA 0x20000L +#define PNG_STRIP_ALPHA 0x40000L +#define PNG_INVERT_ALPHA 0x80000L +#define PNG_USER_TRANSFORM 0x100000L +#define PNG_RGB_TO_GRAY_ERR 0x200000L +#define PNG_RGB_TO_GRAY_WARN 0x400000L +#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ + /* 0x800000L Unused */ +#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ +#define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */ +#define PNG_PREMULTIPLY_ALPHA 0x4000000L /* Added to libpng-1.2.41 */ + /* by volker */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +/* Flags for png_create_struct */ +#define PNG_STRUCT_PNG 0x0001 +#define PNG_STRUCT_INFO 0x0002 + +/* Scaling factor for filter heuristic weighting calculations */ +#define PNG_WEIGHT_SHIFT 8 +#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) +#define PNG_COST_SHIFT 3 +#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) + +/* Flags for the png_ptr->flags rather than declaring a byte for each one */ +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 +#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 +#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 +#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 +#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 +#define PNG_FLAG_ZLIB_FINISHED 0x0020 +#define PNG_FLAG_ROW_INIT 0x0040 +#define PNG_FLAG_FILLER_AFTER 0x0080 +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 +#define PNG_FLAG_FREE_PLTE 0x1000 +#define PNG_FLAG_FREE_TRNS 0x2000 +#define PNG_FLAG_FREE_HIST 0x4000 +#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L +#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L +#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L +#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ +#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ + /* 0x800000L unused */ + /* 0x1000000L unused */ + /* 0x2000000L unused */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* Save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \ + (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) ) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + * ideal-delta..ideal+delta. Each argument is evaluated twice. + * "ideal" and "delta" should be constants, normally simple + * integers, "value" a variable. Added to libpng-1.2.6 JB + */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* Variables declared in png.c - only it needs to define PNG_NO_EXTERN */ +#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) +/* Place to hold the signature string for a PNG file. */ +#ifdef PNG_USE_GLOBAL_ARRAYS + PNG_EXPORT_VAR (PNG_CONST png_byte FARDATA) png_sig[8]; +#else +#endif +#endif /* PNG_NO_EXTERN */ + +/* Constant strings for known chunk types. If you need to add a chunk, + * define the name here, and add an invocation of the macro in png.c and + * wherever it's needed. + */ +#define PNG_IHDR png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} +#define PNG_IDAT png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} +#define PNG_IEND png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} +#define PNG_PLTE png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} +#define PNG_bKGD png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} +#define PNG_cHRM png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} +#define PNG_gAMA png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} +#define PNG_hIST png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} +#define PNG_iCCP png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} +#define PNG_iTXt png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} +#define PNG_oFFs png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} +#define PNG_pCAL png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} +#define PNG_sCAL png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} +#define PNG_pHYs png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} +#define PNG_sBIT png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} +#define PNG_sPLT png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} +#define PNG_sRGB png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} +#define PNG_tEXt png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} +#define PNG_tIME png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} +#define PNG_tRNS png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} +#define PNG_zTXt png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} + +#ifdef PNG_USE_GLOBAL_ARRAYS +PNG_EXPORT_VAR (png_byte FARDATA) png_IHDR[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_IDAT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_IEND[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_PLTE[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_bKGD[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_cHRM[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_gAMA[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_hIST[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_iCCP[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_iTXt[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_oFFs[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_pCAL[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sCAL[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_pHYs[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sBIT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sPLT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sRGB[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tEXt[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tIME[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tRNS[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_zTXt[5]; +#endif /* PNG_USE_GLOBAL_ARRAYS */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for reading, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_read_struct instead). + */ +extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr)) + PNG_DEPRECATED; +#undef png_read_init +#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for writing, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_write_struct instead). + */ +extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr)) + PNG_DEPRECATED; +#undef png_write_init +#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); + +/* Allocate memory for an internal libpng struct */ +PNG_EXTERN png_voidp png_create_struct PNGARG((int type)) PNG_PRIVATE; + +/* Free memory from internal libpng struct */ +PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)) PNG_PRIVATE; + +PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr + malloc_fn, png_voidp mem_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, + png_free_ptr free_fn, png_voidp mem_ptr)) PNG_PRIVATE; + +/* Free any memory that info_ptr points to and reset struct. */ +PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; + +#ifndef PNG_1_0_X +/* Function to allocate memory for zlib. */ +PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, + uInt size)) PNG_PRIVATE; + +/* Function to free memory for zlib */ +PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)) PNG_PRIVATE; + +#ifdef PNG_SIZE_T +/* Function to convert a sizeof an item to png_sizeof item */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)) + PNG_PRIVATE; +#endif + +/* Next four functions are used internally as callbacks. PNGAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. + */ + +PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)) PNG_PRIVATE; + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)) PNG_PRIVATE; +#endif + +PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)) PNG_PRIVATE; + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +#ifdef PNG_STDIO_SUPPORTED +PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)) + PNG_PRIVATE; +#endif +#endif +#else /* PNG_1_0_X */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)) PNG_PRIVATE; +#endif +#endif /* PNG_1_0_X */ + +/* Reset the CRC variable */ +PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +/* Write the "data" buffer to whatever output you are using. */ +PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)) PNG_PRIVATE; + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)) PNG_PRIVATE; + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, + png_size_t length)) PNG_PRIVATE; + +/* Decompress data in a chunk that uses compression */ +#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ + defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) +PNG_EXTERN void png_decompress_chunk PNGARG((png_structp png_ptr, + int comp_type, png_size_t chunklength, + png_size_t prefix_length, png_size_t *data_length)) PNG_PRIVATE; +#endif + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip) + PNG_PRIVATE); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +/* Calculate the CRC over a section of data. Note that we are only + * passing a maximum of 64K on systems that have this as a memory limit, + * since this is the maximum buffer size we can specify. + */ +PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, + png_size_t length)) PNG_PRIVATE; + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)) PNG_PRIVATE; +#endif + +/* Simple function to write the signature */ +PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +/* Write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, + png_uint_32 height, + int bit_depth, int color_type, int compression_method, int filter_method, + int interlace_method)) PNG_PRIVATE; + +PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, + png_uint_32 num_pal)) PNG_PRIVATE; + +PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)) PNG_PRIVATE; + +PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +#ifdef PNG_WRITE_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)) + PNG_PRIVATE; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, + png_fixed_point file_gamma)) PNG_PRIVATE; +#endif +#endif + +#ifdef PNG_WRITE_sBIT_SUPPORTED +PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, + int color_type)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, + double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y)) PNG_PRIVATE; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) PNG_PRIVATE; +#endif +#endif + +#ifdef PNG_WRITE_sRGB_SUPPORTED +PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, + int intent)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_iCCP_SUPPORTED +PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, + png_charp name, int compression_type, + png_charp profile, int proflen)) PNG_PRIVATE; + /* Note to maintainer: profile should be png_bytep */ +#endif + +#ifdef PNG_WRITE_sPLT_SUPPORTED +PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, + png_sPLT_tp palette)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_tRNS_SUPPORTED +PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, + png_color_16p values, int number, int color_type)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_bKGD_SUPPORTED +PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, + png_color_16p values, int color_type)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED +PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, + int num_hist)) PNG_PRIVATE; +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, + png_charp key, png_charpp new_key)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_tEXt_SUPPORTED +PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_zTXt_SUPPORTED +PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len, int compression)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_iTXt_SUPPORTED +PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, + int compression, png_charp key, png_charp lang, png_charp lang_key, + png_charp text)) PNG_PRIVATE; +#endif + +#ifdef PNG_TEXT_SUPPORTED /* Added at version 1.0.14 and 1.2.4 */ +PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED +PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_pCAL_SUPPORTED +PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, + png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_pHYs_SUPPORTED +PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, + png_timep mod_time)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, + int unit, double width, double height)) PNG_PRIVATE; +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, + int unit, png_charp width, png_charp height)) PNG_PRIVATE; +#endif +#endif +#endif + +/* Called when finished processing a row of data */ +PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +/* Internal use only. Called before first row of data */ +PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +#ifdef PNG_READ_GAMMA_SUPPORTED +PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr)) PNG_PRIVATE; +#endif + +/* Combine a row of data, dealing with alpha, etc. if requested */ +PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, + int mask)) PNG_PRIVATE; + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* Expand an interlaced row */ +/* OLD pre-1.0.9 interface: +PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations)) PNG_PRIVATE; + */ +PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)) PNG_PRIVATE; +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Grab pixels out of a row for an interlaced pass */ +PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass)) PNG_PRIVATE; +#endif + +/* Unfilter a row */ +PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, + png_row_infop row_info, png_bytep row, png_bytep prev_row, + int filter)) PNG_PRIVATE; + +/* Choose the best filter to use and filter the row data */ +PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, + png_row_infop row_info)) PNG_PRIVATE; + +/* Write out the filtered row. */ +PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, + png_bytep filtered_row)) PNG_PRIVATE; +/* Finish a row while reading, dealing with interlacing passes, etc. */ +PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); + +/* Initialize the row buffers, etc. */ +PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)) PNG_PRIVATE; +/* Optional call to update the users info structure */ +PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; + +/* These are the functions that do the transformations */ +#ifdef PNG_READ_FILLER_SUPPORTED +PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 filler, png_uint_32 flags)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED +PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED +PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED +PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED +PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 flags)) PNG_PRIVATE; +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop + row_info, png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_PACK_SUPPORTED +PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED +PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p sig_bits)) PNG_PRIVATE; +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED +PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_DITHER_SUPPORTED +PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info, + png_bytep row, png_bytep palette_lookup, + png_bytep dither_lookup)) PNG_PRIVATE; + +# ifdef PNG_CORRECT_PALETTE_SUPPORTED +PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette)) PNG_PRIVATE; +# endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_PACK_SUPPORTED +PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 bit_depth)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED +PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p bit_depth)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +#ifdef PNG_READ_GAMMA_SUPPORTED +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background, + png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift)) PNG_PRIVATE; +#else +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background)) PNG_PRIVATE; +#endif +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED +PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, + png_bytep row, png_colorp palette, png_bytep trans, + int num_trans)) PNG_PRIVATE; +PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, + png_bytep row, png_color_16p trans_value)) PNG_PRIVATE; +#endif + +/* The following decodes the appropriate chunks, and does error correction, + * then calls the appropriate callback for the chunk if it is valid. + */ + +/* Decode the IHDR chunk */ +PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); + +#ifdef PNG_READ_bKGD_SUPPORTED +PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED +PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_gAMA_SUPPORTED +PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_hIST_SUPPORTED +PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_iCCP_SUPPORTED +extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#ifdef PNG_READ_iTXt_SUPPORTED +PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED +PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED +PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED +PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED +PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED +PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_sPLT_SUPPORTED +extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#ifdef PNG_READ_sRGB_SUPPORTED +PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED +PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_tIME_SUPPORTED +PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_tRNS_SUPPORTED +PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED +PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)) PNG_PRIVATE; + +PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, + png_bytep chunk_name)) PNG_PRIVATE; + +/* Handle the transformations for reading and writing */ +PNG_EXTERN void png_do_read_transformations + PNGARG((png_structp png_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_do_write_transformations + PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +PNG_EXTERN void png_init_read_transformations + PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, + png_uint_32 length)) PNG_PRIVATE; +PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)) PNG_PRIVATE; +PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)) PNG_PRIVATE; +PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)) PNG_PRIVATE; +PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, + png_bytep row)) PNG_PRIVATE; +PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_read_push_finish_row + PNGARG((png_structp png_ptr)) PNG_PRIVATE; +#ifdef PNG_READ_tEXt_SUPPORTED +PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)) PNG_PRIVATE; +PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +#endif +#ifdef PNG_READ_zTXt_SUPPORTED +PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)) PNG_PRIVATE; +PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +#endif +#ifdef PNG_READ_iTXt_SUPPORTED +PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)) PNG_PRIVATE; +PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +#endif + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +#ifdef PNG_MMX_CODE_SUPPORTED +/* png.c */ /* PRIVATE */ +PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr)) PNG_PRIVATE; +#endif +#endif + + +/* The following six functions will be exported in libpng-1.4.0. */ +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +PNG_EXTERN png_uint_32 png_get_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_x_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_y_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_x_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_y_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXTERN png_uint_32 png_get_pHYs_dpi PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* Read the chunk header (length + type name) */ +PNG_EXTERN png_uint_32 png_read_chunk_header + PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +/* Added at libpng version 1.2.34 */ +#ifdef PNG_cHRM_SUPPORTED +PNG_EXTERN int png_check_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) PNG_PRIVATE; +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_CHECK_cHRM_SUPPORTED +/* Added at libpng version 1.2.34 */ +PNG_EXTERN void png_64bit_product PNGARG((long v1, long v2, + unsigned long *hi_product, unsigned long *lo_product)) PNG_PRIVATE; +#endif +#endif + +/* Added at libpng version 1.2.41 */ +PNG_EXTERN void png_check_IHDR PNGARG((png_structp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type)) PNG_PRIVATE; + +/* Added at libpng version 1.2.41 */ +PNG_EXTERN png_voidp png_calloc PNGARG((png_structp png_ptr, + png_uint_32 size)); + +/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ + +#endif /* PNG_INTERNAL */ + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngconf.h b/bazaar/plugin/gdal/frmts/png/libpng/pngconf.h new file mode 100644 index 000000000..008142e4d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngconf.h @@ -0,0 +1,1677 @@ + +/* pngconf.h - machine configurable file for libpng + * + * libpng version 1.2.52 - November 20, 2014 + * Copyright (c) 1998-2013 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#define PNG_1_2_X + +/* + * PNG_USER_CONFIG has to be defined on the compiler command line. This + * includes the resource compiler for Windows DLL configurations. + */ +#ifdef PNG_USER_CONFIG +# ifndef PNG_USER_PRIVATEBUILD +# define PNG_USER_PRIVATEBUILD +# endif +#include "pngusr.h" +#endif + +/* PNG_CONFIGURE_LIBPNG is set by the "configure" script. */ +#ifdef PNG_CONFIGURE_LIBPNG +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#endif + +/* + * Added at libpng-1.2.8 + * + * If you create a private DLL you need to define in "pngusr.h" the followings: + * #define PNG_USER_PRIVATEBUILD + * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." + * #define PNG_USER_DLLFNAME_POSTFIX + * e.g. // private DLL "libpng13gx.dll" + * #define PNG_USER_DLLFNAME_POSTFIX "gx" + * + * The following macros are also at your disposal if you want to complete the + * DLL VERSIONINFO structure. + * - PNG_USER_VERSIONINFO_COMMENTS + * - PNG_USER_VERSIONINFO_COMPANYNAME + * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS + */ + +#ifdef __STDC__ +#ifdef SPECIALBUILD +# pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ + are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") +#endif + +#ifdef PRIVATEBUILD +# pragma message("PRIVATEBUILD is deprecated.\ + Use PNG_USER_PRIVATEBUILD instead.") +# define PNG_USER_PRIVATEBUILD PRIVATEBUILD +#endif +#endif /* __STDC__ */ + +#ifndef PNG_VERSION_INFO_ONLY + +/* End of material added to libpng-1.2.8 */ + +/* Added at libpng-1.2.19, removed at libpng-1.2.20 because it caused trouble + Restored at libpng-1.2.21 */ +#if !defined(PNG_NO_WARN_UNINITIALIZED_ROW) && \ + !defined(PNG_WARN_UNINITIALIZED_ROW) +# define PNG_WARN_UNINITIALIZED_ROW 1 +#endif +/* End of material added at libpng-1.2.19/1.2.21 */ + +/* Added at libpng-1.2.51 (ported from 1.4.6) */ +#ifndef PNG_UNUSED +/* Unused formal parameter warnings are silenced using the following macro + * which is expected to have no bad effects on performance (optimizing + * compilers will probably remove it entirely). Note that if you replace + * it with something other than whitespace, you must include the terminating + * semicolon. + */ +# define PNG_UNUSED(param) (void)param; +#endif +/* End of material added to libpng-1.4.6 */ + +/* This is the size of the compression buffer, and thus the size of + * an IDAT chunk. Make this whatever size you feel is best for your + * machine. One of these will be allocated per png_struct. When this + * is full, it writes the data to the disk, and does some other + * calculations. Making this an extremely small size will slow + * the library down, but you may want to experiment to determine + * where it becomes significant, if you are concerned with memory + * usage. Note that zlib allocates at least 32Kb also. For readers, + * this describes the size of the buffer available to read the data in. + * Unless this gets smaller than the size of a row (compressed), + * it should not make much difference how big this is. + */ + +#ifndef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 8192 +#endif + +/* Enable if you want a write-only libpng */ + +#ifndef PNG_NO_READ_SUPPORTED +# define PNG_READ_SUPPORTED +#endif + +/* Enable if you want a read-only libpng */ + +#ifndef PNG_NO_WRITE_SUPPORTED +# define PNG_WRITE_SUPPORTED +#endif + +/* Enabled in 1.2.41. */ +#ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +#else +# ifndef PNG_BENIGN_ERRORS_SUPPORTED +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* Added in libpng-1.2.41 */ +#if !defined(PNG_NO_WARNINGS) && !defined(PNG_WARNINGS_SUPPORTED) +# define PNG_WARNINGS_SUPPORTED +#endif + +#if !defined(PNG_NO_ERROR_TEXT) && !defined(PNG_ERROR_TEXT_SUPPORTED) +# define PNG_ERROR_TEXT_SUPPORTED +#endif + +#if !defined(PNG_NO_CHECK_cHRM) && !defined(PNG_CHECK_cHRM_SUPPORTED) +# define PNG_CHECK_cHRM_SUPPORTED +#endif + +/* Enabled by default in 1.2.0. You can disable this if you don't need to + * support PNGs that are embedded in MNG datastreams + */ +#if !defined(PNG_1_0_X) && !defined(PNG_NO_MNG_FEATURES) +# ifndef PNG_MNG_FEATURES_SUPPORTED +# define PNG_MNG_FEATURES_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_FLOATING_POINT_SUPPORTED +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FLOATING_POINT_SUPPORTED +# endif +#endif + +/* If you are running on a machine where you cannot allocate more + * than 64K of memory at once, uncomment this. While libpng will not + * normally need that much memory in a chunk (unless you load up a very + * large file), zlib needs to know how big of a chunk it can use, and + * libpng thus makes sure to check any memory allocation to verify it + * will fit into memory. +#define PNG_MAX_MALLOC_64K + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +/* Special munging to support doing things the 'cygwin' way: + * 'Normal' png-on-win32 defines/defaults: + * PNG_BUILD_DLL -- building dll + * PNG_USE_DLL -- building an application, linking to dll + * (no define) -- building static library, or building an + * application and linking to the static lib + * 'Cygwin' defines/defaults: + * PNG_BUILD_DLL -- (ignored) building the dll + * (no define) -- (ignored) building an application, linking to the dll + * PNG_STATIC -- (ignored) building the static lib, or building an + * application that links to the static lib. + * ALL_STATIC -- (ignored) building various static libs, or building an + * application that links to the static libs. + * Thus, + * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and + * this bit of #ifdefs will define the 'correct' config variables based on + * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but + * unnecessary. + * + * Also, the precedence order is: + * ALL_STATIC (since we can't #undef something outside our namespace) + * PNG_BUILD_DLL + * PNG_STATIC + * (nothing) == PNG_USE_DLL + * + * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent + * of auto-import in binutils, we no longer need to worry about + * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, + * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes + * to __declspec() stuff. However, we DO need to worry about + * PNG_BUILD_DLL and PNG_STATIC because those change some defaults + * such as CONSOLE_IO and whether GLOBAL_ARRAYS are allowed. + */ +#ifdef __CYGWIN__ +# ifdef ALL_STATIC +# ifdef PNG_BUILD_DLL +# undef PNG_BUILD_DLL +# endif +# ifdef PNG_USE_DLL +# undef PNG_USE_DLL +# endif +# ifdef PNG_DLL +# undef PNG_DLL +# endif +# ifndef PNG_STATIC +# define PNG_STATIC +# endif +# else +# ifdef PNG_BUILD_DLL +# ifdef PNG_STATIC +# undef PNG_STATIC +# endif +# ifdef PNG_USE_DLL +# undef PNG_USE_DLL +# endif +# ifndef PNG_DLL +# define PNG_DLL +# endif +# else +# ifdef PNG_STATIC +# ifdef PNG_USE_DLL +# undef PNG_USE_DLL +# endif +# ifdef PNG_DLL +# undef PNG_DLL +# endif +# else +# ifndef PNG_USE_DLL +# define PNG_USE_DLL +# endif +# ifndef PNG_DLL +# define PNG_DLL +# endif +# endif +# endif +# endif +#endif + +/* This protects us against compilers that run on a windowing system + * and thus don't have or would rather us not use the stdio types: + * stdin, stdout, and stderr. The only one currently used is stderr + * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will + * prevent these from being compiled and used. #defining PNG_NO_STDIO + * will also prevent these, plus will prevent the entire set of stdio + * macros and functions (FILE *, printf, etc.) from being compiled and used, + * unless (PNG_DEBUG > 0) has been #defined. + * + * #define PNG_NO_CONSOLE_IO + * #define PNG_NO_STDIO + */ + +#if !defined(PNG_NO_STDIO) && !defined(PNG_STDIO_SUPPORTED) +# define PNG_STDIO_SUPPORTED +#endif + +#ifdef _WIN32_WCE +# include + /* Console I/O functions are not supported on WindowsCE */ +# define PNG_NO_CONSOLE_IO + /* abort() may not be supported on some/all Windows CE platforms */ +# define PNG_ABORT() exit(-1) +# ifdef PNG_DEBUG +# undef PNG_DEBUG +# endif +#endif + +#ifdef PNG_BUILD_DLL +# ifndef PNG_CONSOLE_IO_SUPPORTED +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# endif +#endif + +# ifdef PNG_NO_STDIO +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# ifdef PNG_DEBUG +# if (PNG_DEBUG > 0) +# include +# endif +# endif +# else +# ifndef _WIN32_WCE +/* "stdio.h" functions are not supported on WindowsCE */ +# include +# endif +# endif + +#if !(defined PNG_NO_CONSOLE_IO) && !defined(PNG_CONSOLE_IO_SUPPORTED) +# define PNG_CONSOLE_IO_SUPPORTED +#endif + +/* This macro protects us against machines that don't have function + * prototypes (ie K&R style headers). If your compiler does not handle + * function prototypes, define this macro and use the included ansi2knr. + * I've always been able to use _NO_PROTO as the indicator, but you may + * need to drag the empty declaration out in front of here, or change the + * ifdef to suit your own needs. + */ +#ifndef PNGARG + +#ifdef OF /* zlib prototype munger */ +# define PNGARG(arglist) OF(arglist) +#else + +#ifdef _NO_PROTO +# define PNGARG(arglist) () +# ifndef PNG_TYPECAST_NULL +# define PNG_TYPECAST_NULL +# endif +#else +# define PNGARG(arglist) arglist +#endif /* _NO_PROTO */ + + +#endif /* OF */ + +#endif /* PNGARG */ + +/* Try to determine if we are compiling on a Mac. Note that testing for + * just __MWERKS__ is not good enough, because the Codewarrior is now used + * on non-Mac platforms. + */ +#ifndef MACOS +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) +# define MACOS +# endif +#endif + +/* enough people need this for various reasons to include it here */ +#if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE) +# include +#endif + +#if !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) +# define PNG_SETJMP_SUPPORTED +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This is an attempt to force a single setjmp behaviour on Linux. If + * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. + * + * You can bypass this test if you know that your application uses exactly + * the same setjmp.h that was included when libpng was built. Only define + * PNG_SKIP_SETJMP_CHECK while building your application, prior to the + * application's '#include "png.h"'. Don't define PNG_SKIP_SETJMP_CHECK + * while building a separate libpng library for general use. + */ + +# ifndef PNG_SKIP_SETJMP_CHECK +# ifdef __linux__ +# ifdef _BSD_SOURCE +# define PNG_SAVE_BSD_SOURCE +# undef _BSD_SOURCE +# endif +# ifdef _SETJMP_H + /* If you encounter a compiler error here, see the explanation + * near the end of INSTALL. + */ + __pngconf.h__ in libpng already includes setjmp.h; + __dont__ include it again.; +# endif +# endif /* __linux__ */ +# endif /* PNG_SKIP_SETJMP_CHECK */ + + /* include setjmp.h for error handling */ +# include + +# ifdef __linux__ +# ifdef PNG_SAVE_BSD_SOURCE +# ifndef _BSD_SOURCE +# define _BSD_SOURCE +# endif +# undef PNG_SAVE_BSD_SOURCE +# endif +# endif /* __linux__ */ +#endif /* PNG_SETJMP_SUPPORTED */ + +#ifdef BSD +# include +#else +# include +#endif + +/* Other defines for things like memory and the like can go here. */ +#ifdef PNG_INTERNAL + +#include + +/* The functions exported by PNG_EXTERN are PNG_INTERNAL functions, which + * aren't usually used outside the library (as far as I know), so it is + * debatable if they should be exported at all. In the future, when it is + * possible to have run-time registry of chunk-handling functions, some of + * these will be made available again. +#define PNG_EXTERN extern + */ +#define PNG_EXTERN + +/* Other defines specific to compilers can go here. Try to keep + * them inside an appropriate ifdef/endif pair for portability. + */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED +# ifdef MACOS + /* We need to check that hasn't already been included earlier + * as it seems it doesn't agree with , yet we should really use + * if possible. + */ +# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) +# include +# endif +# else +# include +# endif +# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) + /* Amiga SAS/C: We must include builtin FPU functions when compiling using + * MATH=68881 + */ +# include +# endif +#endif + +/* Codewarrior on NT has linking problems without this. */ +#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__) +# define PNG_ALWAYS_EXTERN +#endif + +/* This provides the non-ANSI (far) memory allocation routines. */ +#if defined(__TURBOC__) && defined(__MSDOS__) +# include +# include +#endif + +/* I have no idea why is this necessary... */ +#if defined(_MSC_VER) && (defined(WIN32) || defined(_Windows) || \ + defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__)) +# include +#endif + +/* This controls how fine the dithering gets. As this allocates + * a largish chunk of memory (32K), those who are not as concerned + * with dithering quality can decrease some or all of these. + */ +#ifndef PNG_DITHER_RED_BITS +# define PNG_DITHER_RED_BITS 5 +#endif +#ifndef PNG_DITHER_GREEN_BITS +# define PNG_DITHER_GREEN_BITS 5 +#endif +#ifndef PNG_DITHER_BLUE_BITS +# define PNG_DITHER_BLUE_BITS 5 +#endif + +/* This controls how fine the gamma correction becomes when you + * are only interested in 8 bits anyway. Increasing this value + * results in more memory being used, and more pow() functions + * being called to fill in the gamma tables. Don't set this value + * less then 8, and even that may not work (I haven't tested it). + */ + +#ifndef PNG_MAX_GAMMA_8 +# define PNG_MAX_GAMMA_8 11 +#endif + +/* This controls how much a difference in gamma we can tolerate before + * we actually start doing gamma conversion. + */ +#ifndef PNG_GAMMA_THRESHOLD +# define PNG_GAMMA_THRESHOLD 0.05 +#endif + +#endif /* PNG_INTERNAL */ + +/* The following uses const char * instead of char * for error + * and warning message functions, so some compilers won't complain. + * If you do not want to use const, define PNG_NO_CONST here. + */ + +#ifndef PNG_NO_CONST +# define PNG_CONST const +#else +# define PNG_CONST +#endif + +/* The following defines give you the ability to remove code from the + * library that you will not be using. I wish I could figure out how to + * automate this, but I can't do that without making it seriously hard + * on the users. So if you are not using an ability, change the #define + * to and #undef, and that part of the library will not be compiled. If + * your linker can't find a function, you may want to make sure the + * ability is defined here. Some of these depend upon some others being + * defined. I haven't figured out all the interactions here, so you may + * have to experiment awhile to get everything to compile. If you are + * creating or using a shared library, you probably shouldn't touch this, + * as it will affect the size of the structures, and this will cause bad + * things to happen if the library and/or application ever change. + */ + +/* Any features you will not be using can be undef'ed here */ + +/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user + * to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS + * on the compile line, then pick and choose which ones to define without + * having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED + * if you only want to have a png-compliant reader/writer but don't need + * any of the extra transformations. This saves about 80 kbytes in a + * typical installation of the library. (PNG_NO_* form added in version + * 1.0.1c, for consistency) + */ + +/* The size of the png_text structure changed in libpng-1.0.6 when + * iTXt support was added. iTXt support was turned off by default through + * libpng-1.2.x, to support old apps that malloc the png_text structure + * instead of calling png_set_text() and letting libpng malloc it. It + * will be turned on by default in libpng-1.4.0. + */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +# ifndef PNG_NO_iTXt_SUPPORTED +# define PNG_NO_iTXt_SUPPORTED +# endif +# ifndef PNG_NO_READ_iTXt +# define PNG_NO_READ_iTXt +# endif +# ifndef PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_iTXt +# endif +#endif + +#if !defined(PNG_NO_iTXt_SUPPORTED) +# if !defined(PNG_READ_iTXt_SUPPORTED) && !defined(PNG_NO_READ_iTXt) +# define PNG_READ_iTXt +# endif +# if !defined(PNG_WRITE_iTXt_SUPPORTED) && !defined(PNG_NO_WRITE_iTXt) +# define PNG_WRITE_iTXt +# endif +#endif + +/* The following support, added after version 1.0.0, can be turned off here en + * masse by defining PNG_LEGACY_SUPPORTED in case you need binary compatibility + * with old applications that require the length of png_struct and png_info + * to remain unchanged. + */ + +#ifdef PNG_LEGACY_SUPPORTED +# define PNG_NO_FREE_ME +# define PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_NO_HANDLE_AS_UNKNOWN +# define PNG_NO_READ_USER_CHUNKS +# define PNG_NO_READ_iCCP +# define PNG_NO_WRITE_iCCP +# define PNG_NO_READ_iTXt +# define PNG_NO_WRITE_iTXt +# define PNG_NO_READ_sCAL +# define PNG_NO_WRITE_sCAL +# define PNG_NO_READ_sPLT +# define PNG_NO_WRITE_sPLT +# define PNG_NO_INFO_IMAGE +# define PNG_NO_READ_RGB_TO_GRAY +# define PNG_NO_READ_USER_TRANSFORM +# define PNG_NO_WRITE_USER_TRANSFORM +# define PNG_NO_USER_MEM +# define PNG_NO_READ_EMPTY_PLTE +# define PNG_NO_MNG_FEATURES +# define PNG_NO_FIXED_POINT_SUPPORTED +#endif + +/* Ignore attempt to turn off both floating and fixed point support */ +#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ + !defined(PNG_NO_FIXED_POINT_SUPPORTED) +# define PNG_FIXED_POINT_SUPPORTED +#endif + +#ifndef PNG_NO_FREE_ME +# define PNG_FREE_ME_SUPPORTED +#endif + +#ifdef PNG_READ_SUPPORTED + +#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_TRANSFORMS) +# define PNG_READ_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_READ_EXPAND +# define PNG_READ_EXPAND_SUPPORTED +# endif +# ifndef PNG_NO_READ_SHIFT +# define PNG_READ_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACK +# define PNG_READ_PACK_SUPPORTED +# endif +# ifndef PNG_NO_READ_BGR +# define PNG_READ_BGR_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP +# define PNG_READ_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACKSWAP +# define PNG_READ_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT +# define PNG_READ_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_READ_DITHER +# define PNG_READ_DITHER_SUPPORTED +# endif +# ifndef PNG_NO_READ_BACKGROUND +# define PNG_READ_BACKGROUND_SUPPORTED +# endif +# ifndef PNG_NO_READ_16_TO_8 +# define PNG_READ_16_TO_8_SUPPORTED +# endif +# ifndef PNG_NO_READ_FILLER +# define PNG_READ_FILLER_SUPPORTED +# endif +# ifndef PNG_NO_READ_GAMMA +# define PNG_READ_GAMMA_SUPPORTED +# endif +# ifndef PNG_NO_READ_GRAY_TO_RGB +# define PNG_READ_GRAY_TO_RGB_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP_ALPHA +# define PNG_READ_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT_ALPHA +# define PNG_READ_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_STRIP_ALPHA +# define PNG_READ_STRIP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_USER_TRANSFORM +# define PNG_READ_USER_TRANSFORM_SUPPORTED +# endif +# ifndef PNG_NO_READ_RGB_TO_GRAY +# define PNG_READ_RGB_TO_GRAY_SUPPORTED +# endif +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ + +/* PNG_PROGRESSIVE_READ_NOT_SUPPORTED is deprecated. */ +#if !defined(PNG_NO_PROGRESSIVE_READ) && \ + !defined(PNG_PROGRESSIVE_READ_NOT_SUPPORTED) /* if you don't do progressive */ +# define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ +#endif /* about interlacing capability! You'll */ + /* still have interlacing unless you change the following define: */ +#define PNG_READ_INTERLACING_SUPPORTED /* required for PNG-compliant decoders */ + +/* PNG_NO_SEQUENTIAL_READ_SUPPORTED is deprecated. */ +#if !defined(PNG_NO_SEQUENTIAL_READ) && \ + !defined(PNG_SEQUENTIAL_READ_SUPPORTED) && \ + !defined(PNG_NO_SEQUENTIAL_READ_SUPPORTED) +# define PNG_SEQUENTIAL_READ_SUPPORTED +#endif + +#define PNG_READ_INTERLACING_SUPPORTED /* required in PNG-compliant decoders */ + +#ifndef PNG_NO_READ_COMPOSITE_NODIV +# ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ +# define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ +# endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, will be removed from version 2.0.0. + Use PNG_MNG_FEATURES_SUPPORTED instead. */ +#ifndef PNG_NO_READ_EMPTY_PLTE +# define PNG_READ_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_READ_SUPPORTED */ + +#ifdef PNG_WRITE_SUPPORTED + +# if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_TRANSFORMS) +# define PNG_WRITE_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_WRITE_SHIFT +# define PNG_WRITE_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACK +# define PNG_WRITE_PACK_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_BGR +# define PNG_WRITE_BGR_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_SWAP +# define PNG_WRITE_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACKSWAP +# define PNG_WRITE_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT +# define PNG_WRITE_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_FILLER +# define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ +# endif +# ifndef PNG_NO_WRITE_SWAP_ALPHA +# define PNG_WRITE_SWAP_ALPHA_SUPPORTED +# endif +#ifndef PNG_1_0_X +# ifndef PNG_NO_WRITE_INVERT_ALPHA +# define PNG_WRITE_INVERT_ALPHA_SUPPORTED +# endif +#endif +# ifndef PNG_NO_WRITE_USER_TRANSFORM +# define PNG_WRITE_USER_TRANSFORM_SUPPORTED +# endif +#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \ + !defined(PNG_WRITE_INTERLACING_SUPPORTED) +#define PNG_WRITE_INTERLACING_SUPPORTED /* not required for PNG-compliant + encoders, but can cause trouble + if left undefined */ +#endif + +#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ + !defined(PNG_WRITE_WEIGHTED_FILTER) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +# define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#endif + +#ifndef PNG_NO_WRITE_FLUSH +# define PNG_WRITE_FLUSH_SUPPORTED +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, see PNG_MNG_FEATURES_SUPPORTED, above */ +#ifndef PNG_NO_WRITE_EMPTY_PLTE +# define PNG_WRITE_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef PNG_1_0_X +# ifndef PNG_NO_ERROR_NUMBERS +# define PNG_ERROR_NUMBERS_SUPPORTED +# endif +#endif /* PNG_1_0_X */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +# ifndef PNG_NO_USER_TRANSFORM_PTR +# define PNG_USER_TRANSFORM_PTR_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_STDIO +# define PNG_TIME_RFC1123_SUPPORTED +#endif + +/* This adds extra functions in pngget.c for accessing data from the + * info pointer (added in version 0.99) + * png_get_image_width() + * png_get_image_height() + * png_get_bit_depth() + * png_get_color_type() + * png_get_compression_type() + * png_get_filter_type() + * png_get_interlace_type() + * png_get_pixel_aspect_ratio() + * png_get_pixels_per_meter() + * png_get_x_offset_pixels() + * png_get_y_offset_pixels() + * png_get_x_offset_microns() + * png_get_y_offset_microns() + */ +#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) +# define PNG_EASY_ACCESS_SUPPORTED +#endif + +/* PNG_ASSEMBLER_CODE was enabled by default in version 1.2.0 + * and removed from version 1.2.20. The following will be removed + * from libpng-1.4.0 +*/ + +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_OPTIMIZED_CODE) +# ifndef PNG_OPTIMIZED_CODE_SUPPORTED +# define PNG_OPTIMIZED_CODE_SUPPORTED +# endif +#endif + +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_ASSEMBLER_CODE) +# ifndef PNG_ASSEMBLER_CODE_SUPPORTED +# define PNG_ASSEMBLER_CODE_SUPPORTED +# endif + +# if defined(__GNUC__) && defined(__x86_64__) && (__GNUC__ < 4) + /* work around 64-bit gcc compiler bugs in gcc-3.x */ +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# ifdef __APPLE__ +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if (defined(__MWERKS__) && ((__MWERKS__ < 0x0900) || macintosh)) +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_MMX_CODE_SUPPORTED +# endif + +#endif +/* end of obsolete code to be removed from libpng-1.4.0 */ + +/* Added at libpng-1.2.0 */ +#ifndef PNG_1_0_X +#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) +# define PNG_USER_MEM_SUPPORTED +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#ifndef PNG_1_0_X +# ifndef PNG_SET_USER_LIMITS_SUPPORTED +# ifndef PNG_NO_SET_USER_LIMITS +# define PNG_SET_USER_LIMITS_SUPPORTED +# endif +# endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.0.53 and 1.2.43 */ +#ifndef PNG_USER_LIMITS_SUPPORTED +# ifndef PNG_NO_USER_LIMITS +# define PNG_USER_LIMITS_SUPPORTED +# endif +#endif + +/* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGS no matter + * how large, set these limits to 0x7fffffffL + */ +#ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000L +#endif +#ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000L +#endif + +/* Added at libpng-1.2.43. To accept all valid PNGs no matter + * how large, set these two limits to 0. + */ +#ifndef PNG_USER_CHUNK_CACHE_MAX +# define PNG_USER_CHUNK_CACHE_MAX 32765 +#endif + +/* Added at libpng-1.2.43 */ +#ifndef PNG_USER_CHUNK_MALLOC_MAX +# define PNG_USER_CHUNK_MALLOC_MAX 0 +#endif + +#ifndef PNG_LITERAL_SHARP +# define PNG_LITERAL_SHARP 0x23 +#endif +#ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET +# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b +#endif +#ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET +# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d +#endif + +/* Added at libpng-1.2.34 */ +#ifndef PNG_STRING_NEWLINE +#define PNG_STRING_NEWLINE "\n" +#endif + +/* These are currently experimental features, define them if you want */ + +/* very little testing */ +/* +#ifdef PNG_READ_SUPPORTED +# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# endif +#endif +*/ + +/* This is only for PowerPC big-endian and 680x0 systems */ +/* some testing */ +/* +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +# define PNG_READ_BIG_ENDIAN_SUPPORTED +#endif +*/ + +/* Buggy compilers (e.g., gcc 2.7.2.2) need this */ +/* +#define PNG_NO_POINTER_INDEXING +*/ + +#if !defined(PNG_NO_POINTER_INDEXING) && \ + !defined(PNG_POINTER_INDEXING_SUPPORTED) +# define PNG_POINTER_INDEXING_SUPPORTED +#endif + +/* These functions are turned off by default, as they will be phased out. */ +/* +#define PNG_USELESS_TESTS_SUPPORTED +#define PNG_CORRECT_PALETTE_SUPPORTED +*/ + +/* Any chunks you are not interested in, you can undef here. The + * ones that allocate memory may be expecially important (hIST, + * tEXt, zTXt, tRNS, pCAL). Others will just save time and make png_info + * a bit smaller. + */ + +#if defined(PNG_READ_SUPPORTED) && \ + !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_ANCILLARY_CHUNKS) +# define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#if defined(PNG_WRITE_SUPPORTED) && \ + !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) +# define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_READ_TEXT +# define PNG_NO_READ_iTXt +# define PNG_NO_READ_tEXt +# define PNG_NO_READ_zTXt +#endif +#ifndef PNG_NO_READ_bKGD +# define PNG_READ_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +#endif +#ifndef PNG_NO_READ_cHRM +# define PNG_READ_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +#endif +#ifndef PNG_NO_READ_gAMA +# define PNG_READ_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +#endif +#ifndef PNG_NO_READ_hIST +# define PNG_READ_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +#endif +#ifndef PNG_NO_READ_iCCP +# define PNG_READ_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +#endif +#ifndef PNG_NO_READ_iTXt +# ifndef PNG_READ_iTXt_SUPPORTED +# define PNG_READ_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_READ_oFFs +# define PNG_READ_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +#endif +#ifndef PNG_NO_READ_pCAL +# define PNG_READ_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_sCAL +# define PNG_READ_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_pHYs +# define PNG_READ_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +#endif +#ifndef PNG_NO_READ_sBIT +# define PNG_READ_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sPLT +# define PNG_READ_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sRGB +# define PNG_READ_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +#endif +#ifndef PNG_NO_READ_tEXt +# define PNG_READ_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_tIME +# define PNG_READ_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +#endif +#ifndef PNG_NO_READ_tRNS +# define PNG_READ_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +#endif +#ifndef PNG_NO_READ_zTXt +# define PNG_READ_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_OPT_PLTE +# define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ +#endif /* optional PLTE chunk in RGB and RGBA images */ +#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ + defined(PNG_READ_zTXt_SUPPORTED) +# define PNG_READ_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +#endif + +#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ + +#ifndef PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +#endif +#if !defined(PNG_NO_READ_USER_CHUNKS) && \ + defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) +# define PNG_READ_USER_CHUNKS_SUPPORTED +# define PNG_USER_CHUNKS_SUPPORTED +# ifdef PNG_NO_READ_UNKNOWN_CHUNKS +# undef PNG_NO_READ_UNKNOWN_CHUNKS +# endif +# ifdef PNG_NO_HANDLE_AS_UNKNOWN +# undef PNG_NO_HANDLE_AS_UNKNOWN +# endif +#endif + +#ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_WRITE_TEXT +# define PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_tEXt +# define PNG_NO_WRITE_zTXt +#endif +#ifndef PNG_NO_WRITE_bKGD +# define PNG_WRITE_bKGD_SUPPORTED +# ifndef PNG_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_cHRM +# define PNG_WRITE_cHRM_SUPPORTED +# ifndef PNG_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_gAMA +# define PNG_WRITE_gAMA_SUPPORTED +# ifndef PNG_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_hIST +# define PNG_WRITE_hIST_SUPPORTED +# ifndef PNG_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iCCP +# define PNG_WRITE_iCCP_SUPPORTED +# ifndef PNG_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iTXt +# ifndef PNG_WRITE_iTXt_SUPPORTED +# define PNG_WRITE_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_oFFs +# define PNG_WRITE_oFFs_SUPPORTED +# ifndef PNG_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pCAL +# define PNG_WRITE_pCAL_SUPPORTED +# ifndef PNG_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sCAL +# define PNG_WRITE_sCAL_SUPPORTED +# ifndef PNG_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pHYs +# define PNG_WRITE_pHYs_SUPPORTED +# ifndef PNG_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sBIT +# define PNG_WRITE_sBIT_SUPPORTED +# ifndef PNG_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sPLT +# define PNG_WRITE_sPLT_SUPPORTED +# ifndef PNG_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sRGB +# define PNG_WRITE_sRGB_SUPPORTED +# ifndef PNG_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tEXt +# define PNG_WRITE_tEXt_SUPPORTED +# ifndef PNG_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tIME +# define PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tRNS +# define PNG_WRITE_tRNS_SUPPORTED +# ifndef PNG_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_zTXt +# define PNG_WRITE_zTXt_SUPPORTED +# ifndef PNG_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +# endif +#endif +#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ + defined(PNG_WRITE_zTXt_SUPPORTED) +# define PNG_WRITE_TEXT_SUPPORTED +# ifndef PNG_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +# endif +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_NO_CONVERT_tIME +# ifndef _WIN32_WCE +/* The "tm" structure is not supported on WindowsCE */ +# ifndef PNG_CONVERT_tIME_SUPPORTED +# define PNG_CONVERT_tIME_SUPPORTED +# endif +# endif +# endif +#endif + +#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ + +#if !defined(PNG_NO_WRITE_FILTER) && !defined(PNG_WRITE_FILTER_SUPPORTED) +# define PNG_WRITE_FILTER_SUPPORTED +#endif + +#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif +#endif /* PNG_WRITE_SUPPORTED */ + +/* Turn this off to disable png_read_png() and + * png_write_png() and leave the row_pointers member + * out of the info structure. + */ +#ifndef PNG_NO_INFO_IMAGE +# define PNG_INFO_IMAGE_SUPPORTED +#endif + +/* Need the time information for converting tIME chunks */ +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* "time.h" functions are not supported on WindowsCE */ +# include +#endif + +/* Some typedefs to get us started. These should be safe on most of the + * common platforms. The typedefs should be at least as large as the + * numbers suggest (a png_uint_32 must be at least 32 bits long), but they + * don't have to be exactly that size. Some compilers dislike passing + * unsigned shorts as function parameters, so you may be better off using + * unsigned int for png_uint_16. Likewise, for 64-bit systems, you may + * want to have unsigned int for png_uint_32 instead of unsigned long. + */ + +typedef unsigned long png_uint_32; +typedef long png_int_32; +typedef unsigned short png_uint_16; +typedef short png_int_16; +typedef unsigned char png_byte; + +/* This is usually size_t. It is typedef'ed just in case you need it to + change (I'm not sure if you will or not, so I thought I'd be safe) */ +#ifdef PNG_SIZE_T + typedef PNG_SIZE_T png_size_t; +# define png_sizeof(x) png_convert_size(sizeof(x)) +#else + typedef size_t png_size_t; +# define png_sizeof(x) sizeof(x) +#endif + +/* The following is needed for medium model support. It cannot be in the + * PNG_INTERNAL section. Needs modification for other compilers besides + * MSC. Model independent support declares all arrays and pointers to be + * large using the far keyword. The zlib version used must also support + * model independent data. As of version zlib 1.0.4, the necessary changes + * have been made in zlib. The USE_FAR_KEYWORD define triggers other + * changes that are needed. (Tim Wegner) + */ + +/* Separate compiler dependencies (problem here is that zlib.h always + defines FAR. (SJT) */ +#ifdef __BORLANDC__ +# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) +# define LDATA 1 +# else +# define LDATA 0 +# endif + /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ +# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) +# define PNG_MAX_MALLOC_64K +# if (LDATA != 1) +# ifndef FAR +# define FAR __far +# endif +# define USE_FAR_KEYWORD +# endif /* LDATA != 1 */ + /* Possibly useful for moving data out of default segment. + * Uncomment it if you want. Could also define FARDATA as + * const if your compiler supports it. (SJT) +# define FARDATA FAR + */ +# endif /* __WIN32__, __FLAT__, __CYGWIN__ */ +#endif /* __BORLANDC__ */ + + +/* Suggest testing for specific compiler first before testing for + * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM, + * making reliance oncertain keywords suspect. (SJT) + */ + +/* MSC Medium model */ +#ifdef FAR +# ifdef M_I86MM +# define USE_FAR_KEYWORD +# define FARDATA FAR +# include +# endif +#endif + +/* SJT: default case */ +#ifndef FAR +# define FAR +#endif + +/* At this point FAR is always defined */ +#ifndef FARDATA +# define FARDATA +#endif + +/* Typedef for floating-point numbers that are converted + to fixed-point with a multiple of 100,000, e.g., int_gamma */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void FAR * png_voidp; +typedef png_byte FAR * png_bytep; +typedef png_uint_32 FAR * png_uint_32p; +typedef png_int_32 FAR * png_int_32p; +typedef png_uint_16 FAR * png_uint_16p; +typedef png_int_16 FAR * png_int_16p; +typedef PNG_CONST char FAR * png_const_charp; +typedef char FAR * png_charp; +typedef png_fixed_point FAR * png_fixed_point_p; + +#ifndef PNG_NO_STDIO +#ifdef _WIN32_WCE +typedef HANDLE png_FILE_p; +#else +typedef FILE * png_FILE_p; +#endif +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * png_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte FAR * FAR * png_bytepp; +typedef png_uint_32 FAR * FAR * png_uint_32pp; +typedef png_int_32 FAR * FAR * png_int_32pp; +typedef png_uint_16 FAR * FAR * png_uint_16pp; +typedef png_int_16 FAR * FAR * png_int_16pp; +typedef PNG_CONST char FAR * FAR * png_const_charpp; +typedef char FAR * FAR * png_charpp; +typedef png_fixed_point FAR * FAR * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * FAR * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char FAR * FAR * FAR * png_charppp; + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* SPC - Is this stuff deprecated? */ +/* It'll be removed as of libpng-1.4.0 - GR-P */ +/* libpng typedefs for types in zlib. If zlib changes + * or another compression library is used, then change these. + * Eliminates need to change all the source files. + */ +typedef charf * png_zcharp; +typedef charf * FAR * png_zcharpp; +typedef z_stream FAR * png_zstreamp; +#endif /* (PNG_1_0_X) || defined(PNG_1_2_X) */ + +/* + * Define PNG_BUILD_DLL if the module being built is a Windows + * LIBPNG DLL. + * + * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. + * It is equivalent to Microsoft predefined macro _DLL that is + * automatically defined when you compile using the share + * version of the CRT (C Run-Time library) + * + * The cygwin mods make this behavior a little different: + * Define PNG_BUILD_DLL if you are building a dll for use with cygwin + * Define PNG_STATIC if you are building a static library for use with cygwin, + * -or- if you are building an application that you want to link to the + * static library. + * PNG_USE_DLL is defined by default (no user action needed) unless one of + * the other flags is defined. + */ + +#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) +# define PNG_DLL +#endif +/* If CYGWIN, then disallow GLOBAL ARRAYS unless building a static lib. + * When building a static lib, default to no GLOBAL ARRAYS, but allow + * command-line override + */ +#ifdef __CYGWIN__ +# ifndef PNG_STATIC +# ifdef PNG_USE_GLOBAL_ARRAYS +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# ifndef PNG_USE_LOCAL_ARRAYS +# define PNG_USE_LOCAL_ARRAYS +# endif +# else +# if defined(PNG_USE_LOCAL_ARRAYS) || defined(PNG_NO_GLOBAL_ARRAYS) +# ifdef PNG_USE_GLOBAL_ARRAYS +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# endif +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +#endif + +/* Do not use global arrays (helps with building DLL's) + * They are no longer used in libpng itself, since version 1.0.5c, + * but might be required for some pre-1.0.5c applications. + */ +#if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# if defined(PNG_NO_GLOBAL_ARRAYS) || \ + (defined(__GNUC__) && defined(PNG_DLL)) || defined(_MSC_VER) +# define PNG_USE_LOCAL_ARRAYS +# else +# define PNG_USE_GLOBAL_ARRAYS +# endif +#endif + +#ifdef __CYGWIN__ +# undef PNGAPI +# define PNGAPI __cdecl +# undef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", + * you may get warnings regarding the linkage of png_zalloc and png_zfree. + * Don't ignore those warnings; you must also reset the default calling + * convention in your compiler to match your PNGAPI, and you must build + * zlib and your applications the same way you build libpng. + */ + +#if defined(__MINGW32__) && !defined(PNG_MODULEDEF) +# ifndef PNG_NO_MODULEDEF +# define PNG_NO_MODULEDEF +# endif +#endif + +#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) +# define PNG_IMPEXP +#endif + +#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ + (( defined(_Windows) || defined(_WINDOWS) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) + +# ifndef PNGAPI +# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) +# define PNGAPI __cdecl +# else +# define PNGAPI _cdecl +# endif +# endif + +# if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ + 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) +# define PNG_IMPEXP +# endif + +# ifndef PNG_IMPEXP + +# define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol +# define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol + + /* Borland/Microsoft */ +# if defined(_MSC_VER) || defined(__BORLANDC__) +# if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) +# define PNG_EXPORT PNG_EXPORT_TYPE1 +# else +# define PNG_EXPORT PNG_EXPORT_TYPE2 +# ifdef PNG_BUILD_DLL +# define PNG_IMPEXP __export +# else +# define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in + VC++ */ +# endif /* Exists in Borland C++ for + C++ classes (== huge) */ +# endif +# endif + +# ifndef PNG_IMPEXP +# ifdef PNG_BUILD_DLL +# define PNG_IMPEXP __declspec(dllexport) +# else +# define PNG_IMPEXP __declspec(dllimport) +# endif +# endif +# endif /* PNG_IMPEXP */ +#else /* !(DLL || non-cygwin WINDOWS) */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# ifndef PNGAPI +# define PNGAPI _System +# endif +# else +# if 0 /* ... other platforms, with other meanings */ +# endif +# endif +#endif + +#ifndef PNGAPI +# define PNGAPI +#endif +#ifndef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +#ifdef PNG_BUILDSYMS +# ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END +# endif +# ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) PNG_DATA_EXPORT +# endif +# endif +#endif + +#ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol +#endif + +#ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) extern PNG_IMPEXP type +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. + */ +# ifdef __GNUC__ +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif + + /* This specifically protects structure members that should only be + * accessed from within the library, therefore should be empty during + * a library build. + */ +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_DEPSTRUCT +# define PNG_DEPSTRUCT __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif /* PNG_PRIVATE */ +# endif /* __GNUC__ */ +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_DEPSTRUCT +# define PNG_DEPSTRUCT /* Access to this struct member is deprecated */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif + +/* User may want to use these so they are not in PNG_INTERNAL. Any library + * functions that are passed far data must be model independent. + */ + +#ifndef PNG_ABORT +# define PNG_ABORT() abort() +#endif + +#ifdef PNG_SETJMP_SUPPORTED +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_SETJMP_NOT_SUPPORTED) +#endif + +#ifdef USE_FAR_KEYWORD /* memory model independent fns */ +/* Use this to make far-to-near assignments */ +# define CHECK 1 +# define NOCHECK 0 +# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) +# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) +# define png_snprintf _fsnprintf /* Added to v 1.2.19 */ +# define png_strlen _fstrlen +# define png_memcmp _fmemcmp /* SJT: added */ +# define png_memcpy _fmemcpy +# define png_memset _fmemset +#else /* Use the usual functions */ +# define CVT_PTR(ptr) (ptr) +# define CVT_PTR_NOCHECK(ptr) (ptr) +# ifndef PNG_NO_SNPRINTF +# ifdef _MSC_VER +# define png_snprintf _snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 _snprintf +# define png_snprintf6 _snprintf +# else +# define png_snprintf snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 snprintf +# define png_snprintf6 snprintf +# endif +# else + /* You don't have or don't want to use snprintf(). Caution: Using + * sprintf instead of snprintf exposes your application to accidental + * or malevolent buffer overflows. If you don't have snprintf() + * as a general rule you should provide one (you can get one from + * Portable OpenSSH). + */ +# define png_snprintf(s1,n,fmt,x1) sprintf(s1,fmt,x1) +# define png_snprintf2(s1,n,fmt,x1,x2) sprintf(s1,fmt,x1,x2) +# define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \ + sprintf(s1,fmt,x1,x2,x3,x4,x5,x6) +# endif +# define png_strlen strlen +# define png_memcmp memcmp /* SJT: added */ +# define png_memcpy memcpy +# define png_memset memset +#endif +/* End of memory model independent support */ + +/* Just a little check that someone hasn't tried to define something + * contradictory. + */ +#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +# undef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 65536L +#endif + +/* Added at libpng-1.2.8 */ +#endif /* PNG_VERSION_INFO_ONLY */ + +#endif /* PNGCONF_H */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngerror.c b/bazaar/plugin/gdal/frmts/png/libpng/pngerror.c new file mode 100644 index 000000000..d8e79284f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngerror.c @@ -0,0 +1,396 @@ + +/* pngerror.c - stub functions for i/o and memory allocation + * + * Last changed in libpng 1.2.51 [February 6, 2014] + * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all error handling. Users who + * need special error handling are expected to write replacement functions + * and use png_set_error_fn() to use those functions. See the instructions + * at each function. + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +static void /* PRIVATE */ +png_default_error PNGARG((png_structp png_ptr, + png_const_charp error_message)) PNG_NORETURN; +#ifdef PNG_WARNINGS_SUPPORTED +static void /* PRIVATE */ +png_default_warning PNGARG((png_structp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_WARNINGS_SUPPORTED */ + +/* This function is called whenever there is a fatal error. This function + * should not be changed. If there is a need to handle errors differently, + * you should supply a replacement error function and use png_set_error_fn() + * to replace the error function at run-time. + */ +#ifdef PNG_ERROR_TEXT_SUPPORTED +void PNGAPI +png_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + char msg[16]; + if (png_ptr != NULL) + { + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) + { + if (*error_message == PNG_LITERAL_SHARP) + { + /* Strip "#nnnn " from beginning of error message. */ + int offset; + for (offset = 1; offset<15; offset++) + if (error_message[offset] == ' ') + break; + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + int i; + for (i = 0; i < offset - 1; i++) + msg[i] = error_message[i + 1]; + msg[i - 1] = '\0'; + error_message = msg; + } + else + error_message += offset; + } + else + { + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + msg[0] = '0'; + msg[1] = '\0'; + error_message = msg; + } + } + } + } +#endif + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, error_message); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, error_message); +} +#else +void PNGAPI +png_err(png_structp png_ptr) +{ + /* Prior to 1.2.45 the error_fn received a NULL pointer, expressed + * erroneously as '\0', instead of the empty string "". This was + * apparently an error, introduced in libpng-1.2.20, and png_default_error + * will crash in this case. + */ + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, ""); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, ""); +} +#endif /* PNG_ERROR_TEXT_SUPPORTED */ + +#ifdef PNG_WARNINGS_SUPPORTED +/* This function is called whenever there is a non-fatal error. This function + * should not be changed. If there is a need to handle warnings differently, + * you should supply a replacement warning function and use + * png_set_error_fn() to replace the warning function at run-time. + */ +void PNGAPI +png_warning(png_structp png_ptr, png_const_charp warning_message) +{ + int offset = 0; + if (png_ptr != NULL) + { +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) +#endif + { + if (*warning_message == PNG_LITERAL_SHARP) + { + for (offset = 1; offset < 15; offset++) + if (warning_message[offset] == ' ') + break; + } + } + } + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + (*(png_ptr->warning_fn))(png_ptr, warning_message + offset); + else + png_default_warning(png_ptr, warning_message + offset); +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_benign_error(png_structp png_ptr, png_const_charp error_message) +{ + if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) + png_warning(png_ptr, error_message); + else + png_error(png_ptr, error_message); +} +#endif + +/* These utilities are used internally to build an error message that relates + * to the current chunk. The chunk name comes from png_ptr->chunk_name, + * this is used to prefix the message. The message is limited in length + * to 63 bytes, the name characters are output as hex digits wrapped in [] + * if the character is invalid. + */ +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) +static PNG_CONST char png_digit[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' +}; + +#define PNG_MAX_ERROR_TEXT 64 +#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_ERROR_TEXT_SUPPORTED) +static void /* PRIVATE */ +png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp + error_message) +{ + int iout = 0, iin = 0; + + while (iin < 4) + { + int c = png_ptr->chunk_name[iin++]; + if (isnonalpha(c)) + { + buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET; + buffer[iout++] = png_digit[(c & 0xf0) >> 4]; + buffer[iout++] = png_digit[c & 0x0f]; + buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET; + } + else + { + buffer[iout++] = (png_byte)c; + } + } + + if (error_message == NULL) + buffer[iout] = '\0'; + else + { + buffer[iout++] = ':'; + buffer[iout++] = ' '; + + iin = 0; + while (iin < PNG_MAX_ERROR_TEXT-1 && error_message[iin] != '\0') + buffer[iout++] = error_message[iin++]; + + /* iin < PNG_MAX_ERROR_TEXT, so the following is safe: */ + buffer[iout] = '\0'; + } +} + +#ifdef PNG_READ_SUPPORTED +void PNGAPI +png_chunk_error(png_structp png_ptr, png_const_charp error_message) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_error(png_ptr, error_message); + else + { + png_format_buffer(png_ptr, msg, error_message); + png_error(png_ptr, msg); + } +} +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_WARNINGS_SUPPORTED || PNG_ERROR_TEXT_SUPPORTED */ + +#ifdef PNG_WARNINGS_SUPPORTED +void PNGAPI +png_chunk_warning(png_structp png_ptr, png_const_charp warning_message) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_warning(png_ptr, warning_message); + else + { + png_format_buffer(png_ptr, msg, warning_message); + png_warning(png_ptr, msg); + } +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +#ifdef PNG_READ_SUPPORTED +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_chunk_benign_error(png_structp png_ptr, png_const_charp error_message) +{ + if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) + png_chunk_warning(png_ptr, error_message); + else + png_chunk_error(png_ptr, error_message); +} +#endif +#endif /* PNG_READ_SUPPORTED */ + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void /* PRIVATE */ +png_default_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifdef PNG_CONSOLE_IO_SUPPORTED +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*error_message == PNG_LITERAL_SHARP) + { + /* Strip "#nnnn " from beginning of error message. */ + int offset; + char error_number[16]; + for (offset = 0; offset<15; offset++) + { + error_number[offset] = error_message[offset + 1]; + if (error_message[offset] == ' ') + break; + } + if ((offset > 1) && (offset < 15)) + { + error_number[offset - 1] = '\0'; + fprintf(stderr, "libpng error no. %s: %s", + error_number, error_message + offset + 1); + fprintf(stderr, PNG_STRING_NEWLINE); + } + else + { + fprintf(stderr, "libpng error: %s, offset=%d", + error_message, offset); + fprintf(stderr, PNG_STRING_NEWLINE); + } + } + else +#endif + { + fprintf(stderr, "libpng error: %s", error_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } +#endif + +#ifdef PNG_SETJMP_SUPPORTED + if (png_ptr) + { +# ifdef USE_FAR_KEYWORD + { + jmp_buf jmpbuf; + png_memcpy(jmpbuf, png_ptr->jmpbuf, png_sizeof(jmp_buf)); + longjmp(jmpbuf,1); + } +# else + longjmp(png_ptr->jmpbuf, 1); +# endif + } +#endif + /* Here if not setjmp support or if png_ptr is null. */ + PNG_ABORT(); +#ifndef PNG_CONSOLE_IO_SUPPORTED + PNG_UNUSED(error_message) /* Make compiler happy */ +#endif +} + +#ifdef PNG_WARNINGS_SUPPORTED +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want them to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void /* PRIVATE */ +png_default_warning(png_structp png_ptr, png_const_charp warning_message) +{ +#ifdef PNG_CONSOLE_IO_SUPPORTED +# ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*warning_message == PNG_LITERAL_SHARP) + { + int offset; + char warning_number[16]; + for (offset = 0; offset < 15; offset++) + { + warning_number[offset] = warning_message[offset + 1]; + if (warning_message[offset] == ' ') + break; + } + if ((offset > 1) && (offset < 15)) + { + warning_number[offset + 1] = '\0'; + fprintf(stderr, "libpng warning no. %s: %s", + warning_number, warning_message + offset); + fprintf(stderr, PNG_STRING_NEWLINE); + } + else + { + fprintf(stderr, "libpng warning: %s", + warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } + } + else +# endif + { + fprintf(stderr, "libpng warning: %s", warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } +#else + PNG_UNUSED(warning_message) /* Make compiler happy */ +#endif + PNG_UNUSED(png_ptr) /* Make compiler happy */ +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +/* This function is called when the application wants to use another method + * of handling errors and warnings. Note that the error function MUST NOT + * return to the calling routine or serious problems will occur. The return + * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1) + */ +void PNGAPI +png_set_error_fn(png_structp png_ptr, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warning_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->error_ptr = error_ptr; + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; +} + + +/* This function returns a pointer to the error_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_error_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return NULL; + return ((png_voidp)png_ptr->error_ptr); +} + + +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +void PNGAPI +png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode) +{ + if (png_ptr != NULL) + { + png_ptr->flags &= + ((~(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); + } +} +#endif +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pnggccrd.c b/bazaar/plugin/gdal/frmts/png/libpng/pnggccrd.c new file mode 100644 index 000000000..7eb7f67d6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pnggccrd.c @@ -0,0 +1,26 @@ +/* pnggccrd.c + * + * Last changed in libpng 1.2.48 [March 8, 2012] + * Copyright (c) 1998-2012 Glenn Randers-Pehrson + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This code snippet is for use by configure's compilation test. Most of the + * remainder of the file was removed from libpng-1.2.20, and all of the + * assembler code was removed from libpng-1.2.48. + */ + +#if (!defined _MSC_VER) && \ + defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \ + defined(PNG_MMX_CODE_SUPPORTED) + +int PNGAPI png_dummy_mmx_support(void); + +int PNGAPI png_dummy_mmx_support(void) +{ + /* 0: no MMX; 1: MMX supported; 2: not tested */ + return 2; +} +#endif diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngget.c b/bazaar/plugin/gdal/frmts/png/libpng/pngget.c new file mode 100644 index 000000000..c5d654336 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngget.c @@ -0,0 +1,944 @@ + +/* pngget.c - retrieval of values from info struct + * + * Last changed in libpng 1.2.51 [February 6, 2014] + * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +png_uint_32 PNGAPI +png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flag) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->valid & flag); + + else + return(0); +} + +png_uint_32 PNGAPI +png_get_rowbytes(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->rowbytes); + + else + return(0); +} + +#ifdef PNG_INFO_IMAGE_SUPPORTED +png_bytepp PNGAPI +png_get_rows(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->row_pointers); + + else + return(0); +} +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Easy access to info, added in libpng-0.99 */ +png_uint_32 PNGAPI +png_get_image_width(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->width; + + return (0); +} + +png_uint_32 PNGAPI +png_get_image_height(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->height; + + return (0); +} + +png_byte PNGAPI +png_get_bit_depth(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->bit_depth; + + return (0); +} + +png_byte PNGAPI +png_get_color_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->color_type; + + return (0); +} + +png_byte PNGAPI +png_get_filter_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->filter_type; + + return (0); +} + +png_byte PNGAPI +png_get_interlace_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->interlace_type; + + return (0); +} + +png_byte PNGAPI +png_get_compression_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return info_ptr->compression_type; + + return (0); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#ifdef PNG_pHYs_SUPPORTED + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function", "png_get_x_pixels_per_meter"); + + if (info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + + else + return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#ifdef PNG_pHYs_SUPPORTED + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function", "png_get_y_pixels_per_meter"); + + if (info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + + else + return (info_ptr->y_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#ifdef PNG_pHYs_SUPPORTED + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter"); + + if (info_ptr->phys_unit_type != PNG_RESOLUTION_METER || + info_ptr->x_pixels_per_unit != info_ptr->y_pixels_per_unit) + return (0); + + else + return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_pixel_aspect_ratio(png_structp png_ptr, png_infop info_ptr) + { + if (png_ptr != NULL && info_ptr != NULL) +#ifdef PNG_pHYs_SUPPORTED + + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio"); + + if (info_ptr->x_pixels_per_unit == 0) + return ((float)0.0); + + else + return ((float)((float)info_ptr->y_pixels_per_unit + /(float)info_ptr->x_pixels_per_unit)); + } +#else + return (0.0); +#endif + return ((float)0.0); +} +#endif + +png_int_32 PNGAPI +png_get_x_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#ifdef PNG_oFFs_SUPPORTED + + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns"); + + if (info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + + else + return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + +#ifdef PNG_oFFs_SUPPORTED + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns"); + + if (info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + + else + return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_x_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + +#ifdef PNG_oFFs_SUPPORTED + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns"); + + if (info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + + else + return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + +#ifdef PNG_oFFs_SUPPORTED + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns"); + + if (info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + + else + return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +png_uint_32 PNGAPI +png_get_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_x_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_y_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +float PNGAPI +png_get_x_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_x_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +float PNGAPI +png_get_y_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_y_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +#ifdef PNG_pHYs_SUPPORTED +png_uint_32 PNGAPI +png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function", "pHYs"); + + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + if (*unit_type == 1) + { + if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50); + if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50); + } + } + } + return (retval); +} +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* png_get_channels really belongs in here, too, but it's been around longer */ + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +png_byte PNGAPI +png_get_channels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->channels); + else + return (0); +} + +png_bytep PNGAPI +png_get_signature(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->signature); + else + return (NULL); +} + +#ifdef PNG_bKGD_SUPPORTED +png_uint_32 PNGAPI +png_get_bKGD(png_structp png_ptr, png_infop info_ptr, + png_color_16p *background) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) + && background != NULL) + { + png_debug1(1, "in %s retrieval function", "bKGD"); + + *background = &(info_ptr->background); + return (PNG_INFO_bKGD); + } + return (0); +} +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM(png_structp png_ptr, png_infop info_ptr, + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_debug1(1, "in %s retrieval function", "cHRM"); + + if (white_x != NULL) + *white_x = (double)info_ptr->x_white; + if (white_y != NULL) + *white_y = (double)info_ptr->y_white; + if (red_x != NULL) + *red_x = (double)info_ptr->x_red; + if (red_y != NULL) + *red_y = (double)info_ptr->y_red; + if (green_x != NULL) + *green_x = (double)info_ptr->x_green; + if (green_y != NULL) + *green_y = (double)info_ptr->y_green; + if (blue_x != NULL) + *blue_x = (double)info_ptr->x_blue; + if (blue_y != NULL) + *blue_y = (double)info_ptr->y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, + png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, + png_fixed_point *blue_x, png_fixed_point *blue_y) +{ + png_debug1(1, "in %s retrieval function", "cHRM"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + if (white_x != NULL) + *white_x = info_ptr->int_x_white; + if (white_y != NULL) + *white_y = info_ptr->int_y_white; + if (red_x != NULL) + *red_x = info_ptr->int_x_red; + if (red_y != NULL) + *red_y = info_ptr->int_y_red; + if (green_x != NULL) + *green_x = info_ptr->int_x_green; + if (green_y != NULL) + *green_y = info_ptr->int_y_green; + if (blue_x != NULL) + *blue_x = info_ptr->int_x_blue; + if (blue_y != NULL) + *blue_y = info_ptr->int_y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#endif + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma) +{ + png_debug1(1, "in %s retrieval function", "gAMA"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && file_gamma != NULL) + { + *file_gamma = (double)info_ptr->gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *int_file_gamma) +{ + png_debug1(1, "in %s retrieval function", "gAMA"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && int_file_gamma != NULL) + { + *int_file_gamma = info_ptr->int_gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#endif + +#ifdef PNG_sRGB_SUPPORTED +png_uint_32 PNGAPI +png_get_sRGB(png_structp png_ptr, png_infop info_ptr, int *file_srgb_intent) +{ + png_debug1(1, "in %s retrieval function", "sRGB"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB) + && file_srgb_intent != NULL) + { + *file_srgb_intent = (int)info_ptr->srgb_intent; + return (PNG_INFO_sRGB); + } + return (0); +} +#endif + +#ifdef PNG_iCCP_SUPPORTED +png_uint_32 PNGAPI +png_get_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen) +{ + png_debug1(1, "in %s retrieval function", "iCCP"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP) + && name != NULL && profile != NULL && proflen != NULL) + { + *name = info_ptr->iccp_name; + *profile = info_ptr->iccp_profile; + /* Compression_type is a dummy so the API won't have to change + * if we introduce multiple compression types later. + */ + *proflen = (int)info_ptr->iccp_proflen; + *compression_type = (int)info_ptr->iccp_compression; + return (PNG_INFO_iCCP); + } + return (0); +} +#endif + +#ifdef PNG_sPLT_SUPPORTED +png_uint_32 PNGAPI +png_get_sPLT(png_structp png_ptr, png_infop info_ptr, + png_sPLT_tpp spalettes) +{ + if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL) + { + *spalettes = info_ptr->splt_palettes; + return ((png_uint_32)info_ptr->splt_palettes_num); + } + return (0); +} +#endif + +#ifdef PNG_hIST_SUPPORTED +png_uint_32 PNGAPI +png_get_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist) +{ + png_debug1(1, "in %s retrieval function", "hIST"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) + && hist != NULL) + { + *hist = info_ptr->hist; + return (PNG_INFO_hIST); + } + return (0); +} +#endif + +png_uint_32 PNGAPI +png_get_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *width, png_uint_32 *height, int *bit_depth, + int *color_type, int *interlace_type, int *compression_type, + int *filter_type) + +{ + png_debug1(1, "in %s retrieval function", "IHDR"); + + if (png_ptr == NULL || info_ptr == NULL || width == NULL || + height == NULL || bit_depth == NULL || color_type == NULL) + return (0); + + *width = info_ptr->width; + *height = info_ptr->height; + *bit_depth = info_ptr->bit_depth; + *color_type = info_ptr->color_type; + + if (compression_type != NULL) + *compression_type = info_ptr->compression_type; + + if (filter_type != NULL) + *filter_type = info_ptr->filter_type; + + if (interlace_type != NULL) + *interlace_type = info_ptr->interlace_type; + + /* This is redundant if we can be sure that the info_ptr values were all + * assigned in png_set_IHDR(). We do the check anyhow in case an + * application has ignored our advice not to mess with the members + * of info_ptr directly. + */ + png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->compression_type, info_ptr->filter_type); + + return (1); +} + +#ifdef PNG_oFFs_SUPPORTED +png_uint_32 PNGAPI +png_get_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type) +{ + png_debug1(1, "in %s retrieval function", "oFFs"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) + && offset_x != NULL && offset_y != NULL && unit_type != NULL) + { + *offset_x = info_ptr->x_offset; + *offset_y = info_ptr->y_offset; + *unit_type = (int)info_ptr->offset_unit_type; + return (PNG_INFO_oFFs); + } + return (0); +} +#endif + +#ifdef PNG_pCAL_SUPPORTED +png_uint_32 PNGAPI +png_get_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, + png_charp *units, png_charpp *params) +{ + png_debug1(1, "in %s retrieval function", "pCAL"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) + && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && + nparams != NULL && units != NULL && params != NULL) + { + *purpose = info_ptr->pcal_purpose; + *X0 = info_ptr->pcal_X0; + *X1 = info_ptr->pcal_X1; + *type = (int)info_ptr->pcal_type; + *nparams = (int)info_ptr->pcal_nparams; + *units = info_ptr->pcal_units; + *params = info_ptr->pcal_params; + return (PNG_INFO_pCAL); + } + return (0); +} +#endif + +#ifdef PNG_sCAL_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL(png_structp png_ptr, png_infop info_ptr, + int *unit, double *width, double *height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_pixel_width; + *height = info_ptr->scal_pixel_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int *unit, png_charpp width, png_charpp height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_s_width; + *height = info_ptr->scal_s_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#endif +#endif +#endif + +#ifdef PNG_pHYs_SUPPORTED +png_uint_32 PNGAPI +png_get_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + png_debug1(1, "in %s retrieval function", "pHYs"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs)) + { + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + } + } + return (retval); +} +#endif + +png_uint_32 PNGAPI +png_get_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp *palette, + int *num_palette) +{ + png_debug1(1, "in %s retrieval function", "PLTE"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE) + && palette != NULL) + { + *palette = info_ptr->palette; + *num_palette = info_ptr->num_palette; + png_debug1(3, "num_palette = %d", *num_palette); + return (PNG_INFO_PLTE); + } + return (0); +} + +#ifdef PNG_sBIT_SUPPORTED +png_uint_32 PNGAPI +png_get_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit) +{ + png_debug1(1, "in %s retrieval function", "sBIT"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) + && sig_bit != NULL) + { + *sig_bit = &(info_ptr->sig_bit); + return (PNG_INFO_sBIT); + } + return (0); +} +#endif + +#ifdef PNG_TEXT_SUPPORTED +png_uint_32 PNGAPI +png_get_text(png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr, + int *num_text) +{ + if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0) + { + png_debug1(1, "in %s retrieval function", + (png_ptr->chunk_name[0] == '\0' ? "text" + : (png_const_charp)png_ptr->chunk_name)); + + if (text_ptr != NULL) + *text_ptr = info_ptr->text; + + if (num_text != NULL) + *num_text = info_ptr->num_text; + + return ((png_uint_32)info_ptr->num_text); + } + if (num_text != NULL) + *num_text = 0; + return(0); +} +#endif + +#ifdef PNG_tIME_SUPPORTED +png_uint_32 PNGAPI +png_get_tIME(png_structp png_ptr, png_infop info_ptr, png_timep *mod_time) +{ + png_debug1(1, "in %s retrieval function", "tIME"); + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) + && mod_time != NULL) + { + *mod_time = &(info_ptr->mod_time); + return (PNG_INFO_tIME); + } + return (0); +} +#endif + +#ifdef PNG_tRNS_SUPPORTED +png_uint_32 PNGAPI +png_get_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep *trans, int *num_trans, png_color_16p *trans_values) +{ + png_uint_32 retval = 0; + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_debug1(1, "in %s retrieval function", "tRNS"); + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (trans != NULL) + { + *trans = info_ptr->trans; + retval |= PNG_INFO_tRNS; + } + + if (trans_values != NULL) + *trans_values = &(info_ptr->trans_values); + } + else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ + { + if (trans_values != NULL) + { + *trans_values = &(info_ptr->trans_values); + retval |= PNG_INFO_tRNS; + } + + if (trans != NULL) + *trans = NULL; + } + if (num_trans != NULL) + { + *num_trans = info_ptr->num_trans; + retval |= PNG_INFO_tRNS; + } + } + return (retval); +} +#endif + +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +png_uint_32 PNGAPI +png_get_unknown_chunks(png_structp png_ptr, png_infop info_ptr, + png_unknown_chunkpp unknowns) +{ + if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL) + { + *unknowns = info_ptr->unknown_chunks; + return ((png_uint_32)info_ptr->unknown_chunks_num); + } + return (0); +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +png_byte PNGAPI +png_get_rgb_to_gray_status (png_structp png_ptr) +{ + return (png_byte)(png_ptr? png_ptr->rgb_to_gray_status : 0); +} +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +png_voidp PNGAPI +png_get_user_chunk_ptr(png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_chunk_ptr : NULL); +} +#endif + +png_uint_32 PNGAPI +png_get_compression_buffer_size(png_structp png_ptr) +{ + return (png_uint_32)(png_ptr? png_ptr->zbuf_size : 0L); +} + +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +#ifndef PNG_1_0_X +/* This function was added to libpng 1.2.0 and should exist by default */ +png_uint_32 PNGAPI +png_get_asm_flags (png_structp png_ptr) +{ + /* Obsolete, to be removed from libpng-1.4.0 */ + return (png_ptr? 0L: 0L); +} + +/* This function was added to libpng 1.2.0 and should exist by default */ +png_uint_32 PNGAPI +png_get_asm_flagmask (int flag_select) +{ + /* Obsolete, to be removed from libpng-1.4.0 */ + PNG_UNUSED(flag_select) + return 0L; +} + + /* GRR: could add this: && defined(PNG_MMX_CODE_SUPPORTED) */ +/* This function was added to libpng 1.2.0 */ +png_uint_32 PNGAPI +png_get_mmx_flagmask (int flag_select, int *compilerID) +{ + /* Obsolete, to be removed from libpng-1.4.0 */ + PNG_UNUSED(flag_select) + *compilerID = -1; /* unknown (i.e., no asm/MMX code compiled) */ + return 0L; +} + +/* This function was added to libpng 1.2.0 */ +png_byte PNGAPI +png_get_mmx_bitdepth_threshold (png_structp png_ptr) +{ + /* Obsolete, to be removed from libpng-1.4.0 */ + return (png_ptr? 0: 0); +} + +/* This function was added to libpng 1.2.0 */ +png_uint_32 PNGAPI +png_get_mmx_rowbytes_threshold (png_structp png_ptr) +{ + /* Obsolete, to be removed from libpng-1.4.0 */ + return (png_ptr? 0L: 0L); +} +#endif /* ?PNG_1_0_X */ +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* These functions were added to libpng 1.2.6 but not enabled +* by default. They will be enabled in libpng-1.4.0 */ +png_uint_32 PNGAPI +png_get_user_width_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_width_max : 0); +} +png_uint_32 PNGAPI +png_get_user_height_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_height_max : 0); +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngmem.c b/bazaar/plugin/gdal/frmts/png/libpng/pngmem.c new file mode 100644 index 000000000..a18719b81 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngmem.c @@ -0,0 +1,641 @@ + +/* pngmem.c - stub functions for memory allocation + * + * Last changed in libpng 1.2.41 [February 25, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all memory allocation. Users who + * need special memory handling are expected to supply replacement + * functions for png_malloc() and png_free(), and to use + * png_create_read_struct_2() and png_create_write_struct_2() to + * identify the replacement functions. + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +/* Borland DOS special memory handler */ +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* If you change this, be sure to change the one in png.h also */ + +/* Allocate memory for a png_struct. The malloc and memset can be replaced + by a single call to calloc() if this is thought to improve performance. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); +} + +/* Alternate version of png_create_struct, for use with user-defined malloc. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (png_get_copyright(NULL)); + +#ifdef PNG_USER_MEM_SUPPORTED + if (malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, (png_uint_32)size); + } + else +#endif /* PNG_USER_MEM_SUPPORTED */ + struct_ptr = (png_voidp)farmalloc(size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if (free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ + farfree (struct_ptr); + } +} + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + * + * Borland seems to have a problem in DOS mode for exactly 64K. + * It gives you a segment with an offset of 8 (perhaps to store its + * memory stuff). zlib doesn't like this at all, so we have to + * detect and deal with it. This code should not be needed in + * Windows or OS/2 modes, and only in 16 bit mode. This code has + * been updated by Alexander Lehmann for version 0.89 to waste less + * memory. + * + * Note that we can't use png_size_t for the "size" declaration, + * since on some systems a png_size_t is a 16-bit quantity, and as a + * result, we would be truncating potentially larger memory requests + * (which should cause a fatal error) and introducing major problems. + */ +png_voidp /* PRIVATE */ +png_calloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + + ret = (png_malloc(png_ptr, size)); + if (ret != NULL) + png_memset(ret,0,(png_size_t)size); + return (ret); +} + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory!"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { + png_warning(png_ptr, "Cannot Allocate > 64K"); + ret = NULL; + } + else +#endif + + if (size != (size_t)size) + ret = NULL; + else if (size == (png_uint_32)65536L) + { + if (png_ptr->offset_table == NULL) + { + /* Try to see if we need to do any of this fancy stuff */ + ret = farmalloc(size); + if (ret == NULL || ((png_size_t)ret & 0xffff)) + { + int num_blocks; + png_uint_32 total_size; + png_bytep table; + int i; + png_byte huge * hptr; + + if (ret != NULL) + { + farfree(ret); + ret = NULL; + } + + if (png_ptr->zlib_window_bits > 14) + num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14)); + else + num_blocks = 1; + if (png_ptr->zlib_mem_level >= 7) + num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7)); + else + num_blocks++; + + total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16; + + table = farmalloc(total_size); + + if (table == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of Memory."); /* Note "O", "M" */ + else + png_warning(png_ptr, "Out Of Memory."); +#endif + return (NULL); + } + + if ((png_size_t)table & 0xfff0) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, + "Farmalloc didn't return normalized pointer"); + else + png_warning(png_ptr, + "Farmalloc didn't return normalized pointer"); +#endif + return (NULL); + } + + png_ptr->offset_table = table; + png_ptr->offset_table_ptr = farmalloc(num_blocks * + png_sizeof(png_bytep)); + + if (png_ptr->offset_table_ptr == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of memory."); /* Note "O", "m" */ + else + png_warning(png_ptr, "Out Of memory."); +#endif + return (NULL); + } + + hptr = (png_byte huge *)table; + if ((png_size_t)hptr & 0xf) + { + hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L); + hptr = hptr + 16L; /* "hptr += 16L" fails on Turbo C++ 3.0 */ + } + for (i = 0; i < num_blocks; i++) + { + png_ptr->offset_table_ptr[i] = (png_bytep)hptr; + hptr = hptr + (png_uint_32)65536L; /* "+=" fails on TC++3.0 */ + } + + png_ptr->offset_table_number = num_blocks; + png_ptr->offset_table_count = 0; + png_ptr->offset_table_count_free = 0; + } + } + + if (png_ptr->offset_table_count >= png_ptr->offset_table_number) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory."); /* Note "o" and "M" */ + else + png_warning(png_ptr, "Out of Memory."); +#endif + return (NULL); + } + + ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++]; + } + else + ret = farmalloc(size); + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL) + { + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory."); /* Note "o" and "m" */ + else + png_warning(png_ptr, "Out of memory."); /* Note "o" and "m" */ + } +#endif + + return (ret); +} + +/* Free a pointer allocated by png_malloc(). In the default + * configuration, png_ptr is not used, but is passed in case it + * is needed. If ptr is NULL, return without taking any action. + */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else + png_free_default(png_ptr, ptr); +} + +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || ptr == NULL) + return; + + if (png_ptr->offset_table != NULL) + { + int i; + + for (i = 0; i < png_ptr->offset_table_count; i++) + { + if (ptr == png_ptr->offset_table_ptr[i]) + { + ptr = NULL; + png_ptr->offset_table_count_free++; + break; + } + } + if (png_ptr->offset_table_count_free == png_ptr->offset_table_count) + { + farfree(png_ptr->offset_table); + farfree(png_ptr->offset_table_ptr); + png_ptr->offset_table = NULL; + png_ptr->offset_table_ptr = NULL; + } + } + + if (ptr != NULL) + { + farfree(ptr); + } +} + +#else /* Not the Borland DOS special memory handler */ + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); +} + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if (malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); + } +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + struct_ptr = (png_voidp)farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + struct_ptr = (png_voidp)halloc(size, 1); +# else + struct_ptr = (png_voidp)malloc(size); +# endif +#endif + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + + return (struct_ptr); +} + + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if (free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(struct_ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(struct_ptr); +# else + free(struct_ptr); +# endif +#endif + } +} + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + */ + +png_voidp /* PRIVATE */ +png_calloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + + ret = (png_malloc(png_ptr, size)); + if (ret != NULL) + png_memset(ret,0,(png_size_t)size); + return (ret); +} + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr == NULL || size == 0) + return (NULL); + + if (png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory!"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Cannot Allocate > 64K"); + else +#endif + return NULL; + } +#endif + + /* Check for overflow */ +#if defined(__TURBOC__) && !defined(__FLAT__) + if (size != (unsigned long)size) + ret = NULL; + else + ret = farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + if (size != (unsigned long)size) + ret = NULL; + else + ret = halloc(size, 1); +# else + if (size != (size_t)size) + ret = NULL; + else + ret = malloc((size_t)size); +# endif +#endif + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory"); +#endif + + return (ret); +} + +/* Free a pointer allocated by png_malloc(). If ptr is NULL, return + * without taking any action. + */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else + png_free_default(png_ptr, ptr); +} +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(ptr); +# else + free(ptr); +# endif +#endif +} + +#endif /* Not Borland DOS special memory handler */ + +#ifdef PNG_1_0_X +# define png_malloc_warn png_malloc +#else +/* This function was added at libpng version 1.2.3. The png_malloc_warn() + * function will set up png_malloc() to issue a png_warning and return NULL + * instead of issuing a png_error, if it fails to allocate the requested + * memory. + */ +png_voidp PNGAPI +png_malloc_warn(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ptr; + png_uint_32 save_flags; + if (png_ptr == NULL) + return (NULL); + + save_flags = png_ptr->flags; + png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, size); + png_ptr->flags=save_flags; + return(ptr); +} +#endif + +png_voidp PNGAPI +png_memcpy_check (png_structp png_ptr, png_voidp s1, png_voidp s2, + png_uint_32 length) +{ + png_size_t size; + + size = (png_size_t)length; + if ((png_uint_32)size != length) + png_error(png_ptr, "Overflow in png_memcpy_check."); + + return(png_memcpy (s1, s2, size)); +} + +png_voidp PNGAPI +png_memset_check (png_structp png_ptr, png_voidp s1, int value, + png_uint_32 length) +{ + png_size_t size; + + size = (png_size_t)length; + if ((png_uint_32)size != length) + png_error(png_ptr, "Overflow in png_memset_check."); + + return (png_memset (s1, value, size)); + +} + +#ifdef PNG_USER_MEM_SUPPORTED +/* This function is called when the application wants to use another method + * of allocating and freeing memory. + */ +void PNGAPI +png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr + malloc_fn, png_free_ptr free_fn) +{ + if (png_ptr != NULL) + { + png_ptr->mem_ptr = mem_ptr; + png_ptr->malloc_fn = malloc_fn; + png_ptr->free_fn = free_fn; + } +} + +/* This function returns a pointer to the mem_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_mem_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + return ((png_voidp)png_ptr->mem_ptr); +} +#endif /* PNG_USER_MEM_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngpread.c b/bazaar/plugin/gdal/frmts/png/libpng/pngpread.c new file mode 100644 index 000000000..5af209d4d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngpread.c @@ -0,0 +1,1241 @@ + +/* pngpread.c - read a png file in push mode + * + * Last changed in libpng 1.2.44 [June 26, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + +/* Push model modes */ +#define PNG_READ_SIG_MODE 0 +#define PNG_READ_CHUNK_MODE 1 +#define PNG_READ_IDAT_MODE 2 +#define PNG_SKIP_MODE 3 +#define PNG_READ_tEXt_MODE 4 +#define PNG_READ_zTXt_MODE 5 +#define PNG_READ_DONE_MODE 6 +#define PNG_READ_iTXt_MODE 7 +#define PNG_ERROR_MODE 8 + +void PNGAPI +png_process_data(png_structp png_ptr, png_infop info_ptr, + png_bytep buffer, png_size_t buffer_size) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_push_restore_buffer(png_ptr, buffer, buffer_size); + + while (png_ptr->buffer_size) + { + png_process_some_data(png_ptr, info_ptr); + } +} + +/* What we do with the incoming data depends on what we were previously + * doing before we ran out of data... + */ +void /* PRIVATE */ +png_process_some_data(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr == NULL) + return; + + switch (png_ptr->process_mode) + { + case PNG_READ_SIG_MODE: + { + png_push_read_sig(png_ptr, info_ptr); + break; + } + + case PNG_READ_CHUNK_MODE: + { + png_push_read_chunk(png_ptr, info_ptr); + break; + } + + case PNG_READ_IDAT_MODE: + { + png_push_read_IDAT(png_ptr); + break; + } + + case PNG_SKIP_MODE: + { + png_push_crc_finish(png_ptr); + break; + } + + default: + { + png_ptr->buffer_size = 0; + break; + } + } +} + +/* Read any remaining signature bytes from the stream and compare them with + * the correct PNG signature. It is possible that this routine is called + * with bytes already read from the signature, either because they have been + * checked by the calling application, or because of multiple calls to this + * routine. + */ +void /* PRIVATE */ +png_push_read_sig(png_structp png_ptr, png_infop info_ptr) +{ + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + if (png_ptr->buffer_size < num_to_check) + { + num_to_check = png_ptr->buffer_size; + } + + png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), + num_to_check); + png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check); + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + else + { + if (png_ptr->sig_bytes >= 8) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } + } +} + +void /* PRIVATE */ +png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IHDR; + PNG_CONST PNG_IDAT; + PNG_CONST PNG_IEND; + PNG_CONST PNG_PLTE; +#ifdef PNG_READ_bKGD_SUPPORTED + PNG_CONST PNG_bKGD; +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + PNG_CONST PNG_cHRM; +#endif +#ifdef PNG_READ_gAMA_SUPPORTED + PNG_CONST PNG_gAMA; +#endif +#ifdef PNG_READ_hIST_SUPPORTED + PNG_CONST PNG_hIST; +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + PNG_CONST PNG_iCCP; +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + PNG_CONST PNG_iTXt; +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + PNG_CONST PNG_oFFs; +#endif +#ifdef PNG_READ_pCAL_SUPPORTED + PNG_CONST PNG_pCAL; +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + PNG_CONST PNG_pHYs; +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + PNG_CONST PNG_sBIT; +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + PNG_CONST PNG_sCAL; +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + PNG_CONST PNG_sRGB; +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + PNG_CONST PNG_sPLT; +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + PNG_CONST PNG_tEXt; +#endif +#ifdef PNG_READ_tIME_SUPPORTED + PNG_CONST PNG_tIME; +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + PNG_CONST PNG_tRNS; +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + PNG_CONST PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + + /* First we make sure we have enough data for the 4 byte chunk name + * and the 4 byte chunk length before proceeding with decoding the + * chunk data. To fully decode each of these chunks, we also make + * sure we have enough data in the buffer for the 4 byte CRC at the + * end of every chunk (except IDAT, which is handled separately). + */ + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + } + + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + if (png_ptr->mode & PNG_AFTER_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + { + if (png_ptr->push_length != 13) + png_error(png_ptr, "Invalid IHDR length"); + + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); + } + + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); + + png_ptr->process_mode = PNG_READ_DONE_MODE; + png_push_have_end(png_ptr, info_ptr); + } + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + } + } + +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); + } + + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + /* If we reach an IDAT chunk, this means we have read all of the + * header chunks, and we can start reading the image (or if this + * is called after the image has been read - we have an error). + */ + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + { + if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + if (png_ptr->push_length == 0) + return; + + if (png_ptr->mode & PNG_AFTER_IDAT) + png_error(png_ptr, "Too many IDAT's found"); + } + + png_ptr->idat_size = png_ptr->push_length; + png_ptr->mode |= PNG_HAVE_IDAT; + png_ptr->process_mode = PNG_READ_IDAT_MODE; + png_push_have_info(png_ptr, info_ptr); + png_ptr->zstream.avail_out = + (uInt) PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + png_ptr->zstream.next_out = png_ptr->row_buf; + return; + } + +#ifdef PNG_READ_gAMA_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_bKGD_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_hIST_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_tIME_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + + png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); + } + +#endif + else + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + } + + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; +} + +void /* PRIVATE */ +png_push_crc_skip(png_structp png_ptr, png_uint_32 skip) +{ + png_ptr->process_mode = PNG_SKIP_MODE; + png_ptr->skip_length = skip; +} + +void /* PRIVATE */ +png_push_crc_finish(png_structp png_ptr) +{ + if (png_ptr->skip_length && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->save_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->skip_length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->current_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->skip_length) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } +} + +void PNGAPI +png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) +{ + png_bytep ptr; + + if (png_ptr == NULL) + return; + + ptr = buffer; + if (png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->save_buffer_size) + save_size = length; + else + save_size = png_ptr->save_buffer_size; + + png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size); + length -= save_size; + ptr += save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->current_buffer_size) + save_size = length; + + else + save_size = png_ptr->current_buffer_size; + + png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size); + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } +} + +void /* PRIVATE */ +png_push_save_buffer(png_structp png_ptr) +{ + if (png_ptr->save_buffer_size) + { + if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) + { + png_size_t i, istop; + png_bytep sp; + png_bytep dp; + + istop = png_ptr->save_buffer_size; + for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; + i < istop; i++, sp++, dp++) + { + *dp = *sp; + } + } + } + if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > + png_ptr->save_buffer_max) + { + png_size_t new_max; + png_bytep old_buffer; + + if (png_ptr->save_buffer_size > PNG_SIZE_MAX - + (png_ptr->current_buffer_size + 256)) + { + png_error(png_ptr, "Potential overflow of save_buffer"); + } + + new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; + old_buffer = png_ptr->save_buffer; + png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr, + (png_uint_32)new_max); + if (png_ptr->save_buffer == NULL) + { + png_free(png_ptr, old_buffer); + png_error(png_ptr, "Insufficient memory for save_buffer"); + } + png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); + png_free(png_ptr, old_buffer); + png_ptr->save_buffer_max = new_max; + } + if (png_ptr->current_buffer_size) + { + png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, + png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); + png_ptr->save_buffer_size += png_ptr->current_buffer_size; + png_ptr->current_buffer_size = 0; + } + png_ptr->save_buffer_ptr = png_ptr->save_buffer; + png_ptr->buffer_size = 0; +} + +void /* PRIVATE */ +png_push_restore_buffer(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + png_ptr->current_buffer = buffer; + png_ptr->current_buffer_size = buffer_length; + png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; + png_ptr->current_buffer_ptr = png_ptr->current_buffer; +} + +void /* PRIVATE */ +png_push_read_IDAT(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; +#endif + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_error(png_ptr, "Not enough compressed data"); + return; + } + + png_ptr->idat_size = png_ptr->push_length; + } + if (png_ptr->idat_size && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->save_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + + /* Check for overflow */ + if ((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->idat_size && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->current_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + + /* Check for overflow */ + if ((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->idat_size) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; + png_ptr->mode |= PNG_AFTER_IDAT; + } +} + +void /* PRIVATE */ +png_process_IDAT_data(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + /* The caller checks for a non-zero buffer length. */ + if (!(buffer_length > 0) || buffer == NULL) + png_error(png_ptr, "No IDAT data (internal error)"); + + /* This routine must process all the data it has been given + * before returning, calling the row callback as required to + * handle the uncompressed results. + */ + png_ptr->zstream.next_in = buffer; + png_ptr->zstream.avail_in = (uInt)buffer_length; + + /* Keep going until the decompressed data is all processed + * or the stream marked as finished. + */ + while (png_ptr->zstream.avail_in > 0 && + !(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + { + int ret; + + /* We have data for zlib, but we must check that zlib + * has somewhere to put the results. It doesn't matter + * if we don't expect any results -- it may be the input + * data is just the LZ end code. + */ + if (!(png_ptr->zstream.avail_out > 0)) + { + png_ptr->zstream.avail_out = + (uInt) PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + png_ptr->zstream.next_out = png_ptr->row_buf; + } + + /* Using Z_SYNC_FLUSH here means that an unterminated + * LZ stream can still be handled (a stream with a missing + * end code), otherwise (Z_NO_FLUSH) a future zlib + * implementation might defer output and, therefore, + * change the current behavior. (See comments in inflate.c + * for why this doesn't happen at present with zlib 1.2.5.) + */ + ret = inflate(&png_ptr->zstream, Z_SYNC_FLUSH); + + /* Check for any failure before proceeding. */ + if (ret != Z_OK && ret != Z_STREAM_END) + { + /* Terminate the decompression. */ + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + + /* This may be a truncated stream (missing or + * damaged end code). Treat that as a warning. + */ + if (png_ptr->row_number >= png_ptr->num_rows || + png_ptr->pass > 6) + png_warning(png_ptr, "Truncated compressed data in IDAT"); + else + png_error(png_ptr, "Decompression error in IDAT"); + + /* Skip the check on unprocessed input */ + return; + } + + /* Did inflate output any data? */ + if (png_ptr->zstream.next_out != png_ptr->row_buf) + { + /* Is this unexpected data after the last row? + * If it is, artificially terminate the LZ output + * here. + */ + if (png_ptr->row_number >= png_ptr->num_rows || + png_ptr->pass > 6) + { + /* Extra data. */ + png_warning(png_ptr, "Extra compressed data in IDAT"); + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + /* Do no more processing; skip the unprocessed + * input check below. + */ + return; + } + + /* Do we have a complete row? */ + if (png_ptr->zstream.avail_out == 0) + png_push_process_row(png_ptr); + } + + /* And check for the end of the stream. */ + if (ret == Z_STREAM_END) + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + } + + /* All the data should have been processed, if anything + * is left at this point we have bytes of IDAT data + * after the zlib end code. + */ + if (png_ptr->zstream.avail_in > 0) + png_warning(png_ptr, "Extra compression data"); +} + +void /* PRIVATE */ +png_push_process_row(png_structp png_ptr) +{ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, + png_ptr->rowbytes + 1); + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Blow up interlaced rows to full size */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) +/* old interface (pre-1.0.9): + png_do_read_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + switch (png_ptr->pass) + { + case 0: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 0; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */ + } + + if (png_ptr->pass == 2) /* Pass 1 might be empty */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + + if (png_ptr->pass == 4 && png_ptr->height <= 4) + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + + if (png_ptr->pass == 6 && png_ptr->height <= 4) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + + break; + } + + case 1: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 1; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 2) /* Skip top 4 generated rows */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + + break; + } + + case 2: + { + int i; + + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 4) /* Pass 3 might be empty */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + + break; + } + + case 3: + { + int i; + + for (i = 0; i < 4 && png_ptr->pass == 3; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 4) /* Skip top two generated rows */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + + break; + } + + case 4: + { + int i; + + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 6) /* Pass 5 might be empty */ + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + + break; + } + + case 5: + { + int i; + + for (i = 0; i < 2 && png_ptr->pass == 5; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + + if (png_ptr->pass == 6) /* Skip top generated row */ + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + + break; + } + case 6: + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + + if (png_ptr->pass != 6) + break; + + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + } + else +#endif + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } +} + +void /* PRIVATE */ +png_read_push_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + PNG_CONST int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + PNG_CONST int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + PNG_CONST int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + PNG_CONST int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + + /* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h + PNG_CONST int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; + */ +#endif + + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset_check(png_ptr, png_ptr->prev_row, 0, + png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if ((png_ptr->pass == 1 && png_ptr->width < 5) || + (png_ptr->pass == 3 && png_ptr->width < 3) || + (png_ptr->pass == 5 && png_ptr->width < 2)) + png_ptr->pass++; + + if (png_ptr->pass > 7) + png_ptr->pass--; + + if (png_ptr->pass >= 7) + break; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + if (png_ptr->transformations & PNG_INTERLACE) + break; + + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + + } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); + } +#endif /* PNG_READ_INTERLACING_SUPPORTED */ +} + +void /* PRIVATE */ +png_push_have_info(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->info_fn != NULL) + (*(png_ptr->info_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_end(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->end_fn != NULL) + (*(png_ptr->end_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr->row_fn != NULL) + (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, + (int)png_ptr->pass); +} + +void PNGAPI +png_progressive_combine_row (png_structp png_ptr, + png_bytep old_row, png_bytep new_row) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST int FARDATA png_pass_dsp_mask[7] = + {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; +#endif + + if (png_ptr == NULL) + return; + + if (new_row != NULL) /* new_row must == png_ptr->row_buf here. */ + png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]); +} + +void PNGAPI +png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->info_fn = info_fn; + png_ptr->row_fn = row_fn; + png_ptr->end_fn = end_fn; + + png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); +} + +png_voidp PNGAPI +png_get_progressive_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + + return png_ptr->io_ptr; +} +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngread.c b/bazaar/plugin/gdal/frmts/png/libpng/pngread.c new file mode 100644 index 000000000..24277b185 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngread.c @@ -0,0 +1,1515 @@ + +/* pngread.c - read a PNG file + * + * Last changed in libpng 1.2.52 [November 20, 2014] + * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file contains routines that an application calls directly to + * read a PNG file or stream. + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_READ_SUPPORTED + +/* Create a PNG structure for reading, and allocate any memory needed. */ +png_structp PNGAPI +png_create_read_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ + +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate create PNG structure for reading, and allocate any memory + * needed. + */ +png_structp PNGAPI +png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + +#ifdef PNG_SETJMP_SUPPORTED + volatile +#endif + png_structp png_ptr; + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + + int i; + + png_debug(1, "in png_create_read_struct"); + +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif + if (png_ptr == NULL) + return (NULL); + + /* Added at libpng-1.2.6 */ +#ifdef PNG_USER_LIMITS_SUPPORTED + png_ptr->user_width_max = PNG_USER_WIDTH_MAX; + png_ptr->user_height_max = PNG_USER_HEIGHT_MAX; + /* Added at libpng-1.2.43 and 1.4.0 */ + png_ptr->user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, + (png_free_ptr)free_fn, (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf, jmpbuf, png_sizeof(jmp_buf)); +#endif +#endif /* PNG_SETJMP_SUPPORTED */ + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif + + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + if (user_png_ver != NULL) + { + int found_dots = 0; + i = -1; + + do + { + i++; + if (user_png_ver[i] != PNG_LIBPNG_VER_STRING[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + if (user_png_ver[i] == '.') + found_dots++; + } while (found_dots < 2 && user_png_ver[i] != 0 && + PNG_LIBPNG_VER_STRING[i] != 0); + } + else + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* Initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_MEM_ERROR: + case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory error"); + break; + case Z_VERSION_ERROR: png_error(png_ptr, "zlib version error"); + break; + default: png_error(png_ptr, "Unknown zlib error"); + } + + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then + encounter a png_error() will longjmp here. Since the jmpbuf is + then meaningless we abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf, jmpbuf, png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif /* PNG_SETJMP_SUPPORTED */ + + return (png_ptr); +} + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Initialize PNG structure for reading, and allocate any memory needed. + * This interface is deprecated in favour of the png_create_read_struct(), + * and it will disappear as of libpng-1.3.0. + */ +#undef png_read_init +void PNGAPI +png_read_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_read_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} + +void PNGAPI +png_read_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ + if (png_ptr == NULL) + return; +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + if (png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn = NULL; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if (png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn = NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for reading is" + " too small."); + } + if (png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn = NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + png_error(png_ptr, + "The info struct allocated by application for reading is" + " too small."); + } + png_read_init_3(&png_ptr, user_png_ver, png_struct_size); +} +#endif /* PNG_1_0_X || PNG_1_2_X */ + +void PNGAPI +png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + + int i = 0; + + png_structp png_ptr=*ptr_ptr; + + if (png_ptr == NULL) + return; + + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn = NULL; + png_warning(png_ptr, + "Application uses deprecated png_read_init() and should be" + " recompiled."); + break; +#endif + } + } while (png_libpng_ver[i++]); + + png_debug(1, "in png_read_init_3"); + +#ifdef PNG_SETJMP_SUPPORTED + /* Save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf)); +#endif + + if (png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + *ptr_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + png_ptr = *ptr_ptr; + } + + /* Reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof(png_struct)); + +#ifdef PNG_SETJMP_SUPPORTED + /* Restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf)); +#endif + + /* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max = PNG_USER_WIDTH_MAX; + png_ptr->user_height_max = PNG_USER_HEIGHT_MAX; +#endif + + /* Initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory error"); break; + case Z_VERSION_ERROR: png_error(png_ptr, "zlib version error"); + break; + default: png_error(png_ptr, "Unknown zlib error"); + } + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); +} + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. This has been + * changed in v0.90 to allow reading a file that already has the magic + * bytes read from the stream. You can tell libpng how many bytes have + * been read from the beginning of the stream (up to the maximum of 8) + * via png_set_sig_bytes(), and we will only check the remaining bytes + * here. The application can then have access to the signature bytes we + * read if it is determined that this isn't a valid PNG file. + */ +void PNGAPI +png_read_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_info"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* If we haven't checked all of the PNG signature bytes, do so now. */ + if (png_ptr->sig_bytes < 8) + { + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); + png_ptr->sig_bytes = 8; + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + if (num_checked < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; + } + + for (;;) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IHDR; + PNG_CONST PNG_IDAT; + PNG_CONST PNG_IEND; + PNG_CONST PNG_PLTE; +#ifdef PNG_READ_bKGD_SUPPORTED + PNG_CONST PNG_bKGD; +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + PNG_CONST PNG_cHRM; +#endif +#ifdef PNG_READ_gAMA_SUPPORTED + PNG_CONST PNG_gAMA; +#endif +#ifdef PNG_READ_hIST_SUPPORTED + PNG_CONST PNG_hIST; +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + PNG_CONST PNG_iCCP; +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + PNG_CONST PNG_iTXt; +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + PNG_CONST PNG_oFFs; +#endif +#ifdef PNG_READ_pCAL_SUPPORTED + PNG_CONST PNG_pCAL; +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + PNG_CONST PNG_pHYs; +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + PNG_CONST PNG_sBIT; +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + PNG_CONST PNG_sCAL; +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + PNG_CONST PNG_sPLT; +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + PNG_CONST PNG_sRGB; +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + PNG_CONST PNG_tEXt; +#endif +#ifdef PNG_READ_tIME_SUPPORTED + PNG_CONST PNG_tIME; +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + PNG_CONST PNG_tRNS; +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + PNG_CONST PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + png_uint_32 length = png_read_chunk_header(png_ptr); + PNG_CONST png_bytep chunk_name = png_ptr->chunk_name; + + /* This should be a binary subdivision search or a hash for + * matching the chunk name rather than a linear search. + */ + if (!png_memcmp(chunk_name, png_IDAT, 4)) + if (png_ptr->mode & PNG_AFTER_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + if (!png_memcmp(chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, chunk_name)) + { + if (!png_memcmp(chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + else if (!png_memcmp(chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + break; + } + } +#endif + else if (!png_memcmp(chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); + else if (!png_memcmp(chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + png_ptr->idat_size = length; + png_ptr->mode |= PNG_HAVE_IDAT; + break; + } +#ifdef PNG_READ_bKGD_SUPPORTED + else if (!png_memcmp(chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + else if (!png_memcmp(chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_gAMA_SUPPORTED + else if (!png_memcmp(chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_hIST_SUPPORTED + else if (!png_memcmp(chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + else if (!png_memcmp(chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_pCAL_SUPPORTED + else if (!png_memcmp(chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + else if (!png_memcmp(chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + else if (!png_memcmp(chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + else if (!png_memcmp(chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + else if (!png_memcmp(chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + else if (!png_memcmp(chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + else if (!png_memcmp(chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + else if (!png_memcmp(chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_tIME_SUPPORTED + else if (!png_memcmp(chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + else if (!png_memcmp(chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + else if (!png_memcmp(chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + else if (!png_memcmp(chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +/* Optional call to update the users info_ptr structure */ +void PNGAPI +png_read_update_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_update_info"); + + if (png_ptr == NULL) + return; + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + else + png_warning(png_ptr, + "Ignoring extra png_read_update_info() call; row buffer not reallocated"); + + png_read_transform_info(png_ptr, info_ptr); +} + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Initialize palette, background, etc, after transformations + * are set, but before any reading takes place. This allows + * the user to obtain a gamma-corrected palette, for example. + * If the user doesn't call this, we will do it ourselves. + */ +void PNGAPI +png_start_read_image(png_structp png_ptr) +{ + png_debug(1, "in png_start_read_image"); + + if (png_ptr == NULL) + return; + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +void PNGAPI +png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) +{ + PNG_CONST PNG_IDAT; + PNG_CONST int png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, + 0xff}; + PNG_CONST int png_pass_mask[7] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; + int ret; + + if (png_ptr == NULL) + return; + + png_debug2(1, "in png_read_row (row %lu, pass %d)", + png_ptr->row_number, png_ptr->pass); + + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* Check for transforms that have been set but were defined out */ +#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ + !defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined."); +#endif + } + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* If interlaced and we do not need a new row, combine row and return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + if (dsp_row != NULL && (png_ptr->row_number & 4)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 3) || png_ptr->width < 3) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 3) != 2) + { + if (dsp_row != NULL && (png_ptr->row_number & 2)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 1) || png_ptr->width < 2) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 1)) + { + png_read_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "Invalid attempt to read row data"); + + png_ptr->zstream.next_out = png_ptr->row_buf; + png_ptr->zstream.avail_out = + (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1); + do + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_crc_finish(png_ptr, 0); + + png_ptr->idat_size = png_read_chunk_header(png_ptr); + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, + (png_size_t)png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_error(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression error"); + + } while (png_ptr->zstream.avail_out); + + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + if (png_ptr->row_buf[0]) + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, + png_ptr->rowbytes + 1); + +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_read_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Blow up interlaced rows to full size */ + if (png_ptr->interlaced && + (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) + /* Old interface (pre-1.0.9): + * png_do_read_interlace(&(png_ptr->row_info), + * png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + if (row != NULL) + png_combine_row(png_ptr, row, + png_pass_mask[png_ptr->pass]); + } + else +#endif + { + if (row != NULL) + png_combine_row(png_ptr, row, 0xff); + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 0xff); + } + png_read_finish_row(png_ptr); + + if (png_ptr->read_row_fn != NULL) + (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. If the image is interlaced, + * and png_set_interlace_handling() has been called, the rows need to + * contain the contents of the rows from the previous pass. If the + * image has alpha or transparency, and png_handle_alpha()[*] has been + * called, the rows contents must be initialized to the contents of the + * screen. + * + * "row" holds the actual image, and pixels are placed in it + * as they arrive. If the image is displayed after each pass, it will + * appear to "sparkle" in. "display_row" can be used to display a + * "chunky" progressive image, with finer detail added as it becomes + * available. If you do not want this "chunky" display, you may pass + * NULL for display_row. If you do not want the sparkle display, and + * you have not called png_handle_alpha(), you may pass NULL for rows. + * If you have called png_handle_alpha(), and the image has either an + * alpha channel or a transparency chunk, you must provide a buffer for + * rows. In this case, you do not have to provide a display_row buffer + * also, but you may. If the image is not interlaced, or if you have + * not called png_set_interlace_handling(), the display_row buffer will + * be ignored, so pass NULL to it. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ + +void PNGAPI +png_read_rows(png_structp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows) +{ + png_uint_32 i; + png_bytepp rp; + png_bytepp dp; + + png_debug(1, "in png_read_rows"); + + if (png_ptr == NULL) + return; + rp = row; + dp = display_row; + if (rp != NULL && dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp++; + png_bytep dptr = *dp++; + + png_read_row(png_ptr, rptr, dptr); + } + else if (rp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp; + png_read_row(png_ptr, rptr, png_bytep_NULL); + rp++; + } + else if (dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep dptr = *dp; + png_read_row(png_ptr, png_bytep_NULL, dptr); + dp++; + } +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the entire image. If the image has an alpha channel or a tRNS + * chunk, and you have called png_handle_alpha()[*], you will need to + * initialize the image to the current image that PNG will be overlaying. + * We set the num_rows again here, in case it was incorrectly set in + * png_read_start_row() by a call to png_read_update_info() or + * png_start_read_image() if png_set_interlace_handling() wasn't called + * prior to either of these functions like it should have been. You can + * only call this function once. If you desire to have an image for + * each pass of a interlaced image, use png_read_rows() instead. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ +void PNGAPI +png_read_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i, image_height; + int pass, j; + png_bytepp rp; + + png_debug(1, "in png_read_image"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + pass = png_set_interlace_handling(png_ptr); +#else + if (png_ptr->interlaced) + png_error(png_ptr, + "Cannot read interlaced image -- interlace handler disabled."); + pass = 1; +#endif + + + image_height=png_ptr->height; + png_ptr->num_rows = image_height; /* Make sure this is set correctly */ + + for (j = 0; j < pass; j++) + { + rp = image; + for (i = 0; i < image_height; i++) + { + png_read_row(png_ptr, *rp, png_bytep_NULL); + rp++; + } + } +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. Will not read past the end of the + * file, will verify the end is accurate, and will read any comments + * or time information at the end of the file, if info is not NULL. + */ +void PNGAPI +png_read_end(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_end"); + + if (png_ptr == NULL) + return; + png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */ + + do + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IHDR; + PNG_CONST PNG_IDAT; + PNG_CONST PNG_IEND; + PNG_CONST PNG_PLTE; +#ifdef PNG_READ_bKGD_SUPPORTED + PNG_CONST PNG_bKGD; +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + PNG_CONST PNG_cHRM; +#endif +#ifdef PNG_READ_gAMA_SUPPORTED + PNG_CONST PNG_gAMA; +#endif +#ifdef PNG_READ_hIST_SUPPORTED + PNG_CONST PNG_hIST; +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + PNG_CONST PNG_iCCP; +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + PNG_CONST PNG_iTXt; +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + PNG_CONST PNG_oFFs; +#endif +#ifdef PNG_READ_pCAL_SUPPORTED + PNG_CONST PNG_pCAL; +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + PNG_CONST PNG_pHYs; +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + PNG_CONST PNG_sBIT; +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + PNG_CONST PNG_sCAL; +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + PNG_CONST PNG_sPLT; +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + PNG_CONST PNG_sRGB; +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + PNG_CONST PNG_tEXt; +#endif +#ifdef PNG_READ_tIME_SUPPORTED + PNG_CONST PNG_tIME; +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + PNG_CONST PNG_tRNS; +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + PNG_CONST PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + png_uint_32 length = png_read_chunk_header(png_ptr); + PNG_CONST png_bytep chunk_name = png_ptr->chunk_name; + + if (!png_memcmp(chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, chunk_name)) + { + if (!png_memcmp(chunk_name, png_IDAT, 4)) + { + if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + png_error(png_ptr, "Too many IDAT's found"); + } + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + } +#endif + else if (!png_memcmp(chunk_name, png_IDAT, 4)) + { + /* Zero length IDATs are legal after the last IDAT has been + * read, but not after other chunks have been read. + */ + if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + png_error(png_ptr, "Too many IDAT's found"); + png_crc_finish(png_ptr, length); + } + else if (!png_memcmp(chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); +#ifdef PNG_READ_bKGD_SUPPORTED + else if (!png_memcmp(chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_cHRM_SUPPORTED + else if (!png_memcmp(chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_gAMA_SUPPORTED + else if (!png_memcmp(chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_hIST_SUPPORTED + else if (!png_memcmp(chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_oFFs_SUPPORTED + else if (!png_memcmp(chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_pCAL_SUPPORTED + else if (!png_memcmp(chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sCAL_SUPPORTED + else if (!png_memcmp(chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_pHYs_SUPPORTED + else if (!png_memcmp(chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sBIT_SUPPORTED + else if (!png_memcmp(chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sRGB_SUPPORTED + else if (!png_memcmp(chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_iCCP_SUPPORTED + else if (!png_memcmp(chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_sPLT_SUPPORTED + else if (!png_memcmp(chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_tEXt_SUPPORTED + else if (!png_memcmp(chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_tIME_SUPPORTED + else if (!png_memcmp(chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_tRNS_SUPPORTED + else if (!png_memcmp(chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_zTXt_SUPPORTED + else if (!png_memcmp(chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#ifdef PNG_READ_iTXt_SUPPORTED + else if (!png_memcmp(chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } while (!(png_ptr->mode & PNG_HAVE_IEND)); +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +/* Free all memory used by the read */ +void PNGAPI +png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, + png_infopp end_info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL, end_info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn = NULL; + png_voidp mem_ptr = NULL; +#endif + + png_debug(1, "in png_destroy_read_struct"); + + if (png_ptr_ptr != NULL) + png_ptr = *png_ptr_ptr; + if (png_ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (end_info_ptr_ptr != NULL) + end_info_ptr = *end_info_ptr_ptr; + + png_read_destroy(png_ptr, info_ptr, end_info_ptr); + + if (info_ptr != NULL) + { +#ifdef PNG_TEXT_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1); +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (end_info_ptr != NULL) + { +#ifdef PNG_READ_TEXT_SUPPORTED + png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1); +#endif +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)end_info_ptr); +#endif + *end_info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + +/* Free all memory used by the read (old method) */ +void /* PRIVATE */ +png_read_destroy(png_structp png_ptr, png_infop info_ptr, + png_infop end_info_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_read_destroy"); + + if (info_ptr != NULL) + png_info_destroy(png_ptr, info_ptr); + + if (end_info_ptr != NULL) + png_info_destroy(png_ptr, end_info_ptr); + + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->big_row_buf); + png_free(png_ptr, png_ptr->prev_row); + png_free(png_ptr, png_ptr->chunkdata); +#ifdef PNG_READ_DITHER_SUPPORTED + png_free(png_ptr, png_ptr->palette_lookup); + png_free(png_ptr, png_ptr->dither_index); +#endif +#ifdef PNG_READ_GAMMA_SUPPORTED + png_free(png_ptr, png_ptr->gamma_table); +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED + png_free(png_ptr, png_ptr->gamma_from_1); + png_free(png_ptr, png_ptr->gamma_to_1); +#endif +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->free_me &= ~PNG_FREE_PLTE; +#else + if (png_ptr->flags & PNG_FLAG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif +#if defined(PNG_tRNS_SUPPORTED) || \ + defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans); + png_ptr->free_me &= ~PNG_FREE_TRNS; +#else + if (png_ptr->flags & PNG_FLAG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans); + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif +#endif +#ifdef PNG_READ_hIST_SUPPORTED +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->free_me &= ~PNG_FREE_HIST; +#else + if (png_ptr->flags & PNG_FLAG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif +#endif +#ifdef PNG_READ_GAMMA_SUPPORTED + if (png_ptr->gamma_16_table != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_table[i]); + } + png_free(png_ptr, png_ptr->gamma_16_table); + } +#ifdef PNG_READ_BACKGROUND_SUPPORTED + if (png_ptr->gamma_16_from_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_from_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_from_1); + } + if (png_ptr->gamma_16_to_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_to_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_to_1); + } +#endif +#endif +#ifdef PNG_TIME_RFC1123_SUPPORTED + png_free(png_ptr, png_ptr->time_buffer); +#endif + + inflateEnd(&png_ptr->zstream); +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_free(png_ptr, png_ptr->save_buffer); +#endif + + /* Save the important info out of the png_struct, in case it is + * being used again. + */ +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof(png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf)); +#endif + +} + +void PNGAPI +png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->read_row_fn = read_row_fn; +} + + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_read_png(png_structp png_ptr, png_infop info_ptr, + int transforms, + voidp params) +{ + int row; + + if (png_ptr == NULL) + return; +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel from opacity to transparency + */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* png_read_info() gives us all of the information from the + * PNG file before the first IDAT (image data chunk). + */ + png_read_info(png_ptr, info_ptr); + if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep)) + png_error(png_ptr, "Image is too high to process with png_read_png()"); + + /* -------------- image transformations start here ------------------- */ + +#ifdef PNG_READ_16_TO_8_SUPPORTED + /* Tell libpng to strip 16 bit/color files down to 8 bits per color. + */ + if (transforms & PNG_TRANSFORM_STRIP_16) + png_set_strip_16(png_ptr); +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + /* Strip alpha bytes from the input data without combining with + * the background (not recommended). + */ + if (transforms & PNG_TRANSFORM_STRIP_ALPHA) + png_set_strip_alpha(png_ptr); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED) + /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single + * byte into separate bytes (useful for paletted and grayscale images). + */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + /* Change the order of packed pixels to least significant bit first + * (not useful if you are using png_set_packing). + */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED + /* Expand paletted colors into true RGB triplets + * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel + * Expand paletted or RGB images with transparency to full alpha + * channels so the data will be available as RGBA quartets. + */ + if (transforms & PNG_TRANSFORM_EXPAND) + if ((png_ptr->bit_depth < 8) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) || + (info_ptr->valid & PNG_INFO_tRNS)) + png_set_expand(png_ptr); +#endif + + /* We don't handle background color or gamma transformation or dithering. + */ + +#ifdef PNG_READ_INVERT_SUPPORTED + /* Invert monochrome files to have 0 as white and 1 as black + */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED + /* If you want to shift the pixel values from the range [0,255] or + * [0,65535] to the original [0,7] or [0,31], or whatever range the + * colors were originally in: + */ + if ((transforms & PNG_TRANSFORM_SHIFT) && (info_ptr->valid & PNG_INFO_sBIT)) + png_set_shift(png_ptr, &info_ptr->sig_bit); +#endif + +#ifdef PNG_READ_BGR_SUPPORTED + /* Flip the RGB pixels to BGR (or RGBA to BGRA) + */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED + /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) + */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#ifdef PNG_READ_SWAP_SUPPORTED + /* Swap bytes of 16 bit files to least significant byte first + */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + +/* Added at libpng-1.2.41 */ +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel from opacity to transparency + */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + +/* Added at libpng-1.2.41 */ +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* Expand grayscale image to RGB + */ + if (transforms & PNG_TRANSFORM_GRAY_TO_RGB) + png_set_gray_to_rgb(png_ptr); +#endif + + /* We don't handle adding filler bytes */ + + /* Optional call to gamma correct and add the background to the palette + * and update info structure. REQUIRED if you are expecting libpng to + * update the palette for you (i.e., you selected such a transform above). + */ + png_read_update_info(png_ptr, info_ptr); + + /* -------------- image transformations end here ------------------- */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); +#endif + if (info_ptr->row_pointers == NULL) + { + info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, + info_ptr->height * png_sizeof(png_bytep)); + png_memset(info_ptr->row_pointers, 0, info_ptr->height + * png_sizeof(png_bytep)); + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ROWS; +#endif + + for (row = 0; row < (int)info_ptr->height; row++) + info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr, + png_get_rowbytes(png_ptr, info_ptr)); + } + + png_read_image(png_ptr, info_ptr->row_pointers); + info_ptr->valid |= PNG_INFO_IDAT; + + /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); + + PNG_UNUSED(transforms) /* Quiet compiler warnings */ + PNG_UNUSED(params) + +} +#endif /* PNG_INFO_IMAGE_SUPPORTED */ +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngrio.c b/bazaar/plugin/gdal/frmts/png/libpng/pngrio.c new file mode 100644 index 000000000..6978682c7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngrio.c @@ -0,0 +1,180 @@ + +/* pngrio.c - functions for data input + * + * Last changed in libpng 1.2.43 [February 25, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all input. Users who need + * special handling are expected to write a function that has the same + * arguments as this and performs a similar function, but that possibly + * has a different input method. Note that you shouldn't change this + * function, but rather write a replacement function and then make + * libpng use it at run time with png_set_read_fn(...). + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_READ_SUPPORTED + +/* Read the data from whatever input you are using. The default routine + * reads from a file pointer. Note that this routine sometimes gets called + * with very small lengths, so you should implement some kind of simple + * buffering if you are using unbuffered reads. This should never be asked + * to read more then 64K on a 16 bit machine. + */ +void /* PRIVATE */ +png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_debug1(4, "reading %d bytes", (int)length); + + if (png_ptr->read_data_fn != NULL) + (*(png_ptr->read_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL read function"); +} + +#ifdef PNG_STDIO_SUPPORTED +/* This is the function that does the actual reading of data. If you are + * not reading from a standard C stream, you should create a replacement + * read_data function and use it at run time with png_set_read_fn(), rather + * than changing the library. + */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + if (png_ptr == NULL) + return; + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ +#ifdef _WIN32_WCE + if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = (png_size_t)fread(data, (png_size_t)1, length, + (png_FILE_p)png_ptr->io_ptr); +#endif + + if (check != length) + png_error(png_ptr, "Read Error"); +} +#else +/* This is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void PNGAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + int check; + png_byte *n_data; + png_FILE_p io_ptr; + + if (png_ptr == NULL) + return; + /* Check if data really is near. If so, use usual code. */ + n_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)n_data == data) + { +#ifdef _WIN32_WCE + if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, + NULL) ) + check = 0; +#else + check = fread(n_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t read, remaining, err; + check = 0; + remaining = length; + do + { + read = MIN(NEAR_BUF_SIZE, remaining); +#ifdef _WIN32_WCE + if ( !ReadFile((HANDLE)(io_ptr), buf, read, &err, NULL) ) + err = 0; +#else + err = fread(buf, (png_size_t)1, read, io_ptr); +#endif + png_memcpy(data, buf, read); /* copy far buffer to near buffer */ + if (err != read) + break; + else + check += err; + data += read; + remaining -= read; + } + while (remaining != 0); + } + if ((png_uint_32)check != (png_uint_32)length) + png_error(png_ptr, "read Error"); +} +#endif +#endif + +/* This function allows the application to supply a new input function + * for libpng if standard C streams aren't being used. + * + * This function takes as its arguments: + * png_ptr - pointer to a png input data structure + * io_ptr - pointer to user supplied structure containing info about + * the input functions. May be NULL. + * read_data_fn - pointer to a new input function that takes as its + * arguments a pointer to a png_struct, a pointer to + * a location where input data can be stored, and a 32-bit + * unsigned int that is the number of bytes to be read. + * To exit and output any fatal error messages the new write + * function should call png_error(png_ptr, "Error msg"). + * May be NULL, in which case libpng's default function will + * be used. + */ +void PNGAPI +png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->io_ptr = io_ptr; + +#ifdef PNG_STDIO_SUPPORTED + if (read_data_fn != NULL) + png_ptr->read_data_fn = read_data_fn; + else + png_ptr->read_data_fn = png_default_read_data; +#else + png_ptr->read_data_fn = read_data_fn; +#endif + + /* It is an error to write to a read device */ + if (png_ptr->write_data_fn != NULL) + { + png_ptr->write_data_fn = NULL; + png_warning(png_ptr, + "It's an error to set both read_data_fn and write_data_fn in the "); + png_warning(png_ptr, + "same structure. Resetting write_data_fn to NULL."); + } + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_ptr->output_flush_fn = NULL; +#endif +} +#endif /* PNG_READ_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngrtran.c b/bazaar/plugin/gdal/frmts/png/libpng/pngrtran.c new file mode 100644 index 000000000..0a760cef0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngrtran.c @@ -0,0 +1,4476 @@ + +/* pngrtran.c - transforms the data in a row for PNG readers + * + * Last changed in libpng 1.2.51 [February 6, 2014] + * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file contains functions optionally called by an application + * in order to tell libpng how to handle data when reading a PNG. + * Transformations that are used in both reading and writing are + * in pngtrans.c. + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_READ_SUPPORTED + +/* Set the action on getting a CRC error for an ancillary or critical chunk. */ +void PNGAPI +png_set_crc_action(png_structp png_ptr, int crit_action, int ancil_action) +{ + png_debug(1, "in png_set_crc_action"); + + if (png_ptr == NULL) + return; + + /* Tell libpng how we react to CRC errors in critical chunks */ + switch (crit_action) + { + case PNG_CRC_NO_CHANGE: /* Leave setting as is */ + break; + + case PNG_CRC_WARN_USE: /* Warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE; + break; + + case PNG_CRC_QUIET_USE: /* Quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE | + PNG_FLAG_CRC_CRITICAL_IGNORE; + break; + + case PNG_CRC_WARN_DISCARD: /* Not a valid action for critical data */ + png_warning(png_ptr, + "Can't discard critical data on CRC error."); + case PNG_CRC_ERROR_QUIT: /* Error/quit */ + + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + break; + } + + /* Tell libpng how we react to CRC errors in ancillary chunks */ + switch (ancil_action) + { + case PNG_CRC_NO_CHANGE: /* Leave setting as is */ + break; + + case PNG_CRC_WARN_USE: /* Warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE; + break; + + case PNG_CRC_QUIET_USE: /* Quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE | + PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + + case PNG_CRC_ERROR_QUIT: /* Error/quit */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + + case PNG_CRC_WARN_DISCARD: /* Warn/discard data */ + + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + break; + } +} + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* Handle alpha and tRNS via a background color */ +void PNGAPI +png_set_background(png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma) +{ + png_debug(1, "in png_set_background"); + + if (png_ptr == NULL) + return; + if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN) + { + png_warning(png_ptr, "Application must supply a known background gamma"); + return; + } + + png_ptr->transformations |= PNG_BACKGROUND; + png_memcpy(&(png_ptr->background), background_color, + png_sizeof(png_color_16)); + png_ptr->background_gamma = (float)background_gamma; + png_ptr->background_gamma_type = (png_byte)(background_gamma_code); + png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0); +} +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED +/* Strip 16 bit depth files to 8 bit depth */ +void PNGAPI +png_set_strip_16(png_structp png_ptr) +{ + png_debug(1, "in png_set_strip_16"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_16_TO_8; +} +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +void PNGAPI +png_set_strip_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_strip_alpha"); + + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_STRIP_ALPHA; +} +#endif + +#ifdef PNG_READ_DITHER_SUPPORTED +/* Dither file to 8 bit. Supply a palette, the current number + * of elements in the palette, the maximum number of elements + * allowed, and a histogram if possible. If the current number + * of colors is greater then the maximum number, the palette will be + * modified to fit in the maximum number. "full_dither" indicates + * whether we need a dithering cube set up for RGB images, or if we + * simply are reducing the number of colors in a paletted image. + */ + +typedef struct png_dsort_struct +{ + struct png_dsort_struct FAR * next; + png_byte left; + png_byte right; +} png_dsort; +typedef png_dsort FAR * png_dsortp; +typedef png_dsort FAR * FAR * png_dsortpp; + +void PNGAPI +png_set_dither(png_structp png_ptr, png_colorp palette, + int num_palette, int maximum_colors, png_uint_16p histogram, + int full_dither) +{ + png_debug(1, "in png_set_dither"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_DITHER; + + if (!full_dither) + { + int i; + + png_ptr->dither_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof(png_byte))); + for (i = 0; i < num_palette; i++) + png_ptr->dither_index[i] = (png_byte)i; + } + + if (num_palette > maximum_colors) + { + if (histogram != NULL) + { + /* This is easy enough, just throw out the least used colors. + * Perhaps not the best solution, but good enough. + */ + + int i; + + /* Initialize an array to sort colors */ + png_ptr->dither_sort = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof(png_byte))); + + /* Initialize the dither_sort array */ + for (i = 0; i < num_palette; i++) + png_ptr->dither_sort[i] = (png_byte)i; + + /* Find the least used palette entries by starting a + * bubble sort, and running it until we have sorted + * out enough colors. Note that we don't care about + * sorting all the colors, just finding which are + * least used. + */ + + for (i = num_palette - 1; i >= maximum_colors; i--) + { + int done; /* To stop early if the list is pre-sorted */ + int j; + + done = 1; + for (j = 0; j < i; j++) + { + if (histogram[png_ptr->dither_sort[j]] + < histogram[png_ptr->dither_sort[j + 1]]) + { + png_byte t; + + t = png_ptr->dither_sort[j]; + png_ptr->dither_sort[j] = png_ptr->dither_sort[j + 1]; + png_ptr->dither_sort[j + 1] = t; + done = 0; + } + } + if (done) + break; + } + + /* Swap the palette around, and set up a table, if necessary */ + if (full_dither) + { + int j = num_palette; + + /* Put all the useful colors within the max, but don't + * move the others. + */ + for (i = 0; i < maximum_colors; i++) + { + if ((int)png_ptr->dither_sort[i] >= maximum_colors) + { + do + j--; + while ((int)png_ptr->dither_sort[j] >= maximum_colors); + palette[i] = palette[j]; + } + } + } + else + { + int j = num_palette; + + /* Move all the used colors inside the max limit, and + * develop a translation table. + */ + for (i = 0; i < maximum_colors; i++) + { + /* Only move the colors we need to */ + if ((int)png_ptr->dither_sort[i] >= maximum_colors) + { + png_color tmp_color; + + do + j--; + while ((int)png_ptr->dither_sort[j] >= maximum_colors); + + tmp_color = palette[j]; + palette[j] = palette[i]; + palette[i] = tmp_color; + /* Indicate where the color went */ + png_ptr->dither_index[j] = (png_byte)i; + png_ptr->dither_index[i] = (png_byte)j; + } + } + + /* Find closest color for those colors we are not using */ + for (i = 0; i < num_palette; i++) + { + if ((int)png_ptr->dither_index[i] >= maximum_colors) + { + int min_d, k, min_k, d_index; + + /* Find the closest color to one we threw out */ + d_index = png_ptr->dither_index[i]; + min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); + for (k = 1, min_k = 0; k < maximum_colors; k++) + { + int d; + + d = PNG_COLOR_DIST(palette[d_index], palette[k]); + + if (d < min_d) + { + min_d = d; + min_k = k; + } + } + /* Point to closest color */ + png_ptr->dither_index[i] = (png_byte)min_k; + } + } + } + png_free(png_ptr, png_ptr->dither_sort); + png_ptr->dither_sort = NULL; + } + else + { + /* This is much harder to do simply (and quickly). Perhaps + * we need to go through a median cut routine, but those + * don't always behave themselves with only a few colors + * as input. So we will just find the closest two colors, + * and throw out one of them (chosen somewhat randomly). + * [We don't understand this at all, so if someone wants to + * work on improving it, be our guest - AED, GRP] + */ + int i; + int max_d; + int num_new_palette; + png_dsortp t; + png_dsortpp hash; + + t = NULL; + + /* Initialize palette index arrays */ + png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof(png_byte))); + png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof(png_byte))); + + /* Initialize the sort array */ + for (i = 0; i < num_palette; i++) + { + png_ptr->index_to_palette[i] = (png_byte)i; + png_ptr->palette_to_index[i] = (png_byte)i; + } + + hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 * + png_sizeof(png_dsortp))); + + num_new_palette = num_palette; + + /* Initial wild guess at how far apart the farthest pixel + * pair we will be eliminating will be. Larger + * numbers mean more areas will be allocated, Smaller + * numbers run the risk of not saving enough data, and + * having to do this all over again. + * + * I have not done extensive checking on this number. + */ + max_d = 96; + + while (num_new_palette > maximum_colors) + { + for (i = 0; i < num_new_palette - 1; i++) + { + int j; + + for (j = i + 1; j < num_new_palette; j++) + { + int d; + + d = PNG_COLOR_DIST(palette[i], palette[j]); + + if (d <= max_d) + { + + t = (png_dsortp)png_malloc_warn(png_ptr, + (png_uint_32)(png_sizeof(png_dsort))); + if (t == NULL) + break; + t->next = hash[d]; + t->left = (png_byte)i; + t->right = (png_byte)j; + hash[d] = t; + } + } + if (t == NULL) + break; + } + + if (t != NULL) + for (i = 0; i <= max_d; i++) + { + if (hash[i] != NULL) + { + png_dsortp p; + + for (p = hash[i]; p; p = p->next) + { + if ((int)png_ptr->index_to_palette[p->left] + < num_new_palette && + (int)png_ptr->index_to_palette[p->right] + < num_new_palette) + { + int j, next_j; + + if (num_new_palette & 0x01) + { + j = p->left; + next_j = p->right; + } + else + { + j = p->right; + next_j = p->left; + } + + num_new_palette--; + palette[png_ptr->index_to_palette[j]] + = palette[num_new_palette]; + if (!full_dither) + { + int k; + + for (k = 0; k < num_palette; k++) + { + if (png_ptr->dither_index[k] == + png_ptr->index_to_palette[j]) + png_ptr->dither_index[k] = + png_ptr->index_to_palette[next_j]; + if ((int)png_ptr->dither_index[k] == + num_new_palette) + png_ptr->dither_index[k] = + png_ptr->index_to_palette[j]; + } + } + + png_ptr->index_to_palette[png_ptr->palette_to_index + [num_new_palette]] = png_ptr->index_to_palette[j]; + png_ptr->palette_to_index[png_ptr->index_to_palette[j]] + = png_ptr->palette_to_index[num_new_palette]; + + png_ptr->index_to_palette[j] = + (png_byte)num_new_palette; + png_ptr->palette_to_index[num_new_palette] = + (png_byte)j; + } + if (num_new_palette <= maximum_colors) + break; + } + if (num_new_palette <= maximum_colors) + break; + } + } + + for (i = 0; i < 769; i++) + { + if (hash[i] != NULL) + { + png_dsortp p = hash[i]; + while (p) + { + t = p->next; + png_free(png_ptr, p); + p = t; + } + } + hash[i] = 0; + } + max_d += 96; + } + png_free(png_ptr, hash); + png_free(png_ptr, png_ptr->palette_to_index); + png_free(png_ptr, png_ptr->index_to_palette); + png_ptr->palette_to_index = NULL; + png_ptr->index_to_palette = NULL; + } + num_palette = maximum_colors; + } + if (png_ptr->palette == NULL) + { + png_ptr->palette = palette; + } + png_ptr->num_palette = (png_uint_16)num_palette; + + if (full_dither) + { + int i; + png_bytep distance; + int total_bits = PNG_DITHER_RED_BITS + PNG_DITHER_GREEN_BITS + + PNG_DITHER_BLUE_BITS; + int num_red = (1 << PNG_DITHER_RED_BITS); + int num_green = (1 << PNG_DITHER_GREEN_BITS); + int num_blue = (1 << PNG_DITHER_BLUE_BITS); + png_size_t num_entries = ((png_size_t)1 << total_bits); + + png_ptr->palette_lookup = (png_bytep )png_calloc(png_ptr, + (png_uint_32)(num_entries * png_sizeof(png_byte))); + + distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries * + png_sizeof(png_byte))); + png_memset(distance, 0xff, num_entries * png_sizeof(png_byte)); + + for (i = 0; i < num_palette; i++) + { + int ir, ig, ib; + int r = (palette[i].red >> (8 - PNG_DITHER_RED_BITS)); + int g = (palette[i].green >> (8 - PNG_DITHER_GREEN_BITS)); + int b = (palette[i].blue >> (8 - PNG_DITHER_BLUE_BITS)); + + for (ir = 0; ir < num_red; ir++) + { + /* int dr = abs(ir - r); */ + int dr = ((ir > r) ? ir - r : r - ir); + int index_r = (ir << (PNG_DITHER_BLUE_BITS + + PNG_DITHER_GREEN_BITS)); + + for (ig = 0; ig < num_green; ig++) + { + /* int dg = abs(ig - g); */ + int dg = ((ig > g) ? ig - g : g - ig); + int dt = dr + dg; + int dm = ((dr > dg) ? dr : dg); + int index_g = index_r | (ig << PNG_DITHER_BLUE_BITS); + + for (ib = 0; ib < num_blue; ib++) + { + int d_index = index_g | ib; + /* int db = abs(ib - b); */ + int db = ((ib > b) ? ib - b : b - ib); + int dmax = ((dm > db) ? dm : db); + int d = dmax + dt + db; + + if (d < (int)distance[d_index]) + { + distance[d_index] = (png_byte)d; + png_ptr->palette_lookup[d_index] = (png_byte)i; + } + } + } + } + } + + png_free(png_ptr, distance); + } +} +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) +/* Transform the image from the file_gamma to the screen_gamma. We + * only do transformations on images where the file_gamma and screen_gamma + * are not close reciprocals, otherwise it slows things down slightly, and + * also needlessly introduces small errors. + * + * We will turn off gamma transformation later if no semitransparent entries + * are present in the tRNS array for palette images. We can't do it here + * because we don't necessarily have the tRNS chunk yet. + */ +void PNGAPI +png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma) +{ + png_debug(1, "in png_set_gamma"); + + if (png_ptr == NULL) + return; + + if ((fabs(scrn_gamma * file_gamma - 1.0) > PNG_GAMMA_THRESHOLD) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) + png_ptr->transformations |= PNG_GAMMA; + png_ptr->gamma = (float)file_gamma; + png_ptr->screen_gamma = (float)scrn_gamma; +} +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand paletted images to RGB, expand grayscale images of + * less than 8-bit depth to 8-bit depth, and expand tRNS chunks + * to alpha channels. + */ +void PNGAPI +png_set_expand(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +} + +/* GRR 19990627: the following three functions currently are identical + * to png_set_expand(). However, it is entirely reasonable that someone + * might wish to expand an indexed image to RGB but *not* expand a single, + * fully transparent palette entry to a full alpha channel--perhaps instead + * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace + * the transparent color with a particular RGB value, or drop tRNS entirely. + * IOW, a future version of the library may make the transformations flag + * a bit more fine-grained, with separate bits for each of these three + * functions. + * + * More to the point, these functions make it obvious what libpng will be + * doing, whereas "expand" can (and does) mean any number of things. + * + * GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified + * to expand only the sample depth but not to expand the tRNS to alpha + * and its name was changed to png_set_expand_gray_1_2_4_to_8(). + */ + +/* Expand paletted images to RGB. */ +void PNGAPI +png_set_palette_to_rgb(png_structp png_ptr) +{ + png_debug(1, "in png_set_palette_to_rgb"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +} + +#ifndef PNG_1_0_X +/* Expand grayscale images of less than 8-bit depth to 8 bits. */ +void PNGAPI +png_set_expand_gray_1_2_4_to_8(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand_gray_1_2_4_to_8"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= PNG_EXPAND; + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +} +#endif + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Expand grayscale images of less than 8-bit depth to 8 bits. */ +/* Deprecated as of libpng-1.2.9 */ +void PNGAPI +png_set_gray_1_2_4_to_8(png_structp png_ptr) +{ + png_debug(1, "in png_set_gray_1_2_4_to_8"); + + if (png_ptr == NULL) + return; + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} +#endif + + +/* Expand tRNS chunks to alpha channels. */ +void PNGAPI +png_set_tRNS_to_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_tRNS_to_alpha"); + + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +} +#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */ + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +void PNGAPI +png_set_gray_to_rgb(png_structp png_ptr) +{ + png_debug(1, "in png_set_gray_to_rgb"); + + png_ptr->transformations |= PNG_GRAY_TO_RGB; + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +/* Convert a RGB image to a grayscale of the same width. This allows us, + * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image. + */ + +void PNGAPI +png_set_rgb_to_gray(png_structp png_ptr, int error_action, double red, + double green) +{ + int red_fixed, green_fixed; + if (png_ptr == NULL) + return; + if (red > 21474.83647 || red < -21474.83648 || + green > 21474.83647 || green < -21474.83648) + { + png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients"); + red_fixed = -1; + green_fixed = -1; + } + else + { + red_fixed = (int)((float)red*100000.0 + 0.5); + green_fixed = (int)((float)green*100000.0 + 0.5); + } + png_set_rgb_to_gray_fixed(png_ptr, error_action, red_fixed, green_fixed); +} +#endif + +void PNGAPI +png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action, + png_fixed_point red, png_fixed_point green) +{ + png_debug(1, "in png_set_rgb_to_gray"); + + if (png_ptr == NULL) + return; + + switch(error_action) + { + case 1: png_ptr->transformations |= PNG_RGB_TO_GRAY; + break; + + case 2: png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; + break; + + case 3: png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; + } + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#ifdef PNG_READ_EXPAND_SUPPORTED + png_ptr->transformations |= PNG_EXPAND; +#else + { + png_warning(png_ptr, + "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED."); + png_ptr->transformations &= ~PNG_RGB_TO_GRAY; + } +#endif + { + png_uint_16 red_int, green_int; + if (red < 0 || green < 0) + { + red_int = 6968; /* .212671 * 32768 + .5 */ + green_int = 23434; /* .715160 * 32768 + .5 */ + } + else if (red + green < 100000L) + { + red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L); + green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L); + } + else + { + png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients"); + red_int = 6968; + green_int = 23434; + } + png_ptr->rgb_to_gray_red_coeff = red_int; + png_ptr->rgb_to_gray_green_coeff = green_int; + png_ptr->rgb_to_gray_blue_coeff = + (png_uint_16)(32768 - red_int - green_int); + } +} +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +void PNGAPI +png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + read_user_transform_fn) +{ + png_debug(1, "in png_set_read_user_transform_fn"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->read_user_transform_fn = read_user_transform_fn; +#endif +#ifdef PNG_LEGACY_SUPPORTED + if (read_user_transform_fn) + png_warning(png_ptr, + "This version of libpng does not support user transforms"); +#endif +} +#endif + +/* Initialize everything needed for the read. This includes modifying + * the palette. + */ +void /* PRIVATE */ +png_init_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_init_read_transformations"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (png_ptr != NULL) +#endif + { +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_SHIFT_SUPPORTED) || \ + defined(PNG_READ_GAMMA_SUPPORTED) + int color_type = png_ptr->color_type; +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* Detect gray background and attempt to enable optimization + * for gray --> RGB case + * + * Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or + * RGB_ALPHA (in which case need_expand is superfluous anyway), the + * background color might actually be gray yet not be flagged as such. + * This is not a problem for the current code, which uses + * PNG_BACKGROUND_IS_GRAY only to decide when to do the + * png_do_gray_to_rgb() transformation. + */ + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + !(color_type & PNG_COLOR_MASK_COLOR)) + { + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + } else if ((png_ptr->transformations & PNG_BACKGROUND) && + !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_GRAY_TO_RGB) && + png_ptr->background.red == png_ptr->background.green && + png_ptr->background.red == png_ptr->background.blue) + { + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + png_ptr->background.gray = png_ptr->background.red; + } +#endif + + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_EXPAND)) + { + if (!(color_type & PNG_COLOR_MASK_COLOR)) /* i.e., GRAY or GRAY_ALPHA */ + { + /* Expand background and tRNS chunks */ + switch (png_ptr->bit_depth) + { + case 1: + png_ptr->background.gray *= (png_uint_16)0xff; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0xff; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + + case 2: + png_ptr->background.gray *= (png_uint_16)0x55; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0x55; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + + case 4: + png_ptr->background.gray *= (png_uint_16)0x11; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0x11; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + + case 8: + + case 16: + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + break; + } + } + else if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.red = + png_ptr->palette[png_ptr->background.index].red; + png_ptr->background.green = + png_ptr->palette[png_ptr->background.index].green; + png_ptr->background.blue = + png_ptr->palette[png_ptr->background.index].blue; + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_ALPHA) + { +#ifdef PNG_READ_EXPAND_SUPPORTED + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) +#endif + { + /* Invert the alpha channel (in tRNS) unless the pixels are + * going to be expanded, in which case leave it for later + */ + int i, istop; + istop=(int)png_ptr->num_trans; + for (i=0; itrans[i] = (png_byte)(255 - png_ptr->trans[i]); + } + } +#endif + + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + png_ptr->background_1 = png_ptr->background; +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + + if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0) + && (fabs(png_ptr->screen_gamma * png_ptr->gamma - 1.0) + < PNG_GAMMA_THRESHOLD)) + { + int i, k; + k=0; + for (i=0; inum_trans; i++) + { + if (png_ptr->trans[i] != 0 && png_ptr->trans[i] != 0xff) + { + k=1; /* Partial transparency is present */ + break; + } + } + if (k == 0) + png_ptr->transformations &= ~PNG_GAMMA; + } + + if ((png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY)) && + png_ptr->gamma != 0.0) + { + png_build_gamma_table(png_ptr); + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + /* Could skip if no transparency */ + png_color back, back_1; + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g, gs; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + default: + g = 1.0; /* back_1 */ + gs = 1.0; /* back */ + } + + if ( fabs(gs - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + } + else + { + back.red = (png_byte)(pow( + (double)png_ptr->background.red/255, gs) * 255.0 + .5); + back.green = (png_byte)(pow( + (double)png_ptr->background.green/255, gs) * 255.0 + + .5); + back.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255, gs) * 255.0 + .5); + } + + back_1.red = (png_byte)(pow( + (double)png_ptr->background.red/255, g) * 255.0 + .5); + back_1.green = (png_byte)(pow( + (double)png_ptr->background.green/255, g) * 255.0 + .5); + back_1.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255, g) * 255.0 + .5); + } + for (i = 0; i < num_palette; i++) + { + if (i < (int)png_ptr->num_trans && png_ptr->trans[i] != 0xff) + { + if (png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else /* if (png_ptr->trans[i] != 0xff) */ + { + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, png_ptr->trans[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, png_ptr->trans[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, png_ptr->trans[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + /* Prevent the transformations being done again, and make sure + * that the now spurious alpha channel is stripped - the code + * has just reduced background composition and gamma correction + * to a simple alpha channel strip. + */ + png_ptr->transformations &= ~PNG_BACKGROUND; + png_ptr->transformations &= ~PNG_GAMMA; + png_ptr->transformations |= PNG_STRIP_ALPHA; + } + /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ + else + /* color_type != PNG_COLOR_TYPE_PALETTE */ + { + double m = (double)(((png_uint_32)1 << png_ptr->bit_depth) - 1); + double g = 1.0; + double gs = 1.0; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + } + + png_ptr->background_1.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, g) * m + .5); + png_ptr->background.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, gs) * m + .5); + + if ((png_ptr->background.red != png_ptr->background.green) || + (png_ptr->background.red != png_ptr->background.blue) || + (png_ptr->background.red != png_ptr->background.gray)) + { + /* RGB or RGBA with color background */ + png_ptr->background_1.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, g) * m + .5); + png_ptr->background_1.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, g) * m + .5); + png_ptr->background_1.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, g) * m + .5); + png_ptr->background.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, gs) * m + .5); + png_ptr->background.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, gs) * m + .5); + png_ptr->background.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, gs) * m + .5); + } + else + { + /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */ + png_ptr->background_1.red = png_ptr->background_1.green + = png_ptr->background_1.blue = png_ptr->background_1.gray; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + } + } + } + else + /* Transformation does not include PNG_BACKGROUND */ +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + + /* Done the gamma correction. */ + png_ptr->transformations &= ~PNG_GAMMA; + } + } +#ifdef PNG_READ_BACKGROUND_SUPPORTED + else +#endif +#endif /* PNG_READ_GAMMA_SUPPORTED && PNG_FLOATING_POINT_SUPPORTED */ +#ifdef PNG_READ_BACKGROUND_SUPPORTED + /* No GAMMA transformation */ + if ((png_ptr->transformations & PNG_BACKGROUND) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + int i; + int istop = (int)png_ptr->num_trans; + png_color back; + png_colorp palette = png_ptr->palette; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < istop; i++) + { + if (png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else if (png_ptr->trans[i] != 0xff) + { + /* The png_composite() macro is defined in png.h */ + png_composite(palette[i].red, palette[i].red, + png_ptr->trans[i], back.red); + png_composite(palette[i].green, palette[i].green, + png_ptr->trans[i], back.green); + png_composite(palette[i].blue, palette[i].blue, + png_ptr->trans[i], back.blue); + } + } + + /* Handled alpha, still need to strip the channel. */ + png_ptr->transformations &= ~PNG_BACKGROUND; + png_ptr->transformations |= PNG_STRIP_ALPHA; + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + +#ifdef PNG_READ_SHIFT_SUPPORTED + if ((png_ptr->transformations & PNG_SHIFT) && + !(png_ptr->transformations & PNG_EXPAND) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + png_uint_16 i; + png_uint_16 istop = png_ptr->num_palette; + int sr = 8 - png_ptr->sig_bit.red; + int sg = 8 - png_ptr->sig_bit.green; + int sb = 8 - png_ptr->sig_bit.blue; + + if (sr < 0 || sr > 8) + sr = 0; + if (sg < 0 || sg > 8) + sg = 0; + if (sb < 0 || sb > 8) + sb = 0; + for (i = 0; i < istop; i++) + { + png_ptr->palette[i].red >>= sr; + png_ptr->palette[i].green >>= sg; + png_ptr->palette[i].blue >>= sb; + } + + png_ptr->transformations &= ~PNG_SHIFT; + } +#endif /* PNG_READ_SHIFT_SUPPORTED */ + } +#if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \ + && !defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr) + return; +#endif +} + +/* Modify the info structure to reflect the transformations. The + * info should be updated so a PNG file could be written with it, + * assuming the transformations result in valid PNG data. + */ +void /* PRIVATE */ +png_read_transform_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_transform_info"); + +#ifdef PNG_READ_EXPAND_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans) + info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + else + info_ptr->color_type = PNG_COLOR_TYPE_RGB; + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + else + { + if (png_ptr->num_trans) + { + if (png_ptr->transformations & PNG_EXPAND_tRNS) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; + } + if (info_ptr->bit_depth < 8) + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + } +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + if (png_ptr->transformations & PNG_BACKGROUND) + { + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; + info_ptr->num_trans = 0; + info_ptr->background = png_ptr->background; + } +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED + if (png_ptr->transformations & PNG_GAMMA) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = png_ptr->gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = png_ptr->int_gamma; +#endif + } +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED + if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16)) + info_ptr->bit_depth = 8; +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + info_ptr->color_type |= PNG_COLOR_MASK_COLOR; +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR; +#endif + +#ifdef PNG_READ_DITHER_SUPPORTED + if (png_ptr->transformations & PNG_DITHER) + { + if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && + png_ptr->palette_lookup && info_ptr->bit_depth == 8) + { + info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; + } + } +#endif + +#ifdef PNG_READ_PACK_SUPPORTED + if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8)) + info_ptr->bit_depth = 8; +#endif + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; +#endif + + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + +#ifdef PNG_READ_FILLER_SUPPORTED + /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ + if ((png_ptr->transformations & PNG_FILLER) && + ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_GRAY))) + { + info_ptr->channels++; + /* If adding a true alpha channel not just filler */ +#ifndef PNG_1_0_X + if (png_ptr->transformations & PNG_ADD_ALPHA) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; +#endif + } +#endif + +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ +defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if (info_ptr->bit_depth < png_ptr->user_transform_depth) + info_ptr->bit_depth = png_ptr->user_transform_depth; + if (info_ptr->channels < png_ptr->user_transform_channels) + info_ptr->channels = png_ptr->user_transform_channels; + } +#endif + + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * + info_ptr->bit_depth); + + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width); + +#ifndef PNG_READ_EXPAND_SUPPORTED + if (png_ptr) + return; +#endif +} + +/* Transform the row. The order of transformations is significant, + * and is very touchy. If you add a transformation, take care to + * decide how it fits in with the other transformations here. + */ +void /* PRIVATE */ +png_do_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_read_transformations"); + + if (png_ptr->row_buf == NULL) + { +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + char msg[50]; + + png_snprintf2(msg, 50, + "NULL row buffer for row %ld, pass %d", (long)png_ptr->row_number, + png_ptr->pass); + png_error(png_ptr, msg); +#else + png_error(png_ptr, "NULL row buffer"); +#endif + } +#ifdef PNG_WARN_UNINITIALIZED_ROW + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + /* Application has failed to call either png_read_start_image() + * or png_read_update_info() after setting transforms that expand + * pixels. This check added to libpng-1.2.19 + */ +#if (PNG_WARN_UNINITIALIZED_ROW==1) + png_error(png_ptr, "Uninitialized row"); +#else + png_warning(png_ptr, "Uninitialized row"); +#endif +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->palette == NULL) + png_error (png_ptr, "Palette is NULL in indexed image"); + + png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette, png_ptr->trans, png_ptr->num_trans); + } + else + { + if (png_ptr->num_trans && + (png_ptr->transformations & PNG_EXPAND_tRNS)) + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_values)); + else + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + NULL); + } + } +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + PNG_FLAG_FILLER_AFTER | (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + { + int rgb_error = + png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1); + if (rgb_error) + { + png_ptr->rgb_to_gray_status=1; + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_WARN) + png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_ERR) + png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + } + } +#endif + +/* From Andreas Dilger e-mail to png-implement, 26 March 1998: + * + * In most cases, the "simple transparency" should be done prior to doing + * gray-to-RGB, or you will have to test 3x as many bytes to check if a + * pixel is transparent. You would also need to make sure that the + * transparency information is upgraded to RGB. + * + * To summarize, the current flow is: + * - Gray + simple transparency -> compare 1 or 2 gray bytes and composite + * with background "in place" if transparent, + * convert to RGB if necessary + * - Gray + alpha -> composite with gray background and remove alpha bytes, + * convert to RGB if necessary + * + * To support RGB backgrounds for gray images we need: + * - Gray + simple transparency -> convert to RGB + simple transparency, + * compare 3 or 6 bytes and composite with + * background "in place" if transparent + * (3x compare/pixel compared to doing + * composite with gray bkgrnd) + * - Gray + alpha -> convert to RGB + alpha, composite with background and + * remove alpha bytes (3x float + * operations/pixel compared with composite + * on gray background) + * + * Greg's change will do this. The reason it wasn't done before is for + * performance, as this increases the per-pixel operations. If we would check + * in advance if the background was gray or RGB, and position the gray-to-RGB + * transform appropriately, then it would save a lot of work/time. + */ + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* If gray -> RGB, do so now only if background is non-gray; else do later + * for performance reasons + */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + if ((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0 ) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) + png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_values), &(png_ptr->background) +#ifdef PNG_READ_GAMMA_SUPPORTED + , &(png_ptr->background_1), + png_ptr->gamma_table, png_ptr->gamma_from_1, + png_ptr->gamma_to_1, png_ptr->gamma_16_table, + png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1, + png_ptr->gamma_shift +#endif +); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED + if ((png_ptr->transformations & PNG_GAMMA) && +#ifdef PNG_READ_BACKGROUND_SUPPORTED + !((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) && +#endif + (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) + png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->gamma_table, png_ptr->gamma_16_table, + png_ptr->gamma_shift); +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED + if (png_ptr->transformations & PNG_16_TO_8) + png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_DITHER_SUPPORTED + if (png_ptr->transformations & PNG_DITHER) + { + png_do_dither((png_row_infop)&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette_lookup, png_ptr->dither_index); + if (png_ptr->row_info.rowbytes == (png_uint_32)0) + png_error(png_ptr, "png_do_dither returned rowbytes=0"); + } +#endif + +#ifdef PNG_READ_INVERT_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED + if (png_ptr->transformations & PNG_SHIFT) + png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif + +#ifdef PNG_READ_PACK_SUPPORTED + if (png_ptr->transformations & PNG_PACK) + png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_BGR_SUPPORTED + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* If gray -> RGB, do so now only if we did not do so above */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + (png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED + if (png_ptr->transformations & PNG_FILLER) + png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->filler, png_ptr->flags); +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_SWAP_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if (png_ptr->read_user_transform_fn != NULL) + (*(png_ptr->read_user_transform_fn)) /* User read transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + if (png_ptr->user_transform_depth) + png_ptr->row_info.bit_depth = png_ptr->user_transform_depth; + if (png_ptr->user_transform_channels) + png_ptr->row_info.channels = png_ptr->user_transform_channels; +#endif + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + } +#endif + +} + +#ifdef PNG_READ_PACK_SUPPORTED +/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, + * without changing the actual values. Thus, if you had a row with + * a bit depth of 1, you would end up with bytes that only contained + * the numbers 0 or 1. If you would rather they contain 0 and 255, use + * png_do_shift() after this. + */ +void /* PRIVATE */ +png_do_unpack(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_unpack"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL && row_info->bit_depth < 8) +#else + if (row_info->bit_depth < 8) +#endif + { + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + switch (row_info->bit_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x01); + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + + case 2: + { + + png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x03); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + + case 4: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x0f); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_width * row_info->channels; + } +} +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED +/* Reverse the effects of png_do_shift. This routine merely shifts the + * pixels back to their significant bits values. Thus, if you have + * a row of bit depth 8, but only 5 are significant, this will shift + * the values back to 0 through 31. + */ +void /* PRIVATE */ +png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits) +{ + png_debug(1, "in png_do_unshift"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && sig_bits != NULL && +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift[4]; + int channels = 0; + int c; + png_uint_16 value = 0; + png_uint_32 row_width = row_info->width; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift[channels++] = row_info->bit_depth - sig_bits->red; + shift[channels++] = row_info->bit_depth - sig_bits->green; + shift[channels++] = row_info->bit_depth - sig_bits->blue; + } + else + { + shift[channels++] = row_info->bit_depth - sig_bits->gray; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift[channels++] = row_info->bit_depth - sig_bits->alpha; + } + + for (c = 0; c < channels; c++) + { + if (shift[c] <= 0) + shift[c] = 0; + else + value = 1; + } + + if (!value) + return; + + switch (row_info->bit_depth) + { + case 2: + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (bp = row, i = 0; i < istop; i++) + { + *bp >>= 1; + *bp++ &= 0x55; + } + break; + } + + case 4: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) | + (png_byte)((int)0xf >> shift[0])); + + for (i = 0; i < istop; i++) + { + *bp >>= shift[0]; + *bp++ &= mask; + } + break; + } + + case 8: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_width * channels; + + for (i = 0; i < istop; i++) + { + *bp++ >>= shift[i%channels]; + } + break; + } + + case 16: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_width; + + for (i = 0; i < istop; i++) + { + value = (png_uint_16)((*bp << 8) + *(bp + 1)); + value >>= shift[i%channels]; + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + break; + } + } + } +} +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED +/* Chop rows of bit depth 16 down to 8 */ +void /* PRIVATE */ +png_do_chop(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_chop"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL && row_info->bit_depth == 16) +#else + if (row_info->bit_depth == 16) +#endif + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + png_uint_32 istop = row_info->width * row_info->channels; + + for (i = 0; i> 8)) >> 8; + * + * Approximate calculation with shift/add instead of multiply/divide: + * *dp = ((((png_uint_32)(*sp) << 8) | + * (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8; + * + * What we actually do to avoid extra shifting and conversion: + */ + + *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0); +#else + /* Simply discard the low order byte */ + *dp = *sp; +#endif + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_info->width * row_info->channels; + } +} +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_swap_alpha"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL) +#endif + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from RGBA to ARGB */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from RRGGBBAA to AARRGGBB */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from GA to AG */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from GGAA to AAGG */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + } +} +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_invert_alpha"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL) +#endif + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=3; + dp=sp; + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=6; + dp=sp; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = *(--sp); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); +/* + *(--dp) = *(--sp); + *(--dp) = *(--sp); +*/ + sp-=2; + dp=sp; + } + } + } + } +} +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED +/* Add filler channel if we have RGB color */ +void /* PRIVATE */ +png_do_read_filler(png_row_infop row_info, png_bytep row, + png_uint_32 filler, png_uint_32 flags) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_byte hi_filler = (png_byte)((filler>>8) & 0xff); + png_byte lo_filler = (png_byte)(filler & 0xff); + + png_debug(1, "in png_do_read_filler"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + /* This changes the data from G to GX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + /* This changes the data from G to XG */ + else + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + } + else if (row_info->bit_depth == 16) + { + /* This changes the data from GG to GGXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from GG to XXGG */ + else + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + } /* COLOR_TYPE == GRAY */ + else if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if (row_info->bit_depth == 8) + { + /* This changes the data from RGB to RGBX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from RGB to XRGB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + else if (row_info->bit_depth == 16) + { + /* This changes the data from RRGGBB to RRGGBBXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + /* This changes the data from RRGGBB to XXRRGGBB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + } + } /* COLOR_TYPE == RGB */ +} +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand grayscale files to RGB, with or without alpha */ +void /* PRIVATE */ +png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_debug(1, "in png_do_gray_to_rgb"); + + if (row_info->bit_depth >= 8 && +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + !(row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 4 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + row_info->channels += (png_byte)2; + row_info->color_type |= PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB files to grayscale, with or without alpha + * using the equation given in Poynton's ColorFAQ at + * (THIS LINK IS DEAD June 2008) + * New link: + * + * Charles Poynton poynton at poynton.com + * + * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + * + * We approximate this with + * + * Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + * + * which can be expressed with integers as + * + * Y = (6969 * R + 23434 * G + 2365 * B)/32768 + * + * The calculation is to be done in a linear colorspace. + * + * Other integer coefficents can be used via png_set_rgb_to_gray(). + */ +int /* PRIVATE */ +png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row) + +{ + png_uint_32 i; + + png_uint_32 row_width = row_info->width; + int rgb_error = 0; + + png_debug(1, "in png_do_rgb_to_gray"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; + png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; + png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if (red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1[ + (rc*red + gc*green + bc*blue)>>15]; + } + else + *(dp++) = *(sp - 1); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if (red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + } + else + *(dp++) = *(sp - 1); + } + } + } + + else /* RGB bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if (red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = + png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + + bc*blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if (red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + } + } + } + } + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if (red != green || red != blue) + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1 + [(rc*red + gc*green + bc*blue)>>15]; + *(dp++) = *(sp++); /* alpha */ + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if (red != green || red != blue) + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = *(sp++); /* alpha */ + } + } + } + else /* RGBA bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if (red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = + png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc * red_1 + + gc * green_1 + bc * blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + red = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + if (red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + } + } + row_info->channels -= (png_byte)2; + row_info->color_type &= ~PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + return rgb_error; +} +#endif + +/* Build a grayscale palette. Palette is assumed to be 1 << bit_depth + * large of png_color. This lets grayscale images be treated as + * paletted. Most useful for gamma correction and simplification + * of code. + */ +void PNGAPI +png_build_grayscale_palette(int bit_depth, png_colorp palette) +{ + int num_palette; + int color_inc; + int i; + int v; + + png_debug(1, "in png_do_build_grayscale_palette"); + + if (palette == NULL) + return; + + switch (bit_depth) + { + case 1: + num_palette = 2; + color_inc = 0xff; + break; + + case 2: + num_palette = 4; + color_inc = 0x55; + break; + + case 4: + num_palette = 16; + color_inc = 0x11; + break; + + case 8: + num_palette = 256; + color_inc = 1; + break; + + default: + num_palette = 0; + color_inc = 0; + break; + } + + for (i = 0, v = 0; i < num_palette; i++, v += color_inc) + { + palette[i].red = (png_byte)v; + palette[i].green = (png_byte)v; + palette[i].blue = (png_byte)v; + } +} + +/* This function is currently unused. Do we really need it? */ +#if defined(PNG_READ_DITHER_SUPPORTED) && \ + defined(PNG_CORRECT_PALETTE_SUPPORTED) +void /* PRIVATE */ +png_correct_palette(png_structp png_ptr, png_colorp palette, + int num_palette) +{ + png_debug(1, "in png_correct_palette"); + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_READ_GAMMA_SUPPORTED) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) + if (png_ptr->transformations & (PNG_GAMMA | PNG_BACKGROUND)) + { + png_color back, back_1; + + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g; + + g = 1.0 / (png_ptr->background_gamma * png_ptr->screen_gamma); + + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_SCREEN + || fabs(g - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = png_ptr->background.red; + back.green = png_ptr->background.green; + back.blue = png_ptr->background.blue; + } + else + { + back.red = + (png_byte)(pow((double)png_ptr->background.red/255, g) * + 255.0 + 0.5); + back.green = + (png_byte)(pow((double)png_ptr->background.green/255, g) * + 255.0 + 0.5); + back.blue = + (png_byte)(pow((double)png_ptr->background.blue/255, g) * + 255.0 + 0.5); + } + + g = 1.0 / png_ptr->background_gamma; + + back_1.red = + (png_byte)(pow((double)png_ptr->background.red/255, g) * + 255.0 + 0.5); + back_1.green = + (png_byte)(pow((double)png_ptr->background.green/255, g) * + 255.0 + 0.5); + back_1.blue = + (png_byte)(pow((double)png_ptr->background.blue/255, g) * + 255.0 + 0.5); + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_uint_32 i; + + for (i = 0; i < (png_uint_32)num_palette; i++) + { + if (i < png_ptr->num_trans && png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else if (i < png_ptr->num_trans && png_ptr->trans[i] != 0xff) + { + png_byte v, w; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].red]; + png_composite(w, v, png_ptr->trans[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].green]; + png_composite(w, v, png_ptr->trans[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].blue]; + png_composite(w, v, png_ptr->trans[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + else + { + int i; + + for (i = 0; i < num_palette; i++) + { + if (palette[i].red == (png_byte)png_ptr->trans_values.gray) + { + palette[i] = back; + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + } + else +#endif +#ifdef PNG_READ_GAMMA_SUPPORTED + if (png_ptr->transformations & PNG_GAMMA) + { + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } +#ifdef PNG_READ_BACKGROUND_SUPPORTED + else +#endif +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_color back; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < (int)png_ptr->num_trans; i++) + { + if (png_ptr->trans[i] == 0) + { + palette[i].red = back.red; + palette[i].green = back.green; + palette[i].blue = back.blue; + } + else if (png_ptr->trans[i] != 0xff) + { + png_composite(palette[i].red, png_ptr->palette[i].red, + png_ptr->trans[i], back.red); + png_composite(palette[i].green, png_ptr->palette[i].green, + png_ptr->trans[i], back.green); + png_composite(palette[i].blue, png_ptr->palette[i].blue, + png_ptr->trans[i], back.blue); + } + } + } + else /* Assume grayscale palette (what else could it be?) */ + { + int i; + + for (i = 0; i < num_palette; i++) + { + if (i == (png_byte)png_ptr->trans_values.gray) + { + palette[i].red = (png_byte)png_ptr->background.red; + palette[i].green = (png_byte)png_ptr->background.green; + palette[i].blue = (png_byte)png_ptr->background.blue; + } + } + } + } +#endif +} +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Replace any alpha or transparency with the supplied background color. + * "background" is already in the screen gamma, while "background_1" is + * at a gamma of 1.0. Paletted files have already been taken care of. + */ +void /* PRIVATE */ +png_do_background(png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background +#ifdef PNG_READ_GAMMA_SUPPORTED + , png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift +#endif + ) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + int shift; + + png_debug(1, "in png_do_background"); + + if (background != NULL && +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) || + (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_values))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_GRAY: + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row; + shift = 7; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x01) + == trans_values->gray) + { + *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 7; + sp++; + } + else + shift--; + } + break; + } + + case 2: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_values->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x03); + png_byte g = (png_byte)((gamma_table [p | (p << 2) | + (p << 4) | (p << 6)] >> 6) & 0x03); + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + else +#endif + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_values->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + break; + } + + case 4: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_values->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x0f); + png_byte g = (png_byte)((gamma_table[p | + (p << 4)] >> 4) & 0x0f); + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + else +#endif + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_values->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + break; + } + + case 8: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_values->gray) + { + *sp = (png_byte)background->gray; + } + else + { + *sp = gamma_table[*sp]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_values->gray) + { + *sp = (png_byte)background->gray; + } + } + } + break; + } + + case 16: + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_values->gray) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + else + { + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_values->gray) + { + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + } + } + break; + } + } + break; + } + + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_values->red && + *(sp + 1) == trans_values->green && + *(sp + 2) == trans_values->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + else + { + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_values->red && + *(sp + 1) == trans_values->green && + *(sp + 2) == trans_values->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + if (r == trans_values->red && g == trans_values->green && + b == trans_values->blue) + { + /* Background is already in screen gamma */ + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp+1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + + if (r == trans_values->red && g == trans_values->green && + b == trans_values->blue) + { + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + } + } + } + break; + } + + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_uint_16 a = *(sp + 1); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + } + else if (a == 0) + { + /* Background is already in screen gamma */ + *dp = (png_byte)background->gray; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->gray); + *dp = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_byte a = *(sp + 1); + + if (a == 0xff) + { + *dp = *sp; + } +#ifdef PNG_READ_GAMMA_SUPPORTED + else if (a == 0) + { + *dp = (png_byte)background->gray; + } + else + { + png_composite(*dp, *sp, a, background_1->gray); + } +#else + *dp = (png_byte)background->gray; +#endif + } + } + } + else /* if (png_ptr->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#ifdef PNG_READ_GAMMA_SUPPORTED + else if (a == 0) +#else + else +#endif + { + /* Background is already in screen gamma */ + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#ifdef PNG_READ_GAMMA_SUPPORTED + else + { + png_uint_16 g, v, w; + + g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(v, g, a, background_1->gray); + w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8]; + *dp = (png_byte)((w >> 8) & 0xff); + *(dp + 1) = (png_byte)(w & 0xff); + } +#endif + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 2); + } +#ifdef PNG_READ_GAMMA_SUPPORTED + else if (a == 0) +#else + else +#endif + { + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#ifdef PNG_READ_GAMMA_SUPPORTED + else + { + png_uint_16 g, v; + + g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_composite_16(v, g, a, background_1->gray); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#endif + } + } + } + break; + } + + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + *(dp + 1) = gamma_table[*(sp + 1)]; + *(dp + 2) = gamma_table[*(sp + 2)]; + } + else if (a == 0) + { + /* Background is already in screen gamma */ + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->red); + *dp = gamma_from_1[w]; + v = gamma_to_1[*(sp + 1)]; + png_composite(w, v, a, background_1->green); + *(dp + 1) = gamma_from_1[w]; + v = gamma_to_1[*(sp + 2)]; + png_composite(w, v, a, background_1->blue); + *(dp + 2) = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = *sp; + *(dp + 1) = *(sp + 1); + *(dp + 2) = *(sp + 2); + } + else if (a == 0) + { + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_composite(*dp, *sp, a, background->red); + png_composite(*(dp + 1), *(sp + 1), a, + background->green); + png_composite(*(dp + 2), *(sp + 2), a, + background->blue); + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#ifdef PNG_READ_GAMMA_SUPPORTED + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + else if (a == 0) + { + /* Background is already in screen gamma */ + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v, w, x; + + v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(w, v, a, background_1->red); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *dp = (png_byte)((x >> 8) & 0xff); + *(dp + 1) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; + png_composite_16(w, v, a, background_1->green); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *(dp + 2) = (png_byte)((x >> 8) & 0xff); + *(dp + 3) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; + png_composite_16(w, v, a, background_1->blue); + x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8]; + *(dp + 4) = (png_byte)((x >> 8) & 0xff); + *(dp + 5) = (png_byte)(x & 0xff); + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 6); + } + else if (a == 0) + { + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v; + + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + png_composite_16(v, r, a, background->red); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + png_composite_16(v, g, a, background->green); + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + png_composite_16(v, b, a, background->blue); + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + } + } + } + break; + } + } + + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + row_info->channels--; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + } +} +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* Gamma correct the image, avoiding the alpha channel. Make sure + * you do this after you deal with the transparency issue on grayscale + * or RGB images. If your bit depth is 8, use gamma_table, if it + * is 16, use gamma_16_table and gamma_shift. Build these with + * build_gamma_table(). + */ +void /* PRIVATE */ +png_do_gamma(png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift) +{ + png_bytep sp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_gamma"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + ((row_info->bit_depth <= 8 && gamma_table != NULL) || + (row_info->bit_depth == 16 && gamma_16_table != NULL))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp += 2; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + + case PNG_COLOR_TYPE_GRAY: + { + if (row_info->bit_depth == 2) + { + sp = row; + for (i = 0; i < row_width; i += 4) + { + int a = *sp & 0xc0; + int b = *sp & 0x30; + int c = *sp & 0x0c; + int d = *sp & 0x03; + + *sp = (png_byte)( + ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| + ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| + ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| + ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); + sp++; + } + } + + if (row_info->bit_depth == 4) + { + sp = row; + for (i = 0; i < row_width; i += 2) + { + int msb = *sp & 0xf0; + int lsb = *sp & 0x0f; + + *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0) + | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); + sp++; + } + } + + else if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + } + } + + else if (row_info->bit_depth == 16) + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + } + } +} +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expands a palette row to an RGB or RGBA row depending + * upon whether you supply trans and num_trans. + */ +void /* PRIVATE */ +png_do_expand_palette(png_row_infop row_info, png_bytep row, + png_colorp palette, png_bytep trans, int num_trans) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand_palette"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + row_info->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 1; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + + case 2: + { + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)value; + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + + case 4: + { + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((row_width & 0x01) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)value; + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift += 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + switch (row_info->bit_depth) + { + case 8: + { + if (trans != NULL) + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + + for (i = 0; i < row_width; i++) + { + if ((int)(*sp) >= num_trans) + *dp-- = 0xff; + else + *dp-- = trans[*sp]; + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + row_info->color_type = 6; + row_info->channels = 4; + } + else + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width * 3) - 1; + + for (i = 0; i < row_width; i++) + { + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + + row_info->bit_depth = 8; + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + row_info->color_type = 2; + row_info->channels = 3; + } + break; + } + } + } +} + +/* If the bit depth < 8, it is expanded to 8. Also, if the already + * expanded transparency value is supplied, an alpha channel is built. + */ +void /* PRIVATE */ +png_do_expand(png_row_infop row_info, png_bytep row, + png_color_16p trans_value) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0); + + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + gray = (png_uint_16)((gray&0x01)*0xff); + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 0xff; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + + case 2: + { + gray = (png_uint_16)((gray&0x03)*0x55); + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)(value | (value << 2) | (value << 4) | + (value << 6)); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + + case 4: + { + gray = (png_uint_16)((gray&0x0f)*0x11); + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)(value | (value << 4)); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + + if (trans_value != NULL) + { + if (row_info->bit_depth == 8) + { + gray = gray & 0xff; + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*sp == gray) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + } + } + + else if (row_info->bit_depth == 16) + { + png_byte gray_high = (gray >> 8) & 0xff; + png_byte gray_low = gray & 0xff; + sp = row + row_info->rowbytes - 1; + dp = row + (row_info->rowbytes << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 1) == gray_high && *(sp) == gray_low) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + } + } + + row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + row_info->channels = 2; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_width); + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value) + { + if (row_info->bit_depth == 8) + { + png_byte red = trans_value->red & 0xff; + png_byte green = trans_value->green & 0xff; + png_byte blue = trans_value->blue & 0xff; + sp = row + (png_size_t)row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + png_byte red_high = (trans_value->red >> 8) & 0xff; + png_byte green_high = (trans_value->green >> 8) & 0xff; + png_byte blue_high = (trans_value->blue >> 8) & 0xff; + png_byte red_low = trans_value->red & 0xff; + png_byte green_low = trans_value->green & 0xff; + png_byte blue_low = trans_value->blue & 0xff; + sp = row + row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 3) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 5) == red_high && + *(sp - 4) == red_low && + *(sp - 3) == green_high && + *(sp - 2) == green_low && + *(sp - 1) == blue_high && + *(sp ) == blue_low) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + row_info->channels = 4; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + } +} +#endif + +#ifdef PNG_READ_DITHER_SUPPORTED +void /* PRIVATE */ +png_do_dither(png_row_infop row_info, png_bytep row, + png_bytep palette_lookup, png_bytep dither_lookup) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_dither"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB && + palette_lookup && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + + /* This looks real messy, but the compiler will reduce + * it down to a reasonable formula. For example, with + * 5 bits per color, we get: + * p = (((r >> 3) & 0x1f) << 10) | + * (((g >> 3) & 0x1f) << 5) | + * ((b >> 3) & 0x1f); + */ + p = (((r >> (8 - PNG_DITHER_RED_BITS)) & + ((1 << PNG_DITHER_RED_BITS) - 1)) << + (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | + (((g >> (8 - PNG_DITHER_GREEN_BITS)) & + ((1 << PNG_DITHER_GREEN_BITS) - 1)) << + (PNG_DITHER_BLUE_BITS)) | + ((b >> (8 - PNG_DITHER_BLUE_BITS)) & + ((1 << PNG_DITHER_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + palette_lookup != NULL && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + sp++; + + p = (((r >> (8 - PNG_DITHER_RED_BITS)) & + ((1 << PNG_DITHER_RED_BITS) - 1)) << + (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | + (((g >> (8 - PNG_DITHER_GREEN_BITS)) & + ((1 << PNG_DITHER_GREEN_BITS) - 1)) << + (PNG_DITHER_BLUE_BITS)) | + ((b >> (8 - PNG_DITHER_BLUE_BITS)) & + ((1 << PNG_DITHER_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && + dither_lookup && row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + *sp = dither_lookup[*sp]; + } + } + } +} +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +#ifdef PNG_READ_GAMMA_SUPPORTED +static PNG_CONST int png_gamma_shift[] = + {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0, 0x00}; + +/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit + * tables, we don't make a full table if we are reducing to 8-bit in + * the future. Note also how the gamma_16 tables are segmented so that + * we don't need to allocate > 64K chunks for a full 16-bit table. + * + * See the PNG extensions document for an integer algorithm for creating + * the gamma tables. Maybe we will implement that here someday. + * + * We should only reach this point if + * + * the file_gamma is known (i.e., the gAMA or sRGB chunk is present, + * or the application has provided a file_gamma) + * + * AND + * { + * the screen_gamma is known + * OR + * + * RGB_to_gray transformation is being performed + * } + * + * AND + * { + * the screen_gamma is different from the reciprocal of the + * file_gamma by more than the specified threshold + * + * OR + * + * a background color has been specified and the file_gamma + * and screen_gamma are not 1.0, within the specified threshold. + * } + */ + +void /* PRIVATE */ +png_build_gamma_table(png_structp png_ptr) +{ + png_debug(1, "in png_build_gamma_table"); + + if (png_ptr->bit_depth <= 8) + { + int i; + double g; + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + + else + g = 1.0; + + png_ptr->gamma_table = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_to_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + + + png_ptr->gamma_from_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + if (png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + + else + g = png_ptr->gamma; /* Probably doing rgb_to_gray */ + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } + else + { + double g; + int i, j, shift, num; + int sig_bit; + png_uint_32 ig; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit = (int)png_ptr->sig_bit.red; + + if ((int)png_ptr->sig_bit.green > sig_bit) + sig_bit = png_ptr->sig_bit.green; + + if ((int)png_ptr->sig_bit.blue > sig_bit) + sig_bit = png_ptr->sig_bit.blue; + } + else + { + sig_bit = (int)png_ptr->sig_bit.gray; + } + + if (sig_bit > 0) + shift = 16 - sig_bit; + + else + shift = 0; + + if (png_ptr->transformations & PNG_16_TO_8) + { + if (shift < (16 - PNG_MAX_GAMMA_8)) + shift = (16 - PNG_MAX_GAMMA_8); + } + + if (shift > 8) + shift = 8; + + if (shift < 0) + shift = 0; + + png_ptr->gamma_shift = (png_byte)shift; + + num = (1 << (8 - shift)); + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + else + g = 1.0; + + png_ptr->gamma_16_table = (png_uint_16pp)png_calloc(png_ptr, + (png_uint_32)(num * png_sizeof(png_uint_16p))); + + if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND)) + { + double fin, fout; + png_uint_32 last, max; + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof(png_uint_16))); + } + + g = 1.0 / g; + last = 0; + for (i = 0; i < 256; i++) + { + fout = ((double)i + 0.5) / 256.0; + fin = pow(fout, g); + max = (png_uint_32)(fin * (double)((png_uint_32)num << 8)); + while (last <= max) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)( + (png_uint_16)i | ((png_uint_16)i << 8)); + last++; + } + } + while (last < ((png_uint_32)num << 8)) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)65535L; + last++; + } + } + else + { + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof(png_uint_16))); + + ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4); + + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_table[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_16_to_1 = (png_uint_16pp)png_calloc(png_ptr, + (png_uint_32)(num * png_sizeof(png_uint_16p ))); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_to_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof(png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_to_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + + if (png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + + else + g = png_ptr->gamma; /* Probably doing rgb_to_gray */ + + png_ptr->gamma_16_from_1 = (png_uint_16pp)png_calloc(png_ptr, + (png_uint_32)(num * png_sizeof(png_uint_16p))); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_from_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof(png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_from_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } +} +#endif +/* To do: install integer version of png_build_gamma_table here */ +#endif + +#ifdef PNG_MNG_FEATURES_SUPPORTED +/* Undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_read_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_intrapixel"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((256 + *rp + *(rp+1))&0xff); + *(rp+2) = (png_byte)((256 + *(rp+2) + *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); + png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); + png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); + png_uint_32 red = (png_uint_32)((s0 + s1 + 65536L) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2 + s1 + 65536L) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngrutil.c b/bazaar/plugin/gdal/frmts/png/libpng/pngrutil.c new file mode 100644 index 000000000..707bc1b65 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngrutil.c @@ -0,0 +1,3388 @@ + +/* pngrutil.c - utilities to read a PNG file + * + * Last changed in libpng 1.2.51 [February 6, 2014] + * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file contains routines that are only called from within + * libpng itself during the course of reading an image. + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_READ_SUPPORTED + +#if defined(_WIN32_WCE) && (_WIN32_WCE<0x500) +# define WIN32_WCE_OLD +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +# ifdef WIN32_WCE_OLD +/* The strtod() function is not supported on WindowsCE */ +__inline double png_strtod(png_structp png_ptr, PNG_CONST char *nptr, + char **endptr) +{ + double result = 0; + int len; + wchar_t *str, *end; + + len = MultiByteToWideChar(CP_ACP, 0, nptr, -1, NULL, 0); + str = (wchar_t *)png_malloc(png_ptr, len * png_sizeof(wchar_t)); + if ( NULL != str ) + { + MultiByteToWideChar(CP_ACP, 0, nptr, -1, str, len); + result = wcstod(str, &end); + len = WideCharToMultiByte(CP_ACP, 0, end, -1, NULL, 0, NULL, NULL); + *endptr = (char *)nptr + (png_strlen(nptr) - len + 1); + png_free(png_ptr, str); + } + return result; +} +# else +# define png_strtod(p,a,b) strtod(a,b) +# endif +#endif + +png_uint_32 PNGAPI +png_get_uint_31(png_structp png_ptr, png_bytep buf) +{ +#ifdef PNG_READ_BIG_ENDIAN_SUPPORTED + png_uint_32 i = png_get_uint_32(buf); +#else + /* Avoid an extra function call by inlining the result. */ + png_uint_32 i = ((png_uint_32)(*buf) << 24) + + ((png_uint_32)(*(buf + 1)) << 16) + + ((png_uint_32)(*(buf + 2)) << 8) + + (png_uint_32)(*(buf + 3)); +#endif + if (i > PNG_UINT_31_MAX) + png_error(png_ptr, "PNG unsigned integer out of range."); + return (i); +} +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */ +png_uint_32 PNGAPI +png_get_uint_32(png_bytep buf) +{ + png_uint_32 i = ((png_uint_32)(*buf) << 24) + + ((png_uint_32)(*(buf + 1)) << 16) + + ((png_uint_32)(*(buf + 2)) << 8) + + (png_uint_32)(*(buf + 3)); + + return (i); +} + +/* Grab a signed 32-bit integer from a buffer in big-endian format. The + * data is stored in the PNG file in two's complement format, and it is + * assumed that the machine format for signed integers is the same. + */ +png_int_32 PNGAPI +png_get_int_32(png_bytep buf) +{ + png_int_32 i = ((png_int_32)(*buf) << 24) + + ((png_int_32)(*(buf + 1)) << 16) + + ((png_int_32)(*(buf + 2)) << 8) + + (png_int_32)(*(buf + 3)); + + return (i); +} + +/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */ +png_uint_16 PNGAPI +png_get_uint_16(png_bytep buf) +{ + png_uint_16 i = (png_uint_16)(((png_uint_16)(*buf) << 8) + + (png_uint_16)(*(buf + 1))); + + return (i); +} +#endif /* PNG_READ_BIG_ENDIAN_SUPPORTED */ + +/* Read the chunk header (length + type name). + * Put the type name into png_ptr->chunk_name, and return the length. + */ +png_uint_32 /* PRIVATE */ +png_read_chunk_header(png_structp png_ptr) +{ + png_byte buf[8]; + png_uint_32 length; + + /* Read the length and the chunk name */ + png_read_data(png_ptr, buf, 8); + length = png_get_uint_31(png_ptr, buf); + + /* Put the chunk name into png_ptr->chunk_name */ + png_memcpy(png_ptr->chunk_name, buf + 4, 4); + + png_debug2(0, "Reading %s chunk, length = %lu", + png_ptr->chunk_name, length); + + /* Reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, png_ptr->chunk_name, 4); + + /* Check to see if chunk name is valid */ + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + + return length; +} + +/* Read data, and (optionally) run it through the CRC. */ +void /* PRIVATE */ +png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length) +{ + if (png_ptr == NULL) + return; + png_read_data(png_ptr, buf, length); + png_calculate_crc(png_ptr, buf, length); +} + +/* Optionally skip data and then check the CRC. Depending on whether we + * are reading a ancillary or critical chunk, and how the program has set + * things up, we may calculate the CRC on the data and print a message. + * Returns '1' if there was a CRC error, '0' otherwise. + */ +int /* PRIVATE */ +png_crc_finish(png_structp png_ptr, png_uint_32 skip) +{ + png_size_t i; + png_size_t istop = png_ptr->zbuf_size; + + for (i = (png_size_t)skip; i > istop; i -= istop) + { + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + } + if (i) + { + png_crc_read(png_ptr, png_ptr->zbuf, i); + } + + if (png_crc_error(png_ptr)) + { + if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */ + !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) || + (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */ + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))) + { + png_chunk_warning(png_ptr, "CRC error"); + } + else + { + png_chunk_error(png_ptr, "CRC error"); + } + return (1); + } + + return (0); +} + +/* Compare the CRC stored in the PNG file with that calculated by libpng from + * the data it has read thus far. + */ +int /* PRIVATE */ +png_crc_error(png_structp png_ptr) +{ + png_byte crc_bytes[4]; + png_uint_32 crc; + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + png_read_data(png_ptr, crc_bytes, 4); + + if (need_crc) + { + crc = png_get_uint_32(crc_bytes); + return ((int)(crc != png_ptr->crc)); + } + else + return (0); +} + +#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \ + defined(PNG_READ_iCCP_SUPPORTED) +static png_size_t +png_inflate(png_structp png_ptr, const png_byte *data, png_size_t size, + png_bytep output, png_size_t output_size) +{ + png_size_t count = 0; + + png_ptr->zstream.next_in = (png_bytep)data; /* const_cast: VALID */ + png_ptr->zstream.avail_in = size; + + while (1) + { + int ret, avail; + + /* Reset the output buffer each time round - we empty it + * after every inflate call. + */ + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = png_ptr->zbuf_size; + + ret = inflate(&png_ptr->zstream, Z_NO_FLUSH); + avail = png_ptr->zbuf_size - png_ptr->zstream.avail_out; + + /* First copy/count any new output - but only if we didn't + * get an error code. + */ + if ((ret == Z_OK || ret == Z_STREAM_END) && avail > 0) + { + if (output != 0 && output_size > count) + { + png_size_t copy = output_size - count; + if ((png_size_t) avail < copy) copy = (png_size_t) avail; + png_memcpy(output + count, png_ptr->zbuf, copy); + } + count += avail; + } + + if (ret == Z_OK) + continue; + + /* Termination conditions - always reset the zstream, it + * must be left in inflateInit state. + */ + png_ptr->zstream.avail_in = 0; + inflateReset(&png_ptr->zstream); + + if (ret == Z_STREAM_END) + return count; /* NOTE: may be zero. */ + + /* Now handle the error codes - the API always returns 0 + * and the error message is dumped into the uncompressed + * buffer if available. + */ + { + PNG_CONST char *msg; + if (png_ptr->zstream.msg != 0) + msg = png_ptr->zstream.msg; + else + { +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + char umsg[52]; + + switch (ret) + { + case Z_BUF_ERROR: + msg = "Buffer error in compressed datastream in %s chunk"; + break; + case Z_DATA_ERROR: + msg = "Data error in compressed datastream in %s chunk"; + break; + default: + msg = "Incomplete compressed datastream in %s chunk"; + break; + } + + png_snprintf(umsg, sizeof umsg, msg, png_ptr->chunk_name); + msg = umsg; +#else + msg = "Damaged compressed datastream in chunk other than IDAT"; +#endif + } + + png_warning(png_ptr, msg); + } + + /* 0 means an error - notice that this code simple ignores + * zero length compressed chunks as a result. + */ + return 0; + } +} + +/* + * Decompress trailing data in a chunk. The assumption is that chunkdata + * points at an allocated area holding the contents of a chunk with a + * trailing compressed part. What we get back is an allocated area + * holding the original prefix part and an uncompressed version of the + * trailing part (the malloc area passed in is freed). + */ +void /* PRIVATE */ +png_decompress_chunk(png_structp png_ptr, int comp_type, + png_size_t chunklength, + png_size_t prefix_size, png_size_t *newlength) +{ + /* The caller should guarantee this */ + if (prefix_size > chunklength) + { + /* The recovery is to delete the chunk. */ + png_warning(png_ptr, "invalid chunklength"); + prefix_size = 0; /* To delete everything */ + } + + else if (comp_type == PNG_COMPRESSION_TYPE_BASE) + { + png_size_t expanded_size = png_inflate(png_ptr, + (png_bytep)(png_ptr->chunkdata + prefix_size), + chunklength - prefix_size, + 0/*output*/, 0/*output size*/); + + /* Now check the limits on this chunk - if the limit fails the + * compressed data will be removed, the prefix will remain. + */ + if (prefix_size >= (~(png_size_t)0) - 1 || + expanded_size >= (~(png_size_t)0) - 1 - prefix_size +#ifdef PNG_USER_CHUNK_MALLOC_MAX + || ((PNG_USER_CHUNK_MALLOC_MAX > 0) && + prefix_size + expanded_size >= PNG_USER_CHUNK_MALLOC_MAX - 1) +#endif + ) + png_warning(png_ptr, "Exceeded size limit while expanding chunk"); + + /* If the size is zero either there was an error and a message + * has already been output (warning) or the size really is zero + * and we have nothing to do - the code will exit through the + * error case below. + */ + else if (expanded_size > 0) + { + /* Success (maybe) - really uncompress the chunk. */ + png_size_t new_size = 0; + + png_charp text = png_malloc_warn(png_ptr, + prefix_size + expanded_size + 1); + + if (text != NULL) + { + png_memcpy(text, png_ptr->chunkdata, prefix_size); + new_size = png_inflate(png_ptr, + (png_bytep)(png_ptr->chunkdata + prefix_size), + chunklength - prefix_size, + (png_bytep)(text + prefix_size), expanded_size); + text[prefix_size + expanded_size] = 0; /* just in case */ + + if (new_size == expanded_size) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = text; + *newlength = prefix_size + expanded_size; + return; /* The success return! */ + } + + png_warning(png_ptr, "png_inflate logic error"); + png_free(png_ptr, text); + } + else + png_warning(png_ptr, "Not enough memory to decompress chunk."); + } + } + + else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */ + { +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + char umsg[50]; + + png_snprintf(umsg, sizeof umsg, "Unknown zTXt compression type %d", + comp_type); + png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, "Unknown zTXt compression type"); +#endif + + /* The recovery is to simply drop the data. */ + } + + /* Generic error return - leave the prefix, delete the compressed + * data, reallocate the chunkdata to remove the potentially large + * amount of compressed data. + */ + { + png_charp text = png_malloc_warn(png_ptr, prefix_size + 1); + if (text != NULL) + { + if (prefix_size > 0) + png_memcpy(text, png_ptr->chunkdata, prefix_size); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = text; + + /* This is an extra zero in the 'uncompressed' part. */ + *(png_ptr->chunkdata + prefix_size) = 0x00; + } + /* Ignore a malloc error here - it is safe. */ + } + + *newlength = prefix_size; +} +#endif + +/* Read and check the IDHR chunk */ +void /* PRIVATE */ +png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[13]; + png_uint_32 width, height; + int bit_depth, color_type, compression_type, filter_type; + int interlace_type; + + png_debug(1, "in png_handle_IHDR"); + + if (png_ptr->mode & PNG_HAVE_IHDR) + png_error(png_ptr, "Out of place IHDR"); + + /* Check the length */ + if (length != 13) + png_error(png_ptr, "Invalid IHDR chunk"); + + png_ptr->mode |= PNG_HAVE_IHDR; + + png_crc_read(png_ptr, buf, 13); + png_crc_finish(png_ptr, 0); + + width = png_get_uint_31(png_ptr, buf); + height = png_get_uint_31(png_ptr, buf + 4); + bit_depth = buf[8]; + color_type = buf[9]; + compression_type = buf[10]; + filter_type = buf[11]; + interlace_type = buf[12]; + + /* Set internal variables */ + png_ptr->width = width; + png_ptr->height = height; + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->interlaced = (png_byte)interlace_type; + png_ptr->color_type = (png_byte)color_type; +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + + /* Find number of channels */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_PALETTE: + png_ptr->channels = 1; + break; + + case PNG_COLOR_TYPE_RGB: + png_ptr->channels = 3; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_ptr->channels = 2; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + png_ptr->channels = 4; + break; + } + + /* Set up other useful info */ + png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * + png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width); + png_debug1(3, "bit_depth = %d", png_ptr->bit_depth); + png_debug1(3, "channels = %d", png_ptr->channels); + png_debug1(3, "rowbytes = %lu", png_ptr->rowbytes); + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, + color_type, interlace_type, compression_type, filter_type); +} + +/* Read and check the palette */ +void /* PRIVATE */ +png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_color palette[PNG_MAX_PALETTE_LENGTH]; + int num, i; +#ifdef PNG_POINTER_INDEXING_SUPPORTED + png_colorp pal_ptr; +#endif + + png_debug(1, "in png_handle_PLTE"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before PLTE"); + + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid PLTE after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + + else if (png_ptr->mode & PNG_HAVE_PLTE) + png_error(png_ptr, "Duplicate PLTE chunk"); + + png_ptr->mode |= PNG_HAVE_PLTE; + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring PLTE chunk in grayscale PNG"); + png_crc_finish(png_ptr, length); + return; + } +#ifndef PNG_READ_OPT_PLTE_SUPPORTED + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_crc_finish(png_ptr, length); + return; + } +#endif + + if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) + { + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_warning(png_ptr, "Invalid palette chunk"); + png_crc_finish(png_ptr, length); + return; + } + + else + { + png_error(png_ptr, "Invalid palette chunk"); + } + } + + num = (int)length / 3; + +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + pal_ptr->red = buf[0]; + pal_ptr->green = buf[1]; + pal_ptr->blue = buf[2]; + } +#else + for (i = 0; i < num; i++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + /* Don't depend upon png_color being any order */ + palette[i].red = buf[0]; + palette[i].green = buf[1]; + palette[i].blue = buf[2]; + } +#endif + + /* If we actually NEED the PLTE chunk (ie for a paletted image), we do + * whatever the normal CRC configuration tells us. However, if we + * have an RGB image, the PLTE can be considered ancillary, so + * we will act as though it is. + */ +#ifndef PNG_READ_OPT_PLTE_SUPPORTED + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#endif + { + png_crc_finish(png_ptr, 0); + } +#ifndef PNG_READ_OPT_PLTE_SUPPORTED + else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ + { + /* If we don't want to use the data from an ancillary chunk, + we have two options: an error abort, or a warning and we + ignore the data in this chunk (which should be OK, since + it's considered ancillary for a RGB or RGBA image). */ + if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE)) + { + if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) + { + png_chunk_error(png_ptr, "CRC error"); + } + else + { + png_chunk_warning(png_ptr, "CRC error"); + return; + } + } + /* Otherwise, we (optionally) emit a warning and use the chunk. */ + else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) + { + png_chunk_warning(png_ptr, "CRC error"); + } + } +#endif + + png_set_PLTE(png_ptr, info_ptr, palette, num); + +#ifdef PNG_READ_tRNS_SUPPORTED + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + if (png_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect tRNS chunk length"); + png_ptr->num_trans = (png_uint_16)num; + } + if (info_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect info tRNS chunk length"); + info_ptr->num_trans = (png_uint_16)num; + } + } + } +#endif + +} + +void /* PRIVATE */ +png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_debug(1, "in png_handle_IEND"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) + { + png_error(png_ptr, "No image in file"); + } + + png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); + + if (length != 0) + { + png_warning(png_ptr, "Incorrect IEND chunk length"); + } + png_crc_finish(png_ptr, length); + + PNG_UNUSED(info_ptr) /* Quiet compiler warnings about unused info_ptr */ +} + +#ifdef PNG_READ_gAMA_SUPPORTED +void /* PRIVATE */ +png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_fixed_point igamma; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif + png_byte buf[4]; + + png_debug(1, "in png_handle_gAMA"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before gAMA"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid gAMA after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place gAMA chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) +#ifdef PNG_READ_sRGB_SUPPORTED + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate gAMA chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 4) + { + png_warning(png_ptr, "Incorrect gAMA chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 4); + if (png_crc_finish(png_ptr, 0)) + return; + + igamma = (png_fixed_point)png_get_uint_32(buf); + /* Check for zero gamma */ + if (igamma == 0) + { + png_warning(png_ptr, + "Ignoring gAMA chunk with gamma=0"); + return; + } + +#ifdef PNG_READ_sRGB_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifdef PNG_CONSOLE_IO_SUPPORTED + fprintf(stderr, "gamma = (%d/100000)", (int)igamma); +#endif + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float)igamma / (float)100000.0; +# ifdef PNG_READ_GAMMA_SUPPORTED + png_ptr->gamma = file_gamma; +# endif + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_gAMA_fixed(png_ptr, info_ptr, igamma); +#endif +} +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED +void /* PRIVATE */ +png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[4]; + + png_debug(1, "in png_handle_sBIT"); + + buf[0] = buf[1] = buf[2] = buf[3] = 0; + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sBIT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sBIT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + { + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sBIT chunk"); + } + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) + { + png_warning(png_ptr, "Duplicate sBIT chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 3; + else + truelen = (png_size_t)png_ptr->channels; + + if (length != truelen || length > 4) + { + png_warning(png_ptr, "Incorrect sBIT chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[1]; + png_ptr->sig_bit.blue = buf[2]; + png_ptr->sig_bit.alpha = buf[3]; + } + else + { + png_ptr->sig_bit.gray = buf[0]; + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[0]; + png_ptr->sig_bit.blue = buf[0]; + png_ptr->sig_bit.alpha = buf[1]; + } + png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); +} +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED +void /* PRIVATE */ +png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[32]; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif + png_fixed_point int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue; + + png_uint_32 uint_x, uint_y; + + png_debug(1, "in png_handle_cHRM"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before cHRM"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid cHRM after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Missing PLTE before cHRM"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM) +#ifdef PNG_READ_sRGB_SUPPORTED + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate cHRM chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 32) + { + png_warning(png_ptr, "Incorrect cHRM chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 32); + if (png_crc_finish(png_ptr, 0)) + return; + + uint_x = png_get_uint_32(buf); + uint_y = png_get_uint_32(buf + 4); + int_x_white = (png_fixed_point)uint_x; + int_y_white = (png_fixed_point)uint_y; + + uint_x = png_get_uint_32(buf + 8); + uint_y = png_get_uint_32(buf + 12); + int_x_red = (png_fixed_point)uint_x; + int_y_red = (png_fixed_point)uint_y; + + uint_x = png_get_uint_32(buf + 16); + uint_y = png_get_uint_32(buf + 20); + int_x_green = (png_fixed_point)uint_x; + int_y_green = (png_fixed_point)uint_y; + + uint_x = png_get_uint_32(buf + 24); + uint_y = png_get_uint_32(buf + 28); + int_x_blue = (png_fixed_point)uint_x; + int_y_blue = (png_fixed_point)uint_y; + +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float)int_x_white / (float)100000.0; + white_y = (float)int_y_white / (float)100000.0; + red_x = (float)int_x_red / (float)100000.0; + red_y = (float)int_y_red / (float)100000.0; + green_x = (float)int_x_green / (float)100000.0; + green_y = (float)int_y_green / (float)100000.0; + blue_x = (float)int_x_blue / (float)100000.0; + blue_y = (float)int_y_blue / (float)100000.0; +#endif + +#ifdef PNG_READ_sRGB_SUPPORTED + if ((info_ptr != NULL) && (info_ptr->valid & PNG_INFO_sRGB)) + { + if (PNG_OUT_OF_RANGE(int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); +#ifdef PNG_CONSOLE_IO_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr, "wx=%f, wy=%f, rx=%f, ry=%f\n", + white_x, white_y, red_x, red_y); + fprintf(stderr, "gx=%f, gy=%f, bx=%f, by=%f\n", + green_x, green_y, blue_x, blue_y); +#else + fprintf(stderr, "wx=%ld, wy=%ld, rx=%ld, ry=%ld\n", + (long)int_x_white, (long)int_y_white, + (long)int_x_red, (long)int_y_red); + fprintf(stderr, "gx=%ld, gy=%ld, bx=%ld, by=%ld\n", + (long)int_x_green, (long)int_y_green, + (long)int_x_blue, (long)int_y_blue); +#endif +#endif /* PNG_CONSOLE_IO_SUPPORTED */ + } + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue); +#endif +} +#endif + +#ifdef PNG_READ_sRGB_SUPPORTED +void /* PRIVATE */ +png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + int intent; + png_byte buf[1]; + + png_debug(1, "in png_handle_sRGB"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sRGB"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sRGB after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sRGB chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + { + png_warning(png_ptr, "Duplicate sRGB chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 1) + { + png_warning(png_ptr, "Incorrect sRGB chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 1); + if (png_crc_finish(png_ptr, 0)) + return; + + intent = buf[0]; + /* Check for bad intent */ + if (intent >= PNG_sRGB_INTENT_LAST) + { + png_warning(png_ptr, "Unknown sRGB intent"); + return; + } + +#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)) + { + png_fixed_point igamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + igamma=info_ptr->int_gamma; +#else +# ifdef PNG_FLOATING_POINT_SUPPORTED + igamma=(png_fixed_point)(info_ptr->gamma * 100000.); +# endif +#endif + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifdef PNG_CONSOLE_IO_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED + fprintf(stderr, "incorrect gamma=(%d/100000)\n", + (int)png_ptr->int_gamma); +# else +# ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr, "incorrect gamma=%f\n", png_ptr->gamma); +# endif +# endif +#endif + } + } +#endif /* PNG_READ_gAMA_SUPPORTED */ + +#ifdef PNG_READ_cHRM_SUPPORTED +#ifdef PNG_FIXED_POINT_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + if (PNG_OUT_OF_RANGE(info_ptr->int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); + } +#endif /* PNG_FIXED_POINT_SUPPORTED */ +#endif /* PNG_READ_cHRM_SUPPORTED */ + + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent); +} +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_READ_iCCP_SUPPORTED +void /* PRIVATE */ +png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_byte compression_type; + png_bytep pC; + png_charp profile; + png_uint_32 skip = 0; + png_uint_32 profile_size, profile_length; + png_size_t slength, prefix_length, data_length; + + png_debug(1, "in png_handle_iCCP"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iCCP"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid iCCP after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place iCCP chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)) + { + png_warning(png_ptr, "Duplicate iCCP chunk"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "iCCP chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; + + for (profile = png_ptr->chunkdata; *profile; profile++) + /* Empty loop to find end of name */ ; + + ++profile; + + /* There should be at least one zero (the compression type byte) + * following the separator, and we should be on it + */ + if ( profile >= png_ptr->chunkdata + slength - 1) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "Malformed iCCP chunk"); + return; + } + + /* Compression_type should always be zero */ + compression_type = *profile++; + if (compression_type) + { + png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk"); + compression_type = 0x00; /* Reset it to zero (libpng-1.0.6 through 1.0.8 + wrote nonzero) */ + } + + prefix_length = profile - png_ptr->chunkdata; + png_decompress_chunk(png_ptr, compression_type, + slength, prefix_length, &data_length); + + profile_length = data_length - prefix_length; + + if ( prefix_length > data_length || profile_length < 4) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "Profile size field missing from iCCP chunk"); + return; + } + + /* Check the profile_size recorded in the first 32 bits of the ICC profile */ + pC = (png_bytep)(png_ptr->chunkdata + prefix_length); + profile_size = ((*(pC ))<<24) | + ((*(pC + 1))<<16) | + ((*(pC + 2))<< 8) | + ((*(pC + 3)) ); + + if (profile_size < profile_length) + profile_length = profile_size; + + if (profile_size > profile_length) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "Ignoring truncated iCCP profile."); + return; + } + + png_set_iCCP(png_ptr, info_ptr, png_ptr->chunkdata, + compression_type, png_ptr->chunkdata + prefix_length, profile_length); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; +} +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#ifdef PNG_READ_sPLT_SUPPORTED +void /* PRIVATE */ +png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_bytep entry_start; + png_sPLT_t new_palette; +#ifdef PNG_POINTER_INDEXING_SUPPORTED + png_sPLT_entryp pp; +#endif + int data_length, entry_size, i; + png_uint_32 skip = 0; + png_size_t slength; + + png_debug(1, "in png_handle_sPLT"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for sPLT"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sPLT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sPLT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "sPLT chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; + + for (entry_start = (png_bytep)png_ptr->chunkdata; *entry_start; + entry_start++) + /* Empty loop to find end of name */ ; + ++entry_start; + + /* A sample depth should follow the separator, and we should be on it */ + if (entry_start > (png_bytep)png_ptr->chunkdata + slength - 2) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "malformed sPLT chunk"); + return; + } + + new_palette.depth = *entry_start++; + entry_size = (new_palette.depth == 8 ? 6 : 10); + data_length = (slength - (entry_start - (png_bytep)png_ptr->chunkdata)); + + /* Integrity-check the data length */ + if (data_length % entry_size) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "sPLT chunk has bad length"); + return; + } + + new_palette.nentries = (png_int_32) ( data_length / entry_size); + if ((png_uint_32) new_palette.nentries > + (png_uint_32) (PNG_SIZE_MAX / png_sizeof(png_sPLT_entry))) + { + png_warning(png_ptr, "sPLT chunk too long"); + return; + } + new_palette.entries = (png_sPLT_entryp)png_malloc_warn( + png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry)); + if (new_palette.entries == NULL) + { + png_warning(png_ptr, "sPLT chunk requires too much memory"); + return; + } + +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (i = 0; i < new_palette.nentries; i++) + { + pp = new_palette.entries + i; + + if (new_palette.depth == 8) + { + pp->red = *entry_start++; + pp->green = *entry_start++; + pp->blue = *entry_start++; + pp->alpha = *entry_start++; + } + else + { + pp->red = png_get_uint_16(entry_start); entry_start += 2; + pp->green = png_get_uint_16(entry_start); entry_start += 2; + pp->blue = png_get_uint_16(entry_start); entry_start += 2; + pp->alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#else + pp = new_palette.entries; + for (i = 0; i < new_palette.nentries; i++) + { + + if (new_palette.depth == 8) + { + pp[i].red = *entry_start++; + pp[i].green = *entry_start++; + pp[i].blue = *entry_start++; + pp[i].alpha = *entry_start++; + } + else + { + pp[i].red = png_get_uint_16(entry_start); entry_start += 2; + pp[i].green = png_get_uint_16(entry_start); entry_start += 2; + pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; + pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#endif + + /* Discard all chunk data except the name and stash that */ + new_palette.name = png_ptr->chunkdata; + + png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, new_palette.entries); +} +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#ifdef PNG_READ_tRNS_SUPPORTED +void /* PRIVATE */ +png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_tRNS"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tRNS"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid tRNS after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_warning(png_ptr, "Duplicate tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + png_byte buf[2]; + + if (length != 2) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 2); + png_ptr->num_trans = 1; + png_ptr->trans_values.gray = png_get_uint_16(buf); + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_byte buf[6]; + + if (length != 6) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, buf, (png_size_t)length); + png_ptr->num_trans = 1; + png_ptr->trans_values.red = png_get_uint_16(buf); + png_ptr->trans_values.green = png_get_uint_16(buf + 2); + png_ptr->trans_values.blue = png_get_uint_16(buf + 4); + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + /* Should be an error, but we can cope with it. */ + png_warning(png_ptr, "Missing PLTE before tRNS"); + } + if (length > (png_uint_32)png_ptr->num_palette || + length > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + if (length == 0) + { + png_warning(png_ptr, "Zero length tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, readbuf, (png_size_t)length); + png_ptr->num_trans = (png_uint_16)length; + } + else + { + png_warning(png_ptr, "tRNS chunk not allowed with alpha channel"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_crc_finish(png_ptr, 0)) + { + png_ptr->num_trans = 0; + return; + } + + png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, + &(png_ptr->trans_values)); +} +#endif + +#ifdef PNG_READ_bKGD_SUPPORTED +void /* PRIVATE */ +png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[6]; + + png_debug(1, "in png_handle_bKGD"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before bKGD"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid bKGD after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before bKGD"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) + { + png_warning(png_ptr, "Duplicate bKGD chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 1; + else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + truelen = 6; + else + truelen = 2; + + if (length != truelen) + { + png_warning(png_ptr, "Incorrect bKGD chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + /* We convert the index value into RGB components so that we can allow + * arbitrary RGB values for background when we have transparency, and + * so it is easy to determine the RGB values of the background color + * from the info_ptr struct. */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.index = buf[0]; + if (info_ptr && info_ptr->num_palette) + { + if (buf[0] >= info_ptr->num_palette) + { + png_warning(png_ptr, "Incorrect bKGD chunk index value"); + return; + } + png_ptr->background.red = + (png_uint_16)png_ptr->palette[buf[0]].red; + png_ptr->background.green = + (png_uint_16)png_ptr->palette[buf[0]].green; + png_ptr->background.blue = + (png_uint_16)png_ptr->palette[buf[0]].blue; + } + } + else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */ + { + png_ptr->background.red = + png_ptr->background.green = + png_ptr->background.blue = + png_ptr->background.gray = png_get_uint_16(buf); + } + else + { + png_ptr->background.red = png_get_uint_16(buf); + png_ptr->background.green = png_get_uint_16(buf + 2); + png_ptr->background.blue = png_get_uint_16(buf + 4); + } + + png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background)); +} +#endif + +#ifdef PNG_READ_hIST_SUPPORTED +void /* PRIVATE */ +png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + unsigned int num, i; + png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_hIST"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before hIST"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid hIST after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before hIST"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) + { + png_warning(png_ptr, "Duplicate hIST chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length > 2*PNG_MAX_PALETTE_LENGTH || + length != (unsigned int) (2*png_ptr->num_palette)) + { + png_warning(png_ptr, "Incorrect hIST chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + num = length / 2 ; + + for (i = 0; i < num; i++) + { + png_byte buf[2]; + + png_crc_read(png_ptr, buf, 2); + readbuf[i] = png_get_uint_16(buf); + } + + if (png_crc_finish(png_ptr, 0)) + return; + + png_set_hIST(png_ptr, info_ptr, readbuf); +} +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED +void /* PRIVATE */ +png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_uint_32 res_x, res_y; + int unit_type; + + png_debug(1, "in png_handle_pHYs"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pHYs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pHYs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_warning(png_ptr, "Duplicate pHYs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect pHYs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + res_x = png_get_uint_32(buf); + res_y = png_get_uint_32(buf + 4); + unit_type = buf[8]; + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); +} +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED +void /* PRIVATE */ +png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_int_32 offset_x, offset_y; + int unit_type; + + png_debug(1, "in png_handle_oFFs"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before oFFs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid oFFs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_warning(png_ptr, "Duplicate oFFs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect oFFs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + offset_x = png_get_int_32(buf); + offset_y = png_get_int_32(buf + 4); + unit_type = buf[8]; + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); +} +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED +/* Read the pCAL chunk (described in the PNG Extensions document) */ +void /* PRIVATE */ +png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_int_32 X0, X1; + png_byte type, nparams; + png_charp buf, units, endptr; + png_charpp params; + png_size_t slength; + int i; + + png_debug(1, "in png_handle_pCAL"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) + { + png_warning(png_ptr, "Duplicate pCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading pCAL chunk data (%lu bytes)", + length + 1); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "No memory for pCAL purpose."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; /* Null terminate the last string */ + + png_debug(3, "Finding end of pCAL purpose string"); + for (buf = png_ptr->chunkdata; *buf; buf++) + /* Empty loop */ ; + + endptr = png_ptr->chunkdata + slength; + + /* We need to have at least 12 bytes after the purpose string + in order to get the parameter information. */ + if (endptr <= buf + 12) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_debug(3, "Reading pCAL X0, X1, type, nparams, and units"); + X0 = png_get_int_32((png_bytep)buf+1); + X1 = png_get_int_32((png_bytep)buf+5); + type = buf[9]; + nparams = buf[10]; + units = buf + 11; + + png_debug(3, "Checking pCAL equation type and number of parameters"); + /* Check that we have the right number of parameters for known + equation types. */ + if ((type == PNG_EQUATION_LINEAR && nparams != 2) || + (type == PNG_EQUATION_BASE_E && nparams != 3) || + (type == PNG_EQUATION_ARBITRARY && nparams != 3) || + (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) + { + png_warning(png_ptr, "Invalid pCAL parameters for equation type"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + else if (type >= PNG_EQUATION_LAST) + { + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + } + + for (buf = units; *buf; buf++) + /* Empty loop to move past the units string. */ ; + + png_debug(3, "Allocating pCAL parameters array"); + params = (png_charpp)png_malloc_warn(png_ptr, + (png_uint_32)(nparams * png_sizeof(png_charp))) ; + if (params == NULL) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_warning(png_ptr, "No memory for pCAL params."); + return; + } + + /* Get pointers to the start of each parameter string. */ + for (i = 0; i < (int)nparams; i++) + { + buf++; /* Skip the null string terminator from previous parameter. */ + + png_debug1(3, "Reading pCAL parameter %d", i); + for (params[i] = buf; buf <= endptr && *buf != 0x00; buf++) + /* Empty loop to move past each parameter string */ ; + + /* Make sure we haven't run out of data yet */ + if (buf > endptr) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, params); + return; + } + } + + png_set_pCAL(png_ptr, info_ptr, png_ptr->chunkdata, X0, X1, type, nparams, + units, params); + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, params); +} +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED +/* Read the sCAL chunk */ +void /* PRIVATE */ +png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_charp ep; +#ifdef PNG_FLOATING_POINT_SUPPORTED + double width, height; + png_charp vp; +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_charp swidth, sheight; +#endif +#endif + png_size_t slength; + + png_debug(1, "in png_handle_sCAL"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) + { + png_warning(png_ptr, "Duplicate sCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + /* Need unit type, width, \0, height: minimum 4 bytes */ + else if (length < 4) + { + png_warning(png_ptr, "sCAL chunk too short"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading sCAL chunk data (%lu bytes)", + length + 1); + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; /* Null terminate the last string */ + + ep = png_ptr->chunkdata + 1; /* Skip unit byte */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + width = png_strtod(png_ptr, ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed width string in sCAL chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + swidth = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (swidth == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk width"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + png_memcpy(swidth, ep, (png_size_t)png_strlen(ep) + 1); +#endif +#endif + + for (ep = png_ptr->chunkdata + 1; *ep; ep++) + /* Empty loop */ ; + ep++; + + if (png_ptr->chunkdata + slength < ep) + { + png_warning(png_ptr, "Truncated sCAL chunk"); +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); +#endif + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + +#ifdef PNG_FLOATING_POINT_SUPPORTED + height = png_strtod(png_ptr, ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed height string in sCAL chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); +#endif + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + sheight = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (sheight == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk height"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); +#endif + return; + } + png_memcpy(sheight, ep, (png_size_t)png_strlen(ep) + 1); +#endif +#endif + + if (png_ptr->chunkdata + slength < ep +#ifdef PNG_FLOATING_POINT_SUPPORTED + || width <= 0. || height <= 0. +#endif + ) + { + png_warning(png_ptr, "Invalid sCAL data"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif + return; + } + + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_sCAL(png_ptr, info_ptr, png_ptr->chunkdata[0], width, height); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_sCAL_s(png_ptr, info_ptr, png_ptr->chunkdata[0], swidth, sheight); +#endif +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif +} +#endif + +#ifdef PNG_READ_tIME_SUPPORTED +void /* PRIVATE */ +png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[7]; + png_time mod_time; + + png_debug(1, "in png_handle_tIME"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Out of place tIME chunk"); + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) + { + png_warning(png_ptr, "Duplicate tIME chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + + if (length != 7) + { + png_warning(png_ptr, "Incorrect tIME chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 7); + if (png_crc_finish(png_ptr, 0)) + return; + + mod_time.second = buf[6]; + mod_time.minute = buf[5]; + mod_time.hour = buf[4]; + mod_time.day = buf[3]; + mod_time.month = buf[2]; + mod_time.year = png_get_uint_16(buf); + + png_set_tIME(png_ptr, info_ptr, &mod_time); +} +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED +/* Note: this does not properly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp key; + png_charp text; + png_uint_32 skip = 0; + png_size_t slength; + int ret; + + png_debug(1, "in png_handle_tEXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for tEXt"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tEXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "No memory to process text chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + key = png_ptr->chunkdata; + + key[slength] = 0x00; + + for (text = key; *text; text++) + /* Empty loop to find end of key */ ; + + if (text != key + slength) + text++; + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process text chunk."); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = text; + text_ptr->text_length = png_strlen(text); + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to process text chunk."); +} +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED +/* Note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp text; + int comp_type; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_zTXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for zTXt"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before zTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "zTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "Out of memory processing zTXt chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; + + for (text = png_ptr->chunkdata; *text; text++) + /* Empty loop */ ; + + /* zTXt must have some text after the chunkdataword */ + if (text >= png_ptr->chunkdata + slength - 2) + { + png_warning(png_ptr, "Truncated zTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + else + { + comp_type = *(++text); + if (comp_type != PNG_TEXT_COMPRESSION_zTXt) + { + png_warning(png_ptr, "Unknown compression type in zTXt chunk"); + comp_type = PNG_TEXT_COMPRESSION_zTXt; + } + text++; /* Skip the compression_method byte */ + } + prefix_len = text - png_ptr->chunkdata; + + png_decompress_chunk(png_ptr, comp_type, + (png_size_t)length, prefix_len, &data_len); + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process zTXt chunk."); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + text_ptr->compression = comp_type; + text_ptr->key = png_ptr->chunkdata; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = png_ptr->chunkdata + prefix_len; + text_ptr->text_length = data_len; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + if (ret) + png_error(png_ptr, "Insufficient memory to store zTXt chunk."); +} +#endif + +#ifdef PNG_READ_iTXt_SUPPORTED +/* Note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp key, lang, text, lang_key; + int comp_flag; + int comp_type = 0; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_iTXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for iTXt"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "iTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (png_ptr->chunkdata == NULL) + { + png_warning(png_ptr, "No memory to process iTXt chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + png_ptr->chunkdata[slength] = 0x00; + + for (lang = png_ptr->chunkdata; *lang; lang++) + /* Empty loop */ ; + lang++; /* Skip NUL separator */ + + /* iTXt must have a language tag (possibly empty), two compression bytes, + * translated keyword (possibly empty), and possibly some text after the + * keyword + */ + + if (lang >= png_ptr->chunkdata + slength - 3) + { + png_warning(png_ptr, "Truncated iTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + else + { + comp_flag = *lang++; + comp_type = *lang++; + } + + for (lang_key = lang; *lang_key; lang_key++) + /* Empty loop */ ; + lang_key++; /* Skip NUL separator */ + + if (lang_key >= png_ptr->chunkdata + slength) + { + png_warning(png_ptr, "Truncated iTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + for (text = lang_key; *text; text++) + /* Empty loop */ ; + text++; /* Skip NUL separator */ + if (text >= png_ptr->chunkdata + slength) + { + png_warning(png_ptr, "Malformed iTXt chunk"); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + + prefix_len = text - png_ptr->chunkdata; + + key=png_ptr->chunkdata; + if (comp_flag) + png_decompress_chunk(png_ptr, comp_type, + (size_t)length, prefix_len, &data_len); + else + data_len = png_strlen(png_ptr->chunkdata + prefix_len); + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process iTXt chunk."); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + return; + } + text_ptr->compression = (int)comp_flag + 1; + text_ptr->lang_key = png_ptr->chunkdata + (lang_key - key); + text_ptr->lang = png_ptr->chunkdata + (lang - key); + text_ptr->itxt_length = data_len; + text_ptr->text_length = 0; + text_ptr->key = png_ptr->chunkdata; + text_ptr->text = png_ptr->chunkdata + prefix_len; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, png_ptr->chunkdata); + png_ptr->chunkdata = NULL; + if (ret) + png_error(png_ptr, "Insufficient memory to store iTXt chunk."); +} +#endif + +/* This function is called when we haven't found a handler for a + chunk. If there isn't a problem with the chunk itself (ie bad + chunk name, CRC, or a critical chunk), the chunk is silently ignored + -- unless the PNG_FLAG_UNKNOWN_CHUNKS_SUPPORTED flag is on in which + case it will be saved away to be written out later. */ +void /* PRIVATE */ +png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_uint_32 skip = 0; + + png_debug(1, "in png_handle_unknown"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for unknown chunk"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif + + if (png_ptr->mode & PNG_HAVE_IDAT) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; +#endif + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) /* Not an IDAT */ + png_ptr->mode |= PNG_AFTER_IDAT; + } + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + /* GDAL change : png_chunk_error -> png_chunk_warning */ + png_chunk_warning(png_ptr, "unknown critical chunk"); + } + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + if ((png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + || (png_ptr->read_user_chunk_fn != NULL) +#endif + ) + { +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + png_memcpy((png_charp)png_ptr->unknown_chunk.name, + (png_charp)png_ptr->chunk_name, + png_sizeof(png_ptr->unknown_chunk.name)); + png_ptr->unknown_chunk.name[png_sizeof(png_ptr->unknown_chunk.name)-1] + = '\0'; + png_ptr->unknown_chunk.size = (png_size_t)length; + if (length == 0) + png_ptr->unknown_chunk.data = NULL; + else + { + png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length); + png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length); + } +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + if (png_ptr->read_user_chunk_fn != NULL) + { + /* Callback to user unknown chunk handler */ + int ret; + ret = (*(png_ptr->read_user_chunk_fn)) + (png_ptr, &png_ptr->unknown_chunk); + if (ret < 0) + png_chunk_error(png_ptr, "error in user chunk"); + if (ret == 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + png_set_unknown_chunks(png_ptr, info_ptr, + &png_ptr->unknown_chunk, 1); + } + } + else +#endif + png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + else +#endif + skip = length; + + png_crc_finish(png_ptr, skip); + +#ifndef PNG_READ_USER_CHUNKS_SUPPORTED + PNG_UNUSED(info_ptr) /* Quiet compiler warnings about unused info_ptr */ +#endif +} + +/* This function is called to verify that a chunk name is valid. + This function can't have the "critical chunk check" incorporated + into it, since in the future we will need to be able to call user + functions to handle unknown critical chunks after we check that + the chunk name itself is valid. */ + +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) + +void /* PRIVATE */ +png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name) +{ + png_debug(1, "in png_check_chunk_name"); + if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) || + isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3])) + { + /* GDAL change : png_chunk_error -> png_chunk_warning */ + png_chunk_warning(png_ptr, "invalid chunk type"); + } +} + +/* Combines the row recently read in with the existing pixels in the + row. This routine takes care of alpha and transparency if requested. + This routine also handles the two methods of progressive display + of interlaced images, depending on the mask value. + The mask value describes which pixels are to be combined with + the row. The pattern always repeats every 8 pixels, so just 8 + bits are needed. A one indicates the pixel is to be combined, + a zero indicates the pixel is to be skipped. This is in addition + to any alpha or transparency value associated with the pixel. If + you want all pixels to be combined, pass 0xff (255) in mask. */ + +void /* PRIVATE */ +png_combine_row(png_structp png_ptr, png_bytep row, int mask) +{ + png_debug(1, "in png_combine_row"); + if (mask == 0xff) + { + png_memcpy(row, png_ptr->row_buf + 1, + PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width)); + } + else + { + switch (png_ptr->row_info.pixel_depth) + { + case 1: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_inc, s_start, s_end; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 7; + s_inc = 1; + } + else +#endif + { + s_start = 7; + s_end = 0; + s_inc = -1; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + int value; + + value = (*sp >> shift) & 0x01; + *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 2: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 6; + s_inc = 2; + } + else +#endif + { + s_start = 6; + s_end = 0; + s_inc = -2; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0x03; + *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 4: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 4; + s_inc = 4; + } + else +#endif + { + s_start = 4; + s_end = 0; + s_inc = -4; + } + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0xf; + *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + default: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + png_byte m = 0x80; + + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + png_memcpy(dp, sp, pixel_bytes); + } + + sp += pixel_bytes; + dp += pixel_bytes; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + } + } +} + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* OLD pre-1.0.9 interface: +void png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, + png_uint_32 transformations) + */ +void /* PRIVATE */ +png_do_read_interlace(png_structp png_ptr) +{ + png_row_infop row_info = &(png_ptr->row_info); + png_bytep row = png_ptr->row_buf + 1; + int pass = png_ptr->pass; + png_uint_32 transformations = png_ptr->transformations; + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* Offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + png_debug(1, "in png_do_read_interlace"); + if (row != NULL && row_info != NULL) + { + png_uint_32 final_width; + + final_width = row_info->width * png_pass_inc[pass]; + + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_byte v; + png_uint_32 i; + int j; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (transformations & PNG_PACKSWAP) + { + sshift = (int)((row_info->width + 7) & 0x07); + dshift = (int)((final_width + 7) & 0x07); + s_start = 7; + s_end = 0; + s_inc = -1; + } + else +#endif + { + sshift = 7 - (int)((row_info->width + 7) & 0x07); + dshift = 7 - (int)((final_width + 7) & 0x07); + s_start = 0; + s_end = 7; + s_inc = 1; + } + + for (i = 0; i < row_info->width; i++) + { + v = (png_byte)((*sp >> sshift) & 0x01); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 2: + { + png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); + png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_uint_32 i; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 3) & 0x03) << 1); + dshift = (int)(((final_width + 3) & 0x03) << 1); + s_start = 6; + s_end = 0; + s_inc = -2; + } + else +#endif + { + sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1); + dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1); + s_start = 0; + s_end = 6; + s_inc = 2; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0x03); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 4: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + int jstop = png_pass_inc[pass]; + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 1) & 0x01) << 2); + dshift = (int)(((final_width + 1) & 0x01) << 2); + s_start = 4; + s_end = 0; + s_inc = -4; + } + else +#endif + { + sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2); + dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2); + s_start = 0; + s_end = 4; + s_inc = 4; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v = (png_byte)((*sp >> sshift) & 0xf); + int j; + + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + default: + { + png_size_t pixel_bytes = (row_info->pixel_depth >> 3); + png_bytep sp = row + (png_size_t)(row_info->width - 1) + * pixel_bytes; + png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; + + int jstop = png_pass_inc[pass]; + png_uint_32 i; + + for (i = 0; i < row_info->width; i++) + { + png_byte v[8]; + int j; + + png_memcpy(v, sp, pixel_bytes); + for (j = 0; j < jstop; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sp -= pixel_bytes; + } + break; + } + } + row_info->width = final_width; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width); + } +#ifndef PNG_READ_PACKSWAP_SUPPORTED + PNG_UNUSED(transformations) /* Silence compiler warning */ +#endif +} +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + +void /* PRIVATE */ +png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row, + png_bytep prev_row, int filter) +{ + png_debug(1, "in png_read_filter_row"); + png_debug2(2, "row = %lu, filter = %d", png_ptr->row_number, filter); + switch (filter) + { + case PNG_FILTER_VALUE_NONE: + break; + case PNG_FILTER_VALUE_SUB: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + png_bytep lp = row; + + for (i = bpp; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_UP: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_bytep rp = row; + png_bytep pp = prev_row; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_AVG: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *lp++) / 2 ) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_PAETH: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_bytep cp = prev_row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop=row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) /* Use leftover rp,pp */ + { + int a, b, c, pa, pb, pc, p; + + a = *lp++; + b = *pp++; + c = *cp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + /* + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; + */ + + p = (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; + + *rp = (png_byte)(((int)(*rp) + p) & 0xff); + rp++; + } + break; + } + default: + png_warning(png_ptr, "Ignoring bad adaptive filter type"); + *row = 0; + break; + } +} + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +void /* PRIVATE */ +png_read_finish_row(png_structp png_ptr) +{ +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + + png_debug(1, "in png_read_finish_row"); + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset_check(png_ptr, png_ptr->prev_row, 0, + png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (!(png_ptr->num_rows)) + continue; + } + else /* if (png_ptr->transformations & PNG_INTERLACE) */ + break; + } while (png_ptr->iwidth == 0); + + if (png_ptr->pass < 7) + return; + } +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; +#endif + char extra; + int ret; + + png_ptr->zstream.next_out = (Byte *)&extra; + png_ptr->zstream.avail_out = (uInt)1; + for (;;) + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_byte chunk_length[4]; + + png_crc_finish(png_ptr, 0); + + png_read_data(png_ptr, chunk_length, 4); + png_ptr->idat_size = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_warning(png_ptr, "Extra compressed data."); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression Error"); + + if (!(png_ptr->zstream.avail_out)) + { + png_warning(png_ptr, "Extra compressed data."); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + + } + png_ptr->zstream.avail_out = 0; + } + + if (png_ptr->idat_size || png_ptr->zstream.avail_in) + png_warning(png_ptr, "Extra compression data."); + + inflateReset(&png_ptr->zstream); + + png_ptr->mode |= PNG_AFTER_IDAT; +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +void /* PRIVATE */ +png_read_start_row(png_structp png_ptr) +{ +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + int max_pixel_depth; + png_size_t row_bytes; + + png_debug(1, "in png_read_start_row"); + png_ptr->zstream.avail_in = 0; + png_init_read_transformations(png_ptr); +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + else + png_ptr->num_rows = png_ptr->height; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + } + else +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + { + png_ptr->num_rows = png_ptr->height; + png_ptr->iwidth = png_ptr->width; + } + max_pixel_depth = png_ptr->pixel_depth; + +#ifdef PNG_READ_PACK_SUPPORTED + if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8) + max_pixel_depth = 8; +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth < 8) + max_pixel_depth = 8; + if (png_ptr->num_trans) + max_pixel_depth *= 2; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (png_ptr->num_trans) + { + max_pixel_depth *= 4; + max_pixel_depth /= 3; + } + } + } +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED + if (png_ptr->transformations & (PNG_FILLER)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + max_pixel_depth = 32; + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth <= 8) + max_pixel_depth = 16; + else + max_pixel_depth = 32; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (max_pixel_depth <= 32) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + } +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + { + if ( +#ifdef PNG_READ_EXPAND_SUPPORTED + (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || +#endif +#ifdef PNG_READ_FILLER_SUPPORTED + (png_ptr->transformations & (PNG_FILLER)) || +#endif + png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (max_pixel_depth <= 16) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + else + { + if (max_pixel_depth <= 8) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 64; + else + max_pixel_depth = 48; + } + } +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ +defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + int user_pixel_depth = png_ptr->user_transform_depth* + png_ptr->user_transform_channels; + if (user_pixel_depth > max_pixel_depth) + max_pixel_depth=user_pixel_depth; + } +#endif + + /* Align the width on the next larger 8 pixels. Mainly used + * for interlacing + */ + row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7)); + /* Calculate the maximum bytes needed, adding a byte and a pixel + * for safety's sake + */ + row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) + + 1 + ((max_pixel_depth + 7) >> 3); +#ifdef PNG_MAX_MALLOC_64K + if (row_bytes > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + + if (row_bytes + 64 > png_ptr->old_big_row_buf_size) + { + png_free(png_ptr, png_ptr->big_row_buf); + if (png_ptr->interlaced) + png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr, + row_bytes + 64); + else + png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, + row_bytes + 64); + png_ptr->old_big_row_buf_size = row_bytes + 64; + + /* Use 32 bytes of padding before and after row_buf. */ + png_ptr->row_buf = png_ptr->big_row_buf + 32; + png_ptr->old_big_row_buf_size = row_bytes + 64; + } + +#ifdef PNG_MAX_MALLOC_64K + if ((png_uint_32)row_bytes + 1 > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + if ((png_uint_32)row_bytes > (png_uint_32)(PNG_SIZE_MAX - 1)) + png_error(png_ptr, "Row has too many bytes to allocate in memory."); + + if (row_bytes + 1 > png_ptr->old_prev_row_size) + { + png_free(png_ptr, png_ptr->prev_row); + png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)( + row_bytes + 1)); + png_memset_check(png_ptr, png_ptr->prev_row, 0, row_bytes + 1); + png_ptr->old_prev_row_size = row_bytes + 1; + } + + png_ptr->rowbytes = row_bytes; + + png_debug1(3, "width = %lu,", png_ptr->width); + png_debug1(3, "height = %lu,", png_ptr->height); + png_debug1(3, "iwidth = %lu,", png_ptr->iwidth); + png_debug1(3, "num_rows = %lu,", png_ptr->num_rows); + png_debug1(3, "rowbytes = %lu,", png_ptr->rowbytes); + png_debug1(3, "irowbytes = %lu", + PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1); + + png_ptr->flags |= PNG_FLAG_ROW_INIT; +} +#endif /* PNG_READ_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngset.c b/bazaar/plugin/gdal/frmts/png/libpng/pngset.c new file mode 100644 index 000000000..fed6a55b8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngset.c @@ -0,0 +1,1241 @@ + +/* pngset.c - storage of image information into info struct + * + * Last changed in libpng 1.2.51 [February 6, 2014] + * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * The functions here are used during reads to store data from the file + * into the info struct, and during writes to store application data + * into the info struct for writing into the file. This abstracts the + * info struct and allows us to change the structure in the future. + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#ifdef PNG_bKGD_SUPPORTED +void PNGAPI +png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background) +{ + png_debug1(1, "in %s storage function", "bKGD"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16)); + info_ptr->valid |= PNG_INFO_bKGD; +} +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_cHRM(png_structp png_ptr, png_infop info_ptr, + double white_x, double white_y, double red_x, double red_y, + double green_x, double green_y, double blue_x, double blue_y) +{ + png_debug1(1, "in %s storage function", "cHRM"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_white = (float)white_x; + info_ptr->y_white = (float)white_y; + info_ptr->x_red = (float)red_x; + info_ptr->y_red = (float)red_y; + info_ptr->x_green = (float)green_x; + info_ptr->y_green = (float)green_y; + info_ptr->x_blue = (float)blue_x; + info_ptr->y_blue = (float)blue_y; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_x_white = (png_fixed_point)(white_x*100000.+0.5); + info_ptr->int_y_white = (png_fixed_point)(white_y*100000.+0.5); + info_ptr->int_x_red = (png_fixed_point)( red_x*100000.+0.5); + info_ptr->int_y_red = (png_fixed_point)( red_y*100000.+0.5); + info_ptr->int_x_green = (png_fixed_point)(green_x*100000.+0.5); + info_ptr->int_y_green = (png_fixed_point)(green_y*100000.+0.5); + info_ptr->int_x_blue = (png_fixed_point)( blue_x*100000.+0.5); + info_ptr->int_y_blue = (png_fixed_point)( blue_y*100000.+0.5); +#endif + info_ptr->valid |= PNG_INFO_cHRM; +} +#endif /* PNG_FLOATING_POINT_SUPPORTED */ + +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + png_debug1(1, "in %s storage function", "cHRM fixed"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + +#ifdef PNG_CHECK_cHRM_SUPPORTED + if (png_check_cHRM_fixed(png_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y)) +#endif + { + info_ptr->int_x_white = white_x; + info_ptr->int_y_white = white_y; + info_ptr->int_x_red = red_x; + info_ptr->int_y_red = red_y; + info_ptr->int_x_green = green_x; + info_ptr->int_y_green = green_y; + info_ptr->int_x_blue = blue_x; + info_ptr->int_y_blue = blue_y; +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->x_white = (float)(white_x/100000.); + info_ptr->y_white = (float)(white_y/100000.); + info_ptr->x_red = (float)( red_x/100000.); + info_ptr->y_red = (float)( red_y/100000.); + info_ptr->x_green = (float)(green_x/100000.); + info_ptr->y_green = (float)(green_y/100000.); + info_ptr->x_blue = (float)( blue_x/100000.); + info_ptr->y_blue = (float)( blue_y/100000.); +#endif + info_ptr->valid |= PNG_INFO_cHRM; + } +} +#endif /* PNG_FIXED_POINT_SUPPORTED */ +#endif /* PNG_cHRM_SUPPORTED */ + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma) +{ + double png_gamma; + + png_debug1(1, "in %s storage function", "gAMA"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Check for overflow */ + if (file_gamma > 21474.83) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + png_gamma=21474.83; + } + else + png_gamma = file_gamma; + info_ptr->gamma = (float)png_gamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = (int)(png_gamma*100000.+.5); +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if (png_gamma == 0.0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif +void PNGAPI +png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point + int_gamma) +{ + png_fixed_point png_gamma; + + png_debug1(1, "in %s storage function", "gAMA"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (int_gamma > (png_fixed_point)PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + png_gamma=PNG_UINT_31_MAX; + } + else + { + if (int_gamma < 0) + { + png_warning(png_ptr, "Setting negative gamma to zero"); + png_gamma = 0; + } + else + png_gamma = int_gamma; + } +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = (float)(png_gamma/100000.); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = png_gamma; +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if (png_gamma == 0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif + +#ifdef PNG_hIST_SUPPORTED +void PNGAPI +png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist) +{ + int i; + + png_debug1(1, "in %s storage function", "hIST"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (info_ptr->num_palette == 0 || info_ptr->num_palette + > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, + "Invalid palette size, hIST allocation skipped."); + return; + } + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); +#endif + /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in + * version 1.2.1 + */ + png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr, + (png_uint_32)(PNG_MAX_PALETTE_LENGTH * png_sizeof(png_uint_16))); + if (png_ptr->hist == NULL) + { + png_warning(png_ptr, "Insufficient memory for hIST chunk data."); + return; + } + + for (i = 0; i < info_ptr->num_palette; i++) + png_ptr->hist[i] = hist[i]; + info_ptr->hist = png_ptr->hist; + info_ptr->valid |= PNG_INFO_hIST; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_HIST; +#else + png_ptr->flags |= PNG_FLAG_FREE_HIST; +#endif +} +#endif + +void PNGAPI +png_set_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + png_debug1(1, "in %s storage function", "IHDR"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->width = width; + info_ptr->height = height; + info_ptr->bit_depth = (png_byte)bit_depth; + info_ptr->color_type = (png_byte)color_type; + info_ptr->compression_type = (png_byte)compression_type; + info_ptr->filter_type = (png_byte)filter_type; + info_ptr->interlace_type = (png_byte)interlace_type; + + png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->compression_type, info_ptr->filter_type); + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); + + /* Check for potential overflow */ + if (width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + info_ptr->rowbytes = (png_size_t)0; + else + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); +} + +#ifdef PNG_oFFs_SUPPORTED +void PNGAPI +png_set_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 offset_x, png_int_32 offset_y, int unit_type) +{ + png_debug1(1, "in %s storage function", "oFFs"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_offset = offset_x; + info_ptr->y_offset = offset_y; + info_ptr->offset_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_oFFs; +} +#endif + +#ifdef PNG_pCAL_SUPPORTED +void PNGAPI +png_set_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params) +{ + png_uint_32 length; + int i; + + png_debug1(1, "in %s storage function", "pCAL"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + length = png_strlen(purpose) + 1; + png_debug1(3, "allocating purpose for info (%lu bytes)", + (unsigned long)length); + info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_purpose == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL purpose."); + return; + } + png_memcpy(info_ptr->pcal_purpose, purpose, (png_size_t)length); + + png_debug(3, "storing X0, X1, type, and nparams in info"); + info_ptr->pcal_X0 = X0; + info_ptr->pcal_X1 = X1; + info_ptr->pcal_type = (png_byte)type; + info_ptr->pcal_nparams = (png_byte)nparams; + + length = png_strlen(units) + 1; + png_debug1(3, "allocating units for info (%lu bytes)", + (unsigned long)length); + info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_units == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL units."); + return; + } + png_memcpy(info_ptr->pcal_units, units, (png_size_t)length); + + info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr, + (png_uint_32)((nparams + 1) * png_sizeof(png_charp))); + if (info_ptr->pcal_params == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL params."); + return; + } + + png_memset(info_ptr->pcal_params, 0, (nparams + 1) * png_sizeof(png_charp)); + + for (i = 0; i < nparams; i++) + { + length = png_strlen(params[i]) + 1; + png_debug2(3, "allocating parameter %d for info (%lu bytes)", i, + (unsigned long)length); + info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_params[i] == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL parameter."); + return; + } + png_memcpy(info_ptr->pcal_params[i], params[i], (png_size_t)length); + } + + info_ptr->valid |= PNG_INFO_pCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PCAL; +#endif +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) || defined(PNG_WRITE_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_sCAL(png_structp png_ptr, png_infop info_ptr, + int unit, double width, double height) +{ + png_debug1(1, "in %s storage function", "sCAL"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + info_ptr->scal_pixel_width = width; + info_ptr->scal_pixel_height = height; + + info_ptr->valid |= PNG_INFO_sCAL; +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int unit, png_charp swidth, png_charp sheight) +{ + png_uint_32 length; + + png_debug1(1, "in %s storage function", "sCAL"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + + length = png_strlen(swidth) + 1; + png_debug1(3, "allocating unit for info (%u bytes)", + (unsigned int)length); + info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_width == NULL) + { + png_warning(png_ptr, + "Memory allocation failed while processing sCAL."); + return; + } + png_memcpy(info_ptr->scal_s_width, swidth, (png_size_t)length); + + length = png_strlen(sheight) + 1; + png_debug1(3, "allocating unit for info (%u bytes)", + (unsigned int)length); + info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_height == NULL) + { + png_free (png_ptr, info_ptr->scal_s_width); + info_ptr->scal_s_width = NULL; + png_warning(png_ptr, + "Memory allocation failed while processing sCAL."); + return; + } + png_memcpy(info_ptr->scal_s_height, sheight, (png_size_t)length); + info_ptr->valid |= PNG_INFO_sCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SCAL; +#endif +} +#endif +#endif +#endif + +#ifdef PNG_pHYs_SUPPORTED +void PNGAPI +png_set_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 res_x, png_uint_32 res_y, int unit_type) +{ + png_debug1(1, "in %s storage function", "pHYs"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_pixels_per_unit = res_x; + info_ptr->y_pixels_per_unit = res_y; + info_ptr->phys_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_pHYs; +} +#endif + +void PNGAPI +png_set_PLTE(png_structp png_ptr, png_infop info_ptr, + png_colorp palette, int num_palette) +{ + + png_debug1(1, "in %s storage function", "PLTE"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Invalid palette length"); + else + { + png_warning(png_ptr, "Invalid palette length"); + return; + } + } + + /* It may not actually be necessary to set png_ptr->palette here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); +#endif + + /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead + * of num_palette entries, in case of an invalid PNG file that has + * too-large sample values. + */ + png_ptr->palette = (png_colorp)png_calloc(png_ptr, + PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color)); + png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof(png_color)); + info_ptr->palette = png_ptr->palette; + info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PLTE; +#else + png_ptr->flags |= PNG_FLAG_FREE_PLTE; +#endif + + info_ptr->valid |= PNG_INFO_PLTE; +} + +#ifdef PNG_sBIT_SUPPORTED +void PNGAPI +png_set_sBIT(png_structp png_ptr, png_infop info_ptr, + png_color_8p sig_bit) +{ + png_debug1(1, "in %s storage function", "sBIT"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof(png_color_8)); + info_ptr->valid |= PNG_INFO_sBIT; +} +#endif + +#ifdef PNG_sRGB_SUPPORTED +void PNGAPI +png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent) +{ + png_debug1(1, "in %s storage function", "sRGB"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->srgb_intent = (png_byte)intent; + info_ptr->valid |= PNG_INFO_sRGB; +} + +void PNGAPI +png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr, + int intent) +{ +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_file_gamma; +#endif +#endif +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y; +#endif +#endif + png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_set_sRGB(png_ptr, info_ptr, intent); + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float).45455; + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + int_file_gamma = 45455L; + png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma); +#endif +#endif + +#ifdef PNG_cHRM_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED + int_white_x = 31270L; + int_white_y = 32900L; + int_red_x = 64000L; + int_red_y = 33000L; + int_green_x = 30000L; + int_green_y = 60000L; + int_blue_x = 15000L; + int_blue_y = 6000L; + png_set_cHRM_fixed(png_ptr, info_ptr, + int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y); +# endif + +# ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float).3127; + white_y = (float).3290; + red_x = (float).64; + red_y = (float).33; + green_x = (float).30; + green_y = (float).60; + blue_x = (float).15; + blue_y = (float).06; + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +# endif +#endif /* cHRM */ +} +#endif /* sRGB */ + + +#ifdef PNG_iCCP_SUPPORTED +void PNGAPI +png_set_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen) +{ + png_charp new_iccp_name; + png_charp new_iccp_profile; + png_uint_32 length; + + png_debug1(1, "in %s storage function", "iCCP"); + + if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) + return; + + length = png_strlen(name)+1; + new_iccp_name = (png_charp)png_malloc_warn(png_ptr, length); + if (new_iccp_name == NULL) + { + png_warning(png_ptr, "Insufficient memory to process iCCP chunk."); + return; + } + png_memcpy(new_iccp_name, name, length); + new_iccp_profile = (png_charp)png_malloc_warn(png_ptr, proflen); + if (new_iccp_profile == NULL) + { + png_free (png_ptr, new_iccp_name); + png_warning(png_ptr, + "Insufficient memory to process iCCP profile."); + return; + } + png_memcpy(new_iccp_profile, profile, (png_size_t)proflen); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); + + info_ptr->iccp_proflen = proflen; + info_ptr->iccp_name = new_iccp_name; + info_ptr->iccp_profile = new_iccp_profile; + /* Compression is always zero but is here so the API and info structure + * does not have to change if we introduce multiple compression types + */ + info_ptr->iccp_compression = (png_byte)compression_type; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ICCP; +#endif + info_ptr->valid |= PNG_INFO_iCCP; +} +#endif + +#ifdef PNG_TEXT_SUPPORTED +void PNGAPI +png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int ret; + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); + if (ret) + png_error(png_ptr, "Insufficient memory to store text"); +} + +int /* PRIVATE */ +png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int i; + + png_debug1(1, "in %s storage function", ((png_ptr == NULL || + png_ptr->chunk_name[0] == '\0') ? + "text" : (png_const_charp)png_ptr->chunk_name)); + + if (png_ptr == NULL || info_ptr == NULL || num_text == 0) + return(0); + + /* Make sure we have enough space in the "text" array in info_struct + * to hold all of the incoming text_ptr objects. + */ + if (info_ptr->num_text + num_text > info_ptr->max_text) + { + int old_max_text = info_ptr->max_text; + int old_num_text = info_ptr->num_text; + + if (info_ptr->text != NULL) + { + png_textp old_text; + + info_ptr->max_text = info_ptr->num_text + num_text + 8; + old_text = info_ptr->text; + + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof(png_text))); + if (info_ptr->text == NULL) + { + /* Restore to previous condition */ + info_ptr->max_text = old_max_text; + info_ptr->text = old_text; + return(1); + } + png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max_text * + png_sizeof(png_text))); + png_free(png_ptr, old_text); + } + else + { + info_ptr->max_text = num_text + 8; + info_ptr->num_text = 0; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof(png_text))); + if (info_ptr->text == NULL) + { + /* Restore to previous condition */ + info_ptr->num_text = old_num_text; + info_ptr->max_text = old_max_text; + return(1); + } +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TEXT; +#endif + } + png_debug1(3, "allocated %d entries for info_ptr->text", + info_ptr->max_text); + } + + for (i = 0; i < num_text; i++) + { + png_size_t text_length, key_len; + png_size_t lang_len, lang_key_len; + png_textp textp = &(info_ptr->text[info_ptr->num_text]); + + if (text_ptr[i].key == NULL) + continue; + + key_len = png_strlen(text_ptr[i].key); + + if (text_ptr[i].compression <= 0) + { + lang_len = 0; + lang_key_len = 0; + } + + else +#ifdef PNG_iTXt_SUPPORTED + { + /* Set iTXt data */ + + if (text_ptr[i].lang != NULL) + lang_len = png_strlen(text_ptr[i].lang); + else + lang_len = 0; + if (text_ptr[i].lang_key != NULL) + lang_key_len = png_strlen(text_ptr[i].lang_key); + else + lang_key_len = 0; + } +#else /* PNG_iTXt_SUPPORTED */ + { + png_warning(png_ptr, "iTXt chunk not supported."); + continue; + } +#endif + + if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') + { + text_length = 0; +#ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + textp->compression = PNG_ITXT_COMPRESSION_NONE; + else +#endif + textp->compression = PNG_TEXT_COMPRESSION_NONE; + } + + else + { + text_length = png_strlen(text_ptr[i].text); + textp->compression = text_ptr[i].compression; + } + + textp->key = (png_charp)png_malloc_warn(png_ptr, + (png_uint_32) + (key_len + text_length + lang_len + lang_key_len + 4)); + if (textp->key == NULL) + return(1); + png_debug2(2, "Allocated %lu bytes at %x in png_set_text", + (png_uint_32) + (key_len + lang_len + lang_key_len + text_length + 4), + (int)textp->key); + + png_memcpy(textp->key, text_ptr[i].key,(png_size_t)(key_len)); + *(textp->key + key_len) = '\0'; +#ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + { + textp->lang = textp->key + key_len + 1; + png_memcpy(textp->lang, text_ptr[i].lang, lang_len); + *(textp->lang + lang_len) = '\0'; + textp->lang_key = textp->lang + lang_len + 1; + png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); + *(textp->lang_key + lang_key_len) = '\0'; + textp->text = textp->lang_key + lang_key_len + 1; + } + else +#endif + { +#ifdef PNG_iTXt_SUPPORTED + textp->lang=NULL; + textp->lang_key=NULL; +#endif + textp->text = textp->key + key_len + 1; + } + if (text_length) + png_memcpy(textp->text, text_ptr[i].text, + (png_size_t)(text_length)); + *(textp->text + text_length) = '\0'; + +#ifdef PNG_iTXt_SUPPORTED + if (textp->compression > 0) + { + textp->text_length = 0; + textp->itxt_length = text_length; + } + else +#endif + + { + textp->text_length = text_length; +#ifdef PNG_iTXt_SUPPORTED + textp->itxt_length = 0; +#endif + } + info_ptr->num_text++; + png_debug1(3, "transferred text chunk %d", info_ptr->num_text); + } + return(0); +} +#endif + +#ifdef PNG_tIME_SUPPORTED +void PNGAPI +png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time) +{ + png_debug1(1, "in %s storage function", "tIME"); + + if (png_ptr == NULL || info_ptr == NULL || + (png_ptr->mode & PNG_WROTE_tIME)) + return; + + png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof(png_time)); + info_ptr->valid |= PNG_INFO_tIME; +} +#endif + +#ifdef PNG_tRNS_SUPPORTED +void PNGAPI +png_set_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep trans, int num_trans, png_color_16p trans_values) +{ + png_debug1(1, "in %s storage function", "tRNS"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (num_trans < 0 || num_trans > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Ignoring invalid num_trans value"); + return; + } + + if (trans != NULL) + { + /* It may not actually be necessary to set png_ptr->trans here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); +#endif + + /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ + png_ptr->trans = info_ptr->trans = (png_bytep)png_malloc(png_ptr, + (png_uint_32)PNG_MAX_PALETTE_LENGTH); + if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) + png_memcpy(info_ptr->trans, trans, (png_size_t)num_trans); + } + + if (trans_values != NULL) + { + int sample_max = (1 << info_ptr->bit_depth); + if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && + (int)trans_values->gray > sample_max) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + ((int)trans_values->red > sample_max || + (int)trans_values->green > sample_max || + (int)trans_values->blue > sample_max))) + png_warning(png_ptr, + "tRNS chunk has out-of-range samples for bit_depth"); + png_memcpy(&(info_ptr->trans_values), trans_values, + png_sizeof(png_color_16)); + if (num_trans == 0) + num_trans = 1; + } + + info_ptr->num_trans = (png_uint_16)num_trans; + if (num_trans != 0) + { + info_ptr->valid |= PNG_INFO_tRNS; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TRNS; +#else + png_ptr->flags |= PNG_FLAG_FREE_TRNS; +#endif + } +} +#endif + +#ifdef PNG_sPLT_SUPPORTED +void PNGAPI +png_set_sPLT(png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries) +/* + * entries - array of png_sPLT_t structures + * to be added to the list of palettes + * in the info structure. + * nentries - number of palette structures to be + * added. + */ +{ + png_sPLT_tp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL) + return; + + np = (png_sPLT_tp)png_malloc_warn(png_ptr, + (info_ptr->splt_palettes_num + nentries) * + (png_uint_32)png_sizeof(png_sPLT_t)); + if (np == NULL) + { + png_warning(png_ptr, "No memory for sPLT palettes."); + return; + } + + png_memcpy(np, info_ptr->splt_palettes, + info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t)); + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes=NULL; + + for (i = 0; i < nentries; i++) + { + png_sPLT_tp to = np + info_ptr->splt_palettes_num + i; + png_sPLT_tp from = entries + i; + png_uint_32 length; + + length = png_strlen(from->name) + 1; + to->name = (png_charp)png_malloc_warn(png_ptr, length); + if (to->name == NULL) + { + png_warning(png_ptr, + "Out of memory while processing sPLT chunk"); + continue; + } + png_memcpy(to->name, from->name, length); + to->entries = (png_sPLT_entryp)png_malloc_warn(png_ptr, + (png_uint_32)(from->nentries * png_sizeof(png_sPLT_entry))); + if (to->entries == NULL) + { + png_warning(png_ptr, + "Out of memory while processing sPLT chunk"); + png_free(png_ptr, to->name); + to->name = NULL; + continue; + } + png_memcpy(to->entries, from->entries, + from->nentries * png_sizeof(png_sPLT_entry)); + to->nentries = from->nentries; + to->depth = from->depth; + } + + info_ptr->splt_palettes = np; + info_ptr->splt_palettes_num += nentries; + info_ptr->valid |= PNG_INFO_sPLT; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SPLT; +#endif +} +#endif /* PNG_sPLT_SUPPORTED */ + +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +void PNGAPI +png_set_unknown_chunks(png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns) +{ + png_unknown_chunkp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0) + return; + + np = (png_unknown_chunkp)png_malloc_warn(png_ptr, + (png_uint_32)((info_ptr->unknown_chunks_num + num_unknowns) * + png_sizeof(png_unknown_chunk))); + if (np == NULL) + { + png_warning(png_ptr, + "Out of memory while processing unknown chunk."); + return; + } + + png_memcpy(np, info_ptr->unknown_chunks, + info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk)); + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + + for (i = 0; i < num_unknowns; i++) + { + png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i; + png_unknown_chunkp from = unknowns + i; + + png_memcpy((png_charp)to->name, (png_charp)from->name, + png_sizeof(from->name)); + to->name[png_sizeof(to->name)-1] = '\0'; + to->size = from->size; + /* Note our location in the read or write sequence */ + to->location = (png_byte)(png_ptr->mode & 0xff); + + if (from->size == 0) + to->data=NULL; + else + { + to->data = (png_bytep)png_malloc_warn(png_ptr, + (png_uint_32)from->size); + if (to->data == NULL) + { + png_warning(png_ptr, + "Out of memory while processing unknown chunk."); + to->size = 0; + } + else + png_memcpy(to->data, from->data, from->size); + } + } + + info_ptr->unknown_chunks = np; + info_ptr->unknown_chunks_num += num_unknowns; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_UNKN; +#endif +} +void PNGAPI +png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr, + int chunk, int location) +{ + if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk < + (int)info_ptr->unknown_chunks_num) + info_ptr->unknown_chunks[chunk].location = (png_byte)location; +} +#endif + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +void PNGAPI +png_permit_empty_plte (png_structp png_ptr, int empty_plte_permitted) +{ + /* This function is deprecated in favor of png_permit_mng_features() + and will be removed from libpng-1.3.0 */ + + png_debug(1, "in png_permit_empty_plte, DEPRECATED."); + + if (png_ptr == NULL) + return; + png_ptr->mng_features_permitted = (png_byte) + ((png_ptr->mng_features_permitted & (~PNG_FLAG_MNG_EMPTY_PLTE)) | + ((empty_plte_permitted & PNG_FLAG_MNG_EMPTY_PLTE))); +} +#endif +#endif + +#ifdef PNG_MNG_FEATURES_SUPPORTED +png_uint_32 PNGAPI +png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features) +{ + png_debug(1, "in png_permit_mng_features"); + + if (png_ptr == NULL) + return (png_uint_32)0; + png_ptr->mng_features_permitted = + (png_byte)(mng_features & PNG_ALL_MNG_FEATURES); + return (png_uint_32)png_ptr->mng_features_permitted; +} +#endif + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +void PNGAPI +png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_bytep + chunk_list, int num_chunks) +{ + png_bytep new_list, p; + int i, old_num_chunks; + if (png_ptr == NULL) + return; + if (num_chunks == 0) + { + if (keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE) + png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + + if (keep == PNG_HANDLE_CHUNK_ALWAYS) + png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS; + return; + } + if (chunk_list == NULL) + return; + old_num_chunks = png_ptr->num_chunk_list; + new_list=(png_bytep)png_malloc(png_ptr, + (png_uint_32) + (5*(num_chunks + old_num_chunks))); + if (png_ptr->chunk_list != NULL) + { + png_memcpy(new_list, png_ptr->chunk_list, + (png_size_t)(5*old_num_chunks)); + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + } + png_memcpy(new_list + 5*old_num_chunks, chunk_list, + (png_size_t)(5*num_chunks)); + for (p = new_list + 5*old_num_chunks + 4, i = 0; inum_chunk_list = old_num_chunks + num_chunks; + png_ptr->chunk_list = new_list; +#ifdef PNG_FREE_ME_SUPPORTED + png_ptr->free_me |= PNG_FREE_LIST; +#endif +} +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +void PNGAPI +png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr, + png_user_chunk_ptr read_user_chunk_fn) +{ + png_debug(1, "in png_set_read_user_chunk_fn"); + + if (png_ptr == NULL) + return; + + png_ptr->read_user_chunk_fn = read_user_chunk_fn; + png_ptr->user_chunk_ptr = user_chunk_ptr; +} +#endif + +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers) +{ + png_debug1(1, "in %s storage function", "rows"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + info_ptr->row_pointers = row_pointers; + if (row_pointers) + info_ptr->valid |= PNG_INFO_IDAT; +} +#endif + +void PNGAPI +png_set_compression_buffer_size(png_structp png_ptr, + png_uint_32 size) +{ + if (png_ptr == NULL) + return; + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf_size = (png_size_t)size; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; +} + +void PNGAPI +png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask) +{ + if (png_ptr && info_ptr) + info_ptr->valid &= ~mask; +} + + +#ifndef PNG_1_0_X +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +/* Function was added to libpng 1.2.0 and should always exist by default */ +void PNGAPI +png_set_asm_flags (png_structp png_ptr, png_uint_32 asm_flags) +{ +/* Obsolete as of libpng-1.2.20 and will be removed from libpng-1.4.0 */ + if (png_ptr != NULL) + png_ptr->asm_flags = 0; + PNG_UNUSED(asm_flags) /* Quiet the compiler */ +} + +/* This function was added to libpng 1.2.0 */ +void PNGAPI +png_set_mmx_thresholds (png_structp png_ptr, + png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold) +{ +/* Obsolete as of libpng-1.2.20 and will be removed from libpng-1.4.0 */ + if (png_ptr == NULL) + return; + /* Quiet the compiler */ + PNG_UNUSED(mmx_bitdepth_threshold) + PNG_UNUSED(mmx_rowbytes_threshold) +} +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* This function was added to libpng 1.2.6 */ +void PNGAPI +png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max, + png_uint_32 user_height_max) +{ + /* Images with dimensions larger than these limits will be + * rejected by png_set_IHDR(). To accept any PNG datastream + * regardless of dimensions, set both limits to 0x7ffffffL. + */ + if (png_ptr == NULL) + return; + png_ptr->user_width_max = user_width_max; + png_ptr->user_height_max = user_height_max; +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_set_benign_errors(png_structp png_ptr, int allowed) +{ + png_debug(1, "in png_set_benign_errors"); + + if (allowed) + png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; + else + png_ptr->flags &= ~PNG_FLAG_BENIGN_ERRORS_WARN; +} +#endif /* PNG_BENIGN_ERRORS_SUPPORTED */ +#endif /* ?PNG_1_0_X */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngtrans.c b/bazaar/plugin/gdal/frmts/png/libpng/pngtrans.c new file mode 100644 index 000000000..6ad9dcf62 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngtrans.c @@ -0,0 +1,699 @@ + +/* pngtrans.c - transforms the data in a row (used by both readers and writers) + * + * Last changed in libpng 1.2.41 [December 3, 2009] + * Copyright (c) 1998-2009 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Turn on BGR-to-RGB mapping */ +void PNGAPI +png_set_bgr(png_structp png_ptr) +{ + png_debug(1, "in png_set_bgr"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_BGR; +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Turn on 16 bit byte swapping */ +void PNGAPI +png_set_swap(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap"); + + if (png_ptr == NULL) + return; + if (png_ptr->bit_depth == 16) + png_ptr->transformations |= PNG_SWAP_BYTES; +} +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Turn on pixel packing */ +void PNGAPI +png_set_packing(png_structp png_ptr) +{ + png_debug(1, "in png_set_packing"); + + if (png_ptr == NULL) + return; + if (png_ptr->bit_depth < 8) + { + png_ptr->transformations |= PNG_PACK; + png_ptr->usr_bit_depth = 8; + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Turn on packed pixel swapping */ +void PNGAPI +png_set_packswap(png_structp png_ptr) +{ + png_debug(1, "in png_set_packswap"); + + if (png_ptr == NULL) + return; + if (png_ptr->bit_depth < 8) + png_ptr->transformations |= PNG_PACKSWAP; +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +void PNGAPI +png_set_shift(png_structp png_ptr, png_color_8p true_bits) +{ + png_debug(1, "in png_set_shift"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_SHIFT; + png_ptr->shift = *true_bits; +} +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +int PNGAPI +png_set_interlace_handling(png_structp png_ptr) +{ + png_debug(1, "in png_set_interlace handling"); + + if (png_ptr && png_ptr->interlaced) + { + png_ptr->transformations |= PNG_INTERLACE; + return (7); + } + + return (1); +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte on read, or remove a filler or alpha byte on write. + * The filler type has changed in v0.95 to allow future 2-byte fillers + * for 48-bit input data, as well as to avoid problems with some compilers + * that don't like bytes as parameters. + */ +void PNGAPI +png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_filler"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_FILLER; +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->filler = (png_byte)filler; +#else + png_ptr->filler = (png_uint_16)filler; +#endif + if (filler_loc == PNG_FILLER_AFTER) + png_ptr->flags |= PNG_FLAG_FILLER_AFTER; + else + png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; + + /* This should probably go in the "do_read_filler" routine. + * I attempted to do that in libpng-1.0.1a but that caused problems + * so I restored it in libpng-1.0.2a + */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_ptr->usr_channels = 4; + } + + /* Also I added this in libpng-1.0.2a (what happens when we expand + * a less-than-8-bit grayscale to GA? */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8) + { + png_ptr->usr_channels = 2; + } +} + +#ifndef PNG_1_0_X +/* Added to libpng-1.2.7 */ +void PNGAPI +png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_add_alpha"); + + if (png_ptr == NULL) + return; + png_set_filler(png_ptr, filler, filler_loc); + png_ptr->transformations |= PNG_ADD_ALPHA; +} +#endif + +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void PNGAPI +png_set_swap_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap_alpha"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_SWAP_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void PNGAPI +png_set_invert_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_alpha"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_INVERT_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +void PNGAPI +png_set_invert_mono(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_mono"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_INVERT_MONO; +} + +/* Invert monochrome grayscale data */ +void /* PRIVATE */ +png_do_invert(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_invert"); + + /* This test removed from libpng version 1.0.13 and 1.2.0: + * if (row_info->bit_depth == 1 && + */ +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row == NULL || row_info == NULL) + return; +#endif + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(~(*rp)); + rp++; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 8) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=2) + { + *rp = (png_byte)(~(*rp)); + rp+=2; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=4) + { + *rp = (png_byte)(~(*rp)); + *(rp+1) = (png_byte)(~(*(rp+1))); + rp+=4; + } + } +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swaps byte order on 16 bit depth images */ +void /* PRIVATE */ +png_do_swap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_swap"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop= row_info->width * row_info->channels; + + for (i = 0; i < istop; i++, rp += 2) + { + png_byte t = *rp; + *rp = *(rp + 1); + *(rp + 1) = t; + } + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +static PNG_CONST png_byte onebppswaptable[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + +static PNG_CONST png_byte twobppswaptable[256] = { + 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, + 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, + 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, + 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, + 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, + 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, + 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, + 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, + 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, + 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, + 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, + 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, + 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, + 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, + 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, + 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, + 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, + 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, + 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, + 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, + 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, + 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, + 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, + 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, + 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, + 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, + 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, + 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, + 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, + 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, + 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, + 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF +}; + +static PNG_CONST png_byte fourbppswaptable[256] = { + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, + 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, + 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, + 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, + 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, + 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, + 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, + 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, + 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, + 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, + 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, + 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, + 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, + 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, + 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, + 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, + 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, + 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, + 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, + 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, + 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, + 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, + 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, + 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, + 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, + 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, + 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF +}; + +/* Swaps pixel packing order within bytes */ +void /* PRIVATE */ +png_do_packswap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_packswap"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + row_info->bit_depth < 8) + { + png_bytep rp, end, table; + + end = row + row_info->rowbytes; + + if (row_info->bit_depth == 1) + table = (png_bytep)onebppswaptable; + else if (row_info->bit_depth == 2) + table = (png_bytep)twobppswaptable; + else if (row_info->bit_depth == 4) + table = (png_bytep)fourbppswaptable; + else + return; + + for (rp = row; rp < end; rp++) + *rp = table[*rp]; + } +} +#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */ + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +/* Remove filler or alpha byte(s) */ +void /* PRIVATE */ +png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) +{ + png_debug(1, "in png_do_strip_filler"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL) +#endif + { + png_bytep sp=row; + png_bytep dp=row; + png_uint_32 row_width=row_info->width; + png_uint_32 i; + + if ((row_info->color_type == PNG_COLOR_TYPE_RGB || + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 4) + { + if (row_info->bit_depth == 8) + { + /* This converts from RGBX or RGBA to RGB */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + dp+=3; sp+=4; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp++; + } + } + /* This converts from XRGB or ARGB to RGB */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */ + sp += 8; dp += 6; + for (i = 1; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */ + for (i = 0; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + sp+=2; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 48; + row_info->rowbytes = row_width * 6; + } + row_info->channels = 3; + } + else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY || + (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 2) + { + if (row_info->bit_depth == 8) + { + /* This converts from GX or GA to G */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + for (i = 0; i < row_width; i++) + { + *dp++ = *sp++; + sp++; + } + } + /* This converts from XG or AG to G */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from GGXX or GGAA to GG */ + sp += 4; dp += 2; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXGG or AAGG to GG */ + for (i = 0; i < row_width; i++) + { + sp += 2; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + row_info->channels = 1; + } + if (flags & PNG_FLAG_STRIP_ALPHA) + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + } +} +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Swaps red and blue bytes within a pixel */ +void /* PRIVATE */ +png_do_bgr(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_bgr"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 3) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 4) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + } + else if (row_info->bit_depth == 16) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 6) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 8) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + } + } +} +#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +void PNGAPI +png_set_user_transform_info(png_structp png_ptr, png_voidp + user_transform_ptr, int user_transform_depth, int user_transform_channels) +{ + png_debug(1, "in png_set_user_transform_info"); + + if (png_ptr == NULL) + return; +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + png_ptr->user_transform_ptr = user_transform_ptr; + png_ptr->user_transform_depth = (png_byte)user_transform_depth; + png_ptr->user_transform_channels = (png_byte)user_transform_channels; +#else + if (user_transform_ptr || user_transform_depth || user_transform_channels) + png_warning(png_ptr, + "This version of libpng does not support user transform info"); +#endif +} +#endif + +/* This function returns a pointer to the user_transform_ptr associated with + * the user transform functions. The application should free any memory + * associated with this pointer before png_write_destroy and png_read_destroy + * are called. + */ +png_voidp PNGAPI +png_get_user_transform_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + return ((png_voidp)png_ptr->user_transform_ptr); +#else + return (NULL); +#endif +} +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngvcrd.c b/bazaar/plugin/gdal/frmts/png/libpng/pngvcrd.c new file mode 100644 index 000000000..f55313884 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngvcrd.c @@ -0,0 +1,12 @@ +/* pngvcrd.c + * + * Last changed in libpng 1.2.48 [March 8, 2012] + * Copyright (c) 1998-2012 Glenn Randers-Pehrson + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This nearly empty file is for use by configure's compilation test. The + * remainder of the file was removed from libpng-1.2.20. + */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngwio.c b/bazaar/plugin/gdal/frmts/png/libpng/pngwio.c new file mode 100644 index 000000000..44e5ea91c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngwio.c @@ -0,0 +1,260 @@ + +/* pngwio.c - functions for data output + * + * Last changed in libpng 1.2.41 [December 3, 2009] + * Copyright (c) 1998-2009 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all output. Users who need + * special handling are expected to write functions that have the same + * arguments as these and perform similar functions, but that possibly + * use different output methods. Note that you shouldn't change these + * functions, but rather write replacement functions and then change + * them at run time with png_set_write_fn(...). + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Write the data to whatever output you are using. The default routine + * writes to a file pointer. Note that this routine sometimes gets called + * with very small lengths, so you should implement some kind of simple + * buffering if you are using unbuffered writes. This should never be asked + * to write more than 64K on a 16 bit machine. + */ + +void /* PRIVATE */ +png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + if (png_ptr->write_data_fn != NULL ) + (*(png_ptr->write_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL write function"); +} + +#ifdef PNG_STDIO_SUPPORTED +/* This is the function that does the actual writing of data. If you are + * not writing to a standard C stream, you should create a replacement + * write_data function and use it at run time with png_set_write_fn(), rather + * than changing the library. + */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + if (png_ptr == NULL) + return; +#ifdef _WIN32_WCE + if ( !WriteFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); +#endif + if (check != length) + png_error(png_ptr, "Write Error"); +} +#else +/* This is the model-independent version. Since the standard I/O library + * can't handle far buffers in the medium and small models, we have to copy + * the data. + */ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + png_FILE_p io_ptr; + + if (png_ptr == NULL) + return; + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { +#ifdef _WIN32_WCE + if ( !WriteFile(io_ptr, near_data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(near_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* Copy far buffer to near buffer */ +#ifdef _WIN32_WCE + if ( !WriteFile(io_ptr, buf, written, &err, NULL) ) + err = 0; +#else + err = fwrite(buf, 1, written, io_ptr); +#endif + if (err != written) + break; + + else + check += err; + + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + png_error(png_ptr, "Write Error"); +} + +#endif +#endif + +/* This function is called to output any data pending writing (normally + * to disk). After png_flush is called, there should be no data pending + * writing in any buffers. + */ +#ifdef PNG_WRITE_FLUSH_SUPPORTED +void /* PRIVATE */ +png_flush(png_structp png_ptr) +{ + if (png_ptr->output_flush_fn != NULL) + (*(png_ptr->output_flush_fn))(png_ptr); +} + +#ifdef PNG_STDIO_SUPPORTED +void PNGAPI +png_default_flush(png_structp png_ptr) +{ +#ifndef _WIN32_WCE + png_FILE_p io_ptr; +#endif + if (png_ptr == NULL) + return; +#ifndef _WIN32_WCE + io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); + fflush(io_ptr); +#endif +} +#endif +#endif + +/* This function allows the application to supply new output functions for + * libpng if standard C streams aren't being used. + * + * This function takes as its arguments: + * png_ptr - pointer to a png output data structure + * io_ptr - pointer to user supplied structure containing info about + * the output functions. May be NULL. + * write_data_fn - pointer to a new output function that takes as its + * arguments a pointer to a png_struct, a pointer to + * data to be written, and a 32-bit unsigned int that is + * the number of bytes to be written. The new write + * function should call png_error(png_ptr, "Error msg") + * to exit and output any fatal error messages. May be + * NULL, in which case libpng's default function will + * be used. + * flush_data_fn - pointer to a new flush function that takes as its + * arguments a pointer to a png_struct. After a call to + * the flush function, there should be no data in any buffers + * or pending transmission. If the output method doesn't do + * any buffering of output, a function prototype must still be + * supplied although it doesn't have to do anything. If + * PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile + * time, output_flush_fn will be ignored, although it must be + * supplied for compatibility. May be NULL, in which case + * libpng's default function will be used, if + * PNG_WRITE_FLUSH_SUPPORTED is defined. This is not + * a good idea if io_ptr does not point to a standard + * *FILE structure. + */ +void PNGAPI +png_set_write_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->io_ptr = io_ptr; + +#ifdef PNG_STDIO_SUPPORTED + if (write_data_fn != NULL) + png_ptr->write_data_fn = write_data_fn; + + else + png_ptr->write_data_fn = png_default_write_data; +#else + png_ptr->write_data_fn = write_data_fn; +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +#ifdef PNG_STDIO_SUPPORTED + if (output_flush_fn != NULL) + png_ptr->output_flush_fn = output_flush_fn; + + else + png_ptr->output_flush_fn = png_default_flush; +#else + png_ptr->output_flush_fn = output_flush_fn; +#endif +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + + /* It is an error to read while writing a png file */ + if (png_ptr->read_data_fn != NULL) + { + png_ptr->read_data_fn = NULL; + png_warning(png_ptr, + "Attempted to set both read_data_fn and write_data_fn in"); + png_warning(png_ptr, + "the same structure. Resetting read_data_fn to NULL."); + } +} + +#ifdef USE_FAR_KEYWORD +#ifdef _MSC_VER +void *png_far_to_near(png_structp png_ptr, png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + FP_OFF(near_ptr) = FP_OFF(ptr); + far_ptr = (void FAR *)near_ptr; + + if (check != 0) + if (FP_SEG(ptr) != FP_SEG(far_ptr)) + png_error(png_ptr, "segment lost in conversion"); + + return(near_ptr); +} +# else +void *png_far_to_near(png_structp png_ptr, png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + near_ptr = (void FAR *)ptr; + far_ptr = (void FAR *)near_ptr; + + if (check != 0) + if (far_ptr != ptr) + png_error(png_ptr, "segment lost in conversion"); + + return(near_ptr); +} +# endif +# endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngwrite.c b/bazaar/plugin/gdal/frmts/png/libpng/pngwrite.c new file mode 100644 index 000000000..894a9843d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngwrite.c @@ -0,0 +1,1602 @@ + +/* pngwrite.c - general routines to write a PNG file + * + * Last changed in libpng 1.2.52 [November 20, 2014] + * Copyright (c) 1998-2014 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* Get internal access to png.h */ +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Writes all the PNG information. This is the suggested way to use the + * library. If you have a new chunk to add, make a function to write it, + * and put it in the correct location here. If you want the chunk written + * after the image data, put it in png_write_end(). I strongly encourage + * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing + * the chunk, as that will keep the code from breaking if you want to just + * write a plain PNG file. If you have long comments, I suggest writing + * them in png_write_end(), and compressing them. + */ +void PNGAPI +png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_info_before_PLTE"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + { + /* Write PNG signature */ + png_write_sig(png_ptr); +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \ + (png_ptr->mng_features_permitted)) + { + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + png_ptr->mng_features_permitted = 0; + } +#endif + /* Write IHDR information. */ + png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, + info_ptr->filter_type, +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + info_ptr->interlace_type); +#else + 0); +#endif + /* The rest of these check to see if the valid field has the appropriate + * flag set, and if it does, writes the chunk. + */ +#ifdef PNG_WRITE_gAMA_SUPPORTED + if (info_ptr->valid & PNG_INFO_gAMA) + { +# ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_gAMA(png_ptr, info_ptr->gamma); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma); +# endif +#endif + } +#endif +#ifdef PNG_WRITE_sRGB_SUPPORTED + if (info_ptr->valid & PNG_INFO_sRGB) + png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent); +#endif +#ifdef PNG_WRITE_iCCP_SUPPORTED + if (info_ptr->valid & PNG_INFO_iCCP) + png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE, + info_ptr->iccp_profile, (int)info_ptr->iccp_proflen); +#endif +#ifdef PNG_WRITE_sBIT_SUPPORTED + if (info_ptr->valid & PNG_INFO_sBIT) + png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); +#endif +#ifdef PNG_WRITE_cHRM_SUPPORTED + if (info_ptr->valid & PNG_INFO_cHRM) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_cHRM(png_ptr, + info_ptr->x_white, info_ptr->y_white, + info_ptr->x_red, info_ptr->y_red, + info_ptr->x_green, info_ptr->y_green, + info_ptr->x_blue, info_ptr->y_blue); +#else +# ifdef PNG_FIXED_POINT_SUPPORTED + png_write_cHRM_fixed(png_ptr, + info_ptr->int_x_white, info_ptr->int_y_white, + info_ptr->int_x_red, info_ptr->int_y_red, + info_ptr->int_x_green, info_ptr->int_y_green, + info_ptr->int_x_blue, info_ptr->int_y_blue); +# endif +#endif + } +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep = png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && !(up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + if (up->size == 0) + png_warning(png_ptr, "Writing zero-length unknown chunk"); + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; + } +} + +void PNGAPI +png_write_info(png_structp png_ptr, png_infop info_ptr) +{ +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) + int i; +#endif + + png_debug(1, "in png_write_info"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_write_info_before_PLTE(png_ptr, info_ptr); + + if (info_ptr->valid & PNG_INFO_PLTE) + png_write_PLTE(png_ptr, info_ptr->palette, + (png_uint_32)info_ptr->num_palette); + else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Valid palette required for paletted images"); + +#ifdef PNG_WRITE_tRNS_SUPPORTED + if (info_ptr->valid & PNG_INFO_tRNS) + { +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel (in tRNS) */ + if ((png_ptr->transformations & PNG_INVERT_ALPHA) && + info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + int j; + for (j = 0; j<(int)info_ptr->num_trans; j++) + info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]); + } +#endif + png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values), + info_ptr->num_trans, info_ptr->color_type); + } +#endif +#ifdef PNG_WRITE_bKGD_SUPPORTED + if (info_ptr->valid & PNG_INFO_bKGD) + png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); +#endif +#ifdef PNG_WRITE_hIST_SUPPORTED + if (info_ptr->valid & PNG_INFO_hIST) + png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); +#endif +#ifdef PNG_WRITE_oFFs_SUPPORTED + if (info_ptr->valid & PNG_INFO_oFFs) + png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, + info_ptr->offset_unit_type); +#endif +#ifdef PNG_WRITE_pCAL_SUPPORTED + if (info_ptr->valid & PNG_INFO_pCAL) + png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); +#endif + +#ifdef PNG_sCAL_SUPPORTED + if (info_ptr->valid & PNG_INFO_sCAL) +#ifdef PNG_WRITE_sCAL_SUPPORTED +#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) + png_write_sCAL(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_pixel_width, info_ptr->scal_pixel_height); +#else /* !FLOATING_POINT */ +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_s_width, info_ptr->scal_s_height); +#endif /* FIXED_POINT */ +#endif /* FLOATING_POINT */ +#else /* !WRITE_sCAL */ + png_warning(png_ptr, + "png_write_sCAL not supported; sCAL chunk not written."); +#endif /* WRITE_sCAL */ +#endif /* sCAL */ + +#ifdef PNG_WRITE_pHYs_SUPPORTED + if (info_ptr->valid & PNG_INFO_pHYs) + png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +#endif /* pHYs */ + +#ifdef PNG_WRITE_tIME_SUPPORTED + if (info_ptr->valid & PNG_INFO_tIME) + { + png_write_tIME(png_ptr, &(info_ptr->mod_time)); + png_ptr->mode |= PNG_WROTE_tIME; + } +#endif /* tIME */ + +#ifdef PNG_WRITE_sPLT_SUPPORTED + if (info_ptr->valid & PNG_INFO_sPLT) + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); +#endif /* sPLT */ + +#ifdef PNG_WRITE_TEXT_SUPPORTED + /* Check to see if we need to write text chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing header text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* An internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + /* If we want a compressed text chunk */ + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) + { +#ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, + 0); + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; +#else + /* Can't get here */ + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + } + } +#endif /* tEXt */ + +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep = png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + !(up->location & PNG_AFTER_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif +} + +/* Writes the end of the PNG file. If you don't want to write comments or + * time information, you can pass NULL for info. If you already wrote these + * in png_write_info(), do not write them again here. If you have long + * comments, I suggest writing them here, and compressing them. + */ +void PNGAPI +png_write_end(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_end"); + + if (png_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "No IDATs written into file"); + + /* See if user wants us to write information chunks */ + if (info_ptr != NULL) + { +#ifdef PNG_WRITE_TEXT_SUPPORTED + int i; /* local index variable */ +#endif +#ifdef PNG_WRITE_tIME_SUPPORTED + /* Check to see if user has supplied a time chunk */ + if ((info_ptr->valid & PNG_INFO_tIME) && + !(png_ptr->mode & PNG_WROTE_tIME)) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +#endif +#ifdef PNG_WRITE_TEXT_SUPPORTED + /* Loop through comment chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing trailer text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* An internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) + { +#ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep = png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_AFTER_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + } + + png_ptr->mode |= PNG_AFTER_IDAT; + + /* Write end of PNG file */ + png_write_IEND(png_ptr); + /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03, + * and restored again in libpng-1.2.30, may cause some applications that + * do not set png_ptr->output_flush_fn to crash. If your application + * experiences a problem, please try building libpng with + * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to + * png-mng-implement at lists.sf.net . + */ +#ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED + png_flush(png_ptr); +# endif +#endif +} + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* "tm" structure is not supported on WindowsCE */ +void PNGAPI +png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime) +{ + png_debug(1, "in png_convert_from_struct_tm"); + + ptime->year = (png_uint_16)(1900 + ttime->tm_year); + ptime->month = (png_byte)(ttime->tm_mon + 1); + ptime->day = (png_byte)ttime->tm_mday; + ptime->hour = (png_byte)ttime->tm_hour; + ptime->minute = (png_byte)ttime->tm_min; + ptime->second = (png_byte)ttime->tm_sec; +} + +void PNGAPI +png_convert_from_time_t(png_timep ptime, time_t ttime) +{ + struct tm *tbuf; + + png_debug(1, "in png_convert_from_time_t"); + + tbuf = gmtime(&ttime); + png_convert_from_struct_tm(ptime, tbuf); +} +#endif + +/* Initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ +#ifdef PNG_SETJMP_SUPPORTED + volatile +#endif + png_structp png_ptr; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + int i; + + png_debug(1, "in png_create_write_struct"); + +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif /* PNG_USER_MEM_SUPPORTED */ + if (png_ptr == NULL) + return (NULL); + + /* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max = PNG_USER_WIDTH_MAX; + png_ptr->user_height_max = PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, + (png_free_ptr)free_fn, (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf, jmpbuf, png_sizeof(jmp_buf)); +#endif +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif /* PNG_USER_MEM_SUPPORTED */ + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + if (user_png_ver != NULL) + { + int found_dots = 0; + i = -1; + + do + { + i++; + if (user_png_ver[i] != PNG_LIBPNG_VER_STRING[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + if (user_png_ver[i] == '.') + found_dots++; + } while (found_dots < 2 && user_png_ver[i] != 0 && + PNG_LIBPNG_VER_STRING[i] != 0); + } + else + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* Initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Applications that neglect to set up their own setjmp() and then + * encounter a png_error() will longjmp here. Since the jmpbuf is + * then meaningless we abort instead of returning. + */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf, jmpbuf, png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif + return (png_ptr); +} + +/* Initialize png_ptr structure, and allocate any memory needed */ +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Deprecated. */ +#undef png_write_init +void PNGAPI +png_write_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_write_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} + +void PNGAPI +png_write_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ + if (png_ptr == NULL) return; +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + if (png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn = NULL; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if (png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn = NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for writing is" + " too small."); + } + if (png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn = NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + png_error(png_ptr, + "The info struct allocated by the application for writing is" + " too small."); + } + png_write_init_3(&png_ptr, user_png_ver, png_struct_size); +} +#endif /* PNG_1_0_X || PNG_1_2_X */ + + +void PNGAPI +png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ + png_structp png_ptr = *ptr_ptr; +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + + int i = 0; + + if (png_ptr == NULL) + return; + + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn = NULL; + png_warning(png_ptr, + "Application uses deprecated png_write_init() and should be recompiled."); +#endif + } + i++; + } while (png_libpng_ver[i] != 0 && user_png_ver[i] != 0); + + png_debug(1, "in png_write_init_3"); + +#ifdef PNG_SETJMP_SUPPORTED + /* Save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf)); +#endif + + if (png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + *ptr_ptr = png_ptr; + } + + /* Reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof(png_struct)); + + /* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max = PNG_USER_WIDTH_MAX; + png_ptr->user_height_max = PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf)); +#endif + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + + /* Initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif +} + +/* Write a few rows of image data. If the image is interlaced, + * either you will have to write the 7 sub images, or, if you + * have called png_set_interlace_handling(), you will have to + * "write" the image seven times. + */ +void PNGAPI +png_write_rows(png_structp png_ptr, png_bytepp row, + png_uint_32 num_rows) +{ + png_uint_32 i; /* row counter */ + png_bytepp rp; /* row pointer */ + + png_debug(1, "in png_write_rows"); + + if (png_ptr == NULL) + return; + + /* Loop through the rows */ + for (i = 0, rp = row; i < num_rows; i++, rp++) + { + png_write_row(png_ptr, *rp); + } +} + +/* Write the image. You only need to call this function once, even + * if you are writing an interlaced image. + */ +void PNGAPI +png_write_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i; /* row index */ + int pass, num_pass; /* pass variables */ + png_bytepp rp; /* points to current row */ + + if (png_ptr == NULL) + return; + + png_debug(1, "in png_write_image"); + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Initialize interlace handling. If image is not interlaced, + * this will set pass to 1 + */ + num_pass = png_set_interlace_handling(png_ptr); +#else + num_pass = 1; +#endif + /* Loop through passes */ + for (pass = 0; pass < num_pass; pass++) + { + /* Loop through image */ + for (i = 0, rp = image; i < png_ptr->height; i++, rp++) + { + png_write_row(png_ptr, *rp); + } + } +} + +/* Called by user to write a row of image data */ +void PNGAPI +png_write_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr == NULL) + return; + + png_debug2(1, "in png_write_row (row %ld, pass %d)", + png_ptr->row_number, png_ptr->pass); + + /* Initialize transformations and other stuff if first time */ + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* Make sure we wrote the header info */ + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + png_error(png_ptr, + "png_write_info was never called before png_write_row."); + + /* Check for transforms that have been set but were defined out */ +#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, + "PNG_WRITE_INVERT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, + "PNG_WRITE_FILLER_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ + defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, + "PNG_WRITE_PACKSWAP_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined."); +#endif + + png_write_start_row(png_ptr); + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced and not interested in row, return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 0x03) != 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 0x01)) + { + png_write_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + /* Set up row info for transformations */ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->usr_width; + png_ptr->row_info.channels = png_ptr->usr_channels; + png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth; + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_debug1(3, "row_info->color_type = %d", png_ptr->row_info.color_type); + png_debug1(3, "row_info->width = %lu", png_ptr->row_info.width); + png_debug1(3, "row_info->channels = %d", png_ptr->row_info.channels); + png_debug1(3, "row_info->bit_depth = %d", png_ptr->row_info.bit_depth); + png_debug1(3, "row_info->pixel_depth = %d", png_ptr->row_info.pixel_depth); + png_debug1(3, "row_info->rowbytes = %lu", png_ptr->row_info.rowbytes); + + /* Copy user's row into buffer, leaving room for filter byte. */ + png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row, + png_ptr->row_info.rowbytes); + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Handle interlacing */ + if (png_ptr->interlaced && png_ptr->pass < 6 && + (png_ptr->transformations & PNG_INTERLACE)) + { + png_do_write_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass); + /* This should always get caught above, but still ... */ + if (!(png_ptr->row_info.width)) + { + png_write_finish_row(png_ptr); + return; + } + } +#endif + + /* Handle other transformations */ + if (png_ptr->transformations) + png_do_write_transformations(png_ptr); + +#ifdef PNG_MNG_FEATURES_SUPPORTED + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + /* Find a filter if necessary, filter the row and write it out. */ + png_write_find_filter(png_ptr, &(png_ptr->row_info)); + + if (png_ptr->write_row_fn != NULL) + (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set the automatic flush interval or 0 to turn flushing off */ +void PNGAPI +png_set_flush(png_structp png_ptr, int nrows) +{ + png_debug(1, "in png_set_flush"); + + if (png_ptr == NULL) + return; + png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); +} + +/* Flush the current output buffers now */ +void PNGAPI +png_write_flush(png_structp png_ptr) +{ + int wrote_IDAT; + + png_debug(1, "in png_write_flush"); + + if (png_ptr == NULL) + return; + /* We have already written out all of the data */ + if (png_ptr->row_number >= png_ptr->num_rows) + return; + + do + { + int ret; + + /* Compress the data */ + ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH); + wrote_IDAT = 0; + + /* Check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + if (!(png_ptr->zstream.avail_out)) + { + /* Write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + wrote_IDAT = 1; + } + } while(wrote_IDAT == 1); + + /* If there is any data left to be output, write it into a new IDAT */ + if (png_ptr->zbuf_size != png_ptr->zstream.avail_out) + { + /* Write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + png_ptr->flush_rows = 0; + png_flush(png_ptr); +} +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + +/* Free all memory used by the write */ +void PNGAPI +png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn = NULL; + png_voidp mem_ptr = NULL; +#endif + + png_debug(1, "in png_destroy_write_struct"); + + if (png_ptr_ptr != NULL) + { + png_ptr = *png_ptr_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + } + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr != NULL) + { + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; + } +#endif + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + if (png_ptr != NULL) + { + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list = NULL; + png_ptr->num_chunk_list = 0; + } +#endif + } + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { + png_write_destroy(png_ptr); +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + + +/* Free any memory used in png_ptr struct (old method) */ +void /* PRIVATE */ +png_write_destroy(png_structp png_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* Save jump buffer */ +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_write_destroy"); + + /* Free any memory zlib uses */ + deflateEnd(&png_ptr->zstream); + + /* Free our memory. png_free checks NULL for us. */ + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->row_buf); +#ifdef PNG_WRITE_FILTER_SUPPORTED + png_free(png_ptr, png_ptr->prev_row); + png_free(png_ptr, png_ptr->sub_row); + png_free(png_ptr, png_ptr->up_row); + png_free(png_ptr, png_ptr->avg_row); + png_free(png_ptr, png_ptr->paeth_row); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + png_free(png_ptr, png_ptr->time_buffer); +#endif + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_free(png_ptr, png_ptr->prev_filters); + png_free(png_ptr, png_ptr->filter_weights); + png_free(png_ptr, png_ptr->inv_filter_weights); + png_free(png_ptr, png_ptr->filter_costs); + png_free(png_ptr, png_ptr->inv_filter_costs); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Reset structure */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof(png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf)); +#endif +} + +/* Allow the application to select one or more row filters to use. */ +void PNGAPI +png_set_filter(png_structp png_ptr, int method, int filters) +{ + png_debug(1, "in png_set_filter"); + + if (png_ptr == NULL) + return; +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (method == PNG_INTRAPIXEL_DIFFERENCING)) + method = PNG_FILTER_TYPE_BASE; +#endif + if (method == PNG_FILTER_TYPE_BASE) + { + switch (filters & (PNG_ALL_FILTERS | 0x07)) + { +#ifdef PNG_WRITE_FILTER_SUPPORTED + case 5: + case 6: + case 7: png_warning(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + case PNG_FILTER_VALUE_NONE: + png_ptr->do_filter = PNG_FILTER_NONE; break; +#ifdef PNG_WRITE_FILTER_SUPPORTED + case PNG_FILTER_VALUE_SUB: + png_ptr->do_filter = PNG_FILTER_SUB; break; + case PNG_FILTER_VALUE_UP: + png_ptr->do_filter = PNG_FILTER_UP; break; + case PNG_FILTER_VALUE_AVG: + png_ptr->do_filter = PNG_FILTER_AVG; break; + case PNG_FILTER_VALUE_PAETH: + png_ptr->do_filter = PNG_FILTER_PAETH; break; + default: png_ptr->do_filter = (png_byte)filters; break; +#else + default: png_warning(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + } + + /* If we have allocated the row_buf, this means we have already started + * with the image and we should have allocated all of the filter buffers + * that have been selected. If prev_row isn't already allocated, then + * it is too late to start using the filters that need it, since we + * will be missing the data in the previous row. If an application + * wants to start and stop using particular filters during compression, + * it should start out with all of the filters, and then add and + * remove them after the start of compression. + */ + if (png_ptr->row_buf != NULL) + { +#ifdef PNG_WRITE_FILTER_SUPPORTED + if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Up filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_UP; + } + else + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Average filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_AVG; + } + else + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_PAETH) && + png_ptr->paeth_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Paeth filter after starting"); + png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); + } + else + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } + + if (png_ptr->do_filter == PNG_NO_FILTERS) +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + png_ptr->do_filter = PNG_FILTER_NONE; + } + } + else + png_error(png_ptr, "Unknown custom filter method"); +} + +/* This allows us to influence the way in which libpng chooses the "best" + * filter for the current scanline. While the "minimum-sum-of-absolute- + * differences metric is relatively fast and effective, there is some + * question as to whether it can be improved upon by trying to keep the + * filtered data going to zlib more consistent, hopefully resulting in + * better compression. + */ +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */ +void PNGAPI +png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, + int num_weights, png_doublep filter_weights, + png_doublep filter_costs) +{ + int i; + + png_debug(1, "in png_set_filter_heuristics"); + + if (png_ptr == NULL) + return; + if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST) + { + png_warning(png_ptr, "Unknown filter heuristic method"); + return; + } + + if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT) + { + heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; + } + + if (num_weights < 0 || filter_weights == NULL || + heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) + { + num_weights = 0; + } + + png_ptr->num_prev_filters = (png_byte)num_weights; + png_ptr->heuristic_method = (png_byte)heuristic_method; + + if (num_weights > 0) + { + if (png_ptr->prev_filters == NULL) + { + png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_byte) * num_weights)); + + /* To make sure that the weighting starts out fairly */ + for (i = 0; i < num_weights; i++) + { + png_ptr->prev_filters[i] = 255; + } + } + + if (png_ptr->filter_weights == NULL) + { + png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + + png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + for (i = 0; i < num_weights; i++) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + } + + for (i = 0; i < num_weights; i++) + { + if (filter_weights[i] < 0.0) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + else + { + png_ptr->inv_filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5); + png_ptr->filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5); + } + } + } + + /* If, in the future, there are other filter methods, this would + * need to be based on png_ptr->filter. + */ + if (png_ptr->filter_costs == NULL) + { + png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + } + + /* Here is where we set the relative costs of the different filters. We + * should take the desired compression level into account when setting + * the costs, so that Paeth, for instance, has a high relative cost at low + * compression levels, while it has a lower relative cost at higher + * compression settings. The filter types are in order of increasing + * relative cost, so it would be possible to do this with an algorithm. + */ + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + if (filter_costs == NULL || filter_costs[i] < 0.0) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + else if (filter_costs[i] >= 1.0) + { + png_ptr->inv_filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5); + png_ptr->filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5); + } + } +} +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +void PNGAPI +png_set_compression_level(png_structp png_ptr, int level) +{ + png_debug(1, "in png_set_compression_level"); + + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL; + png_ptr->zlib_level = level; +} + +void PNGAPI +png_set_compression_mem_level(png_structp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_compression_mem_level"); + + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL; + png_ptr->zlib_mem_level = mem_level; +} + +void PNGAPI +png_set_compression_strategy(png_structp png_ptr, int strategy) +{ + png_debug(1, "in png_set_compression_strategy"); + + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; +} + +void PNGAPI +png_set_compression_window_bits(png_structp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + if (window_bits > 15) + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + else if (window_bits < 8) + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); +#ifndef WBITS_8_OK + /* Avoid libpng bug with 256-byte windows */ + if (window_bits == 8) + { + png_warning(png_ptr, "Compression window is being reset to 512"); + window_bits = 9; + } +#endif + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS; + png_ptr->zlib_window_bits = window_bits; +} + +void PNGAPI +png_set_compression_method(png_structp png_ptr, int method) +{ + png_debug(1, "in png_set_compression_method"); + + if (png_ptr == NULL) + return; + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD; + png_ptr->zlib_method = method; +} + +void PNGAPI +png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->write_row_fn = write_row_fn; +} + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +void PNGAPI +png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + write_user_transform_fn) +{ + png_debug(1, "in png_set_write_user_transform_fn"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->write_user_transform_fn = write_user_transform_fn; +} +#endif + + +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_write_png(png_structp png_ptr, png_infop info_ptr, + int transforms, voidp params) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Write the file header information. */ + png_write_info(png_ptr, info_ptr); + + /* ------ these transformations don't touch the info structure ------- */ + +#ifdef PNG_WRITE_INVERT_SUPPORTED + /* Invert monochrome pixels */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED + /* Shift the pixels up to a legal bit depth and fill in + * as appropriate to correctly scale the image. + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && (info_ptr->valid & PNG_INFO_sBIT)) + png_set_shift(png_ptr, &info_ptr->sig_bit); +#endif + +#ifdef PNG_WRITE_PACK_SUPPORTED + /* Pack pixels into bytes */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED + /* Swap location of alpha bytes from ARGB to RGBA */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#ifdef PNG_WRITE_FILLER_SUPPORTED + /* Pack XRGB/RGBX/ARGB/RGBA into * RGB (4 channels -> 3 channels) */ + if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) + png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); + else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); +#endif + +#ifdef PNG_WRITE_BGR_SUPPORTED + /* Flip BGR pixels to RGB */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#ifdef PNG_WRITE_SWAP_SUPPORTED + /* Swap bytes of 16-bit files to most significant byte first */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + +#ifdef PNG_WRITE_PACKSWAP_SUPPORTED + /* Swap bits of 1, 2, 4 bit packed pixel formats */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel from opacity to transparency */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* ----------------------- end of transformations ------------------- */ + + /* Write the bits */ + if (info_ptr->valid & PNG_INFO_IDAT) + png_write_image(png_ptr, info_ptr->row_pointers); + + /* It is REQUIRED to call this to finish writing the rest of the file */ + png_write_end(png_ptr, info_ptr); + + PNG_UNUSED(transforms) /* Quiet compiler warnings */ + PNG_UNUSED(params) +} +#endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngwtran.c b/bazaar/plugin/gdal/frmts/png/libpng/pngwtran.c new file mode 100644 index 000000000..0ce9b9b50 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngwtran.c @@ -0,0 +1,582 @@ + +/* pngwtran.c - transforms the data in a row for PNG writers + * + * Last changed in libpng 1.2.43 [February 25, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Transform the data according to the user's wishes. The order of + * transformations is significant. + */ +void /* PRIVATE */ +png_do_write_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_write_transformations"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + if (png_ptr->transformations & PNG_USER_TRANSFORM) + if (png_ptr->write_user_transform_fn != NULL) + (*(png_ptr->write_user_transform_fn)) /* User write transform + function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#endif +#ifdef PNG_WRITE_FILLER_SUPPORTED + if (png_ptr->transformations & PNG_FILLER) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->flags); +#endif +#ifdef PNG_WRITE_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_PACK_SUPPORTED + if (png_ptr->transformations & PNG_PACK) + png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->bit_depth); +#endif +#ifdef PNG_WRITE_SWAP_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_SHIFT_SUPPORTED + if (png_ptr->transformations & PNG_SHIFT) + png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_BGR_SUPPORTED + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_INVERT_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +} + +#ifdef PNG_WRITE_PACK_SUPPORTED +/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The + * row_info bit depth should be 8 (one pixel per byte). The channels + * should be 1 (this only happens on grayscale and paletted images). + */ +void /* PRIVATE */ +png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +{ + png_debug(1, "in png_do_pack"); + + if (row_info->bit_depth == 8 && +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + row_info->channels == 1) + { + switch ((int)bit_depth) + { + case 1: + { + png_bytep sp, dp; + int mask, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + mask = 0x80; + v = 0; + + for (i = 0; i < row_width; i++) + { + if (*sp != 0) + v |= mask; + sp++; + if (mask > 1) + mask >>= 1; + else + { + mask = 0x80; + *dp = (png_byte)v; + dp++; + v = 0; + } + } + if (mask != 0x80) + *dp = (png_byte)v; + break; + } + case 2: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 6; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x03); + v |= (value << shift); + if (shift == 0) + { + shift = 6; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 2; + sp++; + } + if (shift != 6) + *dp = (png_byte)v; + break; + } + case 4: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 4; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x0f); + v |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 4; + + sp++; + } + if (shift != 4) + *dp = (png_byte)v; + break; + } + } + row_info->bit_depth = (png_byte)bit_depth; + row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to row_info->bit_depth. Thus, if you had a row of + * bit depth 4, but the pixels only had values from 0 to 7, you + * would pass 3 as bit_depth, and this routine would translate the + * data to 0 to 15. + */ +void /* PRIVATE */ +png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) +{ + png_debug(1, "in png_do_shift"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL && +#else + if ( +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift_start[4], shift_dec[4]; + int channels = 0; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift_start[channels] = row_info->bit_depth - bit_depth->red; + shift_dec[channels] = bit_depth->red; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->green; + shift_dec[channels] = bit_depth->green; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->blue; + shift_dec[channels] = bit_depth->blue; + channels++; + } + else + { + shift_start[channels] = row_info->bit_depth - bit_depth->gray; + shift_dec[channels] = bit_depth->gray; + channels++; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift_start[channels] = row_info->bit_depth - bit_depth->alpha; + shift_dec[channels] = bit_depth->alpha; + channels++; + } + + /* With low row depths, could only be grayscale, so one channel */ + if (row_info->bit_depth < 8) + { + png_bytep bp = row; + png_uint_32 i; + png_byte mask; + png_uint_32 row_bytes = row_info->rowbytes; + + if (bit_depth->gray == 1 && row_info->bit_depth == 2) + mask = 0x55; + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) + mask = 0x11; + else + mask = 0xff; + + for (i = 0; i < row_bytes; i++, bp++) + { + png_uint_16 v; + int j; + + v = *bp; + *bp = 0; + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & mask); + } + } + } + else if (row_info->bit_depth == 8) + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (i = 0; i < istop; i++, bp++) + { + + png_uint_16 v; + int j; + int c = (int)(i%channels); + + v = *bp; + *bp = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & 0xff); + } + } + } + else + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (bp = row, i = 0; i < istop; i++) + { + int c = (int)(i%channels); + png_uint_16 value, v; + int j; + + v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1)); + value = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); + else + value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); + } + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + } + } +} +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_swap_alpha"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from ARGB to RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AARRGGBB to RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from AG to GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AAGG to GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + } +} +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_invert_alpha"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=3; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=6; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=2; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + } +} +#endif + +#ifdef PNG_MNG_FEATURES_SUPPORTED +/* Undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_write_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_intrapixel"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((*rp - *(rp+1))&0xff); + *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0 - s1) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/libpng/pngwutil.c b/bazaar/plugin/gdal/frmts/png/libpng/pngwutil.c new file mode 100644 index 000000000..c75f53eb7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/libpng/pngwutil.c @@ -0,0 +1,2832 @@ + +/* pngwutil.c - utilities to write a PNG file + * + * Last changed in libpng 1.2.43 [February 25, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Place a 32-bit number into a buffer in PNG byte order. We work + * with unsigned numbers for convenience, although one supported + * ancillary chunk uses signed (two's complement) numbers. + */ +void PNGAPI +png_save_uint_32(png_bytep buf, png_uint_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* The png_save_int_32 function assumes integers are stored in two's + * complement format. If this isn't the case, then this routine needs to + * be modified to write data in two's complement format. + */ +void PNGAPI +png_save_int_32(png_bytep buf, png_int_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +void PNGAPI +png_save_uint_16(png_bytep buf, unsigned int i) +{ + buf[0] = (png_byte)((i >> 8) & 0xff); + buf[1] = (png_byte)(i & 0xff); +} + +/* Simple function to write the signature. If we have already written + * the magic bytes of the signature, or more likely, the PNG stream is + * being embedded into another stream and doesn't need its own signature, + * we should call png_set_sig_bytes() to tell libpng how many of the + * bytes have already been written. + */ +void /* PRIVATE */ +png_write_sig(png_structp png_ptr) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + + /* Write the rest of the 8 byte signature */ + png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], + (png_size_t)(8 - png_ptr->sig_bytes)); + if (png_ptr->sig_bytes < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +/* Write a PNG chunk all at once. The type is an array of ASCII characters + * representing the chunk name. The array must be at least 4 bytes in + * length, and does not need to be null terminated. To be safe, pass the + * pre-defined chunk names here, and if you need a new one, define it + * where the others are defined. The length is the length of the data. + * All the data must be present. If that is not possible, use the + * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end() + * functions instead. + */ +void PNGAPI +png_write_chunk(png_structp png_ptr, png_bytep chunk_name, + png_bytep data, png_size_t length) +{ + if (png_ptr == NULL) + return; + png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length); + png_write_chunk_data(png_ptr, data, (png_size_t)length); + png_write_chunk_end(png_ptr); +} + +/* Write the start of a PNG chunk. The type is the chunk type. + * The total_length is the sum of the lengths of all the data you will be + * passing in png_write_chunk_data(). + */ +void PNGAPI +png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name, + png_uint_32 length) +{ + png_byte buf[8]; + + png_debug2(0, "Writing %s chunk, length = %lu", chunk_name, + (unsigned long)length); + + if (png_ptr == NULL) + return; + + + /* Write the length and the chunk name */ + png_save_uint_32(buf, length); + png_memcpy(buf + 4, chunk_name, 4); + png_write_data(png_ptr, buf, (png_size_t)8); + /* Put the chunk name into png_ptr->chunk_name */ + png_memcpy(png_ptr->chunk_name, chunk_name, 4); + /* Reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, chunk_name, (png_size_t)4); +} + +/* Write the data of a PNG chunk started with png_write_chunk_start(). + * Note that multiple calls to this function are allowed, and that the + * sum of the lengths from these calls *must* add up to the total_length + * given to png_write_chunk_start(). + */ +void PNGAPI +png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + /* Write the data, and run the CRC over it */ + if (png_ptr == NULL) + return; + if (data != NULL && length > 0) + { + png_write_data(png_ptr, data, length); + /* Update the CRC after writing the data, + * in case that the user I/O routine alters it. + */ + png_calculate_crc(png_ptr, data, length); + } +} + +/* Finish a chunk started with png_write_chunk_start(). */ +void PNGAPI +png_write_chunk_end(png_structp png_ptr) +{ + png_byte buf[4]; + + if (png_ptr == NULL) return; + + /* Write the crc in a single operation */ + png_save_uint_32(buf, png_ptr->crc); + + png_write_data(png_ptr, buf, (png_size_t)4); +} + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED) +/* This pair of functions encapsulates the operation of (a) compressing a + * text string, and (b) issuing it later as a series of chunk data writes. + * The compression_state structure is shared context for these functions + * set up by the caller in order to make the whole mess thread-safe. + */ + +typedef struct +{ + char *input; /* The uncompressed input data */ + int input_len; /* Its length */ + int num_output_ptr; /* Number of output pointers used */ + int max_output_ptr; /* Size of output_ptr */ + png_charpp output_ptr; /* Array of pointers to output */ +} compression_state; + +/* Compress given text into storage in the png_ptr structure */ +static int /* PRIVATE */ +png_text_compress(png_structp png_ptr, + png_charp text, png_size_t text_len, int compression, + compression_state *comp) +{ + int ret; + + comp->num_output_ptr = 0; + comp->max_output_ptr = 0; + comp->output_ptr = NULL; + comp->input = NULL; + comp->input_len = 0; + + /* We may just want to pass the text right through */ + if (compression == PNG_TEXT_COMPRESSION_NONE) + { + comp->input = text; + comp->input_len = text_len; + return((int)text_len); + } + + if (compression >= PNG_TEXT_COMPRESSION_LAST) + { +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + char msg[50]; + png_snprintf(msg, 50, "Unknown compression type %d", compression); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "Unknown compression type"); +#endif + } + + /* We can't write the chunk until we find out how much data we have, + * which means we need to run the compressor first and save the + * output. This shouldn't be a problem, as the vast majority of + * comments should be reasonable, but we will set up an array of + * malloc'd pointers to be sure. + * + * If we knew the application was well behaved, we could simplify this + * greatly by assuming we can always malloc an output buffer large + * enough to hold the compressed text ((1001 * text_len / 1000) + 12) + * and malloc this directly. The only time this would be a bad idea is + * if we can't malloc more than 64K and we have 64K of random input + * data, or if the input string is incredibly large (although this + * wouldn't cause a failure, just a slowdown due to swapping). + */ + + /* Set up the compression buffers */ + png_ptr->zstream.avail_in = (uInt)text_len; + png_ptr->zstream.next_in = (Bytef *)text; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf; + + /* This is the same compression loop as in png_write_row() */ + do + { + /* Compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + if (ret != Z_OK) + { + /* Error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + /* Check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* Make sure the output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32) + (comp->max_output_ptr * png_sizeof(png_charpp))); + png_memcpy(comp->output_ptr, old_ptr, old_max + * png_sizeof(png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32) + (comp->max_output_ptr * png_sizeof(png_charp))); + } + + /* Save the data */ + comp->output_ptr[comp->num_output_ptr] = + (png_charp)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + /* Continue until we don't have any more to compress */ + } while (png_ptr->zstream.avail_in); + + /* Finish the compression */ + do + { + /* Tell zlib we are finished */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + + if (ret == Z_OK) + { + /* Check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* Check to make sure our output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + /* This could be optimized to realloc() */ + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof(png_charp))); + png_memcpy(comp->output_ptr, old_ptr, + old_max * png_sizeof(png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof(png_charp))); + } + + /* Save the data */ + comp->output_ptr[comp->num_output_ptr] = + (png_charp)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer pointers */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + } + else if (ret != Z_STREAM_END) + { + /* We got an error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* Text length is number of buffers plus last buffer */ + text_len = png_ptr->zbuf_size * comp->num_output_ptr; + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out; + + return((int)text_len); +} + +/* Ship the compressed text out via chunk writes */ +static void /* PRIVATE */ +png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) +{ + int i; + + /* Handle the no-compression case */ + if (comp->input) + { + png_write_chunk_data(png_ptr, (png_bytep)comp->input, + (png_size_t)comp->input_len); + return; + } + + /* Write saved output buffers, if any */ + for (i = 0; i < comp->num_output_ptr; i++) + { + png_write_chunk_data(png_ptr, (png_bytep)comp->output_ptr[i], + (png_size_t)png_ptr->zbuf_size); + png_free(png_ptr, comp->output_ptr[i]); + comp->output_ptr[i]=NULL; + } + if (comp->max_output_ptr != 0) + png_free(png_ptr, comp->output_ptr); + comp->output_ptr=NULL; + /* Write anything left in zbuf */ + if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size) + png_write_chunk_data(png_ptr, png_ptr->zbuf, + (png_size_t)(png_ptr->zbuf_size - png_ptr->zstream.avail_out)); + + /* Reset zlib for another zTXt/iTXt or image data */ + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} +#endif + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. Note that the rest of this code depends upon this + * information being correct. + */ +void /* PRIVATE */ +png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, + int bit_depth, int color_type, int compression_type, int filter_type, + int interlace_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; +#endif + int ret; + + png_byte buf[13]; /* Buffer to store the IHDR info */ + + png_debug(1, "in png_write_IHDR"); + + /* Check that we have valid input data from the application info */ + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: + case 16: png_ptr->channels = 1; break; + default: png_error(png_ptr, + "Invalid bit depth for grayscale image"); + } + break; + case PNG_COLOR_TYPE_RGB: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGB image"); + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_PALETTE: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: png_ptr->channels = 1; break; + default: png_error(png_ptr, "Invalid bit depth for paletted image"); + } + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGBA image"); + png_ptr->channels = 4; + break; + default: + png_error(png_ptr, "Invalid image color type specified"); + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Invalid compression type specified"); + compression_type = PNG_COMPRESSION_TYPE_BASE; + } + + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ( +#ifdef PNG_MNG_FEATURES_SUPPORTED + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && +#endif + filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Invalid filter type specified"); + filter_type = PNG_FILTER_TYPE_BASE; + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (interlace_type != PNG_INTERLACE_NONE && + interlace_type != PNG_INTERLACE_ADAM7) + { + png_warning(png_ptr, "Invalid interlace type specified"); + interlace_type = PNG_INTERLACE_ADAM7; + } +#else + interlace_type=PNG_INTERLACE_NONE; +#endif + + /* Save the relevent information */ + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->color_type = (png_byte)color_type; + png_ptr->interlaced = (png_byte)interlace_type; +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + png_ptr->width = width; + png_ptr->height = height; + + png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); + /* Set the usr info, so any transformations can modify it */ + png_ptr->usr_width = png_ptr->width; + png_ptr->usr_bit_depth = png_ptr->bit_depth; + png_ptr->usr_channels = png_ptr->channels; + + /* Pack the header information into the buffer */ + png_save_uint_32(buf, width); + png_save_uint_32(buf + 4, height); + buf[8] = (png_byte)bit_depth; + buf[9] = (png_byte)color_type; + buf[10] = (png_byte)compression_type; + buf[11] = (png_byte)filter_type; + buf[12] = (png_byte)interlace_type; + + /* Write the chunk */ + png_write_chunk(png_ptr, (png_bytep)png_IHDR, buf, (png_size_t)13); + + /* Initialize zlib with PNG info */ + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + if (!(png_ptr->do_filter)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + png_ptr->bit_depth < 8) + png_ptr->do_filter = PNG_FILTER_NONE; + else + png_ptr->do_filter = PNG_ALL_FILTERS; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)) + { + if (png_ptr->do_filter != PNG_FILTER_NONE) + png_ptr->zlib_strategy = Z_FILTERED; + else + png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL)) + png_ptr->zlib_level = Z_DEFAULT_COMPRESSION; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL)) + png_ptr->zlib_mem_level = 8; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS)) + png_ptr->zlib_window_bits = 15; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD)) + png_ptr->zlib_method = 8; + ret = deflateInit2(&png_ptr->zstream, png_ptr->zlib_level, + png_ptr->zlib_method, png_ptr->zlib_window_bits, + png_ptr->zlib_mem_level, png_ptr->zlib_strategy); + if (ret != Z_OK) + { + if (ret == Z_VERSION_ERROR) png_error(png_ptr, + "zlib failed to initialize compressor -- version error"); + if (ret == Z_STREAM_ERROR) png_error(png_ptr, + "zlib failed to initialize compressor -- stream error"); + if (ret == Z_MEM_ERROR) png_error(png_ptr, + "zlib failed to initialize compressor -- mem error"); + png_error(png_ptr, "zlib failed to initialize compressor"); + } + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + /* libpng is not interested in zstream.data_type */ + /* Set it to a predefined value, to avoid its evaluation inside zlib */ + png_ptr->zstream.data_type = Z_BINARY; + + png_ptr->mode = PNG_HAVE_IHDR; +} + +/* Write the palette. We are careful not to trust png_color to be in the + * correct order for PNG, so people can redefine it to any convenient + * structure. + */ +void /* PRIVATE */ +png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_PLTE; +#endif + png_uint_32 i; + png_colorp pal_ptr; + png_byte buf[3]; + + png_debug(1, "in png_write_PLTE"); + + if (( +#ifdef PNG_MNG_FEATURES_SUPPORTED + !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && +#endif + num_pal == 0) || num_pal > 256) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_error(png_ptr, "Invalid number of colors in palette"); + } + else + { + png_warning(png_ptr, "Invalid number of colors in palette"); + return; + } + } + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring request to write a PLTE chunk in grayscale PNG"); + return; + } + + png_ptr->num_palette = (png_uint_16)num_pal; + png_debug1(3, "num_palette = %d", png_ptr->num_palette); + + png_write_chunk_start(png_ptr, (png_bytep)png_PLTE, + (png_uint_32)(num_pal * 3)); +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) + { + buf[0] = pal_ptr->red; + buf[1] = pal_ptr->green; + buf[2] = pal_ptr->blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#else + /* This is a little slower but some buggy compilers need to do this + * instead + */ + pal_ptr=palette; + for (i = 0; i < num_pal; i++) + { + buf[0] = pal_ptr[i].red; + buf[1] = pal_ptr[i].green; + buf[2] = pal_ptr[i].blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#endif + png_write_chunk_end(png_ptr); + png_ptr->mode |= PNG_HAVE_PLTE; +} + +/* Write an IDAT chunk */ +void /* PRIVATE */ +png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + + png_debug(1, "in png_write_IDAT"); + + /* Optimize the CMF field in the zlib stream. */ + /* This hack of the zlib stream is compliant to the stream specification. */ + if (!(png_ptr->mode & PNG_HAVE_IDAT) && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + { + unsigned int z_cmf = data[0]; /* zlib compression method and flags */ + if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + { + /* Avoid memory underflows and multiplication overflows. + * + * The conditions below are practically always satisfied; + * however, they still must be checked. + */ + if (length >= 2 && + png_ptr->height < 16384 && png_ptr->width < 16384) + { + png_uint_32 uncompressed_idat_size = png_ptr->height * + ((png_ptr->width * + png_ptr->channels * png_ptr->bit_depth + 15) >> 3); + unsigned int z_cinfo = z_cmf >> 4; + unsigned int half_z_window_size = 1 << (z_cinfo + 7); + while (uncompressed_idat_size <= half_z_window_size && + half_z_window_size >= 256) + { + z_cinfo--; + half_z_window_size >>= 1; + } + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + if (data[0] != (png_byte)z_cmf) + { + data[0] = (png_byte)z_cmf; + data[1] &= 0xe0; + data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); + } + } + } + else + png_error(png_ptr, + "Invalid zlib compression method or flags in IDAT"); + } + + png_write_chunk(png_ptr, (png_bytep)png_IDAT, data, length); + png_ptr->mode |= PNG_HAVE_IDAT; +} + +/* Write an IEND chunk */ +void /* PRIVATE */ +png_write_IEND(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IEND; +#endif + + png_debug(1, "in png_write_IEND"); + + png_write_chunk(png_ptr, (png_bytep)png_IEND, png_bytep_NULL, + (png_size_t)0); + png_ptr->mode |= PNG_HAVE_IEND; +} + +#ifdef PNG_WRITE_gAMA_SUPPORTED +/* Write a gAMA chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA(png_structp png_ptr, double file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_uint_32 igamma; + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA"); + + /* file_gamma is saved in 1/100,000ths */ + igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5); + png_save_uint_32(buf, igamma); + png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA"); + + /* file_gamma is saved in 1/100,000ths */ + png_save_uint_32(buf, (png_uint_32)file_gamma); + png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); +} +#endif +#endif + +#ifdef PNG_WRITE_sRGB_SUPPORTED +/* Write a sRGB chunk */ +void /* PRIVATE */ +png_write_sRGB(png_structp png_ptr, int srgb_intent) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sRGB; +#endif + png_byte buf[1]; + + png_debug(1, "in png_write_sRGB"); + + if (srgb_intent >= PNG_sRGB_INTENT_LAST) + png_warning(png_ptr, + "Invalid sRGB rendering intent specified"); + buf[0]=(png_byte)srgb_intent; + png_write_chunk(png_ptr, (png_bytep)png_sRGB, buf, (png_size_t)1); +} +#endif + +#ifdef PNG_WRITE_iCCP_SUPPORTED +/* Write an iCCP chunk */ +void /* PRIVATE */ +png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type, + png_charp profile, int profile_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iCCP; +#endif + png_size_t name_len; + png_charp new_name; + compression_state comp; + int embedded_profile_len = 0; + + png_debug(1, "in png_write_iCCP"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if ((name_len = png_check_keyword(png_ptr, name, + &new_name)) == 0) + return; + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_warning(png_ptr, "Unknown compression type in iCCP chunk"); + + if (profile == NULL) + profile_len = 0; + + if (profile_len > 3) + embedded_profile_len = + ((*( (png_bytep)profile ))<<24) | + ((*( (png_bytep)profile + 1))<<16) | + ((*( (png_bytep)profile + 2))<< 8) | + ((*( (png_bytep)profile + 3)) ); + + if (embedded_profile_len < 0) + { + png_warning(png_ptr, + "Embedded profile length in iCCP chunk is negative"); + png_free(png_ptr, new_name); + return; + } + + if (profile_len < embedded_profile_len) + { + png_warning(png_ptr, + "Embedded profile length too large in iCCP chunk"); + png_free(png_ptr, new_name); + return; + } + + if (profile_len > embedded_profile_len) + { + png_warning(png_ptr, + "Truncating profile to actual length in iCCP chunk"); + profile_len = embedded_profile_len; + } + + if (profile_len) + profile_len = png_text_compress(png_ptr, profile, + (png_size_t)profile_len, PNG_COMPRESSION_TYPE_BASE, &comp); + + /* Make sure we include the NULL after the name and the compression type */ + png_write_chunk_start(png_ptr, (png_bytep)png_iCCP, + (png_uint_32)(name_len + profile_len + 2)); + new_name[name_len + 1] = 0x00; + png_write_chunk_data(png_ptr, (png_bytep)new_name, + (png_size_t)(name_len + 2)); + + if (profile_len) + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#ifdef PNG_WRITE_sPLT_SUPPORTED +/* Write a sPLT chunk */ +void /* PRIVATE */ +png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sPLT; +#endif + png_size_t name_len; + png_charp new_name; + png_byte entrybuf[10]; + int entry_size = (spalette->depth == 8 ? 6 : 10); + int palette_size = entry_size * spalette->nentries; + png_sPLT_entryp ep; +#ifndef PNG_POINTER_INDEXING_SUPPORTED + int i; +#endif + + png_debug(1, "in png_write_sPLT"); + + if ((name_len = png_check_keyword(png_ptr,spalette->name, &new_name))==0) + return; + + /* Make sure we include the NULL after the name */ + png_write_chunk_start(png_ptr, (png_bytep)png_sPLT, + (png_uint_32)(name_len + 2 + palette_size)); + png_write_chunk_data(png_ptr, (png_bytep)new_name, + (png_size_t)(name_len + 1)); + png_write_chunk_data(png_ptr, (png_bytep)&spalette->depth, (png_size_t)1); + + /* Loop through each palette entry, writing appropriately */ +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (ep = spalette->entries; epentries + spalette->nentries; ep++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep->red; + entrybuf[1] = (png_byte)ep->green; + entrybuf[2] = (png_byte)ep->blue; + entrybuf[3] = (png_byte)ep->alpha; + png_save_uint_16(entrybuf + 4, ep->frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep->red); + png_save_uint_16(entrybuf + 2, ep->green); + png_save_uint_16(entrybuf + 4, ep->blue); + png_save_uint_16(entrybuf + 6, ep->alpha); + png_save_uint_16(entrybuf + 8, ep->frequency); + } + png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + } +#else + ep=spalette->entries; + for (i=0; i>spalette->nentries; i++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep[i].red; + entrybuf[1] = (png_byte)ep[i].green; + entrybuf[2] = (png_byte)ep[i].blue; + entrybuf[3] = (png_byte)ep[i].alpha; + png_save_uint_16(entrybuf + 4, ep[i].frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep[i].red); + png_save_uint_16(entrybuf + 2, ep[i].green); + png_save_uint_16(entrybuf + 4, ep[i].blue); + png_save_uint_16(entrybuf + 6, ep[i].alpha); + png_save_uint_16(entrybuf + 8, ep[i].frequency); + } + png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + } +#endif + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#ifdef PNG_WRITE_sBIT_SUPPORTED +/* Write the sBIT chunk */ +void /* PRIVATE */ +png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sBIT; +#endif + png_byte buf[4]; + png_size_t size; + + png_debug(1, "in png_write_sBIT"); + + /* Make sure we don't depend upon the order of PNG_COLOR_8 */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + png_byte maxbits; + + maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : + png_ptr->usr_bit_depth); + if (sbit->red == 0 || sbit->red > maxbits || + sbit->green == 0 || sbit->green > maxbits || + sbit->blue == 0 || sbit->blue > maxbits) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->red; + buf[1] = sbit->green; + buf[2] = sbit->blue; + size = 3; + } + else + { + if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->gray; + size = 1; + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + { + if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[size++] = sbit->alpha; + } + + png_write_chunk(png_ptr, (png_bytep)png_sBIT, buf, size); +} +#endif + +#ifdef PNG_WRITE_cHRM_SUPPORTED +/* Write the cHRM chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM(png_structp png_ptr, double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + + png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, + int_green_x, int_green_y, int_blue_x, int_blue_y; + + png_debug(1, "in png_write_cHRM"); + + int_white_x = (png_uint_32)(white_x * 100000.0 + 0.5); + int_white_y = (png_uint_32)(white_y * 100000.0 + 0.5); + int_red_x = (png_uint_32)(red_x * 100000.0 + 0.5); + int_red_y = (png_uint_32)(red_y * 100000.0 + 0.5); + int_green_x = (png_uint_32)(green_x * 100000.0 + 0.5); + int_green_y = (png_uint_32)(green_y * 100000.0 + 0.5); + int_blue_x = (png_uint_32)(blue_x * 100000.0 + 0.5); + int_blue_y = (png_uint_32)(blue_y * 100000.0 + 0.5); + +#ifdef PNG_CHECK_cHRM_SUPPORTED + if (png_check_cHRM_fixed(png_ptr, int_white_x, int_white_y, + int_red_x, int_red_y, int_green_x, int_green_y, int_blue_x, int_blue_y)) +#endif + { + /* Each value is saved in 1/100,000ths */ + + png_save_uint_32(buf, int_white_x); + png_save_uint_32(buf + 4, int_white_y); + + png_save_uint_32(buf + 8, int_red_x); + png_save_uint_32(buf + 12, int_red_y); + + png_save_uint_32(buf + 16, int_green_x); + png_save_uint_32(buf + 20, int_green_y); + + png_save_uint_32(buf + 24, int_blue_x); + png_save_uint_32(buf + 28, int_blue_y); + + png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); + } +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, + png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, + png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, + png_fixed_point blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + + png_debug(1, "in png_write_cHRM"); + + /* Each value is saved in 1/100,000ths */ +#ifdef PNG_CHECK_cHRM_SUPPORTED + if (png_check_cHRM_fixed(png_ptr, white_x, white_y, red_x, red_y, + green_x, green_y, blue_x, blue_y)) +#endif + { + png_save_uint_32(buf, (png_uint_32)white_x); + png_save_uint_32(buf + 4, (png_uint_32)white_y); + + png_save_uint_32(buf + 8, (png_uint_32)red_x); + png_save_uint_32(buf + 12, (png_uint_32)red_y); + + png_save_uint_32(buf + 16, (png_uint_32)green_x); + png_save_uint_32(buf + 20, (png_uint_32)green_y); + + png_save_uint_32(buf + 24, (png_uint_32)blue_x); + png_save_uint_32(buf + 28, (png_uint_32)blue_y); + + png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); + } +} +#endif +#endif + +#ifdef PNG_WRITE_tRNS_SUPPORTED +/* Write the tRNS chunk */ +void /* PRIVATE */ +png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran, + int num_trans, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tRNS; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_tRNS"); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid number of transparent colors specified"); + return; + } + /* Write the chunk out as it is */ + png_write_chunk(png_ptr, (png_bytep)png_tRNS, trans, + (png_size_t)num_trans); + } + else if (color_type == PNG_COLOR_TYPE_GRAY) + { + /* One 16 bit value */ + if (tran->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, tran->gray); + png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)2); + } + else if (color_type == PNG_COLOR_TYPE_RGB) + { + /* Three 16 bit values */ + png_save_uint_16(buf, tran->red); + png_save_uint_16(buf + 2, tran->green); + png_save_uint_16(buf + 4, tran->blue); + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)6); + } + else + { + png_warning(png_ptr, "Can't write tRNS with an alpha channel"); + } +} +#endif + +#ifdef PNG_WRITE_bKGD_SUPPORTED +/* Write the background chunk */ +void /* PRIVATE */ +png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_bKGD; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_bKGD"); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if ( +#ifdef PNG_MNG_FEATURES_SUPPORTED + (png_ptr->num_palette || + (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) && +#endif + back->index >= png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid background palette index"); + return; + } + buf[0] = back->index; + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)1); + } + else if (color_type & PNG_COLOR_MASK_COLOR) + { + png_save_uint_16(buf, back->red); + png_save_uint_16(buf + 2, back->green); + png_save_uint_16(buf + 4, back->blue); + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)6); + } + else + { + if (back->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, back->gray); + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)2); + } +} +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED +/* Write the histogram */ +void /* PRIVATE */ +png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_hIST; +#endif + int i; + png_byte buf[3]; + + png_debug(1, "in png_write_hIST"); + + if (num_hist > (int)png_ptr->num_palette) + { + png_debug2(3, "num_hist = %d, num_palette = %d", num_hist, + png_ptr->num_palette); + png_warning(png_ptr, "Invalid number of histogram entries specified"); + return; + } + + png_write_chunk_start(png_ptr, (png_bytep)png_hIST, + (png_uint_32)(num_hist * 2)); + for (i = 0; i < num_hist; i++) + { + png_save_uint_16(buf, hist[i]); + png_write_chunk_data(png_ptr, buf, (png_size_t)2); + } + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, + * and if invalid, correct the keyword rather than discarding the entire + * chunk. The PNG 1.0 specification requires keywords 1-79 characters in + * length, forbids leading or trailing whitespace, multiple internal spaces, + * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. + * + * The new_key is allocated to hold the corrected keyword and must be freed + * by the calling routine. This avoids problems with trying to write to + * static keywords without having to have duplicate copies of the strings. + */ +png_size_t /* PRIVATE */ +png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) +{ + png_size_t key_len; + png_charp kp, dp; + int kflag; + int kwarn=0; + + png_debug(1, "in png_check_keyword"); + + *new_key = NULL; + + if (key == NULL || (key_len = png_strlen(key)) == 0) + { + png_warning(png_ptr, "zero length keyword"); + return ((png_size_t)0); + } + + png_debug1(2, "Keyword to be checked is '%s'", key); + + *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2)); + if (*new_key == NULL) + { + png_warning(png_ptr, "Out of memory while procesing keyword"); + return ((png_size_t)0); + } + + /* Replace non-printing characters with a blank and print a warning */ + for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++) + { + if ((png_byte)*kp < 0x20 || + ((png_byte)*kp > 0x7E && (png_byte)*kp < 0xA1)) + { +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + char msg[40]; + + png_snprintf(msg, 40, + "invalid keyword character 0x%02X", (png_byte)*kp); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "invalid character in keyword"); +#endif + *dp = ' '; + } + else + { + *dp = *kp; + } + } + *dp = '\0'; + + /* Remove any trailing white space. */ + kp = *new_key + key_len - 1; + if (*kp == ' ') + { + png_warning(png_ptr, "trailing spaces removed from keyword"); + + while (*kp == ' ') + { + *(kp--) = '\0'; + key_len--; + } + } + + /* Remove any leading white space. */ + kp = *new_key; + if (*kp == ' ') + { + png_warning(png_ptr, "leading spaces removed from keyword"); + + while (*kp == ' ') + { + kp++; + key_len--; + } + } + + png_debug1(2, "Checking for multiple internal spaces in '%s'", kp); + + /* Remove multiple internal spaces. */ + for (kflag = 0, dp = *new_key; *kp != '\0'; kp++) + { + if (*kp == ' ' && kflag == 0) + { + *(dp++) = *kp; + kflag = 1; + } + else if (*kp == ' ') + { + key_len--; + kwarn=1; + } + else + { + *(dp++) = *kp; + kflag = 0; + } + } + *dp = '\0'; + if (kwarn) + png_warning(png_ptr, "extra interior spaces removed from keyword"); + + if (key_len == 0) + { + png_free(png_ptr, *new_key); + *new_key=NULL; + png_warning(png_ptr, "Zero length keyword"); + } + + if (key_len > 79) + { + png_warning(png_ptr, "keyword length must be 1 - 79 characters"); + (*new_key)[79] = '\0'; + key_len = 79; + } + + return (key_len); +} +#endif + +#ifdef PNG_WRITE_tEXt_SUPPORTED +/* Write a tEXt chunk */ +void /* PRIVATE */ +png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tEXt; +#endif + png_size_t key_len; + png_charp new_key; + + png_debug(1, "in png_write_tEXt"); + + if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) + return; + + if (text == NULL || *text == '\0') + text_len = 0; + else + text_len = png_strlen(text); + + /* Make sure we include the 0 after the key */ + png_write_chunk_start(png_ptr, (png_bytep)png_tEXt, + (png_uint_32)(key_len + text_len + 1)); + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, + (png_size_t)(key_len + 1)); + if (text_len) + png_write_chunk_data(png_ptr, (png_bytep)text, (png_size_t)text_len); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); +} +#endif + +#ifdef PNG_WRITE_zTXt_SUPPORTED +/* Write a compressed text chunk */ +void /* PRIVATE */ +png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len, int compression) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_zTXt; +#endif + png_size_t key_len; + char buf[1]; + png_charp new_key; + compression_state comp; + + png_debug(1, "in png_write_zTXt"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_free(png_ptr, new_key); + return; + } + + if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE) + { + png_write_tEXt(png_ptr, new_key, text, (png_size_t)0); + png_free(png_ptr, new_key); + return; + } + + text_len = png_strlen(text); + + /* Compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression, + &comp); + + /* Write start of chunk */ + png_write_chunk_start(png_ptr, (png_bytep)png_zTXt, + (png_uint_32)(key_len+text_len + 2)); + /* Write key */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, + (png_size_t)(key_len + 1)); + png_free(png_ptr, new_key); + + buf[0] = (png_byte)compression; + /* Write compression */ + png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1); + /* Write the compressed data */ + png_write_compressed_data_out(png_ptr, &comp); + + /* Close the chunk */ + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_iTXt_SUPPORTED +/* Write an iTXt chunk */ +void /* PRIVATE */ +png_write_iTXt(png_structp png_ptr, int compression, png_charp key, + png_charp lang, png_charp lang_key, png_charp text) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iTXt; +#endif + png_size_t lang_len, key_len, lang_key_len, text_len; + png_charp new_lang; + png_charp new_key = NULL; + png_byte cbuf[2]; + compression_state comp; + + png_debug(1, "in png_write_iTXt"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + + if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) + return; + + if ((lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0) + { + png_warning(png_ptr, "Empty language field in iTXt chunk"); + new_lang = NULL; + lang_len = 0; + } + + if (lang_key == NULL) + lang_key_len = 0; + else + lang_key_len = png_strlen(lang_key); + + if (text == NULL) + text_len = 0; + else + text_len = png_strlen(text); + + /* Compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression-2, + &comp); + + + /* Make sure we include the compression flag, the compression byte, + * and the NULs after the key, lang, and lang_key parts */ + + png_write_chunk_start(png_ptr, (png_bytep)png_iTXt, + (png_uint_32)( + 5 /* comp byte, comp flag, terminators for key, lang and lang_key */ + + key_len + + lang_len + + lang_key_len + + text_len)); + + /* We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, + (png_size_t)(key_len + 1)); + + /* Set the compression flag */ + if (compression == PNG_ITXT_COMPRESSION_NONE || \ + compression == PNG_TEXT_COMPRESSION_NONE) + cbuf[0] = 0; + else /* compression == PNG_ITXT_COMPRESSION_zTXt */ + cbuf[0] = 1; + /* Set the compression method */ + cbuf[1] = 0; + png_write_chunk_data(png_ptr, cbuf, (png_size_t)2); + + cbuf[0] = 0; + png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf), + (png_size_t)(lang_len + 1)); + png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf), + (png_size_t)(lang_key_len + 1)); + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); + png_free(png_ptr, new_lang); +} +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED +/* Write the oFFs chunk */ +void /* PRIVATE */ +png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_oFFs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_oFFs"); + + if (unit_type >= PNG_OFFSET_LAST) + png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); + + png_save_int_32(buf, x_offset); + png_save_int_32(buf + 4, y_offset); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, (png_bytep)png_oFFs, buf, (png_size_t)9); +} +#endif +#ifdef PNG_WRITE_pCAL_SUPPORTED +/* Write the pCAL chunk (described in the PNG extensions document) */ +void /* PRIVATE */ +png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, + png_int_32 X1, int type, int nparams, png_charp units, png_charpp params) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pCAL; +#endif + png_size_t purpose_len, units_len, total_len; + png_uint_32p params_len; + png_byte buf[10]; + png_charp new_purpose; + int i; + + png_debug1(1, "in png_write_pCAL (%d parameters)", nparams); + + if (type >= PNG_EQUATION_LAST) + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + + purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1; + png_debug1(3, "pCAL purpose length = %d", (int)purpose_len); + units_len = png_strlen(units) + (nparams == 0 ? 0 : 1); + png_debug1(3, "pCAL units length = %d", (int)units_len); + total_len = purpose_len + units_len + 10; + + params_len = (png_uint_32p)png_malloc(png_ptr, + (png_uint_32)(nparams * png_sizeof(png_uint_32))); + + /* Find the length of each parameter, making sure we don't count the + null terminator for the last parameter. */ + for (i = 0; i < nparams; i++) + { + params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu", i, + (unsigned long) params_len[i]); + total_len += (png_size_t)params_len[i]; + } + + png_debug1(3, "pCAL total length = %d", (int)total_len); + png_write_chunk_start(png_ptr, (png_bytep)png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, (png_bytep)new_purpose, + (png_size_t)purpose_len); + png_save_int_32(buf, X0); + png_save_int_32(buf + 4, X1); + buf[8] = (png_byte)type; + buf[9] = (png_byte)nparams; + png_write_chunk_data(png_ptr, buf, (png_size_t)10); + png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len); + + png_free(png_ptr, new_purpose); + + for (i = 0; i < nparams; i++) + { + png_write_chunk_data(png_ptr, (png_bytep)params[i], + (png_size_t)params_len[i]); + } + + png_free(png_ptr, params_len); + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED +/* Write the sCAL chunk */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) +void /* PRIVATE */ +png_write_sCAL(png_structp png_ptr, int unit, double width, double height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + char buf[64]; + png_size_t total_len; + + png_debug(1, "in png_write_sCAL"); + + buf[0] = (char)unit; +#ifdef _WIN32_WCE +/* sprintf() function is not supported on WindowsCE */ + { + wchar_t wc_buf[32]; + size_t wc_len; + swprintf(wc_buf, TEXT("%12.12e"), width); + wc_len = wcslen(wc_buf); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + 1, wc_len, NULL, + NULL); + total_len = wc_len + 2; + swprintf(wc_buf, TEXT("%12.12e"), height); + wc_len = wcslen(wc_buf); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + total_len, wc_len, + NULL, NULL); + total_len += wc_len; + } +#else + png_snprintf(buf + 1, 63, "%12.12e", width); + total_len = 1 + png_strlen(buf + 1) + 1; + png_snprintf(buf + total_len, 64-total_len, "%12.12e", height); + total_len += png_strlen(buf + total_len); +#endif + + png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); + png_write_chunk(png_ptr, (png_bytep)png_sCAL, (png_bytep)buf, total_len); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width, + png_charp height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + png_byte buf[64]; + png_size_t wlen, hlen, total_len; + + png_debug(1, "in png_write_sCAL_s"); + + wlen = png_strlen(width); + hlen = png_strlen(height); + total_len = wlen + hlen + 2; + if (total_len > 64) + { + png_warning(png_ptr, "Can't write sCAL (buffer too small)"); + return; + } + + buf[0] = (png_byte)unit; + png_memcpy(buf + 1, width, wlen + 1); /* Append the '\0' here */ + png_memcpy(buf + wlen + 2, height, hlen); /* Do NOT append the '\0' here */ + + png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); + png_write_chunk(png_ptr, (png_bytep)png_sCAL, buf, total_len); +} +#endif +#endif +#endif + +#ifdef PNG_WRITE_pHYs_SUPPORTED +/* Write the pHYs chunk */ +void /* PRIVATE */ +png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit, + png_uint_32 y_pixels_per_unit, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pHYs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_pHYs"); + + if (unit_type >= PNG_RESOLUTION_LAST) + png_warning(png_ptr, "Unrecognized unit type for pHYs chunk"); + + png_save_uint_32(buf, x_pixels_per_unit); + png_save_uint_32(buf + 4, y_pixels_per_unit); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, (png_bytep)png_pHYs, buf, (png_size_t)9); +} +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +/* Write the tIME chunk. Use either png_convert_from_struct_tm() + * or png_convert_from_time_t(), or fill in the structure yourself. + */ +void /* PRIVATE */ +png_write_tIME(png_structp png_ptr, png_timep mod_time) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tIME; +#endif + png_byte buf[7]; + + png_debug(1, "in png_write_tIME"); + + if (mod_time->month > 12 || mod_time->month < 1 || + mod_time->day > 31 || mod_time->day < 1 || + mod_time->hour > 23 || mod_time->second > 60) + { + png_warning(png_ptr, "Invalid time specified for tIME chunk"); + return; + } + + png_save_uint_16(buf, mod_time->year); + buf[2] = mod_time->month; + buf[3] = mod_time->day; + buf[4] = mod_time->hour; + buf[5] = mod_time->minute; + buf[6] = mod_time->second; + + png_write_chunk(png_ptr, (png_bytep)png_tIME, buf, (png_size_t)7); +} +#endif + +/* Initializes the row writing capability of libpng */ +void /* PRIVATE */ +png_write_start_row(png_structp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_size_t buf_size; + + png_debug(1, "in png_write_start_row"); + + buf_size = (png_size_t)(PNG_ROWBYTES( + png_ptr->usr_channels*png_ptr->usr_bit_depth, png_ptr->width) + 1); + + /* Set up row buffer */ + png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)buf_size); + png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; + +#ifdef PNG_WRITE_FILTER_SUPPORTED + /* Set up filtering buffer, if using this filter */ + if (png_ptr->do_filter & PNG_FILTER_SUB) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + /* We only need to keep the previous row if we are using one of these. */ + if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) + { + /* Set up previous row buffer */ + png_ptr->prev_row = (png_bytep)png_calloc(png_ptr, + (png_uint_32)buf_size); + + if (png_ptr->do_filter & PNG_FILTER_UP) + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + + if (png_ptr->do_filter & PNG_FILTER_AVG) + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + + if (png_ptr->do_filter & PNG_FILTER_PAETH) + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced, we need to set up width and height of pass */ + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - + png_pass_start[0]) / png_pass_inc[0]; + } + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + } + else +#endif + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; +} + +/* Internal use only. Called when finished processing a row of data. */ +void /* PRIVATE */ +png_write_finish_row(png_structp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + int ret; + + png_debug(1, "in png_write_finish_row"); + + /* Next row */ + png_ptr->row_number++; + + /* See if we are done */ + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced, go to next pass */ + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + if (png_ptr->transformations & PNG_INTERLACE) + { + png_ptr->pass++; + } + else + { + /* Loop until we find a non-zero width or height pass */ + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->usr_width = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (png_ptr->transformations & PNG_INTERLACE) + break; + } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); + + } + + /* Reset the row above the image for the next pass */ + if (png_ptr->pass < 7) + { + if (png_ptr->prev_row != NULL) + png_memset(png_ptr->prev_row, 0, + (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* + png_ptr->usr_bit_depth, png_ptr->width)) + 1); + return; + } + } +#endif + + /* If we get here, we've just written the last row, so we need + to flush the compressor */ + do + { + /* Tell the compressor we are done */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + /* Check for an error */ + if (ret == Z_OK) + { + /* Check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else if (ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* Write any extra space */ + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size - + png_ptr->zstream.avail_out); + } + + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Pick out the correct pixels for the interlace pass. + * The basic idea here is to go through the row with a source + * pointer and a destination pointer (sp and dp), and copy the + * correct pixels for the pass. As the row gets compacted, + * sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. + */ +void /* PRIVATE */ +png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) +{ + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + png_debug(1, "in png_do_write_interlace"); + + /* We don't have to do anything on the last pass (6) */ +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL && pass < 6) +#else + if (pass < 6) +#endif + { + /* Each pixel depth is handled separately */ + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + d = 0; + shift = 7; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 3); + value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; + d |= (value << shift); + + if (shift == 0) + { + shift = 7; + *dp++ = (png_byte)d; + d = 0; + } + else + shift--; + + } + if (shift != 7) + *dp = (png_byte)d; + break; + } + case 2: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 6; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 2); + value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; + d |= (value << shift); + + if (shift == 0) + { + shift = 6; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 2; + } + if (shift != 6) + *dp = (png_byte)d; + break; + } + case 4: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 4; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 1); + value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; + d |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 4; + } + if (shift != 4) + *dp = (png_byte)d; + break; + } + default: + { + png_bytep sp; + png_bytep dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + png_size_t pixel_bytes; + + /* Start at the beginning */ + dp = row; + /* Find out how many bytes each pixel takes up */ + pixel_bytes = (row_info->pixel_depth >> 3); + /* Loop through the row, only looking at the pixels that + matter */ + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + /* Find out where the original pixel is */ + sp = row + (png_size_t)i * pixel_bytes; + /* Move the pixel */ + if (dp != sp) + png_memcpy(dp, sp, pixel_bytes); + /* Next pixel */ + dp += pixel_bytes; + } + break; + } + } + /* Set new row width */ + row_info->width = (row_info->width + + png_pass_inc[pass] - 1 - + png_pass_start[pass]) / + png_pass_inc[pass]; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +/* This filters the row, chooses which filter to use, if it has not already + * been specified by the application, and then writes the row out with the + * chosen filter. + */ +#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) +#define PNG_HISHIFT 10 +#define PNG_LOMASK ((png_uint_32)0xffffL) +#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) +void /* PRIVATE */ +png_write_find_filter(png_structp png_ptr, png_row_infop row_info) +{ + png_bytep best_row; +#ifdef PNG_WRITE_FILTER_SUPPORTED + png_bytep prev_row, row_buf; + png_uint_32 mins, bpp; + png_byte filter_to_do = png_ptr->do_filter; + png_uint_32 row_bytes = row_info->rowbytes; +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + int num_p_filters = (int)png_ptr->num_prev_filters; +#endif + + png_debug(1, "in png_write_find_filter"); + +#ifndef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->row_number == 0 && filter_to_do == PNG_ALL_FILTERS) + { + /* These will never be selected so we need not test them. */ + filter_to_do &= ~(PNG_FILTER_UP | PNG_FILTER_PAETH); + } +#endif + + /* Find out how many bytes offset each pixel is */ + bpp = (row_info->pixel_depth + 7) >> 3; + + prev_row = png_ptr->prev_row; +#endif + best_row = png_ptr->row_buf; +#ifdef PNG_WRITE_FILTER_SUPPORTED + row_buf = best_row; + mins = PNG_MAXSUM; + + /* The prediction method we use is to find which method provides the + * smallest value when summing the absolute values of the distances + * from zero, using anything >= 128 as negative numbers. This is known + * as the "minimum sum of absolute differences" heuristic. Other + * heuristics are the "weighted minimum sum of absolute differences" + * (experimental and can in theory improve compression), and the "zlib + * predictive" method (not implemented yet), which does test compressions + * of lines using different filter methods, and then chooses the + * (series of) filter(s) that give minimum compressed data size (VERY + * computationally expensive). + * + * GRR 980525: consider also + * (1) minimum sum of absolute differences from running average (i.e., + * keep running sum of non-absolute differences & count of bytes) + * [track dispersion, too? restart average if dispersion too large?] + * (1b) minimum sum of absolute differences from sliding average, probably + * with window size <= deflate window (usually 32K) + * (2) minimum sum of squared differences from zero or running average + * (i.e., ~ root-mean-square approach) + */ + + + /* We don't need to test the 'no filter' case if this is the only filter + * that has been chosen, as it doesn't actually do anything to the data. + */ + if ((filter_to_do & PNG_FILTER_NONE) && + filter_to_do != PNG_FILTER_NONE) + { + png_bytep rp; + png_uint_32 sum = 0; + png_uint_32 i; + int v; + + for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) + { + v = *rp; + sum += (v < 128) ? v : 256 - v; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + png_uint_32 sumhi, sumlo; + int j; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */ + + /* Reduce the sum if we match any of the previous rows */ + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + /* Factor in the cost of this filter (this is here for completeness, + * but it makes no sense to have a "cost" for the NONE filter, as + * it has the minimum possible computational cost - none). + */ + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + mins = sum; + } + + /* Sub filter */ + if (filter_to_do == PNG_FILTER_SUB) + /* It's the only filter so no testing is needed */ + { + png_bytep rp, lp, dp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + *dp = *rp; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + } + best_row = png_ptr->sub_row; + } + + else if (filter_to_do & PNG_FILTER_SUB) + { + png_bytep rp, dp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + /* We temporarily increase the "minimum sum" by the factor we + * would reduce the sum of this filter, so that we can do the + * early exit comparison without scaling the sum each time. + */ + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + v = *dp = *rp; + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->sub_row; + } + } + + /* Up filter */ + if (filter_to_do == PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); + } + best_row = png_ptr->up_row; + } + + else if (filter_to_do & PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->up_row; + } + } + + /* Avg filter */ + if (filter_to_do == PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) + & 0xff); + } + best_row = png_ptr->avg_row; + } + + else if (filter_to_do & PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + v = *dp++ = + (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->avg_row; + } + } + + /* Paeth filter */ + if (filter_to_do == PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + } + best_row = png_ptr->paeth_row; + } + + else if (filter_to_do & PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + +#ifndef PNG_SLOW_PAETH + p = b - c; + pc = a - c; +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; +#else /* PNG_SLOW_PAETH */ + p = a + b - c; + pa = abs(p - a); + pb = abs(p - b); + pc = abs(p - c); + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; +#endif /* PNG_SLOW_PAETH */ + + v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + best_row = png_ptr->paeth_row; + } + } +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + /* Do the actual writing of the filtered row data from the chosen filter. */ + + png_write_filtered_row(png_ptr, best_row); + +#ifdef PNG_WRITE_FILTER_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + /* Save the type of filter we picked this time for future calculations */ + if (png_ptr->num_prev_filters > 0) + { + int j; + for (j = 1; j < num_p_filters; j++) + { + png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; + } + png_ptr->prev_filters[j] = best_row[0]; + } +#endif +#endif /* PNG_WRITE_FILTER_SUPPORTED */ +} + + +/* Do the actual writing of a previously filtered row. */ +void /* PRIVATE */ +png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row) +{ + png_debug(1, "in png_write_filtered_row"); + + png_debug1(2, "filter = %d", filtered_row[0]); + /* Set up the zlib input buffer */ + + png_ptr->zstream.next_in = filtered_row; + png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1; + /* Repeat until we have compressed all the data */ + do + { + int ret; /* Return of zlib */ + + /* Compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + /* Check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + /* See if it is time to write another IDAT */ + if (!(png_ptr->zstream.avail_out)) + { + /* Write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + /* Repeat until all data has been compressed */ + } while (png_ptr->zstream.avail_in); + + /* Swap the current and previous rows */ + if (png_ptr->prev_row != NULL) + { + png_bytep tptr; + + tptr = png_ptr->prev_row; + png_ptr->prev_row = png_ptr->row_buf; + png_ptr->row_buf = tptr; + } + + /* Finish row - updates counters and flushes zlib if last row */ + png_write_finish_row(png_ptr); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_ptr->flush_rows++; + + if (png_ptr->flush_dist > 0 && + png_ptr->flush_rows >= png_ptr->flush_dist) + { + png_write_flush(png_ptr); + } +#endif +} +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/bazaar/plugin/gdal/frmts/png/makefile.vc b/bazaar/plugin/gdal/frmts/png/makefile.vc new file mode 100644 index 000000000..6c6943a97 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/makefile.vc @@ -0,0 +1,28 @@ +GDAL_ROOT = ..\.. +!INCLUDE $(GDAL_ROOT)\nmake.opt + +OBJ = \ + pngdataset.obj + + +#EXTRAFLAGS = -I..\zlib -Ilibpng +!IFDEF PNG_EXTERNAL_LIB +EXTRAFLAGS = -I..\zlib -I$(PNGDIR) +!ELSE +EXTRAFLAGS = -I..\zlib -Ilibpng +!ENDIF + +default: $(OBJ) + xcopy /D /Y *.obj ..\o +!IFNDEF PNG_EXTERNAL_LIB + cd libpng + $(MAKE) /f makefile.vc + cd .. +!ENDIF + +clean: + -del *.obj + cd libpng + $(MAKE) /f makefile.vc clean + cd .. + diff --git a/bazaar/plugin/gdal/frmts/png/pngdataset.cpp b/bazaar/plugin/gdal/frmts/png/pngdataset.cpp new file mode 100644 index 000000000..c44a53ac1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/png/pngdataset.cpp @@ -0,0 +1,2441 @@ +/****************************************************************************** + * $Id: pngdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $ + * + * Project: PNG Driver + * Purpose: Implement GDAL PNG Support + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * ISSUES: + * o CollectMetadata() will only capture TEXT chunks before the image + * data as the code is currently structured. + * o Interlaced images are read entirely into memory for use. This is + * bad for large images. + * o Image reading is always strictly sequential. Reading backwards will + * cause the file to be rewound, and access started again from the + * beginning. + * o 1, 2 and 4 bit data promoted to 8 bit. + * o Transparency values not currently read and applied to palette. + * o 16 bit alpha values are not scaled by to eight bit. + * o I should install setjmp()/longjmp() based error trapping for PNG calls. + * Currently a failure in png libraries will result in a complete + * application termination. + * + */ + +#include "gdal_pam.h" +#include "png.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: pngdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $"); + +CPL_C_START +void GDALRegister_PNG(void); +CPL_C_END + +// Define SUPPORT_CREATE if you want Create() call supported. +// Note: callers must provide blocks in increasing Y order. + +// Disclaimer (E. Rouault) : this code is NOT production ready at all. +// A lot of issues remains : uninitialized variables, unclosed file, +// inability to handle properly multiband case, inability to read&write +// at the same time. Do NOT use it unless you're ready to fix it +//#define SUPPORT_CREATE + +// we believe it is ok to use setjmp() in this situation. +#ifdef _MSC_VER +# pragma warning(disable:4611) +#endif + +static void +png_vsi_read_data(png_structp png_ptr, png_bytep data, png_size_t length); + +static void +png_vsi_write_data(png_structp png_ptr, png_bytep data, png_size_t length); + +static void png_vsi_flush(png_structp png_ptr); + +static void png_gdal_error( png_structp png_ptr, const char *error_message ); +static void png_gdal_warning( png_structp png_ptr, const char *error_message ); + +/************************************************************************/ +/* ==================================================================== */ +/* PNGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class PNGRasterBand; + +class PNGDataset : public GDALPamDataset +{ + friend class PNGRasterBand; + + VSILFILE *fpImage; + png_structp hPNG; + png_infop psPNGInfo; + int nBitDepth; + int nColorType; /* PNG_COLOR_TYPE_* */ + int bInterlaced; + + int nBufferStartLine; + int nBufferLines; + int nLastLineRead; + GByte *pabyBuffer; + + GDALColorTable *poColorTable; + + int bGeoTransformValid; + double adfGeoTransform[6]; + + + void CollectMetadata(); + + int bHasReadXMPMetadata; + void CollectXMPMetadata(); + + CPLErr LoadScanline( int ); + CPLErr LoadInterlacedChunk( int ); + void Restart(); + + int bHasTriedLoadWorldFile; + void LoadWorldFile(); + CPLString osWldFilename; + + int bHasReadICCMetadata; + void LoadICCProfile(); + + static void WriteMetadataAsText(png_structp hPNG, png_infop psPNGInfo, + const char* pszKey, const char* pszValue); + + public: + PNGDataset(); + ~PNGDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset* CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + + virtual char **GetFileList(void); + + virtual CPLErr GetGeoTransform( double * ); + virtual void FlushCache( void ); + + virtual char **GetMetadataDomainList(); + + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = NULL ); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing, GSpacing, + GSpacing, + GDALRasterIOExtraArg* psExtraArg ); + + // semi-private. + jmp_buf sSetJmpContext; + +#ifdef SUPPORT_CREATE + int m_nBitDepth; + GByte *m_pabyBuffer; + png_byte *m_pabyAlpha; + png_structp m_hPNG; + png_infop m_psPNGInfo; + png_color *m_pasPNGColors; + VSILFILE *m_fpImage; + int m_bGeoTransformValid; + double m_adfGeoTransform[6]; + char *m_pszFilename; + int m_nColorType; /* PNG_COLOR_TYPE_* */ + + virtual CPLErr SetGeoTransform( double * ); + static GDALDataset *Create( const char* pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType, char** papszParmList ); + protected: + CPLErr write_png_header(); + +#endif +}; + +/************************************************************************/ +/* ==================================================================== */ +/* PNGRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class PNGRasterBand : public GDALPamRasterBand +{ + friend class PNGDataset; + + public: + + PNGRasterBand( PNGDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); + + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + CPLErr SetNoDataValue( double dfNewValue ); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + + int bHaveNoData; + double dfNoDataValue; + + +#ifdef SUPPORT_CREATE + virtual CPLErr SetColorTable(GDALColorTable*); + virtual CPLErr IWriteBlock( int, int, void * ); + + protected: + int m_bBandProvided[5]; + void reset_band_provision_flags() + { + PNGDataset& ds = *(PNGDataset*)poDS; + + for(size_t i = 0; i < ds.nBands; i++) + m_bBandProvided[i] = FALSE; + } +#endif +}; + + +/************************************************************************/ +/* PNGRasterBand() */ +/************************************************************************/ + +PNGRasterBand::PNGRasterBand( PNGDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + if( poDS->nBitDepth == 16 ) + eDataType = GDT_UInt16; + else + eDataType = GDT_Byte; + + nBlockXSize = poDS->nRasterXSize;; + nBlockYSize = 1; + + bHaveNoData = FALSE; + dfNoDataValue = -1; + +#ifdef SUPPORT_CREATE + this->reset_band_provision_flags(); +#endif +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr PNGRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + PNGDataset *poGDS = (PNGDataset *) poDS; + CPLErr eErr; + GByte *pabyScanline; + int i, nPixelSize, nPixelOffset, nXSize = GetXSize(); + + CPLAssert( nBlockXOff == 0 ); + + if( poGDS->nBitDepth == 16 ) + nPixelSize = 2; + else + nPixelSize = 1; + + + if (poGDS->fpImage == NULL) + { + memset( pImage, 0, nPixelSize * nXSize ); + return CE_None; + } + + nPixelOffset = poGDS->nBands * nPixelSize; + +/* -------------------------------------------------------------------- */ +/* Load the desired scanline into the working buffer. */ +/* -------------------------------------------------------------------- */ + eErr = poGDS->LoadScanline( nBlockYOff ); + if( eErr != CE_None ) + return eErr; + + pabyScanline = poGDS->pabyBuffer + + (nBlockYOff - poGDS->nBufferStartLine) * nPixelOffset * nXSize + + nPixelSize * (nBand - 1); + +/* -------------------------------------------------------------------- */ +/* Transfer between the working buffer the the callers buffer. */ +/* -------------------------------------------------------------------- */ + if( nPixelSize == nPixelOffset ) + memcpy( pImage, pabyScanline, nPixelSize * nXSize ); + else if( nPixelSize == 1 ) + { + for( i = 0; i < nXSize; i++ ) + ((GByte *) pImage)[i] = pabyScanline[i*nPixelOffset]; + } + else + { + CPLAssert( nPixelSize == 2 ); + for( i = 0; i < nXSize; i++ ) + { + ((GUInt16 *) pImage)[i] = + *((GUInt16 *) (pabyScanline+i*nPixelOffset)); + } + } + +/* -------------------------------------------------------------------- */ +/* Forceably load the other bands associated with this scanline. */ +/* -------------------------------------------------------------------- */ + int iBand; + for(iBand = 1; iBand < poGDS->GetRasterCount(); iBand++) + { + GDALRasterBlock *poBlock; + + poBlock = + poGDS->GetRasterBand(iBand+1)->GetLockedBlockRef(nBlockXOff,nBlockYOff); + if( poBlock != NULL ) + poBlock->DropLock(); + } + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp PNGRasterBand::GetColorInterpretation() + +{ + PNGDataset *poGDS = (PNGDataset *) poDS; + + if( poGDS->nColorType == PNG_COLOR_TYPE_GRAY ) + return GCI_GrayIndex; + + else if( poGDS->nColorType == PNG_COLOR_TYPE_GRAY_ALPHA ) + { + if( nBand == 1 ) + return GCI_GrayIndex; + else + return GCI_AlphaBand; + } + + else if( poGDS->nColorType == PNG_COLOR_TYPE_PALETTE ) + return GCI_PaletteIndex; + + else if( poGDS->nColorType == PNG_COLOR_TYPE_RGB + || poGDS->nColorType == PNG_COLOR_TYPE_RGB_ALPHA ) + { + if( nBand == 1 ) + return GCI_RedBand; + else if( nBand == 2 ) + return GCI_GreenBand; + else if( nBand == 3 ) + return GCI_BlueBand; + else + return GCI_AlphaBand; + } + else + return GCI_GrayIndex; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *PNGRasterBand::GetColorTable() + +{ + PNGDataset *poGDS = (PNGDataset *) poDS; + + if( nBand == 1 ) + return poGDS->poColorTable; + else + return NULL; +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr PNGRasterBand::SetNoDataValue( double dfNewValue ) + +{ + bHaveNoData = TRUE; + dfNoDataValue = dfNewValue; + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double PNGRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( bHaveNoData ) + { + if( pbSuccess != NULL ) + *pbSuccess = bHaveNoData; + return dfNoDataValue; + } + else + { + return GDALPamRasterBand::GetNoDataValue( pbSuccess ); + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* PNGDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* PNGDataset() */ +/************************************************************************/ + +PNGDataset::PNGDataset() + +{ + fpImage = NULL; + hPNG = NULL; + psPNGInfo = NULL; + pabyBuffer = NULL; + nBufferStartLine = 0; + nBufferLines = 0; + nLastLineRead = -1; + poColorTable = NULL; + nBitDepth = 8; + + bGeoTransformValid = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + bHasTriedLoadWorldFile = FALSE; + bHasReadXMPMetadata = FALSE; + bHasReadICCMetadata = FALSE; +} + +/************************************************************************/ +/* ~PNGDataset() */ +/************************************************************************/ + +PNGDataset::~PNGDataset() + +{ + FlushCache(); + + if( hPNG != NULL ) + png_destroy_read_struct( &hPNG, &psPNGInfo, NULL ); + + if( fpImage ) + VSIFCloseL( fpImage ); + + if( poColorTable != NULL ) + delete poColorTable; +} + +/************************************************************************/ +/* IsFullBandMap() */ +/************************************************************************/ + +static int IsFullBandMap(int *panBandMap, int nBands) +{ + for(int i=0;iGetRasterDataType()) && + (pData != NULL) && + (panBandMap != NULL) && IsFullBandMap(panBandMap, nBands)) + { + int y; + CPLErr tmpError; + int x; + + // Pixel interleaved case + if( nBandSpace == 1 ) + { + for(y = 0; y < nYSize; ++y) + { + tmpError = LoadScanline(y); + if(tmpError != CE_None) return tmpError; + GByte* pabyScanline = pabyBuffer + + (y - nBufferStartLine) * nBands * nXSize; + if( nPixelSpace == nBandSpace * nBandCount ) + { + memcpy(&(((GByte*)pData)[(y*nLineSpace)]), + pabyScanline, nBandCount * nXSize); + } + else + { + for(x = 0; x < nXSize; ++x) + { + memcpy(&(((GByte*)pData)[(y*nLineSpace) + (x*nPixelSpace)]), + (const GByte*)&(pabyScanline[x* nBandCount]), nBandCount); + } + } + } + } + else + { + for(y = 0; y < nYSize; ++y) + { + tmpError = LoadScanline(y); + if(tmpError != CE_None) return tmpError; + GByte* pabyScanline = pabyBuffer + + (y - nBufferStartLine) * nBands * nXSize; + for(x = 0; x < nXSize; ++x) + { + for(int iBand=0;iBand GetRasterYSize() ) + nMaxChunkLines = GetRasterYSize(); + +/* -------------------------------------------------------------------- */ +/* Allocate chunk buffer, if we don't already have it from a */ +/* previous request. */ +/* -------------------------------------------------------------------- */ + nBufferLines = nMaxChunkLines; + if( nMaxChunkLines + iLine > GetRasterYSize() ) + nBufferStartLine = GetRasterYSize() - nMaxChunkLines; + else + nBufferStartLine = iLine; + + if( pabyBuffer == NULL ) + { + pabyBuffer = (GByte *) + VSIMalloc(nPixelOffset*GetRasterXSize()*nMaxChunkLines); + + if( pabyBuffer == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Unable to allocate buffer for whole interlaced PNG" + "image of size %dx%d.\n", + GetRasterXSize(), GetRasterYSize() ); + return CE_Failure; + } +#ifdef notdef + if( nMaxChunkLines < GetRasterYSize() ) + CPLDebug( "PNG", + "Interlaced file being handled in %d line chunks.\n" + "Performance is likely to be quite poor.", + nMaxChunkLines ); +#endif + } + +/* -------------------------------------------------------------------- */ +/* Do we need to restart reading? We do this if we aren't on */ +/* the first attempt to read the image. */ +/* -------------------------------------------------------------------- */ + if( nLastLineRead != -1 ) + { + Restart(); + if( setjmp( sSetJmpContext ) != 0 ) + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Allocate and populate rows array. We create a row for each */ +/* row in the image, but use our dummy line for rows not in the */ +/* target window. */ +/* -------------------------------------------------------------------- */ + int i; + png_bytep dummy_row = (png_bytep)CPLMalloc(nPixelOffset*GetRasterXSize()); + png_rows = (png_bytep*)CPLMalloc(sizeof(png_bytep) * GetRasterYSize()); + + for( i = 0; i < GetRasterYSize(); i++ ) + { + if( i >= nBufferStartLine && i < nBufferStartLine + nBufferLines ) + png_rows[i] = pabyBuffer + + (i-nBufferStartLine) * nPixelOffset * GetRasterXSize(); + else + png_rows[i] = dummy_row; + } + + png_read_image( hPNG, png_rows ); + + CPLFree( png_rows ); + CPLFree( dummy_row ); + + nLastLineRead = nBufferStartLine + nBufferLines - 1; + + return CE_None; +} + +/************************************************************************/ +/* LoadScanline() */ +/************************************************************************/ + +CPLErr PNGDataset::LoadScanline( int nLine ) + +{ + int nPixelOffset; + + CPLAssert( nLine >= 0 && nLine < GetRasterYSize() ); + + if( nLine >= nBufferStartLine && nLine < nBufferStartLine + nBufferLines) + return CE_None; + + if( nBitDepth == 16 ) + nPixelOffset = 2 * GetRasterCount(); + else + nPixelOffset = 1 * GetRasterCount(); + + if( setjmp( sSetJmpContext ) != 0 ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* If the file is interlaced, we will load the entire image */ +/* into memory using the high level API. */ +/* -------------------------------------------------------------------- */ + if( bInterlaced ) + return LoadInterlacedChunk( nLine ); + +/* -------------------------------------------------------------------- */ +/* Ensure we have space allocated for one scanline */ +/* -------------------------------------------------------------------- */ + if( pabyBuffer == NULL ) + pabyBuffer = (GByte *) CPLMalloc(nPixelOffset * GetRasterXSize()); + +/* -------------------------------------------------------------------- */ +/* Otherwise we just try to read the requested row. Do we need */ +/* to rewind and start over? */ +/* -------------------------------------------------------------------- */ + if( nLine <= nLastLineRead ) + { + Restart(); + if( setjmp( sSetJmpContext ) != 0 ) + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Read till we get the desired row. */ +/* -------------------------------------------------------------------- */ + png_bytep row; + + row = pabyBuffer; + while( nLine > nLastLineRead ) + { + png_read_rows( hPNG, &row, NULL, 1 ); + nLastLineRead++; + } + + nBufferStartLine = nLine; + nBufferLines = 1; + +/* -------------------------------------------------------------------- */ +/* Do swap on LSB machines. 16bit PNG data is stored in MSB */ +/* format. */ +/* -------------------------------------------------------------------- */ +#ifdef CPL_LSB + if( nBitDepth == 16 ) + GDALSwapWords( row, 2, GetRasterXSize() * GetRasterCount(), 2 ); +#endif + + return CE_None; +} + +/************************************************************************/ +/* CollectMetadata() */ +/* */ +/* We normally do this after reading up to the image, but be */ +/* forwarned ... we can missing text chunks this way. */ +/* */ +/* We turn each PNG text chunk into one metadata item. It */ +/* might be nice to preserve language information though we */ +/* don't try to now. */ +/************************************************************************/ + +void PNGDataset::CollectMetadata() + +{ + int nTextCount; + png_textp text_ptr; + + if( nBitDepth < 8 ) + { + for( int iBand = 0; iBand < nBands; iBand++ ) + { + GetRasterBand(iBand+1)->SetMetadataItem( + "NBITS", CPLString().Printf( "%d", nBitDepth ), + "IMAGE_STRUCTURE" ); + } + } + + if( png_get_text( hPNG, psPNGInfo, &text_ptr, &nTextCount ) == 0 ) + return; + + for( int iText = 0; iText < nTextCount; iText++ ) + { + char *pszTag = CPLStrdup(text_ptr[iText].key); + + for( int i = 0; pszTag[i] != '\0'; i++ ) + { + if( pszTag[i] == ' ' || pszTag[i] == '=' || pszTag[i] == ':' ) + pszTag[i] = '_'; + } + + GDALDataset::SetMetadataItem( pszTag, text_ptr[iText].text ); + CPLFree( pszTag ); + } +} + +/************************************************************************/ +/* CollectXMPMetadata() */ +/************************************************************************/ + +/* See §2.1.5 of http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf */ + +void PNGDataset::CollectXMPMetadata() + +{ + if (fpImage == NULL || bHasReadXMPMetadata) + return; + + /* Save current position to avoid disturbing PNG stream decoding */ + vsi_l_offset nCurOffset = VSIFTellL(fpImage); + + vsi_l_offset nOffset = 8; + VSIFSeekL( fpImage, nOffset, SEEK_SET ); + + /* Loop over chunks */ + while(TRUE) + { + int nLength; + char pszChunkType[5]; + int nCRC; + + if (VSIFReadL( &nLength, 4, 1, fpImage ) != 1) + break; + nOffset += 4; + CPL_MSBPTR32(&nLength); + if (nLength <= 0) + break; + if (VSIFReadL( pszChunkType, 4, 1, fpImage ) != 1) + break; + nOffset += 4; + pszChunkType[4] = 0; + + if (strcmp(pszChunkType, "iTXt") == 0 && nLength > 22) + { + char* pszContent = (char*)VSIMalloc(nLength + 1); + if (pszContent == NULL) + break; + if (VSIFReadL( pszContent, nLength, 1, fpImage) != 1) + { + VSIFree(pszContent); + break; + } + nOffset += nLength; + pszContent[nLength] = '\0'; + if (memcmp(pszContent, "XML:com.adobe.xmp\0\0\0\0\0", 22) == 0) + { + /* Avoid setting the PAM dirty bit just for that */ + int nOldPamFlags = nPamFlags; + + char *apszMDList[2]; + apszMDList[0] = pszContent + 22; + apszMDList[1] = NULL; + SetMetadata(apszMDList, "xml:XMP"); + + nPamFlags = nOldPamFlags; + + VSIFree(pszContent); + + break; + } + else + { + VSIFree(pszContent); + } + } + else + { + nOffset += nLength; + VSIFSeekL( fpImage, nOffset, SEEK_SET ); + } + + nOffset += 4; + if (VSIFReadL( &nCRC, 4, 1, fpImage ) != 1) + break; + } + + VSIFSeekL( fpImage, nCurOffset, SEEK_SET ); + + bHasReadXMPMetadata = TRUE; +} + +/************************************************************************/ +/* LoadICCProfile() */ +/************************************************************************/ + +void PNGDataset::LoadICCProfile() +{ + if (hPNG == NULL || bHasReadICCMetadata) + return; + bHasReadICCMetadata = TRUE; + + png_charp pszProfileName; + png_uint_32 nProfileLength; +#if (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR > 4) || PNG_LIBPNG_VER_MAJOR > 1 + png_bytep pProfileData; +#else + png_charp pProfileData; +#endif + int nCompressionType; + int nsRGBIntent; + double dfGamma; + bool bGammaAvailable = false; + + /* Avoid setting the PAM dirty bit just for that */ + int nOldPamFlags = nPamFlags; + + if (png_get_iCCP(hPNG, psPNGInfo, &pszProfileName, + &nCompressionType, &pProfileData, &nProfileLength) != 0) + { + /* Escape the profile */ + char *pszBase64Profile = CPLBase64Encode(nProfileLength, (const GByte*)pProfileData); + + /* Set ICC profile metadata */ + SetMetadataItem( "SOURCE_ICC_PROFILE", pszBase64Profile, "COLOR_PROFILE" ); + SetMetadataItem( "SOURCE_ICC_PROFILE_NAME", pszProfileName, "COLOR_PROFILE" ); + + nPamFlags = nOldPamFlags; + + CPLFree(pszBase64Profile); + + return; + } + + if (png_get_sRGB(hPNG, psPNGInfo, &nsRGBIntent) != 0) + { + SetMetadataItem( "SOURCE_ICC_PROFILE_NAME", "sRGB", "COLOR_PROFILE" ); + + nPamFlags = nOldPamFlags; + + return; + } + + if (png_get_valid(hPNG, psPNGInfo, PNG_INFO_gAMA)) + { + bGammaAvailable = true; + + png_get_gAMA(hPNG,psPNGInfo, &dfGamma); + + SetMetadataItem( "PNG_GAMMA", + CPLString().Printf( "%.9f", dfGamma ) , "COLOR_PROFILE" ); + } + + // Check if both cHRM and gAMA available + if (bGammaAvailable && png_get_valid(hPNG, psPNGInfo, PNG_INFO_cHRM)) + { + double dfaWhitepoint[2]; + double dfaCHR[6]; + + png_get_cHRM(hPNG, psPNGInfo, + &dfaWhitepoint[0], &dfaWhitepoint[1], + &dfaCHR[0], &dfaCHR[1], + &dfaCHR[2], &dfaCHR[3], + &dfaCHR[4], &dfaCHR[5]); + + // Set all the colorimetric metadata. + SetMetadataItem( "SOURCE_PRIMARIES_RED", + CPLString().Printf( "%.9f, %.9f, 1.0", dfaCHR[0], dfaCHR[1] ) , "COLOR_PROFILE" ); + SetMetadataItem( "SOURCE_PRIMARIES_GREEN", + CPLString().Printf( "%.9f, %.9f, 1.0", dfaCHR[2], dfaCHR[3] ) , "COLOR_PROFILE" ); + SetMetadataItem( "SOURCE_PRIMARIES_BLUE", + CPLString().Printf( "%.9f, %.9f, 1.0", dfaCHR[4], dfaCHR[5] ) , "COLOR_PROFILE" ); + + SetMetadataItem( "SOURCE_WHITEPOINT", + CPLString().Printf( "%.9f, %.9f, 1.0", dfaWhitepoint[0], dfaWhitepoint[1] ) , "COLOR_PROFILE" ); + + } + + nPamFlags = nOldPamFlags; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **PNGDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "xml:XMP", "COLOR_PROFILE", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **PNGDataset::GetMetadata( const char * pszDomain ) +{ + if (fpImage == NULL) + return NULL; + if (eAccess == GA_ReadOnly && !bHasReadXMPMetadata && + pszDomain != NULL && EQUAL(pszDomain, "xml:XMP")) + CollectXMPMetadata(); + if (eAccess == GA_ReadOnly && !bHasReadICCMetadata && + pszDomain != NULL && EQUAL(pszDomain, "COLOR_PROFILE")) + LoadICCProfile(); + return GDALPamDataset::GetMetadata(pszDomain); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ +const char *PNGDataset::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if (eAccess == GA_ReadOnly && !bHasReadICCMetadata && + pszDomain != NULL && EQUAL(pszDomain, "COLOR_PROFILE")) + LoadICCProfile(); + return GDALPamDataset::GetMetadataItem(pszName, pszDomain); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int PNGDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 4 ) + return FALSE; + + if( png_sig_cmp(poOpenInfo->pabyHeader, (png_size_t)0, + poOpenInfo->nHeaderBytes) != 0 ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *PNGDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) ) + return NULL; + + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The PNG driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + PNGDataset *poDS; + + poDS = new PNGDataset(); + + poDS->fpImage = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + poDS->eAccess = poOpenInfo->eAccess; + + poDS->hPNG = png_create_read_struct( PNG_LIBPNG_VER_STRING, poDS, + NULL, NULL ); + if (poDS->hPNG == NULL) + { +#if (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 2) || PNG_LIBPNG_VER_MAJOR > 1 + int version = png_access_version_number(); + CPLError( CE_Failure, CPLE_NotSupported, + "The PNG driver failed to access libpng with version '%s'," + " library is actually version '%d'.\n", + PNG_LIBPNG_VER_STRING, version); +#else + CPLError( CE_Failure, CPLE_NotSupported, + "The PNG driver failed to in png_create_read_struct().\n" + "This may be due to version compatibility problems." ); +#endif + delete poDS; + return NULL; + } + + poDS->psPNGInfo = png_create_info_struct( poDS->hPNG ); + +/* -------------------------------------------------------------------- */ +/* Setup error handling. */ +/* -------------------------------------------------------------------- */ + png_set_error_fn( poDS->hPNG, &poDS->sSetJmpContext, png_gdal_error, png_gdal_warning ); + + if( setjmp( poDS->sSetJmpContext ) != 0 ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read pre-image data after ensuring the file is rewound. */ +/* -------------------------------------------------------------------- */ + /* we should likely do a setjmp() here */ + + png_set_read_fn( poDS->hPNG, poDS->fpImage, png_vsi_read_data ); + png_read_info( poDS->hPNG, poDS->psPNGInfo ); + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = png_get_image_width( poDS->hPNG, poDS->psPNGInfo); + poDS->nRasterYSize = png_get_image_height( poDS->hPNG,poDS->psPNGInfo); + + poDS->nBands = png_get_channels( poDS->hPNG, poDS->psPNGInfo ); + poDS->nBitDepth = png_get_bit_depth( poDS->hPNG, poDS->psPNGInfo ); + poDS->bInterlaced = png_get_interlace_type( poDS->hPNG, poDS->psPNGInfo ) + != PNG_INTERLACE_NONE; + + poDS->nColorType = png_get_color_type( poDS->hPNG, poDS->psPNGInfo ); + + if( poDS->nColorType == PNG_COLOR_TYPE_PALETTE + && poDS->nBands > 1 ) + { + CPLDebug( "GDAL", "PNG Driver got %d from png_get_channels(),\n" + "but this kind of image (paletted) can only have one band.\n" + "Correcting and continuing, but this may indicate a bug!", + poDS->nBands ); + poDS->nBands = 1; + } + +/* -------------------------------------------------------------------- */ +/* We want to treat 1,2,4 bit images as eight bit. This call */ +/* causes libpng to unpack the image. */ +/* -------------------------------------------------------------------- */ + if( poDS->nBitDepth < 8 ) + png_set_packing( poDS->hPNG ); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < poDS->nBands; iBand++ ) + poDS->SetBand( iBand+1, new PNGRasterBand( poDS, iBand+1 ) ); + +/* -------------------------------------------------------------------- */ +/* Is there a palette? Note: we should also read back and */ +/* apply transparency values if available. */ +/* -------------------------------------------------------------------- */ + if( poDS->nColorType == PNG_COLOR_TYPE_PALETTE ) + { + png_color *pasPNGPalette; + int nColorCount; + GDALColorEntry oEntry; + unsigned char *trans = NULL; + png_color_16 *trans_values = NULL; + int num_trans = 0; + int nNoDataIndex = -1; + + if( png_get_PLTE( poDS->hPNG, poDS->psPNGInfo, + &pasPNGPalette, &nColorCount ) == 0 ) + nColorCount = 0; + + png_get_tRNS( poDS->hPNG, poDS->psPNGInfo, + &trans, &num_trans, &trans_values ); + + poDS->poColorTable = new GDALColorTable(); + + for( int iColor = nColorCount - 1; iColor >= 0; iColor-- ) + { + oEntry.c1 = pasPNGPalette[iColor].red; + oEntry.c2 = pasPNGPalette[iColor].green; + oEntry.c3 = pasPNGPalette[iColor].blue; + + if( iColor < num_trans ) + { + oEntry.c4 = trans[iColor]; + if( oEntry.c4 == 0 ) + { + if( nNoDataIndex == -1 ) + nNoDataIndex = iColor; + else + nNoDataIndex = -2; + } + } + else + oEntry.c4 = 255; + + poDS->poColorTable->SetColorEntry( iColor, &oEntry ); + } + + /* + ** Special hack to an index as the no data value, as long as it + ** is the _only_ transparent color in the palette. + */ + if( nNoDataIndex > -1 ) + { + poDS->GetRasterBand(1)->SetNoDataValue(nNoDataIndex); + } + } + +/* -------------------------------------------------------------------- */ +/* Check for transparency values in greyscale images. */ +/* -------------------------------------------------------------------- */ + if( poDS->nColorType == PNG_COLOR_TYPE_GRAY ) + { + png_color_16 *trans_values = NULL; + unsigned char *trans; + int num_trans; + + if( png_get_tRNS( poDS->hPNG, poDS->psPNGInfo, + &trans, &num_trans, &trans_values ) != 0 + && trans_values != NULL ) + { + poDS->GetRasterBand(1)->SetNoDataValue(trans_values->gray); + } + } + +/* -------------------------------------------------------------------- */ +/* Check for nodata color for RGB images. */ +/* -------------------------------------------------------------------- */ + if( poDS->nColorType == PNG_COLOR_TYPE_RGB ) + { + png_color_16 *trans_values = NULL; + unsigned char *trans; + int num_trans; + + if( png_get_tRNS( poDS->hPNG, poDS->psPNGInfo, + &trans, &num_trans, &trans_values ) != 0 + && trans_values != NULL ) + { + CPLString oNDValue; + + oNDValue.Printf( "%d %d %d", + trans_values->red, + trans_values->green, + trans_values->blue ); + poDS->SetMetadataItem( "NODATA_VALUES", oNDValue.c_str() ); + + poDS->GetRasterBand(1)->SetNoDataValue(trans_values->red); + poDS->GetRasterBand(2)->SetNoDataValue(trans_values->green); + poDS->GetRasterBand(3)->SetNoDataValue(trans_values->blue); + } + } + +/* -------------------------------------------------------------------- */ +/* Extract any text chunks as "metadata". */ +/* -------------------------------------------------------------------- */ + poDS->CollectMetadata(); + +/* -------------------------------------------------------------------- */ +/* More metadata. */ +/* -------------------------------------------------------------------- */ + if( poDS->nBands > 1 ) + { + poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML( poOpenInfo->GetSiblingFiles() ); + +/* -------------------------------------------------------------------- */ +/* Open overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, + poOpenInfo->GetSiblingFiles() ); + + return poDS; +} + +/************************************************************************/ +/* LoadWorldFile() */ +/************************************************************************/ + +void PNGDataset::LoadWorldFile() +{ + if (bHasTriedLoadWorldFile) + return; + bHasTriedLoadWorldFile = TRUE; + + char* pszWldFilename = NULL; + bGeoTransformValid = + GDALReadWorldFile2( GetDescription(), NULL, + adfGeoTransform, oOvManager.GetSiblingFiles(), + &pszWldFilename); + + if( !bGeoTransformValid ) + bGeoTransformValid = + GDALReadWorldFile2( GetDescription(), ".wld", + adfGeoTransform, oOvManager.GetSiblingFiles(), + &pszWldFilename); + + if (pszWldFilename) + { + osWldFilename = pszWldFilename; + CPLFree(pszWldFilename); + } +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **PNGDataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + + LoadWorldFile(); + + if (osWldFilename.size() != 0 && + CSLFindString(papszFileList, osWldFilename) == -1) + { + papszFileList = CSLAddString( papszFileList, osWldFilename ); + } + + return papszFileList; +} + +/************************************************************************/ +/* WriteMetadataAsText() */ +/************************************************************************/ + +#if defined(PNG_iTXt_SUPPORTED) || ((PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 4) || PNG_LIBPNG_VER_MAJOR > 1) +#define HAVE_ITXT_SUPPORT +#endif + +#ifdef HAVE_ITXT_SUPPORT +static int IsASCII(const char* pszStr) +{ + for(int i=0;pszStr[i]!='\0';i++) + { + if( ((GByte*)pszStr)[i] >= 128 ) + return FALSE; + } + return TRUE; +} +#endif + +void PNGDataset::WriteMetadataAsText(png_structp hPNG, png_infop psPNGInfo, + const char* pszKey, const char* pszValue) +{ + png_text sText; + memset(&sText, 0, sizeof(png_text)); + sText.compression = PNG_TEXT_COMPRESSION_NONE; + sText.key = (png_charp) pszKey; + sText.text = (png_charp) pszValue; +#ifdef HAVE_ITXT_SUPPORT + // UTF-8 values should be written in iTXt, whereas TEXT should be LATIN-1 + if( !IsASCII(pszValue) && CPLIsUTF8(pszValue, -1) ) + sText.compression = PNG_ITXT_COMPRESSION_NONE; +#endif + png_set_text(hPNG, psPNGInfo, &sText, 1); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * +PNGDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + if( nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "PNG driver doesn't support %d bands. Must be 1 (grey),\n" + "2 (grey+alpha), 3 (rgb) or 4 (rgba) bands.\n", + nBands ); + + return NULL; + } + + if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte + && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16 ) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "PNG driver doesn't support data type %s. " + "Only eight bit (Byte) and sixteen bit (UInt16) bands supported. %s\n", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()), + (bStrict) ? "" : "Defaulting to Byte" ); + + if (bStrict) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Setup some parameters. */ +/* -------------------------------------------------------------------- */ + int nColorType=0, nBitDepth; + GDALDataType eType; + + if( nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() == NULL ) + nColorType = PNG_COLOR_TYPE_GRAY; + else if( nBands == 1 ) + nColorType = PNG_COLOR_TYPE_PALETTE; + else if( nBands == 2 ) + nColorType = PNG_COLOR_TYPE_GRAY_ALPHA; + else if( nBands == 3 ) + nColorType = PNG_COLOR_TYPE_RGB; + else if( nBands == 4 ) + nColorType = PNG_COLOR_TYPE_RGB_ALPHA; + + if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16 ) + { + eType = GDT_Byte; + nBitDepth = 8; + } + else + { + eType = GDT_UInt16; + nBitDepth = 16; + } + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpImage; + + fpImage = VSIFOpenL( pszFilename, "wb" ); + if( fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create png file %s.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Initialize PNG access to the file. */ +/* -------------------------------------------------------------------- */ + png_structp hPNG; + png_infop psPNGInfo; + + jmp_buf sSetJmpContext; + hPNG = png_create_write_struct( PNG_LIBPNG_VER_STRING, + &sSetJmpContext, png_gdal_error, png_gdal_warning ); + psPNGInfo = png_create_info_struct( hPNG ); + + if( setjmp( sSetJmpContext ) != 0 ) + { + VSIFCloseL( fpImage ); + png_destroy_write_struct( &hPNG, &psPNGInfo ); + return NULL; + } + + png_set_write_fn( hPNG, fpImage, png_vsi_write_data, png_vsi_flush ); + + png_set_IHDR( hPNG, psPNGInfo, nXSize, nYSize, + nBitDepth, nColorType, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE ); + +/* -------------------------------------------------------------------- */ +/* Do we want to control the compression level? */ +/* -------------------------------------------------------------------- */ + const char *pszLevel = CSLFetchNameValue( papszOptions, "ZLEVEL" ); + + if( pszLevel ) + { + int nLevel = atoi(pszLevel); + if( nLevel < 1 || nLevel > 9 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Illegal ZLEVEL value '%s', should be 1-9.", + pszLevel ); + return NULL; + } + + png_set_compression_level( hPNG, nLevel ); + } + +/* -------------------------------------------------------------------- */ +/* Try to handle nodata values as a tRNS block (note for */ +/* paletted images, we save the effect to apply as part of */ +/* palette). */ +/* -------------------------------------------------------------------- */ + png_color_16 sTRNSColor; + + // Gray nodata. + if( nColorType == PNG_COLOR_TYPE_GRAY ) + { + int bHaveNoData = FALSE; + double dfNoDataValue = -1; + + dfNoDataValue = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoData ); + + if ( bHaveNoData && dfNoDataValue >= 0 && dfNoDataValue < 65536 ) + { + sTRNSColor.gray = (png_uint_16) dfNoDataValue; + png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor ); + } + } + + // RGB nodata. + if( nColorType == PNG_COLOR_TYPE_RGB ) + { + // First try to use the NODATA_VALUES metadata item. + if ( poSrcDS->GetMetadataItem( "NODATA_VALUES" ) != NULL ) + { + char **papszValues = CSLTokenizeString( + poSrcDS->GetMetadataItem( "NODATA_VALUES" ) ); + + if( CSLCount(papszValues) >= 3 ) + { + sTRNSColor.red = (png_uint_16) atoi(papszValues[0]); + sTRNSColor.green = (png_uint_16) atoi(papszValues[1]); + sTRNSColor.blue = (png_uint_16) atoi(papszValues[2]); + png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor ); + } + + CSLDestroy( papszValues ); + } + // Otherwise, get the nodata value from the bands. + else + { + int bHaveNoDataRed = FALSE; + int bHaveNoDataGreen = FALSE; + int bHaveNoDataBlue = FALSE; + double dfNoDataValueRed = -1; + double dfNoDataValueGreen = -1; + double dfNoDataValueBlue = -1; + + dfNoDataValueRed = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoDataRed ); + dfNoDataValueGreen= poSrcDS->GetRasterBand(2)->GetNoDataValue( &bHaveNoDataGreen ); + dfNoDataValueBlue = poSrcDS->GetRasterBand(3)->GetNoDataValue( &bHaveNoDataBlue ); + + if ( ( bHaveNoDataRed && dfNoDataValueRed >= 0 && dfNoDataValueRed < 65536 ) && + ( bHaveNoDataGreen && dfNoDataValueGreen >= 0 && dfNoDataValueGreen < 65536 ) && + ( bHaveNoDataBlue && dfNoDataValueBlue >= 0 && dfNoDataValueBlue < 65536 ) ) + { + sTRNSColor.red = (png_uint_16) dfNoDataValueRed; + sTRNSColor.green = (png_uint_16) dfNoDataValueGreen; + sTRNSColor.blue = (png_uint_16) dfNoDataValueBlue; + png_set_tRNS( hPNG, psPNGInfo, NULL, 0, &sTRNSColor ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Copy colour profile data */ +/* -------------------------------------------------------------------- */ + const char *pszICCProfile = CSLFetchNameValue(papszOptions, "SOURCE_ICC_PROFILE"); + const char *pszICCProfileName = CSLFetchNameValue(papszOptions, "SOURCE_ICC_PROFILE_NAME"); + if (pszICCProfileName == NULL) + pszICCProfileName = poSrcDS->GetMetadataItem( "SOURCE_ICC_PROFILE_NAME", "COLOR_PROFILE" ); + + if (pszICCProfile == NULL) + pszICCProfile = poSrcDS->GetMetadataItem( "SOURCE_ICC_PROFILE", "COLOR_PROFILE" ); + + if ((pszICCProfileName != NULL) && EQUAL(pszICCProfileName, "sRGB")) + { + pszICCProfile = NULL; + + png_set_sRGB(hPNG, psPNGInfo, PNG_sRGB_INTENT_PERCEPTUAL); + } + + if (pszICCProfile != NULL) + { + char *pEmbedBuffer = CPLStrdup(pszICCProfile); + png_uint_32 nEmbedLen = CPLBase64DecodeInPlace((GByte*)pEmbedBuffer); + const char* pszLocalICCProfileName = (pszICCProfileName!=NULL)?pszICCProfileName:"ICC Profile"; + + png_set_iCCP(hPNG, psPNGInfo, +#if (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR > 4) || PNG_LIBPNG_VER_MAJOR > 1 + pszLocalICCProfileName, +#else + (png_charp)pszLocalICCProfileName, +#endif + 0, +#if (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR > 4) || PNG_LIBPNG_VER_MAJOR > 1 + (png_const_bytep)pEmbedBuffer, +#else + (png_charp)pEmbedBuffer, +#endif + nEmbedLen); + + CPLFree(pEmbedBuffer); + } + else if ((pszICCProfileName == NULL) || !EQUAL(pszICCProfileName, "sRGB")) + { + // Output gamma, primaries and whitepoint + const char *pszGamma = CSLFetchNameValue(papszOptions, "PNG_GAMMA"); + if (pszGamma == NULL) + pszGamma = poSrcDS->GetMetadataItem( "PNG_GAMMA", "COLOR_PROFILE" ); + + if (pszGamma != NULL) + { + double dfGamma = CPLAtof(pszGamma); + png_set_gAMA(hPNG, psPNGInfo, dfGamma); + } + + const char *pszPrimariesRed = CSLFetchNameValue(papszOptions, "SOURCE_PRIMARIES_RED"); + if (pszPrimariesRed == NULL) + pszPrimariesRed = poSrcDS->GetMetadataItem( "SOURCE_PRIMARIES_RED", "COLOR_PROFILE" ); + const char *pszPrimariesGreen = CSLFetchNameValue(papszOptions, "SOURCE_PRIMARIES_GREEN"); + if (pszPrimariesGreen == NULL) + pszPrimariesGreen = poSrcDS->GetMetadataItem( "SOURCE_PRIMARIES_GREEN", "COLOR_PROFILE" ); + const char *pszPrimariesBlue = CSLFetchNameValue(papszOptions, "SOURCE_PRIMARIES_BLUE"); + if (pszPrimariesBlue == NULL) + pszPrimariesBlue = poSrcDS->GetMetadataItem( "SOURCE_PRIMARIES_BLUE", "COLOR_PROFILE" ); + const char *pszWhitepoint = CSLFetchNameValue(papszOptions, "SOURCE_WHITEPOINT"); + if (pszWhitepoint == NULL) + pszWhitepoint = poSrcDS->GetMetadataItem( "SOURCE_WHITEPOINT", "COLOR_PROFILE" ); + + if ((pszPrimariesRed != NULL) && (pszPrimariesGreen != NULL) && (pszPrimariesBlue != NULL) && + (pszWhitepoint != NULL)) + { + bool bOk = true; + double faColour[8]; + char** apapszTokenList[4]; + + apapszTokenList[0] = CSLTokenizeString2( pszWhitepoint, ",", + CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + apapszTokenList[1] = CSLTokenizeString2( pszPrimariesRed, ",", + CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + apapszTokenList[2] = CSLTokenizeString2( pszPrimariesGreen, ",", + CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + apapszTokenList[3] = CSLTokenizeString2( pszPrimariesBlue, ",", + CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + + if ((CSLCount( apapszTokenList[0] ) == 3) && + (CSLCount( apapszTokenList[1] ) == 3) && + (CSLCount( apapszTokenList[2] ) == 3) && + (CSLCount( apapszTokenList[3] ) == 3)) + { + for( int i = 0; i < 4; i++ ) + { + for( int j = 0; j < 3; j++ ) + { + double v = CPLAtof(apapszTokenList[i][j]); + + if (j == 2) + { + /* Last term of xyY colour must be 1.0 */ + if (v != 1.0) + { + bOk = false; + break; + } + } + else + { + faColour[i*2 + j] = v; + } + } + if (!bOk) + break; + } + + if (bOk) + { + png_set_cHRM(hPNG, psPNGInfo, + faColour[0], faColour[1], + faColour[2], faColour[3], + faColour[4], faColour[5], + faColour[6], faColour[7]); + + } + } + + CSLDestroy( apapszTokenList[0] ); + CSLDestroy( apapszTokenList[1] ); + CSLDestroy( apapszTokenList[2] ); + CSLDestroy( apapszTokenList[3] ); + } + + } + +/* -------------------------------------------------------------------- */ +/* Write palette if there is one. Technically, I think it is */ +/* possible to write 16bit palettes for PNG, but we will omit */ +/* this for now. */ +/* -------------------------------------------------------------------- */ + png_color *pasPNGColors = NULL; + unsigned char *pabyAlpha = NULL; + + if( nColorType == PNG_COLOR_TYPE_PALETTE ) + { + GDALColorTable *poCT; + GDALColorEntry sEntry; + int iColor, bFoundTrans = FALSE; + int bHaveNoData = FALSE; + double dfNoDataValue = -1; + + dfNoDataValue = poSrcDS->GetRasterBand(1)->GetNoDataValue( &bHaveNoData ); + + poCT = poSrcDS->GetRasterBand(1)->GetColorTable(); + + pasPNGColors = (png_color *) CPLMalloc(sizeof(png_color) * + poCT->GetColorEntryCount()); + + for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ ) + { + poCT->GetColorEntryAsRGB( iColor, &sEntry ); + if( sEntry.c4 != 255 ) + bFoundTrans = TRUE; + + pasPNGColors[iColor].red = (png_byte) sEntry.c1; + pasPNGColors[iColor].green = (png_byte) sEntry.c2; + pasPNGColors[iColor].blue = (png_byte) sEntry.c3; + } + + png_set_PLTE( hPNG, psPNGInfo, pasPNGColors, + poCT->GetColorEntryCount() ); + +/* -------------------------------------------------------------------- */ +/* If we have transparent elements in the palette we need to */ +/* write a transparency block. */ +/* -------------------------------------------------------------------- */ + if( bFoundTrans || bHaveNoData ) + { + pabyAlpha = (unsigned char *)CPLMalloc(poCT->GetColorEntryCount()); + + for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ ) + { + poCT->GetColorEntryAsRGB( iColor, &sEntry ); + pabyAlpha[iColor] = (unsigned char) sEntry.c4; + + if( bHaveNoData && iColor == (int) dfNoDataValue ) + pabyAlpha[iColor] = 0; + } + + png_set_tRNS( hPNG, psPNGInfo, pabyAlpha, + poCT->GetColorEntryCount(), NULL ); + } + } + +/* -------------------------------------------------------------------- */ +/* Add text info */ +/* -------------------------------------------------------------------- */ + /* Predefined keywords. See "4.2.7 tEXt Textual data" of http://www.w3.org/TR/PNG-Chunks.html */ + const char* apszKeywords[] = { "Title", "Author", "Description", "Copyright", + "Creation Time", "Software", "Disclaimer", + "Warning", "Source", "Comment", NULL }; + int bWriteMetadataAsText = CSLTestBoolean( + CSLFetchNameValueDef(papszOptions, "WRITE_METADATA_AS_TEXT", "FALSE")); + for(int i=0;apszKeywords[i]!=NULL;i++) + { + const char* pszKey = apszKeywords[i]; + const char* pszValue = CSLFetchNameValue(papszOptions, pszKey); + if( pszValue == NULL && bWriteMetadataAsText ) + pszValue = poSrcDS->GetMetadataItem(pszKey); + if( pszValue != NULL ) + { + WriteMetadataAsText(hPNG, psPNGInfo, pszKey, pszValue); + } + } + if( bWriteMetadataAsText ) + { + char** papszSrcMD = poSrcDS->GetMetadata(); + for( ; papszSrcMD && *papszSrcMD; papszSrcMD++ ) + { + char* pszKey = NULL; + const char* pszValue = CPLParseNameValue(*papszSrcMD, &pszKey ); + if( pszKey && pszValue ) + { + if( CSLFindString((char**)apszKeywords, pszKey) < 0 && + !EQUAL(pszKey, "AREA_OR_POINT") && !EQUAL(pszKey, "NODATA_VALUES") ) + { + WriteMetadataAsText(hPNG, psPNGInfo, pszKey, pszValue); + } + CPLFree(pszKey); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Write infos */ +/* -------------------------------------------------------------------- */ + png_write_info( hPNG, psPNGInfo ); + +/* -------------------------------------------------------------------- */ +/* Loop over image, copying image data. */ +/* -------------------------------------------------------------------- */ + GByte *pabyScanline; + CPLErr eErr = CE_None; + int nWordSize = nBitDepth/8; + + pabyScanline = (GByte *) CPLMalloc( nBands * nXSize * nWordSize ); + + for( int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ ) + { + png_bytep row = pabyScanline; + + eErr = poSrcDS->RasterIO( GF_Read, 0, iLine, nXSize, 1, + pabyScanline, + nXSize, 1, eType, + nBands, NULL, + nBands * nWordSize, + nBands * nXSize * nWordSize, + nWordSize, + NULL ); + +#ifdef CPL_LSB + if( nBitDepth == 16 ) + GDALSwapWords( row, 2, nXSize * nBands, 2 ); +#endif + if( eErr == CE_None ) + png_write_rows( hPNG, &row, 1 ); + + if( eErr == CE_None + && !pfnProgress( (iLine+1) / (double) nYSize, + NULL, pProgressData ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + + CPLFree( pabyScanline ); + + png_write_end( hPNG, psPNGInfo ); + png_destroy_write_struct( &hPNG, &psPNGInfo ); + + VSIFCloseL( fpImage ); + + CPLFree( pabyAlpha ); + CPLFree( pasPNGColors ); + + if( eErr != CE_None ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Do we need a world file? */ +/* -------------------------------------------------------------------- */ + if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) ) + { + double adfGeoTransform[6]; + + if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None ) + GDALWriteWorldFile( pszFilename, "wld", adfGeoTransform ); + } + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + + /* If outputing to stdout, we can't reopen it, so we'll return */ + /* a fake dataset to make the caller happy */ + if( CSLTestBoolean(CPLGetConfigOption("GDAL_OPEN_AFTER_COPY", "YES")) ) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); + PNGDataset *poDS = (PNGDataset*) PNGDataset::Open( &oOpenInfo ); + CPLPopErrorHandler(); + if( poDS ) + { + int nFlags = GCIF_PAM_DEFAULT; + if( bWriteMetadataAsText ) + nFlags &= ~GCIF_METADATA; + poDS->CloneInfo( poSrcDS, nFlags ); + return poDS; + } + CPLErrorReset(); + } + + PNGDataset* poPNG_DS = new PNGDataset(); + poPNG_DS->nRasterXSize = nXSize; + poPNG_DS->nRasterYSize = nYSize; + poPNG_DS->nBitDepth = nBitDepth; + for(int i=0;iSetBand( i+1, new PNGRasterBand( poPNG_DS, i+1) ); + return poPNG_DS; +} + +/************************************************************************/ +/* png_vsi_read_data() */ +/* */ +/* Read data callback through VSI. */ +/************************************************************************/ +static void +png_vsi_read_data(png_structp png_ptr, png_bytep data, png_size_t length) + +{ + png_size_t check; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ + check = (png_size_t)VSIFReadL(data, (png_size_t)1, length, + (VSILFILE*)png_get_io_ptr(png_ptr)); + + if (check != length) + png_error(png_ptr, "Read Error"); +} + +/************************************************************************/ +/* png_vsi_write_data() */ +/************************************************************************/ + +static void +png_vsi_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + check = VSIFWriteL(data, 1, length, (VSILFILE*)png_get_io_ptr(png_ptr)); + + if (check != length) + png_error(png_ptr, "Write Error"); +} + +/************************************************************************/ +/* png_vsi_flush() */ +/************************************************************************/ +static void png_vsi_flush(png_structp png_ptr) +{ + VSIFFlushL( (VSILFILE*)png_get_io_ptr(png_ptr) ); +} + +/************************************************************************/ +/* png_gdal_error() */ +/************************************************************************/ + +static void png_gdal_error( png_structp png_ptr, const char *error_message ) +{ + CPLError( CE_Failure, CPLE_AppDefined, + "libpng: %s", error_message ); + + // We have to use longjmp instead of a C++ exception because + // libpng is generally not built as C++ and so won't honour unwind + // semantics. Ugg. + + jmp_buf* psSetJmpContext = (jmp_buf*) png_get_error_ptr(png_ptr); + if (psSetJmpContext) + { + longjmp( *psSetJmpContext, 1 ); + } +} + +/************************************************************************/ +/* png_gdal_warning() */ +/************************************************************************/ + +static void png_gdal_warning( CPL_UNUSED png_structp png_ptr, const char *error_message ) +{ + CPLError( CE_Warning, CPLE_AppDefined, + "libpng: %s", error_message ); +} + +/************************************************************************/ +/* GDALRegister_PNG() */ +/************************************************************************/ + +void GDALRegister_PNG() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "PNG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "PNG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Portable Network Graphics" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#PNG" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "png" ); + poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/png" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"\n" +" \n" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = PNGDataset::Open; + poDriver->pfnCreateCopy = PNGDataset::CreateCopy; + poDriver->pfnIdentify = PNGDataset::Identify; +#ifdef SUPPORT_CREATE + poDriver->pfnCreate = PNGDataset::Create; +#endif + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + +#ifdef SUPPORT_CREATE +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr PNGRasterBand::IWriteBlock(int x, int y, void* pvData) +{ + // rcg, added to support Create(). + + PNGDataset& ds = *(PNGDataset*)poDS; + + + // Write the block (or consolidate into multichannel block) + // and then write. + + const GDALDataType dt = this->GetRasterDataType(); + const size_t wordsize = ds.m_nBitDepth / 8; + GDALCopyWords( pvData, dt, wordsize, + ds.m_pabyBuffer + (nBand-1) * wordsize, + dt, ds.nBands * wordsize, + nBlockXSize ); + + // See if we got all the bands. + size_t i; + m_bBandProvided[nBand - 1] = TRUE; + for(i = 0; i < ds.nBands; i++) + { + if(!m_bBandProvided[i]) + return CE_None; + } + + // We received all the bands, so + // reset band flags and write pixels out. + this->reset_band_provision_flags(); + + + // If first block, write out file header. + if(x == 0 && y == 0) + { + CPLErr err = ds.write_png_header(); + if(err != CE_None) + return err; + } + +#ifdef CPL_LSB + if( ds.m_nBitDepth == 16 ) + GDALSwapWords( ds.m_pabyBuffer, 2, nBlockXSize * ds.nBands, 2 ); +#endif + png_write_rows( ds.m_hPNG, &ds.m_pabyBuffer, 1 ); + + return CE_None; +} + + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr PNGDataset::SetGeoTransform( double * padfTransform ) +{ + // rcg, added to support Create(). + + CPLErr eErr = CE_None; + + memcpy( m_adfGeoTransform, padfTransform, sizeof(double) * 6 ); + + if ( m_pszFilename ) + { + if ( GDALWriteWorldFile( m_pszFilename, "wld", m_adfGeoTransform ) + == FALSE ) + { + CPLError( CE_Failure, CPLE_FileIO, "Can't write world file." ); + eErr = CE_Failure; + } + } + + return eErr; +} + + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr PNGRasterBand::SetColorTable(GDALColorTable* poCT) +{ + if( poCT == NULL ) + return CE_Failure; + + // rcg, added to support Create(). + // We get called even for grayscale files, since some + // formats need a palette even then. PNG doesn't, so + // if a gray palette is given, just ignore it. + + GDALColorEntry sEntry; + for( size_t i = 0; i < poCT->GetColorEntryCount(); i++ ) + { + poCT->GetColorEntryAsRGB( i, &sEntry ); + if( sEntry.c1 != sEntry.c2 || sEntry.c1 != sEntry.c3) + { + CPLErr err = GDALPamRasterBand::SetColorTable(poCT); + if(err != CE_None) + return err; + + PNGDataset& ds = *(PNGDataset*)poDS; + ds.m_nColorType = PNG_COLOR_TYPE_PALETTE; + break; + // band::IWriteBlock will emit color table as part of + // header preceding first block write. + } + } + + return CE_None; +} + + +/************************************************************************/ +/* PNGDataset::write_png_header() */ +/************************************************************************/ + +CPLErr PNGDataset::write_png_header() +{ +/* -------------------------------------------------------------------- */ +/* Initialize PNG access to the file. */ +/* -------------------------------------------------------------------- */ + + m_hPNG = png_create_write_struct( + PNG_LIBPNG_VER_STRING, NULL, + png_gdal_error, png_gdal_warning ); + + m_psPNGInfo = png_create_info_struct( m_hPNG ); + + png_set_write_fn( m_hPNG, m_fpImage, png_vsi_write_data, png_vsi_flush ); + + png_set_IHDR( m_hPNG, m_psPNGInfo, nRasterXSize, nRasterYSize, + m_nBitDepth, m_nColorType, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); + + png_set_compression_level(m_hPNG, Z_BEST_COMPRESSION); + + //png_set_swap_alpha(m_hPNG); // Use RGBA order, not ARGB. + +/* -------------------------------------------------------------------- */ +/* Try to handle nodata values as a tRNS block (note for */ +/* paletted images, we save the effect to apply as part of */ +/* palette). */ +/* -------------------------------------------------------------------- */ + //m_bHaveNoData = FALSE; + //m_dfNoDataValue = -1; + png_color_16 sTRNSColor; + + + int bHaveNoData = FALSE; + double dfNoDataValue = -1; + + if( m_nColorType == PNG_COLOR_TYPE_GRAY ) + { + dfNoDataValue = this->GetRasterBand(1)->GetNoDataValue( &bHaveNoData ); + + if ( bHaveNoData && dfNoDataValue >= 0 && dfNoDataValue < 65536 ) + { + sTRNSColor.gray = (png_uint_16) dfNoDataValue; + png_set_tRNS( m_hPNG, m_psPNGInfo, NULL, 0, &sTRNSColor ); + } + } + + // RGB nodata. + if( nColorType == PNG_COLOR_TYPE_RGB ) + { + // First try to use the NODATA_VALUES metadata item. + if ( this->GetMetadataItem( "NODATA_VALUES" ) != NULL ) + { + char **papszValues = CSLTokenizeString( + this->GetMetadataItem( "NODATA_VALUES" ) ); + + if( CSLCount(papszValues) >= 3 ) + { + sTRNSColor.red = (png_uint_16) atoi(papszValues[0]); + sTRNSColor.green = (png_uint_16) atoi(papszValues[1]); + sTRNSColor.blue = (png_uint_16) atoi(papszValues[2]); + png_set_tRNS( m_hPNG, m_psPNGInfo, NULL, 0, &sTRNSColor ); + } + + CSLDestroy( papszValues ); + } + // Otherwise, get the nodata value from the bands. + else + { + int bHaveNoDataRed = FALSE; + int bHaveNoDataGreen = FALSE; + int bHaveNoDataBlue = FALSE; + double dfNoDataValueRed = -1; + double dfNoDataValueGreen = -1; + double dfNoDataValueBlue = -1; + + dfNoDataValueRed = this->GetRasterBand(1)->GetNoDataValue( &bHaveNoDataRed ); + dfNoDataValueGreen= this->GetRasterBand(2)->GetNoDataValue( &bHaveNoDataGreen ); + dfNoDataValueBlue = this->GetRasterBand(3)->GetNoDataValue( &bHaveNoDataBlue ); + + if ( ( bHaveNoDataRed && dfNoDataValueRed >= 0 && dfNoDataValueRed < 65536 ) && + ( bHaveNoDataGreen && dfNoDataValueGreen >= 0 && dfNoDataValueGreen < 65536 ) && + ( bHaveNoDataBlue && dfNoDataValueBlue >= 0 && dfNoDataValueBlue < 65536 ) ) + { + sTRNSColor.red = (png_uint_16) dfNoDataValueRed; + sTRNSColor.green = (png_uint_16) dfNoDataValueGreen; + sTRNSColor.blue = (png_uint_16) dfNoDataValueBlue; + png_set_tRNS( m_hPNG, m_psPNGInfo, NULL, 0, &sTRNSColor ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Write palette if there is one. Technically, I think it is */ +/* possible to write 16bit palettes for PNG, but we will omit */ +/* this for now. */ +/* -------------------------------------------------------------------- */ + + if( nColorType == PNG_COLOR_TYPE_PALETTE ) + { + GDALColorTable *poCT; + GDALColorEntry sEntry; + int iColor, bFoundTrans = FALSE; + int bHaveNoData = FALSE; + double dfNoDataValue = -1; + + dfNoDataValue = this->GetRasterBand(1)->GetNoDataValue( &bHaveNoData ); + + poCT = this->GetRasterBand(1)->GetColorTable(); + + m_pasPNGColors = (png_color *) CPLMalloc(sizeof(png_color) * + poCT->GetColorEntryCount()); + + for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ ) + { + poCT->GetColorEntryAsRGB( iColor, &sEntry ); + if( sEntry.c4 != 255 ) + bFoundTrans = TRUE; + + m_pasPNGColors[iColor].red = (png_byte) sEntry.c1; + m_pasPNGColors[iColor].green = (png_byte) sEntry.c2; + m_pasPNGColors[iColor].blue = (png_byte) sEntry.c3; + } + + png_set_PLTE( m_hPNG, m_psPNGInfo, m_pasPNGColors, + poCT->GetColorEntryCount() ); + +/* -------------------------------------------------------------------- */ +/* If we have transparent elements in the palette we need to */ +/* write a transparency block. */ +/* -------------------------------------------------------------------- */ + if( bFoundTrans || bHaveNoData ) + { + m_pabyAlpha = (unsigned char *)CPLMalloc(poCT->GetColorEntryCount()); + + for( iColor = 0; iColor < poCT->GetColorEntryCount(); iColor++ ) + { + poCT->GetColorEntryAsRGB( iColor, &sEntry ); + m_pabyAlpha[iColor] = (unsigned char) sEntry.c4; + + if( bHaveNoData && iColor == (int) dfNoDataValue ) + m_pabyAlpha[iColor] = 0; + } + + png_set_tRNS( m_hPNG, m_psPNGInfo, m_pabyAlpha, + poCT->GetColorEntryCount(), NULL ); + } + } + + png_write_info( m_hPNG, m_psPNGInfo ); + return CE_None; +} + + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +// static +GDALDataset *PNGDataset::Create +( + const char* pszFilename, + int nXSize, int nYSize, + int nBands, + GDALDataType eType, + char **papszOptions +) +{ + if( eType != GDT_Byte && eType != GDT_UInt16) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create PNG dataset with an illegal\n" + "data type (%s), only Byte and UInt16 supported by the format.\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + + if( nBands < 1 || nBands > 4 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "PNG driver doesn't support %d bands. " + "Must be 1 (gray/indexed color),\n" + "2 (gray+alpha), 3 (rgb) or 4 (rgba) bands.\n", + nBands ); + + return NULL; + } + + + // Bands are: + // 1: grayscale or indexed color + // 2: gray plus alpha + // 3: rgb + // 4: rgb plus alpha + + if(nXSize < 1 || nYSize < 1) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Specified pixel dimensions (% d x %d) are bad.\n", + nXSize, nYSize ); + } + +/* -------------------------------------------------------------------- */ +/* Setup some parameters. */ +/* -------------------------------------------------------------------- */ + + PNGDataset* poDS = new PNGDataset(); + + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->eAccess = GA_Update; + poDS->nBands = nBands; + + + switch(nBands) + { + case 1: + poDS->m_nColorType = PNG_COLOR_TYPE_GRAY; + break;// if a non-gray palette is set, we'll change this. + + case 2: + poDS->m_nColorType = PNG_COLOR_TYPE_GRAY_ALPHA; + break; + + case 3: + poDS->m_nColorType = PNG_COLOR_TYPE_RGB; + break; + + case 4: + poDS->m_nColorType = PNG_COLOR_TYPE_RGB_ALPHA; + break; + } + + poDS->m_nBitDepth = (eType == GDT_Byte ? 8 : 16); + + poDS->m_pabyBuffer = (GByte *) CPLMalloc( + nBands * nXSize * poDS->m_nBitDepth / 8 ); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + poDS->SetBand( iBand, new PNGRasterBand( poDS, iBand ) ); + +/* -------------------------------------------------------------------- */ +/* Do we need a world file? */ +/* -------------------------------------------------------------------- */ + if( CSLFetchBoolean( papszOptions, "WORLDFILE", FALSE ) ) + poDS->m_bGeoTransformValid = TRUE; + +/* -------------------------------------------------------------------- */ +/* Create the file. */ +/* -------------------------------------------------------------------- */ + + poDS->m_fpImage = VSIFOpenL( pszFilename, "wb" ); + if( poDS->m_fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create PNG file %s.\n", + pszFilename ); + delete poDS; + return NULL; + } + + poDS->m_pszFilename = CPLStrdup(pszFilename); + + return poDS; +} + +#endif diff --git a/bazaar/plugin/gdal/frmts/postgisraster/GNUmakefile b/bazaar/plugin/gdal/frmts/postgisraster/GNUmakefile new file mode 100644 index 000000000..8a62ff367 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/postgisraster/GNUmakefile @@ -0,0 +1,24 @@ +# USER CONFIGURATION +# END OF USER CONFIGURATION + + +include ../../GDALmake.opt + +OBJ = postgisrasterdriver.o postgisrasterdataset.o postgisrasterrasterband.o postgisrastertiledataset.o postgisrastertilerasterband.o postgisrastertools.o + + +CPPFLAGS := -I ../mem -I ../vrt $(XTRA_OPT) $(PG_INC) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +$(O_OBJ): postgisraster.h ../vrt/vrtdataset.h ../mem/memdataset.h + +clean: + rm -f *.o $(O_OBJ) + +../o/%.$(OBJ_EXT): + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/postgisraster/makefile.vc b/bazaar/plugin/gdal/frmts/postgisraster/makefile.vc new file mode 100644 index 000000000..4f3c63f88 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/postgisraster/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = postgisrasterdataset.obj postgisrasterrasterband.obj postgisrasterdriver.obj postgisrastertiledataset.obj postgisrastertilerasterband.obj postgisrastertools.obj + +EXTRAFLAGS = -I ../mem -I ../vrt -I$(PG_INC_DIR) + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/postgisraster/postgisraster.h b/bazaar/plugin/gdal/frmts/postgisraster/postgisraster.h new file mode 100644 index 000000000..fe5aa2c5a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/postgisraster/postgisraster.h @@ -0,0 +1,402 @@ +/*********************************************************************** + * File : postgisraster.h + * Project: PostGIS Raster driver + * Purpose: Main header file for PostGIS Raster Driver + * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com + * jorgearevalo@libregis.org + * + * Author: David Zwarg, dzwarg@azavea.com + * + * Last changes: $Id: $ + * + *********************************************************************** + * Copyright (c) 2009 - 2013, Jorge Arevalo, David Zwarg + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"),to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + **********************************************************************/ + +#ifndef POSTGISRASTER_H_INCLUDED +#define POSTGISRASTER_H_INCLUDED + +#include "gdal_priv.h" +#include "libpq-fe.h" +#include "vrtdataset.h" +#include "cpl_quad_tree.h" +#include +#include + +//#define DEBUG_VERBOSE +//#define DEBUG_QUERY + +#if defined(DEBUG_VERBOSE) && !defined(DEBUG_QUERY) +#define DEBUG_QUERY +#endif + +/** + * The block size for the cache will be the minimum between the tile + * size from sources and this value. So, please keep it at 2048 or + * lower + **/ +#define MAX_BLOCK_SIZE 2048 + + +#define NO_VALID_RES "-1234.56" + +/** + * To move over the data return by queries + **/ +#define POSTGIS_RASTER_VERSION (GUInt16)0 +#define RASTER_HEADER_SIZE 61 +#define RASTER_BAND_HEADER_FIXED_SIZE 1 + +#define BAND_SIZE(nodatasize, datasize) \ + (RASTER_BAND_HEADER_FIXED_SIZE + (nodatasize) + (datasize)) + +#define GET_BAND_DATA(raster, nband, nodatasize, datasize) \ + ((raster) + RASTER_HEADER_SIZE + (nband) * BAND_SIZE(nodatasize, datasize) - (datasize)) + + +#define GEOTRSFRM_TOPLEFT_X 0 +#define GEOTRSFRM_WE_RES 1 +#define GEOTRSFRM_ROTATION_PARAM1 2 +#define GEOTRSFRM_TOPLEFT_Y 3 +#define GEOTRSFRM_ROTATION_PARAM2 4 +#define GEOTRSFRM_NS_RES 5 + +// Number of results return by ST_Metadata PostGIS function +#define ELEMENTS_OF_METADATA_RECORD 10 + +// Positions of elements of ST_Metadata PostGIS function +#define POS_UPPERLEFTX 0 +#define POS_UPPERLEFTY 1 +#define POS_WIDTH 2 +#define POS_HEIGHT 3 +#define POS_SCALEX 4 +#define POS_SCALEY 5 +#define POS_SKEWX 6 +#define POS_SKEWY 7 +#define POS_SRID 8 +#define POS_NBANDS 9 + +// Number of results return by ST_BandMetadata PostGIS function +#define ELEMENTS_OF_BAND_METADATA_RECORD 4 + +// Positions of elements of ST_BandMetadata PostGIS function +#define POS_PIXELTYPE 0 +#define POS_NODATAVALUE 1 +#define POS_ISOUTDB 2 +#define POS_PATH 3 + +typedef enum +{ + LOWEST_RESOLUTION, + HIGHEST_RESOLUTION, + AVERAGE_RESOLUTION, + USER_RESOLUTION, + AVERAGE_APPROX_RESOLUTION +} ResolutionStrategy; + + +/** + * The driver can work in these modes: + * - NO_MODE: Error case + * - ONE_RASTER_PER_ROW: Each row of the requested table is considered + * as a separated raster object. This is the default mode, if + * database and table name are provided, and no mode is specified. + * - ONE_RASTER_PER_TABLE: All the rows of the requested table are + * considered as tiles of a bigger raster coverage (the whole + * table). If database and table name are specified and mode = 2 + * is present in the connection string, this is the selected mode. + * - BROWSE_SCHEMA: If no table name is speficied, just database and + * schema names, the driver will yell of the schema's raster tables + * as possible datasets. + * - BROWSE_DATABASE: If no table name is specified, just database name, + * the driver will yell of the database's raster tables as possible + * datasets. + **/ +typedef enum +{ + NO_MODE, + ONE_RASTER_PER_ROW, + ONE_RASTER_PER_TABLE, + BROWSE_SCHEMA, + BROWSE_DATABASE +} WorkingMode; + +/** + * Important metadata of a PostGIS Raster band + **/ +typedef struct { + GDALDataType eDataType; + int nBitsDepth; + GBool bSignedByte; + GBool bHasNoDataValue; + GBool bIsOffline; + char * path; + double dfNoDataValue; +} BandMetadata; + +typedef struct { + char * pszSchema; + char * pszTable; + char * pszColumn; + int nFactor; +} PROverview; + + +// Some tools definitions +char * ReplaceQuotes(const char *, int); +char * ReplaceSingleQuotes(const char *, int); +char ** ParseConnectionString(const char *); +GBool TranslateDataType(const char *, GDALDataType *, int *, GBool *); + + +class PostGISRasterRasterBand; +class PostGISRasterTileDataset; + +/*********************************************************************** + * PostGISRasterDriver: extends GDALDriver to support PostGIS Raster + * connect. + **********************************************************************/ +class PostGISRasterDriver : public GDALDriver { + +private: + CPLMutex* hMutex; + std::map oMapConnection; +public: + PostGISRasterDriver(); + virtual ~PostGISRasterDriver(); + PGconn* GetConnection(const char* pszConnectionString, + const char * pszDbnameIn, const char * pszHostIn, const char * pszPortIn, const char * pszUserIn); +}; + + +/*********************************************************************** + * PostGISRasterDataset: extends VRTDataset to support PostGIS Raster + * datasets + **********************************************************************/ +class PostGISRasterDataset : public VRTDataset { + friend class PostGISRasterRasterBand; + friend class PostGISRasterTileRasterBand; +private: + char** papszSubdatasets; + double adfGeoTransform[6]; + int nSrid; + int nOverviewFactor; + int nBandsToCreate; + PGconn* poConn; + GBool bRegularBlocking; + GBool bAllTilesSnapToSameGrid; + GBool bCheckAllTiles; + char* pszSchema; + char* pszTable; + char* pszColumn; + char* pszWhere; + char * pszPrimaryKeyName; + GBool bIsFastPK; + int bHasTriedFetchingPrimaryKeyName; + char* pszProjection; + ResolutionStrategy resolutionStrategy; + WorkingMode nMode; + int nTiles; + double xmin, ymin, xmax, ymax; + PostGISRasterTileDataset ** papoSourcesHolders; + CPLQuadTree * hQuadTree; + + GBool bHasBuiltOverviews; + int nOverviewCount; + PostGISRasterDataset* poParentDS; + PostGISRasterDataset** papoOverviewDS; + + std::map oMapPKIDToRTDS; + + GBool bAssumeMultiBandReadPattern; + int nNextExpectedBand; + int nXOffPrev; + int nYOffPrev; + int nXSizePrev; + int nYSizePrev; + + GBool bHasTriedHasSpatialIndex; + GBool bHasSpatialIndex; + + GBool bBuildQuadTreeDynamically; + + GBool bTilesSameDimension; + int nTileWidth; + int nTileHeight; + + GBool ConstructOneDatasetFromTiles(PGresult *); + GBool YieldSubdatasets(PGresult *, const char *); + GBool SetRasterProperties(const char *); + GBool BrowseDatabase(const char *, const char *); + GBool AddComplexSource(PostGISRasterTileDataset* poRTDS); + GBool GetDstWin(PostGISRasterTileDataset *, int *, int *, int *, + int *); + BandMetadata * GetBandsMetadata(int *); + PROverview * GetOverviewTables(int *); + + PostGISRasterTileDataset* BuildRasterTileDataset(const char* pszMetadata, + const char* pszPKID, + int nBandsFetched, + BandMetadata * poBandMetaData); + void UpdateGlobalResolutionWithTileResolution(double tilePixelSizeX, + double tilePixelSizeY); + void BuildOverviews(); + void BuildBands(BandMetadata * poBandMetaData, int nBandsFetched); + + PostGISRasterTileDataset * GetMatchingSourceRef(const char * pszPKID) { return oMapPKIDToRTDS[pszPKID]; } + PostGISRasterTileDataset * GetMatchingSourceRef(double dfUpperLeftX, double dfUpperLeftY); + + protected: + virtual int CloseDependentDatasets(); + +public: + PostGISRasterDataset(); + virtual ~PostGISRasterDataset(); + static GDALDataset* Open(GDALOpenInfo *); + static int Identify(GDALOpenInfo *); + static GDALDataset* CreateCopy(const char *, GDALDataset *, + int, char **, GDALProgressFunc, void *); + static GBool InsertRaster(PGconn *, PostGISRasterDataset *, + const char *, const char *, const char *); + static CPLErr Delete(const char*); + virtual char **GetMetadataDomainList(); + char ** GetMetadata(const char *); + const char* GetProjectionRef(); + CPLErr SetProjection(const char*); + CPLErr SetGeoTransform(double *); + CPLErr GetGeoTransform(double *); + char **GetFileList(); + + int GetOverviewCount(); + PostGISRasterDataset* GetOverviewDS(int iOvr); + + const char * GetPrimaryKeyRef(); + GBool HasSpatialIndex(); + GBool LoadSources(int nXOff, int nYOff, int nXSize, int nYSize, int nBand); + GBool PolygonFromCoords(int nXOff, int nYOff, int nXEndOff, + int nYEndOff,double adfProjWin[8]); + void CacheTile(const char* pszMetadata, const char* pszRaster, const char *pszPKID, int nBand, int bAllBandCaching); +}; + +/*********************************************************************** + * PostGISRasterRasterBand: extends VRTSourcedRasterBand to support + * PostGIS Raster bands + **********************************************************************/ +class PostGISRasterTileRasterBand; + +class PostGISRasterRasterBand : public VRTSourcedRasterBand { + friend class PostGISRasterDataset; +protected: + GBool bIsOffline; + const char* pszSchema; + const char* pszTable; + const char* pszColumn; + +#ifdef notdef + GBool GetBandMetadata(GDALDataType *, GBool *, double *); + void NullBlock(void *); +#endif + + void NullBuffer(void* pData, + int nBufXSize, + int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, + int nLineSpace); +public: + + PostGISRasterRasterBand(PostGISRasterDataset *, int , + GDALDataType, GBool, double, GBool); + + virtual ~PostGISRasterRasterBand(); + + virtual double GetNoDataValue(int *pbSuccess = NULL); + virtual CPLErr SetNoDataValue(double); + virtual CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, + int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); +#ifdef notdef + virtual CPLErr IReadBlock(int, int, void *); +#endif + virtual int GetOverviewCount(); + virtual GDALRasterBand * GetOverview(int); + virtual GDALColorInterp GetColorInterpretation(); + + virtual double GetMinimum( int *pbSuccess ); + virtual double GetMaximum( int *pbSuccess ); + virtual CPLErr ComputeRasterMinMax( int bApproxOK, double* adfMinMax ); +}; + + + +/*********************************************************************** + * PostGISRasterTileDataset: it holds just a raster tile + **********************************************************************/ +class PostGISRasterTileRasterBand; + +class PostGISRasterTileDataset : public GDALDataset { + friend class PostGISRasterDataset; + friend class PostGISRasterRasterBand; + friend class PostGISRasterTileRasterBand; +private: + PostGISRasterDataset* poRDS; + char * pszPKID; + double adfGeoTransform[6]; + +public: + PostGISRasterTileDataset(PostGISRasterDataset* poRDS, + int nXSize, + int nYSize); + ~PostGISRasterTileDataset(); + CPLErr GetGeoTransform(double *); + void GetExtent(double* pdfMinX, double* pdfMinY, double* pdfMaxX, double* pdfMaxY); + const char* GetPKID() const { return pszPKID; } +}; + +/*********************************************************************** + * PostGISRasterTileRasterBand: it holds a raster tile band, that will + * be used as a source for PostGISRasterRasterBand + **********************************************************************/ +class PostGISRasterTileRasterBand : public GDALRasterBand { + friend class PostGISRasterRasterBand; + friend class PostGISRasterDataset; +private: + GBool bIsOffline; + + GBool IsCached(); + + VRTSource* poSource; + + +public: + PostGISRasterTileRasterBand( + PostGISRasterTileDataset * poRTDS, int nBand, + GDALDataType eDataType, + GBool bIsOffline = false); + ~PostGISRasterTileRasterBand(); + virtual CPLErr IReadBlock(int, int, void *); +}; + +#endif // POSTGISRASTER_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/postgisraster/postgisrasterdataset.cpp b/bazaar/plugin/gdal/frmts/postgisraster/postgisrasterdataset.cpp new file mode 100644 index 000000000..b1dec9c7e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/postgisraster/postgisrasterdataset.cpp @@ -0,0 +1,3689 @@ +/*********************************************************************** + * File : postgisrasterdataset.cpp + * Project: PostGIS Raster driver + * Purpose: GDAL Dataset implementation for PostGIS Raster driver + * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com + * jorgearevalo@libregis.org + * + * Author: David Zwarg, dzwarg@azavea.com + * + * Last changes: $Id: $ + * + *********************************************************************** + * Copyright (c) 2009 - 2013, Jorge Arevalo, David Zwarg + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + **********************************************************************/ +#include "postgisraster.h" +#include + +#ifdef _WIN32 +#define rint(x) floor((x) + 0.5) +#endif + +/* PostgreSQL defaults */ +#define DEFAULT_SCHEMA "public" +#define DEFAULT_COLUMN "rast" + + +CPL_C_START +void GDALRegister_PostGISRaster(void); +CPL_C_END + +/** Note on read performance on mode=2: + + There are different situations: + + 1) the table is registered in the raster_columns table and number of bands, minx,miny,maxx,maxy are available + a) no where clause, the table has a primary key and a GIST index on the raster column. + If the raster_columns advertize a scale_x and scale_y, use it. + Otherwise take the metadata of 10 rasters and compute and average scale_x, scale_y + With above information, we can build the dataset definition. + + (Following logic is implemented in LoadSources()) + + During a IRasterIO() query, + i) we will do a SQL query to retrieve the PKID of tiles that intersect the query window. + ii) If some tiles are not registered as sources, then do a SQL query to fetch their metadata + and instanciate them and register them. + iii) If some tiles are not cached, then determine if the query window is not too big (w.r.t. GDAL cache), + and if not, then do a SQL query to fetch their raster column. + + Note: if raster_columns show that all tiles have same dimensions, we can merge the query ii) and iii) + in the same one. + + b) otherwise, do a full scan of metadata to build the sources + + 2) otherwise, throw a warning to the user and do a full scan of metadata is needed to build the sources + + For 1b) and 2), during a IRasterIO() query, determine which sources are needed and not cached. + + (Following logic is implemented in IRasterIO()) + + If the query window is not too big, + If there's a primary key, then do a SQL query on the IDs of uncached sources to fetch their raster column + and cache them. + Otherwise if there's a GIST index, do a SQL spatial query on the window to fetch their raster column, + and cache them (identification with registered sources is done with the top-left coordinates) + Otherwise do a SQL query based on the range of top-left coordinates of tiles that intersect the query window. + + Conclusion: best performance is achieved with: no where clause, a primary key, a GIST index, known table extent + and, with moderate performance advantage : + - same scale_x, scale_y to save an initial SQL query, + - same blocksize_x, blocksize_y to save one SQL query per IRasterIO() +*/ + +/************************ + * \brief Constructor + ************************/ +PostGISRasterDataset::PostGISRasterDataset():VRTDataset(0, 0) { + + papszSubdatasets = NULL; + nSrid = -1; + nOverviewFactor = 1; + nBandsToCreate = 0; + poConn = NULL; + bRegularBlocking = false; + bAllTilesSnapToSameGrid = false; + + + bCheckAllTiles = CSLTestBoolean( + CPLGetConfigOption("PR_ALLOW_WHOLE_TABLE_SCAN", "YES")); + + pszSchema = NULL; + pszTable = NULL; + pszColumn = NULL; + pszWhere = NULL; + pszProjection = NULL; + + adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = 0.0; + adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0.0; + adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = 0.0; + adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0.0; + adfGeoTransform[GEOTRSFRM_WE_RES] = 0; + adfGeoTransform[GEOTRSFRM_NS_RES] = 0; + + adfGeoTransform[GEOTRSFRM_WE_RES] = + CPLAtof(CPLGetConfigOption("PR_WE_RES", NO_VALID_RES)); + + adfGeoTransform[GEOTRSFRM_NS_RES] = + CPLAtof(CPLGetConfigOption("PR_NS_RES", NO_VALID_RES)); + + // Default + resolutionStrategy = AVERAGE_APPROX_RESOLUTION; + + const char * pszTmp = NULL; + // We ignore this option if we provided the desired resolution + if (CPLIsEqual(adfGeoTransform[GEOTRSFRM_WE_RES], CPLAtof(NO_VALID_RES)) || + CPLIsEqual(adfGeoTransform[GEOTRSFRM_NS_RES], CPLAtof(NO_VALID_RES))) { + + // Resolution didn't have a valid value, so, we initiate it + adfGeoTransform[GEOTRSFRM_WE_RES] = 0.0; + adfGeoTransform[GEOTRSFRM_NS_RES] = 0.0; + + pszTmp = + CPLGetConfigOption("PR_RESOLUTION_STRATEGY", "AVERAGE_APPROX"); + + if (EQUAL(pszTmp, "LOWEST")) + resolutionStrategy = LOWEST_RESOLUTION; + + else if (EQUAL(pszTmp, "HIGHEST")) + resolutionStrategy = HIGHEST_RESOLUTION; + + else if (EQUAL(pszTmp, "USER")) + resolutionStrategy = USER_RESOLUTION; + + else if (EQUAL(pszTmp, "AVERAGE")) + resolutionStrategy = AVERAGE_RESOLUTION; + + } + + else { + resolutionStrategy = USER_RESOLUTION; + pszTmp = "USER"; + } + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", "PostGISRasterDataset::Constructor:" + "STRATEGY = %s", pszTmp); +#endif + + nTiles = 0; + nMode = NO_MODE; + poDriver = NULL; + + nRasterXSize = nRasterYSize = 0; + pszPrimaryKeyName = NULL; + bIsFastPK = false; + bHasTriedFetchingPrimaryKeyName = false; + + papoSourcesHolders = NULL; + hQuadTree = NULL; + + bHasBuiltOverviews = false; + nOverviewCount = 0; + papoOverviewDS = NULL; + poParentDS = NULL; + + bAssumeMultiBandReadPattern = true; + nNextExpectedBand = 1; + nXOffPrev = 0; + nYOffPrev = 0; + nXSizePrev = 0; + nYSizePrev = 0; + + bHasTriedHasSpatialIndex = false; + bHasSpatialIndex = false; + + bBuildQuadTreeDynamically = false; + + bTilesSameDimension = false; + nTileWidth = 0; + nTileHeight = 0; + + SetWritable(false); + + + /** + * TODO: Parametrize bAllTilesSnapToSameGrid. It controls if all the + * raster rows, in ONE_RASTER_PER_TABLE mode, must be checked to + * test if they snap to the same grid and have the same srid. It can + * be the user decission, if he/she's sure all the rows pass the + * test and want more speed. + **/ + +} + +/************************ + * \brief Constructor + ************************/ +PostGISRasterDataset::~PostGISRasterDataset() { + + if (pszSchema) { + CPLFree(pszSchema); + pszSchema = NULL; + } + + if (pszTable) { + CPLFree(pszTable); + pszTable = NULL; + } + + if (pszColumn) { + CPLFree(pszColumn); + pszColumn = NULL; + } + + if (pszWhere) { + CPLFree(pszWhere); + pszWhere = NULL; + } + + if (pszProjection) { + CPLFree(pszProjection); + pszProjection = NULL; + } + + if (pszPrimaryKeyName) { + CPLFree(pszPrimaryKeyName); + pszPrimaryKeyName = NULL; + } + + if (papszSubdatasets) { + CSLDestroy(papszSubdatasets); + papszSubdatasets = NULL; + } + + if (hQuadTree) { + CPLQuadTreeDestroy(hQuadTree); + hQuadTree = NULL; + } + + // Call it now so that the VRT sources + // are deleted and that there is no longer any code + // referencing the bands of the source holders. + // Otherwise this would go wrong because + // of the deleting the source holders just below. + CloseDependentDatasets(); + + if (papoSourcesHolders) { + int i; + for(i = 0; i < nTiles; i++) { + if (papoSourcesHolders[i]) + delete papoSourcesHolders[i]; + + } + + VSIFree(papoSourcesHolders); + papoSourcesHolders = NULL; + } +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int PostGISRasterDataset::CloseDependentDatasets() +{ + int bHasDroppedRef = VRTDataset::CloseDependentDatasets(); + if( nOverviewCount > 0 ) + { + int i; + for(i = 0; i < nOverviewCount; i++) + { + delete papoOverviewDS[i]; + } + CPLFree(papoOverviewDS); + papoOverviewDS = NULL; + nOverviewCount = 0; + bHasDroppedRef = TRUE; + } + + return bHasDroppedRef; +} + +/************************************************************************/ +/* HasSpatialIndex() */ +/************************************************************************/ + +GBool PostGISRasterDataset::HasSpatialIndex() +{ + CPLString osCommand; + PGresult* poResult = NULL; + + // If exists, return it + if (bHasTriedHasSpatialIndex) { + return bHasSpatialIndex; + } + + bHasTriedHasSpatialIndex = true; + + /* For debugging purposes only */ + if( CSLTestBoolean(CPLGetConfigOption("PR_DISABLE_GIST", "FALSE") ) ) + return false; + + // Copyright dustymugs !!! + osCommand.Printf( + "SELECT n.nspname AS schema_name, c2.relname AS table_name, att.attname AS column_name, " + " c.relname AS index_name, am.amname AS index_type " + "FROM pg_catalog.pg_class c " + "JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid " + "JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid " + "JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " + "JOIN pg_am am ON c.relam = am.oid " + "JOIN pg_attribute att ON att.attrelid = c2.oid " + "AND pg_catalog.format_type(att.atttypid, att.atttypmod) = 'raster' " + "WHERE c.relkind IN ('i') " + "AND am.amname = 'gist' " + "AND strpos(split_part(pg_catalog.pg_get_indexdef(i.indexrelid, 0, true), ' gist ', 2), att.attname) > 0 " + "AND n.nspname = '%s' " + "AND c2.relname = '%s' " + "AND att.attname = '%s' ", pszSchema, pszTable, pszColumn); + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::HasSpatialIndex(): Query: %s", + osCommand.c_str()); +#endif + + poResult = PQexec(poConn, osCommand.c_str()); + + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) <= 0 ) + { + bHasSpatialIndex = false; + CPLDebug("PostGIS_Raster", "For better performance, creating a spatial index " + "with 'CREATE INDEX %s_%s_%s_gist_idx ON %s.%s USING GIST (ST_ConvexHull(%s))' is advised", + pszSchema, pszTable, pszColumn, pszSchema, pszTable, pszColumn); + } + else + { + bHasSpatialIndex = true; + } + + if( poResult ) + PQclear(poResult); + return bHasSpatialIndex; +} + +/*********************************************************************** + * \brief Look for a primary key in the table selected by the user + * + * If the table does not have a primary key, it returns NULL + **********************************************************************/ +const char * PostGISRasterDataset::GetPrimaryKeyRef() +{ + CPLString osCommand; + PGresult* poResult = NULL; + + // If exists, return it + if (bHasTriedFetchingPrimaryKeyName) { + return pszPrimaryKeyName; + } + + bHasTriedFetchingPrimaryKeyName = true; + + /* For debugging purposes only */ + if( CSLTestBoolean(CPLGetConfigOption("PR_DISABLE_PK", "FALSE") ) ) + return NULL; + + /* Determine the primary key/unique column on the table */ + osCommand.Printf("select d.attname from pg_catalog.pg_constraint " + "as a join pg_catalog.pg_indexes as b on a.conname = " + "b.indexname join pg_catalog.pg_class as c on c.relname = " + "b.tablename join pg_catalog.pg_attribute as d on " + "c.relfilenode = d.attrelid where b.schemaname = '%s' and " + "b.tablename = '%s' and d.attnum = a.conkey[1] and a.contype " + "in ('p', 'u')", pszSchema, pszTable); + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::GetPrimaryKeyRef(): Query: %s", + osCommand.c_str()); +#endif + + poResult = PQexec(poConn, osCommand.c_str()); + + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) <= 0 ) { + + PQclear(poResult); + + /** + * Maybe there is no primary key or unique constraint; a + * sequence will also suffice; get the first one + **/ + + osCommand.Printf("select cols.column_name from " + "information_schema.columns as cols join " + "information_schema.sequences as seqs on " + "cols.column_default like '%%'||seqs.sequence_name||'%%' " + "where cols.table_schema = '%s' and cols.table_name = '%s'", + pszSchema, pszTable); + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::GetPrimaryKeyRef(): Query: %s", + osCommand.c_str()); +#endif + + poResult = PQexec(poConn, osCommand.c_str()); + + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) <= 0) { + + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::GetPrimaryKeyRef(): Could not " + "find a primary key or unique column on the specified " + "table %s.%s. For better performance, creating a primary key on the table is advised.", + pszSchema, pszTable); + + pszPrimaryKeyName = NULL; // Just in case + + } + + else { + pszPrimaryKeyName = CPLStrdup(PQgetvalue(poResult, 0, 0)); + } + + } + + // Ok, get the primary key + else { + pszPrimaryKeyName = CPLStrdup(PQgetvalue(poResult, 0, 0)); + bIsFastPK = true; + } + + PQclear(poResult); + + return pszPrimaryKeyName; +} + + +/*********************************************************************** + * \brief Look for raster tables in database and store them as + * subdatasets + * + * If no table is provided in connection string, the driver looks for + * the existent raster tables in the schema given as argument. This + * argument, however, is optional. If a NULL value is provided, the + * driver looks for all raster tables in all schemas of the + * user-provided database. + * + * NOTE: Permissions are managed by libpq. The driver only returns an + * error if an error is returned when trying to access to tables not + * allowed to the current user. + **********************************************************************/ +GBool PostGISRasterDataset::BrowseDatabase(const char* pszCurrentSchema, + const char* pszValidConnectionString) { + + /* Be careful! These 3 vars override the class ones! */ + char* pszSchema = NULL; + char* pszTable = NULL; + char* pszColumn = NULL; + + int i = 0; + int nTuples = 0; + PGresult * poResult = NULL; + CPLString osCommand; + + + /************************************************************* + * Fetch all the raster tables and store them as subdatasets + *************************************************************/ + if (pszCurrentSchema == NULL) { + osCommand.Printf("select pg_namespace.nspname as schema, " + "pg_class.relname as table, pg_attribute.attname as column " + "from pg_class, pg_namespace,pg_attribute, pg_type where " + "pg_class.relnamespace = pg_namespace.oid and " + "pg_class.oid = pg_attribute.attrelid and " + "pg_attribute.atttypid = pg_type.oid and " + "pg_type.typname = 'raster'"); + + poResult = PQexec(poConn, osCommand.c_str()); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) <= 0) { + + ReportError(CE_Failure, CPLE_AppDefined, + "Error browsing database for PostGIS Raster tables: %s", + PQerrorMessage(poConn)); + if (poResult != NULL) + PQclear(poResult); + + return false; + } + + + nTuples = PQntuples(poResult); + for (i = 0; i < nTuples; i++) { + pszSchema = PQgetvalue(poResult, i, 0); + pszTable = PQgetvalue(poResult, i, 1); + pszColumn = PQgetvalue(poResult, i, 2); + + papszSubdatasets = CSLSetNameValue(papszSubdatasets, + CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)), + CPLSPrintf("PG:%s schema=%s table=%s column=%s", + pszValidConnectionString, pszSchema, pszTable, + pszColumn)); + + papszSubdatasets = CSLSetNameValue(papszSubdatasets, + CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)), + CPLSPrintf("PostGIS Raster table at %s.%s (%s)", + pszSchema, pszTable, pszColumn)); + } + + PQclear(poResult); + + } + /*************************************************************** + * Fetch all the schema's raster tables and store them as + * subdatasets + **************************************************************/ + else { + osCommand.Printf("select pg_class.relname as table, " + "pg_attribute.attname as column from pg_class, " + "pg_namespace,pg_attribute, pg_type where " + "pg_class.relnamespace = pg_namespace.oid and " + "pg_class.oid = pg_attribute.attrelid and " + "pg_attribute.atttypid = pg_type.oid and " + "pg_type.typname = 'raster' and " + "pg_namespace.nspname = '%s'", pszCurrentSchema); + + poResult = PQexec(poConn, osCommand.c_str()); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) <= 0) { + + ReportError(CE_Failure, CPLE_AppDefined, + "Error browsing database for PostGIS Raster tables: %s", + PQerrorMessage(poConn)); + + if (poResult != NULL) + PQclear(poResult); + + return false; + } + + + nTuples = PQntuples(poResult); + for (i = 0; i < nTuples; i++) { + pszTable = PQgetvalue(poResult, i, 0); + pszColumn = PQgetvalue(poResult, i, 1); + + papszSubdatasets = CSLSetNameValue(papszSubdatasets, + CPLSPrintf("SUBDATASET_%d_NAME", (i + 1)), + CPLSPrintf("PG:%s schema=%s table=%s column=%s", + pszValidConnectionString, pszCurrentSchema, pszTable, + pszColumn)); + + papszSubdatasets = CSLSetNameValue(papszSubdatasets, + CPLSPrintf("SUBDATASET_%d_DESC", (i + 1)), + CPLSPrintf("PostGIS Raster table at %s.%s (%s)", + pszCurrentSchema, pszTable, pszColumn)); + } + + PQclear(poResult); + } + + return true; +} + + +/*********************************************************************** + * \brief Look for overview tables for the bands of the current dataset + **********************************************************************/ +PROverview * PostGISRasterDataset::GetOverviewTables(int * pnOverviews) +{ + PROverview * poOV = NULL; + CPLString osCommand; + PGresult * poResult = NULL; + + osCommand.Printf("SELECT o_table_name, overview_factor, " + "o_raster_column, o_table_schema FROM raster_overviews " + "WHERE r_table_schema = '%s' AND r_table_name = '%s' AND " + "r_raster_column = '%s' ORDER BY overview_factor", this->pszSchema, this->pszTable, + this->pszColumn); + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::GetOverviewTables(): Query: %s", + osCommand.c_str()); +#endif + + poResult = PQexec(poConn, osCommand.c_str()); + + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) < 0) { + + ReportError(CE_Failure, CPLE_AppDefined, + "Error looking for overview tables: %s", + PQerrorMessage(poConn)); + + if (poResult) + PQclear(poResult); + + return NULL; + } + + else if (PQntuples(poResult) == 0) { + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::GetOverviewTables(): No overviews " + "for table %s.%s", pszTable, pszSchema); + + if (poResult) + PQclear(poResult); + + return NULL; + } + + int nTuples = PQntuples(poResult); + + poOV = (PROverview *)VSIMalloc2(nTuples, sizeof(PROverview)); + if (poOV == NULL) { + ReportError(CE_Failure, CPLE_AppDefined, + "Error looking for overview tables"); + + PQclear(poResult); + + return NULL; + } + + int iOVerview = 0; + for(iOVerview = 0; iOVerview < nTuples; iOVerview++) { + poOV[iOVerview].pszSchema = + CPLStrdup(PQgetvalue(poResult, iOVerview, 3)); + + poOV[iOVerview].pszTable = + CPLStrdup(PQgetvalue(poResult, iOVerview, 0)); + + poOV[iOVerview].pszColumn = + CPLStrdup(PQgetvalue(poResult, iOVerview, 2)); + + poOV[iOVerview].nFactor = + atoi(PQgetvalue(poResult, iOVerview, 1)); + + } + + if (pnOverviews) + *pnOverviews = nTuples; + + PQclear(poResult); + + return poOV; +} + +/*********************************************************************** + * \brief Build overview datasets + ***********************************************************************/ +void PostGISRasterDataset::BuildOverviews() +{ + if( bHasBuiltOverviews || poParentDS != NULL ) + return; + + bHasBuiltOverviews = true; + /******************************************************************* + * We also get the names of the overview tables, if they exist. So, + * we'll can use them to create the overview datasets. + ******************************************************************/ + int nOV = 0; + PROverview * poOV = GetOverviewTables(&nOV); + + if (poOV) + { + papoOverviewDS = (PostGISRasterDataset**) CPLCalloc(nOV, sizeof(PostGISRasterDataset*)); + nOverviewCount = 0; + + int iOV; + for(iOV = 0; iOV < nOV; iOV++) + { + PostGISRasterDataset* poOvrDS = new PostGISRasterDataset(); + poOvrDS->nOverviewFactor = poOV[iOV].nFactor; + poOvrDS->poConn = poConn; + poOvrDS->eAccess = eAccess; + poOvrDS->nMode = nMode; + poOvrDS->pszSchema = poOV[iOV].pszSchema; // takes ownership + poOvrDS->pszTable = poOV[iOV].pszTable; // takes ownership + poOvrDS->pszColumn = poOV[iOV].pszColumn; // takes ownership + poOvrDS->pszWhere = pszWhere ? CPLStrdup(pszWhere) : NULL; + poOvrDS->poParentDS = this; + + if (!CSLTestBoolean(CPLGetConfigOption("PG_DIFFERED_OVERVIEWS", "YES")) && + (!poOvrDS->SetRasterProperties(NULL) || + poOvrDS->GetRasterCount() != GetRasterCount())) + { + delete poOvrDS; + } + else + { + papoOverviewDS[nOverviewCount ++] = poOvrDS; + } + } + + VSIFree(poOV); + } +} + +/*********************************************************************** + * \brief Returns overview count + ***********************************************************************/ +int PostGISRasterDataset::GetOverviewCount() +{ + BuildOverviews(); + return nOverviewCount; +} + +/*********************************************************************** + * \brief Returns overview dataset + ***********************************************************************/ +PostGISRasterDataset* PostGISRasterDataset::GetOverviewDS(int iOvr) +{ + if( iOvr < 0 || iOvr > GetOverviewCount() ) + return NULL; + return papoOverviewDS[iOvr]; +} + +/*********************************************************************** + * \brief Calculates the destination window for a VRT source, taking + * into account that the source is a PostGIS Raster tile and the + * destination is the general dataset itself + * + * This method is adapted from gdalbuildvrt as is in GDAL 1.10.0 +***********************************************************************/ +GBool PostGISRasterDataset::GetDstWin( + PostGISRasterTileDataset * psDP, + int* pnDstXOff, int* pnDstYOff, int* pnDstXSize, int* pnDstYSize) +{ + double we_res = this->adfGeoTransform[GEOTRSFRM_WE_RES]; + double ns_res = this->adfGeoTransform[GEOTRSFRM_NS_RES]; + + double adfTileGeoTransform[6]; + psDP->GetGeoTransform(adfTileGeoTransform); + + *pnDstXOff = (int) + (0.5 + (adfTileGeoTransform[GEOTRSFRM_TOPLEFT_X] - xmin) / we_res); + + if( ns_res < 0 ) + *pnDstYOff = (int) + (0.5 + (ymax - adfTileGeoTransform[GEOTRSFRM_TOPLEFT_Y]) / -ns_res); + else + *pnDstYOff = (int) + (0.5 + (adfTileGeoTransform[GEOTRSFRM_TOPLEFT_Y] - ymin) / ns_res); + + *pnDstXSize = (int) + (0.5 + psDP->GetRasterXSize() * + adfTileGeoTransform[GEOTRSFRM_WE_RES] / we_res); + *pnDstYSize = (int) + (0.5 + psDP->GetRasterYSize() * + adfTileGeoTransform[GEOTRSFRM_NS_RES] / ns_res); + + return true; +} + +/*********************************************************************** + * \brief Add tiles bands as complex source for raster bands. + **********************************************************************/ +GBool PostGISRasterDataset::AddComplexSource(PostGISRasterTileDataset* poRTDS) +{ + // Parameters to add the tile bands as sources + int nDstXOff = 0; + int nDstYOff = 0; + int nDstXSize = 0; + int nDstYSize = 0; + + // Get src and dst parameters + GBool bValidTile = GetDstWin(poRTDS, + &nDstXOff, &nDstYOff, &nDstXSize, &nDstYSize); + if (!bValidTile) + return false; + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::AddComplexSource: " + "Tile bounding box from (%d, %d) of size (%d, %d) will " + "cover raster bounding box from (%d, %d) of size " + "(%d, %d)", 0, 0, + poRTDS->GetRasterXSize(), + poRTDS->GetRasterYSize(), + nDstXOff, nDstYOff, nDstXSize, nDstYSize); +#endif + + // Add tiles bands as sources for the raster bands + for(int iBand = 0; iBand < nBandsToCreate; iBand++) + { + PostGISRasterRasterBand * prb = + (PostGISRasterRasterBand *)GetRasterBand(iBand + 1); + + int bHasNoData = FALSE; + double dfBandNoDataValue = prb->GetNoDataValue(&bHasNoData); + + PostGISRasterTileRasterBand * prtb = + (PostGISRasterTileRasterBand *) + poRTDS->GetRasterBand(iBand + 1); + + prb->AddComplexSource(prtb, 0, 0, + poRTDS->GetRasterXSize(), + poRTDS->GetRasterYSize(), + nDstXOff, nDstYOff, nDstXSize, nDstYSize, + 0.0, 1.0, + (bHasNoData) ? dfBandNoDataValue : VRT_NODATA_UNSET); + + prtb->poSource = prb->papoSources[prb->nSources-1]; + } + + return true; +} + +/************************************************************************/ +/* GetMatchingSourceRef() */ +/************************************************************************/ + +/** + * \brief Get the tile dataset that matches the given upper left pixel + **/ +PostGISRasterTileDataset * + PostGISRasterDataset::GetMatchingSourceRef(double dfUpperLeftX, + double dfUpperLeftY) +{ + int i; + PostGISRasterTileDataset * poRTDS = NULL; + + for(i = 0; i < nTiles; i++) + { + poRTDS = papoSourcesHolders[i]; + + if (CPLIsEqual(poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_X], + dfUpperLeftX) && + CPLIsEqual(poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y], + dfUpperLeftY)) { + + return poRTDS; + } + } + + return NULL; +} + +/************************************************************************/ +/* CacheTile() */ +/************************************************************************/ + +void PostGISRasterDataset::CacheTile(const char* pszMetadata, + const char* pszRaster, + const char *pszPKID, + int nBand, + int bAllBandCaching) +{ + /** + * Get metadata record and unpack it + **/ + char* pszRes = CPLStrdup(pszMetadata); + + // Skip first "(" + char* pszFilteredRes = pszRes + 1; + + // Skip last ")" + pszFilteredRes[strlen(pszFilteredRes)-1] = '\0'; + + // Tokenize + char** papszParams = + CSLTokenizeString2(pszFilteredRes, ",", + CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS); + CPLAssert(CSLCount(papszParams) >= ELEMENTS_OF_METADATA_RECORD); + + CPLFree(pszRes); + + double dfTilePixelSizeX = CPLAtof(papszParams[POS_UPPERLEFTX]); + double dfTilePixelSizeY = CPLAtof(papszParams[POS_UPPERLEFTY]); + int nTileXSize = atoi(papszParams[POS_WIDTH]); + int nTileYSize = atoi(papszParams[POS_HEIGHT]); + + CSLDestroy(papszParams); + papszParams = NULL; + + /** + * Get actual raster band data + **/ + int nBandDataTypeSize = GDALGetDataTypeSize(GetRasterBand(nBand)->GetRasterDataType()) / 8; + int nExpectedBandDataSize = + nTileXSize * nTileYSize * nBandDataTypeSize; + + int nWKBLength = 0; + GByte* pbyData = CPLHexToBinary(pszRaster, &nWKBLength); + + int nExpectedBands = bAllBandCaching ? GetRasterCount() : 1; + int nExpectedWKBLength = RASTER_HEADER_SIZE + BAND_SIZE(nBandDataTypeSize, nExpectedBandDataSize) * nExpectedBands; + if( nWKBLength != nExpectedWKBLength ) + { + CPLDebug("PostGIS_Raster", "nWKBLength=%d, nExpectedWKBLength=%d", nWKBLength, nExpectedWKBLength ); + CPLFree(pbyData); + return; + } + + // Do byte-swapping if necessary */ + int bIsLittleEndian = (pbyData[0] == 1); +#ifdef CPL_LSB + int bSwap = !bIsLittleEndian; +#else + int bSwap = bIsLittleEndian; +#endif + + PostGISRasterTileDataset* poRTDS = NULL; + if( GetPrimaryKeyRef() != NULL ) + poRTDS = GetMatchingSourceRef(pszPKID); + else + poRTDS = GetMatchingSourceRef(dfTilePixelSizeX, + dfTilePixelSizeY); + if( poRTDS == NULL ) + { + CPLFree(pbyData); + return; + } + + for(int k=1; k <= nExpectedBands; k++) + { + GByte* pbyDataToRead = (GByte*)GET_BAND_DATA(pbyData, k, + nBandDataTypeSize, nExpectedBandDataSize); + + if( bSwap && nBandDataTypeSize > 1 ) + { + GDALSwapWords( pbyDataToRead, nBandDataTypeSize, + nTileXSize * nTileYSize, + nBandDataTypeSize ); + } + + /** + * Get the right PostGISRasterRasterBand + **/ + int nCurBand = ( nExpectedBands > 1 ) ? k : nBand; + + /** + * Get the right tileband + **/ + GDALRasterBand * poRTB = poRTDS->GetRasterBand(nCurBand); + + /** + * Manually add each tile data to the cache of the + * matching PostGISRasterTileRasterBand. + **/ + if (poRTB) + { + GDALRasterBlock* poBlock = poRTB->GetLockedBlockRef(0, 0, TRUE); + if( poBlock != NULL ) + { + // Point block data ref to fetched data + memcpy(poBlock->GetDataRef(), (void *)pbyDataToRead, + nExpectedBandDataSize); + + poBlock->DropLock(); + } + } + } + + CPLFree(pbyData); +} + +/************************************************************************/ +/* LoadSources() */ +/************************************************************************/ + +GBool PostGISRasterDataset::LoadSources(int nXOff, int nYOff, int nXSize, int nYSize, int nBand) +{ + if( !bBuildQuadTreeDynamically ) + return false; + + CPLString osCommand; + CPLString osSpatialFilter; + CPLString osIDsToFetch; + PGresult * poResult; + + int bFetchAll = FALSE; + if( nXOff == 0 && nYOff == 0 && nXSize == nRasterXSize && nYSize == nRasterYSize ) + { + bFetchAll = TRUE; + } + else + { + double adfProjWin[8]; + PolygonFromCoords(nXOff, nYOff, nXOff + nXSize, nYOff + nYSize, adfProjWin); + osSpatialFilter.Printf("%s && " + "ST_GeomFromText('POLYGON((%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f))') ", + //"AND ST_Intersects(%s, ST_GeomFromEWKT('SRID=%d;POLYGON((%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f))'))", + pszColumn, + adfProjWin[0], adfProjWin[1], + adfProjWin[2], adfProjWin[3], + adfProjWin[4], adfProjWin[5], + adfProjWin[6], adfProjWin[7], + adfProjWin[0], adfProjWin[1] + /*,pszColumn, nSrid, + adfProjWin[0], adfProjWin[1], + adfProjWin[2], adfProjWin[3], + adfProjWin[4], adfProjWin[5], + adfProjWin[6], adfProjWin[7], + adfProjWin[0], adfProjWin[1]*/); + } + + int bLoadRasters = FALSE; + int bAllBandCaching = FALSE; + + if( nTiles > 0 && !bFetchAll ) + { + osCommand.Printf("SELECT %s FROM %s.%s", + pszPrimaryKeyName, pszSchema, pszTable); + osCommand += " WHERE "; + osCommand += osSpatialFilter; + + osSpatialFilter = ""; + + poResult = PQexec(poConn, osCommand.c_str()); + + #ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::LoadSources(): Query = \"%s\" --> number of rows = %d", + osCommand.c_str(), poResult ? PQntuples(poResult) : 0 ); + #endif + + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) < 0) { + + if (poResult) + PQclear(poResult); + + CPLError(CE_Failure, CPLE_AppDefined, + "PostGISRasterDataset::LoadSources(): %s", + PQerrorMessage(poConn)); + + return false; + } + + if( bTilesSameDimension && nBand > 0 ) + { + GIntBig nMemoryRequiredForTiles = PQntuples(poResult) * nTileWidth * nTileHeight * + GDALGetDataTypeSize(GetRasterBand(nBand)->GetRasterDataType()) / 8; + GIntBig nCacheMax = (GIntBig) GDALGetCacheMax64(); + if( nBands * nMemoryRequiredForTiles <= nCacheMax ) + { + bLoadRasters = TRUE; + bAllBandCaching = TRUE; + } + else if( nMemoryRequiredForTiles <= nCacheMax ) + { + bLoadRasters = TRUE; + } + } + + int i; + for(i=0;iGetRasterBand(nBand); + if( !poTileBand->IsCached() ) + bFetchTile = TRUE; + } + if( bFetchTile ) + { + if( osIDsToFetch.size() != 0 ) + osIDsToFetch += ","; + osIDsToFetch += "'"; + osIDsToFetch += pszPKID; + osIDsToFetch += "'"; + } + } + + PQclear(poResult); + } + + if( bFetchAll || osIDsToFetch.size() != 0 || osSpatialFilter.size() != 0 ) + { + osCommand.Printf("SELECT %s, ST_Metadata(%s)", + pszPrimaryKeyName, pszColumn); + if( bLoadRasters ) + { + if( bAllBandCaching ) + osCommand += CPLSPrintf(", %s", pszColumn); + else + osCommand += CPLSPrintf(", ST_Band(%s, %d)", pszColumn, nBand); + } + osCommand += CPLSPrintf(" FROM %s.%s", + pszSchema, pszTable); + if( osIDsToFetch.size() != 0 ) + { + osCommand += " WHERE "; + osCommand += pszPrimaryKeyName; + osCommand += " IN ("; + osCommand += osIDsToFetch; + osCommand += ")"; + } + else if ( osSpatialFilter.size() != 0 ) + { + osCommand += " WHERE "; + osCommand += osSpatialFilter; + } + + poResult = PQexec(poConn, osCommand.c_str()); + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::LoadSources(): Query = \"%s\" --> number of rows = %d", + osCommand.c_str(), poResult ? PQntuples(poResult) : 0 ); +#endif + + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) < 0) { + + if (poResult) + PQclear(poResult); + + CPLError(CE_Failure, CPLE_AppDefined, + "PostGISRasterDataset::LoadSources(): %s", + PQerrorMessage(poConn)); + + return false; + } + + for(int i=0;ipszPKID] = poRTDS; + papoSourcesHolders = (PostGISRasterTileDataset**) + CPLRealloc(papoSourcesHolders, sizeof(PostGISRasterTileDataset*) * (nTiles + 1)); + papoSourcesHolders[nTiles ++] = poRTDS; + CPLQuadTreeInsert(hQuadTree, poRTDS); + } + else + { + delete poRTDS; + poRTDS = NULL; + } + } + } + + if( bLoadRasters && poRTDS != NULL ) + { + const char* pszRaster = PQgetvalue(poResult, i, 2); + CacheTile(pszMetadata, pszRaster, pszPKID, nBand, bAllBandCaching); + } + } + + PQclear(poResult); + } + + // If we have fetched the surface of all the dataset, then all sources have + // been built, and we don't need to do a spatial query on following IRasterIO() calls + if( bFetchAll ) + bBuildQuadTreeDynamically = false; + + return true; +} + +/*********************************************************************** + * \brief Get some useful metadata for all bands + * + * The allocated memory is responsibility of the caller + **********************************************************************/ +BandMetadata * PostGISRasterDataset::GetBandsMetadata(int * pnBands) +{ + BandMetadata * poBMD = NULL; + PGresult * poResult = NULL; + CPLString osCommand; + char * pszRes = NULL; + char * pszFilteredRes = NULL; + char ** papszParams = NULL; + + if (pszWhere == NULL) { + osCommand.Printf("select st_bandmetadata(%s, band) from " + "(select %s, generate_series(1, st_numbands(%s)) band from " + "(select %s from %s.%s limit 1) bar) foo", pszColumn, pszColumn, + pszColumn, pszColumn, pszSchema, pszTable); + } + + else { + osCommand.Printf("select st_bandmetadata(%s, band) from " + "(select %s, generate_series(1, st_numbands(%s)) band from " + "(select %s from %s.%s where %s limit 1) bar) foo", pszColumn, + pszColumn, pszColumn, pszColumn, pszSchema, pszTable, pszWhere); + } + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::GetBandsMetadata(): Query: %s", + osCommand.c_str()); +#endif + + poResult = PQexec(poConn, osCommand.c_str()); + /* Error getting info from database */ + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) <= 0) { + + ReportError(CE_Failure, CPLE_AppDefined, + "Error getting band metadata while creating raster " + "bands"); + + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::GetBandsMetadata(): %s", + PQerrorMessage(poConn)); + + if (poResult) + PQclear(poResult); + + return NULL; + } + + // Matches nBands + int nTuples = PQntuples(poResult); + + poBMD = (BandMetadata *)VSIMalloc2(nTuples, sizeof(BandMetadata)); + if (poBMD == NULL) { + ReportError(CE_Failure, CPLE_OutOfMemory, + "Out of memory getting metadata from bands"); + + PQclear(poResult); + + return NULL; + } + + + int iBand = 0; + + for(iBand = 0; iBand < nTuples; iBand++) { + + // Get metadata record + pszRes = CPLStrdup(PQgetvalue(poResult, iBand, 0)); + + // Skip first "(" + pszFilteredRes = pszRes + 1; + + // Skip last ")" + pszFilteredRes[strlen(pszFilteredRes)-1] = '\0'; + + // Tokenize + papszParams = + CSLTokenizeString2(pszFilteredRes, ",", CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS); + CPLAssert(CSLCount(papszParams) >= ELEMENTS_OF_BAND_METADATA_RECORD); + + CPLFree(pszRes); + + // If the band doesn't have nodata, NULL is returned as nodata + TranslateDataType(papszParams[POS_PIXELTYPE], + &(poBMD[iBand].eDataType), &(poBMD[iBand].nBitsDepth), + &(poBMD[iBand].bSignedByte)); + + if (papszParams[POS_NODATAVALUE] == NULL || + EQUALN(papszParams[POS_NODATAVALUE], "NULL", 4*sizeof(char)) || + EQUALN(papszParams[POS_NODATAVALUE], "f", sizeof(char)) || + EQUAL(papszParams[POS_NODATAVALUE], "")) { + + poBMD[iBand].bHasNoDataValue = false; + poBMD[iBand].dfNoDataValue = CPLAtof(NO_VALID_RES); + } + + else { + poBMD[iBand].bHasNoDataValue = true; + poBMD[iBand].dfNoDataValue = + CPLAtof(papszParams[POS_NODATAVALUE]); + } + + // TODO: Manage outdb and get path + poBMD[iBand].bIsOffline = (papszParams[POS_ISOUTDB] != NULL) ? + EQUALN(papszParams[POS_ISOUTDB], "t", sizeof(char)) : + false; + + CSLDestroy(papszParams); + } + + if (pnBands) + *pnBands = nTuples; + + PQclear(poResult); + + return poBMD; + +} + +/*********************************************************************** + * \brief Function to get the bounding box of each element inserted in + * the QuadTree index + **********************************************************************/ +static void GetTileBoundingBox(const void *hFeature, + CPLRectObj * pBounds) +{ + PostGISRasterTileDataset * poRTD = + (PostGISRasterTileDataset *)hFeature; + + double adfTileGeoTransform[6]; + poRTD->GetGeoTransform(adfTileGeoTransform); + + int nTileWidth = poRTD->GetRasterXSize(); + int nTileHeight = poRTD->GetRasterYSize(); + + pBounds->minx = adfTileGeoTransform[GEOTRSFRM_TOPLEFT_X]; + pBounds->maxx = adfTileGeoTransform[GEOTRSFRM_TOPLEFT_X] + + nTileWidth * + adfTileGeoTransform[GEOTRSFRM_WE_RES]; + + if (adfTileGeoTransform[GEOTRSFRM_NS_RES] >= 0.0) { + pBounds->miny = adfTileGeoTransform[GEOTRSFRM_TOPLEFT_Y]; + pBounds->maxy = adfTileGeoTransform[GEOTRSFRM_TOPLEFT_Y] + + nTileHeight * + adfTileGeoTransform[GEOTRSFRM_NS_RES]; + } + else { + pBounds->maxy = adfTileGeoTransform[GEOTRSFRM_TOPLEFT_Y]; + pBounds->miny = adfTileGeoTransform[GEOTRSFRM_TOPLEFT_Y] + + nTileHeight * + adfTileGeoTransform[GEOTRSFRM_NS_RES]; + } + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", "TileBoundingBox minx=%f miny=%f maxx=%f maxy=%f adfTileGeoTransform[GEOTRSFRM_NS_RES]=%f", + pBounds->minx, pBounds->miny, pBounds->maxx, pBounds->maxy, adfTileGeoTransform[GEOTRSFRM_NS_RES]); +#endif + + return; +} + +/******************************************************** + * \brief Builds a PostGISRasterTileDataset* object from the ST_Metadata + ********************************************************/ +PostGISRasterTileDataset* PostGISRasterDataset::BuildRasterTileDataset(const char* pszMetadata, + const char* pszPKID, + int nBandsFetched, + BandMetadata * poBandMetaData) +{ + // Get metadata record + char* pszRes = CPLStrdup(pszMetadata); + + // Skip first "(" + char* pszFilteredRes = pszRes + 1; + + // Skip last ")" + pszFilteredRes[strlen(pszFilteredRes)-1] = '\0'; + + // Tokenize + char** papszParams = + CSLTokenizeString2(pszFilteredRes, ",", + CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS); + CPLAssert(CSLCount(papszParams) >= ELEMENTS_OF_METADATA_RECORD); + + CPLFree(pszRes); + + double tileSkewX = CPLAtof(papszParams[POS_SKEWX]); + double tileSkewY = CPLAtof(papszParams[POS_SKEWY]); + + // Rotated rasters are not allowed, so far + // TODO: allow them + if (!CPLIsEqual(tileSkewX, 0.0) || + !CPLIsEqual(tileSkewY, 0.0)) { + + ReportError(CE_Failure, CPLE_AppDefined, + "GDAL PostGIS Raster driver can not work with " + "rotated rasters yet."); + + return NULL; + } + + int nTileWidth = atoi(papszParams[POS_WIDTH]); + int nTileHeight = atoi(papszParams[POS_HEIGHT]); + + /** + * Now, construct a PostGISRasterTileDataset, and add + * its bands as sources for the general raster bands + **/ + int nTileBands = atoi(papszParams[POS_NBANDS]); + + /** + * If the source doesn't have the same number of bands than + * the raster band, is discarded + **/ + if (nTileBands != nBandsFetched) { + CPLDebug("PostGIS_Raster", "PostGISRasterDataset::" + "BuildRasterTileDataset(): Tile has %d " + "bands, and the raster has %d bands. Discarding " + "this tile", nTileBands, nBandsFetched); + + CSLDestroy(papszParams); + + return NULL; + } + + PostGISRasterTileDataset* poRTDS = + new PostGISRasterTileDataset(this, nTileWidth, nTileHeight); + + if (GetPrimaryKeyRef() != NULL) + { + poRTDS->pszPKID = CPLStrdup(pszPKID); + } + + poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_X]= + CPLAtof(papszParams[POS_UPPERLEFTX]); + + poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]= + CPLAtof(papszParams[POS_UPPERLEFTY]); + + poRTDS->adfGeoTransform[GEOTRSFRM_WE_RES]= + CPLAtof(papszParams[POS_SCALEX]); + + poRTDS->adfGeoTransform[GEOTRSFRM_NS_RES]= + CPLAtof(papszParams[POS_SCALEY]); + + // TODO: outdb bands should be handled. Not a priority. + for(int j = 0; j < nTileBands; j++) { + + // Create band + poRTDS->SetBand(j + 1, + new PostGISRasterTileRasterBand( + poRTDS, + j + 1, (poBandMetaData ) ? poBandMetaData[j].eDataType : GetRasterBand(j+1)->GetRasterDataType(), + (poBandMetaData ) ? poBandMetaData[j].bIsOffline : FALSE)); + } + + CSLDestroy(papszParams); + + return poRTDS; +} + + +/******************************************************** + * \brief Updates components GEOTRSFRM_WE_RES and GEOTRSFRM_NS_RES + * of dataset adfGeoTransform + ********************************************************/ +void PostGISRasterDataset::UpdateGlobalResolutionWithTileResolution( + double tilePixelSizeX, double tilePixelSizeY) +{ + // Calculate pixel size + if (resolutionStrategy == AVERAGE_RESOLUTION || + resolutionStrategy == AVERAGE_APPROX_RESOLUTION) { + adfGeoTransform[GEOTRSFRM_WE_RES] += tilePixelSizeX; + adfGeoTransform[GEOTRSFRM_NS_RES] += tilePixelSizeY; + } + + else if (resolutionStrategy == HIGHEST_RESOLUTION) { + adfGeoTransform[GEOTRSFRM_WE_RES] = + MIN(adfGeoTransform[GEOTRSFRM_WE_RES], + tilePixelSizeX); + + /** + * Yes : as ns_res is negative, the highest resolution + * is the max value. + * + * Negative tilePixelSizeY means that the coords origin + * is in top left corner. This is not the common + * situation. Most image files store data from top to + * bottom, while the projected coordinate systems + * utilize traditional Cartesian coordinates with the + * origin in the conventional lower-left corner (bottom + * to top). For that reason, this parameter is normally + * negative. + **/ + if (tilePixelSizeY < 0.0) + adfGeoTransform[GEOTRSFRM_NS_RES] = + MAX(adfGeoTransform[GEOTRSFRM_NS_RES], + tilePixelSizeY); + else + adfGeoTransform[GEOTRSFRM_NS_RES] = + MIN(adfGeoTransform[GEOTRSFRM_NS_RES], + tilePixelSizeY); + } + + else if (resolutionStrategy == LOWEST_RESOLUTION) { + adfGeoTransform[GEOTRSFRM_WE_RES] = + MAX(adfGeoTransform[GEOTRSFRM_WE_RES], + tilePixelSizeX); + + if (tilePixelSizeY < 0.0) + adfGeoTransform[GEOTRSFRM_NS_RES] = + MIN(adfGeoTransform[GEOTRSFRM_NS_RES], + tilePixelSizeY); + else + adfGeoTransform[GEOTRSFRM_NS_RES] = + MAX(adfGeoTransform[GEOTRSFRM_NS_RES], + tilePixelSizeY); + } + +} + +/*********************************************************************** + * \brief Build bands + ***********************************************************************/ +void PostGISRasterDataset::BuildBands(BandMetadata * poBandMetaData, + int nBandsFetched) +{ +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::ConstructOneDatasetFromTiles: " + "Now constructing the raster dataset bands"); +#endif + + int iBand; + for (iBand = 0; iBand < nBandsFetched; iBand++) { + + SetBand(iBand + 1, new PostGISRasterRasterBand(this, iBand + 1, + poBandMetaData[iBand].eDataType, + poBandMetaData[iBand].bHasNoDataValue, + poBandMetaData[iBand].dfNoDataValue, + poBandMetaData[iBand].bIsOffline)); + + // Set some band metadata items + GDALRasterBand * b = GetRasterBand(iBand + 1); + + if (poBandMetaData[iBand].bSignedByte) { + b->SetMetadataItem( + "PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" ); + } + + if (poBandMetaData[iBand].nBitsDepth < 8) { + b->SetMetadataItem( + "NBITS", CPLString().Printf( "%d", + poBandMetaData[iBand].nBitsDepth ), + "IMAGE_STRUCTURE" ); + } + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::ConstructOneDatasetFromTiles: " + "Band %d built", iBand + 1); +#endif + } +} + +/*********************************************************************** + * \brief Construct just one dataset from all the results fetched. + * + * This method is not very elegant. It's strongly attached to + * SetRasterProperties (it assumes poResult is not NULL, and the actual + * results are stored at fixed positions). I just did it to avoid a + * huge SetRasterProperties method. + * + * I know, this could be avoided in a better way. Like implementing a + * wrapper to raise queries and get results without all the checking + * overhead. I'd like to do it, someday... + **********************************************************************/ +GBool PostGISRasterDataset::ConstructOneDatasetFromTiles( + PGresult * poResult) +{ + + /******************************************************************* + * We first get the band metadata. So we'll can use it as metadata + * for all the sources. + * + * We just fetch the band metadata from 1 tile. So, we assume that: + * - All the bands have the same data type + * - All the bands have the same NODATA value + * + * It's user's resposibility to ensure the requested table fit in + * this schema. He/she may use the 'where' clause to ensure this + ******************************************************************/ + int nBandsFetched = 0; + BandMetadata * poBandMetaData = GetBandsMetadata(&nBandsFetched); + + + /******************************************************************* + * Now, we can iterate over the input query's results (metadata + * from all the database tiles). + * + * In this iteration, we will construct the dataset GeoTransform + * array and we will add each tile's band as source for each of our + * rasterbands. + ******************************************************************/ + int nTiles = PQntuples(poResult); + + adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = xmin; + + int nField = (GetPrimaryKeyRef() != NULL) ? 1 : 0; + /** + * Optimization - Just one tile: The dataset's geotransform is the + * tile's geotransform. And we don't need to construct sources for + * the raster bands. We just read from the unique tile we have. So, + * we avoid all the VRT stuff. + * + * TODO: For some reason, the implementation of IRasterIO in + * PostGISRasterRasterBand class causes a segmentation fault when + * tries to call GDALRasterBand::IRasterIO. This call intends to + * delegate the I/O in the parent class, that will call + * PostGISRasterRasterBand::IReadBlock. + * + * If you avoid PostGISRasterRasterBand::IRasterIO method, the + * IReadBlock method is directly call and it works fine. + * + * Right now, we avoid this optimization by making the next boolean + * variable always true. + **/ + //GBool bNeedToConstructSourcesHolders = (nTiles > 1); + + // As the optimization is not working, we avoid it + GBool bNeedToConstructSourcesHolders = true; + +#ifdef notdef + // This won't be called if the optimization is disabled + if (!bNeedToConstructSourcesHolders) + { + // Get metadata record + char* pszRes = CPLStrdup(PQgetvalue(poResult, 0, nField)); + + // Skip first "(" + char* pszFilteredRes = pszRes + 1; + + // Skip last ")" + pszFilteredRes[strlen(pszFilteredRes)-1] = '\0'; + + // Tokenize + char** papszParams = + CSLTokenizeString2(pszFilteredRes, ",", CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS); + CPLAssert(CSLCount(papszParams) >= ELEMENTS_OF_METADATA_RECORD); + + CPLFree(pszRes); + + adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = + CPLAtof(papszParams[POS_SKEWX]); + + adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = + CPLAtof(papszParams[POS_SKEWY]); + + // Rotated rasters are not allowed, so far + // TODO: allow them + if (!CPLIsEqual(adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1], 0.0) || + !CPLIsEqual(adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2], 0.0)) { + + ReportError(CE_Failure, CPLE_AppDefined, + "GDAL PostGIS Raster driver can not work with " + "rotated rasters yet."); + + VSIFree(poBandMetaData); + CSLDestroy(papszParams); + + return false; + } + + /** + * We override user resolution. It only makes sense in case we + * have several tiles with different resolutions + **/ + adfGeoTransform[GEOTRSFRM_WE_RES] = + CPLAtof(papszParams[POS_SCALEX]); + adfGeoTransform[GEOTRSFRM_NS_RES] = + CPLAtof(papszParams[POS_SCALEY]); + + CSLDestroy(papszParams); + + } + + /** + * Several tiles: construct the dataset from metadata of all tiles, + * and create PostGISRasterTileDataset objects, to hold the + * PostGISRasterTileRasterBands objects that will be used as sources + **/ + else +#endif + { + int i; + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::ConstructOneDatasetFromTiles: " + "Constructing one dataset from %d tiles", nTiles); +#endif + + papoSourcesHolders = (PostGISRasterTileDataset **) + VSIMalloc2(nTiles, sizeof(PostGISRasterTileDataset *)); + + if (papoSourcesHolders == NULL) { + ReportError(CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating space for dataset bands " + "sources"); + + VSIFree(poBandMetaData); + + return false; + } + + int nValidTiles = 0; + for(i = 0; i < nTiles; i++) + { + PostGISRasterTileDataset* poRTDS = + BuildRasterTileDataset(PQgetvalue(poResult, i, nField), + (GetPrimaryKeyRef() != NULL) ? PQgetvalue(poResult, i, 0) : NULL, + nBandsFetched, + poBandMetaData); + if( poRTDS == NULL ) + continue; + + if( nOverviewFactor == 1 && resolutionStrategy != USER_RESOLUTION ) + { + double tilePixelSizeX = poRTDS->adfGeoTransform[GEOTRSFRM_WE_RES]; + double tilePixelSizeY = poRTDS->adfGeoTransform[GEOTRSFRM_NS_RES]; + + if( nValidTiles == 0 ) + { + adfGeoTransform[GEOTRSFRM_WE_RES] = tilePixelSizeX; + adfGeoTransform[GEOTRSFRM_NS_RES] = tilePixelSizeY; + } + else + { + UpdateGlobalResolutionWithTileResolution(tilePixelSizeX, + tilePixelSizeY); + } + } + + papoSourcesHolders[nValidTiles++] = poRTDS; + + } // end for + + nTiles = nValidTiles; + + if( nOverviewFactor > 1 ) + { + adfGeoTransform[GEOTRSFRM_WE_RES] = poParentDS->adfGeoTransform[GEOTRSFRM_WE_RES] * nOverviewFactor; + adfGeoTransform[GEOTRSFRM_NS_RES] = poParentDS->adfGeoTransform[GEOTRSFRM_NS_RES] * nOverviewFactor; + } + else if ((resolutionStrategy == AVERAGE_RESOLUTION || + resolutionStrategy == AVERAGE_APPROX_RESOLUTION) && nTiles > 0) { + adfGeoTransform[GEOTRSFRM_WE_RES] /= nTiles; + adfGeoTransform[GEOTRSFRM_NS_RES] /= nTiles; + } + + } // end else + + /** + * Complete the rest of geotransform parameters + **/ + if (adfGeoTransform[GEOTRSFRM_NS_RES] >= 0.0) + adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = ymin; + else + adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = ymax; + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::ConstructOneDatasetFromTiles: " + "GeoTransform array = (%f, %f, %f, %f, %f, %f)", + adfGeoTransform[GEOTRSFRM_TOPLEFT_X], + adfGeoTransform[GEOTRSFRM_WE_RES], + adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1], + adfGeoTransform[GEOTRSFRM_TOPLEFT_Y], + adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2], + adfGeoTransform[GEOTRSFRM_NS_RES]); +#endif + + // Calculate the raster size from the geotransform array + nRasterXSize = (int) + fabs(rint((xmax - xmin) / + adfGeoTransform[GEOTRSFRM_WE_RES])); + + nRasterYSize = (int) + fabs(rint((ymax - ymin) / + adfGeoTransform[GEOTRSFRM_NS_RES])); + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::ConstructOneDatasetFromTiles: " + "Raster size: (%d, %d), ",nRasterXSize, nRasterYSize); +#endif + + if (nRasterXSize <= 0 || nRasterYSize <= 0) { + ReportError(CE_Failure, CPLE_AppDefined, + "Computed PostGIS Raster dimension is invalid. You've " + "probably specified unappropriate resolution."); + + return false; + } + + /******************************************************************* + * Now construct the dataset bands + ******************************************************************/ + BuildBands(poBandMetaData, nBandsFetched); + + // And free bandmetadata + VSIFree(poBandMetaData); + + + /******************************************************************* + * Finally, add complex sources and create a quadtree index for them + ******************************************************************/ + if (bNeedToConstructSourcesHolders) { +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::ConstructOneDatasetFromTiles: " + "Finally, adding sources for bands"); +#endif + for(int iSource = 0; iSource < nTiles; iSource++) + { + PostGISRasterTileDataset* poRTDS =papoSourcesHolders[iSource]; + if (!AddComplexSource(poRTDS)) + { + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::ConstructOneDatasetFromTiles:" + "Bounding box of tile %d does not intersect the " + "bounding box of dataset ", iSource); + + continue; + } + if( poRTDS->pszPKID != NULL ) + oMapPKIDToRTDS[poRTDS->pszPKID] = poRTDS; + CPLQuadTreeInsert(hQuadTree, poRTDS); + } + } + + return true; +} + + + +/*********************************************************************** + * \brief Construct subdatasets and show them. + * + * This method is not very elegant. It's strongly attached to + * SetRasterProperties (it assumes poResult is not NULL, and the actual + * results are stored at fixed positions). I just did it to avoid a + * huge SetRasterProperties method. + * + * I know, this could be avoided in a better way. Like implementing a + * wrapper to raise queries and get results without all the checking + * overhead. I'd like to do it, someday... + **********************************************************************/ +GBool PostGISRasterDataset::YieldSubdatasets(PGresult * poResult, +const char * pszValidConnectionString) +{ + int nTiles = PQntuples(poResult); + int i = 0; + double dfTileUpperLeftX = 0; + double dfTileUpperLeftY = 0; + + papszSubdatasets = (char**)VSICalloc(2 * nTiles + 1, sizeof(char*)); + if( papszSubdatasets == NULL ) + return false; + + // Subdatasets identified by primary key + if (GetPrimaryKeyRef() != NULL) { + + for(i = 0; i < nTiles; i++) { + + const char* pszId = PQgetvalue(poResult, i, 0); + + papszSubdatasets[2 * i] = + CPLStrdup(CPLSPrintf("SUBDATASET_%d_NAME=PG:%s schema=%s table=%s column=%s " + "where='%s = %s'", i+ 1, pszValidConnectionString, + pszSchema, pszTable, pszColumn, pszPrimaryKeyName, + pszId)); + + papszSubdatasets[2 * i + 1] = + CPLStrdup(CPLSPrintf("SUBDATASET_%d_DESC=PostGIS Raster at %s.%s (%s), with %s = %s", + i + 1, pszSchema, pszTable, pszColumn, pszPrimaryKeyName, + pszId)); + } + } + + // Subdatasets identified by upper left pixel + else { + + char * pszRes; + char * pszFilteredRes; + char ** papszParams; + + for(i = 0; i < nTiles; i++) { + + pszRes = CPLStrdup(PQgetvalue(poResult, i, 0)); + + // Skip first "(" + pszFilteredRes = pszRes + 1; + + // Skip last ")" + pszFilteredRes[strlen(pszFilteredRes)-1] = '\0'; + + // Tokenize + papszParams = + CSLTokenizeString2(pszFilteredRes, ",", + CSLT_HONOURSTRINGS); + + CPLFree(pszRes); + + dfTileUpperLeftX = CPLAtof(papszParams[POS_UPPERLEFTX]); + dfTileUpperLeftY = CPLAtof(papszParams[POS_UPPERLEFTY]); + + papszSubdatasets[2 * i] = + CPLStrdup(CPLSPrintf("SUBDATASET_%d_NAME=PG:%s schema=%s table=%s column=%s " + "where='abs(ST_UpperLeftX(%s) - %.8f) < 1e-8 AND " + "abs(ST_UpperLeftY(%s) - %.8f) < 1e-8'", + i + 1, pszValidConnectionString, pszSchema, pszTable, + pszColumn, pszColumn, dfTileUpperLeftX, pszColumn, + dfTileUpperLeftY)); + + papszSubdatasets[2 * i + 1] = + CPLStrdup(CPLSPrintf("SUBDATASET_%d_DESC=PostGIS Raster at %s.%s (%s), " + "UpperLeft = %.8f, %.8f", i + 1, pszSchema, pszTable, pszColumn, + dfTileUpperLeftX, dfTileUpperLeftY)); + + CSLDestroy(papszParams); + } + } + + /** + * Not a single raster fetched. Not really needed. Just to keep code clean + **/ + nRasterXSize = 0; + nRasterYSize = 0; + adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = 0.0; + adfGeoTransform[GEOTRSFRM_WE_RES] = 1.0; + adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0.0; + adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = 0.0; + adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0.0; + adfGeoTransform[GEOTRSFRM_NS_RES] = -1.0; + + return true; +} + + + +/*********************************************************************** + * \brief Set the general raster properties. + * + * This method is called when the driver working mode is + * ONE_RASTER_PER_ROW or ONE_RASTER_PER_TABLE. + * + * We must distinguish between tiled and untiled raster coverages. In + * PostGIS Raster, there's no real difference between 'tile' and + * 'raster'. There's only 'raster objects'. Each record of a raster + * table is a raster object, and has its own georeference information, + * whether if the record is a tile of a bigger raster coverage or is a + * complete raster. So, there's no a way of knowing if the rows of a + * raster table are related or not. It's user's responsibility, and + * it's managed by 'mode' parameter in connection string, which + * determines the driver working mode. + * + * The user is responsible to ensure that the raster layer meets the + * minimum topological requirements for analysis. The ideal case is when + * all the raster tiles of a continuous layer are the same size, snap to + * the same grid and do not overlap. + * + **********************************************************************/ +GBool PostGISRasterDataset::SetRasterProperties + (const char * pszValidConnectionString) +{ + PGresult* poResult = NULL; + CPLString osCommand; + GBool bDataFoundInRasterColumns = false; + GBool bNeedToCheckWholeTable = false; + + /******************************************************************* + * Get the extent and the maximum number of bands of the requested + * raster- + * + * TODO: The extent of rotated rasters could be a problem. We'll + * need a ST_RotatedExtent function in PostGIS. Without that + * function, we shouldn't allow rotated rasters + ******************************************************************/ + if (pszWhere != NULL) { + osCommand.Printf( + "select srid, nbband, ST_XMin(geom) as xmin, " + "ST_XMax(geom) as xmax, ST_YMin(geom) as ymin, " + "ST_YMax(geom) as ymax, scale_x, scale_y from (select ST_SRID(%s) srid, " + "ST_Extent(%s::geometry) geom, max(ST_NumBands(%s)) " + "nbband, avg(ST_ScaleX(%s)) scale_x, avg(ST_ScaleY(%s)) scale_y from %s.%s where %s group by ST_SRID(%s)) foo", + pszColumn, pszColumn, pszColumn, pszColumn, pszColumn, + pszSchema, pszTable, pszWhere, + pszColumn); + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::SetRasterProperties(): First query: %s", + osCommand.c_str()); +#endif + + poResult = PQexec(poConn, osCommand.c_str()); + } + + else { + + /** + * Optimization: First, check raster_columns view (it makes + * things faster. See ticket #5046) + * + * This can only be applied if we don't have a 'where' clause, + * because raster_columns view stores statistics about the whole + * table. If the user specified 'where' clause is because is + * just interested in a subset of the table rows. + **/ + osCommand.Printf( + "select srid, nbband, ST_XMin(geom) as xmin, ST_XMax(geom) " + "as xmax, ST_YMin(geom) as ymin, ST_YMax(geom) as ymax, " + "scale_x, scale_y, blocksize_x, blocksize_y, same_alignment, regular_blocking " + "from (select srid, extent geom, num_bands nbband, " + "scale_x, scale_y, blocksize_x, blocksize_y, same_alignment, regular_blocking from " + "raster_columns where r_table_schema = '%s' and " + "r_table_name = '%s' and r_raster_column = '%s' ) foo", + pszSchema, pszTable, pszColumn); + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::SetRasterProperties(): First query: %s", + osCommand.c_str()); +#endif + + poResult = PQexec(poConn, osCommand.c_str()); + + // Query execution error + if(poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) < 0) { + + bNeedToCheckWholeTable = true; + + if (poResult) + PQclear(poResult); + + } + + /** + * We didn't find anything in raster_columns view. Need to check + * the whole table for metadata + **/ + else if (PQntuples(poResult) == 0) { + + ReportError(CE_Warning, CPLE_AppDefined, "Cannot find " + "information about %s.%s table in raster_columns view. The " + "raster table load would take a lot of time. Please, " + "execute AddRasterConstraints PostGIS function to register " + "this table as raster table in raster_columns view. This " + "will save a lot of time.", pszSchema, pszTable); + + PQclear(poResult); + + bNeedToCheckWholeTable = true; + } + + /* There's a result but the row has empty values */ + else if (PQntuples(poResult) == 1 && + (PQgetvalue(poResult, 0, 1)[0] == '\0' || + (poParentDS == NULL && + (PQgetvalue(poResult, 0, 2)[0] == '\0' || + PQgetvalue(poResult, 0, 3)[0] == '\0' || + PQgetvalue(poResult, 0, 4)[0] == '\0' || + PQgetvalue(poResult, 0, 5)[0] == '\0'))) ) { + ReportError(CE_Warning, CPLE_AppDefined, "Cannot find (valid) " + "information about %s.%s table in raster_columns view. The " + "raster table load would take a lot of time. Please, " + "execute AddRasterConstraints PostGIS function to register " + "this table as raster table in raster_columns view. This " + "will save a lot of time.", pszSchema, pszTable); + + PQclear(poResult); + + bNeedToCheckWholeTable = true; + } + + + // We should check whole table but we can't + if (bNeedToCheckWholeTable && !bCheckAllTiles) { + ReportError(CE_Failure, CPLE_AppDefined, "Cannot find " + "information about %s.%s table in raster_columns view. " + "Please, execute AddRasterConstraints PostGIS function to " + "register this table as raster table in raster_columns " + "view. This will save a lot of time. As alternative, " + "provide configuration option " + "PR_ALLOW_WHOLE_TABLE_SCAN=YES. With this option, the " + "driver will work even without the table information " + "stored in raster_columns view, but it could perform " + "really slow.", pszSchema, pszTable); + + PQclear(poResult); + + return false; + + } + + + // We should check the whole table and we can + else if (bNeedToCheckWholeTable) { + osCommand.Printf( + "select srid, nbband, st_xmin(geom) as xmin, " + "st_xmax(geom) as xmax, st_ymin(geom) as ymin, " + "st_ymax(geom) as ymax, scale_x, scale_y from (select st_srid(%s) srid, " + "st_extent(%s::geometry) geom, max(ST_NumBands(%s)) " + "nbband, avg(ST_ScaleX(%s)) scale_x, avg(ST_ScaleY(%s)) scale_y from %s.%s group by st_srid(%s)) foo", + pszColumn, pszColumn, pszColumn, pszColumn, pszColumn, pszSchema, pszTable, pszColumn); + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::SetRasterProperties(): " + "First query: %s", osCommand.c_str()); +#endif + + poResult = PQexec(poConn, osCommand.c_str()); + } + + + // We already found the data in raster_columns + else { + bDataFoundInRasterColumns = true; + } + } + + // Query execution error + if(poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) < 0) { + + ReportError(CE_Failure, CPLE_AppDefined, + "Error browsing database for PostGIS Raster " + "properties : %s", PQerrorMessage(poConn)); + + if (poResult != NULL) + PQclear(poResult); + + return false; + + } + + else if (PQntuples(poResult) == 0) { + ReportError(CE_Failure, CPLE_AppDefined, + "No results found in %s.%s. Did you specify a 'where' " + "clause too restrictive?", pszSchema, pszTable); + + PQclear(poResult); + + return false; + } + + + /** + * Found more than one SRID value in the table. Not allowed. + * + * TODO: We could provide an extra parameter, to transform all the + * tiles to the same SRID + **/ + else if (PQntuples(poResult) > 1) { + ReportError(CE_Failure, CPLE_AppDefined, + "Error, the table %s.%s contains tiles with different " + "srid. This feature is not yet supported by the PostGIS " + "Raster driver. Please, specify a table that contains only " + "tiles with the same srid or provide a 'where' constraint " + "to select just the tiles with the same value for srid", + pszSchema, pszTable); + + PQclear(poResult); + + return false; + } + + // Get some information we will probably need further + nSrid = atoi(PQgetvalue(poResult, 0, 0)); + nBandsToCreate = atoi(PQgetvalue(poResult, 0, 1)); + if( poParentDS != NULL ) + { + /* If we are an overview of a parent dataset, we need to adjust */ + /* to its extent, otherwise IRasterIO() will not work properly */ + xmin = poParentDS->xmin; + xmax = poParentDS->xmax; + ymin = poParentDS->ymin; + ymax = poParentDS->ymax; + } + else + { + xmin = CPLAtof(PQgetvalue(poResult, 0, 2)); + xmax = CPLAtof(PQgetvalue(poResult, 0, 3)); + ymin = CPLAtof(PQgetvalue(poResult, 0, 4)); + ymax = CPLAtof(PQgetvalue(poResult, 0, 5)); + } + + // Create the QuadTree object + CPLRectObj sRect; + sRect.minx = xmin; + sRect.miny = ymin; + sRect.maxx = xmax; + sRect.maxy = ymax; + hQuadTree = CPLQuadTreeCreate(&sRect, GetTileBoundingBox); + + double scale_x = CPLAtof(PQgetvalue(poResult, 0, 6)); + double scale_y = CPLAtof(PQgetvalue(poResult, 0, 7)); + if( nOverviewFactor > 1 ) + { + scale_x = poParentDS->adfGeoTransform[GEOTRSFRM_WE_RES] * nOverviewFactor; + scale_y = poParentDS->adfGeoTransform[GEOTRSFRM_NS_RES] * nOverviewFactor; + } + else if( resolutionStrategy == USER_RESOLUTION) + { + scale_x = adfGeoTransform[GEOTRSFRM_WE_RES]; + scale_y = adfGeoTransform[GEOTRSFRM_NS_RES]; + } + + // These fields can only be fetched from raster_columns view + if (bDataFoundInRasterColumns) + { + nTileWidth = atoi(PQgetvalue(poResult, 0, 8)); + nTileHeight = atoi(PQgetvalue(poResult, 0, 9)); + if( nTileWidth != 0 && nTileHeight != 0 ) + bTilesSameDimension = true; + + bAllTilesSnapToSameGrid = + EQUALN(PQgetvalue(poResult, 0, 10), "t", sizeof(char)); + + bRegularBlocking = + EQUALN(PQgetvalue(poResult, 0, 11), "t", sizeof(char)); + } + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::SetRasterProperties: xmin = %f, " + "xmax = %f, ymin = %f, ymax = %f, scale_x = %f, scale_y = %f", + xmin, xmax, ymin, ymax, scale_x, scale_y); +#endif + + PQclear(poResult); + + /******************************************************************* + * Now, we fetch the metadata of all the raster tiles in the + * database, that will allow us to construct VRT sources to the + * PostGIS Raster bands. + * + * TODO: Improve this. If we have a big amount of tiles, it can be + * a problem. + ******************************************************************/ + // We'll identify each tile for its primary key/unique id (ideal) + if (GetPrimaryKeyRef() != NULL) + { + if (pszWhere == NULL) + { + /* If we don't know the pixel size, then guess it from averaging the metadata */ + /* of a maximum 10 rasters */ + if( bIsFastPK && nMode == ONE_RASTER_PER_TABLE && HasSpatialIndex() && + (scale_x == 0 || scale_y == 0) && + resolutionStrategy == AVERAGE_APPROX_RESOLUTION ) + { + osCommand.Printf("SELECT avg(scale_x) avg_scale_x, avg(scale_y) avg_scale_y FROM " + "(SELECT ST_ScaleX(%s) scale_x, ST_ScaleY(%s) scale_y FROM %s.%s LIMIT 10) foo", + pszColumn, pszColumn, pszSchema, pszTable); + #ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::SetRasterProperties(): Query: %s", + osCommand.c_str()); + #endif + + poResult = PQexec(poConn, osCommand.c_str()); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) <= 0) { + + ReportError(CE_Failure, CPLE_AppDefined, + "Error retrieving raster metadata"); + + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::SetRasterProperties(): %s", + PQerrorMessage(poConn)); + + if (poResult != NULL) + PQclear(poResult); + + return false; + } + + scale_x = CPLAtof(PQgetvalue(poResult, 0, 0)); + scale_y = CPLAtof(PQgetvalue(poResult, 0, 1)); + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::SetRasterProperties: guessed: scale_x = %f, scale_y = %f", + scale_x, scale_y); + + PQclear(poResult); + } + + /* If we build a raster for the whole table, than we have a spatial index, + and a primary key, and we know the pixel size, we can build the dataset + immediately, and IRasterIO() queries will be able to retrieve quickly + the PKID of the tiles to fetch, so we don't need to scan the whole table */ + if( bIsFastPK && nMode == ONE_RASTER_PER_TABLE && HasSpatialIndex() && + scale_x != 0 && scale_y != 0 ) + { + adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = xmin; + adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0.0; + adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = (scale_y < 0) ? ymax : ymin; + adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0.0; + adfGeoTransform[GEOTRSFRM_WE_RES] = scale_x; + adfGeoTransform[GEOTRSFRM_NS_RES] = scale_y; + + // Calculate the raster size from the geotransform array + nRasterXSize = (int) + fabs(rint((xmax - xmin) / + adfGeoTransform[GEOTRSFRM_WE_RES])); + + nRasterYSize = (int) + fabs(rint((ymax - ymin) / + adfGeoTransform[GEOTRSFRM_NS_RES])); + + #ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::ConstructOneDatasetFromTiles: " + "Raster size: (%d, %d), ",nRasterXSize, nRasterYSize); + #endif + + if (nRasterXSize <= 0 || nRasterYSize <= 0) { + ReportError(CE_Failure, CPLE_AppDefined, + "Computed PostGIS Raster dimension is invalid. You've " + "probably specified unappropriate resolution."); + + return false; + } + + bBuildQuadTreeDynamically = true; + + /******************************************************************* + * Now construct the dataset bands + ******************************************************************/ + int nBandsFetched = 0; + BandMetadata * poBandMetaData = GetBandsMetadata(&nBandsFetched); + + BuildBands(poBandMetaData, nBandsFetched); + + // And free bandmetadata + VSIFree(poBandMetaData); + + return true; + } + + osCommand.Printf("select %s, st_metadata(%s) from %s.%s", + pszPrimaryKeyName, pszColumn, pszSchema, pszTable); + + // srid shouldn't be necessary. It was previously checked + } + + else { + osCommand.Printf("select %s, st_metadata(%s) from %s.%s " + "where %s", pszPrimaryKeyName, pszColumn, pszSchema, + pszTable, pszWhere); + } + } + + // No primary key/unique id found. We rely on upper left pixel + else { + if (pszWhere == NULL) { + osCommand.Printf("select st_metadata(%s) from %s.%s", + pszColumn, pszSchema, pszTable); + } + + else { + osCommand.Printf("select st_metadata(%s) from %s.%s " + "where %s", pszColumn, pszSchema, pszTable, pszWhere); + } + } + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::SetRasterProperties(): Query: %s", + osCommand.c_str()); +#endif + + poResult = PQexec(poConn, osCommand.c_str()); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) <= 0) { + + ReportError(CE_Failure, CPLE_AppDefined, + "Error retrieving raster metadata"); + + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::SetRasterProperties(): %s", + PQerrorMessage(poConn)); + + if (poResult != NULL) + PQclear(poResult); + + return false; + } + + // Now we know the number of tiles that form our dataset + nTiles = PQntuples(poResult); + + + /******************************************************************* + * We are going to create a whole dataset as a mosaic with all the + * tiles. We'll consider each tile as a VRT source for + * PostGISRasterRasterBand. The data will be actually read by each + * of these sources, and it will be cached in the sources' caches, + * not in the PostGISRasterRasterBand cache + ******************************************************************/ + if (nTiles == 1 || nMode == ONE_RASTER_PER_TABLE) + { +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::SetRasterProperties(): " + "Constructing one dataset from %d tiles", nTiles); +#endif + + GBool res = ConstructOneDatasetFromTiles(poResult); + + PQclear(poResult); + + return res; + } + + + /*************************************************************** + * One raster per row: collect subdatasets + **************************************************************/ + else if (nMode == ONE_RASTER_PER_ROW) { +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::SetRasterProperties(): " + "Reporting %d datasets", nTiles); +#endif + + GBool res = YieldSubdatasets(poResult, + pszValidConnectionString); + + PQclear(poResult); + + return res; + } + + /*************************************************************** + * Wrong mode: error + **************************************************************/ + else { + ReportError(CE_Failure, CPLE_AppDefined, + "Wrong driver working mode. You must specify mode = 1 or " + "mode = 2 in the connection string. Check PostGIS Raster " + "documentation at " + "http://trac.osgeo.org/gdal/wiki/frmts_wtkraster.html " + "for further information about working modes."); + + PQclear(poResult); + + return false; + } +} + + +/*********************************************************************** + * \brief Get the connection information for a filename. + * + * This method extracts these dataset parameters from the connection + * string, if present: + * - pszSchema: The schema where the table belongs + * - pszTable: The table's name + * - pszColumn: The raster column's name + * - pszWhere: A where constraint to apply to the table's rows + * - pszHost: The PostgreSQL host + * - pszPort: The PostgreSQL port + * - pszUser: The PostgreSQL user + * - pszPassword: The PostgreSQL password + * - nMode: The connection mode + * + * If any of there parameters is not present in the connection string, + * default values are taken. nMode is deducted from the rest of + * parameters if not provided. + * + * Apart from that, bBrowseDatabase is set to TRUE if the mode is + * BROWSE_SCHEMA or BROWSE_DATABASE + **********************************************************************/ +static GBool +GetConnectionInfo(const char * pszFilename, + char ** ppszConnectionString, char ** ppszDbname, char ** ppszSchema, char ** ppszTable, + char ** ppszColumn, char ** ppszWhere, char ** ppszHost, + char ** ppszPort, char ** ppszUser, char ** ppszPassword, + WorkingMode * nMode, GBool * bBrowseDatabase) +{ + int nPos = -1, i; + char * pszTmp = NULL; + char **papszParams = ParseConnectionString(pszFilename); + if (papszParams == NULL) { + return false; + } + + /******************************************************************* + * Get mode: + * - 1. ONE_RASTER_PER_ROW: Each row is considered as a separate + * raster + * - 2. ONE_RASTER_PER_TABLE: All the table rows are considered as + * a whole raster coverage + ******************************************************************/ + nPos = CSLFindName(papszParams, "mode"); + if (nPos != -1) { + int tmp; + tmp = atoi(CPLParseNameValue(papszParams[nPos], NULL)); + + // default value + *nMode = ONE_RASTER_PER_ROW; + + if (tmp == 2) { + *nMode = ONE_RASTER_PER_TABLE; + } + + + /* Remove the mode from connection string */ + papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL); + } + /* Default mode */ + else + *nMode = ONE_RASTER_PER_ROW; + + /** + * Case 1: There's no database name: Error, you need, at least, + * specify a database name (NOTE: insensitive search) + **/ + nPos = CSLFindName(papszParams, "dbname"); + if (nPos == -1) { + CPLError(CE_Failure, CPLE_AppDefined, + "You must specify at least a db name"); + + CSLDestroy(papszParams); + + return false; + } + + *ppszDbname = + CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL)); + + /** + * Case 2: There's database name, but no table name: activate a flag + * for browsing the database, fetching all the schemas that contain + * raster tables + **/ + nPos = CSLFindName(papszParams, "table"); + if (nPos == -1) { + *bBrowseDatabase = true; + + /* Get schema name, if exist */ + nPos = CSLFindName(papszParams, "schema"); + if (nPos != -1) { + *ppszSchema = + CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL)); + + /* Delete this pair from params array */ + papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL); + } + + /** + * Remove the rest of the parameters, if exist (they mustn't be + * present if we want a valid PQ connection string) + **/ + nPos = CSLFindName(papszParams, "column"); + if (nPos != -1) { + /* Delete this pair from params array */ + papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL); + } + + nPos = CSLFindName(papszParams, "where"); + if (nPos != -1) { + /* Delete this pair from params array */ + papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL); + } + } else { + *ppszTable = + CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL)); + /* Delete this pair from params array */ + papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL); + + /** + * Case 3: There's database and table name, but no column + * name: Use a default column name and use the table to create + * the dataset + **/ + nPos = CSLFindName(papszParams, "column"); + if (nPos == -1) { + *ppszColumn = CPLStrdup(DEFAULT_COLUMN); + } + /** + * Case 4: There's database, table and column name: Use the + * table to create a dataset + **/ + else { + *ppszColumn = + CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL)); + + /* Delete this pair from params array */ + papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL); + } + + /* Get the rest of the parameters, if exist */ + nPos = CSLFindName(papszParams, "schema"); + if (nPos == -1) { + *ppszSchema = CPLStrdup(DEFAULT_SCHEMA); + } else { + *ppszSchema = + CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL)); + + /* Delete this pair from params array */ + papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL); + } + + nPos = CSLFindName(papszParams, "where"); + if (nPos != -1) { + *ppszWhere = + CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL)); + + /* Delete this pair from params array */ + papszParams = CSLRemoveStrings(papszParams, nPos, 1, NULL); + } + } + + /* Parse ppszWhere, if needed */ + if (*ppszWhere) { + pszTmp = ReplaceQuotes(*ppszWhere, strlen(*ppszWhere)); + CPLFree(*ppszWhere); + *ppszWhere = pszTmp; + } + + /*************************************** + * Construct a valid connection string + ***************************************/ + *ppszConnectionString = (char*) CPLCalloc(strlen(pszFilename), + sizeof (char)); + for (i = 0; i < CSLCount(papszParams); i++) { + *ppszConnectionString = + strncat(*ppszConnectionString, papszParams[i], + strlen(papszParams[i])); + + *ppszConnectionString = + strncat(*ppszConnectionString, " ", strlen(" ")); + } + + nPos = CSLFindName(papszParams, "host"); + if (nPos != -1) { + *ppszHost = + CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL)); + } + else if (CPLGetConfigOption("PGHOST", NULL) != NULL) { + *ppszHost = CPLStrdup(CPLGetConfigOption("PGHOST", NULL)); + } + else + *ppszHost = NULL; + /*else { + CPLError(CE_Failure, CPLE_AppDefined, + "Host parameter must be provided, or PGHOST environment " + "variable must be set. Please set the host and try again."); + + CSLDestroy(papszParams); + + return false; + }*/ + + nPos = CSLFindName(papszParams, "port"); + if (nPos != -1) { + *ppszPort = + CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL)); + } + else if (CPLGetConfigOption("PGPORT", NULL) != NULL ) { + *ppszPort = CPLStrdup(CPLGetConfigOption("PGPORT", NULL)); + } + else + *ppszPort = NULL; + /*else { + CPLError(CE_Failure, CPLE_AppDefined, + "Port parameter must be provided, or PGPORT environment " + "variable must be set. Please set the port and try again."); + + CSLDestroy(papszParams); + + return false; + }*/ + + nPos = CSLFindName(papszParams, "user"); + if (nPos != -1) { + *ppszUser = + CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL)); + } + else if (CPLGetConfigOption("PGUSER", NULL) != NULL ) { + *ppszUser = CPLStrdup(CPLGetConfigOption("PGUSER", NULL)); + } + else + *ppszUser = NULL; + /*else { + CPLError(CE_Failure, CPLE_AppDefined, + "User parameter must be provided, or PGUSER environment " + "variable must be set. Please set the user and try again."); + + CSLDestroy(papszParams); + + return false; + }*/ + + nPos = CSLFindName(papszParams, "password"); + if (nPos != -1) { + *ppszPassword = + CPLStrdup(CPLParseNameValue(papszParams[nPos], NULL)); + } + else if (CPLGetConfigOption("PGPASSWORD", NULL) != NULL ) { + *ppszPassword = CPLStrdup(CPLGetConfigOption("PGPASSWORD", NULL)); + } + else + *ppszPassword = NULL; + + CSLDestroy(papszParams); + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::GetConnectionInfo(): " + "Mode: %d\nDbname: %s\nSchema: %s\nTable: %s\nColumn: %s\nWhere: %s\n" + "Host: %s\nPort: %s\nUser: %s\nPassword: %s\n" + "Connection String: %s\n", + *nMode, *ppszDbname, + *ppszSchema ? *ppszSchema : "(null)", + *ppszTable ? *ppszTable : "(null)", + *ppszColumn ? *ppszColumn : "(null)", + *ppszWhere ? *ppszWhere : "(null)", + *ppszHost ? *ppszHost : "(null)", + *ppszPort ? *ppszPort : "(null)", + *ppszUser ? *ppszUser : "(null)", + *ppszPassword ? *ppszPassword : "(null)", + *ppszConnectionString); +#endif + + return true; +} + +/*********************************************************************** + * \brief Create a connection to a postgres database + **********************************************************************/ +static PGconn * +GetConnection(const char * pszFilename, char ** ppszConnectionString, + char ** ppszSchema, char ** ppszTable, char ** ppszColumn, + char ** ppszWhere, WorkingMode * nMode, GBool * bBrowseDatabase) +{ + PostGISRasterDriver * poDriver; + PGconn * poConn = NULL; + char * pszDbname = NULL; + char * pszHost = NULL; + char * pszPort = NULL; + char * pszUser = NULL; + char * pszPassword = NULL; + + if (GetConnectionInfo(pszFilename, ppszConnectionString, &pszDbname, ppszSchema, + ppszTable, ppszColumn, ppszWhere, &pszHost, &pszPort, &pszUser, + &pszPassword, nMode, bBrowseDatabase)) + { + /************************************************************** + * Open a new database connection + **************************************************************/ + poDriver = + (PostGISRasterDriver *)GDALGetDriverByName("PostGISRaster"); + + poConn = poDriver->GetConnection(*ppszConnectionString, + pszDbname, pszHost, pszPort, pszUser); + + if (poConn == NULL) { + CPLError(CE_Failure, CPLE_AppDefined, + "Couldn't establish a database connection"); + } + } + + CPLFree(pszDbname); + CPLFree(pszHost); + CPLFree(pszPort); + CPLFree(pszUser); + CPLFree(pszPassword); + + return poConn; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int PostGISRasterDataset::Identify(GDALOpenInfo* poOpenInfo) +{ + if (poOpenInfo->pszFilename == NULL || + poOpenInfo->fpL != NULL || + !EQUALN(poOpenInfo->pszFilename, "PG:", 3)) + { + return FALSE; + } + return TRUE; +} + +/*********************************************************************** + * \brief Open a connection with PostgreSQL. The connection string will + * have the PostgreSQL accepted format, plus the next key=value pairs: + * schema = + * table = + * column = + * where = + * mode = (1 or 2) + * + * These pairs are used for selecting the right raster table. + **********************************************************************/ +GDALDataset* PostGISRasterDataset::Open(GDALOpenInfo* poOpenInfo) { + char* pszConnectionString = NULL; + char* pszSchema = NULL; + char* pszTable = NULL; + char* pszColumn = NULL; + char* pszWhere = NULL; + WorkingMode nMode = NO_MODE; + PGconn * poConn = NULL; + PostGISRasterDataset* poDS = NULL; + GBool bBrowseDatabase = false; + CPLString osCommand; + + /************************** + * Check input parameter + **************************/ + if (!Identify(poOpenInfo)) + return NULL; + + poConn = GetConnection(poOpenInfo->pszFilename, + &pszConnectionString, &pszSchema, &pszTable, &pszColumn, + &pszWhere, &nMode, &bBrowseDatabase); + if (poConn == NULL) { + CPLFree(pszConnectionString); + CPLFree(pszSchema); + CPLFree(pszTable); + CPLFree(pszColumn); + CPLFree(pszWhere); + return NULL; + } + + + /******************************************************************* + * No table will be read. Only shows information about the existent + * raster tables + ******************************************************************/ + if (bBrowseDatabase) { + /** + * Creates empty dataset object, only for subdatasets + **/ + poDS = new PostGISRasterDataset(); + poDS->poConn = poConn; + poDS->eAccess = GA_ReadOnly; + //poDS->poDriver = poDriver; + poDS->nMode = (pszSchema) ? BROWSE_SCHEMA : BROWSE_DATABASE; + + /** + * Look for raster tables at database and + * store them as subdatasets + **/ + if (!poDS->BrowseDatabase(pszSchema, pszConnectionString)) { + CPLFree(pszConnectionString); + delete poDS; + + if (pszSchema) + CPLFree(pszSchema); + if (pszTable) + CPLFree(pszTable); + if (pszColumn) + CPLFree(pszColumn); + if (pszWhere) + CPLFree(pszWhere); + + return NULL; + } + + if (pszSchema) + CPLFree(pszSchema); + if (pszTable) + CPLFree(pszTable); + if (pszColumn) + CPLFree(pszColumn); + if (pszWhere) + CPLFree(pszWhere); + } + + /******************************************************************* + * A table will be read as dataset: Fetch raster properties from db. + ******************************************************************/ + else { + poDS = new PostGISRasterDataset(); + poDS->poConn = poConn; + poDS->eAccess = poOpenInfo->eAccess; + poDS->nMode = nMode; + //poDS->poDriver = poDriver; + + poDS->pszSchema = pszSchema; + poDS->pszTable = pszTable; + poDS->pszColumn = pszColumn; + poDS->pszWhere = pszWhere; + + /** + * Fetch basic raster metadata from db + **/ +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", "Open:: connection string = %s", + pszConnectionString); +#endif + + if (!poDS->SetRasterProperties(pszConnectionString)) { + CPLFree(pszConnectionString); + delete poDS; + return NULL; + } + } + + CPLFree(pszConnectionString); + return poDS; + +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **PostGISRasterDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), + TRUE, + "SUBDATASETS", NULL); +} + +/***************************************** + * \brief Get Metadata from raster + * TODO: Add more options (the result of + * calling ST_Metadata, for example) + *****************************************/ +char** PostGISRasterDataset::GetMetadata(const char *pszDomain) { + if (pszDomain != NULL && EQUALN(pszDomain, "SUBDATASETS", 11)) + return papszSubdatasets; + else + return GDALDataset::GetMetadata(pszDomain); +} + +/***************************************************** + * \brief Fetch the projection definition string + * for this dataset in OpenGIS WKT format. It should + * be suitable for use with the OGRSpatialReference + * class. + *****************************************************/ +const char* PostGISRasterDataset::GetProjectionRef() { + CPLString osCommand; + PGresult* poResult; + + if (nSrid == -1) + return ""; + + if (pszProjection) + return pszProjection; + + /******************************************************** + * Reading proj from database + ********************************************************/ + osCommand.Printf("SELECT srtext FROM spatial_ref_sys where SRID=%d", + nSrid); + poResult = PQexec(this->poConn, osCommand.c_str()); + if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK + && PQntuples(poResult) > 0) { + pszProjection = CPLStrdup(PQgetvalue(poResult, 0, 0)); + } + + if (poResult) + PQclear(poResult); + + return pszProjection; +} + +/********************************************************** + * \brief Set projection definition. The input string must + * be in OGC WKT or PROJ.4 format + **********************************************************/ +CPLErr PostGISRasterDataset::SetProjection(const char * pszProjectionRef) { + VALIDATE_POINTER1(pszProjectionRef, "SetProjection", CE_Failure); + + CPLString osCommand; + PGresult * poResult; + int nFetchedSrid = -1; + + + /***************************************************************** + * Check if the dataset allows updating + *****************************************************************/ + if (GetAccess() != GA_Update) { + ReportError(CE_Failure, CPLE_NoWriteAccess, + "This driver doesn't allow write access"); + return CE_Failure; + } + + /***************************************************************** + * Look for projection with this text + *****************************************************************/ + + // First, WKT text + osCommand.Printf("SELECT srid FROM spatial_ref_sys where srtext='%s'", + pszProjectionRef); + poResult = PQexec(poConn, osCommand.c_str()); + + if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK + && PQntuples(poResult) > 0) { + + nFetchedSrid = atoi(PQgetvalue(poResult, 0, 0)); + + // update class attribute + nSrid = nFetchedSrid; + + + // update raster_columns table + osCommand.Printf("UPDATE raster_columns SET srid=%d WHERE \ + r_table_name = '%s' AND r_column = '%s'", + nSrid, pszTable, pszColumn); + poResult = PQexec(poConn, osCommand.c_str()); + if (poResult == NULL || PQresultStatus(poResult) != PGRES_COMMAND_OK) { + ReportError(CE_Failure, CPLE_AppDefined, + "Couldn't update raster_columns table: %s", + PQerrorMessage(poConn)); + return CE_Failure; + } + + // TODO: Update ALL blocks with the new srid... + + return CE_None; + } + // If not, proj4 text + else { + osCommand.Printf( + "SELECT srid FROM spatial_ref_sys where proj4text='%s'", + pszProjectionRef); + poResult = PQexec(poConn, osCommand.c_str()); + + if (poResult && PQresultStatus(poResult) == PGRES_TUPLES_OK + && PQntuples(poResult) > 0) { + + nFetchedSrid = atoi(PQgetvalue(poResult, 0, 0)); + + // update class attribute + nSrid = nFetchedSrid; + + // update raster_columns table + osCommand.Printf("UPDATE raster_columns SET srid=%d WHERE \ + r_table_name = '%s' AND r_column = '%s'", + nSrid, pszTable, pszColumn); + + poResult = PQexec(poConn, osCommand.c_str()); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + ReportError(CE_Failure, CPLE_AppDefined, + "Couldn't update raster_columns table: %s", + PQerrorMessage(poConn)); + return CE_Failure; + } + + // TODO: Update ALL blocks with the new srid... + + return CE_None; + } + else { + ReportError(CE_Failure, CPLE_WrongFormat, + "Couldn't find WKT neither proj4 definition"); + return CE_Failure; + } + } +} + +/******************************************************** + * \brief Set the affine transformation coefficients + ********************************************************/ +CPLErr PostGISRasterDataset::SetGeoTransform(double* padfGeoTransform) { + if (!padfGeoTransform) + return CE_Failure; + + memcpy(adfGeoTransform, padfGeoTransform, 6 * sizeof(double)); + + return CE_None; +} + +/******************************************************** + * \brief Get the affine transformation coefficients + ********************************************************/ +CPLErr PostGISRasterDataset::GetGeoTransform(double * padfGeoTransform) { + + // copy necessary values in supplied buffer + memcpy(padfGeoTransform, adfGeoTransform, 6 * sizeof(double)); + + if( nRasterXSize == 0 && nRasterYSize == 0 ) + return CE_Failure; + + /* To avoid QGIS trying to create a warped VRT for what is really */ + /* an ungeoreferenced dataset */ + if( CPLIsEqual(padfGeoTransform[0], 0.0) && + CPLIsEqual(padfGeoTransform[1], 1.0) && + CPLIsEqual(padfGeoTransform[2], 0.0) && + CPLIsEqual(padfGeoTransform[3], 0.0) && + CPLIsEqual(padfGeoTransform[4], 0.0) && + CPLIsEqual(padfGeoTransform[5], 1.0) ) + { + return CE_Failure; + } + + return CE_None; +} + + +/********************************************************* + * \brief Fetch files forming dataset. + * + * We need to define this method because the VRTDataset + * method doesn't check for NULL FileList before trying + * to collect the names of all sources' file list. + *********************************************************/ +char **PostGISRasterDataset::GetFileList() +{ + return NULL; +} + +/******************************************************** + * \brief Create a copy of a PostGIS Raster dataset. + ********************************************************/ +GDALDataset * +PostGISRasterDataset::CreateCopy( CPL_UNUSED const char * pszFilename, + GDALDataset *poGSrcDS, + CPL_UNUSED int bStrict, + CPL_UNUSED char ** papszOptions, + CPL_UNUSED GDALProgressFunc pfnProgress, + CPL_UNUSED void * pProgressData ) +{ + char* pszSchema = NULL; + char* pszTable = NULL; + char* pszColumn = NULL; + char* pszWhere = NULL; + GBool bBrowseDatabase = false; + WorkingMode nMode; + char* pszConnectionString = NULL; + const char* pszSubdatasetName; + PGconn * poConn = NULL; + PGresult * poResult = NULL; + CPLString osCommand; + GBool bInsertSuccess; + + if( poGSrcDS->GetDriver() != GDALGetDriverByName("PostGISRaster") ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "PostGISRasterDataset::CreateCopy() only works on source " + "datasets that are PostGISRaster" ); + return NULL; + } + + // Now we can do the cast + PostGISRasterDataset *poSrcDS = (PostGISRasterDataset *)poGSrcDS; + PostGISRasterDataset *poSubDS; + + // Check connection string + if (pszFilename == NULL || + !EQUALN(pszFilename, "PG:", 3)) { + /** + * The connection string provided is not a valid connection + * string. + */ + CPLError( CE_Failure, CPLE_NotSupported, + "PostGIS Raster driver was unable to parse the provided " + "connection string." ); + return NULL; + } + + poConn = GetConnection(pszFilename, &pszConnectionString, &pszSchema, + &pszTable, &pszColumn, &pszWhere, &nMode, &bBrowseDatabase); + if (poConn == NULL || bBrowseDatabase || pszTable == NULL) + { + CPLFree(pszConnectionString); + CPLFree(pszSchema); + CPLFree(pszTable); + CPLFree(pszColumn); + CPLFree(pszWhere); + + // if connection info fails, browsing mode, or no table set + return NULL; + } + + // begin transaction + poResult = PQexec(poConn, "begin"); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + CPLError(CE_Failure, CPLE_AppDefined, + "Error beginning database transaction: %s", + PQerrorMessage(poConn)); + if (poResult != NULL) + PQclear(poResult); + if (pszSchema) + CPLFree(pszSchema); + if (pszTable) + CPLFree(pszTable); + if (pszColumn) + CPLFree(pszColumn); + if (pszWhere) + CPLFree(pszWhere); + + CPLFree(pszConnectionString); + + return NULL; + } + + PQclear(poResult); + + // create table for raster (if not exists because a + // dataset will not be reported for an empty table) + + // TODO: is 'rid' necessary? + osCommand.Printf("create table if not exists %s.%s (rid serial, %s " + "public.raster, constraint %s_pkey primary key (rid));", + pszSchema, pszTable, pszColumn, pszTable); + poResult = PQexec(poConn, osCommand.c_str()); + if ( + poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + + CPLError(CE_Failure, CPLE_AppDefined, + "Error creating needed tables: %s", + PQerrorMessage(poConn)); + if (poResult != NULL) + PQclear(poResult); + + // rollback + poResult = PQexec(poConn, "rollback"); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + + CPLError(CE_Failure, CPLE_AppDefined, + "Error rolling back transaction: %s", + PQerrorMessage(poConn)); + } + if (poResult != NULL) + PQclear(poResult); + if (pszSchema) + CPLFree(pszSchema); + if (pszTable) + CPLFree(pszTable); + if (pszColumn) + CPLFree(pszColumn); + if (pszWhere) + CPLFree(pszWhere); + + CPLFree(pszConnectionString); + + return NULL; + } + + PQclear(poResult); + + osCommand.Printf("create index %s_%s_gist ON %s.%s USING gist " + "(public.st_convexhull(%s));", pszTable, pszColumn, + pszSchema, pszTable, pszColumn); + poResult = PQexec(poConn, osCommand.c_str()); + if ( + poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + + CPLError(CE_Failure, CPLE_AppDefined, + "Error creating needed index: %s", + PQerrorMessage(poConn)); + if (poResult != NULL) + PQclear(poResult); + + // rollback + poResult = PQexec(poConn, "rollback"); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + + CPLError(CE_Failure, CPLE_AppDefined, + "Error rolling back transaction: %s", + PQerrorMessage(poConn)); + } + if (poResult != NULL) + PQclear(poResult); + if (pszSchema) + CPLFree(pszSchema); + if (pszTable) + CPLFree(pszTable); + if (pszColumn) + CPLFree(pszColumn); + if (pszWhere) + CPLFree(pszWhere); + + CPLFree(pszConnectionString); + + return NULL; + } + + PQclear(poResult); + + if (poSrcDS->nMode == ONE_RASTER_PER_TABLE) { + // one raster per table + + // insert one raster + bInsertSuccess = InsertRaster(poConn, poSrcDS, + pszSchema, pszTable, pszColumn); + if (!bInsertSuccess) { + // rollback + poResult = PQexec(poConn, "rollback"); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + + CPLError(CE_Failure, CPLE_AppDefined, + "Error rolling back transaction: %s", + PQerrorMessage(poConn)); + } + if (poResult != NULL) + PQclear(poResult); + if (pszSchema) + CPLFree(pszSchema); + if (pszTable) + CPLFree(pszTable); + if (pszColumn) + CPLFree(pszColumn); + if (pszWhere) + CPLFree(pszWhere); + + CPLFree(pszConnectionString); + + return NULL; + } + } + else if (poSrcDS->nMode == ONE_RASTER_PER_ROW) { + // one raster per row + + // papszSubdatasets contains name/desc for each subdataset + for (int i = 0; i < CSLCount(poSrcDS->papszSubdatasets); i += 2) { + pszSubdatasetName = CPLParseNameValue( poSrcDS->papszSubdatasets[i], NULL); + if (pszSubdatasetName == NULL) { + CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): " + "Could not parse name/value out of subdataset list: " + "%s", poSrcDS->papszSubdatasets[i]); + continue; + } + + // for each subdataset + GDALOpenInfo poOpenInfo( pszSubdatasetName, GA_ReadOnly ); + // open the subdataset + poSubDS = (PostGISRasterDataset *)Open(&poOpenInfo); + + if (poSubDS == NULL) { + // notify! + CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): " + "Could not open a subdataset: %s", + pszSubdatasetName); + continue; + } + + // insert one raster + bInsertSuccess = InsertRaster(poConn, poSubDS, + pszSchema, pszTable, pszColumn); + + if (!bInsertSuccess) { + CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): " + "Could not copy raster subdataset to new dataset." ); + + // keep trying ... + } + + // close this dataset + GDALClose((GDALDatasetH)poSubDS); + } + } + + // commit transaction + poResult = PQexec(poConn, "commit"); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + CPLError(CE_Failure, CPLE_AppDefined, + "Error committing database transaction: %s", + PQerrorMessage(poConn)); + if (poResult != NULL) + PQclear(poResult); + if (pszSchema) + CPLFree(pszSchema); + if (pszTable) + CPLFree(pszTable); + if (pszColumn) + CPLFree(pszColumn); + if (pszWhere) + CPLFree(pszWhere); + + CPLFree(pszConnectionString); + + return NULL; + } + + PQclear(poResult); + + if (pszSchema) + CPLFree(pszSchema); + if (pszTable) + CPLFree(pszTable); + if (pszColumn) + CPLFree(pszColumn); + if (pszWhere) + CPLFree(pszWhere); + + CPLFree(pszConnectionString); + + CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): " + "Opening new dataset: %s", pszFilename); + + // connect to the new dataset + GDALOpenInfo poOpenInfo( pszFilename, GA_Update ); + // open the newdataset + poSubDS = (PostGISRasterDataset *)Open(&poOpenInfo); + + if (poSubDS == NULL) { + CPLDebug("PostGIS_Raster", "PostGISRasterDataset::CreateCopy(): " + "New dataset could not be opened."); + } + + return poSubDS; +} + +/******************************************************** + * \brief Helper method to insert a new raster. + ********************************************************/ +GBool +PostGISRasterDataset::InsertRaster(PGconn * poConn, + PostGISRasterDataset * poSrcDS, const char *pszSchema, + const char * pszTable, const char * pszColumn) +{ + CPLString osCommand; + PGresult * poResult = NULL; + + if (poSrcDS->pszWhere == NULL) { + osCommand.Printf("insert into %s.%s (%s) (select %s from %s.%s)", + pszSchema, pszTable, pszColumn, poSrcDS->pszColumn, + poSrcDS->pszSchema, poSrcDS->pszTable); + } + else { + osCommand.Printf("insert into %s.%s (%s) (select %s from %s.%s where %s)", + pszSchema, pszTable, pszColumn, poSrcDS->pszColumn, + poSrcDS->pszSchema, poSrcDS->pszTable, poSrcDS->pszWhere); + } + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", "PostGISRasterDataset::InsertRaster(): Query = %s", + osCommand.c_str()); +#endif + + poResult = PQexec(poConn, osCommand.c_str()); + if ( + poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + + CPLError(CE_Failure, CPLE_AppDefined, + "Error inserting raster: %s", + PQerrorMessage(poConn)); + if (poResult != NULL) + PQclear(poResult); + + return false; + } + + PQclear(poResult); + + return true; +} + +/********************************************************* + * \brief Delete a PostGIS Raster dataset. + *********************************************************/ +CPLErr +PostGISRasterDataset::Delete(const char* pszFilename) +{ + char* pszSchema = NULL; + char* pszTable = NULL; + char* pszColumn = NULL; + char* pszWhere = NULL; + GBool bBrowseDatabase; + char* pszConnectionString = NULL; + WorkingMode nMode; + PGconn * poConn = NULL; + PGresult * poResult = NULL; + CPLString osCommand; + CPLErr nError = CE_Failure; + + // Check connection string + if (pszFilename == NULL || + !EQUALN(pszFilename, "PG:", 3)) { + /** + * The connection string provided is not a valid connection + * string. + */ + CPLError( CE_Failure, CPLE_NotSupported, + "PostGIS Raster driver was unable to parse the provided " + "connection string. Nothing was deleted." ); + return CE_Failure; + } + + poConn = GetConnection(pszFilename, &pszConnectionString, + &pszSchema, &pszTable, &pszColumn, &pszWhere, + &nMode, &bBrowseDatabase); + if (poConn == NULL || pszSchema == NULL || pszTable == NULL) { + CPLFree(pszConnectionString); + CPLFree(pszSchema); + CPLFree(pszTable); + CPLFree(pszColumn); + CPLFree(pszWhere); + + return CE_Failure; + } + + // begin transaction + poResult = PQexec(poConn, "begin"); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + CPLError(CE_Failure, CPLE_AppDefined, + "Error beginning database transaction: %s", + PQerrorMessage(poConn)); + + // set nMode to NO_MODE to avoid any further processing + nMode = NO_MODE; + } + + PQclear(poResult); + + if ( nMode == ONE_RASTER_PER_TABLE || + (nMode == ONE_RASTER_PER_ROW && pszWhere == NULL)) { + // without a where clause, this delete command shall delete + // all subdatasets, even if the mode is ONE_RASTER_PER_ROW + + // drop table .; + osCommand.Printf("drop table %s.%s", pszSchema, pszTable); + poResult = PQexec(poConn, osCommand.c_str()); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + CPLError(CE_Failure, CPLE_AppDefined, + "Couldn't drop the table %s.%s: %s", + pszSchema, pszTable, PQerrorMessage(poConn)); + } + else { + PQclear(poResult); + nError = CE_None; + } + } + else if (nMode == ONE_RASTER_PER_ROW) { + + // delete from .
    where + osCommand.Printf("delete from %s.%s where %s", pszSchema, + pszTable, pszWhere); + poResult = PQexec(poConn, osCommand.c_str()); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + CPLError(CE_Failure, CPLE_AppDefined, + "Couldn't delete records from the table %s.%s: %s", + pszSchema, pszTable, PQerrorMessage(poConn)); + } + else { + PQclear(poResult); + nError = CE_None; + } + } + + // if mode == NO_MODE, the begin transaction above did not complete, + // so no commit is necessary + if (nMode != NO_MODE) { + poResult = PQexec(poConn, "commit"); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_COMMAND_OK) { + + CPLError(CE_Failure, CPLE_AppDefined, + "Error committing database transaction: %s", + PQerrorMessage(poConn)); + + nError = CE_Failure; + } + } + + if (poResult) + PQclear(poResult); + if (pszSchema) + CPLFree(pszSchema); + if (pszTable) + CPLFree(pszTable); + if (pszColumn) + CPLFree(pszColumn); + if (pszWhere) + CPLFree(pszWhere); + + // clean up connection string + CPLFree(pszConnectionString); + + return nError; +} + +/*********************************************************************** + * \brief Create an array with all the coordinates needed to construct + * a polygon using ST_PolygonFromText. + **********************************************************************/ +GBool PostGISRasterDataset::PolygonFromCoords( + int nXOff, int nYOff, int nXEndOff, int nYEndOff, + double adfProjWin[8]) +{ + // We first construct a polygon to intersect with + int ulx = nXOff; + int uly = nYOff; + int lrx = nXEndOff; + int lry = nYEndOff; + + double xRes = adfGeoTransform[GEOTRSFRM_WE_RES]; + double yRes = adfGeoTransform[GEOTRSFRM_NS_RES]; + + adfProjWin[0] = adfGeoTransform[GEOTRSFRM_TOPLEFT_X] + + ulx * xRes + + uly * adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1]; + adfProjWin[1] = adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] + + ulx * adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] + + uly * yRes; + adfProjWin[2] = adfGeoTransform[GEOTRSFRM_TOPLEFT_X] + + lrx * xRes + + uly * adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1]; + adfProjWin[3] = adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] + + lrx * adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] + + uly * yRes; + adfProjWin[4] = adfGeoTransform[GEOTRSFRM_TOPLEFT_X] + + lrx * xRes + + lry * adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1]; + adfProjWin[5] = adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] + + lrx * adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] + + lry * yRes; + adfProjWin[6] = adfGeoTransform[GEOTRSFRM_TOPLEFT_X] + + ulx * xRes + + lry * adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1]; + adfProjWin[7] = adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] + + ulx * adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] + + lry * yRes; + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::PolygonFromCoords: constructed " + "polygon: POLYGON((%.17f %.17f, %.17f %.17f, %.17f %.17f, " + "%.17f %.17f, %.17f %.17f))", adfProjWin[0], adfProjWin[1], + adfProjWin[2], adfProjWin[3], adfProjWin[4], adfProjWin[5], + adfProjWin[6], adfProjWin[7], adfProjWin[0], adfProjWin[1]); +#endif + + return true; +} + +/*********************************************************************** + * GDALRegister_PostGISRaster() + **********************************************************************/ +void GDALRegister_PostGISRaster() { + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("PostGISRaster driver")) + return; + + if (GDALGetDriverByName("PostGISRaster") == NULL) { + poDriver = new PostGISRasterDriver(); + + poDriver->SetDescription("PostGISRaster"); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, + "PostGIS Raster driver"); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + + poDriver->pfnOpen = PostGISRasterDataset::Open; + poDriver->pfnIdentify = PostGISRasterDataset::Identify; + poDriver->pfnCreateCopy = PostGISRasterDataset::CreateCopy; + poDriver->pfnDelete = PostGISRasterDataset::Delete; + + GetGDALDriverManager()->RegisterDriver(poDriver); + } +} diff --git a/bazaar/plugin/gdal/frmts/postgisraster/postgisrasterdriver.cpp b/bazaar/plugin/gdal/frmts/postgisraster/postgisrasterdriver.cpp new file mode 100644 index 000000000..aec1fa17a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/postgisraster/postgisrasterdriver.cpp @@ -0,0 +1,117 @@ +/****************************************************************************** + * File : PostGISRasterDriver.cpp + * Project: PostGIS Raster driver + * Purpose: Implements PostGIS Raster driver class methods + * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com + * + * Last changes: $Id: $ + * + ****************************************************************************** + * Copyright (c) 2010, Jorge Arevalo, jorge.arevalo@deimos-space.com + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ +#include "postgisraster.h" +#include "cpl_multiproc.h" + +/************************ + * \brief Constructor + ************************/ +PostGISRasterDriver::PostGISRasterDriver() +{ + hMutex = NULL; +} + +/************************ + * \brief Destructor + ************************/ +PostGISRasterDriver::~PostGISRasterDriver() { + + if( hMutex != NULL ) + CPLDestroyMutex(hMutex); + std::map::iterator oIter = oMapConnection.begin(); + for(; oIter != oMapConnection.end(); ++oIter ) + PQfinish(oIter->second); +} + +/*************************************************************************** + * \brief Create a PQconn object and store it in a list + * + * The PostGIS Raster driver keeps the connection with the PostgreSQL database + * server for as long it leaves. Following PostGISRasterDataset instance + * can re-use the existing connection as long it used the same database, + * same host, port and user name. + * + * The PostGIS Raster driver will keep a list of all the successful + * connections so, when connection is requested and it does not exist + * on the list a new one will be instantiated, added to the list and + * returned to the caller. + * + * All connection will be destroyed when the PostGISRasterDriver is destroyed. + * + ***************************************************************************/ +PGconn* PostGISRasterDriver::GetConnection(const char* pszConnectionString, + const char * pszDbnameIn, const char * pszHostIn, const char * pszPortIn, const char * pszUserIn) +{ + PGconn * poConn = NULL; + + if( pszHostIn == NULL ) pszHostIn = "(null)"; + if( pszPortIn == NULL ) pszPortIn = "(null)"; + if( pszUserIn == NULL ) pszUserIn = "(null)"; + CPLString osKey = pszDbnameIn; + osKey += "-"; + osKey += pszHostIn; + osKey += "-"; + osKey += pszPortIn; + osKey += "-"; + osKey += pszUserIn; + osKey += "-"; + osKey += CPLSPrintf(CPL_FRMT_GIB, CPLGetPID()); + + /** + * Look for an existing connection in the map + **/ + CPLMutexHolderD(&hMutex); + std::map::iterator oIter = oMapConnection.find(osKey); + if( oIter != oMapConnection.end() ) + return oIter->second; + + + /** + * There's no existing connection. Create a new one. + **/ + poConn = PQconnectdb(pszConnectionString); + if (poConn == NULL || + PQstatus(poConn) == CONNECTION_BAD) { + CPLError(CE_Failure, CPLE_AppDefined, "PQconnectdb failed: %s\n", + PQerrorMessage(poConn)); + PQfinish(poConn); + return NULL; + } + + /** + * Save connection in the connection map. + **/ + oMapConnection[osKey] = poConn; + return poConn; +} + + + diff --git a/bazaar/plugin/gdal/frmts/postgisraster/postgisrasterrasterband.cpp b/bazaar/plugin/gdal/frmts/postgisraster/postgisrasterrasterband.cpp new file mode 100644 index 000000000..95e80149d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/postgisraster/postgisrasterrasterband.cpp @@ -0,0 +1,1012 @@ +/*********************************************************************** + * File : postgisrasterrasterband.cpp + * Project: PostGIS Raster driver + * Purpose: GDAL RasterBand implementation for PostGIS Raster driver + * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com + * jorgearevalo@libregis.org + * + * Author: David Zwarg, dzwarg@azavea.com + * + * Last changes: $Id: $ + * + *********************************************************************** + * Copyright (c) 2009 - 2013, Jorge Arevalo, David Zwarg + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + **********************************************************************/ +#include "postgisraster.h" + +/** + * \brief Constructor. + * + * nBand it's just necessary for overview band creation + */ +PostGISRasterRasterBand::PostGISRasterRasterBand( + PostGISRasterDataset * poDS, int nBand, + GDALDataType eDataType, GBool bNoDataValueSet, double dfNodata, + GBool bIsOffline = false) : + VRTSourcedRasterBand(poDS, nBand) +{ + /* Basic properties */ + this->poDS = poDS; + this->bIsOffline = bIsOffline; + this->nBand = nBand; + + this->eDataType = eDataType; + this->bNoDataValueSet = bNoDataValueSet; + this->dfNoDataValue = dfNodata; + + this->pszSchema = poDS->pszSchema; + this->pszTable = poDS->pszTable; + this->pszColumn = poDS->pszColumn; + + nRasterXSize = poDS->GetRasterXSize(); + nRasterYSize = poDS->GetRasterYSize(); + + /******************************************************************* + * Finally, set the block size. We apply the same logic than in VRT + * driver. + * + * We limit the size of a block with MAX_BLOCK_SIZE here to prevent + * arrangements of just one big tile. + * + * This value is just used in case whe only have 1 tile in the + * table. Otherwise, the reading operations are performed by the + * sources, not the PostGISRasterBand object itself. + ******************************************************************/ + this->nBlockXSize = MIN(MAX_BLOCK_SIZE, this->nRasterXSize); + this->nBlockYSize = MIN(MAX_BLOCK_SIZE, this->nRasterYSize); + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterRasterBand constructor: Band created (srid = %d)", + poDS->nSrid); + + CPLDebug("PostGIS_Raster", + "PostGISRasterRasterBand constructor: Band size: (%d X %d)", + nRasterXSize, nRasterYSize); + + CPLDebug("PostGIS_Raster", "PostGISRasterRasterBand::Constructor: " + "Block size (%dx%d)", this->nBlockXSize, this->nBlockYSize); +#endif +} + +/*********************************************** + * \brief: Band destructor + ***********************************************/ +PostGISRasterRasterBand::~PostGISRasterRasterBand() +{ +} + +#ifdef notdef +/*********************************************************************** + * \brief Set the block data to the null value if it is set, or zero if + * there is no null data value. + * Parameters: + * - void *: the block data + * Returns: nothing + **********************************************************************/ +void PostGISRasterRasterBand::NullBlock(void *pData) +{ + int nWords = nBlockXSize * nBlockYSize; + int nDataTypeSize = GDALGetDataTypeSize(eDataType) / 8; + + int bNoDataSet; + double dfNoData = GetNoDataValue(&bNoDataSet); + if (!bNoDataSet) { + memset(pData, 0, nWords * nDataTypeSize); + } + + else { + GDALCopyWords(&dfNoData, GDT_Float64, 0, + pData, eDataType, nDataTypeSize, + nWords); + } +} + +/*********************************************************************** + * \brief Returns the metadata for this band + * + * If the metadata is actually stored in band's properties, simply + * returns them. Otherwise, it raises a query to fetch metadata + * FROM db + **********************************************************************/ +GBool PostGISRasterRasterBand::GetBandMetadata( + GDALDataType * peDataType, GBool * pbHasNoData, double * pdfNoData) +{ + // No need to raise a query + if (eDataType != GDT_Unknown) { + if (peDataType) + *peDataType = eDataType; + + if (pbHasNoData) + *pbHasNoData = bNoDataValueSet; + + if (pdfNoData) + *pdfNoData = dfNoDataValue; + + return true; + } + + /** + * Queries are expensive. So, we only raise them if all parameters + * are not null + **/ + if (!peDataType || !pbHasNoData || !pdfNoData) { + return false; + } + + /** + * It is safe to assume all the tiles will have the same values for + * metadata properties. That was checked during band's construction + * (or we simply trusted the user, to avoid expensive checkings). + * So, we can limit the results to just one. + **/ + int nTuples = 0; + CPLString osCommand = NULL; + PGresult * poResult = NULL; + PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS; + + osCommand.Printf("st_bandpixeltype(%s, %d), " + "st_bandnodatavalue(%s, %d) is not null, " + "st_bandnodatavalue(%s, %d) FROM %s.%s limit 1", pszColumn, + nBand, pszColumn, nBand, pszColumn, nBand, pszSchema, pszTable); + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterRasterBand::GetBandMetadata(): Query: %s", + osCommand.c_str()); +#endif + + poResult = PQexec(poRDS->poConn, osCommand.c_str()); + nTuples = PQntuples(poResult); + + /* Error getting info FROM database */ + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + nTuples <= 0) { + + ReportError(CE_Failure, CPLE_AppDefined, + "Error getting band metadata while creating raster " + "bands"); + + CPLDebug("PostGIS_Raster", + "PostGISRasterDataset::GetBandMetadata(): %s", + PQerrorMessage(poRDS->poConn)); + + if (poResult) + PQclear(poResult); + + return false; + } + + // Fill band metadata values + GBool bSignedByte = false; + int nBitsDepth = 8; + char* pszDataType = NULL; + + pszDataType = CPLStrdup(PQgetvalue(poResult, 0, 0)); + + TranslateDataType(pszDataType, &eDataType, &nBitsDepth, + &bSignedByte); + + // Add pixeltype to image structure domain + if (bSignedByte) { + SetMetadataItem("PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE" ); + } + + // Add NBITS to metadata only for sub-byte types + if (nBitsDepth < 8) + SetMetadataItem("NBITS", CPLString().Printf( "%d", nBitsDepth ), + "IMAGE_STRUCTURE" ); + + CPLFree(pszDataType); + + bNoDataValueSet = + EQUALN(PQgetvalue(poResult, 0, 1), "t", sizeof(char)); + + dfNoDataValue = CPLAtof(PQgetvalue(poResult, 0, 2)); + + // Fill output arguments + *peDataType = eDataType; + *pbHasNoData = bNoDataValueSet; + *pdfNoData = dfNoDataValue; + + return true; +} +#endif + +/******************************************************** + * \brief Set nodata value to a buffer + ********************************************************/ +void PostGISRasterRasterBand::NullBuffer(void* pData, + int nBufXSize, + int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, + int nLineSpace) +{ + int j; + for(j = 0; j < nBufYSize; j++) + { + double dfVal = 0.0; + if( bNoDataValueSet ) + dfVal = dfNoDataValue; + GDALCopyWords(&dfVal, GDT_Float64, 0, + (GByte*)pData + j * nLineSpace, eBufType, nPixelSpace, + nBufXSize); + } +} + + +/******************************************************** + * \brief SortTilesByPKID + ********************************************************/ +static int SortTilesByPKID(const void* a, const void* b) +{ + PostGISRasterTileDataset* pa = *(PostGISRasterTileDataset** )a; + PostGISRasterTileDataset* pb = *(PostGISRasterTileDataset** )b; + return strcmp(pa->GetPKID(), pb->GetPKID()); +} + +/** + * Read/write a region of image data for this band. + * + * This method allows reading a region of a PostGISRasterBand into a buffer. + * The write support is still under development + * + * The function fetches all the raster data that intersects with the region + * provided, and store the data in the GDAL cache. + * + * It automatically takes care of data type translation if the data type + * (eBufType) of the buffer is different than that of the PostGISRasterRasterBand. + * + * The nPixelSpace and nLineSpace parameters allow reading into FROM various + * organization of buffers. + * + * @param eRWFlag Either GF_Read to read a region of data (GF_Write, to write + * a region of data, yet not supported) + * + * @param nXOff The pixel offset to the top left corner of the region of the + * band to be accessed. This would be zero to start FROM the left side. + * + * @param nYOff The line offset to the top left corner of the region of the band + * to be accessed. This would be zero to start FROM the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param pData The buffer into which the data should be read, or FROM which it + * should be written. This buffer must contain at least + * nBufXSize * nBufYSize * nBandCount words of type eBufType. It is organized in + * left to right,top to bottom pixel order. Spacing is controlled by the + * nPixelSpace, and nLineSpace parameters. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or FROM which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired region + * is to be read, or FROM which it is to be written. + * + * @param eBufType the type of the pixel values in the pData data buffer. The + * pixel values will automatically be translated to/FROM the + * PostGISRasterRasterBand data type as needed. + * + * @param nPixelSpace The byte offset FROM the start of one pixel value in pData + * to the start of the next pixel value within a scanline. If defaulted (0) the + * size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset FROM the start of one scanline in pData to + * the start of the next. If defaulted (0) the size of the datatype + * eBufType * nBufXSize is used. + * + * @return CE_Failure if the access fails, otherwise CE_None. + */ + +CPLErr PostGISRasterRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, + int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize, + int nBufYSize, GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg* psExtraArg) +{ + /** + * TODO: Write support not implemented yet + **/ + if (eRWFlag == GF_Write) { + ReportError(CE_Failure, CPLE_NotSupported, + "Writing through PostGIS Raster band not supported yet"); + + return CE_Failure; + } + + /******************************************************************* + * Do we have overviews that would be appropriate to satisfy this + * request? + ******************************************************************/ + if( (nBufXSize < nXSize || nBufYSize < nYSize) && + GetOverviewCount() > 0 ) + { + if(OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, + nLineSpace, psExtraArg) == CE_None) + + return CE_None; + } + + PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS; + + int bSameWindowAsOtherBand = + (nXOff == poRDS->nXOffPrev && + nYOff == poRDS->nYOffPrev && + nXSize == poRDS->nXSizePrev && + nYSize == poRDS->nYSizePrev); + poRDS->nXOffPrev = nXOff; + poRDS->nYOffPrev = nYOff; + poRDS->nXSizePrev = nXSize; + poRDS->nYSizePrev = nYSize; + + /* Logic to determine if bands are read in order 1, 2, ... N */ + /* If so, then use multi-band caching, otherwise do just single band caching */ + if( poRDS->bAssumeMultiBandReadPattern ) + { + if( nBand != poRDS->nNextExpectedBand ) + { + CPLDebug("PostGIS_Raster", + "Disabling multi-band caching since band access pattern does not match"); + poRDS->bAssumeMultiBandReadPattern = false; + poRDS->nNextExpectedBand = 1; + } + else + { + poRDS->nNextExpectedBand ++; + if( poRDS->nNextExpectedBand > poRDS->GetRasterCount() ) + poRDS->nNextExpectedBand = 1; + } + } + else + { + if( nBand == poRDS->nNextExpectedBand ) + { + poRDS->nNextExpectedBand ++; + if( poRDS->nNextExpectedBand > poRDS->GetRasterCount() ) + { + CPLDebug("PostGIS_Raster", "Re-enabling multi-band caching"); + poRDS->bAssumeMultiBandReadPattern = true; + poRDS->nNextExpectedBand = 1; + } + } + } + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterRasterBand::IRasterIO: " + "nBand = %d, nXOff = %d, nYOff = %d, nXSize = %d, nYSize = %d, nBufXSize = %d, nBufYSize = %d", + nBand, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize); +#endif + +#ifdef notdef + /******************************************************************* + * Optimization: We just have one tile. So, we can read it with + * IReadBlock + * + * TODO: Review it. It's not working (see comment in + * PostGISRasterDataset::ConstructOneDatasetFromTiles) + ******************************************************************/ + + if (poRDS->nTiles == 1) { + + return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, + nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, + nLineSpace, psExtraArg); + } +#endif + + /******************************************************************* + * Several tiles: we first look in all our sources caches. Missing + * blocks are queried + ******************************************************************/ + double adfProjWin[8]; + int nFeatureCount = 0; + CPLRectObj sAoi; + + poRDS->PolygonFromCoords(nXOff, nYOff, nXOff + nXSize, nYOff + nYSize, adfProjWin); + // (p[6], p[7]) is the minimum (x, y), and (p[2], p[3]) the max + sAoi.minx = adfProjWin[6]; + sAoi.maxx = adfProjWin[2]; + if( adfProjWin[7] < adfProjWin[3] ) + { + sAoi.miny = adfProjWin[7]; + sAoi.maxy = adfProjWin[3]; + } + else + { + sAoi.maxy = adfProjWin[7]; + sAoi.miny = adfProjWin[3]; + } + +#ifdef DEBUG_VERBOSE + CPLDebug("PostGIS_Raster", + "PostGISRasterRasterBand::IRasterIO: " + "Intersection box: (%f, %f) - (%f, %f)", sAoi.minx, + sAoi.miny, sAoi.maxx, sAoi.maxy); +#endif + + if (poRDS->hQuadTree == NULL) + { + ReportError(CE_Failure, CPLE_AppDefined, + "Could not read metadata index."); + return CE_Failure; + } + + NullBuffer(pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace); + + if( poRDS->bBuildQuadTreeDynamically && !bSameWindowAsOtherBand ) + { + if( !(poRDS->LoadSources(nXOff, nYOff, nXSize, nYSize, nBand)) ) + return CE_Failure; + } + + // Matching sources, to avoid a dumb for loop over the sources + PostGISRasterTileDataset ** papsMatchingTiles = + (PostGISRasterTileDataset **) + CPLQuadTreeSearch(poRDS->hQuadTree, &sAoi, &nFeatureCount); + + // No blocks found. This is not an error (the raster may have holes) + if (nFeatureCount == 0) { + CPLFree(papsMatchingTiles); + + return CE_None; + } + + int i; + + /** + * We need to store the max, min coords for the missing tiles in + * any place. This is as good as any other + **/ + sAoi.minx = 0.0; + sAoi.miny = 0.0; + sAoi.maxx = 0.0; + sAoi.maxy = 0.0; + + GIntBig nMemoryRequiredForTiles = 0; + CPLString osIDsToFetch; + int nTilesToFetch = 0; + int nBandDataTypeSize = GDALGetDataTypeSize(eDataType) / 8; + + // Loop just over the intersecting sources + for(i = 0; i < nFeatureCount; i++) { + PostGISRasterTileDataset *poTile = papsMatchingTiles[i]; + PostGISRasterTileRasterBand* poTileBand = + (PostGISRasterTileRasterBand *)poTile->GetRasterBand(nBand); + + nMemoryRequiredForTiles += poTileBand->GetXSize() * poTileBand->GetYSize() * + nBandDataTypeSize; + + // Missing tile: we'll need to query for it + if (!poTileBand->IsCached()) { + + // If we have a PKID, add the tile PKID to the list + if (poTile->pszPKID != NULL) + { + if( osIDsToFetch.size() != 0 ) + osIDsToFetch += ","; + osIDsToFetch += "'"; + osIDsToFetch += poTile->pszPKID; + osIDsToFetch += "'"; + } + + double dfTileMinX, dfTileMinY, dfTileMaxX, dfTileMaxY; + poTile->GetExtent(&dfTileMinX, &dfTileMinY, + &dfTileMaxX, &dfTileMaxY); + + /** + * We keep the general max and min values of all the missing + * tiles, to raise a query that intersect just that area. + * + * TODO: In case of just a few tiles and very separated, + * this strategy is clearly suboptimal. We'll get our + * missing tiles, but with a lot of other not needed tiles. + * + * A possible optimization will be to simply rely on the + * I/O method of the source (must be implemented), in case + * we have minus than a reasonable amount of tiles missing. + * Another criteria to decide would be how separated the + * tiles are. Two queries for just two adjacent tiles is + * also a dumb strategy. + **/ + if( nTilesToFetch == 0 ) + { + sAoi.minx = dfTileMinX; + sAoi.miny = dfTileMinY; + sAoi.maxx = dfTileMaxX; + sAoi.maxy = dfTileMaxY; + } + else + { + if (dfTileMinX < sAoi.minx) + sAoi.minx = dfTileMinX; + + if (dfTileMinY < sAoi.miny) + sAoi.miny = dfTileMinY; + + if (dfTileMaxX > sAoi.maxx) + sAoi.maxx = dfTileMaxX; + + if (dfTileMaxY > sAoi.maxy) + sAoi.maxy = dfTileMaxY; + } + + nTilesToFetch ++; + + } + } + + /* Determine caching strategy */ + int bAllBandCaching = FALSE; + if (nTilesToFetch > 0) + { + GIntBig nCacheMax = (GIntBig) GDALGetCacheMax64(); + if( nMemoryRequiredForTiles > nCacheMax ) + { + CPLDebug("PostGIS_Raster", + "For best performance, the block cache should be able to store " CPL_FRMT_GIB + " bytes for the tiles of the requested window, " + "but it is only " CPL_FRMT_GIB " byte large", + nMemoryRequiredForTiles, nCacheMax ); + nTilesToFetch = 0; + } + + if( poRDS->GetRasterCount() > 1 && poRDS->bAssumeMultiBandReadPattern ) + { + GIntBig nMemoryRequiredForTilesAllBands = + nMemoryRequiredForTiles * poRDS->GetRasterCount(); + if( nMemoryRequiredForTilesAllBands <= nCacheMax ) + { + bAllBandCaching = TRUE; + } + else + { + CPLDebug("PostGIS_Raster", "Caching only this band, but not all bands. " + "Cache should be " CPL_FRMT_GIB " byte large for that", + nMemoryRequiredForTilesAllBands); + } + } + } + + // Raise a query for missing tiles and cache them + if (nTilesToFetch > 0) { + + /** + * There are several options here, to raise the query. + * - Get all the tiles which PKID is in a list of missing + * PKIDs. + * - Get all the tiles that intersect a polygon constructed + * based on the (min - max) values calculated before. + * - Get all the tiles with upper left pixel included in the + * range (min - max) calculated before. + * + * The first option is the most efficient one when a PKID exists. + * After that, the second one is the most efficient one when a + * spatial index exists. + * The third one is the only one available when neither a PKID or spatial + * index exist. + **/ + CPLString osCommand; + PGresult * poResult; + + CPLString osRasterToFetch; + if (bAllBandCaching) + osRasterToFetch = pszColumn; + else + osRasterToFetch.Printf("ST_Band(%s, %d)", pszColumn, nBand); + + int bHasWhere = FALSE; + if (osIDsToFetch.size() && (poRDS->bIsFastPK || !(poRDS->HasSpatialIndex())) ) { + osCommand.Printf("SELECT %s, " + "ST_Metadata(%s), %s FROM %s.%s", + osRasterToFetch.c_str(), pszColumn, + poRDS->GetPrimaryKeyRef(), pszSchema, pszTable); + if( nTilesToFetch < poRDS->nTiles || poRDS->bBuildQuadTreeDynamically ) + { + bHasWhere = TRUE; + osCommand += " WHERE "; + osCommand += poRDS->pszPrimaryKeyName; + osCommand += " IN ("; + osCommand += osIDsToFetch; + osCommand += ")"; + } + } + + else { + bHasWhere = TRUE; + osCommand.Printf("SELECT %s, ST_Metadata(%s), %s FROM %s.%s WHERE ", + osRasterToFetch.c_str(), pszColumn, + (poRDS->GetPrimaryKeyRef()) ? poRDS->GetPrimaryKeyRef() : "'foo'", + pszSchema, pszTable); + if( poRDS->HasSpatialIndex() ) + { + osCommand += CPLSPrintf("%s && " + "ST_GeomFromText('POLYGON((%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f,%.18f %.18f))')", + pszColumn, + adfProjWin[0], adfProjWin[1], + adfProjWin[2], adfProjWin[3], + adfProjWin[4], adfProjWin[5], + adfProjWin[6], adfProjWin[7], + adfProjWin[0], adfProjWin[1]); + } + else + { + #define EPS 1e-5 + osCommand += CPLSPrintf("ST_UpperLeftX(%s)" + " BETWEEN %f AND %f AND ST_UpperLeftY(%s) BETWEEN " + "%f AND %f", pszColumn, sAoi.minx-EPS, sAoi.maxx+EPS, + pszColumn, sAoi.miny-EPS, sAoi.maxy+EPS); + } + } + + if( poRDS->pszWhere != NULL ) + { + if( bHasWhere ) + osCommand += " AND ("; + else + osCommand += " WHERE ("; + osCommand += poRDS->pszWhere; + osCommand += ")"; + } + + poResult = PQexec(poRDS->poConn, osCommand.c_str()); + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterRasterBand::IRasterIO(): Query = \"%s\" --> number of rows = %d", + osCommand.c_str(), poResult ? PQntuples(poResult) : 0 ); +#endif + + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) < 0) { + + if (poResult) + PQclear(poResult); + + CPLError(CE_Failure, CPLE_AppDefined, + "PostGISRasterRasterBand::IRasterIO(): %s", + PQerrorMessage(poRDS->poConn)); + + // Free the object that holds pointers to matching tiles + CPLFree(papsMatchingTiles); + return CE_Failure; + } + + /** + * No data. Return the buffer filled with nodata values + **/ + else if (PQntuples(poResult) == 0) { + PQclear(poResult); + + // Free the object that holds pointers to matching tiles + CPLFree(papsMatchingTiles); + return CE_None; + } + + /** + * Ok, we loop over the results + **/ + int nTuples = PQntuples(poResult); + for(i = 0; i < nTuples; i++) + { + const char* pszMetadata = PQgetvalue(poResult, i, 1); + const char* pszRaster = PQgetvalue(poResult, i, 0); + const char *pszPKID = (poRDS->GetPrimaryKeyRef() != NULL) ? PQgetvalue(poResult, i, 2) : NULL; + poRDS->CacheTile(pszMetadata, pszRaster, pszPKID, nBand, bAllBandCaching); + } // All tiles have been added to cache + + PQclear(poResult); + } // End missing tiles + +/* -------------------------------------------------------------------- */ +/* Overlay each source in turn over top this. */ +/* -------------------------------------------------------------------- */ + + CPLErr eErr = CE_None; + /* Sort tiles by ascending PKID, so that the draw order is determinist */ + if( poRDS->GetPrimaryKeyRef() != NULL ) + { + qsort(papsMatchingTiles, nFeatureCount, sizeof(PostGISRasterTileDataset*), + SortTilesByPKID); + } + + for(i = 0; i < nFeatureCount && eErr == CE_None; i++) + { + PostGISRasterTileDataset *poTile = papsMatchingTiles[i]; + PostGISRasterTileRasterBand* poTileBand = + (PostGISRasterTileRasterBand *)poTile->GetRasterBand(nBand); + eErr = + poTileBand->poSource->RasterIO( nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, NULL); + } + + // Free the object that holds pointers to matching tiles + CPLFree(papsMatchingTiles); + + return eErr; +} + +/** + * \brief Set the no data value for this band. + * Parameters: + * - double: The nodata value + * Returns: + * - CE_None. + */ +CPLErr PostGISRasterRasterBand::SetNoDataValue(double dfNewValue) { + dfNoDataValue = dfNewValue; + + return CE_None; +} + +/** + * \brief Fetch the no data value for this band. + * Parameters: + * - int *: pointer to a boolean to use to indicate if a value is actually + * associated with this layer. May be NULL (default). + * Returns: + * - double: the nodata value for this band. + */ +double PostGISRasterRasterBand::GetNoDataValue(int *pbSuccess) { + if (pbSuccess != NULL) + *pbSuccess = (int) bNoDataValueSet; + + return dfNoDataValue; +} + +/*************************************************** + * \brief Return the number of overview layers available + ***************************************************/ +int PostGISRasterRasterBand::GetOverviewCount() +{ + PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS; + return poRDS->GetOverviewCount(); +} + +/********************************************************** + * \brief Fetch overview raster band object + **********************************************************/ +GDALRasterBand * PostGISRasterRasterBand::GetOverview(int i) +{ + if (i < 0 || i >= GetOverviewCount()) + return NULL; + + PostGISRasterDataset* poRDS = (PostGISRasterDataset *)poDS; + PostGISRasterDataset* poOverviewDS = poRDS->GetOverviewDS(i); + if( poOverviewDS->nBands == 0 ) + { + if (!poOverviewDS->SetRasterProperties(NULL) || + poOverviewDS->GetRasterCount() != poRDS->GetRasterCount()) + { + CPLDebug("PostGIS_Raster", + "Request for overview %d of band %d failed", i, nBand); + return NULL; + } + } + + return poOverviewDS->GetRasterBand(nBand); +} + +#ifdef notdef +/***************************************************** + * \brief Read a natural block of raster band data + *****************************************************/ +CPLErr PostGISRasterRasterBand::IReadBlock(int nBlockXOff, + int nBlockYOff, void * pImage) +{ + PGresult * poResult = NULL; + CPLString osCommand; + int nXOff = 0; + int nYOff = 0; + int nNaturalBlockXSize = 0; + int nNaturalBlockYSize = 0; + double adfProjWin[8]; + PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS; + + int nPixelSize = GDALGetDataTypeSize(eDataType) / 8; + + // Construct a polygon to intersect with + GetBlockSize(&nNaturalBlockXSize, &nNaturalBlockYSize); + + nXOff = nBlockXOff * nNaturalBlockXSize; + nYOff = nBlockYOff * nNaturalBlockYSize; + + poRDS->PolygonFromCoords(nXOff, nYOff, nXOff + nNaturalBlockXSize, nYOff + nNaturalBlockYSize, adfProjWin); + + // Raise the query + if (poRDS->pszWhere == NULL) { + osCommand.Printf("SELECT st_band(%s, %d) FROM %s.%s " + "WHERE st_intersects(%s, ST_PolygonFromText" + "('POLYGON((%.17f %.17f, %.17f %.17f, %.17f %.17f, %.17f " + "%.17f, %.17f %.17f))', %d))", pszColumn, nBand, pszSchema, + pszTable, pszColumn, adfProjWin[0], adfProjWin[1], + adfProjWin[2], adfProjWin[3], adfProjWin[4], adfProjWin[5], + adfProjWin[6], adfProjWin[7], adfProjWin[0], adfProjWin[1], + poRDS->nSrid); + } + + else { + osCommand.Printf("SELECT st_band(%s, %d) FROM %s.%s WHERE (%s) " + "AND st_intersects(%s, ST_PolygonFromText" + "('POLYGON((%.17f %.17f, %.17f %.17f, %.17f %.17f, %.17f " + "%.17f, %.17f %.17f))', %d))", pszColumn, nBand, pszSchema, + pszTable, poRDS->pszWhere, pszColumn, adfProjWin[0], + adfProjWin[1], adfProjWin[2], adfProjWin[3], adfProjWin[4], + adfProjWin[5], adfProjWin[6], adfProjWin[7], adfProjWin[0], + adfProjWin[1], poRDS->nSrid); + } + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", + "PostGISRasterRasterBand::IReadBlock(): Query = %s", + osCommand.c_str()); +#endif + + poResult = PQexec(poRDS->poConn, osCommand.c_str()); + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) < 0) { + + if (poResult) + PQclear(poResult); + + ReportError(CE_Failure, CPLE_AppDefined, + "Error retrieving raster data FROM database"); + + CPLDebug("PostGIS_Raster", + "PostGISRasterRasterBand::IRasterIO(): %s", + PQerrorMessage(poRDS->poConn)); + + return CE_Failure; + } + + /** + * No data. Return the buffer filled with nodata values + **/ + else if (PQntuples(poResult) == 0) { + PQclear(poResult); + + CPLDebug("PostGIS_Raster", + "PostGISRasterRasterBand::IRasterIO(): Null block"); + + NullBlock(pImage); + + return CE_None; + } + + + /** + * Ok, we get the data. Only data size, without payload + * + * TODO: Check byte order + **/ + int nExpectedDataSize = nNaturalBlockXSize * nNaturalBlockYSize * + nPixelSize; + + int nWKBLength = 0; + + GByte * pbyData = CPLHexToBinary(PQgetvalue(poResult, 0, 0), + &nWKBLength); + + char * pbyDataToRead = (char*)GET_BAND_DATA(pbyData,nBand, + nPixelSize, nExpectedDataSize); + + memcpy(pImage, pbyDataToRead, nExpectedDataSize * sizeof(char)); + + CPLDebug("PostGIS_Raster", "IReadBlock: Copied %d bytes FROM block " + "(%d, %d) to %p", nExpectedDataSize, nBlockXOff, + nBlockYOff, pImage); + + CPLFree(pbyData); + PQclear(poResult); + + return CE_None; +} +#endif + +/** + * \brief How should this band be interpreted as color? + * GCI_Undefined is returned when the format doesn't know anything about the + * color interpretation. + **/ +GDALColorInterp PostGISRasterRasterBand::GetColorInterpretation() +{ + if (poDS->GetRasterCount() == 1) { + eColorInterp = GCI_GrayIndex; + } + + else if (poDS->GetRasterCount() == 3) { + if (nBand == 1) + eColorInterp = GCI_RedBand; + else if( nBand == 2 ) + eColorInterp = GCI_GreenBand; + else if( nBand == 3 ) + eColorInterp = GCI_BlueBand; + else + eColorInterp = GCI_Undefined; + } + + else { + eColorInterp = GCI_Undefined; + } + + return eColorInterp; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double PostGISRasterRasterBand::GetMinimum( int *pbSuccess ) +{ + PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS; + if( poRDS->bBuildQuadTreeDynamically && poRDS->nTiles == 0 ) + { + if( pbSuccess ) + *pbSuccess = FALSE; + return 0.0; + } + return VRTSourcedRasterBand::GetMaximum(pbSuccess); +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double PostGISRasterRasterBand::GetMaximum( int *pbSuccess ) +{ + PostGISRasterDataset * poRDS = (PostGISRasterDataset *)poDS; + if( poRDS->bBuildQuadTreeDynamically && poRDS->nTiles == 0 ) + { + if( pbSuccess ) + *pbSuccess = FALSE; + return 0.0; + } + return VRTSourcedRasterBand::GetMaximum(pbSuccess); +} + +/************************************************************************/ +/* ComputeRasterMinMax() */ +/************************************************************************/ + +CPLErr PostGISRasterRasterBand::ComputeRasterMinMax( int bApproxOK, double* adfMinMax ) +{ + if( nRasterXSize < 1024 && nRasterYSize < 1024 ) + return VRTSourcedRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax); + + int nOverviewCount = GetOverviewCount(); + for(int i = 0; i < nOverviewCount; i++) + { + if( GetOverview(i)->GetXSize() < 1024 && GetOverview(i)->GetYSize() < 1024 ) + return GetOverview(i)->ComputeRasterMinMax(bApproxOK, adfMinMax); + } + + return CE_Failure; +} diff --git a/bazaar/plugin/gdal/frmts/postgisraster/postgisrastertiledataset.cpp b/bazaar/plugin/gdal/frmts/postgisraster/postgisrastertiledataset.cpp new file mode 100644 index 000000000..35206022f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/postgisraster/postgisrastertiledataset.cpp @@ -0,0 +1,114 @@ +/*********************************************************************** + * File : postgisrastertiledataset.cpp + * Project: PostGIS Raster driver + * Purpose: GDAL Dataset implementation for PostGIS Raster tile + * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com + * jorgearevalo@libregis.org + * + * Last changes: $Id: $ + * + *********************************************************************** + * Copyright (c) 2013, Jorge Arevalo + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ************************************************************************/ +#include "postgisraster.h" + +/************************ + * \brief Constructor + ************************/ +PostGISRasterTileDataset::PostGISRasterTileDataset(PostGISRasterDataset* poRDS, + int nXSize, + int nYSize) +{ + this->poRDS = poRDS; + this->pszPKID = NULL; + this->nRasterXSize = nXSize; + this->nRasterYSize = nYSize; + + adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = 0; + adfGeoTransform[GEOTRSFRM_WE_RES] = 1; + adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0; + adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = 0; + adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0; + adfGeoTransform[GEOTRSFRM_NS_RES] = 1; +} + + +/************************ + * \brief Destructor + ************************/ +PostGISRasterTileDataset::~PostGISRasterTileDataset() +{ + if (pszPKID) { + CPLFree(pszPKID); + pszPKID = NULL; + } +} + +/******************************************************** + * \brief Get the affine transformation coefficients + ********************************************************/ +CPLErr PostGISRasterTileDataset::GetGeoTransform(double * padfTransform) { + // copy necessary values in supplied buffer + padfTransform[0] = adfGeoTransform[0]; + padfTransform[1] = adfGeoTransform[1]; + padfTransform[2] = adfGeoTransform[2]; + padfTransform[3] = adfGeoTransform[3]; + padfTransform[4] = adfGeoTransform[4]; + padfTransform[5] = adfGeoTransform[5]; + + return CE_None; +} + +/******************************************************** + * \brief Return spatial extent of tile + ********************************************************/ +void PostGISRasterTileDataset::GetExtent(double* pdfMinX, double* pdfMinY, + double* pdfMaxX, double* pdfMaxY) +{ + // FIXME; incorrect in case of non 0 rotation terms + + double dfMinX = adfGeoTransform[GEOTRSFRM_TOPLEFT_X]; + double dfMaxY = adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]; + + double dfMaxX = adfGeoTransform[GEOTRSFRM_TOPLEFT_X] + + nRasterXSize * adfGeoTransform[GEOTRSFRM_WE_RES] + + nRasterYSize * adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1]; + + double dfMinY = adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] + + nRasterXSize * adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] + + nRasterYSize * adfGeoTransform[GEOTRSFRM_NS_RES]; + + // In case yres > 0 + if( dfMinY > dfMaxY ) + { + double dfTemp = dfMinY; + dfMinY = dfMaxY; + dfMaxY = dfTemp; + } + + *pdfMinX = dfMinX; + *pdfMinY = dfMinY; + *pdfMaxX = dfMaxX; + *pdfMaxY = dfMaxY; +} diff --git a/bazaar/plugin/gdal/frmts/postgisraster/postgisrastertilerasterband.cpp b/bazaar/plugin/gdal/frmts/postgisraster/postgisrastertilerasterband.cpp new file mode 100644 index 000000000..8a8b7de85 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/postgisraster/postgisrastertilerasterband.cpp @@ -0,0 +1,193 @@ +/*********************************************************************** + * File : postgisrastertilerasterband.cpp + * Project: PostGIS Raster driver + * Purpose: GDAL Tile RasterBand implementation for PostGIS Raster + * driver + * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com + * jorgearevalo@libregis.org + * Last changes: $Id: $ + * + *********************************************************************** + * Copyright (c) 2009 - 2013, Jorge Arevalo + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + **********************************************************************/ +#include "postgisraster.h" + +/************************ + * \brief Constructor + ************************/ +PostGISRasterTileRasterBand::PostGISRasterTileRasterBand( + PostGISRasterTileDataset * poRTDS, int nBand, + GDALDataType eDataType, GBool bIsOffline) +{ + /* Basic properties */ + this->poDS = poRTDS; + this->bIsOffline = bIsOffline; + this->nBand = nBand; + +#if 0 + CPLDebug("PostGIS_Raster", + "PostGISRasterTileRasterBand::Constructor: Raster tile dataset " + "of dimensions %dx%d", poRTDS->GetRasterXSize(), + poRTDS->GetRasterYSize()); +#endif + + this->eDataType = eDataType; + + nRasterXSize = poRTDS->GetRasterXSize(); + nRasterYSize = poRTDS->GetRasterYSize(); + + nBlockXSize = nRasterXSize; + nBlockYSize = nRasterYSize; +} + + +/************************ + * \brief Destructor + ************************/ +PostGISRasterTileRasterBand::~PostGISRasterTileRasterBand() +{ +} + +/*********************************************************************** + * \brief Returns true if the (only) block is stored in the cache + **********************************************************************/ +GBool PostGISRasterTileRasterBand::IsCached() +{ + GDALRasterBlock * poBlock = TryGetLockedBlockRef(0, 0); + if (poBlock != NULL) { + poBlock->DropLock(); + return true; + } + + return false; +} + +/***************************************************** + * \brief Read a natural block of raster band data + *****************************************************/ +CPLErr PostGISRasterTileRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, + CPL_UNUSED int nBlockYOff, + void * pImage) +{ + CPLString osCommand; + PGresult * poResult = NULL; + int nWKBLength = 0; + + int nPixelSize = GDALGetDataTypeSize(eDataType)/8; + + PostGISRasterTileDataset * poRTDS = + (PostGISRasterTileDataset *)poDS; + + // Get by PKID + if (poRTDS->poRDS->pszPrimaryKeyName) { + osCommand.Printf("select st_band(%s, %d) from %s.%s where " + "%s = '%s'", poRTDS->poRDS->pszColumn, nBand, poRTDS->poRDS->pszSchema, poRTDS->poRDS->pszTable, + poRTDS->poRDS->pszPrimaryKeyName, poRTDS->pszPKID); + + } + + // Get by upperleft + else { + osCommand.Printf("select st_band(%s, %d) from %s.%s where " + "abs(ST_UpperLeftX(%s) - %.8f) < 1e-8 and abs(ST_UpperLeftY(%s) - %.8f) < 1e-8", + poRTDS->poRDS->pszColumn, nBand, poRTDS->poRDS->pszSchema, poRTDS->poRDS->pszTable, poRTDS->poRDS->pszColumn, + poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_X], poRTDS->poRDS->pszColumn, + poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]); + + } + + poResult = PQexec(poRTDS->poRDS->poConn, osCommand.c_str()); + +#ifdef DEBUG_QUERY + CPLDebug("PostGIS_Raster", "PostGISRasterTileRasterBand::IReadBlock(): " + "Query = \"%s\" --> number of rows = %d", + osCommand.c_str(), poResult ? PQntuples(poResult) : 0 ); +#endif + + if (poResult == NULL || + PQresultStatus(poResult) != PGRES_TUPLES_OK || + PQntuples(poResult) <= 0) { + + if (poResult) + PQclear(poResult); + + ReportError(CE_Failure, CPLE_AppDefined, + "Error getting block of data (upperpixel = %f, %f)", + poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_X], + poRTDS->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]); + + return CE_Failure; + } + + + // TODO: Check this + if (bIsOffline) { + CPLError(CE_Failure, CPLE_AppDefined, "This raster has outdb " + "storage. This feature isn't still available"); + + PQclear(poResult); + return CE_Failure; + } + + /* Copy only data size, without payload */ + int nExpectedDataSize = + nBlockXSize * nBlockYSize * nPixelSize; + + GByte * pbyData = CPLHexToBinary(PQgetvalue(poResult, 0, 0), + &nWKBLength); + int nExpectedWKBLength = RASTER_HEADER_SIZE + BAND_SIZE(nPixelSize, nExpectedDataSize); + CPLErr eRet = CE_None; + if( nWKBLength != nExpectedWKBLength ) + { + CPLDebug("PostGIS_Raster", "nWKBLength=%d, nExpectedWKBLength=%d", nWKBLength, nExpectedWKBLength ); + eRet = CE_Failure; + } + else + { + GByte * pbyDataToRead = + (GByte*)GET_BAND_DATA(pbyData,1, nPixelSize, + nExpectedDataSize); + + // Do byte-swapping if necessary */ + int bIsLittleEndian = (pbyData[0] == 1); +#ifdef CPL_LSB + int bSwap = !bIsLittleEndian; +#else + int bSwap = bIsLittleEndian; +#endif + if( bSwap && nPixelSize > 1 ) + { + GDALSwapWords( pbyDataToRead, nPixelSize, + nBlockXSize * nBlockYSize, + nPixelSize ); + } + + memcpy(pImage, pbyDataToRead, nExpectedDataSize); + } + + CPLFree(pbyData); + PQclear(poResult); + + return eRet; +} diff --git a/bazaar/plugin/gdal/frmts/postgisraster/postgisrastertools.cpp b/bazaar/plugin/gdal/frmts/postgisraster/postgisrastertools.cpp new file mode 100644 index 000000000..5b4d58db0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/postgisraster/postgisrastertools.cpp @@ -0,0 +1,226 @@ +/*********************************************************************** + * File : postgisrastertools.cpp + * Project: PostGIS Raster driver + * Purpose: Tools for PostGIS Raster driver + * Author: Jorge Arevalo, jorge.arevalo@deimos-space.com + * jorgearevalo@libregis.org + * + * Author: David Zwarg, dzwarg@azavea.com + * + * Last changes: $Id: $ + * + *********************************************************************** + * Copyright (c) 2009 - 2013, Jorge Arevalo, David Zwarg + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + **********************************************************************/ + #include "postgisraster.h" + + + /********************************************************************** + * \brief Replace the quotes by single quotes in the input string + * + * Needed in the 'where' part of the input string + **********************************************************************/ +char * ReplaceQuotes(const char * pszInput, int nLength) { + int i; + char * pszOutput = NULL; + + if (nLength == -1) + nLength = strlen(pszInput); + + pszOutput = (char*) CPLCalloc(nLength + 1, sizeof (char)); + + for (i = 0; i < nLength; i++) { + if (pszInput[i] == '"') + pszOutput[i] = '\''; + else + pszOutput[i] = pszInput[i]; + } + + return pszOutput; +} + +/************************************************************** + * \brief Replace the single quotes by " in the input string + * + * Needed before tokenize function + *************************************************************/ +char * ReplaceSingleQuotes(const char * pszInput, int nLength) { + int i; + char* pszOutput = NULL; + + if (nLength == -1) + nLength = strlen(pszInput); + + pszOutput = (char*) CPLCalloc(nLength + 1, sizeof (char)); + + for (i = 0; i < nLength; i++) { + if (pszInput[i] == '\'') + pszOutput[i] = '"'; + else + pszOutput[i] = pszInput[i]; + + } + + return pszOutput; +} + + +/*********************************************************************** + * \brief Split connection string into user, password, host, database... + * + * The parameters separated by spaces are return as a list of strings. + * The function accepts all the PostgreSQL recognized parameter keywords. + * + * The returned list must be freed with CSLDestroy when no longer needed + **********************************************************************/ +char** ParseConnectionString(const char * pszConnectionString) { + char * pszEscapedConnectionString = NULL; + + /* Escape string following SQL scheme */ + pszEscapedConnectionString = + ReplaceSingleQuotes(pszConnectionString, -1); + + /* Avoid PG: part */ + char* pszStartPos = (char*) + strstr(pszEscapedConnectionString, ":") + 1; + + /* Tokenize */ + char** papszParams = + CSLTokenizeString2(pszStartPos, " ", CSLT_HONOURSTRINGS); + + /* Free */ + CPLFree(pszEscapedConnectionString); + + return papszParams; +} + +/*********************************************************************** + * \brief Translate a PostGIS Raster datatype string in a valid + * GDALDataType object. + **********************************************************************/ +GBool TranslateDataType(const char * pszDataType, + GDALDataType * poDataType = NULL, int * pnBitsDepth = NULL, + GBool * pbSignedByte = NULL) +{ + if (!pszDataType) + return false; + + if (pbSignedByte) + *pbSignedByte = false; + + if (EQUALN(pszDataType, "1BB", 3 * sizeof(char))) { + if (pnBitsDepth) + *pnBitsDepth = 1; + if (poDataType) + *poDataType = GDT_Byte; + } + + else if (EQUALN(pszDataType, "2BUI", 4 * sizeof (char))) { + if (pnBitsDepth) + *pnBitsDepth = 2; + if (poDataType) + *poDataType = GDT_Byte; + } + + else if (EQUALN(pszDataType, "4BUI", 4 * sizeof (char))) { + if (pnBitsDepth) + *pnBitsDepth = 4; + if (poDataType) + *poDataType = GDT_Byte; + } + + else if (EQUALN(pszDataType, "8BUI", 4 * sizeof (char))) { + if (pnBitsDepth) + *pnBitsDepth = 8; + if (poDataType) + *poDataType = GDT_Byte; + } + + else if (EQUALN(pszDataType, "8BSI", 4 * sizeof (char))) { + if (pnBitsDepth) + *pnBitsDepth = 8; + if (poDataType) + *poDataType = GDT_Byte; + + /** + * To indicate the unsigned byte values between 128 and 255 + * should be interpreted as being values between -128 and -1 for + * applications that recognize the SIGNEDBYTE type. + **/ + if (pbSignedByte) + *pbSignedByte = true; + } + else if (EQUALN(pszDataType, "16BSI", 5 * sizeof (char))) { + if (pnBitsDepth) + *pnBitsDepth = 16; + if (poDataType) + *poDataType = GDT_Int16; + } + + else if (EQUALN(pszDataType, "16BUI", 5 * sizeof (char))) { + if (pnBitsDepth) + *pnBitsDepth = 16; + if (poDataType) + *poDataType = GDT_UInt16; + } + + else if (EQUALN(pszDataType, "32BSI", 5 * sizeof (char))) { + if (pnBitsDepth) + *pnBitsDepth = 32; + if (poDataType) + *poDataType = GDT_Int32; + } + + else if (EQUALN(pszDataType, "32BUI", 5 * sizeof (char))) { + if (pnBitsDepth) + *pnBitsDepth = 32; + if (poDataType) + *poDataType = GDT_UInt32; + } + + else if (EQUALN(pszDataType, "32BF", 4 * sizeof (char))) { + if (pnBitsDepth) + *pnBitsDepth = 32; + if (poDataType) + *poDataType = GDT_Float32; + } + + else if (EQUALN(pszDataType, "64BF", 4 * sizeof (char))) { + if (pnBitsDepth) + *pnBitsDepth = 64; + if (poDataType) + *poDataType = GDT_Float64; + } + + else { + if (pnBitsDepth) + *pnBitsDepth = -1; + if (poDataType) + *poDataType = GDT_Unknown; + + return false; + } + + return true; +} + diff --git a/bazaar/plugin/gdal/frmts/postgisraster/readme b/bazaar/plugin/gdal/frmts/postgisraster/readme new file mode 100644 index 000000000..d1118fd28 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/postgisraster/readme @@ -0,0 +1,20 @@ +This driver was started during the Google Summer of Code 2009, and improved since then, being under development at same time than PostGIS Raster extension itself. So far, this is a read-only driver. The next version of the driver will include these features: + +* Out-db raster support. +* Create and modify raster on PostGIS database. + +Author information: + name: Jorge Arévalo + website: http://www.libregis.org + email: jorgearevalo@libregis.org + +Author information: + name: David Zwarg + website: http://www.azavea.com/about-us/staff-profiles/david-zwarg/ + email: dzwarg@azavea.com + +Important links: + PostGIS Raster extension information: http://trac.osgeo.org/postgis/wiki/WKTRaster + GDAL PostGIS Raster driver page: http://trac.osgeo.org/gdal/wiki/frmts_wtkraster.html + +Last update: 2013-01-05 diff --git a/bazaar/plugin/gdal/frmts/postgisraster/todo b/bazaar/plugin/gdal/frmts/postgisraster/todo new file mode 100644 index 000000000..6ae3b99ff --- /dev/null +++ b/bazaar/plugin/gdal/frmts/postgisraster/todo @@ -0,0 +1,6 @@ +TODO List : + +Please, check open tickets here: http://trac.osgeo.org/gdal/wiki/frmts_wtkraster.html#OpenTickets + +Last update: 2013-01-05 + diff --git a/bazaar/plugin/gdal/frmts/r/GNUmakefile b/bazaar/plugin/gdal/frmts/r/GNUmakefile new file mode 100644 index 000000000..01efd2041 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/r/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = rdataset.o rcreatecopy.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/r/frmt_r.html b/bazaar/plugin/gdal/frmts/r/frmt_r.html new file mode 100644 index 000000000..9507ed78a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/r/frmt_r.html @@ -0,0 +1,37 @@ + + +R -- R Object Data Store + + + + +

    R -- R Object Data Store

    + +The R Object File Format is supported for write access, and limited +read access by GDAL. This format is the native format R uses for objects +saved with the save command and loaded with the load command. +GDAL supports writing a dataset as an array object in this format, and +supports reading files with simple rasters in essentially the same +organization. It will not read most R object files.

    + +Currently there is no support for reading or writing georeferencing +information.

    + +

    Creation Options

    + +
      + +
    • ASCII=YES/NO: Produce an ASCII formatted file, instead of binary, if set to YES. Default is NO. +
    • COMPRESS=YES/NO: Produces a compressed file if YES, otherwise an uncompressed file. Default is YES. +
    + +See Also: +

    + +

    + + + + diff --git a/bazaar/plugin/gdal/frmts/r/makefile.vc b/bazaar/plugin/gdal/frmts/r/makefile.vc new file mode 100644 index 000000000..f1cc3d25a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/r/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = rdataset.obj rcreatecopy.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/r/rcreatecopy.cpp b/bazaar/plugin/gdal/frmts/r/rcreatecopy.cpp new file mode 100644 index 000000000..a4d07f1e1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/r/rcreatecopy.cpp @@ -0,0 +1,257 @@ +/****************************************************************************** + * $Id: rcreatecopy.cpp 28785 2015-03-26 20:46:45Z goatbar $ + * + * Project: R Format Driver + * Purpose: CreateCopy() implementation for R stats package object format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: rcreatecopy.cpp 28785 2015-03-26 20:46:45Z goatbar $"); + +/************************************************************************/ +/* ==================================================================== */ +/* Writer Implementation */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* RWriteInteger() */ +/************************************************************************/ + +static void RWriteInteger( VSILFILE *fp, int bASCII, int nValue ) + +{ + if( bASCII ) + { + char szOutput[50]; + sprintf( szOutput, "%d\n", nValue ); + VSIFWriteL( szOutput, 1, strlen(szOutput), fp ); + } + else + { + CPL_MSBPTR32( &nValue ); + VSIFWriteL( &nValue, 4, 1, fp ); + } +} + +/************************************************************************/ +/* RWriteString() */ +/************************************************************************/ + +static void RWriteString( VSILFILE *fp, int bASCII, const char *pszValue ) + +{ + RWriteInteger( fp, bASCII, 4105 ); + RWriteInteger( fp, bASCII, (int) strlen(pszValue) ); + + if( bASCII ) + { + VSIFWriteL( pszValue, 1, strlen(pszValue), fp ); + VSIFWriteL( "\n", 1, 1, fp ); + } + else + { + VSIFWriteL( pszValue, 1, (int) strlen(pszValue), fp ); + } +} + +/************************************************************************/ +/* RCreateCopy() */ +/************************************************************************/ + +GDALDataset * +RCreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + CPL_UNUSED int bStrict, + char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int bASCII = CSLFetchBoolean( papszOptions, "ASCII", FALSE ); + int bCompressed = CSLFetchBoolean( papszOptions, "COMPRESS", !bASCII ); + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +/* Setup the filename to actually use. We prefix with */ +/* /vsigzip/ if we want compressed output. */ +/* -------------------------------------------------------------------- */ + CPLString osAdjustedFilename; + + if( bCompressed ) + osAdjustedFilename = "/vsigzip/"; + + osAdjustedFilename += pszFilename; + +/* -------------------------------------------------------------------- */ +/* Create the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + + fp = VSIFOpenL( osAdjustedFilename, "wb" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create file %s.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write header with version, etc. */ +/* -------------------------------------------------------------------- */ + if( bASCII ) + { + const char *pszHeader = "RDA2\nA\n"; + VSIFWriteL( pszHeader, 1, strlen(pszHeader), fp ); + } + else + { + const char *pszHeader = "RDX2\nX\n"; + VSIFWriteL( pszHeader, 1, strlen(pszHeader), fp ); + } + + RWriteInteger( fp, bASCII, 2 ); + RWriteInteger( fp, bASCII, 133377 ); + RWriteInteger( fp, bASCII, 131840 ); + +/* -------------------------------------------------------------------- */ +/* Establish the primary pairlist with one component object. */ +/* -------------------------------------------------------------------- */ + RWriteInteger( fp, bASCII, 1026 ); + RWriteInteger( fp, bASCII, 1 ); + +/* -------------------------------------------------------------------- */ +/* Write the object name. Eventually we should derive this */ +/* from the filename, possible with override by a creation */ +/* option. */ +/* -------------------------------------------------------------------- */ + RWriteString( fp, bASCII, "gg" ); + +/* -------------------------------------------------------------------- */ +/* For now we write the raster as a numeric array with */ +/* attributes (526). */ +/* -------------------------------------------------------------------- */ + RWriteInteger( fp, bASCII, 526 ); + RWriteInteger( fp, bASCII, nXSize * nYSize * nBands ); + +/* -------------------------------------------------------------------- */ +/* Write the raster data. */ +/* -------------------------------------------------------------------- */ + double *padfScanline; + CPLErr eErr = CE_None; + int iLine; + + padfScanline = (double *) CPLMalloc( nXSize * sizeof(double) ); + + for( int iBand = 0; iBand < nBands; iBand++ ) + { + GDALRasterBand * poBand = poSrcDS->GetRasterBand( iBand+1 ); + + for( iLine = 0; iLine < nYSize && eErr == CE_None; iLine++ ) + { + int iValue; + + eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + padfScanline, nXSize, 1, GDT_Float64, + sizeof(double), 0, NULL ); + + if( bASCII ) + { + for( iValue = 0; iValue < nXSize; iValue++ ) + { + char szValue[128]; + CPLsprintf(szValue,"%.16g\n", padfScanline[iValue] ); + VSIFWriteL( szValue, 1, strlen(szValue), fp ); + } + } + else + { + for( iValue = 0; iValue < nXSize; iValue++ ) + CPL_MSBPTR64( padfScanline + iValue ); + + VSIFWriteL( padfScanline, 8, nXSize, fp ); + } + + if( eErr == CE_None + && !pfnProgress( (iLine+1) / (double) nYSize, + NULL, pProgressData ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + } + + CPLFree( padfScanline ); + +/* -------------------------------------------------------------------- */ +/* Write out the dims attribute. */ +/* -------------------------------------------------------------------- */ + RWriteInteger( fp, bASCII, 1026 ); + RWriteInteger( fp, bASCII, 1 ); + + RWriteString( fp, bASCII, "dim" ); + + RWriteInteger( fp, bASCII, 13 ); + RWriteInteger( fp, bASCII, 3 ); + RWriteInteger( fp, bASCII, nXSize ); + RWriteInteger( fp, bASCII, nYSize ); + RWriteInteger( fp, bASCII, nBands ); + + RWriteInteger( fp, bASCII, 254 ); + +/* -------------------------------------------------------------------- */ +/* Terminate overall pairlist. */ +/* -------------------------------------------------------------------- */ + RWriteInteger( fp, bASCII, 254 ); + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + VSIFCloseL( fp ); + + if( eErr != CE_None ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + GDALPamDataset *poDS = + (GDALPamDataset *) GDALOpen( pszFilename, GA_ReadOnly ); + + if( poDS ) + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} diff --git a/bazaar/plugin/gdal/frmts/r/rdataset.cpp b/bazaar/plugin/gdal/frmts/r/rdataset.cpp new file mode 100644 index 000000000..2e1af14d4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/r/rdataset.cpp @@ -0,0 +1,615 @@ +/****************************************************************************** + * $Id: rdataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: R Format Driver + * Purpose: Read/write R stats package object format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "../raw/rawdataset.h" + +CPL_CVSID("$Id: rdataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_R(void); +CPL_C_END + +GDALDataset * +RCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); + +#define R_NILSXP 0 +#define R_LISTSXP 2 +#define R_CHARSXP 9 +#define R_INTSXP 13 +#define R_REALSXP 14 +#define R_STRSXP 16 + +/************************************************************************/ +/* ==================================================================== */ +/* RDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class RDataset : public GDALPamDataset +{ + friend class RRasterBand; + VSILFILE *fp; + int bASCII; + CPLString osLastStringRead; + + vsi_l_offset nStartOfData; + + double *padfMatrixValues; + + const char *ASCIIFGets(); + int ReadInteger(); + double ReadFloat(); + const char *ReadString(); + int ReadPair( CPLString &osItemName, int &nItemType ); + + public: + RDataset(); + ~RDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* RRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class RRasterBand : public GDALPamRasterBand +{ + friend class RDataset; + + const double *padfMatrixValues; + + public: + + RRasterBand( RDataset *, int, const double * ); + ~RRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* RRasterBand() */ +/************************************************************************/ + +RRasterBand::RRasterBand( RDataset *poDS, int nBand, + const double *padfMatrixValues ) +{ + this->poDS = poDS; + this->nBand = nBand; + this->padfMatrixValues = padfMatrixValues; + + eDataType = GDT_Float64; + + nBlockXSize = poDS->nRasterXSize; + nBlockYSize = 1; +} + +/************************************************************************/ +/* ~RRasterBand() */ +/************************************************************************/ + +RRasterBand::~RRasterBand() +{ +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr RRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + memcpy( pImage, padfMatrixValues + nBlockYOff * nBlockXSize, + nBlockXSize * 8 ); + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* RDataset() */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* RDataset() */ +/************************************************************************/ + +RDataset::RDataset() +{ + fp = NULL; + padfMatrixValues = NULL; +} + +/************************************************************************/ +/* ~RDataset() */ +/************************************************************************/ + +RDataset::~RDataset() +{ + FlushCache(); + CPLFree(padfMatrixValues); + + if( fp ) + VSIFCloseL( fp ); +} + +/************************************************************************/ +/* ASCIIFGets() */ +/* */ +/* Fetch one line from an ASCII source into osLastStringRead. */ +/************************************************************************/ + +const char *RDataset::ASCIIFGets() + +{ + char chNextChar; + + osLastStringRead.resize(0); + + do + { + chNextChar = '\n'; + VSIFReadL( &chNextChar, 1, 1, fp ); + if( chNextChar != '\n' ) + osLastStringRead += chNextChar; + } while( chNextChar != '\n' && chNextChar != '\0' ); + + return osLastStringRead; +} + +/************************************************************************/ +/* ReadInteger() */ +/************************************************************************/ + +int RDataset::ReadInteger() + +{ + if( bASCII ) + { + return atoi(ASCIIFGets()); + } + else + { + GInt32 nValue; + + if( VSIFReadL( &nValue, 4, 1, fp ) != 1 ) + return -1; + CPL_MSBPTR32( &nValue ); + + return nValue; + } +} + +/************************************************************************/ +/* ReadFloat() */ +/************************************************************************/ + +double RDataset::ReadFloat() + +{ + if( bASCII ) + { + return CPLAtof(ASCIIFGets()); + } + else + { + double dfValue; + + if( VSIFReadL( &dfValue, 8, 1, fp ) != 1 ) + return -1; + CPL_MSBPTR64( &dfValue ); + + return dfValue; + } +} + +/************************************************************************/ +/* ReadString() */ +/************************************************************************/ + +const char *RDataset::ReadString() + +{ + if( ReadInteger() % 256 != R_CHARSXP ) + { + osLastStringRead = ""; + return ""; + } + + size_t nLen = ReadInteger(); + + char *pachWrkBuf = (char *) VSIMalloc(nLen); + if (pachWrkBuf == NULL) + { + osLastStringRead = ""; + return ""; + } + if( VSIFReadL( pachWrkBuf, 1, nLen, fp ) != nLen ) + { + osLastStringRead = ""; + CPLFree( pachWrkBuf ); + return ""; + } + + if( bASCII ) + { + /* suck up newline and any extra junk */ + ASCIIFGets(); + } + + osLastStringRead.assign( pachWrkBuf, nLen ); + CPLFree( pachWrkBuf ); + + return osLastStringRead; +} + +/************************************************************************/ +/* ReadPair() */ +/************************************************************************/ + +int RDataset::ReadPair( CPLString &osObjName, int &nObjCode ) + +{ + nObjCode = ReadInteger(); + if( nObjCode == 254 ) + return TRUE; + + if( (nObjCode % 256) != R_LISTSXP ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Did not find expected object pair object." ); + return FALSE; + } + + int nPairCount = ReadInteger(); + if( nPairCount != 1 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Did not find expected pair count of 1." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Read the object name. */ +/* -------------------------------------------------------------------- */ + const char *pszName = ReadString(); + if( pszName == NULL || pszName[0] == '\0' ) + return FALSE; + + osObjName = pszName; + +/* -------------------------------------------------------------------- */ +/* Confirm that we have a numeric matrix object. */ +/* -------------------------------------------------------------------- */ + nObjCode = ReadInteger(); + + return TRUE; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int RDataset::Identify( GDALOpenInfo *poOpenInfo ) +{ + if( poOpenInfo->nHeaderBytes < 50 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* If the extension is .rda and the file type is gzip */ +/* compressed we assume it is a gziped R binary file. */ +/* -------------------------------------------------------------------- */ + if( memcmp(poOpenInfo->pabyHeader,"\037\213\b",3) == 0 + && EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"rda") ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Is this an ASCII or XDR binary R file? */ +/* -------------------------------------------------------------------- */ + if( !EQUALN((const char *)poOpenInfo->pabyHeader,"RDA2\nA\n",7) + && !EQUALN((const char *)poOpenInfo->pabyHeader,"RDX2\nX\n",7) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *RDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The R driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we need to route the file through the decompression */ +/* machinery? */ +/* -------------------------------------------------------------------- */ + CPLString osAdjustedFilename; + + if( memcmp(poOpenInfo->pabyHeader,"\037\213\b",3) == 0 ) + osAdjustedFilename = "/vsigzip/"; + + osAdjustedFilename += poOpenInfo->pszFilename; + +/* -------------------------------------------------------------------- */ +/* Establish this as a dataset and open the file using VSI*L. */ +/* -------------------------------------------------------------------- */ + RDataset *poDS = new RDataset(); + + poDS->fp = VSIFOpenL( osAdjustedFilename, "r" ); + if( poDS->fp == NULL ) + { + delete poDS; + return NULL; + } + + poDS->bASCII = EQUALN((const char *)poOpenInfo->pabyHeader,"RDA2\nA\n",7); + +/* -------------------------------------------------------------------- */ +/* Confirm this is a version 2 file. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( poDS->fp, 7, SEEK_SET ); + if( poDS->ReadInteger() != R_LISTSXP ) + { + delete poDS; + CPLError( CE_Failure, CPLE_OpenFailed, + "It appears %s is not a version 2 R object file after all!", + poOpenInfo->pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Skip the version values. */ +/* -------------------------------------------------------------------- */ + poDS->ReadInteger(); + poDS->ReadInteger(); + +/* -------------------------------------------------------------------- */ +/* Confirm we have a numeric vector object in a pairlist. */ +/* -------------------------------------------------------------------- */ + CPLString osObjName; + int nObjCode; + + if( !poDS->ReadPair( osObjName, nObjCode ) ) + { + delete poDS; + return NULL; + } + + if( nObjCode % 256 != R_REALSXP ) + { + delete poDS; + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to find expected numeric vector object." ); + return NULL; + } + + poDS->SetMetadataItem( "R_OBJECT_NAME", osObjName ); + +/* -------------------------------------------------------------------- */ +/* Read the count. */ +/* -------------------------------------------------------------------- */ + int nValueCount = poDS->ReadInteger(); + + poDS->nStartOfData = VSIFTellL( poDS->fp ); + +/* -------------------------------------------------------------------- */ +/* Read/Skip ahead to attributes. */ +/* -------------------------------------------------------------------- */ + if( poDS->bASCII ) + { + poDS->padfMatrixValues = (double*) VSIMalloc2( nValueCount, sizeof(double) ); + if (poDS->padfMatrixValues == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot allocate %d doubles", nValueCount); + delete poDS; + return NULL; + } + for( int iValue = 0; iValue < nValueCount; iValue++ ) + poDS->padfMatrixValues[iValue] = poDS->ReadFloat(); + } + else + { + VSIFSeekL( poDS->fp, 8 * nValueCount, SEEK_CUR ); + } + +/* -------------------------------------------------------------------- */ +/* Read pairs till we run out, trying to find a few items that */ +/* have special meaning to us. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = poDS->nRasterYSize = 0; + int nBandCount = 0; + + while( poDS->ReadPair( osObjName, nObjCode ) && nObjCode != 254 ) + { + if( osObjName == "dim" && nObjCode % 256 == R_INTSXP ) + { + int nCount = poDS->ReadInteger(); + if( nCount == 2 ) + { + poDS->nRasterXSize = poDS->ReadInteger(); + poDS->nRasterYSize = poDS->ReadInteger(); + nBandCount = 1; + } + else if( nCount == 3 ) + { + poDS->nRasterXSize = poDS->ReadInteger(); + poDS->nRasterYSize = poDS->ReadInteger(); + nBandCount = poDS->ReadInteger(); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "R 'dim' dimension wrong." ); + delete poDS; + return NULL; + } + } + else if( nObjCode % 256 == R_REALSXP ) + { + int nCount = poDS->ReadInteger(); + while( nCount-- > 0 && !VSIFEofL(poDS->fp) ) + poDS->ReadFloat(); + } + else if( nObjCode % 256 == R_INTSXP ) + { + int nCount = poDS->ReadInteger(); + while( nCount-- > 0 && !VSIFEofL(poDS->fp) ) + poDS->ReadInteger(); + } + else if( nObjCode % 256 == R_STRSXP ) + { + int nCount = poDS->ReadInteger(); + while( nCount-- > 0 && !VSIFEofL(poDS->fp) ) + poDS->ReadString(); + } + else if( nObjCode % 256 == R_CHARSXP ) + { + poDS->ReadString(); + } + } + + if( poDS->nRasterXSize == 0 ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find dim dimension information for R dataset." ); + return NULL; + } + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || + !GDALCheckBandCount(nBandCount, TRUE)) + { + delete poDS; + return NULL; + } + + if( nValueCount + < ((GIntBig) nBandCount) * poDS->nRasterXSize * poDS->nRasterYSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Not enough pixel data." ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the raster band object(s). */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterBand *poBand; + + if( poDS->bASCII ) + poBand = new RRasterBand( poDS, iBand+1, + poDS->padfMatrixValues + iBand * poDS->nRasterXSize * poDS->nRasterYSize ); + else + poBand = new RawRasterBand( poDS, iBand+1, poDS->fp, + poDS->nStartOfData + + poDS->nRasterXSize*poDS->nRasterYSize*8*iBand, + 8, poDS->nRasterXSize * 8, + GDT_Float64, !CPL_IS_LSB, + TRUE, FALSE ); + + poDS->SetBand( iBand+1, poBand ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_R() */ +/************************************************************************/ + +void GDALRegister_R() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "R" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "R" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "R Object Data Store" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_r.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "rda" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Float32" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = RDataset::Open; + poDriver->pfnIdentify = RDataset::Identify; + poDriver->pfnCreateCopy = RCreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/rasdaman/GNUmakefile b/bazaar/plugin/gdal/frmts/rasdaman/GNUmakefile new file mode 100644 index 000000000..f936c2c3b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rasdaman/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = rasdamandataset.o + +CPPFLAGS := $(CPPFLAGS) $(RASDAMAN_INC) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/rasdaman/frmt_rasdaman.html b/bazaar/plugin/gdal/frmts/rasdaman/frmt_rasdaman.html new file mode 100644 index 000000000..ae966ff63 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rasdaman/frmt_rasdaman.html @@ -0,0 +1,54 @@ + + +Rasdaman GDAL driver + + + + +

    Rasdaman GDAL driver

    + +

    Rasdaman is a raster database middleware offering an SQL-style query +language on multi-dimensional arrays of unlimited size, stored in a +relational database. See www.rasdaman.org +for the open-source code, documentation, etc. Currently rasdaman is under +consideration for OSGeo incubation.

    + +

    In our driver implementation, GDAL connects to rasdaman by defining a +query template which is instantiated with the concrete subsetting box +upon every access. This allows delivering 2-D cutouts from n-D data sets +(such as hyperspectral satellite time series, multi-variable climate +simulation data, ocean model data, etc.). In particular, virtual imagery +can be offered which is derived on demand from ground truth data. Some +more technical details are given below.

    + +

    The connect string syntax follows the WKT Raster pattern and goes +like this:
    + rasdaman: + query='select a[$x_lo:$x_hi,$y_lo:$y_hi] from MyImages as a' + [tileXSize=1024] [tileYSize=1024] + [host='localhost'] [port=7001] [database='RASBASE'] + [user='rasguest'] [password='rasguest'] + +

    The rasdaman query language (rasql) string in this case only performs +subsetting. Upon image access by GDAL, the $ parameters are substituted +by the concrete bounding box computed from the input tile coordinates.

    + +

    However, the query provided can include any kind of processing, as long +as it returns something 2-D. For example, this determines the average of +red and near-infrared pixels from the oldest image time series:
    + query='select ( a.red+a.nir ) /2 [$x_lo:$x_hi,$y_lo:$y_hi, 0 ] +from SatStack as a'

    + +

    Currently there is no support for reading or writing georeferencing +information.

    + +See Also: +

    + +

    + + + + diff --git a/bazaar/plugin/gdal/frmts/rasdaman/makefile.vc b/bazaar/plugin/gdal/frmts/rasdaman/makefile.vc new file mode 100644 index 000000000..7ebfcf6ef --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rasdaman/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = rasdamandataset.obj + +EXTRAFLAGS = -I..\iso8211 + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/rasdaman/rasdamandataset.cpp b/bazaar/plugin/gdal/frmts/rasdaman/rasdamandataset.cpp new file mode 100644 index 000000000..3c2efad24 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rasdaman/rasdamandataset.cpp @@ -0,0 +1,734 @@ +/****************************************************************************** + * $Id: rasdamandataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * Project: rasdaman Driver + * Purpose: Implement Rasdaman GDAL driver + * Author: Constantin Jucovschi, jucovschi@yahoo.com + * + ****************************************************************************** + * Copyright (c) 2010, Constantin Jucovschi + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "regex.h" +#include +#include +#include + + +#define __EXECUTABLE__ +#define EARLY_TEMPLATE +#include "raslib/template_inst.hh" +#include "raslib/structuretype.hh" +#include "raslib/type.hh" + +#include "rasodmg/database.hh" + +CPL_CVSID("$Id: rasdamandataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + + +CPL_C_START +void GDALRegister_RASDAMAN(void); +CPL_C_END + + +class Subset +{ +public: + Subset(int x_lo, int x_hi, int y_lo, int y_hi) + : m_x_lo(x_lo), m_x_hi(x_hi), m_y_lo(y_lo), m_y_hi(y_hi) + {} + + bool operator < (const Subset& rhs) const { + if (m_x_lo < rhs.m_x_lo || m_x_hi < rhs.m_x_hi + || m_y_lo < rhs.m_y_lo || m_y_hi < rhs.m_y_hi) { + + return true; + } + return false; + } + + bool contains(const Subset& other) const { + return m_x_lo <= other.m_x_lo && m_x_hi >= other.m_x_hi + && m_y_lo <= other.m_y_lo && m_y_hi >= other.m_y_hi; + } + + bool within(const Subset& other) const { + return other.contains(*this); + } + + void operator = (const Subset& rhs) { + m_x_lo = rhs.m_x_lo; + m_x_hi = rhs.m_x_hi; + m_y_lo = rhs.m_y_lo; + m_y_hi = rhs.m_y_hi; + } + + int x_lo() const { return m_x_lo; } + int x_hi() const { return m_x_hi; } + int y_lo() const { return m_y_lo; } + int y_hi() const { return m_y_hi; } + +private: + int m_x_lo; + int m_x_hi; + int m_y_lo; + int m_y_hi; +}; + + +/************************************************************************/ +/* ==================================================================== */ +/* RasdamanDataset */ +/* ==================================================================== */ +/************************************************************************/ + +typedef std::map > ArrayCache; + +class RasdamanRasterBand; +static CPLString getQuery(const char *templateString, const char* x_lo, const char* x_hi, const char* y_lo, const char* y_hi); + +class RasdamanDataset : public GDALPamDataset +{ + friend class RasdamanRasterBand; + +public: + RasdamanDataset(const char*, int, const char*, const char*, const char*); + ~RasdamanDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + +protected: + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + +private: + + ArrayCache m_array_cache; + + r_Ref& request_array(int x_lo, int x_hi, int y_lo, int y_hi, int& offsetX, int& offsetY); + r_Ref& request_array(const Subset&, int& offsetX, int& offsetY); + + void clear_array_cache(); + + r_Set execute(const char* string); + + void getTypes(const r_Base_Type* baseType, int &counter, int pos); + void createBands(const char* queryString); + + r_Database database; + r_Transaction transaction; + + CPLString queryParam; + CPLString host; + int port; + CPLString username; + CPLString userpassword; + CPLString databasename; + int xPos; + int yPos; + int tileXSize; + int tileYSize; +}; + +/************************************************************************/ +/* RasdamanDataset() */ +/************************************************************************/ + +RasdamanDataset::RasdamanDataset(const char* _host, int _port, const char* _username, + const char* _userpassword, const char* _databasename) + : host(_host), port(_port), username(_username), userpassword(_userpassword), + databasename(_databasename) +{ + database.set_servername(host, port); + database.set_useridentification(username, userpassword); + database.open(databasename); +} + +/************************************************************************/ +/* ~RasdamanDataset() */ +/************************************************************************/ + +RasdamanDataset::~RasdamanDataset() +{ + if (transaction.get_status() == r_Transaction::active) { + transaction.commit(); + } + database.close(); + FlushCache(); +} + + +CPLErr RasdamanDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) +{ + if (eRWFlag != GF_Read) { + CPLError(CE_Failure, CPLE_NoWriteAccess, "Write support is not implemented."); + return CE_Failure; + } + + transaction.begin(r_Transaction::read_only); + + /* TODO: Setup database access/transaction */ + int dummyX, dummyY; + /* Cache the whole image region */ + CPLDebug("rasdaman", "Pre-caching region (%d, %d, %d, %d).", nXOff, nXOff + nXSize, nYOff, nYOff + nYSize); + request_array(nXOff, nXOff + nXSize, nYOff, nYOff + nYSize, dummyX, dummyY); + + CPLErr ret = GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, + nBufXSize, nBufYSize, eBufType, nBandCount, + panBandMap, nPixelSpace, nLineSpace, nBandSpace, + psExtraArg); + + transaction.commit(); + + /* Clear the cache */ + clear_array_cache(); + + return ret; +} + + +r_Ref& RasdamanDataset::request_array(int x_lo, int x_hi, int y_lo, int y_hi, int& offsetX, int& offsetY) +{ + return request_array(Subset(x_lo, x_hi, y_lo, y_hi), offsetX, offsetY); +}; + +r_Ref& RasdamanDataset::request_array(const Subset& subset, int& offsetX, int& offsetY) +{ + // set the offsets to 0 + offsetX = 0; offsetY = 0; + + // check whether or not the subset was already requested + ArrayCache::iterator it = m_array_cache.find(subset); + if (it != m_array_cache.end()) { + CPLDebug("rasdaman", "Fetching tile (%d, %d, %d, %d) from cache.", + subset.x_lo(), subset.x_hi(), subset.y_lo(), subset.y_hi()); + + return it->second; + } + + // check if any tile contains the requested one + for(it = m_array_cache.begin(); it != m_array_cache.end(); ++it) { + if (it->first.contains(subset)) { + const Subset& existing = it->first; + + // TODO: check if offsets are correct + offsetX = subset.x_lo() - existing.x_lo(); + offsetY = subset.y_lo() - existing.y_lo(); + + CPLDebug("rasdaman", "Found matching tile (%d, %d, %d, %d) for requested tile (%d, %d, %d, %d). Offests are (%d, %d).", + existing.x_lo(), existing.x_hi(), existing.y_lo(), existing.y_hi(), + subset.x_lo(), subset.x_hi(), subset.y_lo(), subset.y_hi(), + offsetX, offsetY); + + + return it->second; + } + } + + if (transaction.get_status() != r_Transaction::active) { + transaction.begin(r_Transaction::read_only); + } + + CPLDebug("rasdaman", "Tile (%d, %d, %d, %d) not found in cache, requesting it.", + subset.x_lo(), subset.x_hi(), subset.y_lo(), subset.y_hi()); + + char x_lo[11], x_hi[11], y_lo[11], y_hi[11]; + + snprintf(x_lo, sizeof(x_lo), "%d", subset.x_lo()); + snprintf(x_hi, sizeof(x_hi), "%d", subset.x_hi()); + snprintf(y_lo, sizeof(y_lo), "%d", subset.y_lo()); + snprintf(y_hi, sizeof(y_hi), "%d", subset.y_hi()); + + CPLString queryString = getQuery(queryParam, x_lo, x_hi, y_lo, y_hi); + + r_Set result_set = execute(queryString); + if (result_set.get_element_type_schema()->type_id() == r_Type::MARRAYTYPE) { + // TODO: throw exception + } + + if (result_set.cardinality() != 1) { + // TODO: throw exception + } + + r_Ref result_array = r_Ref(*result_set.create_iterator()); + //std::auto_ptr ptr(new r_GMarray); + //r_GMarray* ptr_ = ptr.get(); + //(*ptr) = *result_array; + //std::pair inserted = m_array_cache.insert(ArrayCache::value_type(subset, ptr)); + + std::pair inserted = m_array_cache.insert(ArrayCache::value_type(subset, result_array)); + + return inserted.first->second;//*(ptr); +}; + + +void RasdamanDataset::clear_array_cache() { + m_array_cache.clear(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* RasdamanRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class RasdamanRasterBand : public GDALPamRasterBand +{ + friend class RasdamanDataset; + + int nRecordSize; + int typeOffset; + int typeSize; + +public: + + RasdamanRasterBand( RasdamanDataset *, int, GDALDataType type, int offset, int size, int nBlockXSize, int nBlockYSize ); + ~RasdamanRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* RasdamanParams */ +/************************************************************************/ + +/*struct RasdamanParams +{ + RasdamanParams(const char* dataset_info); + + void connect(const r_Database&); + + const char *query; + const char *host; + const int port; + const char *username; + const char *password; +};*/ + + +/************************************************************************/ +/* RasdamanRasterBand() */ +/************************************************************************/ + +RasdamanRasterBand::RasdamanRasterBand( RasdamanDataset *poDS, int nBand, GDALDataType type, int offset, int size, int nBlockXSize, int nBlockYSize ) +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = type; + typeSize = size; + typeOffset = offset; + + this->nBlockXSize = nBlockXSize; + this->nBlockYSize = nBlockYSize; + + nRecordSize = nBlockXSize * nBlockYSize * typeSize; +} + +/************************************************************************/ +/* ~RasdamanRasterBand() */ +/************************************************************************/ + +RasdamanRasterBand::~RasdamanRasterBand() +{} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr RasdamanRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + RasdamanDataset *poGDS = (RasdamanDataset *) poDS; + + memset(pImage, 0, nRecordSize); + + try { + int x_lo = nBlockXOff * nBlockXSize, + x_hi = MIN(poGDS->nRasterXSize, (nBlockXOff + 1) * nBlockXSize), + y_lo = nBlockYOff * nBlockYSize, + y_hi = MIN(poGDS->nRasterYSize, (nBlockYOff + 1) * nBlockYSize), + offsetX = 0, offsetY = 0; + + r_Ref& gmdd = poGDS->request_array(x_lo, x_hi, y_lo, y_hi, offsetX, offsetY); + + int xPos = poGDS->xPos; + int yPos = poGDS->yPos; + + r_Minterval sp = gmdd->spatial_domain(); + r_Point extent = sp.get_extent(); + r_Point base = sp.get_origin(); + + int extentX = extent[xPos]; + int extentY = extent[yPos]; + + CPLDebug("rasdaman", "Extents (%d, %d).", extentX, extentY); + + r_Point access = base; + char *resultPtr; + + for(int y = y_lo; y < y_hi; ++y) { + for(int x = x_lo; x < x_hi; ++x) { + resultPtr = (char*)pImage + ((y - y_lo) * nBlockXSize + x - x_lo) * typeSize; + //resultPtr = (char*) pImage + access[xPos] = x;// base[xPos] + offsetX; TODO: check if required + access[yPos] = y;// base[yPos] + offsetY; + const char *data = (*gmdd)[access] + typeOffset; + memcpy(resultPtr, data, typeSize); + } + } + } + catch (r_Error error) { + CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what()); + return CPLGetLastErrorType(); + } + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* RasdamanDataset */ +/* ==================================================================== */ +/************************************************************************/ + +static CPLString getOption(const char *string, regmatch_t cMatch, const char* defaultValue) { + if (cMatch.rm_eo == -1 || cMatch.rm_so == -1) + return defaultValue; + char *result = new char[cMatch.rm_eo-cMatch.rm_so+1]; + strncpy(result, string + cMatch.rm_so, cMatch.rm_eo - cMatch.rm_so); + result[cMatch.rm_eo-cMatch.rm_so] = 0; + CPLString osResult = result; + delete[] result; + return osResult; +} + +static int getOption(const char *string, regmatch_t cMatch, int defaultValue) { + if (cMatch.rm_eo == -1 || cMatch.rm_so == -1) + return defaultValue; + char *result = new char[cMatch.rm_eo-cMatch.rm_so+1]; + strncpy(result, string + cMatch.rm_so, cMatch.rm_eo - cMatch.rm_so); + result[cMatch.rm_eo-cMatch.rm_so] = 0; + int nRet = atoi(result); + delete[] result; + return nRet; +} + +void replace(CPLString& str, const char *from, const char *to) { + if(strlen(from) == 0) + return; + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, strlen(from), to); + start_pos += strlen(to); // In case 'to' contains 'from', like replacing 'x' with 'yx' + } +} + + +static CPLString getQuery(const char *templateString, const char* x_lo, const char* x_hi, const char* y_lo, const char* y_hi) { + CPLString result(templateString); + + replace(result, "$x_lo", x_lo); + replace(result, "$x_hi", x_hi); + replace(result, "$y_lo", y_lo); + replace(result, "$y_hi", y_hi); + + return result; +} + +GDALDataType mapRasdamanTypesToGDAL(r_Type::r_Type_Id typeId) { + switch (typeId) { + case r_Type::ULONG: + return GDT_UInt32; + case r_Type::LONG: + return GDT_Int32; + case r_Type::SHORT: + return GDT_Int16; + case r_Type::USHORT: + return GDT_UInt16; + case r_Type::BOOL: + case r_Type::CHAR: + return GDT_Byte; + case r_Type::DOUBLE: + return GDT_Float64; + case r_Type::FLOAT: + return GDT_Float32; + case r_Type::COMPLEXTYPE1: + return GDT_CFloat32; + case r_Type::COMPLEXTYPE2: + return GDT_CFloat64; + default: + return GDT_Unknown; + } +} + +void RasdamanDataset::getTypes(const r_Base_Type* baseType, int &counter, int pos) { + if (baseType->isStructType()) { + r_Structure_Type* tp = (r_Structure_Type*) baseType; + int elem = tp->count_elements(); + for (int i = 0; i < elem; ++i) { + r_Attribute attr = (*tp)[i]; + getTypes(&attr.type_of(), counter, attr.global_offset()); + } + + } + if (baseType->isPrimitiveType()) { + r_Primitive_Type *primType = (r_Primitive_Type*)baseType; + r_Type::r_Type_Id typeId = primType->type_id(); + SetBand(counter, new RasdamanRasterBand(this, counter, mapRasdamanTypesToGDAL(typeId), pos, primType->size(), this->tileXSize, this->tileYSize)); + counter ++; + } +} + +void RasdamanDataset::createBands(const char* queryString) { + r_Set result_set; + r_OQL_Query query (queryString); + r_oql_execute (query, result_set); + if (result_set.get_element_type_schema()->type_id() == r_Type::MARRAYTYPE) { + r_Iterator iter = result_set.create_iterator(); + r_Ref gmdd = r_Ref(*iter); + const r_Base_Type* baseType = gmdd->get_base_type_schema(); + int counter = 1; + getTypes(baseType, counter, 0); + } +} + +r_Set RasdamanDataset::execute(const char* string) { + CPLDebug("rasdaman", "Executing query '%s'.", string); + r_Set result_set; + r_OQL_Query query(string); + r_oql_execute(query, result_set); + return result_set; +} + + +static int getExtent(const char *queryString, int &pos) { + r_Set result_set; + r_OQL_Query query (queryString); + r_oql_execute (query, result_set); + if (result_set.get_element_type_schema()->type_id() == r_Type::MINTERVALTYPE) { + r_Iterator iter = result_set.create_iterator(); + r_Ref interv = r_Ref(*iter); + r_Point extent = interv->get_extent(); + int dim = extent.dimension(); + int result = -1; + for (int i = 0; i < dim; ++i) { + if (extent[i] == 1) + continue; + if (result != -1) + return -1; + result = extent[i]; + pos = i; + } + if (result == -1) + return 1; + else + return result; + } else + return -1; +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *RasdamanDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + // buffer to communicate errors + char errbuffer[4096]; + + // fast checks if current module should handle the request + // check 1: the request is not on a existing file in the file system + if (poOpenInfo->fpL != NULL) { + return NULL; + } + // check 2: the request contains --collection + char* connString = poOpenInfo->pszFilename; + if (!EQUALN(connString, "rasdaman", 8)) { + return NULL; + } + + // regex for parsing options + regex_t optionRegEx; + // regex for parsing query + regex_t queryRegEx; + // array to store matching subexpressions + regmatch_t matches[10]; + +#define QUERY_POSITION 2 +#define SERVER_POSITION 3 +#define PORT_POSITION 4 +#define USERNAME_POSITION 5 +#define USERPASSWORD_POSITION 6 +#define DATABASE_POSITION 7 +#define TILEXSIZE_POSITION 8 +#define TILEYSIZE_POSITION 9 + + int result = regcomp(&optionRegEx, "^rasdaman:(query='([[:alnum:][:punct:] ]+)'|host='([[:alnum:][:punct:]]+)'|port=([0-9]+)|user='([[:alnum:]]+)'|password='([[:alnum:]]+)'|database='([[:alnum:]]+)'|tileXSize=([0-9]+)|tileYSize=([0-9]+)| )*", REG_EXTENDED); + + // should never happen + if (result != 0) { + regerror(result, &optionRegEx, errbuffer, 4096); + CPLError(CE_Failure, CPLE_AppDefined, "Internal error at compiling option parsing regex: %s", errbuffer); + return NULL; + } + + result = regcomp(&queryRegEx, "^select ([[:alnum:][:punct:] ]*) from ([[:alnum:][:punct:] ]*)$", REG_EXTENDED); + // should never happen + if (result != 0) { + regerror(result, &queryRegEx, errbuffer, 4096); + CPLError(CE_Failure, CPLE_AppDefined, "Internal error at compiling option parsing regex: %s", errbuffer); + return NULL; + } + + // executing option parsing regex on the connection string and checking if it succeeds + result = regexec(&optionRegEx, connString, 10, matches, 0); + if (result != 0) { + regerror(result, &optionRegEx, errbuffer, 4096); + CPLError(CE_Failure, CPLE_AppDefined, "Parsing opening parameters failed with error: %s", errbuffer); + regfree(&optionRegEx); + regfree(&queryRegEx); + return NULL; + } + + regfree(&optionRegEx); + + + // checking if the whole expressions was matches, if not give an error where + // the matching stopped and exit + if (size_t(matches[0].rm_eo) < strlen(connString)) { + CPLError(CE_Failure, CPLE_AppDefined, "Parsing opening parameters failed with error: %s", connString + matches[0].rm_eo); + regfree(&queryRegEx); + return NULL; + } + + CPLString queryParam = getOption(connString, matches[QUERY_POSITION], (const char*)NULL); + CPLString host = getOption(connString, matches[SERVER_POSITION], "localhost"); + int port = getOption(connString, matches[PORT_POSITION], 7001); + CPLString username = getOption(connString, matches[USERNAME_POSITION], "rasguest"); + CPLString userpassword = getOption(connString, matches[USERPASSWORD_POSITION], "rasguest"); + CPLString databasename = getOption(connString, matches[DATABASE_POSITION], "RASBASE"); + int tileXSize = getOption(connString, matches[TILEXSIZE_POSITION], 1024); + int tileYSize = getOption(connString, matches[TILEYSIZE_POSITION], 1024); + + result = regexec(&queryRegEx, queryParam, 10, matches, 0); + if (result != 0) { + regerror(result, &queryRegEx, errbuffer, 4096); + CPLError(CE_Failure, CPLE_AppDefined, "Parsing query parameter failed with error: %s", errbuffer); + regfree(&queryRegEx); + return NULL; + } + + regfree(&queryRegEx); + + CPLString osQueryString = "select sdom("; + osQueryString += getOption(queryParam, matches[1], ""); + osQueryString += ") from "; + osQueryString += getOption(queryParam, matches[2], ""); + + CPLDebug("rasdaman", "osQueryString: %s", osQueryString.c_str()); + + CPLString queryX = getQuery(osQueryString, "*", "*", "0", "0"); + CPLString queryY = getQuery(osQueryString, "0", "0", "*", "*"); + CPLString queryUnit = getQuery(queryParam, "0", "0", "0", "0"); + + CPLDebug("rasdaman", "queryX: %s", queryX.c_str()); + CPLDebug("rasdaman", "queryY: %s", queryY.c_str()); + CPLDebug("rasdaman", "queryUnit: %s", queryUnit.c_str()); + + RasdamanDataset *rasDataset = NULL; + try { + rasDataset = new RasdamanDataset(host, port, username, userpassword, databasename); + //getMyExtent(osQueryString, posX, sizeX, posY, sizeY); + + r_Transaction transaction; + transaction.begin(r_Transaction::read_only); + + int dimX = getExtent(queryX, rasDataset->xPos); + int dimY = getExtent(queryY, rasDataset->yPos); + rasDataset->nRasterXSize = dimX; + rasDataset->nRasterYSize = dimY; + rasDataset->tileXSize = tileXSize; + rasDataset->tileYSize = tileYSize; + rasDataset->createBands(queryUnit); + + transaction.commit(); + + rasDataset->queryParam = queryParam; + rasDataset->host = host; + rasDataset->port = port; + rasDataset->username = username; + rasDataset->userpassword = userpassword; + rasDataset->databasename = databasename; + + return rasDataset; + } catch (r_Error error) { + CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what()); + delete rasDataset; + return NULL; + } + + + return rasDataset; +} + +/************************************************************************/ +/* GDALRegister_RASDAMAN() */ +/************************************************************************/ + +extern void GDALRegister_RASDAMAN() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "RASDAMAN" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "RASDAMAN" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "RASDAMAN" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_rasdaman.html" ); + + poDriver->pfnOpen = RasdamanDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/rasdaman/rasdamandataset.h b/bazaar/plugin/gdal/frmts/rasdaman/rasdamandataset.h new file mode 100644 index 000000000..c557eb15e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rasdaman/rasdamandataset.h @@ -0,0 +1,35 @@ +/****************************************************************************** + * + * Project: rasdaman Driver + * Purpose: Implement Rasdaman GDAL driver + * Author: Constantin Jucovschi, jucovschi@yahoo.com + * + ****************************************************************************** + * Copyright (c) 2010, Constantin Jucovschi + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef _RASDAMAN_DATASET_H_ +#define _RASDAMAN_DATASET_H_ +#include "gdal.h" + +void CPL_DLL CPL_STDCALL GDALRegister_RASDAMAN(); + +#endif diff --git a/bazaar/plugin/gdal/frmts/rasterlite/GNUmakefile b/bazaar/plugin/gdal/frmts/rasterlite/GNUmakefile new file mode 100644 index 000000000..1fbf5d30d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rasterlite/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = rasterlitedataset.o rasterlitecreatecopy.o rasterliteoverviews.o + +CPPFLAGS := $(CPPFLAGS) -I../../ogr + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/rasterlite/frmt_rasterlite.html b/bazaar/plugin/gdal/frmts/rasterlite/frmt_rasterlite.html new file mode 100644 index 000000000..95f22ad16 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rasterlite/frmt_rasterlite.html @@ -0,0 +1,213 @@ + + + + +Rasterlite - Rasters in SQLite DB + + + + +

    Rasterlite - Rasters in SQLite DB

    + +

    Starting with GDAL 1.7.0, the Rasterlite driver allows reading and creating Rasterlite databases.

    +

    + Those databases can be produced by the utilities of the + rasterlite distribution, + such as rasterlite_load, rasterlite_pyramids, ....
    + The driver supports reading grayscale, paletted and RGB images stored as GIF, PNG, TIFF or JPEG tiles. + The driver also supports reading overviews/pyramids, spatial reference system and spatial extent. +

    +

    Wavelet compressed tiles are not supported by default by GDAL, unless the EPSILON driver is compiled. +

    + +

    GDAL/OGR must be compiled with OGR SQLite driver support. For read support, linking against spatialite library is not required, but +recent enough sqlite3 library is needed to read rasterlite databases. rasterlite library is not required either.

    +

    For write support a new table, linking against spatialite library *is* required.

    + +

    + Although the Rasterlite documentation only mentions GIF, PNG, TIFF, JPEG and WAVELET (EPSILON driver) as + compression formats for tiles, the driver supports reading and writing internal tiles in any format handled by GDAL. + Furthermore, the Rasterlite driver also allow reading and writing as many bands and as many band types as supported by + the driver for the internal tiles. +

    + +

    Connexion string syntax in read mode

    + +

    Syntax: 'rasterlitedb_name' or 'RASTERLITE:rasterlitedb_name[,table=raster_table_prefix][,minx=minx_val,miny=miny_val,maxx=maxx_val,maxy=maxy_val][,level=level_number]

    + +

    where :

    +
      +
    • rasterlitedb_name is the filename of the rasterlite DB.
    • +
    • raster_table_prefix is the prefix of the raster table to open. For each raster, there are 2 correspondings SQLite tables, suffixed with _rasters and _metadata
    • +
    • minx_val,miny_val,maxx_val,maxy_val set a user-defined extent (expressed in coordinate system units) for the raster that can be different from the default extent.
    • +
    • level_number is the level of the pyramid/overview to open, 0 being the base pyramid.
    • +
    + +

    Creation issues

    + +

    The driver can create a new database if necessary, create a new raster table if necessary and copy a source dataset into the specified raster table.

    + +

    If data already exists in the raster table, the new data will be added. You can use the WIPE=YES creation options to erase existing data.

    + +

    The driver does not support updating a block in an existing raster table. It can only append new data.

    + +

    Syntax for the name of the output dataset: 'RASTERLITE:rasterlitedb_name,table=raster_table_prefix' or 'rasterlitedb_name'

    + +

    It is possible to specify only the DB name as in the later form, but only if the database does not already exists. In that case, the raster table name will be base on the DB name itself.

    + +

    Creation options

    + +
      +
    • WIPE (=NO by default): Set to YES to erase all prexisting data in the specified table

    • +
    • TILED (=YES by default) : Set to NO if the source dataset must be written as a single tile in the raster table

    • +
    • BLOCKXSIZE=n: Sets tile width, defaults to 256.

    • +
    • BLOCKYSIZE=n: Sets tile height, defaults to 256.

    • +
    • DRIVER=[GTiff/GIF/PNG/JPEG/EPSILON/...] : name of the GDAL driver to use for storing tiles. Defaults to GTiff

    • +
    • COMPRESS=[LZW/JPEG/DEFLATE/...] : (GTiff driver) name of the compression method

    • +
    • PHOTOMETRIC=[RGB/YCbCr/...] : (GTiff driver) photometric interpretation

    • +
    • QUALITY : (JPEG-compressed GTiff, JPEG and WEBP drivers) JPEG/WEBP quality 1-100. Defaults to 75

    • +
    • TARGET : (EPSILON driver) target size reduction as a percentage of the original (0-100). Defaults to 96.

    • +
    • FILTER : (EPSILON driver) Filter ID. Defaults to 'daub97lift'.

    • +
    + +

    Overviews

    + +

    The driver supports building (if the dataset is opened in update mode) and reading internal overviews.

    + +

    If no internal overview is detected, the driver will try using external overviews (.ovr files).

    + +

    Starting with GDAL 1.10, options can be used for internal overviews building. They can be specified with the +RASTERLITE_OVR_OPTIONS configuration option, as a comma separated list of the above creation options. See below +examples.

    + +

    Starting with GDAL 1.10, all resampling methods supported by GDAL overviews are available.

    + +

    Performance hints

    + +Some of the performance hints of the OGR SQLite driver apply. In particular setting the OGR_SQLITE_SYNCHRONOUS +configuration option to OFF when creating a dataset or adding overviews might increase performance on some +filesystems.

    + +After having added all the raster tables and building all the needed overview levels, it is advised to run : +

    +ogrinfo rasterlitedb.sqlite -sql "VACUUM"
    +
    +in order to optimize the database, and increase read performances afterwards. This is particularly true with big rasterlite datasets. +Note that the operation might take a long time.

    + +

    Examples:

    + +
      +
    • Accessing a rasterlite DB with a single raster table : +
      +$ gdalinfo rasterlitedb.sqlite -noct
      +
      +Output: +
      +Driver: Rasterlite/Rasterlite
      +Files: rasterlitedb.sqlite
      +Size is 7200, 7200
      +Coordinate System is:
      +GEOGCS["WGS 84",
      +    DATUM["WGS_1984",
      +        SPHEROID["WGS 84",6378137,298.257223563,
      +            AUTHORITY["EPSG","7030"]],
      +        AUTHORITY["EPSG","6326"]],
      +    PRIMEM["Greenwich",0,
      +        AUTHORITY["EPSG","8901"]],
      +    UNIT["degree",0.01745329251994328,
      +        AUTHORITY["EPSG","9122"]],
      +    AUTHORITY["EPSG","4326"]]
      +Origin = (-5.000000000000000,55.000000000000000)
      +Pixel Size = (0.002083333333333,-0.002083333333333)
      +Metadata:
      +  TILE_FORMAT=GIF
      +Image Structure Metadata:
      +  INTERLEAVE=PIXEL
      +Corner Coordinates:
      +Upper Left  (  -5.0000000,  55.0000000) (  5d 0'0.00"W, 55d 0'0.00"N)
      +Lower Left  (  -5.0000000,  40.0000000) (  5d 0'0.00"W, 40d 0'0.00"N)
      +Upper Right (  10.0000000,  55.0000000) ( 10d 0'0.00"E, 55d 0'0.00"N)
      +Lower Right (  10.0000000,  40.0000000) ( 10d 0'0.00"E, 40d 0'0.00"N)
      +Center      (   2.5000000,  47.5000000) (  2d30'0.00"E, 47d30'0.00"N)
      +Band 1 Block=480x480 Type=Byte, ColorInterp=Palette
      +  Color Table (RGB with 256 entries)
      +
      +
      +
    • + +
    • Listing a multi-raster table DB : +
      +$ gdalinfo multirasterdb.sqlite
      +
      +Output: +
      +Driver: Rasterlite/Rasterlite
      +Files:
      +Size is 512, 512
      +Coordinate System is `'
      +Subdatasets:
      +  SUBDATASET_1_NAME=RASTERLITE:multirasterdb.sqlite,table=raster1
      +  SUBDATASET_1_DESC=RASTERLITE:multirasterdb.sqlite,table=raster1
      +  SUBDATASET_2_NAME=RASTERLITE:multirasterdb.sqlite,table=raster2
      +  SUBDATASET_2_DESC=RASTERLITE:multirasterdb.sqlite,table=raster2
      +Corner Coordinates:
      +Upper Left  (    0.0,    0.0)
      +Lower Left  (    0.0,  512.0)
      +Upper Right (  512.0,    0.0)
      +Lower Right (  512.0,  512.0)
      +Center      (  256.0,  256.0)
      +
      +
      +
    • + +
    • Accessing a raster table within a multi-raster table DB: +
      +$ gdalinfo RASTERLITE:multirasterdb.sqlite,table=raster1
      +
      +
    • + +
    • Creating a new rasterlite DB with data encoded in JPEG tiles : +
      +$ gdal_translate -of Rasterlite source.tif RASTERLITE:my_db.sqlite,table=source -co DRIVER=JPEG
      +
      +
    • + +
    • Creating internal overviews : +
      +$ gdaladdo RASTERLITE:my_db.sqlite,table=source 2 4 8 16
      +
      +
    • + +
    • Cleaning internal overviews : +
      +$ gdaladdo -clean RASTERLITE:my_db.sqlite,table=source
      +
      +
    • + +
    • Creating external overviews in a .ovr file: +
      +$ gdaladdo -ro RASTERLITE:my_db.sqlite,table=source 2 4 8 16
      +
      +
    • + +
    • Creating internal overviews with options (GDAL 1.10 or later): +
      +$ gdaladdo RASTERLITE:my_db.sqlite,table=source 2 4 8 16 --config RASTERLITE_OVR_OPTIONS DRIVER=GTiff,COMPRESS=JPEG,PHOTOMETRIC=YCbCr
      +
      +
    • + +
    + +

    See Also:

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/rasterlite/makefile.vc b/bazaar/plugin/gdal/frmts/rasterlite/makefile.vc new file mode 100644 index 000000000..0719344d5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rasterlite/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = rasterlitedataset.obj rasterlitecreatecopy.obj rasterliteoverviews.obj + +EXTRAFLAGS = -I../../ogr + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/rasterlite/rasterlitecreatecopy.cpp b/bazaar/plugin/gdal/frmts/rasterlite/rasterlitecreatecopy.cpp new file mode 100644 index 000000000..64f5980f2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rasterlite/rasterlitecreatecopy.cpp @@ -0,0 +1,730 @@ +/****************************************************************************** + * $Id: rasterlitecreatecopy.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: GDAL Rasterlite driver + * Purpose: Implement GDAL Rasterlite support using OGR SQLite driver + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_string.h" +#include "ogr_api.h" +#include "ogr_srs_api.h" + +#include "rasterlitedataset.h" + +CPL_CVSID("$Id: rasterlitecreatecopy.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +/************************************************************************/ +/* RasterliteGetTileDriverOptions () */ +/************************************************************************/ + +static char** RasterliteAddTileDriverOptionsForDriver(char** papszOptions, + char** papszTileDriverOptions, + const char* pszOptionName, + const char* pszExpectedDriverName) +{ + const char* pszVal = CSLFetchNameValue(papszOptions, pszOptionName); + if (pszVal) + { + const char* pszDriverName = + CSLFetchNameValueDef(papszOptions, "DRIVER", "GTiff"); + if (EQUAL(pszDriverName, pszExpectedDriverName)) + { + papszTileDriverOptions = + CSLSetNameValue(papszTileDriverOptions, pszOptionName, pszVal); + } + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unexpected option '%s' for driver '%s'", + pszOptionName, pszDriverName); + } + } + return papszTileDriverOptions; +} + +char** RasterliteGetTileDriverOptions(char** papszOptions) +{ + char** papszTileDriverOptions = NULL; + + const char* pszDriverName = + CSLFetchNameValueDef(papszOptions, "DRIVER", "GTiff"); + + if (EQUAL(pszDriverName, "EPSILON")) + { + papszTileDriverOptions = CSLSetNameValue(papszTileDriverOptions, + "RASTERLITE_OUTPUT", "YES"); + } + + const char* pszQuality = CSLFetchNameValue(papszOptions, "QUALITY"); + if (pszQuality) + { + if (EQUAL(pszDriverName, "GTiff")) + { + papszTileDriverOptions = + CSLSetNameValue(papszTileDriverOptions, "JPEG_QUALITY", pszQuality); + } + else if (EQUAL(pszDriverName, "JPEG") || EQUAL(pszDriverName, "WEBP")) + { + papszTileDriverOptions = + CSLSetNameValue(papszTileDriverOptions, "QUALITY", pszQuality); + } + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unexpected option '%s' for driver '%s'", + "QUALITY", pszDriverName); + } + } + + papszTileDriverOptions = RasterliteAddTileDriverOptionsForDriver( + papszOptions, papszTileDriverOptions, "COMPRESS", "GTiff"); + papszTileDriverOptions = RasterliteAddTileDriverOptionsForDriver( + papszOptions, papszTileDriverOptions, "PHOTOMETRIC", "GTiff"); + papszTileDriverOptions = RasterliteAddTileDriverOptionsForDriver( + papszOptions, papszTileDriverOptions, "TARGET", "EPSILON"); + papszTileDriverOptions = RasterliteAddTileDriverOptionsForDriver( + papszOptions, papszTileDriverOptions, "FILTER", "EPSILON"); + + return papszTileDriverOptions; +} + +/************************************************************************/ +/* RasterliteInsertSRID () */ +/************************************************************************/ + +static int RasterliteInsertSRID(OGRDataSourceH hDS, const char* pszWKT) +{ + CPLString osSQL; + + int nAuthorityCode = 0; + CPLString osAuthorityName, osProjCS, osProj4; + if (pszWKT != NULL && strlen(pszWKT) != 0) + { + OGRSpatialReferenceH hSRS = OSRNewSpatialReference(pszWKT); + if (hSRS) + { + const char* pszAuthorityName = OSRGetAuthorityName(hSRS, NULL); + if (pszAuthorityName) osAuthorityName = pszAuthorityName; + + const char* pszProjCS = OSRGetAttrValue(hSRS, "PROJCS", 0); + if (pszProjCS) osProjCS = pszProjCS; + + const char* pszAuthorityCode = OSRGetAuthorityCode(hSRS, NULL); + if (pszAuthorityCode) nAuthorityCode = atoi(pszAuthorityCode); + + char *pszProj4 = NULL; + if( OSRExportToProj4( hSRS, &pszProj4 ) != OGRERR_NONE ) + pszProj4 = CPLStrdup(""); + osProj4 = pszProj4; + CPLFree(pszProj4); + } + OSRDestroySpatialReference(hSRS); + } + + int nSRSId = -1; + if (nAuthorityCode != 0 && osAuthorityName.size() != 0) + { + osSQL.Printf ("SELECT srid FROM spatial_ref_sys WHERE auth_srid = %d", nAuthorityCode); + OGRLayerH hLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + if (hLyr == NULL) + { + nSRSId = nAuthorityCode; + + if ( osProjCS.size() != 0 ) + osSQL.Printf( + "INSERT INTO spatial_ref_sys " + "(srid, auth_name, auth_srid, ref_sys_name, proj4text) " + "VALUES (%d, '%s', '%d', '%s', '%s')", + nSRSId, osAuthorityName.c_str(), + nAuthorityCode, osProjCS.c_str(), osProj4.c_str() ); + else + osSQL.Printf( + "INSERT INTO spatial_ref_sys " + "(srid, auth_name, auth_srid, proj4text) " + "VALUES (%d, '%s', '%d', '%s')", + nSRSId, osAuthorityName.c_str(), + nAuthorityCode, osProj4.c_str() ); + + + OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + } + else + { + OGRFeatureH hFeat = OGR_L_GetNextFeature(hLyr); + if (hFeat) + { + nSRSId = OGR_F_GetFieldAsInteger(hFeat, 0); + OGR_F_Destroy(hFeat); + } + OGR_DS_ReleaseResultSet(hDS, hLyr); + } + } + + return nSRSId; +} + +/************************************************************************/ +/* RasterliteCreateTables () */ +/************************************************************************/ + +OGRDataSourceH RasterliteCreateTables(OGRDataSourceH hDS, const char* pszTableName, + int nSRSId, int bWipeExistingData) +{ + CPLString osSQL; + + CPLString osDBName = OGR_DS_GetName(hDS); + + CPLString osRasterLayer; + osRasterLayer.Printf("%s_rasters", pszTableName); + + CPLString osMetatadataLayer; + osMetatadataLayer.Printf("%s_metadata", pszTableName); + + OGRLayerH hLyr; + + if (OGR_DS_GetLayerByName(hDS, osRasterLayer.c_str()) == NULL) + { +/* -------------------------------------------------------------------- */ +/* The table don't exist. Create them */ +/* -------------------------------------------------------------------- */ + + /* Create _rasters table */ + osSQL.Printf ("CREATE TABLE \"%s\" (" + "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," + "raster BLOB NOT NULL)", osRasterLayer.c_str()); + OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + + /* Create _metadata table */ + osSQL.Printf ("CREATE TABLE \"%s\" (" + "id INTEGER NOT NULL PRIMARY KEY," + "source_name TEXT NOT NULL," + "tile_id INTEGER NOT NULL," + "width INTEGER NOT NULL," + "height INTEGER NOT NULL," + "pixel_x_size DOUBLE NOT NULL," + "pixel_y_size DOUBLE NOT NULL)", + osMetatadataLayer.c_str()); + OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + + /* Add geometry column to _metadata table */ + osSQL.Printf("SELECT AddGeometryColumn('%s', 'geometry', %d, 'POLYGON', 2)", + osMetatadataLayer.c_str(), nSRSId); + if ((hLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL)) == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Check that the OGR SQLite driver has Spatialite support"); + OGRReleaseDataSource(hDS); + return NULL; + } + OGR_DS_ReleaseResultSet(hDS, hLyr); + + /* Create spatial index on _metadata table */ + osSQL.Printf("SELECT CreateSpatialIndex('%s', 'geometry')", + osMetatadataLayer.c_str()); + if ((hLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL)) == NULL) + { + OGRReleaseDataSource(hDS); + return NULL; + } + OGR_DS_ReleaseResultSet(hDS, hLyr); + + /* Create statistics tables */ + osSQL.Printf("SELECT UpdateLayerStatistics()"); + CPLPushErrorHandler(CPLQuietErrorHandler); + hLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + CPLPopErrorHandler(); + OGR_DS_ReleaseResultSet(hDS, hLyr); + + /* Re-open the DB to take into account the new tables*/ + OGRReleaseDataSource(hDS); + + hDS = RasterliteOpenSQLiteDB(osDBName.c_str(), GA_Update); + } + else + { + /* Check that the existing SRS is consistent with the one of the new */ + /* data to be inserted */ + osSQL.Printf("SELECT srid FROM geometry_columns WHERE f_table_name = '%s'", + osMetatadataLayer.c_str()); + hLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + if (hLyr) + { + int nExistingSRID = -1; + OGRFeatureH hFeat = OGR_L_GetNextFeature(hLyr); + if (hFeat) + { + nExistingSRID = OGR_F_GetFieldAsInteger(hFeat, 0); + OGR_F_Destroy(hFeat); + } + OGR_DS_ReleaseResultSet(hDS, hLyr); + + if (nExistingSRID != nSRSId) + { + if (bWipeExistingData) + { + osSQL.Printf("UPDATE geometry_columns SET srid = %d " + "WHERE f_table_name = \"%s\"", + nSRSId, osMetatadataLayer.c_str()); + OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + + /* Re-open the DB to take into account the change of SRS */ + OGRReleaseDataSource(hDS); + + hDS = RasterliteOpenSQLiteDB(osDBName.c_str(), GA_Update); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "New data has not the same SRS as existing data"); + OGRReleaseDataSource(hDS); + return NULL; + } + } + } + + if (bWipeExistingData) + { + osSQL.Printf("DELETE FROM \"%s\"", osRasterLayer.c_str()); + OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + + osSQL.Printf("DELETE FROM \"%s\"", osMetatadataLayer.c_str()); + OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + } + } + + return hDS; +} + +/************************************************************************/ +/* RasterliteCreateCopy () */ +/************************************************************************/ + +GDALDataset * +RasterliteCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + CPL_UNUSED int bStrict, + char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) +{ + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError(CE_Failure, CPLE_NotSupported, "nBands == 0"); + return NULL; + } + + const char* pszDriverName = CSLFetchNameValueDef(papszOptions, "DRIVER", "GTiff"); + if (EQUAL(pszDriverName, "MEM") || EQUAL(pszDriverName, "VRT")) + { + CPLError(CE_Failure, CPLE_AppDefined, "GDAL %s driver cannot be used as underlying driver", + pszDriverName); + return NULL; + } + + GDALDriverH hTileDriver = GDALGetDriverByName(pszDriverName); + if ( hTileDriver == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot load GDAL %s driver", pszDriverName); + return NULL; + } + + GDALDriverH hMemDriver = GDALGetDriverByName("MEM"); + if (hMemDriver == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot load GDAL MEM driver"); + return NULL; + } + + int nXSize = GDALGetRasterXSize(poSrcDS); + int nYSize = GDALGetRasterYSize(poSrcDS); + + double adfGeoTransform[6]; + if (poSrcDS->GetGeoTransform(adfGeoTransform) != CE_None) + { + adfGeoTransform[0] = 0; + adfGeoTransform[1] = 1; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = 0; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = -1; + } + else if (adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot use geotransform with rotational terms"); + return NULL; + } + + int bTiled = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "TILED", "YES")); + int nBlockXSize, nBlockYSize; + if (bTiled) + { + nBlockXSize = atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "256")); + nBlockYSize = atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "256")); + if (nBlockXSize < 64) nBlockXSize = 64; + else if (nBlockXSize > 4096) nBlockXSize = 4096; + if (nBlockYSize < 64) nBlockYSize = 64; + else if (nBlockYSize > 4096) nBlockYSize = 4096; + } + else + { + nBlockXSize = nXSize; + nBlockYSize = nYSize; + } + +/* -------------------------------------------------------------------- */ +/* Analyze arguments */ +/* -------------------------------------------------------------------- */ + + CPLString osDBName; + CPLString osTableName; + VSIStatBuf sBuf; + int bExists; + + /* Skip optionnal RASTERLITE: prefix */ + const char* pszFilenameWithoutPrefix = pszFilename; + if (EQUALN(pszFilename, "RASTERLITE:", 11)) + pszFilenameWithoutPrefix += 11; + + char** papszTokens = CSLTokenizeStringComplex( + pszFilenameWithoutPrefix, ",", FALSE, FALSE ); + int nTokens = CSLCount(papszTokens); + if (nTokens == 0) + { + osDBName = pszFilenameWithoutPrefix; + osTableName = CPLGetBasename(pszFilenameWithoutPrefix); + } + else + { + osDBName = papszTokens[0]; + + int i; + for(i=1;iGetProjectionRef()); + +/* -------------------------------------------------------------------- */ +/* Create or wipe existing tables */ +/* -------------------------------------------------------------------- */ + int bWipeExistingData = + CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "WIPE", "NO")); + + hDS = RasterliteCreateTables(hDS, osTableName.c_str(), + nSRSId, bWipeExistingData); + if (hDS == NULL) + return NULL; + + OGRLayerH hRasterLayer = OGR_DS_GetLayerByName(hDS, osRasterLayer.c_str()); + OGRLayerH hMetadataLayer = OGR_DS_GetLayerByName(hDS, osMetatadataLayer.c_str()); + if (hRasterLayer == NULL || hMetadataLayer == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find metadata and/or raster tables"); + OGRReleaseDataSource(hDS); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Check if there is overlapping data and warn the user */ +/* -------------------------------------------------------------------- */ + double minx = adfGeoTransform[0]; + double maxx = adfGeoTransform[0] + nXSize * adfGeoTransform[1]; + double maxy = adfGeoTransform[3]; + double miny = adfGeoTransform[3] + nYSize * adfGeoTransform[5]; + + osSQL.Printf("SELECT COUNT(geometry) FROM \"%s\" " + "WHERE rowid IN " + "(SELECT pkid FROM \"idx_%s_metadata_geometry\" " + "WHERE %s) AND %s", + osMetatadataLayer.c_str(), + osTableName.c_str(), + RasterliteGetSpatialFilterCond(minx, miny, maxx, maxy).c_str(), + RasterliteGetPixelSizeCond(adfGeoTransform[1], -adfGeoTransform[5]).c_str()); + + int nOverlappingGeoms = 0; + OGRLayerH hCountLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + if (hCountLyr) + { + OGRFeatureH hFeat = OGR_L_GetNextFeature(hCountLyr); + if (hFeat) + { + nOverlappingGeoms = OGR_F_GetFieldAsInteger(hFeat, 0); + OGR_F_Destroy(hFeat); + } + OGR_DS_ReleaseResultSet(hDS, hCountLyr); + } + + if (nOverlappingGeoms != 0) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Raster tiles already exist in the %s table within " + "the extent of the data to be inserted in", + osTableName.c_str()); + } + +/* -------------------------------------------------------------------- */ +/* Iterate over blocks to add data into raster and metadata tables */ +/* -------------------------------------------------------------------- */ + int nXBlocks = (nXSize + nBlockXSize - 1) / nBlockXSize; + int nYBlocks = (nYSize + nBlockYSize - 1) / nBlockYSize; + + GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + int nDataTypeSize = GDALGetDataTypeSize(eDataType) / 8; + GByte* pabyMEMDSBuffer = + (GByte*)VSIMalloc3(nBlockXSize, nBlockYSize, nBands * nDataTypeSize); + if (pabyMEMDSBuffer == NULL) + { + OGRReleaseDataSource(hDS); + return NULL; + } + + CPLString osTempFileName; + osTempFileName.Printf("/vsimem/%p", hDS); + + int nTileId = 0; + int nBlocks = 0; + int nTotalBlocks = nXBlocks * nYBlocks; + + char** papszTileDriverOptions = RasterliteGetTileDriverOptions(papszOptions); + + OGR_DS_ExecuteSQL(hDS, "BEGIN", NULL, NULL); + + CPLErr eErr = CE_None; + int nBlockXOff, nBlockYOff; + for(nBlockYOff=0;eErr == CE_None && nBlockYOff nXSize) + nReqXSize = nXSize - nBlockXOff * nBlockXSize; + if ((nBlockYOff+1) * nBlockYSize > nYSize) + nReqYSize = nYSize - nBlockYOff * nBlockYSize; + + eErr = poSrcDS->RasterIO(GF_Read, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + nReqXSize, nReqYSize, + pabyMEMDSBuffer, nReqXSize, nReqYSize, + eDataType, nBands, NULL, + 0, 0, 0, NULL); + if (eErr != CE_None) + { + break; + } + + GDALDatasetH hMemDS = GDALCreate(hMemDriver, "MEM:::", + nReqXSize, nReqYSize, 0, + eDataType, NULL); + if (hMemDS == NULL) + { + eErr = CE_Failure; + break; + } + + int iBand; + for(iBand = 0; iBand < nBands; iBand ++) + { + char** papszMEMDSOptions = NULL; + char szTmp[64]; + memset(szTmp, 0, sizeof(szTmp)); + CPLPrintPointer(szTmp, + pabyMEMDSBuffer + iBand * nDataTypeSize * + nReqXSize * nReqYSize, sizeof(szTmp)); + papszMEMDSOptions = CSLSetNameValue(papszMEMDSOptions, "DATAPOINTER", szTmp); + GDALAddBand(hMemDS, eDataType, papszMEMDSOptions); + CSLDestroy(papszMEMDSOptions); + } + + GDALDatasetH hOutDS = GDALCreateCopy(hTileDriver, + osTempFileName.c_str(), hMemDS, FALSE, + papszTileDriverOptions, NULL, NULL); + + GDALClose(hMemDS); + if (hOutDS) + GDALClose(hOutDS); + else + { + eErr = CE_Failure; + break; + } + +/* -------------------------------------------------------------------- */ +/* Insert new entry into raster table */ +/* -------------------------------------------------------------------- */ + + vsi_l_offset nDataLength = 0; + GByte *pabyData = VSIGetMemFileBuffer( osTempFileName.c_str(), + &nDataLength, FALSE); + + OGRFeatureH hFeat = OGR_F_Create( OGR_L_GetLayerDefn(hRasterLayer) ); + OGR_F_SetFieldBinary(hFeat, 0, (int)nDataLength, pabyData); + + OGR_L_CreateFeature(hRasterLayer, hFeat); + /* Query raster ID to set it as the ID of the associated metadata */ + int nRasterID = (int)OGR_F_GetFID(hFeat); + + OGR_F_Destroy(hFeat); + + VSIUnlink(osTempFileName.c_str()); + +/* -------------------------------------------------------------------- */ +/* Insert new entry into metadata table */ +/* -------------------------------------------------------------------- */ + + hFeat = OGR_F_Create( OGR_L_GetLayerDefn(hMetadataLayer) ); + OGR_F_SetFID(hFeat, nRasterID); + OGR_F_SetFieldString(hFeat, 0, GDALGetDescription(poSrcDS)); + OGR_F_SetFieldInteger(hFeat, 1, nTileId ++); + OGR_F_SetFieldInteger(hFeat, 2, nReqXSize); + OGR_F_SetFieldInteger(hFeat, 3, nReqYSize); + OGR_F_SetFieldDouble(hFeat, 4, adfGeoTransform[1]); + OGR_F_SetFieldDouble(hFeat, 5, -adfGeoTransform[5]); + + minx = adfGeoTransform[0] + + (nBlockXSize * nBlockXOff) * adfGeoTransform[1]; + maxx = adfGeoTransform[0] + + (nBlockXSize * nBlockXOff + nReqXSize) * adfGeoTransform[1]; + maxy = adfGeoTransform[3] + + (nBlockYSize * nBlockYOff) * adfGeoTransform[5]; + miny = adfGeoTransform[3] + + (nBlockYSize * nBlockYOff + nReqYSize) * adfGeoTransform[5]; + + OGRGeometryH hRectangle = OGR_G_CreateGeometry(wkbPolygon); + OGRGeometryH hLinearRing = OGR_G_CreateGeometry(wkbLinearRing); + OGR_G_AddPoint_2D(hLinearRing, minx, miny); + OGR_G_AddPoint_2D(hLinearRing, minx, maxy); + OGR_G_AddPoint_2D(hLinearRing, maxx, maxy); + OGR_G_AddPoint_2D(hLinearRing, maxx, miny); + OGR_G_AddPoint_2D(hLinearRing, minx, miny); + OGR_G_AddGeometryDirectly(hRectangle, hLinearRing); + + OGR_F_SetGeometryDirectly(hFeat, hRectangle); + + OGR_L_CreateFeature(hMetadataLayer, hFeat); + OGR_F_Destroy(hFeat); + + nBlocks++; + if (pfnProgress && !pfnProgress(1.0 * nBlocks / nTotalBlocks, + NULL, pProgressData)) + eErr = CE_Failure; + } + } + + if (eErr == CE_None) + OGR_DS_ExecuteSQL(hDS, "COMMIT", NULL, NULL); + else + OGR_DS_ExecuteSQL(hDS, "ROLLBACK", NULL, NULL); + + CSLDestroy(papszTileDriverOptions); + + VSIFree(pabyMEMDSBuffer); + + OGRReleaseDataSource(hDS); + + return (GDALDataset*) GDALOpen(pszFilename, GA_Update); +} + +/************************************************************************/ +/* RasterliteDelete () */ +/************************************************************************/ + +CPLErr RasterliteDelete(CPL_UNUSED const char* pszFilename) +{ + return CE_None; +} diff --git a/bazaar/plugin/gdal/frmts/rasterlite/rasterlitedataset.cpp b/bazaar/plugin/gdal/frmts/rasterlite/rasterlitedataset.cpp new file mode 100644 index 000000000..90f691367 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rasterlite/rasterlitedataset.cpp @@ -0,0 +1,1443 @@ +/****************************************************************************** + * $Id: rasterlitedataset.cpp 29265 2015-05-29 10:49:34Z rouault $ + * + * Project: GDAL Rasterlite driver + * Purpose: Implement GDAL Rasterlite support using OGR SQLite driver + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_frmts.h" +#include "cpl_string.h" +#include "ogr_api.h" +#include "ogr_srs_api.h" + +#include "rasterlitedataset.h" + +CPL_CVSID("$Id: rasterlitedataset.cpp 29265 2015-05-29 10:49:34Z rouault $"); + + +/************************************************************************/ +/* RasterliteOpenSQLiteDB() */ +/************************************************************************/ + +OGRDataSourceH RasterliteOpenSQLiteDB(const char* pszFilename, + GDALAccess eAccess) +{ + const char* const apszAllowedDrivers[] = { "SQLITE", NULL }; + return (OGRDataSourceH)GDALOpenEx(pszFilename, + GDAL_OF_VECTOR | + ((eAccess == GA_Update) ? GDAL_OF_UPDATE : 0), + apszAllowedDrivers, NULL, NULL); +} + +/************************************************************************/ +/* RasterliteGetPixelSizeCond() */ +/************************************************************************/ + +CPLString RasterliteGetPixelSizeCond(double dfPixelXSize, + double dfPixelYSize, + const char* pszTablePrefixWithDot) +{ + CPLString osCond; + osCond.Printf("((%spixel_x_size >= %s AND %spixel_x_size <= %s) AND " + "(%spixel_y_size >= %s AND %spixel_y_size <= %s))", + pszTablePrefixWithDot, + CPLString().FormatC(dfPixelXSize - 1e-15,"%.15f").c_str(), + pszTablePrefixWithDot, + CPLString().FormatC(dfPixelXSize + 1e-15,"%.15f").c_str(), + pszTablePrefixWithDot, + CPLString().FormatC(dfPixelYSize - 1e-15,"%.15f").c_str(), + pszTablePrefixWithDot, + CPLString().FormatC(dfPixelYSize + 1e-15,"%.15f").c_str()); + return osCond; +} +/************************************************************************/ +/* RasterliteGetSpatialFilterCond() */ +/************************************************************************/ + +CPLString RasterliteGetSpatialFilterCond(double minx, double miny, + double maxx, double maxy) +{ + CPLString osCond; + osCond.Printf("(xmin < %s AND xmax > %s AND ymin < %s AND ymax > %s)", + CPLString().FormatC(maxx,"%.15f").c_str(), + CPLString().FormatC(minx,"%.15f").c_str(), + CPLString().FormatC(maxy,"%.15f").c_str(), + CPLString().FormatC(miny,"%.15f").c_str()); + return osCond; +} + +/************************************************************************/ +/* RasterliteBand() */ +/************************************************************************/ + +RasterliteBand::RasterliteBand(RasterliteDataset* poDS, int nBand, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize) +{ + this->poDS = poDS; + this->nBand = nBand; + this->eDataType = eDataType; + this->nBlockXSize = nBlockXSize; + this->nBlockYSize = nBlockYSize; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +//#define RASTERLITE_DEBUG + +CPLErr RasterliteBand::IReadBlock( int nBlockXOff, int nBlockYOff, void * pImage) +{ + RasterliteDataset* poGDS = (RasterliteDataset*) poDS; + + double minx = poGDS->adfGeoTransform[0] + + nBlockXOff * nBlockXSize * poGDS->adfGeoTransform[1]; + double maxx = poGDS->adfGeoTransform[0] + + (nBlockXOff + 1) * nBlockXSize * poGDS->adfGeoTransform[1]; + double maxy = poGDS->adfGeoTransform[3] + + nBlockYOff * nBlockYSize * poGDS->adfGeoTransform[5]; + double miny = poGDS->adfGeoTransform[3] + + (nBlockYOff + 1) * nBlockYSize * poGDS->adfGeoTransform[5]; + int nDataTypeSize = GDALGetDataTypeSize(eDataType) / 8; + +#ifdef RASTERLITE_DEBUG + if (nBand == 1) + { + printf("nBlockXOff = %d, nBlockYOff = %d, nBlockXSize = %d, nBlockYSize = %d\n" + "minx=%.15f miny=%.15f maxx=%.15f maxy=%.15f\n", + nBlockXOff, nBlockYOff, nBlockXSize, nBlockYSize, minx, miny, maxx, maxy); + } +#endif + + CPLString osSQL; + osSQL.Printf("SELECT m.geometry, r.raster, m.id, m.width, m.height FROM \"%s_metadata\" AS m, " + "\"%s_rasters\" AS r WHERE m.rowid IN " + "(SELECT pkid FROM \"idx_%s_metadata_geometry\" " + "WHERE %s) AND %s AND r.id = m.id", + poGDS->osTableName.c_str(), + poGDS->osTableName.c_str(), + poGDS->osTableName.c_str(), + RasterliteGetSpatialFilterCond(minx, miny, maxx, maxy).c_str(), + RasterliteGetPixelSizeCond(poGDS->adfGeoTransform[1], -poGDS->adfGeoTransform[5], "m.").c_str()); + + OGRLayerH hSQLLyr = OGR_DS_ExecuteSQL(poGDS->hDS, osSQL.c_str(), NULL, NULL); + if (hSQLLyr == NULL) + { + memset(pImage, 0, nBlockXSize * nBlockYSize * nDataTypeSize); + return CE_None; + } + + CPLString osMemFileName; + osMemFileName.Printf("/vsimem/%p", this); + + int bHasFoundTile = FALSE; + int bHasMemsetTile = FALSE; + +#ifdef RASTERLITE_DEBUG + if (nBand == 1) + { + printf("nTiles = %d\n", OGR_L_GetFeatureCount(hSQLLyr, TRUE)); + } +#endif + + OGRFeatureH hFeat; + while( (hFeat = OGR_L_GetNextFeature(hSQLLyr)) != NULL ) + { + OGRGeometryH hGeom = OGR_F_GetGeometryRef(hFeat); + if (hGeom == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "null geometry found"); + OGR_F_Destroy(hFeat); + OGR_DS_ReleaseResultSet(poGDS->hDS, hSQLLyr); + return CE_Failure; + } + + OGREnvelope oEnvelope; + OGR_G_GetEnvelope(hGeom, &oEnvelope); + + int nTileId = OGR_F_GetFieldAsInteger(hFeat, 1); + int nTileXSize = OGR_F_GetFieldAsInteger(hFeat, 2); + int nTileYSize = OGR_F_GetFieldAsInteger(hFeat, 3); + + int nDstXOff = + (int)((oEnvelope.MinX - minx) / poGDS->adfGeoTransform[1] + 0.5); + int nDstYOff = + (int)((maxy - oEnvelope.MaxY) / (-poGDS->adfGeoTransform[5]) + 0.5); + + int nReqXSize = nTileXSize; + int nReqYSize = nTileYSize; + + int nSrcXOff, nSrcYOff; + + if (nDstXOff >= 0) + { + nSrcXOff = 0; + } + else + { + nSrcXOff = -nDstXOff; + nReqXSize += nDstXOff; + nDstXOff = 0; + } + + + if (nDstYOff >= 0) + { + nSrcYOff = 0; + } + else + { + nSrcYOff = -nDstYOff; + nReqYSize += nDstYOff; + nDstYOff = 0; + } + + if (nDstXOff + nReqXSize > nBlockXSize) + nReqXSize = nBlockXSize - nDstXOff; + + if (nDstYOff + nReqYSize > nBlockYSize) + nReqYSize = nBlockYSize - nDstYOff; + +#ifdef RASTERLITE_DEBUG + if (nBand == 1) + { + printf("id = %d, minx=%.15f miny=%.15f maxx=%.15f maxy=%.15f\n" + "nDstXOff = %d, nDstYOff = %d, nSrcXOff = %d, nSrcYOff = %d, " + "nReqXSize=%d, nReqYSize=%d\n", + nTileId, + oEnvelope.MinX, oEnvelope.MinY, oEnvelope.MaxX, oEnvelope.MaxY, + nDstXOff, nDstYOff, + nSrcXOff, nSrcYOff, nReqXSize, nReqYSize); + } +#endif + + if (nReqXSize > 0 && nReqYSize > 0 && + nSrcXOff < nTileXSize && nSrcYOff < nTileYSize) + { + +#ifdef RASTERLITE_DEBUG + if (nBand == 1) + { + printf("id = %d, selected !\n", nTileId); + } +#endif + int nDataSize = 0; + GByte* pabyData = OGR_F_GetFieldAsBinary(hFeat, 0, &nDataSize); + + VSILFILE * fp = VSIFileFromMemBuffer( osMemFileName.c_str(), pabyData, + nDataSize, FALSE); + VSIFCloseL(fp); + + GDALDatasetH hDSTile = GDALOpenEx(osMemFileName.c_str(), + GDAL_OF_RASTER | GDAL_OF_INTERNAL, + NULL, NULL, NULL); + int nTileBands = 0; + if (hDSTile && (nTileBands = GDALGetRasterCount(hDSTile)) == 0) + { + GDALClose(hDSTile); + hDSTile = NULL; + } + if (hDSTile == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Can't open tile %d", + nTileId); + } + + int nReqBand = 1; + if (nTileBands == poGDS->nBands) + nReqBand = nBand; + else if (eDataType == GDT_Byte && nTileBands == 1 && poGDS->nBands == 3) + nReqBand = 1; + else + { + GDALClose(hDSTile); + hDSTile = NULL; + } + + if (hDSTile) + { + CPLAssert(GDALGetRasterXSize(hDSTile) == nTileXSize); + CPLAssert(GDALGetRasterYSize(hDSTile) == nTileYSize); + + bHasFoundTile = TRUE; + + int bHasJustMemsetTileBand1 = FALSE; + + /* If the source tile doesn't fit the entire block size, then */ + /* we memset 0 before */ + if (!(nDstXOff == 0 && nDstYOff == 0 && + nReqXSize == nBlockXSize && nReqYSize == nBlockYSize) && + !bHasMemsetTile) + { + memset(pImage, 0, nBlockXSize * nBlockYSize * nDataTypeSize); + bHasMemsetTile = TRUE; + bHasJustMemsetTileBand1 = TRUE; + } + + GDALColorTable* poTileCT = + (GDALColorTable* )GDALGetRasterColorTable(GDALGetRasterBand(hDSTile, 1)); + unsigned char* pabyTranslationTable = NULL; + if (poGDS->nBands == 1 && poGDS->poCT != NULL && poTileCT != NULL) + { + pabyTranslationTable = + ((GDALRasterBand*)GDALGetRasterBand(hDSTile, 1))-> + GetIndexColorTranslationTo(this, NULL, NULL); + } + +/* -------------------------------------------------------------------- */ +/* Read tile data */ +/* -------------------------------------------------------------------- */ + GDALRasterIO(GDALGetRasterBand(hDSTile, nReqBand), GF_Read, + nSrcXOff, nSrcYOff, nReqXSize, nReqYSize, + ((char*) pImage) + (nDstXOff + nDstYOff * nBlockXSize) * nDataTypeSize, + nReqXSize, nReqYSize, + eDataType, nDataTypeSize, nBlockXSize * nDataTypeSize); + + if (eDataType == GDT_Byte && pabyTranslationTable) + { +/* -------------------------------------------------------------------- */ +/* Convert from tile CT to band CT */ +/* -------------------------------------------------------------------- */ + int i, j; + for(j=nDstYOff;jnBands == 3 && poTileCT != NULL) + { +/* -------------------------------------------------------------------- */ +/* Expand from PCT to RGB */ +/* -------------------------------------------------------------------- */ + int i, j; + GByte abyCT[256]; + int nEntries = MIN(256, poTileCT->GetColorEntryCount()); + for(i=0;iGetColorEntry(i); + if (nBand == 1) + abyCT[i] = (GByte)psEntry->c1; + else if (nBand == 2) + abyCT[i] = (GByte)psEntry->c2; + else + abyCT[i] = (GByte)psEntry->c3; + } + for(;i<256;i++) + abyCT[i] = 0; + + for(j=nDstYOff;jnBands > 1) + { + int iOtherBand; + for(iOtherBand=2;iOtherBand<=poGDS->nBands;iOtherBand++) + { + GDALRasterBlock *poBlock; + + poBlock = poGDS->GetRasterBand(iOtherBand)-> + GetLockedBlockRef(nBlockXOff,nBlockYOff, TRUE); + if (poBlock == NULL) + break; + + GByte* pabySrcBlock = (GByte *) poBlock->GetDataRef(); + if( pabySrcBlock == NULL ) + { + poBlock->DropLock(); + break; + } + + if (nTileBands == 1) + nReqBand = 1; + else + nReqBand = iOtherBand; + + if (bHasJustMemsetTileBand1) + memset(pabySrcBlock, 0, + nBlockXSize * nBlockYSize * nDataTypeSize); + +/* -------------------------------------------------------------------- */ +/* Read tile data */ +/* -------------------------------------------------------------------- */ + GDALRasterIO(GDALGetRasterBand(hDSTile, nReqBand), GF_Read, + nSrcXOff, nSrcYOff, nReqXSize, nReqYSize, + ((char*) pabySrcBlock) + + (nDstXOff + nDstYOff * nBlockXSize) * nDataTypeSize, + nReqXSize, nReqYSize, + eDataType, nDataTypeSize, nBlockXSize * nDataTypeSize); + + if (eDataType == GDT_Byte && nTileBands == 1 && + poGDS->nBands == 3 && poTileCT != NULL) + { +/* -------------------------------------------------------------------- */ +/* Expand from PCT to RGB */ +/* -------------------------------------------------------------------- */ + + int i, j; + GByte abyCT[256]; + int nEntries = MIN(256, poTileCT->GetColorEntryCount()); + for(i=0;iGetColorEntry(i); + if (iOtherBand == 1) + abyCT[i] = (GByte)psEntry->c1; + else if (iOtherBand == 2) + abyCT[i] = (GByte)psEntry->c2; + else + abyCT[i] = (GByte)psEntry->c3; + } + for(;i<256;i++) + abyCT[i] = 0; + + for(j=nDstYOff;jDropLock(); + } + + } + GDALClose(hDSTile); + } + + VSIUnlink(osMemFileName.c_str()); + } + else + { +#ifdef RASTERLITE_DEBUG + if (nBand == 1) + { + printf("id = %d, NOT selected !\n", nTileId); + } +#endif + } + + OGR_F_Destroy(hFeat); + } + + if (!bHasFoundTile) + { + memset(pImage, 0, nBlockXSize * nBlockYSize * nDataTypeSize); + } + + OGR_DS_ReleaseResultSet(poGDS->hDS, hSQLLyr); + +#ifdef RASTERLITE_DEBUG + if (nBand == 1) + printf("\n"); +#endif + + return CE_None; +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int RasterliteBand::GetOverviewCount() +{ + RasterliteDataset* poGDS = (RasterliteDataset*) poDS; + + if (poGDS->nLimitOvrCount >= 0) + return poGDS->nLimitOvrCount; + else if (poGDS->nResolutions > 1) + return poGDS->nResolutions - 1; + else + return GDALPamRasterBand::GetOverviewCount(); +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand* RasterliteBand::GetOverview(int nLevel) +{ + RasterliteDataset* poGDS = (RasterliteDataset*) poDS; + + if (poGDS->nLimitOvrCount >= 0) + { + if (nLevel < 0 || nLevel >= poGDS->nLimitOvrCount) + return NULL; + } + + if (poGDS->nResolutions == 1) + return GDALPamRasterBand::GetOverview(nLevel); + + if (nLevel < 0 || nLevel >= poGDS->nResolutions - 1) + return NULL; + + GDALDataset* poOvrDS = poGDS->papoOverviews[nLevel]; + if (poOvrDS) + return poOvrDS->GetRasterBand(nBand); + else + return NULL; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp RasterliteBand::GetColorInterpretation() +{ + RasterliteDataset* poGDS = (RasterliteDataset*) poDS; + if (poGDS->nBands == 1) + { + if (poGDS->poCT != NULL) + return GCI_PaletteIndex; + else + return GCI_GrayIndex; + } + else if (poGDS->nBands == 3) + { + if (nBand == 1) + return GCI_RedBand; + else if (nBand == 2) + return GCI_GreenBand; + else if (nBand == 3) + return GCI_BlueBand; + } + + return GCI_Undefined; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable* RasterliteBand::GetColorTable() +{ + RasterliteDataset* poGDS = (RasterliteDataset*) poDS; + if (poGDS->nBands == 1) + return poGDS->poCT; + else + return NULL; +} + +/************************************************************************/ +/* RasterliteDataset() */ +/************************************************************************/ + +RasterliteDataset::RasterliteDataset() +{ + nLimitOvrCount = -1; + bValidGeoTransform = FALSE; + bMustFree = FALSE; + nLevel = 0; + poMainDS = NULL; + nResolutions = 0; + padfXResolutions = NULL; + padfYResolutions = NULL; + pszSRS = NULL; + hDS = NULL; + papoOverviews = NULL; + papszMetadata = NULL; + papszSubDatasets = NULL; + papszImageStructure = + CSLAddString(NULL, "INTERLEAVE=PIXEL"); + poCT = NULL; + bCheckForExistingOverview = TRUE; +} + +/************************************************************************/ +/* RasterliteDataset() */ +/************************************************************************/ + +RasterliteDataset::RasterliteDataset(RasterliteDataset* poMainDS, int nLevel) +{ + nLimitOvrCount = -1; + bMustFree = FALSE; + this->nLevel = nLevel; + this->poMainDS = poMainDS; + nResolutions = poMainDS->nResolutions - nLevel; + padfXResolutions = poMainDS->padfXResolutions + nLevel; + padfYResolutions = poMainDS->padfYResolutions + nLevel; + pszSRS = poMainDS->pszSRS; + hDS = poMainDS->hDS; + papoOverviews = poMainDS->papoOverviews + nLevel; + papszMetadata = poMainDS->papszMetadata; + papszSubDatasets = poMainDS->papszSubDatasets; + papszImageStructure = poMainDS->papszImageStructure; + poCT = poMainDS->poCT; + bCheckForExistingOverview = TRUE; + + osTableName = poMainDS->osTableName; + osFileName = poMainDS->osFileName; + + nRasterXSize = (int)(poMainDS->nRasterXSize * + (poMainDS->padfXResolutions[0] / padfXResolutions[0]) + 0.5); + nRasterYSize = (int)(poMainDS->nRasterYSize * + (poMainDS->padfYResolutions[0] / padfYResolutions[0]) + 0.5); + + bValidGeoTransform = TRUE; + memcpy(adfGeoTransform, poMainDS->adfGeoTransform, 6 * sizeof(double)); + adfGeoTransform[1] = padfXResolutions[0]; + adfGeoTransform[5] = - padfYResolutions[0]; +} + +/************************************************************************/ +/* ~RasterliteDataset() */ +/************************************************************************/ + +RasterliteDataset::~RasterliteDataset() +{ + CloseDependentDatasets(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int RasterliteDataset::CloseDependentDatasets() +{ + int bRet = GDALPamDataset::CloseDependentDatasets(); + + if (poMainDS == NULL && !bMustFree) + { + CSLDestroy(papszMetadata); + papszMetadata = NULL; + CSLDestroy(papszSubDatasets); + papszSubDatasets = NULL; + CSLDestroy(papszImageStructure); + papszImageStructure = NULL; + CPLFree(pszSRS); + pszSRS = NULL; + + if (papoOverviews) + { + int i; + for(i=1;ibMustFree) + { + papoOverviews[i-1]->poMainDS = NULL; + } + delete papoOverviews[i-1]; + } + CPLFree(papoOverviews); + papoOverviews = NULL; + nResolutions = 0; + bRet = TRUE; + } + + if (hDS != NULL) + OGRReleaseDataSource(hDS); + hDS = NULL; + + CPLFree(padfXResolutions); + CPLFree(padfYResolutions); + padfXResolutions = padfYResolutions = NULL; + + delete poCT; + poCT = NULL; + } + else if (poMainDS != NULL && bMustFree) + { + poMainDS->papoOverviews[nLevel-1] = NULL; + delete poMainDS; + poMainDS = NULL; + bRet = TRUE; + } + + return bRet; +} + +/************************************************************************/ +/* AddSubDataset() */ +/************************************************************************/ + +void RasterliteDataset::AddSubDataset( const char* pszDSName) +{ + char szName[80]; + int nCount = CSLCount(papszSubDatasets ) / 2; + + sprintf( szName, "SUBDATASET_%d_NAME", nCount+1 ); + papszSubDatasets = + CSLSetNameValue( papszSubDatasets, szName, pszDSName); + + sprintf( szName, "SUBDATASET_%d_DESC", nCount+1 ); + papszSubDatasets = + CSLSetNameValue( papszSubDatasets, szName, pszDSName); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **RasterliteDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "SUBDATASETS", "IMAGE_STRUCTURE", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **RasterliteDataset::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain != NULL && EQUAL(pszDomain,"SUBDATASETS") ) + return papszSubDatasets; + + if( CSLCount(papszSubDatasets) < 2 && + pszDomain != NULL && EQUAL(pszDomain,"IMAGE_STRUCTURE") ) + return papszImageStructure; + + if ( pszDomain == NULL || EQUAL(pszDomain, "") ) + return papszMetadata; + + return GDALPamDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *RasterliteDataset::GetMetadataItem( const char *pszName, + const char *pszDomain ) +{ + if (pszDomain != NULL &&EQUAL(pszDomain,"OVERVIEWS") ) + { + if (nResolutions > 1 || CSLCount(papszSubDatasets) > 2) + return NULL; + else + { + osOvrFileName.Printf("%s_%s", osFileName.c_str(), osTableName.c_str()); + if (bCheckForExistingOverview == FALSE || + CPLCheckForFile((char*) osOvrFileName.c_str(), NULL)) + return osOvrFileName.c_str(); + else + return NULL; + } + } + return GDALPamDataset::GetMetadataItem(pszName, pszDomain); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr RasterliteDataset::GetGeoTransform( double* padfGeoTransform ) +{ + if (bValidGeoTransform) + { + memcpy(padfGeoTransform, adfGeoTransform, 6 * sizeof(double)); + return CE_None; + } + else + return CE_Failure; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* RasterliteDataset::GetProjectionRef() +{ + if (pszSRS) + return pszSRS; + else + return ""; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char** RasterliteDataset::GetFileList() +{ + char** papszFileList = NULL; + papszFileList = CSLAddString(papszFileList, osFileName); + return papszFileList; +} + +/************************************************************************/ +/* GetBlockParams() */ +/************************************************************************/ + +int RasterliteDataset::GetBlockParams(OGRLayerH hRasterLyr, int nLevel, + int* pnBands, GDALDataType* peDataType, + int* pnBlockXSize, int* pnBlockYSize) +{ + CPLString osSQL; + + osSQL.Printf("SELECT m.geometry, r.raster, m.id " + "FROM \"%s_metadata\" AS m, \"%s_rasters\" AS r " + "WHERE %s AND r.id = m.id", + osTableName.c_str(), osTableName.c_str(), + RasterliteGetPixelSizeCond(padfXResolutions[nLevel],padfYResolutions[nLevel], "m.").c_str()); + + OGRLayerH hSQLLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + if (hSQLLyr == NULL) + { + return FALSE; + } + + OGRFeatureH hFeat = OGR_L_GetNextFeature(hRasterLyr); + if (hFeat == NULL) + { + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + return FALSE; + } + + int nDataSize; + GByte* pabyData = OGR_F_GetFieldAsBinary(hFeat, 0, &nDataSize); + + if (nDataSize > 32 && + EQUALN((const char*)pabyData, "StartWaveletsImage$$", strlen("StartWaveletsImage$$"))) + { + if (GDALGetDriverByName("EPSILON") == NULL) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Rasterlite driver doesn't support WAVELET compressed images if EPSILON driver is not compiled"); + OGR_F_Destroy(hFeat); + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + return FALSE; + } + } + + CPLString osMemFileName; + osMemFileName.Printf("/vsimem/%p", this); + VSILFILE * fp = VSIFileFromMemBuffer( osMemFileName.c_str(), pabyData, + nDataSize, FALSE); + VSIFCloseL(fp); + + GDALDatasetH hDSTile = GDALOpen(osMemFileName.c_str(), GA_ReadOnly); + if (hDSTile) + { + *pnBands = GDALGetRasterCount(hDSTile); + if (*pnBands == 0) + { + GDALClose(hDSTile); + hDSTile = NULL; + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Can't open tile %d", + OGR_F_GetFieldAsInteger(hFeat, 1)); + } + + if (hDSTile) + { + int iBand; + *peDataType = GDALGetRasterDataType(GDALGetRasterBand(hDSTile, 1)); + + for(iBand=2;iBand<=*pnBands;iBand++) + { + if (*peDataType != GDALGetRasterDataType(GDALGetRasterBand(hDSTile, 1))) + { + CPLError(CE_Failure, CPLE_NotSupported, "Band types must be identical"); + GDALClose(hDSTile); + hDSTile = NULL; + goto end; + } + } + + *pnBlockXSize = GDALGetRasterXSize(hDSTile); + *pnBlockYSize = GDALGetRasterYSize(hDSTile); + if (CSLFindName(papszImageStructure, "COMPRESSION") == -1) + { + const char* pszCompression = + GDALGetMetadataItem(hDSTile, "COMPRESSION", "IMAGE_STRUCTURE"); + if (pszCompression != NULL && EQUAL(pszCompression, "JPEG")) + papszImageStructure = + CSLAddString(papszImageStructure, "COMPRESSION=JPEG"); + } + + if (CSLFindName(papszMetadata, "TILE_FORMAT") == -1) + { + papszMetadata = + CSLSetNameValue(papszMetadata, "TILE_FORMAT", + GDALGetDriverShortName(GDALGetDatasetDriver(hDSTile))); + } + + + if (*pnBands == 1 && this->poCT == NULL) + { + GDALColorTable* poCT = + (GDALColorTable*)GDALGetRasterColorTable(GDALGetRasterBand(hDSTile, 1)); + if (poCT) + this->poCT = poCT->Clone(); + } + + GDALClose(hDSTile); + } +end: + VSIUnlink(osMemFileName.c_str()); + + OGR_F_Destroy(hFeat); + + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + + return hDSTile != NULL; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int RasterliteDataset::Identify(GDALOpenInfo* poOpenInfo) +{ + if (!EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "MBTILES") && + !EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "GPKG") && + poOpenInfo->nHeaderBytes >= 1024 && + EQUALN((const char*)poOpenInfo->pabyHeader, "SQLite Format 3", 15)) + { + // Could be a SQLite/Spatialite file as well + return -1; + } + else if (EQUALN(poOpenInfo->pszFilename, "RASTERLITE:", 11)) + { + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset* RasterliteDataset::Open(GDALOpenInfo* poOpenInfo) +{ + CPLString osFileName; + CPLString osTableName; + char **papszTokens = NULL; + int nLevel = 0; + double minx = 0, miny = 0, maxx = 0, maxy = 0; + int bMinXSet = FALSE, bMinYSet = FALSE, bMaxXSet = FALSE, bMaxYSet = FALSE; + int nReqBands = 0; + + if( Identify(poOpenInfo) == FALSE ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Parse "file name" */ +/* -------------------------------------------------------------------- */ + if (poOpenInfo->nHeaderBytes >= 1024 && + EQUALN((const char*)poOpenInfo->pabyHeader, "SQLite Format 3", 15)) + { + osFileName = poOpenInfo->pszFilename; + } + else + { + papszTokens = CSLTokenizeStringComplex( + poOpenInfo->pszFilename + 11, ",", FALSE, FALSE ); + int nTokens = CSLCount(papszTokens); + if (nTokens == 0) + { + CSLDestroy(papszTokens); + return NULL; + } + + osFileName = papszTokens[0]; + + int i; + for(i=1;ieAccess); + CPLDebug("RASTERLITE", "SQLite DB Open"); + + RasterliteDataset* poDS = NULL; + + if (hDS == NULL) + goto end; + + if (strlen(osTableName) == 0) + { + int nCountSubdataset = 0; + int nLayers = OGR_DS_GetLayerCount(hDS); + int i; +/* -------------------------------------------------------------------- */ +/* Add raster layers as subdatasets */ +/* -------------------------------------------------------------------- */ + for(i=0;ipszFilename, "RASTERLITE:", 11)) + osSubdatasetName += "RASTERLITE:"; + osSubdatasetName += poOpenInfo->pszFilename; + osSubdatasetName += ",table="; + osSubdatasetName += pszShortName; + poDS->AddSubDataset(osSubdatasetName.c_str()); + + nCountSubdataset++; + } + + CPLFree(pszShortName); + } + } + + if (nCountSubdataset == 0) + { + goto end; + } + else if (nCountSubdataset != 1) + { + poDS->SetDescription( poOpenInfo->pszFilename ); + goto end; + } + +/* -------------------------------------------------------------------- */ +/* If just one subdataset, then open it */ +/* -------------------------------------------------------------------- */ + delete poDS; + poDS = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Build dataset */ +/* -------------------------------------------------------------------- */ + { + CPLString osMetadataTableName, osRasterTableName; + CPLString osSQL; + OGRLayerH hMetadataLyr, hRasterLyr, hRasterPyramidsLyr; + OGRLayerH hSQLLyr; + OGRFeatureH hFeat; + int i, nResolutions = 0; + int iBand, nBands, nBlockXSize, nBlockYSize; + GDALDataType eDataType; + + osMetadataTableName = osTableName; + osMetadataTableName += "_metadata"; + + hMetadataLyr = OGR_DS_GetLayerByName(hDS, osMetadataTableName.c_str()); + if (hMetadataLyr == NULL) + goto end; + + osRasterTableName = osTableName; + osRasterTableName += "_rasters"; + + hRasterLyr = OGR_DS_GetLayerByName(hDS, osRasterTableName.c_str()); + if (hRasterLyr == NULL) + goto end; + +/* -------------------------------------------------------------------- */ +/* Fetch resolutions */ +/* -------------------------------------------------------------------- */ + + hRasterPyramidsLyr = OGR_DS_GetLayerByName(hDS, "raster_pyramids"); + if (hRasterPyramidsLyr) + { + osSQL.Printf("SELECT pixel_x_size, pixel_y_size " + "FROM raster_pyramids WHERE table_prefix = '%s' " + "ORDER BY pixel_x_size ASC", + osTableName.c_str()); + + hSQLLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + if (hSQLLyr != NULL) + { + nResolutions = OGR_L_GetFeatureCount(hSQLLyr, TRUE); + if( nResolutions == 0 ) + { + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + hSQLLyr = NULL; + } + } + } + else + hSQLLyr = NULL; + + if( hSQLLyr == NULL ) + { + osSQL.Printf("SELECT DISTINCT(pixel_x_size), pixel_y_size " + "FROM \"%s_metadata\" WHERE pixel_x_size != 0 " + "ORDER BY pixel_x_size ASC", + osTableName.c_str()); + + hSQLLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + if (hSQLLyr == NULL) + goto end; + + nResolutions = OGR_L_GetFeatureCount(hSQLLyr, TRUE); + + if (nResolutions == 0) + { + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + goto end; + } + } + +/* -------------------------------------------------------------------- */ +/* Set dataset attributes */ +/* -------------------------------------------------------------------- */ + + poDS = new RasterliteDataset(); + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->eAccess = poOpenInfo->eAccess; + poDS->osTableName = osTableName; + poDS->osFileName = osFileName; + poDS->hDS = hDS; + + /* poDS will release it from now */ + hDS = NULL; + +/* -------------------------------------------------------------------- */ +/* Fetch spatial extent or use the one provided by the user */ +/* -------------------------------------------------------------------- */ + OGREnvelope oEnvelope; + if (bMinXSet && bMinYSet && bMaxXSet && bMaxYSet) + { + oEnvelope.MinX = minx; + oEnvelope.MinY = miny; + oEnvelope.MaxX = maxx; + oEnvelope.MaxY = maxy; + } + else + { + CPLString osOldVal = CPLGetConfigOption("OGR_SQLITE_EXACT_EXTENT", "NO"); + CPLSetThreadLocalConfigOption("OGR_SQLITE_EXACT_EXTENT", "YES"); + OGR_L_GetExtent(hMetadataLyr, &oEnvelope, TRUE); + CPLSetThreadLocalConfigOption("OGR_SQLITE_EXACT_EXTENT", osOldVal.c_str()); + //printf("minx=%.15f miny=%.15f maxx=%.15f maxy=%.15f\n", + // oEnvelope.MinX, oEnvelope.MinY, oEnvelope.MaxX, oEnvelope.MaxY); + } + +/* -------------------------------------------------------------------- */ +/* Store resolutions */ +/* -------------------------------------------------------------------- */ + poDS->nResolutions = nResolutions; + poDS->padfXResolutions = + (double*)CPLMalloc(sizeof(double) * poDS->nResolutions); + poDS->padfYResolutions = + (double*)CPLMalloc(sizeof(double) * poDS->nResolutions); + + i = 0; + while((hFeat = OGR_L_GetNextFeature(hSQLLyr)) != NULL) + { + poDS->padfXResolutions[i] = OGR_F_GetFieldAsDouble(hFeat, 0); + poDS->padfYResolutions[i] = OGR_F_GetFieldAsDouble(hFeat, 1); + + OGR_F_Destroy(hFeat); + + //printf("[%d] xres=%.15f yres=%.15f\n", i, + // poDS->padfXResolutions[i], poDS->padfYResolutions[i]); + + if (poDS->padfXResolutions[i] <= 0 || poDS->padfYResolutions[i] <= 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "res=%d, xres=%.15f, yres=%.15f", + i, poDS->padfXResolutions[i], poDS->padfYResolutions[i]); + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + delete poDS; + poDS = NULL; + goto end; + } + i ++; + } + + OGR_DS_ReleaseResultSet(poDS->hDS, hSQLLyr); + hSQLLyr = NULL; + +/* -------------------------------------------------------------------- */ +/* Compute raster size, geotransform and projection */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = + (int)((oEnvelope.MaxX - oEnvelope.MinX) / poDS->padfXResolutions[0] + 0.5); + poDS->nRasterYSize = + (int)((oEnvelope.MaxY - oEnvelope.MinY) / poDS->padfYResolutions[0] + 0.5); + + poDS->bValidGeoTransform = TRUE; + poDS->adfGeoTransform[0] = oEnvelope.MinX; + poDS->adfGeoTransform[1] = poDS->padfXResolutions[0]; + poDS->adfGeoTransform[2] = 0; + poDS->adfGeoTransform[3] = oEnvelope.MaxY; + poDS->adfGeoTransform[4] = 0; + poDS->adfGeoTransform[5] = - poDS->padfYResolutions[0]; + + OGRSpatialReferenceH hSRS = OGR_L_GetSpatialRef(hMetadataLyr); + if (hSRS) + { + OSRExportToWkt(hSRS, &poDS->pszSRS); + } + +/* -------------------------------------------------------------------- */ +/* Get number of bands and block size */ +/* -------------------------------------------------------------------- */ + + if (poDS->GetBlockParams(hRasterLyr, 0, &nBands, &eDataType, + &nBlockXSize, &nBlockYSize) == FALSE) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find block characteristics"); + delete poDS; + poDS = NULL; + goto end; + } + + if (eDataType == GDT_Byte && nBands == 1 && nReqBands == 3) + nBands = 3; + else if (nReqBands != 0) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Parameters bands=%d ignored", nReqBands); + } + +/* -------------------------------------------------------------------- */ +/* Add bands */ +/* -------------------------------------------------------------------- */ + + for(iBand=0;iBandSetBand(iBand+1, new RasterliteBand(poDS, iBand+1, eDataType, + nBlockXSize, nBlockYSize)); + +/* -------------------------------------------------------------------- */ +/* Add overview levels as internal datasets */ +/* -------------------------------------------------------------------- */ + if (nResolutions > 1) + { + poDS->papoOverviews = (RasterliteDataset**) + CPLCalloc(nResolutions - 1, sizeof(RasterliteDataset*)); + int nLev; + for(nLev=1;nLevGetBlockParams(hRasterLyr, nLev, &nOvrBands, &eOvrDataType, + &nBlockXSize, &nBlockYSize) == FALSE) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find block characteristics for overview %d", nLev); + delete poDS; + poDS = NULL; + goto end; + } + + if (eDataType == GDT_Byte && nOvrBands == 1 && nReqBands == 3) + nOvrBands = 3; + + if (nBands != nOvrBands || eDataType != eOvrDataType) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Overview %d has not the same number characteristics as main band", nLev); + delete poDS; + poDS = NULL; + goto end; + } + + poDS->papoOverviews[nLev-1] = new RasterliteDataset(poDS, nLev); + + for(iBand=0;iBandpapoOverviews[nLev-1]->SetBand(iBand+1, + new RasterliteBand(poDS->papoOverviews[nLev-1], iBand+1, eDataType, + nBlockXSize, nBlockYSize)); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Select an overview if the user has requested so */ +/* -------------------------------------------------------------------- */ + if (nLevel == 0) + { + } + else if (nLevel >= 1 && nLevel <= nResolutions - 1) + { + poDS->papoOverviews[nLevel-1]->bMustFree = TRUE; + poDS = poDS->papoOverviews[nLevel-1]; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid requested level : %d. Must be >= 0 and <= %d", + nLevel, nResolutions - 1); + delete poDS; + poDS = NULL; + } + + } + + if (poDS) + { +/* -------------------------------------------------------------------- */ +/* Setup PAM info for this subdatasets */ +/* -------------------------------------------------------------------- */ + poDS->SetPhysicalFilename( osFileName.c_str() ); + + CPLString osSubdatasetName; + osSubdatasetName.Printf("RASTERLITE:%s:table=%s", + osFileName.c_str(), osTableName.c_str()); + poDS->SetSubdatasetName( osSubdatasetName.c_str() ); + poDS->TryLoadXML(); + poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" ); + } + + +end: + if (hDS) + OGRReleaseDataSource(hDS); + CSLDestroy(papszTokens); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_Rasterlite() */ +/************************************************************************/ + +void GDALRegister_Rasterlite() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("Rasterlite driver")) + return; + + if( GDALGetDriverByName( "Rasterlite" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "Rasterlite" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Rasterlite" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_rasterlite.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "sqlite" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16 Int16 UInt32 Int32 Float32 " + "Float64 CInt16 CInt32 CFloat32 CFloat64" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" " ); + + poDriver->pfnOpen = RasterliteDataset::Open; + poDriver->pfnIdentify = RasterliteDataset::Identify; + poDriver->pfnCreateCopy = RasterliteCreateCopy; + poDriver->pfnDelete = RasterliteDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/rasterlite/rasterlitedataset.h b/bazaar/plugin/gdal/frmts/rasterlite/rasterlitedataset.h new file mode 100644 index 000000000..9fa44a9c0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rasterlite/rasterlitedataset.h @@ -0,0 +1,158 @@ +/****************************************************************************** + * $Id: rasterlitedataset.h 27566 2014-08-05 09:37:57Z rouault $ + * + * Project: GDAL Rasterlite driver + * Purpose: Implement GDAL Rasterlite support using OGR SQLite driver + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef RASTERLITE_DATASET_INCLUDED +#define RASTERLITE_DATASET_INCLUDED + +#include "gdal_pam.h" +#include "ogr_api.h" + +char** RasterliteGetTileDriverOptions(char** papszOptions); + +OGRDataSourceH RasterliteOpenSQLiteDB(const char* pszFilename, + GDALAccess eAccess); +CPLString RasterliteGetPixelSizeCond(double dfPixelXSize, + double dfPixelYSize, + const char* pszTablePrefixWithDot = ""); +CPLString RasterliteGetSpatialFilterCond(double minx, double miny, + double maxx, double maxy); + +class RasterliteBand; + +/************************************************************************/ +/* ==================================================================== */ +/* RasterliteDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class RasterliteDataset : public GDALPamDataset +{ + friend class RasterliteBand; + + public: + RasterliteDataset(); + RasterliteDataset(RasterliteDataset* poMainDS, int nLevel); + + virtual ~RasterliteDataset(); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char *pszDomain ); + virtual const char *GetMetadataItem( const char *pszName, + const char *pszDomain ); + virtual CPLErr GetGeoTransform( double* padfGeoTransform ); + virtual const char* GetProjectionRef(); + + virtual char** GetFileList(); + + virtual CPLErr IBuildOverviews( const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, void * pProgressData ); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + + protected: + virtual int CloseDependentDatasets(); + + private: + + int bMustFree; + RasterliteDataset* poMainDS; + int nLevel; + + char** papszMetadata; + char** papszImageStructure; + char** papszSubDatasets; + + int nResolutions; + double* padfXResolutions, *padfYResolutions; + RasterliteDataset** papoOverviews; + int nLimitOvrCount; + + int bValidGeoTransform; + double adfGeoTransform[6]; + char* pszSRS; + + GDALColorTable* poCT; + + CPLString osTableName; + CPLString osFileName; + + int bCheckForExistingOverview; + CPLString osOvrFileName; + + OGRDataSourceH hDS; + + void AddSubDataset( const char* pszDSName); + int GetBlockParams(OGRLayerH hRasterLyr, int nLevel, int* pnBands, + GDALDataType* peDataType, + int* pnBlockXSize, int* pnBlockYSize); + CPLErr CleanOverviews(); + CPLErr CleanOverviewLevel(int nOvrFactor); + CPLErr ReloadOverviews(); + CPLErr CreateOverviewLevel(const char * pszResampling, + int nOvrFactor, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* RasterliteBand */ +/* ==================================================================== */ +/************************************************************************/ + +class RasterliteBand: public GDALPamRasterBand +{ + friend class RasterliteDataset; + + public: + RasterliteBand( RasterliteDataset* poDS, int nBand, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize); + + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable* GetColorTable(); + + virtual int GetOverviewCount(); + virtual GDALRasterBand* GetOverview(int nLevel); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +GDALDataset * +RasterliteCreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); + +CPLErr RasterliteDelete(const char* pszFilename); + +#endif // RASTERLITE_DATASET_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/rasterlite/rasterliteoverviews.cpp b/bazaar/plugin/gdal/frmts/rasterlite/rasterliteoverviews.cpp new file mode 100644 index 000000000..1b7778883 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rasterlite/rasterliteoverviews.cpp @@ -0,0 +1,807 @@ +/****************************************************************************** + * $Id: rasterliteoverviews.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: GDAL Rasterlite driver + * Purpose: Implement GDAL Rasterlite support using OGR SQLite driver + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_string.h" +#include "ogr_api.h" +#include "ogr_srs_api.h" + +#include "rasterlitedataset.h" + +CPL_CVSID("$Id: rasterliteoverviews.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +/************************************************************************/ +/* ReloadOverviews() */ +/************************************************************************/ + +CPLErr RasterliteDataset::ReloadOverviews() +{ + if (nLevel != 0) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Fetch resolutions */ +/* -------------------------------------------------------------------- */ + + CPLString osSQL; + OGRLayerH hRasterPyramidsLyr = OGR_DS_GetLayerByName(hDS, "raster_pyramids"); + if (hRasterPyramidsLyr) + { + osSQL.Printf("SELECT pixel_x_size, pixel_y_size " + "FROM raster_pyramids WHERE table_prefix = '%s' " + "ORDER BY pixel_x_size ASC", + osTableName.c_str()); + } + else + { + osSQL.Printf("SELECT DISTINCT(pixel_x_size), pixel_y_size " + "FROM \"%s_metadata\" WHERE pixel_x_size != 0 " + "ORDER BY pixel_x_size ASC", + osTableName.c_str()); + } + + OGRLayerH hSQLLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + if (hSQLLyr == NULL) + { + if (hRasterPyramidsLyr == NULL) + return CE_Failure; + + osSQL.Printf("SELECT DISTINCT(pixel_x_size), pixel_y_size " + "FROM \"%s_metadata\" WHERE pixel_x_size != 0 " + "ORDER BY pixel_x_size ASC", + osTableName.c_str()); + + hSQLLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + if (hSQLLyr == NULL) + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + + int i; + for(i=1;i 1) + { + CPLString osRasterTableName = osTableName; + osRasterTableName += "_rasters"; + + OGRLayerH hRasterLyr = OGR_DS_GetLayerByName(hDS, osRasterTableName.c_str()); + + papoOverviews = (RasterliteDataset**) + CPLCalloc(nResolutions - 1, sizeof(RasterliteDataset*)); + int nLev; + for(nLev=1;nLevSetBand(iBand+1, + new RasterliteBand(papoOverviews[nLev-1], iBand+1, eOvrDataType, + nBlockXSize, nBlockYSize)); + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find block characteristics for overview %d", nLev); + papoOverviews[nLev-1] = NULL; + } + } + } + + return CE_None; +} + +/************************************************************************/ +/* CleanOverviews() */ +/************************************************************************/ + +CPLErr RasterliteDataset::CleanOverviews() +{ + CPLString osSQL; + + if (nLevel != 0) + return CE_Failure; + + osSQL.Printf("BEGIN"); + OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + + CPLString osResolutionCond = + "NOT " + RasterliteGetPixelSizeCond(padfXResolutions[0], padfYResolutions[0]); + + osSQL.Printf("DELETE FROM \"%s_rasters\" WHERE id " + "IN(SELECT id FROM \"%s_metadata\" WHERE %s)", + osTableName.c_str(), osTableName.c_str(), + osResolutionCond.c_str()); + OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + + osSQL.Printf("DELETE FROM \"%s_metadata\" WHERE %s", + osTableName.c_str(), osResolutionCond.c_str()); + OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + + OGRLayerH hRasterPyramidsLyr = OGR_DS_GetLayerByName(hDS, "raster_pyramids"); + if (hRasterPyramidsLyr) + { + osSQL.Printf("DELETE FROM raster_pyramids WHERE table_prefix = '%s' AND %s", + osTableName.c_str(), osResolutionCond.c_str()); + OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + } + + osSQL.Printf("COMMIT"); + OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + + int i; + for(i=1;i 4096) nBlockXSize = 4096; + if (nBlockYSize < 64) nBlockYSize = 64; + else if (nBlockYSize > 4096) nBlockYSize = 4096; + } + else + { + nBlockXSize = nOvrXSize; + nBlockYSize = nOvrYSize; + } + + int nXBlocks = (nOvrXSize + nBlockXSize - 1) / nBlockXSize; + int nYBlocks = (nOvrYSize + nBlockYSize - 1) / nBlockYSize; + + const char* pszDriverName = CSLFetchNameValueDef(papszOptions, "DRIVER", "GTiff"); + if (EQUAL(pszDriverName, "MEM") || EQUAL(pszDriverName, "VRT")) + { + CPLError(CE_Failure, CPLE_AppDefined, "GDAL %s driver cannot be used as underlying driver", + pszDriverName); + return CE_Failure; + } + GDALDriverH hTileDriver = GDALGetDriverByName(pszDriverName); + if (hTileDriver == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot load GDAL %s driver", pszDriverName); + return CE_Failure; + } + + GDALDriverH hMemDriver = GDALGetDriverByName("MEM"); + if (hMemDriver == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot load GDAL MEM driver"); + return CE_Failure; + } + + GDALDataType eDataType = GetRasterBand(1)->GetRasterDataType(); + int nDataTypeSize = GDALGetDataTypeSize(eDataType) / 8; + GByte* pabyMEMDSBuffer = + (GByte*)VSIMalloc3(nBlockXSize, nBlockYSize, nBands * nDataTypeSize); + if (pabyMEMDSBuffer == NULL) + { + return CE_Failure; + } + + CPLString osTempFileName; + osTempFileName.Printf("/vsimem/%p", hDS); + + int nTileId = 0; + int nBlocks = 0; + int nTotalBlocks = nXBlocks * nYBlocks; + + CPLString osRasterLayer; + osRasterLayer.Printf("%s_rasters", osTableName.c_str()); + + CPLString osMetatadataLayer; + osMetatadataLayer.Printf("%s_metadata", osTableName.c_str()); + + OGRLayerH hRasterLayer = OGR_DS_GetLayerByName(hDS, osRasterLayer.c_str()); + OGRLayerH hMetadataLayer = OGR_DS_GetLayerByName(hDS, osMetatadataLayer.c_str()); + + CPLString osSourceName = "unknown"; + + osSQL.Printf("SELECT source_name FROM \"%s\" WHERE " + "%s LIMIT 1", + osMetatadataLayer.c_str(), + RasterliteGetPixelSizeCond(padfXResolutions[0], padfYResolutions[0]).c_str()); + OGRLayerH hSQLLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + if (hSQLLyr) + { + OGRFeatureH hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat) + { + const char* pszVal = OGR_F_GetFieldAsString(hFeat, 0); + if (pszVal) + osSourceName = pszVal; + OGR_F_Destroy(hFeat); + } + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + } + +/* -------------------------------------------------------------------- */ +/* Compute up to which existing overview level we can use for */ +/* computing the requested overview */ +/* -------------------------------------------------------------------- */ + int iLev; + nLimitOvrCount = 0; + for(iLev=1;iLev= 2 && iLev <= nResolutions && papoOverviews[iLev-2]) ? + papoOverviews[iLev-2] : this; + double dfRatioPrevOvr = poPrevOvrLevel->GetRasterBand(1)->GetXSize() / nOvrXSize; + int nPrevOvrBlockXSize = (int)(nBlockXSize * dfRatioPrevOvr + 0.5); + int nPrevOvrBlockYSize = (int)(nBlockYSize * dfRatioPrevOvr + 0.5); + GByte* pabyPrevOvrMEMDSBuffer = NULL; + + if( !EQUALN(pszResampling, "NEAR", 4)) + { + pabyPrevOvrMEMDSBuffer = + (GByte*)VSIMalloc3(nPrevOvrBlockXSize, nPrevOvrBlockYSize, nBands * nDataTypeSize); + if (pabyPrevOvrMEMDSBuffer == NULL) + { + VSIFree(pabyMEMDSBuffer); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Iterate over blocks to add data into raster and metadata tables */ +/* -------------------------------------------------------------------- */ + + char** papszTileDriverOptions = RasterliteGetTileDriverOptions(papszOptions); + + OGR_DS_ExecuteSQL(hDS, "BEGIN", NULL, NULL); + + CPLErr eErr = CE_None; + int nBlockXOff, nBlockYOff; + for(nBlockYOff=0;eErr == CE_None && nBlockYOff nOvrXSize) + nReqXSize = nOvrXSize - nBlockXOff * nBlockXSize; + if ((nBlockYOff+1) * nBlockYSize > nOvrYSize) + nReqYSize = nOvrYSize - nBlockYOff * nBlockYSize; + + if( pabyPrevOvrMEMDSBuffer != NULL ) + { + int nPrevOvrReqXSize = + (int)(nReqXSize * dfRatioPrevOvr + 0.5); + int nPrevOvrReqYSize = + (int)(nReqYSize * dfRatioPrevOvr + 0.5); + + eErr = RasterIO(GF_Read, + nBlockXOff * nBlockXSize * nOvrFactor, + nBlockYOff * nBlockYSize * nOvrFactor, + nReqXSize * nOvrFactor, nReqYSize * nOvrFactor, + pabyPrevOvrMEMDSBuffer, nPrevOvrReqXSize, nPrevOvrReqYSize, + eDataType, nBands, NULL, + 0, 0, 0, NULL); + + if (eErr != CE_None) + { + break; + } + + hPrevOvrMemDS = GDALCreate(hMemDriver, "MEM:::", + nPrevOvrReqXSize, nPrevOvrReqYSize, 0, + eDataType, NULL); + + if (hPrevOvrMemDS == NULL) + { + eErr = CE_Failure; + break; + } + + int iBand; + for(iBand = 0; iBand < nBands; iBand ++) + { + char** papszOptions = NULL; + char szTmp[64]; + memset(szTmp, 0, sizeof(szTmp)); + CPLPrintPointer(szTmp, + pabyPrevOvrMEMDSBuffer + iBand * nDataTypeSize * + nPrevOvrReqXSize * nPrevOvrReqYSize, sizeof(szTmp)); + papszOptions = CSLSetNameValue(papszOptions, "DATAPOINTER", szTmp); + GDALAddBand(hPrevOvrMemDS, eDataType, papszOptions); + CSLDestroy(papszOptions); + } + } + else + { + eErr = RasterIO(GF_Read, + nBlockXOff * nBlockXSize * nOvrFactor, + nBlockYOff * nBlockYSize * nOvrFactor, + nReqXSize * nOvrFactor, nReqYSize * nOvrFactor, + pabyMEMDSBuffer, nReqXSize, nReqYSize, + eDataType, nBands, NULL, + 0, 0, 0, NULL); + if (eErr != CE_None) + { + break; + } + } + + GDALDatasetH hMemDS = GDALCreate(hMemDriver, "MEM:::", + nReqXSize, nReqYSize, 0, + eDataType, NULL); + if (hMemDS == NULL) + { + eErr = CE_Failure; + break; + } + + int iBand; + for(iBand = 0; iBand < nBands; iBand ++) + { + char** papszOptions = NULL; + char szTmp[64]; + memset(szTmp, 0, sizeof(szTmp)); + CPLPrintPointer(szTmp, + pabyMEMDSBuffer + iBand * nDataTypeSize * + nReqXSize * nReqYSize, sizeof(szTmp)); + papszOptions = CSLSetNameValue(papszOptions, "DATAPOINTER", szTmp); + GDALAddBand(hMemDS, eDataType, papszOptions); + CSLDestroy(papszOptions); + } + + if( hPrevOvrMemDS != NULL ) + { + for(iBand = 0; iBand < nBands; iBand ++) + { + GDALRasterBandH hDstOvrBand = GDALGetRasterBand(hMemDS, iBand+1); + + eErr = GDALRegenerateOverviews( GDALGetRasterBand(hPrevOvrMemDS, iBand+1), + 1, &hDstOvrBand, + pszResampling, + NULL, NULL ); + if( eErr != CE_None ) + break; + } + + GDALClose(hPrevOvrMemDS); + } + + GDALDatasetH hOutDS = GDALCreateCopy(hTileDriver, + osTempFileName.c_str(), hMemDS, FALSE, + papszTileDriverOptions, NULL, NULL); + + GDALClose(hMemDS); + if (hOutDS) + GDALClose(hOutDS); + else + { + eErr = CE_Failure; + break; + } + +/* -------------------------------------------------------------------- */ +/* Insert new entry into raster table */ +/* -------------------------------------------------------------------- */ + + vsi_l_offset nDataLength; + GByte *pabyData = VSIGetMemFileBuffer( osTempFileName.c_str(), + &nDataLength, FALSE); + + OGRFeatureH hFeat = OGR_F_Create( OGR_L_GetLayerDefn(hRasterLayer) ); + OGR_F_SetFieldBinary(hFeat, 0, (int)nDataLength, pabyData); + + OGR_L_CreateFeature(hRasterLayer, hFeat); + /* Query raster ID to set it as the ID of the associated metadata */ + int nRasterID = (int)OGR_F_GetFID(hFeat); + + OGR_F_Destroy(hFeat); + + VSIUnlink(osTempFileName.c_str()); + +/* -------------------------------------------------------------------- */ +/* Insert new entry into metadata table */ +/* -------------------------------------------------------------------- */ + + hFeat = OGR_F_Create( OGR_L_GetLayerDefn(hMetadataLayer) ); + OGR_F_SetFID(hFeat, nRasterID); + OGR_F_SetFieldString(hFeat, 0, osSourceName); + OGR_F_SetFieldInteger(hFeat, 1, nTileId ++); + OGR_F_SetFieldInteger(hFeat, 2, nReqXSize); + OGR_F_SetFieldInteger(hFeat, 3, nReqYSize); + OGR_F_SetFieldDouble(hFeat, 4, dfXResolution); + OGR_F_SetFieldDouble(hFeat, 5, dfYResolution); + + double minx, maxx, maxy, miny; + minx = adfGeoTransform[0] + + (nBlockXSize * nBlockXOff) * dfXResolution; + maxx = adfGeoTransform[0] + + (nBlockXSize * nBlockXOff + nReqXSize) * dfXResolution; + maxy = adfGeoTransform[3] + + (nBlockYSize * nBlockYOff) * (-dfYResolution); + miny = adfGeoTransform[3] + + (nBlockYSize * nBlockYOff + nReqYSize) * (-dfYResolution); + + OGRGeometryH hRectangle = OGR_G_CreateGeometry(wkbPolygon); + OGRGeometryH hLinearRing = OGR_G_CreateGeometry(wkbLinearRing); + OGR_G_AddPoint_2D(hLinearRing, minx, miny); + OGR_G_AddPoint_2D(hLinearRing, minx, maxy); + OGR_G_AddPoint_2D(hLinearRing, maxx, maxy); + OGR_G_AddPoint_2D(hLinearRing, maxx, miny); + OGR_G_AddPoint_2D(hLinearRing, minx, miny); + OGR_G_AddGeometryDirectly(hRectangle, hLinearRing); + + OGR_F_SetGeometryDirectly(hFeat, hRectangle); + + OGR_L_CreateFeature(hMetadataLayer, hFeat); + OGR_F_Destroy(hFeat); + + nBlocks++; + if (pfnProgress && !pfnProgress(1.0 * nBlocks / nTotalBlocks, + NULL, pProgressData)) + eErr = CE_Failure; + } + } + + nLimitOvrCount = -1; + + if (eErr == CE_None) + OGR_DS_ExecuteSQL(hDS, "COMMIT", NULL, NULL); + else + OGR_DS_ExecuteSQL(hDS, "ROLLBACK", NULL, NULL); + + VSIFree(pabyMEMDSBuffer); + VSIFree(pabyPrevOvrMEMDSBuffer); + + CSLDestroy(papszTileDriverOptions); + papszTileDriverOptions = NULL; + +/* -------------------------------------------------------------------- */ +/* Update raster_pyramids table */ +/* -------------------------------------------------------------------- */ + if (eErr == CE_None) + { + OGRLayerH hRasterPyramidsLyr = OGR_DS_GetLayerByName(hDS, "raster_pyramids"); + if (hRasterPyramidsLyr == NULL) + { + osSQL.Printf ("CREATE TABLE raster_pyramids (" + "table_prefix TEXT NOT NULL," + "pixel_x_size DOUBLE NOT NULL," + "pixel_y_size DOUBLE NOT NULL," + "tile_count INTEGER NOT NULL)"); + OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + + /* Re-open the DB to take into account the new tables*/ + OGRReleaseDataSource(hDS); + + hDS = RasterliteOpenSQLiteDB(osFileName.c_str(), GA_Update); + + hRasterPyramidsLyr = OGR_DS_GetLayerByName(hDS, "raster_pyramids"); + if (hRasterPyramidsLyr == NULL) + return CE_Failure; + } + OGRFeatureDefnH hFDefn = OGR_L_GetLayerDefn(hRasterPyramidsLyr); + + /* Insert base resolution into raster_pyramids if not already done */ + int bHasBaseResolution = FALSE; + osSQL.Printf("SELECT * FROM raster_pyramids WHERE " + "table_prefix = '%s' AND %s", + osTableName.c_str(), + RasterliteGetPixelSizeCond(padfXResolutions[0], padfYResolutions[0]).c_str()); + hSQLLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + if (hSQLLyr) + { + OGRFeatureH hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat) + { + bHasBaseResolution = TRUE; + OGR_F_Destroy(hFeat); + } + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + } + + if (!bHasBaseResolution) + { + osSQL.Printf("SELECT COUNT(*) FROM \"%s\" WHERE %s", + osMetatadataLayer.c_str(), + RasterliteGetPixelSizeCond(padfXResolutions[0], padfYResolutions[0]).c_str()); + + int nBlocksMainRes = 0; + + hSQLLyr = OGR_DS_ExecuteSQL(hDS, osSQL.c_str(), NULL, NULL); + if (hSQLLyr) + { + OGRFeatureH hFeat = OGR_L_GetNextFeature(hSQLLyr); + if (hFeat) + { + nBlocksMainRes = OGR_F_GetFieldAsInteger(hFeat, 0); + OGR_F_Destroy(hFeat); + } + OGR_DS_ReleaseResultSet(hDS, hSQLLyr); + } + + OGRFeatureH hFeat = OGR_F_Create( hFDefn ); + OGR_F_SetFieldString(hFeat, OGR_FD_GetFieldIndex(hFDefn, "table_prefix"), osTableName.c_str()); + OGR_F_SetFieldDouble(hFeat, OGR_FD_GetFieldIndex(hFDefn, "pixel_x_size"), padfXResolutions[0]); + OGR_F_SetFieldDouble(hFeat, OGR_FD_GetFieldIndex(hFDefn, "pixel_y_size"), padfYResolutions[0]); + OGR_F_SetFieldInteger(hFeat, OGR_FD_GetFieldIndex(hFDefn, "tile_count"), nBlocksMainRes); + OGR_L_CreateFeature(hRasterPyramidsLyr, hFeat); + OGR_F_Destroy(hFeat); + } + + OGRFeatureH hFeat = OGR_F_Create( hFDefn ); + OGR_F_SetFieldString(hFeat, OGR_FD_GetFieldIndex(hFDefn, "table_prefix"), osTableName.c_str()); + OGR_F_SetFieldDouble(hFeat, OGR_FD_GetFieldIndex(hFDefn, "pixel_x_size"), dfXResolution); + OGR_F_SetFieldDouble(hFeat, OGR_FD_GetFieldIndex(hFDefn, "pixel_y_size"), dfYResolution); + OGR_F_SetFieldInteger(hFeat, OGR_FD_GetFieldIndex(hFDefn, "tile_count"), nTotalBlocks); + OGR_L_CreateFeature(hRasterPyramidsLyr, hFeat); + OGR_F_Destroy(hFeat); + } + + return eErr; +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr RasterliteDataset::IBuildOverviews( const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + CPLErr eErr = CE_None; + + if (nLevel != 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Overviews can only be computed on the base dataset"); + return CE_Failure; + } + + if (osTableName.size() == 0) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* If we don't have read access, then create the overviews */ +/* externally. */ +/* -------------------------------------------------------------------- */ + if( GetAccess() != GA_Update ) + { + CPLDebug( "Rasterlite", + "File open for read-only accessing, " + "creating overviews externally." ); + + if (nResolutions != 1) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot add external overviews to a " + "dataset with internal overviews"); + return CE_Failure; + } + + bCheckForExistingOverview = FALSE; + eErr = GDALDataset::IBuildOverviews( + pszResampling, nOverviews, panOverviewList, + nBands, panBandList, pfnProgress, pProgressData ); + bCheckForExistingOverview = TRUE; + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* If zero overviews were requested, we need to clear all */ +/* existing overviews. */ +/* -------------------------------------------------------------------- */ + if (nOverviews == 0) + { + return CleanOverviews(); + } + + if( nBands != GetRasterCount() ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Generation of overviews in RASTERLITE only" + " supported when operating on all bands.\n" + "Operation failed.\n" ); + return CE_Failure; + } + + const char* pszOvrOptions = CPLGetConfigOption("RASTERLITE_OVR_OPTIONS", NULL); + char** papszOptions = (pszOvrOptions) ? CSLTokenizeString2( pszOvrOptions, ",", 0) : NULL; + GDALValidateCreationOptions( GetDriver(), papszOptions); + + int i; + for(i=0;i + * + ****************************************************************************** + * Copyright (c) 2011-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: ace2dataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +CPL_C_START +void GDALRegister_ACE2(void); +CPL_C_END + +static const char * const apszCategorySource[] = +{ + "Pure SRTM (above 60deg N pure GLOBE data, below 60S pure ACE [original] data)", + "SRTM voids filled by interpolation and/or altimeter data", + "SRTM data warped using the ERS-1 Geodetic Mission", + "SRTM data warped using EnviSat & ERS-2 data", + "Mean lake level data derived from Altimetry", + "GLOBE/ACE data warped using combined altimetry (only above 60deg N)", + "Pure altimetry data (derived from ERS-1 Geodetic Mission, ERS-2 and EnviSat data using Delaunay Triangulation", + NULL +}; + +static const char * const apszCategoryQuality[] = +{ + "Generic - use base datasets", + "Accuracy of greater than +/- 16m", + "Accuracy between +/- 16m - +/- 10m", + "Accuracy between +/-10m - +/-5m", + "Accuracy between +/-5m - +/-1m", + "Accuracy between +/-1m", + NULL +}; + +static const char * const apszCategoryConfidence[] = +{ + "No confidence could be derived due to lack of data", + "Heights generated by interpolation", + "Low confidence", + "Low confidence", + "Low confidence", + "Medium confidence", + "Medium confidence", + "Medium confidence", + "Medium confidence", + "Medium confidence", + "Medium confidence", + "Medium confidence", + "Medium confidence", + "High confidence", + "High confidence", + "High confidence", + "High confidence", + "Inland water confidence", + "Inland water confidence", + "Inland water confidence", + "Inland water confidence", + "Inland water confidence", + NULL +}; + +/************************************************************************/ +/* ==================================================================== */ +/* ACE2Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +class ACE2Dataset : public GDALPamDataset +{ + friend class ACE2RasterBand; + + double adfGeoTransform[6]; + + public: + + ACE2Dataset(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * ); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* ACE2RasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class ACE2RasterBand : public RawRasterBand +{ + public: + ACE2RasterBand(VSILFILE* fpRaw, + GDALDataType eDataType, + int nXSize, int nYSize); + + virtual const char *GetUnitType(); + virtual char **GetCategoryNames(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* ACE2Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ACE2Dataset() */ +/************************************************************************/ + +ACE2Dataset::ACE2Dataset() +{ + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr ACE2Dataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *ACE2Dataset::GetProjectionRef() + +{ + return SRS_WKT_WGS84; +} + +/************************************************************************/ +/* ACE2RasterBand() */ +/************************************************************************/ + +ACE2RasterBand::ACE2RasterBand(VSILFILE* fpRaw, + GDALDataType eDataType, + int nXSize, int nYSize) : + RawRasterBand( fpRaw, 0, GDALGetDataTypeSize(eDataType) / 8, + nXSize * GDALGetDataTypeSize(eDataType) / 8, eDataType, + CPL_IS_LSB, nXSize, nYSize, TRUE, TRUE) +{ +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +const char *ACE2RasterBand::GetUnitType() +{ + if (eDataType == GDT_Float32) + return "m"; + else + return ""; +} + +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +char **ACE2RasterBand::GetCategoryNames() +{ + if (eDataType == GDT_Int16) + { + const char* pszName = poDS->GetDescription(); + if (strstr(pszName, "_SOURCE_")) + return (char**) apszCategorySource; + else if (strstr(pszName, "_QUALITY_")) + return (char**) apszCategoryQuality; + else if (strstr(pszName, "_CONF_")) + return (char**) apszCategoryConfidence; + } + + return NULL; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int ACE2Dataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if (! (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "ACE2") || + strstr(poOpenInfo->pszFilename, ".ACE2.gz") || + strstr(poOpenInfo->pszFilename, ".ace2.gz")) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *ACE2Dataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if (!Identify(poOpenInfo)) + return NULL; + + const char* pszBasename = CPLGetBasename(poOpenInfo->pszFilename); + int nXSize = 0, nYSize = 0; + + if (strlen(pszBasename) < 7) + return NULL; + + /* Determine southwest coordinates from filename */ + + /* e.g. 30S120W_5M.ACE2 */ + char pszLatLonValueString[4]; + memset(pszLatLonValueString, 0, 4); + strncpy(pszLatLonValueString, &pszBasename[0], 2); + int southWestLat = atoi(pszLatLonValueString); + memset(pszLatLonValueString, 0, 4); + strncpy(pszLatLonValueString, &pszBasename[3], 3); + int southWestLon = atoi(pszLatLonValueString); + + if(pszBasename[2] == 'N' || pszBasename[2] == 'n') + /*southWestLat = southWestLat*/; + else if(pszBasename[2] == 'S' || pszBasename[2] == 's') + southWestLat = southWestLat * -1; + else + return NULL; + + if(pszBasename[6] == 'E' || pszBasename[6] == 'e') + /*southWestLon = southWestLon*/; + else if(pszBasename[6] == 'W' || pszBasename[6] == 'w') + southWestLon = southWestLon * -1; + else + return NULL; + + + GDALDataType eDT; + if (strstr(pszBasename, "_CONF_") || + strstr(pszBasename, "_QUALITY_") || + strstr(pszBasename, "_SOURCE_")) + eDT = GDT_Int16; + else + eDT = GDT_Float32; + int nWordSize = GDALGetDataTypeSize(eDT) / 8; + + VSIStatBufL sStat; + if (strstr(pszBasename, "_5M")) + sStat.st_size = 180 * 180 * nWordSize; + else if (strstr(pszBasename, "_30S")) + sStat.st_size = 1800 * 1800 * nWordSize; + else if (strstr(pszBasename, "_9S")) + sStat.st_size = 6000 * 6000 * nWordSize; + else if (strstr(pszBasename, "_3S")) + sStat.st_size = 18000 * 18000 * nWordSize; + /* Check file size otherwise */ + else if(VSIStatL(poOpenInfo->pszFilename, &sStat) != 0) + { + return NULL; + } + + double dfPixelSize = 0; + if (sStat.st_size == 180 * 180 * nWordSize) + { + /* 5 minute */ + nXSize = nYSize = 180; + dfPixelSize = 5. / 60; + } + else if (sStat.st_size == 1800 * 1800 * nWordSize) + { + /* 30 s */ + nXSize = nYSize = 1800; + dfPixelSize = 30. / 3600; + } + else if (sStat.st_size == 6000 * 6000 * nWordSize) + { + /* 9 s */ + nXSize = nYSize = 6000; + dfPixelSize = 9. / 3600; + } + else if (sStat.st_size == 18000 * 18000 * nWordSize) + { + /* 3 s */ + nXSize = nYSize = 18000; + dfPixelSize = 3. / 3600; + } + else + return NULL; + +/* -------------------------------------------------------------------- */ +/* Open file. */ +/* -------------------------------------------------------------------- */ + + CPLString osFilename = poOpenInfo->pszFilename; + if ((strstr(poOpenInfo->pszFilename, ".ACE2.gz") || + strstr(poOpenInfo->pszFilename, ".ace2.gz")) && + strncmp(poOpenInfo->pszFilename, "/vsigzip/", 9) != 0) + osFilename = "/vsigzip/" + osFilename; + + VSILFILE* fpImage = VSIFOpenL( osFilename, "rb+" ); + if (fpImage == NULL) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + ACE2Dataset *poDS; + + poDS = new ACE2Dataset(); + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + + poDS->adfGeoTransform[0] = southWestLon; + poDS->adfGeoTransform[1] = dfPixelSize; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = southWestLat + nYSize * dfPixelSize; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -dfPixelSize; + +/* -------------------------------------------------------------------- */ +/* Create band information objects */ +/* -------------------------------------------------------------------- */ + poDS->SetBand( 1, new ACE2RasterBand(fpImage, eDT, nXSize, nYSize ) ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_ACE2() */ +/************************************************************************/ + +void GDALRegister_ACE2() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ACE2" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ACE2" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ACE2" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#ACE2" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "ACE2" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = ACE2Dataset::Open; + poDriver->pfnIdentify = ACE2Dataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/atlsci_spheroid.cpp b/bazaar/plugin/gdal/frmts/raw/atlsci_spheroid.cpp new file mode 100644 index 000000000..a64de12cb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/atlsci_spheroid.cpp @@ -0,0 +1,171 @@ +/****************************************************************************** + * $Id: atlsci_spheroid.cpp 22381 2011-05-16 21:14:22Z rouault $ + * + * Project: Spheroid classes + * Purpose: Provide spheroid lookup table base classes. + * Author: Gillian Walter + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "atlsci_spheroid.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: atlsci_spheroid.cpp 22381 2011-05-16 21:14:22Z rouault $"); + +/**********************************************************************/ +/* ================================================================== */ +/* Spheroid definitions */ +/* ================================================================== */ +/**********************************************************************/ + + +void SpheroidItem :: SetValuesByRadii(const char *spheroidname, double eq_radius, double p_radius) +{ + spheroid_name = CPLStrdup(spheroidname); + equitorial_radius=eq_radius; + polar_radius=p_radius; + inverse_flattening=(eq_radius == polar_radius) ? 0 : eq_radius/(eq_radius - polar_radius); +} + +void SpheroidItem :: SetValuesByEqRadiusAndInvFlattening(const char *spheroidname, double eq_radius, double inverseflattening) +{ + spheroid_name = CPLStrdup(spheroidname); + equitorial_radius=eq_radius; + inverse_flattening=inverseflattening; + polar_radius=(inverse_flattening == 0) ? eq_radius : eq_radius*(1.0 - (1.0/inverse_flattening)); +} +SpheroidItem :: SpheroidItem() +{ + spheroid_name=NULL; + equitorial_radius=-1.0; + polar_radius=-1.0; + inverse_flattening=-1.0; +} + +SpheroidItem :: ~SpheroidItem() +{ + if (spheroid_name != NULL) + CPLFree(spheroid_name); +} + +SpheroidList :: SpheroidList() +{ + num_spheroids=0; +} + +SpheroidList :: ~SpheroidList() +{ +} + +char *SpheroidList :: GetSpheroidNameByRadii( double eq_radius, double polar_radius ) +{ + int index=0; + double er=0.0; + double pr=0.0; + + for(index=0;index + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: btdataset.cpp 29284 2015-06-03 13:26:10Z rouault $"); + +CPL_C_START +void GDALRegister_BT(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* BTDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class BTDataset : public GDALPamDataset +{ + friend class BTRasterBand; + + VSILFILE *fpImage; // image data file. + + int bGeoTransformValid; + double adfGeoTransform[6]; + + char *pszProjection; + + int nVersionCode; // version times 10. + + int bHeaderModified; + unsigned char abyHeader[256]; + + float m_fVscale; + + public: + + BTDataset(); + ~BTDataset(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual void FlushCache(); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* BTRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class BTRasterBand : public GDALPamRasterBand +{ + VSILFILE *fpImage; + + public: + + BTRasterBand( GDALDataset * poDS, VSILFILE * fp, + GDALDataType eType ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + + virtual const char* GetUnitType(); + virtual CPLErr SetUnitType(const char*); + virtual double GetNoDataValue( int* = NULL ); + virtual CPLErr SetNoDataValue( double ); +}; + + +/************************************************************************/ +/* BTRasterBand() */ +/************************************************************************/ + +BTRasterBand::BTRasterBand( GDALDataset *poDS, VSILFILE *fp, GDALDataType eType ) + +{ + this->poDS = poDS; + this->nBand = 1; + this->eDataType = eType; + this->fpImage = fp; + + nBlockXSize = 1; + nBlockYSize = poDS->GetRasterYSize(); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr BTRasterBand::IReadBlock( int nBlockXOff, + CPL_UNUSED int nBlockYOff, + void * pImage ) +{ + int nDataSize = GDALGetDataTypeSize( eDataType ) / 8; + int i; + + CPLAssert( nBlockYOff == 0 ); + +/* -------------------------------------------------------------------- */ +/* Seek to profile. */ +/* -------------------------------------------------------------------- */ + if( VSIFSeekL( fpImage, + 256 + nBlockXOff * nDataSize * (vsi_l_offset) nRasterYSize, + SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + ".bt Seek failed:%s", VSIStrerror( errno ) ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Read the profile. */ +/* -------------------------------------------------------------------- */ + if( VSIFReadL( pImage, nDataSize, nRasterYSize, fpImage ) != + (size_t) nRasterYSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + ".bt Read failed:%s", VSIStrerror( errno ) ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Swap on MSB platforms. */ +/* -------------------------------------------------------------------- */ +#ifdef CPL_MSB + GDALSwapWords( pImage, nDataSize, nRasterYSize, nDataSize ); +#endif + +/* -------------------------------------------------------------------- */ +/* Vertical flip, since GDAL expects values from top to bottom, */ +/* but in .bt they are bottom to top. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nRasterYSize / 2; i++ ) + { + GByte abyWrk[8]; + + memcpy( abyWrk, ((GByte *) pImage) + i * nDataSize, nDataSize ); + memcpy( ((GByte *) pImage) + i * nDataSize, + ((GByte *) pImage) + (nRasterYSize - i - 1) * nDataSize, + nDataSize ); + memcpy( ((GByte *) pImage) + (nRasterYSize - i - 1) * nDataSize, + abyWrk, nDataSize ); + } + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr BTRasterBand::IWriteBlock( int nBlockXOff, + CPL_UNUSED int nBlockYOff, + void * pImage ) + +{ + int nDataSize = GDALGetDataTypeSize( eDataType ) / 8; + GByte *pabyWrkBlock; + int i; + + CPLAssert( nBlockYOff == 0 ); + +/* -------------------------------------------------------------------- */ +/* Seek to profile. */ +/* -------------------------------------------------------------------- */ + if( VSIFSeekL( fpImage, + 256 + nBlockXOff * nDataSize * nRasterYSize, + SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + ".bt Seek failed:%s", VSIStrerror( errno ) ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Allocate working buffer. */ +/* -------------------------------------------------------------------- */ + pabyWrkBlock = (GByte *) CPLMalloc(nDataSize * nRasterYSize); + +/* -------------------------------------------------------------------- */ +/* Vertical flip data into work buffer, since GDAL expects */ +/* values from top to bottom, but in .bt they are bottom to */ +/* top. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nRasterYSize; i++ ) + { + memcpy( pabyWrkBlock + (nRasterYSize - i - 1) * nDataSize, + ((GByte *) pImage) + i * nDataSize, nDataSize ); + } + +/* -------------------------------------------------------------------- */ +/* Swap on MSB platforms. */ +/* -------------------------------------------------------------------- */ +#ifdef CPL_MSB + GDALSwapWords( pabyWrkBlock, nDataSize, nRasterYSize, nDataSize ); +#endif + +/* -------------------------------------------------------------------- */ +/* Read the profile. */ +/* -------------------------------------------------------------------- */ + if( VSIFWriteL( pabyWrkBlock, nDataSize, nRasterYSize, fpImage ) != + (size_t) nRasterYSize ) + { + CPLFree( pabyWrkBlock ); + CPLError( CE_Failure, CPLE_FileIO, + ".bt Write failed:%s", VSIStrerror( errno ) ); + return CE_Failure; + } + + CPLFree( pabyWrkBlock ); + + return CE_None; +} + + +double BTRasterBand::GetNoDataValue( int* pbSuccess /*= NULL */ ) +{ + if(pbSuccess != NULL) + *pbSuccess = TRUE; + + return -32768; +} + +CPLErr BTRasterBand::SetNoDataValue( double ) + +{ + return CE_None; +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +static bool approx_equals(float a, float b) +{ + const float epsilon = (float)1e-5; + return (fabs(a-b) <= epsilon); +} + +const char* BTRasterBand::GetUnitType(void) +{ + const BTDataset& ds = *(BTDataset*)poDS; + float f = ds.m_fVscale; + if(f == 1.0f) + return "m"; + if(approx_equals(f, 0.3048f)) + return "ft"; + if(approx_equals(f, 1200.0f/3937.0f)) + return "sft"; + + // todo: the BT spec allows for any value for + // ds.m_fVscale, so rigorous adherence would + // require testing for all possible units you + // may want to support, such as km, yards, miles, etc. + // But m/ft/sft seem to be the top three. + + return ""; +} + +/************************************************************************/ +/* SetUnitType() */ +/************************************************************************/ + +CPLErr BTRasterBand::SetUnitType(const char* psz) +{ + BTDataset& ds = *(BTDataset*)poDS; + if(EQUAL(psz, "m")) + ds.m_fVscale = 1.0f; + else if(EQUAL(psz, "ft")) + ds.m_fVscale = 0.3048f; + else if(EQUAL(psz, "sft")) + ds.m_fVscale = 1200.0f / 3937.0f; + else + return CE_Failure; + + + float fScale = ds.m_fVscale; + + CPL_LSBPTR32(&fScale); + + // Update header's elevation scale field. + memcpy(ds.abyHeader + 62, &fScale, sizeof(fScale)); + + ds.bHeaderModified = TRUE; + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* BTDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* BTDataset() */ +/************************************************************************/ + +BTDataset::BTDataset() +{ + fpImage = NULL; + bGeoTransformValid = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + pszProjection = NULL; + + bHeaderModified = FALSE; +} + +/************************************************************************/ +/* ~BTDataset() */ +/************************************************************************/ + +BTDataset::~BTDataset() + +{ + FlushCache(); + if( fpImage != NULL ) + VSIFCloseL( fpImage ); + CPLFree( pszProjection ); +} + +/************************************************************************/ +/* FlushCache() */ +/* */ +/* We override this to include flush out the header block. */ +/************************************************************************/ + +void BTDataset::FlushCache() + +{ + GDALDataset::FlushCache(); + + if( !bHeaderModified ) + return; + + bHeaderModified = FALSE; + + VSIFSeekL( fpImage, 0, SEEK_SET ); + VSIFWriteL( abyHeader, 256, 1, fpImage ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr BTDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + + if( bGeoTransformValid ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr BTDataset::SetGeoTransform( double *padfTransform ) + +{ + CPLErr eErr = CE_None; + + memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 ); + if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + ".bt format does not support rotational coefficients in geotransform, ignoring." ); + eErr = CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Compute bounds, and update header info. */ +/* -------------------------------------------------------------------- */ + double dfLeft, dfRight, dfTop, dfBottom; + + dfLeft = adfGeoTransform[0]; + dfRight = dfLeft + adfGeoTransform[1] * nRasterXSize; + dfTop = adfGeoTransform[3]; + dfBottom = dfTop + adfGeoTransform[5] * nRasterYSize; + + memcpy( abyHeader + 28, &dfLeft, 8 ); + memcpy( abyHeader + 36, &dfRight, 8 ); + memcpy( abyHeader + 44, &dfBottom, 8 ); + memcpy( abyHeader + 52, &dfTop, 8 ); + + CPL_LSBPTR64( abyHeader + 28 ); + CPL_LSBPTR64( abyHeader + 36 ); + CPL_LSBPTR64( abyHeader + 44 ); + CPL_LSBPTR64( abyHeader + 52 ); + + bHeaderModified = TRUE; + + return eErr; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *BTDataset::GetProjectionRef() + +{ + if( pszProjection == NULL ) + return ""; + else + return pszProjection; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr BTDataset::SetProjection( const char *pszNewProjection ) + +{ + CPLErr eErr = CE_None; + + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszNewProjection ); + + bHeaderModified = TRUE; + +/* -------------------------------------------------------------------- */ +/* Parse projection. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS( pszProjection ); + GInt16 nShortTemp; + +/* -------------------------------------------------------------------- */ +/* Linear units. */ +/* -------------------------------------------------------------------- */ + if( oSRS.IsGeographic() ) + nShortTemp = 0; + else + { + double dfLinear = oSRS.GetLinearUnits(); + + if( ABS(dfLinear - 0.3048) < 0.0000001 ) + nShortTemp = 2; + else if( ABS(dfLinear - CPLAtof(SRS_UL_US_FOOT_CONV)) < 0.00000001 ) + nShortTemp = 3; + else + nShortTemp = 1; + } + + nShortTemp = CPL_LSBWORD16( 1 ); + memcpy( abyHeader + 22, &nShortTemp, 2 ); + +/* -------------------------------------------------------------------- */ +/* UTM Zone */ +/* -------------------------------------------------------------------- */ + int bNorth; + + nShortTemp = (GInt16) oSRS.GetUTMZone( &bNorth ); + if( bNorth ) + nShortTemp = -nShortTemp; + + nShortTemp = CPL_LSBWORD16( nShortTemp ); + memcpy( abyHeader + 24, &nShortTemp, 2 ); + +/* -------------------------------------------------------------------- */ +/* Datum */ +/* -------------------------------------------------------------------- */ + if( oSRS.GetAuthorityName( "GEOGCS|DATUM" ) != NULL + && EQUAL(oSRS.GetAuthorityName( "GEOGCS|DATUM" ),"EPSG") ) + nShortTemp = (GInt16) (atoi(oSRS.GetAuthorityCode( "GEOGCS|DATUM" )) + 2000); + else + nShortTemp = -2; + nShortTemp = CPL_LSBWORD16( nShortTemp ); /* datum unknown */ + memcpy( abyHeader + 26, &nShortTemp, 2 ); + +/* -------------------------------------------------------------------- */ +/* Write out the projection to a .prj file. */ +/* -------------------------------------------------------------------- */ + const char *pszPrjFile = CPLResetExtension( GetDescription(), "prj" ); + VSILFILE * fp; + + fp = VSIFOpenL( pszPrjFile, "wt" ); + if( fp != NULL ) + { + VSIFPrintfL( fp, "%s\n", pszProjection ); + VSIFCloseL( fp ); + abyHeader[60] = 1; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to write out .prj file." ); + eErr = CE_Failure; + } + + return eErr; +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *BTDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify that this is some form of binterr file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 256) + return NULL; + + if( strncmp( (const char *) poOpenInfo->pabyHeader, "binterr", 7 ) != 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + BTDataset *poDS; + + poDS = new BTDataset(); + + memcpy( poDS->abyHeader, poOpenInfo->pabyHeader, 256 ); + +/* -------------------------------------------------------------------- */ +/* Get the version. */ +/* -------------------------------------------------------------------- */ + char szVersion[4]; + + strncpy( szVersion, (char *) (poDS->abyHeader + 7), 3 ); + szVersion[3] = '\0'; + poDS->nVersionCode = (int) (CPLAtof(szVersion) * 10); + +/* -------------------------------------------------------------------- */ +/* Extract core header information, being careful about the */ +/* version. */ +/* -------------------------------------------------------------------- */ + GInt32 nIntTemp; + GInt16 nDataSize; + GDALDataType eType; + + memcpy( &nIntTemp, poDS->abyHeader + 10, 4 ); + poDS->nRasterXSize = CPL_LSBWORD32( nIntTemp ); + + memcpy( &nIntTemp, poDS->abyHeader + 14, 4 ); + poDS->nRasterYSize = CPL_LSBWORD32( nIntTemp ); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + + memcpy( &nDataSize, poDS->abyHeader+18, 2 ); + nDataSize = CPL_LSBWORD16( nDataSize ); + + if( poDS->abyHeader[20] != 0 && nDataSize == 4 ) + eType = GDT_Float32; + else if( poDS->abyHeader[20] == 0 && nDataSize == 4 ) + eType = GDT_Int32; + else if( poDS->abyHeader[20] == 0 && nDataSize == 2 ) + eType = GDT_Int16; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + ".bt file data type unknown, got datasize=%d.", + nDataSize ); + delete poDS; + return NULL; + } + + /* + rcg, apr 7/06: read offset 62 for vert. units. + If zero, assume 1.0 as per spec. + + */ + memcpy( &poDS->m_fVscale, poDS->abyHeader + 62, 4 ); + CPL_LSBPTR32(&poDS->m_fVscale); + if(poDS->m_fVscale == 0.0f) + poDS->m_fVscale = 1.0f; + +/* -------------------------------------------------------------------- */ +/* Try to read a .prj file if it is indicated. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + + if( poDS->nVersionCode >= 12 && poDS->abyHeader[60] != 0 ) + { + const char *pszPrjFile = CPLResetExtension( poOpenInfo->pszFilename, + "prj" ); + VSILFILE *fp; + + fp = VSIFOpenL( pszPrjFile, "rt" ); + if( fp != NULL ) + { + char *pszBuffer, *pszBufPtr; + int nBufMax = 10000; + int nBytes; + + pszBuffer = (char *) CPLMalloc(nBufMax); + nBytes = VSIFReadL( pszBuffer, 1, nBufMax-1, fp ); + VSIFCloseL( fp ); + + pszBuffer[nBytes] = '\0'; + + pszBufPtr = pszBuffer; + if( oSRS.importFromWkt( &pszBufPtr ) != OGRERR_NONE ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to parse .prj file, coordinate system missing." ); + } + CPLFree( pszBuffer ); + } + } + +/* -------------------------------------------------------------------- */ +/* If we didn't find a .prj file, try to use internal info. */ +/* -------------------------------------------------------------------- */ + if( oSRS.GetRoot() == NULL ) + { + GInt16 nUTMZone, nDatum, nHUnits; + + memcpy( &nUTMZone, poDS->abyHeader + 24, 2 ); + nUTMZone = CPL_LSBWORD16( nUTMZone ); + + memcpy( &nDatum, poDS->abyHeader + 26, 2 ); + nDatum = CPL_LSBWORD16( nDatum ); + + memcpy( &nHUnits, poDS->abyHeader + 22, 2 ); + nHUnits = CPL_LSBWORD16( nHUnits ); + + if( nUTMZone != 0 ) + oSRS.SetUTM( ABS(nUTMZone), nUTMZone > 0 ); + else if( nHUnits != 0 ) + oSRS.SetLocalCS( "Unknown" ); + + if( nHUnits == 1 ) + oSRS.SetLinearUnits( SRS_UL_METER, 1.0 ); + else if( nHUnits == 2 ) + oSRS.SetLinearUnits( SRS_UL_FOOT, CPLAtof(SRS_UL_FOOT_CONV) ); + else if( nHUnits == 3 ) + oSRS.SetLinearUnits( SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV) ); + + // Translate some of the more obvious old USGS datum codes + if( nDatum == 0 ) + nDatum = 6201; + else if( nDatum == 1 ) + nDatum = 6209; + else if( nDatum == 2 ) + nDatum = 6210; + else if( nDatum == 3 ) + nDatum = 6202; + else if( nDatum == 4 ) + nDatum = 6203; + else if( nDatum == 6 ) + nDatum = 6222; + else if( nDatum == 7 ) + nDatum = 6230; + else if( nDatum == 13 ) + nDatum = 6267; + else if( nDatum == 14 ) + nDatum = 6269; + else if( nDatum == 17 ) + nDatum = 6277; + else if( nDatum == 19 ) + nDatum = 6284; + else if( nDatum == 21 ) + nDatum = 6301; + else if( nDatum == 22 ) + nDatum = 6322; + else if( nDatum == 23 ) + nDatum = 6326; + + if( !oSRS.IsLocal() ) + { + if( nDatum >= 6000 ) + { + char szName[32]; + sprintf( szName, "EPSG:%d", nDatum-2000 ); + oSRS.SetWellKnownGeogCS( szName ); + } + else + oSRS.SetWellKnownGeogCS( "WGS84" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Convert coordinate system back to WKT. */ +/* -------------------------------------------------------------------- */ + if( oSRS.GetRoot() != NULL ) + oSRS.exportToWkt( &poDS->pszProjection ); + +/* -------------------------------------------------------------------- */ +/* Get georeferencing bounds. */ +/* -------------------------------------------------------------------- */ + if( poDS->nVersionCode >= 11 ) + { + double dfLeft, dfRight, dfTop, dfBottom; + + memcpy( &dfLeft, poDS->abyHeader + 28, 8 ); + CPL_LSBPTR64( &dfLeft ); + + memcpy( &dfRight, poDS->abyHeader + 36, 8 ); + CPL_LSBPTR64( &dfRight ); + + memcpy( &dfBottom, poDS->abyHeader + 44, 8 ); + CPL_LSBPTR64( &dfBottom ); + + memcpy( &dfTop, poDS->abyHeader + 52, 8 ); + CPL_LSBPTR64( &dfTop ); + + poDS->adfGeoTransform[0] = dfLeft; + poDS->adfGeoTransform[1] = (dfRight - dfLeft) / poDS->nRasterXSize; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = dfTop; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = (dfBottom - dfTop) / poDS->nRasterYSize; + + poDS->bGeoTransformValid = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Re-open the file with the desired access. */ +/* -------------------------------------------------------------------- */ + + if( poOpenInfo->eAccess == GA_Update ) + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb+" ); + else + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + + if( poDS->fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to re-open %s within BT driver.\n", + poOpenInfo->pszFilename ); + return NULL; + } + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Create band information objects */ +/* -------------------------------------------------------------------- */ + poDS->SetBand( 1, new BTRasterBand( poDS, poDS->fpImage, eType ) ); + +#ifdef notdef + poDS->bGeoTransformValid = + GDALReadWorldFile( poOpenInfo->pszFilename, ".wld", + poDS->adfGeoTransform ); +#endif + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *BTDataset::Create( const char * pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + CPL_UNUSED char ** papszOptions ) +{ + +/* -------------------------------------------------------------------- */ +/* Verify input options. */ +/* -------------------------------------------------------------------- */ + if( eType != GDT_Int16 && eType != GDT_Int32 && eType != GDT_Float32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create .bt dataset with an illegal\n" + "data type (%s), only Int16, Int32 and Float32 supported.\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + + if( nBands != 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create .bt dataset with %d bands, only 1 supported", + nBands ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to create the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + + fp = VSIFOpenL( pszFilename, "wb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Setup base header. */ +/* -------------------------------------------------------------------- */ + unsigned char abyHeader[256]; + GInt32 nTemp; + GInt16 nShortTemp; + + memset( abyHeader, 0, 256 ); + memcpy( abyHeader, "binterr1.3", 10 ); + + nTemp = CPL_LSBWORD32( nXSize ); + memcpy( abyHeader+10, &nTemp, 4 ); + + nTemp = CPL_LSBWORD32( nYSize ); + memcpy( abyHeader+14, &nTemp, 4 ); + + nShortTemp = (GInt16) (CPL_LSBWORD16( GDALGetDataTypeSize( eType ) / 8 )); + memcpy( abyHeader + 18, &nShortTemp, 2 ); + + if( eType == GDT_Float32 ) + abyHeader[20] = 1; + else + abyHeader[20] = 0; + + nShortTemp = CPL_LSBWORD16( 1 ); /* meters */ + memcpy( abyHeader + 22, &nShortTemp, 2 ); + + nShortTemp = CPL_LSBWORD16( 0 ); /* not utm */ + memcpy( abyHeader + 24, &nShortTemp, 2 ); + + nShortTemp = CPL_LSBWORD16( -2 ); /* datum unknown */ + memcpy( abyHeader + 26, &nShortTemp, 2 ); + +/* -------------------------------------------------------------------- */ +/* Set dummy extents. */ +/* -------------------------------------------------------------------- */ + double dfLeft=0, dfRight=nXSize, dfTop=nYSize, dfBottom=0; + + memcpy( abyHeader + 28, &dfLeft, 8 ); + memcpy( abyHeader + 36, &dfRight, 8 ); + memcpy( abyHeader + 44, &dfBottom, 8 ); + memcpy( abyHeader + 52, &dfTop, 8 ); + + CPL_LSBPTR64( abyHeader + 28 ); + CPL_LSBPTR64( abyHeader + 36 ); + CPL_LSBPTR64( abyHeader + 44 ); + CPL_LSBPTR64( abyHeader + 52 ); + +/* -------------------------------------------------------------------- */ +/* Set dummy scale. */ +/* -------------------------------------------------------------------- */ + float fScale = 1.0; + + memcpy( abyHeader + 62, &fScale, 4 ); + CPL_LSBPTR32( abyHeader + 62 ); + +/* -------------------------------------------------------------------- */ +/* Write to disk. */ +/* -------------------------------------------------------------------- */ + VSIFWriteL( (void *) abyHeader, 256, 1, fp ); + if( VSIFSeekL( fp, (GDALGetDataTypeSize(eType)/8) * nXSize * (vsi_l_offset)nYSize - 1, + SEEK_CUR ) != 0 + || VSIFWriteL( abyHeader+255, 1, 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to extent file to its full size, out of disk space?" + ); + + VSIFCloseL( fp ); + VSIUnlink( pszFilename ); + return NULL; + } + + VSIFCloseL( fp ); + + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* GDALRegister_BT() */ +/************************************************************************/ + +void GDALRegister_BT() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "BT" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "BT" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "VTP .bt (Binary Terrain) 1.3 Format" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#BT" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "bt" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Int16 Int32 Float32" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = BTDataset::Open; + poDriver->pfnCreate = BTDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/cpgdataset.cpp b/bazaar/plugin/gdal/frmts/raw/cpgdataset.cpp new file mode 100644 index 000000000..b5d760e58 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/cpgdataset.cpp @@ -0,0 +1,1701 @@ +/****************************************************************************** + * $Id: cpgdataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: Polarimetric Workstation + * Purpose: Convair PolGASP data (.img/.hdr format). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: cpgdataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_CPG(void); +CPL_C_END + + +enum Interleave {BSQ, BIL, BIP}; + +/************************************************************************/ +/* ==================================================================== */ +/* CPGDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class SIRC_QSLCRasterBand; +class CPG_STOKESRasterBand; + +class CPGDataset : public RawDataset +{ + friend class SIRC_QSLCRasterBand; + friend class CPG_STOKESRasterBand; + + FILE *afpImage[4]; + + int nGCPCount; + GDAL_GCP *pasGCPList; + char *pszGCPProjection; + + double adfGeoTransform[6]; + char *pszProjection; + + int nLoadedStokesLine; + float *padfStokesMatrix; + + int nInterleave; + static int AdjustFilename( char **, const char *, const char * ); + static int FindType1( const char *pszWorkname ); + static int FindType2( const char *pszWorkname ); + static int FindType3( const char *pszWorkname ); + static GDALDataset *InitializeType1Or2Dataset( const char *pszWorkname ); + static GDALDataset *InitializeType3Dataset( const char *pszWorkname ); + CPLErr LoadStokesLine( int iLine, int bNativeOrder ); + + public: + CPGDataset(); + ~CPGDataset(); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * ); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* CPGDataset() */ +/************************************************************************/ + +CPGDataset::CPGDataset() +{ + int iBand; + + nGCPCount = 0; + pasGCPList = NULL; + pszProjection = CPLStrdup(""); + pszGCPProjection = CPLStrdup(""); + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + nLoadedStokesLine = -1; + padfStokesMatrix = NULL; + + for( iBand = 0; iBand < 4; iBand++ ) + afpImage[iBand] = NULL; +} + +/************************************************************************/ +/* ~CPGDataset() */ +/************************************************************************/ + +CPGDataset::~CPGDataset() + +{ + int iBand; + + FlushCache(); + + for( iBand = 0; iBand < 4; iBand++ ) + { + if( afpImage[iBand] != NULL ) + VSIFClose( afpImage[iBand] ); + } + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + CPLFree( pszProjection ); + CPLFree( pszGCPProjection ); + + if (padfStokesMatrix != NULL) + CPLFree( padfStokesMatrix ); + +} + +/************************************************************************/ +/* ==================================================================== */ +/* SIRC_QSLCPRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class SIRC_QSLCRasterBand : public GDALRasterBand +{ + friend class CPGDataset; + + public: + SIRC_QSLCRasterBand( CPGDataset *, int, GDALDataType ); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +#define M11 0 +#define M12 1 +#define M13 2 +#define M14 3 +#define M21 4 +#define M22 5 +#define M23 6 +#define M24 7 +#define M31 8 +#define M32 9 +#define M33 10 +#define M34 11 +#define M41 12 +#define M42 13 +#define M43 14 +#define M44 15 + +/************************************************************************/ +/* ==================================================================== */ +/* CPG_STOKESRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class CPG_STOKESRasterBand : public GDALRasterBand +{ + friend class CPGDataset; + + int nBand; + int bNativeOrder; + + public: + CPG_STOKESRasterBand( GDALDataset *poDS, int nBand, + GDALDataType eType, + int bNativeOrder ); + virtual ~CPG_STOKESRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* AdjustFilename() */ +/* */ +/* Try to find the file with the request polarization and */ +/* extention and update the passed filename accordingly. */ +/* */ +/* Return TRUE if file found otherwise FALSE. */ +/************************************************************************/ + +int CPGDataset::AdjustFilename( char **pszFilename, + const char *pszPolarization, + const char *pszExtension ) + +{ + VSIStatBuf sStatBuf; + const char *pszNewName; + char *subptr; + + /* eventually we should handle upper/lower case ... */ + + if ( EQUAL(pszPolarization,"stokes") ) + { + pszNewName = CPLResetExtension((const char *) *pszFilename, + (const char *) pszExtension); + CPLFree(*pszFilename); + *pszFilename = CPLStrdup(pszNewName); + } + else if (strlen(pszPolarization) == 2) + { + subptr = strstr(*pszFilename,"hh"); + if (subptr == NULL) + subptr = strstr(*pszFilename,"hv"); + if (subptr == NULL) + subptr = strstr(*pszFilename,"vv"); + if (subptr == NULL) + subptr = strstr(*pszFilename,"vh"); + if (subptr == NULL) + return FALSE; + + strncpy( subptr, pszPolarization, 2); + pszNewName = CPLResetExtension((const char *) *pszFilename, + (const char *) pszExtension); + CPLFree(*pszFilename); + *pszFilename = CPLStrdup(pszNewName); + + } + else + { + pszNewName = CPLResetExtension((const char *) *pszFilename, + (const char *) pszExtension); + CPLFree(*pszFilename); + *pszFilename = CPLStrdup(pszNewName); + } + return VSIStat( *pszFilename, &sStatBuf ) == 0; +} + +/************************************************************************/ +/* Search for the various types of Convair filesets */ +/* Return TRUE for a match, FALSE for no match */ +/************************************************************************/ +int CPGDataset::FindType1( const char *pszFilename ) +{ + int nNameLen; + + nNameLen = strlen(pszFilename); + + if ((strstr(pszFilename,"sso") == NULL) && + (strstr(pszFilename,"polgasp") == NULL)) + return FALSE; + + if (( strlen(pszFilename) < 5) || + (!EQUAL(pszFilename+nNameLen-4,".hdr") + && !EQUAL(pszFilename+nNameLen-4,".img"))) + return FALSE; + + /* Expect all bands and headers to be present */ + char* pszTemp = CPLStrdup(pszFilename); + + int bNotFound = !AdjustFilename( &pszTemp, "hh", "img" ) + || !AdjustFilename( &pszTemp, "hh", "hdr" ) + || !AdjustFilename( &pszTemp, "hv", "img" ) + || !AdjustFilename( &pszTemp, "hv", "hdr" ) + || !AdjustFilename( &pszTemp, "vh", "img" ) + || !AdjustFilename( &pszTemp, "vh", "hdr" ) + || !AdjustFilename( &pszTemp, "vv", "img" ) + || !AdjustFilename( &pszTemp, "vv", "hdr" ); + + CPLFree(pszTemp); + + if (bNotFound) + return FALSE; + + return TRUE; +} + +int CPGDataset::FindType2( const char *pszFilename ) +{ + int nNameLen; + + nNameLen = strlen( pszFilename ); + + if (( strlen(pszFilename) < 9) || + (!EQUAL(pszFilename+nNameLen-8,"SIRC.hdr") + && !EQUAL(pszFilename+nNameLen-8,"SIRC.img"))) + return FALSE; + + char* pszTemp = CPLStrdup(pszFilename); + int bNotFound = !AdjustFilename( &pszTemp, "", "img" ) + || !AdjustFilename( &pszTemp, "", "hdr" ); + CPLFree(pszTemp); + + if (bNotFound) + return FALSE; + + return TRUE; +} + +int CPGDataset::FindType3( const char *pszFilename ) +{ + int nNameLen; + + nNameLen = strlen( pszFilename ); + + if ((strstr(pszFilename,"sso") == NULL) && + (strstr(pszFilename,"polgasp") == NULL)) + return FALSE; + + if (( strlen(pszFilename) < 9) || + (!EQUAL(pszFilename+nNameLen-4,".img") + && !EQUAL(pszFilename+nNameLen-8,".img_def"))) + return FALSE; + + char* pszTemp = CPLStrdup(pszFilename); + int bNotFound = !AdjustFilename( &pszTemp, "stokes", "img" ) + || !AdjustFilename( &pszTemp, "stokes", "img_def" ); + CPLFree(pszTemp); + + if (bNotFound) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* LoadStokesLine() */ +/************************************************************************/ + +CPLErr CPGDataset::LoadStokesLine( int iLine, int bNativeOrder ) + +{ + int offset, nBytesToRead, band_index; + int nDataSize = GDALGetDataTypeSize(GDT_Float32)/8; + + if( iLine == nLoadedStokesLine ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* allocate working buffers if we don't have them already. */ +/* -------------------------------------------------------------------- */ + if( padfStokesMatrix == NULL ) + { + padfStokesMatrix = (float *) CPLMalloc(sizeof(float) * nRasterXSize*16); + } + +/* -------------------------------------------------------------------- */ +/* Load all the pixel data associated with this scanline. */ +/* Retains same interleaving as original dataset. */ +/* -------------------------------------------------------------------- */ + if ( nInterleave == BIP ) + { + offset = nRasterXSize*iLine*nDataSize*16; + nBytesToRead = nDataSize*nRasterXSize*16; + if (( VSIFSeek( afpImage[0], offset, SEEK_SET ) != 0 ) || + (int) VSIFRead( ( GByte *) padfStokesMatrix, 1, nBytesToRead, + afpImage[0] ) != nBytesToRead ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Error reading %d bytes of Stokes Convair at offset %d.\n" + "Reading file %s failed.", + nBytesToRead, offset, GetDescription() ); + CPLFree( padfStokesMatrix ); + padfStokesMatrix = NULL; + nLoadedStokesLine = -1; + return CE_Failure; + } + } + else if ( nInterleave == BIL ) + { + for ( band_index = 0; band_index < 16; band_index++) + { + offset = nDataSize * (nRasterXSize*iLine + + nRasterXSize*band_index); + nBytesToRead = nDataSize*nRasterXSize; + if (( VSIFSeek( afpImage[0], offset, SEEK_SET ) != 0 ) || + (int) VSIFRead( + ( GByte *) padfStokesMatrix + nBytesToRead*band_index, + 1, nBytesToRead, + afpImage[0] ) != nBytesToRead ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Error reading %d bytes of Stokes Convair at offset %d.\n" + "Reading file %s failed.", + nBytesToRead, offset, GetDescription() ); + CPLFree( padfStokesMatrix ); + padfStokesMatrix = NULL; + nLoadedStokesLine = -1; + return CE_Failure; + + } + } + } + else + { + for ( band_index = 0; band_index < 16; band_index++) + { + offset = nDataSize * (nRasterXSize*iLine + + nRasterXSize*nRasterYSize*band_index); + nBytesToRead = nDataSize*nRasterXSize; + if (( VSIFSeek( afpImage[0], offset, SEEK_SET ) != 0 ) || + (int) VSIFRead( + ( GByte *) padfStokesMatrix + nBytesToRead*band_index, + 1, nBytesToRead, + afpImage[0] ) != nBytesToRead ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Error reading %d bytes of Stokes Convair at offset %d.\n" + "Reading file %s failed.", + nBytesToRead, offset, GetDescription() ); + CPLFree( padfStokesMatrix ); + padfStokesMatrix = NULL; + nLoadedStokesLine = -1; + return CE_Failure; + + } + } + } + + if (!bNativeOrder) + GDALSwapWords( padfStokesMatrix,nDataSize,nRasterXSize*16, nDataSize ); + + nLoadedStokesLine = iLine; + + return CE_None; +} + +/************************************************************************/ +/* Parse header information and initialize dataset for the */ +/* appropriate Convair dataset style. */ +/* Returns dataset if successful; NULL if there was a problem. */ +/************************************************************************/ + +GDALDataset* CPGDataset::InitializeType1Or2Dataset( const char *pszFilename ) +{ + +/* -------------------------------------------------------------------- */ +/* Read the .hdr file (the hh one for the .sso and polgasp cases) */ +/* and parse it. */ +/* -------------------------------------------------------------------- */ + char **papszHdrLines; + int iLine; + int nLines = 0, nSamples = 0; + int nError = 0; + int nNameLen = 0; + + /* Parameters required for pseudo-geocoding. GCPs map */ + /* slant range to ground range at 16 points. */ + int iGeoParamsFound = 0, itransposed = 0; + double dfaltitude = 0.0, dfnear_srd = 0.0; + double dfsample_size = 0.0, dfsample_size_az = 0.0; + + /* Parameters in geogratis geocoded images */ + int iUTMParamsFound = 0, iUTMZone=0 /* , iCorner=0 */; + double dfnorth = 0.0, dfeast = 0.0; + + char* pszWorkname = CPLStrdup(pszFilename); + AdjustFilename( &pszWorkname, "hh", "hdr" ); + papszHdrLines = CSLLoad( pszWorkname ); + + for( iLine = 0; papszHdrLines && papszHdrLines[iLine] != NULL; iLine++ ) + { + char **papszTokens = CSLTokenizeString( papszHdrLines[iLine] ); + + /* Note: some cv580 file seem to have comments with #, hence the >= + * instead of = for token checking, and the equalN for the corner. + */ + + if( CSLCount( papszTokens ) < 2 ) + { + /* ignore */; + } + else if ( ( CSLCount( papszTokens ) >= 3 ) && + EQUAL(papszTokens[0],"reference") && + EQUAL(papszTokens[1],"north") ) + { + dfnorth = CPLAtof(papszTokens[2]); + iUTMParamsFound++; + } + else if ( ( CSLCount( papszTokens ) >= 3 ) && + EQUAL(papszTokens[0],"reference") && + EQUAL(papszTokens[1],"east") ) + { + dfeast = CPLAtof(papszTokens[2]); + iUTMParamsFound++; + } + else if ( ( CSLCount( papszTokens ) >= 5 ) && + EQUAL(papszTokens[0],"reference") && + EQUAL(papszTokens[1],"projection") && + EQUAL(papszTokens[2],"UTM") && + EQUAL(papszTokens[3],"zone") ) + { + iUTMZone = atoi(papszTokens[4]); + iUTMParamsFound++; + } + else if ( ( CSLCount( papszTokens ) >= 3 ) && + EQUAL(papszTokens[0],"reference") && + EQUAL(papszTokens[1],"corner") && + EQUALN(papszTokens[2],"Upper_Left",10) ) + { + /* iCorner = 0; */ + iUTMParamsFound++; + } + else if( EQUAL(papszTokens[0],"number_lines") ) + nLines = atoi(papszTokens[1]); + + else if( EQUAL(papszTokens[0],"number_samples") ) + nSamples = atoi(papszTokens[1]); + + else if( (EQUAL(papszTokens[0],"header_offset") + && atoi(papszTokens[1]) != 0) + || (EQUAL(papszTokens[0],"number_channels") + && (atoi(papszTokens[1]) != 1) + && (atoi(papszTokens[1]) != 10)) + || (EQUAL(papszTokens[0],"datatype") + && atoi(papszTokens[1]) != 1) + || (EQUAL(papszTokens[0],"number_format") + && !EQUAL(papszTokens[1],"float32") + && !EQUAL(papszTokens[1],"int8"))) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Keyword %s has value %s which does not match CPG driver expectation.", + papszTokens[0], papszTokens[1] ); + nError = 1; + } + else if( EQUAL(papszTokens[0],"altitude") ) + { + dfaltitude = CPLAtof(papszTokens[1]); + iGeoParamsFound++; + } + else if( EQUAL(papszTokens[0],"near_srd") ) + { + dfnear_srd = CPLAtof(papszTokens[1]); + iGeoParamsFound++; + } + + else if( EQUAL(papszTokens[0],"sample_size") ) + { + dfsample_size = CPLAtof(papszTokens[1]); + iGeoParamsFound++; + iUTMParamsFound++; + } + else if( EQUAL(papszTokens[0],"sample_size_az") ) + { + dfsample_size_az = CPLAtof(papszTokens[1]); + iGeoParamsFound++; + iUTMParamsFound++; + } + else if( EQUAL(papszTokens[0],"transposed") ) + { + itransposed = atoi(papszTokens[1]); + iGeoParamsFound++; + iUTMParamsFound++; + } + + + + CSLDestroy( papszTokens ); + } + CSLDestroy( papszHdrLines ); +/* -------------------------------------------------------------------- */ +/* Check for successful completion. */ +/* -------------------------------------------------------------------- */ + if( nError ) + { + CPLFree(pszWorkname); + return NULL; + } + + if( nLines <= 0 || nSamples <= 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Did not find valid number_lines or number_samples keywords in %s.", + pszWorkname ); + CPLFree(pszWorkname); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Initialize dataset. */ +/* -------------------------------------------------------------------- */ + int iBand=0; + CPGDataset *poDS; + + poDS = new CPGDataset(); + + poDS->nRasterXSize = nSamples; + poDS->nRasterYSize = nLines; + +/* -------------------------------------------------------------------- */ +/* Open the four bands. */ +/* -------------------------------------------------------------------- */ + static const char *apszPolarizations[4] = { "hh", "hv", "vv", "vh" }; + + nNameLen = strlen(pszWorkname); + + if ( EQUAL(pszWorkname+nNameLen-7,"IRC.hdr") || + EQUAL(pszWorkname+nNameLen-7,"IRC.img") ) + { + + AdjustFilename( &pszWorkname, "" , "img" ); + poDS->afpImage[0] = VSIFOpen( pszWorkname, "rb" ); + if( poDS->afpImage[0] == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open .img file: %s", + pszWorkname ); + CPLFree(pszWorkname); + delete poDS; + return NULL; + } + for( iBand = 0; iBand < 4; iBand++ ) + { + SIRC_QSLCRasterBand *poBand; + + poBand = new SIRC_QSLCRasterBand( poDS, iBand+1, GDT_CFloat32 ); + poDS->SetBand( iBand+1, poBand ); + poBand->SetMetadataItem( "POLARIMETRIC_INTERP", + apszPolarizations[iBand] ); + } + } + else + { + for( iBand = 0; iBand < 4; iBand++ ) + { + RawRasterBand *poBand; + + AdjustFilename( &pszWorkname, apszPolarizations[iBand], "img" ); + + poDS->afpImage[iBand] = VSIFOpen( pszWorkname, "rb" ); + if( poDS->afpImage[iBand] == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open .img file: %s", + pszWorkname ); + CPLFree(pszWorkname); + delete poDS; + return NULL; + } + + poBand = + new RawRasterBand( poDS, iBand+1, poDS->afpImage[iBand], + 0, 8, 8*nSamples, + GDT_CFloat32, !CPL_IS_LSB, FALSE ); + poDS->SetBand( iBand+1, poBand ); + + poBand->SetMetadataItem( "POLARIMETRIC_INTERP", + apszPolarizations[iBand] ); + } + } + + /* Set an appropriate matrix representation metadata item for the set */ + if ( poDS->GetRasterCount() == 4 ) { + poDS->SetMetadataItem( "MATRIX_REPRESENTATION", "SCATTERING" ); + } + +/* ------------------------------------------------------------------------- */ +/* Add georeferencing or pseudo-geocoding, if enough information found. */ +/* ------------------------------------------------------------------------- */ + if (iUTMParamsFound == 7) + { + OGRSpatialReference oUTM; + double dfnorth_center; + + poDS->adfGeoTransform[1] = 0.0; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = 0.0; + + if (itransposed == 1) + { + printf("Warning- did not have a convair SIRC-style test dataset\n" + "with transposed=1 for testing. Georefencing may be wrong.\n"); + dfnorth_center = dfnorth - nSamples*dfsample_size/2.0; + poDS->adfGeoTransform[0] = dfeast; + poDS->adfGeoTransform[2] = dfsample_size_az; + poDS->adfGeoTransform[3] = dfnorth; + poDS->adfGeoTransform[4] = -1*dfsample_size; + } + else + { + dfnorth_center = dfnorth - nLines*dfsample_size/2.0; + poDS->adfGeoTransform[0] = dfeast; + poDS->adfGeoTransform[1] = dfsample_size_az; + poDS->adfGeoTransform[3] = dfnorth; + poDS->adfGeoTransform[5] = -1*dfsample_size; + } + if (dfnorth_center < 0) + oUTM.SetUTM(iUTMZone, 0); + else + oUTM.SetUTM(iUTMZone, 1); + + /* Assuming WGS84 */ + oUTM.SetWellKnownGeogCS( "WGS84" ); + CPLFree( poDS->pszProjection ); + poDS->pszProjection = NULL; + oUTM.exportToWkt( &(poDS->pszProjection) ); + + + + } + else if (iGeoParamsFound == 5) + { + int ngcp; + double dfgcpLine, dfgcpPixel, dfgcpX, dfgcpY, dftemp; + + poDS->nGCPCount = 16; + poDS->pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),poDS->nGCPCount); + GDALInitGCPs(poDS->nGCPCount, poDS->pasGCPList); + + for( ngcp = 0; ngcp < 16; ngcp ++ ) + { + char szID[32]; + + sprintf(szID,"%d",ngcp+1); + if (itransposed == 1) + { + if (ngcp < 4) + dfgcpPixel = 0.0; + else if (ngcp < 8) + dfgcpPixel = nSamples/3.0; + else if (ngcp < 12) + dfgcpPixel = 2.0*nSamples/3.0; + else + dfgcpPixel = nSamples; + + dfgcpLine = nLines*( ngcp % 4 )/3.0; + + dftemp = dfnear_srd + (dfsample_size*dfgcpLine); + /* -1 so that 0,0 maps to largest Y */ + dfgcpY = -1*sqrt( dftemp*dftemp - dfaltitude*dfaltitude ); + dfgcpX = dfgcpPixel*dfsample_size_az; + + } + else + { + if (ngcp < 4) + dfgcpLine = 0.0; + else if (ngcp < 8) + dfgcpLine = nLines/3.0; + else if (ngcp < 12) + dfgcpLine = 2.0*nLines/3.0; + else + dfgcpLine = nLines; + + dfgcpPixel = nSamples*( ngcp % 4 )/3.0; + + dftemp = dfnear_srd + (dfsample_size*dfgcpPixel); + dfgcpX = sqrt( dftemp*dftemp - dfaltitude*dfaltitude ); + dfgcpY = (nLines - dfgcpLine)*dfsample_size_az; + + } + poDS->pasGCPList[ngcp].dfGCPX = dfgcpX; + poDS->pasGCPList[ngcp].dfGCPY = dfgcpY; + poDS->pasGCPList[ngcp].dfGCPZ = 0.0; + + poDS->pasGCPList[ngcp].dfGCPPixel = dfgcpPixel; + poDS->pasGCPList[ngcp].dfGCPLine = dfgcpLine; + + CPLFree(poDS->pasGCPList[ngcp].pszId); + poDS->pasGCPList[ngcp].pszId = CPLStrdup( szID ); + + } + + CPLFree(poDS->pszGCPProjection); + poDS->pszGCPProjection = CPLStrdup("LOCAL_CS[\"Ground range view / unreferenced meters\",UNIT[\"Meter\",1.0]]"); + + } + + CPLFree(pszWorkname); + + return poDS; +} + +GDALDataset *CPGDataset::InitializeType3Dataset( const char *pszFilename ) +{ + + char **papszHdrLines; + int iLine, iBytesPerPixel = 0, iInterleave=-1; + int nLines = 0, nSamples = 0, nBands = 0; + int nError = 0; + + /* Parameters in geogratis geocoded images */ + int iUTMParamsFound = 0, iUTMZone=0; + double dfnorth = 0.0, dfeast = 0.0, dfOffsetX = 0.0, dfOffsetY = 0.0; + double dfxsize = 0.0, dfysize = 0.0; + + char* pszWorkname = CPLStrdup(pszFilename); + AdjustFilename( &pszWorkname, "stokes", "img_def" ); + papszHdrLines = CSLLoad( pszWorkname ); + + for( iLine = 0; papszHdrLines && papszHdrLines[iLine] != NULL; iLine++ ) + { + char **papszTokens = CSLTokenizeString2( papszHdrLines[iLine], + " \t", + CSLT_HONOURSTRINGS & CSLT_ALLOWEMPTYTOKENS ); + + /* Note: some cv580 file seem to have comments with #, hence the >= + * instead of = for token checking, and the equalN for the corner. + */ + + if ( ( CSLCount( papszTokens ) >= 3 ) && + EQUAL(papszTokens[0],"data") && + EQUAL(papszTokens[1],"organization:")) + { + + if( EQUALN(papszTokens[2], "BSQ", 3) ) + iInterleave = BSQ; + else if( EQUALN(papszTokens[2], "BIL", 3) ) + iInterleave = BIL; + else if( EQUALN(papszTokens[2], "BIP", 3) ) + iInterleave = BIP; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "The interleaving type of the file (%s) is not supported.", + papszTokens[2] ); + nError = 1; + } + + } + else if ( ( CSLCount( papszTokens ) >= 3 ) && + EQUAL(papszTokens[0],"data") && + EQUAL(papszTokens[1],"state:") ) + { + + if( !EQUALN(papszTokens[2], "RAW", 3) && + !EQUALN(papszTokens[2], "GEO", 3) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The data state of the file (%s) is not supported.\n. Only RAW and GEO are currently recognized.", + papszTokens[2] ); + nError = 1; + } + + + } + else if ( ( CSLCount( papszTokens ) >= 4 ) && + EQUAL(papszTokens[0],"data") && + EQUAL(papszTokens[1],"origin") && + EQUAL(papszTokens[2],"point:") ) + { + if (!EQUALN(papszTokens[3], "Upper_Left", 10)) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unexpected value (%s) for data origin point- expect Upper_Left.", + papszTokens[3] ); + nError = 1; + } + iUTMParamsFound++; + } + else if ( ( CSLCount( papszTokens ) >= 5 ) && + EQUAL(papszTokens[0],"map") && + EQUAL(papszTokens[1],"projection:") && + EQUAL(papszTokens[2],"UTM") && + EQUAL(papszTokens[3],"zone") ) + { + iUTMZone = atoi(papszTokens[4]); + iUTMParamsFound++; + } + else if ( ( CSLCount( papszTokens ) >= 4 ) && + EQUAL(papszTokens[0],"project") && + EQUAL(papszTokens[1],"origin:") ) + { + dfeast = CPLAtof(papszTokens[2]); + dfnorth = CPLAtof(papszTokens[3]); + iUTMParamsFound+=2; + } + else if ( ( CSLCount( papszTokens ) >= 4 ) && + EQUAL(papszTokens[0],"file") && + EQUAL(papszTokens[1],"start:")) + { + dfOffsetX = CPLAtof(papszTokens[2]); + dfOffsetY = CPLAtof(papszTokens[3]); + iUTMParamsFound+=2; + } + else if ( ( CSLCount( papszTokens ) >= 6 ) && + EQUAL(papszTokens[0],"pixel") && + EQUAL(papszTokens[1],"size") && + EQUAL(papszTokens[2],"on") && + EQUAL(papszTokens[3],"ground:")) + { + dfxsize = CPLAtof(papszTokens[4]); + dfysize = CPLAtof(papszTokens[5]); + iUTMParamsFound+=2; + + } + else if ( ( CSLCount( papszTokens ) >= 4 ) && + EQUAL(papszTokens[0],"number") && + EQUAL(papszTokens[1],"of") && + EQUAL(papszTokens[2],"pixels:")) + { + nSamples = atoi(papszTokens[3]); + } + else if ( ( CSLCount( papszTokens ) >= 4 ) && + EQUAL(papszTokens[0],"number") && + EQUAL(papszTokens[1],"of") && + EQUAL(papszTokens[2],"lines:")) + { + nLines = atoi(papszTokens[3]); + } + else if ( ( CSLCount( papszTokens ) >= 4 ) && + EQUAL(papszTokens[0],"number") && + EQUAL(papszTokens[1],"of") && + EQUAL(papszTokens[2],"bands:")) + { + nBands = atoi(papszTokens[3]); + if ( nBands != 16) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Number of bands has a value %s which does not match CPG driver\nexpectation (expect a value of 16).", + papszTokens[3] ); + nError = 1; + } + } + else if ( ( CSLCount( papszTokens ) >= 4 ) && + EQUAL(papszTokens[0],"bytes") && + EQUAL(papszTokens[1],"per") && + EQUAL(papszTokens[2],"pixel:")) + { + iBytesPerPixel = atoi(papszTokens[3]); + if (iBytesPerPixel != 4) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Bytes per pixel has a value %s which does not match CPG driver\nexpectation (expect a value of 4).", + papszTokens[1] ); + nError = 1; + } + } + CSLDestroy( papszTokens ); + } + + CSLDestroy( papszHdrLines ); + +/* -------------------------------------------------------------------- */ +/* Check for successful completion. */ +/* -------------------------------------------------------------------- */ + if( nError ) + { + CPLFree(pszWorkname); + return NULL; + } + + if (!GDALCheckDatasetDimensions(nSamples, nLines) || + !GDALCheckBandCount(nBands, FALSE) || iInterleave == -1 || + iBytesPerPixel == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s is missing a required parameter (number of pixels, number of lines,\nnumber of bands, bytes per pixel, or data organization).", + pszWorkname ); + CPLFree(pszWorkname); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Initialize dataset. */ +/* -------------------------------------------------------------------- */ + int iBand=0; + CPGDataset *poDS; + + poDS = new CPGDataset(); + + poDS->nRasterXSize = nSamples; + poDS->nRasterYSize = nLines; + + if( iInterleave == BSQ ) + poDS->nInterleave = BSQ; + else if( iInterleave == BIL ) + poDS->nInterleave = BIL; + else + poDS->nInterleave = BIP; + +/* -------------------------------------------------------------------- */ +/* Open the 16 bands. */ +/* -------------------------------------------------------------------- */ + + AdjustFilename( &pszWorkname, "stokes" , "img" ); + poDS->afpImage[0] = VSIFOpen( pszWorkname, "rb" ); + if( poDS->afpImage[0] == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open .img file: %s", + pszWorkname ); + CPLFree(pszWorkname); + delete poDS; + return NULL; + } + for( iBand = 0; iBand < 16; iBand++ ) + { + CPG_STOKESRasterBand *poBand; + + poBand = new CPG_STOKESRasterBand( poDS, iBand+1, GDT_CFloat32, + !CPL_IS_LSB ); + poDS->SetBand( iBand+1, poBand ); + } + +/* -------------------------------------------------------------------- */ +/* Set appropriate MATRIX_REPRESENTATION. */ +/* -------------------------------------------------------------------- */ + if ( poDS->GetRasterCount() == 6 ) { + poDS->SetMetadataItem( "MATRIX_REPRESENTATION", + "COVARIANCE" ); + } + + +/* ------------------------------------------------------------------------- */ +/* Add georeferencing, if enough information found. */ +/* ------------------------------------------------------------------------- */ + if (iUTMParamsFound == 8) + { + OGRSpatialReference oUTM; + double dfnorth_center; + + + dfnorth_center = dfnorth - nLines*dfysize/2.0; + poDS->adfGeoTransform[0] = dfeast + dfOffsetX; + poDS->adfGeoTransform[1] = dfxsize; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = dfnorth + dfOffsetY; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -1*dfysize; + + if (dfnorth_center < 0) + oUTM.SetUTM(iUTMZone, 0); + else + oUTM.SetUTM(iUTMZone, 1); + + /* Assuming WGS84 */ + oUTM.SetWellKnownGeogCS( "WGS84" ); + CPLFree( poDS->pszProjection ); + poDS->pszProjection = NULL; + oUTM.exportToWkt( &(poDS->pszProjection) ); + } + + return poDS; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *CPGDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Is this a PolGASP fileset? We expect fileset to follow */ +/* one of these patterns: */ +/* 1) hh.img, hh.hdr, */ +/* hv.img, hv.hdr, */ +/* vh.img, vh.hdr, */ +/* vv.img, vv.hdr, */ +/* where or should contain the */ +/* substring "sso" or "polgasp" */ +/* 2) SIRC.hdr and SIRC.img */ +/* 3) .img and .img_def */ +/* where should contain the */ +/* substring "sso" or "polgasp" */ +/* -------------------------------------------------------------------- */ + int nNameLen = strlen(poOpenInfo->pszFilename); + int CPGType = 0; + + if ( FindType1( poOpenInfo->pszFilename )) + CPGType = 1; + else if ( FindType2( poOpenInfo->pszFilename )) + CPGType = 2; + + /* Stokes matrix convair data: not quite working yet- something + * is wrong in the interpretation of the matrix elements in terms + * of hh, hv, vv, vh. Data will load if the next two lines are + * uncommented, but values will be incorrect. Expect C11 = hh*conj(hh), + * C12 = hh*conj(hv), etc. Used geogratis data in both scattering + * matrix and stokes format for comparison. + */ + //else if ( FindType3( poOpenInfo->pszFilename )) + // CPGType = 3; + + /* Set working name back to original */ + + if ( CPGType == 0 ) + { + nNameLen = strlen(poOpenInfo->pszFilename); + if ( (nNameLen > 8) && + ( ( strstr(poOpenInfo->pszFilename,"sso") != NULL ) || + ( strstr(poOpenInfo->pszFilename,"polgasp") != NULL ) ) && + ( EQUAL(poOpenInfo->pszFilename+nNameLen-4,"img") || + EQUAL(poOpenInfo->pszFilename+nNameLen-4,"hdr") || + EQUAL(poOpenInfo->pszFilename+nNameLen-7,"img_def") ) ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Apparent attempt to open Convair PolGASP data failed as\n" + "one or more of the required files is missing (eight files\n" + "are expected for scattering matrix format, two for Stokes)." ); + } + else if ( (nNameLen > 8) && + ( strstr(poOpenInfo->pszFilename,"SIRC") != NULL ) && + ( EQUAL(poOpenInfo->pszFilename+nNameLen-4,"img") || + EQUAL(poOpenInfo->pszFilename+nNameLen-4,"hdr"))) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Apparent attempt to open SIRC Convair PolGASP data failed \n" + "as one of the expected files is missing (hdr or img)!" ); + } + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The CPG driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + /* Read the header info and create the dataset */ + CPGDataset *poDS; + + if ( CPGType < 3 ) + poDS = (CPGDataset *) InitializeType1Or2Dataset( poOpenInfo->pszFilename ); + else + poDS = (CPGDataset *) InitializeType3Dataset( poOpenInfo->pszFilename ); + + if (poDS == NULL) + return NULL; +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + // Need to think about this. + // poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + + return( poDS ); +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int CPGDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *CPGDataset::GetGCPProjection() + +{ + return pszGCPProjection; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *CPGDataset::GetGCPs() + +{ + return pasGCPList; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *CPGDataset::GetProjectionRef() + +{ + return( pszProjection ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr CPGDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return( CE_None ); +} + +/************************************************************************/ +/* SIRC_QSLCRasterBand() */ +/************************************************************************/ + +SIRC_QSLCRasterBand::SIRC_QSLCRasterBand( CPGDataset *poGDS, int nBand, + GDALDataType eType ) + +{ + this->poDS = poGDS; + this->nBand = nBand; + + eDataType = eType; + + nBlockXSize = poGDS->nRasterXSize; + nBlockYSize = 1; + + if( nBand == 1 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "HH" ); + else if( nBand == 2 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "HV" ); + else if( nBand == 3 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "VH" ); + else if( nBand == 4 ) + SetMetadataItem( "POLARIMETRIC_INTERP", "VV" ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +/* From: http://southport.jpl.nasa.gov/software/dcomp/dcomp.html + +ysca = sqrt{ [ (Byte(2) / 254 ) + 1.5] 2Byte(1) } + +Re(SHH) = byte(3) ysca/127 + +Im(SHH) = byte(4) ysca/127 + +Re(SHV) = byte(5) ysca/127 + +Im(SHV) = byte(6) ysca/127 + +Re(SVH) = byte(7) ysca/127 + +Im(SVH) = byte(8) ysca/127 + +Re(SVV) = byte(9) ysca/127 + +Im(SVV) = byte(10) ysca/127 + +*/ + +CPLErr SIRC_QSLCRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + int offset, nBytesPerSample=10; + GByte *pabyRecord; + CPGDataset *poGDS = (CPGDataset *) poDS; + static float afPowTable[256]; + static int bPowTableInitialized = FALSE; + + offset = nBlockXSize* nBlockYOff*nBytesPerSample; + +/* -------------------------------------------------------------------- */ +/* Load all the pixel data associated with this scanline. */ +/* -------------------------------------------------------------------- */ + int nBytesToRead = nBytesPerSample * nBlockXSize; + + pabyRecord = (GByte *) CPLMalloc( nBytesToRead ); + + if( VSIFSeek( poGDS->afpImage[0], offset, SEEK_SET ) != 0 + || (int) VSIFRead( pabyRecord, 1, nBytesToRead, + poGDS->afpImage[0] ) != nBytesToRead ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Error reading %d bytes of SIRC Convair at offset %d.\n" + "Reading file %s failed.", + nBytesToRead, offset, poGDS->GetDescription() ); + CPLFree( pabyRecord ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Initialize our power table if this is our first time through. */ +/* -------------------------------------------------------------------- */ + if( !bPowTableInitialized ) + { + int i; + + bPowTableInitialized = TRUE; + + for( i = 0; i < 256; i++ ) + { + afPowTable[i] = (float) pow( 2.0, i-128 ); + } + } + +/* -------------------------------------------------------------------- */ +/* Copy the desired band out based on the size of the type, and */ +/* the interleaving mode. */ +/* -------------------------------------------------------------------- */ + int iX; + + for( iX = 0; iX < nBlockXSize; iX++ ) + { + unsigned char *pabyGroup = pabyRecord + iX * nBytesPerSample; + signed char *Byte = (signed char*)pabyGroup-1; /* A ones based alias */ + double dfReSHH, dfImSHH, dfReSHV, dfImSHV, + dfReSVH, dfImSVH, dfReSVV, dfImSVV, dfScale; + + dfScale = sqrt( (Byte[2] / 254 + 1.5) * afPowTable[Byte[1] + 128] ); + + if( nBand == 1 ) + { + dfReSHH = Byte[3] * dfScale / 127.0; + dfImSHH = Byte[4] * dfScale / 127.0; + + ((float *) pImage)[iX*2 ] = (float) dfReSHH; + ((float *) pImage)[iX*2+1] = (float) dfImSHH; + } + else if( nBand == 2 ) + { + dfReSHV = Byte[5] * dfScale / 127.0; + dfImSHV = Byte[6] * dfScale / 127.0; + + ((float *) pImage)[iX*2 ] = (float) dfReSHV; + ((float *) pImage)[iX*2+1] = (float) dfImSHV; + } + else if( nBand == 3 ) + { + dfReSVH = Byte[7] * dfScale / 127.0; + dfImSVH = Byte[8] * dfScale / 127.0; + + ((float *) pImage)[iX*2 ] = (float) dfReSVH; + ((float *) pImage)[iX*2+1] = (float) dfImSVH; + } + else if( nBand == 4 ) + { + dfReSVV = Byte[9] * dfScale / 127.0; + dfImSVV = Byte[10]* dfScale / 127.0; + + ((float *) pImage)[iX*2 ] = (float) dfReSVV; + ((float *) pImage)[iX*2+1] = (float) dfImSVV; + } + } + + CPLFree( pabyRecord ); + + return CE_None; +} + +/************************************************************************/ +/* CPG_STOKESRasterBand() */ +/************************************************************************/ + +CPG_STOKESRasterBand::CPG_STOKESRasterBand( GDALDataset *poDS, int nBand, + GDALDataType eType, + int bNativeOrder ) + +{ + static const char *apszPolarizations[16] = { "Covariance_11", + "Covariance_12", + "Covariance_13", + "Covariance_14", + "Covariance_21", + "Covariance_22", + "Covariance_23", + "Covariance_24", + "Covariance_31", + "Covariance_32", + "Covariance_33", + "Covariance_34", + "Covariance_41", + "Covariance_42", + "Covariance_43", + "Covariance_44" }; + + this->poDS = poDS; + this->nBand = nBand; + this->eDataType = eType; + this->bNativeOrder = bNativeOrder; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + SetMetadataItem( "POLARIMETRIC_INTERP",apszPolarizations[nBand-1] ); + SetDescription( apszPolarizations[nBand-1] ); +} + +/************************************************************************/ +/* ~CPG_STOKESRasterBand() */ +/************************************************************************/ + +CPG_STOKESRasterBand::~CPG_STOKESRasterBand() + +{ +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +/* Convert from Stokes to Covariance representation */ + +CPLErr CPG_STOKESRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) + +{ + int iPixel; + int m11, /* m12, */ m13, m14, /* m21, */ m22, m23, m24, step; + int m31, m32, m33, m34, m41, m42, m43, m44; + CPGDataset *poGDS = (CPGDataset *) poDS; + float *M; + float *pafLine; + CPLErr eErr; + + CPLAssert( nBlockXOff == 0 ); + + eErr = poGDS->LoadStokesLine(nBlockYOff, bNativeOrder); + if( eErr != CE_None ) + return eErr; + + M = poGDS->padfStokesMatrix; + pafLine = ( float * ) pImage; + + if ( poGDS->nInterleave == BIP) + { + step = 16; + m11 = M11; + // m12 = M12; + m13 = M13; + m14 = M14; + // m21 = M21; + m22 = M22; + m23 = M23; + m24 = M24; + m31 = M31; + m32 = M32; + m33 = M33; + m34 = M34; + m41 = M41; + m42 = M42; + m43 = M43; + m44 = M44; + } + else + { + step = 1; + m11=0; + // m12=nRasterXSize; + m13=nRasterXSize*2; + m14=nRasterXSize*3; + // m21=nRasterXSize*4; + m22=nRasterXSize*5; + m23=nRasterXSize*6; + m24=nRasterXSize*7; + m31=nRasterXSize*8; + m32=nRasterXSize*9; + m33=nRasterXSize*10; + m34=nRasterXSize*11; + m41=nRasterXSize*12; + m42=nRasterXSize*13; + m43=nRasterXSize*14; + m44=nRasterXSize*15; + } + if ( nBand == 1 ) /* C11 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m11]-M[m22]-M[m33]+M[m44]; + pafLine[iPixel*2+1] = 0.0; + m11 += step; + m22 += step; + m33 += step; + m44 += step; + } + } + else if ( nBand == 2 ) /* C12 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m13]-M[m23]; + pafLine[iPixel*2+1] = M[m14]-M[m24]; + m13 += step; + m23 += step; + m14 += step; + m24 += step; + } + } + else if ( nBand == 3 ) /* C13 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m33]-M[m44]; + pafLine[iPixel*2+1] = M[m43]+M[m34]; + m33 += step; + m44 += step; + m43 += step; + m34 += step; + } + } + else if ( nBand == 4 ) /* C14 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m31]-M[m32]; + pafLine[iPixel*2+1] = M[m41]-M[m42]; + m31 += step; + m32 += step; + m41 += step; + m42 += step; + } + } + else if ( nBand == 5 ) /* C21 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m13]-M[m23]; + pafLine[iPixel*2+1] = M[m24]-M[m14]; + m13 += step; + m23 += step; + m14 += step; + m24 += step; + } + } + else if ( nBand == 6 ) /* C22 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m11]+M[m22]-M[m33]-M[m44]; + pafLine[iPixel*2+1] = 0.0; + m11 += step; + m22 += step; + m33 += step; + m44 += step; + } + } + else if ( nBand == 7 ) /* C23 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m31]+M[m32]; + pafLine[iPixel*2+1] = M[m41]+M[m42]; + m31 += step; + m32 += step; + m41 += step; + m42 += step; + } + } + else if ( nBand == 8 ) /* C24 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m33]+M[m44]; + pafLine[iPixel*2+1] = M[m43]-M[m34]; + m33 += step; + m44 += step; + m43 += step; + m34 += step; + } + } + else if ( nBand == 9 ) /* C31 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m33]-M[m44]; + pafLine[iPixel*2+1] = -1*M[m43]-M[m34]; + m33 += step; + m44 += step; + m43 += step; + m34 += step; + } + } + else if ( nBand == 10 ) /* C32 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m31]+M[m32]; + pafLine[iPixel*2+1] = -1*M[m41]-M[m42]; + m31 += step; + m32 += step; + m41 += step; + m42 += step; + } + } + else if ( nBand == 11 ) /* C33 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m11]+M[m22]+M[m33]+M[m44]; + pafLine[iPixel*2+1] = 0.0; + m11 += step; + m22 += step; + m33 += step; + m44 += step; + } + + } + else if ( nBand == 12 ) /* C34 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m13]-M[m23]; + pafLine[iPixel*2+1] = -1*M[m14]-M[m24]; + m13 += step; + m23 += step; + m14 += step; + m24 += step; + } + } + else if ( nBand == 13 ) /* C41 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m31]-M[m32]; + pafLine[iPixel*2+1] = M[m42]-M[m41]; + m31 += step; + m32 += step; + m41 += step; + m42 += step; + } + } + else if ( nBand == 14 ) /* C42 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m33]+M[m44]; + pafLine[iPixel*2+1] = M[m34]-M[m43]; + m33 += step; + m44 += step; + m43 += step; + m34 += step; + } + } + else if ( nBand == 15 ) /* C43 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m13]-M[m23]; + pafLine[iPixel*2+1] = M[m14]+M[m24]; + m13 += step; + m23 += step; + m14 += step; + m24 += step; + } + } + else /* C44 */ + { + for ( iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafLine[iPixel*2+0] = M[m11]-M[m22]+M[m33]-M[m44]; + pafLine[iPixel*2+1] = 0.0; + m11 += step; + m22 += step; + m33 += step; + m44 += step; + } + } + + return CE_None; +} + +/************************************************************************/ +/* GDALRegister_CPG() */ +/************************************************************************/ + +void GDALRegister_CPG() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "CPG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "CPG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Convair PolGASP" ); + + poDriver->pfnOpen = CPGDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/ctable2dataset.cpp b/bazaar/plugin/gdal/frmts/raw/ctable2dataset.cpp new file mode 100644 index 000000000..ea39ce982 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/ctable2dataset.cpp @@ -0,0 +1,461 @@ +/****************************************************************************** + * $Id$ + * + * Project: Horizontal Datum Formats + * Purpose: Implementation of the CTable2 format, a PROJ.4 specific format + * that is more compact (due to a lack of unused error band) than NTv2 + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2012, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id$"); + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* ==================================================================== */ +/* CTable2Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +class CTable2Dataset : public RawDataset +{ + public: + VSILFILE *fpImage; // image data file. + + double adfGeoTransform[6]; + + public: + CTable2Dataset(); + ~CTable2Dataset(); + + virtual CPLErr SetGeoTransform( double * padfTransform ); + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual const char *GetProjectionRef(); + virtual void FlushCache(void); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* CTable2Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* CTable2Dataset() */ +/************************************************************************/ + +CTable2Dataset::CTable2Dataset() +{ + fpImage = NULL; +} + +/************************************************************************/ +/* ~CTable2Dataset() */ +/************************************************************************/ + +CTable2Dataset::~CTable2Dataset() + +{ + FlushCache(); + + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void CTable2Dataset::FlushCache() + +{ + RawDataset::FlushCache(); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int CTable2Dataset::Identify( GDALOpenInfo *poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 64 ) + return FALSE; + + if( !EQUALN((const char *)poOpenInfo->pabyHeader + 0, "CTABLE V2", 9 ) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *CTable2Dataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + CTable2Dataset *poDS; + + poDS = new CTable2Dataset(); + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + CPLString osFilename; + osFilename = poOpenInfo->pszFilename; + + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fpImage = VSIFOpenL( osFilename, "rb" ); + else + poDS->fpImage = VSIFOpenL( osFilename, "rb+" ); + + if( poDS->fpImage == NULL ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the file header. */ +/* -------------------------------------------------------------------- */ + char achHeader[160]; + CPLString osDescription; + + VSIFSeekL( poDS->fpImage, 0, SEEK_SET ); + VSIFReadL( achHeader, 1, 160, poDS->fpImage ); + + achHeader[16+79] = '\0'; + osDescription = (const char *) achHeader+16; + osDescription.Trim(); + poDS->SetMetadataItem( "DESCRIPTION", osDescription ); + +/* -------------------------------------------------------------------- */ +/* Convert from LSB to local machine byte order. */ +/* -------------------------------------------------------------------- */ + CPL_LSBPTR64( achHeader + 96 ); + CPL_LSBPTR64( achHeader + 104 ); + CPL_LSBPTR64( achHeader + 112 ); + CPL_LSBPTR64( achHeader + 120 ); + CPL_LSBPTR32( achHeader + 128 ); + CPL_LSBPTR32( achHeader + 132 ); + +/* -------------------------------------------------------------------- */ +/* Extract size, and geotransform. */ +/* -------------------------------------------------------------------- */ + GUInt32 nRasterXSize, nRasterYSize; + + memcpy( &nRasterXSize, achHeader + 128, 4 ); + memcpy( &nRasterYSize, achHeader + 132, 4 ); + poDS->nRasterXSize = nRasterXSize; + poDS->nRasterYSize = nRasterYSize; + + int i; + double adfValues[4]; + memcpy( adfValues, achHeader + 96, sizeof(double)*4 ); + + for( i = 0; i < 4; i++ ) + adfValues[i] *= 180/M_PI; // Radians to degrees. + + + poDS->adfGeoTransform[0] = adfValues[0] - adfValues[2]*0.5; + poDS->adfGeoTransform[1] = adfValues[2]; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = adfValues[1] + adfValues[3]*(nRasterYSize-0.5); + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -adfValues[3]; + +/* -------------------------------------------------------------------- */ +/* Setup the bands. */ +/* -------------------------------------------------------------------- */ + RawRasterBand *poBand = + new RawRasterBand( poDS, 1, poDS->fpImage, + 160 + 4 + nRasterXSize * (nRasterYSize-1) * 2 * 4, + 8, -8 * nRasterXSize, + GDT_Float32, CPL_IS_LSB, TRUE, FALSE ); + poBand->SetDescription( "Latitude Offset (radians)" ); + poDS->SetBand( 1, poBand ); + + poBand = + new RawRasterBand( poDS, 2, poDS->fpImage, + 160 + nRasterXSize * (nRasterYSize-1) * 2 * 4, + 8, -8 * nRasterXSize, + GDT_Float32, CPL_IS_LSB, TRUE, FALSE ); + poBand->SetDescription( "Longitude Offset (radians)" ); + poDS->SetBand( 2, poBand ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr CTable2Dataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr CTable2Dataset::SetGeoTransform( double * padfTransform ) + +{ + if( eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Unable to update geotransform on readonly file." ); + return CE_Failure; + } + + if( padfTransform[2] != 0.0 || padfTransform[4] != 0.0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Rotated and sheared geotransforms not supported for CTable2."); + return CE_Failure; + } + + memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 ); + +/* -------------------------------------------------------------------- */ +/* Update grid header. */ +/* -------------------------------------------------------------------- */ + double dfValue; + char achHeader[160]; + double dfDegToRad = M_PI / 180.0; + + // read grid header + VSIFSeekL( fpImage, 0, SEEK_SET ); + VSIFReadL( achHeader, 1, sizeof(achHeader), fpImage ); + + // lower left origin (longitude, center of pixel, radians) + dfValue = (adfGeoTransform[0] + adfGeoTransform[1]*0.5) * dfDegToRad; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 96, &dfValue, 8 ); + + // lower left origin (latitude, center of pixel, radians) + dfValue = (adfGeoTransform[3] + adfGeoTransform[5] * (nRasterYSize-0.5)) * dfDegToRad; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 104, &dfValue, 8 ); + + // pixel width (radians) + dfValue = adfGeoTransform[1] * dfDegToRad; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 112, &dfValue, 8 ); + + // pixel height (radians) + dfValue = adfGeoTransform[5] * -1 * dfDegToRad; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 120, &dfValue, 8 ); + + // write grid header. + VSIFSeekL( fpImage, 0, SEEK_SET ); + VSIFWriteL( achHeader, 11, 16, fpImage ); + + return CE_None; +} + + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *CTable2Dataset::GetProjectionRef() + +{ + return SRS_WKT_WGS84; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *CTable2Dataset::Create( const char * pszFilename, + int nXSize, + int nYSize, + CPL_UNUSED int nBands, + GDALDataType eType, + char ** papszOptions ) +{ + if( eType != GDT_Float32 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Attempt to create CTable2 file with unsupported data type '%s'.", + GDALGetDataTypeName( eType ) ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to open or create file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + + fp = VSIFOpenL( pszFilename, "wb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a file header, with a defaulted georeferencing. */ +/* -------------------------------------------------------------------- */ + char achHeader[160]; + int nValue32; + double dfValue; + + memset( achHeader, 0, sizeof(achHeader)); + + memcpy( achHeader+0, "CTABLE V2.0 ", 16 ); + + if( CSLFetchNameValue( papszOptions, "DESCRIPTION" ) != NULL ) + strncpy( achHeader + 16, + CSLFetchNameValue( papszOptions, "DESCRIPTION" ), + 80 ); + + // lower left origin (longitude, center of pixel, radians) + dfValue = 0; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 96, &dfValue, 8 ); + + // lower left origin (latitude, center of pixel, radians) + dfValue = 0; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 104, &dfValue, 8 ); + + // pixel width (radians) + dfValue = 0.01 * M_PI / 180.0; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 112, &dfValue, 8 ); + + // pixel height (radians) + dfValue = 0.01 * M_PI / 180.0; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 120, &dfValue, 8 ); + + // raster width in pixels + nValue32 = nXSize; + CPL_LSBPTR32( &nValue32 ); + memcpy( achHeader + 128, &nValue32, 4 ); + + // raster width in pixels + nValue32 = nYSize; + CPL_LSBPTR32( &nValue32 ); + memcpy( achHeader + 132, &nValue32, 4 ); + + VSIFWriteL( achHeader, 1, sizeof(achHeader), fp ); + +/* -------------------------------------------------------------------- */ +/* Write zeroed grid data. */ +/* -------------------------------------------------------------------- */ + float *pafLine = (float *) CPLCalloc(sizeof(float)*2,nXSize); + int i; + + for( i = 0; i < nYSize; i++ ) + { + if( (int)VSIFWriteL( pafLine, sizeof(float)*2, nXSize, fp ) != nXSize ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Write failed at line %d, perhaps the disk is full?", + i ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup and return. */ +/* -------------------------------------------------------------------- */ + CPLFree( pafLine ); + + VSIFCloseL( fp ); + + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* GDALRegister_CTable2() */ +/************************************************************************/ + +void GDALRegister_CTable2() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "CTable2" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "CTable2" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "CTable2 Datum Grid Shift" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Float32" ); + + poDriver->pfnOpen = CTable2Dataset::Open; + poDriver->pfnIdentify = CTable2Dataset::Identify; + poDriver->pfnCreate = CTable2Dataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/dipxdataset.cpp b/bazaar/plugin/gdal/frmts/raw/dipxdataset.cpp new file mode 100644 index 000000000..b47334dac --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/dipxdataset.cpp @@ -0,0 +1,357 @@ +/****************************************************************************** + * $Id: dipxdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: GDAL + * Purpose: Implementation for ELAS DIPEx format variant. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2006, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: dipxdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +CPL_C_START +void GDALRegister_DIPEx(void); +CPL_C_END + +typedef struct { + GInt32 NBIH; /* bytes in header, normaly 1024 */ + GInt32 NBPR; /* bytes per data record (all bands of scanline) */ + GInt32 IL; /* initial line - normally 1 */ + GInt32 LL; /* last line */ + GInt32 IE; /* initial element (pixel), normally 1 */ + GInt32 LE; /* last element (pixel) */ + GInt32 NC; /* number of channels (bands) */ + GInt32 H4322; /* header record identifier - always 4322. */ + char unused1[40]; + GByte IH19[4];/* data type, and size flags */ + GInt32 IH20; /* number of secondary headers */ + GInt32 SRID; + char unused2[12]; + double YOffset; + double XOffset; + double YPixSize; + double XPixSize; + double Matrix[4]; + char unused3[344]; + GUInt16 ColorTable[256]; /* RGB packed with 4 bits each */ + char unused4[32]; +} DIPExHeader; + +/************************************************************************/ +/* ==================================================================== */ +/* DIPExDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class DIPExRasterBand; + +class DIPExDataset : public GDALPamDataset +{ + friend class DIPExRasterBand; + + VSILFILE *fp; + CPLString osSRS; + + DIPExHeader sHeader; + + GDALDataType eRasterDataType; + + double adfGeoTransform[6]; + + public: + DIPExDataset(); + ~DIPExDataset(); + + virtual CPLErr GetGeoTransform( double * ); + + virtual const char *GetProjectionRef( void ); + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* DIPExDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* DIPExDataset() */ +/************************************************************************/ + +DIPExDataset::DIPExDataset() + +{ + fp = NULL; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~DIPExDataset() */ +/************************************************************************/ + +DIPExDataset::~DIPExDataset() + +{ + if (fp) + VSIFCloseL( fp ); + fp = NULL; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *DIPExDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* First we check to see if the file has the expected header */ +/* bytes. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 256 ) + return NULL; + + if( CPL_LSBWORD32(*((GInt32 *) (poOpenInfo->pabyHeader+0))) != 1024 ) + return NULL; + + if( CPL_LSBWORD32(*((GInt32 *) (poOpenInfo->pabyHeader+28))) != 4322 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + DIPExDataset *poDS; + const char *pszAccess; + + if( poOpenInfo->eAccess == GA_Update ) + pszAccess = "r+b"; + else + pszAccess = "rb"; + + poDS = new DIPExDataset(); + + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, pszAccess ); + if( poDS->fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to open `%s' with acces `%s' failed.\n", + poOpenInfo->pszFilename, pszAccess ); + delete poDS; + return NULL; + } + + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Read the header information. */ +/* -------------------------------------------------------------------- */ + if( VSIFReadL( &(poDS->sHeader), 1024, 1, poDS->fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Attempt to read 1024 byte header filed on file %s\n", + poOpenInfo->pszFilename ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Extract information of interest from the header. */ +/* -------------------------------------------------------------------- */ + int nStart, nEnd, nDIPExDataType, nBytesPerSample; + int nLineOffset; + + nLineOffset = CPL_LSBWORD32( poDS->sHeader.NBPR ); + + nStart = CPL_LSBWORD32( poDS->sHeader.IL ); + nEnd = CPL_LSBWORD32( poDS->sHeader.LL ); + poDS->nRasterYSize = nEnd - nStart + 1; + + nStart = CPL_LSBWORD32( poDS->sHeader.IE ); + nEnd = CPL_LSBWORD32( poDS->sHeader.LE ); + poDS->nRasterXSize = nEnd - nStart + 1; + + poDS->nBands = CPL_LSBWORD32( poDS->sHeader.NC ); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || + !GDALCheckBandCount(poDS->nBands, FALSE)) + { + delete poDS; + return NULL; + } + + nDIPExDataType = (poDS->sHeader.IH19[1] & 0x7e) >> 2; + nBytesPerSample = poDS->sHeader.IH19[0]; + + if( nDIPExDataType == 0 && nBytesPerSample == 1 ) + poDS->eRasterDataType = GDT_Byte; + else if( nDIPExDataType == 1 && nBytesPerSample == 1 ) + poDS->eRasterDataType = GDT_Byte; + else if( nDIPExDataType == 16 && nBytesPerSample == 4 ) + poDS->eRasterDataType = GDT_Float32; + else if( nDIPExDataType == 17 && nBytesPerSample == 8 ) + poDS->eRasterDataType = GDT_Float64; + else + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised image data type %d, with BytesPerSample=%d.\n", + nDIPExDataType, nBytesPerSample ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 0; iBand < poDS->nBands; iBand++ ) + { + poDS->SetBand( iBand+1, + new RawRasterBand( poDS, iBand+1, poDS->fp, + 1024 + iBand * nLineOffset, + nBytesPerSample, + nLineOffset * poDS->nBands, + poDS->eRasterDataType, + CPL_IS_LSB, TRUE ) ); + } + +/* -------------------------------------------------------------------- */ +/* Extract the projection coordinates, if present. */ +/* -------------------------------------------------------------------- */ + CPL_LSBPTR64(&(poDS->sHeader.XPixSize)); + CPL_LSBPTR64(&(poDS->sHeader.YPixSize)); + CPL_LSBPTR64(&(poDS->sHeader.XOffset)); + CPL_LSBPTR64(&(poDS->sHeader.YOffset)); + + if( poDS->sHeader.XOffset != 0 ) + { + poDS->adfGeoTransform[0] = poDS->sHeader.XOffset; + poDS->adfGeoTransform[1] = poDS->sHeader.XPixSize; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = poDS->sHeader.YOffset; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -1.0 * ABS(poDS->sHeader.YPixSize); + + poDS->adfGeoTransform[0] -= poDS->adfGeoTransform[1] * 0.5; + poDS->adfGeoTransform[3] -= poDS->adfGeoTransform[5] * 0.5; + } + else + { + poDS->adfGeoTransform[0] = 0.0; + poDS->adfGeoTransform[1] = 1.0; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = 1.0; + } + +/* -------------------------------------------------------------------- */ +/* Look for SRID. */ +/* -------------------------------------------------------------------- */ + CPL_LSBPTR32( &(poDS->sHeader.SRID) ); + + if( poDS->sHeader.SRID > 0 && poDS->sHeader.SRID < 33000 ) + { + OGRSpatialReference oSR; + + if( oSR.importFromEPSG( poDS->sHeader.SRID ) == OGRERR_NONE ) + { + char *pszWKT = NULL; + oSR.exportToWkt( &pszWKT ); + poDS->osSRS = pszWKT; + CPLFree( pszWKT ); + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return( poDS ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *DIPExDataset::GetProjectionRef() + +{ + return osSRS.c_str(); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr DIPExDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + + return( CE_None ); +} + +/************************************************************************/ +/* GDALRegister_DIPEx() */ +/************************************************************************/ + +void GDALRegister_DIPEx() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "DIPEx" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "DIPEx" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "DIPEx" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = DIPExDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/doq1dataset.cpp b/bazaar/plugin/gdal/frmts/raw/doq1dataset.cpp new file mode 100644 index 000000000..76ef58d10 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/doq1dataset.cpp @@ -0,0 +1,407 @@ +/****************************************************************************** + * $Id: doq1dataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: USGS DOQ Driver (First Generation Format) + * Purpose: Implementation of DOQ1Dataset + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: doq1dataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +static double DOQGetField( unsigned char *, int ); +static void DOQGetDescription( GDALDataset *, unsigned char * ); + +CPL_C_START +void GDALRegister_DOQ1(void); +CPL_C_END + +#define UTM_FORMAT \ +"PROJCS[\"%s / UTM zone %dN\",GEOGCS[%s,PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",%d],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],%s]" + +#define WGS84_DATUM \ +"\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]]" + +#define WGS72_DATUM \ +"\"WGS 72\",DATUM[\"WGS_1972\",SPHEROID[\"NWL 10D\",6378135,298.26]]" + +#define NAD27_DATUM \ +"\"NAD27\",DATUM[\"North_American_Datum_1927\",SPHEROID[\"Clarke 1866\",6378206.4,294.978698213901]]" + +#define NAD83_DATUM \ +"\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\",6378137,298.257222101]]" + +/************************************************************************/ +/* ==================================================================== */ +/* DOQ1Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +class DOQ1Dataset : public RawDataset +{ + VSILFILE *fpImage; // image data file. + + double dfULX, dfULY; + double dfXPixelSize, dfYPixelSize; + + char *pszProjection; + + public: + DOQ1Dataset(); + ~DOQ1Dataset(); + + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef( void ); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* DOQ1Dataset() */ +/************************************************************************/ + +DOQ1Dataset::DOQ1Dataset() +{ + pszProjection = NULL; + fpImage = NULL; +} + +/************************************************************************/ +/* ~DOQ1Dataset() */ +/************************************************************************/ + +DOQ1Dataset::~DOQ1Dataset() + +{ + FlushCache(); + + CPLFree( pszProjection ); + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr DOQ1Dataset::GetGeoTransform( double * padfTransform ) + +{ + padfTransform[0] = dfULX; + padfTransform[1] = dfXPixelSize; + padfTransform[2] = 0.0; + padfTransform[3] = dfULY; + padfTransform[4] = 0.0; + padfTransform[5] = -1 * dfYPixelSize; + + return( CE_None ); +} + +/************************************************************************/ +/* GetProjectionString() */ +/************************************************************************/ + +const char *DOQ1Dataset::GetProjectionRef() + +{ + return pszProjection; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *DOQ1Dataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int nWidth, nHeight, nBandStorage, nBandTypes; + +/* -------------------------------------------------------------------- */ +/* We assume the user is pointing to the binary (ie. .bil) file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 212 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Attempt to extract a few key values from the header. */ +/* -------------------------------------------------------------------- */ + nWidth = (int) DOQGetField(poOpenInfo->pabyHeader + 150, 6); + nHeight = (int) DOQGetField(poOpenInfo->pabyHeader + 144, 6); + nBandStorage = (int) DOQGetField(poOpenInfo->pabyHeader + 162, 3); + nBandTypes = (int) DOQGetField(poOpenInfo->pabyHeader + 156, 3); + +/* -------------------------------------------------------------------- */ +/* Do these values look coherent for a DOQ file? It would be */ +/* nice to do a more comprehensive test than this! */ +/* -------------------------------------------------------------------- */ + if( nWidth < 500 || nWidth > 25000 + || nHeight < 500 || nHeight > 25000 + || nBandStorage < 0 || nBandStorage > 4 + || nBandTypes < 1 || nBandTypes > 9 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Check the configuration. We don't currently handle all */ +/* variations, only the common ones. */ +/* -------------------------------------------------------------------- */ + if( nBandTypes > 5 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "DOQ Data Type (%d) is not a supported configuration.\n", + nBandTypes ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The DOQ1 driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + DOQ1Dataset *poDS; + + poDS = new DOQ1Dataset(); + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = nWidth; + poDS->nRasterYSize = nHeight; + + poDS->fpImage = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + if (poDS->fpImage == NULL) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Compute layout of data. */ +/* -------------------------------------------------------------------- */ + int nSkipBytes, nBytesPerPixel=0, nBytesPerLine, i; + + if( nBandTypes < 5 ) + nBytesPerPixel = 1; + else if( nBandTypes == 5 ) + nBytesPerPixel = 3; + + nBytesPerLine = nBytesPerPixel * nWidth; + nSkipBytes = 4 * nBytesPerLine; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = nBytesPerPixel; + for( i = 0; i < poDS->nBands; i++ ) + { + poDS->SetBand( i+1, + new RawRasterBand( poDS, i+1, poDS->fpImage, + nSkipBytes + i, nBytesPerPixel, nBytesPerLine, + GDT_Byte, TRUE, TRUE ) ); + } + +/* -------------------------------------------------------------------- */ +/* Set the description. */ +/* -------------------------------------------------------------------- */ + DOQGetDescription(poDS, poOpenInfo->pabyHeader); + +/* -------------------------------------------------------------------- */ +/* Establish the projection string. */ +/* -------------------------------------------------------------------- */ + if( ((int) DOQGetField(poOpenInfo->pabyHeader + 195, 3)) != 1 ) + poDS->pszProjection = VSIStrdup(""); + else + { + const char *pszDatumLong, *pszDatumShort; + const char *pszUnits; + int nZone; + + nZone = (int) DOQGetField(poOpenInfo->pabyHeader + 198, 6); + + if( ((int) DOQGetField(poOpenInfo->pabyHeader + 204, 3)) == 1 ) + pszUnits = "UNIT[\"US survey foot\",0.304800609601219]"; + else + pszUnits = "UNIT[\"metre\",1]"; + + switch( (int) DOQGetField(poOpenInfo->pabyHeader + 167, 2) ) + { + case 1: + pszDatumLong = NAD27_DATUM; + pszDatumShort = "NAD 27"; + break; + + case 2: + pszDatumLong = WGS72_DATUM; + pszDatumShort = "WGS 72"; + break; + + case 3: + pszDatumLong = WGS84_DATUM; + pszDatumShort = "WGS 84"; + break; + + case 4: + pszDatumLong = NAD83_DATUM; + pszDatumShort = "NAD 83"; + break; + + default: + pszDatumLong = "DATUM[\"unknown\"]"; + pszDatumShort = "unknown"; + break; + } + + poDS->pszProjection = + CPLStrdup(CPLSPrintf( UTM_FORMAT, pszDatumShort, nZone, + pszDatumLong, nZone * 6 - 183, pszUnits )); + } + +/* -------------------------------------------------------------------- */ +/* Read the georeferencing information. */ +/* -------------------------------------------------------------------- */ + unsigned char abyRecordData[500]; + + if( VSIFSeekL( poDS->fpImage, nBytesPerLine * 2, SEEK_SET ) != 0 + || VSIFReadL(abyRecordData,sizeof(abyRecordData),1,poDS->fpImage) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Header read error on %s.\n", + poOpenInfo->pszFilename ); + delete poDS; + return NULL; + } + + poDS->dfULX = DOQGetField( abyRecordData + 288, 24 ); + poDS->dfULY = DOQGetField( abyRecordData + 312, 24 ); + + if( VSIFSeekL( poDS->fpImage, nBytesPerLine * 3, SEEK_SET ) != 0 + || VSIFReadL(abyRecordData,sizeof(abyRecordData),1,poDS->fpImage) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Header read error on %s.\n", + poOpenInfo->pszFilename ); + delete poDS; + return NULL; + } + + poDS->dfXPixelSize = DOQGetField( abyRecordData + 59, 12 ); + poDS->dfYPixelSize = DOQGetField( abyRecordData + 71, 12 ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_DOQ1() */ +/************************************************************************/ + +void GDALRegister_DOQ1() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "DOQ1" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "DOQ1" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "USGS DOQ (Old Style)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#DOQ1" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = DOQ1Dataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + +/************************************************************************/ +/* DOQGetField() */ +/************************************************************************/ + +static double DOQGetField( unsigned char *pabyData, int nBytes ) + +{ + char szWork[128]; + int i; + + strncpy( szWork, (const char *) pabyData, nBytes ); + szWork[nBytes] = '\0'; + + for( i = 0; i < nBytes; i++ ) + { + if( szWork[i] == 'D' || szWork[i] == 'd' ) + szWork[i] = 'E'; + } + + return CPLAtof(szWork); +} + +/************************************************************************/ +/* DOQGetDescription() */ +/************************************************************************/ + +static void DOQGetDescription( GDALDataset *poDS, unsigned char *pabyData ) + +{ + char szWork[128]; + int i = 0; + + memset( szWork, ' ', 128 ); + strncpy( szWork, "USGS GeoTIFF DOQ 1:12000 Q-Quad of ", 35 ); + strncpy( szWork + 35, (const char *) pabyData + 0, 38 ); + while ( *(szWork + 72 - i) == ' ' ) { + i++; + } + i--; + strncpy( szWork + 73 - i, (const char *) pabyData + 38, 2 ); + strncpy( szWork + 76 - i, (const char *) pabyData + 44, 2 ); + szWork[77-i] = '\0'; + + poDS->SetMetadataItem( "DOQ_DESC", szWork ); +} diff --git a/bazaar/plugin/gdal/frmts/raw/doq2dataset.cpp b/bazaar/plugin/gdal/frmts/raw/doq2dataset.cpp new file mode 100644 index 000000000..aab668651 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/doq2dataset.cpp @@ -0,0 +1,447 @@ +/****************************************************************************** + * $Id: doq2dataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: USGS DOQ Driver (Second Generation Format) + * Purpose: Implementation of DOQ2Dataset + * Author: Derrick J Brashear, shadow@dementia.org + * + ****************************************************************************** + * Copyright (c) 2000, Derrick J Brashear + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: doq2dataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_DOQ2(void); +CPL_C_END + +#define UTM_FORMAT \ +"PROJCS[\"%s / UTM zone %dN\",GEOGCS[%s,PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",%d],PARAMETER[\"scale_factor\",0.9996],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],%s]" + +#define WGS84_DATUM \ +"\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563]]" + +#define WGS72_DATUM \ +"\"WGS 72\",DATUM[\"WGS_1972\",SPHEROID[\"NWL 10D\",6378135,298.26]]" + +#define NAD27_DATUM \ +"\"NAD27\",DATUM[\"North_American_Datum_1927\",SPHEROID[\"Clarke 1866\",6378206.4,294.978698213901]]" + +#define NAD83_DATUM \ +"\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\",6378137,298.257222101]]" + +/************************************************************************/ +/* ==================================================================== */ +/* DOQ2Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +class DOQ2Dataset : public RawDataset +{ + VSILFILE *fpImage; // image data file. + + double dfULX, dfULY; + double dfXPixelSize, dfYPixelSize; + + char *pszProjection; + + public: + DOQ2Dataset(); + ~DOQ2Dataset(); + + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef( void ); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* DOQ2Dataset() */ +/************************************************************************/ + +DOQ2Dataset::DOQ2Dataset() +{ + pszProjection = NULL; + fpImage = NULL; +} + +/************************************************************************/ +/* ~DOQ2Dataset() */ +/************************************************************************/ + +DOQ2Dataset::~DOQ2Dataset() + +{ + FlushCache(); + + CPLFree( pszProjection ); + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr DOQ2Dataset::GetGeoTransform( double * padfTransform ) + +{ + padfTransform[0] = dfULX; + padfTransform[1] = dfXPixelSize; + padfTransform[2] = 0.0; + padfTransform[3] = dfULY; + padfTransform[4] = 0.0; + padfTransform[5] = -1 * dfYPixelSize; + + return( CE_None ); +} + +/************************************************************************/ +/* GetProjectionString() */ +/************************************************************************/ + +const char *DOQ2Dataset::GetProjectionRef() + +{ + return pszProjection; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *DOQ2Dataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int nWidth=0, nHeight=0, nBandStorage=0, nBandTypes=0; + +/* -------------------------------------------------------------------- */ +/* We assume the user is pointing to the binary (ie. .bil) file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 212 ) + return NULL; + + int nLineCount = 0; + const char *pszLine; + int nBytesPerPixel=0; + const char *pszDatumLong=NULL, *pszDatumShort=NULL; + const char *pszUnits=NULL; + char *pszQuadname = NULL; + char *pszQuadquad = NULL; + char *pszState = NULL; + int nZone=0, nProjType=0; + int nSkipBytes=0, nBytesPerLine, i, nBandCount = 0; + double dfULXMap=0.0, dfULYMap = 0.0; + double dfXDim=0.0, dfYDim=0.0; + char **papszMetadata = NULL; + + if(! EQUALN((const char *) poOpenInfo->pabyHeader, + "BEGIN_USGS_DOQ_HEADER", 21) ) + return NULL; + + VSILFILE* fp = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + if (fp == NULL) + return NULL; + + /* read and discard the first line */ + pszLine = CPLReadLineL( fp ); + + while( (pszLine = CPLReadLineL( fp )) != NULL ) + { + char **papszTokens; + + nLineCount++; + + if( EQUAL(pszLine,"END_USGS_DOQ_HEADER") ) + break; + + papszTokens = CSLTokenizeString( pszLine ); + if( CSLCount( papszTokens ) < 2 ) + { + CSLDestroy( papszTokens ); + break; + } + + if( EQUAL(papszTokens[0],"SAMPLES_AND_LINES") && CSLCount(papszTokens) >= 3 ) + { + nWidth = atoi(papszTokens[1]); + nHeight = atoi(papszTokens[2]); + } + else if( EQUAL(papszTokens[0],"BYTE_COUNT") ) + { + nSkipBytes = atoi(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"XY_ORIGIN") && CSLCount(papszTokens) >= 3 ) + { + dfULXMap = CPLAtof(papszTokens[1]); + dfULYMap = CPLAtof(papszTokens[2]); + } + else if( EQUAL(papszTokens[0],"HORIZONTAL_RESOLUTION") ) + { + dfXDim = dfYDim = CPLAtof(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"BAND_ORGANIZATION") ) + { + if( EQUAL(papszTokens[1],"SINGLE FILE") ) + nBandStorage = 1; + if( EQUAL(papszTokens[1],"BSQ") ) + nBandStorage = 1; + if( EQUAL(papszTokens[1],"BIL") ) + nBandStorage = 1; + if( EQUAL(papszTokens[1],"BIP") ) + nBandStorage = 4; + } + else if( EQUAL(papszTokens[0],"BAND_CONTENT") ) + { + if( EQUAL(papszTokens[1],"BLACK&WHITE") ) + nBandTypes = 1; + else if( EQUAL(papszTokens[1],"COLOR") ) + nBandTypes = 5; + else if( EQUAL(papszTokens[1],"RGB") ) + nBandTypes = 5; + else if( EQUAL(papszTokens[1],"RED") ) + nBandTypes = 5; + else if( EQUAL(papszTokens[1],"GREEN") ) + nBandTypes = 5; + else if( EQUAL(papszTokens[1],"BLUE") ) + nBandTypes = 5; + + nBandCount++; + } + else if( EQUAL(papszTokens[0],"BITS_PER_PIXEL") ) + { + nBytesPerPixel = (atoi(papszTokens[1]) / 8); + } + else if( EQUAL(papszTokens[0],"HORIZONTAL_COORDINATE_SYSTEM") ) + { + if( EQUAL(papszTokens[1],"UTM") ) + nProjType = 1; + else if( EQUAL(papszTokens[1],"SPCS") ) + nProjType = 2; + else if( EQUAL(papszTokens[1],"GEOGRAPHIC") ) + nProjType = 0; + } + else if( EQUAL(papszTokens[0],"COORDINATE_ZONE") ) + { + nZone = atoi(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"HORIZONTAL_UNITS") ) + { + if( EQUAL(papszTokens[1],"METERS") ) + pszUnits = "UNIT[\"metre\",1]"; + else if( EQUAL(papszTokens[1],"FEET") ) + pszUnits = "UNIT[\"US survey foot\",0.304800609601219]"; + } + else if( EQUAL(papszTokens[0],"HORIZONTAL_DATUM") ) + { + if( EQUAL(papszTokens[1],"NAD27") ) + { + pszDatumLong = NAD27_DATUM; + pszDatumShort = "NAD 27"; + } + else if( EQUAL(papszTokens[1],"WGS72") ) + { + pszDatumLong = WGS72_DATUM; + pszDatumShort = "WGS 72"; + } + else if( EQUAL(papszTokens[1],"WGS84") ) + { + pszDatumLong = WGS84_DATUM; + pszDatumShort = "WGS 84"; + } + else if( EQUAL(papszTokens[1],"NAD83") ) + { + pszDatumLong = NAD83_DATUM; + pszDatumShort = "NAD 83"; + } + else + { + pszDatumLong = "DATUM[\"unknown\"]"; + pszDatumShort = "unknown"; + } + } + else + { + /* we want to generically capture all the other metadata */ + CPLString osMetaDataValue; + int iToken; + + for( iToken = 1; papszTokens[iToken] != NULL; iToken++ ) + { + if( EQUAL(papszTokens[iToken],"*") ) + continue; + + if( iToken > 1 ) + osMetaDataValue += " " ; + osMetaDataValue += papszTokens[iToken]; + } + papszMetadata = CSLAddNameValue( papszMetadata, + papszTokens[0], + osMetaDataValue ); + } + + CSLDestroy( papszTokens ); + } + + CPLReadLineL( NULL ); + +/* -------------------------------------------------------------------- */ +/* Do these values look coherent for a DOQ file? It would be */ +/* nice to do a more comprehensive test than this! */ +/* -------------------------------------------------------------------- */ + if( nWidth < 500 || nWidth > 25000 + || nHeight < 500 || nHeight > 25000 + || nBandStorage < 0 || nBandStorage > 4 + || nBandTypes < 1 || nBandTypes > 9 ) + { + CSLDestroy( papszMetadata ); + VSIFCloseL(fp); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Check the configuration. We don't currently handle all */ +/* variations, only the common ones. */ +/* -------------------------------------------------------------------- */ + if( nBandTypes > 5 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "DOQ Data Type (%d) is not a supported configuration.\n", + nBandTypes ); + CSLDestroy( papszMetadata ); + VSIFCloseL(fp); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CSLDestroy( papszMetadata ); + CPLError( CE_Failure, CPLE_NotSupported, + "The DOQ2 driver does not support update access to existing" + " datasets.\n" ); + VSIFCloseL(fp); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + DOQ2Dataset *poDS; + + poDS = new DOQ2Dataset(); + + poDS->nRasterXSize = nWidth; + poDS->nRasterYSize = nHeight; + + poDS->SetMetadata( papszMetadata ); + CSLDestroy( papszMetadata ); + + poDS->fpImage = fp; + +/* -------------------------------------------------------------------- */ +/* Compute layout of data. */ +/* -------------------------------------------------------------------- */ + if( nBandCount < 2 ) + nBandCount = nBytesPerPixel; + else + nBytesPerPixel *= nBandCount; + + nBytesPerLine = nBytesPerPixel * nWidth; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBandCount; i++ ) + { + poDS->SetBand( i+1, + new RawRasterBand( poDS, i+1, poDS->fpImage, + nSkipBytes + i, nBytesPerPixel, nBytesPerLine, + GDT_Byte, TRUE, TRUE ) ); + } + + if (nProjType == 1) + { + poDS->pszProjection = + CPLStrdup(CPLSPrintf( UTM_FORMAT, pszDatumShort, nZone, + pszDatumLong, nZone * 6 - 183, pszUnits )); + } + else + { + poDS->pszProjection = CPLStrdup(""); + } + + poDS->dfULX = dfULXMap; + poDS->dfULY = dfULYMap; + + poDS->dfXPixelSize = dfXDim; + poDS->dfYPixelSize = dfYDim; + + if ( pszQuadname ) CPLFree( pszQuadname ); + if ( pszQuadquad) CPLFree( pszQuadquad ); + if ( pszState) CPLFree( pszState ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_DOQ1() */ +/************************************************************************/ + +void GDALRegister_DOQ2() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "DOQ2" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "DOQ2" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "USGS DOQ (New Style)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#DOQ2" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = DOQ2Dataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/raw/ehdrdataset.cpp b/bazaar/plugin/gdal/frmts/raw/ehdrdataset.cpp new file mode 100644 index 000000000..bdf669df0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/ehdrdataset.cpp @@ -0,0 +1,2050 @@ +/****************************************************************************** + * $Id: ehdrdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: ESRI .hdr Driver + * Purpose: Implementation of EHdrDataset + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ehdrdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_EHdr(void); +CPL_C_END + +#define HAS_MIN_FLAG 0x1 +#define HAS_MAX_FLAG 0x2 +#define HAS_MEAN_FLAG 0x4 +#define HAS_STDDEV_FLAG 0x8 +#define HAS_ALL_FLAGS (HAS_MIN_FLAG | HAS_MAX_FLAG | HAS_MEAN_FLAG | HAS_STDDEV_FLAG) + +/************************************************************************/ +/* ==================================================================== */ +/* EHdrDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class EHdrRasterBand; + +class EHdrDataset : public RawDataset +{ + friend class EHdrRasterBand; + + VSILFILE *fpImage; // image data file. + + CPLString osHeaderExt; + + int bGotTransform; + double adfGeoTransform[6]; + char *pszProjection; + + int bHDRDirty; + char **papszHDR; + + int bCLRDirty; + + CPLErr ReadSTX(); + CPLErr RewriteSTX(); + CPLErr RewriteHDR(); + void ResetKeyValue( const char *pszKey, const char *pszValue ); + const char *GetKeyValue( const char *pszKey, const char *pszDefault = "" ); + void RewriteColorTable( GDALColorTable * ); + + public: + EHdrDataset(); + ~EHdrDataset(); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual CPLErr SetGeoTransform( double *padfTransform ); + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual char **GetFileList(); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); + static GDALDataset *CreateCopy( const char * pszFilename, + GDALDataset * poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + static CPLString GetImageRepFilename(const char* pszFilename); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* EHdrRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class EHdrRasterBand : public RawRasterBand +{ + friend class EHdrDataset; + + int nBits; + long nStartBit; + int nPixelOffsetBits; + int nLineOffsetBits; + + int bNoDataSet; + double dfNoData; + double dfMin; + double dfMax; + double dfMean; + double dfStdDev; + + int minmaxmeanstddev; + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + public: + EHdrRasterBand( GDALDataset *poDS, int nBand, VSILFILE * fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder, + int nBits); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum(int *pbSuccess = NULL ); + virtual CPLErr GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev ); + virtual CPLErr SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ); + virtual CPLErr SetColorTable( GDALColorTable *poNewCT ); + +}; + +/************************************************************************/ +/* EHdrRasterBand() */ +/************************************************************************/ + +EHdrRasterBand::EHdrRasterBand( GDALDataset *poDS, + int nBand, VSILFILE * fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder, + int nBits) +: RawRasterBand( poDS, nBand, fpRaw, nImgOffset, nPixelOffset, nLineOffset, + eDataType, bNativeOrder, TRUE ), + nBits(nBits), + bNoDataSet(FALSE), + dfNoData(0), + dfMin(0), + dfMax(0), + minmaxmeanstddev(0) +{ + EHdrDataset* poEDS = (EHdrDataset*)poDS; + + if (nBits < 8) + { + nStartBit = atoi(poEDS->GetKeyValue("SKIPBYTES")) * 8; + if (nBand >= 2) + { + long nRowBytes = atoi(poEDS->GetKeyValue("BANDROWBYTES")); + if (nRowBytes == 0) + nRowBytes = (nBits * poDS->GetRasterXSize() + 7) / 8; + + nStartBit += nRowBytes * (nBand-1) * 8; + } + + nPixelOffsetBits = nBits; + nLineOffsetBits = atoi(poEDS->GetKeyValue("TOTALROWBYTES")) * 8; + if( nLineOffsetBits == 0 ) + nLineOffsetBits = nPixelOffsetBits * poDS->GetRasterXSize(); + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + SetMetadataItem( "NBITS", + CPLString().Printf( "%d", nBits ), + "IMAGE_STRUCTURE" ); + } + + if( eDataType == GDT_Byte + && EQUAL(poEDS->GetKeyValue("PIXELTYPE",""),"SIGNEDINT") ) + SetMetadataItem( "PIXELTYPE", "SIGNEDBYTE", + "IMAGE_STRUCTURE" ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr EHdrRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + if (nBits >= 8) + return RawRasterBand::IReadBlock(nBlockXOff, nBlockYOff, pImage); + + vsi_l_offset nLineStart; + unsigned int nLineBytes; + int iBitOffset; + GByte *pabyBuffer; + +/* -------------------------------------------------------------------- */ +/* Establish desired position. */ +/* -------------------------------------------------------------------- */ + nLineBytes = (nPixelOffsetBits*nBlockXSize + 7)/8; + nLineStart = (nStartBit + ((vsi_l_offset)nLineOffsetBits) * nBlockYOff) / 8; + iBitOffset = (int) + ((nStartBit + ((vsi_l_offset)nLineOffsetBits) * nBlockYOff) % 8); + +/* -------------------------------------------------------------------- */ +/* Read data into buffer. */ +/* -------------------------------------------------------------------- */ + pabyBuffer = (GByte *) CPLCalloc(nLineBytes,1); + + if( VSIFSeekL( GetFPL(), nLineStart, SEEK_SET ) != 0 + || VSIFReadL( pabyBuffer, 1, nLineBytes, GetFPL() ) != nLineBytes ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read %u bytes at offset %lu.\n%s", + nLineBytes, (unsigned long)nLineStart, + VSIStrerror( errno ) ); + CPLFree( pabyBuffer ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Copy data, promoting to 8bit. */ +/* -------------------------------------------------------------------- */ + int iPixel = 0, iX; + + for( iX = 0; iX < nBlockXSize; iX++ ) + { + int nOutWord = 0, iBit; + + for( iBit = 0; iBit < nBits; iBit++ ) + { + if( pabyBuffer[iBitOffset>>3] & (0x80 >>(iBitOffset & 7)) ) + nOutWord |= (1 << (nBits - 1 - iBit)); + iBitOffset++; + } + + iBitOffset = iBitOffset + nPixelOffsetBits - nBits; + + ((GByte *) pImage)[iPixel++] = (GByte) nOutWord; + } + + CPLFree( pabyBuffer ); + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr EHdrRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + if (nBits >= 8) + return RawRasterBand::IWriteBlock(nBlockXOff, nBlockYOff, pImage); + + vsi_l_offset nLineStart; + unsigned int nLineBytes; + int iBitOffset; + GByte *pabyBuffer; + +/* -------------------------------------------------------------------- */ +/* Establish desired position. */ +/* -------------------------------------------------------------------- */ + nLineBytes = (nPixelOffsetBits*nBlockXSize + 7)/8; + nLineStart = (nStartBit + ((vsi_l_offset)nLineOffsetBits) * nBlockYOff) / 8; + iBitOffset = (int) + ((nStartBit + ((vsi_l_offset)nLineOffsetBits) * nBlockYOff) % 8); + +/* -------------------------------------------------------------------- */ +/* Read data into buffer. */ +/* -------------------------------------------------------------------- */ + pabyBuffer = (GByte *) CPLCalloc(nLineBytes,1); + + if( VSIFSeekL( GetFPL(), nLineStart, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read %u bytes at offset %lu.\n%s", + nLineBytes, (unsigned long)nLineStart, + VSIStrerror( errno ) ); + CPLFree( pabyBuffer ); + return CE_Failure; + } + + VSIFReadL( pabyBuffer, 1, nLineBytes, GetFPL() ); + +/* -------------------------------------------------------------------- */ +/* Copy data, promoting to 8bit. */ +/* -------------------------------------------------------------------- */ + int iPixel = 0, iX; + + for( iX = 0; iX < nBlockXSize; iX++ ) + { + int iBit; + int nOutWord = ((GByte *) pImage)[iPixel++]; + + for( iBit = 0; iBit < nBits; iBit++ ) + { + if( nOutWord & (1 << (nBits - 1 - iBit)) ) + pabyBuffer[iBitOffset>>3] |= (0x80 >>(iBitOffset & 7)); + else + pabyBuffer[iBitOffset>>3] &= ~((0x80 >>(iBitOffset & 7))); + + iBitOffset++; + } + + iBitOffset = iBitOffset + nPixelOffsetBits - nBits; + } + +/* -------------------------------------------------------------------- */ +/* Write the data back out. */ +/* -------------------------------------------------------------------- */ + if( VSIFSeekL( GetFPL(), nLineStart, SEEK_SET ) != 0 + || VSIFWriteL( pabyBuffer, 1, nLineBytes, GetFPL() ) != nLineBytes ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to write %u bytes at offset %lu.\n%s", + nLineBytes, (unsigned long)nLineStart, + VSIStrerror( errno ) ); + return CE_Failure; + } + + CPLFree( pabyBuffer ); + + return CE_None; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr EHdrRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + // Defer to RawRasterBand + if (nBits >= 8) + return RawRasterBand::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, psExtraArg ); + + // Force use of IReadBlock() and IWriteBlock() + else + return GDALRasterBand::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, psExtraArg ); +} + +/************************************************************************/ +/* OSR_GDS() */ +/************************************************************************/ + +static const char*OSR_GDS( char* pszResult, int nResultLen, + char **papszNV, const char * pszField, + const char *pszDefaultValue ) + +{ + int iLine; + + if( papszNV == NULL || papszNV[0] == NULL ) + return pszDefaultValue; + + for( iLine = 0; + papszNV[iLine] != NULL && + !EQUALN(papszNV[iLine],pszField,strlen(pszField)); + iLine++ ) {} + + if( papszNV[iLine] == NULL ) + return pszDefaultValue; + else + { + char **papszTokens; + + papszTokens = CSLTokenizeString(papszNV[iLine]); + + if( CSLCount(papszTokens) > 1 ) + strncpy( pszResult, papszTokens[1], nResultLen); + else + strncpy( pszResult, pszDefaultValue, nResultLen); + pszResult[nResultLen-1] = '\0'; + + CSLDestroy( papszTokens ); + return pszResult; + } +} + + +/************************************************************************/ +/* ==================================================================== */ +/* EHdrDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* EHdrDataset() */ +/************************************************************************/ + +EHdrDataset::EHdrDataset() +{ + fpImage = NULL; + pszProjection = CPLStrdup(""); + bGotTransform = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + papszHDR = NULL; + bHDRDirty = FALSE; + bCLRDirty = FALSE; + osHeaderExt = "hdr"; +} + +/************************************************************************/ +/* ~EHdrDataset() */ +/************************************************************************/ + +EHdrDataset::~EHdrDataset() + +{ + FlushCache(); + + if( nBands > 0 && GetAccess() == GA_Update ) + { + int bNoDataSet; + double dfNoData; + RawRasterBand *poBand = (RawRasterBand *) GetRasterBand( 1 ); + + dfNoData = poBand->GetNoDataValue(&bNoDataSet); + if( bNoDataSet ) + { + ResetKeyValue( "NODATA", + CPLString().Printf( "%.8g", dfNoData ) ); + } + + if( bCLRDirty ) + RewriteColorTable( poBand->GetColorTable() ); + + if( bHDRDirty ) + RewriteHDR(); + } + + if( fpImage != NULL ) + VSIFCloseL( fpImage ); + + CPLFree( pszProjection ); + CSLDestroy( papszHDR ); +} + +/************************************************************************/ +/* GetKeyValue() */ +/************************************************************************/ + +const char *EHdrDataset::GetKeyValue( const char *pszKey, + const char *pszDefault ) + +{ + int i; + + for( i = 0; papszHDR[i] != NULL; i++ ) + { + if( EQUALN(pszKey,papszHDR[i],strlen(pszKey)) + && isspace((unsigned char)papszHDR[i][strlen(pszKey)]) ) + { + const char *pszValue = papszHDR[i] + strlen(pszKey); + while( isspace((unsigned char)*pszValue) ) + pszValue++; + + return pszValue; + } + } + + return pszDefault; +} + +/************************************************************************/ +/* ResetKeyValue() */ +/* */ +/* Replace or add the keyword with the indicated value in the */ +/* papszHDR list. */ +/************************************************************************/ + +void EHdrDataset::ResetKeyValue( const char *pszKey, const char *pszValue ) + +{ + int i; + char szNewLine[82]; + + if( strlen(pszValue) > 65 ) + { + CPLAssert( strlen(pszValue) <= 65 ); + return; + } + + sprintf( szNewLine, "%-15s%s", pszKey, pszValue ); + + for( i = CSLCount(papszHDR)-1; i >= 0; i-- ) + { + if( EQUALN(papszHDR[i],szNewLine,strlen(pszKey)+1 ) ) + { + if( strcmp(papszHDR[i],szNewLine) != 0 ) + { + CPLFree( papszHDR[i] ); + papszHDR[i] = CPLStrdup( szNewLine ); + bHDRDirty = TRUE; + } + return; + } + } + + bHDRDirty = TRUE; + papszHDR = CSLAddString( papszHDR, szNewLine ); +} + +/************************************************************************/ +/* RewriteColorTable() */ +/************************************************************************/ + +void EHdrDataset::RewriteColorTable( GDALColorTable *poTable ) + +{ + CPLString osCLRFilename = CPLResetExtension( GetDescription(), "clr" ); + if( poTable ) + { + VSILFILE *fp = VSIFOpenL( osCLRFilename, "wt" ); + if( fp != NULL ) + { + for( int iColor = 0; iColor < poTable->GetColorEntryCount(); iColor++ ) + { + CPLString oLine; + GDALColorEntry sEntry; + + poTable->GetColorEntryAsRGB( iColor, &sEntry ); + + // I wish we had a way to mark transparency. + oLine.Printf( "%3d %3d %3d %3d\n", + iColor, sEntry.c1, sEntry.c2, sEntry.c3 ); + VSIFWriteL( (void *) oLine.c_str(), 1, strlen(oLine), fp ); + } + VSIFCloseL( fp ); + } + else + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create color file %s.", + osCLRFilename.c_str() ); + } + } + else + VSIUnlink( osCLRFilename ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *EHdrDataset::GetProjectionRef() + +{ + if (pszProjection && strlen(pszProjection) > 0) + return pszProjection; + + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr EHdrDataset::SetProjection( const char *pszSRS ) + +{ +/* -------------------------------------------------------------------- */ +/* Reset coordinate system on the dataset. */ +/* -------------------------------------------------------------------- */ + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszSRS ); + + if( strlen(pszSRS) == 0 ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Convert to ESRI WKT. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS( pszSRS ); + char *pszESRI_SRS = NULL; + + oSRS.morphToESRI(); + oSRS.exportToWkt( &pszESRI_SRS ); + +/* -------------------------------------------------------------------- */ +/* Write to .prj file. */ +/* -------------------------------------------------------------------- */ + CPLString osPrjFilename = CPLResetExtension( GetDescription(), "prj" ); + VSILFILE *fp; + + fp = VSIFOpenL( osPrjFilename.c_str(), "wt" ); + if( fp != NULL ) + { + VSIFWriteL( pszESRI_SRS, 1, strlen(pszESRI_SRS), fp ); + VSIFWriteL( (void *) "\n", 1, 1, fp ); + VSIFCloseL( fp ); + } + + CPLFree( pszESRI_SRS ); + + return CE_None; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr EHdrDataset::GetGeoTransform( double * padfTransform ) + +{ + if( bGotTransform ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; + } + else + { + return GDALPamDataset::GetGeoTransform( padfTransform ); + } +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr EHdrDataset::SetGeoTransform( double *padfGeoTransform ) + +{ +/* -------------------------------------------------------------------- */ +/* We only support non-rotated images with info in the .HDR file. */ +/* -------------------------------------------------------------------- */ + if( padfGeoTransform[2] != 0.0 + || padfGeoTransform[4] != 0.0 ) + { + return GDALPamDataset::SetGeoTransform( padfGeoTransform ); + } + +/* -------------------------------------------------------------------- */ +/* Record new geotransform. */ +/* -------------------------------------------------------------------- */ + bGotTransform = TRUE; + memcpy( adfGeoTransform, padfGeoTransform, sizeof(double) * 6 ); + +/* -------------------------------------------------------------------- */ +/* Strip out all old geotransform keywords from HDR records. */ +/* -------------------------------------------------------------------- */ + int i; + for( i = CSLCount(papszHDR)-1; i >= 0; i-- ) + { + if( EQUALN(papszHDR[i],"ul",2) + || EQUALN(papszHDR[i]+1,"ll",2) + || EQUALN(papszHDR[i],"cell",4) + || EQUALN(papszHDR[i]+1,"dim",3) ) + { + papszHDR = CSLRemoveStrings( papszHDR, i, 1, NULL ); + } + } + +/* -------------------------------------------------------------------- */ +/* Set the transformation information. */ +/* -------------------------------------------------------------------- */ + CPLString oValue; + + oValue.Printf( "%.15g", adfGeoTransform[0] + adfGeoTransform[1] * 0.5 ); + ResetKeyValue( "ULXMAP", oValue ); + + oValue.Printf( "%.15g", adfGeoTransform[3] + adfGeoTransform[5] * 0.5 ); + ResetKeyValue( "ULYMAP", oValue ); + + oValue.Printf( "%.15g", adfGeoTransform[1] ); + ResetKeyValue( "XDIM", oValue ); + + oValue.Printf( "%.15g", fabs(adfGeoTransform[5]) ); + ResetKeyValue( "YDIM", oValue ); + + return CE_None; +} + +/************************************************************************/ +/* RewriteHDR() */ +/************************************************************************/ + +CPLErr EHdrDataset::RewriteHDR() + +{ + CPLString osPath = CPLGetPath( GetDescription() ); + CPLString osName = CPLGetBasename( GetDescription() ); + CPLString osHDRFilename = CPLFormCIFilename( osPath, osName, osHeaderExt ); + +/* -------------------------------------------------------------------- */ +/* Write .hdr file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + int i; + + fp = VSIFOpenL( osHDRFilename, "wt" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to rewrite .hdr file %s.", + osHDRFilename.c_str() ); + return CE_Failure; + } + + for( i = 0; papszHDR[i] != NULL; i++ ) + { + VSIFWriteL( papszHDR[i], 1, strlen(papszHDR[i]), fp ); + VSIFWriteL( (void *) "\n", 1, 1, fp ); + } + + VSIFCloseL( fp ); + + bHDRDirty = FALSE; + + return CE_None; +} + +/************************************************************************/ +/* RewriteSTX() */ +/************************************************************************/ + +CPLErr EHdrDataset::RewriteSTX() +{ + CPLString osPath = CPLGetPath( GetDescription() ); + CPLString osName = CPLGetBasename( GetDescription() ); + CPLString osSTXFilename = CPLFormCIFilename( osPath, osName, "stx" ); + +/* -------------------------------------------------------------------- */ +/* Write .stx file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + fp = VSIFOpenL( osSTXFilename, "wt" ); + if( fp == NULL ) + { + CPLDebug( "EHDR", "Failed to rewrite .stx file %s.", + osSTXFilename.c_str() ); + return CE_Failure; + } + + for (int i = 0; i < nBands; ++i) + { + EHdrRasterBand* poBand = (EHdrRasterBand*)papoBands[i]; + VSIFPrintfL( fp, "%d %.10f %.10f ", i+1, poBand->dfMin, poBand->dfMax ); + if ( poBand->minmaxmeanstddev & HAS_MEAN_FLAG ) + VSIFPrintfL( fp, "%.10f ", poBand->dfMean); + else + VSIFPrintfL( fp, "# "); + + if ( poBand->minmaxmeanstddev & HAS_STDDEV_FLAG ) + VSIFPrintfL( fp, "%.10f\n", poBand->dfStdDev); + else + VSIFPrintfL( fp, "#\n"); + } + + VSIFCloseL( fp ); + + return CE_None; +} + +/************************************************************************/ +/* ReadSTX() */ +/************************************************************************/ + +CPLErr EHdrDataset::ReadSTX() +{ + CPLString osPath = CPLGetPath( GetDescription() ); + CPLString osName = CPLGetBasename( GetDescription() ); + CPLString osSTXFilename = CPLFormCIFilename( osPath, osName, "stx" ); + +/* -------------------------------------------------------------------- */ +/* Read .stx file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + if ((fp = VSIFOpenL( osSTXFilename, "rt" )) != NULL) + { + const char * pszLine; + while( (pszLine = CPLReadLineL( fp )) != NULL ) + { + char **papszTokens; + papszTokens = CSLTokenizeStringComplex( pszLine, " \t", TRUE, FALSE ); + int nTokens = CSLCount( papszTokens ); + if( nTokens >= 5 ) + { + int i = atoi(papszTokens[0]); + if (i > 0 && i <= nBands) + { + EHdrRasterBand* poBand = (EHdrRasterBand*)papoBands[i-1]; + poBand->dfMin = CPLAtof(papszTokens[1]); + poBand->dfMax = CPLAtof(papszTokens[2]); + + int bNoDataSet = FALSE; + double dfNoData = poBand->GetNoDataValue(&bNoDataSet); + if (bNoDataSet && dfNoData == poBand->dfMin) + { + /* Triggered by /vsicurl/http://eros.usgs.gov/archive/nslrsda/GeoTowns/HongKong/srtm/n22e113.zip/n22e113.bil */ + CPLDebug("EHDr", "Ignoring .stx file where min == nodata. " + "The nodata value shouldn't be taken into account " + "in minimum value computation."); + CSLDestroy( papszTokens ); + papszTokens = NULL; + break; + } + + poBand->minmaxmeanstddev = HAS_MIN_FLAG | HAS_MAX_FLAG; + // reads optional mean and stddev + if ( !EQUAL(papszTokens[3], "#") ) + { + poBand->dfMean = CPLAtof(papszTokens[3]); + poBand->minmaxmeanstddev |= HAS_MEAN_FLAG; + } + if ( !EQUAL(papszTokens[4], "#") ) + { + poBand->dfStdDev = CPLAtof(papszTokens[4]); + poBand->minmaxmeanstddev |= HAS_STDDEV_FLAG; + } + + if( nTokens >= 6 && !EQUAL(papszTokens[5], "#") ) + poBand->SetMetadataItem( "STRETCHMIN", papszTokens[5], "RENDERING_HINTS" ); + + if( nTokens >= 7 && !EQUAL(papszTokens[6], "#") ) + poBand->SetMetadataItem( "STRETCHMAX", papszTokens[6], "RENDERING_HINTS" ); + } + } + + CSLDestroy( papszTokens ); + } + + VSIFCloseL( fp ); + } + + return CE_None; +} + + +/************************************************************************/ +/* GetImageRepFilename() */ +/************************************************************************/ + +/* -------------------------------------------------------------------- */ +/* Check for IMAGE.REP (Spatiocarte Defense 1.0) or name_of_image.rep */ +/* if it's a GIS-GeoSPOT image */ +/* For the specification of SPDF (in French), */ +/* see http://eden.ign.fr/download/pub/doc/emabgi/spdf10.pdf/download */ +/* -------------------------------------------------------------------- */ + +CPLString EHdrDataset::GetImageRepFilename(const char* pszFilename) +{ + VSIStatBufL sStatBuf; + + CPLString osPath = CPLGetPath( pszFilename ); + CPLString osName = CPLGetBasename( pszFilename ); + CPLString osREPFilename = + CPLFormCIFilename( osPath, osName, "rep" ); + if( VSIStatExL( osREPFilename.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + return osREPFilename; + + if (EQUAL(CPLGetFilename(pszFilename), "imspatio.bil") || + EQUAL(CPLGetFilename(pszFilename), "haspatio.bil")) + { + CPLString osImageRepFilename(CPLFormCIFilename( osPath, "image", "rep" )); + if( VSIStatExL( osImageRepFilename.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + return osImageRepFilename; + + /* Try in the upper directories if not found in the BIL image directory */ + CPLString dirName(CPLGetDirname(osPath)); + if (CPLIsFilenameRelative(osPath.c_str())) + { + char* cwd = CPLGetCurrentDir(); + if (cwd) + { + dirName = CPLFormFilename(cwd, dirName.c_str(), NULL); + CPLFree(cwd); + } + } + while (dirName[0] != 0 && EQUAL(dirName, ".") == FALSE && EQUAL(dirName, "/") == FALSE) + { + osImageRepFilename = CPLFormCIFilename( dirName.c_str(), "image", "rep" ); + if( VSIStatExL( osImageRepFilename.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + return osImageRepFilename; + + /* Don't try to recurse above the 'image' subdirectory */ + if (EQUAL(dirName, "image")) + { + break; + } + dirName = CPLString(CPLGetDirname(dirName)); + } + } + return CPLString(); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **EHdrDataset::GetFileList() + +{ + VSIStatBufL sStatBuf; + CPLString osPath = CPLGetPath( GetDescription() ); + CPLString osName = CPLGetBasename( GetDescription() ); + char **papszFileList = NULL; + + // Main data file, etc. + papszFileList = GDALPamDataset::GetFileList(); + + // Header file. + CPLString osFilename = CPLFormCIFilename( osPath, osName, osHeaderExt ); + papszFileList = CSLAddString( papszFileList, osFilename ); + + // Statistics file + osFilename = CPLFormCIFilename( osPath, osName, "stx" ); + if( VSIStatExL( osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + papszFileList = CSLAddString( papszFileList, osFilename ); + + // color table file. + osFilename = CPLFormCIFilename( osPath, osName, "clr" ); + if( VSIStatExL( osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + papszFileList = CSLAddString( papszFileList, osFilename ); + + // projections file. + osFilename = CPLFormCIFilename( osPath, osName, "prj" ); + if( VSIStatExL( osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + papszFileList = CSLAddString( papszFileList, osFilename ); + + CPLString imageRepFilename = GetImageRepFilename( GetDescription() ); + if (!imageRepFilename.empty()) + papszFileList = CSLAddString( papszFileList, imageRepFilename.c_str() ); + + return papszFileList; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *EHdrDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i, bSelectedHDR; + +/* -------------------------------------------------------------------- */ +/* We assume the user is pointing to the binary (ie. .bil) file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 2 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Now we need to tear apart the filename to form a .HDR */ +/* filename. */ +/* -------------------------------------------------------------------- */ + CPLString osPath = CPLGetPath( poOpenInfo->pszFilename ); + CPLString osName = CPLGetBasename( poOpenInfo->pszFilename ); + CPLString osHDRFilename; + + const char* pszHeaderExt = "hdr"; + if( EQUAL( CPLGetExtension( poOpenInfo->pszFilename ), "SRC" ) && + osName.size() == 7 && + (osName[0] == 'e' || osName[0] == 'E' || osName[0] == 'w' || osName[0] == 'W') && + (osName[4] == 'n' || osName[4] == 'N' || osName[4] == 's' || osName[4] == 'S') ) + { + /* It is a GTOPO30 or SRTM30 source file, whose header extension is .sch */ + /* see http://dds.cr.usgs.gov/srtm/version1/SRTM30/GTOPO30_Documentation */ + pszHeaderExt = "sch"; + } + + char** papszSiblingFiles = poOpenInfo->GetSiblingFiles(); + if( papszSiblingFiles ) + { + int iFile = CSLFindString(papszSiblingFiles, + CPLFormFilename( NULL, osName, pszHeaderExt ) ); + if( iFile < 0 ) // return if there is no corresponding .hdr file + return NULL; + + osHDRFilename = + CPLFormFilename( osPath, papszSiblingFiles[iFile], + NULL ); + } + else + { + osHDRFilename = CPLFormCIFilename( osPath, osName, pszHeaderExt ); + } + + bSelectedHDR = EQUAL( osHDRFilename, poOpenInfo->pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Do we have a .hdr file? */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + + fp = VSIFOpenL( osHDRFilename, "r" ); + + if( fp == NULL ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Is this file an ESRI header file? Read a few lines of text */ +/* searching for something starting with nrows or ncols. */ +/* -------------------------------------------------------------------- */ + const char * pszLine; + int nRows = -1, nCols = -1, nBands = 1; + int nSkipBytes = 0; + double dfULXMap=0.5, dfULYMap = 0.5, dfYLLCorner = -123.456; + int bCenter = TRUE; + double dfXDim = 1.0, dfYDim = 1.0, dfNoData = 0.0; + int nLineCount = 0, bNoDataSet = FALSE; + GDALDataType eDataType = GDT_Byte; + int nBits = -1; + char chByteOrder = 'M'; + char chPixelType = 'N'; // not defined + char szLayout[10] = "BIL"; + char **papszHDR = NULL; + int bHasInternalProjection = FALSE; + int bHasMin = FALSE; + int bHasMax = FALSE; + double dfMin = 0, dfMax = 0; + + while( (pszLine = CPLReadLineL( fp )) != NULL ) + { + char **papszTokens; + + nLineCount++; + + if( nLineCount > 50 || strlen(pszLine) > 1000 ) + break; + + papszHDR = CSLAddString( papszHDR, pszLine ); + + papszTokens = CSLTokenizeStringComplex( pszLine, " \t", TRUE, FALSE ); + if( CSLCount( papszTokens ) < 2 ) + { + CSLDestroy( papszTokens ); + continue; + } + + if( EQUAL(papszTokens[0],"ncols") ) + { + nCols = atoi(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"nrows") ) + { + nRows = atoi(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"skipbytes") ) + { + nSkipBytes = atoi(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"ulxmap") + || EQUAL(papszTokens[0],"xllcorner") + || EQUAL(papszTokens[0],"xllcenter") ) + { + dfULXMap = CPLAtofM(papszTokens[1]); + if( EQUAL(papszTokens[0],"xllcorner") ) + bCenter = FALSE; + } + else if( EQUAL(papszTokens[0],"ulymap") ) + { + dfULYMap = CPLAtofM(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"yllcorner") + || EQUAL(papszTokens[0],"yllcenter") ) + { + dfYLLCorner = CPLAtofM(papszTokens[1]); + if( EQUAL(papszTokens[0],"yllcorner") ) + bCenter = FALSE; + } + else if( EQUAL(papszTokens[0],"xdim") ) + { + dfXDim = CPLAtofM(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"ydim") ) + { + dfYDim = CPLAtofM(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"cellsize") ) + { + dfXDim = dfYDim = CPLAtofM(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"nbands") ) + { + nBands = atoi(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"layout") ) + { + strncpy( szLayout, papszTokens[1], sizeof(szLayout) ); + szLayout[sizeof(szLayout)-1] = '\0'; + } + else if( EQUAL(papszTokens[0],"NODATA_value") + || EQUAL(papszTokens[0],"NODATA") ) + { + dfNoData = CPLAtofM(papszTokens[1]); + bNoDataSet = TRUE; + } + else if( EQUAL(papszTokens[0],"NBITS") ) + { + nBits = atoi(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"PIXELTYPE") ) + { + chPixelType = (char) toupper(papszTokens[1][0]); + } + else if( EQUAL(papszTokens[0],"byteorder") ) + { + chByteOrder = (char) toupper(papszTokens[1][0]); + } + + /* http://www.worldclim.org/futdown.htm have the projection extensions */ + else if( EQUAL(papszTokens[0],"Projection") ) + { + bHasInternalProjection = TRUE; + } + else if( EQUAL(papszTokens[0],"MinValue") || + EQUAL(papszTokens[0],"MIN_VALUE") ) + { + dfMin = CPLAtofM(papszTokens[1]); + bHasMin = TRUE; + } + else if( EQUAL(papszTokens[0],"MaxValue") || + EQUAL(papszTokens[0],"MAX_VALUE") ) + { + dfMax = CPLAtofM(papszTokens[1]); + bHasMax = TRUE; + } + + CSLDestroy( papszTokens ); + } + + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Did we get the required keywords? If not we return with */ +/* this never having been considered to be a match. This isn't */ +/* an error! */ +/* -------------------------------------------------------------------- */ + if( nRows == -1 || nCols == -1 ) + { + CSLDestroy( papszHDR ); + return NULL; + } + + if (!GDALCheckDatasetDimensions(nCols, nRows) || + !GDALCheckBandCount(nBands, FALSE)) + { + CSLDestroy( papszHDR ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Has the user selected the .hdr file to open? */ +/* -------------------------------------------------------------------- */ + if( bSelectedHDR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The selected file is an ESRI BIL header file, but to\n" + "open ESRI BIL datasets, the data file should be selected\n" + "instead of the .hdr file. Please try again selecting\n" + "the data file (often with the extension .bil) corresponding\n" + "to the header file: %s\n", + poOpenInfo->pszFilename ); + CSLDestroy( papszHDR ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* If we aren't sure of the file type, check the data file */ +/* size. If it is 4 bytes or more per pixel then we assume it */ +/* is floating point data. */ +/* -------------------------------------------------------------------- */ + if( nBits == -1 && chPixelType == 'N' ) + { + VSIStatBufL sStatBuf; + if( VSIStatL( poOpenInfo->pszFilename, &sStatBuf ) == 0 ) + { + size_t nBytes = (size_t) (sStatBuf.st_size/nCols/nRows/nBands); + if( nBytes > 0 && nBytes != 3 ) + nBits = nBytes*8; + + if( nBytes == 4 ) + chPixelType = 'F'; + } + } + +/* -------------------------------------------------------------------- */ +/* If the extension is FLT it is likely a floating point file. */ +/* -------------------------------------------------------------------- */ + if( chPixelType == 'N' ) + { + if( EQUAL( CPLGetExtension( poOpenInfo->pszFilename ), "FLT" ) ) + chPixelType = 'F'; + } + +/* -------------------------------------------------------------------- */ +/* If we have a negative nodata value, let's assume that the */ +/* pixel type is signed. This is necessary for datasets from */ +/* http://www.worldclim.org/futdown.htm */ +/* -------------------------------------------------------------------- */ + if( bNoDataSet && dfNoData < 0 && chPixelType == 'N' ) + { + chPixelType = 'S'; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + EHdrDataset *poDS; + + poDS = new EHdrDataset(); + + poDS->osHeaderExt = pszHeaderExt; + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = nCols; + poDS->nRasterYSize = nRows; + poDS->papszHDR = papszHDR; + +/* -------------------------------------------------------------------- */ +/* Open target binary file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "r+b" ); + + if( poDS->fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s with write permission.\n%s", + osName.c_str(), VSIStrerror( errno ) ); + delete poDS; + return NULL; + } + + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Figure out the data type. */ +/* -------------------------------------------------------------------- */ + if( nBits == 16 ) + { + if ( chPixelType == 'S' ) + eDataType = GDT_Int16; + else + eDataType = GDT_UInt16; // default + } + else if( nBits == 32 ) + { + if( chPixelType == 'S' ) + eDataType = GDT_Int32; + else if( chPixelType == 'F' ) + eDataType = GDT_Float32; + else + eDataType = GDT_UInt32; // default + } + else if( nBits == 8 ) + { + eDataType = GDT_Byte; + nBits = 8; + } + else if( nBits < 8 && nBits >= 1 ) + eDataType = GDT_Byte; + else if( nBits == -1 ) + { + if( chPixelType == 'F' ) + { + eDataType = GDT_Float32; + nBits = 32; + } + else + { + eDataType = GDT_Byte; + nBits = 8; + } + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "EHdr driver does not support %d NBITS value.", + nBits ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Compute the line offset. */ +/* -------------------------------------------------------------------- */ + int nItemSize = GDALGetDataTypeSize(eDataType)/8; + int nPixelOffset, nLineOffset; + vsi_l_offset nBandOffset; + + if( EQUAL(szLayout,"BIP") ) + { + nPixelOffset = nItemSize * nBands; + nLineOffset = nPixelOffset * nCols; + nBandOffset = (vsi_l_offset)nItemSize; + } + else if( EQUAL(szLayout,"BSQ") ) + { + nPixelOffset = nItemSize; + nLineOffset = nPixelOffset * nCols; + nBandOffset = (vsi_l_offset)nLineOffset * nRows; + } + else /* assume BIL */ + { + nPixelOffset = nItemSize; + nLineOffset = nItemSize * nBands * nCols; + nBandOffset = (vsi_l_offset)nItemSize * nCols; + } + + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->PamInitialize(); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = nBands; + for( i = 0; i < poDS->nBands; i++ ) + { + EHdrRasterBand *poBand; + + poBand = + new EHdrRasterBand( poDS, i+1, poDS->fpImage, + nSkipBytes + nBandOffset * i, + nPixelOffset, nLineOffset, eDataType, +#ifdef CPL_LSB + chByteOrder == 'I' || chByteOrder == 'L', +#else + chByteOrder == 'M', +#endif + nBits); + + poBand->bNoDataSet = bNoDataSet; + poBand->dfNoData = dfNoData; + + if( bHasMin && bHasMax ) + { + poBand->dfMin = dfMin; + poBand->dfMax = dfMax; + poBand->minmaxmeanstddev = HAS_MIN_FLAG | HAS_MAX_FLAG; + } + + poDS->SetBand( i+1, poBand ); + } + +/* -------------------------------------------------------------------- */ +/* If we didn't get bounds in the .hdr, look for a worldfile. */ +/* -------------------------------------------------------------------- */ + if( dfYLLCorner != -123.456 ) + { + if( bCenter ) + dfULYMap = dfYLLCorner + (nRows-1) * dfYDim; + else + dfULYMap = dfYLLCorner + nRows * dfYDim; + } + + if( dfULYMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0 ) + { + poDS->bGotTransform = TRUE; + + if( bCenter ) + { + poDS->adfGeoTransform[0] = dfULXMap - dfXDim * 0.5; + poDS->adfGeoTransform[1] = dfXDim; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = dfULYMap + dfYDim * 0.5; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = - dfYDim; + } + else + { + poDS->adfGeoTransform[0] = dfULXMap; + poDS->adfGeoTransform[1] = dfXDim; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = dfULYMap; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = - dfYDim; + } + } + + if( !poDS->bGotTransform ) + poDS->bGotTransform = + GDALReadWorldFile( poOpenInfo->pszFilename, 0, + poDS->adfGeoTransform ); + + if( !poDS->bGotTransform ) + poDS->bGotTransform = + GDALReadWorldFile( poOpenInfo->pszFilename, "wld", + poDS->adfGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Check for a .prj file. */ +/* -------------------------------------------------------------------- */ + const char *pszPrjFilename = CPLFormCIFilename( osPath, osName, "prj" ); + + fp = VSIFOpenL( pszPrjFilename, "r" ); + + /* .hdr files from http://www.worldclim.org/futdown.htm have the projection */ + /* info in the .hdr file itself ! */ + if (fp == NULL && bHasInternalProjection) + { + pszPrjFilename = osHDRFilename; + fp = VSIFOpenL( pszPrjFilename, "r" ); + } + + if( fp != NULL ) + { + char **papszLines; + OGRSpatialReference oSRS; + + VSIFCloseL( fp ); + + papszLines = CSLLoad( pszPrjFilename ); + + if( oSRS.importFromESRI( papszLines ) == OGRERR_NONE ) + { + // If geographic values are in seconds, we must transform. + // Is there a code for minutes too? + char szResult[80]; + if( oSRS.IsGeographic() + && EQUAL(OSR_GDS( szResult, sizeof(szResult), + papszLines, "Units", ""), "DS") ) + { + poDS->adfGeoTransform[0] /= 3600.0; + poDS->adfGeoTransform[1] /= 3600.0; + poDS->adfGeoTransform[2] /= 3600.0; + poDS->adfGeoTransform[3] /= 3600.0; + poDS->adfGeoTransform[4] /= 3600.0; + poDS->adfGeoTransform[5] /= 3600.0; + } + + CPLFree( poDS->pszProjection ); + oSRS.exportToWkt( &(poDS->pszProjection) ); + } + + CSLDestroy( papszLines ); + } + else + { +/* -------------------------------------------------------------------- */ +/* Check for IMAGE.REP (Spatiocarte Defense 1.0) or name_of_image.rep */ +/* if it's a GIS-GeoSPOT image */ +/* For the specification of SPDF (in French), */ +/* see http://eden.ign.fr/download/pub/doc/emabgi/spdf10.pdf/download */ +/* -------------------------------------------------------------------- */ + CPLString szImageRepFilename = GetImageRepFilename(poOpenInfo->pszFilename ); + if (!szImageRepFilename.empty()) + { + fp = VSIFOpenL( szImageRepFilename.c_str(), "r" ); + } + if (fp != NULL) + { + const char *pszLine; + int bUTM = FALSE; + int bWGS84 = FALSE; + int bNorth = FALSE; + int bSouth = FALSE; + int utmZone = 0; + + while( (pszLine = CPLReadLineL( fp )) != NULL ) + { + if (strncmp(pszLine, "PROJ_ID", strlen("PROJ_ID")) == 0 && + strstr(pszLine, "UTM")) + { + bUTM = TRUE; + } + else if (strncmp(pszLine, "PROJ_ZONE", strlen("PROJ_ZONE")) == 0) + { + const char* c = strchr(pszLine, '"'); + if (c) + { + c++; + if (*c >= '0' && *c <= '9') + { + utmZone = atoi(c); + if (utmZone >= 1 && utmZone <= 60) + { + if (strstr(pszLine, "Nord") || strstr(pszLine, "NORD")) + { + bNorth = TRUE; + } + else if (strstr(pszLine, "Sud") || strstr(pszLine, "SUD")) + { + bSouth = TRUE; + } + } + } + } + } + else if (strncmp(pszLine, "PROJ_CODE", strlen("PROJ_CODE")) == 0 && + strstr(pszLine, "FR-MINDEF")) + { + const char* c = strchr(pszLine, 'A'); + if (c) + { + c++; + if (*c >= '0' && *c <= '9') + { + utmZone = atoi(c); + if (utmZone >= 1 && utmZone <= 60) + { + if (c[1] == 'N' || + (c[1] != '\0' && c[2] == 'N')) + { + bNorth = TRUE; + } + else if (c[1] == 'S' || + (c[1] != '\0' && c[2] == 'S')) + { + bSouth = TRUE; + } + } + } + } + } + else if (strncmp(pszLine, "HORIZ_DATUM", strlen("HORIZ_DATUM")) == 0 && + (strstr(pszLine, "WGS 84") || strstr(pszLine, "WGS84"))) + { + bWGS84 = TRUE; + } + else if (strncmp(pszLine, "MAP_NUMBER", strlen("MAP_NUMBER")) == 0) + { + const char* c = strchr(pszLine, '"'); + if (c) + { + char* pszMapNumber = CPLStrdup(c+1); + char* c2 = strchr(pszMapNumber, '"'); + if (c2) *c2 = 0; + poDS->SetMetadataItem("SPDF_MAP_NUMBER", pszMapNumber); + CPLFree(pszMapNumber); + } + } + else if (strncmp(pszLine, "PRODUCTION_DATE", strlen("PRODUCTION_DATE")) == 0) + { + const char* c = pszLine + strlen("PRODUCTION_DATE"); + while(*c == ' ') + c++; + if (*c) + { + poDS->SetMetadataItem("SPDF_PRODUCTION_DATE", c ); + } + } + } + + VSIFCloseL( fp ); + + if (utmZone != 0 && bUTM && bWGS84 && (bNorth || bSouth)) + { + char projCSStr[64]; + OGRSpatialReference oSRS; + + sprintf(projCSStr, "WGS 84 / UTM zone %d%c", + utmZone, (bNorth) ? 'N' : 'S'); + oSRS.SetProjCS(projCSStr); + oSRS.SetWellKnownGeogCS( "WGS84" ); + oSRS.SetUTM(utmZone, bNorth); + oSRS.SetAuthority("PROJCS", "EPSG", ((bNorth) ? 32600 : 32700) + utmZone); + oSRS.AutoIdentifyEPSG(); + + CPLFree( poDS->pszProjection ); + oSRS.exportToWkt( &(poDS->pszProjection) ); + } + else + { + CPLError( CE_Warning, CPLE_NotSupported, "Cannot retrive projection from IMAGE.REP"); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Check for a color table. */ +/* -------------------------------------------------------------------- */ + const char *pszCLRFilename = CPLFormCIFilename( osPath, osName, "clr" ); + + /* Only read the .clr for byte, int16 or uint16 bands */ + if (nItemSize <= 2) + fp = VSIFOpenL( pszCLRFilename, "r" ); + else + fp = NULL; + + if( fp != NULL ) + { + GDALColorTable oColorTable; + int bHasWarned = FALSE; + + while(TRUE) + { + const char *pszLine = CPLReadLineL(fp); + if ( !pszLine ) + break; + + if( *pszLine == '#' || *pszLine == '!' ) + continue; + + char **papszValues = CSLTokenizeString2(pszLine, "\t ", + CSLT_HONOURSTRINGS); + GDALColorEntry oEntry; + + if ( CSLCount(papszValues) >= 4 ) + { + int nIndex = atoi( papszValues[0] ); // Index + if (nIndex >= 0 && nIndex < 65536) + { + oEntry.c1 = (short) atoi( papszValues[1] ); // Red + oEntry.c2 = (short) atoi( papszValues[2] ); // Green + oEntry.c3 = (short) atoi( papszValues[3] ); // Blue + oEntry.c4 = 255; + + oColorTable.SetColorEntry( nIndex, &oEntry ); + } + else + { + /* Negative values are valid. At least we can find use of */ + /* them here : http://www.ngdc.noaa.gov/mgg/topo/elev/esri/clr/ */ + /* but there's no way of representing them with GDAL color */ + /* table model */ + if (!bHasWarned) + CPLDebug("EHdr", "Ignoring color index : %d", nIndex); + bHasWarned = TRUE; + } + } + + CSLDestroy( papszValues ); + } + + VSIFCloseL( fp ); + + for( i = 1; i <= poDS->nBands; i++ ) + { + GDALRasterBand *poBand = poDS->GetRasterBand( i ); + poBand->SetColorTable( &oColorTable ); + poBand->SetColorInterpretation( GCI_PaletteIndex ); + } + + poDS->bCLRDirty = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Read statistics (.STX) */ +/* -------------------------------------------------------------------- */ + poDS->ReadSTX(); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *EHdrDataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char **papszParmList ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify input options. */ +/* -------------------------------------------------------------------- */ + if (nBands <= 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "EHdr driver does not support %d bands.\n", nBands); + return NULL; + } + + if( eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 + && eType != GDT_Int16 && eType != GDT_Int32 && eType != GDT_UInt32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create ESRI .hdr labelled dataset with an illegal\n" + "data type (%s).\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to create the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + + fp = VSIFOpenL( pszFilename, "wb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Just write out a couple of bytes to establish the binary */ +/* file, and then close it. */ +/* -------------------------------------------------------------------- */ + VSIFWriteL( (void *) "\0\0", 2, 1, fp ); + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Create the hdr filename. */ +/* -------------------------------------------------------------------- */ + char *pszHdrFilename; + + pszHdrFilename = + CPLStrdup( CPLResetExtension( pszFilename, "hdr" ) ); + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszHdrFilename, "wt" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszHdrFilename ); + CPLFree( pszHdrFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Decide how many bits the file should have. */ +/* -------------------------------------------------------------------- */ + int nRowBytes; + int nBits = GDALGetDataTypeSize(eType); + + if( CSLFetchNameValue( papszParmList, "NBITS" ) != NULL ) + nBits = atoi(CSLFetchNameValue( papszParmList, "NBITS" )); + + nRowBytes = (nBits * nXSize + 7) / 8; + +/* -------------------------------------------------------------------- */ +/* Check for signed byte. */ +/* -------------------------------------------------------------------- */ + const char *pszPixelType = CSLFetchNameValue( papszParmList, "PIXELTYPE" ); + if( pszPixelType == NULL ) + pszPixelType = ""; + +/* -------------------------------------------------------------------- */ +/* Write out the raw definition for the dataset as a whole. */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, "BYTEORDER I\n" ); + VSIFPrintfL( fp, "LAYOUT BIL\n" ); + VSIFPrintfL( fp, "NROWS %d\n", nYSize ); + VSIFPrintfL( fp, "NCOLS %d\n", nXSize ); + VSIFPrintfL( fp, "NBANDS %d\n", nBands ); + VSIFPrintfL( fp, "NBITS %d\n", nBits ); + VSIFPrintfL( fp, "BANDROWBYTES %d\n", nRowBytes ); + VSIFPrintfL( fp, "TOTALROWBYTES %d\n", nRowBytes * nBands ); + + if( eType == GDT_Float32 ) + VSIFPrintfL( fp, "PIXELTYPE FLOAT\n"); + else if( eType == GDT_Int16 || eType == GDT_Int32 ) + VSIFPrintfL( fp, "PIXELTYPE SIGNEDINT\n"); + else if( eType == GDT_Byte && EQUAL(pszPixelType,"SIGNEDBYTE") ) + VSIFPrintfL( fp, "PIXELTYPE SIGNEDINT\n"); + else + VSIFPrintfL( fp, "PIXELTYPE UNSIGNEDINT\n"); + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + VSIFCloseL( fp ); + + CPLFree( pszHdrFilename ); + + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset *EHdrDataset::CreateCopy( const char * pszFilename, + GDALDataset * poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + char **papszAdjustedOptions = CSLDuplicate( papszOptions ); + GDALDataset *poOutDS; + + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "EHdr driver does not support source dataset with zero band.\n"); + CSLDestroy( papszAdjustedOptions ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Ensure we pass on NBITS and PIXELTYPE structure information. */ +/* -------------------------------------------------------------------- */ + if( poSrcDS->GetRasterBand(1)->GetMetadataItem( "NBITS", + "IMAGE_STRUCTURE" ) !=NULL + && CSLFetchNameValue( papszOptions, "NBITS" ) == NULL ) + { + papszAdjustedOptions = + CSLSetNameValue( papszAdjustedOptions, + "NBITS", + poSrcDS->GetRasterBand(1)->GetMetadataItem("NBITS","IMAGE_STRUCTURE") ); + } + + if( poSrcDS->GetRasterBand(1)->GetMetadataItem( "PIXELTYPE", + "IMAGE_STRUCTURE" ) !=NULL + && CSLFetchNameValue( papszOptions, "PIXELTYPE" ) == NULL ) + { + papszAdjustedOptions = + CSLSetNameValue( papszAdjustedOptions, + "PIXELTYPE", + poSrcDS->GetRasterBand(1)->GetMetadataItem("PIXELTYPE","IMAGE_STRUCTURE") ); + } + +/* -------------------------------------------------------------------- */ +/* Proceed with normal copying using the default createcopy */ +/* operators. */ +/* -------------------------------------------------------------------- */ + GDALDriver *poDriver = (GDALDriver *) GDALGetDriverByName( "EHdr" ); + + poOutDS = poDriver->DefaultCreateCopy( pszFilename, poSrcDS, bStrict, + papszAdjustedOptions, + pfnProgress, pProgressData ); + CSLDestroy( papszAdjustedOptions ); + + if( poOutDS != NULL ) + poOutDS->FlushCache(); + + return poOutDS; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double EHdrRasterBand::GetNoDataValue( int *pbSuccess ) +{ + if( pbSuccess ) + *pbSuccess = bNoDataSet; + + if( bNoDataSet ) + return dfNoData; + + return RawRasterBand::GetNoDataValue( pbSuccess ); +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double EHdrRasterBand::GetMinimum( int *pbSuccess ) +{ + if( pbSuccess != NULL ) + *pbSuccess = (minmaxmeanstddev & HAS_MIN_FLAG) != 0; + + if( minmaxmeanstddev & HAS_MIN_FLAG ) + return dfMin; + + return RawRasterBand::GetMinimum( pbSuccess ); +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double EHdrRasterBand::GetMaximum( int *pbSuccess ) +{ + if( pbSuccess != NULL ) + *pbSuccess = (minmaxmeanstddev & HAS_MAX_FLAG) != 0; + + if( minmaxmeanstddev & HAS_MAX_FLAG ) + return dfMax; + + return RawRasterBand::GetMaximum( pbSuccess ); +} + +/************************************************************************/ +/* GetStatistics() */ +/************************************************************************/ + +CPLErr EHdrRasterBand::GetStatistics( int bApproxOK, int bForce, double *pdfMin, double *pdfMax, double *pdfMean, double *pdfStdDev ) +{ + if( (minmaxmeanstddev & HAS_ALL_FLAGS) == HAS_ALL_FLAGS) + { + if ( pdfMin ) *pdfMin = dfMin; + if ( pdfMax ) *pdfMax = dfMax; + if ( pdfMean ) *pdfMean = dfMean; + if ( pdfStdDev ) *pdfStdDev = dfStdDev; + return CE_None; + } + + CPLErr eErr = RawRasterBand::GetStatistics( bApproxOK, bForce, + &dfMin, &dfMax, + &dfMean, &dfStdDev ); + + if( eErr == CE_None ) + { + EHdrDataset* poEDS = (EHdrDataset *) poDS; + + minmaxmeanstddev = HAS_ALL_FLAGS; + + if( poEDS->RewriteSTX() != CE_None ) + RawRasterBand::SetStatistics( dfMin, dfMax, dfMean, dfStdDev ); + + if( pdfMin ) + *pdfMin = dfMin; + if( pdfMax ) + *pdfMax = dfMax; + if( pdfMean ) + *pdfMean = dfMean; + if( pdfStdDev ) + *pdfStdDev = dfStdDev; + } + + return eErr; +} + +/************************************************************************/ +/* SetStatistics() */ +/************************************************************************/ + +CPLErr EHdrRasterBand::SetStatistics( double dfMin, double dfMax, double dfMean, double dfStdDev ) +{ + // avoid churn if nothing is changing. + if( dfMin == this->dfMin + && dfMax == this->dfMax + && dfMean == this->dfMean + && dfStdDev == this->dfStdDev ) + return CE_None; + + this->dfMin = dfMin; + this->dfMax = dfMax; + this->dfMean = dfMean; + this->dfStdDev = dfStdDev; + + // marks stats valid + minmaxmeanstddev = HAS_ALL_FLAGS; + + EHdrDataset* poEDS = (EHdrDataset *) poDS; + + if( poEDS->RewriteSTX() != CE_None ) + return RawRasterBand::SetStatistics( dfMin, dfMax, dfMean, dfStdDev ); + else + return CE_None; +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr EHdrRasterBand::SetColorTable( GDALColorTable *poNewCT ) +{ + CPLErr err = RawRasterBand::SetColorTable( poNewCT ); + if( err != CE_None ) + return err; + + ((EHdrDataset*)poDS)->bCLRDirty = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GDALRegister_EHdr() */ +/************************************************************************/ + +void GDALRegister_EHdr() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "EHdr" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "EHdr" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ESRI .hdr Labelled" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#EHdr" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 Float32" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->pfnOpen = EHdrDataset::Open; + poDriver->pfnCreate = EHdrDataset::Create; + poDriver->pfnCreateCopy = EHdrDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/eirdataset.cpp b/bazaar/plugin/gdal/frmts/raw/eirdataset.cpp new file mode 100644 index 000000000..a3f96bd6b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/eirdataset.cpp @@ -0,0 +1,586 @@ +/****************************************************************************** + * $Id: $ + * + * Project: Erdas EIR Raw Driver + * Purpose: Implementation of EIRDataset + * Author: Adam Milling, amilling@alumni.uwaterloo.ca + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: $"); + +CPL_C_START +void GDALRegister_EIR(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* EIRDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class EIRDataset : public RawDataset +{ + friend class RawRasterBand; + + VSILFILE *fpImage; // image data file + int bGotTransform; + double adfGeoTransform[6]; + int bHDRDirty; + char **papszHDR; + char **papszExtraFiles; + + void ResetKeyValue( const char *pszKey, const char *pszValue ); + const char *GetKeyValue( const char *pszKey, const char *pszDefault = "" ); + + public: + EIRDataset(); + ~EIRDataset(); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + + virtual char **GetFileList(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); +}; + + +/************************************************************************/ +/* ==================================================================== */ +/* EIRDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* EIRDataset() */ +/************************************************************************/ + +EIRDataset::EIRDataset() +{ + fpImage = NULL; + bGotTransform = FALSE; + papszHDR = NULL; + papszExtraFiles = NULL; + bHDRDirty = FALSE; +} + +/************************************************************************/ +/* ~EIRDataset() */ +/************************************************************************/ + +EIRDataset::~EIRDataset() + +{ + FlushCache(); + + if( nBands > 0 && GetAccess() == GA_Update ) + { + int bNoDataSet; + double dfNoData; + RawRasterBand *poBand = (RawRasterBand *) GetRasterBand( 1 ); + + dfNoData = poBand->GetNoDataValue(&bNoDataSet); + if( bNoDataSet ) + { + ResetKeyValue( "NODATA", + CPLString().Printf( "%.8g", dfNoData ) ); + } + } + + if( fpImage != NULL ) + VSIFCloseL( fpImage ); + + CSLDestroy( papszHDR ); + CSLDestroy( papszExtraFiles ); +} + +/************************************************************************/ +/* GetKeyValue() */ +/************************************************************************/ + +const char *EIRDataset::GetKeyValue( const char *pszKey, + const char *pszDefault ) + +{ + int i; + + for( i = 0; papszHDR[i] != NULL; i++ ) + { + if( EQUALN(pszKey,papszHDR[i],strlen(pszKey)) + && isspace((unsigned char)papszHDR[i][strlen(pszKey)]) ) + { + const char *pszValue = papszHDR[i] + strlen(pszKey); + while( isspace((unsigned char)*pszValue) ) + pszValue++; + + return pszValue; + } + } + + return pszDefault; +} + +/************************************************************************/ +/* ResetKeyValue() */ +/* */ +/* Replace or add the keyword with the indicated value in the */ +/* papszHDR list. */ +/************************************************************************/ + +void EIRDataset::ResetKeyValue( const char *pszKey, const char *pszValue ) + +{ + int i; + char szNewLine[82]; + + if( strlen(pszValue) > 65 ) + { + CPLAssert( strlen(pszValue) <= 65 ); + return; + } + + sprintf( szNewLine, "%-15s%s", pszKey, pszValue ); + + for( i = CSLCount(papszHDR)-1; i >= 0; i-- ) + { + if( EQUALN(papszHDR[i],szNewLine,strlen(pszKey)+1 ) ) + { + if( strcmp(papszHDR[i],szNewLine) != 0 ) + { + CPLFree( papszHDR[i] ); + papszHDR[i] = CPLStrdup( szNewLine ); + bHDRDirty = TRUE; + } + return; + } + } + + bHDRDirty = TRUE; + papszHDR = CSLAddString( papszHDR, szNewLine ); +} + + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr EIRDataset::GetGeoTransform( double * padfTransform ) + +{ + if( bGotTransform ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; + } + else + { + return GDALPamDataset::GetGeoTransform( padfTransform ); + } +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **EIRDataset::GetFileList() + +{ + CPLString osPath = CPLGetPath( GetDescription() ); + CPLString osName = CPLGetBasename( GetDescription() ); + char **papszFileList = NULL; + + // Main data file, etc. + papszFileList = GDALPamDataset::GetFileList(); + + // Header file. + papszFileList = CSLInsertStrings( papszFileList, -1, + papszExtraFiles ); + + return papszFileList; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int EIRDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 100 ) + return FALSE; + + if( strstr((const char *) poOpenInfo->pabyHeader, + "IMAGINE_RAW_FILE" ) == NULL ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *EIRDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i; + VSILFILE *fp; + const char * pszLine; + + + if( !Identify( poOpenInfo ) ) + return NULL; + + fp = VSIFOpenL( poOpenInfo->pszFilename, "r" ); + if( fp == NULL ) + return NULL; + + /* header example and description + + IMAGINE_RAW_FILE // must be on first line, by itself + WIDTH 581 // number of columns in the image + HEIGHT 695 // number of rows in the image + NUM_LAYERS 3 // number of spectral bands in the image; default 1 + PIXEL_FILES raw8_3n_ui_sanjack.bl // raster file + // default: same name with no extension + FORMAT BIL // BIL BIP BSQ; default BIL + DATATYPE U8 // U1 U2 U4 U8 U16 U32 S16 S32 F32 F64; default U8 + BYTE_ORDER // LSB MSB; required for U16 U32 S16 S32 F32 F64 + DATA_OFFSET // start of image data in raster file; default 0 bytes + END_RAW_FILE // end RAW file - stop reading + + For a true color image with three bands (R, G, B) stored using 8 bits + for each pixel in each band, DATA_TYPE equals U8 and NUM_LAYERS equals + 3 for a total of 24 bits per pixel. + + Note that the current version of ERDAS Raw Raster Reader/Writer does + not support the LAYER_SKIP_BYTES, RECORD_SKIP_BYTES, TILE_WIDTH and + TILE_HEIGHT directives. Since the reader does not read the PIXEL_FILES + directive, the reader always assumes that the raw binary file is the + dataset, and the name of this file is the name of the header without the + extension. Currently, the reader does not support multiple raw binary + files in one dataset or a single file with both the header and the raw + binary data at the same time. + */ + + bool bDone = FALSE; + int nRows = -1, nCols = -1, nBands = 1; + int nSkipBytes = 0; + int nLineCount = 0; + GDALDataType eDataType = GDT_Byte; + int nBits = 8; + char chByteOrder = 'M'; + char szLayout[10] = "BIL"; + char **papszHDR = NULL; + + // default raster file: same name with no extension + CPLString osPath = CPLGetPath( poOpenInfo->pszFilename ); + CPLString osName = CPLGetBasename( poOpenInfo->pszFilename ); + CPLString osRasterFilename = CPLFormCIFilename( osPath, osName, "" ); + + // parse the header file + while( !bDone && (pszLine = CPLReadLineL( fp )) != NULL ) + { + char **papszTokens; + + nLineCount++; + + if ( (nLineCount == 1) && !EQUAL(pszLine,"IMAGINE_RAW_FILE") ) { + return NULL; + } + + if ( (nLineCount > 50) || EQUAL(pszLine,"END_RAW_FILE") ) { + bDone = TRUE; + break; + } + + if( strlen(pszLine) > 1000 ) + break; + + papszHDR = CSLAddString( papszHDR, pszLine ); + + papszTokens = CSLTokenizeStringComplex( pszLine, " \t", TRUE, FALSE ); + if( CSLCount( papszTokens ) < 2 ) + { + CSLDestroy( papszTokens ); + continue; + } + + if( EQUAL(papszTokens[0],"WIDTH") ) + { + nCols = atoi(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"HEIGHT") ) + { + nRows = atoi(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"NUM_LAYERS") ) + { + nBands = atoi(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"PIXEL_FILES") ) + { + osRasterFilename = CPLFormCIFilename( osPath, papszTokens[1], "" ); + } + else if( EQUAL(papszTokens[0],"FORMAT") ) + { + strncpy( szLayout, papszTokens[1], sizeof(szLayout) ); + szLayout[sizeof(szLayout)-1] = '\0'; + } + else if( EQUAL(papszTokens[0],"DATATYPE") + || EQUAL(papszTokens[0],"DATA_TYPE") ) + { + if ( EQUAL(papszTokens[1], "U1") + || EQUAL(papszTokens[1], "U2") + || EQUAL(papszTokens[1], "U4") + || EQUAL(papszTokens[1], "U8") ) { + nBits = 8; + eDataType = GDT_Byte; + } + else if( EQUAL(papszTokens[1], "U16") ) { + nBits = 16; + eDataType = GDT_UInt16; + } + else if( EQUAL(papszTokens[1], "U32") ) { + nBits = 32; + eDataType = GDT_UInt32; + } + else if( EQUAL(papszTokens[1], "S16") ) { + nBits = 16; + eDataType = GDT_Int16; + } + else if( EQUAL(papszTokens[1], "S32") ) { + nBits = 32; + eDataType = GDT_Int32; + } + else if( EQUAL(papszTokens[1], "F32") ) { + nBits = 32; + eDataType = GDT_Float32; + } + else if( EQUAL(papszTokens[1], "F64") ) { + nBits = 64; + eDataType = GDT_Float64; + } + else { + CPLError( CE_Failure, CPLE_NotSupported, + "EIR driver does not support DATATYPE %s.", + papszTokens[1] ); + CSLDestroy( papszTokens ); + CSLDestroy( papszHDR ); + VSIFCloseL( fp ); + return NULL; + } + } + else if( EQUAL(papszTokens[0],"BYTE_ORDER") ) + { + // M for MSB, L for LSB + chByteOrder = (char) toupper(papszTokens[1][0]); + } + else if( EQUAL(papszTokens[0],"DATA_OFFSET") ) + { + nSkipBytes = atoi(papszTokens[1]); // TBD: is this mapping right? + } + + CSLDestroy( papszTokens ); + } + + VSIFCloseL( fp ); + + +/* -------------------------------------------------------------------- */ +/* Did we get the required keywords? If not we return with */ +/* this never having been considered to be a match. This isn't */ +/* an error! */ +/* -------------------------------------------------------------------- */ + if( nRows == -1 || nCols == -1 ) + { + CSLDestroy( papszHDR ); + return NULL; + } + + if (!GDALCheckDatasetDimensions(nCols, nRows) || + !GDALCheckBandCount(nBands, FALSE)) + { + CSLDestroy( papszHDR ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CSLDestroy( papszHDR ); + CPLError( CE_Failure, CPLE_NotSupported, + "The EIR driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + EIRDataset *poDS; + + poDS = new EIRDataset(); + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = nCols; + poDS->nRasterYSize = nRows; + poDS->papszHDR = papszHDR; + + +/* -------------------------------------------------------------------- */ +/* Open target binary file. */ +/* -------------------------------------------------------------------- */ + poDS->fpImage = VSIFOpenL( osRasterFilename.c_str(), "rb" ); + if( poDS->fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s.\n%s", + osRasterFilename.c_str(), VSIStrerror( errno ) ); + delete poDS; + return NULL; + } + poDS->papszExtraFiles = + CSLAddString( poDS->papszExtraFiles, + osRasterFilename ); + + poDS->eAccess = poOpenInfo->eAccess; + + +/* -------------------------------------------------------------------- */ +/* Compute the line offset. */ +/* -------------------------------------------------------------------- */ + int nItemSize = GDALGetDataTypeSize(eDataType)/8; + int nPixelOffset, nLineOffset; + vsi_l_offset nBandOffset; + + if( EQUAL(szLayout,"BIP") ) + { + nPixelOffset = nItemSize * nBands; + nLineOffset = nPixelOffset * nCols; + nBandOffset = (vsi_l_offset)nItemSize; + } + else if( EQUAL(szLayout,"BSQ") ) + { + nPixelOffset = nItemSize; + nLineOffset = nPixelOffset * nCols; + nBandOffset = (vsi_l_offset)nLineOffset * nRows; + } + else /* assume BIL */ + { + nPixelOffset = nItemSize; + nLineOffset = nItemSize * nBands * nCols; + nBandOffset = (vsi_l_offset)nItemSize * nCols; + } + + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->PamInitialize(); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = nBands; + for( i = 0; i < poDS->nBands; i++ ) + { + RawRasterBand *poBand; + + poBand = + new RawRasterBand( poDS, i+1, poDS->fpImage, + nSkipBytes + nBandOffset * i, + nPixelOffset, nLineOffset, eDataType, +#ifdef CPL_LSB + chByteOrder == 'I' || chByteOrder == 'L', +#else + chByteOrder == 'M', +#endif + nBits); + + + poDS->SetBand( i+1, poBand ); + } + + +/* -------------------------------------------------------------------- */ +/* look for a worldfile */ +/* -------------------------------------------------------------------- */ + + if( !poDS->bGotTransform ) + poDS->bGotTransform = + GDALReadWorldFile( poOpenInfo->pszFilename, 0, + poDS->adfGeoTransform ); + + if( !poDS->bGotTransform ) + poDS->bGotTransform = + GDALReadWorldFile( poOpenInfo->pszFilename, "wld", + poDS->adfGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + + +/************************************************************************/ +/* GDALRegister_EIR() */ +/************************************************************************/ + +void GDALRegister_EIR() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "EIR" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "EIR" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Erdas Imagine Raw" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#EIR" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = EIRDataset::Open; + poDriver->pfnIdentify = EIRDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/envidataset.cpp b/bazaar/plugin/gdal/frmts/raw/envidataset.cpp new file mode 100644 index 000000000..b0b544c2c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/envidataset.cpp @@ -0,0 +1,2738 @@ +/****************************************************************************** + * $Id: envidataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: ENVI .hdr Driver + * Purpose: Implementation of ENVI .hdr labelled raw raster support. + * Author: Frank Warmerdam, warmerdam@pobox.com + * Maintainer: Chris Padwick (cpadwick at ittvis.com) + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: envidataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_ENVI(void); +CPL_C_END + +static const int anUsgsEsriZones[] = +{ + 101, 3101, + 102, 3126, + 201, 3151, + 202, 3176, + 203, 3201, + 301, 3226, + 302, 3251, + 401, 3276, + 402, 3301, + 403, 3326, + 404, 3351, + 405, 3376, + 406, 3401, + 407, 3426, + 501, 3451, + 502, 3476, + 503, 3501, + 600, 3526, + 700, 3551, + 901, 3601, + 902, 3626, + 903, 3576, + 1001, 3651, + 1002, 3676, + 1101, 3701, + 1102, 3726, + 1103, 3751, + 1201, 3776, + 1202, 3801, + 1301, 3826, + 1302, 3851, + 1401, 3876, + 1402, 3901, + 1501, 3926, + 1502, 3951, + 1601, 3976, + 1602, 4001, + 1701, 4026, + 1702, 4051, + 1703, 6426, + 1801, 4076, + 1802, 4101, + 1900, 4126, + 2001, 4151, + 2002, 4176, + 2101, 4201, + 2102, 4226, + 2103, 4251, + 2111, 6351, + 2112, 6376, + 2113, 6401, + 2201, 4276, + 2202, 4301, + 2203, 4326, + 2301, 4351, + 2302, 4376, + 2401, 4401, + 2402, 4426, + 2403, 4451, + 2500, 0, + 2501, 4476, + 2502, 4501, + 2503, 4526, + 2600, 0, + 2601, 4551, + 2602, 4576, + 2701, 4601, + 2702, 4626, + 2703, 4651, + 2800, 4676, + 2900, 4701, + 3001, 4726, + 3002, 4751, + 3003, 4776, + 3101, 4801, + 3102, 4826, + 3103, 4851, + 3104, 4876, + 3200, 4901, + 3301, 4926, + 3302, 4951, + 3401, 4976, + 3402, 5001, + 3501, 5026, + 3502, 5051, + 3601, 5076, + 3602, 5101, + 3701, 5126, + 3702, 5151, + 3800, 5176, + 3900, 0, + 3901, 5201, + 3902, 5226, + 4001, 5251, + 4002, 5276, + 4100, 5301, + 4201, 5326, + 4202, 5351, + 4203, 5376, + 4204, 5401, + 4205, 5426, + 4301, 5451, + 4302, 5476, + 4303, 5501, + 4400, 5526, + 4501, 5551, + 4502, 5576, + 4601, 5601, + 4602, 5626, + 4701, 5651, + 4702, 5676, + 4801, 5701, + 4802, 5726, + 4803, 5751, + 4901, 5776, + 4902, 5801, + 4903, 5826, + 4904, 5851, + 5001, 6101, + 5002, 6126, + 5003, 6151, + 5004, 6176, + 5005, 6201, + 5006, 6226, + 5007, 6251, + 5008, 6276, + 5009, 6301, + 5010, 6326, + 5101, 5876, + 5102, 5901, + 5103, 5926, + 5104, 5951, + 5105, 5976, + 5201, 6001, + 5200, 6026, + 5200, 6076, + 5201, 6051, + 5202, 6051, + 5300, 0, + 5400, 0 +}; + +/************************************************************************/ +/* ITTVISToUSGSZone() */ +/* */ +/* Convert ITTVIS style state plane zones to NOS style state */ +/* plane zones. The ENVI default is to use the new NOS zones, */ +/* but the old state plane zones can be used. Handle this. */ +/************************************************************************/ + +static int ITTVISToUSGSZone( int nITTVISZone ) + +{ + int nPairs = sizeof(anUsgsEsriZones) / (2*sizeof(int)); + int i; + + // Default is to use the zone as-is, as long as it is in the + // available list + for( i = 0; i < nPairs; i++ ) + { + if( anUsgsEsriZones[i*2] == nITTVISZone ) + return anUsgsEsriZones[i*2]; + } + + // If not found in the new style, see if it is present in the + // old style list and convert it. We don't expect to see this + // often, but older files allowed it and may still exist. + for( i = 0; i < nPairs; i++ ) + { + if( anUsgsEsriZones[i*2+1] == nITTVISZone ) + return anUsgsEsriZones[i*2]; + } + + return nITTVISZone; // perhaps it *is* the USGS zone? +} + +/************************************************************************/ +/* ==================================================================== */ +/* ENVIDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class ENVIRasterBand; + +class ENVIDataset : public RawDataset +{ + friend class ENVIRasterBand; + + VSILFILE *fpImage; // image data file. + VSILFILE *fp; // header file + char *pszHDRFilename; + + int bFoundMapinfo; + + int bHeaderDirty; + + double adfGeoTransform[6]; + + char *pszProjection; + + char **papszHeader; + + CPLString osStaFilename; + + int ReadHeader( VSILFILE * ); + int ProcessMapinfo( const char * ); + void ProcessRPCinfo( const char * ,int ,int); + void ProcessStatsFile(); + int byteSwapInt(int); + float byteSwapFloat(float); + double byteSwapDouble(double); + void SetENVIDatum( OGRSpatialReference *, const char * ); + void SetENVIEllipse( OGRSpatialReference *, char ** ); + void WriteProjectionInfo(); + int ParseRpcCoeffsMetaDataString(const char *psName, char *papszVal[], int& idx); + int WriteRpcInfo(); + int WritePseudoGcpInfo(); + + char **SplitList( const char * ); + + enum Interleave { BSQ, BIL, BIP } interleave; + static int GetEnviType(GDALDataType eType); + + public: + ENVIDataset(); + ~ENVIDataset(); + + virtual void FlushCache( void ); + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual CPLErr SetGeoTransform( double * ); + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + virtual char **GetFileList(void); + + virtual void SetDescription( const char * ); + + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* ENVIRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class ENVIRasterBand : public RawRasterBand +{ + public: + ENVIRasterBand( GDALDataset *poDS, int nBand, void * fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder, + int bIsVSIL = FALSE, int bOwnsFP = FALSE ); + + virtual void SetDescription( const char * ); + + virtual CPLErr SetCategoryNames( char ** ); +}; + +/************************************************************************/ +/* ENVIDataset() */ +/************************************************************************/ + +ENVIDataset::ENVIDataset() +{ + fpImage = NULL; + fp = NULL; + pszHDRFilename = NULL; + pszProjection = CPLStrdup(""); + + papszHeader = NULL; + + bFoundMapinfo = FALSE; + + bHeaderDirty = FALSE; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~ENVIDataset() */ +/************************************************************************/ + +ENVIDataset::~ENVIDataset() + +{ + FlushCache(); + if( fpImage ) + VSIFCloseL( fpImage ); + if( fp ) + VSIFCloseL( fp ); + CPLFree( pszProjection ); + CSLDestroy( papszHeader ); + CPLFree(pszHDRFilename); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void ENVIDataset::FlushCache() + +{ + RawDataset::FlushCache(); + + GDALRasterBand* band = (GetRasterCount() > 0) ? GetRasterBand(1) : NULL; + + if ( band == NULL || !bHeaderDirty ) + return; + + // If opening an existing file in Update mode (i.e. "r+") we need to make + // sure any existing content is cleared, otherwise the file may contain + // trailing content from the previous write. + VSIFTruncateL( fp, 0 ); + + VSIFSeekL( fp, 0, SEEK_SET ); +/* -------------------------------------------------------------------- */ +/* Rewrite out the header. */ +/* -------------------------------------------------------------------- */ + int iBigEndian; + + const char *pszInterleaving; + char** catNames; + +#ifdef CPL_LSB + iBigEndian = 0; +#else + iBigEndian = 1; +#endif + + VSIFPrintfL( fp, "ENVI\n" ); + if ("" != sDescription) + VSIFPrintfL( fp, "description = {\n%s}\n", sDescription.c_str()); + VSIFPrintfL( fp, "samples = %d\nlines = %d\nbands = %d\n", + nRasterXSize, nRasterYSize, nBands ); + + catNames = band->GetCategoryNames(); + + VSIFPrintfL( fp, "header offset = 0\n"); + if (0 == catNames) + VSIFPrintfL( fp, "file type = ENVI Standard\n" ); + else + VSIFPrintfL( fp, "file type = ENVI Classification\n" ); + + int iENVIType = GetEnviType(band->GetRasterDataType()); + VSIFPrintfL( fp, "data type = %d\n", iENVIType ); + switch (interleave) + { + case BIP: + pszInterleaving = "bip"; // interleaved by pixel + break; + case BIL: + pszInterleaving = "bil"; // interleaved by line + break; + case BSQ: + pszInterleaving = "bsq"; // band sequental by default + break; + default: + pszInterleaving = "bsq"; + break; + } + VSIFPrintfL( fp, "interleave = %s\n", pszInterleaving); + VSIFPrintfL( fp, "byte order = %d\n", iBigEndian ); + +/* -------------------------------------------------------------------- */ +/* Write class and color information */ +/* -------------------------------------------------------------------- */ + catNames = band->GetCategoryNames(); + if (0 != catNames) + { + int nrClasses = 0; + while (*catNames++) + ++nrClasses; + + if (nrClasses > 0) + { + VSIFPrintfL( fp, "classes = %d\n", nrClasses ); + + GDALColorTable* colorTable = band->GetColorTable(); + if (0 != colorTable) + { + int nrColors = colorTable->GetColorEntryCount(); + if (nrColors > nrClasses) + nrColors = nrClasses; + VSIFPrintfL( fp, "class lookup = {\n"); + for (int i = 0; i < nrColors; ++i) + { + const GDALColorEntry* color = colorTable->GetColorEntry(i); + VSIFPrintfL(fp, "%d, %d, %d", color->c1, color->c2, color->c3); + if (i < nrColors - 1) + { + VSIFPrintfL(fp, ", "); + if (0 == (i+1) % 5) + VSIFPrintfL(fp, "\n"); + } + } + VSIFPrintfL(fp, "}\n"); + } + + catNames = band->GetCategoryNames(); + if (0 != *catNames) + { + VSIFPrintfL( fp, "class names = {\n%s", *catNames++); + int i = 0; + while (*catNames) { + VSIFPrintfL( fp, ","); + if (0 == (++i) % 5) + VSIFPrintfL(fp, "\n"); + VSIFPrintfL( fp, " %s", *catNames++); + } + VSIFPrintfL( fp, "}\n"); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Write the rest of header. */ +/* -------------------------------------------------------------------- */ + + // only one map info type should be set + // - rpc + // - pseudo/gcp + // - standard + if ( !WriteRpcInfo() ) // are rpcs in the metadata + { + if ( !WritePseudoGcpInfo() ) // are gcps in the metadata + { + WriteProjectionInfo(); // standard - affine xform/coord sys str + } + } + + + VSIFPrintfL( fp, "band names = {\n" ); + for ( int i = 1; i <= nBands; i++ ) + { + CPLString sBandDesc = GetRasterBand( i )->GetDescription(); + + if ( sBandDesc == "" ) + sBandDesc = CPLSPrintf( "Band %d", i ); + VSIFPrintfL( fp, "%s", sBandDesc.c_str() ); + if ( i != nBands ) + VSIFPrintfL( fp, ",\n" ); + } + VSIFPrintfL( fp, "}\n" ); + +/* -------------------------------------------------------------------- */ +/* Write the metadata that was read into the ENVI domain */ +/* -------------------------------------------------------------------- */ + char** papszENVIMetadata = GetMetadata("ENVI"); + + int i; + int count = CSLCount(papszENVIMetadata); + char **papszTokens; + + // For every item of metadata in the ENVI domain + for (i = 0; i < count; i++) + { + // Split the entry into two parts at the = character + char *pszEntry = papszENVIMetadata[i]; + papszTokens = CSLTokenizeString2( pszEntry, "=", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES); + + if (CSLCount(papszTokens) != 2) + { + CPLDebug("ENVI", "Line of header file could not be split at = into two elements: %s", papszENVIMetadata[i]); + CSLDestroy( papszTokens ); + continue; + } + // Replace _'s in the string with spaces + std::string poKey(papszTokens[0]); + std::replace(poKey.begin(), poKey.end(), '_', ' '); + + // Don't write it out if it is one of the bits of metadata that is written out elsewhere in this routine + if (poKey == "description" || poKey == "samples" || poKey == "lines" || + poKey == "bands" || poKey == "header offset" || poKey == "file type" || + poKey == "data type" || poKey == "interleave" || poKey == "byte order" || + poKey == "class names" || poKey == "band names" || poKey == "map info" || + poKey == "projection info") + { + CSLDestroy( papszTokens ); + continue; + } + VSIFPrintfL( fp, "%s = %s\n", poKey.c_str(), papszTokens[1]); + CSLDestroy( papszTokens ); + } + + /* Clean dirty flag */ + bHeaderDirty = FALSE; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **ENVIDataset::GetFileList() + +{ + char **papszFileList = NULL; + + // Main data file, etc. + papszFileList = RawDataset::GetFileList(); + + // Header file. + papszFileList = CSLAddString( papszFileList, pszHDRFilename ); + + // Statistics file + if (osStaFilename.size() != 0) + papszFileList = CSLAddString( papszFileList, osStaFilename ); + + return papszFileList; +} + +/************************************************************************/ +/* GetEPSGGeogCS() */ +/* */ +/* Try to establish what the EPSG code for this coordinate */ +/* systems GEOGCS might be. Returns -1 if no reasonable guess */ +/* can be made. */ +/* */ +/* TODO: We really need to do some name lookups. */ +/************************************************************************/ + +static int ENVIGetEPSGGeogCS( OGRSpatialReference *poThis ) + +{ + const char *pszAuthName = poThis->GetAuthorityName( "GEOGCS" ); + +/* -------------------------------------------------------------------- */ +/* Do we already have it? */ +/* -------------------------------------------------------------------- */ + if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") ) + return atoi(poThis->GetAuthorityCode( "GEOGCS" )); + +/* -------------------------------------------------------------------- */ +/* Get the datum and geogcs names. */ +/* -------------------------------------------------------------------- */ + const char *pszGEOGCS = poThis->GetAttrValue( "GEOGCS" ); + const char *pszDatum = poThis->GetAttrValue( "DATUM" ); + + // We can only operate on coordinate systems with a geogcs. + if( pszGEOGCS == NULL || pszDatum == NULL ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Is this a "well known" geographic coordinate system? */ +/* -------------------------------------------------------------------- */ + int bWGS, bNAD; + + bWGS = strstr(pszGEOGCS,"WGS") != NULL + || strstr(pszDatum, "WGS") + || strstr(pszGEOGCS,"World Geodetic System") + || strstr(pszGEOGCS,"World_Geodetic_System") + || strstr(pszDatum, "World Geodetic System") + || strstr(pszDatum, "World_Geodetic_System"); + + bNAD = strstr(pszGEOGCS,"NAD") != NULL + || strstr(pszDatum, "NAD") + || strstr(pszGEOGCS,"North American") + || strstr(pszGEOGCS,"North_American") + || strstr(pszDatum, "North American") + || strstr(pszDatum, "North_American"); + + if( bWGS && (strstr(pszGEOGCS,"84") || strstr(pszDatum,"84")) ) + return 4326; + + if( bWGS && (strstr(pszGEOGCS,"72") || strstr(pszDatum,"72")) ) + return 4322; + + if( bNAD && (strstr(pszGEOGCS,"83") || strstr(pszDatum,"83")) ) + return 4269; + + if( bNAD && (strstr(pszGEOGCS,"27") || strstr(pszDatum,"27")) ) + return 4267; + +/* -------------------------------------------------------------------- */ +/* If we know the datum, associate the most likely GCS with */ +/* it. */ +/* -------------------------------------------------------------------- */ + pszAuthName = poThis->GetAuthorityName( "GEOGCS|DATUM" ); + + if( pszAuthName != NULL + && EQUAL(pszAuthName,"epsg") + && poThis->GetPrimeMeridian() == 0.0 ) + { + int nDatum = atoi(poThis->GetAuthorityCode("GEOGCS|DATUM")); + + if( nDatum >= 6000 && nDatum <= 6999 ) + return nDatum - 2000; + } + + return -1; +} + +/************************************************************************/ +/* WriteProjectionInfo() */ +/************************************************************************/ + +void ENVIDataset::WriteProjectionInfo() + +{ +/* -------------------------------------------------------------------- */ +/* Format the location (geotransform) portion of the map info */ +/* line. */ +/* -------------------------------------------------------------------- */ + CPLString osLocation; + + osLocation.Printf( "1, 1, %.15g, %.15g, %.15g, %.15g", + adfGeoTransform[0], adfGeoTransform[3], + adfGeoTransform[1], fabs(adfGeoTransform[5]) ); + +/* -------------------------------------------------------------------- */ +/* Minimal case - write out simple geotransform if we have a */ +/* non-default geotransform. */ +/* -------------------------------------------------------------------- */ + if( pszProjection == NULL || strlen(pszProjection) == 0 || + (strlen(pszProjection) >= 8 && strncmp(pszProjection, "LOCAL_CS", 8) == 0 ) ) + { + if( adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 + || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 + || adfGeoTransform[4] != 0.0 || adfGeoTransform[5] != 1.0 ) + { + const char* pszHemisphere = "North"; + VSIFPrintfL( fp, "map info = {Arbitrary, %s, %d, %s}\n", + osLocation.c_str(), 0, pszHemisphere); + } + return; + } + +/* -------------------------------------------------------------------- */ +/* Ingest WKT. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + + char *pszProj = pszProjection; + + if( oSRS.importFromWkt( &pszProj ) != OGRERR_NONE ) + return; + +/* -------------------------------------------------------------------- */ +/* Try to translate the datum and get major/minor ellipsoid */ +/* values. */ +/* -------------------------------------------------------------------- */ + int nEPSG_GCS = ENVIGetEPSGGeogCS( &oSRS ); + CPLString osDatum, osCommaDatum; + double dfA, dfB; + + if( nEPSG_GCS == 4326 ) + osDatum = "WGS-84"; + else if( nEPSG_GCS == 4322 ) + osDatum = "WGS-72"; + else if( nEPSG_GCS == 4269 ) + osDatum = "North America 1983"; + else if( nEPSG_GCS == 4267 ) + osDatum = "North America 1927"; + else if( nEPSG_GCS == 4230 ) + osDatum = "European 1950"; + else if( nEPSG_GCS == 4277 ) + osDatum = "Ordnance Survey of Great Britain '36"; + else if( nEPSG_GCS == 4291 ) + osDatum = "SAD-69/Brazil"; + else if( nEPSG_GCS == 4283 ) + osDatum = "Geocentric Datum of Australia 1994"; + else if( nEPSG_GCS == 4275 ) + osDatum = "Nouvelle Triangulation Francaise IGN"; + + if( osDatum != "" ) + osCommaDatum.Printf( ",%s", osDatum.c_str() ); + + dfA = oSRS.GetSemiMajor(); + dfB = oSRS.GetSemiMinor(); + +/* -------------------------------------------------------------------- */ +/* Do we have unusual linear units? */ +/* -------------------------------------------------------------------- */ + CPLString osOptionalUnits; + if( fabs(oSRS.GetLinearUnits()-0.3048) < 0.0001 ) + osOptionalUnits = ", units=Feet"; + +/* -------------------------------------------------------------------- */ +/* Handle UTM case. */ +/* -------------------------------------------------------------------- */ + const char *pszHemisphere; + const char *pszProjName = oSRS.GetAttrValue("PROJECTION"); + int bNorth; + int iUTMZone; + + iUTMZone = oSRS.GetUTMZone( &bNorth ); + if ( iUTMZone ) + { + if ( bNorth ) + pszHemisphere = "North"; + else + pszHemisphere = "South"; + + VSIFPrintfL( fp, "map info = {UTM, %s, %d, %s%s%s}\n", + osLocation.c_str(), iUTMZone, pszHemisphere, + osCommaDatum.c_str(), osOptionalUnits.c_str() ); + } + else if( oSRS.IsGeographic() ) + { + VSIFPrintfL( fp, "map info = {Geographic Lat/Lon, %s%s}\n", + osLocation.c_str(), osCommaDatum.c_str()); + } + else if( pszProjName == NULL ) + { + // what to do? + } + else if( EQUAL(pszProjName,SRS_PT_NEW_ZEALAND_MAP_GRID) ) + { + VSIFPrintfL( fp, "map info = {New Zealand Map Grid, %s%s%s}\n", + osLocation.c_str(), + osCommaDatum.c_str(), osOptionalUnits.c_str() ); + + VSIFPrintfL( fp, "projection info = {39, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, New Zealand Map Grid}\n", + dfA, dfB, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), + osCommaDatum.c_str() ); + } + else if( EQUAL(pszProjName,SRS_PT_TRANSVERSE_MERCATOR) ) + { + VSIFPrintfL( fp, "map info = {Transverse Mercator, %s%s%s}\n", + osLocation.c_str(), + osCommaDatum.c_str(), osOptionalUnits.c_str() ); + + VSIFPrintfL( fp, "projection info = {3, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Transverse Mercator}\n", + dfA, dfB, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), + oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + osCommaDatum.c_str() ); + } + else if( EQUAL(pszProjName,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) + || EQUAL(pszProjName,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM) ) + { + VSIFPrintfL( fp, "map info = {Lambert Conformal Conic, %s%s%s}\n", + osLocation.c_str(), + osCommaDatum.c_str(), osOptionalUnits.c_str() ); + + VSIFPrintfL( fp, "projection info = {4, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Lambert Conformal Conic}\n", + dfA, dfB, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), + oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), + oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0), + osCommaDatum.c_str() ); + } + else if( EQUAL(pszProjName, + SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN) ) + { + VSIFPrintfL( fp, "map info = {Hotine Oblique Mercator A, %s%s%s}\n", + osLocation.c_str(), + osCommaDatum.c_str(), osOptionalUnits.c_str() ); + + VSIFPrintfL( fp, "projection info = {5, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Hotine Oblique Mercator A}\n", + dfA, dfB, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_1,0.0), + oSRS.GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_1,0.0), + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_2,0.0), + oSRS.GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_2,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), + oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + osCommaDatum.c_str() ); + } + else if( EQUAL(pszProjName,SRS_PT_HOTINE_OBLIQUE_MERCATOR) ) + { + VSIFPrintfL( fp, "map info = {Hotine Oblique Mercator B, %s%s%s}\n", + osLocation.c_str(), + osCommaDatum.c_str(), osOptionalUnits.c_str() ); + + VSIFPrintfL( fp, "projection info = {6, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Hotine Oblique Mercator B}\n", + dfA, dfB, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + oSRS.GetNormProjParm(SRS_PP_AZIMUTH,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), + oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + osCommaDatum.c_str() ); + } + else if( EQUAL(pszProjName,SRS_PT_STEREOGRAPHIC) + || EQUAL(pszProjName,SRS_PT_OBLIQUE_STEREOGRAPHIC) ) + { + VSIFPrintfL( fp, "map info = {Stereographic (ellipsoid), %s%s%s}\n", + osLocation.c_str(), + osCommaDatum.c_str(), osOptionalUnits.c_str() ); + + VSIFPrintfL( fp, "projection info = {7, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %s, Stereographic (ellipsoid)}\n", + dfA, dfB, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), + oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + osCommaDatum.c_str() ); + } + else if( EQUAL(pszProjName,SRS_PT_ALBERS_CONIC_EQUAL_AREA) ) + { + VSIFPrintfL( fp, "map info = {Albers Conical Equal Area, %s%s%s}\n", + osLocation.c_str(), + osCommaDatum.c_str(), osOptionalUnits.c_str() ); + + VSIFPrintfL( fp, "projection info = {9, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Albers Conical Equal Area}\n", + dfA, dfB, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), + oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), + oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0), + osCommaDatum.c_str() ); + } + else if( EQUAL(pszProjName,SRS_PT_POLYCONIC) ) + { + VSIFPrintfL( fp, "map info = {Polyconic, %s%s%s}\n", + osLocation.c_str(), + osCommaDatum.c_str(), osOptionalUnits.c_str() ); + + VSIFPrintfL( fp, "projection info = {10, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Polyconic}\n", + dfA, dfB, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), + osCommaDatum.c_str() ); + } + else if( EQUAL(pszProjName,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) + { + VSIFPrintfL( fp, "map info = {Lambert Azimuthal Equal Area, %s%s%s}\n", + osLocation.c_str(), + osCommaDatum.c_str(), osOptionalUnits.c_str() ); + + VSIFPrintfL( fp, "projection info = {11, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Lambert Azimuthal Equal Area}\n", + dfA, dfB, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), + osCommaDatum.c_str() ); + } + else if( EQUAL(pszProjName,SRS_PT_AZIMUTHAL_EQUIDISTANT) ) + { + VSIFPrintfL( fp, "map info = {Azimuthal Equadistant, %s%s%s}\n", + osLocation.c_str(), + osCommaDatum.c_str(), osOptionalUnits.c_str() ); + + VSIFPrintfL( fp, "projection info = {12, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Azimuthal Equadistant}\n", + dfA, dfB, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), + osCommaDatum.c_str() ); + } + else if( EQUAL(pszProjName,SRS_PT_POLAR_STEREOGRAPHIC) ) + { + VSIFPrintfL( fp, "map info = {Polar Stereographic, %s%s%s}\n", + osLocation.c_str(), + osCommaDatum.c_str(), osOptionalUnits.c_str() ); + + VSIFPrintfL( fp, "projection info = {31, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Polar Stereographic}\n", + dfA, dfB, + oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,90.0), + oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), + osCommaDatum.c_str() ); + } + else + { + VSIFPrintfL( fp, "map info = {%s, %s}\n", + pszProjName, osLocation.c_str()); + } + + // write out coordinate system string + if ( oSRS.morphToESRI() == OGRERR_NONE ) + { + char *pszProjESRI = NULL; + if ( oSRS.exportToWkt(&pszProjESRI) == OGRERR_NONE ) + { + if ( strlen(pszProjESRI) ) + VSIFPrintfL( fp, "coordinate system string = {%s}\n", pszProjESRI); + } + CPLFree(pszProjESRI); + pszProjESRI = NULL; + } +} + +/************************************************************************/ +/* ParseRpcCoeffsMetaDataString() */ +/************************************************************************/ + +int ENVIDataset::ParseRpcCoeffsMetaDataString(const char *psName, char **papszVal, + int& idx) +{ + // separate one string with 20 coefficients into an array of 20 strings. + const char *psz20Vals = GetMetadataItem(psName, "RPC"); + if (!psz20Vals) + return FALSE; + + char** papszArr = CSLTokenizeString2(psz20Vals, " ", 0); + if (!papszArr) + return FALSE; + + int x = 0; + while ((papszArr[x] != NULL) && (x < 20)) + { + papszVal[idx++] = CPLStrdup(papszArr[x]); + x++; + } + + CSLDestroy(papszArr); + + return (x == 20); +} + +#define CPLStrdupIfNotNull(x) ((x) ? CPLStrdup(x) : NULL) + +/************************************************************************/ +/* WriteRpcInfo() */ +/************************************************************************/ + +int ENVIDataset::WriteRpcInfo() +{ + // write out 90 rpc coeffs into the envi header plus 3 envi specific rpc values + // returns 0 if the coeffs are not present or not valid + int bRet = FALSE; + int x, idx = 0; + char* papszVal[93]; + + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LINE_OFF", "RPC")); + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("SAMP_OFF", "RPC")); + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LAT_OFF", "RPC")); + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LONG_OFF", "RPC")); + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("HEIGHT_OFF", "RPC")); + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LINE_SCALE", "RPC")); + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("SAMP_SCALE", "RPC")); + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LAT_SCALE", "RPC")); + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LONG_SCALE", "RPC")); + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("HEIGHT_SCALE", "RPC")); + + for (x=0; x<10; x++) // if we do not have 10 values we return 0 + { + if (!papszVal[x]) + goto end; + } + + if (!ParseRpcCoeffsMetaDataString("LINE_NUM_COEFF", papszVal, idx)) + goto end; + + if (!ParseRpcCoeffsMetaDataString("LINE_DEN_COEFF", papszVal, idx)) + goto end; + + if (!ParseRpcCoeffsMetaDataString("SAMP_NUM_COEFF", papszVal, idx)) + goto end; + + if (!ParseRpcCoeffsMetaDataString("SAMP_DEN_COEFF", papszVal, idx)) + goto end; + + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("TILE_ROW_OFFSET", "RPC")); + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("TILE_COL_OFFSET", "RPC")); + papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("ENVI_RPC_EMULATION", "RPC")); + CPLAssert(idx == 93); + for (x=90; x<93; x++) + { + if (!papszVal[x]) + goto end; + } + + // ok all the needed 93 values are present so write the rpcs into the envi header + x = 1; + VSIFPrintfL(fp, "rpc info = {\n"); + for (int iR=0; iR<93; iR++) + { + if (papszVal[iR][0] == '-') + VSIFPrintfL(fp, " %s", papszVal[iR]); + else + VSIFPrintfL(fp, " %s", papszVal[iR]); + + if (iR<92) + VSIFPrintfL(fp, ","); + + if ((x % 4) == 0) + VSIFPrintfL(fp, "\n"); + + x++; + if (x > 4) + x = 1; + } + + VSIFPrintfL(fp, "}\n" ); + + bRet = TRUE; + +end: + for (x=0;x iFStart && pszInput[iFEnd] == ' ' ) + iFEnd--; + + pszInput[iFEnd + 1] = '\0'; + papszReturn = CSLAddString( papszReturn, pszInput + iFStart ); + } + + CPLFree( pszInput ); + + return papszReturn; +} + +/************************************************************************/ +/* SetENVIDatum() */ +/************************************************************************/ + +void ENVIDataset::SetENVIDatum( OGRSpatialReference *poSRS, + const char *pszENVIDatumName ) + +{ + // datums + if( EQUAL(pszENVIDatumName, "WGS-84") ) + poSRS->SetWellKnownGeogCS( "WGS84" ); + else if( EQUAL(pszENVIDatumName, "WGS-72") ) + poSRS->SetWellKnownGeogCS( "WGS72" ); + else if( EQUAL(pszENVIDatumName, "North America 1983") ) + poSRS->SetWellKnownGeogCS( "NAD83" ); + else if( EQUAL(pszENVIDatumName, "North America 1927") + || strstr(pszENVIDatumName,"NAD27") + || strstr(pszENVIDatumName,"NAD-27") ) + poSRS->SetWellKnownGeogCS( "NAD27" ); + else if( EQUALN(pszENVIDatumName, "European 1950",13) ) + poSRS->SetWellKnownGeogCS( "EPSG:4230" ); + else if( EQUAL(pszENVIDatumName, "Ordnance Survey of Great Britain '36") ) + poSRS->SetWellKnownGeogCS( "EPSG:4277" ); + else if( EQUAL(pszENVIDatumName, "SAD-69/Brazil") ) + poSRS->SetWellKnownGeogCS( "EPSG:4291" ); + else if( EQUAL(pszENVIDatumName, "Geocentric Datum of Australia 1994") ) + poSRS->SetWellKnownGeogCS( "EPSG:4283" ); + else if( EQUAL(pszENVIDatumName, "Australian Geodetic 1984") ) + poSRS->SetWellKnownGeogCS( "EPSG:4203" ); + else if( EQUAL(pszENVIDatumName, "Nouvelle Triangulation Francaise IGN") ) + poSRS->SetWellKnownGeogCS( "EPSG:4275" ); + + // Ellipsoids + else if( EQUAL(pszENVIDatumName, "GRS 80") ) + poSRS->SetWellKnownGeogCS( "NAD83" ); + else if( EQUAL(pszENVIDatumName, "Airy") ) + poSRS->SetWellKnownGeogCS( "EPSG:4001" ); + else if( EQUAL(pszENVIDatumName, "Australian National") ) + poSRS->SetWellKnownGeogCS( "EPSG:4003" ); + else if( EQUAL(pszENVIDatumName, "Bessel 1841") ) + poSRS->SetWellKnownGeogCS( "EPSG:4004" ); + else if( EQUAL(pszENVIDatumName, "Clark 1866") ) + poSRS->SetWellKnownGeogCS( "EPSG:4008" ); + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unrecognised datum '%s', defaulting to WGS84.", pszENVIDatumName); + poSRS->SetWellKnownGeogCS( "WGS84" ); + } +} + +/************************************************************************/ +/* SetENVIEllipse() */ +/************************************************************************/ + +void ENVIDataset::SetENVIEllipse( OGRSpatialReference *poSRS, + char **papszPI_EI ) + +{ + double dfA = CPLAtofM(papszPI_EI[0]); + double dfB = CPLAtofM(papszPI_EI[1]); + double dfInvF; + + if( fabs(dfA-dfB) < 0.1 ) + dfInvF = 0.0; // sphere + else + dfInvF = dfA / (dfA - dfB); + + + poSRS->SetGeogCS( "Ellipse Based", "Ellipse Based", "Unnamed", + dfA, dfInvF ); +} + +/************************************************************************/ +/* ProcessMapinfo() */ +/* */ +/* Extract projection, and geotransform from a mapinfo value in */ +/* the header. */ +/************************************************************************/ + +int ENVIDataset::ProcessMapinfo( const char *pszMapinfo ) + +{ + char **papszFields; + int nCount; + OGRSpatialReference oSRS; + + papszFields = SplitList( pszMapinfo ); + nCount = CSLCount(papszFields); + + if( nCount < 7 ) + { + CSLDestroy( papszFields ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Check if we have coordinate system string, and if so parse it. */ +/* -------------------------------------------------------------------- */ + char **papszCSS = NULL; + if( CSLFetchNameValue( papszHeader, "coordinate_system_string" ) != NULL ) + { + papszCSS = CSLTokenizeString2( + CSLFetchNameValue( papszHeader, "coordinate_system_string" ), + "{}", CSLT_PRESERVEQUOTES ); + } + +/* -------------------------------------------------------------------- */ +/* Check if we have projection info, and if so parse it. */ +/* -------------------------------------------------------------------- */ + char **papszPI = NULL; + int nPICount = 0; + if( CSLFetchNameValue( papszHeader, "projection_info" ) != NULL ) + { + papszPI = SplitList( + CSLFetchNameValue( papszHeader, "projection_info" ) ); + nPICount = CSLCount(papszPI); + } + +/* -------------------------------------------------------------------- */ +/* Capture geotransform. */ +/* -------------------------------------------------------------------- */ + adfGeoTransform[1] = CPLAtof(papszFields[5]); // Pixel width + adfGeoTransform[5] = -CPLAtof(papszFields[6]); // Pixel height + adfGeoTransform[0] = // Upper left X coordinate + CPLAtof(papszFields[3]) - (CPLAtof(papszFields[1]) - 1) * adfGeoTransform[1]; + adfGeoTransform[3] = // Upper left Y coordinate + CPLAtof(papszFields[4]) - (CPLAtof(papszFields[2]) - 1) * adfGeoTransform[5]; + adfGeoTransform[2] = 0.0; + adfGeoTransform[4] = 0.0; + +/* -------------------------------------------------------------------- */ +/* Capture projection. */ +/* -------------------------------------------------------------------- */ + if ( oSRS.importFromESRI( papszCSS ) != OGRERR_NONE ) + { + oSRS.Clear(); + + if( EQUALN(papszFields[0],"UTM",3) && nCount >= 9 ) + { + oSRS.SetUTM( atoi(papszFields[7]), + !EQUAL(papszFields[8],"South") ); + if( nCount >= 10 && strstr(papszFields[9],"=") == NULL ) + SetENVIDatum( &oSRS, papszFields[9] ); + else + oSRS.SetWellKnownGeogCS( "NAD27" ); + } + else if( EQUALN(papszFields[0],"State Plane (NAD 27)",19) + && nCount >= 7 ) + { + oSRS.SetStatePlane( ITTVISToUSGSZone(atoi(papszFields[7])), FALSE ); + } + else if( EQUALN(papszFields[0],"State Plane (NAD 83)",19) + && nCount >= 7 ) + { + oSRS.SetStatePlane( ITTVISToUSGSZone(atoi(papszFields[7])), TRUE ); + } + else if( EQUALN(papszFields[0],"Geographic Lat",14) + && nCount >= 8 ) + { + if( nCount >= 8 && strstr(papszFields[7],"=") == NULL ) + SetENVIDatum( &oSRS, papszFields[7] ); + else + oSRS.SetWellKnownGeogCS( "WGS84" ); + } + else if( nPICount > 8 && atoi(papszPI[0]) == 3 ) // TM + { + oSRS.SetTM( CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), + CPLAtofM(papszPI[7]), + CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); + } + else if( nPICount > 8 && atoi(papszPI[0]) == 4 ) // Lambert Conformal Conic + { + oSRS.SetLCC( CPLAtofM(papszPI[7]), CPLAtofM(papszPI[8]), + CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), + CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); + } + else if( nPICount > 10 && atoi(papszPI[0]) == 5 ) // Oblique Merc (2 point) + { + oSRS.SetHOM2PNO( CPLAtofM(papszPI[3]), + CPLAtofM(papszPI[4]), CPLAtofM(papszPI[5]), + CPLAtofM(papszPI[6]), CPLAtofM(papszPI[7]), + CPLAtofM(papszPI[10]), + CPLAtofM(papszPI[8]), CPLAtofM(papszPI[9]) ); + } + else if( nPICount > 8 && atoi(papszPI[0]) == 6 ) // Oblique Merc + { + oSRS.SetHOM(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), + CPLAtofM(papszPI[5]), 0.0, + CPLAtofM(papszPI[8]), + CPLAtofM(papszPI[6]), CPLAtofM(papszPI[7]) ); + } + else if( nPICount > 8 && atoi(papszPI[0]) == 7 ) // Stereographic + { + oSRS.SetStereographic( CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), + CPLAtofM(papszPI[7]), + CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); + } + else if( nPICount > 8 && atoi(papszPI[0]) == 9 ) // Albers Equal Area + { + oSRS.SetACEA( CPLAtofM(papszPI[7]), CPLAtofM(papszPI[8]), + CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), + CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); + } + else if( nPICount > 6 && atoi(papszPI[0]) == 10 ) // Polyconic + { + oSRS.SetPolyconic(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), + CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); + } + else if( nPICount > 6 && atoi(papszPI[0]) == 11 ) // LAEA + { + oSRS.SetLAEA(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), + CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); + } + else if( nPICount > 6 && atoi(papszPI[0]) == 12 ) // Azimuthal Equid. + { + oSRS.SetAE(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), + CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); + } + else if( nPICount > 6 && atoi(papszPI[0]) == 31 ) // Polar Stereographic + { + oSRS.SetPS(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), + 1.0, + CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); + } + } + + CSLDestroy( papszCSS ); + + // Still lots more that could be added for someone with the patience. + +/* -------------------------------------------------------------------- */ +/* fallback to localcs if we don't recognise things. */ +/* -------------------------------------------------------------------- */ + if( oSRS.GetRoot() == NULL ) + oSRS.SetLocalCS( papszFields[0] ); + +/* -------------------------------------------------------------------- */ +/* Try to set datum from projection info line if we have a */ +/* projected coordinate system without a GEOGCS. */ +/* -------------------------------------------------------------------- */ + if( oSRS.IsProjected() && oSRS.GetAttrNode("GEOGCS") == NULL + && nPICount > 3 ) + { + // Do we have a datum on the projection info line? + int iDatum = nPICount-1; + + // Ignore units= items. + if( strstr(papszPI[iDatum],"=") != NULL ) + iDatum--; + + // Skip past the name. + iDatum--; + + CPLString osDatumName = papszPI[iDatum]; + if( osDatumName.find_first_of("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ") + != CPLString::npos ) + { + SetENVIDatum( &oSRS, osDatumName ); + } + else + { + SetENVIEllipse( &oSRS, papszPI + 1 ); + } + } + + +/* -------------------------------------------------------------------- */ +/* Try to process specialized units. */ +/* -------------------------------------------------------------------- */ + if( EQUALN( papszFields[nCount-1],"units",5)) + { + /* Handle linear units first. */ + if (EQUAL(papszFields[nCount-1],"units=Feet") ) + oSRS.SetLinearUnitsAndUpdateParameters( SRS_UL_FOOT, CPLAtof(SRS_UL_FOOT_CONV) ); + else if (EQUAL(papszFields[nCount-1],"units=Meters") ) + oSRS.SetLinearUnitsAndUpdateParameters( SRS_UL_METER, 1. ); + else if (EQUAL(papszFields[nCount-1],"units=Km") ) + oSRS.SetLinearUnitsAndUpdateParameters( "Kilometer", 1000. ); + else if (EQUAL(papszFields[nCount-1],"units=Yards") ) + oSRS.SetLinearUnitsAndUpdateParameters( "Yard", .9144 ); + else if (EQUAL(papszFields[nCount-1],"units=Miles") ) + oSRS.SetLinearUnitsAndUpdateParameters( "Mile", 1609.344 ); + else if (EQUAL(papszFields[nCount-1],"units=Nautical Miles") ) + oSRS.SetLinearUnitsAndUpdateParameters( SRS_UL_NAUTICAL_MILE, CPLAtof(SRS_UL_NAUTICAL_MILE_CONV) ); + + /* Only handle angular units if we know the projection is geographic. */ + if (oSRS.IsGeographic()) + { + if (EQUAL(papszFields[nCount-1],"units=Radians") ) + oSRS.SetAngularUnits( SRS_UA_RADIAN, 1. ); + else + { + /* Degrees, minutes and seconds will all be represented as degrees. */ + oSRS.SetAngularUnits( SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV)); + + double conversionFactor = 1.; + if (EQUAL(papszFields[nCount-1],"units=Minutes") ) + conversionFactor = 60.; + else if( EQUAL(papszFields[nCount-1],"units=Seconds") ) + conversionFactor = 3600.; + adfGeoTransform[0] /= conversionFactor; + adfGeoTransform[1] /= conversionFactor; + adfGeoTransform[2] /= conversionFactor; + adfGeoTransform[3] /= conversionFactor; + adfGeoTransform[4] /= conversionFactor; + adfGeoTransform[5] /= conversionFactor; + } + } + } +/* -------------------------------------------------------------------- */ +/* Turn back into WKT. */ +/* -------------------------------------------------------------------- */ + if( oSRS.GetRoot() != NULL ) + { + oSRS.Fixup(); + if ( pszProjection ) + { + CPLFree( pszProjection ); + pszProjection = NULL; + } + oSRS.exportToWkt( &pszProjection ); + } + + CSLDestroy( papszFields ); + CSLDestroy( papszPI ); + return TRUE; +} + +/************************************************************************/ +/* ProcessRPCinfo() */ +/* */ +/* Extract RPC transformation coefficients if they are present */ +/* and sets into the standard metadata fields for RPC. */ +/************************************************************************/ + +void ENVIDataset::ProcessRPCinfo( const char *pszRPCinfo, + int numCols, int numRows) +{ + char **papszFields; + char sVal[1280]; + int nCount; + + papszFields = SplitList( pszRPCinfo ); + nCount = CSLCount(papszFields); + + if( nCount < 90 ) + { + CSLDestroy( papszFields ); + return; + } + + CPLsnprintf(sVal, sizeof(sVal), "%.16g",CPLAtof(papszFields[0])); + SetMetadataItem("LINE_OFF",sVal,"RPC"); + CPLsnprintf(sVal, sizeof(sVal), "%.16g",CPLAtof(papszFields[5])); + SetMetadataItem("LINE_SCALE",sVal,"RPC"); + CPLsnprintf(sVal, sizeof(sVal), "%.16g",CPLAtof(papszFields[1])); + SetMetadataItem("SAMP_OFF",sVal,"RPC"); + CPLsnprintf(sVal, sizeof(sVal), "%.16g",CPLAtof(papszFields[6])); + SetMetadataItem("SAMP_SCALE",sVal,"RPC"); + CPLsnprintf(sVal, sizeof(sVal), "%.16g",CPLAtof(papszFields[2])); + SetMetadataItem("LAT_OFF",sVal,"RPC"); + CPLsnprintf(sVal, sizeof(sVal), "%.16g",CPLAtof(papszFields[7])); + SetMetadataItem("LAT_SCALE",sVal,"RPC"); + CPLsnprintf(sVal, sizeof(sVal), "%.16g",CPLAtof(papszFields[3])); + SetMetadataItem("LONG_OFF",sVal,"RPC"); + CPLsnprintf(sVal, sizeof(sVal), "%.16g",CPLAtof(papszFields[8])); + SetMetadataItem("LONG_SCALE",sVal,"RPC"); + CPLsnprintf(sVal, sizeof(sVal), "%.16g",CPLAtof(papszFields[4])); + SetMetadataItem("HEIGHT_OFF",sVal,"RPC"); + CPLsnprintf(sVal, sizeof(sVal), "%.16g",CPLAtof(papszFields[9])); + SetMetadataItem("HEIGHT_SCALE",sVal,"RPC"); + + sVal[0] = '\0'; + int i; + for(i = 0; i < 20; i++ ) + CPLsnprintf(sVal+strlen(sVal), sizeof(sVal), "%.16g ", + CPLAtof(papszFields[10+i])); + SetMetadataItem("LINE_NUM_COEFF",sVal,"RPC"); + + sVal[0] = '\0'; + for(i = 0; i < 20; i++ ) + CPLsnprintf(sVal+strlen(sVal), sizeof(sVal), "%.16g ", + CPLAtof(papszFields[30+i])); + SetMetadataItem("LINE_DEN_COEFF",sVal,"RPC"); + + sVal[0] = '\0'; + for(i = 0; i < 20; i++ ) + CPLsnprintf(sVal+strlen(sVal), sizeof(sVal), "%.16g ", + CPLAtof(papszFields[50+i])); + SetMetadataItem("SAMP_NUM_COEFF",sVal,"RPC"); + + sVal[0] = '\0'; + for(i = 0; i < 20; i++ ) + CPLsnprintf(sVal+strlen(sVal), sizeof(sVal), "%.16g ", + CPLAtof(papszFields[70+i])); + SetMetadataItem("SAMP_DEN_COEFF",sVal,"RPC"); + + CPLsnprintf(sVal, sizeof(sVal), "%.16g", + CPLAtof(papszFields[3]) - CPLAtof(papszFields[8])); + SetMetadataItem("MIN_LONG",sVal,"RPC"); + + CPLsnprintf(sVal, sizeof(sVal), "%.16g", + CPLAtof(papszFields[3]) + CPLAtof(papszFields[8]) ); + SetMetadataItem("MAX_LONG",sVal,"RPC"); + + CPLsnprintf(sVal, sizeof(sVal), "%.16g", + CPLAtof(papszFields[2]) - CPLAtof(papszFields[7])); + SetMetadataItem("MIN_LAT",sVal,"RPC"); + + CPLsnprintf(sVal, sizeof(sVal), "%.16g", + CPLAtof(papszFields[2]) + CPLAtof(papszFields[7])); + SetMetadataItem("MAX_LAT",sVal,"RPC"); + + if (nCount == 93) + { + SetMetadataItem("TILE_ROW_OFFSET",papszFields[90],"RPC"); + SetMetadataItem("TILE_COL_OFFSET",papszFields[91],"RPC"); + SetMetadataItem("ENVI_RPC_EMULATION",papszFields[92],"RPC"); + } + + /* Handle the chipping case where the image is a subset. */ + double rowOffset, colOffset; + rowOffset = (nCount == 93) ? CPLAtof(papszFields[90]) : 0; + colOffset = (nCount == 93) ? CPLAtof(papszFields[91]) : 0; + if (rowOffset || colOffset) + { + SetMetadataItem("ICHIP_SCALE_FACTOR", "1"); + SetMetadataItem("ICHIP_ANAMORPH_CORR", "0"); + SetMetadataItem("ICHIP_SCANBLK_NUM", "0"); + + SetMetadataItem("ICHIP_OP_ROW_11", "0.5"); + SetMetadataItem("ICHIP_OP_COL_11", "0.5"); + SetMetadataItem("ICHIP_OP_ROW_12", "0.5"); + SetMetadataItem("ICHIP_OP_COL_21", "0.5"); + CPLsnprintf(sVal, sizeof(sVal), "%.16g", numCols - 0.5); + SetMetadataItem("ICHIP_OP_COL_12", sVal); + SetMetadataItem("ICHIP_OP_COL_22", sVal); + CPLsnprintf(sVal, sizeof(sVal), "%.16g", numRows - 0.5); + SetMetadataItem("ICHIP_OP_ROW_21", sVal); + SetMetadataItem("ICHIP_OP_ROW_22", sVal); + + CPLsnprintf(sVal, sizeof(sVal), "%.16g", rowOffset + 0.5); + SetMetadataItem("ICHIP_FI_ROW_11", sVal); + SetMetadataItem("ICHIP_FI_ROW_12", sVal); + CPLsnprintf(sVal, sizeof(sVal), "%.16g", colOffset + 0.5); + SetMetadataItem("ICHIP_FI_COL_11", sVal); + SetMetadataItem("ICHIP_FI_COL_21", sVal); + CPLsnprintf(sVal, sizeof(sVal), "%.16g", colOffset + numCols - 0.5); + SetMetadataItem("ICHIP_FI_COL_12", sVal); + SetMetadataItem("ICHIP_FI_COL_22", sVal); + CPLsnprintf(sVal, sizeof(sVal), "%.16g", rowOffset + numRows - 0.5); + SetMetadataItem("ICHIP_FI_ROW_21", sVal); + SetMetadataItem("ICHIP_FI_ROW_22", sVal); + } + CSLDestroy(papszFields); +} + +void ENVIDataset::ProcessStatsFile() +{ + VSILFILE *fpStaFile; + + osStaFilename = CPLResetExtension( pszHDRFilename, "sta" ); + fpStaFile = VSIFOpenL( osStaFilename, "rb" ); + + if (!fpStaFile) + { + osStaFilename = ""; + return; + } + + int lTestHeader[10],lOffset; + + if( VSIFReadL( lTestHeader, sizeof(int), 10, fpStaFile ) != 10 ) + { + VSIFCloseL( fpStaFile ); + osStaFilename = ""; + return; + } + + int isFloat; + isFloat = (byteSwapInt(lTestHeader[0]) == 1111838282); + + int nb,i; + float * fStats; + double * dStats, dMin, dMax, dMean, dStd; + + nb=byteSwapInt(lTestHeader[3]); + + if (nb < 0 || nb > nBands) + { + CPLDebug("ENVI", ".sta file has statistics for %d bands, " + "whereas the dataset has only %d bands", nb, nBands); + nb = nBands; + } + + VSIFSeekL(fpStaFile,40+(nb+1)*4,SEEK_SET); + + if (VSIFReadL(&lOffset,sizeof(int),1,fpStaFile) == 1) + { + VSIFSeekL(fpStaFile,40+(nb+1)*8+byteSwapInt(lOffset)+nb,SEEK_SET); + // This should be the beginning of the statistics + if (isFloat) + { + fStats = (float*)CPLCalloc(nb*4,4); + if ((int)VSIFReadL(fStats,4,nb*4,fpStaFile) == nb*4) + { + for (i=0;iSetStatistics( + byteSwapFloat(fStats[i]), + byteSwapFloat(fStats[nb+i]), + byteSwapFloat(fStats[2*nb+i]), + byteSwapFloat(fStats[3*nb+i])); + } + } + CPLFree(fStats); + } + else + { + dStats = (double*)CPLCalloc(nb*4,8); + if ((int)VSIFReadL(dStats,8,nb*4,fpStaFile) == nb*4) + { + for (i=0;iSetStatistics(dMin,dMax,dMean,dStd); + } + } + CPLFree(dStats); + } + } + VSIFCloseL( fpStaFile ); +} + +int ENVIDataset::byteSwapInt(int swapMe) +{ + CPL_MSBPTR32(&swapMe); + return swapMe; +} + +float ENVIDataset::byteSwapFloat(float swapMe) +{ + CPL_MSBPTR32(&swapMe); + return swapMe; +} + +double ENVIDataset::byteSwapDouble(double swapMe) +{ + CPL_MSBPTR64(&swapMe); + return swapMe; +} + + +/************************************************************************/ +/* ReadHeader() */ +/************************************************************************/ + +int ENVIDataset::ReadHeader( VSILFILE * fpHdr ) + +{ + + CPLReadLineL( fpHdr ); + +/* -------------------------------------------------------------------- */ +/* Now start forming sets of name/value pairs. */ +/* -------------------------------------------------------------------- */ + while( TRUE ) + { + const char *pszNewLine; + char *pszWorkingLine; + + pszNewLine = CPLReadLineL( fpHdr ); + if( pszNewLine == NULL ) + break; + + if( strstr(pszNewLine,"=") == NULL ) + continue; + + pszWorkingLine = CPLStrdup(pszNewLine); + + // Collect additional lines if we have open sqiggly bracket. + if( strstr(pszWorkingLine,"{") != NULL + && strstr(pszWorkingLine,"}") == NULL ) + { + do { + pszNewLine = CPLReadLineL( fpHdr ); + if( pszNewLine ) + { + pszWorkingLine = (char *) + CPLRealloc(pszWorkingLine, + strlen(pszWorkingLine)+strlen(pszNewLine)+1); + strcat( pszWorkingLine, pszNewLine ); + } + } while( pszNewLine != NULL && strstr(pszNewLine,"}") == NULL ); + } + + // Try to break input into name and value portions. Trim whitespace. + const char *pszValue; + int iEqual; + + for( iEqual = 0; + pszWorkingLine[iEqual] != '\0' && pszWorkingLine[iEqual] != '='; + iEqual++ ) {} + + if( pszWorkingLine[iEqual] == '=' ) + { + int i; + + pszValue = pszWorkingLine + iEqual + 1; + while( *pszValue == ' ' || *pszValue == '\t' ) + pszValue++; + + pszWorkingLine[iEqual--] = '\0'; + while( iEqual > 0 + && (pszWorkingLine[iEqual] == ' ' + || pszWorkingLine[iEqual] == '\t') ) + pszWorkingLine[iEqual--] = '\0'; + + // Convert spaces in the name to underscores. + for( i = 0; pszWorkingLine[i] != '\0'; i++ ) + { + if( pszWorkingLine[i] == ' ' ) + pszWorkingLine[i] = '_'; + } + + papszHeader = CSLSetNameValue( papszHeader, + pszWorkingLine, pszValue ); + } + + CPLFree( pszWorkingLine ); + } + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *ENVIDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* We assume the user is pointing to the binary (ie. .bil) file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 2 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Do we have a .hdr file? Try upper and lower case, and */ +/* replacing the extension as well as appending the extension */ +/* to whatever we currently have. */ +/* -------------------------------------------------------------------- */ + const char *pszMode; + CPLString osHdrFilename; + VSILFILE *fpHeader = NULL; + + if( poOpenInfo->eAccess == GA_Update ) + pszMode = "r+"; + else + pszMode = "r"; + + char** papszSiblingFiles = poOpenInfo->GetSiblingFiles(); + if (papszSiblingFiles == NULL) + { + osHdrFilename = CPLResetExtension( poOpenInfo->pszFilename, "hdr" ); + fpHeader = VSIFOpenL( osHdrFilename, pszMode ); + + if( fpHeader == NULL && VSIIsCaseSensitiveFS(osHdrFilename) ) + { + osHdrFilename = CPLResetExtension( poOpenInfo->pszFilename, "HDR" ); + fpHeader = VSIFOpenL( osHdrFilename, pszMode ); + } + + if( fpHeader == NULL ) + { + osHdrFilename = CPLFormFilename( NULL, poOpenInfo->pszFilename, + "hdr" ); + fpHeader = VSIFOpenL( osHdrFilename, pszMode ); + } + + if( fpHeader == NULL && VSIIsCaseSensitiveFS(osHdrFilename) ) + { + osHdrFilename = CPLFormFilename( NULL, poOpenInfo->pszFilename, + "HDR" ); + fpHeader = VSIFOpenL( osHdrFilename, pszMode ); + } + + } + else + { + /* -------------------------------------------------------------------- */ + /* Now we need to tear apart the filename to form a .HDR */ + /* filename. */ + /* -------------------------------------------------------------------- */ + CPLString osPath = CPLGetPath( poOpenInfo->pszFilename ); + CPLString osName = CPLGetFilename( poOpenInfo->pszFilename ); + + int iFile = CSLFindString(papszSiblingFiles, + CPLResetExtension( osName, "hdr" ) ); + if( iFile >= 0 ) + { + osHdrFilename = CPLFormFilename( osPath, papszSiblingFiles[iFile], + NULL ); + fpHeader = VSIFOpenL( osHdrFilename, pszMode ); + } + else + { + iFile = CSLFindString(papszSiblingFiles, + CPLFormFilename( NULL, osName, "hdr" )); + if( iFile >= 0 ) + { + osHdrFilename = CPLFormFilename( osPath, papszSiblingFiles[iFile], + NULL ); + fpHeader = VSIFOpenL( osHdrFilename, pszMode ); + } + } + } + + if( fpHeader == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Check that the first line says "ENVI". */ +/* -------------------------------------------------------------------- */ + char szTestHdr[4]; + + if( VSIFReadL( szTestHdr, 4, 1, fpHeader ) != 1 ) + { + VSIFCloseL( fpHeader ); + return NULL; + } + if( strncmp(szTestHdr,"ENVI",4) != 0 ) + { + VSIFCloseL( fpHeader ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + ENVIDataset *poDS; + + poDS = new ENVIDataset(); + poDS->pszHDRFilename = CPLStrdup(osHdrFilename); + poDS->fp = fpHeader; + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + if( !poDS->ReadHeader( fpHeader ) ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Has the user selected the .hdr file to open? */ +/* -------------------------------------------------------------------- */ + if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "hdr") ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "The selected file is an ENVI header file, but to\n" + "open ENVI datasets, the data file should be selected\n" + "instead of the .hdr file. Please try again selecting\n" + "the data file corresponding to the header file:\n" + " %s\n", + poOpenInfo->pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Has the user selected the .sta (stats) file to open? */ +/* -------------------------------------------------------------------- */ + if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "sta") ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "The selected file is an ENVI statistics file.\n" + "To open ENVI datasets, the data file should be selected\n" + "instead of the .sta file. Please try again selecting\n" + "the data file corresponding to the statistics file:\n" + " %s\n", + poOpenInfo->pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Extract required values from the .hdr. */ +/* -------------------------------------------------------------------- */ + int nLines = 0, nSamples = 0, nBands = 0, nHeaderSize = 0; + const char *pszInterleave = NULL; + + if( CSLFetchNameValue(poDS->papszHeader,"lines") ) + nLines = atoi(CSLFetchNameValue(poDS->papszHeader,"lines")); + + if( CSLFetchNameValue(poDS->papszHeader,"samples") ) + nSamples = atoi(CSLFetchNameValue(poDS->papszHeader,"samples")); + + if( CSLFetchNameValue(poDS->papszHeader,"bands") ) + nBands = atoi(CSLFetchNameValue(poDS->papszHeader,"bands")); + + pszInterleave = CSLFetchNameValue(poDS->papszHeader,"interleave"); + + /* In case, there is no interleave keyword, we try to derive it from the */ + /* file extension. */ + if( pszInterleave == NULL ) + { + const char* pszExtension = CPLGetExtension(poOpenInfo->pszFilename); + pszInterleave = pszExtension; + } + + if ( !EQUALN(pszInterleave, "BSQ",3) && + !EQUALN(pszInterleave, "BIP",3) && + !EQUALN(pszInterleave, "BIL",3) ) + { + CPLDebug("ENVI", "Unset or unknown value for 'interleave' keyword --> assuming BSQ interleaving"); + pszInterleave = "bsq"; + } + + if (!GDALCheckDatasetDimensions(nSamples, nLines) || !GDALCheckBandCount(nBands, FALSE)) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "The file appears to have an associated ENVI header, but\n" + "one or more of the samples, lines and bands\n" + "keywords appears to be missing or invalid." ); + return NULL; + } + + if( CSLFetchNameValue(poDS->papszHeader,"header_offset") ) + nHeaderSize = atoi(CSLFetchNameValue(poDS->papszHeader,"header_offset")); + +/* -------------------------------------------------------------------- */ +/* Translate the datatype. */ +/* -------------------------------------------------------------------- */ + GDALDataType eType = GDT_Byte; + + if( CSLFetchNameValue(poDS->papszHeader,"data_type" ) != NULL ) + { + switch( atoi(CSLFetchNameValue(poDS->papszHeader,"data_type" )) ) + { + case 1: + eType = GDT_Byte; + break; + + case 2: + eType = GDT_Int16; + break; + + case 3: + eType = GDT_Int32; + break; + + case 4: + eType = GDT_Float32; + break; + + case 5: + eType = GDT_Float64; + break; + + case 6: + eType = GDT_CFloat32; + break; + + case 9: + eType = GDT_CFloat64; + break; + + case 12: + eType = GDT_UInt16; + break; + + case 13: + eType = GDT_UInt32; + break; + + /* 14=Int64, 15=UInt64 */ + + default: + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "The file does not have a value for the data_type\n" + "that is recognised by the GDAL ENVI driver."); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Translate the byte order. */ +/* -------------------------------------------------------------------- */ + int bNativeOrder = TRUE; + + if( CSLFetchNameValue(poDS->papszHeader,"byte_order" ) != NULL ) + { +#ifdef CPL_LSB + bNativeOrder = atoi(CSLFetchNameValue(poDS->papszHeader, + "byte_order" )) == 0; +#else + bNativeOrder = atoi(CSLFetchNameValue(poDS->papszHeader, + "byte_order" )) != 0; +#endif + } + +/* -------------------------------------------------------------------- */ +/* Warn about unsupported file types virtual mosaic and meta file.*/ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue(poDS->papszHeader,"file_type" ) != NULL ) + { + // when the file type is one of these we return an invalid file type err + //'envi meta file' + //'envi virtual mosaic' + //'envi spectral library' + //'envi fft result' + + // when the file type is one of these we open it + //'envi standard' + //'envi classification' + + // when the file type is anything else we attempt to open it as a raster. + + const char * pszEnviFileType; + pszEnviFileType = CSLFetchNameValue(poDS->papszHeader,"file_type"); + + // envi gdal does not support any of these + // all others we will attempt to open + if(EQUAL(pszEnviFileType, "envi meta file") || + EQUAL(pszEnviFileType, "envi virtual mosaic") || + EQUAL(pszEnviFileType, "envi spectral library")) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "File %s contains an invalid file type in the ENVI .hdr\n" + "GDAL does not support '%s' type files.", + poOpenInfo->pszFilename, pszEnviFileType ); + delete poDS; + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Detect (gzipped) compressed datasets. */ +/* -------------------------------------------------------------------- */ + int bIsCompressed = FALSE; + if( CSLFetchNameValue(poDS->papszHeader,"file_compression" ) != NULL ) + { + if( atoi(CSLFetchNameValue(poDS->papszHeader,"file_compression" )) + != 0 ) + { + bIsCompressed = TRUE; + } + } + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = nSamples; + poDS->nRasterYSize = nLines; + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Reopen file in update mode if necessary. */ +/* -------------------------------------------------------------------- */ + CPLString osImageFilename(poOpenInfo->pszFilename); + if (bIsCompressed) + osImageFilename = "/vsigzip/" + osImageFilename; + if( poOpenInfo->eAccess == GA_Update ) + { + if (bIsCompressed) + { + delete poDS; + CPLError( CE_Failure, CPLE_OpenFailed, + "Cannot open compressed file in update mode.\n"); + return NULL; + } + poDS->fpImage = VSIFOpenL( osImageFilename, "rb+" ); + } + else + poDS->fpImage = VSIFOpenL( osImageFilename, "rb" ); + + if( poDS->fpImage == NULL ) + { + delete poDS; + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to re-open %s within ENVI driver.\n", + poOpenInfo->pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Compute the line offset. */ +/* -------------------------------------------------------------------- */ + int nDataSize = GDALGetDataTypeSize(eType)/8; + int nPixelOffset, nLineOffset; + vsi_l_offset nBandOffset; + int bIntOverflow = FALSE; + + if( EQUALN(pszInterleave, "bil", 3) ) + { + poDS->interleave = BIL; + poDS->SetMetadataItem( "INTERLEAVE", "LINE", "IMAGE_STRUCTURE" ); + if (nSamples > INT_MAX / (nDataSize * nBands)) bIntOverflow = TRUE; + nLineOffset = nDataSize * nSamples * nBands; + nPixelOffset = nDataSize; + nBandOffset = (vsi_l_offset)nDataSize * nSamples; + } + else if( EQUALN(pszInterleave, "bip", 3) ) + { + poDS->interleave = BIP; + poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + if (nSamples > INT_MAX / (nDataSize * nBands)) bIntOverflow = TRUE; + nLineOffset = nDataSize * nSamples * nBands; + nPixelOffset = nDataSize * nBands; + nBandOffset = nDataSize; + } + else /* bsq */ + { + poDS->interleave = BSQ; + poDS->SetMetadataItem( "INTERLEAVE", "BAND", "IMAGE_STRUCTURE" ); + if (nSamples > INT_MAX / nDataSize) bIntOverflow = TRUE; + nLineOffset = nDataSize * nSamples; + nPixelOffset = nDataSize; + nBandOffset = (vsi_l_offset)nLineOffset * nLines; + } + + if (bIntOverflow) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "Int overflow occured."); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = nBands; + for( i = 0; i < poDS->nBands; i++ ) + { + poDS->SetBand( i + 1, + new ENVIRasterBand(poDS, i + 1, poDS->fpImage, + nHeaderSize + nBandOffset * i, + nPixelOffset, nLineOffset, eType, + bNativeOrder, TRUE) ); + } + +/* -------------------------------------------------------------------- */ +/* Apply band names if we have them. */ +/* Use wavelength for more descriptive information if possible */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue( poDS->papszHeader, "band_names" ) != NULL || + CSLFetchNameValue( poDS->papszHeader, "wavelength" ) != NULL) + { + char **papszBandNames = + poDS->SplitList( CSLFetchNameValue( poDS->papszHeader, + "band_names" ) ); + char **papszWL = + poDS->SplitList( CSLFetchNameValue( poDS->papszHeader, + "wavelength" ) ); + + const char *pszWLUnits = NULL; + int nWLCount = CSLCount(papszWL); + if (papszWL) + { + /* If WL information is present, process wavelength units */ + pszWLUnits = CSLFetchNameValue( poDS->papszHeader, + "wavelength_units" ); + if (pszWLUnits) + { + /* Don't show unknown or index units */ + if (EQUAL(pszWLUnits,"Unknown") || + EQUAL(pszWLUnits,"Index") ) + pszWLUnits=0; + } + if (pszWLUnits) + { + /* set wavelength units to dataset metadata */ + poDS->SetMetadataItem("wavelength_units", pszWLUnits); + } + } + + for( i = 0; i < nBands; i++ ) + { + CPLString osBandId, osBandName, osWavelength; + + /* First set up the wavelength names and units if available */ + if (papszWL && nWLCount > i) + { + osWavelength = papszWL[i]; + if (pszWLUnits) + { + osWavelength += " "; + osWavelength += pszWLUnits; + } + } + + /* Build the final name for this band */ + if (papszBandNames && CSLCount(papszBandNames) > i) + { + osBandName = papszBandNames[i]; + if (strlen(osWavelength) > 0) + { + osBandName += " ("; + osBandName += osWavelength; + osBandName += ")"; + } + } + else /* WL but no band names */ + osBandName = osWavelength; + + /* Description is for internal GDAL usage */ + poDS->GetRasterBand(i + 1)->SetDescription( osBandName ); + + /* Metadata field named Band_1, etc. needed for ArcGIS integration */ + osBandId = CPLSPrintf("Band_%i", i+1); + poDS->SetMetadataItem(osBandId, osBandName); + + /* Set wavelength metadata to band */ + if (papszWL && nWLCount > i) + { + poDS->GetRasterBand(i+1)->SetMetadataItem( + "wavelength", papszWL[i]); + + if (pszWLUnits) + { + poDS->GetRasterBand(i+1)->SetMetadataItem( + "wavelength_units", pszWLUnits); + } + } + + } + CSLDestroy( papszWL ); + CSLDestroy( papszBandNames ); + } +/* -------------------------------------------------------------------- */ +/* Apply class names if we have them. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue( poDS->papszHeader, "class_names" ) != NULL ) + { + char **papszClassNames = + poDS->SplitList( CSLFetchNameValue( poDS->papszHeader, + "class_names" ) ); + + poDS->GetRasterBand(1)->SetCategoryNames( papszClassNames ); + CSLDestroy( papszClassNames ); + } + +/* -------------------------------------------------------------------- */ +/* Apply colormap if we have one. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue( poDS->papszHeader, "class_lookup" ) != NULL ) + { + char **papszClassColors = + poDS->SplitList( CSLFetchNameValue( poDS->papszHeader, + "class_lookup" ) ); + int nColorValueCount = CSLCount(papszClassColors); + GDALColorTable oCT; + + for( i = 0; i*3+2 < nColorValueCount; i++ ) + { + GDALColorEntry sEntry; + + sEntry.c1 = (short) atoi(papszClassColors[i*3+0]); + sEntry.c2 = (short) atoi(papszClassColors[i*3+1]); + sEntry.c3 = (short) atoi(papszClassColors[i*3+2]); + sEntry.c4 = 255; + oCT.SetColorEntry( i, &sEntry ); + } + + CSLDestroy( papszClassColors ); + + poDS->GetRasterBand(1)->SetColorTable( &oCT ); + poDS->GetRasterBand(1)->SetColorInterpretation( GCI_PaletteIndex ); + } + +/* -------------------------------------------------------------------- */ +/* Set the nodata value if it is present */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue(poDS->papszHeader,"data_ignore_value" ) != NULL ) + { + for( i = 0; i < poDS->nBands; i++ ) + ((RawRasterBand*)poDS->GetRasterBand(i+1))->SetNoDataValue(CPLAtof( + CSLFetchNameValue(poDS->papszHeader,"data_ignore_value"))); + } + +/* -------------------------------------------------------------------- */ +/* Set all the header metadata into the ENVI domain */ +/* -------------------------------------------------------------------- */ + { + char** pTmp = poDS->papszHeader; + while (*pTmp != NULL) + { + char** pTokens = CSLTokenizeString2(*pTmp, "=", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES); + if (pTokens[0] != NULL && pTokens[1] != NULL && pTokens[2] == NULL) + { + poDS->SetMetadataItem(pTokens[0], pTokens[1], "ENVI"); + } + CSLDestroy(pTokens); + pTmp++; + } + } + +/* -------------------------------------------------------------------- */ +/* Read the stats file if it is present */ +/* -------------------------------------------------------------------- */ + poDS->ProcessStatsFile(); + +/* -------------------------------------------------------------------- */ +/* Look for mapinfo */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue( poDS->papszHeader, "map_info" ) != NULL ) + { + poDS->bFoundMapinfo = + poDS->ProcessMapinfo( + CSLFetchNameValue(poDS->papszHeader,"map_info") ); + } + +/* -------------------------------------------------------------------- */ +/* Look for RPC mapinfo */ +/* -------------------------------------------------------------------- */ + if( !poDS->bFoundMapinfo && + CSLFetchNameValue( poDS->papszHeader, "rpc_info" ) != NULL ) + { + poDS->ProcessRPCinfo( + CSLFetchNameValue(poDS->papszHeader,"rpc_info"), + poDS->nRasterXSize, poDS->nRasterYSize); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + // SetMetadata() calls in Open() makes the header dirty. + // Don't re-write the header if nothing external has changed the metadata + poDS->bHeaderDirty = FALSE; + + return( poDS ); +} + +int ENVIDataset::GetEnviType(GDALDataType eType) +{ + int iENVIType; + switch( eType ) + { + case GDT_Byte: + iENVIType = 1; + break; + case GDT_Int16: + iENVIType = 2; + break; + case GDT_Int32: + iENVIType = 3; + break; + case GDT_Float32: + iENVIType = 4; + break; + case GDT_Float64: + iENVIType = 5; + break; + case GDT_CFloat32: + iENVIType = 6; + break; + case GDT_CFloat64: + iENVIType = 9; + break; + case GDT_UInt16: + iENVIType = 12; + break; + case GDT_UInt32: + iENVIType = 13; + break; + + /* 14=Int64, 15=UInt64 */ + + default: + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create ENVI .hdr labelled dataset with an illegal\n" + "data type (%s).\n", + GDALGetDataTypeName(eType) ); + return 1; + } + return iENVIType; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *ENVIDataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify input options. */ +/* -------------------------------------------------------------------- */ + int iENVIType = GetEnviType(eType); + if (0 == iENVIType) + return 0; + +/* -------------------------------------------------------------------- */ +/* Try to create the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + + fp = VSIFOpenL( pszFilename, "wb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Just write out a couple of bytes to establish the binary */ +/* file, and then close it. */ +/* -------------------------------------------------------------------- */ + VSIFWriteL( (void *) "\0\0", 2, 1, fp ); + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Create the .hdr filename. */ +/* -------------------------------------------------------------------- */ + const char *pszHDRFilename; + const char *pszSuffix; + + pszSuffix = CSLFetchNameValue( papszOptions, "SUFFIX" ); + if ( pszSuffix && EQUALN( pszSuffix, "ADD", 3 )) + pszHDRFilename = CPLFormFilename( NULL, pszFilename, "hdr" ); + else + pszHDRFilename = CPLResetExtension(pszFilename, "hdr" ); + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszHDRFilename, "wt" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszHDRFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write out the header. */ +/* -------------------------------------------------------------------- */ + int iBigEndian; + const char *pszInterleaving; + +#ifdef CPL_LSB + iBigEndian = 0; +#else + iBigEndian = 1; +#endif + + VSIFPrintfL( fp, "ENVI\n" ); + VSIFPrintfL( fp, "samples = %d\nlines = %d\nbands = %d\n", + nXSize, nYSize, nBands ); + VSIFPrintfL( fp, "header offset = 0\nfile type = ENVI Standard\n" ); + VSIFPrintfL( fp, "data type = %d\n", iENVIType ); + pszInterleaving = CSLFetchNameValue( papszOptions, "INTERLEAVE" ); + if ( pszInterleaving ) + { + if ( EQUALN( pszInterleaving, "bip", 3 ) ) + pszInterleaving = "bip"; // interleaved by pixel + else if ( EQUALN( pszInterleaving, "bil", 3 ) ) + pszInterleaving = "bil"; // interleaved by line + else + pszInterleaving = "bsq"; // band sequental by default + } + else + pszInterleaving = "bsq"; + VSIFPrintfL( fp, "interleave = %s\n", pszInterleaving); + VSIFPrintfL( fp, "byte order = %d\n", iBigEndian ); + + VSIFCloseL( fp ); + + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* ENVIRasterBand() */ +/************************************************************************/ + +ENVIRasterBand::ENVIRasterBand( GDALDataset *poDS, int nBand, void * fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder, + int bIsVSIL, int bOwnsFP ) : + RawRasterBand(poDS, nBand, fpRaw, nImgOffset, nPixelOffset, + nLineOffset, eDataType, bNativeOrder, bIsVSIL, bOwnsFP) +{ +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +void ENVIRasterBand::SetDescription( const char * pszDescription ) +{ + ((ENVIDataset*)poDS)->bHeaderDirty = TRUE; + RawRasterBand::SetDescription(pszDescription); +} + +/************************************************************************/ +/* SetCategoryNames() */ +/************************************************************************/ + +CPLErr ENVIRasterBand::SetCategoryNames( char ** papszCategoryNames ) +{ + ((ENVIDataset*)poDS)->bHeaderDirty = TRUE; + return RawRasterBand::SetCategoryNames(papszCategoryNames); +} + +/************************************************************************/ +/* GDALRegister_ENVI() */ +/************************************************************************/ + +void GDALRegister_ENVI() +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ENVI" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ENVI" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ENVI .hdr Labelled" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#ENVI" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 " + "Float32 Float64 CFloat32 CFloat64" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" " +"" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->pfnOpen = ENVIDataset::Open; + poDriver->pfnCreate = ENVIDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/fastdataset.cpp b/bazaar/plugin/gdal/frmts/raw/fastdataset.cpp new file mode 100644 index 000000000..fbc362116 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/fastdataset.cpp @@ -0,0 +1,1146 @@ +/****************************************************************************** + * $Id: fastdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: EOSAT FAST Format reader + * Purpose: Reads Landsat FAST-L7A, IRS 1C/1D + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2002, Andrey Kiselev + * Copyright (c) 2007-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_string.h" +#include "cpl_conv.h" +#include "ogr_spatialref.h" +#include "rawdataset.h" + +CPL_CVSID("$Id: fastdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +CPL_C_START +void GDALRegister_FAST(void); +CPL_C_END + +#define ADM_STD_HEADER_SIZE 4608 // XXX: Format specification says it +#define ADM_HEADER_SIZE 5000 // should be 4608, but some vendors + // ship broken large datasets. +#define ADM_MIN_HEADER_SIZE 1536 // ...and sometimes it can be + // even 1/3 of standard size + +#define ACQUISITION_DATE "ACQUISITION DATE" +#define ACQUISITION_DATE_SIZE 8 + +#define SATELLITE_NAME "SATELLITE" +#define SATELLITE_NAME_SIZE 10 + +#define SENSOR_NAME "SENSOR" +#define SENSOR_NAME_SIZE 10 + +#define BANDS_PRESENT "BANDS PRESENT" +#define BANDS_PRESENT_SIZE 32 + +#define FILENAME "FILENAME" +#define FILENAME_SIZE 29 + +#define PIXELS "PIXELS PER LINE" +#define PIXELS_SIZE 5 + +#define LINES1 "LINES PER BAND" +#define LINES2 "LINES PER IMAGE" +#define LINES_SIZE 5 + +#define BITS_PER_PIXEL "OUTPUT BITS PER PIXEL" +#define BITS_PER_PIXEL_SIZE 2 + +#define PROJECTION_NAME "MAP PROJECTION" +#define PROJECTION_NAME_SIZE 4 + +#define ELLIPSOID_NAME "ELLIPSOID" +#define ELLIPSOID_NAME_SIZE 18 + +#define DATUM_NAME "DATUM" +#define DATUM_NAME_SIZE 6 + +#define ZONE_NUMBER "USGS MAP ZONE" +#define ZONE_NUMBER_SIZE 6 + +#define USGS_PARAMETERS "USGS PROJECTION PARAMETERS" + +#define CORNER_UPPER_LEFT "UL " +#define CORNER_UPPER_RIGHT "UR " +#define CORNER_LOWER_LEFT "LL " +#define CORNER_LOWER_RIGHT "LR " +#define CORNER_VALUE_SIZE 13 + +#define VALUE_SIZE 24 + +enum FASTSatellite // Satellites: +{ + LANDSAT, // Landsat 7 + IRS // IRS 1C/1D +}; + +/************************************************************************/ +/* ==================================================================== */ +/* FASTDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class FASTDataset : public GDALPamDataset +{ + friend class FASTRasterBand; + + double adfGeoTransform[6]; + char *pszProjection; + + VSILFILE *fpHeader; + CPLString apoChannelFilenames[7]; + VSILFILE *fpChannels[7]; + const char *pszFilename; + char *pszDirname; + GDALDataType eDataType; + FASTSatellite iSatellite; + + int OpenChannel( const char *pszFilename, int iBand ); + + public: + FASTDataset(); + ~FASTDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + + CPLErr GetGeoTransform( double * ); + const char *GetProjectionRef(); + VSILFILE *FOpenChannel( const char *, int iBand, int iFASTBand ); + void TryEuromap_IRS_1C_1D_ChannelNameConvention(); + + virtual char** GetFileList(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* FASTRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class FASTRasterBand : public RawRasterBand +{ + friend class FASTDataset; + + public: + + FASTRasterBand( FASTDataset *, int, VSILFILE *, vsi_l_offset, + int, int, GDALDataType, int ); +}; + + +/************************************************************************/ +/* FASTRasterBand() */ +/************************************************************************/ + +FASTRasterBand::FASTRasterBand( FASTDataset *poDS, int nBand, VSILFILE * fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, GDALDataType eDataType, + int bNativeOrder) : + RawRasterBand( poDS, nBand, fpRaw, nImgOffset, nPixelOffset, + nLineOffset, eDataType, bNativeOrder, TRUE) +{ + +} + +/************************************************************************/ +/* ==================================================================== */ +/* FASTDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* FASTDataset() */ +/************************************************************************/ + +FASTDataset::FASTDataset() + +{ + fpHeader = NULL; + pszDirname = NULL; + pszProjection = CPLStrdup( "" ); + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + nBands = 0; +} + +/************************************************************************/ +/* ~FASTDataset() */ +/************************************************************************/ + +FASTDataset::~FASTDataset() + +{ + int i; + + FlushCache(); + + if ( pszDirname ) + CPLFree( pszDirname ); + if ( pszProjection ) + CPLFree( pszProjection ); + for ( i = 0; i < nBands; i++ ) + if ( fpChannels[i] ) + VSIFCloseL( fpChannels[i] ); + if( fpHeader != NULL ) + VSIFCloseL( fpHeader ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr FASTDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *FASTDataset::GetProjectionRef() + +{ + if( pszProjection ) + return pszProjection; + else + return ""; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char** FASTDataset::GetFileList() +{ + char** papszFileList = GDALPamDataset::GetFileList(); + int i; + for(i=0;i<6;i++) + { + if (apoChannelFilenames[i].size() > 0) + papszFileList = + CSLAddString(papszFileList, apoChannelFilenames[i].c_str()); + } + + return papszFileList; +} + +/************************************************************************/ +/* OpenChannel() */ +/************************************************************************/ + +int FASTDataset::OpenChannel( const char *pszFilename, int iBand ) +{ + fpChannels[iBand] = VSIFOpenL( pszFilename, "rb" ); + if (fpChannels[iBand]) + apoChannelFilenames[iBand] = pszFilename; + return fpChannels[iBand] != NULL; +} + +/************************************************************************/ +/* FOpenChannel() */ +/************************************************************************/ + + +VSILFILE *FASTDataset::FOpenChannel( const char *pszBandname, int iBand, int iFASTBand ) +{ + const char *pszChannelFilename = NULL; + char *pszPrefix = CPLStrdup( CPLGetBasename( pszFilename ) ); + char *pszSuffix = CPLStrdup( CPLGetExtension( pszFilename ) ); + + fpChannels[iBand] = NULL; + + switch ( iSatellite ) + { + case LANDSAT: + if ( pszBandname && !EQUAL( pszBandname, "" ) ) + { + pszChannelFilename = + CPLFormCIFilename( pszDirname, pszBandname, NULL ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = + CPLFormFilename( pszDirname, + CPLSPrintf( "%s.b%02d", pszPrefix, iFASTBand ), + NULL ); + OpenChannel( pszChannelFilename, iBand ); + } + break; + case IRS: + default: + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "%s.%d", pszPrefix, iFASTBand ), pszSuffix ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "IMAGERY%d", iFASTBand ), pszSuffix ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "imagery%d", iFASTBand ), pszSuffix ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "IMAGERY%d.DAT", iFASTBand ), NULL ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "imagery%d.dat", iFASTBand ), NULL ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "IMAGERY%d.dat", iFASTBand ), NULL ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "imagery%d.DAT", iFASTBand ), NULL ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "BAND%d", iFASTBand ), pszSuffix ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "band%d", iFASTBand ), pszSuffix ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "BAND%d.DAT", iFASTBand ), NULL ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "band%d.dat", iFASTBand ), NULL ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "BAND%d.dat", iFASTBand ), NULL ); + if ( OpenChannel( pszChannelFilename, iBand ) ) + break; + pszChannelFilename = CPLFormFilename( pszDirname, + CPLSPrintf( "band%d.DAT", iFASTBand ), NULL ); + OpenChannel( pszChannelFilename, iBand ); + break; + } + + CPLDebug( "FAST", "Band %d filename=%s", iBand + 1, pszChannelFilename); + + CPLFree( pszPrefix ); + CPLFree( pszSuffix ); + return fpChannels[iBand]; +} + +/************************************************************************/ +/* TryEuromap_IRS_1C_1D_ChannelNameConvention() */ +/************************************************************************/ + +void FASTDataset::TryEuromap_IRS_1C_1D_ChannelNameConvention() +{ + /* Filename convention explained in http://www.euromap.de/download/em_names.pdf */ + + char chLastLetterHeader = pszFilename[strlen(pszFilename)-1]; + if (EQUAL(GetMetadataItem("SENSOR"), "PAN")) + { + /* Converting upper-case to lower case */ + if (chLastLetterHeader >= 'A' && chLastLetterHeader <= 'M') + chLastLetterHeader += 'a' - 'A'; + + if (chLastLetterHeader >= 'a' && chLastLetterHeader <= 'j') + { + char chLastLetterData = chLastLetterHeader - 'a' + '0'; + char* pszChannelFilename = CPLStrdup(pszFilename); + pszChannelFilename[strlen(pszChannelFilename)-1] = chLastLetterData; + if (OpenChannel( pszChannelFilename, 0 )) + nBands++; + else + CPLDebug("FAST", "Could not find %s", pszChannelFilename); + CPLFree(pszChannelFilename); + } + else if (chLastLetterHeader >= 'k' && chLastLetterHeader <= 'm') + { + char chLastLetterData = chLastLetterHeader - 'k' + 'n'; + char* pszChannelFilename = CPLStrdup(pszFilename); + pszChannelFilename[strlen(pszChannelFilename)-1] = chLastLetterData; + if (OpenChannel( pszChannelFilename, 0 )) + nBands++; + else + { + /* Trying upper-case */ + pszChannelFilename[strlen(pszChannelFilename)-1] = chLastLetterData - 'a' + 'A'; + if (OpenChannel( pszChannelFilename, 0 )) + nBands++; + else + CPLDebug("FAST", "Could not find %s", pszChannelFilename); + } + CPLFree(pszChannelFilename); + } + else + { + CPLDebug("FAST", "Unknown last letter (%c) for a IRS PAN Euromap FAST dataset", chLastLetterHeader); + } + } + else if (EQUAL(GetMetadataItem("SENSOR"), "LISS3")) + { + const char apchLISSFilenames[7][5] = { + { '0', '2', '3', '4', '5' }, + { '6', '7', '8', '9', 'a' }, + { 'b', 'c', 'd', 'e', 'f' }, + { 'g', 'h', 'i', 'j', 'k' }, + { 'l', 'm', 'n', 'o', 'p' }, + { 'q', 'r', 's', 't', 'u' }, + { 'v', 'w', 'x', 'y', 'z' } }; + int i; + for (i = 0; i < 7 ; i++) + { + if (chLastLetterHeader == apchLISSFilenames[i][0] || + (apchLISSFilenames[i][0] >= 'a' && apchLISSFilenames[i][0] <= 'z' && + (apchLISSFilenames[i][0] - chLastLetterHeader == 0 || + apchLISSFilenames[i][0] - chLastLetterHeader == 32))) + { + for (int j = 0; j < 4; j ++) + { + char* pszChannelFilename = CPLStrdup(pszFilename); + pszChannelFilename[strlen(pszChannelFilename)-1] = apchLISSFilenames[i][j+1]; + if (OpenChannel( pszChannelFilename, nBands )) + nBands++; + else if (apchLISSFilenames[i][j+1] >= 'a' && apchLISSFilenames[i][j+1] <= 'z') + { + /* Trying upper-case */ + pszChannelFilename[strlen(pszChannelFilename)-1] = apchLISSFilenames[i][j+1] - 'a' + 'A'; + if (OpenChannel( pszChannelFilename, nBands )) + nBands++; + else + { + CPLDebug("FAST", "Could not find %s", pszChannelFilename); + } + } + else + { + CPLDebug("FAST", "Could not find %s", pszChannelFilename); + } + CPLFree(pszChannelFilename); + } + break; + } + } + if (i == 7) + { + CPLDebug("FAST", "Unknown last letter (%c) for a IRS LISS3 Euromap FAST dataset", chLastLetterHeader); + } + } + else if (EQUAL(GetMetadataItem("SENSOR"), "WIFS")) + { + if (chLastLetterHeader == '0') + { + for (int j = 0; j < 2; j ++) + { + char* pszChannelFilename = CPLStrdup(pszFilename); + pszChannelFilename[strlen(pszChannelFilename)-1] + = (char) ('1' + j); + if (OpenChannel( pszChannelFilename, nBands )) + nBands++; + else + { + CPLDebug("FAST", "Could not find %s", pszChannelFilename); + } + CPLFree(pszChannelFilename); + } + } + else + { + CPLDebug("FAST", "Unknown last letter (%c) for a IRS WIFS Euromap FAST dataset", chLastLetterHeader); + } + } + else + { + CPLAssert(0); + } +} + +/************************************************************************/ +/* GetValue() */ +/************************************************************************/ + +static char *GetValue( const char *pszString, const char *pszName, + int iValueSize, int bNormalize ) +{ + char *pszTemp = strstr( (char *) pszString, pszName ); + + if ( pszTemp ) + { + // Skip the parameter name + pszTemp += strlen( pszName ); + // Skip whitespaces and equal signs + while ( *pszTemp == ' ' ) + pszTemp++; + while ( *pszTemp == '=' ) + pszTemp++; + + pszTemp = CPLScanString( pszTemp, iValueSize, TRUE, bNormalize ); + } + + return pszTemp; +} + +/************************************************************************/ +/* USGSMnemonicToCode() */ +/************************************************************************/ + +static long USGSMnemonicToCode( const char* pszMnemonic ) +{ + if ( EQUAL(pszMnemonic, "UTM") ) + return 1L; + else if ( EQUAL(pszMnemonic, "LCC") ) + return 4L; + else if ( EQUAL(pszMnemonic, "PS") ) + return 6L; + else if ( EQUAL(pszMnemonic, "PC") ) + return 7L; + else if ( EQUAL(pszMnemonic, "TM") ) + return 9L; + else if ( EQUAL(pszMnemonic, "OM") ) + return 20L; + else if ( EQUAL(pszMnemonic, "SOM") ) + return 22L; + else + return 1L; // UTM by default +} + +/************************************************************************/ +/* USGSEllipsoidToCode() */ +/************************************************************************/ + +static long USGSEllipsoidToCode( const char* pszMnemonic ) +{ + if ( EQUAL(pszMnemonic, "CLARKE_1866") ) + return 0L; + else if ( EQUAL(pszMnemonic, "CLARKE_1880") ) + return 1L; + else if ( EQUAL(pszMnemonic, "BESSEL") ) + return 2L; + else if ( EQUAL(pszMnemonic, "INTERNATL_1967") ) + return 3L; + else if ( EQUAL(pszMnemonic, "INTERNATL_1909") ) + return 4L; + else if ( EQUAL(pszMnemonic, "WGS72") || EQUAL(pszMnemonic, "WGS_72") ) + return 5L; + else if ( EQUAL(pszMnemonic, "EVEREST") ) + return 6L; + else if ( EQUAL(pszMnemonic, "WGS66") || EQUAL(pszMnemonic, "WGS_66") ) + return 7L; + else if ( EQUAL(pszMnemonic, "GRS_80") ) + return 8L; + else if ( EQUAL(pszMnemonic, "AIRY") ) + return 9L; + else if ( EQUAL(pszMnemonic, "MODIFIED_EVEREST") ) + return 10L; + else if ( EQUAL(pszMnemonic, "MODIFIED_AIRY") ) + return 11L; + else if ( EQUAL(pszMnemonic, "WGS84") || EQUAL(pszMnemonic, "WGS_84") ) + return 12L; + else if ( EQUAL(pszMnemonic, "SOUTHEAST_ASIA") ) + return 13L; + else if ( EQUAL(pszMnemonic, "AUSTRALIAN_NATL") ) + return 14L; + else if ( EQUAL(pszMnemonic, "KRASSOVSKY") ) + return 15L; + else if ( EQUAL(pszMnemonic, "HOUGH") ) + return 16L; + else if ( EQUAL(pszMnemonic, "MERCURY_1960") ) + return 17L; + else if ( EQUAL(pszMnemonic, "MOD_MERC_1968") ) + return 18L; + else if ( EQUAL(pszMnemonic, "6370997_M_SPHERE") ) + return 19L; + else + return 0L; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *FASTDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i; + + if( poOpenInfo->nHeaderBytes < 1024) + return NULL; + + if( !EQUALN((const char *) poOpenInfo->pabyHeader + 52, + "ACQUISITION DATE =", 18) + && !EQUALN((const char *) poOpenInfo->pabyHeader + 36, + "ACQUISITION DATE =", 18) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + FASTDataset *poDS; + + poDS = new FASTDataset(); + + poDS->fpHeader = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + if (poDS->fpHeader == NULL) + { + delete poDS; + return NULL; + } + + poDS->pszFilename = poOpenInfo->pszFilename; + poDS->pszDirname = CPLStrdup( CPLGetDirname( poOpenInfo->pszFilename ) ); + +/* -------------------------------------------------------------------- */ +/* Read the administrative record. */ +/* -------------------------------------------------------------------- */ + char *pszTemp; + char *pszHeader = (char *) CPLMalloc( ADM_HEADER_SIZE + 1 ); + size_t nBytesRead; + + VSIFSeekL( poDS->fpHeader, 0, SEEK_SET ); + nBytesRead = VSIFReadL( pszHeader, 1, ADM_HEADER_SIZE, poDS->fpHeader ); + if ( nBytesRead < ADM_MIN_HEADER_SIZE ) + { + CPLDebug( "FAST", "Header file too short. Reading failed" ); + CPLFree(pszHeader); + delete poDS; + return NULL; + } + pszHeader[nBytesRead] = '\0'; + + // Read acquisition date + pszTemp = GetValue( pszHeader, ACQUISITION_DATE, + ACQUISITION_DATE_SIZE, TRUE ); + if (pszTemp == NULL) + { + CPLDebug( "FAST", "Cannot get ACQUISITION_DATE, using empty value." ); + pszTemp = CPLStrdup(""); + } + poDS->SetMetadataItem( "ACQUISITION_DATE", pszTemp ); + CPLFree( pszTemp ); + + // Read satellite name (will read the first one only) + pszTemp = GetValue( pszHeader, SATELLITE_NAME, SATELLITE_NAME_SIZE, TRUE ); + if (pszTemp == NULL) + { + CPLDebug( "FAST", "Cannot get SATELLITE_NAME, using empty value." ); + pszTemp = CPLStrdup( "" ); + } + poDS->SetMetadataItem( "SATELLITE", pszTemp ); + if ( EQUALN(pszTemp, "LANDSAT", 7) ) + poDS->iSatellite = LANDSAT; + else if ( EQUALN(pszTemp, "IRS", 3) ) + poDS->iSatellite = IRS; + else + poDS->iSatellite = IRS; + CPLFree( pszTemp ); + + // Read sensor name (will read the first one only) + pszTemp = GetValue( pszHeader, SENSOR_NAME, SENSOR_NAME_SIZE, TRUE ); + if (pszTemp == NULL) + { + CPLDebug( "FAST", "Cannot get SENSOR_NAME, using empty value." ); + pszTemp = CPLStrdup(""); + } + poDS->SetMetadataItem( "SENSOR", pszTemp ); + CPLFree( pszTemp ); + + // Read filenames + poDS->nBands = 0; + + if (strstr( pszHeader, FILENAME ) == NULL) + { + if (strstr(pszHeader, "GENERATING AGENCY =EUROMAP")) + { + /* If we don't find the FILENAME field, let's try with the Euromap */ + /* PAN / LISS3 / WIFS IRS filename convention */ + if ((EQUAL(poDS->GetMetadataItem("SATELLITE"), "IRS 1C") || + EQUAL(poDS->GetMetadataItem("SATELLITE"), "IRS 1D")) && + (EQUAL(poDS->GetMetadataItem("SENSOR"), "PAN") || + EQUAL(poDS->GetMetadataItem("SENSOR"), "LISS3") || + EQUAL(poDS->GetMetadataItem("SENSOR"), "WIFS"))) + { + poDS->TryEuromap_IRS_1C_1D_ChannelNameConvention(); + } + else if (EQUAL(poDS->GetMetadataItem("SATELLITE"), "CARTOSAT-1") && + (EQUAL(poDS->GetMetadataItem("SENSOR"), "FORE") || + EQUAL(poDS->GetMetadataItem("SENSOR"), "AFT"))) + { + /* See http://www.euromap.de/download/p5fast_20050301.pdf, appendix F */ + CPLString osSuffix = CPLGetExtension( poDS->pszFilename ); + const char *papszBasenames[] = { "BANDF", "bandf", "BANDA", "banda" }; + for ( i=0;i<4;i++) + { + CPLString osChannelFilename = CPLFormFilename( poDS->pszDirname, papszBasenames[i], osSuffix ); + if (poDS->OpenChannel( osChannelFilename, 0 )) + { + poDS->nBands = 1; + break; + } + } + } + else if (EQUAL(poDS->GetMetadataItem("SATELLITE"), "IRS P6")) + { + /* If BANDS_PRESENT="2345", the file bands are "BAND2.DAT", "BAND3.DAT", etc. */ + pszTemp = GetValue( pszHeader, BANDS_PRESENT, BANDS_PRESENT_SIZE, TRUE ); + if (pszTemp) + { + for( i=0; pszTemp[i] != '\0'; i++) + { + if (pszTemp[i] >= '2' && pszTemp[i] <= '5') + { + if (poDS->FOpenChannel(poDS->pszFilename, poDS->nBands, pszTemp[i] - '0')) + poDS->nBands++; + } + } + CPLFree( pszTemp ); + } + } + } + } + + /* If the previous lookup for band files didn't success, fallback to the */ + /* standard way of finding them, either by the FILENAME field, either with */ + /* the usual patterns like bandX.dat, etc... */ + if ( !poDS->nBands ) + { + pszTemp = pszHeader; + for ( i = 0; i < 7; i++ ) + { + char *pszFilename = NULL ; + if ( pszTemp ) + pszTemp = strstr( pszTemp, FILENAME ); + if ( pszTemp ) + { + // Skip the parameter name + pszTemp += strlen(FILENAME); + // Skip whitespaces and equal signs + while ( *pszTemp == ' ' ) + pszTemp++; + while ( *pszTemp == '=' ) + pszTemp++; + pszFilename = CPLScanString( pszTemp, FILENAME_SIZE, TRUE, FALSE ); + } + else + pszTemp = NULL; + if ( poDS->FOpenChannel( pszFilename, poDS->nBands, poDS->nBands + 1 ) ) + poDS->nBands++; + if ( pszFilename ) + CPLFree( pszFilename ); + } + } + + if ( !poDS->nBands ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Failed to find and open band data files." ); + CPLFree(pszHeader); + delete poDS; + return NULL; + } + + // Read number of pixels/lines and bit depth + pszTemp = GetValue( pszHeader, PIXELS, PIXELS_SIZE, FALSE ); + if ( pszTemp ) + { + poDS->nRasterXSize = atoi( pszTemp ); + CPLFree( pszTemp ); + } + else + { + CPLDebug( "FAST", "Failed to find number of pixels in line." ); + CPLFree(pszHeader); + delete poDS; + return NULL; + } + + pszTemp = GetValue( pszHeader, LINES1, LINES_SIZE, FALSE ); + if ( !pszTemp ) + pszTemp = GetValue( pszHeader, LINES2, LINES_SIZE, FALSE ); + if ( pszTemp ) + { + poDS->nRasterYSize = atoi( pszTemp ); + CPLFree( pszTemp ); + } + else + { + CPLDebug( "FAST", "Failed to find number of lines in raster." ); + CPLFree(pszHeader); + delete poDS; + return NULL; + } + + + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + CPLFree(pszHeader); + delete poDS; + return NULL; + } + + pszTemp = GetValue( pszHeader, BITS_PER_PIXEL, BITS_PER_PIXEL_SIZE, FALSE ); + if ( pszTemp ) + { + switch( atoi(pszTemp) ) + { + case 8: + default: + poDS->eDataType = GDT_Byte; + break; + case 10: /* For a strange reason, some Euromap products declare 10 bits output, but are 16 bits */ + case 16: + poDS->eDataType = GDT_UInt16; + break; + } + CPLFree( pszTemp ); + } + else + poDS->eDataType = GDT_Byte; + +/* -------------------------------------------------------------------- */ +/* Read radiometric record. */ +/* -------------------------------------------------------------------- */ + const char *pszFirst, *pszSecond; + + // Read gains and biases. This is a trick! + pszTemp = strstr( pszHeader, "BIASES" );// It may be "BIASES AND GAINS" + // or "GAINS AND BIASES" + if ( pszTemp > strstr( pszHeader, "GAINS" ) ) + { + pszFirst = "GAIN%d"; + pszSecond = "BIAS%d"; + } + else + { + pszFirst = "BIAS%d"; + pszSecond = "GAIN%d"; + } + + // Now search for the first number occurance after that string + for ( i = 1; i <= poDS->nBands; i++ ) + { + char *pszValue = NULL; + size_t nValueLen = VALUE_SIZE; + + pszTemp = strpbrk( pszTemp, "-.0123456789" ); + if ( pszTemp ) + { + nValueLen = strspn( pszTemp, "+-.0123456789" ); + pszValue = CPLScanString( pszTemp, nValueLen, TRUE, TRUE ); + poDS->SetMetadataItem( CPLSPrintf(pszFirst, i ), pszValue ); + CPLFree( pszValue ); + } + pszTemp += nValueLen; + pszTemp = strpbrk( pszTemp, "-.0123456789" ); + if ( pszTemp ) + { + nValueLen = strspn( pszTemp, "+-.0123456789" ); + pszValue = CPLScanString( pszTemp, nValueLen, TRUE, TRUE ); + poDS->SetMetadataItem( CPLSPrintf(pszSecond, i ), pszValue ); + CPLFree( pszValue ); + } + pszTemp += nValueLen; + } + +/* -------------------------------------------------------------------- */ +/* Read geometric record. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + long iProjSys, iZone, iDatum; + // Coordinates of pixel's centers + double dfULX = 0.0, dfULY = 0.0; + double dfURX = 0.0, dfURY = 0.0; + double dfLLX = 0.0, dfLLY = 0.0; + double dfLRX = 0.0, dfLRY = 0.0; + double adfProjParms[15]; + + // Read projection name + pszTemp = GetValue( pszHeader, PROJECTION_NAME, + PROJECTION_NAME_SIZE, FALSE ); + if ( pszTemp && !EQUAL( pszTemp, "" ) ) + iProjSys = USGSMnemonicToCode( pszTemp ); + else + iProjSys = 1L; // UTM by default + CPLFree( pszTemp ); + + // Read ellipsoid name + pszTemp = GetValue( pszHeader, ELLIPSOID_NAME, ELLIPSOID_NAME_SIZE, FALSE ); + if ( pszTemp && !EQUAL( pszTemp, "" ) ) + iDatum = USGSEllipsoidToCode( pszTemp ); + else + iDatum = 0L; // Clarke, 1866 (NAD1927) by default + CPLFree( pszTemp ); + + // Read zone number + pszTemp = GetValue( pszHeader, ZONE_NUMBER, ZONE_NUMBER_SIZE, FALSE ); + if ( pszTemp && !EQUAL( pszTemp, "" ) ) + iZone = atoi( pszTemp ); + else + iZone = 0L; + CPLFree( pszTemp ); + + // Read 15 USGS projection parameters + for ( i = 0; i < 15; i++ ) + adfProjParms[i] = 0.0; + pszTemp = strstr( pszHeader, USGS_PARAMETERS ); + if ( pszTemp && !EQUAL( pszTemp, "" ) ) + { + pszTemp += strlen( USGS_PARAMETERS ); + for ( i = 0; i < 15; i++ ) + { + pszTemp = strpbrk( pszTemp, "-.0123456789" ); + if ( pszTemp ) + adfProjParms[i] = CPLScanDouble( pszTemp, VALUE_SIZE ); + pszTemp = strpbrk( pszTemp, " \t" ); + } + } + + // Coordinates should follow the word "PROJECTION", otherwise we can + // be confused by other occurences of the corner keywords. + char *pszGeomRecord = strstr( pszHeader, "PROJECTION" ); + // Read corner coordinates + pszTemp = strstr( pszGeomRecord, CORNER_UPPER_LEFT ); + if ( pszTemp && !EQUAL( pszTemp, "" ) ) + { + pszTemp += strlen( CORNER_UPPER_LEFT ) + 28; + dfULX = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); + pszTemp += CORNER_VALUE_SIZE + 1; + dfULY = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); + } + + pszTemp = strstr( pszGeomRecord, CORNER_UPPER_RIGHT ); + if ( pszTemp && !EQUAL( pszTemp, "" ) ) + { + pszTemp += strlen( CORNER_UPPER_RIGHT ) + 28; + dfURX = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); + pszTemp += CORNER_VALUE_SIZE + 1; + dfURY = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); + } + + pszTemp = strstr( pszGeomRecord, CORNER_LOWER_LEFT ); + if ( pszTemp && !EQUAL( pszTemp, "" ) ) + { + pszTemp += strlen( CORNER_LOWER_LEFT ) + 28; + dfLLX = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); + pszTemp += CORNER_VALUE_SIZE + 1; + dfLLY = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); + } + + pszTemp = strstr( pszGeomRecord, CORNER_LOWER_RIGHT ); + if ( pszTemp && !EQUAL( pszTemp, "" ) ) + { + pszTemp += strlen( CORNER_LOWER_RIGHT ) + 28; + dfLRX = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); + pszTemp += CORNER_VALUE_SIZE + 1; + dfLRY = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); + } + + if ( dfULX != 0.0 && dfULY != 0.0 + && dfURX != 0.0 && dfURY != 0.0 + && dfLLX != 0.0 && dfLLY != 0.0 + && dfLRX != 0.0 && dfLRY != 0.0 ) + { + int transform_ok=FALSE; + GDAL_GCP *pasGCPList; + int bAnglesInPackedDMSFormat; + + // Strip out zone number from the easting values, if either + if ( dfULX >= 1000000.0 ) + dfULX -= (double)iZone * 1000000.0; + if ( dfURX >= 1000000.0 ) + dfURX -= (double)iZone * 1000000.0; + if ( dfLLX >= 1000000.0 ) + dfLLX -= (double)iZone * 1000000.0; + if ( dfLRX >= 1000000.0 ) + dfLRX -= (double)iZone * 1000000.0; + + // In EOSAT FAST Rev C, the angles are in decimal degrees + // otherwise they are in packed DMS format. + if (strstr(pszHeader, "REV C") != NULL) + bAnglesInPackedDMSFormat = FALSE; + else + bAnglesInPackedDMSFormat = TRUE; + + // Create projection definition + OGRErr eErr = + oSRS.importFromUSGS( iProjSys, iZone, adfProjParms, iDatum, bAnglesInPackedDMSFormat ); + if ( eErr != OGRERR_NONE ) + CPLDebug("FAST", "Import projection from USGS failed: %d", eErr); + oSRS.SetLinearUnits( SRS_UL_METER, 1.0 ); + + // Read datum name + pszTemp = GetValue( pszHeader, DATUM_NAME, DATUM_NAME_SIZE, FALSE ); + if ( pszTemp ) + { + if ( EQUAL( pszTemp, "WGS84" ) ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + else if ( EQUAL( pszTemp, "NAD27" ) ) + oSRS.SetWellKnownGeogCS( "NAD27" ); + else if ( EQUAL( pszTemp, "NAD83" ) ) + oSRS.SetWellKnownGeogCS( "NAD83" ); + CPLFree( pszTemp ); + } + else + { + // Reasonable fallback + oSRS.SetWellKnownGeogCS( "WGS84" ); + } + + if ( poDS->pszProjection ) + CPLFree( poDS->pszProjection ); + eErr = oSRS.exportToWkt( &poDS->pszProjection ); + if ( eErr != OGRERR_NONE ) + CPLDebug("FAST", "Export projection to WKT USGS failed: %d", eErr); + + // Generate GCPs + pasGCPList = (GDAL_GCP *) CPLCalloc( sizeof( GDAL_GCP ), 4 ); + GDALInitGCPs( 4, pasGCPList ); + CPLFree(pasGCPList[0].pszId); + CPLFree(pasGCPList[1].pszId); + CPLFree(pasGCPList[2].pszId); + CPLFree(pasGCPList[3].pszId); + + /* Let's order the GCP in TL, TR, BR, BL order to benefit from the */ + /* GDALGCPsToGeoTransform optimization */ + pasGCPList[0].pszId = CPLStrdup("UPPER_LEFT"); + pasGCPList[0].dfGCPX = dfULX; + pasGCPList[0].dfGCPY = dfULY; + pasGCPList[0].dfGCPZ = 0.0; + pasGCPList[0].dfGCPPixel = 0.5; + pasGCPList[0].dfGCPLine = 0.5; + pasGCPList[1].pszId = CPLStrdup("UPPER_RIGHT"); + pasGCPList[1].dfGCPX = dfURX; + pasGCPList[1].dfGCPY = dfURY; + pasGCPList[1].dfGCPZ = 0.0; + pasGCPList[1].dfGCPPixel = poDS->nRasterXSize-0.5; + pasGCPList[1].dfGCPLine = 0.5; + pasGCPList[2].pszId = CPLStrdup("LOWER_RIGHT"); + pasGCPList[2].dfGCPX = dfLRX; + pasGCPList[2].dfGCPY = dfLRY; + pasGCPList[2].dfGCPZ = 0.0; + pasGCPList[2].dfGCPPixel = poDS->nRasterXSize-0.5; + pasGCPList[2].dfGCPLine = poDS->nRasterYSize-0.5; + pasGCPList[3].pszId = CPLStrdup("LOWER_LEFT"); + pasGCPList[3].dfGCPX = dfLLX; + pasGCPList[3].dfGCPY = dfLLY; + pasGCPList[3].dfGCPZ = 0.0; + pasGCPList[3].dfGCPPixel = 0.5; + pasGCPList[3].dfGCPLine = poDS->nRasterYSize-0.5; + + // Calculate transformation matrix, if accurate + transform_ok = + GDALGCPsToGeoTransform(4,pasGCPList,poDS->adfGeoTransform,0); + if (transform_ok == FALSE) + { + + poDS->adfGeoTransform[0] = 0.0; + poDS->adfGeoTransform[1] = 1.0; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = 1.0; + if ( poDS->pszProjection ) + CPLFree( poDS->pszProjection ); + poDS->pszProjection = CPLStrdup(""); + } + + GDALDeinitGCPs(4, pasGCPList); + CPLFree(pasGCPList); + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int nPixelOffset = GDALGetDataTypeSize(poDS->eDataType) / 8; + int nLineOffset = poDS->nRasterXSize * nPixelOffset; + + for( i = 1; i <= poDS->nBands; i++ ) + { + poDS->SetBand( i, new FASTRasterBand( poDS, i, poDS->fpChannels[i - 1], + 0, nPixelOffset, nLineOffset, poDS->eDataType, TRUE)); + } + + CPLFree( pszHeader ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + + // opens overviews. + poDS->oOvManager.Initialize(poDS, poDS->pszFilename); + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + delete poDS; + CPLError( CE_Failure, CPLE_NotSupported, + "The FAST driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_FAST() */ +/************************************************************************/ + +void GDALRegister_FAST() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "FAST" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "FAST" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "EOSAT FAST Format" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_fast.html" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = FASTDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/raw/frmt_fast.html b/bazaar/plugin/gdal/frmts/raw/frmt_fast.html new file mode 100644 index 000000000..6ae5c447d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/frmt_fast.html @@ -0,0 +1,184 @@ + + +FAST -- EOSAT FAST Format + + + + +

    FAST -- EOSAT FAST Format

    + +Supported reading from FAST-L7A format (Landsat TM data) and EOSAT Fast Format +Rev. C (IRS-1C/1D data). If you want to read other datasets in this format (SPOT), +write to me (Andrey Kiselev, dron@ak4719.spb.edu). +You should share data samples with me.

    + +Datasets in FAST format represented by several files: one or more +administrative headers and one or more files with actual image data in raw +format. Administrative files contains different information about scene +parameters including filenames of images. You can read files with +administrative headers with any text viewer/editor, it is just plain ASCII +text.

    + +This driver wants administrative file for input. Filenames of images will be +extracted and data will be imported, every file will be interpreted as band.

    + +

    Data

    + +

    FAST-L7A

    + +FAST-L7A consists form several files: big ones with image data and three +small files with administrative information. You should give to driver one +of the administrative files:

    + +

      +
    • L7fppprrr_rrrYYYYMMDD_HPN.FST: panchromatic band header file with 1 + band +
    • L7fppprrr_rrrYYYYMMDD_HRF.FST: VNIR/ SWIR bands header file with 6 + bands +
    • L7fppprrr_rrrYYYYMMDD_HTM.FST: thermal bands header file with 2 bands +
    + +All raw images corresponded to their administrative files will be imported +as GDAL bands.

    + +From the `` +Level 1 Product Output Files Data Format Control Book'':

    + +The file naming convention for the FAST-L7A product files is +
    +L7fppprrr_rrrYYYYMMDD_AAA.FST
    +
    +where
    +
    +L7 = Landsat 7 mission
    +
    +f = ETM+ format (1 or 2) (data not pertaining to a specific format defaults to 1)
    +
    +ppp = starting path of the product
    +
    +rrr_rrr = starting and ending rows of the product
    +
    +YYYYMMDD = acquisition date of the image
    +
    +AAA = file type:
    +HPN = panchromatic band header file
    +HRF = VNIR/ SWIR bands header file
    +HTM = thermal bands header file
    +B10 = band 1
    +B20 = band 2
    +B30 = band 3
    +B40 = band 4
    +B50 = band 5
    +B61 = band 6L
    +B62 = band 6H
    +B70 = band 7
    +B80 = band 8
    +
    +FST = FAST file extension
    +

    + +So you should give to driver one of the L7fppprrr_rrrYYYYMMDD_HPN.FST, +L7fppprrr_rrrYYYYMMDD_HRF.FST or +L7fppprrr_rrrYYYYMMDD_HTM.FST files. + +

    IRS-1C/1D

    + +Fast Format REV. C does not contain band filenames in administrative header. +So we should guess band filenames, because different data distributors name +their files differently. Several naming schemes hardcoded in GDAL's FAST +driver. These are:

    + + +<header>.<ext>
    +<header>.1.<ext>
    +<header>.2.<ext>
    +...
    +

    + +or

    + + +<header>.<ext>
    +band1.<ext>
    +band2.<ext>
    +...
    +

    + +or

    + + +<header>.<ext>
    +band1.dat
    +band2.dat
    +...
    +

    + +or

    + + +<header>.<ext>
    +imagery1.<ext>
    +imagery2.<ext>
    +...
    +

    + +or

    + + +<header>.<ext>
    +imagery1.dat
    +imagery2.dat
    +...
    +

    + +in lower or upper case. Header file could be named arbitrarily. This should +cover majority of distributors fantasy in naming files. But if you out of +luck and your datasets named differently you should rename them manually +before importing data with GDAL.

    + +GDAL also supports the logic for naming band files for datasets produced by +Euromap GmbH for IRS-1C/IRS-1D PAN, LISS3 and WIFS sensors. +Their filename logic is explained in the +Euromap Naming Conventions document. + +

    Georeference

    + +All USGS projections should be supported (namely UTM, LCC, PS, PC, TM, OM, SOM). +Contact me if you have troubles with proper projection extraction. + +

    Metadata

    + +Calibration coefficients for each band reported as metadata items. + +
      + +
    • ACQUISITION_DATE: First scene acquisition date in yyyyddmm + format.

      + +

    • SATELLITE: First scene satellite name.

      + +

    • SENSOR: First scene sensor name.

      + +

    • BIASn: Bias value for the channel n.

      + +

    • GAINn: Gain value for the channel n.

      + +

    + +

    See Also:

    + +
      +
    • Implemented as gdal/frmts/raw/fastdataset.cpp.

      + +

    • Landsat FAST L7A format description available from + +http://ltpwww.gsfc.nasa.gov/IAS/htmls/l7_review.html +(see +ESDIS Level 1 Product Generation System (LPGS) Output Files DFCB, Vol. 5, Book 2)

      + +

    • EOSAT Fast Format REV. C description available from +http://www.euromap.de/docs/doc_001.html +

      + + + diff --git a/bazaar/plugin/gdal/frmts/raw/frmt_lcp.html b/bazaar/plugin/gdal/frmts/raw/frmt_lcp.html new file mode 100644 index 000000000..d094954c6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/frmt_lcp.html @@ -0,0 +1,649 @@ + + + +GDAL Driver for FARSITE LCP + + + + +

      GDAL Driver for FARSITE v.4 LCP Format

      +

      FARSITE v. 4 landscape file (LCP) is a multi-band raster format used by +wildland fire behavior and fire effect simulation models such as FARSITE, +FLAMMAP, and FBAT (www.fire.org). The bands of an LCP file store data describing +terrain, tree canopy, and surface fuel. The +USGS National Map for LANDFIRE +distributes data in LCP format, and +programs such as FARSITE and LFDAT can create LCP files from a set of input rasters.

      +

      An LCP file (.lcp) is basically a raw format with a 7,316-byte header +described below. The data type for all bands is 16-bit signed integer. Bands are +interleaved by pixel. Five bands are required: elevation, slope, aspect, fuel +model, and tree canopy cover. Crown fuel bands (canopy height, canopy base +height, canopy bulk density), and surface fuel bands (duff, coarse woody debris) +are optional.

      +

      The LCP driver reads the linear unit, cell size, and extent, but the LCP file +does not specify the projection. UTM projections are typical, but other +projections are possible.

      +

      The GDAL LCP driver reports dataset- and band-level metadata:

      +

      Dataset

      +
      +

      LATITUDE: Latitude of the dataset, negative for southern hemisphere
      + LINEAR_UNIT: Feet or meters
      + DESCRIPTION: LCP file description

      +
      +

      Band

      +
      +

      <band>_UNIT or <band>_OPTION: units or options code for the band
      + <band>_UNIT_NAME or <band>_OPTION_DESC: descriptive name of + units/options
      + <band>_MIN: minimum value
      + <band>_MAX: maximum value
      + <band>_NUM_CLASSES: number of classes, -1 if > 100
      + <band>_VALUES: comma-delimited list of class values (fuel model band + only)
      + <band>_FILE: original input raster file name for the band

      +
      +

      Note: The LCP driver derives from the RawDataset helper class declared +in gdal/frmts/raw. It should be implemented as gdal/frmts/raw/lcpdataset.cpp.

      +

      Creation Options

      +The LCP driver supports CreateCopy() and metadata values can be set via +creation options. Below is a list of options with default values listed first. +
    • ELEVATION_UNIT=[METERS/FEET]: Vertical unit for elevation +band.

    • + +
    • SLOPE_UNIT=[DEGREES/PERCENT]

    • +
    • ASPECT_UNIT=[AZIMUTH_DEGREES/GRASS_CATEGORIES/GRASS_DEGRESS]

    • +
    • FUEL_MODEL_OPTION=[NO_CUSTOM_AND_NO_FILE/CUSTOM_AND_NO_FILE/ + NO_CUSTOM_AND_FILE/CUSTOM_AND_FILE]: Specify whether or not custom + fuel models are used, and if a custom fuel model file is present.

    • +
    • CANOPY_COV_UNIT=[PERCENT/CATEGORIES]

    • +
    • CANOPY_HT_UNIT=[METERS_X_10/FEET/METERS/FEET_X_10]

    • +
    • CBH_UNIT=[METERS_X_10/METERS/FEET/FEET_X_10]

    • +
    • CBD_UNIT=[KG_PER_CUBIC_METER_X_100/POUND_PER_CUBIC_FOOT/ + KG_PER_CUBIC_METER/POUND_PER_CUBIC_FOOT_X_1000/TONS_PER_ACRE_X_100]

    • +
    • DUFF_UNIT=[MG_PER_HECTARE_X_10/TONS_PER_ACRE_X_10]

    • +
    • CALCULATE_STATS=[YES/NO]: Calculate and write the min/max for + each band and write the appropriate flags and values in the header. This is + mostly a legacy feature used for creating legends.

    • +
    • CLASSIFY_DATA=[YES/NO]: Classify the data into 100 unique values + or less and write and write the appropriate flags and values in the header. + This is mostly a legacy feature used for creating legends.

    • +
    • LINEAR_UNIT=[SET_FROM_SRS/METER/FOOT/KILOMETER]: Set the + linear unit, overriding (if it can be calculated) the value in the + associated spatial reference. If no spatial reference is available, it + defaults to METER.

    • +
    • LATITUDE=[-90-90]: Override the latitude from the spatial + reference. If no spatial reference is available, this should be set, + otherwise creation will fail.

    • +
    • DESCRIPTION=[...]: A short description(less than 512 characters) + of the dataset

    • + +Creation options that are units of linear measure are fairly lenient. +METERS=METER and FOOT=FEET for the most part. + +

      Note: CreateCopy does not scale or change any data. By setting the +units for various bands, it is assumed that the values are in the specified +units.

      + +

      LCP header format:

      +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Start byte No. of bytes Format Name Description
    0 4 long crown fuels 20 if no crown fuels, 21 if crown fuels exist (crown fuels = + canopy height, canopy base height, canopy bulk density)
    4 4 long ground fuels 20 if no ground fuels, 21 if ground fuels exist (ground + fuels = duff loading, coarse woody)
    8 4 long latitude latitude (negative for southern hemisphere)
    12 8 double loeast offset to preserve coordinate precision (legacy from 16-bit OS days)
    20 8 double hieast offset to preserve coordinate precision (legacy from 16-bit OS days)
    28 8 double lonorth offset to preserve coordinate precision (legacy from 16-bit OS days)
    36 8 double hinorth offset to preserve coordinate precision (legacy from 16-bit OS days)
    44 4 long loelev minimum elevation
    48 4 long hielev maximum elevation
    52 4 long numelev number of elevation classes, -1 if > 100
    56 400 long elevation values list of elevation values as longs
    456 4 long loslope minimum slope
    460 4 long hislope maximum slope
    464 4 long numslope number of slope classes, -1 if > 100
    468 400 long slope values list of slope values as longs
    868 4 long loaspect minimum aspect
    872 4 long hiaspect maximum aspect
    876 4 long numaspects number of aspect classes, -1 if > 100
    880 400 long aspect values list of aspect values as longs
    1280 4 long lofuel minimum fuel model value
    1284 4 long hifuel maximum fuel model value
    1288 4 long numfuel number of fuel models -1 if > 100
    1292 400 long fuel values list of fuel model values as longs
    1692 4 long locover minimum canopy cover
    1696 4 long hicover maximum canopy cover
    1700 4 long numcover number of canopy cover classes, -1 if > 100
    1704 400 long cover values list of canopy cover values as longs
    2104 4 long loheight minimum canopy height
    2108 4 long hiheight maximum canopy height
    2112 4 long numheight number of canopy height classes, -1 if > 100
    2116 400 long height values list of canopy height values as longs
    2516 4 long lobase minimum canopy base height
    2520 4 long hibase maximum canopy base height
    2524 4 long numbase number of canopy base height classes, -1 if > 100
    2528 400 long base values list of canopy base height values as longs
    2928 4 long lodensity minimum canopy bulk density
    2932 4 long hidensity maximum canopy bulk density
    2936 4 long numdensity number of canopy bulk density classes, -1 if >100
    2940 400 long density values list of canopy bulk density values as longs
    3340 4 long loduff minimum duff
    3344 4 long hiduff maximum duff
    3348 4 long numduff number of duff classes, -1 if > 100
    3352 400 long duff values list of duff values as longs
    3752 4 long lowoody minimum coarse woody
    3756 4 long hiwoody maximum coarse woody
    3760 4 long numwoodies number of coarse woody classes, -1 if > 100
    3764 400 long woody values list of coarse woody values as longs
    4164 4 long numeast number of raster columns
    4168 4 long numnorth number of raster rows
    4172 8 double EastUtm max X
    4180 8 double WestUtm min X
    4188 8 double NorthUtm max Y
    4196 8 double SouthUtm min Y
    4204 4 long GridUnits linear unit: 0 = meters, 1 = feet, 2 = kilometers
    4208 8 double XResol cell size width in GridUnits
    4216 8 double YResol cell size height in GridUnits
    4224 2 short EUnits elevation units: 0 = meters, 1 = feet
    4226 2 short SUnits slope units: 0 = degrees, 1 = percent
    4228 2 short AUnits aspect units: 0 = Grass categories, 1 = Grass degrees, 2 = + azimuth degrees
    4230 2 short FOptions fuel model options: 0 = no custom models AND no conversion + file, 1 = custom models BUT no conversion file, 2 = no custom models BUT + conversion file, 3 = custom models AND conversion file needed
    4232 2 short CUnits canopy cover units: 0 = categories (0-4), 1 = percent
    4234 2 short HUnits canopy height units: 1 = meters, 2 = feet, 3 = m x 10, 4 = + ft x 10
    4236 2 short BUnits canopy base height units: 1 = meters, 2 = feet, 3 = m x 10, + 4 = ft x 10
    4238 2 short PUnits canopy bulk density units: 1 = kg/m^3, 2 = lb/ft^3, 3 = + kg/m^3 x 100, 4 = lb/ft^3 x 1000
    4240 2 short DUnits duff units: 1 = Mg/ha x 10, 2 = t/ac x 10
    4242 2 short WOptions coarse woody options (1 if coarse woody band is present)
    4244 256 char[] ElevFile elevation file name
    4500 256 char[] SlopeFile slope file name
    4756 256 char[] AspectFile aspect file name
    5012 256 char[] FuelFile fuel model file name
    5268 256 char[] CoverFile canopy cover file name
    5524 256 char[] HeightFile canopy height file name
    5780 256 char[] BaseFile canopy base file name
    6036 256 char[] DensityFile canopy bulk density file name
    6292 256 char[] DuffFile duff file name
    6548 256 char[] WoodyFile coarse woody file name
    6804 512 char[] Description LCP file description
    + +

    Chris Toney, 2009-02-14

    + + + + diff --git a/bazaar/plugin/gdal/frmts/raw/frmt_mff2.html b/bazaar/plugin/gdal/frmts/raw/frmt_mff2.html new file mode 100644 index 000000000..13e684ebc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/frmt_mff2.html @@ -0,0 +1,241 @@ + + +MFF2 -- Vexcel MFF2 Image + + + + +

    MFF2 -- Vexcel MFF2 Image

    +GDAL supports MFF2 Image raster file format for read, update, and creation. +The MFF2 (Multi-File Format 2) format was designed to fit into +Vexcel Hierarchical Key-Value (HKV) databases, which can store +binary data as well as ASCII parameters. +This format is primarily used internally to the Vexcel InSAR +processing system.

    + +To select an MFF2 dataset, select the directory containing the attrib, +and image_data files for the dataset.

    + +Currently only latitude/longitude and UTM projection are supported +(georef.projection.name = ll or georef.projection.name = utm), +with the affine transform computed from the lat/long control points. In +any event, if GCPs are available in a georef file, they are returned with +the dataset.

    + +Newly created files (with a type of MFF2) are always just raw rasters +with no georeferencing information. For read, and creation all data types +(real, integer and complex in bit depths of 8, 16, 32) should be supported.

    + +IMPORTANT: When creating a new MFF2, be sure to set the projection before setting the geotransform (this is necessary because the geotransform is stored internally as 5 latitude-longitude ground control points, and the projection is needed to do the conversion).

    + +NOTE: Implemented as gdal/frmts/raw/hkvdataset.cpp.

    + +

    Format Details

    + +

    MFF2 Top-level Structure

    + +An MFF2 "file" is actually a set of files stored in a directory +containing an ASCII header file entitled "attrib", and binary +image data entitled "image_data". Optionally, there may be +an ASCII "georef" file containing georeferencing and projection +information, and an "image_data_ovr" (for "image_data" binary image data) +file containing tiled overviews of the image in TIFF format. +The ASCII files are arranged in key=value pairs. The allowable +pairs for each file are described below. + + +

    The "attrib" File

    + +As a minimum, the "attrib" file must specify the image extents, +pixel size in bytes, pixel encoding and datatype, and pixel +byte order. For example, + +
    +extent.cols    = 800
    +extent.rows    = 1040
    +pixel.size     = 32
    +pixel.encoding = { unsigned twos_complement *ieee_754 }
    +pixel.field    = { *real complex }
    +pixel.order    = { lsbf *msbf }
    +version        = 1.1
    +
    + +specifies an image that is 1040 lines by 800 pixels in extent. The +pixels are 32 bits of real data in "most significant byte first" (msbf) +order, encoded according to the ieee_754 specification. In MFF2, when +a value must belong to a certain subset (eg. pixel.order must be either +lsbf or msbf), all options are displayed between curly brackets, and +the one appropriate for the current file is indicated with a "*". +

    +The file may also contain the following lines indicating the number of +channels of data, and how they are interleaved within the binary data +file. + +

    +channel.enumeration = 1
    +channel.interleave = { *pixel tile sequential }
    +
    + +

    The "image_data" File

    + +The "image_data" file consists of raw binary data, with extents, +pixel encoding, and number of channels as indicated in the "attrib" file. + +

    The "georef" File

    + +The "georef" file is used to describe the geocoding and projection +information for the binary data. For example, + +
    +top_left.latitude            = 32.93333333333334
    +top_left.longitude           = 130.0
    +top_right.latitude           = 32.93333333333334
    +top_right.longitude          = 130.5
    +bottom_left.latitude         = 32.50000000000001
    +bottom_left.longitude        = 130.0
    +bottom_right.latitude        = 32.50000000000001
    +bottom_right.longitude       = 130.5
    +centre.latitude              = 32.71666666666668
    +centre.longitude             = 130.25
    +projection.origin_longitude  = 0
    +projection.name              = ll
    +spheroid.name                = wgs-84
    +
    +
    + +describes an orthogonal latitude/longitude (ll) projected image, with +latitudes and longitudes based on the wgs-84 ellipsoid. +

    +Since MFF2 version 1.1, +top_left refers to the top left corner of the top left pixel. +top_right refers to the top right corner of the top right pixel. +bottom_left refers to the bottom left corner of the bottom left pixel. +bottom_right refers to the bottom right corner of the bottom right pixel. +centre refers to the centre of the four corners defined above (center +of the image). +

    +Mathematically, for an Npix by Nline image, the corners and centre in +(pixel,line) coordinates for MFF2 version 1.1 are: + +

    +top_left: (0,0)
    +top_right: (Npix,0)
    +bottom_left: (0,Nline)
    +bottom_right: (Npix,Nline)
    +centre: (Npix/2.0,Nline/2.0)
    +
    + +These calculations are done using floating point arithmetic (ie. +centre coordinates may take on non-integer values). +

    +Note that the corners are always expressed in latitudes/longitudes, even +for projected images. +

    + +

    Supported projections

    + +ll- Orthogonal latitude/longitude projected image, with latitude +parallel to the rows, longitude parallel to the columns. Parameters: +spheroid name, projection.origin_longitude (longitude at the origin of +the projection coordinates). If not set, this should default to the +central longitude of the output image based on its projection boundaries. +

    +utm- Universal Transverse Mercator projected image. Parameters: +spheroid name, projection.origin_longitude (central meridian for the +utm projection). The central meridian must be the meridian at the +centre of a UTM zone, ie. 3 degrees, 9 degrees, 12 degrees, etc. If +this is not specified or set a valid UTM central meridian, the reader +should reset the value to the nearest valid central meridian based on +the central longitude of the output image. The latitude at the origin +of the UTM projection is always 0 degrees. +

    + +

    Recognized ellipsoids

    + +MFF2 format associates the following names with ellipsoid equatorial +radius and inverse flattening parameters: + +
    +airy-18304:            6377563.396      299.3249646
    +modified-airy4:        6377340.189      299.3249646
    +australian-national4:  6378160          298.25
    +bessel-1841-namibia4:  6377483.865      299.1528128
    +bessel-18414:          6377397.155      299.1528128
    +clarke-18584:          6378294.0        294.297
    +clarke-18664:          6378206.4        294.9786982
    +clarke-18804:          6378249.145      293.465
    +everest-india-18304:   6377276.345      300.8017
    +everest-sabah-sarawak4:6377298.556      300.8017
    +everest-india-19564:   6377301.243      300.8017
    +everest-malaysia-19694:6377295.664      300.8017
    +everest-malay-sing4:   6377304.063      300.8017
    +everest-pakistan4:     6377309.613      300.8017
    +modified-fisher-19604: 6378155          298.3
    +helmert-19064:         6378200          298.3
    +hough-19604:           6378270          297
    +hughes4:               6378273.0        298.279
    +indonesian-1974:       6378160          298.247
    +international-1924:    6378388          297
    +iugc-67:               6378160.0        298.254
    +iugc-75:               6378140.0        298.25298
    +krassovsky-1940:       6378245          298.3
    +kaula:                 6378165.0        292.308
    +grs-80:                6378137          298.257222101
    +south-american-1969:   6378160          298.25
    +wgs-72:                6378135          298.26
    +wgs-84:                6378137          298.257223563
    +ev-wgs-84:             6378137          298.252841
    +ev-bessel:             6377397          299.1976073
    +
    + + +

    Explanation of fields

    + +
    +channel.enumeration:  (optional- only needed for multiband)
    +Number of channels of data (eg. 3 for rgb)
    +
    +channel.interleave = { *pixel tile sequential } :  (optional- only 
    +needed for multiband)
    +
    +For multiband data, indicates how the channels are interleaved.  *pixel 
    +indicates that data is stored red value, green value, blue value, red 
    +value, green value, blue value etc. as opposed to (line of red values) 
    +(line of green values) (line of blue values) or (entire red channel) 
    +(entire green channel) (entire blue channel)
    +
    +extent.cols:
    +Number of columns of data.
    +
    +extent.rows:
    +Number of rows of data.
    +
    +pixel.encoding = { *unsigned twos-complement ieee-754 }:
    +Combines with pixel.size and pixel.field to give the data type:
    +(encoding, field, size)- type
    +(unsigned, real, 8)- unsigned byte data
    +(unsigned, real, 16)- unsigned int 16 data
    +(unsigned, real, 32)- unsigned int 32 data
    +(twos-complement, real, 16)- signed int 16 data
    +(twos-complement, real, 32)- signed int 32 data
    +(twos-complement, complex, 64)- complex signed int 32 data
    +(ieee-754, real, 32)- real 32 bit floating point data
    +(ieee-754, real, 64)- real 64 bit floating point data
    +(ieee-754, complex, 64)- complex 32 bit floating point data
    +(ieee-754, complex, 128)- complex 64 bit floating point data
    +
    +pixel.size:
    +Size of one pixel of one channel (bits).
    +
    +pixel.field = { *real complex }:
    +Whether the data is real or complex.
    +
    +pixel.order = { *lsbf msbf }:
    +Byte ordering of the data (least or most significant byte first).
    +
    +version: (only in newer versions- if not present, older version is 
    +assumed) Version of mff2.  
    +
    + + + diff --git a/bazaar/plugin/gdal/frmts/raw/fujibasdataset.cpp b/bazaar/plugin/gdal/frmts/raw/fujibasdataset.cpp new file mode 100644 index 000000000..af2bb09e8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/fujibasdataset.cpp @@ -0,0 +1,253 @@ +/****************************************************************************** + * $Id: fujibasdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: eCognition + * Purpose: Implementation of FUJI BAS Format + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: fujibasdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +CPL_C_START +void GDALRegister_FujiBAS(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* FujiBASDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class FujiBASDataset : public RawDataset +{ + FILE *fpImage; // image data file. + + char **papszHeader; + + public: + FujiBASDataset(); + ~FujiBASDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* FujiBASDataset() */ +/************************************************************************/ + +FujiBASDataset::FujiBASDataset() +{ + fpImage = NULL; + papszHeader = NULL; +} + +/************************************************************************/ +/* ~FujiBASDataset() */ +/************************************************************************/ + +FujiBASDataset::~FujiBASDataset() + +{ + FlushCache(); + if( fpImage != NULL ) + VSIFClose( fpImage ); + CSLDestroy( papszHeader ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *FujiBASDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* We assume the user is pointing to the header (.pcb) file. */ +/* Does this appear to be a pcb file? */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 80 || poOpenInfo->fpL == NULL ) + return NULL; + + if( !EQUALN((const char *)poOpenInfo->pabyHeader,"[Raw data]",10) + || strstr((const char *)poOpenInfo->pabyHeader, "Fuji BAS") == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Load the header file. */ +/* -------------------------------------------------------------------- */ + char **papszHeader; + int nXSize, nYSize; + const char *pszOrgFile; + + papszHeader = CSLLoad( poOpenInfo->pszFilename ); + + if( papszHeader == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Munge header information into form suitable for CSL functions. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; papszHeader[i] != NULL; i++ ) + { + char *pszSep = strstr(papszHeader[i]," = "); + + if( pszSep != NULL ) + { + memmove( pszSep + 1, pszSep + 3, strlen(pszSep+3)+1 ); + *pszSep = '='; + } + } + +/* -------------------------------------------------------------------- */ +/* Fetch required fields. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue(papszHeader, "width") == NULL + || CSLFetchNameValue(papszHeader, "height") == NULL + || CSLFetchNameValue(papszHeader, "OrgFile") == NULL ) + { + CSLDestroy( papszHeader ); + return NULL; + } + + nYSize = atoi(CSLFetchNameValue(papszHeader,"width")); + nXSize = atoi(CSLFetchNameValue(papszHeader,"height")); + + pszOrgFile = CSLFetchNameValue(papszHeader,"OrgFile"); + + if( nXSize < 1 || nYSize < 1 ) + { + CSLDestroy( papszHeader ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The FUJIBAS driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to open the original data file. */ +/* -------------------------------------------------------------------- */ + const char *pszRawFile; + char *pszPath = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename)); + FILE *fpRaw; + + pszRawFile = CPLFormCIFilename( pszPath, pszOrgFile, "IMG" ); + CPLFree( pszPath ); + + fpRaw = VSIFOpen( pszRawFile, "rb" ); + if( fpRaw == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Trying to open Fuji BAS image with the header file:\n" + " Header=%s\n" + "but expected raw image file doesn't appear to exist. Trying to open:\n" + " Raw File=%s\n" + "Perhaps the raw file needs to be renamed to match expected?", + poOpenInfo->pszFilename, + pszRawFile ); + CSLDestroy( papszHeader ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + FujiBASDataset *poDS; + + poDS = new FujiBASDataset(); + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->papszHeader = papszHeader; + poDS->fpImage = fpRaw; + +/* -------------------------------------------------------------------- */ +/* Create band information object. */ +/* -------------------------------------------------------------------- */ + int bNativeOrder; +#ifdef CPL_MSB + bNativeOrder = TRUE; +#else + bNativeOrder = FALSE; +#endif + poDS->SetBand( 1, + new RawRasterBand( poDS, 1, poDS->fpImage, + 0, 2, nXSize * 2, GDT_UInt16, bNativeOrder )); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_FujiBAS() */ +/************************************************************************/ + +void GDALRegister_FujiBAS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "FujiBAS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "FujiBAS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Fuji BAS Scanner Image" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#FujiBAS" ); + + poDriver->pfnOpen = FujiBASDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/raw/genbindataset.cpp b/bazaar/plugin/gdal/frmts/raw/genbindataset.cpp new file mode 100644 index 000000000..704695323 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/genbindataset.cpp @@ -0,0 +1,903 @@ +/****************************************************************************** + * $Id: ehdrdataset.cpp 12350 2007-10-08 17:41:32Z rouault $ + * + * Project: GDAL + * Purpose: Generic Binary format driver (.hdr but not ESRI .hdr!) + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ehdrdataset.cpp 12350 2007-10-08 17:41:32Z rouault $"); + +CPL_C_START +void GDALRegister_GenBin(void); +CPL_C_END + +/* ==================================================================== */ +/* Table relating USGS and ESRI state plane zones. */ +/* ==================================================================== */ +static const int anUsgsEsriZones[] = +{ + 101, 3101, + 102, 3126, + 201, 3151, + 202, 3176, + 203, 3201, + 301, 3226, + 302, 3251, + 401, 3276, + 402, 3301, + 403, 3326, + 404, 3351, + 405, 3376, + 406, 3401, + 407, 3426, + 501, 3451, + 502, 3476, + 503, 3501, + 600, 3526, + 700, 3551, + 901, 3601, + 902, 3626, + 903, 3576, + 1001, 3651, + 1002, 3676, + 1101, 3701, + 1102, 3726, + 1103, 3751, + 1201, 3776, + 1202, 3801, + 1301, 3826, + 1302, 3851, + 1401, 3876, + 1402, 3901, + 1501, 3926, + 1502, 3951, + 1601, 3976, + 1602, 4001, + 1701, 4026, + 1702, 4051, + 1703, 6426, + 1801, 4076, + 1802, 4101, + 1900, 4126, + 2001, 4151, + 2002, 4176, + 2101, 4201, + 2102, 4226, + 2103, 4251, + 2111, 6351, + 2112, 6376, + 2113, 6401, + 2201, 4276, + 2202, 4301, + 2203, 4326, + 2301, 4351, + 2302, 4376, + 2401, 4401, + 2402, 4426, + 2403, 4451, + 2500, 0, + 2501, 4476, + 2502, 4501, + 2503, 4526, + 2600, 0, + 2601, 4551, + 2602, 4576, + 2701, 4601, + 2702, 4626, + 2703, 4651, + 2800, 4676, + 2900, 4701, + 3001, 4726, + 3002, 4751, + 3003, 4776, + 3101, 4801, + 3102, 4826, + 3103, 4851, + 3104, 4876, + 3200, 4901, + 3301, 4926, + 3302, 4951, + 3401, 4976, + 3402, 5001, + 3501, 5026, + 3502, 5051, + 3601, 5076, + 3602, 5101, + 3701, 5126, + 3702, 5151, + 3800, 5176, + 3900, 0, + 3901, 5201, + 3902, 5226, + 4001, 5251, + 4002, 5276, + 4100, 5301, + 4201, 5326, + 4202, 5351, + 4203, 5376, + 4204, 5401, + 4205, 5426, + 4301, 5451, + 4302, 5476, + 4303, 5501, + 4400, 5526, + 4501, 5551, + 4502, 5576, + 4601, 5601, + 4602, 5626, + 4701, 5651, + 4702, 5676, + 4801, 5701, + 4802, 5726, + 4803, 5751, + 4901, 5776, + 4902, 5801, + 4903, 5826, + 4904, 5851, + 5001, 6101, + 5002, 6126, + 5003, 6151, + 5004, 6176, + 5005, 6201, + 5006, 6226, + 5007, 6251, + 5008, 6276, + 5009, 6301, + 5010, 6326, + 5101, 5876, + 5102, 5901, + 5103, 5926, + 5104, 5951, + 5105, 5976, + 5201, 6001, + 5200, 6026, + 5200, 6076, + 5201, 6051, + 5202, 6051, + 5300, 0, + 5400, 0 +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GenBinDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GenBinDataset : public RawDataset +{ + friend class GenBinBitRasterBand; + + VSILFILE *fpImage; // image data file. + + int bGotTransform; + double adfGeoTransform[6]; + char *pszProjection; + + char **papszHDR; + + void ParseCoordinateSystem( char ** ); + + public: + GenBinDataset(); + ~GenBinDataset(); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual const char *GetProjectionRef(void); + + virtual char **GetFileList(); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GenBinBitRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class GenBinBitRasterBand : public GDALPamRasterBand +{ + int nBits; + + public: + GenBinBitRasterBand( GenBinDataset *poDS, int nBits ); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* GenBinBitRasterBand() */ +/************************************************************************/ + +GenBinBitRasterBand::GenBinBitRasterBand( GenBinDataset *poDS, int nBitsIn ) +{ + SetMetadataItem( "NBITS", + CPLString().Printf("%d",nBitsIn), + "IMAGE_STRUCTURE" ); + + this->poDS = poDS; + nBits = nBitsIn; + nBand = 1; + + eDataType = GDT_Byte; + + nBlockXSize = poDS->nRasterXSize; + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GenBinBitRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) + +{ + GenBinDataset *poGDS = (GenBinDataset *) poDS; + vsi_l_offset nLineStart; + unsigned int nLineBytes; + int iBitOffset; + GByte *pabyBuffer; + +/* -------------------------------------------------------------------- */ +/* Establish desired position. */ +/* -------------------------------------------------------------------- */ + nLineStart = (((vsi_l_offset)nBlockXSize) * nBlockYOff * nBits) / 8; + iBitOffset = (int)((((vsi_l_offset)nBlockXSize) * nBlockYOff * nBits) % 8); + nLineBytes = (int) ((((vsi_l_offset)nBlockXSize) * (nBlockYOff+1) * nBits + 7) / 8 - nLineStart); + +/* -------------------------------------------------------------------- */ +/* Read data into buffer. */ +/* -------------------------------------------------------------------- */ + pabyBuffer = (GByte *) CPLCalloc(nLineBytes,1); + + if( VSIFSeekL( poGDS->fpImage, nLineStart, SEEK_SET ) != 0 + || VSIFReadL( pabyBuffer, 1, nLineBytes, poGDS->fpImage) != nLineBytes ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read %u bytes at offset %lu.\n%s", + nLineBytes, (unsigned long)nLineStart, + VSIStrerror( errno ) ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Copy data, promoting to 8bit. */ +/* -------------------------------------------------------------------- */ + int iX; + + if( nBits == 1 ) + { + for( iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits ) + { + if( pabyBuffer[iBitOffset>>3] & (0x80 >>(iBitOffset & 7)) ) + ((GByte *) pImage)[iX] = 1; + else + ((GByte *) pImage)[iX] = 0; + } + } + else if( nBits == 2 ) + { + for( iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits ) + { + ((GByte *) pImage)[iX] = + ((pabyBuffer[iBitOffset>>3]) >> (6-(iBitOffset&0x7)) & 0x3); + } + } + else if( nBits == 4 ) + { + for( iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits ) + { + if( iBitOffset == 0 ) + ((GByte *) pImage)[iX] = (pabyBuffer[iBitOffset>>3]) >> 4; + else + ((GByte *) pImage)[iX] = (pabyBuffer[iBitOffset>>3]) & 0xf; + } + } + else { + CPLAssert( FALSE ); + } + + CPLFree( pabyBuffer ); + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* GenBinDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GenBinDataset() */ +/************************************************************************/ + +GenBinDataset::GenBinDataset() +{ + fpImage = NULL; + pszProjection = CPLStrdup(""); + bGotTransform = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + papszHDR = NULL; +} + +/************************************************************************/ +/* ~GenBinDataset() */ +/************************************************************************/ + +GenBinDataset::~GenBinDataset() + +{ + FlushCache(); + + if( fpImage != NULL ) + VSIFCloseL( fpImage ); + + CPLFree( pszProjection ); + CSLDestroy( papszHDR ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GenBinDataset::GetProjectionRef() + +{ + if (pszProjection && strlen(pszProjection) > 0) + return pszProjection; + + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GenBinDataset::GetGeoTransform( double * padfTransform ) + +{ + if( bGotTransform ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; + } + else + { + return GDALPamDataset::GetGeoTransform( padfTransform ); + } +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **GenBinDataset::GetFileList() + +{ + CPLString osPath = CPLGetPath( GetDescription() ); + CPLString osName = CPLGetBasename( GetDescription() ); + char **papszFileList = NULL; + + // Main data file, etc. + papszFileList = GDALPamDataset::GetFileList(); + + // Header file. + CPLString osFilename = CPLFormCIFilename( osPath, osName, "hdr" ); + papszFileList = CSLAddString( papszFileList, osFilename ); + + return papszFileList; +} + +/************************************************************************/ +/* ParseCoordinateSystem() */ +/************************************************************************/ + +void GenBinDataset::ParseCoordinateSystem( char **papszHdr ) + +{ + const char *pszProjName = CSLFetchNameValue( papszHdr, "PROJECTION_NAME" ); + OGRSpatialReference oSRS; + + if( pszProjName == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Translate zone and parameters into numeric form. */ +/* -------------------------------------------------------------------- */ + int nZone = 0; + double adfProjParms[15]; + const char *pszUnits = CSLFetchNameValue( papszHdr, "MAP_UNITS" ); + const char *pszDatumName = CSLFetchNameValue( papszHdr, "DATUM_NAME" ); + + if( CSLFetchNameValue( papszHdr, "PROJECTION_ZONE" ) ) + nZone = atoi(CSLFetchNameValue( papszHdr, "PROJECTION_ZONE" )); + + memset( adfProjParms, 0, sizeof(adfProjParms) ); + if( CSLFetchNameValue( papszHdr, "PROJECTION_PARAMETERS" ) ) + { + int i; + char **papszTokens = CSLTokenizeString( + CSLFetchNameValue( papszHdr, "PROJECTION_PARAMETERS" ) ); + + for( i = 0; i < 15 && papszTokens[i] != NULL; i++ ) + adfProjParms[i] = CPLAtofM( papszTokens[i] ); + + CSLDestroy( papszTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Handle projections. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszProjName,"UTM") && nZone != 0 ) + { + // honestly, I'm just getting that the negative zone for + // southern hemisphere is used. + oSRS.SetUTM( ABS(nZone), nZone > 0 ); + } + + else if( EQUAL(pszProjName,"State Plane") && nZone != 0 ) + { + int nPairs = sizeof(anUsgsEsriZones) / (2*sizeof(int)); + int i; + double dfUnits = 0.0; + + for( i = 0; i < nPairs; i++ ) + { + if( anUsgsEsriZones[i*2+1] == nZone ) + { + nZone = anUsgsEsriZones[i*2]; + break; + } + + } + + if( EQUAL(pszUnits,"feet") ) + dfUnits = CPLAtofM(SRS_UL_US_FOOT_CONV); + else if( EQUALN(pszUnits,"MET",3) ) + dfUnits = 1.0; + else + pszUnits = NULL; + + oSRS.SetStatePlane( ABS(nZone), + pszDatumName==NULL || !EQUAL(pszDatumName,"NAD27"), + pszUnits, dfUnits ); + } + +/* -------------------------------------------------------------------- */ +/* Setup the geographic coordinate system. */ +/* -------------------------------------------------------------------- */ + if( oSRS.GetAttrNode( "GEOGCS" ) == NULL ) + { + if( pszDatumName != NULL + && oSRS.SetWellKnownGeogCS( pszDatumName ) == OGRERR_NONE ) + { + // good + } + else if( CSLFetchNameValue( papszHdr, "SPHEROID_NAME" ) + && CSLFetchNameValue( papszHdr, "SEMI_MAJOR_AXIS" ) + && CSLFetchNameValue( papszHdr, "SEMI_MINOR_AXIS" ) ) + { + double dfSemiMajor = CPLAtofM(CSLFetchNameValue( papszHdr, "SEMI_MAJOR_AXIS")); + double dfSemiMinor = CPLAtofM(CSLFetchNameValue( papszHdr, "SEMI_MINOR_AXIS")); + + oSRS.SetGeogCS( CSLFetchNameValue( papszHdr, "SPHEROID_NAME" ), + CSLFetchNameValue( papszHdr, "SPHEROID_NAME" ), + CSLFetchNameValue( papszHdr, "SPHEROID_NAME" ), + dfSemiMajor, + 1.0 / (1.0 - dfSemiMinor/dfSemiMajor) ); + } + else // fallback default. + oSRS.SetWellKnownGeogCS( "WGS84" ); + } + +/* -------------------------------------------------------------------- */ +/* Convert to WKT. */ +/* -------------------------------------------------------------------- */ + CPLFree( pszProjection ); + pszProjection = NULL; + + oSRS.exportToWkt( &pszProjection ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GenBinDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i, bSelectedHDR; + +/* -------------------------------------------------------------------- */ +/* We assume the user is pointing to the binary (ie. .bil) file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 2 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Now we need to tear apart tfhe filename to form a .HDR */ +/* filename. */ +/* -------------------------------------------------------------------- */ + CPLString osPath = CPLGetPath( poOpenInfo->pszFilename ); + CPLString osName = CPLGetBasename( poOpenInfo->pszFilename ); + CPLString osHDRFilename; + + char** papszSiblingFiles = poOpenInfo->GetSiblingFiles(); + if( papszSiblingFiles ) + { + int iFile = CSLFindString(papszSiblingFiles, + CPLFormFilename( NULL, osName, "hdr" ) ); + if( iFile < 0 ) // return if there is no corresponding .hdr file + return NULL; + + osHDRFilename = + CPLFormFilename( osPath, papszSiblingFiles[iFile], + NULL ); + } + else + { + osHDRFilename = CPLFormCIFilename( osPath, osName, "hdr" ); + } + + bSelectedHDR = EQUAL( osHDRFilename, poOpenInfo->pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Do we have a .hdr file? */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + + fp = VSIFOpenL( osHDRFilename, "r" ); + + if( fp == NULL ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read a chunk to skim for expected keywords. */ +/* -------------------------------------------------------------------- */ + char achHeader[1000]; + + int nRead = VSIFReadL( achHeader, 1, sizeof(achHeader) - 1, fp ); + achHeader[nRead] = '\0'; + VSIFSeekL( fp, 0, SEEK_SET ); + + if( strstr( achHeader, "BANDS:" ) == NULL + || strstr( achHeader, "ROWS:" ) == NULL + || strstr( achHeader, "COLS:" ) == NULL ) + { + VSIFCloseL( fp ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Has the user selected the .hdr file to open? */ +/* -------------------------------------------------------------------- */ + if( bSelectedHDR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The selected file is an Generic Binary header file, but to\n" + "open Generic Binary datasets, the data file should be selected\n" + "instead of the .hdr file. Please try again selecting\n" + "the raw data file corresponding to the header file: %s\n", + poOpenInfo->pszFilename ); + VSIFCloseL( fp ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the .hdr file. */ +/* -------------------------------------------------------------------- */ + char **papszHdr = NULL; + const char *pszLine = CPLReadLineL( fp ); + + while( pszLine != NULL ) + { + if( EQUAL(pszLine,"PROJECTION_PARAMETERS:") ) + { + CPLString osPP = pszLine; + + pszLine = CPLReadLineL(fp); + while( pszLine != NULL + && (*pszLine == '\t' || *pszLine == ' ') ) + { + osPP += pszLine; + pszLine = CPLReadLineL(fp); + } + papszHdr = CSLAddString( papszHdr, osPP ); + } + else + { + char *pszName; + CPLString osValue; + + osValue = CPLParseNameValue( pszLine, &pszName ); + osValue.Trim(); + + papszHdr = CSLSetNameValue( papszHdr, pszName, osValue ); + CPLFree( pszName ); + + pszLine = CPLReadLineL( fp ); + } + } + + VSIFCloseL( fp ); + + if( CSLFetchNameValue( papszHdr, "COLS" ) == NULL + || CSLFetchNameValue( papszHdr, "ROWS" ) == NULL + || CSLFetchNameValue( papszHdr, "BANDS" ) == NULL ) + { + CSLDestroy( papszHdr ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GenBinDataset *poDS; + + poDS = new GenBinDataset(); + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + int nBands = atoi(CSLFetchNameValue( papszHdr, "BANDS" )); + + poDS->nRasterXSize = atoi(CSLFetchNameValue( papszHdr, "COLS" )); + poDS->nRasterYSize = atoi(CSLFetchNameValue( papszHdr, "ROWS" )); + poDS->papszHDR = papszHdr; + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || + !GDALCheckBandCount(nBands, FALSE)) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open target binary file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "r+b" ); + + if( poDS->fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s with write permission.\n%s", + osName.c_str(), VSIStrerror( errno ) ); + delete poDS; + return NULL; + } + + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Figure out the data type. */ +/* -------------------------------------------------------------------- */ + const char *pszDataType = CSLFetchNameValue( papszHdr, "DATATYPE" ); + GDALDataType eDataType; + int nBits = -1; // Only needed for partial byte types + + if( pszDataType == NULL ) + eDataType = GDT_Byte; + else if( EQUAL(pszDataType,"U16") ) + eDataType = GDT_UInt16; + else if( EQUAL(pszDataType,"S16") ) + eDataType = GDT_Int16; + else if( EQUAL(pszDataType,"F32") ) + eDataType = GDT_Float32; + else if( EQUAL(pszDataType,"F64") ) + eDataType = GDT_Float64; + else if( EQUAL(pszDataType,"U8") ) + eDataType = GDT_Byte; + else if( EQUAL(pszDataType,"U1") + || EQUAL(pszDataType,"U2") + || EQUAL(pszDataType,"U4") ) + { + nBits = atoi(pszDataType+1); + eDataType = GDT_Byte; + if( nBands != 1 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Only one band is supported for U1/U2/U4 data type" ); + delete poDS; + return NULL; + } + } + else + { + eDataType = GDT_Byte; + CPLError( CE_Warning, CPLE_AppDefined, + "DATATYPE=%s not recognised, assuming Byte.", + pszDataType ); + } + +/* -------------------------------------------------------------------- */ +/* Do we need byte swapping? */ +/* -------------------------------------------------------------------- */ + const char *pszBYTE_ORDER = CSLFetchNameValue(papszHdr,"BYTE_ORDER"); + int bNative = TRUE; + + if( pszBYTE_ORDER != NULL ) + { +#ifdef CPL_LSB + bNative = EQUALN(pszBYTE_ORDER,"LSB",3); +#else + bNative = !EQUALN(pszBYTE_ORDER,"LSB",3); +#endif + } + +/* -------------------------------------------------------------------- */ +/* Work out interleaving info. */ +/* -------------------------------------------------------------------- */ + int nItemSize = GDALGetDataTypeSize(eDataType)/8; + const char *pszInterleaving = CSLFetchNameValue(papszHdr,"INTERLEAVING"); + int nPixelOffset, nLineOffset; + vsi_l_offset nBandOffset; + int bIntOverflow = FALSE; + + if( pszInterleaving == NULL ) + pszInterleaving = "BIL"; + + if( EQUAL(pszInterleaving,"BSQ") || EQUAL(pszInterleaving,"NA") ) + { + nPixelOffset = nItemSize; + if (poDS->nRasterXSize > INT_MAX / nItemSize) bIntOverflow = TRUE; + nLineOffset = nItemSize * poDS->nRasterXSize; + nBandOffset = nLineOffset * poDS->nRasterYSize; + } + else if( EQUAL(pszInterleaving,"BIP") ) + { + nPixelOffset = nItemSize * nBands; + if (poDS->nRasterXSize > INT_MAX / nPixelOffset) bIntOverflow = TRUE; + nLineOffset = nPixelOffset * poDS->nRasterXSize; + nBandOffset = nItemSize; + } + else + { + if( !EQUAL(pszInterleaving,"BIL") ) + CPLError( CE_Warning, CPLE_AppDefined, + "INTERLEAVING:%s not recognised, assume BIL.", + pszInterleaving ); + + nPixelOffset = nItemSize; + if (poDS->nRasterXSize > INT_MAX / (nPixelOffset * nBands)) bIntOverflow = TRUE; + nLineOffset = nPixelOffset * nBands * poDS->nRasterXSize; + nBandOffset = nItemSize * poDS->nRasterXSize; + } + + if (bIntOverflow) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "Int overflow occured."); + return NULL; + } + + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->PamInitialize(); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = nBands; + for( i = 0; i < poDS->nBands; i++ ) + { + if( nBits != -1 ) + { + poDS->SetBand( i+1, new GenBinBitRasterBand( poDS, nBits ) ); + } + else + poDS->SetBand( + i+1, + new RawRasterBand( poDS, i+1, poDS->fpImage, + nBandOffset * i, nPixelOffset, nLineOffset, + eDataType, bNative, TRUE ) ); + } + +/* -------------------------------------------------------------------- */ +/* Get geotransform. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue(papszHdr,"UL_X_COORDINATE") != NULL + && CSLFetchNameValue(papszHdr,"UL_Y_COORDINATE") != NULL + && CSLFetchNameValue(papszHdr,"LR_X_COORDINATE") != NULL + && CSLFetchNameValue(papszHdr,"LR_Y_COORDINATE") != NULL ) + { + double dfULX = CPLAtofM(CSLFetchNameValue(papszHdr,"UL_X_COORDINATE")); + double dfULY = CPLAtofM(CSLFetchNameValue(papszHdr,"UL_Y_COORDINATE")); + double dfLRX = CPLAtofM(CSLFetchNameValue(papszHdr,"LR_X_COORDINATE")); + double dfLRY = CPLAtofM(CSLFetchNameValue(papszHdr,"LR_Y_COORDINATE")); + + poDS->adfGeoTransform[1] = (dfLRX - dfULX) / (poDS->nRasterXSize-1); + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = (dfLRY - dfULY) / (poDS->nRasterYSize-1); + + poDS->adfGeoTransform[0] = dfULX - poDS->adfGeoTransform[1] * 0.5; + poDS->adfGeoTransform[3] = dfULY - poDS->adfGeoTransform[5] * 0.5; + + poDS->bGotTransform = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Try and parse the coordinate system. */ +/* -------------------------------------------------------------------- */ + poDS->ParseCoordinateSystem( papszHdr ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_GenBin() */ +/************************************************************************/ + +void GDALRegister_GenBin() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GenBin" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GenBin" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Generic Binary (.hdr Labelled)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#GenBin" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = GenBinDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/gscdataset.cpp b/bazaar/plugin/gdal/frmts/raw/gscdataset.cpp new file mode 100644 index 000000000..38112f1c8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/gscdataset.cpp @@ -0,0 +1,245 @@ +/****************************************************************************** + * $Id: gscdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: GSC Geogrid format driver. + * Purpose: Implements support for reading and writing GSC Geogrid format. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: gscdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* GSCDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GSCDataset : public RawDataset +{ + VSILFILE *fpImage; // image data file. + + double adfGeoTransform[6]; + + public: + GSCDataset(); + ~GSCDataset(); + + CPLErr GetGeoTransform( double * padfTransform ); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* GSCDataset() */ +/************************************************************************/ + +GSCDataset::GSCDataset() +{ + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + fpImage = NULL; +} + +/************************************************************************/ +/* ~GSCDataset() */ +/************************************************************************/ + +GSCDataset::~GSCDataset() + +{ + FlushCache(); + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GSCDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + + return CE_None; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GSCDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int nPixels, nLines, i, nRecordLen; + +/* -------------------------------------------------------------------- */ +/* Does this plausible look like a GSC Geogrid file? */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 20 ) + return NULL; + + if( poOpenInfo->pabyHeader[12] != 0x02 + || poOpenInfo->pabyHeader[13] != 0x00 + || poOpenInfo->pabyHeader[14] != 0x00 + || poOpenInfo->pabyHeader[15] != 0x00 ) + return NULL; + + nRecordLen = CPL_LSBWORD32(((GInt32 *) poOpenInfo->pabyHeader)[0]); + nPixels = CPL_LSBWORD32(((GInt32 *) poOpenInfo->pabyHeader)[1]); + nLines = CPL_LSBWORD32(((GInt32 *) poOpenInfo->pabyHeader)[2]); + + if( nPixels < 1 || nLines < 1 || nPixels > 100000 || nLines > 100000 ) + return NULL; + + if( nRecordLen != nPixels * 4 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GSC driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + nRecordLen += 8; /* for record length markers */ + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GSCDataset *poDS; + + poDS = new GSCDataset(); + + poDS->nRasterXSize = nPixels; + poDS->nRasterYSize = nLines; + +/* -------------------------------------------------------------------- */ +/* Assume ownership of the file handled from the GDALOpenInfo. */ +/* -------------------------------------------------------------------- */ + poDS->fpImage = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + if (poDS->fpImage == NULL) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the header information in the second record. */ +/* -------------------------------------------------------------------- */ + float afHeaderInfo[8]; + + if( VSIFSeekL( poDS->fpImage, nRecordLen + 12, SEEK_SET ) != 0 + || VSIFReadL( afHeaderInfo, sizeof(float), 8, poDS->fpImage ) != 8 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failure reading second record of GSC file with %d record length.", + nRecordLen ); + delete poDS; + return NULL; + } + + for( i = 0; i < 8; i++ ) + { + CPL_LSBPTR32( afHeaderInfo + i ); + } + + poDS->adfGeoTransform[0] = afHeaderInfo[2]; + poDS->adfGeoTransform[1] = afHeaderInfo[0]; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = afHeaderInfo[5]; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -afHeaderInfo[1]; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + RawRasterBand *poBand; +#ifdef CPL_LSB + int bNative = TRUE; +#else + int bNative = FALSE; +#endif + + poBand = new RawRasterBand( poDS, 1, poDS->fpImage, + nRecordLen * 2 + 4, + sizeof(float), nRecordLen, + GDT_Float32, bNative, TRUE ); + poDS->SetBand( 1, poBand ); + + poBand->SetNoDataValue( -1.0000000150474662199e+30 ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_GSC() */ +/************************************************************************/ + +void GDALRegister_GSC() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GSC" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GSC" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GSC Geogrid" ); +// poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, +// "frmt_various.html#GSC" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = GSCDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/raw/gtxdataset.cpp b/bazaar/plugin/gdal/frmts/raw/gtxdataset.cpp new file mode 100644 index 000000000..b0efc30d3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/gtxdataset.cpp @@ -0,0 +1,415 @@ +/****************************************************************************** + * $Id: gtxdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: Vertical Datum Transformation + * Purpose: Implementation of NOAA .gtx vertical datum shift file format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: gtxdataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/** + +NOAA .GTX Vertical Datum Grid Shift Format + +All values are bigendian + +Header +------ + +float64 latitude_of_origin +float64 longitude_of_origin (0-360) +float64 cell size (x?y?) +float64 cell size (x?y?) +int32 length in pixels +int32 width in pixels + +Data +---- + +float32 * width in pixels * length in pixels + +Values are an offset in meters between two vertical datums. + +**/ + +/************************************************************************/ +/* ==================================================================== */ +/* GTXDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GTXDataset : public RawDataset +{ + public: + VSILFILE *fpImage; // image data file. + + double adfGeoTransform[6]; + + public: + GTXDataset(); + ~GTXDataset(); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual CPLErr SetGeoTransform( double * padfTransform ); + virtual const char *GetProjectionRef(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GTXDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GTXDataset() */ +/************************************************************************/ + +GTXDataset::GTXDataset() +{ + fpImage = NULL; +} + +/************************************************************************/ +/* ~GTXDataset() */ +/************************************************************************/ + +GTXDataset::~GTXDataset() + +{ + FlushCache(); + + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int GTXDataset::Identify( GDALOpenInfo *poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 40 ) + return FALSE; + + if( !EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"gtx") ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GTXDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + GTXDataset *poDS; + + poDS = new GTXDataset(); + + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb+" ); + + if( poDS->fpImage == NULL ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + + VSIFReadL( poDS->adfGeoTransform+3, 8, 1, poDS->fpImage ); + VSIFReadL( poDS->adfGeoTransform+0, 8, 1, poDS->fpImage ); + VSIFReadL( poDS->adfGeoTransform+5, 8, 1, poDS->fpImage ); + VSIFReadL( poDS->adfGeoTransform+1, 8, 1, poDS->fpImage ); + + VSIFReadL( &(poDS->nRasterYSize), 4, 1, poDS->fpImage ); + VSIFReadL( &(poDS->nRasterXSize), 4, 1, poDS->fpImage ); + + CPL_MSBPTR32( &(poDS->nRasterYSize) ); + CPL_MSBPTR32( &(poDS->nRasterXSize) ); + + CPL_MSBPTR64( poDS->adfGeoTransform + 0 ); + CPL_MSBPTR64( poDS->adfGeoTransform + 1 ); + CPL_MSBPTR64( poDS->adfGeoTransform + 3 ); + CPL_MSBPTR64( poDS->adfGeoTransform + 5 ); + + poDS->adfGeoTransform[3] += + poDS->adfGeoTransform[5] * (poDS->nRasterYSize-1); + + poDS->adfGeoTransform[0] -= poDS->adfGeoTransform[1] * 0.5; + poDS->adfGeoTransform[3] += poDS->adfGeoTransform[5] * 0.5; + + poDS->adfGeoTransform[5] *= -1; + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Guess the data type. Since October 1, 2009, it should be */ +/* Float32. Before it was double. */ +/* -------------------------------------------------------------------- */ + GDALDataType eDT = GDT_Float32; + VSIFSeekL(poDS->fpImage, 0, SEEK_END); + vsi_l_offset nSize = VSIFTellL(poDS->fpImage); + if( nSize == 40 + 8 * (vsi_l_offset)poDS->nRasterXSize * poDS->nRasterYSize ) + eDT = GDT_Float64; + int nDTSize = GDALGetDataTypeSize(eDT) / 8; + +/* -------------------------------------------------------------------- */ +/* Create band information object. */ +/* -------------------------------------------------------------------- */ + RawRasterBand *poBand = new RawRasterBand( poDS, 1, poDS->fpImage, + (poDS->nRasterYSize-1)*poDS->nRasterXSize*nDTSize + 40, + nDTSize, poDS->nRasterXSize * -nDTSize, + eDT, + !CPL_IS_LSB, TRUE, FALSE ); + if (eDT == GDT_Float64) + poBand->SetNoDataValue( -88.8888 ); + else + /* GDT_Float32 */ + poBand->SetNoDataValue( (double)-88.8888f ); + poDS->SetBand( 1, poBand ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GTXDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr GTXDataset::SetGeoTransform( double * padfTransform ) + +{ + if( padfTransform[2] != 0.0 || padfTransform[4] != 0.0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to write skewed or rotated geotransform to gtx." ); + return CE_Failure; + } + + memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 ); + + unsigned char header[32]; + double dfXOrigin, dfYOrigin, dfWidth, dfHeight; + + dfXOrigin = adfGeoTransform[0] + 0.5 * adfGeoTransform[1]; + dfYOrigin = adfGeoTransform[3] + (nRasterYSize-0.5) * adfGeoTransform[5]; + dfWidth = adfGeoTransform[1]; + dfHeight = - adfGeoTransform[5]; + + + memcpy( header + 0, &dfYOrigin, 8 ); + CPL_MSBPTR64( header + 0 ); + + memcpy( header + 8, &dfXOrigin, 8 ); + CPL_MSBPTR64( header + 8 ); + + memcpy( header + 16, &dfHeight, 8 ); + CPL_MSBPTR64( header + 16 ); + + memcpy( header + 24, &dfWidth, 8 ); + CPL_MSBPTR64( header + 24 ); + + if( VSIFSeekL( fpImage, SEEK_SET, 0 ) != 0 + || VSIFWriteL( header, 32, 1, fpImage ) != 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to write geotrasform header to gtx failed." ); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GTXDataset::GetProjectionRef() + +{ + return SRS_WKT_WGS84; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *GTXDataset::Create( const char * pszFilename, + int nXSize, + int nYSize, + CPL_UNUSED int nBands, + GDALDataType eType, + CPL_UNUSED char ** papszOptions ) +{ + if( eType != GDT_Float32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create gtx file with unsupported data type '%s'.", + GDALGetDataTypeName( eType ) ); + return NULL; + } + + if( !EQUAL(CPLGetExtension(pszFilename),"gtx") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create gtx file with extension other than gtx." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to create the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + + fp = VSIFOpenL( pszFilename, "wb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write out the header with stub georeferencing. */ +/* -------------------------------------------------------------------- */ + unsigned char header[40]; + double dfXOrigin=0, dfYOrigin=0, dfXSize=0.01, dfYSize=0.01; + GInt32 nXSize32 = nXSize, nYSize32 = nYSize; + + memcpy( header + 0, &dfYOrigin, 8 ); + CPL_MSBPTR64( header + 0 ); + + memcpy( header + 8, &dfXOrigin, 8 ); + CPL_MSBPTR64( header + 8 ); + + memcpy( header + 16, &dfYSize, 8 ); + CPL_MSBPTR64( header + 16 ); + + memcpy( header + 24, &dfXSize, 8 ); + CPL_MSBPTR64( header + 24 ); + + memcpy( header + 32, &nYSize32, 4 ); + CPL_MSBPTR32( header + 32 ); + memcpy( header + 36, &nXSize32, 4 ); + CPL_MSBPTR32( header + 36 ); + + VSIFWriteL( header, 40, 1, fp ); + VSIFCloseL( fp ); + + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + + +/************************************************************************/ +/* GDALRegister_GTX() */ +/************************************************************************/ + +void GDALRegister_GTX() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GTX" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GTX" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "NOAA Vertical Datum .GTX" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gtx" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); +// poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, +// "frmt_various.html#GTX" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Float32" ); + + poDriver->pfnOpen = GTXDataset::Open; + poDriver->pfnIdentify = GTXDataset::Identify; + poDriver->pfnCreate = GTXDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/hkvdataset.cpp b/bazaar/plugin/gdal/frmts/raw/hkvdataset.cpp new file mode 100644 index 000000000..5bb67cd1c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/hkvdataset.cpp @@ -0,0 +1,1928 @@ +/****************************************************************************** + * $Id: hkvdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: GView + * Purpose: Implementation of Atlantis HKV labelled blob support + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" +#include +#include "ogr_spatialref.h" +#include "atlsci_spheroid.h" + +CPL_CVSID("$Id: hkvdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_HKV(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* HKVRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class HKVDataset; + +class HKVRasterBand : public RawRasterBand +{ + friend class HKVDataset; + + public: + HKVRasterBand( HKVDataset *poDS, int nBand, VSILFILE * fpRaw, + unsigned int nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder ); + virtual ~HKVRasterBand(); + + virtual CPLErr SetNoDataValue( double ); +}; + +/************************************************************************/ +/* HKV Spheroids */ +/************************************************************************/ + +class HKVSpheroidList : public SpheroidList +{ + +public: + + HKVSpheroidList(); + ~HKVSpheroidList(); + +}; + +HKVSpheroidList :: HKVSpheroidList() +{ + num_spheroids = 58; + epsilonR = 0.1; + epsilonI = 0.000001; + + spheroids[0].SetValuesByEqRadiusAndInvFlattening("airy-1830",6377563.396,299.3249646); + spheroids[1].SetValuesByEqRadiusAndInvFlattening("modified-airy",6377340.189,299.3249646); + spheroids[2].SetValuesByEqRadiusAndInvFlattening("australian-national",6378160,298.25); + spheroids[3].SetValuesByEqRadiusAndInvFlattening("bessel-1841-namibia",6377483.865,299.1528128); + spheroids[4].SetValuesByEqRadiusAndInvFlattening("bessel-1841",6377397.155,299.1528128); + spheroids[5].SetValuesByEqRadiusAndInvFlattening("clarke-1858",6378294.0,294.297); + spheroids[6].SetValuesByEqRadiusAndInvFlattening("clarke-1866",6378206.4,294.9786982); + spheroids[7].SetValuesByEqRadiusAndInvFlattening("clarke-1880",6378249.145,293.465); + spheroids[8].SetValuesByEqRadiusAndInvFlattening("everest-india-1830",6377276.345,300.8017); + spheroids[9].SetValuesByEqRadiusAndInvFlattening("everest-sabah-sarawak",6377298.556,300.8017); + spheroids[10].SetValuesByEqRadiusAndInvFlattening("everest-india-1956",6377301.243,300.8017); + spheroids[11].SetValuesByEqRadiusAndInvFlattening("everest-malaysia-1969",6377295.664,300.8017); + spheroids[12].SetValuesByEqRadiusAndInvFlattening("everest-malay-sing",6377304.063,300.8017); + spheroids[13].SetValuesByEqRadiusAndInvFlattening("everest-pakistan",6377309.613,300.8017); + spheroids[14].SetValuesByEqRadiusAndInvFlattening("modified-fisher-1960",6378155,298.3); + spheroids[15].SetValuesByEqRadiusAndInvFlattening("helmert-1906",6378200,298.3); + spheroids[16].SetValuesByEqRadiusAndInvFlattening("hough-1960",6378270,297); + spheroids[17].SetValuesByEqRadiusAndInvFlattening("hughes",6378273.0,298.279); + spheroids[18].SetValuesByEqRadiusAndInvFlattening("indonesian-1974",6378160,298.247); + spheroids[19].SetValuesByEqRadiusAndInvFlattening("international-1924",6378388,297); + spheroids[20].SetValuesByEqRadiusAndInvFlattening("iugc-67",6378160.0,298.254); + spheroids[21].SetValuesByEqRadiusAndInvFlattening("iugc-75",6378140.0,298.25298); + spheroids[22].SetValuesByEqRadiusAndInvFlattening("krassovsky-1940",6378245,298.3); + spheroids[23].SetValuesByEqRadiusAndInvFlattening("kaula",6378165.0,292.308); + spheroids[24].SetValuesByEqRadiusAndInvFlattening("grs-80",6378137,298.257222101); + spheroids[25].SetValuesByEqRadiusAndInvFlattening("south-american-1969",6378160,298.25); + spheroids[26].SetValuesByEqRadiusAndInvFlattening("wgs-72",6378135,298.26); + spheroids[27].SetValuesByEqRadiusAndInvFlattening("wgs-84",6378137,298.257223563); + spheroids[28].SetValuesByEqRadiusAndInvFlattening("ev-wgs-84",6378137.0,298.252841); + spheroids[29].SetValuesByEqRadiusAndInvFlattening("ev-bessel",6377397.0,299.1976073); + + spheroids[30].SetValuesByEqRadiusAndInvFlattening("airy_1830",6377563.396,299.3249646); + spheroids[31].SetValuesByEqRadiusAndInvFlattening("modified_airy",6377340.189,299.3249646); + spheroids[32].SetValuesByEqRadiusAndInvFlattening("australian_national",6378160,298.25); + spheroids[33].SetValuesByEqRadiusAndInvFlattening("bessel_1841_namibia",6377483.865,299.1528128); + spheroids[34].SetValuesByEqRadiusAndInvFlattening("bessel_1841",6377397.155,299.1528128); + spheroids[35].SetValuesByEqRadiusAndInvFlattening("clarke_1858",6378294.0,294.297); + spheroids[36].SetValuesByEqRadiusAndInvFlattening("clarke_1866",6378206.4,294.9786982); + spheroids[37].SetValuesByEqRadiusAndInvFlattening("clarke_1880",6378249.145,293.465); + spheroids[38].SetValuesByEqRadiusAndInvFlattening("everest_india_1830",6377276.345,300.8017); + spheroids[39].SetValuesByEqRadiusAndInvFlattening("everest_sabah_sarawak",6377298.556,300.8017); + spheroids[40].SetValuesByEqRadiusAndInvFlattening("everest_india_1956",6377301.243,300.8017); + spheroids[41].SetValuesByEqRadiusAndInvFlattening("everest_malaysia_1969",6377295.664,300.8017); + spheroids[42].SetValuesByEqRadiusAndInvFlattening("everest_malay_sing",6377304.063,300.8017); + spheroids[43].SetValuesByEqRadiusAndInvFlattening("everest_pakistan",6377309.613,300.8017); + spheroids[44].SetValuesByEqRadiusAndInvFlattening("modified_fisher_1960",6378155,298.3); + spheroids[45].SetValuesByEqRadiusAndInvFlattening("helmert_1906",6378200,298.3); + spheroids[46].SetValuesByEqRadiusAndInvFlattening("hough_1960",6378270,297); + spheroids[47].SetValuesByEqRadiusAndInvFlattening("indonesian_1974",6378160,298.247); + spheroids[48].SetValuesByEqRadiusAndInvFlattening("international_1924",6378388,297); + spheroids[49].SetValuesByEqRadiusAndInvFlattening("iugc_67",6378160.0,298.254); + spheroids[50].SetValuesByEqRadiusAndInvFlattening("iugc_75",6378140.0,298.25298); + spheroids[51].SetValuesByEqRadiusAndInvFlattening("krassovsky_1940",6378245,298.3); + spheroids[52].SetValuesByEqRadiusAndInvFlattening("grs_80",6378137,298.257222101); + spheroids[53].SetValuesByEqRadiusAndInvFlattening("south_american_1969",6378160,298.25); + spheroids[54].SetValuesByEqRadiusAndInvFlattening("wgs_72",6378135,298.26); + spheroids[55].SetValuesByEqRadiusAndInvFlattening("wgs_84",6378137,298.257223563); + spheroids[56].SetValuesByEqRadiusAndInvFlattening("ev_wgs_84",6378137.0,298.252841); + spheroids[57].SetValuesByEqRadiusAndInvFlattening("ev_bessel",6377397.0,299.1976073); + +} + +HKVSpheroidList::~HKVSpheroidList() + +{ +} + +CPLErr SaveHKVAttribFile( const char *pszFilenameIn, + int nXSize, int nYSize, int nBands, + GDALDataType eType, int bNoDataSet, + double dfNoDataValue ); + +/************************************************************************/ +/* ==================================================================== */ +/* HKVDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class HKVDataset : public RawDataset +{ + friend class HKVRasterBand; + + char *pszPath; + VSILFILE *fpBlob; + + int nGCPCount; + GDAL_GCP *pasGCPList; + + void ProcessGeoref(const char *); + void ProcessGeorefGCP(char **, const char *, double, double); + void SetVersion( float version_number ); + float GetVersion(); + float MFF2version; + + CPLErr SetGCPProjection(const char *); /* for use in CreateCopy */ + + GDALDataType eRasterType; + + void SetNoDataValue( double ); + + char *pszProjection; + char *pszGCPProjection; + double adfGeoTransform[6]; + + char **papszAttrib; + + int bGeorefChanged; + char **papszGeoref; + + /* NOTE: The MFF2 format goes against GDAL's API in that nodata values are set + * per-dataset rather than per-band. To compromise, for writing out, the + * dataset's nodata value will be set to the last value set on any of the + * raster bands. + */ + + int bNoDataSet; + int bNoDataChanged; + double dfNoDataValue; + + public: + HKVDataset(); + virtual ~HKVDataset(); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * ); + + virtual CPLErr SetGeoTransform( double * ); + virtual CPLErr SetProjection( const char * ); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); + static GDALDataset *CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + + static CPLErr Delete( const char * pszName ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* HKVRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* HKVRasterBand() */ +/************************************************************************/ + +HKVRasterBand::HKVRasterBand( HKVDataset *poDS, int nBand, VSILFILE * fpRaw, + unsigned int nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder ) + : RawRasterBand( (GDALDataset *) poDS, nBand, + fpRaw, nImgOffset, nPixelOffset, + nLineOffset, eDataType, bNativeOrder, TRUE ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr HKVRasterBand::SetNoDataValue( double dfNewValue ) + +{ + HKVDataset *poHKVDS = (HKVDataset *) poDS; + this->RawRasterBand::SetNoDataValue( dfNewValue ); + poHKVDS->SetNoDataValue( dfNewValue ); + + return CE_None; +} + +/************************************************************************/ +/* ~HKVRasterBand() */ +/************************************************************************/ + +HKVRasterBand::~HKVRasterBand() + +{ +} + +/************************************************************************/ +/* ==================================================================== */ +/* HKVDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* HKVDataset() */ +/************************************************************************/ + +HKVDataset::HKVDataset() +{ + pszPath = NULL; + papszAttrib = NULL; + papszGeoref = NULL; + bGeorefChanged = FALSE; + + nGCPCount = 0; + pasGCPList = NULL; + pszProjection = CPLStrdup(""); + pszGCPProjection = CPLStrdup(""); + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + bNoDataSet = FALSE; + bNoDataChanged = FALSE; + + /* Initialize datasets to new version; change if necessary */ + MFF2version = (float) 1.1; +} + +/************************************************************************/ +/* ~HKVDataset() */ +/************************************************************************/ + +HKVDataset::~HKVDataset() + +{ + FlushCache(); + if( bGeorefChanged ) + { + const char *pszFilename; + + pszFilename = CPLFormFilename(pszPath, "georef", NULL ); + + CSLSave( papszGeoref, pszFilename ); + } + + if( bNoDataChanged ) + { + SaveHKVAttribFile(pszPath, + this->nRasterXSize, + this->nRasterYSize, + this->nBands, + this->eRasterType, + this->bNoDataSet, + this->dfNoDataValue ); + + } + + if( fpBlob != NULL ) + VSIFCloseL( fpBlob ); + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + CPLFree( pszProjection ); + CPLFree( pszGCPProjection ); + CPLFree( pszPath ); + CSLDestroy( papszGeoref ); + CSLDestroy( papszAttrib ); +} + +/************************************************************************/ +/* SetVersion() */ +/************************************************************************/ + +void HKVDataset::SetVersion(float version_number) + +{ + //update stored info + MFF2version = version_number; +} + +/************************************************************************/ +/* GetVersion() */ +/************************************************************************/ + +float HKVDataset::GetVersion() + +{ + return( MFF2version ); +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +void HKVDataset::SetNoDataValue( double dfNewValue ) + +{ + + this->bNoDataSet = TRUE; + this->bNoDataChanged = TRUE; + this->dfNoDataValue = dfNewValue; +} + +/************************************************************************/ +/* SaveHKVAttribFile() */ +/************************************************************************/ + +CPLErr SaveHKVAttribFile( const char *pszFilenameIn, + int nXSize, int nYSize, int nBands, + GDALDataType eType, int bNoDataSet, + double dfNoDataValue ) + +{ + + FILE *fp; + const char *pszFilename; + + pszFilename = CPLFormFilename( pszFilenameIn, "attrib", NULL ); + + fp = VSIFOpen( pszFilename, "wt" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Couldn't create %s.\n", pszFilename ); + return CE_Failure; + } + + fprintf( fp, "channel.enumeration = %d\n", nBands ); + fprintf( fp, "channel.interleave = { *pixel tile sequential }\n" ); + fprintf( fp, "extent.cols = %d\n", nXSize ); + fprintf( fp, "extent.rows = %d\n", nYSize ); + + switch( eType ) + { + case GDT_Byte: + fprintf( fp, "pixel.encoding = " + "{ *unsigned twos-complement ieee-754 }\n" ); + break; + + case GDT_UInt16: + fprintf( fp, "pixel.encoding = " + "{ *unsigned twos-complement ieee-754 }\n" ); + break; + + case GDT_CInt16: + case GDT_Int16: + fprintf( fp, "pixel.encoding = " + "{ unsigned *twos-complement ieee-754 }\n" ); + break; + + case GDT_CFloat32: + case GDT_Float32: + fprintf( fp, "pixel.encoding = " + "{ unsigned twos-complement *ieee-754 }\n" ); + break; + + default: + CPLAssert( FALSE ); + } + + fprintf( fp, "pixel.size = %d\n", GDALGetDataTypeSize(eType) ); + if( GDALDataTypeIsComplex( eType ) ) + fprintf( fp, "pixel.field = { real *complex }\n" ); + else + fprintf( fp, "pixel.field = { *real complex }\n" ); + +#ifdef CPL_MSB + fprintf( fp, "pixel.order = { lsbf *msbf }\n" ); +#else + fprintf( fp, "pixel.order = { *lsbf msbf }\n" ); +#endif + + if ( bNoDataSet ) + fprintf( fp, "pixel.no_data = %s\n", CPLSPrintf("%f", dfNoDataValue) ); + + /* version information- only create the new style */ + fprintf( fp, "version = 1.1"); + + + VSIFClose( fp ); + return CE_None; +} + + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *HKVDataset::GetProjectionRef() + +{ + return( pszProjection ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr HKVDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return( CE_None ); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr HKVDataset::SetGeoTransform( double * padfTransform ) + +{ + char szValue[128]; + + /* NOTE: Geotransform coordinates must match the current projection */ + /* of the dataset being changed (not the geotransform source). */ + /* ie. be in lat/longs for LL projected; UTM for UTM projected. */ + /* SET PROJECTION BEFORE SETTING GEOTRANSFORM TO AVOID SYNCHRONIZATION */ + /* PROBLEMS! */ + + /* Update the geotransform itself */ + memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 ); + + /* Clear previous gcps */ + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + nGCPCount = 0; + pasGCPList = NULL; + + /* Return if the identity transform is set */ + if (adfGeoTransform[0] == 0.0 && adfGeoTransform[1] == 1.0 + && adfGeoTransform[2] == 0.0 && adfGeoTransform[3] == 0.0 + && adfGeoTransform[4] == 0.0 && adfGeoTransform[5] == 1.0 ) + return CE_None; + + /* Update georef text info for saving later, and */ + /* update GCPs to match geotransform. */ + + double temp_lat, temp_long; + OGRSpatialReference oUTM; + OGRSpatialReference oLL; + OGRCoordinateTransformation *poTransform = NULL; + int bSuccess=TRUE; + char *pszPtemp; + char *pszGCPtemp; + + /* Projection parameter checking will have been done */ + /* in SetProjection. */ + if(( CSLFetchNameValue( papszGeoref, "projection.name" ) != NULL ) && + ( EQUAL(CSLFetchNameValue( papszGeoref, "projection.name" ),"UTM" ))) + + { + /* pass copies of projection info, not originals (pointers */ + /* get updated by importFromWkt) */ + pszPtemp = CPLStrdup(pszProjection); + oUTM.importFromWkt(&pszPtemp); + (oUTM.GetAttrNode("GEOGCS"))->exportToWkt(&pszGCPtemp); + oLL.importFromWkt(&pszGCPtemp); + poTransform = OGRCreateCoordinateTransformation( &oUTM, &oLL ); + if( poTransform == NULL ) + { + bSuccess = FALSE; + CPLErrorReset(); + } + } + else if ((( CSLFetchNameValue( papszGeoref, "projection.name" ) != NULL ) && + ( !EQUAL(CSLFetchNameValue( papszGeoref, "projection.name" ),"LL" ))) || + (CSLFetchNameValue( papszGeoref, "projection.name" ) == NULL )) + { + return CE_Failure; + } + + nGCPCount = 0; + pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),5); + + /* -------------------------------------------------------------------- */ + /* top left */ + /* -------------------------------------------------------------------- */ + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + CPLFree( pasGCPList[nGCPCount].pszId ); + pasGCPList[nGCPCount].pszId = CPLStrdup( "top_left" ); + + if (MFF2version > 1.0) + { + temp_lat = padfTransform[3]; + temp_long = padfTransform[0]; + pasGCPList[nGCPCount].dfGCPPixel = 0.0; + pasGCPList[nGCPCount].dfGCPLine = 0.0; + } + else + { + temp_lat = padfTransform[3] + 0.5 * padfTransform[4] + 0.5 * padfTransform[5]; + temp_long = padfTransform[0] + 0.5 * padfTransform[1]+ 0.5 * padfTransform[2]; + pasGCPList[nGCPCount].dfGCPPixel = 0.5; + pasGCPList[nGCPCount].dfGCPLine = 0.5; + } + pasGCPList[nGCPCount].dfGCPX = temp_long; + pasGCPList[nGCPCount].dfGCPY = temp_lat; + pasGCPList[nGCPCount].dfGCPZ = 0.0; + nGCPCount++; + + if (poTransform != NULL) + { + if( !bSuccess || !poTransform->Transform( 1, &temp_long, &temp_lat ) ) + bSuccess = FALSE; + } + + if (bSuccess) + { + CPLsprintf( szValue, "%.10f", temp_lat ); + papszGeoref = CSLSetNameValue( papszGeoref, "top_left.latitude", + szValue ); + + CPLsprintf( szValue, "%.10f", temp_long ); + papszGeoref = CSLSetNameValue( papszGeoref, "top_left.longitude", + szValue ); + } + + /* -------------------------------------------------------------------- */ + /* top_right */ + /* -------------------------------------------------------------------- */ + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + CPLFree( pasGCPList[nGCPCount].pszId ); + pasGCPList[nGCPCount].pszId = CPLStrdup( "top_right" ); + + if (MFF2version > 1.0) + { + temp_lat = padfTransform[3] + GetRasterXSize() * padfTransform[4]; + temp_long = padfTransform[0] + GetRasterXSize() * padfTransform[1]; + pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize(); + pasGCPList[nGCPCount].dfGCPLine = 0.0; + } + else + { + temp_lat = padfTransform[3] + (GetRasterXSize()-0.5) * padfTransform[4] + 0.5 * padfTransform[5]; + temp_long = padfTransform[0] + (GetRasterXSize()-0.5) * padfTransform[1] + 0.5 * padfTransform[2]; + pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize()-0.5; + pasGCPList[nGCPCount].dfGCPLine = 0.5; + } + pasGCPList[nGCPCount].dfGCPX = temp_long; + pasGCPList[nGCPCount].dfGCPY = temp_lat; + pasGCPList[nGCPCount].dfGCPZ = 0.0; + nGCPCount++; + + if (poTransform != NULL) + { + if( !bSuccess || !poTransform->Transform( 1, &temp_long, &temp_lat ) ) + bSuccess = FALSE; + } + + if (bSuccess) + { + CPLsprintf( szValue, "%.10f", temp_lat ); + papszGeoref = CSLSetNameValue( papszGeoref, "top_right.latitude", + szValue ); + + CPLsprintf( szValue, "%.10f", temp_long ); + papszGeoref = CSLSetNameValue( papszGeoref, "top_right.longitude", + szValue ); + } + + /* -------------------------------------------------------------------- */ + /* bottom_left */ + /* -------------------------------------------------------------------- */ + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + CPLFree( pasGCPList[nGCPCount].pszId ); + pasGCPList[nGCPCount].pszId = CPLStrdup( "bottom_left" ); + + if (MFF2version > 1.0) + { + temp_lat = padfTransform[3] + GetRasterYSize() * padfTransform[5]; + temp_long = padfTransform[0] + GetRasterYSize() * padfTransform[2]; + pasGCPList[nGCPCount].dfGCPPixel = 0.0; + pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize(); + } + else + { + temp_lat = padfTransform[3] + 0.5 * padfTransform[4] + (GetRasterYSize()-0.5) * padfTransform[5]; + temp_long = padfTransform[0] + 0.5 * padfTransform[1] + (GetRasterYSize()-0.5) * padfTransform[2]; + pasGCPList[nGCPCount].dfGCPPixel = 0.5; + pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize()-0.5; + } + pasGCPList[nGCPCount].dfGCPX = temp_long; + pasGCPList[nGCPCount].dfGCPY = temp_lat; + pasGCPList[nGCPCount].dfGCPZ = 0.0; + nGCPCount++; + + if (poTransform != NULL) + { + if( !bSuccess || !poTransform->Transform( 1, &temp_long, &temp_lat ) ) + bSuccess = FALSE; + } + + if (bSuccess) + { + CPLsprintf( szValue, "%.10f", temp_lat ); + papszGeoref = CSLSetNameValue( papszGeoref, "bottom_left.latitude", + szValue ); + + CPLsprintf( szValue, "%.10f", temp_long ); + papszGeoref = CSLSetNameValue( papszGeoref, "bottom_left.longitude", + szValue ); + } + + /* -------------------------------------------------------------------- */ + /* bottom_right */ + /* -------------------------------------------------------------------- */ + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + CPLFree( pasGCPList[nGCPCount].pszId ); + pasGCPList[nGCPCount].pszId = CPLStrdup( "bottom_right" ); + + if (MFF2version > 1.0) + { + temp_lat = padfTransform[3] + GetRasterXSize() * padfTransform[4] + + GetRasterYSize() * padfTransform[5]; + temp_long = padfTransform[0] + GetRasterXSize() * padfTransform[1] + + GetRasterYSize() * padfTransform[2]; + pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize(); + pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize(); + + } + else + { + temp_lat = padfTransform[3] + (GetRasterXSize()-0.5) * padfTransform[4] + + (GetRasterYSize()-0.5) * padfTransform[5]; + temp_long = padfTransform[0] + (GetRasterXSize()-0.5) * padfTransform[1] + + (GetRasterYSize()-0.5) * padfTransform[2]; + pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize()-0.5; + pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize()-0.5; + } + pasGCPList[nGCPCount].dfGCPX = temp_long; + pasGCPList[nGCPCount].dfGCPY = temp_lat; + pasGCPList[nGCPCount].dfGCPZ = 0.0; + nGCPCount++; + + if (poTransform != NULL) + { + if( !bSuccess || !poTransform->Transform( 1, &temp_long, &temp_lat ) ) + bSuccess = FALSE; + } + + if (bSuccess) + { + CPLsprintf( szValue, "%.10f", temp_lat ); + papszGeoref = CSLSetNameValue( papszGeoref, "bottom_right.latitude", + szValue ); + + CPLsprintf( szValue, "%.10f", temp_long ); + papszGeoref = CSLSetNameValue( papszGeoref, "bottom_right.longitude", + szValue ); + } + + /* -------------------------------------------------------------------- */ + /* Center */ + /* -------------------------------------------------------------------- */ + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + CPLFree( pasGCPList[nGCPCount].pszId ); + pasGCPList[nGCPCount].pszId = CPLStrdup( "centre" ); + + if (MFF2version > 1.0) + { + temp_lat = padfTransform[3] + GetRasterXSize() * padfTransform[4] * 0.5 + + GetRasterYSize() * padfTransform[5] * 0.5; + temp_long = padfTransform[0] + GetRasterXSize() * padfTransform[1] * 0.5 + + GetRasterYSize() * padfTransform[2] * 0.5; + pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize()/2.0; + pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize()/2.0; + } + else + { + temp_lat = padfTransform[3] + GetRasterXSize() * padfTransform[4] * 0.5 + + GetRasterYSize() * padfTransform[5] * 0.5; + temp_long = padfTransform[0] + GetRasterXSize() * padfTransform[1] * 0.5 + + GetRasterYSize() * padfTransform[2] * 0.5; + pasGCPList[nGCPCount].dfGCPPixel = GetRasterXSize()/2.0; + pasGCPList[nGCPCount].dfGCPLine = GetRasterYSize()/2.0; + } + pasGCPList[nGCPCount].dfGCPX = temp_long; + pasGCPList[nGCPCount].dfGCPY = temp_lat; + pasGCPList[nGCPCount].dfGCPZ = 0.0; + nGCPCount++; + + if (poTransform != NULL) + { + if( !bSuccess || !poTransform->Transform( 1, &temp_long, &temp_lat ) ) + bSuccess = FALSE; + } + + if (bSuccess) + { + CPLsprintf( szValue, "%.10f", temp_lat ); + papszGeoref = CSLSetNameValue( papszGeoref, "centre.latitude", + szValue ); + + CPLsprintf( szValue, "%.10f", temp_long ); + papszGeoref = CSLSetNameValue( papszGeoref, "centre.longitude", + szValue ); + } + + if (!bSuccess) + { + CPLError(CE_Warning,CPLE_AppDefined, + "Warning- error setting header info in SetGeoTransform. Changes may not be saved properly.\n"); + } + + if (poTransform != NULL) + delete poTransform; + + + + bGeorefChanged = TRUE; + + return( CE_None ); +} + +CPLErr HKVDataset::SetGCPProjection( const char *pszNewProjection ) +{ + + CPLFree( pszGCPProjection ); + this->pszGCPProjection = CPLStrdup(pszNewProjection); + + return CE_None; +} + +/************************************************************************/ +/* SetProjection() */ +/* */ +/* We provide very limited support for setting the projection. */ +/************************************************************************/ + +CPLErr HKVDataset::SetProjection( const char * pszNewProjection ) + +{ + HKVSpheroidList *hkvEllipsoids; + double eq_radius, inv_flattening; + OGRErr ogrerrorEq=OGRERR_NONE; + OGRErr ogrerrorInvf=OGRERR_NONE; + OGRErr ogrerrorOl=OGRERR_NONE; + + char *spheroid_name = NULL; + + /* This function is used to update a georef file */ + + + /* printf( "HKVDataset::SetProjection(%s)\n", pszNewProjection ); */ + + if( !EQUALN(pszNewProjection,"GEOGCS",6) + && !EQUALN(pszNewProjection,"PROJCS",6) + && !EQUAL(pszNewProjection,"") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Only OGC WKT Projections supported for writing to HKV.\n" + "%s not supported.", + pszNewProjection ); + + return CE_Failure; + } + else if (EQUAL(pszNewProjection,"")) + { + CPLFree( pszProjection ); + pszProjection = (char *) CPLStrdup(pszNewProjection); + + return CE_None; + } + CPLFree( pszProjection ); + pszProjection = (char *) CPLStrdup(pszNewProjection); + + + OGRSpatialReference oSRS(pszNewProjection); + + if ((oSRS.GetAttrValue("PROJECTION") != NULL) && + (EQUAL(oSRS.GetAttrValue("PROJECTION"),SRS_PT_TRANSVERSE_MERCATOR))) + { + char *ol_txt; + ol_txt=(char *) CPLMalloc(255); + papszGeoref = CSLSetNameValue( papszGeoref, "projection.name", "utm" ); + CPLsprintf(ol_txt,"%f",oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0,&ogrerrorOl)); + papszGeoref = CSLSetNameValue( papszGeoref, "projection.origin_longitude", + ol_txt ); + CPLFree(ol_txt); + } + else if ((oSRS.GetAttrValue("PROJECTION") == NULL) && (oSRS.IsGeographic())) + { + papszGeoref = CSLSetNameValue( papszGeoref, "projection.name", "LL" ); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unrecognized projection."); + return CE_Failure; + } + eq_radius = oSRS.GetSemiMajor(&ogrerrorEq); + inv_flattening = oSRS.GetInvFlattening(&ogrerrorInvf); + if ((ogrerrorEq == OGRERR_NONE) && (ogrerrorInvf == OGRERR_NONE)) + { + hkvEllipsoids = new HKVSpheroidList; + spheroid_name = hkvEllipsoids->GetSpheroidNameByEqRadiusAndInvFlattening(eq_radius,inv_flattening); + if (spheroid_name != NULL) + { + papszGeoref = CSLSetNameValue( papszGeoref, "spheroid.name", + spheroid_name ); + } + CPLFree(spheroid_name); + delete hkvEllipsoids; + } + else + { + /* default to previous behaviour if spheroid not found by */ + /* radius and inverse flattening */ + + if( strstr(pszNewProjection,"Bessel") != NULL ) + { + papszGeoref = CSLSetNameValue( papszGeoref, "spheroid.name", + "ev-bessel" ); + } + else + { + papszGeoref = CSLSetNameValue( papszGeoref, "spheroid.name", + "ev-wgs-84" ); + } + } + bGeorefChanged = TRUE; + return CE_None; +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int HKVDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *HKVDataset::GetGCPProjection() + +{ + return pszGCPProjection; +} + +/************************************************************************/ +/* GetGCP() */ +/************************************************************************/ + +const GDAL_GCP *HKVDataset::GetGCPs() + +{ + return pasGCPList; +} + +/************************************************************************/ +/* ProcessGeorefGCP() */ +/************************************************************************/ + +void HKVDataset::ProcessGeorefGCP( char **papszGeoref, const char *pszBase, + double dfRasterX, double dfRasterY ) + +{ + char szFieldName[128]; + double dfLat, dfLong; + +/* -------------------------------------------------------------------- */ +/* Fetch the GCP from the string list. */ +/* -------------------------------------------------------------------- */ + sprintf( szFieldName, "%s.latitude", pszBase ); + if( CSLFetchNameValue(papszGeoref, szFieldName) == NULL ) + return; + else + dfLat = CPLAtof(CSLFetchNameValue(papszGeoref, szFieldName)); + + sprintf( szFieldName, "%s.longitude", pszBase ); + if( CSLFetchNameValue(papszGeoref, szFieldName) == NULL ) + return; + else + dfLong = CPLAtof(CSLFetchNameValue(papszGeoref, szFieldName)); + +/* -------------------------------------------------------------------- */ +/* Add the gcp to the internal list. */ +/* -------------------------------------------------------------------- */ + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + + CPLFree( pasGCPList[nGCPCount].pszId ); + + pasGCPList[nGCPCount].pszId = CPLStrdup( pszBase ); + + pasGCPList[nGCPCount].dfGCPX = dfLong; + pasGCPList[nGCPCount].dfGCPY = dfLat; + pasGCPList[nGCPCount].dfGCPZ = 0.0; + + pasGCPList[nGCPCount].dfGCPPixel = dfRasterX; + pasGCPList[nGCPCount].dfGCPLine = dfRasterY; + + nGCPCount++; +} + +/************************************************************************/ +/* ProcessGeoref() */ +/************************************************************************/ + +void HKVDataset::ProcessGeoref( const char * pszFilename ) + +{ + int i; + HKVSpheroidList *hkvEllipsoids = NULL; + +/* -------------------------------------------------------------------- */ +/* Load the georef file, and boil white space away from around */ +/* the equal sign. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( papszGeoref ); + papszGeoref = CSLLoad( pszFilename ); + if( papszGeoref == NULL ) + return; + + hkvEllipsoids = new HKVSpheroidList; + + for( i = 0; papszGeoref[i] != NULL; i++ ) + { + int bAfterEqual = FALSE; + int iSrc, iDst; + char *pszLine = papszGeoref[i]; + + for( iSrc=0, iDst=0; pszLine[iSrc] != '\0'; iSrc++ ) + { + if( bAfterEqual || pszLine[iSrc] != ' ' ) + { + pszLine[iDst++] = pszLine[iSrc]; + } + + if( iDst > 0 && pszLine[iDst-1] == '=' ) + bAfterEqual = FALSE; + } + pszLine[iDst] = '\0'; + } + +/* -------------------------------------------------------------------- */ +/* Try to get GCPs, in lat/longs . */ +/* -------------------------------------------------------------------- */ + nGCPCount = 0; + pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),5); + + if (MFF2version > 1.0) + { + ProcessGeorefGCP( papszGeoref, "top_left", + 0, 0 ); + ProcessGeorefGCP( papszGeoref, "top_right", + GetRasterXSize(), 0 ); + ProcessGeorefGCP( papszGeoref, "bottom_left", + 0, GetRasterYSize() ); + ProcessGeorefGCP( papszGeoref, "bottom_right", + GetRasterXSize(), GetRasterYSize() ); + ProcessGeorefGCP( papszGeoref, "centre", + GetRasterXSize()/2.0, GetRasterYSize()/2.0 ); + } + else + { + ProcessGeorefGCP( papszGeoref, "top_left", + 0.5, 0.5 ); + ProcessGeorefGCP( papszGeoref, "top_right", + GetRasterXSize()-0.5, 0.5 ); + ProcessGeorefGCP( papszGeoref, "bottom_left", + 0.5, GetRasterYSize()-0.5 ); + ProcessGeorefGCP( papszGeoref, "bottom_right", + GetRasterXSize()-0.5, GetRasterYSize()-0.5 ); + ProcessGeorefGCP( papszGeoref, "centre", + GetRasterXSize()/2.0, GetRasterYSize()/2.0 ); + } + + if (nGCPCount == 0) + { + CPLFree(pasGCPList); + pasGCPList = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we have a recognised projection? */ +/* -------------------------------------------------------------------- */ + const char *pszProjName, *pszOriginLong, *pszSpheroidName; + /* double eq_radius, inv_flattening; */ + + pszProjName = CSLFetchNameValue(papszGeoref, + "projection.name"); + pszOriginLong = CSLFetchNameValue(papszGeoref, + "projection.origin_longitude"); + pszSpheroidName = CSLFetchNameValue(papszGeoref, + "spheroid.name"); + + + if ((pszSpheroidName != NULL) && (hkvEllipsoids->SpheroidInList(pszSpheroidName))) + { + /* eq_radius=hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName); */ + /* inv_flattening=hkvEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName); */ + } + else if (pszProjName != NULL) + { + CPLError(CE_Warning,CPLE_AppDefined,"Warning- unrecognized ellipsoid. Using wgs-84 parameters.\n"); + /* eq_radius=hkvEllipsoids->GetSpheroidEqRadius("wgs-84"); */ + /* inv_flattening=hkvEllipsoids->GetSpheroidInverseFlattening("wgs-84"); */ + } + + if( (pszProjName != NULL) && EQUAL(pszProjName,"utm") && (nGCPCount == 5) ) + { + /*int nZone = (int)((CPLAtof(pszOriginLong)+184.5) / 6.0); */ + int nZone; + + if (pszOriginLong == NULL) + { + /* If origin not specified, assume 0.0 */ + CPLError(CE_Warning,CPLE_AppDefined, + "Warning- no projection origin longitude specified. Assuming 0.0."); + nZone = 31; + } + else + nZone = 31 + (int) floor(CPLAtof(pszOriginLong)/6.0); + + OGRSpatialReference oUTM; + OGRSpatialReference oLL; + OGRCoordinateTransformation *poTransform = NULL; + double dfUtmX[5], dfUtmY[5]; + int gcp_index; + + int bSuccess = TRUE; + + if( pasGCPList[4].dfGCPY < 0 ) + oUTM.SetUTM( nZone, 0 ); + else + oUTM.SetUTM( nZone, 1 ); + + if (pszOriginLong != NULL) + { + oUTM.SetProjParm(SRS_PP_CENTRAL_MERIDIAN,CPLAtof(pszOriginLong)); + oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN,CPLAtof(pszOriginLong)); + } + + if ((pszSpheroidName == NULL) || (EQUAL(pszSpheroidName,"wgs-84")) || + (EQUAL(pszSpheroidName,"wgs_84"))) + { + oUTM.SetWellKnownGeogCS( "WGS84" ); + oLL.SetWellKnownGeogCS( "WGS84" ); + } + else + { + if (hkvEllipsoids->SpheroidInList(pszSpheroidName)) + { + oUTM.SetGeogCS( "unknown","unknown",pszSpheroidName, + hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName), + hkvEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName) + ); + oLL.SetGeogCS( "unknown","unknown",pszSpheroidName, + hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName), + hkvEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName) + ); + } + else + { + CPLError(CE_Warning,CPLE_AppDefined,"Warning- unrecognized ellipsoid. Using wgs-84 parameters.\n"); + oUTM.SetWellKnownGeogCS( "WGS84" ); + oLL.SetWellKnownGeogCS( "WGS84" ); + } + } + + poTransform = OGRCreateCoordinateTransformation( &oLL, &oUTM ); + if( poTransform == NULL ) + { + CPLErrorReset(); + bSuccess = FALSE; + } + + for(gcp_index=0;gcp_index<5;gcp_index++) + { + dfUtmX[gcp_index] = pasGCPList[gcp_index].dfGCPX; + dfUtmY[gcp_index] = pasGCPList[gcp_index].dfGCPY; + + if( bSuccess && !poTransform->Transform( 1, &(dfUtmX[gcp_index]), &(dfUtmY[gcp_index]) ) ) + bSuccess = FALSE; + + } + + if( bSuccess ) + { + int transform_ok = FALSE; + + /* update GCPS to proper projection */ + for(gcp_index=0;gcp_index<5;gcp_index++) + { + pasGCPList[gcp_index].dfGCPX = dfUtmX[gcp_index]; + pasGCPList[gcp_index].dfGCPY = dfUtmY[gcp_index]; + } + + CPLFree( pszGCPProjection ); + pszGCPProjection = NULL; + oUTM.exportToWkt( &pszGCPProjection ); + + transform_ok = GDALGCPsToGeoTransform(5,pasGCPList,adfGeoTransform,0); + + CPLFree( pszProjection ); + pszProjection = NULL; + if (transform_ok == FALSE) + { + /* transform may not be sufficient in all cases (slant range projection) */ + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + pszProjection = CPLStrdup(""); + } + else + oUTM.exportToWkt( &pszProjection ); + + } + + if( poTransform != NULL ) + delete poTransform; + } + else if ((pszProjName != NULL) && (nGCPCount == 5)) + { + OGRSpatialReference oLL; + int transform_ok = FALSE; + + + if (pszOriginLong != NULL) + { + oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN,CPLAtof(pszOriginLong)); + } + + if ((pszSpheroidName == NULL) || (EQUAL(pszSpheroidName,"wgs-84")) || + (EQUAL(pszSpheroidName,"wgs_84"))) + { + oLL.SetWellKnownGeogCS( "WGS84" ); + } + else + { + if (hkvEllipsoids->SpheroidInList(pszSpheroidName)) + { + oLL.SetGeogCS( "","",pszSpheroidName, + hkvEllipsoids->GetSpheroidEqRadius(pszSpheroidName), + hkvEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName) + ); + } + else + { + CPLError(CE_Warning,CPLE_AppDefined,"Warning- unrecognized ellipsoid. Using wgs-84 parameters.\n"); + oLL.SetWellKnownGeogCS( "WGS84" ); + } + } + + transform_ok = GDALGCPsToGeoTransform(5,pasGCPList,adfGeoTransform,0); + + CPLFree( pszProjection ); + pszProjection = NULL; + + if (transform_ok == FALSE) + { + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + } + else + { + oLL.exportToWkt( &pszProjection ); + } + + CPLFree( pszGCPProjection ); + pszGCPProjection = NULL; + oLL.exportToWkt( &pszGCPProjection ); + + } + + delete hkvEllipsoids; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *HKVDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i, bNoDataSet = FALSE; + double dfNoDataValue = 0.0; + char **papszAttrib; + const char *pszFilename, *pszValue; + VSIStatBuf sStat; + +/* -------------------------------------------------------------------- */ +/* We assume the dataset is passed as a directory. Check for */ +/* an attrib and blob file as a minimum. */ +/* -------------------------------------------------------------------- */ + if( !poOpenInfo->bIsDirectory ) + return NULL; + + pszFilename = CPLFormFilename(poOpenInfo->pszFilename, "image_data", NULL); + if( VSIStat(pszFilename,&sStat) != 0 ) + pszFilename = CPLFormFilename(poOpenInfo->pszFilename, "blob", NULL ); + if( VSIStat(pszFilename,&sStat) != 0 ) + return NULL; + + pszFilename = CPLFormFilename(poOpenInfo->pszFilename, "attrib", NULL ); + if( VSIStat(pszFilename,&sStat) != 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Load the attrib file, and boil white space away from around */ +/* the equal sign. */ +/* -------------------------------------------------------------------- */ + papszAttrib = CSLLoad( pszFilename ); + if( papszAttrib == NULL ) + return NULL; + + for( i = 0; papszAttrib[i] != NULL; i++ ) + { + int bAfterEqual = FALSE; + int iSrc, iDst; + char *pszLine = papszAttrib[i]; + + for( iSrc=0, iDst=0; pszLine[iSrc] != '\0'; iSrc++ ) + { + if( bAfterEqual || pszLine[iSrc] != ' ' ) + { + pszLine[iDst++] = pszLine[iSrc]; + } + + if( iDst > 0 && pszLine[iDst-1] == '=' ) + bAfterEqual = FALSE; + } + pszLine[iDst] = '\0'; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + HKVDataset *poDS; + + poDS = new HKVDataset(); + + poDS->pszPath = CPLStrdup( poOpenInfo->pszFilename ); + poDS->papszAttrib = papszAttrib; + + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Set some dataset wide information. */ +/* -------------------------------------------------------------------- */ + int bNative, bComplex; + int nRawBands = 0; + + if( CSLFetchNameValue( papszAttrib, "extent.cols" ) == NULL + || CSLFetchNameValue( papszAttrib, "extent.rows" ) == NULL ) + return NULL; + + poDS->nRasterXSize = atoi(CSLFetchNameValue(papszAttrib,"extent.cols")); + poDS->nRasterYSize = atoi(CSLFetchNameValue(papszAttrib,"extent.rows")); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + + pszValue = CSLFetchNameValue(papszAttrib,"pixel.order"); + if( pszValue == NULL ) + bNative = TRUE; + else + { +#ifdef CPL_MSB + bNative = (strstr(pszValue,"*msbf") != NULL); +#else + bNative = (strstr(pszValue,"*lsbf") != NULL); +#endif + } + + pszValue = CSLFetchNameValue(papszAttrib,"pixel.no_data"); + if( pszValue != NULL ) + { + bNoDataSet = TRUE; + dfNoDataValue = CPLAtof(pszValue); + } + + pszValue = CSLFetchNameValue(papszAttrib,"channel.enumeration"); + if( pszValue != NULL ) + nRawBands = atoi(pszValue); + else + nRawBands = 1; + + if (!GDALCheckBandCount(nRawBands, TRUE)) + { + delete poDS; + return NULL; + } + + pszValue = CSLFetchNameValue(papszAttrib,"pixel.field"); + if( pszValue != NULL && strstr(pszValue,"*complex") != NULL ) + bComplex = TRUE; + else + bComplex = FALSE; + + /* Get the version number, if present (if not, assume old version. */ + /* Versions differ in their interpretation of corner coordinates. */ + + if (CSLFetchNameValue( papszAttrib, "version" ) != NULL) + poDS->SetVersion((float) + CPLAtof(CSLFetchNameValue(papszAttrib, "version"))); + else + poDS->SetVersion(1.0); + +/* -------------------------------------------------------------------- */ +/* Figure out the datatype */ +/* -------------------------------------------------------------------- */ + const char * pszEncoding; + int nSize = 1; + /* int nPseudoBands; */ + GDALDataType eType; + + pszEncoding = CSLFetchNameValue(papszAttrib,"pixel.encoding"); + if( pszEncoding == NULL ) + pszEncoding = "{ *unsigned }"; + + if( CSLFetchNameValue(papszAttrib,"pixel.size") != NULL ) + nSize = atoi(CSLFetchNameValue(papszAttrib,"pixel.size"))/8; +#if 0 + if( bComplex ) + nPseudoBands = 2; + else + nPseudoBands = 1; +#endif + + if( nSize == 1 ) + eType = GDT_Byte; + else if( nSize == 2 && strstr(pszEncoding,"*unsigned") != NULL ) + eType = GDT_UInt16; + else if( nSize == 4 && bComplex ) + eType = GDT_CInt16; + else if( nSize == 2 ) + eType = GDT_Int16; + else if( nSize == 4 && strstr(pszEncoding,"*unsigned") != NULL ) + eType = GDT_UInt32; + else if( nSize == 8 && strstr(pszEncoding,"*two") != NULL && bComplex ) + eType = GDT_CInt32; + else if( nSize == 4 && strstr(pszEncoding,"*two") != NULL ) + eType = GDT_Int32; + else if( nSize == 8 && bComplex ) + eType = GDT_CFloat32; + else if( nSize == 4 ) + eType = GDT_Float32; + else if( nSize == 16 && bComplex ) + eType = GDT_CFloat64; + else if( nSize == 8 ) + eType = GDT_Float64; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported pixel data type in %s.\n" + "pixel.size=%d pixel.encoding=%s\n", + poDS->pszPath, nSize, pszEncoding ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open the blob file. */ +/* -------------------------------------------------------------------- */ + pszFilename = CPLFormFilename(poDS->pszPath, "image_data", NULL ); + if( VSIStat(pszFilename,&sStat) != 0 ) + pszFilename = CPLFormFilename(poDS->pszPath, "blob", NULL ); + if( poOpenInfo->eAccess == GA_ReadOnly ) + { + poDS->fpBlob = VSIFOpenL( pszFilename, "rb" ); + if( poDS->fpBlob == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open file %s for read access.\n", + pszFilename ); + delete poDS; + return NULL; + } + } + else + { + poDS->fpBlob = VSIFOpenL( pszFilename, "rb+" ); + if( poDS->fpBlob == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open file %s for update access.\n", + pszFilename ); + delete poDS; + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Build the overview filename, as blob file = "_ovr". */ +/* -------------------------------------------------------------------- */ + char *pszOvrFilename = (char *) CPLMalloc(strlen(pszFilename)+5); + + sprintf( pszOvrFilename, "%s_ovr", pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Define the bands. */ +/* -------------------------------------------------------------------- */ + int nPixelOffset, nLineOffset, nOffset; + + nPixelOffset = nRawBands * nSize; + nLineOffset = nPixelOffset * poDS->GetRasterXSize(); + nOffset = 0; + + for( int iRawBand=0; iRawBand < nRawBands; iRawBand++ ) + { + HKVRasterBand *poBand; + + poBand = + new HKVRasterBand( poDS, poDS->GetRasterCount()+1, poDS->fpBlob, + nOffset, nPixelOffset, nLineOffset, + eType, bNative ); + poDS->SetBand( poDS->GetRasterCount()+1, poBand ); + nOffset += GDALGetDataTypeSize( eType ) / 8; + + if( bNoDataSet ) + poBand->SetNoDataValue( dfNoDataValue ); + } + + poDS->eRasterType = eType; + +/* -------------------------------------------------------------------- */ +/* Process the georef file if there is one. */ +/* -------------------------------------------------------------------- */ + pszFilename = CPLFormFilename(poDS->pszPath, "georef", NULL ); + if( VSIStat(pszFilename,&sStat) == 0 ) + poDS->ProcessGeoref(pszFilename); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( pszOvrFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Handle overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, pszOvrFilename, NULL, TRUE ); + + CPLFree( pszOvrFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *HKVDataset::Create( const char * pszFilenameIn, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** /* papszParmList */ ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify input options. */ +/* -------------------------------------------------------------------- */ + if (nBands <= 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "HKV driver does not support %d bands.\n", nBands); + return NULL; + } + + if( eType != GDT_Byte + && eType != GDT_UInt16 && eType != GDT_Int16 + && eType != GDT_CInt16 && eType != GDT_Float32 + && eType != GDT_CFloat32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create HKV file with currently unsupported\n" + "data type (%s).\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Establish the name of the directory we will be creating the */ +/* new HKV directory in. Verify that this is a directory. */ +/* -------------------------------------------------------------------- */ + char *pszBaseDir; + VSIStatBuf sStat; + + if( strlen(CPLGetPath(pszFilenameIn)) == 0 ) + pszBaseDir = CPLStrdup("."); + else + pszBaseDir = CPLStrdup(CPLGetPath(pszFilenameIn)); + + if( CPLStat( pszBaseDir, &sStat ) != 0 || !VSI_ISDIR( sStat.st_mode ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create HKV dataset under %s,\n" + "but this is not a valid directory.\n", + pszBaseDir); + CPLFree( pszBaseDir ); + return NULL; + } + + CPLFree( pszBaseDir ); + pszBaseDir = NULL; + + if( VSIMkdir( pszFilenameIn, 0755 ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create directory %s.\n", + pszFilenameIn ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the header file. */ +/* -------------------------------------------------------------------- */ + CPLErr CEHeaderCreated; + + CEHeaderCreated = SaveHKVAttribFile( pszFilenameIn, nXSize, nYSize, + nBands, eType, FALSE, 0.0 ); + + if (CEHeaderCreated != CE_None ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the blob file. */ +/* -------------------------------------------------------------------- */ + + FILE *fp; + const char *pszFilename; + + pszFilename = CPLFormFilename( pszFilenameIn, "image_data", NULL ); + fp = VSIFOpen( pszFilename, "wb" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Couldn't create %s.\n", pszFilename ); + return NULL; + } + + VSIFWrite( (void*)"", 1, 1, fp ); + VSIFClose( fp ); + +/* -------------------------------------------------------------------- */ +/* Open the dataset normally. */ +/* -------------------------------------------------------------------- */ + return (GDALDataset *) GDALOpen( pszFilenameIn, GA_Update ); +} + +/************************************************************************/ +/* Delete() */ +/* */ +/* An HKV Blob dataset consists of a bunch of files in a */ +/* directory. Try to delete all the files, then the */ +/* directory. */ +/************************************************************************/ + +CPLErr HKVDataset::Delete( const char * pszName ) + +{ + VSIStatBuf sStat; + char **papszFiles; + int i; + + if( CPLStat( pszName, &sStat ) != 0 + || !VSI_ISDIR(sStat.st_mode) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s does not appear to be an HKV Dataset, as it is not\n" + "a path to a directory.", + pszName ); + return CE_Failure; + } + + papszFiles = CPLReadDir( pszName ); + for( i = 0; i < CSLCount(papszFiles); i++ ) + { + const char *pszTarget; + + if( EQUAL(papszFiles[i],".") || EQUAL(papszFiles[i],"..") ) + continue; + + pszTarget = CPLFormFilename(pszName, papszFiles[i], NULL ); + if( VSIUnlink(pszTarget) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to delete file %s,\n" + "HKVDataset Delete(%s) failed.\n", + pszTarget, + pszName ); + CSLDestroy( papszFiles ); + return CE_Failure; + } + } + + CSLDestroy( papszFiles ); + + if( VSIRmdir( pszName ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to delete directory %s,\n" + "HKVDataset Delete() failed.\n", + pszName ); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * +HKVDataset::CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + CPL_UNUSED int bStrict, + char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + HKVDataset *poDS; + GDALDataType eType; + int iBand; + + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "HKV driver does not support source dataset with zero band.\n"); + return NULL; + } + + eType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + + /* check that other bands match type- sets type */ + /* to unknown if they differ. */ + for( iBand = 1; iBand < poSrcDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand *poBand = poSrcDS->GetRasterBand( iBand+1 ); + eType = GDALDataTypeUnion( eType, poBand->GetRasterDataType() ); + } + + poDS = (HKVDataset *) Create( pszFilename, + poSrcDS->GetRasterXSize(), + poSrcDS->GetRasterYSize(), + poSrcDS->GetRasterCount(), + eType, papszOptions ); + + /* Check that Create worked- return Null if it didn't */ + if (poDS == NULL) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Copy the image data. */ +/* -------------------------------------------------------------------- */ + int nXSize = poDS->GetRasterXSize(); + int nYSize = poDS->GetRasterYSize(); + int nBlockXSize, nBlockYSize, nBlockTotal, nBlocksDone; + + poDS->GetRasterBand(1)->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + nBlockTotal = ((nXSize + nBlockXSize - 1) / nBlockXSize) + * ((nYSize + nBlockYSize - 1) / nBlockYSize) + * poSrcDS->GetRasterCount(); + + nBlocksDone = 0; + for( iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + GDALRasterBand *poDstBand = poDS->GetRasterBand( iBand+1 ); + int iYOffset, iXOffset; + void *pData; + CPLErr eErr; + int pbSuccess; + double dfSrcNoDataValue =0.0; + + /* Get nodata value, if relevant */ + dfSrcNoDataValue = poSrcBand->GetNoDataValue( &pbSuccess ); + if ( pbSuccess ) + poDS->SetNoDataValue( dfSrcNoDataValue ); + + pData = CPLMalloc(nBlockXSize * nBlockYSize + * GDALGetDataTypeSize(eType) / 8); + + for( iYOffset = 0; iYOffset < nYSize; iYOffset += nBlockYSize ) + { + for( iXOffset = 0; iXOffset < nXSize; iXOffset += nBlockXSize ) + { + int nTBXSize, nTBYSize; + + if( !pfnProgress( (nBlocksDone++) / (float) nBlockTotal, + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated" ); + delete poDS; + CPLFree(pData); + + GDALDriver *poHKVDriver = + (GDALDriver *) GDALGetDriverByName( "MFF2" ); + poHKVDriver->Delete( pszFilename ); + return NULL; + } + + nTBXSize = MIN(nBlockXSize,nXSize-iXOffset); + nTBYSize = MIN(nBlockYSize,nYSize-iYOffset); + + eErr = poSrcBand->RasterIO( GF_Read, + iXOffset, iYOffset, + nTBXSize, nTBYSize, + pData, nTBXSize, nTBYSize, + eType, 0, 0, NULL ); + if( eErr != CE_None ) + { + delete poDS; + CPLFree(pData); + return NULL; + } + + eErr = poDstBand->RasterIO( GF_Write, + iXOffset, iYOffset, + nTBXSize, nTBYSize, + pData, nTBXSize, nTBYSize, + eType, 0, 0, NULL ); + + if( eErr != CE_None ) + { + delete poDS; + CPLFree(pData); + return NULL; + } + } + } + + CPLFree( pData ); + } + +/* -------------------------------------------------------------------- */ +/* Copy georeferencing information, if enough is available. */ +/* Only copy geotransform-style info (won't work for slant range). */ +/* -------------------------------------------------------------------- */ + + double *tempGeoTransform=NULL; + + tempGeoTransform = (double *) CPLMalloc(6*sizeof(double)); + + if (( poSrcDS->GetGeoTransform( tempGeoTransform ) == CE_None) + && (tempGeoTransform[0] != 0.0 || tempGeoTransform[1] != 1.0 + || tempGeoTransform[2] != 0.0 || tempGeoTransform[3] != 0.0 + || tempGeoTransform[4] != 0.0 || ABS(tempGeoTransform[5]) != 1.0 )) + { + + poDS->SetGCPProjection(poSrcDS->GetProjectionRef()); + poDS->SetProjection(poSrcDS->GetProjectionRef()); + poDS->SetGeoTransform(tempGeoTransform); + + CPLFree(tempGeoTransform); + + /* georef file will be saved automatically when dataset is deleted */ + /* because SetProjection sets a flag to indicate it's necessary. */ + + } + else + { + CPLFree(tempGeoTransform); + } + + /* Make sure image data gets flushed */ + for( iBand = 0; iBand < poDS->GetRasterCount(); iBand++ ) + { + RawRasterBand *poDstBand = (RawRasterBand *) poDS->GetRasterBand( iBand+1 ); + poDstBand->FlushCache(); + } + + + if( !pfnProgress( 1.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated" ); + delete poDS; + + GDALDriver *poHKVDriver = + (GDALDriver *) GDALGetDriverByName( "MFF2" ); + poHKVDriver->Delete( pszFilename ); + return NULL; + } + + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} + + +/************************************************************************/ +/* GDALRegister_HKV() */ +/************************************************************************/ + +void GDALRegister_HKV() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "MFF2" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "MFF2" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Vexcel MFF2 (HKV) Raster" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_mff2.html" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 CInt16 CInt32 Float32 Float64 CFloat32 CFloat64" ); + + poDriver->pfnOpen = HKVDataset::Open; + poDriver->pfnCreate = HKVDataset::Create; + poDriver->pfnDelete = HKVDataset::Delete; + poDriver->pfnCreateCopy = HKVDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/idadataset.cpp b/bazaar/plugin/gdal/frmts/raw/idadataset.cpp new file mode 100644 index 000000000..c0656495f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/idadataset.cpp @@ -0,0 +1,1129 @@ +/****************************************************************************** + * $Id: idadataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: IDA Raster Driver + * Purpose: Implemenents IDA driver/dataset/rasterband. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "ogr_spatialref.h" +#include "gdal_rat.h" + +CPL_CVSID("$Id: idadataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +CPL_C_START +void GDALRegister_IDA(void); +CPL_C_END + +// convert a Turbo Pascal real into a double +static double tp2c(GByte *r); + +// convert a double into a Turbo Pascal real +static void c2tp(double n, GByte *r); + +/************************************************************************/ +/* ==================================================================== */ +/* IDADataset */ +/* ==================================================================== */ +/************************************************************************/ + +class IDADataset : public RawDataset +{ + friend class IDARasterBand; + + int nImageType; + int nProjection; + char szTitle[81]; + double dfLatCenter; + double dfLongCenter; + double dfXCenter; + double dfYCenter; + double dfDX; + double dfDY; + double dfParallel1; + double dfParallel2; + int nMissing; + double dfM; + double dfB; + + VSILFILE *fpRaw; + + char *pszProjection; + double adfGeoTransform[6]; + + void ProcessGeoref(); + + GByte abyHeader[512]; + int bHeaderDirty; + + void ReadColorTable(); + + public: + IDADataset(); + ~IDADataset(); + + virtual void FlushCache(); + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** /* papszParmList */ ); + +}; + +/************************************************************************/ +/* ==================================================================== */ +/* IDARasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class IDARasterBand : public RawRasterBand +{ + friend class IDADataset; + + GDALRasterAttributeTable *poRAT; + GDALColorTable *poColorTable; + + public: + IDARasterBand( IDADataset *poDSIn, VSILFILE *fpRaw, int nXSize ); + virtual ~IDARasterBand(); + + virtual GDALRasterAttributeTable *GetDefaultRAT(); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual double GetOffset( int *pbSuccess = NULL ); + virtual CPLErr SetOffset( double dfNewValue ); + virtual double GetScale( int *pbSuccess = NULL ); + virtual CPLErr SetScale( double dfNewValue ); + virtual double GetNoDataValue( int *pbSuccess = NULL ); +}; + +/************************************************************************/ +/* IDARasterBand */ +/************************************************************************/ + +IDARasterBand::IDARasterBand( IDADataset *poDSIn, + VSILFILE *fpRaw, int nXSize ) + : RawRasterBand( poDSIn, 1, fpRaw, 512, 1, nXSize, + GDT_Byte, FALSE, TRUE ) + +{ + poColorTable = NULL; + poRAT = NULL; +} + +/************************************************************************/ +/* ~IDARasterBand() */ +/************************************************************************/ + +IDARasterBand::~IDARasterBand() + +{ + delete poColorTable; + delete poRAT; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double IDARasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + return ((IDADataset *) poDS)->nMissing; +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +double IDARasterBand::GetOffset( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + return ((IDADataset *) poDS)->dfB; +} + +/************************************************************************/ +/* SetOffset() */ +/************************************************************************/ + +CPLErr IDARasterBand::SetOffset( double dfNewValue ) + +{ + IDADataset *poIDS = (IDADataset *) poDS; + + if( dfNewValue == poIDS->dfB ) + return CE_None; + + if( poIDS->nImageType != 200 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Setting explicit offset only support for image type 200."); + return CE_Failure; + } + + poIDS->dfB = dfNewValue; + c2tp( dfNewValue, poIDS->abyHeader + 177 ); + poIDS->bHeaderDirty = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ + +double IDARasterBand::GetScale( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + return ((IDADataset *) poDS)->dfM; +} + +/************************************************************************/ +/* SetScale() */ +/************************************************************************/ + +CPLErr IDARasterBand::SetScale( double dfNewValue ) + +{ + IDADataset *poIDS = (IDADataset *) poDS; + + if( dfNewValue == poIDS->dfM ) + return CE_None; + + if( poIDS->nImageType != 200 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Setting explicit scale only support for image type 200."); + return CE_Failure; + } + + poIDS->dfM = dfNewValue; + c2tp( dfNewValue, poIDS->abyHeader + 171 ); + poIDS->bHeaderDirty = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *IDARasterBand::GetColorTable() + +{ + if( poColorTable ) + return poColorTable; + else + return RawRasterBand::GetColorTable(); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp IDARasterBand::GetColorInterpretation() + +{ + if( poColorTable ) + return GCI_PaletteIndex; + else + return RawRasterBand::GetColorInterpretation(); +} + +/************************************************************************/ +/* GetDefaultRAT() */ +/************************************************************************/ + +GDALRasterAttributeTable *IDARasterBand::GetDefaultRAT() + +{ + if( poRAT ) + return poRAT; + else + return RawRasterBand::GetDefaultRAT(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* IDADataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* IDADataset() */ +/************************************************************************/ + +IDADataset::IDADataset() +{ + fpRaw = NULL; + pszProjection = NULL; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + bHeaderDirty = FALSE; +} + +/************************************************************************/ +/* ~IDADataset() */ +/************************************************************************/ + +IDADataset::~IDADataset() + +{ + FlushCache(); + + if( fpRaw != NULL ) + VSIFCloseL( fpRaw ); + CPLFree( pszProjection ); +} + +/************************************************************************/ +/* ProcessGeoref() */ +/************************************************************************/ + +void IDADataset::ProcessGeoref() + +{ + OGRSpatialReference oSRS; + + if( nProjection == 3 ) + { + oSRS.SetWellKnownGeogCS( "WGS84" ); + } + else if( nProjection == 4 ) + { + oSRS.SetLCC( dfParallel1, dfParallel2, + dfLatCenter, dfLongCenter, + 0.0, 0.0 ); + oSRS.SetGeogCS( "Clarke 1866", "Clarke 1866", "Clarke 1866", + 6378206.4, 293.97869821389662 ); + } + else if( nProjection == 6 ) + { + oSRS.SetLAEA( dfLatCenter, dfLongCenter, 0.0, 0.0 ); + oSRS.SetGeogCS( "Sphere", "Sphere", "Sphere", + 6370997.0, 0.0 ); + } + else if( nProjection == 8 ) + { + oSRS.SetACEA( dfParallel1, dfParallel2, + dfLatCenter, dfLongCenter, + 0.0, 0.0 ); + oSRS.SetGeogCS( "Clarke 1866", "Clarke 1866", "Clarke 1866", + 6378206.4, 293.97869821389662 ); + } + else if( nProjection == 9 ) + { + oSRS.SetGH( dfLongCenter, 0.0, 0.0 ); + oSRS.SetGeogCS( "Sphere", "Sphere", "Sphere", + 6370997.0, 0.0 ); + } + + if( oSRS.GetRoot() != NULL ) + { + CPLFree( pszProjection ); + pszProjection = NULL; + + oSRS.exportToWkt( &pszProjection ); + } + + adfGeoTransform[0] = 0 - dfDX * dfXCenter; + adfGeoTransform[1] = dfDX; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = dfDY * dfYCenter; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = -dfDY; + + if( nProjection == 3 ) + { + adfGeoTransform[0] += dfLongCenter; + adfGeoTransform[3] += dfLatCenter; + } +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void IDADataset::FlushCache() + +{ + RawDataset::FlushCache(); + + if( bHeaderDirty ) + { + VSIFSeekL( fpRaw, 0, SEEK_SET ); + VSIFWriteL( abyHeader, 512, 1, fpRaw ); + bHeaderDirty = FALSE; + } +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr IDADataset::GetGeoTransform( double *padfGeoTransform ) + +{ + memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr IDADataset::SetGeoTransform( double *padfGeoTransform ) + +{ + if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0 ) + return GDALPamDataset::SetGeoTransform( padfGeoTransform ); + + memcpy( adfGeoTransform, padfGeoTransform, sizeof(double) * 6 ); + bHeaderDirty = TRUE; + + dfDX = adfGeoTransform[1]; + dfDY = -adfGeoTransform[5]; + dfXCenter = -adfGeoTransform[0] / dfDX; + dfYCenter = adfGeoTransform[3] / dfDY; + + c2tp( dfDX, abyHeader + 144 ); + c2tp( dfDY, abyHeader + 150 ); + c2tp( dfXCenter, abyHeader + 132 ); + c2tp( dfYCenter, abyHeader + 138 ); + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *IDADataset::GetProjectionRef() + +{ + if( pszProjection ) + return pszProjection; + else + return ""; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr IDADataset::SetProjection( const char *pszWKTIn ) + +{ + OGRSpatialReference oSRS; + + oSRS.importFromWkt( (char **) &pszWKTIn ); + + if( !oSRS.IsGeographic() && !oSRS.IsProjected() ) + GDALPamDataset::SetProjection( pszWKTIn ); + +/* -------------------------------------------------------------------- */ +/* Clear projection parameters. */ +/* -------------------------------------------------------------------- */ + dfParallel1 = 0.0; + dfParallel2 = 0.0; + dfLatCenter = 0.0; + dfLongCenter = 0.0; + +/* -------------------------------------------------------------------- */ +/* Geographic. */ +/* -------------------------------------------------------------------- */ + if( oSRS.IsGeographic() ) + { + // If no change, just return. + if( nProjection == 3 ) + return CE_None; + + nProjection = 3; + } + +/* -------------------------------------------------------------------- */ +/* Verify we don't have a false easting or northing as these */ +/* will be ignored for the projections we do support. */ +/* -------------------------------------------------------------------- */ + if( oSRS.GetProjParm( SRS_PP_FALSE_EASTING ) != 0.0 + || oSRS.GetProjParm( SRS_PP_FALSE_NORTHING ) != 0.0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to set a projection on an IDA file with a non-zero\n" + "false easting and/or northing. This is not supported." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Lambert Conformal Conic. Note that we don't support false */ +/* eastings or nothings. */ +/* -------------------------------------------------------------------- */ + const char *pszProjection = oSRS.GetAttrValue( "PROJECTION" ); + + if( pszProjection == NULL ) + { + /* do nothing - presumably geographic */; + } + else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ) + { + nProjection = 4; + dfParallel1 = oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); + dfParallel2 = oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0); + dfLatCenter = oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + dfLongCenter = oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + } + else if( EQUAL(pszProjection,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) + { + nProjection = 6; + dfLatCenter = oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + dfLongCenter = oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + } + else if( EQUAL(pszProjection,SRS_PT_ALBERS_CONIC_EQUAL_AREA) ) + { + nProjection = 8; + dfParallel1 = oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); + dfParallel2 = oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0); + dfLatCenter = oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + dfLongCenter = oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + } + else if( EQUAL(pszProjection,SRS_PT_GOODE_HOMOLOSINE) ) + { + nProjection = 9; + dfLongCenter = oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + } + else + { + return GDALPamDataset::SetProjection( pszWKTIn ); + } + +/* -------------------------------------------------------------------- */ +/* Update header and mark it as dirty. */ +/* -------------------------------------------------------------------- */ + bHeaderDirty = TRUE; + + abyHeader[23] = (GByte) nProjection; + c2tp( dfLatCenter, abyHeader + 120 ); + c2tp( dfLongCenter, abyHeader + 126 ); + c2tp( dfParallel1, abyHeader + 156 ); + c2tp( dfParallel2, abyHeader + 162 ); + + return CE_None; +} + +/************************************************************************/ +/* ReadColorTable() */ +/************************************************************************/ + +void IDADataset::ReadColorTable() + +{ +/* -------------------------------------------------------------------- */ +/* Decide what .clr file to look for and try to open. */ +/* -------------------------------------------------------------------- */ + CPLString osCLRFilename; + + osCLRFilename = CPLGetConfigOption( "IDA_COLOR_FILE", "" ); + if( strlen(osCLRFilename) == 0 ) + osCLRFilename = CPLResetExtension(GetDescription(), "clr" ); + + + FILE *fp = VSIFOpen( osCLRFilename, "r" ); + if( fp == NULL ) + { + osCLRFilename = CPLResetExtension(osCLRFilename, "CLR" ); + fp = VSIFOpen( osCLRFilename, "r" ); + } + + if( fp == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Skip first line, with the column titles. */ +/* -------------------------------------------------------------------- */ + CPLReadLine( fp ); + +/* -------------------------------------------------------------------- */ +/* Create a RAT to populate. */ +/* -------------------------------------------------------------------- */ + GDALRasterAttributeTable *poRAT = new GDALDefaultRasterAttributeTable(); + + poRAT->CreateColumn( "FROM", GFT_Integer, GFU_Min ); + poRAT->CreateColumn( "TO", GFT_Integer, GFU_Max ); + poRAT->CreateColumn( "RED", GFT_Integer, GFU_Red ); + poRAT->CreateColumn( "GREEN", GFT_Integer, GFU_Green ); + poRAT->CreateColumn( "BLUE", GFT_Integer, GFU_Blue ); + poRAT->CreateColumn( "LEGEND", GFT_String, GFU_Name ); + +/* -------------------------------------------------------------------- */ +/* Apply lines. */ +/* -------------------------------------------------------------------- */ + const char *pszLine = CPLReadLine( fp ); + int iRow = 0; + + while( pszLine != NULL ) + { + char **papszTokens = + CSLTokenizeStringComplex( pszLine, " \t", FALSE, FALSE ); + + if( CSLCount( papszTokens ) >= 5 ) + { + poRAT->SetValue( iRow, 0, atoi(papszTokens[0]) ); + poRAT->SetValue( iRow, 1, atoi(papszTokens[1]) ); + poRAT->SetValue( iRow, 2, atoi(papszTokens[2]) ); + poRAT->SetValue( iRow, 3, atoi(papszTokens[3]) ); + poRAT->SetValue( iRow, 4, atoi(papszTokens[4]) ); + + // find name, first nonspace after 5th token. + const char *pszName = pszLine; + + // skip from + while( *pszName == ' ' || *pszName == '\t' ) + pszName++; + while( *pszName != ' ' && *pszName != '\t' && *pszName != '\0' ) + pszName++; + + // skip to + while( *pszName == ' ' || *pszName == '\t' ) + pszName++; + while( *pszName != ' ' && *pszName != '\t' && *pszName != '\0' ) + pszName++; + + // skip red + while( *pszName == ' ' || *pszName == '\t' ) + pszName++; + while( *pszName != ' ' && *pszName != '\t' && *pszName != '\0' ) + pszName++; + + // skip green + while( *pszName == ' ' || *pszName == '\t' ) + pszName++; + while( *pszName != ' ' && *pszName != '\t' && *pszName != '\0' ) + pszName++; + + // skip blue + while( *pszName == ' ' || *pszName == '\t' ) + pszName++; + while( *pszName != ' ' && *pszName != '\t' && *pszName != '\0' ) + pszName++; + + // skip pre-name white space + while( *pszName == ' ' || *pszName == '\t' ) + pszName++; + + poRAT->SetValue( iRow, 5, pszName ); + + iRow++; + } + + CSLDestroy( papszTokens ); + pszLine = CPLReadLine( fp ); + } + + VSIFClose( fp ); + +/* -------------------------------------------------------------------- */ +/* Attach RAT to band. */ +/* -------------------------------------------------------------------- */ + ((IDARasterBand *) GetRasterBand( 1 ))->poRAT = poRAT; + +/* -------------------------------------------------------------------- */ +/* Build a conventional color table from this. */ +/* -------------------------------------------------------------------- */ + ((IDARasterBand *) GetRasterBand( 1 ))->poColorTable = + poRAT->TranslateToColorTable(); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *IDADataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Is this an IDA file? */ +/* -------------------------------------------------------------------- */ + int nXSize, nYSize; + GIntBig nExpectedFileSize, nActualFileSize; + + if( poOpenInfo->fpL == NULL ) + return NULL; + + if( poOpenInfo->nHeaderBytes < 512 ) + return NULL; + + // projection legal? + if( poOpenInfo->pabyHeader[23] > 10 ) + return NULL; + + // imagetype legal? + if( (poOpenInfo->pabyHeader[22] > 14 + && poOpenInfo->pabyHeader[22] < 100) + || (poOpenInfo->pabyHeader[22] > 114 + && poOpenInfo->pabyHeader[22] != 200 ) ) + return NULL; + + nXSize = poOpenInfo->pabyHeader[30] + poOpenInfo->pabyHeader[31] * 256; + nYSize = poOpenInfo->pabyHeader[32] + poOpenInfo->pabyHeader[33] * 256; + + if( nXSize == 0 || nYSize == 0 ) + return NULL; + + // The file just be exactly the image size + header size in length. + nExpectedFileSize = nXSize * nYSize + 512; + + VSIFSeekL( poOpenInfo->fpL, 0, SEEK_END ); + nActualFileSize = VSIFTellL( poOpenInfo->fpL ); + VSIRewindL( poOpenInfo->fpL ); + + if( nActualFileSize != nExpectedFileSize ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + IDADataset *poDS = new IDADataset(); + + memcpy( poDS->abyHeader, poOpenInfo->pabyHeader, 512 ); + +/* -------------------------------------------------------------------- */ +/* Parse various values out of the header. */ +/* -------------------------------------------------------------------- */ + poDS->nImageType = poOpenInfo->pabyHeader[22]; + poDS->nProjection = poOpenInfo->pabyHeader[23]; + + poDS->nRasterYSize = poOpenInfo->pabyHeader[30] + + poOpenInfo->pabyHeader[31] * 256; + poDS->nRasterXSize = poOpenInfo->pabyHeader[32] + + poOpenInfo->pabyHeader[33] * 256; + + strncpy( poDS->szTitle, (const char *) poOpenInfo->pabyHeader+38, 80 ); + poDS->szTitle[80] = '\0'; + + int nLastTitleChar = strlen(poDS->szTitle)-1; + while( nLastTitleChar > -1 + && (poDS->szTitle[nLastTitleChar] == 10 + || poDS->szTitle[nLastTitleChar] == 13 + || poDS->szTitle[nLastTitleChar] == ' ') ) + poDS->szTitle[nLastTitleChar--] = '\0'; + + poDS->dfLatCenter = tp2c( poOpenInfo->pabyHeader + 120 ); + poDS->dfLongCenter = tp2c( poOpenInfo->pabyHeader + 126 ); + poDS->dfXCenter = tp2c( poOpenInfo->pabyHeader + 132 ); + poDS->dfYCenter = tp2c( poOpenInfo->pabyHeader + 138 ); + poDS->dfDX = tp2c( poOpenInfo->pabyHeader + 144 ); + poDS->dfDY = tp2c( poOpenInfo->pabyHeader + 150 ); + poDS->dfParallel1 = tp2c( poOpenInfo->pabyHeader + 156 ); + poDS->dfParallel2 = tp2c( poOpenInfo->pabyHeader + 162 ); + + poDS->ProcessGeoref(); + + poDS->SetMetadataItem( "TITLE", poDS->szTitle ); + +/* -------------------------------------------------------------------- */ +/* Handle various image types. */ +/* -------------------------------------------------------------------- */ + +/* +GENERIC = 0 +FEW S NDVI = 1 +EROS NDVI = 6 +ARTEMIS CUTOFF = 10 +ARTEMIS RECODE = 11 +ARTEMIS NDVI = 12 +ARTEMIS FEWS = 13 +ARTEMIS NEWNASA = 14 +GENERIC DIFF = 100 +FEW S NDVI DIFF = 101 +EROS NDVI DIFF = 106 +ARTEMIS CUTOFF DIFF = 110 +ARTEMIS RECODE DIFF = 111 +ARTEMIS NDVI DIFF = 112 +ARTEMIS FEWS DIFF = 113 +ARTEMIS NEWNASA DIFF = 114 +CALCULATED =200 +*/ + + poDS->nMissing = 0; + + switch( poDS->nImageType ) + { + case 1: + poDS->SetMetadataItem( "IMAGETYPE", "1, FEWS NDVI" ); + poDS->dfM = 1/256.0; + poDS->dfB = -82/256.0; + break; + + case 6: + poDS->SetMetadataItem( "IMAGETYPE", "6, EROS NDVI" ); + poDS->dfM = 1/100.0; + poDS->dfB = -100/100.0; + break; + + case 10: + poDS->SetMetadataItem( "IMAGETYPE", "10, ARTEMIS CUTOFF" ); + poDS->dfM = 1.0; + poDS->dfB = 0.0; + poDS->nMissing = 254; + break; + + case 11: + poDS->SetMetadataItem( "IMAGETYPE", "11, ARTEMIS RECODE" ); + poDS->dfM = 4.0; + poDS->dfB = 0.0; + poDS->nMissing = 254; + break; + + case 12: /* ANDVI */ + poDS->SetMetadataItem( "IMAGETYPE", "12, ARTEMIS NDVI" ); + poDS->dfM = 4/500.0; + poDS->dfB = -3/500.0 - 1.0; + poDS->nMissing = 254; + break; + + case 13: /* AFEWS */ + poDS->SetMetadataItem( "IMAGETYPE", "13, ARTEMIS FEWS" ); + poDS->dfM = 1/256.0; + poDS->dfB = -82/256.0; + poDS->nMissing = 254; + break; + + case 14: /* NEWNASA */ + poDS->SetMetadataItem( "IMAGETYPE", "13, ARTEMIS NEWNASA" ); + poDS->dfM = 0.75/250.0; + poDS->dfB = 0.0; + poDS->nMissing = 254; + break; + + case 101: /* NDVI_DIFF (FEW S) */ + poDS->dfM = 1/128.0; + poDS->dfB = -1.0; + poDS->nMissing = 0; + break; + + case 106: /* EROS_DIFF */ + poDS->dfM = 1/50.0; + poDS->dfB = -128/50.0; + poDS->nMissing = 0; + break; + + case 110: /* CUTOFF_DIFF */ + poDS->dfM = 2.0; + poDS->dfB = -128*2; + poDS->nMissing = 254; + break; + + case 111: /* RECODE_DIFF */ + poDS->dfM = 8; + poDS->dfB = -128*8; + poDS->nMissing = 254; + break; + + case 112: /* ANDVI_DIFF */ + poDS->dfM = 8/1000.0; + poDS->dfB = (-128*8)/1000.0; + poDS->nMissing = 254; + break; + + case 113: /* AFEWS_DIFF */ + poDS->dfM = 1/128.0; + poDS->dfB = -1; + poDS->nMissing = 254; + break; + + case 114: /* NEWNASA_DIFF */ + poDS->dfM = 0.75/125.0; + poDS->dfB = -128*poDS->dfM; + poDS->nMissing = 254; + break; + + case 200: + /* we use the values from the header */ + poDS->dfM = tp2c( poOpenInfo->pabyHeader + 171 ); + poDS->dfB = tp2c( poOpenInfo->pabyHeader + 177 ); + poDS->nMissing = poOpenInfo->pabyHeader[170]; + break; + + default: + poDS->dfM = 1.0; + poDS->dfB = 0.0; + break; + } + +/* -------------------------------------------------------------------- */ +/* Create the band. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_ReadOnly ) + { + poDS->fpRaw = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + } + else + { + poDS->fpRaw = VSIFOpenL( poOpenInfo->pszFilename, "rb+" ); + poDS->eAccess = GA_Update; + if( poDS->fpRaw == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s for write access.", + poOpenInfo->pszFilename ); + return NULL; + } + } + + poDS->SetBand( 1, new IDARasterBand( poDS, poDS->fpRaw, + poDS->nRasterXSize ) ); + +/* -------------------------------------------------------------------- */ +/* Check for a color table. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->ReadColorTable(); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* tp2c() */ +/* */ +/* convert a Turbo Pascal real into a double */ +/************************************************************************/ + +static double tp2c(GByte *r) +{ + double mant; + int sign, exp, i; + + // handle 0 case + if (r[0] == 0) + return 0.0; + + // extract sign: bit 7 of byte 5 + sign = r[5] & 0x80 ? -1 : 1; + + // extract mantissa from first bit of byte 1 to bit 7 of byte 5 + mant = 0; + for (i = 1; i < 5; i++) + mant = (r[i] + mant) / 256; + mant = (mant + (r[5] & 0x7F)) / 128 + 1; + + // extract exponent + exp = r[0] - 129; + + // compute the damned number + return sign * ldexp(mant, exp); +} + +/************************************************************************/ +/* c2tp() */ +/* */ +/* convert a double into a Turbo Pascal real */ +/************************************************************************/ + +static void c2tp(double x, GByte *r) +{ + double mant, temp; + int negative, exp, i; + + // handle 0 case + if (x == 0.0) + { + for (i = 0; i < 6; r[i++] = 0); + return; + } + + // compute mantissa, sign and exponent + mant = frexp(x, &exp) * 2 - 1; + exp--; + negative = 0; + if (mant < 0) + { + mant = -mant; + negative = 1; + } + // stuff mantissa into Turbo Pascal real + mant = modf(mant * 128, &temp); + r[5] = (unsigned char) temp; + for (i = 4; i >= 1; i--) + { + mant = modf(mant * 256, &temp); + r[i] = (unsigned char) temp; + } + // add sign + if (negative) + r[5] |= 0x80; + + // put exponent + r[0] = (GByte) (exp + 129); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *IDADataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** /* papszParmList */ ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify input options. */ +/* -------------------------------------------------------------------- */ + if( eType != GDT_Byte || nBands != 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Only 1 band, Byte datasets supported for IDA format." ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to create the file. */ +/* -------------------------------------------------------------------- */ + FILE *fp; + + fp = VSIFOpen( pszFilename, "wb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Prepare formatted header. */ +/* -------------------------------------------------------------------- */ + GByte abyHeader[512]; + + memset( abyHeader, 0, sizeof(abyHeader) ); + + abyHeader[22] = 200; /* image type - CALCULATED */ + abyHeader[23] = 0; /* projection - NONE */ + abyHeader[30] = nYSize % 256; + abyHeader[31] = (GByte) (nYSize / 256); + abyHeader[32] = nXSize % 256; + abyHeader[33] = (GByte) (nXSize / 256); + + abyHeader[170] = 255; /* missing = 255 */ + c2tp( 1.0, abyHeader + 171 ); /* slope = 1.0 */ + c2tp( 0.0, abyHeader + 177 ); /* offset = 0 */ + abyHeader[168] = 0; // lower limit + abyHeader[169] = 254; // upper limit + + // pixel size = 1.0 + c2tp( 1.0, abyHeader + 144 ); + c2tp( 1.0, abyHeader + 150 ); + + if( VSIFWrite( abyHeader, 1, 512, fp ) != 512 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "IO error writing %s.\n%s", + pszFilename, VSIStrerror( errno ) ); + VSIFClose( fp ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Now we need to extend the file to just the right number of */ +/* bytes for the data we have to ensure it will open again */ +/* properly. */ +/* -------------------------------------------------------------------- */ + if( VSIFSeek( fp, nXSize * nYSize - 1, SEEK_CUR ) != 0 + || VSIFWrite( abyHeader, 1, 1, fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "IO error writing %s.\n%s", + pszFilename, VSIStrerror( errno ) ); + VSIFClose( fp ); + return NULL; + } + + VSIFClose( fp ); + + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* GDALRegister_IDA() */ +/************************************************************************/ + +void GDALRegister_IDA() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "IDA" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "IDA" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Image Data and Analysis" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#IDA" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = IDADataset::Open; + poDriver->pfnCreate = IDADataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/raw/krodataset.cpp b/bazaar/plugin/gdal/frmts/raw/krodataset.cpp new file mode 100644 index 000000000..86f468f28 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/krodataset.cpp @@ -0,0 +1,319 @@ +/****************************************************************************** + * $Id: krodataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: KRO format reader/writer + * Purpose: Implementation of KOLOR Raw Format + * Author: Even Rouault, + * Financial Support: SITES (http://www.sites.fr) + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: krodataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/* http://www.autopano.net/wiki-en/Format_KRO */ + +/************************************************************************/ +/* ==================================================================== */ +/* KRODataset */ +/* ==================================================================== */ +/************************************************************************/ + +class KRODataset : public RawDataset +{ + public: + VSILFILE *fpImage; // image data file. + + public: + KRODataset(); + ~KRODataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* KRODataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* KRODataset() */ +/************************************************************************/ + +KRODataset::KRODataset() +{ + fpImage = NULL; +} + +/************************************************************************/ +/* ~KRODataset() */ +/************************************************************************/ + +KRODataset::~KRODataset() + +{ + FlushCache(); + + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int KRODataset::Identify( GDALOpenInfo *poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 20 ) + return FALSE; + + if( !EQUALN((const char *)poOpenInfo->pabyHeader, "KRO\x01", 4 ) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *KRODataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + KRODataset *poDS; + + poDS = new KRODataset(); + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb+" ); + + if( poDS->fpImage == NULL ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the file header. */ +/* -------------------------------------------------------------------- */ + char achHeader[20]; + int nXSize, nYSize, nDepth, nComp; + + VSIFReadL( achHeader, 1, 20, poDS->fpImage ); + + memcpy(&nXSize, achHeader + 4, 4); + CPL_MSBPTR32( &nXSize ); + + memcpy(&nYSize, achHeader + 8, 4); + CPL_MSBPTR32( &nYSize ); + + memcpy(&nDepth, achHeader + 12, 4); + CPL_MSBPTR32( &nDepth ); + + memcpy(&nComp, achHeader + 16, 4); + CPL_MSBPTR32( &nComp ); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || + !GDALCheckBandCount(nComp, FALSE)) + { + delete poDS; + return NULL; + } + + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + + GDALDataType eDT; + if( nDepth == 8 ) + eDT = GDT_Byte; + else if( nDepth == 16 ) + eDT = GDT_UInt16; + else if( nDepth == 32 ) + eDT = GDT_Float32; + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unhandled depth : %d", nDepth); + delete poDS; + return NULL; + } + + int nDataTypeSize = nDepth / 8; + +/* -------------------------------------------------------------------- */ +/* Create bands. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < nComp; iBand++ ) + { + RawRasterBand *poBand = + new RawRasterBand( poDS, iBand+1, poDS->fpImage, + 20 + nDataTypeSize * iBand, + nComp * nDataTypeSize, + poDS->nRasterXSize * nComp * nDataTypeSize, + eDT, !CPL_IS_LSB, TRUE, FALSE ); + if( nComp == 3 || nComp == 4 ) + { + poBand->SetColorInterpretation( (GDALColorInterp) (GCI_RedBand + iBand) ); + } + poDS->SetBand( iBand+1, poBand ); + } + + if( nComp > 1 ) + poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *KRODataset::Create( const char * pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + CPL_UNUSED char ** papszOptions ) +{ + if( eType != GDT_Byte && eType != GDT_UInt16 && eType != GDT_Float32 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Attempt to create KRO file with unsupported data type '%s'.", + GDALGetDataTypeName( eType ) ); + return NULL; + } + + VSILFILE *fp; + +/* -------------------------------------------------------------------- */ +/* Try to create file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszFilename, "wb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + + int nRet = 0; + nRet += VSIFWriteL("KRO\01", 4, 1, fp); + + int nTmp; +/* -------------------------------------------------------------------- */ +/* Create a file level header. */ +/* -------------------------------------------------------------------- */ + nTmp = nXSize; + CPL_MSBPTR32(&nTmp); + nRet += VSIFWriteL(&nTmp, 4, 1, fp); + + nTmp = nYSize; + CPL_MSBPTR32(&nTmp); + nRet += VSIFWriteL(&nTmp, 4, 1, fp); + + nTmp = GDALGetDataTypeSize(eType); + CPL_MSBPTR32(&nTmp); + nRet += VSIFWriteL(&nTmp, 4, 1, fp); + + nTmp = nBands; + CPL_MSBPTR32(&nTmp); + nRet += VSIFWriteL(&nTmp, 4, 1, fp); + +/* -------------------------------------------------------------------- */ +/* Zero out image data */ +/* -------------------------------------------------------------------- */ + + VSIFSeekL(fp, (vsi_l_offset)nXSize * nYSize * (GDALGetDataTypeSize(eType) / 8) * nBands - 1, + SEEK_CUR); + GByte byNul = 0; + nRet += VSIFWriteL(&byNul, 1, 1, fp); + VSIFCloseL(fp); + + if( nRet != 6 ) + return NULL; + + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* GDALRegister_KRO() */ +/************************************************************************/ + +void GDALRegister_KRO() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "KRO" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "KRO" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "KOLOR Raw" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "kro" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16 Float32" ); + + poDriver->pfnIdentify = KRODataset::Identify; + poDriver->pfnOpen = KRODataset::Open; + poDriver->pfnCreate = KRODataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/landataset.cpp b/bazaar/plugin/gdal/frmts/raw/landataset.cpp new file mode 100644 index 000000000..b0bd15645 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/landataset.cpp @@ -0,0 +1,1044 @@ +/****************************************************************************** + * $Id: landataset.cpp 28435 2015-02-07 14:35:34Z rouault $ + * + * Project: eCognition + * Purpose: Implementation of Erdas .LAN / .GIS format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: landataset.cpp 28435 2015-02-07 14:35:34Z rouault $"); + +CPL_C_START +void GDALRegister_LAN(void); +CPL_C_END + +/** + +Erdas Header format: "HEAD74" + +Offset Size Type Description +------ ---- ---- ----------- +0 6 char magic cookie / version (ie. HEAD74). +6 2 Int16 Pixel type, 0=8bit, 1=4bit, 2=16bit +8 2 Int16 Number of Bands. +10 6 char Unknown. +16 4 Int32 Width +20 4 Int32 Height +24 4 Int32 X Start (offset in original file?) +28 4 Int32 Y Start (offset in original file?) +32 56 char Unknown. +88 2 Int16 0=LAT, 1=UTM, 2=StatePlane, 3- are projections? +90 2 Int16 Classes in coverage. +92 14 char Unknown. +106 2 Int16 Area Unit (0=none, 1=Acre, 2=Hectare, 3=Other) +108 4 Float32 Pixel area. +112 4 Float32 Upper Left corner X (center of pixel?) +116 4 Float32 Upper Left corner Y (center of pixel?) +120 4 Float32 Width of a pixel. +124 4 Float32 Height of a pixel. + +Erdas Header format: "HEADER" + +Offset Size Type Description +------ ---- ---- ----------- +0 6 char magic cookie / version (ie. HEAD74). +6 2 Int16 Pixel type, 0=8bit, 1=4bit, 2=16bit +8 2 Int16 Number of Bands. +10 6 char Unknown. +16 4 Float32 Width +20 4 Float32 Height +24 4 Int32 X Start (offset in original file?) +28 4 Int32 Y Start (offset in original file?) +32 56 char Unknown. +88 2 Int16 0=LAT, 1=UTM, 2=StatePlane, 3- are projections? +90 2 Int16 Classes in coverage. +92 14 char Unknown. +106 2 Int16 Area Unit (0=none, 1=Acre, 2=Hectare, 3=Other) +108 4 Float32 Pixel area. +112 4 Float32 Upper Left corner X (center of pixel?) +116 4 Float32 Upper Left corner Y (center of pixel?) +120 4 Float32 Width of a pixel. +124 4 Float32 Height of a pixel. + +All binary fields are in the same byte order but it may be big endian or +little endian depending on what platform the file was written on. Usually +this can be checked against the number of bands though this test won't work +if there are more than 255 bands. + +There is also some information on .STA and .TRL files at: + + http://www.pcigeomatics.com/cgi-bin/pcihlp/ERDASWR%7CTRAILER+FORMAT + +**/ + +#define ERD_HEADER_SIZE 128 + +/************************************************************************/ +/* ==================================================================== */ +/* LAN4BitRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class LANDataset; + +class LAN4BitRasterBand : public GDALPamRasterBand +{ + GDALColorTable *poCT; + GDALColorInterp eInterp; + + public: + LAN4BitRasterBand( LANDataset *, int ); + ~LAN4BitRasterBand(); + + virtual GDALColorTable *GetColorTable(); + virtual GDALColorInterp GetColorInterpretation(); + virtual CPLErr SetColorTable( GDALColorTable * ); + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* LANDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class LANDataset : public RawDataset +{ + public: + VSILFILE *fpImage; // image data file. + + char pachHeader[ERD_HEADER_SIZE]; + + char *pszProjection; + + double adfGeoTransform[6]; + + CPLString osSTAFilename; + void CheckForStatistics(void); + + virtual char **GetFileList(); + + public: + LANDataset(); + ~LANDataset(); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual CPLErr SetGeoTransform( double * padfTransform ); + virtual const char *GetProjectionRef(); + virtual CPLErr SetProjection( const char * ); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* LAN4BitRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* LAN4BitRasterBand() */ +/************************************************************************/ + +LAN4BitRasterBand::LAN4BitRasterBand( LANDataset *poDS, int nBandIn ) + +{ + this->poDS = poDS; + this->nBand = nBandIn; + this->eDataType = GDT_Byte; + + nBlockXSize = poDS->GetRasterXSize();; + nBlockYSize = 1; + + poCT = NULL; + eInterp = GCI_Undefined; +} + +/************************************************************************/ +/* ~LAN4BitRasterBand() */ +/************************************************************************/ + +LAN4BitRasterBand::~LAN4BitRasterBand() + +{ + if( poCT ) + delete poCT; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr LAN4BitRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) + +{ + LANDataset *poLAN_DS = (LANDataset *) poDS; + CPLAssert( nBlockXOff == 0 ); + +/* -------------------------------------------------------------------- */ +/* Seek to profile. */ +/* -------------------------------------------------------------------- */ + int nOffset; + + nOffset = + ERD_HEADER_SIZE + + (nBlockYOff * nRasterXSize * poLAN_DS->GetRasterCount()) / 2 + + ((nBand - 1) * nRasterXSize) / 2; + + if( VSIFSeekL( poLAN_DS->fpImage, nOffset, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "LAN Seek failed:%s", VSIStrerror( errno ) ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Read the profile. */ +/* -------------------------------------------------------------------- */ + if( VSIFReadL( pImage, 1, nRasterXSize/2, poLAN_DS->fpImage ) != + (size_t) nRasterXSize / 2 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "LAN Read failed:%s", VSIStrerror( errno ) ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Convert 4bit to 8bit. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = nRasterXSize-1; i >= 0; i-- ) + { + if( (i & 0x01) != 0 ) + ((GByte *) pImage)[i] = ((GByte *) pImage)[i/2] & 0x0f; + else + ((GByte *) pImage)[i] = (((GByte *) pImage)[i/2] & 0xf0)/16; + } + + return CE_None; +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr LAN4BitRasterBand::SetColorTable( GDALColorTable *poNewCT ) + +{ + if( poCT ) + delete poCT; + if( poNewCT == NULL ) + poCT = NULL; + else + poCT = poNewCT->Clone(); + + return CE_None; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *LAN4BitRasterBand::GetColorTable() + +{ + if( poCT != NULL ) + return poCT; + else + return GDALPamRasterBand::GetColorTable(); +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +CPLErr LAN4BitRasterBand::SetColorInterpretation( GDALColorInterp eNewInterp ) + +{ + eInterp = eNewInterp; + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp LAN4BitRasterBand::GetColorInterpretation() + +{ + return eInterp; +} + +/************************************************************************/ +/* ==================================================================== */ +/* LANDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* LANDataset() */ +/************************************************************************/ + +LANDataset::LANDataset() +{ + fpImage = NULL; + pszProjection = NULL; +} + +/************************************************************************/ +/* ~LANDataset() */ +/************************************************************************/ + +LANDataset::~LANDataset() + +{ + FlushCache(); + + if( fpImage != NULL ) + VSIFCloseL( fpImage ); + + CPLFree( pszProjection ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *LANDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* We assume the user is pointing to the header (.pcb) file. */ +/* Does this appear to be a pcb file? */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < ERD_HEADER_SIZE ) + return NULL; + + if( !EQUALN((const char *)poOpenInfo->pabyHeader,"HEADER",6) + && !EQUALN((const char *)poOpenInfo->pabyHeader,"HEAD74",6) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + LANDataset *poDS; + + poDS = new LANDataset(); + + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Adopt the openinfo file pointer for use with this file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb+" ); + + if( poDS->fpImage == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Do we need to byte swap the headers to local machine order? */ +/* -------------------------------------------------------------------- */ + int bBigEndian = poOpenInfo->pabyHeader[8] == 0; + int bNeedSwap; + + memcpy( poDS->pachHeader, poOpenInfo->pabyHeader, ERD_HEADER_SIZE ); + +#ifdef CPL_LSB + bNeedSwap = bBigEndian; +#else + bNeedSwap = !bBigEndian; +#endif + + if( bNeedSwap ) + { + CPL_SWAP16PTR( poDS->pachHeader + 6 ); + CPL_SWAP16PTR( poDS->pachHeader + 8 ); + + CPL_SWAP32PTR( poDS->pachHeader + 16 ); + CPL_SWAP32PTR( poDS->pachHeader + 20 ); + CPL_SWAP32PTR( poDS->pachHeader + 24 ); + CPL_SWAP32PTR( poDS->pachHeader + 28 ); + + CPL_SWAP16PTR( poDS->pachHeader + 88 ); + CPL_SWAP16PTR( poDS->pachHeader + 90 ); + + CPL_SWAP16PTR( poDS->pachHeader + 106 ); + CPL_SWAP32PTR( poDS->pachHeader + 108 ); + CPL_SWAP32PTR( poDS->pachHeader + 112 ); + CPL_SWAP32PTR( poDS->pachHeader + 116 ); + CPL_SWAP32PTR( poDS->pachHeader + 120 ); + CPL_SWAP32PTR( poDS->pachHeader + 124 ); + } + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + int nBandCount, nPixelOffset; + GDALDataType eDataType; + + if( EQUALN(poDS->pachHeader,"HEADER",7) ) + { + float fTmp; + memcpy(&fTmp, poDS->pachHeader + 16, 4); + CPL_LSBPTR32(&fTmp); + poDS->nRasterXSize = (int) fTmp; + memcpy(&fTmp, poDS->pachHeader + 20, 4); + CPL_LSBPTR32(&fTmp); + poDS->nRasterYSize = (int) fTmp; + } + else + { + GInt32 nTmp; + memcpy(&nTmp, poDS->pachHeader + 16, 4); + CPL_LSBPTR32(&nTmp); + poDS->nRasterXSize = nTmp; + memcpy(&nTmp, poDS->pachHeader + 20, 4); + CPL_LSBPTR32(&nTmp); + poDS->nRasterYSize = nTmp; + } + + GInt16 nTmp16; + memcpy(&nTmp16, poDS->pachHeader + 6, 2); + CPL_LSBPTR16(&nTmp16); + + if( nTmp16 == 0 ) + { + eDataType = GDT_Byte; + nPixelOffset = 1; + } + else if( nTmp16 == 1 ) /* 4bit! */ + { + eDataType = GDT_Byte; + nPixelOffset = -1; + } + else if( nTmp16 == 2 ) + { + nPixelOffset = 2; + eDataType = GDT_Int16; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported pixel type (%d).", + nTmp16 ); + + delete poDS; + return NULL; + } + + memcpy(&nTmp16, poDS->pachHeader + 8, 2); + CPL_LSBPTR16(&nTmp16); + nBandCount = nTmp16; + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || + !GDALCheckBandCount(nBandCount, FALSE)) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information object. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 1; iBand <= nBandCount; iBand++ ) + { + if( nPixelOffset == -1 ) /* 4 bit case */ + poDS->SetBand( iBand, + new LAN4BitRasterBand( poDS, iBand ) ); + else + poDS->SetBand( + iBand, + new RawRasterBand( poDS, iBand, poDS->fpImage, + ERD_HEADER_SIZE + (iBand-1) + * nPixelOffset * poDS->nRasterXSize, + nPixelOffset, + poDS->nRasterXSize*nPixelOffset*nBandCount, + eDataType, !bNeedSwap, TRUE )); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->CheckForStatistics(); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Try to interprete georeferencing. */ +/* -------------------------------------------------------------------- */ + float fTmp; + + memcpy(&fTmp, poDS->pachHeader + 112, 4); + CPL_LSBPTR32(&fTmp); + poDS->adfGeoTransform[0] = fTmp; + memcpy(&fTmp, poDS->pachHeader + 120, 4); + CPL_LSBPTR32(&fTmp); + poDS->adfGeoTransform[1] = fTmp; + poDS->adfGeoTransform[2] = 0.0; + memcpy(&fTmp, poDS->pachHeader + 116, 4); + CPL_LSBPTR32(&fTmp); + poDS->adfGeoTransform[3] = fTmp; + poDS->adfGeoTransform[4] = 0.0; + memcpy(&fTmp, poDS->pachHeader + 124, 4); + CPL_LSBPTR32(&fTmp); + poDS->adfGeoTransform[5] = - fTmp; + + // adjust for center of pixel vs. top left corner of pixel. + poDS->adfGeoTransform[0] -= poDS->adfGeoTransform[1] * 0.5; + poDS->adfGeoTransform[3] -= poDS->adfGeoTransform[5] * 0.5; + +/* -------------------------------------------------------------------- */ +/* If we didn't get any georeferencing, try for a worldfile. */ +/* -------------------------------------------------------------------- */ + if( poDS->adfGeoTransform[1] == 0.0 + || poDS->adfGeoTransform[5] == 0.0 ) + { + if( !GDALReadWorldFile( poOpenInfo->pszFilename, NULL, + poDS->adfGeoTransform ) ) + GDALReadWorldFile( poOpenInfo->pszFilename, ".wld", + poDS->adfGeoTransform ); + } + +/* -------------------------------------------------------------------- */ +/* Try to come up with something for the coordinate system. */ +/* -------------------------------------------------------------------- */ + memcpy(&nTmp16, poDS->pachHeader + 88, 2); + CPL_LSBPTR16(&nTmp16); + int nCoordSys = nTmp16; + + if( nCoordSys == 0 ) + { + poDS->pszProjection = CPLStrdup(SRS_WKT_WGS84); + + } + else if( nCoordSys == 1 ) + { + poDS->pszProjection = + CPLStrdup("LOCAL_CS[\"UTM - Zone Unknown\",UNIT[\"Meter\",1]]"); + } + else if( nCoordSys == 2 ) + { + poDS->pszProjection = CPLStrdup("LOCAL_CS[\"State Plane - Zone Unknown\",UNIT[\"US survey foot\",0.3048006096012192]]"); + } + else + { + poDS->pszProjection = + CPLStrdup("LOCAL_CS[\"Unknown\",UNIT[\"Meter\",1]]"); + } + +/* -------------------------------------------------------------------- */ +/* Check for a trailer file with a colormap in it. */ +/* -------------------------------------------------------------------- */ + char *pszPath = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename)); + char *pszBasename = CPLStrdup(CPLGetBasename(poOpenInfo->pszFilename)); + const char *pszTRLFilename = + CPLFormCIFilename( pszPath, pszBasename, "trl" ); + VSILFILE *fpTRL; + + fpTRL = VSIFOpenL( pszTRLFilename, "rb" ); + if( fpTRL != NULL ) + { + char szTRLData[896]; + int iColor; + GDALColorTable *poCT; + + VSIFReadL( szTRLData, 1, 896, fpTRL ); + VSIFCloseL( fpTRL ); + + poCT = new GDALColorTable(); + for( iColor = 0; iColor < 256; iColor++ ) + { + GDALColorEntry sEntry; + + sEntry.c2 = ((GByte *) szTRLData)[iColor+128]; + sEntry.c1 = ((GByte *) szTRLData)[iColor+128+256]; + sEntry.c3 = ((GByte *) szTRLData)[iColor+128+512]; + sEntry.c4 = 255; + poCT->SetColorEntry( iColor, &sEntry ); + + // only 16 colors in 4bit files. + if( nPixelOffset == -1 && iColor == 15 ) + break; + } + + poDS->GetRasterBand(1)->SetColorTable( poCT ); + poDS->GetRasterBand(1)->SetColorInterpretation( GCI_PaletteIndex ); + + delete poCT; + } + + CPLFree( pszPath ); + CPLFree( pszBasename ); + + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr LANDataset::GetGeoTransform( double * padfTransform ) + +{ + if( adfGeoTransform[1] != 0.0 && adfGeoTransform[5] != 0.0 ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + return CE_None; + } + else + return GDALPamDataset::GetGeoTransform( padfTransform ); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr LANDataset::SetGeoTransform( double * padfTransform ) + +{ + unsigned char abyHeader[128]; + + memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 ); + + VSIFSeekL( fpImage, 0, SEEK_SET ); + VSIFReadL( abyHeader, 128, 1, fpImage ); + + // Upper Left X + float f32Val; + + f32Val = (float) (adfGeoTransform[0] + 0.5 * adfGeoTransform[1]); + memcpy( abyHeader + 112, &f32Val, 4 ); + + // Upper Left Y + f32Val = (float) (adfGeoTransform[3] + 0.5 * adfGeoTransform[5]); + memcpy( abyHeader + 116, &f32Val, 4 ); + + // width of pixel + f32Val = (float) adfGeoTransform[1]; + memcpy( abyHeader + 120, &f32Val, 4 ); + + // height of pixel + f32Val = (float) fabs(adfGeoTransform[5]); + memcpy( abyHeader + 124, &f32Val, 4 ); + + if( VSIFSeekL( fpImage, 0, SEEK_SET ) != 0 + || VSIFWriteL( abyHeader, 128, 1, fpImage ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "File IO Error writing header with new geotransform." ); + return CE_Failure; + } + else + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/* */ +/* Use PAM coordinate system if available in preference to the */ +/* generally poor value derived from the file itself. */ +/************************************************************************/ + +const char *LANDataset::GetProjectionRef() + +{ + const char* pszPamPrj = GDALPamDataset::GetProjectionRef(); + + if( pszProjection != NULL && strlen(pszPamPrj) == 0 ) + return pszProjection; + else + return pszPamPrj; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr LANDataset::SetProjection( const char * pszWKT ) + +{ + unsigned char abyHeader[128]; + + VSIFSeekL( fpImage, 0, SEEK_SET ); + VSIFReadL( abyHeader, 128, 1, fpImage ); + + OGRSpatialReference oSRS( pszWKT ); + + GUInt16 nProjCode = 0; + + if( oSRS.IsGeographic() ) + nProjCode = 0; + + else if( oSRS.GetUTMZone() != 0 ) + nProjCode = 1; + + // Too bad we have no way of recognising state plane projections. + + else + { + const char *pszProjection = oSRS.GetAttrValue("PROJECTION"); + + if( pszProjection == NULL ) + ; + else if( EQUAL(pszProjection, + SRS_PT_ALBERS_CONIC_EQUAL_AREA) ) + nProjCode = 3; + else if( EQUAL(pszProjection, + SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) ) + nProjCode = 4; + else if( EQUAL(pszProjection, + SRS_PT_MERCATOR_1SP) ) + nProjCode = 5; + else if( EQUAL(pszProjection, + SRS_PT_POLAR_STEREOGRAPHIC) ) + nProjCode = 6; + else if( EQUAL(pszProjection, + SRS_PT_POLYCONIC) ) + nProjCode = 7; + else if( EQUAL(pszProjection, + SRS_PT_EQUIDISTANT_CONIC) ) + nProjCode = 8; + else if( EQUAL(pszProjection, + SRS_PT_TRANSVERSE_MERCATOR) ) + nProjCode = 9; + else if( EQUAL(pszProjection, + SRS_PT_STEREOGRAPHIC) ) + nProjCode = 10; + else if( EQUAL(pszProjection, + SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) + nProjCode = 11; + else if( EQUAL(pszProjection, + SRS_PT_AZIMUTHAL_EQUIDISTANT) ) + nProjCode = 12; + else if( EQUAL(pszProjection, + SRS_PT_GNOMONIC) ) + nProjCode = 13; + else if( EQUAL(pszProjection, + SRS_PT_ORTHOGRAPHIC) ) + nProjCode = 14; + // we don't have GVNP. + else if( EQUAL(pszProjection, + SRS_PT_SINUSOIDAL) ) + nProjCode = 16; + else if( EQUAL(pszProjection, + SRS_PT_EQUIRECTANGULAR) ) + nProjCode = 17; + else if( EQUAL(pszProjection, + SRS_PT_MILLER_CYLINDRICAL) ) + nProjCode = 18; + else if( EQUAL(pszProjection, + SRS_PT_VANDERGRINTEN) ) + nProjCode = 19; + else if( EQUAL(pszProjection, + SRS_PT_HOTINE_OBLIQUE_MERCATOR) ) + nProjCode = 20; + } + + memcpy( abyHeader + 88, &nProjCode, 2 ); + + VSIFSeekL( fpImage, 0, SEEK_SET ); + VSIFWriteL( abyHeader, 128, 1, fpImage ); + + return GDALPamDataset::SetProjection( pszWKT ); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **LANDataset::GetFileList() + +{ + char **papszFileList = NULL; + + // Main data file, etc. + papszFileList = GDALPamDataset::GetFileList(); + + if( strlen(osSTAFilename) > 0 ) + papszFileList = CSLAddString( papszFileList, osSTAFilename ); + + return papszFileList; +} + +/************************************************************************/ +/* CheckForStatistics() */ +/************************************************************************/ + +void LANDataset::CheckForStatistics() + +{ +/* -------------------------------------------------------------------- */ +/* Do we have a statistics file? */ +/* -------------------------------------------------------------------- */ + osSTAFilename = CPLResetExtension(GetDescription(),"sta"); + + VSILFILE *fpSTA = VSIFOpenL( osSTAFilename, "r" ); + + if( fpSTA == NULL && VSIIsCaseSensitiveFS(osSTAFilename) ) + { + osSTAFilename = CPLResetExtension(GetDescription(),"STA"); + fpSTA = VSIFOpenL( osSTAFilename, "r" ); + } + + if( fpSTA == NULL ) + { + osSTAFilename = ""; + return; + } + +/* -------------------------------------------------------------------- */ +/* Read it one band at a time. */ +/* -------------------------------------------------------------------- */ + GByte abyBandInfo[1152]; + int iBand; + + for( iBand = 0; iBand < nBands; iBand++ ) + { + if( VSIFReadL( abyBandInfo, 1152, 1, fpSTA ) != 1 ) + break; + + int nBandNumber = abyBandInfo[7]; + GDALRasterBand *poBand = GetRasterBand(nBandNumber); + if( poBand == NULL ) + break; + + float fMean, fStdDev; + GInt16 nMin, nMax; + + if( poBand->GetRasterDataType() != GDT_Byte ) + { + memcpy( &nMin, abyBandInfo + 28, 2 ); + memcpy( &nMax, abyBandInfo + 30, 2 ); + CPL_LSBPTR16( &nMin ); + CPL_LSBPTR16( &nMax ); + } + else + { + nMin = abyBandInfo[9]; + nMax = abyBandInfo[8]; + } + + memcpy( &fMean, abyBandInfo + 12, 4 ); + memcpy( &fStdDev, abyBandInfo + 24, 4 ); + CPL_LSBPTR32( &fMean ); + CPL_LSBPTR32( &fStdDev ); + + poBand->SetStatistics( nMin, nMax, fMean, fStdDev ); + } + + VSIFCloseL( fpSTA ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *LANDataset::Create( const char * pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + CPL_UNUSED char ** papszOptions ) +{ + if( eType != GDT_Byte && eType != GDT_Int16 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create .GIS file with unsupported data type '%s'.", + GDALGetDataTypeName( eType ) ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to create the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + + fp = VSIFOpenL( pszFilename, "wb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write out the header. */ +/* -------------------------------------------------------------------- */ + unsigned char abyHeader[128]; + GInt16 n16Val; + GInt32 n32Val; + + memset( abyHeader, 0, sizeof(abyHeader) ); + + memcpy( abyHeader + 0, "HEAD74", 6 ); + + // Pixel type + if( eType == GDT_Byte ) // do we want 4bit? + n16Val = 0; + else + n16Val = 2; + memcpy( abyHeader + 6, &n16Val, 2 ); + + // Number of Bands. + n16Val = (GInt16) nBands; + memcpy( abyHeader + 8, &n16Val, 2 ); + + // Unknown (6) + + // Width + n32Val = nXSize; + memcpy( abyHeader + 16, &n32Val, 4 ); + + // Height + n32Val = nYSize; + memcpy( abyHeader + 20, &n32Val, 4 ); + + // X Start (4) + // Y Start (4) + + // Unknown (56) + + // Coordinate System + n16Val = 0; + memcpy( abyHeader + 88, &n16Val, 2 ); + + // Classes in coverage + n16Val = 0; + memcpy( abyHeader + 90, &n16Val, 2 ); + + // Unknown (14) + + // Area Unit + n16Val = 0; + memcpy( abyHeader + 106, &n16Val, 2 ); + + // Pixel Area + float f32Val; + + f32Val = 0.0f; + memcpy( abyHeader + 108, &f32Val, 4 ); + + // Upper Left X + f32Val = 0.5f; + memcpy( abyHeader + 112, &f32Val, 4 ); + + // Upper Left Y + f32Val = (float) (nYSize - 0.5); + memcpy( abyHeader + 116, &f32Val, 4 ); + + // width of pixel + f32Val = 1.0f; + memcpy( abyHeader + 120, &f32Val, 4 ); + + // height of pixel + f32Val = 1.0f; + memcpy( abyHeader + 124, &f32Val, 4 ); + + VSIFWriteL( abyHeader, sizeof(abyHeader), 1, fp ); + +/* -------------------------------------------------------------------- */ +/* Extend the file to the target size. */ +/* -------------------------------------------------------------------- */ + vsi_l_offset nImageBytes; + + if( eType != GDT_Byte ) + nImageBytes = nXSize * (vsi_l_offset) nYSize * 2; + else + nImageBytes = nXSize * (vsi_l_offset) nYSize; + + memset( abyHeader, 0, sizeof(abyHeader) ); + + while( nImageBytes > 0 ) + { + vsi_l_offset nWriteThisTime = MIN(nImageBytes,sizeof(abyHeader)); + + if( VSIFWriteL( abyHeader, 1, (size_t)nWriteThisTime, fp ) + != nWriteThisTime ) + { + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_FileIO, + "Failed to write whole Istar file." ); + return NULL; + } + nImageBytes -= nWriteThisTime; + } + + VSIFCloseL( fp ); + + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* GDALRegister_LAN() */ +/************************************************************************/ + +void GDALRegister_LAN() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "LAN" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "LAN" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Erdas .LAN/.GIS" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#LAN" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16" ); + + poDriver->pfnOpen = LANDataset::Open; + poDriver->pfnCreate = LANDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/lcpdataset.cpp b/bazaar/plugin/gdal/frmts/raw/lcpdataset.cpp new file mode 100644 index 000000000..ac48c08ec --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/lcpdataset.cpp @@ -0,0 +1,1784 @@ +/****************************************************************************** + * $Id: lcpdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: LCP Driver + * Purpose: FARSITE v.4 Landscape file (.lcp) reader for GDAL + * Author: Chris Toney + * + ****************************************************************************** + * Copyright (c) 2008, Chris Toney + * Copyright (c) 2008-2011, Even Rouault + * Copyright (c) 2013, Kyle Shannon + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_port.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: lcpdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_LCP(void); +CPL_C_END + +#define LCP_HEADER_SIZE 7316 +#define LCP_MAX_BANDS 10 +#define LCP_MAX_PATH 256 +#define LCP_MAX_DESC 512 +#define LCP_MAX_CLASSES 100 + +/************************************************************************/ +/* ==================================================================== */ +/* LCPDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class LCPDataset : public RawDataset +{ + VSILFILE *fpImage; // image data file. + char pachHeader[LCP_HEADER_SIZE]; + + CPLString osPrjFilename; + char *pszProjection; + + static CPLErr ClassifyBandData( GDALRasterBand *poBand, + GInt32 *pnNumClasses, + GInt32 *panClasses ); + + public: + LCPDataset(); + ~LCPDataset(); + + virtual char **GetFileList(void); + + virtual CPLErr GetGeoTransform( double * ); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + virtual const char *GetProjectionRef(void); + + int bHaveProjection; +}; + +/************************************************************************/ +/* LCPDataset() */ +/************************************************************************/ + +LCPDataset::LCPDataset() +{ + fpImage = NULL; + pszProjection = CPLStrdup( "" ); + bHaveProjection = FALSE; +} + +/************************************************************************/ +/* ~LCPDataset() */ +/************************************************************************/ + +LCPDataset::~LCPDataset() + +{ + FlushCache(); + if( fpImage != NULL ) + VSIFCloseL( fpImage ); + CPLFree(pszProjection); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr LCPDataset::GetGeoTransform( double * padfTransform ) +{ + double dfEast, dfWest, dfNorth, dfSouth, dfCellX, dfCellY; + + memcpy(&dfEast, pachHeader + 4172, sizeof(double)); + memcpy(&dfWest, pachHeader + 4180, sizeof(double)); + memcpy(&dfNorth, pachHeader + 4188, sizeof(double)); + memcpy(&dfSouth, pachHeader + 4196, sizeof(double)); + memcpy(&dfCellX, pachHeader + 4208, sizeof(double)); + memcpy(&dfCellY, pachHeader + 4216, sizeof(double)); + CPL_LSBPTR64(&dfEast); + CPL_LSBPTR64(&dfWest); + CPL_LSBPTR64(&dfNorth); + CPL_LSBPTR64(&dfSouth); + CPL_LSBPTR64(&dfCellX); + CPL_LSBPTR64(&dfCellY); + + padfTransform[0] = dfWest; + padfTransform[3] = dfNorth; + padfTransform[1] = dfCellX; + padfTransform[2] = 0.0; + + padfTransform[4] = 0.0; + padfTransform[5] = -1 * dfCellY; + + return CE_None; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int LCPDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify that this is a FARSITE v.4 LCP file */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 50 ) + return FALSE; + + /* check if first three fields have valid data */ + if( (CPL_LSBINT32PTR(poOpenInfo->pabyHeader) != 20 + && CPL_LSBINT32PTR(poOpenInfo->pabyHeader) != 21) + || (CPL_LSBINT32PTR(poOpenInfo->pabyHeader+4) != 20 + && CPL_LSBINT32PTR(poOpenInfo->pabyHeader+4) != 21) + || (CPL_LSBINT32PTR(poOpenInfo->pabyHeader+8) < -90 + || CPL_LSBINT32PTR(poOpenInfo->pabyHeader+8) > 90) ) + { + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **LCPDataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + + if( bHaveProjection ) + { + papszFileList = CSLAddString( papszFileList, osPrjFilename ); + } + + return papszFileList; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *LCPDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify that this is a FARSITE LCP file */ +/* -------------------------------------------------------------------- */ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The LCP driver does not support update access to existing" + " datasets." ); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + LCPDataset *poDS; + VSILFILE *fpImage; + + fpImage = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + if (fpImage == NULL) + return NULL; + + poDS = new LCPDataset(); + poDS->fpImage = fpImage; + +/* -------------------------------------------------------------------- */ +/* Read the header and extract some information. */ +/* -------------------------------------------------------------------- */ + int bHaveCrownFuels, bHaveGroundFuels; + int nBands, i; + long nWidth = -1, nHeight = -1; + int nTemp, nTemp2; + char szTemp[32]; + char* pszList; + + VSIFSeekL( poDS->fpImage, 0, SEEK_SET ); + if (VSIFReadL( poDS->pachHeader, 1, LCP_HEADER_SIZE, poDS->fpImage ) != LCP_HEADER_SIZE) + { + CPLError(CE_Failure, CPLE_FileIO, "File too short"); + delete poDS; + return NULL; + } + + nWidth = CPL_LSBINT32PTR (poDS->pachHeader + 4164); + nHeight = CPL_LSBINT32PTR (poDS->pachHeader + 4168); + + poDS->nRasterXSize = nWidth; + poDS->nRasterYSize = nHeight; + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + + // crown fuels = canopy height, canopy base height, canopy bulk density + // 21 = have them, 20 = don't have them + bHaveCrownFuels = ( CPL_LSBINT32PTR (poDS->pachHeader + 0) - 20 ); + // ground fuels = duff loading, coarse woody + bHaveGroundFuels = ( CPL_LSBINT32PTR (poDS->pachHeader + 4) - 20 ); + + if( bHaveCrownFuels ) + { + if( bHaveGroundFuels ) + nBands = 10; + else + nBands = 8; + } + else + { + if( bHaveGroundFuels ) + nBands = 7; + else + nBands = 5; + } + + // add dataset-level metadata + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 8); + sprintf(szTemp, "%d", nTemp); + poDS->SetMetadataItem( "LATITUDE", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 4204); + if ( nTemp == 0 ) + poDS->SetMetadataItem( "LINEAR_UNIT", "Meters" ); + if ( nTemp == 1 ) + poDS->SetMetadataItem( "LINEAR_UNIT", "Feet" ); + + poDS->pachHeader[LCP_HEADER_SIZE-1] = '\0'; + poDS->SetMetadataItem( "DESCRIPTION", poDS->pachHeader + 6804 ); + + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + + int iPixelSize; + iPixelSize = nBands * 2; + int bNativeOrder; + + if (nWidth > INT_MAX / iPixelSize) + { + CPLError( CE_Failure, CPLE_AppDefined, "Int overflow occured"); + delete poDS; + return NULL; + } + +#ifdef CPL_LSB + bNativeOrder = TRUE; +#else + bNativeOrder = FALSE; +#endif + + pszList = (char*)CPLMalloc(2048); + pszList[0] = '\0'; + + for( int iBand = 1; iBand <= nBands; iBand++ ) + { + GDALRasterBand *poBand = NULL; + + poBand = new RawRasterBand( + poDS, iBand, poDS->fpImage, LCP_HEADER_SIZE + ((iBand-1)*2), + iPixelSize, iPixelSize * nWidth, GDT_Int16, bNativeOrder, TRUE ); + + poDS->SetBand(iBand, poBand); + + switch ( iBand ) { + case 1: + poBand->SetDescription("Elevation"); + + nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4224); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "ELEVATION_UNIT", szTemp ); + + if ( nTemp == 0 ) + poBand->SetMetadataItem( "ELEVATION_UNIT_NAME", "Meters" ); + if ( nTemp == 1 ) + poBand->SetMetadataItem( "ELEVATION_UNIT_NAME", "Feet" ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 44); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "ELEVATION_MIN", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 48); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "ELEVATION_MAX", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 52); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "ELEVATION_NUM_CLASSES", szTemp ); + + *(poDS->pachHeader + 4244 + 255) = '\0'; + poBand->SetMetadataItem( "ELEVATION_FILE", poDS->pachHeader + 4244 ); + + break; + + case 2: + poBand->SetDescription("Slope"); + + nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4226); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "SLOPE_UNIT", szTemp ); + + if ( nTemp == 0 ) + poBand->SetMetadataItem( "SLOPE_UNIT_NAME", "Degrees" ); + if ( nTemp == 1 ) + poBand->SetMetadataItem( "SLOPE_UNIT_NAME", "Percent" ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 456); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "SLOPE_MIN", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 460); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "SLOPE_MAX", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 464); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "SLOPE_NUM_CLASSES", szTemp ); + + *(poDS->pachHeader + 4500 + 255) = '\0'; + poBand->SetMetadataItem( "SLOPE_FILE", poDS->pachHeader + 4500 ); + + break; + + case 3: + poBand->SetDescription("Aspect"); + + nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4228); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "ASPECT_UNIT", szTemp ); + + if ( nTemp == 0 ) + poBand->SetMetadataItem( "ASPECT_UNIT_NAME", "Grass categories" ); + if ( nTemp == 1 ) + poBand->SetMetadataItem( "ASPECT_UNIT_NAME", "Grass degrees" ); + if ( nTemp == 2 ) + poBand->SetMetadataItem( "ASPECT_UNIT_NAME", "Azimuth degrees" ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 868); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "ASPECT_MIN", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 872); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "ASPECT_MAX", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 876); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "ASPECT_NUM_CLASSES", szTemp ); + + *(poDS->pachHeader + 4756 + 255) = '\0'; + poBand->SetMetadataItem( "ASPECT_FILE", poDS->pachHeader + 4756 ); + + break; + + case 4: + int nMinFM, nMaxFM; + + poBand->SetDescription("Fuel models"); + + nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4230); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "FUEL_MODEL_OPTION", szTemp ); + + if ( nTemp == 0 ) + poBand->SetMetadataItem( "FUEL_MODEL_OPTION_DESC", "no custom models AND no conversion file needed" ); + if ( nTemp == 1 ) + poBand->SetMetadataItem( "FUEL_MODEL_OPTION_DESC", "custom models BUT no conversion file needed" ); + if ( nTemp == 2 ) + poBand->SetMetadataItem( "FUEL_MODEL_OPTION_DESC", "no custom models BUT conversion file needed" ); + if ( nTemp == 3 ) + poBand->SetMetadataItem( "FUEL_MODEL_OPTION_DESC", "custom models AND conversion file needed" ); + + nMinFM = CPL_LSBINT32PTR (poDS->pachHeader + 1280); + sprintf(szTemp, "%d", nMinFM); + poBand->SetMetadataItem( "FUEL_MODEL_MIN", szTemp ); + + nMaxFM = CPL_LSBINT32PTR (poDS->pachHeader + 1284); + sprintf(szTemp, "%d", nMaxFM); + poBand->SetMetadataItem( "FUEL_MODEL_MAX", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 1288); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "FUEL_MODEL_NUM_CLASSES", szTemp ); + + if (nTemp > 0 && nTemp <= 100) { + strcpy(pszList, ""); + for ( i = 0; i <= nTemp; i++ ) { + nTemp2 = CPL_LSBINT32PTR (poDS->pachHeader + (1292+(i*4))) ; + if ( nTemp2 >= nMinFM && nTemp2 <= nMaxFM ) { + sprintf(szTemp, "%d", nTemp2); + strcat(pszList, szTemp); + if (i < (nTemp) ) + strcat(pszList, ","); + } + } + } + poBand->SetMetadataItem( "FUEL_MODEL_VALUES", pszList ); + + *(poDS->pachHeader + 5012 + 255) = '\0'; + poBand->SetMetadataItem( "FUEL_MODEL_FILE", poDS->pachHeader + 5012 ); + + break; + + case 5: + poBand->SetDescription("Canopy cover"); + + nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4232); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CANOPY_COV_UNIT", szTemp ); + + if ( nTemp == 0 ) + poBand->SetMetadataItem( "CANOPY_COV_UNIT_NAME", "Categories (0-4)" ); + if ( nTemp == 1 ) + poBand->SetMetadataItem( "CANOPY_COV_UNIT_NAME", "Percent" ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 1692); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CANOPY_COV_MIN", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 1696); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CANOPY_COV_MAX", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 1700); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CANOPY_COV_NUM_CLASSES", szTemp ); + + *(poDS->pachHeader + 5268 + 255) = '\0'; + poBand->SetMetadataItem( "CANOPY_COV_FILE", poDS->pachHeader + 5268 ); + + break; + + case 6: + if(bHaveCrownFuels) { + poBand->SetDescription("Canopy height"); + + nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4234); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CANOPY_HT_UNIT", szTemp ); + + if ( nTemp == 1 ) + poBand->SetMetadataItem( "CANOPY_HT_UNIT_NAME", "Meters" ); + if ( nTemp == 2 ) + poBand->SetMetadataItem( "CANOPY_HT_UNIT_NAME", "Feet" ); + if ( nTemp == 3 ) + poBand->SetMetadataItem( "CANOPY_HT_UNIT_NAME", "Meters x 10" ); + if ( nTemp == 4 ) + poBand->SetMetadataItem( "CANOPY_HT_UNIT_NAME", "Feet x 10" ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2104); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CANOPY_HT_MIN", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2108); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CANOPY_HT_MAX", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2112); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CANOPY_HT_NUM_CLASSES", szTemp ); + + *(poDS->pachHeader + 5524 + 255) = '\0'; + poBand->SetMetadataItem( "CANOPY_HT_FILE", poDS->pachHeader + 5524 ); + } + else { + poBand->SetDescription("Duff"); + + nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4240); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "DUFF_UNIT", szTemp ); + + if ( nTemp == 1 ) + poBand->SetMetadataItem( "DUFF_UNIT_NAME", "Mg/ha" ); + if ( nTemp == 2 ) + poBand->SetMetadataItem( "DUFF_UNIT_NAME", "t/ac" ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3340); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "DUFF_MIN", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3344); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "DUFF_MAX", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3348); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "DUFF_NUM_CLASSES", szTemp ); + + *(poDS->pachHeader + 6292 + 255) = '\0'; + poBand->SetMetadataItem( "DUFF_FILE", poDS->pachHeader + 6292 ); + } + break; + + case 7: + if(bHaveCrownFuels) { + poBand->SetDescription("Canopy base height"); + + nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4236); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CBH_UNIT", szTemp ); + + if ( nTemp == 1 ) + poBand->SetMetadataItem( "CBH_UNIT_NAME", "Meters" ); + if ( nTemp == 2 ) + poBand->SetMetadataItem( "CBH_UNIT_NAME", "Feet" ); + if ( nTemp == 3 ) + poBand->SetMetadataItem( "CBH_UNIT_NAME", "Meters x 10" ); + if ( nTemp == 4 ) + poBand->SetMetadataItem( "CBH_UNIT_NAME", "Feet x 10" ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2516); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CBH_MIN", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2520); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CBH_MAX", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2524); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CBH_NUM_CLASSES", szTemp ); + + *(poDS->pachHeader + 5780 + 255) = '\0'; + poBand->SetMetadataItem( "CBH_FILE", poDS->pachHeader + 5780 ); + } + else { + poBand->SetDescription("Coarse woody debris"); + + nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4242); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CWD_OPTION", szTemp ); + + //if ( nTemp == 1 ) + // poBand->SetMetadataItem( "CWD_UNIT_DESC", "?" ); + //if ( nTemp == 2 ) + // poBand->SetMetadataItem( "CWD_UNIT_DESC", "?" ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3752); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CWD_MIN", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3756); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CWD_MAX", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3760); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CWD_NUM_CLASSES", szTemp ); + + *(poDS->pachHeader + 6548 + 255) = '\0'; + poBand->SetMetadataItem( "CWD_FILE", poDS->pachHeader + 6548 ); + } + break; + + case 8: + poBand->SetDescription("Canopy bulk density"); + + nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4238); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CBD_UNIT", szTemp ); + + if ( nTemp == 1 ) + poBand->SetMetadataItem( "CBD_UNIT_NAME", "kg/m^3" ); + if ( nTemp == 2 ) + poBand->SetMetadataItem( "CBD_UNIT_NAME", "lb/ft^3" ); + if ( nTemp == 3 ) + poBand->SetMetadataItem( "CBD_UNIT_NAME", "kg/m^3 x 100" ); + if ( nTemp == 4 ) + poBand->SetMetadataItem( "CBD_UNIT_NAME", "lb/ft^3 x 1000" ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2928); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CBD_MIN", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2932); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CBD_MAX", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2936); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CBD_NUM_CLASSES", szTemp ); + + *(poDS->pachHeader + 6036 + 255) = '\0'; + poBand->SetMetadataItem( "CBD_FILE", poDS->pachHeader + 6036 ); + + break; + + case 9: + poBand->SetDescription("Duff"); + + nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4240); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "DUFF_UNIT", szTemp ); + + if ( nTemp == 1 ) + poBand->SetMetadataItem( "DUFF_UNIT_NAME", "Mg/ha" ); + if ( nTemp == 2 ) + poBand->SetMetadataItem( "DUFF_UNIT_NAME", "t/ac" ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3340); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "DUFF_MIN", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3344); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "DUFF_MAX", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3348); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "DUFF_NUM_CLASSES", szTemp ); + + *(poDS->pachHeader + 6292 + 255) = '\0'; + poBand->SetMetadataItem( "DUFF_FILE", poDS->pachHeader + 6292 ); + + break; + + case 10: + poBand->SetDescription("Coarse woody debris"); + + nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4242); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CWD_OPTION", szTemp ); + + //if ( nTemp == 1 ) + // poBand->SetMetadataItem( "CWD_UNIT_DESC", "?" ); + //if ( nTemp == 2 ) + // poBand->SetMetadataItem( "CWD_UNIT_DESC", "?" ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3752); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CWD_MIN", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3756); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CWD_MAX", szTemp ); + + nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3760); + sprintf(szTemp, "%d", nTemp); + poBand->SetMetadataItem( "CWD_NUM_CLASSES", szTemp ); + + *(poDS->pachHeader + 6548 + 255) = '\0'; + poBand->SetMetadataItem( "CWD_FILE", poDS->pachHeader + 6548 ); + + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Try to read projection file. */ +/* -------------------------------------------------------------------- */ + char *pszDirname, *pszBasename; + VSIStatBufL sStatBuf; + + pszDirname = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename)); + pszBasename = CPLStrdup(CPLGetBasename(poOpenInfo->pszFilename)); + + poDS->osPrjFilename = CPLFormFilename( pszDirname, pszBasename, "prj" ); + int nRet = VSIStatL( poDS->osPrjFilename, &sStatBuf ); + + if( nRet != 0 && VSIIsCaseSensitiveFS(poDS->osPrjFilename)) + { + poDS->osPrjFilename = CPLFormFilename( pszDirname, pszBasename, "PRJ" ); + nRet = VSIStatL( poDS->osPrjFilename, &sStatBuf ); + } + + if( nRet == 0 ) + { + OGRSpatialReference oSRS; + + char** papszPrj = CSLLoad( poDS->osPrjFilename ); + + CPLDebug( "LCP", "Loaded SRS from %s", + poDS->osPrjFilename.c_str() ); + + if( oSRS.importFromESRI( papszPrj ) == OGRERR_NONE ) + { + CPLFree( poDS->pszProjection ); + oSRS.exportToWkt( &(poDS->pszProjection) ); + poDS->bHaveProjection = TRUE; + } + + CSLDestroy(papszPrj); + } + + CPLFree( pszDirname ); + CPLFree( pszBasename ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + CPLFree(pszList); + + return( poDS ); +} + +/************************************************************************/ +/* ClassifyBandData() */ +/* Classify a band and store 99 or fewer unique values. If there are */ +/* more than 99 unique values, then set pnNumClasses to -1 as a flag */ +/* that represents this. These are legacy values in the header, and */ +/* while we should never deprecate them, we could possibly not */ +/* calculate them by default. */ +/************************************************************************/ + +CPLErr LCPDataset::ClassifyBandData( GDALRasterBand *poBand, + GInt32 *pnNumClasses, + GInt32 *panClasses ) +{ + if( pnNumClasses == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid pointer for panClasses" ); + return CE_Failure; + } + + if( panClasses == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid pointer for panClasses" ); + *pnNumClasses = -1; + return CE_Failure; + } + + if( poBand == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid band passed to ClassifyBandData()" ); + *pnNumClasses = -1; + memset( panClasses, 0, 400 ); + return CE_Failure; + } + + int nXSize = poBand->GetXSize(); + int nYSize = poBand->GetYSize(); + double dfMax, dfDummy; + poBand->GetStatistics( FALSE, TRUE, &dfDummy, &dfMax, &dfDummy, &dfDummy ); + + int nSpan = (int)dfMax; + GInt16 *panValues = (GInt16*)CPLMalloc( sizeof( GInt16 ) * nXSize ); + GByte *pabyFlags = (GByte*)CPLMalloc( sizeof( GByte ) * nSpan + 1 ); + memset( pabyFlags, 0, nSpan + 1 ); + + int nFound = 0; + int bTooMany = FALSE; + CPLErr eErr = CE_None; + for( int iLine = 0; iLine < nYSize; iLine++ ) + { + eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + panValues, nXSize, 1, + GDT_Int16, 0, 0, NULL ); + for( int iPixel = 0; iPixel < nXSize; iPixel++ ) + { + if( panValues[iPixel] == -9999 ) + { + continue; + } + if( nFound > 99 ) + { + CPLDebug( "LCP", "Found more that 100 unique values in " \ + "band %d. Not 'classifying' the data.", + poBand->GetBand() ); + nFound = -1; + bTooMany = TRUE; + break; + } + if( bTooMany ) + { + break; + } + if( pabyFlags[panValues[iPixel]] == 0 ) + { + pabyFlags[panValues[iPixel]] = 1; + nFound++; + } + } + } + CPLAssert( nFound <= 100 ); + /* + ** The classes are always padded with a leading 0. This was for aligning + ** offsets, or making it a 1-based array instead of 0-based. + */ + panClasses[0] = 0; + int nIndex = 1; + for( int j = 0; j < nSpan + 1; j++ ) + { + if( pabyFlags[j] == 1 ) + { + panClasses[nIndex++] = j; + } + } + *pnNumClasses = nFound; + CPLFree( (void*)pabyFlags ); + CPLFree( (void*)panValues ); + + return eErr; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset *LCPDataset::CreateCopy( const char * pszFilename, + GDALDataset * poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + +/* -------------------------------------------------------------------- */ +/* Verify input options. */ +/* -------------------------------------------------------------------- */ + if( nBands != 5 && nBands != 7 && nBands != 8 && nBands != 10 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "LCP driver doesn't support %d bands. Must be 5, 7, 8 " + "or 10 bands.", nBands ); + return NULL; + } + + GDALDataType eType = poSrcDS->GetRasterBand( 1 )->GetRasterDataType(); + if( eType != GDT_Int16 && bStrict ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "LCP only supports 16-bit signed integer data types." ); + return NULL; + } + else if( eType != GDT_Int16 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Setting data type to 16-bit integer." ); + } + +/* -------------------------------------------------------------------- */ +/* What schema do we have (ground/crown fuels) */ +/* -------------------------------------------------------------------- */ + int bHaveCrownFuels = FALSE; + int bHaveGroundFuels = FALSE; + + if( nBands == 8 || nBands == 10 ) + { + bHaveCrownFuels = TRUE; + } + if( nBands == 7 || nBands == 10 ) + { + bHaveGroundFuels = TRUE; + } + + /* + ** Since units are 'configurable', we should check for user + ** defined units. This is a bit cumbersome, but the user should + ** be allowed to specify none to get default units/options. Use + ** default units every chance we get. + */ + GInt16 panMetadata[LCP_MAX_BANDS]; + int i; + GInt32 nTemp; + double dfTemp; + const char *pszTemp; + + panMetadata[0] = 0; /* ELEVATION_UNIT */ + panMetadata[1] = 0; /* SLOPE_UNIT */ + panMetadata[2] = 2; /* ASPECT_UNIT */ + panMetadata[3] = 0; /* FUEL_MODEL_OPTION */ + panMetadata[4] = 1; /* CANOPY_COV_UNIT */ + panMetadata[5] = 3; /* CANOPY_HT_UNIT */ + panMetadata[6] = 3; /* CBH_UNIT */ + panMetadata[7] = 3; /* CBD_UNIT */ + panMetadata[8] = 1; /* DUFF_UNIT */ + panMetadata[9] = 0; /* CWD_OPTION */ + /* Check the units/options for user overrides */ + pszTemp = CSLFetchNameValueDef( papszOptions, "ELEVATION_UNIT", "METERS" ); + if( EQUALN( pszTemp, "METER", 5 ) ) + { + panMetadata[0] = 0; + } + else if( EQUAL( pszTemp, "FEET" ) || EQUAL( pszTemp, "FOOT" ) ) + { + panMetadata[0] = 1; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value (%s) for ELEVATION_UNIT.", + pszTemp ); + return NULL; + } + + pszTemp = CSLFetchNameValueDef( papszOptions, "SLOPE_UNIT", "DEGREES" ); + if( EQUAL( pszTemp, "DEGREES" ) ) + { + panMetadata[1] = 0; + } + else if( EQUAL( pszTemp, "PERCENT" ) ) + { + panMetadata[1] = 1; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value (%s) for SLOPE_UNIT.", + pszTemp ); + return NULL; + } + + pszTemp = CSLFetchNameValueDef( papszOptions, "ASPECT_UNIT", + "AZIMUTH_DEGREES" ); + if( EQUAL( pszTemp, "GRASS_CATEGORIES" ) ) + { + panMetadata[2] = 0; + } + else if( EQUAL( pszTemp, "GRASS_DEGREES" ) ) + { + panMetadata[2] = 1; + } + else if( EQUAL( pszTemp, "AZIMUTH_DEGREES" ) ) + { + panMetadata[2] = 2; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value (%s) for ASPECT_UNIT.", + pszTemp ); + return NULL; + } + + pszTemp = CSLFetchNameValueDef( papszOptions, "FUEL_MODEL_OPTION", + "NO_CUSTOM_AND_NO_FILE" ); + if( EQUAL( pszTemp, "NO_CUSTOM_AND_NO_FILE" ) ) + { + panMetadata[3] = 0; + } + else if( EQUAL( pszTemp, "CUSTOM_AND_NO_FILE" ) ) + { + panMetadata[3] = 1; + } + else if( EQUAL( pszTemp, "NO_CUSTOM_AND_FILE" ) ) + { + panMetadata[3] = 2; + } + else if( EQUAL( pszTemp, "CUSTOM_AND_FILE" ) ) + { + panMetadata[3] = 3; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value (%s) for FUEL_MODEL_OPTION.", + pszTemp ); + return NULL; + } + + pszTemp = CSLFetchNameValueDef( papszOptions, "CANOPY_COV_UNIT", + "PERCENT" ); + if( EQUAL( pszTemp, "CATEGORIES" ) ) + { + panMetadata[4] = 0; + } + else if( EQUAL( pszTemp, "PERCENT" ) ) + { + panMetadata[4] = 1; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value (%s) for CANOPY_COV_UNIT.", + pszTemp ); + return NULL; + } + + if( bHaveCrownFuels ) + { + pszTemp = CSLFetchNameValueDef( papszOptions, "CANOPY_HT_UNIT", + "METERS_X_10" ); + if( EQUAL( pszTemp, "METERS" ) || EQUAL( pszTemp, "METER" ) ) + { + panMetadata[5] = 1; + } + else if( EQUAL( pszTemp, "FEET" ) || EQUAL( pszTemp, "FOOT" ) ) + { + panMetadata[5] = 2; + } + else if( EQUAL( pszTemp, "METERS_X_10" ) || + EQUAL( pszTemp, "METER_X_10" ) ) + { + panMetadata[5] = 3; + } + else if( EQUAL( pszTemp, "FEET_X_10" ) || EQUAL( pszTemp, "FOOT_X_10" ) ) + { + panMetadata[5] = 4; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value (%s) for CANOPY_HT_UNIT.", + pszTemp ); + return NULL; + } + + pszTemp = CSLFetchNameValueDef( papszOptions, "CBH_UNIT", + "METERS_X_10" ); + if( EQUAL( pszTemp, "METERS" ) || EQUAL( pszTemp, "METER" ) ) + { + panMetadata[6] = 1; + } + else if( EQUAL( pszTemp, "FEET" ) || EQUAL( pszTemp, "FOOT" ) ) + { + panMetadata[6] = 2; + } + else if( EQUAL( pszTemp, "METERS_X_10" ) || + EQUAL( pszTemp, "METER_X_10" ) ) + { + panMetadata[6] = 3; + } + else if( EQUAL( pszTemp, "FEET_X_10" ) || EQUAL( pszTemp, "FOOT_X_10" ) ) + { + panMetadata[6] = 4; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value (%s) for CBH_UNIT.", + pszTemp ); + return NULL; + } + + pszTemp = CSLFetchNameValueDef( papszOptions, "CBD_UNIT", + "KG_PER_CUBIC_METER_X_100" ); + if( EQUAL( pszTemp, "KG_PER_CUBIC_METER" ) ) + { + panMetadata[7] = 1; + } + else if( EQUAL( pszTemp, "POUND_PER_CUBIC_FOOT" ) ) + { + panMetadata[7] = 2; + } + else if( EQUAL( pszTemp, "KG_PER_CUBIC_METER_X_100" ) ) + { + panMetadata[7] = 3; + } + else if( EQUAL( pszTemp, "POUND_PER_CUBIC_FOOT_X_1000" ) ) + { + panMetadata[7] = 4; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value (%s) for CBD_UNIT.", + pszTemp ); + return NULL; + } + } + + if( bHaveGroundFuels ) + { + pszTemp = CSLFetchNameValueDef( papszOptions, "DUFF_UNIT", + "MG_PER_HECTARE_X_10" ); + if( EQUAL( pszTemp, "MG_PER_HECTARE_X_10" ) ) + { + panMetadata[8] = 1; + } + else if ( EQUAL( pszTemp, "TONS_PER_ACRE_X_10" ) ) + { + panMetadata[8] = 2; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value (%s) for DUFF_UNIT.", + pszTemp ); + return NULL; + } + + if( bHaveGroundFuels ) + { + panMetadata[9] = 1; + } + else + { + panMetadata[9] = 0; + } + } + + /* + ** Calculate the stats for each band. The binary file carries along + ** these metadata for display purposes(?). + */ + int bCalculateStats = CSLFetchBoolean( papszOptions, "CALCULATE_STATS", + TRUE ); + int bClassifyData = CSLFetchBoolean( papszOptions, "CLASSIFY_DATA", + TRUE ); + /* + ** We should have stats if we classify, we'll get them anyway. + */ + if( bClassifyData && !bCalculateStats ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Ignoring request to not calculate statistics, " \ + "because CLASSIFY_DATA was set to ON" ); + bCalculateStats = TRUE; + } + + pszTemp = CSLFetchNameValueDef( papszOptions, "LINEAR_UNIT", + "SET_FROM_SRS" ); + int nLinearUnits = 0; + int bSetLinearUnits = FALSE; + if( EQUAL( pszTemp, "SET_FROM_SRS" ) ) + { + bSetLinearUnits = TRUE; + } + else if( EQUALN( pszTemp, "METER", 5 ) ) + { + nLinearUnits = 0; + } + else if( EQUAL( pszTemp, "FOOT" ) || EQUAL( pszTemp, "FEET" ) ) + { + nLinearUnits = 1; + } + else if( EQUALN( pszTemp, "KILOMETER", 9 ) ) + { + nLinearUnits = 2; + } + int bCalculateLatitude = TRUE; + int nLatitude; + if( CSLFetchNameValue( papszOptions, "LATITUDE" ) != NULL ) + { + bCalculateLatitude = FALSE; + nLatitude = atoi( CSLFetchNameValue( papszOptions, "LATITUDE" ) ); + if( nLatitude > 90 || nLatitude < -90 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Invalid value (%d) for LATITUDE.", nLatitude ); + return NULL; + } + } + /* + ** If no latitude is supplied, attempt to extract the central latitude + ** from the image. It must be set either manually or here, otherwise + ** we fail. + */ + double adfSrcGeoTransform[6]; + poSrcDS->GetGeoTransform( adfSrcGeoTransform ); + OGRSpatialReference oSrcSRS, oDstSRS; + const char *pszWkt = poSrcDS->GetProjectionRef(); + double dfLongitude = 0.0; + double dfLatitude = 0.0; + if( !bCalculateLatitude ) + { + dfLatitude = nLatitude; + } + else if( !EQUAL( pszWkt, "" ) ) + { + oSrcSRS.importFromWkt( (char**)&pszWkt ); + oDstSRS.importFromEPSG( 4269 ); + OGRCoordinateTransformation *poCT; + poCT = (OGRCoordinateTransformation*) + OGRCreateCoordinateTransformation( &oSrcSRS, &oDstSRS ); + int nErr; + if( poCT != NULL ) + { + dfLatitude = adfSrcGeoTransform[3] + adfSrcGeoTransform[5] * nYSize / 2; + nErr = (int)poCT->Transform( 1, &dfLongitude, &dfLatitude ); + if( !nErr ) + { + dfLatitude = 0.0; + /* + ** For the most part, this is an invalid LCP, but it is a + ** changeable value in Flammap/Farsite, etc. We should + ** probably be strict here all the time. + */ + CPLError( CE_Failure, CPLE_AppDefined, + "Could not calculate latitude from spatial " \ + "reference and LATITUDE was not set." ); + return NULL; + } + } + OGRCoordinateTransformation::DestroyCT( poCT ); + } + else + { + /* + ** See comment above on failure to transform. + */ + CPLError( CE_Failure, CPLE_AppDefined, + "Could not calculate latitude from spatial reference " \ + "and LATITUDE was not set." ); + return NULL; + } + /* + ** Set the linear units if the metadata item wasn't already set, and we + ** have an SRS. + */ + if( bSetLinearUnits && !EQUAL( pszWkt, "" ) ) + { + const char *pszUnit; + pszUnit = oSrcSRS.GetAttrValue( "UNIT", 0 ); + if( pszUnit == NULL ) + { + if( bStrict ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Could not parse linear unit." ); + return NULL; + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Could not parse linear unit, using meters" ); + nLinearUnits = 0; + } + } + else + { + CPLDebug( "LCP", "Setting linear unit to %s", pszUnit ); + if( EQUAL( pszUnit, "meter" ) || EQUAL( pszUnit, "metre" ) ) + { + nLinearUnits = 0; + } + else if( EQUAL( pszUnit, "feet" ) || EQUAL( pszUnit, "foot" ) ) + { + nLinearUnits = 1; + } + else if( EQUALN( pszUnit, "kilomet", 7 ) ) + { + nLinearUnits = 2; + } + else + { + if( bStrict ) + nLinearUnits = 0; + } + pszUnit = oSrcSRS.GetAttrValue( "UNIT", 1 ); + if( pszUnit != NULL ) + { + double dfScale = CPLAtof( pszUnit ); + if( dfScale != 1.0 ) + { + if( bStrict ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unit scale is %lf (!=1.0). It is not " \ + "supported.", dfScale ); + return NULL; + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unit scale is %lf (!=1.0). It is not " \ + "supported, ignoring.", dfScale ); + } + } + } + } + } + else if( bSetLinearUnits ) + { + /* + ** This can be defaulted if it isn't a strict creation. + */ + if( bStrict ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Could not parse linear unit from spatial reference " + "and LINEAR_UNIT was not set." ); + return NULL; + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Could not parse linear unit from spatial reference " + "and LINEAR_UNIT was not set, defaulting to meters." ); + nLinearUnits = 0; + } + } + + const char *pszDescription = + CSLFetchNameValueDef( papszOptions, "DESCRIPTION", + "LCP file created by GDAL." ); + + /* + ** Loop through and get the stats for the bands if we need to calculate + ** them. This probably should be done when we copy the data over to the + ** destination dataset, since we load the values into memory, but this is + ** much simpler code using GDALRasterBand->GetStatistics(). We also may + ** need to classify the data (number of unique values and a list of those + ** values if the number of unique values is > 100. It is currently unclear + ** how these data are used though, so we will implement that at some point + ** if need be. + */ + GDALRasterBand *poBand; + double *padfMin = (double*)CPLMalloc( sizeof( double ) * nBands ); + double *padfMax = (double*)CPLMalloc( sizeof( double ) * nBands ); + double dfDummy; + GInt32 *panFound = (GInt32*)VSIMalloc2( sizeof( GInt32 ), nBands ); + GInt32 *panClasses = (GInt32*)VSIMalloc3( sizeof( GInt32 ), nBands, LCP_MAX_CLASSES ); + /* + ** Initialize these arrays to zeros + */ + memset( panFound, 0, sizeof( GInt32 ) * nBands ); + memset( panClasses, 0, sizeof( GInt32 ) * nBands * LCP_MAX_CLASSES ); + + CPLErr eErr; + if( bCalculateStats ) + { + for( i = 0; i < nBands; i++ ) + { + poBand = poSrcDS->GetRasterBand( i + 1 ); + eErr = poBand->GetStatistics( FALSE, TRUE, &padfMin[i], + &padfMax[i], &dfDummy, &dfDummy ); + if( eErr != CE_None ) + { + CPLError( CE_Warning, CPLE_AppDefined, "Failed to properly " \ + "calculate statistics " + "on band %d", i ); + padfMin[i] = 0.0; + padfMax[i] = 0.0; + } + /* + ** See comment above. + */ + if( bClassifyData ) + { + eErr = ClassifyBandData( poBand, panFound+ i, + panClasses + ( i * LCP_MAX_CLASSES ) ); + } + } + } + + VSILFILE *fp; + + fp = VSIFOpenL( pszFilename, "wb" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create lcp file %s.", pszFilename ); + CPLFree( padfMin ); + CPLFree( padfMax ); + CPLFree( panFound ); + CPLFree( panClasses ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write the header */ +/* -------------------------------------------------------------------- */ + + nTemp = bHaveCrownFuels ? 21 : 20; + CPL_LSBINT32PTR( &nTemp ); + VSIFWriteL( &nTemp, 4, 1, fp ); + nTemp = bHaveGroundFuels ? 21 : 20; + CPL_LSBINT32PTR( &nTemp ); + VSIFWriteL( &nTemp, 4, 1, fp ); + + nTemp = (GInt32)( dfLatitude + 0.5 ); + CPL_LSBINT32PTR( &nTemp ); + VSIFWriteL( &nTemp, 4, 1, fp ); + dfLongitude = adfSrcGeoTransform[0] + adfSrcGeoTransform[1] * nXSize; + CPL_LSBPTR64( &dfLongitude ); + VSIFWriteL( &dfLongitude, 8, 1, fp ); + dfLongitude = adfSrcGeoTransform[0]; + CPL_LSBPTR64( &dfLongitude ); + VSIFWriteL( &dfLongitude, 8, 1, fp ); + dfLatitude = adfSrcGeoTransform[3]; + CPL_LSBPTR64( &dfLatitude ); + VSIFWriteL( &dfLatitude, 8, 1, fp ); + dfLatitude = adfSrcGeoTransform[3] + adfSrcGeoTransform[5] * nYSize; + CPL_LSBPTR64( &dfLatitude ); + VSIFWriteL( &dfLatitude, 8, 1, fp ); + + /* + ** Swap the two classification arrays if we are writing them, and they need + ** to be swapped. + */ +#ifdef CPL_MSB + if( bClassifyData ) + { + GDALSwapWords( panFound, 2, nBands, 2 ); + GDALSwapWords( panClasses, 2, LCP_MAX_CLASSES, 2 ); + } +#endif + + if( bCalculateStats ) + { + for( i = 0; i < nBands; i++ ) + { + /* + ** If we don't have Crown fuels, but do have Ground fuels, we + ** have to 'fast forward'. + */ + if( i == 5 && !bHaveCrownFuels && bHaveGroundFuels ) + { + VSIFSeekL( fp, 3340, SEEK_SET ); + } + nTemp = (GInt32)padfMin[i]; + CPL_LSBINT32PTR( &nTemp ); + VSIFWriteL( &nTemp, 4, 1, fp ); + nTemp = (GInt32)padfMax[i]; + CPL_LSBINT32PTR( &nTemp ); + VSIFWriteL( &nTemp, 4, 1, fp ); + if( bClassifyData ) + { + /* + ** These two arrays were swapped in their entirety above. + */ + VSIFWriteL( panFound + i, 4, 1, fp ); + VSIFWriteL( panClasses + ( i * LCP_MAX_CLASSES ), 4, 100, fp ); + } + else + { + nTemp = -1; + CPL_LSBINT32PTR( &nTemp ); + VSIFWriteL( &nTemp, 4, 1, fp ); + VSIFSeekL( fp, 400, SEEK_CUR ); + } + } + } + else + { + VSIFSeekL( fp, 4164, SEEK_SET ); + } + CPLFree( (void*)padfMin ); + CPLFree( (void*)padfMax ); + CPLFree( (void*)panFound ); + CPLFree( (void*)panClasses ); + + /* + ** Should be at one of 3 locations, 2104, 3340, or 4164. + */ + CPLAssert( VSIFTellL( fp ) == 2104 || + VSIFTellL( fp ) == 3340 || + VSIFTellL( fp ) == 4164 ); + VSIFSeekL( fp, 4164, SEEK_SET ); + + /* Image size */ + nTemp = (GInt32)nXSize; + CPL_LSBINT32PTR( &nTemp ); + VSIFWriteL( &nTemp, 4, 1, fp ); + nTemp = (GInt32)nYSize; + CPL_LSBINT32PTR( &nTemp ); + VSIFWriteL( &nTemp, 4, 1, fp ); + + /* X and Y boundaries */ + /* max x */ + dfTemp = adfSrcGeoTransform[0] + adfSrcGeoTransform[1] * nXSize; + CPL_LSBPTR64( &dfTemp ); + VSIFWriteL( &dfTemp, 8, 1, fp ); + /* min x */ + dfTemp = adfSrcGeoTransform[0]; + CPL_LSBPTR64( &dfTemp ); + VSIFWriteL( &dfTemp, 8, 1, fp ); + /* max y */ + dfTemp = adfSrcGeoTransform[3]; + CPL_LSBPTR64( &dfTemp ); + VSIFWriteL( &dfTemp, 8, 1, fp ); + /* min y */ + dfTemp = adfSrcGeoTransform[3] + adfSrcGeoTransform[5] * nYSize; + CPL_LSBPTR64( &dfTemp ); + VSIFWriteL( &dfTemp, 8, 1, fp ); + + nTemp = nLinearUnits; + CPL_LSBINT32PTR( &nTemp ); + VSIFWriteL( &nTemp, 4, 1, fp ); + + /* Resolution */ + /* x resolution */ + dfTemp = adfSrcGeoTransform[1]; + CPL_LSBPTR64( &dfTemp ); + VSIFWriteL( &dfTemp, 8, 1, fp ); + /* y resolution */ + dfTemp = fabs( adfSrcGeoTransform[5] ); + CPL_LSBPTR64( &dfTemp ); + VSIFWriteL( &dfTemp, 8, 1, fp ); + +#ifdef CPL_MSB + GDALSwapWords( panMetadata, 2, LCP_MAX_BANDS, 2 ); +#endif + VSIFWriteL( panMetadata, 2, LCP_MAX_BANDS, fp ); + + /* Write the source filenames */ + char **papszFileList = poSrcDS->GetFileList(); + if( papszFileList != NULL ) + { + for( i = 0; i < nBands; i++ ) + { + if( i == 5 && !bHaveCrownFuels && bHaveGroundFuels ) + { + VSIFSeekL( fp, 6292, SEEK_SET ); + } + VSIFWriteL( papszFileList[0], 1, + CPLStrnlen( papszFileList[0], LCP_MAX_PATH ), fp ); + VSIFSeekL( fp, 4244 + ( 256 * ( i+1 ) ), SEEK_SET ); + } + } + /* + ** No file list, mem driver, etc. + */ + else + { + VSIFSeekL( fp, 6804, SEEK_SET ); + } + CSLDestroy( papszFileList ); + /* + ** Should be at location 5524, 6292 or 6804. + */ + CPLAssert( VSIFTellL( fp ) == 5524 || + VSIFTellL( fp ) == 6292 || + VSIFTellL( fp ) == 6804 ); + VSIFSeekL( fp, 6804, SEEK_SET ); + + /* Description */ + VSIFWriteL( pszDescription, 1, CPLStrnlen( pszDescription, LCP_MAX_DESC ), + fp ); + /* + ** Should be at or below location 7316, all done with the header. + */ + CPLAssert( VSIFTellL( fp ) <= 7316 ); + VSIFSeekL( fp, 7316, SEEK_SET ); + +/* -------------------------------------------------------------------- */ +/* Loop over image, copying image data. */ +/* -------------------------------------------------------------------- */ + + GInt16 *panScanline = (GInt16 *)VSIMalloc3( 2, nBands, nXSize ); + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + VSIFCloseL( fp ); + VSIFree( (void*)panScanline ); + return NULL; + } + for( int iLine = 0; iLine < nYSize; iLine++ ) + { + for( int iBand = 0; iBand < nBands; iBand++ ) + { + GDALRasterBand * poBand = poSrcDS->GetRasterBand( iBand+1 ); + eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + panScanline + iBand, nXSize, 1, GDT_Int16, + nBands * 2, nBands * nXSize * 2, NULL ); + /* Not sure what to do here */ + if( eErr != CE_None ) + { + CPLError( CE_Warning, CPLE_AppDefined, "Error reported in " \ + "RasterIO" ); + /* + ** CPLError( eErr, CPLE_AppDefined, + ** "Error reported in RasterIO" ); + */ + } + } +#ifdef CPL_MSB + GDALSwapWords( panScanline, 2, nBands * nXSize, 2 ); +#endif + VSIFWriteL( panScanline, 2, nBands * nXSize, fp ); + + if( !pfnProgress( iLine / (double)nYSize, NULL, pProgressData ) ) + { + VSIFree( (void*)panScanline ); + VSIFCloseL( fp ); + return NULL; + } + } + VSIFree( panScanline ); + VSIFCloseL( fp ); + if( !pfnProgress( 1.0, NULL, pProgressData ) ) + { + return NULL; + } + + /* + ** Try to write projection file. *Most* landfire data follows ESRI + **style projection files, so we use the same code as the AAIGrid driver. + */ + const char *pszOriginalProjection; + + pszOriginalProjection = (char *)poSrcDS->GetProjectionRef(); + if( !EQUAL( pszOriginalProjection, "" ) ) + { + char *pszDirname, *pszBasename; + char *pszPrjFilename; + char *pszESRIProjection = NULL; + VSILFILE *fp; + OGRSpatialReference oSRS; + + pszDirname = CPLStrdup( CPLGetPath(pszFilename) ); + pszBasename = CPLStrdup( CPLGetBasename(pszFilename) ); + + pszPrjFilename = CPLStrdup( CPLFormFilename( pszDirname, pszBasename, "prj" ) ); + fp = VSIFOpenL( pszPrjFilename, "wt" ); + if (fp != NULL) + { + oSRS.importFromWkt( (char **) &pszOriginalProjection ); + oSRS.morphToESRI(); + oSRS.exportToWkt( &pszESRIProjection ); + VSIFWriteL( pszESRIProjection, 1, strlen(pszESRIProjection), fp ); + + VSIFCloseL( fp ); + CPLFree( pszESRIProjection ); + } + else + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to create file %s.", pszPrjFilename ); + } + CPLFree( pszDirname ); + CPLFree( pszBasename ); + CPLFree( pszPrjFilename ); + } + return (GDALDataset *) GDALOpen( pszFilename, GA_ReadOnly ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *LCPDataset::GetProjectionRef() + +{ + return pszProjection; +} + +/************************************************************************/ +/* GDALRegister_LCP() */ +/************************************************************************/ + +void GDALRegister_LCP() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "LCP" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "LCP" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "FARSITE v.4 Landscape File (.lcp)" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "lcp" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_lcp.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Int16" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" " +" " +" " +" " +" " +" " +" " +" " +/* I don't think we need to override this, but maybe? */ +/*" " +" " ); + poDriver->pfnOpen = LCPDataset::Open; + poDriver->pfnCreateCopy = LCPDataset::CreateCopy; + poDriver->pfnIdentify = LCPDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/raw/loslasdataset.cpp b/bazaar/plugin/gdal/frmts/raw/loslasdataset.cpp new file mode 100644 index 000000000..52c2368b7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/loslasdataset.cpp @@ -0,0 +1,287 @@ +/****************************************************************************** + * $Id: loslasdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: Horizontal Datum Formats + * Purpose: Implementation of NOAA/NADCON los/las datum shift format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * Financial Support: i-cubed (http://www.i-cubed.com) + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: loslasdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/** + +NOAA .LOS/.LAS Datum Grid Shift Format + +All values are little endian + +Header +------ + +char[56] "NADCON EXTRACTED REGION" +char[8] "NADGRD " +int32 grid width +int32 grid height +int32 z count (1) +float32 origin longitude +float32 grid cell width longitude +float32 origin latitude +float32 grid cell height latitude +float32 angle (0.0) + +Data +---- + +int32 ? always 0 +float32*gridwidth offset in arcseconds. + +Note that the record length is always gridwidth*4 + 4, and +even the header record is this length though it means some waste. + +**/ + +/************************************************************************/ +/* ==================================================================== */ +/* LOSLASDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class LOSLASDataset : public RawDataset +{ + public: + VSILFILE *fpImage; // image data file. + + int nRecordLength; + + double adfGeoTransform[6]; + + public: + LOSLASDataset(); + ~LOSLASDataset(); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual const char *GetProjectionRef(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* LOSLASDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* LOSLASDataset() */ +/************************************************************************/ + +LOSLASDataset::LOSLASDataset() +{ + fpImage = NULL; +} + +/************************************************************************/ +/* ~LOSLASDataset() */ +/************************************************************************/ + +LOSLASDataset::~LOSLASDataset() + +{ + FlushCache(); + + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int LOSLASDataset::Identify( GDALOpenInfo *poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 64 ) + return FALSE; + + if( !EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"las") + && !EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"los") ) + return FALSE; + + if( !EQUALN((const char *)poOpenInfo->pabyHeader + 56, "NADGRD", 6 ) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *LOSLASDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + LOSLASDataset *poDS; + + poDS = new LOSLASDataset(); + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + + if( poDS->fpImage == NULL ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( poDS->fpImage, 64, SEEK_SET ); + + VSIFReadL( &(poDS->nRasterXSize), 4, 1, poDS->fpImage ); + VSIFReadL( &(poDS->nRasterYSize), 4, 1, poDS->fpImage ); + + CPL_LSBPTR32( &(poDS->nRasterXSize) ); + CPL_LSBPTR32( &(poDS->nRasterYSize) ); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + + VSIFSeekL( poDS->fpImage, 76, SEEK_SET ); + + float min_lon, min_lat, delta_lon, delta_lat; + + VSIFReadL( &min_lon, 4, 1, poDS->fpImage ); + VSIFReadL( &delta_lon, 4, 1, poDS->fpImage ); + VSIFReadL( &min_lat, 4, 1, poDS->fpImage ); + VSIFReadL( &delta_lat, 4, 1, poDS->fpImage ); + + CPL_LSBPTR32( &min_lon ); + CPL_LSBPTR32( &delta_lon ); + CPL_LSBPTR32( &min_lat ); + CPL_LSBPTR32( &delta_lat ); + + poDS->nRecordLength = poDS->nRasterXSize * 4 + 4; + +/* -------------------------------------------------------------------- */ +/* Create band information object. */ +/* */ +/* Note we are setting up to read from the last image record to */ +/* the first since the data comes with the southern most record */ +/* first, not the northernmost like we would want. */ +/* -------------------------------------------------------------------- */ + poDS->SetBand( + 1, new RawRasterBand( poDS, 1, poDS->fpImage, + poDS->nRasterYSize * poDS->nRecordLength + 4, + 4, -1 * poDS->nRecordLength, + GDT_Float32, + CPL_IS_LSB, TRUE, FALSE ) ); + +/* -------------------------------------------------------------------- */ +/* Setup georeferencing. */ +/* -------------------------------------------------------------------- */ + poDS->adfGeoTransform[0] = min_lon - delta_lon*0.5; + poDS->adfGeoTransform[1] = delta_lon; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = min_lat + (poDS->nRasterYSize-0.5) * delta_lat; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = -1 * delta_lat; + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr LOSLASDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *LOSLASDataset::GetProjectionRef() + +{ + return SRS_WKT_WGS84; +} + +/************************************************************************/ +/* GDALRegister_LOSLAS() */ +/************************************************************************/ + +void GDALRegister_LOSLAS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "LOSLAS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "LOSLAS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "NADCON .los/.las Datum Grid Shift" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = LOSLASDataset::Open; + poDriver->pfnIdentify = LOSLASDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/raw/makefile.vc b/bazaar/plugin/gdal/frmts/raw/makefile.vc new file mode 100644 index 000000000..0d7cd724e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/makefile.vc @@ -0,0 +1,21 @@ + +OBJ = rawdataset.obj ehdrdataset.obj pauxdataset.obj doq1dataset.obj\ + hkvdataset.obj mffdataset.obj pnmdataset.obj \ + fujibasdataset.obj doq2dataset.obj envidataset.obj \ + gscdataset.obj fastdataset.obj atlsci_spheroid.obj \ + btdataset.obj landataset.obj cpgdataset.obj idadataset.obj \ + ndfdataset.obj dipxdataset.obj genbindataset.obj \ + lcpdataset.obj eirdataset.obj gtxdataset.obj loslasdataset.obj \ + ntv2dataset.obj ace2dataset.obj snodasdataset.obj \ + ctable2dataset.obj krodataset.obj roipacdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/raw/mffdataset.cpp b/bazaar/plugin/gdal/frmts/raw/mffdataset.cpp new file mode 100644 index 000000000..754725ec5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/mffdataset.cpp @@ -0,0 +1,1637 @@ +/****************************************************************************** + * $Id: mffdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: GView + * Purpose: Implementation of Atlantis MFF Support + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" +#include +#include "ogr_spatialref.h" +#include "atlsci_spheroid.h" + +CPL_CVSID("$Id: mffdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_MFF(void); +CPL_C_END + +enum { + MFFPRJ_NONE, + MFFPRJ_LL, + MFFPRJ_UTM, + MFFPRJ_UNRECOGNIZED +} ; + +static int GetMFFProjectionType(const char * pszNewProjection); + +/************************************************************************/ +/* ==================================================================== */ +/* MFFDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class MFFDataset : public RawDataset +{ + int nGCPCount; + GDAL_GCP *pasGCPList; + + char *pszProjection; + char *pszGCPProjection; + double adfGeoTransform[6]; + + void ScanForGCPs(); + void ScanForProjectionInfo(); + + + public: + MFFDataset(); + ~MFFDataset(); + + char **papszHdrLines; + + VSILFILE **pafpBandFiles; + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + virtual const char *GetProjectionRef(); + virtual CPLErr GetGeoTransform( double * ); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); + static GDALDataset *CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + +}; + +/************************************************************************/ +/* ==================================================================== */ +/* MFFTiledBand */ +/* ==================================================================== */ +/************************************************************************/ + +class MFFTiledBand : public GDALRasterBand +{ + friend class MFFDataset; + + VSILFILE *fpRaw; + int bNative; + + public: + + MFFTiledBand( MFFDataset *, int, VSILFILE *, int, int, + GDALDataType, int ); + ~MFFTiledBand(); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + + +/************************************************************************/ +/* MFFTiledBand() */ +/************************************************************************/ + +MFFTiledBand::MFFTiledBand( MFFDataset *poDS, int nBand, VSILFILE *fp, + int nTileXSize, int nTileYSize, + GDALDataType eDataType, int bNative ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + this->eDataType = eDataType; + + this->bNative = bNative; + + this->nBlockXSize = nTileXSize; + this->nBlockYSize = nTileYSize; + + this->fpRaw = fp; +} + +/************************************************************************/ +/* ~MFFTiledBand() */ +/************************************************************************/ + +MFFTiledBand::~MFFTiledBand() + +{ + VSIFCloseL( fpRaw ); +} + + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr MFFTiledBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + long nOffset; + int nTilesPerRow; + int nWordSize, nBlockSize; + + nTilesPerRow = (nRasterXSize + nBlockXSize - 1) / nBlockXSize; + nWordSize = GDALGetDataTypeSize( eDataType ) / 8; + nBlockSize = nWordSize * nBlockXSize * nBlockYSize; + + nOffset = nBlockSize * (nBlockXOff + nBlockYOff*nTilesPerRow); + + if( VSIFSeekL( fpRaw, nOffset, SEEK_SET ) == -1 + || VSIFReadL( pImage, 1, nBlockSize, fpRaw ) < 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Read of tile %d/%d failed with fseek or fread error.", + nBlockXOff, nBlockYOff ); + return CE_Failure; + } + + if( !bNative && nWordSize > 1 ) + { + if( GDALDataTypeIsComplex( eDataType ) ) + { + GDALSwapWords( pImage, nWordSize/2, nBlockXSize*nBlockYSize, + nWordSize ); + GDALSwapWords( ((GByte *) pImage)+nWordSize/2, + nWordSize/2, nBlockXSize*nBlockYSize, nWordSize ); + } + else + GDALSwapWords( pImage, nWordSize, + nBlockXSize * nBlockYSize, nWordSize ); + } + + return CE_None; +} + +/************************************************************************/ +/* MFF Spheroids */ +/************************************************************************/ + +class MFFSpheroidList : public SpheroidList +{ + +public: + + MFFSpheroidList(); + ~MFFSpheroidList(); + +}; + +MFFSpheroidList :: MFFSpheroidList() +{ + num_spheroids = 18; + + epsilonR = 0.1; + epsilonI = 0.000001; + + spheroids[0].SetValuesByRadii("SPHERE",6371007.0,6371007.0); + spheroids[1].SetValuesByRadii("EVEREST",6377304.0,6356103.0); + spheroids[2].SetValuesByRadii("BESSEL",6377397.0,6356082.0); + spheroids[3].SetValuesByRadii("AIRY",6377563.0,6356300.0); + spheroids[4].SetValuesByRadii("CLARKE_1858",6378294.0,6356621.0); + spheroids[5].SetValuesByRadii("CLARKE_1866",6378206.4,6356583.8); + spheroids[6].SetValuesByRadii("CLARKE_1880",6378249.0,6356517.0); + spheroids[7].SetValuesByRadii("HAYFORD",6378388.0,6356915.0); + spheroids[8].SetValuesByRadii("KRASOVSKI",6378245.0,6356863.0); + spheroids[9].SetValuesByRadii("HOUGH",6378270.0,6356794.0); + spheroids[10].SetValuesByRadii("FISHER_60",6378166.0,6356784.0); + spheroids[11].SetValuesByRadii("KAULA",6378165.0,6356345.0); + spheroids[12].SetValuesByRadii("IUGG_67",6378160.0,6356775.0); + spheroids[13].SetValuesByRadii("FISHER_68",6378150.0,6356330.0); + spheroids[14].SetValuesByRadii("WGS_72",6378135.0,6356751.0); + spheroids[15].SetValuesByRadii("IUGG_75",6378140.0,6356755.0); + spheroids[16].SetValuesByRadii("WGS_84",6378137.0,6356752.0); + spheroids[17].SetValuesByRadii("HUGHES",6378273.0,6356889.4); +} + +MFFSpheroidList::~MFFSpheroidList() + +{ +} + +/************************************************************************/ +/* ==================================================================== */ +/* MFFDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* MFFDataset() */ +/************************************************************************/ + +MFFDataset::MFFDataset() +{ + papszHdrLines = NULL; + pafpBandFiles = NULL; + nGCPCount = 0; + pasGCPList = NULL; + + pszProjection = CPLStrdup(""); + pszGCPProjection = CPLStrdup(""); + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~MFFDataset() */ +/************************************************************************/ + +MFFDataset::~MFFDataset() + +{ + FlushCache(); + CSLDestroy( papszHdrLines ); + if( pafpBandFiles != NULL ) + { + for( int i = 0; i < GetRasterCount(); i++ ) + { + if( pafpBandFiles[i] != NULL ) + VSIFCloseL( pafpBandFiles[i] ); + } + CPLFree( pafpBandFiles ); + } + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + } + CPLFree( pasGCPList ); + CPLFree( pszProjection ); + CPLFree( pszGCPProjection ); + +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int MFFDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *MFFDataset::GetGCPProjection() + +{ + if( nGCPCount > 0 ) + return pszGCPProjection; + else + return ""; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *MFFDataset::GetProjectionRef() + +{ + return ( pszProjection ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr MFFDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return( CE_None ); +} + +/************************************************************************/ +/* GetGCP() */ +/************************************************************************/ + +const GDAL_GCP *MFFDataset::GetGCPs() + +{ + return pasGCPList; +} + +/************************************************************************/ +/* ScanForGCPs() */ +/************************************************************************/ + +void MFFDataset::ScanForGCPs() + +{ + int nCorner; + int NUM_GCPS = 0; + + if( CSLFetchNameValue(papszHdrLines, "NUM_GCPS") != NULL ) + NUM_GCPS = atoi(CSLFetchNameValue(papszHdrLines, "NUM_GCPS")); + if (NUM_GCPS < 0) + return; + + nGCPCount = 0; + pasGCPList = (GDAL_GCP *) VSICalloc(sizeof(GDAL_GCP),5+NUM_GCPS); + if (pasGCPList == NULL) + return; + + for( nCorner = 0; nCorner < 5; nCorner++ ) + { + const char * pszBase=NULL; + double dfRasterX=0.0, dfRasterY=0.0; + char szLatName[40], szLongName[40]; + + if( nCorner == 0 ) + { + dfRasterX = 0.5; + dfRasterY = 0.5; + pszBase = "TOP_LEFT_CORNER"; + } + else if( nCorner == 1 ) + { + dfRasterX = GetRasterXSize()-0.5; + dfRasterY = 0.5; + pszBase = "TOP_RIGHT_CORNER"; + } + else if( nCorner == 2 ) + { + dfRasterX = GetRasterXSize()-0.5; + dfRasterY = GetRasterYSize()-0.5; + pszBase = "BOTTOM_RIGHT_CORNER"; + } + else if( nCorner == 3 ) + { + dfRasterX = 0.5; + dfRasterY = GetRasterYSize()-0.5; + pszBase = "BOTTOM_LEFT_CORNER"; + } + else if( nCorner == 4 ) + { + dfRasterX = GetRasterXSize()/2.0; + dfRasterY = GetRasterYSize()/2.0; + pszBase = "CENTRE"; + } + + sprintf( szLatName, "%s_LATITUDE", pszBase ); + sprintf( szLongName, "%s_LONGITUDE", pszBase ); + + if( CSLFetchNameValue(papszHdrLines, szLatName) != NULL + && CSLFetchNameValue(papszHdrLines, szLongName) != NULL ) + { + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + + CPLFree( pasGCPList[nGCPCount].pszId ); + + pasGCPList[nGCPCount].pszId = CPLStrdup( pszBase ); + + pasGCPList[nGCPCount].dfGCPX = + CPLAtof(CSLFetchNameValue(papszHdrLines, szLongName)); + pasGCPList[nGCPCount].dfGCPY = + CPLAtof(CSLFetchNameValue(papszHdrLines, szLatName)); + pasGCPList[nGCPCount].dfGCPZ = 0.0; + + pasGCPList[nGCPCount].dfGCPPixel = dfRasterX; + pasGCPList[nGCPCount].dfGCPLine = dfRasterY; + + nGCPCount++; + } + + } + +/* -------------------------------------------------------------------- */ +/* Collect standalone GCPs. They look like: */ +/* */ +/* GCPn = row, col, lat, long */ +/* GCP1 = 1, 1, 45.0, -75.0 */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < NUM_GCPS; i++ ) + { + char szName[25]; + char **papszTokens; + + sprintf( szName, "GCP%d", i+1 ); + if( CSLFetchNameValue( papszHdrLines, szName ) == NULL ) + continue; + + papszTokens = CSLTokenizeStringComplex( + CSLFetchNameValue( papszHdrLines, szName ), + ",", FALSE, FALSE ); + if( CSLCount(papszTokens) == 4 ) + { + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + + CPLFree( pasGCPList[nGCPCount].pszId ); + pasGCPList[nGCPCount].pszId = CPLStrdup( szName ); + + pasGCPList[nGCPCount].dfGCPX = CPLAtof(papszTokens[3]); + pasGCPList[nGCPCount].dfGCPY = CPLAtof(papszTokens[2]); + pasGCPList[nGCPCount].dfGCPZ = 0.0; + pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(papszTokens[1])+0.5; + pasGCPList[nGCPCount].dfGCPLine = CPLAtof(papszTokens[0])+0.5; + + nGCPCount++; + } + + CSLDestroy(papszTokens); + } +} + +/************************************************************************/ +/* ScanForProjectionInfo */ +/************************************************************************/ + +void MFFDataset::ScanForProjectionInfo() +{ + const char *pszProjName, *pszOriginLong, *pszSpheroidName; + const char *pszSpheroidEqRadius, *pszSpheroidPolarRadius; + double eq_radius, polar_radius; + OGRSpatialReference oProj; + OGRSpatialReference oLL; + MFFSpheroidList *mffEllipsoids; + + pszProjName = CSLFetchNameValue(papszHdrLines, + "PROJECTION_NAME"); + pszOriginLong = CSLFetchNameValue(papszHdrLines, + "PROJECTION_ORIGIN_LONGITUDE"); + pszSpheroidName = CSLFetchNameValue(papszHdrLines, + "SPHEROID_NAME"); + + if (pszProjName == NULL) + { + CPLFree( pszProjection ); + CPLFree( pszGCPProjection ); + pszProjection=CPLStrdup(""); + pszGCPProjection=CPLStrdup(""); + return; + } + else if ((!EQUAL(pszProjName,"utm")) && (!EQUAL(pszProjName,"ll"))) + { + CPLError(CE_Warning,CPLE_AppDefined, + "Warning- only utm and lat/long projections are currently supported."); + CPLFree( pszProjection ); + CPLFree( pszGCPProjection ); + pszProjection=CPLStrdup(""); + pszGCPProjection=CPLStrdup(""); + return; + } + mffEllipsoids = new MFFSpheroidList; + + if( EQUAL(pszProjName,"utm") ) + { + int nZone; + + if (pszOriginLong == NULL) + { + /* If origin not specified, assume 0.0 */ + CPLError(CE_Warning,CPLE_AppDefined, + "Warning- no projection origin longitude specified. Assuming 0.0."); + nZone = 31; + } + else + nZone = 31 + (int) floor(CPLAtof(pszOriginLong)/6.0); + + + if( nGCPCount >= 5 && pasGCPList[4].dfGCPY < 0 ) + oProj.SetUTM( nZone, 0 ); + else + oProj.SetUTM( nZone, 1 ); + + if (pszOriginLong != NULL) + oProj.SetProjParm(SRS_PP_CENTRAL_MERIDIAN,CPLAtof(pszOriginLong)); + + } + + if (pszOriginLong != NULL) + oLL.SetProjParm(SRS_PP_LONGITUDE_OF_ORIGIN,CPLAtof(pszOriginLong)); + + if (pszSpheroidName == NULL) + { + CPLError(CE_Warning,CPLE_AppDefined, + "Warning- unspecified ellipsoid. Using wgs-84 parameters.\n"); + + oProj.SetWellKnownGeogCS( "WGS84" ); + oLL.SetWellKnownGeogCS( "WGS84" ); + } + else + { + if (mffEllipsoids->SpheroidInList(pszSpheroidName)) + { + oProj.SetGeogCS( "unknown","unknown",pszSpheroidName, + mffEllipsoids->GetSpheroidEqRadius(pszSpheroidName), + mffEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName) + ); + oLL.SetGeogCS( "unknown","unknown",pszSpheroidName, + mffEllipsoids->GetSpheroidEqRadius(pszSpheroidName), + mffEllipsoids->GetSpheroidInverseFlattening(pszSpheroidName) + ); + } + else if (EQUAL(pszSpheroidName,"USER_DEFINED")) + { + pszSpheroidEqRadius = CSLFetchNameValue(papszHdrLines, + "SPHEROID_EQUATORIAL_RADIUS"); + pszSpheroidPolarRadius = CSLFetchNameValue(papszHdrLines, + "SPHEROID_POLAR_RADIUS"); + if ((pszSpheroidEqRadius != NULL) && (pszSpheroidPolarRadius != NULL)) + { + eq_radius = CPLAtof( pszSpheroidEqRadius ); + polar_radius = CPLAtof( pszSpheroidPolarRadius ); + oProj.SetGeogCS( "unknown","unknown","unknown", + eq_radius, eq_radius/(eq_radius - polar_radius)); + oLL.SetGeogCS( "unknown","unknown","unknown", + eq_radius, eq_radius/(eq_radius - polar_radius)); + } + else + { + CPLError(CE_Warning,CPLE_AppDefined, + "Warning- radii not specified for user-defined ellipsoid. Using wgs-84 parameters. \n"); + oProj.SetWellKnownGeogCS( "WGS84" ); + oLL.SetWellKnownGeogCS( "WGS84" ); + } + } + else + { + CPLError(CE_Warning,CPLE_AppDefined, + "Warning- unrecognized ellipsoid. Using wgs-84 parameters.\n"); + oProj.SetWellKnownGeogCS( "WGS84" ); + oLL.SetWellKnownGeogCS( "WGS84" ); + } + } + + /* If a geotransform is sufficient to represent the GCP's (ie. each */ + /* estimated gcp is within 0.25*pixel size of the actual value- this */ + /* is the test applied by GDALGCPsToGeoTransform), store the */ + /* geotransform. */ + int transform_ok = FALSE; + + if (EQUAL(pszProjName,"LL")) + { + transform_ok = GDALGCPsToGeoTransform(nGCPCount,pasGCPList,adfGeoTransform,0); + } + else + { + OGRCoordinateTransformation *poTransform = NULL; + double *dfPrjX, *dfPrjY; + int gcp_index; + int bSuccess = TRUE; + + dfPrjX = (double *) CPLMalloc(nGCPCount*sizeof(double)); + dfPrjY = (double *) CPLMalloc(nGCPCount*sizeof(double)); + + + poTransform = OGRCreateCoordinateTransformation( &oLL, &oProj ); + if( poTransform == NULL ) + { + CPLErrorReset(); + bSuccess = FALSE; + } + + for(gcp_index=0;gcp_indexTransform( 1, &(dfPrjX[gcp_index]), &(dfPrjY[gcp_index]) ) ) + bSuccess = FALSE; + + } + + if( bSuccess ) + { + + for(gcp_index=0;gcp_indexnHeaderBytes < 17 || poOpenInfo->fpL == NULL ) + return NULL; + + if( !EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"hdr") ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Load the .hdr file, and compress white space out around the */ +/* equal sign. */ +/* -------------------------------------------------------------------- */ + papszHdrLines = CSLLoad( poOpenInfo->pszFilename ); + if( papszHdrLines == NULL ) + return NULL; + + for( i = 0; papszHdrLines[i] != NULL; i++ ) + { + int bAfterEqual = FALSE; + int iSrc, iDst; + char *pszLine = papszHdrLines[i]; + + for( iSrc=0, iDst=0; pszLine[iSrc] != '\0'; iSrc++ ) + { + if( bAfterEqual || pszLine[iSrc] != ' ' ) + { + pszLine[iDst++] = pszLine[iSrc]; + } + + if( iDst > 0 && pszLine[iDst-1] == '=' ) + bAfterEqual = FALSE; + } + pszLine[iDst] = '\0'; + } + +/* -------------------------------------------------------------------- */ +/* Verify it is an MFF file. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue( papszHdrLines, "IMAGE_FILE_FORMAT" ) != NULL + && !EQUAL(CSLFetchNameValue(papszHdrLines,"IMAGE_FILE_FORMAT"),"MFF") ) + { + CSLDestroy( papszHdrLines ); + return NULL; + } + + if( (CSLFetchNameValue( papszHdrLines, "IMAGE_LINES" ) == NULL + || CSLFetchNameValue(papszHdrLines,"LINE_SAMPLES") == NULL) + && (CSLFetchNameValue( papszHdrLines, "no_rows" ) == NULL + || CSLFetchNameValue(papszHdrLines,"no_columns") == NULL) ) + { + CSLDestroy( papszHdrLines ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + MFFDataset *poDS; + + poDS = new MFFDataset(); + + poDS->papszHdrLines = papszHdrLines; + + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Set some dataset wide information. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue(papszHdrLines,"no_rows") != NULL + && CSLFetchNameValue(papszHdrLines,"no_columns") != NULL ) + { + poDS->nRasterXSize = atoi(CSLFetchNameValue(papszHdrLines,"no_columns")); + poDS->nRasterYSize = atoi(CSLFetchNameValue(papszHdrLines,"no_rows")); + } + else + { + poDS->nRasterXSize = atoi(CSLFetchNameValue(papszHdrLines,"LINE_SAMPLES")); + poDS->nRasterYSize = atoi(CSLFetchNameValue(papszHdrLines,"IMAGE_LINES")); + } + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + + if( CSLFetchNameValue( papszHdrLines, "BYTE_ORDER" ) != NULL ) + { +#ifdef CPL_MSB + bNative = EQUAL(CSLFetchNameValue(papszHdrLines,"BYTE_ORDER"),"MSB"); +#else + bNative = EQUAL(CSLFetchNameValue(papszHdrLines,"BYTE_ORDER"),"LSB"); +#endif + } + +/* -------------------------------------------------------------------- */ +/* Get some information specific to APP tiled files. */ +/* -------------------------------------------------------------------- */ + int bTiled, nTileXSize=0, nTileYSize=0; + const char *pszRefinedType = NULL; + + pszRefinedType = CSLFetchNameValue(papszHdrLines, "type" ); + + bTiled = CSLFetchNameValue(papszHdrLines,"no_rows") != NULL; + if( bTiled ) + { + if( CSLFetchNameValue(papszHdrLines,"tile_size_rows") ) + nTileYSize = + atoi(CSLFetchNameValue(papszHdrLines,"tile_size_rows")); + if( CSLFetchNameValue(papszHdrLines,"tile_size_columns") ) + nTileXSize = + atoi(CSLFetchNameValue(papszHdrLines,"tile_size_columns")); + + if (nTileXSize <= 0 || nTileYSize <= 0 || + poDS->nRasterXSize > INT_MAX - (nTileXSize - 1) || + poDS->nRasterYSize > INT_MAX - (nTileYSize - 1)) + { + delete poDS; + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Read the directory to find matching band files. */ +/* -------------------------------------------------------------------- */ + char **papszDirFiles; + char *pszTargetBase, *pszTargetPath; + int nRawBand, nSkipped=0; + + pszTargetPath = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename)); + pszTargetBase = CPLStrdup(CPLGetBasename( poOpenInfo->pszFilename )); + papszDirFiles = CPLReadDir( CPLGetPath( poOpenInfo->pszFilename ) ); + if( papszDirFiles == NULL ) + { + CPLFree(pszTargetPath); + CPLFree(pszTargetBase); + delete poDS; + return NULL; + } + + for( nRawBand = 0; TRUE; nRawBand++ ) + { + const char *pszExtension; + int nBand; + GDALDataType eDataType; + + /* Find the next raw band file. */ + for( i = 0; papszDirFiles[i] != NULL; i++ ) + { + if( !EQUAL(CPLGetBasename(papszDirFiles[i]),pszTargetBase) ) + continue; + + pszExtension = CPLGetExtension(papszDirFiles[i]); + if( strlen(pszExtension) >= 2 + && isdigit(pszExtension[1]) + && atoi(pszExtension+1) == nRawBand + && strchr("bBcCiIjJrRxXzZ",pszExtension[0]) != NULL ) + break; + } + + if( papszDirFiles[i] == NULL ) + break; + + /* open the file for required level of access */ + VSILFILE *fpRaw; + const char *pszRawFilename = CPLFormFilename(pszTargetPath, + papszDirFiles[i], NULL ); + + if( poOpenInfo->eAccess == GA_Update ) + fpRaw = VSIFOpenL( pszRawFilename, "rb+" ); + else + fpRaw = VSIFOpenL( pszRawFilename, "rb" ); + + if( fpRaw == NULL ) + { + CPLError( CE_Warning, CPLE_OpenFailed, + "Unable to open %s ... skipping.\n", + pszRawFilename ); + nSkipped++; + continue; + } + + pszExtension = CPLGetExtension(papszDirFiles[i]); + if( pszRefinedType != NULL ) + { + if( EQUAL(pszRefinedType,"C*4") ) + eDataType = GDT_CFloat32; + else if( EQUAL(pszRefinedType,"C*8") ) + eDataType = GDT_CFloat64; + else if( EQUAL(pszRefinedType,"R*4") ) + eDataType = GDT_Float32; + else if( EQUAL(pszRefinedType,"R*8") ) + eDataType = GDT_Float64; + else if( EQUAL(pszRefinedType,"I*1") ) + eDataType = GDT_Byte; + else if( EQUAL(pszRefinedType,"I*2") ) + eDataType = GDT_Int16; + else if( EQUAL(pszRefinedType,"I*4") ) + eDataType = GDT_Int32; + else if( EQUAL(pszRefinedType,"U*2") ) + eDataType = GDT_UInt16; + else if( EQUAL(pszRefinedType,"U*4") ) + eDataType = GDT_UInt32; + else if( EQUAL(pszRefinedType,"J*1") ) + { + CPLError( CE_Warning, CPLE_OpenFailed, + "Unable to open band %d because type J*1 is not handled ... skipping.\n", + nRawBand + 1 ); + nSkipped++; + VSIFCloseL(fpRaw); + continue; /* we don't support 1 byte complex */ + } + else if( EQUAL(pszRefinedType,"J*2") ) + eDataType = GDT_CInt16; + else if( EQUAL(pszRefinedType,"K*4") ) + eDataType = GDT_CInt32; + else + { + CPLError( CE_Warning, CPLE_OpenFailed, + "Unable to open band %d because type %s is not handled ... skipping.\n", + nRawBand + 1, pszRefinedType ); + nSkipped++; + VSIFCloseL(fpRaw); + continue; + } + } + else if( EQUALN(pszExtension,"b",1) ) + { + eDataType = GDT_Byte; + } + else if( EQUALN(pszExtension,"i",1) ) + { + eDataType = GDT_UInt16; + } + else if( EQUALN(pszExtension,"j",1) ) + { + eDataType = GDT_CInt16; + } + else if( EQUALN(pszExtension,"r",1) ) + { + eDataType = GDT_Float32; + } + else if( EQUALN(pszExtension,"x",1) ) + { + eDataType = GDT_CFloat32; + } + else + { + CPLError( CE_Warning, CPLE_OpenFailed, + "Unable to open band %d because extension %s is not handled ... skipping.\n", + nRawBand + 1, pszExtension ); + nSkipped++; + VSIFCloseL(fpRaw); + continue; + } + + nBand = poDS->GetRasterCount() + 1; + + int nPixelOffset = GDALGetDataTypeSize(eDataType)/8; + GDALRasterBand *poBand = NULL; + + if( bTiled ) + { + poBand = + new MFFTiledBand( poDS, nBand, fpRaw, nTileXSize, nTileYSize, + eDataType, bNative ); + } + else + { + if (poDS->GetRasterXSize() > INT_MAX / nPixelOffset) + { + CPLError( CE_Warning, CPLE_AppDefined, "Int overflow occured... skipping"); + nSkipped++; + VSIFCloseL(fpRaw); + continue; + } + + poBand = + new RawRasterBand( poDS, nBand, fpRaw, 0, nPixelOffset, + nPixelOffset * poDS->GetRasterXSize(), + eDataType, bNative, TRUE, TRUE ); + } + + poDS->SetBand( nBand, poBand ); + } + + CPLFree(pszTargetPath); + CPLFree(pszTargetBase); + CSLDestroy(papszDirFiles); + +/* -------------------------------------------------------------------- */ +/* Check if we have bands. */ +/* -------------------------------------------------------------------- */ + if( poDS->GetRasterCount() == 0 ) + { + if( nSkipped > 0 && poOpenInfo->eAccess ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %d files that were apparently bands.\n" + "Perhaps this dataset is readonly?\n", + nSkipped ); + delete poDS; + return NULL; + } + else + { + CPLError( CE_Failure, CPLE_OpenFailed, + "MFF header file read successfully, but no bands\n" + "were successfully found and opened." ); + delete poDS; + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Set all information from the .hdr that isn't well know to be */ +/* metadata. */ +/* -------------------------------------------------------------------- */ + for( i = 0; papszHdrLines[i] != NULL; i++ ) + { + const char *pszValue; + char *pszName; + + pszValue = CPLParseNameValue(papszHdrLines[i], &pszName); + if( pszName == NULL || pszValue == NULL ) + continue; + + if( !EQUAL(pszName,"END") + && !EQUAL(pszName,"FILE_TYPE") + && !EQUAL(pszName,"BYTE_ORDER") + && !EQUAL(pszName,"no_columns") + && !EQUAL(pszName,"no_rows") + && !EQUAL(pszName,"type") + && !EQUAL(pszName,"tile_size_rows") + && !EQUAL(pszName,"tile_size_columns") + && !EQUAL(pszName,"IMAGE_FILE_FORMAT") + && !EQUAL(pszName,"IMAGE_LINES") + && !EQUAL(pszName,"LINE_SAMPLES") ) + { + poDS->SetMetadataItem( pszName, pszValue ); + } + + CPLFree( pszName ); + } + +/* -------------------------------------------------------------------- */ +/* Any GCPs in header file? */ +/* -------------------------------------------------------------------- */ + poDS->ScanForGCPs(); + poDS->ScanForProjectionInfo(); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +int GetMFFProjectionType(const char *pszNewProjection) +{ + OGRSpatialReference oSRS(pszNewProjection); + + if( !EQUALN(pszNewProjection,"GEOGCS",6) + && !EQUALN(pszNewProjection,"PROJCS",6) + && !EQUAL(pszNewProjection,"") ) + { + return MFFPRJ_UNRECOGNIZED; + } + else if (EQUAL(pszNewProjection,"")) + { + return MFFPRJ_NONE; + } + else + { + if ((oSRS.GetAttrValue("PROJECTION") != NULL) && + (EQUAL(oSRS.GetAttrValue("PROJECTION"),SRS_PT_TRANSVERSE_MERCATOR))) + { + return MFFPRJ_UTM; + } + else if ((oSRS.GetAttrValue("PROJECTION") == NULL) && (oSRS.IsGeographic())) + { + return MFFPRJ_LL; + } + else + { + return MFFPRJ_UNRECOGNIZED; + } + } +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *MFFDataset::Create( const char * pszFilenameIn, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszParmList ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify input options. */ +/* -------------------------------------------------------------------- */ + if (nBands <= 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "MFF driver does not support %d bands.\n", nBands); + return NULL; + } + + if( eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 + && eType != GDT_CInt16 && eType != GDT_CFloat32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create MFF file with currently unsupported\n" + "data type (%s).\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Establish the base filename (path+filename, less extension). */ +/* -------------------------------------------------------------------- */ + char *pszBaseFilename; + int i; + + pszBaseFilename = (char *) CPLMalloc(strlen(pszFilenameIn)+5); + strcpy( pszBaseFilename, pszFilenameIn ); + + for( i = strlen(pszBaseFilename)-1; i > 0; i-- ) + { + if( pszBaseFilename[i] == '.' ) + { + pszBaseFilename[i] = '\0'; + break; + } + + if( pszBaseFilename[i] == '/' || pszBaseFilename[i] == '\\' ) + break; + } + +/* -------------------------------------------------------------------- */ +/* Create the header file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + const char *pszFilename; + + pszFilename = CPLFormFilename( NULL, pszBaseFilename, "hdr" ); + + fp = VSIFOpenL( pszFilename, "wt" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Couldn't create %s.\n", pszFilename ); + CPLFree(pszBaseFilename); + return NULL; + } + + VSIFPrintfL( fp, "IMAGE_FILE_FORMAT = MFF\n" ); + VSIFPrintfL( fp, "FILE_TYPE = IMAGE\n" ); + VSIFPrintfL( fp, "IMAGE_LINES = %d\n", nYSize ); + VSIFPrintfL( fp, "LINE_SAMPLES = %d\n", nXSize ); +#ifdef CPL_MSB + VSIFPrintfL( fp, "BYTE_ORDER = MSB\n" ); +#else + VSIFPrintfL( fp, "BYTE_ORDER = LSB\n" ); +#endif + + if (CSLFetchNameValue(papszParmList,"NO_END") == NULL) + VSIFPrintfL( fp, "END\n" ); + + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Create the data files, but don't bother writing any data to them.*/ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < nBands; iBand++ ) + { + char szExtension[4]; + + if( eType == GDT_Byte ) + sprintf( szExtension, "b%02d", iBand ); + else if( eType == GDT_UInt16 ) + sprintf( szExtension, "i%02d", iBand ); + else if( eType == GDT_Float32 ) + sprintf( szExtension, "r%02d", iBand ); + else if( eType == GDT_CInt16 ) + sprintf( szExtension, "j%02d", iBand ); + else if( eType == GDT_CFloat32 ) + sprintf( szExtension, "x%02d", iBand ); + + pszFilename = CPLFormFilename( NULL, pszBaseFilename, szExtension ); + fp = VSIFOpenL( pszFilename, "wb" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Couldn't create %s.\n", pszFilename ); + CPLFree(pszBaseFilename); + return NULL; + } + + VSIFWriteL( (void *) "", 1, 1, fp ); + VSIFCloseL( fp ); + } + +/* -------------------------------------------------------------------- */ +/* Open the dataset normally. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poDS; + + strcat( pszBaseFilename, ".hdr" ); + poDS = (GDALDataset *) GDALOpen( pszBaseFilename, GA_Update ); + CPLFree( pszBaseFilename ); + + return poDS; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * +MFFDataset::CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + CPL_UNUSED int bStrict, + char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + MFFDataset *poDS; + GDALDataType eType; + int iBand; + char **newpapszOptions=NULL; + + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "MFF driver does not support source dataset with zero band.\n"); + return NULL; + } + + eType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + + /* check that other bands match type- sets type */ + /* to unknown if they differ. */ + for( iBand = 1; iBand < poSrcDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand *poBand = poSrcDS->GetRasterBand( iBand+1 ); + eType = GDALDataTypeUnion( eType, poBand->GetRasterDataType() ); + } + + newpapszOptions=CSLDuplicate(papszOptions); + newpapszOptions=CSLSetNameValue(newpapszOptions,"NO_END","TRUE"); + + poDS = (MFFDataset *) Create( pszFilename, + poSrcDS->GetRasterXSize(), + poSrcDS->GetRasterYSize(), + poSrcDS->GetRasterCount(), + eType, newpapszOptions ); + + CSLDestroy(newpapszOptions); + + + /* Check that Create worked- return Null if it didn't */ + if (poDS == NULL) + return NULL; + + +/* -------------------------------------------------------------------- */ +/* Copy the image data. */ +/* -------------------------------------------------------------------- */ + int nXSize = poDS->GetRasterXSize(); + int nYSize = poDS->GetRasterYSize(); + int nBlockXSize, nBlockYSize, nBlockTotal, nBlocksDone; + + poDS->GetRasterBand(1)->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + nBlockTotal = ((nXSize + nBlockXSize - 1) / nBlockXSize) + * ((nYSize + nBlockYSize - 1) / nBlockYSize) + * poSrcDS->GetRasterCount(); + + nBlocksDone = 0; + for( iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + GDALRasterBand *poDstBand = poDS->GetRasterBand( iBand+1 ); + int iYOffset, iXOffset; + void *pData; + CPLErr eErr; + + + pData = CPLMalloc(nBlockXSize * nBlockYSize + * GDALGetDataTypeSize(eType) / 8); + + for( iYOffset = 0; iYOffset < nYSize; iYOffset += nBlockYSize ) + { + for( iXOffset = 0; iXOffset < nXSize; iXOffset += nBlockXSize ) + { + int nTBXSize, nTBYSize; + + if( !pfnProgress( (nBlocksDone++) / (float) nBlockTotal, + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated" ); + delete poDS; + CPLFree( pData ); + + GDALDriver *poMFFDriver = + (GDALDriver *) GDALGetDriverByName( "MFF" ); + poMFFDriver->Delete( pszFilename ); + return NULL; + } + + nTBXSize = MIN(nBlockXSize,nXSize-iXOffset); + nTBYSize = MIN(nBlockYSize,nYSize-iYOffset); + + eErr = poSrcBand->RasterIO( GF_Read, + iXOffset, iYOffset, + nTBXSize, nTBYSize, + pData, nTBXSize, nTBYSize, + eType, 0, 0, NULL ); + if( eErr != CE_None ) + { + delete poDS; + CPLFree( pData ); + return NULL; + } + + eErr = poDstBand->RasterIO( GF_Write, + iXOffset, iYOffset, + nTBXSize, nTBYSize, + pData, nTBXSize, nTBYSize, + eType, 0, 0, NULL ); + + if( eErr != CE_None ) + { + delete poDS; + CPLFree( pData ); + return NULL; + } + } + } + + CPLFree( pData ); + } + +/* -------------------------------------------------------------------- */ +/* Copy georeferencing information, if enough is available. */ +/* -------------------------------------------------------------------- */ + + +/* -------------------------------------------------------------------- */ +/* Establish the base filename (path+filename, less extension). */ +/* -------------------------------------------------------------------- */ + char *pszBaseFilename; + int i; + VSILFILE *fp; + const char *pszFilenameGEO; + + pszBaseFilename = (char *) CPLMalloc(strlen(pszFilename)+5); + strcpy( pszBaseFilename, pszFilename ); + + for( i = strlen(pszBaseFilename)-1; i > 0; i-- ) + { + if( pszBaseFilename[i] == '.' ) + { + pszBaseFilename[i] = '\0'; + break; + } + + if( pszBaseFilename[i] == '/' || pszBaseFilename[i] == '\\' ) + break; + } + + pszFilenameGEO = CPLFormFilename( NULL, pszBaseFilename, "hdr" ); + + fp = VSIFOpenL( pszFilenameGEO, "at" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Couldn't open %s for appending.\n", pszFilenameGEO ); + CPLFree(pszBaseFilename); + return NULL; + } + + + /* MFF requires corner and center gcps */ + double *padfTiepoints; + int src_prj; + int georef_created = FALSE; + + padfTiepoints = (double *) CPLMalloc(2*sizeof(double)*5); + + src_prj = GetMFFProjectionType(poSrcDS->GetProjectionRef()); + + if ((src_prj != MFFPRJ_NONE) && (src_prj != MFFPRJ_UNRECOGNIZED)) + { + double *tempGeoTransform = NULL; + + tempGeoTransform = (double *) CPLMalloc(6*sizeof(double)); + + if (( poSrcDS->GetGeoTransform( tempGeoTransform ) == CE_None) + && (tempGeoTransform[0] != 0.0 || tempGeoTransform[1] != 1.0 + || tempGeoTransform[2] != 0.0 || tempGeoTransform[3] != 0.0 + || tempGeoTransform[4] != 0.0 || ABS(tempGeoTransform[5]) != 1.0 )) + { + OGRCoordinateTransformation *poTransform = NULL; + char *newGCPProjection=NULL; + + padfTiepoints[0]=tempGeoTransform[0] + tempGeoTransform[1]*0.5 +\ + tempGeoTransform[2]*0.5; + + padfTiepoints[1]=tempGeoTransform[3] + tempGeoTransform[4]*0.5 +\ + tempGeoTransform[5]*0.5; + + padfTiepoints[2]=tempGeoTransform[0] + tempGeoTransform[2]*0.5 +\ + tempGeoTransform[1]*(poSrcDS->GetRasterXSize()-0.5); + + padfTiepoints[3]=tempGeoTransform[3] + tempGeoTransform[5]*0.5 +\ + tempGeoTransform[4]*(poSrcDS->GetRasterXSize()-0.5); + + padfTiepoints[4]=tempGeoTransform[0] + tempGeoTransform[1]*0.5 +\ + tempGeoTransform[2]*(poSrcDS->GetRasterYSize()-0.5); + + padfTiepoints[5]=tempGeoTransform[3] + tempGeoTransform[4]*0.5 +\ + tempGeoTransform[5]*(poSrcDS->GetRasterYSize()-0.5); + + padfTiepoints[6]=tempGeoTransform[0] +\ + tempGeoTransform[1]*(poSrcDS->GetRasterXSize()-0.5) +\ + tempGeoTransform[2]*(poSrcDS->GetRasterYSize()-0.5); + + padfTiepoints[7]=tempGeoTransform[3]+\ + tempGeoTransform[4]*(poSrcDS->GetRasterXSize()-0.5)+\ + tempGeoTransform[5]*(poSrcDS->GetRasterYSize()-0.5); + + padfTiepoints[8]=tempGeoTransform[0]+\ + tempGeoTransform[1]*(poSrcDS->GetRasterXSize())/2.0+\ + tempGeoTransform[2]*(poSrcDS->GetRasterYSize())/2.0; + + padfTiepoints[9]=tempGeoTransform[3]+\ + tempGeoTransform[4]*(poSrcDS->GetRasterXSize())/2.0+\ + tempGeoTransform[5]*(poSrcDS->GetRasterYSize())/2.0; + + OGRSpatialReference oUTMorLL(poSrcDS->GetProjectionRef()); + (oUTMorLL.GetAttrNode("GEOGCS"))->exportToWkt(&newGCPProjection); + OGRSpatialReference oLL(newGCPProjection); + CPLFree(newGCPProjection); + newGCPProjection = NULL; + + if EQUALN(poSrcDS->GetProjectionRef(),"PROJCS",6) + { + // projected coordinate system- need to translate gcps */ + int bSuccess=TRUE; + int index; + + poTransform = OGRCreateCoordinateTransformation( &oUTMorLL, &oLL ); + if( poTransform == NULL ) + bSuccess = FALSE; + + for (index=0;index<5;index++) + { + if( !bSuccess || !poTransform->Transform( 1, &(padfTiepoints[index*2]), &(padfTiepoints[index*2+1]) ) ) + bSuccess = FALSE; + } + if (bSuccess == TRUE) + georef_created = TRUE; + } + else + { + georef_created = TRUE; + } + } + CPLFree(tempGeoTransform); + } + + if (georef_created == TRUE) + { + /* -------------------------------------------------------------------- */ + /* top left */ + /* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, "TOP_LEFT_CORNER_LATITUDE = %.10f\n", padfTiepoints[1] ); + VSIFPrintfL( fp, "TOP_LEFT_CORNER_LONGITUDE = %.10f\n", padfTiepoints[0] ); + /* -------------------------------------------------------------------- */ + /* top_right */ + /* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, "TOP_RIGHT_CORNER_LATITUDE = %.10f\n", padfTiepoints[3] ); + VSIFPrintfL( fp, "TOP_RIGHT_CORNER_LONGITUDE = %.10f\n", padfTiepoints[2] ); + /* -------------------------------------------------------------------- */ + /* bottom_left */ + /* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, "BOTTOM_LEFT_CORNER_LATITUDE = %.10f\n", padfTiepoints[5] ); + VSIFPrintfL( fp, "BOTTOM_LEFT_CORNER_LONGITUDE = %.10f\n", padfTiepoints[4] ); + /* -------------------------------------------------------------------- */ + /* bottom_right */ + /* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, "BOTTOM_RIGHT_CORNER_LATITUDE = %.10f\n", padfTiepoints[7] ); + VSIFPrintfL( fp, "BOTTOM_RIGHT_CORNER_LONGITUDE = %.10f\n", padfTiepoints[6] ); + /* -------------------------------------------------------------------- */ + /* Center */ + /* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, "CENTRE_LATITUDE = %.10f\n", padfTiepoints[9] ); + VSIFPrintfL( fp, "CENTRE_LONGITUDE = %.10f\n", padfTiepoints[8] ); + /* ------------------------------------------------------------------- */ + /* Ellipsoid/projection */ + /* --------------------------------------------------------------------*/ + + + MFFSpheroidList *mffEllipsoids; + double eq_radius, inv_flattening; + OGRErr ogrerrorEq=OGRERR_NONE; + OGRErr ogrerrorInvf=OGRERR_NONE; + OGRErr ogrerrorOl=OGRERR_NONE; + const char *pszSrcProjection = poSrcDS->GetProjectionRef(); + char *spheroid_name = NULL; + + if( !EQUALN(pszSrcProjection,"GEOGCS",6) + && !EQUALN(pszSrcProjection,"PROJCS",6) + && !EQUAL(pszSrcProjection,"") ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Only OGC WKT Projections supported for writing to MFF.\n" + "%s not supported.", + pszSrcProjection ); + } + else if (!EQUAL(pszSrcProjection,"")) + { + OGRSpatialReference oSRS(pszSrcProjection); + + if ((oSRS.GetAttrValue("PROJECTION") != NULL) && + (EQUAL(oSRS.GetAttrValue("PROJECTION"),SRS_PT_TRANSVERSE_MERCATOR))) + { + VSIFPrintfL(fp,"PROJECTION_NAME = UTM\n"); + VSIFPrintfL(fp,"PROJECTION_ORIGIN_LONGITUDE = %f\n", + oSRS.GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0,&ogrerrorOl)); + } + else if ((oSRS.GetAttrValue("PROJECTION") == NULL) && (oSRS.IsGeographic())) + { + VSIFPrintfL(fp,"PROJECTION_NAME = LL\n"); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unrecognized projection- no georeferencing information transferred."); + VSIFPrintfL(fp,"PROJECTION_NAME = LL\n"); + } + eq_radius = oSRS.GetSemiMajor(&ogrerrorEq); + inv_flattening = oSRS.GetInvFlattening(&ogrerrorInvf); + if ((ogrerrorEq == OGRERR_NONE) && (ogrerrorInvf == OGRERR_NONE)) + { + mffEllipsoids = new MFFSpheroidList; + spheroid_name = mffEllipsoids->GetSpheroidNameByEqRadiusAndInvFlattening(eq_radius,inv_flattening); + if (spheroid_name != NULL) + { + VSIFPrintfL(fp,"SPHEROID_NAME = %s\n",spheroid_name ); + } + else + { + VSIFPrintfL(fp, + "SPHEROID_NAME = USER_DEFINED\nSPHEROID_EQUATORIAL_RADIUS = %.10f\nSPHEROID_POLAR_RADIUS = %.10f\n", + eq_radius,eq_radius*(1-1.0/inv_flattening) ); + } + delete mffEllipsoids; + CPLFree(spheroid_name); + } + } + } + + CPLFree( padfTiepoints ); + VSIFPrintfL( fp, "END\n" ); + VSIFCloseL( fp ); + + /* End of georeferencing stuff */ + + /* Make sure image data gets flushed */ + for( iBand = 0; iBand < poDS->GetRasterCount(); iBand++ ) + { + RawRasterBand *poDstBand = (RawRasterBand *) poDS->GetRasterBand( iBand+1 ); + poDstBand->FlushCache(); + } + + + if( !pfnProgress( 1.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated" ); + delete poDS; + + GDALDriver *poMFFDriver = + (GDALDriver *) GDALGetDriverByName( "MFF" ); + poMFFDriver->Delete( pszFilename ); + CPLFree(pszBaseFilename); + return NULL; + } + + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + CPLFree(pszBaseFilename); + + return poDS; +} + + +/************************************************************************/ +/* GDALRegister_MFF() */ +/************************************************************************/ + +void GDALRegister_MFF() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "MFF" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "MFF" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Vexcel MFF Raster" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#MFF" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "hdr" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16 Float32 CInt16 CFloat32" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = MFFDataset::Open; + poDriver->pfnCreate = MFFDataset::Create; + poDriver->pfnCreateCopy = MFFDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/ndfdataset.cpp b/bazaar/plugin/gdal/frmts/raw/ndfdataset.cpp new file mode 100644 index 000000000..e5286a141 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/ndfdataset.cpp @@ -0,0 +1,456 @@ +/****************************************************************************** + * $Id: ndfdataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: NDF Driver + * Purpose: Implementation of NLAPS Data Format read support. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ndfdataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* NDFDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class NDFDataset : public RawDataset +{ + double adfGeoTransform[6]; + + char *pszProjection; + char **papszExtraFiles; + + char **papszHeader; + const char *Get( const char *pszKey, const char *pszDefault); + + public: + NDFDataset(); + ~NDFDataset(); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual const char *GetProjectionRef(void); + virtual char **GetFileList(void); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* NDFDataset() */ +/************************************************************************/ + +NDFDataset::NDFDataset() +{ + pszProjection = CPLStrdup(""); + + papszHeader = NULL; + papszExtraFiles = NULL; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~NDFDataset() */ +/************************************************************************/ + +NDFDataset::~NDFDataset() + +{ + FlushCache(); + CPLFree( pszProjection ); + CSLDestroy( papszHeader ); + CSLDestroy( papszExtraFiles ); + + for( int i = 0; i < GetRasterCount(); i++ ) + { + VSIFCloseL( ((RawRasterBand *) GetRasterBand(i+1))->GetFPL() ); + } +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *NDFDataset::GetProjectionRef() + +{ + return pszProjection; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr NDFDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; +} + +/************************************************************************/ +/* Get() */ +/* */ +/* Fetch a value from the header by keyword. */ +/************************************************************************/ + +const char *NDFDataset::Get( const char *pszKey, const char *pszDefault ) + +{ + const char *pszResult = CSLFetchNameValue( papszHeader, pszKey ); + + if( pszResult == NULL ) + return pszDefault; + else + return pszResult; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **NDFDataset::GetFileList() + +{ + char **papszFileList = NULL; + + // Main data file, etc. + papszFileList = GDALPamDataset::GetFileList(); + + // Header file. + papszFileList = CSLInsertStrings( papszFileList, -1, + papszExtraFiles ); + + return papszFileList; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *NDFDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* The user must select the header file (ie. .H1). */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 50 ) + return NULL; + + if( !EQUALN((const char *)poOpenInfo->pabyHeader,"NDF_REVISION=2",14) + && !EQUALN((const char *)poOpenInfo->pabyHeader,"NDF_REVISION=0",14) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Read and process the header into a local name/value */ +/* stringlist. We just take off the trailing semicolon. The */ +/* keyword is already seperated from the value by an equal */ +/* sign. */ +/* -------------------------------------------------------------------- */ + + VSILFILE* fp = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + if (fp == NULL) + return NULL; + + const char *pszLine; + const int nHeaderMax = 1000; + int nHeaderLines = 0; + char **papszHeader = (char **) CPLMalloc(sizeof(char *) * (nHeaderMax+1)); + + while( nHeaderLines < nHeaderMax + && (pszLine = CPLReadLineL( fp )) != NULL + && !EQUAL(pszLine,"END_OF_HDR;") ) + { + char *pszFixed; + + if( strstr(pszLine,"=") == NULL ) + break; + + pszFixed = CPLStrdup( pszLine ); + if( pszFixed[strlen(pszFixed)-1] == ';' ) + pszFixed[strlen(pszFixed)-1] = '\0'; + + papszHeader[nHeaderLines++] = pszFixed; + papszHeader[nHeaderLines] = NULL; + } + VSIFCloseL(fp); + fp = NULL; + + if( CSLFetchNameValue( papszHeader, "PIXELS_PER_LINE" ) == NULL + || CSLFetchNameValue( papszHeader, "LINES_PER_DATA_FILE" ) == NULL + || CSLFetchNameValue( papszHeader, "BITS_PER_PIXEL" ) == NULL + || CSLFetchNameValue( papszHeader, "PIXEL_FORMAT" ) == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Dataset appears to be NDF but is missing a required field."); + CSLDestroy( papszHeader ); + return NULL; + } + + if( !EQUAL(CSLFetchNameValue( papszHeader, "PIXEL_FORMAT"), + "BYTE" ) + || !EQUAL(CSLFetchNameValue( papszHeader, "BITS_PER_PIXEL"),"8") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Currently NDF driver supports only 8bit BYTE format." ); + CSLDestroy( papszHeader ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CSLDestroy( papszHeader ); + CPLError( CE_Failure, CPLE_NotSupported, + "The NDF driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + NDFDataset *poDS; + + poDS = new NDFDataset(); + poDS->papszHeader = papszHeader; + + poDS->nRasterXSize = atoi(poDS->Get("PIXELS_PER_LINE","")); + poDS->nRasterYSize = atoi(poDS->Get("LINES_PER_DATA_FILE","")); + +/* -------------------------------------------------------------------- */ +/* Create a raw raster band for each file. */ +/* -------------------------------------------------------------------- */ + int iBand; + const char* pszBand = CSLFetchNameValue(papszHeader, + "NUMBER_OF_BANDS_IN_VOLUME"); + if (pszBand == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find band count"); + delete poDS; + return NULL; + } + int nBands = atoi(pszBand); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || + !GDALCheckBandCount(nBands, FALSE)) + { + delete poDS; + return NULL; + } + + for( iBand = 0; iBand < nBands; iBand++ ) + { + char szKey[100]; + CPLString osFilename; + + sprintf( szKey, "BAND%d_FILENAME", iBand+1 ); + osFilename = poDS->Get(szKey,""); + + // NDF1 file do not include the band filenames. + if( osFilename.size() == 0 ) + { + char szBandExtension[15]; + sprintf( szBandExtension, "I%d", iBand+1 ); + osFilename = CPLResetExtension( poOpenInfo->pszFilename, + szBandExtension ); + } + else + { + CPLString osBasePath = CPLGetPath(poOpenInfo->pszFilename); + osFilename = CPLFormFilename( osBasePath, osFilename, NULL); + } + + VSILFILE *fpRaw = VSIFOpenL( osFilename, "rb" ); + if( fpRaw == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to open band file: %s", + osFilename.c_str() ); + delete poDS; + return NULL; + } + poDS->papszExtraFiles = + CSLAddString( poDS->papszExtraFiles, + osFilename ); + + RawRasterBand *poBand = + new RawRasterBand( poDS, iBand+1, fpRaw, 0, 1, poDS->nRasterXSize, + GDT_Byte, TRUE, TRUE ); + + sprintf( szKey, "BAND%d_NAME", iBand+1 ); + poBand->SetDescription( poDS->Get(szKey, "") ); + + sprintf( szKey, "BAND%d_WAVELENGTHS", iBand+1 ); + poBand->SetMetadataItem( "WAVELENGTHS", poDS->Get(szKey,"") ); + + sprintf( szKey, "BAND%d_RADIOMETRIC_GAINS/BIAS", iBand+1 ); + poBand->SetMetadataItem( "RADIOMETRIC_GAINS_BIAS", + poDS->Get(szKey,"") ); + + poDS->SetBand( iBand+1, poBand ); + } + +/* -------------------------------------------------------------------- */ +/* Fetch and parse USGS projection parameters. */ +/* -------------------------------------------------------------------- */ + double adfUSGSParms[15] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + char **papszParmTokens = + CSLTokenizeStringComplex( poDS->Get( "USGS_PROJECTION_NUMBER", "" ), + ",", FALSE, TRUE ); + + if( CSLCount( papszParmTokens ) >= 15 ) + { + int i; + for( i = 0; i < 15; i++ ) + adfUSGSParms[i] = CPLAtof(papszParmTokens[i]); + } + CSLDestroy(papszParmTokens); + papszParmTokens = NULL; + +/* -------------------------------------------------------------------- */ +/* Minimal georef support ... should add full USGS style */ +/* support at some point. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + int nUSGSProjection = atoi(poDS->Get( "USGS_PROJECTION_NUMBER", "" )); + int nZone = atoi(poDS->Get("USGS_MAP_ZONE","0")); + + oSRS.importFromUSGS( nUSGSProjection, nZone, adfUSGSParms, 12 ); + + CPLString osDatum = poDS->Get( "HORIZONTAL_DATUM", "" ); + if( EQUAL(osDatum,"WGS84") || EQUAL(osDatum,"NAD83") + || EQUAL(osDatum,"NAD27") ) + { + oSRS.SetWellKnownGeogCS( osDatum ); + } + else if( EQUALN(osDatum,"NAD27",5) ) + { + oSRS.SetWellKnownGeogCS( "NAD27" ); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unrecognised datum name in NLAPS/NDF file:%s, assuming WGS84.", + osDatum.c_str() ); + oSRS.SetWellKnownGeogCS( "WGS84" ); + } + + if( oSRS.GetRoot() != NULL ) + { + CPLFree( poDS->pszProjection ); + poDS->pszProjection = NULL; + oSRS.exportToWkt( &(poDS->pszProjection) ); + } + +/* -------------------------------------------------------------------- */ +/* Get geotransform. */ +/* -------------------------------------------------------------------- */ + char **papszUL = CSLTokenizeString2( + poDS->Get("UPPER_LEFT_CORNER",""), ",", 0 ); + char **papszUR = CSLTokenizeString2( + poDS->Get("UPPER_RIGHT_CORNER",""), ",", 0 ); + char **papszLL = CSLTokenizeString2( + poDS->Get("LOWER_LEFT_CORNER",""), ",", 0 ); + + if( CSLCount(papszUL) == 4 + && CSLCount(papszUR) == 4 + && CSLCount(papszLL) == 4 ) + { + poDS->adfGeoTransform[0] = CPLAtof(papszUL[2]); + poDS->adfGeoTransform[1] = + (CPLAtof(papszUR[2]) - CPLAtof(papszUL[2])) / (poDS->nRasterXSize-1); + poDS->adfGeoTransform[2] = + (CPLAtof(papszUR[3]) - CPLAtof(papszUL[3])) / (poDS->nRasterXSize-1); + + poDS->adfGeoTransform[3] = CPLAtof(papszUL[3]); + poDS->adfGeoTransform[4] = + (CPLAtof(papszLL[2]) - CPLAtof(papszUL[2])) / (poDS->nRasterYSize-1); + poDS->adfGeoTransform[5] = + (CPLAtof(papszLL[3]) - CPLAtof(papszUL[3])) / (poDS->nRasterYSize-1); + + // Move origin up-left half a pixel. + poDS->adfGeoTransform[0] -= poDS->adfGeoTransform[1] * 0.5; + poDS->adfGeoTransform[0] -= poDS->adfGeoTransform[4] * 0.5; + poDS->adfGeoTransform[3] -= poDS->adfGeoTransform[2] * 0.5; + poDS->adfGeoTransform[3] -= poDS->adfGeoTransform[5] * 0.5; + } + + CSLDestroy( papszUL ); + CSLDestroy( papszLL ); + CSLDestroy( papszUR ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_NDF() */ +/************************************************************************/ + +void GDALRegister_NDF() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "NDF" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "NDF" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "NLAPS Data Format" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#NDF" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = NDFDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/raw/ntv2dataset.cpp b/bazaar/plugin/gdal/frmts/raw/ntv2dataset.cpp new file mode 100644 index 000000000..c4037494c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/ntv2dataset.cpp @@ -0,0 +1,885 @@ +/****************************************************************************** + * $Id: ntv2dataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: Horizontal Datum Formats + * Purpose: Implementation of NTv2 datum shift format used in Canada, France, + * Australia and elsewhere. + * Author: Frank Warmerdam, warmerdam@pobox.com + * Financial Support: i-cubed (http://www.i-cubed.com) + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: ntv2dataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/** + * The header for the file, and each grid consists of 11 16byte records. + * The first half is an ASCII label, and the second half is the value + * often in a little endian int or float. + * + * Example: + +00000000 4e 55 4d 5f 4f 52 45 43 0b 00 00 00 00 00 00 00 |NUM_OREC........| +00000010 4e 55 4d 5f 53 52 45 43 0b 00 00 00 00 00 00 00 |NUM_SREC........| +00000020 4e 55 4d 5f 46 49 4c 45 01 00 00 00 00 00 00 00 |NUM_FILE........| +00000030 47 53 5f 54 59 50 45 20 53 45 43 4f 4e 44 53 20 |GS_TYPE SECONDS | +00000040 56 45 52 53 49 4f 4e 20 49 47 4e 30 37 5f 30 31 |VERSION IGN07_01| +00000050 53 59 53 54 45 4d 5f 46 4e 54 46 20 20 20 20 20 |SYSTEM_FNTF | +00000060 53 59 53 54 45 4d 5f 54 52 47 46 39 33 20 20 20 |SYSTEM_TRGF93 | +00000070 4d 41 4a 4f 52 5f 46 20 cd cc cc 4c c2 54 58 41 |MAJOR_F ...L.TXA| +00000080 4d 49 4e 4f 52 5f 46 20 00 00 00 c0 88 3f 58 41 |MINOR_F .....?XA| +00000090 4d 41 4a 4f 52 5f 54 20 00 00 00 40 a6 54 58 41 |MAJOR_T ...@.TXA| +000000a0 4d 49 4e 4f 52 5f 54 20 27 e0 1a 14 c4 3f 58 41 |MINOR_T '....?XA| +000000b0 53 55 42 5f 4e 41 4d 45 46 52 41 4e 43 45 20 20 |SUB_NAMEFRANCE | +000000c0 50 41 52 45 4e 54 20 20 4e 4f 4e 45 20 20 20 20 |PARENT NONE | +000000d0 43 52 45 41 54 45 44 20 33 31 2f 31 30 2f 30 37 |CREATED 31/10/07| +000000e0 55 50 44 41 54 45 44 20 20 20 20 20 20 20 20 20 |UPDATED | +000000f0 53 5f 4c 41 54 20 20 20 00 00 00 00 80 04 02 41 |S_LAT .......A| +00000100 4e 5f 4c 41 54 20 20 20 00 00 00 00 00 da 06 41 |N_LAT .......A| +00000110 45 5f 4c 4f 4e 47 20 20 00 00 00 00 00 94 e1 c0 |E_LONG ........| +00000120 57 5f 4c 4f 4e 47 20 20 00 00 00 00 00 56 d3 40 |W_LONG .....V.@| +00000130 4c 41 54 5f 49 4e 43 20 00 00 00 00 00 80 76 40 |LAT_INC ......v@| +00000140 4c 4f 4e 47 5f 49 4e 43 00 00 00 00 00 80 76 40 |LONG_INC......v@| +00000150 47 53 5f 43 4f 55 4e 54 a4 43 00 00 00 00 00 00 |GS_COUNT.C......| +00000160 94 f7 c1 3e 70 ee a3 3f 2a c7 84 3d ff 42 af 3d |...>p..?*..=.B.=| + +the actual grid data is a raster with 4 float32 bands (lat offset, long +offset, lat error, long error). The offset values are in arc seconds. +The grid is flipped in the x and y axis from our usual GDAL orientation. +That is, the first pixel is the south east corner with scanlines going +east to west, and rows from south to north. As a GDAL dataset we represent +these both in the more conventional orientation. + */ + +/************************************************************************/ +/* ==================================================================== */ +/* NTv2Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +class NTv2Dataset : public RawDataset +{ + public: + VSILFILE *fpImage; // image data file. + + int nRecordLength; + vsi_l_offset nGridOffset; + + double adfGeoTransform[6]; + + void CaptureMetadataItem( char *pszItem ); + + int OpenGrid( char *pachGridHeader, vsi_l_offset nDataStart ); + + public: + NTv2Dataset(); + ~NTv2Dataset(); + + virtual CPLErr SetGeoTransform( double * padfTransform ); + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual const char *GetProjectionRef(); + virtual void FlushCache(void); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* NTv2Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* NTv2Dataset() */ +/************************************************************************/ + +NTv2Dataset::NTv2Dataset() +{ + fpImage = NULL; +} + +/************************************************************************/ +/* ~NTv2Dataset() */ +/************************************************************************/ + +NTv2Dataset::~NTv2Dataset() + +{ + FlushCache(); + + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void NTv2Dataset::FlushCache() + +{ +/* -------------------------------------------------------------------- */ +/* Nothing to do in readonly mode, or if nothing seems to have */ +/* changed metadata wise. */ +/* -------------------------------------------------------------------- */ + if( eAccess != GA_Update || !(GetPamFlags() & GPF_DIRTY) ) + { + RawDataset::FlushCache(); + return; + } + +/* -------------------------------------------------------------------- */ +/* Load grid and file headers. */ +/* -------------------------------------------------------------------- */ + char achFileHeader[11*16]; + char achGridHeader[11*16]; + + VSIFSeekL( fpImage, 0, SEEK_SET ); + VSIFReadL( achFileHeader, 11, 16, fpImage ); + + VSIFSeekL( fpImage, nGridOffset, SEEK_SET ); + VSIFReadL( achGridHeader, 11, 16, fpImage ); + +/* -------------------------------------------------------------------- */ +/* Update the grid, and file headers with any available */ +/* metadata. If all available metadata is recognised then mark */ +/* things "clean" from a PAM point of view. */ +/* -------------------------------------------------------------------- */ + char **papszMD = GetMetadata(); + int i; + int bSomeLeftOver = FALSE; + + for( i = 0; papszMD != NULL && papszMD[i] != NULL; i++ ) + { + char *pszKey = NULL; + const char *pszValue = CPLParseNameValue( papszMD[i], &pszKey ); + if( pszKey == NULL ) + continue; + + if( EQUAL(pszKey,"GS_TYPE") ) + { + memcpy( achFileHeader + 3*16+8, " ", 8 ); + memcpy( achFileHeader + 3*16+8, pszValue, MIN(8,strlen(pszValue)) ); + } + else if( EQUAL(pszKey,"VERSION") ) + { + memcpy( achFileHeader + 4*16+8, " ", 8 ); + memcpy( achFileHeader + 4*16+8, pszValue, MIN(8,strlen(pszValue)) ); + } + else if( EQUAL(pszKey,"SYSTEM_F") ) + { + memcpy( achFileHeader + 5*16+8, " ", 8 ); + memcpy( achFileHeader + 5*16+8, pszValue, MIN(8,strlen(pszValue)) ); + } + else if( EQUAL(pszKey,"SYSTEM_T") ) + { + memcpy( achFileHeader + 6*16+8, " ", 8 ); + memcpy( achFileHeader + 6*16+8, pszValue, MIN(8,strlen(pszValue)) ); + } + else if( EQUAL(pszKey,"MAJOR_F") ) + { + double dfValue = CPLAtof(pszValue); + CPL_LSBPTR64( &dfValue ); + memcpy( achFileHeader + 7*16+8, &dfValue, 8 ); + } + else if( EQUAL(pszKey,"MINOR_F") ) + { + double dfValue = CPLAtof(pszValue); + CPL_LSBPTR64( &dfValue ); + memcpy( achFileHeader + 8*16+8, &dfValue, 8 ); + } + else if( EQUAL(pszKey,"MAJOR_T") ) + { + double dfValue = CPLAtof(pszValue); + CPL_LSBPTR64( &dfValue ); + memcpy( achFileHeader + 9*16+8, &dfValue, 8 ); + } + else if( EQUAL(pszKey,"MINOR_T") ) + { + double dfValue = CPLAtof(pszValue); + CPL_LSBPTR64( &dfValue ); + memcpy( achFileHeader + 10*16+8, &dfValue, 8 ); + } + else if( EQUAL(pszKey,"SUB_NAME") ) + { + memcpy( achGridHeader + 0*16+8, " ", 8 ); + memcpy( achGridHeader + 0*16+8, pszValue, MIN(8,strlen(pszValue)) ); + } + else if( EQUAL(pszKey,"PARENT") ) + { + memcpy( achGridHeader + 1*16+8, " ", 8 ); + memcpy( achGridHeader + 1*16+8, pszValue, MIN(8,strlen(pszValue)) ); + } + else if( EQUAL(pszKey,"CREATED") ) + { + memcpy( achGridHeader + 2*16+8, " ", 8 ); + memcpy( achGridHeader + 2*16+8, pszValue, MIN(8,strlen(pszValue)) ); + } + else if( EQUAL(pszKey,"UPDATED") ) + { + memcpy( achGridHeader + 3*16+8, " ", 8 ); + memcpy( achGridHeader + 3*16+8, pszValue, MIN(8,strlen(pszValue)) ); + } + else + { + bSomeLeftOver = TRUE; + } + + CPLFree( pszKey ); + } + +/* -------------------------------------------------------------------- */ +/* Load grid and file headers. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( fpImage, 0, SEEK_SET ); + VSIFWriteL( achFileHeader, 11, 16, fpImage ); + + VSIFSeekL( fpImage, nGridOffset, SEEK_SET ); + VSIFWriteL( achGridHeader, 11, 16, fpImage ); + +/* -------------------------------------------------------------------- */ +/* Clear flags if we got everything, then let pam and below do */ +/* their flushing. */ +/* -------------------------------------------------------------------- */ + if( !bSomeLeftOver ) + SetPamFlags( GetPamFlags() & (~GPF_DIRTY) ); + + RawDataset::FlushCache(); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int NTv2Dataset::Identify( GDALOpenInfo *poOpenInfo ) + +{ + if( EQUALN(poOpenInfo->pszFilename,"NTv2:",5) ) + return TRUE; + + if( poOpenInfo->nHeaderBytes < 64 ) + return FALSE; + + if( !EQUALN((const char *)poOpenInfo->pabyHeader + 0, "NUM_OREC", 8 ) ) + return FALSE; + + if( !EQUALN((const char *)poOpenInfo->pabyHeader +16, "NUM_SREC", 8 ) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *NTv2Dataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Are we targetting a particular grid? */ +/* -------------------------------------------------------------------- */ + CPLString osFilename; + int iTargetGrid = -1; + + if( EQUALN(poOpenInfo->pszFilename,"NTv2:",5) ) + { + const char *pszRest = poOpenInfo->pszFilename+5; + + iTargetGrid = atoi(pszRest); + while( *pszRest != '\0' && *pszRest != ':' ) + pszRest++; + + if( *pszRest == ':' ) + pszRest++; + + osFilename = pszRest; + } + else + osFilename = poOpenInfo->pszFilename; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + NTv2Dataset *poDS; + + poDS = new NTv2Dataset(); + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fpImage = VSIFOpenL( osFilename, "rb" ); + else + poDS->fpImage = VSIFOpenL( osFilename, "rb+" ); + + if( poDS->fpImage == NULL ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the file header. */ +/* -------------------------------------------------------------------- */ + char achHeader[11*16]; + GInt32 nSubFileCount; + double dfValue; + CPLString osFValue; + + VSIFSeekL( poDS->fpImage, 0, SEEK_SET ); + VSIFReadL( achHeader, 11, 16, poDS->fpImage ); + + CPL_LSBPTR32( achHeader + 2*16 + 8 ); + memcpy( &nSubFileCount, achHeader + 2*16 + 8, 4 ); + if (nSubFileCount <= 0 || nSubFileCount >= 1024) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid value for NUM_FILE : %d", nSubFileCount); + delete poDS; + return NULL; + } + + poDS->CaptureMetadataItem( achHeader + 3*16 ); + poDS->CaptureMetadataItem( achHeader + 4*16 ); + poDS->CaptureMetadataItem( achHeader + 5*16 ); + poDS->CaptureMetadataItem( achHeader + 6*16 ); + + memcpy( &dfValue, achHeader + 7*16 + 8, 8 ); + CPL_LSBPTR64( &dfValue ); + osFValue.Printf( "%.15g", dfValue ); + poDS->SetMetadataItem( "MAJOR_F", osFValue ); + + memcpy( &dfValue, achHeader + 8*16 + 8, 8 ); + CPL_LSBPTR64( &dfValue ); + osFValue.Printf( "%.15g", dfValue ); + poDS->SetMetadataItem( "MINOR_F", osFValue ); + + memcpy( &dfValue, achHeader + 9*16 + 8, 8 ); + CPL_LSBPTR64( &dfValue ); + osFValue.Printf( "%.15g", dfValue ); + poDS->SetMetadataItem( "MAJOR_T", osFValue ); + + memcpy( &dfValue, achHeader + 10*16 + 8, 8 ); + CPL_LSBPTR64( &dfValue ); + osFValue.Printf( "%.15g", dfValue ); + poDS->SetMetadataItem( "MINOR_T", osFValue ); + +/* ==================================================================== */ +/* Loop over grids. */ +/* ==================================================================== */ + int iGrid; + vsi_l_offset nGridOffset = sizeof(achHeader); + + for( iGrid = 0; iGrid < nSubFileCount; iGrid++ ) + { + CPLString osSubName; + int i; + GUInt32 nGSCount; + + VSIFSeekL( poDS->fpImage, nGridOffset, SEEK_SET ); + if (VSIFReadL( achHeader, 11, 16, poDS->fpImage ) != 16) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read header for subfile %d", iGrid); + delete poDS; + return NULL; + } + + for( i = 4; i <= 9; i++ ) + CPL_LSBPTR64( achHeader + i*16 + 8 ); + + CPL_LSBPTR32( achHeader + 10*16 + 8 ); + + memcpy( &nGSCount, achHeader + 10*16 + 8, 4 ); + + osSubName.assign( achHeader + 8, 8 ); + osSubName.Trim(); + + // If this is our target grid, open it as a dataset. + if( iTargetGrid == iGrid || (iTargetGrid == -1 && iGrid == 0) ) + { + if( !poDS->OpenGrid( achHeader, nGridOffset ) ) + { + delete poDS; + return NULL; + } + } + + // If we are opening the file as a whole, list subdatasets. + if( iTargetGrid == -1 ) + { + CPLString osKey, osValue; + + osKey.Printf( "SUBDATASET_%d_NAME", iGrid ); + osValue.Printf( "NTv2:%d:%s", iGrid, osFilename.c_str() ); + poDS->SetMetadataItem( osKey, osValue, "SUBDATASETS" ); + + osKey.Printf( "SUBDATASET_%d_DESC", iGrid ); + osValue.Printf( "%s", osSubName.c_str() ); + poDS->SetMetadataItem( osKey, osValue, "SUBDATASETS" ); + } + + nGridOffset += (11+(vsi_l_offset)nGSCount) * 16; + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* OpenGrid() */ +/* */ +/* Note that the caller will already have byte swapped needed */ +/* portions of the header. */ +/************************************************************************/ + +int NTv2Dataset::OpenGrid( char *pachHeader, vsi_l_offset nGridOffset ) + +{ + this->nGridOffset = nGridOffset; + +/* -------------------------------------------------------------------- */ +/* Read the grid header. */ +/* -------------------------------------------------------------------- */ + double s_lat, n_lat, e_long, w_long, lat_inc, long_inc; + + CaptureMetadataItem( pachHeader + 0*16 ); + CaptureMetadataItem( pachHeader + 1*16 ); + CaptureMetadataItem( pachHeader + 2*16 ); + CaptureMetadataItem( pachHeader + 3*16 ); + + memcpy( &s_lat, pachHeader + 4*16 + 8, 8 ); + memcpy( &n_lat, pachHeader + 5*16 + 8, 8 ); + memcpy( &e_long, pachHeader + 6*16 + 8, 8 ); + memcpy( &w_long, pachHeader + 7*16 + 8, 8 ); + memcpy( &lat_inc, pachHeader + 8*16 + 8, 8 ); + memcpy( &long_inc, pachHeader + 9*16 + 8, 8 ); + + e_long *= -1; + w_long *= -1; + + nRasterXSize = (int) floor((e_long - w_long) / long_inc + 1.5); + nRasterYSize = (int) floor((n_lat - s_lat) / lat_inc + 1.5); + + if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize)) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Create band information object. */ +/* */ +/* We use unusual offsets to remap from bottom to top, to top */ +/* to bottom orientation, and also to remap east to west, to */ +/* west to east. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 0; iBand < 4; iBand++ ) + { + RawRasterBand *poBand = + new RawRasterBand( this, iBand+1, fpImage, + nGridOffset + 4*iBand + 11*16 + + (nRasterXSize-1) * 16 + + (nRasterYSize-1) * 16 * nRasterXSize, + -16, -16 * nRasterXSize, + GDT_Float32, CPL_IS_LSB, TRUE, FALSE ); + SetBand( iBand+1, poBand ); + } + + GetRasterBand(1)->SetDescription( "Latitude Offset (arc seconds)" ); + GetRasterBand(2)->SetDescription( "Longitude Offset (arc seconds)" ); + GetRasterBand(3)->SetDescription( "Latitude Error" ); + GetRasterBand(4)->SetDescription( "Longitude Error" ); + +/* -------------------------------------------------------------------- */ +/* Setup georeferencing. */ +/* -------------------------------------------------------------------- */ + adfGeoTransform[0] = (w_long - long_inc*0.5) / 3600.0; + adfGeoTransform[1] = long_inc / 3600.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = (n_lat + lat_inc*0.5) / 3600.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = (-1 * lat_inc) / 3600.0; + + return TRUE; +} + +/************************************************************************/ +/* CaptureMetadataItem() */ +/************************************************************************/ + +void NTv2Dataset::CaptureMetadataItem( char *pszItem ) + +{ + CPLString osKey, osValue; + + osKey.assign( pszItem, 8 ); + osValue.assign( pszItem+8, 8 ); + + SetMetadataItem( osKey.Trim(), osValue.Trim() ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr NTv2Dataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr NTv2Dataset::SetGeoTransform( double * padfTransform ) + +{ + if( eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Unable to update geotransform on readonly file." ); + return CE_Failure; + } + + if( padfTransform[2] != 0.0 || padfTransform[4] != 0.0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Rotated and sheared geotransforms not supported for NTv2."); + return CE_Failure; + } + + memcpy( adfGeoTransform, padfTransform, sizeof(double)*6 ); + +/* -------------------------------------------------------------------- */ +/* Update grid header. */ +/* -------------------------------------------------------------------- */ + double dfValue; + char achHeader[11*16]; + + // read grid header + VSIFSeekL( fpImage, nGridOffset, SEEK_SET ); + VSIFReadL( achHeader, 11, 16, fpImage ); + + // S_LAT + dfValue = 3600 * (adfGeoTransform[3] + (nRasterYSize-0.5) * adfGeoTransform[5]); + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 4*16 + 8, &dfValue, 8 ); + + // N_LAT + dfValue = 3600 * (adfGeoTransform[3] + 0.5 * adfGeoTransform[5]); + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 5*16 + 8, &dfValue, 8 ); + + // E_LONG + dfValue = -3600 * (adfGeoTransform[0] + (nRasterXSize-0.5)*adfGeoTransform[1]); + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 6*16 + 8, &dfValue, 8 ); + + // W_LONG + dfValue = -3600 * (adfGeoTransform[0] + 0.5 * adfGeoTransform[1]); + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 7*16 + 8, &dfValue, 8 ); + + // LAT_INC + dfValue = -3600 * adfGeoTransform[5]; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 8*16 + 8, &dfValue, 8 ); + + // LONG_INC + dfValue = 3600 * adfGeoTransform[1]; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 9*16 + 8, &dfValue, 8 ); + + // write grid header. + VSIFSeekL( fpImage, nGridOffset, SEEK_SET ); + VSIFWriteL( achHeader, 11, 16, fpImage ); + + return CE_None; +} + + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *NTv2Dataset::GetProjectionRef() + +{ + return SRS_WKT_WGS84; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *NTv2Dataset::Create( const char * pszFilename, + int nXSize, int nYSize, + CPL_UNUSED int nBands, + GDALDataType eType, + char ** papszOptions ) +{ + if( eType != GDT_Float32 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Attempt to create NTv2 file with unsupported data type '%s'.", + GDALGetDataTypeName( eType ) ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Are we extending an existing file? */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + GUInt32 nNumFile = 1; + + int bAppend = CSLFetchBoolean(papszOptions,"APPEND_SUBDATASET",FALSE); + +/* -------------------------------------------------------------------- */ +/* Try to open or create file. */ +/* -------------------------------------------------------------------- */ + if( bAppend ) + fp = VSIFOpenL( pszFilename, "rb+" ); + else + fp = VSIFOpenL( pszFilename, "wb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to open/create file `%s' failed.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a file level header if we are creating new. */ +/* -------------------------------------------------------------------- */ + char achHeader[11*16]; + const char *pszValue; + + if( !bAppend ) + { + memset( achHeader, 0, sizeof(achHeader) ); + + memcpy( achHeader + 0*16, "NUM_OREC", 8 ); + achHeader[ 0*16 + 8] = 0xb; + + memcpy( achHeader + 1*16, "NUM_SREC", 8 ); + achHeader[ 1*16 + 8] = 0xb; + + memcpy( achHeader + 2*16, "NUM_FILE", 8 ); + achHeader[ 2*16 + 8] = 0x1; + + memcpy( achHeader + 3*16, "GS_TYPE ", 16 ); + pszValue = CSLFetchNameValueDef( papszOptions, "GS_TYPE", "SECONDS"); + memcpy( achHeader + 3*16+8, pszValue, MIN(16,strlen(pszValue)) ); + + memcpy( achHeader + 4*16, "VERSION ", 16 ); + pszValue = CSLFetchNameValueDef( papszOptions, "VERSION", "" ); + memcpy( achHeader + 4*16+8, pszValue, MIN(16,strlen(pszValue)) ); + + memcpy( achHeader + 5*16, "SYSTEM_F ", 16 ); + pszValue = CSLFetchNameValueDef( papszOptions, "SYSTEM_F", "" ); + memcpy( achHeader + 5*16+8, pszValue, MIN(16,strlen(pszValue)) ); + + memcpy( achHeader + 6*16, "SYSTEM_T ", 16 ); + pszValue = CSLFetchNameValueDef( papszOptions, "SYSTEM_T", "" ); + memcpy( achHeader + 6*16+8, pszValue, MIN(16,strlen(pszValue)) ); + + memcpy( achHeader + 7*16, "MAJOR_F ", 8); + memcpy( achHeader + 8*16, "MINOR_F ", 8 ); + memcpy( achHeader + 9*16, "MAJOR_T ", 8 ); + memcpy( achHeader + 10*16, "MINOR_T ", 8 ); + + VSIFWriteL( achHeader, 1, sizeof(achHeader), fp ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise update the header with an increased subfile count, */ +/* and advanced to the last record of the file. */ +/* -------------------------------------------------------------------- */ + else + { + VSIFSeekL( fp, 2*16 + 8, SEEK_SET ); + VSIFReadL( &nNumFile, 1, 4, fp ); + CPL_LSBPTR32( &nNumFile ); + + nNumFile++; + + CPL_LSBPTR32( &nNumFile ); + VSIFSeekL( fp, 2*16 + 8, SEEK_SET ); + VSIFWriteL( &nNumFile, 1, 4, fp ); + + vsi_l_offset nEnd; + + VSIFSeekL( fp, 0, SEEK_END ); + nEnd = VSIFTellL( fp ); + VSIFSeekL( fp, nEnd-16, SEEK_SET ); + } + +/* -------------------------------------------------------------------- */ +/* Write the grid header. */ +/* -------------------------------------------------------------------- */ + memset( achHeader, 0, sizeof(achHeader) ); + + memcpy( achHeader + 0*16, "SUB_NAME ", 16 ); + pszValue = CSLFetchNameValueDef( papszOptions, "SUB_NAME", "" ); + memcpy( achHeader + 0*16+8, pszValue, MIN(16,strlen(pszValue)) ); + + memcpy( achHeader + 1*16, "PARENT ", 16 ); + pszValue = CSLFetchNameValueDef( papszOptions, "PARENT", "NONE" ); + memcpy( achHeader + 1*16+8, pszValue, MIN(16,strlen(pszValue)) ); + + memcpy( achHeader + 2*16, "CREATED ", 16 ); + pszValue = CSLFetchNameValueDef( papszOptions, "CREATED", "" ); + memcpy( achHeader + 2*16+8, pszValue, MIN(16,strlen(pszValue)) ); + + memcpy( achHeader + 3*16, "UPDATED ", 16 ); + pszValue = CSLFetchNameValueDef( papszOptions, "UPDATED", "" ); + memcpy( achHeader + 3*16+8, pszValue, MIN(16,strlen(pszValue)) ); + + double dfValue; + + memcpy( achHeader + 4*16, "S_LAT ", 8 ); + dfValue = 0; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 4*16 + 8, &dfValue, 8 ); + + memcpy( achHeader + 5*16, "N_LAT ", 8 ); + dfValue = nYSize-1; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 5*16 + 8, &dfValue, 8 ); + + memcpy( achHeader + 6*16, "E_LONG ", 8 ); + dfValue = -1*(nXSize-1); + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 6*16 + 8, &dfValue, 8 ); + + memcpy( achHeader + 7*16, "W_LONG ", 8 ); + dfValue = 0; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 7*16 + 8, &dfValue, 8 ); + + memcpy( achHeader + 8*16, "LAT_INC ", 8 ); + dfValue = 1; + CPL_LSBPTR64( &dfValue ); + memcpy( achHeader + 8*16 + 8, &dfValue, 8 ); + + memcpy( achHeader + 9*16, "LONG_INC", 8 ); + memcpy( achHeader + 9*16 + 8, &dfValue, 8 ); + + memcpy( achHeader + 10*16, "GS_COUNT", 8 ); + GUInt32 nGSCount = nXSize * nYSize; + CPL_LSBPTR32( &nGSCount ); + memcpy( achHeader + 10*16+8, &nGSCount, 4 ); + + VSIFWriteL( achHeader, 1, sizeof(achHeader), fp ); + +/* -------------------------------------------------------------------- */ +/* Write zeroed grid data. */ +/* -------------------------------------------------------------------- */ + int i; + + memset( achHeader, 0, 16 ); + + // Use -1 (0x000080bf) as the default error value. + memset( achHeader + 10, 0x80, 1 ); + memset( achHeader + 11, 0xbf, 1 ); + memset( achHeader + 14, 0x80, 1 ); + memset( achHeader + 15, 0xbf, 1 ); + + for( i = 0; i < nXSize * nYSize; i++ ) + VSIFWriteL( achHeader, 1, 16, fp ); + +/* -------------------------------------------------------------------- */ +/* Write the end record. */ +/* -------------------------------------------------------------------- */ + memset( achHeader, 0, 16 ); + memcpy( achHeader, "END ", 8 ); + VSIFWriteL( achHeader, 1, 16, fp ); + VSIFCloseL( fp ); + + if( nNumFile == 1 ) + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); + else + { + CPLString osSubDSName; + osSubDSName.Printf( "NTv2:%d:%s", nNumFile-1, pszFilename ); + return (GDALDataset *) GDALOpen( osSubDSName, GA_Update ); + } +} + +/************************************************************************/ +/* GDALRegister_NTv2() */ +/************************************************************************/ + +void GDALRegister_NTv2() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "NTv2" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "NTv2" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "NTv2 Datum Grid Shift" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gsb" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Float32" ); + + poDriver->pfnOpen = NTv2Dataset::Open; + poDriver->pfnIdentify = NTv2Dataset::Identify; + poDriver->pfnCreate = NTv2Dataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/pauxdataset.cpp b/bazaar/plugin/gdal/frmts/raw/pauxdataset.cpp new file mode 100644 index 000000000..78682817c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/pauxdataset.cpp @@ -0,0 +1,1139 @@ +/****************************************************************************** + * $Id: pauxdataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: PCI .aux Driver + * Purpose: Implementation of PAuxDataset + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: pauxdataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_PAux(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* PAuxDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class PAuxRasterBand; + +class PAuxDataset : public RawDataset +{ + friend class PAuxRasterBand; + + VSILFILE *fpImage; // image data file. + + int nGCPCount; + GDAL_GCP *pasGCPList; + char *pszGCPProjection; + + void ScanForGCPs(); + char *PCI2WKT( const char *pszGeosys, const char *pszProjParms ); + + char *pszProjection; + + public: + PAuxDataset(); + ~PAuxDataset(); + + char *pszAuxFilename; + char **papszAuxLines; + int bAuxUpdated; + + virtual const char *GetProjectionRef(); + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszParmList ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* PAuxRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class PAuxRasterBand : public RawRasterBand +{ + GDALColorTable *poCT; + + public: + + PAuxRasterBand( GDALDataset *poDS, int nBand, VSILFILE * fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder ); + + ~PAuxRasterBand(); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual CPLErr SetNoDataValue( double ); + + virtual GDALColorTable *GetColorTable(); + virtual GDALColorInterp GetColorInterpretation(); + + virtual void SetDescription( const char *pszNewDescription ); +}; + +/************************************************************************/ +/* PAuxRasterBand() */ +/************************************************************************/ + +PAuxRasterBand::PAuxRasterBand( GDALDataset *poDS, int nBand, + VSILFILE * fpRaw, vsi_l_offset nImgOffset, + int nPixelOffset, int nLineOffset, + GDALDataType eDataType, int bNativeOrder ) + : RawRasterBand( poDS, nBand, fpRaw, + nImgOffset, nPixelOffset, nLineOffset, + eDataType, bNativeOrder, TRUE ) + +{ + PAuxDataset *poPDS = (PAuxDataset *) poDS; + + poCT = NULL; + +/* -------------------------------------------------------------------- */ +/* Does this channel have a description? */ +/* -------------------------------------------------------------------- */ + char szTarget[128]; + + sprintf( szTarget, "ChanDesc-%d", nBand ); + if( CSLFetchNameValue( poPDS->papszAuxLines, szTarget ) != NULL ) + GDALRasterBand::SetDescription( + CSLFetchNameValue( poPDS->papszAuxLines, szTarget ) ); + +/* -------------------------------------------------------------------- */ +/* See if we have colors. Currently we must have color zero, */ +/* but this shouldn't really be a limitation. */ +/* -------------------------------------------------------------------- */ + sprintf( szTarget, "METADATA_IMG_%d_Class_%d_Color", nBand, 0 ); + if( CSLFetchNameValue( poPDS->papszAuxLines, szTarget ) != NULL ) + { + const char *pszLine; + int i; + + poCT = new GDALColorTable(); + + for( i = 0; i < 256; i++ ) + { + int nRed, nGreen, nBlue; + + sprintf( szTarget, "METADATA_IMG_%d_Class_%d_Color", nBand, i ); + pszLine = CSLFetchNameValue( poPDS->papszAuxLines, szTarget ); + while( pszLine && *pszLine == ' ' ) + pszLine++; + + if( pszLine != NULL + && EQUALN(pszLine, "(RGB:",5) + && sscanf( pszLine+5, "%d %d %d", + &nRed, &nGreen, &nBlue ) == 3 ) + { + GDALColorEntry oColor; + + oColor.c1 = (short) nRed; + oColor.c2 = (short) nGreen; + oColor.c3 = (short) nBlue; + oColor.c4 = 255; + + poCT->SetColorEntry( i, &oColor ); + } + } + } +} + +/************************************************************************/ +/* ~PAuxRasterBand() */ +/************************************************************************/ + +PAuxRasterBand::~PAuxRasterBand() + +{ + if( poCT != NULL ) + delete poCT; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double PAuxRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + PAuxDataset *poPDS = (PAuxDataset *) poDS; + char szTarget[128]; + const char *pszLine; + + sprintf( szTarget, "METADATA_IMG_%d_NO_DATA_VALUE", nBand ); + + pszLine = CSLFetchNameValue( poPDS->papszAuxLines, szTarget ); + + if( pbSuccess != NULL ) + *pbSuccess = (pszLine != NULL); + + if( pszLine == NULL ) + return -1e8; + else + return CPLAtof(pszLine); +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr PAuxRasterBand::SetNoDataValue( double dfNewValue ) + +{ + PAuxDataset *poPDS = (PAuxDataset *) poDS; + char szTarget[128]; + char szValue[128]; + + if( GetAccess() == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Can't update readonly dataset." ); + return CE_Failure; + } + + sprintf( szTarget, "METADATA_IMG_%d_NO_DATA_VALUE", nBand ); + CPLsprintf( szValue, "%24.12f", dfNewValue ); + poPDS->papszAuxLines = + CSLSetNameValue( poPDS->papszAuxLines, szTarget, szValue ); + + poPDS->bAuxUpdated = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* SetDescription() */ +/* */ +/* We override the set description so we can mark the auxfile */ +/* info as changed. */ +/************************************************************************/ + +void PAuxRasterBand::SetDescription( const char *pszNewDescription ) + +{ + PAuxDataset *poPDS = (PAuxDataset *) poDS; + + if( GetAccess() == GA_Update ) + { + char szTarget[128]; + + sprintf( szTarget, "ChanDesc-%d", nBand ); + poPDS->papszAuxLines = + CSLSetNameValue( poPDS->papszAuxLines, + szTarget, pszNewDescription ); + + poPDS->bAuxUpdated = TRUE; + } + + GDALRasterBand::SetDescription( pszNewDescription ); +} + + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *PAuxRasterBand::GetColorTable() + +{ + return poCT; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp PAuxRasterBand::GetColorInterpretation() + +{ + if( poCT == NULL ) + return GCI_Undefined; + else + return GCI_PaletteIndex; +} + +/************************************************************************/ +/* ==================================================================== */ +/* PAuxDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* PAuxDataset() */ +/************************************************************************/ + +PAuxDataset::PAuxDataset() +{ + papszAuxLines = NULL; + fpImage = NULL; + bAuxUpdated = FALSE; + pszAuxFilename = NULL; + nGCPCount = 0; + pasGCPList = NULL; + + pszProjection = NULL; + pszGCPProjection = NULL; +} + +/************************************************************************/ +/* ~PAuxDataset() */ +/************************************************************************/ + +PAuxDataset::~PAuxDataset() + +{ + FlushCache(); + if( fpImage != NULL ) + VSIFCloseL( fpImage ); + + if( bAuxUpdated ) + { + CSLSetNameValueSeparator( papszAuxLines, ": " ); + CSLSave( papszAuxLines, pszAuxFilename ); + } + + CPLFree( pszProjection ); + + CPLFree( pszGCPProjection ); + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + + CPLFree( pszAuxFilename ); + CSLDestroy( papszAuxLines ); +} + +/************************************************************************/ +/* PCI2WKT() */ +/* */ +/* Convert PCI coordinate system to WKT. For now this is very */ +/* incomplete, but can be filled out in the future. */ +/************************************************************************/ + +char *PAuxDataset::PCI2WKT( const char *pszGeosys, + const char *pszProjParms ) + +{ + OGRSpatialReference oSRS; + + while( *pszGeosys == ' ' ) + pszGeosys++; + +/* -------------------------------------------------------------------- */ +/* Parse projection parameters array. */ +/* -------------------------------------------------------------------- */ + double adfProjParms[16]; + + memset( adfProjParms, 0, sizeof(adfProjParms) ); + + if( pszProjParms != NULL ) + { + char **papszTokens; + int i; + + papszTokens = CSLTokenizeString( pszProjParms ); + + for( i=0; papszTokens != NULL && papszTokens[i] != NULL && i < 16; i++) + adfProjParms[i] = CPLAtof(papszTokens[i]); + + CSLDestroy( papszTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Convert to SRS. */ +/* -------------------------------------------------------------------- */ + if( oSRS.importFromPCI( pszGeosys, NULL, adfProjParms ) == OGRERR_NONE ) + { + char *pszResult = NULL; + + oSRS.exportToWkt( &pszResult ); + + return pszResult; + } + else + return NULL; +} + +/************************************************************************/ +/* ScanForGCPs() */ +/************************************************************************/ + +void PAuxDataset::ScanForGCPs() + +{ +#define MAX_GCP 256 + + nGCPCount = 0; + pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),MAX_GCP); + +/* -------------------------------------------------------------------- */ +/* Get the GCP coordinate system. */ +/* -------------------------------------------------------------------- */ + const char *pszMapUnits, *pszProjParms; + + pszMapUnits = CSLFetchNameValue( papszAuxLines, "GCP_1_MapUnits" ); + pszProjParms = CSLFetchNameValue( papszAuxLines, "GCP_1_ProjParms" ); + + if( pszMapUnits != NULL ) + pszGCPProjection = PCI2WKT( pszMapUnits, pszProjParms ); + +/* -------------------------------------------------------------------- */ +/* Collect standalone GCPs. They look like: */ +/* */ +/* GCP_1_n = row, col, x, y [,z [,"id"[, "desc"]]] */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; nGCPCount < MAX_GCP; i++ ) + { + char szName[50]; + char **papszTokens; + + sprintf( szName, "GCP_1_%d", i+1 ); + if( CSLFetchNameValue( papszAuxLines, szName ) == NULL ) + break; + + papszTokens = CSLTokenizeStringComplex( + CSLFetchNameValue( papszAuxLines, szName ), + " ", TRUE, FALSE ); + + if( CSLCount(papszTokens) >= 4 ) + { + GDALInitGCPs( 1, pasGCPList + nGCPCount ); + + pasGCPList[nGCPCount].dfGCPX = CPLAtof(papszTokens[2]); + pasGCPList[nGCPCount].dfGCPY = CPLAtof(papszTokens[3]); + pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(papszTokens[0]); + pasGCPList[nGCPCount].dfGCPLine = CPLAtof(papszTokens[1]); + + if( CSLCount(papszTokens) > 4 ) + pasGCPList[nGCPCount].dfGCPZ = CPLAtof(papszTokens[4]); + + CPLFree( pasGCPList[nGCPCount].pszId ); + if( CSLCount(papszTokens) > 5 ) + { + pasGCPList[nGCPCount].pszId = CPLStrdup(papszTokens[5]); + } + else + { + sprintf( szName, "GCP_%d", i+1 ); + pasGCPList[nGCPCount].pszId = CPLStrdup( szName ); + } + + if( CSLCount(papszTokens) > 6 ) + { + CPLFree( pasGCPList[nGCPCount].pszInfo ); + pasGCPList[nGCPCount].pszInfo = CPLStrdup(papszTokens[6]); + } + + nGCPCount++; + } + + CSLDestroy(papszTokens); + } +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int PAuxDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *PAuxDataset::GetGCPProjection() + +{ + if( nGCPCount > 0 && pszGCPProjection != NULL ) + return pszGCPProjection; + else + return ""; +} + +/************************************************************************/ +/* GetGCP() */ +/************************************************************************/ + +const GDAL_GCP *PAuxDataset::GetGCPs() + +{ + return pasGCPList; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *PAuxDataset::GetProjectionRef() + +{ + if( pszProjection ) + return pszProjection; + else + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr PAuxDataset::GetGeoTransform( double * padfGeoTransform ) + +{ + if( CSLFetchNameValue(papszAuxLines, "UpLeftX") != NULL + && CSLFetchNameValue(papszAuxLines, "UpLeftY") != NULL + && CSLFetchNameValue(papszAuxLines, "LoRightX") != NULL + && CSLFetchNameValue(papszAuxLines, "LoRightY") != NULL ) + { + double dfUpLeftX, dfUpLeftY, dfLoRightX, dfLoRightY; + + dfUpLeftX = CPLAtof(CSLFetchNameValue(papszAuxLines, "UpLeftX" )); + dfUpLeftY = CPLAtof(CSLFetchNameValue(papszAuxLines, "UpLeftY" )); + dfLoRightX = CPLAtof(CSLFetchNameValue(papszAuxLines, "LoRightX" )); + dfLoRightY = CPLAtof(CSLFetchNameValue(papszAuxLines, "LoRightY" )); + + padfGeoTransform[0] = dfUpLeftX; + padfGeoTransform[1] = (dfLoRightX - dfUpLeftX) / GetRasterXSize(); + padfGeoTransform[2] = 0.0; + padfGeoTransform[3] = dfUpLeftY; + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = (dfLoRightY - dfUpLeftY) / GetRasterYSize(); + + return CE_None; + } + else + { + padfGeoTransform[0] = 0.0; + padfGeoTransform[1] = 1.0; + padfGeoTransform[2] = 0.0; + padfGeoTransform[3] = 0.0; + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = 1.0; + + return CE_Failure; + } +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr PAuxDataset::SetGeoTransform( double * padfGeoTransform ) + +{ + char szUpLeftX[128]; + char szUpLeftY[128]; + char szLoRightX[128]; + char szLoRightY[128]; + + if( ABS(padfGeoTransform[0]) < 181 + && ABS(padfGeoTransform[1]) < 1 ) + { + CPLsprintf( szUpLeftX, "%.12f", padfGeoTransform[0] ); + CPLsprintf( szUpLeftY, "%.12f", padfGeoTransform[3] ); + CPLsprintf( szLoRightX, "%.12f", + padfGeoTransform[0] + padfGeoTransform[1] * GetRasterXSize() ); + CPLsprintf( szLoRightY, "%.12f", + padfGeoTransform[3] + padfGeoTransform[5] * GetRasterYSize() ); + } + else + { + CPLsprintf( szUpLeftX, "%.3f", padfGeoTransform[0] ); + CPLsprintf( szUpLeftY, "%.3f", padfGeoTransform[3] ); + CPLsprintf( szLoRightX, "%.3f", + padfGeoTransform[0] + padfGeoTransform[1] * GetRasterXSize() ); + CPLsprintf( szLoRightY, "%.3f", + padfGeoTransform[3] + padfGeoTransform[5] * GetRasterYSize() ); + } + + papszAuxLines = CSLSetNameValue( papszAuxLines, + "UpLeftX", szUpLeftX ); + papszAuxLines = CSLSetNameValue( papszAuxLines, + "UpLeftY", szUpLeftY ); + papszAuxLines = CSLSetNameValue( papszAuxLines, + "LoRightX", szLoRightX ); + papszAuxLines = CSLSetNameValue( papszAuxLines, + "LoRightY", szLoRightY ); + + bAuxUpdated = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *PAuxDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i; + CPLString osAuxFilename; + char **papszTokens; + CPLString osTarget; + + if( poOpenInfo->nHeaderBytes < 1 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If this is an .aux file, fetch out and form the name of the */ +/* file it references. */ +/* -------------------------------------------------------------------- */ + + osTarget = poOpenInfo->pszFilename; + + if( EQUAL(CPLGetExtension( poOpenInfo->pszFilename ),"aux") + && EQUALN((const char *) poOpenInfo->pabyHeader,"AuxilaryTarget: ",16)) + { + char szAuxTarget[1024]; + char *pszPath; + const char *pszSrc = (const char *) poOpenInfo->pabyHeader+16; + + for( i = 0; + pszSrc[i] != 10 && pszSrc[i] != 13 && pszSrc[i] != '\0' + && i < (int) sizeof(szAuxTarget)-1; + i++ ) + { + szAuxTarget[i] = pszSrc[i]; + } + szAuxTarget[i] = '\0'; + + pszPath = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename)); + osTarget = CPLFormFilename(pszPath, szAuxTarget, NULL); + CPLFree(pszPath); + } + +/* -------------------------------------------------------------------- */ +/* Now we need to tear apart the filename to form a .aux */ +/* filename. */ +/* -------------------------------------------------------------------- */ + osAuxFilename = CPLResetExtension(osTarget,"aux"); + +/* -------------------------------------------------------------------- */ +/* Do we have a .aux file? */ +/* -------------------------------------------------------------------- */ + char** papszSiblingFiles = poOpenInfo->GetSiblingFiles(); + if( papszSiblingFiles != NULL + && CSLFindString( papszSiblingFiles, + CPLGetFilename(osAuxFilename) ) == -1 ) + { + return NULL; + } + + VSILFILE *fp; + + fp = VSIFOpenL( osAuxFilename, "r" ); + if( fp == NULL ) + { + osAuxFilename = CPLResetExtension(osTarget,"AUX"); + fp = VSIFOpenL( osAuxFilename, "r" ); + } + + if( fp == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Is this file a PCI .aux file? Check the first line for the */ +/* telltale AuxilaryTarget keyword. */ +/* */ +/* At this point we should be verifying that it refers to our */ +/* binary file, but that is a pretty involved test. */ +/* -------------------------------------------------------------------- */ + const char * pszLine; + + pszLine = CPLReadLineL( fp ); + + VSIFCloseL( fp ); + + if( pszLine == NULL + || (!EQUALN(pszLine,"AuxilaryTarget",14) + && !EQUALN(pszLine,"AuxiliaryTarget",15)) ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + PAuxDataset *poDS; + + poDS = new PAuxDataset(); + +/* -------------------------------------------------------------------- */ +/* Load the .aux file into a string list suitable to be */ +/* searched with CSLFetchNameValue(). */ +/* -------------------------------------------------------------------- */ + poDS->papszAuxLines = CSLLoad( osAuxFilename ); + poDS->pszAuxFilename = CPLStrdup(osAuxFilename); + +/* -------------------------------------------------------------------- */ +/* Find the RawDefinition line to establish overall parameters. */ +/* -------------------------------------------------------------------- */ + pszLine = CSLFetchNameValue(poDS->papszAuxLines, "RawDefinition"); + + // It seems PCI now writes out .aux files without RawDefinition in + // some cases. See bug 947. + if( pszLine == NULL ) + { + delete poDS; + return NULL; + } + + papszTokens = CSLTokenizeString(pszLine); + + if( CSLCount(papszTokens) < 3 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "RawDefinition missing or corrupt in %s.", + poOpenInfo->pszFilename ); + CSLDestroy( papszTokens ); + return NULL; + } + + poDS->nRasterXSize = atoi(papszTokens[0]); + poDS->nRasterYSize = atoi(papszTokens[1]); + poDS->nBands = atoi(papszTokens[2]); + poDS->eAccess = poOpenInfo->eAccess; + + CSLDestroy( papszTokens ); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || + !GDALCheckBandCount(poDS->nBands, FALSE)) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + poDS->fpImage = VSIFOpenL( osTarget, "rb+" ); + + if( poDS->fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "File %s is missing or read-only, check permissions.", + osTarget.c_str() ); + + delete poDS; + return NULL; + } + } + else + { + poDS->fpImage = VSIFOpenL( osTarget, "rb" ); + + if( poDS->fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "File %s is missing or unreadable.", + osTarget.c_str() ); + + delete poDS; + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Collect raw definitions of each channel and create */ +/* corresponding bands. */ +/* -------------------------------------------------------------------- */ + int iBand = 0; + for( i = 0; i < poDS->nBands; i++ ) + { + char szDefnName[32]; + GDALDataType eType; + int bNative = TRUE; + + sprintf( szDefnName, "ChanDefinition-%d", i+1 ); + + pszLine = CSLFetchNameValue(poDS->papszAuxLines, szDefnName); + if (pszLine == NULL) + { + continue; + } + + papszTokens = CSLTokenizeString(pszLine); + if( CSLCount(papszTokens) < 4 ) + { + // Skip the band with broken description + CSLDestroy( papszTokens ); + continue; + } + + if( EQUAL(papszTokens[0],"16U") ) + eType = GDT_UInt16; + else if( EQUAL(papszTokens[0],"16S") ) + eType = GDT_Int16; + else if( EQUAL(papszTokens[0],"32R") ) + eType = GDT_Float32; + else + eType = GDT_Byte; + + if( CSLCount(papszTokens) > 4 ) + { +#ifdef CPL_LSB + bNative = EQUAL(papszTokens[4],"Swapped"); +#else + bNative = EQUAL(papszTokens[4],"Unswapped"); +#endif + } + + vsi_l_offset nBandOffset = CPLScanUIntBig(papszTokens[1], + strlen(papszTokens[1])); + int nPixelOffset = atoi(papszTokens[2]); + int nLineOffset = atoi(papszTokens[3]); + + if (nPixelOffset <= 0 || nLineOffset <= 0) + { + // Skip the band with broken offsets + CSLDestroy( papszTokens ); + continue; + } + + poDS->SetBand( iBand+1, + new PAuxRasterBand( poDS, iBand+1, poDS->fpImage, + nBandOffset, + nPixelOffset, + nLineOffset, eType, bNative ) ); + iBand ++; + + CSLDestroy( papszTokens ); + } + + poDS->nBands = iBand; + +/* -------------------------------------------------------------------- */ +/* Get the projection. */ +/* -------------------------------------------------------------------- */ + const char *pszMapUnits, *pszProjParms; + + pszMapUnits = CSLFetchNameValue( poDS->papszAuxLines, "MapUnits" ); + pszProjParms = CSLFetchNameValue( poDS->papszAuxLines, "ProjParms" ); + + if( pszMapUnits != NULL ) + poDS->pszProjection = poDS->PCI2WKT( pszMapUnits, pszProjParms ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( osTarget ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, osTarget ); + + poDS->ScanForGCPs(); + poDS->bAuxUpdated = FALSE; + + return( poDS ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *PAuxDataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char **papszOptions ) + +{ + char *pszAuxFilename; + + const char *pszInterleave = CSLFetchNameValue( papszOptions, "INTERLEAVE" ); + if( pszInterleave == NULL ) + pszInterleave = "BAND"; + +/* -------------------------------------------------------------------- */ +/* Verify input options. */ +/* -------------------------------------------------------------------- */ + if( eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 + && eType != GDT_Int16 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create PCI .Aux labelled dataset with an illegal\n" + "data type (%s).\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Sum the sizes of the band pixel types. */ +/* -------------------------------------------------------------------- */ + int nPixelSizeSum = 0; + int iBand; + + for( iBand = 0; iBand < nBands; iBand++ ) + nPixelSizeSum += (GDALGetDataTypeSize(eType)/8); + +/* -------------------------------------------------------------------- */ +/* Try to create the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + + fp = VSIFOpenL( pszFilename, "w" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Just write out a couple of bytes to establish the binary */ +/* file, and then close it. */ +/* -------------------------------------------------------------------- */ + VSIFWriteL( (void *) "\0\0", 2, 1, fp ); + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Create the aux filename. */ +/* -------------------------------------------------------------------- */ + pszAuxFilename = (char *) CPLMalloc(strlen(pszFilename)+5); + strcpy( pszAuxFilename, pszFilename );; + + for( int i = strlen(pszAuxFilename)-1; i > 0; i-- ) + { + if( pszAuxFilename[i] == '.' ) + { + pszAuxFilename[i] = '\0'; + break; + } + } + + strcat( pszAuxFilename, ".aux" ); + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszAuxFilename, "wt" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszAuxFilename ); + return NULL; + } + CPLFree( pszAuxFilename ); + +/* -------------------------------------------------------------------- */ +/* We need to write out the original filename but without any */ +/* path components in the AuxilaryTarget line. Do so now. */ +/* -------------------------------------------------------------------- */ + int iStart; + + iStart = strlen(pszFilename)-1; + while( iStart > 0 && pszFilename[iStart-1] != '/' + && pszFilename[iStart-1] != '\\' ) + iStart--; + + VSIFPrintfL( fp, "AuxilaryTarget: %s\n", pszFilename + iStart ); + +/* -------------------------------------------------------------------- */ +/* Write out the raw definition for the dataset as a whole. */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, "RawDefinition: %d %d %d\n", + nXSize, nYSize, nBands ); + +/* -------------------------------------------------------------------- */ +/* Write out a definition for each band. We always write band */ +/* sequential files for now as these are pretty efficiently */ +/* handled by GDAL. */ +/* -------------------------------------------------------------------- */ + vsi_l_offset nImgOffset = 0; + + for( iBand = 0; iBand < nBands; iBand++ ) + { + const char *pszTypeName; + int nPixelOffset, nLineOffset; + vsi_l_offset nNextImgOffset; + +/* -------------------------------------------------------------------- */ +/* Establish our file layout based on supplied interleaving. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszInterleave,"LINE") ) + { + nPixelOffset = GDALGetDataTypeSize(eType)/8; + nLineOffset = nXSize * nPixelSizeSum; + nNextImgOffset = nImgOffset + nPixelOffset * nXSize; + } + else if( EQUAL(pszInterleave,"PIXEL") ) + { + nPixelOffset = nPixelSizeSum; + nLineOffset = nXSize * nPixelOffset; + nNextImgOffset = nImgOffset + (GDALGetDataTypeSize(eType)/8); + } + else /* default to band */ + { + nPixelOffset = GDALGetDataTypeSize(eType)/8; + nLineOffset = nXSize * nPixelOffset; + nNextImgOffset = nImgOffset + nYSize * (vsi_l_offset) nLineOffset; + } + +/* -------------------------------------------------------------------- */ +/* Write out line indicating layout. */ +/* -------------------------------------------------------------------- */ + if( eType == GDT_Float32 ) + pszTypeName = "32R"; + else if( eType == GDT_Int16 ) + pszTypeName = "16S"; + else if( eType == GDT_UInt16 ) + pszTypeName = "16U"; + else + pszTypeName = "8U"; + + VSIFPrintfL( fp, "ChanDefinition-%d: %s " CPL_FRMT_GIB " %d %d %s\n", + iBand+1, + pszTypeName, (GIntBig) nImgOffset, + nPixelOffset, nLineOffset, +#ifdef CPL_LSB + "Swapped" +#else + "Unswapped" +#endif + ); + + nImgOffset = nNextImgOffset; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + VSIFCloseL( fp ); + + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* PAuxDelete() */ +/************************************************************************/ + +CPLErr PAuxDelete( const char * pszBasename ) + +{ + VSILFILE *fp; + const char *pszLine; + + fp = VSIFOpenL( CPLResetExtension( pszBasename, "aux" ), "r" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s does not appear to be a PAux dataset, there is no .aux file.", + pszBasename ); + return CE_Failure; + } + + pszLine = CPLReadLineL( fp ); + VSIFCloseL( fp ); + + if( pszLine == NULL || !EQUALN(pszLine,"AuxilaryTarget",14) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s does not appear to be a PAux dataset,\n" + "the .aux file does not start with AuxilaryTarget", + pszBasename ); + return CE_Failure; + } + + if( VSIUnlink( pszBasename ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OS unlinking file %s.", pszBasename ); + return CE_Failure; + } + + VSIUnlink( CPLResetExtension( pszBasename, "aux" ) ); + + return CE_None; +} + +/************************************************************************/ +/* GDALRegister_PAux() */ +/************************************************************************/ + +void GDALRegister_PAux() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "PAux" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "PAux" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "PCI .aux Labelled" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#PAux" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Float32" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +"" ); + + poDriver->pfnOpen = PAuxDataset::Open; + poDriver->pfnCreate = PAuxDataset::Create; + poDriver->pfnDelete = PAuxDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/raw/pnmdataset.cpp b/bazaar/plugin/gdal/frmts/raw/pnmdataset.cpp new file mode 100644 index 000000000..26bf627ed --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/pnmdataset.cpp @@ -0,0 +1,439 @@ +/****************************************************************************** + * $Id: pnmdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: PNM Driver + * Purpose: Portable anymap file format imlementation + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: pnmdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +CPL_C_START +void GDALRegister_PNM(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* PNMDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class PNMDataset : public RawDataset +{ + VSILFILE *fpImage; // image data file. + + int bGeoTransformValid; + double adfGeoTransform[6]; + + public: + PNMDataset(); + ~PNMDataset(); + + virtual CPLErr GetGeoTransform( double * ); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ); +}; + +/************************************************************************/ +/* PNMDataset() */ +/************************************************************************/ + +PNMDataset::PNMDataset() +{ + fpImage = NULL; + bGeoTransformValid = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~PNMDataset() */ +/************************************************************************/ + +PNMDataset::~PNMDataset() + +{ + FlushCache(); + if( fpImage != NULL ) + VSIFCloseL( fpImage ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr PNMDataset::GetGeoTransform( double * padfTransform ) + +{ + + if( bGeoTransformValid ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + return CE_None; + } + else + return CE_Failure; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int PNMDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify that this is a _raw_ ppm or pgm file. Note, we don't */ +/* support ascii files, or pbm (1bit) files. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 10 ) + return FALSE; + + if( poOpenInfo->pabyHeader[0] != 'P' || + (poOpenInfo->pabyHeader[2] != ' ' && // XXX: Magick number + poOpenInfo->pabyHeader[2] != '\t' && // may be followed + poOpenInfo->pabyHeader[2] != '\n' && // any of the blank + poOpenInfo->pabyHeader[2] != '\r') ) // characters + return FALSE; + + if( poOpenInfo->pabyHeader[1] != '5' + && poOpenInfo->pabyHeader[1] != '6' ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *PNMDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify that this is a _raw_ ppm or pgm file. Note, we don't */ +/* support ascii files, or pbm (1bit) files. */ +/* -------------------------------------------------------------------- */ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Parse out the tokens from the header. */ +/* -------------------------------------------------------------------- */ + const char *pszSrc = (const char *) poOpenInfo->pabyHeader; + char szToken[512]; + int iIn, iToken = 0, nWidth =-1, nHeight=-1, nMaxValue=-1; + unsigned int iOut; + + iIn = 2; + while( iIn < poOpenInfo->nHeaderBytes && iToken < 3 ) + { + iOut = 0; + szToken[0] = '\0'; + while( iOut < sizeof(szToken) && iIn < poOpenInfo->nHeaderBytes ) + { + if( pszSrc[iIn] == '#' ) + { + while( pszSrc[iIn] != 10 && pszSrc[iIn] != 13 + && iIn < poOpenInfo->nHeaderBytes - 1 ) + iIn++; + } + + if( iOut != 0 && isspace((unsigned char)pszSrc[iIn]) ) + { + szToken[iOut] = '\0'; + + if( iToken == 0 ) + nWidth = atoi(szToken); + else if( iToken == 1 ) + nHeight = atoi(szToken); + else if( iToken == 2 ) + nMaxValue = atoi(szToken); + + iToken++; + iIn++; + break; + } + + else if( !isspace((unsigned char)pszSrc[iIn]) ) + { + szToken[iOut++] = pszSrc[iIn]; + } + + iIn++; + } + } + + CPLDebug( "PNM", "PNM header contains: width=%d, height=%d, maxval=%d", + nWidth, nHeight, nMaxValue ); + + if( iToken != 3 || nWidth < 1 || nHeight < 1 || nMaxValue < 1 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + PNMDataset *poDS; + + poDS = new PNMDataset(); + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = nWidth; + poDS->nRasterYSize = nHeight; + +/* -------------------------------------------------------------------- */ +/* Open file */ +/* -------------------------------------------------------------------- */ + + if( poOpenInfo->eAccess == GA_Update ) + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb+" ); + else + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + + if( poDS->fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to re-open %s within PNM driver.\n", + poOpenInfo->pszFilename ); + return NULL; + } + + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int bMSBFirst = TRUE, iPixelSize; + GDALDataType eDataType; + +#ifdef CPL_LSB + bMSBFirst = FALSE; +#endif + + if ( nMaxValue < 256 ) + eDataType = GDT_Byte; + else + eDataType = GDT_UInt16; + + iPixelSize = GDALGetDataTypeSize( eDataType ) / 8; + + if( poOpenInfo->pabyHeader[1] == '5' ) + { + if (nWidth > INT_MAX / iPixelSize) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Int overflow occured."); + delete poDS; + return NULL; + } + poDS->SetBand( + 1, new RawRasterBand( poDS, 1, poDS->fpImage, iIn, iPixelSize, + nWidth*iPixelSize, eDataType, bMSBFirst, TRUE )); + poDS->GetRasterBand(1)->SetColorInterpretation( GCI_GrayIndex ); + } + else + { + if (nWidth > INT_MAX / (3 * iPixelSize)) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Int overflow occured."); + delete poDS; + return NULL; + } + poDS->SetBand( + 1, new RawRasterBand( poDS, 1, poDS->fpImage, iIn, 3*iPixelSize, + nWidth*3*iPixelSize, eDataType, bMSBFirst, TRUE )); + poDS->SetBand( + 2, new RawRasterBand( poDS, 2, poDS->fpImage, iIn+iPixelSize, + 3*iPixelSize, nWidth*3*iPixelSize, + eDataType, bMSBFirst, TRUE )); + poDS->SetBand( + 3, new RawRasterBand( poDS, 3, poDS->fpImage, iIn+2*iPixelSize, + 3*iPixelSize, nWidth*3*iPixelSize, + eDataType, bMSBFirst, TRUE )); + + poDS->GetRasterBand(1)->SetColorInterpretation( GCI_RedBand ); + poDS->GetRasterBand(2)->SetColorInterpretation( GCI_GreenBand ); + poDS->GetRasterBand(3)->SetColorInterpretation( GCI_BlueBand ); + } + +/* -------------------------------------------------------------------- */ +/* Check for world file. */ +/* -------------------------------------------------------------------- */ + poDS->bGeoTransformValid = + GDALReadWorldFile( poOpenInfo->pszFilename, ".wld", + poDS->adfGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *PNMDataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify input options. */ +/* -------------------------------------------------------------------- */ + if( eType != GDT_Byte && eType != GDT_UInt16 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create PNM dataset with an illegal\n" + "data type (%s), only Byte and UInt16 supported.\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + + if( nBands != 1 && nBands != 3 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create PNM dataset with an illegal number\n" + "of bands (%d). Must be 1 (greyscale) or 3 (RGB).\n", + nBands ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to create the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + + fp = VSIFOpenL( pszFilename, "wb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write out the header. */ +/* -------------------------------------------------------------------- */ + char szHeader[500]; + const char *pszMaxValue = NULL; + int nMaxValue = 0; + + pszMaxValue = CSLFetchNameValue( papszOptions, "MAXVAL" ); + if ( pszMaxValue ) + { + nMaxValue = atoi( pszMaxValue ); + if ( eType == GDT_Byte && (nMaxValue > 255 || nMaxValue < 0) ) + nMaxValue = 255; + else if ( nMaxValue > 65535 || nMaxValue < 0 ) + nMaxValue = 65535; + } + else + { + if ( eType == GDT_Byte ) + nMaxValue = 255; + else + nMaxValue = 65535; + } + + + memset( szHeader, 0, sizeof(szHeader) ); + + if( nBands == 3 ) + sprintf( szHeader, "P6\n%d %d\n%d\n", nXSize, nYSize, nMaxValue ); + else + sprintf( szHeader, "P5\n%d %d\n%d\n", nXSize, nYSize, nMaxValue ); + + VSIFWriteL( (void *) szHeader, strlen(szHeader) + 2, 1, fp ); + VSIFCloseL( fp ); + + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* GDALRegister_PNM() */ +/************************************************************************/ + +void GDALRegister_PNM() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "PNM" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "PNM" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Portable Pixmap Format (netpbm)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#PNM" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "pnm" ); + poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, + "image/x-portable-anymap" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte UInt16" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = PNMDataset::Open; + poDriver->pfnCreate = PNMDataset::Create; + poDriver->pfnIdentify = PNMDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/rawdataset.cpp b/bazaar/plugin/gdal/frmts/raw/rawdataset.cpp new file mode 100644 index 000000000..338272b98 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/rawdataset.cpp @@ -0,0 +1,1242 @@ +/****************************************************************************** + * $Id: rawdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: Generic Raw Binary Driver + * Purpose: Implementation of RawDataset and RawRasterBand classes. + * Author: Frank Warmerdam, warmerda@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: rawdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +/************************************************************************/ +/* RawRasterBand() */ +/************************************************************************/ + +RawRasterBand::RawRasterBand( GDALDataset *poDS, int nBand, + void * fpRaw, vsi_l_offset nImgOffset, + int nPixelOffset, int nLineOffset, + GDALDataType eDataType, int bNativeOrder, + int bIsVSIL, int bOwnsFP ) + +{ + this->poDS = poDS; + this->nBand = nBand; + this->eDataType = eDataType; + this->bIsVSIL = bIsVSIL; + this->bOwnsFP =bOwnsFP; + + if (bIsVSIL) + { + this->fpRaw = NULL; + this->fpRawL = (VSILFILE*) fpRaw; + } + else + { + this->fpRaw = (FILE*) fpRaw; + this->fpRawL = NULL; + } + this->nImgOffset = nImgOffset; + this->nPixelOffset = nPixelOffset; + this->nLineOffset = nLineOffset; + this->bNativeOrder = bNativeOrder; + + CPLDebug( "GDALRaw", + "RawRasterBand(%p,%d,%p,\n" + " Off=%d,PixOff=%d,LineOff=%d,%s,%d)\n", + poDS, nBand, fpRaw, + (unsigned int) nImgOffset, nPixelOffset, nLineOffset, + GDALGetDataTypeName(eDataType), bNativeOrder ); + +/* -------------------------------------------------------------------- */ +/* Treat one scanline as the block size. */ +/* -------------------------------------------------------------------- */ + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + +/* -------------------------------------------------------------------- */ +/* Initialize other fields, and setup the line buffer. */ +/* -------------------------------------------------------------------- */ + Initialize(); +} + +/************************************************************************/ +/* RawRasterBand() */ +/************************************************************************/ + +RawRasterBand::RawRasterBand( void * fpRaw, vsi_l_offset nImgOffset, + int nPixelOffset, int nLineOffset, + GDALDataType eDataType, int bNativeOrder, + int nXSize, int nYSize, int bIsVSIL, int bOwnsFP ) + +{ + this->poDS = NULL; + this->nBand = 1; + this->eDataType = eDataType; + this->bIsVSIL = bIsVSIL; + this->bOwnsFP =bOwnsFP; + + if (bIsVSIL) + { + this->fpRaw = NULL; + this->fpRawL = (VSILFILE*) fpRaw; + } + else + { + this->fpRaw = (FILE*) fpRaw; + this->fpRawL = NULL; + } + this->nImgOffset = nImgOffset; + this->nPixelOffset = nPixelOffset; + this->nLineOffset = nLineOffset; + this->bNativeOrder = bNativeOrder; + + + CPLDebug( "GDALRaw", + "RawRasterBand(floating,Off=%d,PixOff=%d,LineOff=%d,%s,%d)\n", + (unsigned int) nImgOffset, nPixelOffset, nLineOffset, + GDALGetDataTypeName(eDataType), bNativeOrder ); + +/* -------------------------------------------------------------------- */ +/* Treat one scanline as the block size. */ +/* -------------------------------------------------------------------- */ + nBlockXSize = nXSize; + nBlockYSize = 1; + nRasterXSize = nXSize; + nRasterYSize = nYSize; + if (!GDALCheckDatasetDimensions(nXSize, nYSize)) + { + pLineBuffer = NULL; + return; + } + +/* -------------------------------------------------------------------- */ +/* Initialize other fields, and setup the line buffer. */ +/* -------------------------------------------------------------------- */ + Initialize(); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void RawRasterBand::Initialize() + +{ + poCT = NULL; + eInterp = GCI_Undefined; + + papszCategoryNames = NULL; + + bDirty = FALSE; + +/* -------------------------------------------------------------------- */ +/* Allocate working scanline. */ +/* -------------------------------------------------------------------- */ + nLoadedScanline = -1; + if (nBlockXSize <= 0 || nPixelOffset > INT_MAX / nBlockXSize) + { + nLineSize = 0; + pLineBuffer = NULL; + } + else + { + nLineSize = ABS(nPixelOffset) * nBlockXSize; + pLineBuffer = VSIMalloc2( ABS(nPixelOffset), nBlockXSize ); + } + if (pLineBuffer == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Could not allocate line buffer : nPixelOffset=%d, nBlockXSize=%d", + nPixelOffset, nBlockXSize); + } + + if( nPixelOffset >= 0 ) + pLineStart = pLineBuffer; + else + pLineStart = ((char *) pLineBuffer) + ABS(nPixelOffset) * (nBlockXSize-1); +} + +/************************************************************************/ +/* ~RawRasterBand() */ +/************************************************************************/ + +RawRasterBand::~RawRasterBand() + +{ + if( poCT ) + delete poCT; + + CSLDestroy( papszCategoryNames ); + + FlushCache(); + + if (bOwnsFP) + { + if ( bIsVSIL ) + VSIFCloseL( fpRawL ); + else + VSIFClose( fpRaw ); + } + + CPLFree( pLineBuffer ); +} + + +/************************************************************************/ +/* SetAccess() */ +/************************************************************************/ + +void RawRasterBand::SetAccess( GDALAccess eAccess ) +{ + this->eAccess = eAccess; +} + +/************************************************************************/ +/* FlushCache() */ +/* */ +/* We override this so we have the opportunity to call */ +/* fflush(). We don't want to do this all the time in the */ +/* write block function as it is kind of expensive. */ +/************************************************************************/ + +CPLErr RawRasterBand::FlushCache() + +{ + CPLErr eErr; + + eErr = GDALRasterBand::FlushCache(); + if( eErr != CE_None ) + return eErr; + + // If we have unflushed raw, flush it to disk now. + if ( bDirty ) + { + if( bIsVSIL ) + VSIFFlushL( fpRawL ); + else + VSIFFlush( fpRaw ); + + bDirty = FALSE; + } + + return CE_None; +} + +/************************************************************************/ +/* AccessLine() */ +/************************************************************************/ + +CPLErr RawRasterBand::AccessLine( int iLine ) + +{ + if (pLineBuffer == NULL) + return CE_Failure; + + if( nLoadedScanline == iLine ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Figure out where to start reading. */ +/* -------------------------------------------------------------------- */ + vsi_l_offset nReadStart; + if( nPixelOffset >= 0 ) + nReadStart = nImgOffset + (vsi_l_offset)iLine * nLineOffset; + else + { + nReadStart = nImgOffset + (vsi_l_offset)iLine * nLineOffset + - ABS(nPixelOffset) * (nBlockXSize-1); + } + +/* -------------------------------------------------------------------- */ +/* Seek to the right line. */ +/* -------------------------------------------------------------------- */ + if( Seek(nReadStart, SEEK_SET) == -1 ) + { + if (poDS != NULL && poDS->GetAccess() == GA_ReadOnly) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to scanline %d @ " CPL_FRMT_GUIB ".\n", + iLine, nImgOffset + (vsi_l_offset)iLine * nLineOffset ); + return CE_Failure; + } + else + { + memset( pLineBuffer, 0, nPixelOffset * nBlockXSize ); + nLoadedScanline = iLine; + return CE_None; + } + } + +/* -------------------------------------------------------------------- */ +/* Read the line. Take care not to request any more bytes than */ +/* are needed, and not to lose a partially successful scanline */ +/* read. */ +/* -------------------------------------------------------------------- */ + int nBytesToRead, nBytesActuallyRead; + + nBytesToRead = ABS(nPixelOffset) * (nBlockXSize - 1) + + GDALGetDataTypeSize(GetRasterDataType()) / 8; + + nBytesActuallyRead = Read( pLineBuffer, 1, nBytesToRead ); + if( nBytesActuallyRead < nBlockXSize ) + { + if (poDS != NULL && poDS->GetAccess() == GA_ReadOnly) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read scanline %d.\n", + iLine); + return CE_Failure; + } + else + { + memset( ((GByte *) pLineBuffer) + nBytesActuallyRead, + 0, nBytesToRead - nBytesActuallyRead ); + } + } + +/* -------------------------------------------------------------------- */ +/* Byte swap the interesting data, if required. */ +/* -------------------------------------------------------------------- */ + if( !bNativeOrder && eDataType != GDT_Byte ) + { + if( GDALDataTypeIsComplex( eDataType ) ) + { + int nWordSize; + + nWordSize = GDALGetDataTypeSize(eDataType)/16; + GDALSwapWords( pLineBuffer, nWordSize, nBlockXSize, ABS(nPixelOffset) ); + GDALSwapWords( ((GByte *) pLineBuffer)+nWordSize, + nWordSize, nBlockXSize, ABS(nPixelOffset) ); + } + else + GDALSwapWords( pLineBuffer, GDALGetDataTypeSize(eDataType)/8, + nBlockXSize, ABS(nPixelOffset) ); + } + + nLoadedScanline = iLine; + + return CE_None; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr RawRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + CPLErr eErr; + + CPLAssert( nBlockXOff == 0 ); + + if (pLineBuffer == NULL) + return CE_Failure; + + eErr = AccessLine( nBlockYOff ); + +/* -------------------------------------------------------------------- */ +/* Copy data from disk buffer to user block buffer. */ +/* -------------------------------------------------------------------- */ + GDALCopyWords( pLineStart, eDataType, nPixelOffset, + pImage, eDataType, GDALGetDataTypeSize(eDataType)/8, + nBlockXSize ); + + return eErr; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr RawRasterBand::IWriteBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + CPLErr eErr = CE_None; + + CPLAssert( nBlockXOff == 0 ); + + if (pLineBuffer == NULL) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* If the data for this band is completely contiguous we don't */ +/* have to worry about pre-reading from disk. */ +/* -------------------------------------------------------------------- */ + if( ABS(nPixelOffset) > GDALGetDataTypeSize(eDataType) / 8 ) + eErr = AccessLine( nBlockYOff ); + +/* -------------------------------------------------------------------- */ +/* Copy data from user buffer into disk buffer. */ +/* -------------------------------------------------------------------- */ + GDALCopyWords( pImage, eDataType, GDALGetDataTypeSize(eDataType)/8, + pLineStart, eDataType, nPixelOffset, + nBlockXSize ); + +/* -------------------------------------------------------------------- */ +/* Byte swap (if necessary) back into disk order before writing. */ +/* -------------------------------------------------------------------- */ + if( !bNativeOrder && eDataType != GDT_Byte ) + { + if( GDALDataTypeIsComplex( eDataType ) ) + { + int nWordSize; + + nWordSize = GDALGetDataTypeSize(eDataType)/16; + GDALSwapWords( pLineBuffer, nWordSize, nBlockXSize, + ABS(nPixelOffset) ); + GDALSwapWords( ((GByte *) pLineBuffer)+nWordSize, + nWordSize, nBlockXSize, ABS(nPixelOffset) ); + } + else + GDALSwapWords( pLineBuffer, GDALGetDataTypeSize(eDataType)/8, + nBlockXSize, ABS(nPixelOffset) ); + } + +/* -------------------------------------------------------------------- */ +/* Figure out where to start reading. */ +/* -------------------------------------------------------------------- */ + vsi_l_offset nWriteStart; + if( nPixelOffset >= 0 ) + nWriteStart = nImgOffset + (vsi_l_offset)nBlockYOff * nLineOffset; + else + { + nWriteStart = nImgOffset + (vsi_l_offset)nBlockYOff * nLineOffset + - ABS(nPixelOffset) * (nBlockXSize-1); + } + +/* -------------------------------------------------------------------- */ +/* Seek to correct location. */ +/* -------------------------------------------------------------------- */ + if( Seek( nWriteStart, SEEK_SET ) == -1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to scanline %d @ " CPL_FRMT_GUIB " to write to file.\n", + nBlockYOff, nImgOffset + nBlockYOff * nLineOffset ); + + eErr = CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Write data buffer. */ +/* -------------------------------------------------------------------- */ + int nBytesToWrite; + + nBytesToWrite = ABS(nPixelOffset) * (nBlockXSize - 1) + + GDALGetDataTypeSize(GetRasterDataType()) / 8; + + if( eErr == CE_None + && Write( pLineBuffer, 1, nBytesToWrite ) < (size_t) nBytesToWrite ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to write scanline %d to file.\n", + nBlockYOff ); + + eErr = CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Byte swap (if necessary) back into machine order so the */ +/* buffer is still usable for reading purposes. */ +/* -------------------------------------------------------------------- */ + if( !bNativeOrder && eDataType != GDT_Byte ) + { + if( GDALDataTypeIsComplex( eDataType ) ) + { + int nWordSize; + + nWordSize = GDALGetDataTypeSize(eDataType)/16; + GDALSwapWords( pLineBuffer, nWordSize, nBlockXSize, + ABS(nPixelOffset) ); + GDALSwapWords( ((GByte *) pLineBuffer)+nWordSize, + nWordSize, nBlockXSize, + ABS(nPixelOffset) ); + } + else + GDALSwapWords( pLineBuffer, GDALGetDataTypeSize(eDataType)/8, + nBlockXSize, ABS(nPixelOffset) ); + } + + bDirty = TRUE; + return eErr; +} + +/************************************************************************/ +/* AccessBlock() */ +/************************************************************************/ + +CPLErr RawRasterBand::AccessBlock( vsi_l_offset nBlockOff, int nBlockSize, + void * pData ) +{ + int nBytesActuallyRead; + +/* -------------------------------------------------------------------- */ +/* Seek to the right block. */ +/* -------------------------------------------------------------------- */ + if( Seek( nBlockOff, SEEK_SET ) == -1 ) + { + memset( pData, 0, nBlockSize ); + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Read the block. */ +/* -------------------------------------------------------------------- */ + nBytesActuallyRead = Read( pData, 1, nBlockSize ); + if( nBytesActuallyRead < nBlockSize ) + { + + memset( ((GByte *) pData) + nBytesActuallyRead, + 0, nBlockSize - nBytesActuallyRead ); + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Byte swap the interesting data, if required. */ +/* -------------------------------------------------------------------- */ + if( !bNativeOrder && eDataType != GDT_Byte ) + { + if( GDALDataTypeIsComplex( eDataType ) ) + { + int nWordSize; + + nWordSize = GDALGetDataTypeSize(eDataType)/16; + GDALSwapWords( pData, nWordSize, nBlockSize / nPixelOffset, + nPixelOffset ); + GDALSwapWords( ((GByte *) pData) + nWordSize, + nWordSize, nBlockSize / nPixelOffset, nPixelOffset ); + } + else + GDALSwapWords( pData, GDALGetDataTypeSize(eDataType) / 8, + nBlockSize / nPixelOffset, nPixelOffset ); + } + + return CE_None; +} + +/************************************************************************/ +/* IsSignificantNumberOfLinesLoaded() */ +/* */ +/* Check if there is a significant number of scanlines (>20%) from the */ +/* specified block of lines already cached. */ +/************************************************************************/ + +int RawRasterBand::IsSignificantNumberOfLinesLoaded( int nLineOff, int nLines ) +{ + int iLine; + int nCountLoaded = 0; + + for ( iLine = nLineOff; iLine < nLineOff + nLines; iLine++ ) + { + GDALRasterBlock *poBlock = TryGetLockedBlockRef( 0, iLine ); + if( poBlock != NULL ) + { + poBlock->DropLock(); + nCountLoaded ++; + if( nCountLoaded > nLines / 20 ) + { + return TRUE; + } + } + } + + return FALSE; +} + +/************************************************************************/ +/* CanUseDirectIO() */ +/************************************************************************/ + +int RawRasterBand::CanUseDirectIO(CPL_UNUSED int nXOff, + int nYOff, + int nXSize, + int nYSize, + CPL_UNUSED GDALDataType eBufType) +{ + +/* -------------------------------------------------------------------- */ +/* Use direct IO without caching if: */ +/* */ +/* GDAL_ONE_BIG_READ is enabled */ +/* */ +/* or */ +/* */ +/* the length of a scanline on disk is more than 50000 bytes, and the */ +/* width of the requested chunk is less than 40% of the whole scanline */ +/* and no significant number of requested scanlines are already in the */ +/* cache. */ +/* -------------------------------------------------------------------- */ + if( nPixelOffset < 0 ) + { + return FALSE; + } + + const char* pszGDAL_ONE_BIG_READ = CPLGetConfigOption( "GDAL_ONE_BIG_READ", NULL); + if ( pszGDAL_ONE_BIG_READ == NULL ) + { + int nBytesToRW = nPixelOffset * nXSize; + if ( nLineSize < 50000 + || nBytesToRW > nLineSize / 5 * 2 + || IsSignificantNumberOfLinesLoaded( nYOff, nYSize ) ) + { + + return FALSE; + } + return TRUE; + } + else + return CSLTestBoolean(pszGDAL_ONE_BIG_READ); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr RawRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + int nBandDataSize = GDALGetDataTypeSize(eDataType) / 8; + int nBufDataSize = GDALGetDataTypeSize( eBufType ) / 8; + int nBytesToRW = nPixelOffset * nXSize; + + if( !CanUseDirectIO(nXOff, nYOff, nXSize, nYSize, eBufType ) ) + { + return GDALRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, + nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + } + + CPLDebug("RAW", "Using direct IO implementation"); + +/* ==================================================================== */ +/* Read data. */ +/* ==================================================================== */ + if ( eRWFlag == GF_Read ) + { +/* -------------------------------------------------------------------- */ +/* Do we have overviews that would be appropriate to satisfy */ +/* this request? */ +/* -------------------------------------------------------------------- */ + if( (nBufXSize < nXSize || nBufYSize < nYSize) + && GetOverviewCount() > 0 ) + { + if( OverviewRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, psExtraArg ) == CE_None ) + return CE_None; + } + +/* ==================================================================== */ +/* 1. Simplest case when we should get contiguous block */ +/* of uninterleaved pixels. */ +/* ==================================================================== */ + if ( nXSize == GetXSize() + && nXSize == nBufXSize + && nYSize == nBufYSize + && eBufType == eDataType + && nPixelOffset == nBandDataSize + && nPixelSpace == nBufDataSize + && nLineSpace == nPixelSpace * nXSize ) + { + vsi_l_offset nOffset = nImgOffset + + (vsi_l_offset)nYOff * nLineOffset + nXOff; + if ( AccessBlock( nOffset, + nXSize * nYSize * nBandDataSize, pData ) != CE_None ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read %d bytes at " CPL_FRMT_GUIB ".", + nXSize * nYSize * nBandDataSize, nOffset); + } + } + +/* ==================================================================== */ +/* 2. Case when we need deinterleave and/or subsample data. */ +/* ==================================================================== */ + else + { + GByte *pabyData; + double dfSrcXInc, dfSrcYInc; + int iLine; + + dfSrcXInc = (double)nXSize / nBufXSize; + dfSrcYInc = (double)nYSize / nBufYSize; + + + pabyData = (GByte *) CPLMalloc( nBytesToRW ); + + for ( iLine = 0; iLine < nBufYSize; iLine++ ) + { + vsi_l_offset nOffset = nImgOffset + + ((vsi_l_offset)nYOff + + (vsi_l_offset)(iLine * dfSrcYInc)) * nLineOffset + + nXOff * nPixelOffset; + if ( AccessBlock( nOffset, + nBytesToRW, pabyData ) != CE_None ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read %d bytes at " CPL_FRMT_GUIB ".", + nBytesToRW, nOffset ); + } + +/* -------------------------------------------------------------------- */ +/* Copy data from disk buffer to user block buffer and subsample, */ +/* if needed. */ +/* -------------------------------------------------------------------- */ + if ( nXSize == nBufXSize && nYSize == nBufYSize ) + { + GDALCopyWords( pabyData, eDataType, nPixelOffset, + (GByte *)pData + (vsi_l_offset)iLine * nLineSpace, + eBufType, nPixelSpace, nXSize ); + } + else + { + int iPixel; + + for ( iPixel = 0; iPixel < nBufXSize; iPixel++ ) + { + GDALCopyWords( pabyData + + (vsi_l_offset)(iPixel * dfSrcXInc) * nPixelOffset, + eDataType, nPixelOffset, + (GByte *)pData + (vsi_l_offset)iLine * nLineSpace + + (vsi_l_offset)iPixel * nPixelSpace, + eBufType, nPixelSpace, 1 ); + } + } + + if( psExtraArg->pfnProgress != NULL && + !psExtraArg->pfnProgress(1.0 * (iLine + 1) / nBufYSize, "", + psExtraArg->pProgressData) ) + { + CPLFree( pabyData ); + return CE_Failure; + } + } + + CPLFree( pabyData ); + } + } + +/* ==================================================================== */ +/* Write data. */ +/* ==================================================================== */ + else + { + int nBytesActuallyWritten; + +/* ==================================================================== */ +/* 1. Simplest case when we should write contiguous block */ +/* of uninterleaved pixels. */ +/* ==================================================================== */ + if ( nXSize == GetXSize() + && nXSize == nBufXSize + && nYSize == nBufYSize + && eBufType == eDataType + && nPixelOffset == nBandDataSize + && nPixelSpace == nBufDataSize + && nLineSpace == nPixelSpace * nXSize ) + { +/* -------------------------------------------------------------------- */ +/* Byte swap the data buffer, if required. */ +/* -------------------------------------------------------------------- */ + if( !bNativeOrder && eDataType != GDT_Byte ) + { + if( GDALDataTypeIsComplex( eDataType ) ) + { + int nWordSize; + + nWordSize = GDALGetDataTypeSize(eDataType)/16; + GDALSwapWords( pData, nWordSize, nXSize, nPixelOffset ); + GDALSwapWords( ((GByte *) pData) + nWordSize, + nWordSize, nXSize, nPixelOffset ); + } + else + GDALSwapWords( pData, nBandDataSize, nXSize, nPixelOffset ); + } + +/* -------------------------------------------------------------------- */ +/* Seek to the right block. */ +/* -------------------------------------------------------------------- */ + vsi_l_offset nOffset = nImgOffset + (vsi_l_offset)nYOff * nLineOffset + nXOff; + if( Seek( nOffset, SEEK_SET) == -1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to " CPL_FRMT_GUIB " to write data.\n", + nOffset); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Write the block. */ +/* -------------------------------------------------------------------- */ + nBytesToRW = nXSize * nYSize * nBandDataSize; + + nBytesActuallyWritten = Write( pData, 1, nBytesToRW ); + if( nBytesActuallyWritten < nBytesToRW ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to write %d bytes to file. %d bytes written", + nBytesToRW, nBytesActuallyWritten ); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Byte swap (if necessary) back into machine order so the */ +/* buffer is still usable for reading purposes. */ +/* -------------------------------------------------------------------- */ + if( !bNativeOrder && eDataType != GDT_Byte ) + { + if( GDALDataTypeIsComplex( eDataType ) ) + { + int nWordSize; + + nWordSize = GDALGetDataTypeSize(eDataType)/16; + GDALSwapWords( pData, nWordSize, nXSize, nPixelOffset ); + GDALSwapWords( ((GByte *) pData) + nWordSize, + nWordSize, nXSize, nPixelOffset ); + } + else + GDALSwapWords( pData, nBandDataSize, nXSize, nPixelOffset ); + } + } + +/* ==================================================================== */ +/* 2. Case when we need deinterleave and/or subsample data. */ +/* ==================================================================== */ + else + { + GByte *pabyData; + double dfSrcXInc, dfSrcYInc; + vsi_l_offset nBlockOff; + int iLine; + + dfSrcXInc = (double)nXSize / nBufXSize; + dfSrcYInc = (double)nYSize / nBufYSize; + + pabyData = (GByte *) CPLMalloc( nBytesToRW ); + + for ( iLine = 0; iLine < nBufYSize; iLine++ ) + { + nBlockOff = nImgOffset + + ((vsi_l_offset)nYOff + (vsi_l_offset)(iLine*dfSrcYInc))*nLineOffset + + nXOff * nPixelOffset; + +/* -------------------------------------------------------------------- */ +/* If the data for this band is completely contiguous we don't */ +/* have to worry about pre-reading from disk. */ +/* -------------------------------------------------------------------- */ + if( nPixelOffset > nBandDataSize ) + AccessBlock( nBlockOff, nBytesToRW, pabyData ); + +/* -------------------------------------------------------------------- */ +/* Copy data from user block buffer to disk buffer and subsample, */ +/* if needed. */ +/* -------------------------------------------------------------------- */ + if ( nXSize == nBufXSize && nYSize == nBufYSize ) + { + GDALCopyWords( (GByte *)pData + (vsi_l_offset)iLine * nLineSpace, + eBufType, nPixelSpace, + pabyData, eDataType, nPixelOffset, nXSize ); + } + else + { + int iPixel; + + for ( iPixel = 0; iPixel < nBufXSize; iPixel++ ) + { + GDALCopyWords( (GByte *)pData+(vsi_l_offset)iLine*nLineSpace + + (vsi_l_offset)iPixel * nPixelSpace, + eBufType, nPixelSpace, + pabyData + + (vsi_l_offset)(iPixel * dfSrcXInc) * nPixelOffset, + eDataType, nPixelOffset, 1 ); + } + } + +/* -------------------------------------------------------------------- */ +/* Byte swap the data buffer, if required. */ +/* -------------------------------------------------------------------- */ + if( !bNativeOrder && eDataType != GDT_Byte ) + { + if( GDALDataTypeIsComplex( eDataType ) ) + { + int nWordSize; + + nWordSize = GDALGetDataTypeSize(eDataType)/16; + GDALSwapWords( pabyData, nWordSize, nXSize, nPixelOffset ); + GDALSwapWords( ((GByte *) pabyData) + nWordSize, + nWordSize, nXSize, nPixelOffset ); + } + else + GDALSwapWords( pabyData, nBandDataSize, nXSize, + nPixelOffset ); + } + +/* -------------------------------------------------------------------- */ +/* Seek to the right line in block. */ +/* -------------------------------------------------------------------- */ + if( Seek( nBlockOff, SEEK_SET) == -1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to " CPL_FRMT_GUIB " to read.\n", + nBlockOff ); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Write the line of block. */ +/* -------------------------------------------------------------------- */ + nBytesActuallyWritten = Write( pabyData, 1, nBytesToRW ); + if( nBytesActuallyWritten < nBytesToRW ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to write %d bytes to file. %d bytes written", + nBytesToRW, nBytesActuallyWritten ); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Byte swap (if necessary) back into machine order so the */ +/* buffer is still usable for reading purposes. */ +/* -------------------------------------------------------------------- */ + if( !bNativeOrder && eDataType != GDT_Byte ) + { + if( GDALDataTypeIsComplex( eDataType ) ) + { + int nWordSize; + + nWordSize = GDALGetDataTypeSize(eDataType)/16; + GDALSwapWords( pabyData, nWordSize, nXSize, nPixelOffset ); + GDALSwapWords( ((GByte *) pabyData) + nWordSize, + nWordSize, nXSize, nPixelOffset ); + } + else + GDALSwapWords( pabyData, nBandDataSize, nXSize, + nPixelOffset ); + } + + } + + bDirty = TRUE; + CPLFree( pabyData ); + } + } + + return CE_None; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int RawRasterBand::Seek( vsi_l_offset nOffset, int nSeekMode ) + +{ + if( bIsVSIL ) + return VSIFSeekL( fpRawL, nOffset, nSeekMode ); + else + return VSIFSeek( fpRaw, (long) nOffset, nSeekMode ); +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t RawRasterBand::Read( void *pBuffer, size_t nSize, size_t nCount ) + +{ + if( bIsVSIL ) + return VSIFReadL( pBuffer, nSize, nCount, fpRawL ); + else + return VSIFRead( pBuffer, nSize, nCount, fpRaw ); +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t RawRasterBand::Write( void *pBuffer, size_t nSize, size_t nCount ) + +{ + if( bIsVSIL ) + return VSIFWriteL( pBuffer, nSize, nCount, fpRawL ); + else + return VSIFWrite( pBuffer, nSize, nCount, fpRaw ); +} + +/************************************************************************/ +/* StoreNoDataValue() */ +/* */ +/* This is a helper function for datasets to associate a no */ +/* data value with this band, it isn't intended to be called by */ +/* applications. */ +/************************************************************************/ + +void RawRasterBand::StoreNoDataValue( double dfValue ) + +{ + SetNoDataValue( dfValue ); +} + +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +char **RawRasterBand::GetCategoryNames() + +{ + return papszCategoryNames; +} + +/************************************************************************/ +/* SetCategoryNames() */ +/************************************************************************/ + +CPLErr RawRasterBand::SetCategoryNames( char ** papszNewNames ) + +{ + CSLDestroy( papszCategoryNames ); + papszCategoryNames = CSLDuplicate( papszNewNames ); + + return CE_None; +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr RawRasterBand::SetColorTable( GDALColorTable *poNewCT ) + +{ + if( poCT ) + delete poCT; + if( poNewCT == NULL ) + poCT = NULL; + else + poCT = poNewCT->Clone(); + + return CE_None; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *RawRasterBand::GetColorTable() + +{ + return poCT; +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +CPLErr RawRasterBand::SetColorInterpretation( GDALColorInterp eNewInterp ) + +{ + eInterp = eNewInterp; + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp RawRasterBand::GetColorInterpretation() + +{ + return eInterp; +} + +/************************************************************************/ +/* GetVirtualMemAuto() */ +/************************************************************************/ + +CPLVirtualMem *RawRasterBand::GetVirtualMemAuto( GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ) +{ + CPLAssert(pnPixelSpace); + CPLAssert(pnLineSpace); + + vsi_l_offset nSize = (vsi_l_offset)(nRasterYSize - 1) * nLineOffset + + (nRasterXSize - 1) * nPixelOffset + GDALGetDataTypeSize(eDataType) / 8; + + if( !bIsVSIL || VSIFGetNativeFileDescriptorL(fpRawL) == NULL || + !CPLIsVirtualMemFileMapAvailable() || (eDataType != GDT_Byte && !bNativeOrder) || + (size_t)nSize != nSize || nPixelOffset < 0 || nLineOffset < 0 || + CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "USE_DEFAULT_IMPLEMENTATION", "NO")) ) + { + return GDALRasterBand::GetVirtualMemAuto(eRWFlag, pnPixelSpace, + pnLineSpace, papszOptions); + } + + FlushCache(); + + CPLVirtualMem* pVMem = CPLVirtualMemFileMapNew( + fpRawL, nImgOffset, nSize, + (eRWFlag == GF_Write) ? VIRTUALMEM_READWRITE : VIRTUALMEM_READONLY, + NULL, NULL); + if( pVMem == NULL ) + { + return GDALRasterBand::GetVirtualMemAuto(eRWFlag, pnPixelSpace, + pnLineSpace, papszOptions); + } + else + { + *pnPixelSpace = nPixelOffset; + *pnLineSpace = nLineOffset; + return pVMem; + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* RawDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* RawDataset() */ +/************************************************************************/ + +RawDataset::RawDataset() + +{ +} + +/************************************************************************/ +/* ~RawDataset() */ +/************************************************************************/ + +RawDataset::~RawDataset() + +{ + /* It's pure virtual function but must be defined, even if empty. */ +} + +/************************************************************************/ +/* IRasterIO() */ +/* */ +/* Multi-band raster io handler. */ +/************************************************************************/ + +CPLErr RawDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + const char* pszInterleave; + + /* The default GDALDataset::IRasterIO() implementation would go to */ + /* BlockBasedRasterIO if the dataset is interleaved. However if the */ + /* access pattern is compatible with DirectIO() we don't want to go */ + /* BlockBasedRasterIO, but rather used our optimized path in RawRasterBand::IRasterIO() */ + if (nXSize == nBufXSize && nYSize == nBufYSize && nBandCount > 1 && + (pszInterleave = GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE")) != NULL && + EQUAL(pszInterleave, "PIXEL")) + { + int iBandIndex; + for(iBandIndex = 0; iBandIndex < nBandCount; iBandIndex ++ ) + { + RawRasterBand* poBand = (RawRasterBand*) GetRasterBand(panBandMap[iBandIndex]); + if( !poBand->CanUseDirectIO(nXOff, nYOff, nXSize, nYSize, eBufType ) ) + { + break; + } + } + if( iBandIndex == nBandCount ) + { + + GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress; + void *pProgressDataGlobal = psExtraArg->pProgressData; + + CPLErr eErr = CE_None; + for( iBandIndex = 0; + iBandIndex < nBandCount && eErr == CE_None; + iBandIndex++ ) + { + GDALRasterBand *poBand = GetRasterBand(panBandMap[iBandIndex]); + GByte *pabyBandData; + + if (poBand == NULL) + { + eErr = CE_Failure; + break; + } + + pabyBandData = ((GByte *) pData) + iBandIndex * nBandSpace; + + psExtraArg->pfnProgress = GDALScaledProgress; + psExtraArg->pProgressData = + GDALCreateScaledProgress( 1.0 * iBandIndex / nBandCount, + 1.0 * (iBandIndex + 1) / nBandCount, + pfnProgressGlobal, + pProgressDataGlobal ); + + eErr = poBand->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + (void *) pabyBandData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, psExtraArg ); + + GDALDestroyScaledProgress( psExtraArg->pProgressData ); + } + + psExtraArg->pfnProgress = pfnProgressGlobal; + psExtraArg->pProgressData = pProgressDataGlobal; + + return eErr; + } + } + + return GDALDataset::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + psExtraArg); +} diff --git a/bazaar/plugin/gdal/frmts/raw/rawdataset.h b/bazaar/plugin/gdal/frmts/raw/rawdataset.h new file mode 100644 index 000000000..bdd00b880 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/rawdataset.h @@ -0,0 +1,174 @@ +/****************************************************************************** + * $Id: rawdataset.h 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: Raw Translator + * Purpose: Implementation of RawDataset class. Intented to be subclassed + * by other raw formats. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_FRMTS_RAW_RAWDATASET_H_INCLUDED +#define GDAL_FRMTS_RAW_RAWDATASET_H_INCLUDED + +#include "gdal_pam.h" + +/************************************************************************/ +/* ==================================================================== */ +/* RawDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class RawRasterBand; + +/** + * \brief Abstract Base Class dedicated to define new raw dataset types. + */ +class CPL_DLL RawDataset : public GDALPamDataset +{ + friend class RawRasterBand; + + protected: + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ); + public: + RawDataset(); + ~RawDataset() = 0; + +}; + +/************************************************************************/ +/* ==================================================================== */ +/* RawRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/** + * \brief Abstract Base Class dedicated to define raw datasets. + * \note It is not defined an Abstract Base Class, but it's advised to + * consider it as such and not use it directly in client's code. + */ +class CPL_DLL RawRasterBand : public GDALPamRasterBand +{ +protected: + friend class RawDataset; + + FILE *fpRaw; + VSILFILE *fpRawL; + int bIsVSIL; + + vsi_l_offset nImgOffset; + int nPixelOffset; + int nLineOffset; + int nLineSize; + int bNativeOrder; + + int nLoadedScanline; + void *pLineBuffer; + void *pLineStart; + int bDirty; + + GDALColorTable *poCT; + GDALColorInterp eInterp; + + char **papszCategoryNames; + + int bOwnsFP; + + int Seek( vsi_l_offset, int ); + size_t Read( void *, size_t, size_t ); + size_t Write( void *, size_t, size_t ); + + CPLErr AccessBlock( vsi_l_offset nBlockOff, int nBlockSize, + void * pData ); + int IsSignificantNumberOfLinesLoaded( int nLineOff, int nLines ); + void Initialize(); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + int CanUseDirectIO(int nXOff, int nYOff, int nXSize, int nYSize, + GDALDataType eBufType); + +public: + + RawRasterBand( GDALDataset *poDS, int nBand, void * fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder, + int bIsVSIL = FALSE, int bOwnsFP = FALSE ); + + RawRasterBand( void * fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder, + int nXSize, int nYSize, int bIsVSIL = FALSE, int bOwnsFP = FALSE ); + + ~RawRasterBand() /* = 0 */ ; + + // should override RasterIO eventually. + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + + virtual GDALColorTable *GetColorTable(); + virtual GDALColorInterp GetColorInterpretation(); + virtual CPLErr SetColorTable( GDALColorTable * ); + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + + virtual char **GetCategoryNames(); + virtual CPLErr SetCategoryNames( char ** ); + + virtual CPLErr FlushCache(); + + virtual CPLVirtualMem *GetVirtualMemAuto( GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ); + + CPLErr AccessLine( int iLine ); + + void SetAccess( GDALAccess eAccess ); + + // this is deprecated. + void StoreNoDataValue( double ); + + // Query methods for internal data. + vsi_l_offset GetImgOffset() { return nImgOffset; } + int GetPixelOffset() { return nPixelOffset; } + int GetLineOffset() { return nLineOffset; } + int GetNativeOrder() { return bNativeOrder; } + int GetIsVSIL() { return bIsVSIL; } + FILE *GetFP() { return (bIsVSIL) ? (FILE*)fpRawL : fpRaw; } + VSILFILE *GetFPL() { CPLAssert(bIsVSIL); return fpRawL; } + int GetOwnsFP() { return bOwnsFP; } +}; + +#endif // GDAL_FRMTS_RAW_RAWDATASET_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/raw/roipacdataset.cpp b/bazaar/plugin/gdal/frmts/raw/roipacdataset.cpp new file mode 100644 index 000000000..c63d69e93 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/roipacdataset.cpp @@ -0,0 +1,868 @@ +/****************************************************************************** + * $Id: roipacdataset.cpp 28974 2015-04-22 17:57:26Z rouault $ + * + * Project: ROI_PAC Raster Reader + * Purpose: Implementation of the ROI_PAC raster reader + * Author: Matthieu Volat (ISTerre), matthieu.volat@ujf-grenoble.fr + * + ****************************************************************************** + * Copyright (c) 2014, Matthieu Volat + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: roipacdataset.cpp 28974 2015-04-22 17:57:26Z rouault $"); + +CPL_C_START +void GDALRegister_ROIPAC(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* ROIPACDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class ROIPACRasterBand; + +class ROIPACDataset : public RawDataset +{ + friend class ROIPACRasterBand; + + VSILFILE *fpImage; + VSILFILE *fpRsc; + + char *pszRscFilename; + + double adfGeoTransform[6]; + bool bValidGeoTransform; + char *pszProjection; + + public: + ROIPACDataset( void ); + ~ROIPACDataset( void ); + + static GDALDataset *Open( GDALOpenInfo *poOpenInfo ); + static int Identify( GDALOpenInfo *poOpenInfo ); + static GDALDataset *Create( const char *pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char **papszOptions ); + + virtual void FlushCache( void ); + CPLErr GetGeoTransform( double *padfTransform ); + virtual CPLErr SetGeoTransform( double *padfTransform ); + const char *GetProjectionRef( void ); + virtual CPLErr SetProjection( const char *pszNewProjection ); + virtual char **GetFileList( void ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* ROIPACRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class ROIPACRasterBand : public RawRasterBand +{ + public: + ROIPACRasterBand( GDALDataset *poDS, int nBand, void *fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder, + int bIsVSIL = FALSE, int bOwnsFP = FALSE ); +}; + +/************************************************************************/ +/* getRscFilename() */ +/************************************************************************/ + +static CPLString getRscFilename( GDALOpenInfo *poOpenInfo ) +{ + CPLString osRscFilename; + + char **papszSiblingFiles = poOpenInfo->GetSiblingFiles(); + if ( papszSiblingFiles == NULL ) + { + osRscFilename = CPLFormFilename( NULL, poOpenInfo->pszFilename, + "rsc" ); + VSIStatBufL psRscStatBuf; + if ( VSIStatL( osRscFilename, &psRscStatBuf ) != 0 ) + { + osRscFilename = ""; + } + } + else + { + /* ------------------------------------------------------------ */ + /* We need to tear apart the filename to form a .rsc */ + /* filename. */ + /* ------------------------------------------------------------ */ + CPLString osPath = CPLGetPath( poOpenInfo->pszFilename ); + CPLString osName = CPLGetFilename( poOpenInfo->pszFilename ); + + int iFile = CSLFindString( papszSiblingFiles, + CPLFormFilename( NULL, osName, "rsc" ) ); + if( iFile >= 0 ) + { + osRscFilename = CPLFormFilename( osPath, + papszSiblingFiles[iFile], + NULL ); + } + } + + return osRscFilename; +} + +/************************************************************************/ +/* ROIPACDataset() */ +/************************************************************************/ + +ROIPACDataset::ROIPACDataset( void ) +{ + fpImage = NULL; + fpRsc = NULL; + pszRscFilename = NULL; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + pszProjection = NULL; + bValidGeoTransform = false; +} + +/************************************************************************/ +/* ~ROIPACDataset() */ +/************************************************************************/ + +ROIPACDataset::~ROIPACDataset( void ) +{ + FlushCache(); + if ( fpRsc != NULL ) + { + VSIFCloseL( fpRsc ); + } + if ( fpImage != NULL ) + { + VSIFCloseL( fpImage ); + } + CPLFree( pszRscFilename ); + CPLFree( pszProjection ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *ROIPACDataset::Open( GDALOpenInfo *poOpenInfo ) +{ +/* -------------------------------------------------------------------- */ +/* Confirm that the header is compatible with a ROIPAC dataset. */ +/* -------------------------------------------------------------------- */ + if ( !Identify(poOpenInfo) ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open the .rsc file */ +/* -------------------------------------------------------------------- */ + CPLString osRscFilename = getRscFilename( poOpenInfo ); + if ( osRscFilename.empty() ) + { + return NULL; + } + VSILFILE *fpRsc; + if ( poOpenInfo->eAccess == GA_Update ) + { + fpRsc = VSIFOpenL( osRscFilename, "r+" ); + } + else + { + fpRsc = VSIFOpenL( osRscFilename, "r" ); + } + if ( fpRsc == NULL ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Load the .rsc information. */ +/* -------------------------------------------------------------------- */ + char **papszRsc = NULL; + while ( true ) + { + const char *pszLine; + char **papszTokens; + + pszLine = CPLReadLineL( fpRsc ); + if (pszLine == NULL) + { + break; + } + + papszTokens = CSLTokenizeString2( pszLine, " \t", + CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES + | CSLT_PRESERVEQUOTES + | CSLT_PRESERVEESCAPES ); + if ( papszTokens == NULL + || papszTokens[0] == NULL || papszTokens[1] == NULL ) + { + CSLDestroy ( papszTokens ); + break; + } + papszRsc = CSLSetNameValue( papszRsc, + papszTokens[0], papszTokens[1] ); + + CSLDestroy ( papszTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Fetch required fields. */ +/* -------------------------------------------------------------------- */ + int nWidth = 0, nFileLength = 0; + if ( CSLFetchNameValue( papszRsc, "WIDTH" ) == NULL + || CSLFetchNameValue( papszRsc, "FILE_LENGTH" ) == NULL ) + { + CSLDestroy( papszRsc ); + VSIFCloseL( fpRsc ); + return NULL; + } + nWidth = atoi( CSLFetchNameValue( papszRsc, "WIDTH" ) ); + nFileLength = atoi( CSLFetchNameValue( papszRsc, "FILE_LENGTH" ) ); + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + ROIPACDataset *poDS; + poDS = new ROIPACDataset(); + poDS->nRasterXSize = nWidth; + poDS->nRasterYSize = nFileLength; + poDS->eAccess = poOpenInfo->eAccess; + poDS->fpRsc = fpRsc; + poDS->pszRscFilename = CPLStrdup( osRscFilename.c_str() ); + +/* -------------------------------------------------------------------- */ +/* Reopen file in update mode if necessary. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb+" ); + } + else + { + poDS->fpImage = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + } + if( poDS->fpImage == NULL ) + { + delete poDS; + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to re-open %s within ROI_PAC driver.\n", + poOpenInfo->pszFilename ); + CSLDestroy( papszRsc ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + GDALDataType eDataType; + int nBands; + enum Interleave { LINE, PIXEL } eInterleave; + const char *pszExtension = CPLGetExtension(poOpenInfo->pszFilename); + if ( strcmp( pszExtension, "raw" ) == 0 ) + { + /* ------------------------------------------------------------ */ + /* TODO: ROI_PAC raw images are what would be GDT_CInt8 typed, */ + /* but since that type do not exist, we will have to implement */ + /* a specific case in the RasterBand to convert it to */ + /* GDT_CInt16 for example */ + /* ------------------------------------------------------------ */ +#if 0 + eDataType = GDT_CInt8; + nBands = 1; + eInterleave = PIXEL; +#else + CPLError( CE_Failure, CPLE_NotSupported, + "Reading ROI_PAC raw files is not supported yet." ); + delete poDS; + CSLDestroy( papszRsc ); + return NULL; +#endif + } + else if ( strcmp( pszExtension, "int" ) == 0 + || strcmp( pszExtension, "slc" ) == 0 ) + { + eDataType = GDT_CFloat32; + nBands = 1; + eInterleave = PIXEL; + } + else if ( strcmp( pszExtension, "amp" ) == 0 ) + { + eDataType = GDT_Float32; + nBands = 2; + eInterleave = PIXEL; + } + else if ( strcmp( pszExtension, "cor" ) == 0 + || strcmp( pszExtension, "hgt" ) == 0 + || strcmp( pszExtension, "unw" ) == 0 + || strcmp( pszExtension, "msk" ) == 0 + || strcmp( pszExtension, "trans" ) == 0 ) + { + eDataType = GDT_Float32; + nBands = 2; + eInterleave = LINE; + } + else if ( strcmp( pszExtension, "dem" ) == 0 ) + { + eDataType = GDT_Int16; + nBands = 1; + eInterleave = PIXEL; + } + else { /* Eeek */ + delete poDS; + CSLDestroy( papszRsc ); + return NULL; + } + int nPixelOffset; + int nLineOffset; + int nBandOffset; + if (eInterleave == LINE) + { + nPixelOffset = GDALGetDataTypeSize(eDataType)/8; + nLineOffset = nPixelOffset * nWidth * nBands; + nBandOffset = GDALGetDataTypeSize(eDataType)/8 * nWidth; + } + else { /* PIXEL */ + nPixelOffset = GDALGetDataTypeSize(eDataType)/8 * nBands; + nLineOffset = nPixelOffset * nWidth * nBands; + nBandOffset = GDALGetDataTypeSize(eDataType)/8; + } + poDS->nBands = nBands; + for (int b = 0; b < nBands; b++) + { + poDS->SetBand( b + 1, + new ROIPACRasterBand( poDS, b + 1, poDS->fpImage, + nBandOffset * b, + nPixelOffset, nLineOffset, + eDataType, TRUE, + TRUE, FALSE ) ); + } + +/* -------------------------------------------------------------------- */ +/* Interpret georeferencing, if present. */ +/* -------------------------------------------------------------------- */ + if ( CSLFetchNameValue( papszRsc, "X_FIRST" ) != NULL + && CSLFetchNameValue( papszRsc, "X_STEP" ) != NULL + && CSLFetchNameValue( papszRsc, "Y_FIRST" ) != NULL + && CSLFetchNameValue( papszRsc, "Y_STEP" ) != NULL ) + { + poDS->adfGeoTransform[0] = CPLAtof( CSLFetchNameValue( papszRsc, + "X_FIRST" ) ); + poDS->adfGeoTransform[1] = CPLAtof( CSLFetchNameValue( papszRsc, + "X_STEP" ) ); + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = CPLAtof( CSLFetchNameValue( papszRsc, + "Y_FIRST" ) ); + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = CPLAtof( CSLFetchNameValue( papszRsc, + "Y_STEP" ) ); + poDS->bValidGeoTransform = true; + } + if ( CSLFetchNameValue( papszRsc, "PROJECTION" ) != NULL ) + { + /* ------------------------------------------------------------ */ + /* In ROI_PAC, images are georeferenced either with lat/long or */ + /* UTM projection. However, using UTM projection is dangerous */ + /* because there is no North/South field, or use of latitude */ + /* bands! */ + /* ------------------------------------------------------------ */ + OGRSpatialReference oSRS; + if ( strcmp( CSLFetchNameValue( papszRsc, "PROJECTION" ), + "LL" ) == 0 ) + { + if ( CSLFetchNameValue( papszRsc, "DATUM" ) != NULL ) + { + oSRS.SetWellKnownGeogCS( CSLFetchNameValue( papszRsc, + "DATUM" ) ); + } + else { + oSRS.SetWellKnownGeogCS( "WGS84" ); + } + } + else if( strncmp( CSLFetchNameValue( papszRsc, "PROJECTION" ), + "UTM", 3 ) == 0 ) + { + const char *pszZone = CSLFetchNameValue( papszRsc, + "PROJECTION" ) + 3; + oSRS.SetUTM( atoi( pszZone ), TRUE ); /* FIXME: north/south? */ + if ( CSLFetchNameValue( papszRsc, "DATUM" ) != NULL ) + { + oSRS.SetWellKnownGeogCS( CSLFetchNameValue( papszRsc, + "DATUM" ) ); + } + else { + oSRS.SetWellKnownGeogCS( "NAD27" ); + } + } + oSRS.exportToWkt( &poDS->pszProjection ); + } + + +/* -------------------------------------------------------------------- */ +/* Set all the other header metadata into the ROI_PAC domain */ +/* -------------------------------------------------------------------- */ + for (int i = 0; i < CSLCount( papszRsc ); i++) + { + char **papszTokens; + papszTokens = CSLTokenizeString2( papszRsc[i], + "=", + CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES); + if ( strcmp( papszTokens[0], "WIDTH" ) == 0 + || strcmp( papszTokens[0], "FILE_LENGTH" ) == 0 + || strcmp( papszTokens[0], "X_FIRST" ) == 0 + || strcmp( papszTokens[0], "X_STEP" ) == 0 + || strcmp( papszTokens[0], "Y_FIRST" ) == 0 + || strcmp( papszTokens[0], "Y_STEP" ) == 0 + || strcmp( papszTokens[0], "PROJECTION" ) == 0 + || strcmp( papszTokens[0], "DATUM" ) == 0 ) + { + CSLDestroy( papszTokens ); + continue; + } + poDS->SetMetadataItem(papszTokens[0], papszTokens[1], "ROI_PAC"); + CSLDestroy( papszTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Free papszRsc */ +/* -------------------------------------------------------------------- */ + CSLDestroy( papszRsc ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int ROIPACDataset::Identify( GDALOpenInfo *poOpenInfo ) +{ +/* -------------------------------------------------------------------- */ +/* Check if: */ +/* * 1. The data file extension is known */ +/* -------------------------------------------------------------------- */ + const char *pszExtension = CPLGetExtension(poOpenInfo->pszFilename); + if ( strcmp( pszExtension, "raw" ) == 0 ) + { + /* Since gdal do not read natively CInt8, more work is needed + * to read raw files */ + return false; + } + bool bExtensionIsValid = strcmp( pszExtension, "int" ) == 0 + || strcmp( pszExtension, "slc" ) == 0 + || strcmp( pszExtension, "amp" ) == 0 + || strcmp( pszExtension, "cor" ) == 0 + || strcmp( pszExtension, "hgt" ) == 0 + || strcmp( pszExtension, "unw" ) == 0 + || strcmp( pszExtension, "msk" ) == 0 + || strcmp( pszExtension, "trans" ) == 0 + || strcmp( pszExtension, "dem" ) == 0; + if ( !bExtensionIsValid ) + { + return false; + } + +/* -------------------------------------------------------------------- */ +/* * 2. there is a .rsc file */ +/* -------------------------------------------------------------------- */ + CPLString osRscFilename = getRscFilename( poOpenInfo ); + if ( osRscFilename.empty() ) + { + return false; + } + + return true; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *ROIPACDataset::Create( const char *pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char **papszOptions ) +{ +/* -------------------------------------------------------------------- */ +/* Verify input options. */ +/* -------------------------------------------------------------------- */ + const char *pszExtension = CPLGetExtension(pszFilename); + if ( strcmp( pszExtension, "int" ) == 0 + || strcmp( pszExtension, "slc" ) == 0 ) + { + if ( nBands != 1 || eType != GDT_CFloat32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create ROI_PAC %s dataset with an illegal\n" + "number of bands (%d) and/or data type (%s).\n", + pszExtension, nBands, GDALGetDataTypeName(eType) ); + return NULL; + } + } + else if ( strcmp( pszExtension, "amp" ) == 0 ) + { + if ( nBands != 2 || eType != GDT_Float32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create ROI_PAC %s dataset with an illegal\n" + "number of bands (%d) and/or data type (%s).\n", + pszExtension, nBands, GDALGetDataTypeName(eType) ); + return NULL; + } + } + else if ( strcmp( pszExtension, "cor" ) == 0 + || strcmp( pszExtension, "hgt" ) == 0 + || strcmp( pszExtension, "unw" ) == 0 + || strcmp( pszExtension, "msk" ) == 0 + || strcmp( pszExtension, "trans" ) == 0 ) + { + if ( nBands != 2 || eType != GDT_Float32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create ROI_PAC %s dataset with an illegal\n" + "number of bands (%d) and/or data type (%s).\n", + pszExtension, nBands, GDALGetDataTypeName(eType) ); + return NULL; + } + } + else if ( strcmp( pszExtension, "dem" ) == 0 ) + { + if ( nBands != 1 || eType != GDT_Int16 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create ROI_PAC %s dataset with an illegal\n" + "number of bands (%d) and/or data type (%s).\n", + pszExtension, nBands, GDALGetDataTypeName(eType) ); + return NULL; + } + } + else { /* Eeek */ + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create ROI_PAC dataset with an unknown type (%s)\n", + pszExtension ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to create the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + fp = VSIFOpenL( pszFilename, "wb" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Just write out a couple of bytes to establish the binary */ +/* file, and then close it. */ +/* -------------------------------------------------------------------- */ + VSIFWriteL( (void *) "\0\0", 2, 1, fp ); + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Open the RSC file. */ +/* -------------------------------------------------------------------- */ + const char *pszRSCFilename; + pszRSCFilename = CPLFormFilename( NULL, pszFilename, "rsc" ); + fp = VSIFOpenL( pszRSCFilename, "wt" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszRSCFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write out the header. */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, "%-40s %d\n", "WIDTH", nXSize ); + VSIFPrintfL( fp, "%-40s %d\n", "FILE_LENGTH", nYSize ); + VSIFCloseL( fp ); + + return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void ROIPACDataset::FlushCache( void ) +{ + RawDataset::FlushCache(); + + GDALRasterBand *band = (GetRasterCount() > 0) ? GetRasterBand(1) : NULL; + + if ( eAccess == GA_ReadOnly || band == NULL ) + return; + + // If opening an existing file in Update mode (i.e. "r+") we need to make + // sure any existing content is cleared, otherwise the file may contain + // trailing content from the previous write. + VSIFTruncateL( fpRsc, 0 ); + + VSIFSeekL( fpRsc, 0, SEEK_SET ); +/* -------------------------------------------------------------------- */ +/* Rewrite out the header. */ +/* -------------------------------------------------------------------- */ +/* -------------------------------------------------------------------- */ +/* Raster dimensions. */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL( fpRsc, "%-40s %d\n", "WIDTH", nRasterXSize ); + VSIFPrintfL( fpRsc, "%-40s %d\n", "FILE_LENGTH", nRasterYSize ); + +/* -------------------------------------------------------------------- */ +/* Georeferencing. */ +/* -------------------------------------------------------------------- */ + if ( pszProjection != NULL ) + { + char *pszProjectionTmp = pszProjection; + OGRSpatialReference oSRS; + if( oSRS.importFromWkt( &pszProjectionTmp ) == OGRERR_NONE ) + { + int bNorth; + int iUTMZone; + + iUTMZone = oSRS.GetUTMZone( &bNorth ); + if ( iUTMZone != 0 ) + { + VSIFPrintfL( fpRsc, "%-40s %s%d\n", "PROJECTION", "UTM", iUTMZone ); + } + else if ( oSRS.IsGeographic() ) + { + VSIFPrintfL( fpRsc, "%-40s %s\n", "PROJECTION", "LL" ); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "ROI_PAC format only support Latitude/Longitude and " + "UTM projections, discarding projection."); + } + + if ( oSRS.GetAttrValue( "DATUM" ) != NULL ) + { + if ( strcmp( oSRS.GetAttrValue( "DATUM" ), "WGS_1984" ) == 0 ) + { + VSIFPrintfL( fpRsc, "%-40s %s\n", "DATUM", "WGS84" ); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Datum \"%s\" probably not supported in the " + "ROI_PAC format, saving it anyway", + oSRS.GetAttrValue( "DATUM" ) ); + VSIFPrintfL( fpRsc, "%-40s %s\n", "DATUM", oSRS.GetAttrValue( "DATUM" ) ); + } + } + if ( oSRS.GetAttrValue( "UNIT" ) != NULL ) + { + VSIFPrintfL( fpRsc, "%-40s %s\n", "X_UNIT", oSRS.GetAttrValue( "UNIT" ) ); + VSIFPrintfL( fpRsc, "%-40s %s\n", "Y_UNIT", oSRS.GetAttrValue( "UNIT" ) ); + } + } + } + if( bValidGeoTransform ) + { + if ( adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "ROI_PAC format do not support geotransform with " + "rotation, discarding info."); + } + else + { + VSIFPrintfL( fpRsc, "%-40s %.16g\n", "X_FIRST", adfGeoTransform[0] ); + VSIFPrintfL( fpRsc, "%-40s %.16g\n", "X_STEP", adfGeoTransform[1] ); + VSIFPrintfL( fpRsc, "%-40s %.16g\n", "Y_FIRST", adfGeoTransform[3] ); + VSIFPrintfL( fpRsc, "%-40s %.16g\n", "Y_STEP", adfGeoTransform[5] ); + } + } + +/* -------------------------------------------------------------------- */ +/* Metadata stored in the ROI_PAC domain. */ +/* -------------------------------------------------------------------- */ + char** papszROIPACMetadata = GetMetadata( "ROI_PAC" ); + for (int i = 0; i < CSLCount( papszROIPACMetadata ); i++) + { + char **papszTokens; + + /* Get the tokens from the metadata item */ + papszTokens = CSLTokenizeString2( papszROIPACMetadata[i], + "=", + CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES); + if ( CSLCount( papszTokens ) != 2 ) + { + CPLDebug("ROI_PAC", + "Line of header file could not be split at = into two elements: %s", + papszROIPACMetadata[i]); + CSLDestroy( papszTokens ); + continue; + } + + /* Don't write it out if it is one of the bits of metadata that is + * written out elsewhere in this routine */ + if ( strcmp( papszTokens[0], "WIDTH" ) == 0 + || strcmp( papszTokens[0], "FILE_LENGTH" ) == 0 ) + { + CSLDestroy( papszTokens ); + continue; + } + VSIFPrintfL( fpRsc, "%-40s %s\n", papszTokens[0], papszTokens[1] ); + CSLDestroy( papszTokens ); + } +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr ROIPACDataset::GetGeoTransform( double *padfTransform ) +{ + memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform) ); + return (bValidGeoTransform) ? CE_None : CE_Failure; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr ROIPACDataset::SetGeoTransform( double *padfTransform ) +{ + memcpy( adfGeoTransform, padfTransform, sizeof(adfGeoTransform) ); + bValidGeoTransform = true; + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *ROIPACDataset::GetProjectionRef( void ) +{ + return (pszProjection != NULL) ? pszProjection : ""; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr ROIPACDataset::SetProjection( const char *pszNewProjection ) + +{ + CPLFree( pszProjection ); + pszProjection = (pszNewProjection) ? CPLStrdup( pszNewProjection ) : NULL; + return CE_None; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **ROIPACDataset::GetFileList() +{ + char **papszFileList = NULL; + + // Main data file, etc. + papszFileList = RawDataset::GetFileList(); + + // RSC file. + papszFileList = CSLAddString( papszFileList, pszRscFilename ); + + return papszFileList; +} + +/************************************************************************/ +/* ROIPACRasterBand() */ +/************************************************************************/ + +ROIPACRasterBand::ROIPACRasterBand( GDALDataset *poDS, int nBand, void *fpRaw, + vsi_l_offset nImgOffset, int nPixelOffset, + int nLineOffset, + GDALDataType eDataType, int bNativeOrder, + int bIsVSIL, int bOwnsFP ) : + RawRasterBand(poDS, nBand, fpRaw, nImgOffset, nPixelOffset, + nLineOffset, eDataType, bNativeOrder, bIsVSIL, bOwnsFP) +{ +} + +/************************************************************************/ +/* GDALRegister_ROIPAC() */ +/************************************************************************/ + +void GDALRegister_ROIPAC( void ) +{ + GDALDriver *poDriver; + + if (!GDAL_CHECK_VERSION("ROI_PAC")) + { + return; + } + + if ( GDALGetDriverByName( "ROI_PAC" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ROI_PAC" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ROI_PAC raster" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#ROI_PAC" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = ROIPACDataset::Open; + poDriver->pfnIdentify = ROIPACDataset::Identify; + poDriver->pfnCreate = ROIPACDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/raw/snodasdataset.cpp b/bazaar/plugin/gdal/frmts/raw/snodasdataset.cpp new file mode 100644 index 000000000..194196fa5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/raw/snodasdataset.cpp @@ -0,0 +1,517 @@ +/****************************************************************************** + * $Id: snodasdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: SNODAS driver + * Purpose: Implementation of SNODASDataset + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "rawdataset.h" +#include "ogr_srs_api.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: snodasdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +// g++ -g -Wall -fPIC frmts/raw/snodasdataset.cpp -shared -o gdal_SNODAS.so -Iport -Igcore -Ifrmts/raw -Iogr -L. -lgdal + +CPL_C_START +void GDALRegister_SNODAS(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* SNODASDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class SNODASRasterBand; + +class SNODASDataset : public RawDataset +{ + CPLString osDataFilename; + int bGotTransform; + double adfGeoTransform[6]; + int bHasNoData; + double dfNoData; + int bHasMin; + double dfMin; + int bHasMax; + double dfMax; + + friend class SNODASRasterBand; + + public: + SNODASDataset(); + ~SNODASDataset(); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual const char *GetProjectionRef(void); + + virtual char **GetFileList(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); +}; + + +/************************************************************************/ +/* ==================================================================== */ +/* SNODASRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class SNODASRasterBand : public RawRasterBand +{ + public: + SNODASRasterBand(VSILFILE* fpRaw, int nXSize, int nYSize); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum(int *pbSuccess = NULL ); +}; + + +/************************************************************************/ +/* SNODASRasterBand() */ +/************************************************************************/ + +SNODASRasterBand::SNODASRasterBand(VSILFILE* fpRaw, + int nXSize, int nYSize) : + RawRasterBand( fpRaw, 0, 2, + nXSize * 2, GDT_Int16, + !CPL_IS_LSB, nXSize, nYSize, TRUE, TRUE) +{ +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double SNODASRasterBand::GetNoDataValue( int *pbSuccess ) +{ + SNODASDataset* poGDS = (SNODASDataset*) poDS; + if (pbSuccess) + *pbSuccess = poGDS->bHasNoData; + if (poGDS->bHasNoData) + return poGDS->dfNoData; + else + return RawRasterBand::GetNoDataValue(pbSuccess); +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double SNODASRasterBand::GetMinimum( int *pbSuccess ) +{ + SNODASDataset* poGDS = (SNODASDataset*) poDS; + if (pbSuccess) + *pbSuccess = poGDS->bHasMin; + if (poGDS->bHasMin) + return poGDS->dfMin; + else + return RawRasterBand::GetMinimum(pbSuccess); +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double SNODASRasterBand::GetMaximum( int *pbSuccess ) +{ + SNODASDataset* poGDS = (SNODASDataset*) poDS; + if (pbSuccess) + *pbSuccess = poGDS->bHasMax; + if (poGDS->bHasMax) + return poGDS->dfMax; + else + return RawRasterBand::GetMaximum(pbSuccess); +} + +/************************************************************************/ +/* ==================================================================== */ +/* SNODASDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* SNODASDataset() */ +/************************************************************************/ + +SNODASDataset::SNODASDataset() +{ + bGotTransform = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + bHasNoData = FALSE; + dfNoData = 0.0; + bHasMin = FALSE; + dfMin = 0.0; + bHasMax = FALSE; + dfMax = 0.0; +} + +/************************************************************************/ +/* ~SNODASDataset() */ +/************************************************************************/ + +SNODASDataset::~SNODASDataset() + +{ + FlushCache(); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *SNODASDataset::GetProjectionRef() + +{ + return SRS_WKT_WGS84; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr SNODASDataset::GetGeoTransform( double * padfTransform ) + +{ + if( bGotTransform ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; + } + else + { + return GDALPamDataset::GetGeoTransform( padfTransform ); + } +} + + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **SNODASDataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + + papszFileList = CSLAddString(papszFileList, osDataFilename); + + return papszFileList; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int SNODASDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + if (poOpenInfo->nHeaderBytes == 0) + return FALSE; + + return EQUALN((const char*)poOpenInfo->pabyHeader, + "Format version: NOHRSC GIS/RS raster file v1.1", + strlen("Format version: NOHRSC GIS/RS raster file v1.1")); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *SNODASDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify(poOpenInfo) ) + return NULL; + + VSILFILE *fp; + + fp = VSIFOpenL( poOpenInfo->pszFilename, "r" ); + + if( fp == NULL ) + { + return NULL; + } + + const char * pszLine; + int nRows = -1, nCols = -1; + CPLString osDataFilename; + int bIsInteger = FALSE, bIs2Bytes = FALSE; + double dfNoData = 0; + int bHasNoData = FALSE; + double dfMin = 0; + int bHasMin = FALSE; + double dfMax = 0; + int bHasMax = FALSE; + double dfMinX = 0.0, dfMinY = 0.0, dfMaxX = 0.0, dfMaxY = 0.0; + int bHasMinX = FALSE, bHasMinY = FALSE, bHasMaxX = FALSE, bHasMaxY = FALSE; + int bNotProjected = FALSE, bIsWGS84 = FALSE; + CPLString osDescription, osDataUnits; + int nStartYear = -1, nStartMonth = -1, nStartDay = -1, + nStartHour = -1, nStartMinute = -1, nStartSecond = -1; + int nStopYear = -1, nStopMonth = -1, nStopDay = -1, + nStopHour = -1, nStopMinute = -1, nStopSecond = -1; + + while( (pszLine = CPLReadLine2L( fp, 256, NULL )) != NULL ) + { + char** papszTokens = CSLTokenizeStringComplex( pszLine, ":", TRUE, FALSE ); + if( CSLCount( papszTokens ) != 2 ) + { + CSLDestroy( papszTokens ); + continue; + } + if( papszTokens[1][0] == ' ' ) + memmove(papszTokens[1], papszTokens[1] + 1, strlen(papszTokens[1] + 1) + 1); + + if( EQUAL(papszTokens[0],"Data file pathname") ) + { + osDataFilename = papszTokens[1]; + } + else if( EQUAL(papszTokens[0],"Description") ) + { + osDescription = papszTokens[1]; + } + else if( EQUAL(papszTokens[0],"Data units") ) + { + osDataUnits= papszTokens[1]; + } + + else if( EQUAL(papszTokens[0],"Start year") ) + nStartYear = atoi(papszTokens[1]); + else if( EQUAL(papszTokens[0],"Start month") ) + nStartMonth = atoi(papszTokens[1]); + else if( EQUAL(papszTokens[0],"Start day") ) + nStartDay = atoi(papszTokens[1]); + else if( EQUAL(papszTokens[0],"Start hour") ) + nStartHour = atoi(papszTokens[1]); + else if( EQUAL(papszTokens[0],"Start minute") ) + nStartMinute = atoi(papszTokens[1]); + else if( EQUAL(papszTokens[0],"Start second") ) + nStartSecond = atoi(papszTokens[1]); + + else if( EQUAL(papszTokens[0],"Stop year") ) + nStopYear = atoi(papszTokens[1]); + else if( EQUAL(papszTokens[0],"Stop month") ) + nStopMonth = atoi(papszTokens[1]); + else if( EQUAL(papszTokens[0],"Stop day") ) + nStopDay = atoi(papszTokens[1]); + else if( EQUAL(papszTokens[0],"Stop hour") ) + nStopHour = atoi(papszTokens[1]); + else if( EQUAL(papszTokens[0],"Stop minute") ) + nStopMinute = atoi(papszTokens[1]); + else if( EQUAL(papszTokens[0],"Stop second") ) + nStopSecond = atoi(papszTokens[1]); + + else if( EQUAL(papszTokens[0],"Number of columns") ) + { + nCols = atoi(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"Number of rows") ) + { + nRows = atoi(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"Data type")) + { + bIsInteger = EQUAL(papszTokens[1],"integer"); + } + else if( EQUAL(papszTokens[0],"Data bytes per pixel")) + { + bIs2Bytes = EQUAL(papszTokens[1],"2"); + } + else if( EQUAL(papszTokens[0],"Projected")) + { + bNotProjected = EQUAL(papszTokens[1],"no"); + } + else if( EQUAL(papszTokens[0],"Horizontal datum")) + { + bIsWGS84 = EQUAL(papszTokens[1],"WGS84"); + } + else if( EQUAL(papszTokens[0],"No data value")) + { + bHasNoData = TRUE; + dfNoData = CPLAtofM(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"Minimum data value")) + { + bHasMin = TRUE; + dfMin = CPLAtofM(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"Maximum data value")) + { + bHasMax = TRUE; + dfMax = CPLAtofM(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"Minimum x-axis coordinate") ) + { + bHasMinX = TRUE; + dfMinX = CPLAtofM(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"Minimum y-axis coordinate") ) + { + bHasMinY = TRUE; + dfMinY = CPLAtofM(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"Maximum x-axis coordinate") ) + { + bHasMaxX = TRUE; + dfMaxX = CPLAtofM(papszTokens[1]); + } + else if( EQUAL(papszTokens[0],"Maximum y-axis coordinate") ) + { + bHasMaxY = TRUE; + dfMaxY = CPLAtofM(papszTokens[1]); + } + + CSLDestroy( papszTokens ); + } + + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Did we get the required keywords? If not we return with */ +/* this never having been considered to be a match. This isn't */ +/* an error! */ +/* -------------------------------------------------------------------- */ + if( nRows == -1 || nCols == -1 || !bIsInteger || !bIs2Bytes ) + return NULL; + + if( !bNotProjected || !bIsWGS84 ) + return NULL; + + if( osDataFilename.size() == 0 ) + return NULL; + + if (!GDALCheckDatasetDimensions(nCols, nRows)) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Open target binary file. */ +/* -------------------------------------------------------------------- */ + const char* pszPath = CPLGetPath(poOpenInfo->pszFilename); + osDataFilename = CPLFormFilename(pszPath, osDataFilename, NULL); + + VSILFILE* fpRaw = VSIFOpenL( osDataFilename, "rb" ); + + if( fpRaw == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + SNODASDataset *poDS; + + poDS = new SNODASDataset(); + + poDS->nRasterXSize = nCols; + poDS->nRasterYSize = nRows; + poDS->osDataFilename = osDataFilename; + poDS->bHasNoData = bHasNoData; + poDS->dfNoData = dfNoData; + poDS->bHasMin = bHasMin; + poDS->dfMin = dfMin; + poDS->bHasMax = bHasMax; + poDS->dfMax = dfMax; + if (bHasMinX && bHasMinY && bHasMaxX && bHasMaxY) + { + poDS->bGotTransform = TRUE; + poDS->adfGeoTransform[0] = dfMinX; + poDS->adfGeoTransform[1] = (dfMaxX - dfMinX) / nCols; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = dfMaxY; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = - (dfMaxY - dfMinY) / nRows; + } + + if (osDescription.size()) + poDS->SetMetadataItem("Description", osDescription); + if (osDataUnits.size()) + poDS->SetMetadataItem("Data_Units", osDataUnits); + if (nStartYear != -1 && nStartMonth != -1 && nStartDay != -1 && + nStartHour != -1 && nStartMinute != -1 && nStartSecond != -1) + poDS->SetMetadataItem("Start_Date", + CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02d", + nStartYear, nStartMonth, nStartDay, + nStartHour, nStartMinute, nStartSecond)); + if (nStopYear != -1 && nStopMonth != -1 && nStopDay != -1 && + nStopHour != -1 && nStopMinute != -1 && nStopSecond != -1) + poDS->SetMetadataItem("Stop_Date", + CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02d", + nStopYear, nStopMonth, nStopDay, + nStopHour, nStopMinute, nStopSecond)); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->SetBand( 1, new SNODASRasterBand( fpRaw, nCols, nRows) ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_SNODAS() */ +/************************************************************************/ + +void GDALRegister_SNODAS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "SNODAS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SNODAS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Snow Data Assimilation System" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#SNODAS" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "hdr" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->pfnOpen = SNODASDataset::Open; + poDriver->pfnIdentify = SNODASDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/rik/GNUmakefile b/bazaar/plugin/gdal/frmts/rik/GNUmakefile new file mode 100644 index 000000000..b7de07ff4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rik/GNUmakefile @@ -0,0 +1,19 @@ + +include ../../GDALmake.opt + +ifeq ($(LIBZ_SETTING),internal) +XTRA_OPT = -I../zlib +else +XTRA_OPT = +endif + +OBJ = rikdataset.o + +CPPFLAGS := $(XTRA_OPT) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/rik/frmt_rik.html b/bazaar/plugin/gdal/frmts/rik/frmt_rik.html new file mode 100644 index 000000000..ce9172a1e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rik/frmt_rik.html @@ -0,0 +1,20 @@ + + +RIK -- Swedish Grid Maps + + + + +

    RIK -- Swedish Grid Maps

    + +Supported by GDAL for read access. This format is used in maps issued by the +swedish organization Lantmäteriet. Supports versions 1, 2 and 3 of the RIK +format, but only 8 bits per pixel.

    + +This driver is based on the work done in the +TRikPanel project.

    + +NOTE: Implemented as gdal/frmts/rik/rikdataset.cpp.

    + + + diff --git a/bazaar/plugin/gdal/frmts/rik/makefile.vc b/bazaar/plugin/gdal/frmts/rik/makefile.vc new file mode 100644 index 000000000..88669a014 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rik/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = rikdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I..\zlib + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/rik/rikdataset.cpp b/bazaar/plugin/gdal/frmts/rik/rikdataset.cpp new file mode 100644 index 000000000..76580a4fb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rik/rikdataset.cpp @@ -0,0 +1,1217 @@ +/****************************************************************************** + * $Id: rikdataset.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: RIK Reader + * Purpose: All code for RIK Reader + * Author: Daniel Wallner, daniel.wallner@bredband.net + * + ****************************************************************************** + * Copyright (c) 2005, Daniel Wallner + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include +#include "gdal_pam.h" + +CPL_CVSID("$Id: rikdataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_RIK(void); +CPL_C_END + +#define RIK_HEADER_DEBUG 0 +#define RIK_CLEAR_DEBUG 0 +#define RIK_PIXEL_DEBUG 0 + +//#define RIK_SINGLE_BLOCK 0 + +#define RIK_ALLOW_BLOCK_ERRORS 1 + +// +// The RIK file format information was extracted from the trikpanel project: +// http://sourceforge.net/projects/trikpanel/ +// +// A RIK file consists of the following elements: +// +// +--------------------+ +// | Magic "RIK3" | (Only in RIK version 3) +// +--------------------+ +// | Map name | (The first two bytes is the string length) +// +--------------------+ +// | Header | (Three different formats exists) +// +--------------------+ +// | Color palette | +// +--------------------+ +// | Block offset array | (Only in compressed formats) +// +--------------------+ +// | Image blocks | +// +--------------------+ +// +// All numbers are stored in little endian. +// +// There are four different image block formats: +// +// 1. Uncompressed image block +// +// A stream of palette indexes. +// +// 2. RLE image block +// +// The RLE image block is a stream of byte pairs: +// | Run length - 1 (byte) | Pixel value (byte) | Run length - 1 ... +// +// 3. LZW image block +// +// The LZW image block uses the same LZW encoding as a GIF file +// except that there is no EOF code and maximum code length is 13 bits. +// These blocks are upside down compared to GDAL. +// +// 4. ZLIB image block +// +// These blocks are upside down compared to GDAL. +// + +typedef struct +{ + GUInt16 iUnknown; + double fSouth; // Map bounds + double fWest; + double fNorth; + double fEast; + GUInt32 iScale; // Source map scale + float iMPPNum; // Meters per pixel numerator + GUInt32 iMPPDen; // Meters per pixel denominator + // Only used if fSouth < 4000000 + GUInt32 iBlockWidth; + GUInt32 iBlockHeight; + GUInt32 iHorBlocks; // Number of horizontal blocks + GUInt32 iVertBlocks; // Number of vertical blocks + // Only used if fSouth >= 4000000 + GByte iBitsPerPixel; + GByte iOptions; +} RIKHeader; + +/************************************************************************/ +/* ==================================================================== */ +/* RIKDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class RIKRasterBand; + +class RIKDataset : public GDALPamDataset +{ + friend class RIKRasterBand; + + VSILFILE *fp; + + double fTransform[6]; + + GUInt32 nBlockXSize; + GUInt32 nBlockYSize; + GUInt32 nHorBlocks; + GUInt32 nVertBlocks; + GUInt32 nFileSize; + GUInt32 *pOffsets; + GByte options; + + GDALColorTable *poColorTable; + + public: + ~RIKDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* RIKRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class RIKRasterBand : public GDALPamRasterBand +{ + friend class RIKDataset; + + public: + + RIKRasterBand( RIKDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); +}; + +/************************************************************************/ +/* RIKRasterBand() */ +/************************************************************************/ + +RIKRasterBand::RIKRasterBand( RIKDataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Byte; + + nBlockXSize = poDS->nBlockXSize; + nBlockYSize = poDS->nBlockYSize; +} + +/************************************************************************/ +/* GetNextLZWCode() */ +/************************************************************************/ + +static int GetNextLZWCode( int codeBits, + GByte *blockData, + GUInt32 &filePos, + GUInt32 &fileAlign, + int &bitsTaken ) + +{ + if( filePos == fileAlign ) + { + fileAlign += codeBits; + } + + const int BitMask[] = { + 0x0000, 0x0001, 0x0003, 0x0007, + 0x000f, 0x001f, 0x003f, 0x007f }; + + int ret = 0; + int bitsLeftToGo = codeBits; + + while( bitsLeftToGo > 0 ) + { + int tmp; + + tmp = blockData[filePos]; + tmp = tmp >> bitsTaken; + + if( bitsLeftToGo < 8 ) + tmp &= BitMask[bitsLeftToGo]; + + tmp = tmp << (codeBits - bitsLeftToGo); + + ret |= tmp; + + bitsLeftToGo -= (8 - bitsTaken); + bitsTaken = 0; + + if( bitsLeftToGo < 0 ) + bitsTaken = 8 + bitsLeftToGo; + + if( bitsTaken == 0 ) + filePos++; + } + +#if RIK_PIXEL_DEBUG + printf( "c%03X\n", ret ); +#endif + + return ret; +} + +/************************************************************************/ +/* OutputPixel() */ +/************************************************************************/ + +static void OutputPixel( GByte pixel, + void * image, + GUInt32 imageWidth, + GUInt32 lineBreak, + int &imageLine, + GUInt32 &imagePos ) + +{ + if( imagePos < imageWidth && imageLine >= 0) + ((GByte *) image)[imagePos + imageLine * imageWidth] = pixel; + + imagePos++; + +#if RIK_PIXEL_DEBUG + printf( "_%02X %d\n", pixel, imagePos ); +#endif + + // Check if we need to change line + + if( imagePos == lineBreak ) + { +#if RIK_PIXEL_DEBUG + printf( "\n%d\n", imageLine ); +#endif + + imagePos = 0; + + imageLine--; + } +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr RIKRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + RIKDataset *poRDS = (RIKDataset *) poDS; + GByte *blockData; + GUInt32 blocks; + GUInt32 nBlockIndex; + GUInt32 nBlockOffset; + GUInt32 nBlockSize; + + blocks = poRDS->nHorBlocks * poRDS->nVertBlocks; + nBlockIndex = nBlockXOff + nBlockYOff * poRDS->nHorBlocks; + nBlockOffset = poRDS->pOffsets[nBlockIndex]; + + nBlockSize = poRDS->nFileSize; + for( GUInt32 bi = nBlockIndex + 1; bi < blocks; bi++ ) + { + if( poRDS->pOffsets[bi] ) + { + nBlockSize = poRDS->pOffsets[bi]; + break; + } + } + nBlockSize -= nBlockOffset; + + GUInt32 pixels; + + pixels = poRDS->nBlockXSize * poRDS->nBlockYSize; + + if( !nBlockOffset || !nBlockSize +#ifdef RIK_SINGLE_BLOCK + || nBlockIndex != RIK_SINGLE_BLOCK +#endif + ) + { + for( GUInt32 i = 0; i < pixels; i++ ) + ((GByte *) pImage)[i] = 0; + return CE_None; + } + + VSIFSeekL( poRDS->fp, nBlockOffset, SEEK_SET ); + +/* -------------------------------------------------------------------- */ +/* Read uncompressed block. */ +/* -------------------------------------------------------------------- */ + + if( poRDS->options == 0x00 || poRDS->options == 0x40 ) + { + VSIFReadL( pImage, 1, nBlockSize, poRDS->fp ); + return CE_None; + } + + // Read block to memory + blockData = (GByte *) CPLMalloc(nBlockSize); + VSIFReadL( blockData, 1, nBlockSize, poRDS->fp ); + + GUInt32 filePos = 0; + GUInt32 imagePos = 0; + +/* -------------------------------------------------------------------- */ +/* Read RLE block. */ +/* -------------------------------------------------------------------- */ + + if( poRDS->options == 0x01 || + poRDS->options == 0x41 ) do + { + GByte count = blockData[filePos++]; + GByte color = blockData[filePos++]; + + for (GByte i = 0; i <= count; i++) + { + ((GByte *) pImage)[imagePos++] = color; + } + } while( filePos < nBlockSize && imagePos < pixels ); + +/* -------------------------------------------------------------------- */ +/* Read LZW block. */ +/* -------------------------------------------------------------------- */ + + else if( poRDS->options == 0x0b ) + { + const bool LZW_HAS_CLEAR_CODE = !!(blockData[4] & 0x80); + const int LZW_MAX_BITS = blockData[4] & 0x1f; // Max 13 + const int LZW_BITS_PER_PIXEL = 8; + const int LZW_OFFSET = 5; + + const int LZW_CLEAR = 1 << LZW_BITS_PER_PIXEL; + const int LZW_CODES = 1 << LZW_MAX_BITS; + const int LZW_NO_SUCH_CODE = LZW_CODES + 1; + + int lastAdded = LZW_HAS_CLEAR_CODE ? LZW_CLEAR : LZW_CLEAR - 1; + int codeBits = LZW_BITS_PER_PIXEL + 1; + + int code; + int lastCode; + GByte lastOutput; + int bitsTaken = 0; + + int prefix[8192]; // only need LZW_CODES for size. + GByte character[8192]; // only need LZW_CODES for size. + + int i; + + for( i = 0; i < LZW_CLEAR; i++ ) + character[i] = (GByte)i; + for( i = 0; i < LZW_CODES; i++ ) + prefix[i] = LZW_NO_SUCH_CODE; + + filePos = LZW_OFFSET; + GUInt32 fileAlign = LZW_OFFSET; + int imageLine = poRDS->nBlockYSize - 1; + + GUInt32 lineBreak = poRDS->nBlockXSize; + + // 32 bit alignment + lineBreak += 3; + lineBreak &= 0xfffffffc; + + code = GetNextLZWCode( codeBits, blockData, filePos, + fileAlign, bitsTaken ); + + OutputPixel( (GByte)code, pImage, poRDS->nBlockXSize, + lineBreak, imageLine, imagePos ); + lastOutput = (GByte)code; + + while( imageLine >= 0 && + (imageLine || imagePos < poRDS->nBlockXSize) && + filePos < nBlockSize ) try + { + lastCode = code; + code = GetNextLZWCode( codeBits, blockData, + filePos, fileAlign, bitsTaken ); + if( VSIFEofL( poRDS->fp ) ) + { + CPLFree( blockData ); + CPLError( CE_Failure, CPLE_AppDefined, + "RIK decompression failed. " + "Read past end of file.\n" ); + return CE_Failure; + } + + if( LZW_HAS_CLEAR_CODE && code == LZW_CLEAR ) + { +#if RIK_CLEAR_DEBUG + CPLDebug( "RIK", + "Clearing block %d\n" + " x=%d y=%d\n" + " pos=%d size=%d\n", + nBlockIndex, + imagePos, imageLine, + filePos, nBlockSize ); +#endif + + // Clear prefix table + for( i = LZW_CLEAR; i < LZW_CODES; i++ ) + prefix[i] = LZW_NO_SUCH_CODE; + lastAdded = LZW_CLEAR; + codeBits = LZW_BITS_PER_PIXEL + 1; + + filePos = fileAlign; + bitsTaken = 0; + + code = GetNextLZWCode( codeBits, blockData, + filePos, fileAlign, bitsTaken ); + + if( code > lastAdded ) + { + throw "Clear Error"; + } + + OutputPixel( (GByte)code, pImage, poRDS->nBlockXSize, + lineBreak, imageLine, imagePos ); + lastOutput = (GByte)code; + } + else + { + // Set-up decoding + + GByte stack[8192]; // only need LZW_CODES for size. + + int stackPtr = 0; + int decodeCode = code; + + if( code == lastAdded + 1 ) + { + // Handle special case + *stack = lastOutput; + stackPtr = 1; + decodeCode = lastCode; + } + else if( code > lastAdded + 1 ) + { + throw "Too high code"; + } + + // Decode + + i = 0; + while( ++i < LZW_CODES && + decodeCode >= LZW_CLEAR && + decodeCode < LZW_NO_SUCH_CODE ) + { + stack[stackPtr++] = character[decodeCode]; + decodeCode = prefix[decodeCode]; + } + stack[stackPtr++] = (GByte)decodeCode; + + if( i == LZW_CODES || decodeCode >= LZW_NO_SUCH_CODE ) + { + throw "Decode error"; + } + + // Output stack + + lastOutput = stack[stackPtr - 1]; + + while( stackPtr != 0 && imagePos < pixels ) + { + OutputPixel( stack[--stackPtr], pImage, poRDS->nBlockXSize, + lineBreak, imageLine, imagePos ); + } + + // Add code to string table + + if( lastCode != LZW_NO_SUCH_CODE && + lastAdded != LZW_CODES - 1 ) + { + prefix[++lastAdded] = lastCode; + character[lastAdded] = lastOutput; + } + + // Check if we need to use more bits + + if( lastAdded == (1 << codeBits) - 1 && + codeBits != LZW_MAX_BITS ) + { + codeBits++; + + filePos = fileAlign; + bitsTaken = 0; + } + } + } + catch (const char *errStr) + { +#if RIK_ALLOW_BLOCK_ERRORS + CPLDebug( "RIK", + "LZW Decompress Failed: %s\n" + " blocks: %d\n" + " blockindex: %d\n" + " blockoffset: %X\n" + " blocksize: %d\n", + errStr, blocks, nBlockIndex, + nBlockOffset, nBlockSize ); + break; +#else + CPLFree( blockData ); + CPLError( CE_Failure, CPLE_AppDefined, + "RIK decompression failed. " + "Corrupt image block." ); + return CE_Failure; +#endif + } + } + +/* -------------------------------------------------------------------- */ +/* Read ZLIB block. */ +/* -------------------------------------------------------------------- */ + + else if( poRDS->options == 0x0d ) + { + uLong destLen = pixels; + Byte *upsideDown = (Byte *) CPLMalloc( pixels ); + + uncompress( upsideDown, &destLen, blockData, nBlockSize ); + + for (GUInt32 i = 0; i < poRDS->nBlockYSize; i++) + { + memcpy( ((Byte *)pImage) + poRDS->nBlockXSize * i, + upsideDown + poRDS->nBlockXSize * + (poRDS->nBlockYSize - i - 1), + poRDS->nBlockXSize ); + } + + CPLFree( upsideDown ); + } + + CPLFree( blockData ); + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp RIKRasterBand::GetColorInterpretation() + +{ + return GCI_PaletteIndex; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *RIKRasterBand::GetColorTable() + +{ + RIKDataset *poRDS = (RIKDataset *) poDS; + + return poRDS->poColorTable; +} + +/************************************************************************/ +/* ==================================================================== */ +/* RIKDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ~RIKDataset() */ +/************************************************************************/ + +RIKDataset::~RIKDataset() + +{ + FlushCache(); + CPLFree( pOffsets ); + if( fp != NULL ) + VSIFCloseL( fp ); + delete poColorTable; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr RIKDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, &fTransform, sizeof(double) * 6 ); + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *RIKDataset::GetProjectionRef() + +{ + return( "PROJCS[\"RT90 2.5 gon V\",GEOGCS[\"RT90\",DATUM[\"Rikets_koordinatsystem_1990\",SPHEROID[\"Bessel 1841\",6377397.155,299.1528128,AUTHORITY[\"EPSG\",\"7004\"]],TOWGS84[414.1055246174,41.3265500042,603.0582474221,-0.8551163377,2.1413174055,-7.0227298286,0],AUTHORITY[\"EPSG\",\"6124\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4124\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",15.80827777777778],PARAMETER[\"scale_factor\",1],PARAMETER[\"false_easting\",1500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"3021\"]]" ); +} + +/************************************************************************/ +/* GetRikString() */ +/************************************************************************/ + +static GUInt16 GetRikString( VSILFILE *fp, + char *str, + GUInt16 strLength ) + +{ + GUInt16 actLength; + + VSIFReadL( &actLength, 1, sizeof(actLength), fp ); +#ifdef CPL_MSB + CPL_SWAP16PTR( &actLength ); +#endif + + if( actLength + 2 > strLength ) + { + return actLength; + } + + VSIFReadL( str, 1, actLength, fp ); + + str[actLength] = '\0'; + + return actLength; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int RIKDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->fpL == NULL || poOpenInfo->nHeaderBytes < 50 ) + return FALSE; + + if( EQUALN((const char *) poOpenInfo->pabyHeader, "RIK3", 4) ) + { + return TRUE; + } + else + { + GUInt16 actLength; + memcpy(&actLength, poOpenInfo->pabyHeader, 2); +#ifdef CPL_MSB + CPL_SWAP16PTR( &actLength ); +#endif + if( actLength + 2 > 1024 ) + { + return FALSE; + } + if( actLength == 0 ) + return -1; + if( strlen( (const char*)poOpenInfo->pabyHeader + 2 ) != actLength ) + { + return FALSE; + } + return TRUE; + } +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *RIKDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( Identify(poOpenInfo) == FALSE ) + return NULL; + + bool rik3header = false; + + if( EQUALN((const char *) poOpenInfo->pabyHeader, "RIK3", 4) ) + { + rik3header = true; + VSIFSeekL( poOpenInfo->fpL, 4, SEEK_SET ); + } + else + VSIFSeekL( poOpenInfo->fpL, 0, SEEK_SET ); + +/* -------------------------------------------------------------------- */ +/* Read the map name. */ +/* -------------------------------------------------------------------- */ + + char name[1024]; + + GUInt16 nameLength = GetRikString( poOpenInfo->fpL, name, sizeof(name) ); + + if( nameLength > sizeof(name) - 1 ) + { + return NULL; + } + + if( !rik3header ) + { + if( nameLength == 0 || nameLength != strlen(name) ) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + + RIKHeader header; + double metersPerPixel; + + const char *headerType = "RIK3"; + + if( rik3header ) + { +/* -------------------------------------------------------------------- */ +/* RIK3 header. */ +/* -------------------------------------------------------------------- */ + + // Read projection name + + char projection[1024]; + + GUInt16 projLength = GetRikString( poOpenInfo->fpL, + projection, sizeof(projection) ); + + if( projLength > sizeof(projection) - 1 ) + { + // Unreasonable string length, assume wrong format + return NULL; + } + + // Read unknown string + + projLength = GetRikString( poOpenInfo->fpL, projection, sizeof(projection) ); + + // Read map north edge + + char tmpStr[16]; + + GUInt16 tmpLength = GetRikString( poOpenInfo->fpL, + tmpStr, sizeof(tmpStr) ); + + if( tmpLength > sizeof(tmpStr) - 1 ) + { + // Unreasonable string length, assume wrong format + return NULL; + } + + header.fNorth = CPLAtof( tmpStr ); + + // Read map west edge + + tmpLength = GetRikString( poOpenInfo->fpL, + tmpStr, sizeof(tmpStr) ); + + if( tmpLength > sizeof(tmpStr) - 1 ) + { + // Unreasonable string length, assume wrong format + return NULL; + } + + header.fWest = CPLAtof( tmpStr ); + + // Read binary values + + VSIFReadL( &header.iScale, 1, sizeof(header.iScale), poOpenInfo->fpL ); + VSIFReadL( &header.iMPPNum, 1, sizeof(header.iMPPNum), poOpenInfo->fpL ); + VSIFReadL( &header.iBlockWidth, 1, sizeof(header.iBlockWidth), poOpenInfo->fpL ); + VSIFReadL( &header.iBlockHeight, 1, sizeof(header.iBlockHeight), poOpenInfo->fpL ); + VSIFReadL( &header.iHorBlocks, 1, sizeof(header.iHorBlocks), poOpenInfo->fpL ); + VSIFReadL( &header.iVertBlocks, 1, sizeof(header.iVertBlocks), poOpenInfo->fpL ); +#ifdef CPL_MSB + CPL_SWAP32PTR( &header.iScale ); + CPL_SWAP32PTR( &header.iMPPNum ); + CPL_SWAP32PTR( &header.iBlockWidth ); + CPL_SWAP32PTR( &header.iBlockHeight ); + CPL_SWAP32PTR( &header.iHorBlocks ); + CPL_SWAP32PTR( &header.iVertBlocks ); +#endif + + VSIFReadL( &header.iBitsPerPixel, 1, sizeof(header.iBitsPerPixel), poOpenInfo->fpL ); + VSIFReadL( &header.iOptions, 1, sizeof(header.iOptions), poOpenInfo->fpL ); + header.iUnknown = header.iOptions; + VSIFReadL( &header.iOptions, 1, sizeof(header.iOptions), poOpenInfo->fpL ); + + header.fSouth = header.fNorth - + header.iVertBlocks * header.iBlockHeight * header.iMPPNum; + header.fEast = header.fWest + + header.iHorBlocks * header.iBlockWidth * header.iMPPNum; + + metersPerPixel = header.iMPPNum; + } + else + { +/* -------------------------------------------------------------------- */ +/* Old RIK header. */ +/* -------------------------------------------------------------------- */ + + VSIFReadL( &header.iUnknown, 1, sizeof(header.iUnknown), poOpenInfo->fpL ); + VSIFReadL( &header.fSouth, 1, sizeof(header.fSouth), poOpenInfo->fpL ); + VSIFReadL( &header.fWest, 1, sizeof(header.fWest), poOpenInfo->fpL ); + VSIFReadL( &header.fNorth, 1, sizeof(header.fNorth), poOpenInfo->fpL ); + VSIFReadL( &header.fEast, 1, sizeof(header.fEast), poOpenInfo->fpL ); + VSIFReadL( &header.iScale, 1, sizeof(header.iScale), poOpenInfo->fpL ); + VSIFReadL( &header.iMPPNum, 1, sizeof(header.iMPPNum), poOpenInfo->fpL ); +#ifdef CPL_MSB + CPL_SWAP64PTR( &header.fSouth ); + CPL_SWAP64PTR( &header.fWest ); + CPL_SWAP64PTR( &header.fNorth ); + CPL_SWAP64PTR( &header.fEast ); + CPL_SWAP32PTR( &header.iScale ); + CPL_SWAP32PTR( &header.iMPPNum ); +#endif + + if (!CPLIsFinite(header.fSouth) | + !CPLIsFinite(header.fWest) | + !CPLIsFinite(header.fNorth) | + !CPLIsFinite(header.fEast)) + return NULL; + + bool offsetBounds; + + offsetBounds = header.fSouth < 4000000; + + header.iMPPDen = 1; + + if( offsetBounds ) + { + header.fSouth += 4002995; + header.fNorth += 5004000; + header.fWest += 201000; + header.fEast += 302005; + + VSIFReadL( &header.iMPPDen, 1, sizeof(header.iMPPDen), poOpenInfo->fpL ); +#ifdef CPL_MSB + CPL_SWAP32PTR( &header.iMPPDen ); +#endif + + headerType = "RIK1"; + } + else + { + headerType = "RIK2"; + } + + metersPerPixel = header.iMPPNum / double(header.iMPPDen); + + VSIFReadL( &header.iBlockWidth, 1, sizeof(header.iBlockWidth), poOpenInfo->fpL ); + VSIFReadL( &header.iBlockHeight, 1, sizeof(header.iBlockHeight), poOpenInfo->fpL ); + VSIFReadL( &header.iHorBlocks, 1, sizeof(header.iHorBlocks), poOpenInfo->fpL ); +#ifdef CPL_MSB + CPL_SWAP32PTR( &header.iBlockWidth ); + CPL_SWAP32PTR( &header.iBlockHeight ); + CPL_SWAP32PTR( &header.iHorBlocks ); +#endif + + if(( header.iBlockWidth > 2000 ) || ( header.iBlockWidth < 10 ) || + ( header.iBlockHeight > 2000 ) || ( header.iBlockHeight < 10 )) + return NULL; + + if( !offsetBounds ) + { + VSIFReadL( &header.iVertBlocks, 1, sizeof(header.iVertBlocks), poOpenInfo->fpL ); +#ifdef CPL_MSB + CPL_SWAP32PTR( &header.iVertBlocks ); +#endif + } + + if( offsetBounds || !header.iVertBlocks ) + { + header.iVertBlocks = (GUInt32) + ceil( (header.fNorth - header.fSouth) / + (header.iBlockHeight * metersPerPixel) ); + } + +#if RIK_HEADER_DEBUG + CPLDebug( "RIK", + "Original vertical blocks %d\n", + header.iVertBlocks ); +#endif + + VSIFReadL( &header.iBitsPerPixel, 1, sizeof(header.iBitsPerPixel), poOpenInfo->fpL ); + + if( header.iBitsPerPixel != 8 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "File %s has unsupported number of bits per pixel.\n", + poOpenInfo->pszFilename ); + return NULL; + } + + VSIFReadL( &header.iOptions, 1, sizeof(header.iOptions), poOpenInfo->fpL ); + + if( !header.iHorBlocks || !header.iVertBlocks ) + return NULL; + + if( header.iOptions != 0x00 && // Uncompressed + header.iOptions != 0x40 && // Uncompressed + header.iOptions != 0x01 && // RLE + header.iOptions != 0x41 && // RLE + header.iOptions != 0x0B && // LZW + header.iOptions != 0x0D ) // ZLIB + { + CPLError( CE_Failure, CPLE_OpenFailed, + "File %s. Unknown map options.\n", + poOpenInfo->pszFilename ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Read the palette. */ +/* -------------------------------------------------------------------- */ + + GByte palette[768]; + + GUInt16 i; + for( i = 0; i < 256; i++ ) + { + VSIFReadL( &palette[i * 3 + 2], 1, 1, poOpenInfo->fpL ); + VSIFReadL( &palette[i * 3 + 1], 1, 1, poOpenInfo->fpL ); + VSIFReadL( &palette[i * 3 + 0], 1, 1, poOpenInfo->fpL ); + } + +/* -------------------------------------------------------------------- */ +/* Find block offsets. */ +/* -------------------------------------------------------------------- */ + + GUInt32 blocks; + GUInt32 *offsets; + + blocks = header.iHorBlocks * header.iVertBlocks; + offsets = (GUInt32 *)CPLMalloc( blocks * sizeof(GUInt32) ); + + if( !offsets ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "File %s. Unable to allocate offset table.\n", + poOpenInfo->pszFilename ); + return NULL; + } + + if( header.iOptions == 0x00 ) + { + offsets[0] = VSIFTellL( poOpenInfo->fpL ); + + for( GUInt32 i = 1; i < blocks; i++ ) + { + offsets[i] = offsets[i - 1] + + header.iBlockWidth * header.iBlockHeight; + } + } + else + { + for( GUInt32 i = 0; i < blocks; i++ ) + { + VSIFReadL( &offsets[i], 1, sizeof(offsets[i]), poOpenInfo->fpL ); +#ifdef CPL_MSB + CPL_SWAP32PTR( &offsets[i] ); +#endif + if( rik3header ) + { + GUInt32 blockSize; + VSIFReadL( &blockSize, 1, sizeof(blockSize), poOpenInfo->fpL ); +#ifdef CPL_MSB + CPL_SWAP32PTR( &blockSize ); +#endif + } + } + } + +/* -------------------------------------------------------------------- */ +/* Final checks. */ +/* -------------------------------------------------------------------- */ + + // File size + + if( VSIFEofL( poOpenInfo->fpL ) ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "File %s. Read past end of file.\n", + poOpenInfo->pszFilename ); + return NULL; + } + + VSIFSeekL( poOpenInfo->fpL, 0, SEEK_END ); + GUInt32 fileSize = VSIFTellL( poOpenInfo->fpL ); + +#if RIK_HEADER_DEBUG + CPLDebug( "RIK", + "File size %d\n", + fileSize ); +#endif + + // Make sure the offset table is valid + + GUInt32 lastoffset = 0; + + for( GUInt32 y = 0; y < header.iVertBlocks; y++) + { + for( GUInt32 x = 0; x < header.iHorBlocks; x++) + { + if( !offsets[x + y * header.iHorBlocks] ) + { + continue; + } + + if( offsets[x + y * header.iHorBlocks] >= fileSize ) + { + if( !y ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "File %s too short.\n", + poOpenInfo->pszFilename ); + return NULL; + } + header.iVertBlocks = y; + break; + } + + if( offsets[x + y * header.iHorBlocks] < lastoffset ) + { + if( !y ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "File %s. Corrupt offset table.\n", + poOpenInfo->pszFilename ); + return NULL; + } + header.iVertBlocks = y; + break; + } + + lastoffset = offsets[x + y * header.iHorBlocks]; + } + } + +#if RIK_HEADER_DEBUG + CPLDebug( "RIK", + "first offset %d\n" + "last offset %d\n", + offsets[0], + lastoffset ); +#endif + + const char *compression = "RLE"; + + if( header.iOptions == 0x00 || + header.iOptions == 0x40 ) + compression = "Uncompressed"; + if( header.iOptions == 0x0b ) + compression = "LZW"; + if( header.iOptions == 0x0d ) + compression = "ZLIB"; + + CPLDebug( "RIK", + "RIK file parameters:\n" + " name: %s\n" + " header: %s\n" + " unknown: 0x%X\n" + " south: %f\n" + " west: %f\n" + " north: %f\n" + " east: %f\n" + " original scale: %d\n" + " meters per pixel: %f\n" + " block width: %d\n" + " block height: %d\n" + " horizontal blocks: %d\n" + " vertical blocks: %d\n" + " bits per pixel: %d\n" + " options: 0x%X\n" + " compression: %s\n", + name, headerType, header.iUnknown, + header.fSouth, header.fWest, header.fNorth, header.fEast, + header.iScale, metersPerPixel, + header.iBlockWidth, header.iBlockHeight, + header.iHorBlocks, header.iVertBlocks, + header.iBitsPerPixel, header.iOptions, compression); + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + + RIKDataset *poDS; + + poDS = new RIKDataset(); + + poDS->fp = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + + poDS->fTransform[0] = header.fWest - metersPerPixel / 2.0; + poDS->fTransform[1] = metersPerPixel; + poDS->fTransform[2] = 0.0; + poDS->fTransform[3] = header.fNorth + metersPerPixel / 2.0; + poDS->fTransform[4] = 0.0; + poDS->fTransform[5] = -metersPerPixel; + + poDS->nBlockXSize = header.iBlockWidth; + poDS->nBlockYSize = header.iBlockHeight; + poDS->nHorBlocks = header.iHorBlocks; + poDS->nVertBlocks = header.iVertBlocks; + poDS->pOffsets = offsets; + poDS->options = header.iOptions; + poDS->nFileSize = fileSize; + + poDS->nRasterXSize = header.iBlockWidth * header.iHorBlocks; + poDS->nRasterYSize = header.iBlockHeight * header.iVertBlocks; + + poDS->nBands = 1; + + GDALColorEntry oEntry; + poDS->poColorTable = new GDALColorTable(); + for( i = 0; i < 256; i++ ) + { + oEntry.c1 = palette[i * 3 + 2]; // Red + oEntry.c2 = palette[i * 3 + 1]; // Green + oEntry.c3 = palette[i * 3]; // Blue + oEntry.c4 = 255; + + poDS->poColorTable->SetColorEntry( i, &oEntry ); + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + + poDS->SetBand( 1, new RIKRasterBand( poDS, 1 )); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + delete poDS; + CPLError( CE_Failure, CPLE_NotSupported, + "The RIK driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_RIK() */ +/************************************************************************/ + +void GDALRegister_RIK() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "RIK" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "RIK" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Swedish Grid RIK (.rik)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#RIK" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "rik" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = RIKDataset::Open; + poDriver->pfnIdentify = RIKDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/rmf/GNUmakefile b/bazaar/plugin/gdal/frmts/rmf/GNUmakefile new file mode 100644 index 000000000..edcfce094 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rmf/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = rmfdataset.o rmflzw.o rmfdem.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/rmf/frmt_rmf.html b/bazaar/plugin/gdal/frmts/rmf/frmt_rmf.html new file mode 100644 index 000000000..3914d9d43 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rmf/frmt_rmf.html @@ -0,0 +1,56 @@ + + + RMF --- Raster Matrix Format + + + + +

    RMF --- Raster Matrix Format

    + +RMF is a simple tiled raster format used in the GIS "Integration" and +"Panorama" GIS. The format itself has very poor capabilities.

    + +There are two flavours of RMF called MTW and RSW. MTW supports 16-bit integer +and 32/64-bit floating point data in a single channel and aimed to store DEM +data. RSW is a general purpose raster, it supports single channel colormapped +or three channel RGB images. Only 8-bit data can be stored in RSW. Simple +georeferencing can be provided for both image types.

    + +

    Metadata

    +
      +
    • ELEVATION_MINIMUM: Minimum elevation value (MTW only).

      + +

    • ELEVATION_MAXIMUM: Maximum elevation value (MTW only).

      + +

    • ELEVATION_UNITS: Name of the units for raster values + (MTW only). Can be "m" (meters), "cm" (centimeters), + "dm" (decimeters), "mm" (millimeters).

      + +

    • ELEVATION_TYPE: Could be either 0 (absolute elevation) + or 1 (total elevation). MTW only.

      + +

    + +

    Creation Options

    +
      +
    • MTW=ON: Force the generation of MTW matrix (RSW will be + created by default).

      + +

    • BLOCKXSIZE=n: Sets tile width, defaults to 256.

      + +

    • BLOCKYSIZE=n: Set tile height. Tile height defaults to 256.

      + +

    + +

    See Also:

    + + + + + + diff --git a/bazaar/plugin/gdal/frmts/rmf/makefile.vc b/bazaar/plugin/gdal/frmts/rmf/makefile.vc new file mode 100644 index 000000000..840a1d452 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rmf/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = rmfdataset.obj rmfdem.obj rmflzw.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/rmf/rmfdataset.cpp b/bazaar/plugin/gdal/frmts/rmf/rmfdataset.cpp new file mode 100644 index 000000000..337083a90 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rmf/rmfdataset.cpp @@ -0,0 +1,1836 @@ +/****************************************************************************** + * $Id: rmfdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: Raster Matrix Format + * Purpose: Read/write raster files used in GIS "Integratsia" + * (also known as "Panorama" GIS). + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2005, Andrey Kiselev + * Copyright (c) 2007-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "cpl_string.h" + +#include "rmfdataset.h" + +CPL_CVSID("$Id: rmfdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +CPL_C_START +void GDALRegister_RMF(void); +CPL_C_END + +#define RMF_DEFAULT_BLOCKXSIZE 256 +#define RMF_DEFAULT_BLOCKYSIZE 256 + +static const char RMF_SigRSW[] = { 'R', 'S', 'W', '\0' }; +static const char RMF_SigRSW_BE[] = { '\0', 'W', 'S', 'R' }; +static const char RMF_SigMTW[] = { 'M', 'T', 'W', '\0' }; + +static const char RMF_UnitsEmpty[] = ""; +static const char RMF_UnitsM[] = "m"; +static const char RMF_UnitsCM[] = "cm"; +static const char RMF_UnitsDM[] = "dm"; +static const char RMF_UnitsMM[] = "mm"; + +/************************************************************************/ +/* ==================================================================== */ +/* RMFRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* RMFRasterBand() */ +/************************************************************************/ + +RMFRasterBand::RMFRasterBand( RMFDataset *poDS, int nBand, + GDALDataType eType ) +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = eType; + nBytesPerPixel = poDS->sHeader.nBitDepth / 8; + nDataSize = GDALGetDataTypeSize( eDataType ) / 8; + nBlockXSize = poDS->sHeader.nTileWidth; + nBlockYSize = poDS->sHeader.nTileHeight; + nBlockSize = nBlockXSize * nBlockYSize; + nBlockBytes = nBlockSize * nDataSize; + nLastTileXBytes = + (poDS->GetRasterXSize() % poDS->sHeader.nTileWidth) * nDataSize; + nLastTileHeight = poDS->GetRasterYSize() % poDS->sHeader.nTileHeight; + +#ifdef DEBUG + CPLDebug( "RMF", + "Band %d: tile width is %d, tile height is %d, " + " last tile width %d, last tile height %d, " + "bytes per pixel is %d, data type size is %d", + nBand, nBlockXSize, nBlockYSize, + poDS->sHeader.nLastTileWidth, poDS->sHeader.nLastTileHeight, + nBytesPerPixel, nDataSize ); +#endif +} + +/************************************************************************/ +/* ~RMFRasterBand() */ +/************************************************************************/ + +RMFRasterBand::~RMFRasterBand() +{ +} + +/************************************************************************/ +/* ReadBuffer() */ +/* */ +/* Helper fucntion to read specified amount of bytes from the input */ +/* file stream. */ +/************************************************************************/ + +CPLErr RMFRasterBand::ReadBuffer( GByte *pabyBuf, GUInt32 nBytes ) const +{ + RMFDataset *poGDS = (RMFDataset *) poDS; + + CPLAssert( pabyBuf != NULL && poGDS->fp != 0 ); + + vsi_l_offset nOffset = VSIFTellL( poGDS->fp ); + + if ( VSIFReadL( pabyBuf, 1, nBytes, poGDS->fp ) < nBytes ) + { + // XXX + if( poGDS->eAccess == GA_Update ) + { + return CE_Failure; + } + else + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't read at offset %ld from input file.\n%s\n", + (long)nOffset, VSIStrerror( errno ) ); + return CE_Failure; + } + } + +#ifdef CPL_MSB + if ( poGDS->eRMFType == RMFT_MTW ) + { + GUInt32 i; + + if ( poGDS->sHeader.nBitDepth == 16 ) + { + for ( i = 0; i < nBytes; i += 2 ) + CPL_SWAP16PTR( pabyBuf + i ); + } + + else if ( poGDS->sHeader.nBitDepth == 32 ) + { + for ( i = 0; i < nBytes; i += 4 ) + CPL_SWAP32PTR( pabyBuf + i ); + } + + else if ( poGDS->sHeader.nBitDepth == 64 ) + { + for ( i = 0; i < nBytes; i += 8 ) + CPL_SWAPDOUBLE( pabyBuf + i ); + } + } +#endif + + return CE_None; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr RMFRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + RMFDataset *poGDS = (RMFDataset *) poDS; + GUInt32 nTile = nBlockYOff * poGDS->nXTiles + nBlockXOff; + GUInt32 nTileBytes; + GUInt32 nCurBlockYSize; + + CPLAssert( poGDS != NULL + && nBlockXOff >= 0 + && nBlockYOff >= 0 + && pImage != NULL ); + + memset( pImage, 0, nBlockBytes ); + + if (2 * nTile + 1 >= poGDS->sHeader.nTileTblSize / sizeof(GUInt32)) + { + return CE_Failure; + } + + nTileBytes = poGDS->paiTiles[2 * nTile + 1]; + + if ( poGDS->sHeader.nLastTileHeight + && (GUInt32) nBlockYOff == poGDS->nYTiles - 1 ) + nCurBlockYSize = poGDS->sHeader.nLastTileHeight; + else + nCurBlockYSize = nBlockYSize; + + if ( VSIFSeekL( poGDS->fp, poGDS->paiTiles[2 * nTile], SEEK_SET ) < 0 ) + { + // XXX: We will not report error here, because file just may be + // in update state and data for this block will be available later + if( poGDS->eAccess == GA_Update ) + return CE_None; + else + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't seek to offset %ld in input file to read data.\n%s\n", + (long) poGDS->paiTiles[2 * nTile], VSIStrerror( errno ) ); + return CE_Failure; + } + } + + if ( poGDS->nBands == 1 && + ( poGDS->sHeader.nBitDepth == 8 + || poGDS->sHeader.nBitDepth == 16 + || poGDS->sHeader.nBitDepth == 32 + || poGDS->sHeader.nBitDepth == 64 ) ) + { + if ( nTileBytes > nBlockBytes ) + nTileBytes = nBlockBytes; + +/* -------------------------------------------------------------------- */ +/* Decompress buffer, if needed. */ +/* -------------------------------------------------------------------- */ + if ( poGDS->Decompress ) + { + GUInt32 nRawBytes; + + if ( nLastTileXBytes && (GUInt32)nBlockXOff == poGDS->nXTiles - 1 ) + nRawBytes = nLastTileXBytes; + else + nRawBytes = poGDS->nBands * nBlockXSize * nDataSize; + + if ( nLastTileHeight && (GUInt32)nBlockYOff == poGDS->nYTiles - 1 ) + nRawBytes *= nLastTileHeight; + else + nRawBytes *= nBlockYSize; + + if ( nRawBytes > nTileBytes ) + { + GByte *pabyTile = (GByte *) VSIMalloc( nTileBytes ); + + if ( !pabyTile ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't allocate tile block of size %lu.\n%s\n", + (unsigned long) nTileBytes, VSIStrerror( errno ) ); + return CE_Failure; + } + + if ( ReadBuffer( pabyTile, nTileBytes ) == CE_Failure ) + { + // XXX: Do not fail here, just return empty block + // and continue reading. + CPLFree( pabyTile ); + return CE_None; + } + + (*poGDS->Decompress)( pabyTile, nTileBytes, + (GByte*)pImage, nRawBytes ); + CPLFree( pabyTile ); + nTileBytes = nRawBytes; + } + else + { + if ( ReadBuffer( (GByte *)pImage, nTileBytes ) == CE_Failure ) + { + // XXX: Do not fail here, just return empty block + // and continue reading. + return CE_None; + } + } + } + + else + { + + if ( ReadBuffer( (GByte *)pImage, nTileBytes ) == CE_Failure ) + { + // XXX: Do not fail here, just return empty block + // and continue reading. + return CE_None; + } + + } + + } + + else if ( poGDS->eRMFType == RMFT_RSW ) + { + GByte *pabyTile = (GByte *) VSIMalloc( nTileBytes ); + + if ( !pabyTile ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't allocate tile block of size %lu.\n%s\n", + (unsigned long) nTileBytes, VSIStrerror( errno ) ); + return CE_Failure; + } + + + if ( ReadBuffer( pabyTile, nTileBytes ) == CE_Failure ) + { + // XXX: Do not fail here, just return empty block + // and continue reading. + CPLFree( pabyTile ); + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* If buffer was compressed, decompress it first. */ +/* -------------------------------------------------------------------- */ + if ( poGDS->Decompress ) + { + GUInt32 nRawBytes; + + if ( nLastTileXBytes && (GUInt32)nBlockXOff == poGDS->nXTiles - 1 ) + nRawBytes = nLastTileXBytes; + else + nRawBytes = poGDS->nBands * nBlockXSize * nDataSize; + + if ( nLastTileHeight && (GUInt32)nBlockYOff == poGDS->nYTiles - 1 ) + nRawBytes *= nLastTileHeight; + else + nRawBytes *= nBlockYSize; + + if ( nRawBytes > nTileBytes ) + { + GByte *pszRawBuf = (GByte *)VSIMalloc( nRawBytes ); + if (pszRawBuf == NULL) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't allocate a buffer for raw data of size %lu.\n%s\n", + (unsigned long) nRawBytes, VSIStrerror( errno ) ); + + VSIFree( pabyTile ); + return CE_Failure; + } + + (*poGDS->Decompress)( pabyTile, nTileBytes, + pszRawBuf, nRawBytes ); + CPLFree( pabyTile ); + pabyTile = pszRawBuf; + nTileBytes = nRawBytes; + } + } + +/* -------------------------------------------------------------------- */ +/* Deinterleave pixels from input buffer. */ +/* -------------------------------------------------------------------- */ + GUInt32 i; + + if ( poGDS->sHeader.nBitDepth == 24 || poGDS->sHeader.nBitDepth == 32 ) + { + GUInt32 nTileSize = nTileBytes / nBytesPerPixel; + + if ( nTileSize > nBlockSize ) + nTileSize = nBlockSize; + + for ( i = 0; i < nTileSize; i++ ) + { + // Colour triplets in RMF file organized in reverse order: + // blue, green, red. When we have 32-bit RMF the forth byte + // in quadriplet should be discarded as it has no meaning. + // That is why we always use 3 byte count in the following + // pabyTemp index. + ((GByte *) pImage)[i] = + pabyTile[i * nBytesPerPixel + 3 - nBand]; + } + } + + else if ( poGDS->sHeader.nBitDepth == 16 ) + { + GUInt32 nTileSize = nTileBytes / nBytesPerPixel; + + if ( nTileSize > nBlockSize ) + nTileSize = nBlockSize; + + for ( i = 0; i < nTileSize; i++ ) + { + switch ( nBand ) + { + case 1: + ((GByte *) pImage)[i] = + (GByte)((((GUInt16*)pabyTile)[i] & 0x7c00) >> 7); + break; + case 2: + ((GByte *) pImage)[i] = + (GByte)((((GUInt16*)pabyTile)[i] & 0x03e0) >> 2); + break; + case 3: + ((GByte *) pImage)[i] = + (GByte)(((GUInt16*)pabyTile)[i] & 0x1F) << 3; + break; + default: + break; + } + } + } + + else if ( poGDS->sHeader.nBitDepth == 4 ) + { + GByte *pabyTemp = pabyTile; + + for ( i = 0; i < nBlockSize; i++ ) + { + // Most significant part of the byte represents leftmost pixel + if ( i & 0x01 ) + ((GByte *) pImage)[i] = *pabyTemp++ & 0x0F; + else + ((GByte *) pImage)[i] = (*pabyTemp & 0xF0) >> 4; + } + } + + else if ( poGDS->sHeader.nBitDepth == 1 ) + { + GByte *pabyTemp = pabyTile; + + for ( i = 0; i < nBlockSize; i++ ) + { + switch ( i & 0x7 ) + { + case 0: + ((GByte *) pImage)[i] = (*pabyTemp & 0x80) >> 7; + break; + case 1: + ((GByte *) pImage)[i] = (*pabyTemp & 0x40) >> 6; + break; + case 2: + ((GByte *) pImage)[i] = (*pabyTemp & 0x20) >> 5; + break; + case 3: + ((GByte *) pImage)[i] = (*pabyTemp & 0x10) >> 4; + break; + case 4: + ((GByte *) pImage)[i] = (*pabyTemp & 0x08) >> 3; + break; + case 5: + ((GByte *) pImage)[i] = (*pabyTemp & 0x04) >> 2; + break; + case 6: + ((GByte *) pImage)[i] = (*pabyTemp & 0x02) >> 1; + break; + case 7: + ((GByte *) pImage)[i] = *pabyTemp++ & 0x01; + break; + default: + break; + } + } + } + + CPLFree( pabyTile ); + } + + if ( nLastTileXBytes + && (GUInt32) nBlockXOff == poGDS->nXTiles - 1 ) + { + GUInt32 iRow; + + for ( iRow = nCurBlockYSize - 1; iRow > 0; iRow-- ) + { + memmove( (GByte *)pImage + nBlockXSize * iRow * nDataSize, + (GByte *)pImage + iRow * nLastTileXBytes, + nLastTileXBytes ); + } + + } + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr RMFRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + RMFDataset *poGDS = (RMFDataset *)poDS; + GUInt32 nTile = nBlockYOff * poGDS->nXTiles + nBlockXOff; + GUInt32 nTileBytes = nDataSize * poGDS->nBands; + GUInt32 iInPixel, iOutPixel, nCurBlockYSize; + GByte *pabyTile; + + CPLAssert( poGDS != NULL + && nBlockXOff >= 0 + && nBlockYOff >= 0 + && pImage != NULL ); + + if ( poGDS->paiTiles[2 * nTile] ) + { + if ( VSIFSeekL( poGDS->fp, poGDS->paiTiles[2 * nTile], SEEK_SET ) < 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't seek to offset %ld in output file to write data.\n%s", + (long) poGDS->paiTiles[2 * nTile], + VSIStrerror( errno ) ); + return CE_Failure; + } + } + else + { + if ( VSIFSeekL( poGDS->fp, 0, SEEK_END ) < 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't seek to offset %ld in output file to write data.\n%s", + (long) poGDS->paiTiles[2 * nTile], + VSIStrerror( errno ) ); + return CE_Failure; + } + poGDS->paiTiles[2 * nTile] = (GUInt32) VSIFTellL( poGDS->fp ); + + poGDS->bHeaderDirty = TRUE; + } + + if ( nLastTileXBytes + && (GUInt32) nBlockXOff == poGDS->nXTiles - 1 ) + nTileBytes *= poGDS->sHeader.nLastTileWidth; + else + nTileBytes *= nBlockXSize; + + if ( poGDS->sHeader.nLastTileHeight + && (GUInt32) nBlockYOff == poGDS->nYTiles - 1 ) + nCurBlockYSize = poGDS->sHeader.nLastTileHeight; + else + nCurBlockYSize = nBlockYSize; + + nTileBytes *= nCurBlockYSize; + + pabyTile = (GByte *) VSICalloc( nTileBytes, 1 ); + if ( !pabyTile ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't allocate space for the tile blocak of size %lu.\n%s", + (unsigned long) nTileBytes, VSIStrerror( errno ) ); + return CE_Failure; + } + + if ( nLastTileXBytes + && (GUInt32) nBlockXOff == poGDS->nXTiles - 1 ) + { + GUInt32 iRow; + + if ( poGDS->nBands == 1 ) + { + for ( iRow = 0; iRow < nCurBlockYSize; iRow++ ) + { + memcpy( pabyTile + iRow * nLastTileXBytes, + (GByte*)pImage + nBlockXSize * iRow * nDataSize, + nLastTileXBytes ); + } + } + else + { + if ( poGDS->paiTiles[2 * nTile + 1] ) + { + VSIFReadL( pabyTile, 1, nTileBytes, poGDS->fp ); + VSIFSeekL( poGDS->fp, poGDS->paiTiles[2 * nTile], SEEK_SET ); + } + + for ( iRow = 0; iRow < nCurBlockYSize; iRow++ ) + { + for ( iInPixel = 0, iOutPixel = nBytesPerPixel - nBand; + iOutPixel < nLastTileXBytes * poGDS->nBands; + iInPixel++, iOutPixel += poGDS->nBands ) + (pabyTile + iRow * nLastTileXBytes * poGDS->nBands)[iOutPixel] = + ((GByte *) pImage + nBlockXSize * iRow * nDataSize)[iInPixel]; + } + } + } + else + { + if ( poGDS->nBands == 1 ) + memcpy( pabyTile, pImage, nTileBytes ); + else + { + if ( poGDS->paiTiles[2 * nTile + 1] ) + { + VSIFReadL( pabyTile, 1, nTileBytes, poGDS->fp ); + VSIFSeekL( poGDS->fp, poGDS->paiTiles[2 * nTile], SEEK_SET ); + } + + for ( iInPixel = 0, iOutPixel = nBytesPerPixel - nBand; + iOutPixel < nTileBytes; + iInPixel++, iOutPixel += poGDS->nBands ) + pabyTile[iOutPixel] = ((GByte *) pImage)[iInPixel]; + + } + } + +#ifdef CPL_MSB + if ( poGDS->eRMFType == RMFT_MTW ) + { + GUInt32 i; + + if ( poGDS->sHeader.nBitDepth == 16 ) + { + for ( i = 0; i < nTileBytes; i += 2 ) + CPL_SWAP16PTR( pabyTile + i ); + } + + else if ( poGDS->sHeader.nBitDepth == 32 ) + { + for ( i = 0; i < nTileBytes; i += 4 ) + CPL_SWAP32PTR( pabyTile + i ); + } + + else if ( poGDS->sHeader.nBitDepth == 64 ) + { + for ( i = 0; i < nTileBytes; i += 8 ) + CPL_SWAPDOUBLE( pabyTile + i ); + } + } +#endif + + if ( VSIFWriteL( pabyTile, 1, nTileBytes, poGDS->fp ) < nTileBytes ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Can't write block with X offset %d and Y offset %d.\n%s", + nBlockXOff, nBlockYOff, VSIStrerror( errno ) ); + VSIFree( pabyTile ); + return CE_Failure; + } + + poGDS->paiTiles[2 * nTile + 1] = nTileBytes; + VSIFree( pabyTile ); + + poGDS->bHeaderDirty = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +const char *RMFRasterBand::GetUnitType() + +{ + RMFDataset *poGDS = (RMFDataset *) poDS; + + return (const char *)poGDS->pszUnitType; +} + +/************************************************************************/ +/* SetUnitType() */ +/************************************************************************/ + +CPLErr RMFRasterBand::SetUnitType( const char *pszNewValue ) + +{ + RMFDataset *poGDS = (RMFDataset *) poDS; + + CPLFree(poGDS->pszUnitType); + poGDS->pszUnitType = CPLStrdup( pszNewValue ); + + return CE_None; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *RMFRasterBand::GetColorTable() +{ + RMFDataset *poGDS = (RMFDataset *) poDS; + + return poGDS->poColorTable; +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr RMFRasterBand::SetColorTable( GDALColorTable *poColorTable ) +{ + RMFDataset *poGDS = (RMFDataset *) poDS; + + if ( poColorTable ) + { + if ( poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1 ) + { + GDALColorEntry oEntry; + GUInt32 i; + + if ( !poGDS->pabyColorTable ) + return CE_Failure; + + for( i = 0; i < poGDS->nColorTableSize; i++ ) + { + poColorTable->GetColorEntryAsRGB( i, &oEntry ); + poGDS->pabyColorTable[i * 4] = (GByte) oEntry.c1; // Red + poGDS->pabyColorTable[i * 4 + 1] = (GByte) oEntry.c2; // Green + poGDS->pabyColorTable[i * 4 + 2] = (GByte) oEntry.c3; // Blue + poGDS->pabyColorTable[i * 4 + 3] = 0; + } + + poGDS->bHeaderDirty = TRUE; + } + } + else + return CE_Failure; + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp RMFRasterBand::GetColorInterpretation() +{ + RMFDataset *poGDS = (RMFDataset *) poDS; + + if( poGDS->nBands == 3 ) + { + if( nBand == 1 ) + return GCI_RedBand; + else if( nBand == 2 ) + return GCI_GreenBand; + else if( nBand == 3 ) + return GCI_BlueBand; + else + return GCI_Undefined; + } + else + { + if ( poGDS->eRMFType == RMFT_RSW ) + return GCI_PaletteIndex; + else + return GCI_Undefined; + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* RMFDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* RMFDataset() */ +/************************************************************************/ + +RMFDataset::RMFDataset() +{ + pszFilename = NULL; + fp = NULL; + nBands = 0; + nXTiles = 0; + nYTiles = 0; + paiTiles = NULL; + pszProjection = CPLStrdup( "" ); + pszUnitType = CPLStrdup( RMF_UnitsEmpty ); + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + pabyColorTable = NULL; + poColorTable = NULL; + eRMFType = RMFT_RSW; + memset( &sHeader, 0, sizeof(sHeader) ); + memset( &sExtHeader, 0, sizeof(sExtHeader) ); + + Decompress = NULL; + + bBigEndian = FALSE; + bHeaderDirty = FALSE; +} + +/************************************************************************/ +/* ~RMFDataset() */ +/************************************************************************/ + +RMFDataset::~RMFDataset() +{ + FlushCache(); + + if ( paiTiles ) + CPLFree( paiTiles ); + if ( pszProjection ) + CPLFree( pszProjection ); + if ( pszUnitType ) + CPLFree( pszUnitType ); + if ( pabyColorTable ) + CPLFree( pabyColorTable ); + if ( poColorTable != NULL ) + delete poColorTable; + if( fp != NULL ) + VSIFCloseL( fp ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr RMFDataset::GetGeoTransform( double * padfTransform ) +{ + memcpy( padfTransform, adfGeoTransform, sizeof(adfGeoTransform[0]) * 6 ); + + if( sHeader.iGeorefFlag ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr RMFDataset::SetGeoTransform( double * padfTransform ) +{ + memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 ); + sHeader.dfPixelSize = adfGeoTransform[1]; + if ( sHeader.dfPixelSize != 0.0 ) + sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize; + sHeader.dfLLX = adfGeoTransform[0]; + sHeader.dfLLY = adfGeoTransform[3] - nRasterYSize * sHeader.dfPixelSize; + sHeader.iGeorefFlag = 1; + + bHeaderDirty = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *RMFDataset::GetProjectionRef() +{ + if( pszProjection ) + return pszProjection; + else + return ""; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr RMFDataset::SetProjection( const char * pszNewProjection ) + +{ + if ( pszProjection ) + CPLFree( pszProjection ); + pszProjection = CPLStrdup( (pszNewProjection) ? pszNewProjection : "" ); + + bHeaderDirty = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* WriteHeader() */ +/************************************************************************/ + +CPLErr RMFDataset::WriteHeader() +{ +/* -------------------------------------------------------------------- */ +/* Setup projection. */ +/* -------------------------------------------------------------------- */ + if( pszProjection && !EQUAL( pszProjection, "" ) ) + { + OGRSpatialReference oSRS; + long iProjection, iDatum, iEllips, iZone; + char *pszProj = pszProjection; + + if ( oSRS.importFromWkt( &pszProj ) == OGRERR_NONE ) + { + double adfPrjParams[7]; + + oSRS.exportToPanorama( &iProjection, &iDatum, &iEllips, &iZone, + adfPrjParams ); + sHeader.iProjection = iProjection; + sHeader.dfStdP1 = adfPrjParams[0]; + sHeader.dfStdP2 = adfPrjParams[1]; + sHeader.dfCenterLat = adfPrjParams[2]; + sHeader.dfCenterLong = adfPrjParams[3]; + + sExtHeader.nEllipsoid = iEllips; + sExtHeader.nDatum = iDatum; + sExtHeader.nZone = iZone; + } + } + +#define RMF_WRITE_LONG( ptr, value, offset ) \ +do { \ + GInt32 iLong = CPL_LSBWORD32( value ); \ + memcpy( (ptr) + (offset), &iLong, 4 ); \ +} while(0); + +#define RMF_WRITE_ULONG( ptr,value, offset ) \ +do { \ + GUInt32 iULong = CPL_LSBWORD32( value ); \ + memcpy( (ptr) + (offset), &iULong, 4 ); \ +} while(0); + +#define RMF_WRITE_DOUBLE( ptr,value, offset ) \ +do { \ + double dfDouble = (value); \ + CPL_LSBPTR64( &dfDouble ); \ + memcpy( (ptr) + (offset), &dfDouble, 8 ); \ +} while(0); + +/* -------------------------------------------------------------------- */ +/* Write out the main header. */ +/* -------------------------------------------------------------------- */ + { + GByte abyHeader[RMF_HEADER_SIZE]; + + memset( abyHeader, 0, sizeof(abyHeader) ); + + memcpy( abyHeader, sHeader.bySignature, RMF_SIGNATURE_SIZE ); + RMF_WRITE_ULONG( abyHeader, sHeader.iVersion, 4 ); + // + RMF_WRITE_ULONG( abyHeader, sHeader.nOvrOffset, 12 ); + RMF_WRITE_ULONG( abyHeader, sHeader.iUserID, 16 ); + memcpy( abyHeader + 20, sHeader.byName, RMF_NAME_SIZE ); + RMF_WRITE_ULONG( abyHeader, sHeader.nBitDepth, 52 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nHeight, 56 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nWidth, 60 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nXTiles, 64 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nYTiles, 68 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nTileHeight, 72 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nTileWidth, 76 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nLastTileHeight, 80 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nLastTileWidth, 84 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nROIOffset, 88 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nROISize, 92 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nClrTblOffset, 96 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nClrTblSize, 100 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nTileTblOffset, 104 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nTileTblSize, 108 ); + RMF_WRITE_LONG( abyHeader, sHeader.iMapType, 124 ); + RMF_WRITE_LONG( abyHeader, sHeader.iProjection, 128 ); + RMF_WRITE_DOUBLE( abyHeader, sHeader.dfScale, 136 ); + RMF_WRITE_DOUBLE( abyHeader, sHeader.dfResolution, 144 ); + RMF_WRITE_DOUBLE( abyHeader, sHeader.dfPixelSize, 152 ); + RMF_WRITE_DOUBLE( abyHeader, sHeader.dfLLY, 160 ); + RMF_WRITE_DOUBLE( abyHeader, sHeader.dfLLX, 168 ); + RMF_WRITE_DOUBLE( abyHeader, sHeader.dfStdP1, 176 ); + RMF_WRITE_DOUBLE( abyHeader, sHeader.dfStdP2, 184 ); + RMF_WRITE_DOUBLE( abyHeader, sHeader.dfCenterLong, 192 ); + RMF_WRITE_DOUBLE( abyHeader, sHeader.dfCenterLat, 200 ); + *(abyHeader + 208) = sHeader.iCompression; + *(abyHeader + 209) = sHeader.iMaskType; + *(abyHeader + 210) = sHeader.iMaskStep; + *(abyHeader + 211) = sHeader.iFrameFlag; + RMF_WRITE_ULONG( abyHeader, sHeader.nFlagsTblOffset, 212 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nFlagsTblSize, 216 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nFileSize0, 220 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nFileSize1, 224 ); + *(abyHeader + 228) = sHeader.iUnknown; + *(abyHeader + 244) = sHeader.iGeorefFlag; + *(abyHeader + 245) = sHeader.iInverse; + memcpy( abyHeader + 248, sHeader.abyInvisibleColors, + sizeof(sHeader.abyInvisibleColors) ); + RMF_WRITE_DOUBLE( abyHeader, sHeader.adfElevMinMax[0], 280 ); + RMF_WRITE_DOUBLE( abyHeader, sHeader.adfElevMinMax[1], 288 ); + RMF_WRITE_DOUBLE( abyHeader, sHeader.dfNoData, 296 ); + RMF_WRITE_ULONG( abyHeader, sHeader.iElevationUnit, 304 ); + *(abyHeader + 308) = sHeader.iElevationType; + RMF_WRITE_ULONG( abyHeader, sHeader.nExtHdrOffset, 312 ); + RMF_WRITE_ULONG( abyHeader, sHeader.nExtHdrSize, 316 ); + + VSIFSeekL( fp, 0, SEEK_SET ); + VSIFWriteL( abyHeader, 1, sizeof(abyHeader), fp ); + } + +/* -------------------------------------------------------------------- */ +/* Write out the extended header. */ +/* -------------------------------------------------------------------- */ + + if ( sHeader.nExtHdrOffset && sHeader.nExtHdrSize ) + { + GByte *pabyExtHeader = (GByte *)CPLCalloc( sHeader.nExtHdrSize, 1 ); + + RMF_WRITE_LONG( pabyExtHeader, sExtHeader.nEllipsoid, 24 ); + RMF_WRITE_LONG( pabyExtHeader, sExtHeader.nDatum, 32 ); + RMF_WRITE_LONG( pabyExtHeader, sExtHeader.nZone, 36 ); + + VSIFSeekL( fp, sHeader.nExtHdrOffset, SEEK_SET ); + VSIFWriteL( pabyExtHeader, 1, sHeader.nExtHdrSize, fp ); + + CPLFree( pabyExtHeader ); + } + +#undef RMF_WRITE_DOUBLE +#undef RMF_WRITE_ULONG +#undef RMF_WRITE_LONG + +/* -------------------------------------------------------------------- */ +/* Write out the color table. */ +/* -------------------------------------------------------------------- */ + + if ( sHeader.nClrTblOffset && sHeader.nClrTblSize ) + { + VSIFSeekL( fp, sHeader.nClrTblOffset, SEEK_SET ); + VSIFWriteL( pabyColorTable, 1, sHeader.nClrTblSize, fp ); + } + +/* -------------------------------------------------------------------- */ +/* Write out the block table, swap if needed. */ +/* -------------------------------------------------------------------- */ + + VSIFSeekL( fp, sHeader.nTileTblOffset, SEEK_SET ); + +#ifdef CPL_MSB + GUInt32 i; + GUInt32 *paiTilesSwapped = (GUInt32 *)CPLMalloc( sHeader.nTileTblSize ); + + if ( !paiTilesSwapped ) + return CE_Failure; + + memcpy( paiTilesSwapped, paiTiles, sHeader.nTileTblSize ); + for ( i = 0; i < sHeader.nTileTblSize / sizeof(GUInt32); i++ ) + CPL_SWAP32PTR( paiTilesSwapped + i ); + VSIFWriteL( paiTilesSwapped, 1, sHeader.nTileTblSize, fp ); + + CPLFree( paiTilesSwapped ); +#else + VSIFWriteL( paiTiles, 1, sHeader.nTileTblSize, fp ); +#endif + + bHeaderDirty = FALSE; + + return CE_None; +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void RMFDataset::FlushCache() + +{ + GDALDataset::FlushCache(); + + if ( !bHeaderDirty ) + return; + + if ( eRMFType == RMFT_MTW ) + { + GDALRasterBand *poBand = GetRasterBand(1); + + if ( poBand ) + { + poBand->ComputeRasterMinMax( FALSE, sHeader.adfElevMinMax ); + bHeaderDirty = TRUE; + } + } + WriteHeader(); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int RMFDataset::Identify( GDALOpenInfo *poOpenInfo ) + +{ + if( poOpenInfo->pabyHeader == NULL) + return FALSE; + + if( memcmp(poOpenInfo->pabyHeader, RMF_SigRSW, sizeof(RMF_SigRSW)) != 0 + && memcmp(poOpenInfo->pabyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) != 0 + && memcmp(poOpenInfo->pabyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) != 0 ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *RMFDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + if ( !Identify(poOpenInfo) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + RMFDataset *poDS; + + poDS = new RMFDataset(); + + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" ); + if ( !poDS->fp ) + { + delete poDS; + return NULL; + } + +#define RMF_READ_ULONG(ptr, value, offset) \ +do { \ + if ( poDS->bBigEndian ) \ + { \ + (value) = CPL_MSBWORD32(*(GUInt32*)((ptr) + (offset))); \ + } \ + else \ + { \ + (value) = CPL_LSBWORD32(*(GUInt32*)((ptr) + (offset))); \ + } \ +} while(0); + +#define RMF_READ_LONG(ptr, value, offset) \ +do { \ + if ( poDS->bBigEndian ) \ + { \ + (value) = CPL_MSBWORD32(*(GInt32*)((ptr) + (offset))); \ + } \ + else \ + { \ + (value) = CPL_LSBWORD32(*(GInt32*)((ptr) + (offset))); \ + } \ +} while(0); + +#define RMF_READ_DOUBLE(ptr, value, offset) \ +do { \ + (value) = *(double*)((ptr) + (offset)); \ + if ( poDS->bBigEndian ) \ + { \ + CPL_MSBPTR64(&(value)); \ + } \ + else \ + { \ + CPL_LSBPTR64(&(value)); \ + } \ +} while(0); + +/* -------------------------------------------------------------------- */ +/* Read the main header. */ +/* -------------------------------------------------------------------- */ + + { + GByte abyHeader[RMF_HEADER_SIZE]; + + VSIFSeekL( poDS->fp, 0, SEEK_SET ); + VSIFReadL( abyHeader, 1, sizeof(abyHeader), poDS->fp ); + + if ( memcmp(abyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) == 0 ) + poDS->eRMFType = RMFT_MTW; + else if ( memcmp(abyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) == 0 ) + { + poDS->eRMFType = RMFT_RSW; + poDS->bBigEndian = TRUE; + } + else + poDS->eRMFType = RMFT_RSW; + + memcpy( poDS->sHeader.bySignature, abyHeader, RMF_SIGNATURE_SIZE ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.iVersion, 4 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nSize, 8 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nOvrOffset, 12 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.iUserID, 16 ); + memcpy( poDS->sHeader.byName, abyHeader + 20, + sizeof(poDS->sHeader.byName) ); + poDS->sHeader.byName[sizeof(poDS->sHeader.byName) - 1] = '\0'; + RMF_READ_ULONG( abyHeader, poDS->sHeader.nBitDepth, 52 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nHeight, 56 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nWidth, 60 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nXTiles, 64 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nYTiles, 68 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nTileHeight, 72 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nTileWidth, 76 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nLastTileHeight, 80 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nLastTileWidth, 84 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nROIOffset, 88 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nROISize, 92 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nClrTblOffset, 96 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nClrTblSize, 100 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nTileTblOffset, 104 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nTileTblSize, 108 ); + RMF_READ_LONG( abyHeader, poDS->sHeader.iMapType, 124 ); + RMF_READ_LONG( abyHeader, poDS->sHeader.iProjection, 128 ); + RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfScale, 136 ); + RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfResolution, 144 ); + RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfPixelSize, 152 ); + RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfLLY, 160 ); + RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfLLX, 168 ); + RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfStdP1, 176 ); + RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfStdP2, 184 ); + RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfCenterLong, 192 ); + RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfCenterLat, 200 ); + poDS->sHeader.iCompression = *(abyHeader + 208); + poDS->sHeader.iMaskType = *(abyHeader + 209); + poDS->sHeader.iMaskStep = *(abyHeader + 210); + poDS->sHeader.iFrameFlag = *(abyHeader + 211); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nFlagsTblOffset, 212 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nFlagsTblSize, 216 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nFileSize0, 220 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nFileSize1, 224 ); + poDS->sHeader.iUnknown = *(abyHeader + 228); + poDS->sHeader.iGeorefFlag = *(abyHeader + 244); + poDS->sHeader.iInverse = *(abyHeader + 245); + memcpy( poDS->sHeader.abyInvisibleColors, + abyHeader + 248, sizeof(poDS->sHeader.abyInvisibleColors) ); + RMF_READ_DOUBLE( abyHeader, poDS->sHeader.adfElevMinMax[0], 280 ); + RMF_READ_DOUBLE( abyHeader, poDS->sHeader.adfElevMinMax[1], 288 ); + RMF_READ_DOUBLE( abyHeader, poDS->sHeader.dfNoData, 296 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.iElevationUnit, 304 ); + poDS->sHeader.iElevationType = *(abyHeader + 308); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nExtHdrOffset, 312 ); + RMF_READ_ULONG( abyHeader, poDS->sHeader.nExtHdrSize, 316 ); + } + +/* -------------------------------------------------------------------- */ +/* Read the extended header. */ +/* -------------------------------------------------------------------- */ + + if ( poDS->sHeader.nExtHdrOffset && poDS->sHeader.nExtHdrSize ) + { + GByte *pabyExtHeader = + (GByte *)VSICalloc( poDS->sHeader.nExtHdrSize, 1 ); + if (pabyExtHeader == NULL) + { + delete poDS; + return NULL; + } + + VSIFSeekL( poDS->fp, poDS->sHeader.nExtHdrOffset, SEEK_SET ); + VSIFReadL( pabyExtHeader, 1, poDS->sHeader.nExtHdrSize, poDS->fp ); + + RMF_READ_LONG( pabyExtHeader, poDS->sExtHeader.nEllipsoid, 24 ); + RMF_READ_LONG( pabyExtHeader, poDS->sExtHeader.nDatum, 32 ); + RMF_READ_LONG( pabyExtHeader, poDS->sExtHeader.nZone, 36 ); + + CPLFree( pabyExtHeader ); + } + +#undef RMF_READ_DOUBLE +#undef RMF_READ_LONG +#undef RMF_READ_ULONG + +#ifdef DEBUG + + CPLDebug( "RMF", "%s image has width %d, height %d, bit depth %d, " + "compression scheme %d, %s, nodata %f", + (poDS->eRMFType == RMFT_MTW) ? "MTW" : "RSW", + poDS->sHeader.nWidth, poDS->sHeader.nHeight, + poDS->sHeader.nBitDepth, poDS->sHeader.iCompression, + poDS->bBigEndian ? "big endian" : "little endian", + poDS->sHeader.dfNoData ); + CPLDebug( "RMF", "Size %d, offset to overview %#lx, user ID %d, " + "ROI offset %#lx, ROI size %d", + poDS->sHeader.nSize, (unsigned long)poDS->sHeader.nOvrOffset, + poDS->sHeader.iUserID, (unsigned long)poDS->sHeader.nROIOffset, + poDS->sHeader.nROISize ); + CPLDebug( "RMF", "Map type %d, projection %d, scale %f, resolution %f, ", + poDS->sHeader.iMapType, poDS->sHeader.iProjection, + poDS->sHeader.dfScale, poDS->sHeader.dfResolution ); + CPLDebug( "RMF", "Georeferencing: pixel size %f, LLX %f, LLY %f", + poDS->sHeader.dfPixelSize, + poDS->sHeader.dfLLX, poDS->sHeader.dfLLY ); + if ( poDS->sHeader.nROIOffset && poDS->sHeader.nROISize ) + { + GUInt32 i; + GInt32 nValue; + + CPLDebug( "RMF", "ROI coordinates:" ); + for ( i = 0; i < poDS->sHeader.nROISize; i += sizeof(nValue) ) + { + GUInt32 nValue; + + VSIFSeekL( poDS->fp, poDS->sHeader.nROIOffset + i, SEEK_SET ); + VSIFReadL( &nValue, 1, sizeof(nValue), poDS->fp ); + + CPLDebug( "RMF", "%d", nValue ); + } + } +#endif + +/* -------------------------------------------------------------------- */ +/* Read array of blocks offsets/sizes. */ +/* -------------------------------------------------------------------- */ + GUInt32 i; + + if ( VSIFSeekL( poDS->fp, poDS->sHeader.nTileTblOffset, SEEK_SET ) < 0) + { + delete poDS; + return NULL; + } + + poDS->paiTiles = (GUInt32 *)VSIMalloc( poDS->sHeader.nTileTblSize ); + if ( !poDS->paiTiles ) + { + delete poDS; + return NULL; + } + + if ( VSIFReadL( poDS->paiTiles, 1, poDS->sHeader.nTileTblSize, + poDS->fp ) < poDS->sHeader.nTileTblSize ) + { + CPLDebug( "RMF", "Can't read tiles offsets/sizes table." ); + delete poDS; + return NULL; + } + +#ifdef CPL_MSB + if ( !poDS->bBigEndian ) + { + for ( i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32); i++ ) + CPL_SWAP32PTR( poDS->paiTiles + i ); + } +#else + if ( poDS->bBigEndian ) + { + for ( i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32); i++ ) + CPL_SWAP32PTR( poDS->paiTiles + i ); + } +#endif + +#ifdef DEBUG + CPLDebug( "RMF", "List of block offsets/sizes:" ); + + for ( i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32); i += 2 ) + { + CPLDebug( "RMF", " %d / %d", + poDS->paiTiles[i], poDS->paiTiles[i + 1] ); + } +#endif + +/* -------------------------------------------------------------------- */ +/* Set up essential image parameters. */ +/* -------------------------------------------------------------------- */ + GDALDataType eType = GDT_Byte; + + poDS->nRasterXSize = poDS->sHeader.nWidth; + poDS->nRasterYSize = poDS->sHeader.nHeight; + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + + if ( poDS->eRMFType == RMFT_RSW ) + { + switch ( poDS->sHeader.nBitDepth ) + { + case 32: + case 24: + case 16: + poDS->nBands = 3; + break; + case 1: + case 4: + case 8: + { + // Allocate memory for colour table and read it + poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth; + if ( poDS->nColorTableSize * 4 > poDS->sHeader.nClrTblSize ) + { + CPLDebug( "RMF", + "Wrong color table size. Expected %d, got %d.", + poDS->nColorTableSize * 4, + poDS->sHeader.nClrTblSize ); + delete poDS; + return NULL; + } + poDS->pabyColorTable = + (GByte *)VSIMalloc( poDS->sHeader.nClrTblSize ); + if (poDS->pabyColorTable == NULL) + { + CPLDebug( "RMF", "Can't allocate color table." ); + delete poDS; + return NULL; + } + if ( VSIFSeekL( poDS->fp, poDS->sHeader.nClrTblOffset, + SEEK_SET ) < 0 ) + { + CPLDebug( "RMF", "Can't seek to color table location." ); + delete poDS; + return NULL; + } + if ( VSIFReadL( poDS->pabyColorTable, 1, + poDS->sHeader.nClrTblSize, poDS->fp ) + < poDS->sHeader.nClrTblSize ) + { + CPLDebug( "RMF", "Can't read color table." ); + delete poDS; + return NULL; + } + + GDALColorEntry oEntry; + poDS->poColorTable = new GDALColorTable(); + for( i = 0; i < poDS->nColorTableSize; i++ ) + { + oEntry.c1 = poDS->pabyColorTable[i * 4]; // Red + oEntry.c2 = poDS->pabyColorTable[i * 4 + 1]; // Green + oEntry.c3 = poDS->pabyColorTable[i * 4 + 2]; // Blue + oEntry.c4 = 255; // Alpha + + poDS->poColorTable->SetColorEntry( i, &oEntry ); + } + } + poDS->nBands = 1; + break; + default: + break; + } + eType = GDT_Byte; + } + else + { + poDS->nBands = 1; + if ( poDS->sHeader.nBitDepth == 8 ) + eType = GDT_Byte; + else if ( poDS->sHeader.nBitDepth == 16 ) + eType = GDT_Int16; + else if ( poDS->sHeader.nBitDepth == 32 ) + eType = GDT_Int32; + else if ( poDS->sHeader.nBitDepth == 64 ) + eType = GDT_Float64; + } + + if (poDS->sHeader.nTileWidth == 0 || + poDS->sHeader.nTileHeight == 0) + { + CPLDebug ("RMF", "Invalid tile dimension : %d x %d", + poDS->sHeader.nTileWidth, poDS->sHeader.nTileHeight); + delete poDS; + return NULL; + } + + poDS->nXTiles = ( poDS->nRasterXSize + poDS->sHeader.nTileWidth - 1 ) / + poDS->sHeader.nTileWidth; + poDS->nYTiles = ( poDS->nRasterYSize + poDS->sHeader.nTileHeight - 1 ) / + poDS->sHeader.nTileHeight; + +#ifdef DEBUG + CPLDebug( "RMF", "Image is %d tiles wide, %d tiles long", + poDS->nXTiles, poDS->nYTiles ); +#endif + +/* -------------------------------------------------------------------- */ +/* Choose compression scheme. */ +/* XXX: The DEM compression method seems to be only applicable */ +/* to Int32 data. */ +/* -------------------------------------------------------------------- */ + if ( poDS->sHeader.iCompression == RMF_COMPRESSION_LZW ) + poDS->Decompress = &LZWDecompress; + else if ( poDS->sHeader.iCompression == RMF_COMPRESSION_DEM + && eType == GDT_Int32 ) + poDS->Decompress = &DEMDecompress; + else // No compression + poDS->Decompress = NULL; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + poDS->SetBand( iBand, new RMFRasterBand( poDS, iBand, eType ) ); + +/* -------------------------------------------------------------------- */ +/* Set up projection. */ +/* */ +/* XXX: If projection value is not specified, but image still have */ +/* georeferencing information, assume Gauss-Kruger projection. */ +/* -------------------------------------------------------------------- */ + if( poDS->sHeader.iProjection > 0 || + (poDS->sHeader.dfPixelSize != 0.0 && + poDS->sHeader.dfLLX != 0.0 && + poDS->sHeader.dfLLY != 0.0) ) + { + OGRSpatialReference oSRS; + GInt32 nProj = + (poDS->sHeader.iProjection) ? poDS->sHeader.iProjection : 1L; + double padfPrjParams[8]; + + padfPrjParams[0] = poDS->sHeader.dfStdP1; + padfPrjParams[1] = poDS->sHeader.dfStdP2; + padfPrjParams[2] = poDS->sHeader.dfCenterLat; + padfPrjParams[3] = poDS->sHeader.dfCenterLong; + padfPrjParams[4] = 1.0; + padfPrjParams[5] = 0.0; + padfPrjParams[6] = 0.0; + + // XXX: Compute zone number for Gauss-Kruger (Transverse Mercator) + // projection if it is not specified. + if ( nProj == 1L && poDS->sHeader.dfCenterLong == 0.0 ) + { + if ( poDS->sExtHeader.nZone == 0 ) + { + double centerXCoord = poDS->sHeader.dfLLX + + (poDS->nRasterXSize * poDS->sHeader.dfPixelSize / 2.0); + padfPrjParams[7] = + floor((centerXCoord - 500000.0 ) / 1000000.0); + } + else + padfPrjParams[7] = poDS->sExtHeader.nZone; + } + else + padfPrjParams[7] = 0.0; + + oSRS.importFromPanorama( nProj, poDS->sExtHeader.nDatum, + poDS->sExtHeader.nEllipsoid, padfPrjParams ); + if ( poDS->pszProjection ) + CPLFree( poDS->pszProjection ); + oSRS.exportToWkt( &poDS->pszProjection ); + } + +/* -------------------------------------------------------------------- */ +/* Set up georeferencing. */ +/* -------------------------------------------------------------------- */ + if ( (poDS->eRMFType == RMFT_RSW && poDS->sHeader.iGeorefFlag) || + (poDS->eRMFType == RMFT_MTW && poDS->sHeader.dfPixelSize != 0.0) ) + { + poDS->adfGeoTransform[0] = poDS->sHeader.dfLLX; + poDS->adfGeoTransform[3] = poDS->sHeader.dfLLY + + poDS->nRasterYSize * poDS->sHeader.dfPixelSize; + poDS->adfGeoTransform[1] = poDS->sHeader.dfPixelSize; + poDS->adfGeoTransform[5] = - poDS->sHeader.dfPixelSize; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + } + +/* -------------------------------------------------------------------- */ +/* Set units. */ +/* -------------------------------------------------------------------- */ + + if ( poDS->eRMFType == RMFT_MTW ) + { + CPLFree(poDS->pszUnitType); + switch ( poDS->sHeader.iElevationUnit ) + { + case 0: + poDS->pszUnitType = CPLStrdup( RMF_UnitsM ); + break; + case 1: + poDS->pszUnitType = CPLStrdup( RMF_UnitsCM ); + break; + case 2: + poDS->pszUnitType = CPLStrdup( RMF_UnitsDM ); + break; + case 3: + poDS->pszUnitType = CPLStrdup( RMF_UnitsMM ); + break; + default: + poDS->pszUnitType = CPLStrdup( RMF_UnitsEmpty ); + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Report some other dataset related information. */ +/* -------------------------------------------------------------------- */ + + if ( poDS->eRMFType == RMFT_MTW ) + { + char szTemp[256]; + + snprintf( szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[0] ); + poDS->SetMetadataItem( "ELEVATION_MINIMUM", szTemp ); + + snprintf( szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[1] ); + poDS->SetMetadataItem( "ELEVATION_MAXIMUM", szTemp ); + + poDS->SetMetadataItem( "ELEVATION_UNITS", poDS->pszUnitType ); + + snprintf( szTemp, sizeof(szTemp), "%d", poDS->sHeader.iElevationType ); + poDS->SetMetadataItem( "ELEVATION_TYPE", szTemp ); + } + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *RMFDataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char **papszParmList ) + +{ + if ( nBands != 1 && nBands != 3 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "RMF driver doesn't support %d bands. Must be 1 or 3.\n", + nBands ); + + return NULL; + } + + if ( nBands == 1 + && eType != GDT_Byte + && eType != GDT_Int16 + && eType != GDT_Int32 + && eType != GDT_Float64 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create RMF dataset with an illegal data type (%s),\n" + "only Byte, Int16, Int32 and Float64 types supported " + "by the format for single-band images.\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + + if ( nBands == 3 && eType != GDT_Byte ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create RMF dataset with an illegal data type (%s),\n" + "only Byte type supported by the format for three-band images.\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + RMFDataset *poDS; + + poDS = new RMFDataset(); + + poDS->fp = VSIFOpenL( pszFilename, "w+b" ); + if( poDS->fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, "Unable to create file %s.\n", + pszFilename ); + delete poDS; + return NULL; + } + + poDS->pszFilename = pszFilename; + +/* -------------------------------------------------------------------- */ +/* Fill the RMFHeader */ +/* -------------------------------------------------------------------- */ + GUInt32 nTileSize, nCurPtr = 0; + GUInt32 nBlockXSize = + ( nXSize < RMF_DEFAULT_BLOCKXSIZE ) ? nXSize : RMF_DEFAULT_BLOCKXSIZE; + GUInt32 nBlockYSize = + ( nYSize < RMF_DEFAULT_BLOCKYSIZE ) ? nYSize : RMF_DEFAULT_BLOCKYSIZE; + const char *pszValue; + + if ( CSLFetchBoolean( papszParmList, "MTW", FALSE) ) + poDS->eRMFType = RMFT_MTW; + else + poDS->eRMFType = RMFT_RSW; + if ( poDS->eRMFType == RMFT_MTW ) + memcpy( poDS->sHeader.bySignature, RMF_SigMTW, RMF_SIGNATURE_SIZE ); + else + memcpy( poDS->sHeader.bySignature, RMF_SigRSW, RMF_SIGNATURE_SIZE ); + poDS->sHeader.iVersion = 0x0200; + poDS->sHeader.nOvrOffset = 0x00; + poDS->sHeader.iUserID = 0x00; + memset( poDS->sHeader.byName, 0, sizeof(poDS->sHeader.byName) ); + poDS->sHeader.nBitDepth = GDALGetDataTypeSize( eType ) * nBands; + poDS->sHeader.nHeight = nYSize; + poDS->sHeader.nWidth = nXSize; + + pszValue = CSLFetchNameValue(papszParmList,"BLOCKXSIZE"); + if( pszValue != NULL ) + nBlockXSize = atoi( pszValue ); + + pszValue = CSLFetchNameValue(papszParmList,"BLOCKYSIZE"); + if( pszValue != NULL ) + nBlockYSize = atoi( pszValue ); + + poDS->sHeader.nTileWidth = nBlockXSize; + poDS->sHeader.nTileHeight = nBlockYSize; + + poDS->nXTiles = poDS->sHeader.nXTiles = + ( nXSize + poDS->sHeader.nTileWidth - 1 ) / poDS->sHeader.nTileWidth; + poDS->nYTiles = poDS->sHeader.nYTiles = + ( nYSize + poDS->sHeader.nTileHeight - 1 ) / poDS->sHeader.nTileHeight; + poDS->sHeader.nLastTileHeight = nYSize % poDS->sHeader.nTileHeight; + if ( !poDS->sHeader.nLastTileHeight ) + poDS->sHeader.nLastTileHeight = poDS->sHeader.nTileHeight; + poDS->sHeader.nLastTileWidth = nXSize % poDS->sHeader.nTileWidth; + if ( !poDS->sHeader.nLastTileWidth ) + poDS->sHeader.nLastTileWidth = poDS->sHeader.nTileWidth; + + poDS->sHeader.nROIOffset = 0x00; + poDS->sHeader.nROISize = 0x00; + + nCurPtr += RMF_HEADER_SIZE; + + // Extended header + poDS->sHeader.nExtHdrOffset = nCurPtr; + poDS->sHeader.nExtHdrSize = RMF_EXT_HEADER_SIZE; + nCurPtr += poDS->sHeader.nExtHdrSize; + + // Color table + if ( poDS->eRMFType == RMFT_RSW && nBands == 1 ) + { + GUInt32 i; + + if ( poDS->sHeader.nBitDepth > 8 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot create color table of RSW with nBitDepth = %d. Retry with MTW ?", + poDS->sHeader.nBitDepth ); + delete poDS; + return NULL; + } + + poDS->sHeader.nClrTblOffset = nCurPtr; + poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth; + poDS->sHeader.nClrTblSize = poDS->nColorTableSize * 4; + poDS->pabyColorTable = (GByte *) VSIMalloc( poDS->sHeader.nClrTblSize ); + if (poDS->pabyColorTable == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + delete poDS; + return NULL; + } + for ( i = 0; i < poDS->nColorTableSize; i++ ) + { + poDS->pabyColorTable[i * 4] = + poDS->pabyColorTable[i * 4 + 1] = + poDS->pabyColorTable[i * 4 + 2] = (GByte) i; + poDS->pabyColorTable[i * 4 + 3] = 0; + } + nCurPtr += poDS->sHeader.nClrTblSize; + } + else + { + poDS->sHeader.nClrTblOffset = 0x00; + poDS->sHeader.nClrTblSize = 0x00; + } + + // Blocks table + poDS->sHeader.nTileTblOffset = nCurPtr; + poDS->sHeader.nTileTblSize = + poDS->sHeader.nXTiles * poDS->sHeader.nYTiles * 4 * 2; + poDS->paiTiles = (GUInt32 *)CPLCalloc( poDS->sHeader.nTileTblSize, 1 ); + nCurPtr += poDS->sHeader.nTileTblSize; + nTileSize = poDS->sHeader.nTileWidth * poDS->sHeader.nTileHeight + * GDALGetDataTypeSize( eType ) / 8; + poDS->sHeader.nSize = + poDS->paiTiles[poDS->sHeader.nTileTblSize / 4 - 2] + nTileSize; + + // Elevation units + if ( EQUAL(poDS->pszUnitType, RMF_UnitsM) ) + poDS->sHeader.iElevationUnit = 0; + else if ( EQUAL(poDS->pszUnitType, RMF_UnitsCM) ) + poDS->sHeader.iElevationUnit = 1; + else if ( EQUAL(poDS->pszUnitType, RMF_UnitsDM) ) + poDS->sHeader.iElevationUnit = 2; + else if ( EQUAL(poDS->pszUnitType, RMF_UnitsMM) ) + poDS->sHeader.iElevationUnit = 3; + else + poDS->sHeader.iElevationUnit = 0; + + poDS->sHeader.iMapType = -1; + poDS->sHeader.iProjection = -1; + poDS->sHeader.dfScale = 10000.0; + poDS->sHeader.dfResolution = 100.0; + poDS->sHeader.iCompression = 0; + poDS->sHeader.iMaskType = 0; + poDS->sHeader.iMaskStep = 0; + poDS->sHeader.iFrameFlag = 0; + poDS->sHeader.nFlagsTblOffset = 0x00; + poDS->sHeader.nFlagsTblSize = 0x00; + poDS->sHeader.nFileSize0 = 0x00; + poDS->sHeader.nFileSize1 = 0x00; + poDS->sHeader.iUnknown = 0; + poDS->sHeader.iGeorefFlag = 0; + poDS->sHeader.iInverse = 0; + memset( poDS->sHeader.abyInvisibleColors, 0, + sizeof(poDS->sHeader.abyInvisibleColors) ); + poDS->sHeader.adfElevMinMax[0] = 0.0; + poDS->sHeader.adfElevMinMax[1] = 0.0; + poDS->sHeader.dfNoData = 0.0; + poDS->sHeader.iElevationType = 0; + + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->eAccess = GA_Update; + poDS->nBands = nBands; + + poDS->WriteHeader(); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 1; iBand <= poDS->nBands; iBand++ ) + poDS->SetBand( iBand, new RMFRasterBand( poDS, iBand, eType ) ); + + return (GDALDataset *) poDS; +} + +/************************************************************************/ +/* GDALRegister_RMF() */ +/************************************************************************/ + +void GDALRegister_RMF() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "RMF" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "RMF" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Raster Matrix Format" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_rmf.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "rsw" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 Int32 Float64" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = RMFDataset::Identify; + poDriver->pfnOpen = RMFDataset::Open; + poDriver->pfnCreate = RMFDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/rmf/rmfdataset.h b/bazaar/plugin/gdal/frmts/rmf/rmfdataset.h new file mode 100644 index 000000000..35c3bebfb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rmf/rmfdataset.h @@ -0,0 +1,199 @@ +/****************************************************************************** + * $Id: rmfdataset.h 20996 2010-10-28 18:38:15Z rouault $ + * + * Project: Raster Matrix Format + * Purpose: Private class declarations for the RMF classes used to read/write + * GIS "Integratsia" raster files (also known as "Panorama" GIS). + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2007, Andrey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +#define RMF_HEADER_SIZE 320 +#define RMF_EXT_HEADER_SIZE 320 + +#define RMF_COMPRESSION_NONE 0 +#define RMF_COMPRESSION_LZW 1 +#define RMF_COMPRESSION_DEM 32 + +enum RMFType +{ + RMFT_RSW, // Raster map + RMFT_MTW // Digital elevation model +}; + +/************************************************************************/ +/* RMFHeader */ +/************************************************************************/ + +typedef struct +{ +#define RMF_SIGNATURE_SIZE 4 + char bySignature[RMF_SIGNATURE_SIZE];// "RSW" for raster + // map or "MTW" for DEM + GUInt32 iVersion; + GUInt32 nSize; // File size in bytes + GUInt32 nOvrOffset; // Offset to overview + GUInt32 iUserID; +#define RMF_NAME_SIZE 32 + GByte byName[RMF_NAME_SIZE]; + GUInt32 nBitDepth; // Number of bits per pixel + GUInt32 nHeight; // Image length + GUInt32 nWidth; // Image width + GUInt32 nXTiles; // Number of tiles in line + GUInt32 nYTiles; // Number of tiles in column + GUInt32 nTileHeight; + GUInt32 nTileWidth; + GUInt32 nLastTileHeight; + GUInt32 nLastTileWidth; + GUInt32 nROIOffset; + GUInt32 nROISize; + GUInt32 nClrTblOffset; // Position and size + GUInt32 nClrTblSize; // of the colour table + GUInt32 nTileTblOffset; // Position and size of the + GUInt32 nTileTblSize; // tile offsets/sizes table + GInt32 iMapType; + GInt32 iProjection; + double dfScale; + double dfResolution; + double dfPixelSize; + double dfLLX; + double dfLLY; + double dfStdP1; + double dfStdP2; + double dfCenterLong; + double dfCenterLat; + GByte iCompression; + GByte iMaskType; + GByte iMaskStep; + GByte iFrameFlag; + GUInt32 nFlagsTblOffset; + GUInt32 nFlagsTblSize; + GUInt32 nFileSize0; + GUInt32 nFileSize1; + GByte iUnknown; + GByte iGeorefFlag; + GByte iInverse; +#define RMF_INVISIBLE_COLORS_SIZE 32 + GByte abyInvisibleColors[RMF_INVISIBLE_COLORS_SIZE]; + double adfElevMinMax[2]; + double dfNoData; + GUInt32 iElevationUnit; + GByte iElevationType; + GUInt32 nExtHdrOffset; + GUInt32 nExtHdrSize; +} RMFHeader; + +/************************************************************************/ +/* RMFExtHeader */ +/************************************************************************/ + +typedef struct +{ + GInt32 nEllipsoid; + GInt32 nDatum; + GInt32 nZone; +} RMFExtHeader; + +/************************************************************************/ +/* RMFDataset */ +/************************************************************************/ + +class RMFDataset : public GDALDataset +{ + friend class RMFRasterBand; + + RMFHeader sHeader; + RMFExtHeader sExtHeader; + RMFType eRMFType; + GUInt32 nXTiles; + GUInt32 nYTiles; + GUInt32 *paiTiles; + + GUInt32 nColorTableSize; + GByte *pabyColorTable; + GDALColorTable *poColorTable; + double adfGeoTransform[6]; + char *pszProjection; + + char *pszUnitType; + + int bBigEndian; + int bHeaderDirty; + + const char *pszFilename; + VSILFILE *fp; + + CPLErr WriteHeader(); + static int LZWDecompress( const GByte*, GUInt32, GByte*, GUInt32 ); + static int DEMDecompress( const GByte*, GUInt32, GByte*, GUInt32 ); + int (*Decompress)( const GByte*, GUInt32, GByte*, GUInt32 ); + + public: + RMFDataset(); + ~RMFDataset(); + + static int Identify( GDALOpenInfo * poOpenInfo ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char *, int, int, int, + GDALDataType, char ** ); + virtual void FlushCache( void ); + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual CPLErr SetGeoTransform( double * ); + virtual const char *GetProjectionRef(); + virtual CPLErr SetProjection( const char * ); +}; + +/************************************************************************/ +/* RMFRasterBand */ +/************************************************************************/ + +class RMFRasterBand : public GDALRasterBand +{ + friend class RMFDataset; + + private: + + GUInt32 nBytesPerPixel; + GUInt32 nBlockSize, nBlockBytes; + GUInt32 nLastTileXBytes, nLastTileHeight; + GUInt32 nDataSize; + + CPLErr ReadBuffer( GByte *, GUInt32 ) const; + + public: + + RMFRasterBand( RMFDataset *, int, GDALDataType ); + ~RMFRasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + virtual const char *GetUnitType(); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual CPLErr SetUnitType(const char *); + virtual CPLErr SetColorTable( GDALColorTable * ); +}; + diff --git a/bazaar/plugin/gdal/frmts/rmf/rmfdem.cpp b/bazaar/plugin/gdal/frmts/rmf/rmfdem.cpp new file mode 100644 index 000000000..78779a942 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rmf/rmfdem.cpp @@ -0,0 +1,278 @@ +/****************************************************************************** + * $Id: rmflzw.cpp 11865 2007-08-09 11:53:57Z warmerdam $ + * + * Project: Raster Matrix Format + * Purpose: Implementation of the ad-hoc compression algorithm used in + * GIS "Panorama"/"Integratsia". + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2009, Andrey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" + +#include "rmfdataset.h" + +/* + * The encoded data stream is a series of records. + * + * Encoded record consist from the 1-byte record header followed by the + * encoded data block. Header specifies the number of elements in the data + * block and encoding type. Header format + * + * +---+---+---+---+---+---+---+---+ + * | type | count | + * +---+---+---+---+---+---+---+---+ + * 7 6 5 4 3 2 1 0 + * + * If count is zero then it means that there are more than 31 elements in this + * record. Read the next byte in the stream and increase its value with 32 to + * get the count. In this case maximum number of elements is 287. + * + * The "type" field specifies encoding type. It can be either difference + * between the previous and the next data value (for the first element the + * previous value is zero) or out-of-range codes. + * + * In case of "out of range" or "zero difference" values there are no more + * elements in record after the header. Otherwise read as much encoded + * elements as count specifies. + */ + +// Encoding types +#define TYPE_OUT 0x00 // Value is out of range +#define TYPE_ZERO 0x20 // Zero difference +#define TYPE_INT4 0x40 // Difference is 4-bit wide +#define TYPE_INT8 0x60 // Difference is 8-bit wide +#define TYPE_INT12 0x80 // Difference is 12-bit wide +#define TYPE_INT16 0xA0 // Difference is 16-bit wide +#define TYPE_INT24 0xC0 // Difference is 24-bit wide +#define TYPE_INT32 0xE0 // Difference is 32-bit wide + +// Encoding ranges +#define RANGE_INT4 0x00000007L // 4-bit +#define RANGE_INT12 0x000007FFL // 12-bit +#define RANGE_INT24 0x007FFFFFL // 24-bit + +// Out of range codes +#define OUT_INT4 ((GInt32)0xFFFFFFF8) +#define OUT_INT8 ((GInt32)0xFFFFFF80) +#define OUT_INT12 ((GInt32)0xFFFFF800) +#define OUT_INT16 ((GInt32)0xFFFF8000) +#define OUT_INT24 ((GInt32)0xFF800000) +#define OUT_INT32 ((GInt32)0x80000000) + +// Inversion masks +#define INV_INT4 0xFFFFFFF0L +#define INV_INT12 0xFFFFF000L +#define INV_INT24 0xFF000000L + + +/************************************************************************/ +/* DEMDecompress() */ +/************************************************************************/ + +int RMFDataset::DEMDecompress( const GByte* pabyIn, GUInt32 nSizeIn, + GByte* pabyOut, GUInt32 nSizeOut ) +{ + GUInt32 nCount; // Number of encoded data elements to read + char* pabyTempIn; + GInt32* paiOut; + GInt32 nType; // The encoding type + GInt32 iPrev = 0; // The last data value decoded + GInt32 nCode; + + if ( pabyIn == 0 || + pabyOut == 0 || + nSizeOut < nSizeIn || + nSizeIn < 2 ) + return 0; + + pabyTempIn = (char*)pabyIn; + paiOut = (GInt32*)pabyOut; + nSizeOut /= sizeof(GInt32); + + while ( nSizeIn > 0 ) + { + // Read number of codes in the record and encoding type + nCount = *pabyTempIn & 0x1F; + nType = *pabyTempIn++ & 0xE0; + nSizeIn--; + if ( nCount == 0 ) + { + if ( nSizeIn == 0 ) + break; + nCount = 32 + *((unsigned char*)pabyTempIn++); + nSizeIn--; + } + + switch (nType) + { + case TYPE_ZERO: + if ( nSizeOut < nCount ) + break; + nSizeOut -= nCount; + while ( nCount-- > 0 ) + *paiOut++ = iPrev; + break; + + case TYPE_OUT: + if ( nSizeOut < nCount ) + break; + nSizeOut -= nCount; + while ( nCount-- > 0 ) + *paiOut++ = OUT_INT32; + break; + + case TYPE_INT4: + if ( nSizeIn < nCount / 2 ) + break; + if ( nSizeOut < nCount ) + break; + nSizeIn -= nCount / 2; + nSizeOut -= nCount; + while ( nCount-- > 0 ) + { + nCode = (*pabyTempIn) & 0x0F; + if ( nCode > RANGE_INT4 ) + nCode |= INV_INT4; + *paiOut++ = ( nCode == OUT_INT4 ) ? + OUT_INT32 : iPrev += nCode; + + if ( nCount-- == 0 ) + { + pabyTempIn++; + nSizeIn--; + break; + } + + nCode = ((*pabyTempIn++)>>4) & 0x0F; + if ( nCode > RANGE_INT4 ) + nCode |= INV_INT4; + *paiOut++ = ( nCode == OUT_INT4 ) ? + OUT_INT32 : iPrev += nCode; + } + break; + + case TYPE_INT8: + if ( nSizeIn < nCount ) + break; + if ( nSizeOut < nCount ) + break; + nSizeIn -= nCount; + nSizeOut -= nCount; + while ( nCount-- > 0 ) + { + *paiOut++ = ( (nCode = *pabyTempIn++) == OUT_INT8 ) ? + OUT_INT32 : iPrev += nCode; + } + break; + + case TYPE_INT12: + if ( nSizeIn < 3 * nCount / 2 ) + break; + if ( nSizeOut < nCount ) + break; + nSizeIn -= 3 * nCount / 2; + nSizeOut -= nCount; + + while ( nCount-- > 0 ) + { + nCode = *((GInt16*)pabyTempIn++) & 0x0FFF; + if ( nCode > RANGE_INT12 ) + nCode |= INV_INT12; + *paiOut++ = ( nCode == OUT_INT12 ) ? + OUT_INT32 : iPrev += nCode; + + if ( nCount-- == 0 ) + { + pabyTempIn++; + nSizeIn--; + break; + } + + nCode = ( (*(GInt16*)pabyTempIn) >> 4 ) & 0x0FFF; + pabyTempIn += 2; + if ( nCode > RANGE_INT12 ) + nCode |= INV_INT12; + *paiOut++ = ( nCode == OUT_INT12 ) ? + OUT_INT32 : iPrev += nCode; + } + break; + + case TYPE_INT16: + if ( nSizeIn < 2 * nCount ) + break; + if ( nSizeOut < nCount ) + break; + nSizeIn -= 2 * nCount; + nSizeOut -= nCount; + + while ( nCount-- > 0 ) + { + nCode = *((GInt16*)pabyTempIn); + pabyTempIn += 2; + *paiOut++ = ( nCode == OUT_INT16 ) ? + OUT_INT32 : iPrev += nCode; + } + break; + + case TYPE_INT24: + if ( nSizeIn < 3 * nCount ) + break; + if ( nSizeOut < nCount ) + break; + nSizeIn -= 3 * nCount; + nSizeOut -= nCount; + + while ( nCount-- > 0 ) + { + nCode =*((GInt32 *)pabyTempIn) & 0x00FFFFFF; + pabyTempIn += 3; + if ( nCode > RANGE_INT24 ) + nCode |= INV_INT24; + *paiOut++ = ( nCode == OUT_INT24 ) ? + OUT_INT32 : iPrev += nCode; + } + break; + + case TYPE_INT32: + if ( nSizeIn < 4 * nCount ) + break; + if ( nSizeOut < nCount ) + break; + nSizeIn -= 4 * nCount; + nSizeOut -= nCount; + + while ( nCount-- > 0 ) + { + nCode = *(GInt32 *)pabyTempIn; + pabyTempIn += 4; + *paiOut++ = ( nCode == OUT_INT32 ) ? + OUT_INT32 : iPrev += nCode; + } + break; + } + } + + return ((GByte*)paiOut - pabyOut); +} + diff --git a/bazaar/plugin/gdal/frmts/rmf/rmflzw.cpp b/bazaar/plugin/gdal/frmts/rmf/rmflzw.cpp new file mode 100644 index 000000000..e471e6fd6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rmf/rmflzw.cpp @@ -0,0 +1,241 @@ +/****************************************************************************** + * $Id: rmflzw.cpp 11865 2007-08-09 11:53:57Z warmerdam $ + * + * Project: Raster Matrix Format + * Purpose: Implementation of the LZW compression algorithm as used in + * GIS "Panorama"/"Integratsia" raster files. Based on implementation + * of Kent Williams, but heavily modified over it. The key point + * in the initial implementation is a hashing algorithm. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2007, Andrey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * COPYRIGHT NOTICE FROM THE INITIAL IMPLEMENTATION: + * + * The programs LZWCOM and LZWUNC, both in binary executable and source forms, + * are in the public domain. No warranty is given or implied, and no + * liability will be assumed by the author. + * + * Everyone on earth is hereby given permission to use, copy, distribute, + * change, mangle, destroy or otherwise employ these programs, provided they + * hurt no one but themselves in the process. + * + * Kent Williams + * Norand Inc. + * 550 2nd St S.E. + * Cedar Rapids, Iowa 52401 + * (319) 369-3131 + ****************************************************************************/ + +#include "cpl_conv.h" + +#include "rmfdataset.h" + +// Code marks that there is no predecessor in the string +#define NO_PRED 0xFFFF + +// We are using 12-bit codes in this particular implementation +#define TABSIZE 4096U +#define STACKSIZE TABSIZE + +/************************************************************************/ +/* LZWStringTab */ +/************************************************************************/ + +typedef struct +{ + int bUsed; + GUInt32 iNext; // hi bit is 'used' flag + GUInt32 iPredecessor; // 12 bit code + GByte iFollower; +} LZWStringTab; + +/************************************************************************/ +/* LZWUpdateTab() */ +/************************************************************************/ + +static void LZWUpdateTab(LZWStringTab *poCodeTab, GUInt32 iPred, char bFoll) +{ + GUInt32 nNext; + +/* -------------------------------------------------------------------- */ +/* Hash uses the 'mid-square' algorithm. I.E. for a hash val of n bits */ +/* hash = middle binary digits of (key * key). Upon collision, hash */ +/* searches down linked list of keys that hashed to that key already. */ +/* It will NOT notice if the table is full. This must be handled */ +/* elsewhere */ +/* -------------------------------------------------------------------- */ + GUInt32 nLocal = (iPred + bFoll) | 0x0800; + nLocal = (nLocal*nLocal >> 6) & 0x0FFF; // middle 12 bits of result + + // If string is not used + if (poCodeTab[nLocal].bUsed == FALSE) + nNext = nLocal; + else + { + // If collision has occured + while ( (nNext = poCodeTab[nLocal].iNext) != 0 ) + nLocal = nNext; + + // Search for free entry from nLocal + 101 + nNext = (nLocal + 101) & 0x0FFF; + while ( poCodeTab[nNext].bUsed ) + { + if ( ++nNext >= TABSIZE ) + nNext = 0; + } + + // Put new tempnext into last element in collision list + poCodeTab[nLocal].iNext = nNext; + } + + poCodeTab[nNext].bUsed = TRUE; + poCodeTab[nNext].iNext = 0; + poCodeTab[nNext].iPredecessor = iPred; + poCodeTab[nNext].iFollower = bFoll; +} + +/************************************************************************/ +/* LZWDecompress() */ +/************************************************************************/ + +int RMFDataset::LZWDecompress( const GByte* pabyIn, GUInt32 nSizeIn, + GByte* pabyOut, GUInt32 nSizeOut ) +{ + GUInt32 nCount = TABSIZE - 256; + GUInt32 iCode, iOldCode, iInCode; + GByte iFinChar, bLastChar=FALSE; + LZWStringTab *poCodeTab; + int bBitsleft; + + if ( pabyIn == 0 || + pabyOut == 0 || + nSizeOut < nSizeIn || + nSizeIn < 2 ) + return 0; + + // Allocate space for the new table and pre-fill it + poCodeTab = (LZWStringTab *)CPLMalloc( TABSIZE * sizeof(LZWStringTab) ); + if ( !poCodeTab ) + return 0; + memset( poCodeTab, 0, TABSIZE * sizeof(LZWStringTab) ); + for ( iCode = 0; iCode < 256; iCode++ ) + LZWUpdateTab( poCodeTab, NO_PRED, (char)iCode ); + + // The first code is always known + iCode = (*pabyIn++ << 4) & 0xFF0; nSizeIn--; + iCode += (*pabyIn >> 4) & 0x00F; + iOldCode = iCode; + bBitsleft = TRUE; + + *pabyOut++ = iFinChar = poCodeTab[iCode].iFollower; nSizeOut--; + + // Decompress the input buffer + while ( nSizeIn > 0 ) + { + int bNewCode; + GUInt32 nStackCount = 0; + GByte abyStack[STACKSIZE]; + GByte *pabyTail = abyStack + STACKSIZE; + + // Fetch 12-bit code from input stream + if ( bBitsleft ) + { + iCode = ((*pabyIn++ & 0x0F) << 8) & 0xF00; nSizeIn--; + if ( nSizeIn <= 0 ) + break; + iCode += *pabyIn++; nSizeIn--; + bBitsleft = FALSE; + } + else + { + iCode = (*pabyIn++ << 4) & 0xFF0; nSizeIn--; + if ( nSizeIn <= 0 ) + break; + iCode += (*pabyIn >> 4) & 0x00F; + bBitsleft = TRUE; + } + iInCode = iCode; + + // Do we have unknown code? + if ( poCodeTab[iCode].bUsed ) + bNewCode = FALSE; + else + { + iCode = iOldCode; + bLastChar = iFinChar; + bNewCode = TRUE; + } + + while ( poCodeTab[iCode].iPredecessor != NO_PRED ) + { + // Stack overrun + if ( nStackCount >= STACKSIZE ) + goto bad; + // Put the decoded character into stack + *(--pabyTail) = poCodeTab[iCode].iFollower; nStackCount++; + iCode = poCodeTab[iCode].iPredecessor; + } + + if ( !nSizeOut ) + goto bad; + // The first character + *pabyOut++ = iFinChar = poCodeTab[iCode].iFollower; nSizeOut--; + + // Output buffer overrun + if ( nStackCount > nSizeOut ) + goto bad; + + // Now copy the stack contents into output buffer. Our stack was + // filled in reverse order, so no need in character reordering + memcpy( pabyOut, pabyTail, nStackCount ); + nSizeOut -= nStackCount; + pabyOut += nStackCount; + + // If code isn't known + if ( bNewCode ) + { + // Output buffer overrun + if ( !nSizeOut ) + goto bad; + *pabyOut++ = iFinChar = bLastChar; // the follower char of last + nSizeOut--; + } + + if ( nCount > 0 ) + { + nCount--; + // Add code to the table + LZWUpdateTab( poCodeTab, iOldCode, iFinChar ); + } + + iOldCode = iInCode; + } + + CPLFree( poCodeTab ); + return 1; + +bad: + CPLFree( poCodeTab ); + return 0; +} + diff --git a/bazaar/plugin/gdal/frmts/rs2/GNUmakefile b/bazaar/plugin/gdal/frmts/rs2/GNUmakefile new file mode 100644 index 000000000..1c42e7000 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rs2/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = rs2dataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/rs2/frmt_rs2.html b/bazaar/plugin/gdal/frmts/rs2/frmt_rs2.html new file mode 100644 index 000000000..a5413d9df --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rs2/frmt_rs2.html @@ -0,0 +1,52 @@ + + +RS2 -- RadarSat 2 XML Product + + + + +

    RS2 -- RadarSat 2 XML Product

    + +This driver will read some RadarSat 2 XML polarimetric products. In +particular complex products, and 16bit magnitude detected products.

    + +The RadarSat 2 XML products are distributed with a primary XML file called +product.xml, and a set of supporting XML data files with the actual imagery +stored in TIFF files. The RS2 driver will be used if the product.xml or +the containing directory is selected, and it can treat all the imagery as +one consistent dataset.

    + +The complex products use "32bit void typed" TIFF files which are not +meaningfully readable normally. The RS2 driver takes care of converting +this into useful CInt16 format internally.

    + +The RS2 driver also reads geolocation tiepoints from the product.xml file +and represents them as GCPs on the dataset.

    + +It is very likely that RadarSat International will be distributing +other sorts of datasets in this format; however, at this time this driver +only supports specific RadarSat 2 polarimetric products. All other will +be ignored, or result in various runtime errors. It is hoped that this +driver can be generalized when other product samples become available.

    + +

    Data Calibration

    +If you wish to have GDAL apply a particular calibration LUT to the data when you open it, you have to open the appropriate subdatasets. The following subdatasets exist within the SUBDATASET domain for RS2 products: +
      +
    • uncalibrated - open with RADARSAT_2_CALIB:UNCALIB: prepended to filename +
    • beta0 - open with RADARSAT_2_CALIB:BETA0: prepended to filename +
    • sigma0 - open with RADARSAT_2_CALIB:SIGMA0: prepended to filename +
    • gamma - open with RADARSAT_2_CALIB:GAMMA: prepended to filename +
    + +

    Note that geocoded (SPG/SSG) products do not have this functionality available. Also be aware that the LUTs must be in the product directory where specified in the product.xml, otherwise loading the product with the calibration LUT applied will fail. + +

    One caveat worth noting is that the RADARSAT-2 driver will supply the calibrated data as GDT_Float32 or GDT_CFloat32 depending on the type of calibration selected. The uncalibrated data is provided as GDT_Int16/GDT_Byte/GDT_CInt16, also depending on the type of product selected. + +

    See Also:

    + +

      +
    • RadarSat document RN-RP-51-27. +
    + + + diff --git a/bazaar/plugin/gdal/frmts/rs2/makefile.vc b/bazaar/plugin/gdal/frmts/rs2/makefile.vc new file mode 100644 index 000000000..ce12ca693 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rs2/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = rs2dataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/rs2/rs2dataset.cpp b/bazaar/plugin/gdal/frmts/rs2/rs2dataset.cpp new file mode 100644 index 000000000..525ad1f76 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/rs2/rs2dataset.cpp @@ -0,0 +1,1534 @@ +/****************************************************************************** + * $Id: rs2dataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: Polarimetric Workstation + * Purpose: Radarsat 2 - XML Products (product.xml) driver + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_minixml.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: rs2dataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_RS2(void); +CPL_C_END + +typedef enum eCalibration_t { + Sigma0 = 0, + Gamma, + Beta0, + Uncalib, + None +} eCalibration; + +/*** Function to test for valid LUT files ***/ +static bool IsValidXMLFile( const char *pszPath, const char *pszLut) +{ + /* Return true for valid xml file, false otherwise */ + + CPLXMLNode *psLut; + char *pszLutFile; + bool retVal = false; + + pszLutFile = VSIStrdup(CPLFormFilename(pszPath, pszLut, NULL)); + + psLut = CPLParseXMLFile(pszLutFile); + + if (psLut != NULL) + { + CPLDestroyXMLNode(psLut); + retVal = true; + } + + CPLFree(pszLutFile); + + return retVal; +} + +/************************************************************************/ +/* ==================================================================== */ +/* RS2Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +class RS2Dataset : public GDALPamDataset +{ + CPLXMLNode *psProduct; + + int nGCPCount; + GDAL_GCP *pasGCPList; + char *pszGCPProjection; + char **papszSubDatasets; + char *pszProjection; + double adfGeoTransform[6]; + bool bHaveGeoTransform; + + char **papszExtraFiles; + + protected: + virtual int CloseDependentDatasets(); + + public: + RS2Dataset(); + ~RS2Dataset(); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * ); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual char **GetFileList(void); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + + CPLXMLNode *GetProduct() { return psProduct; } +}; + +/************************************************************************/ +/* ==================================================================== */ +/* RS2RasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class RS2RasterBand : public GDALPamRasterBand +{ + GDALDataset *poBandFile; + + public: + RS2RasterBand( RS2Dataset *poDSIn, + GDALDataType eDataTypeIn, + const char *pszPole, + GDALDataset *poBandFile ); + virtual ~RS2RasterBand(); + + virtual CPLErr IReadBlock( int, int, void * ); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + + +/************************************************************************/ +/* RS2RasterBand */ +/************************************************************************/ + +RS2RasterBand::RS2RasterBand( RS2Dataset *poDSIn, + GDALDataType eDataTypeIn, + const char *pszPole, + GDALDataset *poBandFileIn ) + +{ + GDALRasterBand *poSrcBand; + + poDS = poDSIn; + poBandFile = poBandFileIn; + + poSrcBand = poBandFile->GetRasterBand( 1 ); + + poSrcBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + eDataType = eDataTypeIn; + + if( *pszPole != '\0' ) + SetMetadataItem( "POLARIMETRIC_INTERP", pszPole ); +} + +/************************************************************************/ +/* RSRasterBand() */ +/************************************************************************/ + +RS2RasterBand::~RS2RasterBand() + +{ + if( poBandFile != NULL ) + GDALClose( (GDALRasterBandH) poBandFile ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr RS2RasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + int nRequestYSize; + int nRequestXSize; + +/* -------------------------------------------------------------------- */ +/* If the last strip is partial, we need to avoid */ +/* over-requesting. We also need to initialize the extra part */ +/* of the block to zero. */ +/* -------------------------------------------------------------------- */ + if( (nBlockYOff + 1) * nBlockYSize > nRasterYSize ) + { + nRequestYSize = nRasterYSize - nBlockYOff * nBlockYSize; + memset( pImage, 0, (GDALGetDataTypeSize( eDataType ) / 8) * + nBlockXSize * nBlockYSize ); + } + else + { + nRequestYSize = nBlockYSize; + } + +/*-------------------------------------------------------------------- */ +/* If the input imagery is tiled, also need to avoid over- */ +/* requesting in the X-direction. */ +/* ------------------------------------------------------------------- */ + if( (nBlockXOff + 1) * nBlockXSize > nRasterXSize ) + { + nRequestXSize = nRasterXSize - nBlockXOff * nBlockXSize; + memset( pImage, 0, (GDALGetDataTypeSize( eDataType ) / 8) * + nBlockXSize * nBlockYSize ); + } + else + { + nRequestXSize = nBlockXSize; + } + if( eDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 2 ) + return + poBandFile->RasterIO( GF_Read, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + nRequestXSize, nRequestYSize, + pImage, nRequestXSize, nRequestYSize, + GDT_Int16, + 2, NULL, 4, nBlockXSize * 4, 2, NULL ); + +/* -------------------------------------------------------------------- */ +/* File has one sample marked as sample format void, a 32bits. */ +/* -------------------------------------------------------------------- */ + else if( eDataType == GDT_CInt16 && poBandFile->GetRasterCount() == 1 ) + { + CPLErr eErr; + + eErr = + poBandFile->RasterIO( GF_Read, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + nRequestXSize, nRequestYSize, + pImage, nRequestXSize, nRequestYSize, + GDT_UInt32, + 1, NULL, 4, nBlockXSize * 4, 0, NULL ); + +#ifdef CPL_LSB + /* First, undo the 32bit swap. */ + GDALSwapWords( pImage, 4, nBlockXSize * nBlockYSize, 4 ); + + /* Then apply 16 bit swap. */ + GDALSwapWords( pImage, 2, nBlockXSize * nBlockYSize * 2, 2 ); +#endif + + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* The 16bit case is straight forward. The underlying file */ +/* looks like a 16bit unsigned data too. */ +/* -------------------------------------------------------------------- */ + else if( eDataType == GDT_UInt16 ) + return + poBandFile->RasterIO( GF_Read, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + nRequestXSize, nRequestYSize, + pImage, nRequestXSize, nRequestYSize, + GDT_UInt16, + 1, NULL, 2, nBlockXSize * 2, 0, NULL ); + else if ( eDataType == GDT_Byte ) + /* Ticket #2104: Support for ScanSAR products */ + return + poBandFile->RasterIO( GF_Read, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + nRequestXSize, nRequestYSize, + pImage, nRequestXSize, nRequestYSize, + GDT_Byte, + 1, NULL, 1, nBlockXSize, 0, NULL ); + else + { + CPLAssert( FALSE ); + return CE_Failure; + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* RS2CalibRasterBand */ +/* ==================================================================== */ +/************************************************************************/ +/* Returns data that has been calibrated to sigma nought, gamma */ +/* or beta nought. */ +/************************************************************************/ + +class RS2CalibRasterBand : public GDALPamRasterBand { +private: + eCalibration m_eCalib; + GDALDataset *m_poBandDataset; + GDALDataType m_eType; /* data type of data being ingested */ + float *m_nfTable; + int m_nTableSize; + float m_nfOffset; + char *m_pszLUTFile; + + void ReadLUT(); +public: + RS2CalibRasterBand( RS2Dataset *poDataset, const char *pszPolarization, + GDALDataType eType, GDALDataset *poBandDataset, eCalibration eCalib, + const char *pszLUT); + ~RS2CalibRasterBand(); + + CPLErr IReadBlock( int nBlockXOff, int nBlockYOff, void *pImage); +}; + +/************************************************************************/ +/* ReadLUT() */ +/************************************************************************/ +/* Read the provided LUT in to m_ndTable */ +/************************************************************************/ +void RS2CalibRasterBand::ReadLUT() { + CPLXMLNode *psLUT; + char **papszLUTList; + + psLUT = CPLParseXMLFile(m_pszLUTFile); + + this->m_nfOffset = (float) CPLAtof(CPLGetXMLValue(psLUT, "=lut.offset", + "0.0")); + + papszLUTList = CSLTokenizeString2( CPLGetXMLValue(psLUT, + "=lut.gains", ""), " ", CSLT_HONOURSTRINGS); + + this->m_nTableSize = CSLCount(papszLUTList); + + this->m_nfTable = (float *)CPLMalloc(sizeof(float) * this->m_nTableSize); + + for (int i = 0; i < this->m_nTableSize; i++) { + m_nfTable[i] = (float) CPLAtof(papszLUTList[i]); + } + + CPLDestroyXMLNode(psLUT); + + CSLDestroy(papszLUTList); +} + +/************************************************************************/ +/* RS2CalibRasterBand() */ +/************************************************************************/ + +RS2CalibRasterBand::RS2CalibRasterBand( RS2Dataset *poDataset, + const char *pszPolarization, GDALDataType eType, GDALDataset *poBandDataset, + eCalibration eCalib, const char *pszLUT ) +{ + this->poDS = poDataset; + + if (*pszPolarization != '\0') { + this->SetMetadataItem( "POLARIMETRIC_INTERP", pszPolarization ); + } + + this->m_eType = eType; + this->m_poBandDataset = poBandDataset; + this->m_eCalib = eCalib; + this->m_pszLUTFile = VSIStrdup(pszLUT); + + this->m_nfTable = NULL; + this->m_nTableSize = 0; + + if (eType == GDT_CInt16) + this->eDataType = GDT_CFloat32; + else + this->eDataType = GDT_Float32; + + GDALRasterBand *poRasterBand = poBandDataset->GetRasterBand( 1 ); + poRasterBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + + this->ReadLUT(); +} + +/************************************************************************/ +/* ~RS2CalibRasterBand() */ +/************************************************************************/ + +RS2CalibRasterBand::~RS2CalibRasterBand() { + if (this->m_nfTable != NULL) + CPLFree(this->m_nfTable); + + if (this->m_pszLUTFile != NULL) + CPLFree(this->m_pszLUTFile); + + if (this->m_poBandDataset != NULL) + GDALClose( this->m_poBandDataset ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr RS2CalibRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void *pImage ) +{ + CPLErr eErr; + int nRequestYSize; + +/* -------------------------------------------------------------------- */ +/* If the last strip is partial, we need to avoid */ +/* over-requesting. We also need to initialize the extra part */ +/* of the block to zero. */ +/* -------------------------------------------------------------------- */ + if( (nBlockYOff + 1) * nBlockYSize > nRasterYSize ) + { + nRequestYSize = nRasterYSize - nBlockYOff * nBlockYSize; + memset( pImage, 0, (GDALGetDataTypeSize( eDataType ) / 8) * + nBlockXSize * nBlockYSize ); + } + else + { + nRequestYSize = nBlockYSize; + } + + if (this->m_eType == GDT_CInt16) { + GInt16 *pnImageTmp; + /* read in complex values */ + pnImageTmp = (GInt16 *)CPLMalloc(2 * nBlockXSize * nBlockYSize * + GDALGetDataTypeSize( GDT_Int16 ) / 8); + if (m_poBandDataset->GetRasterCount() == 2) { + eErr = m_poBandDataset->RasterIO( GF_Read, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + nBlockXSize, nRequestYSize, + pnImageTmp, nBlockXSize, nRequestYSize, + GDT_Int16, + 2, NULL, 4, nBlockXSize * 4, 2, NULL ); + + } + else { + eErr = + m_poBandDataset->RasterIO( GF_Read, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + nBlockXSize, nRequestYSize, + pnImageTmp, nBlockXSize, nRequestYSize, + GDT_UInt32, + 1, NULL, 4, nBlockXSize * 4, 0, NULL ); + +#ifdef CPL_LSB + /* First, undo the 32bit swap. */ + GDALSwapWords( pImage, 4, nBlockXSize * nBlockYSize, 4 ); + + /* Then apply 16 bit swap. */ + GDALSwapWords( pImage, 2, nBlockXSize * nBlockYSize * 2, 2 ); +#endif + } + + /* calibrate the complex values */ + for (int i = 0; i < nBlockYSize; i++) { + for (int j = 0; j < nBlockXSize; j++) { + /* calculate pixel offset in memory*/ + int nPixOff = (2 * (i * nBlockXSize)) + (j * 2); + + ((float *)pImage)[nPixOff] = (float)pnImageTmp[nPixOff]/ + (m_nfTable[nBlockXOff + j]); + ((float *)pImage)[nPixOff + 1] = + (float)pnImageTmp[nPixOff + 1]/(m_nfTable[nBlockXOff + j]); + } + } + CPLFree(pnImageTmp); + } + else if (this->m_eType == GDT_UInt16) { + GUInt16 *pnImageTmp; + /* read in detected values */ + pnImageTmp = (GUInt16 *)CPLMalloc(nBlockXSize * nBlockYSize * + GDALGetDataTypeSize( GDT_UInt16 ) / 8); + eErr = m_poBandDataset->RasterIO( GF_Read, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + nBlockXSize, nRequestYSize, + pnImageTmp, nBlockXSize, nRequestYSize, + GDT_UInt16, + 1, NULL, 2, nBlockXSize * 2, 0, NULL ); + + /* iterate over detected values */ + for (int i = 0; i < nBlockYSize; i++) { + for (int j = 0; j < nBlockXSize; j++) { + int nPixOff = (i * nBlockXSize) + j; + + ((float *)pImage)[nPixOff] = (((float)pnImageTmp[nPixOff] * + (float)pnImageTmp[nPixOff]) + + this->m_nfOffset)/m_nfTable[nBlockXOff + j]; + } + } + CPLFree(pnImageTmp); + } /* Ticket #2104: Support for ScanSAR products */ + else if (this->m_eType == GDT_Byte) { + GByte *pnImageTmp; + pnImageTmp = (GByte *)CPLMalloc(nBlockXSize * nBlockYSize * + GDALGetDataTypeSize( GDT_Byte ) / 8); + eErr = m_poBandDataset->RasterIO( GF_Read, + nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, + nBlockXSize, nRequestYSize, + pnImageTmp, nBlockXSize, nRequestYSize, + GDT_Byte, + 1, NULL, 1, 1, 0, NULL); + + /* iterate over detected values */ + for (int i = 0; i < nBlockYSize; i++) { + for (int j = 0; j < nBlockXSize; j++) { + int nPixOff = (i * nBlockXSize) + j; + + ((float *)pImage)[nPixOff] = ((pnImageTmp[nPixOff] * + pnImageTmp[nPixOff]) + + this->m_nfOffset)/m_nfTable[nBlockXOff + j]; + } + } + CPLFree(pnImageTmp); + } + else { + CPLAssert( FALSE ); + return CE_Failure; + } + return eErr; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* RS2Dataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* RS2Dataset() */ +/************************************************************************/ + +RS2Dataset::RS2Dataset() +{ + psProduct = NULL; + + nGCPCount = 0; + pasGCPList = NULL; + pszGCPProjection = CPLStrdup(""); + pszProjection = CPLStrdup(""); + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + bHaveGeoTransform = FALSE; + + papszSubDatasets = NULL; + papszExtraFiles = NULL; +} + +/************************************************************************/ +/* ~RS2Dataset() */ +/************************************************************************/ + +RS2Dataset::~RS2Dataset() + +{ + FlushCache(); + + CPLDestroyXMLNode( psProduct ); + CPLFree( pszProjection ); + + CPLFree( pszGCPProjection ); + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + CloseDependentDatasets(); + + CSLDestroy( papszSubDatasets ); + CSLDestroy( papszExtraFiles ); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int RS2Dataset::CloseDependentDatasets() +{ + int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets(); + + if (nBands != 0) + bHasDroppedRef = TRUE; + + for( int iBand = 0; iBand < nBands; iBand++ ) + { + delete papoBands[iBand]; + } + nBands = 0; + + return bHasDroppedRef; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **RS2Dataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + + papszFileList = CSLInsertStrings( papszFileList, -1, papszExtraFiles ); + + return papszFileList; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int RS2Dataset::Identify( GDALOpenInfo *poOpenInfo ) +{ + + /* Check for the case where we're trying to read the calibrated data: */ + if (EQUALN("RADARSAT_2_CALIB:",poOpenInfo->pszFilename,17)) { + return 1; + } + + /* Check for directory access when there is a product.xml file in the + directory. */ + if( poOpenInfo->bIsDirectory ) + { + VSIStatBufL sStat; + + CPLString osMDFilename = + CPLFormCIFilename( poOpenInfo->pszFilename, "product.xml", NULL ); + + if( VSIStatL( osMDFilename, &sStat ) == 0 ) + return TRUE; + else + return FALSE; + } + + /* otherwise, do our normal stuff */ + if( strlen(poOpenInfo->pszFilename) < 11 + || !EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename)-11, + "product.xml") ) + return 0; + + if( poOpenInfo->nHeaderBytes < 100 ) + return 0; + + if( strstr((const char *) poOpenInfo->pabyHeader, "/rs2" ) == NULL + || strstr((const char *) poOpenInfo->pabyHeader, "pszFilename; + eCalibration eCalib = None; + + if (EQUALN("RADARSAT_2_CALIB:",pszFilename,17)) { + pszFilename += 17; + + if (EQUALN("BETA0",pszFilename,5)) + eCalib = Beta0; + else if (EQUALN("SIGMA0",pszFilename,6)) + eCalib = Sigma0; + else if (EQUALN("GAMMA", pszFilename,5)) + eCalib = Gamma; + else if (EQUALN("UNCALIB", pszFilename,7)) + eCalib = Uncalib; + else + eCalib = None; + + /* advance the pointer to the actual filename */ + while ( *pszFilename != '\0' && *pszFilename != ':' ) + pszFilename++; + + if (*pszFilename == ':') + pszFilename++; + + //need to redo the directory check: + //the GDALOpenInfo check would have failed because of the calibration string on the filename + VSIStatBufL sStat; + if( VSIStatL( pszFilename, &sStat ) == 0 ) + poOpenInfo->bIsDirectory = VSI_ISDIR( sStat.st_mode ); + } + + if( poOpenInfo->bIsDirectory ) + { + osMDFilename = + CPLFormCIFilename( pszFilename, "product.xml", NULL ); + } + else + osMDFilename = pszFilename; + +/* -------------------------------------------------------------------- */ +/* Ingest the Product.xml file. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psProduct, *psImageAttributes, *psImageGenerationParameters; + + psProduct = CPLParseXMLFile( osMDFilename ); + if( psProduct == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLDestroyXMLNode( psProduct ); + CPLError( CE_Failure, CPLE_NotSupported, + "The RS2 driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + psImageAttributes = CPLGetXMLNode(psProduct, "=product.imageAttributes" ); + if( psImageAttributes == NULL ) + { + CPLDestroyXMLNode( psProduct ); + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to find in document." ); + return NULL; + } + + psImageGenerationParameters = CPLGetXMLNode( psProduct, + "=product.imageGenerationParameters" ); + if (psImageGenerationParameters == NULL) { + CPLDestroyXMLNode( psProduct ); + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to find in document." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + RS2Dataset *poDS = new RS2Dataset(); + + poDS->psProduct = psProduct; + +/* -------------------------------------------------------------------- */ +/* Get overall image information. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = + atoi(CPLGetXMLValue( psImageAttributes, + "rasterAttributes.numberOfSamplesPerLine", + "-1" )); + poDS->nRasterYSize = + atoi(CPLGetXMLValue( psImageAttributes, + "rasterAttributes.numberofLines", + "-1" )); + if (poDS->nRasterXSize <= 1 || poDS->nRasterYSize <= 1) { + CPLError( CE_Failure, CPLE_OpenFailed, + "Non-sane raster dimensions provided in product.xml. If this is " + "a valid RADARSAT-2 scene, please contact your data provider for " + "a corrected dataset." ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Check product type, as to determine if there are LUTs for */ +/* calibration purposes. */ +/* -------------------------------------------------------------------- */ + + char *pszBeta0LUT = NULL; + char *pszGammaLUT = NULL; + char *pszSigma0LUT = NULL; + int bCanCalib = 0; + + const char *pszProductType = CPLGetXMLValue( psImageGenerationParameters, + "generalProcessingInformation.productType", + "UNK" ); + + poDS->SetMetadataItem("PRODUCT_TYPE", pszProductType); + + /* the following cases can be assumed to have no LUTs, as per + * RN-RP-51-2713, but also common sense + */ + if (!(EQUALN(pszProductType, "UNK", 3) || + EQUALN(pszProductType, "SSG", 3) || + EQUALN(pszProductType, "SPG", 3))) + { + bCanCalib = 1; + } + +/* -------------------------------------------------------------------- */ +/* Get dataType (so we can recognise complex data), and the */ +/* bitsPerSample. */ +/* -------------------------------------------------------------------- */ + GDALDataType eDataType; + + const char *pszDataType = + CPLGetXMLValue( psImageAttributes, "rasterAttributes.dataType", + "" ); + int nBitsPerSample = + atoi( CPLGetXMLValue( psImageAttributes, + "rasterAttributes.bitsPerSample", "" ) ); + + if( nBitsPerSample == 16 && EQUAL(pszDataType,"Complex") ) + eDataType = GDT_CInt16; + else if( nBitsPerSample == 16 && EQUALN(pszDataType,"Mag",3) ) + eDataType = GDT_UInt16; + else if( nBitsPerSample == 8 && EQUALN(pszDataType,"Mag",3) ) + eDataType = GDT_Byte; + else + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "dataType=%s, bitsPerSample=%d: not a supported configuration.", + pszDataType, nBitsPerSample ); + return NULL; + } + + /* while we're at it, extract the pixel spacing information */ + const char *pszPixelSpacing = CPLGetXMLValue( psImageAttributes, + "rasterAttributes.sampledPixelSpacing", "UNK" ); + poDS->SetMetadataItem( "PIXEL_SPACING", pszPixelSpacing ); + + const char *pszLineSpacing = CPLGetXMLValue( psImageAttributes, + "rasterAttributes.sampledLineSpacing", "UNK" ); + poDS->SetMetadataItem( "LINE_SPACING", pszLineSpacing ); + +/* -------------------------------------------------------------------- */ +/* Open each of the data files as a complex band. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psNode; + char *pszPath = CPLStrdup(CPLGetPath( osMDFilename )); + char *pszBuf; + int nFLen = strlen(osMDFilename); + + for( psNode = psImageAttributes->psChild; + psNode != NULL; + psNode = psNode->psNext ) + { + const char *pszBasename; + if( psNode->eType != CXT_Element + || !(EQUAL(psNode->pszValue,"fullResolutionImageData") + || EQUAL(psNode->pszValue,"lookupTable")) ) + continue; + + if ( EQUAL(psNode->pszValue, "lookupTable") && bCanCalib ) { + /* Determine which incidence angle correction this is */ + const char *pszLUTType = CPLGetXMLValue( psNode, + "incidenceAngleCorrection", "" ); + const char *pszLUTFile = CPLGetXMLValue( psNode, "", "" ); + CPLString osLUTFilePath = CPLFormFilename( pszPath, pszLUTFile, + NULL ); + + if (EQUAL(pszLUTType, "")) + continue; + else if (EQUAL(pszLUTType, "Beta Nought") && + IsValidXMLFile(pszPath,pszLUTFile)) + { + poDS->papszExtraFiles = + CSLAddString( poDS->papszExtraFiles, osLUTFilePath ); + + pszBuf = (char *)CPLMalloc(nFLen + 27); + pszBeta0LUT = VSIStrdup( pszLUTFile ); + poDS->SetMetadataItem( "BETA_NOUGHT_LUT", pszLUTFile ); + + sprintf(pszBuf, "RADARSAT_2_CALIB:BETA0:%s", + osMDFilename.c_str() ); + poDS->papszSubDatasets = CSLSetNameValue( + poDS->papszSubDatasets, "SUBDATASET_3_NAME", pszBuf ); + poDS->papszSubDatasets = CSLSetNameValue( + poDS->papszSubDatasets, "SUBDATASET_3_DESC", + "Beta Nought calibrated" ); + CPLFree(pszBuf); + } + else if (EQUAL(pszLUTType, "Sigma Nought") && + IsValidXMLFile(pszPath,pszLUTFile)) + { + poDS->papszExtraFiles = + CSLAddString( poDS->papszExtraFiles, osLUTFilePath ); + + pszBuf = (char *)CPLMalloc(nFLen + 27); + pszSigma0LUT = VSIStrdup( pszLUTFile ); + poDS->SetMetadataItem( "SIGMA_NOUGHT_LUT", pszLUTFile ); + + sprintf(pszBuf, "RADARSAT_2_CALIB:SIGMA0:%s", + osMDFilename.c_str() ); + poDS->papszSubDatasets = CSLSetNameValue( + poDS->papszSubDatasets, "SUBDATASET_2_NAME", pszBuf ); + poDS->papszSubDatasets = CSLSetNameValue( + poDS->papszSubDatasets, "SUBDATASET_2_DESC", + "Sigma Nought calibrated" ); + CPLFree(pszBuf); + } + else if (EQUAL(pszLUTType, "Gamma") && + IsValidXMLFile(pszPath,pszLUTFile)) + { + poDS->papszExtraFiles = + CSLAddString( poDS->papszExtraFiles, osLUTFilePath ); + + pszBuf = (char *)CPLMalloc(nFLen + 27); + pszGammaLUT = VSIStrdup( pszLUTFile ); + poDS->SetMetadataItem( "GAMMA_LUT", pszLUTFile ); + sprintf(pszBuf, "RADARSAT_2_CALIB:GAMMA:%s", + osMDFilename.c_str()); + poDS->papszSubDatasets = CSLSetNameValue( + poDS->papszSubDatasets, "SUBDATASET_4_NAME", pszBuf ); + poDS->papszSubDatasets = CSLSetNameValue( + poDS->papszSubDatasets, "SUBDATASET_4_DESC", + "Gamma calibrated" ); + CPLFree(pszBuf); + } + continue; + } + +/* -------------------------------------------------------------------- */ +/* Fetch filename. */ +/* -------------------------------------------------------------------- */ + pszBasename = CPLGetXMLValue( psNode, "", "" ); + if( *pszBasename == '\0' ) + continue; + +/* -------------------------------------------------------------------- */ +/* Form full filename (path of product.xml + basename). */ +/* -------------------------------------------------------------------- */ + char *pszFullname = + CPLStrdup(CPLFormFilename( pszPath, pszBasename, NULL )); + +/* -------------------------------------------------------------------- */ +/* Try and open the file. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poBandFile; + + poBandFile = (GDALDataset *) GDALOpen( pszFullname, GA_ReadOnly ); + if( poBandFile == NULL ) + { + CPLFree(pszFullname); + continue; + } + if (poBandFile->GetRasterCount() == 0) + { + GDALClose( (GDALRasterBandH) poBandFile ); + CPLFree(pszFullname); + continue; + } + + poDS->papszExtraFiles = CSLAddString( poDS->papszExtraFiles, + pszFullname ); + +/* -------------------------------------------------------------------- */ +/* Create the band. */ +/* -------------------------------------------------------------------- */ + if (eCalib == None || eCalib == Uncalib) { + RS2RasterBand *poBand; + poBand = new RS2RasterBand( poDS, eDataType, + CPLGetXMLValue( psNode, "pole", "" ), + poBandFile ); + + poDS->SetBand( poDS->GetRasterCount() + 1, poBand ); + } + else { + const char *pszLUT; + switch (eCalib) { + case Sigma0: + pszLUT = pszSigma0LUT; + break; + case Beta0: + pszLUT = pszBeta0LUT; + break; + case Gamma: + pszLUT = pszGammaLUT; + break; + default: + /* we should bomb gracefully... */ + pszLUT = pszSigma0LUT; + } + RS2CalibRasterBand *poBand; + poBand = new RS2CalibRasterBand( poDS, CPLGetXMLValue( psNode, + "pole", "" ), eDataType, poBandFile, eCalib, + CPLFormFilename(pszPath, pszLUT, NULL) ); + poDS->SetBand( poDS->GetRasterCount() + 1, poBand ); + } + + CPLFree( pszFullname ); + } + + if (poDS->papszSubDatasets != NULL && eCalib == None) { + char *pszBuf; + pszBuf = (char *)CPLMalloc(nFLen + 28); + sprintf(pszBuf, "RADARSAT_2_CALIB:UNCALIB:%s", + osMDFilename.c_str() ); + poDS->papszSubDatasets = CSLSetNameValue( poDS->papszSubDatasets, + "SUBDATASET_1_NAME", pszBuf ); + poDS->papszSubDatasets = CSLSetNameValue( poDS->papszSubDatasets, + "SUBDATASET_1_DESC", "Uncalibrated digital numbers" ); + CPLFree(pszBuf); + } + else if (poDS->papszSubDatasets != NULL) { + CSLDestroy( poDS->papszSubDatasets ); + poDS->papszSubDatasets = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Set the appropriate MATRIX_REPRESENTATION. */ +/* -------------------------------------------------------------------- */ + + if ( poDS->GetRasterCount() == 4 && (eDataType == GDT_CInt16 || + eDataType == GDT_CFloat32) ) + { + poDS->SetMetadataItem( "MATRIX_REPRESENTATION", "SCATTERING" ); + } + +/* -------------------------------------------------------------------- */ +/* Collect a few useful metadata items */ +/* -------------------------------------------------------------------- */ + + CPLXMLNode *psSourceAttrs = CPLGetXMLNode( psProduct, + "=product.sourceAttributes"); + const char *pszItem; + + pszItem = CPLGetXMLValue( psSourceAttrs, + "satellite", "" ); + poDS->SetMetadataItem( "SATELLITE_IDENTIFIER", pszItem ); + + pszItem = CPLGetXMLValue( psSourceAttrs, + "sensor", "" ); + poDS->SetMetadataItem( "SENSOR_IDENTIFIER", pszItem ); + + if (psSourceAttrs != NULL) { + /* Get beam mode mnemonic */ + pszItem = CPLGetXMLValue( psSourceAttrs, "beamModeMnemonic", "UNK" ); + poDS->SetMetadataItem( "BEAM_MODE", pszItem ); + pszItem = CPLGetXMLValue( psSourceAttrs, "rawDataStartTime", "UNK" ); + poDS->SetMetadataItem( "ACQUISITION_START_TIME", pszItem ); + + pszItem = CPLGetXMLValue( psSourceAttrs, "inputDatasetFacilityId", "UNK" ); + poDS->SetMetadataItem( "FACILITY_IDENTIFIER", pszItem ); + + pszItem = CPLGetXMLValue( psSourceAttrs, + "orbitAndAttitude.orbitInformation.passDirection", "UNK" ); + poDS->SetMetadataItem( "ORBIT_DIRECTION", pszItem ); + pszItem = CPLGetXMLValue( psSourceAttrs, + "orbitAndAttitude.orbitInformation.orbitDataSource", "UNK" ); + poDS->SetMetadataItem( "ORBIT_DATA_SOURCE", pszItem ); + pszItem = CPLGetXMLValue( psSourceAttrs, + "orbitAndAttitude.orbitInformation.orbitDataFile", "UNK" ); + poDS->SetMetadataItem( "ORBIT_DATA_FILE", pszItem ); + + } + + CPLXMLNode *psSarProcessingInformation = + CPLGetXMLNode( psProduct, "=product.imageGenerationParameters" ); + + if (psSarProcessingInformation != NULL) { + /* Get incidence angle information */ + pszItem = CPLGetXMLValue( psSarProcessingInformation, + "sarProcessingInformation.incidenceAngleNearRange", "UNK" ); + poDS->SetMetadataItem( "NEAR_RANGE_INCIDENCE_ANGLE", pszItem ); + + pszItem = CPLGetXMLValue( psSarProcessingInformation, + "sarProcessingInformation.incidenceAngleFarRange", "UNK" ); + poDS->SetMetadataItem( "FAR_RANGE_INCIDENCE_ANGLE", pszItem ); + + pszItem = CPLGetXMLValue( psSarProcessingInformation, + "sarProcessingInformation.slantRangeNearEdge", "UNK" ); + poDS->SetMetadataItem( "SLANT_RANGE_NEAR_EDGE", pszItem ); + + pszItem = CPLGetXMLValue( psSarProcessingInformation, + "sarProcessingInformation.zeroDopplerTimeFirstLine", "UNK" ); + poDS->SetMetadataItem( "FIRST_LINE_TIME", pszItem ); + + pszItem = CPLGetXMLValue( psSarProcessingInformation, + "sarProcessingInformation.zeroDopplerTimeLastLine", "UNK" ); + poDS->SetMetadataItem( "LAST_LINE_TIME", pszItem ); + + pszItem = CPLGetXMLValue( psSarProcessingInformation, + "generalProcessingInformation.productType", "UNK" ); + poDS->SetMetadataItem( "PRODUCT_TYPE", pszItem ); + + pszItem = CPLGetXMLValue( psSarProcessingInformation, + "generalProcessingInformation.processingFacility", "UNK" ); + poDS->SetMetadataItem( "PROCESSING_FACILITY", pszItem ); + + pszItem = CPLGetXMLValue( psSarProcessingInformation, + "generalProcessingInformation.processingTime", "UNK" ); + poDS->SetMetadataItem( "PROCESSING_TIME", pszItem ); + + } + + + +/*--------------------------------------------------------------------- */ +/* Collect Map projection/Geotransform information, if present */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psMapProjection = + CPLGetXMLNode( psImageAttributes, + "geographicInformation.mapProjection" ); + + if ( psMapProjection != NULL ) { + CPLXMLNode *psPos = + CPLGetXMLNode( psMapProjection, "positioningInformation" ); + + pszItem = CPLGetXMLValue( psMapProjection, + "mapProjectionDescriptor", "UNK" ); + poDS->SetMetadataItem( "MAP_PROJECTION_DESCRIPTOR", pszItem ); + + pszItem = CPLGetXMLValue( psMapProjection, + "mapProjectionOrientation", "UNK" ); + poDS->SetMetadataItem( "MAP_PROJECTION_ORIENTATION", pszItem ); + + pszItem = CPLGetXMLValue( psMapProjection, + "resamplingKernel", "UNK" ); + poDS->SetMetadataItem( "RESAMPLING_KERNEL", pszItem ); + + pszItem = CPLGetXMLValue( psMapProjection, + "satelliteHeading", "UNK" ); + poDS->SetMetadataItem( "SATELLITE_HEADING", pszItem ); + + if (psPos != NULL) { + double testx, testy, br_x, br_y, tl_x, tl_y, tr_x, tr_y, + bl_x, bl_y; + + tl_x = CPLStrtod(CPLGetXMLValue( psPos, + "upperLeftCorner.mapCoordinate.easting", "0.0" ), NULL); + tl_y = CPLStrtod(CPLGetXMLValue( psPos, + "upperLeftCorner.mapCoordinate.northing", "0.0" ), NULL); + bl_x = CPLStrtod(CPLGetXMLValue( psPos, + "lowerLeftCorner.mapCoordinate.easting", "0.0" ), NULL); + bl_y = CPLStrtod(CPLGetXMLValue( psPos, + "lowerLeftCorner.mapCoordinate.northing", "0.0" ), NULL); + tr_x = CPLStrtod(CPLGetXMLValue( psPos, + "upperRightCorner.mapCoordinate.easting", "0.0" ), NULL); + tr_y = CPLStrtod(CPLGetXMLValue( psPos, + "upperRightCorner.mapCoordinate.northing", "0.0" ), NULL); + poDS->adfGeoTransform[1] = (tr_x - tl_x)/(poDS->nRasterXSize - 1); + poDS->adfGeoTransform[4] = (tr_y - tl_y)/(poDS->nRasterXSize - 1); + poDS->adfGeoTransform[2] = (bl_x - tl_x)/(poDS->nRasterYSize - 1); + poDS->adfGeoTransform[5] = (bl_y - tl_y)/(poDS->nRasterYSize - 1); + poDS->adfGeoTransform[0] = (tl_x - 0.5*poDS->adfGeoTransform[1] + - 0.5*poDS->adfGeoTransform[2]); + poDS->adfGeoTransform[3] = (tl_y - 0.5*poDS->adfGeoTransform[4] + - 0.5*poDS->adfGeoTransform[5]); + + /* Use bottom right pixel to test geotransform */ + br_x = CPLStrtod(CPLGetXMLValue( psPos, + "lowerRightCorner.mapCoordinate.easting", "0.0" ), NULL); + br_y = CPLStrtod(CPLGetXMLValue( psPos, + "lowerRightCorner.mapCoordinate.northing", "0.0" ), NULL); + testx = poDS->adfGeoTransform[0] + poDS->adfGeoTransform[1] * + (poDS->nRasterXSize - 0.5) + poDS->adfGeoTransform[2] * + (poDS->nRasterYSize - 0.5); + testy = poDS->adfGeoTransform[3] + poDS->adfGeoTransform[4] * + (poDS->nRasterXSize - 0.5) + poDS->adfGeoTransform[5] * + (poDS->nRasterYSize - 0.5); + + /* Give 1/4 pixel numerical error leeway in calculating location + based on affine transform */ + if ( (fabs(testx - br_x) > + fabs(0.25*(poDS->adfGeoTransform[1]+poDS->adfGeoTransform[2]))) + || (fabs(testy - br_y) > fabs(0.25*(poDS->adfGeoTransform[4] + + poDS->adfGeoTransform[5]))) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unexpected error in calculating affine transform: " + "corner coordinates inconsistent."); + } + else + poDS->bHaveGeoTransform = TRUE; + + } + + } + + +/* -------------------------------------------------------------------- */ +/* Collect Projection String Information */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psEllipsoid = + CPLGetXMLNode( psImageAttributes, + "geographicInformation.referenceEllipsoidParameters" ); + + if ( psEllipsoid != NULL ) { + const char *pszEllipsoidName; + double minor_axis, major_axis, inv_flattening; + OGRSpatialReference oLL, oPrj; + + pszEllipsoidName = CPLGetXMLValue( psEllipsoid, "ellipsoidName", "" ); + minor_axis = CPLAtof(CPLGetXMLValue( psEllipsoid, "semiMinorAxis", + "0.0" )); + major_axis = CPLAtof(CPLGetXMLValue( psEllipsoid, "semiMajorAxis", + "0.0" )); + + if ( EQUAL(pszEllipsoidName, "") || ( minor_axis == 0.0 ) || + ( major_axis == 0.0 ) ) + { + CPLError(CE_Warning,CPLE_AppDefined,"Warning- incomplete" + " ellipsoid information. Using wgs-84 parameters.\n"); + oLL.SetWellKnownGeogCS( "WGS84" ); + oPrj.SetWellKnownGeogCS( "WGS84" ); + } + else if ( EQUAL( pszEllipsoidName, "WGS84" ) ) { + oLL.SetWellKnownGeogCS( "WGS84" ); + oPrj.SetWellKnownGeogCS( "WGS84" ); + } + else { + inv_flattening = major_axis/(major_axis - minor_axis); + oLL.SetGeogCS( "","",pszEllipsoidName, major_axis, + inv_flattening); + oPrj.SetGeogCS( "","",pszEllipsoidName, major_axis, + inv_flattening); + } + + if ( psMapProjection != NULL ) { + const char *pszProj = CPLGetXMLValue( + psMapProjection, "mapProjectionDescriptor", "" ); + bool bUseProjInfo = FALSE; + + CPLXMLNode *psUtmParams = + CPLGetXMLNode( psMapProjection, + "utmProjectionParameters" ); + + CPLXMLNode *psNspParams = + CPLGetXMLNode( psMapProjection, + "nspProjectionParameters" ); + + if ((psUtmParams != NULL) && poDS->bHaveGeoTransform ) { + const char *pszHemisphere; + int utmZone; + /* double origEasting, origNorthing; */ + bool bNorth = TRUE; + + utmZone = atoi(CPLGetXMLValue( psUtmParams, "utmZone", "" )); + pszHemisphere = CPLGetXMLValue( psUtmParams, + "hemisphere", "" ); +#if 0 + origEasting = CPLStrtod(CPLGetXMLValue( psUtmParams, + "mapOriginFalseEasting", "0.0" ), NULL); + origNorthing = CPLStrtod(CPLGetXMLValue( psUtmParams, + "mapOriginFalseNorthing", "0.0" ), NULL); +#endif + if ( EQUALN(pszHemisphere,"southern",8) ) + bNorth = FALSE; + + if (EQUALN(pszProj,"UTM",3)) { + oPrj.SetUTM(utmZone, bNorth); + bUseProjInfo = TRUE; + } + } + else if ((psNspParams != NULL) && poDS->bHaveGeoTransform) { + double origEasting, origNorthing, copLong, copLat, sP1, sP2; + + origEasting = CPLStrtod(CPLGetXMLValue( psNspParams, + "mapOriginFalseEasting", "0.0" ), NULL); + origNorthing = CPLStrtod(CPLGetXMLValue( psNspParams, + "mapOriginFalseNorthing", "0.0" ), NULL); + copLong = CPLStrtod(CPLGetXMLValue( psNspParams, + "centerOfProjectionLongitude", "0.0" ), NULL); + copLat = CPLStrtod(CPLGetXMLValue( psNspParams, + "centerOfProjectionLatitude", "0.0" ), NULL); + sP1 = CPLStrtod(CPLGetXMLValue( psNspParams, + "standardParallels1", "0.0" ), NULL); + sP2 = CPLStrtod(CPLGetXMLValue( psNspParams, + "standardParallels2", "0.0" ), NULL); + + if (EQUALN(pszProj,"ARC",3)) { + /* Albers Conical Equal Area */ + oPrj.SetACEA(sP1, sP2, copLat, copLong, origEasting, + origNorthing); + bUseProjInfo = TRUE; + } + else if (EQUALN(pszProj,"LCC",3)) { + /* Lambert Conformal Conic */ + oPrj.SetLCC(sP1, sP2, copLat, copLong, origEasting, + origNorthing); + bUseProjInfo = TRUE; + } + else if (EQUALN(pszProj,"STPL",3)) { + /* State Plate + ASSUMPTIONS: "zone" in product.xml matches USGS + definition as expected by ogr spatial reference; NAD83 + zones (versus NAD27) are assumed. */ + + int nSPZone = atoi(CPLGetXMLValue( psNspParams, + "zone", "1" )); + + oPrj.SetStatePlane( nSPZone, TRUE, NULL, 0.0 ); + bUseProjInfo = TRUE; + } + } + + if (bUseProjInfo) { + CPLFree( poDS->pszProjection ); + poDS->pszProjection = NULL; + oPrj.exportToWkt( &(poDS->pszProjection) ); + } + else { + CPLError(CE_Warning,CPLE_AppDefined,"Unable to interpret " + "projection information; check mapProjection info in " + "product.xml!"); + } + } + + CPLFree( poDS->pszGCPProjection ); + poDS->pszGCPProjection = NULL; + oLL.exportToWkt( &(poDS->pszGCPProjection) ); + + } + +/* -------------------------------------------------------------------- */ +/* Collect GCPs. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psGeoGrid = + CPLGetXMLNode( psImageAttributes, + "geographicInformation.geolocationGrid" ); + + if( psGeoGrid != NULL ) { + /* count GCPs */ + poDS->nGCPCount = 0; + + for( psNode = psGeoGrid->psChild; psNode != NULL; + psNode = psNode->psNext ) + { + if( EQUAL(psNode->pszValue,"imageTiePoint") ) + poDS->nGCPCount++ ; + } + + poDS->pasGCPList = (GDAL_GCP *) + CPLCalloc(sizeof(GDAL_GCP),poDS->nGCPCount); + + poDS->nGCPCount = 0; + + for( psNode = psGeoGrid->psChild; psNode != NULL; + psNode = psNode->psNext ) + { + char szID[32]; + GDAL_GCP *psGCP = poDS->pasGCPList + poDS->nGCPCount; + + if( !EQUAL(psNode->pszValue,"imageTiePoint") ) + continue; + + poDS->nGCPCount++ ; + + sprintf( szID, "%d", poDS->nGCPCount ); + psGCP->pszId = CPLStrdup( szID ); + psGCP->pszInfo = CPLStrdup(""); + psGCP->dfGCPPixel = + CPLAtof(CPLGetXMLValue(psNode,"imageCoordinate.pixel","0")); + psGCP->dfGCPLine = + CPLAtof(CPLGetXMLValue(psNode,"imageCoordinate.line","0")); + psGCP->dfGCPX = + CPLAtof(CPLGetXMLValue(psNode,"geodeticCoordinate.longitude","")); + psGCP->dfGCPY = + CPLAtof(CPLGetXMLValue(psNode,"geodeticCoordinate.latitude","")); + psGCP->dfGCPZ = + CPLAtof(CPLGetXMLValue(psNode,"geodeticCoordinate.height","")); + } + } + + CPLFree( pszPath ); + if (pszBeta0LUT) CPLFree(pszBeta0LUT); + if (pszSigma0LUT) CPLFree(pszSigma0LUT); + if (pszGammaLUT) CPLFree(pszGammaLUT); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + CPLString osDescription; + + switch (eCalib) { + case Sigma0: + osDescription.Printf( "RADARSAT_2_CALIB:SIGMA0:%s", + osMDFilename.c_str() ); + break; + case Beta0: + osDescription.Printf( "RADARSAT_2_CALIB:BETA0:%s", + osMDFilename.c_str()); + break; + case Gamma: + osDescription.Printf( "RADARSAT_2_CALIB:GAMMA0:%s", + osMDFilename.c_str() ); + break; + case Uncalib: + osDescription.Printf( "RADARSAT_2_CALIB:UNCALIB:%s", + osMDFilename.c_str() ); + break; + default: + osDescription = osMDFilename; + } + + if( eCalib != None ) + poDS->papszExtraFiles = + CSLAddString( poDS->papszExtraFiles, osMDFilename ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( osDescription ); + + poDS->SetPhysicalFilename( osMDFilename ); + poDS->SetSubdatasetName( osDescription ); + + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" ); + + return( poDS ); +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int RS2Dataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *RS2Dataset::GetGCPProjection() + +{ + return pszGCPProjection; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *RS2Dataset::GetGCPs() + +{ + return pasGCPList; +} + + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *RS2Dataset::GetProjectionRef() + +{ + return( pszProjection ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr RS2Dataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + + if (bHaveGeoTransform) + return( CE_None ); + + return( CE_Failure ); +} + + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **RS2Dataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), + TRUE, + "SUBDATASETS", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **RS2Dataset::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain != NULL && EQUALN( pszDomain, "SUBDATASETS", 11 ) && + papszSubDatasets != NULL) + return papszSubDatasets; + else + return GDALDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* GDALRegister_RS2() */ +/************************************************************************/ + +void GDALRegister_RS2() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "RS2" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "RS2" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "RadarSat 2 XML Product" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_rs2.html" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + + poDriver->pfnOpen = RS2Dataset::Open; + poDriver->pfnIdentify = RS2Dataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/saga/GNUmakefile b/bazaar/plugin/gdal/frmts/saga/GNUmakefile new file mode 100644 index 000000000..985aa3450 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/saga/GNUmakefile @@ -0,0 +1,11 @@ + +include ../../GDALmake.opt + +OBJ = sagadataset.o + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/saga/makefile.vc b/bazaar/plugin/gdal/frmts/saga/makefile.vc new file mode 100644 index 000000000..528e22acc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/saga/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = sagadataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/saga/sagadataset.cpp b/bazaar/plugin/gdal/frmts/saga/sagadataset.cpp new file mode 100644 index 000000000..26db27370 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/saga/sagadataset.cpp @@ -0,0 +1,1104 @@ +/****************************************************************************** + * $Id: sagadataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * Project: SAGA GIS Binary Driver + * Purpose: Implements the SAGA GIS Binary Grid Format. + * Author: Volker Wichmann, wichmann@laserdata.at + * (Based on gsbgdataset.cpp by Kevin Locke and Frank Warmerdam) + * + ****************************************************************************** + * Copyright (c) 2009, Volker Wichmann + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" + +#include +#include +#include + +#include "gdal_pam.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: sagadataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#ifndef INT_MAX +# define INT_MAX 2147483647 +#endif /* INT_MAX */ + +/* NODATA Values */ +//#define SG_NODATA_GDT_Bit 0.0 +#define SG_NODATA_GDT_Byte 255 +#define SG_NODATA_GDT_UInt16 65535 +#define SG_NODATA_GDT_Int16 -32767 +#define SG_NODATA_GDT_UInt32 4294967295U +#define SG_NODATA_GDT_Int32 -2147483647 +#define SG_NODATA_GDT_Float32 -99999.0 +#define SG_NODATA_GDT_Float64 -99999.0 + + +CPL_C_START +void GDALRegister_SAGA(void); +CPL_C_END + + +/************************************************************************/ +/* ==================================================================== */ +/* SAGADataset */ +/* ==================================================================== */ +/************************************************************************/ + +class SAGARasterBand; + +class SAGADataset : public GDALPamDataset +{ + friend class SAGARasterBand; + + static CPLErr WriteHeader( CPLString osHDRFilename, GDALDataType eType, + int nXSize, int nYSize, + double dfMinX, double dfMinY, + double dfCellsize, double dfNoData, + double dfZFactor, bool bTopToBottom ); + VSILFILE *fp; + char *pszProjection; + + public: + SAGADataset(); + ~SAGADataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char **papszParmList ); + static GDALDataset *CreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressData ); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + virtual char **GetFileList(); + + CPLErr GetGeoTransform( double *padfGeoTransform ); + CPLErr SetGeoTransform( double *padfGeoTransform ); +}; + + +/************************************************************************/ +/* ==================================================================== */ +/* SAGARasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class SAGARasterBand : public GDALPamRasterBand +{ + friend class SAGADataset; + + int m_Cols; + int m_Rows; + double m_Xmin; + double m_Ymin; + double m_Cellsize; + double m_NoData; + int m_ByteOrder; + int m_nBits; + + void SetDataType( GDALDataType eType ); + void SwapBuffer(void* pImage); +public: + + SAGARasterBand( SAGADataset *, int ); + ~SAGARasterBand(); + + CPLErr IReadBlock( int, int, void * ); + CPLErr IWriteBlock( int, int, void * ); + + double GetNoDataValue( int *pbSuccess = NULL ); +}; + +/************************************************************************/ +/* SAGARasterBand() */ +/************************************************************************/ + +SAGARasterBand::SAGARasterBand( SAGADataset *poDS, int nBand ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Float32; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; +} + +/************************************************************************/ +/* ~SAGARasterBand() */ +/************************************************************************/ + +SAGARasterBand::~SAGARasterBand( ) + +{ +} + +/************************************************************************/ +/* SetDataType() */ +/************************************************************************/ + +void SAGARasterBand::SetDataType( GDALDataType eType ) + +{ + eDataType = eType; + return; +} + +/************************************************************************/ +/* SwapBuffer() */ +/************************************************************************/ + +void SAGARasterBand::SwapBuffer(void* pImage) +{ + +#ifdef CPL_LSB + int bSwap = ( m_ByteOrder == 1); +#else + int bSwap = ( m_ByteOrder == 0); +#endif + + if (bSwap) + { + if ( m_nBits == 16 ) + { + short* pImage16 = (short*) pImage; + for( int iPixel=0; iPixel nRasterYSize - 1 || nBlockXOff != 0 ) + return CE_Failure; + + SAGADataset *poGDS = dynamic_cast(poDS); + vsi_l_offset offset = (vsi_l_offset) (m_nBits / 8) * nRasterXSize * (nRasterYSize - nBlockYOff - 1); + + if( VSIFSeekL( poGDS->fp, offset, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to beginning of grid row.\n" ); + return CE_Failure; + } + if( VSIFReadL( pImage, m_nBits / 8, nBlockXSize, + poGDS->fp ) != static_cast(nBlockXSize) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read block from grid file.\n" ); + return CE_Failure; + } + + SwapBuffer(pImage); + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr SAGARasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void *pImage ) + +{ + if( eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Unable to write block, dataset opened read only.\n" ); + return CE_Failure; + } + + if( nBlockYOff < 0 || nBlockYOff > nRasterYSize - 1 || nBlockXOff != 0 ) + return CE_Failure; + + vsi_l_offset offset = (vsi_l_offset) (m_nBits / 8) * nRasterXSize * (nRasterYSize - nBlockYOff - 1); + SAGADataset *poGDS = dynamic_cast(poDS); + assert( poGDS != NULL ); + + if( VSIFSeekL( poGDS->fp, offset, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to seek to beginning of grid row.\n" ); + return CE_Failure; + } + + SwapBuffer(pImage); + + int bSuccess = ( VSIFWriteL( pImage, m_nBits / 8, nBlockXSize, + poGDS->fp ) == static_cast(nBlockXSize) ); + + SwapBuffer(pImage); + + if (!bSuccess) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write block to grid file.\n" ); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double SAGARasterBand::GetNoDataValue( int * pbSuccess ) +{ + if( pbSuccess ) + *pbSuccess = TRUE; + + return m_NoData; +} + +/************************************************************************/ +/* ==================================================================== */ +/* SAGADataset */ +/* ==================================================================== */ +/************************************************************************/ + + +SAGADataset::SAGADataset() + +{ + pszProjection = CPLStrdup(""); +} + +SAGADataset::~SAGADataset() + +{ + CPLFree( pszProjection ); + FlushCache(); + if( fp != NULL ) + VSIFCloseL( fp ); +} + + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char** SAGADataset::GetFileList() +{ + CPLString osPath = CPLGetPath( GetDescription() ); + CPLString osName = CPLGetBasename( GetDescription() ); + VSIStatBufL sStatBuf; + + char **papszFileList = NULL; + + // Main data file, etc. + papszFileList = GDALPamDataset::GetFileList(); + + // Header file. + CPLString osFilename = CPLFormCIFilename( osPath, osName, ".sgrd" ); + papszFileList = CSLAddString( papszFileList, osFilename ); + + // projections file. + osFilename = CPLFormCIFilename( osPath, osName, "prj" ); + if( VSIStatExL( osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + papszFileList = CSLAddString( papszFileList, osFilename ); + + return papszFileList; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *SAGADataset::GetProjectionRef() + +{ + if (pszProjection && strlen(pszProjection) > 0) + return pszProjection; + + return GDALPamDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr SAGADataset::SetProjection( const char *pszSRS ) + +{ +/* -------------------------------------------------------------------- */ +/* Reset coordinate system on the dataset. */ +/* -------------------------------------------------------------------- */ + CPLFree( pszProjection ); + pszProjection = CPLStrdup( pszSRS ); + + if( strlen(pszSRS) == 0 ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Convert to ESRI WKT. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS( pszSRS ); + char *pszESRI_SRS = NULL; + + oSRS.morphToESRI(); + oSRS.exportToWkt( &pszESRI_SRS ); + +/* -------------------------------------------------------------------- */ +/* Write to .prj file. */ +/* -------------------------------------------------------------------- */ + CPLString osPrjFilename = CPLResetExtension( GetDescription(), "prj" ); + VSILFILE *fp; + + fp = VSIFOpenL( osPrjFilename.c_str(), "wt" ); + if( fp != NULL ) + { + VSIFWriteL( pszESRI_SRS, 1, strlen(pszESRI_SRS), fp ); + VSIFWriteL( (void *) "\n", 1, 1, fp ); + VSIFCloseL( fp ); + } + + CPLFree( pszESRI_SRS ); + + return CE_None; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *SAGADataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + /* -------------------------------------------------------------------- */ + /* We assume the user is pointing to the binary (ie. .sdat) file. */ + /* -------------------------------------------------------------------- */ + if( !EQUAL(CPLGetExtension( poOpenInfo->pszFilename ), "sdat")) + { + return NULL; + } + + CPLString osPath = CPLGetPath( poOpenInfo->pszFilename ); + CPLString osName = CPLGetBasename( poOpenInfo->pszFilename ); + CPLString osHDRFilename; + + osHDRFilename = CPLFormCIFilename( osPath, osName, ".sgrd" ); + + + VSILFILE *fp; + + fp = VSIFOpenL( osHDRFilename, "r" ); + + if( fp == NULL ) + { + return NULL; + } + + /* -------------------------------------------------------------------- */ + /* Is this file a SAGA header file? Read a few lines of text */ + /* searching for something starting with nrows or ncols. */ + /* -------------------------------------------------------------------- */ + const char *pszLine; + int nRows = -1, nCols = -1; + double dXmin = 0.0, dYmin = 0.0, dCellsize = 0.0, dNoData = 0.0, dZFactor = 0.0; + int nLineCount = 0; + char szDataFormat[20] = "DOUBLE"; + char szByteOrderBig[10] = "FALSE"; + char szTopToBottom[10] = "FALSE"; + char **papszHDR = NULL; + + + while( (pszLine = CPLReadLineL( fp )) != NULL ) + { + char **papszTokens; + + nLineCount++; + + if( nLineCount > 50 || strlen(pszLine) > 1000 ) + break; + + papszHDR = CSLAddString( papszHDR, pszLine ); + + papszTokens = CSLTokenizeStringComplex( pszLine, " =", TRUE, FALSE ); + if( CSLCount( papszTokens ) < 2 ) + { + CSLDestroy( papszTokens ); + continue; + } + + if( EQUALN(papszTokens[0],"CELLCOUNT_X",strlen("CELLCOUNT_X")) ) + nCols = atoi(papszTokens[1]); + else if( EQUALN(papszTokens[0],"CELLCOUNT_Y",strlen("CELLCOUNT_Y")) ) + nRows = atoi(papszTokens[1]); + else if( EQUALN(papszTokens[0],"POSITION_XMIN",strlen("POSITION_XMIN")) ) + dXmin = CPLAtofM(papszTokens[1]); + else if( EQUALN(papszTokens[0],"POSITION_YMIN",strlen("POSITION_YMIN")) ) + dYmin = CPLAtofM(papszTokens[1]); + else if( EQUALN(papszTokens[0],"CELLSIZE",strlen("CELLSIZE")) ) + dCellsize = CPLAtofM(papszTokens[1]); + else if( EQUALN(papszTokens[0],"NODATA_VALUE",strlen("NODATA_VALUE")) ) + dNoData = CPLAtofM(papszTokens[1]); + else if( EQUALN(papszTokens[0],"DATAFORMAT",strlen("DATAFORMAT")) ) + strncpy( szDataFormat, papszTokens[1], sizeof(szDataFormat)-1 ); + else if( EQUALN(papszTokens[0],"BYTEORDER_BIG",strlen("BYTEORDER_BIG")) ) + strncpy( szByteOrderBig, papszTokens[1], sizeof(szByteOrderBig)-1 ); + else if( EQUALN(papszTokens[0],"TOPTOBOTTOM",strlen("TOPTOBOTTOM")) ) + strncpy( szTopToBottom, papszTokens[1], sizeof(szTopToBottom)-1 ); + else if( EQUALN(papszTokens[0],"Z_FACTOR",strlen("Z_FACTOR")) ) + dZFactor = CPLAtofM(papszTokens[1]); + + CSLDestroy( papszTokens ); + } + + VSIFCloseL( fp ); + + CSLDestroy( papszHDR ); + + /* -------------------------------------------------------------------- */ + /* Did we get the required keywords? If not we return with */ + /* this never having been considered to be a match. This isn't */ + /* an error! */ + /* -------------------------------------------------------------------- */ + if( nRows == -1 || nCols == -1 ) + { + return NULL; + } + + if (!GDALCheckDatasetDimensions(nCols, nRows)) + { + return NULL; + } + + if( EQUALN(szTopToBottom,"TRUE",strlen("TRUE")) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Currently the SAGA Binary Grid driver does not support\n" + "SAGA grids written TOPTOBOTTOM.\n"); + return NULL; + } + if( dZFactor != 1.0) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Currently the SAGA Binary Grid driver does not support\n" + "ZFACTORs other than 1.\n"); + } + + + + /* -------------------------------------------------------------------- */ + /* Create a corresponding GDALDataset. */ + /* -------------------------------------------------------------------- */ + SAGADataset *poDS = new SAGADataset(); + + poDS->eAccess = poOpenInfo->eAccess; + if( poOpenInfo->eAccess == GA_ReadOnly ) + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + else + poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" ); + + if( poDS->fp == NULL ) + { + delete poDS; + CPLError( CE_Failure, CPLE_OpenFailed, + "VSIFOpenL(%s) failed unexpectedly.", + poOpenInfo->pszFilename ); + return NULL; + } + + poDS->nRasterXSize = nCols; + poDS->nRasterYSize = nRows; + + SAGARasterBand *poBand = new SAGARasterBand( poDS, 1 ); + + + /* -------------------------------------------------------------------- */ + /* Figure out the byte order. */ + /* -------------------------------------------------------------------- */ + if( EQUALN(szByteOrderBig,"TRUE",strlen("TRUE")) ) + poBand->m_ByteOrder = 1; + else if( EQUALN(szByteOrderBig,"FALSE",strlen("FALSE")) ) + poBand->m_ByteOrder = 0; + + + /* -------------------------------------------------------------------- */ + /* Figure out the data type. */ + /* -------------------------------------------------------------------- */ + if( EQUAL(szDataFormat,"BIT") ) + { + poBand->SetDataType(GDT_Byte); + poBand->m_nBits = 8; + } + else if( EQUAL(szDataFormat,"BYTE_UNSIGNED") ) + { + poBand->SetDataType(GDT_Byte); + poBand->m_nBits = 8; + } + else if( EQUAL(szDataFormat,"BYTE") ) + { + poBand->SetDataType(GDT_Byte); + poBand->m_nBits = 8; + } + else if( EQUAL(szDataFormat,"SHORTINT_UNSIGNED") ) + { + poBand->SetDataType(GDT_UInt16); + poBand->m_nBits = 16; + } + else if( EQUAL(szDataFormat,"SHORTINT") ) + { + poBand->SetDataType(GDT_Int16); + poBand->m_nBits = 16; + } + else if( EQUAL(szDataFormat,"INTEGER_UNSIGNED") ) + { + poBand->SetDataType(GDT_UInt32); + poBand->m_nBits = 32; + } + else if( EQUAL(szDataFormat,"INTEGER") ) + { + poBand->SetDataType(GDT_Int32); + poBand->m_nBits = 32; + } + else if( EQUAL(szDataFormat,"FLOAT") ) + { + poBand->SetDataType(GDT_Float32); + poBand->m_nBits = 32; + } + else if( EQUAL(szDataFormat,"DOUBLE") ) + { + poBand->SetDataType(GDT_Float64); + poBand->m_nBits = 64; + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "SAGA driver does not support the dataformat %s.", + szDataFormat ); + delete poBand; + delete poDS; + return NULL; + } + + /* -------------------------------------------------------------------- */ + /* Save band information */ + /* -------------------------------------------------------------------- */ + poBand->m_Xmin = dXmin; + poBand->m_Ymin = dYmin; + poBand->m_NoData = dNoData; + poBand->m_Cellsize = dCellsize; + poBand->m_Rows = nRows; + poBand->m_Cols = nCols; + + poDS->SetBand( 1, poBand ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for a .prj file. */ +/* -------------------------------------------------------------------- */ + const char *pszPrjFilename = CPLFormCIFilename( osPath, osName, "prj" ); + + fp = VSIFOpenL( pszPrjFilename, "r" ); + + if( fp != NULL ) + { + char **papszLines; + OGRSpatialReference oSRS; + + VSIFCloseL( fp ); + + papszLines = CSLLoad( pszPrjFilename ); + + if( oSRS.importFromESRI( papszLines ) == OGRERR_NONE ) + { + CPLFree( poDS->pszProjection ); + oSRS.exportToWkt( &(poDS->pszProjection) ); + } + + CSLDestroy( papszLines ); + } + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return poDS; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr SAGADataset::GetGeoTransform( double *padfGeoTransform ) +{ + if( padfGeoTransform == NULL ) + return CE_Failure; + + SAGARasterBand *poGRB = dynamic_cast(GetRasterBand( 1 )); + + if( poGRB == NULL ) + { + padfGeoTransform[0] = 0; + padfGeoTransform[1] = 1; + padfGeoTransform[2] = 0; + padfGeoTransform[3] = 0; + padfGeoTransform[4] = 0; + padfGeoTransform[5] = 1; + return CE_Failure; + } + + /* check if we have a PAM GeoTransform stored */ + CPLPushErrorHandler( CPLQuietErrorHandler ); + CPLErr eErr = GDALPamDataset::GetGeoTransform( padfGeoTransform ); + CPLPopErrorHandler(); + + if( eErr == CE_None ) + return CE_None; + + padfGeoTransform[1] = poGRB->m_Cellsize; + padfGeoTransform[5] = poGRB->m_Cellsize * -1.0; + padfGeoTransform[0] = poGRB->m_Xmin - poGRB->m_Cellsize / 2; + padfGeoTransform[3] = poGRB->m_Ymin + (nRasterYSize - 1) * poGRB->m_Cellsize + poGRB->m_Cellsize / 2; + + /* tilt/rotation is not supported by SAGA grids */ + padfGeoTransform[4] = 0.0; + padfGeoTransform[2] = 0.0; + + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr SAGADataset::SetGeoTransform( double *padfGeoTransform ) +{ + + if( eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Unable to set GeoTransform, dataset opened read only.\n" ); + return CE_Failure; + } + + SAGARasterBand *poGRB = dynamic_cast(GetRasterBand( 1 )); + + if( poGRB == NULL || padfGeoTransform == NULL) + return CE_Failure; + + if( padfGeoTransform[1] != padfGeoTransform[5] * -1.0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to set GeoTransform, SAGA binary grids only support " + "the same cellsize in x-y.\n" ); + return CE_Failure; + } + + double dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2; + double dfMinY = + padfGeoTransform[5] * (nRasterYSize - 0.5) + padfGeoTransform[3]; + + CPLString osPath = CPLGetPath( GetDescription() ); + CPLString osName = CPLGetBasename( GetDescription() ); + CPLString osHDRFilename = CPLFormCIFilename( osPath, osName, ".sgrd" ); + + CPLErr eErr = WriteHeader( osHDRFilename, poGRB->GetRasterDataType(), + poGRB->nRasterXSize, poGRB->nRasterYSize, + dfMinX, dfMinY, padfGeoTransform[1], + poGRB->m_NoData, 1.0, false ); + + + if( eErr == CE_None ) + { + poGRB->m_Xmin = dfMinX; + poGRB->m_Ymin = dfMinY; + poGRB->m_Cellsize = padfGeoTransform[1]; + poGRB->m_Cols = nRasterXSize; + poGRB->m_Rows = nRasterYSize; + } + + return eErr; +} + +/************************************************************************/ +/* WriteHeader() */ +/************************************************************************/ + +CPLErr SAGADataset::WriteHeader( CPLString osHDRFilename, GDALDataType eType, + int nXSize, int nYSize, + double dfMinX, double dfMinY, + double dfCellsize, double dfNoData, + double dfZFactor, bool bTopToBottom ) + +{ + VSILFILE *fp; + + fp = VSIFOpenL( osHDRFilename, "wt" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to write .sgrd file %s.", + osHDRFilename.c_str() ); + return CE_Failure; + } + + VSIFPrintfL( fp, "NAME\t= %s\n", CPLGetBasename( osHDRFilename ) ); + VSIFPrintfL( fp, "DESCRIPTION\t=\n" ); + VSIFPrintfL( fp, "UNIT\t=\n" ); + VSIFPrintfL( fp, "DATAFILE_OFFSET\t= 0\n" ); + + if( eType == GDT_Int32 ) + VSIFPrintfL( fp, "DATAFORMAT\t= INTEGER\n" ); + else if( eType == GDT_UInt32 ) + VSIFPrintfL( fp, "DATAFORMAT\t= INTEGER_UNSIGNED\n" ); + else if( eType == GDT_Int16 ) + VSIFPrintfL( fp, "DATAFORMAT\t= SHORTINT\n" ); + else if( eType == GDT_UInt16 ) + VSIFPrintfL( fp, "DATAFORMAT\t= SHORTINT_UNSIGNED\n" ); + else if( eType == GDT_Byte ) + VSIFPrintfL( fp, "DATAFORMAT\t= BYTE_UNSIGNED\n" ); + else if( eType == GDT_Float32 ) + VSIFPrintfL( fp, "DATAFORMAT\t= FLOAT\n" ); + else //if( eType == GDT_Float64 ) + VSIFPrintfL( fp, "DATAFORMAT\t= DOUBLE\n" ); +#ifdef CPL_LSB + VSIFPrintfL( fp, "BYTEORDER_BIG\t= FALSE\n" ); +#else + VSIFPrintfL( fp, "BYTEORDER_BIG\t= TRUE\n" ); +#endif + + VSIFPrintfL( fp, "POSITION_XMIN\t= %.10f\n", dfMinX ); + VSIFPrintfL( fp, "POSITION_YMIN\t= %.10f\n", dfMinY ); + VSIFPrintfL( fp, "CELLCOUNT_X\t= %d\n", nXSize ); + VSIFPrintfL( fp, "CELLCOUNT_Y\t= %d\n", nYSize ); + VSIFPrintfL( fp, "CELLSIZE\t= %.10f\n", dfCellsize ); + VSIFPrintfL( fp, "Z_FACTOR\t= %f\n", dfZFactor ); + VSIFPrintfL( fp, "NODATA_VALUE\t= %f\n", dfNoData ); + if (bTopToBottom) + VSIFPrintfL( fp, "TOPTOBOTTOM\t= TRUE\n" ); + else + VSIFPrintfL( fp, "TOPTOBOTTOM\t= FALSE\n" ); + + + VSIFCloseL( fp ); + + return CE_None; +} + + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *SAGADataset::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char **papszParmList ) + +{ + if( nXSize <= 0 || nYSize <= 0 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Unable to create grid, both X and Y size must be " + "non-negative.\n" ); + + return NULL; + } + + if( nBands != 1 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "SAGA Binary Grid only supports 1 band" ); + return NULL; + } + + if( eType != GDT_Byte && eType != GDT_UInt16 && eType != GDT_Int16 + && eType != GDT_UInt32 && eType != GDT_Int32 && eType != GDT_Float32 + && eType != GDT_Float64 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SAGA Binary Grid only supports Byte, UInt16, Int16, " + "UInt32, Int32, Float32 and Float64 datatypes. Unable to " + "create with type %s.\n", GDALGetDataTypeName( eType ) ); + + return NULL; + } + + VSILFILE *fp = VSIFOpenL( pszFilename, "w+b" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file '%s' failed.\n", + pszFilename ); + return NULL; + } + + char abyNoData[8]; + double dfNoDataVal = 0.0; + + const char* pszNoDataValue = CSLFetchNameValue(papszParmList, "NODATA_VALUE"); + if (pszNoDataValue) + { + dfNoDataVal = CPLAtofM(pszNoDataValue); + } + else + { + switch (eType) /* GDT_Byte, GDT_UInt16, GDT_Int16, GDT_UInt32 */ + { /* GDT_Int32, GDT_Float32, GDT_Float64 */ + case (GDT_Byte): + { + dfNoDataVal = SG_NODATA_GDT_Byte; + break; + } + case (GDT_UInt16): + { + dfNoDataVal = SG_NODATA_GDT_UInt16; + break; + } + case (GDT_Int16): + { + dfNoDataVal = SG_NODATA_GDT_Int16; + break; + } + case (GDT_UInt32): + { + dfNoDataVal = SG_NODATA_GDT_UInt32; + break; + } + case (GDT_Int32): + { + dfNoDataVal = SG_NODATA_GDT_Int32; + break; + } + default: + case (GDT_Float32): + { + dfNoDataVal = SG_NODATA_GDT_Float32; + break; + } + case (GDT_Float64): + { + dfNoDataVal = SG_NODATA_GDT_Float64; + break; + } + } + } + + GDALCopyWords(&dfNoDataVal, GDT_Float64, 0, + abyNoData, eType, 0, 1); + + CPLString osHdrFilename = CPLResetExtension( pszFilename, "sgrd" ); + CPLErr eErr = WriteHeader( osHdrFilename, eType, + nXSize, nYSize, + 0.0, 0.0, 1.0, + dfNoDataVal, 1.0, false ); + + if( eErr != CE_None ) + { + VSIFCloseL( fp ); + return NULL; + } + + if (CSLFetchBoolean( papszParmList , "FILL_NODATA", TRUE )) + { + int nDataTypeSize = GDALGetDataTypeSize(eType) / 8; + GByte* pabyNoDataBuf = (GByte*) VSIMalloc2(nDataTypeSize, nXSize); + if (pabyNoDataBuf == NULL) + { + VSIFCloseL( fp ); + return NULL; + } + + for( int iCol = 0; iCol < nXSize; iCol++) + { + memcpy(pabyNoDataBuf + iCol * nDataTypeSize, abyNoData, nDataTypeSize); + } + + for( int iRow = 0; iRow < nYSize; iRow++ ) + { + if( VSIFWriteL( pabyNoDataBuf, nDataTypeSize, nXSize, fp ) != (unsigned)nXSize ) + { + VSIFCloseL( fp ); + VSIFree(pabyNoDataBuf); + CPLError( CE_Failure, CPLE_FileIO, + "Unable to write grid cell. Disk full?\n" ); + return NULL; + } + } + + VSIFree(pabyNoDataBuf); + } + + VSIFCloseL( fp ); + + return (GDALDataset *)GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset *SAGADataset::CreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, CPL_UNUSED char **papszOptions, + GDALProgressFunc pfnProgress, + void *pProgressData ) +{ + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "SAGA driver does not support source dataset with zero band.\n"); + return NULL; + } + else if (nBands > 1) + { + if( bStrict ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to create copy, SAGA Binary Grid " + "format only supports one raster band.\n" ); + return NULL; + } + else + CPLError( CE_Warning, CPLE_NotSupported, + "SAGA Binary Grid format only supports one " + "raster band, first band will be copied.\n" ); + } + + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( 1 ); + + char** papszCreateOptions = NULL; + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "FILL_NODATA", "NO"); + + int bHasNoDataValue = FALSE; + double dfNoDataValue = poSrcBand->GetNoDataValue(&bHasNoDataValue); + if (bHasNoDataValue) + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "NODATA_VALUE", + CPLSPrintf("%.16g", dfNoDataValue)); + + GDALDataset* poDstDS = + Create(pszFilename, poSrcBand->GetXSize(), poSrcBand->GetYSize(), + 1, poSrcBand->GetRasterDataType(), papszCreateOptions); + CSLDestroy(papszCreateOptions); + + if (poDstDS == NULL) + return NULL; + + /* -------------------------------------------------------------------- */ + /* Copy band data. */ + /* -------------------------------------------------------------------- */ + + CPLErr eErr; + + eErr = GDALDatasetCopyWholeRaster( (GDALDatasetH) poSrcDS, + (GDALDatasetH) poDstDS, + NULL, + pfnProgress, pProgressData ); + + if (eErr == CE_Failure) + { + delete poDstDS; + return NULL; + } + + double adfGeoTransform[6]; + + poSrcDS->GetGeoTransform( adfGeoTransform ); + poDstDS->SetGeoTransform( adfGeoTransform ); + + poDstDS->SetProjection( poSrcDS->GetProjectionRef() ); + + return poDstDS; +} + +/************************************************************************/ +/* GDALRegister_SAGA() */ +/************************************************************************/ + +void GDALRegister_SAGA() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "SAGA" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SAGA" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "SAGA GIS Binary Grid (.sdat)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#SAGA" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "sdat" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 Float32 Float64" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = SAGADataset::Open; + poDriver->pfnCreate = SAGADataset::Create; + poDriver->pfnCreateCopy = SAGADataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/sde/GNUmakefile b/bazaar/plugin/gdal/frmts/sde/GNUmakefile new file mode 100644 index 000000000..452569a2d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sde/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = sdedataset.o sdeerror.o sderasterband.o + +CPPFLAGS := $(SDE_INC) -DFRMT_sde $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/sde/frmt_sde.html b/bazaar/plugin/gdal/frmts/sde/frmt_sde.html new file mode 100644 index 000000000..69c7aa80c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sde/frmt_sde.html @@ -0,0 +1,93 @@ + + + + +ESRI ArcSDE Raster + + + + +

    ESRI ArcSDE Raster

    +

    + ESRI ArcSDE provides an abstraction layer over numerous databases that + allows the storage of raster data. ArcSDE supports n-band imagery at many bit depths, + and the current implementation of the GDAL driver should support + as many bands as you can throw at it. ArcSDE supports the storage of + LZW, JP2K, and uncompressed data and transparently presents this through its + C API SDK. +

    +

    GDAL ArcSDE Raster driver features

    +

    + The current driver supports the following features: +

    +
      +
    • Read support only.
    • +
    • GeoTransform information for rasters that have defined it.
    • +
    • Coordinate reference information.
    • +
    • Color interpretation (palette for datasets with colormaps, greyscale otherwise).
    • +
    • Band statistics if ArcSDE has cached them, otherwise GDAL will calculate.
    • +
    • ArcSDE overview (pyramid) support
    • +
    • 1 bit, 4 bit, 8 bit, 16 bit, and 32 bit data
    • +
    • IReadBlock support that maps to ArcSDE's representation of the data in the database.
    • +
    • ArcSDE 9.1 and 9.2 SDK's. Older versions may also work, but they have not been tested.
    • +
    +

    + The current driver does not support the following: +

    +
      +
    • Writing GDAL datasets into the database.
    • +
    • Large, fast, single-pass reads from the database.
    • +
    • Reading from "ESRI ArcSDE Raster Catalogs."
    • +
    • NODATA masks.
    • +
    + +

    Performance considerations

    +

    + The ArcSDE raster driver currently only supports block read methods. + Each call to this method results in a request for a + block of raster data for each band of data in the raster, and single-pass requests + for all of the bands for a block or given area is not currently done. + This approach consequently results in extra network overhead. It is hoped + that the driver will be improved to support single-pass reads in the + near future. +

    +

    + The ArcSDE raster driver should only consume a single ArcSDE connection + throughout the lifespan of the dataset. Each connection to the database has + an overhead of approximately 2 seconds, with additional overhead that is + taken for calculating dataset information. Therefore, usage of the + driver in situations where there is a lot of opening and closing of + datasets is not expected to be very performant. +

    +

    + Although the ArcSDE C SDK does support threading and locking, the + GDAL ArcSDE raster driver does not utilize this capability. Therefore, + the ArcSDE raster driver should be considered not threadsafe, and + sharing datasets between threads will have undefined (and often disasterous) + results. +

    + +

    Dataset specification

    +

    + SDE datasets are specified with the following information: +

    +
    + SDE:sdemachine.iastate.edu,5151,database,username,password,fully.specified.tablename,RASTER
    +
    +

    +

      +
    • SDE: -- this is the prefix that tips off GDAL to use the SDE driver.
    • +
    • sdemachine.iastate.edu -- the DNS name or IP address of the server we are connecting to.
    • +
    • 5151 -- the port number (5151 or port:5151) or service entry (typically esri_sde).
    • +
    • database -- the database to connect to. This can also be blank and specified as ,,.
    • +
    • username -- username.
    • +
    • password -- password.
    • +
    • fully.specified.tablename -- It is prudent to use a fully specified tablename wherever possible, although it is not absolutely required.
    • +
    • RASTER -- Optional raster column name.
    • +
    + + + + + + diff --git a/bazaar/plugin/gdal/frmts/sde/gdal_sde.h b/bazaar/plugin/gdal/frmts/sde/gdal_sde.h new file mode 100644 index 000000000..ac6968d2c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sde/gdal_sde.h @@ -0,0 +1,26 @@ +#ifndef GDAL_SDE_INCLUDED +#define GDAL_SDE_INCLUDED + +#include "gdal_pam.h" + + +CPL_CVSID("$Id: gdal_sde.h 10726 2007-01-30 04:43:45Z hobu $"); + +CPL_C_START +void GDALRegister_SDE(void); + + +CPL_C_END + +#include +#include +#include + +#include "cpl_string.h" +#include "ogr_spatialref.h" + +#include "sdeerror.h" +#include "sderasterband.h" +#include "sdedataset.h" + +#endif diff --git a/bazaar/plugin/gdal/frmts/sde/makefile.vc b/bazaar/plugin/gdal/frmts/sde/makefile.vc new file mode 100644 index 000000000..8255c5b37 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sde/makefile.vc @@ -0,0 +1,34 @@ + +EXTRAFLAGS = -I$(SDE_INC) /D_MBCS /DWIN32 /D_WIN32 + +OBJ = sdedataset.obj sderasterband.obj sdeerror.obj + +PLUGIN_DLL = gdal_SDE.dll + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + $(INSTALL) *.obj ..\o + +all: default + +clean: + -del *.obj + -del *.dll + -del *.exp + -del *.lib + -del *.manifest + -del *.pdb + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(GDAL_ROOT)/gdal_i.lib $(SDE_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) + diff --git a/bazaar/plugin/gdal/frmts/sde/sdedataset.cpp b/bazaar/plugin/gdal/frmts/sde/sdedataset.cpp new file mode 100644 index 000000000..7c4dc68df --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sde/sdedataset.cpp @@ -0,0 +1,539 @@ +/****************************************************************************** + * $Id: sdedataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: ESRI ArcSDE Raster reader + * Purpose: Dataset implementaion for ESRI ArcSDE Rasters + * Author: Howard Butler, hobu@hobu.net + * + * This work was sponsored by the Geological Survey of Canada, Natural + * Resources Canada. http://gsc.nrcan.gc.ca/ + * + ****************************************************************************** + * Copyright (c) 2007, Howard Butler + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdedataset.h" + + +/************************************************************************/ +/* GetRastercount() */ +/************************************************************************/ + +int SDEDataset::GetRasterCount( void ) + +{ + return nBands; +} + +/************************************************************************/ +/* GetRasterXSize() */ +/************************************************************************/ + +int SDEDataset::GetRasterXSize( void ) + +{ + return nRasterXSize; +} + +/************************************************************************/ +/* GetRasterYSize() */ +/************************************************************************/ + +int SDEDataset::GetRasterYSize( void ) + +{ + return nRasterYSize; +} + + +/************************************************************************/ +/* ComputeRasterInfo() */ +/************************************************************************/ +CPLErr SDEDataset::ComputeRasterInfo() { + long nSDEErr; + SE_RASTERINFO raster; + + nSDEErr = SE_rasterinfo_create(&raster); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasterinfo_create" ); + return CE_Fatal; + } + + LONG nRasterColumnId = 0; + + nSDEErr = SE_rascolinfo_get_id( hRasterColumn, + &nRasterColumnId); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rascolinfo_get_id" ); + return CE_Fatal; + } + + nSDEErr = SE_raster_get_info_by_id( hConnection, + nRasterColumnId, + 1, + raster); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rascolinfo_get_id" ); + return CE_Fatal; + } + + LONG nBandsRet; + nSDEErr = SE_raster_get_bands( hConnection, + raster, + &paohSDERasterBands, + &nBandsRet); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_raster_get_bands" ); + return CE_Fatal; + } + + nBands = nBandsRet; + + SE_RASBANDINFO band; + + // grab our other stuff from the first band and hope for the best + band = paohSDERasterBands[0]; + + LONG nXSRet, nYSRet; + nSDEErr = SE_rasbandinfo_get_band_size( band, &nXSRet, &nYSRet ); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasbandinfo_get_band_size" ); + return CE_Fatal; + } + + nRasterXSize = nXSRet; + nRasterYSize = nYSRet; + + SE_ENVELOPE extent; + nSDEErr = SE_rasbandinfo_get_extent(band, &extent); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasbandinfo_get_extent" ); + return CE_Fatal; + } + dfMinX = extent.minx; + dfMinY = extent.miny; + dfMaxX = extent.maxx; + dfMaxY = extent.maxy; + + CPLDebug("SDERASTER", "minx: %.5f, miny: %.5f, maxx: %.5f, maxy: %.5f", dfMinX, dfMinY, dfMaxX, dfMaxY); + + // x0 roughly corresponds to dfMinX + // y0 roughly corresponds to dfMaxY + // They can be different than the extent parameters because + // SDE uses offsets. The following info is from Duarte Carreira + // in relation to bug #2063 http://trac.osgeo.org/gdal/ticket/2063 : + + // Depending on how the data was loaded, the pixel width + // or pixel height may include a pixel offset from the nearest + // tile boundary. An offset will be indicated by aplus sign + // "+" followed by a value. The value indicates the number + // of pixels the nearest tile boundary is to the left of + // the image for the x dimension or the number of + // pixels the nearest tile boundary is above the image for + // the y dimension. The offset information is only useful + // for advanced application developers who need to know + // where the image begins in relation to the underlying tile structure + + LFLOAT x0, y0; + nSDEErr = SE_rasbandinfo_get_tile_origin(band, &x0, &y0); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasbandinfo_get_tile_origin" ); + return CE_Fatal; + } + CPLDebug("SDERASTER", "Tile origin: %.5f, %.5f", x0, y0); + + // we also need to adjust dfMaxX and dfMinY otherwise the cell size will change + dfMaxX = (x0-dfMinX) + dfMaxX; + dfMinY = (y0-dfMaxY) + dfMinY; + + // adjust the bbox based on the tile origin. + dfMinX = MIN(x0, dfMinX); + dfMaxY = MAX(y0, dfMaxY); + + nSDEErr = SE_rasterattr_create(&hAttributes, false); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasterattr_create" ); + return CE_Fatal; + } + + // Grab the pointer for our member variable + + nSDEErr = SE_stream_create(hConnection, &hStream); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_stream_create" ); + return CE_Fatal; + } + + + for (int i=0; i < nBands; i++) { + SetBand( i+1, new SDERasterBand( this, i+1, -1, &(paohSDERasterBands[i]) )); + } + + GDALRasterBand* b = GetRasterBand(1); + + eDataType = b->GetRasterDataType(); + + SE_rasterinfo_free(raster); + + return CE_None; +} + + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr SDEDataset::GetGeoTransform( double * padfTransform ) + +{ + + if (dfMinX == 0.0 && dfMinY == 0.0 && dfMaxX == 0.0 && dfMaxY == 0.0) + return CE_Fatal; + + padfTransform[0] = dfMinX - 0.5*(dfMaxX - dfMinX) / (GetRasterXSize()-1); + padfTransform[3] = dfMaxY + 0.5*(dfMaxY - dfMinY) / (GetRasterYSize()-1); + + padfTransform[1] = (dfMaxX - dfMinX) / (GetRasterXSize()-1); + padfTransform[2] = 0.0; + + padfTransform[4] = 0.0; + padfTransform[5] = -1 * (dfMaxY - dfMinY) / (GetRasterYSize()-1); + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *SDEDataset::GetProjectionRef() + +{ + long nSDEErr; + SE_COORDREF coordref; + nSDEErr = SE_coordref_create(&coordref); + + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_coordref_create" ); + return FALSE; + } + + if (!hRasterColumn){ + CPLError ( CE_Failure, CPLE_AppDefined, + "Raster Column not defined"); + return (""); + } + + nSDEErr = SE_rascolinfo_get_coordref(hRasterColumn, coordref); + + if (nSDEErr == SE_NO_COORDREF) { + return (""); + } + + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rascolinfo_get_coordref" ); + } + + char szWKT[SE_MAX_SPATIALREF_SRTEXT_LEN]; + nSDEErr = SE_coordref_get_description(coordref, szWKT); + if (nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_coordref_get_description"); + } + SE_coordref_free(coordref); + + OGRSpatialReference *poSRS; + CPLDebug ("SDERASTER", "SDE says the coordinate system is: %s'", szWKT); + poSRS = new OGRSpatialReference(szWKT); + poSRS->morphFromESRI(); + + poSRS->exportToWkt(&pszWKT); + poSRS->Release(); + + return pszWKT; +} + +/************************************************************************/ +/* SDEDataset() */ +/************************************************************************/ + +SDEDataset::SDEDataset( ) + +{ + hConnection = NULL; + nSubDataCount = 0; + pszLayerName = NULL; + hAttributes = NULL; + pszColumnName = NULL; + paohSDERasterColumns = NULL; + paohSDERasterBands = NULL; + hStream = NULL; + hRasterColumn = NULL; + pszWKT = NULL; + pszLayerName = NULL; + pszColumnName = NULL; + nBands = 0; + nRasterXSize = 0; + nRasterYSize = 0; + + dfMinX = 0.0; + dfMinY = 0.0; + dfMaxX = 0.0; + dfMaxY = 0.0; + SE_rascolinfo_create(&hRasterColumn); + +} + + + +/************************************************************************/ +/* ~SDEDataset() */ +/************************************************************************/ + +SDEDataset::~SDEDataset() + +{ + + if (paohSDERasterBands) + SE_rasterband_free_info_list(nBands, paohSDERasterBands); + + if (hRasterColumn) + SE_rascolinfo_free(hRasterColumn); + + if (hStream) + SE_stream_free(hStream); + + if (hAttributes) + SE_rasterattr_free(hAttributes); + + if (hConnection) + SE_connection_free(hConnection); + + if (pszWKT) + CPLFree(pszWKT); + + if (pszLayerName) + CPLFree(pszLayerName); + + if (pszColumnName) + CPLFree(pszColumnName); +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *SDEDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + +/* -------------------------------------------------------------------- */ +/* If we aren't prefixed with SDE: then ignore this datasource. */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(poOpenInfo->pszFilename,"SDE:",4) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Parse arguments on comma. We expect (layer is optional): */ +/* SDE:server,instance,database,username,password,layer */ +/* -------------------------------------------------------------------- */ + char **papszTokens = CSLTokenizeStringComplex( poOpenInfo->pszFilename+4, + ",", + TRUE, + TRUE ); + CPLDebug( "SDERASTER", "Open(\"%s\") revealed %d tokens.", + poOpenInfo->pszFilename, + CSLCount( papszTokens ) ); + + + if( CSLCount( papszTokens ) < 5 || CSLCount( papszTokens ) > 7 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "SDE connect string had wrong number of arguments.\n" + "Expected 'SDE:server,instance,database,username,password,layer'\n" + "The layer name value is optional.\n" + "Got '%s'", + poOpenInfo->pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + + SDEDataset *poDS; + + poDS = new SDEDataset(); +/* -------------------------------------------------------------------- */ +/* Try to establish connection. */ +/* -------------------------------------------------------------------- */ + int nSDEErr; + SE_ERROR hSDEErrorInfo; + nSDEErr = SE_connection_create( papszTokens[0], + papszTokens[1], + papszTokens[2], + papszTokens[3], + papszTokens[4], + &(hSDEErrorInfo), + &(poDS->hConnection) ); + + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_connection_create" ); + return NULL; + } + + +/* -------------------------------------------------------------------- */ +/* Set unprotected concurrency policy, suitable for single */ +/* threaded access. */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_connection_set_concurrency( poDS->hConnection, + SE_UNPROTECTED_POLICY); + + if( nSDEErr != SE_SUCCESS) { + IssueSDEError( nSDEErr, NULL ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* If we were given a layer name, use that directly, otherwise */ +/* query for subdatasets. */ +/* -------------------------------------------------------------------- */ + + // Get the RASTER column name if it was set + if (CSLCount (papszTokens) == 7) { + poDS->pszColumnName = CPLStrdup( papszTokens[6] ); + } + else { + poDS->pszColumnName = CPLStrdup( "RASTER" ); + } + + CPLDebug ("SDERASTER", "SDE Column name is '%s'", poDS->pszColumnName); + + if (CSLCount( papszTokens ) >= 6 ) { + + poDS->pszLayerName = CPLStrdup( papszTokens[5] ); + + + nSDEErr = SE_rascolinfo_create (&(poDS->hRasterColumn)); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rastercolumn_create" ); + return NULL; + } + CPLDebug( "SDERASTER", "'%s' raster layer specified... "\ + "using it directly with '%s' as the raster column name.", + poDS->pszLayerName, + poDS->pszColumnName); + nSDEErr = SE_rastercolumn_get_info_by_name( poDS->hConnection, + poDS->pszLayerName, + poDS->pszColumnName, + poDS->hRasterColumn); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rastercolumn_get_info_by_name" ); + return NULL; + } + poDS->ComputeRasterInfo(); + + + + } else { + + nSDEErr = SE_rastercolumn_get_info_list(poDS->hConnection, + &(poDS->paohSDERasterColumns), + &(poDS->nSubDataCount)); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rascolinfo_get_info_list" ); + return NULL; + } + + CPLDebug( "SDERASTER", "No layername specified, %d subdatasets available.", + poDS->nSubDataCount); + + + for (int i = 0; i < poDS->nSubDataCount; i++) { + + char szTableName[SE_QUALIFIED_TABLE_NAME+1]; + char szColumnName[SE_MAX_COLUMN_LEN+1]; + nSDEErr = SE_rascolinfo_get_raster_column (poDS->paohSDERasterColumns[i], + szTableName, + szColumnName); + CPLDebug( "SDERASTER", + "Layer '%s' with column '%s' found.", + szTableName, + szColumnName); + + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rascolinfo_get_raster_column" ); + return NULL; + } + } + + return NULL; + } + CSLDestroy( papszTokens); + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_SDE() */ +/************************************************************************/ + +void GDALRegister_SDE() + +{ + GDALDriver *poDriver; + + if (! GDAL_CHECK_VERSION("SDE driver")) + return; + + if( GDALGetDriverByName( "SDE" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SDE" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ESRI ArcSDE" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#SDE" ); + + poDriver->pfnOpen = SDEDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/sde/sdedataset.h b/bazaar/plugin/gdal/frmts/sde/sdedataset.h new file mode 100644 index 000000000..ac70e743f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sde/sdedataset.h @@ -0,0 +1,54 @@ +#ifndef SDERASTER_INCLUDED +#define SDERASTER_INCLUDED + +#include "gdal_sde.h" + + +class SDEDataset : public GDALDataset +{ + friend class SDERasterBand; + + private: + + + LONG nSubDataCount; + char* pszWKT; + + double dfMinX, dfMaxX, dfMinY, dfMaxY; + + GDALDataType eDataType; + SE_RASCOLINFO* paohSDERasterColumns; + SE_RASCOLINFO hRasterColumn; + + + CPLErr ComputeRasterInfo(void); + SE_RASBANDINFO* paohSDERasterBands; + + public: + SDEDataset(); + ~SDEDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + + + protected: + + // SDE-specific stuff + SE_CONNECTION hConnection; + SE_RASTERATTR hAttributes; + SE_STREAM hStream; + + char *pszLayerName; + char *pszColumnName; + + virtual CPLErr GetGeoTransform( double * padfTransform ); + virtual int GetRasterCount(void); + virtual int GetRasterXSize(void); + virtual int GetRasterYSize(void); + + const char *GetProjectionRef(); +}; + + + +#endif diff --git a/bazaar/plugin/gdal/frmts/sde/sdeerror.cpp b/bazaar/plugin/gdal/frmts/sde/sdeerror.cpp new file mode 100644 index 000000000..3999327ae --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sde/sdeerror.cpp @@ -0,0 +1,59 @@ + +#include "sdeerror.h" + +/************************************************************************/ +/* IssueSDEError() */ +/* from ogrsdedatasource.cpp */ +/************************************************************************/ + +void IssueSDEError( int nErrorCode, + const char *pszFunction ) + +{ + char szErrorMsg[SE_MAX_MESSAGE_LENGTH+1]; + + if( pszFunction == NULL ) + pszFunction = "SDERASTER"; + + SE_error_get_string( nErrorCode, szErrorMsg ); + + CPLError( CE_Failure, CPLE_AppDefined, + "%s: %d/%s", + pszFunction, nErrorCode, szErrorMsg ); +} + +/************************************************************************/ +/* IssueSDEExtendedError() */ +/************************************************************************/ +void IssueSDEExtendedError ( int nErrorCode, + const char *pszFunction, + SE_CONNECTION* connection, + SE_STREAM* stream) { + + SE_ERROR err; + char szErrorMsg[SE_MAX_MESSAGE_LENGTH+1]; + + if( pszFunction == NULL ) + pszFunction = "SDERASTER"; + + SE_error_get_string( nErrorCode, szErrorMsg ); + + + if (connection) + SE_connection_get_ext_error( *connection, &err ); + if (stream) + SE_stream_get_ext_error( *stream, &err ); + + if (connection || stream) { + CPLError ( CE_Failure, CPLE_AppDefined, + "%s: %d/%s ---- %s ---- %s ---- %s ---- %s", + pszFunction, nErrorCode, szErrorMsg, + err.sde_error, err.ext_error, + err.err_msg1, err.err_msg2 ); + + } else { + CPLError ( CE_Failure, CPLE_AppDefined, + "%s: %d/%s", + pszFunction, nErrorCode, szErrorMsg ); + } +} diff --git a/bazaar/plugin/gdal/frmts/sde/sdeerror.h b/bazaar/plugin/gdal/frmts/sde/sdeerror.h new file mode 100644 index 000000000..9e1422d09 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sde/sdeerror.h @@ -0,0 +1,15 @@ +#ifndef SDEERROR_INCLUDED +#define SDEERROR_INCLUDED + + +#include "gdal_sde.h" + +void IssueSDEError( int nErrorCode, + const char *pszFunction ); +void IssueSDEExtendedError ( int nErrorCode, + const char *pszFunction, + SE_CONNECTION* connection, + SE_STREAM* stream); + + +#endif diff --git a/bazaar/plugin/gdal/frmts/sde/sderasterband.cpp b/bazaar/plugin/gdal/frmts/sde/sderasterband.cpp new file mode 100644 index 000000000..647f7946e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sde/sderasterband.cpp @@ -0,0 +1,885 @@ +/****************************************************************************** + * $Id: sdedataset.cpp 10804 2007-02-08 23:24:59Z hobu $ + * + * Project: ESRI ArcSDE Raster reader + * Purpose: Rasterband implementaion for ESRI ArcSDE Rasters + * Author: Howard Butler, hobu@hobu.net + * + * This work was sponsored by the Geological Survey of Canada, Natural + * Resources Canada. http://gsc.nrcan.gc.ca/ + * + ****************************************************************************** + * Copyright (c) 2007, Howard Butler + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + +#include "sderasterband.h" + + + +/************************************************************************/ +/* SDERasterBand implements a GDAL RasterBand for ArcSDE. This class */ +/* carries around a pointer to SDE's internal band representation */ +/* is of type SE_RASBANDINFO*. SDERasterBand provides the following */ +/* capabilities: */ +/* */ +/* -- Statistics support - uses SDE's internal band statistics */ +/* -- Colortable - translates SDE's internal colortable to GDAL's */ +/* -- Block reading through IReadBlock */ +/* -- Overview support */ +/* -- NODATA support */ +/* */ +/* Instantiating a SDERasterBand is rather expensive because of all */ +/* of the round trips to the database the SDE C API must make to */ +/* calculate band information. This overhead hit is also taken in */ +/* the case of grabbing an overview, because information between */ +/* bands is not shared. It might be possible in the future to do */ +/* do so, but it would likely make things rather complicated. */ +/* In particular, the stream, constraint, and queryinfo SDE objects */ +/* could be passed around from band to overview band without having */ +/* to be instantiated every time. Stream creation has an especially */ +/* large overhead. */ +/* */ +/* Once the band or overview band is established, querying raster */ +/* blocks does not carry much more network overhead than that requied */ +/* to actually download the bytes. */ +/* */ +/* Overview of internal methods: */ +/* -- InitializeBand - does most of the work of construction */ +/* of the band and communication with SDE. */ +/* Calls InitializeConstraint and */ +/* IntializeQuery. */ +/* -- InitializeQuery - Initializes a SDE queryinfo object */ +/* that contains information about which */ +/* tables we are querying from. */ +/* -- InitializeConstraint - Specifies block constraints (which */ +/* are initially set to none in */ +/* InitializeBand) as well as which */ +/* band for SDE to query from. */ +/* -- MorphESRIRasterType - translates SDE's raster type to GDAL*/ +/* -- MorphESRIRasterDepth - calculates the bit depth from SDE */ +/* -- ComputeColorTable - does the work of getting and */ +/* translating the SDE colortable to GDAL. */ +/* -- ComputeSDEBandNumber - returns the band # for SDE's */ +/* internal representation of the band.*/ +/* -- QueryRaster - Does the work of setting the constraint */ +/* and preparing for querying tiles from SDE. */ +/* */ +/************************************************************************/ + + +/************************************************************************/ +/* SDERasterBand() */ +/************************************************************************/ + +SDERasterBand::SDERasterBand( SDEDataset *poDS, + int nBand, + int nOverview, + const SE_RASBANDINFO* band ) + +{ + // Carry some of the data we were given at construction. + // If we were passed -1 for an overview at construction, reset it + // to 0 to ensure we get the zero'th level from SDE. + // The SE_RASBANDINFO* we were given is actually owned by the + // dataset. We want it around for convenience. + this->poDS = poDS; + this->nBand = nBand; + this->nOverview = nOverview; + this->poBand = band; + + // Initialize our SDE opaque object pointers to NULL. + // The nOverviews private data member will be updated when + // GetOverviewCount is called and subsequently returned immediately in + // later calls if it has been set to anything other than 0. + this->hConstraint = NULL; + this->hQuery = NULL; + this->poColorTable = NULL; + + if (this->nOverview == -1 || this->nOverview == 0) + this->nOverviews = GetOverviewCount(); + else + this->nOverviews = 0; + + if (nOverview == -1) { + this->papoOverviews = (GDALRasterBand**) CPLMalloc( nOverviews * sizeof(GDALRasterBand*) ); + } + else { + this->papoOverviews = NULL; + } + this->eDataType = GetRasterDataType(); + + // nSDERasterType is set by GetRasterDataType + this->dfDepth = MorphESRIRasterDepth(nSDERasterType); + InitializeBand(this->nOverview); + + +} + +/************************************************************************/ +/* ~SDERasterBand() */ +/************************************************************************/ +SDERasterBand::~SDERasterBand( void ) + +{ + + if (hQuery) + SE_queryinfo_free(hQuery); + + if (hConstraint) + SE_rasconstraint_free(hConstraint); + + if (papoOverviews) + for (int i=0; i < nOverviews; i++) + delete papoOverviews[i]; + CPLFree(papoOverviews); + + if (poColorTable != NULL) + delete poColorTable; +} + + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ +GDALColorTable* SDERasterBand::GetColorTable(void) +{ + + if (SE_rasbandinfo_has_colormap(*poBand)) { + if (poColorTable == NULL) + ComputeColorTable(); + return poColorTable; + } else { + return NULL; + } +} + + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ +GDALColorInterp SDERasterBand::GetColorInterpretation() +{ + // Only return Paletted images when SDE has a colormap. Otherwise, + // just return gray, even in the instance where we have 3 or 4 band, + // imagery. Let the client be smart instead of trying to do too much. + if (SE_rasbandinfo_has_colormap(*poBand)) + return GCI_PaletteIndex; + else + return GCI_GrayIndex; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ +GDALRasterBand* SDERasterBand::GetOverview( int nOverviewValue ) +{ + + if (papoOverviews) { + return papoOverviews[nOverviewValue]; + } + else + return NULL; + + +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ +int SDERasterBand::GetOverviewCount( void ) +{ + // grab our existing overview count if we have already gotten it, + // otherwise request it from SDE and set our member data with it. + long nSDEErr; + BOOL bSkipLevel; + LONG nOvRet; + + // return nothing if we were an overview band + if (nOverview != -1) + return 0; + + nSDEErr = SE_rasbandinfo_get_max_level(*poBand, &nOvRet, &bSkipLevel); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasbandinfo_get_band_size" ); + } + + nOverviews = nOvRet; + + return nOverviews; +} + +/************************************************************************/ +/* GetRasterDataType() */ +/************************************************************************/ +GDALDataType SDERasterBand::GetRasterDataType(void) +{ + // Always ask SDE what it thinks our type is. + LONG nSDEErr; + + nSDEErr = SE_rasbandinfo_get_pixel_type(*poBand, &nSDERasterType); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasbandinfo_get_pixel_type" ); + return GDT_Byte; + } + + return MorphESRIRasterType(nSDERasterType); +} + +/************************************************************************/ +/* GetStatistics() */ +/************************************************************************/ + +CPLErr SDERasterBand::GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev ) +{ + // if SDE hasn't already cached our statistics, we'll depend on the + // GDALRasterBands's method for getting them. + bool bHasStats; + bHasStats = SE_rasbandinfo_has_stats (*poBand); + if (!bHasStats) + return GDALRasterBand::GetStatistics( bApproxOK, + bForce, + pdfMin, + pdfMax, + pdfMean, + pdfStdDev); + + // bForce has no effect currently. We always go to SDE to get our + // stats if SDE has them. + + // bApproxOK has no effect currently. If we're getting stats from + // SDE, we're hoping SDE calculates them in the way we want. + long nSDEErr; + nSDEErr = SE_rasbandinfo_get_stats_min(*poBand, pdfMin); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasbandinfo_get_stats_min" ); + return CE_Fatal; + } + + nSDEErr = SE_rasbandinfo_get_stats_max(*poBand, pdfMax); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasbandinfo_get_stats_max" ); + return CE_Fatal; + } + + nSDEErr = SE_rasbandinfo_get_stats_mean(*poBand, pdfMean); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasbandinfo_get_stats_mean" ); + return CE_Fatal; + } + + nSDEErr = SE_rasbandinfo_get_stats_stddev(*poBand, pdfStdDev); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasbandinfo_get_stats_stddev" ); + return CE_Fatal; + } + return CE_None; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double SDERasterBand::GetMinimum(int *pbSuccess) +{ + double dfMin, dfMax, dfMean, dfStdDev; + CPLErr error = GetStatistics( TRUE, TRUE, + &dfMin, + &dfMax, + &dfMean, + &dfStdDev ); + if (error == CE_None) { + *pbSuccess = TRUE; + return dfMin; + } + *pbSuccess = FALSE; + return 0.0; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double SDERasterBand::GetMaximum(int *pbSuccess) +{ + double dfMin, dfMax, dfMean, dfStdDev; + CPLErr error = GetStatistics( TRUE, TRUE, + &dfMin, + &dfMax, + &dfMean, + &dfStdDev ); + if (error == CE_None) { + *pbSuccess = TRUE; + return dfMax; + } + *pbSuccess = FALSE; + return 0.0; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr SDERasterBand::IReadBlock( int nBlockXOff, + int nBlockYOff, + void * pImage ) + +{ + // grab our Dataset to limit the casting we have to do. + SDEDataset *poGDS = (SDEDataset *) poDS; + + + // SDE manages the acquisition of raster data in "TileInfo" objects. + // The hTile is the only heap-allocated object in this method, and + // we should make sure to delete it at the end. Once we get the + // pixel data, we'll memcopy it back on to the pImage pointer. + + SE_RASTILEINFO hTile; + long nSDEErr; + nSDEErr = SE_rastileinfo_create(&hTile); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rastileinfo_create" ); + return CE_Fatal; + } + + hConstraint = InitializeConstraint( (long*) &nBlockXOff, (long*) &nBlockYOff ); + if (!hConstraint) + CPLError( CE_Failure, CPLE_AppDefined, + "ConstraintInfo initialization failed"); + + CPLErr error = QueryRaster(hConstraint); + if (error != CE_None) + return error; + + LONG level; + nSDEErr = SE_rastileinfo_get_level(hTile, &level); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rastileinfo_get_level" ); + return CE_Fatal; + } + + nSDEErr = SE_stream_get_raster_tile(poGDS->hStream, hTile); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_stream_get_raster_tile" ); + return CE_Fatal; + } + + LONG row, column; + nSDEErr = SE_rastileinfo_get_rowcol(hTile, &row, &column); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rastileinfo_get_level" ); + return CE_Fatal; + } + + LONG length; + unsigned char* pixels; + nSDEErr = SE_rastileinfo_get_pixel_data(hTile, (void**) &pixels, &length); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rastileinfo_get_pixel_data" ); + return CE_Fatal; + } + + int bits_per_pixel = static_cast(dfDepth * 8 + 0.0001); + int block_size = (nBlockXSize * bits_per_pixel + 7) / 8 * nBlockYSize; + int bitmap_size = (nBlockXSize * nBlockYSize + 7) / 8; + + + if (length == 0) { + // ArcSDE says the block has no data in it. + // Write 0's and be done with it + memset( pImage, 0, + nBlockXSize*nBlockYSize*GDALGetDataTypeSize(eDataType)/8); + return CE_None; + } + if ((length == block_size) || (length == (block_size + bitmap_size))) { + if (bits_per_pixel >= 8) { + memcpy(pImage, pixels, block_size); + } else { + GByte *p = reinterpret_cast(pImage); + int bit_mask = (2 << bits_per_pixel) - 1; + int i = 0; + for (int y = 0; y < nBlockYSize; ++y) { + for (int x = 0; x < nBlockXSize; ++x) { + *p++ = (pixels[i >> 3] >> (i & 7)) & bit_mask; + i += bits_per_pixel; + } + i = (i + 7) / 8 * 8; + } + } + } else { + + CPLError( CE_Failure, CPLE_AppDefined, + "Bit size calculation failed... "\ + "SDE's length:%d With bitmap length: %d Without bitmap length: %d", + length, block_size + bitmap_size, block_size ); + return CE_Fatal; + } + + SE_rastileinfo_free (hTile); + + return CE_None ; +} + + + +/* ---------------------------------------------------------------------*/ +/* Private Methods */ + +/************************************************************************/ +/* ComputeColorTable() */ +/************************************************************************/ +void SDERasterBand::ComputeColorTable(void) +{ + + SE_COLORMAP_TYPE eCMap_Type; + SE_COLORMAP_DATA_TYPE eCMap_DataType; + + LONG nCMapEntries; + void * phSDEColormapData; + + unsigned char* puszSDECMapData; + unsigned short* pushSDECMapData; + + long nSDEErr; + + nSDEErr = SE_rasbandinfo_get_colormap( *poBand, + &eCMap_Type, + &eCMap_DataType, + &nCMapEntries, + &phSDEColormapData); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasbandinfo_get_colormap" ); + } + + // Assign both the short and char pointers + // to the void*, and we'll switch and read based + // on the eCMap_DataType + puszSDECMapData = (unsigned char*) phSDEColormapData; + pushSDECMapData = (unsigned short*) phSDEColormapData; + + poColorTable = new GDALColorTable(GPI_RGB); + + int red, green, blue, alpha; + + CPLDebug("SDERASTER", "%d colormap entries specified", nCMapEntries); + switch (eCMap_DataType) { + case SE_COLORMAP_DATA_BYTE: + switch (eCMap_Type){ + case SE_COLORMAP_RGB: + for (int i = 0; i < (nCMapEntries); i++) { + int j = i*3; + red = puszSDECMapData[j]; + green = puszSDECMapData[j+1]; + blue = puszSDECMapData[j+2]; + GDALColorEntry sColor; + sColor.c1 = red; + sColor.c2 = green; + sColor.c3 = blue; + sColor.c4 = 255; + + // sColor is copied + poColorTable->SetColorEntry(i,&sColor); + CPLDebug ("SDERASTER", "SE_COLORMAP_DATA_BYTE "\ + "SE_COLORMAP_RGB Colormap Entry: %d %d %d", + red, blue, green); + } + break; + case SE_COLORMAP_RGBA: + for (int i = 0; i < (nCMapEntries); i++) { + int j = i*4; + red = puszSDECMapData[j]; + green = puszSDECMapData[j+1]; + blue = puszSDECMapData[j+2]; + alpha = puszSDECMapData[j+3]; + GDALColorEntry sColor; + sColor.c1 = red; + sColor.c2 = green; + sColor.c3 = blue; + sColor.c4 = alpha; + + // sColor is copied + poColorTable->SetColorEntry(i,&sColor); + CPLDebug ("SDERASTER", "SE_COLORMAP_DATA_BYTE "\ + "SE_COLORMAP_RGBA Colormap Entry: %d %d %d %d", + red, blue, green, alpha); + } + break; + case SE_COLORMAP_NONE: + break; + } + break; + case SE_COLORMAP_DATA_SHORT: + switch (eCMap_Type) { + case SE_COLORMAP_RGB: + for (int i = 0; i < (nCMapEntries); i++) { + int j = i*3; + red = pushSDECMapData[j]; + green = pushSDECMapData[j+1]; + blue = pushSDECMapData[j+2]; + GDALColorEntry sColor; + sColor.c1 = red; + sColor.c2 = green; + sColor.c3 = blue; + sColor.c4 = 255; + + // sColor is copied + poColorTable->SetColorEntry(i,&sColor); + CPLDebug ("SDERASTER", "SE_COLORMAP_DATA_SHORT "\ + "SE_COLORMAP_RGB Colormap Entry: %d %d %d", + red, blue, green); + } + break; + case SE_COLORMAP_RGBA: + for (int i = 0; i < (nCMapEntries); i++) { + int j = i*4; + red = pushSDECMapData[j]; + green = pushSDECMapData[j+1]; + blue = pushSDECMapData[j+2]; + alpha = pushSDECMapData[j+3]; + GDALColorEntry sColor; + sColor.c1 = red; + sColor.c2 = green; + sColor.c3 = blue; + sColor.c4 = alpha; + + // sColor is copied + poColorTable->SetColorEntry(i,&sColor); + CPLDebug ("SDERASTER", "SE_COLORMAP_DATA_SHORT "\ + "SE_COLORMAP_RGBA Colormap Entry: %d %d %d %d", + red, blue, green, alpha); + } + break; + case SE_COLORMAP_NONE: + break; + } + break; + } + SE_rasbandinfo_free_colormap(phSDEColormapData); +} + + +/************************************************************************/ +/* InitializeBand() */ +/************************************************************************/ +CPLErr SDERasterBand::InitializeBand( int nOverview ) + +{ + + SDEDataset *poGDS = (SDEDataset *) poDS; + + long nSDEErr; + + + hConstraint = InitializeConstraint( NULL, NULL ); + if (!hConstraint) + CPLError( CE_Failure, CPLE_AppDefined, + "ConstraintInfo initialization failed"); + + if (!hQuery) { + hQuery = InitializeQuery(); + if (!hQuery) + CPLError( CE_Failure, CPLE_AppDefined, + "QueryInfo initialization failed"); + } + + nSDEErr = SE_stream_close(poGDS->hStream, 1); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_stream_close" ); + return CE_Fatal; + } + + nSDEErr = SE_stream_query_with_info(poGDS->hStream, hQuery); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_stream_query_with_info" ); + return CE_Fatal; + } + + nSDEErr = SE_stream_execute (poGDS->hStream); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_stream_execute" ); + return CE_Fatal; + } + nSDEErr = SE_stream_fetch (poGDS->hStream); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_stream_fetch" ); + return CE_Fatal; + } + + + CPLErr error = QueryRaster(hConstraint); + if (error != CE_None) + return error; + + LONG nBXRet, nBYRet; + nSDEErr = SE_rasterattr_get_tile_size (poGDS->hAttributes, + &nBXRet, &nBYRet); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasterattr_get_tile_size" ); + return CE_Fatal; + } + + nBlockXSize = nBXRet; + nBlockYSize = nBYRet; + + LONG offset_x, offset_y, num_bands, nXSRet, nYSRet; + + nSDEErr = SE_rasterattr_get_image_size_by_level (poGDS->hAttributes, + &nXSRet, &nYSRet, + &offset_x, + &offset_y, + &num_bands, + (nOverview == -1) ? (0): (nOverview)); + + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasterattr_get_image_size_by_level" ); + return CE_Fatal; + } + + nRasterXSize = nXSRet; + nRasterYSize = nYSRet; + + nBlockSize = nBlockXSize * nBlockYSize; + + // We're the base level + if (nOverview == -1) { + for (int i = 0; inOverviews; i++) { + papoOverviews[i]= new SDERasterBand(poGDS, nBand, i, poBand); + + } + } + return CE_None; +} + +/************************************************************************/ +/* InitializeConstraint() */ +/************************************************************************/ +SE_RASCONSTRAINT& SDERasterBand::InitializeConstraint( long* nBlockXOff, + long* nBlockYOff) +{ + + long nSDEErr; + + if (!hConstraint) { + nSDEErr = SE_rasconstraint_create(&hConstraint); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasconstraint_create" ); + } + + nSDEErr = SE_rasconstraint_set_level(hConstraint, (nOverview == -1) ? (0): (nOverview)); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasconstraint_create" ); + } + + LONG nBandIn = nBand; + nSDEErr = SE_rasconstraint_set_bands(hConstraint, 1, &nBandIn); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasconstraint_set_bands" ); + } + nSDEErr = SE_rasconstraint_set_interleave(hConstraint, SE_RASTER_INTERLEAVE_BSQ); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasconstraint_set_interleave" ); + } + + } + + if (nBlockXSize != -1 && nBlockYSize != -1) { // we aren't initialized yet + if (nBlockXSize >= 0 && nBlockYSize >= 0) { + if (*nBlockXOff >= 0 && *nBlockYOff >= 0) { + long nMinX, nMinY, nMaxX, nMaxY; + + nMinX = *nBlockXOff; + nMinY = *nBlockYOff; + nMaxX = *nBlockXOff; + nMaxY = *nBlockYOff; + + nSDEErr = SE_rasconstraint_set_envelope (hConstraint, + nMinX, + nMinY, + nMaxX, + nMaxY); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_rasconstraint_set_envelope" ); + } + } + } + } + return hConstraint; +} + +/************************************************************************/ +/* InitializeQuery() */ +/************************************************************************/ +SE_QUERYINFO& SDERasterBand::InitializeQuery( void ) +{ + SDEDataset *poGDS = (SDEDataset *) poDS; + long nSDEErr; + + nSDEErr = SE_queryinfo_create(&hQuery); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_queryinfo_create" ); + } + + nSDEErr = SE_queryinfo_set_tables(hQuery, + 1, + (const char**) &(poGDS->pszLayerName), + NULL); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_queryinfo_set_tables" ); + } + + nSDEErr = SE_queryinfo_set_where_clause(hQuery, (const char*) ""); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_queryinfo_set_where" ); + } + + nSDEErr = SE_queryinfo_set_columns(hQuery, + 1, + (const char**) &(poGDS->pszColumnName)); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_queryinfo_set_where" ); + } + return hQuery; +} + + + +/************************************************************************/ +/* MorphESRIRasterDepth() */ +/************************************************************************/ +double SDERasterBand::MorphESRIRasterDepth(int gtype) { + + switch (gtype) { + case SE_PIXEL_TYPE_1BIT: + return 0.125; + case SE_PIXEL_TYPE_4BIT: + return 0.5; + case SE_PIXEL_TYPE_8BIT_U: + return 1.0; + case SE_PIXEL_TYPE_8BIT_S: + return 1.0; + case SE_PIXEL_TYPE_16BIT_U: + return 2.0; + case SE_PIXEL_TYPE_16BIT_S: + return 2.0; + case SE_PIXEL_TYPE_32BIT_U: + return 4.0; + case SE_PIXEL_TYPE_32BIT_S: + return 4.0; + case SE_PIXEL_TYPE_32BIT_REAL: + return 4.0; + case SE_PIXEL_TYPE_64BIT_REAL: + return 8.0; + default: + return 2.0; + } +} + +/************************************************************************/ +/* MorphESRIRasterType() */ +/************************************************************************/ +GDALDataType SDERasterBand::MorphESRIRasterType(int gtype) { + + switch (gtype) { + case SE_PIXEL_TYPE_1BIT: + return GDT_Byte; + case SE_PIXEL_TYPE_4BIT: + return GDT_Byte; + case SE_PIXEL_TYPE_8BIT_U: + return GDT_Byte; + case SE_PIXEL_TYPE_8BIT_S: + return GDT_Byte; + case SE_PIXEL_TYPE_16BIT_U: + return GDT_UInt16; + case SE_PIXEL_TYPE_16BIT_S: + return GDT_Int16; + case SE_PIXEL_TYPE_32BIT_U: + return GDT_UInt32; + case SE_PIXEL_TYPE_32BIT_S: + return GDT_Int32; + case SE_PIXEL_TYPE_32BIT_REAL: + return GDT_Float32; + case SE_PIXEL_TYPE_64BIT_REAL: + return GDT_Float64; + default: + return GDT_UInt16; + } +} + +/************************************************************************/ +/* QueryRaster() */ +/************************************************************************/ +CPLErr SDERasterBand::QueryRaster( SE_RASCONSTRAINT& constraint ) +{ + + SDEDataset *poGDS = (SDEDataset *) poDS; + + long nSDEErr; + + + + nSDEErr = SE_stream_query_raster_tile(poGDS->hStream, constraint); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_stream_query_raster_tile" ); + return CE_Fatal; + } + + nSDEErr = SE_stream_get_raster (poGDS->hStream, 1, poGDS->hAttributes); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_stream_fetch" ); + return CE_Fatal; + } + + return CE_None; +} + +//T:\>gdal_translate -of GTiff SDE:nakina.gis.iastate.edu,5151,,geoservwrite,EsrI4ever,sde_master.geoservwrite.century foo.tif +//T:\>gdalinfo SDE:nakina.gis.iastate.edu,5151,,geoservwrite,EsrI4ever,sde_master.geoservwrite.century diff --git a/bazaar/plugin/gdal/frmts/sde/sderasterband.h b/bazaar/plugin/gdal/frmts/sde/sderasterband.h new file mode 100644 index 000000000..4edc61679 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sde/sderasterband.h @@ -0,0 +1,66 @@ +#ifndef SDERASTERBAND_INCLUDED +#define SDERASTERBAND_INCLUDED + +#include "gdal_sde.h" + +/************************************************************************/ +/* ==================================================================== */ +/* SDERasterBand */ +/* ==================================================================== */ +/************************************************************************/ +class SDEDataset; + +class SDERasterBand : public GDALRasterBand +{ + friend class SDEDataset; + + private: + const SE_RASBANDINFO* poBand; + + double MorphESRIRasterDepth( int gtype ); + GDALDataType MorphESRIRasterType( int gtype ); + void ComputeColorTable( void ); + CPLErr InitializeBand( int nOverview ); + SE_QUERYINFO& InitializeQuery( void ); + SE_RASCONSTRAINT& InitializeConstraint ( long* nBlockXOff, + long* nBlockYOff); + CPLErr QueryRaster( SE_RASCONSTRAINT& constraint ); + + + int nOverview; + int nOverviews; + long nBlockSize; + double dfDepth; + LONG nSDERasterType; + SE_QUERYINFO hQuery; + SE_RASCONSTRAINT hConstraint; + GDALRasterBand** papoOverviews; + GDALColorTable* poColorTable; + + public: + + SDERasterBand( SDEDataset* poDS, + int nBand, + int nOverview, + const SE_RASBANDINFO* band); + + ~SDERasterBand( void ); + + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev ); + virtual GDALDataType GetRasterDataType(void); + virtual GDALColorTable *GetColorTable(); + virtual GDALColorInterp GetColorInterpretation(); + + + virtual double GetMinimum( int *pbSuccess ); + virtual double GetMaximum( int *pbSuccess ); + virtual int GetOverviewCount(void); + virtual GDALRasterBand* GetOverview(int nOverview); + +}; + +#endif diff --git a/bazaar/plugin/gdal/frmts/sdts/Doxyfile b/bazaar/plugin/gdal/frmts/sdts/Doxyfile new file mode 100644 index 000000000..d4831605f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/Doxyfile @@ -0,0 +1,255 @@ +# This file describes the settings to be used by doxygen for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of word surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = SDTS_AL + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each page. A value of NO (the default) enables the index and the +# value YES disables it. + +DISABLE_INDEX = NO + +# If the EXTRACT_ALL tag is set to YES all classes and functions will be +# included in the documentation, even if no documentation was available. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members inside documented classes or files. + +HIDE_UNDOC_MEMBERS = YES + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output + +GENERATE_HTML = YES + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the FULL_PATH_NAMES tag is set to YES Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used + +FULL_PATH_NAMES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . ../iso8211 ../../port + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +FILE_PATTERNS = *.h *.cpp *.dox + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = . ../iso8211 + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. + +MACRO_EXPANSION = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). In the former case 1 is used as the +# definition. + +PREDEFINED = + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED tag. + +EXPAND_ONLY_PREDEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/local/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = /usr/local/bin/ + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/bazaar/plugin/gdal/frmts/sdts/GNUmakefile b/bazaar/plugin/gdal/frmts/sdts/GNUmakefile new file mode 100644 index 000000000..a1cd2b861 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/GNUmakefile @@ -0,0 +1,71 @@ +VERSION = 1_3 +DISTDIR = sdts_$(VERSION) +WEB_DIR = /u/www/projects/sdts + +SHPDIR = ../shapelib +ISO8211DIR = ../iso8211 + +include ../../GDALmake.opt + +OBJ = sdtsiref.o sdtscatd.o sdtslinereader.o sdtslib.o \ + sdtspointreader.o sdtsattrreader.o sdtstransfer.o \ + sdtspolygonreader.o sdtsxref.o sdtsrasterreader.o \ + sdtsindexedreader.o + +CPPFLAGS := -I$(ISO8211DIR) $(CPPFLAGS) + +SDTSLIB = libsdts_al.a +LIBS := $(SDTSLIB) $(ISO8211DIR)/libiso8211.a $(GDAL_LIB) $(LIBS) + +default: $(SDTSLIB) sdtsdataset.$(OBJ_EXT) + +$(O_OBJ): sdts_al.h + +all: $(SDTSLIB) sdts2shp + +clean: clean-dist + rm -rf *.o sdts2shp html man $(SDTSLIB) + +clean-dist: + rm -rf $(DISTDIR) $(DISTDIR).zip $(DISTDIR).tar.gz + +$(SDTSLIB): $(OBJ:.o=.$(OBJ_EXT)) + ar r $(SDTSLIB) $? + +sdts2shp: sdts2shp.cpp $(SDTSLIB) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(SHPDIR) sdts2shp.cpp \ + $(SHPDIR)/shpopen.$(OBJ_EXT) $(SHPDIR)/dbfopen.$(OBJ_EXT) \ + $(LIBS) -o sdts2shp + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) ../o/sdtsdataset.$(OBJ_EXT) + +docs: + rm -rf html + mkdir html + doxygen + rm html/index.html + cp html/sdts_al_main.html html/index.html + +dist: docs + rm -rf $(DISTDIR) + mkdir $(DISTDIR) + mkdir $(DISTDIR)/html + cp html/* $(DISTDIR)/html + autoconf + cp *.cpp *.h configure Makefile.in $(DISTDIR) + cp makefile.vc.dist $(DISTDIR)/Makefile.vc + rm $(DISTDIR)/sdtsdataset.cpp + cp $(ISO8211DIR)/{*.cpp,*.h} $(DISTDIR) + rm configure + cp ../../port/{cpl_error*,cpl_port*,cpl_string*} $(DISTDIR) + cp ../../port/{cpl_vsisimple.cpp,cpl_config.h.in} $(DISTDIR) + cp ../../port/{cpl_vsi.h,cpl_conv.*,cpl_path.cpp} $(DISTDIR) + cp ../../port/cpl_config.h.in $(DISTDIR)/cpl_config.h + cp $(SHPDIR)/{shpopen.c,dbfopen.c,shapefil.h} $(DISTDIR) + rm $(DISTDIR)/*.o + tar czf $(DISTDIR).tar.gz $(DISTDIR) + zip -r $(DISTDIR).zip $(DISTDIR) + +update-web: dist docs + cp html/* $(WEB_DIR) + cp $(DISTDIR).tar.gz $(DISTDIR).zip /u/ftp/pub/outgoing diff --git a/bazaar/plugin/gdal/frmts/sdts/Makefile.in b/bazaar/plugin/gdal/frmts/sdts/Makefile.in new file mode 100644 index 000000000..3868db67f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/Makefile.in @@ -0,0 +1,133 @@ + +OBJ = sdtsiref.o sdtscatd.o sdtslinereader.o sdtslib.o \ + sdtspointreader.o sdtsattrreader.o sdtstransfer.o \ + sdtspolygonreader.o sdtsxref.o sdtsrasterreader.o \ + sdtsindexedreader.o \ + \ + ddfmodule.o ddfutils.o ddffielddefn.o ddfrecord.o ddffield.o \ + ddfsubfielddefn.o \ + \ + shpopen.o dbfopen.o \ + \ + cpl_error.o cpl_vsisimple.o cpl_string.o cpl_conv.o cpl_path.o + +CXXFLAGS = @CXXFLAGS@ @CXX_WFLAGS@ +LIBS = @LIBS@ -lm +CXX = @CXX@ + + +default: sdts2shp 8211view + +libsdts_al.a: $(OBJ) + ar r libsdts_al.a $(OBJ) + + +# +# SDTS library +# +sdtsiref.o: sdtsiref.cpp + $(CXX) -c $(CXXFLAGS) sdtsiref.cpp + +sdtscatd.o: sdtscatd.cpp + $(CXX) -c $(CXXFLAGS) sdtscatd.cpp + +sdtslinereader.o: sdtslinereader.cpp + $(CXX) -c $(CXXFLAGS) sdtslinereader.cpp + +sdtslib.o: sdtslib.cpp + $(CXX) -c $(CXXFLAGS) sdtslib.cpp + +sdtspointreader.o: sdtspointreader.cpp + $(CXX) -c $(CXXFLAGS) sdtspointreader.cpp + +sdtsattrreader.o: sdtsattrreader.cpp + $(CXX) -c $(CXXFLAGS) sdtsattrreader.cpp + +sdtstransfer.o: sdtstransfer.cpp + $(CXX) -c $(CXXFLAGS) sdtstransfer.cpp + +sdtspolygonreader.o: sdtspolygonreader.cpp + $(CXX) -c $(CXXFLAGS) sdtspolygonreader.cpp + +sdtsxref.o: sdtsxref.cpp + $(CXX) -c $(CXXFLAGS) sdtsxref.cpp + +sdtsrasterreader.o: sdtsrasterreader.cpp + $(CXX) -c $(CXXFLAGS) sdtsrasterreader.cpp + +sdtsindexedreader.o: sdtsindexedreader.cpp + $(CXX) -c $(CXXFLAGS) sdtsindexedreader.cpp + +# +# from iso8211 library +# + +ddfmodule.o: ddfmodule.cpp + $(CXX) -c $(CXXFLAGS) ddfmodule.cpp + +ddfutils.o: ddfutils.cpp + $(CXX) -c $(CXXFLAGS) ddfutils.cpp + +ddffielddefn.o: ddffielddefn.cpp + $(CXX) -c $(CXXFLAGS) ddffielddefn.cpp + +ddfrecord.o: ddfrecord.cpp + $(CXX) -c $(CXXFLAGS) ddfrecord.cpp + +ddffield.o: ddffield.cpp + $(CXX) -c $(CXXFLAGS) ddffield.cpp + +ddfsubfielddefn.o: ddfsubfielddefn.cpp + $(CXX) -c $(CXXFLAGS) ddfsubfielddefn.cpp + +# +# Common Portability Library +# + +cpl_error.o: cpl_error.cpp + $(CXX) -c $(CXXFLAGS) cpl_error.cpp + +cpl_string.o: cpl_string.cpp + $(CXX) -c $(CXXFLAGS) cpl_string.cpp + +cpl_conv.o: cpl_conv.cpp + $(CXX) -c $(CXXFLAGS) cpl_conv.cpp + +cpl_vsisimple.o: cpl_vsisimple.cpp + $(CXX) -c $(CXXFLAGS) cpl_vsisimple.cpp + +cpl_path.o: cpl_path.cpp + $(CXX) -c $(CXXFLAGS) cpl_path.cpp + +# +# Shapefile access +# +shpopen.o: shpopen.c + $(CXX) -c $(CXXFLAGS) shpopen.c + +dbfopen.o: dbfopen.c + $(CXX) -c $(CXXFLAGS) dbfopen.c + +# +# Mainlines +# + +sdts2shp.o: sdts2shp.cpp + $(CXX) -c $(CXXFLAGS) sdts2shp.cpp + +8211view.o: 8211view.cpp + $(CXX) -c $(CXXFLAGS) 8211view.cpp + +8211dump.o: 8211dump.cpp + $(CXX) -c $(CXXFLAGS) 8211dump.cpp + +sdts2shp: sdts2shp.o libsdts_al.a + $(CXX) $(CXXFLAGS) sdts2shp.o libsdts_al.a $(LIBS) -o sdts2shp + +8211view: 8211view.o libsdts_al.a + $(CXX) $(CXXFLAGS) 8211view.o libsdts_al.a $(LIBS) -o 8211view + +8211dump: 8211dump.o libsdts_al.a + $(CXX) $(CXXFLAGS) 8211dump.o libsdts_al.a $(LIBS) -o 8211dump + + diff --git a/bazaar/plugin/gdal/frmts/sdts/aclocal.m4 b/bazaar/plugin/gdal/frmts/sdts/aclocal.m4 new file mode 100644 index 000000000..d4d18f50c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/aclocal.m4 @@ -0,0 +1,15 @@ +AC_DEFUN(AC_COMPILER_WFLAGS, +[ + # Remove -g from compile flags, we will add via CFG variable if + # we need it. + CXXFLAGS=`echo "$CXXFLAGS " | sed "s/-g //"` + CFLAGS=`echo "$CFLAGS " | sed "s/-g //"` + + # check for GNU compiler, and use -Wall + if test "$GXX" = "yes"; then + CXX_WFLAGS="-Wall" + AC_DEFINE(USE_GNUCC) + fi + AC_SUBST(CXX_WFLAGS,$CXX_WFLAGS) + AC_SUBST(C_WFLAGS,$C_WFLAGS) +]) diff --git a/bazaar/plugin/gdal/frmts/sdts/configure.in b/bazaar/plugin/gdal/frmts/sdts/configure.in new file mode 100644 index 000000000..b3a8a31f2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/configure.in @@ -0,0 +1,18 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(Makefile.in) +AC_CONFIG_HEADER(cpl_config.h) + +dnl Checks for programs. +AC_PROG_CXX + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(fcntl.h unistd.h) + +dnl Checks for library functions. +AC_C_BIGENDIAN +AC_FUNC_VPRINTF + +AC_COMPILER_WFLAGS + +AC_OUTPUT(Makefile) diff --git a/bazaar/plugin/gdal/frmts/sdts/makefile.vc b/bazaar/plugin/gdal/frmts/sdts/makefile.vc new file mode 100644 index 000000000..2d5695ad5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/makefile.vc @@ -0,0 +1,34 @@ + +BOBJ = sdtsiref.obj sdtscatd.obj sdtslinereader.obj sdtslib.obj \ + sdtspointreader.obj sdtsattrreader.obj sdtstransfer.obj \ + sdtspolygonreader.obj sdtsxref.obj sdtsrasterreader.obj \ + sdtsindexedreader.obj +OBJ = sdtsdataset.obj $(BOBJ) + +EXTRAFLAGS = -I..\iso8211 + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +base: $(BOBJ) + +clean: + -del *.obj + -del *.exe + -del *.lib + +sdts2shp.exe: sdts2shp.cpp $(BOBJ) + $(CC) $(CFLAGS) /I..\shapelib sdts2shp.cpp $(BOBJ) \ + ..\iso8211\ddfmodule.cpp \ + ..\iso8211\ddfutils.cpp \ + ..\iso8211\ddffielddefn.cpp \ + ..\iso8211\ddfrecord.cpp \ + ..\iso8211\ddffield.cpp \ + ..\iso8211\ddfsubfielddefn.cpp \ + ..\shapelib\shpopen.c ..\shapelib\dbfopen.c \ + $(GDAL_ROOT)\port\cpl.lib $(LIBS) + diff --git a/bazaar/plugin/gdal/frmts/sdts/makefile.vc.dist b/bazaar/plugin/gdal/frmts/sdts/makefile.vc.dist new file mode 100644 index 000000000..2e714253e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/makefile.vc.dist @@ -0,0 +1,47 @@ +CFLAGS = /W3 +CXXFLAGS = /W3 + +LIBNAME = sdts_al.lib + +OBJ = sdtsiref.obj sdtscatd.obj sdtslinereader.obj sdtslib.obj \ + sdtspointreader.obj sdtsattrreader.obj sdtstransfer.obj \ + sdtspolygonreader.obj sdtsxref.obj sdtsrasterreader.obj \ + sdtsindexedreader.obj \ + \ + ddfmodule.obj ddfutils.obj ddffielddefn.obj ddfrecord.obj \ + ddffield.obj ddfsubfielddefn.obj \ + \ + shpopen.obj dbfopen.obj \ + \ + cpl_error.obj cpl_vsisimple.obj cpl_string.obj cpl_conv.obj \ + cpl_path.obj + + +default: $(LIBNAME) sdts2shp.exe 8211view.exe 8211dump.exe + +clean: + del *.obj $(LIBNAME) + +.c.obj: + $(CC) $(CFLAGS) /c $*.c + +.cpp.obj: + $(CXX) $(CXXFLAGS) /c $*.cpp + +$(LIBNAME): $(OBJ) + if exist $(LIBNAME) del $(LIBNAME) + lib /out:$(LIBNAME) $(OBJ) + +# +# Mainlines +# +sdts2shp.exe: sdts2shp.cpp $(LIBNAME) + $(CXX) sdts2shp.cpp $(LIBNAME) + +8211view.exe: 8211view.cpp $(LIBNAME) + $(CXX) 8211view.cpp $(LIBNAME) + +8211dump.exe: 8211dump.cpp $(LIBNAME) + $(CXX) 8211dump.cpp $(LIBNAME) + + diff --git a/bazaar/plugin/gdal/frmts/sdts/sdts2shp.cpp b/bazaar/plugin/gdal/frmts/sdts/sdts2shp.cpp new file mode 100644 index 000000000..a23c906f2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdts2shp.cpp @@ -0,0 +1,850 @@ +/* **************************************************************************** + * $Id: sdts2shp.cpp 28831 2015-04-01 16:46:05Z rouault $ + * + * Project: SDTS Translator + * Purpose: Mainline for converting to ArcView Shapefiles. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" +#include "shapefil.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: sdts2shp.cpp 28831 2015-04-01 16:46:05Z rouault $"); + +static int bVerbose = FALSE; + +static void WriteLineShapefile( const char *, SDTSTransfer *, + const char * ); +static void WritePointShapefile( const char *, SDTSTransfer *, + const char * ); +static void WriteAttributeDBF( const char *, SDTSTransfer *, + const char * ); +static void WritePolygonShapefile( const char *, SDTSTransfer *, + const char * ); + +static void +AddPrimaryAttrToDBFSchema( DBFHandle hDBF, SDTSTransfer * poTransfer, + char ** papszModuleList ); +static void +WritePrimaryAttrToDBF( DBFHandle hDBF, int nRecord, + SDTSTransfer *, SDTSFeature * poFeature ); +static void +WriteAttrRecordToDBF( DBFHandle hDBF, int nRecord, + SDTSTransfer *, DDFField * poAttributes ); + +/* **********************************************************************/ +/* Usage() */ +/* **********************************************************************/ + +static void Usage() + +{ + printf( "Usage: sdts2shp CATD_filename [-o shapefile_name]\n" + " [-m module_name] [-v]\n" + "\n" + "Modules include `LE01', `PC01', `NP01' and `ARDF'\n" ); + + exit( 1 ); +} + +/* **********************************************************************/ +/* main() */ +/* **********************************************************************/ + +int main( int nArgc, char ** papszArgv ) + +{ +{ + int i; + const char *pszCATDFilename = NULL; + const char *pszMODN = "LE01"; + char *pszShapefile = "sdts_out.shp"; + SDTSTransfer oTransfer; + + +/* -------------------------------------------------------------------- */ +/* Interpret commandline switches. */ +/* -------------------------------------------------------------------- */ + if( nArgc < 2 ) + Usage(); + + pszCATDFilename = papszArgv[1]; + + for( i = 2; i < nArgc; i++ ) + { + if( EQUAL(papszArgv[i],"-m") && i+1 < nArgc ) + pszMODN = papszArgv[++i]; + else if( EQUAL(papszArgv[i],"-o") && i+1 < nArgc ) + pszShapefile = papszArgv[++i]; + else if( EQUAL(papszArgv[i],"-v") ) + bVerbose = TRUE; + else + { + printf( "Incomplete, or unsupported option `%s'\n\n", + papszArgv[i] ); + Usage(); + } + } + +/* -------------------------------------------------------------------- */ +/* Massage shapefile name to have no extension. */ +/* -------------------------------------------------------------------- */ + pszShapefile = CPLStrdup(pszShapefile); + for( i = strlen(pszShapefile)-1; i >= 0; i-- ) + { + if( pszShapefile[i] == '.' ) + { + pszShapefile[i] = '\0'; + break; + } + else if( pszShapefile[i] == '/' || pszShapefile[i] == '\\' ) + break; + } + +/* -------------------------------------------------------------------- */ +/* Open the transfer. */ +/* -------------------------------------------------------------------- */ + if( !oTransfer.Open( pszCATDFilename ) ) + { + fprintf( stderr, + "Failed to read CATD file `%s'\n", + pszCATDFilename ); + exit( 100 ); + } + +/* -------------------------------------------------------------------- */ +/* Dump available layer in verbose mode. */ +/* -------------------------------------------------------------------- */ + if( bVerbose ) + { + printf( "Layers:\n" ); + for( i = 0; i < oTransfer.GetLayerCount(); i++ ) + { + int iCATDEntry = oTransfer.GetLayerCATDEntry(i); + + printf( " %s: `%s'\n", + oTransfer.GetCATD()->GetEntryModule(iCATDEntry), + oTransfer.GetCATD()->GetEntryTypeDesc(iCATDEntry) ); + } + printf( "\n" ); + } + +/* -------------------------------------------------------------------- */ +/* Check that module exists. */ +/* -------------------------------------------------------------------- */ + if( oTransfer.FindLayer( pszMODN ) == -1 ) + { + fprintf( stderr, "Unable to identify module: %s\n", pszMODN ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* If the module is an LE module, write it to an Arc file. */ +/* -------------------------------------------------------------------- */ + if( pszMODN[0] == 'L' || pszMODN[0] == 'l' ) + { + WriteLineShapefile( pszShapefile, &oTransfer, pszMODN ); + } + +/* -------------------------------------------------------------------- */ +/* If the module is an attribute primary one, dump to DBF. */ +/* -------------------------------------------------------------------- */ + else if( pszMODN[0] == 'A' || pszMODN[0] == 'a' + || pszMODN[0] == 'B' || pszMODN[0] == 'b' ) + { + WriteAttributeDBF( pszShapefile, &oTransfer, pszMODN ); + } + +/* -------------------------------------------------------------------- */ +/* If the module is a point one, dump to Shapefile. */ +/* -------------------------------------------------------------------- */ + else if( pszMODN[0] == 'N' || pszMODN[0] == 'N' ) + { + WritePointShapefile( pszShapefile, &oTransfer, pszMODN ); + } + +/* -------------------------------------------------------------------- */ +/* If the module is a polygon one, dump to Shapefile. */ +/* -------------------------------------------------------------------- */ + else if( pszMODN[0] == 'P' || pszMODN[0] == 'p' ) + { + WritePolygonShapefile( pszShapefile, &oTransfer, pszMODN ); + } + + else + { + fprintf( stderr, "Unrecognised module name: %s\n", pszMODN ); + } + + CPLFree( pszShapefile ); +} +#ifdef DBMALLOC + malloc_dump(1); +#endif +} + +/* **********************************************************************/ +/* WriteLineShapefile() */ +/* **********************************************************************/ + +static void WriteLineShapefile( const char * pszShapefile, + SDTSTransfer * poTransfer, + const char * pszMODN ) + +{ + SDTSLineReader *poLineReader; + +/* -------------------------------------------------------------------- */ +/* Fetch a reference to the indexed Pointgon reader. */ +/* -------------------------------------------------------------------- */ + poLineReader = (SDTSLineReader *) + poTransfer->GetLayerIndexedReader( poTransfer->FindLayer( pszMODN ) ); + + if( poLineReader == NULL ) + { + fprintf( stderr, "Failed to open %s.\n", + poTransfer->GetCATD()->GetModuleFilePath( pszMODN ) ); + return; + } + + poLineReader->Rewind(); + +/* -------------------------------------------------------------------- */ +/* Create the Shapefile. */ +/* -------------------------------------------------------------------- */ + SHPHandle hSHP; + + hSHP = SHPCreate( pszShapefile, SHPT_ARC ); + if( hSHP == NULL ) + { + fprintf( stderr, "Unable to create shapefile `%s'\n", + pszShapefile ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Create the database file, and our basic set of attributes. */ +/* -------------------------------------------------------------------- */ + DBFHandle hDBF; + int nLeftPolyField, nRightPolyField; + int nStartNodeField, nEndNodeField, nSDTSRecordField; + char szDBFFilename[1024]; + + sprintf( szDBFFilename, "%s.dbf", pszShapefile ); + + hDBF = DBFCreate( szDBFFilename ); + if( hDBF == NULL ) + { + fprintf( stderr, "Unable to create shapefile .dbf for `%s'\n", + pszShapefile ); + return; + } + + nSDTSRecordField = DBFAddField( hDBF, "SDTSRecId", FTInteger, 8, 0 ); + nLeftPolyField = DBFAddField( hDBF, "LeftPoly", FTString, 12, 0 ); + nRightPolyField = DBFAddField( hDBF, "RightPoly", FTString, 12, 0 ); + nStartNodeField = DBFAddField( hDBF, "StartNode", FTString, 12, 0 ); + nEndNodeField = DBFAddField( hDBF, "EndNode", FTString, 12, 0 ); + + char **papszModRefs = poLineReader->ScanModuleReferences(); + AddPrimaryAttrToDBFSchema( hDBF, poTransfer, papszModRefs ); + CSLDestroy( papszModRefs ); + +/* ==================================================================== */ +/* Process all the line features in the module. */ +/* ==================================================================== */ + SDTSRawLine *poRawLine; + + while( (poRawLine = poLineReader->GetNextLine()) != NULL ) + { + int iShape; + +/* -------------------------------------------------------------------- */ +/* Write out a shape with the vertices. */ +/* -------------------------------------------------------------------- */ + SHPObject *psShape; + + psShape = SHPCreateSimpleObject( SHPT_ARC, poRawLine->nVertices, + poRawLine->padfX, poRawLine->padfY, + poRawLine->padfZ ); + + iShape = SHPWriteObject( hSHP, -1, psShape ); + + SHPDestroyObject( psShape ); + +/* -------------------------------------------------------------------- */ +/* Write out the attributes. */ +/* -------------------------------------------------------------------- */ + char szID[13]; + + DBFWriteIntegerAttribute( hDBF, iShape, nSDTSRecordField, + poRawLine->oModId.nRecord ); + + sprintf( szID, "%s:%ld", + poRawLine->oLeftPoly.szModule, + poRawLine->oLeftPoly.nRecord ); + DBFWriteStringAttribute( hDBF, iShape, nLeftPolyField, szID ); + + sprintf( szID, "%s:%ld", + poRawLine->oRightPoly.szModule, + poRawLine->oRightPoly.nRecord ); + DBFWriteStringAttribute( hDBF, iShape, nRightPolyField, szID ); + + sprintf( szID, "%s:%ld", + poRawLine->oStartNode.szModule, + poRawLine->oStartNode.nRecord ); + DBFWriteStringAttribute( hDBF, iShape, nStartNodeField, szID ); + + sprintf( szID, "%s:%ld", + poRawLine->oEndNode.szModule, + poRawLine->oEndNode.nRecord ); + DBFWriteStringAttribute( hDBF, iShape, nEndNodeField, szID ); + + WritePrimaryAttrToDBF( hDBF, iShape, poTransfer, poRawLine ); + + if( !poLineReader->IsIndexed() ) + delete poRawLine; + } + +/* -------------------------------------------------------------------- */ +/* Close, and cleanup. */ +/* -------------------------------------------------------------------- */ + DBFClose( hDBF ); + SHPClose( hSHP ); +} + +/* **********************************************************************/ +/* WritePointShapefile() */ +/* **********************************************************************/ + +static void WritePointShapefile( const char * pszShapefile, + SDTSTransfer * poTransfer, + const char * pszMODN ) + +{ + SDTSPointReader *poPointReader; + +/* -------------------------------------------------------------------- */ +/* Fetch a reference to the indexed Pointgon reader. */ +/* -------------------------------------------------------------------- */ + poPointReader = (SDTSPointReader *) + poTransfer->GetLayerIndexedReader( poTransfer->FindLayer( pszMODN ) ); + + if( poPointReader == NULL ) + { + fprintf( stderr, "Failed to open %s.\n", + poTransfer->GetCATD()->GetModuleFilePath( pszMODN ) ); + return; + } + + poPointReader->Rewind(); + +/* -------------------------------------------------------------------- */ +/* Create the Shapefile. */ +/* -------------------------------------------------------------------- */ + SHPHandle hSHP; + + hSHP = SHPCreate( pszShapefile, SHPT_POINT ); + if( hSHP == NULL ) + { + fprintf( stderr, "Unable to create shapefile `%s'\n", + pszShapefile ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Create the database file, and our basic set of attributes. */ +/* -------------------------------------------------------------------- */ + DBFHandle hDBF; + int nAreaField, nSDTSRecordField; + char szDBFFilename[1024]; + + sprintf( szDBFFilename, "%s.dbf", pszShapefile ); + + hDBF = DBFCreate( szDBFFilename ); + if( hDBF == NULL ) + { + fprintf( stderr, "Unable to create shapefile .dbf for `%s'\n", + pszShapefile ); + return; + } + + nSDTSRecordField = DBFAddField( hDBF, "SDTSRecId", FTInteger, 8, 0 ); + nAreaField = DBFAddField( hDBF, "AreaId", FTString, 12, 0 ); + + char **papszModRefs = poPointReader->ScanModuleReferences(); + AddPrimaryAttrToDBFSchema( hDBF, poTransfer, papszModRefs ); + CSLDestroy( papszModRefs ); + +/* ==================================================================== */ +/* Process all the line features in the module. */ +/* ==================================================================== */ + SDTSRawPoint *poRawPoint; + + while( (poRawPoint = poPointReader->GetNextPoint()) != NULL ) + { + int iShape; + +/* -------------------------------------------------------------------- */ +/* Write out a shape with the vertices. */ +/* -------------------------------------------------------------------- */ + SHPObject *psShape; + + psShape = SHPCreateSimpleObject( SHPT_POINT, 1, + &(poRawPoint->dfX), + &(poRawPoint->dfY), + &(poRawPoint->dfZ) ); + + iShape = SHPWriteObject( hSHP, -1, psShape ); + + SHPDestroyObject( psShape ); + +/* -------------------------------------------------------------------- */ +/* Write out the attributes. */ +/* -------------------------------------------------------------------- */ + char szID[13]; + + DBFWriteIntegerAttribute( hDBF, iShape, nSDTSRecordField, + poRawPoint->oModId.nRecord ); + + sprintf( szID, "%s:%ld", + poRawPoint->oAreaId.szModule, + poRawPoint->oAreaId.nRecord ); + DBFWriteStringAttribute( hDBF, iShape, nAreaField, szID ); + + WritePrimaryAttrToDBF( hDBF, iShape, poTransfer, poRawPoint ); + + if( !poPointReader->IsIndexed() ) + delete poRawPoint; + } + +/* -------------------------------------------------------------------- */ +/* Close, and cleanup. */ +/* -------------------------------------------------------------------- */ + DBFClose( hDBF ); + SHPClose( hSHP ); +} + +/* **********************************************************************/ +/* WriteAttributeDBF() */ +/* **********************************************************************/ + +static void WriteAttributeDBF( const char * pszShapefile, + SDTSTransfer * poTransfer, + const char * pszMODN ) + +{ + SDTSAttrReader *poAttrReader; + +/* -------------------------------------------------------------------- */ +/* Fetch a reference to the indexed Pointgon reader. */ +/* -------------------------------------------------------------------- */ + poAttrReader = (SDTSAttrReader *) + poTransfer->GetLayerIndexedReader( poTransfer->FindLayer( pszMODN ) ); + + if( poAttrReader == NULL ) + { + fprintf( stderr, "Failed to open %s.\n", + poTransfer->GetCATD()->GetModuleFilePath( pszMODN ) ); + return; + } + + poAttrReader->Rewind(); + +/* -------------------------------------------------------------------- */ +/* Create the database file, and our basic set of attributes. */ +/* -------------------------------------------------------------------- */ + DBFHandle hDBF; + char szDBFFilename[1024]; + + sprintf( szDBFFilename, "%s.dbf", pszShapefile ); + + hDBF = DBFCreate( szDBFFilename ); + if( hDBF == NULL ) + { + fprintf( stderr, "Unable to create shapefile .dbf for `%s'\n", + pszShapefile ); + return; + } + + DBFAddField( hDBF, "SDTSRecId", FTInteger, 8, 0 ); + +/* -------------------------------------------------------------------- */ +/* Prepare the schema. */ +/* -------------------------------------------------------------------- */ + char **papszMODNList = CSLAddString( NULL, pszMODN ); + + AddPrimaryAttrToDBFSchema( hDBF, poTransfer, papszMODNList ); + + CSLDestroy( papszMODNList ); + +/* ==================================================================== */ +/* Process all the records in the module. */ +/* ==================================================================== */ + SDTSAttrRecord *poRecord; + int iRecord = 0; + + while((poRecord = (SDTSAttrRecord*)poAttrReader->GetNextFeature()) != NULL) + { + DBFWriteIntegerAttribute( hDBF, iRecord, 0, + poRecord->oModId.nRecord ); + + WriteAttrRecordToDBF( hDBF, iRecord, poTransfer, poRecord->poATTR ); + + if( !poAttrReader->IsIndexed() ) + delete poRecord; + + iRecord++; + } + +/* -------------------------------------------------------------------- */ +/* Close, and cleanup. */ +/* -------------------------------------------------------------------- */ + DBFClose( hDBF ); +} + +/* **********************************************************************/ +/* WritePolygonShapefile() */ +/* **********************************************************************/ + +static void WritePolygonShapefile( const char * pszShapefile, + SDTSTransfer * poTransfer, + const char * pszMODN ) + +{ + SDTSPolygonReader *poPolyReader; + +/* -------------------------------------------------------------------- */ +/* Fetch a reference to the indexed polygon reader. */ +/* -------------------------------------------------------------------- */ + poPolyReader = (SDTSPolygonReader *) + poTransfer->GetLayerIndexedReader( poTransfer->FindLayer( pszMODN ) ); + + if( poPolyReader == NULL ) + { + fprintf( stderr, "Failed to open %s.\n", + poTransfer->GetCATD()->GetModuleFilePath( pszMODN ) ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Assemble polygon geometries from all the line layers. */ +/* -------------------------------------------------------------------- */ + poPolyReader->AssembleRings( poTransfer, poTransfer->FindLayer(pszMODN) ); + +/* -------------------------------------------------------------------- */ +/* Create the Shapefile. */ +/* -------------------------------------------------------------------- */ + SHPHandle hSHP; + + hSHP = SHPCreate( pszShapefile, SHPT_POLYGON ); + if( hSHP == NULL ) + { + fprintf( stderr, "Unable to create shapefile `%s'\n", + pszShapefile ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Create the database file, and our basic set of attributes. */ +/* -------------------------------------------------------------------- */ + DBFHandle hDBF; + int nSDTSRecordField; + char szDBFFilename[1024]; + + sprintf( szDBFFilename, "%s.dbf", pszShapefile ); + + hDBF = DBFCreate( szDBFFilename ); + if( hDBF == NULL ) + { + fprintf( stderr, "Unable to create shapefile .dbf for `%s'\n", + pszShapefile ); + return; + } + + nSDTSRecordField = DBFAddField( hDBF, "SDTSRecId", FTInteger, 8, 0 ); + + char **papszModRefs = poPolyReader->ScanModuleReferences(); + AddPrimaryAttrToDBFSchema( hDBF, poTransfer, papszModRefs ); + CSLDestroy( papszModRefs ); + +/* ==================================================================== */ +/* Process all the polygon features in the module. */ +/* ==================================================================== */ + SDTSRawPolygon *poRawPoly; + + poPolyReader->Rewind(); + while( (poRawPoly = (SDTSRawPolygon *) poPolyReader->GetNextFeature()) + != NULL ) + { + int iShape; + +/* -------------------------------------------------------------------- */ +/* Write out a shape with the vertices. */ +/* -------------------------------------------------------------------- */ + SHPObject *psShape; + + psShape = SHPCreateObject( SHPT_POLYGON, -1, poRawPoly->nRings, + poRawPoly->panRingStart, NULL, + poRawPoly->nVertices, + poRawPoly->padfX, + poRawPoly->padfY, + poRawPoly->padfZ, + NULL ); + + iShape = SHPWriteObject( hSHP, -1, psShape ); + + SHPDestroyObject( psShape ); + +/* -------------------------------------------------------------------- */ +/* Write out the attributes. */ +/* -------------------------------------------------------------------- */ + DBFWriteIntegerAttribute( hDBF, iShape, nSDTSRecordField, + poRawPoly->oModId.nRecord ); + WritePrimaryAttrToDBF( hDBF, iShape, poTransfer, poRawPoly ); + + if( !poPolyReader->IsIndexed() ) + delete poRawPoly; + } + +/* -------------------------------------------------------------------- */ +/* Close, and cleanup. */ +/* -------------------------------------------------------------------- */ + DBFClose( hDBF ); + SHPClose( hSHP ); +} + +/* **********************************************************************/ +/* AddPrimaryAttrToDBF() */ +/* */ +/* Add the fields from all the given primary attribute modules */ +/* to the schema of the passed DBF file. */ +/* **********************************************************************/ + +static void +AddPrimaryAttrToDBFSchema( DBFHandle hDBF, SDTSTransfer *poTransfer, + char ** papszModuleList ) + +{ + for( int iModule = 0; + papszModuleList != NULL && papszModuleList[iModule] != NULL; + iModule++ ) + { + SDTSAttrReader *poAttrReader; + +/* -------------------------------------------------------------------- */ +/* Get a reader on the desired module. */ +/* -------------------------------------------------------------------- */ + poAttrReader = (SDTSAttrReader *) + poTransfer->GetLayerIndexedReader( + poTransfer->FindLayer( papszModuleList[iModule] ) ); + + if( poAttrReader == NULL ) + { + printf( "Unable to open attribute module %s, skipping.\n" , + papszModuleList[iModule] ); + continue; + } + + poAttrReader->Rewind(); + +/* -------------------------------------------------------------------- */ +/* Read the first record so we can clone schema information off */ +/* of it. */ +/* -------------------------------------------------------------------- */ + SDTSAttrRecord *poAttrFeature; + + poAttrFeature = (SDTSAttrRecord *) poAttrReader->GetNextFeature(); + if( poAttrFeature == NULL ) + { + fprintf( stderr, + "Didn't find any meaningful attribute records in %s.\n", + papszModuleList[iModule] ); + + continue; + } + +/* -------------------------------------------------------------------- */ +/* Clone schema off the first record. Eventually we need to */ +/* get the information out of the DDR record, but it isn't */ +/* clear to me how to accomplish that with the SDTS++ API. */ +/* */ +/* The following approach may fail (dramatically) if some */ +/* records do not include all subfields. Furthermore, no */ +/* effort is made to make DBF field names unique. The SDTS */ +/* attributes often have names much beyond the 14 character dbf */ +/* limit which may result in non-unique attributes. */ +/* -------------------------------------------------------------------- */ + DDFFieldDefn *poFDefn = poAttrFeature->poATTR->GetFieldDefn(); + int iSF; + DDFField *poSR = poAttrFeature->poATTR; + + for( iSF=0; iSF < poFDefn->GetSubfieldCount(); iSF++ ) + { + DDFSubfieldDefn *poSFDefn = poFDefn->GetSubfield( iSF ); + int nWidth = poSFDefn->GetWidth(); + + switch( poSFDefn->GetType() ) + { + case DDFString: + if( nWidth == 0 ) + { + int nMaxBytes; + + const char * pachData = poSR->GetSubfieldData(poSFDefn, + &nMaxBytes); + + nWidth = strlen(poSFDefn->ExtractStringData(pachData, + nMaxBytes, NULL )); + } + + DBFAddField( hDBF, poSFDefn->GetName(), FTString, nWidth, 0 ); + break; + + case DDFInt: + if( nWidth == 0 ) + nWidth = 9; + + DBFAddField( hDBF, poSFDefn->GetName(), FTInteger, nWidth, 0 ); + break; + + case DDFFloat: + DBFAddField( hDBF, poSFDefn->GetName(), FTDouble, 18, 6 ); + break; + + default: + fprintf( stderr, + "Dropping attribute `%s' of module `%s'. " + "Type unsupported\n", + poSFDefn->GetName(), + papszModuleList[iModule] ); + break; + } + } + + if( !poAttrReader->IsIndexed() ) + delete poAttrFeature; + + } /* next module */ +} + +/* **********************************************************************/ +/* WritePrimaryAttrToDBF() */ +/* **********************************************************************/ + +static void +WritePrimaryAttrToDBF( DBFHandle hDBF, int iRecord, + SDTSTransfer * poTransfer, SDTSFeature * poFeature ) + +{ +/* ==================================================================== */ +/* Loop over all the attribute records linked to this feature. */ +/* ==================================================================== */ + int iAttrRecord; + + for( iAttrRecord = 0; iAttrRecord < poFeature->nAttributes; iAttrRecord++) + { + DDFField *poSR; + + poSR = poTransfer->GetAttr( poFeature->paoATID+iAttrRecord ); + + WriteAttrRecordToDBF( hDBF, iRecord, poTransfer, poSR ); + } +} + +/* **********************************************************************/ +/* WriteAttrRecordToDBF() */ +/* **********************************************************************/ + +static void +WriteAttrRecordToDBF( DBFHandle hDBF, int iRecord, + SDTSTransfer * poTransfer, DDFField * poSR ) + +{ +/* -------------------------------------------------------------------- */ +/* Process each subfield in the record. */ +/* -------------------------------------------------------------------- */ + DDFFieldDefn *poFDefn = poSR->GetFieldDefn(); + + for( int iSF=0; iSF < poFDefn->GetSubfieldCount(); iSF++ ) + { + DDFSubfieldDefn *poSFDefn = poFDefn->GetSubfield( iSF ); + int iField; + int nMaxBytes; + const char * pachData = poSR->GetSubfieldData(poSFDefn, + &nMaxBytes); + +/* -------------------------------------------------------------------- */ +/* Identify the related DBF field, if any. */ +/* -------------------------------------------------------------------- */ + for( iField = 0; iField < hDBF->nFields; iField++ ) + { + if( EQUALN(poSFDefn->GetName(), + hDBF->pszHeader+iField*32,10) ) + break; + } + + if( iField == hDBF->nFields ) + iField = -1; + +/* -------------------------------------------------------------------- */ +/* Handle each of the types. */ +/* -------------------------------------------------------------------- */ + switch( poSFDefn->GetType() ) + { + case DDFString: + const char *pszValue; + + pszValue = poSFDefn->ExtractStringData(pachData, nMaxBytes, + NULL); + + if( iField != -1 ) + DBFWriteStringAttribute(hDBF, iRecord, iField, pszValue ); + break; + + case DDFFloat: + double dfValue; + + dfValue = poSFDefn->ExtractFloatData(pachData, nMaxBytes, + NULL); + + if( iField != -1 ) + DBFWriteDoubleAttribute( hDBF, iRecord, iField, dfValue ); + break; + + case DDFInt: + int nValue; + + nValue = poSFDefn->ExtractIntData(pachData, nMaxBytes, NULL); + + if( iField != -1 ) + DBFWriteIntegerAttribute( hDBF, iRecord, iField, nValue ); + break; + + default: + break; + } + } /* next subfield */ +} diff --git a/bazaar/plugin/gdal/frmts/sdts/sdts_al.h b/bazaar/plugin/gdal/frmts/sdts/sdts_al.h new file mode 100644 index 000000000..faa83cf5b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdts_al.h @@ -0,0 +1,680 @@ +/****************************************************************************** + * $Id: sdts_al.h 26613 2013-11-14 17:42:08Z goatbar $ + * + * Project: SDTS Translator + * Purpose: Include file for entire SDTS Abstraction Layer functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef SDTS_AL_H_INCLUDED +#define SDTS_AL_H_INCLUDED + +#include "cpl_conv.h" +#include "iso8211.h" + +class SDTS_IREF; +class SDTSModId; +class SDTSTransfer; + +#define SDTS_SIZEOF_SADR 8 + +char **SDTSScanModuleReferences( DDFModule *, const char * ); + +/************************************************************************/ +/* SDTS_IREF */ +/************************************************************************/ + +/** + Class holding SDTS IREF (internal reference) information, internal + coordinate system format, scaling and resolution. This object isn't + normally needed by applications. +*/ +class SDTS_IREF +{ + int nDefaultSADRFormat; + + public: + SDTS_IREF(); + ~SDTS_IREF(); + + int Read( const char *pszFilename ); + + char *pszXAxisName; /* XLBL */ + char *pszYAxisName; /* YLBL */ + + double dfXScale; /* SFAX */ + double dfYScale; /* SFAY */ + + double dfXOffset; /* XORG */ + double dfYOffset; /* YORG */ + + double dfXRes; /* XHRS */ + double dfYRes; /* YHRS */ + + char *pszCoordinateFormat; /* HFMT */ + + int GetSADRCount( DDFField * ); + int GetSADR( DDFField *, int, double *, double *, double * ); +}; + +/************************************************************************/ +/* SDTS_XREF */ +/************************************************************************/ + +/** + Class for reading the XREF (external reference) module containing the + data projection definition. +*/ + +class SDTS_XREF +{ + public: + SDTS_XREF(); + ~SDTS_XREF(); + + int Read( const char *pszFilename ); + + /** Projection system name, from the RSNM field. One of GEO, SPCS, UTM, + UPS, OTHR, UNSP. */ + char *pszSystemName; + + + /** Horizontal datum name, from the HDAT field. One of NAS, NAX, WGA, + WGB, WGC, WGE. */ + char *pszDatum; + + /** Zone number for UTM and SPCS projections, from the ZONE field. */ + int nZone; +}; + +/************************************************************************/ +/* SDTS_CATD */ +/************************************************************************/ +class SDTS_CATDEntry; + +/** + List of feature layer types. See SDTSTransfer::GetLayerType(). + */ + +typedef enum { + SLTUnknown, + SLTPoint, + SLTLine, + SLTAttr, + SLTPoly, + SLTRaster +} SDTSLayerType; + +/** + Class for accessing the CATD (Catalog Directory) file containing a list of + all other files (modules) in the transfer. +*/ +class SDTS_CATD +{ + char *pszPrefixPath; + + int nEntries; + SDTS_CATDEntry **papoEntries; + + public: + SDTS_CATD(); + ~SDTS_CATD(); + + int Read( const char * pszFilename ); + + const char *GetModuleFilePath( const char * pszModule ); + + int GetEntryCount() { return nEntries; } + const char * GetEntryModule(int); + const char * GetEntryTypeDesc(int); + const char * GetEntryFilePath(int); + SDTSLayerType GetEntryType(int); +}; + +/************************************************************************/ +/* SDTSModId */ +/************************************************************************/ + +/** + Object representing a unique module/record identifier within an SDTS + transfer. +*/ +class SDTSModId +{ + public: + SDTSModId() { szModule[0] = '\0'; + nRecord = -1; + szOBRP[0] = '\0'; + szName[0] = '\0'; } + + int Set( DDFField * ); + + const char *GetName(); + + /** The module name, such as PC01, containing the indicated record. */ + char szModule[8]; + + /** The record within the module referred to. This is -1 for unused + SDTSModIds. */ + long nRecord; + + /** The "role" of this record within the module. This is normally empty + for references, but set in the oModId member of a feature. */ + char szOBRP[8]; + + /** String "szModule:nRecord" */ + char szName[20]; + + +}; + +/************************************************************************/ +/* SDTSFeature */ +/************************************************************************/ + +/** + Base class for various SDTS features classes, providing a generic + module identifier, and list of attribute references. +*/ +class SDTSFeature +{ +public: + + SDTSFeature(); + virtual ~SDTSFeature(); + + /** Unique identifier for this record/feature within transfer. */ + SDTSModId oModId; + + /** Number of attribute links (aoATID[]) on this feature. */ + int nAttributes; + + /** List of nAttributes attribute record identifiers related to this + feature. */ + SDTSModId *paoATID; + + void ApplyATID( DDFField * ); + + + /** Dump reable description of feature to indicated stream. */ + virtual void Dump( FILE * ) = 0; +}; + +/************************************************************************/ +/* SDTSIndexedReader */ +/************************************************************************/ + +/** + Base class for all the SDTSFeature type readers. Provides feature + caching semantics and fetching based on a record number. + */ + +class SDTSIndexedReader +{ + int nIndexSize; + SDTSFeature **papoFeatures; + + int iCurrentFeature; + +protected: + DDFModule oDDFModule; + +public: + SDTSIndexedReader(); + virtual ~SDTSIndexedReader(); + + virtual SDTSFeature *GetNextRawFeature() = 0; + + SDTSFeature *GetNextFeature(); + + virtual void Rewind(); + + void FillIndex(); + void ClearIndex(); + int IsIndexed(); + + SDTSFeature *GetIndexedFeatureRef( int ); + char ** ScanModuleReferences( const char * = "ATID" ); + + DDFModule *GetModule() { return &oDDFModule; } +}; + + +/************************************************************************/ +/* SDTSRawLine */ +/************************************************************************/ + +/** SDTS line feature, as read from LE* modules by SDTSLineReader. */ + +class SDTSRawLine : public SDTSFeature +{ + public: + SDTSRawLine(); + virtual ~SDTSRawLine(); + + int Read( SDTS_IREF *, DDFRecord * ); + + /** Number of vertices in the padfX, padfY and padfZ arrays. */ + int nVertices; + + /** List of nVertices X coordinates. */ + double *padfX; + /** List of nVertices Y coordinates. */ + double *padfY; + /** List of nVertices Z coordinates - currently always zero. */ + double *padfZ; + + /** Identifier of polygon to left of this line. This is the SDTS PIDL + subfield. */ + SDTSModId oLeftPoly; + + /** Identifier of polygon to right of this line. This is the SDTS PIDR + subfield. */ + SDTSModId oRightPoly; + + /** Identifier for the start node of this line. This is the SDTS SNID + subfield. */ + SDTSModId oStartNode; /* SNID */ + + /** Identifier for the end node of this line. This is the SDTS ENID + subfield. */ + SDTSModId oEndNode; /* ENID */ + + void Dump( FILE * ); +}; + +/************************************************************************/ +/* SDTSLineReader */ +/* */ +/* Class for reading any of the files lines. */ +/************************************************************************/ + +/** + Reader for SDTS line modules. + + Returns SDTSRawLine features. Normally readers are instantiated with + the SDTSTransfer::GetIndexedReader() method. + + */ + +class SDTSLineReader : public SDTSIndexedReader +{ + SDTS_IREF *poIREF; + + public: + SDTSLineReader( SDTS_IREF * ); + ~SDTSLineReader(); + + int Open( const char * ); + SDTSRawLine *GetNextLine( void ); + void Close(); + + SDTSFeature *GetNextRawFeature( void ) { return GetNextLine(); } + + void AttachToPolygons( SDTSTransfer *, int iPolyLayer ); +}; + +/************************************************************************/ +/* SDTSAttrRecord */ +/************************************************************************/ + +/** + SDTS attribute record feature, as read from A* modules by + SDTSAttrReader. + + Note that even though SDTSAttrRecord is derived from SDTSFeature, there + are never any attribute records associated with attribute records using + the aoATID[] mechanism. SDTSFeature::nAttributes will always be zero. + */ + +class SDTSAttrRecord : public SDTSFeature +{ + public: + SDTSAttrRecord(); + virtual ~SDTSAttrRecord(); + + /** The entire DDFRecord read from the file. */ + DDFRecord *poWholeRecord; + + /** The ATTR DDFField with the user attribute. Each subfield is a + attribute value. */ + + DDFField *poATTR; + + virtual void Dump( FILE * ); +}; + +/************************************************************************/ +/* SDTSAttrReader */ +/************************************************************************/ + +/** + Class for reading SDTSAttrRecord features from a primary or secondary + attribute module. + */ + +class SDTSAttrReader : public SDTSIndexedReader +{ + SDTS_IREF *poIREF; + + int bIsSecondary; + + public: + SDTSAttrReader( SDTS_IREF * ); + virtual ~SDTSAttrReader(); + + int Open( const char * ); + DDFField *GetNextRecord( SDTSModId * = NULL, + DDFRecord ** = NULL, + int bDuplicate = FALSE ); + SDTSAttrRecord *GetNextAttrRecord(); + void Close(); + + /** + Returns TRUE if this is a Attribute Secondary layer rather than + an Attribute Primary layer. + */ + int IsSecondary() { return bIsSecondary; } + + SDTSFeature *GetNextRawFeature( void ) { return GetNextAttrRecord(); } +}; + +/************************************************************************/ +/* SDTSRawPoint */ +/************************************************************************/ + +/** + Object containing a point feature (type NA, NO or NP). + */ +class SDTSRawPoint : public SDTSFeature +{ + public: + SDTSRawPoint(); + virtual ~SDTSRawPoint(); + + int Read( SDTS_IREF *, DDFRecord * ); + + /** X coordinate of point. */ + double dfX; + /** Y coordinate of point. */ + double dfY; + /** Z coordinate of point. */ + double dfZ; + + /** Optional identifier of area marked by this point (ie. PC01:27). */ + SDTSModId oAreaId; /* ARID */ + + virtual void Dump( FILE * ); +}; + +/************************************************************************/ +/* SDTSPointReader */ +/************************************************************************/ + +/** + Class for reading SDTSRawPoint features from a point module (type NA, NO + or NP). + */ + +class SDTSPointReader : public SDTSIndexedReader +{ + SDTS_IREF *poIREF; + + public: + SDTSPointReader( SDTS_IREF * ); + virtual ~SDTSPointReader(); + + int Open( const char * ); + SDTSRawPoint *GetNextPoint( void ); + void Close(); + + SDTSFeature *GetNextRawFeature( void ) { return GetNextPoint(); } +}; + +/************************************************************************/ +/* SDTSRawPolygon */ +/************************************************************************/ + +/** + Class for holding information about a polygon feature. + + When directly read from a polygon module, the polygon has no concept + of it's geometry. Just it's ID, and references to attribute records. + However, if the SDTSLineReader::AttachToPolygons() method is called on + the module containing the lines forming the polygon boundaries, then the + nEdges/papoEdges information on the SDTSRawPolygon will be filled in. + + Once this is complete the AssembleRings() method can be used to fill in the + nRings/nVertices/panRingStart/padfX/padfY/padfZ information defining the + ring geometry. + + Note that the rings may not appear in any particular order, nor with any + meaningful direction (clockwise or counterclockwise). + */ + +class SDTSRawPolygon : public SDTSFeature +{ + void AddEdgeToRing( int, double *, double *, double *, int, int ); + + public: + SDTSRawPolygon(); + virtual ~SDTSRawPolygon(); + + int Read( DDFRecord * ); + + int nEdges; + SDTSRawLine **papoEdges; + + void AddEdge( SDTSRawLine * ); + + /** This method will assemble the edges associated with a polygon into + rings, returning FALSE if problems are encountered during assembly. */ + int AssembleRings(); + + /** Number of rings in assembled polygon. */ + int nRings; + /** Total number of vertices in all rings of assembled polygon. */ + int nVertices; + /** Offsets into padfX/padfY/padfZ for the beginning of each ring in the + polygon. This array is nRings long. */ + int *panRingStart; + + /** List of nVertices X coordinates for the polygon (split over multiple + rings via panRingStart. */ + double *padfX; + /** List of nVertices Y coordinates for the polygon (split over multiple + rings via panRingStart. */ + double *padfY; + /** List of nVertices Z coordinates for the polygon (split over multiple + rings via panRingStart. The values are almost always zero. */ + double *padfZ; + + virtual void Dump( FILE * ); +}; + +/************************************************************************/ +/* SDTSPolygonReader */ +/************************************************************************/ + +/** Class for reading SDTSRawPolygon features from a polygon (PC*) module. */ + +class SDTSPolygonReader : public SDTSIndexedReader +{ + int bRingsAssembled; + + public: + SDTSPolygonReader(); + virtual ~SDTSPolygonReader(); + + int Open( const char * ); + SDTSRawPolygon *GetNextPolygon( void ); + void Close(); + + SDTSFeature *GetNextRawFeature( void ) { return GetNextPolygon(); } + + void AssembleRings( SDTSTransfer *, int iPolyLayer ); +}; + +/************************************************************************/ +/* SDTSRasterReader */ +/************************************************************************/ + +/** + Class for reading raster data from a raster layer. + + This class is somewhat unique amoung the reader classes in that it isn't + derived from SDTSIndexedFeature, and it doesn't return "features". Instead + it is used to read raster blocks, in the natural block size of the dataset. + */ + +class SDTSRasterReader +{ + DDFModule oDDFModule; + + char szModule[20]; + + int nXSize; + int nYSize; + int nXBlockSize; + int nYBlockSize; + + int nXStart; /* SOCI */ + int nYStart; /* SORI */ + + double adfTransform[6]; + + public: + char szINTR[4]; /* CE is center, TL is top left */ + char szFMT[32]; + char szUNITS[64]; + char szLabel[64]; + + SDTSRasterReader(); + ~SDTSRasterReader(); + + int Open( SDTS_CATD * poCATD, SDTS_IREF *, + const char * pszModule ); + void Close(); + + int GetRasterType(); /* 1 = int16, see GDAL types */ +#define SDTS_RT_INT16 1 +#define SDTS_RT_FLOAT32 6 + + int GetTransform( double * ); + + int GetMinMax( double * pdfMin, double * pdfMax, + double dfNoData ); + + /** + Fetch the raster width. + + @return the width in pixels. + */ + int GetXSize() { return nXSize; } + /** + Fetch the raster height. + + @return the height in pixels. + */ + int GetYSize() { return nYSize; } + + /** Fetch the width of a source block (usually same as raster width). */ + int GetBlockXSize() { return nXBlockSize; } + /** Fetch the height of a source block (usually one). */ + int GetBlockYSize() { return nYBlockSize; } + + int GetBlock( int nXOffset, int nYOffset, void * pData ); +}; + +/************************************************************************/ +/* SDTSTransfer */ +/************************************************************************/ + +/** + Master class representing an entire SDTS transfer. + + This class is used to open the transfer, to get a list of available + feature layers, and to instantiate readers for those layers. + + */ + +class SDTSTransfer +{ + public: + SDTSTransfer(); + ~SDTSTransfer(); + + int Open( const char * ); + void Close(); + + int FindLayer( const char * ); + int GetLayerCount() { return nLayers; } + SDTSLayerType GetLayerType( int ); + int GetLayerCATDEntry( int ); + + SDTSLineReader *GetLayerLineReader( int ); + SDTSPointReader *GetLayerPointReader( int ); + SDTSPolygonReader *GetLayerPolygonReader( int ); + SDTSAttrReader *GetLayerAttrReader( int ); + SDTSRasterReader *GetLayerRasterReader( int ); + DDFModule *GetLayerModuleReader( int ); + + SDTSIndexedReader *GetLayerIndexedReader( int ); + + /** + Fetch the catalog object for this transfer. + + @return pointer to the internally managed SDTS_CATD for the transfer. + */ + SDTS_CATD *GetCATD() { return &oCATD ; } + + SDTS_IREF *GetIREF() { return &oIREF; } + + /** + Fetch the external reference object for this transfer. + + @return pointer to the internally managed SDTS_XREF for the transfer. + */ + SDTS_XREF *GetXREF() { return &oXREF; } + + SDTSFeature *GetIndexedFeatureRef( SDTSModId *, + SDTSLayerType *peType = NULL); + + DDFField *GetAttr( SDTSModId * ); + + int GetBounds( double *pdfMinX, double *pdfMinY, + double *pdfMaxX, double *pdfMaxY ); + + private: + + SDTS_CATD oCATD; + SDTS_IREF oIREF; + SDTS_XREF oXREF; + + int nLayers; + int *panLayerCATDEntry; + SDTSIndexedReader **papoLayerReader; +}; + +#endif /* ifndef SDTS_AL_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/sdts/sdts_main.dox b/bazaar/plugin/gdal/frmts/sdts/sdts_main.dox new file mode 100644 index 000000000..98d2728a4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdts_main.dox @@ -0,0 +1,226 @@ +/*! \page sdts_al_main + +
    +SDTS Abstraction Library +
    + +

    Introduction

    + +SDTS_AL, the SDTS Abstraction Library, is intended to be an relatively +easy to use library for reading vector from SDTS TVP (Topological Vector +Profile) files, primary DLG data from the USGS. It also include support +for reading raster data such as USGS DEMs in SDTS format. It consists +of open source, easy to compile and integrate C++ code.

    + + + +

    SDTS Background

    + +The USGS SDTS Page at +http://mcmcweb.er.usgs.gov/sdts +is the definative source of information on the SDTS format. The SDTS +format is based on the +ISO 8211 +encoding scheme for the underlying files, and the SDTS Abstraction Library +uses +ISO8211Lib +library to decode them. All references to DDF* classes are from ISO8211Lib.

    + +An SDTS Transfer is a grouping of ISO8211 encoded files (ending in the +.DDF extension), normally with part of the basename in common. For instance +a USGS DLG SDTS transfer might consists of many files matching the +SC01????.DDF pattern. The key file in an SDTS transfer is the catalog +file, such as SC01CATD.DDF.

    + + + +

    Development Information

    + +The sdts_al.h +include file contains the definitions for all public +SDTS classes, enumerations and other services.

    + +The SDTSTransfer class is used to access a transfer as a whole. The +SDTSTransfer::Open() method is passed the name of the catalog file, +such as SC01CATD.DDF, to open.

    + +The SDTSTransfer analyses the catalog, and some other aspects of the +transfer, and builds a list of feature layers. This list can be +accessed using the SDTSTransfer::GetLayerCount(), SDTSTransfer::GetLayerType(), +and SDTSTransfer::GetLayerIndexedReader() methods. A typical TVP (Topological +Vector Profile) transfer might include three point layers (of type +SLTPoint), a line layer (of type SLTLine), a polygon layer (of type SLTPoly) +as well as some additional attribute layers (of type SLTAttr). the +SDTSTransfer::GetLayerIndexedReader() method can be used to instantiate a +reader object for reading a particular layer. (NOTE: raster layers are +handled differently).

    + +Each type of SDTSIndexedReader (SDTSPointReader, SDTSLineReader, +SDTSPolygonReader, and SDTSAttrReader) returns specific subclasses of +SDTSIndexedFeature from the SDTSIndexedReader::GetNextFeature() method. +These classes are SDTSRawPoint, SDTSRawLine, SDTSRawPolygon and +SDTSAttrRecord. These classes can be investigated for details on the +data available for each.

    + +See the SDTS_AL Tutorial for more information +on how to use this library.

    + + + +

    Building the Source on Unix

    + +
      + +
    1. First, fetch the source. The most recent source should be accessable +at an url such as + +ftp://gdal.velocet.ca/pub/outgoing/sdts_1_3.tar.gz.

      + + +

    2. Unpack the source.

      +

      +% gzip -d sdts_1_3.tar.gz
      +% tar xzvf sdts_1_3.tar.gz
      +
      + +
    3. Type ``configure'' to establish configuration +options.

      + +

    4. Type make to build sdts_al.a, and the sample +mainline sdts2shp.

      + +

    + +See the SDTS_AL Tutorial for more information +on how to use this library.

    + + + +

    Building the Source on Windows

    + +
      + +
    1. First, fetch the source. The most recent source should be accessable +at an url such as + +ftp://gdal.velocet.ca/pub/outgoing/sdts_1_3.zip.

      + + +

    2. Unpack the source.

      + +

      +C:\SDTS> unzip sdts_1_3.zip
      +
      + +
    3. Build using makefile.vc with VC++. You will need the VC++ runtime +environment variables (LIB/INCLUDE) set properly. This will build the +library (sdts_al.lib), and the executables sdts2shp.exe, 8211view.exe and +8211dump.exe.

      + +

      +C:\SDTS> nmake /f makefile.vc
      +
      + +
    + +See the SDTS_AL Tutorial for more information +on how to use this library.

    + + + +

    The sdts2shp Sample Program

    + +The sdts2shp program distributed with this toolkit is primary intended to +serve as an example of how to use the SDTS access library. However, it can +be useful to translate SDTS datasets into ESRI Shapefile format. + +
    +Usage: sdts2shp CATD_filename [-o shapefile_name]
    +                [-m module_name] [-v]
    +
    +Modules include `LE01', `PC01', `NP01' and `ARDF'
    +
    + +A typical session in which we inspect the contents of a transfer, and then +extract polygon and line layers might look like this:

    + +

    +warmerda[134]% sdts2shp data/SC01CATD.DDF -v
    +Layers:
    +  ASCF: `Attribute Primary         '
    +  AHDR: `Attribute Primary         '
    +  NP01: `Point-Node                '
    +  NA01: `Point-Node                '
    +  NO01: `Point-Node                '
    +  LE01: `Line                      '
    +  PC01: `Polygon                   '
    +
    +warmerda[135]% sdts2shp data/SC01CATD.DDF -m PC01 -o pc01.shp
    +warmerda[136]% sdts2shp data/SC01CATD.DDF -m LE01 -o le01.shp
    +
    + +A prebuilt executable is available for +Windows.

    + + + +

    Licensing

    + +This library is offered as Open Source. +In particular, it is offered under the X Consortium license which doesn't +attempt to impose any copyleft, or credit requirements on users of the code.

    + +The precise license text is:

    + + + Copyright (c) 1999, Frank Warmerdam +

    + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: +

    + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. +

    + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +

    + + + + +

    Author and Acknowledgements

    + +The primary author of SDTS_AL is +Frank Warmerdam, and I can be reached at +warmerdam@pobox.com. I am eager to +receive bug reports, and also open to praise or suggestions.

    + +I would like to thank:

    + +

      +
    • Safe Software +who funded development of this library, and agreed for it to be Open Source.

      + +

    • Mark Colletti, a primary author of +SDTS++ from +which I derived most of what I know about SDTS and ISO8211 and who was very +supportive, answering a variety of questions.

      + +

    + +I would also like to dedicate this library to the memory of Sol Katz. +Sol released a variety of SDTS translators, at substantial +personal effort, to the GIS community along with the many other generous +contributions he made to the community. His example has been an inspiration +to me, and I hope similar efforts on my part will contribute to his memory.

    + +*/ diff --git a/bazaar/plugin/gdal/frmts/sdts/sdts_tut.dox b/bazaar/plugin/gdal/frmts/sdts/sdts_tut.dox new file mode 100644 index 000000000..a929e0408 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdts_tut.dox @@ -0,0 +1,469 @@ +/*! \page SDTS_AL_TUT + +

    +SDTS Abstraction Library Tutorial +
    + +This page is a walk through of the polygon layer portion of the +sdts2shp.cpp example application. It is should +give sufficient information to utilize the SDTS_AL library to read SDTS +files.

    + +

    Opening the Transfer

    + +The following statements will open an SDTS transfer. The filename +passed to SDTSTransfer::Open() should be the name of the catalog file, +such as palo_alto/SC01CATD.DDF. The Open() method returns FALSE +if it fails for any reason. In addition to the message we print out ourselves, +the SDTSTransfer::Open() method will also emit it's own error message using +CPLError(). See the cpl_error.h page for more information on how to +capture and control CPLError() style error reporting.

    + +

    +#include "stds_al.h"
    +
    +...
    +
    +    SDTSTransfer oTransfer;
    +
    +    if( !oTransfer.Open( pszCATDFilename ) )
    +    {
    +        fprintf( stderr,
    +                 "Failed to read CATD file `%s'\n",
    +                 pszCATDFilename );
    +        exit( 100 );
    +    }
    +
    + +

    Getting a Layer List

    + +Once an SDTSTransfer has been opened, it is possible to establish what +layers are available. The sdts2shp example problem includes a -v argument +to dump a list of available layers. It isn't normally necessary to use the +SDTS_CATD (catalog) from an application to access SDTS files; however, in +this example we use it to fetch a module name, and description for each +of the available layers.

    + +In particular, the SDTSTransfer::GetLayerCount() method returns the +number of feature layers in the transfer and the +SDTSTransfer::GetLayerCATDEntry() is used to translate layer indexes into +SDTS_CATD compatible CATD indexes.

    + +

    +        printf( "Layers:\n" );
    +        for( i = 0; i < oTransfer.GetLayerCount(); i++ )
    +        {
    +            int		iCATDEntry = oTransfer.GetLayerCATDEntry(i);
    +            
    +            printf( "  %s: `%s'\n",
    +                    oTransfer.GetCATD()->GetEntryModule(iCATDEntry),
    +                    oTransfer.GetCATD()->GetEntryTypeDesc(iCATDEntry) );
    +        }
    +        printf( "\n" );
    +
    + +The following would be a typical layer list. Note that there are many +other modules (files) registered with the catalog, but only these ones +are considered to be feature layers by the SDTSTransfer object. The +rest are supporting information, much of it, like data quality, is ignored +by the SDTS_AL library.

    + +

    +warmerda@cs46980-c[113]% sdts2shp data/SC01CATD.DDF -v
    +Layers:
    +  ASCF: `Attribute Primary         '
    +  AHDR: `Attribute Primary         '
    +  NP01: `Point-Node                '
    +  NA01: `Point-Node                '
    +  NO01: `Point-Node                '
    +  LE01: `Line                      '
    +  PC01: `Polygon                   '
    +
    + +

    Getting a Reader

    + +In order to read polygon features, it is necessary to instantiate a polygon +reader on the desired layer. The sdts2shp.cpp program allow the user to +select a module name (such as PC01, stored in pszMODN) to write to shape +format. Other application might just search for, and operate on all known +layers of a desired type.

    + +The SDTSTransfer::GetLayerIndexedReader() method instantiates a reader of +the desired type. In this case we know we are instantiating a +SDTSPolygonReader so we can safely cast the returned SDTSIndexedReader +pointer to the more specific type SDTSPolygonReader.

    + +

    +    SDTSPolygonReader *poPolyReader;
    +
    +    poPolyReader = (SDTSPolygonReader *) 
    +        poTransfer->GetLayerIndexedReader( poTransfer->FindLayer( pszMODN ) );
    +    
    +    if( poPolyReader == NULL )
    +    {
    +        fprintf( stderr, "Failed to open %s.\n",
    +                 poTransfer->GetCATD()->GetModuleFilePath( pszMODN ) );
    +        return;
    +    }
    +
    + +Note that readers returned by SDTSTransfer::GetLayerIndexedReader() are +managed by the SDTSTransfer, and should not be deleted by the application.

    + +

    Collecting Polygon Geometry

    + +The SDTS TVP format does not directly associate a polygons geometry (the +points forming it's boundary) with the polygon feature. Instead it is +stored in separate line layers, and the lines contain references to the +right, and left polygons that the lines border.

    + +The SDTS_AL library provides a convenient method for forming the polygon +geometry. Basically just call the SDTSPolygonReader::AssemblePolygons() +method. This method will scan all SLTLine layers in the transfer, indexing +them and attaching their line work to the polygons. Then it assembles the +line work into rings. It also ensures that the outer ring comes first, that +the outer ring is counter-clockwise and that the inner ring(s) are +clockwise. + +

    +    poPolyReader->AssembleRings( poTransfer );
    +
    + +Upon completion the SDTSPolygonReader will have been "indexed". That means +that all the polygon information will have been read from disk, and the +polygon objects will now have information stored with them indicating the +list of edges that form their border.

    + +

    Identifying Attributes

    + +In order to create the schema for the output shapefile dataset, it is +necessary to identify the attributes associated with the polygons. There +are two types of attributes which can occur. The first are hardcoded +attributes specific to the feature type, and the second are generic +user attributes stored in a separate primary attribute layer.

    + +In the case of SDTSRawPolygon, there is only one attribute of interest, +and that is the record number of the polygon. This is actually stored within +the oModId data member of the SDTSIndexedFeature base class, as will be seen +in later examples when we write it to disk. For now we create a DBF +field for the record number. This record number is a unique identifier of +the polygon within this module/layer.

    + +

    +    nSDTSRecordField = DBFAddField( hDBF, "SDTSRecId", FTInteger, 8, 0 );
    +
    + +Identification of user attributes is more complicated. Any feature in a +layer can have associates with 0, 1, 2 or potentially more attribute records +in other primary attribute layers. In order to establish a schema for the +layer it is necessary to build up a list of all attribute layers (tables) +to which references appear. The SDTSIndexedReader::ScanModuleReferences() +method can be used to scan a whole module for references to attribute modules +via the ATID field. The return result is a list of referenced modules in the +form of a string list. In a typical case this is one or two modules, such +as "ASCF".

    + +

    +    char  **papszModRefs = poPolyReader->ScanModuleReferences();
    +
    + +In sdts2shp.cpp, a subroutine (AddPrimaryAttrToDBFSchema()) is defined +to add all the fields of all references attribute layers to the DBF file. +For each module in the list the following steps are executed.

    + +

    Fetch an Attribute Module Reader

    + +The following code is similar to our code for create a polygon layer +reader. It creates a reader on one of the attribute layers referenced. +We explicitly rewind it since it may have been previously opened and +read by another part of the application.

    + +

    +        SDTSAttrReader	*poAttrReader;
    +
    +        poAttrReader = (SDTSAttrReader *)
    +            poTransfer->GetLayerIndexedReader(
    +                poTransfer->FindLayer( papszModuleList[iModule] ) );
    +
    +        if( poAttrReader == NULL )
    +        {
    +            printf( "Unable to open attribute module %s, skipping.\n" ,
    +                    papszModuleList[iModule] );
    +            continue;
    +        }
    +
    +        poAttrReader->Rewind();
    +
    + +

    Get a Prototype Record

    + +In order to get access to field definitions, and in order to establish +some sort of reasonable default lengths for field without fixed lengths +the sdts2shp program fetches a prototype record from the attribute module. + +
    +        SDTSAttrRecord 	*poAttrFeature;
    +
    +        poAttrFeature = (SDTSAttrRecord *) poAttrReader->GetNextFeature();
    +        if( poAttrFeature == NULL )
    +        {
    +            fprintf( stderr,
    +                     "Didn't find any meaningful attribute records in %s.\n",
    +                     papszModuleList[iModule] );
    +        
    +            continue;
    +        }
    +
    + +When no longer needed, the attribute record may need to be explicitly +deleted if it is not part of an indexed cached.

    + +

    +        if( !poAttrReader->IsIndexed() )
    +            delete poAttrFeature;
    +
    + +

    Extract Field Definitions

    + + +The Shapefile DBF fields are defined based on the information available for +each of the subfields of the attribute records ATTR DDFField (the poATTR +data member). The following code loops over each of the subfields, +getting a pointer to the DDBSubfieldDefn containing information about that +subfield.

    + +

    +        DDFFieldDefn 	*poFDefn = poAttrFeature->poATTR->GetFieldDefn();
    +        int		iSF;
    +        DDFField	*poSR = poAttrFeature->poATTR;
    +    
    +        for( iSF=0; iSF < poFDefn->GetSubfieldCount(); iSF++ )
    +        {
    +            DDFSubfieldDefn	*poSFDefn = poFDefn->GetSubfield( iSF );
    +
    + +Then each of the significant ISO8211 field types is translated to an +appropriate DBF field type. In cases where the nWidth field is zero, +indicating that the field is variable width, we use the length of the +field in the prototype record. Ideally we would scan the whole file to find +the longest value for each field, but that would be a significant amount of +work.

    + +

    +            int		nWidth = poSFDefn->GetWidth();
    +
    +            switch( poSFDefn->GetType() )
    +            {
    +              case DDFString:
    +                if( nWidth == 0 )
    +                {
    +                    int		nMaxBytes;
    +                
    +                    const char * pachData = poSR->GetSubfieldData(poSFDefn,
    +                                                                  &nMaxBytes);
    +
    +                    nWidth = strlen(poSFDefn->ExtractStringData(pachData,
    +                                                                nMaxBytes, NULL ));
    +                }
    +            
    +                DBFAddField( hDBF, poSFDefn->GetName(), FTString, nWidth, 0 );
    +                break;
    +
    +              case DDFInt:
    +                if( nWidth == 0 )
    +                    nWidth = 9;
    +
    +                DBFAddField( hDBF, poSFDefn->GetName(), FTInteger, nWidth, 0 );
    +                break;
    +
    +              case DDFFloat:
    +                DBFAddField( hDBF, poSFDefn->GetName(), FTDouble, 18, 6 );
    +                break;
    +
    +              default:
    +                fprintf( stderr,
    +                         "Dropping attribute `%s' of module `%s'.  "
    +                         "Type unsupported\n",
    +                         poSFDefn->GetName(),
    +                         papszModuleList[iModule] );
    +                break;
    +            }
    +        }
    +
    + +

    Reading Polygon Features

    + +With definition of the attribute schema out of the way, we return to the +main event, reading polygons from the polygon layer. We have already +instantiated the SDTSPolygonReader (poPolyReader), and now we loop reading +features from it. Note that we Rewind() the reader to ensure we are +starting at the beginning. After we are done process the polygon we +delete it, if and only if the layer does not have an index cache.

    + +

    +    SDTSRawPolygon	*poRawPoly;
    +        
    +    poPolyReader->Rewind();
    +    while( (poRawPoly = (SDTSRawPolygon *) poPolyReader->GetNextFeature())
    +           != NULL )
    +    {
    +        ... process and write polygon ...
    +
    +        if( !poPolyReader->IsIndexed() )
    +            delete poRawPoly;
    +    }
    +
    + +

    Translate Geometry

    + +In an earlier step we used the SDTSPolygonReader::AssembleRings() method to +build ring geometry on the polygons from the linework in the line layers.

    + +Coincidently (well, ok, maybe it isn't a coincidence) it so happens that the +ring organization exactly matches what is needed for the shapefile api. +The following call creates a polygon from the ring information in the +SDTSRawPolygon. See the SDTSRawPolygon reference help for a fuller +definition of the nRings, panRingStart, nVertices, and vertex fields.

    + +

    +        psShape = SHPCreateObject( SHPT_POLYGON, -1, poRawPoly->nRings,
    +                                   poRawPoly->panRingStart, NULL,
    +                                   poRawPoly->nVertices,
    +                                   poRawPoly->padfX, 
    +                                   poRawPoly->padfY, 
    +                                   poRawPoly->padfZ,
    +                                   NULL );
    +
    + +

    Write Record Number

    + +The following call is used to write out the record number of the polygon, +fetched from the SDTSIndexedFeature::oModId data member. The szModule value +in this data field will always match the module name for the whole layer. +While not shown here, there is also an szOBRP field on oModId which have +different values depending on whether the polygon is a universe or regular +polygon.

    + +

    +        DBFWriteIntegerAttribute( hDBF, iShape, nSDTSRecordField,
    +                                  poRawPoly->oModId.nRecord );
    +
    + +

    Fetch Associated User Records

    + +In keeping with the setting up of the schema, accessing the user records +is somewhat complicated. In sdts2shp, the primary attribute records associated +with any feature (including SDTSRawPolygons) can be fetched with the +WriteAttrRecordToDBF() function defined as follows.

    + +In particular, the poFeature->nAttributes member indicates how many +associated attribute records there are. The poFeature->aoATID[] array +contains the SDTSModId's for each record. This SDTSModId can be passed +to SDTSTransfer::GetAttr() to fetch the DDFField pointer for the user +attributes. The WriteAttrRecordToDBF() method is specific to sdts2shp +and will be define later.

    + +

    +    int		iAttrRecord;
    +    
    +    for( iAttrRecord = 0; iAttrRecord < poFeature->nAttributes; iAttrRecord++)
    +    {
    +        DDFField	*poSR;
    +
    +        poSR = poTransfer->GetAttr( poFeature->aoATID+iAttrRecord );
    +          
    +        WriteAttrRecordToDBF( hDBF, iRecord, poTransfer, poSR );
    +    }
    +
    + +

    Write User Attributes

    + +In a manner analygous to the definition of the fields from the prototype +attribute record, the following code loops over the subfields, and fetches +the data for each. The data extraction via poSR->GetSubfieldData() is +a bit involved, and more information can be found on the DDFField reference +page.

    + +

    +/* -------------------------------------------------------------------- */
    +/*      Process each subfield in the record.                            */
    +/* -------------------------------------------------------------------- */
    +    DDFFieldDefn 	*poFDefn = poSR->GetFieldDefn();
    +        
    +    for( int iSF=0; iSF < poFDefn->GetSubfieldCount(); iSF++ )
    +    {
    +        DDFSubfieldDefn	*poSFDefn = poFDefn->GetSubfield( iSF );
    +        int			iField;
    +        int			nMaxBytes;
    +        const char * 	pachData = poSR->GetSubfieldData(poSFDefn,
    +                                                         &nMaxBytes);
    +
    +/* -------------------------------------------------------------------- */
    +/*      Identify the related DBF field, if any.                         */
    +/* -------------------------------------------------------------------- */
    +        for( iField = 0; iField < hDBF->nFields; iField++ )
    +        {
    +            if( EQUALN(poSFDefn->GetName(),
    +                       hDBF->pszHeader+iField*32,10) )
    +                break;
    +        }
    +
    +        if( iField == hDBF->nFields )
    +            iField = -1;
    +            
    +/* -------------------------------------------------------------------- */
    +/*      Handle each of the types.                                       */
    +/* -------------------------------------------------------------------- */
    +        switch( poSFDefn->GetType() )
    +        {
    +          case DDFString:
    +            const char	*pszValue;
    +
    +            pszValue = poSFDefn->ExtractStringData(pachData, nMaxBytes,
    +                                                   NULL);
    +
    +            if( iField != -1 )
    +                DBFWriteStringAttribute(hDBF, iRecord, iField, pszValue );
    +            break;
    +
    +          case DDFFloat:
    +            double	dfValue;
    +
    +            dfValue = poSFDefn->ExtractFloatData(pachData, nMaxBytes,
    +                                                 NULL);
    +
    +            if( iField != -1 )
    +                DBFWriteDoubleAttribute( hDBF, iRecord, iField, dfValue );
    +            break;
    +
    +          case DDFInt:
    +            int		nValue;
    +
    +            nValue = poSFDefn->ExtractIntData(pachData, nMaxBytes, NULL);
    +
    +            if( iField != -1 )
    +                DBFWriteIntegerAttribute( hDBF, iRecord, iField, nValue );
    +            break;
    +
    +          default:
    +            break;
    +        }
    +    } /* next subfield */
    +
    + +

    Cleanup

    + +In the case of sdts2shp, the SDTSTransfer is created on the stack. When it +falls out of scope it is destroyed, and all the indexed readers, and their +indexed features caches are also cleaned up.

    + +*/ + +/*! +\page sdts2shp.cpp +

    +SDTS To Shape Example Application +
    + +\include sdts2shp.cpp +*/ diff --git a/bazaar/plugin/gdal/frmts/sdts/sdtsattrreader.cpp b/bazaar/plugin/gdal/frmts/sdts/sdtsattrreader.cpp new file mode 100644 index 000000000..d77bcb52f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdtsattrreader.cpp @@ -0,0 +1,226 @@ +/****************************************************************************** + * $Id: sdtsattrreader.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: SDTS Translator + * Purpose: Implementation of SDTSAttrReader class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" + +CPL_CVSID("$Id: sdtsattrreader.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + + +/************************************************************************/ +/* ==================================================================== */ +/* SDTSAttrRecord */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* SDTSAttrRecord() */ +/************************************************************************/ + +SDTSAttrRecord::SDTSAttrRecord() + +{ + poWholeRecord = NULL; + poATTR = NULL; +} + +/************************************************************************/ +/* ~SDTSAttrRecord() */ +/************************************************************************/ + +SDTSAttrRecord::~SDTSAttrRecord() + +{ + if( poWholeRecord != NULL ) + delete poWholeRecord; +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +void SDTSAttrRecord::Dump( FILE * fp ) + +{ + if( poATTR != NULL ) + poATTR->Dump( fp ); +} + + +/************************************************************************/ +/* ==================================================================== */ +/* SDTSAttrReader */ +/* */ +/* This is the class used to read a primary attribute module. */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* SDTSAttrReader() */ +/************************************************************************/ + +SDTSAttrReader::SDTSAttrReader( SDTS_IREF * poIREFIn ) + +{ + poIREF = poIREFIn; +} + +/************************************************************************/ +/* ~SDTSAttrReader() */ +/************************************************************************/ + +SDTSAttrReader::~SDTSAttrReader() +{ + Close(); +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +void SDTSAttrReader::Close() + +{ + ClearIndex(); + oDDFModule.Close(); +} + +/************************************************************************/ +/* Open() */ +/* */ +/* Open the requested attr file, and prepare to start reading */ +/* data records. */ +/************************************************************************/ + +int SDTSAttrReader::Open( const char *pszFilename ) + +{ + int bSuccess; + + bSuccess = oDDFModule.Open( pszFilename ); + + if( bSuccess ) + bIsSecondary = (oDDFModule.FindFieldDefn("ATTS") != NULL); + + return bSuccess; +} + +/************************************************************************/ +/* GetNextRecord() */ +/************************************************************************/ + +DDFField *SDTSAttrReader::GetNextRecord( SDTSModId * poModId, + DDFRecord ** ppoRecord, + int bDuplicate ) + +{ + DDFRecord *poRecord; + DDFField *poATTP; + +/* -------------------------------------------------------------------- */ +/* Fetch a record. */ +/* -------------------------------------------------------------------- */ + if( ppoRecord != NULL ) + *ppoRecord = NULL; + + if( oDDFModule.GetFP() == NULL ) + return NULL; + + poRecord = oDDFModule.ReadRecord(); + + if( poRecord == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Make a copy of the record for persistent use if requested by */ +/* the caller. */ +/* -------------------------------------------------------------------- */ + if( bDuplicate ) + poRecord = poRecord->Clone(); + +/* -------------------------------------------------------------------- */ +/* Find the ATTP field. */ +/* -------------------------------------------------------------------- */ + poATTP = poRecord->FindField( "ATTP", 0 ); + if( poATTP == NULL ) + { + poATTP = poRecord->FindField( "ATTS", 0 ); + } + + if( poATTP == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Update the module ID if required. */ +/* -------------------------------------------------------------------- */ + if( poModId != NULL ) + { + DDFField *poATPR = poRecord->FindField( "ATPR" ); + + if( poATPR == NULL ) + poATPR = poRecord->FindField( "ATSC" ); + + if( poATPR != NULL ) + poModId->Set( poATPR ); + } + +/* -------------------------------------------------------------------- */ +/* return proper answer. */ +/* -------------------------------------------------------------------- */ + if( ppoRecord != NULL ) + *ppoRecord = poRecord; + + return poATTP; +} + +/************************************************************************/ +/* GetNextAttrRecord() */ +/************************************************************************/ + +SDTSAttrRecord *SDTSAttrReader::GetNextAttrRecord() + +{ + DDFRecord *poRawRecord; + DDFField *poATTRField; + SDTSModId oModId; + SDTSAttrRecord *poAttrRecord; + + poATTRField = GetNextRecord( &oModId, &poRawRecord, TRUE ); + + if( poATTRField == NULL ) + return NULL; + + poAttrRecord = new SDTSAttrRecord(); + + poAttrRecord->poWholeRecord = poRawRecord; + poAttrRecord->poATTR = poATTRField; + memcpy( &(poAttrRecord->oModId), &oModId, sizeof(SDTSModId) ); + + return poAttrRecord; +} + diff --git a/bazaar/plugin/gdal/frmts/sdts/sdtscatd.cpp b/bazaar/plugin/gdal/frmts/sdts/sdtscatd.cpp new file mode 100644 index 000000000..c6e54778a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdtscatd.cpp @@ -0,0 +1,321 @@ +/****************************************************************************** + * $Id: sdtscatd.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: SDTS Translator + * Purpose: Implementation of SDTS_CATD and SDTS_CATDEntry classes for + * reading CATD files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" + +CPL_CVSID("$Id: sdtscatd.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + + +/************************************************************************/ +/* ==================================================================== */ +/* SDTS_CATDEntry */ +/* */ +/* This class is for internal use of the SDTS_CATD class only, */ +/* and represents one entry in the directory ... a reference */ +/* to another module file. */ +/* ==================================================================== */ +/************************************************************************/ + +class SDTS_CATDEntry + +{ + public: + char * pszModule; + char * pszType; + char * pszFile; + char * pszExternalFlag; + + char * pszFullPath; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* SDTS_CATD */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* SDTS_CATD() */ +/************************************************************************/ + +SDTS_CATD::SDTS_CATD() + +{ + nEntries = 0; + papoEntries = NULL; + pszPrefixPath = NULL; +} + +/************************************************************************/ +/* ~SDTS_CATD() */ +/************************************************************************/ + +SDTS_CATD::~SDTS_CATD() +{ + int i; + + for( i = 0; i < nEntries; i++ ) + { + CPLFree( papoEntries[i]->pszModule ); + CPLFree( papoEntries[i]->pszType ); + CPLFree( papoEntries[i]->pszFile ); + CPLFree( papoEntries[i]->pszExternalFlag ); + CPLFree( papoEntries[i]->pszFullPath ); + delete papoEntries[i]; + } + + CPLFree( papoEntries ); + CPLFree( pszPrefixPath ); +} + +/************************************************************************/ +/* Read() */ +/* */ +/* Read the named file to initialize this structure. */ +/************************************************************************/ + +int SDTS_CATD::Read( const char * pszFilename ) + +{ + DDFModule oCATDFile; + DDFRecord *poRecord; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + if( !oCATDFile.Open( pszFilename ) ) + return FALSE; + + CPLErrorReset(); // clear any ADRG "unrecognised data_struct_code" errors + +/* -------------------------------------------------------------------- */ +/* Does this file have a CATD field? If not, it isn't an SDTS */ +/* record and we won't even try reading the first record for */ +/* fear it will we a huge honking ADRG data record or something. */ +/* -------------------------------------------------------------------- */ + if( oCATDFile.FindFieldDefn( "CATD" ) == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Strip off the filename, and keep the path prefix. */ +/* -------------------------------------------------------------------- */ + int i; + + pszPrefixPath = CPLStrdup( pszFilename ); + for( i = strlen(pszPrefixPath)-1; i > 0; i-- ) + { + if( pszPrefixPath[i] == '\\' || pszPrefixPath[i] == '/' ) + { + pszPrefixPath[i] = '\0'; + break; + } + } + + if( i <= 0 ) + { + strcpy( pszPrefixPath, "." ); + } + +/* ==================================================================== */ +/* Loop reading CATD records, and adding to our list of entries */ +/* for each. */ +/* ==================================================================== */ + while( (poRecord = oCATDFile.ReadRecord()) != NULL ) + { +/* -------------------------------------------------------------------- */ +/* Verify that we have a proper CATD record. */ +/* -------------------------------------------------------------------- */ + if( poRecord->GetStringSubfield( "CATD", 0, "MODN", 0 ) == NULL ) + continue; + +/* -------------------------------------------------------------------- */ +/* Create a new entry, and get the module and file name. */ +/* -------------------------------------------------------------------- */ + SDTS_CATDEntry *poEntry = new SDTS_CATDEntry; + + poEntry->pszModule = + CPLStrdup(poRecord->GetStringSubfield( "CATD", 0, "NAME", 0 )); + poEntry->pszFile = + CPLStrdup(poRecord->GetStringSubfield( "CATD", 0, "FILE", 0 )); + poEntry->pszExternalFlag = + CPLStrdup(poRecord->GetStringSubfield( "CATD", 0, "EXTR", 0 )); + poEntry->pszType = + CPLStrdup(poRecord->GetStringSubfield( "CATD", 0, "TYPE", 0 )); + +/* -------------------------------------------------------------------- */ +/* Create a full path to the file. */ +/* -------------------------------------------------------------------- */ + poEntry->pszFullPath = + CPLStrdup(CPLFormCIFilename( pszPrefixPath, poEntry->pszFile, + NULL )); + +/* -------------------------------------------------------------------- */ +/* Add the entry to the list. */ +/* -------------------------------------------------------------------- */ + papoEntries = (SDTS_CATDEntry **) + CPLRealloc(papoEntries, sizeof(void*) * ++nEntries ); + papoEntries[nEntries-1] = poEntry; + } + + return nEntries > 0; +} + + +/************************************************************************/ +/* GetModuleFilePath() */ +/************************************************************************/ + +const char * SDTS_CATD::GetModuleFilePath( const char * pszModule ) + +{ + int i; + + for( i = 0; i < nEntries; i++ ) + { + if( EQUAL(papoEntries[i]->pszModule,pszModule) ) + return papoEntries[i]->pszFullPath; + } + + return NULL; +} + +/************************************************************************/ +/* GetEntryModule() */ +/************************************************************************/ + +const char * SDTS_CATD::GetEntryModule( int iEntry ) + +{ + if( iEntry < 0 || iEntry >= nEntries ) + return NULL; + else + return papoEntries[iEntry]->pszModule; +} + +/************************************************************************/ +/* GetEntryTypeDesc() */ +/************************************************************************/ + +/** + * Fetch the type description of a module in the catalog. + * + * @param iEntry The module index within the CATD catalog. A number from + * zero to GetEntryCount()-1. + * + * @return A pointer to an internal string with the type description for + * this module. This is from the CATD file (subfield TYPE of field CATD), + * and will be something like "Attribute Primary ". + */ + +const char * SDTS_CATD::GetEntryTypeDesc( int iEntry ) + +{ + if( iEntry < 0 || iEntry >= nEntries ) + return NULL; + else + return papoEntries[iEntry]->pszType; +} + +/************************************************************************/ +/* GetEntryType() */ +/************************************************************************/ + +/** + * Fetch the enumerated type of a module in the catalog. + * + * @param iEntry The module index within the CATD catalog. A number from + * zero to GetEntryCount()-1. + * + * @return A value from the SDTSLayerType enumeration indicating the type of + * the module, and indicating the corresponding type of reader.

    + * + *

      + *
    • SLTPoint: Read with SDTSPointReader, underlying type of + * Point-Node. + *
    • SLTLine: Read with SDTSLineReader, underlying type of + * Line. + *
    • SLTAttr: Read with SDTSAttrReader, underlying type of + * Attribute Primary or Attribute Secondary. + *
    • SLTPolygon: Read with SDTSPolygonReader, underlying type of + * Polygon. + *
    + */ + +SDTSLayerType SDTS_CATD::GetEntryType( int iEntry ) + +{ + if( iEntry < 0 || iEntry >= nEntries ) + return SLTUnknown; + + else if( EQUALN(papoEntries[iEntry]->pszType,"Attribute Primary",17) ) + return SLTAttr; + + else if( EQUALN(papoEntries[iEntry]->pszType,"Attribute Secondary",17) ) + return SLTAttr; + + else if( EQUAL(papoEntries[iEntry]->pszType,"Line") + || EQUALN(papoEntries[iEntry]->pszType,"Line ",5) ) + return SLTLine; + + else if( EQUALN(papoEntries[iEntry]->pszType,"Point-Node",10) ) + return SLTPoint; + + else if( EQUALN(papoEntries[iEntry]->pszType,"Polygon",7) ) + return SLTPoly; + + else if( EQUALN(papoEntries[iEntry]->pszType,"Cell",4) ) + return SLTRaster; + + else + return SLTUnknown; +} + +/************************************************************************/ +/* GetEntryFilePath() */ +/************************************************************************/ + +/** + * Fetch the full filename of the requested module. + * + * @param iEntry The module index within the CATD catalog. A number from + * zero to GetEntryCount()-1. + * + * @return A pointer to an internal string containing the filename. This + * string should not be altered, or freed by the application. + */ + +const char * SDTS_CATD::GetEntryFilePath( int iEntry ) + +{ + if( iEntry < 0 || iEntry >= nEntries ) + return NULL; + else + return papoEntries[iEntry]->pszFullPath; +} diff --git a/bazaar/plugin/gdal/frmts/sdts/sdtsdataset.cpp b/bazaar/plugin/gdal/frmts/sdts/sdtsdataset.cpp new file mode 100644 index 000000000..66df9a79a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdtsdataset.cpp @@ -0,0 +1,416 @@ +/****************************************************************************** + * $Id: sdtsdataset.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: SDTS Translator + * Purpose: GDALDataset driver for SDTS Raster translator. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" +#include "gdal_pam.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: sdtsdataset.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/** + \file sdtsdataset.cpp + + exclude +*/ + +CPL_C_START +void GDALRegister_SDTS(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* SDTSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class SDTSRasterBand; + +class SDTSDataset : public GDALPamDataset +{ + friend class SDTSRasterBand; + + SDTSTransfer *poTransfer; + SDTSRasterReader *poRL; + + char *pszProjection; + + public: + virtual ~SDTSDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * ); +}; + +class SDTSRasterBand : public GDALPamRasterBand +{ + friend class SDTSDataset; + + SDTSRasterReader *poRL; + + public: + + SDTSRasterBand( SDTSDataset *, int, SDTSRasterReader * ); + + virtual CPLErr IReadBlock( int, int, void * ); + + virtual double GetNoDataValue( int *pbSuccess ); + virtual const char *GetUnitType(); +}; + + +/************************************************************************/ +/* ~SDTSDataset() */ +/************************************************************************/ + +SDTSDataset::~SDTSDataset() + +{ + FlushCache(); + + if( poTransfer != NULL ) + delete poTransfer; + + if( poRL != NULL ) + delete poRL; + + if( pszProjection != NULL ) + CPLFree( pszProjection ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *SDTSDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Before trying SDTSOpen() we first verify that the first */ +/* record is in fact a SDTS file descriptor record. */ +/* -------------------------------------------------------------------- */ + char *pachLeader = (char *) poOpenInfo->pabyHeader; + + if( poOpenInfo->nHeaderBytes < 24 ) + return NULL; + + if( pachLeader[5] != '1' && pachLeader[5] != '2' && pachLeader[5] != '3' ) + return NULL; + + if( pachLeader[6] != 'L' ) + return NULL; + + if( pachLeader[8] != '1' && pachLeader[8] != ' ' ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Try opening the dataset. */ +/* -------------------------------------------------------------------- */ + SDTSTransfer *poTransfer = new SDTSTransfer; + + if( !poTransfer->Open( poOpenInfo->pszFilename ) ) + { + delete poTransfer; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + delete poTransfer; + CPLError( CE_Failure, CPLE_NotSupported, + "The SDTS driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Find the first raster layer. If there are none, abort */ +/* returning an error. */ +/* -------------------------------------------------------------------- */ + SDTSRasterReader *poRL = NULL; + + for( i = 0; i < poTransfer->GetLayerCount(); i++ ) + { + if( poTransfer->GetLayerType( i ) == SLTRaster ) + { + poRL = poTransfer->GetLayerRasterReader( i ); + break; + } + } + + if( poRL == NULL ) + { + delete poTransfer; + + CPLError( CE_Warning, CPLE_AppDefined, + "%s is an SDTS transfer, but has no raster cell layers.\n" + "Perhaps it is a vector transfer?\n", + poOpenInfo->pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Initialize a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + SDTSDataset *poDS = new SDTSDataset(); + + poDS->poTransfer = poTransfer; + poDS->poRL = poRL; + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = poRL->GetXSize(); + poDS->nRasterYSize = poRL->GetYSize(); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = 1; + poDS->papoBands = (GDALRasterBand **) + VSICalloc(sizeof(GDALRasterBand *),poDS->nBands); + + for( i = 0; i < poDS->nBands; i++ ) + poDS->SetBand( i+1, new SDTSRasterBand( poDS, i+1, poRL ) ); + +/* -------------------------------------------------------------------- */ +/* Try to establish the projection string. For now we only */ +/* support UTM and GEO. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + SDTS_XREF *poXREF = poTransfer->GetXREF(); + + if( EQUAL(poXREF->pszSystemName,"UTM") ) + { + oSRS.SetUTM( poXREF->nZone ); + } + else if( EQUAL(poXREF->pszSystemName,"GEO") ) + { + /* we set datum later */ + } + else + oSRS.SetLocalCS( poXREF->pszSystemName ); + + if( oSRS.IsLocal() ) + /* don't try to set datum. */; + else if( EQUAL(poXREF->pszDatum,"NAS") ) + oSRS.SetWellKnownGeogCS( "NAD27" ); + else if( EQUAL(poXREF->pszDatum, "NAX") ) + oSRS.SetWellKnownGeogCS( "NAD83" ); + else if( EQUAL(poXREF->pszDatum, "WGC") ) + oSRS.SetWellKnownGeogCS( "WGS72" ); + else if( EQUAL(poXREF->pszDatum, "WGE") ) + oSRS.SetWellKnownGeogCS( "WGS84" ); + else + oSRS.SetWellKnownGeogCS( "WGS84" ); + + oSRS.Fixup(); + + poDS->pszProjection = NULL; + if( oSRS.exportToWkt( &poDS->pszProjection ) != OGRERR_NONE ) + poDS->pszProjection = CPLStrdup(""); + + +/* -------------------------------------------------------------------- */ +/* Get metadata from the IDEN file. */ +/* -------------------------------------------------------------------- */ + const char* pszIDENFilePath = poTransfer->GetCATD()->GetModuleFilePath("IDEN"); + if (pszIDENFilePath) + { + DDFModule oIDENFile; + if( oIDENFile.Open( pszIDENFilePath ) ) + { + DDFRecord* poRecord; + + while( (poRecord = oIDENFile.ReadRecord()) != NULL ) + { + + if( poRecord->GetStringSubfield( "IDEN", 0, "MODN", 0 ) == NULL ) + continue; + + static const char* fields[][2] = { { "TITL", "TITLE" }, + { "DAID", "DATASET_ID" }, + { "DAST", "DATA_STRUCTURE" }, + { "MPDT", "MAP_DATE" }, + { "DCDT", "DATASET_CREATION_DATE" } }; + + for (i = 0; i < (int)sizeof(fields) / (int)sizeof(fields[0]) ; i++) + { + const char* pszFieldValue = + poRecord->GetStringSubfield( "IDEN", 0, fields[i][0], 0 ); + if ( pszFieldValue ) + poDS->SetMetadataItem(fields[i][1], pszFieldValue); + } + + break; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for external overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr SDTSDataset::GetGeoTransform( double * padfTransform ) + +{ + if( poRL->GetTransform( padfTransform ) ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *SDTSDataset::GetProjectionRef() + +{ + return pszProjection; +} + +/************************************************************************/ +/* ==================================================================== */ +/* SDTSRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* SDTSRasterBand() */ +/************************************************************************/ + +SDTSRasterBand::SDTSRasterBand( SDTSDataset *poDS, int nBand, + SDTSRasterReader * poRL ) + +{ + this->poDS = poDS; + this->nBand = nBand; + this->poRL = poRL; + + if( poRL->GetRasterType() == SDTS_RT_INT16 ) + eDataType = GDT_Int16; + else + eDataType = GDT_Float32; + + nBlockXSize = poRL->GetBlockXSize(); + nBlockYSize = poRL->GetBlockYSize(); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr SDTSRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + if( poRL->GetBlock( nBlockXOff, nBlockYOff, pImage ) ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double SDTSRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return -32766.0; +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +const char *SDTSRasterBand::GetUnitType() + +{ + if( EQUAL(poRL->szUNITS,"FEET") ) + return "ft"; + else if( EQUALN(poRL->szUNITS,"MET",3) ) + return "m"; + else + return poRL->szUNITS; +} + +/************************************************************************/ +/* GDALRegister_SDTS() */ +/************************************************************************/ + +void GDALRegister_SDTS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "SDTS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SDTS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "SDTS Raster" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#SDTS" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "ddf" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = SDTSDataset::Open; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/sdts/sdtsindexedreader.cpp b/bazaar/plugin/gdal/frmts/sdts/sdtsindexedreader.cpp new file mode 100644 index 000000000..3c10ea951 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdtsindexedreader.cpp @@ -0,0 +1,269 @@ +/****************************************************************************** + * $Id: sdtsindexedreader.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: SDTS Translator + * Purpose: Implmementation of SDTSIndexedReader class. This base class for + * various reader classes provides indexed caching of features for + * quick fetching when assembling composite features for other + * readers. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" + +CPL_CVSID("$Id: sdtsindexedreader.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* SDTSIndexedReader() */ +/************************************************************************/ + +SDTSIndexedReader::SDTSIndexedReader() + +{ + nIndexSize = 0; + papoFeatures = NULL; + iCurrentFeature = 0; +} + +/************************************************************************/ +/* ~SDTSIndexedReader() */ +/************************************************************************/ + +SDTSIndexedReader::~SDTSIndexedReader() + +{ + ClearIndex(); +} + +/************************************************************************/ +/* IsIndexed() */ +/************************************************************************/ + +/** + Returns TRUE if the module is indexed, otherwise it returns FALSE. + + If the module is indexed all the feature have already been read into + memory, and searches based on the record number can be performed + efficiently. + */ + +int SDTSIndexedReader::IsIndexed() + +{ + return nIndexSize != 0; +} + +/************************************************************************/ +/* ClearIndex() */ +/************************************************************************/ + +/** + Free all features in the index (if filled). + + After this the reader is considered to not be indexed, and IsIndexed() + will return FALSE untill the index is forcably filled again. + */ + +void SDTSIndexedReader::ClearIndex() + +{ + for( int i = 0; i < nIndexSize; i++ ) + { + if( papoFeatures[i] != NULL ) + delete papoFeatures[i]; + } + + CPLFree( papoFeatures ); + + papoFeatures = NULL; + nIndexSize = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +/** + Fetch the next available feature from this reader. + + The returned SDTSFeature * is to an internal indexed object if the + IsIndexed() method returns TRUE, otherwise the returned feature becomes the + responsibility of the caller to destroy with delete. + + Note that the Rewind() method can be used to start over at the beginning of + the modules feature list. + + @return next feature, or NULL if no more are left. Please review above + ownership/delete semantics. + + */ + +SDTSFeature *SDTSIndexedReader::GetNextFeature() + +{ + if( nIndexSize == 0 ) + return GetNextRawFeature(); + else + { + while( iCurrentFeature < nIndexSize ) + { + if( papoFeatures[iCurrentFeature] != NULL ) + return papoFeatures[iCurrentFeature++]; + else + iCurrentFeature++; + } + + return NULL; + } +} + +/************************************************************************/ +/* GetIndexedFeatureRef() */ +/************************************************************************/ + +/** + Fetch a feature based on it's record number. + + This method will forceably fill the feature cache, reading all the + features in the file into memory, if they haven't already been loaded. + The ClearIndex() method can be used to flush this cache when no longer + needed. + + @param iRecordId the record to fetch, normally based on the nRecord + field of an SDTSModId. + + @return a pointer to an internal feature (not to be deleted) or NULL + if there is no matching feature. +*/ + +SDTSFeature *SDTSIndexedReader::GetIndexedFeatureRef( int iRecordId ) + +{ + if( nIndexSize == 0 ) + FillIndex(); + + if( iRecordId < 0 || iRecordId >= nIndexSize ) + return NULL; + else + return papoFeatures[iRecordId]; +} + +/************************************************************************/ +/* FillIndex() */ +/************************************************************************/ + +/** + Read all features into a memory indexed cached. + + The ClearIndex() method can be used to free all indexed features. + FillIndex() does nothing, if an index has already been built. +*/ + +void SDTSIndexedReader::FillIndex() + +{ + SDTSFeature *poFeature; + + if( nIndexSize != 0 ) + return; + + Rewind(); + + while( (poFeature = GetNextRawFeature()) != NULL ) + { + int iRecordId = poFeature->oModId.nRecord; + + CPLAssert( iRecordId < 1000000 ); + if( iRecordId >= 1000000 ) + { + delete poFeature; + continue; + } + + if( iRecordId >= nIndexSize ) + { + int nNewSize = (int) (iRecordId * 1.25 + 100); + + papoFeatures = (SDTSFeature **) + CPLRealloc( papoFeatures, sizeof(void*) * nNewSize); + + for( int i = nIndexSize; i < nNewSize; i++ ) + papoFeatures[i] = NULL; + + nIndexSize = nNewSize; + } + + CPLAssert( papoFeatures[iRecordId] == NULL ); + papoFeatures[iRecordId] = poFeature; + } +} + +/************************************************************************/ +/* ScanModuleReferences() */ +/************************************************************************/ + +/** + Scan an entire SDTS module for record references with the given field + name. + + The fields are required to have a MODN subfield from which the + module is extracted. + + This method is normally used to find all the attribute modules referred + to by a point, line or polygon module to build a unified schema. + + This method will have the side effect of rewinding unindexed readers + because the scanning operation requires reading all records in the module + from disk. + + @param pszFName the field name to search for. By default "ATID" is + used. + + @return a NULL terminated list of module names. Free with CSLDestroy(). +*/ + +char ** SDTSIndexedReader::ScanModuleReferences( const char * pszFName ) + +{ + return SDTSScanModuleReferences( &oDDFModule, pszFName ); +} + +/************************************************************************/ +/* Rewind() */ +/************************************************************************/ + +/** + Rewind so that the next feature returned by GetNextFeature() will be the + first in the module. + +*/ + +void SDTSIndexedReader::Rewind() + +{ + if( nIndexSize != 0 ) + iCurrentFeature = 0; + else + oDDFModule.Rewind(); +} diff --git a/bazaar/plugin/gdal/frmts/sdts/sdtsiref.cpp b/bazaar/plugin/gdal/frmts/sdts/sdtsiref.cpp new file mode 100644 index 000000000..1d03bb475 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdtsiref.cpp @@ -0,0 +1,288 @@ +/****************************************************************************** + * $Id: sdtsiref.cpp 17434 2009-07-23 19:55:45Z chaitanya $ + * + * Project: SDTS Translator + * Purpose: Implementation of SDTS_IREF class for reading IREF module. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" + +CPL_CVSID("$Id: sdtsiref.cpp 17434 2009-07-23 19:55:45Z chaitanya $"); + +/************************************************************************/ +/* SDTS_IREF() */ +/************************************************************************/ + +SDTS_IREF::SDTS_IREF() + +{ + dfXScale = 1.0; + dfYScale = 1.0; + dfXOffset = 0.0; + dfYOffset = 0.0; + dfXRes = 1.0; + dfYRes = 1.0; + + pszXAxisName = CPLStrdup(""); + pszYAxisName = CPLStrdup(""); + pszCoordinateFormat = CPLStrdup(""); +} + +/************************************************************************/ +/* ~SDTS_IREF() */ +/************************************************************************/ + +SDTS_IREF::~SDTS_IREF() +{ + CPLFree( pszXAxisName ); + CPLFree( pszYAxisName ); + CPLFree( pszCoordinateFormat ); +} + +/************************************************************************/ +/* Read() */ +/* */ +/* Read the named file to initialize this structure. */ +/************************************************************************/ + +int SDTS_IREF::Read( const char * pszFilename ) + +{ + DDFModule oIREFFile; + DDFRecord *poRecord; + +/* -------------------------------------------------------------------- */ +/* Open the file, and read the header. */ +/* -------------------------------------------------------------------- */ + if( !oIREFFile.Open( pszFilename ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read the first record, and verify that this is an IREF record. */ +/* -------------------------------------------------------------------- */ + poRecord = oIREFFile.ReadRecord(); + if( poRecord == NULL ) + return FALSE; + + if( poRecord->GetStringSubfield( "IREF", 0, "MODN", 0 ) == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get the labels. */ +/* -------------------------------------------------------------------- */ + CPLFree( pszXAxisName ); + pszXAxisName = CPLStrdup( poRecord->GetStringSubfield( "IREF", 0, + "XLBL", 0 ) ); + CPLFree( pszYAxisName ); + pszYAxisName = CPLStrdup( poRecord->GetStringSubfield( "IREF", 0, + "YLBL", 0 ) ); + +/* -------------------------------------------------------------------- */ +/* Get the coordinate encoding. */ +/* -------------------------------------------------------------------- */ + CPLFree( pszCoordinateFormat ); + pszCoordinateFormat = + CPLStrdup( poRecord->GetStringSubfield( "IREF", 0, "HFMT", 0 ) ); + +/* -------------------------------------------------------------------- */ +/* Get the transformation information, and resolution. */ +/* -------------------------------------------------------------------- */ + dfXScale = poRecord->GetFloatSubfield( "IREF", 0, "SFAX", 0 ); + dfYScale = poRecord->GetFloatSubfield( "IREF", 0, "SFAY", 0 ); + + dfXOffset = poRecord->GetFloatSubfield( "IREF", 0, "XORG", 0 ); + dfYOffset = poRecord->GetFloatSubfield( "IREF", 0, "YORG", 0 ); + + dfXRes = poRecord->GetFloatSubfield( "IREF", 0, "XHRS", 0 ); + dfYRes = poRecord->GetFloatSubfield( "IREF", 0, "YHRS", 0 ); + + nDefaultSADRFormat = EQUAL(pszCoordinateFormat,"BI32"); + + return TRUE; +} + +/************************************************************************/ +/* GetSADRCount() */ +/* */ +/* Return the number of SADR'es in the passed field. */ +/************************************************************************/ + +int SDTS_IREF::GetSADRCount( DDFField * poField ) + +{ + if( nDefaultSADRFormat ) + return poField->GetDataSize() / SDTS_SIZEOF_SADR; + else + return poField->GetRepeatCount(); +} + +/************************************************************************/ +/* GetSADR() */ +/************************************************************************/ + +int SDTS_IREF::GetSADR( DDFField * poField, int nVertices, + double *padfX, double * padfY, double * padfZ ) + +{ +/* -------------------------------------------------------------------- */ +/* For the sake of efficiency we depend on our knowledge that */ +/* the SADR field is a series of bigendian int32's and decode */ +/* them directly. */ +/* -------------------------------------------------------------------- */ + if( nDefaultSADRFormat + && poField->GetFieldDefn()->GetSubfieldCount() == 2 ) + { + GInt32 anXY[2]; + const char *pachRawData = poField->GetData(); + + CPLAssert( poField->GetDataSize() >= nVertices * SDTS_SIZEOF_SADR ); + + for( int iVertex = 0; iVertex < nVertices; iVertex++ ) + { + // we copy to a temp buffer to ensure it is world aligned. + memcpy( anXY, pachRawData, 8 ); + pachRawData += 8; + + // possibly byte swap, and always apply scale factor + padfX[iVertex] = dfXOffset + + dfXScale * ((int) CPL_MSBWORD32( anXY[0] )); + padfY[iVertex] = dfYOffset + + dfYScale * ((int) CPL_MSBWORD32( anXY[1] )); + + padfZ[iVertex] = 0.0; + } + } + +/* -------------------------------------------------------------------- */ +/* This is the generic case. We assume either two or three */ +/* subfields, and treat these as X, Y and Z regardless of */ +/* name. */ +/* -------------------------------------------------------------------- */ + else + { + DDFFieldDefn *poFieldDefn = poField->GetFieldDefn(); + int nBytesRemaining = poField->GetDataSize(); + const char *pachFieldData = poField->GetData(); + int iVertex; + + CPLAssert( poFieldDefn->GetSubfieldCount() == 2 + || poFieldDefn->GetSubfieldCount() == 3 ); + + for( iVertex = 0; iVertex < nVertices; iVertex++ ) + { + double adfXYZ[3]; + GByte *pabyBString; + + adfXYZ[2] = 0.0; + + for( int iEntry = 0; + iEntry < poFieldDefn->GetSubfieldCount(); + iEntry++ ) + { + int nBytesConsumed = 0; + DDFSubfieldDefn *poSF = poFieldDefn->GetSubfield(iEntry); + + switch( poSF->GetType() ) + { + case DDFInt: + adfXYZ[iEntry] = + poSF->ExtractIntData( pachFieldData, + nBytesRemaining, + &nBytesConsumed ); + break; + + case DDFFloat: + adfXYZ[iEntry] = + poSF->ExtractFloatData( pachFieldData, + nBytesRemaining, + &nBytesConsumed ); + break; + + case DDFBinaryString: + pabyBString = (GByte *) + poSF->ExtractStringData( pachFieldData, + nBytesRemaining, + &nBytesConsumed ); + + if( EQUAL(pszCoordinateFormat,"BI32") ) + { + GInt32 nValue; + memcpy( &nValue, pabyBString, 4 ); + adfXYZ[iEntry] = ((int) CPL_MSBWORD32( nValue )); + } + else if( EQUAL(pszCoordinateFormat,"BI16") ) + { + GInt16 nValue; + memcpy( &nValue, pabyBString, 2 ); + adfXYZ[iEntry] = ((int) CPL_MSBWORD16( nValue )); + } + else if( EQUAL(pszCoordinateFormat,"BU32") ) + { + GUInt32 nValue; + memcpy( &nValue, pabyBString, 4 ); + adfXYZ[iEntry] = ((GUInt32) CPL_MSBWORD32( nValue )); + } + else if( EQUAL(pszCoordinateFormat,"BU16") ) + { + GUInt16 nValue; + memcpy( &nValue, pabyBString, 2 ); + adfXYZ[iEntry] = ((GUInt16) CPL_MSBWORD16( nValue )); + } + else if( EQUAL(pszCoordinateFormat,"BFP32") ) + { + float fValue; + + memcpy( &fValue, pabyBString, 4 ); + CPL_MSBPTR32( &fValue ); + adfXYZ[iEntry] = fValue; + } + else if( EQUAL(pszCoordinateFormat,"BFP64") ) + { + double dfValue; + + memcpy( &dfValue, pabyBString, 8 ); + CPL_MSBPTR64( &dfValue ); + adfXYZ[iEntry] = dfValue; + } + break; + + default: + adfXYZ[iEntry] = 0.0; + break; + } + + pachFieldData += nBytesConsumed; + nBytesRemaining -= nBytesConsumed; + } /* next iEntry */ + + padfX[iVertex] = dfXOffset + adfXYZ[0] * dfXScale; + padfY[iVertex] = dfYOffset + adfXYZ[1] * dfYScale; + padfZ[iVertex] = adfXYZ[2]; + } /* next iVertex */ + } + + return TRUE; +} + diff --git a/bazaar/plugin/gdal/frmts/sdts/sdtslib.cpp b/bazaar/plugin/gdal/frmts/sdts/sdtslib.cpp new file mode 100644 index 000000000..e2ef9baf5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdtslib.cpp @@ -0,0 +1,251 @@ +/****************************************************************************** + * $Id: sdtslib.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: SDTS Translator + * Purpose: Various utility functions that apply to all SDTS profiles. + * SDTSModId, and SDTSFeature methods. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: sdtslib.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* SDTSFeature() */ +/************************************************************************/ + +SDTSFeature::SDTSFeature() + +{ + nAttributes = 0; + paoATID = NULL; +} + +/************************************************************************/ +/* SDTSFeature::ApplyATID() */ +/************************************************************************/ + +void SDTSFeature::ApplyATID( DDFField * poField ) + +{ + int nRepeatCount = poField->GetRepeatCount(); + int bUsualFormat; + DDFSubfieldDefn *poMODN; + + poMODN = poField->GetFieldDefn()->FindSubfieldDefn( "MODN" ); + if( poMODN == NULL ) + { + //CPLAssert( FALSE ); + return; + } + + bUsualFormat = poMODN->GetWidth() == 4; + for( int iRepeat = 0; iRepeat < nRepeatCount; iRepeat++ ) + { + paoATID = (SDTSModId *) CPLRealloc(paoATID, + sizeof(SDTSModId)*(nAttributes+1)); + + const char * pabyData; + SDTSModId *poModId = paoATID + nAttributes; + + if( bUsualFormat ) + { + pabyData = poField->GetSubfieldData( poMODN, NULL, iRepeat ); + + memcpy( poModId->szModule, pabyData, 4 ); + poModId->szModule[4] = '\0'; + poModId->nRecord = atoi(pabyData + 4); + poModId->szOBRP[0] = '\0'; + } + else + { + poModId->Set( poField ); + } + + nAttributes++; + } +} + +/************************************************************************/ +/* ~SDTSFeature() */ +/************************************************************************/ + +SDTSFeature::~SDTSFeature() + +{ + CPLFree( paoATID ); + paoATID = NULL; +} + +/************************************************************************/ +/* SDTSModId::Set() */ +/* */ +/* Set a module from a field. We depend on our pre-knowledge */ +/* of the data layout to fetch more efficiently. */ +/************************************************************************/ + +int SDTSModId::Set( DDFField *poField ) + +{ + const char *pachData = poField->GetData(); + DDFFieldDefn *poDefn = poField->GetFieldDefn(); + + if( poDefn->GetSubfieldCount() >= 2 + && poDefn->GetSubfield(0)->GetWidth() == 4 ) + { + memcpy( szModule, pachData, 4 ); + szModule[4] = '\0'; + + nRecord = atoi( pachData + 4 ); + } + else + { + DDFSubfieldDefn *poSF; + int nBytesRemaining; + const char *pachData; + + poSF = poField->GetFieldDefn()->FindSubfieldDefn( "MODN" ); + pachData = poField->GetSubfieldData(poSF, &nBytesRemaining); + strncpy( szModule, + poSF->ExtractStringData( pachData, nBytesRemaining, NULL), + sizeof(szModule) ); + szModule[sizeof(szModule)-1] = '\0'; + + poSF = poField->GetFieldDefn()->FindSubfieldDefn( "RCID" ); + if( poSF != NULL ) + { + pachData = poField->GetSubfieldData(poSF, &nBytesRemaining); + if( pachData != NULL ) + nRecord = poSF->ExtractIntData( pachData, nBytesRemaining, NULL); + } + } + + if( poDefn->GetSubfieldCount() == 3 ) + { + DDFSubfieldDefn *poSF; + + poSF = poField->GetFieldDefn()->FindSubfieldDefn( "OBRP" ); + if( poSF != NULL ) + { + int nBytesRemaining; + const char *pachData; + + pachData = poField->GetSubfieldData(poSF, &nBytesRemaining); + if( pachData != NULL ) + { + strncpy( szOBRP, + poSF->ExtractStringData( pachData, nBytesRemaining, NULL), + sizeof(szOBRP) ); + + szOBRP[sizeof(szOBRP)-1] = '\0'; + } + } + } + + return FALSE; +} + +/************************************************************************/ +/* SDTSModId::GetName() */ +/************************************************************************/ + +const char * SDTSModId::GetName() + +{ + sprintf( szName, "%s:%ld", szModule, nRecord ); + + return szName; +} + +/************************************************************************/ +/* SDTSScanModuleReferences() */ +/* */ +/* Find all modules references by records in this module based */ +/* on a particular field name. That field must be in module */ +/* reference form (contain MODN/RCID subfields). */ +/************************************************************************/ + +char **SDTSScanModuleReferences( DDFModule * poModule, const char * pszFName ) + +{ +/* -------------------------------------------------------------------- */ +/* Identify the field, and subfield we are interested in. */ +/* -------------------------------------------------------------------- */ + DDFFieldDefn *poIDField; + DDFSubfieldDefn *poMODN; + + poIDField = poModule->FindFieldDefn( pszFName ); + + if( poIDField == NULL ) + return NULL; + + poMODN = poIDField->FindSubfieldDefn( "MODN" ); + if( poMODN == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Scan the file. */ +/* -------------------------------------------------------------------- */ + DDFRecord *poRecord; + char **papszModnList = NULL; + + poModule->Rewind(); + while( (poRecord = poModule->ReadRecord()) != NULL ) + { + int iField; + + for( iField = 0; iField < poRecord->GetFieldCount(); iField++ ) + { + DDFField *poField = poRecord->GetField( iField ); + + if( poField->GetFieldDefn() == poIDField ) + { + const char *pszModName; + int i; + + for( i = 0; i < poField->GetRepeatCount(); i++ ) + { + char szName[5]; + + pszModName = poField->GetSubfieldData(poMODN,NULL,i); + + strncpy( szName, pszModName, 4 ); + szName[4] = '\0'; + + if( CSLFindString( papszModnList, szName ) == -1 ) + papszModnList = CSLAddString( papszModnList, szName ); + } + } + } + } + + poModule->Rewind(); + + return papszModnList; +} + + diff --git a/bazaar/plugin/gdal/frmts/sdts/sdtslinereader.cpp b/bazaar/plugin/gdal/frmts/sdts/sdtslinereader.cpp new file mode 100644 index 000000000..1d7ad754e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdtslinereader.cpp @@ -0,0 +1,357 @@ +/****************************************************************************** + * $Id: sdtslinereader.cpp 28039 2014-11-30 18:24:59Z rouault $ + * + * Project: SDTS Translator + * Purpose: Implementation of SDTSLineReader and SDTSRawLine classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" + +CPL_CVSID("$Id: sdtslinereader.cpp 28039 2014-11-30 18:24:59Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* SDTSRawLine */ +/* */ +/* This is a simple class for holding the data related with a */ +/* line feature. */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* SDTSRawLine() */ +/************************************************************************/ + +SDTSRawLine::SDTSRawLine() + +{ + nVertices = 0; + padfX = padfY = padfZ = NULL; + nAttributes = 0; +} + +/************************************************************************/ +/* ~STDSRawLine() */ +/************************************************************************/ + +SDTSRawLine::~SDTSRawLine() + +{ + CPLFree( padfX ); +} + +/************************************************************************/ +/* Read() */ +/* */ +/* Read a record from the passed SDTSLineReader, and assign the */ +/* values from that record to this line. This is the bulk of */ +/* the work in this whole file. */ +/************************************************************************/ + +int SDTSRawLine::Read( SDTS_IREF * poIREF, DDFRecord * poRecord ) + +{ + // E.Rouault: Not sure if this test is really useful + if( poRecord->GetStringSubfield( "LINE", 0, "MODN", 0 ) == NULL ) + return FALSE; + +/* ==================================================================== */ +/* Loop over fields in this record, looking for those we */ +/* recognise, and need. I don't use the getSubfield() */ +/* interface on the record in order to retain some slight bit */ +/* of efficiency. */ +/* ==================================================================== */ + for( int iField = 0; iField < poRecord->GetFieldCount(); iField++ ) + { + DDFField *poField = poRecord->GetField( iField ); + const char *pszFieldName; + + CPLAssert( poField != NULL ); + pszFieldName = poField->GetFieldDefn()->GetName(); + + if( EQUAL(pszFieldName,"LINE") ) + oModId.Set( poField ); + + else if( EQUAL(pszFieldName,"ATID") ) + ApplyATID( poField ); + + else if( EQUAL(pszFieldName,"PIDL") ) + oLeftPoly.Set( poField ); + + else if( EQUAL(pszFieldName,"PIDR") ) + oRightPoly.Set( poField ); + + else if( EQUAL(pszFieldName,"SNID") ) + oStartNode.Set( poField ); + + else if( EQUAL(pszFieldName,"ENID") ) + oEndNode.Set( poField ); + + else if( EQUAL(pszFieldName,"SADR") ) + { + nVertices = poIREF->GetSADRCount( poField ); + + padfX = (double*) CPLRealloc(padfX,sizeof(double)*nVertices*3); + padfY = padfX + nVertices; + padfZ = padfX + 2*nVertices; + + poIREF->GetSADR( poField, nVertices, padfX, padfY, padfZ ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* Dump() */ +/* */ +/* Write info about this object to a text file. */ +/************************************************************************/ + +void SDTSRawLine::Dump( FILE * fp ) + +{ + int i; + + fprintf( fp, "SDTSRawLine\n" ); + fprintf( fp, " Module=%s, Record#=%ld\n", + oModId.szModule, oModId.nRecord ); + if( oLeftPoly.nRecord != -1 ) + fprintf( fp, " LeftPoly (Module=%s, Record=%ld)\n", + oLeftPoly.szModule, oLeftPoly.nRecord ); + if( oRightPoly.nRecord != -1 ) + fprintf( fp, " RightPoly (Module=%s, Record=%ld)\n", + oRightPoly.szModule, oRightPoly.nRecord ); + if( oStartNode.nRecord != -1 ) + fprintf( fp, " StartNode (Module=%s, Record=%ld)\n", + oStartNode.szModule, oStartNode.nRecord ); + if( oEndNode.nRecord != -1 ) + fprintf( fp, " EndNode (Module=%s, Record=%ld)\n", + oEndNode.szModule, oEndNode.nRecord ); + for( i = 0; i < nAttributes; i++ ) + fprintf( fp, " Attribute (Module=%s, Record=%ld)\n", + paoATID[i].szModule, paoATID[i].nRecord ); + + for( i = 0; i < nVertices; i++ ) + { + fprintf( fp, " Vertex[%3d] = (%.2f,%.2f,%.2f)\n", + i, padfX[i], padfY[i], padfZ[i] ); + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* SDTSLineReader */ +/* */ +/* This is the class used to read a line module. */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* SDTSLineReader() */ +/************************************************************************/ + +SDTSLineReader::SDTSLineReader( SDTS_IREF * poIREFIn ) + +{ + poIREF = poIREFIn; +} + +/************************************************************************/ +/* ~SDTSLineReader() */ +/************************************************************************/ + +SDTSLineReader::~SDTSLineReader() +{ + Close(); +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +void SDTSLineReader::Close() + +{ + oDDFModule.Close(); +} + +/************************************************************************/ +/* Open() */ +/* */ +/* Open the requested line file, and prepare to start reading */ +/* data records. */ +/************************************************************************/ + +int SDTSLineReader::Open( const char * pszFilename ) + +{ + return( oDDFModule.Open( pszFilename ) ); +} + +/************************************************************************/ +/* GetNextLine() */ +/* */ +/* Fetch the next line feature as an STDSRawLine. */ +/************************************************************************/ + +SDTSRawLine * SDTSLineReader::GetNextLine() + +{ +/* -------------------------------------------------------------------- */ +/* Are we initialized? */ +/* -------------------------------------------------------------------- */ + if( oDDFModule.GetFP() == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Read the record. */ +/* -------------------------------------------------------------------- */ + DDFRecord *poRecord = oDDFModule.ReadRecord(); + + if( poRecord == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Transform into a line feature. */ +/* -------------------------------------------------------------------- */ + SDTSRawLine *poRawLine = new SDTSRawLine(); + + if( poRawLine->Read( poIREF, poRecord ) ) + { + return( poRawLine ); + } + else + { + delete poRawLine; + return NULL; + } +} + +/************************************************************************/ +/* AttachToPolygons() */ +/* */ +/* Attach line features to all the polygon features they relate */ +/* to. */ +/************************************************************************/ + +/** + Attach lines in this module to their polygons as the first step in + polygon formation. + + See also the SDTSRawPolygon::AssembleRings() method. + + @param poTransfer the SDTSTransfer of this SDTSLineReader, and from + which the related SDTSPolygonReader will be instantiated. + @param iTargetPolyLayer the polygon reader instance number, used to avoid + processing lines for other layers. + +*/ + +void SDTSLineReader::AttachToPolygons( SDTSTransfer * poTransfer, + int iTargetPolyLayer ) + +{ +/* -------------------------------------------------------------------- */ +/* We force a filling of the index because when we attach the */ +/* lines we are just providing a pointer back to the line */ +/* features in this readers index. If they aren't cached in */ +/* the index then the pointer will be invalid. */ +/* -------------------------------------------------------------------- */ + FillIndex(); + +/* ==================================================================== */ +/* Loop over all lines, attaching them to the polygons they */ +/* have as right and left faces. */ +/* ==================================================================== */ + SDTSRawLine *poLine; + SDTSPolygonReader *poPolyReader = NULL; + + Rewind(); + while( (poLine = (SDTSRawLine *) GetNextFeature()) != NULL ) + { +/* -------------------------------------------------------------------- */ +/* Skip lines with the same left and right polygon face. These */ +/* are dangles, and will not contribute in any useful fashion */ +/* to the resulting polygon. */ +/* -------------------------------------------------------------------- */ + if( poLine->oLeftPoly.nRecord == poLine->oRightPoly.nRecord ) + continue; + +/* -------------------------------------------------------------------- */ +/* If we don't have our indexed polygon reader yet, try to get */ +/* it now. */ +/* -------------------------------------------------------------------- */ + if( poPolyReader == NULL ) + { + int iPolyLayer = -1; + + if( poLine->oLeftPoly.nRecord != -1 ) + { + iPolyLayer = poTransfer->FindLayer(poLine->oLeftPoly.szModule); + } + else if( poLine->oRightPoly.nRecord != -1 ) + { + iPolyLayer = poTransfer->FindLayer(poLine->oRightPoly.szModule); + } + + if( iPolyLayer == -1 ) + continue; + + if( iPolyLayer != iTargetPolyLayer ) + continue; + + poPolyReader = (SDTSPolygonReader *) + poTransfer->GetLayerIndexedReader(iPolyLayer); + + if( poPolyReader == NULL ) + return; + } + +/* -------------------------------------------------------------------- */ +/* Attach line to right and/or left polygons. */ +/* -------------------------------------------------------------------- */ + if( poLine->oLeftPoly.nRecord != -1 ) + { + SDTSRawPolygon *poPoly; + + poPoly = (SDTSRawPolygon *) poPolyReader->GetIndexedFeatureRef( + poLine->oLeftPoly.nRecord ); + if( poPoly != NULL ) + poPoly->AddEdge( poLine ); + } + + if( poLine->oRightPoly.nRecord != -1 ) + { + SDTSRawPolygon *poPoly; + + poPoly = (SDTSRawPolygon *) poPolyReader->GetIndexedFeatureRef( + poLine->oRightPoly.nRecord ); + + if( poPoly != NULL ) + poPoly->AddEdge( poLine ); + } + } +} diff --git a/bazaar/plugin/gdal/frmts/sdts/sdtspointreader.cpp b/bazaar/plugin/gdal/frmts/sdts/sdtspointreader.cpp new file mode 100644 index 000000000..0d5ad2466 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdtspointreader.cpp @@ -0,0 +1,211 @@ +/****************************************************************************** + * $Id: sdtspointreader.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: SDTS Translator + * Purpose: Implementation of SDTSPointReader and SDTSRawPoint classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" + +CPL_CVSID("$Id: sdtspointreader.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* ==================================================================== */ +/* SDTSRawPoint */ +/* */ +/* This is a simple class for holding the data related with a */ +/* point feature. */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* SDTSRawPoint() */ +/************************************************************************/ + +SDTSRawPoint::SDTSRawPoint() + +{ + nAttributes = 0; +} + +/************************************************************************/ +/* ~STDSRawPoint() */ +/************************************************************************/ + +SDTSRawPoint::~SDTSRawPoint() + +{ +} + +/************************************************************************/ +/* Read() */ +/* */ +/* Read a record from the passed SDTSPointReader, and assign the */ +/* values from that record to this point. This is the bulk of */ +/* the work in this whole file. */ +/************************************************************************/ + +int SDTSRawPoint::Read( SDTS_IREF * poIREF, DDFRecord * poRecord ) + +{ +/* ==================================================================== */ +/* Loop over fields in this record, looking for those we */ +/* recognise, and need. */ +/* ==================================================================== */ + for( int iField = 0; iField < poRecord->GetFieldCount(); iField++ ) + { + DDFField *poField = poRecord->GetField( iField ); + const char *pszFieldName; + + CPLAssert( poField != NULL ); + pszFieldName = poField->GetFieldDefn()->GetName(); + + if( EQUAL(pszFieldName,"PNTS") ) + oModId.Set( poField ); + + else if( EQUAL(pszFieldName,"ATID") ) + ApplyATID( poField ); + + else if( EQUAL(pszFieldName,"ARID") ) + { + oAreaId.Set( poField ); + } + else if( EQUAL(pszFieldName,"SADR") ) + { + poIREF->GetSADR( poField, 1, &dfX, &dfY, &dfZ ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +void SDTSRawPoint::Dump( FILE * fp ) + +{ + int i; + + fprintf( fp, "SDTSRawPoint %s: ", oModId.GetName() ); + + if( oAreaId.nRecord != -1 ) + fprintf( fp, " AreaId=%s", oAreaId.GetName() ); + + for( i = 0; i < nAttributes; i++ ) + fprintf( fp, " ATID[%d]=%s", i, paoATID[i].GetName() ); + + fprintf( fp, " Vertex = (%.2f,%.2f,%.2f)\n", dfX, dfY, dfZ ); +} + + +/************************************************************************/ +/* ==================================================================== */ +/* SDTSPointReader */ +/* */ +/* This is the class used to read a point module. */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* SDTSPointReader() */ +/************************************************************************/ + +SDTSPointReader::SDTSPointReader( SDTS_IREF * poIREFIn ) + +{ + poIREF = poIREFIn; +} + +/************************************************************************/ +/* ~SDTSLineReader() */ +/************************************************************************/ + +SDTSPointReader::~SDTSPointReader() +{ +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +void SDTSPointReader::Close() + +{ + oDDFModule.Close(); +} + +/************************************************************************/ +/* Open() */ +/* */ +/* Open the requested line file, and prepare to start reading */ +/* data records. */ +/************************************************************************/ + +int SDTSPointReader::Open( const char * pszFilename ) + +{ + return( oDDFModule.Open( pszFilename ) ); +} + +/************************************************************************/ +/* GetNextPoint() */ +/* */ +/* Fetch the next feature as an STDSRawPoint. */ +/************************************************************************/ + +SDTSRawPoint * SDTSPointReader::GetNextPoint() + +{ + DDFRecord *poRecord; + +/* -------------------------------------------------------------------- */ +/* Read a record. */ +/* -------------------------------------------------------------------- */ + if( oDDFModule.GetFP() == NULL ) + return NULL; + + poRecord = oDDFModule.ReadRecord(); + + if( poRecord == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Transform into a point feature. */ +/* -------------------------------------------------------------------- */ + SDTSRawPoint *poRawPoint = new SDTSRawPoint(); + + if( poRawPoint->Read( poIREF, poRecord ) ) + { + return( poRawPoint ); + } + else + { + delete poRawPoint; + return NULL; + } +} + diff --git a/bazaar/plugin/gdal/frmts/sdts/sdtspolygonreader.cpp b/bazaar/plugin/gdal/frmts/sdts/sdtspolygonreader.cpp new file mode 100644 index 000000000..ee6f56f29 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdtspolygonreader.cpp @@ -0,0 +1,638 @@ +/****************************************************************************** + * $Id: sdtspolygonreader.cpp 25138 2012-10-15 23:12:05Z rouault $ + * + * Project: SDTS Translator + * Purpose: Implementation of SDTSPolygonReader and SDTSRawPolygon classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" + +CPL_CVSID("$Id: sdtspolygonreader.cpp 25138 2012-10-15 23:12:05Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* SDTSRawPolygon */ +/* */ +/* This is a simple class for holding the data related with a */ +/* polygon feature. */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* SDTSRawPolygon() */ +/************************************************************************/ + +SDTSRawPolygon::SDTSRawPolygon() + +{ + nAttributes = 0; + nEdges = nRings = nVertices = 0; + papoEdges = NULL; + + panRingStart = NULL; + padfX = padfY = padfZ = NULL; +} + +/************************************************************************/ +/* ~SDTSRawPolygon() */ +/************************************************************************/ + +SDTSRawPolygon::~SDTSRawPolygon() + +{ + CPLFree( papoEdges ); + CPLFree( panRingStart ); + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); +} + +/************************************************************************/ +/* Read() */ +/* */ +/* Read a record from the passed SDTSPolygonReader, and assign the */ +/* values from that record to this object. This is the bulk of */ +/* the work in this whole file. */ +/************************************************************************/ + +int SDTSRawPolygon::Read( DDFRecord * poRecord ) + +{ +/* ==================================================================== */ +/* Loop over fields in this record, looking for those we */ +/* recognise, and need. */ +/* ==================================================================== */ + for( int iField = 0; iField < poRecord->GetFieldCount(); iField++ ) + { + DDFField *poField = poRecord->GetField( iField ); + const char *pszFieldName; + + CPLAssert( poField != NULL ); + pszFieldName = poField->GetFieldDefn()->GetName(); + + if( EQUAL(pszFieldName,"POLY") ) + { + oModId.Set( poField ); + } + + else if( EQUAL(pszFieldName,"ATID") ) + { + ApplyATID( poField ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* AddEdge() */ +/************************************************************************/ + +void SDTSRawPolygon::AddEdge( SDTSRawLine * poNewLine ) + +{ + nEdges++; + + papoEdges = (SDTSRawLine **) CPLRealloc(papoEdges, sizeof(void*)*nEdges ); + papoEdges[nEdges-1] = poNewLine; +} + +/************************************************************************/ +/* AddEdgeToRing() */ +/************************************************************************/ + +void SDTSRawPolygon::AddEdgeToRing( int nVertToAdd, + double * padfXToAdd, + double * padfYToAdd, + double * padfZToAdd, + int bReverse, int bDropVertex ) + +{ + int iStart=0, iEnd=nVertToAdd-1, iStep=1; + + if( bDropVertex && bReverse ) + { + iStart = nVertToAdd - 2; + iEnd = 0; + iStep = -1; + } + else if( bDropVertex && !bReverse ) + { + iStart = 1; + iEnd = nVertToAdd - 1; + iStep = 1; + } + else if( !bDropVertex && !bReverse ) + { + iStart = 0; + iEnd = nVertToAdd - 1; + iStep = 1; + } + else if( !bDropVertex && bReverse ) + { + iStart = nVertToAdd - 1; + iEnd = 0; + iStep = -1; + } + + for( int i = iStart; i != (iEnd+iStep); i += iStep ) + { + padfX[nVertices] = padfXToAdd[i]; + padfY[nVertices] = padfYToAdd[i]; + padfZ[nVertices] = padfZToAdd[i]; + + nVertices++; + } +} + +/************************************************************************/ +/* AssembleRings() */ +/************************************************************************/ + +/** + * Form border lines (arcs) into outer and inner rings. + * + * See SDTSPolygonReader::AssemblePolygons() for a simple one step process + * to assembling geometry for all polygons in a transfer. + * + * This method will assemble the lines attached to a polygon into + * an outer ring, and zero or more inner rings. Before calling it is + * necessary that all the lines associated with this polygon have already + * been attached. Normally this is accomplished by calling + * SDTSLineReader::AttachToPolygons() on all line layers that might + * contain edges related to this layer. + * + * This method then forms the lines into rings. Rings are formed by: + *
      + *
    1. Take a previously unconsumed line, and start a ring with it. Mark + * it as consumed, and keep track of it's start and end node ids as + * being the start and end node ids of the ring. + *
    2. If the rings start id is the same as the end node id then this ring + * is completely formed, return to step 1. + *
    3. Search all unconsumed lines for a line with the same start or end + * node id as the rings current node id. If none are found then the + * assembly has failed. Return to step 1 but report failure on + * completion. + *
    4. Once found, add the line to the current ring, dropping the duplicated + * vertex and reverse order if necessary. Mark the line as consumed, + * and update the rings end node id accordingly. + *
    5. go to step 2. + *
    + * + * Once ring assembly from lines is complete, another pass is made to + * order the rings such that the exterior ring is first, the first ring + * has counter-clockwise vertex ordering and the inner rings have clockwise + * vertex ordering. This is accomplished based on the assumption that the + * outer ring has the largest area, and using the +/- sign of area to establish + * direction of rings. + * + * @return TRUE if all rings assembled without problems or FALSE if a problem + * occured. If a problem occurs rings are still formed from all lines, but + * some of the rings will not be closed, and rings will have no particular + * order or direction. + */ + +int SDTSRawPolygon::AssembleRings() + +{ + int iEdge; + int bSuccess = TRUE; + + if( nRings > 0 ) + return TRUE; + + if( nEdges == 0 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Allocate ring arrays. */ +/* -------------------------------------------------------------------- */ + panRingStart = (int *) CPLMalloc(sizeof(int) * nEdges); + + nVertices = 0; + for( iEdge = 0; iEdge < nEdges; iEdge++ ) + { + nVertices += papoEdges[iEdge]->nVertices; + } + + padfX = (double *) CPLMalloc(sizeof(double) * nVertices); + padfY = (double *) CPLMalloc(sizeof(double) * nVertices); + padfZ = (double *) CPLMalloc(sizeof(double) * nVertices); + + nVertices = 0; + +/* -------------------------------------------------------------------- */ +/* Setup array of line markers indicating if they have been */ +/* added to a ring yet. */ +/* -------------------------------------------------------------------- */ + int *panEdgeConsumed, nRemainingEdges = nEdges; + + panEdgeConsumed = (int *) CPLCalloc(sizeof(int),nEdges); + +/* ==================================================================== */ +/* Loop generating rings. */ +/* ==================================================================== */ + while( nRemainingEdges > 0 ) + { + int nStartNode, nLinkNode; + +/* -------------------------------------------------------------------- */ +/* Find the first unconsumed edge. */ +/* -------------------------------------------------------------------- */ + SDTSRawLine *poEdge; + + for( iEdge = 0; panEdgeConsumed[iEdge]; iEdge++ ) {} + + poEdge = papoEdges[iEdge]; + +/* -------------------------------------------------------------------- */ +/* Start a new ring, copying in the current line directly */ +/* -------------------------------------------------------------------- */ + panRingStart[nRings++] = nVertices; + + AddEdgeToRing( poEdge->nVertices, + poEdge->padfX, poEdge->padfY, poEdge->padfZ, + FALSE, FALSE ); + + panEdgeConsumed[iEdge] = TRUE; + nRemainingEdges--; + + nStartNode = poEdge->oStartNode.nRecord; + nLinkNode = poEdge->oEndNode.nRecord; + +/* ==================================================================== */ +/* Loop adding edges to this ring until we make a whole pass */ +/* within finding anything to add. */ +/* ==================================================================== */ + int bWorkDone = TRUE; + + while( nLinkNode != nStartNode + && nRemainingEdges > 0 + && bWorkDone ) + { + bWorkDone = FALSE; + + for( iEdge = 0; iEdge < nEdges; iEdge++ ) + { + if( panEdgeConsumed[iEdge] ) + continue; + + poEdge = papoEdges[iEdge]; + if( poEdge->oStartNode.nRecord == nLinkNode ) + { + AddEdgeToRing( poEdge->nVertices, + poEdge->padfX, poEdge->padfY, poEdge->padfZ, + FALSE, TRUE ); + nLinkNode = poEdge->oEndNode.nRecord; + } + else if( poEdge->oEndNode.nRecord == nLinkNode ) + { + AddEdgeToRing( poEdge->nVertices, + poEdge->padfX, poEdge->padfY, poEdge->padfZ, + TRUE, TRUE ); + nLinkNode = poEdge->oStartNode.nRecord; + } + else + { + continue; + } + + panEdgeConsumed[iEdge] = TRUE; + nRemainingEdges--; + bWorkDone = TRUE; + } + } + +/* -------------------------------------------------------------------- */ +/* Did we fail to complete the ring? */ +/* -------------------------------------------------------------------- */ + if( nLinkNode != nStartNode ) + bSuccess = FALSE; + + } /* next ring */ + + CPLFree( panEdgeConsumed ); + + if( !bSuccess ) + return bSuccess; + +/* ==================================================================== */ +/* Compute the area of each ring. The sign will be positive */ +/* for counter clockwise rings, otherwise negative. */ +/* */ +/* The algorithm used in this function was taken from _Graphics */ +/* Gems II_, James Arvo, 1991, Academic Press, Inc., section 1.1, */ +/* "The Area of a Simple Polygon", Jon Rokne, pp. 5-6. */ +/* ==================================================================== */ + double *padfRingArea, dfMaxArea = 0.0; + int iRing, iBiggestRing = -1; + + padfRingArea = (double *) CPLCalloc(sizeof(double),nRings); + + for( iRing = 0; iRing < nRings; iRing++ ) + { + double dfSum1 = 0.0, dfSum2 = 0.0; + int i, nRingVertices; + + if( iRing == nRings - 1 ) + nRingVertices = nVertices - panRingStart[iRing]; + else + nRingVertices = panRingStart[iRing+1] - panRingStart[iRing]; + + for( i = panRingStart[iRing]; + i < panRingStart[iRing] + nRingVertices - 1; + i++) + { + dfSum1 += padfX[i] * padfY[i+1]; + dfSum2 += padfY[i] * padfX[i+1]; + } + + padfRingArea[iRing] = (dfSum1 - dfSum2) / 2; + + if( ABS(padfRingArea[iRing]) > dfMaxArea ) + { + dfMaxArea = ABS(padfRingArea[iRing]); + iBiggestRing = iRing; + } + } + + if( iBiggestRing < 0 ) + { + CPLFree(padfRingArea); + return FALSE; + } + +/* ==================================================================== */ +/* Make a new set of vertices, and copy the largest ring into */ +/* it, adjusting the direction if necessary to ensure that this */ +/* outer ring is counter clockwise. */ +/* ==================================================================== */ + double *padfXRaw = padfX; + double *padfYRaw = padfY; + double *padfZRaw = padfZ; + int *panRawRingStart = panRingStart; + int nRawVertices = nVertices; + int nRawRings = nRings; + int nRingVertices; + + padfX = (double *) CPLMalloc(sizeof(double) * nVertices); + padfY = (double *) CPLMalloc(sizeof(double) * nVertices); + padfZ = (double *) CPLMalloc(sizeof(double) * nVertices); + panRingStart = (int *) CPLMalloc(sizeof(int) * nRawRings); + nVertices = 0; + nRings = 0; + + if( iBiggestRing == nRawRings - 1 ) + nRingVertices = nRawVertices - panRawRingStart[iBiggestRing]; + else + nRingVertices = + panRawRingStart[iBiggestRing+1] - panRawRingStart[iBiggestRing]; + + panRingStart[nRings++] = 0; + AddEdgeToRing( nRingVertices, + padfXRaw + panRawRingStart[iBiggestRing], + padfYRaw + panRawRingStart[iBiggestRing], + padfZRaw + panRawRingStart[iBiggestRing], + padfRingArea[iBiggestRing] < 0.0, FALSE ); + +/* ==================================================================== */ +/* Add the rest of the rings, which must be holes, in clockwise */ +/* order. */ +/* ==================================================================== */ + for( iRing = 0; iRing < nRawRings; iRing++ ) + { + if( iRing == iBiggestRing ) + continue; + + if( iRing == nRawRings - 1 ) + nRingVertices = nRawVertices - panRawRingStart[iRing]; + else + nRingVertices = panRawRingStart[iRing+1] - panRawRingStart[iRing]; + + panRingStart[nRings++] = nVertices; + AddEdgeToRing( nRingVertices, + padfXRaw + panRawRingStart[iRing], + padfYRaw + panRawRingStart[iRing], + padfZRaw + panRawRingStart[iRing], + padfRingArea[iRing] > 0.0, FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( padfXRaw ); + CPLFree( padfYRaw ); + CPLFree( padfZRaw ); + CPLFree( padfRingArea ); + CPLFree( panRawRingStart ); + + CPLFree( papoEdges ); + papoEdges = NULL; + nEdges = 0; + + return TRUE; +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +void SDTSRawPolygon::Dump( FILE * fp ) + +{ + int i; + + fprintf( fp, "SDTSRawPolygon %s: ", oModId.GetName() ); + + for( i = 0; i < nAttributes; i++ ) + fprintf( fp, " ATID[%d]=%s", i, paoATID[i].GetName() ); + + fprintf( fp, "\n" ); +} + +/************************************************************************/ +/* ==================================================================== */ +/* SDTSPolygonReader */ +/* */ +/* This is the class used to read a Polygon module. */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* SDTSPolygonReader() */ +/************************************************************************/ + +SDTSPolygonReader::SDTSPolygonReader() + +{ + bRingsAssembled = FALSE; +} + +/************************************************************************/ +/* ~SDTSPolygonReader() */ +/************************************************************************/ + +SDTSPolygonReader::~SDTSPolygonReader() +{ +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +void SDTSPolygonReader::Close() + +{ + oDDFModule.Close(); +} + +/************************************************************************/ +/* Open() */ +/* */ +/* Open the requested line file, and prepare to start reading */ +/* data records. */ +/************************************************************************/ + +int SDTSPolygonReader::Open( const char * pszFilename ) + +{ + return( oDDFModule.Open( pszFilename ) ); +} + +/************************************************************************/ +/* GetNextPolygon() */ +/* */ +/* Fetch the next feature as an STDSRawPolygon. */ +/************************************************************************/ + +SDTSRawPolygon * SDTSPolygonReader::GetNextPolygon() + +{ + DDFRecord *poRecord; + +/* -------------------------------------------------------------------- */ +/* Read a record. */ +/* -------------------------------------------------------------------- */ + if( oDDFModule.GetFP() == NULL ) + return NULL; + + poRecord = oDDFModule.ReadRecord(); + + if( poRecord == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Transform into a Polygon feature. */ +/* -------------------------------------------------------------------- */ + SDTSRawPolygon *poRawPolygon = new SDTSRawPolygon(); + + if( poRawPolygon->Read( poRecord ) ) + { + return( poRawPolygon ); + } + else + { + delete poRawPolygon; + return NULL; + } +} + +/************************************************************************/ +/* AssembleRings() */ +/************************************************************************/ + +/** + * Assemble geometry for a polygon transfer. + * + * This method takes care of attaching lines from all the line layers in + * this transfer to this polygon layer, assembling the lines into rings on + * the polygons, and then cleaning up unnecessary intermediate results. + * + * Currently this method will leave the line layers rewound to the beginning + * but indexed, and the polygon layer rewound but indexed. In the future + * it may restore reading positions, and possibly flush line indexes if they + * were not previously indexed. + * + * This method does nothing if the rings have already been assembled on + * this layer using this method. + * + * See SDTSRawPolygon::AssembleRings() for more information on how the lines + * are assembled into rings. + * + * @param poTransfer the SDTSTransfer that this reader is a part of. Used + * to get a list of line layers that might be needed. + * @param iPolyLayer the polygon reader instance number, used to avoid processing + * lines for other layers. + */ + +void SDTSPolygonReader::AssembleRings( SDTSTransfer * poTransfer, + int iPolyLayer ) + +{ + if( bRingsAssembled ) + return; + + bRingsAssembled = TRUE; + +/* -------------------------------------------------------------------- */ +/* To write polygons we need to build them from their related */ +/* arcs. We don't know off hand which arc (line) layers */ +/* contribute so we process all line layers, attaching them to */ +/* polygons as appropriate. */ +/* -------------------------------------------------------------------- */ + for( int iLineLayer = 0; + iLineLayer < poTransfer->GetLayerCount(); + iLineLayer++ ) + { + SDTSLineReader *poLineReader; + + if( poTransfer->GetLayerType(iLineLayer) != SLTLine ) + continue; + + poLineReader = (SDTSLineReader *) + poTransfer->GetLayerIndexedReader( iLineLayer ); + if( poLineReader == NULL ) + continue; + + poLineReader->AttachToPolygons( poTransfer, iPolyLayer ); + poLineReader->Rewind(); + } + +/* -------------------------------------------------------------------- */ +/* Scan all polygons indexed on this reader, and assemble their */ +/* rings. */ +/* -------------------------------------------------------------------- */ + SDTSFeature *poFeature; + + Rewind(); + while( (poFeature = GetNextFeature()) != NULL ) + { + SDTSRawPolygon *poPoly = (SDTSRawPolygon *) poFeature; + + poPoly->AssembleRings(); + } + + Rewind(); +} diff --git a/bazaar/plugin/gdal/frmts/sdts/sdtsrasterreader.cpp b/bazaar/plugin/gdal/frmts/sdts/sdtsrasterreader.cpp new file mode 100644 index 000000000..3d514b482 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdtsrasterreader.cpp @@ -0,0 +1,605 @@ +/****************************************************************************** + * $Id: sdtsrasterreader.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: SDTS Translator + * Purpose: Implementation of SDTSRasterReader class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" + +CPL_CVSID("$Id: sdtsrasterreader.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* SDTSRasterReader() */ +/************************************************************************/ + +SDTSRasterReader::SDTSRasterReader() + +{ + nXSize = 0; + nYSize = 0; + nXBlockSize = 0; + nYBlockSize = 0; + nXStart = 0; + nYStart = 0; + + strcpy( szINTR, "CE" ); +} + +/************************************************************************/ +/* ~SDTSRasterReader() */ +/************************************************************************/ + +SDTSRasterReader::~SDTSRasterReader() +{ +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +void SDTSRasterReader::Close() + +{ + oDDFModule.Close(); +} + +/************************************************************************/ +/* Open() */ +/* */ +/* Open the requested cell file, and collect required */ +/* information. */ +/************************************************************************/ + +int SDTSRasterReader::Open( SDTS_CATD * poCATD, SDTS_IREF * poIREF, + const char * pszModule ) + +{ + strncpy( szModule, pszModule, sizeof(szModule) ); + szModule[sizeof(szModule) - 1] = '\0'; + +/* ==================================================================== */ +/* Search the LDEF module for the requested cell module. */ +/* ==================================================================== */ + DDFModule oLDEF; + DDFRecord *poRecord; + +/* -------------------------------------------------------------------- */ +/* Open the LDEF module, and report failure if it is missing. */ +/* -------------------------------------------------------------------- */ + if( poCATD->GetModuleFilePath("LDEF") == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find LDEF entry in CATD module ... " + "can't treat as raster.\n" ); + return FALSE; + } + + if( !oLDEF.Open( poCATD->GetModuleFilePath("LDEF") ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read each record, till we find what we want. */ +/* -------------------------------------------------------------------- */ + while( (poRecord = oLDEF.ReadRecord() ) != NULL ) + { + const char* pszCandidateModule = poRecord->GetStringSubfield("LDEF",0,"CMNM",0); + if( pszCandidateModule == NULL ) + { + poRecord = NULL; + break; + } + if( EQUAL(pszCandidateModule, pszModule) ) + break; + } + + if( poRecord == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find module `%s' in LDEF file.\n", + pszModule ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Extract raster dimensions, and origin offset (0/1). */ +/* -------------------------------------------------------------------- */ + nXSize = poRecord->GetIntSubfield( "LDEF", 0, "NCOL", 0 ); + nYSize = poRecord->GetIntSubfield( "LDEF", 0, "NROW", 0 ); + + nXStart = poRecord->GetIntSubfield( "LDEF", 0, "SOCI", 0 ); + nYStart = poRecord->GetIntSubfield( "LDEF", 0, "SORI", 0 ); + +/* -------------------------------------------------------------------- */ +/* Get the point in the pixel that the origin defines. We only */ +/* support top left and center. */ +/* -------------------------------------------------------------------- */ + const char* pszINTR = poRecord->GetStringSubfield( "LDEF", 0, "INTR", 0 ); + if( pszINTR == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Can't find INTR subfield of LDEF field" ); + return FALSE; + } + strcpy( szINTR, pszINTR ); + if( EQUAL(szINTR,"") ) + strcpy( szINTR, "CE" ); + + if( !EQUAL(szINTR,"CE") && !EQUAL(szINTR,"TL") ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unsupported INTR value of `%s', assume CE.\n" + "Positions may be off by one pixel.\n", + szINTR ); + strcpy( szINTR, "CE" ); + } + +/* -------------------------------------------------------------------- */ +/* Record the LDEF record number we used so we can find the */ +/* corresponding RSDF record. */ +/* -------------------------------------------------------------------- */ + int nLDEF_RCID; + + nLDEF_RCID = poRecord->GetIntSubfield( "LDEF", 0, "RCID", 0 ); + + oLDEF.Close(); + +/* ==================================================================== */ +/* Search the RSDF module for the requested cell module. */ +/* ==================================================================== */ + DDFModule oRSDF; + +/* -------------------------------------------------------------------- */ +/* Open the RSDF module, and report failure if it is missing. */ +/* -------------------------------------------------------------------- */ + if( poCATD->GetModuleFilePath("RSDF") == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find RSDF entry in CATD module ... " + "can't treat as raster.\n" ); + return FALSE; + } + + if( !oRSDF.Open( poCATD->GetModuleFilePath("RSDF") ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read each record, till we find what we want. */ +/* -------------------------------------------------------------------- */ + while( (poRecord = oRSDF.ReadRecord() ) != NULL ) + { + if( poRecord->GetIntSubfield("LYID",0,"RCID",0) == nLDEF_RCID ) + break; + } + + if( poRecord == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find LDEF:%d record in RSDF file.\n", + nLDEF_RCID ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Establish the raster pixel/line to georef transformation. */ +/* -------------------------------------------------------------------- */ + double dfZ; + + if( poRecord->FindField( "SADR" ) == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find SADR field in RSDF record.\n" ); + return FALSE; + } + + poIREF->GetSADR( poRecord->FindField( "SADR" ), 1, + adfTransform + 0, adfTransform + 3, &dfZ ); + + adfTransform[1] = poIREF->dfXRes; + adfTransform[2] = 0.0; + adfTransform[4] = 0.0; + adfTransform[5] = -1 * poIREF->dfYRes; + +/* -------------------------------------------------------------------- */ +/* If the origin is the center of the pixel, then shift it back */ +/* half a pixel to the top left of the top left. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(szINTR,"CE") ) + { + adfTransform[0] -= adfTransform[1] * 0.5; + adfTransform[3] -= adfTransform[5] * 0.5; + } + +/* -------------------------------------------------------------------- */ +/* Verify some other assumptions. */ +/* -------------------------------------------------------------------- */ + const char *pszString; + + pszString = poRecord->GetStringSubfield( "RSDF", 0, "OBRP", 0); + if( pszString == NULL ) pszString = ""; + if( !EQUAL(pszString,"G2") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OBRP value of `%s' not expected 2D raster code (G2).\n", + pszString ); + return FALSE; + } + + pszString = poRecord->GetStringSubfield( "RSDF", 0, "SCOR", 0); + if( pszString == NULL ) pszString = ""; + if( !EQUAL(pszString,"TL") ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "SCOR (origin) is `%s' instead of expected top left.\n" + "Georef coordinates will likely be incorrect.\n", + pszString ); + } + + oRSDF.Close(); + +/* -------------------------------------------------------------------- */ +/* For now we will assume that the block size is one scanline. */ +/* We will blow a gasket later while reading the cell file if */ +/* this isn't the case. */ +/* */ +/* This isn't a very flexible raster implementation! */ +/* -------------------------------------------------------------------- */ + nXBlockSize = nXSize; + nYBlockSize = 1; + +/* ==================================================================== */ +/* Fetch the data type used for the raster, and the units from */ +/* the data dictionary/schema record (DDSH). */ +/* ==================================================================== */ + DDFModule oDDSH; + +/* -------------------------------------------------------------------- */ +/* Open the DDSH module, and report failure if it is missing. */ +/* -------------------------------------------------------------------- */ + if( poCATD->GetModuleFilePath("DDSH") == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find DDSH entry in CATD module ... " + "can't treat as raster.\n" ); + return FALSE; + } + + if( !oDDSH.Open( poCATD->GetModuleFilePath("DDSH") ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read each record, till we find what we want. */ +/* -------------------------------------------------------------------- */ + while( (poRecord = oDDSH.ReadRecord() ) != NULL ) + { + const char* pszName = poRecord->GetStringSubfield("DDSH",0,"NAME",0); + if( pszName == NULL ) + { + poRecord = NULL; + break; + } + if( EQUAL(pszName,pszModule) ) + break; + } + + if( poRecord == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find DDSH record for %s.\n", + pszModule ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Get some values we are interested in. */ +/* -------------------------------------------------------------------- */ + if( poRecord->GetStringSubfield("DDSH",0,"FMT",0) != NULL ) + strcpy( szFMT, poRecord->GetStringSubfield("DDSH",0,"FMT",0) ); + else + strcpy( szFMT, "BUI16" ); + + if( poRecord->GetStringSubfield("DDSH",0,"UNIT",0) != NULL ) + strcpy( szUNITS, poRecord->GetStringSubfield("DDSH",0,"UNIT",0) ); + else + strcpy( szUNITS, "METERS" ); + + if( poRecord->GetStringSubfield("DDSH",0,"ATLB",0) != NULL ) + strcpy( szLabel, poRecord->GetStringSubfield("DDSH",0,"ATLB",0) ); + else + strcpy( szLabel, "" ); + +/* -------------------------------------------------------------------- */ +/* Open the cell file. */ +/* -------------------------------------------------------------------- */ + return( oDDFModule.Open( poCATD->GetModuleFilePath(pszModule) ) ); +} + +/************************************************************************/ +/* GetBlock() */ +/* */ +/* Read a requested block of raster data from the file. */ +/* */ +/* Currently we will always use sequential access. In the */ +/* future we should modify the iso8211 library to support */ +/* seeking, and modify this to seek directly to the right */ +/* record once it's location is known. */ +/************************************************************************/ + +/** + Read a block of raster data from the file. + + @param nXOffset X block offset into the file. Normally zero for scanline + organized raster files. + + @param nYOffset Y block offset into the file. Normally the scanline offset + from top of raster for scanline organized raster files. + + @param pData pointer to GInt16 (signed short) buffer of data into which to + read the raster. + + @return TRUE on success and FALSE on error. + + */ + +int SDTSRasterReader::GetBlock( CPL_UNUSED int nXOffset, + int nYOffset, + void * pData ) +{ + DDFRecord *poRecord = NULL; + int nBytesPerValue; + int iTry; + + CPLAssert( nXOffset == 0 ); + +/* -------------------------------------------------------------------- */ +/* Analyse the datatype. */ +/* -------------------------------------------------------------------- */ + CPLAssert( EQUAL(szFMT,"BI16") || EQUAL(szFMT,"BFP32") ); + + if( EQUAL(szFMT,"BI16") ) + nBytesPerValue = 2; + else + nBytesPerValue = 4; + + for(iTry=0;iTry<2;iTry++) + { + /* -------------------------------------------------------------------- */ + /* Read through till we find the desired record. */ + /* -------------------------------------------------------------------- */ + CPLErrorReset(); + while( (poRecord = oDDFModule.ReadRecord()) != NULL ) + { + if( poRecord->GetIntSubfield( "CELL", 0, "ROWI", 0 ) + == nYOffset + nYStart ) + { + break; + } + } + + if( CPLGetLastErrorType() == CE_Failure ) + return FALSE; + + /* -------------------------------------------------------------------- */ + /* If we didn't get what we needed just start over. */ + /* -------------------------------------------------------------------- */ + if( poRecord == NULL ) + { + if (iTry == 0) + oDDFModule.Rewind(); + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot read scanline %d. Raster access failed.\n", + nYOffset ); + return FALSE; + } + } + else + { + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Validate the records size. Does it represent exactly one */ +/* scanline? */ +/* -------------------------------------------------------------------- */ + DDFField *poCVLS; + + poCVLS = poRecord->FindField( "CVLS" ); + if( poCVLS == NULL ) + return FALSE; + + if( poCVLS->GetRepeatCount() != nXSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cell record is %d long, but we expected %d, the number\n" + "of pixels in a scanline. Raster access failed.\n", + poCVLS->GetRepeatCount(), nXSize ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Does the CVLS field consist of exactly 1 B(16) field? */ +/* -------------------------------------------------------------------- */ + if( poCVLS->GetDataSize() < nBytesPerValue * nXSize + || poCVLS->GetDataSize() > nBytesPerValue * nXSize + 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cell record is not of expected format. Raster access " + "failed.\n" ); + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Copy the data to the application buffer, and byte swap if */ +/* required. */ +/* -------------------------------------------------------------------- */ + memcpy( pData, poCVLS->GetData(), nXSize * nBytesPerValue ); + +#ifdef CPL_LSB + if( nBytesPerValue == 2 ) + { + for( int i = 0; i < nXSize; i++ ) + { + ((GInt16 *) pData)[i] = CPL_MSBWORD16(((GInt16 *) pData)[i]); + } + } + else + { + for( int i = 0; i < nXSize; i++ ) + { + CPL_MSBPTR32( ((GByte *)pData) + i*4 ); + } + } +#endif + + return TRUE; +} + +/************************************************************************/ +/* GetTransform() */ +/************************************************************************/ + +/** + Fetch the transformation between pixel/line coordinates and georeferenced + coordinates. + + @param padfTransformOut pointer to an array of six doubles which will be + filled with the georeferencing transform. + + @return TRUE is returned, indicating success. + + The padfTransformOut array consists of six values. The pixel/line coordinate + (Xp,Yp) can be related to a georeferenced coordinate (Xg,Yg) or (Easting, + Northing). + +
    +  Xg = padfTransformOut[0] + Xp * padfTransform[1] + Yp * padfTransform[2]
    +  Yg = padfTransformOut[3] + Xp * padfTransform[4] + Yp * padfTransform[5]
    +  
    + + In other words, for a north up image the top left corner of the top left + pixel is at georeferenced coordinate (padfTransform[0],padfTransform[3]) + the pixel width is padfTransform[1], the pixel height is padfTransform[5] + and padfTransform[2] and padfTransform[4] will be zero. + + */ + +int SDTSRasterReader::GetTransform( double * padfTransformOut ) + +{ + memcpy( padfTransformOut, adfTransform, sizeof(double)*6 ); + + return TRUE; +} + +/************************************************************************/ +/* GetRasterType() */ +/************************************************************************/ + +/** + * Fetch the pixel data type. + * + * Returns one of SDTS_RT_INT16 (1) or SDTS_RT_FLOAT32 (6) indicating the + * type of buffer that should be passed to GetBlock(). + */ + +int SDTSRasterReader::GetRasterType() + +{ + if( EQUAL(szFMT,"BFP32") ) + return 6; + else + return 1; +} + +/************************************************************************/ +/* GetMinMax() */ +/************************************************************************/ + +/** + * Fetch the minimum and maximum raster values that occur in the file. + * + * Note this operation current results in a scan of the entire file. + * + * @param pdfMin variable in which the minimum value encountered is returned. + * @param pdfMax variable in which the maximum value encountered is returned. + * @param dfNoData a value to ignore when computing min/max, defaults to + * -32766. + * + * @return TRUE on success, or FALSE if an error occurs. + */ + +int SDTSRasterReader::GetMinMax( double * pdfMin, double * pdfMax, + double dfNoData ) + +{ + void *pBuffer; + int bFirst = TRUE; + int b32Bit = GetRasterType() == SDTS_RT_FLOAT32; + + CPLAssert( GetBlockXSize() == GetXSize() && GetBlockYSize() == 1 ); + + pBuffer = CPLMalloc(sizeof(float) * GetXSize()); + + for( int iLine = 0; iLine < GetYSize(); iLine++ ) + { + if( !GetBlock( 0, iLine, pBuffer ) ) + { + CPLFree( pBuffer ); + return FALSE; + } + + for( int iPixel = 0; iPixel < GetXSize(); iPixel++ ) + { + double dfValue; + + if( b32Bit ) + dfValue = ((float *) pBuffer)[iPixel]; + else + dfValue = ((short *) pBuffer)[iPixel]; + + if( dfValue != dfNoData ) + { + if( bFirst ) + { + *pdfMin = *pdfMax = dfValue; + bFirst = FALSE; + } + else + { + *pdfMin = MIN(*pdfMin,dfValue); + *pdfMax = MAX(*pdfMax,dfValue); + } + } + } + } + + CPLFree( pBuffer ); + + return !bFirst; +} diff --git a/bazaar/plugin/gdal/frmts/sdts/sdtstransfer.cpp b/bazaar/plugin/gdal/frmts/sdts/sdtstransfer.cpp new file mode 100644 index 000000000..6494f5bb7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdtstransfer.cpp @@ -0,0 +1,682 @@ +/****************************************************************************** + * $Id: sdtstransfer.cpp 21298 2010-12-20 10:58:34Z rouault $ + * + * Project: SDTS Translator + * Purpose: Implementation of SDTSTransfer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" + +CPL_CVSID("$Id: sdtstransfer.cpp 21298 2010-12-20 10:58:34Z rouault $"); + +/************************************************************************/ +/* SDTSTransfer() */ +/************************************************************************/ + +SDTSTransfer::SDTSTransfer() + +{ + nLayers = 0; + panLayerCATDEntry = NULL; + papoLayerReader = NULL; +} + +/************************************************************************/ +/* ~SDTSTransfer() */ +/************************************************************************/ + +SDTSTransfer::~SDTSTransfer() + +{ + Close(); +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +/** + * Open an SDTS transfer, and establish a list of data layers in the + * transfer. + * + * @param pszFilename The name of the CATD file within the transfer. + * + * @return TRUE if the open success, or FALSE if it fails. + */ + +int SDTSTransfer::Open( const char * pszFilename ) + +{ +/* -------------------------------------------------------------------- */ +/* Open the catalog. */ +/* -------------------------------------------------------------------- */ + if( !oCATD.Read( pszFilename ) ) + return FALSE; + + +/* -------------------------------------------------------------------- */ +/* Read the IREF file. */ +/* -------------------------------------------------------------------- */ + if( oCATD.GetModuleFilePath( "IREF" ) == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find IREF module in transfer `%s'.\n", + pszFilename ); + return FALSE; + } + + if( !oIREF.Read( oCATD.GetModuleFilePath( "IREF" ) ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read the XREF file. */ +/* -------------------------------------------------------------------- */ + if( oCATD.GetModuleFilePath( "XREF" ) == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Can't find XREF module in transfer `%s'.\n", + pszFilename ); + } + else if( !oXREF.Read( oCATD.GetModuleFilePath( "XREF" ) ) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Can't read XREF module, even though found in transfer `%s'.\n", + pszFilename ); + } + +/* -------------------------------------------------------------------- */ +/* Build an index of layer types we recognise and care about. */ +/* -------------------------------------------------------------------- */ + int iCATDLayer; + + panLayerCATDEntry = (int *) CPLMalloc(sizeof(int) * oCATD.GetEntryCount()); + + for( iCATDLayer = 0; iCATDLayer < oCATD.GetEntryCount(); iCATDLayer++ ) + { + switch( oCATD.GetEntryType(iCATDLayer) ) + { + case SLTPoint: + case SLTLine: + case SLTAttr: + case SLTPoly: + case SLTRaster: + panLayerCATDEntry[nLayers++] = iCATDLayer; + break; + + default: + /* ignore */ + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Initialized the related indexed readers list. */ +/* -------------------------------------------------------------------- */ + papoLayerReader = (SDTSIndexedReader **) + CPLCalloc(sizeof(SDTSIndexedReader*),oCATD.GetEntryCount()); + + return TRUE; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +void SDTSTransfer::Close() + +{ + for( int i = 0; i < nLayers; i++ ) + { + if( papoLayerReader[i] != NULL ) + delete papoLayerReader[i]; + } + CPLFree( papoLayerReader ); + papoLayerReader = NULL; + CPLFree( panLayerCATDEntry ); + panLayerCATDEntry = NULL; + nLayers = 0; +} + +/************************************************************************/ +/* GetLayerType() */ +/************************************************************************/ + +/** + Fetch type of requested feature layer. + + @param iEntry the index of the layer to fetch information on. A value + from zero to GetLayerCount()-1. + + @return the layer type. + +
      +
    • SLTPoint: A point layer. An SDTSPointReader is returned by + SDTSTransfer::GetLayerIndexedReader(). + +
    • SLTLine: A line layer. An SDTSLineReader is returned by + SDTSTransfer::GetLayerIndexedReader(). + +
    • SLTAttr: An attribute primary or secondary layer. An SDTSAttrReader + is returned by SDTSTransfer::GetLayerIndexedReader(). + +
    • SLTPoly: A polygon layer. An SDTSPolygonReader is returned by + SDTSTransfer::GetLayerIndexedReader(). + +
    • SLTRaster: A raster layer. SDTSTransfer::GetLayerIndexedReader() + is not implemented. Use SDTSTransfer::GetLayerRasterReader() instead. +
    + + */ + +SDTSLayerType SDTSTransfer::GetLayerType( int iEntry ) + +{ + if( iEntry < 0 || iEntry >= nLayers ) + return SLTUnknown; + + return oCATD.GetEntryType( panLayerCATDEntry[iEntry] ); +} + +/************************************************************************/ +/* GetLayerCATDEntry() */ +/************************************************************************/ + +/** + Fetch the CATD module index for a layer. This can be used to fetch + details about the layer/module from the SDTS_CATD object, such as it's + filename, and description. + + @param iEntry the layer index from 0 to GetLayerCount()-1. + + @return the module index suitable for use with the various SDTS_CATD + methods. + */ + +int SDTSTransfer::GetLayerCATDEntry( int iEntry ) + +{ + if( iEntry < 0 || iEntry >= nLayers ) + return -1; + + return panLayerCATDEntry[iEntry]; +} + +/************************************************************************/ +/* GetLayerLineReader() */ +/************************************************************************/ + +SDTSLineReader *SDTSTransfer::GetLayerLineReader( int iEntry ) + +{ + SDTSLineReader *poLineReader; + + if( iEntry < 0 + || iEntry >= nLayers + || oCATD.GetEntryType( panLayerCATDEntry[iEntry] ) != SLTLine ) + { + return NULL; + } + + + poLineReader = new SDTSLineReader( &oIREF ); + + if( !poLineReader->Open( + oCATD.GetEntryFilePath( panLayerCATDEntry[iEntry] ) ) ) + { + delete poLineReader; + return NULL; + } + else + { + return poLineReader; + } +} + +/************************************************************************/ +/* GetLayerPointReader() */ +/************************************************************************/ + +SDTSPointReader *SDTSTransfer::GetLayerPointReader( int iEntry ) + +{ + SDTSPointReader *poPointReader; + + if( iEntry < 0 + || iEntry >= nLayers + || oCATD.GetEntryType( panLayerCATDEntry[iEntry] ) != SLTPoint ) + { + return NULL; + } + + + poPointReader = new SDTSPointReader( &oIREF ); + + if( !poPointReader->Open( + oCATD.GetEntryFilePath( panLayerCATDEntry[iEntry] ) ) ) + { + delete poPointReader; + return NULL; + } + else + { + return poPointReader; + } +} + +/************************************************************************/ +/* GetLayerPolygonReader() */ +/************************************************************************/ + +SDTSPolygonReader *SDTSTransfer::GetLayerPolygonReader( int iEntry ) + +{ + SDTSPolygonReader *poPolyReader; + + if( iEntry < 0 + || iEntry >= nLayers + || oCATD.GetEntryType( panLayerCATDEntry[iEntry] ) != SLTPoly ) + { + return NULL; + } + + + poPolyReader = new SDTSPolygonReader(); + + if( !poPolyReader->Open( + oCATD.GetEntryFilePath( panLayerCATDEntry[iEntry] ) ) ) + { + delete poPolyReader; + return NULL; + } + else + { + return poPolyReader; + } +} + +/************************************************************************/ +/* GetLayerAttrReader() */ +/************************************************************************/ + +SDTSAttrReader *SDTSTransfer::GetLayerAttrReader( int iEntry ) + +{ + SDTSAttrReader *poAttrReader; + + if( iEntry < 0 + || iEntry >= nLayers + || oCATD.GetEntryType( panLayerCATDEntry[iEntry] ) != SLTAttr ) + { + return NULL; + } + + + poAttrReader = new SDTSAttrReader( &oIREF ); + + if( !poAttrReader->Open( + oCATD.GetEntryFilePath( panLayerCATDEntry[iEntry] ) ) ) + { + delete poAttrReader; + return NULL; + } + else + { + return poAttrReader; + } +} + +/************************************************************************/ +/* GetLayerRasterReader() */ +/************************************************************************/ + +/** + Instantiate an SDTSRasterReader for the indicated layer. + + @param iEntry the index of the layer to instantiate a reader for. A + value between 0 and GetLayerCount()-1. + + @return a pointer to a new SDTSRasterReader object, or NULL if the method + fails. + + NOTE: The reader returned from GetLayerRasterReader() becomes the + responsibility of the caller to delete, and isn't automatically deleted + when the SDTSTransfer is destroyed. This method is different from + the GetLayerIndexedReader() method in this regard. + */ + +SDTSRasterReader *SDTSTransfer::GetLayerRasterReader( int iEntry ) + +{ + SDTSRasterReader *poRasterReader; + + if( iEntry < 0 + || iEntry >= nLayers + || oCATD.GetEntryType( panLayerCATDEntry[iEntry] ) != SLTRaster ) + { + return NULL; + } + + poRasterReader = new SDTSRasterReader(); + + if( !poRasterReader->Open( &oCATD, &oIREF, + oCATD.GetEntryModule(panLayerCATDEntry[iEntry] ) ) ) + { + delete poRasterReader; + return NULL; + } + else + { + return poRasterReader; + } +} + +/************************************************************************/ +/* GetLayerModuleReader() */ +/************************************************************************/ + +DDFModule *SDTSTransfer::GetLayerModuleReader( int iEntry ) + +{ + DDFModule *poModuleReader; + + if( iEntry < 0 || iEntry >= nLayers ) + { + return NULL; + } + + + poModuleReader = new DDFModule; + + if( !poModuleReader->Open( + oCATD.GetEntryFilePath( panLayerCATDEntry[iEntry] ) ) ) + { + delete poModuleReader; + return NULL; + } + else + { + return poModuleReader; + } +} + +/************************************************************************/ +/* GetLayerIndexedReader() */ +/************************************************************************/ + +/** + Returns a pointer to a reader of the appropriate type to the requested + layer. + + Notes: +
      +
    • The returned reader remains owned by the SDTSTransfer, and will be + destroyed when the SDTSTransfer is destroyed. It should not be + destroyed by the application. + +
    • If an indexed reader was already created for this layer using + GetLayerIndexedReader(), it will be returned instead of creating a new + reader. Amoung other things this means that the returned reader may not + be positioned to read from the beginning of the module, and may already + have it's index filled. + +
    • The returned reader will be of a type appropriate to the layer. + See SDTSTransfer::GetLayerType() to see what reader classes correspond + to what layer types, so it can be cast accordingly (if necessary). + +
    + + @param iEntry the index of the layer to instantiate a reader for. A + value between 0 and GetLayerCount()-1. + + @return a pointer to an appropriate reader or NULL if the method fails. + */ + +SDTSIndexedReader *SDTSTransfer::GetLayerIndexedReader( int iEntry ) + +{ + if( papoLayerReader[iEntry] == NULL ) + { + switch( oCATD.GetEntryType( panLayerCATDEntry[iEntry] ) ) + { + case SLTAttr: + papoLayerReader[iEntry] = GetLayerAttrReader( iEntry ); + break; + + case SLTPoint: + papoLayerReader[iEntry] = GetLayerPointReader( iEntry ); + break; + + case SLTLine: + papoLayerReader[iEntry] = GetLayerLineReader( iEntry ); + break; + + case SLTPoly: + papoLayerReader[iEntry] = GetLayerPolygonReader( iEntry ); + break; + + default: + break; + } + } + + return papoLayerReader[iEntry]; +} + +/************************************************************************/ +/* FindLayer() */ +/************************************************************************/ + +/** + Fetch the SDTSTransfer layer number corresponding to a module name. + + @param pszModule the name of the module to search for, such as "PC01". + + @return the layer number (between 0 and GetLayerCount()-1 corresponding to + the module, or -1 if it doesn't correspond to a layer. + */ + +int SDTSTransfer::FindLayer( const char * pszModule ) + +{ + int iLayer; + + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszModule, + oCATD.GetEntryModule( panLayerCATDEntry[iLayer] ) ) ) + { + return iLayer; + } + } + + return -1; +} + +/************************************************************************/ +/* GetIndexedFeatureRef() */ +/************************************************************************/ + +SDTSFeature *SDTSTransfer::GetIndexedFeatureRef( SDTSModId *poModId, + SDTSLayerType *peType ) + +{ +/* -------------------------------------------------------------------- */ +/* Find the desired layer ... this is likely a significant slow */ +/* point in the whole process ... perhaps the last found could */ +/* be cached or something. */ +/* -------------------------------------------------------------------- */ + int iLayer = FindLayer( poModId->szModule ); + if( iLayer == -1 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get the reader, and read a feature from it. */ +/* -------------------------------------------------------------------- */ + SDTSIndexedReader *poReader; + + poReader = GetLayerIndexedReader( iLayer ); + if( poReader == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* return type, if requested. */ +/* -------------------------------------------------------------------- */ + if( peType != NULL ) + *peType = GetLayerType(iLayer); + + return poReader->GetIndexedFeatureRef( poModId->nRecord ); +} + +/************************************************************************/ +/* GetAttr() */ +/* */ +/* Fetch the attribute information corresponding to a given */ +/* SDTSModId. */ +/************************************************************************/ + +/** + Fetch the attribute fields given a particular module/record id. + + @param poModId an attribute record identifer, normally taken from the + aoATID[] array of an SDTSIndexedFeature. + + @return a pointer to the DDFField containing the user attribute values as + subfields. + */ + +DDFField *SDTSTransfer::GetAttr( SDTSModId *poModId ) + +{ + SDTSAttrRecord *poAttrRecord; + + poAttrRecord = (SDTSAttrRecord *) GetIndexedFeatureRef( poModId ); + + if( poAttrRecord == NULL ) + return NULL; + + return poAttrRecord->poATTR; +} + +/************************************************************************/ +/* GetBounds() */ +/************************************************************************/ + +/** + Fetch approximate bounds for a transfer by scanning all point layers + and raster layers. + + For TVP datasets (where point layers are scanned) the results can, in + theory miss some lines that go outside the bounds of the point layers. + However, this isn't common since most TVP sets contain a bounding rectangle + whose corners will define the most extreme extents. + + @param pdfMinX western edge of dataset + @param pdfMinY southern edge of dataset + @param pdfMaxX eastern edge of dataset + @param pdfMaxY northern edge of dataset + + @return TRUE if success, or FALSE on a failure. + */ + +int SDTSTransfer::GetBounds( double *pdfMinX, double *pdfMinY, + double *pdfMaxX, double *pdfMaxY ) + +{ + int bFirst = TRUE; + + for( int iLayer = 0; iLayer < GetLayerCount(); iLayer++ ) + { + if( GetLayerType( iLayer ) == SLTPoint ) + { + SDTSPointReader *poLayer; + SDTSRawPoint *poPoint; + + poLayer = (SDTSPointReader *) GetLayerIndexedReader( iLayer ); + if( poLayer == NULL ) + continue; + + poLayer->Rewind(); + while( (poPoint = (SDTSRawPoint*) poLayer->GetNextFeature()) != NULL ) + { + if( bFirst ) + { + *pdfMinX = *pdfMaxX = poPoint->dfX; + *pdfMinY = *pdfMaxY = poPoint->dfY; + bFirst = FALSE; + } + else + { + *pdfMinX = MIN(*pdfMinX,poPoint->dfX); + *pdfMaxX = MAX(*pdfMaxX,poPoint->dfX); + *pdfMinY = MIN(*pdfMinY,poPoint->dfY); + *pdfMaxY = MAX(*pdfMaxY,poPoint->dfY); + } + + if( !poLayer->IsIndexed() ) + delete poPoint; + } + } + + else if( GetLayerType( iLayer ) == SLTRaster ) + { + SDTSRasterReader *poRL; + double adfGeoTransform[6]; + double dfMinX, dfMaxX, dfMinY, dfMaxY; + + poRL = GetLayerRasterReader( iLayer ); + if( poRL == NULL ) + continue; + + poRL->GetTransform( adfGeoTransform ); + + dfMinX = adfGeoTransform[0]; + dfMaxY = adfGeoTransform[3]; + dfMaxX = adfGeoTransform[0] + poRL->GetXSize()*adfGeoTransform[1]; + dfMinY = adfGeoTransform[3] + poRL->GetYSize()*adfGeoTransform[5]; + + if( bFirst ) + { + *pdfMinX = dfMinX; + *pdfMaxX = dfMaxX; + *pdfMinY = dfMinY; + *pdfMaxY = dfMaxY; + bFirst = FALSE; + } + else + { + *pdfMinX = MIN(dfMinX,*pdfMinX); + *pdfMaxX = MAX(dfMaxX,*pdfMaxX); + *pdfMinY = MIN(dfMinY,*pdfMinY); + *pdfMaxY = MAX(dfMaxY,*pdfMaxY); + } + + delete poRL; + } + } + + return !bFirst; +} + diff --git a/bazaar/plugin/gdal/frmts/sdts/sdtsxref.cpp b/bazaar/plugin/gdal/frmts/sdts/sdtsxref.cpp new file mode 100644 index 000000000..40b9c1ec6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sdts/sdtsxref.cpp @@ -0,0 +1,99 @@ +/****************************************************************************** + * $Id: sdtsxref.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: SDTS Translator + * Purpose: Implementation of SDTS_XREF class for reading XREF module. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "sdts_al.h" + +CPL_CVSID("$Id: sdtsxref.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* SDTS_XREF() */ +/************************************************************************/ + +SDTS_XREF::SDTS_XREF() + +{ + pszSystemName = CPLStrdup( "" ); + pszDatum = CPLStrdup( "" ); + nZone = 0; +} + +/************************************************************************/ +/* ~SDTS_XREF() */ +/************************************************************************/ + +SDTS_XREF::~SDTS_XREF() +{ + CPLFree( pszSystemName ); + CPLFree( pszDatum ); +} + +/************************************************************************/ +/* Read() */ +/* */ +/* Read the named file to initialize this structure. */ +/************************************************************************/ + +int SDTS_XREF::Read( const char * pszFilename ) + +{ + DDFModule oXREFFile; + DDFRecord *poRecord; + +/* -------------------------------------------------------------------- */ +/* Open the file, and read the header. */ +/* -------------------------------------------------------------------- */ + if( !oXREFFile.Open( pszFilename ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read the first record, and verify that this is an XREF record. */ +/* -------------------------------------------------------------------- */ + poRecord = oXREFFile.ReadRecord(); + if( poRecord == NULL ) + return FALSE; + + if( poRecord->GetStringSubfield( "XREF", 0, "MODN", 0 ) == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read fields of interest. */ +/* -------------------------------------------------------------------- */ + + CPLFree( pszSystemName ); + pszSystemName = + CPLStrdup( poRecord->GetStringSubfield( "XREF", 0, "RSNM", 0 ) ); + + CPLFree( pszDatum ); + pszDatum = + CPLStrdup( poRecord->GetStringSubfield( "XREF", 0, "HDAT", 0 ) ); + + nZone = poRecord->GetIntSubfield( "XREF", 0, "ZONE", 0 ); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/frmts/sgi/GNUmakefile b/bazaar/plugin/gdal/frmts/sgi/GNUmakefile new file mode 100644 index 000000000..e45417744 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sgi/GNUmakefile @@ -0,0 +1,18 @@ + +include ../../GDALmake.opt + +OBJ = sgidataset.o + +CPPFLAGS := $(XTRA_OPT) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +../o/%.$(OBJ_EXT): + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/sgi/makefile.vc b/bazaar/plugin/gdal/frmts/sgi/makefile.vc new file mode 100644 index 000000000..fca56bb5e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sgi/makefile.vc @@ -0,0 +1,14 @@ +GDAL_ROOT = ..\.. +!INCLUDE $(GDAL_ROOT)\nmake.opt + + +OBJ = sgidataset.obj + +EXTRAFLAGS = -I./ + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/sgi/sgidataset.cpp b/bazaar/plugin/gdal/frmts/sgi/sgidataset.cpp new file mode 100644 index 000000000..c7e938fba --- /dev/null +++ b/bazaar/plugin/gdal/frmts/sgi/sgidataset.cpp @@ -0,0 +1,833 @@ +/****************************************************************************** + * $Id: sgidataset.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: SGI Image Driver + * Purpose: Implement SGI Image Support based on Paul Bourke's SGI Image code. + * http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/ + * ftp://ftp.sgi.com/graphics/SGIIMAGESPEC + * Authors: Mike Mazzella (GDAL driver) + * Paul Bourke (original SGI format code) + * Frank Warmerdam (write support) + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_port.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: sgidataset.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +CPL_C_START +void GDALRegister_SGI(void); +CPL_C_END + +struct ImageRec +{ + GUInt16 imagic; + GByte type; + GByte bpc; + GUInt16 dim; + GUInt16 xsize; + GUInt16 ysize; + GUInt16 zsize; + GUInt32 min; + GUInt32 max; + char wasteBytes[4]; + char name[80]; + GUInt32 colorMap; + + VSILFILE* file; + std::string fileName; + unsigned char* tmp; + GUInt32 rleEnd; + int rleTableDirty; + GUInt32* rowStart; + GInt32* rowSize; + + ImageRec() + : imagic(0), + type(0), + bpc(1), + dim(0), + xsize(0), + ysize(0), + zsize(0), + min(0), + max(0), + colorMap(0), + file(NULL), + fileName(""), + tmp(NULL), + rleEnd(0), + rleTableDirty(FALSE), + rowStart(NULL), + rowSize(NULL) + { + memset(wasteBytes, 0, 4); + memset(name, 0, 80); + } + + void Swap() + { +#ifdef CPL_LSB + CPL_SWAP16PTR(&imagic); + CPL_SWAP16PTR(&dim); + CPL_SWAP16PTR(&xsize); + CPL_SWAP16PTR(&ysize); + CPL_SWAP16PTR(&zsize); + CPL_SWAP32PTR(&min); + CPL_SWAP32PTR(&max); +#endif + } +}; + +/************************************************************************/ +/* ConvertLong() */ +/************************************************************************/ +static void ConvertLong(GUInt32* array, GInt32 length) +{ +#ifdef CPL_LSB + GUInt32* ptr; + ptr = (GUInt32*)array; + while(length--) + CPL_SWAP32PTR(ptr++); +#endif +} + +/************************************************************************/ +/* ImageGetRow() */ +/************************************************************************/ +static CPLErr ImageGetRow(ImageRec* image, unsigned char* buf, int y, int z) +{ + unsigned char *iPtr, *oPtr, pixel; + int count; + + y = image->ysize - 1 - y; + + if(int(image->type) == 1) + { + // reads row + VSIFSeekL(image->file, (long)image->rowStart[y+z*image->ysize], SEEK_SET); + if(VSIFReadL(image->tmp, 1, (GUInt32)image->rowSize[y+z*image->ysize], image->file) != (GUInt32)image->rowSize[y+z*image->ysize]) + { + CPLError(CE_Failure, CPLE_OpenFailed, "file read error: row (%d) of (%s)\n", y, image->fileName.empty() ? "none" : image->fileName.c_str()); + return CE_Failure; + } + + // expands row + iPtr = image->tmp; + oPtr = buf; + int xsizeCount = 0; + for(;;) + { + pixel = *iPtr++; + count = (int)(pixel & 0x7F); + if(!count) + { + if(xsizeCount != image->xsize) + { + CPLError(CE_Failure, CPLE_OpenFailed, "file read error: row (%d) of (%s)\n", y, image->fileName.empty() ? "none" : image->fileName.c_str()); + return CE_Failure; + } + else + { + return CE_None; + } + } + + if( xsizeCount + count > image->xsize ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong repetition number that would overflow data at line %d", y); + return CE_Failure; + } + + if(pixel & 0x80) + { + memcpy(oPtr, iPtr, count); + iPtr += count; + } + else + { + pixel = *iPtr++; + memset(oPtr, pixel, count); + } + oPtr += count; + xsizeCount += count; + } + } + else + { + VSIFSeekL(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize), SEEK_SET); + if(VSIFReadL(buf, 1, image->xsize, image->file) != image->xsize) + { + CPLError(CE_Failure, CPLE_OpenFailed, "file read error: row (%d) of (%s)\n", y, image->fileName.empty() ? "none" : image->fileName.c_str()); + return CE_Failure; + } + } + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* SGIDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class SGIRasterBand; + +class SGIDataset : public GDALPamDataset +{ + friend class SGIRasterBand; + + VSILFILE* fpImage; + + int bGeoTransformValid; + double adfGeoTransform[6]; + + ImageRec image; + +public: + SGIDataset(); + ~SGIDataset(); + + virtual CPLErr GetGeoTransform(double*); + static GDALDataset* Open(GDALOpenInfo*); + static GDALDataset *Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char **papszOptions ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* SGIRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class SGIRasterBand : public GDALPamRasterBand +{ + friend class SGIDataset; + +public: + SGIRasterBand(SGIDataset*, int); + + virtual CPLErr IReadBlock(int, int, void*); + virtual CPLErr IWriteBlock(int, int, void*); + virtual GDALColorInterp GetColorInterpretation(); +}; + + +/************************************************************************/ +/* SGIRasterBand() */ +/************************************************************************/ + +SGIRasterBand::SGIRasterBand(SGIDataset* poDS, int nBand) + +{ + this->poDS = poDS; + this->nBand = nBand; + if(poDS == NULL) + { + eDataType = GDT_Byte; + } + else + { + if(int(poDS->image.bpc) == 1) + eDataType = GDT_Byte; + else + eDataType = GDT_Int16; + } + nBlockXSize = poDS->nRasterXSize;; + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr SGIRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void* pImage) +{ + SGIDataset* poGDS = (SGIDataset*) poDS; + + CPLAssert(nBlockXOff == 0); + +/* -------------------------------------------------------------------- */ +/* Load the desired data into the working buffer. */ +/* -------------------------------------------------------------------- */ + return ImageGetRow(&(poGDS->image), (unsigned char*)pImage, nBlockYOff, nBand-1); +} + +/************************************************************************/ +/* IWritelock() */ +/************************************************************************/ + +CPLErr SGIRasterBand::IWriteBlock(CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void* pImage) +{ + SGIDataset* poGDS = (SGIDataset*) poDS; + ImageRec *image = &(poGDS->image); + + CPLAssert(nBlockXOff == 0); + +/* -------------------------------------------------------------------- */ +/* Handle the fairly trivial non-RLE case. */ +/* -------------------------------------------------------------------- */ + if( image->type == 0 ) + { + VSIFSeekL(image->file, + 512 + (nBlockYOff*image->xsize) + + ((nBand-1)*image->xsize*image->ysize ), + SEEK_SET); + if(VSIFWriteL(pImage, 1, image->xsize, image->file) != image->xsize) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "file write error: row (%d)\n", nBlockYOff ); + return CE_Failure; + } + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Handle RLE case. */ +/* -------------------------------------------------------------------- */ + const GByte *pabyRawBuf = (const GByte *) pImage; + GByte *pabyRLEBuf = (GByte *) CPLMalloc(image->xsize * 2 + 6); + int iX = 0, nRLEBytes = 0; + + while( iX < image->xsize ) + { + int nRepeatCount = 1; + + while( iX + nRepeatCount < image->xsize + && nRepeatCount < 127 + && pabyRawBuf[iX + nRepeatCount] == pabyRawBuf[iX] ) + nRepeatCount++; + + if( nRepeatCount > 2 + || iX + nRepeatCount == image->xsize + || (iX + nRepeatCount < image->xsize - 2 + && pabyRawBuf[iX + nRepeatCount + 1] + == pabyRawBuf[iX + nRepeatCount + 2] + && pabyRawBuf[iX + nRepeatCount + 1] + == pabyRawBuf[iX + nRepeatCount + 3]) ) + { // encode a constant run. + pabyRLEBuf[nRLEBytes++] = (GByte) nRepeatCount; + pabyRLEBuf[nRLEBytes++] = pabyRawBuf[iX]; + iX += nRepeatCount; + } + else + { // copy over mixed data. + nRepeatCount = 1; + + for( nRepeatCount = 1; + iX + nRepeatCount < image->xsize && nRepeatCount < 127; + nRepeatCount++ ) + { + if( iX + nRepeatCount + 3 >= image->xsize ) + continue; + + // quit if the next 3 pixels match + if( pabyRawBuf[iX + nRepeatCount] + == pabyRawBuf[iX + nRepeatCount+1] + && pabyRawBuf[iX + nRepeatCount] + == pabyRawBuf[iX + nRepeatCount+2] ) + break; + } + + pabyRLEBuf[nRLEBytes++] = (GByte) (0x80 | nRepeatCount); + memcpy( pabyRLEBuf + nRLEBytes, + pabyRawBuf + iX, + nRepeatCount ); + + nRLEBytes += nRepeatCount; + iX += nRepeatCount; + } + } + + // EOL marker. + pabyRLEBuf[nRLEBytes++] = 0; + +/* -------------------------------------------------------------------- */ +/* Write RLE Buffer at end of file. */ +/* -------------------------------------------------------------------- */ + int row = (image->ysize - nBlockYOff - 1) + (nBand-1) * image->ysize; + + VSIFSeekL(image->file, 0, SEEK_END ); + + image->rowStart[row] = (GUInt32) VSIFTellL( image->file ); + image->rowSize[row] = nRLEBytes; + image->rleTableDirty = TRUE; + + if( (int) VSIFWriteL(pabyRLEBuf, 1, nRLEBytes, image->file) != nRLEBytes ) + { + CPLFree( pabyRLEBuf ); + CPLError(CE_Failure, CPLE_OpenFailed, + "file write error: row (%d)\n", nBlockYOff ); + return CE_Failure; + } + + CPLFree( pabyRLEBuf ); + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp SGIRasterBand::GetColorInterpretation() + +{ + SGIDataset* poGDS = (SGIDataset*)poDS; + + if(poGDS->nBands == 1) + return GCI_GrayIndex; + else if(poGDS->nBands == 2) + { + if(nBand == 1) + return GCI_GrayIndex; + else + return GCI_AlphaBand; + } + else if(poGDS->nBands == 3) + { + if(nBand == 1) + return GCI_RedBand; + else if(nBand == 2) + return GCI_GreenBand; + else + return GCI_BlueBand; + } + else if(poGDS->nBands == 4) + { + if(nBand == 1) + return GCI_RedBand; + else if(nBand == 2) + return GCI_GreenBand; + else if(nBand == 3) + return GCI_BlueBand; + else + return GCI_AlphaBand; + } + return GCI_Undefined; +} + +/************************************************************************/ +/* ==================================================================== */ +/* SGIDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* SGIDataset() */ +/************************************************************************/ + +SGIDataset::SGIDataset() + : fpImage(NULL), + bGeoTransformValid(FALSE) +{ + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~SGIDataset() */ +/************************************************************************/ + +SGIDataset::~SGIDataset() + +{ + FlushCache(); + + // Do we need to write out rle table? + if( image.rleTableDirty ) + { + CPLDebug( "SGI", "Flushing RLE offset table." ); + ConvertLong( image.rowStart, image.ysize * image.zsize ); + ConvertLong( (GUInt32 *) image.rowSize, image.ysize * image.zsize ); + + VSIFSeekL( fpImage, 512, SEEK_SET ); + VSIFWriteL( image.rowStart, 4, image.ysize * image.zsize, fpImage ); + VSIFWriteL( image.rowSize, 4, image.ysize * image.zsize, fpImage ); + image.rleTableDirty = FALSE; + } + + if(fpImage != NULL) + VSIFCloseL(fpImage); + + CPLFree(image.tmp); + CPLFree(image.rowSize); + CPLFree(image.rowStart); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr SGIDataset::GetGeoTransform(double * padfTransform) + +{ + if(bGeoTransformValid) + { + memcpy(padfTransform, adfGeoTransform, sizeof(double)*6); + + return CE_None; + } + else + return GDALPamDataset::GetGeoTransform(padfTransform); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset* SGIDataset::Open(GDALOpenInfo* poOpenInfo) + +{ +/* -------------------------------------------------------------------- */ +/* First we check to see if the file has the expected header */ +/* bytes. */ +/* -------------------------------------------------------------------- */ + if(poOpenInfo->nHeaderBytes < 12) + return NULL; + + ImageRec tmpImage; + memcpy(&tmpImage, poOpenInfo->pabyHeader, 12); + tmpImage.Swap(); + + if(tmpImage.imagic != 474) + return NULL; + + if (tmpImage.type != 0 && tmpImage.type != 1) + return NULL; + + if (tmpImage.bpc != 1 && tmpImage.bpc != 2) + return NULL; + + if (tmpImage.dim != 1 && tmpImage.dim != 2 && tmpImage.dim != 3) + return NULL; + + if(tmpImage.bpc != 1) + { + CPLError(CE_Failure, CPLE_NotSupported, + "The SGI driver only supports 1 byte channel values.\n"); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + SGIDataset* poDS; + + poDS = new SGIDataset(); + poDS->eAccess = poOpenInfo->eAccess; + +/* -------------------------------------------------------------------- */ +/* Open the file using the large file api. */ +/* -------------------------------------------------------------------- */ + if( poDS->eAccess == GA_ReadOnly ) + poDS->fpImage = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + else + poDS->fpImage = VSIFOpenL(poOpenInfo->pszFilename, "rb+"); + if(poDS->fpImage == NULL) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "VSIFOpenL(%s) failed unexpectedly in sgidataset.cpp\n%s", + poOpenInfo->pszFilename, + VSIStrerror( errno ) ); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read pre-image data after ensuring the file is rewound. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL(poDS->fpImage, 0, SEEK_SET); + if(VSIFReadL((void*)(&(poDS->image)), 1, 12, poDS->fpImage) != 12) + { + CPLError(CE_Failure, CPLE_OpenFailed, "file read error while reading header in sgidataset.cpp"); + delete poDS; + return NULL; + } + poDS->image.Swap(); + poDS->image.file = poDS->fpImage; + poDS->image.fileName = poOpenInfo->pszFilename; + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = poDS->image.xsize; + poDS->nRasterYSize = poDS->image.ysize; + if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Invalid image dimensions : %d x %d", poDS->nRasterXSize, poDS->nRasterYSize); + delete poDS; + return NULL; + } + poDS->nBands = MAX(1,poDS->image.zsize); + if (poDS->nBands > 256) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Too many bands : %d", poDS->nBands); + delete poDS; + return NULL; + } + + int numItems = (int(poDS->image.bpc) == 1) ? 256 : 65536; + poDS->image.tmp = (unsigned char*)VSICalloc(poDS->image.xsize,numItems); + if (poDS->image.tmp == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read RLE Pointer tables. */ +/* -------------------------------------------------------------------- */ + if(int(poDS->image.type) == 1) // RLE compressed + { + int x = poDS->image.ysize * poDS->nBands * sizeof(GUInt32); + poDS->image.rowStart = (GUInt32*)VSIMalloc2(poDS->image.ysize, poDS->nBands * sizeof(GUInt32)); + poDS->image.rowSize = (GInt32*)VSIMalloc2(poDS->image.ysize, poDS->nBands * sizeof(GUInt32)); + if (poDS->image.rowStart == NULL || poDS->image.rowSize == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + delete poDS; + return NULL; + } + memset(poDS->image.rowStart, 0, x); + memset(poDS->image.rowSize, 0, x); + poDS->image.rleEnd = 512 + (2 * x); + VSIFSeekL(poDS->fpImage, 512, SEEK_SET); + if((int) VSIFReadL(poDS->image.rowStart, 1, x, poDS->image.file) != x) + { + delete poDS; + CPLError(CE_Failure, CPLE_OpenFailed, + "file read error while reading start positions in sgidataset.cpp"); + return NULL; + } + if((int) VSIFReadL(poDS->image.rowSize, 1, x, poDS->image.file) != x) + { + delete poDS; + CPLError(CE_Failure, CPLE_OpenFailed, + "file read error while reading row lengths in sgidataset.cpp"); + return NULL; + } + ConvertLong(poDS->image.rowStart, x/(int)sizeof(GUInt32)); + ConvertLong((GUInt32*)poDS->image.rowSize, x/(int)sizeof(GInt32)); + } + else // uncompressed. + { + poDS->image.rowStart = NULL; + poDS->image.rowSize = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for(int iBand = 0; iBand < poDS->nBands; iBand++) + poDS->SetBand(iBand+1, new SGIRasterBand(poDS, iBand+1)); + +/* -------------------------------------------------------------------- */ +/* Check for world file. */ +/* -------------------------------------------------------------------- */ + poDS->bGeoTransformValid = + GDALReadWorldFile(poOpenInfo->pszFilename, ".wld", + poDS->adfGeoTransform); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription(poOpenInfo->pszFilename); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *SGIDataset::Create( const char * pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eType, + CPL_UNUSED char **papszOptions ) +{ + if( eType != GDT_Byte ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create SGI dataset with an illegal\n" + "data type (%s), only Byte supported by the format.\n", + GDALGetDataTypeName(eType) ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open the file for output. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFOpenL( pszFilename, "w" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create file '%s': %s", + pszFilename, VSIStrerror( errno ) ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Prepare and write 512 byte header. */ +/* -------------------------------------------------------------------- */ + GByte abyHeader[512]; + GInt16 nShortValue; + GInt32 nIntValue; + + memset( abyHeader, 0, 512 ); + + abyHeader[0] = 1; + abyHeader[1] = 218; + abyHeader[2] = 1; // RLE + abyHeader[3] = 1; // 8bit + + if( nBands == 1 ) + nShortValue = CPL_MSBWORD16(2); + else + nShortValue = CPL_MSBWORD16(3); + memcpy( abyHeader + 4, &nShortValue, 2 ); + + nShortValue = CPL_MSBWORD16(nXSize); + memcpy( abyHeader + 6, &nShortValue, 2 ); + + nShortValue = CPL_MSBWORD16(nYSize); + memcpy( abyHeader + 8, &nShortValue, 2 ); + + nShortValue = CPL_MSBWORD16(nBands); + memcpy( abyHeader + 10, &nShortValue, 2 ); + + nIntValue = CPL_MSBWORD32(0); + memcpy( abyHeader + 12, &nIntValue, 4 ); + + nIntValue = CPL_MSBWORD32(255); + memcpy( abyHeader + 16, &nIntValue, 4 ); + + VSIFWriteL( abyHeader, 1, 512, fp ); + +/* -------------------------------------------------------------------- */ +/* Create our RLE compressed zero-ed dummy line. */ +/* -------------------------------------------------------------------- */ + GByte *pabyRLELine; + GInt32 nRLEBytes = 0; + int nPixelsRemaining = nXSize; + + pabyRLELine = (GByte *) CPLMalloc((nXSize/127) * 2 + 4); + + while( nPixelsRemaining > 0 ) + { + pabyRLELine[nRLEBytes] = (GByte) MIN(127,nPixelsRemaining); + pabyRLELine[nRLEBytes+1] = 0; + nPixelsRemaining -= pabyRLELine[nRLEBytes]; + + nRLEBytes += 2; + } + +/* -------------------------------------------------------------------- */ +/* Prepare and write RLE offset/size tables with everything */ +/* zeroed indicating dummy lines. */ +/* -------------------------------------------------------------------- */ + int i; + int nTableLen = nYSize * nBands; + GInt32 nDummyRLEOffset = 512 + 4 * nTableLen * 2; + + CPL_MSBPTR32( &nRLEBytes ); + CPL_MSBPTR32( &nDummyRLEOffset ); + + for( i = 0; i < nTableLen; i++ ) + VSIFWriteL( &nDummyRLEOffset, 1, 4, fp ); + + for( i = 0; i < nTableLen; i++ ) + VSIFWriteL( &nRLEBytes, 1, 4, fp ); + +/* -------------------------------------------------------------------- */ +/* write the dummy RLE blank line. */ +/* -------------------------------------------------------------------- */ + CPL_MSBPTR32( &nRLEBytes ); + if( (GInt32) VSIFWriteL( pabyRLELine, 1, nRLEBytes, fp ) != nRLEBytes ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failure writing SGI file '%s'.\n%s", + pszFilename, + VSIStrerror( errno ) ); + return NULL; + } + + VSIFCloseL( fp ); + CPLFree( pabyRLELine ); + + return (GDALDataset*) GDALOpen( pszFilename, GA_Update ); +} + +/************************************************************************/ +/* GDALRegister_SGI() */ +/************************************************************************/ + +void GDALRegister_SGI() + +{ + GDALDriver* poDriver; + + if(GDALGetDriverByName("SGI") == NULL) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription("SGI"); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, + "SGI Image File Format 1.0"); + poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "rgb"); + poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/rgb"); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#SGI" ); + poDriver->pfnOpen = SGIDataset::Open; + poDriver->pfnCreate = SGIDataset::Create; + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte" ); + GetGDALDriverManager()->RegisterDriver(poDriver); + } +} diff --git a/bazaar/plugin/gdal/frmts/srtmhgt/GNUmakefile b/bazaar/plugin/gdal/frmts/srtmhgt/GNUmakefile new file mode 100644 index 000000000..663095b58 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/srtmhgt/GNUmakefile @@ -0,0 +1,18 @@ + +include ../../GDALmake.opt + +OBJ = srtmhgtdataset.o + +CPPFLAGS := $(XTRA_OPT) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +../o/%.$(OBJ_EXT): + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ + +all: $(OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/srtmhgt/makefile.vc b/bazaar/plugin/gdal/frmts/srtmhgt/makefile.vc new file mode 100644 index 000000000..eac1b44d2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/srtmhgt/makefile.vc @@ -0,0 +1,14 @@ +GDAL_ROOT = ..\.. +!INCLUDE $(GDAL_ROOT)\nmake.opt + + +OBJ = srtmhgtdataset.obj + +EXTRAFLAGS = -I./ + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/srtmhgt/srtmhgtdataset.cpp b/bazaar/plugin/gdal/frmts/srtmhgt/srtmhgtdataset.cpp new file mode 100644 index 000000000..9a0044c94 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/srtmhgt/srtmhgtdataset.cpp @@ -0,0 +1,587 @@ +/****************************************************************************** + * $Id: srtmhgtdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: SRTM HGT Driver + * Purpose: SRTM HGT File Read Support. + * http://dds.cr.usgs.gov/srtm/version2_1/Documentation/SRTM_Topo.pdf + * http://www2.jpl.nasa.gov/srtm/faq.html + * http://dds.cr.usgs.gov/srtm/version2_1 + * Authors: Michael Mazzella, Even Rouault + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2007-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_port.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +#define SRTMHG_NODATA_VALUE -32768 + +CPL_CVSID("$Id: srtmhgtdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_SRTMHGT(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* SRTMHGTDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class SRTMHGTRasterBand; + +class SRTMHGTDataset : public GDALPamDataset +{ + friend class SRTMHGTRasterBand; + + VSILFILE* fpImage; + double adfGeoTransform[6]; + GInt16* panBuffer; + + public: + SRTMHGTDataset(); + ~SRTMHGTDataset(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform(double*); + + static int Identify( GDALOpenInfo * poOpenInfo ); + static GDALDataset* Open(GDALOpenInfo*); + static GDALDataset* CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); + +}; + +/************************************************************************/ +/* ==================================================================== */ +/* SRTMHGTRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class SRTMHGTRasterBand : public GDALPamRasterBand +{ + friend class SRTMHGTDataset; + + int bNoDataSet; + double dfNoDataValue; + + public: + SRTMHGTRasterBand(SRTMHGTDataset*, int); + + virtual CPLErr IReadBlock(int, int, void*); + virtual CPLErr IWriteBlock(int nBlockXOff, int nBlockYOff, void* pImage); + + virtual GDALColorInterp GetColorInterpretation(); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + + virtual const char* GetUnitType() { return "m"; } +}; + +/************************************************************************/ +/* SRTMHGTRasterBand() */ +/************************************************************************/ + +SRTMHGTRasterBand::SRTMHGTRasterBand(SRTMHGTDataset* poDS, int nBand) +{ + this->poDS = poDS; + this->nBand = nBand; + eDataType = GDT_Int16; + nBlockXSize = poDS->nRasterXSize; + nBlockYSize = 1; + bNoDataSet = TRUE; + dfNoDataValue = SRTMHG_NODATA_VALUE; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr SRTMHGTRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, + void* pImage) +{ + SRTMHGTDataset* poGDS = (SRTMHGTDataset*) poDS; + + CPLAssert(nBlockXOff == 0); + if(nBlockXOff != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "unhandled nBlockXOff value : %d", nBlockXOff); + return CE_Failure; + } + + if((poGDS == NULL) || (poGDS->fpImage == NULL)) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Load the desired data into the working buffer. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL(poGDS->fpImage, nBlockYOff*nBlockXSize*2, SEEK_SET); + VSIFReadL((unsigned char*)pImage, nBlockXSize, 2, poGDS->fpImage); +#ifdef CPL_LSB + GDALSwapWords(pImage, 2, nBlockXSize, 2); +#endif + + return CE_None; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr SRTMHGTRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void* pImage) +{ + SRTMHGTDataset* poGDS = (SRTMHGTDataset*) poDS; + + CPLAssert(nBlockXOff == 0); + if(nBlockXOff != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, "unhandled nBlockXOff value : %d", nBlockXOff); + return CE_Failure; + } + + if((poGDS == NULL) || (poGDS->fpImage == NULL) || (poGDS->eAccess != GA_Update)) + return CE_Failure; + + VSIFSeekL(poGDS->fpImage, nBlockYOff*nBlockXSize*2, SEEK_SET); + +#ifdef CPL_LSB + memcpy(poGDS->panBuffer, pImage, nBlockXSize*sizeof(GInt16)); + GDALSwapWords(poGDS->panBuffer, 2, nBlockXSize, 2); + VSIFWriteL((unsigned char*)poGDS->panBuffer, nBlockXSize, 2, poGDS->fpImage); +#else + VSIFWriteL((unsigned char*)pImage, nBlockXSize, 2, poGDS->fpImage); +#endif + + return CE_None; +} +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double SRTMHGTRasterBand::GetNoDataValue( int * pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bNoDataSet; + + return dfNoDataValue; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp SRTMHGTRasterBand::GetColorInterpretation() +{ + return GCI_Undefined; +} + +/************************************************************************/ +/* ==================================================================== */ +/* SRTMHGTDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* SRTMHGTDataset() */ +/************************************************************************/ + +SRTMHGTDataset::SRTMHGTDataset() +{ + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + fpImage = NULL; + panBuffer = NULL; +} + +/************************************************************************/ +/* ~SRTMHGTDataset() */ +/************************************************************************/ + +SRTMHGTDataset::~SRTMHGTDataset() +{ + FlushCache(); + if(fpImage != NULL) + VSIFCloseL(fpImage); + CPLFree(panBuffer); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr SRTMHGTDataset::GetGeoTransform(double * padfTransform) +{ + memcpy(padfTransform, adfGeoTransform, sizeof(double)*6); + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *SRTMHGTDataset::GetProjectionRef() + +{ + return( SRS_WKT_WGS84 ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int SRTMHGTDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + const char* fileName = CPLGetFilename(poOpenInfo->pszFilename); + if( strlen(fileName) < 11 || !EQUALN(&fileName[7], ".hgt", 4) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* We check the file size to see if it is 25,934,402 bytes */ +/* (SRTM 1) or 2,884,802 bytes (SRTM 3) */ +/* -------------------------------------------------------------------- */ + VSIStatBufL fileStat; + + if(VSIStatL(poOpenInfo->pszFilename, &fileStat) != 0) + return FALSE; + if(fileStat.st_size != 25934402 && fileStat.st_size != 2884802) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset* SRTMHGTDataset::Open(GDALOpenInfo* poOpenInfo) +{ + if (!Identify(poOpenInfo)) + return NULL; + + const char* fileName = CPLGetFilename(poOpenInfo->pszFilename); + + char latLonValueString[4]; + memset(latLonValueString, 0, 4); + strncpy(latLonValueString, &fileName[1], 2); + int southWestLat = atoi(latLonValueString); + memset(latLonValueString, 0, 4); + strncpy(latLonValueString, &fileName[4], 3); + int southWestLon = atoi(latLonValueString); + + if(fileName[0] == 'N' || fileName[0] == 'n') + /*southWestLat = southWestLat */; + else if(fileName[0] == 'S' || fileName[0] == 's') + southWestLat = southWestLat * -1; + else + return NULL; + + if(fileName[3] == 'E' || fileName[3] == 'e') + /*southWestLon = southWestLon */; + else if(fileName[3] == 'W' || fileName[3] == 'w') + southWestLon = southWestLon * -1; + else + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + SRTMHGTDataset* poDS; + + poDS = new SRTMHGTDataset(); + +/* -------------------------------------------------------------------- */ +/* Open the file using the large file api. */ +/* -------------------------------------------------------------------- */ + poDS->fpImage = VSIFOpenL(poOpenInfo->pszFilename, (poOpenInfo->eAccess == GA_Update) ? "rb+" : "rb"); + if(poDS->fpImage == NULL) + { + CPLError(CE_Failure, CPLE_OpenFailed, "VSIFOpenL(%s) failed unexpectedly in srtmhgtdataset.cpp", poOpenInfo->pszFilename); + return NULL; + } + + VSIStatBufL fileStat; + if(VSIStatL(poOpenInfo->pszFilename, &fileStat) != 0) + { + return NULL; + } + int numPixels = (fileStat.st_size == 25934402) ? 3601 : /* 2884802 */ 1201; + + poDS->eAccess = poOpenInfo->eAccess; +#ifdef CPL_LSB + if(poDS->eAccess == GA_Update) + { + poDS->panBuffer = (GInt16*) CPLMalloc(numPixels * sizeof(GInt16)); + } +#endif + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = numPixels; + poDS->nRasterYSize = numPixels; + poDS->nBands = 1; + + poDS->adfGeoTransform[0] = southWestLon - 0.5 / (numPixels - 1); + poDS->adfGeoTransform[1] = 1.0 / (numPixels-1); + poDS->adfGeoTransform[2] = 0.0000000000; + poDS->adfGeoTransform[3] = southWestLat + 1 + 0.5 / (numPixels - 1); + poDS->adfGeoTransform[4] = 0.0000000000; + poDS->adfGeoTransform[5] = -1.0 / (numPixels-1); + + poDS->SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT ); + +/* -------------------------------------------------------------------- */ +/* Create band information object. */ +/* -------------------------------------------------------------------- */ + SRTMHGTRasterBand* tmpBand = new SRTMHGTRasterBand(poDS, 1); + poDS->SetBand(1, tmpBand); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription(poOpenInfo->pszFilename); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return poDS; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * SRTMHGTDataset::CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, + CPL_UNUSED char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + + if( pfnProgress && !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "SRTMHGT driver does not support source dataset with zero band.\n"); + return NULL; + } + else if (nBands != 1) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "SRTMHGT driver only uses the first band of the dataset.\n"); + if (bStrict) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Checks the input SRS */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference ogrsr_input; + OGRSpatialReference ogrsr_wgs84; + char* c = (char*)poSrcDS->GetProjectionRef(); + ogrsr_input.importFromWkt(&c); + ogrsr_wgs84.SetWellKnownGeogCS( "WGS84" ); + if ( ogrsr_input.IsSameGeogCS(&ogrsr_wgs84) == FALSE) + { + CPLError( CE_Warning, CPLE_AppDefined, + "The source projection coordinate system is %s. Only WGS 84 is supported.\n" + "The SRTMHGT driver will generate a file as if the source was WGS 84 projection coordinate system.", + poSrcDS->GetProjectionRef() ); + } + +/* -------------------------------------------------------------------- */ +/* Work out the LL origin. */ +/* -------------------------------------------------------------------- */ + int nLLOriginLat, nLLOriginLong; + double adfGeoTransform[6]; + + if (poSrcDS->GetGeoTransform( adfGeoTransform ) != CE_None) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Source image must have a geo transform matrix."); + return NULL; + } + + nLLOriginLat = (int) + floor(adfGeoTransform[3] + + poSrcDS->GetRasterYSize() * adfGeoTransform[5] + 0.5); + + nLLOriginLong = (int) floor(adfGeoTransform[0] + 0.5); + + if (fabs(nLLOriginLat - (adfGeoTransform[3] + + (poSrcDS->GetRasterYSize() - 0.5) * adfGeoTransform[5])) > 1e-10 || + fabs(nLLOriginLong - (adfGeoTransform[0] + 0.5 * adfGeoTransform[1])) > 1e-10) + { + CPLError( CE_Warning, CPLE_AppDefined, + "The corner coordinates of the source are not properly " + "aligned on plain latitude/longitude boundaries."); + } + +/* -------------------------------------------------------------------- */ +/* Check image dimensions. */ +/* -------------------------------------------------------------------- */ + if (!((nXSize == 1201 && nYSize == 1201) || (nXSize == 3601 && nYSize == 3601))) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Image dimensions should be 1201x1201 or 3601x3601."); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Check filename. */ +/* -------------------------------------------------------------------- */ + char expectedFileName[12]; + snprintf(expectedFileName, sizeof(expectedFileName), "%c%02d%c%03d.HGT", + (nLLOriginLat >= 0) ? 'N' : 'S', + (nLLOriginLat >= 0) ? nLLOriginLat : -nLLOriginLat, + (nLLOriginLong >= 0) ? 'E' : 'W', + (nLLOriginLong >= 0) ? nLLOriginLong : -nLLOriginLong); + if (!EQUAL(expectedFileName, CPLGetFilename(pszFilename))) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Expected output filename is %s.", expectedFileName); + } + +/* -------------------------------------------------------------------- */ +/* Write output file. */ +/* -------------------------------------------------------------------- */ + VSILFILE* fp = VSIFOpenL(pszFilename, "wb"); + if (fp == NULL) + { + CPLError( CE_Failure, CPLE_FileIO, + "Cannot create file %s", pszFilename ); + return NULL; + } + + GInt16* panData = (GInt16*) CPLMalloc(sizeof(GInt16) * nXSize); + GDALRasterBand* poSrcBand = poSrcDS->GetRasterBand(1); + + int bSrcBandHasNoData; + double srcBandNoData = poSrcBand->GetNoDataValue(&bSrcBandHasNoData); + + for( int iY = 0; iY < nYSize; iY++ ) + { + poSrcBand->RasterIO( GF_Read, 0, iY, nXSize, 1, + (void *) panData, nXSize, 1, + GDT_Int16, 0, 0, NULL ); + + /* Translate nodata values */ + if (bSrcBandHasNoData && srcBandNoData != SRTMHG_NODATA_VALUE) + { + for( int iX = 0; iX < nXSize; iX++ ) + { + if (panData[iX] == srcBandNoData) + panData[iX] = SRTMHG_NODATA_VALUE; + } + } + +#ifdef CPL_LSB + GDALSwapWords(panData, 2, nXSize, 2); +#endif + + if( VSIFWriteL( panData,sizeof(GInt16) * nXSize,1,fp ) != 1) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to write line %d in SRTMHGT dataset.\n", + iY ); + VSIFCloseL(fp); + CPLFree( panData ); + return NULL; + } + + if( pfnProgress && !pfnProgress((iY+1) / (double) nYSize, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + VSIFCloseL(fp); + CPLFree( panData ); + return NULL; + } + } + + CPLFree( panData ); + VSIFCloseL(fp); + +/* -------------------------------------------------------------------- */ +/* Reopen and copy missing information into a PAM file. */ +/* -------------------------------------------------------------------- */ + GDALPamDataset *poDS = (GDALPamDataset *) + GDALOpen( pszFilename, GA_ReadOnly ); + + if( poDS ) + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_SRTMHGT() */ +/************************************************************************/ +void GDALRegister_SRTMHGT() +{ + GDALDriver* poDriver; + + if(GDALGetDriverByName("SRTMHGT") == NULL) + { + poDriver = new GDALDriver(); + poDriver->SetDescription("SRTMHGT"); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "SRTMHGT File Format"); + poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "hgt"); + poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, + "frmt_various.html#SRTMHGT" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = SRTMHGTDataset::Identify; + poDriver->pfnOpen = SRTMHGTDataset::Open; + poDriver->pfnCreateCopy = SRTMHGTDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver(poDriver); + } +} diff --git a/bazaar/plugin/gdal/frmts/terragen/GNUmakefile b/bazaar/plugin/gdal/frmts/terragen/GNUmakefile new file mode 100644 index 000000000..113c183b0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/terragen/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = terragendataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/terragen/frmt_terragen.html b/bazaar/plugin/gdal/frmts/terragen/frmt_terragen.html new file mode 100644 index 000000000..ecaa9fce6 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/terragen/frmt_terragen.html @@ -0,0 +1,86 @@ + + + Terragen --- Terragen Terrain File + + + + +

    Terragen --- Terragen™ Terrain File

    + +Terragen terrain files store 16-bit elevation values with +optional gridspacing (but not positioning). +The file extension for Terragen heightfields is "TER" or "TERRAIN" (which +in the former case is the same as Leveller, but the driver only recognizes Terragen files). The driver ID is "Terragen". +The dataset is file-based and has only one elevation band. +Void elevations are not supported. Pixels are considered points. +

    + +

    Reading

    +
    +dataset::GetProjectionRef() returns a local coordinate system +using meters. +

    +band::GetUnitType() returns meters. +

    +Elevations are Int16. You must use the band::GetScale() and +band::GetOffset() to convert them to meters. +

     
    +

    + +

    Writing

    +
    +Use the Create call. Set the MINUSERPIXELVALUE option (a float) +to the lowest elevation of your elevation data, and +MAXUSERPIXELVALUE to the highest. The units must match +the elevation units you will give to band::SetUnitType(). +

    +Call dataset::SetProjection() and dataset::SetGeoTransform() +with your coordinate system details. Otherwise, the driver +will not encode physical elevations properly. Geographic +(degree-based) coordinate systems will be converted to +a local meter-based system. +

    +To maintain precision, a best-fit baseheight and scaling will +be used to use as much of the 16-bit range as possible. +

    +Elevations are Float32. +

     
    +

    + + +

    Roundtripping

    +
    +Errors per trip tend to be a few centimeters for elevations +and up to one or two meters for ground extents if +degree-based coordinate systems are written. Large degree-based +DEMs incur unavoidable distortions since the driver currently +only uses meters. +

     
    +

    + +

    + +

    History

    +v1.0 (Mar 26/06): Created.
    +v1.1 (Apr 20/06): Added Create() support and SIZE-only read fix.
    +v1.2 (Jun 6/07): Improved baseheight/scale determination when writing. +

    + +

    See Also:

    + + + + + + + diff --git a/bazaar/plugin/gdal/frmts/terragen/makefile.vc b/bazaar/plugin/gdal/frmts/terragen/makefile.vc new file mode 100644 index 000000000..4b6de67bb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/terragen/makefile.vc @@ -0,0 +1,11 @@ +OBJ = terragendataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj diff --git a/bazaar/plugin/gdal/frmts/terragen/readme.txt b/bazaar/plugin/gdal/frmts/terragen/readme.txt new file mode 100644 index 000000000..f3d73e3f1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/terragen/readme.txt @@ -0,0 +1,14 @@ +Terragen(tm) GDAL Driver 1.2 +Copyright 2006-2007 Daylon Graphics Ltd. +Support: support@daylongraphics.com + + +This is a GDAL driver for Terragen heightfield files. + +... + +For more information on Terragen, please visit +the Planetside website at +http://www.planetside.co.uk/terragen + + diff --git a/bazaar/plugin/gdal/frmts/terragen/terragendataset.cpp b/bazaar/plugin/gdal/frmts/terragen/terragendataset.cpp new file mode 100644 index 000000000..57e3ecc7d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/terragen/terragendataset.cpp @@ -0,0 +1,1165 @@ +/****************************************************************************** + * terragendataset.cpp,v 1.2 + * + * Project: Terragen(tm) TER Driver + * Purpose: Reader for Terragen TER documents + * Author: Ray Gardener, Daylon Graphics Ltd. + * + * Portions of this module derived from GDAL drivers by + * Frank Warmerdam, see http://www.gdal.org + + rcg apr 19/06 Fixed bug with hf size being misread by one + if xpts/ypts tags not included in file. + Added Create() support. + Treat pixels as points. + + rcg jun 6/07 Better heightscale/baseheight determination + when writing. + + * + ****************************************************************************** + * Copyright (c) 2006-2007 Daylon Graphics Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + */ + +/* + Terragen format notes: + + Based on official Planetside specs. + + All distances along all three axes are in + terrain units, which are 30m by default. + If a SCAL chunk is present, however, it + can indicate something other than 30. + Note that uniform scaling should be used. + + The offset (base height) in the ALTW chunk + is in terrain units, and the scale (height scale) + is a normalized value using unsigned 16-bit notation. + The physical terrain value for a read pixel is + hv' = hv * scale / 65536 + offset. + It still needs to be scaled by SCAL to + get to meters. + + For writing: + SCAL = gridpost distance in meters + hv_px = hv_m / SCAL + span_px = span_m / SCAL + offset = see TerragenDataset::write_header() + scale = see TerragenDataset::write_header() + physical hv = + (hv_px - offset) * 65536.0/scale + + + We tell callers that: + + Elevations are Int16 when reading, + and Float32 when writing. We need logical + elevations when writing so that we can + encode them with as much precision as possible + when going down to physical 16-bit ints. + Implementing band::SetScale/SetOffset won't work because + it requires callers to know format write details. + So we've added two Create() options that let the + caller tell us the span's logical extent, and with + those two values we can convert to physical pixels. + + band::GetUnitType() returns meters. + band::GetScale() returns SCAL * (scale/65536) + band::GetOffset() returns SCAL * offset + ds::GetProjectionRef() returns a local CS + using meters. + ds::GetGeoTransform() returns a scale matrix + having SCAL sx,sy members. + + ds::SetGeoTransform() lets us establish the + size of ground pixels. + ds::SetProjection() lets us establish what + units ground measures are in (also needed + to calc the size of ground pixels). + band::SetUnitType() tells us what units + the given Float32 elevations are in. + band::SetScale() is unused. + band::SetOffset() is unused. +*/ + +#include "cpl_string.h" +#include "gdal_pam.h" +#include "ogr_spatialref.h" + +// CPL_CVSID("$Id: terragendataset.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void GDALRegister_Terragen(void); +CPL_C_END + + +const double kdEarthCircumPolar = 40007849; +const double kdEarthCircumEquat = 40075004; + +#define str_equal(_s1, _s2) (0 == strcmp((_s1),(_s2))) +#define array_size(_a) (sizeof(_a) / sizeof(_a[0])) + +#ifndef min + #define min(a, b) ( (a) < (b) ? (a) : (b) ) +#endif + +static double average(double a, double b) +{ + return 0.5 * (a + b); +} + + +static double degrees_to_radians(double d) +{ + return (d * 0.017453292); +} + +static bool approx_equal(double a, double b) +{ + const double epsilon = 1e-5; + return (fabs(a-b) <= epsilon); +} + + + +/************************************************************************/ +/* ==================================================================== */ +/* TerragenDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class TerragenRasterBand; + +class TerragenDataset : public GDALPamDataset +{ + friend class TerragenRasterBand; + + + double m_dScale, + m_dOffset, + m_dSCAL, // 30.0 normally, from SCAL chunk + m_adfTransform[6], + m_dGroundScale, + m_dMetersPerGroundUnit, + m_dMetersPerElevUnit, + m_dLogSpan[2], + m_span_m[2], + m_span_px[2]; + + VSILFILE* m_fp; + vsi_l_offset m_nDataOffset; + + GInt16 m_nHeightScale; + GInt16 m_nBaseHeight; + + char* m_pszFilename; + char* m_pszProjection; + char m_szUnits[32]; + + bool m_bIsGeo; + + + int LoadFromFile(); + + + public: + TerragenDataset(); + ~TerragenDataset(); + + static GDALDataset* Open( GDALOpenInfo* ); + static GDALDataset* Create( const char* pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char** papszOptions ); + + virtual CPLErr GetGeoTransform( double* ); + virtual const char* GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + virtual CPLErr SetGeoTransform( double * ); + + protected: + bool get(GInt16&); + bool get(GUInt16&); + bool get(float&); + bool put(GInt16); + bool put(float); + bool skip(size_t n) { return ( 0 == VSIFSeekL(m_fp, n, SEEK_CUR) ); } + bool pad(size_t n) { return this->skip(n); } + + bool read_next_tag(char*); + bool write_next_tag(const char*); + bool tag_is(const char* szTag, const char*); + + bool write_header(void); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* TerragenRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class TerragenRasterBand : public GDALPamRasterBand +{ + friend class TerragenDataset; + + void* m_pvLine; + bool m_bFirstTime; + +public: + + TerragenRasterBand(TerragenDataset*); + virtual ~TerragenRasterBand() + { + if(m_pvLine != NULL) + CPLFree(m_pvLine); + } + + // Geomeasure support. + virtual CPLErr IReadBlock( int, int, void * ); + virtual const char* GetUnitType(); + virtual double GetOffset(int* pbSuccess = NULL); + virtual double GetScale(int* pbSuccess = NULL); + + virtual CPLErr IWriteBlock( int, int, void * ); + virtual CPLErr SetUnitType( const char* ); +}; + + +/************************************************************************/ +/* TerragenRasterBand() */ +/************************************************************************/ + +TerragenRasterBand::TerragenRasterBand( TerragenDataset *poDS ) +{ + m_bFirstTime = true; + this->poDS = poDS; + this->nBand = 1; + + eDataType = poDS->GetAccess() == GA_ReadOnly + ? GDT_Int16 + : GDT_Float32; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + m_pvLine = CPLMalloc(sizeof(GInt16) * nBlockXSize); +} + + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr TerragenRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void* pImage ) +{ + //CPLAssert( sizeof(float) == sizeof(GInt32) ); + CPLAssert( nBlockXOff == 0 ); + CPLAssert( pImage != NULL ); + + TerragenDataset& ds = *(TerragenDataset *) poDS; + +/* -------------------------------------------------------------------- */ +/* Seek to scanline. + Terragen is a bottom-top format, so we have to + invert the row location. + -------------------------------------------------------------------- */ + const size_t rowbytes = nBlockXSize * sizeof(GInt16); + + if(0 != VSIFSeekL( + ds.m_fp, + ds.m_nDataOffset + + (ds.GetRasterYSize() -1 - nBlockYOff) * rowbytes, + SEEK_SET)) + { + CPLError( CE_Failure, CPLE_FileIO, + "Terragen Seek failed:%s", VSIStrerror( errno ) ); + return CE_Failure; + } + + +/* -------------------------------------------------------------------- */ +/* Read the scanline into the line buffer. */ +/* -------------------------------------------------------------------- */ + + if( VSIFReadL( pImage, rowbytes, 1, ds.m_fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Terragen read failed:%s", VSIStrerror( errno ) ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Swap on MSB platforms. */ +/* -------------------------------------------------------------------- */ +#ifdef CPL_MSB + GDALSwapWords( pImage, sizeof(GInt16), nRasterXSize, sizeof(GInt16) ); +#endif + + return CE_None; +} + + + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ +const char *TerragenRasterBand::GetUnitType() +{ + // todo: Return elevation units. + // For Terragen documents, it's the same as the ground units. + TerragenDataset *poGDS = (TerragenDataset *) poDS; + + return poGDS->m_szUnits; +} + + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ + +double TerragenRasterBand::GetScale(int* pbSuccess) +{ + const TerragenDataset& ds = *(TerragenDataset *) poDS; + if(pbSuccess != NULL) + *pbSuccess = TRUE; + return ds.m_dScale; +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +double TerragenRasterBand::GetOffset(int* pbSuccess) +{ + const TerragenDataset& ds = *(TerragenDataset *) poDS; + if(pbSuccess != NULL) + *pbSuccess = TRUE; + return ds.m_dOffset; +} + + + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr TerragenRasterBand::IWriteBlock +( + CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void* pImage +) +{ + CPLAssert( nBlockXOff == 0 ); + CPLAssert( pImage != NULL ); + CPLAssert( m_pvLine != NULL ); + + #define sgn(_n) ((_n) < 0 ? -1 : ((_n) > 0 ? 1 : 0) ) + #define sround(_f) \ + (int)((_f) + (0.5 * sgn(_f))) + + const size_t pixelsize = sizeof(GInt16); + + + TerragenDataset& ds = *(TerragenDataset*)poDS; + if(m_bFirstTime) + { + m_bFirstTime = false; + ds.write_header(); + ds.m_nDataOffset = VSIFTellL(ds.m_fp); + } + const size_t rowbytes = nBlockXSize * pixelsize; + + GInt16* pLine = (GInt16*)m_pvLine; + + + if(0 == VSIFSeekL( + ds.m_fp, + ds.m_nDataOffset + + // Terragen is Y inverted. + (ds.GetRasterYSize()-1-nBlockYOff) * rowbytes, + SEEK_SET)) + { + // Convert each float32 to int16. + float* pfImage = (float*)pImage; + for(size_t x = 0; x < (size_t)nBlockXSize; x++) + { + double f = pfImage[x]; + f *= ds.m_dMetersPerElevUnit; + f /= ds.m_dSCAL; + GInt16 hv = + (GInt16)((f - ds.m_nBaseHeight) * + 65536.0 / ds.m_nHeightScale /*+ ds.m_nShift*/); + pLine[x] = hv; + } + +#ifdef CPL_MSB + GDALSwapWords( m_pvLine, pixelsize, nBlockXSize, pixelsize ); +#endif + if(1 == VSIFWriteL(m_pvLine, rowbytes, 1, ds.m_fp)) + return CE_None; + } + + return CE_Failure; +} + + +CPLErr TerragenRasterBand::SetUnitType( const char* psz ) +{ + TerragenDataset& ds = *(TerragenDataset*)poDS; + + if(EQUAL(psz, "m")) + ds.m_dMetersPerElevUnit = 1.0; + else if(EQUAL(psz, "ft")) + ds.m_dMetersPerElevUnit = 0.3048; + else if(EQUAL(psz, "sft")) + ds.m_dMetersPerElevUnit = 1200.0 / 3937.0; + else + return CE_Failure; + + return CE_None; +} + + + +/************************************************************************/ +/* ==================================================================== */ +/* TerragenDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* TerragenDataset() */ +/************************************************************************/ + +TerragenDataset::TerragenDataset() +{ + m_pszFilename = NULL; + m_fp = NULL; + m_bIsGeo = false; + m_pszProjection = NULL; + m_nHeightScale = 0; // fix for ticket 2119 + m_nBaseHeight = 0; + m_dMetersPerGroundUnit = 1.0; + m_dSCAL = 30.0; + m_dLogSpan[0] = m_dLogSpan[1] = 0.0; + + m_adfTransform[0] = 0.0; + m_adfTransform[1] = m_dSCAL; + m_adfTransform[2] = 0.0; + m_adfTransform[3] = 0.0; + m_adfTransform[4] = 0.0; + m_adfTransform[5] = m_dSCAL; +} + +/************************************************************************/ +/* ~TerragenDataset() */ +/************************************************************************/ + +TerragenDataset::~TerragenDataset() + +{ + FlushCache(); + + CPLFree(m_pszProjection); + CPLFree(m_pszFilename); + + if( m_fp != NULL ) + VSIFCloseL( m_fp ); +} + + + +bool TerragenDataset::write_header() +{ + char szHeader[16]; + memcpy(szHeader, "TERRAGENTERRAIN ", sizeof(szHeader)); + + if(1 != VSIFWriteL( (void *) szHeader, sizeof(szHeader), 1, m_fp )) + { + CPLError( CE_Failure, CPLE_FileIO, + "Couldn't write to Terragen file %s.\n" + "Is file system full?", + m_pszFilename ); + VSIFCloseL( m_fp ); + + return false; + } + + +// -------------------------------------------------------------------- +// Write out the heightfield dimensions, etc. +// -------------------------------------------------------------------- + + const int nXSize = this->GetRasterXSize(); + const int nYSize = this->GetRasterYSize(); + + this->write_next_tag("SIZE"); + this->put((GInt16)(min(nXSize, nYSize)-1)); + this->pad(sizeof(GInt16)); + + if(nXSize != nYSize) + { + this->write_next_tag("XPTS"); + this->put((GInt16)nXSize); this->pad(sizeof(GInt16)); + this->write_next_tag("YPTS"); + this->put((GInt16)nYSize); this->pad(sizeof(GInt16)); + } + + if(m_bIsGeo) + { + /* + With a geographic projection (degrees), + m_dGroundScale will be in degrees and + m_dMetersPerGroundUnit is undefined. + So we're going to estimate a m_dMetersPerGroundUnit + value here (i.e., meters per degree). + + We figure out the degree size of one + pixel, and then the latitude degrees + of the heightfield's center. The circumference of + the latitude's great circle lets us know how + wide the pixel is in meters, and we + average that with the pixel's meter breadth, + which is based on the polar circumference. + */ + + /*const double m_dDegLongPerPixel = + fabs(m_adfTransform[1]);*/ + + const double m_dDegLatPerPixel = + fabs(m_adfTransform[5]); + + /*const double m_dCenterLongitude = + m_adfTransform[0] + + (0.5 * m_dDegLongPerPixel * (nXSize-1));*/ + + const double m_dCenterLatitude = + m_adfTransform[3] + + (0.5 * m_dDegLatPerPixel * (nYSize-1)); + + + const double dLatCircum = kdEarthCircumEquat + * sin(degrees_to_radians(90.0 - m_dCenterLatitude)); + + const double dMetersPerDegLongitude = dLatCircum / 360; + /*const double dMetersPerPixelX = + (m_dDegLongPerPixel / 360) * dLatCircum;*/ + + const double dMetersPerDegLatitude = + kdEarthCircumPolar / 360; + /*const double dMetersPerPixelY = + (m_dDegLatPerPixel / 360) * kdEarthCircumPolar;*/ + + m_dMetersPerGroundUnit = + average(dMetersPerDegLongitude, dMetersPerDegLatitude); + + } + + m_dSCAL = m_dGroundScale * m_dMetersPerGroundUnit; + + if(m_dSCAL != 30.0) + { + const float sc = (float)m_dSCAL; + this->write_next_tag("SCAL"); + this->put(sc); + this->put(sc); + this->put(sc); + } + + if(!this->write_next_tag("ALTW")) + { + CPLError( CE_Failure, CPLE_FileIO, + "Couldn't write to Terragen file %s.\n" + "Is file system full?", + m_pszFilename ); + VSIFCloseL( m_fp ); + + return false; + } + + // Compute physical scales and offsets. + m_span_m[0] = m_dLogSpan[0] * m_dMetersPerElevUnit; + m_span_m[1] = m_dLogSpan[1] * m_dMetersPerElevUnit; + + m_span_px[0] = m_span_m[0] / m_dSCAL; + m_span_px[1] = m_span_m[1] / m_dSCAL; + + const double span_px = m_span_px[1] - m_span_px[0]; + m_nHeightScale = (GInt16)span_px; + if(m_nHeightScale == 0) + m_nHeightScale++; + + #define P2L_PX(n, hs, bh) \ + ((double)(n) / 65536.0 * (hs) + (bh)) + + #define L2P_PX(n, hs, bh) \ + ((int)(((n)-(bh)) * 65536.0 / (hs))) + + // Increase the heightscale until the physical span + // fits within a 16-bit range. The smaller the logical span, + // the more necessary this becomes. + int hs, bh=0; + for(hs = m_nHeightScale; hs <= 32767; hs++) + { + double prevdelta = 1.0e30; + for(bh = -32768; bh <= 32767; bh++) + { + int nValley = L2P_PX(m_span_px[0], hs, bh); + if(nValley < -32768) continue; + int nPeak = L2P_PX(m_span_px[1], hs, bh); + if(nPeak > 32767) continue; + + // now see how closely the baseheight gets + // to the pixel span. + double d = P2L_PX(nValley, hs, bh); + double delta = fabs(d - m_span_px[0]); + if(delta < prevdelta) // Converging? + prevdelta = delta; + else + { + // We're diverging, so use the previous bh + // and stop looking. + bh--; + break; + } + } + if(bh != 32768) break; + } + if(hs == 32768) + { + CPLError( CE_Failure, CPLE_FileIO, + "Couldn't write to Terragen file %s.\n" + "Cannot find adequate heightscale/baseheight combination.", + m_pszFilename ); + VSIFCloseL( m_fp ); + + return false; + } + + m_nHeightScale = (GInt16) hs; + m_nBaseHeight = (GInt16) bh; + + + // m_nHeightScale is the one that gives us the + // widest use of the 16-bit space. However, there + // might be larger heightscales that, even though + // the reduce the space usage, give us a better fit + // for preserving the span extents. + + + return (this->put(m_nHeightScale) && + this->put(m_nBaseHeight)); +} + + + +/************************************************************************/ +/* get() */ +/************************************************************************/ + +bool TerragenDataset::get(GInt16& value) +{ + if(1 == VSIFReadL(&value, sizeof(value), 1, m_fp)) + { + CPL_LSBPTR16(&value); + return true; + } + return false; +} + + +bool TerragenDataset::get(GUInt16& value) +{ + if(1 == VSIFReadL(&value, sizeof(value), 1, m_fp)) + { + CPL_LSBPTR16(&value); + return true; + } + return false; +} + + +bool TerragenDataset::get(float& value) +{ + if(1 == VSIFReadL(&value, sizeof(value), 1, m_fp)) + { + CPL_LSBPTR32(&value); + return true; + } + return false; +} + + +/************************************************************************/ +/* put() */ +/************************************************************************/ + +bool TerragenDataset::put(GInt16 n) +{ + CPL_LSBPTR16(&n); + return (1 == VSIFWriteL(&n, sizeof(n), 1, m_fp)); +} + + +bool TerragenDataset::put(float f) +{ + CPL_LSBPTR32(&f); + return( 1 == VSIFWriteL(&f, sizeof(f), 1, m_fp) ); +} + +/************************************************************************/ +/* tag stuff */ +/************************************************************************/ + + +bool TerragenDataset::read_next_tag(char* szTag) +{ + return (1 == VSIFReadL(szTag, 4, 1, m_fp)); +} + + +bool TerragenDataset::write_next_tag(const char* szTag) +{ + return (1 == VSIFWriteL((void*)szTag, 4, 1, m_fp)); +} + + +bool TerragenDataset::tag_is(const char* szTag, const char* sz) +{ + return (0 == memcmp(szTag, sz, 4)); +} + + + +/************************************************************************/ +/* LoadFromFile() */ +/************************************************************************/ + +int TerragenDataset::LoadFromFile() +{ + GUInt16 nSize, xpts=0, ypts=0; + m_dSCAL = 30.0; + m_nDataOffset = 0; + + if(0 != VSIFSeekL(m_fp, 16, SEEK_SET)) + return 0; + + char szTag[4]; + if(!this->read_next_tag(szTag) || !tag_is(szTag, "SIZE")) + return 0; + + if(!this->get(nSize) || !this->skip(2)) + return 0; + + // Set dimensions to SIZE chunk. If we don't + // encounter XPTS/YPTS chunks, we can assume + // the terrain to be square. + xpts = ypts = nSize+1; + + while(this->read_next_tag(szTag)) + { + if(this->tag_is(szTag, "XPTS")) + { + this->get(xpts); + if(xpts < nSize || !this->skip(2)) + return 0; + continue; + } + + if(this->tag_is(szTag, "YPTS")) + { + this->get(ypts); + if(ypts < nSize || !this->skip(2)) + return 0; + continue; + } + + if(this->tag_is(szTag, "SCAL")) + { + float sc[3]; + this->get(sc[0]); + this->get(sc[1]); + this->get(sc[2]); + m_dSCAL = sc[1]; + continue; + } + + if(this->tag_is(szTag, "CRAD")) + { + if(!this->skip(sizeof(float))) + return 0; + continue; + } + if(this->tag_is(szTag, "CRVM")) + { + if(!this->skip(sizeof(GUInt32))) + return 0; + continue; + } + if(this->tag_is(szTag, "ALTW")) + { + this->get(m_nHeightScale); + this->get(m_nBaseHeight); + m_nDataOffset = VSIFTellL(m_fp); + if(!this->skip(xpts * ypts * sizeof(GInt16))) + return 0; + continue; + } + if(this->tag_is(szTag, "EOF ")) + { + break; + } + } + + + if(xpts == 0 || ypts == 0 || m_nDataOffset == 0) + return 0; + + nRasterXSize = xpts; + nRasterYSize = ypts; + + // todo: sanity check: do we have enough pixels? + + // Cache realworld scaling and offset. + m_dScale = m_dSCAL / 65536 * m_nHeightScale; + m_dOffset = m_dSCAL * m_nBaseHeight; + strcpy(m_szUnits, "m"); + + // Make our projection to have origin at the + // NW corner, and groundscale to match elev scale + // (i.e., uniform voxels). + m_adfTransform[0] = 0.0; + m_adfTransform[1] = m_dSCAL; + m_adfTransform[2] = 0.0; + m_adfTransform[3] = 0.0; + m_adfTransform[4] = 0.0; + m_adfTransform[5] = m_dSCAL; + + +/* -------------------------------------------------------------------- */ +/* Set projection. */ +/* -------------------------------------------------------------------- */ + // Terragen files as of Apr 2006 are partially georeferenced, + // we can declare a local coordsys that uses meters. + OGRSpatialReference sr; + + sr.SetLocalCS("Terragen world space"); + if(OGRERR_NONE != sr.SetLinearUnits("m", 1.0)) + return 0; + + if(OGRERR_NONE != sr.exportToWkt(&m_pszProjection)) + return 0; + + return TRUE; +} + + + + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr TerragenDataset::SetProjection( const char * pszNewProjection ) +{ + // Terragen files aren't really georeferenced, but + // we should get the projection's linear units so + // that we can scale elevations correctly. + + //m_dSCAL = 30.0; // default + + OGRSpatialReference oSRS( pszNewProjection ); + +/* -------------------------------------------------------------------- */ +/* Linear units. */ +/* -------------------------------------------------------------------- */ + m_bIsGeo = (oSRS.IsGeographic() != FALSE); + if(m_bIsGeo) + { + // The caller is using degrees. We need to convert + // to meters, otherwise we can't derive a SCAL + // value to scale elevations with. + m_bIsGeo = true; + } + else + { + double dfLinear = oSRS.GetLinearUnits(); + + if( approx_equal(dfLinear, 0.3048)) + m_dMetersPerGroundUnit = 0.3048; + else if( approx_equal(dfLinear, CPLAtof(SRS_UL_US_FOOT_CONV)) ) + m_dMetersPerGroundUnit = CPLAtof(SRS_UL_US_FOOT_CONV); + else + m_dMetersPerGroundUnit = 1.0; + } + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* TerragenDataset::GetProjectionRef(void) +{ + if(m_pszProjection == NULL ) + return ""; + else + return m_pszProjection; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr TerragenDataset::SetGeoTransform( double *padfGeoTransform ) +{ + memcpy(m_adfTransform, padfGeoTransform, + sizeof(m_adfTransform)); + + // Average the projection scales. + m_dGroundScale = + average(fabs(m_adfTransform[1]), fabs(m_adfTransform[5])); + return CE_None; +} + + + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr TerragenDataset::GetGeoTransform(double* padfTransform) +{ + memcpy(padfTransform, m_adfTransform, sizeof(m_adfTransform)); + return CE_None; +} + + +/************************************************************************/ +/* Create() */ +/************************************************************************/ +GDALDataset* TerragenDataset::Create +( + const char* pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char** papszOptions +) +{ + TerragenDataset* poDS = new TerragenDataset(); + + poDS->eAccess = GA_Update; + + poDS->m_pszFilename = CPLStrdup(pszFilename); + + // -------------------------------------------------------------------- + // Verify input options. + // -------------------------------------------------------------------- + const char* pszValue = CSLFetchNameValue( + papszOptions,"MINUSERPIXELVALUE"); + if( pszValue != NULL ) + poDS->m_dLogSpan[0] = CPLAtof( pszValue ); + + pszValue = CSLFetchNameValue( + papszOptions,"MAXUSERPIXELVALUE"); + if( pszValue != NULL ) + poDS->m_dLogSpan[1] = CPLAtof( pszValue ); + + + if( poDS->m_dLogSpan[1] <= poDS->m_dLogSpan[0] ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Inverted, flat, or unspecified span for Terragen file." ); + + delete poDS; + return NULL; + } + + if( eType != GDT_Float32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create Terragen dataset with a non-float32\n" + "data type (%s).\n", + GDALGetDataTypeName(eType) ); + + delete poDS; + return NULL; + } + + + if( nBands != 1 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Terragen driver doesn't support %d bands. Must be 1.\n", + nBands ); + + delete poDS; + return NULL; + } + + +// -------------------------------------------------------------------- +// Try to create the file. +// -------------------------------------------------------------------- + + + poDS->m_fp = VSIFOpenL( pszFilename, "wb+" ); + + if( poDS->m_fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create file `%s' failed.\n", + pszFilename ); + delete poDS; + return NULL; + } + + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + + // Don't bother writing the header here; the first + // call to IWriteBlock will do that instead, since + // the elevation data's location depends on the + // header size. + + +// -------------------------------------------------------------------- +// Instance a band. +// -------------------------------------------------------------------- + poDS->SetBand( 1, new TerragenRasterBand( poDS ) ); + + + //VSIFClose( poDS->m_fp ); + + //return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); + return (GDALDataset *) poDS; + +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *TerragenDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + // The file should have at least 32 header bytes + if( poOpenInfo->nHeaderBytes < 32 ) + return NULL; + + if( !EQUALN((const char *) poOpenInfo->pabyHeader, + "TERRAGENTERRAIN ", 16) ) + return NULL; + + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + TerragenDataset *poDS; + + poDS = new TerragenDataset(); + + // Reopen for large file access. + if( poOpenInfo->eAccess == GA_Update ) + poDS->m_fp = VSIFOpenL( poOpenInfo->pszFilename, "rb+" ); + else + poDS->m_fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + + if( poDS->m_fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to re-open %s within Terragen driver.\n", + poOpenInfo->pszFilename ); + return NULL; + } + poDS->eAccess = poOpenInfo->eAccess; + + +/* -------------------------------------------------------------------- */ +/* Read the file. */ +/* -------------------------------------------------------------------- */ + if( !poDS->LoadFromFile() ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->SetBand( 1, new TerragenRasterBand( poDS )); + + poDS->SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_Terragen() */ +/************************************************************************/ + +void GDALRegister_Terragen() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "Terragen" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "Terragen" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, + "ter" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Terragen heightfield" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_terragen.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); + + poDriver->pfnOpen = TerragenDataset::Open; + poDriver->pfnCreate = TerragenDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/til/GNUmakefile b/bazaar/plugin/gdal/frmts/til/GNUmakefile new file mode 100644 index 000000000..d4d4363e8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/til/GNUmakefile @@ -0,0 +1,15 @@ + +include ../../GDALmake.opt + +OBJ = tildataset.o + +CPPFLAGS := -I../vrt $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +$(OBJ) $(O_OBJ): ../vrt/vrtdataset.h + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/til/makefile.vc b/bazaar/plugin/gdal/frmts/til/makefile.vc new file mode 100644 index 000000000..0bfe2d86c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/til/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = tildataset.obj + +GDAL_ROOT = ..\.. + +EXTRAFLAGS = -I..\vrt + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/til/tildataset.cpp b/bazaar/plugin/gdal/frmts/til/tildataset.cpp new file mode 100644 index 000000000..e329e67d0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/til/tildataset.cpp @@ -0,0 +1,514 @@ +/****************************************************************************** + * $Id: tildataset.cpp 29198 2015-05-15 08:45:00Z rouault $ + * + * Project: EarthWatch .TIL Driver + * Purpose: Implementation of the TILDataset class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "gdal_proxy.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include "vrtdataset.h" +#include "cpl_multiproc.h" +#include "cplkeywordparser.h" +#include "gdal_mdreader.h" + +CPL_CVSID("$Id: tildataset.cpp 29198 2015-05-15 08:45:00Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* TILDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class CPL_DLL TILDataset : public GDALPamDataset +{ + VRTDataset *poVRTDS; + std::vector apoTileDS; + + char **papszMetadataFiles; + + protected: + virtual int CloseDependentDatasets(); + + public: + TILDataset(); + ~TILDataset(); + + virtual char **GetFileList(void); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo *poOpenInfo ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* TILRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class TILRasterBand : public GDALPamRasterBand +{ + friend class TILDataset; + + VRTSourcedRasterBand *poVRTBand; + + public: + TILRasterBand( TILDataset *, int, VRTSourcedRasterBand * ); + virtual ~TILRasterBand() {}; + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); +}; + +/************************************************************************/ +/* TILRasterBand() */ +/************************************************************************/ + +TILRasterBand::TILRasterBand( TILDataset *poTILDS, int nBand, + VRTSourcedRasterBand *poVRTBand ) + +{ + this->poDS = poTILDS; + this->poVRTBand = poVRTBand; + this->nBand = nBand; + this->eDataType = poVRTBand->GetRasterDataType(); + + poVRTBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr TILRasterBand::IReadBlock( int iBlockX, int iBlockY, void *pBuffer ) + +{ + return poVRTBand->ReadBlock( iBlockX, iBlockY, pBuffer ); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr TILRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + if(GetOverviewCount() > 0) + { + return GDALPamRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + } + else //if not exist TIL overviews, try to use band source overviews + { + return poVRTBand->IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* TILDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* TILDataset() */ +/************************************************************************/ + +TILDataset::TILDataset() + +{ + poVRTDS = NULL; + papszMetadataFiles = NULL; +} + +/************************************************************************/ +/* ~TILDataset() */ +/************************************************************************/ + +TILDataset::~TILDataset() + +{ + CloseDependentDatasets(); + CSLDestroy(papszMetadataFiles); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int TILDataset::CloseDependentDatasets() +{ + int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets(); + + if( poVRTDS ) + { + bHasDroppedRef = TRUE; + delete poVRTDS; + poVRTDS = NULL; + } + + while( !apoTileDS.empty() ) + { + GDALClose( (GDALDatasetH) apoTileDS.back() ); + apoTileDS.pop_back(); + } + + return bHasDroppedRef; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int TILDataset::Identify( GDALOpenInfo *poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 200 + || !EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"TIL") ) + return FALSE; + + if( strstr((const char *) poOpenInfo->pabyHeader,"numTiles") == NULL ) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *TILDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The TIL driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + CPLString osDirname = CPLGetDirname(poOpenInfo->pszFilename); + +// get metadata reader + + GDALMDReaderManager mdreadermanager; + GDALMDReaderBase* mdreader = mdreadermanager.GetReader(poOpenInfo->pszFilename, + poOpenInfo->GetSiblingFiles(), MDR_DG); + + if(NULL == mdreader) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open .TIL dataset due to missing metadata file." ); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Try to find the corresponding .IMD file. */ +/* -------------------------------------------------------------------- */ + char **papszIMD = mdreader->GetMetadataDomain(MD_DOMAIN_IMD); + + if( papszIMD == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open .TIL dataset due to missing .IMD file." ); + return NULL; + } + + if( CSLFetchNameValue( papszIMD, "numRows" ) == NULL + || CSLFetchNameValue( papszIMD, "numColumns" ) == NULL + || CSLFetchNameValue( papszIMD, "bitsPerPixel" ) == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Missing a required field in the .IMD file." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to load and parse the .TIL file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFOpenL( poOpenInfo->pszFilename, "r" ); + + if( fp == NULL ) + { + return NULL; + } + + CPLKeywordParser oParser; + + if( !oParser.Ingest( fp ) ) + { + VSIFCloseL( fp ); + return NULL; + } + + VSIFCloseL( fp ); + + char **papszTIL = oParser.GetAllKeywords(); + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + TILDataset *poDS; + + poDS = new TILDataset(); + poDS->papszMetadataFiles = mdreader->GetMetadataFiles(); + mdreader->FillMetadata(&poDS->oMDMD); + poDS->nRasterXSize = atoi(CSLFetchNameValueDef(papszIMD,"numColumns","0")); + poDS->nRasterYSize = atoi(CSLFetchNameValueDef(papszIMD,"numRows","0")); + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* We need to open one of the images in order to establish */ +/* details like the band count and types. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poTemplateDS; + const char *pszFilename = CSLFetchNameValue( papszTIL, "TILE_1.filename" ); + if( pszFilename == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing TILE_1.filename in .TIL file." ); + delete poDS; + return NULL; + } + + // trim double quotes. + if( pszFilename[0] == '"' ) + pszFilename++; + if( pszFilename[strlen(pszFilename)-1] == '"' ) + ((char *) pszFilename)[strlen(pszFilename)-1] = '\0'; + + CPLString osFilename = CPLFormFilename(osDirname, pszFilename, NULL); + poTemplateDS = (GDALDataset *) GDALOpen( osFilename, GA_ReadOnly ); + if( poTemplateDS == NULL || poTemplateDS->GetRasterCount() == 0) + { + delete poDS; + if (poTemplateDS != NULL) + GDALClose( poTemplateDS ); + return NULL; + } + + GDALRasterBand *poTemplateBand = poTemplateDS->GetRasterBand(1); + GDALDataType eDT = poTemplateBand->GetRasterDataType(); + int nBandCount = poTemplateDS->GetRasterCount(); + + //we suppose the first tile have the same projection as others (usually so) + CPLString pszProjection(poTemplateDS->GetProjectionRef()); + if(!pszProjection.empty()) + poDS->SetProjection(pszProjection); + + //we suppose the first tile have the same GeoTransform as others (usually so) + double adfGeoTransform[6]; + if( poTemplateDS->GetGeoTransform( adfGeoTransform ) == CE_None ) + { + // According to https://www.digitalglobe.com/sites/default/files/ISD_External.pdf, ulx=originX and + // is "Easting of the center of the upper left pixel of the image." + adfGeoTransform[0] = CPLAtof(CSLFetchNameValueDef(papszIMD,"MAP_PROJECTED_PRODUCT.ULX","0")) - adfGeoTransform[1] / 2; + adfGeoTransform[3] = CPLAtof(CSLFetchNameValueDef(papszIMD,"MAP_PROJECTED_PRODUCT.ULY","0")) - adfGeoTransform[5] / 2; + poDS->SetGeoTransform(adfGeoTransform); + } + + poTemplateBand = NULL; + GDALClose( poTemplateDS ); + +/* -------------------------------------------------------------------- */ +/* Create and initialize the corresponding VRT dataset used to */ +/* manage the tiled data access. */ +/* -------------------------------------------------------------------- */ + int iBand; + + poDS->poVRTDS = new VRTDataset(poDS->nRasterXSize,poDS->nRasterYSize); + + for( iBand = 0; iBand < nBandCount; iBand++ ) + poDS->poVRTDS->AddBand( eDT, NULL ); + + /* Don't try to write a VRT file */ + poDS->poVRTDS->SetWritable(FALSE); + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( iBand = 1; iBand <= nBandCount; iBand++ ) + poDS->SetBand( iBand, + new TILRasterBand( poDS, iBand, + (VRTSourcedRasterBand *) poDS->poVRTDS->GetRasterBand(iBand))); + +/* -------------------------------------------------------------------- */ +/* Add tiles as sources for each band. */ +/* -------------------------------------------------------------------- */ + int nTileCount = atoi(CSLFetchNameValueDef(papszTIL,"numTiles","0")); + int iTile = 0; + + for( iTile = 1; iTile <= nTileCount; iTile++ ) + { + CPLString osKey; + + osKey.Printf( "TILE_%d.filename", iTile ); + pszFilename = CSLFetchNameValue( papszTIL, osKey ); + if( pszFilename == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing TILE_%d.filename in .TIL file.", iTile ); + delete poDS; + return NULL; + } + + // trim double quotes. + if( pszFilename[0] == '"' ) + pszFilename++; + if( pszFilename[strlen(pszFilename)-1] == '"' ) + ((char *) pszFilename)[strlen(pszFilename)-1] = '\0'; + osFilename = CPLFormFilename(osDirname, pszFilename, NULL); + + osKey.Printf( "TILE_%d.ULColOffset", iTile ); + int nULX = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0")); + + osKey.Printf( "TILE_%d.ULRowOffset", iTile ); + int nULY = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0")); + + osKey.Printf( "TILE_%d.LRColOffset", iTile ); + int nLRX = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0")); + + osKey.Printf( "TILE_%d.LRRowOffset", iTile ); + int nLRY = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0")); + + GDALDataset *poTileDS = + new GDALProxyPoolDataset( osFilename, + nLRX - nULX + 1, nLRY - nULY + 1 ); + if( poTileDS == NULL ) + continue; + + poDS->apoTileDS.push_back( poTileDS ); + + for( iBand = 1; iBand <= nBandCount; iBand++ ) + { + ((GDALProxyPoolDataset *) poTileDS)-> + AddSrcBandDescription( eDT, nLRX - nULX + 1, 1 ); + + GDALRasterBand *poSrcBand = poTileDS->GetRasterBand(iBand); + + VRTSourcedRasterBand *poVRTBand = + (VRTSourcedRasterBand *) poDS->poVRTDS->GetRasterBand(iBand); + + poVRTBand->AddSimpleSource( poSrcBand, + 0, 0, + nLRX - nULX + 1, nLRY - nULY + 1, + nULX, nULY, + nLRX - nULX + 1, nLRY - nULY + 1 ); + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **TILDataset::GetFileList() + +{ + unsigned int i; + char **papszFileList = GDALPamDataset::GetFileList(); + + for( i = 0; i < apoTileDS.size(); i++ ) + papszFileList = CSLAddString( papszFileList, + apoTileDS[i]->GetDescription() ); + + if(NULL != papszMetadataFiles) + { + for( int i = 0; papszMetadataFiles[i] != NULL; i++ ) + { + papszFileList = CSLAddString( papszFileList, papszMetadataFiles[i] ); + } + } + + return papszFileList; +} + +/************************************************************************/ +/* GDALRegister_TIL() */ +/************************************************************************/ + +void GDALRegister_TIL() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "TIL" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "TIL" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "EarthWatch .TIL" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_til.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = TILDataset::Open; + poDriver->pfnIdentify = TILDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + + diff --git a/bazaar/plugin/gdal/frmts/tsx/GNUmakefile b/bazaar/plugin/gdal/frmts/tsx/GNUmakefile new file mode 100644 index 000000000..a53ce4f67 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/tsx/GNUmakefile @@ -0,0 +1,10 @@ +include ../../GDALmake.opt + +OBJ = tsxdataset.o + + +default: $(OBJ:.o=.$(OBJ_EXT)) +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/tsx/makefile.vc b/bazaar/plugin/gdal/frmts/tsx/makefile.vc new file mode 100644 index 000000000..741106d6a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/tsx/makefile.vc @@ -0,0 +1,12 @@ +OBJ = tsxdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/tsx/tsxdataset.cpp b/bazaar/plugin/gdal/frmts/tsx/tsxdataset.cpp new file mode 100644 index 000000000..949cf027c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/tsx/tsxdataset.cpp @@ -0,0 +1,816 @@ +/****************************************************************************** + * $Id: tsxdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: TerraSAR-X XML Product Support + * Purpose: Support for TerraSAR-X XML Metadata files + * Author: Philippe Vachon + * Description: This driver adds support for reading metadata and georef data + * associated with TerraSAR-X products. + * + ****************************************************************************** + * Copyright (c) 2007, Philippe Vachon + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_minixml.h" +#include "ogr_spatialref.h" + +#define MAX_GCPS 5000 //this should be more than enough ground control points + +CPL_CVSID("$Id: tsxdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_TSX(void); +CPL_C_END + + +enum ePolarization { + HH=0, + HV, + VH, + VV +}; + +enum eProductType { + eSSC = 0, + eMGD, + eEEC, + eGEC, + eUnknown +}; + +/************************************************************************/ +/* Helper Functions */ +/************************************************************************/ + +/* GetFilePath: return a relative path to a file within an XML node. + * Returns Null on failure + */ +const char *GetFilePath(CPLXMLNode *psXMLNode, const char **pszNodeType) { + const char *pszDirectory, *pszFilename; + + pszDirectory = CPLGetXMLValue( psXMLNode, "file.location.path", "" ); + pszFilename = CPLGetXMLValue( psXMLNode, "file.location.filename", "" ); + *pszNodeType = CPLGetXMLValue (psXMLNode, "type", " " ); + + if (pszDirectory == NULL || pszFilename == NULL) { + return NULL; + } + + return CPLFormFilename( pszDirectory, pszFilename, "" ); +} + +/************************************************************************/ +/* ==================================================================== */ +/* TSXDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class TSXDataset : public GDALPamDataset { + int nGCPCount; + GDAL_GCP *pasGCPList; + + char *pszGCPProjection; + + char *pszProjection; + double adfGeoTransform[6]; + bool bHaveGeoTransform; + + eProductType nProduct; +public: + TSXDataset(); + ~TSXDataset(); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + CPLErr GetGeoTransform( double* padfTransform); + const char* GetProjectionRef(); + + static GDALDataset *Open( GDALOpenInfo *poOpenInfo ); + static int Identify( GDALOpenInfo *poOpenInfo ); +private: + bool getGCPsFromGEOREF_XML(char *pszGeorefFilename); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* TSXRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class TSXRasterBand : public GDALPamRasterBand { + GDALDataset *poBand; + ePolarization ePol; +public: + TSXRasterBand( TSXDataset *poDSIn, GDALDataType eDataType, + ePolarization ePol, GDALDataset *poBand ); + virtual ~TSXRasterBand(); + + virtual CPLErr IReadBlock( int nBlockXOff, int nBlockYOff, void *pImage ); + + static GDALDataset *Open( GDALOpenInfo *poOpenInfo ); +}; + +/************************************************************************/ +/* TSXRasterBand */ +/************************************************************************/ + +TSXRasterBand::TSXRasterBand( TSXDataset *poDS, GDALDataType eDataType, + ePolarization ePol, GDALDataset *poBand ) +{ + this->poDS = poDS; + this->eDataType = eDataType; + this->ePol = ePol; + + switch (ePol) { + case HH: + SetMetadataItem( "POLARIMETRIC_INTERP", "HH" ); + break; + case HV: + SetMetadataItem( "POLARIMETRIC_INTERP", "HV" ); + break; + case VH: + SetMetadataItem( "POLARIMETRIC_INTERP", "VH" ); + break; + case VV: + SetMetadataItem( "POLARIMETRIC_INTERP", "VV" ); + break; + } + + + /* now setup the actual raster reader */ + this->poBand = poBand; + + GDALRasterBand *poSrcBand = poBand->GetRasterBand( 1 ); + poSrcBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); +} + +/************************************************************************/ +/* TSXRasterBand() */ +/************************************************************************/ + +TSXRasterBand::~TSXRasterBand() { + if( poBand != NULL ) + GDALClose( (GDALRasterBandH) poBand ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr TSXRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + int nRequestYSize; + + /* Check if the last strip is partial so we can avoid over-requesting */ + if ( (nBlockYOff + 1) * nBlockYSize > nRasterYSize ) { + nRequestYSize = nRasterYSize - nBlockYOff * nBlockYSize; + memset( pImage, 0, (GDALGetDataTypeSize( eDataType ) / 8) * + nBlockXSize * nBlockYSize); + } + else { + nRequestYSize = nBlockYSize; + } + + /* Read Complex Data */ + if ( eDataType == GDT_CInt16 ) { + return poBand->RasterIO( GF_Read, nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, nBlockXSize, nRequestYSize, + pImage, nBlockXSize, nRequestYSize, GDT_CInt16, 1, NULL, 4, + nBlockXSize * 4, 0, NULL ); + } + else { /* Detected Product */ + return poBand->RasterIO( GF_Read, nBlockXOff * nBlockXSize, + nBlockYOff * nBlockYSize, nBlockXSize, nRequestYSize, + pImage, nBlockXSize, nRequestYSize, GDT_UInt16, 1, NULL, 2, + nBlockXSize * 2, 0, NULL ); + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* TSXDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* TSXDataset() */ +/************************************************************************/ + +TSXDataset::TSXDataset() { + nGCPCount = 0; + pasGCPList = NULL; + pszGCPProjection = CPLStrdup(""); + pszProjection = CPLStrdup(""); + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + bHaveGeoTransform = FALSE; +} + +/************************************************************************/ +/* ~TSXDataset() */ +/************************************************************************/ + +TSXDataset::~TSXDataset() { + FlushCache(); + + CPLFree( pszProjection ); + + CPLFree( pszGCPProjection ); + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int TSXDataset::Identify( GDALOpenInfo *poOpenInfo ) +{ + if (poOpenInfo->fpL == NULL || poOpenInfo->nHeaderBytes < 260) + { + if( poOpenInfo->bIsDirectory ) + { + CPLString osFilename = + CPLFormCIFilename( poOpenInfo->pszFilename, CPLGetFilename( poOpenInfo->pszFilename ), "xml" ); + + /* Check if the filename contains TSX1_SAR (TerraSAR-X) or TDX1_SAR (TanDEM-X) */ + if (!(EQUALN(CPLGetBasename( osFilename ), "TSX1_SAR", 8) || + EQUALN(CPLGetBasename( osFilename ), "TDX1_SAR", 8))) + return 0; + + VSIStatBufL sStat; + if( VSIStatL( osFilename, &sStat ) == 0 ) + return 1; + } + + return 0; + } + + /* Check if the filename contains TSX1_SAR (TerraSAR-X) or TDX1_SAR (TanDEM-X) */ + if (!(EQUALN(CPLGetBasename( poOpenInfo->pszFilename ), "TSX1_SAR", 8) || + EQUALN(CPLGetBasename( poOpenInfo->pszFilename ), "TDX1_SAR", 8))) + return 0; + + /* finally look for the pabyHeader, "psChild; psNode != NULL; psNode = psNode->psNext ) + if( EQUAL(psNode->pszValue,"gridPoint") ) + nGCPCount++ ; + } + //if there are no gcps, fail + if(nGCPCount<=0) + { + CPLDestroyXMLNode( psGeorefData ); + return false; + } + + //put some reasonable limits of the number of gcps + if (nGCPCount>MAX_GCPS ) + nGCPCount=MAX_GCPS; + //allocate memory for the gcps + pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP),nGCPCount); + //loop through all gcps and set info + int gcps_allocated = nGCPCount; //save the number allocated to ensure it does not run off the end of the array + nGCPCount=0; //reset to zero and count + //do a check on the grid point to make sure it has lat,long row, and column + //it seems that only SSC products contain row, col - how to map lat long otherwise?? + //for now fail if row and col are not present - just check the first and assume the rest are the same + for( psNode = psGeolocationGrid->psChild; psNode != NULL; psNode = psNode->psNext ) + { + if( !EQUAL(psNode->pszValue,"gridPoint") ) + continue; + + if ( !strcmp(CPLGetXMLValue(psNode,"col","error"), "error") || + !strcmp(CPLGetXMLValue(psNode,"row","error"), "error") || + !strcmp(CPLGetXMLValue(psNode,"lon","error"), "error") || + !strcmp(CPLGetXMLValue(psNode,"lat","error"), "error")) + { + CPLDestroyXMLNode( psGeorefData ); + return false; + } + } + for( psNode = psGeolocationGrid->psChild; psNode != NULL; psNode = psNode->psNext ) + { + //break out if the end of the array has been reached + if (nGCPCount >= gcps_allocated) + { + CPLError(CE_Warning, CPLE_AppDefined, "GDAL TSX driver: Truncating the number of GCPs."); + break; + } + + char szID[32]; + GDAL_GCP *psGCP = pasGCPList + nGCPCount; + + if( !EQUAL(psNode->pszValue,"gridPoint") ) + continue; + + nGCPCount++ ; + + sprintf( szID, "%d", nGCPCount ); + psGCP->pszId = CPLStrdup( szID ); + psGCP->pszInfo = CPLStrdup(""); + psGCP->dfGCPPixel = + CPLAtof(CPLGetXMLValue(psNode,"col","0")); + psGCP->dfGCPLine = + CPLAtof(CPLGetXMLValue(psNode,"row","0")); + psGCP->dfGCPX = + CPLAtof(CPLGetXMLValue(psNode,"lon","")); + psGCP->dfGCPY = + CPLAtof(CPLGetXMLValue(psNode,"lat","")); + //looks like height is in meters - should it be converted so xyz are all on the same scale?? + psGCP->dfGCPZ = 0; + //CPLAtof(CPLGetXMLValue(psNode,"height","")); + } + + CPLFree(pszGCPProjection); + osr.exportToWkt( &(pszGCPProjection) ); + + CPLDestroyXMLNode( psGeorefData ); + + return true; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *TSXDataset::Open( GDALOpenInfo *poOpenInfo ) { +/* -------------------------------------------------------------------- */ +/* Is this a TerraSAR-X product file? */ +/* -------------------------------------------------------------------- */ + if (!TSXDataset::Identify( poOpenInfo )) + { + return NULL; /* nope */ + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The TSX driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + CPLString osFilename; + + if( poOpenInfo->bIsDirectory ) + { + osFilename = + CPLFormCIFilename( poOpenInfo->pszFilename, CPLGetFilename( poOpenInfo->pszFilename ), "xml" ); + } + else + osFilename = poOpenInfo->pszFilename; + + /* Ingest the XML */ + CPLXMLNode *psData, *psComponents, *psProductInfo; + psData = CPLParseXMLFile( osFilename ); + if (psData == NULL) + return NULL; + + /* find the product components */ + psComponents = CPLGetXMLNode( psData, "=level1Product.productComponents" ); + if (psComponents == NULL) { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to find tag in file.\n" ); + CPLDestroyXMLNode(psData); + return NULL; + } + + /* find the product info tag */ + psProductInfo = CPLGetXMLNode( psData, "=level1Product.productInfo" ); + if (psProductInfo == NULL) { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to find tag in file.\n" ); + CPLDestroyXMLNode(psData); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + + TSXDataset *poDS = new TSXDataset(); + +/* -------------------------------------------------------------------- */ +/* Read in product info. */ +/* -------------------------------------------------------------------- */ + + poDS->SetMetadataItem( "SCENE_CENTRE_TIME", CPLGetXMLValue( psProductInfo, + "sceneInfo.sceneCenterCoord.azimuthTimeUTC", "unknown" ) ); + poDS->SetMetadataItem( "OPERATIONAL_MODE", CPLGetXMLValue( psProductInfo, + "generationInfo.groundOperationsType", "unknown" ) ); + poDS->SetMetadataItem( "ORBIT_CYCLE", CPLGetXMLValue( psProductInfo, + "missionInfo.orbitCycle", "unknown" ) ); + poDS->SetMetadataItem( "ABSOLUTE_ORBIT", CPLGetXMLValue( psProductInfo, + "missionInfo.absOrbit", "unknown" ) ); + poDS->SetMetadataItem( "ORBIT_DIRECTION", CPLGetXMLValue( psProductInfo, + "missionInfo.orbitDirection", "unknown" ) ); + poDS->SetMetadataItem( "IMAGING_MODE", CPLGetXMLValue( psProductInfo, + "acquisitionInfo.imagingMode", "unknown" ) ); + poDS->SetMetadataItem( "PRODUCT_VARIANT", CPLGetXMLValue( psProductInfo, + "productVariantInfo.productVariant", "unknown" ) ); + char *pszDataType = CPLStrdup( CPLGetXMLValue( psProductInfo, + "imageDataInfo.imageDataType", "unknown" ) ); + poDS->SetMetadataItem( "IMAGE_TYPE", pszDataType ); + + /* Get raster information */ + int nRows = atoi( CPLGetXMLValue( psProductInfo, + "imageDataInfo.imageRaster.numberOfRows", "" ) ); + int nCols = atoi( CPLGetXMLValue( psProductInfo, + "imageDataInfo.imageRaster.numberOfColumns", "" ) ); + + poDS->nRasterXSize = nCols; + poDS->nRasterYSize = nRows; + + poDS->SetMetadataItem( "ROW_SPACING", CPLGetXMLValue( psProductInfo, + "imageDataInfo.imageRaster.rowSpacing", "unknown" ) ); + poDS->SetMetadataItem( "COL_SPACING", CPLGetXMLValue( psProductInfo, + "imageDataInfo.imageRaster.columnSpacing", "unknown" ) ); + poDS->SetMetadataItem( "COL_SPACING_UNITS", CPLGetXMLValue( psProductInfo, + "imageDataInfo.imageRaster.columnSpacing.units", "unknown" ) ); + + /* Get equivalent number of looks */ + poDS->SetMetadataItem( "AZIMUTH_LOOKS", CPLGetXMLValue( psProductInfo, + "imageDataInfo.imageRaster.azimuthLooks", "unknown" ) ); + poDS->SetMetadataItem( "RANGE_LOOKS", CPLGetXMLValue( psProductInfo, + "imageDataInfo.imageRaster.rangeLooks", "unknown" ) ); + + const char *pszProductVariant; + pszProductVariant = CPLGetXMLValue( psProductInfo, + "productVariantInfo.productVariant", "unknown" ); + + poDS->SetMetadataItem( "PRODUCT_VARIANT", pszProductVariant ); + + /* Determine what product variant this is */ + if (EQUALN(pszProductVariant,"SSC",3)) + poDS->nProduct = eSSC; + else if (EQUALN(pszProductVariant,"MGD",3)) + poDS->nProduct = eMGD; + else if (EQUALN(pszProductVariant,"EEC",3)) + poDS->nProduct = eEEC; + else if (EQUALN(pszProductVariant,"GEC",3)) + poDS->nProduct = eGEC; + else + poDS->nProduct = eUnknown; + + /* Start reading in the product components */ + const char *pszPath; + char *pszGeorefFile = NULL; + CPLXMLNode *psComponent; + CPLErr geoTransformErr=CE_Failure; + for (psComponent = psComponents->psChild; psComponent != NULL; + psComponent = psComponent->psNext) + { + const char *pszType = NULL; + pszPath = CPLFormFilename( + CPLGetDirname( osFilename ), + GetFilePath(psComponent, &pszType), + "" ); + const char *pszPolLayer = CPLGetXMLValue(psComponent, "polLayer", " "); + + if ( !EQUALN(pszType," ",1) ) { + if (EQUALN(pszType, "MAPPING_GRID", 12) ) { + /* the mapping grid... save as a metadata item this path */ + poDS->SetMetadataItem( "MAPPING_GRID", pszPath ); + } + else if (EQUALN(pszType, "GEOREF", 6)) { + /* save the path to the georef data for later use */ + pszGeorefFile = CPLStrdup( pszPath ); + } + } + else if( !EQUALN(pszPolLayer, " ", 1) && + EQUALN(psComponent->pszValue, "imageData", 9) ) { + /* determine the polarization of this band */ + ePolarization ePol; + if ( EQUALN(pszPolLayer, "HH", 2) ) { + ePol = HH; + } + else if ( EQUALN(pszPolLayer, "HV" , 2) ) { + ePol = HV; + } + else if ( EQUALN(pszPolLayer, "VH", 2) ) { + ePol = VH; + } + else { + ePol = VV; + } + + GDALDataType eDataType = EQUALN(pszDataType, "COMPLEX", 7) ? + GDT_CInt16 : GDT_UInt16; + + /* try opening the file that represents that band */ + TSXRasterBand *poBand; + GDALDataset *poBandData; + + poBandData = (GDALDataset *) GDALOpen( pszPath, GA_ReadOnly ); + if ( poBandData != NULL ) { + poBand = new TSXRasterBand( poDS, eDataType, ePol, + poBandData ); + poDS->SetBand( poDS->GetRasterCount() + 1, poBand ); + + //copy georeferencing info from the band + //need error checking?? + //it will just save the info from the last band + CPLFree( poDS->pszProjection ); + poDS->pszProjection = CPLStrdup(poBandData->GetProjectionRef()); + geoTransformErr = poBandData->GetGeoTransform(poDS->adfGeoTransform); + } + } + } + + //now check if there is a geotransform + if ( strcmp(poDS->pszProjection, "") && geoTransformErr==CE_None) + { + poDS->bHaveGeoTransform = TRUE; + } + else + { + poDS->bHaveGeoTransform = FALSE; + CPLFree( poDS->pszProjection ); + poDS->pszProjection = CPLStrdup(""); + poDS->adfGeoTransform[0] = 0.0; + poDS->adfGeoTransform[1] = 1.0; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = 1.0; + } + + CPLFree(pszDataType); + + +/* -------------------------------------------------------------------- */ +/* Check and set matrix representation. */ +/* -------------------------------------------------------------------- */ + + if (poDS->GetRasterCount() == 4) { + poDS->SetMetadataItem( "MATRIX_REPRESENTATION", "SCATTERING" ); + } + +/* -------------------------------------------------------------------- */ +/* Read the four corners and centre GCPs in */ +/* -------------------------------------------------------------------- */ + + CPLXMLNode *psSceneInfo = CPLGetXMLNode( psData, + "=level1Product.productInfo.sceneInfo" ); + if (psSceneInfo != NULL) + { + /* extract the GCPs from the provided file */ + bool success = false; + if (pszGeorefFile != NULL) + success = poDS->getGCPsFromGEOREF_XML(pszGeorefFile); + + //if the gcp's cannot be extracted from the georef file, try to get the corner coordinates + //for now just SSC because the others don't have refColumn and refRow + if (!success && poDS->nProduct == eSSC) + { + CPLXMLNode *psNode; + int nGCP = 0; + double dfAvgHeight = CPLAtof(CPLGetXMLValue(psSceneInfo, + "sceneAverageHeight", "0.0")); + + //count and allocate gcps - there should be five - 4 corners and a centre + poDS->nGCPCount = 0; + for (psNode = psSceneInfo->psChild; psNode != NULL; psNode = psNode->psNext ) + { + if (!EQUAL(psNode->pszValue, "sceneCenterCoord") && + !EQUAL(psNode->pszValue, "sceneCornerCoord")) + continue; + + poDS->nGCPCount++; + } + if (poDS->nGCPCount > 0) + { + poDS->pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP), poDS->nGCPCount); + + /* iterate over GCPs */ + for (psNode = psSceneInfo->psChild; psNode != NULL; psNode = psNode->psNext ) + { + GDAL_GCP *psGCP = poDS->pasGCPList + nGCP; + + if (!EQUAL(psNode->pszValue, "sceneCenterCoord") && + !EQUAL(psNode->pszValue, "sceneCornerCoord")) + continue; + + psGCP->dfGCPPixel = CPLAtof(CPLGetXMLValue(psNode, "refColumn", + "0.0")); + psGCP->dfGCPLine = CPLAtof(CPLGetXMLValue(psNode, "refRow", "0.0")); + psGCP->dfGCPX = CPLAtof(CPLGetXMLValue(psNode, "lon", "0.0")); + psGCP->dfGCPY = CPLAtof(CPLGetXMLValue(psNode, "lat", "0.0")); + psGCP->dfGCPZ = dfAvgHeight; + psGCP->pszId = CPLStrdup( CPLSPrintf( "%d", nGCP ) ); + psGCP->pszInfo = CPLStrdup(""); + + nGCP++; + } + + //set the projection string - the fields are lat/long - seems to be WGS84 datum + OGRSpatialReference osr; + osr.SetWellKnownGeogCS( "WGS84" ); + CPLFree(poDS->pszGCPProjection); + osr.exportToWkt( &(poDS->pszGCPProjection) ); + } + } + + //gcps override geotransform - does it make sense to have both?? + if (poDS->nGCPCount>0) + { + poDS->bHaveGeoTransform = FALSE; + CPLFree( poDS->pszProjection ); + poDS->pszProjection = CPLStrdup(""); + poDS->adfGeoTransform[0] = 0.0; + poDS->adfGeoTransform[1] = 1.0; + poDS->adfGeoTransform[2] = 0.0; + poDS->adfGeoTransform[3] = 0.0; + poDS->adfGeoTransform[4] = 0.0; + poDS->adfGeoTransform[5] = 1.0; + } + + } + else { + CPLError(CE_Warning, CPLE_AppDefined, + "Unable to find sceneInfo tag in XML document. " + "Proceeding with caution."); + } + + CPLFree(pszGeorefFile); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Check for overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + CPLDestroyXMLNode(psData); + + return poDS; +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int TSXDataset::GetGCPCount() { + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *TSXDataset::GetGCPProjection() { + return pszGCPProjection; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *TSXDataset::GetGCPs() { + return pasGCPList; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ +const char *TSXDataset::GetProjectionRef() +{ + return pszProjection; +} + +/************************************************************************/ +/* GetGeotransform() */ +/************************************************************************/ +CPLErr TSXDataset::GetGeoTransform(double* padfTransform) +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + + if (bHaveGeoTransform) + return( CE_None ); + + return( CE_Failure ); +} + +/************************************************************************/ +/* GDALRegister_TSX() */ +/************************************************************************/ + +void GDALRegister_TSX() { + GDALDriver *poDriver; + + if( GDALGetDriverByName( "TSX" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "TSX" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "TerraSAR-X Product" ); +/* poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_tsx.html" ); */ + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = TSXDataset::Open; + poDriver->pfnIdentify = TSXDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/frmts/usgsdem/CDED.notes b/bazaar/plugin/gdal/frmts/usgsdem/CDED.notes new file mode 100644 index 000000000..a332550ca --- /dev/null +++ b/bazaar/plugin/gdal/frmts/usgsdem/CDED.notes @@ -0,0 +1,106 @@ + + +Field Map +========= + + +Offset Length Name Notes +------ ------ ---- ----- + + 0 40 Filename Generated from output filename. + + 40 60 Producer of Data Template or blank. + + 100 9 Filler Blank + + 109 26 SW Geographic Corner Generated. + + 135 1 Process Code Template or blank. + + 136 1 Filler Blank + + 137 3 Sectional Indicator Template or blank. + + 140 4 Origin Code Template or blank. + + 144 6 DEM Level Code Template or blank. + "1" for PRODUCT=CDED50K + + 150 6 Elevation Pattern Hardcoded to "1" (regular) + + 156 6 Horizontal Reference Hardcoded to "0" (Geographic) + System + + 162 6 UTM/SP Zone Harcoded to "0". + + 168 360 Projection Parameters Each hardcoded to 0.0. + + 528 6 Horz. Unit of Measure Hardcoded to "3" (arc seconds) + + 534 6 Vert. Unit of Measure Hardcoded to "2" (meters) + + 540 6 Cov. Polygon Sides Hardcoded to "4". + + 546 192 Corner Coordinates Generated + + 738 24 Minimum Elevation Generated + + 762 24 Maximum Elevation Generated + + 786 24 Rotation Angle Hardcoded to "0". + + 810 6 Accurancy Code Hardcoded to "0" + + 816 36 Spatial Resolution X/Y generated, Z hardcoded to 1.0. + + 852 6 Rows of Profiles Hardcoded to "1". + + 858 6 Columns of Profiles Generated (height of dem in pixels) + + 864 12 Largest/Smallest Primary Blank + Contour Intervals and Units + + 876 4 Data source data Template or blank. + + 880 4 Data inspection or Template or blank. + revision date + + 884 1 Inspect. Revision Flag Template or blank. + + 885 1 Data Validation Flag Template or blank. + + 886 2 Suspect/Void Flag Generated (0=none,2=void area) + + 888 2 Vertical Datum Template or "1" (MSL). + + 890 2 Horizontal Datum Template or "4" (NAD83). + + 892 4 Data edition/version Template or blank. + Spec edition/version "1020" for PRODUCT=CDED50K + + 896 4 Percent Void Generated. + + 900 8 Edge Matching Flags Template or blank. + + 908 7 Vertical Datum Shift Hardcoded to "0.0". + + + +CDED Production Issues +====================== + +The following fields should be addressed in a template file. + +Offset Length Name Notes +------ ------ ---- ----- + + 40 60 Producer of Data + + 135 1 Process Code The CDED spec lists options as + "8" = ANUDEM + "9" = FME for LINUX, Build 842 + "A" = TopoGrid + + 140 4 Origin Code Should be "YT" in Yukon I believe. + + diff --git a/bazaar/plugin/gdal/frmts/usgsdem/GNUmakefile b/bazaar/plugin/gdal/frmts/usgsdem/GNUmakefile new file mode 100644 index 000000000..23753bc19 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/usgsdem/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = usgsdemdataset.o usgsdem_create.o + +CPPFLAGS := -I../../alg $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/usgsdem/frmt_usgsdem.html b/bazaar/plugin/gdal/frmts/usgsdem/frmt_usgsdem.html new file mode 100644 index 000000000..e6b254d28 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/usgsdem/frmt_usgsdem.html @@ -0,0 +1,120 @@ + + +USGSDEM -- USGS ASCII DEM (and CDED) + + + + +

    USGSDEM -- USGS ASCII DEM (and CDED)

    + +GDAL includes support for reading USGS ASCII DEM files. This is the +traditional format used by USGS before being replaced by SDTS, and is the +format used for CDED DEM data products from the Canada. Most popular +variations on USGS DEM files should be supported, including correct +recognition of coordinate system, and georeferenced positioning.

    + +The 7.5 minute (UTM grid) USGS DEM files will generally have regions of +missing data around the edges, and these are properly marked with +a nodata value. Elevation values in USGS DEM files may be in meters or +feet, and this will be indicated by the return value of +GDALRasterBand::GetUnitType() (either "m" or "ft").

    + +Note that USGS DEM files are represented as one big tile. This may cause +cache thrashing problems if the GDAL tile cache size is small. It will also +result in a substantial delay when the first pixel is read as the whole file +will be ingested.

    + +Some of the code for implementing usgsdemdataset.cpp was derived from +VTP code by Ben Discoe. See the Virtual +Terrain project for more information on VTP.

    + +

    Creation Issues

    + +GDAL supports export of geographic (and UTM) USGS DEM and CDED data +files, including the ability to generate CDED 2.0 50K products to Canadian +federal government specifications.

    + +Input data must already be sampled in a geographic or UTM coordinate +system. By default the entire area of the input file will be output, +but for CDED50K products the output file will be sampled at the +production specified resolution and on product tile boundaries.

    + +If the input file has appropriate coordinate system information set, +export to specific product formats can take input in different coordinate +systems (ie. from Albers projection to NAD83 geographic for CDED50K +production).

    + +Creation Options:

    + +

      + +
    • PRODUCT=DEFAULT/CDED50K: When CDED50K is specified, the output file will be forced to +adhere to CDED 50K product specifications. The output will always be 1201x1201 +and generally a 15 minute by 15 minute tile (though wider in longitude in +far north areas).

      + +

    • TOPLEFT=long,lat: For CDED50K products, this is used to +specify the top left corner of the tile to be generated. It should be +on a 15 minute boundary and can be given in decimal degrees or degrees and +minutes (eg. TOPLEFT=117d15w,52d30n).

      + +

    • RESAMPLE=Nearest/Bilinear/Cubic/CubicSpline: Set the resampling +kernel used for resampling the data to the target grid. Only has an effect +when particular products like CDED50K are being produced. Defaults to +Bilinear.

      + +

    • DEMLevelCode=integer DEM Level (1, 2 or 3 if set). Defaults to 1.

      + +

    • DataSpecVersion=integer :Data and Specification version/revision (eg. 1020)

      + +

    • PRODUCER=text: Up to 60 characters to be put into the producer +field of the generated file .

      + +

    • OriginCode=text: Up to 4 characters to be put into the origin +code field of the generated file (YT for Yukon).

      + +

    • ProcessCode=code: One character to be put into the process +code field of the generated file (8=ANUDEM, 9=FME, A=TopoGrid).

      + +

    • TEMPLATE=filename: For any output file, a template file can be +specified. A number of fields (including the Data Producer) will be copied +from the template file if provided, and are otherwise left blank.

      + +

    • ZRESOLUTION=float: DEM's store elevation information as +positive integers, and these integers are scaled using the "z resolution." +By default, this resolution is written as 1.0. However, you may specify a +different resolution here, if you would like your integers to be scaled +into floating point numbers.

      + +

    • NTS=name: NTS Mapsheet name, used to derive TOPLEFT. Only has an effect +when particular products like CDED50K are being produced.

      + +

    • INTERNALNAME=name: Dataset name written into file header. Only has an effect +when particular products like CDED50K are being produced.

      + +

    + +Example: + +The following would generate a single CDED50K tile, extracting from the +larger DEM coverage yk_3arcsec for a tile with the top left corner -117w,60n. +The file yk_template.dem is used to set some product fields including the +Producer of Data, Process Code and Origin Code fields. + +
    +gdal_translate -of USGSDEM -co PRODUCT=CDED50K -co TEMPLATE=yk_template.dem \
    +               -co TOPLEFT=-117w,60n yk_3arcsec 031a01_e.dem
    +
    + +
    + +NOTE: Implemented as gdal/frmts/usgsdem/usgsdemdataset.cpp.

    + +The USGS DEM reading code in GDAL was derived from the importer in the +VTP software. The export capability +was developed with the financial support of the Yukon Department of +Environment.

    + + + + diff --git a/bazaar/plugin/gdal/frmts/usgsdem/makefile.vc b/bazaar/plugin/gdal/frmts/usgsdem/makefile.vc new file mode 100644 index 000000000..e1c99850d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/usgsdem/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = usgsdemdataset.obj usgsdem_create.obj + +EXTRAFLAGS = -I..\..\alg + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/usgsdem/usgsdem_create.cpp b/bazaar/plugin/gdal/frmts/usgsdem/usgsdem_create.cpp new file mode 100644 index 000000000..3f6edae71 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/usgsdem/usgsdem_create.cpp @@ -0,0 +1,1575 @@ +/****************************************************************************** + * $Id: usgsdem_create.cpp 28785 2015-03-26 20:46:45Z goatbar $ + * + * Project: USGS DEM Driver + * Purpose: CreateCopy() implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + * This writing code based on the format specification: + * Canadian Digital Elevation Data Product Specification - Edition 2.0 + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2007-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "ogr_spatialref.h" +#include "cpl_string.h" +#include "gdalwarper.h" +#include "cpl_csv.h" + +CPL_CVSID("$Id: usgsdem_create.cpp 28785 2015-03-26 20:46:45Z goatbar $"); + +typedef struct +{ + GDALDataset *poSrcDS; + char *pszFilename; + int nXSize, nYSize; + + char *pszDstSRS; + + double dfLLX, dfLLY; // These are adjusted in to center of + double dfULX, dfULY; // corner pixels, and in decimal degrees. + double dfURX, dfURY; + double dfLRX, dfLRY; + + int utmzone; + char horizdatum[2]; + + double dfHorizStepSize; + double dfVertStepSize; + double dfElevStepSize; + + char **papszOptions; + int bStrict; + + VSILFILE *fp; + + GInt16 *panData; + +} USGSDEMWriteInfo; + +#define DEM_NODATA -32767 + +/************************************************************************/ +/* USGSDEMWriteCleanup() */ +/************************************************************************/ + +static void USGSDEMWriteCleanup( USGSDEMWriteInfo *psWInfo ) + +{ + CSLDestroy( psWInfo->papszOptions ); + CPLFree( psWInfo->pszDstSRS ); + CPLFree( psWInfo->pszFilename ); + if( psWInfo->fp != NULL ) + VSIFCloseL( psWInfo->fp ); + if( psWInfo->panData != NULL ) + VSIFree( psWInfo->panData ); +} + +/************************************************************************/ +/* USGSDEMDectoPackedDMS() */ +/************************************************************************/ +const char *USGSDEMDecToPackedDMS( double dfDec ) +{ + double dfSeconds; + int nDegrees, nMinutes, nSign; + static char szPackBuf[100]; + + nSign = ( dfDec < 0.0 )? -1 : 1; + + dfDec = ABS( dfDec ); + /* If the difference between the value and the nearest degree + is less than 1e-5 second, then we force to round to the + nearest degree, to avoid result strings like '40 59 60.0000' instead of '41'. + This is of general interest, but was mainly done to workaround a strange + Valgrind bug when running usgsdem_6 where the value of psDInfo->dfULCornerY + computed in DTEDOpen() differ between Valgrind and non-Valgrind executions. + */ + if (fabs(dfDec - (int) floor( dfDec + .5)) < 1e-5 / 3600) + dfDec = nDegrees = (int) floor( dfDec + .5); + else + nDegrees = (int) floor( dfDec ); + nMinutes = (int) floor( ( dfDec - nDegrees ) * 60.0 ); + dfSeconds = (dfDec - nDegrees) * 3600.0 - nMinutes * 60.0; + + CPLsprintf( szPackBuf, "%4d%2d%7.4f", + nSign * nDegrees, nMinutes, dfSeconds ); + return szPackBuf; +} + +/************************************************************************/ +/* TextFill() */ +/************************************************************************/ + +static void TextFill( char *pszTarget, unsigned int nMaxChars, + const char *pszSrc ) + +{ + if( strlen(pszSrc) < nMaxChars ) + { + memcpy( pszTarget, pszSrc, strlen(pszSrc) ); + memset( pszTarget + strlen(pszSrc), ' ', nMaxChars - strlen(pszSrc)); + } + else + { + memcpy( pszTarget, pszSrc, nMaxChars ); + } +} + +/************************************************************************/ +/* TextFillR() */ +/* */ +/* Right justified. */ +/************************************************************************/ + +static void TextFillR( char *pszTarget, unsigned int nMaxChars, + const char *pszSrc ) + +{ + if( strlen(pszSrc) < nMaxChars ) + { + memset( pszTarget, ' ', nMaxChars - strlen(pszSrc) ); + memcpy( pszTarget + nMaxChars - strlen(pszSrc), pszSrc, + strlen(pszSrc) ); + } + else + memcpy( pszTarget, pszSrc, nMaxChars ); +} + +/************************************************************************/ +/* USGSDEMPrintDouble() */ +/* */ +/* The MSVC C runtime library uses 3 digits */ +/* for the exponent. This causes various problems, so we try */ +/* to correct it here. */ +/************************************************************************/ + +#if defined(_MSC_VER) || defined(__MSVCRT__) +# define MSVC_HACK +#endif + +static void USGSDEMPrintDouble( char *pszBuffer, double dfValue ) + +{ +#define DOUBLE_BUFFER_SIZE 64 + + char szTemp[DOUBLE_BUFFER_SIZE]; + int i; +#ifdef MSVC_HACK + const char *pszFormat = "%25.15e"; +#else + const char *pszFormat = "%24.15e"; +#endif + + if ( !pszBuffer ) + return; + +#if defined(HAVE_SNPRINTF) + CPLsnprintf( szTemp, DOUBLE_BUFFER_SIZE, pszFormat, dfValue ); +#else + CPLsprintf( szTemp, pszFormat, dfValue ); +#endif + szTemp[DOUBLE_BUFFER_SIZE - 1] = '\0'; + + for( i = 0; szTemp[i] != '\0'; i++ ) + { + if( szTemp[i] == 'E' || szTemp[i] == 'e' ) + szTemp[i] = 'D'; +#ifdef MSVC_HACK + if( (szTemp[i] == '+' || szTemp[i] == '-') + && szTemp[i+1] == '0' && isdigit(szTemp[i+2]) + && isdigit(szTemp[i+3]) && szTemp[i+4] == '\0' ) + { + memmove( szTemp+i+1, szTemp+i+2, 2 ); + szTemp[i+3] = '\0'; + break; + } +#endif + } + + TextFillR( pszBuffer, 24, szTemp ); +} + +/************************************************************************/ +/* USGSDEMPrintSingle() */ +/* */ +/* The MSVC C runtime library uses 3 digits */ +/* for the exponent. This causes various problems, so we try */ +/* to correct it here. */ +/************************************************************************/ + +static void USGSDEMPrintSingle( char *pszBuffer, double dfValue ) + +{ +#define DOUBLE_BUFFER_SIZE 64 + + char szTemp[DOUBLE_BUFFER_SIZE]; + int i; +#ifdef MSVC_HACK + const char *pszFormat = "%13.6e"; +#else + const char *pszFormat = "%12.6e"; +#endif + + if ( !pszBuffer ) + return; + +#if defined(HAVE_SNPRINTF) + CPLsnprintf( szTemp, DOUBLE_BUFFER_SIZE, pszFormat, dfValue ); +#else + CPLsprintf( szTemp, pszFormat, dfValue ); +#endif + szTemp[DOUBLE_BUFFER_SIZE - 1] = '\0'; + + for( i = 0; szTemp[i] != '\0'; i++ ) + { + if( szTemp[i] == 'E' || szTemp[i] == 'e' ) + szTemp[i] = 'D'; +#ifdef MSVC_HACK + if( (szTemp[i] == '+' || szTemp[i] == '-') + && szTemp[i+1] == '0' && isdigit(szTemp[i+2]) + && isdigit(szTemp[i+3]) && szTemp[i+4] == '\0' ) + { + memmove( szTemp+i+1, szTemp+i+2, 2 ); + szTemp[i+3] = '\0'; + break; + } +#endif + } + + TextFillR( pszBuffer, 12, szTemp ); +} + +/************************************************************************/ +/* USGSDEMWriteARecord() */ +/************************************************************************/ + +static int USGSDEMWriteARecord( USGSDEMWriteInfo *psWInfo ) + +{ + char achARec[1024]; + int i; + const char *pszOption; + +/* -------------------------------------------------------------------- */ +/* Init to blanks. */ +/* -------------------------------------------------------------------- */ + memset( achARec, ' ', sizeof(achARec) ); + +/* -------------------------------------------------------------------- */ +/* Load template file, if one is indicated. */ +/* -------------------------------------------------------------------- */ + const char *pszTemplate = + CSLFetchNameValue( psWInfo->papszOptions, "TEMPLATE" ); + if( pszTemplate != NULL ) + { + VSILFILE *fpTemplate; + + fpTemplate = VSIFOpenL( pszTemplate, "rb" ); + if( fpTemplate == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open template file '%s'.\n%s", + pszTemplate, VSIStrerror( errno ) ); + return FALSE; + } + + if( VSIFReadL( achARec, 1, 1024, fpTemplate ) != 1024 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Unable to read 1024 byte A Record from template file '%s'.\n%s", + pszTemplate, VSIStrerror( errno ) ); + return FALSE; + } + VSIFCloseL( fpTemplate ); + } + +/* -------------------------------------------------------------------- */ +/* Filename (right justify) */ +/* -------------------------------------------------------------------- */ + TextFillR( achARec + 0, 40, CPLGetFilename( psWInfo->pszFilename ) ); + +/* -------------------------------------------------------------------- */ +/* Producer */ +/* -------------------------------------------------------------------- */ + pszOption = CSLFetchNameValue( psWInfo->papszOptions, "PRODUCER" ); + + if( pszOption != NULL ) + TextFillR( achARec + 40, 60, pszOption ); + + else if( pszTemplate == NULL ) + TextFill( achARec + 40, 60, "" ); + +/* -------------------------------------------------------------------- */ +/* Filler */ +/* -------------------------------------------------------------------- */ + TextFill( achARec + 100, 9, "" ); + +/* -------------------------------------------------------------------- */ +/* SW Geographic Corner - SDDDMMSS.SSSS - longitude then latitude */ +/* -------------------------------------------------------------------- */ + if ( ! psWInfo->utmzone ) + { + TextFill( achARec + 109, 13, + USGSDEMDecToPackedDMS( psWInfo->dfLLX ) ); // longitude + TextFill( achARec + 122, 13, + USGSDEMDecToPackedDMS( psWInfo->dfLLY ) ); // latitude + } + /* this may not be best according to the spec. But for now, + * we won't try to convert the UTM coordinates to lat/lon + */ + +/* -------------------------------------------------------------------- */ +/* Process code. */ +/* -------------------------------------------------------------------- */ + pszOption = CSLFetchNameValue( psWInfo->papszOptions, "ProcessCode" ); + + if( pszOption != NULL ) + TextFill( achARec + 135, 1, pszOption ); + + else if( pszTemplate == NULL ) + TextFill( achARec + 135, 1, " " ); + +/* -------------------------------------------------------------------- */ +/* Filler */ +/* -------------------------------------------------------------------- */ + TextFill( achARec + 136, 1, "" ); + +/* -------------------------------------------------------------------- */ +/* Sectional indicator */ +/* -------------------------------------------------------------------- */ + if( pszTemplate == NULL ) + TextFill( achARec + 137, 3, "" ); + +/* -------------------------------------------------------------------- */ +/* Origin code */ +/* -------------------------------------------------------------------- */ + pszOption = CSLFetchNameValue( psWInfo->papszOptions, "OriginCode" ); + + if( pszOption != NULL ) + TextFill( achARec + 140, 4, pszOption ); // Should be YT for Yukon. + + else if( pszTemplate == NULL ) + TextFill( achARec + 140, 4, "" ); + +/* -------------------------------------------------------------------- */ +/* DEM level code (right justify) */ +/* -------------------------------------------------------------------- */ + pszOption = CSLFetchNameValue( psWInfo->papszOptions, "DEMLevelCode" ); + + if( pszOption != NULL ) + TextFillR( achARec + 144, 6, pszOption ); // 1, 2 or 3. + + else if( pszTemplate == NULL ) + TextFillR( achARec + 144, 6, "1" ); // 1, 2 or 3. + /* some DEM readers require a value, 1 seems to be a + * default + */ + +/* -------------------------------------------------------------------- */ +/* Elevation Pattern */ +/* -------------------------------------------------------------------- */ + TextFillR( achARec + 150, 6, "1" ); // "1" for regular (random is 2) + +/* -------------------------------------------------------------------- */ +/* Horizontal Reference System. */ +/* */ +/* 0 = Geographic */ +/* 1 = UTM */ +/* 2 = Stateplane */ +/* -------------------------------------------------------------------- */ + if ( ! psWInfo->utmzone ) + { + TextFillR( achARec + 156, 6, "0" ); + } + else + { + TextFillR( achARec + 156, 6, "1" ); + } + +/* -------------------------------------------------------------------- */ +/* UTM / State Plane zone. */ +/* -------------------------------------------------------------------- */ + if ( ! psWInfo->utmzone ) + { + TextFillR( achARec + 162, 6, "0"); + } + else + { + TextFillR( achARec + 162, 6, + CPLSPrintf( "%02d", psWInfo->utmzone) ); + } + +/* -------------------------------------------------------------------- */ +/* Map Projection Parameters (all 0.0). */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < 15; i++ ) + TextFillR( achARec + 168 + i*24, 24, "0.0" ); + +/* -------------------------------------------------------------------- */ +/* Horizontal Unit of Measure */ +/* 0 = radians */ +/* 1 = feet */ +/* 2 = meters */ +/* 3 = arc seconds */ +/* -------------------------------------------------------------------- */ + if ( ! psWInfo->utmzone ) + { + TextFillR( achARec + 528, 6, "3" ); + } + else + { + TextFillR( achARec + 528, 6, "2" ); + } + +/* -------------------------------------------------------------------- */ +/* Vertical unit of measure. */ +/* 1 = feet */ +/* 2 = meters */ +/* -------------------------------------------------------------------- */ + TextFillR( achARec + 534, 6, "2" ); + +/* -------------------------------------------------------------------- */ +/* Number of sides in coverage polygon (always 4) */ +/* -------------------------------------------------------------------- */ + TextFillR( achARec + 540, 6, "4" ); + +/* -------------------------------------------------------------------- */ +/* 4 corner coordinates: SW, NW, NE, SE */ +/* Corners are in 24.15 format in arc seconds. */ +/* -------------------------------------------------------------------- */ + if ( ! psWInfo->utmzone ) + { + // SW - longitude + USGSDEMPrintDouble( achARec + 546, psWInfo->dfLLX * 3600.0 ); + // SW - latitude + USGSDEMPrintDouble( achARec + 570, psWInfo->dfLLY * 3600.0 ); + + // NW - longitude + USGSDEMPrintDouble( achARec + 594, psWInfo->dfULX * 3600.0 ); + // NW - latitude + USGSDEMPrintDouble( achARec + 618, psWInfo->dfULY * 3600.0 ); + + // NE - longitude + USGSDEMPrintDouble( achARec + 642, psWInfo->dfURX * 3600.0 ); + // NE - latitude + USGSDEMPrintDouble( achARec + 666, psWInfo->dfURY * 3600.0 ); + + // SE - longitude + USGSDEMPrintDouble( achARec + 690, psWInfo->dfLRX * 3600.0 ); + // SE - latitude + USGSDEMPrintDouble( achARec + 714, psWInfo->dfLRY * 3600.0 ); + } + else + { + // SW - easting + USGSDEMPrintDouble( achARec + 546, psWInfo->dfLLX ); + // SW - northing + USGSDEMPrintDouble( achARec + 570, psWInfo->dfLLY ); + + // NW - easting + USGSDEMPrintDouble( achARec + 594, psWInfo->dfULX ); + // NW - northing + USGSDEMPrintDouble( achARec + 618, psWInfo->dfULY ); + + // NE - easting + USGSDEMPrintDouble( achARec + 642, psWInfo->dfURX ); + // NE - northing + USGSDEMPrintDouble( achARec + 666, psWInfo->dfURY ); + + // SE - easting + USGSDEMPrintDouble( achARec + 690, psWInfo->dfLRX ); + // SE - northing + USGSDEMPrintDouble( achARec + 714, psWInfo->dfLRY ); + } + +/* -------------------------------------------------------------------- */ +/* Minimum and Maximum elevations for this cell. */ +/* 24.15 format. */ +/* -------------------------------------------------------------------- */ + GInt16 nMin = DEM_NODATA, nMax = DEM_NODATA; + int nVoid = 0; + + for( i = psWInfo->nXSize*psWInfo->nYSize-1; i >= 0; i-- ) + { + if( psWInfo->panData[i] != DEM_NODATA ) + { + if( nMin == DEM_NODATA ) + { + nMin = nMax = psWInfo->panData[i]; + } + else + { + nMin = MIN(nMin,psWInfo->panData[i]); + nMax = MAX(nMax,psWInfo->panData[i]); + } + } + else + nVoid++; + } + + /* take into account z resolutions that are not 1.0 */ + nMin = (GInt16) floor(nMin * psWInfo->dfElevStepSize); + nMax = (GInt16) ceil(nMax * psWInfo->dfElevStepSize); + + USGSDEMPrintDouble( achARec + 738, (double) nMin ); + USGSDEMPrintDouble( achARec + 762, (double) nMax ); + +/* -------------------------------------------------------------------- */ +/* Counter Clockwise angle (in radians). Normally 0 */ +/* -------------------------------------------------------------------- */ + TextFillR( achARec + 786, 24, "0.0" ); + +/* -------------------------------------------------------------------- */ +/* Accurancy code for elevations. 0 means there will be no C */ +/* record. */ +/* -------------------------------------------------------------------- */ + TextFillR( achARec + 810, 6, "0" ); + +/* -------------------------------------------------------------------- */ +/* Spatial Resolution (x, y and z). 12.6 format. */ +/* -------------------------------------------------------------------- */ + if ( ! psWInfo->utmzone ) + { + USGSDEMPrintSingle( achARec + 816, + psWInfo->dfHorizStepSize*3600.0 ); + USGSDEMPrintSingle( achARec + 828, + psWInfo->dfVertStepSize*3600.0 ); + } + else + { + USGSDEMPrintSingle( achARec + 816, + psWInfo->dfHorizStepSize ); + USGSDEMPrintSingle( achARec + 828, + psWInfo->dfVertStepSize ); + } + + USGSDEMPrintSingle( achARec + 840, psWInfo->dfElevStepSize); + +/* -------------------------------------------------------------------- */ +/* Rows and Columns of profiles. */ +/* -------------------------------------------------------------------- */ + TextFillR( achARec + 852, 6, CPLSPrintf( "%d", 1 ) ); + TextFillR( achARec + 858, 6, CPLSPrintf( "%d", psWInfo->nXSize ) ); + +/* -------------------------------------------------------------------- */ +/* Largest primary contour interval (blank). */ +/* -------------------------------------------------------------------- */ + TextFill( achARec + 864, 5, "" ); + +/* -------------------------------------------------------------------- */ +/* Largest source contour internal unit (blank). */ +/* -------------------------------------------------------------------- */ + TextFill( achARec + 869, 1, "" ); + +/* -------------------------------------------------------------------- */ +/* Smallest primary contour interval. */ +/* -------------------------------------------------------------------- */ + TextFill( achARec + 870, 5, "" ); + +/* -------------------------------------------------------------------- */ +/* Smallest source contour interval unit. */ +/* -------------------------------------------------------------------- */ + TextFill( achARec + 875, 1, "" ); + +/* -------------------------------------------------------------------- */ +/* Data source data - YYMM */ +/* -------------------------------------------------------------------- */ + if( pszTemplate == NULL ) + TextFill( achARec + 876, 4, "" ); + +/* -------------------------------------------------------------------- */ +/* Data inspection/revision data (YYMM). */ +/* -------------------------------------------------------------------- */ + if( pszTemplate == NULL ) + TextFill( achARec + 880, 4, "" ); + +/* -------------------------------------------------------------------- */ +/* Inspection revision flag (I or R) (blank) */ +/* -------------------------------------------------------------------- */ + if( pszTemplate == NULL ) + TextFill( achARec + 884, 1, "" ); + +/* -------------------------------------------------------------------- */ +/* Data validation flag. */ +/* -------------------------------------------------------------------- */ + if( pszTemplate == NULL ) + TextFill( achARec + 885, 1, "" ); + +/* -------------------------------------------------------------------- */ +/* Suspect and void area flag. */ +/* 0 = none */ +/* 1 = suspect areas */ +/* 2 = void areas */ +/* 3 = suspect and void areas */ +/* -------------------------------------------------------------------- */ + if( nVoid > 0 ) + TextFillR( achARec + 886, 2, "2" ); + else + TextFillR( achARec + 886, 2, "0" ); + +/* -------------------------------------------------------------------- */ +/* Vertical datum */ +/* 1 = MSL */ +/* 2 = NGVD29 */ +/* 3 = NAVD88 */ +/* -------------------------------------------------------------------- */ + if( pszTemplate == NULL ) + TextFillR( achARec + 888, 2, "1" ); + +/* -------------------------------------------------------------------- */ +/* Horizonal Datum */ +/* 1 = NAD27 */ +/* 2 = WGS72 */ +/* 3 = WGS84 */ +/* 4 = NAD83 */ +/* -------------------------------------------------------------------- */ + if( strlen( psWInfo->horizdatum ) == 0) { + if( pszTemplate == NULL ) + TextFillR( achARec + 890, 2, "4" ); + } + else + { + if( pszTemplate == NULL ) + TextFillR( achARec + 890, 2, psWInfo->horizdatum ); + } + +/* -------------------------------------------------------------------- */ +/* Data edition/version, specification edition/version. */ +/* -------------------------------------------------------------------- */ + pszOption = CSLFetchNameValue( psWInfo->papszOptions, "DataSpecVersion" ); + + if( pszOption != NULL ) + TextFill( achARec + 892, 4, pszOption ); + + else if( pszTemplate == NULL ) + TextFill( achARec + 892, 4, "" ); + +/* -------------------------------------------------------------------- */ +/* Percent void. */ +/* */ +/* Round to nearest integer percentage. */ +/* -------------------------------------------------------------------- */ + int nPercent; + + nPercent = (int) + (((nVoid * 100.0) / (psWInfo->nXSize * psWInfo->nYSize)) + 0.5); + + TextFillR( achARec + 896, 4, CPLSPrintf( "%4d", nPercent ) ); + +/* -------------------------------------------------------------------- */ +/* Edge matching flags. */ +/* -------------------------------------------------------------------- */ + if( pszTemplate == NULL ) + TextFill( achARec + 900, 8, "" ); + +/* -------------------------------------------------------------------- */ +/* Vertical datum shift (F7.2). */ +/* -------------------------------------------------------------------- */ + TextFillR( achARec + 908, 7, "" ); + +/* -------------------------------------------------------------------- */ +/* Write to file. */ +/* -------------------------------------------------------------------- */ + if( VSIFWriteL( achARec, 1, 1024, psWInfo->fp ) != 1024 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Error writing DEM/CDED A record.\n%s", + VSIStrerror( errno ) ); + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* USGSDEMWriteProfile() */ +/* */ +/* Write B logical record. Split into 1024 byte chunks. */ +/************************************************************************/ + +static int USGSDEMWriteProfile( USGSDEMWriteInfo *psWInfo, int iProfile ) + +{ + char achBuffer[1024]; + + memset( achBuffer, ' ', sizeof(achBuffer) ); + +/* -------------------------------------------------------------------- */ +/* Row #. */ +/* -------------------------------------------------------------------- */ + TextFillR( achBuffer + 0, 6, "1" ); + +/* -------------------------------------------------------------------- */ +/* Column #. */ +/* -------------------------------------------------------------------- */ + TextFillR( achBuffer + 6, 6, CPLSPrintf( "%d", iProfile + 1 ) ); + +/* -------------------------------------------------------------------- */ +/* Number of data items. */ +/* -------------------------------------------------------------------- */ + TextFillR( achBuffer + 12, 6, CPLSPrintf( "%d", psWInfo->nYSize ) ); + TextFillR( achBuffer + 18, 6, "1" ); + +/* -------------------------------------------------------------------- */ +/* Location of center of bottom most sample in profile. */ +/* Format D24.15. In arc-seconds if geographic, meters */ +/* if UTM. */ +/* -------------------------------------------------------------------- */ + if( ! psWInfo->utmzone ) + { + // longitude + USGSDEMPrintDouble( achBuffer + 24, + 3600 * (psWInfo->dfLLX + + iProfile * psWInfo->dfHorizStepSize) ); + + // latitude + USGSDEMPrintDouble( achBuffer + 48, psWInfo->dfLLY * 3600.0 ); + } + else + { + // easting + USGSDEMPrintDouble( achBuffer + 24, + (psWInfo->dfLLX + + iProfile * psWInfo->dfHorizStepSize) ); + + // northing + USGSDEMPrintDouble( achBuffer + 48, psWInfo->dfLLY ); + } + + +/* -------------------------------------------------------------------- */ +/* Local vertical datum offset. */ +/* -------------------------------------------------------------------- */ + TextFillR( achBuffer + 72, 24, "0.000000D+00" ); + +/* -------------------------------------------------------------------- */ +/* Min/Max elevation values for this profile. */ +/* -------------------------------------------------------------------- */ + int iY; + GInt16 nMin = DEM_NODATA, nMax = DEM_NODATA; + + for( iY = 0; iY < psWInfo->nYSize; iY++ ) + { + int iData = (psWInfo->nYSize-iY-1) * psWInfo->nXSize + iProfile; + + if( psWInfo->panData[iData] != DEM_NODATA ) + { + if( nMin == DEM_NODATA ) + { + nMin = nMax = psWInfo->panData[iData]; + } + else + { + nMin = MIN(nMin,psWInfo->panData[iData]); + nMax = MAX(nMax,psWInfo->panData[iData]); + } + } + } + + /* take into account z resolutions that are not 1.0 */ + nMin = (GInt16) floor(nMin * psWInfo->dfElevStepSize); + nMax = (GInt16) ceil(nMax * psWInfo->dfElevStepSize); + + USGSDEMPrintDouble( achBuffer + 96, (double) nMin ); + USGSDEMPrintDouble( achBuffer + 120, (double) nMax ); + +/* -------------------------------------------------------------------- */ +/* Output all the actually elevation values, flushing blocks */ +/* when they fill up. */ +/* -------------------------------------------------------------------- */ + int iOffset = 144; + + for( iY = 0; iY < psWInfo->nYSize; iY++ ) + { + int iData = (psWInfo->nYSize-iY-1) * psWInfo->nXSize + iProfile; + char szWord[10]; + + if( iOffset + 6 > 1024 ) + { + if( VSIFWriteL( achBuffer, 1, 1024, psWInfo->fp ) != 1024 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failure writing profile to disk.\n%s", + VSIStrerror( errno ) ); + return FALSE; + } + iOffset = 0; + memset( achBuffer, ' ', 1024 ); + } + + sprintf( szWord, "%d", psWInfo->panData[iData] ); + TextFillR( achBuffer + iOffset, 6, szWord ); + + iOffset += 6; + } + +/* -------------------------------------------------------------------- */ +/* Flush final partial block. */ +/* -------------------------------------------------------------------- */ + if( VSIFWriteL( achBuffer, 1, 1024, psWInfo->fp ) != 1024 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failure writing profile to disk.\n%s", + VSIStrerror( errno ) ); + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* USGSDEM_LookupNTSByLoc() */ +/************************************************************************/ + +static int +USGSDEM_LookupNTSByLoc( double dfULLong, double dfULLat, + char *pszTile, char *pszName ) + +{ +/* -------------------------------------------------------------------- */ +/* Access NTS 1:50k sheet CSV file. */ +/* -------------------------------------------------------------------- */ + const char *pszNTSFilename = CSVFilename( "NTS-50kindex.csv" ); + FILE *fpNTS; + + fpNTS = VSIFOpen( pszNTSFilename, "rb" ); + if( fpNTS == NULL ) + { + CPLError( CE_Failure, CPLE_FileIO, "Unable to find NTS mapsheet lookup file: %s", + pszNTSFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Skip column titles line. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( CSVReadParseLine( fpNTS ) ); + +/* -------------------------------------------------------------------- */ +/* Find desired sheet. */ +/* -------------------------------------------------------------------- */ + int bGotHit = FALSE; + char **papszTokens; + + while( !bGotHit + && (papszTokens = CSVReadParseLine( fpNTS )) != NULL ) + { + if( CSLCount( papszTokens ) != 4 ) + continue; + + if( ABS(dfULLong - CPLAtof(papszTokens[2])) < 0.01 + && ABS(dfULLat - CPLAtof(papszTokens[3])) < 0.01 ) + { + bGotHit = TRUE; + strncpy( pszTile, papszTokens[0], 7 ); + if( pszName != NULL ) + strncpy( pszName, papszTokens[1], 100 ); + } + + CSLDestroy( papszTokens ); + } + + VSIFClose( fpNTS ); + + return bGotHit; +} + +/************************************************************************/ +/* USGSDEM_LookupNTSByTile() */ +/************************************************************************/ + +static int +USGSDEM_LookupNTSByTile( const char *pszTile, char *pszName, + double *pdfULLong, double *pdfULLat ) + +{ +/* -------------------------------------------------------------------- */ +/* Access NTS 1:50k sheet CSV file. */ +/* -------------------------------------------------------------------- */ + const char *pszNTSFilename = CSVFilename( "NTS-50kindex.csv" ); + FILE *fpNTS; + + fpNTS = VSIFOpen( pszNTSFilename, "rb" ); + if( fpNTS == NULL ) + { + CPLError( CE_Failure, CPLE_FileIO, "Unable to find NTS mapsheet lookup file: %s", + pszNTSFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Skip column titles line. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( CSVReadParseLine( fpNTS ) ); + +/* -------------------------------------------------------------------- */ +/* Find desired sheet. */ +/* -------------------------------------------------------------------- */ + int bGotHit = FALSE; + char **papszTokens; + + while( !bGotHit + && (papszTokens = CSVReadParseLine( fpNTS )) != NULL ) + { + if( CSLCount( papszTokens ) != 4 ) + continue; + + if( EQUAL(pszTile,papszTokens[0]) ) + { + bGotHit = TRUE; + if( pszName != NULL ) + strncpy( pszName, papszTokens[1], 100 ); + *pdfULLong = CPLAtof(papszTokens[2]); + *pdfULLat = CPLAtof(papszTokens[3]); + } + + CSLDestroy( papszTokens ); + } + + VSIFClose( fpNTS ); + + return bGotHit; +} + +/************************************************************************/ +/* USGSDEMProductSetup_CDED50K() */ +/************************************************************************/ + +static int USGSDEMProductSetup_CDED50K( USGSDEMWriteInfo *psWInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch TOPLEFT location so we know what cell we are dealing */ +/* with. */ +/* -------------------------------------------------------------------- */ + const char *pszNTS = + CSLFetchNameValue( psWInfo->papszOptions, "NTS" ); + const char *pszTOPLEFT = CSLFetchNameValue( psWInfo->papszOptions, + "TOPLEFT" ); + double dfULX = (psWInfo->dfULX+psWInfo->dfURX)*0.5; + double dfULY = (psWInfo->dfULY+psWInfo->dfURY)*0.5; + + // Have we been given an explicit NTS mapsheet name? + if( pszNTS != NULL ) + { + char szTrimmedTile[7]; + + strncpy( szTrimmedTile, pszNTS, 6 ); + szTrimmedTile[6] = '\0'; + + if( !USGSDEM_LookupNTSByTile( szTrimmedTile, NULL, &dfULX, &dfULY ) ) + return FALSE; + + if( EQUALN(pszNTS+6,"e",1) ) + dfULX += (( dfULY < 68.1 ) ? 0.25 : ( dfULY < 80.1 ) ? 0.5 : 1); + } + + // Try looking up TOPLEFT as a NTS mapsheet name. + else if( pszTOPLEFT != NULL && strstr(pszTOPLEFT,",") == NULL + && (strlen(pszTOPLEFT) == 6 || strlen(pszTOPLEFT) == 7) ) + { + char szTrimmedTile[7]; + + strncpy( szTrimmedTile, pszTOPLEFT, 6 ); + szTrimmedTile[6] = '\0'; + + if( !USGSDEM_LookupNTSByTile( szTrimmedTile, NULL, &dfULX, &dfULY ) ) + return FALSE; + + if( EQUAL(pszTOPLEFT+6,"e") ) + dfULX += (( dfULY < 68.1 ) ? 0.25 : ( dfULY < 80.1 ) ? 0.5 : 1); + } + + // Assume TOPLEFT is a long/lat corner. + else if( pszTOPLEFT != NULL ) + { + char **papszTokens = CSLTokenizeString2( pszTOPLEFT, ",", 0 ); + + if( CSLCount( papszTokens ) != 2 ) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to parse TOPLEFT, should have form like '138d15W,59d0N'." ); + return FALSE; + } + + dfULX = CPLDMSToDec( papszTokens[0] ); + dfULY = CPLDMSToDec( papszTokens[1] ); + CSLDestroy( papszTokens ); + + if( ABS(dfULX*4-floor(dfULX*4+0.00005)) > 0.0001 + || ABS(dfULY*4-floor(dfULY*4+0.00005)) > 0.0001 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "TOPLEFT must be on a 15\" boundary for CDED50K, but is not." ); + return FALSE; + } + } + else if( strlen(psWInfo->pszFilename) == 12 + && psWInfo->pszFilename[6] == '_' + && EQUAL(psWInfo->pszFilename+8,".dem") ) + { + char szTrimmedTile[7]; + + strncpy( szTrimmedTile, psWInfo->pszFilename, 6 ); + szTrimmedTile[6] = '\0'; + + if( !USGSDEM_LookupNTSByTile( szTrimmedTile, NULL, &dfULX, &dfULY ) ) + return FALSE; + + if( EQUALN(psWInfo->pszFilename+7,"e",1) ) + dfULX += (( dfULY < 68.1 ) ? 0.25 : ( dfULY < 80.1 ) ? 0.5 : 1); + } + + else if( strlen(psWInfo->pszFilename) == 14 + && EQUALN(psWInfo->pszFilename+6,"DEM",3) + && EQUAL(psWInfo->pszFilename+10,".dem") ) + { + char szTrimmedTile[7]; + + strncpy( szTrimmedTile, psWInfo->pszFilename, 6 ); + szTrimmedTile[6] = '\0'; + + if( !USGSDEM_LookupNTSByTile( szTrimmedTile, NULL, &dfULX, &dfULY ) ) + return FALSE; + + if( EQUALN(psWInfo->pszFilename+9,"e",1) ) + dfULX += (( dfULY < 68.1 ) ? 0.25 : ( dfULY < 80.1 ) ? 0.5 : 1); + } + +/* -------------------------------------------------------------------- */ +/* Set resolution and size information. */ +/* -------------------------------------------------------------------- */ + + dfULX = floor( dfULX * 4 + 0.00005 ) / 4.0; + dfULY = floor( dfULY * 4 + 0.00005 ) / 4.0; + + psWInfo->nXSize = 1201; + psWInfo->nYSize = 1201; + psWInfo->dfVertStepSize = 0.75 / 3600.0; + + /* Region A */ + if( dfULY < 68.1 ) + { + psWInfo->dfHorizStepSize = 0.75 / 3600.0; + } + + /* Region B */ + else if( dfULY < 80.1 ) + { + psWInfo->dfHorizStepSize = 1.5 / 3600.0; + dfULX = floor( dfULX * 2 + 0.001 ) / 2.0; + } + + /* Region C */ + else + { + psWInfo->dfHorizStepSize = 3.0 / 3600.0; + dfULX = floor( dfULX + 0.001 ); + } + +/* -------------------------------------------------------------------- */ +/* Set bounds based on this top left anchor. */ +/* -------------------------------------------------------------------- */ + + psWInfo->dfULX = dfULX; + psWInfo->dfULY = dfULY; + psWInfo->dfLLX = dfULX; + psWInfo->dfLLY = dfULY - 0.25; + psWInfo->dfURX = dfULX + psWInfo->dfHorizStepSize * 1200.0; + psWInfo->dfURY = dfULY; + psWInfo->dfLRX = dfULX + psWInfo->dfHorizStepSize * 1200.0; + psWInfo->dfLRY = dfULY - 0.25; + +/* -------------------------------------------------------------------- */ +/* Can we find the NTS 50k tile name that corresponds with */ +/* this? */ +/* -------------------------------------------------------------------- */ + const char *pszINTERNAL = + CSLFetchNameValue( psWInfo->papszOptions, "INTERNALNAME" ); + char szTile[10]; + char chEWFlag = ' '; + + if( USGSDEM_LookupNTSByLoc( dfULX, dfULY, szTile, NULL ) ) + { + chEWFlag = 'w'; + } + else if( USGSDEM_LookupNTSByLoc( dfULX-0.25, dfULY, szTile, NULL ) ) + { + chEWFlag = 'e'; + } + + if( pszINTERNAL != NULL ) + { + CPLFree( psWInfo->pszFilename ); + psWInfo->pszFilename = CPLStrdup( pszINTERNAL ); + } + else if( chEWFlag != ' ' ) + { + CPLFree( psWInfo->pszFilename ); + psWInfo->pszFilename = + CPLStrdup( CPLSPrintf("%sDEM%c", szTile, chEWFlag ) ); + } + else + { + const char *pszBasename = CPLGetFilename( psWInfo->pszFilename); + if( !EQUALN(pszBasename+6,"DEM",3) + || strlen(pszBasename) != 10 ) + CPLError( CE_Warning, CPLE_AppDefined, + "Internal filename required to be of 'nnnannDEMz', the output\n" + "filename is not of the required format, and the tile could not be\n" + "identified in the NTS mapsheet list (or the NTS mapsheet could not\n" + "be found). Correct output filename for correct CDED production." ); + } + +/* -------------------------------------------------------------------- */ +/* Set some specific options for CDED 50K. */ +/* -------------------------------------------------------------------- */ + psWInfo->papszOptions = + CSLSetNameValue( psWInfo->papszOptions, "DEMLevelCode", "1" ); + + if( CSLFetchNameValue( psWInfo->papszOptions, "DataSpecVersion" ) == NULL ) + psWInfo->papszOptions = + CSLSetNameValue( psWInfo->papszOptions, "DataSpecVersion", + "1020" ); + +/* -------------------------------------------------------------------- */ +/* Set the destination coordinate system. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + oSRS.SetWellKnownGeogCS( "NAD83" ); + strncpy( psWInfo->horizdatum, "4", 2 ); //USGS DEM code for NAD83 + + oSRS.exportToWkt( &(psWInfo->pszDstSRS) ); + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + CPLReadLine( NULL ); + + return TRUE; +} + +/************************************************************************/ +/* USGSDEMProductSetup_DEFAULT() */ +/* */ +/* Sets up the new DEM dataset parameters, using the source */ +/* dataset's parameters. If the source dataset uses UTM or */ +/* geographic coordinates, the coordinate system is carried over */ +/* to the new DEM file's parameters. If the source dataset has a */ +/* DEM compatible horizontal datum, the datum is carried over. */ +/* Otherwise, the DEM dataset is configured to use geographic */ +/* coordinates and a default datum. */ +/* (Hunter Blanks, 8/31/04, hblanks@artifex.org) */ +/************************************************************************/ + +static int USGSDEMProductSetup_DEFAULT( USGSDEMWriteInfo *psWInfo ) + +{ + +/* -------------------------------------------------------------------- */ +/* Set the destination coordinate system. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference DstoSRS; + OGRSpatialReference SrcoSRS; + char *sourceWkt; + int bNorth = TRUE; + /* XXX here we are assume (!) northern hemisphere UTM datasets */ + char **readSourceWkt; + int i; + int numdatums = 4; + const char DatumCodes[4][2] = { "1", "2", "3", "4" }; + char Datums[4][6] = { "NAD27", "WGS72", "WGS84", + "NAD83" }; + + /* get the source dataset's projection */ + sourceWkt = (char *) psWInfo->poSrcDS->GetProjectionRef(); + readSourceWkt = &sourceWkt; + if (SrcoSRS.importFromWkt(readSourceWkt) != OGRERR_NONE) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DEM Default Setup: Importing source dataset projection failed" ); + return FALSE; + } + + /* Set the destination dataset's projection. If the source datum + * used is DEM compatible, just use it. Otherwise, default to the + * last datum in the Datums array. + */ + for( i=0; i < numdatums; i++ ) + { + if (DstoSRS.SetWellKnownGeogCS(Datums[i]) != OGRERR_NONE) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DEM Default Setup: Failed to set datum of destination" ); + return FALSE; + } + /* XXX Hopefully it's ok, to just keep changing the projection + * of our destination. If not, we'll want to reinitialize the + * OGRSpatialReference each time. + */ + if ( DstoSRS.IsSameGeogCS( &SrcoSRS ) ) + { + break; + } + } + strncpy( psWInfo->horizdatum, DatumCodes[i], 2 ); + + /* get the UTM zone, if any */ + psWInfo->utmzone = SrcoSRS.GetUTMZone(&bNorth); + if (psWInfo->utmzone) + { + if (DstoSRS.SetUTM(psWInfo->utmzone) != OGRERR_NONE) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DEM Default Setup: Failed to set utm zone of destination" ); + /* SetUTM isn't documented to return OGRERR_NONE + * on success, but it does, so, we'll check for it. + */ + return FALSE; + } + } + + /* export the projection to sWInfo */ + if (DstoSRS.exportToWkt( &(psWInfo->pszDstSRS) ) != OGRERR_NONE) + { + CPLError( CE_Failure, CPLE_AppDefined, + "UTMDEM: Failed to export destination Wkt to psWInfo" ); + } + return TRUE; +} + +/************************************************************************/ +/* USGSDEMLoadRaster() */ +/* */ +/* Loads the raster from the source dataset (not normally USGS */ +/* DEM) into memory. If nodata is marked, a special effort is */ +/* made to translate it properly into the USGS nodata value. */ +/************************************************************************/ + +static int USGSDEMLoadRaster( CPL_UNUSED USGSDEMWriteInfo *psWInfo, + CPL_UNUSED GDALRasterBand *poSrcBand ) +{ + CPLErr eErr; + int i; + +/* -------------------------------------------------------------------- */ +/* Allocate output array, and pre-initialize to NODATA value. */ +/* -------------------------------------------------------------------- */ + psWInfo->panData = + (GInt16 *) VSIMalloc3( 2, psWInfo->nXSize, psWInfo->nYSize ); + if( psWInfo->panData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating %d byte internal copy of DEM.", + 2 * psWInfo->nXSize * psWInfo->nYSize ); + return FALSE; + } + + for( i = 0; i < psWInfo->nXSize * psWInfo->nYSize; i++ ) + psWInfo->panData[i] = DEM_NODATA; + +/* -------------------------------------------------------------------- */ +/* Make a "memory dataset" wrapper for this data array. */ +/* -------------------------------------------------------------------- */ + GDALDriver *poMemDriver = (GDALDriver *) GDALGetDriverByName( "MEM" ); + GDALDataset *poMemDS; + + if( poMemDriver == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find MEM driver." ); + return FALSE; + } + + poMemDS = + poMemDriver->Create( "USGSDEM_temp", psWInfo->nXSize, psWInfo->nYSize, + 0, GDT_Int16, NULL ); + if( poMemDS == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Now add the array itself as a band. */ +/* -------------------------------------------------------------------- */ + char szDataPointer[100]; + char *apszOptions[] = { szDataPointer, NULL }; + + memset( szDataPointer, 0, sizeof(szDataPointer) ); + sprintf( szDataPointer, "DATAPOINTER=" ); + CPLPrintPointer( szDataPointer+strlen(szDataPointer), + psWInfo->panData, + sizeof(szDataPointer) - strlen(szDataPointer) ); + + if( poMemDS->AddBand( GDT_Int16, apszOptions ) != CE_None ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Assign geotransform and nodata indicators. */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + + adfGeoTransform[0] = psWInfo->dfULX - psWInfo->dfHorizStepSize * 0.5; + adfGeoTransform[1] = psWInfo->dfHorizStepSize; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = psWInfo->dfULY + psWInfo->dfVertStepSize * 0.5; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = -psWInfo->dfVertStepSize; + + poMemDS->SetGeoTransform( adfGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Set coordinate system if we have a special one to set. */ +/* -------------------------------------------------------------------- */ + if( psWInfo->pszDstSRS ) + poMemDS->SetProjection( psWInfo->pszDstSRS ); + +/* -------------------------------------------------------------------- */ +/* Establish the resampling kernel to use. */ +/* -------------------------------------------------------------------- */ + GDALResampleAlg eResampleAlg = GRA_Bilinear; + const char *pszResample = CSLFetchNameValue( psWInfo->papszOptions, + "RESAMPLE" ); + + if( pszResample == NULL ) + /* bilinear */; + else if( EQUAL(pszResample,"Nearest") ) + eResampleAlg = GRA_NearestNeighbour; + else if( EQUAL(pszResample,"Bilinear") ) + eResampleAlg = GRA_Bilinear; + else if( EQUAL(pszResample,"Cubic") ) + eResampleAlg = GRA_Cubic; + else if( EQUAL(pszResample,"CubicSpline") ) + eResampleAlg = GRA_CubicSpline; + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "RESAMPLE=%s, not a supported resampling kernel.", + pszResample ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Perform a warp from source dataset to destination buffer */ +/* (memory dataset). */ +/* -------------------------------------------------------------------- */ + eErr = GDALReprojectImage( (GDALDatasetH) psWInfo->poSrcDS, + psWInfo->poSrcDS->GetProjectionRef(), + (GDALDatasetH) poMemDS, + psWInfo->pszDstSRS, + eResampleAlg, 0.0, 0.0, NULL, NULL, + NULL ); + +/* -------------------------------------------------------------------- */ +/* Deallocate memory wrapper for the buffer. */ +/* -------------------------------------------------------------------- */ + delete poMemDS; + + return eErr == CE_None; +} + + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * +USGSDEMCreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, + char **papszOptions, + CPL_UNUSED GDALProgressFunc pfnProgress, + CPL_UNUSED void * pProgressData ) +{ + USGSDEMWriteInfo sWInfo; + + if( poSrcDS->GetRasterCount() != 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create multi-band USGS DEM / CDED files." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Capture some preliminary information. */ +/* -------------------------------------------------------------------- */ + memset( &sWInfo, 0, sizeof(sWInfo) ); + + sWInfo.poSrcDS = poSrcDS; + sWInfo.pszFilename = CPLStrdup(pszFilename); + sWInfo.nXSize = poSrcDS->GetRasterXSize(); + sWInfo.nYSize = poSrcDS->GetRasterYSize(); + sWInfo.papszOptions = CSLDuplicate( papszOptions ); + sWInfo.bStrict = bStrict; + sWInfo.utmzone = 0; + strncpy( sWInfo.horizdatum, "", 1 ); + + if ( sWInfo.nXSize <= 1 || sWInfo.nYSize <= 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Source dataset dimensions must be at least 2x2." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Work out corner coordinates. */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + + poSrcDS->GetGeoTransform( adfGeoTransform ); + + sWInfo.dfLLX = adfGeoTransform[0] + adfGeoTransform[1] * 0.5; + sWInfo.dfLLY = adfGeoTransform[3] + + adfGeoTransform[5] * (sWInfo.nYSize - 0.5); + + sWInfo.dfULX = adfGeoTransform[0] + adfGeoTransform[1] * 0.5; + sWInfo.dfULY = adfGeoTransform[3] + adfGeoTransform[5] * 0.5; + + sWInfo.dfURX = adfGeoTransform[0] + + adfGeoTransform[1] * (sWInfo.nXSize - 0.5); + sWInfo.dfURY = adfGeoTransform[3] + adfGeoTransform[5] * 0.5; + + sWInfo.dfLRX = adfGeoTransform[0] + + adfGeoTransform[1] * (sWInfo.nXSize - 0.5); + sWInfo.dfLRY = adfGeoTransform[3] + + adfGeoTransform[5] * (sWInfo.nYSize - 0.5); + + sWInfo.dfHorizStepSize = (sWInfo.dfURX - sWInfo.dfULX) / (sWInfo.nXSize-1); + sWInfo.dfVertStepSize = (sWInfo.dfURY - sWInfo.dfLRY) / (sWInfo.nYSize-1); + +/* -------------------------------------------------------------------- */ +/* Allow override of z resolution, but default to 1.0. */ +/* -------------------------------------------------------------------- */ + const char *zResolution = CSLFetchNameValue( + sWInfo.papszOptions, "ZRESOLUTION" ); + + if( zResolution == NULL || EQUAL(zResolution,"DEFAULT") ) + { + sWInfo.dfElevStepSize = 1.0; + } + else + { + // XXX: We are using CPLAtof() here instead of CPLAtof() because + // zResolution value comes from user's input and supposed to be + // written according to user's current locale. CPLAtof() honors locale + // setting, CPLAtof() is not. + sWInfo.dfElevStepSize = CPLAtof( zResolution ); + if ( sWInfo.dfElevStepSize <= 0 ) + { + /* don't allow negative values */ + sWInfo.dfElevStepSize = 1.0; + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize for special product configurations. */ +/* -------------------------------------------------------------------- */ + const char *pszProduct = CSLFetchNameValue( sWInfo.papszOptions, + "PRODUCT" ); + + if( pszProduct == NULL || EQUAL(pszProduct,"DEFAULT") ) + { + if ( !USGSDEMProductSetup_DEFAULT( &sWInfo ) ) + { + USGSDEMWriteCleanup( &sWInfo ); + return NULL; + } + } + else if( EQUAL(pszProduct,"CDED50K") ) + { + if( !USGSDEMProductSetup_CDED50K( &sWInfo ) ) + { + USGSDEMWriteCleanup( &sWInfo ); + return NULL; + } + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "DEM PRODUCT='%s' not recognised.", + pszProduct ); + USGSDEMWriteCleanup( &sWInfo ); + return NULL; + } + + +/* -------------------------------------------------------------------- */ +/* Read the whole area of interest into memory. */ +/* -------------------------------------------------------------------- */ + if( !USGSDEMLoadRaster( &sWInfo, poSrcDS->GetRasterBand( 1 ) ) ) + { + USGSDEMWriteCleanup( &sWInfo ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the output file. */ +/* -------------------------------------------------------------------- */ + sWInfo.fp = VSIFOpenL( pszFilename, "wb" ); + if( sWInfo.fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s", VSIStrerror( errno ) ); + USGSDEMWriteCleanup( &sWInfo ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write the A record. */ +/* -------------------------------------------------------------------- */ + if( !USGSDEMWriteARecord( &sWInfo ) ) + { + USGSDEMWriteCleanup( &sWInfo ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write profiles. */ +/* -------------------------------------------------------------------- */ + int iProfile; + + for( iProfile = 0; iProfile < sWInfo.nXSize; iProfile++ ) + { + if( !USGSDEMWriteProfile( &sWInfo, iProfile ) ) + { + USGSDEMWriteCleanup( &sWInfo ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + USGSDEMWriteCleanup( &sWInfo ); + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + GDALPamDataset *poDS = (GDALPamDataset *) + GDALOpen( pszFilename, GA_ReadOnly ); + + if( poDS ) + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} diff --git a/bazaar/plugin/gdal/frmts/usgsdem/usgsdemdataset.cpp b/bazaar/plugin/gdal/frmts/usgsdem/usgsdemdataset.cpp new file mode 100644 index 000000000..0da516468 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/usgsdem/usgsdemdataset.cpp @@ -0,0 +1,898 @@ +/****************************************************************************** + * $Id: usgsdemdataset.cpp 28351 2015-01-23 18:47:42Z rouault $ + * + * Project: USGS DEM Driver + * Purpose: All reader for USGS DEM Reader + * Author: Frank Warmerdam, warmerdam@pobox.com + * + * Portions of this module derived from the VTP USGS DEM driver by Ben + * Discoe, see http://www.vterrain.org + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: usgsdemdataset.cpp 28351 2015-01-23 18:47:42Z rouault $"); + +CPL_C_START +void GDALRegister_USGSDEM(void); +CPL_C_END + +typedef struct { + double x; + double y; +} DPoint2; + +#define USGSDEM_NODATA -32767 + +GDALDataset *USGSDEMCreateCopy( const char *, GDALDataset *, int, char **, + GDALProgressFunc pfnProgress, + void * pProgressData ); + + +/************************************************************************/ +/* ReadInt() */ +/************************************************************************/ + +static int ReadInt( VSILFILE *fp ) +{ + int nVal = 0; + char c; + int nRead = 0; + vsi_l_offset nOffset = VSIFTellL(fp); + + while (TRUE) + { + if (VSIFReadL(&c, 1, 1, fp) != 1) + { + return 0; + } + else + nRead ++; + if (!isspace((int)c)) + break; + } + + int nSign = 1; + if (c == '-') + nSign = -1; + else if (c == '+') + nSign = 1; + else if (c >= '0' && c <= '9') + nVal = c - '0'; + else + { + VSIFSeekL(fp, nOffset + nRead, SEEK_SET); + return 0; + } + + while (TRUE) + { + if (VSIFReadL(&c, 1, 1, fp) != 1) + return nSign * nVal; + nRead ++; + if (c >= '0' && c <= '9') + nVal = nVal * 10 + (c - '0'); + else + { + VSIFSeekL(fp, nOffset + (nRead - 1), SEEK_SET); + return nSign * nVal; + } + } +} + +typedef struct +{ + VSILFILE *fp; + int max_size; + char* buffer; + int buffer_size; + int cur_index; +} Buffer; + +/************************************************************************/ +/* USGSDEMRefillBuffer() */ +/************************************************************************/ + +static void USGSDEMRefillBuffer( Buffer* psBuffer ) +{ + memmove(psBuffer->buffer, psBuffer->buffer + psBuffer->cur_index, + psBuffer->buffer_size - psBuffer->cur_index); + + psBuffer->buffer_size -= psBuffer->cur_index; + psBuffer->buffer_size += VSIFReadL(psBuffer->buffer + psBuffer->buffer_size, + 1, psBuffer->max_size - psBuffer->buffer_size, + psBuffer->fp); + psBuffer->cur_index = 0; +} + +/************************************************************************/ +/* USGSDEMReadIntFromBuffer() */ +/************************************************************************/ + +static int USGSDEMReadIntFromBuffer( Buffer* psBuffer, int* pbSuccess = NULL ) +{ + int nVal = 0; + char c; + + while (TRUE) + { + if (psBuffer->cur_index >= psBuffer->buffer_size) + { + USGSDEMRefillBuffer(psBuffer); + if (psBuffer->cur_index >= psBuffer->buffer_size) + { + if( pbSuccess ) *pbSuccess = FALSE; + return 0; + } + } + + c = psBuffer->buffer[psBuffer->cur_index]; + psBuffer->cur_index ++; + if (!isspace((int)c)) + break; + } + + int nSign = 1; + if (c == '-') + nSign = -1; + else if (c == '+') + nSign = 1; + else if (c >= '0' && c <= '9') + nVal = c - '0'; + else + { + if( pbSuccess ) *pbSuccess = FALSE; + return 0; + } + + while (TRUE) + { + if (psBuffer->cur_index >= psBuffer->buffer_size) + { + USGSDEMRefillBuffer(psBuffer); + if (psBuffer->cur_index >= psBuffer->buffer_size) + { + if( pbSuccess ) *pbSuccess = TRUE; + return nSign * nVal; + } + } + + c = psBuffer->buffer[psBuffer->cur_index]; + if (c >= '0' && c <= '9') + { + psBuffer->cur_index ++; + nVal = nVal * 10 + (c - '0'); + } + else + { + if( pbSuccess ) *pbSuccess = TRUE; + return nSign * nVal; + } + } +} + +/************************************************************************/ +/* USGSDEMReadDoubleFromBuffer() */ +/************************************************************************/ + +static double USGSDEMReadDoubleFromBuffer( Buffer* psBuffer, int nCharCount ) + +{ + int i; + + if (psBuffer->cur_index + nCharCount > psBuffer->buffer_size) + { + USGSDEMRefillBuffer(psBuffer); + if (psBuffer->cur_index + nCharCount > psBuffer->buffer_size) + { + return 0; + } + } + + char* szPtr = psBuffer->buffer + psBuffer->cur_index; + char backupC = szPtr[nCharCount]; + szPtr[nCharCount] = 0; + for( i = 0; i < nCharCount; i++ ) + { + if( szPtr[i] == 'D' ) + szPtr[i] = 'E'; + } + + double dfVal = CPLAtof(szPtr); + szPtr[nCharCount] = backupC; + psBuffer->cur_index += nCharCount; + + return dfVal; +} + +/************************************************************************/ +/* DConvert() */ +/************************************************************************/ + +static double DConvert( VSILFILE *fp, int nCharCount ) + +{ + char szBuffer[100]; + int i; + + VSIFReadL( szBuffer, nCharCount, 1, fp ); + szBuffer[nCharCount] = '\0'; + + for( i = 0; i < nCharCount; i++ ) + { + if( szBuffer[i] == 'D' ) + szBuffer[i] = 'E'; + } + + return CPLAtof(szBuffer); +} + +/************************************************************************/ +/* ==================================================================== */ +/* USGSDEMDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class USGSDEMRasterBand; + +class USGSDEMDataset : public GDALPamDataset +{ + friend class USGSDEMRasterBand; + + int nDataStartOffset; + GDALDataType eNaturalDataFormat; + + double adfGeoTransform[6]; + char *pszProjection; + + double fVRes; + + const char *pszUnits; + + int LoadFromFile( VSILFILE * ); + + VSILFILE *fp; + + public: + USGSDEMDataset(); + ~USGSDEMDataset(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + CPLErr GetGeoTransform( double * padfTransform ); + const char *GetProjectionRef(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* USGSDEMRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class USGSDEMRasterBand : public GDALPamRasterBand +{ + friend class USGSDEMDataset; + + public: + + USGSDEMRasterBand( USGSDEMDataset * ); + + virtual const char *GetUnitType(); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual CPLErr IReadBlock( int, int, void * ); +}; + + +/************************************************************************/ +/* USGSDEMRasterBand() */ +/************************************************************************/ + +USGSDEMRasterBand::USGSDEMRasterBand( USGSDEMDataset *poDS ) + +{ + this->poDS = poDS; + this->nBand = 1; + + eDataType = poDS->eNaturalDataFormat; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = poDS->GetRasterYSize(); + +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr USGSDEMRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + CPL_UNUSED int nBlockYOff, + void * pImage ) + +{ + double dfYMin; + /* int bad = FALSE; */ + USGSDEMDataset *poGDS = (USGSDEMDataset *) poDS; + +/* -------------------------------------------------------------------- */ +/* Initialize image buffer to nodata value. */ +/* -------------------------------------------------------------------- */ + for( int k = GetXSize() * GetYSize() - 1; k >= 0; k-- ) + { + if( GetRasterDataType() == GDT_Int16 ) + ((GInt16 *) pImage)[k] = USGSDEM_NODATA; + else + ((float *) pImage)[k] = USGSDEM_NODATA; + } + +/* -------------------------------------------------------------------- */ +/* Seek to data. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL(poGDS->fp, poGDS->nDataStartOffset, 0); + + dfYMin = poGDS->adfGeoTransform[3] + + (GetYSize()-0.5) * poGDS->adfGeoTransform[5]; + +/* -------------------------------------------------------------------- */ +/* Read all the profiles into the image buffer. */ +/* -------------------------------------------------------------------- */ + + Buffer sBuffer; + sBuffer.max_size = 32768; + sBuffer.buffer = (char*) CPLMalloc(sBuffer.max_size + 1); + sBuffer.fp = poGDS->fp; + sBuffer.buffer_size = 0; + sBuffer.cur_index = 0; + + for( int i = 0; i < GetXSize(); i++) + { + int /* njunk, */ nCPoints, lygap; + double /* djunk, dxStart, */ dyStart, dfElevOffset; + + /* njunk = */ USGSDEMReadIntFromBuffer(&sBuffer); + /* njunk = */ USGSDEMReadIntFromBuffer(&sBuffer); + nCPoints = USGSDEMReadIntFromBuffer(&sBuffer); + /* njunk = */ USGSDEMReadIntFromBuffer(&sBuffer); + + /* dxStart = */ USGSDEMReadDoubleFromBuffer(&sBuffer, 24); + dyStart = USGSDEMReadDoubleFromBuffer(&sBuffer, 24); + dfElevOffset = USGSDEMReadDoubleFromBuffer(&sBuffer, 24); + /* djunk = */ USGSDEMReadDoubleFromBuffer(&sBuffer, 24); + /* djunk = */ USGSDEMReadDoubleFromBuffer(&sBuffer, 24); + + if( EQUALN(poGDS->pszProjection,"GEOGCS",6) ) + dyStart = dyStart / 3600.0; + + lygap = (int)((dfYMin - dyStart)/poGDS->adfGeoTransform[5]+ 0.5); + + for (int j=lygap; j < (nCPoints+(int)lygap); j++) + { + int iY = GetYSize() - j - 1; + int nElev; + int bSuccess; + + nElev = USGSDEMReadIntFromBuffer(&sBuffer, &bSuccess); + if( !bSuccess ) + { + CPLFree(sBuffer.buffer); + return CE_Failure; + } + + if (iY < 0 || iY >= GetYSize() ) { + /* bad = TRUE; */ + } else if( nElev == USGSDEM_NODATA ) + /* leave in output buffer as nodata */; + else + { + float fComputedElev = + (float)(nElev * poGDS->fVRes + dfElevOffset); + + if( GetRasterDataType() == GDT_Int16 ) + { + ((GInt16 *) pImage)[i + iY*GetXSize()] = + (GInt16) fComputedElev; + } + else + { + ((float *) pImage)[i + iY*GetXSize()] = fComputedElev; + } + } + } + } + CPLFree(sBuffer.buffer); + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double USGSDEMRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return USGSDEM_NODATA; +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ +const char *USGSDEMRasterBand::GetUnitType() +{ + USGSDEMDataset *poGDS = (USGSDEMDataset *) poDS; + + return poGDS->pszUnits; +} + +/************************************************************************/ +/* ==================================================================== */ +/* USGSDEMDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* USGSDEMDataset() */ +/************************************************************************/ + +USGSDEMDataset::USGSDEMDataset() + +{ + fp = NULL; + pszProjection = NULL; +} + +/************************************************************************/ +/* ~USGSDEMDataset() */ +/************************************************************************/ + +USGSDEMDataset::~USGSDEMDataset() + +{ + FlushCache(); + + CPLFree( pszProjection ); + if( fp != NULL ) + VSIFCloseL( fp ); +} + +/************************************************************************/ +/* LoadFromFile() */ +/* */ +/* If the data from DEM is in meters, then values are stored as */ +/* shorts. If DEM data is in feet, then height data will be */ +/* stored in float, to preserve the precision of the original */ +/* data. returns true if the file was successfully opened and */ +/* read. */ +/************************************************************************/ + +int USGSDEMDataset::LoadFromFile(VSILFILE *InDem) +{ + int i, j; + int nRow, nColumn; + int nVUnit, nGUnit; + double dxdelta, dydelta; + /* double dElevMax, dElevMin; */ + int bNewFormat; + int nCoordSystem; + int nProfiles; + char szDateBuffer[5]; + DPoint2 corners[4]; // SW, NW, NE, SE + DPoint2 extent_min, extent_max; + int iUTMZone; + + // check for version of DEM format + VSIFSeekL(InDem, 864, 0); + + // Read DEM into matrix + nRow = ReadInt(InDem); + nColumn = ReadInt(InDem); + bNewFormat = ((nRow!=1)||(nColumn!=1)); + if (bNewFormat) + { + VSIFSeekL(InDem, 1024, 0); // New Format + i = ReadInt(InDem); + j = ReadInt(InDem); + if ((i!=1)||(j!=1 && j != 0)) // File OK? + { + VSIFSeekL(InDem, 893, 0); // Undocumented Format (39109h1.dem) + i = ReadInt(InDem); + j = ReadInt(InDem); + if ((i!=1)||(j!=1)) // File OK? + { + CPLError( CE_Failure, CPLE_AppDefined, + "Does not appear to be a USGS DEM file." ); + return FALSE; + } + else + nDataStartOffset = 893; + } + else + nDataStartOffset = 1024; + } + else + nDataStartOffset = 864; + + VSIFSeekL(InDem, 156, 0); + nCoordSystem = ReadInt(InDem); + iUTMZone = ReadInt(InDem); + + VSIFSeekL(InDem, 528, 0); + nGUnit = ReadInt(InDem); + nVUnit = ReadInt(InDem); + + // Vertical Units in meters + if (nVUnit==1) + pszUnits = "ft"; + else + pszUnits = "m"; + + VSIFSeekL(InDem, 816, 0); + dxdelta = DConvert(InDem, 12); + dydelta = DConvert(InDem, 12); + fVRes = DConvert(InDem, 12); + +/* -------------------------------------------------------------------- */ +/* Should we treat this as floating point, or GInt16. */ +/* -------------------------------------------------------------------- */ + if (nVUnit==1 || fVRes < 1.0) + eNaturalDataFormat = GDT_Float32; + else + eNaturalDataFormat = GDT_Int16; + +/* -------------------------------------------------------------------- */ +/* Read four corner coordinates. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL(InDem, 546, 0); + for (i = 0; i < 4; i++) + { + corners[i].x = DConvert(InDem, 24); + corners[i].y = DConvert(InDem, 24); + } + + // find absolute extents of raw vales + extent_min.x = MIN(corners[0].x, corners[1].x); + extent_max.x = MAX(corners[2].x, corners[3].x); + extent_min.y = MIN(corners[0].y, corners[3].y); + extent_max.y = MAX(corners[1].y, corners[2].y); + + /* dElevMin = */ DConvert(InDem, 48); + /* dElevMax = */ DConvert(InDem, 48); + + VSIFSeekL(InDem, 858, 0); + nProfiles = ReadInt(InDem); + +/* -------------------------------------------------------------------- */ +/* Collect the spatial reference system. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference sr; + int bNAD83 =TRUE; + + // OLD format header ends at byte 864 + if (bNewFormat) + { + char szHorzDatum[3]; + + // year of data compilation + VSIFSeekL(InDem, 876, 0); + VSIFReadL(szDateBuffer, 4, 1, InDem); + szDateBuffer[4] = 0; + + // Horizontal datum + // 1=North American Datum 1927 (NAD 27) + // 2=World Geodetic System 1972 (WGS 72) + // 3=WGS 84 + // 4=NAD 83 + // 5=Old Hawaii Datum + // 6=Puerto Rico Datum + int datum; + VSIFSeekL(InDem, 890, 0); + VSIFReadL( szHorzDatum, 1, 2, InDem ); + szHorzDatum[2] = '\0'; + datum = atoi(szHorzDatum); + switch (datum) + { + case 1: + sr.SetWellKnownGeogCS( "NAD27" ); + bNAD83 = FALSE; + break; + + case 2: + sr.SetWellKnownGeogCS( "WGS72" ); + break; + + case 3: + sr.SetWellKnownGeogCS( "WGS84" ); + break; + + case 4: + sr.SetWellKnownGeogCS( "NAD83" ); + break; + + case -9: + break; + + default: + sr.SetWellKnownGeogCS( "NAD27" ); + break; + } + } + else + { + sr.SetWellKnownGeogCS( "NAD27" ); + bNAD83 = FALSE; + } + + if (nCoordSystem == 1) // UTM + { + sr.SetUTM( iUTMZone, TRUE ); + if( nGUnit == 1 ) + { + sr.SetLinearUnitsAndUpdateParameters( SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV) ); + char szUTMName[128]; + sprintf( szUTMName, "UTM Zone %d, Northern Hemisphere, us-ft", iUTMZone ); + sr.SetNode( "PROJCS", szUTMName ); + } + } + + else if (nCoordSystem == 2) // state plane + { + if( nGUnit == 1 ) + sr.SetStatePlane( iUTMZone, bNAD83, + "Foot", CPLAtof(SRS_UL_US_FOOT_CONV) ); + else + sr.SetStatePlane( iUTMZone, bNAD83 ); + } + + sr.exportToWkt( &pszProjection ); + +/* -------------------------------------------------------------------- */ +/* For UTM we use the extents (really the UTM coordinates of */ +/* the lat/long corners of the quad) to determine the size in */ +/* pixels and lines, but we have to make the anchors be modulus */ +/* the pixel size which what really gets used. */ +/* -------------------------------------------------------------------- */ + if (nCoordSystem == 1 // UTM + || nCoordSystem == 2 // State Plane + || nCoordSystem == -9999 ) // unknown + { + /* int njunk; */ + double dxStart; + + // expand extents modulus the pixel size. + extent_min.y = floor(extent_min.y/dydelta) * dydelta; + extent_max.y = ceil(extent_max.y/dydelta) * dydelta; + + // Forceably compute X extents based on first profile and pixelsize. + VSIFSeekL(InDem, nDataStartOffset, 0); + /* njunk = */ ReadInt(InDem); + /* njunk = */ ReadInt(InDem); + /* njunk = */ ReadInt(InDem); + /* njunk = */ ReadInt(InDem); + dxStart = DConvert(InDem, 24); + + nRasterYSize = (int) ((extent_max.y - extent_min.y)/dydelta + 1.5); + nRasterXSize = nProfiles; + + adfGeoTransform[0] = dxStart - dxdelta/2.0; + adfGeoTransform[1] = dxdelta; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = extent_max.y + dydelta/2.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = -dydelta; + } +/* -------------------------------------------------------------------- */ +/* Geographic -- use corners directly. */ +/* -------------------------------------------------------------------- */ + else + { + nRasterYSize = (int) ((extent_max.y - extent_min.y)/dydelta + 1.5); + nRasterXSize = nProfiles; + + // Translate extents from arc-seconds to decimal degrees. + adfGeoTransform[0] = (extent_min.x - dxdelta/2.0) / 3600.0; + adfGeoTransform[1] = dxdelta / 3600.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = (extent_max.y + dydelta/2.0) / 3600.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = (-dydelta) / 3600.0; + } + + if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize)) + { + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr USGSDEMDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *USGSDEMDataset::GetProjectionRef() + +{ + return pszProjection; +} + + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int USGSDEMDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 200 ) + return FALSE; + + if( !EQUALN((const char *) poOpenInfo->pabyHeader+156, " 0",6) + && !EQUALN((const char *) poOpenInfo->pabyHeader+156, " 1",6) + && !EQUALN((const char *) poOpenInfo->pabyHeader+156, " 2",6) + && !EQUALN((const char *) poOpenInfo->pabyHeader+156, " 3",6) + && !EQUALN((const char *) poOpenInfo->pabyHeader+156, " -9999",6) ) + return FALSE; + + if( !EQUALN((const char *) poOpenInfo->pabyHeader+150, " 1",6) + && !EQUALN((const char *) poOpenInfo->pabyHeader+150, " 4",6)) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *USGSDEMDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) ) + return NULL; + + VSILFILE* fp = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + if (fp == NULL) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + USGSDEMDataset *poDS; + + poDS = new USGSDEMDataset(); + + poDS->fp = fp; + +/* -------------------------------------------------------------------- */ +/* Read the file. */ +/* -------------------------------------------------------------------- */ + if( !poDS->LoadFromFile( poDS->fp ) ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + delete poDS; + CPLError( CE_Failure, CPLE_NotSupported, + "The USGSDEM driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->SetBand( 1, new USGSDEMRasterBand( poDS )); + + poDS->SetMetadataItem( GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Open overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return( poDS ); +} + +/************************************************************************/ +/* GDALRegister_USGSDEM() */ +/************************************************************************/ + +void GDALRegister_USGSDEM() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "USGSDEM" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "USGSDEM" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, + "dem" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "USGS Optional ASCII DEM (and CDED)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_usgsdem.html" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Int16" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" " +" " ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = USGSDEMDataset::Open; + poDriver->pfnCreateCopy = USGSDEMCreateCopy; + poDriver->pfnIdentify = USGSDEMDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/vrt/GNUmakefile b/bazaar/plugin/gdal/frmts/vrt/GNUmakefile new file mode 100644 index 000000000..95722bfd0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/GNUmakefile @@ -0,0 +1,22 @@ + + +include ../../GDALmake.opt + +OBJ = vrtdataset.o vrtrasterband.o vrtdriver.o vrtsources.o \ + vrtfilters.o vrtsourcedrasterband.o vrtrawrasterband.o \ + vrtwarped.o vrtderivedrasterband.o + +CPPFLAGS := -I../raw $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +$(OBJ) $(O_OBJ): vrtdataset.h ../../alg/gdalwarper.h ../raw/rawdataset.h ../../gcore/gdal_proxy.h + +install: + $(INSTALL_DATA) vrtdataset.h $(DESTDIR)$(INST_INCLUDE) + $(INSTALL_DATA) gdal_vrt.h $(DESTDIR)$(INST_INCLUDE) diff --git a/bazaar/plugin/gdal/frmts/vrt/gdal_vrt.h b/bazaar/plugin/gdal/frmts/vrt/gdal_vrt.h new file mode 100644 index 000000000..2ae9162d3 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/gdal_vrt.h @@ -0,0 +1,105 @@ +/****************************************************************************** + * $Id: gdal_vrt.h 12156 2007-09-14 14:05:58Z dron $ + * + * Project: Virtual GDAL Datasets + * Purpose: C/Public declarations of virtual GDAL dataset objects. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2007, Andrey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_VRT_H_INCLUDED +#define GDAL_VRT_H_INCLUDED + +/** + * \file gdal_vrt.h + * + * Public (C callable) entry points for virtual GDAL dataset objects. + */ + +#include "gdal.h" +#include "cpl_port.h" +#include "cpl_error.h" +#include "cpl_minixml.h" + +#define VRT_NODATA_UNSET -1234.56 + +CPL_C_START + +void GDALRegister_VRT(void); +typedef CPLErr +(*VRTImageReadFunc)( void *hCBData, + int nXOff, int nYOff, int nXSize, int nYSize, + void *pData ); + +/* -------------------------------------------------------------------- */ +/* Define handle types related to various VRT dataset classes. */ +/* -------------------------------------------------------------------- */ +typedef void *VRTDriverH; +typedef void *VRTSourceH; +typedef void *VRTSimpleSourceH; +typedef void *VRTAveragedSourceH; +typedef void *VRTComplexSourceH; +typedef void *VRTFilteredSourceH; +typedef void *VRTKernelFilteredSourceH; +typedef void *VRTAverageFilteredSourceH; +typedef void *VRTFuncSourceH; +typedef void *VRTDatasetH; +typedef void *VRTWarpedDatasetH; +typedef void *VRTRasterBandH; +typedef void *VRTSourcedRasterBandH; +typedef void *VRTWarpedRasterBandH; +typedef void *VRTDerivedRasterBandH; +typedef void *VRTRawRasterBandH; + +/* ==================================================================== */ +/* VRTDataset class. */ +/* ==================================================================== */ + +VRTDatasetH CPL_DLL CPL_STDCALL VRTCreate( int, int ); +void CPL_DLL CPL_STDCALL VRTFlushCache( VRTDatasetH ); +CPLXMLNode CPL_DLL * CPL_STDCALL VRTSerializeToXML( VRTDatasetH, const char * ); +int CPL_DLL CPL_STDCALL VRTAddBand( VRTDatasetH, GDALDataType, char ** ); + +/* ==================================================================== */ +/* VRTSourcedRasterBand class. */ +/* ==================================================================== */ + +CPLErr CPL_STDCALL VRTAddSource( VRTSourcedRasterBandH, VRTSourceH ); +CPLErr CPL_DLL CPL_STDCALL VRTAddSimpleSource( VRTSourcedRasterBandH, + GDALRasterBandH, + int, int, int, int, + int, int, int, int, + const char *, double ); +CPLErr CPL_DLL CPL_STDCALL VRTAddComplexSource( VRTSourcedRasterBandH, + GDALRasterBandH, + int, int, int, int, + int, int, int, int, + double, double, double ); +CPLErr CPL_DLL CPL_STDCALL VRTAddFuncSource( VRTSourcedRasterBandH, + VRTImageReadFunc, + void *, double ); + +CPL_C_END + +#endif /* GDAL_VRT_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/frmts/vrt/makefile.vc b/bazaar/plugin/gdal/frmts/vrt/makefile.vc new file mode 100644 index 000000000..e84f5d51e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/makefile.vc @@ -0,0 +1,17 @@ + +OBJ = vrtdataset.obj vrtrasterband.obj vrtdriver.obj \ + vrtsources.obj vrtfilters.obj vrtsourcedrasterband.obj \ + vrtrawrasterband.obj vrtderivedrasterband.obj vrtwarped.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I..\raw + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/vrt/vrt_tutorial.dox b/bazaar/plugin/gdal/frmts/vrt/vrt_tutorial.dox new file mode 100644 index 000000000..6b0d220c5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/vrt_tutorial.dox @@ -0,0 +1,902 @@ +#ifndef DOXYGEN_SKIP +/* $Id: vrt_tutorial.dox 29342 2015-06-14 18:00:24Z rouault $ */ +#endif /* DOXYGEN_SKIP */ + +/*! + +\page gdal_vrttut GDAL Virtual Format Tutorial + +\section gdal_vrttut_intro Introduction + +The VRT driver is a format driver for GDAL that allows a virtual GDAL dataset +to be composed from other GDAL datasets with repositioning, and algorithms +potentially applied as well as various kinds of metadata altered or added. +VRT descriptions of datasets can be saved in an XML format normally given the +extension .vrt.

    + +An example of a simple .vrt file referring to a 512x512 dataset with one band +loaded from utm.tif might look like this: + +\code + + 440720.0, 60.0, 0.0, 3751320.0, 0.0, -60.0 + + Gray + + utm.tif + 1 + + + + + +\endcode + +Many aspects of the VRT file are a direct XML encoding of the +GDAL Data Model which should be reviewed +for understanding of the semantics of various elements.

    + +VRT files can be produced by translating to VRT format. The resulting file can +then be edited to modify mappings, add metadata or other purposes. VRT files +can also be produced programmatically by various means.

    + +This tutorial will cover the .vrt file format (suitable for users editing +.vrt files), and how .vrt files may be created and manipulated programmatically +for developers.

    + +\section gdal_vrttut_format .vrt Format + +A XML schema of the GDAL VRT format +is available.

    + +Virtual files stored on disk are kept in an XML format with the following +elements.

    + +VRTDataset: This is the root element for the whole GDAL dataset. +It must have the attributes rasterXSize and rasterYSize describing the width +and height of the dataset in pixels. It may have SRS, GeoTransform, +GCPList, Metadata, MaskBand and VRTRasterBand subelements. + +\code + +\endcode + +The allowed subelements for VRTDataset are : + +

      + +
    • SRS: This element contains the spatial reference system (coordinate +system) in OGC WKT format. Note that this must be appropriately escaped for +XML, so items like quotes will have the ampersand escape sequences substituted. +As as well WKT, and valid input to the SetFromUserInput() method (such as well +known GEOGCS names, and PROJ.4 format) is also allowed in the SRS element. + +\code + PROJCS["NAD27 / UTM zone 11N",GEOGCS["NAD27",DATUM["North_American_Datum_1927",SPHEROID["Clarke 1866",6378206.4,294.9786982139006,AUTHORITY["EPSG","7008"]],AUTHORITY["EPSG","6267"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4267"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-117],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AUTHORITY["EPSG","26711"]] +\endcode +
    • + +
    • GeoTransform: This element contains a six value affine +geotransformation for the dataset, mapping between pixel/line coordinates +and georeferenced coordinates.

      + +\code + 440720.0, 60, 0.0, 3751320.0, 0.0, -60.0 +\endcode +

    • + +
    • GCPList: This element contains a list of Ground Control Points for +the dataset, mapping between pixel/line coordinates and georeferenced +coordinates. The Projection attribute should contain the SRS of the +georeferenced coordinates in the same format as the SRS element.

      + +\code + + + + +\endcode +

    • + +
    • Metadata: This element contains a list of metadata name/value +pairs associated with the VRTDataset as a whole, or a VRTRasterBand. It has +<MDI> (metadata item) subelements which have a "key" attribute and the value +as the data of the element. The Metadata element can be repeated multiple times, +in which case it must be accompanied with a "domain" attribute to indicate the +name of the metadata domain. + +\code + + Metadata value + +\endcode +
    • + +
    • MaskBand: (GDAL >= 1.8.0) This element represents a mask band that is +shared between all bands on the dataset (see GMF_PER_DATASET in RFC 15). +It must contain a single VRTRasterBand child element, that is the description of +the mask band itself. + +\code + + + + utm.tif + mask,1 + + + + + +\endcode + +
    • + +
    • VRTRasterBand: This represents one band of a dataset. It will +have a dataType attribute with the type of the pixel data associated with +this band (use names Byte, UInt16, Int16, UInt32, Int32, Float32, Float64, +CInt16, CInt32, CFloat32 or CFloat64) and the band this element represents +(1 based). This element may have Metadata, ColorInterp, NoDataValue, HideNoDataValue, +ColorTable, Description and MaskBand subelements as well as the various kinds of +source elements such as SimpleSource, ComplexSource, etc. A raster band may have many "sources" +indicating where the actual raster data should be fetched from, and how it +should be mapped into the raster bands pixel space.

      + +The allowed subelements for VRTRasterBand are : + +

        + +
      • ColorInterp: The data of this element should be the name of +a color interpretation type. One of Gray, Palette, Red, Green, Blue, Alpha, +Hue, Saturation, Lightness, Cyan, Magenta, Yellow, Black, or Unknown.

        + +\code + Gray: +\endcode +

      • + +
      • NoDataValue: If this element exists a raster band has a +nodata value associated with, of the value given as data in the element. + +\code + -100.0 +\endcode +
      • + +
      • HideNoDataValue: If this value is 1, the nodata value will not be +reported. Essentially, the caller will not be aware of a nodata pixel when it +reads one. Any datasets copied/translated from this will not have a nodata +value. This is useful when you want to specify a fixed background value for +the dataset. The background will be the value specified by the NoDataValue +element. + +Default value is 0 when this element is absent. + +\code + 1 +\endcode +
      • + +
      • ColorTable: This element is parent to a set of Entry +elements defining the entries in a color table. Currently only RGBA +color tables are supported with c1 being red, c2 being green, c3 being +blue and c4 being alpha. The entries are ordered and will be assumed +to start from color table entry 0. + +\code + + + + +\endcode +
      • + +
      • Description: This element contains the optional description +of a raster band as it's text value. + +\code + Crop Classification Layer +\endcode +
      • + +
      • UnitType: This optional element contains the vertical units for +elevation band data. One of "m" for meters or "ft" for feet. Default +assumption is meters.

        + +\code + ft +\endcode +

      • + +
      • Offset: This optional element contains the offset that should be +applied when computing "real" pixel values from scaled pixel values on +a raster band. The default is 0.0.

        + +\code + 0.0 +\endcode +

      • + +
      • Scale: This optional element contains the scale that should be +applied when computing "real" pixel values from scaled pixel values on a +raster band. The default is 1.0.

        + +\code + 0.0 +\endcode +

      • + +
      • Overview: This optional element describes one overview level for +the band. It should have a child SourceFilename and SourceBand element. The +SourceFilename may have a relativeToVRT boolean attribute. Multiple elements +may be used to describe multiple overviews.

        + +\code + + yellowstone_2.1.ntf.r2 + 1 + +\endcode +

      • + +
      • CategoryNames: This optional element contains a list of Category +subelements with the names of the categories for classified raster band.

        + +\code + + Missing + Non-Crop + Wheat + Corn + Soybeans + +\endcode +

      • + +
      • SimpleSource: The SimpleSource indicates that raster data +should be read from a separate dataset, indicating the dataset, and band to be +read from, and how the data should map into this bands raster space. +The SimpleSource may have the SourceFilename, SourceBand, SrcRect, and DstRect +subelements. The SrcRect element will indicate what rectangle on the indicated +source file should be read, and the DstRect element indicates how that +rectangle of source data should be mapped into the VRTRasterBands space. + +The relativeToVRT attribute on the SourceFilename indicates whether the +filename should be interpreted as relative to the .vrt file (value is 1) +or not relative to the .vrt file (value is 0). The default is 0. + +The shared attribute, added in GDAL 2.0.0, on the SourceFilename indicates whether the +dataset should be shared (value is 1) or not (value is 0). The default is 1. +If several VRT datasets refering to the same underlying sources are used in a multithreaded context, +shared should be set to 0. Alternatively, the VRT_SHARED_SOURCE configuration +option can be set to 0 to force non-shared mode. + +Some characteristics of the source band can be specified in the optional +SourceProperties tag to enable the VRT driver to differ the opening of the source +dataset until it really needs to read data from it. This is particularly useful +when building VRTs with a big number of source datasets. The needed parameters are the +raster dimensions, the size of the blocks and the data type. If the SourceProperties +tag is not present, the source dataset will be opened at the same time as the VRT itself. + +Starting with GDAL 1.8.0, the content of the SourceBand subelement can refer to +a mask band. For example mask,1 means the the mask band of the first band of the source. + +\code + + utm.tif + 1 + + + + +\endcode + +Starting with GDAL 2.0, a OpenOptions subelement can be added to specify +the open options to apply when opening the source dataset. It has <OOI> (open option item) +subelements which have a "key" attribute and the value as the data of the element. + +\code + + utm.tif + + 0 + + 1 + + + + +\endcode + +Starting with GDAL 2.0, a resampling attribute can be specified on a SimpleSource +or ComplexSource element to specified the resampling algorithm used when the +size of the destination rectangle is not the same as the size of the source +rectangle. The values allowed for that attribute are : nearest,bilinear,cubic, +cubicspline,lanczos,average,mode. + +\code + + utm.tif + 1 + + + + +\endcode + +
      • + +
      • AveragedSource: The AveragedSource is derived from the +SimpleSource and shares the same properties except that it uses an averaging +resampling instead of a nearest neighbour algorithm as in SimpleSource, when +the size of the destination rectangle is not the same as the size of the source +rectangle. Note: starting with GDAL 2.0, a more general mechanism to specify +resampling algorithms can be used. See above paragraph about the 'resampling' attribute. + +
      • ComplexSource: The ComplexSource is derived from the +SimpleSource (so it shares the SourceFilename, SourceBand, SrcRect and +DestRect elements), but it provides support to rescale and offset +the range of the source values. Certain regions of the source +can be masked by specifying the NODATA value. + +Starting with GDAL 1.11, alternatively to linear scaling, non-linear +scaling using a power function can be used by specifying the Exponent, +SrcMin, SrcMax, DstMin and DstMax elements. If SrcMin and SrcMax are +not specified, they are computed from the source minimum and maximum +value (which might require analyzing the whole source dataset). Exponent +must be positive. (Those 5 values can be set with the -exponent and -scale +options of gdal_translate.) + +The ComplexSource supports adding a custom lookup table to transform +the source values to the destination. The LUT can be specified using +the following form: + +\code + [src value 1]:[dest value 1],[src value 2]:[dest value 2],... +\endcode + +The intermediary values are calculated using a linear interpolation +between the bounding destination values of the corresponding range. + +The ComplexSource supports fetching a color component from a source raster +band that has a color table. The ColorTableComponent value is the index of the +color component to extract : 1 for the red band, 2 for the green band, 3 for +the blue band or 4 for the alpha band. + +When transforming the source values the operations are executed +in the following order: + +
          +
        1. Nodata masking
        2. +
        3. Color table expansion
        4. +
        5. For linear scaling, applying the scale ratio, then scale offset
        6. +
        7. For non-linear scaling, apply (DstMax-DstMin) * pow( (SrcValue-SrcMin) / (SrcMax-SrcMin), Exponent) + DstMin
        8. +
        9. Table lookup
        10. +
        + +\code + + utm.tif + 1 + 0 + 1 + 1 + 0:0,2345.12:64,56789.5:128,2364753.02:255 + 0 + + + +\endcode + +Non-linear scaling: + +\code + + 16bit.tif + 1 + 0.75 + 0 + 65535 + 0 + 255 + + + +\endcode + +
      • + +
      • KernelFilteredSource: This is a pixel source derived from the +Simple Source (so it shares the SourceFilename, SourceBand, SrcRect and +DestRect elements, but it also passes the data through a simple filtering +kernel specified with the Kernel element. The Kernel element should have +two child elements, Size and Coefs and optionally the boolean attribute +normalized (defaults to false=0). The size must always be an odd number, +and the Coefs must have Size * Size entries separated by spaces. + +\code + + /debian/home/warmerda/openev/utm.tif + 1 + + 3 + 0.11111111 0.11111111 0.11111111 0.11111111 0.11111111 0.11111111 0.11111111 0.11111111 0.11111111 + + +\endcode +
      • + +
      • MaskBand: (GDAL >= 1.8.0) This element represents a mask band that is +specific to the VRTRasterBand it contains. It must contain a single VRTRasterBand +child element, that is the description of the mask band itself. +
      • + +
      + +
    + +\section gdal_vrttut_vrt .vrt Descriptions for Raw Files + +So far we have described how to derive new virtual datasets from existing +files supports by GDAL. However, it is also common to need to utilize +raw binary raster files for which the regular layout of the data is known +but for which no format specific driver exists. This can be accomplished +by writing a .vrt file describing the raw file. + +For example, the following .vrt describes a raw raster file containing +floating point complex pixels in a file called l2p3hhsso.img. The +image data starts from the first byte (ImageOffset=0). The byte offset +between pixels is 8 (PixelOffset=8), the size of a CFloat32. The byte offset +from the start of one line to the start of the next is 9376 bytes +(LineOffset=9376) which is the width (1172) times the size of a pixel (8). + +\code + + + l2p3hhsso.img + 0 + 8 + 9376 + MSB + + +\endcode + +Some things to note are that the VRTRasterBand has a subClass specifier +of "VRTRawRasterBand". Also, the VRTRawRasterBand contains a number of +previously unseen elements but no "source" information. VRTRawRasterBands +may never have sources (ie. SimpleSource), but should contain the following +elements in addition to all the normal "metadata" elements previously +described which are still supported. + +
      + +
    • SourceFilename: The name of the raw file containing the +data for this band. The relativeToVRT attribute can be used to indicate +if the SourceFilename is relative to the .vrt file (1) or not (0). + +
    • ImageOffset: The offset in bytes to the beginning of the first +pixel of data of this image band. Defaults to zero. + +
    • PixelOffset: The offset in bytes from the beginning of one +pixel and the next on the same line. In packed single band data this will +be the size of the dataType in bytes. + +
    • LineOffset: The offset in bytes from the beginning of one +scanline of data and the next scanline of data. In packed single band +data this will be PixelOffset * rasterXSize. + +
    • ByteOrder: Defines the byte order of the data on disk. +Either LSB (Least Significant Byte first) such as the natural byte order +on Intel x86 systems or MSB (Most Significant Byte first) such as the natural +byte order on Motorola or Sparc systems. Defaults to being the local machine +order. + +
    + +A few other notes: + +
      + +
    • The image data on disk is assumed to be of the same data type as +the band dataType of the VRTRawRasterBand. + +
    • All the non-source attributes of the VRTRasterBand are supported, +including color tables, metadata, nodata values, and color interpretation. + +
    • The VRTRawRasterBand supports in place update of the raster, whereas +the source based VRTRasterBand is always read-only. + +
    • The OpenEV tool includes a File menu option to input parameters +describing a raw raster file in a GUI and create the corresponding .vrt +file. + +
    • Multiple bands in the one .vrt file can come from the same raw file. +Just ensure that the ImageOffset, PixelOffset, and LineOffset definition +for each band is appropriate for the pixels of that particular band. + +
    + +Another example, in this case a 400x300 RGB pixel interleaved image. + +\code + + + Red + rgb.raw + 0 + 3 + 1200 + + + Green + rgb.raw + 1 + 3 + 1200 + + + Blue + rgb.raw + 2 + 3 + 1200 + + +\endcode + +\section gdal_vrttut_creation Programatic Creation of VRT Datasets + +The VRT driver supports several methods of creating VRT datasets. As of +GDAL 1.2.0 the vrtdataset.h include file should be installed with the core +GDAL include files, allowing direct access to the VRT classes. However, +even without that most capabilities remain available through standard GDAL +interfaces.

    + +To create a VRT dataset that is a clone of an existing dataset use the +CreateCopy() method. For example to clone utm.tif into a wrk.vrt file in +C++ the following could be used: + +\code + GDALDriver *poDriver = (GDALDriver *) GDALGetDriverByName( "VRT" ); + GDALDataset *poSrcDS, *poVRTDS; + + poSrcDS = (GDALDataset *) GDALOpenShared( "utm.tif", GA_ReadOnly ); + + poVRTDS = poDriver->CreateCopy( "wrk.vrt", poSrcDS, FALSE, NULL, NULL, NULL ); + + GDALClose((GDALDatasetH) poVRTDS); + GDALClose((GDALDatasetH) poSrcDS); +\endcode + +Note the use of GDALOpenShared() when opening the source dataset. It is advised +to use GDALOpenShared() in this situation so that you are able to release +the explicit reference to it before closing the VRT dataset itself. In other +words, in the previous example, you could also invert the 2 last lines, whereas +if you open the source dataset with GDALOpen(), you'd need to close the VRT dataset +before closing the source dataset. + +To create a virtual copy of a dataset with some attributes added or changed +such as metadata or coordinate system that are often hard to change on other +formats, you might do the following. In this case, the virtual dataset is +created "in memory" only by virtual of creating it with an empty filename, and +then used as a modified source to pass to a CreateCopy() written out in TIFF +format. + +\code + poVRTDS = poDriver->CreateCopy( "", poSrcDS, FALSE, NULL, NULL, NULL ); + + poVRTDS->SetMetadataItem( "SourceAgency", "United States Geological Survey"); + poVRTDS->SetMetadataItem( "SourceDate", "July 21, 2003" ); + + poVRTDS->GetRasterBand( 1 )->SetNoDataValue( -999.0 ); + + GDALDriver *poTIFFDriver = (GDALDriver *) GDALGetDriverByName( "GTiff" ); + GDALDataset *poTiffDS; + + poTiffDS = poTIFFDriver->CreateCopy( "wrk.tif", poVRTDS, FALSE, NULL, NULL, NULL ); + + GDALClose((GDALDatasetH) poTiffDS); +\endcode + +In the above example the nodata value is set as -999. You can set the +HideNoDataValue element in the VRT dataset's band using SetMetadataItem() on +that band. + +\code + poVRTDS->GetRasterBand( 1 )->SetMetadataItem( "HideNoDataValue" , "1" ); +\endcode + +In this example a virtual dataset is created with the Create() method, and +adding bands and sources programmatically, but still via the "generic" API. +A special attribute of VRT datasets is that sources can be added to the VRTRasterBand +(but not to VRTRawRasterBand) by passing the XML describing the source into SetMetadata() on the special +domain target "new_vrt_sources". The domain target "vrt_sources" may also be +used, in which case any existing sources will be discarded before adding the +new ones. In this example we construct a simple averaging filter source +instead of using the simple source. + +\code + // construct XML for simple 3x3 average filter kernel source. + const char *pszFilterSourceXML = +"" +" utm.tif1" +" " +" 3" +" 0.111 0.111 0.111 0.111 0.111 0.111 0.111 0.111 0.111" +" " +""; + + // Create the virtual dataset. + poVRTDS = poDriver->Create( "", 512, 512, 1, GDT_Byte, NULL ); + poVRTDS->GetRasterBand(1)->SetMetadataItem("source_0",pszFilterSourceXML, + "new_vrt_sources"); +\endcode + +A more general form of this that will produce a 3x3 average filtered clone +of any input datasource might look like the following. In this case we +deliberately set the filtered datasource as in the "vrt_sources" domain +to override the SimpleSource created by the CreateCopy() method. The fact +that we used CreateCopy() ensures that all the other metadata, georeferencing +and so forth is preserved from the source dataset ... the only thing we are +changing is the data source for each band. + +\code + int nBand; + GDALDriver *poDriver = (GDALDriver *) GDALGetDriverByName( "VRT" ); + GDALDataset *poSrcDS, *poVRTDS; + + poSrcDS = (GDALDataset *) GDALOpenShared( pszSourceFilename, GA_ReadOnly ); + + poVRTDS = poDriver->CreateCopy( "", poSrcDS, FALSE, NULL, NULL, NULL ); + + for( nBand = 1; nBand <= poVRTDS->GetRasterCount(); nBand++ ) + { + char szFilterSourceXML[10000]; + + GDALRasterBand *poBand = poVRTDS->GetRasterBand( nBand ); + + sprintf( szFilterSourceXML, + "" + " %s%d" + " " + " 3" + " 0.111 0.111 0.111 0.111 0.111 0.111 0.111 0.111 0.111" + " " + "", + pszSourceFilename, nBand ); + + poBand->SetMetadataItem( "source_0", szFilterSourceXML, "vrt_sources" ); + } +\endcode + +The VRTDataset class is one of the few dataset implementations that supports the AddBand() +method. The options passed to the AddBand() method can be used to control the type of the +band created (VRTRasterBand, VRTRawRasterBand, VRTDerivedRasterBand), and in the case of +the VRTRawRasterBand to set its various parameters. For standard VRTRasterBand, sources +should be specified with the above SetMetadata() / SetMetadataItem() examples. + +\code + GDALDriver *poDriver = (GDALDriver *) GDALGetDriverByName( "VRT" ); + GDALDataset *poVRTDS; + + poVRTDS = poDriver->Create( "out.vrt", 512, 512, 0, GDT_Byte, NULL ); + char** papszOptions = NULL; + papszOptions = CSLAddNameValue(papszOptions, "subclass", "VRTRawRasterBand"); // if not specified, default to VRTRasterBand + papszOptions = CSLAddNameValue(papszOptions, "SourceFilename", "src.tif"); // mandatory + papszOptions = CSLAddNameValue(papszOptions, "ImageOffset", "156"); // optionnal. default = 0 + papszOptions = CSLAddNameValue(papszOptions, "PixelOffset", "2"); // optionnal. default = size of band type + papszOptions = CSLAddNameValue(papszOptions, "LineOffset", "1024"); // optionnal. default = size of band type * width + papszOptions = CSLAddNameValue(papszOptions, "ByteOrder", "LSB"); // optionnal. default = machine order + papszOptions = CSLAddNameValue(papszOptions, "relativeToVRT", "true"); // optionnal. default = false + poVRTDS->AddBand(GDT_Byte, papszOptions); + CSLDestroy(papszOptions); + + delete poVRTDS; +\endcode + +

    Using Derived Bands

    + +A specialized type of band is a 'derived' band which derives its pixel +information from its source bands. With this type of band you must also +specify a pixel function, which has the responsibility of generating the +output raster. Pixel functions are created by an application and then +registered with GDAL using a unique key. + +Using derived bands you can create VRT datasets that manipulate bands on +the fly without having to create new band files on disk. For example, you +might want to generate a band using four source bands from a nine band input +dataset (x0, x3, x4, and x8): + +\code + band_value = sqrt((x3*x3+x4*x4)/(x0*x8)); +\endcode + +You could write the pixel function to compute this value and then register +it with GDAL with the name "MyFirstFunction". Then, the following VRT XML +could be used to display this derived band: + +\code + + + Magnitude + MyFirstFunction + + nine_band.dat + 1 + + + + + nine_band.dat + 4 + + + + + nine_band.dat + 5 + + + + + nine_band.dat + 9 + + + + + +\endcode + +In addition to the subclass specification (VRTDerivedRasterBand) and +the PixelFunctionType value, there is another new parameter that can come +in handy: SourceTransferType. Typically the source rasters are obtained +using the data type of the derived band. There might be times, +however, when you want the pixel function to have access to +higher resolution source data than the data type being generated. +For example, you might have a derived band of type "Float", which takes +a single source of type "CFloat32" or "CFloat64", and returns the imaginary +portion. To accomplish this, set the SourceTransferType to "CFloat64". +Otherwise the source would be converted to "Float" prior to +calling the pixel function, and the imaginary portion would be lost. + +\code + + + Magnitude + MyFirstFunction + CFloat64 + ... +\endcode + +

    Writing Pixel Functions

    + +To register this function with GDAL (prior to accessing any VRT datasets +with derived bands that use this function), an application calls +GDALAddDerivedBandPixelFunc with a key and a GDALDerivedPixelFunc: + +\code + GDALAddDerivedBandPixelFunc("MyFirstFunction", TestFunction); +\endcode + +A good time to do this is at the beginning of an application when the +GDAL drivers are registered. + +GDALDerivedPixelFunc is defined with a signature similar to IRasterIO: + +@param papoSources A pointer to packed rasters; one per source. The +datatype of all will be the same, specified in the eSrcType parameter. + +@param nSources The number of source rasters. + +@param pData The buffer into which the data should be read, or from which +it should be written. This buffer must contain at least nBufXSize * +nBufYSize words of type eBufType. It is organized in left to right, +top to bottom pixel order. Spacing is controlled by the nPixelSpace, +and nLineSpace parameters. + +@param nBufXSize The width of the buffer image into which the desired +region is to be read, or from which it is to be written. + +@param nBufYSize The height of the buffer image into which the desired +region is to be read, or from which it is to be written. + +@param eSrcType The type of the pixel values in the papoSources raster +array. + +@param eBufType The type of the pixel values that the pixel function must +generate in the pData data buffer. + +@param nPixelSpace The byte offset from the start of one pixel value in +pData to the start of the next pixel value within a scanline. If +defaulted (0) the size of the datatype eBufType is used. + +@param nLineSpace The byte offset from the start of one scanline in +pData to the start of the next. + +@return CE_Failure on failure, otherwise CE_None. + +\code +typedef CPLErr +(*GDALDerivedPixelFunc)(void **papoSources, int nSources, void *pData, + int nXSize, int nYSize, + GDALDataType eSrcType, GDALDataType eBufType, + int nPixelSpace, int nLineSpace); +\endcode + +The following is an implementation of the pixel function: + +\code +#include "gdal.h" + +CPLErr TestFunction(void **papoSources, int nSources, void *pData, + int nXSize, int nYSize, + GDALDataType eSrcType, GDALDataType eBufType, + int nPixelSpace, int nLineSpace) +{ + int ii, iLine, iCol; + double pix_val; + double x0, x3, x4, x8; + + // ---- Init ---- + if (nSources != 4) return CE_Failure; + + // ---- Set pixels ---- + for( iLine = 0; iLine < nYSize; iLine++ ) + { + for( iCol = 0; iCol < nXSize; iCol++ ) + { + ii = iLine * nXSize + iCol; + /* Source raster pixels may be obtained with SRCVAL macro */ + x0 = SRCVAL(papoSources[0], eSrcType, ii); + x3 = SRCVAL(papoSources[1], eSrcType, ii); + x4 = SRCVAL(papoSources[2], eSrcType, ii); + x8 = SRCVAL(papoSources[3], eSrcType, ii); + + pix_val = sqrt((x3*x3+x4*x4)/(x0*x8)); + + GDALCopyWords(&pix_val, GDT_Float64, 0, + ((GByte *)pData) + nLineSpace * iLine + iCol * nPixelSpace, + eBufType, nPixelSpace, 1); + } + } + + // ---- Return success ---- + return CE_None; +} +\endcode + + +\section gdal_vrttut_mt Multi-threading issues + +When using VRT datasets in a multi-threading environment, you should be +careful to open the VRT dataset by the thread that will use it afterwards. The +reason for that is that the VRT dataset uses GDALOpenShared when opening the +underlying datasets. So, if you open twice the same VRT dataset by the same +thread, both VRT datasets will share the same handles to the underlying +datasets. + +The shared attribute, added in GDAL 2.0.0, on the SourceFilename indicates whether the +dataset should be shared (value is 1) or not (value is 0). The default is 1. +If several VRT datasets refering to the same underlying sources are used in a multithreaded context, +shared should be set to 0. Alternatively, the VRT_SHARED_SOURCE configuration +option can be set to 0 to force non-shared mode. + +\section gdal_vrttut_perf Performance considerations + +A VRT can reference many (hundreds, thousands, or more) datasets. Due to +operating system limitations, and for performance at opening time, it is +not reasonable/possible to open them all at the same time. GDAL has a "pool" +of datasets opened by VRT files whose maximum limit is 100 by default. When it +needs to access a dataset referenced by a VRT, it checks if it is already in +the pool of open datasets. If not, when the pool has reached its limit, it closes +the least recently used dataset to be able to open the new one. This maximum +limit of the pool can be increased by setting the GDAL_MAX_DATASET_POOL_SIZE + configuration option to a bigger value. Note that a typical user process on +Linux is limited to 1024 simultaneously opened files, and you should let some +margin for shared libraries, etc... +As of GDAL 2.0, gdal_translate and gdalwarp, by default, increase the pool size +to 450. + +*/ diff --git a/bazaar/plugin/gdal/frmts/vrt/vrtdataset.cpp b/bazaar/plugin/gdal/frmts/vrt/vrtdataset.cpp new file mode 100644 index 000000000..29a443173 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/vrtdataset.cpp @@ -0,0 +1,1399 @@ +/****************************************************************************** + * $Id: vrtdataset.cpp 29294 2015-06-05 08:52:15Z rouault $ + * + * Project: Virtual GDAL Datasets + * Purpose: Implementation of VRTDataset + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "vrtdataset.h" +#include "cpl_string.h" +#include "cpl_minixml.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: vrtdataset.cpp 29294 2015-06-05 08:52:15Z rouault $"); + +/************************************************************************/ +/* VRTDataset() */ +/************************************************************************/ + +VRTDataset::VRTDataset( int nXSize, int nYSize ) + +{ + nRasterXSize = nXSize; + nRasterYSize = nYSize; + pszProjection = NULL; + + bNeedsFlush = FALSE; + bWritable = TRUE; + + bGeoTransformSet = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + nGCPCount = 0; + pasGCPList = NULL; + pszGCPProjection = NULL; + + pszVRTPath = NULL; + + poMaskBand = NULL; + + GDALRegister_VRT(); + poDriver = (GDALDriver *) GDALGetDriverByName( "VRT" ); + + bCompatibleForDatasetIO = -1; +} + +/************************************************************************/ +/* VRTCreate() */ +/************************************************************************/ + +/** + * @see VRTDataset::VRTDataset() + */ + +VRTDatasetH CPL_STDCALL VRTCreate(int nXSize, int nYSize) + +{ + return ( new VRTDataset(nXSize, nYSize) ); +} + +/************************************************************************/ +/* ~VRTDataset() */ +/************************************************************************/ + +VRTDataset::~VRTDataset() + +{ + FlushCache(); + CPLFree( pszProjection ); + + CPLFree( pszGCPProjection ); + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + CPLFree( pszVRTPath ); + + delete poMaskBand; +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void VRTDataset::FlushCache() + +{ + GDALDataset::FlushCache(); + + if( !bNeedsFlush || bWritable == FALSE) + return; + + bNeedsFlush = FALSE; + + // We don't write to disk if there is no filename. This is a + // memory only dataset. + if( strlen(GetDescription()) == 0 + || EQUALN(GetDescription(),"FlushCache(); +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *VRTDataset::SerializeToXML( const char *pszVRTPath ) + +{ + /* -------------------------------------------------------------------- */ + /* Setup root node and attributes. */ + /* -------------------------------------------------------------------- */ + CPLXMLNode *psDSTree = NULL; + CPLXMLNode *psMD = NULL; + char szNumber[128]; + + psDSTree = CPLCreateXMLNode( NULL, CXT_Element, "VRTDataset" ); + + sprintf( szNumber, "%d", GetRasterXSize() ); + CPLSetXMLValue( psDSTree, "#rasterXSize", szNumber ); + + sprintf( szNumber, "%d", GetRasterYSize() ); + CPLSetXMLValue( psDSTree, "#rasterYSize", szNumber ); + + /* -------------------------------------------------------------------- */ + /* SRS */ + /* -------------------------------------------------------------------- */ + if( pszProjection != NULL && strlen(pszProjection) > 0 ) + CPLSetXMLValue( psDSTree, "SRS", pszProjection ); + + /* -------------------------------------------------------------------- */ + /* Geotransform. */ + /* -------------------------------------------------------------------- */ + if( bGeoTransformSet ) + { + CPLSetXMLValue( psDSTree, "GeoTransform", + CPLSPrintf( "%24.16e,%24.16e,%24.16e,%24.16e,%24.16e,%24.16e", + adfGeoTransform[0], + adfGeoTransform[1], + adfGeoTransform[2], + adfGeoTransform[3], + adfGeoTransform[4], + adfGeoTransform[5] ) ); + } + +/* -------------------------------------------------------------------- */ +/* Metadata */ +/* -------------------------------------------------------------------- */ + psMD = oMDMD.Serialize(); + if( psMD != NULL ) + { + CPLAddXMLChild( psDSTree, psMD ); + } + + /* -------------------------------------------------------------------- */ + /* GCPs */ + /* -------------------------------------------------------------------- */ + if( nGCPCount > 0 ) + { + GDALSerializeGCPListToXML( psDSTree, + pasGCPList, + nGCPCount, + pszGCPProjection ); + } + + /* -------------------------------------------------------------------- */ + /* Serialize bands. */ + /* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < nBands; iBand++ ) + { + CPLXMLNode *psBandTree = + ((VRTRasterBand *) papoBands[iBand])->SerializeToXML(pszVRTPath); + + if( psBandTree != NULL ) + CPLAddXMLChild( psDSTree, psBandTree ); + } + + /* -------------------------------------------------------------------- */ + /* Serialize dataset mask band. */ + /* -------------------------------------------------------------------- */ + if (poMaskBand) + { + CPLXMLNode *psBandTree = + poMaskBand->SerializeToXML(pszVRTPath); + + if( psBandTree != NULL ) + { + CPLXMLNode *psMaskBandElement = CPLCreateXMLNode( psDSTree, CXT_Element, + "MaskBand" ); + CPLAddXMLChild( psMaskBandElement, psBandTree ); + } + } + + return psDSTree; +} + +/************************************************************************/ +/* VRTSerializeToXML() */ +/************************************************************************/ + +/** + * @see VRTDataset::SerializeToXML() + */ + +CPLXMLNode * CPL_STDCALL VRTSerializeToXML( VRTDatasetH hDataset, + const char *pszVRTPath ) +{ + VALIDATE_POINTER1( hDataset, "VRTSerializeToXML", NULL ); + + return ((VRTDataset *)hDataset)->SerializeToXML(pszVRTPath); +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr VRTDataset::XMLInit( CPLXMLNode *psTree, const char *pszVRTPath ) + +{ + if( pszVRTPath != NULL ) + this->pszVRTPath = CPLStrdup(pszVRTPath); + +/* -------------------------------------------------------------------- */ +/* Check for an SRS node. */ +/* -------------------------------------------------------------------- */ + if( strlen(CPLGetXMLValue(psTree, "SRS", "")) > 0 ) + { + OGRSpatialReference oSRS; + + CPLFree( pszProjection ); + pszProjection = NULL; + + if( oSRS.SetFromUserInput( CPLGetXMLValue(psTree, "SRS", "") ) + == OGRERR_NONE ) + oSRS.exportToWkt( &pszProjection ); + } + +/* -------------------------------------------------------------------- */ +/* Check for a GeoTransform node. */ +/* -------------------------------------------------------------------- */ + if( strlen(CPLGetXMLValue(psTree, "GeoTransform", "")) > 0 ) + { + const char *pszGT = CPLGetXMLValue(psTree, "GeoTransform", ""); + char **papszTokens; + + papszTokens = CSLTokenizeStringComplex( pszGT, ",", FALSE, FALSE ); + if( CSLCount(papszTokens) != 6 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "GeoTransform node does not have expected six values."); + } + else + { + for( int iTA = 0; iTA < 6; iTA++ ) + adfGeoTransform[iTA] = CPLAtof(papszTokens[iTA]); + bGeoTransformSet = TRUE; + } + + CSLDestroy( papszTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Check for GCPs. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psGCPList = CPLGetXMLNode( psTree, "GCPList" ); + + if( psGCPList != NULL ) + { + GDALDeserializeGCPListFromXML( psGCPList, + &pasGCPList, + &nGCPCount, + &pszGCPProjection ); + } + +/* -------------------------------------------------------------------- */ +/* Apply any dataset level metadata. */ +/* -------------------------------------------------------------------- */ + oMDMD.XMLInit( psTree, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Create dataset mask band. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psChild; + + /* Parse dataset mask band first */ + CPLXMLNode* psMaskBandNode = CPLGetXMLNode(psTree, "MaskBand"); + if (psMaskBandNode) + psChild = psMaskBandNode->psChild; + else + psChild = NULL; + for( ; psChild != NULL; psChild=psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(psChild->pszValue,"VRTRasterBand") ) + { + VRTRasterBand *poBand = NULL; + const char *pszSubclass = CPLGetXMLValue( psChild, "subclass", + "VRTSourcedRasterBand" ); + + if( EQUAL(pszSubclass,"VRTSourcedRasterBand") ) + poBand = new VRTSourcedRasterBand( this, 0 ); + else if( EQUAL(pszSubclass, "VRTDerivedRasterBand") ) + poBand = new VRTDerivedRasterBand( this, 0 ); + else if( EQUAL(pszSubclass, "VRTRawRasterBand") ) + poBand = new VRTRawRasterBand( this, 0 ); + else if( EQUAL(pszSubclass, "VRTWarpedRasterBand") ) + poBand = new VRTWarpedRasterBand( this, 0 ); + else + CPLError( CE_Failure, CPLE_AppDefined, + "VRTRasterBand of unrecognised subclass '%s'.", + pszSubclass ); + + if( poBand != NULL + && poBand->XMLInit( psChild, pszVRTPath ) == CE_None ) + { + SetMaskBand(poBand); + break; + } + else + { + if( poBand ) + delete poBand; + return CE_Failure; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int nBands = 0; + for( psChild=psTree->psChild; psChild != NULL; psChild=psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(psChild->pszValue,"VRTRasterBand") ) + { + VRTRasterBand *poBand = NULL; + const char *pszSubclass = CPLGetXMLValue( psChild, "subclass", + "VRTSourcedRasterBand" ); + + if( EQUAL(pszSubclass,"VRTSourcedRasterBand") ) + poBand = new VRTSourcedRasterBand( this, nBands+1 ); + else if( EQUAL(pszSubclass, "VRTDerivedRasterBand") ) + poBand = new VRTDerivedRasterBand( this, nBands+1 ); + else if( EQUAL(pszSubclass, "VRTRawRasterBand") ) + poBand = new VRTRawRasterBand( this, nBands+1 ); + else if( EQUAL(pszSubclass, "VRTWarpedRasterBand") ) + poBand = new VRTWarpedRasterBand( this, nBands+1 ); + else + CPLError( CE_Failure, CPLE_AppDefined, + "VRTRasterBand of unrecognised subclass '%s'.", + pszSubclass ); + + if( poBand != NULL + && poBand->XMLInit( psChild, pszVRTPath ) == CE_None ) + { + SetBand( ++nBands, poBand ); + } + else + { + if( poBand ) + delete poBand; + return CE_Failure; + } + } + } + + return CE_None; +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int VRTDataset::GetGCPCount() + +{ + return nGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *VRTDataset::GetGCPProjection() + +{ + if( pszGCPProjection == NULL ) + return ""; + else + return pszGCPProjection; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *VRTDataset::GetGCPs() + +{ + return pasGCPList; +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +CPLErr VRTDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ) + +{ + CPLFree( this->pszGCPProjection ); + if( this->nGCPCount > 0 ) + { + GDALDeinitGCPs( this->nGCPCount, this->pasGCPList ); + CPLFree( this->pasGCPList ); + } + + this->pszGCPProjection = CPLStrdup(pszGCPProjection); + + this->nGCPCount = nGCPCount; + + this->pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPList ); + + this->bNeedsFlush = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr VRTDataset::SetProjection( const char *pszWKT ) + +{ + CPLFree( pszProjection ); + pszProjection = NULL; + + if( pszWKT != NULL ) + pszProjection = CPLStrdup(pszWKT); + + bNeedsFlush = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *VRTDataset::GetProjectionRef() + +{ + if( pszProjection == NULL ) + return ""; + else + return pszProjection; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr VRTDataset::SetGeoTransform( double *padfGeoTransformIn ) + +{ + memcpy( adfGeoTransform, padfGeoTransformIn, sizeof(double) * 6 ); + bGeoTransformSet = TRUE; + + bNeedsFlush = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr VRTDataset::GetGeoTransform( double * padfGeoTransform ) + +{ + memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 ); + + if( bGeoTransformSet ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr VRTDataset::SetMetadata( char **papszMetadata, + const char *pszDomain ) + +{ + SetNeedsFlush(); + + return GDALDataset::SetMetadata( papszMetadata, pszDomain ); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr VRTDataset::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ + SetNeedsFlush(); + + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int VRTDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes > 20 + && strstr((const char *)poOpenInfo->pabyHeader,"pszFilename,"fpL; + if( fp != NULL ) + { + unsigned int nLength; + + poOpenInfo->fpL = NULL; + + VSIFSeekL( fp, 0, SEEK_END ); + nLength = (int) VSIFTellL( fp ); + VSIFSeekL( fp, 0, SEEK_SET ); + + pszXML = (char *) VSIMalloc(nLength+1); + + if( pszXML == NULL ) + { + VSIFCloseL(fp); + CPLError( CE_Failure, CPLE_OutOfMemory, + "Failed to allocate %d byte buffer to hold VRT xml file.", + nLength ); + return NULL; + } + + if( VSIFReadL( pszXML, 1, nLength, fp ) != nLength ) + { + VSIFCloseL(fp); + CPLFree( pszXML ); + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read %d bytes from VRT xml file.", + nLength ); + return NULL; + } + + pszXML[nLength] = '\0'; + + char* pszCurDir = CPLGetCurrentDir(); + const char *currentVrtFilename = CPLProjectRelativeFilename(pszCurDir, poOpenInfo->pszFilename); + CPLFree(pszCurDir); +#if defined(HAVE_READLINK) && defined(HAVE_LSTAT) + VSIStatBuf statBuffer; + char filenameBuffer[2048]; + + while( true ) { + int lstatCode = lstat( currentVrtFilename, &statBuffer ); + if ( lstatCode == -1 ) { + if (errno == ENOENT) { + // The file could be a virtual file, let later checks handle it. + break; + } else { + VSIFCloseL(fp); + CPLFree( pszXML ); + CPLError( CE_Failure, CPLE_FileIO, + "Failed to lstat %s: %s", + currentVrtFilename, + VSIStrerror(errno) ); + return NULL; + } + } + + if ( !VSI_ISLNK(statBuffer.st_mode) ) { + break; + } + + int bufferSize = readlink(currentVrtFilename, filenameBuffer, sizeof(filenameBuffer)); + if (bufferSize != -1) { + filenameBuffer[MIN(bufferSize, (int) sizeof(filenameBuffer) - 1)] = 0; + // The filename in filenameBuffer might be a relative path from the linkfile resolve it before looping + currentVrtFilename = CPLProjectRelativeFilename(CPLGetDirname(currentVrtFilename), filenameBuffer); + } else { + VSIFCloseL(fp); + CPLFree( pszXML ); + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read filename from symlink %s: %s", + currentVrtFilename, + VSIStrerror(errno) ); + return NULL; + } + } +#endif + + pszVRTPath = CPLStrdup(CPLGetPath(currentVrtFilename)); + + VSIFCloseL(fp); + } +/* -------------------------------------------------------------------- */ +/* Or use the filename as the XML input. */ +/* -------------------------------------------------------------------- */ + else + { + pszXML = CPLStrdup( poOpenInfo->pszFilename ); + } + + if( CSLFetchNameValue(poOpenInfo->papszOpenOptions, "ROOT_PATH") != NULL ) + { + CPLFree(pszVRTPath); + pszVRTPath = CPLStrdup(CSLFetchNameValue(poOpenInfo->papszOpenOptions, "ROOT_PATH")); + } + +/* -------------------------------------------------------------------- */ +/* Turn the XML representation into a VRTDataset. */ +/* -------------------------------------------------------------------- */ + VRTDataset *poDS = (VRTDataset *) OpenXML( pszXML, pszVRTPath, poOpenInfo->eAccess ); + + if( poDS != NULL ) + poDS->bNeedsFlush = FALSE; + + CPLFree( pszXML ); + CPLFree( pszVRTPath ); + +/* -------------------------------------------------------------------- */ +/* Open overviews. */ +/* -------------------------------------------------------------------- */ + if( fp != NULL && poDS != NULL ) + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, + poOpenInfo->GetSiblingFiles() ); + + return poDS; +} + +/************************************************************************/ +/* OpenXML() */ +/* */ +/* Create an open VRTDataset from a supplied XML representation */ +/* of the dataset. */ +/************************************************************************/ + +GDALDataset *VRTDataset::OpenXML( const char *pszXML, const char *pszVRTPath, + GDALAccess eAccess) + +{ + /* -------------------------------------------------------------------- */ + /* Parse the XML. */ + /* -------------------------------------------------------------------- */ + CPLXMLNode *psTree; + + psTree = CPLParseXMLString( pszXML ); + + if( psTree == NULL ) + return NULL; + + CPLXMLNode *psRoot = CPLGetXMLNode( psTree, "=VRTDataset" ); + if (psRoot == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing VRTDataset element." ); + CPLDestroyXMLNode( psTree ); + return NULL; + } + + if( CPLGetXMLNode( psRoot, "rasterXSize" ) == NULL + || CPLGetXMLNode( psRoot, "rasterYSize" ) == NULL + || CPLGetXMLNode( psRoot, "VRTRasterBand" ) == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing one of rasterXSize, rasterYSize or bands on" + " VRTDataset." ); + CPLDestroyXMLNode( psTree ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the new virtual dataset object. */ +/* -------------------------------------------------------------------- */ + VRTDataset *poDS; + int nXSize = atoi(CPLGetXMLValue(psRoot,"rasterXSize","0")); + int nYSize = atoi(CPLGetXMLValue(psRoot,"rasterYSize","0")); + + if ( !GDALCheckDatasetDimensions(nXSize, nYSize) ) + { + CPLDestroyXMLNode( psTree ); + return NULL; + } + + if( strstr(pszXML,"VRTWarpedDataset") != NULL ) + poDS = new VRTWarpedDataset( nXSize, nYSize ); + else + { + poDS = new VRTDataset( nXSize, nYSize ); + poDS->eAccess = eAccess; + } + + if( poDS->XMLInit( psRoot, pszVRTPath ) != CE_None ) + { + delete poDS; + poDS = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to return a regular handle on the file. */ +/* -------------------------------------------------------------------- */ + CPLDestroyXMLNode( psTree ); + + return poDS; +} + +/************************************************************************/ +/* AddBand() */ +/************************************************************************/ + +CPLErr VRTDataset::AddBand( GDALDataType eType, char **papszOptions ) + +{ + int i; + + const char *pszSubClass = CSLFetchNameValue(papszOptions, "subclass"); + + bNeedsFlush = 1; + +/* ==================================================================== */ +/* Handle a new raw band. */ +/* ==================================================================== */ + if( pszSubClass != NULL && EQUAL(pszSubClass,"VRTRawRasterBand") ) + { + int nWordDataSize = GDALGetDataTypeSize( eType ) / 8; + vsi_l_offset nImageOffset = 0; + int nPixelOffset = nWordDataSize; + int nLineOffset = nWordDataSize * GetRasterXSize(); + const char *pszFilename; + const char *pszByteOrder = NULL; + int bRelativeToVRT = FALSE; + +/* -------------------------------------------------------------------- */ +/* Collect required information. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue(papszOptions, "ImageOffset") != NULL ) + nImageOffset = CPLScanUIntBig( + CSLFetchNameValue(papszOptions, "ImageOffset"), 20); + + if( CSLFetchNameValue(papszOptions, "PixelOffset") != NULL ) + nPixelOffset = atoi(CSLFetchNameValue(papszOptions,"PixelOffset")); + + if( CSLFetchNameValue(papszOptions, "LineOffset") != NULL ) + nLineOffset = atoi(CSLFetchNameValue(papszOptions, "LineOffset")); + + if( CSLFetchNameValue(papszOptions, "ByteOrder") != NULL ) + pszByteOrder = CSLFetchNameValue(papszOptions, "ByteOrder"); + + if( CSLFetchNameValue(papszOptions, "SourceFilename") != NULL ) + pszFilename = CSLFetchNameValue(papszOptions, "SourceFilename"); + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "AddBand() requires a SourceFilename option for VRTRawRasterBands." ); + return CE_Failure; + } + + bRelativeToVRT = + CSLFetchBoolean( papszOptions, "relativeToVRT", FALSE ); + +/* -------------------------------------------------------------------- */ +/* Create and initialize the band. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + VRTRawRasterBand *poBand = + new VRTRawRasterBand( this, GetRasterCount() + 1, eType ); + + eErr = + poBand->SetRawLink( pszFilename, NULL, bRelativeToVRT, + nImageOffset, nPixelOffset, nLineOffset, + pszByteOrder ); + if( eErr != CE_None ) + { + delete poBand; + return eErr; + } + + SetBand( GetRasterCount() + 1, poBand ); + + return CE_None; + } + +/* ==================================================================== */ +/* Handle a new "sourced" band. */ +/* ==================================================================== */ + else + { + VRTSourcedRasterBand *poBand; + + /* ---- Check for our sourced band 'derived' subclass ---- */ + if(pszSubClass != NULL && EQUAL(pszSubClass,"VRTDerivedRasterBand")) { + + /* We'll need a pointer to the subclass in case we need */ + /* to set the new band's pixel function below. */ + VRTDerivedRasterBand* poDerivedBand; + + poDerivedBand = new VRTDerivedRasterBand + (this, GetRasterCount() + 1, eType, + GetRasterXSize(), GetRasterYSize()); + + /* Set the pixel function options it provided. */ + const char* pszFuncName = + CSLFetchNameValue(papszOptions, "PixelFunctionType"); + if (pszFuncName != NULL) + poDerivedBand->SetPixelFunctionName(pszFuncName); + + const char* pszTransferTypeName = + CSLFetchNameValue(papszOptions, "SourceTransferType"); + if (pszTransferTypeName != NULL) { + GDALDataType eTransferType = + GDALGetDataTypeByName(pszTransferTypeName); + if (eTransferType == GDT_Unknown) { + CPLError( CE_Failure, CPLE_AppDefined, + "invalid SourceTransferType: \"%s\".", + pszTransferTypeName); + delete poDerivedBand; + return CE_Failure; + } + poDerivedBand->SetSourceTransferType(eTransferType); + } + + /* We're done with the derived band specific stuff, so */ + /* we can assigned the base class pointer now. */ + poBand = poDerivedBand; + } + else { + + /* ---- Standard sourced band ---- */ + poBand = new VRTSourcedRasterBand + (this, GetRasterCount() + 1, eType, + GetRasterXSize(), GetRasterYSize()); + } + + SetBand( GetRasterCount() + 1, poBand ); + + for( i=0; papszOptions != NULL && papszOptions[i] != NULL; i++ ) + { + if( EQUALN(papszOptions[i],"AddFuncSource=", 14) ) + { + VRTImageReadFunc pfnReadFunc = NULL; + void *pCBData = NULL; + double dfNoDataValue = VRT_NODATA_UNSET; + + char **papszTokens = CSLTokenizeStringComplex( papszOptions[i]+14, + ",", TRUE, FALSE ); + + if( CSLCount(papszTokens) < 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "AddFuncSource() ... required argument missing." ); + } + + sscanf( papszTokens[0], "%p", &pfnReadFunc ); + if( CSLCount(papszTokens) > 1 ) + sscanf( papszTokens[1], "%p", &pCBData ); + if( CSLCount(papszTokens) > 2 ) + dfNoDataValue = CPLAtof( papszTokens[2] ); + + poBand->AddFuncSource( pfnReadFunc, pCBData, dfNoDataValue ); + } + } + + return CE_None; + } +} + +/************************************************************************/ +/* VRTAddBand() */ +/************************************************************************/ + +/** + * @see VRTDataset::VRTAddBand(). + */ + +int CPL_STDCALL VRTAddBand( VRTDatasetH hDataset, GDALDataType eType, + char **papszOptions ) + +{ + VALIDATE_POINTER1( hDataset, "VRTAddBand", 0 ); + + return ((VRTDataset *) hDataset)->AddBand(eType, papszOptions); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset * +VRTDataset::Create( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ) + +{ + VRTDataset *poDS = NULL; + int iBand = 0; + + (void) papszOptions; + + if( EQUALN(pszName,"SetDescription( "" ); + return poDS; + } + else + { + const char *pszSubclass = CSLFetchNameValue( papszOptions, + "SUBCLASS" ); + + if( pszSubclass == NULL || EQUAL(pszSubclass,"VRTDataset") ) + poDS = new VRTDataset( nXSize, nYSize ); + else if( EQUAL(pszSubclass,"VRTWarpedDataset") ) + { + poDS = new VRTWarpedDataset( nXSize, nYSize ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "SUBCLASS=%s not recognised.", + pszSubclass ); + return NULL; + } + poDS->eAccess = GA_Update; + + poDS->SetDescription( pszName ); + + for( iBand = 0; iBand < nBands; iBand++ ) + poDS->AddBand( eType, NULL ); + + poDS->bNeedsFlush = 1; + + poDS->oOvManager.Initialize( poDS, pszName ); + + return poDS; + } +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char** VRTDataset::GetFileList() +{ + char** papszFileList = GDALDataset::GetFileList(); + + int nSize = CSLCount(papszFileList); + int nMaxSize = nSize; + + /* Don't need an element desallocator as each string points to an */ + /* element of the papszFileList */ + CPLHashSet* hSetFiles = CPLHashSetNew(CPLHashSetHashStr, + CPLHashSetEqualStr, + NULL); + + for( int iBand = 0; iBand < nBands; iBand++ ) + { + ((VRTRasterBand *) papoBands[iBand])->GetFileList( + &papszFileList, &nSize, &nMaxSize, hSetFiles); + } + + CPLHashSetDestroy(hSetFiles); + + return papszFileList; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +/* We implement Delete() to avoid that the default implementation */ +/* in GDALDriver::Delete() destroys the source files listed by GetFileList(),*/ +/* which would be an undesired effect... */ +CPLErr VRTDataset::Delete( const char * pszFilename ) +{ + GDALDriverH hDriver = GDALIdentifyDriver(pszFilename, NULL); + if (hDriver && EQUAL(GDALGetDriverShortName(hDriver), "VRT")) + { + if( strstr(pszFilename, "poMaskBand; + this->poMaskBand = poMaskBand; + poMaskBand->SetIsMaskBand(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int VRTDataset::CloseDependentDatasets() +{ + /* We need to call it before removing the sources, otherwise */ + /* we would remove them from the serizalized VRT */ + FlushCache(); + + int bHasDroppedRef = GDALDataset::CloseDependentDatasets(); + + for( int iBand = 0; iBand < nBands; iBand++ ) + { + bHasDroppedRef |= ((VRTRasterBand *) papoBands[iBand])-> + CloseDependentDatasets(); + } + return bHasDroppedRef; +} + +/************************************************************************/ +/* CheckCompatibleForDatasetIO() */ +/************************************************************************/ + +/* We will return TRUE only if all the bands are VRTSourcedRasterBands */ +/* made of identical sources, that are strictly VRTSimpleSource, and that */ +/* the band number of each source is the band number of the VRTSouredRasterBand */ + +int VRTDataset::CheckCompatibleForDatasetIO() +{ + int iBand; + int nSources = 0; + VRTSource **papoSources = NULL; + CPLString osResampling; + for(iBand = 0; iBand < nBands; iBand++) + { + if (!((VRTRasterBand *) papoBands[iBand])->IsSourcedRasterBand()) + return FALSE; + + VRTSourcedRasterBand* poBand = (VRTSourcedRasterBand* )papoBands[iBand]; + + /* If there are overviews, let's VRTSourcedRasterBand::IRasterIO() */ + /* do the job */ + if (poBand->GetOverviewCount() != 0) + return FALSE; + + if (iBand == 0) + { + nSources = poBand->nSources; + papoSources = poBand->papoSources; + for(int iSource = 0; iSource < nSources; iSource++) + { + if (!papoSources[iSource]->IsSimpleSource()) + return FALSE; + + VRTSimpleSource* poSource = (VRTSimpleSource* )papoSources[iSource]; + if (!EQUAL(poSource->GetType(), "SimpleSource")) + return FALSE; + + GDALRasterBand *srcband = poSource->GetBand(); + if (srcband == NULL) + return FALSE; + if (srcband->GetDataset() == NULL) + return FALSE; + if (srcband->GetDataset()->GetRasterCount() <= iBand) + return FALSE; + if (srcband->GetDataset()->GetRasterBand(iBand + 1) != srcband) + return FALSE; + osResampling = poSource->GetResampling(); + } + } + else if (nSources != poBand->nSources) + { + return FALSE; + } + else + { + for(int iSource = 0; iSource < nSources; iSource++) + { + VRTSimpleSource* poRefSource = (VRTSimpleSource* )papoSources[iSource]; + VRTSimpleSource* poSource = (VRTSimpleSource* )poBand->papoSources[iSource]; + if (!EQUAL(poSource->GetType(), "SimpleSource")) + return FALSE; + if (!poSource->IsSameExceptBandNumber(poRefSource)) + return FALSE; + + GDALRasterBand *srcband = poSource->GetBand(); + if (srcband == NULL) + return FALSE; + if (srcband->GetDataset() == NULL) + return FALSE; + if (srcband->GetDataset()->GetRasterCount() <= iBand) + return FALSE; + if (srcband->GetDataset()->GetRasterBand(iBand + 1) != srcband) + return FALSE; + if (osResampling.compare(poSource->GetResampling()) != 0) + return FALSE; + } + } + } + + return nSources != 0; +} + +/************************************************************************/ +/* GetSingleSimpleSource() */ +/* */ +/* Returns a non-NULL dataset if the VRT is made of a single source */ +/* that is a simple source, in its full extent, and with all of its */ +/* bands. Basically something produced by : */ +/* gdal_translate src dst.vrt -of VRT (-a_srs / -a_ullr) */ +/************************************************************************/ + +GDALDataset* VRTDataset::GetSingleSimpleSource() +{ + if (!CheckCompatibleForDatasetIO()) + return NULL; + + VRTSourcedRasterBand* poVRTBand = (VRTSourcedRasterBand* )papoBands[0]; + VRTSimpleSource* poSource = (VRTSimpleSource* )poVRTBand->papoSources[0]; + GDALRasterBand* poBand = poSource->GetBand(); + if (poBand == NULL) + return NULL; + GDALDataset* poSrcDS = poBand->GetDataset(); + if (poSrcDS == NULL) + return NULL; + + /* Check that it uses the full source dataset */ + double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize; + int nReqXOff, nReqYOff, nReqXSize, nReqYSize; + int nOutXOff, nOutYOff, nOutXSize, nOutYSize; + poSource->GetSrcDstWindow( 0, 0, + poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(), + poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(), + &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, + &nReqXOff, &nReqYOff, + &nReqXSize, &nReqYSize, + &nOutXOff, &nOutYOff, + &nOutXSize, &nOutYSize ); + + if (nReqXOff != 0 || nReqYOff != 0 || + nReqXSize != poSrcDS->GetRasterXSize() || + nReqYSize != poSrcDS->GetRasterYSize()) + return NULL; + + if (nOutXOff != 0 || nOutYOff != 0 || + nOutXSize != poSrcDS->GetRasterXSize() || + nOutYSize != poSrcDS->GetRasterYSize()) + return NULL; + + return poSrcDS; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr VRTDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) +{ + if (bCompatibleForDatasetIO < 0) + { + bCompatibleForDatasetIO = CheckCompatibleForDatasetIO(); + } + if (bCompatibleForDatasetIO && eRWFlag == GF_Read) + { + for(int iBandIndex=0; iBandIndexnSources; + poBand->nSources = 0; + + GByte *pabyBandData = ((GByte *) pData) + iBandIndex * nBandSpace; + poBand->IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, + pabyBandData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace, psExtraArg); + + poBand->nSources = nSavedSources; + } + + CPLErr eErr = CE_None; + + GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress; + void *pProgressDataGlobal = psExtraArg->pProgressData; + + /* Use the last band, because when sources reference a GDALProxyDataset, they */ + /* don't necessary instanciate all underlying rasterbands */ + VRTSourcedRasterBand* poBand = (VRTSourcedRasterBand* )papoBands[nBands - 1]; + for(int iSource = 0; eErr == CE_None && iSource < poBand->nSources; iSource++) + { + psExtraArg->pfnProgress = GDALScaledProgress; + psExtraArg->pProgressData = + GDALCreateScaledProgress( 1.0 * iSource / poBand->nSources, + 1.0 * (iSource + 1) / poBand->nSources, + pfnProgressGlobal, + pProgressDataGlobal ); + + VRTSimpleSource* poSource = (VRTSimpleSource* )poBand->papoSources[iSource]; + eErr = poSource->DatasetRasterIO( nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + psExtraArg); + + GDALDestroyScaledProgress( psExtraArg->pProgressData ); + } + + psExtraArg->pfnProgress = pfnProgressGlobal; + psExtraArg->pProgressData = pProgressDataGlobal; + + return eErr; + } + + return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg); +} + +/************************************************************************/ +/* UnsetPreservedRelativeFilenames() */ +/************************************************************************/ + +void VRTDataset::UnsetPreservedRelativeFilenames() +{ + for(int iBand = 0; iBand < nBands; iBand++) + { + if (!((VRTRasterBand *) papoBands[iBand])->IsSourcedRasterBand()) + continue; + + VRTSourcedRasterBand* poBand = (VRTSourcedRasterBand* )papoBands[iBand]; + int nSources = poBand->nSources; + VRTSource** papoSources = poBand->papoSources; + for(int iSource = 0; iSource < nSources; iSource++) + { + if (!papoSources[iSource]->IsSimpleSource()) + continue; + + VRTSimpleSource* poSource = (VRTSimpleSource* )papoSources[iSource]; + poSource->UnsetPreservedRelativeFilenames(); + } + } +} diff --git a/bazaar/plugin/gdal/frmts/vrt/vrtdataset.h b/bazaar/plugin/gdal/frmts/vrt/vrtdataset.h new file mode 100644 index 000000000..cf45befb7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/vrtdataset.h @@ -0,0 +1,904 @@ +/****************************************************************************** + * $Id: vrtdataset.h 29294 2015-06-05 08:52:15Z rouault $ + * + * Project: Virtual GDAL Datasets + * Purpose: Declaration of virtual gdal dataset classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef VIRTUALDATASET_H_INCLUDED +#define VIRTUALDATASET_H_INCLUDED + +#include "gdal_priv.h" +#include "gdal_pam.h" +#include "gdal_vrt.h" +#include "cpl_hash_set.h" + +int VRTApplyMetadata( CPLXMLNode *, GDALMajorObject * ); +CPLXMLNode *VRTSerializeMetadata( GDALMajorObject * ); + +#if 0 +int VRTWarpedOverviewTransform( void *pTransformArg, int bDstToSrc, + int nPointCount, + double *padfX, double *padfY, double *padfZ, + int *panSuccess ); +void* VRTDeserializeWarpedOverviewTransformer( CPLXMLNode *psTree ); +#endif + +/************************************************************************/ +/* VRTOverviewInfo() */ +/************************************************************************/ +class VRTOverviewInfo +{ +public: + CPLString osFilename; + int nBand; + GDALRasterBand *poBand; + int bTriedToOpen; + + VRTOverviewInfo() : poBand(NULL), bTriedToOpen(FALSE) {} + ~VRTOverviewInfo() { + if( poBand == NULL ) + /* do nothing */; + else if( poBand->GetDataset()->GetShared() ) + GDALClose( (GDALDatasetH) poBand->GetDataset() ); + else + poBand->GetDataset()->Dereference(); + } +}; + + +/************************************************************************/ +/* VRTSource */ +/************************************************************************/ + +class CPL_DLL VRTSource +{ +public: + virtual ~VRTSource(); + + virtual CPLErr RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) = 0; + + virtual double GetMinimum( int nXSize, int nYSize, int *pbSuccess ) = 0; + virtual double GetMaximum( int nXSize, int nYSize, int *pbSuccess ) = 0; + virtual CPLErr ComputeRasterMinMax( int nXSize, int nYSize, int bApproxOK, double* adfMinMax ) = 0; + virtual CPLErr ComputeStatistics( int nXSize, int nYSize, + int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, void *pProgressData ) = 0; + virtual CPLErr GetHistogram( int nXSize, int nYSize, + double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, void *pProgressData ) = 0; + + virtual CPLErr XMLInit( CPLXMLNode *psTree, const char * ) = 0; + virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath ) = 0; + + virtual void GetFileList(char*** ppapszFileList, int *pnSize, + int *pnMaxSize, CPLHashSet* hSetFiles); + + virtual int IsSimpleSource() { return FALSE; } +}; + +typedef VRTSource *(*VRTSourceParser)(CPLXMLNode *, const char *); + +VRTSource *VRTParseCoreSources( CPLXMLNode *psTree, const char * ); +VRTSource *VRTParseFilterSources( CPLXMLNode *psTree, const char * ); + +/************************************************************************/ +/* VRTDataset */ +/************************************************************************/ + +class VRTRasterBand; + +class CPL_DLL VRTDataset : public GDALDataset +{ + friend class VRTRasterBand; + + char *pszProjection; + + int bGeoTransformSet; + double adfGeoTransform[6]; + + int nGCPCount; + GDAL_GCP *pasGCPList; + char *pszGCPProjection; + + int bNeedsFlush; + int bWritable; + + char *pszVRTPath; + + VRTRasterBand *poMaskBand; + + int bCompatibleForDatasetIO; + int CheckCompatibleForDatasetIO(); + + protected: + virtual int CloseDependentDatasets(); + + public: + VRTDataset(int nXSize, int nYSize); + ~VRTDataset(); + + void SetNeedsFlush() { bNeedsFlush = TRUE; } + virtual void FlushCache(); + + void SetWritable(int bWritable) { this->bWritable = bWritable; } + + virtual CPLErr CreateMaskBand( int nFlags ); + void SetMaskBand(VRTRasterBand* poMaskBand); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual CPLErr SetMetadata( char **papszMD, const char *pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char *pszName, const char *pszValue, + const char *pszDomain = "" ); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + virtual CPLErr AddBand( GDALDataType eType, + char **papszOptions=NULL ); + + virtual char **GetFileList(); + + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath); + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + + /* Used by PDF driver for example */ + GDALDataset* GetSingleSimpleSource(); + + void UnsetPreservedRelativeFilenames(); + + static int Identify( GDALOpenInfo * ); + static GDALDataset *Open( GDALOpenInfo * ); + static GDALDataset *OpenXML( const char *, const char * = NULL, GDALAccess eAccess = GA_ReadOnly ); + static GDALDataset *Create( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ); + static CPLErr Delete( const char * pszFilename ); +}; + +/************************************************************************/ +/* VRTWarpedDataset */ +/************************************************************************/ + +class GDALWarpOperation; +class VRTWarpedRasterBand; + +class CPL_DLL VRTWarpedDataset : public VRTDataset +{ + int nBlockXSize; + int nBlockYSize; + GDALWarpOperation *poWarper; + + int nOverviewCount; + VRTWarpedDataset **papoOverviews; + int nSrcOvrLevel; + + void CreateImplicitOverviews(); + + friend class VRTWarpedRasterBand; + + protected: + virtual int CloseDependentDatasets(); + +public: + VRTWarpedDataset( int nXSize, int nYSize ); + ~VRTWarpedDataset(); + + CPLErr Initialize( /* GDALWarpOptions */ void * ); + + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + + virtual CPLErr SetMetadataItem( const char *pszName, const char *pszValue, + const char *pszDomain = "" ); + + virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath ); + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + + virtual CPLErr AddBand( GDALDataType eType, + char **papszOptions=NULL ); + + virtual char **GetFileList(); + + CPLErr ProcessBlock( int iBlockX, int iBlockY ); + + void GetBlockSize( int *, int * ); +}; + +/************************************************************************/ +/* VRTRasterBand */ +/* */ +/* Provides support for all the various kinds of metadata but */ +/* no raster access. That is handled by derived classes. */ +/************************************************************************/ + +class CPL_DLL VRTRasterBand : public GDALRasterBand +{ + protected: + int bIsMaskBand; + + int bNoDataValueSet; + int bHideNoDataValue; // If set to true, will not report the existance of nodata + double dfNoDataValue; + + GDALColorTable *poColorTable; + + GDALColorInterp eColorInterp; + + char *pszUnitType; + char **papszCategoryNames; + + double dfOffset; + double dfScale; + + CPLXMLNode *psSavedHistograms; + + void Initialize( int nXSize, int nYSize ); + + std::vector apoOverviews; + + VRTRasterBand *poMaskBand; + + public: + + VRTRasterBand(); + virtual ~VRTRasterBand(); + + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + virtual CPLXMLNode * SerializeToXML( const char *pszVRTPath ); + + virtual CPLErr SetNoDataValue( double ); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + + virtual CPLErr SetColorTable( GDALColorTable * ); + virtual GDALColorTable *GetColorTable(); + + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + virtual GDALColorInterp GetColorInterpretation(); + + virtual const char *GetUnitType(); + CPLErr SetUnitType( const char * ); + + virtual char **GetCategoryNames(); + virtual CPLErr SetCategoryNames( char ** ); + + virtual CPLErr SetMetadata( char **papszMD, const char *pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char *pszName, const char *pszValue, + const char *pszDomain = "" ); + + virtual double GetOffset( int *pbSuccess = NULL ); + CPLErr SetOffset( double ); + virtual double GetScale( int *pbSuccess = NULL ); + CPLErr SetScale( double ); + + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); + + virtual CPLErr GetHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc, void *pProgressData ); + + virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig ** ppanHistogram, + int bForce, + GDALProgressFunc, void *pProgressData); + + virtual CPLErr SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram ); + + CPLErr CopyCommonInfoFrom( GDALRasterBand * ); + + virtual void GetFileList(char*** ppapszFileList, int *pnSize, + int *pnMaxSize, CPLHashSet* hSetFiles); + + virtual void SetDescription( const char * ); + + virtual GDALRasterBand *GetMaskBand(); + virtual int GetMaskFlags(); + + virtual CPLErr CreateMaskBand( int nFlags ); + + void SetMaskBand(VRTRasterBand* poMaskBand); + + void SetIsMaskBand(); + + CPLErr UnsetNoDataValue(); + + virtual int CloseDependentDatasets(); + + virtual int IsSourcedRasterBand() { return FALSE; } +}; + +/************************************************************************/ +/* VRTSourcedRasterBand */ +/************************************************************************/ + +class VRTSimpleSource; + +class CPL_DLL VRTSourcedRasterBand : public VRTRasterBand +{ + private: + int nRecursionCounter; + CPLString osLastLocationInfo; + char **papszSourceList; + + void Initialize( int nXSize, int nYSize ); + + int CanUseSourcesMinMaxImplementations(); + + public: + int nSources; + VRTSource **papoSources; + int bEqualAreas; + + VRTSourcedRasterBand( GDALDataset *poDS, int nBand ); + VRTSourcedRasterBand( GDALDataType eType, + int nXSize, int nYSize ); + VRTSourcedRasterBand( GDALDataset *poDS, int nBand, + GDALDataType eType, + int nXSize, int nYSize ); + virtual ~VRTSourcedRasterBand(); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); + + virtual char **GetMetadataDomainList(); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + virtual CPLXMLNode * SerializeToXML( const char *pszVRTPath ); + + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum(int *pbSuccess = NULL ); + virtual CPLErr ComputeRasterMinMax( int bApproxOK, double* adfMinMax ); + virtual CPLErr ComputeStatistics( int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, void *pProgressData ); + virtual CPLErr GetHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, void *pProgressData ); + + CPLErr AddSource( VRTSource * ); + CPLErr AddSimpleSource( GDALRasterBand *poSrcBand, + int nSrcXOff=-1, int nSrcYOff=-1, + int nSrcXSize=-1, int nSrcYSize=-1, + int nDstXOff=-1, int nDstYOff=-1, + int nDstXSize=-1, int nDstYSize=-1, + const char *pszResampling = "near", + double dfNoDataValue = VRT_NODATA_UNSET); + CPLErr AddComplexSource( GDALRasterBand *poSrcBand, + int nSrcXOff=-1, int nSrcYOff=-1, + int nSrcXSize=-1, int nSrcYSize=-1, + int nDstXOff=-1, int nDstYOff=-1, + int nDstXSize=-1, int nDstYSize=-1, + double dfScaleOff=0.0, + double dfScaleRatio=1.0, + double dfNoDataValue = VRT_NODATA_UNSET, + int nColorTableComponent = 0); + + CPLErr AddMaskBandSource( GDALRasterBand *poSrcBand, + int nSrcXOff=-1, int nSrcYOff=-1, + int nSrcXSize=-1, int nSrcYSize=-1, + int nDstXOff=-1, int nDstYOff=-1, + int nDstXSize=-1, int nDstYSize=-1 ); + + CPLErr AddFuncSource( VRTImageReadFunc pfnReadFunc, void *hCBData, + double dfNoDataValue = VRT_NODATA_UNSET ); + + void ConfigureSource(VRTSimpleSource *poSimpleSource, + GDALRasterBand *poSrcBand, + int bAddAsMaskBand, + int nSrcXOff, int nSrcYOff, + int nSrcXSize, int nSrcYSize, + int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize); + + virtual CPLErr IReadBlock( int, int, void * ); + + virtual void GetFileList(char*** ppapszFileList, int *pnSize, + int *pnMaxSize, CPLHashSet* hSetFiles); + + virtual int CloseDependentDatasets(); + + virtual int IsSourcedRasterBand() { return TRUE; } +}; + +/************************************************************************/ +/* VRTWarpedRasterBand */ +/************************************************************************/ + +class CPL_DLL VRTWarpedRasterBand : public VRTRasterBand +{ + public: + VRTWarpedRasterBand( GDALDataset *poDS, int nBand, + GDALDataType eType = GDT_Unknown ); + virtual ~VRTWarpedRasterBand(); + + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + virtual CPLXMLNode * SerializeToXML( const char *pszVRTPath ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); +}; + +/************************************************************************/ +/* VRTDerivedRasterBand */ +/************************************************************************/ + +class CPL_DLL VRTDerivedRasterBand : public VRTSourcedRasterBand +{ + + public: + char *pszFuncName; + GDALDataType eSourceTransferType; + + VRTDerivedRasterBand(GDALDataset *poDS, int nBand); + VRTDerivedRasterBand(GDALDataset *poDS, int nBand, + GDALDataType eType, int nXSize, int nYSize); + virtual ~VRTDerivedRasterBand(); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + static CPLErr AddPixelFunction + (const char *pszFuncName, GDALDerivedPixelFunc pfnPixelFunc); + static GDALDerivedPixelFunc GetPixelFunction(const char *pszFuncName); + + void SetPixelFunctionName(const char *pszFuncName); + void SetSourceTransferType(GDALDataType eDataType); + + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + virtual CPLXMLNode * SerializeToXML( const char *pszVRTPath ); + +}; + +/************************************************************************/ +/* VRTRawRasterBand */ +/************************************************************************/ + +class RawRasterBand; + +class CPL_DLL VRTRawRasterBand : public VRTRasterBand +{ + RawRasterBand *poRawRaster; + + char *pszSourceFilename; + int bRelativeToVRT; + + public: + VRTRawRasterBand( GDALDataset *poDS, int nBand, + GDALDataType eType = GDT_Unknown ); + virtual ~VRTRawRasterBand(); + + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + virtual CPLXMLNode * SerializeToXML( const char *pszVRTPath ); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + + CPLErr SetRawLink( const char *pszFilename, + const char *pszVRTPath, + int bRelativeToVRT, + vsi_l_offset nImageOffset, + int nPixelOffset, int nLineOffset, + const char *pszByteOrder ); + + void ClearRawLink(); + + virtual void GetFileList(char*** ppapszFileList, int *pnSize, + int *pnMaxSize, CPLHashSet* hSetFiles); +}; + +/************************************************************************/ +/* VRTDriver */ +/************************************************************************/ + +class VRTDriver : public GDALDriver +{ + void *pDeserializerData; + + public: + VRTDriver(); + ~VRTDriver(); + + char **papszSourceParsers; + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + + VRTSource *ParseSource( CPLXMLNode *psSrc, const char *pszVRTPath ); + void AddSourceParser( const char *pszElementName, + VRTSourceParser pfnParser ); +}; + +/************************************************************************/ +/* VRTSimpleSource */ +/************************************************************************/ + +class CPL_DLL VRTSimpleSource : public VRTSource +{ +protected: + GDALRasterBand *poRasterBand; + + /* when poRasterBand is a mask band, poMaskBandMainBand is the band */ + /* from which the mask band is taken */ + GDALRasterBand *poMaskBandMainBand; + + int nSrcXOff; + int nSrcYOff; + int nSrcXSize; + int nSrcYSize; + + int nDstXOff; + int nDstYOff; + int nDstXSize; + int nDstYSize; + + int bNoDataSet; + double dfNoDataValue; + CPLString osResampling; + + int bRelativeToVRTOri; + CPLString osSourceFileNameOri; + +public: + VRTSimpleSource(); + virtual ~VRTSimpleSource(); + + virtual CPLErr XMLInit( CPLXMLNode *psTree, const char * ); + virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath ); + + void SetSrcBand( GDALRasterBand * ); + void SetSrcMaskBand( GDALRasterBand * ); + void SetSrcWindow( int, int, int, int ); + void SetDstWindow( int, int, int, int ); + void SetNoDataValue( double dfNoDataValue ); + const CPLString& GetResampling() const { return osResampling; } + void SetResampling( const char* pszResampling ); + + int GetSrcDstWindow( int, int, int, int, int, int, + double *pdfReqXOff, double *pdfReqYOff, + double *pdfReqXSize, double *pdfReqYSize, + int *, int *, int *, int *, + int *, int *, int *, int * ); + + virtual CPLErr RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + virtual double GetMinimum( int nXSize, int nYSize, int *pbSuccess ); + virtual double GetMaximum( int nXSize, int nYSize, int *pbSuccess ); + virtual CPLErr ComputeRasterMinMax( int nXSize, int nYSize, int bApproxOK, double* adfMinMax ); + virtual CPLErr ComputeStatistics( int nXSize, int nYSize, + int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, void *pProgressData ); + virtual CPLErr GetHistogram( int nXSize, int nYSize, + double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, void *pProgressData ); + + void DstToSrc( double dfX, double dfY, + double &dfXOut, double &dfYOut ); + void SrcToDst( double dfX, double dfY, + double &dfXOut, double &dfYOut ); + + virtual void GetFileList(char*** ppapszFileList, int *pnSize, + int *pnMaxSize, CPLHashSet* hSetFiles); + + virtual int IsSimpleSource() { return TRUE; } + virtual const char* GetType() { return "SimpleSource"; } + + GDALRasterBand* GetBand(); + int IsSameExceptBandNumber(VRTSimpleSource* poOtherSource); + CPLErr DatasetRasterIO( + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + void UnsetPreservedRelativeFilenames(); +}; + +/************************************************************************/ +/* VRTAveragedSource */ +/************************************************************************/ + +class VRTAveragedSource : public VRTSimpleSource +{ +public: + VRTAveragedSource(); + virtual CPLErr RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + virtual double GetMinimum( int nXSize, int nYSize, int *pbSuccess ); + virtual double GetMaximum( int nXSize, int nYSize, int *pbSuccess ); + virtual CPLErr ComputeRasterMinMax( int nXSize, int nYSize, int bApproxOK, double* adfMinMax ); + virtual CPLErr ComputeStatistics( int nXSize, int nYSize, + int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, void *pProgressData ); + virtual CPLErr GetHistogram( int nXSize, int nYSize, + double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, void *pProgressData ); + + virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath ); + virtual const char* GetType() { return "AveragedSource"; } +}; + +/************************************************************************/ +/* VRTComplexSource */ +/************************************************************************/ + +typedef enum +{ + VRT_SCALING_NONE, + VRT_SCALING_LINEAR, + VRT_SCALING_EXPONENTIAL, +} VRTComplexSourceScaling; + +class CPL_DLL VRTComplexSource : public VRTSimpleSource +{ +protected: + VRTComplexSourceScaling eScalingType; + double dfScaleOff; /* for linear scaling */ + double dfScaleRatio; /* for linear scaling */ + + /* For non-linear scaling with a power function. */ + int bSrcMinMaxDefined; + double dfSrcMin; + double dfSrcMax; + double dfDstMin; + double dfDstMax; + double dfExponent; + + int nColorTableComponent; + + CPLErr RasterIOInternal( int nReqXOff, int nReqYOff, + int nReqXSize, int nReqYSize, + void *pData, int nOutXSize, int nOutYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + +public: + VRTComplexSource(); + virtual ~VRTComplexSource(); + + virtual CPLErr RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + virtual double GetMinimum( int nXSize, int nYSize, int *pbSuccess ); + virtual double GetMaximum( int nXSize, int nYSize, int *pbSuccess ); + virtual CPLErr ComputeRasterMinMax( int nXSize, int nYSize, int bApproxOK, double* adfMinMax ); + virtual CPLErr ComputeStatistics( int nXSize, int nYSize, + int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, void *pProgressData ); + virtual CPLErr GetHistogram( int nXSize, int nYSize, + double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, void *pProgressData ); + + virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath ); + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + virtual const char* GetType() { return "ComplexSource"; } + + double LookupValue( double dfInput ); + + void SetLinearScaling(double dfOffset, double dfScale); + void SetPowerScaling(double dfExponent, + double dfSrcMin, + double dfSrcMax, + double dfDstMin, + double dfDstMax); + void SetColorTableComponent(int nComponent); + + double *padfLUTInputs; + double *padfLUTOutputs; + int nLUTItemCount; + +}; + +/************************************************************************/ +/* VRTFilteredSource */ +/************************************************************************/ + +class VRTFilteredSource : public VRTComplexSource +{ +private: + int IsTypeSupported( GDALDataType eType ); + +protected: + int nSupportedTypesCount; + GDALDataType aeSupportedTypes[20]; + + int nExtraEdgePixels; + +public: + VRTFilteredSource(); + virtual ~VRTFilteredSource(); + + void SetExtraEdgePixels( int ); + void SetFilteringDataTypesSupported( int, GDALDataType * ); + + virtual CPLErr FilterData( int nXSize, int nYSize, GDALDataType eType, + GByte *pabySrcData, GByte *pabyDstData ) = 0; + + virtual CPLErr RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); +}; + +/************************************************************************/ +/* VRTKernelFilteredSource */ +/************************************************************************/ + +class VRTKernelFilteredSource : public VRTFilteredSource +{ +protected: + int nKernelSize; + + double *padfKernelCoefs; + + int bNormalized; + +public: + VRTKernelFilteredSource(); + virtual ~VRTKernelFilteredSource(); + + virtual CPLErr XMLInit( CPLXMLNode *psTree, const char * ); + virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath ); + + virtual CPLErr FilterData( int nXSize, int nYSize, GDALDataType eType, + GByte *pabySrcData, GByte *pabyDstData ); + + CPLErr SetKernel( int nKernelSize, double *padfCoefs ); + void SetNormalized( int ); +}; + +/************************************************************************/ +/* VRTAverageFilteredSource */ +/************************************************************************/ + +class VRTAverageFilteredSource : public VRTKernelFilteredSource +{ +public: + VRTAverageFilteredSource( int nKernelSize ); + virtual ~VRTAverageFilteredSource(); + + virtual CPLErr XMLInit( CPLXMLNode *psTree, const char * ); + virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath ); +}; + +/************************************************************************/ +/* VRTFuncSource */ +/************************************************************************/ +class VRTFuncSource : public VRTSource +{ +public: + VRTFuncSource(); + virtual ~VRTFuncSource(); + + virtual CPLErr XMLInit( CPLXMLNode *, const char *) { return CE_Failure; } + virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath ); + + virtual CPLErr RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + virtual double GetMinimum( int nXSize, int nYSize, int *pbSuccess ); + virtual double GetMaximum( int nXSize, int nYSize, int *pbSuccess ); + virtual CPLErr ComputeRasterMinMax( int nXSize, int nYSize, int bApproxOK, double* adfMinMax ); + virtual CPLErr ComputeStatistics( int nXSize, int nYSize, + int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, void *pProgressData ); + virtual CPLErr GetHistogram( int nXSize, int nYSize, + double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, void *pProgressData ); + + VRTImageReadFunc pfnReadFunc; + void *pCBData; + GDALDataType eType; + + float fNoDataValue; +}; + +#endif /* ndef VIRTUALDATASET_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/vrt/vrtderivedrasterband.cpp b/bazaar/plugin/gdal/frmts/vrt/vrtderivedrasterband.cpp new file mode 100644 index 000000000..2459b2b4c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/vrtderivedrasterband.cpp @@ -0,0 +1,460 @@ +/****************************************************************************** + * + * Project: Virtual GDAL Datasets + * Purpose: Implementation of a sourced raster band that derives its raster + * by applying an algorithm (GDALDerivedPixelFunc) to the sources. + * Author: Pete Nagy + * + ****************************************************************************** + * Copyright (c) 2005 Vexcel Corp. + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "vrtdataset.h" +#include "cpl_minixml.h" +#include "cpl_string.h" +#include + +static std::map osMapPixelFunction; + +/************************************************************************/ +/* ==================================================================== */ +/* VRTDerivedRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VRTDerivedRasterBand() */ +/************************************************************************/ + +VRTDerivedRasterBand::VRTDerivedRasterBand(GDALDataset *poDS, int nBand) + : VRTSourcedRasterBand( poDS, nBand ) + +{ + this->pszFuncName = NULL; + this->eSourceTransferType = GDT_Unknown; +} + +/************************************************************************/ +/* VRTDerivedRasterBand() */ +/************************************************************************/ + +VRTDerivedRasterBand::VRTDerivedRasterBand(GDALDataset *poDS, int nBand, + GDALDataType eType, + int nXSize, int nYSize) + : VRTSourcedRasterBand(poDS, nBand, eType, nXSize, nYSize) + +{ + this->pszFuncName = NULL; + this->eSourceTransferType = GDT_Unknown; +} + +/************************************************************************/ +/* ~VRTDerivedRasterBand() */ +/************************************************************************/ + +VRTDerivedRasterBand::~VRTDerivedRasterBand() + +{ + if (this->pszFuncName != NULL) { + CPLFree(this->pszFuncName); + this->pszFuncName = NULL; + } +} + +/************************************************************************/ +/* AddPixelFunction() */ +/************************************************************************/ + +/** + * This adds a pixel function to the global list of available pixel + * functions for derived bands. Pixel functions must be registered + * in this way before a derived band tries to access data. + * + * Derived bands are stored with only the name of the pixel function + * that it will apply, and if a pixel function matching the name is not + * found the IRasterIO() call will do nothing. + * + * @param pszFuncName Name used to access pixel function + * @param pfnNewFunction Pixel function associated with name. An + * existing pixel function registered with the same name will be + * replaced with the new one. + * + * @return CE_None, invalid (NULL) parameters are currently ignored. + */ +CPLErr CPL_STDCALL GDALAddDerivedBandPixelFunc +(const char *pszFuncName, GDALDerivedPixelFunc pfnNewFunction) +{ + /* ---- Init ---- */ + if ((pszFuncName == NULL) || (pszFuncName[0] == '\0') || + (pfnNewFunction == NULL)) + { + return CE_None; + } + + osMapPixelFunction[pszFuncName] = pfnNewFunction; + + return CE_None; +} +/** + * This adds a pixel function to the global list of available pixel + * functions for derived bands. + * + * This is the same as the c function GDALAddDerivedBandPixelFunc() + * + * @param pszFuncName Name used to access pixel function + * @param pfnNewFunction Pixel function associated with name. An + * existing pixel function registered with the same name will be + * replaced with the new one. + * + * @return CE_None, invalid (NULL) parameters are currently ignored. + */ +CPLErr VRTDerivedRasterBand::AddPixelFunction +(const char *pszFuncName, GDALDerivedPixelFunc pfnNewFunction) +{ + return GDALAddDerivedBandPixelFunc(pszFuncName, pfnNewFunction); +} + +/************************************************************************/ +/* GetPixelFunction() */ +/************************************************************************/ + +/** + * Get a pixel function previously registered using the global + * AddPixelFunction. + * + * @param pszFuncName The name associated with the pixel function. + * + * @return A derived band pixel function, or NULL if none have been + * registered for pszFuncName. + */ +GDALDerivedPixelFunc VRTDerivedRasterBand::GetPixelFunction +(const char *pszFuncName) +{ + /* ---- Init ---- */ + if ((pszFuncName == NULL) || (pszFuncName[0] == '\0')) + { + return NULL; + } + + std::map::iterator oIter = + osMapPixelFunction.find(pszFuncName); + + if( oIter == osMapPixelFunction.end()) + return NULL; + + return oIter->second; +} + +/************************************************************************/ +/* SetPixelFunctionName() */ +/************************************************************************/ + +/** + * Set the pixel function name to be applied to this derived band. The + * name should match a pixel function registered using AddPixelFunction. + * + * @param pszFuncName Name of pixel function to be applied to this derived + * band. + */ +void VRTDerivedRasterBand::SetPixelFunctionName(const char *pszFuncName) +{ + this->pszFuncName = CPLStrdup( pszFuncName ); +} + +/************************************************************************/ +/* SetSourceTransferType() */ +/************************************************************************/ + +/** + * Set the transfer type to be used to obtain pixel information from + * all of the sources. If unset, the transfer type used will be the + * same as the derived band data type. This makes it possible, for + * example, to pass CFloat32 source pixels to the pixel function, even + * if the pixel function generates a raster for a derived band that + * is of type Byte. + * + * @param eDataType Data type to use to obtain pixel information from + * the sources to be passed to the derived band pixel function. + */ +void VRTDerivedRasterBand::SetSourceTransferType(GDALDataType eDataType) +{ + this->eSourceTransferType = eDataType; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +/** + * Read/write a region of image data for this band. + * + * Each of the sources for this derived band will be read and passed to + * the derived band pixel function. The pixel function is responsible + * for applying whatever algorithm is necessary to generate this band's + * pixels from the sources. + * + * The sources will be read using the transfer type specified for sources + * using SetSourceTransferType(). If no transfer type has been set for + * this derived band, the band's data type will be used as the transfer type. + * + * @see gdalrasterband + * + * @param eRWFlag Either GF_Read to read a region of data, or GT_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param pData The buffer into which the data should be read, or from which + * it should be written. This buffer must contain at least nBufXSize * + * nBufYSize words of type eBufType. It is organized in left to right, + * top to bottom pixel order. Spacing is controlled by the nPixelSpace, + * and nLineSpace parameters. + * + * @param nBufXSize The width of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param nBufYSize The height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType The type of the pixel values in the pData data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nPixelSpace The byte offset from the start of one pixel value in + * pData to the start of the next pixel value within a scanline. If defaulted + * (0) the size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset from the start of one scanline in + * pData to the start of the next. If defaulted the size of the datatype + * eBufType * nBufXSize is used. + * + * @return CE_Failure if the access fails, otherwise CE_None. + */ +CPLErr VRTDerivedRasterBand::IRasterIO(GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, + int nYSize, void * pData, int nBufXSize, + int nBufYSize, GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ + GDALDerivedPixelFunc pfnPixelFunc; + void **pBuffers; + CPLErr eErr = CE_None; + int iSource, ii, typesize, sourcesize; + GDALDataType eSrcType; + + if( eRWFlag == GF_Write ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Writing through VRTSourcedRasterBand is not supported." ); + return CE_Failure; + } + + typesize = GDALGetDataTypeSize(eBufType) / 8; + if (GDALGetDataTypeSize(eBufType) % 8 > 0) typesize++; + eSrcType = this->eSourceTransferType; + if ((eSrcType == GDT_Unknown) || (eSrcType >= GDT_TypeCount)) { + eSrcType = eBufType; + } + sourcesize = GDALGetDataTypeSize(eSrcType) / 8; + +/* -------------------------------------------------------------------- */ +/* Initialize the buffer to some background value. Use the */ +/* nodata value if available. */ +/* -------------------------------------------------------------------- */ + if ( nPixelSpace == typesize && + (!bNoDataValueSet || dfNoDataValue == 0) ) { + memset( pData, 0, nBufXSize * nBufYSize * nPixelSpace ); + } + else if ( !bEqualAreas || bNoDataValueSet ) + { + double dfWriteValue = 0.0; + int iLine; + + if( bNoDataValueSet ) + dfWriteValue = dfNoDataValue; + + for( iLine = 0; iLine < nBufYSize; iLine++ ) + { + GDALCopyWords( &dfWriteValue, GDT_Float64, 0, + ((GByte *)pData) + nLineSpace * iLine, + eBufType, nPixelSpace, nBufXSize ); + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have overviews that would be appropriate to satisfy */ +/* this request? */ +/* -------------------------------------------------------------------- */ + if( (nBufXSize < nXSize || nBufYSize < nYSize) + && GetOverviewCount() > 0 ) + { + if( OverviewRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, psExtraArg ) == CE_None ) + return CE_None; + } + + /* ---- Get pixel function for band ---- */ + pfnPixelFunc = VRTDerivedRasterBand::GetPixelFunction(this->pszFuncName); + if (pfnPixelFunc == NULL) { + CPLError( CE_Failure, CPLE_IllegalArg, + "VRTDerivedRasterBand::IRasterIO:" \ + "Derived band pixel function '%s' not registered.\n", + this->pszFuncName); + return CE_Failure; + } + + /* TODO: It would be nice to use a MallocBlock function for each + individual buffer that would recycle blocks of memory from a + cache by reassigning blocks that are nearly the same size. + A corresponding FreeBlock might only truly free if the total size + of freed blocks gets to be too great of a percentage of the size + of the allocated blocks. */ + + /* ---- Get buffers for each source ---- */ + pBuffers = (void **) CPLMalloc(sizeof(void *) * nSources); + for (iSource = 0; iSource < nSources; iSource++) { + pBuffers[iSource] = (void *) + VSIMalloc(sourcesize * nBufXSize * nBufYSize); + if (pBuffers[iSource] == NULL) + { + for (ii = 0; ii < iSource; ii++) { + VSIFree(pBuffers[iSource]); + } + CPLError( CE_Failure, CPLE_OutOfMemory, + "VRTDerivedRasterBand::IRasterIO:" \ + "Out of memory allocating " CPL_FRMT_GIB " bytes.\n", + (GIntBig)nPixelSpace * nBufXSize * nBufYSize); + return CE_Failure; + } + + /* ------------------------------------------------------------ */ + /* #4045: Initialize the newly allocated buffers before handing */ + /* them off to the sources. These buffers are packed, so we */ + /* don't need any special line-by-line handling when a nonzero */ + /* nodata value is set. */ + /* ------------------------------------------------------------ */ + if ( !bNoDataValueSet || dfNoDataValue == 0 ) + { + memset( pBuffers[iSource], 0, sourcesize * nBufXSize * nBufYSize ); + } + else + { + GDALCopyWords( &dfNoDataValue, GDT_Float64, 0, + (GByte *) pBuffers[iSource], eSrcType, sourcesize, + nBufXSize * nBufYSize); + } + } + + GDALRasterIOExtraArg sExtraArg; + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + + /* ---- Load values for sources into packed buffers ---- */ + for(iSource = 0; iSource < nSources; iSource++) { + eErr = ((VRTSource *)papoSources[iSource])->RasterIO + (nXOff, nYOff, nXSize, nYSize, + pBuffers[iSource], nBufXSize, nBufYSize, + eSrcType, GDALGetDataTypeSize( eSrcType ) / 8, + (GDALGetDataTypeSize( eSrcType ) / 8) * nBufXSize, &sExtraArg); + } + + /* ---- Apply pixel function ---- */ + if (eErr == CE_None) { + eErr = pfnPixelFunc((void **)pBuffers, nSources, + pData, nBufXSize, nBufYSize, + eSrcType, eBufType, nPixelSpace, nLineSpace); + } + + /* ---- Release buffers ---- */ + for (iSource = 0; iSource < nSources; iSource++) { + VSIFree(pBuffers[iSource]); + } + CPLFree(pBuffers); + + return eErr; +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr VRTDerivedRasterBand::XMLInit(CPLXMLNode *psTree, + const char *pszVRTPath) + +{ + CPLErr eErr; + const char *pszTypeName; + + eErr = VRTSourcedRasterBand::XMLInit( psTree, pszVRTPath ); + if( eErr != CE_None ) + return eErr; + + /* ---- Read derived pixel function type ---- */ + this->SetPixelFunctionName + (CPLGetXMLValue(psTree, "PixelFunctionType", NULL)); + + /* ---- Read optional source transfer data type ---- */ + pszTypeName = CPLGetXMLValue(psTree, "SourceTransferType", NULL); + if (pszTypeName != NULL) { + this->eSourceTransferType = GDALGetDataTypeByName(pszTypeName); + } + + return CE_None; +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *VRTDerivedRasterBand::SerializeToXML(const char *pszVRTPath) +{ + CPLXMLNode *psTree; + + psTree = VRTSourcedRasterBand::SerializeToXML( pszVRTPath ); + +/* -------------------------------------------------------------------- */ +/* Set subclass. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLNode( + CPLCreateXMLNode( psTree, CXT_Attribute, "subClass" ), + CXT_Text, "VRTDerivedRasterBand" ); + + /* ---- Encode DerivedBand-specific fields ---- */ + if( pszFuncName != NULL && strlen(pszFuncName) > 0 ) + CPLSetXMLValue(psTree, "PixelFunctionType", this->pszFuncName); + if( this->eSourceTransferType != GDT_Unknown) + CPLSetXMLValue(psTree, "SourceTransferType", + GDALGetDataTypeName(this->eSourceTransferType)); + + return psTree; +} + diff --git a/bazaar/plugin/gdal/frmts/vrt/vrtdriver.cpp b/bazaar/plugin/gdal/frmts/vrt/vrtdriver.cpp new file mode 100644 index 000000000..38f952384 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/vrtdriver.cpp @@ -0,0 +1,396 @@ +/****************************************************************************** + * $Id: vrtdriver.cpp 29294 2015-06-05 08:52:15Z rouault $ + * + * Project: Virtual GDAL Datasets + * Purpose: Implementation of VRTDriver + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "vrtdataset.h" +#include "cpl_minixml.h" +#include "cpl_string.h" +#include "gdal_alg_priv.h" + +CPL_CVSID("$Id: vrtdriver.cpp 29294 2015-06-05 08:52:15Z rouault $"); + +/************************************************************************/ +/* VRTDriver() */ +/************************************************************************/ + +VRTDriver::VRTDriver() + +{ + papszSourceParsers = NULL; +#if 0 + pDeserializerData = GDALRegisterTransformDeserializer("WarpedOverviewTransformer", + VRTWarpedOverviewTransform, + VRTDeserializeWarpedOverviewTransformer); +#else + pDeserializerData = NULL; +#endif +} + +/************************************************************************/ +/* ~VRTDriver() */ +/************************************************************************/ + +VRTDriver::~VRTDriver() + +{ + CSLDestroy( papszSourceParsers ); +#if 0 + if ( pDeserializerData ) + { + GDALUnregisterTransformDeserializer( pDeserializerData ); + } +#endif +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **VRTDriver::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALDriver::GetMetadataDomainList(), + TRUE, + "SourceParsers", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **VRTDriver::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain && EQUAL(pszDomain,"SourceParsers") ) + return papszSourceParsers; + else + return GDALDriver::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr VRTDriver::SetMetadata( char **papszMetadata, const char *pszDomain ) + +{ + if( pszDomain && EQUAL(pszDomain,"SourceParsers") ) + { + CSLDestroy( papszSourceParsers ); + papszSourceParsers = CSLDuplicate( papszMetadata ); + return CE_None; + } + else + return GDALDriver::SetMetadata( papszMetadata, pszDomain ); +} + +/************************************************************************/ +/* AddSourceParser() */ +/************************************************************************/ + +void VRTDriver::AddSourceParser( const char *pszElementName, + VRTSourceParser pfnParser ) + +{ + char szPtrValue[128]; + + sprintf( szPtrValue, "%p", pfnParser ); + papszSourceParsers = CSLSetNameValue( papszSourceParsers, + pszElementName, szPtrValue ); +} + +/************************************************************************/ +/* ParseSource() */ +/************************************************************************/ + +VRTSource *VRTDriver::ParseSource( CPLXMLNode *psSrc, const char *pszVRTPath ) + +{ + const char *pszParserFunc; + + if( psSrc == NULL || psSrc->eType != CXT_Element ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Corrupt or empty VRT source XML document." ); + return NULL; + } + + pszParserFunc = CSLFetchNameValue( papszSourceParsers, psSrc->pszValue ); + if( pszParserFunc == NULL ) + return NULL; + + VRTSourceParser pfnParser = NULL; + + sscanf( pszParserFunc, "%p", &pfnParser ); + + if( pfnParser == NULL ) + return NULL; + else + return pfnParser( psSrc, pszVRTPath ); +} + +/************************************************************************/ +/* VRTCreateCopy() */ +/************************************************************************/ + +static GDALDataset * +VRTCreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, + char ** papszOptions, + CPL_UNUSED GDALProgressFunc pfnProgress, + CPL_UNUSED void * pProgressData ) +{ + VRTDataset *poVRTDS = NULL; + + (void) bStrict; + (void) papszOptions; + + CPLAssert( NULL != poSrcDS ); + +/* -------------------------------------------------------------------- */ +/* If the source dataset is a virtual dataset then just write */ +/* it to disk as a special case to avoid extra layers of */ +/* indirection. */ +/* -------------------------------------------------------------------- */ + if( poSrcDS->GetDriver() != NULL && + EQUAL(poSrcDS->GetDriver()->GetDescription(),"VRT") ) + { + + /* -------------------------------------------------------------------- */ + /* Convert tree to a single block of XML text. */ + /* -------------------------------------------------------------------- */ + char *pszVRTPath = CPLStrdup(CPLGetPath(pszFilename)); + ((VRTDataset *) poSrcDS)->UnsetPreservedRelativeFilenames(); + CPLXMLNode *psDSTree = ((VRTDataset *) poSrcDS)->SerializeToXML( pszVRTPath ); + + char *pszXML = CPLSerializeXMLTree( psDSTree ); + + CPLDestroyXMLNode( psDSTree ); + + CPLFree( pszVRTPath ); + + /* -------------------------------------------------------------------- */ + /* Write to disk. */ + /* -------------------------------------------------------------------- */ + GDALDataset* pCopyDS = NULL; + + if( 0 != strlen( pszFilename ) ) + { + VSILFILE *fpVRT = NULL; + + fpVRT = VSIFOpenL( pszFilename, "wb" ); + if (fpVRT == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot create %s", pszFilename); + CPLFree( pszXML ); + return NULL; + } + + VSIFWriteL( pszXML, 1, strlen(pszXML), fpVRT ); + VSIFCloseL( fpVRT ); + + pCopyDS = (GDALDataset *) GDALOpen( pszFilename, GA_Update ); + } + else + { + /* No destination file is given, so pass serialized XML directly. */ + + pCopyDS = (GDALDataset *) GDALOpen( pszXML, GA_Update ); + } + + CPLFree( pszXML ); + + return pCopyDS; + } + +/* -------------------------------------------------------------------- */ +/* Create the virtual dataset. */ +/* -------------------------------------------------------------------- */ + poVRTDS = (VRTDataset *) + VRTDataset::Create( pszFilename, + poSrcDS->GetRasterXSize(), + poSrcDS->GetRasterYSize(), + 0, GDT_Byte, NULL ); + if (poVRTDS == NULL) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Do we have a geotransform? */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + + if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None ) + { + poVRTDS->SetGeoTransform( adfGeoTransform ); + } + +/* -------------------------------------------------------------------- */ +/* Copy projection */ +/* -------------------------------------------------------------------- */ + poVRTDS->SetProjection( poSrcDS->GetProjectionRef() ); + +/* -------------------------------------------------------------------- */ +/* Emit dataset level metadata. */ +/* -------------------------------------------------------------------- */ + poVRTDS->SetMetadata( poSrcDS->GetMetadata() ); + +/* -------------------------------------------------------------------- */ +/* Copy any special domains that should be transportable. */ +/* -------------------------------------------------------------------- */ + char **papszMD; + + papszMD = poSrcDS->GetMetadata( "RPC" ); + if( papszMD ) + poVRTDS->SetMetadata( papszMD, "RPC" ); + + papszMD = poSrcDS->GetMetadata( "IMD" ); + if( papszMD ) + poVRTDS->SetMetadata( papszMD, "IMD" ); + + papszMD = poSrcDS->GetMetadata( "GEOLOCATION" ); + if( papszMD ) + poVRTDS->SetMetadata( papszMD, "GEOLOCATION" ); + +/* -------------------------------------------------------------------- */ +/* GCPs */ +/* -------------------------------------------------------------------- */ + if( poSrcDS->GetGCPCount() > 0 ) + { + poVRTDS->SetGCPs( poSrcDS->GetGCPCount(), + poSrcDS->GetGCPs(), + poSrcDS->GetGCPProjection() ); + } + +/* -------------------------------------------------------------------- */ +/* Loop over all the bands. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < poSrcDS->GetRasterCount(); iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + +/* -------------------------------------------------------------------- */ +/* Create the band with the appropriate band type. */ +/* -------------------------------------------------------------------- */ + poVRTDS->AddBand( poSrcBand->GetRasterDataType(), NULL ); + + VRTSourcedRasterBand *poVRTBand = + (VRTSourcedRasterBand *) poVRTDS->GetRasterBand( iBand+1 ); + +/* -------------------------------------------------------------------- */ +/* Setup source mapping. */ +/* -------------------------------------------------------------------- */ + poVRTBand->AddSimpleSource( poSrcBand ); + +/* -------------------------------------------------------------------- */ +/* Emit various band level metadata. */ +/* -------------------------------------------------------------------- */ + poVRTBand->CopyCommonInfoFrom( poSrcBand ); + +/* -------------------------------------------------------------------- */ +/* Add specific mask band. */ +/* -------------------------------------------------------------------- */ + if ( (poSrcBand->GetMaskFlags() & (GMF_PER_DATASET | GMF_ALL_VALID | GMF_NODATA)) == 0) + { + VRTSourcedRasterBand* poVRTMaskBand = new VRTSourcedRasterBand(poVRTDS, 0, + poSrcBand->GetMaskBand()->GetRasterDataType(), + poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize()); + poVRTMaskBand->AddMaskBandSource( poSrcBand ); + poVRTBand->SetMaskBand( poVRTMaskBand ); + } + } + +/* -------------------------------------------------------------------- */ +/* Add dataset mask band */ +/* -------------------------------------------------------------------- */ + if (poSrcDS->GetRasterCount() != 0 && + poSrcDS->GetRasterBand(1) != NULL && + poSrcDS->GetRasterBand(1)->GetMaskFlags() == GMF_PER_DATASET) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1); + VRTSourcedRasterBand* poVRTMaskBand = new VRTSourcedRasterBand(poVRTDS, 0, + poSrcBand->GetMaskBand()->GetRasterDataType(), + poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize()); + poVRTMaskBand->AddMaskBandSource( poSrcBand ); + poVRTDS->SetMaskBand( poVRTMaskBand ); + } + + poVRTDS->FlushCache(); + + return poVRTDS; +} + +/************************************************************************/ +/* GDALRegister_VRT() */ +/************************************************************************/ + +void GDALRegister_VRT() + +{ + VRTDriver *poDriver; + + if( GDALGetDriverByName( "VRT" ) == NULL ) + { + poDriver = new VRTDriver(); + + poDriver->SetDescription( "VRT" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Virtual Raster" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "vrt" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "gdal_vrttut.html" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte Int16 UInt16 Int32 UInt32 Float32 Float64 CInt16 CInt32 CFloat32 CFloat64" ); + + poDriver->pfnOpen = VRTDataset::Open; + poDriver->pfnCreateCopy = VRTCreateCopy; + poDriver->pfnCreate = VRTDataset::Create; + poDriver->pfnIdentify = VRTDataset::Identify; + poDriver->pfnDelete = VRTDataset::Delete; + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->AddSourceParser( "SimpleSource", + VRTParseCoreSources ); + poDriver->AddSourceParser( "ComplexSource", + VRTParseCoreSources ); + poDriver->AddSourceParser( "AveragedSource", + VRTParseCoreSources ); + + poDriver->AddSourceParser( "KernelFilteredSource", + VRTParseFilterSources ); + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/vrt/vrtfilters.cpp b/bazaar/plugin/gdal/frmts/vrt/vrtfilters.cpp new file mode 100644 index 000000000..ad291c997 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/vrtfilters.cpp @@ -0,0 +1,648 @@ +/****************************************************************************** + * $Id: vrtfilters.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: Virtual GDAL Datasets + * Purpose: Implementation of some filter types. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "vrtdataset.h" +#include "cpl_minixml.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: vrtfilters.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* VRTFilteredSource */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VRTFilteredSource() */ +/************************************************************************/ + +VRTFilteredSource::VRTFilteredSource() + +{ + nExtraEdgePixels = 0; + nSupportedTypesCount = 1; + aeSupportedTypes[0] = GDT_Float32; +} + +/************************************************************************/ +/* ~VRTFilteredSource() */ +/************************************************************************/ + +VRTFilteredSource::~VRTFilteredSource() + +{ +} + +/************************************************************************/ +/* SetExtraEdgePixels() */ +/************************************************************************/ + +void VRTFilteredSource::SetExtraEdgePixels( int nEdgePixels ) + +{ + nExtraEdgePixels = nEdgePixels; +} + +/************************************************************************/ +/* SetFilteringDataTypesSupported() */ +/************************************************************************/ + +void VRTFilteredSource::SetFilteringDataTypesSupported( int nTypeCount, + GDALDataType *paeTypes) + +{ + if( nTypeCount > + (int) (sizeof(aeSupportedTypes)/sizeof(GDALDataType)) ) + { + CPLAssert( FALSE ); + nTypeCount = (int) + (sizeof(aeSupportedTypes)/sizeof(GDALDataType)); + } + + nSupportedTypesCount = nTypeCount; + memcpy( aeSupportedTypes, paeTypes, sizeof(GDALDataType) * nTypeCount ); +} + +/************************************************************************/ +/* IsTypeSupported() */ +/************************************************************************/ + +int VRTFilteredSource::IsTypeSupported( GDALDataType eTestType ) + +{ + int i; + + for( i = 0; i < nSupportedTypesCount; i++ ) + { + if( eTestType == aeSupportedTypes[i] ) + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* RasterIO() */ +/************************************************************************/ + +CPLErr +VRTFilteredSource::RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ +/* -------------------------------------------------------------------- */ +/* For now we don't support filtered access to non-full */ +/* resolution requests. Just collect the data directly without */ +/* any operator. */ +/* -------------------------------------------------------------------- */ + if( nBufXSize != nXSize || nBufYSize != nYSize ) + { + return VRTComplexSource::RasterIO( nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, psExtraArg ); + } + + // The window we will actually request from the source raster band. + double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize; + int nReqXOff, nReqYOff, nReqXSize, nReqYSize; + + // The window we will actual set _within_ the pData buffer. + int nOutXOff, nOutYOff, nOutXSize, nOutYSize; + + if( !GetSrcDstWindow( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, + &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, + &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, + &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) ) + return CE_None; + + pData = ((GByte *)pData) + + nPixelSpace * nOutXOff + + nLineSpace * nOutYOff; + +/* -------------------------------------------------------------------- */ +/* Determine the data type we want to request. We try to match */ +/* the source or destination request, and if both those fail we */ +/* fallback to the first supported type at least as expressive */ +/* as the request. */ +/* -------------------------------------------------------------------- */ + GDALDataType eOperDataType = GDT_Unknown; + int i; + + if( IsTypeSupported( eBufType ) ) + eOperDataType = eBufType; + + if( eOperDataType == GDT_Unknown + && IsTypeSupported( poRasterBand->GetRasterDataType() ) ) + eOperDataType = poRasterBand->GetRasterDataType(); + + if( eOperDataType == GDT_Unknown ) + { + for( i = 0; i < nSupportedTypesCount; i++ ) + { + if( GDALDataTypeUnion( aeSupportedTypes[i], eBufType ) + == aeSupportedTypes[i] ) + { + eOperDataType = aeSupportedTypes[i]; + } + } + } + + if( eOperDataType == GDT_Unknown ) + { + eOperDataType = aeSupportedTypes[0]; + + for( i = 1; i < nSupportedTypesCount; i++ ) + { + if( GDALGetDataTypeSize( aeSupportedTypes[i] ) + > GDALGetDataTypeSize( eOperDataType ) ) + { + eOperDataType = aeSupportedTypes[i]; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Allocate the buffer of data into which our imagery will be */ +/* read, with the extra edge pixels as well. This will be the */ +/* source data fed into the filter. */ +/* -------------------------------------------------------------------- */ + int nPixelOffset, nLineOffset; + int nExtraXSize = nOutXSize + 2 * nExtraEdgePixels; + int nExtraYSize = nOutYSize + 2 * nExtraEdgePixels; + GByte *pabyWorkData; + + // FIXME? : risk of multiplication overflow + pabyWorkData = (GByte *) + VSICalloc( nExtraXSize * nExtraYSize, + (GDALGetDataTypeSize(eOperDataType) / 8) ); + + if( pabyWorkData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Work buffer allocation failed." ); + return CE_Failure; + } + + nPixelOffset = GDALGetDataTypeSize( eOperDataType ) / 8; + nLineOffset = nPixelOffset * nExtraXSize; + +/* -------------------------------------------------------------------- */ +/* Allocate the output buffer if the passed in output buffer is */ +/* not of the same type as our working format, or if the passed */ +/* in buffer has an unusual organization. */ +/* -------------------------------------------------------------------- */ + GByte *pabyOutData; + + if( nPixelSpace != nPixelOffset || nLineSpace != nLineOffset + || eOperDataType != eBufType ) + { + pabyOutData = (GByte *) + VSIMalloc3(nOutXSize, nOutYSize, nPixelOffset ); + + if( pabyOutData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Work buffer allocation failed." ); + return CE_Failure; + } + } + else + pabyOutData = (GByte *) pData; + +/* -------------------------------------------------------------------- */ +/* Figure out the extended window that we want to load. Note */ +/* that we keep track of the file window as well as the amount */ +/* we will need to edge fill past the edge of the source dataset. */ +/* -------------------------------------------------------------------- */ + int nTopFill=0, nLeftFill=0, nRightFill=0, nBottomFill=0; + int nFileXOff, nFileYOff, nFileXSize, nFileYSize; + + nFileXOff = nReqXOff - nExtraEdgePixels; + nFileYOff = nReqYOff - nExtraEdgePixels; + nFileXSize = nExtraXSize; + nFileYSize = nExtraYSize; + + if( nFileXOff < 0 ) + { + nLeftFill = -nFileXOff; + nFileXOff = 0; + nFileXSize -= nLeftFill; + } + + if( nFileYOff < 0 ) + { + nTopFill = -nFileYOff; + nFileYOff = 0; + nFileYSize -= nTopFill; + } + + if( nFileXOff + nFileXSize > poRasterBand->GetXSize() ) + { + nRightFill = nFileXOff + nFileXSize - poRasterBand->GetXSize(); + nFileXSize -= nRightFill; + } + + if( nFileYOff + nFileYSize > poRasterBand->GetYSize() ) + { + nBottomFill = nFileYOff + nFileYSize - poRasterBand->GetYSize(); + nFileYSize -= nBottomFill; + } + +/* -------------------------------------------------------------------- */ +/* Load the data. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + eErr = + VRTComplexSource::RasterIOInternal( nFileXOff, nFileYOff, nFileXSize, nFileYSize, + pabyWorkData + + nLineOffset * nTopFill + + nPixelOffset * nLeftFill, + nFileXSize, nFileYSize, eOperDataType, + nPixelOffset, nLineOffset, psExtraArg ); + + if( eErr != CE_None ) + { + if( pabyWorkData != pData ) + VSIFree( pabyWorkData ); + + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Fill in missing areas. Note that we replicate the edge */ +/* valid values out. We don't using "mirroring" which might be */ +/* more suitable for some times of filters. We also don't mark */ +/* these pixels as "nodata" though perhaps we should. */ +/* -------------------------------------------------------------------- */ + if( nLeftFill != 0 || nRightFill != 0 ) + { + for( i = nTopFill; i < nExtraYSize - nBottomFill; i++ ) + { + if( nLeftFill != 0 ) + GDALCopyWords( pabyWorkData + nPixelOffset * nLeftFill + + i * nLineOffset, eOperDataType, 0, + pabyWorkData + i * nLineOffset, eOperDataType, + nPixelOffset, nLeftFill ); + + if( nRightFill != 0 ) + GDALCopyWords( pabyWorkData + i * nLineOffset + + nPixelOffset * (nExtraXSize - nRightFill - 1), + eOperDataType, 0, + pabyWorkData + i * nLineOffset + + nPixelOffset * (nExtraXSize - nRightFill), + eOperDataType, nPixelOffset, nRightFill ); + } + } + + for( i = 0; i < nTopFill; i++ ) + { + memcpy( pabyWorkData + i * nLineOffset, + pabyWorkData + nTopFill * nLineOffset, + nLineOffset ); + } + + for( i = nExtraYSize - nBottomFill; i < nExtraYSize; i++ ) + { + memcpy( pabyWorkData + i * nLineOffset, + pabyWorkData + (nExtraYSize - nBottomFill - 1) * nLineOffset, + nLineOffset ); + } + +/* -------------------------------------------------------------------- */ +/* Filter the data. */ +/* -------------------------------------------------------------------- */ + eErr = FilterData( nOutXSize, nOutYSize, eOperDataType, + pabyWorkData, pabyOutData ); + + VSIFree( pabyWorkData ); + if( eErr != CE_None ) + { + if( pabyOutData != pData ) + VSIFree( pabyOutData ); + + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Copy from work buffer to target buffer. */ +/* -------------------------------------------------------------------- */ + if( pabyOutData != pData ) + { + for( i = 0; i < nOutYSize; i++ ) + { + GDALCopyWords( pabyOutData + i * (nPixelOffset * nOutXSize), + eOperDataType, nPixelOffset, + ((GByte *) pData) + i * nLineSpace, + eBufType, nPixelSpace, nOutXSize ); + } + + VSIFree( pabyOutData ); + } + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VRTKernelFilteredSource */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VRTKernelFilteredSource() */ +/************************************************************************/ + +VRTKernelFilteredSource::VRTKernelFilteredSource() + +{ + GDALDataType aeSupTypes[] = { GDT_Float32 }; + padfKernelCoefs = NULL; + nKernelSize = 0; + bNormalized = FALSE; + + SetFilteringDataTypesSupported( 1, aeSupTypes ); +} + +/************************************************************************/ +/* ~VRTKernelFilteredSource() */ +/************************************************************************/ + +VRTKernelFilteredSource::~VRTKernelFilteredSource() + +{ + CPLFree( padfKernelCoefs ); +} + +/************************************************************************/ +/* SetNormalized() */ +/************************************************************************/ + +void VRTKernelFilteredSource::SetNormalized( int bNormalizedIn ) + +{ + bNormalized = bNormalizedIn; +} + +/************************************************************************/ +/* SetKernel() */ +/************************************************************************/ + +CPLErr VRTKernelFilteredSource::SetKernel( int nNewKernelSize, + double *padfNewCoefs ) + +{ + if( nNewKernelSize < 1 || (nNewKernelSize % 2) != 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Illegal filtering kernel size %d, must be odd positive number.", + nNewKernelSize ); + return CE_Failure; + } + + CPLFree( padfKernelCoefs ); + nKernelSize = nNewKernelSize; + + padfKernelCoefs = (double *) + CPLMalloc(sizeof(double) * nKernelSize * nKernelSize ); + memcpy( padfKernelCoefs, padfNewCoefs, + sizeof(double) * nKernelSize * nKernelSize ); + + SetExtraEdgePixels( (nNewKernelSize - 1) / 2 ); + + return CE_None; +} + +/************************************************************************/ +/* FilterData() */ +/************************************************************************/ + +CPLErr VRTKernelFilteredSource:: +FilterData( int nXSize, int nYSize, GDALDataType eType, + GByte *pabySrcData, GByte *pabyDstData ) + +{ +/* -------------------------------------------------------------------- */ +/* Validate data type. */ +/* -------------------------------------------------------------------- */ + if( eType != GDT_Float32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported data type (%s) in VRTKernelFilteredSource::FilterData()", + GDALGetDataTypeName( eType ) ); + return CE_Failure; + } + + CPLAssert( nExtraEdgePixels*2 + 1 == nKernelSize || + (nKernelSize == 0 && nExtraEdgePixels == 0) ); + +/* -------------------------------------------------------------------- */ +/* Float32 case. */ +/* -------------------------------------------------------------------- */ + if( eType == GDT_Float32 ) + { + int iX, iY; + + int bHasNoData; + float fNoData = (float) poRasterBand->GetNoDataValue(&bHasNoData); + + for( iY = 0; iY < nYSize; iY++ ) + { + for( iX = 0; iX < nXSize; iX++ ) + { + int iYY, iKern = 0; + double dfSum = 0.0, dfKernSum = 0.0; + float fResult; + int iIndex = (iY+nKernelSize/2 ) * (nXSize+2*nExtraEdgePixels) + iX + nKernelSize/2; + float fCenter = ((float *)pabySrcData)[iIndex]; + + // Check if center srcpixel is NoData + if(!bHasNoData || fCenter != fNoData) + { + for( iYY = 0; iYY < nKernelSize; iYY++ ) + { + int i; + float *pafData = ((float *)pabySrcData) + + (iY+iYY) * (nXSize+2*nExtraEdgePixels) + iX; + + for( i = 0; i < nKernelSize; i++, pafData++, iKern++ ) + { + if(!bHasNoData || *pafData != fNoData) + { + dfSum += *pafData * padfKernelCoefs[iKern]; + dfKernSum += padfKernelCoefs[iKern]; + } + } + } + if( bNormalized ) + { + if( dfKernSum != 0.0 ) + fResult = (float) (dfSum / dfKernSum); + else + fResult = 0.0; + } + else + fResult = (float) dfSum; + + ((float *) pabyDstData)[iX + iY * nXSize] = fResult; + } + else + ((float *) pabyDstData)[iX + iY * nXSize] = fNoData; + } + } + } + + return CE_None; +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr VRTKernelFilteredSource::XMLInit( CPLXMLNode *psTree, + const char *pszVRTPath ) + +{ + CPLErr eErr = VRTFilteredSource::XMLInit( psTree, pszVRTPath ); + int nNewKernelSize, i, nCoefs; + double *padfNewCoefs; + + if( eErr != CE_None ) + return eErr; + + nNewKernelSize = atoi(CPLGetXMLValue(psTree,"Kernel.Size","0")); + + if( nNewKernelSize == 0 ) + return CE_None; + + char **papszCoefItems = + CSLTokenizeString( CPLGetXMLValue(psTree,"Kernel.Coefs","") ); + + nCoefs = CSLCount(papszCoefItems); + + if( nCoefs != nNewKernelSize * nNewKernelSize ) + { + CSLDestroy( papszCoefItems ); + CPLError( CE_Failure, CPLE_AppDefined, + "Got wrong number of filter kernel coefficients (%s).\n" + "Expected %d, got %d.", + CPLGetXMLValue(psTree,"Kernel.Coefs",""), + nNewKernelSize * nNewKernelSize, nCoefs ); + return CE_Failure; + } + + padfNewCoefs = (double *) CPLMalloc(sizeof(double) * nCoefs); + + for( i = 0; i < nCoefs; i++ ) + padfNewCoefs[i] = CPLAtof(papszCoefItems[i]); + + eErr = SetKernel( nNewKernelSize, padfNewCoefs ); + + CPLFree( padfNewCoefs ); + CSLDestroy( papszCoefItems ); + + SetNormalized( atoi(CPLGetXMLValue(psTree,"Kernel.normalized","0")) ); + + return eErr; +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *VRTKernelFilteredSource::SerializeToXML( const char *pszVRTPath ) + +{ + CPLXMLNode *psSrc = VRTFilteredSource::SerializeToXML( pszVRTPath ); + CPLXMLNode *psKernel; + char *pszKernelCoefs; + int iCoef, nCoefCount = nKernelSize * nKernelSize; + + if( psSrc == NULL ) + return NULL; + + CPLFree( psSrc->pszValue ); + psSrc->pszValue = CPLStrdup("KernelFilteredSource" ); + + if( nKernelSize == 0 ) + return psSrc; + + psKernel = CPLCreateXMLNode( psSrc, CXT_Element, "Kernel" ); + + if( bNormalized ) + CPLCreateXMLNode( + CPLCreateXMLNode( psKernel, CXT_Attribute, "normalized" ), + CXT_Text, "1" ); + else + CPLCreateXMLNode( + CPLCreateXMLNode( psKernel, CXT_Attribute, "normalized" ), + CXT_Text, "0" ); + + pszKernelCoefs = (char *) CPLMalloc(nCoefCount * 32); + + strcpy( pszKernelCoefs, "" ); + for( iCoef = 0; iCoef < nCoefCount; iCoef++ ) + CPLsprintf( pszKernelCoefs + strlen(pszKernelCoefs), + "%.8g ", padfKernelCoefs[iCoef] ); + + CPLSetXMLValue( psKernel, "Size", CPLSPrintf( "%d", nKernelSize ) ); + CPLSetXMLValue( psKernel, "Coefs", pszKernelCoefs ); + + CPLFree( pszKernelCoefs ); + + return psSrc; +} + +/************************************************************************/ +/* VRTParseFilterSources() */ +/************************************************************************/ + +VRTSource *VRTParseFilterSources( CPLXMLNode *psChild, const char *pszVRTPath ) + +{ + VRTSource *poSrc; + + if( EQUAL(psChild->pszValue,"KernelFilteredSource") ) + { + poSrc = new VRTKernelFilteredSource(); + if( poSrc->XMLInit( psChild, pszVRTPath ) == CE_None ) + return poSrc; + else + delete poSrc; + } + + return NULL; +} + diff --git a/bazaar/plugin/gdal/frmts/vrt/vrtrasterband.cpp b/bazaar/plugin/gdal/frmts/vrt/vrtrasterband.cpp new file mode 100644 index 000000000..3936f0535 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/vrtrasterband.cpp @@ -0,0 +1,1143 @@ +/****************************************************************************** + * $Id: vrtrasterband.cpp 29038 2015-04-28 09:03:36Z rouault $ + * + * Project: Virtual GDAL Datasets + * Purpose: Implementation of VRTRasterBand + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "vrtdataset.h" +#include "cpl_minixml.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: vrtrasterband.cpp 29038 2015-04-28 09:03:36Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* VRTRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VRTRasterBand() */ +/************************************************************************/ + +VRTRasterBand::VRTRasterBand() + +{ + Initialize( 0, 0 ); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void VRTRasterBand::Initialize( int nXSize, int nYSize ) + +{ + poDS = NULL; + nBand = 0; + eAccess = GA_ReadOnly; + eDataType = GDT_Byte; + + nRasterXSize = nXSize; + nRasterYSize = nYSize; + + nBlockXSize = MIN(128,nXSize); + nBlockYSize = MIN(128,nYSize); + + bIsMaskBand = FALSE; + bNoDataValueSet = FALSE; + bHideNoDataValue = FALSE; + dfNoDataValue = -10000.0; + poColorTable = NULL; + eColorInterp = GCI_Undefined; + + pszUnitType = NULL; + papszCategoryNames = NULL; + dfOffset = 0.0; + dfScale = 1.0; + + psSavedHistograms = NULL; + + poMaskBand = NULL; +} + +/************************************************************************/ +/* ~VRTRasterBand() */ +/************************************************************************/ + +VRTRasterBand::~VRTRasterBand() + +{ + CPLFree( pszUnitType ); + + if( poColorTable != NULL ) + delete poColorTable; + + CSLDestroy( papszCategoryNames ); + if( psSavedHistograms != NULL ) + CPLDestroyXMLNode( psSavedHistograms ); + + delete poMaskBand; +} + +/************************************************************************/ +/* CopyCommonInfoFrom() */ +/* */ +/* Copy common metadata, pixel descriptions, and color */ +/* interpretation from the provided source band. */ +/************************************************************************/ + +CPLErr VRTRasterBand::CopyCommonInfoFrom( GDALRasterBand * poSrcBand ) + +{ + int bSuccess; + double dfNoData; + + SetMetadata( poSrcBand->GetMetadata() ); + SetColorTable( poSrcBand->GetColorTable() ); + SetColorInterpretation(poSrcBand->GetColorInterpretation()); + if( strlen(poSrcBand->GetDescription()) > 0 ) + SetDescription( poSrcBand->GetDescription() ); + dfNoData = poSrcBand->GetNoDataValue( &bSuccess ); + if( bSuccess ) + SetNoDataValue( dfNoData ); + + SetOffset( poSrcBand->GetOffset() ); + SetScale( poSrcBand->GetScale() ); + SetCategoryNames( poSrcBand->GetCategoryNames() ); + if( !EQUAL(poSrcBand->GetUnitType(),"") ) + SetUnitType( poSrcBand->GetUnitType() ); + + return CE_None; +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr VRTRasterBand::SetMetadata( char **papszMetadata, + const char *pszDomain ) + +{ + ((VRTDataset *) poDS)->SetNeedsFlush(); + + return GDALRasterBand::SetMetadata( papszMetadata, pszDomain ); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr VRTRasterBand::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ + ((VRTDataset *) poDS)->SetNeedsFlush(); + + if( EQUAL(pszName,"HideNoDataValue") ) + { + bHideNoDataValue = CSLTestBoolean( pszValue ); + return CE_None; + } + else + return GDALRasterBand::SetMetadataItem( pszName, pszValue, pszDomain ); +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +const char *VRTRasterBand::GetUnitType() + +{ + if( pszUnitType == NULL ) + return ""; + else + return pszUnitType; +} + +/************************************************************************/ +/* SetUnitType() */ +/************************************************************************/ + +CPLErr VRTRasterBand::SetUnitType( const char *pszNewValue ) + +{ + ((VRTDataset *) poDS)->SetNeedsFlush(); + + CPLFree( pszUnitType ); + + if( pszNewValue == NULL ) + pszUnitType = NULL; + else + pszUnitType = CPLStrdup(pszNewValue); + + return CE_None; +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +double VRTRasterBand::GetOffset( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return dfOffset; +} + +/************************************************************************/ +/* SetOffset() */ +/************************************************************************/ + +CPLErr VRTRasterBand::SetOffset( double dfNewOffset ) + +{ + ((VRTDataset *) poDS)->SetNeedsFlush(); + + dfOffset = dfNewOffset; + return CE_None; +} + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ + +double VRTRasterBand::GetScale( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return dfScale; +} + +/************************************************************************/ +/* SetScale() */ +/************************************************************************/ + +CPLErr VRTRasterBand::SetScale( double dfNewScale ) + +{ + ((VRTDataset *) poDS)->SetNeedsFlush(); + + dfScale = dfNewScale; + return CE_None; +} + +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +char **VRTRasterBand::GetCategoryNames() + +{ + return papszCategoryNames; +} + +/************************************************************************/ +/* SetCategoryNames() */ +/************************************************************************/ + +CPLErr VRTRasterBand::SetCategoryNames( char ** papszNewNames ) + +{ + ((VRTDataset *) poDS)->SetNeedsFlush(); + + CSLDestroy( papszCategoryNames ); + papszCategoryNames = CSLDuplicate( papszNewNames ); + + return CE_None; +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr VRTRasterBand::XMLInit( CPLXMLNode * psTree, + const char *pszVRTPath ) + +{ +/* -------------------------------------------------------------------- */ +/* Validate a bit. */ +/* -------------------------------------------------------------------- */ + if( psTree == NULL || psTree->eType != CXT_Element + || !EQUAL(psTree->pszValue,"VRTRasterBand") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid node passed to VRTRasterBand::XMLInit()." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Set the band if provided as an attribute. */ +/* -------------------------------------------------------------------- */ + const char* pszBand = CPLGetXMLValue( psTree, "band", NULL); + if( pszBand != NULL ) + { + nBand = atoi(pszBand); + } + +/* -------------------------------------------------------------------- */ +/* Set the band if provided as an attribute. */ +/* -------------------------------------------------------------------- */ + const char *pszDataType = CPLGetXMLValue( psTree, "dataType", NULL); + if( pszDataType != NULL ) + { + eDataType = GDALGetDataTypeByName(pszDataType); + } + +/* -------------------------------------------------------------------- */ +/* Apply any band level metadata. */ +/* -------------------------------------------------------------------- */ + oMDMD.XMLInit( psTree, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Collect various other items of metadata. */ +/* -------------------------------------------------------------------- */ + SetDescription( CPLGetXMLValue( psTree, "Description", "" ) ); + + if( CPLGetXMLValue( psTree, "NoDataValue", NULL ) != NULL ) + SetNoDataValue( CPLAtofM(CPLGetXMLValue( psTree, "NoDataValue", "0" )) ); + + if( CPLGetXMLValue( psTree, "HideNoDataValue", NULL ) != NULL ) + bHideNoDataValue = CSLTestBoolean( CPLGetXMLValue( psTree, "HideNoDataValue", "0" ) ); + + SetUnitType( CPLGetXMLValue( psTree, "UnitType", NULL ) ); + + SetOffset( CPLAtof(CPLGetXMLValue( psTree, "Offset", "0.0" )) ); + SetScale( CPLAtof(CPLGetXMLValue( psTree, "Scale", "1.0" )) ); + + if( CPLGetXMLValue( psTree, "ColorInterp", NULL ) != NULL ) + { + const char *pszInterp = CPLGetXMLValue( psTree, "ColorInterp", NULL ); + SetColorInterpretation(GDALGetColorInterpretationByName(pszInterp)); + } + +/* -------------------------------------------------------------------- */ +/* Category names. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psTree, "CategoryNames" ) != NULL ) + { + CPLXMLNode *psEntry; + + CSLDestroy( papszCategoryNames ); + papszCategoryNames = NULL; + + CPLStringList oCategoryNames; + + for( psEntry = CPLGetXMLNode( psTree, "CategoryNames" )->psChild; + psEntry != NULL; psEntry = psEntry->psNext ) + { + if( psEntry->eType != CXT_Element + || !EQUAL(psEntry->pszValue,"Category") + || (psEntry->psChild != NULL && psEntry->psChild->eType != CXT_Text) ) + continue; + + oCategoryNames.AddString( + (psEntry->psChild) ? psEntry->psChild->pszValue : ""); + } + + papszCategoryNames = oCategoryNames.StealList(); + } + +/* -------------------------------------------------------------------- */ +/* Collect a color table. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psTree, "ColorTable" ) != NULL ) + { + CPLXMLNode *psEntry; + GDALColorTable oTable; + int iEntry = 0; + + for( psEntry = CPLGetXMLNode( psTree, "ColorTable" )->psChild; + psEntry != NULL; psEntry = psEntry->psNext ) + { + GDALColorEntry sCEntry; + + sCEntry.c1 = (short) atoi(CPLGetXMLValue( psEntry, "c1", "0" )); + sCEntry.c2 = (short) atoi(CPLGetXMLValue( psEntry, "c2", "0" )); + sCEntry.c3 = (short) atoi(CPLGetXMLValue( psEntry, "c3", "0" )); + sCEntry.c4 = (short) atoi(CPLGetXMLValue( psEntry, "c4", "255" )); + + oTable.SetColorEntry( iEntry++, &sCEntry ); + } + + SetColorTable( &oTable ); + } + +/* -------------------------------------------------------------------- */ +/* Histograms */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psHist = CPLGetXMLNode( psTree, "Histograms" ); + if( psHist != NULL ) + { + CPLXMLNode *psNext = psHist->psNext; + psHist->psNext = NULL; + + psSavedHistograms = CPLCloneXMLTree( psHist ); + psHist->psNext = psNext; + } + +/* ==================================================================== */ +/* Overviews */ +/* ==================================================================== */ + CPLXMLNode *psNode; + + for( psNode = psTree->psChild; psNode != NULL; psNode = psNode->psNext ) + { + if( psNode->eType != CXT_Element + || !EQUAL(psNode->pszValue,"Overview") ) + continue; + +/* -------------------------------------------------------------------- */ +/* Prepare filename. */ +/* -------------------------------------------------------------------- */ + char *pszSrcDSName = NULL; + CPLXMLNode* psFileNameNode=CPLGetXMLNode(psNode,"SourceFilename"); + const char *pszFilename = + psFileNameNode ? CPLGetXMLValue(psFileNameNode,NULL, NULL) : NULL; + + if( pszFilename == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Missing element in Overview." ); + return CE_Failure; + } + + if (EQUALN(pszFilename, "MEM:::", 6) && pszVRTPath != NULL && + !CSLTestBoolean(CPLGetConfigOption("VRT_ALLOW_MEM_DRIVER", "NO"))) + { + CPLError( CE_Failure, CPLE_AppDefined, + " points to a MEM dataset, which is rather suspect! " + "If you know what you are doing, define the VRT_ALLOW_MEM_DRIVER configuration option to YES" ); + return CE_Failure; + } + + if( pszVRTPath != NULL + && atoi(CPLGetXMLValue( psFileNameNode, "relativetoVRT", "0")) ) + { + pszSrcDSName = CPLStrdup( + CPLProjectRelativeFilename( pszVRTPath, pszFilename ) ); + } + else + pszSrcDSName = CPLStrdup( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Get the raster band. */ +/* -------------------------------------------------------------------- */ + int nSrcBand = atoi(CPLGetXMLValue(psNode,"SourceBand","1")); + + apoOverviews.resize( apoOverviews.size() + 1 ); + apoOverviews[apoOverviews.size()-1].osFilename = pszSrcDSName; + apoOverviews[apoOverviews.size()-1].nBand = nSrcBand; + + CPLFree( pszSrcDSName ); + } + +/* ==================================================================== */ +/* Mask band (specific to that raster band) */ +/* ==================================================================== */ + CPLXMLNode* psMaskBandNode = CPLGetXMLNode(psTree, "MaskBand"); + if (psMaskBandNode) + psNode = psMaskBandNode->psChild; + else + psNode = NULL; + for( ; psNode != NULL; psNode = psNode->psNext ) + { + if( psNode->eType != CXT_Element + || !EQUAL(psNode->pszValue,"VRTRasterBand") ) + continue; + + if( ((VRTDataset*)poDS)->poMaskBand != NULL) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Illegal mask band at raster band level when a dataset mask band already exists." ); + break; + } + + const char *pszSubclass = CPLGetXMLValue( psNode, "subclass", + "VRTSourcedRasterBand" ); + VRTRasterBand *poBand = NULL; + + if( EQUAL(pszSubclass,"VRTSourcedRasterBand") ) + poBand = new VRTSourcedRasterBand( GetDataset(), 0 ); + else if( EQUAL(pszSubclass, "VRTDerivedRasterBand") ) + poBand = new VRTDerivedRasterBand( GetDataset(), 0 ); + else if( EQUAL(pszSubclass, "VRTRawRasterBand") ) + poBand = new VRTRawRasterBand( GetDataset(), 0 ); + else if( EQUAL(pszSubclass, "VRTWarpedRasterBand") ) + poBand = new VRTWarpedRasterBand( GetDataset(), 0 ); + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "VRTRasterBand of unrecognised subclass '%s'.", + pszSubclass ); + break; + } + + + if( poBand->XMLInit( psNode, pszVRTPath ) == CE_None ) + { + SetMaskBand(poBand); + } + + break; + } + + return CE_None; +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *VRTRasterBand::SerializeToXML( const char *pszVRTPath ) + +{ + CPLXMLNode *psTree; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "VRTRasterBand" ); + +/* -------------------------------------------------------------------- */ +/* Various kinds of metadata. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psMD; + + CPLSetXMLValue( psTree, "#dataType", + GDALGetDataTypeName( GetRasterDataType() ) ); + + if( nBand > 0 ) + CPLSetXMLValue( psTree, "#band", CPLSPrintf( "%d", GetBand() ) ); + + psMD = oMDMD.Serialize(); + if( psMD != NULL ) + { + CPLAddXMLChild( psTree, psMD ); + } + + if( strlen(GetDescription()) > 0 ) + CPLSetXMLValue( psTree, "Description", GetDescription() ); + + if( bNoDataValueSet ) + { + if (CPLIsNan(dfNoDataValue)) + CPLSetXMLValue( psTree, "NoDataValue", "nan"); + else + CPLSetXMLValue( psTree, "NoDataValue", + CPLSPrintf( "%.14E", dfNoDataValue ) ); + } + + if( bHideNoDataValue ) + CPLSetXMLValue( psTree, "HideNoDataValue", + CPLSPrintf( "%d", bHideNoDataValue ) ); + + if( pszUnitType != NULL ) + CPLSetXMLValue( psTree, "UnitType", pszUnitType ); + + if( dfOffset != 0.0 ) + CPLSetXMLValue( psTree, "Offset", + CPLSPrintf( "%.16g", dfOffset ) ); + + if( dfScale != 1.0 ) + CPLSetXMLValue( psTree, "Scale", + CPLSPrintf( "%.16g", dfScale ) ); + + if( eColorInterp != GCI_Undefined ) + CPLSetXMLValue( psTree, "ColorInterp", + GDALGetColorInterpretationName( eColorInterp ) ); + +/* -------------------------------------------------------------------- */ +/* Category names. */ +/* -------------------------------------------------------------------- */ + if( papszCategoryNames != NULL ) + { + CPLXMLNode *psCT_XML = CPLCreateXMLNode( psTree, CXT_Element, + "CategoryNames" ); + CPLXMLNode* psLastChild = NULL; + + for( int iEntry=0; papszCategoryNames[iEntry] != NULL; iEntry++ ) + { + CPLXMLNode *psNode = CPLCreateXMLElementAndValue( NULL, "Category", + papszCategoryNames[iEntry] ); + if( psLastChild == NULL ) + psCT_XML->psChild = psNode; + else + psLastChild->psNext = psNode; + psLastChild = psNode; + } + } + +/* -------------------------------------------------------------------- */ +/* Histograms. */ +/* -------------------------------------------------------------------- */ + if( psSavedHistograms != NULL ) + CPLAddXMLChild( psTree, CPLCloneXMLTree( psSavedHistograms ) ); + +/* -------------------------------------------------------------------- */ +/* Color Table. */ +/* -------------------------------------------------------------------- */ + if( poColorTable != NULL ) + { + CPLXMLNode *psCT_XML = CPLCreateXMLNode( psTree, CXT_Element, + "ColorTable" ); + CPLXMLNode* psLastChild = NULL; + + for( int iEntry=0; iEntry < poColorTable->GetColorEntryCount(); + iEntry++ ) + { + GDALColorEntry sEntry; + CPLXMLNode *psEntry_XML = CPLCreateXMLNode( NULL, CXT_Element, + "Entry" ); + if( psLastChild == NULL ) + psCT_XML->psChild = psEntry_XML; + else + psLastChild->psNext = psEntry_XML; + psLastChild = psEntry_XML; + + poColorTable->GetColorEntryAsRGB( iEntry, &sEntry ); + + CPLSetXMLValue( psEntry_XML, "#c1", CPLSPrintf("%d",sEntry.c1) ); + CPLSetXMLValue( psEntry_XML, "#c2", CPLSPrintf("%d",sEntry.c2) ); + CPLSetXMLValue( psEntry_XML, "#c3", CPLSPrintf("%d",sEntry.c3) ); + CPLSetXMLValue( psEntry_XML, "#c4", CPLSPrintf("%d",sEntry.c4) ); + } + } + +/* ==================================================================== */ +/* Overviews */ +/* ==================================================================== */ + + for( int iOvr = 0; iOvr < (int)apoOverviews.size(); iOvr ++ ) + { + CPLXMLNode *psOVR_XML = CPLCreateXMLNode( psTree, CXT_Element, + "Overview" ); + + int bRelativeToVRT; + const char *pszRelativePath; + VSIStatBufL sStat; + + if( VSIStatExL( apoOverviews[iOvr].osFilename, &sStat, VSI_STAT_EXISTS_FLAG ) != 0 ) + { + pszRelativePath = apoOverviews[iOvr].osFilename; + bRelativeToVRT = FALSE; + } + else + { + pszRelativePath = + CPLExtractRelativePath( pszVRTPath, apoOverviews[iOvr].osFilename, + &bRelativeToVRT ); + } + + CPLSetXMLValue( psOVR_XML, "SourceFilename", pszRelativePath ); + + CPLCreateXMLNode( + CPLCreateXMLNode( CPLGetXMLNode( psOVR_XML, "SourceFilename" ), + CXT_Attribute, "relativeToVRT" ), + CXT_Text, bRelativeToVRT ? "1" : "0" ); + + CPLSetXMLValue( psOVR_XML, "SourceBand", + CPLSPrintf("%d",apoOverviews[iOvr].nBand) ); + } + +/* ==================================================================== */ +/* Mask band (specific to that raster band) */ +/* ==================================================================== */ + + if( poMaskBand != NULL ) + { + CPLXMLNode *psBandTree = + poMaskBand->SerializeToXML(pszVRTPath); + + if( psBandTree != NULL ) + { + CPLXMLNode *psMaskBandElement = CPLCreateXMLNode( psTree, CXT_Element, + "MaskBand" ); + CPLAddXMLChild( psMaskBandElement, psBandTree ); + } + } + + return psTree; +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr VRTRasterBand::SetNoDataValue( double dfNewValue ) + +{ + bNoDataValueSet = TRUE; + dfNoDataValue = dfNewValue; + + ((VRTDataset *)poDS)->SetNeedsFlush(); + + return CE_None; +} + +/************************************************************************/ +/* UnsetNoDataValue() */ +/************************************************************************/ + +CPLErr VRTRasterBand::UnsetNoDataValue() +{ + bNoDataValueSet = FALSE; + dfNoDataValue = -10000.0; + + ((VRTDataset *)poDS)->SetNeedsFlush(); + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double VRTRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( pbSuccess ) + *pbSuccess = bNoDataValueSet && !bHideNoDataValue; + + return dfNoDataValue; +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr VRTRasterBand::SetColorTable( GDALColorTable *poTableIn ) + +{ + if( poColorTable != NULL ) + { + delete poColorTable; + poColorTable = NULL; + } + + if( poTableIn ) + { + poColorTable = poTableIn->Clone(); + eColorInterp = GCI_PaletteIndex; + } + + ((VRTDataset *)poDS)->SetNeedsFlush(); + + return CE_None; +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *VRTRasterBand::GetColorTable() + +{ + return poColorTable; +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +CPLErr VRTRasterBand::SetColorInterpretation( GDALColorInterp eInterpIn ) + +{ + ((VRTDataset *)poDS)->SetNeedsFlush(); + + eColorInterp = eInterpIn; + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp VRTRasterBand::GetColorInterpretation() + +{ + return eColorInterp; +} + +/************************************************************************/ +/* GetHistogram() */ +/************************************************************************/ + +CPLErr VRTRasterBand::GetHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ +/* -------------------------------------------------------------------- */ +/* Check if we have a matching histogram. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psHistItem; + + psHistItem = PamFindMatchingHistogram( psSavedHistograms, + dfMin, dfMax, nBuckets, + bIncludeOutOfRange, bApproxOK ); + if( psHistItem != NULL ) + { + GUIntBig *panTempHist = NULL; + + if( PamParseHistogram( psHistItem, &dfMin, &dfMax, &nBuckets, + &panTempHist, + &bIncludeOutOfRange, &bApproxOK ) ) + { + memcpy( panHistogram, panTempHist, sizeof(GUIntBig) * nBuckets ); + CPLFree( panTempHist ); + return CE_None; + } + } + +/* -------------------------------------------------------------------- */ +/* We don't have an existing histogram matching the request, so */ +/* generate one manually. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + eErr = GDALRasterBand::GetHistogram( dfMin, dfMax, + nBuckets, panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Save an XML description of this histogram. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None ) + { + CPLXMLNode *psXMLHist; + + psXMLHist = PamHistogramToXMLTree( dfMin, dfMax, nBuckets, + panHistogram, + bIncludeOutOfRange, bApproxOK ); + if( psXMLHist != NULL ) + { + ((VRTDataset *) poDS)->SetNeedsFlush(); + + if( psSavedHistograms == NULL ) + psSavedHistograms = CPLCreateXMLNode( NULL, CXT_Element, + "Histograms" ); + + CPLAddXMLChild( psSavedHistograms, psXMLHist ); + } + } + + return eErr; +} + +/************************************************************************/ +/* SetDefaultHistogram() */ +/************************************************************************/ + +CPLErr VRTRasterBand::SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram) + +{ + CPLXMLNode *psNode; + +/* -------------------------------------------------------------------- */ +/* Do we have a matching histogram we should replace? */ +/* -------------------------------------------------------------------- */ + psNode = PamFindMatchingHistogram( psSavedHistograms, + dfMin, dfMax, nBuckets, + TRUE, TRUE ); + if( psNode != NULL ) + { + /* blow this one away */ + CPLRemoveXMLChild( psSavedHistograms, psNode ); + CPLDestroyXMLNode( psNode ); + } + +/* -------------------------------------------------------------------- */ +/* Translate into a histogram XML tree. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psHistItem; + + psHistItem = PamHistogramToXMLTree( dfMin, dfMax, nBuckets, + panHistogram, TRUE, FALSE ); + if( psHistItem == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Insert our new default histogram at the front of the */ +/* histogram list so that it will be the default histogram. */ +/* -------------------------------------------------------------------- */ + ((VRTDataset *) poDS)->SetNeedsFlush(); + + if( psSavedHistograms == NULL ) + psSavedHistograms = CPLCreateXMLNode( NULL, CXT_Element, + "Histograms" ); + + psHistItem->psNext = psSavedHistograms->psChild; + psSavedHistograms->psChild = psHistItem; + + return CE_None; +} + +/************************************************************************/ +/* GetDefaultHistogram() */ +/************************************************************************/ + +CPLErr +VRTRasterBand::GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig **ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + if( psSavedHistograms != NULL ) + { + CPLXMLNode *psXMLHist; + + for( psXMLHist = psSavedHistograms->psChild; + psXMLHist != NULL; psXMLHist = psXMLHist->psNext ) + { + int bApprox, bIncludeOutOfRange; + + if( psXMLHist->eType != CXT_Element + || !EQUAL(psXMLHist->pszValue,"HistItem") ) + continue; + + if( PamParseHistogram( psXMLHist, pdfMin, pdfMax, pnBuckets, + ppanHistogram, &bIncludeOutOfRange, + &bApprox ) ) + return CE_None; + else + return CE_Failure; + } + } + + return GDALRasterBand::GetDefaultHistogram( pdfMin, pdfMax, pnBuckets, + ppanHistogram, bForce, + pfnProgress,pProgressData); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +void VRTRasterBand::GetFileList(char*** ppapszFileList, int *pnSize, + int *pnMaxSize, CPLHashSet* hSetFiles) +{ + for( unsigned int iOver = 0; iOver < apoOverviews.size(); iOver++ ) + { + CPLString &osFilename = apoOverviews[iOver].osFilename; + +/* -------------------------------------------------------------------- */ +/* Is the filename even a real filesystem object? */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStat; + if( VSIStatL( osFilename, &sStat ) != 0 ) + return; + +/* -------------------------------------------------------------------- */ +/* Is it already in the list ? */ +/* -------------------------------------------------------------------- */ + if( CPLHashSetLookup(hSetFiles, osFilename) != NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Grow array if necessary */ +/* -------------------------------------------------------------------- */ + if (*pnSize + 1 >= *pnMaxSize) + { + *pnMaxSize = 2 + 2 * (*pnMaxSize); + *ppapszFileList = (char **) CPLRealloc( + *ppapszFileList, sizeof(char*) * (*pnMaxSize) ); + } + +/* -------------------------------------------------------------------- */ +/* Add the string to the list */ +/* -------------------------------------------------------------------- */ + (*ppapszFileList)[*pnSize] = CPLStrdup(osFilename); + (*ppapszFileList)[(*pnSize + 1)] = NULL; + CPLHashSetInsert(hSetFiles, (*ppapszFileList)[*pnSize]); + + (*pnSize) ++; + } +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int VRTRasterBand::GetOverviewCount() + +{ + if( apoOverviews.size() > 0 ) + return apoOverviews.size(); + else + return GDALRasterBand::GetOverviewCount(); +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *VRTRasterBand::GetOverview( int iOverview ) + +{ + if( apoOverviews.size() > 0 ) + { + if( iOverview < 0 || iOverview >= (int) apoOverviews.size() ) + return NULL; + + if( apoOverviews[iOverview].poBand == NULL + && !apoOverviews[iOverview].bTriedToOpen ) + { + apoOverviews[iOverview].bTriedToOpen = TRUE; + + GDALDataset *poSrcDS = (GDALDataset *) + GDALOpenShared( apoOverviews[iOverview].osFilename, GA_ReadOnly ); + + if( poSrcDS == NULL ) + return NULL; + + apoOverviews[iOverview].poBand = poSrcDS->GetRasterBand( + apoOverviews[iOverview].nBand ); + + if (apoOverviews[iOverview].poBand == NULL) + { + GDALClose( (GDALDatasetH)poSrcDS ); + } + } + + return apoOverviews[iOverview].poBand; + } + else + return GDALRasterBand::GetOverview( iOverview ); +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +void VRTRasterBand::SetDescription(const char* pszDescription) + +{ + ((VRTDataset *)poDS)->SetNeedsFlush(); + + GDALRasterBand::SetDescription(pszDescription); +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +CPLErr VRTRasterBand::CreateMaskBand( int nFlags ) +{ + VRTDataset* poGDS = (VRTDataset *)poDS; + + if (poGDS->poMaskBand) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot create mask band at raster band level when a dataset mask band already exists." ); + return CE_Failure; + } + + if (poMaskBand != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "This VRT band has already a mask band"); + return CE_Failure; + } + + if ((nFlags & GMF_PER_DATASET) != 0) + return poGDS->CreateMaskBand(nFlags); + + SetMaskBand(new VRTSourcedRasterBand( poGDS, 0 )); + + return CE_None; +} + +/************************************************************************/ +/* GetMaskBand() */ +/************************************************************************/ + +GDALRasterBand* VRTRasterBand::GetMaskBand() +{ + VRTDataset* poGDS = (VRTDataset *)poDS; + if (poGDS->poMaskBand) + return poGDS->poMaskBand; + else if (poMaskBand) + return poMaskBand; + else + return GDALRasterBand::GetMaskBand(); +} + +/************************************************************************/ +/* GetMaskFlags() */ +/************************************************************************/ + +int VRTRasterBand::GetMaskFlags() +{ + VRTDataset* poGDS = (VRTDataset *)poDS; + if (poGDS->poMaskBand) + return GMF_PER_DATASET; + else if (poMaskBand) + return 0; + else + return GDALRasterBand::GetMaskFlags(); +} + +/************************************************************************/ +/* SetMaskBand() */ +/************************************************************************/ + +void VRTRasterBand::SetMaskBand(VRTRasterBand* poMaskBand) +{ + delete this->poMaskBand; + this->poMaskBand = poMaskBand; + poMaskBand->SetIsMaskBand(); +} + +/************************************************************************/ +/* SetIsMaskBand() */ +/************************************************************************/ + +void VRTRasterBand::SetIsMaskBand() +{ + nBand = 0; + bIsMaskBand = TRUE; +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int VRTRasterBand::CloseDependentDatasets() +{ + return FALSE; +} diff --git a/bazaar/plugin/gdal/frmts/vrt/vrtrawrasterband.cpp b/bazaar/plugin/gdal/frmts/vrt/vrtrawrasterband.cpp new file mode 100644 index 000000000..7b69da729 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/vrtrawrasterband.cpp @@ -0,0 +1,483 @@ +/****************************************************************************** + * $Id: vrtrawrasterband.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: Virtual GDAL Datasets + * Purpose: Implementation of VRTRawRasterBand + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "vrtdataset.h" +#include "cpl_minixml.h" +#include "cpl_string.h" +#include "rawdataset.h" + +CPL_CVSID("$Id: vrtrawrasterband.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* VRTRawRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VRTRawRasterBand() */ +/************************************************************************/ + +VRTRawRasterBand::VRTRawRasterBand( GDALDataset *poDS, int nBand, + GDALDataType eType ) + +{ + Initialize( poDS->GetRasterXSize(), poDS->GetRasterYSize() ); + + this->poDS = poDS; + this->nBand = nBand; + + if( eType != GDT_Unknown ) + this->eDataType = eType; + + poRawRaster = NULL; + pszSourceFilename = NULL; +} + +/************************************************************************/ +/* ~VRTRawRasterBand() */ +/************************************************************************/ + +VRTRawRasterBand::~VRTRawRasterBand() + +{ + FlushCache(); + ClearRawLink(); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr VRTRawRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg) +{ + if( poRawRaster == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No raw raster band configured on VRTRawRasterBand." ); + return CE_Failure; + } + + if( eRWFlag == GF_Write && eAccess == GA_ReadOnly ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Attempt to write to read only dataset in" + "VRTRawRasterBand::IRasterIO().\n" ); + + return( CE_Failure ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have overviews that would be appropriate to satisfy */ +/* this request? */ +/* -------------------------------------------------------------------- */ + if( (nBufXSize < nXSize || nBufYSize < nYSize) + && GetOverviewCount() > 0 ) + { + if( OverviewRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, psExtraArg ) == CE_None ) + return CE_None; + } + + poRawRaster->SetAccess(eAccess); + + return poRawRaster->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, psExtraArg ); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr VRTRawRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + if( poRawRaster == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No raw raster band configured on VRTRawRasterBand." ); + return CE_Failure; + } + + return poRawRaster->ReadBlock( nBlockXOff, nBlockYOff, pImage ); +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr VRTRawRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + if( poRawRaster == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No raw raster band configured on VRTRawRasterBand." ); + return CE_Failure; + } + + poRawRaster->SetAccess(eAccess); + + return poRawRaster->WriteBlock( nBlockXOff, nBlockYOff, pImage ); +} + +/************************************************************************/ +/* SetRawLink() */ +/************************************************************************/ + +CPLErr VRTRawRasterBand::SetRawLink( const char *pszFilename, + const char *pszVRTPath, + int bRelativeToVRT, + vsi_l_offset nImageOffset, + int nPixelOffset, int nLineOffset, + const char *pszByteOrder ) + +{ + ClearRawLink(); + + ((VRTDataset *)poDS)->SetNeedsFlush(); + +/* -------------------------------------------------------------------- */ +/* Prepare filename. */ +/* -------------------------------------------------------------------- */ + char *pszExpandedFilename = NULL; + + if( pszFilename == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Missing element in VRTRasterBand." ); + return CE_Failure; + } + + if( pszVRTPath != NULL && bRelativeToVRT ) + { + pszExpandedFilename = CPLStrdup( + CPLProjectRelativeFilename( pszVRTPath, pszFilename ) ); + } + else + pszExpandedFilename = CPLStrdup( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Try and open the file. We always use the large file API. */ +/* -------------------------------------------------------------------- */ + FILE *fp = CPLOpenShared( pszExpandedFilename, "rb+", TRUE ); + + if( fp == NULL ) + fp = CPLOpenShared( pszExpandedFilename, "rb", TRUE ); + + if( fp == NULL && ((VRTDataset *)poDS)->GetAccess() == GA_Update ) + { + fp = CPLOpenShared( pszExpandedFilename, "wb+", TRUE ); + } + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open %s.\n%s", + pszExpandedFilename, + VSIStrerror( errno ) ); + + CPLFree( pszExpandedFilename ); + return CE_Failure; + } + + CPLFree( pszExpandedFilename ); + + pszSourceFilename = CPLStrdup(pszFilename); + this->bRelativeToVRT = bRelativeToVRT; + +/* -------------------------------------------------------------------- */ +/* Work out if we are in native mode or not. */ +/* -------------------------------------------------------------------- */ + int bNative = TRUE; + + if( pszByteOrder != NULL ) + { + if( EQUAL(pszByteOrder,"LSB") ) + bNative = CPL_IS_LSB; + else if( EQUAL(pszByteOrder,"MSB") ) + bNative = !CPL_IS_LSB; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Illegal ByteOrder value '%s', should be LSB or MSB.", + pszByteOrder ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding RawRasterBand. */ +/* -------------------------------------------------------------------- */ + poRawRaster = new RawRasterBand( fp, nImageOffset, nPixelOffset, + nLineOffset, GetRasterDataType(), + bNative, GetXSize(), GetYSize(), TRUE ); + +/* -------------------------------------------------------------------- */ +/* Reset block size to match the raw raster. */ +/* -------------------------------------------------------------------- */ + poRawRaster->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + return CE_None; +} + +/************************************************************************/ +/* ClearRawLink() */ +/************************************************************************/ + +void VRTRawRasterBand::ClearRawLink() + +{ + if( poRawRaster != NULL ) + { + VSILFILE* fp = poRawRaster->GetFPL(); + delete poRawRaster; + poRawRaster = NULL; + /* We close the file after deleting the raster band */ + /* since data can be flushed in the destructor */ + if( fp != NULL ) + { + CPLCloseShared( (FILE*) fp ); + } + } + CPLFree( pszSourceFilename ); + pszSourceFilename = NULL; +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr VRTRawRasterBand::XMLInit( CPLXMLNode * psTree, + const char *pszVRTPath ) + +{ + CPLErr eErr; + + eErr = VRTRasterBand::XMLInit( psTree, pszVRTPath ); + if( eErr != CE_None ) + return eErr; + + +/* -------------------------------------------------------------------- */ +/* Validate a bit. */ +/* -------------------------------------------------------------------- */ + if( psTree == NULL || psTree->eType != CXT_Element + || !EQUAL(psTree->pszValue,"VRTRasterBand") + || !EQUAL(CPLGetXMLValue(psTree,"subClass",""),"VRTRawRasterBand") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid node passed to VRTRawRasterBand::XMLInit()." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Prepare filename. */ +/* -------------------------------------------------------------------- */ + int bRelativeToVRT; + + const char *pszFilename = + CPLGetXMLValue(psTree, "SourceFilename", NULL); + + if( pszFilename == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Missing element in VRTRasterBand." ); + return CE_Failure; + } + + bRelativeToVRT = atoi(CPLGetXMLValue(psTree,"SourceFilename.relativeToVRT", + "1")); + +/* -------------------------------------------------------------------- */ +/* Collect layout information. */ +/* -------------------------------------------------------------------- */ + vsi_l_offset nImageOffset; + int nPixelOffset, nLineOffset; + int nWordDataSize = GDALGetDataTypeSize( GetRasterDataType() ) / 8; + + nImageOffset = CPLScanUIntBig( + CPLGetXMLValue( psTree, "ImageOffset", "0"), 20); + + if( CPLGetXMLValue( psTree, "PixelOffset", NULL ) == NULL ) + nPixelOffset = nWordDataSize; + else + nPixelOffset = atoi(CPLGetXMLValue( psTree, "PixelOffset", "0") ); + if (nPixelOffset <= 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value for element : %d", nPixelOffset ); + return CE_Failure; + } + + if( CPLGetXMLValue( psTree, "LineOffset", NULL ) == NULL ) + nLineOffset = nWordDataSize * GetXSize(); + else + nLineOffset = atoi(CPLGetXMLValue( psTree, "LineOffset", "0") ); + + const char *pszByteOrder = CPLGetXMLValue( psTree, "ByteOrder", NULL ); + +/* -------------------------------------------------------------------- */ +/* Open the file, and setup the raw layer access to the data. */ +/* -------------------------------------------------------------------- */ + return SetRawLink( pszFilename, pszVRTPath, bRelativeToVRT, + nImageOffset, nPixelOffset, nLineOffset, + pszByteOrder ); +} + +/************************************************************************/ +/* VRTRawStripSpace() */ +/************************************************************************/ + +static const char* VRTRawStripSpace(const char* pszStr) +{ + while( *pszStr == ' ' ) + pszStr ++; + return pszStr; +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *VRTRawRasterBand::SerializeToXML( const char *pszVRTPath ) + +{ + CPLXMLNode *psTree; + + psTree = VRTRasterBand::SerializeToXML( pszVRTPath ); + +/* -------------------------------------------------------------------- */ +/* Set subclass. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLNode( + CPLCreateXMLNode( psTree, CXT_Attribute, "subClass" ), + CXT_Text, "VRTRawRasterBand" ); + +/* -------------------------------------------------------------------- */ +/* Setup the filename with relative flag. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psNode; + + psNode = + CPLCreateXMLElementAndValue( psTree, "SourceFilename", + pszSourceFilename ); + + CPLCreateXMLNode( + CPLCreateXMLNode( psNode, CXT_Attribute, "relativeToVRT" ), + CXT_Text, bRelativeToVRT ? "1" : "0" ); + +/* -------------------------------------------------------------------- */ +/* We can't set the layout if there is no open rawband. */ +/* -------------------------------------------------------------------- */ + if( poRawRaster == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "VRTRawRasterBand::SerializeToXML() fails because poRawRaster is NULL." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Set other layout information. */ +/* -------------------------------------------------------------------- */ + char szOffset[22]; + + CPLPrintUIntBig(szOffset, poRawRaster->GetImgOffset(), sizeof(szOffset)-1); + szOffset[sizeof(szOffset)-1] = '\0'; + CPLCreateXMLElementAndValue(psTree, "ImageOffset", VRTRawStripSpace(szOffset)); + + CPLPrintUIntBig(szOffset, poRawRaster->GetPixelOffset(),sizeof(szOffset)-1); + szOffset[sizeof(szOffset)-1] = '\0'; + CPLCreateXMLElementAndValue(psTree, "PixelOffset", VRTRawStripSpace(szOffset)); + + CPLPrintUIntBig(szOffset, poRawRaster->GetLineOffset(), sizeof(szOffset)-1); + szOffset[sizeof(szOffset)-1] = '\0'; + CPLCreateXMLElementAndValue(psTree, "LineOffset", VRTRawStripSpace(szOffset)); + +#if CPL_IS_LSB == 1 + if( poRawRaster->GetNativeOrder() ) +#else + if( !poRawRaster->GetNativeOrder() ) +#endif + CPLCreateXMLElementAndValue( psTree, "ByteOrder", "LSB" ); + else + CPLCreateXMLElementAndValue( psTree, "ByteOrder", "MSB" ); + + return psTree; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +void VRTRawRasterBand::GetFileList(char*** ppapszFileList, int *pnSize, + int *pnMaxSize, CPLHashSet* hSetFiles) +{ + if (pszSourceFilename == NULL) + return; + +/* -------------------------------------------------------------------- */ +/* Is it already in the list ? */ +/* -------------------------------------------------------------------- */ + if( CPLHashSetLookup(hSetFiles, pszSourceFilename) != NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Grow array if necessary */ +/* -------------------------------------------------------------------- */ + if (*pnSize + 1 >= *pnMaxSize) + { + *pnMaxSize = 2 + 2 * (*pnMaxSize); + *ppapszFileList = (char **) CPLRealloc( + *ppapszFileList, sizeof(char*) * (*pnMaxSize) ); + } + +/* -------------------------------------------------------------------- */ +/* Add the string to the list */ +/* -------------------------------------------------------------------- */ + (*ppapszFileList)[*pnSize] = CPLStrdup(pszSourceFilename); + (*ppapszFileList)[(*pnSize + 1)] = NULL; + CPLHashSetInsert(hSetFiles, (*ppapszFileList)[*pnSize]); + + (*pnSize) ++; + + VRTRasterBand::GetFileList( ppapszFileList, pnSize, + pnMaxSize, hSetFiles); +} diff --git a/bazaar/plugin/gdal/frmts/vrt/vrtsourcedrasterband.cpp b/bazaar/plugin/gdal/frmts/vrt/vrtsourcedrasterband.cpp new file mode 100644 index 000000000..1c8e5a911 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/vrtsourcedrasterband.cpp @@ -0,0 +1,1408 @@ +/****************************************************************************** + * $Id: vrtsourcedrasterband.cpp 29161 2015-05-06 10:18:19Z rouault $ + * + * Project: Virtual GDAL Datasets + * Purpose: Implementation of VRTSourcedRasterBand + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "vrtdataset.h" +#include "cpl_minixml.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: vrtsourcedrasterband.cpp 29161 2015-05-06 10:18:19Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* VRTSourcedRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VRTSourcedRasterBand() */ +/************************************************************************/ + +VRTSourcedRasterBand::VRTSourcedRasterBand( GDALDataset *poDS, int nBand ) + +{ + Initialize( poDS->GetRasterXSize(), poDS->GetRasterYSize() ); + + this->poDS = poDS; + this->nBand = nBand; +} + +/************************************************************************/ +/* VRTSourcedRasterBand() */ +/************************************************************************/ + +VRTSourcedRasterBand::VRTSourcedRasterBand( GDALDataType eType, + int nXSize, int nYSize ) + +{ + Initialize( nXSize, nYSize ); + + eDataType = eType; +} + +/************************************************************************/ +/* VRTSourcedRasterBand() */ +/************************************************************************/ + +VRTSourcedRasterBand::VRTSourcedRasterBand( GDALDataset *poDS, int nBand, + GDALDataType eType, + int nXSize, int nYSize ) + +{ + Initialize( nXSize, nYSize ); + + this->poDS = poDS; + this->nBand = nBand; + + eDataType = eType; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void VRTSourcedRasterBand::Initialize( int nXSize, int nYSize ) + +{ + VRTRasterBand::Initialize( nXSize, nYSize ); + + nSources = 0; + papoSources = NULL; + bEqualAreas = FALSE; + nRecursionCounter = 0; + papszSourceList = NULL; +} + +/************************************************************************/ +/* ~VRTSourcedRasterBand() */ +/************************************************************************/ + +VRTSourcedRasterBand::~VRTSourcedRasterBand() + +{ + CloseDependentDatasets(); + CSLDestroy(papszSourceList); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr VRTSourcedRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + int iSource; + CPLErr eErr = CE_None; + + if( eRWFlag == GF_Write ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Writing through VRTSourcedRasterBand is not supported." ); + return CE_Failure; + } + + /* When using GDALProxyPoolDataset for sources, the recusion will not be */ + /* detected at VRT opening but when doing RasterIO. As the proxy pool will */ + /* return the already opened dataset, we can just test a member variable. */ + /* We allow 1, since IRasterIO() can be called from ComputeStatistics(), which */ + /* itselfs increments the recursion counter */ + if ( nRecursionCounter > 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "VRTSourcedRasterBand::IRasterIO() called recursively on the same band. " + "It looks like the VRT is referencing itself." ); + return CE_Failure; + } + +/* ==================================================================== */ +/* Do we have overviews that would be appropriate to satisfy */ +/* this request? */ +/* ==================================================================== */ + if( (nBufXSize < nXSize || nBufYSize < nYSize) + && GetOverviewCount() > 0 ) + { + if( OverviewRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, psExtraArg ) == CE_None ) + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Initialize the buffer to some background value. Use the */ +/* nodata value if available. */ +/* -------------------------------------------------------------------- */ + if ( nPixelSpace == GDALGetDataTypeSize(eBufType)/8 && + (!bNoDataValueSet || (!CPLIsNan(dfNoDataValue) && dfNoDataValue == 0)) ) + { + if (nLineSpace == nBufXSize * nPixelSpace) + { + memset( pData, 0, (GIntBig)nBufYSize * nLineSpace ); + } + else + { + int iLine; + for( iLine = 0; iLine < nBufYSize; iLine++ ) + { + memset( ((GByte*)pData) + (GIntBig)iLine * nLineSpace, 0, nBufXSize * nPixelSpace ); + } + } + } + else if ( !bEqualAreas || bNoDataValueSet ) + { + double dfWriteValue = 0.0; + int iLine; + + if( bNoDataValueSet ) + dfWriteValue = dfNoDataValue; + + for( iLine = 0; iLine < nBufYSize; iLine++ ) + { + GDALCopyWords( &dfWriteValue, GDT_Float64, 0, + ((GByte *)pData) + (GIntBig)nLineSpace * iLine, + eBufType, nPixelSpace, nBufXSize ); + } + } + + nRecursionCounter ++; + + GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress; + void *pProgressDataGlobal = psExtraArg->pProgressData; + +/* -------------------------------------------------------------------- */ +/* Overlay each source in turn over top this. */ +/* -------------------------------------------------------------------- */ + for( iSource = 0; eErr == CE_None && iSource < nSources; iSource++ ) + { + psExtraArg->pfnProgress = GDALScaledProgress; + psExtraArg->pProgressData = + GDALCreateScaledProgress( 1.0 * iSource / nSources, + 1.0 * (iSource + 1) / nSources, + pfnProgressGlobal, + pProgressDataGlobal ); + if( psExtraArg->pProgressData == NULL ) + psExtraArg->pfnProgress = NULL; + + eErr = + papoSources[iSource]->RasterIO( nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, + psExtraArg); + + GDALDestroyScaledProgress( psExtraArg->pProgressData ); + } + + psExtraArg->pfnProgress = pfnProgressGlobal; + psExtraArg->pProgressData = pProgressDataGlobal; + + nRecursionCounter --; + + return eErr; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr VRTSourcedRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + int nPixelSize = GDALGetDataTypeSize(eDataType)/8; + int nReadXSize, nReadYSize; + + if( (nBlockXOff+1) * nBlockXSize > GetXSize() ) + nReadXSize = GetXSize() - nBlockXOff * nBlockXSize; + else + nReadXSize = nBlockXSize; + + if( (nBlockYOff+1) * nBlockYSize > GetYSize() ) + nReadYSize = GetYSize() - nBlockYOff * nBlockYSize; + else + nReadYSize = nBlockYSize; + + GDALRasterIOExtraArg sExtraArg; + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + + return IRasterIO( GF_Read, + nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, + nReadXSize, nReadYSize, + pImage, nReadXSize, nReadYSize, eDataType, + nPixelSize, nPixelSize * nBlockXSize, &sExtraArg ); +} + + +/************************************************************************/ +/* CanUseSourcesMinMaxImplementations() */ +/************************************************************************/ + +int VRTSourcedRasterBand::CanUseSourcesMinMaxImplementations() +{ + const char* pszUseSources = CPLGetConfigOption("VRT_MIN_MAX_FROM_SOURCES", NULL); + if( pszUseSources ) + return CSLTestBoolean(pszUseSources); + + // Use heuristics to determine if we are going to use the source GetMinimum() + // or GetMaximum() implementation: all the sources must be "simple" sources + // with a dataset description that match a "regular" file on the filesystem, + // whose open time and GetMinimum()/GetMaximum() implementations we hope to + // be fast enough. + // In case of doubt return FALSE + for( int iSource = 0; iSource < nSources; iSource++ ) + { + if( !(papoSources[iSource]->IsSimpleSource()) ) + return FALSE; + VRTSimpleSource* poSimpleSource = (VRTSimpleSource*) papoSources[iSource]; + GDALRasterBand* poBand = poSimpleSource->GetBand(); + if( poBand == NULL ) + return FALSE; + if( poBand->GetDataset() == NULL ) + return FALSE; + const char* pszFilename = poBand->GetDataset()->GetDescription(); + if( pszFilename == NULL ) + return FALSE; + /* /vsimem/ should be fast */ + if( strncmp(pszFilename, "/vsimem/", 8) == 0 ) + continue; + /* but not other /vsi filesystems */ + if( strncmp(pszFilename, "/vsi", 4) == 0 ) + return FALSE; + int i = 0; + char ch; + /* We will assume that filenames that are only with ascii characters */ + /* are real filenames and so we will not try to 'stat' them */ + for( i = 0; (ch = pszFilename[i]) != '\0'; i++ ) + { + if( !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || ch == ':' || ch == '/' || ch == '\\' || + ch == ' ' || ch == '.') ) + break; + } + if( ch != '\0' ) + { + /* Otherwise do a real filesystem check */ + VSIStatBuf sStat; + if( VSIStat(pszFilename, &sStat) != 0 ) + return FALSE; + } + } + return TRUE; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double VRTSourcedRasterBand::GetMinimum( int *pbSuccess ) +{ + const char *pszValue = NULL; + + if( !CanUseSourcesMinMaxImplementations() ) + return GDALRasterBand::GetMinimum(pbSuccess); + + if( (pszValue = GetMetadataItem("STATISTICS_MINIMUM")) != NULL ) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return CPLAtofM(pszValue); + } + + if ( nRecursionCounter > 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "VRTSourcedRasterBand::GetMinimum() called recursively on the same band. " + "It looks like the VRT is referencing itself." ); + if( pbSuccess != NULL ) + *pbSuccess = FALSE; + return 0.0; + } + nRecursionCounter ++; + + double dfMin = 0; + for( int iSource = 0; iSource < nSources; iSource++ ) + { + int bSuccess = FALSE; + double dfSourceMin = papoSources[iSource]->GetMinimum(GetXSize(), GetYSize(), &bSuccess); + if (!bSuccess) + { + dfMin = GDALRasterBand::GetMinimum(pbSuccess); + nRecursionCounter --; + return dfMin; + } + + if (iSource == 0 || dfSourceMin < dfMin) + dfMin = dfSourceMin; + } + + nRecursionCounter --; + + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return dfMin; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double VRTSourcedRasterBand::GetMaximum(int *pbSuccess ) +{ + const char *pszValue = NULL; + + if( !CanUseSourcesMinMaxImplementations() ) + return GDALRasterBand::GetMaximum(pbSuccess); + + if( (pszValue = GetMetadataItem("STATISTICS_MAXIMUM")) != NULL ) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return CPLAtofM(pszValue); + } + + if ( nRecursionCounter > 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "VRTSourcedRasterBand::GetMaximum() called recursively on the same band. " + "It looks like the VRT is referencing itself." ); + if( pbSuccess != NULL ) + *pbSuccess = FALSE; + return 0.0; + } + nRecursionCounter ++; + + double dfMax = 0; + for( int iSource = 0; iSource < nSources; iSource++ ) + { + int bSuccess = FALSE; + double dfSourceMax = papoSources[iSource]->GetMaximum(GetXSize(), GetYSize(), &bSuccess); + if (!bSuccess) + { + dfMax = GDALRasterBand::GetMaximum(pbSuccess); + nRecursionCounter --; + return dfMax; + } + + if (iSource == 0 || dfSourceMax > dfMax) + dfMax = dfSourceMax; + } + + nRecursionCounter --; + + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return dfMax; +} + +/************************************************************************/ +/* ComputeRasterMinMax() */ +/************************************************************************/ + +CPLErr VRTSourcedRasterBand::ComputeRasterMinMax( int bApproxOK, double* adfMinMax ) +{ + double dfMin = 0.0; + double dfMax = 0.0; + +/* -------------------------------------------------------------------- */ +/* Does the driver already know the min/max? */ +/* -------------------------------------------------------------------- */ + if( bApproxOK ) + { + int bSuccessMin, bSuccessMax; + + dfMin = GetMinimum( &bSuccessMin ); + dfMax = GetMaximum( &bSuccessMax ); + + if( bSuccessMin && bSuccessMax ) + { + adfMinMax[0] = dfMin; + adfMinMax[1] = dfMax; + return CE_None; + } + } + +/* -------------------------------------------------------------------- */ +/* If we have overview bands, use them for min/max. */ +/* -------------------------------------------------------------------- */ + if ( bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews() ) + { + GDALRasterBand *poBand; + + poBand = GetRasterSampleOverview( GDALSTAT_APPROX_NUMSAMPLES ); + + if ( poBand != this ) + return poBand->ComputeRasterMinMax( FALSE, adfMinMax ); + } + +/* -------------------------------------------------------------------- */ +/* Try with source bands. */ +/* -------------------------------------------------------------------- */ + if ( nRecursionCounter > 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "VRTSourcedRasterBand::ComputeRasterMinMax() called recursively on the same band. " + "It looks like the VRT is referencing itself." ); + return CE_Failure; + } + nRecursionCounter ++; + + adfMinMax[0] = 0.0; + adfMinMax[1] = 0.0; + for( int iSource = 0; iSource < nSources; iSource++ ) + { + double adfSourceMinMax[2]; + CPLErr eErr = papoSources[iSource]->ComputeRasterMinMax(GetXSize(), GetYSize(), bApproxOK, adfSourceMinMax); + if (eErr != CE_None) + { + eErr = GDALRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax); + nRecursionCounter --; + return eErr; + } + + if (iSource == 0 || adfSourceMinMax[0] < adfMinMax[0]) + adfMinMax[0] = adfSourceMinMax[0]; + if (iSource == 0 || adfSourceMinMax[1] > adfMinMax[1]) + adfMinMax[1] = adfSourceMinMax[1]; + } + + nRecursionCounter --; + + return CE_None; +} + +/************************************************************************/ +/* ComputeStatistics() */ +/************************************************************************/ + +CPLErr +VRTSourcedRasterBand::ComputeStatistics( int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + if( nSources != 1 || bNoDataValueSet ) + return GDALRasterBand::ComputeStatistics( bApproxOK, + pdfMin, pdfMax, + pdfMean, pdfStdDev, + pfnProgress, pProgressData ); + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* If we have overview bands, use them for statistics. */ +/* -------------------------------------------------------------------- */ + if( bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews() ) + { + GDALRasterBand *poBand; + + poBand = GetRasterSampleOverview( GDALSTAT_APPROX_NUMSAMPLES ); + + if( poBand != this ) + return poBand->ComputeStatistics( FALSE, + pdfMin, pdfMax, + pdfMean, pdfStdDev, + pfnProgress, pProgressData ); + } + +/* -------------------------------------------------------------------- */ +/* Try with source bands. */ +/* -------------------------------------------------------------------- */ + if ( nRecursionCounter > 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "VRTSourcedRasterBand::ComputeStatistics() called recursively on the same band. " + "It looks like the VRT is referencing itself." ); + return CE_Failure; + } + nRecursionCounter ++; + + double dfMin = 0.0, dfMax = 0.0, dfMean = 0.0, dfStdDev = 0.0; + + CPLErr eErr = papoSources[0]->ComputeStatistics(GetXSize(), GetYSize(), bApproxOK, + &dfMin, &dfMax, + &dfMean, &dfStdDev, + pfnProgress, pProgressData); + if (eErr != CE_None) + { + eErr = GDALRasterBand::ComputeStatistics(bApproxOK, + pdfMin, pdfMax, + pdfMean, pdfStdDev, + pfnProgress, pProgressData); + nRecursionCounter --; + return eErr; + } + + nRecursionCounter --; + + SetStatistics( dfMin, dfMax, dfMean, dfStdDev ); + +/* -------------------------------------------------------------------- */ +/* Record results. */ +/* -------------------------------------------------------------------- */ + if( pdfMin != NULL ) + *pdfMin = dfMin; + if( pdfMax != NULL ) + *pdfMax = dfMax; + + if( pdfMean != NULL ) + *pdfMean = dfMean; + + if( pdfStdDev != NULL ) + *pdfStdDev = dfStdDev; + + return CE_None; +} + +/************************************************************************/ +/* GetHistogram() */ +/************************************************************************/ + +CPLErr VRTSourcedRasterBand::GetHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + if( nSources != 1 ) + return GDALRasterBand::GetHistogram( dfMin, dfMax, + nBuckets, panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* If we have overviews, use them for the histogram. */ +/* -------------------------------------------------------------------- */ + if( bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews() ) + { + // FIXME: should we use the most reduced overview here or use some + // minimum number of samples like GDALRasterBand::ComputeStatistics() + // does? + GDALRasterBand *poBestOverview = GetRasterSampleOverview( 0 ); + + if( poBestOverview != this ) + { + return poBestOverview->GetHistogram( dfMin, dfMax, nBuckets, + panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); + } + } + +/* -------------------------------------------------------------------- */ +/* Try with source bands. */ +/* -------------------------------------------------------------------- */ + if ( nRecursionCounter > 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "VRTSourcedRasterBand::GetHistogram() called recursively on the same band. " + "It looks like the VRT is referencing itself." ); + return CE_Failure; + } + nRecursionCounter ++; + + CPLErr eErr = papoSources[0]->GetHistogram(GetXSize(), GetYSize(), dfMin, dfMax, nBuckets, + panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData); + if (eErr != CE_None) + { + eErr = GDALRasterBand::GetHistogram( dfMin, dfMax, + nBuckets, panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); + nRecursionCounter --; + return eErr; + } + + nRecursionCounter --; + + return CE_None; +} + +/************************************************************************/ +/* AddSource() */ +/************************************************************************/ + +CPLErr VRTSourcedRasterBand::AddSource( VRTSource *poNewSource ) + +{ + nSources++; + + papoSources = (VRTSource **) + CPLRealloc(papoSources, sizeof(void*) * nSources); + papoSources[nSources-1] = poNewSource; + + ((VRTDataset *)poDS)->SetNeedsFlush(); + + return CE_None; +} + +/************************************************************************/ +/* VRTAddSource() */ +/************************************************************************/ + +/** + * @see VRTSourcedRasterBand::AddSource(). + */ + +CPLErr CPL_STDCALL VRTAddSource( VRTSourcedRasterBandH hVRTBand, + VRTSourceH hNewSource ) +{ + VALIDATE_POINTER1( hVRTBand, "VRTAddSource", CE_Failure ); + + return ((VRTSourcedRasterBand *) hVRTBand)-> + AddSource( (VRTSource *)hNewSource ); +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr VRTSourcedRasterBand::XMLInit( CPLXMLNode * psTree, + const char *pszVRTPath ) + +{ + CPLErr eErr; + + eErr = VRTRasterBand::XMLInit( psTree, pszVRTPath ); + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Validate a bit. */ +/* -------------------------------------------------------------------- */ + if( psTree == NULL || psTree->eType != CXT_Element + || (!EQUAL(psTree->pszValue,"VRTSourcedRasterBand") + && !EQUAL(psTree->pszValue,"VRTRasterBand") + && !EQUAL(psTree->pszValue,"VRTDerivedRasterBand")) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid node passed to VRTSourcedRasterBand::XMLInit()." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Process sources. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psChild; + VRTDriver *poDriver = (VRTDriver *) GDALGetDriverByName( "VRT" ); + + for( psChild = psTree->psChild; + psChild != NULL && poDriver != NULL; + psChild = psChild->psNext) + { + VRTSource *poSource; + + if( psChild->eType != CXT_Element ) + continue; + + CPLErrorReset(); + poSource = poDriver->ParseSource( psChild, pszVRTPath ); + if( poSource != NULL ) + AddSource( poSource ); + else if( CPLGetLastErrorType() != CE_None ) + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Done. */ +/* -------------------------------------------------------------------- */ + if( nSources == 0 ) + CPLDebug( "VRT", "No valid sources found for band in VRT file:\n%s", + pszVRTPath ? pszVRTPath : "(null)" ); + + return CE_None; +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *VRTSourcedRasterBand::SerializeToXML( const char *pszVRTPath ) + +{ + CPLXMLNode *psTree; + + psTree = VRTRasterBand::SerializeToXML( pszVRTPath ); + CPLXMLNode* psLastChild = psTree->psChild; + while( psLastChild != NULL && psLastChild->psNext != NULL ) + psLastChild = psLastChild->psNext; + +/* -------------------------------------------------------------------- */ +/* Process Sources. */ +/* -------------------------------------------------------------------- */ + for( int iSource = 0; iSource < nSources; iSource++ ) + { + CPLXMLNode *psXMLSrc; + + psXMLSrc = papoSources[iSource]->SerializeToXML( pszVRTPath ); + + if( psXMLSrc != NULL ) + { + if( psLastChild == NULL ) + psTree->psChild = psXMLSrc; + else + psLastChild->psNext = psXMLSrc; + psLastChild = psXMLSrc; + } + } + + return psTree; +} + +/************************************************************************/ +/* ConfigureSource() */ +/************************************************************************/ + +void VRTSourcedRasterBand::ConfigureSource(VRTSimpleSource *poSimpleSource, + GDALRasterBand *poSrcBand, + int bAddAsMaskBand, + int nSrcXOff, int nSrcYOff, + int nSrcXSize, int nSrcYSize, + int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize) +{ +/* -------------------------------------------------------------------- */ +/* Default source and dest rectangles. */ +/* -------------------------------------------------------------------- */ + if( nSrcYSize == -1 ) + { + nSrcXOff = 0; + nSrcYOff = 0; + nSrcXSize = poSrcBand->GetXSize(); + nSrcYSize = poSrcBand->GetYSize(); + } + + if( nDstYSize == -1 ) + { + nDstXOff = 0; + nDstYOff = 0; + nDstXSize = nRasterXSize; + nDstYSize = nRasterYSize; + } + + if( bAddAsMaskBand ) + poSimpleSource->SetSrcMaskBand( poSrcBand ); + else + poSimpleSource->SetSrcBand( poSrcBand ); + poSimpleSource->SetSrcWindow( nSrcXOff, nSrcYOff, nSrcXSize, nSrcYSize ); + poSimpleSource->SetDstWindow( nDstXOff, nDstYOff, nDstXSize, nDstYSize ); + +/* -------------------------------------------------------------------- */ +/* Default source and dest rectangles. */ +/* -------------------------------------------------------------------- */ + if ( nSrcXOff == nDstXOff && nSrcYOff == nDstYOff && + nSrcXSize == nDstXSize && nSrcYSize == nRasterYSize ) + bEqualAreas = TRUE; + +/* -------------------------------------------------------------------- */ +/* If we can get the associated GDALDataset, add a reference to it.*/ +/* -------------------------------------------------------------------- */ + if( poSrcBand->GetDataset() != NULL ) + poSrcBand->GetDataset()->Reference(); + +} + +/************************************************************************/ +/* AddSimpleSource() */ +/************************************************************************/ + +CPLErr VRTSourcedRasterBand::AddSimpleSource( GDALRasterBand *poSrcBand, + int nSrcXOff, int nSrcYOff, + int nSrcXSize, int nSrcYSize, + int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + const char *pszResampling, + double dfNoDataValue ) + +{ +/* -------------------------------------------------------------------- */ +/* Create source. */ +/* -------------------------------------------------------------------- */ + VRTSimpleSource *poSimpleSource; + + if( pszResampling != NULL && EQUALN(pszResampling,"aver",4) ) + poSimpleSource = new VRTAveragedSource(); + else + { + poSimpleSource = new VRTSimpleSource(); + if( dfNoDataValue != VRT_NODATA_UNSET ) + CPLError( + CE_Warning, CPLE_AppDefined, + "NODATA setting not currently supported for nearest\n" + "neighbour sampled simple sources on Virtual Datasources." ); + } + + ConfigureSource(poSimpleSource, + poSrcBand, + FALSE, + nSrcXOff, nSrcYOff, + nSrcXSize, nSrcYSize, + nDstXOff, nDstYOff, + nDstXSize, nDstYSize); + + if( dfNoDataValue != VRT_NODATA_UNSET ) + poSimpleSource->SetNoDataValue( dfNoDataValue ); + +/* -------------------------------------------------------------------- */ +/* add to list. */ +/* -------------------------------------------------------------------- */ + return AddSource( poSimpleSource ); +} + +/************************************************************************/ +/* AddMaskBandSource() */ +/************************************************************************/ + +/* poSrcBand is not the mask band, but the band from which the mask band is taken */ +CPLErr VRTSourcedRasterBand::AddMaskBandSource( GDALRasterBand *poSrcBand, + int nSrcXOff, int nSrcYOff, + int nSrcXSize, int nSrcYSize, + int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize ) +{ +/* -------------------------------------------------------------------- */ +/* Create source. */ +/* -------------------------------------------------------------------- */ + VRTSimpleSource* poSimpleSource = new VRTSimpleSource(); + + ConfigureSource(poSimpleSource, + poSrcBand, + TRUE, + nSrcXOff, nSrcYOff, + nSrcXSize, nSrcYSize, + nDstXOff, nDstYOff, + nDstXSize, nDstYSize); + +/* -------------------------------------------------------------------- */ +/* add to list. */ +/* -------------------------------------------------------------------- */ + return AddSource( poSimpleSource ); +} + +/************************************************************************/ +/* VRTAddSimpleSource() */ +/************************************************************************/ + +/** + * @see VRTSourcedRasterBand::AddSimpleSource(). + */ + +CPLErr CPL_STDCALL VRTAddSimpleSource( VRTSourcedRasterBandH hVRTBand, + GDALRasterBandH hSrcBand, + int nSrcXOff, int nSrcYOff, + int nSrcXSize, int nSrcYSize, + int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + const char *pszResampling, + double dfNoDataValue ) +{ + VALIDATE_POINTER1( hVRTBand, "VRTAddSimpleSource", CE_Failure ); + + return ((VRTSourcedRasterBand *) hVRTBand)->AddSimpleSource( + (GDALRasterBand *)hSrcBand, + nSrcXOff, nSrcYOff, + nSrcXSize, nSrcYSize, + nDstXOff, nDstYOff, + nDstXSize, nDstYSize, + pszResampling, dfNoDataValue ); +} + +/************************************************************************/ +/* AddComplexSource() */ +/************************************************************************/ + +CPLErr VRTSourcedRasterBand::AddComplexSource( GDALRasterBand *poSrcBand, + int nSrcXOff, int nSrcYOff, + int nSrcXSize, int nSrcYSize, + int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + double dfScaleOff, + double dfScaleRatio, + double dfNoDataValue, + int nColorTableComponent) + +{ +/* -------------------------------------------------------------------- */ +/* Create source. */ +/* -------------------------------------------------------------------- */ + VRTComplexSource *poSource; + + poSource = new VRTComplexSource(); + + ConfigureSource(poSource, + poSrcBand, + FALSE, + nSrcXOff, nSrcYOff, + nSrcXSize, nSrcYSize, + nDstXOff, nDstYOff, + nDstXSize, nDstYSize); + +/* -------------------------------------------------------------------- */ +/* Set complex parameters. */ +/* -------------------------------------------------------------------- */ + if( dfNoDataValue != VRT_NODATA_UNSET ) + poSource->SetNoDataValue( dfNoDataValue ); + + if( dfScaleOff != 0.0 || dfScaleRatio != 1.0 ) + poSource->SetLinearScaling(dfScaleOff, dfScaleRatio); + + poSource->SetColorTableComponent(nColorTableComponent); + +/* -------------------------------------------------------------------- */ +/* add to list. */ +/* -------------------------------------------------------------------- */ + return AddSource( poSource ); +} + +/************************************************************************/ +/* VRTAddComplexSource() */ +/************************************************************************/ + +/** + * @see VRTSourcedRasterBand::AddComplexSource(). + */ + +CPLErr CPL_STDCALL VRTAddComplexSource( VRTSourcedRasterBandH hVRTBand, + GDALRasterBandH hSrcBand, + int nSrcXOff, int nSrcYOff, + int nSrcXSize, int nSrcYSize, + int nDstXOff, int nDstYOff, + int nDstXSize, int nDstYSize, + double dfScaleOff, + double dfScaleRatio, + double dfNoDataValue ) +{ + VALIDATE_POINTER1( hVRTBand, "VRTAddComplexSource", CE_Failure ); + + return ((VRTSourcedRasterBand *) hVRTBand)->AddComplexSource( + (GDALRasterBand *)hSrcBand, + nSrcXOff, nSrcYOff, + nSrcXSize, nSrcYSize, + nDstXOff, nDstYOff, + nDstXSize, nDstYSize, + dfScaleOff, dfScaleRatio, + dfNoDataValue ); +} + +/************************************************************************/ +/* AddFuncSource() */ +/************************************************************************/ + +CPLErr VRTSourcedRasterBand::AddFuncSource( VRTImageReadFunc pfnReadFunc, + void *pCBData, double dfNoDataValue ) + +{ +/* -------------------------------------------------------------------- */ +/* Create source. */ +/* -------------------------------------------------------------------- */ + VRTFuncSource *poFuncSource = new VRTFuncSource; + + poFuncSource->fNoDataValue = (float) dfNoDataValue; + poFuncSource->pfnReadFunc = pfnReadFunc; + poFuncSource->pCBData = pCBData; + poFuncSource->eType = GetRasterDataType(); + +/* -------------------------------------------------------------------- */ +/* add to list. */ +/* -------------------------------------------------------------------- */ + return AddSource( poFuncSource ); +} + +/************************************************************************/ +/* VRTAddFuncSource() */ +/************************************************************************/ + +/** + * @see VRTSourcedRasterBand::AddFuncSource(). + */ + +CPLErr CPL_STDCALL VRTAddFuncSource( VRTSourcedRasterBandH hVRTBand, + VRTImageReadFunc pfnReadFunc, + void *pCBData, double dfNoDataValue ) +{ + VALIDATE_POINTER1( hVRTBand, "VRTAddFuncSource", CE_Failure ); + + return ((VRTSourcedRasterBand *) hVRTBand)-> + AddFuncSource( pfnReadFunc, pCBData, dfNoDataValue ); +} + + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **VRTSourcedRasterBand::GetMetadataDomainList() +{ + return CSLAddString(GDALRasterBand::GetMetadataDomainList(), "LocationInfo"); +} + + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *VRTSourcedRasterBand::GetMetadataItem( const char * pszName, + const char * pszDomain ) + +{ +/* ==================================================================== */ +/* LocationInfo handling. */ +/* ==================================================================== */ + if( pszDomain != NULL + && EQUAL(pszDomain,"LocationInfo") + && (EQUALN(pszName,"Pixel_",6) || EQUALN(pszName,"GeoPixel_",9)) ) + { + int iPixel, iLine; + +/* -------------------------------------------------------------------- */ +/* What pixel are we aiming at? */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszName,"Pixel_",6) ) + { + if( sscanf( pszName+6, "%d_%d", &iPixel, &iLine ) != 2 ) + return NULL; + } + else if( EQUALN(pszName,"GeoPixel_",9) ) + { + double adfGeoTransform[6]; + double adfInvGeoTransform[6]; + double dfGeoX, dfGeoY; + + dfGeoX = CPLAtof(pszName + 9); + const char* pszUnderscore = strchr(pszName + 9, '_'); + if( !pszUnderscore ) + return NULL; + dfGeoY = CPLAtof(pszUnderscore + 1); + + if( GetDataset() == NULL ) + return NULL; + + if( GetDataset()->GetGeoTransform( adfGeoTransform ) != CE_None ) + return NULL; + + if( !GDALInvGeoTransform( adfGeoTransform, adfInvGeoTransform ) ) + return NULL; + + iPixel = (int) floor( + adfInvGeoTransform[0] + + adfInvGeoTransform[1] * dfGeoX + + adfInvGeoTransform[2] * dfGeoY ); + iLine = (int) floor( + adfInvGeoTransform[3] + + adfInvGeoTransform[4] * dfGeoX + + adfInvGeoTransform[5] * dfGeoY ); + } + else + return NULL; + + if( iPixel < 0 || iLine < 0 + || iPixel >= GetXSize() + || iLine >= GetYSize() ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Find the file(s) at this location. */ +/* -------------------------------------------------------------------- */ + char **papszFileList = NULL; + int nListMaxSize = 0, nListSize = 0; + CPLHashSet* hSetFiles = CPLHashSetNew(CPLHashSetHashStr, + CPLHashSetEqualStr, + NULL); + + for( int iSource = 0; iSource < nSources; iSource++ ) + { + double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize; + int nReqXOff, nReqYOff, nReqXSize, nReqYSize; + int nOutXOff, nOutYOff, nOutXSize, nOutYSize; + + if (!papoSources[iSource]->IsSimpleSource()) + continue; + + VRTSimpleSource *poSrc = (VRTSimpleSource *) papoSources[iSource]; + + if( !poSrc->GetSrcDstWindow( iPixel, iLine, 1, 1, 1, 1, + &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, + &nReqXOff, &nReqYOff, + &nReqXSize, &nReqYSize, + &nOutXOff, &nOutYOff, + &nOutXSize, &nOutYSize ) ) + continue; + + poSrc->GetFileList( &papszFileList, &nListSize, &nListMaxSize, + hSetFiles ); + } + +/* -------------------------------------------------------------------- */ +/* Format into XML. */ +/* -------------------------------------------------------------------- */ + int i; + + osLastLocationInfo = ""; + for( i = 0; i < nListSize; i++ ) + { + osLastLocationInfo += ""; + char* pszXMLEscaped = CPLEscapeString(papszFileList[i], -1, CPLES_XML); + osLastLocationInfo += pszXMLEscaped; + CPLFree(pszXMLEscaped); + osLastLocationInfo += ""; + } + osLastLocationInfo += ""; + + CSLDestroy( papszFileList ); + CPLHashSetDestroy( hSetFiles ); + + return osLastLocationInfo.c_str(); + } + +/* ==================================================================== */ +/* Other domains. */ +/* ==================================================================== */ + else + return GDALRasterBand::GetMetadataItem( pszName, pszDomain ); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **VRTSourcedRasterBand::GetMetadata( const char *pszDomain ) + +{ +/* ==================================================================== */ +/* vrt_sources domain handling. */ +/* ==================================================================== */ + if( pszDomain != NULL && EQUAL(pszDomain,"vrt_sources") ) + { + CSLDestroy(papszSourceList); + papszSourceList = NULL; + +/* -------------------------------------------------------------------- */ +/* Process SimpleSources. */ +/* -------------------------------------------------------------------- */ + for( int iSource = 0; iSource < nSources; iSource++ ) + { + CPLXMLNode *psXMLSrc; + char *pszXML; + + psXMLSrc = papoSources[iSource]->SerializeToXML( NULL ); + if( psXMLSrc == NULL ) + continue; + + pszXML = CPLSerializeXMLTree( psXMLSrc ); + + papszSourceList = + CSLSetNameValue( papszSourceList, + CPLSPrintf( "source_%d", iSource ), pszXML ); + CPLFree( pszXML ); + CPLDestroyXMLNode( psXMLSrc ); + } + + return papszSourceList; + } + +/* ==================================================================== */ +/* Other domains. */ +/* ==================================================================== */ + else + return GDALRasterBand::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr VRTSourcedRasterBand::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ + CPLDebug( "VRT", "VRTSourcedRasterBand::SetMetadataItem(%s,%s,%s)\n", + pszName, pszValue, pszDomain ); + + if( pszDomain != NULL + && EQUAL(pszDomain,"new_vrt_sources") ) + { + VRTDriver *poDriver = (VRTDriver *) GDALGetDriverByName( "VRT" ); + + CPLXMLNode *psTree = CPLParseXMLString( pszValue ); + VRTSource *poSource; + + if( psTree == NULL ) + return CE_Failure; + + poSource = poDriver->ParseSource( psTree, NULL ); + CPLDestroyXMLNode( psTree ); + + if( poSource != NULL ) + return AddSource( poSource ); + else + return CE_Failure; + } + else if( pszDomain != NULL + && EQUAL(pszDomain,"vrt_sources") ) + { + int iSource; + if (sscanf(pszName, "source_%d", &iSource) != 1 || iSource < 0 || + iSource >= nSources) + { + CPLError(CE_Failure, CPLE_AppDefined, + "%s metadata item name is not recognized. " + "Should be between source_0 and source_%d", + pszName, nSources - 1); + return CE_Failure; + } + + VRTDriver *poDriver = (VRTDriver *) GDALGetDriverByName( "VRT" ); + + CPLXMLNode *psTree = CPLParseXMLString( pszValue ); + VRTSource *poSource; + + if( psTree == NULL ) + return CE_Failure; + + poSource = poDriver->ParseSource( psTree, NULL ); + CPLDestroyXMLNode( psTree ); + + if( poSource != NULL ) + { + delete papoSources[iSource]; + papoSources[iSource] = poSource; + ((VRTDataset *)poDS)->SetNeedsFlush(); + return CE_None; + } + else + return CE_Failure; + } + else + return VRTRasterBand::SetMetadataItem( pszName, pszValue, pszDomain ); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr VRTSourcedRasterBand::SetMetadata( char **papszNewMD, const char *pszDomain ) + +{ + if( pszDomain != NULL + && (EQUAL(pszDomain,"new_vrt_sources") + || EQUAL(pszDomain,"vrt_sources")) ) + { + VRTDriver *poDriver = (VRTDriver *) GDALGetDriverByName( "VRT" ); + CPLErr eErr; + int i; + + if( EQUAL(pszDomain,"vrt_sources") ) + { + for( int i = 0; i < nSources; i++ ) + delete papoSources[i]; + CPLFree( papoSources ); + papoSources = NULL; + nSources = 0; + } + + for( i = 0; i < CSLCount(papszNewMD); i++ ) + { + const char *pszXML = CPLParseNameValue( papszNewMD[i], NULL ); + CPLXMLNode *psTree = CPLParseXMLString( pszXML ); + VRTSource *poSource; + + if( psTree == NULL ) + return CE_Failure; + + poSource = poDriver->ParseSource( psTree, NULL ); + CPLDestroyXMLNode( psTree ); + + if( poSource != NULL ) + { + eErr = AddSource( poSource ); + if( eErr != CE_None ) + return eErr; + } + else + return CE_Failure; + } + + return CE_None; + } + else + return VRTRasterBand::SetMetadata( papszNewMD, pszDomain ); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +void VRTSourcedRasterBand::GetFileList(char*** ppapszFileList, int *pnSize, + int *pnMaxSize, CPLHashSet* hSetFiles) +{ + for( int i = 0; i < nSources; i++ ) + { + papoSources[i]->GetFileList(ppapszFileList, pnSize, + pnMaxSize, hSetFiles); + } + + VRTRasterBand::GetFileList( ppapszFileList, pnSize, + pnMaxSize, hSetFiles); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int VRTSourcedRasterBand::CloseDependentDatasets() +{ + if (nSources == 0) + return FALSE; + + for( int i = 0; i < nSources; i++ ) + delete papoSources[i]; + + CPLFree( papoSources ); + papoSources = NULL; + nSources = 0; + + return TRUE; +} diff --git a/bazaar/plugin/gdal/frmts/vrt/vrtsources.cpp b/bazaar/plugin/gdal/frmts/vrt/vrtsources.cpp new file mode 100644 index 000000000..9738fead0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/vrtsources.cpp @@ -0,0 +1,2381 @@ +/****************************************************************************** + * $Id: vrtsources.cpp 29326 2015-06-10 20:36:31Z rouault $ + * + * Project: Virtual GDAL Datasets + * Purpose: Implementation of VRTSimpleSource, VRTFuncSource and + * VRTAveragedSource. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "vrtdataset.h" +#include "gdal_proxy.h" +#include "cpl_minixml.h" +#include "cpl_string.h" + +/* See #5459 */ +#ifdef isnan +#define HAS_ISNAN_MACRO +#endif +#include +#if defined(HAS_ISNAN_MACRO) && !defined(isnan) +#define isnan std::isnan +#endif + +CPL_CVSID("$Id: vrtsources.cpp 29326 2015-06-10 20:36:31Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* VRTSource */ +/* ==================================================================== */ +/************************************************************************/ + +VRTSource::~VRTSource() +{ +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +void VRTSource::GetFileList(CPL_UNUSED char*** ppapszFileList, + CPL_UNUSED int *pnSize, + CPL_UNUSED int *pnMaxSize, + CPL_UNUSED CPLHashSet* hSetFiles) +{ +} + +/************************************************************************/ +/* ==================================================================== */ +/* VRTSimpleSource */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VRTSimpleSource() */ +/************************************************************************/ + +VRTSimpleSource::VRTSimpleSource() + +{ + poRasterBand = NULL; + poMaskBandMainBand = NULL; + bNoDataSet = FALSE; + dfNoDataValue = VRT_NODATA_UNSET; + bRelativeToVRTOri = -1; +} + +/************************************************************************/ +/* ~VRTSimpleSource() */ +/************************************************************************/ + +VRTSimpleSource::~VRTSimpleSource() + +{ + // We use bRelativeToVRTOri to know if the file has been opened from + // XMLInit(), and thus we are sure that no other code has a direct reference + // to the dataset + if( poMaskBandMainBand != NULL ) + { + if (poMaskBandMainBand->GetDataset() != NULL ) + { + if( poMaskBandMainBand->GetDataset()->GetShared() || bRelativeToVRTOri >= 0 ) + GDALClose( (GDALDatasetH) poMaskBandMainBand->GetDataset() ); + else + poMaskBandMainBand->GetDataset()->Dereference(); + } + } + else if( poRasterBand != NULL && poRasterBand->GetDataset() != NULL ) + { + if( poRasterBand->GetDataset()->GetShared() || bRelativeToVRTOri >= 0 ) + GDALClose( (GDALDatasetH) poRasterBand->GetDataset() ); + else + poRasterBand->GetDataset()->Dereference(); + } +} + +/************************************************************************/ +/* UnsetPreservedRelativeFilenames() */ +/************************************************************************/ + +void VRTSimpleSource::UnsetPreservedRelativeFilenames() +{ + bRelativeToVRTOri = -1; + osSourceFileNameOri = ""; +} + +/************************************************************************/ +/* SetSrcBand() */ +/************************************************************************/ + +void VRTSimpleSource::SetSrcBand( GDALRasterBand *poNewSrcBand ) + +{ + poRasterBand = poNewSrcBand; +} + + +/************************************************************************/ +/* SetSrcMaskBand() */ +/************************************************************************/ + +/* poSrcBand is not the mask band, but the band from which the mask band is taken */ +void VRTSimpleSource::SetSrcMaskBand( GDALRasterBand *poNewSrcBand ) + +{ + poRasterBand = poNewSrcBand->GetMaskBand(); + poMaskBandMainBand = poNewSrcBand; +} + +/************************************************************************/ +/* SetSrcWindow() */ +/************************************************************************/ + +void VRTSimpleSource::SetSrcWindow( int nNewXOff, int nNewYOff, + int nNewXSize, int nNewYSize ) + +{ + nSrcXOff = nNewXOff; + nSrcYOff = nNewYOff; + nSrcXSize = nNewXSize; + nSrcYSize = nNewYSize; +} + +/************************************************************************/ +/* SetDstWindow() */ +/************************************************************************/ + +void VRTSimpleSource::SetDstWindow( int nNewXOff, int nNewYOff, + int nNewXSize, int nNewYSize ) + +{ + nDstXOff = nNewXOff; + nDstYOff = nNewYOff; + nDstXSize = nNewXSize; + nDstYSize = nNewYSize; +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +void VRTSimpleSource::SetNoDataValue( double dfNewNoDataValue ) + +{ + if( dfNewNoDataValue == VRT_NODATA_UNSET ) + { + bNoDataSet = FALSE; + dfNoDataValue = VRT_NODATA_UNSET; + } + else + { + bNoDataSet = TRUE; + dfNoDataValue = dfNewNoDataValue; + } +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +static const char* apszSpecialSyntax[] = { "HDF5:\"{FILENAME}\":{ANY}", + "HDF5:{FILENAME}:{ANY}", + "NETCDF:\"{FILENAME}\":{ANY}", + "NETCDF:{FILENAME}:{ANY}", + "NITF_IM:{ANY}:{FILENAME}", + "PDF:{ANY}:{FILENAME}", + "RASTERLITE:{FILENAME},{ANY}" }; + +CPLXMLNode *VRTSimpleSource::SerializeToXML( const char *pszVRTPath ) + +{ + CPLXMLNode *psSrc; + int bRelativeToVRT; + const char *pszRelativePath; + int nBlockXSize, nBlockYSize; + + if( poRasterBand == NULL ) + return NULL; + + GDALDataset *poDS; + + if (poMaskBandMainBand) + { + poDS = poMaskBandMainBand->GetDataset(); + if( poDS == NULL || poMaskBandMainBand->GetBand() < 1 ) + return NULL; + } + else + { + poDS = poRasterBand->GetDataset(); + if( poDS == NULL || poRasterBand->GetBand() < 1 ) + return NULL; + } + + psSrc = CPLCreateXMLNode( NULL, CXT_Element, "SimpleSource" ); + + if( osResampling.size() ) + { + CPLCreateXMLNode( + CPLCreateXMLNode( psSrc, CXT_Attribute, "resampling" ), + CXT_Text, osResampling.c_str() ); + } + + VSIStatBufL sStat; + CPLString osTmp; + if( bRelativeToVRTOri >= 0 ) + { + pszRelativePath = osSourceFileNameOri; + bRelativeToVRT = bRelativeToVRTOri; + } + else if ( strstr(poDS->GetDescription(), "/vsicurl/http") != NULL || + strstr(poDS->GetDescription(), "/vsicurl/ftp") != NULL ) + { + /* Testing the existence of remote resources can be excruciating */ + /* slow, so let's just suppose they exist */ + pszRelativePath = poDS->GetDescription(); + bRelativeToVRT = FALSE; + } + /* If this isn't actually a file, don't even try to know if it is */ + /* a relative path. It can't be !, and unfortunately */ + /* CPLIsFilenameRelative() can only work with strings that are filenames */ + /* To be clear NITF_TOC_ENTRY:CADRG_JOG-A_250K_1_0:some_path isn't a relative */ + /* file path */ + else if( VSIStatExL( poDS->GetDescription(), &sStat, VSI_STAT_EXISTS_FLAG ) != 0 ) + { + pszRelativePath = poDS->GetDescription(); + bRelativeToVRT = FALSE; + for( size_t i = 0; i < sizeof(apszSpecialSyntax) / sizeof(apszSpecialSyntax[0]); i ++) + { + const char* pszSyntax = apszSpecialSyntax[i]; + CPLString osPrefix(pszSyntax); + osPrefix.resize(strchr(pszSyntax, ':') - pszSyntax + 1); + if( pszSyntax[osPrefix.size()] == '"' ) + osPrefix += '"'; + if( EQUALN(pszRelativePath, osPrefix, osPrefix.size()) ) + { + if( EQUALN(pszSyntax + osPrefix.size(), "{ANY}", strlen("{ANY}")) ) + { + const char* pszLastPart = strrchr(pszRelativePath, ':') + 1; + /* CSV:z:/foo.xyz */ + if( (pszLastPart[0] == '/' || pszLastPart[0] == '\\') && + pszLastPart - pszRelativePath >= 3 && pszLastPart[-3] == ':' ) + pszLastPart -= 2; + CPLString osPrefixFilename = pszRelativePath; + osPrefixFilename.resize(pszLastPart - pszRelativePath); + pszRelativePath = + CPLExtractRelativePath( pszVRTPath, pszLastPart, + &bRelativeToVRT ); + osTmp = osPrefixFilename + pszRelativePath; + pszRelativePath = osTmp.c_str(); + } + else if( EQUALN(pszSyntax + osPrefix.size(), "{FILENAME}", strlen("{FILENAME}")) ) + { + CPLString osFilename(pszRelativePath + osPrefix.size()); + size_t nPos = 0; + if( osFilename.size() >= 3 && osFilename[1] == ':' && + (osFilename[2] == '\\' || osFilename[2] == '/') ) + nPos = 2; + nPos = osFilename.find(pszSyntax[osPrefix.size() + strlen("{FILENAME}")], nPos); + if( nPos != std::string::npos ) + { + CPLString osSuffix = osFilename.substr(nPos); + osFilename.resize(nPos); + pszRelativePath = + CPLExtractRelativePath( pszVRTPath, osFilename, + &bRelativeToVRT ); + osTmp = osPrefix + pszRelativePath + osSuffix; + pszRelativePath = osTmp.c_str(); + } + } + break; + } + } + } + else + { + pszRelativePath = + CPLExtractRelativePath( pszVRTPath, poDS->GetDescription(), + &bRelativeToVRT ); + } + + CPLSetXMLValue( psSrc, "SourceFilename", pszRelativePath ); + + CPLCreateXMLNode( + CPLCreateXMLNode( CPLGetXMLNode( psSrc, "SourceFilename" ), + CXT_Attribute, "relativeToVRT" ), + CXT_Text, bRelativeToVRT ? "1" : "0" ); + + if( !CSLTestBoolean(CPLGetConfigOption("VRT_SHARED_SOURCE", "TRUE")) ) + { + CPLCreateXMLNode( + CPLCreateXMLNode( CPLGetXMLNode( psSrc, "SourceFilename" ), + CXT_Attribute, "shared" ), + CXT_Text, "0" ); + } + + char** papszOpenOptions = poDS->GetOpenOptions(); + GDALSerializeOpenOptionsToXML(psSrc, papszOpenOptions); + + if (poMaskBandMainBand) + CPLSetXMLValue( psSrc, "SourceBand", + CPLSPrintf("mask,%d",poMaskBandMainBand->GetBand()) ); + else + CPLSetXMLValue( psSrc, "SourceBand", + CPLSPrintf("%d",poRasterBand->GetBand()) ); + + /* Write a few additional useful properties of the dataset */ + /* so that we can use a proxy dataset when re-opening. See XMLInit() */ + /* below */ + CPLSetXMLValue( psSrc, "SourceProperties.#RasterXSize", + CPLSPrintf("%d",poRasterBand->GetXSize()) ); + CPLSetXMLValue( psSrc, "SourceProperties.#RasterYSize", + CPLSPrintf("%d",poRasterBand->GetYSize()) ); + CPLSetXMLValue( psSrc, "SourceProperties.#DataType", + GDALGetDataTypeName( poRasterBand->GetRasterDataType() ) ); + poRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + CPLSetXMLValue( psSrc, "SourceProperties.#BlockXSize", + CPLSPrintf("%d",nBlockXSize) ); + CPLSetXMLValue( psSrc, "SourceProperties.#BlockYSize", + CPLSPrintf("%d",nBlockYSize) ); + + if( nSrcXOff != -1 + || nSrcYOff != -1 + || nSrcXSize != -1 + || nSrcYSize != -1 ) + { + CPLSetXMLValue( psSrc, "SrcRect.#xOff", + CPLSPrintf( "%d", nSrcXOff ) ); + CPLSetXMLValue( psSrc, "SrcRect.#yOff", + CPLSPrintf( "%d", nSrcYOff ) ); + CPLSetXMLValue( psSrc, "SrcRect.#xSize", + CPLSPrintf( "%d", nSrcXSize ) ); + CPLSetXMLValue( psSrc, "SrcRect.#ySize", + CPLSPrintf( "%d", nSrcYSize ) ); + } + + if( nDstXOff != -1 + || nDstYOff != -1 + || nDstXSize != -1 + || nDstYSize != -1 ) + { + CPLSetXMLValue( psSrc, "DstRect.#xOff", CPLSPrintf( "%d", nDstXOff ) ); + CPLSetXMLValue( psSrc, "DstRect.#yOff", CPLSPrintf( "%d", nDstYOff ) ); + CPLSetXMLValue( psSrc, "DstRect.#xSize",CPLSPrintf( "%d", nDstXSize )); + CPLSetXMLValue( psSrc, "DstRect.#ySize",CPLSPrintf( "%d", nDstYSize )); + } + + return psSrc; +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr VRTSimpleSource::XMLInit( CPLXMLNode *psSrc, const char *pszVRTPath ) + +{ + osResampling = CPLGetXMLValue( psSrc, "resampling", ""); + +/* -------------------------------------------------------------------- */ +/* Prepare filename. */ +/* -------------------------------------------------------------------- */ + char *pszSrcDSName = NULL; + CPLXMLNode* psSourceFileNameNode = CPLGetXMLNode(psSrc,"SourceFilename"); + const char *pszFilename = + psSourceFileNameNode ? CPLGetXMLValue(psSourceFileNameNode,NULL, NULL) : NULL; + + if( pszFilename == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Missing element in VRTRasterBand." ); + return CE_Failure; + } + + // Backup original filename and relativeToVRT so as to be able to + // serialize them identically again (#5985) + osSourceFileNameOri = pszFilename; + bRelativeToVRTOri = atoi(CPLGetXMLValue( psSourceFileNameNode, "relativetoVRT", "0")); + const char* pszShared = CPLGetXMLValue( psSourceFileNameNode, "shared", NULL); + int bShared = FALSE; + if( pszShared != NULL ) + bShared = CSLTestBoolean(pszShared); + else + bShared = CSLTestBoolean(CPLGetConfigOption("VRT_SHARED_SOURCE", "TRUE")); + + if( pszVRTPath != NULL + && bRelativeToVRTOri ) + { + int bDone = FALSE; + for( size_t i = 0; i < sizeof(apszSpecialSyntax) / sizeof(apszSpecialSyntax[0]); i ++) + { + const char* pszSyntax = apszSpecialSyntax[i]; + CPLString osPrefix(pszSyntax); + osPrefix.resize(strchr(pszSyntax, ':') - pszSyntax + 1); + if( pszSyntax[osPrefix.size()] == '"' ) + osPrefix += '"'; + if( EQUALN(pszFilename, osPrefix, osPrefix.size()) ) + { + if( EQUALN(pszSyntax + osPrefix.size(), "{ANY}", strlen("{ANY}")) ) + { + const char* pszLastPart = strrchr(pszFilename, ':') + 1; + /* CSV:z:/foo.xyz */ + if( (pszLastPart[0] == '/' || pszLastPart[0] == '\\') && + pszLastPart - pszFilename >= 3 && pszLastPart[-3] == ':' ) + pszLastPart -= 2; + CPLString osPrefixFilename = pszFilename; + osPrefixFilename.resize(pszLastPart - pszFilename); + pszSrcDSName = CPLStrdup( (osPrefixFilename + + CPLProjectRelativeFilename( pszVRTPath, pszLastPart )).c_str() ); + bDone = TRUE; + } + else if( EQUALN(pszSyntax + osPrefix.size(), "{FILENAME}", strlen("{FILENAME}")) ) + { + CPLString osFilename(pszFilename + osPrefix.size()); + size_t nPos = 0; + if( osFilename.size() >= 3 && osFilename[1] == ':' && + (osFilename[2] == '\\' || osFilename[2] == '/') ) + nPos = 2; + nPos = osFilename.find(pszSyntax[osPrefix.size() + strlen("{FILENAME}")], nPos); + if( nPos != std::string::npos ) + { + CPLString osSuffix = osFilename.substr(nPos); + osFilename.resize(nPos); + pszSrcDSName = CPLStrdup( (osPrefix + + CPLProjectRelativeFilename( pszVRTPath, osFilename ) + osSuffix).c_str() ); + bDone = TRUE; + } + } + break; + } + } + if( !bDone ) + { + pszSrcDSName = CPLStrdup( + CPLProjectRelativeFilename( pszVRTPath, pszFilename ) ); + } + } + else + pszSrcDSName = CPLStrdup( pszFilename ); + + const char* pszSourceBand = CPLGetXMLValue(psSrc,"SourceBand","1"); + int nSrcBand = 0; + int bGetMaskBand = FALSE; + if (EQUALN(pszSourceBand, "mask",4)) + { + bGetMaskBand = TRUE; + if (pszSourceBand[4] == ',') + nSrcBand = atoi(pszSourceBand + 5); + else + nSrcBand = 1; + } + else + nSrcBand = atoi(pszSourceBand); + if (!GDALCheckBandCount(nSrcBand, 0)) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Invalid element in VRTRasterBand." ); + CPLFree( pszSrcDSName ); + return CE_Failure; + } + + /* Newly generated VRT will have RasterXSize, RasterYSize, DataType, */ + /* BlockXSize, BlockYSize tags, so that we don't have actually to */ + /* open the real dataset immediately, but we can use a proxy dataset */ + /* instead. This is particularly useful when dealing with huge VRT */ + /* For example, a VRT with the world coverage of DTED0 (25594 files) */ + CPLXMLNode* psSrcProperties = CPLGetXMLNode(psSrc,"SourceProperties"); + int nRasterXSize = 0, nRasterYSize =0; + GDALDataType eDataType = (GDALDataType)-1; + int nBlockXSize = 0, nBlockYSize = 0; + if (psSrcProperties) + { + nRasterXSize = atoi(CPLGetXMLValue(psSrcProperties,"RasterXSize","0")); + nRasterYSize = atoi(CPLGetXMLValue(psSrcProperties,"RasterYSize","0")); + const char *pszDataType = CPLGetXMLValue(psSrcProperties, "DataType", NULL); + if( pszDataType != NULL ) + { + for( int iType = 0; iType < GDT_TypeCount; iType++ ) + { + const char *pszThisName = GDALGetDataTypeName((GDALDataType)iType); + + if( pszThisName != NULL && EQUAL(pszDataType,pszThisName) ) + { + eDataType = (GDALDataType) iType; + break; + } + } + } + nBlockXSize = atoi(CPLGetXMLValue(psSrcProperties,"BlockXSize","0")); + nBlockYSize = atoi(CPLGetXMLValue(psSrcProperties,"BlockYSize","0")); + } + + char** papszOpenOptions = GDALDeserializeOpenOptionsFromXML(psSrc); + if( strstr(pszSrcDSName,"SetOpenOptions(papszOpenOptions); + poSrcDS = proxyDS; + + /* Only the information of rasterBand nSrcBand will be accurate */ + /* but that's OK since we only use that band afterwards */ + for(i=1;i<=nSrcBand;i++) + proxyDS->AddSrcBandDescription(eDataType, nBlockXSize, nBlockYSize); + if (bGetMaskBand) + ((GDALProxyPoolRasterBand*)proxyDS->GetRasterBand(nSrcBand))->AddSrcMaskBandDescription(eDataType, nBlockXSize, nBlockYSize); + } + + CSLDestroy(papszOpenOptions); + + CPLFree( pszSrcDSName ); + + if( poSrcDS == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Get the raster band. */ +/* -------------------------------------------------------------------- */ + + poRasterBand = poSrcDS->GetRasterBand(nSrcBand); + if( poRasterBand == NULL ) + { + if( poSrcDS->GetShared() ) + GDALClose( (GDALDatasetH) poSrcDS ); + return CE_Failure; + } + if (bGetMaskBand) + { + poMaskBandMainBand = poRasterBand; + poRasterBand = poRasterBand->GetMaskBand(); + if( poRasterBand == NULL ) + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Set characteristics. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode* psSrcRect = CPLGetXMLNode(psSrc,"SrcRect"); + if (psSrcRect) + { + nSrcXOff = atoi(CPLGetXMLValue(psSrcRect,"xOff","-1")); + nSrcYOff = atoi(CPLGetXMLValue(psSrcRect,"yOff","-1")); + nSrcXSize = atoi(CPLGetXMLValue(psSrcRect,"xSize","-1")); + nSrcYSize = atoi(CPLGetXMLValue(psSrcRect,"ySize","-1")); + } + else + { + nSrcXOff = nSrcYOff = nSrcXSize = nSrcYSize = -1; + } + + CPLXMLNode* psDstRect = CPLGetXMLNode(psSrc,"DstRect"); + if (psDstRect) + { + nDstXOff = atoi(CPLGetXMLValue(psDstRect,"xOff","-1")); + nDstYOff = atoi(CPLGetXMLValue(psDstRect,"yOff","-1")); + nDstXSize = atoi(CPLGetXMLValue(psDstRect,"xSize","-1")); + nDstYSize = atoi(CPLGetXMLValue(psDstRect,"ySize","-1")); + } + else + { + nDstXOff = nDstYOff = nDstXSize = nDstYSize = -1; + } + + return CE_None; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +void VRTSimpleSource::GetFileList(char*** ppapszFileList, int *pnSize, + int *pnMaxSize, CPLHashSet* hSetFiles) +{ + const char* pszFilename; + if (poRasterBand != NULL && poRasterBand->GetDataset() != NULL && + (pszFilename = poRasterBand->GetDataset()->GetDescription()) != NULL) + { +/* -------------------------------------------------------------------- */ +/* Is the filename even a real filesystem object? */ +/* -------------------------------------------------------------------- */ + if ( strstr(pszFilename, "/vsicurl/http") != NULL || + strstr(pszFilename, "/vsicurl/ftp") != NULL ) + { + /* Testing the existence of remote resources can be excruciating */ + /* slow, so let's just suppose they exist */ + } + else + { + VSIStatBufL sStat; + if( VSIStatExL( pszFilename, &sStat, VSI_STAT_EXISTS_FLAG ) != 0 ) + return; + } + +/* -------------------------------------------------------------------- */ +/* Is it already in the list ? */ +/* -------------------------------------------------------------------- */ + if( CPLHashSetLookup(hSetFiles, pszFilename) != NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Grow array if necessary */ +/* -------------------------------------------------------------------- */ + if (*pnSize + 1 >= *pnMaxSize) + { + *pnMaxSize = 2 + 2 * (*pnMaxSize); + *ppapszFileList = (char **) CPLRealloc( + *ppapszFileList, sizeof(char*) * (*pnMaxSize) ); + } + +/* -------------------------------------------------------------------- */ +/* Add the string to the list */ +/* -------------------------------------------------------------------- */ + (*ppapszFileList)[*pnSize] = CPLStrdup(pszFilename); + (*ppapszFileList)[(*pnSize + 1)] = NULL; + CPLHashSetInsert(hSetFiles, (*ppapszFileList)[*pnSize]); + + (*pnSize) ++; + } +} + +/************************************************************************/ +/* GetBand() */ +/************************************************************************/ + +GDALRasterBand* VRTSimpleSource::GetBand() +{ + return poMaskBandMainBand ? NULL : poRasterBand; +} + +/************************************************************************/ +/* IsSameExceptBandNumber() */ +/************************************************************************/ + +int VRTSimpleSource::IsSameExceptBandNumber(VRTSimpleSource* poOtherSource) +{ + return nSrcXOff == poOtherSource->nSrcXOff && + nSrcYOff == poOtherSource->nSrcYOff && + nSrcXSize == poOtherSource->nSrcXSize && + nSrcYSize == poOtherSource->nSrcYSize && + nDstXOff == poOtherSource->nDstXOff && + nDstYOff == poOtherSource->nDstYOff && + nDstXSize == poOtherSource->nDstXSize && + nDstYSize == poOtherSource->nDstYSize && + bNoDataSet == poOtherSource->bNoDataSet && + dfNoDataValue == poOtherSource->dfNoDataValue && + GetBand() != NULL && poOtherSource->GetBand() != NULL && + GetBand()->GetDataset() != NULL && + poOtherSource->GetBand()->GetDataset() != NULL && + EQUAL(GetBand()->GetDataset()->GetDescription(), + poOtherSource->GetBand()->GetDataset()->GetDescription()); +} + +/************************************************************************/ +/* SrcToDst() */ +/* */ +/* Note: this is a no-op if the dst window is -1,-1,-1,-1. */ +/************************************************************************/ + +void VRTSimpleSource::SrcToDst( double dfX, double dfY, + double &dfXOut, double &dfYOut ) + +{ + dfXOut = ((dfX - nSrcXOff) / nSrcXSize) * nDstXSize + nDstXOff; + dfYOut = ((dfY - nSrcYOff) / nSrcYSize) * nDstYSize + nDstYOff; +} + +/************************************************************************/ +/* DstToSrc() */ +/* */ +/* Note: this is a no-op if the dst window is -1,-1,-1,-1. */ +/************************************************************************/ + +void VRTSimpleSource::DstToSrc( double dfX, double dfY, + double &dfXOut, double &dfYOut ) + +{ + dfXOut = ((dfX - nDstXOff) / nDstXSize) * nSrcXSize + nSrcXOff; + dfYOut = ((dfY - nDstYOff) / nDstYSize) * nSrcYSize + nSrcYOff; +} + +/************************************************************************/ +/* GetSrcDstWindow() */ +/************************************************************************/ + +int +VRTSimpleSource::GetSrcDstWindow( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + double *pdfReqXOff, double *pdfReqYOff, + double *pdfReqXSize, double *pdfReqYSize, + int *pnReqXOff, int *pnReqYOff, + int *pnReqXSize, int *pnReqYSize, + int *pnOutXOff, int *pnOutYOff, + int *pnOutXSize, int *pnOutYSize ) + +{ + int bDstWinSet = nDstXOff != -1 || nDstXSize != -1 + || nDstYOff != -1 || nDstYSize != -1; + +#ifdef DEBUG + int bSrcWinSet = nSrcXOff != -1 || nSrcXSize != -1 + || nSrcYOff != -1 || nSrcYSize != -1; + + if( bSrcWinSet != bDstWinSet ) + { + return FALSE; + } +#endif + +/* -------------------------------------------------------------------- */ +/* If the input window completely misses the portion of the */ +/* virtual dataset provided by this source we have nothing to do. */ +/* -------------------------------------------------------------------- */ + if( bDstWinSet ) + { + if( nXOff >= nDstXOff + nDstXSize + || nYOff >= nDstYOff + nDstYSize + || nXOff + nXSize < nDstXOff + || nYOff + nYSize < nDstYOff ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* This request window corresponds to the whole output buffer. */ +/* -------------------------------------------------------------------- */ + *pnOutXOff = 0; + *pnOutYOff = 0; + *pnOutXSize = nBufXSize; + *pnOutYSize = nBufYSize; + +/* -------------------------------------------------------------------- */ +/* If the input window extents outside the portion of the on */ +/* the virtual file that this source can set, then clip down */ +/* the requested window. */ +/* -------------------------------------------------------------------- */ + int bModifiedX = FALSE, bModifiedY = FALSE; + int nRXOff = nXOff; + int nRYOff = nYOff; + int nRXSize = nXSize; + int nRYSize = nYSize; + + + if( bDstWinSet ) + { + if( nRXOff < nDstXOff ) + { + nRXSize = nRXSize + nRXOff - nDstXOff; + nRXOff = nDstXOff; + bModifiedX = TRUE; + } + + if( nRYOff < nDstYOff ) + { + nRYSize = nRYSize + nRYOff - nDstYOff; + nRYOff = nDstYOff; + bModifiedY = TRUE; + } + + if( nRXOff + nRXSize > nDstXOff + nDstXSize ) + { + nRXSize = nDstXOff + nDstXSize - nRXOff; + bModifiedX = TRUE; + } + + if( nRYOff + nRYSize > nDstYOff + nDstYSize ) + { + nRYSize = nDstYOff + nDstYSize - nRYOff; + bModifiedY = TRUE; + } + } + +/* -------------------------------------------------------------------- */ +/* Translate requested region in virtual file into the source */ +/* band coordinates. */ +/* -------------------------------------------------------------------- */ + double dfScaleX = nSrcXSize / (double) nDstXSize; + double dfScaleY = nSrcYSize / (double) nDstYSize; + + *pdfReqXOff = (nRXOff - nDstXOff) * dfScaleX + nSrcXOff; + *pdfReqYOff = (nRYOff - nDstYOff) * dfScaleY + nSrcYOff; + *pdfReqXSize = nRXSize * dfScaleX; + *pdfReqYSize = nRYSize * dfScaleY; + +/* -------------------------------------------------------------------- */ +/* Clamp within the bounds of the available source data. */ +/* -------------------------------------------------------------------- */ + if( *pdfReqXOff < 0 ) + { + *pdfReqXSize += *pdfReqXOff; + *pdfReqXOff = 0; + bModifiedX = TRUE; + } + if( *pdfReqYOff < 0 ) + { + *pdfReqYSize += *pdfReqYOff; + *pdfReqYOff = 0; + bModifiedY = TRUE; + } + + *pnReqXOff = (int) floor(*pdfReqXOff); + *pnReqYOff = (int) floor(*pdfReqYOff); + + *pnReqXSize = (int) floor(*pdfReqXSize + 0.5); + *pnReqYSize = (int) floor(*pdfReqYSize + 0.5); + +/* -------------------------------------------------------------------- */ +/* Clamp within the bounds of the available source data. */ +/* -------------------------------------------------------------------- */ + + if( *pnReqXSize == 0 ) + *pnReqXSize = 1; + if( *pnReqYSize == 0 ) + *pnReqYSize = 1; + + if( *pnReqXOff + *pnReqXSize > poRasterBand->GetXSize() ) + { + *pnReqXSize = poRasterBand->GetXSize() - *pnReqXOff; + bModifiedX = TRUE; + } + if( *pdfReqXOff + *pdfReqXSize > poRasterBand->GetXSize() ) + { + *pdfReqXSize = poRasterBand->GetXSize() - *pdfReqXOff; + bModifiedX = TRUE; + } + + if( *pnReqYOff + *pnReqYSize > poRasterBand->GetYSize() ) + { + *pnReqYSize = poRasterBand->GetYSize() - *pnReqYOff; + bModifiedY = TRUE; + } + if( *pdfReqYOff + *pdfReqYSize > poRasterBand->GetYSize() ) + { + *pdfReqYSize = poRasterBand->GetYSize() - *pdfReqYOff; + bModifiedY = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Don't do anything if the requesting region is completely off */ +/* the source image. */ +/* -------------------------------------------------------------------- */ + if( *pnReqXOff >= poRasterBand->GetXSize() + || *pnReqYOff >= poRasterBand->GetYSize() + || *pnReqXSize <= 0 || *pnReqYSize <= 0 ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If we haven't had to modify the source rectangle, then the */ +/* destination rectangle must be the whole region. */ +/* -------------------------------------------------------------------- */ + if( !bModifiedX && !bModifiedY ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Now transform this possibly reduced request back into the */ +/* destination buffer coordinates in case the output region is */ +/* less than the whole buffer. */ +/* -------------------------------------------------------------------- */ + double dfDstULX, dfDstULY, dfDstLRX, dfDstLRY; + double dfScaleWinToBufX, dfScaleWinToBufY; + + SrcToDst( (double) *pnReqXOff, (double) *pnReqYOff, dfDstULX, dfDstULY ); + SrcToDst( *pnReqXOff + *pnReqXSize, *pnReqYOff + *pnReqYSize, + dfDstLRX, dfDstLRY ); + + if( bModifiedX ) + { + dfScaleWinToBufX = nBufXSize / (double) nXSize; + + *pnOutXOff = (int) ((dfDstULX - nXOff) * dfScaleWinToBufX+0.001); + *pnOutXSize = (int) ((dfDstLRX - nXOff) * dfScaleWinToBufX+0.5) + - *pnOutXOff; + + *pnOutXOff = MAX(0,*pnOutXOff); + if( *pnOutXOff + *pnOutXSize > nBufXSize ) + *pnOutXSize = nBufXSize - *pnOutXOff; + } + + if( bModifiedY ) + { + dfScaleWinToBufY = nBufYSize / (double) nYSize; + + *pnOutYOff = (int) ((dfDstULY - nYOff) * dfScaleWinToBufY+0.001); + *pnOutYSize = (int) ((dfDstLRY - nYOff) * dfScaleWinToBufY+0.5) + - *pnOutYOff; + + *pnOutYOff = MAX(0,*pnOutYOff); + if( *pnOutYOff + *pnOutYSize > nBufYSize ) + *pnOutYSize = nBufYSize - *pnOutYOff; + } + + if( *pnOutXSize < 1 || *pnOutYSize < 1 ) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* RasterIO() */ +/************************************************************************/ + +CPLErr +VRTSimpleSource::RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArgIn ) + +{ + GDALRasterIOExtraArg sExtraArg; + + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + GDALRasterIOExtraArg* psExtraArg = &sExtraArg; + + // The window we will actually request from the source raster band. + double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize; + int nReqXOff, nReqYOff, nReqXSize, nReqYSize; + + // The window we will actual set _within_ the pData buffer. + int nOutXOff, nOutYOff, nOutXSize, nOutYSize; + + if( !GetSrcDstWindow( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, + &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, + &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) ) + { + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Actually perform the IO request. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + if( osResampling.size() ) + { + psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(osResampling); + } + else if( psExtraArgIn != NULL ) + { + psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg; + } + psExtraArg->bFloatingPointWindowValidity = TRUE; + psExtraArg->dfXOff = dfReqXOff; + psExtraArg->dfYOff = dfReqYOff; + psExtraArg->dfXSize = dfReqXSize; + psExtraArg->dfYSize = dfReqYSize; + + eErr = + poRasterBand->RasterIO( GF_Read, + nReqXOff, nReqYOff, nReqXSize, nReqYSize, + ((unsigned char *) pData) + + nOutXOff * nPixelSpace + + (GPtrDiff_t)nOutYOff * nLineSpace, + nOutXSize, nOutYSize, + eBufType, nPixelSpace, nLineSpace, psExtraArg ); + + return eErr; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double VRTSimpleSource::GetMinimum( int nXSize, int nYSize, int *pbSuccess ) +{ + // The window we will actually request from the source raster band. + double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize; + int nReqXOff, nReqYOff, nReqXSize, nReqYSize; + + // The window we will actual set _within_ the pData buffer. + int nOutXOff, nOutYOff, nOutXSize, nOutYSize; + + if( !GetSrcDstWindow( 0, 0, nXSize, nYSize, + nXSize, nYSize, + &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, + &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, + &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) || + nReqXOff != 0 || nReqYOff != 0 || + nReqXSize != poRasterBand->GetXSize() || + nReqYSize != poRasterBand->GetYSize()) + { + *pbSuccess = FALSE; + return 0; + } + + return poRasterBand->GetMinimum(pbSuccess); +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double VRTSimpleSource::GetMaximum( int nXSize, int nYSize, int *pbSuccess ) +{ + // The window we will actually request from the source raster band. + double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize; + int nReqXOff, nReqYOff, nReqXSize, nReqYSize; + + // The window we will actual set _within_ the pData buffer. + int nOutXOff, nOutYOff, nOutXSize, nOutYSize; + + if( !GetSrcDstWindow( 0, 0, nXSize, nYSize, + nXSize, nYSize, + &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, + &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, + &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) || + nReqXOff != 0 || nReqYOff != 0 || + nReqXSize != poRasterBand->GetXSize() || + nReqYSize != poRasterBand->GetYSize()) + { + *pbSuccess = FALSE; + return 0; + } + + return poRasterBand->GetMaximum(pbSuccess); +} + +/************************************************************************/ +/* ComputeRasterMinMax() */ +/************************************************************************/ + +CPLErr VRTSimpleSource::ComputeRasterMinMax( int nXSize, int nYSize, int bApproxOK, double* adfMinMax ) +{ + // The window we will actually request from the source raster band. + double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize; + int nReqXOff, nReqYOff, nReqXSize, nReqYSize; + + // The window we will actual set _within_ the pData buffer. + int nOutXOff, nOutYOff, nOutXSize, nOutYSize; + + if( !GetSrcDstWindow( 0, 0, nXSize, nYSize, + nXSize, nYSize, + &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, + &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, + &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) || + nReqXOff != 0 || nReqYOff != 0 || + nReqXSize != poRasterBand->GetXSize() || + nReqYSize != poRasterBand->GetYSize()) + { + return CE_Failure; + } + + return poRasterBand->ComputeRasterMinMax(bApproxOK, adfMinMax); +} + +/************************************************************************/ +/* ComputeStatistics() */ +/************************************************************************/ + +CPLErr VRTSimpleSource::ComputeStatistics( int nXSize, int nYSize, + int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, void *pProgressData ) +{ + // The window we will actually request from the source raster band. + double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize; + int nReqXOff, nReqYOff, nReqXSize, nReqYSize; + + // The window we will actual set _within_ the pData buffer. + int nOutXOff, nOutYOff, nOutXSize, nOutYSize; + + if( !GetSrcDstWindow( 0, 0, nXSize, nYSize, + nXSize, nYSize, + &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, + &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, + &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) || + nReqXOff != 0 || nReqYOff != 0 || + nReqXSize != poRasterBand->GetXSize() || + nReqYSize != poRasterBand->GetYSize()) + { + return CE_Failure; + } + + return poRasterBand->ComputeStatistics(bApproxOK, pdfMin, pdfMax, + pdfMean, pdfStdDev, + pfnProgress, pProgressData); +} + +/************************************************************************/ +/* GetHistogram() */ +/************************************************************************/ + +CPLErr VRTSimpleSource::GetHistogram( int nXSize, int nYSize, + double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, void *pProgressData ) +{ + // The window we will actually request from the source raster band. + double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize; + int nReqXOff, nReqYOff, nReqXSize, nReqYSize; + + // The window we will actual set _within_ the pData buffer. + int nOutXOff, nOutYOff, nOutXSize, nOutYSize; + + if( !GetSrcDstWindow( 0, 0, nXSize, nYSize, + nXSize, nYSize, + &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, + &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, + &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) || + nReqXOff != 0 || nReqYOff != 0 || + nReqXSize != poRasterBand->GetXSize() || + nReqYSize != poRasterBand->GetYSize()) + { + return CE_Failure; + } + + return poRasterBand->GetHistogram( dfMin, dfMax, nBuckets, + panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* DatasetRasterIO() */ +/************************************************************************/ + +CPLErr VRTSimpleSource::DatasetRasterIO( + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArgIn) +{ + if (!EQUAL(GetType(), "SimpleSource")) + { + CPLError(CE_Failure, CPLE_NotSupported, + "DatasetRasterIO() not implemented for %s", GetType()); + return CE_Failure; + } + + GDALRasterIOExtraArg sExtraArg; + + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + GDALRasterIOExtraArg* psExtraArg = &sExtraArg; + + // The window we will actually request from the source raster band. + double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize; + int nReqXOff, nReqYOff, nReqXSize, nReqYSize; + + // The window we will actual set _within_ the pData buffer. + int nOutXOff, nOutYOff, nOutXSize, nOutYSize; + + if( !GetSrcDstWindow( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, + &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, + &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) ) + { + return CE_None; + } + + GDALDataset* poDS = poRasterBand->GetDataset(); + if (poDS == NULL) + return CE_Failure; + + if( osResampling.size() ) + { + psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(osResampling); + } + else if( psExtraArgIn != NULL ) + { + psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg; + } + psExtraArg->bFloatingPointWindowValidity = TRUE; + psExtraArg->dfXOff = dfReqXOff; + psExtraArg->dfYOff = dfReqYOff; + psExtraArg->dfXSize = dfReqXSize; + psExtraArg->dfYSize = dfReqYSize; + + CPLErr eErr = poDS->RasterIO( GF_Read, + nReqXOff, nReqYOff, nReqXSize, nReqYSize, + ((unsigned char *) pData) + + nOutXOff * nPixelSpace + + (GPtrDiff_t)nOutYOff * nLineSpace, + nOutXSize, nOutYSize, + eBufType, nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); + + return eErr; +} + +/************************************************************************/ +/* SetResampling() */ +/************************************************************************/ + +void VRTSimpleSource::SetResampling( const char* pszResampling ) +{ + osResampling = (pszResampling) ? pszResampling : ""; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VRTAveragedSource */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VRTAveragedSource() */ +/************************************************************************/ + +VRTAveragedSource::VRTAveragedSource() +{ +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *VRTAveragedSource::SerializeToXML( const char *pszVRTPath ) + +{ + CPLXMLNode *psSrc = VRTSimpleSource::SerializeToXML( pszVRTPath ); + + if( psSrc == NULL ) + return NULL; + + CPLFree( psSrc->pszValue ); + psSrc->pszValue = CPLStrdup( "AveragedSource" ); + + return psSrc; +} + +/************************************************************************/ +/* RasterIO() */ +/************************************************************************/ + +CPLErr +VRTAveragedSource::RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArgIn ) + +{ + GDALRasterIOExtraArg sExtraArg; + + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + GDALRasterIOExtraArg* psExtraArg = &sExtraArg; + + // The window we will actually request from the source raster band. + double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize; + int nReqXOff, nReqYOff, nReqXSize, nReqYSize; + + // The window we will actual set _within_ the pData buffer. + int nOutXOff, nOutYOff, nOutXSize, nOutYSize; + + if( !GetSrcDstWindow( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, + &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, + &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, + &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Allocate a temporary buffer to whole the full resolution */ +/* data from the area of interest. */ +/* -------------------------------------------------------------------- */ + float *pafSrc; + + pafSrc = (float *) VSIMalloc3(sizeof(float), nReqXSize, nReqYSize); + if( pafSrc == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating working buffer in VRTAveragedSource::RasterIO()." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Load it. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + if( osResampling.size() ) + { + psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(osResampling); + } + else if( psExtraArgIn != NULL ) + { + psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg; + } + + psExtraArg->bFloatingPointWindowValidity = TRUE; + psExtraArg->dfXOff = dfReqXOff; + psExtraArg->dfYOff = dfReqYOff; + psExtraArg->dfXSize = dfReqXSize; + psExtraArg->dfYSize = dfReqYSize; + + eErr = poRasterBand->RasterIO( GF_Read, + nReqXOff, nReqYOff, nReqXSize, nReqYSize, + pafSrc, nReqXSize, nReqYSize, GDT_Float32, + 0, 0, psExtraArg ); + + if( eErr != CE_None ) + { + VSIFree( pafSrc ); + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Do the averaging. */ +/* -------------------------------------------------------------------- */ + for( int iBufLine = nOutYOff; iBufLine < nOutYOff + nOutYSize; iBufLine++ ) + { + double dfYDst; + + dfYDst = (iBufLine / (double) nBufYSize) * nYSize + nYOff; + + for( int iBufPixel = nOutXOff; + iBufPixel < nOutXOff + nOutXSize; + iBufPixel++ ) + { + double dfXDst; + double dfXSrcStart, dfXSrcEnd, dfYSrcStart, dfYSrcEnd; + int iXSrcStart, iYSrcStart, iXSrcEnd, iYSrcEnd; + + dfXDst = (iBufPixel / (double) nBufXSize) * nXSize + nXOff; + + // Compute the source image rectangle needed for this pixel. + DstToSrc( dfXDst, dfYDst, dfXSrcStart, dfYSrcStart ); + DstToSrc( dfXDst+1.0, dfYDst+1.0, dfXSrcEnd, dfYSrcEnd ); + + // Convert to integers, assuming that the center of the source + // pixel must be in our rect to get included. + if (dfXSrcEnd >= dfXSrcStart + 1) + { + iXSrcStart = (int) floor(dfXSrcStart+0.5); + iXSrcEnd = (int) floor(dfXSrcEnd+0.5); + } + else + { + /* If the resampling factor is less than 100%, the distance */ + /* between the source pixel is < 1, so we stick to nearest */ + /* neighbour */ + iXSrcStart = (int) floor(dfXSrcStart); + iXSrcEnd = iXSrcStart + 1; + } + if (dfYSrcEnd >= dfYSrcStart + 1) + { + iYSrcStart = (int) floor(dfYSrcStart+0.5); + iYSrcEnd = (int) floor(dfYSrcEnd+0.5); + } + else + { + iYSrcStart = (int) floor(dfYSrcStart); + iYSrcEnd = iYSrcStart + 1; + } + + // Transform into the coordinate system of the source *buffer* + iXSrcStart -= nReqXOff; + iYSrcStart -= nReqYOff; + iXSrcEnd -= nReqXOff; + iYSrcEnd -= nReqYOff; + + double dfSum = 0.0; + int nPixelCount = 0; + + for( int iY = iYSrcStart; iY < iYSrcEnd; iY++ ) + { + if( iY < 0 || iY >= nReqYSize ) + continue; + + for( int iX = iXSrcStart; iX < iXSrcEnd; iX++ ) + { + if( iX < 0 || iX >= nReqXSize ) + continue; + + float fSampledValue = pafSrc[iX + iY * nReqXSize]; + if (CPLIsNan(fSampledValue)) + continue; + + if( bNoDataSet && ARE_REAL_EQUAL(fSampledValue, dfNoDataValue)) + continue; + + nPixelCount++; + dfSum += pafSrc[iX + iY * nReqXSize]; + } + } + + if( nPixelCount == 0 ) + continue; + + // Compute output value. + float dfOutputValue = (float) (dfSum / nPixelCount); + + // Put it in the output buffer. + GByte *pDstLocation; + + pDstLocation = ((GByte *)pData) + + nPixelSpace * iBufPixel + + (GPtrDiff_t)nLineSpace * iBufLine; + + if( eBufType == GDT_Byte ) + *pDstLocation = (GByte) MIN(255,MAX(0,dfOutputValue + 0.5)); + else + GDALCopyWords( &dfOutputValue, GDT_Float32, 4, + pDstLocation, eBufType, 8, 1 ); + } + } + + VSIFree( pafSrc ); + + return CE_None; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double VRTAveragedSource::GetMinimum( CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED int *pbSuccess ) +{ + *pbSuccess = FALSE; + return 0; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double VRTAveragedSource::GetMaximum( CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED int *pbSuccess ) +{ + *pbSuccess = FALSE; + return 0; +} + +/************************************************************************/ +/* ComputeRasterMinMax() */ +/************************************************************************/ + +CPLErr VRTAveragedSource::ComputeRasterMinMax( CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED int bApproxOK, + CPL_UNUSED double* adfMinMax ) +{ + return CE_Failure; +} + +/************************************************************************/ +/* ComputeStatistics() */ +/************************************************************************/ + +CPLErr VRTAveragedSource::ComputeStatistics( CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED int bApproxOK, + CPL_UNUSED double *pdfMin, + CPL_UNUSED double *pdfMax, + CPL_UNUSED double *pdfMean, + CPL_UNUSED double *pdfStdDev, + CPL_UNUSED GDALProgressFunc pfnProgress, + CPL_UNUSED void *pProgressData ) +{ + return CE_Failure; +} + +/************************************************************************/ +/* GetHistogram() */ +/************************************************************************/ + +CPLErr VRTAveragedSource::GetHistogram( CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED double dfMin, + CPL_UNUSED double dfMax, + CPL_UNUSED int nBuckets, + CPL_UNUSED GUIntBig * panHistogram, + CPL_UNUSED int bIncludeOutOfRange, + CPL_UNUSED int bApproxOK, + CPL_UNUSED GDALProgressFunc pfnProgress, + CPL_UNUSED void *pProgressData ) +{ + return CE_Failure; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VRTComplexSource */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VRTComplexSource() */ +/************************************************************************/ + +VRTComplexSource::VRTComplexSource() + +{ + eScalingType = VRT_SCALING_NONE; + dfScaleOff = 0.0; + dfScaleRatio = 1.0; + + bNoDataSet = FALSE; + dfNoDataValue = 0.0; + + padfLUTInputs = NULL; + padfLUTOutputs = NULL; + nLUTItemCount = 0; + nColorTableComponent = 0; + + bSrcMinMaxDefined = FALSE; + dfSrcMin = 0.0; + dfSrcMax = 0.0; + dfDstMin = 0.0; + dfDstMax = 0.0; + dfExponent = 1.0; +} + +VRTComplexSource::~VRTComplexSource() +{ + if (padfLUTInputs) + VSIFree( padfLUTInputs ); + if (padfLUTOutputs) + VSIFree( padfLUTOutputs ); +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *VRTComplexSource::SerializeToXML( const char *pszVRTPath ) + +{ + CPLXMLNode *psSrc = VRTSimpleSource::SerializeToXML( pszVRTPath ); + + if( psSrc == NULL ) + return NULL; + + CPLFree( psSrc->pszValue ); + psSrc->pszValue = CPLStrdup( "ComplexSource" ); + + if( bNoDataSet ) + { + if (CPLIsNan(dfNoDataValue)) + CPLSetXMLValue( psSrc, "NODATA", "nan"); + else + CPLSetXMLValue( psSrc, "NODATA", + CPLSPrintf("%g", dfNoDataValue) ); + } + + switch( eScalingType ) + { + case VRT_SCALING_NONE: + break; + + case VRT_SCALING_LINEAR: + { + CPLSetXMLValue( psSrc, "ScaleOffset", + CPLSPrintf("%g", dfScaleOff) ); + CPLSetXMLValue( psSrc, "ScaleRatio", + CPLSPrintf("%g", dfScaleRatio) ); + break; + } + + case VRT_SCALING_EXPONENTIAL: + { + CPLSetXMLValue( psSrc, "Exponent", + CPLSPrintf("%g", dfExponent) ); + CPLSetXMLValue( psSrc, "SrcMin", + CPLSPrintf("%g", dfSrcMin) ); + CPLSetXMLValue( psSrc, "SrcMax", + CPLSPrintf("%g", dfSrcMax) ); + CPLSetXMLValue( psSrc, "DstMin", + CPLSPrintf("%g", dfDstMin) ); + CPLSetXMLValue( psSrc, "DstMax", + CPLSPrintf("%g", dfDstMax) ); + break; + } + } + + if ( nLUTItemCount ) + { + CPLString osLUT = CPLString().Printf("%g:%g", padfLUTInputs[0], padfLUTOutputs[0]); + int i; + for ( i = 1; i < nLUTItemCount; i++ ) + osLUT += CPLString().Printf(",%g:%g", padfLUTInputs[i], padfLUTOutputs[i]); + CPLSetXMLValue( psSrc, "LUT", osLUT ); + } + + if ( nColorTableComponent ) + { + CPLSetXMLValue( psSrc, "ColorTableComponent", + CPLSPrintf("%d", nColorTableComponent) ); + } + + return psSrc; +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr VRTComplexSource::XMLInit( CPLXMLNode *psSrc, const char *pszVRTPath ) + +{ + CPLErr eErr; + +/* -------------------------------------------------------------------- */ +/* Do base initialization. */ +/* -------------------------------------------------------------------- */ + eErr = VRTSimpleSource::XMLInit( psSrc, pszVRTPath ); + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Complex parameters. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLValue(psSrc, "ScaleOffset", NULL) != NULL + || CPLGetXMLValue(psSrc, "ScaleRatio", NULL) != NULL ) + { + eScalingType = VRT_SCALING_LINEAR; + dfScaleOff = CPLAtof(CPLGetXMLValue(psSrc, "ScaleOffset", "0") ); + dfScaleRatio = CPLAtof(CPLGetXMLValue(psSrc, "ScaleRatio", "1") ); + } + else if( CPLGetXMLValue(psSrc, "Exponent", NULL) != NULL && + CPLGetXMLValue(psSrc, "DstMin", NULL) != NULL && + CPLGetXMLValue(psSrc, "DstMax", NULL) != NULL ) + { + eScalingType = VRT_SCALING_EXPONENTIAL; + dfExponent = CPLAtof(CPLGetXMLValue(psSrc, "Exponent", "1.0") ); + + if( CPLGetXMLValue(psSrc, "SrcMin", NULL) != NULL + && CPLGetXMLValue(psSrc, "SrcMax", NULL) != NULL ) + { + dfSrcMin = CPLAtof(CPLGetXMLValue(psSrc, "SrcMin", "0.0") ); + dfSrcMax = CPLAtof(CPLGetXMLValue(psSrc, "SrcMax", "0.0") ); + bSrcMinMaxDefined = TRUE; + } + + dfDstMin = CPLAtof(CPLGetXMLValue(psSrc, "DstMin", "0.0") ); + dfDstMax = CPLAtof(CPLGetXMLValue(psSrc, "DstMax", "0.0") ); + } + + if( CPLGetXMLValue(psSrc, "NODATA", NULL) != NULL ) + { + bNoDataSet = TRUE; + dfNoDataValue = CPLAtofM(CPLGetXMLValue(psSrc, "NODATA", "0")); + } + + if( CPLGetXMLValue(psSrc, "LUT", NULL) != NULL ) + { + int nIndex; + char **papszValues = CSLTokenizeString2(CPLGetXMLValue(psSrc, "LUT", ""), ",:", CSLT_ALLOWEMPTYTOKENS); + + if (nLUTItemCount) + { + if (padfLUTInputs) + { + VSIFree( padfLUTInputs ); + padfLUTInputs = NULL; + } + if (padfLUTOutputs) + { + VSIFree( padfLUTOutputs ); + padfLUTOutputs = NULL; + } + nLUTItemCount = 0; + } + + nLUTItemCount = CSLCount(papszValues) / 2; + + padfLUTInputs = (double *) VSIMalloc2(nLUTItemCount, sizeof(double)); + if ( !padfLUTInputs ) + { + CSLDestroy(papszValues); + nLUTItemCount = 0; + return CE_Failure; + } + + padfLUTOutputs = (double *) VSIMalloc2(nLUTItemCount, sizeof(double)); + if ( !padfLUTOutputs ) + { + CSLDestroy(papszValues); + VSIFree( padfLUTInputs ); + padfLUTInputs = NULL; + nLUTItemCount = 0; + return CE_Failure; + } + + for ( nIndex = 0; nIndex < nLUTItemCount; nIndex++ ) + { + padfLUTInputs[nIndex] = CPLAtof( papszValues[nIndex * 2] ); + padfLUTOutputs[nIndex] = CPLAtof( papszValues[nIndex * 2 + 1] ); + + // Enforce the requirement that the LUT input array is monotonically non-decreasing. + if ( nIndex > 0 && padfLUTInputs[nIndex] < padfLUTInputs[nIndex - 1] ) + { + CSLDestroy(papszValues); + VSIFree( padfLUTInputs ); + VSIFree( padfLUTOutputs ); + padfLUTInputs = NULL; + padfLUTOutputs = NULL; + nLUTItemCount = 0; + return CE_Failure; + } + } + + CSLDestroy(papszValues); + } + + if( CPLGetXMLValue(psSrc, "ColorTableComponent", NULL) != NULL ) + { + nColorTableComponent = atoi(CPLGetXMLValue(psSrc, "ColorTableComponent", "0")); + } + + return CE_None; +} + +/************************************************************************/ +/* LookupValue() */ +/************************************************************************/ + +double +VRTComplexSource::LookupValue( double dfInput ) +{ + // Find the index of the first element in the LUT input array that + // is not smaller than the input value. + int i = std::lower_bound(padfLUTInputs, padfLUTInputs + nLUTItemCount, dfInput) - padfLUTInputs; + + if (i == 0) + return padfLUTOutputs[0]; + + // If the index is beyond the end of the LUT input array, the input + // value is larger than all the values in the array. + if (i == nLUTItemCount) + return padfLUTOutputs[nLUTItemCount - 1]; + + if (padfLUTInputs[i] == dfInput) + return padfLUTOutputs[i]; + + // Otherwise, interpolate. + return padfLUTOutputs[i - 1] + (dfInput - padfLUTInputs[i - 1]) * + ((padfLUTOutputs[i] - padfLUTOutputs[i - 1]) / (padfLUTInputs[i] - padfLUTInputs[i - 1])); +} + +/************************************************************************/ +/* SetLinearScaling() */ +/************************************************************************/ + +void VRTComplexSource::SetLinearScaling(double dfOffset, double dfScale) +{ + eScalingType = VRT_SCALING_LINEAR; + dfScaleOff = dfOffset; + dfScaleRatio = dfScale; +} + +/************************************************************************/ +/* SetPowerScaling() */ +/************************************************************************/ + +void VRTComplexSource::SetPowerScaling(double dfExponent, + double dfSrcMin, + double dfSrcMax, + double dfDstMin, + double dfDstMax) +{ + eScalingType = VRT_SCALING_EXPONENTIAL; + this->dfExponent = dfExponent; + this->dfSrcMin = dfSrcMin; + this->dfSrcMax = dfSrcMax; + this->dfDstMin = dfDstMin; + this->dfDstMax = dfDstMax; + bSrcMinMaxDefined = TRUE; +} + +/************************************************************************/ +/* SetColorTableComponent() */ +/************************************************************************/ + +void VRTComplexSource::SetColorTableComponent(int nComponent) +{ + nColorTableComponent = nComponent; +} + +/************************************************************************/ +/* RasterIO() */ +/************************************************************************/ + +CPLErr +VRTComplexSource::RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArgIn) + +{ + GDALRasterIOExtraArg sExtraArg; + + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + GDALRasterIOExtraArg* psExtraArg = &sExtraArg; + + // The window we will actually request from the source raster band. + double dfReqXOff, dfReqYOff, dfReqXSize, dfReqYSize; + int nReqXOff, nReqYOff, nReqXSize, nReqYSize; + + // The window we will actual set _within_ the pData buffer. + int nOutXOff, nOutYOff, nOutXSize, nOutYSize; + + if( !GetSrcDstWindow( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, + &dfReqXOff, &dfReqYOff, &dfReqXSize, &dfReqYSize, + &nReqXOff, &nReqYOff, &nReqXSize, &nReqYSize, + &nOutXOff, &nOutYOff, &nOutXSize, &nOutYSize ) ) + return CE_None; + + if( osResampling.size() ) + { + psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(osResampling); + } + else if( psExtraArgIn != NULL ) + { + psExtraArg->eResampleAlg = psExtraArgIn->eResampleAlg; + } + psExtraArg->bFloatingPointWindowValidity = TRUE; + psExtraArg->dfXOff = dfReqXOff; + psExtraArg->dfYOff = dfReqYOff; + psExtraArg->dfXSize = dfReqXSize; + psExtraArg->dfYSize = dfReqYSize; + + CPLErr eErr = RasterIOInternal(nReqXOff, nReqYOff, nReqXSize, nReqYSize, + ((GByte *)pData) + + nPixelSpace * nOutXOff + + (GPtrDiff_t)nLineSpace * nOutYOff, + nOutXSize, nOutYSize, + eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + + return eErr; +} + +/************************************************************************/ +/* RasterIOInternal() */ +/************************************************************************/ + +/* nReqXOff, nReqYOff, nReqXSize, nReqYSize are expressed in source band */ +/* referential */ +CPLErr VRTComplexSource::RasterIOInternal( int nReqXOff, int nReqYOff, + int nReqXSize, int nReqYSize, + void *pData, int nOutXSize, int nOutYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ +/* -------------------------------------------------------------------- */ +/* Read into a temporary buffer. */ +/* -------------------------------------------------------------------- */ + float *pafData; + CPLErr eErr; + GDALColorTable* poColorTable = NULL; + int bIsComplex = GDALDataTypeIsComplex(eBufType); + GDALDataType eWrkDataType = (bIsComplex) ? GDT_CFloat32 : GDT_Float32; + int nWordSize = GDALGetDataTypeSize(eWrkDataType) / 8; + int bNoDataSetAndNotNan = bNoDataSet && !CPLIsNan(dfNoDataValue); + + if( eScalingType == VRT_SCALING_LINEAR && bNoDataSet == FALSE && dfScaleRatio == 0) + { +/* -------------------------------------------------------------------- */ +/* Optimization when outputing a constant value */ +/* (used by the -addalpha option of gdalbuildvrt) */ +/* -------------------------------------------------------------------- */ + pafData = NULL; + } + else + { + pafData = (float *) VSIMalloc3(nOutXSize,nOutYSize,nWordSize); + if (pafData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + return CE_Failure; + } + + GDALRIOResampleAlg eResampleAlgBack = psExtraArg->eResampleAlg; + if( osResampling.size() ) + { + psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(osResampling); + } + + eErr = poRasterBand->RasterIO( GF_Read, + nReqXOff, nReqYOff, nReqXSize, nReqYSize, + pafData, nOutXSize, nOutYSize, eWrkDataType, + nWordSize, nWordSize * (GSpacing)nOutXSize, psExtraArg ); + if( osResampling.size() ) + psExtraArg->eResampleAlg = eResampleAlgBack; + + if( eErr != CE_None ) + { + CPLFree( pafData ); + return eErr; + } + + if (nColorTableComponent != 0) + { + poColorTable = poRasterBand->GetColorTable(); + if (poColorTable == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Source band has no color table."); + CPLFree( pafData ); + return CE_Failure; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Selectively copy into output buffer with nodata masking, */ +/* and/or scaling. */ +/* -------------------------------------------------------------------- */ + int iX, iY; + + for( iY = 0; iY < nOutYSize; iY++ ) + { + for( iX = 0; iX < nOutXSize; iX++ ) + { + GByte *pDstLocation; + + pDstLocation = ((GByte *)pData) + + nPixelSpace * iX + + (GPtrDiff_t)nLineSpace * iY; + + if (pafData && !bIsComplex) + { + float fResult = pafData[iX + iY * nOutXSize]; + if( CPLIsNan(dfNoDataValue) && CPLIsNan(fResult) ) + continue; + if( bNoDataSetAndNotNan && ARE_REAL_EQUAL(fResult, dfNoDataValue) ) + continue; + + if (nColorTableComponent) + { + const GDALColorEntry* poEntry = poColorTable->GetColorEntry((int)fResult); + if (poEntry) + { + if (nColorTableComponent == 1) + fResult = poEntry->c1; + else if (nColorTableComponent == 2) + fResult = poEntry->c2; + else if (nColorTableComponent == 3) + fResult = poEntry->c3; + else if (nColorTableComponent == 4) + fResult = poEntry->c4; + } + else + { + static int bHasWarned = FALSE; + if (!bHasWarned) + { + bHasWarned = TRUE; + CPLError(CE_Failure, CPLE_AppDefined, + "No entry %d.", (int)fResult); + } + continue; + } + } + + if( eScalingType == VRT_SCALING_LINEAR ) + fResult = (float) (fResult * dfScaleRatio + dfScaleOff); + else if( eScalingType == VRT_SCALING_EXPONENTIAL ) + { + if( !bSrcMinMaxDefined ) + { + int bSuccessMin = FALSE, bSuccessMax = FALSE; + double adfMinMax[2]; + adfMinMax[0] = poRasterBand->GetMinimum(&bSuccessMin); + adfMinMax[1] = poRasterBand->GetMaximum(&bSuccessMax); + if( (bSuccessMin && bSuccessMax) || + poRasterBand->ComputeRasterMinMax( TRUE, adfMinMax ) == CE_None ) + { + dfSrcMin = adfMinMax[0]; + dfSrcMax = adfMinMax[1]; + bSrcMinMaxDefined = TRUE; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot determine source min/max value"); + return CE_Failure; + } + } + + double dfPowVal = + (fResult - dfSrcMin) / (dfSrcMax - dfSrcMin); + if( dfPowVal < 0.0 ) + dfPowVal = 0.0; + else if( dfPowVal > 1.0 ) + dfPowVal = 1.0; + fResult = (float) (dfDstMax - dfDstMin) * pow( dfPowVal, dfExponent ) + dfDstMin; + } + + if (nLUTItemCount) + fResult = (float) LookupValue( fResult ); + + if( eBufType == GDT_Byte ) + *pDstLocation = (GByte) MIN(255,MAX(0,fResult + 0.5)); + else + GDALCopyWords( &fResult, GDT_Float32, 0, + pDstLocation, eBufType, 0, 1 ); + } + else if (pafData && bIsComplex) + { + float afResult[2]; + afResult[0] = pafData[2 * (iX + iY * nOutXSize)]; + afResult[1] = pafData[2 * (iX + iY * nOutXSize) + 1]; + + /* Do not use color table */ + + if( eScalingType == VRT_SCALING_LINEAR ) + { + afResult[0] = (float) (afResult[0] * dfScaleRatio + dfScaleOff); + afResult[1] = (float) (afResult[1] * dfScaleRatio + dfScaleOff); + } + + /* Do not use LUT */ + + if( eBufType == GDT_Byte ) + *pDstLocation = (GByte) MIN(255,MAX(0,afResult[0] + 0.5)); + else + GDALCopyWords( afResult, GDT_CFloat32, 0, + pDstLocation, eBufType, 0, 1 ); + } + else + { + float fResult = (float) dfScaleOff; + + if (nLUTItemCount) + fResult = (float) LookupValue( fResult ); + + if( eBufType == GDT_Byte ) + *pDstLocation = (GByte) MIN(255,MAX(0,fResult + 0.5)); + else + GDALCopyWords( &fResult, GDT_Float32, 0, + pDstLocation, eBufType, 0, 1 ); + } + + } + } + + CPLFree( pafData ); + + return CE_None; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double VRTComplexSource::GetMinimum( int nXSize, int nYSize, int *pbSuccess ) +{ + if (dfScaleOff == 0.0 && dfScaleRatio == 1.0 && + nLUTItemCount == 0 && nColorTableComponent == 0) + { + return VRTSimpleSource::GetMinimum(nXSize, nYSize, pbSuccess); + } + + *pbSuccess = FALSE; + return 0; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double VRTComplexSource::GetMaximum( int nXSize, int nYSize, int *pbSuccess ) +{ + if (dfScaleOff == 0.0 && dfScaleRatio == 1.0 && + nLUTItemCount == 0 && nColorTableComponent == 0) + { + return VRTSimpleSource::GetMaximum(nXSize, nYSize, pbSuccess); + } + + *pbSuccess = FALSE; + return 0; +} + +/************************************************************************/ +/* ComputeRasterMinMax() */ +/************************************************************************/ + +CPLErr VRTComplexSource::ComputeRasterMinMax( int nXSize, int nYSize, int bApproxOK, double* adfMinMax ) +{ + if (dfScaleOff == 0.0 && dfScaleRatio == 1.0 && + nLUTItemCount == 0 && nColorTableComponent == 0) + { + return VRTSimpleSource::ComputeRasterMinMax(nXSize, nYSize, bApproxOK, adfMinMax); + } + + return CE_Failure; +} + +/************************************************************************/ +/* GetHistogram() */ +/************************************************************************/ + +CPLErr VRTComplexSource::GetHistogram( int nXSize, int nYSize, + double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, void *pProgressData ) +{ + if (dfScaleOff == 0.0 && dfScaleRatio == 1.0 && + nLUTItemCount == 0 && nColorTableComponent == 0) + { + return VRTSimpleSource::GetHistogram(nXSize, nYSize, + dfMin, dfMax, nBuckets, + panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData); + } + + return CE_Failure; +} + +/************************************************************************/ +/* ComputeStatistics() */ +/************************************************************************/ + +CPLErr VRTComplexSource::ComputeStatistics( int nXSize, int nYSize, + int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, void *pProgressData ) +{ + if (dfScaleOff == 0.0 && dfScaleRatio == 1.0 && + nLUTItemCount == 0 && nColorTableComponent == 0) + { + return VRTSimpleSource::ComputeStatistics(nXSize, nYSize, bApproxOK, pdfMin, pdfMax, + pdfMean, pdfStdDev, + pfnProgress, pProgressData); + } + + return CE_Failure; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VRTFuncSource */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VRTFuncSource() */ +/************************************************************************/ + +VRTFuncSource::VRTFuncSource() + +{ + pfnReadFunc = NULL; + pCBData = NULL; + fNoDataValue = (float) VRT_NODATA_UNSET; + eType = GDT_Byte; +} + +/************************************************************************/ +/* ~VRTFuncSource() */ +/************************************************************************/ + +VRTFuncSource::~VRTFuncSource() + +{ +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *VRTFuncSource::SerializeToXML( CPL_UNUSED const char * pszVRTPath ) +{ + return NULL; +} + +/************************************************************************/ +/* RasterIO() */ +/************************************************************************/ + +CPLErr +VRTFuncSource::RasterIO( int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + CPL_UNUSED GDALRasterIOExtraArg* psExtraArg ) +{ + if( nPixelSpace*8 == GDALGetDataTypeSize( eBufType ) + && nLineSpace == nPixelSpace * nXSize + && nBufXSize == nXSize && nBufYSize == nYSize + && eBufType == eType ) + { + return pfnReadFunc( pCBData, + nXOff, nYOff, nXSize, nYSize, + pData ); + } + else + { + printf( "%d,%d %d,%d, %d,%d %d,%d %d,%d\n", + (int)nPixelSpace*8, GDALGetDataTypeSize(eBufType), + (int)nLineSpace, (int)nPixelSpace * nXSize, + nBufXSize, nXSize, + nBufYSize, nYSize, + (int) eBufType, (int) eType ); + CPLError( CE_Failure, CPLE_AppDefined, + "VRTFuncSource::RasterIO() - Irregular request." ); + return CE_Failure; + } +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double VRTFuncSource::GetMinimum( CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED CPL_UNUSED int *pbSuccess ) +{ + *pbSuccess = FALSE; + return 0; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double VRTFuncSource::GetMaximum( CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED int *pbSuccess ) +{ + *pbSuccess = FALSE; + return 0; +} + +/************************************************************************/ +/* ComputeRasterMinMax() */ +/************************************************************************/ + +CPLErr VRTFuncSource::ComputeRasterMinMax( CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED int bApproxOK, + CPL_UNUSED double* adfMinMax ) +{ + return CE_Failure; +} + +/************************************************************************/ +/* ComputeStatistics() */ +/************************************************************************/ + +CPLErr VRTFuncSource::ComputeStatistics( CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED int bApproxOK, + CPL_UNUSED double *pdfMin, + CPL_UNUSED double *pdfMax, + CPL_UNUSED double *pdfMean, + CPL_UNUSED double *pdfStdDev, + CPL_UNUSED GDALProgressFunc pfnProgress, + CPL_UNUSED void *pProgressData ) +{ + return CE_Failure; +} + +/************************************************************************/ +/* GetHistogram() */ +/************************************************************************/ + +CPLErr VRTFuncSource::GetHistogram( CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED double dfMin, + CPL_UNUSED double dfMax, + CPL_UNUSED int nBuckets, + CPL_UNUSED GUIntBig * panHistogram, + CPL_UNUSED int bIncludeOutOfRange, + CPL_UNUSED int bApproxOK, + CPL_UNUSED GDALProgressFunc pfnProgress, + CPL_UNUSED void *pProgressData ) +{ + return CE_Failure; +} + +/************************************************************************/ +/* VRTParseCoreSources() */ +/************************************************************************/ + +VRTSource *VRTParseCoreSources( CPLXMLNode *psChild, const char *pszVRTPath ) + +{ + VRTSource * poSource; + + if( EQUAL(psChild->pszValue,"AveragedSource") + || (EQUAL(psChild->pszValue,"SimpleSource") + && EQUALN(CPLGetXMLValue(psChild, "Resampling", "Nearest"), + "Aver",4)) ) + { + poSource = new VRTAveragedSource(); + } + else if( EQUAL(psChild->pszValue,"SimpleSource") ) + { + poSource = new VRTSimpleSource(); + } + else if( EQUAL(psChild->pszValue,"ComplexSource") ) + { + poSource = new VRTComplexSource(); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "VRTParseCoreSources() - Unknown source : %s", psChild->pszValue ); + return NULL; + } + + if ( poSource->XMLInit( psChild, pszVRTPath ) == CE_None ) + return poSource; + + delete poSource; + return NULL; +} diff --git a/bazaar/plugin/gdal/frmts/vrt/vrtwarped.cpp b/bazaar/plugin/gdal/frmts/vrt/vrtwarped.cpp new file mode 100644 index 000000000..93cf4727c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/vrt/vrtwarped.cpp @@ -0,0 +1,1648 @@ +/****************************************************************************** + * $Id: vrtwarped.cpp 28292 2015-01-05 19:35:55Z rouault $ + * + * Project: Virtual GDAL Datasets + * Purpose: Implementation of VRTWarpedRasterBand *and VRTWarpedDataset. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "vrtdataset.h" +#include "cpl_minixml.h" +#include "cpl_string.h" +#include "gdalwarper.h" +#include "gdal_alg_priv.h" +#include + +CPL_CVSID("$Id: vrtwarped.cpp 28292 2015-01-05 19:35:55Z rouault $"); + +/************************************************************************/ +/* GDALAutoCreateWarpedVRT() */ +/************************************************************************/ + +/** + * Create virtual warped dataset automatically. + * + * This function will create a warped virtual file representing the + * input image warped into the target coordinate system. A GenImgProj + * transformation is created to accomplish any required GCP/Geotransform + * warp and reprojection to the target coordinate system. The output virtual + * dataset will be "northup" in the target coordinate system. The + * GDALSuggestedWarpOutput() function is used to determine the bounds and + * resolution of the output virtual file which should be large enough to + * include all the input image + * + * Note that the constructed GDALDatasetH will acquire one or more references + * to the passed in hSrcDS. Reference counting semantics on the source + * dataset should be honoured. That is, don't just GDALClose() it unless it + * was opened with GDALOpenShared(). + * + * The returned dataset will have no associated filename for itself. If you + * want to write the virtual dataset description to a file, use the + * GDALSetDescription() function (or SetDescription() method) on the dataset + * to assign a filename before it is closed. + * + * @param hSrcDS The source dataset. + * + * @param pszSrcWKT The coordinate system of the source image. If NULL, it + * will be read from the source image. + * + * @param pszDstWKT The coordinate system to convert to. If NULL no change + * of coordinate system will take place. + * + * @param eResampleAlg One of GRA_NearestNeighbour, GRA_Bilinear, GRA_Cubic, + * GRA_CubicSpline, GRA_Lanczos, GRA_Average or GRA_Mode. + * Controls the sampling method used. + * + * @param dfMaxError Maximum error measured in input pixels that is allowed in + * approximating the transformation (0.0 for exact calculations). + * + * @param psOptionsIn Additional warp options, normally NULL. + * + * @return NULL on failure, or a new virtual dataset handle on success. + */ + +GDALDatasetH CPL_STDCALL +GDALAutoCreateWarpedVRT( GDALDatasetH hSrcDS, + const char *pszSrcWKT, + const char *pszDstWKT, + GDALResampleAlg eResampleAlg, + double dfMaxError, + const GDALWarpOptions *psOptionsIn ) + +{ + GDALWarpOptions *psWO; + int i; + + VALIDATE_POINTER1( hSrcDS, "GDALAutoCreateWarpedVRT", NULL ); + +/* -------------------------------------------------------------------- */ +/* Populate the warp options. */ +/* -------------------------------------------------------------------- */ + if( psOptionsIn != NULL ) + psWO = GDALCloneWarpOptions( psOptionsIn ); + else + psWO = GDALCreateWarpOptions(); + + psWO->eResampleAlg = eResampleAlg; + + psWO->hSrcDS = hSrcDS; + + psWO->nBandCount = GDALGetRasterCount( hSrcDS ); + psWO->panSrcBands = (int *) CPLMalloc(sizeof(int) * psWO->nBandCount); + psWO->panDstBands = (int *) CPLMalloc(sizeof(int) * psWO->nBandCount); + + for( i = 0; i < psWO->nBandCount; i++ ) + { + psWO->panSrcBands[i] = i+1; + psWO->panDstBands[i] = i+1; + } + + /* TODO: should fill in no data where available */ + +/* -------------------------------------------------------------------- */ +/* Create the transformer. */ +/* -------------------------------------------------------------------- */ + psWO->pfnTransformer = GDALGenImgProjTransform; + psWO->pTransformerArg = + GDALCreateGenImgProjTransformer( psWO->hSrcDS, pszSrcWKT, + NULL, pszDstWKT, + TRUE, 1.0, 0 ); + + if( psWO->pTransformerArg == NULL ) + { + GDALDestroyWarpOptions( psWO ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Figure out the desired output bounds and resolution. */ +/* -------------------------------------------------------------------- */ + double adfDstGeoTransform[6]; + int nDstPixels, nDstLines; + CPLErr eErr; + + eErr = + GDALSuggestedWarpOutput( hSrcDS, psWO->pfnTransformer, + psWO->pTransformerArg, + adfDstGeoTransform, &nDstPixels, &nDstLines ); + if( eErr != CE_None ) + { + GDALDestroyTransformer( psWO->pTransformerArg ); + GDALDestroyWarpOptions( psWO ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Update the transformer to include an output geotransform */ +/* back to pixel/line coordinates. */ +/* */ +/* -------------------------------------------------------------------- */ + GDALSetGenImgProjTransformerDstGeoTransform( + psWO->pTransformerArg, adfDstGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Do we want to apply an approximating transformation? */ +/* -------------------------------------------------------------------- */ + if( dfMaxError > 0.0 ) + { + psWO->pTransformerArg = + GDALCreateApproxTransformer( psWO->pfnTransformer, + psWO->pTransformerArg, + dfMaxError ); + psWO->pfnTransformer = GDALApproxTransform; + GDALApproxTransformerOwnsSubtransformer(psWO->pTransformerArg, TRUE); + } + +/* -------------------------------------------------------------------- */ +/* Create the VRT file. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hDstDS; + + hDstDS = GDALCreateWarpedVRT( hSrcDS, nDstPixels, nDstLines, + adfDstGeoTransform, psWO ); + + GDALDestroyWarpOptions( psWO ); + + if( pszDstWKT != NULL ) + GDALSetProjection( hDstDS, pszDstWKT ); + else if( pszSrcWKT != NULL ) + GDALSetProjection( hDstDS, pszSrcWKT ); + else if( GDALGetGCPCount( hSrcDS ) > 0 ) + GDALSetProjection( hDstDS, GDALGetGCPProjection( hSrcDS ) ); + else + GDALSetProjection( hDstDS, GDALGetProjectionRef( hSrcDS ) ); + + return hDstDS; +} + +/************************************************************************/ +/* GDALCreateWarpedVRT() */ +/************************************************************************/ + +/** + * Create virtual warped dataset. + * + * This function will create a warped virtual file representing the + * input image warped based on a provided transformation. Output bounds + * and resolution are provided explicitly. + * + * Note that the constructed GDALDatasetH will acquire one or more references + * to the passed in hSrcDS. Reference counting semantics on the source + * dataset should be honoured. That is, don't just GDALClose() it unless it + * was opened with GDALOpenShared(). + * + * @param hSrcDS The source dataset. + * + * @param nPixels Width of the virtual warped dataset to create + * + * @param nLines Height of the virtual warped dataset to create + * + * @param padfGeoTransform Geotransform matrix of the virtual warped dataset to create + * + * @param psOptions Warp options. Must be different from NULL. + * + * @return NULL on failure, or a new virtual dataset handle on success. + */ + +GDALDatasetH CPL_STDCALL +GDALCreateWarpedVRT( GDALDatasetH hSrcDS, + int nPixels, int nLines, double *padfGeoTransform, + GDALWarpOptions *psOptions ) + +{ + VALIDATE_POINTER1( hSrcDS, "GDALCreateWarpedVRT", NULL ); + +/* -------------------------------------------------------------------- */ +/* Create the VRTDataset and populate it with bands. */ +/* -------------------------------------------------------------------- */ + VRTWarpedDataset *poDS = new VRTWarpedDataset( nPixels, nLines ); + int i; + + psOptions->hDstDS = (GDALDatasetH) poDS; + + poDS->SetGeoTransform( padfGeoTransform ); + + for( i = 0; i < psOptions->nBandCount; i++ ) + { + VRTWarpedRasterBand *poBand; + GDALRasterBand *poSrcBand = (GDALRasterBand *) + GDALGetRasterBand( hSrcDS, i+1 ); + + poDS->AddBand( poSrcBand->GetRasterDataType(), NULL ); + + poBand = (VRTWarpedRasterBand *) poDS->GetRasterBand( i+1 ); + poBand->CopyCommonInfoFrom( poSrcBand ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize the warp on the VRTWarpedDataset. */ +/* -------------------------------------------------------------------- */ + poDS->Initialize( psOptions ); + + return (GDALDatasetH) poDS; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VRTWarpedDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VRTWarpedDataset() */ +/************************************************************************/ + +VRTWarpedDataset::VRTWarpedDataset( int nXSize, int nYSize ) + : VRTDataset( nXSize, nYSize ) + +{ + poWarper = NULL; + nBlockXSize = MIN(nXSize, 512); + nBlockYSize = MIN(nYSize, 128); + eAccess = GA_Update; + + nOverviewCount = 0; + papoOverviews = NULL; + nSrcOvrLevel = -2; +} + +/************************************************************************/ +/* ~VRTWarpedDataset() */ +/************************************************************************/ + +VRTWarpedDataset::~VRTWarpedDataset() + +{ + CloseDependentDatasets(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int VRTWarpedDataset::CloseDependentDatasets() +{ + FlushCache(); + + int bHasDroppedRef = VRTDataset::CloseDependentDatasets(); + +/* -------------------------------------------------------------------- */ +/* Cleanup overviews. */ +/* -------------------------------------------------------------------- */ + int iOverview; + + for( iOverview = 0; iOverview < nOverviewCount; iOverview++ ) + { + GDALDatasetH hDS = (GDALDatasetH) papoOverviews[iOverview]; + + if( GDALDereferenceDataset( hDS ) < 1 ) + { + GDALReferenceDataset( hDS ); + GDALClose( hDS ); + bHasDroppedRef = TRUE; + } + } + + CPLFree( papoOverviews ); + nOverviewCount = 0; + papoOverviews = NULL; + +/* -------------------------------------------------------------------- */ +/* Cleanup warper if one is in effect. */ +/* -------------------------------------------------------------------- */ + if( poWarper != NULL ) + { + const GDALWarpOptions *psWO = poWarper->GetOptions(); + +/* -------------------------------------------------------------------- */ +/* We take care to only call GDALClose() on psWO->hSrcDS if the */ +/* reference count drops to zero. This is makes it so that we */ +/* can operate reference counting semantics more-or-less */ +/* properly even if the dataset isn't open in shared mode, */ +/* though we require that the caller also honour the reference */ +/* counting semantics even though it isn't a shared dataset. */ +/* -------------------------------------------------------------------- */ + if( psWO->hSrcDS != NULL ) + { + if( GDALDereferenceDataset( psWO->hSrcDS ) < 1 ) + { + GDALReferenceDataset( psWO->hSrcDS ); + GDALClose( psWO->hSrcDS ); + bHasDroppedRef = TRUE; + } + } + +/* -------------------------------------------------------------------- */ +/* We are responsible for cleaning up the transformer outselves. */ +/* -------------------------------------------------------------------- */ + if( psWO->pTransformerArg != NULL ) + GDALDestroyTransformer( psWO->pTransformerArg ); + + delete poWarper; + poWarper = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Destroy the raster bands if they exist. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < nBands; iBand++ ) + { + delete papoBands[iBand]; + } + nBands = 0; + + return bHasDroppedRef; +} + +/************************************************************************/ +/* SetSrcOverviewLevel() */ +/************************************************************************/ + +CPLErr VRTWarpedDataset::SetMetadataItem( const char *pszName, const char *pszValue, + const char *pszDomain ) + +{ + + if( (pszDomain == NULL || EQUAL(pszDomain, "")) && EQUAL(pszName, "SrcOvrLevel") ) + { + int nOldValue = nSrcOvrLevel; + if( EQUAL(pszValue, "AUTO") ) + nSrcOvrLevel = -2; + else if( EQUALN(pszValue,"AUTO-",5) ) + nSrcOvrLevel = -2-atoi(pszValue + 5); + else if( EQUAL(pszValue, "NONE") ) + nSrcOvrLevel = -1; + else if( CPLGetValueType(pszValue) == CPL_VALUE_INTEGER ) + nSrcOvrLevel = atoi(pszValue); + if( nSrcOvrLevel != nOldValue ) + SetNeedsFlush(); + return CE_None; + } + return VRTDataset::SetMetadataItem(pszName, pszValue, pszDomain); +} + +/************************************************************************/ +/* Initialize() */ +/* */ +/* Initialize a dataset from passed in warp options. */ +/************************************************************************/ + +CPLErr VRTWarpedDataset::Initialize( void *psWO ) + +{ + if( poWarper != NULL ) + delete poWarper; + + poWarper = new GDALWarpOperation(); + + GDALWarpOptions* psWO_Dup = GDALCloneWarpOptions((GDALWarpOptions *) psWO); + + /* Avoid errors when adding an alpha band, but source dataset has */ + /* no alpha band (#4571) */ + if (CSLFetchNameValue( psWO_Dup->papszWarpOptions, "INIT_DEST" ) == NULL) + psWO_Dup->papszWarpOptions = CSLSetNameValue(psWO_Dup->papszWarpOptions, "INIT_DEST", "0"); + + // The act of initializing this warped dataset with this warp options + // will result in our assuming ownership of a reference to the + // hSrcDS. + + if( ((GDALWarpOptions *) psWO)->hSrcDS != NULL ) + GDALReferenceDataset( psWO_Dup->hSrcDS ); + + CPLErr eErr = poWarper->Initialize( psWO_Dup ); + + GDALDestroyWarpOptions(psWO_Dup); + + return eErr; +} + +/************************************************************************/ +/* CreateImplicitOverviews() */ +/* */ +/* For each overview of the source dataset, create an overview */ +/* in the warped VRT dataset. */ +/************************************************************************/ + +void VRTWarpedDataset::CreateImplicitOverviews() +{ + if( poWarper == NULL || nOverviewCount != 0 ) + return; + + const GDALWarpOptions *psWO = poWarper->GetOptions(); + + if( psWO->hSrcDS == NULL || GDALGetRasterCount(psWO->hSrcDS) == 0 ) + return; + + GDALDataset* poSrcDS = (GDALDataset*)psWO->hSrcDS; + int nOvrCount = poSrcDS->GetRasterBand(1)->GetOverviewCount(); + for(int iOvr = 0; iOvr < nOvrCount; iOvr++) + { + int bDeleteSrcOvrDataset = FALSE; + GDALDataset* poSrcOvrDS = poSrcDS; + if( nSrcOvrLevel < -2 ) + { + if( iOvr + nSrcOvrLevel + 2 >= 0 ) + { + bDeleteSrcOvrDataset = TRUE; + poSrcOvrDS = GDALCreateOverviewDataset(poSrcDS, + iOvr + nSrcOvrLevel + 2, FALSE, FALSE); + } + } + else if( nSrcOvrLevel == -2 ) + { + bDeleteSrcOvrDataset = TRUE; + poSrcOvrDS = GDALCreateOverviewDataset(poSrcDS, iOvr, FALSE, FALSE); + } + else if( nSrcOvrLevel >= 0 ) + { + bDeleteSrcOvrDataset = TRUE; + poSrcOvrDS = GDALCreateOverviewDataset(poSrcDS, nSrcOvrLevel, TRUE, FALSE); + } + if( poSrcOvrDS == NULL ) + break; + + double dfSrcRatioX = (double)poSrcDS->GetRasterXSize() / poSrcOvrDS->GetRasterXSize(); + double dfSrcRatioY = (double)poSrcDS->GetRasterYSize() / poSrcOvrDS->GetRasterYSize(); + double dfTargetRatio = (double)poSrcDS->GetRasterXSize() / + poSrcDS->GetRasterBand(1)->GetOverview(iOvr)->GetXSize(); + +/* -------------------------------------------------------------------- */ +/* Figure out the desired output bounds and resolution. */ +/* -------------------------------------------------------------------- */ + double adfDstGeoTransform[6]; + int nDstPixels, nDstLines; + + nDstPixels = (int)(nRasterXSize / dfTargetRatio + 0.5); + nDstLines = (int)(nRasterYSize / dfTargetRatio + 0.5); + GetGeoTransform(adfDstGeoTransform); + if( adfDstGeoTransform[2] == 0.0 && adfDstGeoTransform[4] == 0.0 ) + { + adfDstGeoTransform[1] *= (double)nRasterXSize / nDstPixels; + adfDstGeoTransform[5] *= (double)nRasterYSize / nDstLines; + } + else + { + adfDstGeoTransform[1] *= dfTargetRatio; + adfDstGeoTransform[2] *= dfTargetRatio; + adfDstGeoTransform[4] *= dfTargetRatio; + adfDstGeoTransform[5] *= dfTargetRatio; + } + + if( nDstPixels < 1 || nDstLines < 1 ) + { + if( bDeleteSrcOvrDataset ) + delete poSrcOvrDS; + break; + } + +/* -------------------------------------------------------------------- */ +/* Create transformer and warping options. */ +/* -------------------------------------------------------------------- */ + void *pTransformerArg = GDALCreateSimilarTransformer(psWO->pTransformerArg, + dfSrcRatioX, + dfSrcRatioY); + if( pTransformerArg == NULL ) + { + if( bDeleteSrcOvrDataset ) + delete poSrcOvrDS; + break; + } + + GDALWarpOptions* psWOOvr = GDALCloneWarpOptions( psWO ); + psWOOvr->hSrcDS = (GDALDatasetH)poSrcOvrDS; + psWOOvr->pfnTransformer = psWO->pfnTransformer; + psWOOvr->pTransformerArg = pTransformerArg; + +/* -------------------------------------------------------------------- */ +/* Update the transformer to include an output geotransform */ +/* back to pixel/line coordinates. */ +/* */ +/* -------------------------------------------------------------------- */ + GDALSetTransformerDstGeoTransform( + psWOOvr->pTransformerArg, adfDstGeoTransform ); + +/* -------------------------------------------------------------------- */ +/* Create the VRT file. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hDstDS; + + hDstDS = GDALCreateWarpedVRT( (GDALDatasetH)poSrcOvrDS, + nDstPixels, nDstLines, + adfDstGeoTransform, psWOOvr ); + + if( bDeleteSrcOvrDataset ) + GDALDereferenceDataset( (GDALDatasetH)poSrcOvrDS ); + + GDALDestroyWarpOptions(psWOOvr); + + if( hDstDS == NULL ) + { + GDALDestroyTransformer( pTransformerArg ); + break; + } + + nOverviewCount++; + papoOverviews = (VRTWarpedDataset **) + CPLRealloc( papoOverviews, sizeof(void*) * nOverviewCount ); + + papoOverviews[nOverviewCount-1] = (VRTWarpedDataset*)hDstDS; + } +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char** VRTWarpedDataset::GetFileList() +{ + char** papszFileList = GDALDataset::GetFileList(); + + if( poWarper != NULL ) + { + const GDALWarpOptions *psWO = poWarper->GetOptions(); + const char* pszFilename; + + if( psWO->hSrcDS != NULL && + (pszFilename = + ((GDALDataset*)psWO->hSrcDS)->GetDescription()) != NULL ) + { + VSIStatBufL sStat; + if( VSIStatL( pszFilename, &sStat ) == 0 ) + { + papszFileList = CSLAddString(papszFileList, pszFilename); + } + } + } + + return papszFileList; +} + + + +/************************************************************************/ +/* ==================================================================== */ +/* VRTWarpedOverviewTransformer */ +/* ==================================================================== */ +/************************************************************************/ + +typedef struct { + GDALTransformerInfo sTI; + + GDALTransformerFunc pfnBaseTransformer; + void *pBaseTransformerArg; + int bOwnSubtransformer; + + double dfXOverviewFactor; + double dfYOverviewFactor; +} VWOTInfo; + + +static +void* VRTCreateWarpedOverviewTransformer( GDALTransformerFunc pfnBaseTransformer, + void *pBaseTransformArg, + double dfXOverviewFactor, + double dfYOverviewFactor ); +static +void VRTDestroyWarpedOverviewTransformer(void* pTransformArg); + +static +int VRTWarpedOverviewTransform( void *pTransformArg, int bDstToSrc, + int nPointCount, + double *padfX, double *padfY, double *padfZ, + int *panSuccess ); + +#if 0 +/************************************************************************/ +/* VRTSerializeWarpedOverviewTransformer() */ +/************************************************************************/ + +static CPLXMLNode * +VRTSerializeWarpedOverviewTransformer( void *pTransformArg ) + +{ + CPLXMLNode *psTree; + VWOTInfo *psInfo = (VWOTInfo *) pTransformArg; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "WarpedOverviewTransformer" ); + + CPLCreateXMLElementAndValue( psTree, "XFactor", + CPLString().Printf("%g",psInfo->dfXOverviewFactor) ); + CPLCreateXMLElementAndValue( psTree, "YFactor", + CPLString().Printf("%g",psInfo->dfYOverviewFactor) ); + +/* -------------------------------------------------------------------- */ +/* Capture underlying transformer. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psTransformerContainer; + CPLXMLNode *psTransformer; + + psTransformerContainer = + CPLCreateXMLNode( psTree, CXT_Element, "BaseTransformer" ); + + psTransformer = GDALSerializeTransformer( psInfo->pfnBaseTransformer, + psInfo->pBaseTransformerArg ); + if( psTransformer != NULL ) + CPLAddXMLChild( psTransformerContainer, psTransformer ); + + return psTree; +} + +/************************************************************************/ +/* VRTWarpedOverviewTransformerOwnsSubtransformer() */ +/************************************************************************/ + +static void VRTWarpedOverviewTransformerOwnsSubtransformer( void *pTransformArg, + int bOwnFlag ) +{ + VWOTInfo *psInfo = + (VWOTInfo *) pTransformArg; + + psInfo->bOwnSubtransformer = bOwnFlag; +} + +/************************************************************************/ +/* VRTDeserializeWarpedOverviewTransformer() */ +/************************************************************************/ + +void* VRTDeserializeWarpedOverviewTransformer( CPLXMLNode *psTree ) + +{ + double dfXOverviewFactor = CPLAtof(CPLGetXMLValue( psTree, "XFactor", "1" )); + double dfYOverviewFactor = CPLAtof(CPLGetXMLValue( psTree, "YFactor", "1" )); + CPLXMLNode *psContainer; + GDALTransformerFunc pfnBaseTransform = NULL; + void *pBaseTransformerArg = NULL; + + psContainer = CPLGetXMLNode( psTree, "BaseTransformer" ); + + if( psContainer != NULL && psContainer->psChild != NULL ) + { + GDALDeserializeTransformer( psContainer->psChild, + &pfnBaseTransform, + &pBaseTransformerArg ); + + } + + if( pfnBaseTransform == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot get base transform for scaled coord transformer." ); + return NULL; + } + else + { + void *pApproxCBData = + VRTCreateWarpedOverviewTransformer( pfnBaseTransform, + pBaseTransformerArg, + dfXOverviewFactor, + dfYOverviewFactor ); + VRTWarpedOverviewTransformerOwnsSubtransformer( pApproxCBData, TRUE ); + + return pApproxCBData; + } +} +#endif + +/************************************************************************/ +/* VRTCreateWarpedOverviewTransformer() */ +/************************************************************************/ + +static +void* VRTCreateWarpedOverviewTransformer( GDALTransformerFunc pfnBaseTransformer, + void *pBaseTransformerArg, + double dfXOverviewFactor, + double dfYOverviewFactor) + +{ + VWOTInfo *psSCTInfo; + + if (pfnBaseTransformer == NULL) + return NULL; + + psSCTInfo = (VWOTInfo*) + CPLMalloc(sizeof(VWOTInfo)); + psSCTInfo->pfnBaseTransformer = pfnBaseTransformer; + psSCTInfo->pBaseTransformerArg = pBaseTransformerArg; + psSCTInfo->dfXOverviewFactor = dfXOverviewFactor; + psSCTInfo->dfYOverviewFactor = dfYOverviewFactor; + psSCTInfo->bOwnSubtransformer = FALSE; + + memcpy( psSCTInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE, strlen(GDAL_GTI2_SIGNATURE) ); + psSCTInfo->sTI.pszClassName = "VRTWarpedOverviewTransformer"; + psSCTInfo->sTI.pfnTransform = VRTWarpedOverviewTransform; + psSCTInfo->sTI.pfnCleanup = VRTDestroyWarpedOverviewTransformer; +#if 0 + psSCTInfo->sTI.pfnSerialize = VRTSerializeWarpedOverviewTransformer; +#endif + return psSCTInfo; +} + +/************************************************************************/ +/* VRTDestroyWarpedOverviewTransformer() */ +/************************************************************************/ + +static +void VRTDestroyWarpedOverviewTransformer(void* pTransformArg) +{ + VWOTInfo *psInfo = (VWOTInfo *) pTransformArg; + + if( psInfo->bOwnSubtransformer ) + GDALDestroyTransformer( psInfo->pBaseTransformerArg ); + + CPLFree( psInfo ); +} + +/************************************************************************/ +/* VRTWarpedOverviewTransform() */ +/************************************************************************/ + +static +int VRTWarpedOverviewTransform( void *pTransformArg, int bDstToSrc, + int nPointCount, + double *padfX, double *padfY, double *padfZ, + int *panSuccess ) + +{ + VWOTInfo *psInfo = (VWOTInfo *) pTransformArg; + int i, bSuccess; + + if( bDstToSrc ) + { + for( i = 0; i < nPointCount; i++ ) + { + padfX[i] *= psInfo->dfXOverviewFactor; + padfY[i] *= psInfo->dfYOverviewFactor; + } + } + + bSuccess = psInfo->pfnBaseTransformer( psInfo->pBaseTransformerArg, + bDstToSrc, + nPointCount, padfX, padfY, padfZ, + panSuccess ); + + if( !bDstToSrc ) + { + for( i = 0; i < nPointCount; i++ ) + { + padfX[i] /= psInfo->dfXOverviewFactor; + padfY[i] /= psInfo->dfYOverviewFactor; + } + } + + return bSuccess; +} + +/************************************************************************/ +/* BuildOverviews() */ +/* */ +/* For overviews, we actually just build a whole new dataset */ +/* with an extra layer of transformation on the warper used to */ +/* accomplish downsampling by the desired factor. */ +/************************************************************************/ + +CPLErr +VRTWarpedDataset::IBuildOverviews( CPL_UNUSED const char *pszResampling, + int nOverviews, + int *panOverviewList, + CPL_UNUSED int nListBands, + CPL_UNUSED int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + if( poWarper == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Initial progress result. */ +/* -------------------------------------------------------------------- */ + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Establish which of the overview levels we already have, and */ +/* which are new. */ +/* -------------------------------------------------------------------- */ + int i, nNewOverviews, *panNewOverviewList = NULL; + + nNewOverviews = 0; + panNewOverviewList = (int *) CPLCalloc(sizeof(int),nOverviews); + for( i = 0; i < nOverviews; i++ ) + { + int j; + + for( j = 0; j < nOverviewCount; j++ ) + { + int nOvFactor; + GDALDataset *poOverview = papoOverviews[j]; + + nOvFactor = GDALComputeOvFactor(poOverview->GetRasterXSize(), + GetRasterXSize(), + poOverview->GetRasterYSize(), + GetRasterYSize()); + + if( nOvFactor == panOverviewList[i] + || nOvFactor == GDALOvLevelAdjust2( panOverviewList[i], + GetRasterXSize(), + GetRasterYSize() ) ) + panOverviewList[i] *= -1; + } + + if( panOverviewList[i] > 0 ) + panNewOverviewList[nNewOverviews++] = panOverviewList[i]; + } + +/* -------------------------------------------------------------------- */ +/* Create each missing overview (we don't need to do anything */ +/* to update existing overviews). */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nNewOverviews; i++ ) + { + int nOXSize, nOYSize, iBand; + VRTWarpedDataset *poOverviewDS; + +/* -------------------------------------------------------------------- */ +/* What size should this overview be. */ +/* -------------------------------------------------------------------- */ + nOXSize = (GetRasterXSize() + panNewOverviewList[i] - 1) + / panNewOverviewList[i]; + + nOYSize = (GetRasterYSize() + panNewOverviewList[i] - 1) + / panNewOverviewList[i]; + +/* -------------------------------------------------------------------- */ +/* Find the most appropriate base dataset onto which to build the */ +/* new one. The preference will be an overview dataset with a ratio*/ +/* greater than ours, and which is not using */ +/* VRTWarpedOverviewTransform, since those ones are slow. The other*/ +/* ones are based on overviews of the source dataset. */ +/* -------------------------------------------------------------------- */ + VRTWarpedDataset* poBaseDataset = this; + for( int j = 0; j < nOverviewCount; j++ ) + { + if( papoOverviews[j]->GetRasterXSize() > nOXSize && + papoOverviews[j]->poWarper->GetOptions()->pfnTransformer != + VRTWarpedOverviewTransform && + papoOverviews[j]->GetRasterXSize() < poBaseDataset->GetRasterXSize() ) + { + poBaseDataset = papoOverviews[j]; + } + } + +/* -------------------------------------------------------------------- */ +/* Create the overview dataset. */ +/* -------------------------------------------------------------------- */ + poOverviewDS = new VRTWarpedDataset( nOXSize, nOYSize ); + + for( iBand = 0; iBand < GetRasterCount(); iBand++ ) + { + GDALRasterBand *poOldBand = GetRasterBand(iBand+1); + VRTWarpedRasterBand *poNewBand = + new VRTWarpedRasterBand( poOverviewDS, iBand+1, + poOldBand->GetRasterDataType() ); + + poNewBand->CopyCommonInfoFrom( poOldBand ); + poOverviewDS->SetBand( iBand+1, poNewBand ); + } + + nOverviewCount++; + papoOverviews = (VRTWarpedDataset **) + CPLRealloc( papoOverviews, sizeof(void*) * nOverviewCount ); + + papoOverviews[nOverviewCount-1] = poOverviewDS; + +/* -------------------------------------------------------------------- */ +/* Prepare update transformation information that will apply */ +/* the overview decimation. */ +/* -------------------------------------------------------------------- */ + GDALWarpOptions *psWO = (GDALWarpOptions *) poBaseDataset->poWarper->GetOptions(); + +/* -------------------------------------------------------------------- */ +/* Initialize the new dataset with adjusted warp options, and */ +/* then restore to original condition. */ +/* -------------------------------------------------------------------- */ + GDALTransformerFunc pfnTransformerBase = psWO->pfnTransformer; + void* pTransformerBaseArg = psWO->pTransformerArg; + + psWO->pfnTransformer = VRTWarpedOverviewTransform; + psWO->pTransformerArg = VRTCreateWarpedOverviewTransformer( + pfnTransformerBase, + pTransformerBaseArg, + poBaseDataset->GetRasterXSize() / (double) nOXSize, + poBaseDataset->GetRasterYSize() / (double) nOYSize ); + + poOverviewDS->Initialize( psWO ); + + psWO->pfnTransformer = pfnTransformerBase; + psWO->pTransformerArg = pTransformerBaseArg; + } + + CPLFree( panNewOverviewList ); + +/* -------------------------------------------------------------------- */ +/* Progress finished. */ +/* -------------------------------------------------------------------- */ + pfnProgress( 1.0, NULL, pProgressData ); + + SetNeedsFlush(); + + return CE_None; +} + +/************************************************************************/ +/* GDALInitializeWarpedVRT() */ +/************************************************************************/ + +/** + * Set warp info on virtual warped dataset. + * + * Initializes all the warping information for a virtual warped dataset. + * + * This method is the same as the C++ method VRTWarpedDataset::Initialize(). + * + * @param hDS dataset previously created with the VRT driver, and a + * SUBCLASS of "VRTWarpedDataset". + * + * @param psWO the warp options to apply. Note that ownership of the + * transformation information is taken over by the function though everything + * else remains the property of the caller. + * + * @return CE_None on success or CE_Failure if an error occurs. + */ + +CPLErr CPL_STDCALL +GDALInitializeWarpedVRT( GDALDatasetH hDS, GDALWarpOptions *psWO ) + +{ + VALIDATE_POINTER1( hDS, "GDALInitializeWarpedVRT", CE_Failure ); + + return ((VRTWarpedDataset *) hDS)->Initialize( psWO ); +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr VRTWarpedDataset::XMLInit( CPLXMLNode *psTree, const char *pszVRTPath ) + +{ + CPLErr eErr; + +/* -------------------------------------------------------------------- */ +/* Initialize blocksize before calling sub-init so that the */ +/* band initializers can get it from the dataset object when */ +/* they are created. */ +/* -------------------------------------------------------------------- */ + nBlockXSize = atoi(CPLGetXMLValue(psTree,"BlockXSize","512")); + nBlockYSize = atoi(CPLGetXMLValue(psTree,"BlockYSize","128")); + +/* -------------------------------------------------------------------- */ +/* Initialize all the general VRT stuff. This will even */ +/* create the VRTWarpedRasterBands and initialize them. */ +/* -------------------------------------------------------------------- */ + eErr = VRTDataset::XMLInit( psTree, pszVRTPath ); + + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Find the GDALWarpOptions XML tree. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psOptionsTree; + psOptionsTree = CPLGetXMLNode( psTree, "GDALWarpOptions" ); + if( psOptionsTree == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Count not find required GDALWarpOptions in XML." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Adjust the SourceDataset in the warp options to take into */ +/* account that it is relative to the VRT if appropriate. */ +/* -------------------------------------------------------------------- */ + int bRelativeToVRT = + atoi(CPLGetXMLValue(psOptionsTree, + "SourceDataset.relativeToVRT", "0" )); + + const char *pszRelativePath = CPLGetXMLValue(psOptionsTree, + "SourceDataset", "" ); + char *pszAbsolutePath; + + if( bRelativeToVRT ) + pszAbsolutePath = + CPLStrdup(CPLProjectRelativeFilename( pszVRTPath, + pszRelativePath ) ); + else + pszAbsolutePath = CPLStrdup(pszRelativePath); + + CPLSetXMLValue( psOptionsTree, "SourceDataset", pszAbsolutePath ); + CPLFree( pszAbsolutePath ); + +/* -------------------------------------------------------------------- */ +/* And instantiate the warp options, and corresponding warp */ +/* operation. */ +/* -------------------------------------------------------------------- */ + GDALWarpOptions *psWO; + + psWO = GDALDeserializeWarpOptions( psOptionsTree ); + if( psWO == NULL ) + return CE_Failure; + + /* Avoid errors when adding an alpha band, but source dataset has */ + /* no alpha band (#4571) */ + if (CSLFetchNameValue( psWO->papszWarpOptions, "INIT_DEST" ) == NULL) + psWO->papszWarpOptions = CSLSetNameValue(psWO->papszWarpOptions, "INIT_DEST", "0"); + + this->eAccess = GA_Update; + + if( psWO->hDstDS != NULL ) + { + GDALClose( psWO->hDstDS ); + psWO->hDstDS = NULL; + } + + psWO->hDstDS = this; + +/* -------------------------------------------------------------------- */ +/* Instantiate the warp operation. */ +/* -------------------------------------------------------------------- */ + poWarper = new GDALWarpOperation(); + + eErr = poWarper->Initialize( psWO ); + if( eErr != CE_None) + { +/* -------------------------------------------------------------------- */ +/* We are responsible for cleaning up the transformer outselves. */ +/* -------------------------------------------------------------------- */ + if( psWO->pTransformerArg != NULL ) + { + GDALDestroyTransformer( psWO->pTransformerArg ); + psWO->pTransformerArg = NULL; + } + + if( psWO->hSrcDS != NULL ) + { + GDALClose( psWO->hSrcDS ); + psWO->hSrcDS = NULL; + } + } + + GDALDestroyWarpOptions( psWO ); + if( eErr != CE_None ) + { + delete poWarper; + poWarper = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Deserialize SrcOvrLevel */ +/* -------------------------------------------------------------------- */ + const char* pszSrcOvrLevel = CPLGetXMLValue( psTree, "SrcOvrLevel", NULL ); + if( pszSrcOvrLevel != NULL ) + { + SetMetadataItem("SrcOvrLevel", pszSrcOvrLevel); + } + +/* -------------------------------------------------------------------- */ +/* Generate overviews, if appropriate. */ +/* -------------------------------------------------------------------- */ + + CreateImplicitOverviews(); + + /* OverviewList is historical, and quite inefficient, since it uses */ + /* the full resolution source dataset, so only build it afterwards */ + char **papszTokens = CSLTokenizeString( + CPLGetXMLValue( psTree, "OverviewList", "" )); + int iOverview; + + for( iOverview = 0; + papszTokens != NULL && papszTokens[iOverview] != NULL; + iOverview++ ) + { + int nOvFactor = atoi(papszTokens[iOverview]); + + if (nOvFactor > 0) + BuildOverviews( "NEAREST", 1, &nOvFactor, 0, NULL, NULL, NULL ); + else + CPLError(CE_Failure, CPLE_AppDefined, + "Bad value for overview factor : %s", papszTokens[iOverview]); + } + + CSLDestroy( papszTokens ); + + return eErr; +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *VRTWarpedDataset::SerializeToXML( const char *pszVRTPath ) + +{ + CPLXMLNode *psTree; + + psTree = VRTDataset::SerializeToXML( pszVRTPath ); + + if( psTree == NULL ) + return psTree; + +/* -------------------------------------------------------------------- */ +/* Set subclass. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLNode( + CPLCreateXMLNode( psTree, CXT_Attribute, "subClass" ), + CXT_Text, "VRTWarpedDataset" ); + +/* -------------------------------------------------------------------- */ +/* Serialize the block size. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( psTree, "BlockXSize", + CPLSPrintf( "%d", nBlockXSize ) ); + CPLCreateXMLElementAndValue( psTree, "BlockYSize", + CPLSPrintf( "%d", nBlockYSize ) ); + +/* -------------------------------------------------------------------- */ +/* Serialize the overview list (only for non implicit overviews) */ +/* -------------------------------------------------------------------- */ + if( nOverviewCount > 0 ) + { + char *pszOverviewList; + int iOverview; + + int nSrcDSOvrCount = 0; + if( poWarper != NULL && poWarper->GetOptions() != NULL && + poWarper->GetOptions()->hSrcDS != NULL && + GDALGetRasterCount(poWarper->GetOptions()->hSrcDS) > 0 ) + { + nSrcDSOvrCount = ((GDALDataset*)poWarper->GetOptions()->hSrcDS)-> + GetRasterBand(1)->GetOverviewCount(); + } + + if( nOverviewCount != nSrcDSOvrCount ) + { + pszOverviewList = (char *) CPLMalloc(nOverviewCount*8 + 10); + pszOverviewList[0] = '\0'; + for( iOverview = 0; iOverview < nOverviewCount; iOverview++ ) + { + int nOvFactor; + + nOvFactor = (int) + (0.5+GetRasterXSize() + / (double) papoOverviews[iOverview]->GetRasterXSize()); + + sprintf( pszOverviewList + strlen(pszOverviewList), + "%d ", nOvFactor ); + } + + CPLCreateXMLElementAndValue( psTree, "OverviewList", pszOverviewList ); + + CPLFree( pszOverviewList ); + } + } + +/* -------------------------------------------------------------------- */ +/* Serialize source overview level. */ +/* -------------------------------------------------------------------- */ + if( nSrcOvrLevel != -2 ) + { + if( nSrcOvrLevel < -2 ) + CPLCreateXMLElementAndValue( psTree, "SrcOvrLevel", CPLSPrintf("AUTO%d", nSrcOvrLevel+2) ); + else if( nSrcOvrLevel == -1 ) + CPLCreateXMLElementAndValue( psTree, "SrcOvrLevel", "NONE" ); + else + CPLCreateXMLElementAndValue( psTree, "SrcOvrLevel", CPLSPrintf("%d", nSrcOvrLevel) ); + } + +/* ==================================================================== */ +/* Serialize the warp options. */ +/* ==================================================================== */ + CPLXMLNode *psWOTree; + + if( poWarper != NULL ) + { +/* -------------------------------------------------------------------- */ +/* We reset the destination dataset name so it doesn't get */ +/* written out in the serialize warp options. */ +/* -------------------------------------------------------------------- */ + char *pszSavedName = CPLStrdup(GetDescription()); + SetDescription(""); + + psWOTree = GDALSerializeWarpOptions( poWarper->GetOptions() ); + CPLAddXMLChild( psTree, psWOTree ); + + SetDescription( pszSavedName ); + CPLFree( pszSavedName ); + +/* -------------------------------------------------------------------- */ +/* We need to consider making the source dataset relative to */ +/* the VRT file if possible. Adjust accordingly. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psSDS = CPLGetXMLNode( psWOTree, "SourceDataset" ); + int bRelativeToVRT = FALSE; + VSIStatBufL sStat; + + if( VSIStatExL( psSDS->psChild->pszValue, &sStat, + VSI_STAT_EXISTS_FLAG) == 0 ) + { + char *pszRelativePath; + + pszRelativePath = CPLStrdup( + CPLExtractRelativePath( pszVRTPath, psSDS->psChild->pszValue, + &bRelativeToVRT ) ); + + CPLFree( psSDS->psChild->pszValue ); + psSDS->psChild->pszValue = pszRelativePath; + } + + CPLCreateXMLNode( + CPLCreateXMLNode( psSDS, CXT_Attribute, "relativeToVRT" ), + CXT_Text, bRelativeToVRT ? "1" : "0" ); + } + + return psTree; +} + +/************************************************************************/ +/* GetBlockSize() */ +/************************************************************************/ + +void VRTWarpedDataset::GetBlockSize( int *pnBlockXSize, int *pnBlockYSize ) + +{ + assert( NULL != pnBlockXSize ); + assert( NULL != pnBlockYSize ); + + *pnBlockXSize = nBlockXSize; + *pnBlockYSize = nBlockYSize; +} + +/************************************************************************/ +/* ProcessBlock() */ +/* */ +/* Warp a single requested block, and then push each band of */ +/* the result into the block cache. */ +/************************************************************************/ + +CPLErr VRTWarpedDataset::ProcessBlock( int iBlockX, int iBlockY ) + +{ + if( poWarper == NULL ) + return CE_Failure; + + const GDALWarpOptions *psWO = poWarper->GetOptions(); + +/* -------------------------------------------------------------------- */ +/* Allocate block of memory large enough to hold all the bands */ +/* for this block. */ +/* -------------------------------------------------------------------- */ + int iBand; + GByte *pabyDstBuffer; + int nDstBufferSize; + int nWordSize = (GDALGetDataTypeSize(psWO->eWorkingDataType) / 8); + + // FIXME? : risk of overflow in multiplication if nBlockXSize or nBlockYSize are very large + nDstBufferSize = nBlockXSize * nBlockYSize * psWO->nBandCount * nWordSize; + + pabyDstBuffer = (GByte *) VSIMalloc(nDstBufferSize); + + if( pabyDstBuffer == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating %d byte buffer in VRTWarpedDataset::ProcessBlock()", + nDstBufferSize ); + return CE_Failure; + } + + memset( pabyDstBuffer, 0, nDstBufferSize ); + +/* -------------------------------------------------------------------- */ +/* Process INIT_DEST option to initialize the buffer prior to */ +/* warping into it. */ +/* NOTE:The following code is 99% similar in gdalwarpoperation.cpp and */ +/* vrtwarped.cpp. Be careful to keep it in sync ! */ +/* -------------------------------------------------------------------- */ + const char *pszInitDest = CSLFetchNameValue( psWO->papszWarpOptions, + "INIT_DEST" ); + + if( pszInitDest != NULL && !EQUAL(pszInitDest, "") ) + { + char **papszInitValues = + CSLTokenizeStringComplex( pszInitDest, ",", FALSE, FALSE ); + int nInitCount = CSLCount(papszInitValues); + + for( iBand = 0; iBand < psWO->nBandCount; iBand++ ) + { + double adfInitRealImag[2]; + GByte *pBandData; + int nBandSize = nBlockXSize * nBlockYSize * nWordSize; + const char *pszBandInit = papszInitValues[MIN(iBand,nInitCount-1)]; + + if( EQUAL(pszBandInit,"NO_DATA") + && psWO->padfDstNoDataReal != NULL ) + { + adfInitRealImag[0] = psWO->padfDstNoDataReal[iBand]; + adfInitRealImag[1] = psWO->padfDstNoDataImag[iBand]; + } + else + { + CPLStringToComplex( pszBandInit, + adfInitRealImag + 0, adfInitRealImag + 1); + } + + pBandData = ((GByte *) pabyDstBuffer) + iBand * nBandSize; + + if( psWO->eWorkingDataType == GDT_Byte ) + memset( pBandData, + MAX(0,MIN(255,(int)adfInitRealImag[0])), + nBandSize); + else if( !CPLIsNan(adfInitRealImag[0]) && adfInitRealImag[0] == 0.0 && + !CPLIsNan(adfInitRealImag[1]) && adfInitRealImag[1] == 0.0 ) + { + memset( pBandData, 0, nBandSize ); + } + else if( !CPLIsNan(adfInitRealImag[1]) && adfInitRealImag[1] == 0.0 ) + { + GDALCopyWords( &adfInitRealImag, GDT_Float64, 0, + pBandData,psWO->eWorkingDataType,nWordSize, + nBlockXSize * nBlockYSize ); + } + else + { + GDALCopyWords( &adfInitRealImag, GDT_CFloat64, 0, + pBandData,psWO->eWorkingDataType,nWordSize, + nBlockXSize * nBlockYSize ); + } + } + + CSLDestroy( papszInitValues ); + } + +/* -------------------------------------------------------------------- */ +/* Warp into this buffer. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + int nReqXSize = nBlockXSize; + if( iBlockX * nBlockXSize + nReqXSize > nRasterXSize ) + nReqXSize = nRasterXSize - iBlockX * nBlockXSize; + int nReqYSize = nBlockYSize; + if( iBlockY * nBlockYSize + nReqYSize > nRasterYSize ) + nReqYSize = nRasterYSize - iBlockY * nBlockYSize; + + eErr = + poWarper->WarpRegionToBuffer( + iBlockX * nBlockXSize, iBlockY * nBlockYSize, + nReqXSize, nReqYSize, + pabyDstBuffer, psWO->eWorkingDataType ); + + if( eErr != CE_None ) + { + VSIFree( pabyDstBuffer ); + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Copy out into cache blocks for each band. */ +/* -------------------------------------------------------------------- */ + for( iBand = 0; iBand < MIN(nBands, psWO->nBandCount); iBand++ ) + { + GDALRasterBand *poBand; + GDALRasterBlock *poBlock; + + poBand = GetRasterBand(iBand+1); + poBlock = poBand->GetLockedBlockRef( iBlockX, iBlockY, TRUE ); + + if( poBlock != NULL ) + { + if ( poBlock->GetDataRef() != NULL ) + { + if( nReqXSize == nBlockXSize && nReqYSize == nBlockYSize ) + { + GDALCopyWords( pabyDstBuffer + iBand*nBlockXSize*nBlockYSize*nWordSize, + psWO->eWorkingDataType, nWordSize, + poBlock->GetDataRef(), + poBlock->GetDataType(), + GDALGetDataTypeSize(poBlock->GetDataType())/8, + nBlockXSize * nBlockYSize ); + } + else + { + GByte* pabyBlock = (GByte*) poBlock->GetDataRef(); + int nDTSize = GDALGetDataTypeSize(poBlock->GetDataType())/8; + for(int iY=0;iYeWorkingDataType, nWordSize, + pabyBlock + iY * nBlockXSize * nDTSize, + poBlock->GetDataType(), + nDTSize, + nReqXSize ); + } + } + } + + poBlock->DropLock(); + } + } + + VSIFree( pabyDstBuffer ); + + return CE_None; +} + +/************************************************************************/ +/* AddBand() */ +/************************************************************************/ + +CPLErr VRTWarpedDataset::AddBand( GDALDataType eType, char **papszOptions ) + +{ + UNREFERENCED_PARAM( papszOptions ); + + SetBand( GetRasterCount() + 1, + new VRTWarpedRasterBand( this, GetRasterCount() + 1, eType ) ); + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VRTWarpedRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VRTWarpedRasterBand() */ +/************************************************************************/ + +VRTWarpedRasterBand::VRTWarpedRasterBand( GDALDataset *poDS, int nBand, + GDALDataType eType ) + +{ + Initialize( poDS->GetRasterXSize(), poDS->GetRasterYSize() ); + + this->poDS = poDS; + this->nBand = nBand; + this->eAccess = GA_Update; + + ((VRTWarpedDataset *) poDS)->GetBlockSize( &nBlockXSize, + &nBlockYSize ); + + if( eType != GDT_Unknown ) + this->eDataType = eType; +} + +/************************************************************************/ +/* ~VRTWarpedRasterBand() */ +/************************************************************************/ + +VRTWarpedRasterBand::~VRTWarpedRasterBand() + +{ + FlushCache(); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr VRTWarpedRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + CPLErr eErr; + VRTWarpedDataset *poWDS = (VRTWarpedDataset *) poDS; + GDALRasterBlock *poBlock; + + poBlock = GetLockedBlockRef( nBlockXOff, nBlockYOff, TRUE ); + if( poBlock == NULL ) + return CE_Failure; + + eErr = poWDS->ProcessBlock( nBlockXOff, nBlockYOff ); + + if( eErr == CE_None && pImage != poBlock->GetDataRef() ) + { + int nDataBytes; + nDataBytes = (GDALGetDataTypeSize(poBlock->GetDataType()) / 8) + * poBlock->GetXSize() * poBlock->GetYSize(); + memcpy( pImage, poBlock->GetDataRef(), nDataBytes ); + } + + poBlock->DropLock(); + + return eErr; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr VRTWarpedRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + CPLErr eErr; + VRTWarpedDataset *poWDS = (VRTWarpedDataset *) poDS; + + /* This is a bit tricky. In the case we are warping a VRTWarpedDataset */ + /* with a destination alpha band, IWriteBlock can be called on that alpha */ + /* band by GDALWarpDstAlphaMasker */ + /* We don't need to do anything since the data will be kept in the block */ + /* cache by VRTWarpedRasterBand::IReadBlock */ + if (poWDS->poWarper->GetOptions()->nDstAlphaBand == nBand) + { + eErr = CE_None; + } + else + { + /* Otherwise, call the superclass method, that will fail of course */ + eErr = VRTRasterBand::IWriteBlock(nBlockXOff, nBlockYOff, pImage); + } + + return eErr; +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr VRTWarpedRasterBand::XMLInit( CPLXMLNode * psTree, + const char *pszVRTPath ) + +{ + return VRTRasterBand::XMLInit( psTree, pszVRTPath ); +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *VRTWarpedRasterBand::SerializeToXML( const char *pszVRTPath ) + +{ + CPLXMLNode *psTree = VRTRasterBand::SerializeToXML( pszVRTPath ); + +/* -------------------------------------------------------------------- */ +/* Set subclass. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLNode( + CPLCreateXMLNode( psTree, CXT_Attribute, "subClass" ), + CXT_Text, "VRTWarpedRasterBand" ); + + return psTree; +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int VRTWarpedRasterBand::GetOverviewCount() + +{ + VRTWarpedDataset *poWDS = (VRTWarpedDataset *) poDS; + + poWDS->CreateImplicitOverviews(); + + return poWDS->nOverviewCount; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *VRTWarpedRasterBand::GetOverview( int iOverview ) + +{ + VRTWarpedDataset *poWDS = (VRTWarpedDataset *) poDS; + + if( iOverview < 0 || iOverview >= GetOverviewCount() ) + return NULL; + else + return poWDS->papoOverviews[iOverview]->GetRasterBand( nBand ); +} diff --git a/bazaar/plugin/gdal/frmts/wcs/GNUmakefile b/bazaar/plugin/gdal/frmts/wcs/GNUmakefile new file mode 100644 index 000000000..421e75e31 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wcs/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = wcsdataset.o httpdriver.o + +CPPFLAGS := $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(OBJ) $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/wcs/frmt_wcs.html b/bazaar/plugin/gdal/frmts/wcs/frmt_wcs.html new file mode 100644 index 000000000..600098cd1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wcs/frmt_wcs.html @@ -0,0 +1,82 @@ + + +WCS -- OGC Web Coverage Service + + + + +

    WCS -- OGC Web Coverage Service

    + +The optional GDAL WCS driver allows use of a coverage in a WCS server as a +raster dataset. GDAL acts as a client to the WCS server.

    + +Accessing a WCS server is accomplished by creating a local service description +xml file looking something like the following, with the coverage server url, +and the name of the coverage to access. It is important that there be no +spaces or other content before the <WCS_GDAL> element.

    + +

    +<WCS_GDAL>
    +  <ServiceURL>http://laits.gmu.edu/cgi-bin/NWGISS/NWGISS?</ServiceURL>
    +  <CoverageName>AUTUMN.hdf</CoverageName>
    +</WCS_GDAL>
    +
    + +When first opened, GDAL will fetch the coverage description, and a small test +image to establish details about the raster. This information will be cached +in the service description file to make future opens faster - no server access +should be required till imagery is read for future opens.

    + +The WCS driver should support WCS 1.0.0 and 1.1.0 servers, but WCS 0.7 servers +are not supported. Any return format that is a single file, and is in a format +supported by GDAL should work. The driver will prefer a format with "tiff" +in the name, otherwise +it will fallback to the first offered format. Coordinate systems are read +from the DescribeCoverage result, and are expected to be in the form of +EPSG:n in the <supportedCRSs> element.

    + +The service description file has the following additional elements as +immediate children of the WCS_GDAL element that may be optionally +set.

    + +

      +
    • PreferredFormat: the format to use for GetCoverage calls.

      +

    • BandCount: Number of bands in the dataset, normally captured from the sample request. +
    • BandType: The pixel data type to use. Normally established from the sample request. +
    • BlockXSize: The block width to use for block cached remote access. +
    • BlockYSize: The block height to use for block cached remote access. +
    • NoDataValue: The nodata value to use for all the bands (blank for none). Normally defaulted from CoverageOffering info. +
    • Timeout: The timeout to use for remote service requests. If not provided, the libcurl default is used. +
    • UserPwd: May be supplied with userid:password to pass a +userid and password to the remote server. +
    • HttpAuth: May be BASIC, NTLM or ANY to control the authentication scheme to be used. +
    • OverviewCount: The number of overviews to represent bands as having. Defaults to a number such that the top overview is fairly smaller (less than 1K x 1K or so). +
    • GetCoverageExtra: An additional set of keywords to add to GetCoverage requests in URL encoded form. eg. "&RESAMPLE=BILINEAR&Band=1" +
    • DescribeCoverageExtra: An additional set of keywords to add to DescribeCoverage requests in URL encoded form. eg. "&CustNo=775" +
    • Version: Set a specific WCS version to use. Currently defaults to 1.0.0 and 1.1.0 is also supported. +
    • FieldName: Name of the field being accessed. Used only with WCS 1.1.0+. Defaults to the first field in the DescribeCoverage result. +
    • DefaultTime: A timePosition to use by default when accessing +coverages with a time dimension. Populated with the last offered time +position by default. +
    + + +

    Time

    + +Starting with GDAL 1.9.0, this driver includes experimental support for time based WCS 1.0.0 servers. +On initial access the last offered time position will be identified as the +DefaultTime. Each time position available for the coverage will be treated +as a subdataset.

    + +Note that time based subdatasets are not supported when the service description +is the filename. Currently time support is not available for versions other +than WCS 1.0.0.

    + +See Also:

    + +

    + + + diff --git a/bazaar/plugin/gdal/frmts/wcs/httpdriver.cpp b/bazaar/plugin/gdal/frmts/wcs/httpdriver.cpp new file mode 100644 index 000000000..640bc663b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wcs/httpdriver.cpp @@ -0,0 +1,218 @@ +/****************************************************************************** + * $Id: httpdriver.cpp 29102 2015-05-01 22:26:04Z rouault $ + * + * Project: WCS Client Driver + * Purpose: Implementation of an HTTP fetching driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "cpl_http.h" +#include "cpl_atomic_ops.h" + +CPL_CVSID("$Id: httpdriver.cpp 29102 2015-05-01 22:26:04Z rouault $"); + + +/************************************************************************/ +/* HTTPFetchContentDispositionFilename() */ +/************************************************************************/ + +static const char* HTTPFetchContentDispositionFilename(char** papszHeaders) +{ + char** papszIter = papszHeaders; + while(papszIter && *papszIter) + { + /* For multipart, we have in raw format, but without end-of-line characters */ + if (strncmp(*papszIter, "Content-Disposition: attachment; filename=", 42) == 0) + { + return *papszIter + 42; + } + /* For single part, the headers are in KEY=VAL format, but with e-o-l ... */ + else if (strncmp(*papszIter, "Content-Disposition=attachment; filename=", 41) == 0) + { + char* pszVal = (char*)(*papszIter + 41); + char* pszEOL = strchr(pszVal, '\r'); + if (pszEOL) *pszEOL = 0; + pszEOL = strchr(pszVal, '\n'); + if (pszEOL) *pszEOL = 0; + return pszVal; + } + papszIter ++; + } + return NULL; +} + +/************************************************************************/ +/* HTTPOpen() */ +/************************************************************************/ + +static GDALDataset *HTTPOpen( GDALOpenInfo * poOpenInfo ) + +{ + static volatile int nCounter = 0; + + if( poOpenInfo->nHeaderBytes != 0 ) + return NULL; + + if( !EQUALN(poOpenInfo->pszFilename,"http:",5) + && !EQUALN(poOpenInfo->pszFilename,"https:",6) + && !EQUALN(poOpenInfo->pszFilename,"ftp:",4) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Fetch the result. */ +/* -------------------------------------------------------------------- */ + CPLErrorReset(); + + CPLHTTPResult *psResult = CPLHTTPFetch( poOpenInfo->pszFilename, NULL ); + +/* -------------------------------------------------------------------- */ +/* Try to handle errors. */ +/* -------------------------------------------------------------------- */ + if( psResult == NULL || psResult->nDataLen == 0 + || CPLGetLastErrorNo() != 0 ) + { + CPLHTTPDestroyResult( psResult ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a memory file from the result. */ +/* -------------------------------------------------------------------- */ + CPLString osResultFilename; + + int nNewCounter = CPLAtomicInc(&nCounter); + + const char* pszFilename = HTTPFetchContentDispositionFilename(psResult->papszHeaders); + if (pszFilename == NULL) + { + pszFilename = CPLGetFilename(poOpenInfo->pszFilename); + /* If we have special characters, let's default to a fixed name */ + if (strchr(pszFilename, '?') || strchr(pszFilename, '&')) + pszFilename = "file.dat"; + } + + osResultFilename.Printf( "/vsimem/http_%d/%s", + nNewCounter, pszFilename ); + + VSILFILE *fp = VSIFileFromMemBuffer( osResultFilename, + psResult->pabyData, + psResult->nDataLen, + TRUE ); + + if( fp == NULL ) + return NULL; + + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Steal the memory buffer from HTTP result before destroying */ +/* it. */ +/* -------------------------------------------------------------------- */ + psResult->pabyData = NULL; + psResult->nDataLen = psResult->nDataAlloc = 0; + + CPLHTTPDestroyResult( psResult ); + +/* -------------------------------------------------------------------- */ +/* Try opening this result as a gdaldataset. */ +/* -------------------------------------------------------------------- */ + /* suppress errors as not all drivers support /vsimem */ + CPLPushErrorHandler( CPLQuietErrorHandler ); + GDALDataset *poDS = (GDALDataset *) + GDALOpenEx( osResultFilename, poOpenInfo->nOpenFlags, NULL, + poOpenInfo->papszOpenOptions, NULL); + CPLPopErrorHandler(); + +/* -------------------------------------------------------------------- */ +/* If opening it in memory didn't work, perhaps we need to */ +/* write to a temp file on disk? */ +/* -------------------------------------------------------------------- */ + if( poDS == NULL ) + { + CPLString osTempFilename; + +#ifdef WIN32 + const char* pszPath = CPLGetPath(CPLGenerateTempFilename(NULL)); +#else + const char* pszPath = "/tmp"; +#endif + osTempFilename = CPLFormFilename(pszPath, CPLGetFilename(osResultFilename), NULL ); + if( CPLCopyFile( osTempFilename, osResultFilename ) != 0 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create temporary file:%s", + osTempFilename.c_str() ); + } + else + { + poDS = (GDALDataset *) + GDALOpenEx( osTempFilename, poOpenInfo->nOpenFlags, NULL, + poOpenInfo->papszOpenOptions, NULL ); + if( VSIUnlink( osTempFilename ) != 0 && poDS != NULL ) + poDS->MarkSuppressOnClose(); /* VSIUnlink() may not work on windows */ + if( poDS && strcmp(poDS->GetDescription(), osTempFilename) == 0 ) + poDS->SetDescription(poOpenInfo->pszFilename); + + } + } + else if( strcmp(poDS->GetDescription(), osResultFilename) == 0 ) + poDS->SetDescription(poOpenInfo->pszFilename); + +/* -------------------------------------------------------------------- */ +/* Release our hold on the vsi memory file, though if it is */ +/* held open by a dataset it will continue to exist till that */ +/* lets it go. */ +/* -------------------------------------------------------------------- */ + VSIUnlink( osResultFilename ); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_HTTP() */ +/************************************************************************/ + +void GDALRegister_HTTP() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "HTTP" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "HTTP" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "HTTP Fetching Wrapper" ); + + poDriver->pfnOpen = HTTPOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/wcs/makefile.vc b/bazaar/plugin/gdal/frmts/wcs/makefile.vc new file mode 100644 index 000000000..c3bfe2ea9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wcs/makefile.vc @@ -0,0 +1,16 @@ + +OBJ = wcsdataset.obj httpdriver.obj + +EXTRAFLAGS = -DHAVE_CURL $(CURL_CFLAGS) $(CURL_INC) + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + -del *.dll diff --git a/bazaar/plugin/gdal/frmts/wcs/wcsdataset.cpp b/bazaar/plugin/gdal/frmts/wcs/wcsdataset.cpp new file mode 100644 index 000000000..0e2d0c90f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wcs/wcsdataset.cpp @@ -0,0 +1,2431 @@ +/****************************************************************************** + * $Id: wcsdataset.cpp 28218 2014-12-25 18:09:13Z goatbar $ + * + * Project: WCS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WCS. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2006, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "cpl_minixml.h" +#include "cpl_http.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: wcsdataset.cpp 28218 2014-12-25 18:09:13Z goatbar $"); + +/************************************************************************/ +/* ==================================================================== */ +/* WCSDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class WCSRasterBand; + +class CPL_DLL WCSDataset : public GDALPamDataset +{ + friend class WCSRasterBand; + + int bServiceDirty; + CPLXMLNode *psService; + + char *apszCoverageOfferingMD[2]; + + char **papszSDSModifiers; + + int nVersion; // eg 100 for 1.0.0, 110 for 1.1.0 + + CPLString osCRS; + + char *pszProjection; + double adfGeoTransform[6]; + + CPLString osBandIdentifier; + + CPLString osDefaultTime; + std::vector aosTimePositions; + + int TestUseBlockIO( int, int, int, int, int, int ); + CPLErr DirectRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + CPLErr GetCoverage( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + int nBandCount, int *panBandList, + CPLHTTPResult **ppsResult ); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + int DescribeCoverage(); + int ExtractGridInfo100(); + int ExtractGridInfo(); + int EstablishRasterDetails(); + + int ProcessError( CPLHTTPResult *psResult ); + GDALDataset *GDALOpenResult( CPLHTTPResult *psResult ); + void FlushMemoryResult(); + CPLString osResultFilename; + GByte *pabySavedDataBuffer; + + char **papszHttpOptions; + + int nMaxCols; + int nMaxRows; + + public: + WCSDataset(); + ~WCSDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual const char *GetProjectionRef(void); + virtual char **GetFileList(void); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char *pszDomain ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* WCSRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class WCSRasterBand : public GDALPamRasterBand +{ + friend class WCSDataset; + + int iOverview; + int nResFactor; + + WCSDataset *poODS; + + int nOverviewCount; + WCSRasterBand **papoOverviews; + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); + + public: + + WCSRasterBand( WCSDataset *, int nBand, int iOverview ); + ~WCSRasterBand(); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); + + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); + + virtual CPLErr IReadBlock( int, int, void * ); +}; + +/************************************************************************/ +/* WCSRasterBand() */ +/************************************************************************/ + +WCSRasterBand::WCSRasterBand( WCSDataset *poDS, int nBand, int iOverview ) + +{ + poODS = poDS; + this->nBand = nBand; + + eDataType = GDALGetDataTypeByName( + CPLGetXMLValue( poDS->psService, "BandType", "Byte" ) ); + +/* -------------------------------------------------------------------- */ +/* Establish resolution reduction for this overview level. */ +/* -------------------------------------------------------------------- */ + this->iOverview = iOverview; + nResFactor = 1 << (iOverview+1); // iOverview == -1 is base layer + +/* -------------------------------------------------------------------- */ +/* Establish block size. */ +/* -------------------------------------------------------------------- */ + nRasterXSize = poDS->GetRasterXSize() / nResFactor; + nRasterYSize = poDS->GetRasterYSize() / nResFactor; + + nBlockXSize = atoi(CPLGetXMLValue( poDS->psService, "BlockXSize", "0" ) ); + nBlockYSize = atoi(CPLGetXMLValue( poDS->psService, "BlockYSize", "0" ) ); + + if( nBlockXSize < 1 ) + { + if( nRasterXSize > 1800 ) + nBlockXSize = 1024; + else + nBlockXSize = nRasterXSize; + } + + if( nBlockYSize < 1 ) + { + if( nRasterYSize > 900 ) + nBlockYSize = 512; + else + nBlockYSize = nRasterYSize; + } + +/* -------------------------------------------------------------------- */ +/* If this is the base layer, create the overview layers. */ +/* -------------------------------------------------------------------- */ + if( iOverview == -1 ) + { + int i; + + nOverviewCount = atoi(CPLGetXMLValue(poODS->psService,"OverviewCount", + "-1")); + if( nOverviewCount < 0 ) + { + for( nOverviewCount = 0; + (MAX(nRasterXSize,nRasterYSize) / (1 << nOverviewCount)) > 900; + nOverviewCount++ ) {} + } + else if( nOverviewCount > 30 ) + { + /* There's no reason to have more than 30 overviews, because */ + /* 2^(30+1) overflows a int32 */ + nOverviewCount = 30; + } + + papoOverviews = (WCSRasterBand **) + CPLCalloc( nOverviewCount, sizeof(void*) ); + + for( i = 0; i < nOverviewCount; i++ ) + papoOverviews[i] = new WCSRasterBand( poODS, nBand, i ); + } + else + { + nOverviewCount = 0; + papoOverviews = NULL; + } +} + +/************************************************************************/ +/* ~WCSRasterBand() */ +/************************************************************************/ + +WCSRasterBand::~WCSRasterBand() + +{ + FlushCache(); + + if( nOverviewCount > 0 ) + { + int i; + + for( i = 0; i < nOverviewCount; i++ ) + delete papoOverviews[i]; + + CPLFree( papoOverviews ); + } +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr WCSRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, + void * pImage ) + +{ + CPLErr eErr; + CPLHTTPResult *psResult = NULL; + + eErr = poODS->GetCoverage( nBlockXOff * nBlockXSize * nResFactor, + nBlockYOff * nBlockYSize * nResFactor, + nBlockXSize * nResFactor, + nBlockYSize * nResFactor, + nBlockXSize, nBlockYSize, + 1, &nBand, &psResult ); + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Try and open result as a dataseat. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poTileDS = poODS->GDALOpenResult( psResult ); + + if( poTileDS == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Verify configuration. */ +/* -------------------------------------------------------------------- */ + if( poTileDS->GetRasterXSize() != nBlockXSize + || poTileDS->GetRasterYSize() != nBlockYSize ) + { + CPLDebug( "WCS", "Got size=%dx%d instead of %dx%d.", + poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(), + nBlockXSize, nBlockYSize ); + + CPLError( CE_Failure, CPLE_AppDefined, + "Returned tile does not match expected configuration.\n" + "Got %dx%d instead of %dx%d.", + poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(), + nBlockXSize, nBlockYSize ); + delete poTileDS; + return CE_Failure; + } + + if( (strlen(poODS->osBandIdentifier) && poTileDS->GetRasterCount() != 1) + || (!strlen(poODS->osBandIdentifier) + && poTileDS->GetRasterCount() != poODS->GetRasterCount()) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Returned tile does not match expected band configuration."); + delete poTileDS; + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Process all bands of memory result, copying into pBuffer, or */ +/* pushing into cache for other bands. */ +/* -------------------------------------------------------------------- */ + int iBand; + eErr = CE_None; + + for( iBand = 0; + iBand < poTileDS->GetRasterCount() && eErr == CE_None; + iBand++ ) + { + GDALRasterBand *poTileBand = poTileDS->GetRasterBand( iBand+1 ); + + if( iBand+1 == GetBand() || strlen(poODS->osBandIdentifier) ) + { + eErr = poTileBand->RasterIO( GF_Read, + 0, 0, nBlockXSize, nBlockYSize, + pImage, nBlockXSize, nBlockYSize, + eDataType, 0, 0, NULL ); + } + else + { + GDALRasterBand *poTargBand = poODS->GetRasterBand( iBand+1 ); + + if( iOverview != -1 ) + poTargBand = poTargBand->GetOverview( iOverview ); + + GDALRasterBlock *poBlock = poTargBand->GetLockedBlockRef( + nBlockXOff, nBlockYOff, TRUE ); + + if( poBlock != NULL ) + { + eErr = poTileBand->RasterIO( GF_Read, + 0, 0, nBlockXSize, nBlockYSize, + poBlock->GetDataRef(), + nBlockXSize, nBlockYSize, + eDataType, 0, 0, NULL ); + poBlock->DropLock(); + } + else + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + delete poTileDS; + + poODS->FlushMemoryResult(); + + return eErr; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr WCSRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ + if( (poODS->nMaxCols > 0 && poODS->nMaxCols < nBufXSize) + || (poODS->nMaxRows > 0 && poODS->nMaxRows < nBufYSize) ) + return CE_Failure; + + if( poODS->TestUseBlockIO( nXOff, nYOff, nXSize, nYSize, + nBufXSize,nBufYSize ) ) + return GDALPamRasterBand::IRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + else + return poODS->DirectRasterIO( + eRWFlag, + nXOff * nResFactor, nYOff * nResFactor, + nXSize * nResFactor, nYSize * nResFactor, + pData, nBufXSize, nBufYSize, eBufType, + 1, &nBand, nPixelSpace, nLineSpace, 0, psExtraArg ); +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double WCSRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + const char *pszSV = CPLGetXMLValue( poODS->psService, "NoDataValue", NULL); + + if( pszSV == NULL ) + return GDALPamRasterBand::GetNoDataValue( pbSuccess ); + else + { + if( pbSuccess ) + *pbSuccess = TRUE; + return CPLAtof(pszSV); + } +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int WCSRasterBand::GetOverviewCount() + +{ + return nOverviewCount; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *WCSRasterBand::GetOverview( int iOverview ) + +{ + if( iOverview < 0 || iOverview >= nOverviewCount ) + return NULL; + else + return papoOverviews[iOverview]; +} + +/************************************************************************/ +/* ==================================================================== */ +/* WCSDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* WCSDataset() */ +/************************************************************************/ + +WCSDataset::WCSDataset() + +{ + psService = NULL; + bServiceDirty = FALSE; + pszProjection = NULL; + + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + + pabySavedDataBuffer = NULL; + papszHttpOptions = NULL; + + nMaxCols = -1; + nMaxRows = -1; + + apszCoverageOfferingMD[0] = NULL; + apszCoverageOfferingMD[1] = NULL; + + papszSDSModifiers = NULL; +} + +/************************************************************************/ +/* ~WCSDataset() */ +/************************************************************************/ + +WCSDataset::~WCSDataset() + +{ + // perhaps this should be moved into a FlushCache() method. + if( bServiceDirty && !EQUALN(GetDescription(),"",10) ) + { + CPLSerializeXMLTreeToFile( psService, GetDescription() ); + bServiceDirty = FALSE; + } + + CPLDestroyXMLNode( psService ); + + CPLFree( pszProjection ); + pszProjection = NULL; + + CSLDestroy( papszHttpOptions ); + CSLDestroy( papszSDSModifiers ); + + CPLFree( apszCoverageOfferingMD[0] ); + + FlushMemoryResult(); +} + +/************************************************************************/ +/* TestUseBlockIO() */ +/* */ +/* Check whether we should use blocked IO (true) or direct io */ +/* (FALSE) for a given request configuration and environment. */ +/************************************************************************/ + +int WCSDataset::TestUseBlockIO( CPL_UNUSED int nXOff, + CPL_UNUSED int nYOff, + int nXSize, + int nYSize, + int nBufXSize, + int nBufYSize ) +{ + int bUseBlockedIO = bForceCachedIO; + + if( nYSize == 1 || nXSize * ((double) nYSize) < 100.0 ) + bUseBlockedIO = TRUE; + + if( nBufYSize == 1 || nBufXSize * ((double) nBufYSize) < 100.0 ) + bUseBlockedIO = TRUE; + + if( bUseBlockedIO + && CSLTestBoolean( CPLGetConfigOption( "GDAL_ONE_BIG_READ", "NO") ) ) + bUseBlockedIO = FALSE; + + return bUseBlockedIO; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr WCSDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ + if( (nMaxCols > 0 && nMaxCols < nBufXSize) + || (nMaxRows > 0 && nMaxRows < nBufYSize) ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* We need various criteria to skip out to block based methods. */ +/* -------------------------------------------------------------------- */ + if( TestUseBlockIO( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize ) ) + return GDALPamDataset::IRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); + else + return DirectRasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg ); +} + +/************************************************************************/ +/* DirectRasterIO() */ +/* */ +/* Make exactly one request to the server for this data. */ +/************************************************************************/ + +CPLErr +WCSDataset::DirectRasterIO( CPL_UNUSED GDALRWFlag eRWFlag, + int nXOff, + int nYOff, + int nXSize, + int nYSize, + void * pData, + int nBufXSize, + int nBufYSize, + GDALDataType eBufType, + int nBandCount, + int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + CPL_UNUSED GDALRasterIOExtraArg* psExtraArg) +{ + CPLDebug( "WCS", "DirectRasterIO(%d,%d,%d,%d) -> (%d,%d) (%d bands)\n", + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, nBandCount ); + +/* -------------------------------------------------------------------- */ +/* Get the coverage. */ +/* -------------------------------------------------------------------- */ + CPLHTTPResult *psResult = NULL; + CPLErr eErr = + GetCoverage( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, + nBandCount, panBandMap, &psResult ); + + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Try and open result as a dataseat. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poTileDS = GDALOpenResult( psResult ); + + if( poTileDS == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Verify configuration. */ +/* -------------------------------------------------------------------- */ + if( poTileDS->GetRasterXSize() != nBufXSize + || poTileDS->GetRasterYSize() != nBufYSize ) + { + CPLDebug( "WCS", "Got size=%dx%d instead of %dx%d.", + poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(), + nBufXSize, nBufYSize ); + + CPLError( CE_Failure, CPLE_AppDefined, + "Returned tile does not match expected configuration.\n" + "Got %dx%d instead of %dx%d.", + poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(), + nBufXSize, nBufYSize ); + delete poTileDS; + return CE_Failure; + } + + if( (strlen(osBandIdentifier) && poTileDS->GetRasterCount() != nBandCount) + || (!strlen(osBandIdentifier) && poTileDS->GetRasterCount() != + GetRasterCount() ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Returned tile does not match expected band count." ); + delete poTileDS; + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Pull requested bands from the downloaded dataset. */ +/* -------------------------------------------------------------------- */ + int iBand; + + eErr = CE_None; + + for( iBand = 0; + iBand < nBandCount && eErr == CE_None; + iBand++ ) + { + GDALRasterBand *poTileBand; + + if( strlen(osBandIdentifier) ) + poTileBand = poTileDS->GetRasterBand( iBand + 1 ); + else + poTileBand = poTileDS->GetRasterBand( panBandMap[iBand] ); + + eErr = poTileBand->RasterIO( GF_Read, + 0, 0, nBufXSize, nBufYSize, + ((GByte *) pData) + + iBand * nBandSpace, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + delete poTileDS; + + FlushMemoryResult(); + + return eErr; +} + +/************************************************************************/ +/* GetCoverage() */ +/* */ +/* Issue the appropriate version of request for a given window, */ +/* buffer size and band list. */ +/************************************************************************/ + +CPLErr WCSDataset::GetCoverage( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + int nBandCount, int *panBandList, + CPLHTTPResult **ppsResult ) + +{ +/* -------------------------------------------------------------------- */ +/* Figure out the georeferenced extents. */ +/* -------------------------------------------------------------------- */ + double dfMinX, dfMaxX, dfMinY, dfMaxY; + + // WCS 1.0 extents are the outer edges of outer pixels. + dfMinX = adfGeoTransform[0] + + (nXOff) * adfGeoTransform[1]; + dfMaxX = adfGeoTransform[0] + + (nXOff + nXSize) * adfGeoTransform[1]; + dfMaxY = adfGeoTransform[3] + + (nYOff) * adfGeoTransform[5]; + dfMinY = adfGeoTransform[3] + + (nYOff + nYSize) * adfGeoTransform[5]; + +/* -------------------------------------------------------------------- */ +/* Build band list if we have the band identifier. */ +/* -------------------------------------------------------------------- */ + CPLString osBandList; + int bSelectingBands = FALSE; + + if( strlen(osBandIdentifier) && nBandCount > 0 ) + { + int iBand; + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + if( iBand > 0 ) + osBandList += ","; + osBandList += CPLString().Printf( "%d", panBandList[iBand] ); + } + + bSelectingBands = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* URL encode strings that could have questionable characters. */ +/* -------------------------------------------------------------------- */ + CPLString osCoverage, osFormat; + char *pszEncoded; + + osCoverage = CPLGetXMLValue( psService, "CoverageName", "" ); + + pszEncoded = CPLEscapeString( osCoverage, -1, CPLES_URL ); + osCoverage = pszEncoded; + CPLFree( pszEncoded ); + + osFormat = CPLGetXMLValue( psService, "PreferredFormat", "" ); + + pszEncoded = CPLEscapeString( osFormat, -1, CPLES_URL ); + osFormat = pszEncoded; + CPLFree( pszEncoded ); + +/* -------------------------------------------------------------------- */ +/* Do we have a time we want to use? */ +/* -------------------------------------------------------------------- */ + CPLString osTime; + + osTime = CSLFetchNameValueDef( papszSDSModifiers, "time", osDefaultTime ); + +/* -------------------------------------------------------------------- */ +/* Construct a "simple" GetCoverage request (WCS 1.0). */ +/* -------------------------------------------------------------------- */ + CPLString osRequest; + + if( nVersion == 100 ) + { + osRequest.Printf( + "%sSERVICE=WCS&VERSION=1.0.0&REQUEST=GetCoverage&COVERAGE=%s" + "&FORMAT=%s&BBOX=%.15g,%.15g,%.15g,%.15g&WIDTH=%d&HEIGHT=%d&CRS=%s%s", + CPLGetXMLValue( psService, "ServiceURL", "" ), + osCoverage.c_str(), + osFormat.c_str(), + dfMinX, dfMinY, dfMaxX, dfMaxY, + nBufXSize, nBufYSize, + osCRS.c_str(), + CPLGetXMLValue( psService, "GetCoverageExtra", "" ) ); + + if( CPLGetXMLValue( psService, "Resample", NULL ) ) + { + osRequest += "&INTERPOLATION="; + osRequest += CPLGetXMLValue( psService, "Resample", "" ); + } + + if( osTime != "" ) + { + osRequest += "&time="; + osRequest += osTime; + } + + if( bSelectingBands ) + { + osRequest += CPLString().Printf( "&%s=%s", + osBandIdentifier.c_str(), + osBandList.c_str() ); + } + } + +/* -------------------------------------------------------------------- */ +/* Construct a "simple" GetCoverage request (WCS 1.1+). */ +/* -------------------------------------------------------------------- */ + else + { + CPLString osRangeSubset; + + osRangeSubset.Printf("&RangeSubset=%s", + CPLGetXMLValue(psService,"FieldName","")); + + if( CPLGetXMLValue( psService, "Resample", NULL ) ) + { + osRangeSubset += ":"; + osRangeSubset += CPLGetXMLValue( psService, "Resample", ""); + } + + if( bSelectingBands ) + { + osRangeSubset += + CPLString().Printf( "[%s[%s]]", + osBandIdentifier.c_str(), + osBandList.c_str() ); + } + + // WCS 1.1 extents are centers of outer pixels. + dfMaxX -= adfGeoTransform[1] * 0.5; + dfMinX += adfGeoTransform[1] * 0.5; + dfMinY -= adfGeoTransform[5] * 0.5; + dfMaxY += adfGeoTransform[5] * 0.5; + + // Carefully adjust bounds for pixel centered values at new + // sampling density. + + double dfXStep = adfGeoTransform[1]; + double dfYStep = adfGeoTransform[5]; + + if( nBufXSize != nXSize || nBufYSize != nYSize ) + { + dfXStep = (nXSize/(double)nBufXSize) * adfGeoTransform[1]; + dfYStep = (nYSize/(double)nBufYSize) * adfGeoTransform[5]; + + dfMinX = nXOff * adfGeoTransform[1] + adfGeoTransform[0] + + dfXStep * 0.5; + dfMaxX = dfMinX + (nBufXSize - 1) * dfXStep; + + dfMaxY = nYOff * adfGeoTransform[5] + adfGeoTransform[3] + + dfYStep * 0.5; + dfMinY = dfMaxY + (nBufYSize - 1) * dfYStep; + } + + osRequest.Printf( + "%sSERVICE=WCS&VERSION=%s&REQUEST=GetCoverage&IDENTIFIER=%s" + "&FORMAT=%s&BOUNDINGBOX=%.15g,%.15g,%.15g,%.15g,%s%s%s", + CPLGetXMLValue( psService, "ServiceURL", "" ), + CPLGetXMLValue( psService, "Version", "" ), + osCoverage.c_str(), + osFormat.c_str(), + dfMinX, dfMinY, dfMaxX, dfMaxY, + osCRS.c_str(), + osRangeSubset.c_str(), + CPLGetXMLValue( psService, "GetCoverageExtra", "" ) ); + + if( nBufXSize != nXSize || nBufYSize != nYSize ) + { + osRequest += CPLString().Printf( + "&GridBaseCRS=%s" + "&GridCS=%s" + "&GridType=urn:ogc:def:method:WCS:1.1:2dGridIn2dCrs" + "&GridOrigin=%.15g,%.15g" + "&GridOffsets=%.15g,%.15g", + osCRS.c_str(), + osCRS.c_str(), + dfMinX, dfMaxY, + dfXStep, dfYStep ); + } + } + +/* -------------------------------------------------------------------- */ +/* Fetch the result. */ +/* -------------------------------------------------------------------- */ + CPLErrorReset(); + + *ppsResult = CPLHTTPFetch( osRequest, papszHttpOptions ); + + if( ProcessError( *ppsResult ) ) + return CE_Failure; + else + return CE_None; +} + +/************************************************************************/ +/* DescribeCoverage() */ +/* */ +/* Fetch the DescribeCoverage result and attach it to the */ +/* service description. */ +/************************************************************************/ + +int WCSDataset::DescribeCoverage() + +{ + CPLString osRequest; + +/* -------------------------------------------------------------------- */ +/* Fetch coverage description for this coverage. */ +/* -------------------------------------------------------------------- */ + if( nVersion == 100 ) + osRequest.Printf( + "%sSERVICE=WCS&REQUEST=DescribeCoverage&VERSION=%s&COVERAGE=%s%s", + CPLGetXMLValue( psService, "ServiceURL", "" ), + CPLGetXMLValue( psService, "Version", "1.0.0" ), + CPLGetXMLValue( psService, "CoverageName", "" ), + CPLGetXMLValue( psService, "DescribeCoverageExtra", "" ) ); + else + osRequest.Printf( + "%sSERVICE=WCS&REQUEST=DescribeCoverage&VERSION=%s&IDENTIFIERS=%s%s&FORMAT=text/xml", + CPLGetXMLValue( psService, "ServiceURL", "" ), + CPLGetXMLValue( psService, "Version", "1.0.0" ), + CPLGetXMLValue( psService, "CoverageName", "" ), + CPLGetXMLValue( psService, "DescribeCoverageExtra", "" ) ); + + CPLErrorReset(); + + CPLHTTPResult *psResult = CPLHTTPFetch( osRequest, papszHttpOptions ); + + if( ProcessError( psResult ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Parse result. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psDC = CPLParseXMLString( (const char *) psResult->pabyData ); + CPLHTTPDestroyResult( psResult ); + + if( psDC == NULL ) + return FALSE; + + CPLStripXMLNamespace( psDC, NULL, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Did we get a CoverageOffering? */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psCO; + + if( nVersion == 100 ) + psCO = CPLGetXMLNode( psDC, "=CoverageDescription.CoverageOffering" ); + else + psCO =CPLGetXMLNode( psDC,"=CoverageDescriptions.CoverageDescription"); + + if( !psCO ) + { + CPLDestroyXMLNode( psDC ); + + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to fetch a back %s.", + osRequest.c_str() ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Duplicate the coverage offering, and insert into */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psNext = psCO->psNext; + psCO->psNext = NULL; + + CPLAddXMLChild( psService, CPLCloneXMLTree( psCO ) ); + bServiceDirty = TRUE; + + psCO->psNext = psNext; + + CPLDestroyXMLNode( psDC ); + return TRUE; +} + +/************************************************************************/ +/* ExtractGridInfo100() */ +/* */ +/* Collect info about grid from describe coverage for WCS 1.0.0 */ +/* and above. */ +/************************************************************************/ + +int WCSDataset::ExtractGridInfo100() + +{ + CPLXMLNode * psCO = CPLGetXMLNode( psService, "CoverageOffering" ); + + if( psCO == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* We need to strip off name spaces so it is easier to */ +/* searchfor plain gml names. */ +/* -------------------------------------------------------------------- */ + CPLStripXMLNamespace( psCO, NULL, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Verify we have a Rectified Grid. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRG = + CPLGetXMLNode( psCO, "domainSet.spatialDomain.RectifiedGrid" ); + + if( psRG == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find RectifiedGrid in CoverageOffering,\n" + "unable to process WCS Coverage." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Extract size, geotransform and coordinate system. */ +/* -------------------------------------------------------------------- */ + if( GDALParseGMLCoverage( psRG, &nRasterXSize, &nRasterYSize, + adfGeoTransform, &pszProjection ) != CE_None ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Fallback to nativeCRSs declaration. */ +/* -------------------------------------------------------------------- */ + const char *pszNativeCRSs = + CPLGetXMLValue( psCO, "supportedCRSs.nativeCRSs", NULL ); + + if( pszNativeCRSs == NULL ) + pszNativeCRSs = + CPLGetXMLValue( psCO, "supportedCRSs.requestResponseCRSs", NULL ); + + if( pszNativeCRSs == NULL ) + pszNativeCRSs = + CPLGetXMLValue( psCO, "supportedCRSs.requestCRSs", NULL ); + + if( pszNativeCRSs == NULL ) + pszNativeCRSs = + CPLGetXMLValue( psCO, "supportedCRSs.responseCRSs", NULL ); + + if( pszNativeCRSs != NULL + && (pszProjection == NULL || strlen(pszProjection) == 0) ) + { + OGRSpatialReference oSRS; + + if( oSRS.SetFromUserInput( pszNativeCRSs ) == OGRERR_NONE ) + { + CPLFree( pszProjection ); + oSRS.exportToWkt( &pszProjection ); + } + else + CPLDebug( "WCS", + " element contents not parsable:\n%s", + pszNativeCRSs ); + } + + // We should try to use the services name for the CRS if possible. + if( pszNativeCRSs != NULL + && ( EQUALN(pszNativeCRSs,"EPSG:",5) + || EQUALN(pszNativeCRSs,"AUTO:",5) + || EQUALN(pszNativeCRSs,"Image ",6) + || EQUALN(pszNativeCRSs,"Engineering ",12) + || EQUALN(pszNativeCRSs,"OGC:",4) ) ) + { + osCRS = pszNativeCRSs; + + size_t nDivider = osCRS.find( " " ); + + if( nDivider != std::string::npos ) + osCRS.resize( nDivider-1 ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a coordinate system override? */ +/* -------------------------------------------------------------------- */ + const char *pszProjOverride = CPLGetXMLValue( psService, "SRS", NULL ); + + if( pszProjOverride ) + { + OGRSpatialReference oSRS; + + if( oSRS.SetFromUserInput( pszProjOverride ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + " element contents not parsable:\n%s", + pszProjOverride ); + return FALSE; + } + + CPLFree( pszProjection ); + oSRS.exportToWkt( &pszProjection ); + + if( EQUALN(pszProjOverride,"EPSG:",5) + || EQUALN(pszProjOverride,"AUTO:",5) + || EQUALN(pszProjOverride,"OGC:",4) + || EQUALN(pszProjOverride,"Image ",6) + || EQUALN(pszProjOverride,"Engineering ",12) ) + osCRS = pszProjOverride; + } + +/* -------------------------------------------------------------------- */ +/* Build CRS name to use. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + const char *pszAuth; + + if( pszProjection && strlen(pszProjection) > 0 && osCRS == "" ) + { + oSRS.SetFromUserInput( pszProjection ); + pszAuth = oSRS.GetAuthorityName(NULL); + + if( pszAuth != NULL && EQUAL(pszAuth,"EPSG") ) + { + pszAuth = oSRS.GetAuthorityCode(NULL); + if( pszAuth ) + { + osCRS = "EPSG:"; + osCRS += pszAuth; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to define CRS to use." ); + return FALSE; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Pick a format type if we don't already have one selected. */ +/* */ +/* We will prefer anything that sounds like TIFF, otherwise */ +/* falling back to the first supported format. Should we */ +/* consider preferring the nativeFormat if available? */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLValue( psService, "PreferredFormat", NULL ) == NULL ) + { + CPLXMLNode *psSF = CPLGetXMLNode( psCO, "supportedFormats" ); + CPLXMLNode *psNode; + char **papszFormatList = NULL; + CPLString osPreferredFormat; + int iFormat; + + if( psSF == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No tag in service definition file, and no\n" + " in coverageOffering." ); + return FALSE; + } + + for( psNode = psSF->psChild; psNode != NULL; psNode = psNode->psNext ) + { + if( psNode->eType == CXT_Element + && EQUAL(psNode->pszValue,"formats") + && psNode->psChild != NULL + && psNode->psChild->eType == CXT_Text ) + { + // This check is looking for deprecated WCS 1.0 capabilities + // with multiple formats space delimited in a single + // element per GDAL ticket 1748 (done by MapServer 4.10 and + // earlier for instance). + if( papszFormatList == NULL + && psNode->psNext == NULL + && strstr(psNode->psChild->pszValue," ") != NULL + && strstr(psNode->psChild->pszValue,";") == NULL ) + { + char **papszSubList = + CSLTokenizeString( psNode->psChild->pszValue ); + papszFormatList = CSLInsertStrings( papszFormatList, + -1, papszSubList ); + CSLDestroy( papszSubList ); + } + else + { + papszFormatList = CSLAddString( papszFormatList, + psNode->psChild->pszValue); + } + } + } + + for( iFormat = 0; + papszFormatList != NULL && papszFormatList[iFormat] != NULL; + iFormat++ ) + { + if( strlen(osPreferredFormat) == 0 ) + osPreferredFormat = papszFormatList[iFormat]; + + if( strstr(papszFormatList[iFormat],"tiff") != NULL + || strstr(papszFormatList[iFormat],"TIFF") != NULL + || strstr(papszFormatList[iFormat],"Tiff") != NULL ) + { + osPreferredFormat = papszFormatList[iFormat]; + break; + } + } + + CSLDestroy( papszFormatList ); + + if( strlen(osPreferredFormat) > 0 ) + { + bServiceDirty = TRUE; + CPLCreateXMLElementAndValue( psService, "PreferredFormat", + osPreferredFormat ); + } + } + +/* -------------------------------------------------------------------- */ +/* Try to identify a nodata value. For now we only support the */ +/* singleValue mechanism. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLValue( psService, "NoDataValue", NULL ) == NULL ) + { + const char *pszSV = CPLGetXMLValue( psCO, "rangeSet.RangeSet.nullValues.singleValue", NULL ); + + if( pszSV != NULL && (CPLAtof(pszSV) != 0.0 || *pszSV == '0') ) + { + bServiceDirty = TRUE; + CPLCreateXMLElementAndValue( psService, "NoDataValue", + pszSV ); + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have a Band range type. For now we look for a fairly */ +/* specific configuration. The rangeset my have one axis named */ +/* "Band", with a set of ascending numerical values. */ +/* -------------------------------------------------------------------- */ + osBandIdentifier = CPLGetXMLValue( psService, "BandIdentifier", "" ); + CPLXMLNode * psAD = CPLGetXMLNode( psService, + "CoverageOffering.rangeSet.RangeSet.axisDescription.AxisDescription" ); + CPLXMLNode *psValues; + + if( strlen(osBandIdentifier) == 0 + && psAD != NULL + && (EQUAL(CPLGetXMLValue(psAD,"name",""),"Band") + || EQUAL(CPLGetXMLValue(psAD,"name",""),"Bands")) + && ( (psValues = CPLGetXMLNode( psAD, "values" )) != NULL ) ) + { + CPLXMLNode *psSV; + int iBand; + + osBandIdentifier = CPLGetXMLValue(psAD,"name",""); + + for( psSV = psValues->psChild, iBand = 1; + psSV != NULL; + psSV = psSV->psNext, iBand++ ) + { + if( psSV->eType != CXT_Element + || !EQUAL(psSV->pszValue,"singleValue") + || psSV->psChild == NULL + || psSV->psChild->eType != CXT_Text + || atoi(psSV->psChild->pszValue) != iBand ) + { + osBandIdentifier = ""; + break; + } + } + + if( strlen(osBandIdentifier) ) + { + bServiceDirty = TRUE; + CPLCreateXMLElementAndValue( psService, "BandIdentifier", + osBandIdentifier ); + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have a temporal domain? If so, try to identify a */ +/* default time value. */ +/* -------------------------------------------------------------------- */ + osDefaultTime = CPLGetXMLValue( psService, "DefaultTime", "" ); + CPLXMLNode * psTD = + CPLGetXMLNode( psService, "CoverageOffering.domainSet.temporalDomain" ); + CPLString osServiceURL = CPLGetXMLValue( psService, "ServiceURL", "" ); + CPLString osCoverageExtra = CPLGetXMLValue( psService, "GetCoverageExtra", "" ); + + if( psTD != NULL ) + { + CPLXMLNode *psTime; + + // collect all the allowed time positions. + + for( psTime = psTD->psChild; psTime != NULL; psTime = psTime->psNext ) + { + if( psTime->eType == CXT_Element + && EQUAL(psTime->pszValue,"timePosition") + && psTime->psChild != NULL + && psTime->psChild->eType == CXT_Text ) + aosTimePositions.push_back( psTime->psChild->pszValue ); + } + + // we will default to the last - likely the most recent - entry. + + if( aosTimePositions.size() > 0 + && osDefaultTime == "" + && osServiceURL.ifind("time=") == std::string::npos + && osCoverageExtra.ifind("time=") == std::string::npos ) + { + osDefaultTime = aosTimePositions[aosTimePositions.size()-1]; + + bServiceDirty = TRUE; + CPLCreateXMLElementAndValue( psService, "DefaultTime", + osDefaultTime ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* ParseBoundingBox() */ +/************************************************************************/ + +static int ParseBoundingBox( CPLXMLNode *psBoundingBox, CPLString &osCRS, + double &dfLowerX, double &dfLowerY, + double &dfUpperX, double &dfUpperY ) + +{ + int nRet = TRUE; + + osCRS = CPLGetXMLValue( psBoundingBox, "crs", "" ); + + char **papszLC = CSLTokenizeStringComplex( + CPLGetXMLValue( psBoundingBox, "LowerCorner", ""), + " ", FALSE, FALSE ); + char **papszUC = CSLTokenizeStringComplex( + CPLGetXMLValue( psBoundingBox, "UpperCorner", ""), + " ", FALSE, FALSE ); + + if( CSLCount(papszLC) >= 2 && CSLCount(papszUC) >= 2 ) + { + dfLowerX = CPLAtof(papszLC[0]); + dfLowerY = CPLAtof(papszLC[1]); + dfUpperX = CPLAtof(papszUC[0]); + dfUpperY = CPLAtof(papszUC[1]); + } + else + nRet = FALSE; + + CSLDestroy( papszUC ); + CSLDestroy( papszLC ); + + return nRet; +} + +/************************************************************************/ +/* ExtractGridInfo() */ +/* */ +/* Collect info about grid from describe coverage for WCS 1.1 */ +/* and above. */ +/************************************************************************/ + +int WCSDataset::ExtractGridInfo() + +{ + if( nVersion == 100 ) + return ExtractGridInfo100(); + + CPLXMLNode * psCO = CPLGetXMLNode( psService, "CoverageDescription" ); + + if( psCO == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* We need to strip off name spaces so it is easier to */ +/* searchfor plain gml names. */ +/* -------------------------------------------------------------------- */ + CPLStripXMLNamespace( psCO, NULL, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Verify we have a SpatialDomain and GridCRS. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psSD = + CPLGetXMLNode( psCO, "Domain.SpatialDomain" ); + CPLXMLNode *psGCRS = + CPLGetXMLNode( psSD, "GridCRS" ); + + if( psSD == NULL || psGCRS == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find GridCRS in CoverageDescription,\n" + "unable to process WCS Coverage." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Extract Geotransform from GridCRS. */ +/* -------------------------------------------------------------------- */ + const char *pszGridType = CPLGetXMLValue( psGCRS, "GridType", + "urn:ogc:def:method:WCS::2dSimpleGrid" ); + + char **papszOriginTokens = + CSLTokenizeStringComplex( CPLGetXMLValue( psGCRS, "GridOrigin", ""), + " ", FALSE, FALSE ); + char **papszOffsetTokens = + CSLTokenizeStringComplex( CPLGetXMLValue( psGCRS, "GridOffsets", ""), + " ", FALSE, FALSE ); + + if( strstr(pszGridType,":2dGridIn2dCrs") + || strstr(pszGridType,":2dGridin2dCrs") ) + { + if( CSLCount(papszOffsetTokens) == 4 + && CSLCount(papszOriginTokens) == 2 ) + { + adfGeoTransform[0] = CPLAtof(papszOriginTokens[0]); + adfGeoTransform[1] = CPLAtof(papszOffsetTokens[0]); + adfGeoTransform[2] = CPLAtof(papszOffsetTokens[1]); + adfGeoTransform[3] = CPLAtof(papszOriginTokens[1]); + adfGeoTransform[4] = CPLAtof(papszOffsetTokens[2]); + adfGeoTransform[5] = CPLAtof(papszOffsetTokens[3]); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "2dGridIn2dCrs does not have expected GridOrigin or\n" + "GridOffsets values - unable to process WCS coverage."); + return FALSE; + } + } + + else if( strstr(pszGridType,":2dGridIn3dCrs") ) + { + if( CSLCount(papszOffsetTokens) == 6 + && CSLCount(papszOriginTokens) == 3 ) + { + adfGeoTransform[0] = CPLAtof(papszOriginTokens[0]); + adfGeoTransform[1] = CPLAtof(papszOffsetTokens[0]); + adfGeoTransform[2] = CPLAtof(papszOffsetTokens[1]); + adfGeoTransform[3] = CPLAtof(papszOriginTokens[1]); + adfGeoTransform[4] = CPLAtof(papszOffsetTokens[3]); + adfGeoTransform[5] = CPLAtof(papszOffsetTokens[4]); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "2dGridIn3dCrs does not have expected GridOrigin or\n" + "GridOffsets values - unable to process WCS coverage."); + return FALSE; + } + } + + else if( strstr(pszGridType,":2dSimpleGrid") ) + { + if( CSLCount(papszOffsetTokens) == 2 + && CSLCount(papszOriginTokens) == 2 ) + { + adfGeoTransform[0] = CPLAtof(papszOriginTokens[0]); + adfGeoTransform[1] = CPLAtof(papszOffsetTokens[0]); + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = CPLAtof(papszOriginTokens[1]); + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = CPLAtof(papszOffsetTokens[1]); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "2dSimpleGrid does not have expected GridOrigin or\n" + "GridOffsets values - unable to process WCS coverage."); + return FALSE; + } + } + + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised GridCRS.GridType value '%s',\n" + "unable to process WCS coverage.", + pszGridType ); + return FALSE; + } + + CSLDestroy( papszOffsetTokens ); + CSLDestroy( papszOriginTokens ); + + // GridOrigin is center of pixel ... offset half pixel to adjust. + + adfGeoTransform[0] -= (adfGeoTransform[1]+adfGeoTransform[2]) * 0.5; + adfGeoTransform[3] -= (adfGeoTransform[4]+adfGeoTransform[5]) * 0.5; + +/* -------------------------------------------------------------------- */ +/* Establish our coordinate system. */ +/* -------------------------------------------------------------------- */ + osCRS = CPLGetXMLValue( psGCRS, "GridBaseCRS", "" ); + + if( strlen(osCRS) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find GridCRS.GridBaseCRS" ); + return FALSE; + } + else if( strstr(osCRS,":imageCRS") ) + { + // raw image. + } + else + { + OGRSpatialReference oSRS; + if( oSRS.importFromURN( osCRS ) == OGRERR_NONE ) + { + CPLFree( pszProjection ); + oSRS.exportToWkt( &pszProjection ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to interprete GridBaseCRS '%s'.", + osCRS.c_str() ); + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Search for an ImageCRS for raster size. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psNode; + + nRasterXSize = -1; + nRasterYSize = -1; + for( psNode = psSD->psChild; + psNode != NULL && nRasterXSize == -1; + psNode = psNode->psNext ) + { + if( psNode->eType != CXT_Element + || !EQUAL(psNode->pszValue,"BoundingBox") ) + continue; + + double dfLX, dfLY, dfUX, dfUY; + CPLString osBBCRS; + + if( ParseBoundingBox( psNode, osBBCRS, dfLX, dfLY, dfUX, dfUY ) + && strstr(osBBCRS,":imageCRS") + && dfLX == 0 && dfLY == 0 ) + { + nRasterXSize = (int) (dfUX + 1.01); + nRasterYSize = (int) (dfUY + 1.01); + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise we search for a bounding box in our coordinate */ +/* system and derive the size from that. */ +/* -------------------------------------------------------------------- */ + for( psNode = psSD->psChild; + psNode != NULL && nRasterXSize == -1; + psNode = psNode->psNext ) + { + if( psNode->eType != CXT_Element + || !EQUAL(psNode->pszValue,"BoundingBox") ) + continue; + + double dfLX, dfLY, dfUX, dfUY; + CPLString osBBCRS; + + if( ParseBoundingBox( psNode, osBBCRS, dfLX, dfLY, dfUX, dfUY ) + && osBBCRS == osCRS + && adfGeoTransform[2] == 0.0 + && adfGeoTransform[4] == 0.0 ) + { + nRasterXSize = + (int) ((dfUX - dfLX) / adfGeoTransform[1] + 1.01); + nRasterYSize = + (int) ((dfUY - dfLY) / fabs(adfGeoTransform[5]) + 1.01); + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have a coordinate system override? */ +/* -------------------------------------------------------------------- */ + const char *pszProjOverride = CPLGetXMLValue( psService, "SRS", NULL ); + + if( pszProjOverride ) + { + OGRSpatialReference oSRS; + + if( oSRS.SetFromUserInput( pszProjOverride ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + " element contents not parsable:\n%s", + pszProjOverride ); + return FALSE; + } + + CPLFree( pszProjection ); + oSRS.exportToWkt( &pszProjection ); + } + +/* -------------------------------------------------------------------- */ +/* Pick a format type if we don't already have one selected. */ +/* */ +/* We will prefer anything that sounds like TIFF, otherwise */ +/* falling back to the first supported format. Should we */ +/* consider preferring the nativeFormat if available? */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLValue( psService, "PreferredFormat", NULL ) == NULL ) + { + CPLXMLNode *psNode; + CPLString osPreferredFormat; + + for( psNode = psCO->psChild; psNode != NULL; psNode = psNode->psNext ) + { + if( psNode->eType == CXT_Element + && EQUAL(psNode->pszValue,"SupportedFormat") + && psNode->psChild + && psNode->psChild->eType == CXT_Text ) + { + if( strlen(osPreferredFormat) == 0 ) + osPreferredFormat = psNode->psChild->pszValue; + + if( strstr(psNode->psChild->pszValue,"tiff") != NULL + || strstr(psNode->psChild->pszValue,"TIFF") != NULL + || strstr(psNode->psChild->pszValue,"Tiff") != NULL ) + { + osPreferredFormat = psNode->psChild->pszValue; + break; + } + } + } + + if( strlen(osPreferredFormat) > 0 ) + { + bServiceDirty = TRUE; + CPLCreateXMLElementAndValue( psService, "PreferredFormat", + osPreferredFormat ); + } + } + +/* -------------------------------------------------------------------- */ +/* Try to identify a nodata value. For now we only support the */ +/* singleValue mechanism. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLValue( psService, "NoDataValue", NULL ) == NULL ) + { + const char *pszSV = + CPLGetXMLValue( psCO, "Range.Field.NullValue", NULL ); + + if( pszSV != NULL && (CPLAtof(pszSV) != 0.0 || *pszSV == '0') ) + { + bServiceDirty = TRUE; + CPLCreateXMLElementAndValue( psService, "NoDataValue", + pszSV ); + } + } + +/* -------------------------------------------------------------------- */ +/* Grab the field name, if possible. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLValue( psService, "FieldName", NULL ) == NULL ) + { + CPLString osFieldName = + CPLGetXMLValue( psCO, "Range.Field.Identifier", "" ); + + if( strlen(osFieldName) > 0 ) + { + bServiceDirty = TRUE; + CPLCreateXMLElementAndValue( psService, "FieldName", + osFieldName ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find required Identifier name %s for Range Field.", + osCRS.c_str() ); + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have a "Band" axis? If so try to grab the bandcount */ +/* and data type from it. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode * psAxis = CPLGetXMLNode( + psService, "CoverageDescription.Range.Field.Axis" ); + + if( (EQUAL(CPLGetXMLValue(psAxis,"Identifier",""),"Band") + || EQUAL(CPLGetXMLValue(psAxis,"Identifier",""),"Bands")) + && CPLGetXMLNode(psAxis,"AvailableKeys") != NULL ) + { + osBandIdentifier = CPLGetXMLValue(psAxis,"Identifier",""); + + // verify keys are ascending starting at 1 + CPLXMLNode *psValues = CPLGetXMLNode(psAxis,"AvailableKeys"); + CPLXMLNode *psSV; + int iBand; + + for( psSV = psValues->psChild, iBand = 1; + psSV != NULL; + psSV = psSV->psNext, iBand++ ) + { + if( psSV->eType != CXT_Element + || !EQUAL(psSV->pszValue,"Key") + || psSV->psChild == NULL + || psSV->psChild->eType != CXT_Text + || atoi(psSV->psChild->pszValue) != iBand ) + { + osBandIdentifier = ""; + break; + } + } + + if( strlen(osBandIdentifier) ) + { + bServiceDirty = TRUE; + if( CPLGetXMLValue(psService,"BandIdentifier",NULL) == NULL ) + CPLCreateXMLElementAndValue( psService, "BandIdentifier", + osBandIdentifier ); + + if( CPLGetXMLValue(psService,"BandCount",NULL) == NULL ) + CPLCreateXMLElementAndValue( psService, "BandCount", + CPLString().Printf("%d",iBand-1)); + } + + // Is this an ESRI server returning a GDAL recognised data type? + CPLString osDataType = CPLGetXMLValue( psAxis, "DataType", "" ); + if( GDALGetDataTypeByName(osDataType) != GDT_Unknown + && CPLGetXMLValue(psService,"BandType",NULL) == NULL ) + { + bServiceDirty = TRUE; + CPLCreateXMLElementAndValue( psService, "BandType", osDataType ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* ProcessError() */ +/* */ +/* Process an HTTP error, reporting it via CPL, and destroying */ +/* the HTTP result object. Returns TRUE if there was an error, */ +/* or FALSE if the result seems ok. */ +/************************************************************************/ + +int WCSDataset::ProcessError( CPLHTTPResult *psResult ) + +{ +/* -------------------------------------------------------------------- */ +/* There isn't much we can do in this case. Hopefully an error */ +/* was already issued by CPLHTTPFetch() */ +/* -------------------------------------------------------------------- */ + if( psResult == NULL || psResult->nDataLen == 0 ) + { + CPLHTTPDestroyResult( psResult ); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* If we got an html document, we presume it is an error */ +/* message and report it verbatim up to a certain size limit. */ +/* -------------------------------------------------------------------- */ + + if( psResult->pszContentType != NULL + && strstr(psResult->pszContentType, "html") != NULL ) + { + CPLString osErrorMsg = (char *) psResult->pabyData; + + if( osErrorMsg.size() > 2048 ) + osErrorMsg.resize( 2048 ); + + CPLError( CE_Failure, CPLE_AppDefined, + "Malformed Result:\n%s", + osErrorMsg.c_str() ); + CPLHTTPDestroyResult( psResult ); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Does this look like a service exception? We would like to */ +/* check based on the Content-type, but this seems quite */ +/* undependable, even from MapServer! */ +/* -------------------------------------------------------------------- */ + if( strstr((const char *)psResult->pabyData, "ServiceException") + || strstr((const char *)psResult->pabyData, "ExceptionReport") ) + { + CPLXMLNode *psTree = CPLParseXMLString( (const char *) + psResult->pabyData ); + const char *pszMsg = NULL; + + CPLStripXMLNamespace( psTree, NULL, TRUE ); + + // VERSION 1.0.0 + if( psTree != NULL ) + pszMsg = CPLGetXMLValue(psTree, + "=ServiceExceptionReport.ServiceException", + NULL ); + // VERSION 1.1.0 + if( pszMsg == NULL ) + pszMsg = CPLGetXMLValue(psTree, + "=ExceptionReport.Exception.ExceptionText", + NULL ); + + if( pszMsg ) + CPLError( CE_Failure, CPLE_AppDefined, + "%s", pszMsg ); + else + CPLError( CE_Failure, CPLE_AppDefined, + "Corrupt Service Exception:\n%s", + (const char *) psResult->pabyData ); + + CPLDestroyXMLNode( psTree ); + CPLHTTPDestroyResult( psResult ); + return TRUE; + } + + +/* -------------------------------------------------------------------- */ +/* Hopefully the error already issued by CPLHTTPFetch() is */ +/* sufficient. */ +/* -------------------------------------------------------------------- */ + if( CPLGetLastErrorNo() != 0 ) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* EstablishRasterDetails() */ +/* */ +/* Do a "test" coverage query to work out the number of bands, */ +/* and pixel data type of the remote coverage. */ +/************************************************************************/ + +int WCSDataset::EstablishRasterDetails() + +{ + CPLXMLNode * psCO = CPLGetXMLNode( psService, "CoverageOffering" ); + + const char* pszCols = CPLGetXMLValue( psCO, "dimensionLimit.columns", NULL ); + const char* pszRows = CPLGetXMLValue( psCO, "dimensionLimit.rows", NULL ); + if( pszCols && pszRows ) + { + nMaxCols = atoi(pszCols); + nMaxRows = atoi(pszRows); + SetMetadataItem("MAXNCOLS", pszCols, "IMAGE_STRUCTURE" ); + SetMetadataItem("MAXNROWS", pszRows, "IMAGE_STRUCTURE" ); + } + +/* -------------------------------------------------------------------- */ +/* Do we already have bandcount and pixel type settings? */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLValue( psService, "BandCount", NULL ) != NULL + && CPLGetXMLValue( psService, "BandType", NULL ) != NULL ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Fetch a small block of raster data. */ +/* -------------------------------------------------------------------- */ + CPLHTTPResult *psResult = NULL; + CPLErr eErr; + + eErr = GetCoverage( 0, 0, 2, 2, 2, 2, 0, NULL, &psResult ); + if( eErr != CE_None ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Try and open result as a dataseat. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poDS = GDALOpenResult( psResult ); + + if( poDS == NULL ) + return FALSE; + + const char* pszPrj = poDS->GetProjectionRef(); + if( pszPrj && strlen(pszPrj) > 0 ) + { + if( pszProjection ) + CPLFree( pszProjection ); + + pszProjection = CPLStrdup( pszPrj ); + } + +/* -------------------------------------------------------------------- */ +/* Record details. */ +/* -------------------------------------------------------------------- */ + if( poDS->GetRasterCount() < 1 ) + { + delete poDS; + return FALSE; + } + + if( CPLGetXMLValue(psService,"BandCount",NULL) == NULL ) + CPLCreateXMLElementAndValue( + psService, "BandCount", + CPLString().Printf("%d",poDS->GetRasterCount())); + + CPLCreateXMLElementAndValue( + psService, "BandType", + GDALGetDataTypeName(poDS->GetRasterBand(1)->GetRasterDataType()) ); + + bServiceDirty = TRUE; + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + delete poDS; + + FlushMemoryResult(); + + return TRUE; +} + +/************************************************************************/ +/* FlushMemoryResult() */ +/* */ +/* This actually either cleans up the in memory /vsimem/ */ +/* temporary file, or the on disk temporary file. */ +/************************************************************************/ +void WCSDataset::FlushMemoryResult() + +{ + if( strlen(osResultFilename) > 0 ) + { + VSIUnlink( osResultFilename ); + osResultFilename = ""; + } + + if( pabySavedDataBuffer ) + { + CPLFree( pabySavedDataBuffer ); + pabySavedDataBuffer = NULL; + } +} + +/************************************************************************/ +/* GDALOpenResult() */ +/* */ +/* Open a CPLHTTPResult as a GDALDataset (if possible). First */ +/* attempt is to open handle it "in memory". Eventually we */ +/* will add support for handling it on file if necessary. */ +/* */ +/* This method will free CPLHTTPResult, the caller should not */ +/* access it after the call. */ +/************************************************************************/ + +GDALDataset *WCSDataset::GDALOpenResult( CPLHTTPResult *psResult ) + +{ + FlushMemoryResult(); + + CPLDebug( "WCS", "GDALOpenResult() on content-type: %s", + psResult->pszContentType ); + +/* -------------------------------------------------------------------- */ +/* If this is multipart/related content type, we should search */ +/* for the second part. */ +/* -------------------------------------------------------------------- */ + GByte *pabyData = psResult->pabyData; + int nDataLen = psResult->nDataLen; + + if( psResult->pszContentType + && strstr(psResult->pszContentType,"multipart") + && CPLHTTPParseMultipartMime(psResult) ) + { + if( psResult->nMimePartCount > 1 ) + { + pabyData = psResult->pasMimePart[1].pabyData; + nDataLen = psResult->pasMimePart[1].nDataLen; + + if (CSLFindString(psResult->pasMimePart[1].papszHeaders, + "Content-Transfer-Encoding: base64") != -1) + { + nDataLen = CPLBase64DecodeInPlace(pabyData); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create a memory file from the result. */ +/* -------------------------------------------------------------------- */ + // Eventually we should be looking at mime info and stuff to figure + // out an optimal filename, but for now we just use a fixed one. + osResultFilename.Printf( "/vsimem/wcs/%p/wcsresult.dat", + this ); + + VSILFILE *fp = VSIFileFromMemBuffer( osResultFilename, pabyData, nDataLen, + FALSE ); + + if( fp == NULL ) + { + CPLHTTPDestroyResult(psResult); + return NULL; + } + + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Try opening this result as a gdaldataset. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poDS = (GDALDataset *) + GDALOpen( osResultFilename, GA_ReadOnly ); + +/* -------------------------------------------------------------------- */ +/* If opening it in memory didn't work, perhaps we need to */ +/* write to a temp file on disk? */ +/* -------------------------------------------------------------------- */ + if( poDS == NULL ) + { + CPLString osTempFilename; + VSILFILE *fpTemp; + + osTempFilename.Printf( "/tmp/%p_wcs.dat", this ); + + fpTemp = VSIFOpenL( osTempFilename, "wb" ); + if( fpTemp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create temporary file:%s", + osTempFilename.c_str() ); + } + else + { + if( VSIFWriteL( pabyData, nDataLen, 1, fpTemp ) + != 1 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to write temporary file:%s", + osTempFilename.c_str() ); + VSIFCloseL( fpTemp ); + VSIUnlink( osTempFilename ); + } + else + { + VSIFCloseL( fpTemp ); + VSIUnlink( osResultFilename ); + osResultFilename = osTempFilename; + + poDS = (GDALDataset *) + GDALOpen( osResultFilename, GA_ReadOnly ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Steal the memory buffer from HTTP result. */ +/* -------------------------------------------------------------------- */ + pabySavedDataBuffer = psResult->pabyData; + + psResult->pabyData = NULL; + psResult->nDataLen = psResult->nDataAlloc = 0; + + if( poDS == NULL ) + FlushMemoryResult(); + + CPLHTTPDestroyResult(psResult); + + return poDS; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int WCSDataset::Identify( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* Is this a WCS_GDAL service description file or "in url" */ +/* equivelent? */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes == 0 + && EQUALN((const char *) poOpenInfo->pszFilename,"",10) ) + return TRUE; + + else if( poOpenInfo->nHeaderBytes >= 10 + && EQUALN((const char *) poOpenInfo->pabyHeader,"",10) ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Is this apparently a WCS subdataset reference? */ +/* -------------------------------------------------------------------- */ + else if( EQUALN((const char *) poOpenInfo->pszFilename,"WCS_SDS:",8) + && poOpenInfo->nHeaderBytes == 0 ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *WCSDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + char **papszModifiers = NULL; + +/* -------------------------------------------------------------------- */ +/* Is this a WCS_GDAL service description file or "in url" */ +/* equivelent? */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psService = NULL; + + if( poOpenInfo->nHeaderBytes == 0 + && EQUALN((const char *) poOpenInfo->pszFilename,"",10) ) + { + psService = CPLParseXMLString( poOpenInfo->pszFilename ); + } + else if( poOpenInfo->nHeaderBytes >= 10 + && EQUALN((const char *) poOpenInfo->pabyHeader,"",10) ) + { + psService = CPLParseXMLFile( poOpenInfo->pszFilename ); + } +/* -------------------------------------------------------------------- */ +/* Is this apparently a subdataset? */ +/* -------------------------------------------------------------------- */ + else if( EQUALN((const char *) poOpenInfo->pszFilename,"WCS_SDS:",8) + && poOpenInfo->nHeaderBytes == 0 ) + { + int iLast; + + papszModifiers = CSLTokenizeString2( poOpenInfo->pszFilename+8, ",", + CSLT_HONOURSTRINGS ); + + iLast = CSLCount(papszModifiers)-1; + if( iLast >= 0 ) + { + psService = CPLParseXMLFile( papszModifiers[iLast] ); + CPLFree( papszModifiers[iLast] ); + papszModifiers[iLast] = NULL; + } + + } + +/* -------------------------------------------------------------------- */ +/* Success so far? */ +/* -------------------------------------------------------------------- */ + if( psService == NULL ) + { + CSLDestroy( papszModifiers ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CSLDestroy( papszModifiers ); + CPLDestroyXMLNode( psService ); + CPLError( CE_Failure, CPLE_NotSupported, + "The WCS driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Check for required minimum fields. */ +/* -------------------------------------------------------------------- */ + if( !CPLGetXMLValue( psService, "ServiceURL", NULL ) + || !CPLGetXMLValue( psService, "CoverageName", NULL ) ) + { + CSLDestroy( papszModifiers ); + CPLError( CE_Failure, CPLE_OpenFailed, + "Missing one or both of ServiceURL and CoverageName elements.\n" + "See WCS driver documentation for details on service description file format." ); + + CPLDestroyXMLNode( psService ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* What version are we working with? */ +/* -------------------------------------------------------------------- */ + const char *pszVersion = CPLGetXMLValue( psService, "Version", "1.0.0" ); + int nVersion; + + if (EQUAL(pszVersion, "1.1.2") ) + nVersion = 112; + else if( EQUAL(pszVersion,"1.1.1") ) + nVersion = 111; + else if( EQUAL(pszVersion,"1.1.0") ) + nVersion = 110; + else if( EQUAL(pszVersion,"1.0.0") ) + nVersion = 100; + else + { + CSLDestroy( papszModifiers ); + CPLError( CE_Failure, CPLE_AppDefined, + "WCS Version '%s' not supported.", pszVersion ); + CPLDestroyXMLNode( psService ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + WCSDataset *poDS; + + poDS = new WCSDataset(); + + poDS->psService = psService; + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->nVersion = nVersion; + poDS->papszSDSModifiers = papszModifiers; + +/* -------------------------------------------------------------------- */ +/* Capture HTTP parameters. */ +/* -------------------------------------------------------------------- */ + const char *pszParm; + + poDS->papszHttpOptions = + CSLSetNameValue(poDS->papszHttpOptions, + "TIMEOUT", + CPLGetXMLValue( psService, "Timeout", "30" ) ); + + pszParm = CPLGetXMLValue( psService, "HTTPAUTH", NULL ); + if( pszParm ) + poDS->papszHttpOptions = + CSLSetNameValue( poDS->papszHttpOptions, + "HTTPAUTH", pszParm ); + + pszParm = CPLGetXMLValue( psService, "USERPWD", NULL ); + if( pszParm ) + poDS->papszHttpOptions = + CSLSetNameValue( poDS->papszHttpOptions, + "USERPWD", pszParm ); + +/* -------------------------------------------------------------------- */ +/* If we don't have the DescribeCoverage result for this */ +/* coverage, fetch it now. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psService, "CoverageOffering" ) == NULL + && CPLGetXMLNode( psService, "CoverageDescription" ) == NULL ) + { + if( !poDS->DescribeCoverage() ) + { + delete poDS; + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Extract coordinate system, grid size, and geotransform from */ +/* the coverage description and/or service description */ +/* information. */ +/* -------------------------------------------------------------------- */ + if( !poDS->ExtractGridInfo() ) + { + delete poDS; + return NULL; + } + + if( !poDS->EstablishRasterDetails() ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + int nBandCount = atoi(CPLGetXMLValue(psService,"BandCount","1")); + int iBand; + + if (!GDALCheckBandCount(nBandCount, 0)) + { + delete poDS; + return NULL; + } + + for( iBand = 0; iBand < nBandCount; iBand++ ) + poDS->SetBand( iBand+1, new WCSRasterBand( poDS, iBand+1, -1 ) ); + +/* -------------------------------------------------------------------- */ +/* Set time metadata on the dataset if we are selecting a */ +/* temporal slice. */ +/* -------------------------------------------------------------------- */ + CPLString osTime = CSLFetchNameValueDef( poDS->papszSDSModifiers, "time", + poDS->osDefaultTime ); + + if( osTime != "" ) + poDS->GDALMajorObject::SetMetadataItem( "TIME_POSITION", + osTime.c_str() ); + +/* -------------------------------------------------------------------- */ +/* Do we have a band identifier to select only a subset of bands? */ +/* -------------------------------------------------------------------- */ + poDS->osBandIdentifier = CPLGetXMLValue(psService,"BandIdentifier",""); + +/* -------------------------------------------------------------------- */ +/* Do we have time based subdatasets? If so, record them in */ +/* metadata. Note we don't do subdatasets if this is a */ +/* subdataset or if this is an all-in-memory service. */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(poOpenInfo->pszFilename,"WCS_SDS:",8) + && !EQUALN(poOpenInfo->pszFilename,"",10) + && poDS->aosTimePositions.size() > 0 ) + { + char **papszSubdatasets = NULL; + int iTime; + + for( iTime = 0; iTime < (int)poDS->aosTimePositions.size(); iTime++ ) + { + CPLString osName; + CPLString osValue; + + osName.Printf( "SUBDATASET_%d_NAME", iTime+1 ); + osValue.Printf( "WCS_SDS:time=\"%s\",%s", + poDS->aosTimePositions[iTime].c_str(), + poOpenInfo->pszFilename ); + papszSubdatasets = CSLSetNameValue( papszSubdatasets, + osName, osValue ); + + CPLString osCoverage = + CPLGetXMLValue( poDS->psService, "CoverageName", "" ); + + osName.Printf( "SUBDATASET_%d_DESC", iTime+1 ); + osValue.Printf( "Coverage %s at time %s", + osCoverage.c_str(), + poDS->aosTimePositions[iTime].c_str() ); + papszSubdatasets = CSLSetNameValue( papszSubdatasets, + osName, osValue ); + } + + poDS->GDALMajorObject::SetMetadata( papszSubdatasets, + "SUBDATASETS" ); + + CSLDestroy( papszSubdatasets ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->TryLoadXML(); + return( poDS ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr WCSDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + return( CE_None ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *WCSDataset::GetProjectionRef() + +{ + const char* pszPrj = GDALPamDataset::GetProjectionRef(); + if( pszPrj && strlen(pszPrj) > 0 ) + return pszPrj; + + if ( pszProjection && strlen(pszProjection) > 0 ) + return pszProjection; + + return( "" ); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **WCSDataset::GetFileList() + +{ + char **papszFileList = GDALPamDataset::GetFileList(); + +/* -------------------------------------------------------------------- */ +/* ESRI also wishes to include service urls in the file list */ +/* though this is not currently part of the general definition */ +/* of GetFileList() for GDAL. */ +/* -------------------------------------------------------------------- */ +#ifdef ESRI_BUILD + CPLString file; + file.Printf( "%s%s", + CPLGetXMLValue( psService, "ServiceURL", "" ), + CPLGetXMLValue( psService, "CoverageName", "" ) ); + papszFileList = CSLAddString( papszFileList, file.c_str() ); +#endif /* def ESRI_BUILD */ + + return papszFileList; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **WCSDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "xml:CoverageOffering", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **WCSDataset::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain == NULL + || !EQUAL(pszDomain,"xml:CoverageOffering") ) + return GDALPamDataset::GetMetadata( pszDomain ); + + + CPLXMLNode *psNode = CPLGetXMLNode( psService, "CoverageOffering" ); + + if( psNode == NULL ) + psNode = CPLGetXMLNode( psService, "CoverageDescription" ); + + if( psNode == NULL ) + return NULL; + + if( apszCoverageOfferingMD[0] == NULL ) + { + CPLXMLNode *psNext = psNode->psNext; + psNode->psNext = NULL; + + apszCoverageOfferingMD[0] = CPLSerializeXMLTree( psNode ); + + psNode->psNext = psNext; + } + + return apszCoverageOfferingMD; +} + + +/************************************************************************/ +/* GDALRegister_WCS() */ +/************************************************************************/ + +void GDALRegister_WCS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "WCS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "WCS" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "OGC Web Coverage Service" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_wcs.html" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + + poDriver->pfnOpen = WCSDataset::Open; + poDriver->pfnIdentify = WCSDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/webp/GNUmakefile b/bazaar/plugin/gdal/frmts/webp/GNUmakefile new file mode 100644 index 000000000..835a60aa9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/webp/GNUmakefile @@ -0,0 +1,13 @@ + +include ../../GDALmake.opt + +OBJ = webpdataset.o + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/webp/frmt_webp.html b/bazaar/plugin/gdal/frmts/webp/frmt_webp.html new file mode 100644 index 000000000..7f36fc6c5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/webp/frmt_webp.html @@ -0,0 +1,51 @@ + + + + +WEBP - WEBP + + + + +

    WEBP - WEBP

    + +

    Starting with GDAL 1.9.0, GDAL can read and write WebP images through the WebP library.

    + +

    WebP is a new image format that provides lossy compression for photographic images. +A WebP file consists of VP8 image data, and a container based on RIFF.

    + +

    The driver rely on the Open Source WebP library (BSD licenced). The WebP library (at least in its +version 0.1) only offers compression and decompression of whole images, so RAM might be a limitation +when dealing with big images (which are limited to 16383x16383 pixels).

    + +

    The WEBP driver supports 3 bands (RGB) images. It also supports 4 bands (RGBA) starting with GDAL 1.10 and libwebp 0.1.4.

    + +

    The WEBP driver can be used as the internal format used by the Rasterlite driver.

    + +

    Starting with GDAL 1.10, XMP metadata can be extracted from the file, and will be +stored as XML raw content in the xml:XMP metadata domain.

    + +

    Creation options

    + +Various creation options exists, among them : + +
      +
    • QUALITY=n: By default the quality flag is set to 75, but this +option can be used to select other values. Values must be in the +range 1-100. Low values result in higher compression ratios, but poorer +image quality.

    • + +
    • LOSSLESS=True/False (GDAL >= 1.10 and libwebp >= 0.1.4): +By default, lossy compression is used. If set to True, +lossless compression will be used.

    • + +
    + +

    See Also:

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/webp/makefile.vc b/bazaar/plugin/gdal/frmts/webp/makefile.vc new file mode 100644 index 000000000..5e5220b05 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/webp/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = webpdataset.obj + +EXTRAFLAGS = $(WEBP_CFLAGS) + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/webp/webpdataset.cpp b/bazaar/plugin/gdal/frmts/webp/webpdataset.cpp new file mode 100644 index 000000000..61e113c63 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/webp/webpdataset.cpp @@ -0,0 +1,886 @@ +/****************************************************************************** + * $Id: webpdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $ + * + * Project: GDAL WEBP Driver + * Purpose: Implement GDAL WEBP Support based on libwebp + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" + +#include "webp/decode.h" +#include "webp/encode.h" + +CPL_CVSID("$Id: webpdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $"); + +CPL_C_START +void GDALRegister_WEBP(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* WEBPDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class WEBPRasterBand; + +class WEBPDataset : public GDALPamDataset +{ + friend class WEBPRasterBand; + + VSILFILE* fpImage; + GByte* pabyUncompressed; + int bHasBeenUncompressed; + CPLErr eUncompressErrRet; + CPLErr Uncompress(); + + int bHasReadXMPMetadata; + + public: + WEBPDataset(); + ~WEBPDataset(); + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain = "" ); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset* CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* WEBPRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class WEBPRasterBand : public GDALPamRasterBand +{ + friend class WEBPDataset; + + public: + + WEBPRasterBand( WEBPDataset *, int ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual GDALColorInterp GetColorInterpretation(); +}; + +/************************************************************************/ +/* WEBPRasterBand() */ +/************************************************************************/ + +WEBPRasterBand::WEBPRasterBand( WEBPDataset *poDS, CPL_UNUSED int nBand ) +{ + this->poDS = poDS; + + eDataType = GDT_Byte; + + nBlockXSize = poDS->nRasterXSize; + nBlockYSize = 1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr WEBPRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, int nBlockYOff, + void * pImage ) +{ + WEBPDataset* poGDS = (WEBPDataset*) poDS; + + if( poGDS->Uncompress() != CE_None ) + return CE_Failure; + + int i; + GByte* pabyUncompressed = + &poGDS->pabyUncompressed[nBlockYOff * nRasterXSize * poGDS->nBands + nBand - 1]; + for(i=0;inBands * i]; + + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp WEBPRasterBand::GetColorInterpretation() + +{ + if ( nBand == 1 ) + return GCI_RedBand; + + else if( nBand == 2 ) + return GCI_GreenBand; + + else if ( nBand == 3 ) + return GCI_BlueBand; + + else + return GCI_AlphaBand; +} + +/************************************************************************/ +/* ==================================================================== */ +/* WEBPDataset */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* WEBPDataset() */ +/************************************************************************/ + +WEBPDataset::WEBPDataset() + +{ + fpImage = NULL; + pabyUncompressed = NULL; + bHasBeenUncompressed = FALSE; + eUncompressErrRet = CE_None; + bHasReadXMPMetadata = FALSE; +} + +/************************************************************************/ +/* ~WEBPDataset() */ +/************************************************************************/ + +WEBPDataset::~WEBPDataset() + +{ + FlushCache(); + if (fpImage) + VSIFCloseL(fpImage); + VSIFree(pabyUncompressed); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **WEBPDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "xml:XMP", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **WEBPDataset::GetMetadata( const char * pszDomain ) +{ + if ((pszDomain != NULL && EQUAL(pszDomain, "xml:XMP")) && !bHasReadXMPMetadata) + { + bHasReadXMPMetadata = TRUE; + + VSIFSeekL(fpImage, 12, SEEK_SET); + + int bFirst = TRUE; + while(TRUE) + { + char szHeader[5]; + GUInt32 nChunkSize; + + if (VSIFReadL(szHeader, 1, 4, fpImage) != 4 || + VSIFReadL(&nChunkSize, 1, 4, fpImage) != 4) + break; + + szHeader[4] = '\0'; + CPL_LSBPTR32(&nChunkSize); + + if (bFirst) + { + if (strcmp(szHeader, "VP8X") != 0 || nChunkSize < 10) + break; + + int nFlags; + if (VSIFReadL(&nFlags, 1, 4, fpImage) != 4) + break; + CPL_LSBPTR32(&nFlags); + if ((nFlags & 8) == 0) + break; + + VSIFSeekL(fpImage, nChunkSize - 4, SEEK_CUR); + + bFirst = FALSE; + } + else if (strcmp(szHeader, "META") == 0) + { + if (nChunkSize > 1024 * 1024) + break; + + char* pszXMP = (char*) VSIMalloc(nChunkSize + 1); + if (pszXMP == NULL) + break; + + if ((GUInt32)VSIFReadL(pszXMP, 1, nChunkSize, fpImage) != nChunkSize) + { + VSIFree(pszXMP); + break; + } + pszXMP[nChunkSize] = '\0'; + + /* Avoid setting the PAM dirty bit just for that */ + int nOldPamFlags = nPamFlags; + + char *apszMDList[2]; + apszMDList[0] = pszXMP; + apszMDList[1] = NULL; + SetMetadata(apszMDList, "xml:XMP"); + + nPamFlags = nOldPamFlags; + + VSIFree(pszXMP); + break; + } + else + VSIFSeekL(fpImage, nChunkSize, SEEK_CUR); + } + } + + return GDALPamDataset::GetMetadata(pszDomain); +} + +/************************************************************************/ +/* Uncompress() */ +/************************************************************************/ + +CPLErr WEBPDataset::Uncompress() +{ + if (bHasBeenUncompressed) + return eUncompressErrRet; + bHasBeenUncompressed = TRUE; + eUncompressErrRet = CE_Failure; + + pabyUncompressed = (GByte*)VSIMalloc3(nRasterXSize, nRasterYSize, nBands); + if (pabyUncompressed == NULL) + return CE_Failure; + + VSIFSeekL(fpImage, 0, SEEK_END); + vsi_l_offset nSize = VSIFTellL(fpImage); + if (nSize != (vsi_l_offset)(uint32_t)nSize) + return CE_Failure; + VSIFSeekL(fpImage, 0, SEEK_SET); + uint8_t* pabyCompressed = (uint8_t*)VSIMalloc(nSize); + if (pabyCompressed == NULL) + return CE_Failure; + VSIFReadL(pabyCompressed, 1, nSize, fpImage); + uint8_t* pRet; + + if (nBands == 4) + pRet = WebPDecodeRGBAInto(pabyCompressed, (uint32_t)nSize, + (uint8_t*)pabyUncompressed, nRasterXSize * nRasterYSize * nBands, + nRasterXSize * nBands); + else + pRet = WebPDecodeRGBInto(pabyCompressed, (uint32_t)nSize, + (uint8_t*)pabyUncompressed, nRasterXSize * nRasterYSize * nBands, + nRasterXSize * nBands); + VSIFree(pabyCompressed); + if (pRet == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "WebPDecodeRGBInto() failed"); + return CE_Failure; + } + eUncompressErrRet = CE_None; + + return CE_None; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr WEBPDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void *pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + if((eRWFlag == GF_Read) && + (nBandCount == nBands) && + (nXOff == 0) && (nXOff == 0) && + (nXSize == nBufXSize) && (nXSize == nRasterXSize) && + (nYSize == nBufYSize) && (nYSize == nRasterYSize) && + (eBufType == GDT_Byte) && + (pData != NULL) && + (panBandMap != NULL) && + (panBandMap[0] == 1) && (panBandMap[1] == 2) && (panBandMap[2] == 3) && (nBands == 3 || panBandMap[3] == 4)) + { + if( Uncompress() != CE_None ) + return CE_Failure; + if( nPixelSpace == nBands && nLineSpace == (nPixelSpace*nXSize) && nBandSpace == 1 ) + { + memcpy(pData, pabyUncompressed, nBands * nXSize * nYSize); + } + else + { + for(int y = 0; y < nYSize; ++y) + { + GByte* pabyScanline = pabyUncompressed + y * nBands * nXSize; + for(int x = 0; x < nXSize; ++x) + { + for(int iBand=0;iBandnHeaderBytes; + + pabyHeader = poOpenInfo->pabyHeader; + + if( nHeaderBytes < 20 ) + return FALSE; + + return memcmp(pabyHeader, "RIFF", 4) == 0 && + memcmp(pabyHeader + 8, "WEBP", 4) == 0 && + (memcmp(pabyHeader + 12, "VP8 ", 4) == 0 || + memcmp(pabyHeader + 12, "VP8L", 4) == 0 || + memcmp(pabyHeader + 12, "VP8X", 4) == 0); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *WEBPDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if( !Identify( poOpenInfo ) || poOpenInfo->fpL == NULL ) + return NULL; + + int nWidth, nHeight; + if (!WebPGetInfo((const uint8_t*)poOpenInfo->pabyHeader, (uint32_t)poOpenInfo->nHeaderBytes, + &nWidth, &nHeight)) + return NULL; + + int nBands = 3; + +#if WEBP_DECODER_ABI_VERSION >= 0x0002 + WebPDecoderConfig config; + if (!WebPInitDecoderConfig(&config)) + return NULL; + + int bOK = WebPGetFeatures(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes, &config.input) == VP8_STATUS_OK; + + if (config.input.has_alpha) + nBands = 4; + + WebPFreeDecBuffer(&config.output); + + if (!bOK) + return NULL; + +#endif + + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The WEBP driver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + WEBPDataset *poDS; + + poDS = new WEBPDataset(); + poDS->nRasterXSize = nWidth; + poDS->nRasterYSize = nHeight; + poDS->fpImage = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; iBand < nBands; iBand++ ) + poDS->SetBand( iBand+1, new WEBPRasterBand( poDS, iBand+1 ) ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + + poDS->TryLoadXML( poOpenInfo->GetSiblingFiles() ); + +/* -------------------------------------------------------------------- */ +/* Open overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() ); + + return poDS; +} + +/************************************************************************/ +/* WebPUserData */ +/************************************************************************/ + +typedef struct +{ + VSILFILE *fp; + GDALProgressFunc pfnProgress; + void *pProgressData; +} WebPUserData; + +/************************************************************************/ +/* WEBPDatasetWriter() */ +/************************************************************************/ + +static +int WEBPDatasetWriter(const uint8_t* data, size_t data_size, + const WebPPicture* const picture) +{ + WebPUserData* pUserData = (WebPUserData*)picture->custom_ptr; + return VSIFWriteL(data, 1, data_size, pUserData->fp) == data_size; +} + +/************************************************************************/ +/* WEBPDatasetProgressHook() */ +/************************************************************************/ + +#if WEBP_ENCODER_ABI_VERSION >= 0x0100 +static +int WEBPDatasetProgressHook(int percent, const WebPPicture* const picture) +{ + WebPUserData* pUserData = (WebPUserData*)picture->custom_ptr; + return pUserData->pfnProgress( percent / 100.0, NULL, pUserData->pProgressData ); +} +#endif + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset * +WEBPDataset::CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + +/* -------------------------------------------------------------------- */ +/* WEBP library initialization */ +/* -------------------------------------------------------------------- */ + + WebPPicture sPicture; + if (!WebPPictureInit(&sPicture)) + { + CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureInit() failed"); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + + if( nXSize > 16383 || nYSize > 16383 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "WEBP maximum image dimensions are 16383 x 16383."); + + return NULL; + } + + if( nBands != 3 +#if WEBP_ENCODER_ABI_VERSION >= 0x0100 + && nBands != 4 +#endif + ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "WEBP driver doesn't support %d bands. Must be 3 (RGB) " +#if WEBP_ENCODER_ABI_VERSION >= 0x0100 + "or 4 (RGBA) " +#endif + "bands.", + nBands ); + + return NULL; + } + + GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + + if( eDT != GDT_Byte ) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "WEBP driver doesn't support data type %s. " + "Only eight bit byte bands supported.", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); + + if (bStrict) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* What options has the user selected? */ +/* -------------------------------------------------------------------- */ + float fQuality = 75.0f; + const char* pszQUALITY = CSLFetchNameValue(papszOptions, "QUALITY"); + if( pszQUALITY != NULL ) + { + fQuality = (float) CPLAtof(pszQUALITY); + if( fQuality < 0.0f || fQuality > 100.0f ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "%s=%s is not a legal value.", "QUALITY", pszQUALITY); + return NULL; + } + } + + WebPPreset nPreset = WEBP_PRESET_DEFAULT; + const char* pszPRESET = CSLFetchNameValueDef(papszOptions, "PRESET", "DEFAULT"); + if (EQUAL(pszPRESET, "DEFAULT")) + nPreset = WEBP_PRESET_DEFAULT; + else if (EQUAL(pszPRESET, "PICTURE")) + nPreset = WEBP_PRESET_PICTURE; + else if (EQUAL(pszPRESET, "PHOTO")) + nPreset = WEBP_PRESET_PHOTO; + else if (EQUAL(pszPRESET, "PICTURE")) + nPreset = WEBP_PRESET_PICTURE; + else if (EQUAL(pszPRESET, "DRAWING")) + nPreset = WEBP_PRESET_DRAWING; + else if (EQUAL(pszPRESET, "ICON")) + nPreset = WEBP_PRESET_ICON; + else if (EQUAL(pszPRESET, "TEXT")) + nPreset = WEBP_PRESET_TEXT; + else + { + CPLError( CE_Failure, CPLE_IllegalArg, + "%s=%s is not a legal value.", "PRESET", pszPRESET ); + return NULL; + } + + WebPConfig sConfig; + if (!WebPConfigInitInternal(&sConfig, nPreset, fQuality, WEBP_ENCODER_ABI_VERSION)) + { + CPLError(CE_Failure, CPLE_AppDefined, "WebPConfigInit() failed"); + return NULL; + } + + +#define FETCH_AND_SET_OPTION_INT(name, fieldname, minval, maxval) \ +{ \ + const char* pszVal = CSLFetchNameValue(papszOptions, name); \ + if (pszVal != NULL) \ + { \ + sConfig.fieldname = atoi(pszVal); \ + if (sConfig.fieldname < minval || sConfig.fieldname > maxval) \ + { \ + CPLError( CE_Failure, CPLE_IllegalArg, \ + "%s=%s is not a legal value.", name, pszVal ); \ + return NULL; \ + } \ + } \ +} + + FETCH_AND_SET_OPTION_INT("TARGETSIZE", target_size, 0, INT_MAX); + + const char* pszPSNR = CSLFetchNameValue(papszOptions, "PSNR"); + if (pszPSNR) + { + sConfig.target_PSNR = CPLAtof(pszPSNR); + if (sConfig.target_PSNR < 0) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "PSNR=%s is not a legal value.", pszPSNR ); + return NULL; + } + } + + FETCH_AND_SET_OPTION_INT("METHOD", method, 0, 6); + FETCH_AND_SET_OPTION_INT("SEGMENTS", segments, 1, 4); + FETCH_AND_SET_OPTION_INT("SNS_STRENGTH", sns_strength, 0, 100); + FETCH_AND_SET_OPTION_INT("FILTER_STRENGTH", filter_strength, 0, 100); + FETCH_AND_SET_OPTION_INT("FILTER_SHARPNESS", filter_sharpness, 0, 7); + FETCH_AND_SET_OPTION_INT("FILTER_TYPE", filter_type, 0, 1); + FETCH_AND_SET_OPTION_INT("AUTOFILTER", autofilter, 0, 1); + FETCH_AND_SET_OPTION_INT("PASS", pass, 1, 10); + FETCH_AND_SET_OPTION_INT("PREPROCESSING", preprocessing, 0, 1); + FETCH_AND_SET_OPTION_INT("PARTITIONS", partitions, 0, 3); +#if WEBP_ENCODER_ABI_VERSION >= 0x0002 + FETCH_AND_SET_OPTION_INT("PARTITION_LIMIT", partition_limit, 0, 100); +#endif +#if WEBP_ENCODER_ABI_VERSION >= 0x0100 + sConfig.lossless = CSLFetchBoolean(papszOptions, "LOSSLESS", FALSE); + if (sConfig.lossless) + sPicture.use_argb = 1; +#endif + + if (!WebPValidateConfig(&sConfig)) + { + CPLError(CE_Failure, CPLE_AppDefined, "WebPValidateConfig() failed"); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Allocate memory */ +/* -------------------------------------------------------------------- */ + GByte *pabyBuffer; + + pabyBuffer = (GByte *) VSIMalloc( nBands * nXSize * nYSize ); + if (pabyBuffer == NULL) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the dataset. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fpImage; + + fpImage = VSIFOpenL( pszFilename, "wb" ); + if( fpImage == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create WEBP file %s.\n", + pszFilename ); + VSIFree(pabyBuffer); + return NULL; + } + + WebPUserData sUserData; + sUserData.fp = fpImage; + sUserData.pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress; + sUserData.pProgressData = pProgressData; + +/* -------------------------------------------------------------------- */ +/* WEBP library settings */ +/* -------------------------------------------------------------------- */ + + sPicture.width = nXSize; + sPicture.height = nYSize; + sPicture.writer = WEBPDatasetWriter; + sPicture.custom_ptr = &sUserData; +#if WEBP_ENCODER_ABI_VERSION >= 0x0100 + sPicture.progress_hook = WEBPDatasetProgressHook; +#endif + if (!WebPPictureAlloc(&sPicture)) + { + CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureAlloc() failed"); + VSIFree(pabyBuffer); + VSIFCloseL( fpImage ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Acquire source imagery. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + + eErr = poSrcDS->RasterIO( GF_Read, 0, 0, nXSize, nYSize, + pabyBuffer, nXSize, nYSize, GDT_Byte, + nBands, NULL, + nBands, nBands * nXSize, 1, NULL ); + +/* -------------------------------------------------------------------- */ +/* Import and write to file */ +/* -------------------------------------------------------------------- */ +#if WEBP_ENCODER_ABI_VERSION >= 0x0100 + if (eErr == CE_None && nBands == 4) + { + if (!WebPPictureImportRGBA(&sPicture, pabyBuffer, nBands * nXSize)) + { + CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureImportRGBA() failed"); + eErr = CE_Failure; + } + } + else +#endif + if (eErr == CE_None && + !WebPPictureImportRGB(&sPicture, pabyBuffer, nBands * nXSize)) + { + CPLError(CE_Failure, CPLE_AppDefined, "WebPPictureImportRGB() failed"); + eErr = CE_Failure; + } + + if (eErr == CE_None && !WebPEncode(&sConfig, &sPicture)) + { + const char* pszErrorMsg = NULL; +#if WEBP_ENCODER_ABI_VERSION >= 0x0100 + switch(sPicture.error_code) + { + case VP8_ENC_ERROR_OUT_OF_MEMORY: pszErrorMsg = "Out of memory"; break; + case VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY: pszErrorMsg = "Out of memory while flushing bits"; break; + case VP8_ENC_ERROR_NULL_PARAMETER: pszErrorMsg = "A pointer parameter is NULL"; break; + case VP8_ENC_ERROR_INVALID_CONFIGURATION: pszErrorMsg = "Configuration is invalid"; break; + case VP8_ENC_ERROR_BAD_DIMENSION: pszErrorMsg = "Picture has invalid width/height"; break; + case VP8_ENC_ERROR_PARTITION0_OVERFLOW: pszErrorMsg = "Partition is bigger than 512k. Try using less SEGMENTS, or increase PARTITION_LIMIT value"; break; + case VP8_ENC_ERROR_PARTITION_OVERFLOW: pszErrorMsg = "Partition is bigger than 16M"; break; + case VP8_ENC_ERROR_BAD_WRITE: pszErrorMsg = "Error while flusing bytes"; break; + case VP8_ENC_ERROR_FILE_TOO_BIG: pszErrorMsg = "File is bigger than 4G"; break; + case VP8_ENC_ERROR_USER_ABORT: pszErrorMsg = "User interrupted"; break; + default: break; + } +#endif + if (pszErrorMsg) + CPLError(CE_Failure, CPLE_AppDefined, "WebPEncode() failed : %s", pszErrorMsg); + else + CPLError(CE_Failure, CPLE_AppDefined, "WebPEncode() failed"); + eErr = CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup and close. */ +/* -------------------------------------------------------------------- */ + CPLFree( pabyBuffer ); + + WebPPictureFree(&sPicture); + + VSIFCloseL( fpImage ); + + if( eErr != CE_None ) + { + VSIUnlink( pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); + + /* If outputing to stdout, we can't reopen it, so we'll return */ + /* a fake dataset to make the caller happy */ + CPLPushErrorHandler(CPLQuietErrorHandler); + WEBPDataset *poDS = (WEBPDataset*) WEBPDataset::Open( &oOpenInfo ); + CPLPopErrorHandler(); + if( poDS ) + { + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + return poDS; + } + + return NULL; +} + +/************************************************************************/ +/* GDALRegister_WEBP() */ +/************************************************************************/ + +void GDALRegister_WEBP() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "WEBP" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "WEBP" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "WEBP" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_webp.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "webp" ); + poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/webp" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"\n" +" \n" +" \n" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = WEBPDataset::Identify; + poDriver->pfnOpen = WEBPDataset::Open; + poDriver->pfnCreateCopy = WEBPDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/wms/GNUmakefile b/bazaar/plugin/gdal/frmts/wms/GNUmakefile new file mode 100644 index 000000000..4b20a1f65 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/GNUmakefile @@ -0,0 +1,23 @@ + +include ../../GDALmake.opt + +OBJ = gdalwmscache.o gdalwmsdataset.o gdalwmsrasterband.o \ + gdalhttp.o md5.o minidriver.o \ + wmsutils.o wmsdriver.o minidriver_wms.o \ + minidriver_tileservice.o minidriver_worldwind.o \ + minidriver_tms.o minidriver_tiled_wms.o wmsmetadataset.o \ + minidriver_virtualearth.o minidriver_arcgis_server.o + +CPPFLAGS := $(CPPFLAGS) $(CURL_INC) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(OBJ) $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +$(OBJ) $(O_OBJ): gdalhttp.h md5.h minidriver_tileservice.h \ + minidriver_wms.h minidriver_worldwind.h minidriver_tms.h \ + minidriver_tiled_wms.h wmsdriver.h wmsmetadataset.h \ + minidriver_virtualearth.h minidriver_arcgis_server.h diff --git a/bazaar/plugin/gdal/frmts/wms/WMSServerList.txt b/bazaar/plugin/gdal/frmts/wms/WMSServerList.txt new file mode 100644 index 000000000..4e067d270 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/WMSServerList.txt @@ -0,0 +1,49 @@ +TMS: +http://maps.opengeo.org/geowebcache/service/tms/1.0.0 +http://demo.opengeo.org/geoserver/gwc/service/tms/1.0.0 +http://osm.omniscale.net/proxy/tms/1.0.0 +http://apps.esdi-humboldt.cz/mapproxy/tms/1.0.0 +http://apps.esdi-humboldt.cz/cgi-bin/tilecache/tilecache.cgi/1.0.0 +http://tilecache.osgeo.org/wms-c/Basic.py/1.0.0 +http://www2.terriscope.fr/geocache/tms + +WMS-C: +http://maps.opengeo.org/geowebcache/service/wms?SERVICE=WMS&TILED=true +http://demo.opengeo.org/geoserver/gwc/service/wms?SERVICE=WMS&TILED=true +http://osm.omniscale.net/proxy/service?SERVICE=WMS&TILED=true +http://apps.esdi-humboldt.cz/mapproxy/service?SERVICE=WMS&tiled=true +http://tilecache.osgeo.org/wms-c/Basic.py/1.0.0?SERVICE=WMS +http://wmsc1.terrapages.net/getpngmap?SERVICE=WMS +http://wxs.ign.fr/geoportail/wmsc?SERVICE=WMS # requires a key +http://www2.terriscope.fr/geocache/wms?SERVICE=WMS + +Regular WMS: +http://maps.opengeo.org/geowebcache/service/wms?SERVICE=WMS +http://demo.opengeo.org/geoserver/gwc/service/wms?SERVICE=WMS +http://osm.omniscale.net/proxy/service?SERVICE=WMS +http://apps.esdi-humboldt.cz/mapproxy/service?SERVICE=WMS +http://wms.geobase.ca/wms-bin/cubeserv.cgi?SERVICE=WMS +http://mapserver-slp.mendelu.cz/cgi-bin/mapserv?map=/var/local/slp/krtinyWMS.map&SERVICE=WMS +http://vmap0.tiles.osgeo.org/wms/vmap0?SERVICE=WMS +http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi?SERVICE=WMS +http://www.ga.gov.au/bin/getmap.pl?dataset=national&Service=WMS +http://wms.geosignal.fr/metropole?SERVICE=WMS&FORMAT=image/png +http://wms.lizardtech.com/lizardtech/iserv/ows?SERVICE=WMS +http://wms.ess-ws.nrcan.gc.ca/wms/mapserv?map=/export/wms/mapfiles/indexcouverture/canvec.map&SERVICE=WMS +http://210.69.84.29:80/liteview6.5/servlet/MapGuideLiteView?SERVICE=WMS # EPSG:NONE +http://geodata.epa.gov:80/wmsconnector/com.esri.wms.Esrimap/WasteWaterImage?SERVICE=WMS # EPSG:NONE +http://map.ngdc.noaa.gov:80/wmsconnector/com.esri.wms.Esrimap/basicworld?SERVICE=WMS # EPSG:NONE +http://neowms.sci.gsfc.nasa.gov/wms/wms?SERVICE=WMS # SRS only defined in top layer +http://www.cubewerx.com/demo/cubeserv/cubeserv.cgi?SERVICE=WMS +http://www2.demis.nl/wms/wms.asp?wms=WorldMap&SERVICE=WMS +http://iws.erdas.com/ecwp/ecw_wms.dll?service=WMS +http://v2.suite.opengeo.org/geoserver/gwc/service/wms?SERVICE=WMS +http://demo.mapserver.org/cgi-bin/wms?SERVICE=WMS +http://geonames.nga.mil/names/request.asp?SERVICE=WMS + +WMTS: +http://maps.opengeo.org/geowebcache/service/wmts?REQUEST=getcapabilities +http://v2.suite.opengeo.org/geoserver/gwc/service/wmts?REQUEST=getcapabilities +http://www.cubewerx.com/demo/cubeserv/cubeserv.cgi?SERVICE=WMTS&REQUEST=getcapabilities +http://iws.erdas.com/ecwp/ecw_wmts.dll?service=WMTS&request=getcapabilities +http://www2.terriscope.fr/geocache/wmts?service=WMTS&request=getcapabilities \ No newline at end of file diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_ags_arcgisonline.xml b/bazaar/plugin/gdal/frmts/wms/frmt_ags_arcgisonline.xml new file mode 100644 index 000000000..90e209c9b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_ags_arcgisonline.xml @@ -0,0 +1,15 @@ + + + http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer + xyXY + 3857 + + + -20037508.34 + 20037508.34 + 20037508.34 + -20037508.34 + 512 + 512 + + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_twms_Clementine.xml b/bazaar/plugin/gdal/frmts/wms/frmt_twms_Clementine.xml new file mode 100644 index 000000000..ccf3914df --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_twms_Clementine.xml @@ -0,0 +1,6 @@ + + + http://onmoon.jpl.nasa.gov/wms.cgi? + Clementine + + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_twms_Moon.xml b/bazaar/plugin/gdal/frmts/wms/frmt_twms_Moon.xml new file mode 100644 index 000000000..a24762051 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_twms_Moon.xml @@ -0,0 +1,6 @@ + + + http://onmoon.lmmp.nasa.gov/wms.cgi? + LRO WAC Mosaic, LMMP + + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_twms_daily.xml b/bazaar/plugin/gdal/frmts/wms/frmt_twms_daily.xml new file mode 100644 index 000000000..7b4cc4292 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_twms_daily.xml @@ -0,0 +1,8 @@ + + + http://map1.vis.earthdata.nasa.gov/twms-geo/twms.cgi? + MODIS TERRA tileset + + 2012-11-27 + + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_twms_srtm.xml b/bazaar/plugin/gdal/frmts/wms/frmt_twms_srtm.xml new file mode 100644 index 000000000..f4b35724f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_twms_srtm.xml @@ -0,0 +1,6 @@ + + + http://onearth.jpl.nasa.gov/wms.cgi? + Global SRTM Elevation + + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_wms.html b/bazaar/plugin/gdal/frmts/wms/frmt_wms.html new file mode 100644 index 000000000..12b2a729b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_wms.html @@ -0,0 +1,540 @@ + + + + +WMS -- Web Map Services + + + + + +

    WMS -- Web Map Services

    + +

    + Accessing several different types of web image services is possible + using the WMS format in GDAL. Services are accessed by creating a + local service description XML file -- there are examples below for + each of the supported image services. It is important that there be no + spaces or other content before the <GDAL_WMS> element. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    <GDAL_WMS>
    <Service name="WMS">Define what mini-driver to use, currently supported are: WMS, WorldWind, TileService, TMS, TiledWMS, VirtualEarth or AGS. (required)
    <Version>1.1.1</Version>WMS version. (optional, defaults to 1.1.1)
    <ServerUrl>http://onearth.jpl.nasa.gov/wms.cgi?</ServerUrl>WMS server URL. (required)
    <SRS>EPSG:4326</SRS>Image projection (optional, defaults to EPSG:4326 in WMS and 102100 in AGS, WMS version 1.1.1 or below only and ArcGIS Server). + For ArcGIS Server the spatial reference can be specified as either a well-known ID or as a spatial reference json object
    <CRS>CRS:83</CRS>Image projection (optional, defaults to EPSG:4326, WMS version 1.3.0 or above only)
    <ImageFormat>image/jpeg</ImageFormat>Format in which to request data. Paletted formats like image/gif will be converted to RGB. (optional, defaults to image/jpeg)
    <Transparent>FALSE</Transparent>Set to TRUE to include "transparent=TRUE" in the WMS GetMap request (optional defaults to FALSE).  The request format and BandsCount need to support alpha.
    <Layers>modis%2Cglobal_mosaic</Layers>A URL encoded, comma separated string of layers (required, except for TiledWMS)
    <TiledGroupName>Clementine</TiledGroupName>Comma separated list of layers. (required for TiledWMS)
    <Styles></Styles>Comma separated list of styles. (optional)
    <BBoxOrder>xyXY</BBoxOrder>Reorder bbox coordinates arbitrarly. May be required for version 1.3 servers. (optional)
    + x - low X coordinate, y - low Y coordinate, X - high X coordinate, Y - high Y coordinate +
    </Service>
    <DataWindow>Define size and extents of the data. (required, except for TiledWMS and VirtualEarth)
    <UpperLeftX>-180.0</UpperLeftX>X (longitude) coordinate of upper-left corner. (optional, defaults to -180.0, except for VirtualEarth)
    <UpperLeftY>90.0</UpperLeftY>Y (latitude) coordinate of upper-left corner. (optional, defaults to 90.0, except for VirtualEarth)
    <LowerRightX>180.0</LowerRightX>X (longitude) coordinate of lower-right corner. (optional, defaults to 180.0, except for VirtualEarth)
    <LowerRightY>-90.0</LowerRightY>Y (latitude) coordinate of lower-right corner. (optional, defaults to -90.0, except for VirtualEarth)
    <SizeX>2666666</SizeX>Image size in pixels.
    <SizeY>1333333</SizeY>Image size in pixels.
    <TileX>0</TileX>Added to tile X value at highest resolution. (ignored for WMS, tiled image sources only, optional, defaults to 0)
    <TileY>0</TileY>Added to tile Y value at highest resolution. (ignored for WMS, tiled image sources only, optional, defaults to 0)
    <TileLevel>0</TileLevel>Tile level at highest resolution. (tiled image sources only, optional, defaults to 0)
    <TileCountX>0</TileCountX>Can be used to define image size, SizeX = TileCountX * BlockSizeX * 2TileLevel. (tiled image sources only, optional, defaults to 0)
    <TileCountY>0</TileCountY>Can be used to define image size, SizeY = TileCountY * BlockSizeY * 2TileLevel. (tiled image sources only, optional, defaults to 0)
    <YOrigin>top</YOrigin>Can be used to define the position of the Y origin with respect to the tile grid. Possible values are 'top', 'bottom', and 'default', where the default behavior is mini-driver-specific. (TMS mini-driver only, optional, defaults to 'bottom' for TMS)
    </DataWindow>
    <Projection>EPSG:4326</Projection>Image projection (optional, defaults to value reported by mini-driver or EPSG:4326)
    <IdentificationTolerance>2</IdentificationTolerance>Identification tolerance (optional, defaults to 2)
    <BandsCount>3</BandsCount>Number of bands/channels, 1 for grayscale data, 3 for RGB, 4 for RGBA. (optional, defaults to 3)
    <DataType>Byte</DataType>Band data type, amont Byte, Int16, UInt16, Int32, UInt32, Float32, Float64, etc.. (optional, defaults to Byte)
    <BlockSizeX>1024</BlockSizeX>Block size in pixels. (optional, defaults to 1024, except for VirtualEarth)
    <BlockSizeY>1024</BlockSizeY>Block size in pixels. (optional, defaults to 1024, except for VirtualEarth)
    <OverviewCount>10</OverviewCount>Count of reduced resolution layers each having 2 times lower resolution. (optional, default is calculated at runtime)
    <Cache>Enable local disk cache. Allows for offline operation. (optional, defaults to no cache)
    <Path>./gdalwmscache</Path>Location where to store cache files. It is safe to use same cache path for different data sources. (optional, defaults to ./gdalwmscache if GDAL_DEFAULT_WMS_CACHE_PATH configuration option is not specified)
    <Depth>2</Depth>Number of directory layers. 2 will result in files being written as cache_path/A/B/ABCDEF... (optional, defaults to 2)
    <Extension>.jpg</Extension>Append to cache files. (optional, defaults to none)
    </Cache>
    <MaxConnections>2</MaxConnections>Maximum number of simultaneous connections. (optional, defaults to 2)
    <Timeout>300</Timeout>Connection timeout in seconds. (optional, defaults to 300)
    <OfflineMode>true</OfflineMode>Do not download any new images, use only what is in cache. Useful only with cache enabled. (optional, defaults to false)
    <AdviseRead>true</AdviseRead>Enable AdviseRead API call - download images into cache. (optional, defaults to false)
    <VerifyAdviseRead>true</VerifyAdviseRead>Open each downloaded image and do some basic checks before writing into cache. Disabling can save some CPU cycles if server is trusted to always return correct images. (optional, defaults to true)
    <ClampRequests>false</ClampRequests>Should requests, that otherwise would be partially outside of defined data window, be clipped resulting in smaller than block size request. (optional, defaults to true)
    <UserAgent>GDAL WMS driver (http://www.gdal.org/frmt_wms.html)</UserAgent>HTTP User-agent string. Some servers might require a well-known user-agent such as "Mozilla/5.0" (optional, defaults to "GDAL WMS driver (http://www.gdal.org/frmt_wms.html)"). Added in GDAL 1.8.0
    <UserPwd>user:password</UserPwd>User and Password for HTTP authentication (optional). Added in GDAL 1.10.0
    <UnsafeSSL>true</UnsafeSSL>Skip SSL certificate verification. May be needed if server is using a self signed certificate (optional, defaults to false). Added in GDAL 1.8.0.
    <Referer>http://example.foo/</Referer>HTTP Referer string. Some servers might require it (optional). Added in GDAL 1.9.0
    <ZeroBlockHttpCodes>204,404</ZeroBlockHttpCodes>Comma separated list of HTTP response codes that will be interpreted as a 0 filled image (i.e. black for 3 bands, and transparent for 4 bands) instead of aborting the request. Added in GDAL 1.9.0. (optional, defaults to 204)
    <ZeroBlockOnServerException>true</ZeroBlockOnServerException>Wether to treat a Service Exception returned by the server as a 0 filled image instead of aborting the request. Added in 1.9.0. (optional, defaults to false)
    </GDAL_WMS>
    + +

    Minidrivers

    +

    + The GDAL WMS driver has support for several internal 'minidrivers', which + allow access to different web mapping services. Each of these services may + support a different set of options in the Service block. +

    +

    WMS

    +

    + Communications with an OGC WMS server. Has support for both tiled and + untiled requests. +

    + +

    + Starting with GDAL >= 1.10, WMS layers can be queried (through a GetFeatureInfo request) with the gdallocationinfo utility, or + with a GetMetadataItem("Pixel_iCol_iLine", "LocationInfo") call on a band object.

    +

    gdallocationinfo "WMS:http://demo.opengeo.org/geoserver/gwc/service/wms?SERVICE=WMS&VERSION=1.1.1&
    +                            REQUEST=GetMap&LAYERS=og%3Abugsites&SRS=EPSG:900913&
    +                            BBOX=-1.15841845090625E7,5479006.186718751,-1.1505912992109375E7,5557277.703671876&
    +                            FORMAT=image/png&TILESIZE=256&OVERVIEWCOUNT=25&MINRESOLUTION=0.0046653459640220&TILED=true"
    +                           -geoloc -11547071.455 5528616 -xml -b 1
    +    
    + Output: +
    +Report pixel="248595" line="191985">
    +  <BandReport band="1">
    +    <LocationInfo>
    +      <wfs:FeatureCollection xmlns="http://www.opengis.net/wfs"
    +                                xmlns:wfs="http://www.opengis.net/wfs"
    +                                xmlns:gml="http://www.opengis.net/gml"
    +                                xmlns:og="http://opengeo.org"
    +                                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +                                xsi:schemaLocation="http://opengeo.org http://demo.opengeo.org/geoserver/wfs?service=WFS&version=1.0.0&request=DescribeFeatureType&typeName=og%3Abugsites http://www.opengis.net/wfs http://demo.opengeo.org/geoserver/schemas/wfs/1.0.0/WFS-basic.xsd">
    +        <gml:boundedBy>
    +          <gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#26713">
    +            <gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">601228,4917635 601228,4917635</gml:coordinates>
    +          </gml:Box>
    +        </gml:boundedBy>
    +        <gml:featureMember>
    +          <og:bugsites fid="bugsites.40946">
    +            <gml:boundedBy>
    +              <gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#26713">
    +                <gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">601228,4917635 601228,4917635</gml:coordinates>
    +              </gml:Box>
    +            </gml:boundedBy>
    +            <og:cat>86</og:cat>
    +            <og:str1>Beetle site</og:str1>
    +            <og:the_geom>
    +              <gml:Point srsName="http://www.opengis.net/gml/srs/epsg.xml#26713">
    +                <gml:coordinates xmlns:gml="http://www.opengis.net/gml" decimal="." cs="," ts=" ">601228,4917635</gml:coordinates>
    +              </gml:Point>
    +            </og:the_geom>
    +          </og:bugsites>
    +        </gml:featureMember>
    +      </wfs:FeatureCollection>
    +    </LocationInfo>
    +    <Value>255</Value>
    +  </BandReport>
    +</Report>
    +    
    +

    + +

    TileService

    +

    + Service to support talking to a WorldWind TileService. Access is always tile based. +

    + +

    WorldWind

    +

    + Access to web-based WorldWind tile services. Access is always tile based. +

    + +

    TMS (GDAL 1.7.0 and later)

    +

    + The TMS Minidriver is designed primarily to support the users of the + TMS + Specification. This service supports only access by tiles. +

    +

    + Because TMS is similar to many other 'x/y/z' flavored services on the web, + this service can also be used to access these services. To use it in this + fashion, you can use replacement variables, of the format ${x}, ${y}, etc. +

    +

    + Supported variables (name is case sensitive) are : +

    +
      +
    • ${x} -- x position of the tile
    • +
    • ${y} -- y position of the tile. This can be either from the top or the bottom of the tileset, based on whether the YOrigin parameter is set to true or false.
    • +
    • ${z} -- z position of the tile -- zoom level
    • +
    • ${version} -- version parameter, set in the config file. Defaults to 1.0.0.
    • +
    • ${format} -- format parameter, set in the config file. Defaults to 'jpg'.
    • +
    • ${layer} -- layer parameter, set in the config file. Defaults to nothing.
    • +
    +

    + A typical ServerURL might look like:
    + http://tilecache.osgeo.org/wms-c/Basic.py/${version}/${layer}/${z}/${x}/${y}.${format}
    + In order to better suit TMS users, any URL that does not contain "${" will automatically have the string above (after "Basic.py/") appended to their URL. +

    +

    + The TMS Service has 3 XML configuration elements that are different from + other services: Format which defaults to jpg, Layer which has no default, and Version which defaults to 1.0.0. +

    +

    + Additionally, the TMS service respects one additional parameter, at the + DataWindow level, which is the YOrigin element. This element should be one of + bottom (the default in TMS) or top, which matches + OpenStreetMap and many other popular tile services. +

    +

    + Two examples of usage of the TMS service are included in the examples below. +

    + +

    OnEarth Tiled WMS (GDAL 1.9.0 and later)

    +

    +The OnEarth Tiled WMS minidriver supports the Tiled WMS specification +implemented for the JPL OnEarth driver per the specification at +http://onearth.jpl.nasa.gov/tiled.html.

    + +A typical OnEarth Tiled WMS configuration file might look like: + +

    +<GDAL_WMS>
    +    <Service name="TiledWMS">
    +	<ServerUrl>http://onmoon.jpl.nasa.gov/wms.cgi?</ServerUrl>
    +	<TiledGroupName>Clementine</TiledGroupName>
    +    </Service>
    +</GDAL_WMS>
    +
    + +Most of the other information is automatically fetched from the remote +server using the GetTileService method at open time.

    + +

    VirtualEarth (GDAL 1.9.0 and later)

    +

    +Access to web-based Virtual Earth tile services. Access is always tile based.

    +

    The ${quadkey} variable must be found in the ServerUrl element.

    +

    The DataWindow element might be omitted. The default values are : +

      +
    • UpperLeftX = -20037508.34
    • +
    • UpperLeftY = 20037508.34
    • +
    • LowerRightX = 20037508.34
    • +
    • LowerRightY = -20037508.34
    • +
    • TileLevel = 19
    • +
    • OverviewCount = 18
    • +
    • SRS = EPSG:900913
    • +
    • BlockSizeX = 256
    • +
    • BlockSizeY = 256
    • +
    +

    + +

    ArcGIS REST API (GDAL 2.0 and later)

    +

    +Access to ArcGIS REST map service resource (untiled requests). +

    +

    AGS layers can be queried (through a GetFeatureInfo request) with the gdallocationinfo utility, or with a GetMetadataItem("Pixel_iCol_iLine", "LocationInfo") call on a band object. +

    + +

    +

    gdallocationinfo -wgs84 "<GDAL_WMS><Service name=\"AGS\"><ServerUrl>http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer</ServerUrl><BBoxOrder>xyXY</BBoxOrder><SRS>3857</SRS></Service><DataWindow><UpperLeftX>-20037508.34</UpperLeftX><UpperLeftY>20037508.34</UpperLeftY><LowerRightX>20037508.34</LowerRightX><LowerRightY>-20037508.34</LowerRightY><SizeX>512</SizeX><SizeY>512</SizeY></DataWindow></GDAL_WMS>" -75.704 39.75
    +    
    + + +

    Examples

    + +
      +
    • + onearth_global_mosaic.xml - Landsat mosaic from a OnEarth WMS server
      +

      gdal_translate -of JPEG -outsize 500 250 onearth_global_mosaic.xml onearth_global_mosaic.jpg
      +
      gdal_translate -of JPEG -projwin -10 55 30 35 -outsize 500 250 onearth_global_mosaic.xml onearth_global_mosaic2.jpg
      +

      +

      Note : this particular server does no longer accept regular WMS queries.

      +
    • + +
    • + metacarta_wmsc.xml - + It is possible to configure a WMS Service conforming to a WMS-C cache by + specifying a number of overviews and specifying the 'block size' as the + tile size of the cache. The following example is a sample set up for + a 19-level "Global Profile" WMS-C cache. +

      gdal_translate -of PNG -outsize 500 250 metacarta_wmsc.xml metacarta_wmsc.png
      + example output +

    • + +
    • + tileservice_bmng.xml - TileService, Blue Marble NG (January)
      +

      gdal_translate -of JPEG -outsize 500 250 tileservice_bmng.xml tileservice_bmng.jpg
      + example output +

    • + +
    • + tileservice_nysdop2004.xml - TileService, NYSDOP 2004
      +

      gdal_translate -of JPEG -projwin -73.687030 41.262680 -73.686359 41.262345 -outsize 500 250 tileservice_nysdop2004.xml tileservice_nysdop2004.jpg
      + example output +

    • + +
    • + OpenStreetMap TMS Service Example: Connect to OpenStreetMap tile service. Note that this file takes advantage of the tile cache; more information about configuring the tile cache settings is available above.
      + gdal_translate -of PNG -outsize 512 512 frmt_wms_openstreetmap_tms.xml openstreetmap.png +

    • + +
    • + MetaCarta TMS Layer Example, accessing the default MetaCarta TMS layer.
      + gdal_translate -of PNG -outsize 512 256 frmt_wms_metacarta_tms.xml metacarta.png +

    • + +
    • + BlueMarble Amazon S3 Example accessed with the TMS minidriver. +

    • + +
    • + Google Maps accessed with the TMS minidriver. +

    • + +
    • + ArcGIS MapServer Tiles accessed with the TMS minidriver. +

    • + +
    • + OnEarth Tiled WMS Clementine, + daily, and + srtm examples.

      +

    • + +
    • + VirtualEarth Aerial Layer accessed with the VirtualEarth minidriver. +

    • + +
    • + ArcGIS online sample server layer accessed with the ArcGIS Server REST API minidriver. +

    • + +
    + +

    Open syntax

    + +The WMS driver can open : +
      +
    • +a local service description XML file :

      gdalinfo description_file.xml
      +

    • + +
    • +the content of a description XML file provided as filename : +

      gdalinfo "<GDAL_WMS><Service name=\"TiledWMS\"><ServerUrl>http://onearth.jpl.nasa.gov/wms.cgi?</ServerUrl><TiledGroupName>Global SRTM Elevation</TiledGroupName></Service></GDAL_WMS>"
      +

    • + +
    • +(GDAL >= 1.9.0) the base URL of a WMS service, prefixed with WMS: : +

      gdalinfo "WMS:http://wms.geobase.ca/wms-bin/cubeserv.cgi"

      +A list of subdatasets will be returned, resulting from the parsing of the GetCapabilities request on that server. +

    • + +
    • +(GDAL >= 1.9.0) a pseudo GetMap request, such as the subdataset name returned by the previous syntax : +

      gdalinfo "WMS:http://wms.geobase.ca/wms-bin/cubeserv.cgi?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&LAYERS=DNEC_250K%3AELEVATION%2FELEVATION&SRS=EPSG:42304&BBOX=-3000000,-1500000,6000000,4500000"
      +

    • + +
    • +(GDAL >= 1.9.0) the base URL of a Tiled WMS service, prefixed with WMS: and with request=GetTileService as GET argument: +

      gdalinfo "WMS:http://onearth.jpl.nasa.gov/wms.cgi?request=GetTileService"

      +A list of subdatasets will be returned, resulting from the parsing of the GetTileService request on that server. +

    • + +
    • +(GDAL >= 1.9.0) the URL of a REST definition for a ArcGIS MapServer: +

      gdalinfo "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer?f=json&pretty=true"

      +

    • +
    + +

    Generation of WMS service description XML file

    + +The WMS service description XML file can be generated manually, or created +as the output of the CreateCopy() operation of the WMS driver, only if the source +dataset is itself a WMS dataset. Said otherwise, you can use gdal_translate +with as source dataset any of the above syntax mentioned in "Open syntax" and +as output an XML file. + +For example: +
    +gdal_translate "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer?f=json" wms.xml -of WMS
    +
    + +The generated file will come with default values that you may need to edit. + +

    See Also:

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_wms_arcgis_mapserver_tms.xml b/bazaar/plugin/gdal/frmts/wms/frmt_wms_arcgis_mapserver_tms.xml new file mode 100644 index 000000000..dd4d9d3dc --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_wms_arcgis_mapserver_tms.xml @@ -0,0 +1,21 @@ + + + http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/${z}/${y}/${x}> + + + -20037508.34 + 20037508.34 + 20037508.34 + -20037508.34 + 17 + 1 + 1 + top + + EPSG:900913 + 256 + 256 + 3 + 10 + + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_wms_bluemarble_s3_tms.xml b/bazaar/plugin/gdal/frmts/wms/frmt_wms_bluemarble_s3_tms.xml new file mode 100644 index 000000000..eea12a4a7 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_wms_bluemarble_s3_tms.xml @@ -0,0 +1,20 @@ + + + http://s3.amazonaws.com/com.modestmaps.bluemarble/${z}-r${y}-c${x}.jpg + + + -20037508.34 + 20037508.34 + 20037508.34 + -20037508.34 + 9 + 1 + 1 + top + + EPSG:900913 + 256 + 256 + 3 + + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_wms_googlemaps_tms.xml b/bazaar/plugin/gdal/frmts/wms/frmt_wms_googlemaps_tms.xml new file mode 100644 index 000000000..64b2fd273 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_wms_googlemaps_tms.xml @@ -0,0 +1,29 @@ + + + + + + http://mt.google.com/vt/lyrs=m&x=${x}&y=${y}&z=${z} + + + + + + + -20037508.34 + 20037508.34 + 20037508.34 + -20037508.34 + 20 + 1 + 1 + top + + EPSG:900913 + 256 + 256 + 3 + 5 + + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_wms_metacarta_tms.xml b/bazaar/plugin/gdal/frmts/wms/frmt_wms_metacarta_tms.xml new file mode 100644 index 000000000..81c50e7de --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_wms_metacarta_tms.xml @@ -0,0 +1,20 @@ + + + http://tilecache.osgeo.org/wms-c/Basic.py + basic + png + + + -180.0 + 90.0 + 180.0 + -90.0 + 19 + 2 + 1 + + EPSG:4326 + 256 + 256 + 3 + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_wms_metacarta_wmsc.xml b/bazaar/plugin/gdal/frmts/wms/frmt_wms_metacarta_wmsc.xml new file mode 100644 index 000000000..ebb27369a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_wms_metacarta_wmsc.xml @@ -0,0 +1,20 @@ + + + 1 + http://tilecache.osgeo.org/wms-c/Basic.py? + basic + + + -180.0 + 90.0 + 180.0 + -90.0 + 19 + 2 + 1 + + EPSG:4326 + 256 + 256 + 3 + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_wms_onearth_global_mosaic.xml b/bazaar/plugin/gdal/frmts/wms/frmt_wms_onearth_global_mosaic.xml new file mode 100644 index 000000000..efaf5a9fd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_wms_onearth_global_mosaic.xml @@ -0,0 +1,24 @@ + + + 1.1.1 + http://onearth.jpl.nasa.gov/wms.cgi? + EPSG:4326 + image/jpeg + global_mosaic + visual + + + -180.0 + 90.0 + 180.0 + -90.0 + 2949120 + 1474560 + + 12 + 512 + 512 + EPSG:4326 + 3 + false + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_wms_openstreetmap_tms.xml b/bazaar/plugin/gdal/frmts/wms/frmt_wms_openstreetmap_tms.xml new file mode 100644 index 000000000..c236e0e57 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_wms_openstreetmap_tms.xml @@ -0,0 +1,20 @@ + + + http://tile.openstreetmap.org/${z}/${x}/${y}.png + + + -20037508.34 + 20037508.34 + 20037508.34 + -20037508.34 + 18 + 1 + 1 + top + + EPSG:3857 + 256 + 256 + 3 + + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_wms_tileservice_bmng.xml b/bazaar/plugin/gdal/frmts/wms/frmt_wms_tileservice_bmng.xml new file mode 100644 index 000000000..9898165bd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_wms_tileservice_bmng.xml @@ -0,0 +1,20 @@ + + + 1 + http://s0.tileservice.worldwindcentral.com/getTile? + bmng.topo.bathy.200401 + + + -180.0 + 90.0 + 180.0 + -90.0 + 65536 + 32768 + 7 + + EPSG:4326 + 512 + 512 + 3 + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_wms_tileservice_nysdop2004.xml b/bazaar/plugin/gdal/frmts/wms/frmt_wms_tileservice_nysdop2004.xml new file mode 100644 index 000000000..bd6030b1c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_wms_tileservice_nysdop2004.xml @@ -0,0 +1,25 @@ + + + 1 + http://s0.tileservice.worldwindcentral.com/getTile? + us.newyork_nysdop2004_0.15m + + + -74.53125 + 41.484375 + -73.125 + 40.078125 + 1048576 + 1048576 + 153600 + 70656 + 19 + 2 + 2 + + EPSG:4326 + 512 + 512 + 3 + 10 + diff --git a/bazaar/plugin/gdal/frmts/wms/frmt_wms_virtualearth.xml b/bazaar/plugin/gdal/frmts/wms/frmt_wms_virtualearth.xml new file mode 100644 index 000000000..0e566bb3e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/frmt_wms_virtualearth.xml @@ -0,0 +1,7 @@ + + + http://a${server_num}.ortho.tiles.virtualearth.net/tiles/a${quadkey}.jpeg?g=90 + + 4 + + diff --git a/bazaar/plugin/gdal/frmts/wms/gdalhttp.cpp b/bazaar/plugin/gdal/frmts/wms/gdalhttp.cpp new file mode 100644 index 000000000..71076c2db --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/gdalhttp.cpp @@ -0,0 +1,251 @@ +/****************************************************************************** + * $Id: gdalhttp.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" + +void CPLHTTPSetOptions(CURL *http_handle, char** papszOptions); + +/* CURLINFO_RESPONSE_CODE was known as CURLINFO_HTTP_CODE in libcurl 7.10.7 and earlier */ +#if LIBCURL_VERSION_NUM < 0x070a07 +#define CURLINFO_RESPONSE_CODE CURLINFO_HTTP_CODE +#endif + +static size_t CPLHTTPWriteFunc(void *buffer, size_t count, size_t nmemb, void *req) { + CPLHTTPRequest *psRequest = reinterpret_cast(req); + size_t size = count * nmemb; + + if (size == 0) return 0; + + const size_t required_size = psRequest->nDataLen + size + 1; + if (required_size > psRequest->nDataAlloc) { + size_t new_size = required_size * 2; + if (new_size < 512) new_size = 512; + psRequest->nDataAlloc = new_size; + GByte * pabyNewData = reinterpret_cast(VSIRealloc(psRequest->pabyData, new_size)); + if (pabyNewData == NULL) { + VSIFree(psRequest->pabyData); + psRequest->pabyData = NULL; + psRequest->pszError = CPLStrdup(CPLString().Printf("Out of memory allocating %u bytes for HTTP data buffer.", static_cast(new_size))); + psRequest->nDataAlloc = 0; + psRequest->nDataLen = 0; + return 0; + } + psRequest->pabyData = pabyNewData; + } + memcpy(psRequest->pabyData + psRequest->nDataLen, buffer, size); + psRequest->nDataLen += size; + psRequest->pabyData[psRequest->nDataLen] = 0; + return nmemb; +} + +void CPLHTTPInitializeRequest(CPLHTTPRequest *psRequest, const char *pszURL, const char *const *papszOptions) { + psRequest->pszURL = CPLStrdup(pszURL); + psRequest->papszOptions = CSLDuplicate(const_cast(papszOptions)); + psRequest->nStatus = 0; + psRequest->pszContentType = 0; + psRequest->pszError = 0; + psRequest->pabyData = 0; + psRequest->nDataLen = 0; + psRequest->nDataAlloc = 0; + psRequest->m_curl_handle = 0; + psRequest->m_headers = 0; + psRequest->m_curl_error = 0; + + psRequest->m_curl_handle = curl_easy_init(); + if (psRequest->m_curl_handle == NULL) { + CPLError(CE_Fatal, CPLE_AppDefined, "CPLHTTPInitializeRequest(): Unable to create CURL handle."); + } + + char** papszOptionsDup = CSLDuplicate(const_cast(psRequest->papszOptions)); + + /* Set User-Agent */ + const char *pszUserAgent = CSLFetchNameValue(papszOptionsDup, "USERAGENT"); + if (pszUserAgent == NULL) + papszOptionsDup = CSLAddNameValue(papszOptionsDup, "USERAGENT", + "GDAL WMS driver (http://www.gdal.org/frmt_wms.html)"); + + /* Set URL */ + curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_URL, psRequest->pszURL); + + /* Set Headers (copied&pasted from cpl_http.cpp, but unused by callers of CPLHTTPInitializeRequest) .*/ + const char *headers = CSLFetchNameValue(const_cast(psRequest->papszOptions), "HEADERS"); + if (headers != NULL) { + psRequest->m_headers = curl_slist_append(psRequest->m_headers, headers); + curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_HTTPHEADER, psRequest->m_headers); + } + + curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_WRITEDATA, psRequest); + curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_WRITEFUNCTION, CPLHTTPWriteFunc); + + psRequest->m_curl_error = reinterpret_cast(CPLMalloc(CURL_ERROR_SIZE + 1)); + psRequest->m_curl_error[0] = '\0'; + curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_ERRORBUFFER, psRequest->m_curl_error); + + CPLHTTPSetOptions(psRequest->m_curl_handle, papszOptionsDup); + + CSLDestroy(papszOptionsDup); +} + +void CPLHTTPCleanupRequest(CPLHTTPRequest *psRequest) { + if (psRequest->m_curl_handle) { + curl_easy_cleanup(psRequest->m_curl_handle); + psRequest->m_curl_handle = 0; + } + if (psRequest->m_headers) { + curl_slist_free_all(psRequest->m_headers); + psRequest->m_headers = 0; + } + if (psRequest->m_curl_error) { + CPLFree(psRequest->m_curl_error); + psRequest->m_curl_error = 0; + } + + if (psRequest->pszContentType) { + CPLFree(psRequest->pszContentType); + psRequest->pszContentType = 0; + } + if (psRequest->pszError) { + CPLFree(psRequest->pszError); + psRequest->pszError = 0; + } + if (psRequest->pabyData) { + CPLFree(psRequest->pabyData); + psRequest->pabyData = 0; + psRequest->nDataLen = 0; + psRequest->nDataAlloc = 0; + } + if (psRequest->papszOptions) { + CSLDestroy(psRequest->papszOptions); + psRequest->papszOptions = 0; + } + if (psRequest->pszURL) { + CPLFree(psRequest->pszURL); + psRequest->pszURL = 0; + } +} + +CPLErr CPLHTTPFetchMulti(CPLHTTPRequest *pasRequest, int nRequestCount, const char *const *papszOptions) { + CPLErr ret = CE_None; + CURLM *curl_multi = 0; + int still_running; + int max_conn; + int i, conn_i; + + const char *max_conn_opt = CSLFetchNameValue(const_cast(papszOptions), "MAXCONN"); + if (max_conn_opt && (max_conn_opt[0] != '\0')) { + max_conn = MAX(1, MIN(atoi(max_conn_opt), 1000)); + } else { + max_conn = 5; + } + + curl_multi = curl_multi_init(); + if (curl_multi == NULL) { + CPLError(CE_Fatal, CPLE_AppDefined, "CPLHTTPFetchMulti(): Unable to create CURL multi-handle."); + } + + // add at most max_conn requests + for (conn_i = 0; conn_i < MIN(nRequestCount, max_conn); ++conn_i) { + CPLHTTPRequest *const psRequest = &pasRequest[conn_i]; + CPLDebug("HTTP", "Requesting [%d/%d] %s", conn_i + 1, nRequestCount, pasRequest[conn_i].pszURL); + curl_multi_add_handle(curl_multi, psRequest->m_curl_handle); + } + + while (curl_multi_perform(curl_multi, &still_running) == CURLM_CALL_MULTI_PERFORM); + while (still_running || (conn_i != nRequestCount)) { + struct timeval timeout; + fd_set fdread, fdwrite, fdexcep; + int maxfd; + CURLMsg *msg; + int msgs_in_queue; + + do { + msg = curl_multi_info_read(curl_multi, &msgs_in_queue); + if (msg != NULL) { + if (msg->msg == CURLMSG_DONE) { // transfer completed, check if we have more waiting and add them + if (conn_i < nRequestCount) { + CPLHTTPRequest *const psRequest = &pasRequest[conn_i]; + CPLDebug("HTTP", "Requesting [%d/%d] %s", conn_i + 1, nRequestCount, pasRequest[conn_i].pszURL); + curl_multi_add_handle(curl_multi, psRequest->m_curl_handle); + ++conn_i; + } + } + } + } while (msg != NULL); + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + curl_multi_fdset(curl_multi, &fdread, &fdwrite, &fdexcep, &maxfd); + if( maxfd >= 0 ) + { + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); + } + while (curl_multi_perform(curl_multi, &still_running) == CURLM_CALL_MULTI_PERFORM); + } + + if (conn_i != nRequestCount) { // something gone really really wrong + CPLError(CE_Fatal, CPLE_AppDefined, "CPLHTTPFetchMulti(): conn_i != nRequestCount, this should never happen ..."); + } + for (i = 0; i < nRequestCount; ++i) { + CPLHTTPRequest *const psRequest = &pasRequest[i]; + + long response_code = 0; + curl_easy_getinfo(psRequest->m_curl_handle, CURLINFO_RESPONSE_CODE, &response_code); + psRequest->nStatus = response_code; + + char *content_type = 0; + curl_easy_getinfo(psRequest->m_curl_handle, CURLINFO_CONTENT_TYPE, &content_type); + if (content_type) psRequest->pszContentType = CPLStrdup(content_type); + + if ((psRequest->pszError == NULL) && (psRequest->m_curl_error != NULL) && (psRequest->m_curl_error[0] != '\0')) { + psRequest->pszError = CPLStrdup(psRequest->m_curl_error); + } + + /* In the case of a file:// URL, curl will return a status == 0, so if there's no */ + /* error returned, patch the status code to be 200, as it would be for http:// */ + if (strncmp(psRequest->pszURL, "file://", 7) == 0 && psRequest->nStatus == 0 && + psRequest->pszError == NULL) + { + psRequest->nStatus = 200; + } + + CPLDebug("HTTP", "Request [%d] %s : status = %d, content type = %s, error = %s", + i, psRequest->pszURL, psRequest->nStatus, + (psRequest->pszContentType) ? psRequest->pszContentType : "(null)", + (psRequest->pszError) ? psRequest->pszError : "(null)"); + + curl_multi_remove_handle(curl_multi, pasRequest[i].m_curl_handle); + } + curl_multi_cleanup(curl_multi); + + return ret; +} diff --git a/bazaar/plugin/gdal/frmts/wms/gdalhttp.h b/bazaar/plugin/gdal/frmts/wms/gdalhttp.h new file mode 100644 index 000000000..6c4ed71c4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/gdalhttp.h @@ -0,0 +1,56 @@ +/***************************************************************************** + * $Id: gdalhttp.h 18020 2009-11-14 14:33:20Z rouault $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +typedef struct { + /* Input */ + char *pszURL; + char **papszOptions; + + /* Output */ + int nStatus; /* 200 = success, 404 = not found, 0 = no response / error */ + char *pszContentType; + char *pszError; + + GByte *pabyData; + size_t nDataLen; + size_t nDataAlloc; + + // int nMimePartCount; + // CPLMimePart *pasMimePart; + + /* Internal stuff */ + CURL *m_curl_handle; + struct curl_slist *m_headers; + char *m_curl_error; +} CPLHTTPRequest; + +void CPL_DLL CPLHTTPInitializeRequest(CPLHTTPRequest *psRequest, const char *pszURL = 0, const char *const *papszOptions = 0); +void CPL_DLL CPLHTTPCleanupRequest(CPLHTTPRequest *psRequest); +CPLErr CPL_DLL CPLHTTPFetchMulti(CPLHTTPRequest *pasRequest, int nRequestCount = 1, const char *const *papszOptions = 0); diff --git a/bazaar/plugin/gdal/frmts/wms/gdalwmscache.cpp b/bazaar/plugin/gdal/frmts/wms/gdalwmscache.cpp new file mode 100644 index 000000000..20026bebb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/gdalwmscache.cpp @@ -0,0 +1,108 @@ +/****************************************************************************** + * $Id: gdalwmscache.cpp 27554 2014-08-02 17:46:37Z rouault $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" + +GDALWMSCache::GDALWMSCache() { + m_cache_path = "./gdalwmscache"; + m_postfix = ""; + m_cache_depth = 2; +} + +GDALWMSCache::~GDALWMSCache() { +} + +CPLErr GDALWMSCache::Initialize(CPLXMLNode *config) { + const char *xmlcache_path = CPLGetXMLValue(config, "Path", NULL); + const char *usercache_path = CPLGetConfigOption("GDAL_DEFAULT_WMS_CACHE_PATH", NULL); + if(xmlcache_path) + { + m_cache_path = xmlcache_path; + } + else + { + if(usercache_path) + { + m_cache_path = usercache_path; + } + else + { + m_cache_path = "./gdalwmscache"; + } + } + + const char *cache_depth = CPLGetXMLValue(config, "Depth", "2"); + m_cache_depth = atoi(cache_depth); + + const char *cache_extension = CPLGetXMLValue(config, "Extension", ""); + m_postfix = cache_extension; + + return CE_None; +} + +CPLErr GDALWMSCache::Write(const char *key, const CPLString &file_name) { + CPLString cache_file(KeyToCacheFile(key)); + // printf("GDALWMSCache::Write(%s, %s) -> %s\n", key, file_name.c_str()); + if (CPLCopyFile(cache_file.c_str(), file_name.c_str()) != CE_None) { + MakeDirs(cache_file.c_str()); + CPLCopyFile(cache_file.c_str(), file_name.c_str()); + } + + return CE_None; +} + +CPLErr GDALWMSCache::Read(const char *key, CPLString *file_name) { + CPLErr ret = CE_Failure; + CPLString cache_file(KeyToCacheFile(key)); + VSILFILE* fp = VSIFOpenL(cache_file.c_str(), "rb"); + if (fp != NULL) + { + VSIFCloseL(fp); + *file_name = cache_file; + ret = CE_None; + } + // printf("GDALWMSCache::Read(...) -> %s\n", cache_file.c_str()); + + return ret; +} + +CPLString GDALWMSCache::KeyToCacheFile(const char *key) { + CPLString hash(MD5String(key)); + CPLString cache_file(m_cache_path); + + if (cache_file.size() && (cache_file[cache_file.size() - 1] != '/')) cache_file.append(1, '/'); + for (int i = 0; i < m_cache_depth; ++i) { + cache_file.append(1, hash[i]); + cache_file.append(1, '/'); + } + cache_file.append(hash); + cache_file.append(m_postfix); + return cache_file; +} diff --git a/bazaar/plugin/gdal/frmts/wms/gdalwmsdataset.cpp b/bazaar/plugin/gdal/frmts/wms/gdalwmsdataset.cpp new file mode 100644 index 000000000..ff95daaa1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/gdalwmsdataset.cpp @@ -0,0 +1,647 @@ +/****************************************************************************** + * $Id: gdalwmsdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + **************************************************************************** + * + * dataset.cpp: + * Initialization of the GDALWMSdriver, parsing the XML configuration file, + * instantiation of the minidrivers and accessors used by minidrivers + * + ***************************************************************************/ + + +#include "wmsdriver.h" + +#include "minidriver_wms.h" +#include "minidriver_tileservice.h" +#include "minidriver_worldwind.h" +#include "minidriver_tms.h" +#include "minidriver_tiled_wms.h" +#include "minidriver_virtualearth.h" + +/************************************************************************/ +/* GDALWMSDataset() */ +/************************************************************************/ +GDALWMSDataset::GDALWMSDataset() { + m_mini_driver = 0; + m_cache = 0; + m_hint.m_valid = false; + m_data_type = GDT_Byte; + m_clamp_requests = true; + m_unsafeSsl = false; + m_data_window.m_sx = -1; + nBands = 0; + m_default_block_size_x = 1024; + m_default_block_size_y = 1024; + m_bNeedsDataWindow = TRUE; + m_default_tile_count_x = 1; + m_default_tile_count_y = 1; + m_default_overview_count = -1; + m_zeroblock_on_serverexceptions = 0; + m_poColorTable = NULL; +} + +/************************************************************************/ +/* ~GDALWMSDataset() */ +/************************************************************************/ +GDALWMSDataset::~GDALWMSDataset() { + if (m_mini_driver) delete m_mini_driver; + if (m_cache) delete m_cache; + if (m_poColorTable) delete m_poColorTable; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ +CPLErr GDALWMSDataset::Initialize(CPLXMLNode *config) { + CPLErr ret = CE_None; + + char* pszXML = CPLSerializeXMLTree( config ); + if (pszXML) + { + m_osXML = pszXML; + CPLFree(pszXML); + } + + // Initialize the minidriver, which can set parameters for the dataset using member functions + CPLXMLNode *service_node = CPLGetXMLNode(config, "Service"); + if (service_node != NULL) + { + const CPLString service_name = CPLGetXMLValue(service_node, "name", ""); + if (!service_name.empty()) + { + GDALWMSMiniDriverManager *const mdm = GetGDALWMSMiniDriverManager(); + GDALWMSMiniDriverFactory *const mdf = mdm->Find(service_name); + if (mdf != NULL) + { + m_mini_driver = mdf->New(); + m_mini_driver->m_parent_dataset = this; + if (m_mini_driver->Initialize(service_node) == CE_None) + { + m_mini_driver_caps.m_capabilities_version = -1; + m_mini_driver->GetCapabilities(&m_mini_driver_caps); + if (m_mini_driver_caps.m_capabilities_version == -1) + { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Internal error, mini-driver capabilities version not set."); + ret = CE_Failure; + } + } + else + { + delete m_mini_driver; + m_mini_driver = NULL; + + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Failed to initialize minidriver."); + ret = CE_Failure; + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "GDALWMS: No mini-driver registered for '%s'.", service_name.c_str()); + ret = CE_Failure; + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: No Service specified."); + ret = CE_Failure; + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: No Service specified."); + ret = CE_Failure; + } + + + /* + Parameters that could be set by minidriver already, based on server side information. + If the size is set, minidriver has done this already + A "server" side minidriver needs to set at least: + - Blocksize (x and y) + - Clamp flag (defaults to true) + - DataWindow + - Band Count + - Data Type + It should also initialize and register the bands and overviews. + */ + + if (m_data_window.m_sx<1) + { + int nOverviews = 0; + + if (ret == CE_None) + { + m_block_size_x = atoi(CPLGetXMLValue(config, "BlockSizeX", CPLString().Printf("%d", m_default_block_size_x))); + m_block_size_y = atoi(CPLGetXMLValue(config, "BlockSizeY", CPLString().Printf("%d", m_default_block_size_y))); + if (m_block_size_x <= 0 || m_block_size_y <= 0) + { + CPLError( CE_Failure, CPLE_AppDefined, "GDALWMS: Invalid value in BlockSizeX or BlockSizeY" ); + ret = CE_Failure; + } + } + + if (ret == CE_None) + { + m_clamp_requests = StrToBool(CPLGetXMLValue(config, "ClampRequests", "true")); + if (m_clamp_requests<0) + { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Invalid value of ClampRequests, true/false expected."); + ret = CE_Failure; + } + } + + if (ret == CE_None) + { + CPLXMLNode *data_window_node = CPLGetXMLNode(config, "DataWindow"); + if (data_window_node == NULL && m_bNeedsDataWindow) + { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: DataWindow missing."); + ret = CE_Failure; + } + else + { + CPLString osDefaultX0, osDefaultX1, osDefaultY0, osDefaultY1; + CPLString osDefaultTileCountX, osDefaultTileCountY, osDefaultTileLevel; + CPLString osDefaultOverviewCount; + osDefaultX0.Printf("%.8f", m_default_data_window.m_x0); + osDefaultX1.Printf("%.8f", m_default_data_window.m_x1); + osDefaultY0.Printf("%.8f", m_default_data_window.m_y0); + osDefaultY1.Printf("%.8f", m_default_data_window.m_y1); + osDefaultTileCountX.Printf("%d", m_default_tile_count_x); + osDefaultTileCountY.Printf("%d", m_default_tile_count_y); + if (m_default_data_window.m_tlevel >= 0) + osDefaultTileLevel.Printf("%d", m_default_data_window.m_tlevel); + if (m_default_overview_count >= 0) + osDefaultOverviewCount.Printf("%d", m_default_overview_count); + const char *overview_count = CPLGetXMLValue(config, "OverviewCount", osDefaultOverviewCount); + const char *ulx = CPLGetXMLValue(data_window_node, "UpperLeftX", osDefaultX0); + const char *uly = CPLGetXMLValue(data_window_node, "UpperLeftY", osDefaultY0); + const char *lrx = CPLGetXMLValue(data_window_node, "LowerRightX", osDefaultX1); + const char *lry = CPLGetXMLValue(data_window_node, "LowerRightY", osDefaultY1); + const char *sx = CPLGetXMLValue(data_window_node, "SizeX", ""); + const char *sy = CPLGetXMLValue(data_window_node, "SizeY", ""); + const char *tx = CPLGetXMLValue(data_window_node, "TileX", "0"); + const char *ty = CPLGetXMLValue(data_window_node, "TileY", "0"); + const char *tlevel = CPLGetXMLValue(data_window_node, "TileLevel", osDefaultTileLevel); + const char *str_tile_count_x = CPLGetXMLValue(data_window_node, "TileCountX", osDefaultTileCountX); + const char *str_tile_count_y = CPLGetXMLValue(data_window_node, "TileCountY", osDefaultTileCountY); + const char *y_origin = CPLGetXMLValue(data_window_node, "YOrigin", "default"); + + if (ret == CE_None) + { + if ((ulx[0] != '\0') && (uly[0] != '\0') && (lrx[0] != '\0') && (lry[0] != '\0')) + { + m_data_window.m_x0 = CPLAtof(ulx); + m_data_window.m_y0 = CPLAtof(uly); + m_data_window.m_x1 = CPLAtof(lrx); + m_data_window.m_y1 = CPLAtof(lry); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "GDALWMS: Mandatory elements of DataWindow missing: UpperLeftX, UpperLeftY, LowerRightX, LowerRightY."); + ret = CE_Failure; + } + } + + m_data_window.m_tlevel = atoi(tlevel); + + if (ret == CE_None) + { + if ((sx[0] != '\0') && (sy[0] != '\0')) + { + m_data_window.m_sx = atoi(sx); + m_data_window.m_sy = atoi(sy); + } + else if ((tlevel[0] != '\0') && (str_tile_count_x[0] != '\0') && (str_tile_count_y[0] != '\0')) + { + int tile_count_x = atoi(str_tile_count_x); + int tile_count_y = atoi(str_tile_count_y); + m_data_window.m_sx = tile_count_x * m_block_size_x * (1 << m_data_window.m_tlevel); + m_data_window.m_sy = tile_count_y * m_block_size_y * (1 << m_data_window.m_tlevel); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "GDALWMS: Mandatory elements of DataWindow missing: SizeX, SizeY."); + ret = CE_Failure; + } + } + if (ret == CE_None) + { + if ((tx[0] != '\0') && (ty[0] != '\0')) + { + m_data_window.m_tx = atoi(tx); + m_data_window.m_ty = atoi(ty); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "GDALWMS: Mandatory elements of DataWindow missing: TileX, TileY."); + ret = CE_Failure; + } + } + + if (ret == CE_None) + { + if (overview_count[0] != '\0') + { + nOverviews = atoi(overview_count); + } + else if (tlevel[0] != '\0') + { + nOverviews = m_data_window.m_tlevel; + } + else + { + const int min_overview_size = MAX(32, MIN(m_block_size_x, m_block_size_y)); + double a = log(static_cast(MIN(m_data_window.m_sx, m_data_window.m_sy))) / log(2.0) + - log(static_cast(min_overview_size)) / log(2.0); + nOverviews = MAX(0, MIN(static_cast(ceil(a)), 32)); + } + } + if (ret == CE_None) + { + CPLString y_origin_str = y_origin; + if (y_origin_str == "top") { + m_data_window.m_y_origin = GDALWMSDataWindow::TOP; + } else if (y_origin_str == "bottom") { + m_data_window.m_y_origin = GDALWMSDataWindow::BOTTOM; + } else if (y_origin_str == "default") { + m_data_window.m_y_origin = GDALWMSDataWindow::DEFAULT; + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: DataWindow YOrigin must be set to " + "one of 'default', 'top', or 'bottom', not '%s'.", y_origin_str.c_str()); + ret = CE_Failure; + } + } + } + } + + if (ret == CE_None) + { + if (nBands<1) + nBands=atoi(CPLGetXMLValue(config,"BandsCount","3")); + if (nBands<1) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GDALWMS: Bad number of bands."); + ret = CE_Failure; + } + } + + if (ret == CE_None) + { + const char *data_type = CPLGetXMLValue(config, "DataType", "Byte"); + m_data_type = GDALGetDataTypeByName( data_type ); + if ( m_data_type == GDT_Unknown || m_data_type >= GDT_TypeCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GDALWMS: Invalid value in DataType. Data type \"%s\" is not supported.", data_type ); + ret = CE_Failure; + } + } + + // Initialize the bands and the overviews. Assumes overviews are powers of two + if (ret == CE_None) + { + nRasterXSize = m_data_window.m_sx; + nRasterYSize = m_data_window.m_sy; + + if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize) || + !GDALCheckBandCount(nBands, TRUE)) + { + return CE_Failure; + } + + GDALColorInterp default_color_interp[4][4] = { + { GCI_GrayIndex, GCI_Undefined, GCI_Undefined, GCI_Undefined }, + { GCI_GrayIndex, GCI_AlphaBand, GCI_Undefined, GCI_Undefined }, + { GCI_RedBand, GCI_GreenBand, GCI_BlueBand, GCI_Undefined }, + { GCI_RedBand, GCI_GreenBand, GCI_BlueBand, GCI_AlphaBand } + }; + for (int i = 0; i < nBands; ++i) + { + GDALColorInterp color_interp = (nBands <= 4 && i <= 3 ? default_color_interp[nBands - 1][i] : GCI_Undefined); + GDALWMSRasterBand *band = new GDALWMSRasterBand(this, i, 1.0); + band->m_color_interp = color_interp; + SetBand(i + 1, band); + double scale = 0.5; + for (int j = 0; j < nOverviews; ++j) + { + band->AddOverview(scale); + band->m_color_interp = color_interp; + scale *= 0.5; + } + } + } + } + + // UserPwd + const char *pszUserPwd = CPLGetXMLValue(config, "UserPwd", ""); + if (pszUserPwd[0] != '\0') + m_osUserPwd = pszUserPwd; + + const char *pszUserAgent = CPLGetXMLValue(config, "UserAgent", ""); + if (pszUserAgent[0] != '\0') + m_osUserAgent = pszUserAgent; + + const char *pszReferer = CPLGetXMLValue(config, "Referer", ""); + if (pszReferer[0] != '\0') + m_osReferer = pszReferer; + + if (ret == CE_None) { + const char *pszHttpZeroBlockCodes = CPLGetXMLValue(config, "ZeroBlockHttpCodes", ""); + if(pszHttpZeroBlockCodes[0] == '\0') { + m_http_zeroblock_codes.push_back(204); + } else { + char **kv = CSLTokenizeString2(pszHttpZeroBlockCodes,",",CSLT_HONOURSTRINGS); + int nCount = CSLCount(kv); + for(int i=0; iInitialize(cache_node) != CE_None) { + delete m_cache; + m_cache = NULL; + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Failed to initialize cache."); + ret = CE_Failure; + } + } + } + + if (ret == CE_None) { + const int v = StrToBool(CPLGetXMLValue(config, "UnsafeSSL", "false")); + if (v == -1) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Invalid value of UnsafeSSL: true or false expected."); + ret = CE_Failure; + } else { + m_unsafeSsl = v; + } + } + + if (ret == CE_None) { + /* If we dont have projection already set ask mini-driver. */ + if (!m_projection.size()) { + const char *proj = m_mini_driver->GetProjectionInWKT(); + if (proj != NULL) { + m_projection = proj; + } + } + } + + return ret; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ +CPLErr GDALWMSDataset::IRasterIO(GDALRWFlag rw, int x0, int y0, int sx, int sy, + void *buffer, int bsx, int bsy, GDALDataType bdt, + int band_count, int *band_map, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) { + CPLErr ret; + + if (rw != GF_Read) return CE_Failure; + if (buffer == NULL) return CE_Failure; + if ((sx == 0) || (sy == 0) || (bsx == 0) || (bsy == 0) || (band_count == 0)) return CE_None; + + m_hint.m_x0 = x0; + m_hint.m_y0 = y0; + m_hint.m_sx = sx; + m_hint.m_sy = sy; + m_hint.m_overview = -1; + m_hint.m_valid = true; + // printf("[%p] GDALWMSDataset::IRasterIO(x0: %d, y0: %d, sx: %d, sy: %d, bsx: %d, bsy: %d, band_count: %d, band_map: %p)\n", this, x0, y0, sx, sy, bsx, bsy, band_count, band_map); + ret = GDALDataset::IRasterIO(rw, x0, y0, sx, sy, buffer, bsx, bsy, bdt, band_count, band_map, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg); + m_hint.m_valid = false; + + return ret; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ +const char *GDALWMSDataset::GetProjectionRef() { + return m_projection.c_str(); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ +CPLErr GDALWMSDataset::SetProjection(CPL_UNUSED const char *proj) { + return CE_Failure; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ +CPLErr GDALWMSDataset::GetGeoTransform(double *gt) { + gt[0] = m_data_window.m_x0; + gt[1] = (m_data_window.m_x1 - m_data_window.m_x0) / static_cast(m_data_window.m_sx); + gt[2] = 0.0; + gt[3] = m_data_window.m_y0; + gt[4] = 0.0; + gt[5] = (m_data_window.m_y1 - m_data_window.m_y0) / static_cast(m_data_window.m_sy); + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ +CPLErr GDALWMSDataset::SetGeoTransform(CPL_UNUSED double *gt) { + return CE_Failure; +} + +/************************************************************************/ +/* AdviseRead() */ +/************************************************************************/ +CPLErr GDALWMSDataset::AdviseRead(int x0, int y0, + int sx, int sy, + int bsx, int bsy, + GDALDataType bdt, + CPL_UNUSED int band_count, + CPL_UNUSED int *band_map, + char **options) { +// printf("AdviseRead(%d, %d, %d, %d)\n", x0, y0, sx, sy); + if (m_offline_mode || !m_use_advise_read) return CE_None; + if (m_cache == NULL) return CE_Failure; + + GDALRasterBand *band = GetRasterBand(1); + if (band == NULL) return CE_Failure; + return band->AdviseRead(x0, y0, sx, sy, bsx, bsy, bdt, options); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **GDALWMSDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "WMS", NULL); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ +const char *GDALWMSDataset::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if( pszName != NULL && EQUAL(pszName, "XML") && + pszDomain != NULL && EQUAL(pszDomain, "WMS") ) + { + return (m_osXML.size()) ? m_osXML.c_str() : NULL; + } + + return GDALPamDataset::GetMetadataItem(pszName, pszDomain); +} diff --git a/bazaar/plugin/gdal/frmts/wms/gdalwmsrasterband.cpp b/bazaar/plugin/gdal/frmts/wms/gdalwmsrasterband.cpp new file mode 100644 index 000000000..16d0156bb --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/gdalwmsrasterband.cpp @@ -0,0 +1,878 @@ +/****************************************************************************** + * $Id: gdalwmsrasterband.cpp 28562 2015-02-26 15:00:55Z rouault $ + * + * Project: WMS Client Driver + * Purpose: GDALWMSRasterBand implementation. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" + +GDALWMSRasterBand::GDALWMSRasterBand(GDALWMSDataset *parent_dataset, int band, double scale) { + // printf("[%p] GDALWMSRasterBand::GDALWMSRasterBand(%p, %d, %f)\n", this, parent_dataset, band, scale); + m_parent_dataset = parent_dataset; + m_scale = scale; + m_overview = -1; + m_color_interp = GCI_Undefined; + + if( scale == 1.0 ) + poDS = parent_dataset; + else + poDS = NULL; + nRasterXSize = static_cast(m_parent_dataset->m_data_window.m_sx * scale + 0.5); + nRasterYSize = static_cast(m_parent_dataset->m_data_window.m_sy * scale + 0.5); + nBand = band; + eDataType = m_parent_dataset->m_data_type; + nBlockXSize = m_parent_dataset->m_block_size_x; + nBlockYSize = m_parent_dataset->m_block_size_y; +} + +GDALWMSRasterBand::~GDALWMSRasterBand() { + for (std::vector::iterator it = m_overviews.begin(); it != m_overviews.end(); ++it) { + GDALWMSRasterBand *p = *it; + delete p; + } +} + +char** GDALWMSRasterBand::BuildHTTPRequestOpts() +{ + char **http_request_opts = NULL; + if (m_parent_dataset->m_http_timeout != -1) { + CPLString http_request_optstr; + http_request_optstr.Printf("TIMEOUT=%d", m_parent_dataset->m_http_timeout); + http_request_opts = CSLAddString(http_request_opts, http_request_optstr.c_str()); + } + + if (m_parent_dataset->m_osUserAgent.size() != 0) + { + CPLString osUserAgentOptStr("USERAGENT="); + osUserAgentOptStr += m_parent_dataset->m_osUserAgent; + http_request_opts = CSLAddString(http_request_opts, osUserAgentOptStr.c_str()); + } + if (m_parent_dataset->m_osReferer.size() != 0) + { + CPLString osRefererOptStr("REFERER="); + osRefererOptStr += m_parent_dataset->m_osReferer; + http_request_opts = CSLAddString(http_request_opts, osRefererOptStr.c_str()); + } + if (m_parent_dataset->m_unsafeSsl >= 1) { + http_request_opts = CSLAddString(http_request_opts, "UNSAFESSL=1"); + } + if (m_parent_dataset->m_osUserPwd.size() != 0) + { + CPLString osUserPwdOptStr("USERPWD="); + osUserPwdOptStr += m_parent_dataset->m_osUserPwd; + http_request_opts = CSLAddString(http_request_opts, osUserPwdOptStr.c_str()); + } + + return http_request_opts; +} + +CPLErr GDALWMSRasterBand::ReadBlocks(int x, int y, void *buffer, int bx0, int by0, int bx1, int by1, int advise_read) { + CPLErr ret = CE_None; + int i; + + int max_request_count = (bx1 - bx0 + 1) * (by1 - by0 + 1); + int request_count = 0; + CPLHTTPRequest *download_requests = NULL; + GDALWMSCache *cache = m_parent_dataset->m_cache; + struct BlockXY { + int x, y; + } *download_blocks = NULL; + if (!m_parent_dataset->m_offline_mode) { + download_requests = new CPLHTTPRequest[max_request_count]; + download_blocks = new BlockXY[max_request_count]; + } + + char **http_request_opts = BuildHTTPRequestOpts(); + + for (int iy = by0; iy <= by1; ++iy) { + for (int ix = bx0; ix <= bx1; ++ix) { + bool need_this_block = false; + if (!advise_read) { + for (int ib = 1; ib <= m_parent_dataset->nBands; ++ib) { + if ((ix == x) && (iy == y) && (ib == nBand)) { + need_this_block = true; + } else { + GDALWMSRasterBand *band = static_cast(m_parent_dataset->GetRasterBand(ib)); + if (m_overview >= 0) band = static_cast(band->GetOverview(m_overview)); + if (!band->IsBlockInCache(ix, iy)) need_this_block = true; + } + } + } else { + need_this_block = true; + } + CPLString url; + if (need_this_block) { + CPLString file_name; + AskMiniDriverForBlock(&url, ix, iy); + if ((cache != NULL) && (cache->Read(url.c_str(), &file_name) == CE_None)) { + if (advise_read) { + need_this_block = false; + } else { + void *p = 0; + if ((ix == x) && (iy == y)) p = buffer; + if (ReadBlockFromFile(ix, iy, file_name.c_str(), nBand, p, 0) == CE_None) need_this_block = false; + } + } + } + if (need_this_block) { + if (m_parent_dataset->m_offline_mode) { + if (!advise_read) { + void *p = 0; + if ((ix == x) && (iy == y)) p = buffer; + if (ZeroBlock(ix, iy, nBand, p) != CE_None) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: ZeroBlock failed."); + ret = CE_Failure; + } + } + } else { + CPLHTTPInitializeRequest(&download_requests[request_count], url.c_str(), http_request_opts); + download_blocks[request_count].x = ix; + download_blocks[request_count].y = iy; + ++request_count; + } + } + } + } + if (http_request_opts != NULL) { + CSLDestroy(http_request_opts); + } + + if (request_count > 0) { + char **opts = NULL; + CPLString optstr; + if (m_parent_dataset->m_http_max_conn != -1) { + optstr.Printf("MAXCONN=%d", m_parent_dataset->m_http_max_conn); + opts = CSLAddString(opts, optstr.c_str()); + } + if (CPLHTTPFetchMulti(download_requests, request_count, opts) != CE_None) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: CPLHTTPFetchMulti failed."); + ret = CE_Failure; + } + if (opts != NULL) { + CSLDestroy(opts); + } + } + + for (i = 0; i < request_count; ++i) { + if (ret == CE_None) { + if ((download_requests[i].nStatus == 200) && (download_requests[i].pabyData != NULL) && (download_requests[i].nDataLen > 0)) { + CPLString file_name(BufferToVSIFile(download_requests[i].pabyData, download_requests[i].nDataLen)); + if (file_name.size() > 0) { + bool wms_exception = false; + /* check for error xml */ + if (download_requests[i].nDataLen >= 20) { + const char *download_data = reinterpret_cast(download_requests[i].pabyData); + if (EQUALN(download_data, "m_verify_advise_read) { + if (cache != NULL) { + cache->Write(download_requests[i].pszURL, file_name); + } + } else { + void *p = 0; + if ((download_blocks[i].x == x) && (download_blocks[i].y == y)) p = buffer; + if (ReadBlockFromFile(download_blocks[i].x, download_blocks[i].y, file_name.c_str(), nBand, p, advise_read) == CE_None) { + if (cache != NULL) { + cache->Write(download_requests[i].pszURL, file_name); + } + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: ReadBlockFromFile (%s) failed.", + download_requests[i].pszURL); + ret = CE_Failure; + } + } + } else if( wms_exception && m_parent_dataset->m_zeroblock_on_serverexceptions ) { + void *p = 0; + if ((download_blocks[i].x == x) && (download_blocks[i].y == y)) p = buffer; + if (ZeroBlock(download_blocks[i].x, download_blocks[i].y, nBand, p) != CE_None) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: ZeroBlock failed."); + } else { + ret = CE_None; + } + + } + VSIUnlink(file_name.c_str()); + } + } else { + std::vector::iterator zero_it = std::find( + m_parent_dataset->m_http_zeroblock_codes.begin(), + m_parent_dataset->m_http_zeroblock_codes.end(), + download_requests[i].nStatus); + if ( zero_it != m_parent_dataset->m_http_zeroblock_codes.end() ) { + if (!advise_read) { + void *p = 0; + if ((download_blocks[i].x == x) && (download_blocks[i].y == y)) p = buffer; + if (ZeroBlock(download_blocks[i].x, download_blocks[i].y, nBand, p) != CE_None) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: ZeroBlock failed."); + ret = CE_Failure; + } + } + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Unable to download block %d, %d.\n URL: %s\n HTTP status code: %d, error: %s.\n" + "Add the HTTP status code to to ignore that error (see http://www.gdal.org/frmt_wms.html).", + download_blocks[i].x, download_blocks[i].y, download_requests[i].pszURL, download_requests[i].nStatus, + download_requests[i].pszError ? download_requests[i].pszError : "(null)"); + ret = CE_Failure; + } + } + } + CPLHTTPCleanupRequest(&download_requests[i]); + } + if (!m_parent_dataset->m_offline_mode) { + delete[] download_blocks; + delete[] download_requests; + } + + return ret; +} + +CPLErr GDALWMSRasterBand::IReadBlock(int x, int y, void *buffer) { + int bx0 = x; + int by0 = y; + int bx1 = x; + int by1 = y; + + if ((m_parent_dataset->m_hint.m_valid) && (m_parent_dataset->m_hint.m_overview == m_overview)) { + int tbx0 = m_parent_dataset->m_hint.m_x0 / nBlockXSize; + int tby0 = m_parent_dataset->m_hint.m_y0 / nBlockYSize; + int tbx1 = (m_parent_dataset->m_hint.m_x0 + m_parent_dataset->m_hint.m_sx - 1) / nBlockXSize; + int tby1 = (m_parent_dataset->m_hint.m_y0 + m_parent_dataset->m_hint.m_sy - 1) / nBlockYSize; + if ((tbx0 <= bx0) && (tby0 <= by0) && (tbx1 >= bx1) && (tby1 >= by1)) { + bx0 = tbx0; + by0 = tby0; + bx1 = tbx1; + by1 = tby1; + } + } + + CPLErr eErr = ReadBlocks(x, y, buffer, bx0, by0, bx1, by1, 0); + + if ((m_parent_dataset->m_hint.m_valid) && (m_parent_dataset->m_hint.m_overview == m_overview)) + { + m_parent_dataset->m_hint.m_valid = false; + } + + return eErr; +} + +CPLErr GDALWMSRasterBand::IRasterIO(GDALRWFlag rw, int x0, int y0, int sx, int sy, + void *buffer, int bsx, int bsy, GDALDataType bdt, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg) { + CPLErr ret; + + if (rw != GF_Read) return CE_Failure; + if (buffer == NULL) return CE_Failure; + if ((sx == 0) || (sy == 0) || (bsx == 0) || (bsy == 0)) return CE_None; + + m_parent_dataset->m_hint.m_x0 = x0; + m_parent_dataset->m_hint.m_y0 = y0; + m_parent_dataset->m_hint.m_sx = sx; + m_parent_dataset->m_hint.m_sy = sy; + m_parent_dataset->m_hint.m_overview = m_overview; + m_parent_dataset->m_hint.m_valid = true; + ret = GDALRasterBand::IRasterIO(rw, x0, y0, sx, sy, buffer, bsx, bsy, bdt, nPixelSpace, nLineSpace, psExtraArg); + m_parent_dataset->m_hint.m_valid = false; + + return ret; +} + +int GDALWMSRasterBand::HasArbitraryOverviews() { +// return m_parent_dataset->m_mini_driver_caps.m_has_arb_overviews; + return 0; // not implemented yet +} + +int GDALWMSRasterBand::GetOverviewCount() { + return m_overviews.size(); +} + +GDALRasterBand *GDALWMSRasterBand::GetOverview(int n) { + if ((m_overviews.size() > 0) && (static_cast(n) < m_overviews.size())) return m_overviews[n]; + else return NULL; +} + +void GDALWMSRasterBand::AddOverview(double scale) { + GDALWMSRasterBand *overview = new GDALWMSRasterBand(m_parent_dataset, nBand, scale); + std::vector::iterator it = m_overviews.begin(); + for (; it != m_overviews.end(); ++it) { + GDALWMSRasterBand *p = *it; + if (p->m_scale < scale) break; + } + m_overviews.insert(it, overview); + it = m_overviews.begin(); + for (int i = 0; it != m_overviews.end(); ++it, ++i) { + GDALWMSRasterBand *p = *it; + p->m_overview = i; + } +} + +bool GDALWMSRasterBand::IsBlockInCache(int x, int y) { + bool ret = false; + GDALRasterBlock *b = TryGetLockedBlockRef(x, y); + if (b != NULL) { + ret = true; + b->DropLock(); + } + return ret; +} + +// This is the function that calculates the block coordinates for the fetch +void GDALWMSRasterBand::AskMiniDriverForBlock(CPLString *url, int x, int y) +{ + GDALWMSImageRequestInfo iri; + GDALWMSTiledImageRequestInfo tiri; + + ComputeRequestInfo(iri, tiri, x, y); + + m_parent_dataset->m_mini_driver->TiledImageRequest(url, iri, tiri); +} + +void GDALWMSRasterBand::ComputeRequestInfo(GDALWMSImageRequestInfo &iri, + GDALWMSTiledImageRequestInfo &tiri, + int x, int y) +{ + int x0 = MAX(0, x * nBlockXSize); + int y0 = MAX(0, y * nBlockYSize); + int x1 = MAX(0, (x + 1) * nBlockXSize); + int y1 = MAX(0, (y + 1) * nBlockYSize); + if (m_parent_dataset->m_clamp_requests) { + x0 = MIN(x0, nRasterXSize); + y0 = MIN(y0, nRasterYSize); + x1 = MIN(x1, nRasterXSize); + y1 = MIN(y1, nRasterYSize); + } + + const double rx = (m_parent_dataset->m_data_window.m_x1 - m_parent_dataset->m_data_window.m_x0) / static_cast(nRasterXSize); + const double ry = (m_parent_dataset->m_data_window.m_y1 - m_parent_dataset->m_data_window.m_y0) / static_cast(nRasterYSize); + /* Use different method for x0,y0 and x1,y1 to make sure calculated values are exact for corner requests */ + iri.m_x0 = x0 * rx + m_parent_dataset->m_data_window.m_x0; + iri.m_y0 = y0 * ry + m_parent_dataset->m_data_window.m_y0; + iri.m_x1 = m_parent_dataset->m_data_window.m_x1 - (nRasterXSize - x1) * rx; + iri.m_y1 = m_parent_dataset->m_data_window.m_y1 - (nRasterYSize - y1) * ry; + iri.m_sx = x1 - x0; + iri.m_sy = y1 - y0; + + int level = m_overview + 1; + tiri.m_x = (m_parent_dataset->m_data_window.m_tx >> level) + x; + tiri.m_y = (m_parent_dataset->m_data_window.m_ty >> level) + y; + tiri.m_level = m_parent_dataset->m_data_window.m_tlevel - level; +} + + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **GDALWMSRasterBand::GetMetadataDomainList() +{ + return CSLAddString(GDALPamRasterBand::GetMetadataDomainList(), "LocationInfo"); +} + +const char *GDALWMSRasterBand::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ +/* ==================================================================== */ +/* LocationInfo handling. */ +/* ==================================================================== */ + if( pszDomain != NULL + && EQUAL(pszDomain,"LocationInfo") + && (EQUALN(pszName,"Pixel_",6) || EQUALN(pszName,"GeoPixel_",9)) ) + { + int iPixel, iLine; + +/* -------------------------------------------------------------------- */ +/* What pixel are we aiming at? */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszName,"Pixel_",6) ) + { + if( sscanf( pszName+6, "%d_%d", &iPixel, &iLine ) != 2 ) + return NULL; + } + else if( EQUALN(pszName,"GeoPixel_",9) ) + { + double adfGeoTransform[6]; + double adfInvGeoTransform[6]; + double dfGeoX, dfGeoY; + + { + dfGeoX = CPLAtof(pszName + 9); + const char* pszUnderscore = strchr(pszName + 9, '_'); + if( !pszUnderscore ) + return NULL; + dfGeoY = CPLAtof(pszUnderscore+1); + } + + if( m_parent_dataset->GetGeoTransform( adfGeoTransform ) != CE_None ) + return NULL; + + if( !GDALInvGeoTransform( adfGeoTransform, adfInvGeoTransform ) ) + return NULL; + + iPixel = (int) floor( + adfInvGeoTransform[0] + + adfInvGeoTransform[1] * dfGeoX + + adfInvGeoTransform[2] * dfGeoY ); + iLine = (int) floor( + adfInvGeoTransform[3] + + adfInvGeoTransform[4] * dfGeoX + + adfInvGeoTransform[5] * dfGeoY ); + + /* The GetDataset() for the WMS driver is always the main overview level, so rescale */ + /* the values if we are an overview */ + if (m_overview >= 0) + { + iPixel = (int) (1.0 * iPixel * GetXSize() / m_parent_dataset->GetRasterBand(1)->GetXSize()); + iLine = (int) (1.0 * iLine * GetYSize() / m_parent_dataset->GetRasterBand(1)->GetYSize()); + } + } + else + return NULL; + + if( iPixel < 0 || iLine < 0 + || iPixel >= GetXSize() + || iLine >= GetYSize() ) + return NULL; + + if (nBand != 1) + { + GDALRasterBand* poFirstBand = m_parent_dataset->GetRasterBand(1); + if (m_overview >= 0) + poFirstBand = poFirstBand->GetOverview(m_overview); + if (poFirstBand) + return poFirstBand->GetMetadataItem(pszName, pszDomain); + } + + GDALWMSImageRequestInfo iri; + GDALWMSTiledImageRequestInfo tiri; + int nBlockXOff = iPixel / nBlockXSize; + int nBlockYOff = iLine / nBlockYSize; + + ComputeRequestInfo(iri, tiri, nBlockXOff, nBlockYOff); + + CPLString url; + m_parent_dataset->m_mini_driver->GetTiledImageInfo(&url, + iri, tiri, + iPixel % nBlockXSize, + iLine % nBlockXSize); + + + char* pszRes = NULL; + + if (url.size() != 0) + { + if (url == osMetadataItemURL) + { + return osMetadataItem.size() != 0 ? osMetadataItem.c_str() : NULL; + } + osMetadataItemURL = url; + + char **http_request_opts = BuildHTTPRequestOpts(); + CPLHTTPResult* psResult = CPLHTTPFetch( url.c_str(), http_request_opts); + if( psResult && psResult->pabyData ) + pszRes = CPLStrdup((const char*) psResult->pabyData); + CPLHTTPDestroyResult(psResult); + CSLDestroy(http_request_opts); + } + + if (pszRes) + { + osMetadataItem = ""; + CPLPushErrorHandler(CPLQuietErrorHandler); + CPLXMLNode* psXML = CPLParseXMLString(pszRes); + CPLPopErrorHandler(); + if (psXML != NULL && psXML->eType == CXT_Element) + { + if (strcmp(psXML->pszValue, "?xml") == 0) + { + if (psXML->psNext) + { + char* pszXML = CPLSerializeXMLTree(psXML->psNext); + osMetadataItem += pszXML; + CPLFree(pszXML); + } + } + else + { + osMetadataItem += pszRes; + } + } + else + { + char* pszEscapedXML = CPLEscapeString(pszRes, -1, CPLES_XML_BUT_QUOTES); + osMetadataItem += pszEscapedXML; + CPLFree(pszEscapedXML); + } + if (psXML != NULL) + CPLDestroyXMLNode(psXML); + + osMetadataItem += ""; + CPLFree(pszRes); + return osMetadataItem.c_str(); + } + else + { + osMetadataItem = ""; + return NULL; + } + } + + return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain); +} + +CPLErr GDALWMSRasterBand::ReadBlockFromFile(int x, int y, const char *file_name, int to_buffer_band, void *buffer, int advise_read) { + CPLErr ret = CE_None; + GDALDataset *ds = 0; + GByte *color_table = NULL; + int i; + + //CPLDebug("WMS", "ReadBlockFromFile: to_buffer_band=%d, (x,y)=(%d, %d)", to_buffer_band, x, y); + + /* expected size */ + const int esx = MIN(MAX(0, (x + 1) * nBlockXSize), nRasterXSize) - MIN(MAX(0, x * nBlockXSize), nRasterXSize); + const int esy = MIN(MAX(0, (y + 1) * nBlockYSize), nRasterYSize) - MIN(MAX(0, y * nBlockYSize), nRasterYSize); + ds = reinterpret_cast(GDALOpen(file_name, GA_ReadOnly)); + if (ds != NULL) { + int sx = ds->GetRasterXSize(); + int sy = ds->GetRasterYSize(); + bool accepted_as_no_alpha = false; // if the request is for 4 bands but the wms returns 3 + /* Allow bigger than expected so pre-tiled constant size images work on corners */ + if ((sx > nBlockXSize) || (sy > nBlockYSize) || (sx < esx) || (sy < esy)) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Incorrect size %d x %d of downloaded block, expected %d x %d, max %d x %d.", + sx, sy, esx, esy, nBlockXSize, nBlockYSize); + ret = CE_Failure; + } + if (ret == CE_None) { + int nDSRasterCount = ds->GetRasterCount(); + if (nDSRasterCount != m_parent_dataset->nBands) { + /* Maybe its an image with color table */ + bool accepted_as_ct = false; + if ((eDataType == GDT_Byte) && (ds->GetRasterCount() == 1)) { + GDALRasterBand *rb = ds->GetRasterBand(1); + if (rb->GetRasterDataType() == GDT_Byte) { + GDALColorTable *ct = rb->GetColorTable(); + if (ct != NULL) { + accepted_as_ct = true; + if (!advise_read) { + color_table = new GByte[256 * 4]; + const int count = MIN(256, ct->GetColorEntryCount()); + for (i = 0; i < count; ++i) { + GDALColorEntry ce; + ct->GetColorEntryAsRGB(i, &ce); + color_table[i] = static_cast(ce.c1); + color_table[i + 256] = static_cast(ce.c2); + color_table[i + 512] = static_cast(ce.c3); + color_table[i + 768] = static_cast(ce.c4); + } + for (i = count; i < 256; ++i) { + color_table[i] = 0; + color_table[i + 256] = 0; + color_table[i + 512] = 0; + color_table[i + 768] = 0; + } + } + } + } + } + + if (nDSRasterCount == 4 && m_parent_dataset->nBands == 3) + { + /* metacarta TMS service sometimes return a 4 band PNG instead of the expected 3 band... */ + } + else if (!accepted_as_ct) { + if (ds->GetRasterCount()==3 && m_parent_dataset->nBands == 4 && (eDataType == GDT_Byte)) + { // WMS returned a file with no alpha so we will fill the alpha band with "opaque" + accepted_as_no_alpha = true; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Incorrect bands count %d in downloaded block, expected %d.", + nDSRasterCount, m_parent_dataset->nBands); + ret = CE_Failure; + } + } + } + } + if (!advise_read) { + for (int ib = 1; ib <= m_parent_dataset->nBands; ++ib) { + if (ret == CE_None) { + void *p = NULL; + GDALRasterBlock *b = NULL; + if ((buffer != NULL) && (ib == to_buffer_band)) { + p = buffer; + } else { + GDALWMSRasterBand *band = static_cast(m_parent_dataset->GetRasterBand(ib)); + if (m_overview >= 0) band = static_cast(band->GetOverview(m_overview)); + if (!band->IsBlockInCache(x, y)) { + b = band->GetLockedBlockRef(x, y, true); + if (b != NULL) { + p = b->GetDataRef(); + if (p == NULL) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: GetDataRef returned NULL."); + ret = CE_Failure; + } + } + } + else + { + //CPLDebug("WMS", "Band %d, block (x,y)=(%d, %d) already in cache", band->GetBand(), x, y); + } + } + if (p != NULL) { + int pixel_space = GDALGetDataTypeSize(eDataType) / 8; + int line_space = pixel_space * nBlockXSize; + if (color_table == NULL) { + if( ib <= ds->GetRasterCount()) { + GDALDataType dt=eDataType; + // Get the data from the PNG as stored instead of converting, if the server asks for that + // TODO: This hack is from #3493 - not sure it really belongs here. + if ((GDT_Int16==dt)&&(GDT_UInt16==ds->GetRasterBand(ib)->GetRasterDataType())) + dt=GDT_UInt16; + if (ds->RasterIO(GF_Read, 0, 0, sx, sy, p, sx, sy, dt, 1, &ib, pixel_space, line_space, 0, NULL) != CE_None) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: RasterIO failed on downloaded block."); + ret = CE_Failure; + } + } + else + { // parent expects 4 bands but file only has 3 so generate a all "opaque" 4th band + if (accepted_as_no_alpha) + { + // the file had 3 bands and we are reading band 4 (Alpha) so fill with 255 (no alpha) + GByte *byte_buffer = reinterpret_cast(p); + for (int y = 0; y < sy; ++y) { + for (int x = 0; x < sx; ++x) { + const int offset = x + y * line_space; + byte_buffer[offset] = 255; // fill with opaque + } + } + } + else + { // we should never get here because this case was caught above + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Incorrect bands count %d in downloaded block, expected %d.", + ds->GetRasterCount(), m_parent_dataset->nBands); + ret = CE_Failure; + } + } + } else if (ib <= 4) { + if (ds->RasterIO(GF_Read, 0, 0, sx, sy, p, sx, sy, eDataType, 1, NULL, pixel_space, line_space, 0, NULL) != CE_None) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: RasterIO failed on downloaded block."); + ret = CE_Failure; + } + if (ret == CE_None) { + GByte *band_color_table = color_table + 256 * (ib - 1); + GByte *byte_buffer = reinterpret_cast(p); + for (int y = 0; y < sy; ++y) { + for (int x = 0; x < sx; ++x) { + const int offset = x + y * line_space; + byte_buffer[offset] = band_color_table[byte_buffer[offset]]; + } + } + } + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Color table supports at most 4 components."); + ret = CE_Failure; + } + } + if (b != NULL) { + b->DropLock(); + } + } + } + } + GDALClose(ds); + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: Unable to open downloaded block."); + ret = CE_Failure; + } + + if (color_table != NULL) { + delete[] color_table; + } + + return ret; +} + +CPLErr GDALWMSRasterBand::ZeroBlock(int x, int y, int to_buffer_band, void *buffer) { + CPLErr ret = CE_None; + + for (int ib = 1; ib <= m_parent_dataset->nBands; ++ib) { + if (ret == CE_None) { + void *p = NULL; + GDALRasterBlock *b = NULL; + if ((buffer != NULL) && (ib == to_buffer_band)) { + p = buffer; + } else { + GDALWMSRasterBand *band = static_cast(m_parent_dataset->GetRasterBand(ib)); + if (m_overview >= 0) band = static_cast(band->GetOverview(m_overview)); + if (!band->IsBlockInCache(x, y)) { + b = band->GetLockedBlockRef(x, y, true); + if (b != NULL) { + p = b->GetDataRef(); + if (p == NULL) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: GetDataRef returned NULL."); + ret = CE_Failure; + } + } + } + } + if (p != NULL) { + unsigned char *b = reinterpret_cast(p); + int block_size = nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType) / 8); + for (int i = 0; i < block_size; ++i) b[i] = 0; + } + if (b != NULL) { + b->DropLock(); + } + } + } + + return ret; +} + +CPLErr GDALWMSRasterBand::ReportWMSException(const char *file_name) { + CPLErr ret = CE_None; + int reported_errors_count = 0; + + CPLXMLNode *orig_root = CPLParseXMLFile(file_name); + CPLXMLNode *root = orig_root; + if (root != NULL) { + root = CPLGetXMLNode(root, "=ServiceExceptionReport"); + } + if (root != NULL) { + CPLXMLNode *n = CPLGetXMLNode(root, "ServiceException"); + while (n != NULL) { + const char *exception = CPLGetXMLValue(n, "=ServiceException", ""); + const char *exception_code = CPLGetXMLValue(n, "=ServiceException.code", ""); + if (exception[0] != '\0') { + if (exception_code[0] != '\0') { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: The server returned exception code '%s': %s", exception_code, exception); + ++reported_errors_count; + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: The server returned exception: %s", exception); + ++reported_errors_count; + } + } else if (exception_code[0] != '\0') { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS: The server returned exception code '%s'.", exception_code); + ++reported_errors_count; + } + + n = n->psNext; + if (n != NULL) { + n = CPLGetXMLNode(n, "=ServiceException"); + } + } + } else { + ret = CE_Failure; + } + if (orig_root != NULL) { + CPLDestroyXMLNode(orig_root); + } + + if (reported_errors_count == 0) { + ret = CE_Failure; + } + + return ret; +} + + +CPLErr GDALWMSRasterBand::AdviseRead(int x0, int y0, + int sx, int sy, + CPL_UNUSED int bsx, + CPL_UNUSED int bsy, + CPL_UNUSED GDALDataType bdt, + CPL_UNUSED char **options) { +// printf("AdviseRead(%d, %d, %d, %d)\n", x0, y0, sx, sy); + if (m_parent_dataset->m_offline_mode || !m_parent_dataset->m_use_advise_read) return CE_None; + if (m_parent_dataset->m_cache == NULL) return CE_Failure; + + int bx0 = x0 / nBlockXSize; + int by0 = y0 / nBlockYSize; + int bx1 = (x0 + sx - 1) / nBlockXSize; + int by1 = (y0 + sy - 1) / nBlockYSize; + + return ReadBlocks(0, 0, NULL, bx0, by0, bx1, by1, 1); +} + +GDALColorInterp GDALWMSRasterBand::GetColorInterpretation() { + return m_color_interp; +} + +CPLErr GDALWMSRasterBand::SetColorInterpretation( GDALColorInterp eNewInterp ) +{ + m_color_interp = eNewInterp; + return CE_None; +} + +// Utility function, returns a value from a vector corresponding to the band index +// or the first entry +static double getBandValue(std::vector &v,size_t idx) +{ + idx--; + if (v.size()>idx) return v[idx]; + return v[0]; +} + +double GDALWMSRasterBand::GetNoDataValue( int *pbSuccess) +{ + std::vector &v=m_parent_dataset->vNoData; + if (v.size()==0) + return GDALPamRasterBand::GetNoDataValue(pbSuccess); + if (pbSuccess) *pbSuccess=TRUE; + return getBandValue(v,nBand); +} + +double GDALWMSRasterBand::GetMinimum( int *pbSuccess) +{ + std::vector &v=m_parent_dataset->vMin; + if (v.size()==0) + return GDALPamRasterBand::GetMinimum(pbSuccess); + if (pbSuccess) *pbSuccess=TRUE; + return getBandValue(v,nBand); +} + +double GDALWMSRasterBand::GetMaximum( int *pbSuccess) +{ + std::vector &v=m_parent_dataset->vMax; + if (v.size()==0) + return GDALPamRasterBand::GetMaximum(pbSuccess); + if (pbSuccess) *pbSuccess=TRUE; + return getBandValue(v,nBand); +} + +GDALColorTable *GDALWMSRasterBand::GetColorTable() +{ + return m_parent_dataset->m_poColorTable; +} diff --git a/bazaar/plugin/gdal/frmts/wms/makefile.vc b/bazaar/plugin/gdal/frmts/wms/makefile.vc new file mode 100644 index 000000000..47da4d0b0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/makefile.vc @@ -0,0 +1,21 @@ + +OBJ = \ + gdalwmscache.obj gdalwmsdataset.obj gdalwmsrasterband.obj wmsutils.obj \ + gdalhttp.obj md5.obj minidriver.obj wmsdriver.obj \ + minidriver_wms.obj minidriver_tileservice.obj \ + minidriver_worldwind.obj minidriver_tms.obj minidriver_tiled_wms.obj \ + wmsmetadataset.obj minidriver_virtualearth.obj minidriver_arcgis_server.obj + +EXTRAFLAGS = -DHAVE_CURL $(CURL_CFLAGS) $(CURL_INC) + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + -del *.dll diff --git a/bazaar/plugin/gdal/frmts/wms/md5.cpp b/bazaar/plugin/gdal/frmts/wms/md5.cpp new file mode 100644 index 000000000..63dc7e1f4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/md5.cpp @@ -0,0 +1,292 @@ +/* +* This code implements the MD5 message-digest algorithm. +* The algorithm is due to Ron Rivest. This code was +* written by Colin Plumb in 1993, no copyright is claimed. +* This code is in the public domain; do with it what you wish. +* +* Equivalent code is available from RSA Data Security, Inc. +* This code has been tested against that, and is equivalent, +* except that you don't need to include two pages of legalese +* with every copy. +* +* To compute the message digest of a chunk of bytes, declare an +* MD5Context structure, pass it to MD5Init, call MD5Update as +* needed on buffers full of bytes, and then call MD5Final, which +* will fill a supplied 16-byte array with the digest. +*/ + +/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to +not require an integer type which is exactly 32 bits. This work +draws on the changes for the same purpose by Tatu Ylonen + as part of SSH, but since I didn't actually use +that code, there is no copyright issue. I hereby disclaim +copyright in any changes I have made; this code remains in the +public domain. */ + +/* Note regarding cvs_* namespace: this avoids potential conflicts +with libraries such as some versions of Kerberos. No particular +need to worry about whether the system supplies an MD5 library, as +this file is only about 3k of object code. */ + +/* Modified by E. Rouault, to fix : + warning: argument to 'sizeof' in 'memset' call is the same expression as the destination; did you mean to dereference it? [-Wsizeof-pointer-memaccess] + memset(ctx, 0, sizeof(ctx)); */ /* In case it's sensitive */ +/* at the end of cvs_MD5Final */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include /* for memcpy() and memset() */ + +#include "md5.h" + +/* Little-endian byte-swapping routines. Note that these do not +depend on the size of datatypes such as cvs_uint32, nor do they require +us to detect the endianness of the machine we are running on. It +is possible they should be macros for speed, but I would be +surprised if they were a performance bottleneck for MD5. */ + +static cvs_uint32 getu32(const unsigned char *addr) +{ + return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) + | addr[1]) << 8 | addr[0]; +} + +static void +putu32 ( + cvs_uint32 data, + unsigned char *addr) +{ + addr[0] = (unsigned char)data; + addr[1] = (unsigned char)(data >> 8); + addr[2] = (unsigned char)(data >> 16); + addr[3] = (unsigned char)(data >> 24); +} + +/* +* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious +* initialization constants. +*/ +void +cvs_MD5Init ( +struct cvs_MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* +* Update context to reflect the concatenation of another buffer full +* of bytes. +*/ +void +cvs_MD5Update ( +struct cvs_MD5Context *ctx, + unsigned char const *buf, + unsigned len) +{ + cvs_uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = (t + ((cvs_uint32)len << 3)) & 0xffffffff) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = ctx->in + t; + + t = 64-t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + cvs_MD5Transform (ctx->buf, ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + cvs_MD5Transform (ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* +* Final wrapup - pad to 64-byte boundary with the bit pattern +* 1 0* (64-bit count of bits processed, MSB-first) +*/ +void +cvs_MD5Final ( + unsigned char digest[16], +struct cvs_MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + cvs_MD5Transform (ctx->buf, ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count-8); + } + + /* Append length in bits and transform */ + putu32(ctx->bits[0], ctx->in + 56); + putu32(ctx->bits[1], ctx->in + 60); + + cvs_MD5Transform (ctx->buf, ctx->in); + putu32(ctx->buf[0], digest); + putu32(ctx->buf[1], digest + 4); + putu32(ctx->buf[2], digest + 8); + putu32(ctx->buf[3], digest + 12); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<>(32-s), w += x ) + +/* +* The core of the MD5 algorithm, this alters an existing MD5 hash to +* reflect the addition of 16 longwords of new data. MD5Update blocks +* the data and converts bytes into longwords for this routine. +*/ +void +cvs_MD5Transform ( + cvs_uint32 buf[4], + const unsigned char inraw[64]) +{ + register cvs_uint32 a, b, c, d; + cvs_uint32 in[16]; + int i; + + for (i = 0; i < 16; ++i) + in[i] = getu32 (inraw + 4 * i); + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +#endif diff --git a/bazaar/plugin/gdal/frmts/wms/md5.h b/bazaar/plugin/gdal/frmts/wms/md5.h new file mode 100644 index 000000000..7b85ca1de --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/md5.h @@ -0,0 +1,24 @@ +/* See md5.c for explanation and copyright information. */ + +#ifndef MD5_H +#define MD5_H + +/* Unlike previous versions of this code, uint32 need not be exactly +32 bits, merely 32 bits or more. Choosing a data type which is 32 +bits instead of 64 is not important; speed is considerably more +important. ANSI guarantees that "unsigned long" will be big enough, +and always using it seems to have few disadvantages. */ +typedef unsigned long cvs_uint32; + +struct cvs_MD5Context { + cvs_uint32 buf[4]; + cvs_uint32 bits[2]; + unsigned char in[64]; +}; + +void cvs_MD5Init(struct cvs_MD5Context *context); +void cvs_MD5Update(struct cvs_MD5Context *context, unsigned char const *buf, unsigned len); +void cvs_MD5Final(unsigned char digest[16], struct cvs_MD5Context *context); +void cvs_MD5Transform(cvs_uint32 buf[4], const unsigned char in[64]); + +#endif /* !MD5_H */ diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver.cpp b/bazaar/plugin/gdal/frmts/wms/minidriver.cpp new file mode 100644 index 000000000..58d88566c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver.cpp @@ -0,0 +1,123 @@ +/****************************************************************************** + * $Id: minidriver.cpp 28459 2015-02-12 13:48:21Z rouault $ + * + * Project: WMS Client Driver + * Purpose: GDALWMSMiniDriver base class implementation. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" + +static volatile GDALWMSMiniDriverManager *g_mini_driver_manager = NULL; +static CPLMutex *g_mini_driver_manager_mutex = NULL; + +GDALWMSMiniDriver::GDALWMSMiniDriver() { + m_parent_dataset = 0; +} + +GDALWMSMiniDriver::~GDALWMSMiniDriver() { +} + +CPLErr GDALWMSMiniDriver::Initialize(CPL_UNUSED CPLXMLNode *config) { + return CE_None; +} + +void GDALWMSMiniDriver::GetCapabilities(CPL_UNUSED GDALWMSMiniDriverCapabilities *caps) { +} + +void GDALWMSMiniDriver::ImageRequest(CPL_UNUSED CPLString *url, CPL_UNUSED const GDALWMSImageRequestInfo &iri) { +} + +void GDALWMSMiniDriver::TiledImageRequest(CPL_UNUSED CPLString *url, + CPL_UNUSED const GDALWMSImageRequestInfo &iri, + CPL_UNUSED const GDALWMSTiledImageRequestInfo &tiri) { +} + +void GDALWMSMiniDriver::GetTiledImageInfo(CPL_UNUSED CPLString *url, + CPL_UNUSED const GDALWMSImageRequestInfo &iri, + CPL_UNUSED const GDALWMSTiledImageRequestInfo &tiri, + CPL_UNUSED int nXInBlock, + CPL_UNUSED int nYInBlock) +{ +} + +const char *GDALWMSMiniDriver::GetProjectionInWKT() { + return NULL; +} + +GDALWMSMiniDriverFactory::GDALWMSMiniDriverFactory() { +} + +GDALWMSMiniDriverFactory::~GDALWMSMiniDriverFactory() { +} + +GDALWMSMiniDriverManager *GetGDALWMSMiniDriverManager() { + if (g_mini_driver_manager == NULL) { + CPLMutexHolderD(&g_mini_driver_manager_mutex); + if (g_mini_driver_manager == NULL) { + g_mini_driver_manager = new GDALWMSMiniDriverManager(); + } + CPLAssert(g_mini_driver_manager != NULL); + } + return const_cast(g_mini_driver_manager); +} + +void DestroyWMSMiniDriverManager() + +{ + CPLMutexHolderD(&g_mini_driver_manager_mutex); + + if( g_mini_driver_manager != 0 ) + { + delete g_mini_driver_manager; + g_mini_driver_manager = NULL; + } +} + +GDALWMSMiniDriverManager::GDALWMSMiniDriverManager() { +} + +GDALWMSMiniDriverManager::~GDALWMSMiniDriverManager() { + for (std::list::iterator it = m_mdfs.begin(); + it != m_mdfs.end(); ++it) { + GDALWMSMiniDriverFactory *mdf = *it; + delete mdf; + } +} + +void GDALWMSMiniDriverManager::Register(GDALWMSMiniDriverFactory *mdf) { + CPLMutexHolderD(&g_mini_driver_manager_mutex); + + m_mdfs.push_back(mdf); +} + +GDALWMSMiniDriverFactory *GDALWMSMiniDriverManager::Find(const CPLString &name) { + CPLMutexHolderD(&g_mini_driver_manager_mutex); + + for (std::list::iterator it = m_mdfs.begin(); it != m_mdfs.end(); ++it) { + GDALWMSMiniDriverFactory *const mdf = *it; + if (EQUAL(mdf->GetName().c_str(), name.c_str())) return mdf; + } + return NULL; +} diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_arcgis_server.cpp b/bazaar/plugin/gdal/frmts/wms/minidriver_arcgis_server.cpp new file mode 100644 index 000000000..6dd62c9e1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_arcgis_server.cpp @@ -0,0 +1,263 @@ +/****************************************************************************** + * $Id$ + * + * Project: Arc GIS Server Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Alexander Lisovenko + * + ****************************************************************************** + * Copyright (c) 2014-2015, NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" +#include "minidriver_arcgis_server.h" + +CPP_GDALWMSMiniDriverFactory(AGS) + +GDALWMSMiniDriver_AGS::GDALWMSMiniDriver_AGS() +{ +} + +GDALWMSMiniDriver_AGS::~GDALWMSMiniDriver_AGS() +{ +} + +CPLErr GDALWMSMiniDriver_AGS::Initialize(CPLXMLNode *config) +{ + CPLErr ret = CE_None; + int i; + + if (ret == CE_None) + { + const char *base_url = CPLGetXMLValue(config, "ServerURL", ""); + if (base_url[0] != '\0') + { + /* Try the old name */ + base_url = CPLGetXMLValue(config, "ServerUrl", ""); + } + + if (base_url[0] != '\0') + { + m_base_url = base_url; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, ArcGIS Server mini-driver: ServerURL missing."); + ret = CE_Failure; + } + } + + if (ret == CE_None) + { + m_image_format = CPLGetXMLValue(config, "ImageFormat", "png"); + m_transparent = CPLGetXMLValue(config, "Transparent",""); + // the transparent flag needs to be "true" or "false" + // in lower case according to the ArcGIS Server REST API + for(i = 0; i < (int)m_transparent.size(); i++) + { + m_transparent[i] = (char) tolower(m_transparent[i]); + } + + m_layers = CPLGetXMLValue(config, "Layers", ""); + } + + if (ret == CE_None) + { + const char* irs = CPLGetXMLValue(config, "SRS", "102100"); + + if (irs != NULL) + { + if(EQUALN(irs, "EPSG:", 5)) //if we have EPSG code just convert it to WKT + { + m_projection_wkt = ProjToWKT(irs); + m_irs = irs + 5; + } + else //if we have AGS code - try if it's EPSG + { + m_irs = irs; + m_projection_wkt = ProjToWKT("EPSG:" + m_irs); + } + // TODO: if we have AGS JSON + } + m_identification_tolerance = CPLGetXMLValue(config, "IdentificationTolerance", "2"); + } + + if (ret == CE_None) + { + const char *bbox_order = CPLGetXMLValue(config, "BBoxOrder", "xyXY"); + if (bbox_order[0] != '\0') + { + for (i = 0; i < 4; ++i) + { + if ((bbox_order[i] != 'x') && (bbox_order[i] != 'y') && + (bbox_order[i] != 'X') && (bbox_order[i] != 'Y')) + break; + } + + if (i == 4) + { + m_bbox_order = bbox_order; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, ArcGIS Server mini-driver: Incorrect BBoxOrder."); + ret = CE_Failure; + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, ArcGIS Server mini-driver: BBoxOrder missing."); + ret = CE_Failure; + } + } + + return ret; +} + +void GDALWMSMiniDriver_AGS::GetCapabilities(GDALWMSMiniDriverCapabilities *caps) +{ + caps->m_capabilities_version = 1; + caps->m_has_arb_overviews = 1; + caps->m_has_image_request = 1; + caps->m_has_tiled_image_requeset = 1; + caps->m_max_overview_count = 32; +} + +void GDALWMSMiniDriver_AGS::ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri) +{ + *url = m_base_url; + + if (m_base_url.ifind( "/export?") == std::string::npos) + URLAppend(url, "/export?"); + + URLAppendF(url, "&f=image"); + URLAppendF(url, "&bbox=%.8f,%.8f,%.8f,%.8f", + GetBBoxCoord(iri, m_bbox_order[0]), GetBBoxCoord(iri, m_bbox_order[1]), + GetBBoxCoord(iri, m_bbox_order[2]), GetBBoxCoord(iri, m_bbox_order[3])); + URLAppendF(url, "&size=%d,%d", iri.m_sx,iri.m_sy); + URLAppendF(url, "&dpi="); + URLAppendF(url, "&imageSR=%s", m_irs.c_str()); + URLAppendF(url, "&bboxSR=%s", m_irs.c_str()); + URLAppendF(url, "&format=%s", m_image_format.c_str()); + + URLAppendF(url, "&layerdefs="); + URLAppendF(url, "&layers=%s", m_layers.c_str()); + + if(m_transparent.size()) + URLAppendF(url, "&transparent=%s", m_transparent.c_str()); + else + URLAppendF(url, "&transparent=%s", "false"); + + URLAppendF(url, "&time="); + URLAppendF(url, "&layerTimeOptions="); + URLAppendF(url, "&dynamicLayers="); + + CPLDebug("AGS", "URL = %s\n", url->c_str()); +} + +void GDALWMSMiniDriver_AGS::TiledImageRequest(CPLString *url, + const GDALWMSImageRequestInfo &iri, + const GDALWMSTiledImageRequestInfo &tiri) +{ + ImageRequest(url, iri); +} + + +void GDALWMSMiniDriver_AGS::GetTiledImageInfo(CPLString *url, + const GDALWMSImageRequestInfo &iri, + const GDALWMSTiledImageRequestInfo &tiri, + int nXInBlock, + int nYInBlock) +{ + *url = m_base_url; + + if (m_base_url.ifind( "/identify?") == std::string::npos) + URLAppend(url, "/identify?"); + + URLAppendF(url, "&f=json"); + + double fX = GetBBoxCoord(iri, 'x') + nXInBlock * (GetBBoxCoord(iri, 'X') - + GetBBoxCoord(iri, 'x')) / iri.m_sx; + double fY = GetBBoxCoord(iri, 'y') + (iri.m_sy - nYInBlock) * (GetBBoxCoord(iri, 'Y') - + GetBBoxCoord(iri, 'y')) / iri.m_sy; + + URLAppendF(url, "&geometry=%8f,%8f", fX, fY); + URLAppendF(url, "&geometryType=esriGeometryPoint"); + + URLAppendF(url, "&sr=%s", m_irs.c_str()); + URLAppendF(url, "&layerdefs="); + URLAppendF(url, "&time="); + URLAppendF(url, "&layerTimeOptions="); + + CPLString layers("visible"); + if ( m_layers.find("show") != std::string::npos ) + { + layers = m_layers; + layers.replace( layers.find("show"), 4, "all" ); + } + + if ( m_layers.find("hide") != std::string::npos ) + { + layers = "top"; + } + + if ( m_layers.find("include") != std::string::npos ) + { + layers = "top"; + } + + if ( m_layers.find("exclude") != std::string::npos ) + { + layers = "top"; + } + + URLAppendF(url, "&layers=%s", layers.c_str()); + + URLAppendF(url, "&tolerance=%s", m_identification_tolerance.c_str()); + URLAppendF(url, "&mapExtent=%.8f,%.8f,%.8f,%.8f", + GetBBoxCoord(iri, m_bbox_order[0]), GetBBoxCoord(iri, m_bbox_order[1]), + GetBBoxCoord(iri, m_bbox_order[2]), GetBBoxCoord(iri, m_bbox_order[3])); + URLAppendF(url, "&imageDisplay=%d,%d,96", iri.m_sx,iri.m_sy); + URLAppendF(url, "&returnGeometry=false"); + + URLAppendF(url, "&maxAllowableOffset="); + CPLDebug("AGS", "URL = %s", url->c_str()); +} + + +const char *GDALWMSMiniDriver_AGS::GetProjectionInWKT() +{ + return m_projection_wkt.c_str(); +} + +double GDALWMSMiniDriver_AGS::GetBBoxCoord(const GDALWMSImageRequestInfo &iri, char what) +{ + switch (what) + { + case 'x': return MIN(iri.m_x0, iri.m_x1); + case 'y': return MIN(iri.m_y0, iri.m_y1); + case 'X': return MAX(iri.m_x0, iri.m_x1); + case 'Y': return MAX(iri.m_y0, iri.m_y1); + } + return 0.0; +} + diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_arcgis_server.h b/bazaar/plugin/gdal/frmts/wms/minidriver_arcgis_server.h new file mode 100644 index 000000000..28c000647 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_arcgis_server.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * $Id$ + * + * Project: Arc GIS Server Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Alexander Lisovenko + * + ****************************************************************************** + * Copyright (c) 2014-2015, NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +H_GDALWMSMiniDriverFactory(AGS) + +class GDALWMSMiniDriver_AGS : public GDALWMSMiniDriver +{ +public: + GDALWMSMiniDriver_AGS(); + virtual ~GDALWMSMiniDriver_AGS(); + +public: + virtual CPLErr Initialize(CPLXMLNode *config); + virtual void GetCapabilities(GDALWMSMiniDriverCapabilities *caps); + virtual void ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri); + virtual void TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, + const GDALWMSTiledImageRequestInfo &tiri); + virtual void GetTiledImageInfo(CPLString *url, + const GDALWMSImageRequestInfo &iri, + const GDALWMSTiledImageRequestInfo &tiri, + int nXInBlock, + int nYInBlock); + virtual const char *GetProjectionInWKT(); + +protected: + double GetBBoxCoord(const GDALWMSImageRequestInfo &iri, char what); + +protected: + CPLString m_base_url; + /* + * png | png8 | png24 | jpg | pdf | bmp | gif | svg | png32 + * http://resources.arcgis.com/en/help/rest/apiref/ + * Parameter - format + */ + CPLString m_image_format; + CPLString m_transparent; + CPLString m_bbox_order; + CPLString m_irs; + + CPLString m_layers; + CPLString m_srs; + CPLString m_crs; + CPLString m_projection_wkt; + + CPLString m_identification_tolerance; +}; + diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_tiled_wms.cpp b/bazaar/plugin/gdal/frmts/wms/minidriver_tiled_wms.cpp new file mode 100644 index 000000000..e96d74451 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_tiled_wms.cpp @@ -0,0 +1,698 @@ +/****************************************************************************** + * + * Project: WMS Client Driver + * Purpose: Implementation of the OnEarth Tiled WMS minidriver. + * http://onearth.jpl.nasa.gov/tiled.html + * Author: Lucian Plesea (Lucian dot Plesea at jpl.nasa.gov) + * Adam Nowacki + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * Copyright (c) 2011-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" +#include "minidriver_tiled_wms.h" + +CPP_GDALWMSMiniDriverFactory(TiledWMS) + +static char SIG[]="GDAL_WMS TiledWMS: "; + +/* + *\brief Read a number from an xml element + */ + +static double getXMLNum(CPLXMLNode *poRoot, const char *pszPath, const char *pszDefault) +{ + return CPLAtof(CPLGetXMLValue(poRoot,pszPath,pszDefault)); +} + +/* + *\brief Read a ColorEntry XML node, return a GDALColorEntry structure + * + */ + +static GDALColorEntry GetXMLColorEntry(CPLXMLNode *p) +{ + GDALColorEntry ce; + ce.c1= static_cast(getXMLNum(p,"c1","0")); + ce.c2= static_cast(getXMLNum(p,"c2","0")); + ce.c3= static_cast(getXMLNum(p,"c3","0")); + ce.c4= static_cast(getXMLNum(p,"c4","255")); + return ce; +} + + +/************************************************************************/ +/* SearchXMLSiblings() */ +/************************************************************************/ + +/* + * \brief Search for a sibling of the root node with a given name. + * + * Searches only the next siblings of the node passed in for the named element or attribute. + * If the first character of the pszElement is '=', the search includes the psRoot node + * + * @param psRoot the root node to search. This should be a node of type + * CXT_Element. NULL is safe. + * + * @param pszElement the name of the element or attribute to search for. + * + * + * @return The first matching node or NULL on failure. + */ + +static CPLXMLNode *SearchXMLSiblings( CPLXMLNode *psRoot, const char *pszElement ) + +{ + if( psRoot == NULL || pszElement == NULL ) + return NULL; + + // If the strings starts with '=', skip it and test the root + // If not, start testing with the next sibling + if (pszElement[0]=='=') + pszElement++; + else + psRoot=psRoot->psNext; + + for (;psRoot!=NULL;psRoot=psRoot->psNext) + { + if ((psRoot->eType == CXT_Element || + psRoot->eType == CXT_Attribute) + && EQUAL(pszElement,psRoot->pszValue)) + return psRoot; + } + return NULL; +} + +/************************************************************************/ +/* SearchLeafGroupName() */ +/************************************************************************/ + +/* + * \brief Search for a leaf TileGroup node by name. + * + * @param psRoot the root node to search. This should be a node of type + * CXT_Element. NULL is safe. + * + * @param pszElement the name of the TileGroup to search for. + * + * @return The XML node of the matching TileGroup or NULL on failure. + */ + +static CPLXMLNode *SearchLeafGroupName( CPLXMLNode *psRoot, const char *name ) + +{ + CPLXMLNode *ret=NULL; + + if( psRoot == NULL || name == NULL ) return NULL; + + // Has to be a leaf TileGroup with the right name + if (NULL==SearchXMLSiblings(psRoot->psChild,"TiledGroup")) + { + if (EQUAL(name,CPLGetXMLValue(psRoot,"Name",""))) + { + return psRoot; + } + else + { // Try a sibling + return SearchLeafGroupName(psRoot->psNext,name); + } + } + else + { // Is metagroup, try children then siblings + ret=SearchLeafGroupName(psRoot->psChild,name); + if (NULL!=ret) return ret; + return SearchLeafGroupName(psRoot->psNext,name); + } +} + +/************************************************************************/ +/* BandInterp() */ +/************************************************************************/ + +/* + * \brief Utility function to calculate color band interpretation. + * Only handles Gray, GrayAlpha, RGB and RGBA, based on total band count + * + * @param nbands is the total number of bands in the image + * + * @param band is the band number, starting with 1 + * + * @return GDALColorInterp of the band + */ + +static GDALColorInterp BandInterp(int nbands, int band) { + switch (nbands) { + case 1: return GCI_GrayIndex; + case 2: return ((band==1)?GCI_GrayIndex:GCI_AlphaBand); + case 3: // RGB + case 4: // RBGA + if (band<3) + return ((band==1)?GCI_RedBand:GCI_GreenBand); + return ((band==3)?GCI_BlueBand:GCI_AlphaBand); + default: + return GCI_Undefined; + } +} + +/************************************************************************/ +/* FindBbox() */ +/************************************************************************/ + +/* + * \brief Utility function to find the position of the bbox parameter value + * within a request string. The search for the bbox is case insensitive + * + * @param in, the string to search into + * + * @return The position from the begining of the string or -1 if not found + */ + +static int FindBbox(CPLString in) { + + size_t pos = in.ifind("&bbox="); + if (pos == std::string::npos) + return -1; + else + return (int)pos + 6; +} + +/************************************************************************/ +/* FindChangePattern() */ +/************************************************************************/ + +/* + * \brief Build the right request pattern based on the change request list + * It only gets called on initialization + * @param cdata, possible request strings, white space separated + * @param substs, the list of substitutions to be applied + * @param keys, the list of available substitution keys + * @param ret The return value, a matching request or an empty string + */ + +static void FindChangePattern( char *cdata,char **substs, char **keys, CPLString &ret) +{ + char **papszTokens=CSLTokenizeString2(cdata," \t\n\r", + CSLT_STRIPLEADSPACES|CSLT_STRIPENDSPACES); + ret.clear(); + + int matchcount=CSLCount(substs); + int keycount=CSLCount(keys); + if (keycount=scale) + { + scale=tscale; + position=i; + } + i++; + } + if (position>-1) + { + req=list[position]; + list = CSLRemoveStrings(list,position,1,NULL); + } + return req; +} + +/* + *\Brief Initialize minidriver with info from the server + */ + +CPLErr GDALWMSMiniDriver_TiledWMS::Initialize(CPLXMLNode *config) +{ + CPLErr ret = CE_None; + CPLXMLNode *tileServiceConfig=NULL; + CPLHTTPResult *psResult=NULL; + CPLXMLNode *TG=NULL; + + char **requests=NULL; + char **substs=NULL; + char **keys=NULL; + + for (int once=1;once;once--) { // Something to break out of + // Parse info from the service + + m_end_url = CPLGetXMLValue(config,"AdditionalArgs",""); + m_base_url = CPLGetXMLValue(config, "ServerURL", ""); + if (m_base_url.empty()) { + CPLError(ret=CE_Failure, CPLE_AppDefined, "%s ServerURL missing.",SIG); + break; + } + + CPLString tiledGroupName (CPLGetXMLValue(config, "TiledGroupName", "")); + if (tiledGroupName.empty()) { + CPLError(ret=CE_Failure, CPLE_AppDefined, "%s TiledGroupName missing.",SIG); + break; + } + + // Change strings, key is an attribute, value is the value of the Change node + // Multiple substitutions are possible + TG=CPLSearchXMLNode(config, "Change"); + while(TG!=NULL) { + CPLString name=CPLGetXMLValue(TG,"key",""); + if (name.empty()) { + CPLError(ret=CE_Failure, CPLE_AppDefined, + "%s Change element needs a non-empty \"key\" attribute",SIG); + break; + } + substs=CSLSetNameValue(substs,name,CPLGetXMLValue(TG,"","")); + TG=SearchXMLSiblings(TG,"Change"); + } + if (ret!=CE_None) break; + + CPLString getTileServiceUrl = m_base_url + "request=GetTileService"; + psResult = CPLHTTPFetch(getTileServiceUrl, NULL); + + if (NULL==psResult) { + CPLError(ret=CE_Failure, CPLE_AppDefined, "%s Can't use HTTP", SIG); + break; + } + + if ((psResult->nStatus!=0)||(NULL==psResult->pabyData)||('\0'==psResult->pabyData[0])) { + CPLError(ret=CE_Failure, CPLE_AppDefined, "%s Server response error on GetTileService.",SIG); + break; + } + + if (NULL==(tileServiceConfig=CPLParseXMLString((const char*)psResult->pabyData))) { + CPLError(ret=CE_Failure,CPLE_AppDefined, "%s Error parsing the GetTileService response.",SIG); + break; + } + + if (NULL==(TG=CPLSearchXMLNode(tileServiceConfig, "TiledPatterns"))) { + CPLError(ret=CE_Failure,CPLE_AppDefined, + "%s Can't locate TiledPatterns in server response.",SIG); + break; + } + + // Get the global base_url and bounding box, these can be overwritten at the tileGroup level + // They are just pointers into existing structures, cleanup is not required + const char *global_base_url=CPLGetXMLValue(tileServiceConfig,"TiledPatterns.OnlineResource.xlink:href",""); + CPLXMLNode *global_latlonbbox=CPLGetXMLNode(tileServiceConfig, "TiledPatterns.LatLonBoundingBox"); + CPLXMLNode *global_bbox=CPLGetXMLNode(tileServiceConfig, "TiledPatterns.BoundingBox"); + + if (NULL==(TG=SearchLeafGroupName(TG->psChild,tiledGroupName))) { + CPLError(ret=CE_Failure,CPLE_AppDefined, + "%s Can't locate TiledGroup ""%s"" in server response.",SIG, + tiledGroupName.c_str()); + break; + } + + int band_count=atoi(CPLGetXMLValue(TG, "Bands", "3")); + + if (!GDALCheckBandCount(band_count, FALSE)) { + CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG, + "Invalid number of bands in server response"); + break; + } + + // Collect all keys defined by this tileset + if (NULL!=CPLGetXMLNode(TG,"Key")) { + CPLXMLNode *node=CPLGetXMLNode(TG,"Key"); + while (NULL!=node) { + const char *val=CPLGetXMLValue(node,NULL,NULL); + if (val) keys=CSLAddString(keys,val); + node=SearchXMLSiblings(node,"Key"); + } + } + + // Data values are attributes, they include NoData Min and Max + if (0!=CPLGetXMLNode(TG,"DataValues")) { + const char *nodata=CPLGetXMLValue(TG,"DataValues.NoData",NULL); + if (nodata!=NULL) m_parent_dataset->WMSSetNoDataValue(nodata); + const char *min=CPLGetXMLValue(TG,"DataValues.min",NULL); + if (min!=NULL) m_parent_dataset->WMSSetMinValue(min); + const char *max=CPLGetXMLValue(TG,"DataValues.max",NULL); + if (max!=NULL) m_parent_dataset->WMSSetMaxValue(max); + } + + m_parent_dataset->WMSSetBandsCount(band_count); + m_parent_dataset->WMSSetDataType(GDALGetDataTypeByName(CPLGetXMLValue(TG, "DataType", "Byte"))); + m_projection_wkt=CPLGetXMLValue(TG, "Projection",""); + + m_base_url=CPLGetXMLValue(TG,"OnlineResource.xlink:href",global_base_url); + if (m_base_url[0]=='\0') { + CPLError(ret=CE_Failure,CPLE_AppDefined, "%s%s",SIG, + "Can't locate OnlineResource in the server response."); + break; + } + + // Bounding box, local, global, local lat-lon, global lat-lon, in this order + CPLXMLNode *bbox = CPLGetXMLNode(TG, "BoundingBox"); + if (NULL==bbox) bbox=global_bbox; + if (NULL==bbox) bbox=CPLGetXMLNode(TG, "LatLonBoundingBox"); + if (NULL==bbox) bbox=global_latlonbbox; + + if (NULL==bbox) { + CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG, + "Can't locate the LatLonBoundingBox in server response."); + break; + } + + m_data_window.m_x0=CPLAtof(CPLGetXMLValue(bbox,"minx","0")); + m_data_window.m_x1=CPLAtof(CPLGetXMLValue(bbox,"maxx","-1")); + m_data_window.m_y0=CPLAtof(CPLGetXMLValue(bbox,"maxy","0")); + m_data_window.m_y1=CPLAtof(CPLGetXMLValue(bbox,"miny","-1")); + + if ((m_data_window.m_x1-m_data_window.m_x0)<0) { + CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s", SIG, + "Coordinate order in BBox, problem in server response"); + break; + } + + // Is there a palette? + // + // Format is + // + // N : Optional + // RGBA|RGB|CMYK|HSV|HLS|L :mandatory + // :Optional + // + // + // the idx attribute is optional, it autoincrements + // The entries are actually vertices, interpolation takes place inside + // The palette starts initialized with zeros + // HSV and HLS are the similar, with c2 and c3 swapped + // RGB or RGBA are same + // + + GDALColorTable *poColorTable=NULL; + + if ((band_count==1) && CPLGetXMLNode(TG,"Palette")) { + + CPLXMLNode *node=CPLGetXMLNode(TG,"Palette"); + + int entries=static_cast(getXMLNum(node,"Size","255")); + GDALPaletteInterp eInterp=GPI_RGB; + + CPLString pModel=CPLGetXMLValue(node,"Model","RGB"); + if (!pModel.empty() && pModel.find("RGB")!=std::string::npos) + eInterp=GPI_RGB; + else { + CPLError(CE_Failure, CPLE_AppDefined, + "%s Palette Model %s is unknown, use RGB or RGBA", + SIG, pModel.c_str()); + return CE_Failure; + } + + if ((entries>0)&&(entries<257)) { + int start_idx, end_idx; + GDALColorEntry ce_start={0,0,0,255},ce_end={0,0,0,255}; + + // Create it and initialize it to nothing + poColorTable = new GDALColorTable(eInterp); + poColorTable->CreateColorRamp(0,&ce_start,entries-1,&ce_end); + // Read the values + CPLXMLNode *p=CPLGetXMLNode(node,"Entry"); + if (p) { + // Initialize the first entry + start_idx=static_cast(getXMLNum(p,"idx","0")); + ce_start=GetXMLColorEntry(p); + if (start_idx<0) { + CPLError(CE_Failure, CPLE_AppDefined, + "%s Palette index %d not allowed",SIG,start_idx); + delete poColorTable; + return CE_Failure; + } + poColorTable->SetColorEntry(start_idx,&ce_start); + while (NULL!=(p=SearchXMLSiblings(p,"Entry"))) { + // For every entry, create a ramp + ce_end=GetXMLColorEntry(p); + end_idx=static_cast(getXMLNum(p,"idx",CPLString().FormatC(start_idx+1).c_str())); + if ((end_idx<=start_idx)||(start_idx>=entries)) { + CPLError(CE_Failure, CPLE_AppDefined, + "%s Index Error at index %d",SIG,end_idx); + delete poColorTable; + return CE_Failure; + } + poColorTable->CreateColorRamp(start_idx,&ce_start, + end_idx,&ce_end); + ce_start=ce_end; + start_idx=end_idx; + } + } + m_parent_dataset->SetColorTable(poColorTable); + } else { + CPLError(CE_Failure, CPLE_AppDefined,"%s Palette definition error",SIG); + return CE_Failure; + } + } + + int overview_count=0; + CPLXMLNode *Pattern=TG->psChild; + + m_bsx=m_bsy=-1; + m_data_window.m_sx=m_data_window.m_sy=0; + + for (int once=1;once;once--) { // Something to break out of + while ((NULL!=Pattern)&&(NULL!=(Pattern=SearchXMLSiblings(Pattern,"=TilePattern")))) { + int mbsx,mbsy; + + CPLString request; + FindChangePattern(Pattern->psChild->pszValue,substs,keys,request); + + char **papszTokens=CSLTokenizeString2(request,"&",0); + + const char* pszWIDTH = CSLFetchNameValue(papszTokens,"WIDTH"); + const char* pszHEIGHT = CSLFetchNameValue(papszTokens,"HEIGHT"); + if (pszWIDTH == NULL || pszHEIGHT == NULL) + { + CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG, + "Cannot find width and/or height parameters."); + overview_count=0; + CSLDestroy(papszTokens); + break; + } + + mbsx=atoi(pszWIDTH); + mbsy=atoi(pszHEIGHT); + if (m_projection_wkt.empty()) { + m_projection_wkt = CSLFetchNameValueDef(papszTokens,"SRS", ""); + if (!m_projection_wkt.empty()) + m_projection_wkt=ProjToWKT(m_projection_wkt); + } + + if (-1==m_bsx) m_bsx=mbsx; + if (-1==m_bsy) m_bsy=mbsy; + if ((m_bsy!=mbsy)||(m_bsy!=mbsy)) { + CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG, + "Tileset uses different block sizes."); + overview_count=0; + CSLDestroy(papszTokens); + break; + } + + double x,y,X,Y; + if (CPLsscanf(CSLFetchNameValueDef(papszTokens,"BBOX", ""),"%lf,%lf,%lf,%lf",&x,&y,&X,&Y)!=4) + { + CPLError(ret=CE_Failure,CPLE_AppDefined, + "%s Error parsing BBOX, pattern %d\n",SIG,overview_count+1); + CSLDestroy(papszTokens); + break; + } + // Pick the largest size + int sx=static_cast((m_data_window.m_x1-m_data_window.m_x0)/(X-x)*m_bsx); + int sy=static_cast(fabs((m_data_window.m_y1-m_data_window.m_y0)/(Y-y)*m_bsy)); + if (sx>m_data_window.m_sx) m_data_window.m_sx=sx; + if (sy>m_data_window.m_sy) m_data_window.m_sy=sy; + CSLDestroy(papszTokens); + + // Only use overlays where the top coordinate is within a pixel from the top of coverage + double pix_off,temp; + pix_off=m_bsy*modf(fabs((Y-m_data_window.m_y0)/(Y-y)),&temp); + if ((pix_off<1)||((m_bsy-pix_off)<1)) { + requests=CSLAddString(requests,request); + overview_count++; + } else + CPLError(CE_Warning,CPLE_AppDefined, + "%s Overlay size %dX%d can't be used due to alignment",SIG,sx,sy); + + Pattern=Pattern->psNext; + + } + + // The tlevel is needed, the tx and ty are not used by this minidriver + m_data_window.m_tlevel = 0; + m_data_window.m_tx = 0; + m_data_window.m_ty = 0; + + // Make sure the parent_dataset values are set before creating the bands + m_parent_dataset->WMSSetBlockSize(m_bsx,m_bsy); + m_parent_dataset->WMSSetRasterSize(m_data_window.m_sx,m_data_window.m_sy); + + m_parent_dataset->WMSSetDataWindow(m_data_window); + //m_parent_dataset->WMSSetOverviewCount(overview_count); + m_parent_dataset->WMSSetClamp(false); + + // Ready for the Rasterband creation + for (int i=0;i 1e-6)) { + CPLError(ret=CE_Failure,CPLE_AppDefined,"%s%s",SIG, + "Base resolution pattern missing."); + break; + } + + // Prepare the request and insert it back into the list + // Find returns an answer relative to the original string start! + size_t startBbox=FindBbox(request); + size_t endBbox=request.find('&',startBbox); + if (endBbox==std::string::npos) endBbox=request.size(); + request.replace(startBbox,endBbox-startBbox,"${GDAL_BBOX}"); + requests = CSLInsertString(requests,i,request); + + // Create the Rasterband or overview + for (int j = 1; j <= band_count; j++) { + if (i!=0) + m_parent_dataset->mGetBand(j)->AddOverview(scale); + else { // Base resolution + GDALWMSRasterBand *band=new + GDALWMSRasterBand(m_parent_dataset,j,1); + if (poColorTable!=NULL) band->SetColorInterpretation(GCI_PaletteIndex); + else band->SetColorInterpretation(BandInterp(band_count,j)); + m_parent_dataset->mSetBand(j, band); + }; + } + } + if ((overview_count==0)||(m_bsx<1)||(m_bsy<1)) { + CPLError(ret=CE_Failure,CPLE_AppDefined, + "%s No usable TilePattern elements found",SIG); + break; + } + } + } + + CSLDestroy(keys); + CSLDestroy(substs); + if (tileServiceConfig) CPLDestroyXMLNode(tileServiceConfig); + if (psResult) CPLHTTPDestroyResult(psResult); + + m_requests=requests; + return ret; +} + +void GDALWMSMiniDriver_TiledWMS::GetCapabilities(GDALWMSMiniDriverCapabilities *caps) { + caps->m_capabilities_version = 1; + caps->m_has_arb_overviews = 0; + caps->m_has_image_request = 1; + caps->m_has_tiled_image_requeset = 1; + caps->m_max_overview_count = 32; +} + + +// not called +void GDALWMSMiniDriver_TiledWMS::ImageRequest(CPL_UNUSED CPLString *url, + CPL_UNUSED const GDALWMSImageRequestInfo &iri) { +} + +void GDALWMSMiniDriver_TiledWMS::TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri) { + *url = m_base_url; + URLAppend(url,CSLGetField(m_requests,-tiri.m_level)); + URLSearchAndReplace(url,"${GDAL_BBOX}","%013.8f,%013.8f,%013.8f,%013.8f", + iri.m_x0,iri.m_y1,iri.m_x1,iri.m_y0); + URLAppend(url,m_end_url); +} + +const char *GDALWMSMiniDriver_TiledWMS::GetProjectionInWKT() { + return m_projection_wkt.c_str(); +} diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_tiled_wms.h b/bazaar/plugin/gdal/frmts/wms/minidriver_tiled_wms.h new file mode 100644 index 000000000..c63e1f7e2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_tiled_wms.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * + * Project: WMS Client Driver + * Purpose: Declarations for the OnEarth Tiled WMS minidriver. + * http://onearth.jpl.nasa.gov/tiled.html + * Author: Lucian Plesea (Lucian dot Plesea at jpl.nasa.gov) + * Adam Nowacki + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +H_GDALWMSMiniDriverFactory(TiledWMS) + +class GDALWMSMiniDriver_TiledWMS : public GDALWMSMiniDriver { +public: + GDALWMSMiniDriver_TiledWMS(); + virtual ~GDALWMSMiniDriver_TiledWMS(); + +public: + virtual CPLErr Initialize(CPLXMLNode *config); + virtual void GetCapabilities(GDALWMSMiniDriverCapabilities *caps); + virtual void ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri); + virtual void TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri); + virtual const char *GetProjectionInWKT(); + +protected: + double Scale(const char *request); + CPLString GetLowestScale(char **&list,int i); + GDALWMSDataWindow m_data_window; + char **m_requests; + CPLString m_base_url; + CPLString m_end_url; + int m_bsx,m_bsy; + CPLString m_projection_wkt; +}; diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_tileservice.cpp b/bazaar/plugin/gdal/frmts/wms/minidriver_tileservice.cpp new file mode 100644 index 000000000..364fcf08f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_tileservice.cpp @@ -0,0 +1,94 @@ +/****************************************************************************** + * $Id: minidriver_tileservice.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" +#include "minidriver_tileservice.h" + +CPP_GDALWMSMiniDriverFactory(TileService) + +GDALWMSMiniDriver_TileService::GDALWMSMiniDriver_TileService() { +} + +GDALWMSMiniDriver_TileService::~GDALWMSMiniDriver_TileService() { +} + +CPLErr GDALWMSMiniDriver_TileService::Initialize(CPLXMLNode *config) { + CPLErr ret = CE_None; + + if (ret == CE_None) { + const char *version = CPLGetXMLValue(config, "Version", "1"); + if (version[0] != '\0') { + m_version = version; + } + } + + if (ret == CE_None) { + const char *base_url = CPLGetXMLValue(config, "ServerURL", ""); + if (base_url[0] != '\0') { + /* Try the old name */ + base_url = CPLGetXMLValue(config, "ServerUrl", ""); + } + if (base_url[0] != '\0') { + m_base_url = base_url; + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, TileService mini-driver: ServerURL missing."); + ret = CE_Failure; + } + } + + m_dataset = CPLGetXMLValue(config, "Dataset", ""); + + return ret; +} + +void GDALWMSMiniDriver_TileService::GetCapabilities(GDALWMSMiniDriverCapabilities *caps) { + caps->m_capabilities_version = 1; + caps->m_has_arb_overviews = 0; + caps->m_has_image_request = 0; + caps->m_has_tiled_image_requeset = 1; + caps->m_max_overview_count = 32; +} + +void GDALWMSMiniDriver_TileService::ImageRequest(CPL_UNUSED CPLString *url, + CPL_UNUSED const GDALWMSImageRequestInfo &iri) { +} + +void GDALWMSMiniDriver_TileService::TiledImageRequest(CPLString *url, + CPL_UNUSED const GDALWMSImageRequestInfo &iri, + const GDALWMSTiledImageRequestInfo &tiri) { + // http://s0.tileservice.worldwindcentral.com/getTile?interface=map&version=1&dataset=bmng.topo.bathy.200401&level=5&x=18&y=6 + *url = m_base_url; + URLAppend(url, "&interface=map"); + URLAppendF(url, "&version=%s", m_version.c_str()); + URLAppendF(url, "&dataset=%s", m_dataset.c_str()); + URLAppendF(url, "&level=%d", tiri.m_level); + URLAppendF(url, "&x=%d", tiri.m_x); + URLAppendF(url, "&y=%d", tiri.m_y); +} diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_tileservice.h b/bazaar/plugin/gdal/frmts/wms/minidriver_tileservice.h new file mode 100644 index 000000000..ab1744d7a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_tileservice.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * $Id: minidriver_tileservice.h 18020 2009-11-14 14:33:20Z rouault $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +H_GDALWMSMiniDriverFactory(TileService) + +class GDALWMSMiniDriver_TileService : public GDALWMSMiniDriver { +public: + GDALWMSMiniDriver_TileService(); + virtual ~GDALWMSMiniDriver_TileService(); + +public: + virtual CPLErr Initialize(CPLXMLNode *config); + virtual void GetCapabilities(GDALWMSMiniDriverCapabilities *caps); + virtual void ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri); + virtual void TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri); + +protected: + CPLString m_base_url; + CPLString m_version; + CPLString m_dataset; +}; diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_tms.cpp b/bazaar/plugin/gdal/frmts/wms/minidriver_tms.cpp new file mode 100644 index 000000000..f4d7bcb01 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_tms.cpp @@ -0,0 +1,106 @@ +/****************************************************************************** + * $Id: minidriver_tms.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Chris Schmidt + * + ****************************************************************************** + * Copyright (c) 2007, Chris Schmidt + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" +#include "minidriver_tms.h" + + +CPP_GDALWMSMiniDriverFactory(TMS) + +GDALWMSMiniDriver_TMS::GDALWMSMiniDriver_TMS() { +} + +GDALWMSMiniDriver_TMS::~GDALWMSMiniDriver_TMS() { +} + +CPLErr GDALWMSMiniDriver_TMS::Initialize(CPLXMLNode *config) { + CPLErr ret = CE_None; + + if (ret == CE_None) { + const char *base_url = CPLGetXMLValue(config, "ServerURL", ""); + if (base_url[0] != '\0') { + m_base_url = base_url; + if (m_base_url.find("${") == std::string::npos) { + if (m_base_url[m_base_url.size()-1] != '/') { + m_base_url += "/"; + } + m_base_url += "${version}/${layer}/${z}/${x}/${y}.${format}"; + } + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, TMS mini-driver: ServerURL missing."); + ret = CE_Failure; + } + } + + m_dataset = CPLGetXMLValue(config, "Layer", ""); + m_version = CPLGetXMLValue(config, "Version", "1.0.0"); + m_format = CPLGetXMLValue(config, "Format", "jpg"); + + return ret; +} + +void GDALWMSMiniDriver_TMS::GetCapabilities(GDALWMSMiniDriverCapabilities *caps) { + caps->m_capabilities_version = 1; + caps->m_has_arb_overviews = 0; + caps->m_has_image_request = 0; + caps->m_has_tiled_image_requeset = 1; + caps->m_max_overview_count = 32; +} + +void GDALWMSMiniDriver_TMS::ImageRequest(CPL_UNUSED CPLString *url, + CPL_UNUSED const GDALWMSImageRequestInfo &iri) { +} + +void GDALWMSMiniDriver_TMS::TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri) { + const GDALWMSDataWindow *data_window = m_parent_dataset->WMSGetDataWindow(); + int tms_y; + + if (data_window->m_y_origin != GDALWMSDataWindow::TOP) { + tms_y = static_cast(floor(((data_window->m_y1 - data_window->m_y0) + / (iri.m_y1 - iri.m_y0)) + 0.5)) - tiri.m_y - 1; + } else { + tms_y = tiri.m_y; + } + // http://tms25.arc.nasa.gov/tile/tile.aspx?T=geocover2000&L=0&X=86&Y=39 + *url = m_base_url; + + URLSearchAndReplace(url, "${version}", "%s", m_version.c_str()); + URLSearchAndReplace(url, "${layer}", "%s", m_dataset.c_str()); + URLSearchAndReplace(url, "${format}", "%s", m_format.c_str()); + URLSearchAndReplace(url, "${x}", "%d", tiri.m_x); + URLSearchAndReplace(url, "${y}", "%d", tms_y); + URLSearchAndReplace(url, "${z}", "%d", tiri.m_level); + + /* Hack for some TMS like servers that require tile numbers split into 3 groups of */ + /* 3 digits, like http://tile8.geo.admin.ch/geoadmin/ch.swisstopo.pixelkarte-farbe */ + URLSearchAndReplace(url, "${xxx}", "%03d/%03d/%03d", tiri.m_x / 1000000, (tiri.m_x / 1000) % 1000, tiri.m_x % 1000); + URLSearchAndReplace(url, "${yyy}", "%03d/%03d/%03d", tms_y / 1000000, (tms_y / 1000) % 1000, tms_y % 1000); + +} diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_tms.h b/bazaar/plugin/gdal/frmts/wms/minidriver_tms.h new file mode 100644 index 000000000..fd20b2e4c --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_tms.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * $Id: minidriver_tms.h 18589 2010-01-19 18:54:53Z warmerdam $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Chris Schmidt + * + ****************************************************************************** + * Copyright (c) 2007, Chris Schmidt + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +H_GDALWMSMiniDriverFactory(TMS) + +class GDALWMSMiniDriver_TMS : public GDALWMSMiniDriver { +public: + GDALWMSMiniDriver_TMS(); + virtual ~GDALWMSMiniDriver_TMS(); + +public: + virtual CPLErr Initialize(CPLXMLNode *config); + virtual void GetCapabilities(GDALWMSMiniDriverCapabilities *caps); + virtual void ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri); + virtual void TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri); + +protected: + CPLString m_base_url; + CPLString m_dataset; + CPLString m_version; + CPLString m_format; +}; diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_virtualearth.cpp b/bazaar/plugin/gdal/frmts/wms/minidriver_virtualearth.cpp new file mode 100644 index 000000000..e5e4724b8 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_virtualearth.cpp @@ -0,0 +1,116 @@ +/****************************************************************************** + * $Id: minidriver_virtualearth.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" +#include "minidriver_virtualearth.h" + + +CPP_GDALWMSMiniDriverFactory(VirtualEarth) + +GDALWMSMiniDriver_VirtualEarth::GDALWMSMiniDriver_VirtualEarth() +{ +} + +GDALWMSMiniDriver_VirtualEarth::~GDALWMSMiniDriver_VirtualEarth() +{ +} + +CPLErr GDALWMSMiniDriver_VirtualEarth::Initialize(CPLXMLNode *config) +{ + CPLErr ret = CE_None; + + if (ret == CE_None) { + const char *base_url = CPLGetXMLValue(config, "ServerURL", ""); + if (base_url[0] != '\0') { + m_base_url = base_url; + if (m_base_url.find("${quadkey}") == std::string::npos) { + CPLError(CE_Failure, CPLE_AppDefined, + "GDALWMS, VirtualEarth mini-driver: ${quadkey} missing in ServerURL."); + ret = CE_Failure; + } + } else { + CPLError(CE_Failure, CPLE_AppDefined, + "GDALWMS, VirtualEarth mini-driver: ServerURL missing."); + ret = CE_Failure; + } + } + + m_parent_dataset->WMSSetDefaultBlockSize(256, 256); + m_parent_dataset->WMSSetDefaultDataWindowCoordinates(-20037508.34,20037508.34,20037508.34,-20037508.34); + m_parent_dataset->WMSSetDefaultTileLevel(19); + m_parent_dataset->WMSSetDefaultOverviewCount(18); + m_parent_dataset->WMSSetNeedsDataWindow(FALSE); + + m_projection_wkt=ProjToWKT("EPSG:900913"); + + return ret; +} + +void GDALWMSMiniDriver_VirtualEarth::GetCapabilities(GDALWMSMiniDriverCapabilities *caps) +{ + caps->m_capabilities_version = 1; + caps->m_has_arb_overviews = 0; + caps->m_has_image_request = 0; + caps->m_has_tiled_image_requeset = 1; + caps->m_max_overview_count = 32; +} + +void GDALWMSMiniDriver_VirtualEarth::TiledImageRequest(CPLString *url, + CPL_UNUSED const GDALWMSImageRequestInfo &iri, + const GDALWMSTiledImageRequestInfo &tiri) +{ + + *url = m_base_url; + + char szTileNumber[64]; + int x = tiri.m_x; + int y = tiri.m_y; + int z = MIN(32,tiri.m_level); + + for(int i = 0; i < z; i ++) + { + int row = (y & 1); + int col = (x & 1); + + szTileNumber[z-1-i] = (char) ('0' + (col | (row << 1))); + + x = x >> 1; + y = y >> 1; + } + szTileNumber[z] = 0; + + URLSearchAndReplace(url, "${quadkey}", "%s", szTileNumber); + URLSearchAndReplace(url, "${server_num}", "%d", + (tiri.m_x + tiri.m_y + z) % 4); +} + +const char *GDALWMSMiniDriver_VirtualEarth::GetProjectionInWKT() { + return m_projection_wkt.c_str(); +} diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_virtualearth.h b/bazaar/plugin/gdal/frmts/wms/minidriver_virtualearth.h new file mode 100644 index 000000000..208ded1f9 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_virtualearth.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * $Id: minidriver_virtualearth.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +H_GDALWMSMiniDriverFactory(VirtualEarth) + +class GDALWMSMiniDriver_VirtualEarth : public GDALWMSMiniDriver { +public: + GDALWMSMiniDriver_VirtualEarth(); + virtual ~GDALWMSMiniDriver_VirtualEarth(); + +public: + virtual CPLErr Initialize(CPLXMLNode *config); + virtual void GetCapabilities(GDALWMSMiniDriverCapabilities *caps); + virtual void TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri); + virtual const char* GetProjectionInWKT(); + +protected: + CPLString m_base_url; + CPLString m_projection_wkt; +}; diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_wms.cpp b/bazaar/plugin/gdal/frmts/wms/minidriver_wms.cpp new file mode 100644 index 000000000..f9b3c974a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_wms.cpp @@ -0,0 +1,214 @@ +/****************************************************************************** + * $Id: minidriver_wms.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * Copyright (c) 2007-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" +#include "minidriver_wms.h" + +CPP_GDALWMSMiniDriverFactory(WMS) + +GDALWMSMiniDriver_WMS::GDALWMSMiniDriver_WMS() { +} + +GDALWMSMiniDriver_WMS::~GDALWMSMiniDriver_WMS() { +} + +CPLErr GDALWMSMiniDriver_WMS::Initialize(CPLXMLNode *config) { + CPLErr ret = CE_None; + + if (ret == CE_None) { + const char *version = CPLGetXMLValue(config, "Version", "1.1.0"); + if (version[0] != '\0') { + m_version = version; + m_iversion = VersionStringToInt(version); + if (m_iversion == -1) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: Invalid version."); + ret = CE_Failure; + } + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: Version missing."); + ret = CE_Failure; + } + } + + if (ret == CE_None) { + const char *base_url = CPLGetXMLValue(config, "ServerURL", ""); + if (base_url[0] != '\0') { + /* Try the old name */ + base_url = CPLGetXMLValue(config, "ServerUrl", ""); + } + if (base_url[0] != '\0') { + m_base_url = base_url; + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: ServerURL missing."); + ret = CE_Failure; + } + } + + if (ret == CE_None) { +/* SRS is WMS version 1.1 and earlier, if SRS is not set use default unless CRS is set + CRS is WMS version 1.3, if CRS is not set use default unless SRS is set */ + const char *crs = CPLGetXMLValue(config, "CRS", ""); + const char *srs = CPLGetXMLValue(config, "SRS", ""); + if (m_iversion >= VersionStringToInt("1.3")) { + /* Version 1.3 and above */ + if ((srs[0] != '\0') && (crs[0] == '\0')) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: WMS version 1.3 and above expects CRS however SRS was set instead."); + ret = CE_Failure; + } else if (crs[0] != '\0') { + m_crs = crs; + } else { + m_crs = "EPSG:4326"; + } + } else { + /* Version 1.1.1 and below */ + if ((srs[0] == '\0') && (crs[0] != '\0')) { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: WMS version 1.1.1 and below expects SRS however CRS was set instead."); + ret = CE_Failure; + } else if (srs[0] != '\0') { + m_srs = srs; + } else { + m_srs = "EPSG:4326"; + } + } + } + + if (ret == CE_None) { + if (m_srs.size()) { + m_projection_wkt = ProjToWKT(m_srs); + } else if (m_crs.size()) { + m_projection_wkt = ProjToWKT(m_crs); + } + } + + if (ret == CE_None) { + m_image_format = CPLGetXMLValue(config, "ImageFormat", "image/jpeg"); + m_layers = CPLGetXMLValue(config, "Layers", ""); + m_styles = CPLGetXMLValue(config, "Styles", ""); + m_transparent = CPLGetXMLValue(config, "Transparent",""); + // the transparent flag needs to be "TRUE" or "FALSE" in upper case according to the WMS spec so force upper case + for(int i=0; i<(int)m_transparent.size();i++) + { + m_transparent[i] = (char) toupper(m_transparent[i]); + } + } + + if (ret == CE_None) { + const char *bbox_order = CPLGetXMLValue(config, "BBoxOrder", "xyXY"); + if (bbox_order[0] != '\0') { + int i; + for (i = 0; i < 4; ++i) { + if ((bbox_order[i] != 'x') && (bbox_order[i] != 'y') && (bbox_order[i] != 'X') && (bbox_order[i] != 'Y')) break; + } + if (i == 4) { + m_bbox_order = bbox_order; + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: Incorrect BBoxOrder."); + ret = CE_Failure; + } + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WMS mini-driver: BBoxOrder missing."); + ret = CE_Failure; + } + } + + return ret; +} + +void GDALWMSMiniDriver_WMS::GetCapabilities(GDALWMSMiniDriverCapabilities *caps) { + caps->m_capabilities_version = 1; + caps->m_has_arb_overviews = 1; + caps->m_has_image_request = 1; + caps->m_has_tiled_image_requeset = 1; + caps->m_max_overview_count = 32; +} + +void GDALWMSMiniDriver_WMS::BuildURL(CPLString *url, const GDALWMSImageRequestInfo &iri, const char* pszRequest) { + // http://onearth.jpl.nasa.gov/wms.cgi?request=GetMap&width=1000&height=500&layers=modis,global_mosaic&styles=&srs=EPSG:4326&format=image/jpeg&bbox=-180.000000,-90.000000,180.000000,090.000000 + *url = m_base_url; + if (m_base_url.ifind( "service=") == std::string::npos) + URLAppend(url, "&service=WMS"); + URLAppendF(url, "&request=%s", pszRequest); + URLAppendF(url, "&version=%s", m_version.c_str()); + URLAppendF(url, "&layers=%s", m_layers.c_str()); + URLAppendF(url, "&styles=%s", m_styles.c_str()); + if (m_srs.size()) URLAppendF(url, "&srs=%s", m_srs.c_str()); + if (m_crs.size()) URLAppendF(url, "&crs=%s", m_crs.c_str()); + if (m_transparent.size()) URLAppendF(url, "&transparent=%s", m_transparent.c_str()); + URLAppendF(url, "&format=%s", m_image_format.c_str()); + URLAppendF(url, "&width=%d", iri.m_sx); + URLAppendF(url, "&height=%d", iri.m_sy); + URLAppendF(url, "&bbox=%.8f,%.8f,%.8f,%.8f", + GetBBoxCoord(iri, m_bbox_order[0]), GetBBoxCoord(iri, m_bbox_order[1]), + GetBBoxCoord(iri, m_bbox_order[2]), GetBBoxCoord(iri, m_bbox_order[3])); +} + +void GDALWMSMiniDriver_WMS::ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri) { + BuildURL(url, iri, "GetMap"); + CPLDebug("WMS", "URL = %s", url->c_str()); +} + +void GDALWMSMiniDriver_WMS::TiledImageRequest(CPLString *url, + const GDALWMSImageRequestInfo &iri, + CPL_UNUSED const GDALWMSTiledImageRequestInfo &tiri) { + ImageRequest(url, iri); +} + + +void GDALWMSMiniDriver_WMS::GetTiledImageInfo(CPLString *url, + const GDALWMSImageRequestInfo &iri, + CPL_UNUSED const GDALWMSTiledImageRequestInfo &tiri, + int nXInBlock, + int nYInBlock) +{ + BuildURL(url, iri, "GetFeatureInfo"); + URLAppendF(url, "&query_layers=%s", m_layers.c_str()); + URLAppendF(url, "&x=%d", nXInBlock); + URLAppendF(url, "&y=%d", nYInBlock); + const char* pszInfoFormat = CPLGetConfigOption("WMS_INFO_FORMAT", "application/vnd.ogc.gml"); + URLAppendF(url, "&info_format=%s", pszInfoFormat); + + CPLDebug("WMS", "URL = %s", url->c_str()); +} + + +const char *GDALWMSMiniDriver_WMS::GetProjectionInWKT() { + return m_projection_wkt.c_str(); +} + +double GDALWMSMiniDriver_WMS::GetBBoxCoord(const GDALWMSImageRequestInfo &iri, char what) { + switch (what) { + case 'x': return MIN(iri.m_x0, iri.m_x1); + case 'y': return MIN(iri.m_y0, iri.m_y1); + case 'X': return MAX(iri.m_x0, iri.m_x1); + case 'Y': return MAX(iri.m_y0, iri.m_y1); + } + return 0.0; +} diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_wms.h b/bazaar/plugin/gdal/frmts/wms/minidriver_wms.h new file mode 100644 index 000000000..90dc92bad --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_wms.h @@ -0,0 +1,68 @@ +/****************************************************************************** + * $Id: minidriver_wms.h 23722 2012-01-07 22:15:29Z rouault $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +H_GDALWMSMiniDriverFactory(WMS) + +class GDALWMSMiniDriver_WMS : public GDALWMSMiniDriver { + + void BuildURL(CPLString *url, const GDALWMSImageRequestInfo &iri, const char* pszRequest); + +public: + GDALWMSMiniDriver_WMS(); + virtual ~GDALWMSMiniDriver_WMS(); + +public: + virtual CPLErr Initialize(CPLXMLNode *config); + virtual void GetCapabilities(GDALWMSMiniDriverCapabilities *caps); + virtual void ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri); + virtual void TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri); + virtual void GetTiledImageInfo(CPLString *url, + const GDALWMSImageRequestInfo &iri, + const GDALWMSTiledImageRequestInfo &tiri, + int nXInBlock, + int nYInBlock); + virtual const char *GetProjectionInWKT(); + +protected: + double GetBBoxCoord(const GDALWMSImageRequestInfo &iri, char what); + +protected: + CPLString m_base_url; + CPLString m_version; + int m_iversion; + CPLString m_layers; + CPLString m_styles; + CPLString m_srs; + CPLString m_crs; + CPLString m_image_format; + CPLString m_projection_wkt; + CPLString m_bbox_order; + CPLString m_transparent; +}; diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_worldwind.cpp b/bazaar/plugin/gdal/frmts/wms/minidriver_worldwind.cpp new file mode 100644 index 000000000..ead48228f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_worldwind.cpp @@ -0,0 +1,90 @@ +/****************************************************************************** + * $Id: minidriver_worldwind.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" +#include "minidriver_worldwind.h" + +CPP_GDALWMSMiniDriverFactory(WorldWind) + +GDALWMSMiniDriver_WorldWind::GDALWMSMiniDriver_WorldWind() { +} + +GDALWMSMiniDriver_WorldWind::~GDALWMSMiniDriver_WorldWind() { +} + +CPLErr GDALWMSMiniDriver_WorldWind::Initialize(CPLXMLNode *config) { + CPLErr ret = CE_None; + + if (ret == CE_None) { + const char *base_url = CPLGetXMLValue(config, "ServerURL", ""); + if (base_url[0] != '\0') { + /* Try the old name */ + base_url = CPLGetXMLValue(config, "ServerUrl", ""); + } + if (base_url[0] != '\0') { + m_base_url = base_url; + } else { + CPLError(CE_Failure, CPLE_AppDefined, "GDALWMS, WorldWind mini-driver: ServerURL missing."); + ret = CE_Failure; + } + } + + m_dataset = CPLGetXMLValue(config, "Layer", ""); + m_projection_wkt = ProjToWKT("EPSG:4326"); + + return ret; +} + +void GDALWMSMiniDriver_WorldWind::GetCapabilities(GDALWMSMiniDriverCapabilities *caps) { + caps->m_capabilities_version = 1; + caps->m_has_arb_overviews = 0; + caps->m_has_image_request = 0; + caps->m_has_tiled_image_requeset = 1; + caps->m_max_overview_count = 32; +} + +void GDALWMSMiniDriver_WorldWind::ImageRequest(CPL_UNUSED CPLString *url, + CPL_UNUSED const GDALWMSImageRequestInfo &iri) { +} + +void GDALWMSMiniDriver_WorldWind::TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri) { + const GDALWMSDataWindow *data_window = m_parent_dataset->WMSGetDataWindow(); + int worldwind_y = static_cast(floor(((data_window->m_y1 - data_window->m_y0) / (iri.m_y1 - iri.m_y0)) + 0.5)) - tiri.m_y - 1; + // http://worldwind25.arc.nasa.gov/tile/tile.aspx?T=geocover2000&L=0&X=86&Y=39 + *url = m_base_url; + URLAppendF(url, "&T=%s", m_dataset.c_str()); + URLAppendF(url, "&L=%d", tiri.m_level); + URLAppendF(url, "&X=%d", tiri.m_x); + URLAppendF(url, "&Y=%d", worldwind_y); +} + +const char *GDALWMSMiniDriver_WorldWind::GetProjectionInWKT() { + return m_projection_wkt.c_str(); +} diff --git a/bazaar/plugin/gdal/frmts/wms/minidriver_worldwind.h b/bazaar/plugin/gdal/frmts/wms/minidriver_worldwind.h new file mode 100644 index 000000000..812f54726 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/minidriver_worldwind.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * $Id: minidriver_worldwind.h 18020 2009-11-14 14:33:20Z rouault $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +H_GDALWMSMiniDriverFactory(WorldWind) + +class GDALWMSMiniDriver_WorldWind : public GDALWMSMiniDriver { +public: + GDALWMSMiniDriver_WorldWind(); + virtual ~GDALWMSMiniDriver_WorldWind(); + +public: + virtual CPLErr Initialize(CPLXMLNode *config); + virtual void GetCapabilities(GDALWMSMiniDriverCapabilities *caps); + virtual void ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri); + virtual void TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri); + virtual const char *GetProjectionInWKT(); + +protected: + CPLString m_base_url; + CPLString m_dataset; + CPLString m_projection_wkt; +}; diff --git a/bazaar/plugin/gdal/frmts/wms/wmsdriver.cpp b/bazaar/plugin/gdal/frmts/wms/wmsdriver.cpp new file mode 100644 index 000000000..54cfca4cf --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/wmsdriver.cpp @@ -0,0 +1,929 @@ +/****************************************************************************** + * $Id: wmsdriver.cpp 28911 2015-04-15 14:46:06Z bishop $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * Copyright (c) 2009-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" +#include "wmsmetadataset.h" + +#include "minidriver_wms.h" +#include "minidriver_tileservice.h" +#include "minidriver_worldwind.h" +#include "minidriver_tms.h" +#include "minidriver_tiled_wms.h" +#include "minidriver_virtualearth.h" +#include "minidriver_arcgis_server.h" + +/************************************************************************/ +/* GDALWMSDatasetGetConfigFromURL() */ +/************************************************************************/ + +static +CPLXMLNode * GDALWMSDatasetGetConfigFromURL(GDALOpenInfo *poOpenInfo) +{ + const char* pszBaseURL = poOpenInfo->pszFilename; + if (EQUALN(pszBaseURL, "WMS:", 4)) + pszBaseURL += 4; + + CPLString osLayer = CPLURLGetValue(pszBaseURL, "LAYERS"); + CPLString osVersion = CPLURLGetValue(pszBaseURL, "VERSION"); + CPLString osSRS = CPLURLGetValue(pszBaseURL, "SRS"); + CPLString osCRS = CPLURLGetValue(pszBaseURL, "CRS"); + CPLString osBBOX = CPLURLGetValue(pszBaseURL, "BBOX"); + CPLString osFormat = CPLURLGetValue(pszBaseURL, "FORMAT"); + CPLString osTransparent = CPLURLGetValue(pszBaseURL, "TRANSPARENT"); + + /* GDAL specific extensions to alter the default settings */ + CPLString osOverviewCount = CPLURLGetValue(pszBaseURL, "OVERVIEWCOUNT"); + CPLString osTileSize = CPLURLGetValue(pszBaseURL, "TILESIZE"); + CPLString osMinResolution = CPLURLGetValue(pszBaseURL, "MINRESOLUTION"); + CPLString osBBOXOrder = CPLURLGetValue(pszBaseURL, "BBOXORDER"); + + CPLString osBaseURL = pszBaseURL; + /* Remove all keywords to get base URL */ + + osBaseURL = CPLURLAddKVP(osBaseURL, "VERSION", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "REQUEST", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "LAYERS", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "SRS", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "CRS", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "BBOX", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "FORMAT", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "TRANSPARENT", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "STYLES", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "WIDTH", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "HEIGHT", NULL); + + osBaseURL = CPLURLAddKVP(osBaseURL, "OVERVIEWCOUNT", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "TILESIZE", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "MINRESOLUTION", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "BBOXORDER", NULL); + + if (osBaseURL.size() > 0 && osBaseURL[osBaseURL.size() - 1] == '&') + osBaseURL.resize(osBaseURL.size() - 1); + + if (osVersion.size() == 0) + osVersion = "1.1.1"; + + CPLString osSRSTag; + CPLString osSRSValue; + if(VersionStringToInt(osVersion.c_str())>= VersionStringToInt("1.3.0")) + { + if (osSRS.size()) + { + CPLError(CE_Warning, CPLE_AppDefined, + "WMS version 1.3 and above expects CRS however SRS was set instead."); + } + osSRSValue = osCRS; + osSRSTag = "CRS"; + } + else + { + if (osCRS.size()) + { + CPLError(CE_Warning, CPLE_AppDefined, + "WMS version 1.1.1 and below expects SRS however CRS was set instead."); + } + osSRSValue = osSRS; + osSRSTag = "SRS"; + } + + if (osSRSValue.size() == 0) + osSRSValue = "EPSG:4326"; + + if (osBBOX.size() == 0) + { + if (osBBOXOrder.compare("yxYX") == 0) + osBBOX = "-90,-180,90,180"; + else + osBBOX = "-180,-90,180,90"; + } + + char** papszTokens = CSLTokenizeStringComplex(osBBOX, ",", 0, 0); + if (CSLCount(papszTokens) != 4) + { + CSLDestroy(papszTokens); + return NULL; + } + const char* pszMinX = papszTokens[0]; + const char* pszMinY = papszTokens[1]; + const char* pszMaxX = papszTokens[2]; + const char* pszMaxY = papszTokens[3]; + + if (osBBOXOrder.compare("yxYX") == 0) + { + std::swap(pszMinX, pszMinY); + std::swap(pszMaxX, pszMaxY); + } + + double dfMinX = CPLAtofM(pszMinX); + double dfMinY = CPLAtofM(pszMinY); + double dfMaxX = CPLAtofM(pszMaxX); + double dfMaxY = CPLAtofM(pszMaxY); + + if (dfMaxY <= dfMinY || dfMaxX <= dfMinX) + { + CSLDestroy(papszTokens); + return NULL; + } + + int nTileSize = atoi(osTileSize); + if (nTileSize <= 128 || nTileSize > 2048) + nTileSize = 1024; + + int nXSize, nYSize; + + int nOverviewCount = (osOverviewCount.size()) ? atoi(osOverviewCount) : 20; + + if (osMinResolution.size() != 0) + { + double dfMinResolution = CPLAtofM(osMinResolution); + + while (nOverviewCount > 20) + { + nOverviewCount --; + dfMinResolution *= 2; + } + + nXSize = (int) ((dfMaxX - dfMinX) / dfMinResolution + 0.5); + nYSize = (int) ((dfMaxY - dfMinY) / dfMinResolution + 0.5); + } + else + { + double dfRatio = (dfMaxX - dfMinX) / (dfMaxY - dfMinY); + if (dfRatio > 1) + { + nXSize = nTileSize; + nYSize = (int) (nXSize / dfRatio); + } + else + { + nYSize = nTileSize; + nXSize = (int) (nYSize * dfRatio); + } + + if (nOverviewCount < 0 || nOverviewCount > 20) + nOverviewCount = 20; + + nXSize = nXSize * (1 << nOverviewCount); + nYSize = nYSize * (1 << nOverviewCount); + } + + int bTransparent = osTransparent.size() ? CSLTestBoolean(osTransparent) : FALSE; + + if (osFormat.size() == 0) + { + if (!bTransparent) + { + osFormat = "image/jpeg"; + } + else + { + osFormat = "image/png"; + } + } + + char* pszEscapedURL = CPLEscapeString(osBaseURL.c_str(), -1, CPLES_XML); + char* pszEscapedLayerXML = CPLEscapeString(osLayer.c_str(), -1, CPLES_XML); + + CPLString osXML = CPLSPrintf( + "\n" + " \n" + " %s\n" + " %s\n" + " %s\n" + " <%s>%s\n" + " %s\n" + " %s\n" + " %s\n" + " \n" + " \n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %d\n" + " %d\n" + " \n" + " %d\n" + " %d\n" + " %d\n" + " %d\n" + "\n", + osVersion.c_str(), + pszEscapedURL, + pszEscapedLayerXML, + osSRSTag.c_str(), + osSRSValue.c_str(), + osSRSTag.c_str(), + osFormat.c_str(), + (bTransparent) ? "TRUE" : "FALSE", + (osBBOXOrder.size()) ? osBBOXOrder.c_str() : "xyXY", + pszMinX, pszMaxY, pszMaxX, pszMinY, + nXSize, nYSize, + (bTransparent) ? 4 : 3, + nTileSize, nTileSize, + nOverviewCount); + + CPLFree(pszEscapedURL); + CPLFree(pszEscapedLayerXML); + + CSLDestroy(papszTokens); + + CPLDebug("WMS", "Opening WMS :\n%s", osXML.c_str()); + + return CPLParseXMLString(osXML); +} + +/************************************************************************/ +/* GDALWMSDatasetGetConfigFromTileMap() */ +/************************************************************************/ + +static +CPLXMLNode * GDALWMSDatasetGetConfigFromTileMap(CPLXMLNode* psXML) +{ + CPLXMLNode* psRoot = CPLGetXMLNode( psXML, "=TileMap" ); + if (psRoot == NULL) + return NULL; + + CPLXMLNode* psTileSets = CPLGetXMLNode(psRoot, "TileSets"); + if (psTileSets == NULL) + return NULL; + + const char* pszURL = CPLGetXMLValue(psRoot, "tilemapservice", NULL); + + int bCanChangeURL = TRUE; + + CPLString osURL; + if (pszURL) + { + osURL = pszURL; + /* Special hack for http://tilecache.osgeo.org/wms-c/Basic.py/1.0.0/basic/ */ + if (strlen(pszURL) > 10 && + strncmp(pszURL, "http://tilecache.osgeo.org/wms-c/Basic.py/1.0.0/", + strlen("http://tilecache.osgeo.org/wms-c/Basic.py/1.0.0/")) == 0 && + strcmp(pszURL + strlen(pszURL) - strlen("1.0.0/"), "1.0.0/") == 0) + { + osURL.resize(strlen(pszURL) - strlen("1.0.0/")); + bCanChangeURL = FALSE; + } + osURL += "${z}/${x}/${y}.${format}"; + } + + const char* pszSRS = CPLGetXMLValue(psRoot, "SRS", NULL); + if (pszSRS == NULL) + return NULL; + + CPLXMLNode* psBoundingBox = CPLGetXMLNode( psRoot, "BoundingBox" ); + if (psBoundingBox == NULL) + return NULL; + + const char* pszMinX = CPLGetXMLValue(psBoundingBox, "minx", NULL); + const char* pszMinY = CPLGetXMLValue(psBoundingBox, "miny", NULL); + const char* pszMaxX = CPLGetXMLValue(psBoundingBox, "maxx", NULL); + const char* pszMaxY = CPLGetXMLValue(psBoundingBox, "maxy", NULL); + if (pszMinX == NULL || pszMinY == NULL || pszMaxX == NULL || pszMaxY == NULL) + return NULL; + + double dfMinX = CPLAtofM(pszMinX); + double dfMinY = CPLAtofM(pszMinY); + double dfMaxX = CPLAtofM(pszMaxX); + double dfMaxY = CPLAtofM(pszMaxY); + if (dfMaxY <= dfMinY || dfMaxX <= dfMinX) + return NULL; + + CPLXMLNode* psTileFormat = CPLGetXMLNode( psRoot, "TileFormat" ); + if (psTileFormat == NULL) + return NULL; + + const char* pszTileWidth = CPLGetXMLValue(psTileFormat, "width", NULL); + const char* pszTileHeight = CPLGetXMLValue(psTileFormat, "height", NULL); + const char* pszTileFormat = CPLGetXMLValue(psTileFormat, "extension", NULL); + if (pszTileWidth == NULL || pszTileHeight == NULL || pszTileFormat == NULL) + return NULL; + + int nTileWidth = atoi(pszTileWidth); + int nTileHeight = atoi(pszTileHeight); + if (nTileWidth < 128 || nTileHeight < 128) + return NULL; + + CPLXMLNode* psIter = psTileSets->psChild; + int nLevelCount = 0; + double dfPixelSize = 0; + for(; psIter != NULL; psIter = psIter->psNext) + { + if (psIter->eType == CXT_Element && + EQUAL(psIter->pszValue, "TileSet")) + { + const char* pszOrder = + CPLGetXMLValue(psIter, "order", NULL); + if (pszOrder == NULL) + { + CPLDebug("WMS", "Cannot find order attribute"); + return NULL; + } + if (atoi(pszOrder) != nLevelCount) + { + CPLDebug("WMS", "Expected order=%d, got %s", nLevelCount, pszOrder); + return NULL; + } + + const char* pszHref = + CPLGetXMLValue(psIter, "href", NULL); + if (nLevelCount == 0 && pszHref != NULL) + { + if (bCanChangeURL && strlen(pszHref) > 10 && + strcmp(pszHref + strlen(pszHref) - strlen("/0"), "/0") == 0) + { + osURL = pszHref; + osURL.resize(strlen(pszHref) - strlen("/0")); + osURL += "/${z}/${x}/${y}.${format}"; + } + } + const char* pszUnitsPerPixel = + CPLGetXMLValue(psIter, "units-per-pixel", NULL); + if (pszUnitsPerPixel == NULL) + return NULL; + dfPixelSize = CPLAtofM(pszUnitsPerPixel); + + nLevelCount++; + } + } + + if (nLevelCount == 0 || osURL.size() == 0) + return NULL; + + int nXSize = 0; + int nYSize = 0; + + while(nLevelCount > 0) + { + GIntBig nXSizeBig = (GIntBig)((dfMaxX - dfMinX) / dfPixelSize + 0.5); + GIntBig nYSizeBig = (GIntBig)((dfMaxY - dfMinY) / dfPixelSize + 0.5); + if (nXSizeBig < INT_MAX && nYSizeBig < INT_MAX) + { + nXSize = (int)nXSizeBig; + nYSize = (int)nYSizeBig; + break; + } + CPLDebug("WMS", "Dropping one overview level so raster size fits into 32bit..."); + dfPixelSize *= 2; + nLevelCount --; + } + + char* pszEscapedURL = CPLEscapeString(osURL.c_str(), -1, CPLES_XML); + + CPLString osXML = CPLSPrintf( + "\n" + " \n" + " %s\n" + " %s\n" + " \n" + " \n" + " %s\n" + " %s\n" + " %s\n" + " %s\n" + " %d\n" + " %d\n" + " %d\n" + " \n" + " %s\n" + " %d\n" + " %d\n" + " %d\n" + "\n", + pszEscapedURL, + pszTileFormat, + pszMinX, pszMaxY, pszMaxX, pszMinY, + nLevelCount - 1, + nXSize, nYSize, + pszSRS, + nTileWidth, nTileHeight, 3); + CPLDebug("WMS", "Opening TMS :\n%s", osXML.c_str()); + + CPLFree(pszEscapedURL); + + return CPLParseXMLString(osXML); +} + +/************************************************************************/ +/* GetJSonValue() */ +/************************************************************************/ + +static const char* GetJSonValue(const char* pszLine, const char* pszKey) +{ + const char* pszJSonKey = CPLSPrintf("\"%s\" : ", pszKey); + const char* pszPtr; + if( (pszPtr = strstr(pszLine, pszJSonKey)) != NULL ) + return pszPtr + strlen(pszJSonKey); + pszJSonKey = CPLSPrintf("\"%s\": ", pszKey); + if( (pszPtr = strstr(pszLine, pszJSonKey)) != NULL ) + return pszPtr + strlen(pszJSonKey); + return NULL; +} + +/************************************************************************/ +/* GDALWMSDatasetGetConfigFromArcGISJSON() */ +/************************************************************************/ + +static CPLXMLNode* GDALWMSDatasetGetConfigFromArcGISJSON(const char* pszURL, + const char* pszContent) +{ + /* TODO : use JSONC library to parse. But we don't really need it */ + CPLString osTmpFilename(CPLSPrintf("/vsimem/WMSArcGISJSON%p", pszURL)); + VSILFILE* fp = VSIFileFromMemBuffer( osTmpFilename, + (GByte*)pszContent, + strlen(pszContent), + FALSE); + const char* pszLine; + int nTileWidth = -1, nTileHeight = -1; + int nWKID = -1; + double dfMinX = 0, dfMaxY = 0; + int bHasMinX = FALSE, bHasMaxY = FALSE; + int nExpectedLevel = 0; + double dfBaseResolution = 0; + while((pszLine = CPLReadLine2L(fp, 4096, NULL)) != NULL) + { + const char* pszVal; + if ((pszVal = GetJSonValue(pszLine, "rows")) != NULL) + nTileHeight = atoi(pszVal); + else if ((pszVal = GetJSonValue(pszLine, "cols")) != NULL) + nTileWidth = atoi(pszVal); + else if ((pszVal = GetJSonValue(pszLine, "wkid")) != NULL) + { + int nVal = atoi(pszVal); + if (nWKID < 0) + nWKID = nVal; + else if (nWKID != nVal) + { + CPLDebug("WMS", "Inconsisant WKID values : %d, %d", nVal, nWKID); + VSIFCloseL(fp); + return NULL; + } + } + else if ((pszVal = GetJSonValue(pszLine, "x")) != NULL) + { + bHasMinX = TRUE; + dfMinX = CPLAtofM(pszVal); + } + else if ((pszVal = GetJSonValue(pszLine, "y")) != NULL) + { + bHasMaxY = TRUE; + dfMaxY = CPLAtofM(pszVal); + } + else if ((pszVal = GetJSonValue(pszLine, "level")) != NULL) + { + int nLevel = atoi(pszVal); + if (nLevel != nExpectedLevel) + { + CPLDebug("WMS", "Expected level : %d, got : %d", nExpectedLevel, nLevel); + VSIFCloseL(fp); + return NULL; + } + + pszVal = GetJSonValue(pszLine, "resolution"); + if( pszVal == NULL ) + { + pszLine = CPLReadLine2L(fp, 4096, NULL); + if( pszLine == NULL ) + break; + pszVal = GetJSonValue(pszLine, "resolution"); + } + if (pszVal != NULL) + { + double dfResolution = CPLAtofM(pszVal); + if (nLevel == 0) + dfBaseResolution = dfResolution; + } + else + { + CPLDebug("WMS", "Did not get resolution"); + VSIFCloseL(fp); + return NULL; + } + nExpectedLevel ++; + } + } + VSIFCloseL(fp); + + int nLevelCount = nExpectedLevel - 1; + if (nLevelCount < 1) + { + CPLDebug("WMS", "Did not get levels"); + return NULL; + } + + if (nTileWidth <= 0) + { + CPLDebug("WMS", "Did not get tile width"); + return NULL; + } + if (nTileHeight <= 0) + { + CPLDebug("WMS", "Did not get tile height"); + return NULL; + } + if (nWKID <= 0) + { + CPLDebug("WMS", "Did not get WKID"); + return NULL; + } + if (!bHasMinX) + { + CPLDebug("WMS", "Did not get min x"); + return NULL; + } + if (!bHasMaxY) + { + CPLDebug("WMS", "Did not get max y"); + return NULL; + } + + if (nWKID == 102100) + nWKID = 3857; + + const char* pszEndURL = strstr(pszURL, "/MapServer?f=json"); + CPLAssert(pszEndURL); + CPLString osURL(pszURL); + osURL.resize(pszEndURL - pszURL); + + double dfMaxX = dfMinX + dfBaseResolution * nTileWidth; + double dfMinY = dfMaxY - dfBaseResolution * nTileHeight; + + int nTileCountX = 1; + if (fabs(dfMinX - -180) < 1e-4 && fabs(dfMaxY - 90) < 1e-4 && + fabs(dfMinY - -90) < 1e-4) + { + nTileCountX = 2; + dfMaxX = 180; + } + + CPLString osXML = CPLSPrintf( + "\n" + " \n" + " %s/MapServer/tile/${z}/${y}/${x}\n" + " \n" + " \n" + " %.8f\n" + " %.8f\n" + " %.8f\n" + " %.8f\n" + " %d\n" + " %d\n" + " top\n" + " \n" + " EPSG:%d\n" + " %d\n" + " %d\n" + " \n" + "\n", + osURL.c_str(), + dfMinX, dfMaxY, dfMaxX, dfMinY, + nLevelCount, + nTileCountX, + nWKID, + nTileWidth, nTileHeight); + CPLDebug("WMS", "Opening TMS :\n%s", osXML.c_str()); + + return CPLParseXMLString(osXML); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int GDALWMSDataset::Identify(GDALOpenInfo *poOpenInfo) +{ + const char* pszFilename = poOpenInfo->pszFilename; + const char* pabyHeader = (const char *) poOpenInfo->pabyHeader; + if (poOpenInfo->nHeaderBytes == 0 && + EQUALN(pszFilename, "", 10)) + { + return TRUE; + } + else if (poOpenInfo->nHeaderBytes >= 10 && + EQUALN(pabyHeader, "", 10)) + { + return TRUE; + } + else if (poOpenInfo->nHeaderBytes == 0 && + (EQUALN(pszFilename, "WMS:", 4) || + CPLString(pszFilename).ifind("SERVICE=WMS") != std::string::npos) ) + { + return TRUE; + } + else if (poOpenInfo->nHeaderBytes != 0 && + (strstr(pabyHeader, "nHeaderBytes != 0 && + strstr(pabyHeader, "nHeaderBytes != 0 && + strstr(pabyHeader, "nHeaderBytes != 0 && + strstr(pabyHeader, "nHeaderBytes != 0 && + strstr(pabyHeader, "nHeaderBytes == 0 && + EQUALN(pszFilename, "http", 4) && + strstr(pszFilename, "/MapServer?f=json") != NULL) + { + return TRUE; + } + else if (poOpenInfo->nHeaderBytes == 0 && + EQUALN(pszFilename, "AGS:", 4)) + { + return TRUE; + } + else + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GDALWMSDataset::Open(GDALOpenInfo *poOpenInfo) +{ + CPLXMLNode *config = NULL; + CPLErr ret = CE_None; + + const char* pszFilename = poOpenInfo->pszFilename; + const char* pabyHeader = (const char *) poOpenInfo->pabyHeader; + + if (poOpenInfo->nHeaderBytes == 0 && + EQUALN(pszFilename, "", 10)) + { + config = CPLParseXMLString(pszFilename); + } + else if (poOpenInfo->nHeaderBytes >= 10 && + EQUALN(pabyHeader, "", 10)) + { + config = CPLParseXMLFile(pszFilename); + } + else if (poOpenInfo->nHeaderBytes == 0 && + (EQUALN(pszFilename, "WMS:http", 8) || + EQUALN(pszFilename, "http", 4)) && + strstr(pszFilename, "/MapServer?f=json") != NULL) + { + if (EQUALN(pszFilename, "WMS:http", 8)) + pszFilename += 4; + CPLString osURL(pszFilename); + if (strstr(pszFilename, "&pretty=true") == NULL) + osURL += "&pretty=true"; + CPLHTTPResult *psResult = CPLHTTPFetch(osURL.c_str(), NULL); + if (psResult == NULL) + return NULL; + if (psResult->pabyData == NULL) + { + CPLHTTPDestroyResult(psResult); + return NULL; + } + config = GDALWMSDatasetGetConfigFromArcGISJSON(osURL, + (const char*)psResult->pabyData); + CPLHTTPDestroyResult(psResult); + } + + else if (poOpenInfo->nHeaderBytes == 0 && + (EQUALN(pszFilename, "WMS:", 4) || + CPLString(pszFilename).ifind("SERVICE=WMS") != std::string::npos)) + { + CPLString osLayers = CPLURLGetValue(pszFilename, "LAYERS"); + CPLString osRequest = CPLURLGetValue(pszFilename, "REQUEST"); + if (osLayers.size() != 0) + config = GDALWMSDatasetGetConfigFromURL(poOpenInfo); + else if (EQUAL(osRequest, "GetTileService")) + return GDALWMSMetaDataset::DownloadGetTileService(poOpenInfo); + else + return GDALWMSMetaDataset::DownloadGetCapabilities(poOpenInfo); + } + else if (poOpenInfo->nHeaderBytes != 0 && + (strstr(pabyHeader, "nHeaderBytes != 0 && + strstr(pabyHeader, "nHeaderBytes != 0 && + strstr(pabyHeader, "nHeaderBytes != 0 && + strstr(pabyHeader, "nHeaderBytes != 0 && + strstr(pabyHeader, "nHeaderBytes == 0 && + EQUALN(pszFilename, "AGS:", 4)) + { + return NULL; + } + else + return NULL; + if (config == NULL) return NULL; + +/* -------------------------------------------------------------------- */ +/* Confirm the requested access is supported. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->eAccess == GA_Update ) + { + CPLDestroyXMLNode(config); + CPLError( CE_Failure, CPLE_NotSupported, + "The WMS poDriver does not support update access to existing" + " datasets.\n" ); + return NULL; + } + + GDALWMSDataset *ds = new GDALWMSDataset(); + ret = ds->Initialize(config); + if (ret != CE_None) { + delete ds; + ds = NULL; + } + CPLDestroyXMLNode(config); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + if (ds != NULL) + { + ds->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); + ds->SetDescription( poOpenInfo->pszFilename ); + ds->TryLoadXML(); + } + + return ds; +} +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset *GDALWMSDataset::CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + CPL_UNUSED int bStrict, + CPL_UNUSED char ** papszOptions, + CPL_UNUSED GDALProgressFunc pfnProgress, + CPL_UNUSED void * pProgressData ) +{ + if (poSrcDS->GetDriver() == NULL || + !EQUAL(poSrcDS->GetDriver()->GetDescription(), "WMS")) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Source dataset must be a WMS dataset"); + return NULL; + } + + const char* pszXML = poSrcDS->GetMetadataItem("XML", "WMS"); + if (pszXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot get XML definition of source WMS dataset"); + return NULL; + } + + VSILFILE* fp = VSIFOpenL(pszFilename, "wb"); + if (fp == NULL) + return NULL; + + VSIFWriteL(pszXML, 1, strlen(pszXML), fp); + VSIFCloseL(fp); + + GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly); + return Open(&oOpenInfo); +} + +/************************************************************************/ +/* GDALDeregister_WMS() */ +/************************************************************************/ + +static void GDALDeregister_WMS( GDALDriver * ) + +{ + DestroyWMSMiniDriverManager(); +} + +/************************************************************************/ +/* GDALRegister_WMS() */ +/************************************************************************/ + +void GDALRegister_WMS() { + GDALDriver *poDriver; + if (GDALGetDriverByName("WMS") == NULL) { + poDriver = new GDALDriver(); + + poDriver->SetDescription("WMS"); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "OGC Web Map Service"); + poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "frmt_wms.html"); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + + poDriver->pfnOpen = GDALWMSDataset::Open; + poDriver->pfnIdentify = GDALWMSDataset::Identify; + poDriver->pfnUnloadDriver = GDALDeregister_WMS; + poDriver->pfnCreateCopy = GDALWMSDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver(poDriver); + + GDALWMSMiniDriverManager *const mdm = GetGDALWMSMiniDriverManager(); + mdm->Register(new GDALWMSMiniDriverFactory_WMS()); + mdm->Register(new GDALWMSMiniDriverFactory_TileService()); + mdm->Register(new GDALWMSMiniDriverFactory_WorldWind()); + mdm->Register(new GDALWMSMiniDriverFactory_TMS()); + mdm->Register(new GDALWMSMiniDriverFactory_TiledWMS()); + mdm->Register(new GDALWMSMiniDriverFactory_VirtualEarth()); + mdm->Register(new GDALWMSMiniDriverFactory_AGS()); + } +} diff --git a/bazaar/plugin/gdal/frmts/wms/wmsdriver.h b/bazaar/plugin/gdal/frmts/wms/wmsdriver.h new file mode 100644 index 000000000..7ce6a4a80 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/wmsdriver.h @@ -0,0 +1,448 @@ +/****************************************************************************** + * $Id: wmsdriver.h 28432 2015-02-06 21:15:27Z rouault $ + * + * Project: WMS Client Driver + * Purpose: Implementation of Dataset and RasterBand classes for WMS + * and other similar services. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef WMSDRIVER_H_INCLUDED +#define WMSDRIVER_H_INCLUDED + +#include +#include +#include +#include +#include + +#include "cpl_conv.h" +#include "cpl_http.h" +#include "cpl_multiproc.h" +#include "gdal_pam.h" +#include "ogr_spatialref.h" +#include "gdalwarper.h" +#include "gdal_alg.h" + +#include "md5.h" +#include "gdalhttp.h" + +class GDALWMSDataset; +class GDALWMSRasterBand; + +/* -------------------------------------------------------------------- */ +/* Helper functions. */ +/* -------------------------------------------------------------------- */ +CPLString MD5String(const char *s); +CPLString ProjToWKT(const CPLString &proj); +void URLAppend(CPLString *url, const char *s); +void URLAppendF(CPLString *url, const char *s, ...) CPL_PRINT_FUNC_FORMAT (2, 3); +void URLAppend(CPLString *url, const CPLString &s); +CPLString BufferToVSIFile(GByte *buffer, size_t size); +CPLErr MakeDirs(const char *path); + + +int StrToBool(const char *p); +int URLSearchAndReplace (CPLString *base, const char *search, const char *fmt, ...) CPL_PRINT_FUNC_FORMAT (3, 4); +/* Convert a.b.c.d to a * 0x1000000 + b * 0x10000 + c * 0x100 + d */ +int VersionStringToInt(const char *version); + + +class GDALWMSImageRequestInfo { +public: + double m_x0, m_y0; + double m_x1, m_y1; + int m_sx, m_sy; +}; + +class GDALWMSDataWindow { +public: + double m_x0, m_y0; + double m_x1, m_y1; + int m_sx, m_sy; + int m_tx, m_ty, m_tlevel; + enum { BOTTOM = -1, DEFAULT = 0, TOP = 1 } m_y_origin; + + GDALWMSDataWindow() : m_x0(-180), m_y0(90), m_x1(180), m_y1(-90), + m_sx(-1), m_sy(-1), m_tx(0), m_ty(0), + m_tlevel(-1), m_y_origin(DEFAULT) {} +}; + +class GDALWMSTiledImageRequestInfo { +public: + int m_x, m_y; + int m_level; +}; + +/************************************************************************/ +/* Mini Driver Related */ +/************************************************************************/ + +class GDALWMSRasterIOHint { +public: + int m_x0, m_y0; + int m_sx, m_sy; + int m_overview; + bool m_valid; +}; + +class GDALWMSMiniDriverCapabilities { +public: +/* Version N capabilities require all version N and earlier variables to be set to correct values */ + int m_capabilities_version; + +/* Version 1 capabilities */ + int m_has_image_request; // 1 if ImageRequest method is implemented + int m_has_tiled_image_requeset; // 1 if TiledImageRequest method is implemented + int m_has_arb_overviews; // 1 if ImageRequest method supports arbitrary overviews / resolutions + int m_max_overview_count; // Maximum number of overviews supported if known, -1 otherwise +}; + +/* All data returned by mini-driver as pointer should remain valid for mini-driver lifetime + and should be freed by mini-driver destructor unless otherwise specified. */ +class GDALWMSMiniDriver { +friend class GDALWMSDataset; +public: + GDALWMSMiniDriver(); + virtual ~GDALWMSMiniDriver(); + +public: +/* Read mini-driver specific configuration. */ + virtual CPLErr Initialize(CPLXMLNode *config); + +public: + virtual void GetCapabilities(GDALWMSMiniDriverCapabilities *caps); + virtual void ImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri); + virtual void TiledImageRequest(CPLString *url, const GDALWMSImageRequestInfo &iri, const GDALWMSTiledImageRequestInfo &tiri); + virtual void GetTiledImageInfo(CPLString *url, + const GDALWMSImageRequestInfo &iri, + const GDALWMSTiledImageRequestInfo &tiri, + int nXInBlock, + int nYInBlock); + +/* Return data projection in WKT format, NULL or empty string if unknown */ + virtual const char *GetProjectionInWKT(); + +protected: + GDALWMSDataset *m_parent_dataset; +}; + +class GDALWMSMiniDriverFactory { +public: + GDALWMSMiniDriverFactory(); + virtual ~GDALWMSMiniDriverFactory(); + +public: + virtual GDALWMSMiniDriver* New() = 0; + virtual void Delete(GDALWMSMiniDriver *instance) = 0; + +public: + const CPLString &GetName() { + return m_name; + } + +protected: + CPLString m_name; +}; + +class GDALWMSMiniDriverManager { +public: + GDALWMSMiniDriverManager(); + ~GDALWMSMiniDriverManager(); + +public: + void Register(GDALWMSMiniDriverFactory *mdf); + GDALWMSMiniDriverFactory *Find(const CPLString &name); + +protected: + std::list m_mdfs; +}; + +#define H_GDALWMSMiniDriverFactory(name) \ +class GDALWMSMiniDriverFactory_##name : public GDALWMSMiniDriverFactory { \ +public: \ + GDALWMSMiniDriverFactory_##name(); \ + virtual ~GDALWMSMiniDriverFactory_##name(); \ + \ +public: \ + virtual GDALWMSMiniDriver* New(); \ + virtual void Delete(GDALWMSMiniDriver *instance); \ +}; + +#define CPP_GDALWMSMiniDriverFactory(name) \ + GDALWMSMiniDriverFactory_##name::GDALWMSMiniDriverFactory_##name() { \ + m_name = #name;\ +} \ + \ + GDALWMSMiniDriverFactory_##name::~GDALWMSMiniDriverFactory_##name() { \ +} \ + \ + GDALWMSMiniDriver* GDALWMSMiniDriverFactory_##name::New() { \ + return new GDALWMSMiniDriver_##name(); \ +} \ + \ + void GDALWMSMiniDriverFactory_##name::Delete(GDALWMSMiniDriver *instance) { \ + delete instance; \ +} + +/************************************************************************/ +/* GDALWMSCache */ +/************************************************************************/ + +class GDALWMSCache { +public: + GDALWMSCache(); + ~GDALWMSCache(); + +public: + CPLErr Initialize(CPLXMLNode *config); + CPLErr Write(const char *key, const CPLString &file_name); + CPLErr Read(const char *key, CPLString *file_name); + +protected: + CPLString KeyToCacheFile(const char *key); + +protected: + CPLString m_cache_path; + CPLString m_postfix; + int m_cache_depth; +}; + +/************************************************************************/ +/* GDALWMSDataset */ +/************************************************************************/ + +class GDALWMSDataset : public GDALPamDataset { + friend class GDALWMSRasterBand; + +public: + GDALWMSDataset(); + virtual ~GDALWMSDataset(); + + virtual const char *GetProjectionRef(); + virtual CPLErr SetProjection(const char *proj); + virtual CPLErr GetGeoTransform(double *gt); + virtual CPLErr SetGeoTransform(double *gt); + virtual CPLErr AdviseRead(int x0, int y0, int sx, int sy, int bsx, int bsy, GDALDataType bdt, int band_count, int *band_map, char **options); + + virtual char **GetMetadataDomainList(); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + + void SetColorTable(GDALColorTable *pct) { m_poColorTable=pct; } + + void mSetBand(int i, GDALRasterBand *band) { SetBand(i,band); }; + GDALWMSRasterBand *mGetBand(int i) { return reinterpret_cast(GetRasterBand(i)); }; + + const GDALWMSDataWindow *WMSGetDataWindow() const { + return &m_data_window; + } + + void WMSSetBlockSize(int x, int y) { + m_block_size_x=x; + m_block_size_y=y; + } + + void WMSSetRasterSize(int x, int y) { + nRasterXSize=x; + nRasterYSize=y; + } + + void WMSSetBandsCount(int count) { + nBands=count; + } + + void WMSSetClamp(bool flag=true) { + m_clamp_requests=flag; + } + + void WMSSetDataType(GDALDataType type) { + m_data_type=type; + } + + void WMSSetDataWindow(GDALWMSDataWindow &window) { + m_data_window=window; + } + + void WMSSetDefaultBlockSize(int x, int y) { + m_default_block_size_x=x; + m_default_block_size_y=y; + } + + void WMSSetDefaultDataWindowCoordinates(double x0, double y0, double x1, double y1) { + m_default_data_window.m_x0 = x0; + m_default_data_window.m_y0 = y0; + m_default_data_window.m_x1 = x1; + m_default_data_window.m_y1 = y1; + } + + void WMSSetDefaultTileCount(int tilecountx, int tilecounty) { + m_default_tile_count_x = tilecountx; + m_default_tile_count_y = tilecounty; + } + + void WMSSetDefaultTileLevel(int tlevel) { + m_default_data_window.m_tlevel = tlevel; + } + + void WMSSetDefaultOverviewCount(int overview_count) { + m_default_overview_count = overview_count; + } + + void WMSSetNeedsDataWindow(int flag) { + m_bNeedsDataWindow = flag; + } + + static void list2vec(std::vector &v,const char *pszList) { + if ((pszList==NULL)||(pszList[0]==0)) return; + char **papszTokens=CSLTokenizeString2(pszList," \t\n\r", + CSLT_STRIPLEADSPACES|CSLT_STRIPENDSPACES); + v.clear(); + for (int i=0;i vNoData, vMin, vMax; + GDALDataType m_data_type; + int m_block_size_x, m_block_size_y; + GDALWMSRasterIOHint m_hint; + int m_use_advise_read; + int m_verify_advise_read; + int m_offline_mode; + int m_http_max_conn; + int m_http_timeout; + int m_clamp_requests; + int m_unsafeSsl; + std::vector m_http_zeroblock_codes; + int m_zeroblock_on_serverexceptions; + CPLString m_osUserAgent; + CPLString m_osReferer; + CPLString m_osUserPwd; + + GDALWMSDataWindow m_default_data_window; + int m_default_block_size_x, m_default_block_size_y; + int m_default_tile_count_x, m_default_tile_count_y; + int m_default_overview_count; + + int m_bNeedsDataWindow; + + CPLString m_osXML; +}; + +/************************************************************************/ +/* GDALWMSRasterBand */ +/************************************************************************/ + +class GDALWMSRasterBand : public GDALPamRasterBand { + friend class GDALWMSDataset; + + char** BuildHTTPRequestOpts(); + void ComputeRequestInfo( GDALWMSImageRequestInfo &iri, + GDALWMSTiledImageRequestInfo &tiri, + int x, int y); + + CPLString osMetadataItem; + CPLString osMetadataItemURL; + +public: + GDALWMSRasterBand(GDALWMSDataset *parent_dataset, int band, double scale); + virtual ~GDALWMSRasterBand(); + void AddOverview(double scale); + virtual double GetNoDataValue( int * ); + virtual double GetMinimum( int * ); + virtual double GetMaximum( int * ); + virtual GDALColorTable *GetColorTable(); + virtual CPLErr AdviseRead(int x0, int y0, int sx, int sy, int bsx, int bsy, GDALDataType bdt, char **options); + + virtual GDALColorInterp GetColorInterpretation(); + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + virtual CPLErr IReadBlock(int x, int y, void *buffer); + virtual CPLErr IRasterIO(GDALRWFlag rw, int x0, int y0, int sx, int sy, void *buffer, int bsx, int bsy, GDALDataType bdt, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); + virtual int HasArbitraryOverviews(); + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int n); + + virtual char **GetMetadataDomainList(); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + +protected: + CPLErr ReadBlocks(int x, int y, void *buffer, int bx0, int by0, int bx1, int by1, int advise_read); + bool IsBlockInCache(int x, int y); + void AskMiniDriverForBlock(CPLString *url, int x, int y); + CPLErr ReadBlockFromFile(int x, int y, const char *file_name, int to_buffer_band, void *buffer, int advise_read); + CPLErr ZeroBlock(int x, int y, int to_buffer_band, void *buffer); + CPLErr ReportWMSException(const char *file_name); + +protected: + GDALWMSDataset *m_parent_dataset; + double m_scale; + std::vector m_overviews; + int m_overview; + GDALColorInterp m_color_interp; +}; + +GDALWMSMiniDriverManager *GetGDALWMSMiniDriverManager(); +void DestroyWMSMiniDriverManager(void); + +#endif /* notdef WMSDRIVER_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/frmts/wms/wmsmetadataset.cpp b/bazaar/plugin/gdal/frmts/wms/wmsmetadataset.cpp new file mode 100644 index 000000000..777e8e931 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/wmsmetadataset.cpp @@ -0,0 +1,763 @@ +/****************************************************************************** + * $Id: wmsmetadataset.cpp 27892 2014-10-20 22:02:57Z rouault $ + * + * Project: WMS Client Driver + * Purpose: Definition of GDALWMSMetaDataset class + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsmetadataset.h" + +int VersionStringToInt(const char *version); + +/************************************************************************/ +/* GDALWMSMetaDataset() */ +/************************************************************************/ + +GDALWMSMetaDataset::GDALWMSMetaDataset() : papszSubDatasets(NULL) +{ +} + +/************************************************************************/ +/* ~GDALWMSMetaDataset() */ +/************************************************************************/ + +GDALWMSMetaDataset::~GDALWMSMetaDataset() +{ + CSLDestroy(papszSubDatasets); +} + +/************************************************************************/ +/* AddSubDataset() */ +/************************************************************************/ + +void GDALWMSMetaDataset::AddSubDataset(const char* pszName, + const char* pszDesc) +{ + char szName[80]; + int nCount = CSLCount(papszSubDatasets ) / 2; + + sprintf( szName, "SUBDATASET_%d_NAME", nCount+1 ); + papszSubDatasets = + CSLSetNameValue( papszSubDatasets, szName, pszName ); + + sprintf( szName, "SUBDATASET_%d_DESC", nCount+1 ); + papszSubDatasets = + CSLSetNameValue( papszSubDatasets, szName, pszDesc); +} + +/************************************************************************/ +/* DownloadGetCapabilities() */ +/************************************************************************/ + +GDALDataset *GDALWMSMetaDataset::DownloadGetCapabilities(GDALOpenInfo *poOpenInfo) +{ + const char* pszURL = poOpenInfo->pszFilename; + if (EQUALN(pszURL, "WMS:", 4)) + pszURL += 4; + + CPLString osFormat = CPLURLGetValue(pszURL, "FORMAT"); + CPLString osTransparent = CPLURLGetValue(pszURL, "TRANSPARENT"); + CPLString osVersion = CPLURLGetValue(pszURL, "VERSION"); + CPLString osPreferredSRS = CPLURLGetValue(pszURL, "SRS"); + if( osPreferredSRS.size() == 0 ) + osPreferredSRS = CPLURLGetValue(pszURL, "CRS"); + + if (osVersion.size() == 0) + osVersion = "1.1.1"; + + CPLString osURL(pszURL); + osURL = CPLURLAddKVP(osURL, "SERVICE", "WMS"); + osURL = CPLURLAddKVP(osURL, "VERSION", osVersion); + osURL = CPLURLAddKVP(osURL, "REQUEST", "GetCapabilities"); + /* Remove all other keywords */ + osURL = CPLURLAddKVP(osURL, "LAYERS", NULL); + osURL = CPLURLAddKVP(osURL, "SRS", NULL); + osURL = CPLURLAddKVP(osURL, "CRS", NULL); + osURL = CPLURLAddKVP(osURL, "BBOX", NULL); + osURL = CPLURLAddKVP(osURL, "FORMAT", NULL); + osURL = CPLURLAddKVP(osURL, "TRANSPARENT", NULL); + osURL = CPLURLAddKVP(osURL, "STYLES", NULL); + osURL = CPLURLAddKVP(osURL, "WIDTH", NULL); + osURL = CPLURLAddKVP(osURL, "HEIGHT", NULL); + + CPLHTTPResult* psResult = CPLHTTPFetch( osURL, NULL ); + if (psResult == NULL) + { + return NULL; + } + if (psResult->nStatus != 0 || psResult->pszErrBuf != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error returned by server : %s (%d)", + (psResult->pszErrBuf) ? psResult->pszErrBuf : "unknown", + psResult->nStatus); + CPLHTTPDestroyResult(psResult); + return NULL; + } + if (psResult->pabyData == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Empty content returned by server"); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + CPLXMLNode* psXML = CPLParseXMLString( (const char*) psResult->pabyData ); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + GDALDataset* poRet = AnalyzeGetCapabilities(psXML, osFormat, osTransparent, osPreferredSRS); + + CPLHTTPDestroyResult(psResult); + CPLDestroyXMLNode( psXML ); + + return poRet; +} + + +/************************************************************************/ +/* DownloadGetTileService() */ +/************************************************************************/ + +GDALDataset *GDALWMSMetaDataset::DownloadGetTileService(GDALOpenInfo *poOpenInfo) +{ + const char* pszURL = poOpenInfo->pszFilename; + if (EQUALN(pszURL, "WMS:", 4)) + pszURL += 4; + + CPLString osURL(pszURL); + osURL = CPLURLAddKVP(osURL, "SERVICE", "WMS"); + osURL = CPLURLAddKVP(osURL, "REQUEST", "GetTileService"); + /* Remove all other keywords */ + osURL = CPLURLAddKVP(osURL, "VERSION", NULL); + osURL = CPLURLAddKVP(osURL, "LAYERS", NULL); + osURL = CPLURLAddKVP(osURL, "SRS", NULL); + osURL = CPLURLAddKVP(osURL, "CRS", NULL); + osURL = CPLURLAddKVP(osURL, "BBOX", NULL); + osURL = CPLURLAddKVP(osURL, "FORMAT", NULL); + osURL = CPLURLAddKVP(osURL, "TRANSPARENT", NULL); + osURL = CPLURLAddKVP(osURL, "STYLES", NULL); + osURL = CPLURLAddKVP(osURL, "WIDTH", NULL); + osURL = CPLURLAddKVP(osURL, "HEIGHT", NULL); + + CPLHTTPResult* psResult = CPLHTTPFetch( osURL, NULL ); + if (psResult == NULL) + { + return NULL; + } + if (psResult->nStatus != 0 || psResult->pszErrBuf != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error returned by server : %s (%d)", + (psResult->pszErrBuf) ? psResult->pszErrBuf : "unknown", + psResult->nStatus); + CPLHTTPDestroyResult(psResult); + return NULL; + } + if (psResult->pabyData == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Empty content returned by server"); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + CPLXMLNode* psXML = CPLParseXMLString( (const char*) psResult->pabyData ); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + GDALDataset* poRet = AnalyzeGetTileService(psXML); + + CPLHTTPDestroyResult(psResult); + CPLDestroyXMLNode( psXML ); + + return poRet; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **GDALWMSMetaDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "SUBDATASETS", NULL); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GDALWMSMetaDataset::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain != NULL && EQUAL(pszDomain,"SUBDATASETS") ) + return papszSubDatasets; + + return GDALPamDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* AddSubDataset() */ +/************************************************************************/ + +void GDALWMSMetaDataset::AddSubDataset( const char* pszLayerName, + const char* pszTitle, + CPL_UNUSED const char* pszAbstract, + const char* pszSRS, + const char* pszMinX, + const char* pszMinY, + const char* pszMaxX, + const char* pszMaxY, + CPLString osFormat, + CPLString osTransparent) +{ + CPLString osSubdatasetName = "WMS:"; + osSubdatasetName += osGetURL; + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "SERVICE", "WMS"); + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "VERSION", osVersion); + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "REQUEST", "GetMap"); + char* pszEscapedLayerName = CPLEscapeString(pszLayerName, -1, CPLES_URL); + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "LAYERS", pszEscapedLayerName); + CPLFree(pszEscapedLayerName); + if(VersionStringToInt(osVersion.c_str())>= VersionStringToInt("1.3.0")) + { + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "CRS", pszSRS); + /* FIXME: this should apply to all SRS that need axis inversion */ + if (strcmp(pszSRS, "EPSG:4326") == 0) + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "BBOXORDER", "yxYX"); + } + else + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "SRS", pszSRS); + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "BBOX", + CPLSPrintf("%s,%s,%s,%s", pszMinX, pszMinY, pszMaxX, pszMaxY)); + if (osFormat.size() != 0) + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "FORMAT", + osFormat); + if (osTransparent.size() != 0) + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "TRANSPARENT", + osTransparent); + + if (pszTitle) + { + if (osXMLEncoding.size() != 0 && + osXMLEncoding != "utf-8" && + osXMLEncoding != "UTF-8") + { + char* pszRecodedTitle = CPLRecode(pszTitle, osXMLEncoding.c_str(), + CPL_ENC_UTF8); + if (pszRecodedTitle) + AddSubDataset(osSubdatasetName, pszRecodedTitle); + else + AddSubDataset(osSubdatasetName, pszTitle); + CPLFree(pszRecodedTitle); + } + else + { + AddSubDataset(osSubdatasetName, pszTitle); + } + } + else + { + AddSubDataset(osSubdatasetName, pszLayerName); + } +} + + +/************************************************************************/ +/* AddWMSCSubDataset() */ +/************************************************************************/ + +void GDALWMSMetaDataset::AddWMSCSubDataset(WMSCTileSetDesc& oWMSCTileSetDesc, + const char* pszTitle, + CPLString osTransparent) +{ + CPLString osSubdatasetName = "WMS:"; + osSubdatasetName += osGetURL; + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "SERVICE", "WMS"); + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "VERSION", osVersion); + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "REQUEST", "GetMap"); + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "LAYERS", oWMSCTileSetDesc.osLayers); + if(VersionStringToInt(osVersion.c_str())>= VersionStringToInt("1.3.0")) + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "CRS", oWMSCTileSetDesc.osSRS); + else + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "SRS", oWMSCTileSetDesc.osSRS); + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "BBOX", + CPLSPrintf("%s,%s,%s,%s", oWMSCTileSetDesc.osMinX.c_str(), + oWMSCTileSetDesc.osMinY.c_str(), + oWMSCTileSetDesc.osMaxX.c_str(), + oWMSCTileSetDesc.osMaxY.c_str())); + + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "FORMAT", oWMSCTileSetDesc.osFormat); + if (osTransparent.size() != 0) + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "TRANSPARENT", + osTransparent); + if (oWMSCTileSetDesc.nTileWidth != oWMSCTileSetDesc.nTileHeight) + CPLDebug("WMS", "Weird: nTileWidth != nTileHeight for %s", + oWMSCTileSetDesc.osLayers.c_str()); + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "TILESIZE", + CPLSPrintf("%d", oWMSCTileSetDesc.nTileWidth)); + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "OVERVIEWCOUNT", + CPLSPrintf("%d", oWMSCTileSetDesc.nResolutions - 1)); + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "MINRESOLUTION", + CPLSPrintf("%.16f", oWMSCTileSetDesc.dfMinResolution)); + osSubdatasetName = CPLURLAddKVP(osSubdatasetName, "TILED", "true"); + + if (pszTitle) + { + if (osXMLEncoding.size() != 0 && + osXMLEncoding != "utf-8" && + osXMLEncoding != "UTF-8") + { + char* pszRecodedTitle = CPLRecode(pszTitle, osXMLEncoding.c_str(), + CPL_ENC_UTF8); + if (pszRecodedTitle) + AddSubDataset(osSubdatasetName, pszRecodedTitle); + else + AddSubDataset(osSubdatasetName, pszTitle); + CPLFree(pszRecodedTitle); + } + else + { + AddSubDataset(osSubdatasetName, pszTitle); + } + } + else + { + AddSubDataset(osSubdatasetName, oWMSCTileSetDesc.osLayers); + } +} + +/************************************************************************/ +/* ExploreLayer() */ +/************************************************************************/ + +void GDALWMSMetaDataset::ExploreLayer(CPLXMLNode* psXML, + CPLString osFormat, + CPLString osTransparent, + CPLString osPreferredSRS, + const char* pszSRS, + const char* pszMinX, + const char* pszMinY, + const char* pszMaxX, + const char* pszMaxY) +{ + const char* pszName = CPLGetXMLValue(psXML, "Name", NULL); + const char* pszTitle = CPLGetXMLValue(psXML, "Title", NULL); + const char* pszAbstract = CPLGetXMLValue(psXML, "Abstract", NULL); + + CPLXMLNode* psSRS = NULL; + const char* pszSRSLocal = NULL; + const char* pszMinXLocal = NULL; + const char* pszMinYLocal = NULL; + const char* pszMaxXLocal = NULL; + const char* pszMaxYLocal = NULL; + + const char* pszSRSTagName = + VersionStringToInt(osVersion.c_str()) >= VersionStringToInt("1.3.0") ? "CRS" : "SRS"; + + /* Use local bounding box if available, otherwise use the one */ + /* that comes from an upper layer */ + /* such as in http://neowms.sci.gsfc.nasa.gov/wms/wms */ + CPLXMLNode* psIter = psXML->psChild; + while( psIter != NULL ) + { + if( psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "BoundingBox") == 0 ) + { + psSRS = psIter; + pszSRSLocal = CPLGetXMLValue(psSRS, pszSRSTagName, NULL); + if( osPreferredSRS.size() == 0 || pszSRSLocal == NULL ) + break; + if( EQUAL(osPreferredSRS, pszSRSLocal) ) + break; + psSRS = NULL; + pszSRSLocal = NULL; + } + psIter = psIter->psNext; + } + + if (psSRS == NULL) + { + psSRS = CPLGetXMLNode( psXML, "LatLonBoundingBox" ); + pszSRSLocal = CPLGetXMLValue(psXML, pszSRSTagName, NULL); + if (pszSRSLocal == NULL) + pszSRSLocal = "EPSG:4326"; + } + + + if (pszSRSLocal != NULL && psSRS != NULL) + { + pszMinXLocal = CPLGetXMLValue(psSRS, "minx", NULL); + pszMinYLocal = CPLGetXMLValue(psSRS, "miny", NULL); + pszMaxXLocal = CPLGetXMLValue(psSRS, "maxx", NULL); + pszMaxYLocal = CPLGetXMLValue(psSRS, "maxy", NULL); + + if (pszMinXLocal && pszMinYLocal && pszMaxXLocal && pszMaxYLocal) + { + pszSRS = pszSRSLocal; + pszMinX = pszMinXLocal; + pszMinY = pszMinYLocal; + pszMaxX = pszMaxXLocal; + pszMaxY = pszMaxYLocal; + } + } + + if (pszName != NULL && pszSRS && pszMinX && pszMinY && pszMaxX && pszMaxY) + { + CPLString osLocalTransparent(osTransparent); + if (osLocalTransparent.size() == 0) + { + const char* pszOpaque = CPLGetXMLValue(psXML, "opaque", "0"); + if (EQUAL(pszOpaque, "1")) + osLocalTransparent = "FALSE"; + } + + WMSCKeyType oWMSCKey(pszName, pszSRS); + std::map::iterator oIter = osMapWMSCTileSet.find(oWMSCKey); + if (oIter != osMapWMSCTileSet.end()) + { + AddWMSCSubDataset(oIter->second, pszTitle, osLocalTransparent); + } + else + { + AddSubDataset(pszName, pszTitle, pszAbstract, + pszSRS, pszMinX, pszMinY, + pszMaxX, pszMaxY, osFormat, osLocalTransparent); + } + } + + psIter = psXML->psChild; + for(; psIter != NULL; psIter = psIter->psNext) + { + if (psIter->eType == CXT_Element) + { + if (EQUAL(psIter->pszValue, "Layer")) + ExploreLayer(psIter, osFormat, osTransparent, osPreferredSRS, + pszSRS, pszMinX, pszMinY, pszMaxX, pszMaxY); + } + } +} + +/************************************************************************/ +/* ParseWMSCTileSets() */ +/************************************************************************/ + +void GDALWMSMetaDataset::ParseWMSCTileSets(CPLXMLNode* psXML) +{ + CPLXMLNode* psIter = psXML->psChild; + for(;psIter;psIter = psIter->psNext) + { + if (psIter->eType == CXT_Element && EQUAL(psIter->pszValue, "TileSet")) + { + const char* pszSRS = CPLGetXMLValue(psIter, "SRS", NULL); + if (pszSRS == NULL) + continue; + + CPLXMLNode* psBoundingBox = CPLGetXMLNode( psIter, "BoundingBox" ); + if (psBoundingBox == NULL) + continue; + + const char* pszMinX = CPLGetXMLValue(psBoundingBox, "minx", NULL); + const char* pszMinY = CPLGetXMLValue(psBoundingBox, "miny", NULL); + const char* pszMaxX = CPLGetXMLValue(psBoundingBox, "maxx", NULL); + const char* pszMaxY = CPLGetXMLValue(psBoundingBox, "maxy", NULL); + if (pszMinX == NULL || pszMinY == NULL || pszMaxX == NULL || pszMaxY == NULL) + continue; + + double dfMinX = CPLAtofM(pszMinX); + double dfMinY = CPLAtofM(pszMinY); + double dfMaxX = CPLAtofM(pszMaxX); + double dfMaxY = CPLAtofM(pszMaxY); + if (dfMaxY <= dfMinY || dfMaxX <= dfMinX) + continue; + + const char* pszFormat = CPLGetXMLValue( psIter, "Format", NULL ); + if (pszFormat == NULL) + continue; + if (strstr(pszFormat, "kml")) + continue; + + const char* pszTileWidth = CPLGetXMLValue(psIter, "Width", NULL); + const char* pszTileHeight = CPLGetXMLValue(psIter, "Height", NULL); + if (pszTileWidth == NULL || pszTileHeight == NULL) + continue; + + int nTileWidth = atoi(pszTileWidth); + int nTileHeight = atoi(pszTileHeight); + if (nTileWidth < 128 || nTileHeight < 128) + continue; + + const char* pszLayers = CPLGetXMLValue(psIter, "Layers", NULL); + if (pszLayers == NULL) + continue; + + const char* pszResolutions = CPLGetXMLValue(psIter, "Resolutions", NULL); + if (pszResolutions == NULL) + continue; + char** papszTokens = CSLTokenizeStringComplex(pszResolutions, " ", 0, 0); + double dfMinResolution = 0; + int i; + for(i=0; papszTokens && papszTokens[i]; i++) + { + double dfResolution = CPLAtofM(papszTokens[i]); + if (i==0 || dfResolution < dfMinResolution) + dfMinResolution = dfResolution; + } + CSLDestroy(papszTokens); + int nResolutions = i; + if (nResolutions == 0) + continue; + + const char* pszStyles = CPLGetXMLValue(psIter, "Styles", ""); + + /* http://demo.opengeo.org/geoserver/gwc/service/wms?tiled=TRUE&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities */ + /* has different variations of formats for the same (formats, SRS) tuple, so just */ + /* keep the first one which is a png format */ + WMSCKeyType oWMSCKey(pszLayers, pszSRS); + std::map::iterator oIter = osMapWMSCTileSet.find(oWMSCKey); + if (oIter != osMapWMSCTileSet.end()) + continue; + + WMSCTileSetDesc oWMSCTileSet; + oWMSCTileSet.osLayers = pszLayers; + oWMSCTileSet.osSRS = pszSRS; + oWMSCTileSet.osMinX = pszMinX; + oWMSCTileSet.osMinY = pszMinY; + oWMSCTileSet.osMaxX = pszMaxX; + oWMSCTileSet.osMaxY = pszMaxY; + oWMSCTileSet.dfMinX = dfMinX; + oWMSCTileSet.dfMinY = dfMinY; + oWMSCTileSet.dfMaxX = dfMaxX; + oWMSCTileSet.dfMaxY = dfMaxY; + oWMSCTileSet.nResolutions = nResolutions; + oWMSCTileSet.dfMinResolution = dfMinResolution; + oWMSCTileSet.osFormat = pszFormat; + oWMSCTileSet.osStyle = pszStyles; + oWMSCTileSet.nTileWidth = nTileWidth; + oWMSCTileSet.nTileHeight = nTileHeight; + + osMapWMSCTileSet[oWMSCKey] = oWMSCTileSet; + } + } +} + +/************************************************************************/ +/* AnalyzeGetCapabilities() */ +/************************************************************************/ + +GDALDataset* GDALWMSMetaDataset::AnalyzeGetCapabilities(CPLXMLNode* psXML, + CPLString osFormat, + CPLString osTransparent, + CPLString osPreferredSRS) +{ + const char* pszEncoding = NULL; + if (psXML->eType == CXT_Element && strcmp(psXML->pszValue, "?xml") == 0) + pszEncoding = CPLGetXMLValue(psXML, "encoding", NULL); + + CPLXMLNode* psRoot = CPLGetXMLNode( psXML, "=WMT_MS_Capabilities" ); + if (psRoot == NULL) + psRoot = CPLGetXMLNode( psXML, "=WMS_Capabilities" ); + if (psRoot == NULL) + return NULL; + CPLXMLNode* psCapability = CPLGetXMLNode(psRoot, "Capability"); + if (psCapability == NULL) + return NULL; + + CPLXMLNode* psOnlineResource = CPLGetXMLNode(psCapability, + "Request.GetMap.DCPType.HTTP.Get.OnlineResource"); + if (psOnlineResource == NULL) + return NULL; + const char* pszGetURL = + CPLGetXMLValue(psOnlineResource, "xlink:href", NULL); + if (pszGetURL == NULL) + return NULL; + + CPLXMLNode* psLayer = CPLGetXMLNode(psCapability, "Layer"); + if (psLayer == NULL) + return NULL; + + CPLXMLNode* psVendorSpecificCapabilities = + CPLGetXMLNode(psCapability, "VendorSpecificCapabilities"); + + GDALWMSMetaDataset* poDS = new GDALWMSMetaDataset(); + const char* pszVersion = CPLGetXMLValue(psRoot, "version", NULL); + if (pszVersion) + poDS->osVersion = pszVersion; + else + poDS->osVersion = "1.1.1"; + poDS->osGetURL = pszGetURL; + poDS->osXMLEncoding = pszEncoding ? pszEncoding : ""; + if (psVendorSpecificCapabilities) + poDS->ParseWMSCTileSets(psVendorSpecificCapabilities); + poDS->ExploreLayer(psLayer, osFormat, osTransparent, osPreferredSRS); + + return poDS; +} + +/************************************************************************/ +/* AddTiledSubDataset() */ +/************************************************************************/ + +void GDALWMSMetaDataset::AddTiledSubDataset(const char* pszTiledGroupName, + const char* pszTitle) +{ + CPLString osSubdatasetName = ""; + osSubdatasetName += osGetURL; + osSubdatasetName += ""; + osSubdatasetName += pszTiledGroupName; + osSubdatasetName += ""; + + if (pszTitle) + { + if (osXMLEncoding.size() != 0 && + osXMLEncoding != "utf-8" && + osXMLEncoding != "UTF-8") + { + char* pszRecodedTitle = CPLRecode(pszTitle, osXMLEncoding.c_str(), + CPL_ENC_UTF8); + if (pszRecodedTitle) + AddSubDataset(osSubdatasetName, pszRecodedTitle); + else + AddSubDataset(osSubdatasetName, pszTitle); + CPLFree(pszRecodedTitle); + } + else + { + AddSubDataset(osSubdatasetName, pszTitle); + } + } + else + { + AddSubDataset(osSubdatasetName, pszTiledGroupName); + } +} + +/************************************************************************/ +/* AnalyzeGetTileServiceRecurse() */ +/************************************************************************/ + +void GDALWMSMetaDataset::AnalyzeGetTileServiceRecurse(CPLXMLNode* psXML) +{ + CPLXMLNode* psIter = psXML->psChild; + for(; psIter != NULL; psIter = psIter->psNext) + { + if (psIter->eType == CXT_Element && + EQUAL(psIter->pszValue, "TiledGroup")) + { + const char* pszName = CPLGetXMLValue(psIter, "Name", NULL); + const char* pszTitle = CPLGetXMLValue(psIter, "Title", NULL); + if (pszName) + AddTiledSubDataset(pszName, pszTitle); + } + else if (psIter->eType == CXT_Element && + EQUAL(psIter->pszValue, "TiledGroups")) + { + AnalyzeGetTileServiceRecurse(psIter); + } + } +} + +/************************************************************************/ +/* AnalyzeGetTileService() */ +/************************************************************************/ + +GDALDataset* GDALWMSMetaDataset::AnalyzeGetTileService(CPLXMLNode* psXML) +{ + const char* pszEncoding = NULL; + if (psXML->eType == CXT_Element && strcmp(psXML->pszValue, "?xml") == 0) + pszEncoding = CPLGetXMLValue(psXML, "encoding", NULL); + + CPLXMLNode* psRoot = CPLGetXMLNode( psXML, "=WMS_Tile_Service" ); + if (psRoot == NULL) + return NULL; + CPLXMLNode* psTiledPatterns = CPLGetXMLNode(psRoot, "TiledPatterns"); + if (psTiledPatterns == NULL) + return NULL; + + const char* pszURL = CPLGetXMLValue(psTiledPatterns, + "OnlineResource.xlink:href", NULL); + if (pszURL == NULL) + return NULL; + + GDALWMSMetaDataset* poDS = new GDALWMSMetaDataset(); + poDS->osGetURL = pszURL; + poDS->osXMLEncoding = pszEncoding ? pszEncoding : ""; + + poDS->AnalyzeGetTileServiceRecurse(psTiledPatterns); + + return poDS; +} + +/************************************************************************/ +/* AnalyzeTileMapService() */ +/************************************************************************/ + +GDALDataset* GDALWMSMetaDataset::AnalyzeTileMapService(CPLXMLNode* psXML) +{ + CPLXMLNode* psRoot = CPLGetXMLNode( psXML, "=TileMapService" ); + if (psRoot == NULL) + return NULL; + CPLXMLNode* psTileMaps = CPLGetXMLNode(psRoot, "TileMaps"); + if (psTileMaps == NULL) + return NULL; + + GDALWMSMetaDataset* poDS = new GDALWMSMetaDataset(); + + CPLXMLNode* psIter = psTileMaps->psChild; + for(; psIter != NULL; psIter = psIter->psNext) + { + if (psIter->eType == CXT_Element && + EQUAL(psIter->pszValue, "TileMap")) + { + const char* pszHref = CPLGetXMLValue(psIter, "href", NULL); + const char* pszTitle = CPLGetXMLValue(psIter, "title", NULL); + if (pszHref && pszTitle) + { + CPLString osHref(pszHref); + const char* pszDup100 = strstr(pszHref, "1.0.0/1.0.0/"); + if (pszDup100) + { + osHref.resize(pszDup100 - pszHref); + osHref += pszDup100 + strlen("1.0.0/"); + } + poDS->AddSubDataset(osHref, pszTitle); + } + } + } + + return poDS; +} diff --git a/bazaar/plugin/gdal/frmts/wms/wmsmetadataset.h b/bazaar/plugin/gdal/frmts/wms/wmsmetadataset.h new file mode 100644 index 000000000..24f2634e4 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/wmsmetadataset.h @@ -0,0 +1,122 @@ +/****************************************************************************** + * $Id: wmsmetadataset.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: WMS Client Driver + * Purpose: Declaration of GDALWMSMetaDataset class + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _WMS_METADATASET_H_INCLUDED +#define _WMS_METADATASET_H_INCLUDED + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "cpl_http.h" +#include + +class WMSCTileSetDesc +{ + public: + CPLString osLayers; + CPLString osSRS; + CPLString osMinX, osMinY, osMaxX, osMaxY; + double dfMinX, dfMinY, dfMaxX, dfMaxY; + int nResolutions; + double dfMinResolution; + CPLString osFormat; + CPLString osStyle; + int nTileWidth, nTileHeight; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* GDALWMSMetaDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class GDALWMSMetaDataset : public GDALPamDataset +{ + private: + CPLString osGetURL; + CPLString osVersion; + CPLString osXMLEncoding; + char** papszSubDatasets; + + typedef std::pair WMSCKeyType; + std::map osMapWMSCTileSet; + + void AddSubDataset(const char* pszName, + const char* pszDesc); + + void AddSubDataset(const char* pszLayerName, + const char* pszTitle, + const char* pszAbstract, + const char* pszSRS, + const char* pszMinX, + const char* pszMinY, + const char* pszMaxX, + const char* pszMaxY, + CPLString osFormat, + CPLString osTransparent); + + void ExploreLayer(CPLXMLNode* psXML, + CPLString osFormat, + CPLString osTransparent, + CPLString osPreferredSRS, + const char* pszSRS = NULL, + const char* pszMinX = NULL, + const char* pszMinY = NULL, + const char* pszMaxX = NULL, + const char* pszMaxY = NULL); + + void AddTiledSubDataset(const char* pszTiledGroupName, + const char* pszTitle); + + void AnalyzeGetTileServiceRecurse(CPLXMLNode* psXML); + + void AddWMSCSubDataset(WMSCTileSetDesc& oWMSCTileSetDesc, + const char* pszTitle, + CPLString osTransparent); + + void ParseWMSCTileSets(CPLXMLNode* psXML); + + public: + GDALWMSMetaDataset(); + ~GDALWMSMetaDataset(); + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain = "" ); + + static GDALDataset* AnalyzeGetCapabilities(CPLXMLNode* psXML, + CPLString osFormat = "", + CPLString osTransparent = "", + CPLString osPreferredSRS = ""); + static GDALDataset* AnalyzeGetTileService(CPLXMLNode* psXML); + static GDALDataset* AnalyzeTileMapService(CPLXMLNode* psXML); + + static GDALDataset* DownloadGetCapabilities(GDALOpenInfo *poOpenInfo); + static GDALDataset* DownloadGetTileService(GDALOpenInfo *poOpenInfo); +}; + +#endif // _WMS_METADATASET_H_INCLUDED diff --git a/bazaar/plugin/gdal/frmts/wms/wmsutils.cpp b/bazaar/plugin/gdal/frmts/wms/wmsutils.cpp new file mode 100644 index 000000000..73d901a79 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/wms/wmsutils.cpp @@ -0,0 +1,151 @@ +/****************************************************************************** + * $Id: wmsutils.cpp 26079 2013-06-13 01:31:48Z warmerdam $ + * + * Project: WMS Client Driver + * Purpose: Supporting utility functions for GDAL WMS driver. + * Author: Adam Nowacki, nowak@xpam.de + * + ****************************************************************************** + * Copyright (c) 2007, Adam Nowacki + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "wmsdriver.h" + +CPLString MD5String(const char *s) { + unsigned char hash[16]; + char hhash[33]; + const char *tohex = "0123456789abcdef"; + struct cvs_MD5Context context; + cvs_MD5Init(&context); + cvs_MD5Update(&context, reinterpret_cast(s), strlen(s)); + cvs_MD5Final(hash, &context); + for (int i = 0; i < 16; ++i) { + hhash[i * 2] = tohex[(hash[i] >> 4) & 0xf]; + hhash[i * 2 + 1] = tohex[hash[i] & 0xf]; + } + hhash[32] = '\0'; + return CPLString(hhash); +} + +CPLString ProjToWKT(const CPLString &proj) { + char* wkt = NULL; + OGRSpatialReference sr; + CPLString srs; + + /* We could of course recognize OSGEO:41001 to SetFromUserInput(), but this hackish SRS */ + /* is almost only used in the context of WMS */ + if (strcmp(proj.c_str(),"OSGEO:41001") == 0) + { + if (sr.SetFromUserInput("EPSG:3857") != OGRERR_NONE) return srs; + } + else if (EQUAL(proj.c_str(),"EPSG:NONE")) + { + return srs; + } + else + { + if (sr.SetFromUserInput(proj.c_str()) != OGRERR_NONE) return srs; + } + sr.exportToWkt(&wkt); + srs = wkt; + OGRFree(wkt); + return srs; +} + +void URLAppend(CPLString *url, const char *s) { + if ((s == NULL) || (s[0] == '\0')) return; + if (s[0] == '&') { + if (url->find('?') == std::string::npos) url->append(1, '?'); + if (((*url)[url->size() - 1] == '?') || ((*url)[url->size() - 1] == '&')) url->append(s + 1); + else url->append(s); + } else url->append(s); +} + +void URLAppendF(CPLString *url, const char *s, ...) { + CPLString tmp; + va_list args; + + va_start(args, s); + tmp.vPrintf(s, args); + va_end(args); + + URLAppend(url, tmp.c_str()); +} + +void URLAppend(CPLString *url, const CPLString &s) { + URLAppend(url, s.c_str()); +} + +CPLString BufferToVSIFile(GByte *buffer, size_t size) { + CPLString file_name; + + file_name.Printf("/vsimem/wms/%p/wmsresult.dat", buffer); + VSILFILE *f = VSIFileFromMemBuffer(file_name.c_str(), buffer, size, false); + if (f == NULL) return CPLString(); + VSIFCloseL(f); + return file_name; +} + +CPLErr MakeDirs(const char *path) { + char *p = CPLStrdup(CPLGetDirname(path)); + if (strlen(p) >= 2) { + MakeDirs(p); + } + VSIMkdir(p, 0744); + CPLFree(p); + return CE_None; +} + +int VersionStringToInt(const char *version) { + if (version == NULL) return -1; + const char *p = version; + int v = 0; + for (int i = 3; i >= 0; --i) { + v += (1 << (i * 8)) * atoi(p); + for (; (*p != '\0') && (*p != '.'); ++p); + if (*p != '\0') ++p; + } + return v; +} + +int StrToBool(const char *p) { + if (p == NULL) return -1; + if (EQUAL(p, "1") || EQUAL(p, "true") || EQUAL(p, "yes") || EQUAL(p, "enable") || EQUAL(p, "enabled") || EQUAL(p, "on")) return 1; + if (EQUAL(p, "0") || EQUAL(p, "false") || EQUAL(p, "no") || EQUAL(p, "disable") || EQUAL(p, "disabled") || EQUAL(p, "off")) return 0; + return -1; +} + +int URLSearchAndReplace (CPLString *base, const char *search, const char *fmt, ...) { + CPLString tmp; + va_list args; + + size_t start = base->find(search); + if (start == std::string::npos) { + return -1; + } + + va_start(args, fmt); + tmp.vPrintf(fmt, args); + va_end(args); + + base->replace(start, strlen(search), tmp); + return start; +} diff --git a/bazaar/plugin/gdal/frmts/xpm/GNUmakefile b/bazaar/plugin/gdal/frmts/xpm/GNUmakefile new file mode 100644 index 000000000..733ec3db1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/xpm/GNUmakefile @@ -0,0 +1,15 @@ + +OBJ = xpmdataset.o + +include ../../GDALmake.opt + +XTRAOPTS = -I../mem + +CPPFLAGS := $(XTRAOPTS) $(CPPFLAGS) + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/xpm/makefile.vc b/bazaar/plugin/gdal/frmts/xpm/makefile.vc new file mode 100644 index 000000000..51d7f0240 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/xpm/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = xpmdataset.obj + +GDAL_ROOT = ..\.. + +EXTRAFLAGS = -I..\mem + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/xpm/xpmdataset.cpp b/bazaar/plugin/gdal/frmts/xpm/xpmdataset.cpp new file mode 100644 index 000000000..bec76646b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/xpm/xpmdataset.cpp @@ -0,0 +1,654 @@ +/****************************************************************************** + * $Id: xpmdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $ + * + * Project: XPM Driver + * Purpose: Implement GDAL XPM Support + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "memdataset.h" +#include "gdal_frmts.h" + + +CPL_CVSID("$Id: xpmdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $"); + +static unsigned char *ParseXPM( const char *pszInput, + int *pnXSize, int *pnYSize, + GDALColorTable **ppoRetTable ); + + +/************************************************************************/ +/* ==================================================================== */ +/* XPMDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class XPMDataset : public GDALPamDataset +{ + public: + XPMDataset(); + ~XPMDataset(); + + static GDALDataset *Open( GDALOpenInfo * ); +}; + +/************************************************************************/ +/* XPMDataset() */ +/************************************************************************/ + +XPMDataset::XPMDataset() + +{ +} + +/************************************************************************/ +/* ~XPMDataset() */ +/************************************************************************/ + +XPMDataset::~XPMDataset() + +{ + FlushCache(); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *XPMDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ +/* -------------------------------------------------------------------- */ +/* First we check to see if the file has the expected header */ +/* bytes. For now we expect the XPM file to start with a line */ +/* containing the letters XPM, and to have "static" in the */ +/* header. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 32 + || strstr((const char *) poOpenInfo->pabyHeader,"XPM") == NULL + || strstr((const char *) poOpenInfo->pabyHeader,"static") == NULL ) + return NULL; + + if( poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The XPM driver does not support update access to existing" + " files." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the whole file into a memory strings. */ +/* -------------------------------------------------------------------- */ + unsigned int nFileSize; + char *pszFileContents; + VSILFILE *fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); + if( fp == NULL ) + return NULL; + + VSIFSeekL( fp, 0, SEEK_END ); + nFileSize = (unsigned int) VSIFTellL( fp ); + + pszFileContents = (char *) VSIMalloc(nFileSize+1); + if( pszFileContents == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Insufficient memory for loading XPM file %s into memory.", + poOpenInfo->pszFilename ); + VSIFCloseL(fp); + return NULL; + } + pszFileContents[nFileSize] = '\0'; + + VSIFSeekL( fp, 0, SEEK_SET ); + + if( VSIFReadL( pszFileContents, 1, nFileSize, fp ) != nFileSize) + { + CPLFree( pszFileContents ); + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read all %d bytes from file %s.", + nFileSize, poOpenInfo->pszFilename ); + VSIFCloseL(fp); + return NULL; + } + + VSIFCloseL(fp); + fp = NULL; + +/* -------------------------------------------------------------------- */ +/* Convert into a binary image. */ +/* -------------------------------------------------------------------- */ + GByte *pabyImage; + int nXSize, nYSize; + GDALColorTable *poCT = NULL; + + CPLErrorReset(); + + pabyImage = ParseXPM( pszFileContents, &nXSize, &nYSize, &poCT ); + CPLFree( pszFileContents ); + + if( pabyImage == NULL ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + XPMDataset *poDS; + + poDS = new XPMDataset(); + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file that is of interest. */ +/* -------------------------------------------------------------------- */ + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + MEMRasterBand *poBand; + + poBand = new MEMRasterBand( poDS, 1, pabyImage, GDT_Byte, 1, nXSize, + TRUE ); + poBand->SetColorTable( poCT ); + poDS->SetBand( 1, poBand ); + + delete poCT; + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + + return poDS; +} + +/************************************************************************/ +/* XPMCreateCopy() */ +/************************************************************************/ + +static GDALDataset * +XPMCreateCopy( const char * pszFilename, + CPL_UNUSED GDALDataset *poSrcDS, + int bStrict, + CPL_UNUSED char ** papszOptions, + CPL_UNUSED GDALProgressFunc pfnProgress, + CPL_UNUSED void * pProgressData ) +{ + int nBands = poSrcDS->GetRasterCount(); + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + GDALColorTable *poCT; + +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + if( nBands != 1 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "XPM driver only supports one band images.\n" ); + + return NULL; + } + + if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte + && bStrict ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "XPM driver doesn't support data type %s. " + "Only eight bit bands supported.\n", + GDALGetDataTypeName( + poSrcDS->GetRasterBand(1)->GetRasterDataType()) ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* If there is no colortable on the source image, create a */ +/* greyscale one with 64 levels of grey. */ +/* -------------------------------------------------------------------- */ + GDALRasterBand *poBand = poSrcDS->GetRasterBand(1); + int i; + GDALColorTable oGreyTable; + + poCT = poBand->GetColorTable(); + if( poCT == NULL ) + { + poCT = &oGreyTable; + + for( i = 0; i < 256; i++ ) + { + GDALColorEntry sColor; + + sColor.c1 = (short) i; + sColor.c2 = (short) i; + sColor.c3 = (short) i; + sColor.c4 = 255; + + poCT->SetColorEntry( i, &sColor ); + } + } + +/* -------------------------------------------------------------------- */ +/* Build list of active colors, and the mapping from pixels to */ +/* our active colormap. */ +/* -------------------------------------------------------------------- */ + const char *pszColorCodes = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-+=[]|:;,.<>?/"; + + int anPixelMapping[256]; + GDALColorEntry asPixelColor[256]; + int nActiveColors = MIN(poCT->GetColorEntryCount(),256); + + // Setup initial colortable and pixel value mapping. + memset( anPixelMapping+0, 0, sizeof(int) * 256 ); + for( i = 0; i < nActiveColors; i++ ) + { + poCT->GetColorEntryAsRGB( i, asPixelColor + i ); + anPixelMapping[i] = i; + } + +/* ==================================================================== */ +/* Iterate merging colors until we are under our limit (about 85). */ +/* ==================================================================== */ + while( nActiveColors > (int) strlen(pszColorCodes) ) + { + int nClosestDistance = 768; + int iClose1 = -1, iClose2 = -1; + int iColor1, iColor2; + + // Find the closest pair of colors. + for( iColor1 = 0; iColor1 < nActiveColors; iColor1++ ) + { + for( iColor2 = iColor1+1; iColor2 < nActiveColors; iColor2++ ) + { + int nDistance; + + if( asPixelColor[iColor1].c4 < 128 + && asPixelColor[iColor2].c4 < 128 ) + nDistance = 0; + else + nDistance = + ABS(asPixelColor[iColor1].c1-asPixelColor[iColor2].c1) + + ABS(asPixelColor[iColor1].c2-asPixelColor[iColor2].c2) + + ABS(asPixelColor[iColor1].c3-asPixelColor[iColor2].c3); + + if( nDistance < nClosestDistance ) + { + nClosestDistance = nDistance; + iClose1 = iColor1; + iClose2 = iColor2; + } + } + + if( nClosestDistance < 8 ) + break; + } + + // This should never happen! + if( iClose1 == -1 ) + break; + + // Merge two selected colors - shift icolor2 into icolor1 and + // move the last active color into icolor2's slot. + for( i = 0; i < 256; i++ ) + { + if( anPixelMapping[i] == iClose2 ) + anPixelMapping[i] = iClose1; + else if( anPixelMapping[i] == nActiveColors-1 ) + anPixelMapping[i] = iClose2; + } + + asPixelColor[iClose2] = asPixelColor[nActiveColors-1]; + nActiveColors--; + } + +/* ==================================================================== */ +/* Write the output image. */ +/* ==================================================================== */ + VSILFILE *fpPBM; + + fpPBM = VSIFOpenL( pszFilename, "wb+" ); + if( fpPBM == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create file `%s'.", + pszFilename ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write the header lines. */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL( fpPBM, "/* XPM */\n" ); + VSIFPrintfL( fpPBM, "static char *%s[] = {\n", + CPLGetBasename( pszFilename ) ); + VSIFPrintfL( fpPBM, "/* width height num_colors chars_per_pixel */\n" ); + VSIFPrintfL( fpPBM, "\" %3d %3d %3d 1\",\n", + nXSize, nYSize, nActiveColors ); + VSIFPrintfL( fpPBM, "/* colors */\n" ); + +/* -------------------------------------------------------------------- */ +/* Write the color table. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nActiveColors; i++ ) + { + if( asPixelColor[i].c4 < 128 ) + VSIFPrintfL( fpPBM, "\"%c c None\",\n", pszColorCodes[i] ); + else + VSIFPrintfL( fpPBM, + "\"%c c #%02x%02x%02x\",\n", + pszColorCodes[i], + asPixelColor[i].c1, + asPixelColor[i].c2, + asPixelColor[i].c3 ); + } + +/* -------------------------------------------------------------------- */ +/* Dump image. */ +/* -------------------------------------------------------------------- */ + int iLine; + GByte *pabyScanline; + + pabyScanline = (GByte *) CPLMalloc( nXSize ); + for( iLine = 0; iLine < nYSize; iLine++ ) + { + poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1, + (void *) pabyScanline, nXSize, 1, GDT_Byte, 0, 0, NULL ); + + VSIFPutcL( '"', fpPBM ); + for( int iPixel = 0; iPixel < nXSize; iPixel++ ) + VSIFPutcL( pszColorCodes[anPixelMapping[pabyScanline[iPixel]]], + fpPBM); + VSIFPrintfL( fpPBM, "\",\n" ); + } + + CPLFree( pabyScanline ); + +/* -------------------------------------------------------------------- */ +/* cleanup */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL( fpPBM, "};\n" ); + VSIFCloseL( fpPBM ); + +/* -------------------------------------------------------------------- */ +/* Re-open dataset, and copy any auxiliary pam information. */ +/* -------------------------------------------------------------------- */ + GDALPamDataset *poDS = (GDALPamDataset *) + GDALOpen( pszFilename, GA_ReadOnly ); + + if( poDS ) + poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); + + return poDS; +} + +/************************************************************************/ +/* GDALRegister_XPM() */ +/************************************************************************/ + +void GDALRegister_XPM() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "XPM" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "XPM" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "X11 PixMap Format" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#XPM" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "xpm" ); + poDriver->SetMetadataItem( GDAL_DMD_MIMETYPE, "image/x-xpixmap" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, + "Byte" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = XPMDataset::Open; + poDriver->pfnCreateCopy = XPMCreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + +/************************************************************************/ +/* ParseXPM() */ +/************************************************************************/ + +static unsigned char * +ParseXPM( const char *pszInput, int *pnXSize, int *pnYSize, + GDALColorTable **ppoRetTable ) + +{ +/* ==================================================================== */ +/* Parse input into an array of strings from within the first C */ +/* initializer (list os comma separated strings in braces). */ +/* ==================================================================== */ + char **papszXPMList = NULL; + const char *pszNext = pszInput; + int i; + + // Skip till after open brace. + while( *pszNext != '\0' && *pszNext != '{' ) + pszNext++; + + if( *pszNext == '\0' ) + return NULL; + + pszNext++; + + // Read lines till close brace. + + while( *pszNext != '\0' && *pszNext != '}' ) + { + // skip whole comment. + if( EQUALN(pszNext,"/*",2) ) + { + pszNext += 2; + while( *pszNext != '\0' && !EQUALN(pszNext,"*/",2) ) + pszNext++; + } + + // reading string constants + else if( *pszNext == '"' ) + { + char *pszLine; + + pszNext++; + i = 0; + + while( pszNext[i] != '\0' && pszNext[i] != '"' ) + i++; + + if( pszNext[i] == '\0' ) + { + CSLDestroy( papszXPMList ); + return NULL; + } + + pszLine = (char *) CPLMalloc(i+1); + strncpy( pszLine, pszNext, i ); + pszLine[i] = '\0'; + + papszXPMList = CSLAddString( papszXPMList, pszLine ); + CPLFree( pszLine ); + pszNext = pszNext + i + 1; + } + + // just ignore everything else (whitespace, commas, newlines, etc). + else + pszNext++; + } + + if( CSLCount(papszXPMList) < 3 || *pszNext != '}' ) + { + CSLDestroy( papszXPMList ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Get the image information. */ +/* -------------------------------------------------------------------- */ + int nColorCount, nCharsPerPixel; + + if( sscanf( papszXPMList[0], "%d %d %d %d", + pnXSize, pnYSize, &nColorCount, &nCharsPerPixel ) != 4 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Image definition (%s) not well formed.", + papszXPMList[0] ); + CSLDestroy( papszXPMList ); + return NULL; + } + + if( nCharsPerPixel != 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Only one character per pixel XPM images supported by GDAL at this time." ); + CSLDestroy( papszXPMList ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Parse out colors. */ +/* -------------------------------------------------------------------- */ + int iColor; + int anCharLookup[256]; + GDALColorTable oCTable; + + for( i = 0; i < 256; i++ ) + anCharLookup[i] = -1; + + for( iColor = 0; iColor < nColorCount; iColor++ ) + { + char **papszTokens = CSLTokenizeString( papszXPMList[iColor+1]+1 ); + GDALColorEntry sColor; + int nRed, nGreen, nBlue; + + if( CSLCount(papszTokens) != 2 || !EQUAL(papszTokens[0],"c") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Ill formed color definition (%s) in XPM header.", + papszXPMList[iColor+1] ); + CSLDestroy( papszXPMList ); + CSLDestroy( papszTokens ); + return NULL; + } + + anCharLookup[(int)papszXPMList[iColor+1][0]] = iColor; + + if( EQUAL(papszTokens[1],"None") ) + { + sColor.c1 = 0; + sColor.c2 = 0; + sColor.c3 = 0; + sColor.c4 = 0; + } + else if( sscanf( papszTokens[1], "#%02x%02x%02x", + &nRed, &nGreen, &nBlue ) != 3 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Ill formed color definition (%s) in XPM header.", + papszXPMList[iColor+1] ); + CSLDestroy( papszXPMList ); + CSLDestroy( papszTokens ); + return NULL; + } + else + { + sColor.c1 = (short) nRed; + sColor.c2 = (short) nGreen; + sColor.c3 = (short) nBlue; + sColor.c4 = 255; + } + + oCTable.SetColorEntry( iColor, &sColor ); + + CSLDestroy( papszTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Prepare image buffer. */ +/* -------------------------------------------------------------------- */ + GByte *pabyImage; + + pabyImage = (GByte *) VSIMalloc2(*pnXSize, *pnYSize); + if( pabyImage == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Insufficient memory for %dx%d XPM image buffer.", + *pnXSize, *pnYSize ); + CSLDestroy( papszXPMList ); + return NULL; + } + + memset( pabyImage, 0, *pnXSize * *pnYSize ); + +/* -------------------------------------------------------------------- */ +/* Parse image. */ +/* -------------------------------------------------------------------- */ + for( int iLine = 0; iLine < *pnYSize; iLine++ ) + { + const char *pszInLine = papszXPMList[iLine + nColorCount + 1]; + + if( pszInLine == NULL ) + { + CPLFree( pabyImage ); + CSLDestroy( papszXPMList ); + CPLError( CE_Failure, CPLE_AppDefined, + "Insufficient imagery lines in XPM image." ); + return NULL; + } + + for( int iPixel = 0; + pszInLine[iPixel] != '\0' && iPixel < *pnXSize; + iPixel++ ) + { + int nPixelValue = anCharLookup[(int)pszInLine[iPixel]]; + if( nPixelValue != -1 ) + pabyImage[iLine * *pnXSize + iPixel] = (GByte) nPixelValue; + } + } + + CSLDestroy( papszXPMList ); + + *ppoRetTable = oCTable.Clone(); + + return pabyImage; +} diff --git a/bazaar/plugin/gdal/frmts/xyz/GNUmakefile b/bazaar/plugin/gdal/frmts/xyz/GNUmakefile new file mode 100644 index 000000000..7fb5fbe5f --- /dev/null +++ b/bazaar/plugin/gdal/frmts/xyz/GNUmakefile @@ -0,0 +1,13 @@ + +OBJ = xyzdataset.o + +include ../../GDALmake.opt + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/xyz/frmt_xyz.html b/bazaar/plugin/gdal/frmts/xyz/frmt_xyz.html new file mode 100644 index 000000000..e3351833a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/xyz/frmt_xyz.html @@ -0,0 +1,48 @@ + + +XYZ -- ASCII Gridded XYZ + + + + +

    XYZ -- ASCII Gridded XYZ

    + +(Available for GDAL >= 1.8.0) +

    +GDAL supports reading and writting ASCII gridded XYZ raster datasets (ie. +ungridded XYZ, LIDAR XYZ etc. must be opened by other means. +See the documentation of the gdal_grid utility). +

    +Those datasets are ASCII files with (at least) 3 columns, each line containing +the X and Y coordinates of the center of the cell and the value of the cell. +

    +The spacing between each cell must be constant and no missing value is supported. +Cells with same Y coordinates must be placed on consecutive lines. For a same Y coordinate value, +the lines in the dataset must be organized by increasing X values. The value of the Y coordinate +can increase or decrease however. The supported column separators are space, comma, semicolon and tabulations. +

    +The driver tries to autodetect an header line and will look for 'x', 'lon' or 'east' names to detect +the index of the X column, 'y', 'lat' or 'north' for the Y column and 'z', 'alt' or 'height' for the Z column. +If no header is present or one of the column could not be identified in the header, the X, Y and Z columns +(in that order) are assumed to be the first 3 columns of each line. +

    +The opening of a big dataset can be slow as the driver must scan the whole file to determine +the dataset size and spatial resolution. The driver will autodetect the data type among +Byte, Int16, Int32 or Float32. +

    + +

    Creation options

    + +
      +
    • COLUMN_SEPARATOR=a_value : where a_value is a string used to separate the values of the X,Y and Z columns. Defaults to one space character
    • +
    • ADD_HEADER_LINE=YES/NO : whether an header line must be written (content is X <col_sep> Y <col_sep> Z) . Defaults to NO
    • +
    + +

    See also

    + + + + + diff --git a/bazaar/plugin/gdal/frmts/xyz/makefile.vc b/bazaar/plugin/gdal/frmts/xyz/makefile.vc new file mode 100644 index 000000000..f8996920d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/xyz/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = xyzdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/xyz/xyzdataset.cpp b/bazaar/plugin/gdal/frmts/xyz/xyzdataset.cpp new file mode 100644 index 000000000..896e6be33 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/xyz/xyzdataset.cpp @@ -0,0 +1,1222 @@ +/****************************************************************************** + * $Id: xyzdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: XYZ driver + * Purpose: GDALDataset driver for XYZ dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "gdal_pam.h" +#include +#include + +CPL_CVSID("$Id: xyzdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_XYZ(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* XYZDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class XYZRasterBand; + +class XYZDataset : public GDALPamDataset +{ + friend class XYZRasterBand; + + VSILFILE *fp; + int bHasHeaderLine; + int nCommentLineCount; + char chDecimalSep; + int nXIndex; + int nYIndex; + int nZIndex; + int nMinTokens; + int nLineNum; /* any line */ + int nDataLineNum; /* line with values (header line and empty lines ignored) */ + double adfGeoTransform[6]; + int bSameNumberOfValuesPerLine; + double dfMinZ; + double dfMaxZ; + + static int IdentifyEx( GDALOpenInfo *, int&, int& nCommentLineCount ); + + public: + XYZDataset(); + virtual ~XYZDataset(); + + virtual CPLErr GetGeoTransform( double * ); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset *CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* XYZRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class XYZRasterBand : public GDALPamRasterBand +{ + friend class XYZDataset; + + int nLastYOff; + + public: + + XYZRasterBand( XYZDataset *, int, GDALDataType ); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum( int *pbSuccess = NULL ); + virtual double GetNoDataValue( int *pbSuccess = NULL ); +}; + + +/************************************************************************/ +/* XYZRasterBand() */ +/************************************************************************/ + +XYZRasterBand::XYZRasterBand( XYZDataset *poDS, int nBand, GDALDataType eDT ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = eDT; + + nBlockXSize = poDS->GetRasterXSize(); + nBlockYSize = 1; + + nLastYOff = -1; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr XYZRasterBand::IReadBlock( CPL_UNUSED int nBlockXOff, + int nBlockYOff, + void * pImage ) +{ + XYZDataset *poGDS = (XYZDataset *) poDS; + + if (poGDS->fp == NULL) + return CE_Failure; + + if( pImage ) + { + int bSuccess = FALSE; + double dfNoDataValue = GetNoDataValue(&bSuccess); + if( !bSuccess ) + dfNoDataValue = 0.0; + GDALCopyWords(&dfNoDataValue, GDT_Float64, 0, + pImage, eDataType, GDALGetDataTypeSize(eDataType) / 8, + nRasterXSize); + } + + int nLineInFile = nBlockYOff * nBlockXSize; // only valid if bSameNumberOfValuesPerLine + if ( (poGDS->bSameNumberOfValuesPerLine && poGDS->nDataLineNum > nLineInFile) || + (!poGDS->bSameNumberOfValuesPerLine && (nLastYOff == -1 || nBlockYOff == 0)) ) + { + poGDS->nDataLineNum = 0; + poGDS->nLineNum = 0; + VSIFSeekL(poGDS->fp, 0, SEEK_SET); + + for(int i=0;inCommentLineCount;i++) + { + CPLReadLine2L(poGDS->fp, 100, NULL); + poGDS->nLineNum ++; + } + + if (poGDS->bHasHeaderLine) + { + const char* pszLine = CPLReadLine2L(poGDS->fp, 100, 0); + if (pszLine == NULL) + return CE_Failure; + poGDS->nLineNum ++; + } + } + + if( !poGDS->bSameNumberOfValuesPerLine && nBlockYOff != nLastYOff + 1 ) + { + int iY; + if( nBlockYOff < nLastYOff ) + { + nLastYOff = -1; + for(iY = 0; iY < nBlockYOff; iY++) + { + if( IReadBlock(0, iY, NULL) != CE_None ) + return CE_Failure; + } + } + else + { + for(iY = nLastYOff + 1; iY < nBlockYOff; iY++) + { + if( IReadBlock(0, iY, NULL) != CE_None ) + return CE_Failure; + } + } + } + else if( poGDS->bSameNumberOfValuesPerLine ) + { + while(poGDS->nDataLineNum < nLineInFile) + { + const char* pszLine = CPLReadLine2L(poGDS->fp, 100, 0); + if (pszLine == NULL) + return CE_Failure; + poGDS->nLineNum ++; + + const char* pszPtr = pszLine; + char ch; + int nCol = 0; + int bLastWasSep = TRUE; + while((ch = *pszPtr) != '\0') + { + if (ch == ' ') + { + if (!bLastWasSep) + nCol ++; + bLastWasSep = TRUE; + } + else if ((ch == ',' && poGDS->chDecimalSep != ',') || ch == '\t' || ch == ';') + { + nCol ++; + bLastWasSep = TRUE; + } + else + { + bLastWasSep = FALSE; + } + pszPtr ++; + } + + /* Skip empty line */ + if (nCol == 0 && bLastWasSep) + continue; + + poGDS->nDataLineNum ++; + } + } + + double dfExpectedY = poGDS->adfGeoTransform[3] + (0.5 + nBlockYOff) * poGDS->adfGeoTransform[5]; + int idx = -1; + while(TRUE) + { + int nCol; + int bLastWasSep; + do + { + vsi_l_offset nOffsetBefore = VSIFTellL(poGDS->fp); + const char* pszLine = CPLReadLine2L(poGDS->fp, 100, 0); + if (pszLine == NULL) + { + if( poGDS->bSameNumberOfValuesPerLine ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot read line %d", poGDS->nLineNum + 1); + return CE_Failure; + } + else + { + nLastYOff = nBlockYOff; + return CE_None; + } + } + poGDS->nLineNum ++; + + const char* pszPtr = pszLine; + char ch; + nCol = 0; + bLastWasSep = TRUE; + double dfX = 0.0, dfY = 0.0, dfZ = 0.0; + int bUsefulColsFound = 0; + while((ch = *pszPtr) != '\0') + { + if (ch == ' ') + { + if (!bLastWasSep) + nCol ++; + bLastWasSep = TRUE; + } + else if ((ch == ',' && poGDS->chDecimalSep != ',') || ch == '\t' || ch == ';') + { + nCol ++; + bLastWasSep = TRUE; + } + else + { + if (bLastWasSep) + { + if (nCol == poGDS->nXIndex) + { + bUsefulColsFound ++; + if( !poGDS->bSameNumberOfValuesPerLine ) + dfX = CPLAtofDelim(pszPtr, poGDS->chDecimalSep); + } + else if (nCol == poGDS->nYIndex) + { + bUsefulColsFound ++; + if( !poGDS->bSameNumberOfValuesPerLine ) + dfY = CPLAtofDelim(pszPtr, poGDS->chDecimalSep); + } + else if( nCol == poGDS->nZIndex) + { + bUsefulColsFound ++; + dfZ = CPLAtofDelim(pszPtr, poGDS->chDecimalSep); + } + } + bLastWasSep = FALSE; + } + pszPtr ++; + } + nCol ++; + + if( bUsefulColsFound == 3 ) + { + if( poGDS->bSameNumberOfValuesPerLine ) + { + idx ++; + } + else + { + if( fabs(dfY - dfExpectedY) > 1e-8 ) + { + if( idx < 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "At line %d, found %f instead of %f for nBlockYOff = %d", + poGDS->nLineNum, dfY, dfExpectedY, nBlockYOff); + return CE_Failure; + } + VSIFSeekL(poGDS->fp, nOffsetBefore, SEEK_SET); + nLastYOff = nBlockYOff; + poGDS->nLineNum --; + return CE_None; + } + + idx = (int)((dfX - 0.5 * poGDS->adfGeoTransform[1] - poGDS->adfGeoTransform[0]) / poGDS->adfGeoTransform[1] + 0.5); + } + CPLAssert(idx >= 0 && idx < nRasterXSize); + + if( pImage ) + { + if (eDataType == GDT_Float32) + { + ((float*)pImage)[idx] = (float)dfZ; + } + else if (eDataType == GDT_Int32) + { + ((GInt32*)pImage)[idx] = (GInt32)dfZ; + } + else if (eDataType == GDT_Int16) + { + ((GInt16*)pImage)[idx] = (GInt16)dfZ; + } + else + { + ((GByte*)pImage)[idx] = (GByte)dfZ; + } + } + } + /* Skip empty line */ + } + while (nCol == 1 && bLastWasSep); + + poGDS->nDataLineNum ++; + if (nCol < poGDS->nMinTokens) + return CE_Failure; + + if( idx + 1 == nRasterXSize ) + break; + } + + if( poGDS->bSameNumberOfValuesPerLine ) { + CPLAssert(poGDS->nDataLineNum == (nBlockYOff + 1) * nBlockXSize); + } + + nLastYOff = nBlockYOff; + + return CE_None; +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +double XYZRasterBand::GetMinimum(int *pbSuccess) +{ + XYZDataset *poGDS = (XYZDataset *) poDS; + if( pbSuccess ) *pbSuccess = TRUE; + return poGDS->dfMinZ; +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +double XYZRasterBand::GetMaximum(int *pbSuccess) +{ + XYZDataset *poGDS = (XYZDataset *) poDS; + if( pbSuccess ) *pbSuccess = TRUE; + return poGDS->dfMaxZ; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double XYZRasterBand::GetNoDataValue(int *pbSuccess) +{ + XYZDataset *poGDS = (XYZDataset *) poDS; + if( !poGDS->bSameNumberOfValuesPerLine && + poGDS->dfMinZ > -32768 && eDataType != GDT_Byte ) + { + if( pbSuccess ) *pbSuccess = TRUE; + return (poGDS->dfMinZ > 0) ? 0 : -32768; + } + else if ( !poGDS->bSameNumberOfValuesPerLine && + poGDS->dfMinZ > 0 && eDataType == GDT_Byte ) + { + if( pbSuccess ) *pbSuccess = TRUE; + return 0; + } + return GDALPamRasterBand::GetNoDataValue(pbSuccess); +} + +/************************************************************************/ +/* ~XYZDataset() */ +/************************************************************************/ + +XYZDataset::XYZDataset() +{ + fp = NULL; + nDataLineNum = INT_MAX; + nLineNum = 0; + nCommentLineCount = 0; + chDecimalSep = '.'; + bHasHeaderLine = FALSE; + nXIndex = -1; + nYIndex = -1; + nZIndex = -1; + nMinTokens = 0; + adfGeoTransform[0] = 0; + adfGeoTransform[1] = 1; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = 0; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = 1; + bSameNumberOfValuesPerLine = TRUE; + dfMinZ = 0; + dfMaxZ = 0; +} + +/************************************************************************/ +/* ~XYZDataset() */ +/************************************************************************/ + +XYZDataset::~XYZDataset() + +{ + FlushCache(); + if (fp) + VSIFCloseL(fp); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int XYZDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + int bHasHeaderLine, nCommentLineCount; + return IdentifyEx(poOpenInfo, bHasHeaderLine, nCommentLineCount); +} + +/************************************************************************/ +/* IdentifyEx() */ +/************************************************************************/ + + +int XYZDataset::IdentifyEx( GDALOpenInfo * poOpenInfo, + int& bHasHeaderLine, + int& nCommentLineCount) + +{ + int i; + + bHasHeaderLine = FALSE; + nCommentLineCount = 0; + + CPLString osFilename(poOpenInfo->pszFilename); + + GDALOpenInfo* poOpenInfoToDelete = NULL; + /* GZipped .xyz files are common, so automagically open them */ + /* if the /vsigzip/ has not been explicitly passed */ + if (strlen(poOpenInfo->pszFilename) > 6 && + EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6, "xyz.gz") && + !EQUALN(poOpenInfo->pszFilename, "/vsigzip/", 9)) + { + osFilename = "/vsigzip/"; + osFilename += poOpenInfo->pszFilename; + poOpenInfo = poOpenInfoToDelete = + new GDALOpenInfo(osFilename.c_str(), GA_ReadOnly, + poOpenInfo->GetSiblingFiles()); + } + + if (poOpenInfo->nHeaderBytes == 0) + { + delete poOpenInfoToDelete; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Chech that it looks roughly as a XYZ dataset */ +/* -------------------------------------------------------------------- */ + const char* pszData = (const char*)poOpenInfo->pabyHeader; + + /* Skip comments line at the beginning such as in */ + /* http://pubs.usgs.gov/of/2003/ofr-03-230/DATA/NSLCU.XYZ */ + i=0; + if (pszData[i] == '/') + { + nCommentLineCount ++; + + i++; + for(;inHeaderBytes;i++) + { + char ch = pszData[i]; + if (ch == 13 || ch == 10) + { + if (ch == 13 && pszData[i+1] == 10) + i++; + if (pszData[i+1] == '/') + { + nCommentLineCount ++; + i++; + } + else + break; + } + } + } + + for(;inHeaderBytes;i++) + { + char ch = pszData[i]; + if (ch == 13 || ch == 10) + { + break; + } + else if (ch == ' ' || ch == ',' || ch == '\t' || ch == ';') + ; + else if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' || + ch == '-' || ch == 'e' || ch == 'E') + ; + else if (ch == '"' || (ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z')) + bHasHeaderLine = TRUE; + else + { + delete poOpenInfoToDelete; + return FALSE; + } + } + int bHasFoundNewLine = FALSE; + int bPrevWasSep = TRUE; + int nCols = 0; + int nMaxCols = 0; + for(;inHeaderBytes;i++) + { + char ch = pszData[i]; + if (ch == 13 || ch == 10) + { + bHasFoundNewLine = TRUE; + if (!bPrevWasSep) + { + nCols ++; + if (nCols > nMaxCols) + nMaxCols = nCols; + } + bPrevWasSep = TRUE; + nCols = 0; + } + else if (ch == ' ' || ch == ',' || ch == '\t' || ch == ';') + { + if (!bPrevWasSep) + { + nCols ++; + if (nCols > nMaxCols) + nMaxCols = nCols; + } + bPrevWasSep = TRUE; + } + else if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '+' || + ch == '-' || ch == 'e' || ch == 'E') + { + bPrevWasSep = FALSE; + } + else + { + delete poOpenInfoToDelete; + return FALSE; + } + } + + delete poOpenInfoToDelete; + return bHasFoundNewLine && nMaxCols >= 3; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *XYZDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + int i; + int bHasHeaderLine; + int nCommentLineCount = 0; + + if (!IdentifyEx(poOpenInfo, bHasHeaderLine, nCommentLineCount)) + return NULL; + + CPLString osFilename(poOpenInfo->pszFilename); + + /* GZipped .xyz files are common, so automagically open them */ + /* if the /vsigzip/ has not been explicitly passed */ + if (strlen(poOpenInfo->pszFilename) > 6 && + EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6, "xyz.gz") && + !EQUALN(poOpenInfo->pszFilename, "/vsigzip/", 9)) + { + osFilename = "/vsigzip/"; + osFilename += poOpenInfo->pszFilename; + } + +/* -------------------------------------------------------------------- */ +/* Find dataset characteristics */ +/* -------------------------------------------------------------------- */ + VSILFILE* fp = VSIFOpenL(osFilename.c_str(), "rb"); + if (fp == NULL) + return NULL; + + /* For better performance of CPLReadLine2L() we create a buffered reader */ + /* (except for /vsigzip/ since it has one internally) */ + if (!EQUALN(poOpenInfo->pszFilename, "/vsigzip/", 9)) + fp = (VSILFILE*) VSICreateBufferedReaderHandle((VSIVirtualHandle*)fp); + + const char* pszLine; + int nXIndex = -1, nYIndex = -1, nZIndex = -1; + int nMinTokens = 0; + + for(i=0;i adfStepX, adfStepY; + GDALDataType eDT = GDT_Byte; + int bSameNumberOfValuesPerLine = TRUE; + char chDecimalSep = '\0'; + int bStepYSign = 0; + while((pszLine = CPLReadLine2L(fp, 100, NULL)) != NULL) + { + nLineNum ++; + + const char* pszPtr = pszLine; + char ch; + int nCol = 0; + int bLastWasSep = TRUE; + if( chDecimalSep == '\0' ) + { + int nCountComma = 0; + int nCountFieldSep = 0; + while((ch = *pszPtr) != '\0') + { + if( ch == '.' ) + { + chDecimalSep = '.'; + break; + } + else if( ch == ',' ) + { + nCountComma ++; + bLastWasSep = FALSE; + } + else if( ch == ' ' ) + { + if (!bLastWasSep) + nCountFieldSep ++; + bLastWasSep = TRUE; + } + else if( ch == '\t' || ch == ';' ) + { + nCountFieldSep ++; + bLastWasSep = TRUE; + } + else + bLastWasSep = FALSE; + pszPtr ++; + } + if( chDecimalSep == '\0' ) + { + /* 1,2,3 */ + if( nCountComma >= 2 && nCountFieldSep == 0 ) + chDecimalSep = '.'; + /* 23,5;33;45 */ + else if ( nCountComma > 0 && nCountFieldSep > 0 ) + chDecimalSep = ','; + } + pszPtr = pszLine; + bLastWasSep = TRUE; + } + + char chLocalDecimalSep = chDecimalSep ? chDecimalSep : '.'; + while((ch = *pszPtr) != '\0') + { + if (ch == ' ') + { + if (!bLastWasSep) + nCol ++; + bLastWasSep = TRUE; + } + else if ((ch == ',' && chLocalDecimalSep != ',') || ch == '\t' || ch == ';') + { + nCol ++; + bLastWasSep = TRUE; + } + else + { + if (bLastWasSep) + { + if (nCol == nXIndex) + dfX = CPLAtofDelim(pszPtr, chLocalDecimalSep); + else if (nCol == nYIndex) + dfY = CPLAtofDelim(pszPtr, chLocalDecimalSep); + else if (nCol == nZIndex) + { + dfZ = CPLAtofDelim(pszPtr, chLocalDecimalSep); + if( nDataLineNum == 0 ) + dfMinZ = dfMaxZ = dfZ; + else if( dfZ < dfMinZ ) + dfMinZ = dfZ; + else if( dfZ > dfMaxZ ) + dfMaxZ = dfZ; + int nZ = (int)dfZ; + if ((double)nZ != dfZ) + { + eDT = GDT_Float32; + } + else if ((eDT == GDT_Byte || eDT == GDT_Int16) && (nZ < 0 || nZ > 255)) + { + if (nZ < -32768 || nZ > 32767) + eDT = GDT_Int32; + else + eDT = GDT_Int16; + } + } + } + bLastWasSep = FALSE; + } + pszPtr ++; + } + /* skip empty lines */ + if (bLastWasSep && nCol == 0) + { + continue; + } + nDataLineNum ++; + nCol ++; + if (nCol < nMinTokens) + { + CPLError(CE_Failure, CPLE_AppDefined, + "At line %d, found %d tokens. Expected %d at least", + nLineNum, nCol, nMinTokens); + VSIFCloseL(fp); + return NULL; + } + + if (nDataLineNum == 1) + { + dfMinX = dfMaxX = dfX; + dfMinY = dfMaxY = dfY; + } + else + { + double dfStepY = dfY - dfLastY; + if( dfStepY == 0.0 ) + { + double dfStepX = dfX - dfLastX; + if( dfStepX <= 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Ungridded dataset: At line %d, X spacing was %f. Expected >0 value", + nLineNum, dfStepX); + VSIFCloseL(fp); + return NULL; + } + if( std::find(adfStepX.begin(), adfStepX.end(), dfStepX) == adfStepX.end() ) + { + int bAddNewValue = TRUE; + std::vector::iterator oIter = adfStepX.begin(); + while( oIter != adfStepX.end() ) + { + if( dfStepX < *oIter && fmod( *oIter, dfStepX ) < 1e-8 ) + { + adfStepX.erase(oIter); + } + else if( dfStepX > *oIter && fmod( dfStepX, *oIter ) < 1e-8 ) + { + bAddNewValue = FALSE; + break; + } + else + { + ++ oIter; + } + } + if( bAddNewValue ) + { + adfStepX.push_back(dfStepX); + if( adfStepX.size() == 10 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Ungridded dataset: too many stepX values"); + VSIFCloseL(fp); + return NULL; + } + } + } + } + else + { + int bNewStepYSign = (dfStepY < 0.0) ? -1 : 1; + if( bStepYSign == 0 ) + bStepYSign = bNewStepYSign; + else if( bStepYSign != bNewStepYSign ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Ungridded dataset: At line %d, change of Y direction", + nLineNum); + VSIFCloseL(fp); + return NULL; + } + if( bNewStepYSign < 0 ) dfStepY = -dfStepY; + if( adfStepY.size() == 0 ) + adfStepY.push_back(dfStepY); + else if( adfStepY[0] != dfStepY ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Ungridded dataset: At line %d, too many stepY values", nLineNum); + VSIFCloseL(fp); + return NULL; + } + } + + if (dfX < dfMinX) dfMinX = dfX; + if (dfX > dfMaxX) dfMaxX = dfX; + if (dfY < dfMinY) dfMinY = dfY; + if (dfY > dfMaxY) dfMaxY = dfY; + } + + dfLastX = dfX; + dfLastY = dfY; + } + + if (adfStepX.size() != 1) + { + CPLError(CE_Failure, CPLE_AppDefined, "Couldn't determine X spacing"); + VSIFCloseL(fp); + return NULL; + } + + if (adfStepY.size() != 1) + { + CPLError(CE_Failure, CPLE_AppDefined, "Couldn't determine Y spacing"); + VSIFCloseL(fp); + return NULL; + } + + double dfStepX = adfStepX[0]; + double dfStepY = adfStepY[0] * bStepYSign; + int nXSize = 1 + int((dfMaxX - dfMinX) / dfStepX + 0.5); + int nYSize = 1 + int((dfMaxY - dfMinY) / fabs(dfStepY) + 0.5); + + //CPLDebug("XYZ", "minx=%f maxx=%f stepx=%f", dfMinX, dfMaxX, dfStepX); + //CPLDebug("XYZ", "miny=%f maxy=%f stepy=%f", dfMinY, dfMaxY, dfStepY); + + if (nDataLineNum != nXSize * nYSize) + { + bSameNumberOfValuesPerLine = FALSE; + } + + if (poOpenInfo->eAccess == GA_Update) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The XYZ driver does not support update access to existing" + " datasets.\n" ); + VSIFCloseL(fp); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + XYZDataset *poDS; + + poDS = new XYZDataset(); + poDS->fp = fp; + poDS->bHasHeaderLine = bHasHeaderLine; + poDS->nCommentLineCount = nCommentLineCount; + poDS->chDecimalSep = chDecimalSep ? chDecimalSep : '.'; + poDS->nXIndex = nXIndex; + poDS->nYIndex = nYIndex; + poDS->nZIndex = nZIndex; + poDS->nMinTokens = nMinTokens; + poDS->nRasterXSize = nXSize; + poDS->nRasterYSize = nYSize; + poDS->adfGeoTransform[0] = dfMinX - dfStepX / 2; + poDS->adfGeoTransform[1] = dfStepX; + poDS->adfGeoTransform[3] = (dfStepY < 0) ? dfMaxY - dfStepY / 2 : + dfMinY - dfStepY / 2; + poDS->adfGeoTransform[5] = dfStepY; + poDS->bSameNumberOfValuesPerLine = bSameNumberOfValuesPerLine; + poDS->dfMinZ = dfMinZ; + poDS->dfMaxZ = dfMaxZ; + //CPLDebug("XYZ", "bSameNumberOfValuesPerLine = %d", bSameNumberOfValuesPerLine); + + if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = 1; + for( i = 0; i < poDS->nBands; i++ ) + poDS->SetBand( i+1, new XYZRasterBand( poDS, i+1, eDT ) ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + return( poDS ); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset* XYZDataset::CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "XYZ driver does not support source dataset with zero band.\n"); + return NULL; + } + + if (nBands != 1) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "XYZ driver only uses the first band of the dataset.\n"); + if (bStrict) + return NULL; + } + + if( pfnProgress && !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get source dataset info */ +/* -------------------------------------------------------------------- */ + + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + double adfGeoTransform[6]; + poSrcDS->GetGeoTransform(adfGeoTransform); + if (adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "XYZ driver does not support CreateCopy() from skewed or rotated dataset.\n"); + return NULL; + } + + GDALDataType eSrcDT = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + GDALDataType eReqDT; + if (eSrcDT == GDT_Byte || eSrcDT == GDT_Int16 || + eSrcDT == GDT_UInt16 || eSrcDT == GDT_Int32) + eReqDT = GDT_Int32; + else + eReqDT = GDT_Float32; + +/* -------------------------------------------------------------------- */ +/* Create target file */ +/* -------------------------------------------------------------------- */ + + VSILFILE* fp = VSIFOpenL(pszFilename, "wb"); + if (fp == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot create %s", pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read creation options */ +/* -------------------------------------------------------------------- */ + const char* pszColSep = + CSLFetchNameValue(papszOptions, "COLUMN_SEPARATOR"); + if (pszColSep == NULL) + pszColSep = " "; + else if (EQUAL(pszColSep, "COMMA")) + pszColSep = ","; + else if (EQUAL(pszColSep, "SPACE")) + pszColSep = " "; + else if (EQUAL(pszColSep, "SEMICOLON")) + pszColSep = ";"; + else if (EQUAL(pszColSep, "\\t") || EQUAL(pszColSep, "TAB")) + pszColSep = "\t"; + + const char* pszAddHeaderLine = + CSLFetchNameValue(papszOptions, "ADD_HEADER_LINE"); + if (pszAddHeaderLine != NULL && CSLTestBoolean(pszAddHeaderLine)) + { + VSIFPrintfL(fp, "X%sY%sZ\n", pszColSep, pszColSep); + } + +/* -------------------------------------------------------------------- */ +/* Copy imagery */ +/* -------------------------------------------------------------------- */ + void* pLineBuffer = (void*) CPLMalloc(nXSize * sizeof(int)); + int i, j; + CPLErr eErr = CE_None; + for(j=0;jGetRasterBand(1)->RasterIO( + GF_Read, 0, j, nXSize, 1, + pLineBuffer, nXSize, 1, + eReqDT, 0, 0, NULL); + if (eErr != CE_None) + break; + double dfY = adfGeoTransform[3] + (j + 0.5) * adfGeoTransform[5]; + CPLString osBuf; + for(i=0;inRasterXSize = nXSize; + poXYZ_DS->nRasterYSize = nYSize; + poXYZ_DS->nBands = 1; + poXYZ_DS->SetBand( 1, new XYZRasterBand( poXYZ_DS, 1, eReqDT ) ); + /* If outputing to stdout, we can't reopen it --> silence warning */ + CPLPushErrorHandler(CPLQuietErrorHandler); + poXYZ_DS->fp = VSIFOpenL( pszFilename, "rb" ); + CPLPopErrorHandler(); + memcpy( &(poXYZ_DS->adfGeoTransform), adfGeoTransform, sizeof(double)*6 ); + poXYZ_DS->nXIndex = 0; + poXYZ_DS->nYIndex = 1; + poXYZ_DS->nZIndex = 2; + if( pszAddHeaderLine ) + { + poXYZ_DS->nDataLineNum = 1; + poXYZ_DS->bHasHeaderLine = TRUE; + } + + return poXYZ_DS; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr XYZDataset::GetGeoTransform( double * padfTransform ) + +{ + memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double)); + + return( CE_None ); +} + +/************************************************************************/ +/* GDALRegister_XYZ() */ +/************************************************************************/ + +void GDALRegister_XYZ() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "XYZ" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "XYZ" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ASCII Gridded XYZ" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_xyz.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "xyz" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = XYZDataset::Open; + poDriver->pfnIdentify = XYZDataset::Identify; + poDriver->pfnCreateCopy = XYZDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/frmts/zlib/GNUmakefile b/bazaar/plugin/gdal/frmts/zlib/GNUmakefile new file mode 100644 index 000000000..954db571b --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/GNUmakefile @@ -0,0 +1,26 @@ +# $Id: GNUmakefile 15888 2008-12-03 08:22:31Z dron $ +# +# Makefile to build zlib using GNU Make and GCC. +# +include ../../GDALmake.opt + +OBJ = \ + adler32.o \ + compress.o \ + crc32.o \ + deflate.o \ + gzio.o \ + infback.o \ + inffast.o \ + inflate.o \ + inftrees.o \ + trees.o \ + uncompr.o \ + zutil.o + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/zlib/README b/bazaar/plugin/gdal/frmts/zlib/README new file mode 100644 index 000000000..3b8b3727a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/README @@ -0,0 +1,150 @@ +$Id: README 10656 2007-01-19 01:31:01Z mloskot $ + +zlib 1.2.3 is a general purpose data compression library. +All the code is thread safe. The data format used by the zlib library +is described by RFCs (Request for Comments) 1950 to 1952 in the files +ftp://ds.internic.net/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate +format) and rfc1952.txt (gzip format). These documents are also available in +other formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html + +All functions of the compression library are documented in the file zlib.h +(volunteer to write man pages welcome, contact jloup@gzip.org). A usage +example of the library is given in the file example.c which also tests that +the library is working correctly. Another example is given in the file +minigzip.c. The compression library itself is composed of all source files +except example.c and minigzip.c. + +To compile all files and run the test program, follow the instructions +given at the top of Makefile. In short "make test; make install" +should work for most machines. For Unix: "configure; make test; make install" +For MSDOS, use one of the special makefiles such as Makefile.msc. +For VMS, use Make_vms.com or descrip.mms. + +Questions about zlib should be sent to , or to +Gilles Vollant for the Windows DLL version. +The zlib home page is http://www.cdrom.com/pub/infozip/zlib/ +The official zlib ftp site is ftp://ftp.cdrom.com/pub/infozip/zlib/ +Before reporting a problem, please check those sites to verify that +you have the latest version of zlib; otherwise get the latest version and +check whether the problem still exists or not. + +Mark Nelson wrote an article about zlib for the Jan. 1997 +issue of Dr. Dobb's Journal; a copy of the article is available in +http://web2.airmail.net/markn/articles/zlibtool/zlibtool.htm + +The changes made in version 1.1.3 are documented in the file ChangeLog. +The main changes since 1.1.2 are: + +- fix "an inflate input buffer bug that shows up on rare but persistent + occasions" (Mark) +- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) +- fix gzseek(..., SEEK_SET) in write mode +- fix crc check after a gzeek (Frank Faubert) +- fix miniunzip when the last entry in a zip file is itself a zip file + (J Lillge) +- add contrib/asm586 and contrib/asm686 (Brian Raiter) + See http://www.muppetlabs.com/~breadbox/software/assembly.html +- add support for Delphi 3 in contrib/delphi (Bob Dellaca) +- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) +- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) +- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) +- added a FAQ file + +plus many changes for portability. + +Unsupported third party contributions are provided in directory "contrib". + +A Java implementation of zlib is available in the Java Development Kit 1.1 +http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html +See the zlib home page http://www.cdrom.com/pub/infozip/zlib/ for details. + +A Perl interface to zlib written by Paul Marquess +is in the CPAN (Comprehensive Perl Archive Network) sites, such as: +ftp://ftp.cis.ufl.edu/pub/perl/CPAN/modules/by-module/Compress/Compress-Zlib* + +A Python interface to zlib written by A.M. Kuchling +is available in Python 1.5 and later versions, see +http://www.python.org/doc/lib/module-zlib.html + +A zlib binding for TCL written by Andreas Kupries +is availlable at http://www.westend.com/~kupries/doc/trf/man/man.html + +An experimental package to read and write files in .zip format, +written on top of zlib by Gilles Vollant , is +available at http://www.winimage.com/zLibDll/unzip.html +and also in the contrib/minizip directory of zlib. + + +Notes for some targets: + +- To build a Windows DLL version, include in a DLL project zlib.def, zlib.rc + and all .c files except example.c and minigzip.c; compile with -DZLIB_DLL + The zlib DLL support was initially done by Alessandro Iacopetti and is + now maintained by Gilles Vollant . Check the zlib DLL + home page at http://www.winimage.com/zLibDll + + From Visual Basic, you can call the DLL functions which do not take + a structure as argument: compress, uncompress and all gz* functions. + See contrib/visual-basic.txt for more information, or get + http://www.tcfb.com/dowseware/cmp-z-it.zip + +- For 64-bit Irix, deflate.c must be compiled without any optimization. + With -O, one libpng test fails. The test works in 32 bit mode (with + the -n32 compiler flag). The compiler bug has been reported to SGI. + +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 + it works when compiled with cc. + +- on Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 + is necessary to get gzprintf working correctly. This is done by configure. + +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works + with other compilers. Use "make test" to check your compiler. + +- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers. + +- For Turbo C the small model is supported only with reduced performance to + avoid any far allocation; it was tested with -DMAX_WBITS=11 -DMAX_MEM_LEVEL=3 + +- For PalmOs, see http://www.cs.uit.no/~perm/PASTA/pilot/software.html + Per Harald Myrvang + + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate + and zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; + they are too numerous to cite here. + +Copyright notice: + + (C) 1995-1998 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* +receiving lengthy legal documents to sign. The sources are provided +for free but without warranty of any kind. The library has been +entirely written by Jean-loup Gailly and Mark Adler; it does not +include third-party code. + +If you redistribute modified sources, we would appreciate that you include +in the file ChangeLog history information documenting your changes. diff --git a/bazaar/plugin/gdal/frmts/zlib/adler32.c b/bazaar/plugin/gdal/frmts/zlib/adler32.c new file mode 100644 index 000000000..ffccbe49d --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: adler32.c 10656 2007-01-19 01:31:01Z mloskot $ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/bazaar/plugin/gdal/frmts/zlib/compress.c b/bazaar/plugin/gdal/frmts/zlib/compress.c new file mode 100644 index 000000000..17f60a119 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: compress.c 10656 2007-01-19 01:31:01Z mloskot $ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/bazaar/plugin/gdal/frmts/zlib/crc32.c b/bazaar/plugin/gdal/frmts/zlib/crc32.c new file mode 100644 index 000000000..65f2f23a2 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id: crc32.c 10656 2007-01-19 01:31:01Z mloskot $ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/bazaar/plugin/gdal/frmts/zlib/crc32.h b/bazaar/plugin/gdal/frmts/zlib/crc32.h new file mode 100644 index 000000000..8053b6117 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/bazaar/plugin/gdal/frmts/zlib/deflate.c b/bazaar/plugin/gdal/frmts/zlib/deflate.c new file mode 100644 index 000000000..6560287b5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id: deflate.c 10656 2007-01-19 01:31:01Z mloskot $ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/bazaar/plugin/gdal/frmts/zlib/deflate.h b/bazaar/plugin/gdal/frmts/zlib/deflate.h new file mode 100644 index 000000000..180cc5008 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: deflate.h 28039 2014-11-30 18:24:59Z rouault $ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to suppress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/bazaar/plugin/gdal/frmts/zlib/gzio.c b/bazaar/plugin/gdal/frmts/zlib/gzio.c new file mode 100644 index 000000000..37afda47e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/gzio.c @@ -0,0 +1,1026 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id: gzio.c 10656 2007-01-19 01:31:01Z mloskot $ */ + +#include + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int ZEXPORT gzdirect (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff --git a/bazaar/plugin/gdal/frmts/zlib/infback.c b/bazaar/plugin/gdal/frmts/zlib/infback.c new file mode 100644 index 000000000..455dbc9ee --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/infback.c @@ -0,0 +1,623 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + + /* process literal */ + if (this.op == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/bazaar/plugin/gdal/frmts/zlib/inffast.c b/bazaar/plugin/gdal/frmts/zlib/inffast.c new file mode 100644 index 000000000..bbee92ed1 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/inffast.c @@ -0,0 +1,318 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/bazaar/plugin/gdal/frmts/zlib/inffast.h b/bazaar/plugin/gdal/frmts/zlib/inffast.h new file mode 100644 index 000000000..1e88d2d97 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/bazaar/plugin/gdal/frmts/zlib/inffixed.h b/bazaar/plugin/gdal/frmts/zlib/inffixed.h new file mode 100644 index 000000000..75ed4b597 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/bazaar/plugin/gdal/frmts/zlib/inflate.c b/bazaar/plugin/gdal/frmts/zlib/inflate.c new file mode 100644 index 000000000..a6c40dd44 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/inflate.c @@ -0,0 +1,1370 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->flags = 0; + state->dmax = 32768U; + state->check = 0; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/bazaar/plugin/gdal/frmts/zlib/inflate.h b/bazaar/plugin/gdal/frmts/zlib/inflate.h new file mode 100644 index 000000000..07bd3e78a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/bazaar/plugin/gdal/frmts/zlib/inftrees.c b/bazaar/plugin/gdal/frmts/zlib/inftrees.c new file mode 100644 index 000000000..8a9c13ff0 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/bazaar/plugin/gdal/frmts/zlib/inftrees.h b/bazaar/plugin/gdal/frmts/zlib/inftrees.h new file mode 100644 index 000000000..b1104c87e --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/bazaar/plugin/gdal/frmts/zlib/makefile.vc b/bazaar/plugin/gdal/frmts/zlib/makefile.vc new file mode 100644 index 000000000..9dd2a02a5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/makefile.vc @@ -0,0 +1,30 @@ +# $Id: makefile.vc 21680 2011-02-11 21:12:07Z warmerdam $ +# +# Makefile to build zlib using NMAKE and Visual C++ compiler. +# +OBJ = \ + adler32.obj \ + compress.obj \ + crc32.obj \ + deflate.obj \ + gzio.obj \ + infback.obj \ + inffast.obj \ + inflate.obj \ + inftrees.obj \ + trees.obj \ + uncompr.obj \ + zutil.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = $(SOFTWARNFLAGS) /wd4131 + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/zlib/trees.c b/bazaar/plugin/gdal/frmts/zlib/trees.c new file mode 100644 index 000000000..8faf40ed5 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id: trees.c 10656 2007-01-19 01:31:01Z mloskot $ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/bazaar/plugin/gdal/frmts/zlib/trees.h b/bazaar/plugin/gdal/frmts/zlib/trees.h new file mode 100644 index 000000000..72facf900 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/bazaar/plugin/gdal/frmts/zlib/uncompr.c b/bazaar/plugin/gdal/frmts/zlib/uncompr.c new file mode 100644 index 000000000..e73deb549 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/uncompr.c @@ -0,0 +1,61 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: uncompr.c 10656 2007-01-19 01:31:01Z mloskot $ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/bazaar/plugin/gdal/frmts/zlib/zconf.h b/bazaar/plugin/gdal/frmts/zlib/zconf.h new file mode 100644 index 000000000..7164db018 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zconf.h 10656 2007-01-19 01:31:01Z mloskot $ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/bazaar/plugin/gdal/frmts/zlib/zlib.h b/bazaar/plugin/gdal/frmts/zlib/zlib.h new file mode 100644 index 000000000..6c5f03e04 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/zlib.h @@ -0,0 +1,1357 @@ + /* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/bazaar/plugin/gdal/frmts/zlib/zutil.c b/bazaar/plugin/gdal/frmts/zlib/zutil.c new file mode 100644 index 000000000..72f6364dd --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zutil.c 10656 2007-01-19 01:31:01Z mloskot $ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/bazaar/plugin/gdal/frmts/zlib/zutil.h b/bazaar/plugin/gdal/frmts/zlib/zutil.h new file mode 100644 index 000000000..eacad9e24 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zlib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: zutil.h 13360 2007-12-18 03:47:06Z mloskot $ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) && (_MSC_VER < 1500) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/bazaar/plugin/gdal/frmts/zmap/GNUmakefile b/bazaar/plugin/gdal/frmts/zmap/GNUmakefile new file mode 100644 index 000000000..f94d95414 --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zmap/GNUmakefile @@ -0,0 +1,13 @@ + +OBJ = zmapdataset.o + +include ../../GDALmake.opt + + + +default: $(OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/frmts/zmap/makefile.vc b/bazaar/plugin/gdal/frmts/zmap/makefile.vc new file mode 100644 index 000000000..d26204a3a --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zmap/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = zmapdataset.obj + +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + xcopy /D /Y *.obj ..\o + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/frmts/zmap/zmapdataset.cpp b/bazaar/plugin/gdal/frmts/zmap/zmapdataset.cpp new file mode 100644 index 000000000..7f6cf45ab --- /dev/null +++ b/bazaar/plugin/gdal/frmts/zmap/zmapdataset.cpp @@ -0,0 +1,726 @@ +/****************************************************************************** + * $Id: zmapdataset.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: ZMap driver + * Purpose: GDALDataset driver for ZMap dataset. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "gdal_pam.h" + +CPL_CVSID("$Id: zmapdataset.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +CPL_C_START +void GDALRegister_ZMap(void); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* ZMapDataset */ +/* ==================================================================== */ +/************************************************************************/ + +class ZMapRasterBand; + +class ZMapDataset : public GDALPamDataset +{ + friend class ZMapRasterBand; + + VSILFILE *fp; + int nValuesPerLine; + int nFieldSize; + int nDecimalCount; + int nColNum; + double dfNoDataValue; + vsi_l_offset nDataStartOff; + double adfGeoTransform[6]; + + public: + ZMapDataset(); + virtual ~ZMapDataset(); + + virtual CPLErr GetGeoTransform( double * ); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset *CreateCopy( const char * pszFilename, GDALDataset *poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* ZMapRasterBand */ +/* ==================================================================== */ +/************************************************************************/ + +class ZMapRasterBand : public GDALPamRasterBand +{ + friend class ZMapDataset; + + public: + + ZMapRasterBand( ZMapDataset * ); + + virtual CPLErr IReadBlock( int, int, void * ); + + virtual double GetNoDataValue( int *pbSuccess = NULL ); +}; + + +/************************************************************************/ +/* ZMapRasterBand() */ +/************************************************************************/ + +ZMapRasterBand::ZMapRasterBand( ZMapDataset *poDS ) + +{ + this->poDS = poDS; + this->nBand = nBand; + + eDataType = GDT_Float64; + + nBlockXSize = 1; + nBlockYSize = poDS->GetRasterYSize(); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr ZMapRasterBand::IReadBlock( int nBlockXOff, + CPL_UNUSED int nBlockYOff, + void * pImage ) +{ + int i; + ZMapDataset *poGDS = (ZMapDataset *) poDS; + + if (poGDS->fp == NULL) + return CE_Failure; + + if (nBlockXOff < poGDS->nColNum + 1) + { + VSIFSeekL(poGDS->fp, poGDS->nDataStartOff, SEEK_SET); + poGDS->nColNum = -1; + } + + if (nBlockXOff > poGDS->nColNum + 1) + { + for(i=poGDS->nColNum + 1;inDecimalCount); + while(ifp); + if (pszLine == NULL) + return CE_Failure; + int nExpected = nRasterYSize - i; + if (nExpected > poGDS->nValuesPerLine) + nExpected = poGDS->nValuesPerLine; + if ((int)strlen(pszLine) != nExpected * poGDS->nFieldSize) + return CE_Failure; + + for(int j=0;jnFieldSize; + char chSaved = pszValue[poGDS->nFieldSize]; + pszValue[poGDS->nFieldSize] = 0; + if (strchr(pszValue, '.') != NULL) + ((double*)pImage)[i+j] = CPLAtofM(pszValue); + else + ((double*)pImage)[i+j] = atoi(pszValue) * dfExp; + pszValue[poGDS->nFieldSize] = chSaved; + } + + i += nExpected; + } + + poGDS->nColNum ++; + + return CE_None; +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double ZMapRasterBand::GetNoDataValue( int *pbSuccess ) +{ + ZMapDataset *poGDS = (ZMapDataset *) poDS; + + if (pbSuccess) + *pbSuccess = TRUE; + + return poGDS->dfNoDataValue; +} + +/************************************************************************/ +/* ~ZMapDataset() */ +/************************************************************************/ + +ZMapDataset::ZMapDataset() +{ + fp = NULL; + nDataStartOff = 0; + nColNum = -1; + nValuesPerLine = 0; + nFieldSize = 0; + nDecimalCount = 0; + dfNoDataValue = 0.0; + adfGeoTransform[0] = 0; + adfGeoTransform[1] = 1; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = 0; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = 1; +} + +/************************************************************************/ +/* ~ZMapDataset() */ +/************************************************************************/ + +ZMapDataset::~ZMapDataset() + +{ + FlushCache(); + if (fp) + VSIFCloseL(fp); +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int ZMapDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + int i; + + if (poOpenInfo->nHeaderBytes == 0) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Chech that it looks roughly as a ZMap dataset */ +/* -------------------------------------------------------------------- */ + const char* pszData = (const char*)poOpenInfo->pabyHeader; + + /* Skip comments line at the beginning */ + i=0; + if (pszData[i] == '!') + { + i++; + for(;inHeaderBytes;i++) + { + char ch = pszData[i]; + if (ch == 13 || ch == 10) + { + i++; + if (ch == 13 && pszData[i] == 10) + i++; + if (pszData[i] != '!') + break; + } + } + } + + if (pszData[i] != '@') + return FALSE; + i++; + + char** papszTokens = CSLTokenizeString2( pszData+i, ",", 0 ); + if (CSLCount(papszTokens) < 3) + { + CSLDestroy(papszTokens); + return FALSE; + } + + const char* pszToken = papszTokens[1]; + while (*pszToken == ' ') + pszToken ++; + + if (strncmp(pszToken, "GRID", 4) != 0) + { + CSLDestroy(papszTokens); + return FALSE; + } + + CSLDestroy(papszTokens); + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *ZMapDataset::Open( GDALOpenInfo * poOpenInfo ) + +{ + if (!Identify(poOpenInfo)) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Find dataset characteristics */ +/* -------------------------------------------------------------------- */ + VSILFILE* fp = VSIFOpenL(poOpenInfo->pszFilename, "rb"); + if (fp == NULL) + return NULL; + + const char* pszLine; + + while((pszLine = CPLReadLine2L(fp, 100, NULL)) != NULL) + { + if (*pszLine == '!') + { + continue; + } + else + break; + } + if (pszLine == NULL) + { + VSIFCloseL(fp); + return NULL; + } + + /* Parse first header line */ + char** papszTokens = CSLTokenizeString2( pszLine, ",", 0 ); + if (CSLCount(papszTokens) != 3) + { + CSLDestroy(papszTokens); + VSIFCloseL(fp); + return NULL; + } + + int nValuesPerLine = atoi(papszTokens[2]); + if (nValuesPerLine <= 0) + { + CSLDestroy(papszTokens); + VSIFCloseL(fp); + return NULL; + } + + CSLDestroy(papszTokens); + papszTokens = NULL; + + /* Parse second header line */ + pszLine = CPLReadLine2L(fp, 100, NULL); + if (pszLine == NULL) + { + VSIFCloseL(fp); + return NULL; + } + papszTokens = CSLTokenizeString2( pszLine, ",", 0 ); + if (CSLCount(papszTokens) != 5) + { + CSLDestroy(papszTokens); + VSIFCloseL(fp); + return NULL; + } + + int nFieldSize = atoi(papszTokens[0]); + double dfNoDataValue = CPLAtofM(papszTokens[1]); + int nDecimalCount = atoi(papszTokens[3]); + int nColumnNumber = atoi(papszTokens[4]); + + CSLDestroy(papszTokens); + papszTokens = NULL; + + if (nFieldSize <= 0 || nFieldSize >= 40 || + nDecimalCount <= 0 || nDecimalCount >= nFieldSize || + nColumnNumber != 1) + { + CPLDebug("ZMap", "nFieldSize=%d, nDecimalCount=%d, nColumnNumber=%d", + nFieldSize, nDecimalCount, nColumnNumber); + VSIFCloseL(fp); + return NULL; + } + + /* Parse third header line */ + pszLine = CPLReadLine2L(fp, 100, NULL); + if (pszLine == NULL) + { + VSIFCloseL(fp); + return NULL; + } + papszTokens = CSLTokenizeString2( pszLine, ",", 0 ); + if (CSLCount(papszTokens) != 6) + { + CSLDestroy(papszTokens); + VSIFCloseL(fp); + return NULL; + } + + int nRows = atoi(papszTokens[0]); + int nCols = atoi(papszTokens[1]); + double dfMinX = CPLAtofM(papszTokens[2]); + double dfMaxX = CPLAtofM(papszTokens[3]); + double dfMinY = CPLAtofM(papszTokens[4]); + double dfMaxY = CPLAtofM(papszTokens[5]); + + CSLDestroy(papszTokens); + papszTokens = NULL; + + if (!GDALCheckDatasetDimensions(nCols, nRows) || + nCols == 1 || nRows == 1) + { + VSIFCloseL(fp); + return NULL; + } + + /* Ignore fourth header line */ + pszLine = CPLReadLine2L(fp, 100, NULL); + if (pszLine == NULL) + { + VSIFCloseL(fp); + return NULL; + } + + /* Check fifth header line */ + pszLine = CPLReadLine2L(fp, 100, NULL); + if (pszLine == NULL || pszLine[0] != '@') + { + VSIFCloseL(fp); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding GDALDataset. */ +/* -------------------------------------------------------------------- */ + ZMapDataset *poDS; + + poDS = new ZMapDataset(); + poDS->fp = fp; + poDS->nDataStartOff = VSIFTellL(fp); + poDS->nValuesPerLine = nValuesPerLine; + poDS->nFieldSize = nFieldSize; + poDS->nDecimalCount = nDecimalCount; + poDS->nRasterXSize = nCols; + poDS->nRasterYSize = nRows; + poDS->dfNoDataValue = dfNoDataValue; + + if (CSLTestBoolean(CPLGetConfigOption("ZMAP_PIXEL_IS_POINT", "FALSE"))) + { + double dfStepX = (dfMaxX - dfMinX) / (nCols - 1); + double dfStepY = (dfMaxY - dfMinY) / (nRows - 1); + + poDS->adfGeoTransform[0] = dfMinX - dfStepX / 2; + poDS->adfGeoTransform[1] = dfStepX; + poDS->adfGeoTransform[3] = dfMaxY + dfStepY / 2; + poDS->adfGeoTransform[5] = -dfStepY; + } + else + { + double dfStepX = (dfMaxX - dfMinX) / nCols ; + double dfStepY = (dfMaxY - dfMinY) / nRows; + + poDS->adfGeoTransform[0] = dfMinX; + poDS->adfGeoTransform[1] = dfStepX; + poDS->adfGeoTransform[3] = dfMaxY; + poDS->adfGeoTransform[5] = -dfStepY; + } + +/* -------------------------------------------------------------------- */ +/* Create band information objects. */ +/* -------------------------------------------------------------------- */ + poDS->nBands = 1; + poDS->SetBand( 1, new ZMapRasterBand( poDS ) ); + +/* -------------------------------------------------------------------- */ +/* Initialize any PAM information. */ +/* -------------------------------------------------------------------- */ + poDS->SetDescription( poOpenInfo->pszFilename ); + poDS->TryLoadXML(); + +/* -------------------------------------------------------------------- */ +/* Support overviews. */ +/* -------------------------------------------------------------------- */ + poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); + return( poDS ); +} + + +/************************************************************************/ +/* WriteRightJustified() */ +/************************************************************************/ + +static void WriteRightJustified(VSILFILE* fp, const char *pszValue, int nWidth) +{ + int nLen = strlen(pszValue); + CPLAssert(nLen <= nWidth); + int i; + for(i=0;i= 0) + sprintf(szFormat, "%%.%df", nDecimals); + else + sprintf(szFormat, "%%g"); + char* pszValue = (char*)CPLSPrintf(szFormat, dfValue); + char* pszE = strchr(pszValue, 'e'); + if (pszE) + *pszE = 'E'; + + if ((int)strlen(pszValue) > nWidth) + { + sprintf(szFormat, "%%.%dg", nDecimals); + pszValue = (char*)CPLSPrintf(szFormat, dfValue); + pszE = strchr(pszValue, 'e'); + if (pszE) + *pszE = 'E'; + } + + CPLString osValue(pszValue); + WriteRightJustified(fp, osValue.c_str(), nWidth); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset* ZMapDataset::CreateCopy( const char * pszFilename, + GDALDataset *poSrcDS, + int bStrict, + CPL_UNUSED char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ +/* -------------------------------------------------------------------- */ +/* Some some rudimentary checks */ +/* -------------------------------------------------------------------- */ + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "ZMap driver does not support source dataset with zero band.\n"); + return NULL; + } + + if (nBands != 1) + { + CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported, + "ZMap driver only uses the first band of the dataset.\n"); + if (bStrict) + return NULL; + } + + if( pfnProgress && !pfnProgress( 0.0, NULL, pProgressData ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get source dataset info */ +/* -------------------------------------------------------------------- */ + + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + if (nXSize == 1 || nYSize == 1) + { + return NULL; + } + + double adfGeoTransform[6]; + poSrcDS->GetGeoTransform(adfGeoTransform); + if (adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "ZMap driver does not support CreateCopy() from skewed or rotated dataset.\n"); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create target file */ +/* -------------------------------------------------------------------- */ + + VSILFILE* fp = VSIFOpenL(pszFilename, "wb"); + if (fp == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot create %s", pszFilename ); + return NULL; + } + + int nFieldSize = 20; + int nValuesPerLine = 4; + int nDecimalCount = 7; + + int bHasNoDataValue = FALSE; + double dfNoDataValue = + poSrcDS->GetRasterBand(1)->GetNoDataValue(&bHasNoDataValue); + if (!bHasNoDataValue) + dfNoDataValue = 1.e30; + + VSIFPrintfL(fp, "!\n"); + VSIFPrintfL(fp, "! Created by GDAL.\n"); + VSIFPrintfL(fp, "!\n"); + VSIFPrintfL(fp, "@GRID FILE, GRID, %d\n", nValuesPerLine); + + WriteRightJustified(fp, nFieldSize, 10); + VSIFPrintfL(fp, ","); + WriteRightJustified(fp, dfNoDataValue, 10); + VSIFPrintfL(fp, ","); + WriteRightJustified(fp, "", 10); + VSIFPrintfL(fp, ","); + WriteRightJustified(fp, nDecimalCount, 10); + VSIFPrintfL(fp, ","); + WriteRightJustified(fp, 1, 10); + VSIFPrintfL(fp, "\n"); + + WriteRightJustified(fp, nYSize, 10); + VSIFPrintfL(fp, ","); + WriteRightJustified(fp, nXSize, 10); + VSIFPrintfL(fp, ","); + + if (CSLTestBoolean(CPLGetConfigOption("ZMAP_PIXEL_IS_POINT", "FALSE"))) + { + WriteRightJustified(fp, adfGeoTransform[0] + adfGeoTransform[1] / 2, 14, 7); + VSIFPrintfL(fp, ","); + WriteRightJustified(fp, adfGeoTransform[0] + adfGeoTransform[1] * nXSize - + adfGeoTransform[1] / 2, 14, 7); + VSIFPrintfL(fp, ","); + WriteRightJustified(fp, adfGeoTransform[3] + adfGeoTransform[5] * nYSize - + adfGeoTransform[5] / 2, 14, 7); + VSIFPrintfL(fp, ","); + WriteRightJustified(fp, adfGeoTransform[3] + adfGeoTransform[5] / 2, 14, 7); + } + else + { + WriteRightJustified(fp, adfGeoTransform[0], 14, 7); + VSIFPrintfL(fp, ","); + WriteRightJustified(fp, adfGeoTransform[0] + adfGeoTransform[1] * nXSize, 14, 7); + VSIFPrintfL(fp, ","); + WriteRightJustified(fp, adfGeoTransform[3] + adfGeoTransform[5] * nYSize, 14, 7); + VSIFPrintfL(fp, ","); + WriteRightJustified(fp, adfGeoTransform[3], 14, 7); + } + + VSIFPrintfL(fp, "\n"); + + VSIFPrintfL(fp, "0.0, 0.0, 0.0\n"); + VSIFPrintfL(fp, "@\n"); + +/* -------------------------------------------------------------------- */ +/* Copy imagery */ +/* -------------------------------------------------------------------- */ + double* padfLineBuffer = (double*) CPLMalloc(nYSize * sizeof(double)); + int i, j; + CPLErr eErr = CE_None; + for(i=0;iGetRasterBand(1)->RasterIO( + GF_Read, i, 0, 1, nYSize, + padfLineBuffer, 1, nYSize, + GDT_Float64, 0, 0, NULL); + if (eErr != CE_None) + break; + int bEOLPrinted = FALSE; + for(j=0;jSetDescription( "ZMap" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ZMap Plus Grid" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "frmt_various.html#ZMap" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "dat" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = ZMapDataset::Open; + poDriver->pfnIdentify = ZMapDataset::Identify; + poDriver->pfnCreateCopy = ZMapDataset::CreateCopy; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/gcore/GNUmakefile b/bazaar/plugin/gdal/gcore/GNUmakefile new file mode 100644 index 000000000..9c0c4bc9a --- /dev/null +++ b/bazaar/plugin/gdal/gcore/GNUmakefile @@ -0,0 +1,60 @@ + +include ../GDALmake.opt + +OBJ = gdalopeninfo.o gdaldrivermanager.o gdaldriver.o gdaldataset.o \ + gdalrasterband.o gdal_misc.o rasterio.o gdalrasterblock.o \ + gdalcolortable.o gdalmajorobject.o overview.o \ + gdaldefaultoverviews.o gdalpamdataset.o gdalpamrasterband.o \ + gdaljp2metadata.o gdaljp2box.o gdalmultidomainmetadata.o \ + gdal_rat.o gdalgmlcoverage.o gdalpamproxydb.o \ + gdalallvalidmaskband.o gdalnodatamaskband.o \ + gdalproxydataset.o gdalproxypool.o gdaldefaultasync.o \ + gdalnodatavaluesmaskband.o gdaldllmain.o gdalexif.o gdalclientserver.o \ + gdalgeorefpamdataset.o gdaljp2abstractdataset.o gdalvirtualmem.o \ + gdaloverviewdataset.o gdalrescaledalphaband.o gdaljp2structure.o \ + gdal_mdreader.o gdaljp2metadatagenerator.o + +# Enable the following if you want to use MITAB's code to convert +# .tab coordinate systems into well known text. But beware that linking +# against static libraries becomes more complicated because of the odd +# call out. + +ifeq ($(TIFF_SETTING),internal) +ifeq ($(RENAME_INTERNAL_LIBTIFF_SYMBOLS),yes) +XTRA_OPT := $(XTRA_OPT) -DRENAME_INTERNAL_LIBTIFF_SYMBOLS +endif +endif + +CPPFLAGS := -I../frmts/gtiff -I../frmts/mem -I../frmts/vrt -I../ogr -I../ogr/ogrsf_frmts/generic $(JSON_INCLUDE) -I../ogr/ogrsf_frmts/geojson $(CPPFLAGS) $(PAM_SETTING) $(XTRA_OPT) + +ifeq ($(HAVE_SQLITE),yes) +CXXFLAGS := $(CXXFLAGS) -DSQLITE_ENABLED +endif + +ifeq ($(HAVE_LIBXML2),yes) +CXXFLAGS := $(CXXFLAGS) $(LIBXML2_INC) -DHAVE_LIBXML2 +endif + +default: mdreader-target $(OBJ:.o=.$(OBJ_EXT)) + +$(OBJ): gdal_priv.h gdal_proxy.h + +clean: mdreader-clean + $(RM) *.o $(O_OBJ) + +docs: + (cd ..; $(MAKE) docs) + +gdal_misc.$(OBJ_EXT): gdal_misc.cpp gdal_version.h + +gdaldrivermanager.$(OBJ_EXT): gdaldrivermanager.cpp ../GDALmake.opt + $(CXX) -c $(GDAL_INCLUDE) $(CXXFLAGS) -DINST_DATA=\"$(INST_DATA)\" \ + $< -o $@ + +install: + for f in *.h ; do $(INSTALL_DATA) $$f $(DESTDIR)$(INST_INCLUDE) ; done + +# Small test tool using gdaljp2box.cpp to read jpeg2000 boxes. Not normally +# built or installed. +jp2dump: jp2dump.o + $(CXX) $(CXXFLAGS) jp2dump.o $(CONFIG_LIBS) -o jp2dump diff --git a/bazaar/plugin/gdal/gcore/Version.rc b/bazaar/plugin/gdal/gcore/Version.rc new file mode 100644 index 000000000..a1eecaf1b --- /dev/null +++ b/bazaar/plugin/gdal/gcore/Version.rc @@ -0,0 +1,95 @@ +/****************************************************************************** + * $Id: Version.rc 13360 2007-12-18 03:47:06Z mloskot $ + * + * Project: GDAL Core + * Purpose: GDAL DLL registration information. + * Author: Martin Daly (Cadcorp) + * + * Copyright assignment provided by Martin Daly by email, "Be my guest. Fame! + * At last! Best not let it go to my head, eh?" + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * $Log$ + * Revision 1.6 2006/03/30 15:42:55 fwarmerdam + * Added explicit not on right to use. + * + * Revision 1.5 2006/03/28 14:49:56 fwarmerdam + * updated contact info + * + */ +#define APSTUDIO_HIDDEN_SYMBOLS +#include +#undef APSTUDIO_HIDDEN_SYMBOLS +#include + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +#ifdef _WIN32 +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(1252) +#endif //_WIN32 + +#include "gdal.h" + +#ifndef _MAC + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION GDAL_VERSION_MAJOR,GDAL_VERSION_MINOR,GDAL_VERSION_REV,GDAL_VERSION_BUILD + PRODUCTVERSION GDAL_VERSION_MAJOR,GDAL_VERSION_MINOR,GDAL_VERSION_REV,GDAL_VERSION_BUILD + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", "OSGeo\0" + VALUE "FileDescription", "Geospatial Data Abstraction Library\0" + VALUE "FileVersion", GDAL_RELEASE_NAME "\0" + VALUE "InternalName", "GDAL\0" + VALUE "LegalCopyright", "See LICENSE.TXT" + VALUE "ProductName", "GDAL/OGR\0" + VALUE "ProductVersion", GDAL_RELEASE_NAME "\0" + VALUE "WebPage", "http://www.gdal.org/\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + +#endif // !_MAC + +#endif // Neutral resources diff --git a/bazaar/plugin/gdal/gcore/gdal.h b/bazaar/plugin/gdal/gcore/gdal.h new file mode 100644 index 000000000..d361ae5fb --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdal.h @@ -0,0 +1,1090 @@ +/****************************************************************************** + * $Id: gdal.h 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: GDAL Core + * Purpose: GDAL Core C/Public declarations. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, 2002 Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_H_INCLUDED +#define GDAL_H_INCLUDED + +/** + * \file gdal.h + * + * Public (C callable) GDAL entry points. + */ + +#ifndef DOXYGEN_SKIP +#include "gdal_version.h" +#include "cpl_port.h" +#include "cpl_error.h" +#include "cpl_progress.h" +#include "cpl_virtualmem.h" +#include "cpl_minixml.h" +#include "ogr_api.h" +#endif + +/* -------------------------------------------------------------------- */ +/* Significant constants. */ +/* -------------------------------------------------------------------- */ + +CPL_C_START + +/*! Pixel data types */ +typedef enum { + /*! Unknown or unspecified type */ GDT_Unknown = 0, + /*! Eight bit unsigned integer */ GDT_Byte = 1, + /*! Sixteen bit unsigned integer */ GDT_UInt16 = 2, + /*! Sixteen bit signed integer */ GDT_Int16 = 3, + /*! Thirty two bit unsigned integer */ GDT_UInt32 = 4, + /*! Thirty two bit signed integer */ GDT_Int32 = 5, + /*! Thirty two bit floating point */ GDT_Float32 = 6, + /*! Sixty four bit floating point */ GDT_Float64 = 7, + /*! Complex Int16 */ GDT_CInt16 = 8, + /*! Complex Int32 */ GDT_CInt32 = 9, + /*! Complex Float32 */ GDT_CFloat32 = 10, + /*! Complex Float64 */ GDT_CFloat64 = 11, + GDT_TypeCount = 12 /* maximum type # + 1 */ +} GDALDataType; + +int CPL_DLL CPL_STDCALL GDALGetDataTypeSize( GDALDataType ); +int CPL_DLL CPL_STDCALL GDALDataTypeIsComplex( GDALDataType ); +const char CPL_DLL * CPL_STDCALL GDALGetDataTypeName( GDALDataType ); +GDALDataType CPL_DLL CPL_STDCALL GDALGetDataTypeByName( const char * ); +GDALDataType CPL_DLL CPL_STDCALL GDALDataTypeUnion( GDALDataType, GDALDataType ); + +/** +* status of the asynchronous stream +*/ +typedef enum +{ + GARIO_PENDING = 0, + GARIO_UPDATE = 1, + GARIO_ERROR = 2, + GARIO_COMPLETE = 3, + GARIO_TypeCount = 4 +} GDALAsyncStatusType; + +const char CPL_DLL * CPL_STDCALL GDALGetAsyncStatusTypeName( GDALAsyncStatusType ); +GDALAsyncStatusType CPL_DLL CPL_STDCALL GDALGetAsyncStatusTypeByName( const char * ); + +/*! Flag indicating read/write, or read-only access to data. */ +typedef enum { + /*! Read only (no update) access */ GA_ReadOnly = 0, + /*! Read/write access. */ GA_Update = 1 +} GDALAccess; + +/*! Read/Write flag for RasterIO() method */ +typedef enum { + /*! Read data */ GF_Read = 0, + /*! Write data */ GF_Write = 1 +} GDALRWFlag; + +/* NOTE: values are selected to be consistent with GDALResampleAlg of alg/gdalwarper.h */ +/** RasterIO() resampling method. + * @since GDAL 2.0 + */ +typedef enum +{ + /*! Nearest neighbour */ GRIORA_NearestNeighbour = 0, + /*! Bilinear (2x2 kernel) */ GRIORA_Bilinear = 1, + /*! Cubic Convolution Approximation (4x4 kernel) */ GRIORA_Cubic = 2, + /*! Cubic B-Spline Approximation (4x4 kernel) */ GRIORA_CubicSpline = 3, + /*! Lanczos windowed sinc interpolation (6x6 kernel) */ GRIORA_Lanczos = 4, + /*! Average */ GRIORA_Average = 5, + /*! Mode (selects the value which appears most often of all the sampled points) */ + GRIORA_Mode = 6, + /*! Gauss blurring */ GRIORA_Gauss = 7 + /* NOTE: values 8 to 12 are reserved for max,min,med,Q1,Q3 */ +} GDALRIOResampleAlg; + +/* NOTE to developers: only add members, and if so edit INIT_RASTERIO_EXTRA_ARG */ +/* and INIT_RASTERIO_EXTRA_ARG */ +/** Structure to pass extra arguments to RasterIO() method + * @since GDAL 2.0 + */ +typedef struct +{ + /*! Version of structure (to allow future extensions of the structure) */ + int nVersion; + + /*! Resampling algorithm */ + GDALRIOResampleAlg eResampleAlg; + + /*! Progress callback */ + GDALProgressFunc pfnProgress; + /*! Progress callback user data */ + void *pProgressData; + + /*! Indicate if dfXOff, dfYOff, dfXSize and dfYSize are set. + Mostly reserved from the VRT driver to communicate a more precise + source window. Must be such that dfXOff - nXOff < 1.0 and + dfYOff - nYOff < 1.0 and nXSize - dfXSize < 1.0 and nYSize - dfYSize < 1.0 */ + int bFloatingPointWindowValidity; + /*! Pixel offset to the top left corner. Only valid if bFloatingPointWindowValidity = TRUE */ + double dfXOff; + /*! Line offset to the top left corner. Only valid if bFloatingPointWindowValidity = TRUE */ + double dfYOff; + /*! Width in pixels of the area of interest. Only valid if bFloatingPointWindowValidity = TRUE */ + double dfXSize; + /*! Height in pixels of the area of interest. Only valid if bFloatingPointWindowValidity = TRUE */ + double dfYSize; +} GDALRasterIOExtraArg; + +#define RASTERIO_EXTRA_ARG_CURRENT_VERSION 1 + +/** Macro to initialize an instance of GDALRasterIOExtraArg structure. + * @since GDAL 2.0 + */ +#define INIT_RASTERIO_EXTRA_ARG(s) \ + do { (s).nVersion = RASTERIO_EXTRA_ARG_CURRENT_VERSION; \ + (s).eResampleAlg = GRIORA_NearestNeighbour; \ + (s).pfnProgress = NULL; \ + (s).pProgressData = NULL; \ + (s).bFloatingPointWindowValidity = FALSE; } while(0) + +/*! Types of color interpretation for raster bands. */ +typedef enum +{ + GCI_Undefined=0, + /*! Greyscale */ GCI_GrayIndex=1, + /*! Paletted (see associated color table) */ GCI_PaletteIndex=2, + /*! Red band of RGBA image */ GCI_RedBand=3, + /*! Green band of RGBA image */ GCI_GreenBand=4, + /*! Blue band of RGBA image */ GCI_BlueBand=5, + /*! Alpha (0=transparent, 255=opaque) */ GCI_AlphaBand=6, + /*! Hue band of HLS image */ GCI_HueBand=7, + /*! Saturation band of HLS image */ GCI_SaturationBand=8, + /*! Lightness band of HLS image */ GCI_LightnessBand=9, + /*! Cyan band of CMYK image */ GCI_CyanBand=10, + /*! Magenta band of CMYK image */ GCI_MagentaBand=11, + /*! Yellow band of CMYK image */ GCI_YellowBand=12, + /*! Black band of CMLY image */ GCI_BlackBand=13, + /*! Y Luminance */ GCI_YCbCr_YBand=14, + /*! Cb Chroma */ GCI_YCbCr_CbBand=15, + /*! Cr Chroma */ GCI_YCbCr_CrBand=16, + /*! Max current value */ GCI_Max=16 +} GDALColorInterp; + +const char CPL_DLL *GDALGetColorInterpretationName( GDALColorInterp ); +GDALColorInterp CPL_DLL GDALGetColorInterpretationByName( const char *pszName ); + +/*! Types of color interpretations for a GDALColorTable. */ +typedef enum +{ + /*! Grayscale (in GDALColorEntry.c1) */ GPI_Gray=0, + /*! Red, Green, Blue and Alpha in (in c1, c2, c3 and c4) */ GPI_RGB=1, + /*! Cyan, Magenta, Yellow and Black (in c1, c2, c3 and c4)*/ GPI_CMYK=2, + /*! Hue, Lightness and Saturation (in c1, c2, and c3) */ GPI_HLS=3 +} GDALPaletteInterp; + +const char CPL_DLL *GDALGetPaletteInterpretationName( GDALPaletteInterp ); + +/* "well known" metadata items. */ + +#define GDALMD_AREA_OR_POINT "AREA_OR_POINT" +# define GDALMD_AOP_AREA "Area" +# define GDALMD_AOP_POINT "Point" + +/* -------------------------------------------------------------------- */ +/* GDAL Specific error codes. */ +/* */ +/* error codes 100 to 299 reserved for GDAL. */ +/* -------------------------------------------------------------------- */ +#define CPLE_WrongFormat 200 + +/* -------------------------------------------------------------------- */ +/* Define handle types related to various internal classes. */ +/* -------------------------------------------------------------------- */ + +/** Opaque type used for the C bindings of the C++ GDALMajorObject class */ +typedef void *GDALMajorObjectH; + +/** Opaque type used for the C bindings of the C++ GDALDataset class */ +typedef void *GDALDatasetH; + +/** Opaque type used for the C bindings of the C++ GDALRasterBand class */ +typedef void *GDALRasterBandH; + +/** Opaque type used for the C bindings of the C++ GDALDriver class */ +typedef void *GDALDriverH; + +/** Opaque type used for the C bindings of the C++ GDALColorTable class */ +typedef void *GDALColorTableH; + +/** Opaque type used for the C bindings of the C++ GDALRasterAttributeTable class */ +typedef void *GDALRasterAttributeTableH; + +/** Opaque type used for the C bindings of the C++ GDALAsyncReader class */ +typedef void *GDALAsyncReaderH; + +/** Type to express pixel, line or band spacing. Signed 64 bit integer. */ +typedef GIntBig GSpacing; + +/* ==================================================================== */ +/* Registration/driver related. */ +/* ==================================================================== */ + +/** Long name of the driver */ +#define GDAL_DMD_LONGNAME "DMD_LONGNAME" + +/** URL (relative to http://gdal.org/) to the help page of the driver */ +#define GDAL_DMD_HELPTOPIC "DMD_HELPTOPIC" + +/** MIME type handled by the driver. */ +#define GDAL_DMD_MIMETYPE "DMD_MIMETYPE" + +/** Extension handled by the driver. */ +#define GDAL_DMD_EXTENSION "DMD_EXTENSION" + +/** Connection prefix to provide as the file name of the the open function. + * Typically set for non-file based drivers. Generally used with open options. + * @since GDAL 2.0 + */ +#define GDAL_DMD_CONNECTION_PREFIX "DMD_CONNECTION_PREFIX" + +/** List of (space separated) extensions handled by the driver. + * @since GDAL 2.0 + */ +#define GDAL_DMD_EXTENSIONS "DMD_EXTENSIONS" + +/** XML snippet with creation options. */ +#define GDAL_DMD_CREATIONOPTIONLIST "DMD_CREATIONOPTIONLIST" + +/** XML snippet with open options. + * @since GDAL 2.0 + */ +#define GDAL_DMD_OPENOPTIONLIST "DMD_OPENOPTIONLIST" + +/** List of (space separated) raster data types support by the Create()/CreateCopy() API. */ +#define GDAL_DMD_CREATIONDATATYPES "DMD_CREATIONDATATYPES" + +/** List of (space separated) vector field types support by the CreateField() API. + * @since GDAL 2.0 + * */ +#define GDAL_DMD_CREATIONFIELDDATATYPES "DMD_CREATIONFIELDDATATYPES" + +/** Capability set by a driver that exposes Subdatasets. */ +#define GDAL_DMD_SUBDATASETS "DMD_SUBDATASETS" + +/** Capability set by a driver that implements the Open() API. */ +#define GDAL_DCAP_OPEN "DCAP_OPEN" + +/** Capability set by a driver that implements the Create() API. */ +#define GDAL_DCAP_CREATE "DCAP_CREATE" + +/** Capability set by a driver that implements the CreateCopy() API. */ +#define GDAL_DCAP_CREATECOPY "DCAP_CREATECOPY" + +/** Capability set by a driver that can read/create datasets through the VSI*L API. */ +#define GDAL_DCAP_VIRTUALIO "DCAP_VIRTUALIO" + +/** Capability set by a driver having raster capability. + * @since GDAL 2.0 + */ +#define GDAL_DCAP_RASTER "DCAP_RASTER" + +/** Capability set by a driver having vector capability. + * @since GDAL 2.0 + */ +#define GDAL_DCAP_VECTOR "DCAP_VECTOR" + +/** Capability set by a driver that can create fields with NOT NULL constraint. + * @since GDAL 2.0 + */ +#define GDAL_DCAP_NOTNULL_FIELDS "DCAP_NOTNULL_FIELDS" + +/** Capability set by a driver that can create fields with DEFAULT values. + * @since GDAL 2.0 + */ +#define GDAL_DCAP_DEFAULT_FIELDS "DCAP_DEFAULT_FIELDS" + +/** Capability set by a driver that can create geometry fields with NOT NULL constraint. + * @since GDAL 2.0 + */ +#define GDAL_DCAP_NOTNULL_GEOMFIELDS "DCAP_NOTNULL_GEOMFIELDS" + +void CPL_DLL CPL_STDCALL GDALAllRegister( void ); + +GDALDatasetH CPL_DLL CPL_STDCALL GDALCreate( GDALDriverH hDriver, + const char *, int, int, int, GDALDataType, + char ** ) CPL_WARN_UNUSED_RESULT; +GDALDatasetH CPL_DLL CPL_STDCALL +GDALCreateCopy( GDALDriverH, const char *, GDALDatasetH, + int, char **, GDALProgressFunc, void * ) CPL_WARN_UNUSED_RESULT; + +GDALDriverH CPL_DLL CPL_STDCALL GDALIdentifyDriver( const char * pszFilename, + char ** papszFileList ); +GDALDatasetH CPL_DLL CPL_STDCALL +GDALOpen( const char *pszFilename, GDALAccess eAccess ) CPL_WARN_UNUSED_RESULT; +GDALDatasetH CPL_DLL CPL_STDCALL GDALOpenShared( const char *, GDALAccess ) CPL_WARN_UNUSED_RESULT; + + +/* Note: we define GDAL_OF_READONLY and GDAL_OF_UPDATE to be on purpose */ +/* equals to GA_ReadOnly and GA_Update */ + +/** Open in read-only mode. + * Used by GDALOpenEx(). + * @since GDAL 2.0 + */ +#define GDAL_OF_READONLY 0x00 + +/** Open in update mode. + * Used by GDALOpenEx(). + * @since GDAL 2.0 + */ +#define GDAL_OF_UPDATE 0x01 + +/** Allow raster and vector drivers to be used. + * Used by GDALOpenEx(). + * @since GDAL 2.0 + */ +#define GDAL_OF_ALL 0x00 + +/** Allow raster drivers to be used. + * Used by GDALOpenEx(). + * @since GDAL 2.0 + */ +#define GDAL_OF_RASTER 0x02 + +/** Allow vector drivers to be used. + * Used by GDALOpenEx(). + * @since GDAL 2.0 + */ +#define GDAL_OF_VECTOR 0x04 +/* Some space for GDAL 3.0 new types ;-) */ +/*#define GDAL_OF_OTHER_KIND1 0x08 */ +/*#define GDAL_OF_OTHER_KIND2 0x10 */ +#ifndef DOXYGEN_SKIP +#define GDAL_OF_KIND_MASK 0x1E +#endif + +/** Open in shared mode. + * Used by GDALOpenEx(). + * @since GDAL 2.0 + */ +#define GDAL_OF_SHARED 0x20 + +/** Emit error message in case of failed open. + * Used by GDALOpenEx(). + * @since GDAL 2.0 + */ +#define GDAL_OF_VERBOSE_ERROR 0x40 + +/** Open as internal dataset. Such dataset isn't registered in the global list + * of opened dataset. Cannot be used with GDAL_OF_SHARED. + * + * Used by GDALOpenEx(). + * @since GDAL 2.0 + */ +#define GDAL_OF_INTERNAL 0x80 + +GDALDatasetH CPL_DLL CPL_STDCALL GDALOpenEx( const char* pszFilename, + unsigned int nOpenFlags, + const char* const* papszAllowedDrivers, + const char* const* papszOpenOptions, + const char* const* papszSiblingFiles ) CPL_WARN_UNUSED_RESULT; + +int CPL_DLL CPL_STDCALL GDALDumpOpenDatasets( FILE * ); + +GDALDriverH CPL_DLL CPL_STDCALL GDALGetDriverByName( const char * ); +int CPL_DLL CPL_STDCALL GDALGetDriverCount( void ); +GDALDriverH CPL_DLL CPL_STDCALL GDALGetDriver( int ); +void CPL_DLL CPL_STDCALL GDALDestroyDriver( GDALDriverH ); +int CPL_DLL CPL_STDCALL GDALRegisterDriver( GDALDriverH ); +void CPL_DLL CPL_STDCALL GDALDeregisterDriver( GDALDriverH ); +void CPL_DLL CPL_STDCALL GDALDestroyDriverManager( void ); +void CPL_DLL GDALDestroy( void ); +CPLErr CPL_DLL CPL_STDCALL GDALDeleteDataset( GDALDriverH, const char * ); +CPLErr CPL_DLL CPL_STDCALL GDALRenameDataset( GDALDriverH, + const char * pszNewName, + const char * pszOldName ); +CPLErr CPL_DLL CPL_STDCALL GDALCopyDatasetFiles( GDALDriverH, + const char * pszNewName, + const char * pszOldName); +int CPL_DLL CPL_STDCALL GDALValidateCreationOptions( GDALDriverH, + char** papszCreationOptions); + +/* The following are deprecated */ +const char CPL_DLL * CPL_STDCALL GDALGetDriverShortName( GDALDriverH ); +const char CPL_DLL * CPL_STDCALL GDALGetDriverLongName( GDALDriverH ); +const char CPL_DLL * CPL_STDCALL GDALGetDriverHelpTopic( GDALDriverH ); +const char CPL_DLL * CPL_STDCALL GDALGetDriverCreationOptionList( GDALDriverH ); + +/* ==================================================================== */ +/* GDAL_GCP */ +/* ==================================================================== */ + +/** Ground Control Point */ +typedef struct +{ + /** Unique identifier, often numeric */ + char *pszId; + + /** Informational message or "" */ + char *pszInfo; + + /** Pixel (x) location of GCP on raster */ + double dfGCPPixel; + /** Line (y) location of GCP on raster */ + double dfGCPLine; + + /** X position of GCP in georeferenced space */ + double dfGCPX; + + /** Y position of GCP in georeferenced space */ + double dfGCPY; + + /** Elevation of GCP, or zero if not known */ + double dfGCPZ; +} GDAL_GCP; + +void CPL_DLL CPL_STDCALL GDALInitGCPs( int, GDAL_GCP * ); +void CPL_DLL CPL_STDCALL GDALDeinitGCPs( int, GDAL_GCP * ); +GDAL_GCP CPL_DLL * CPL_STDCALL GDALDuplicateGCPs( int, const GDAL_GCP * ); + +int CPL_DLL CPL_STDCALL +GDALGCPsToGeoTransform( int nGCPCount, const GDAL_GCP *pasGCPs, + double *padfGeoTransform, int bApproxOK ) CPL_WARN_UNUSED_RESULT; +int CPL_DLL CPL_STDCALL +GDALInvGeoTransform( double *padfGeoTransformIn, + double *padfInvGeoTransformOut ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL CPL_STDCALL GDALApplyGeoTransform( double *, double, double, + double *, double * ); +void CPL_DLL GDALComposeGeoTransforms(const double *padfGeoTransform1, + const double *padfGeoTransform2, + double *padfGeoTransformOut); + +/* ==================================================================== */ +/* major objects (dataset, and, driver, drivermanager). */ +/* ==================================================================== */ + +char CPL_DLL ** CPL_STDCALL GDALGetMetadataDomainList( GDALMajorObjectH hObject ); +char CPL_DLL ** CPL_STDCALL GDALGetMetadata( GDALMajorObjectH, const char * ); +CPLErr CPL_DLL CPL_STDCALL GDALSetMetadata( GDALMajorObjectH, char **, + const char * ); +const char CPL_DLL * CPL_STDCALL +GDALGetMetadataItem( GDALMajorObjectH, const char *, const char * ); +CPLErr CPL_DLL CPL_STDCALL +GDALSetMetadataItem( GDALMajorObjectH, const char *, const char *, + const char * ); +const char CPL_DLL * CPL_STDCALL GDALGetDescription( GDALMajorObjectH ); +void CPL_DLL CPL_STDCALL GDALSetDescription( GDALMajorObjectH, const char * ); + +/* ==================================================================== */ +/* GDALDataset class ... normally this represents one file. */ +/* ==================================================================== */ + +#define GDAL_DS_LAYER_CREATIONOPTIONLIST "DS_LAYER_CREATIONOPTIONLIST" + +GDALDriverH CPL_DLL CPL_STDCALL GDALGetDatasetDriver( GDALDatasetH ); +char CPL_DLL ** CPL_STDCALL GDALGetFileList( GDALDatasetH ); +void CPL_DLL CPL_STDCALL GDALClose( GDALDatasetH ); +int CPL_DLL CPL_STDCALL GDALGetRasterXSize( GDALDatasetH ); +int CPL_DLL CPL_STDCALL GDALGetRasterYSize( GDALDatasetH ); +int CPL_DLL CPL_STDCALL GDALGetRasterCount( GDALDatasetH ); +GDALRasterBandH CPL_DLL CPL_STDCALL GDALGetRasterBand( GDALDatasetH, int ); + +CPLErr CPL_DLL CPL_STDCALL GDALAddBand( GDALDatasetH hDS, GDALDataType eType, + char **papszOptions ); + +GDALAsyncReaderH CPL_DLL CPL_STDCALL +GDALBeginAsyncReader(GDALDatasetH hDS, int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, int nBufXSize, int nBufYSize, + GDALDataType eBufType, int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, int nBandSpace, + char **papszOptions); + +void CPL_DLL CPL_STDCALL +GDALEndAsyncReader(GDALDatasetH hDS, GDALAsyncReaderH hAsynchReaderH); + +CPLErr CPL_DLL CPL_STDCALL GDALDatasetRasterIO( + GDALDatasetH hDS, GDALRWFlag eRWFlag, + int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, + void * pBuffer, int nBXSize, int nBYSize, GDALDataType eBDataType, + int nBandCount, int *panBandCount, + int nPixelSpace, int nLineSpace, int nBandSpace); + +CPLErr CPL_DLL CPL_STDCALL GDALDatasetRasterIOEx( + GDALDatasetH hDS, GDALRWFlag eRWFlag, + int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, + void * pBuffer, int nBXSize, int nBYSize, GDALDataType eBDataType, + int nBandCount, int *panBandCount, + GSpacing nPixelSpace, GSpacing nLineSpace, GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + +CPLErr CPL_DLL CPL_STDCALL GDALDatasetAdviseRead( GDALDatasetH hDS, + int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, + int nBXSize, int nBYSize, GDALDataType eBDataType, + int nBandCount, int *panBandCount, char **papszOptions ); + +const char CPL_DLL * CPL_STDCALL GDALGetProjectionRef( GDALDatasetH ); +CPLErr CPL_DLL CPL_STDCALL GDALSetProjection( GDALDatasetH, const char * ); +CPLErr CPL_DLL CPL_STDCALL GDALGetGeoTransform( GDALDatasetH, double * ); +CPLErr CPL_DLL CPL_STDCALL GDALSetGeoTransform( GDALDatasetH, double * ); + +int CPL_DLL CPL_STDCALL GDALGetGCPCount( GDALDatasetH ); +const char CPL_DLL * CPL_STDCALL GDALGetGCPProjection( GDALDatasetH ); +const GDAL_GCP CPL_DLL * CPL_STDCALL GDALGetGCPs( GDALDatasetH ); +CPLErr CPL_DLL CPL_STDCALL GDALSetGCPs( GDALDatasetH, int, const GDAL_GCP *, + const char * ); + +void CPL_DLL * CPL_STDCALL GDALGetInternalHandle( GDALDatasetH, const char * ); +int CPL_DLL CPL_STDCALL GDALReferenceDataset( GDALDatasetH ); +int CPL_DLL CPL_STDCALL GDALDereferenceDataset( GDALDatasetH ); + +CPLErr CPL_DLL CPL_STDCALL +GDALBuildOverviews( GDALDatasetH, const char *, int, int *, + int, int *, GDALProgressFunc, void * ); +void CPL_DLL CPL_STDCALL GDALGetOpenDatasets( GDALDatasetH **hDS, int *pnCount ); +int CPL_DLL CPL_STDCALL GDALGetAccess( GDALDatasetH hDS ); +void CPL_DLL CPL_STDCALL GDALFlushCache( GDALDatasetH hDS ); + +CPLErr CPL_DLL CPL_STDCALL + GDALCreateDatasetMaskBand( GDALDatasetH hDS, int nFlags ); + +CPLErr CPL_DLL CPL_STDCALL GDALDatasetCopyWholeRaster( + GDALDatasetH hSrcDS, GDALDatasetH hDstDS, char **papszOptions, + GDALProgressFunc pfnProgress, void *pProgressData ); + +CPLErr CPL_DLL CPL_STDCALL GDALRasterBandCopyWholeRaster( + GDALRasterBandH hSrcBand, GDALRasterBandH hDstBand, char **papszOptions, + GDALProgressFunc pfnProgress, void *pProgressData ); + +CPLErr CPL_DLL +GDALRegenerateOverviews( GDALRasterBandH hSrcBand, + int nOverviewCount, GDALRasterBandH *pahOverviewBands, + const char *pszResampling, + GDALProgressFunc pfnProgress, void *pProgressData ); + +int CPL_DLL GDALDatasetGetLayerCount( GDALDatasetH ); +OGRLayerH CPL_DLL GDALDatasetGetLayer( GDALDatasetH, int ); +OGRLayerH CPL_DLL GDALDatasetGetLayerByName( GDALDatasetH, const char * ); +OGRErr CPL_DLL GDALDatasetDeleteLayer( GDALDatasetH, int ); +OGRLayerH CPL_DLL GDALDatasetCreateLayer( GDALDatasetH, const char *, + OGRSpatialReferenceH, OGRwkbGeometryType, + char ** ); +OGRLayerH CPL_DLL GDALDatasetCopyLayer( GDALDatasetH, OGRLayerH, const char *, + char ** ); +int CPL_DLL GDALDatasetTestCapability( GDALDatasetH, const char * ); +OGRLayerH CPL_DLL GDALDatasetExecuteSQL( GDALDatasetH, const char *, + OGRGeometryH, const char * ); +void CPL_DLL GDALDatasetReleaseResultSet( GDALDatasetH, OGRLayerH ); +OGRStyleTableH CPL_DLL GDALDatasetGetStyleTable( GDALDatasetH ); +void CPL_DLL GDALDatasetSetStyleTableDirectly( GDALDatasetH, OGRStyleTableH ); +void CPL_DLL GDALDatasetSetStyleTable( GDALDatasetH, OGRStyleTableH ); +OGRErr CPL_DLL GDALDatasetStartTransaction(GDALDatasetH hDS, int bForce); +OGRErr CPL_DLL GDALDatasetCommitTransaction(GDALDatasetH hDS); +OGRErr CPL_DLL GDALDatasetRollbackTransaction(GDALDatasetH hDS); + + +/* ==================================================================== */ +/* GDALRasterBand ... one band/channel in a dataset. */ +/* ==================================================================== */ + +/** + * SRCVAL - Macro which may be used by pixel functions to obtain + * a pixel from a source buffer. + */ +#define SRCVAL(papoSource, eSrcType, ii) \ + (eSrcType == GDT_Byte ? \ + ((GByte *)papoSource)[ii] : \ + (eSrcType == GDT_Float32 ? \ + ((float *)papoSource)[ii] : \ + (eSrcType == GDT_Float64 ? \ + ((double *)papoSource)[ii] : \ + (eSrcType == GDT_Int32 ? \ + ((GInt32 *)papoSource)[ii] : \ + (eSrcType == GDT_UInt16 ? \ + ((GUInt16 *)papoSource)[ii] : \ + (eSrcType == GDT_Int16 ? \ + ((GInt16 *)papoSource)[ii] : \ + (eSrcType == GDT_UInt32 ? \ + ((GUInt32 *)papoSource)[ii] : \ + (eSrcType == GDT_CInt16 ? \ + ((GInt16 *)papoSource)[ii * 2] : \ + (eSrcType == GDT_CInt32 ? \ + ((GInt32 *)papoSource)[ii * 2] : \ + (eSrcType == GDT_CFloat32 ? \ + ((float *)papoSource)[ii * 2] : \ + (eSrcType == GDT_CFloat64 ? \ + ((double *)papoSource)[ii * 2] : 0))))))))))) + +typedef CPLErr +(*GDALDerivedPixelFunc)(void **papoSources, int nSources, void *pData, + int nBufXSize, int nBufYSize, + GDALDataType eSrcType, GDALDataType eBufType, + int nPixelSpace, int nLineSpace); + +GDALDataType CPL_DLL CPL_STDCALL GDALGetRasterDataType( GDALRasterBandH ); +void CPL_DLL CPL_STDCALL +GDALGetBlockSize( GDALRasterBandH, int * pnXSize, int * pnYSize ); + +CPLErr CPL_DLL CPL_STDCALL GDALRasterAdviseRead( GDALRasterBandH hRB, + int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, + int nBXSize, int nBYSize, GDALDataType eBDataType, char **papszOptions ); + +CPLErr CPL_DLL CPL_STDCALL +GDALRasterIO( GDALRasterBandH hRBand, GDALRWFlag eRWFlag, + int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, + void * pBuffer, int nBXSize, int nBYSize,GDALDataType eBDataType, + int nPixelSpace, int nLineSpace ); +CPLErr CPL_DLL CPL_STDCALL +GDALRasterIOEx( GDALRasterBandH hRBand, GDALRWFlag eRWFlag, + int nDSXOff, int nDSYOff, int nDSXSize, int nDSYSize, + void * pBuffer, int nBXSize, int nBYSize,GDALDataType eBDataType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ); +CPLErr CPL_DLL CPL_STDCALL GDALReadBlock( GDALRasterBandH, int, int, void * ); +CPLErr CPL_DLL CPL_STDCALL GDALWriteBlock( GDALRasterBandH, int, int, void * ); +int CPL_DLL CPL_STDCALL GDALGetRasterBandXSize( GDALRasterBandH ); +int CPL_DLL CPL_STDCALL GDALGetRasterBandYSize( GDALRasterBandH ); +GDALAccess CPL_DLL CPL_STDCALL GDALGetRasterAccess( GDALRasterBandH ); +int CPL_DLL CPL_STDCALL GDALGetBandNumber( GDALRasterBandH ); +GDALDatasetH CPL_DLL CPL_STDCALL GDALGetBandDataset( GDALRasterBandH ); + +GDALColorInterp CPL_DLL CPL_STDCALL +GDALGetRasterColorInterpretation( GDALRasterBandH ); +CPLErr CPL_DLL CPL_STDCALL +GDALSetRasterColorInterpretation( GDALRasterBandH, GDALColorInterp ); +GDALColorTableH CPL_DLL CPL_STDCALL GDALGetRasterColorTable( GDALRasterBandH ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterColorTable( GDALRasterBandH, GDALColorTableH ); +int CPL_DLL CPL_STDCALL GDALHasArbitraryOverviews( GDALRasterBandH ); +int CPL_DLL CPL_STDCALL GDALGetOverviewCount( GDALRasterBandH ); +GDALRasterBandH CPL_DLL CPL_STDCALL GDALGetOverview( GDALRasterBandH, int ); +double CPL_DLL CPL_STDCALL GDALGetRasterNoDataValue( GDALRasterBandH, int * ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterNoDataValue( GDALRasterBandH, double ); +char CPL_DLL ** CPL_STDCALL GDALGetRasterCategoryNames( GDALRasterBandH ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterCategoryNames( GDALRasterBandH, char ** ); +double CPL_DLL CPL_STDCALL GDALGetRasterMinimum( GDALRasterBandH, int *pbSuccess ); +double CPL_DLL CPL_STDCALL GDALGetRasterMaximum( GDALRasterBandH, int *pbSuccess ); +CPLErr CPL_DLL CPL_STDCALL GDALGetRasterStatistics( + GDALRasterBandH, int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, double *pdfMean, double *pdfStdDev ); +CPLErr CPL_DLL CPL_STDCALL GDALComputeRasterStatistics( + GDALRasterBandH, int bApproxOK, + double *pdfMin, double *pdfMax, double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, void *pProgressData ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterStatistics( + GDALRasterBandH hBand, + double dfMin, double dfMax, double dfMean, double dfStdDev ); + +const char CPL_DLL * CPL_STDCALL GDALGetRasterUnitType( GDALRasterBandH ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterUnitType( GDALRasterBandH hBand, const char *pszNewValue ); +double CPL_DLL CPL_STDCALL GDALGetRasterOffset( GDALRasterBandH, int *pbSuccess ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterOffset( GDALRasterBandH hBand, double dfNewOffset); +double CPL_DLL CPL_STDCALL GDALGetRasterScale( GDALRasterBandH, int *pbSuccess ); +CPLErr CPL_DLL CPL_STDCALL GDALSetRasterScale( GDALRasterBandH hBand, double dfNewOffset ); +void CPL_DLL CPL_STDCALL +GDALComputeRasterMinMax( GDALRasterBandH hBand, int bApproxOK, + double adfMinMax[2] ); +CPLErr CPL_DLL CPL_STDCALL GDALFlushRasterCache( GDALRasterBandH hBand ); +CPLErr CPL_DLL CPL_STDCALL GDALGetRasterHistogram( GDALRasterBandH hBand, + double dfMin, double dfMax, + int nBuckets, int *panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void * pProgressData ) CPL_WARN_DEPRECATED("Use GDALGetRasterHistogramEx() instead"); +CPLErr CPL_DLL CPL_STDCALL GDALGetRasterHistogramEx( GDALRasterBandH hBand, + double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void * pProgressData ); +CPLErr CPL_DLL CPL_STDCALL GDALGetDefaultHistogram( GDALRasterBandH hBand, + double *pdfMin, double *pdfMax, + int *pnBuckets, int **ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void * pProgressData ) CPL_WARN_DEPRECATED("Use GDALGetDefaultHistogramEx() instead"); +CPLErr CPL_DLL CPL_STDCALL GDALGetDefaultHistogramEx( GDALRasterBandH hBand, + double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig **ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void * pProgressData ); +CPLErr CPL_DLL CPL_STDCALL GDALSetDefaultHistogram( GDALRasterBandH hBand, + double dfMin, double dfMax, + int nBuckets, int *panHistogram ) CPL_WARN_DEPRECATED("Use GDALSetDefaultHistogramEx() instead"); +CPLErr CPL_DLL CPL_STDCALL GDALSetDefaultHistogramEx( GDALRasterBandH hBand, + double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram ); +int CPL_DLL CPL_STDCALL +GDALGetRandomRasterSample( GDALRasterBandH, int, float * ); +GDALRasterBandH CPL_DLL CPL_STDCALL +GDALGetRasterSampleOverview( GDALRasterBandH, int ); +GDALRasterBandH CPL_DLL CPL_STDCALL +GDALGetRasterSampleOverviewEx( GDALRasterBandH, GUIntBig ); +CPLErr CPL_DLL CPL_STDCALL GDALFillRaster( GDALRasterBandH hBand, + double dfRealValue, double dfImaginaryValue ); +CPLErr CPL_DLL CPL_STDCALL +GDALComputeBandStats( GDALRasterBandH hBand, int nSampleStep, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, + void *pProgressData ); +CPLErr CPL_DLL GDALOverviewMagnitudeCorrection( GDALRasterBandH hBaseBand, + int nOverviewCount, + GDALRasterBandH *pahOverviews, + GDALProgressFunc pfnProgress, + void *pProgressData ); + +GDALRasterAttributeTableH CPL_DLL CPL_STDCALL GDALGetDefaultRAT( + GDALRasterBandH hBand ); +CPLErr CPL_DLL CPL_STDCALL GDALSetDefaultRAT( GDALRasterBandH, + GDALRasterAttributeTableH ); +CPLErr CPL_DLL CPL_STDCALL GDALAddDerivedBandPixelFunc( const char *pszName, + GDALDerivedPixelFunc pfnPixelFunc ); + +GDALRasterBandH CPL_DLL CPL_STDCALL GDALGetMaskBand( GDALRasterBandH hBand ); +int CPL_DLL CPL_STDCALL GDALGetMaskFlags( GDALRasterBandH hBand ); +CPLErr CPL_DLL CPL_STDCALL + GDALCreateMaskBand( GDALRasterBandH hBand, int nFlags ); + +#define GMF_ALL_VALID 0x01 +#define GMF_PER_DATASET 0x02 +#define GMF_ALPHA 0x04 +#define GMF_NODATA 0x08 + +/* ==================================================================== */ +/* GDALAsyncReader */ +/* ==================================================================== */ + +GDALAsyncStatusType CPL_DLL CPL_STDCALL +GDALARGetNextUpdatedRegion(GDALAsyncReaderH hARIO, double dfTimeout, + int* pnXBufOff, int* pnYBufOff, + int* pnXBufSize, int* pnYBufSize ); +int CPL_DLL CPL_STDCALL GDALARLockBuffer(GDALAsyncReaderH hARIO, + double dfTimeout); +void CPL_DLL CPL_STDCALL GDALARUnlockBuffer(GDALAsyncReaderH hARIO); + +/* -------------------------------------------------------------------- */ +/* Helper functions. */ +/* -------------------------------------------------------------------- */ +int CPL_DLL CPL_STDCALL GDALGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv, + int nOptions ); +void CPL_DLL CPL_STDCALL GDALSwapWords( void *pData, int nWordSize, int nWordCount, + int nWordSkip ); +void CPL_DLL CPL_STDCALL + GDALCopyWords( void * pSrcData, GDALDataType eSrcType, int nSrcPixelOffset, + void * pDstData, GDALDataType eDstType, int nDstPixelOffset, + int nWordCount ); + +void CPL_DLL +GDALCopyBits( const GByte *pabySrcData, int nSrcOffset, int nSrcStep, + GByte *pabyDstData, int nDstOffset, int nDstStep, + int nBitCount, int nStepCount ); + +int CPL_DLL CPL_STDCALL GDALLoadWorldFile( const char *, double * ); +int CPL_DLL CPL_STDCALL GDALReadWorldFile( const char *, const char *, + double * ); +int CPL_DLL CPL_STDCALL GDALWriteWorldFile( const char *, const char *, + double * ); +int CPL_DLL CPL_STDCALL GDALLoadTabFile( const char *, double *, char **, + int *, GDAL_GCP ** ); +int CPL_DLL CPL_STDCALL GDALReadTabFile( const char *, double *, char **, + int *, GDAL_GCP ** ); +int CPL_DLL CPL_STDCALL GDALLoadOziMapFile( const char *, double *, char **, + int *, GDAL_GCP ** ); +int CPL_DLL CPL_STDCALL GDALReadOziMapFile( const char * , double *, + char **, int *, GDAL_GCP ** ); + +const char CPL_DLL * CPL_STDCALL GDALDecToDMS( double, const char *, int ); +double CPL_DLL CPL_STDCALL GDALPackedDMSToDec( double ); +double CPL_DLL CPL_STDCALL GDALDecToPackedDMS( double ); + +/* Note to developers : please keep this section in sync with ogr_core.h */ + +#ifndef GDAL_VERSION_INFO_DEFINED +#define GDAL_VERSION_INFO_DEFINED +const char CPL_DLL * CPL_STDCALL GDALVersionInfo( const char * ); +#endif + +#ifndef GDAL_CHECK_VERSION + +int CPL_DLL CPL_STDCALL GDALCheckVersion( int nVersionMajor, int nVersionMinor, + const char* pszCallingComponentName); + +/** Helper macro for GDALCheckVersion() + @see GDALCheckVersion() + */ +#define GDAL_CHECK_VERSION(pszCallingComponentName) \ + GDALCheckVersion(GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR, pszCallingComponentName) + +#endif + +typedef struct { + double dfLINE_OFF; + double dfSAMP_OFF; + double dfLAT_OFF; + double dfLONG_OFF; + double dfHEIGHT_OFF; + + double dfLINE_SCALE; + double dfSAMP_SCALE; + double dfLAT_SCALE; + double dfLONG_SCALE; + double dfHEIGHT_SCALE; + + double adfLINE_NUM_COEFF[20]; + double adfLINE_DEN_COEFF[20]; + double adfSAMP_NUM_COEFF[20]; + double adfSAMP_DEN_COEFF[20]; + + double dfMIN_LONG; + double dfMIN_LAT; + double dfMAX_LONG; + double dfMAX_LAT; + +} GDALRPCInfo; + +int CPL_DLL CPL_STDCALL GDALExtractRPCInfo( char **, GDALRPCInfo * ); + +/* ==================================================================== */ +/* Color tables. */ +/* ==================================================================== */ + +/** Color tuple */ +typedef struct +{ + /*! gray, red, cyan or hue */ + short c1; + + /*! green, magenta, or lightness */ + short c2; + + /*! blue, yellow, or saturation */ + short c3; + + /*! alpha or blackband */ + short c4; +} GDALColorEntry; + +GDALColorTableH CPL_DLL CPL_STDCALL GDALCreateColorTable( GDALPaletteInterp ); +void CPL_DLL CPL_STDCALL GDALDestroyColorTable( GDALColorTableH ); +GDALColorTableH CPL_DLL CPL_STDCALL GDALCloneColorTable( GDALColorTableH ); +GDALPaletteInterp CPL_DLL CPL_STDCALL GDALGetPaletteInterpretation( GDALColorTableH ); +int CPL_DLL CPL_STDCALL GDALGetColorEntryCount( GDALColorTableH ); +const GDALColorEntry CPL_DLL * CPL_STDCALL GDALGetColorEntry( GDALColorTableH, int ); +int CPL_DLL CPL_STDCALL GDALGetColorEntryAsRGB( GDALColorTableH, int, GDALColorEntry *); +void CPL_DLL CPL_STDCALL GDALSetColorEntry( GDALColorTableH, int, const GDALColorEntry * ); +void CPL_DLL CPL_STDCALL GDALCreateColorRamp( GDALColorTableH hTable, + int nStartIndex, const GDALColorEntry *psStartColor, + int nEndIndex, const GDALColorEntry *psEndColor ); + +/* ==================================================================== */ +/* Raster Attribute Table */ +/* ==================================================================== */ + +/** Field type of raster attribute table */ +typedef enum { + /*! Integer field */ GFT_Integer , + /*! Floating point (double) field */ GFT_Real, + /*! String field */ GFT_String +} GDALRATFieldType; + +/** Field usage of raster attribute table */ +typedef enum { + /*! General purpose field. */ GFU_Generic = 0, + /*! Histogram pixel count */ GFU_PixelCount = 1, + /*! Class name */ GFU_Name = 2, + /*! Class range minimum */ GFU_Min = 3, + /*! Class range maximum */ GFU_Max = 4, + /*! Class value (min=max) */ GFU_MinMax = 5, + /*! Red class color (0-255) */ GFU_Red = 6, + /*! Green class color (0-255) */ GFU_Green = 7, + /*! Blue class color (0-255) */ GFU_Blue = 8, + /*! Alpha (0=transparent,255=opaque)*/ GFU_Alpha = 9, + /*! Color Range Red Minimum */ GFU_RedMin = 10, + /*! Color Range Green Minimum */ GFU_GreenMin = 11, + /*! Color Range Blue Minimum */ GFU_BlueMin = 12, + /*! Color Range Alpha Minimum */ GFU_AlphaMin = 13, + /*! Color Range Red Maximum */ GFU_RedMax = 14, + /*! Color Range Green Maximum */ GFU_GreenMax = 15, + /*! Color Range Blue Maximum */ GFU_BlueMax = 16, + /*! Color Range Alpha Maximum */ GFU_AlphaMax = 17, + /*! Maximum GFU value */ GFU_MaxCount +} GDALRATFieldUsage; + +GDALRasterAttributeTableH CPL_DLL CPL_STDCALL + GDALCreateRasterAttributeTable(void); +void CPL_DLL CPL_STDCALL GDALDestroyRasterAttributeTable( + GDALRasterAttributeTableH ); + +int CPL_DLL CPL_STDCALL GDALRATGetColumnCount( GDALRasterAttributeTableH ); + +const char CPL_DLL * CPL_STDCALL GDALRATGetNameOfCol( + GDALRasterAttributeTableH, int ); +GDALRATFieldUsage CPL_DLL CPL_STDCALL GDALRATGetUsageOfCol( + GDALRasterAttributeTableH, int ); +GDALRATFieldType CPL_DLL CPL_STDCALL GDALRATGetTypeOfCol( + GDALRasterAttributeTableH, int ); + +int CPL_DLL CPL_STDCALL GDALRATGetColOfUsage( GDALRasterAttributeTableH, + GDALRATFieldUsage ); +int CPL_DLL CPL_STDCALL GDALRATGetRowCount( GDALRasterAttributeTableH ); + +const char CPL_DLL * CPL_STDCALL GDALRATGetValueAsString( + GDALRasterAttributeTableH, int ,int); +int CPL_DLL CPL_STDCALL GDALRATGetValueAsInt( + GDALRasterAttributeTableH, int ,int); +double CPL_DLL CPL_STDCALL GDALRATGetValueAsDouble( + GDALRasterAttributeTableH, int ,int); + +void CPL_DLL CPL_STDCALL GDALRATSetValueAsString( GDALRasterAttributeTableH, int, int, + const char * ); +void CPL_DLL CPL_STDCALL GDALRATSetValueAsInt( GDALRasterAttributeTableH, int, int, + int ); +void CPL_DLL CPL_STDCALL GDALRATSetValueAsDouble( GDALRasterAttributeTableH, int, int, + double ); + +int CPL_DLL CPL_STDCALL GDALRATChangesAreWrittenToFile( GDALRasterAttributeTableH hRAT ); + +CPLErr CPL_DLL CPL_STDCALL GDALRATValuesIOAsDouble( GDALRasterAttributeTableH hRAT, GDALRWFlag eRWFlag, + int iField, int iStartRow, int iLength, double *pdfData ); +CPLErr CPL_DLL CPL_STDCALL GDALRATValuesIOAsInteger( GDALRasterAttributeTableH hRAT, GDALRWFlag eRWFlag, + int iField, int iStartRow, int iLength, int *pnData); +CPLErr CPL_DLL CPL_STDCALL GDALRATValuesIOAsString( GDALRasterAttributeTableH hRAT, GDALRWFlag eRWFlag, + int iField, int iStartRow, int iLength, char **papszStrList); + +void CPL_DLL CPL_STDCALL GDALRATSetRowCount( GDALRasterAttributeTableH, + int ); +CPLErr CPL_DLL CPL_STDCALL GDALRATCreateColumn( GDALRasterAttributeTableH, + const char *, + GDALRATFieldType, + GDALRATFieldUsage ); +CPLErr CPL_DLL CPL_STDCALL GDALRATSetLinearBinning( GDALRasterAttributeTableH, + double, double ); +int CPL_DLL CPL_STDCALL GDALRATGetLinearBinning( GDALRasterAttributeTableH, + double *, double * ); +CPLErr CPL_DLL CPL_STDCALL GDALRATInitializeFromColorTable( + GDALRasterAttributeTableH, GDALColorTableH ); +GDALColorTableH CPL_DLL CPL_STDCALL GDALRATTranslateToColorTable( + GDALRasterAttributeTableH, int nEntryCount ); +void CPL_DLL CPL_STDCALL GDALRATDumpReadable( GDALRasterAttributeTableH, + FILE * ); +GDALRasterAttributeTableH CPL_DLL CPL_STDCALL + GDALRATClone( GDALRasterAttributeTableH ); + +void CPL_DLL* CPL_STDCALL + GDALRATSerializeJSON( GDALRasterAttributeTableH ); + +int CPL_DLL CPL_STDCALL GDALRATGetRowOfValue( GDALRasterAttributeTableH , double ); + + +/* ==================================================================== */ +/* GDAL Cache Management */ +/* ==================================================================== */ + +void CPL_DLL CPL_STDCALL GDALSetCacheMax( int nBytes ); +int CPL_DLL CPL_STDCALL GDALGetCacheMax(void); +int CPL_DLL CPL_STDCALL GDALGetCacheUsed(void); +void CPL_DLL CPL_STDCALL GDALSetCacheMax64( GIntBig nBytes ); +GIntBig CPL_DLL CPL_STDCALL GDALGetCacheMax64(void); +GIntBig CPL_DLL CPL_STDCALL GDALGetCacheUsed64(void); + +int CPL_DLL CPL_STDCALL GDALFlushCacheBlock(void); + +/* ==================================================================== */ +/* GDAL virtual memory */ +/* ==================================================================== */ + +CPLVirtualMem CPL_DLL* GDALDatasetGetVirtualMem( GDALDatasetH hDS, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, + GIntBig nLineSpace, + GIntBig nBandSpace, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + char **papszOptions ); + +CPLVirtualMem CPL_DLL* GDALRasterBandGetVirtualMem( GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, + GIntBig nLineSpace, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + char **papszOptions ); + +CPLVirtualMem CPL_DLL* GDALGetVirtualMemAuto( GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ); + +typedef enum +{ + /*! Tile Interleaved by Pixel: tile (0,0) with internal band interleaved by pixel organization, tile (1, 0), ... */ + GTO_TIP, + /*! Band Interleaved by Tile : tile (0,0) of first band, tile (0,0) of second band, ... tile (1,0) of fisrt band, tile (1,0) of second band, ... */ + GTO_BIT, + /*! Band SeQuential : all the tiles of first band, all the tiles of following band... */ + GTO_BSQ +} GDALTileOrganization; + +CPLVirtualMem CPL_DLL* GDALDatasetGetTiledVirtualMem( GDALDatasetH hDS, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + GDALTileOrganization eTileOrganization, + size_t nCacheSize, + int bSingleThreadUsage, + char **papszOptions ); + +CPLVirtualMem CPL_DLL* GDALRasterBandGetTiledVirtualMem( GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + size_t nCacheSize, + int bSingleThreadUsage, + char **papszOptions ); + +/* =================================================================== */ +/* Misc API */ +/* ==================================================================== */ + +CPLXMLNode CPL_DLL* GDALGetJPEG2000Structure(const char* pszFilename, + char** papszOptions); + +CPL_C_END + +#endif /* ndef GDAL_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/gcore/gdal_frmts.h b/bazaar/plugin/gdal/gcore/gdal_frmts.h new file mode 100644 index 000000000..261f241da --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdal_frmts.h @@ -0,0 +1,187 @@ +/****************************************************************************** + * $Id: gdal_frmts.h 28859 2015-04-07 08:40:10Z rouault $ + * + * Project: GDAL + * Purpose: Prototypes for all format specific driver initializations. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_FRMTS_H_INCLUDED +#define GDAL_FRMTS_H_INCLUDED + +#include "cpl_port.h" + +CPL_C_START +void CPL_DLL GDALRegister_GDB(void); +void CPL_DLL GDALRegister_GTiff(void); +void CPL_DLL GDALRegister_GXF(void); +void CPL_DLL GDALRegister_OGDI(void); +void CPL_DLL GDALRegister_HFA(void); +void CPL_DLL GDALRegister_AAIGrid(void); +void CPL_DLL GDALRegister_GRASSASCIIGrid(void); +void CPL_DLL GDALRegister_AIGrid(void); +void CPL_DLL GDALRegister_AIGrid2(void); +void CPL_DLL GDALRegister_CEOS(void); +void CPL_DLL GDALRegister_SAR_CEOS(void); +void CPL_DLL GDALRegister_SDTS(void); +void CPL_DLL GDALRegister_ELAS(void); +void CPL_DLL GDALRegister_EHdr(void); +void CPL_DLL GDALRegister_GenBin(void); +void CPL_DLL GDALRegister_PAux(void); +void CPL_DLL GDALRegister_ENVI(void); +void CPL_DLL GDALRegister_DOQ1(void); +void CPL_DLL GDALRegister_DOQ2(void); +void CPL_DLL GDALRegister_DTED(void); +void CPL_DLL GDALRegister_MFF(void); +void CPL_DLL GDALRegister_HKV(void); +void CPL_DLL GDALRegister_PNG(void); +void CPL_DLL GDALRegister_DDS(void); +void CPL_DLL GDALRegister_GTA(void); +void CPL_DLL GDALRegister_JPEG(void); +void CPL_DLL GDALRegister_JPEG2000(void); +void CPL_DLL GDALRegister_JP2KAK(void); +void CPL_DLL GDALRegister_JPIPKAK(void); +void CPL_DLL GDALRegister_MEM(void); +void CPL_DLL GDALRegister_JDEM(void); +void CPL_DLL GDALRegister_RASDAMAN(void); +void CPL_DLL GDALRegister_GRASS(void); +void CPL_DLL GDALRegister_PNM(void); +void CPL_DLL GDALRegister_GIF(void); +void CPL_DLL GDALRegister_BIGGIF(void); +void CPL_DLL GDALRegister_Envisat(void); +void CPL_DLL GDALRegister_FITS(void); +void CPL_DLL GDALRegister_ECW(void); +void CPL_DLL GDALRegister_JP2ECW(void); +void CPL_DLL GDALRegister_ECW_JP2ECW(); +void CPL_DLL GDALRegister_FujiBAS(void); +void CPL_DLL GDALRegister_FIT(void); +void CPL_DLL GDALRegister_VRT(void); +void CPL_DLL GDALRegister_USGSDEM(void); +void CPL_DLL GDALRegister_FAST(void); +void CPL_DLL GDALRegister_HDF4(void); +void CPL_DLL GDALRegister_HDF4Image(void); +void CPL_DLL GDALRegister_L1B(void); +void CPL_DLL GDALRegister_LDF(void); +void CPL_DLL GDALRegister_BSB(void); +void CPL_DLL GDALRegister_XPM(void); +void CPL_DLL GDALRegister_BMP(void); +void CPL_DLL GDALRegister_GSC(void); +void CPL_DLL GDALRegister_NITF(void); +void CPL_DLL GDALRegister_RPFTOC(void); +void CPL_DLL GDALRegister_ECRGTOC(void); +void CPL_DLL GDALRegister_MrSID(void); +void CPL_DLL GDALRegister_MG4Lidar(void); +void CPL_DLL GDALRegister_PCIDSK(void); +void CPL_DLL GDALRegister_BT(void); +void CPL_DLL GDALRegister_DODS(void); +void CPL_DLL GDALRegister_GMT(void); +void CPL_DLL GDALRegister_netCDF(void); +void CPL_DLL GDALRegister_LAN(void); +void CPL_DLL GDALRegister_CPG(void); +void CPL_DLL GDALRegister_AirSAR(void); +void CPL_DLL GDALRegister_RS2(void); +void CPL_DLL GDALRegister_ILWIS(void); +void CPL_DLL GDALRegister_PCRaster(void); +void CPL_DLL GDALRegister_IDA(void); +void CPL_DLL GDALRegister_NDF(void); +void CPL_DLL GDALRegister_RMF(void); +void CPL_DLL GDALRegister_BAG(void); +void CPL_DLL GDALRegister_HDF5(void); +void CPL_DLL GDALRegister_HDF5Image(void); +void CPL_DLL GDALRegister_MSGN(void); +void CPL_DLL GDALRegister_MSG(void); +void CPL_DLL GDALRegister_RIK(void); +void CPL_DLL GDALRegister_Leveller(void); +void CPL_DLL GDALRegister_SGI(void); +void CPL_DLL GDALRegister_SRTMHGT(void); +void CPL_DLL GDALRegister_DIPEx(void); +void CPL_DLL GDALRegister_ISIS3(void); +void CPL_DLL GDALRegister_ISIS2(void); +void CPL_DLL GDALRegister_PDS(void); +void CPL_DLL GDALRegister_VICAR(void); +void CPL_DLL GDALRegister_IDRISI(void); +void CPL_DLL GDALRegister_Terragen(void); +void CPL_DLL GDALRegister_WCS(void); +void CPL_DLL GDALRegister_WMS(void); +void CPL_DLL GDALRegister_HTTP(void); +void CPL_DLL GDALRegister_SDE(void); +void CPL_DLL GDALRegister_GSAG(void); +void CPL_DLL GDALRegister_GSBG(void); +void CPL_DLL GDALRegister_GS7BG(void); +void CPL_DLL GDALRegister_GRIB(void); +void CPL_DLL GDALRegister_INGR(void); +void CPL_DLL GDALRegister_ERS(void); +void CPL_DLL GDALRegister_PALSARJaxa(void); +void CPL_DLL GDALRegister_DIMAP(); +void CPL_DLL GDALRegister_GFF(void); +void CPL_DLL GDALRegister_COSAR(void); +void CPL_DLL GDALRegister_TSX(void); +void CPL_DLL GDALRegister_ADRG(void); +void CPL_DLL GDALRegister_SRP(void); +void CPL_DLL GDALRegister_COASP(void); +void CPL_DLL GDALRegister_BLX(void); +void CPL_DLL GDALRegister_LCP(void); +void CPL_DLL GDALRegister_PGCHIP(void); +void CPL_DLL GDALRegister_TMS(void); +void CPL_DLL GDALRegister_EIR(void); +void CPL_DLL GDALRegister_GEOR(void); +void CPL_DLL GDALRegister_TIL(void); +void CPL_DLL GDALRegister_R(void); +void CPL_DLL GDALRegister_Rasterlite(void); +void CPL_DLL GDALRegister_EPSILON(void); +void CPL_DLL GDALRegister_PostGISRaster(void); +void CPL_DLL GDALRegister_NWT_GRD(void); +void CPL_DLL GDALRegister_NWT_GRC(void); +void CPL_DLL GDALRegister_SAGA(void); +void CPL_DLL GDALRegister_KMLSUPEROVERLAY(void); +void CPL_DLL GDALRegister_GTX(void); +void CPL_DLL GDALRegister_LOSLAS(void); +void CPL_DLL GDALRegister_Istar(void); +void CPL_DLL GDALRegister_NTv2(void); +void CPL_DLL GDALRegister_CTable2(void); +void CPL_DLL GDALRegister_JP2OpenJPEG(void); +void CPL_DLL GDALRegister_XYZ(void); +void CPL_DLL GDALRegister_HF2(void); +void CPL_DLL GDALRegister_PDF(void); +void CPL_DLL GDALRegister_JPEGLS(void); +void CPL_DLL GDALRegister_MAP(void); +void CPL_DLL GDALRegister_OZI(void); +void CPL_DLL GDALRegister_ACE2(void); +void CPL_DLL GDALRegister_CTG(void); +void CPL_DLL GDALRegister_E00GRID(void); +void CPL_DLL GDALRegister_SNODAS(void); +void CPL_DLL GDALRegister_WEBP(void); +void CPL_DLL GDALRegister_ZMap(void); +void CPL_DLL GDALRegister_NGSGEOID(void); +void CPL_DLL GDALRegister_MBTiles(void); +void CPL_DLL GDALRegister_ARG(void); +void CPL_DLL GDALRegister_IRIS(void); +void CPL_DLL GDALRegister_KRO(void); +void CPL_DLL GDALRegister_KEA(void); +void CPL_DLL GDALRegister_ROIPAC(void); +void CPL_DLL GDALRegister_PLMOSAIC(void); +CPL_C_END + +#endif /* ndef GDAL_FRMTS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/gcore/gdal_mdreader.cpp b/bazaar/plugin/gdal/gcore/gdal_mdreader.cpp new file mode 100644 index 000000000..8f1ef560a --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdal_mdreader.cpp @@ -0,0 +1,1110 @@ +/****************************************************************************** + * $Id: gdal_mdreader.cpp 29190 2015-05-13 21:40:30Z bishop $ + * + * Project: GDAL Core + * Purpose: Read metadata (mainly the remote sensing imagery) from files of + * different providers like DigitalGlobe, GeoEye etc. + * Author: Frank Warmerdam, warmerdam@pobox.com + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) HER MAJESTY THE QUEEN IN RIGHT OF CANADA (2008) + * as represented by the Canadian Nuclear Safety Commission + * Copyright (c) 2014-2015, NextGIS info@nextgis.ru + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_mdreader.h" +#include "cpl_string.h" +#include "cplkeywordparser.h" + +//readers +#include "mdreader/reader_digital_globe.h" +#include "mdreader/reader_geo_eye.h" +#include "mdreader/reader_orb_view.h" +#include "mdreader/reader_pleiades.h" +#include "mdreader/reader_rdk1.h" +#include "mdreader/reader_landsat.h" +#include "mdreader/reader_spot.h" +#include "mdreader/reader_rapid_eye.h" +#include "mdreader/reader_alos.h" +#include "mdreader/reader_eros.h" +#include "mdreader/reader_kompsat.h" + +CPL_CVSID("$Id: gdal_mdreader.cpp 29190 2015-05-13 21:40:30Z bishop $"); + +/** + * The RPC parameters names + */ + +static const char *apszRPCTXTSingleValItems[] = +{ + RPC_LINE_OFF, + RPC_SAMP_OFF, + RPC_LAT_OFF, + RPC_LONG_OFF, + RPC_HEIGHT_OFF, + RPC_LINE_SCALE, + RPC_SAMP_SCALE, + RPC_LAT_SCALE, + RPC_LONG_SCALE, + RPC_HEIGHT_SCALE, + NULL +}; + +static const char *apszRPCTXT20ValItems[] = +{ + RPC_LINE_NUM_COEFF, + RPC_LINE_DEN_COEFF, + RPC_SAMP_NUM_COEFF, + RPC_SAMP_DEN_COEFF, + NULL +}; + +/** + * GDALMDReaderManager() + */ +GDALMDReaderManager::GDALMDReaderManager() +{ + m_pReader = NULL; +} + +/** + * ~GDALMDReaderManager() + */ +GDALMDReaderManager::~GDALMDReaderManager() +{ + if(NULL != m_pReader) + { + delete m_pReader; + } +} + +/** + * GetReader() + */ + +#define INIT_READER(reader) \ + GDALMDReaderBase* pReaderBase = new reader(pszPath, papszSiblingFiles); \ + if(pReaderBase->HasRequiredFiles()) { m_pReader = pReaderBase; \ + return m_pReader; } \ + delete pReaderBase + +GDALMDReaderBase* GDALMDReaderManager::GetReader(const char *pszPath, + char **papszSiblingFiles, + GUInt32 nType) +{ + if(nType & MDR_DG) + { + INIT_READER(GDALMDReaderDigitalGlobe); + } + + // required filename.tif filename.pvl filename_rpc.txt + if(nType & MDR_OV) + { + INIT_READER(GDALMDReaderOrbView); + } + + if(nType & MDR_GE) + { + INIT_READER(GDALMDReaderGeoEye); + } + + if(nType & MDR_LS) + { + INIT_READER(GDALMDReaderLandsat); + } + + if(nType & MDR_PLEIADES) + { + INIT_READER(GDALMDReaderPleiades); + } + + if(nType & MDR_SPOT) + { + INIT_READER(GDALMDReaderSpot); + } + + if(nType & MDR_RDK1) + { + INIT_READER(GDALMDReaderResursDK1); + } + + if(nType & MDR_RE) + { + INIT_READER(GDALMDReaderRapidEye); + } + + // required filename.tif filename.rpc filename.txt + if(nType & MDR_KOMPSAT) + { + INIT_READER(GDALMDReaderKompsat); + } + + if(nType & MDR_EROS) + { + INIT_READER(GDALMDReaderEROS); + } + + if(nType & MDR_ALOS) + { + INIT_READER(GDALMDReaderALOS); + } + + return NULL; +} + +/** + * GDALMDReaderBase() + */ +GDALMDReaderBase::GDALMDReaderBase(CPL_UNUSED const char *pszPath, + CPL_UNUSED char **papszSiblingFiles) +{ + m_bIsMetadataLoad = false; + m_papszIMDMD = NULL; + m_papszRPCMD = NULL; + m_papszIMAGERYMD = NULL; + m_papszDEFAULTMD = NULL; +} + +/** + * ~GDALMDReaderBase() + */ +GDALMDReaderBase::~GDALMDReaderBase() +{ + CSLDestroy(m_papszIMDMD); + CSLDestroy(m_papszRPCMD); + CSLDestroy(m_papszIMAGERYMD); + CSLDestroy(m_papszDEFAULTMD); +} + +/** + * GetMetadataItem() + */ +char ** GDALMDReaderBase::GetMetadataDomain(const char *pszDomain) +{ + LoadMetadata(); + if(EQUAL(pszDomain, MD_DOMAIN_DEFAULT)) + return m_papszDEFAULTMD; + else if(EQUAL(pszDomain, MD_DOMAIN_IMD)) + return m_papszIMDMD; + else if(EQUAL(pszDomain, MD_DOMAIN_RPC)) + return m_papszRPCMD; + else if(EQUAL(pszDomain, MD_DOMAIN_IMAGERY)) + return m_papszIMAGERYMD; + return NULL; +} + +/** + * LoadMetadata() + */ +void GDALMDReaderBase::LoadMetadata() +{ + if(m_bIsMetadataLoad) + return; + m_bIsMetadataLoad = true; +} + +/** + * GetAcqisitionTimeFromString() + */ +const time_t GDALMDReaderBase::GetAcquisitionTimeFromString( + const char* pszDateTime) +{ + if(NULL == pszDateTime) + return 0; + + int iYear; + int iMonth; + int iDay; + int iHours; + int iMin; + int iSec; + + int r = sscanf ( pszDateTime, "%d-%d-%dT%d:%d:%d.%*dZ", + &iYear, &iMonth, &iDay, &iHours, &iMin, &iSec); + + if (r != 6) + return 0; + + struct tm tmDateTime; + tmDateTime.tm_sec = iSec; + tmDateTime.tm_min = iMin; + tmDateTime.tm_hour = iHours; + tmDateTime.tm_mday = iDay; + tmDateTime.tm_mon = iMonth - 1; + tmDateTime.tm_year = iYear - 1900; + tmDateTime.tm_isdst = -1; + + return mktime(&tmDateTime); +} + +/** + * FillMetadata() + */ + +#define SETMETADATA(mdmd, md, domain) if(NULL != md) { \ + char** papszCurrentMd = CSLDuplicate(mdmd->GetMetadata(domain)); \ + papszCurrentMd = CSLMerge(papszCurrentMd, md); \ + mdmd->SetMetadata(papszCurrentMd, domain); \ + CSLDestroy(papszCurrentMd); } + +bool GDALMDReaderBase::FillMetadata(GDALMultiDomainMetadata* poMDMD) +{ + if(NULL == poMDMD) + return false; + + LoadMetadata(); + + SETMETADATA(poMDMD, m_papszIMDMD, MD_DOMAIN_IMD ); + SETMETADATA(poMDMD, m_papszRPCMD, MD_DOMAIN_RPC ); + SETMETADATA(poMDMD, m_papszIMAGERYMD, MD_DOMAIN_IMAGERY ); + SETMETADATA(poMDMD, m_papszDEFAULTMD, MD_DOMAIN_DEFAULT ); + + return true; +} + +/** + * AddXMLNameValueToList() + */ +char** GDALMDReaderBase::AddXMLNameValueToList(char** papszList, + const char *pszName, + const char *pszValue) +{ + return CSLAddNameValue(papszList, pszName, pszValue); +} + +/** + * CPLReadXMLToList() + */ +char** GDALMDReaderBase::ReadXMLToList(CPLXMLNode* psNode, char** papszList, + const char* pszName) +{ + if(NULL == psNode) + return papszList; + + if (psNode->eType == CXT_Text) + { + papszList = AddXMLNameValueToList(papszList, pszName, psNode->pszValue); + } + + if (psNode->eType == CXT_Element) + { + + int nAddIndex = 0; + bool bReset = false; + for(CPLXMLNode* psChildNode = psNode->psChild; NULL != psChildNode; + psChildNode = psChildNode->psNext) + { + if (psChildNode->eType == CXT_Element) + { + // check name duplicates + if(NULL != psChildNode->psNext) + { + if(bReset) + { + bReset = false; + nAddIndex = 0; + } + + if(EQUAL(psChildNode->pszValue, psChildNode->psNext->pszValue)) + { + nAddIndex++; + } + else + { // the name changed + + if(nAddIndex > 0) + { + bReset = true; + nAddIndex++; + } + } + } + else + { + if(bReset) + { + bReset = false; + nAddIndex = 0; + } + + if(nAddIndex > 0) + { + nAddIndex++; + } + } + + char szName[512]; + if(nAddIndex > 0) + { + CPLsnprintf( szName, 511, "%s_%d", psChildNode->pszValue, + nAddIndex); + } + else + { + CPLStrlcpy(szName, psChildNode->pszValue, 511); + } + + char szNameNew[512]; + if(CPLStrnlen( pszName, 511 ) > 0) //if no prefix just set name to node name + { + CPLsnprintf( szNameNew, 511, "%s.%s", pszName, szName ); + } + else + { + CPLsnprintf( szNameNew, 511, "%s.%s", psNode->pszValue, szName ); + } + + papszList = ReadXMLToList(psChildNode, papszList, szNameNew); + } + else if( psChildNode->eType == CXT_Attribute ) + { + papszList = AddXMLNameValueToList(papszList, + CPLSPrintf("%s.%s", pszName, psChildNode->pszValue), + psChildNode->psChild->pszValue); + } + else + { + // Text nodes should always have name + if(EQUAL(pszName, "")) + { + papszList = ReadXMLToList(psChildNode, papszList, psNode->pszValue); + } + else + { + papszList = ReadXMLToList(psChildNode, papszList, pszName); + } + } + } + } + + // proceed next only on top level + + if(NULL != psNode->psNext && EQUAL(pszName, "")) + { + papszList = ReadXMLToList(psNode->psNext, papszList, pszName); + } + + return papszList; +} + +//------------------------------------------------------------------------------ +// Miscellaneous functions +//------------------------------------------------------------------------------ + + +/** + * GDALCheckFileHeader() + */ +const bool GDALCheckFileHeader(const CPLString& soFilePath, + const char * pszTestString, int nBufferSize) +{ + VSILFILE* fpL = VSIFOpenL( soFilePath, "r" ); + if( fpL == NULL ) + return false; + char *pBuffer = new char[nBufferSize + 1]; + pBuffer[nBufferSize] = 0; + int nReadBytes = (int) VSIFReadL( pBuffer, 1, nBufferSize, fpL ); + VSIFCloseL(fpL); + if(nReadBytes == 0) + { + delete [] pBuffer; + return false; + } + + bool bResult = strstr(pBuffer, pszTestString) != NULL; + delete [] pBuffer; + + return bResult; +} + +/** + * CPLStrip() + */ +CPLString CPLStrip(const CPLString& sString, const char cChar) +{ + if(sString.empty()) + return sString; + + size_t dCopyFrom = 0; + size_t dCopyCount = sString.size(); + + if (sString[0] == cChar) + { + dCopyFrom++; + dCopyCount--; + } + + if (sString[sString.size() - 1] == cChar) + dCopyCount--; + + if(dCopyCount == 0) + return CPLString(); + + return sString.substr(dCopyFrom, dCopyCount); +} + +/** + * CPLStripQuotes() + */ +CPLString CPLStripQuotes(const CPLString& sString) +{ + return CPLStrip( CPLStrip(sString, '"'), '\''); +} + + + + +/************************************************************************/ +/* GDALLoadRPBFile() */ +/************************************************************************/ + +static const char *apszRPBMap[] = { + apszRPCTXTSingleValItems[0], "IMAGE.lineOffset", + apszRPCTXTSingleValItems[1], "IMAGE.sampOffset", + apszRPCTXTSingleValItems[2], "IMAGE.latOffset", + apszRPCTXTSingleValItems[3], "IMAGE.longOffset", + apszRPCTXTSingleValItems[4], "IMAGE.heightOffset", + apszRPCTXTSingleValItems[5], "IMAGE.lineScale", + apszRPCTXTSingleValItems[6], "IMAGE.sampScale", + apszRPCTXTSingleValItems[7], "IMAGE.latScale", + apszRPCTXTSingleValItems[8], "IMAGE.longScale", + apszRPCTXTSingleValItems[9], "IMAGE.heightScale", + apszRPCTXT20ValItems[0], "IMAGE.lineNumCoef", + apszRPCTXT20ValItems[1], "IMAGE.lineDenCoef", + apszRPCTXT20ValItems[2], "IMAGE.sampNumCoef", + apszRPCTXT20ValItems[3], "IMAGE.sampDenCoef", + NULL, NULL }; + +char **GDALLoadRPBFile( const CPLString& soFilePath ) +{ + if( soFilePath.empty() ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Read file and parse. */ +/* -------------------------------------------------------------------- */ + CPLKeywordParser oParser; + + VSILFILE *fp = VSIFOpenL( soFilePath, "r" ); + + if( fp == NULL ) + return NULL; + + if( !oParser.Ingest( fp ) ) + { + VSIFCloseL( fp ); + return NULL; + } + + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Extract RPC information, in a GDAL "standard" metadata format. */ +/* -------------------------------------------------------------------- */ + int i; + char **papszMD = NULL; + for( i = 0; apszRPBMap[i] != NULL; i += 2 ) + { + const char *pszRPBVal = oParser.GetKeyword( apszRPBMap[i+1] ); + CPLString osAdjVal; + + if( pszRPBVal == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s file found, but missing %s field (and possibly others).", + soFilePath.c_str(), apszRPBMap[i+1] ); + CSLDestroy( papszMD ); + return NULL; + } + + if( strchr(pszRPBVal,',') == NULL ) + osAdjVal = pszRPBVal; + else + { + // strip out commas and turn newlines into spaces. + int j; + + for( j = 0; pszRPBVal[j] != '\0'; j++ ) + { + switch( pszRPBVal[j] ) + { + case ',': + case '\n': + case '\r': + osAdjVal += ' '; + break; + + case '(': + case ')': + break; + + default: + osAdjVal += pszRPBVal[j]; + } + } + } + + papszMD = CSLSetNameValue( papszMD, apszRPBMap[i], osAdjVal ); + } + + return papszMD; +} + +/************************************************************************/ +/* GDALLoadRPCFile() */ +/************************************************************************/ + +/* Load a GeoEye _rpc.txt file. See ticket http://trac.osgeo.org/gdal/ticket/3639 */ + +char ** GDALLoadRPCFile( const CPLString& soFilePath ) +{ + if( soFilePath.empty() ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Read file and parse. */ +/* -------------------------------------------------------------------- */ + char **papszLines = CSLLoad2( soFilePath, 100, 100, NULL ); + if(!papszLines) + return NULL; + + char **papszMD = NULL; + + /* From LINE_OFF to HEIGHT_SCALE */ + for(size_t i = 0; i < 19; i += 2 ) + { + const char *pszRPBVal = CSLFetchNameValue(papszLines, apszRPBMap[i] ); + if( pszRPBVal == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s file found, but missing %s field (and possibly others).", + soFilePath.c_str(), apszRPBMap[i]); + CSLDestroy( papszMD ); + CSLDestroy( papszLines ); + return NULL; + } + else + { + while( *pszRPBVal == ' ' || *pszRPBVal == '\t' ) pszRPBVal ++; + papszMD = CSLSetNameValue( papszMD, apszRPBMap[i], pszRPBVal ); + } + } + + /* For LINE_NUM_COEFF, LINE_DEN_COEFF, SAMP_NUM_COEFF, SAMP_DEN_COEFF */ + /* parameters that have 20 values each */ + for(size_t i = 20; apszRPBMap[i] != NULL; i += 2 ) + { + CPLString soVal; + for(int j = 1; j <= 20; j++) + { + CPLString soRPBMapItem; + soRPBMapItem.Printf("%s_%d", apszRPBMap[i], j); + const char *pszRPBVal = CSLFetchNameValue(papszLines, soRPBMapItem.c_str() ); + if( pszRPBVal == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s file found, but missing %s field (and possibly others).", + soFilePath.c_str(), soRPBMapItem.c_str() ); + CSLDestroy( papszMD ); + CSLDestroy( papszLines ); + return NULL; + } + else + { + while( *pszRPBVal == ' ' || *pszRPBVal == '\t' ) pszRPBVal ++; + soVal += pszRPBVal; + soVal += " "; + } + } + papszMD = CSLSetNameValue( papszMD, apszRPBMap[i], soVal.c_str() ); + } + + CSLDestroy( papszLines ); + return papszMD; +} + +/************************************************************************/ +/* GDALWriteRPCTXTFile() */ +/************************************************************************/ + +CPLErr GDALWriteRPCTXTFile( const char *pszFilename, char **papszMD ) + +{ + CPLString osRPCFilename = pszFilename; + CPLString soPt("."); + size_t found = osRPCFilename.rfind(soPt); + if (found == CPLString::npos) + return CE_Failure; + osRPCFilename.replace (found, osRPCFilename.size() - found, "_RPC.TXT"); + +/* -------------------------------------------------------------------- */ +/* Read file and parse. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFOpenL( osRPCFilename, "w" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create %s for writing.\n%s", + osRPCFilename.c_str(), CPLGetLastErrorMsg() ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Write RPC values from our RPC metadata. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; apszRPCTXTSingleValItems[i] != NULL; i ++ ) + { + const char *pszRPCVal = CSLFetchNameValue( papszMD, apszRPCTXTSingleValItems[i] ); + if( pszRPCVal == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s field missing in metadata, %s file not written.", + apszRPCTXTSingleValItems[i], osRPCFilename.c_str() ); + VSIFCloseL( fp ); + VSIUnlink( osRPCFilename ); + return CE_Failure; + } + + VSIFPrintfL( fp, "%s: %s\n", apszRPCTXTSingleValItems[i], pszRPCVal ); + } + + + for( i = 0; apszRPCTXT20ValItems[i] != NULL; i ++ ) + { + const char *pszRPCVal = CSLFetchNameValue( papszMD, apszRPCTXT20ValItems[i] ); + if( pszRPCVal == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s field missing in metadata, %s file not written.", + apszRPCTXTSingleValItems[i], osRPCFilename.c_str() ); + VSIFCloseL( fp ); + VSIUnlink( osRPCFilename ); + return CE_Failure; + } + + char **papszItems = CSLTokenizeStringComplex( pszRPCVal, " ,", + FALSE, FALSE ); + + if( CSLCount(papszItems) != 20 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s field is corrupt (not 20 values), %s file not written.\n%s = %s", + apszRPCTXT20ValItems[i], osRPCFilename.c_str(), + apszRPCTXT20ValItems[i], pszRPCVal ); + VSIFCloseL( fp ); + VSIUnlink( osRPCFilename ); + CSLDestroy( papszItems ); + return CE_Failure; + } + + int j; + + for( j = 0; j < 20; j++ ) + { + VSIFPrintfL( fp, "%s_%d: %s\n", apszRPCTXT20ValItems[i], j+1, + papszItems[j] ); + } + CSLDestroy( papszItems ); + } + + + VSIFCloseL( fp ); + + return CE_None; +} + +/************************************************************************/ +/* GDALWriteRPBFile() */ +/************************************************************************/ + +CPLErr GDALWriteRPBFile( const char *pszFilename, char **papszMD ) + +{ + CPLString osRPBFilename = CPLResetExtension( pszFilename, "RPB" ); + + +/* -------------------------------------------------------------------- */ +/* Read file and parse. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFOpenL( osRPBFilename, "w" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create %s for writing.\n%s", + osRPBFilename.c_str(), CPLGetLastErrorMsg() ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Write the prefix information. */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, "%s", "satId = \"QB02\";\n" ); + VSIFPrintfL( fp, "%s", "bandId = \"P\";\n" ); + VSIFPrintfL( fp, "%s", "SpecId = \"RPC00B\";\n" ); + VSIFPrintfL( fp, "%s", "BEGIN_GROUP = IMAGE\n" ); + VSIFPrintfL( fp, "%s", "\terrBias = 0.0;\n" ); + VSIFPrintfL( fp, "%s", "\terrRand = 0.0;\n" ); + +/* -------------------------------------------------------------------- */ +/* Write RPC values from our RPC metadata. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; apszRPBMap[i] != NULL; i += 2 ) + { + const char *pszRPBVal = CSLFetchNameValue( papszMD, apszRPBMap[i] ); + const char *pszRPBTag; + + if( pszRPBVal == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s field missing in metadata, %s file not written.", + apszRPBMap[i], osRPBFilename.c_str() ); + VSIFCloseL( fp ); + VSIUnlink( osRPBFilename ); + return CE_Failure; + } + + pszRPBTag = apszRPBMap[i+1]; + if( EQUALN(pszRPBTag,"IMAGE.",6) ) + pszRPBTag += 6; + + if( strstr(apszRPBMap[i], "COEF" ) == NULL ) + { + VSIFPrintfL( fp, "\t%s = %s;\n", pszRPBTag, pszRPBVal ); + } + else + { + // Reformat in brackets with commas over multiple lines. + + VSIFPrintfL( fp, "\t%s = (\n", pszRPBTag ); + + char **papszItems = CSLTokenizeStringComplex( pszRPBVal, " ,", + FALSE, FALSE ); + + if( CSLCount(papszItems) != 20 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s field is corrupt (not 20 values), %s file not written.\n%s = %s", + apszRPBMap[i], osRPBFilename.c_str(), + apszRPBMap[i], pszRPBVal ); + VSIFCloseL( fp ); + VSIUnlink( osRPBFilename ); + CSLDestroy( papszItems ); + return CE_Failure; + } + + int j; + + for( j = 0; j < 20; j++ ) + { + if( j < 19 ) + VSIFPrintfL( fp, "\t\t\t%s,\n", papszItems[j] ); + else + VSIFPrintfL( fp, "\t\t\t%s);\n", papszItems[j] ); + } + CSLDestroy( papszItems ); + } + } + +/* -------------------------------------------------------------------- */ +/* Write end part */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, "%s", "END_GROUP = IMAGE\n" ); + VSIFPrintfL( fp, "END;\n" ); + VSIFCloseL( fp ); + + return CE_None; +} + +/************************************************************************/ +/* GDAL_IMD_AA2R() */ +/* */ +/* Translate AA version IMD file to R version. */ +/************************************************************************/ + +static int GDAL_IMD_AA2R( char ***ppapszIMD ) + +{ + char **papszIMD = *ppapszIMD; + +/* -------------------------------------------------------------------- */ +/* Verify that we have a new format file. */ +/* -------------------------------------------------------------------- */ + const char *pszValue = CSLFetchNameValue( papszIMD, "version" ); + + if( pszValue == NULL ) + return FALSE; + + if( EQUAL(pszValue,"\"R\"") ) + return TRUE; + + if( !EQUAL(pszValue,"\"AA\"") ) + { + CPLDebug( "IMD", "The file is not the expected 'version = \"AA\"' format.\nProceeding, but file may be corrupted." ); + } + +/* -------------------------------------------------------------------- */ +/* Fix the version line. */ +/* -------------------------------------------------------------------- */ + papszIMD = CSLSetNameValue( papszIMD, "version", "\"R\"" ); + +/* -------------------------------------------------------------------- */ +/* remove a bunch of fields. */ +/* -------------------------------------------------------------------- */ + int iKey; + + static const char *apszToRemove[] = { + "productCatalogId", + "childCatalogId", + "productType", + "numberOfLooks", + "effectiveBandwidth", + "mode", + "scanDirection", + "cloudCover", + "productGSD", + NULL }; + + for( iKey = 0; apszToRemove[iKey] != NULL; iKey++ ) + { + int iTarget = CSLFindName( papszIMD, apszToRemove[iKey] ); + if( iTarget != -1 ) + papszIMD = CSLRemoveStrings( papszIMD, iTarget, 1, NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Replace various min/mean/max with just the mean. */ +/* -------------------------------------------------------------------- */ + static const char *keylist[] = { + "CollectedRowGSD", + "CollectedColGSD", + "SunAz", + "SunEl", + "SatAz", + "SatEl", + "InTrackViewAngle", + "CrossTrackViewAngle", + "OffNadirViewAngle", + NULL }; + + for( iKey = 0; keylist[iKey] != NULL; iKey++ ) + { + CPLString osTarget; + int iTarget; + + osTarget.Printf( "IMAGE_1.min%s", keylist[iKey] ); + iTarget = CSLFindName( papszIMD, osTarget ); + if( iTarget != -1 ) + papszIMD = CSLRemoveStrings( papszIMD, iTarget, 1, NULL ); + + osTarget.Printf( "IMAGE_1.max%s", keylist[iKey] ); + iTarget = CSLFindName( papszIMD, osTarget ); + if( iTarget != -1 ) + papszIMD = CSLRemoveStrings( papszIMD, iTarget, 1, NULL ); + + osTarget.Printf( "IMAGE_1.mean%s", keylist[iKey] ); + iTarget = CSLFindName( papszIMD, osTarget ); + if( iTarget != -1 ) + { + CPLString osValue = CSLFetchNameValue( papszIMD, osTarget ); + CPLString osLine; + + osTarget.Printf( "IMAGE_1.%c%s", + tolower(keylist[iKey][0]), + keylist[iKey]+1 ); + + osLine = osTarget + "=" + osValue; + + CPLFree( papszIMD[iTarget] ); + papszIMD[iTarget] = CPLStrdup(osLine); + } + } + + *ppapszIMD = papszIMD; + return TRUE; +} + +/************************************************************************/ +/* GDALLoadIMDFile() */ +/************************************************************************/ + +char ** GDALLoadIMDFile( const CPLString& osFilePath ) +{ + if( osFilePath.empty() ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Read file and parse. */ +/* -------------------------------------------------------------------- */ + CPLKeywordParser oParser; + + VSILFILE *fp = VSIFOpenL( osFilePath, "r" ); + + if( fp == NULL ) + return NULL; + + if( !oParser.Ingest( fp ) ) + { + VSIFCloseL( fp ); + return NULL; + } + + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Consider version changing. */ +/* -------------------------------------------------------------------- */ + char **papszIMD = CSLDuplicate( oParser.GetAllKeywords() ); + const char *pszVersion = CSLFetchNameValue( papszIMD, "version" ); + + if( pszVersion == NULL ) + { + /* ? */; + } + else if( EQUAL(pszVersion,"\"AA\"") ) + { + GDAL_IMD_AA2R( &papszIMD ); + } + + return papszIMD; +} + +/************************************************************************/ +/* GDALWriteIMDMultiLine() */ +/* */ +/* Write a value that is split over multiple lines. */ +/************************************************************************/ + +static void GDALWriteIMDMultiLine( VSILFILE *fp, const char *pszValue ) + +{ + char **papszItems = CSLTokenizeStringComplex( pszValue, "(,) ", + FALSE, FALSE ); + int nItemCount = CSLCount(papszItems); + int i; + + VSIFPrintfL( fp, "(\n" ); + + for( i = 0; i < nItemCount; i++ ) + { + if( i == nItemCount-1 ) + VSIFPrintfL( fp, "\t%s );\n", papszItems[i] ); + else + VSIFPrintfL( fp, "\t%s,\n", papszItems[i] ); + } + CSLDestroy( papszItems ); +} + +/************************************************************************/ +/* GDALWriteIMDFile() */ +/************************************************************************/ + +CPLErr GDALWriteIMDFile( const char *pszFilename, char **papszMD ) + +{ + CPLString osRPBFilename = CPLResetExtension( pszFilename, "IMD" ); + +/* -------------------------------------------------------------------- */ +/* Read file and parse. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFOpenL( osRPBFilename, "w" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to create %s for writing.\n%s", + osRPBFilename.c_str(), CPLGetLastErrorMsg() ); + return CE_Failure; + } + +/* ==================================================================== */ +/* -------------------------------------------------------------------- */ +/* Loop through all values writing. */ +/* -------------------------------------------------------------------- */ +/* ==================================================================== */ + int iKey; + CPLString osCurSection; + + for( iKey = 0; papszMD[iKey] != NULL; iKey++ ) + { + char *pszRawKey = NULL; + const char *pszValue = CPLParseNameValue( papszMD[iKey], &pszRawKey ); + CPLString osKeySection, osKeyItem; + char *pszDot = strchr(pszRawKey,'.'); + +/* -------------------------------------------------------------------- */ +/* Split stuff like BAND_P.ULLon into section and item. */ +/* -------------------------------------------------------------------- */ + if( pszDot == NULL ) + { + osKeyItem = pszRawKey; + } + else + { + osKeyItem = pszDot+1; + *pszDot = '\0'; + osKeySection = pszRawKey; + } + CPLFree( pszRawKey ); + +/* -------------------------------------------------------------------- */ +/* Close and/or start sections as needed. */ +/* -------------------------------------------------------------------- */ + if( osCurSection.size() && !EQUAL(osCurSection,osKeySection) ) + VSIFPrintfL( fp, "END_GROUP = %s\n", osCurSection.c_str() ); + + if( osKeySection.size() && !EQUAL(osCurSection,osKeySection) ) + VSIFPrintfL( fp, "BEGIN_GROUP = %s\n", osKeySection.c_str() ); + + osCurSection = osKeySection; + +/* -------------------------------------------------------------------- */ +/* Print out simple item. */ +/* -------------------------------------------------------------------- */ + if( osCurSection.size() ) + VSIFPrintfL( fp, "\t%s = ", osKeyItem.c_str() ); + else + VSIFPrintfL( fp, "%s = ", osKeyItem.c_str() ); + + if( pszValue[0] != '(' ) + VSIFPrintfL( fp, "%s;\n", pszValue ); + else + GDALWriteIMDMultiLine( fp, pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Close off. */ +/* -------------------------------------------------------------------- */ + if( osCurSection.size() ) + VSIFPrintfL( fp, "END_GROUP = %s\n", osCurSection.c_str() ); + + VSIFPrintfL( fp, "END;\n" ); + + VSIFCloseL( fp ); + + return CE_None; +} diff --git a/bazaar/plugin/gdal/gcore/gdal_mdreader.h b/bazaar/plugin/gdal/gcore/gdal_mdreader.h new file mode 100644 index 000000000..f060cae22 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdal_mdreader.h @@ -0,0 +1,207 @@ +/****************************************************************************** + * $Id: gdal_mdreader.h 29190 2015-05-13 21:40:30Z bishop $ + * + * Project: GDAL Core + * Purpose: Read metadata (mainly the remote sensing imagery) from files of + * different providers like DigitalGlobe, GeoEye etc. + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015, NextGIS info@nextgis.ru + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_MDREADER_H_INCLUDED +#define GDAL_MDREADER_H_INCLUDED + + +#include "cpl_port.h" +#include "gdal_priv.h" + +#define MD_DOMAIN_IMD "IMD" /**< image metadata section */ +#define MD_DOMAIN_RPC "RPC" /**< rpc metadata section */ +#define MD_DOMAIN_IMAGERY "IMAGERY" /**< imagery metadata section */ +#define MD_DOMAIN_DEFAULT "" /**< default metadata section */ + +#define MD_NAME_ACQDATETIME "ACQUISITIONDATETIME" /**< Acquisition Date Time property name. The time should be in UTC */ +#define MD_NAME_SATELLITE "SATELLITEID" /**< Satellite identificator property name */ +#define MD_NAME_CLOUDCOVER "CLOUDCOVER" /**< Cloud coverage property name. The value between 0 - 100 or 999 if n/a */ +#define MD_NAME_MDTYPE "METADATATYPE" /**< Metadata reader type property name. The reader processed this metadata */ + +#define MD_DATETIMEFORMAT "%Y-%m-%d %H:%M:%S" /**< Date time format */ +#define MD_CLOUDCOVER_NA "999" /**< The value if cloud cover is n/a */ + +/** + * RPC/RPB specific defines + */ + +#define RPC_LINE_OFF "LINE_OFF" +#define RPC_SAMP_OFF "SAMP_OFF" +#define RPC_LAT_OFF "LAT_OFF" +#define RPC_LONG_OFF "LONG_OFF" +#define RPC_HEIGHT_OFF "HEIGHT_OFF" +#define RPC_LINE_SCALE "LINE_SCALE" +#define RPC_SAMP_SCALE "SAMP_SCALE" +#define RPC_LAT_SCALE "LAT_SCALE" +#define RPC_LONG_SCALE "LONG_SCALE" +#define RPC_HEIGHT_SCALE "HEIGHT_SCALE" +#define RPC_LINE_NUM_COEFF "LINE_NUM_COEFF" +#define RPC_LINE_DEN_COEFF "LINE_DEN_COEFF" +#define RPC_SAMP_NUM_COEFF "SAMP_NUM_COEFF" +#define RPC_SAMP_DEN_COEFF "SAMP_DEN_COEFF" + +/** + * Enumerator of metadata readers + */ + +typedef enum { + MDR_None = 0x00000000, /**< no reader */ + MDR_DG = 0x00000001, /**< Digital Globe, METADATATYPE=DG */ + MDR_GE = 0x00000002, /**< Geo Eye, METADATATYPE=GE */ + MDR_OV = 0x00000004, /**< Orb View, METADATATYPE=OV */ + MDR_PLEIADES = 0x00000008, /**< Pleiades, METADATATYPE=DIMAP */ + MDR_SPOT = 0x00000010, /**< Spot, METADATATYPE=DIMAP */ + MDR_RDK1 = 0x00000020, /**< Resurs DK1, METADATATYPE=MSP */ + MDR_LS = 0x00000040, /**< Landsat, METADATATYPE=ODL */ + MDR_RE = 0x00000080, /**< RapidEye, METADATATYPE=RE */ + MDR_KOMPSAT = 0x00000100, /**< Kompsat, METADATATYPE=KARI */ + MDR_EROS = 0x00000200, /**< EROS, METADATATYPE=EROS */ + MDR_ALOS = 0x00000400, /**< ALOS, METADATATYPE=ALOS */ + MDR_ANY = MDR_DG | MDR_GE | MDR_OV | MDR_PLEIADES | MDR_SPOT | MDR_RDK1 | + MDR_LS | MDR_RE | MDR_KOMPSAT | MDR_EROS | MDR_ALOS /**< any reader */ +} MDReaders; + + +/** + * The base class for all metadata readers + */ +class GDALMDReaderBase{ +public: + GDALMDReaderBase(const char *pszPath, char **papszSiblingFiles); + virtual ~GDALMDReaderBase(); + + /** + * @brief Get specified metadata domain + * @param pszDomain The metadata domain to return + * @return List of metadata items + */ + virtual char ** GetMetadataDomain(const char *pszDomain); + /** + * @brief Fill provided metatada store class + * @param poMDMD Metatada store class + * @return true on success or false + */ + virtual bool FillMetadata(GDALMultiDomainMetadata* poMDMD); + /** + * @brief Determine whether the input parameter correspond to the particular + * provider of remote sensing data completely + * @return True if all needed sources files found + */ + virtual const bool HasRequiredFiles() const = 0; + /** + * @brief Get metadata file names. The caller become owner of returned list + * and have to free it via CSLDestroy. + * @return A file name list + */ + virtual char** GetMetadataFiles() const = 0; +protected: + /** + * @brief Load metadata to the correspondent IMD, RPB, IMAGERY and DEFAULT + * domains + */ + virtual void LoadMetadata(); + /** + * @brief Convert string like 2012-02-25T00:25:59.9440000Z to time + * @param pszDateTime String to convert + * @return value in time_t + */ + virtual const time_t GetAcquisitionTimeFromString(const char* pszDateTime); + /** + * @brief ReadXMLToList Transform xml to list of NULL terminated name=value + * strings + * @param psNode A xml node to process + * @param papszList A list to fill with name=value strings + * @param pszName A name of parent node. For root xml node should be empty. + * If name is not empty, the sibling nodes will not proceed + * @return An input list filled with values + */ + virtual char** ReadXMLToList(CPLXMLNode* psNode, char** papszList, + const char* pszName = ""); + /** + * @brief AddXMLNameValueToList Execute from ReadXMLToList to add name and + * value to list. One can override this function for special + * processing input values before add to list. + * @param papszList A list to fill with name=value strings + * @param pszName A name to add + * @param pszValue A value to add + * @return An input list filled with values + */ + virtual char** AddXMLNameValueToList(char** papszList, const char *pszName, + const char *pszValue); +protected: + char **m_papszIMDMD; + char **m_papszRPCMD; + char **m_papszIMAGERYMD; + char **m_papszDEFAULTMD; + bool m_bIsMetadataLoad; +}; + +/** + * The metadata reader main class. + * The main purpose of this class is to provide an correspondent reader + * for provided path. + */ +class CPL_DLL GDALMDReaderManager{ +public: + GDALMDReaderManager(); + virtual ~GDALMDReaderManager(); + + /** + * @brief Try to detect metadata reader correspondent to the provided + * datasource path + * @param pszPath a path to GDALDataset + * @param papszSiblingFiles file list for metadata search purposes + * @param nType a preferable reader type (may be the OR of MDReaders) + * @return an appropriate reader or NULL if no such reader or error. + * The pointer delete by the GDALMDReaderManager, so the user have not + * delete it. + */ + virtual GDALMDReaderBase* GetReader(const char *pszPath, + char **papszSiblingFiles, + GUInt32 nType = MDR_ANY); +protected: + GDALMDReaderBase *m_pReader; +}; + +// misc +CPLString CPLStrip(const CPLString& osString, const char cChar); +CPLString CPLStripQuotes(const CPLString& osString); +char** GDALLoadRPBFile( const CPLString& osFilePath ); +char** GDALLoadRPCFile( const CPLString& osFilePath ); +char** GDALLoadIMDFile( const CPLString& osFilePath ); +const bool GDALCheckFileHeader(const CPLString& soFilePath, + const char * pszTestString, + int nBufferSize = 256); + +CPLErr GDALWriteRPBFile( const char *pszFilename, char **papszMD ); +CPLErr GDALWriteRPCTXTFile( const char *pszFilename, char **papszMD ); +CPLErr GDALWriteIMDFile( const char *pszFilename, char **papszMD ); + +#endif //GDAL_MDREADER_H_INCLUDED diff --git a/bazaar/plugin/gdal/gcore/gdal_misc.cpp b/bazaar/plugin/gdal/gcore/gdal_misc.cpp new file mode 100644 index 000000000..b34372363 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdal_misc.cpp @@ -0,0 +1,3386 @@ +/****************************************************************************** + * $Id: gdal_misc.cpp 29326 2015-06-10 20:36:31Z rouault $ + * + * Project: GDAL Core + * Purpose: Free standing functions for GDAL. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_string.h" +#include "cpl_minixml.h" +#include "cpl_multiproc.h" +#include +#include + +CPL_CVSID("$Id: gdal_misc.cpp 29326 2015-06-10 20:36:31Z rouault $"); + +#include "ogr_spatialref.h" +#include "gdal_mdreader.h" + +/************************************************************************/ +/* __pure_virtual() */ +/* */ +/* The following is a gross hack to remove the last remaining */ +/* dependency on the GNU C++ standard library. */ +/************************************************************************/ + +#ifdef __GNUC__ + +extern "C" +void __pure_virtual() + +{ +} + +#endif + +/************************************************************************/ +/* GDALDataTypeUnion() */ +/************************************************************************/ + +/** + * \brief Return the smallest data type that can fully express both input data + * types. + * + * @param eType1 first data type. + * @param eType2 second data type. + * + * @return a data type able to express eType1 and eType2. + */ + +GDALDataType CPL_STDCALL +GDALDataTypeUnion( GDALDataType eType1, GDALDataType eType2 ) + +{ + int bFloating, bComplex, nBits, bSigned; + + bComplex = GDALDataTypeIsComplex(eType1) | GDALDataTypeIsComplex(eType2); + + switch( eType1 ) + { + case GDT_Byte: + nBits = 8; + bSigned = FALSE; + bFloating = FALSE; + break; + + case GDT_Int16: + case GDT_CInt16: + nBits = 16; + bSigned = TRUE; + bFloating = FALSE; + break; + + case GDT_UInt16: + nBits = 16; + bSigned = FALSE; + bFloating = FALSE; + break; + + case GDT_Int32: + case GDT_CInt32: + nBits = 32; + bSigned = TRUE; + bFloating = FALSE; + break; + + case GDT_UInt32: + nBits = 32; + bSigned = FALSE; + bFloating = FALSE; + break; + + case GDT_Float32: + case GDT_CFloat32: + nBits = 32; + bSigned = TRUE; + bFloating = TRUE; + break; + + case GDT_Float64: + case GDT_CFloat64: + nBits = 64; + bSigned = TRUE; + bFloating = TRUE; + break; + + default: + CPLAssert( FALSE ); + return GDT_Unknown; + } + + switch( eType2 ) + { + case GDT_Byte: + break; + + case GDT_Int16: + case GDT_CInt16: + nBits = MAX(nBits,16); + bSigned = TRUE; + break; + + case GDT_UInt16: + nBits = MAX(nBits,16); + break; + + case GDT_Int32: + case GDT_CInt32: + nBits = MAX(nBits,32); + bSigned = TRUE; + break; + + case GDT_UInt32: + nBits = MAX(nBits,32); + break; + + case GDT_Float32: + case GDT_CFloat32: + nBits = MAX(nBits,32); + bSigned = TRUE; + bFloating = TRUE; + break; + + case GDT_Float64: + case GDT_CFloat64: + nBits = MAX(nBits,64); + bSigned = TRUE; + bFloating = TRUE; + break; + + default: + CPLAssert( FALSE ); + return GDT_Unknown; + } + + if( nBits == 8 ) + return GDT_Byte; + else if( nBits == 16 && bComplex ) + return GDT_CInt16; + else if( nBits == 16 && bSigned ) + return GDT_Int16; + else if( nBits == 16 && !bSigned ) + return GDT_UInt16; + else if( nBits == 32 && bFloating && bComplex ) + return GDT_CFloat32; + else if( nBits == 32 && bFloating ) + return GDT_Float32; + else if( nBits == 32 && bComplex ) + return GDT_CInt32; + else if( nBits == 32 && bSigned ) + return GDT_Int32; + else if( nBits == 32 && !bSigned ) + return GDT_UInt32; + else if( nBits == 64 && bComplex ) + return GDT_CFloat64; + else + return GDT_Float64; +} + + +/************************************************************************/ +/* GDALGetDataTypeSize() */ +/************************************************************************/ + +/** + * \brief Get data type size in bits. + * + * Returns the size of a a GDT_* type in bits, not bytes! + * + * @param eDataType type, such as GDT_Byte. + * @return the number of bits or zero if it is not recognised. + */ + +int CPL_STDCALL GDALGetDataTypeSize( GDALDataType eDataType ) + +{ + switch( eDataType ) + { + case GDT_Byte: + return 8; + + case GDT_UInt16: + case GDT_Int16: + return 16; + + case GDT_UInt32: + case GDT_Int32: + case GDT_Float32: + case GDT_CInt16: + return 32; + + case GDT_Float64: + case GDT_CInt32: + case GDT_CFloat32: + return 64; + + case GDT_CFloat64: + return 128; + + default: + return 0; + } +} + +/************************************************************************/ +/* GDALDataTypeIsComplex() */ +/************************************************************************/ + +/** + * \brief Is data type complex? + * + * @return TRUE if the passed type is complex (one of GDT_CInt16, GDT_CInt32, + * GDT_CFloat32 or GDT_CFloat64), that is it consists of a real and imaginary + * component. + */ + +int CPL_STDCALL GDALDataTypeIsComplex( GDALDataType eDataType ) + +{ + switch( eDataType ) + { + case GDT_CInt16: + case GDT_CInt32: + case GDT_CFloat32: + case GDT_CFloat64: + return TRUE; + + default: + return FALSE; + } +} + +/************************************************************************/ +/* GDALGetDataTypeName() */ +/************************************************************************/ + +/** + * \brief Get name of data type. + * + * Returns a symbolic name for the data type. This is essentially the + * the enumerated item name with the GDT_ prefix removed. So GDT_Byte returns + * "Byte". The returned strings are static strings and should not be modified + * or freed by the application. These strings are useful for reporting + * datatypes in debug statements, errors and other user output. + * + * @param eDataType type to get name of. + * @return string corresponding to existing data type + * or NULL pointer if invalid type given. + */ + +const char * CPL_STDCALL GDALGetDataTypeName( GDALDataType eDataType ) + +{ + switch( eDataType ) + { + case GDT_Unknown: + return "Unknown"; + + case GDT_Byte: + return "Byte"; + + case GDT_UInt16: + return "UInt16"; + + case GDT_Int16: + return "Int16"; + + case GDT_UInt32: + return "UInt32"; + + case GDT_Int32: + return "Int32"; + + case GDT_Float32: + return "Float32"; + + case GDT_Float64: + return "Float64"; + + case GDT_CInt16: + return "CInt16"; + + case GDT_CInt32: + return "CInt32"; + + case GDT_CFloat32: + return "CFloat32"; + + case GDT_CFloat64: + return "CFloat64"; + + default: + return NULL; + } +} + +/************************************************************************/ +/* GDALGetDataTypeByName() */ +/************************************************************************/ + +/** + * \brief Get data type by symbolic name. + * + * Returns a data type corresponding to the given symbolic name. This + * function is opposite to the GDALGetDataTypeName(). + * + * @param pszName string containing the symbolic name of the type. + * + * @return GDAL data type. + */ + +GDALDataType CPL_STDCALL GDALGetDataTypeByName( const char *pszName ) + +{ + VALIDATE_POINTER1( pszName, "GDALGetDataTypeByName", GDT_Unknown ); + + int iType; + + for( iType = 1; iType < GDT_TypeCount; iType++ ) + { + if( GDALGetDataTypeName((GDALDataType)iType) != NULL + && EQUAL(GDALGetDataTypeName((GDALDataType)iType), pszName) ) + { + return (GDALDataType)iType; + } + } + + return GDT_Unknown; +} + +/************************************************************************/ +/* GDALGetAsyncStatusTypeByName() */ +/************************************************************************/ +/** + * Get AsyncStatusType by symbolic name. + * + * Returns a data type corresponding to the given symbolic name. This + * function is opposite to the GDALGetAsyncStatusTypeName(). + * + * @param pszName string containing the symbolic name of the type. + * + * @return GDAL AsyncStatus type. + */ +GDALAsyncStatusType CPL_DLL CPL_STDCALL GDALGetAsyncStatusTypeByName( const char *pszName ) +{ + VALIDATE_POINTER1( pszName, "GDALGetAsyncStatusTypeByName", GARIO_ERROR); + + int iType; + + for( iType = 1; iType < GARIO_TypeCount; iType++ ) + { + if( GDALGetAsyncStatusTypeName((GDALAsyncStatusType)iType) != NULL + && EQUAL(GDALGetAsyncStatusTypeName((GDALAsyncStatusType)iType), pszName) ) + { + return (GDALAsyncStatusType)iType; + } + } + + return GARIO_ERROR; +} + + +/************************************************************************/ +/* GDALGetAsyncStatusTypeName() */ +/************************************************************************/ + +/** + * Get name of AsyncStatus data type. + * + * Returns a symbolic name for the AsyncStatus data type. This is essentially the + * the enumerated item name with the GARIO_ prefix removed. So GARIO_COMPLETE returns + * "COMPLETE". The returned strings are static strings and should not be modified + * or freed by the application. These strings are useful for reporting + * datatypes in debug statements, errors and other user output. + * + * @param eAsyncStatusType type to get name of. + * @return string corresponding to type. + */ + + const char * CPL_STDCALL GDALGetAsyncStatusTypeName( GDALAsyncStatusType eAsyncStatusType ) + +{ + switch( eAsyncStatusType ) + { + case GARIO_PENDING: + return "PENDING"; + + case GARIO_UPDATE: + return "UPDATE"; + + case GARIO_ERROR: + return "ERROR"; + + case GARIO_COMPLETE: + return "COMPLETE"; + default: + return NULL; + } +} + +/************************************************************************/ +/* GDALGetPaletteInterpretationName() */ +/************************************************************************/ + +/** + * \brief Get name of palette interpretation + * + * Returns a symbolic name for the palette interpretation. This is the + * the enumerated item name with the GPI_ prefix removed. So GPI_Gray returns + * "Gray". The returned strings are static strings and should not be modified + * or freed by the application. + * + * @param eInterp palette interpretation to get name of. + * @return string corresponding to palette interpretation. + */ + +const char *GDALGetPaletteInterpretationName( GDALPaletteInterp eInterp ) + +{ + switch( eInterp ) + { + case GPI_Gray: + return "Gray"; + + case GPI_RGB: + return "RGB"; + + case GPI_CMYK: + return "CMYK"; + + case GPI_HLS: + return "HLS"; + + default: + return "Unknown"; + } +} + +/************************************************************************/ +/* GDALGetColorInterpretationName() */ +/************************************************************************/ + +/** + * \brief Get name of color interpretation + * + * Returns a symbolic name for the color interpretation. This is derived from + * the enumerated item name with the GCI_ prefix removed, but there are some + * variations. So GCI_GrayIndex returns "Gray" and GCI_RedBand returns "Red". + * The returned strings are static strings and should not be modified + * or freed by the application. + * + * @param eInterp color interpretation to get name of. + * @return string corresponding to color interpretation + * or NULL pointer if invalid enumerator given. + */ + +const char *GDALGetColorInterpretationName( GDALColorInterp eInterp ) + +{ + switch( eInterp ) + { + case GCI_Undefined: + return "Undefined"; + + case GCI_GrayIndex: + return "Gray"; + + case GCI_PaletteIndex: + return "Palette"; + + case GCI_RedBand: + return "Red"; + + case GCI_GreenBand: + return "Green"; + + case GCI_BlueBand: + return "Blue"; + + case GCI_AlphaBand: + return "Alpha"; + + case GCI_HueBand: + return "Hue"; + + case GCI_SaturationBand: + return "Saturation"; + + case GCI_LightnessBand: + return "Lightness"; + + case GCI_CyanBand: + return "Cyan"; + + case GCI_MagentaBand: + return "Magenta"; + + case GCI_YellowBand: + return "Yellow"; + + case GCI_BlackBand: + return "Black"; + + case GCI_YCbCr_YBand: + return "YCbCr_Y"; + + case GCI_YCbCr_CbBand: + return "YCbCr_Cb"; + + case GCI_YCbCr_CrBand: + return "YCbCr_Cr"; + + default: + return "Unknown"; + } +} + +/************************************************************************/ +/* GDALGetColorInterpretationByName() */ +/************************************************************************/ + +/** + * \brief Get color interpreation by symbolic name. + * + * Returns a color interpreation corresponding to the given symbolic name. This + * function is opposite to the GDALGetColorInterpretationName(). + * + * @param pszName string containing the symbolic name of the color interpretation. + * + * @return GDAL color interpretation. + * + * @since GDAL 1.7.0 + */ + +GDALColorInterp GDALGetColorInterpretationByName( const char *pszName ) + +{ + VALIDATE_POINTER1( pszName, "GDALGetColorInterpretationByName", GCI_Undefined ); + + int iType; + + for( iType = 0; iType <= GCI_Max; iType++ ) + { + if( EQUAL(GDALGetColorInterpretationName((GDALColorInterp)iType), pszName) ) + { + return (GDALColorInterp)iType; + } + } + + return GCI_Undefined; +} + +/************************************************************************/ +/* GDALGetRandomRasterSample() */ +/************************************************************************/ + +int CPL_STDCALL +GDALGetRandomRasterSample( GDALRasterBandH hBand, int nSamples, + float *pafSampleBuf ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRandomRasterSample", 0 ); + + GDALRasterBand *poBand; + + poBand = (GDALRasterBand *) GDALGetRasterSampleOverview( hBand, nSamples ); + CPLAssert( NULL != poBand ); + +/* -------------------------------------------------------------------- */ +/* Figure out the ratio of blocks we will read to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + int nBlockXSize, nBlockYSize; + int nBlocksPerRow, nBlocksPerColumn; + int nSampleRate; + int bGotNoDataValue; + double dfNoDataValue; + int nActualSamples = 0; + int nBlockSampleRate; + int nBlockPixels, nBlockCount; + + dfNoDataValue = poBand->GetNoDataValue( &bGotNoDataValue ); + + poBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + nBlocksPerRow = (poBand->GetXSize() + nBlockXSize - 1) / nBlockXSize; + nBlocksPerColumn = (poBand->GetYSize() + nBlockYSize - 1) / nBlockYSize; + + nBlockPixels = nBlockXSize * nBlockYSize; + nBlockCount = nBlocksPerRow * nBlocksPerColumn; + + if( nBlocksPerRow == 0 || nBlocksPerColumn == 0 || nBlockPixels == 0 + || nBlockCount == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GDALGetRandomRasterSample(): returning because band" + " appears degenerate." ); + + return FALSE; + } + + nSampleRate = (int) MAX(1,sqrt((double) nBlockCount)-2.0); + + if( nSampleRate == nBlocksPerRow && nSampleRate > 1 ) + nSampleRate--; + + while( nSampleRate > 1 + && ((nBlockCount-1) / nSampleRate + 1) * nBlockPixels < nSamples ) + nSampleRate--; + + if ((nSamples / ((nBlockCount-1) / nSampleRate + 1)) == 0) + nBlockSampleRate = 1; + else + nBlockSampleRate = + MAX(1,nBlockPixels / (nSamples / ((nBlockCount-1) / nSampleRate + 1))); + + for( int iSampleBlock = 0; + iSampleBlock < nBlockCount; + iSampleBlock += nSampleRate ) + { + double dfValue = 0.0, dfReal, dfImag; + int iXBlock, iYBlock, iX, iY, iXValid, iYValid, iRemainder = 0; + GDALRasterBlock *poBlock; + + iYBlock = iSampleBlock / nBlocksPerRow; + iXBlock = iSampleBlock - nBlocksPerRow * iYBlock; + + poBlock = poBand->GetLockedBlockRef( iXBlock, iYBlock ); + if( poBlock == NULL ) + continue; + if( poBlock->GetDataRef() == NULL ) + { + poBlock->DropLock(); + continue; + } + + if( (iXBlock + 1) * nBlockXSize > poBand->GetXSize() ) + iXValid = poBand->GetXSize() - iXBlock * nBlockXSize; + else + iXValid = nBlockXSize; + + if( (iYBlock + 1) * nBlockYSize > poBand->GetYSize() ) + iYValid = poBand->GetYSize() - iYBlock * nBlockYSize; + else + iYValid = nBlockYSize; + + for( iY = 0; iY < iYValid; iY++ ) + { + for( iX = iRemainder; iX < iXValid; iX += nBlockSampleRate ) + { + int iOffset; + + iOffset = iX + iY * nBlockXSize; + switch( poBlock->GetDataType() ) + { + case GDT_Byte: + dfValue = ((GByte *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_UInt16: + dfValue = ((GUInt16 *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_Float64: + dfValue = ((double *) poBlock->GetDataRef())[iOffset]; + break; + case GDT_CInt16: + dfReal = ((GInt16 *) poBlock->GetDataRef())[iOffset*2]; + dfImag = ((GInt16 *) poBlock->GetDataRef())[iOffset*2+1]; + dfValue = sqrt(dfReal*dfReal + dfImag*dfImag); + break; + case GDT_CInt32: + dfReal = ((GInt32 *) poBlock->GetDataRef())[iOffset*2]; + dfImag = ((GInt32 *) poBlock->GetDataRef())[iOffset*2+1]; + dfValue = sqrt(dfReal*dfReal + dfImag*dfImag); + break; + case GDT_CFloat32: + dfReal = ((float *) poBlock->GetDataRef())[iOffset*2]; + dfImag = ((float *) poBlock->GetDataRef())[iOffset*2+1]; + dfValue = sqrt(dfReal*dfReal + dfImag*dfImag); + break; + case GDT_CFloat64: + dfReal = ((double *) poBlock->GetDataRef())[iOffset*2]; + dfImag = ((double *) poBlock->GetDataRef())[iOffset*2+1]; + dfValue = sqrt(dfReal*dfReal + dfImag*dfImag); + break; + default: + CPLAssert( FALSE ); + } + + if( bGotNoDataValue && dfValue == dfNoDataValue ) + continue; + + if( nActualSamples < nSamples ) + pafSampleBuf[nActualSamples++] = (float) dfValue; + } + + iRemainder = iX - iXValid; + } + + poBlock->DropLock(); + } + + return nActualSamples; +} + +/************************************************************************/ +/* GDALInitGCPs() */ +/************************************************************************/ + +void CPL_STDCALL GDALInitGCPs( int nCount, GDAL_GCP * psGCP ) + +{ + if( nCount > 0 ) + { + VALIDATE_POINTER0( psGCP, "GDALInitGCPs" ); + } + + for( int iGCP = 0; iGCP < nCount; iGCP++ ) + { + memset( psGCP, 0, sizeof(GDAL_GCP) ); + psGCP->pszId = CPLStrdup(""); + psGCP->pszInfo = CPLStrdup(""); + psGCP++; + } +} + +/************************************************************************/ +/* GDALDeinitGCPs() */ +/************************************************************************/ + +void CPL_STDCALL GDALDeinitGCPs( int nCount, GDAL_GCP * psGCP ) + +{ + if ( nCount > 0 ) + { + VALIDATE_POINTER0( psGCP, "GDALDeinitGCPs" ); + } + + for( int iGCP = 0; iGCP < nCount; iGCP++ ) + { + CPLFree( psGCP->pszId ); + CPLFree( psGCP->pszInfo ); + psGCP++; + } +} + +/************************************************************************/ +/* GDALDuplicateGCPs() */ +/************************************************************************/ + +GDAL_GCP * CPL_STDCALL +GDALDuplicateGCPs( int nCount, const GDAL_GCP *pasGCPList ) + +{ + GDAL_GCP *pasReturn; + + pasReturn = (GDAL_GCP *) CPLMalloc(sizeof(GDAL_GCP) * nCount); + GDALInitGCPs( nCount, pasReturn ); + + for( int iGCP = 0; iGCP < nCount; iGCP++ ) + { + CPLFree( pasReturn[iGCP].pszId ); + pasReturn[iGCP].pszId = CPLStrdup( pasGCPList[iGCP].pszId ); + + CPLFree( pasReturn[iGCP].pszInfo ); + pasReturn[iGCP].pszInfo = CPLStrdup( pasGCPList[iGCP].pszInfo ); + + pasReturn[iGCP].dfGCPPixel = pasGCPList[iGCP].dfGCPPixel; + pasReturn[iGCP].dfGCPLine = pasGCPList[iGCP].dfGCPLine; + pasReturn[iGCP].dfGCPX = pasGCPList[iGCP].dfGCPX; + pasReturn[iGCP].dfGCPY = pasGCPList[iGCP].dfGCPY; + pasReturn[iGCP].dfGCPZ = pasGCPList[iGCP].dfGCPZ; + } + + return pasReturn; +} + +/************************************************************************/ +/* GDALFindAssociatedFile() */ +/************************************************************************/ + +/** + * Find file with alternate extension. + * + * Finds the file with the indicated extension, substituting it in place + * of the extension of the base filename. Generally used to search for + * associated files like world files .RPB files, etc. If necessary, the + * extension will be tried in both upper and lower case. If a sibling file + * list is available it will be used instead of doing VSIStatExL() calls to + * probe the file system. + * + * Note that the result is a dynamic CPLString so this method should not + * be used in a situation where there could be cross heap issues. It is + * generally imprudent for application built on GDAL to use this function + * unless they are sure they will always use the same runtime heap as GDAL. + * + * @param pszBaseFilename the filename relative to which to search. + * @param pszExt the target extension in either upper or lower case. + * @param papszSiblingFiles the list of files in the same directory as + * pszBaseFilename or NULL if they are not known. + * @param nFlags special options controlling search. None defined yet, just + * pass 0. + * + * @return an empty string if the target is not found, otherwise the target + * file with similar path style as the pszBaseFilename. + */ + +CPLString GDALFindAssociatedFile( const char *pszBaseFilename, + const char *pszExt, + char **papszSiblingFiles, + int nFlags ) + +{ + (void) nFlags; + + CPLString osTarget = CPLResetExtension( pszBaseFilename, pszExt ); + + if( papszSiblingFiles == NULL ) + { + VSIStatBufL sStatBuf; + + if( VSIStatExL( osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG ) != 0 ) + { + CPLString osAltExt = pszExt; + + if( islower( pszExt[0] ) ) + osAltExt.toupper(); + else + osAltExt.tolower(); + + osTarget = CPLResetExtension( pszBaseFilename, osAltExt ); + + if( VSIStatExL( osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG ) != 0 ) + return ""; + } + } + else + { + int iSibling = CSLFindString( papszSiblingFiles, + CPLGetFilename(osTarget) ); + if( iSibling < 0 ) + return ""; + + osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling])); + osTarget += papszSiblingFiles[iSibling]; + } + + return osTarget; +} + +/************************************************************************/ +/* GDALLoadOziMapFile() */ +/************************************************************************/ + +#define MAX_GCP 30 + +int CPL_STDCALL GDALLoadOziMapFile( const char *pszFilename, + double *padfGeoTransform, char **ppszWKT, + int *pnGCPCount, GDAL_GCP **ppasGCPs ) + + +{ + char **papszLines; + int iLine, nLines=0; + int nCoordinateCount = 0; + GDAL_GCP asGCPs[MAX_GCP]; + + VALIDATE_POINTER1( pszFilename, "GDALLoadOziMapFile", FALSE ); + VALIDATE_POINTER1( padfGeoTransform, "GDALLoadOziMapFile", FALSE ); + VALIDATE_POINTER1( pnGCPCount, "GDALLoadOziMapFile", FALSE ); + VALIDATE_POINTER1( ppasGCPs, "GDALLoadOziMapFile", FALSE ); + + papszLines = CSLLoad2( pszFilename, 1000, 200, NULL ); + + if ( !papszLines ) + return FALSE; + + nLines = CSLCount( papszLines ); + + // Check the OziExplorer Map file signature + if ( nLines < 5 + || !EQUALN(papszLines[0], "OziExplorer Map Data File Version ", 34) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GDALLoadOziMapFile(): file \"%s\" is not in OziExplorer Map format.", + pszFilename ); + CSLDestroy( papszLines ); + return FALSE; + } + + OGRSpatialReference oSRS; + OGRErr eErr = OGRERR_NONE; + + /* The Map Scale Factor has been introduced recently on the 6th line */ + /* and is a trick that is used to just change that line without changing */ + /* the rest of the MAP file but providing an imagery that is smaller or larger */ + /* so we have to correct the pixel/line values read in the .MAP file so they */ + /* match the actual imagery dimension. Well, this is a bad summary of what */ + /* is explained at http://tech.groups.yahoo.com/group/OziUsers-L/message/12484 */ + double dfMSF = 1; + + for ( iLine = 5; iLine < nLines; iLine++ ) + { + if ( EQUALN(papszLines[iLine], "MSF,", 4) ) + { + dfMSF = CPLAtof(papszLines[iLine] + 4); + if (dfMSF <= 0.01) /* Suspicious values */ + { + CPLDebug("OZI", "Suspicious MSF value : %s", papszLines[iLine]); + dfMSF = 1; + } + } + } + + eErr = oSRS.importFromOzi( papszLines ); + if ( eErr == OGRERR_NONE ) + { + if ( ppszWKT != NULL ) + oSRS.exportToWkt( ppszWKT ); + } + + // Iterate all lines in the MAP-file + for ( iLine = 5; iLine < nLines; iLine++ ) + { + char **papszTok = NULL; + + papszTok = CSLTokenizeString2( papszLines[iLine], ",", + CSLT_ALLOWEMPTYTOKENS + | CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES ); + + if ( CSLCount(papszTok) < 12 ) + { + CSLDestroy(papszTok); + continue; + } + + if ( CSLCount(papszTok) >= 17 + && EQUALN(papszTok[0], "Point", 5) + && !EQUAL(papszTok[2], "") + && !EQUAL(papszTok[3], "") + && nCoordinateCount < MAX_GCP ) + { + int bReadOk = FALSE; + double dfLon = 0., dfLat = 0.; + + if ( !EQUAL(papszTok[6], "") + && !EQUAL(papszTok[7], "") + && !EQUAL(papszTok[9], "") + && !EQUAL(papszTok[10], "") ) + { + // Set geographical coordinates of the pixels + dfLon = CPLAtofM(papszTok[9]) + CPLAtofM(papszTok[10]) / 60.0; + dfLat = CPLAtofM(papszTok[6]) + CPLAtofM(papszTok[7]) / 60.0; + if ( EQUAL(papszTok[11], "W") ) + dfLon = -dfLon; + if ( EQUAL(papszTok[8], "S") ) + dfLat = -dfLat; + + // Transform from the geographical coordinates into projected + // coordinates. + if ( eErr == OGRERR_NONE ) + { + OGRSpatialReference *poLatLong = NULL; + OGRCoordinateTransformation *poTransform = NULL; + + poLatLong = oSRS.CloneGeogCS(); + if ( poLatLong ) + { + poTransform = OGRCreateCoordinateTransformation( poLatLong, &oSRS ); + if ( poTransform ) + { + bReadOk = poTransform->Transform( 1, &dfLon, &dfLat ); + delete poTransform; + } + delete poLatLong; + } + } + } + else if ( !EQUAL(papszTok[14], "") + && !EQUAL(papszTok[15], "") ) + { + // Set cartesian coordinates of the pixels. + dfLon = CPLAtofM(papszTok[14]); + dfLat = CPLAtofM(papszTok[15]); + bReadOk = TRUE; + + //if ( EQUAL(papszTok[16], "S") ) + // dfLat = -dfLat; + } + + if ( bReadOk ) + { + GDALInitGCPs( 1, asGCPs + nCoordinateCount ); + + // Set pixel/line part + asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]) / dfMSF; + asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]) / dfMSF; + + asGCPs[nCoordinateCount].dfGCPX = dfLon; + asGCPs[nCoordinateCount].dfGCPY = dfLat; + + nCoordinateCount++; + } + } + + CSLDestroy( papszTok ); + } + + CSLDestroy( papszLines ); + + if ( nCoordinateCount == 0 ) + { + CPLDebug( "GDAL", "GDALLoadOziMapFile(\"%s\") did read no GCPs.", + pszFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try to convert the GCPs into a geotransform definition, if */ +/* possible. Otherwise we will need to use them as GCPs. */ +/* -------------------------------------------------------------------- */ + if( !GDALGCPsToGeoTransform( nCoordinateCount, asGCPs, padfGeoTransform, + CSLTestBoolean(CPLGetConfigOption("OZI_APPROX_GEOTRANSFORM", "NO")) ) ) + { + if ( pnGCPCount && ppasGCPs ) + { + CPLDebug( "GDAL", + "GDALLoadOziMapFile(%s) found file, wasn't able to derive a\n" + "first order geotransform. Using points as GCPs.", + pszFilename ); + + *ppasGCPs = (GDAL_GCP *) + CPLCalloc( sizeof(GDAL_GCP),nCoordinateCount ); + memcpy( *ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount ); + *pnGCPCount = nCoordinateCount; + } + } + else + { + GDALDeinitGCPs( nCoordinateCount, asGCPs ); + } + + return TRUE; +} + +#undef MAX_GCP + +/************************************************************************/ +/* GDALReadOziMapFile() */ +/************************************************************************/ + +int CPL_STDCALL GDALReadOziMapFile( const char * pszBaseFilename, + double *padfGeoTransform, char **ppszWKT, + int *pnGCPCount, GDAL_GCP **ppasGCPs ) + + +{ + const char *pszOzi; + FILE *fpOzi; + +/* -------------------------------------------------------------------- */ +/* Try lower case, then upper case. */ +/* -------------------------------------------------------------------- */ + pszOzi = CPLResetExtension( pszBaseFilename, "map" ); + + fpOzi = VSIFOpen( pszOzi, "rt" ); + + if ( fpOzi == NULL && VSIIsCaseSensitiveFS(pszOzi) ) + { + pszOzi = CPLResetExtension( pszBaseFilename, "MAP" ); + fpOzi = VSIFOpen( pszOzi, "rt" ); + } + + if ( fpOzi == NULL ) + return FALSE; + + VSIFClose( fpOzi ); + +/* -------------------------------------------------------------------- */ +/* We found the file, now load and parse it. */ +/* -------------------------------------------------------------------- */ + return GDALLoadOziMapFile( pszOzi, padfGeoTransform, ppszWKT, + pnGCPCount, ppasGCPs ); +} + +/************************************************************************/ +/* GDALLoadTabFile() */ +/* */ +/* Helper function for translator implementators wanting */ +/* support for MapInfo .tab-files. */ +/************************************************************************/ + +#define MAX_GCP 256 + +int CPL_STDCALL GDALLoadTabFile( const char *pszFilename, + double *padfGeoTransform, char **ppszWKT, + int *pnGCPCount, GDAL_GCP **ppasGCPs ) + + +{ + char **papszLines; + char **papszTok=NULL; + int bTypeRasterFound = FALSE; + int bInsideTableDef = FALSE; + int iLine, numLines=0; + int nCoordinateCount = 0; + GDAL_GCP asGCPs[MAX_GCP]; + + papszLines = CSLLoad2( pszFilename, 1000, 200, NULL ); + + if ( !papszLines ) + return FALSE; + + numLines = CSLCount(papszLines); + + // Iterate all lines in the TAB-file + for(iLine=0; iLine 4 + && EQUAL(papszTok[4], "Label") + && nCoordinateCount < MAX_GCP ) + { + GDALInitGCPs( 1, asGCPs + nCoordinateCount ); + + asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]); + asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]); + asGCPs[nCoordinateCount].dfGCPX = CPLAtofM(papszTok[0]); + asGCPs[nCoordinateCount].dfGCPY = CPLAtofM(papszTok[1]); + if( papszTok[5] != NULL ) + { + CPLFree( asGCPs[nCoordinateCount].pszId ); + asGCPs[nCoordinateCount].pszId = CPLStrdup(papszTok[5]); + } + + nCoordinateCount++; + } + else if( bTypeRasterFound && bInsideTableDef + && EQUAL(papszTok[0],"CoordSys") + && ppszWKT != NULL ) + { + OGRSpatialReference oSRS; + + if( oSRS.importFromMICoordSys( papszLines[iLine] ) == OGRERR_NONE ) + oSRS.exportToWkt( ppszWKT ); + } + else if( EQUAL(papszTok[0],"Units") + && CSLCount(papszTok) > 1 + && EQUAL(papszTok[1],"degree") ) + { + /* + ** If we have units of "degree", but a projected coordinate + ** system we need to convert it to geographic. See to01_02.TAB. + */ + if( ppszWKT != NULL && *ppszWKT != NULL + && EQUALN(*ppszWKT,"PROJCS",6) ) + { + OGRSpatialReference oSRS, oSRSGeogCS; + char *pszSrcWKT = *ppszWKT; + + oSRS.importFromWkt( &pszSrcWKT ); + oSRSGeogCS.CopyGeogCSFrom( &oSRS ); + CPLFree( *ppszWKT ); + oSRSGeogCS.exportToWkt( ppszWKT ); + } + } + + } + + CSLDestroy(papszTok); + CSLDestroy(papszLines); + + if( nCoordinateCount == 0 ) + { + CPLDebug( "GDAL", "GDALLoadTabFile(%s) did not get any GCPs.", + pszFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try to convert the GCPs into a geotransform definition, if */ +/* possible. Otherwise we will need to use them as GCPs. */ +/* -------------------------------------------------------------------- */ + if( !GDALGCPsToGeoTransform( nCoordinateCount, asGCPs, padfGeoTransform, + CSLTestBoolean(CPLGetConfigOption("TAB_APPROX_GEOTRANSFORM", "NO")) ) ) + { + if (pnGCPCount && ppasGCPs) + { + CPLDebug( "GDAL", + "GDALLoadTabFile(%s) found file, wasn't able to derive a\n" + "first order geotransform. Using points as GCPs.", + pszFilename ); + + *ppasGCPs = (GDAL_GCP *) + CPLCalloc( sizeof(GDAL_GCP),nCoordinateCount ); + memcpy( *ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount ); + *pnGCPCount = nCoordinateCount; + } + } + else + { + GDALDeinitGCPs( nCoordinateCount, asGCPs ); + } + + return TRUE; +} + +#undef MAX_GCP + +/************************************************************************/ +/* GDALReadTabFile() */ +/* */ +/* Helper function for translator implementators wanting */ +/* support for MapInfo .tab-files. */ +/************************************************************************/ + +int CPL_STDCALL GDALReadTabFile( const char * pszBaseFilename, + double *padfGeoTransform, char **ppszWKT, + int *pnGCPCount, GDAL_GCP **ppasGCPs ) + + +{ + return GDALReadTabFile2(pszBaseFilename, padfGeoTransform, + ppszWKT, pnGCPCount, ppasGCPs, + NULL, NULL); +} + + +int GDALReadTabFile2( const char * pszBaseFilename, + double *padfGeoTransform, char **ppszWKT, + int *pnGCPCount, GDAL_GCP **ppasGCPs, + char** papszSiblingFiles, char** ppszTabFileNameOut ) +{ + const char *pszTAB; + VSILFILE *fpTAB; + + if (ppszTabFileNameOut) + *ppszTabFileNameOut = NULL; + + if( !GDALCanFileAcceptSidecarFile(pszBaseFilename) ) + return FALSE; + + pszTAB = CPLResetExtension( pszBaseFilename, "tab" ); + + if (papszSiblingFiles) + { + int iSibling = CSLFindString(papszSiblingFiles, CPLGetFilename(pszTAB)); + if (iSibling >= 0) + { + CPLString osTabFilename = pszBaseFilename; + osTabFilename.resize(strlen(pszBaseFilename) - + strlen(CPLGetFilename(pszBaseFilename))); + osTabFilename += papszSiblingFiles[iSibling]; + if ( GDALLoadTabFile(osTabFilename, padfGeoTransform, ppszWKT, + pnGCPCount, ppasGCPs ) ) + { + if (ppszTabFileNameOut) + *ppszTabFileNameOut = CPLStrdup(osTabFilename); + return TRUE; + } + } + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try lower case, then upper case. */ +/* -------------------------------------------------------------------- */ + + fpTAB = VSIFOpenL( pszTAB, "rt" ); + + if( fpTAB == NULL && VSIIsCaseSensitiveFS(pszTAB) ) + { + pszTAB = CPLResetExtension( pszBaseFilename, "TAB" ); + fpTAB = VSIFOpenL( pszTAB, "rt" ); + } + + if( fpTAB == NULL ) + return FALSE; + + VSIFCloseL( fpTAB ); + +/* -------------------------------------------------------------------- */ +/* We found the file, now load and parse it. */ +/* -------------------------------------------------------------------- */ + if (GDALLoadTabFile( pszTAB, padfGeoTransform, ppszWKT, + pnGCPCount, ppasGCPs ) ) + { + if (ppszTabFileNameOut) + *ppszTabFileNameOut = CPLStrdup(pszTAB); + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* GDALLoadWorldFile() */ +/************************************************************************/ + +/** + * \brief Read ESRI world file. + * + * This function reads an ESRI style world file, and formats a geotransform + * from its contents. + * + * The world file contains an affine transformation with the parameters + * in a different order than in a geotransform array. + * + *
      + *
    • geotransform[1] : width of pixel + *
    • geotransform[4] : rotational coefficient, zero for north up images. + *
    • geotransform[2] : rotational coefficient, zero for north up images. + *
    • geotransform[5] : height of pixel (but negative) + *
    • geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel. + *
    • geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel. + *
    + * + * @param pszFilename the world file name. + * @param padfGeoTransform the six double array into which the + * geotransformation should be placed. + * + * @return TRUE on success or FALSE on failure. + */ + +int CPL_STDCALL +GDALLoadWorldFile( const char *pszFilename, double *padfGeoTransform ) + +{ + char **papszLines; + + VALIDATE_POINTER1( pszFilename, "GDALLoadWorldFile", FALSE ); + VALIDATE_POINTER1( padfGeoTransform, "GDALLoadWorldFile", FALSE ); + + papszLines = CSLLoad2( pszFilename, 100, 100, NULL ); + + if ( !papszLines ) + return FALSE; + + double world[6]; + // reads the first 6 non-empty lines + int nLines = 0; + int nLinesCount = CSLCount(papszLines); + for( int i = 0; i < nLinesCount && nLines < 6; ++i ) + { + CPLString line(papszLines[i]); + if( line.Trim().empty() ) + continue; + + world[nLines] = CPLAtofM(line); + ++nLines; + } + + if( nLines == 6 + && (world[0] != 0.0 || world[2] != 0.0) + && (world[3] != 0.0 || world[1] != 0.0) ) + { + padfGeoTransform[0] = world[4]; + padfGeoTransform[1] = world[0]; + padfGeoTransform[2] = world[2]; + padfGeoTransform[3] = world[5]; + padfGeoTransform[4] = world[1]; + padfGeoTransform[5] = world[3]; + + // correct for center of pixel vs. top left of pixel + padfGeoTransform[0] -= 0.5 * padfGeoTransform[1]; + padfGeoTransform[0] -= 0.5 * padfGeoTransform[2]; + padfGeoTransform[3] -= 0.5 * padfGeoTransform[4]; + padfGeoTransform[3] -= 0.5 * padfGeoTransform[5]; + + CSLDestroy(papszLines); + + return TRUE; + } + else + { + CPLDebug( "GDAL", + "GDALLoadWorldFile(%s) found file, but it was corrupt.", + pszFilename ); + CSLDestroy(papszLines); + return FALSE; + } +} + +/************************************************************************/ +/* GDALReadWorldFile() */ +/************************************************************************/ + +/** + * \brief Read ESRI world file. + * + * This function reads an ESRI style world file, and formats a geotransform + * from its contents. It does the same as GDALLoadWorldFile() function, but + * it will form the filename for the worldfile from the filename of the raster + * file referred and the suggested extension. If no extension is provided, + * the code will internally try the unix style and windows style world file + * extensions (eg. for .tif these would be .tfw and .tifw). + * + * The world file contains an affine transformation with the parameters + * in a different order than in a geotransform array. + * + *
      + *
    • geotransform[1] : width of pixel + *
    • geotransform[4] : rotational coefficient, zero for north up images. + *
    • geotransform[2] : rotational coefficient, zero for north up images. + *
    • geotransform[5] : height of pixel (but negative) + *
    • geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel. + *
    • geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel. + *
    + * + * @param pszBaseFilename the target raster file. + * @param pszExtension the extension to use (ie. ".wld") or NULL to derive it + * from the pszBaseFilename + * @param padfGeoTransform the six double array into which the + * geotransformation should be placed. + * + * @return TRUE on success or FALSE on failure. + */ + +int CPL_STDCALL +GDALReadWorldFile( const char *pszBaseFilename, const char *pszExtension, + double *padfGeoTransform ) + +{ + return GDALReadWorldFile2(pszBaseFilename, pszExtension, + padfGeoTransform, NULL, NULL); +} + +int GDALReadWorldFile2( const char *pszBaseFilename, const char *pszExtension, + double *padfGeoTransform, char** papszSiblingFiles, + char** ppszWorldFileNameOut ) +{ + const char *pszTFW; + char szExtUpper[32], szExtLower[32]; + int i; + + VALIDATE_POINTER1( pszBaseFilename, "GDALReadWorldFile", FALSE ); + VALIDATE_POINTER1( padfGeoTransform, "GDALReadWorldFile", FALSE ); + + if (ppszWorldFileNameOut) + *ppszWorldFileNameOut = NULL; + + if( !GDALCanFileAcceptSidecarFile(pszBaseFilename) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* If we aren't given an extension, try both the unix and */ +/* windows style extensions. */ +/* -------------------------------------------------------------------- */ + if( pszExtension == NULL ) + { + char szDerivedExtension[100]; + std::string oBaseExt = CPLGetExtension( pszBaseFilename ); + + if( oBaseExt.length() < 2 ) + return FALSE; + + // windows version - first + last + 'w' + szDerivedExtension[0] = oBaseExt[0]; + szDerivedExtension[1] = oBaseExt[oBaseExt.length()-1]; + szDerivedExtension[2] = 'w'; + szDerivedExtension[3] = '\0'; + + if( GDALReadWorldFile2( pszBaseFilename, szDerivedExtension, + padfGeoTransform, papszSiblingFiles, + ppszWorldFileNameOut ) ) + return TRUE; + + // unix version - extension + 'w' + if( oBaseExt.length() > sizeof(szDerivedExtension)-2 ) + return FALSE; + + strcpy( szDerivedExtension, oBaseExt.c_str() ); + strcat( szDerivedExtension, "w" ); + return GDALReadWorldFile2( pszBaseFilename, szDerivedExtension, + padfGeoTransform, papszSiblingFiles, + ppszWorldFileNameOut ); + } + +/* -------------------------------------------------------------------- */ +/* Skip the leading period in the extension if there is one. */ +/* -------------------------------------------------------------------- */ + if( *pszExtension == '.' ) + pszExtension++; + +/* -------------------------------------------------------------------- */ +/* Generate upper and lower case versions of the extension. */ +/* -------------------------------------------------------------------- */ + CPLStrlcpy( szExtUpper, pszExtension, sizeof(szExtUpper) ); + CPLStrlcpy( szExtLower, pszExtension, sizeof(szExtLower) ); + + for( i = 0; szExtUpper[i] != '\0'; i++ ) + { + szExtUpper[i] = (char) toupper(szExtUpper[i]); + szExtLower[i] = (char) tolower(szExtLower[i]); + } + + VSIStatBufL sStatBuf; + int bGotTFW; + + pszTFW = CPLResetExtension( pszBaseFilename, szExtLower ); + + if (papszSiblingFiles) + { + int iSibling = CSLFindString(papszSiblingFiles, CPLGetFilename(pszTFW)); + if (iSibling >= 0) + { + CPLString osTFWFilename = pszBaseFilename; + osTFWFilename.resize(strlen(pszBaseFilename) - + strlen(CPLGetFilename(pszBaseFilename))); + osTFWFilename += papszSiblingFiles[iSibling]; + if (GDALLoadWorldFile( osTFWFilename, padfGeoTransform )) + { + if (ppszWorldFileNameOut) + *ppszWorldFileNameOut = CPLStrdup(osTFWFilename); + return TRUE; + } + } + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try lower case, then upper case. */ +/* -------------------------------------------------------------------- */ + + bGotTFW = VSIStatExL( pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0; + + if( !bGotTFW && VSIIsCaseSensitiveFS(pszTFW) ) + { + pszTFW = CPLResetExtension( pszBaseFilename, szExtUpper ); + bGotTFW = VSIStatExL( pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0; + } + + if( !bGotTFW ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* We found the file, now load and parse it. */ +/* -------------------------------------------------------------------- */ + if (GDALLoadWorldFile( pszTFW, padfGeoTransform )) + { + if (ppszWorldFileNameOut) + *ppszWorldFileNameOut = CPLStrdup(pszTFW); + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* GDALWriteWorldFile() */ +/* */ +/* Helper function for translator implementators wanting */ +/* support for ESRI world files. */ +/************************************************************************/ + +/** + * \brief Write ESRI world file. + * + * This function writes an ESRI style world file from the passed geotransform. + * + * The world file contains an affine transformation with the parameters + * in a different order than in a geotransform array. + * + *
      + *
    • geotransform[1] : width of pixel + *
    • geotransform[4] : rotational coefficient, zero for north up images. + *
    • geotransform[2] : rotational coefficient, zero for north up images. + *
    • geotransform[5] : height of pixel (but negative) + *
    • geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel. + *
    • geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel. + *
    + * + * @param pszBaseFilename the target raster file. + * @param pszExtension the extension to use (ie. ".wld"). Must not be NULL + * @param padfGeoTransform the six double array from which the + * geotransformation should be read. + * + * @return TRUE on success or FALSE on failure. + */ + +int CPL_STDCALL +GDALWriteWorldFile( const char * pszBaseFilename, const char *pszExtension, + double *padfGeoTransform ) + +{ + VALIDATE_POINTER1( pszBaseFilename, "GDALWriteWorldFile", FALSE ); + VALIDATE_POINTER1( pszExtension, "GDALWriteWorldFile", FALSE ); + VALIDATE_POINTER1( padfGeoTransform, "GDALWriteWorldFile", FALSE ); + +/* -------------------------------------------------------------------- */ +/* Prepare the text to write to the file. */ +/* -------------------------------------------------------------------- */ + CPLString osTFWText; + + osTFWText.Printf( "%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n", + padfGeoTransform[1], + padfGeoTransform[4], + padfGeoTransform[2], + padfGeoTransform[5], + padfGeoTransform[0] + + 0.5 * padfGeoTransform[1] + + 0.5 * padfGeoTransform[2], + padfGeoTransform[3] + + 0.5 * padfGeoTransform[4] + + 0.5 * padfGeoTransform[5] ); + +/* -------------------------------------------------------------------- */ +/* Update extention, and write to disk. */ +/* -------------------------------------------------------------------- */ + const char *pszTFW; + VSILFILE *fpTFW; + + pszTFW = CPLResetExtension( pszBaseFilename, pszExtension ); + fpTFW = VSIFOpenL( pszTFW, "wt" ); + if( fpTFW == NULL ) + return FALSE; + + VSIFWriteL( (void *) osTFWText.c_str(), 1, osTFWText.size(), fpTFW ); + VSIFCloseL( fpTFW ); + + return TRUE; +} + +/************************************************************************/ +/* GDALVersionInfo() */ +/************************************************************************/ + +/** + * \brief Get runtime version information. + * + * Available pszRequest values: + *
      + *
    • "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string. ie. "1170" + * Note: starting with GDAL 1.10, this string will be longer than 4 characters. + *
    • "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a string. + * ie. "20020416". + *
    • "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "1.1.7" + *
    • "--version": Returns one line version message suitable for use in + * response to --version requests. ie. "GDAL 1.1.7, released 2002/04/16" + *
    • "LICENSE": Returns the content of the LICENSE.TXT file from the GDAL_DATA directory. + * Before GDAL 1.7.0, the returned string was leaking memory but this is now resolved. + * So the result should not been freed by the caller. + *
    • "BUILD_INFO": List of NAME=VALUE pairs separated by newlines with + * information on build time options. + *
    + * + * @param pszRequest the type of version info desired, as listed above. + * + * @return an internal string containing the requested information. + */ + +const char * CPL_STDCALL GDALVersionInfo( const char *pszRequest ) + +{ +/* -------------------------------------------------------------------- */ +/* Try to capture as much build information as practical. */ +/* -------------------------------------------------------------------- */ + if( pszRequest != NULL && EQUAL(pszRequest,"BUILD_INFO") ) + { + CPLString osBuildInfo; + +#ifdef ESRI_BUILD + osBuildInfo += "ESRI_BUILD=YES\n"; +#endif +#ifdef PAM_ENABLED + osBuildInfo += "PAM_ENABLED=YES\n"; +#endif +#ifdef OGR_ENABLED + osBuildInfo += "OGR_ENABLED=YES\n"; +#endif + + CPLFree(CPLGetTLS(CTLS_VERSIONINFO)); + CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE ); + return (char *) CPLGetTLS(CTLS_VERSIONINFO); + } + +/* -------------------------------------------------------------------- */ +/* LICENSE is a special case. We try to find and read the */ +/* LICENSE.TXT file from the GDAL_DATA directory and return it */ +/* -------------------------------------------------------------------- */ + if( pszRequest != NULL && EQUAL(pszRequest,"LICENSE") ) + { + char* pszResultLicence = (char*) CPLGetTLS( CTLS_VERSIONINFO_LICENCE ); + if( pszResultLicence != NULL ) + { + return pszResultLicence; + } + + const char *pszFilename = CPLFindFile( "etc", "LICENSE.TXT" ); + VSILFILE *fp = NULL; + int nLength; + + if( pszFilename != NULL ) + fp = VSIFOpenL( pszFilename, "r" ); + + if( fp != NULL ) + { + VSIFSeekL( fp, 0, SEEK_END ); + nLength = (int) VSIFTellL( fp ) + 1; + VSIFSeekL( fp, SEEK_SET, 0 ); + + pszResultLicence = (char *) VSICalloc(1,nLength); + if (pszResultLicence) + VSIFReadL( pszResultLicence, 1, nLength-1, fp ); + + VSIFCloseL( fp ); + } + + if (!pszResultLicence) + { + pszResultLicence = CPLStrdup( + "GDAL/OGR is released under the MIT/X license.\n" + "The LICENSE.TXT distributed with GDAL/OGR should\n" + "contain additional details.\n" ); + } + + CPLSetTLS( CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE ); + return pszResultLicence; + } + +/* -------------------------------------------------------------------- */ +/* All other strings are fairly small. */ +/* -------------------------------------------------------------------- */ + CPLString osVersionInfo; + + if( pszRequest == NULL || EQUAL(pszRequest,"VERSION_NUM") ) + osVersionInfo.Printf( "%d", GDAL_VERSION_NUM ); + else if( EQUAL(pszRequest,"RELEASE_DATE") ) + osVersionInfo.Printf( "%d", GDAL_RELEASE_DATE ); + else if( EQUAL(pszRequest,"RELEASE_NAME") ) + osVersionInfo.Printf( GDAL_RELEASE_NAME ); + else // --version + osVersionInfo.Printf( "GDAL %s, released %d/%02d/%02d", + GDAL_RELEASE_NAME, + GDAL_RELEASE_DATE / 10000, + (GDAL_RELEASE_DATE % 10000) / 100, + GDAL_RELEASE_DATE % 100 ); + + CPLFree(CPLGetTLS(CTLS_VERSIONINFO)); // clear old value. + CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE ); + return (char *) CPLGetTLS(CTLS_VERSIONINFO); +} + +/************************************************************************/ +/* GDALCheckVersion() */ +/************************************************************************/ + +/** Return TRUE if GDAL library version at runtime matches nVersionMajor.nVersionMinor. + + The purpose of this method is to ensure that calling code will run with the GDAL + version it is compiled for. It is primarly intented for external plugins. + + @param nVersionMajor Major version to be tested against + @param nVersionMinor Minor version to be tested against + @param pszCallingComponentName If not NULL, in case of version mismatch, the method + will issue a failure mentionning the name of + the calling component. + + @return TRUE if GDAL library version at runtime matches nVersionMajor.nVersionMinor, FALSE otherwise. + */ +int CPL_STDCALL GDALCheckVersion( int nVersionMajor, int nVersionMinor, + const char* pszCallingComponentName) +{ + if (nVersionMajor == GDAL_VERSION_MAJOR && + nVersionMinor == GDAL_VERSION_MINOR) + return TRUE; + + if (pszCallingComponentName) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s was compiled against GDAL %d.%d but current library version is %d.%d\n", + pszCallingComponentName, nVersionMajor, nVersionMinor, + GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR); + } + return FALSE; +} + +/************************************************************************/ +/* GDALDecToDMS() */ +/* */ +/* Translate a decimal degrees value to a DMS string with */ +/* hemisphere. */ +/************************************************************************/ + +const char * CPL_STDCALL GDALDecToDMS( double dfAngle, const char * pszAxis, + int nPrecision ) + +{ + return CPLDecToDMS( dfAngle, pszAxis, nPrecision ); +} + +/************************************************************************/ +/* GDALPackedDMSToDec() */ +/************************************************************************/ + +/** + * \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees. + * + * See CPLPackedDMSToDec(). + */ + +double CPL_STDCALL GDALPackedDMSToDec( double dfPacked ) + +{ + return CPLPackedDMSToDec( dfPacked ); +} + +/************************************************************************/ +/* GDALDecToPackedDMS() */ +/************************************************************************/ + +/** + * \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS). + * + * See CPLDecToPackedDMS(). + */ + +double CPL_STDCALL GDALDecToPackedDMS( double dfDec ) + +{ + return CPLDecToPackedDMS( dfDec ); +} + +/************************************************************************/ +/* GDALGCPsToGeoTransform() */ +/************************************************************************/ + +/** + * \brief Generate Geotransform from GCPs. + * + * Given a set of GCPs perform first order fit as a geotransform. + * + * Due to imprecision in the calculations the fit algorithm will often + * return non-zero rotational coefficients even if given perfectly non-rotated + * inputs. A special case has been implemented for corner corner coordinates + * given in TL, TR, BR, BL order. So when using this to get a geotransform + * from 4 corner coordinates, pass them in this order. + * + * @param nGCPCount the number of GCPs being passed in. + * @param pasGCPs the list of GCP structures. + * @param padfGeoTransform the six double array in which the affine + * geotransformation will be returned. + * @param bApproxOK If FALSE the function will fail if the geotransform is not + * essentially an exact fit (within 0.25 pixel) for all GCPs. + * + * @return TRUE on success or FALSE if there aren't enough points to prepare a + * geotransform, the pointers are ill-determined or if bApproxOK is FALSE + * and the fit is poor. + */ + +int CPL_STDCALL +GDALGCPsToGeoTransform( int nGCPCount, const GDAL_GCP *pasGCPs, + double *padfGeoTransform, int bApproxOK ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Recognise a few special cases. */ +/* -------------------------------------------------------------------- */ + if( nGCPCount < 2 ) + return FALSE; + + if( nGCPCount == 2 ) + { + if( pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel + || pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine ) + return FALSE; + + padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) + / (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel); + padfGeoTransform[2] = 0.0; + + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY) + / (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine); + + padfGeoTransform[0] = pasGCPs[0].dfGCPX + - pasGCPs[0].dfGCPPixel * padfGeoTransform[1] + - pasGCPs[0].dfGCPLine * padfGeoTransform[2]; + + padfGeoTransform[3] = pasGCPs[0].dfGCPY + - pasGCPs[0].dfGCPPixel * padfGeoTransform[4] + - pasGCPs[0].dfGCPLine * padfGeoTransform[5]; + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Special case of 4 corner coordinates of a non-rotated */ +/* image. The points must be in TL-TR-BR-BL order for now. */ +/* This case helps avoid some imprecision in the general */ +/* calcuations. */ +/* -------------------------------------------------------------------- */ + if( nGCPCount == 4 + && pasGCPs[0].dfGCPLine == pasGCPs[1].dfGCPLine + && pasGCPs[2].dfGCPLine == pasGCPs[3].dfGCPLine + && pasGCPs[0].dfGCPPixel == pasGCPs[3].dfGCPPixel + && pasGCPs[1].dfGCPPixel == pasGCPs[2].dfGCPPixel + && pasGCPs[0].dfGCPLine != pasGCPs[2].dfGCPLine + && pasGCPs[0].dfGCPPixel != pasGCPs[1].dfGCPPixel + && pasGCPs[0].dfGCPY == pasGCPs[1].dfGCPY + && pasGCPs[2].dfGCPY == pasGCPs[3].dfGCPY + && pasGCPs[0].dfGCPX == pasGCPs[3].dfGCPX + && pasGCPs[1].dfGCPX == pasGCPs[2].dfGCPX + && pasGCPs[0].dfGCPY != pasGCPs[2].dfGCPY + && pasGCPs[0].dfGCPX != pasGCPs[1].dfGCPX ) + { + padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) + / (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel); + padfGeoTransform[2] = 0.0; + padfGeoTransform[4] = 0.0; + padfGeoTransform[5] = (pasGCPs[2].dfGCPY - pasGCPs[1].dfGCPY) + / (pasGCPs[2].dfGCPLine - pasGCPs[1].dfGCPLine); + + padfGeoTransform[0] = + pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1]; + padfGeoTransform[3] = + pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5]; + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Compute source and destination ranges so we can normalize */ +/* the values to make the least squares computation more stable. */ +/* -------------------------------------------------------------------- */ + double min_pixel = pasGCPs[0].dfGCPPixel; + double max_pixel = pasGCPs[0].dfGCPPixel; + double min_line = pasGCPs[0].dfGCPLine; + double max_line = pasGCPs[0].dfGCPLine; + double min_geox = pasGCPs[0].dfGCPX; + double max_geox = pasGCPs[0].dfGCPX; + double min_geoy = pasGCPs[0].dfGCPY; + double max_geoy = pasGCPs[0].dfGCPY; + + for (i = 1; i < nGCPCount; ++i) { + min_pixel = MIN(min_pixel, pasGCPs[i].dfGCPPixel); + max_pixel = MAX(max_pixel, pasGCPs[i].dfGCPPixel); + min_line = MIN(min_line, pasGCPs[i].dfGCPLine); + max_line = MAX(max_line, pasGCPs[i].dfGCPLine); + min_geox = MIN(min_geox, pasGCPs[i].dfGCPX); + max_geox = MAX(max_geox, pasGCPs[i].dfGCPX); + min_geoy = MIN(min_geoy, pasGCPs[i].dfGCPY); + max_geoy = MAX(max_geoy, pasGCPs[i].dfGCPY); + } + + double EPS = 1.0e-12; + + if( ABS(max_pixel - min_pixel) < EPS + || ABS(max_line - min_line) < EPS + || ABS(max_geox - min_geox) < EPS + || ABS(max_geoy - min_geoy) < EPS) + { + return FALSE; // degenerate in at least one dimension. + } + + double pl_normalize[6], geo_normalize[6]; + + pl_normalize[0] = -min_pixel / (max_pixel - min_pixel); + pl_normalize[1] = 1.0 / (max_pixel - min_pixel); + pl_normalize[2] = 0.0; + pl_normalize[3] = -min_line / (max_line - min_line); + pl_normalize[4] = 0.0; + pl_normalize[5] = 1.0 / (max_line - min_line); + + geo_normalize[0] = -min_geox / (max_geox - min_geox); + geo_normalize[1] = 1.0 / (max_geox - min_geox); + geo_normalize[2] = 0.0; + geo_normalize[3] = -min_geoy / (max_geoy - min_geoy); + geo_normalize[4] = 0.0; + geo_normalize[5] = 1.0 / (max_geoy - min_geoy); + +/* -------------------------------------------------------------------- */ +/* In the general case, do a least squares error approximation by */ +/* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum */ +/* -------------------------------------------------------------------- */ + + double sum_x = 0.0, sum_y = 0.0, sum_xy = 0.0, sum_xx = 0.0, sum_yy = 0.0; + double sum_Lon = 0.0, sum_Lonx = 0.0, sum_Lony = 0.0; + double sum_Lat = 0.0, sum_Latx = 0.0, sum_Laty = 0.0; + double divisor; + + for (i = 0; i < nGCPCount; ++i) { + double pixel, line, geox, geoy; + + GDALApplyGeoTransform(pl_normalize, + pasGCPs[i].dfGCPPixel, + pasGCPs[i].dfGCPLine, + &pixel, &line); + GDALApplyGeoTransform(geo_normalize, + pasGCPs[i].dfGCPX, + pasGCPs[i].dfGCPY, + &geox, &geoy); + + sum_x += pixel; + sum_y += line; + sum_xy += pixel * line; + sum_xx += pixel * pixel; + sum_yy += line * line; + sum_Lon += geox; + sum_Lonx += geox * pixel; + sum_Lony += geox * line; + sum_Lat += geoy; + sum_Latx += geoy * pixel; + sum_Laty += geoy * line; + } + + divisor = nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy) + + 2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx + - sum_x * sum_x * sum_yy; + +/* -------------------------------------------------------------------- */ +/* If the divisor is zero, there is no valid solution. */ +/* -------------------------------------------------------------------- */ + if (divisor == 0.0) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Compute top/left origin. */ +/* -------------------------------------------------------------------- */ + double gt_normalized[6]; + gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy) + + sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy) + + sum_Lony * (sum_x * sum_xy - sum_y * sum_xx)) + / divisor; + + gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy) + + sum_Latx * (sum_y * sum_xy - sum_x * sum_yy) + + sum_Laty * (sum_x * sum_xy - sum_y * sum_xx)) + / divisor; + +/* -------------------------------------------------------------------- */ +/* Compute X related coefficients. */ +/* -------------------------------------------------------------------- */ + gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy) + + sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y) + + sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount)) + / divisor; + + gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx) + + sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy) + + sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x)) + / divisor; + +/* -------------------------------------------------------------------- */ +/* Compute Y related coefficients. */ +/* -------------------------------------------------------------------- */ + gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy) + + sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y) + + sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount)) + / divisor; + + gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx) + + sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy) + + sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x)) + / divisor; + +/* -------------------------------------------------------------------- */ +/* Compose the resulting transformation with the normalization */ +/* geotransformations. */ +/* -------------------------------------------------------------------- */ + double gt1p2[6], inv_geo_normalize[6]; + if( !GDALInvGeoTransform(geo_normalize, inv_geo_normalize)) + return FALSE; + + GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2); + GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform); + +/* -------------------------------------------------------------------- */ +/* Now check if any of the input points fit this poorly. */ +/* -------------------------------------------------------------------- */ + if( !bApproxOK ) + { + // FIXME? Not sure if it is the more accurate way of computing + // pixel size + double dfPixelSize = 0.5 * (ABS(padfGeoTransform[1]) + + ABS(padfGeoTransform[2]) + + ABS(padfGeoTransform[4]) + + ABS(padfGeoTransform[5])); + + for( i = 0; i < nGCPCount; i++ ) + { + double dfErrorX, dfErrorY; + + dfErrorX = + (pasGCPs[i].dfGCPPixel * padfGeoTransform[1] + + pasGCPs[i].dfGCPLine * padfGeoTransform[2] + + padfGeoTransform[0]) + - pasGCPs[i].dfGCPX; + dfErrorY = + (pasGCPs[i].dfGCPPixel * padfGeoTransform[4] + + pasGCPs[i].dfGCPLine * padfGeoTransform[5] + + padfGeoTransform[3]) + - pasGCPs[i].dfGCPY; + + if( ABS(dfErrorX) > 0.25 * dfPixelSize + || ABS(dfErrorY) > 0.25 * dfPixelSize ) + { + CPLDebug("GDAL", "dfErrorX/dfPixelSize = %.2f, dfErrorY/dfPixelSize = %.2f", + ABS(dfErrorX)/dfPixelSize, ABS(dfErrorY)/dfPixelSize); + return FALSE; + } + } + } + + return TRUE; +} + +/************************************************************************/ +/* GDALComposeGeoTransforms() */ +/************************************************************************/ + +/** + * \brief Compose two geotransforms. + * + * The resulting geotransform is the equivelent to padfGT1 and then padfGT2 + * being applied to a point. + * + * @param padfGT1 the first geotransform, six values. + * @param padfGT2 the second geotransform, six values. + * @param padfGTOut the output geotransform, six values, may safely be the same + * array as padfGT1 or padfGT2. + */ + +void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2, + double *padfGTOut) + +{ + double gtwrk[6]; + // We need to think of the geotransform in a more normal form to do + // the matrix multiple: + // + // __ __ + // | gt[1] gt[2] gt[0] | + // | gt[4] gt[5] gt[3] | + // | 0.0 0.0 1.0 | + // -- -- + // + // Then we can use normal matrix multiplication to produce the + // composed transformation. I don't actually reform the matrix + // explicitly which is why the following may seem kind of spagettish. + + gtwrk[1] = + padfGT2[1] * padfGT1[1] + + padfGT2[2] * padfGT1[4]; + gtwrk[2] = + padfGT2[1] * padfGT1[2] + + padfGT2[2] * padfGT1[5]; + gtwrk[0] = + padfGT2[1] * padfGT1[0] + + padfGT2[2] * padfGT1[3] + + padfGT2[0] * 1.0; + + gtwrk[4] = + padfGT2[4] * padfGT1[1] + + padfGT2[5] * padfGT1[4]; + gtwrk[5] = + padfGT2[4] * padfGT1[2] + + padfGT2[5] * padfGT1[5]; + gtwrk[3] = + padfGT2[4] * padfGT1[0] + + padfGT2[5] * padfGT1[3] + + padfGT2[3] * 1.0; + memcpy(padfGTOut, gtwrk, sizeof(double) * 6); +} + +/************************************************************************/ +/* GDALGeneralCmdLineProcessor() */ +/************************************************************************/ + +/** + * \brief General utility option processing. + * + * This function is intended to provide a variety of generic commandline + * options for all GDAL commandline utilities. It takes care of the following + * commandline options: + * + * --version: report version of GDAL in use. + * --build: report build info about GDAL in use. + * --license: report GDAL license info. + * --formats: report all format drivers configured. + * --format [format]: report details of one format driver. + * --optfile filename: expand an option file into the argument list. + * --config key value: set system configuration option. + * --debug [on/off/value]: set debug level. + * --mempreload dir: preload directory contents into /vsimem + * --pause: Pause for user input (allows time to attach debugger) + * --locale [locale]: Install a locale using setlocale() (debugging) + * --help-general: report detailed help on general options. + * + * The argument array is replaced "in place" and should be freed with + * CSLDestroy() when no longer needed. The typical usage looks something + * like the following. Note that the formats should be registered so that + * the --formats and --format options will work properly. + * + * int main( int argc, char ** argv ) + * { + * GDALAllRegister(); + * + * argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 ); + * if( argc < 1 ) + * exit( -argc ); + * + * @param nArgc number of values in the argument list. + * @param ppapszArgv pointer to the argument list array (will be updated in place). + * @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR + * to determine which drivers should be displayed by --formats. + * If set to 0, GDAL_OF_RASTER is assumed. + * + * @return updated nArgc argument count. Return of 0 requests terminate + * without error, return of -1 requests exit with error code. + */ + +int CPL_STDCALL +GDALGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv, int nOptions ) + +{ + char **papszReturn = NULL; + int iArg; + char **papszArgv = *ppapszArgv; + +/* -------------------------------------------------------------------- */ +/* Preserve the program name. */ +/* -------------------------------------------------------------------- */ + papszReturn = CSLAddString( papszReturn, papszArgv[0] ); + +/* ==================================================================== */ +/* Loop over all arguments. */ +/* ==================================================================== */ + for( iArg = 1; iArg < nArgc; iArg++ ) + { +/* -------------------------------------------------------------------- */ +/* --version */ +/* -------------------------------------------------------------------- */ + if( EQUAL(papszArgv[iArg],"--version") ) + { + printf( "%s\n", GDALVersionInfo( "--version" ) ); + CSLDestroy( papszReturn ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* --build */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--build") ) + { + printf( "%s", GDALVersionInfo( "BUILD_INFO" ) ); + CSLDestroy( papszReturn ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* --license */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--license") ) + { + printf( "%s\n", GDALVersionInfo( "LICENSE" ) ); + CSLDestroy( papszReturn ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* --config */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--config") ) + { + if( iArg + 2 >= nArgc ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--config option given without a key and value argument." ); + CSLDestroy( papszReturn ); + return -1; + } + + CPLSetConfigOption( papszArgv[iArg+1], papszArgv[iArg+2] ); + + iArg += 2; + } + +/* -------------------------------------------------------------------- */ +/* --mempreload */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--mempreload") ) + { + int i; + + if( iArg + 1 >= nArgc ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--mempreload option given without directory path."); + CSLDestroy( papszReturn ); + return -1; + } + + char **papszFiles = CPLReadDir( papszArgv[iArg+1] ); + if( CSLCount(papszFiles) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--mempreload given invalid or empty directory."); + CSLDestroy( papszReturn ); + return -1; + } + + for( i = 0; papszFiles[i] != NULL; i++ ) + { + CPLString osOldPath, osNewPath; + VSIStatBufL sStatBuf; + + if( EQUAL(papszFiles[i],".") || EQUAL(papszFiles[i],"..") ) + continue; + + osOldPath = CPLFormFilename( papszArgv[iArg+1], + papszFiles[i], NULL ); + osNewPath.Printf( "/vsimem/%s", papszFiles[i] ); + + if( VSIStatL( osOldPath, &sStatBuf ) != 0 + || VSI_ISDIR( sStatBuf.st_mode ) ) + { + CPLDebug( "VSI", "Skipping preload of %s.", + osOldPath.c_str() ); + continue; + } + + CPLDebug( "VSI", "Preloading %s to %s.", + osOldPath.c_str(), osNewPath.c_str() ); + + if( CPLCopyFile( osNewPath, osOldPath ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to copy %s to /vsimem", + osOldPath.c_str() ); + return -1; + } + } + + CSLDestroy( papszFiles ); + iArg += 1; + } + +/* -------------------------------------------------------------------- */ +/* --debug */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--debug") ) + { + if( iArg + 1 >= nArgc ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--debug option given without debug level." ); + CSLDestroy( papszReturn ); + return -1; + } + + CPLSetConfigOption( "CPL_DEBUG", papszArgv[iArg+1] ); + iArg += 1; + } + +/* -------------------------------------------------------------------- */ +/* --optfile */ +/* */ +/* Annoyingly the options inserted by --optfile will *not* be */ +/* processed properly if they are general options. */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--optfile") ) + { + const char *pszLine; + FILE *fpOptFile; + + if( iArg + 1 >= nArgc ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--optfile option given without filename." ); + CSLDestroy( papszReturn ); + return -1; + } + + fpOptFile = VSIFOpen( papszArgv[iArg+1], "rb" ); + + if( fpOptFile == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open optfile '%s'.\n%s", + papszArgv[iArg+1], VSIStrerror( errno ) ); + CSLDestroy( papszReturn ); + return -1; + } + + while( (pszLine = CPLReadLine( fpOptFile )) != NULL ) + { + char **papszTokens; + int i; + + if( pszLine[0] == '#' || strlen(pszLine) == 0 ) + continue; + + papszTokens = CSLTokenizeString( pszLine ); + for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; i++) + papszReturn = CSLAddString( papszReturn, papszTokens[i] ); + CSLDestroy( papszTokens ); + } + + VSIFClose( fpOptFile ); + + iArg += 1; + } + +/* -------------------------------------------------------------------- */ +/* --formats */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg], "--formats") ) + { + int iDr; + + if( nOptions == 0 ) + nOptions = GDAL_OF_RASTER; + + printf( "Supported Formats:\n" ); + for( iDr = 0; iDr < GDALGetDriverCount(); iDr++ ) + { + GDALDriverH hDriver = GDALGetDriver(iDr); + + const char *pszRFlag = "", *pszWFlag, *pszVirtualIO, *pszSubdatasets, *pszKind; + char** papszMD = GDALGetMetadata( hDriver, NULL ); + + if( nOptions == GDAL_OF_RASTER && + !CSLFetchBoolean( papszMD, GDAL_DCAP_RASTER, FALSE ) ) + continue; + if( nOptions == GDAL_OF_VECTOR && + !CSLFetchBoolean( papszMD, GDAL_DCAP_VECTOR, FALSE ) ) + continue; + + if( CSLFetchBoolean( papszMD, GDAL_DCAP_OPEN, FALSE ) ) + pszRFlag = "r"; + + if( CSLFetchBoolean( papszMD, GDAL_DCAP_CREATE, FALSE ) ) + pszWFlag = "w+"; + else if( CSLFetchBoolean( papszMD, GDAL_DCAP_CREATECOPY, FALSE ) ) + pszWFlag = "w"; + else + pszWFlag = "o"; + + if( CSLFetchBoolean( papszMD, GDAL_DCAP_VIRTUALIO, FALSE ) ) + pszVirtualIO = "v"; + else + pszVirtualIO = ""; + + if( CSLFetchBoolean( papszMD, GDAL_DMD_SUBDATASETS, FALSE ) ) + pszSubdatasets = "s"; + else + pszSubdatasets = ""; + + if( CSLFetchBoolean( papszMD, GDAL_DCAP_RASTER, FALSE ) && + CSLFetchBoolean( papszMD, GDAL_DCAP_VECTOR, FALSE )) + pszKind = "raster,vector"; + else if( CSLFetchBoolean( papszMD, GDAL_DCAP_RASTER, FALSE ) ) + pszKind = "raster"; + else if( CSLFetchBoolean( papszMD, GDAL_DCAP_VECTOR, FALSE ) ) + pszKind = "vector"; + else + pszKind = "unknown kind"; + + printf( " %s -%s- (%s%s%s%s): %s\n", + GDALGetDriverShortName( hDriver ), + pszKind, + pszRFlag, pszWFlag, pszVirtualIO, pszSubdatasets, + GDALGetDriverLongName( hDriver ) ); + } + + CSLDestroy( papszReturn ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* --format */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg], "--format") ) + { + GDALDriverH hDriver; + char **papszMD; + + CSLDestroy( papszReturn ); + + if( iArg + 1 >= nArgc ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--format option given without a format code." ); + return -1; + } + + hDriver = GDALGetDriverByName( papszArgv[iArg+1] ); + if( hDriver == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "--format option given with format '%s', but that format not\n" + "recognised. Use the --formats option to get a list of available formats,\n" + "and use the short code (ie. GTiff or HFA) as the format identifier.\n", + papszArgv[iArg+1] ); + return -1; + } + + printf( "Format Details:\n" ); + printf( " Short Name: %s\n", GDALGetDriverShortName( hDriver ) ); + printf( " Long Name: %s\n", GDALGetDriverLongName( hDriver ) ); + + papszMD = GDALGetMetadata( hDriver, NULL ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_RASTER, FALSE ) ) + printf( " Supports: Raster\n" ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_VECTOR, FALSE ) ) + printf( " Supports: Vector\n" ); + + const char* pszExt = CSLFetchNameValue( papszMD, GDAL_DMD_EXTENSIONS ); + if( pszExt != NULL ) + printf( " Extension%s: %s\n", (strchr(pszExt, ' ') ? "s" : ""), + pszExt ); + + if( CSLFetchNameValue( papszMD, GDAL_DMD_MIMETYPE ) ) + printf( " Mime Type: %s\n", + CSLFetchNameValue( papszMD, GDAL_DMD_MIMETYPE ) ); + if( CSLFetchNameValue( papszMD, GDAL_DMD_HELPTOPIC ) ) + printf( " Help Topic: %s\n", + CSLFetchNameValue( papszMD, GDAL_DMD_HELPTOPIC ) ); + + if( CSLFetchBoolean( papszMD, GDAL_DMD_SUBDATASETS, FALSE ) ) + printf( " Supports: Subdatasets\n" ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_OPEN, FALSE ) ) + printf( " Supports: Open() - Open existing dataset.\n" ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_CREATE, FALSE ) ) + printf( " Supports: Create() - Create writeable dataset.\n" ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_CREATECOPY, FALSE ) ) + printf( " Supports: CreateCopy() - Create dataset by copying another.\n" ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_VIRTUALIO, FALSE ) ) + printf( " Supports: Virtual IO - eg. /vsimem/\n" ); + if( CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONDATATYPES ) ) + printf( " Creation Datatypes: %s\n", + CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONDATATYPES ) ); + if( CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONFIELDDATATYPES ) ) + printf( " Creation Field Datatypes: %s\n", + CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONFIELDDATATYPES ) ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_NOTNULL_FIELDS, FALSE ) ) + printf( " Supports: Creating fields with NOT NULL constraint.\n" ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_DEFAULT_FIELDS, FALSE ) ) + printf( " Supports: Creating fields with DEFAULT values.\n" ); + if( CSLFetchBoolean( papszMD, GDAL_DCAP_NOTNULL_GEOMFIELDS, FALSE ) ) + printf( " Supports: Creating geometry fields with NOT NULL constraint.\n" ); + if( CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONOPTIONLIST ) ) + { + CPLXMLNode *psCOL = + CPLParseXMLString( + CSLFetchNameValue( papszMD, + GDAL_DMD_CREATIONOPTIONLIST ) ); + char *pszFormattedXML = + CPLSerializeXMLTree( psCOL ); + + CPLDestroyXMLNode( psCOL ); + + printf( "\n%s\n", pszFormattedXML ); + CPLFree( pszFormattedXML ); + } + if( CSLFetchNameValue( papszMD, GDAL_DS_LAYER_CREATIONOPTIONLIST ) ) + { + CPLXMLNode *psCOL = + CPLParseXMLString( + CSLFetchNameValue( papszMD, + GDAL_DS_LAYER_CREATIONOPTIONLIST ) ); + char *pszFormattedXML = + CPLSerializeXMLTree( psCOL ); + + CPLDestroyXMLNode( psCOL ); + + printf( "\n%s\n", pszFormattedXML ); + CPLFree( pszFormattedXML ); + } + + if( CSLFetchNameValue( papszMD, GDAL_DMD_CONNECTION_PREFIX ) ) + printf( " Connection prefix: %s\n", + CSLFetchNameValue( papszMD, GDAL_DMD_CONNECTION_PREFIX ) ); + + if( CSLFetchNameValue( papszMD, GDAL_DMD_OPENOPTIONLIST ) ) + { + CPLXMLNode *psCOL = + CPLParseXMLString( + CSLFetchNameValue( papszMD, + GDAL_DMD_OPENOPTIONLIST ) ); + char *pszFormattedXML = + CPLSerializeXMLTree( psCOL ); + + CPLDestroyXMLNode( psCOL ); + + printf( "%s\n", pszFormattedXML ); + CPLFree( pszFormattedXML ); + } + return 0; + } + +/* -------------------------------------------------------------------- */ +/* --help-general */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--help-general") ) + { + printf( "Generic GDAL utility command options:\n" ); + printf( " --version: report version of GDAL in use.\n" ); + printf( " --license: report GDAL license info.\n" ); + printf( " --formats: report all configured format drivers.\n" ); + printf( " --format [format]: details of one format.\n" ); + printf( " --optfile filename: expand an option file into the argument list.\n" ); + printf( " --config key value: set system configuration option.\n" ); + printf( " --debug [on/off/value]: set debug level.\n" ); + printf( " --pause: wait for user input, time to attach debugger\n" ); + printf( " --locale [locale]: install locale for debugging (ie. en_US.UTF-8)\n" ); + printf( " --help-general: report detailed help on general options.\n" ); + CSLDestroy( papszReturn ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* --locale */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--locale") && iArg < nArgc-1 ) + { + CPLsetlocale( LC_ALL, papszArgv[++iArg] ); + } + +/* -------------------------------------------------------------------- */ +/* --pause */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(papszArgv[iArg],"--pause") ) + { + printf( "Hit to Continue.\n" ); + CPLReadLine( stdin ); + } + +/* -------------------------------------------------------------------- */ +/* carry through unrecognised options. */ +/* -------------------------------------------------------------------- */ + else + { + papszReturn = CSLAddString( papszReturn, papszArgv[iArg] ); + } + } + + *ppapszArgv = papszReturn; + + return CSLCount( *ppapszArgv ); +} + + +/************************************************************************/ +/* _FetchDblFromMD() */ +/************************************************************************/ + +static int _FetchDblFromMD( char **papszMD, const char *pszKey, + double *padfTarget, int nCount, double dfDefault ) + +{ + char szFullKey[200]; + + sprintf( szFullKey, "%s", pszKey ); + + const char *pszValue = CSLFetchNameValue( papszMD, szFullKey ); + int i; + + for( i = 0; i < nCount; i++ ) + padfTarget[i] = dfDefault; + + if( pszValue == NULL ) + return FALSE; + + if( nCount == 1 ) + { + *padfTarget = CPLAtofM( pszValue ); + return TRUE; + } + + char **papszTokens = CSLTokenizeStringComplex( pszValue, " ,", + FALSE, FALSE ); + + if( CSLCount( papszTokens ) != nCount ) + { + CSLDestroy( papszTokens ); + return FALSE; + } + + for( i = 0; i < nCount; i++ ) + padfTarget[i] = CPLAtofM(papszTokens[i]); + + CSLDestroy( papszTokens ); + + return TRUE; +} + +/************************************************************************/ +/* GDALExtractRPCInfo() */ +/* */ +/* Extract RPC info from metadata, and apply to an RPCInfo */ +/* structure. The inverse of this function is RPCInfoToMD() in */ +/* alg/gdal_rpc.cpp (should it be needed). */ +/************************************************************************/ + +int CPL_STDCALL GDALExtractRPCInfo( char **papszMD, GDALRPCInfo *psRPC ) + +{ + if( CSLFetchNameValue( papszMD, RPC_LINE_NUM_COEFF ) == NULL ) + return FALSE; + + if( CSLFetchNameValue( papszMD, RPC_LINE_NUM_COEFF ) == NULL + || CSLFetchNameValue( papszMD, RPC_LINE_DEN_COEFF ) == NULL + || CSLFetchNameValue( papszMD, RPC_SAMP_NUM_COEFF ) == NULL + || CSLFetchNameValue( papszMD, RPC_SAMP_DEN_COEFF ) == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Some required RPC metadata missing in GDALExtractRPCInfo()"); + return FALSE; + } + + _FetchDblFromMD( papszMD, RPC_LINE_OFF, &(psRPC->dfLINE_OFF), 1, 0.0 ); + _FetchDblFromMD( papszMD, RPC_LINE_SCALE, &(psRPC->dfLINE_SCALE), 1, 1.0 ); + _FetchDblFromMD( papszMD, RPC_SAMP_OFF, &(psRPC->dfSAMP_OFF), 1, 0.0 ); + _FetchDblFromMD( papszMD, RPC_SAMP_SCALE, &(psRPC->dfSAMP_SCALE), 1, 1.0 ); + _FetchDblFromMD( papszMD, RPC_HEIGHT_OFF, &(psRPC->dfHEIGHT_OFF), 1, 0.0 ); + _FetchDblFromMD( papszMD, RPC_HEIGHT_SCALE, &(psRPC->dfHEIGHT_SCALE),1, 1.0); + _FetchDblFromMD( papszMD, RPC_LAT_OFF, &(psRPC->dfLAT_OFF), 1, 0.0 ); + _FetchDblFromMD( papszMD, RPC_LAT_SCALE, &(psRPC->dfLAT_SCALE), 1, 1.0 ); + _FetchDblFromMD( papszMD, RPC_LONG_OFF, &(psRPC->dfLONG_OFF), 1, 0.0 ); + _FetchDblFromMD( papszMD, RPC_LONG_SCALE, &(psRPC->dfLONG_SCALE), 1, 1.0 ); + + _FetchDblFromMD( papszMD, RPC_LINE_NUM_COEFF, psRPC->adfLINE_NUM_COEFF, + 20, 0.0 ); + _FetchDblFromMD( papszMD, RPC_LINE_DEN_COEFF, psRPC->adfLINE_DEN_COEFF, + 20, 0.0 ); + _FetchDblFromMD( papszMD, RPC_SAMP_NUM_COEFF, psRPC->adfSAMP_NUM_COEFF, + 20, 0.0 ); + _FetchDblFromMD( papszMD, RPC_SAMP_DEN_COEFF, psRPC->adfSAMP_DEN_COEFF, + 20, 0.0 ); + + _FetchDblFromMD( papszMD, "MIN_LONG", &(psRPC->dfMIN_LONG), 1, -180.0 ); + _FetchDblFromMD( papszMD, "MIN_LAT", &(psRPC->dfMIN_LAT), 1, -90.0 ); + _FetchDblFromMD( papszMD, "MAX_LONG", &(psRPC->dfMAX_LONG), 1, 180.0 ); + _FetchDblFromMD( papszMD, "MAX_LAT", &(psRPC->dfMAX_LAT), 1, 90.0 ); + + return TRUE; +} + +/************************************************************************/ +/* GDALFindAssociatedAuxFile() */ +/************************************************************************/ + +GDALDataset *GDALFindAssociatedAuxFile( const char *pszBasename, + GDALAccess eAccess, + GDALDataset *poDependentDS ) + +{ + const char *pszAuxSuffixLC = "aux"; + const char *pszAuxSuffixUC = "AUX"; + + if( EQUAL(CPLGetExtension(pszBasename), pszAuxSuffixLC) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Don't even try to look for an .aux file if we don't have a */ +/* path of any kind. */ +/* -------------------------------------------------------------------- */ + if( strlen(pszBasename) == 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* We didn't find that, so try and find a corresponding aux */ +/* file. Check that we are the dependent file of the aux */ +/* file, or if we aren't verify that the dependent file does */ +/* not exist, likely mean it is us but some sort of renaming */ +/* has occured. */ +/* -------------------------------------------------------------------- */ + CPLString osJustFile = CPLGetFilename(pszBasename); // without dir + CPLString osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixLC); + GDALDataset *poODS = NULL; + GByte abyHeader[32]; + VSILFILE *fp; + + fp = VSIFOpenL( osAuxFilename, "rb" ); + + + if ( fp == NULL && VSIIsCaseSensitiveFS(osAuxFilename)) + { + // Can't found file with lower case suffix. Try the upper case one. + osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixUC); + fp = VSIFOpenL( osAuxFilename, "rb" ); + } + + if( fp != NULL ) + { + if( VSIFReadL( abyHeader, 1, 32, fp ) == 32 && + EQUALN((char *) abyHeader,"EHFA_HEADER_TAG",15) ) + { + /* Avoid causing failure in opening of main file from SWIG bindings */ + /* when auxiliary file cannot be opened (#3269) */ + CPLTurnFailureIntoWarning(TRUE); + if( poDependentDS != NULL && poDependentDS->GetShared() ) + poODS = (GDALDataset *) GDALOpenShared( osAuxFilename, eAccess ); + else + poODS = (GDALDataset *) GDALOpen( osAuxFilename, eAccess ); + CPLTurnFailureIntoWarning(FALSE); + } + VSIFCloseL( fp ); + } + +/* -------------------------------------------------------------------- */ +/* Try replacing extension with .aux */ +/* -------------------------------------------------------------------- */ + if( poODS != NULL ) + { + const char *pszDep + = poODS->GetMetadataItem( "HFA_DEPENDENT_FILE", "HFA" ); + if( pszDep == NULL ) + { + CPLDebug( "AUX", + "Found %s but it has no dependent file, ignoring.", + osAuxFilename.c_str() ); + GDALClose( poODS ); + poODS = NULL; + } + else if( !EQUAL(pszDep,osJustFile) ) + { + VSIStatBufL sStatBuf; + + if( VSIStatExL( pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + { + CPLDebug( "AUX", "%s is for file %s, not %s, ignoring.", + osAuxFilename.c_str(), + pszDep, osJustFile.c_str() ); + GDALClose( poODS ); + poODS = NULL; + } + else + { + CPLDebug( "AUX", "%s is for file %s, not %s, but since\n" + "%s does not exist, we will use .aux file as our own.", + osAuxFilename.c_str(), + pszDep, osJustFile.c_str(), + pszDep ); + } + } + +/* -------------------------------------------------------------------- */ +/* Confirm that the aux file matches the configuration of the */ +/* dependent dataset. */ +/* -------------------------------------------------------------------- */ + if( poODS != NULL && poDependentDS != NULL + && (poODS->GetRasterCount() != poDependentDS->GetRasterCount() + || poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() + || poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()) ) + { + CPLDebug( "AUX", + "Ignoring aux file %s as its raster configuration\n" + "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)", + osAuxFilename.c_str(), + poODS->GetRasterXSize(), + poODS->GetRasterYSize(), + poODS->GetRasterCount(), + poDependentDS->GetRasterXSize(), + poDependentDS->GetRasterYSize(), + poDependentDS->GetRasterCount() ); + + GDALClose( poODS ); + poODS = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Try appending .aux to the end of the filename. */ +/* -------------------------------------------------------------------- */ + if( poODS == NULL ) + { + osAuxFilename = pszBasename; + osAuxFilename += "."; + osAuxFilename += pszAuxSuffixLC; + fp = VSIFOpenL( osAuxFilename, "rb" ); + if ( fp == NULL && VSIIsCaseSensitiveFS(osAuxFilename) ) + { + // Can't found file with lower case suffix. Try the upper case one. + osAuxFilename = pszBasename; + osAuxFilename += "."; + osAuxFilename += pszAuxSuffixUC; + fp = VSIFOpenL( osAuxFilename, "rb" ); + } + + if( fp != NULL ) + { + if( VSIFReadL( abyHeader, 1, 32, fp ) == 32 && + EQUALN((char *) abyHeader,"EHFA_HEADER_TAG",15) ) + { + /* Avoid causing failure in opening of main file from SWIG bindings */ + /* when auxiliary file cannot be opened (#3269) */ + CPLTurnFailureIntoWarning(TRUE); + if( poDependentDS != NULL && poDependentDS->GetShared() ) + poODS = (GDALDataset *) GDALOpenShared( osAuxFilename, eAccess ); + else + poODS = (GDALDataset *) GDALOpen( osAuxFilename, eAccess ); + CPLTurnFailureIntoWarning(FALSE); + } + VSIFCloseL( fp ); + } + + if( poODS != NULL ) + { + const char *pszDep + = poODS->GetMetadataItem( "HFA_DEPENDENT_FILE", "HFA" ); + if( pszDep == NULL ) + { + CPLDebug( "AUX", + "Found %s but it has no dependent file, ignoring.", + osAuxFilename.c_str() ); + GDALClose( poODS ); + poODS = NULL; + } + else if( !EQUAL(pszDep,osJustFile) ) + { + VSIStatBufL sStatBuf; + + if( VSIStatExL( pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + { + CPLDebug( "AUX", "%s is for file %s, not %s, ignoring.", + osAuxFilename.c_str(), + pszDep, osJustFile.c_str() ); + GDALClose( poODS ); + poODS = NULL; + } + else + { + CPLDebug( "AUX", "%s is for file %s, not %s, but since\n" + "%s does not exist, we will use .aux file as our own.", + osAuxFilename.c_str(), + pszDep, osJustFile.c_str(), + pszDep ); + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Confirm that the aux file matches the configuration of the */ +/* dependent dataset. */ +/* -------------------------------------------------------------------- */ + if( poODS != NULL && poDependentDS != NULL + && (poODS->GetRasterCount() != poDependentDS->GetRasterCount() + || poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() + || poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()) ) + { + CPLDebug( "AUX", + "Ignoring aux file %s as its raster configuration\n" + "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)", + osAuxFilename.c_str(), + poODS->GetRasterXSize(), + poODS->GetRasterYSize(), + poODS->GetRasterCount(), + poDependentDS->GetRasterXSize(), + poDependentDS->GetRasterYSize(), + poDependentDS->GetRasterCount() ); + + GDALClose( poODS ); + poODS = NULL; + } + + return poODS; +} + +/************************************************************************/ +/* -------------------------------------------------------------------- */ +/* The following stubs are present to ensure that older GDAL */ +/* bridges don't fail with newer libraries. */ +/* -------------------------------------------------------------------- */ +/************************************************************************/ + +CPL_C_START + +void * CPL_STDCALL GDALCreateProjDef( const char * ) +{ + CPLDebug( "GDAL", "GDALCreateProjDef no longer supported." ); + return NULL; +} + +CPLErr CPL_STDCALL GDALReprojectToLongLat( void *, double *, double * ) +{ + CPLDebug( "GDAL", "GDALReprojectToLatLong no longer supported." ); + return CE_Failure; +} + +CPLErr CPL_STDCALL GDALReprojectFromLongLat( void *, double *, double * ) +{ + CPLDebug( "GDAL", "GDALReprojectFromLatLong no longer supported." ); + return CE_Failure; +} + +void CPL_STDCALL GDALDestroyProjDef( void * ) + +{ + CPLDebug( "GDAL", "GDALDestroyProjDef no longer supported." ); +} + +CPL_C_END + +/************************************************************************/ +/* Infrastructure to check that dataset characteristics are valid */ +/************************************************************************/ + +CPL_C_START + +/** + * \brief Return TRUE if the dataset dimensions are valid. + * + * @param nXSize raster width + * @param nYSize raster height + * + * @since GDAL 1.7.0 + */ +int GDALCheckDatasetDimensions( int nXSize, int nYSize ) +{ + if (nXSize <= 0 || nYSize <= 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid dataset dimensions : %d x %d", nXSize, nYSize); + return FALSE; + } + return TRUE; +} + +/** + * \brief Return TRUE if the band count is valid. + * + * If the configuration option GDAL_MAX_BAND_COUNT is defined, + * the band count will be compared to the maximum number of band allowed. + * + * @param nBands the band count + * @param bIsZeroAllowed TRUE if band count == 0 is allowed + * + * @since GDAL 1.7.0 + */ + +int GDALCheckBandCount( int nBands, int bIsZeroAllowed ) +{ + int nMaxBands = -1; + const char* pszMaxBandCount = CPLGetConfigOption("GDAL_MAX_BAND_COUNT", NULL); + if (pszMaxBandCount != NULL) + { + nMaxBands = atoi(pszMaxBandCount); + } + if (nBands < 0 || (!bIsZeroAllowed && nBands == 0) || + (nMaxBands >= 0 && nBands > nMaxBands) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid band count : %d", nBands); + return FALSE; + } + return TRUE; +} + +CPL_C_END + + +/************************************************************************/ +/* GDALSerializeGCPListToXML() */ +/************************************************************************/ + +void GDALSerializeGCPListToXML( CPLXMLNode* psParentNode, + GDAL_GCP* pasGCPList, + int nGCPCount, + const char* pszGCPProjection ) +{ + CPLString oFmt; + + CPLXMLNode *psPamGCPList = CPLCreateXMLNode( psParentNode, CXT_Element, + "GCPList" ); + + CPLXMLNode* psLastChild = NULL; + + if( pszGCPProjection != NULL + && strlen(pszGCPProjection) > 0 ) + { + CPLSetXMLValue( psPamGCPList, "#Projection", + pszGCPProjection ); + psLastChild = psPamGCPList->psChild; + } + + for( int iGCP = 0; iGCP < nGCPCount; iGCP++ ) + { + CPLXMLNode *psXMLGCP; + GDAL_GCP *psGCP = pasGCPList + iGCP; + + psXMLGCP = CPLCreateXMLNode( NULL, CXT_Element, "GCP" ); + + if( psLastChild == NULL ) + psPamGCPList->psChild = psXMLGCP; + else + psLastChild->psNext = psXMLGCP; + psLastChild = psXMLGCP; + + CPLSetXMLValue( psXMLGCP, "#Id", psGCP->pszId ); + + if( psGCP->pszInfo != NULL && strlen(psGCP->pszInfo) > 0 ) + CPLSetXMLValue( psXMLGCP, "Info", psGCP->pszInfo ); + + CPLSetXMLValue( psXMLGCP, "#Pixel", + oFmt.Printf( "%.4f", psGCP->dfGCPPixel ) ); + + CPLSetXMLValue( psXMLGCP, "#Line", + oFmt.Printf( "%.4f", psGCP->dfGCPLine ) ); + + CPLSetXMLValue( psXMLGCP, "#X", + oFmt.Printf( "%.12E", psGCP->dfGCPX ) ); + + CPLSetXMLValue( psXMLGCP, "#Y", + oFmt.Printf( "%.12E", psGCP->dfGCPY ) ); + + /* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it back */ + if( psGCP->dfGCPZ != 0.0 ) + CPLSetXMLValue( psXMLGCP, "#Z", + oFmt.Printf( "%.12E", psGCP->dfGCPZ ) ); + } +} + +/************************************************************************/ +/* GDALDeserializeGCPListFromXML() */ +/************************************************************************/ + +void GDALDeserializeGCPListFromXML( CPLXMLNode* psGCPList, + GDAL_GCP** ppasGCPList, + int* pnGCPCount, + char** ppszGCPProjection ) +{ + CPLXMLNode *psXMLGCP; + OGRSpatialReference oSRS; + + if( ppszGCPProjection ) + { + const char *pszRawProj = CPLGetXMLValue(psGCPList, "Projection", ""); + + if( strlen(pszRawProj) > 0 + && oSRS.SetFromUserInput( pszRawProj ) == OGRERR_NONE ) + oSRS.exportToWkt( ppszGCPProjection ); + else + *ppszGCPProjection = CPLStrdup(""); + } + + // Count GCPs. + int nGCPMax = 0; + + for( psXMLGCP = psGCPList->psChild; psXMLGCP != NULL; + psXMLGCP = psXMLGCP->psNext ) + nGCPMax++; + + *ppasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPMax); + *pnGCPCount = 0; + + for( psXMLGCP = psGCPList->psChild; psXMLGCP != NULL; + psXMLGCP = psXMLGCP->psNext ) + { + GDAL_GCP *psGCP = *ppasGCPList + *pnGCPCount; + + if( !EQUAL(psXMLGCP->pszValue,"GCP") || + psXMLGCP->eType != CXT_Element ) + continue; + + GDALInitGCPs( 1, psGCP ); + + CPLFree( psGCP->pszId ); + psGCP->pszId = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Id","")); + + CPLFree( psGCP->pszInfo ); + psGCP->pszInfo = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Info","")); + + psGCP->dfGCPPixel = CPLAtof(CPLGetXMLValue(psXMLGCP,"Pixel","0.0")); + psGCP->dfGCPLine = CPLAtof(CPLGetXMLValue(psXMLGCP,"Line","0.0")); + + psGCP->dfGCPX = CPLAtof(CPLGetXMLValue(psXMLGCP,"X","0.0")); + psGCP->dfGCPY = CPLAtof(CPLGetXMLValue(psXMLGCP,"Y","0.0")); + const char* pszZ = CPLGetXMLValue(psXMLGCP,"Z",NULL); + if( pszZ == NULL ) + { + /* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it back */ + pszZ = CPLGetXMLValue(psXMLGCP,"GCPZ","0.0"); + } + psGCP->dfGCPZ = CPLAtof(pszZ); + + (*pnGCPCount) ++; + } +} + +/************************************************************************/ +/* GDALSerializeOpenOptionsToXML() */ +/************************************************************************/ + +void GDALSerializeOpenOptionsToXML( CPLXMLNode* psParentNode, char** papszOpenOptions) +{ + if( papszOpenOptions != NULL ) + { + CPLXMLNode* psOpenOptions = CPLCreateXMLNode( psParentNode, CXT_Element, "OpenOptions" ); + CPLXMLNode* psLastChild = NULL; + + for(char** papszIter = papszOpenOptions; *papszIter != NULL; papszIter ++ ) + { + const char *pszRawValue; + char *pszKey = NULL; + CPLXMLNode *psOOI; + + pszRawValue = CPLParseNameValue( *papszIter, &pszKey ); + + psOOI = CPLCreateXMLNode( NULL, CXT_Element, "OOI" ); + if( psLastChild == NULL ) + psOpenOptions->psChild = psOOI; + else + psLastChild->psNext = psOOI; + psLastChild = psOOI; + + CPLSetXMLValue( psOOI, "#key", pszKey ); + CPLCreateXMLNode( psOOI, CXT_Text, pszRawValue ); + + CPLFree( pszKey ); + } + } +} + +/************************************************************************/ +/* GDALDeserializeOpenOptionsFromXML() */ +/************************************************************************/ + +char** GDALDeserializeOpenOptionsFromXML( CPLXMLNode* psParentNode ) +{ + char** papszOpenOptions = NULL; + CPLXMLNode* psOpenOptions = CPLGetXMLNode(psParentNode, "OpenOptions"); + if( psOpenOptions != NULL ) + { + CPLXMLNode* psOOI; + for( psOOI = psOpenOptions->psChild; psOOI != NULL; + psOOI = psOOI->psNext ) + { + if( !EQUAL(psOOI->pszValue,"OOI") + || psOOI->eType != CXT_Element + || psOOI->psChild == NULL + || psOOI->psChild->psNext == NULL + || psOOI->psChild->eType != CXT_Attribute + || psOOI->psChild->psChild == NULL ) + continue; + + char* pszName = psOOI->psChild->psChild->pszValue; + char* pszValue = psOOI->psChild->psNext->pszValue; + if( pszName != NULL && pszValue != NULL ) + papszOpenOptions = CSLSetNameValue( papszOpenOptions, pszName, pszValue ); + } + } + return papszOpenOptions; +} + +/************************************************************************/ +/* GDALRasterIOGetResampleAlg() */ +/************************************************************************/ + +GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char* pszResampling) +{ + GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour; + if( EQUALN(pszResampling, "NEAR", 4) ) + eResampleAlg = GRIORA_NearestNeighbour; + else if( EQUAL(pszResampling, "BILINEAR") ) + eResampleAlg = GRIORA_Bilinear; + else if( EQUAL(pszResampling, "CUBIC") ) + eResampleAlg = GRIORA_Cubic; + else if( EQUAL(pszResampling, "CUBICSPLINE") ) + eResampleAlg = GRIORA_CubicSpline; + else if( EQUAL(pszResampling, "LANCZOS") ) + eResampleAlg = GRIORA_Lanczos; + else if( EQUAL(pszResampling, "AVERAGE") ) + eResampleAlg = GRIORA_Average; + else if( EQUAL(pszResampling, "MODE") ) + eResampleAlg = GRIORA_Mode; + else if( EQUAL(pszResampling, "GAUSS") ) + eResampleAlg = GRIORA_Gauss; + else + CPLError(CE_Warning, CPLE_NotSupported, + "GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling); + return eResampleAlg; +} + +/************************************************************************/ +/* GDALRasterIOExtraArgSetResampleAlg() */ +/************************************************************************/ + +void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg* psExtraArg, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize) +{ + if( (nBufXSize != nXSize || nBufYSize != nYSize) && + psExtraArg->eResampleAlg == GRIORA_NearestNeighbour ) + { + const char* pszResampling = CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", NULL); + if( pszResampling != NULL ) + { + psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(pszResampling); + } + } +} + +/************************************************************************/ +/* GDALCanFileAcceptSidecarFile() */ +/************************************************************************/ + +int GDALCanFileAcceptSidecarFile(const char* pszFilename) +{ + if( strstr(pszFilename, "/vsicurl/") && strchr(pszFilename, '?') ) + return FALSE; + return TRUE; +} diff --git a/bazaar/plugin/gdal/gcore/gdal_pam.h b/bazaar/plugin/gdal/gcore/gdal_pam.h new file mode 100644 index 000000000..1e3c1c009 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdal_pam.h @@ -0,0 +1,316 @@ +/****************************************************************************** + * $Id: gdal_pam.h 28899 2015-04-14 09:27:00Z rouault $ + * + * Project: GDAL Core + * Purpose: Declaration for Peristable Auxiliary Metadata classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_PAM_H_INCLUDED +#define GDAL_PAM_H_INCLUDED + +#include "gdal_priv.h" + +class GDALPamRasterBand; + +/* Clone Info Flags */ + +#define GCIF_GEOTRANSFORM 0x01 +#define GCIF_PROJECTION 0x02 +#define GCIF_METADATA 0x04 +#define GCIF_GCPS 0x08 + +#define GCIF_NODATA 0x001000 +#define GCIF_CATEGORYNAMES 0x002000 +#define GCIF_MINMAX 0x004000 +#define GCIF_SCALEOFFSET 0x008000 +#define GCIF_UNITTYPE 0x010000 +#define GCIF_COLORTABLE 0x020000 +#define GCIF_COLORINTERP 0x020000 +#define GCIF_BAND_METADATA 0x040000 +#define GCIF_RAT 0x080000 +#define GCIF_MASK 0x100000 +#define GCIF_BAND_DESCRIPTION 0x200000 + +#define GCIF_ONLY_IF_MISSING 0x10000000 +#define GCIF_PROCESS_BANDS 0x20000000 + +#define GCIF_PAM_DEFAULT (GCIF_GEOTRANSFORM | GCIF_PROJECTION | \ + GCIF_METADATA | GCIF_GCPS | \ + GCIF_NODATA | GCIF_CATEGORYNAMES | \ + GCIF_MINMAX | GCIF_SCALEOFFSET | \ + GCIF_UNITTYPE | GCIF_COLORTABLE | \ + GCIF_COLORINTERP | GCIF_BAND_METADATA | \ + GCIF_RAT | GCIF_MASK | \ + GCIF_ONLY_IF_MISSING | GCIF_PROCESS_BANDS|\ + GCIF_BAND_DESCRIPTION) + +/* GDAL PAM Flags */ +/* ERO 2011/04/13 : GPF_AUXMODE seems to be unimplemented */ +#define GPF_DIRTY 0x01 // .pam file needs to be written on close +#define GPF_TRIED_READ_FAILED 0x02 // no need to keep trying to read .pam. +#define GPF_DISABLED 0x04 // do not try any PAM stuff. +#define GPF_AUXMODE 0x08 // store info in .aux (HFA) file. +#define GPF_NOSAVE 0x10 // do not try to save pam info. + +/* ==================================================================== */ +/* GDALDatasetPamInfo */ +/* */ +/* We make these things a seperate structure of information */ +/* primarily so we can modify it without altering the size of */ +/* the GDALPamDataset. It is an effort to reduce ABI churn for */ +/* driver plugins. */ +/* ==================================================================== */ +class GDALDatasetPamInfo +{ +public: + char *pszPamFilename; + + char *pszProjection; + + int bHaveGeoTransform; + double adfGeoTransform[6]; + + int nGCPCount; + GDAL_GCP *pasGCPList; + char *pszGCPProjection; + + CPLString osPhysicalFilename; + CPLString osSubdatasetName; + CPLString osAuxFilename; + + int bHasMetadata; +}; + +/* ******************************************************************** */ +/* GDALPamDataset */ +/* ******************************************************************** */ + +class CPL_DLL GDALPamDataset : public GDALDataset +{ + friend class GDALPamRasterBand; + + private: + int IsPamFilenameAPotentialSiblingFile(); + + protected: + GDALPamDataset(void); + + int nPamFlags; + GDALDatasetPamInfo *psPam; + + virtual CPLXMLNode *SerializeToXML( const char *); + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + + virtual CPLErr TryLoadXML(char **papszSiblingFiles = NULL); + virtual CPLErr TrySaveXML(); + + CPLErr TryLoadAux(char **papszSiblingFiles = NULL); + CPLErr TrySaveAux(); + + virtual const char *BuildPamFilename(); + + void PamInitialize(); + void PamClear(); + + void SetPhysicalFilename( const char * ); + const char *GetPhysicalFilename(); + void SetSubdatasetName( const char *); + const char *GetSubdatasetName(); + + public: + virtual ~GDALPamDataset(); + + virtual void FlushCache(void); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + + virtual char **GetFileList(void); + + virtual CPLErr CloneInfo( GDALDataset *poSrcDS, int nCloneInfoFlags ); + + virtual CPLErr IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ); + + + // "semi private" methods. + void MarkPamDirty() { nPamFlags |= GPF_DIRTY; } + GDALDatasetPamInfo *GetPamInfo() { return psPam; } + int GetPamFlags() { return nPamFlags; } + void SetPamFlags(int nValue ) { nPamFlags = nValue; } +}; + +/* ==================================================================== */ +/* GDALRasterBandPamInfo */ +/* */ +/* We make these things a seperate structure of information */ +/* primarily so we can modify it without altering the size of */ +/* the GDALPamDataset. It is an effort to reduce ABI churn for */ +/* driver plugins. */ +/* ==================================================================== */ +typedef struct { + GDALPamDataset *poParentDS; + + int bNoDataValueSet; + double dfNoDataValue; + + GDALColorTable *poColorTable; + + GDALColorInterp eColorInterp; + + char *pszUnitType; + char **papszCategoryNames; + + double dfOffset; + double dfScale; + + int bHaveMinMax; + double dfMin; + double dfMax; + + int bHaveStats; + double dfMean; + double dfStdDev; + + CPLXMLNode *psSavedHistograms; + + GDALRasterAttributeTable *poDefaultRAT; + +} GDALRasterBandPamInfo; + +/* ******************************************************************** */ +/* GDALPamRasterBand */ +/* ******************************************************************** */ +class CPL_DLL GDALPamRasterBand : public GDALRasterBand +{ + friend class GDALPamDataset; + + protected: + + virtual CPLXMLNode *SerializeToXML( const char *pszVRTPath ); + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + + void PamInitialize(); + void PamClear(); + + GDALRasterBandPamInfo *psPam; + + public: + GDALPamRasterBand(); + virtual ~GDALPamRasterBand(); + + virtual void SetDescription( const char * ); + + virtual CPLErr SetNoDataValue( double ); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + + virtual CPLErr SetColorTable( GDALColorTable * ); + virtual GDALColorTable *GetColorTable(); + + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + virtual GDALColorInterp GetColorInterpretation(); + + virtual const char *GetUnitType(); + CPLErr SetUnitType( const char * ); + + virtual char **GetCategoryNames(); + virtual CPLErr SetCategoryNames( char ** ); + + virtual double GetOffset( int *pbSuccess = NULL ); + CPLErr SetOffset( double ); + virtual double GetScale( int *pbSuccess = NULL ); + CPLErr SetScale( double ); + + virtual CPLErr GetHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc, void *pProgressData ); + + virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig ** ppanHistogram, + int bForce, + GDALProgressFunc, void *pProgressData); + + virtual CPLErr SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram ); + + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + virtual GDALRasterAttributeTable *GetDefaultRAT(); + virtual CPLErr SetDefaultRAT( const GDALRasterAttributeTable * ); + + // new in GDALPamRasterBand. + virtual CPLErr CloneInfo( GDALRasterBand *poSrcBand, int nCloneInfoFlags ); + + // "semi private" methods. + GDALRasterBandPamInfo *GetPamInfo() { return psPam; } +}; + +// These are mainly helper functions for internal use. +int CPL_DLL PamParseHistogram( CPLXMLNode *psHistItem, + double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig **ppanHistogram, + int *pbIncludeOutOfRange, int *pbApproxOK ); +CPLXMLNode CPL_DLL * +PamFindMatchingHistogram( CPLXMLNode *psSavedHistograms, + double dfMin, double dfMax, int nBuckets, + int bIncludeOutOfRange, int bApproxOK ); +CPLXMLNode CPL_DLL * +PamHistogramToXMLTree( double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApprox ); + +// For managing the proxy file database. +const char CPL_DLL * PamGetProxy( const char * ); +const char CPL_DLL * PamAllocateProxy( const char * ); +const char CPL_DLL * PamDeallocateProxy( const char * ); +void CPL_DLL PamCleanProxyDB( void ); + +#endif /* ndef GDAL_PAM_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/gcore/gdal_priv.h b/bazaar/plugin/gdal/gcore/gdal_priv.h new file mode 100644 index 000000000..0ceb9db16 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdal_priv.h @@ -0,0 +1,1237 @@ +/****************************************************************************** + * $Id: gdal_priv.h 29284 2015-06-03 13:26:10Z rouault $ + * + * Name: gdal_priv.h + * Project: GDAL Core + * Purpose: GDAL Core C++/Private declarations. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_PRIV_H_INCLUDED +#define GDAL_PRIV_H_INCLUDED + +/* -------------------------------------------------------------------- */ +/* Predeclare various classes before pulling in gdal.h, the */ +/* public declarations. */ +/* -------------------------------------------------------------------- */ +class GDALMajorObject; +class GDALDataset; +class GDALRasterBand; +class GDALDriver; +class GDALRasterAttributeTable; +class GDALProxyDataset; +class GDALProxyRasterBand; +class GDALAsyncReader; + +/* -------------------------------------------------------------------- */ +/* Pull in the public declarations. This gets the C apis, and */ +/* also various constants. However, we will still get to */ +/* provide the real class definitions for the GDAL classes. */ +/* -------------------------------------------------------------------- */ + +#include "gdal.h" +#include "gdal_frmts.h" +#include "cpl_vsi.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_minixml.h" +#include "cpl_multiproc.h" +#include +#include +#include "ogr_core.h" + +#define GMO_VALID 0x0001 +#define GMO_IGNORE_UNIMPLEMENTED 0x0002 +#define GMO_SUPPORT_MD 0x0004 +#define GMO_SUPPORT_MDMD 0x0008 +#define GMO_MD_DIRTY 0x0010 +#define GMO_PAM_CLASS 0x0020 + +/************************************************************************/ +/* GDALMultiDomainMetadata */ +/************************************************************************/ + +class CPL_DLL GDALMultiDomainMetadata +{ +private: + char **papszDomainList; + CPLStringList **papoMetadataLists; + +public: + GDALMultiDomainMetadata(); + ~GDALMultiDomainMetadata(); + + int XMLInit( CPLXMLNode *psMetadata, int bMerge ); + CPLXMLNode *Serialize(); + + char **GetDomainList() { return papszDomainList; } + + char **GetMetadata( const char * pszDomain = "" ); + CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + void Clear(); +}; + +/* ******************************************************************** */ +/* GDALMajorObject */ +/* */ +/* Base class providing metadata, description and other */ +/* services shared by major objects. */ +/* ******************************************************************** */ + +//! Object with metadata. + +class CPL_DLL GDALMajorObject +{ + protected: + int nFlags; // GMO_* flags. + CPLString sDescription; + GDALMultiDomainMetadata oMDMD; + + char **BuildMetadataDomainList(char** papszList, int bCheckNonEmpty, ...) CPL_NULL_TERMINATED; + + public: + GDALMajorObject(); + virtual ~GDALMajorObject(); + + int GetMOFlags(); + void SetMOFlags(int nFlagsIn); + + virtual const char *GetDescription() const; + virtual void SetDescription( const char * ); + + virtual char **GetMetadataDomainList(); + + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); +}; + +/* ******************************************************************** */ +/* GDALDefaultOverviews */ +/* ******************************************************************** */ +class CPL_DLL GDALDefaultOverviews +{ + friend class GDALDataset; + + GDALDataset *poDS; + GDALDataset *poODS; + + CPLString osOvrFilename; + + int bOvrIsAux; + + int bCheckedForMask; + int bOwnMaskDS; + GDALDataset *poMaskDS; + + // for "overview datasets" we record base level info so we can + // find our way back to get overview masks. + GDALDataset *poBaseDS; + + // Stuff for deferred initialize/overviewscans... + bool bCheckedForOverviews; + void OverviewScan(); + char *pszInitName; + int bInitNameIsOVR; + char **papszInitSiblingFiles; + + public: + GDALDefaultOverviews(); + ~GDALDefaultOverviews(); + + void Initialize( GDALDataset *poDSIn, const char *pszName = NULL, + char **papszSiblingFiles = NULL, + int bNameIsOVR = FALSE ); + + int IsInitialized(); + + int CloseDependentDatasets(); + + // Overview Related + + int GetOverviewCount(int); + GDALRasterBand *GetOverview(int,int); + + CPLErr BuildOverviews( const char * pszBasename, + const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, + void *pProgressData ); + + CPLErr BuildOverviewsSubDataset( const char * pszPhysicalFile, + const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, + void *pProgressData ); + + CPLErr CleanOverviews(); + + // Mask Related + + CPLErr CreateMaskBand( int nFlags, int nBand = -1 ); + GDALRasterBand *GetMaskBand( int nBand ); + int GetMaskFlags( int nBand ); + + int HaveMaskFile( char **papszSiblings = NULL, + const char *pszBasename = NULL ); + + char** GetSiblingFiles() { return papszInitSiblingFiles; } +}; + +/* ******************************************************************** */ +/* GDALOpenInfo */ +/* */ +/* Structure of data about dataset for open functions. */ +/* ******************************************************************** */ + +class CPL_DLL GDALOpenInfo +{ + int bHasGotSiblingFiles; + char **papszSiblingFiles; + int nHeaderBytesTried; + + public: + GDALOpenInfo( const char * pszFile, int nOpenFlagsIn, + char **papszSiblingFiles = NULL ); + ~GDALOpenInfo( void ); + + char *pszFilename; + char** papszOpenOptions; + + GDALAccess eAccess; + int nOpenFlags; + + int bStatOK; + int bIsDirectory; + + VSILFILE *fpL; + + int nHeaderBytes; + GByte *pabyHeader; + + int TryToIngest(int nBytes); + char **GetSiblingFiles(); +}; + +/* ******************************************************************** */ +/* GDALDataset */ +/* ******************************************************************** */ + +class OGRLayer; +class OGRGeometry; +class OGRSpatialReference; +class OGRStyleTable; +class swq_select; +class swq_select_parse_options; +typedef struct GDALSQLParseInfo GDALSQLParseInfo; + +#ifdef DETECT_OLD_IRASTERIO +typedef void signature_changed; +#endif + +#ifdef GDAL_COMPILATION +#define OPTIONAL_OUTSIDE_GDAL(val) +#else +#define OPTIONAL_OUTSIDE_GDAL(val) = val +#endif + +//! A set of associated raster bands, usually from one file. + +class CPL_DLL GDALDataset : public GDALMajorObject +{ + friend GDALDatasetH CPL_STDCALL GDALOpenEx( const char* pszFilename, + unsigned int nOpenFlags, + const char* const* papszAllowedDrivers, + const char* const* papszOpenOptions, + const char* const* papszSiblingFiles ); + friend void CPL_STDCALL GDALClose( GDALDatasetH hDS ); + + friend class GDALDriver; + friend class GDALDefaultOverviews; + friend class GDALProxyDataset; + friend class GDALDriverManager; + + void AddToDatasetOpenList(); + + protected: + GDALDriver *poDriver; + GDALAccess eAccess; + + // Stored raster information. + int nRasterXSize; + int nRasterYSize; + int nBands; + GDALRasterBand **papoBands; + + int bForceCachedIO; + + int nRefCount; + int bShared; + GByte bIsInternal; + GByte bSuppressOnClose; + GByte bReserved1; + GByte bReserved2; + + GDALDataset(void); + + void RasterInitialize( int, int ); + void SetBand( int, GDALRasterBand * ); + + GDALDefaultOverviews oOvManager; + + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + +#ifdef DETECT_OLD_IRASTERIO + virtual signature_changed IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, int, int, int ) {}; +#endif + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, GSpacing, GSpacing, GSpacing, + GDALRasterIOExtraArg* psExtraArg ); + + CPLErr BlockBasedRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, GSpacing, GSpacing, GSpacing, + GDALRasterIOExtraArg* psExtraArg ); + void BlockBasedFlushCache(); + + CPLErr ValidateRasterIOOrAdviseReadParameters( + const char* pszCallingFunc, + int* pbStopProcessingOnCENone, + int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + int nBandCount, int *panBandMap); + + virtual int CloseDependentDatasets(); + + int ValidateLayerCreationOptions( const char* const* papszLCO ); + + char **papszOpenOptions; + + friend class GDALRasterBand; + + int EnterReadWrite(GDALRWFlag eRWFlag); + void LeaveReadWrite(); + + + public: + virtual ~GDALDataset(); + + int GetRasterXSize( void ); + int GetRasterYSize( void ); + int GetRasterCount( void ); + GDALRasterBand *GetRasterBand( int ); + + virtual void FlushCache(void); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual CPLErr AddBand( GDALDataType eType, + char **papszOptions=NULL ); + + virtual void *GetInternalHandle( const char * ); + virtual GDALDriver *GetDriver(void); + virtual char **GetFileList(void); + + virtual const char* GetDriverName(); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + char **papszOptions ); + + virtual CPLErr CreateMaskBand( int nFlagsIn ); + + virtual GDALAsyncReader* + BeginAsyncReader(int nXOff, int nYOff, int nXSize, int nYSize, + void *pBuf, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, int nBandSpace, + char **papszOptions); + virtual void EndAsyncReader(GDALAsyncReader *); + + CPLErr RasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, GSpacing, GSpacing, GSpacing, + GDALRasterIOExtraArg* psExtraArg +#ifndef DOXYGEN_SKIP + OPTIONAL_OUTSIDE_GDAL(NULL) +#endif + ); + + int Reference(); + int Dereference(); + GDALAccess GetAccess() { return eAccess; } + + int GetShared(); + void MarkAsShared(); + + void MarkSuppressOnClose() { bSuppressOnClose = TRUE; } + + char **GetOpenOptions() { return papszOpenOptions; } + + static GDALDataset **GetOpenDatasets( int *pnDatasetCount ); + + CPLErr BuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + + void ReportError(CPLErr eErrClass, int err_no, const char *fmt, ...) CPL_PRINT_FUNC_FORMAT (4, 5); + +private: + CPLMutex *m_hMutex; + + OGRLayer* BuildLayerFromSelectInfo(swq_select* psSelectInfo, + OGRGeometry *poSpatialFilter, + const char *pszDialect, + swq_select_parse_options* poSelectParseOptions); + + public: + + virtual int GetLayerCount(); + virtual OGRLayer *GetLayer(int); + virtual OGRLayer *GetLayerByName(const char *); + virtual OGRErr DeleteLayer(int); + + virtual int TestCapability( const char * ); + + virtual OGRLayer *CreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + virtual OGRLayer *CopyLayer( OGRLayer *poSrcLayer, + const char *pszNewName, + char **papszOptions = NULL ); + + virtual OGRStyleTable *GetStyleTable(); + virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable ); + + virtual void SetStyleTable(OGRStyleTable *poStyleTable); + + virtual OGRLayer * ExecuteSQL( const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poResultsSet ); + + int GetRefCount() const; + int GetSummaryRefCount() const; + OGRErr Release(); + + virtual OGRErr StartTransaction(int bForce=FALSE); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + static int IsGenericSQLDialect(const char* pszDialect); + + // Semi-public methods. Only to be used by in-tree drivers. + GDALSQLParseInfo* BuildParseInfo(swq_select* psSelectInfo, + swq_select_parse_options* poSelectParseOptions); + void DestroyParseInfo(GDALSQLParseInfo* psParseInfo ); + OGRLayer * ExecuteSQL( const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect, + swq_select_parse_options* poSelectParseOptions); + + protected: + + virtual OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + + OGRErr ProcessSQLCreateIndex( const char * ); + OGRErr ProcessSQLDropIndex( const char * ); + OGRErr ProcessSQLDropTable( const char * ); + OGRErr ProcessSQLAlterTableAddColumn( const char * ); + OGRErr ProcessSQLAlterTableDropColumn( const char * ); + OGRErr ProcessSQLAlterTableAlterColumn( const char * ); + OGRErr ProcessSQLAlterTableRenameColumn( const char * ); + + OGRStyleTable *m_poStyleTable; +}; + +/* ******************************************************************** */ +/* GDALRasterBlock */ +/* ******************************************************************** */ + +//! A single raster block in the block cache. + +class CPL_DLL GDALRasterBlock +{ + GDALDataType eType; + + int bDirty; + int nLockCount; + + int nXOff; + int nYOff; + + int nXSize; + int nYSize; + + void *pData; + + GDALRasterBand *poBand; + + GDALRasterBlock *poNext; + GDALRasterBlock *poPrevious; + + int bMustDetach; + + void Touch_unlocked( void ); + void Detach_unlocked( void ); + + public: + GDALRasterBlock( GDALRasterBand *, int, int ); + virtual ~GDALRasterBlock(); + + CPLErr Internalize( void ); + void Touch( void ); + void MarkDirty( void ); + void MarkClean( void ); + void AddLock( void ) { nLockCount++; } + void DropLock( void ) { nLockCount--; } + void Detach(); + + CPLErr Write(); + + GDALDataType GetDataType() { return eType; } + int GetXOff() { return nXOff; } + int GetYOff() { return nYOff; } + int GetXSize() { return nXSize; } + int GetYSize() { return nYSize; } + int GetDirty() { return bDirty; } + int GetLockCount() { return nLockCount; } + + void *GetDataRef( void ) { return pData; } + int GetBlockSize() { return nXSize * nYSize * (GDALGetDataTypeSize(eType) / 8); } + + /// @brief Accessor to source GDALRasterBand object. + /// @return source raster band of the raster block. + GDALRasterBand *GetBand() { return poBand; } + + static void FlushDirtyBlocks(); + static int FlushCacheBlock(int bDirtyBlocksOnly = FALSE); + static void Verify(); + + static int SafeLockBlock( GDALRasterBlock ** ); + + /* Should only be called by GDALDestroyDriverManager() */ + static void DestroyRBMutex(); +}; + +/* ******************************************************************** */ +/* GDALColorTable */ +/* ******************************************************************** */ + +/*! A color table / palette. */ + +class CPL_DLL GDALColorTable +{ + GDALPaletteInterp eInterp; + + std::vector aoEntries; + +public: + GDALColorTable( GDALPaletteInterp = GPI_RGB ); + ~GDALColorTable(); + + GDALColorTable *Clone() const; + int IsSame(const GDALColorTable* poOtherCT) const; + + GDALPaletteInterp GetPaletteInterpretation() const; + + int GetColorEntryCount() const; + const GDALColorEntry *GetColorEntry( int ) const; + int GetColorEntryAsRGB( int, GDALColorEntry * ) const; + void SetColorEntry( int, const GDALColorEntry * ); + int CreateColorRamp( int, const GDALColorEntry * , + int, const GDALColorEntry * ); +}; + +/* ******************************************************************** */ +/* GDALRasterBand */ +/* ******************************************************************** */ + +//! A single raster band (or channel). + +class CPL_DLL GDALRasterBand : public GDALMajorObject +{ + private: + CPLErr eFlushBlockErr; + + void SetFlushBlockErr( CPLErr eErr ); + + friend class GDALRasterBlock; + CPLErr UnreferenceBlock( int nXBlockOff, int nYBlockOff ); + + protected: + GDALDataset *poDS; + int nBand; /* 1 based */ + + int nRasterXSize; + int nRasterYSize; + + GDALDataType eDataType; + GDALAccess eAccess; + + /* stuff related to blocking, and raster cache */ + int nBlockXSize; + int nBlockYSize; + int nBlocksPerRow; + int nBlocksPerColumn; + + int bSubBlockingActive; + int nSubBlocksPerRow; + int nSubBlocksPerColumn; + GDALRasterBlock **papoBlocks; + + int nBlockReads; + int bForceCachedIO; + + GDALRasterBand *poMask; + bool bOwnMask; + int nMaskFlags; + + void InvalidateMaskBand(); + + friend class GDALDataset; + friend class GDALProxyRasterBand; + friend class GDALDefaultOverviews; + + CPLErr RasterIOResampled( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing, GSpacing, GDALRasterIOExtraArg* psExtraArg ); + + int EnterReadWrite(GDALRWFlag eRWFlag); + void LeaveReadWrite(); + + protected: + virtual CPLErr IReadBlock( int, int, void * ) = 0; + virtual CPLErr IWriteBlock( int, int, void * ); + +#ifdef DETECT_OLD_IRASTERIO + virtual signature_changed IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int ) {}; +#endif + + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing, GSpacing, GDALRasterIOExtraArg* psExtraArg ); + CPLErr OverviewRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing, GSpacing, GDALRasterIOExtraArg* psExtraArg ); + + int InitBlockInfo(); + + CPLErr AdoptBlock( int, int, GDALRasterBlock * ); + GDALRasterBlock *TryGetLockedBlockRef( int nXBlockOff, int nYBlockYOff ); + + public: + GDALRasterBand(); + + virtual ~GDALRasterBand(); + + int GetXSize(); + int GetYSize(); + int GetBand(); + GDALDataset*GetDataset(); + + GDALDataType GetRasterDataType( void ); + void GetBlockSize( int *, int * ); + GDALAccess GetAccess(); + + CPLErr RasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing, GSpacing, GDALRasterIOExtraArg* psExtraArg +#ifndef DOXYGEN_SKIP + OPTIONAL_OUTSIDE_GDAL(NULL) +#endif + ); + CPLErr ReadBlock( int, int, void * ); + + CPLErr WriteBlock( int, int, void * ); + + GDALRasterBlock *GetLockedBlockRef( int nXBlockOff, int nYBlockOff, + int bJustInitialize = FALSE ); + CPLErr FlushBlock( int = -1, int = -1, int bWriteDirtyBlock = TRUE ); + + unsigned char* GetIndexColorTranslationTo(/* const */ GDALRasterBand* poReferenceBand, + unsigned char* pTranslationTable = NULL, + int* pApproximateMatching = NULL); + + // New OpengIS CV_SampleDimension stuff. + + virtual CPLErr FlushCache(); + virtual char **GetCategoryNames(); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum(int *pbSuccess = NULL ); + virtual double GetOffset( int *pbSuccess = NULL ); + virtual double GetScale( int *pbSuccess = NULL ); + virtual const char *GetUnitType(); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual CPLErr Fill(double dfRealValue, double dfImaginaryValue = 0); + + virtual CPLErr SetCategoryNames( char ** ); + virtual CPLErr SetNoDataValue( double ); + virtual CPLErr SetColorTable( GDALColorTable * ); + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + virtual CPLErr SetOffset( double ); + virtual CPLErr SetScale( double ); + virtual CPLErr SetUnitType( const char * ); + + virtual CPLErr GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *padfStdDev ); + virtual CPLErr ComputeStatistics( int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc, void *pProgressData ); + virtual CPLErr SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ); + virtual CPLErr ComputeRasterMinMax( int, double* ); + + virtual int HasArbitraryOverviews(); + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); + virtual GDALRasterBand *GetRasterSampleOverview( GUIntBig ); + virtual CPLErr BuildOverviews( const char *, int, int *, + GDALProgressFunc, void * ); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ); + + virtual CPLErr GetHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc, void *pProgressData ); + + virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig ** ppanHistogram, + int bForce, + GDALProgressFunc, void *pProgressData); + virtual CPLErr SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram ); + + virtual GDALRasterAttributeTable *GetDefaultRAT(); + virtual CPLErr SetDefaultRAT( const GDALRasterAttributeTable * ); + + virtual GDALRasterBand *GetMaskBand(); + virtual int GetMaskFlags(); + virtual CPLErr CreateMaskBand( int nFlagsIn ); + + virtual CPLVirtualMem *GetVirtualMemAuto( GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ); + + void ReportError(CPLErr eErrClass, int err_no, const char *fmt, ...) CPL_PRINT_FUNC_FORMAT (4, 5); +}; + +/* ******************************************************************** */ +/* GDALAllValidMaskBand */ +/* ******************************************************************** */ + +class CPL_DLL GDALAllValidMaskBand : public GDALRasterBand +{ + protected: + virtual CPLErr IReadBlock( int, int, void * ); + + public: + GDALAllValidMaskBand( GDALRasterBand * ); + virtual ~GDALAllValidMaskBand(); + + virtual GDALRasterBand *GetMaskBand(); + virtual int GetMaskFlags(); +}; + +/* ******************************************************************** */ +/* GDALNoDataMaskBand */ +/* ******************************************************************** */ + +class CPL_DLL GDALNoDataMaskBand : public GDALRasterBand +{ + double dfNoDataValue; + GDALRasterBand *poParent; + + protected: + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing, GSpacing, GDALRasterIOExtraArg* psExtraArg ); + + public: + GDALNoDataMaskBand( GDALRasterBand * ); + virtual ~GDALNoDataMaskBand(); +}; + +/* ******************************************************************** */ +/* GDALNoDataValuesMaskBand */ +/* ******************************************************************** */ + +class CPL_DLL GDALNoDataValuesMaskBand : public GDALRasterBand +{ + double *padfNodataValues; + + protected: + virtual CPLErr IReadBlock( int, int, void * ); + + public: + GDALNoDataValuesMaskBand( GDALDataset * ); + virtual ~GDALNoDataValuesMaskBand(); +}; + +/* ******************************************************************** */ +/* GDALRescaledAlphaBand */ +/* ******************************************************************** */ + +class GDALRescaledAlphaBand : public GDALRasterBand +{ + GDALRasterBand *poParent; + void *pTemp; + + protected: + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing, GSpacing, GDALRasterIOExtraArg* psExtraArg ); + + public: + GDALRescaledAlphaBand( GDALRasterBand * ); + virtual ~GDALRescaledAlphaBand(); +}; + +/* ******************************************************************** */ +/* GDALDriver */ +/* ******************************************************************** */ + + +/** + * \brief Format specific driver. + * + * An instance of this class is created for each supported format, and + * manages information about the format. + * + * This roughly corresponds to a file format, though some + * drivers may be gateways to many formats through a secondary + * multi-library. + */ + +class CPL_DLL GDALDriver : public GDALMajorObject +{ + public: + GDALDriver(); + ~GDALDriver(); + + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + +/* -------------------------------------------------------------------- */ +/* Public C++ methods. */ +/* -------------------------------------------------------------------- */ + GDALDataset *Create( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ) CPL_WARN_UNUSED_RESULT; + + CPLErr Delete( const char * pszName ); + CPLErr Rename( const char * pszNewName, + const char * pszOldName ); + CPLErr CopyFiles( const char * pszNewName, + const char * pszOldName ); + + GDALDataset *CreateCopy( const char *, GDALDataset *, + int, char **, + GDALProgressFunc pfnProgress, + void * pProgressData ) CPL_WARN_UNUSED_RESULT; + +/* -------------------------------------------------------------------- */ +/* The following are semiprivate, not intended to be accessed */ +/* by anyone but the formats instantiating and populating the */ +/* drivers. */ +/* -------------------------------------------------------------------- */ + GDALDataset *(*pfnOpen)( GDALOpenInfo * ); + + GDALDataset *(*pfnCreate)( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ); + + CPLErr (*pfnDelete)( const char * pszName ); + + GDALDataset *(*pfnCreateCopy)( const char *, GDALDataset *, + int, char **, + GDALProgressFunc pfnProgress, + void * pProgressData ); + + void *pDriverData; + + void (*pfnUnloadDriver)(GDALDriver *); + + /* Return 1 if the passed file is certainly recognized by the driver */ + /* Return 0 if the passed file is certainly NOT recognized by the driver */ + /* Return -1 if the passed file may be or may not be recognized by the driver, + and that a potentially costly test must be done with pfnOpen */ + int (*pfnIdentify)( GDALOpenInfo * ); + + CPLErr (*pfnRename)( const char * pszNewName, + const char * pszOldName ); + CPLErr (*pfnCopyFiles)( const char * pszNewName, + const char * pszOldName ); + + /* For legacy OGR drivers */ + GDALDataset *(*pfnOpenWithDriverArg)( GDALDriver*, GDALOpenInfo * ); + GDALDataset *(*pfnCreateVectorOnly)( GDALDriver*, + const char * pszName, + char ** papszOptions ); + CPLErr (*pfnDeleteDataSource)( GDALDriver*, + const char * pszName ); + +/* -------------------------------------------------------------------- */ +/* Helper methods. */ +/* -------------------------------------------------------------------- */ + GDALDataset *DefaultCreateCopy( const char *, GDALDataset *, + int, char **, + GDALProgressFunc pfnProgress, + void * pProgressData ) CPL_WARN_UNUSED_RESULT; + static CPLErr DefaultCopyMasks( GDALDataset *poSrcDS, + GDALDataset *poDstDS, + int bStrict ); + static CPLErr QuietDelete( const char * pszName ); + + CPLErr DefaultRename( const char * pszNewName, + const char * pszOldName ); + CPLErr DefaultCopyFiles( const char * pszNewName, + const char * pszOldName ); +}; + +/* ******************************************************************** */ +/* GDALDriverManager */ +/* ******************************************************************** */ + +/** + * Class for managing the registration of file format drivers. + * + * Use GetGDALDriverManager() to fetch the global singleton instance of + * this class. + */ + +class CPL_DLL GDALDriverManager : public GDALMajorObject +{ + int nDrivers; + GDALDriver **papoDrivers; + std::map oMapNameToDrivers; + + GDALDriver *GetDriver_unlocked( int iDriver ) + { return (iDriver >= 0 && iDriver < nDrivers) ? papoDrivers[iDriver] : NULL; } + + GDALDriver *GetDriverByName_unlocked( const char * pszName ) + { return oMapNameToDrivers[CPLString(pszName).toupper()]; } + + public: + GDALDriverManager(); + ~GDALDriverManager(); + + int GetDriverCount( void ); + GDALDriver *GetDriver( int ); + GDALDriver *GetDriverByName( const char * ); + + int RegisterDriver( GDALDriver * ); + void DeregisterDriver( GDALDriver * ); + + void AutoLoadDrivers(); + void AutoSkipDrivers(); +}; + +CPL_C_START +GDALDriverManager CPL_DLL * GetGDALDriverManager( void ); +CPL_C_END + +/* ******************************************************************** */ +/* GDALAsyncReader */ +/* ******************************************************************** */ + +/** + * Class used as a session object for asynchronous requests. They are + * created with GDALDataset::BeginAsyncReader(), and destroyed with + * GDALDataset::EndAsyncReader(). + */ +class CPL_DLL GDALAsyncReader +{ + protected: + GDALDataset* poDS; + int nXOff; + int nYOff; + int nXSize; + int nYSize; + void * pBuf; + int nBufXSize; + int nBufYSize; + GDALDataType eBufType; + int nBandCount; + int* panBandMap; + int nPixelSpace; + int nLineSpace; + int nBandSpace; + + public: + GDALAsyncReader(); + virtual ~GDALAsyncReader(); + + GDALDataset* GetGDALDataset() {return poDS;} + int GetXOffset() {return nXOff;} + int GetYOffset() {return nYOff;} + int GetXSize() {return nXSize;} + int GetYSize() {return nYSize;} + void * GetBuffer() {return pBuf;} + int GetBufferXSize() {return nBufXSize;} + int GetBufferYSize() {return nBufYSize;} + GDALDataType GetBufferType() {return eBufType;} + int GetBandCount() {return nBandCount;} + int* GetBandMap() {return panBandMap;} + int GetPixelSpace() {return nPixelSpace;} + int GetLineSpace() {return nLineSpace;} + int GetBandSpace() {return nBandSpace;} + + virtual GDALAsyncStatusType + GetNextUpdatedRegion(double dfTimeout, + int* pnBufXOff, int* pnBufYOff, + int* pnBufXSize, int* pnBufYSize) = 0; + virtual int LockBuffer( double dfTimeout = -1.0 ); + virtual void UnlockBuffer(); +}; + +/* ==================================================================== */ +/* An assortment of overview related stuff. */ +/* ==================================================================== */ + +/* Not a public symbol for the moment */ +CPLErr +GDALRegenerateOverviewsMultiBand(int nBands, GDALRasterBand** papoSrcBands, + int nOverviews, + GDALRasterBand*** papapoOverviewBands, + const char * pszResampling, + GDALProgressFunc pfnProgress, void * pProgressData ); + +typedef CPLErr (*GDALResampleFunction) + ( double dfXRatioDstToSrc, + double dfYRatioDstToSrc, + double dfSrcXDelta, + double dfSrcYDelta, + GDALDataType eWrkDataType, + void * pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + int nDstXOff, int nDstXOff2, + int nDstYOff, int nDstYOff2, + GDALRasterBand * poOverview, + const char * pszResampling, + int bHasNoData, float fNoDataValue, + GDALColorTable* poColorTable, + GDALDataType eSrcDataType); + +GDALResampleFunction GDALGetResampleFunction(const char* pszResampling, + int* pnRadius); +GDALDataType GDALGetOvrWorkDataType(const char* pszResampling, + GDALDataType eSrcDataType); + +CPL_C_START + +#ifndef WIN32CE + +CPLErr CPL_DLL +HFAAuxBuildOverviews( const char *pszOvrFilename, GDALDataset *poParentDS, + GDALDataset **ppoDS, + int nBands, int *panBandList, + int nNewOverviews, int *panNewOverviewList, + const char *pszResampling, + GDALProgressFunc pfnProgress, + void *pProgressData ); + +#endif /* WIN32CE */ + +CPLErr CPL_DLL +GTIFFBuildOverviews( const char * pszFilename, + int nBands, GDALRasterBand **papoBandList, + int nOverviews, int * panOverviewList, + const char * pszResampling, + GDALProgressFunc pfnProgress, void * pProgressData ); + +CPLErr CPL_DLL +GDALDefaultBuildOverviews( GDALDataset *hSrcDS, const char * pszBasename, + const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, void * pProgressData); + +int CPL_DLL GDALBandGetBestOverviewLevel(GDALRasterBand* poBand, + int &nXOff, int &nYOff, + int &nXSize, int &nYSize, + int nBufXSize, int nBufYSize) CPL_WARN_DEPRECATED("Use GDALBandGetBestOverviewLevel2 instead"); +int CPL_DLL GDALBandGetBestOverviewLevel2(GDALRasterBand* poBand, + int &nXOff, int &nYOff, + int &nXSize, int &nYSize, + int nBufXSize, int nBufYSize, + GDALRasterIOExtraArg* psExtraArg); + +int CPL_DLL GDALOvLevelAdjust( int nOvLevel, int nXSize ) CPL_WARN_DEPRECATED("Use GDALOvLevelAdjust2 instead"); +int CPL_DLL GDALOvLevelAdjust2( int nOvLevel, int nXSize, int nYSize ); +int CPL_DLL GDALComputeOvFactor( int nOvrXSize, int nRasterXSize, + int nOvrYSize, int nRasterYSize ); + +GDALDataset CPL_DLL * +GDALFindAssociatedAuxFile( const char *pszBasefile, GDALAccess eAccess, + GDALDataset *poDependentDS ); + +/* ==================================================================== */ +/* Misc functions. */ +/* ==================================================================== */ + +CPLErr CPL_DLL GDALParseGMLCoverage( CPLXMLNode *psTree, + int *pnXSize, int *pnYSize, + double *padfGeoTransform, + char **ppszProjection ); + +/* ==================================================================== */ +/* Infrastructure to check that dataset characteristics are valid */ +/* ==================================================================== */ + +int CPL_DLL GDALCheckDatasetDimensions( int nXSize, int nYSize ); +int CPL_DLL GDALCheckBandCount( int nBands, int bIsZeroAllowed ); + + +// Test if 2 floating point values match. Useful when comparing values +// stored as a string at some point. See #3573, #4183, #4506 +#define ARE_REAL_EQUAL(dfVal1, dfVal2) \ + (dfVal1 == dfVal2 || fabs(dfVal1 - dfVal2) < 1e-10 || (dfVal2 != 0 && fabs(1 - dfVal1 / dfVal2) < 1e-10 )) + +/* Internal use only */ + +/* CPL_DLL exported, but only for in-tree drivers that can be built as plugins */ +int CPL_DLL GDALReadWorldFile2( const char *pszBaseFilename, const char *pszExtension, + double *padfGeoTransform, char** papszSiblingFiles, + char** ppszWorldFileNameOut); +int GDALReadTabFile2( const char * pszBaseFilename, + double *padfGeoTransform, char **ppszWKT, + int *pnGCPCount, GDAL_GCP **ppasGCPs, + char** papszSiblingFiles, char** ppszTabFileNameOut ); + +void CPL_DLL GDALCopyRasterIOExtraArg(GDALRasterIOExtraArg* psDestArg, + GDALRasterIOExtraArg* psSrcArg); + +CPL_C_END + +void GDALNullifyOpenDatasetsList(); +CPLMutex** GDALGetphDMMutex(); +CPLMutex** GDALGetphDLMutex(); +void GDALNullifyProxyPoolSingleton(); +GDALDriver* GDALGetAPIPROXYDriver(); +void GDALSetResponsiblePIDForCurrentThread(GIntBig responsiblePID); +GIntBig GDALGetResponsiblePIDForCurrentThread(); + +CPLString GDALFindAssociatedFile( const char *pszBasename, const char *pszExt, + char **papszSiblingFiles, int nFlags ); + +CPLErr EXIFExtractMetadata(char**& papszMetadata, + void *fpL, int nOffset, + int bSwabflag, int nTIFFHEADER, + int& nExifOffset, int& nInterOffset, int& nGPSOffset); + +int GDALValidateOpenOptions( GDALDriverH hDriver, + const char* const* papszOptionOptions); +int GDALValidateOptions( const char* pszOptionList, + const char* const* papszOptionsToValidate, + const char* pszErrorMessageOptionType, + const char* pszErrorMessageContainerName); + +GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char* pszResampling); + +void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg* psExtraArg, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize); + +/* CPL_DLL exported, but only for gdalwarp */ +GDALDataset CPL_DLL* GDALCreateOverviewDataset(GDALDataset* poDS, int nOvrLevel, + int bThisLevelOnly, int bOwnDS); + +#define DIV_ROUND_UP(a, b) ( ((a) % (b)) == 0 ? ((a) / (b)) : (((a) / (b)) + 1) ) + +// Number of data samples that will be used to compute approximate statistics +// (minimum value, maximum value, etc.) +#define GDALSTAT_APPROX_NUMSAMPLES 2500 + +CPL_C_START +/* Caution: for technical reason this declaration is duplicated in gdal_crs.c */ +/* so any signature change should be reflected there too */ +void GDALSerializeGCPListToXML( CPLXMLNode* psParentNode, + GDAL_GCP* pasGCPList, + int nGCPCount, + const char* pszGCPProjection ); +void GDALDeserializeGCPListFromXML( CPLXMLNode* psGCPList, + GDAL_GCP** ppasGCPList, + int* pnGCPCount, + char** ppszGCPProjection ); +CPL_C_END + +void GDALSerializeOpenOptionsToXML( CPLXMLNode* psParentNode, char** papszOpenOptions); +char** GDALDeserializeOpenOptionsFromXML( CPLXMLNode* psParentNode ); + +int GDALCanFileAcceptSidecarFile(const char* pszFilename); + +#endif /* ndef GDAL_PRIV_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/gcore/gdal_proxy.h b/bazaar/plugin/gdal/gcore/gdal_proxy.h new file mode 100644 index 000000000..a00705676 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdal_proxy.h @@ -0,0 +1,384 @@ +/****************************************************************************** + * $Id: gdal_proxy.h 28899 2015-04-14 09:27:00Z rouault $ + * + * Project: GDAL Core + * Purpose: GDAL Core C++/Private declarations + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_PROXY_H_INCLUDED +#define GDAL_PROXY_H_INCLUDED + +#include "gdal.h" + +#ifdef __cplusplus + +#include "gdal_priv.h" +#include "cpl_hash_set.h" + +/* ******************************************************************** */ +/* GDALProxyDataset */ +/* ******************************************************************** */ + +class CPL_DLL GDALProxyDataset : public GDALDataset +{ + protected: + virtual GDALDataset *RefUnderlyingDataset() = 0; + virtual void UnrefUnderlyingDataset(GDALDataset* poUnderlyingDataset); + + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, GSpacing, GSpacing, GSpacing, + GDALRasterIOExtraArg* psExtraArg ); + public: + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ); + + virtual void FlushCache(void); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual void *GetInternalHandle( const char * ); + virtual GDALDriver *GetDriver(void); + virtual char **GetFileList(void); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + char **papszOptions ); + + virtual CPLErr CreateMaskBand( int nFlags ); + +}; + +/* ******************************************************************** */ +/* GDALProxyRasterBand */ +/* ******************************************************************** */ + +class CPL_DLL GDALProxyRasterBand : public GDALRasterBand +{ + protected: + virtual GDALRasterBand* RefUnderlyingRasterBand() = 0; + virtual void UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand); + + virtual CPLErr IReadBlock( int, int, void * ); + virtual CPLErr IWriteBlock( int, int, void * ); + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + GSpacing, GSpacing, GDALRasterIOExtraArg* psExtraArg ); + + public: + + virtual char **GetMetadataDomainList(); + virtual char **GetMetadata( const char * pszDomain ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ); + virtual CPLErr FlushCache(); + virtual char **GetCategoryNames(); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum(int *pbSuccess = NULL ); + virtual double GetOffset( int *pbSuccess = NULL ); + virtual double GetScale( int *pbSuccess = NULL ); + virtual const char *GetUnitType(); + virtual GDALColorInterp GetColorInterpretation(); + virtual GDALColorTable *GetColorTable(); + virtual CPLErr Fill(double dfRealValue, double dfImaginaryValue = 0); + + virtual CPLErr SetCategoryNames( char ** ); + virtual CPLErr SetNoDataValue( double ); + virtual CPLErr SetColorTable( GDALColorTable * ); + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + virtual CPLErr SetOffset( double ); + virtual CPLErr SetScale( double ); + virtual CPLErr SetUnitType( const char * ); + + virtual CPLErr GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *padfStdDev ); + virtual CPLErr ComputeStatistics( int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc, void *pProgressData ); + virtual CPLErr SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ); + virtual CPLErr ComputeRasterMinMax( int, double* ); + + virtual int HasArbitraryOverviews(); + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); + virtual GDALRasterBand *GetRasterSampleOverview( GUIntBig ); + virtual CPLErr BuildOverviews( const char *, int, int *, + GDALProgressFunc, void * ); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ); + + virtual CPLErr GetHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc, void *pProgressData ); + + virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig ** ppanHistogram, + int bForce, + GDALProgressFunc, void *pProgressData); + virtual CPLErr SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram ); + + virtual GDALRasterAttributeTable *GetDefaultRAT(); + virtual CPLErr SetDefaultRAT( const GDALRasterAttributeTable * ); + + virtual GDALRasterBand *GetMaskBand(); + virtual int GetMaskFlags(); + virtual CPLErr CreateMaskBand( int nFlags ); + + virtual CPLVirtualMem *GetVirtualMemAuto( GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ); +}; + + +/* ******************************************************************** */ +/* GDALProxyPoolDataset */ +/* ******************************************************************** */ + +typedef struct _GDALProxyPoolCacheEntry GDALProxyPoolCacheEntry; +class GDALProxyPoolRasterBand; + +class CPL_DLL GDALProxyPoolDataset : public GDALProxyDataset +{ + private: + GIntBig responsiblePID; + + char *pszProjectionRef; + double adfGeoTransform[6]; + int bHasSrcProjection; + int bHasSrcGeoTransform; + char *pszGCPProjection; + int nGCPCount; + GDAL_GCP *pasGCPList; + CPLHashSet *metadataSet; + CPLHashSet *metadataItemSet; + + GDALProxyPoolCacheEntry* cacheEntry; + + protected: + virtual GDALDataset *RefUnderlyingDataset(); + virtual void UnrefUnderlyingDataset(GDALDataset* poUnderlyingDataset); + + friend class GDALProxyPoolRasterBand; + + public: + GDALProxyPoolDataset(const char* pszSourceDatasetDescription, + int nRasterXSize, int nRasterYSize, + GDALAccess eAccess = GA_ReadOnly, + int bShared = FALSE, + const char * pszProjectionRef = NULL, + double * padfGeoTransform = NULL); + ~GDALProxyPoolDataset(); + + void SetOpenOptions(char** papszOpenOptions); + void AddSrcBandDescription( GDALDataType eDataType, int nBlockXSize, int nBlockYSize); + + virtual const char *GetProjectionRef(void); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + /* Special behaviour for the following methods : they return a pointer */ + /* data type, that must be cached by the proxy, so it doesn't become invalid */ + /* when the underlying object get closed */ + virtual char **GetMetadata( const char * pszDomain ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain ); + + virtual void *GetInternalHandle( const char * pszRequest ); + + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); +}; + +/* ******************************************************************** */ +/* GDALProxyPoolRasterBand */ +/* ******************************************************************** */ + +class GDALProxyPoolOverviewRasterBand; +class GDALProxyPoolMaskBand; + +class CPL_DLL GDALProxyPoolRasterBand : public GDALProxyRasterBand +{ + private: + CPLHashSet *metadataSet; + CPLHashSet *metadataItemSet; + char *pszUnitType; + char **papszCategoryNames; + GDALColorTable *poColorTable; + + int nSizeProxyOverviewRasterBand; + GDALProxyPoolOverviewRasterBand **papoProxyOverviewRasterBand; + GDALProxyPoolMaskBand *poProxyMaskBand; + + void Init(); + + protected: + virtual GDALRasterBand* RefUnderlyingRasterBand(); + virtual void UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand); + + friend class GDALProxyPoolOverviewRasterBand; + friend class GDALProxyPoolMaskBand; + + public: + GDALProxyPoolRasterBand(GDALProxyPoolDataset* poDS, int nBand, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize); + GDALProxyPoolRasterBand(GDALProxyPoolDataset* poDS, + GDALRasterBand* poUnderlyingRasterBand); + ~GDALProxyPoolRasterBand(); + + void AddSrcMaskBandDescription( GDALDataType eDataType, int nBlockXSize, int nBlockYSize); + + /* Special behaviour for the following methods : they return a pointer */ + /* data type, that must be cached by the proxy, so it doesn't become invalid */ + /* when the underlying object get closed */ + virtual char **GetMetadata( const char * pszDomain ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain ); + virtual char **GetCategoryNames(); + virtual const char *GetUnitType(); + virtual GDALColorTable *GetColorTable(); + virtual GDALRasterBand *GetOverview(int); + virtual GDALRasterBand *GetRasterSampleOverview( GUIntBig nDesiredSamples); // TODO + virtual GDALRasterBand *GetMaskBand(); + +}; + +/* ******************************************************************** */ +/* GDALProxyPoolOverviewRasterBand */ +/* ******************************************************************** */ + +class GDALProxyPoolOverviewRasterBand : public GDALProxyPoolRasterBand +{ + private: + GDALProxyPoolRasterBand *poMainBand; + int nOverviewBand; + + GDALRasterBand *poUnderlyingMainRasterBand; + int nRefCountUnderlyingMainRasterBand; + + protected: + virtual GDALRasterBand* RefUnderlyingRasterBand(); + virtual void UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand); + + public: + GDALProxyPoolOverviewRasterBand(GDALProxyPoolDataset* poDS, + GDALRasterBand* poUnderlyingOverviewBand, + GDALProxyPoolRasterBand* poMainBand, + int nOverviewBand); + ~GDALProxyPoolOverviewRasterBand(); +}; + +/* ******************************************************************** */ +/* GDALProxyPoolMaskBand */ +/* ******************************************************************** */ + +class GDALProxyPoolMaskBand : public GDALProxyPoolRasterBand +{ + private: + GDALProxyPoolRasterBand *poMainBand; + + GDALRasterBand *poUnderlyingMainRasterBand; + int nRefCountUnderlyingMainRasterBand; + + protected: + virtual GDALRasterBand* RefUnderlyingRasterBand(); + virtual void UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand); + + public: + GDALProxyPoolMaskBand(GDALProxyPoolDataset* poDS, + GDALRasterBand* poUnderlyingMaskBand, + GDALProxyPoolRasterBand* poMainBand); + GDALProxyPoolMaskBand(GDALProxyPoolDataset* poDS, + GDALProxyPoolRasterBand* poMainBand, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize); + ~GDALProxyPoolMaskBand(); +}; + +#endif + + +/* ******************************************************************** */ +/* C types and methods declarations */ +/* ******************************************************************** */ + + +CPL_C_START + +typedef struct GDALProxyPoolDatasetHS *GDALProxyPoolDatasetH; + +GDALProxyPoolDatasetH CPL_DLL GDALProxyPoolDatasetCreate(const char* pszSourceDatasetDescription, + int nRasterXSize, int nRasterYSize, + GDALAccess eAccess, int bShared, + const char * pszProjectionRef, + double * padfGeoTransform); + +void CPL_DLL GDALProxyPoolDatasetDelete(GDALProxyPoolDatasetH hProxyPoolDataset); + +void CPL_DLL GDALProxyPoolDatasetAddSrcBandDescription( GDALProxyPoolDatasetH hProxyPoolDataset, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize); + +CPL_C_END + +#endif /* GDAL_PROXY_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/gcore/gdal_rat.cpp b/bazaar/plugin/gdal/gcore/gdal_rat.cpp new file mode 100644 index 000000000..a5830d68d --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdal_rat.cpp @@ -0,0 +1,1933 @@ +/****************************************************************************** + * $Id: gdal_rat.cpp 29243 2015-05-24 15:53:26Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALRasterAttributeTable and related classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_rat.h" +#include "json.h" +#include "ogrgeojsonwriter.h" + +CPL_CVSID("$Id: gdal_rat.cpp 29243 2015-05-24 15:53:26Z rouault $"); + +/** + * \class GDALRasterAttributeTable + * + * The GDALRasterAttributeTable (or RAT) class is used to encapsulate a table + * used to provide attribute information about pixel values. Each row + * in the table applies to a range of pixel values (or a single value in + * some cases), and might have attributes such as the histogram count for + * that range, the color pixels of that range should be drawn names of classes + * or any other generic information. + * + * Raster attribute tables can be used to represent histograms, color tables, + * and classification information. + * + * Each column in a raster attribute table has a name, a type (integer, + * floating point or string), and a GDALRATFieldUsage. The usage distinguishes + * columns with particular understood purposes (such as color, histogram + * count, name) and columns that have specific purposes not understood by + * the library (long label, suitability_for_growing_wheat, etc). + * + * In the general case each row has a column indicating the minimum pixel + * values falling into that category, and a column indicating the maximum + * pixel value. These are indicated with usage values of GFU_Min, and + * GFU_Max. In other cases where each row is a discrete pixel value, one + * column of usage GFU_MinMax can be used. + * + * In other cases all the categories are of equal size and regularly spaced + * and the categorization information can be determine just by knowing the + * value at which the categories start, and the size of a category. This + * is called "Linear Binning" and the information is kept specially on + * the raster attribute table as a whole. + * + * RATs are normally associated with GDALRasterBands and be be queried + * using the GDALRasterBand::GetDefaultRAT() method. + */ + +/************************************************************************/ +/* ~GDALRasterAttributeTable() */ +/* */ +/* Virtual Destructor */ +/************************************************************************/ + +GDALRasterAttributeTable::~GDALRasterAttributeTable() +{ + +} + +/************************************************************************/ +/* ValuesIO() */ +/* */ +/* Default Implementations */ +/************************************************************************/ + +/** + * \brief Read or Write a block of doubles to/from the Attribute Table. + * + * This method is the same as the C function GDALRATValuesIOAsDouble(). + * + * @param eRWFlag Either GF_Read or GF_Write + * @param iField column of the Attribute Table + * @param iStartRow start row to start reading/writing (zero based) + * @param iLength number of rows to read or write + * @param pdfData pointer to array of doubles to read/write. Should be at least iLength long. + * + * @return CE_None or CE_Failure if iStartRow + iLength greater than number of rows in table. + */ + +CPLErr GDALRasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, double *pdfData) +{ + int iIndex; + + if( (iStartRow + iLength) > GetRowCount() ) + { + return CE_Failure; + } + + if( eRWFlag == GF_Read ) + { + for(iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++ ) + { + pdfData[iIndex] = GetValueAsDouble(iIndex, iField); + } + } + else + { + for(iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++ ) + { + SetValue(iIndex, iField, pdfData[iIndex]); + } + } + return CE_None; +} + +/************************************************************************/ +/* GDALRATValuesIOAsDouble() */ +/************************************************************************/ + +/** + * \brief Read or Write a block of doubles to/from the Attribute Table. + * + * This function is the same as the C++ method GDALRasterAttributeTable::ValuesIO() + */ +CPLErr CPL_STDCALL GDALRATValuesIOAsDouble( GDALRasterAttributeTableH hRAT, GDALRWFlag eRWFlag, + int iField, int iStartRow, int iLength, double *pdfData ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATValuesIOAsDouble", CE_Failure ); + + return ((GDALRasterAttributeTable *) hRAT)->ValuesIO(eRWFlag, iField, iStartRow, iLength, pdfData); +} + +/** + * \brief Read or Write a block of integers to/from the Attribute Table. + * + * This method is the same as the C function GDALRATValuesIOAsInteger(). + * + * @param eRWFlag Either GF_Read or GF_Write + * @param iField column of the Attribute Table + * @param iStartRow start row to start reading/writing (zero based) + * @param iLength number of rows to read or write + * @param pnData pointer to array of ints to read/write. Should be at least iLength long. + * + * @return CE_None or CE_Failure if iStartRow + iLength greater than number of rows in table. + */ + +CPLErr GDALRasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, int *pnData) +{ + int iIndex; + + if( (iStartRow + iLength) > GetRowCount() ) + { + return CE_Failure; + } + + if( eRWFlag == GF_Read ) + { + for(iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++ ) + { + pnData[iIndex] = GetValueAsInt(iIndex, iField); + } + } + else + { + for(iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++ ) + { + SetValue(iIndex, iField, pnData[iIndex]); + } + } + return CE_None; +} + +/************************************************************************/ +/* GDALRATValuesIOAsInteger() */ +/************************************************************************/ + +/** + * \brief Read or Write a block of ints to/from the Attribute Table. + * + * This function is the same as the C++ method GDALRasterAttributeTable::ValuesIO() + */ +CPLErr CPL_STDCALL GDALRATValuesIOAsInteger( GDALRasterAttributeTableH hRAT, GDALRWFlag eRWFlag, + int iField, int iStartRow, int iLength, int *pnData) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATValuesIOAsInteger", CE_Failure ); + + return ((GDALRasterAttributeTable *) hRAT)->ValuesIO(eRWFlag, iField, iStartRow, iLength, pnData); +} + +/** + * \brief Read or Write a block of strings to/from the Attribute Table. + * + * This method is the same as the C function GDALRATValuesIOAsString(). + * When reading, papszStrList must be already allocated to the correct size. + * The caller is expected to call CPLFree on each read string. + * + * @param eRWFlag Either GF_Read or GF_Write + * @param iField column of the Attribute Table + * @param iStartRow start row to start reading/writing (zero based) + * @param iLength number of rows to read or write + * @param papszStrList pointer to array of strings to read/write. Should be at least iLength long. + * + * @return CE_None or CE_Failure if iStartRow + iLength greater than number of rows in table. + */ + +CPLErr GDALRasterAttributeTable::ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, char **papszStrList) +{ + int iIndex; + + if( (iStartRow + iLength) > GetRowCount() ) + { + return CE_Failure; + } + + if( eRWFlag == GF_Read ) + { + for(iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++ ) + { + papszStrList[iIndex] = VSIStrdup(GetValueAsString(iIndex, iField)); + } + } + else + { + for(iIndex = iStartRow; iIndex < (iStartRow + iLength); iIndex++ ) + { + SetValue(iIndex, iField, papszStrList[iIndex]); + } + } + return CE_None; +} + +/************************************************************************/ +/* GDALRATValuesIOAsString() */ +/************************************************************************/ + +/** + * \brief Read or Write a block of strings to/from the Attribute Table. + * + * This function is the same as the C++ method GDALRasterAttributeTable::ValuesIO() + */ +CPLErr CPL_STDCALL GDALRATValuesIOAsString( GDALRasterAttributeTableH hRAT, GDALRWFlag eRWFlag, + int iField, int iStartRow, int iLength, char **papszStrList) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATValuesIOAsString", CE_Failure ); + + return ((GDALRasterAttributeTable *) hRAT)->ValuesIO(eRWFlag, iField, iStartRow, iLength, papszStrList); +} + +/************************************************************************/ +/* SetRowCount() */ +/************************************************************************/ + +/** + * \brief Set row count. + * + * Resizes the table to include the indicated number of rows. Newly created + * rows will be initialized to their default values - "" for strings, + * and zero for numeric fields. + * + * This method is the same as the C function GDALRATSetRowCount(). + * + * @param nNewCount the new number of rows. + */ + +void GDALRasterAttributeTable::SetRowCount( CPL_UNUSED int nNewCount ) +{ +} + +/************************************************************************/ +/* GDALRATSetRowCount() */ +/************************************************************************/ + +/** + * \brief Set row count. + * + * This function is the same as the C++ method GDALRasterAttributeTable::SetRowCount() + */ +void CPL_STDCALL +GDALRATSetRowCount( GDALRasterAttributeTableH hRAT, int nNewCount ) + +{ + VALIDATE_POINTER0( hRAT, "GDALRATSetRowCount" ); + + ((GDALRasterAttributeTable *) hRAT)->SetRowCount( nNewCount ); +} + +/************************************************************************/ +/* GetRowOfValue() */ +/************************************************************************/ + +/** + * \brief Get row for pixel value. + * + * Given a raw pixel value, the raster attribute table is scanned to + * determine which row in the table applies to the pixel value. The + * row index is returned. + * + * This method is the same as the C function GDALRATGetRowOfValue(). + * + * @param dfValue the pixel value. + * + * @return the row index or -1 if no row is appropriate. + */ + +int GDALRasterAttributeTable::GetRowOfValue( CPL_UNUSED double dfValue ) const +{ + return -1; +} + +/************************************************************************/ +/* GDALRATGetRowOfValue() */ +/************************************************************************/ + +/** + * \brief Get row for pixel value. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetRowOfValue() + */ +int CPL_STDCALL +GDALRATGetRowOfValue( GDALRasterAttributeTableH hRAT, double dfValue ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetRowOfValue", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetRowOfValue( dfValue ); +} + +/************************************************************************/ +/* GetRowOfValue() */ +/* */ +/* Int arg for now just converted to double. Perhaps we will */ +/* handle this in a special way some day? */ +/************************************************************************/ + +int GDALRasterAttributeTable::GetRowOfValue( int nValue ) const + +{ + return GetRowOfValue( (double) nValue ); +} + +/************************************************************************/ +/* CreateColumn() */ +/************************************************************************/ + +/** + * \brief Create new column. + * + * If the table already has rows, all row values for the new column will + * be initialized to the default value ("", or zero). The new column is + * always created as the last column, can will be column (field) + * "GetColumnCount()-1" after CreateColumn() has completed successfully. + * + * This method is the same as the C function GDALRATCreateColumn(). + * + * @param pszFieldName the name of the field to create. + * @param eFieldType the field type (integer, double or string). + * @param eFieldUsage the field usage, GFU_Generic if not known. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr GDALRasterAttributeTable::CreateColumn( CPL_UNUSED const char *pszFieldName, + CPL_UNUSED GDALRATFieldType eFieldType, + CPL_UNUSED GDALRATFieldUsage eFieldUsage ) +{ + return CE_Failure; +} + +/************************************************************************/ +/* GDALRATCreateColumn() */ +/************************************************************************/ + +/** + * \brief Create new column. + * + * This function is the same as the C++ method GDALRasterAttributeTable::CreateColumn() + */ +CPLErr CPL_STDCALL GDALRATCreateColumn( GDALRasterAttributeTableH hRAT, + const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATCreateColumn", CE_Failure ); + + return ((GDALRasterAttributeTable *) hRAT)->CreateColumn( pszFieldName, + eFieldType, + eFieldUsage ); +} + +/************************************************************************/ +/* SetLinearBinning() */ +/************************************************************************/ + +/** + * \brief Set linear binning information. + * + * For RATs with equal sized categories (in pixel value space) that are + * evenly spaced, this method may be used to associate the linear binning + * information with the table. + * + * This method is the same as the C function GDALRATSetLinearBinning(). + * + * @param dfRow0MinIn the lower bound (pixel value) of the first category. + * @param dfBinSizeIn the width of each category (in pixel value units). + * + * @return CE_None on success or CE_Failure on failure. + */ + +CPLErr GDALRasterAttributeTable::SetLinearBinning( CPL_UNUSED double dfRow0MinIn, + CPL_UNUSED double dfBinSizeIn ) +{ + return CE_Failure; +} + +/************************************************************************/ +/* GDALRATSetLinearBinning() */ +/************************************************************************/ + +/** + * \brief Set linear binning information. + * + * This function is the same as the C++ method GDALRasterAttributeTable::SetLinearBinning() + */ +CPLErr CPL_STDCALL +GDALRATSetLinearBinning( GDALRasterAttributeTableH hRAT, + double dfRow0Min, double dfBinSize ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATSetLinearBinning", CE_Failure ); + + return ((GDALRasterAttributeTable *) hRAT)->SetLinearBinning( + dfRow0Min, dfBinSize ); +} + +/************************************************************************/ +/* GetLinearBinning() */ +/************************************************************************/ + +/** + * \brief Get linear binning information. + * + * Returns linear binning information if any is associated with the RAT. + * + * This method is the same as the C function GDALRATGetLinearBinning(). + * + * @param pdfRow0Min (out) the lower bound (pixel value) of the first category. + * @param pdfBinSize (out) the width of each category (in pixel value units). + * + * @return TRUE if linear binning information exists or FALSE if there is none. + */ + +int GDALRasterAttributeTable::GetLinearBinning( CPL_UNUSED double *pdfRow0Min, + CPL_UNUSED double *pdfBinSize ) const +{ + return FALSE; +} + +/************************************************************************/ +/* GDALRATGetLinearBinning() */ +/************************************************************************/ + +/** + * \brief Get linear binning information. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetLinearBinning() + */ +int CPL_STDCALL +GDALRATGetLinearBinning( GDALRasterAttributeTableH hRAT, + double *pdfRow0Min, double *pdfBinSize ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetLinearBinning", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetLinearBinning( + pdfRow0Min, pdfBinSize ); +} + +/************************************************************************/ +/* Serialize() */ +/************************************************************************/ + +CPLXMLNode *GDALRasterAttributeTable::Serialize() const + +{ + CPLXMLNode *psTree = NULL; + CPLXMLNode *psRow = NULL; + + if( ( GetColumnCount() == 0 ) && ( GetRowCount() == 0 ) ) + return NULL; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "GDALRasterAttributeTable" ); + +/* -------------------------------------------------------------------- */ +/* Add attributes with regular binning info if appropriate. */ +/* -------------------------------------------------------------------- */ + char szValue[128]; + double dfRow0Min, dfBinSize; + + if( GetLinearBinning(&dfRow0Min, &dfBinSize) ) + { + CPLsprintf( szValue, "%.16g", dfRow0Min ); + CPLCreateXMLNode( + CPLCreateXMLNode( psTree, CXT_Attribute, "Row0Min" ), + CXT_Text, szValue ); + + CPLsprintf( szValue, "%.16g", dfBinSize ); + CPLCreateXMLNode( + CPLCreateXMLNode( psTree, CXT_Attribute, "BinSize" ), + CXT_Text, szValue ); + } + +/* -------------------------------------------------------------------- */ +/* Define each column. */ +/* -------------------------------------------------------------------- */ + int iCol; + int iColCount = GetColumnCount(); + + for( iCol = 0; iCol < iColCount; iCol++ ) + { + CPLXMLNode *psCol; + + psCol = CPLCreateXMLNode( psTree, CXT_Element, "FieldDefn" ); + + sprintf( szValue, "%d", iCol ); + CPLCreateXMLNode( + CPLCreateXMLNode( psCol, CXT_Attribute, "index" ), + CXT_Text, szValue ); + + CPLCreateXMLElementAndValue( psCol, "Name", + GetNameOfCol(iCol) ); + + sprintf( szValue, "%d", (int) GetTypeOfCol(iCol) ); + CPLCreateXMLElementAndValue( psCol, "Type", szValue ); + + sprintf( szValue, "%d", (int) GetUsageOfCol(iCol) ); + CPLCreateXMLElementAndValue( psCol, "Usage", szValue ); + } + +/* -------------------------------------------------------------------- */ +/* Write out each row. */ +/* -------------------------------------------------------------------- */ + int iRow; + int iRowCount = GetRowCount(); + CPLXMLNode *psTail = NULL; + + for( iRow = 0; iRow < iRowCount; iRow++ ) + { + psRow = CPLCreateXMLNode( NULL, CXT_Element, "Row" ); + if( psTail == NULL ) + CPLAddXMLChild( psTree, psRow ); + else + psTail->psNext = psRow; + psTail = psRow; + + sprintf( szValue, "%d", iRow ); + CPLCreateXMLNode( + CPLCreateXMLNode( psRow, CXT_Attribute, "index" ), + CXT_Text, szValue ); + + for( iCol = 0; iCol < iColCount; iCol++ ) + { + const char *pszValue = szValue; + + if( GetTypeOfCol(iCol) == GFT_Integer ) + sprintf( szValue, "%d", GetValueAsInt(iRow, iCol) ); + else if( GetTypeOfCol(iCol) == GFT_Real ) + CPLsprintf( szValue, "%.16g", GetValueAsDouble(iRow, iCol) ); + else + pszValue = GetValueAsString(iRow, iCol); + + CPLCreateXMLElementAndValue( psRow, "F", pszValue ); + } + } + + return psTree; +} + +/************************************************************************/ +/* SerializeJSON() */ +/************************************************************************/ + +void *GDALRasterAttributeTable::SerializeJSON() const + +{ + json_object *poRAT = json_object_new_object(); + + if( ( GetColumnCount() == 0 ) && ( GetRowCount() == 0 ) ) + return poRAT; + + +/* -------------------------------------------------------------------- */ +/* Add attributes with regular binning info if appropriate. */ +/* -------------------------------------------------------------------- */ + double dfRow0Min, dfBinSize; + json_object *poRow0Min, *poBinSize; + + if( GetLinearBinning(&dfRow0Min, &dfBinSize) ) + { + poRow0Min = json_object_new_double_with_precision( dfRow0Min, 16 ); + json_object_object_add( poRAT, "row0Min", poRow0Min ); + + poBinSize = json_object_new_double_with_precision( dfBinSize, 16 ); + json_object_object_add( poRAT, "binSize", poBinSize ); + } + +/* -------------------------------------------------------------------- */ +/* Define each column. */ +/* -------------------------------------------------------------------- */ + int iCol; + int iColCount = GetColumnCount(); + json_object *poFieldDefnArray = json_object_new_array(); + json_object *poFieldDefn, *poColumnIndex, *poName, *poType, *poUsage; + + for( iCol = 0; iCol < iColCount; iCol++ ) + { + poFieldDefn = json_object_new_object(); + + poColumnIndex = json_object_new_int( iCol ); + json_object_object_add( poFieldDefn, "index", poColumnIndex ); + + poName = json_object_new_string( GetNameOfCol(iCol) ); + json_object_object_add( poFieldDefn, "name", poName ); + + poType = json_object_new_int( (int) GetTypeOfCol(iCol) ); + json_object_object_add( poFieldDefn, "type", poType ); + + poUsage = json_object_new_int( (int) GetUsageOfCol(iCol) ); + json_object_object_add( poFieldDefn, "usage", poUsage ); + + json_object_array_add( poFieldDefnArray, poFieldDefn ); + } + + json_object_object_add( poRAT, "fieldDefn", poFieldDefnArray ); + +/* -------------------------------------------------------------------- */ +/* Write out each row. */ +/* -------------------------------------------------------------------- */ + int iRow; + int iRowCount = GetRowCount(); + json_object *poRowArray = json_object_new_array(); + json_object *poRow, *poRowIndex, *poFArray, *poF; + + for( iRow = 0; iRow < iRowCount; iRow++ ) + { + poRow = json_object_new_object(); + + poRowIndex = json_object_new_int(iRow); + json_object_object_add( poRow, "index", poRowIndex ); + + poFArray = json_object_new_array(); + + for( iCol = 0; iCol < iColCount; iCol++ ) + { + if( GetTypeOfCol(iCol) == GFT_Integer ) + poF = json_object_new_int( GetValueAsInt(iRow, iCol) ); + else if( GetTypeOfCol(iCol) == GFT_Real ) + poF = json_object_new_double_with_precision( GetValueAsDouble(iRow, iCol), 16 ); + else + poF = json_object_new_string( GetValueAsString(iRow, iCol) ); + + json_object_array_add( poFArray, poF ); + } + json_object_object_add( poRow, "f", poFArray ); + json_object_array_add( poRowArray, poRow ); + } + json_object_object_add( poRAT, "row", poRowArray ); + + return poRAT; +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr GDALRasterAttributeTable::XMLInit( CPLXMLNode *psTree, + const char * /*pszVRTPath*/ ) + +{ + CPLAssert( GetRowCount() == 0 && GetColumnCount() == 0 ); + +/* -------------------------------------------------------------------- */ +/* Linear binning. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLValue( psTree, "Row0Min", NULL ) + && CPLGetXMLValue( psTree, "BinSize", NULL ) ) + { + SetLinearBinning( CPLAtof(CPLGetXMLValue( psTree, "Row0Min","" )), + CPLAtof(CPLGetXMLValue( psTree, "BinSize","" )) ); + } + +/* -------------------------------------------------------------------- */ +/* Column definitions */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psChild; + + for( psChild = psTree->psChild; psChild != NULL; psChild = psChild->psNext) + { + if( psChild->eType == CXT_Element + && EQUAL(psChild->pszValue,"FieldDefn") ) + { + CreateColumn( + CPLGetXMLValue( psChild, "Name", "" ), + (GDALRATFieldType) atoi(CPLGetXMLValue( psChild, "Type", "1" )), + (GDALRATFieldUsage) atoi(CPLGetXMLValue( psChild, "Usage","0"))); + } + } + +/* -------------------------------------------------------------------- */ +/* Row data. */ +/* -------------------------------------------------------------------- */ + for( psChild = psTree->psChild; psChild != NULL; psChild = psChild->psNext) + { + if( psChild->eType == CXT_Element + && EQUAL(psChild->pszValue,"Row") ) + { + int iRow = atoi(CPLGetXMLValue(psChild,"index","0")); + int iField = 0; + CPLXMLNode *psF; + + for( psF = psChild->psChild; psF != NULL; psF = psF->psNext ) + { + if( psF->eType != CXT_Element || !EQUAL(psF->pszValue,"F") ) + continue; + + if( psF->psChild != NULL && psF->psChild->eType == CXT_Text ) + SetValue( iRow, iField++, psF->psChild->pszValue ); + else + SetValue( iRow, iField++, "" ); + } + } + } + + return CE_None; +} + + +/************************************************************************/ +/* InitializeFromColorTable() */ +/************************************************************************/ + +/** + * \brief Initialize from color table. + * + * This method will setup a whole raster attribute table based on the + * contents of the passed color table. The Value (GFU_MinMax), + * Red (GFU_Red), Green (GFU_Green), Blue (GFU_Blue), and Alpha (GFU_Alpha) + * fields are created, and a row is set for each entry in the color table. + * + * The raster attribute table must be empty before calling + * InitializeFromColorTable(). + * + * The Value fields are set based on the implicit assumption with color + * tables that entry 0 applies to pixel value 0, 1 to 1, etc. + * + * This method is the same as the C function GDALRATInitializeFromColorTable(). + * + * @param poTable the color table to copy from. + * + * @return CE_None on success or CE_Failure if something goes wrong. + */ + +CPLErr GDALRasterAttributeTable::InitializeFromColorTable( + const GDALColorTable *poTable ) + +{ + int iRow; + + if( GetRowCount() > 0 || GetColumnCount() > 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Raster Attribute Table not empty in InitializeFromColorTable()" ); + return CE_Failure; + } + + SetLinearBinning( 0.0, 1.0 ); + CreateColumn( "Value", GFT_Integer, GFU_MinMax ); + CreateColumn( "Red", GFT_Integer, GFU_Red ); + CreateColumn( "Green", GFT_Integer, GFU_Green ); + CreateColumn( "Blue", GFT_Integer, GFU_Blue ); + CreateColumn( "Alpha", GFT_Integer, GFU_Alpha ); + + SetRowCount( poTable->GetColorEntryCount() ); + + for( iRow = 0; iRow < poTable->GetColorEntryCount(); iRow++ ) + { + GDALColorEntry sEntry; + + poTable->GetColorEntryAsRGB( iRow, &sEntry ); + + SetValue( iRow, 0, iRow ); + SetValue( iRow, 1, sEntry.c1 ); + SetValue( iRow, 2, sEntry.c2 ); + SetValue( iRow, 3, sEntry.c3 ); + SetValue( iRow, 4, sEntry.c4 ); + } + + return CE_None; +} + +/************************************************************************/ +/* GDALRATInitializeFromColorTable() */ +/************************************************************************/ + +/** + * \brief Initialize from color table. + * + * This function is the same as the C++ method GDALRasterAttributeTable::InitializeFromColorTable() + */ +CPLErr CPL_STDCALL +GDALRATInitializeFromColorTable( GDALRasterAttributeTableH hRAT, + GDALColorTableH hCT ) + + +{ + VALIDATE_POINTER1( hRAT, "GDALRATInitializeFromColorTable", CE_Failure ); + + return ((GDALRasterAttributeTable *) hRAT)-> + InitializeFromColorTable( (GDALColorTable *) hCT ); +} + +/************************************************************************/ +/* TranslateToColorTable() */ +/************************************************************************/ + +/** + * \brief Translate to a color table. + * + * This method will attempt to create a corresponding GDALColorTable from + * this raster attribute table. + * + * This method is the same as the C function GDALRATTranslateToColorTable(). + * + * @param nEntryCount The number of entries to produce (0 to nEntryCount-1), or -1 to auto-determine the number of entries. + * + * @return the generated color table or NULL on failure. + */ + +GDALColorTable *GDALRasterAttributeTable::TranslateToColorTable( + int nEntryCount ) + +{ +/* -------------------------------------------------------------------- */ +/* Establish which fields are red, green, blue and alpha. */ +/* -------------------------------------------------------------------- */ + int iRed, iGreen, iBlue, iAlpha; + + iRed = GetColOfUsage( GFU_Red ); + iGreen = GetColOfUsage( GFU_Green ); + iBlue = GetColOfUsage( GFU_Blue ); + iAlpha = GetColOfUsage( GFU_Alpha ); + + if( iRed == -1 || iGreen == -1 || iBlue == -1 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we aren't given an explicit number of values to scan for, */ +/* search for the maximum "max" value. */ +/* -------------------------------------------------------------------- */ + if( nEntryCount == -1 ) + { + int iRow; + int iMaxCol; + + iMaxCol = GetColOfUsage( GFU_Max ); + if( iMaxCol == -1 ) + iMaxCol = GetColOfUsage( GFU_MinMax ); + + if( iMaxCol == -1 || GetRowCount() == 0 ) + return NULL; + + for( iRow = 0; iRow < GetRowCount(); iRow++ ) + nEntryCount = MAX(nEntryCount,GetValueAsInt(iRow,iMaxCol)+1); + + if( nEntryCount < 0 ) + return NULL; + + // restrict our number of entries to something vaguely sensible + nEntryCount = MIN(65535,nEntryCount); + } + +/* -------------------------------------------------------------------- */ +/* Assign values to color table. */ +/* -------------------------------------------------------------------- */ + GDALColorTable *poCT = new GDALColorTable(); + int iEntry; + + for( iEntry = 0; iEntry < nEntryCount; iEntry++ ) + { + GDALColorEntry sColor; + int iRow = GetRowOfValue( iEntry ); + + if( iRow == -1 ) + { + sColor.c1 = sColor.c2 = sColor.c3 = sColor.c4 = 0; + } + else + { + sColor.c1 = (short) GetValueAsInt( iRow, iRed ); + sColor.c2 = (short) GetValueAsInt( iRow, iGreen ); + sColor.c3 = (short) GetValueAsInt( iRow, iBlue ); + if( iAlpha == -1 ) + sColor.c4 = 255; + else + sColor.c4 = (short) GetValueAsInt( iRow, iAlpha ); + } + + poCT->SetColorEntry( iEntry, &sColor ); + } + + return poCT; +} + +/************************************************************************/ +/* GDALRATInitializeFromColorTable() */ +/************************************************************************/ + +/** + * \brief Translate to a color table. + * + * This function is the same as the C++ method GDALRasterAttributeTable::TranslateToColorTable() + */ +GDALColorTableH CPL_STDCALL +GDALRATTranslateToColorTable( GDALRasterAttributeTableH hRAT, + int nEntryCount ) + + +{ + VALIDATE_POINTER1( hRAT, "GDALRATTranslateToColorTable", NULL ); + + return ((GDALRasterAttributeTable *) hRAT)-> + TranslateToColorTable( nEntryCount ); +} + + +/************************************************************************/ +/* DumpReadable() */ +/************************************************************************/ + +/** + * \brief Dump RAT in readable form. + * + * Currently the readable form is the XML encoding ... only barely + * readable. + * + * This method is the same as the C function GDALRATDumpReadable(). + * + * @param fp file to dump to or NULL for stdout. + */ + +void GDALRasterAttributeTable::DumpReadable( FILE * fp ) + +{ + CPLXMLNode *psTree = Serialize(); + char *pszXMLText = CPLSerializeXMLTree( psTree ); + + CPLDestroyXMLNode( psTree ); + + if( fp == NULL ) + fp = stdout; + + fprintf( fp, "%s\n", pszXMLText ); + + CPLFree( pszXMLText ); +} + +/************************************************************************/ +/* GDALRATDumpReadable() */ +/************************************************************************/ + +/** + * \brief Dump RAT in readable form. + * + * This function is the same as the C++ method GDALRasterAttributeTable::DumpReadable() + */ +void CPL_STDCALL +GDALRATDumpReadable( GDALRasterAttributeTableH hRAT, FILE *fp ) + +{ + VALIDATE_POINTER0( hRAT, "GDALRATDumpReadable" ); + + ((GDALRasterAttributeTable *) hRAT)->DumpReadable( fp ); +} + + +/* \class GDALDefaultRasterAttributeTable + * + * An implementation of GDALRasterAttributeTable that keeps + * all data in memory. This is the same as the implementation + * of GDALRasterAttributeTable in GDAL <= 1.10. + */ + +/************************************************************************/ +/* GDALDefaultRasterAttributeTable() */ +/* */ +/* Simple initialization constructor. */ +/************************************************************************/ + +//! Construct empty table. + +GDALDefaultRasterAttributeTable::GDALDefaultRasterAttributeTable() + +{ + bColumnsAnalysed = FALSE; + nMinCol = -1; + nMaxCol = -1; + bLinearBinning = FALSE; + dfRow0Min = -0.5; + dfBinSize = 1.0; + nRowCount = 0; +} + +/************************************************************************/ +/* GDALCreateRasterAttributeTable() */ +/************************************************************************/ + +/** + * \brief Construct empty table. + * + * This function is the same as the C++ method GDALDefaultRasterAttributeTable::GDALDefaultRasterAttributeTable() + */ +GDALRasterAttributeTableH CPL_STDCALL GDALCreateRasterAttributeTable() + +{ + return (GDALRasterAttributeTableH) (new GDALDefaultRasterAttributeTable()); +} + +/************************************************************************/ +/* GDALDefaultRasterAttributeTable() */ +/************************************************************************/ + +//! Copy constructor. + +GDALDefaultRasterAttributeTable::GDALDefaultRasterAttributeTable( + const GDALDefaultRasterAttributeTable &oOther ) + +{ + // We have tried to be careful to allow wholesale assignment + *this = oOther; +} + +/************************************************************************/ +/* ~GDALDefaultRasterAttributeTable() */ +/* */ +/* All magic done by magic by the container destructors. */ +/************************************************************************/ + +GDALDefaultRasterAttributeTable::~GDALDefaultRasterAttributeTable() + +{ +} + +/************************************************************************/ +/* GDALDestroyRasterAttributeTable() */ +/************************************************************************/ + +/** + * \brief Destroys a RAT. + * + * This function is the same as the C++ method GDALRasterAttributeTable::~GDALRasterAttributeTable() + */ +void CPL_STDCALL +GDALDestroyRasterAttributeTable( GDALRasterAttributeTableH hRAT ) + +{ + if( hRAT != NULL ) + delete static_cast(hRAT); +} + +/************************************************************************/ +/* AnalyseColumns() */ +/* */ +/* Internal method to work out which column to use for various */ +/* tasks. */ +/************************************************************************/ + +void GDALDefaultRasterAttributeTable::AnalyseColumns() + +{ + bColumnsAnalysed = TRUE; + + nMinCol = GetColOfUsage( GFU_Min ); + if( nMinCol == -1 ) + nMinCol = GetColOfUsage( GFU_MinMax ); + + nMaxCol = GetColOfUsage( GFU_Max ); + if( nMaxCol == -1 ) + nMaxCol = GetColOfUsage( GFU_MinMax ); +} + +/************************************************************************/ +/* GetColumnCount() */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::GetColumnCount() const + +{ + return aoFields.size(); +} + +/************************************************************************/ +/* GDALRATGetColumnCount() */ +/************************************************************************/ + +/** + * \brief Fetch table column count. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetColumnCount() + */ +int CPL_STDCALL GDALRATGetColumnCount( GDALRasterAttributeTableH hRAT ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetColumnCount", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetColumnCount(); +} + +/************************************************************************/ +/* GetNameOfCol() */ +/************************************************************************/ + +const char *GDALDefaultRasterAttributeTable::GetNameOfCol( int iCol ) const + +{ + if( iCol < 0 || iCol >= (int) aoFields.size() ) + return ""; + + else + return aoFields[iCol].sName; +} + +/************************************************************************/ +/* GDALRATGetNameOfCol() */ +/************************************************************************/ + +/** + * \brief Fetch name of indicated column. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetNameOfCol() + */ +const char *CPL_STDCALL GDALRATGetNameOfCol( GDALRasterAttributeTableH hRAT, + int iCol ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetNameOfCol", NULL ); + + return ((GDALRasterAttributeTable *) hRAT)->GetNameOfCol( iCol ); +} + +/************************************************************************/ +/* GetUsageOfCol() */ +/************************************************************************/ + +GDALRATFieldUsage GDALDefaultRasterAttributeTable::GetUsageOfCol( int iCol ) const + +{ + if( iCol < 0 || iCol >= (int) aoFields.size() ) + return GFU_Generic; + + else + return aoFields[iCol].eUsage; +} + +/************************************************************************/ +/* GDALRATGetUsageOfCol() */ +/************************************************************************/ + +/** + * \brief Fetch column usage value. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetUsageOfColetNameOfCol() + */ +GDALRATFieldUsage CPL_STDCALL +GDALRATGetUsageOfCol( GDALRasterAttributeTableH hRAT, int iCol ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetUsageOfCol", GFU_Generic ); + + return ((GDALRasterAttributeTable *) hRAT)->GetUsageOfCol( iCol ); +} + +/************************************************************************/ +/* GetTypeOfCol() */ +/************************************************************************/ + +GDALRATFieldType GDALDefaultRasterAttributeTable::GetTypeOfCol( int iCol ) const + +{ + if( iCol < 0 || iCol >= (int) aoFields.size() ) + return GFT_Integer; + + else + return aoFields[iCol].eType; +} + +/************************************************************************/ +/* GDALRATGetTypeOfCol() */ +/************************************************************************/ + +/** + * \brief Fetch column type. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetTypeOfCol() + */ +GDALRATFieldType CPL_STDCALL +GDALRATGetTypeOfCol( GDALRasterAttributeTableH hRAT, int iCol ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetTypeOfCol", GFT_Integer ); + + return ((GDALRasterAttributeTable *) hRAT)->GetTypeOfCol( iCol ); +} + +/************************************************************************/ +/* GetColOfUsage() */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::GetColOfUsage( GDALRATFieldUsage eUsage ) const + +{ + unsigned int i; + + for( i = 0; i < aoFields.size(); i++ ) + { + if( aoFields[i].eUsage == eUsage ) + return i; + } + + return -1; +} + +/************************************************************************/ +/* GDALRATGetColOfUsage() */ +/************************************************************************/ + +/** + * \brief Fetch column index for given usage. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetColOfUsage() + */ +int CPL_STDCALL +GDALRATGetColOfUsage( GDALRasterAttributeTableH hRAT, + GDALRATFieldUsage eUsage ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetColOfUsage", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetColOfUsage( eUsage ); +} + +/************************************************************************/ +/* GetRowCount() */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::GetRowCount() const + +{ + return (int) nRowCount; +} + +/************************************************************************/ +/* GDALRATGetUsageOfCol() */ +/************************************************************************/ +/** + * \brief Fetch row count. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetRowCount() + */ +int CPL_STDCALL +GDALRATGetRowCount( GDALRasterAttributeTableH hRAT ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetRowCount", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetRowCount(); +} + +/************************************************************************/ +/* GetValueAsString() */ +/************************************************************************/ + +const char * +GDALDefaultRasterAttributeTable::GetValueAsString( int iRow, int iField ) const + +{ + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return ""; + } + + if( iRow < 0 || iRow >= nRowCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iRow (%d) out of range.", iRow ); + + return ""; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + { + ((GDALDefaultRasterAttributeTable *) this)-> + osWorkingResult.Printf( "%d", aoFields[iField].anValues[iRow] ); + return osWorkingResult; + } + + case GFT_Real: + { + ((GDALDefaultRasterAttributeTable *) this)-> + osWorkingResult.Printf( "%.16g", aoFields[iField].adfValues[iRow]); + return osWorkingResult; + } + + case GFT_String: + { + return aoFields[iField].aosValues[iRow]; + } + } + + return ""; +} + +/************************************************************************/ +/* GDALRATGetValueAsString() */ +/************************************************************************/ +/** + * \brief Fetch field value as a string. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetValueAsString() + */ +const char * CPL_STDCALL +GDALRATGetValueAsString( GDALRasterAttributeTableH hRAT, int iRow, int iField ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetValueAsString", NULL ); + + return ((GDALRasterAttributeTable *) hRAT)->GetValueAsString(iRow, iField); +} + +/************************************************************************/ +/* GetValueAsInt() */ +/************************************************************************/ + +int +GDALDefaultRasterAttributeTable::GetValueAsInt( int iRow, int iField ) const + +{ + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return 0; + } + + if( iRow < 0 || iRow >= nRowCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iRow (%d) out of range.", iRow ); + + return 0; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + return aoFields[iField].anValues[iRow]; + + case GFT_Real: + return (int) aoFields[iField].adfValues[iRow]; + + case GFT_String: + return atoi( aoFields[iField].aosValues[iRow].c_str() ); + } + + return 0; +} + +/************************************************************************/ +/* GDALRATGetValueAsInt() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a integer. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetValueAsInt() + */ +int CPL_STDCALL +GDALRATGetValueAsInt( GDALRasterAttributeTableH hRAT, int iRow, int iField ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetValueAsInt", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetValueAsInt( iRow, iField ); +} + +/************************************************************************/ +/* GetValueAsDouble() */ +/************************************************************************/ + +double +GDALDefaultRasterAttributeTable::GetValueAsDouble( int iRow, int iField ) const + +{ + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return 0; + } + + if( iRow < 0 || iRow >= nRowCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iRow (%d) out of range.", iRow ); + + return 0; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + return aoFields[iField].anValues[iRow]; + + case GFT_Real: + return aoFields[iField].adfValues[iRow]; + + case GFT_String: + return CPLAtof( aoFields[iField].aosValues[iRow].c_str() ); + } + + return 0; +} + +/************************************************************************/ +/* GDALRATGetValueAsDouble() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a double. + * + * This function is the same as the C++ method GDALRasterAttributeTable::GetValueAsDouble() + */ +double CPL_STDCALL +GDALRATGetValueAsDouble( GDALRasterAttributeTableH hRAT, int iRow, int iField ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATGetValueAsDouble", 0 ); + + return ((GDALRasterAttributeTable *) hRAT)->GetValueAsDouble(iRow,iField); +} + +/************************************************************************/ +/* SetRowCount() */ +/************************************************************************/ + +void GDALDefaultRasterAttributeTable::SetRowCount( int nNewCount ) + +{ + if( nNewCount == nRowCount ) + return; + + unsigned int iField; + for( iField = 0; iField < aoFields.size(); iField++ ) + { + switch( aoFields[iField].eType ) + { + case GFT_Integer: + aoFields[iField].anValues.resize( nNewCount ); + break; + + case GFT_Real: + aoFields[iField].adfValues.resize( nNewCount ); + break; + + case GFT_String: + aoFields[iField].aosValues.resize( nNewCount ); + break; + } + } + + nRowCount = nNewCount; +} + +/************************************************************************/ +/* SetValue() */ +/************************************************************************/ + +void GDALDefaultRasterAttributeTable::SetValue( int iRow, int iField, + const char *pszValue ) + +{ + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return; + } + + if( iRow == nRowCount ) + SetRowCount( nRowCount+1 ); + + if( iRow < 0 || iRow >= nRowCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iRow (%d) out of range.", iRow ); + + return; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + aoFields[iField].anValues[iRow] = atoi(pszValue); + break; + + case GFT_Real: + aoFields[iField].adfValues[iRow] = CPLAtof(pszValue); + break; + + case GFT_String: + aoFields[iField].aosValues[iRow] = pszValue; + break; + } +} + +/************************************************************************/ +/* GDALRATSetValueAsString() */ +/************************************************************************/ + +/** + * \brief Set field value from string. + * + * This function is the same as the C++ method GDALRasterAttributeTable::SetValue() + */ +void CPL_STDCALL +GDALRATSetValueAsString( GDALRasterAttributeTableH hRAT, int iRow, int iField, + const char *pszValue ) + +{ + VALIDATE_POINTER0( hRAT, "GDALRATSetValueAsString" ); + + ((GDALRasterAttributeTable *) hRAT)->SetValue( iRow, iField, pszValue ); +} + +/************************************************************************/ +/* SetValue() */ +/************************************************************************/ + +void GDALDefaultRasterAttributeTable::SetValue( int iRow, int iField, + int nValue ) + +{ + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return; + } + + if( iRow == nRowCount ) + SetRowCount( nRowCount+1 ); + + if( iRow < 0 || iRow >= nRowCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iRow (%d) out of range.", iRow ); + + return; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + aoFields[iField].anValues[iRow] = nValue; + break; + + case GFT_Real: + aoFields[iField].adfValues[iRow] = nValue; + break; + + case GFT_String: + { + char szValue[100]; + + sprintf( szValue, "%d", nValue ); + aoFields[iField].aosValues[iRow] = szValue; + } + break; + } +} + +/************************************************************************/ +/* GDALRATSetValueAsInt() */ +/************************************************************************/ + +/** + * \brief Set field value from integer. + * + * This function is the same as the C++ method GDALRasterAttributeTable::SetValue() + */ +void CPL_STDCALL +GDALRATSetValueAsInt( GDALRasterAttributeTableH hRAT, int iRow, int iField, + int nValue ) + +{ + VALIDATE_POINTER0( hRAT, "GDALRATSetValueAsInt" ); + + ((GDALRasterAttributeTable *) hRAT)->SetValue( iRow, iField, nValue); +} + +/************************************************************************/ +/* SetValue() */ +/************************************************************************/ + +void GDALDefaultRasterAttributeTable::SetValue( int iRow, int iField, + double dfValue ) + +{ + if( iField < 0 || iField >= (int) aoFields.size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iField (%d) out of range.", iField ); + + return; + } + + if( iRow == nRowCount ) + SetRowCount( nRowCount+1 ); + + if( iRow < 0 || iRow >= nRowCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "iRow (%d) out of range.", iRow ); + + return; + } + + switch( aoFields[iField].eType ) + { + case GFT_Integer: + aoFields[iField].anValues[iRow] = (int) dfValue; + break; + + case GFT_Real: + aoFields[iField].adfValues[iRow] = dfValue; + break; + + case GFT_String: + { + char szValue[100]; + + CPLsprintf( szValue, "%.15g", dfValue ); + aoFields[iField].aosValues[iRow] = szValue; + } + break; + } +} + +/************************************************************************/ +/* GDALRATSetValueAsDouble() */ +/************************************************************************/ + +/** + * \brief Set field value from double. + * + * This function is the same as the C++ method GDALRasterAttributeTable::SetValue() + */ +void CPL_STDCALL +GDALRATSetValueAsDouble( GDALRasterAttributeTableH hRAT, int iRow, int iField, + double dfValue ) + +{ + VALIDATE_POINTER0( hRAT, "GDALRATSetValueAsDouble" ); + + ((GDALRasterAttributeTable *) hRAT)->SetValue( iRow, iField, dfValue ); +} + +/************************************************************************/ +/* ChangesAreWrittenToFile() */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::ChangesAreWrittenToFile() +{ + // GDALRasterBand.SetDefaultRAT needs to be called on instances of + // GDALDefaultRasterAttributeTable since changes are just in-memory + return FALSE; +} + +/************************************************************************/ +/* GDALRATChangesAreWrittenToFile() */ +/************************************************************************/ + +/** + * \brief Determine whether changes made to this RAT are reflected directly in the dataset + * + * This function is the same as the C++ method GDALRasterAttributeTable::ChangesAreWrittenToFile() + */ +int CPL_STDCALL +GDALRATChangesAreWrittenToFile( GDALRasterAttributeTableH hRAT ) +{ + VALIDATE_POINTER1( hRAT, "GDALRATChangesAreWrittenToFile", FALSE ); + + return ((GDALRasterAttributeTable *) hRAT)->ChangesAreWrittenToFile(); +} + +/************************************************************************/ +/* GetRowOfValue() */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::GetRowOfValue( double dfValue ) const + +{ +/* -------------------------------------------------------------------- */ +/* Handle case of regular binning. */ +/* -------------------------------------------------------------------- */ + if( bLinearBinning ) + { + int iBin = (int) floor((dfValue - dfRow0Min) / dfBinSize); + if( iBin < 0 || iBin >= nRowCount ) + return -1; + else + return iBin; + } + +/* -------------------------------------------------------------------- */ +/* Do we have any information? */ +/* -------------------------------------------------------------------- */ + const GDALRasterAttributeField *poMin, *poMax; + + if( !bColumnsAnalysed ) + ((GDALDefaultRasterAttributeTable *) this)->AnalyseColumns(); + + if( nMinCol == -1 && nMaxCol == -1 ) + return -1; + + if( nMinCol != -1 ) + poMin = &(aoFields[nMinCol]); + else + poMin = NULL; + + if( nMaxCol != -1 ) + poMax = &(aoFields[nMaxCol]); + else + poMax = NULL; + +/* -------------------------------------------------------------------- */ +/* Search through rows for match. */ +/* -------------------------------------------------------------------- */ + int iRow; + + for( iRow = 0; iRow < nRowCount; iRow++ ) + { + if( poMin != NULL ) + { + if( poMin->eType == GFT_Integer ) + { + while( iRow < nRowCount && dfValue < poMin->anValues[iRow] ) + iRow++; + } + else if( poMin->eType == GFT_Real ) + { + while( iRow < nRowCount && dfValue < poMin->adfValues[iRow] ) + iRow++; + } + + if( iRow == nRowCount ) + break; + } + + if( poMax != NULL ) + { + if( (poMax->eType == GFT_Integer + && dfValue > poMax->anValues[iRow] ) + || (poMax->eType == GFT_Real + && dfValue > poMax->adfValues[iRow] ) ) + continue; + } + + return iRow; + } + + return -1; +} + +/************************************************************************/ +/* GetRowOfValue() */ +/* */ +/* Int arg for now just converted to double. Perhaps we will */ +/* handle this in a special way some day? */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::GetRowOfValue( int nValue ) const + +{ + return GetRowOfValue( (double) nValue ); +} + +/************************************************************************/ +/* SetLinearBinning() */ +/************************************************************************/ + +CPLErr GDALDefaultRasterAttributeTable::SetLinearBinning( double dfRow0MinIn, + double dfBinSizeIn ) + +{ + bLinearBinning = TRUE; + dfRow0Min = dfRow0MinIn; + dfBinSize = dfBinSizeIn; + + return CE_None; +} + +/************************************************************************/ +/* GetLinearBinning() */ +/************************************************************************/ + +int GDALDefaultRasterAttributeTable::GetLinearBinning( double *pdfRow0Min, + double *pdfBinSize ) const + +{ + if( !bLinearBinning ) + return FALSE; + + *pdfRow0Min = dfRow0Min; + *pdfBinSize = dfBinSize; + + return TRUE; +} + +/************************************************************************/ +/* CreateColumn() */ +/************************************************************************/ + +CPLErr GDALDefaultRasterAttributeTable::CreateColumn( const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ) + +{ + int iNewField = aoFields.size(); + + aoFields.resize( iNewField+1 ); + + aoFields[iNewField].sName = pszFieldName; + + // color columns should be int 0..255 + if( ( eFieldUsage == GFU_Red ) || ( eFieldUsage == GFU_Green ) || + ( eFieldUsage == GFU_Blue ) || ( eFieldUsage == GFU_Alpha ) ) + { + eFieldType = GFT_Integer; + } + aoFields[iNewField].eType = eFieldType; + aoFields[iNewField].eUsage = eFieldUsage; + + if( eFieldType == GFT_Integer ) + aoFields[iNewField].anValues.resize( nRowCount ); + else if( eFieldType == GFT_Real ) + aoFields[iNewField].adfValues.resize( nRowCount ); + else if( eFieldType == GFT_String ) + aoFields[iNewField].aosValues.resize( nRowCount ); + + return CE_None; +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +GDALDefaultRasterAttributeTable *GDALDefaultRasterAttributeTable::Clone() const + +{ + return new GDALDefaultRasterAttributeTable( *this ); +} + +/************************************************************************/ +/* GDALRATClone() */ +/************************************************************************/ + +/** + * \brief Copy Raster Attribute Table + * + * This function is the same as the C++ method GDALRasterAttributeTable::Clone() + */ +GDALRasterAttributeTableH CPL_STDCALL +GDALRATClone( GDALRasterAttributeTableH hRAT ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATClone", NULL ); + + return ((GDALRasterAttributeTable *) hRAT)->Clone(); +} + +/************************************************************************/ +/* GDALRATSerializeJSON() */ +/************************************************************************/ + +/** + * \brief Serialize Raster Attribute Table in Json format + * + * This function is the same as the C++ method GDALRasterAttributeTable::SerializeJSON() + */ +void* CPL_STDCALL +GDALRATSerializeJSON( GDALRasterAttributeTableH hRAT ) + +{ + VALIDATE_POINTER1( hRAT, "GDALRATSerializeJSON", NULL ); + + return ((GDALRasterAttributeTable *) hRAT)->SerializeJSON(); +} diff --git a/bazaar/plugin/gdal/gcore/gdal_rat.h b/bazaar/plugin/gdal/gcore/gdal_rat.h new file mode 100644 index 000000000..a70445d5e --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdal_rat.h @@ -0,0 +1,349 @@ +/****************************************************************************** + * $Id: gdal_rat.h 29243 2015-05-24 15:53:26Z rouault $ + * + * Project: GDAL Core + * Purpose: GDALRasterAttributeTable class declarations. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_RAT_H_INCLUDED +#define GDAL_RAT_H_INCLUDED + +#include "cpl_minixml.h" + +// Clone and Serialize are allowed to fail if GetRowCount()*GetColCount() greater +// than this number +#define RAT_MAX_ELEM_FOR_CLONE 1000000 + +/************************************************************************/ +/* GDALRasterAttributeTable */ +/************************************************************************/ + +//! Raster Attribute Table interface. +class GDALDefaultRasterAttributeTable; + +class CPL_DLL GDALRasterAttributeTable +{ +public: + virtual ~GDALRasterAttributeTable(); + /** + * \brief Copy Raster Attribute Table + * + * Creates a new copy of an existing raster attribute table. The new copy + * becomes the responsibility of the caller to destroy. + * May fail (return NULL) if the attribute table is too large to clone + * (GetRowCount() * GetColCount() > RAT_MAX_ELEM_FOR_CLONE) + * + * This method is the same as the C function GDALRATClone(). + * + * @return new copy of the RAT as an in-memory implementation. + */ + virtual GDALDefaultRasterAttributeTable *Clone() const = 0; + + /** + * \brief Fetch table column count. + * + * This method is the same as the C function GDALRATGetColumnCount(). + * + * @return the number of columns. + */ + virtual int GetColumnCount() const = 0; + + /** + * \brief Fetch name of indicated column. + * + * This method is the same as the C function GDALRATGetNameOfCol(). + * + * @param iCol the column index (zero based). + * + * @return the column name or an empty string for invalid column numbers. + */ + virtual const char *GetNameOfCol( int ) const = 0; + + /** + * \brief Fetch column usage value. + * + * This method is the same as the C function GDALRATGetUsageOfCol(). + * + * @param iCol the column index (zero based). + * + * @return the column usage, or GFU_Generic for improper column numbers. + */ + virtual GDALRATFieldUsage GetUsageOfCol( int ) const = 0; + + /** + * \brief Fetch column type. + * + * This method is the same as the C function GDALRATGetTypeOfCol(). + * + * @param iCol the column index (zero based). + * + * @return column type or GFT_Integer if the column index is illegal. + */ + virtual GDALRATFieldType GetTypeOfCol( int ) const = 0; + + /** + * \brief Fetch column index for given usage. + * + * Returns the index of the first column of the requested usage type, or -1 + * if no match is found. + * + * This method is the same as the C function GDALRATGetUsageOfCol(). + * + * @param eUsage usage type to search for. + * + * @return column index, or -1 on failure. + */ + virtual int GetColOfUsage( GDALRATFieldUsage ) const = 0; + + /** + * \brief Fetch row count. + * + * This method is the same as the C function GDALRATGetRowCount(). + * + * @return the number of rows. + */ + virtual int GetRowCount() const = 0; + + /** + * \brief Fetch field value as a string. + * + * The value of the requested column in the requested row is returned + * as a string. If the field is numeric, it is formatted as a string + * using default rules, so some precision may be lost. + * + * The returned string is temporary and cannot be expected to be + * available after the next GDAL call. + * + * This method is the same as the C function GDALRATGetValueAsString(). + * + * @param iRow row to fetch (zero based). + * @param iField column to fetch (zero based). + * + * @return field value. + */ + virtual const char *GetValueAsString( int iRow, int iField ) const = 0; + + /** + * \brief Fetch field value as a integer. + * + * The value of the requested column in the requested row is returned + * as an integer. Non-integer fields will be converted to integer with + * the possibility of data loss. + * + * This method is the same as the C function GDALRATGetValueAsInt(). + * + * @param iRow row to fetch (zero based). + * @param iField column to fetch (zero based). + * + * @return field value + */ + virtual int GetValueAsInt( int iRow, int iField ) const = 0; + + /** + * \brief Fetch field value as a double. + * + * The value of the requested column in the requested row is returned + * as a double. Non double fields will be converted to double with + * the possibility of data loss. + * + * This method is the same as the C function GDALRATGetValueAsDouble(). + * + * @param iRow row to fetch (zero based). + * @param iField column to fetch (zero based). + * + * @return field value + */ + virtual double GetValueAsDouble( int iRow, int iField ) const = 0; + + /** + * \brief Set field value from string. + * + * The indicated field (column) on the indicated row is set from the + * passed value. The value will be automatically converted for other field + * types, with a possible loss of precision. + * + * This method is the same as the C function GDALRATSetValueAsString(). + * + * @param iRow row to fetch (zero based). + * @param iField column to fetch (zero based). + * @param pszValue the value to assign. + */ + virtual void SetValue( int iRow, int iField, const char *pszValue ) = 0; + + /** + * \brief Set field value from integer. + * + * The indicated field (column) on the indicated row is set from the + * passed value. The value will be automatically converted for other field + * types, with a possible loss of precision. + * + * This method is the same as the C function GDALRATSetValueAsInteger(). + * + * @param iRow row to fetch (zero based). + * @param iField column to fetch (zero based). + * @param nValue the value to assign. + */ + virtual void SetValue( int iRow, int iField, int nValue ) = 0; + + /** + * \brief Set field value from double. + * + * The indicated field (column) on the indicated row is set from the + * passed value. The value will be automatically converted for other field + * types, with a possible loss of precision. + * + * This method is the same as the C function GDALRATSetValueAsDouble(). + * + * @param iRow row to fetch (zero based). + * @param iField column to fetch (zero based). + * @param dfValue the value to assign. + */ + virtual void SetValue( int iRow, int iField, double dfValue) = 0; + + /** + * \brief Determine whether changes made to this RAT are reflected directly in the dataset + * + * If this returns FALSE then GDALRasterBand.SetDefaultRAT() should be called. Otherwise + * this is unnecessary since changes to this object are reflected in the dataset. + * + * This method is the same as the C function GDALRATChangesAreWrittenToFile(). + * + */ + virtual int ChangesAreWrittenToFile() = 0; + + virtual CPLErr ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, double *pdfData); + virtual CPLErr ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, int *pnData); + virtual CPLErr ValuesIO(GDALRWFlag eRWFlag, int iField, int iStartRow, int iLength, char **papszStrList); + + virtual void SetRowCount( int iCount ); + virtual int GetRowOfValue( double dfValue ) const; + virtual int GetRowOfValue( int nValue ) const; + + virtual CPLErr CreateColumn( const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ); + virtual CPLErr SetLinearBinning( double dfRow0Min, double dfBinSize ); + virtual int GetLinearBinning( double *pdfRow0Min, double *pdfBinSize ) const; + + /** + * \brief Serialize + * + * May fail (return NULL) if the attribute table is too large to serialize + * (GetRowCount() * GetColCount() > RAT_MAX_ELEM_FOR_CLONE) + */ + virtual CPLXMLNode *Serialize() const; + virtual void *SerializeJSON() const; + virtual CPLErr XMLInit( CPLXMLNode *, const char * ); + + virtual CPLErr InitializeFromColorTable( const GDALColorTable * ); + virtual GDALColorTable *TranslateToColorTable( int nEntryCount = -1 ); + + virtual void DumpReadable( FILE * = NULL ); +}; + +/************************************************************************/ +/* GDALRasterAttributeField */ +/* */ +/* (private) */ +/************************************************************************/ + +class GDALRasterAttributeField +{ +public: + CPLString sName; + + GDALRATFieldType eType; + + GDALRATFieldUsage eUsage; + + std::vector anValues; + std::vector adfValues; + std::vector aosValues; +}; + +/************************************************************************/ +/* GDALDefaultRasterAttributeTable */ +/************************************************************************/ + +//! Raster Attribute Table container. + +class CPL_DLL GDALDefaultRasterAttributeTable : public GDALRasterAttributeTable +{ +private: + std::vector aoFields; + + int bLinearBinning; + double dfRow0Min; + double dfBinSize; + + void AnalyseColumns(); + int bColumnsAnalysed; + int nMinCol; + int nMaxCol; + + int nRowCount; + + CPLString osWorkingResult; + +public: + GDALDefaultRasterAttributeTable(); + GDALDefaultRasterAttributeTable(const GDALDefaultRasterAttributeTable&); + ~GDALDefaultRasterAttributeTable(); + + GDALDefaultRasterAttributeTable *Clone() const; + + virtual int GetColumnCount() const; + + virtual const char *GetNameOfCol( int ) const; + virtual GDALRATFieldUsage GetUsageOfCol( int ) const; + virtual GDALRATFieldType GetTypeOfCol( int ) const; + + virtual int GetColOfUsage( GDALRATFieldUsage ) const; + + virtual int GetRowCount() const; + + virtual const char *GetValueAsString( int iRow, int iField ) const; + virtual int GetValueAsInt( int iRow, int iField ) const; + virtual double GetValueAsDouble( int iRow, int iField ) const; + + virtual void SetValue( int iRow, int iField, const char *pszValue ); + virtual void SetValue( int iRow, int iField, double dfValue); + virtual void SetValue( int iRow, int iField, int nValue ); + + virtual int ChangesAreWrittenToFile(); + virtual void SetRowCount( int iCount ); + + virtual int GetRowOfValue( double dfValue ) const; + virtual int GetRowOfValue( int nValue ) const; + + virtual CPLErr CreateColumn( const char *pszFieldName, + GDALRATFieldType eFieldType, + GDALRATFieldUsage eFieldUsage ); + virtual CPLErr SetLinearBinning( double dfRow0Min, double dfBinSize ); + virtual int GetLinearBinning( double *pdfRow0Min, double *pdfBinSize ) const; + +}; + +#endif /* ndef GDAL_RAT_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/gcore/gdal_version.h b/bazaar/plugin/gdal/gcore/gdal_version.h new file mode 100644 index 000000000..22cc12cb6 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdal_version.h @@ -0,0 +1,29 @@ + +/* -------------------------------------------------------------------- */ +/* GDAL Version Information. */ +/* -------------------------------------------------------------------- */ + +#ifndef GDAL_VERSION_MAJOR +# define GDAL_VERSION_MAJOR 2 +# define GDAL_VERSION_MINOR 0 +# define GDAL_VERSION_REV 0 +# define GDAL_VERSION_BUILD 0 +#endif + +/* GDAL_COMPUTE_VERSION macro introduced in GDAL 1.10 */ +/* Must be used ONLY to compare with version numbers for GDAL >= 1.10 */ +#ifndef GDAL_COMPUTE_VERSION +#define GDAL_COMPUTE_VERSION(maj,min,rev) ((maj)*1000000+(min)*10000+(rev)*100) +#endif + +/* Note: the formula to compute GDAL_VERSION_NUM has changed in GDAL 1.10 */ +#ifndef GDAL_VERSION_NUM +# define GDAL_VERSION_NUM (GDAL_COMPUTE_VERSION(GDAL_VERSION_MAJOR,GDAL_VERSION_MINOR,GDAL_VERSION_REV)+GDAL_VERSION_BUILD) +#endif + +#ifndef GDAL_RELEASE_DATE +# define GDAL_RELEASE_DATE 20150614 +#endif +#ifndef GDAL_RELEASE_NAME +# define GDAL_RELEASE_NAME "2.0.0" +#endif diff --git a/bazaar/plugin/gdal/gcore/gdalallvalidmaskband.cpp b/bazaar/plugin/gdal/gcore/gdalallvalidmaskband.cpp new file mode 100644 index 000000000..b5ab48ff3 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalallvalidmaskband.cpp @@ -0,0 +1,92 @@ +/****************************************************************************** + * $Id: gdalallvalidmaskband.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALAllValidMaskBand, a class implementing all + * a default 'all valid' band mask. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +CPL_CVSID("$Id: gdalallvalidmaskband.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* GDALAllValidMaskBand() */ +/************************************************************************/ + +GDALAllValidMaskBand::GDALAllValidMaskBand( GDALRasterBand *poParent ) + +{ + poDS = NULL; + nBand = 0; + + nRasterXSize = poParent->GetXSize(); + nRasterYSize = poParent->GetYSize(); + + eDataType = GDT_Byte; + poParent->GetBlockSize( &nBlockXSize, &nBlockYSize ); +} + +/************************************************************************/ +/* ~GDALAllValidMaskBand() */ +/************************************************************************/ + +GDALAllValidMaskBand::~GDALAllValidMaskBand() + +{ +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GDALAllValidMaskBand::IReadBlock( CPL_UNUSED int nXBlockOff, + CPL_UNUSED int nYBlockOff, + void * pImage ) +{ + memset( pImage, 255, nBlockXSize * nBlockYSize ); + + return CE_None; +} + +/************************************************************************/ +/* GetMaskBand() */ +/************************************************************************/ + +GDALRasterBand *GDALAllValidMaskBand::GetMaskBand() + +{ + return this; +} + +/************************************************************************/ +/* GetMaskFlags() */ +/************************************************************************/ + +int GDALAllValidMaskBand::GetMaskFlags() + +{ + return GMF_ALL_VALID; +} diff --git a/bazaar/plugin/gdal/gcore/gdalclientserver.cpp b/bazaar/plugin/gdal/gcore/gdalclientserver.cpp new file mode 100644 index 000000000..1026b210e --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalclientserver.cpp @@ -0,0 +1,6089 @@ +/****************************************************************************** + * $Id: gdalclientserver.cpp 28899 2015-04-14 09:27:00Z rouault $ + * + * Project: GDAL Core + * Purpose: GDAL Client/server dataset mechanism. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_port.h" + +#ifdef WIN32 + #ifdef _WIN32_WINNT + #undef _WIN32_WINNT + #endif + #define _WIN32_WINNT 0x0501 + #include + #include + typedef SOCKET CPL_SOCKET; + #ifndef HAVE_GETADDRINFO + #define HAVE_GETADDRINFO 1 + #endif +#else + #include + #include + #include + #include + #include + #include + typedef int CPL_SOCKET; + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 + #define SOCKADDR struct sockaddr + #define WSAGetLastError() errno + #define WSACleanup() + #define closesocket(s) close(s) +#endif + +#include "gdal_pam.h" +#include "gdal_rat.h" +#include "cpl_spawn.h" +#include "cpl_multiproc.h" + +/*! +\page gdal_api_proxy GDAL API Proxy + +\section gdal_api_proxy_intro Introduction + +(GDAL >= 1.10.0) + +When dealing with some file formats, particularly the drivers relying on third-party +(potentially closed-source) libraries, it is difficult to ensure that those third-party +libraries will be robust to hostile/corrupted datasource. + +The implemented solution is to have a (private) API_PROXY driver that will expose a GDALClientDataset +object, which will forward all the GDAL API calls to another process ("server"), where the real driver +will be effectively run. This way, if the server aborts due to a fatal error, the calling process will +be unaffected and will report a clean error instead of aborting itself. + +\section gdal_api_proxy_enabling How to enable ? + +The API_PROXY mechanism can be enabled by setting the GDAL_API_PROXY config option to YES. +The option can also be set to a list of file extensions that must be the only ones to trigger +this mechanism (e.g. GDAL_API_PROXY=ecw,sid). + +When enabled, datasets can be handled with GDALOpen(), GDALCreate() or GDALCreateCopy() with +their nominal filename (or connexion string). + +Alternatively, the API_PROXY mechanism can be used selectively on a datasource by prefixing its +name with API_PROXY:, for example GDALOpen("API_PROXY:foo.tif", GA_ReadOnly). + +\section gdal_api_proxy_options Advanced options + +For now, the server launched is the gdalserver executable on Windows. On Unix, the default behaviour is +to just fork() the current process. It is also possible to launch the gdalserver executable +by forcing GDAL_API_PROXY_SERVER=YES. +The full filename of the gdalserver executable can also be specified in the GDAL_API_PROXY_SERVER. + +It is also possible to connect to a gdalserver in TCP, possibly on a remote host. In that case, +gdalserver must be launched on a host with "gdalserver -tcpserver the_tcp_port". And the client +must set GDAL_API_PROXY_SERVER="hostname:the_tcp_port", where hostname is a string or a IP address. + +In case of many dataset opening or creation, to avoid the cost of repeated process forking, +a pool of unused connections is established. Each time a dataset is closed, the associated connection +is pushed in the pool (if there's an empty bucket). When a following dataset is to be opened, one of those +connections will be reused. This behaviour is controlled with the GDAL_API_PROXY_CONN_POOL config option +that is set to YES by default, and will keep a maximum of 4 unused connections. +GDAL_API_PROXY_CONN_POOL can be set to a integer value to specify the maximum number of unused connections. + +\section gdal_api_proxy_limitations Limitations + +Datasets stored in the memory virtual file system (/vsimem) or handled by the MEM driver are excluded from +the API Proxy mechanism. + +Additionnaly, for GDALCreate() or GDALCreateCopy(), the VRT driver is also excluded from that mechanism. + +Currently, the client dataset returned is not protected by a mutex, so it is unsafe to use it concurrently +from multiple threads. However, it is safe to use several client datasets from multiple threads. + +*/ + +/* REMINDER: upgrade this number when the on-wire protocol changes */ +/* Note: please at least keep the version exchange protocol unchanged ! */ +#define GDAL_CLIENT_SERVER_PROTOCOL_MAJOR 2 +#define GDAL_CLIENT_SERVER_PROTOCOL_MINOR 0 + +#include +#include + +CPL_C_START +int CPL_DLL GDALServerLoop(CPL_FILE_HANDLE fin, CPL_FILE_HANDLE fout); +const char* GDALClientDatasetGetFilename(const char* pszFilename); +int CPL_DLL GDALServerLoopSocket(CPL_SOCKET nSocket); +CPL_C_END + +#define BUFFER_SIZE 1024 +typedef struct +{ + CPL_FILE_HANDLE fin; + CPL_FILE_HANDLE fout; + CPL_SOCKET nSocket; + int bOK; + GByte abyBuffer[BUFFER_SIZE]; + int nBufferSize; +} GDALPipe; + +typedef struct +{ + CPLSpawnedProcess *sp; + GDALPipe *p; +} GDALServerSpawnedProcess; + +typedef struct +{ + int bUpdated; + double dfComplete; + char *pszProgressMsg; + int bRet; + CPLMutex *hMutex; +} GDALServerAsyncProgress; + +typedef enum +{ + INSTR_INVALID = 0, + INSTR_GetGDALVersion = 1, /* do not change this ! */ + INSTR_EXIT, + INSTR_EXIT_FAIL, + INSTR_SetConfigOption, + INSTR_Progress, + INSTR_Reset, + INSTR_Open, + INSTR_Identify, + INSTR_Create, + INSTR_CreateCopy, + INSTR_QuietDelete, + INSTR_AddBand, + INSTR_GetGeoTransform, + INSTR_SetGeoTransform, + INSTR_GetProjectionRef, + INSTR_SetProjection, + INSTR_GetGCPCount, + INSTR_GetGCPProjection, + INSTR_GetGCPs, + INSTR_SetGCPs, + INSTR_GetFileList, + INSTR_FlushCache, + INSTR_SetDescription, + INSTR_GetMetadata, + INSTR_GetMetadataItem, + INSTR_SetMetadata, + INSTR_SetMetadataItem, + INSTR_IRasterIO_Read, + INSTR_IRasterIO_Write, + INSTR_IBuildOverviews, + INSTR_AdviseRead, + INSTR_CreateMaskBand, + INSTR_Band_First, + INSTR_Band_FlushCache, + INSTR_Band_GetCategoryNames, + INSTR_Band_SetCategoryNames, + INSTR_Band_SetDescription, + INSTR_Band_GetMetadata, + INSTR_Band_GetMetadataItem, + INSTR_Band_SetMetadata, + INSTR_Band_SetMetadataItem, + INSTR_Band_GetColorInterpretation, + INSTR_Band_SetColorInterpretation, + INSTR_Band_GetNoDataValue, + INSTR_Band_GetMinimum, + INSTR_Band_GetMaximum, + INSTR_Band_GetOffset, + INSTR_Band_GetScale, + INSTR_Band_SetNoDataValue, + INSTR_Band_SetOffset, + INSTR_Band_SetScale, + INSTR_Band_IReadBlock, + INSTR_Band_IWriteBlock, + INSTR_Band_IRasterIO_Read, + INSTR_Band_IRasterIO_Write, + INSTR_Band_GetStatistics, + INSTR_Band_ComputeStatistics, + INSTR_Band_SetStatistics, + INSTR_Band_ComputeRasterMinMax, + INSTR_Band_GetHistogram, + INSTR_Band_GetDefaultHistogram, + INSTR_Band_SetDefaultHistogram, + INSTR_Band_HasArbitraryOverviews, + INSTR_Band_GetOverviewCount, + INSTR_Band_GetOverview, + INSTR_Band_GetMaskBand, + INSTR_Band_GetMaskFlags, + INSTR_Band_CreateMaskBand, + INSTR_Band_Fill, + INSTR_Band_GetColorTable, + INSTR_Band_SetColorTable, + INSTR_Band_GetUnitType, + INSTR_Band_SetUnitType, + INSTR_Band_BuildOverviews, + INSTR_Band_GetDefaultRAT, + INSTR_Band_SetDefaultRAT, + INSTR_Band_AdviseRead, + INSTR_Band_End, + INSTR_END +} InstrEnum; + +#ifdef DEBUG +static const char* apszInstr[] = +{ + "INVALID", + "GetGDALVersion", + "EXIT", + "FAIL", + "SetConfigOption", + "Progress", + "Reset", + "Open", + "Identify", + "Create", + "CreateCopy", + "QuietDelete", + "AddBand", + "GetGeoTransform", + "SetGeoTransform", + "GetProjectionRef", + "SetProjection", + "GetGCPCount", + "GetGCPProjection", + "GetGCPs", + "SetGCPs", + "GetFileList", + "FlushCache", + "SetDescription", + "GetMetadata", + "GetMetadataItem", + "SetMetadata", + "SetMetadataItem", + "IRasterIO_Read", + "IRasterIO_Write", + "IBuildOverviews", + "AdviseRead", + "CreateMaskBand", + "Band_First", + "Band_FlushCache", + "Band_GetCategoryNames", + "Band_SetCategoryNames", + "Band_SetDescription", + "Band_GetMetadata", + "Band_GetMetadataItem", + "Band_SetMetadata", + "Band_SetMetadataItem", + "Band_GetColorInterpretation", + "Band_SetColorInterpretation", + "Band_GetNoDataValue", + "Band_GetMinimum", + "Band_GetMaximum", + "Band_GetOffset", + "Band_GetScale", + "Band_SetNoDataValue", + "Band_SetOffset", + "Band_SetScale", + "Band_IReadBlock", + "Band_IWriteBlock", + "Band_IRasterIO_Read", + "Band_IRasterIO_Write", + "Band_GetStatistics", + "Band_ComputeStatistics", + "Band_SetStatistics", + "Band_ComputeRasterMinMax", + "Band_GetHistogram", + "Band_GetDefaultHistogram", + "Band_SetDefaultHistogram", + "Band_HasArbitraryOverviews", + "Band_GetOverviewCount", + "Band_GetOverview", + "Band_GetMaskBand", + "Band_GetMaskFlags", + "Band_CreateMaskBand", + "Band_Fill", + "Band_GetColorTable", + "Band_SetColorTable", + "Band_GetUnitType", + "Band_SetUnitType", + "Band_BuildOverviews", + "Band_GetDefaultRAT", + "Band_SetDefaultRAT", + "Band_AdviseRead", + "Band_End", + "END", +}; +#endif + +static const GByte abyEndOfJunkMarker[] = { 0xDE, 0xAD, 0xBE, 0xEF }; + +/* Recycling of connexions to child processes */ +#define MAX_RECYCLED 128 +#define DEFAULT_RECYCLED 4 +static int bRecycleChild = FALSE; +static int nMaxRecycled = 0; +static GDALServerSpawnedProcess* aspRecycled[MAX_RECYCLED]; + +/************************************************************************/ +/* EnterObject */ +/************************************************************************/ + +#ifdef DEBUG_VERBOSE +class EnterObject +{ + const char* pszFunction; + + public: + EnterObject(const char* pszFunction) : pszFunction(pszFunction) + { + CPLDebug("GDAL", "Enter %s", pszFunction); + } + + ~EnterObject() + { + CPLDebug("GDAL", "Leave %s", pszFunction); + } +}; + +#define CLIENT_ENTER() EnterObject o(__FUNCTION__) +#else +#define CLIENT_ENTER() while(0) +#endif + +/************************************************************************/ +/* MyChdir() */ +/************************************************************************/ + +static void MyChdir( +#ifndef WIN32 +CPL_UNUSED +#endif + const char* pszCWD) +{ +#ifdef WIN32 + SetCurrentDirectory(pszCWD); +#else + CPLAssert(chdir(pszCWD) == 0); +#endif +} + +/************************************************************************/ +/* MyChdirRootDirectory() */ +/************************************************************************/ + +static void MyChdirRootDirectory() +{ +#ifdef WIN32 + SetCurrentDirectory("C:\\"); +#else + CPLAssert(chdir("/") == 0); +#endif +} + +/************************************************************************/ +/* GDALClientDataset */ +/************************************************************************/ + +class GDALClientDataset: public GDALPamDataset +{ + GDALServerSpawnedProcess *ssp; + GDALPipe *p; + CPLString osProjection; + CPLString osGCPProjection; + int bFreeDriver; + int nGCPCount; + GDAL_GCP *pasGCPs; + std::map aoMapMetadata; + std::map< std::pair, char*> aoMapMetadataItem; + GDALServerAsyncProgress *async; + GByte abyCaps[16]; /* 16 * 8 = 128 > INSTR_END */ + + int mCreateCopy(const char* pszFilename, + GDALDataset* poSrcDS, + int bStrict, char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData); + int mCreate( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ); + + GDALClientDataset(GDALServerSpawnedProcess* ssp); + + static GDALClientDataset* CreateAndConnect(); + + protected: + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg); + public: + GDALClientDataset(GDALPipe* p); + ~GDALClientDataset(); + + int Init(const char* pszFilename, GDALAccess eAccess); + + void AttachAsyncProgress(GDALServerAsyncProgress* async) { this->async = async; } + int ProcessAsyncProgress(); + int SupportsInstr(InstrEnum instr) const { return abyCaps[instr / 8] & (1 << (instr % 8)); } + + virtual void FlushCache(); + + virtual CPLErr AddBand( GDALDataType eType, + char **papszOptions=NULL ); + + //virtual void SetDescription( const char * ); + + virtual const char* GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + virtual const char* GetProjectionRef(); + virtual CPLErr SetProjection( const char * ); + + virtual CPLErr GetGeoTransform( double * ); + virtual CPLErr SetGeoTransform( double * ); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ); + + virtual char **GetFileList(void); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + char **papszOptions ); + + virtual CPLErr CreateMaskBand( int nFlags ); + + static GDALDataset *Open( GDALOpenInfo * ); + static int Identify( GDALOpenInfo * ); + static GDALDataset *CreateCopy( const char * pszFilename, + GDALDataset * poSrcDS, int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, void * pProgressData ); + static GDALDataset* Create( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ); + static CPLErr Delete( const char * pszName ); +}; + +/************************************************************************/ +/* GDALClientRasterBand */ +/************************************************************************/ + +class GDALClientRasterBand : public GDALPamRasterBand +{ + friend class GDALClientDataset; + + GDALPipe *p; + int iSrvBand; + std::map aMapOvrBands; + std::map aMapOvrBandsCurrent; + GDALRasterBand *poMaskBand; + std::map aoMapMetadata; + std::map< std::pair, char*> aoMapMetadataItem; + char **papszCategoryNames; + GDALColorTable *poColorTable; + char *pszUnitType; + GDALRasterAttributeTable *poRAT; + std::vector apoOldMaskBands; + GByte abyCaps[16]; /* 16 * 8 = 128 > INSTR_END */ + + int WriteInstr(InstrEnum instr); + + double GetDouble( InstrEnum instr, int *pbSuccess ); + CPLErr SetDouble( InstrEnum instr, double dfVal ); + + GDALRasterBand *CreateFakeMaskBand(); + + int bEnableLineCaching; + int nSuccessiveLinesRead; + GDALDataType eLastBufType; + int nLastYOff; + GByte *pabyCachedLines; + GDALDataType eCachedBufType; + int nCachedYStart; + int nCachedLines; + + void InvalidateCachedLines(); + CPLErr IRasterIO_read_internal( + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace ); + protected: + + virtual CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void* pImage); + virtual CPLErr IWriteBlock(int nBlockXOff, int nBlockYOff, void* pImage); + virtual CPLErr IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg); + + public: + GDALClientRasterBand(GDALPipe* p, int iSrvBand, + GDALClientDataset* poDS, int nBand, GDALAccess eAccess, + int nRasterXSize, int nRasterYSize, + GDALDataType eDataType, int nBlockXSize, int nBlockYSize, + GByte abyCaps[16]); + ~GDALClientRasterBand(); + + int GetSrvBand() const { return iSrvBand; } + int SupportsInstr(InstrEnum instr) const { return abyCaps[instr / 8] & (1 << (instr % 8)); } + + void ClearOverviewCache() { aMapOvrBandsCurrent.clear(); } + + virtual CPLErr FlushCache(); + + virtual void SetDescription( const char * ); + + virtual const char* GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + virtual GDALColorInterp GetColorInterpretation(); + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + + virtual char **GetCategoryNames(); + virtual double GetNoDataValue( int *pbSuccess = NULL ); + virtual double GetMinimum( int *pbSuccess = NULL ); + virtual double GetMaximum(int *pbSuccess = NULL ); + virtual double GetOffset( int *pbSuccess = NULL ); + virtual double GetScale( int *pbSuccess = NULL ); + + virtual GDALColorTable *GetColorTable(); + virtual CPLErr SetColorTable( GDALColorTable * ); + + virtual const char *GetUnitType(); + virtual CPLErr SetUnitType( const char * ); + + virtual CPLErr Fill(double dfRealValue, double dfImaginaryValue = 0); + + virtual CPLErr SetCategoryNames( char ** ); + virtual CPLErr SetNoDataValue( double ); + virtual CPLErr SetOffset( double ); + virtual CPLErr SetScale( double ); + + virtual CPLErr GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *padfStdDev ); + virtual CPLErr ComputeStatistics( int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc, void *pProgressData ); + virtual CPLErr SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ); + virtual CPLErr ComputeRasterMinMax( int, double* ); + + virtual CPLErr GetHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ); + + virtual CPLErr GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig ** ppanHistogram, + int bForce, + GDALProgressFunc, void *pProgressData); + virtual CPLErr SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram ); + + virtual int HasArbitraryOverviews(); + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); + + virtual GDALRasterBand *GetMaskBand(); + virtual int GetMaskFlags(); + virtual CPLErr CreateMaskBand( int nFlags ); + + virtual CPLErr BuildOverviews( const char *, int, int *, + GDALProgressFunc, void * ); + + virtual GDALRasterAttributeTable *GetDefaultRAT(); + virtual CPLErr SetDefaultRAT( const GDALRasterAttributeTable * ); + + virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ); + /* + virtual GDALRasterBand *GetRasterSampleOverview( GUIntBig ); + */ + +}; + +/************************************************************************/ +/* GDALPipeBuild() */ +/************************************************************************/ + +static GDALPipe* GDALPipeBuild(CPLSpawnedProcess* sp) +{ + GDALPipe* p = (GDALPipe*)CPLMalloc(sizeof(GDALPipe)); + p->bOK = TRUE; + p->fin = CPLSpawnAsyncGetInputFileHandle(sp); + p->fout = CPLSpawnAsyncGetOutputFileHandle(sp); + p->nSocket = INVALID_SOCKET; + p->nBufferSize = 0; + return p; +} + +static GDALPipe* GDALPipeBuild(CPL_SOCKET nSocket) +{ + GDALPipe* p = (GDALPipe*)CPLMalloc(sizeof(GDALPipe)); + p->bOK = TRUE; + p->fin = CPL_FILE_INVALID_HANDLE; + p->fout = CPL_FILE_INVALID_HANDLE; + p->nSocket = nSocket; + p->nBufferSize = 0; + return p; +} + +static GDALPipe* GDALPipeBuild(CPL_FILE_HANDLE fin, CPL_FILE_HANDLE fout) +{ + GDALPipe* p = (GDALPipe*)CPLMalloc(sizeof(GDALPipe)); + p->bOK = TRUE; + p->fin = fin; + p->fout = fout; + p->nSocket = INVALID_SOCKET; + p->nBufferSize = 0; + return p; +} + +/************************************************************************/ +/* GDALPipeWrite_internal() */ +/************************************************************************/ + +static int GDALPipeWrite_internal(GDALPipe* p, const void* data, int length) +{ + if(!p->bOK) + return FALSE; + if( p->fout != CPL_FILE_INVALID_HANDLE ) + { + int nRet = CPLPipeWrite(p->fout, data, length); + if( !nRet ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Write to pipe failed"); + p->bOK = FALSE; + } + return nRet; + } + else + { + const char* pabyData = (const char*) data; + int nRemain = length; + while( nRemain > 0 ) + { + int nRet = send(p->nSocket, pabyData, nRemain, 0); + if( nRet < 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Write to socket failed"); + p->bOK = FALSE; + return FALSE; + } + pabyData += nRet; + nRemain -= nRet; + } + return TRUE; + } +} + +/************************************************************************/ +/* GDALPipeFlushBuffer() */ +/************************************************************************/ + +static int GDALPipeFlushBuffer(GDALPipe * p) +{ + if( p->nBufferSize == 0 ) + return TRUE; + if( GDALPipeWrite_internal(p, p->abyBuffer, p->nBufferSize) ) + { + p->nBufferSize = 0; + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* GDALPipeFree() */ +/************************************************************************/ + +static void GDALPipeFree(GDALPipe * p) +{ + GDALPipeFlushBuffer(p); + if( p->nSocket != INVALID_SOCKET ) + { + closesocket(p->nSocket); + WSACleanup(); + } + CPLFree(p); +} + +/************************************************************************/ +/* GDALPipeRead() */ +/************************************************************************/ + +static int GDALPipeRead(GDALPipe* p, void* data, int length) +{ + if(!p->bOK) + return FALSE; + if(!GDALPipeFlushBuffer(p)) + return FALSE; + + if( p->fout != CPL_FILE_INVALID_HANDLE ) + { + if( CPLPipeRead(p->fin, data, length) ) + return TRUE; + // fprintf(stderr, "[%d] Read from pipe failed\n", (int)getpid()); + CPLError(CE_Failure, CPLE_AppDefined, "Read from pipe failed"); + p->bOK = FALSE; + return FALSE; + } + else + { + char* pabyData = (char*) data; + int nRemain = length; + while( nRemain > 0 ) + { + int nRet = recv(p->nSocket, pabyData, nRemain, 0); + if( nRet <= 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Read from socket failed"); + p->bOK = FALSE; + return FALSE; + } + pabyData += nRet; + nRemain -= nRet; + } + return TRUE; + } + +} + +/************************************************************************/ +/* GDALPipeWrite() */ +/************************************************************************/ + +static int GDALPipeWrite(GDALPipe* p, const void* data, + int length) +{ + //return GDALPipeWrite_internal(p, data, length); + GByte* pCur = (GByte*) data; + int nRemain = length; + while( nRemain > 0 ) + { + if( p->nBufferSize + nRemain <= BUFFER_SIZE ) + { + memcpy(p->abyBuffer + p->nBufferSize, pCur, nRemain); + pCur += nRemain; + p->nBufferSize += nRemain; + nRemain = 0; + } + else if( nRemain > BUFFER_SIZE ) + { + if( !GDALPipeFlushBuffer(p) ) + return FALSE; + if( !GDALPipeWrite_internal(p, pCur, nRemain) ) + return FALSE; + pCur += nRemain; + nRemain = 0; + } + else + { + memcpy(p->abyBuffer + p->nBufferSize, pCur, + BUFFER_SIZE - p->nBufferSize); + pCur += (BUFFER_SIZE - p->nBufferSize); + nRemain -= (BUFFER_SIZE - p->nBufferSize); + p->nBufferSize = BUFFER_SIZE; + if( !GDALPipeFlushBuffer(p) ) + return FALSE; + } + } + return TRUE; +} + +/************************************************************************/ +/* GDALPipeRead() */ +/************************************************************************/ + +static int GDALPipeRead(GDALPipe* p, int* pnInt) +{ + return GDALPipeRead(p, pnInt, 4); +} + +static int GDALPipeRead(GDALPipe* p, GIntBig* pnInt) +{ + return GDALPipeRead(p, pnInt, 8); +} + +static int GDALPipeRead(GDALPipe* p, CPLErr* peErr) +{ + return GDALPipeRead(p, peErr, 4); +} + +static int GDALPipeRead(GDALPipe* p, double* pdfDouble) +{ + return GDALPipeRead(p, pdfDouble, 8); +} + +static int GDALPipeRead_nolength(GDALPipe* p, int nLength, void* pabyData) +{ + return GDALPipeRead(p, pabyData, nLength); +} + +static int GDALPipeRead(GDALPipe* p, int nExpectedLength, void* pabyData) +{ + int nLength; + return GDALPipeRead(p, &nLength) && + nLength == nExpectedLength && + GDALPipeRead_nolength(p, nLength, pabyData); +} + +static int GDALPipeRead(GDALPipe* p, char** ppszStr) +{ + int nLength; + if( !GDALPipeRead(p, &nLength) || nLength < 0 ) + { + *ppszStr = NULL; + return FALSE; + } + if( nLength == 0 ) + { + *ppszStr = NULL; + return TRUE; + } + *ppszStr = (nLength < INT_MAX-1) ? (char*) VSIMalloc(nLength + 1) : NULL; + if( *ppszStr == NULL ) + return FALSE; + if( nLength > 0 && !GDALPipeRead_nolength(p, nLength, *ppszStr) ) + { + CPLFree(*ppszStr); + *ppszStr = NULL; + return FALSE; + } + (*ppszStr)[nLength] = 0; + return TRUE; +} + + +static int GDALPipeRead(GDALPipe* p, char*** ppapszStr) +{ + int nStrCount; + if( !GDALPipeRead(p, &nStrCount) ) + return FALSE; + if( nStrCount < 0 ) + { + *ppapszStr = NULL; + return TRUE; + } + + *ppapszStr = (char**) VSIMalloc2(sizeof(char*), (nStrCount + 1)); + if( *ppapszStr == NULL ) + return FALSE; + for(int i=0;iSetColorEntry(i, &eEntry); + } + } + *ppoColorTable = poColorTable; + return TRUE; +} + +static int GDALPipeRead(GDALPipe* p, GDALRasterAttributeTable** ppoRAT) +{ + *ppoRAT = NULL; + char* pszRAT = NULL; + if( !GDALPipeRead(p, &pszRAT)) + return FALSE; + if( pszRAT == NULL ) + return TRUE; + + CPLXMLNode* poNode = CPLParseXMLString( pszRAT ); + CPLFree(pszRAT); + if( poNode == NULL ) + return FALSE; + + *ppoRAT = new GDALDefaultRasterAttributeTable(); + if( (*ppoRAT)->XMLInit(poNode, NULL) != CE_None ) + { + CPLDestroyXMLNode(poNode); + delete *ppoRAT; + *ppoRAT = NULL; + return FALSE; + } + CPLDestroyXMLNode(poNode); + return TRUE; +} + +static int GDALPipeRead(GDALPipe* p, int* pnGCPCount, GDAL_GCP** ppasGCPs) +{ + *pnGCPCount = 0; + *ppasGCPs = NULL; + int nGCPCount; + if( !GDALPipeRead(p, &nGCPCount) ) + return FALSE; + GDAL_GCP* pasGCPs = (GDAL_GCP* )CPLCalloc(nGCPCount, sizeof(GDAL_GCP)); + for(int i=0;iGDALMajorObject::SetDescription(pszDescription); + CPLFree(pszDescription); + + *ppoBand = poBand; + return TRUE; +} + +/************************************************************************/ +/* GDALSkipUntilEndOfJunkMarker() */ +/************************************************************************/ + +static int GDALSkipUntilEndOfJunkMarker(GDALPipe* p) +{ + if(!p->bOK) + return FALSE; + GByte c; + size_t nIter = 0; + int nStep = 0; + CPLString osJunk; + int nMarkerSize = (int)sizeof(abyEndOfJunkMarker); + GByte abyBuffer[sizeof(abyEndOfJunkMarker)]; + if( !GDALPipeRead_nolength(p, sizeof(abyBuffer), abyBuffer ) ) + return FALSE; + if( memcmp(abyEndOfJunkMarker, abyBuffer, sizeof(abyBuffer)) == 0 ) + return TRUE; + while(TRUE) + { + if( nIter < sizeof(abyBuffer) ) + c = abyBuffer[nIter ++]; + else if( !GDALPipeRead_nolength(p, 1, &c ) ) + return FALSE; + + if( c != 0 ) + osJunk += c; + if( c == abyEndOfJunkMarker[0] ) nStep = 1; + else if( c == abyEndOfJunkMarker[nStep] ) + { + nStep ++; + if( nStep == nMarkerSize ) + { + osJunk.resize(osJunk.size() - nMarkerSize); + if( osJunk.size() ) + CPLDebug("GDAL", "Got junk : %s", osJunk.c_str()); + return TRUE; + } + } + else + nStep = 0; + } +} + +/************************************************************************/ +/* GDALPipeWrite() */ +/************************************************************************/ + +static int GDALPipeWrite(GDALPipe* p, int nInt) +{ + return GDALPipeWrite(p, &nInt, 4); +} + +static int GDALPipeWrite(GDALPipe* p, GIntBig nInt) +{ + return GDALPipeWrite(p, &nInt, 8); +} + +static int GDALPipeWrite(GDALPipe* p, double dfDouble) +{ + return GDALPipeWrite(p, &dfDouble, 8); +} + +static int GDALPipeWrite_nolength(GDALPipe* p, int nLength, const void* pabyData) +{ + return GDALPipeWrite(p, pabyData, nLength); +} + +static int GDALPipeWrite(GDALPipe* p, int nLength, const void* pabyData) +{ + if( !GDALPipeWrite(p, nLength) || + !GDALPipeWrite_nolength(p, nLength, pabyData) ) + return FALSE; + return TRUE; +} + +static int GDALPipeWrite(GDALPipe* p, const char* pszStr) +{ + if( pszStr == NULL ) + return GDALPipeWrite(p, 0); + return GDALPipeWrite(p, (int)strlen(pszStr) + 1, pszStr); +} + +static int GDALPipeWrite(GDALPipe* p, char** papszStr) +{ + if( papszStr == NULL ) + return GDALPipeWrite(p, -1); + + int nCount = CSLCount(papszStr); + if( !GDALPipeWrite(p, nCount) ) + return FALSE; + for(int i=0; i < nCount; i++) + { + if( !GDALPipeWrite(p, papszStr[i]) ) + return FALSE; + } + return TRUE; +} + +static int GDALPipeWrite(GDALPipe* p, + std::vector& aBands, + GDALRasterBand* poBand) +{ + if( poBand == NULL ) + GDALPipeWrite(p, -1); + else + { + GDALPipeWrite(p, (int)aBands.size()); + aBands.push_back(poBand); + GDALPipeWrite(p, poBand->GetBand()); + GDALPipeWrite(p, poBand->GetAccess()); + GDALPipeWrite(p, poBand->GetXSize()); + GDALPipeWrite(p, poBand->GetYSize()); + GDALPipeWrite(p, poBand->GetRasterDataType()); + int nBlockXSize, nBlockYSize; + poBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + GDALPipeWrite(p, nBlockXSize); + GDALPipeWrite(p, nBlockYSize); + GDALPipeWrite(p, poBand->GetDescription() ); + } + return TRUE; +} + +static int GDALPipeWrite(GDALPipe* p, GDALColorTable* poColorTable) +{ + if( poColorTable == NULL ) + { + if( !GDALPipeWrite(p, -1) ) + return FALSE; + } + else + { + int nCount = poColorTable->GetColorEntryCount(); + if( !GDALPipeWrite(p, poColorTable->GetPaletteInterpretation()) || + !GDALPipeWrite(p, nCount) ) + return FALSE; + + for(int i=0; i < nCount; i++) + { + const GDALColorEntry* poColorEntry = poColorTable->GetColorEntry(i); + if( !GDALPipeWrite(p, poColorEntry->c1) || + !GDALPipeWrite(p, poColorEntry->c2) || + !GDALPipeWrite(p, poColorEntry->c3) || + !GDALPipeWrite(p, poColorEntry->c4) ) + return FALSE; + } + } + return TRUE; +} + +static int GDALPipeWrite(GDALPipe* p, const GDALRasterAttributeTable* poRAT) +{ + int bRet; + if( poRAT == NULL ) + bRet = GDALPipeWrite(p, (const char*)NULL); + else + { + CPLXMLNode* poNode = poRAT->Serialize(); + if( poNode != NULL ) + { + char* pszRAT = CPLSerializeXMLTree(poNode); + bRet = GDALPipeWrite(p, pszRAT); + CPLFree(pszRAT); + CPLDestroyXMLNode(poNode); + } + else + bRet = GDALPipeWrite(p, (const char*)NULL); + } + return bRet; +} + +static int GDALPipeWrite(GDALPipe* p, int nGCPCount, const GDAL_GCP* pasGCPs) +{ + if( !GDALPipeWrite(p, nGCPCount ) ) + return FALSE; + for(int i=0;ip->bOK ) + { + /* Store the descriptor in a free slot if available for a */ + /* later reuse */ + CPLMutexHolderD(GDALGetphDMMutex()); + for(int i = 0; i < nMaxRecycled; i ++) + { + if( aspRecycled[i] == NULL ) + { + if( !GDALEmitReset(ssp->p) ) + break; + + aspRecycled[i] = ssp; + return TRUE; + } + } + } + + if(ssp->p->bOK) + { + GDALEmitEXIT(ssp->p); + } + + CPLDebug("GDAL", "Destroy spawned process %p", ssp); + GDALPipeFree(ssp->p); + int nRet = ssp->sp ? CPLSpawnAsyncFinish(ssp->sp, TRUE, TRUE) : 0; + CPLFree(ssp); + return nRet; +} + +/************************************************************************/ +/* GDALCheckServerVersion() */ +/************************************************************************/ + +static int GDALCheckServerVersion(GDALPipe* p) +{ + GDALPipeWrite(p, INSTR_GetGDALVersion); + char bIsLSB = CPL_IS_LSB; + GDALPipeWrite_nolength(p, 1, &bIsLSB); + GDALPipeWrite(p, GDAL_RELEASE_NAME); + GDALPipeWrite(p, GDAL_VERSION_MAJOR); + GDALPipeWrite(p, GDAL_VERSION_MINOR); + GDALPipeWrite(p, GDAL_CLIENT_SERVER_PROTOCOL_MAJOR); + GDALPipeWrite(p, GDAL_CLIENT_SERVER_PROTOCOL_MINOR); + GDALPipeWrite(p, 0); /* extra bytes */ + + char* pszVersion = NULL; + int nMajor, nMinor, nProtocolMajor, nProtocolMinor, nExtraBytes; + if( !GDALPipeRead(p, &pszVersion) || + !GDALPipeRead(p, &nMajor) || + !GDALPipeRead(p, &nMinor) || + !GDALPipeRead(p, &nProtocolMajor) || + !GDALPipeRead(p, &nProtocolMinor) || + !GDALPipeRead(p, &nExtraBytes) ) + { + CPLFree(pszVersion); + return FALSE; + } + + if( nExtraBytes > 0 ) + { + void* pTemp = VSIMalloc(nExtraBytes); + if( !pTemp ) + { + CPLFree(pszVersion); + return FALSE; + } + if( !GDALPipeRead_nolength(p, nExtraBytes, pTemp) ) + { + CPLFree(pszVersion); + CPLFree(pTemp); + return FALSE; + } + CPLFree(pTemp); + } + + CPLDebug("GDAL", + "Server version : %s (%d.%d), " + "Server protocol version = %d.%d", + pszVersion, + nMajor, nMinor, + nProtocolMajor, nProtocolMinor); + CPLDebug("GDAL", + "Client version : %s (%d.%d), " + "Client protocol version = %d.%d", + GDAL_RELEASE_NAME, GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR, + GDAL_CLIENT_SERVER_PROTOCOL_MAJOR, + GDAL_CLIENT_SERVER_PROTOCOL_MINOR); + if( nProtocolMajor != GDAL_CLIENT_SERVER_PROTOCOL_MAJOR ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GDAL server (GDAL version=%s, protocol version=%d.%d) is " + "incompatible with GDAL client (GDAL version=%s, protocol version=%d.%d)", + pszVersion, + nProtocolMajor, nProtocolMinor, + GDAL_RELEASE_NAME, + GDAL_CLIENT_SERVER_PROTOCOL_MAJOR, + GDAL_CLIENT_SERVER_PROTOCOL_MINOR); + CPLFree(pszVersion); + return FALSE; + } + else if( nProtocolMinor != GDAL_CLIENT_SERVER_PROTOCOL_MINOR ) + { + CPLDebug("GDAL", "Note: client/server protocol versions differ by minor number."); + } + CPLFree(pszVersion); + return TRUE; +} + +/************************************************************************/ +/* GDALServerLoopForked() */ +/************************************************************************/ + +#ifndef WIN32 +void CPLReinitAllMutex(); + +static int GDALServerLoopForked(CPL_FILE_HANDLE fin, CPL_FILE_HANDLE fout) +{ + /* Do not try to close datasets at process closing */ + GDALNullifyOpenDatasetsList(); + /* Nullify the existing mutex to avoid issues with locked mutex by */ + /* parent's process threads */ + GDALNullifyProxyPoolSingleton(); +#ifdef CPL_MULTIPROC_PTHREAD + CPLReinitAllMutex(); +#endif + + memset(aspRecycled, 0, sizeof(aspRecycled)); + + return GDALServerLoop(fin, fout); +} +#endif + +/************************************************************************/ +/* GDALServerSpawnAsync() */ +/************************************************************************/ + +static GDALServerSpawnedProcess* GDALServerSpawnAsync() +{ + if( bRecycleChild ) + { + /* Try to find an existing unused descriptor to reuse it */ + CPLMutexHolderD(GDALGetphDMMutex()); + for(int i = 0; i < nMaxRecycled; i ++) + { + if( aspRecycled[i] != NULL ) + { + GDALServerSpawnedProcess* ssp = aspRecycled[i]; + aspRecycled[i] = NULL; + return ssp; + } + } + } + +#ifdef WIN32 + const char* pszSpawnServer = CPLGetConfigOption("GDAL_API_PROXY_SERVER", "gdalserver"); +#else + const char* pszSpawnServer = CPLGetConfigOption("GDAL_API_PROXY_SERVER", "NO"); +#endif + + const char* pszColon = strchr(pszSpawnServer, ':'); + if( pszColon != NULL && + pszColon != pszSpawnServer + 1 /* do not confuse with c:/some_path/gdalserver.exe */ ) + { + CPLString osHost(pszSpawnServer); + osHost.resize(pszColon - pszSpawnServer); + CPL_SOCKET nConnSocket = INVALID_SOCKET; + int nRet; + +#ifdef WIN32 + WSADATA wsaData; + + nRet = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (nRet != NO_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "WSAStartup() failed with error: %d\n", nRet); + return NULL; + } +#endif + +#ifdef HAVE_GETADDRINFO + struct addrinfo sHints; + struct addrinfo* psResults = NULL, *psResultsIter; + memset(&sHints, 0, sizeof(struct addrinfo)); + sHints.ai_family = AF_UNSPEC; + sHints.ai_socktype = SOCK_STREAM; + sHints.ai_flags = 0; + sHints.ai_protocol = IPPROTO_TCP; + + nRet = getaddrinfo(osHost, pszColon + 1, &sHints, &psResults); + if (nRet) + { + CPLError(CE_Failure, CPLE_AppDefined, + "getaddrinfo(): %s", gai_strerror(nRet)); + WSACleanup(); + return NULL; + } + + for( psResultsIter = psResults; + psResultsIter != NULL; + psResultsIter = psResultsIter->ai_next) + { + nConnSocket = socket(psResultsIter->ai_family, + psResultsIter->ai_socktype, + psResultsIter->ai_protocol); + if (nConnSocket == INVALID_SOCKET) + continue; + + if (connect(nConnSocket, psResultsIter->ai_addr, + psResultsIter->ai_addrlen) != SOCKET_ERROR) + break; + + closesocket(nConnSocket); + } + + freeaddrinfo(psResults); + + if (psResultsIter == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Could not connect"); + WSACleanup(); + return NULL; + } +#else + struct sockaddr_in sockAddrIn; + int nPort = atoi(pszColon + 1); + sockAddrIn.sin_family = AF_INET; + sockAddrIn.sin_addr.s_addr = inet_addr(osHost); + if (sockAddrIn.sin_addr.s_addr == INADDR_NONE) + { + struct hostent *hp; + hp = gethostbyname(osHost); + if (hp == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unknown host : %s", osHost.c_str()); + WSACleanup(); + return NULL; + } + else + { + sockAddrIn.sin_family = hp->h_addrtype; + memcpy(&(sockAddrIn.sin_addr.s_addr), hp->h_addr, hp->h_length); + } + } + sockAddrIn.sin_port = htons(nPort); + + nConnSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (nConnSocket == INVALID_SOCKET) + { + CPLError(CE_Failure, CPLE_AppDefined, + "socket() failed with error: %d", WSAGetLastError()); + WSACleanup(); + return NULL; + } + + if (connect(nConnSocket, (const SOCKADDR *)&sockAddrIn, sizeof (sockAddrIn)) == SOCKET_ERROR ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "connect() function failed with error: %d", WSAGetLastError()); + closesocket(nConnSocket); + WSACleanup(); + return NULL; + } +#endif + + GDALServerSpawnedProcess* ssp = + (GDALServerSpawnedProcess*)CPLMalloc(sizeof(GDALServerSpawnedProcess)); + ssp->sp = NULL; + ssp->p = GDALPipeBuild(nConnSocket); + + CPLDebug("GDAL", "Create spawned process %p", ssp); + if( !GDALCheckServerVersion(ssp->p) ) + { + GDALServerSpawnAsyncFinish(ssp); + return NULL; + } + return ssp; + } + +#ifndef WIN32 + VSIStatBuf sStat; + if( VSIStat(pszSpawnServer, &sStat) == 0 && sStat.st_size == 0 ) + { + int nConnSocket = socket(AF_UNIX, SOCK_STREAM, 0); + if (nConnSocket >= 0) + { + struct sockaddr_un sockAddrUnix; + sockAddrUnix.sun_family = AF_UNIX; + CPLStrlcpy(sockAddrUnix.sun_path, pszSpawnServer, sizeof(sockAddrUnix.sun_path)); + + if (connect(nConnSocket, (const SOCKADDR *)&sockAddrUnix, sizeof (sockAddrUnix)) >= 0 ) + { + GDALServerSpawnedProcess* ssp = + (GDALServerSpawnedProcess*)CPLMalloc(sizeof(GDALServerSpawnedProcess)); + ssp->sp = NULL; + ssp->p = GDALPipeBuild(nConnSocket); + + CPLDebug("GDAL", "Create spawned process %p", ssp); + if( !GDALCheckServerVersion(ssp->p) ) + { + GDALServerSpawnAsyncFinish(ssp); + return NULL; + } + return ssp; + } + else + closesocket(nConnSocket); + } + } +#endif + + if( EQUAL(pszSpawnServer, "YES") || EQUAL(pszSpawnServer, "ON") || + EQUAL(pszSpawnServer, "TRUE") || EQUAL(pszSpawnServer, "1") ) + pszSpawnServer = "gdalserver"; +#ifdef WIN32 + const char* apszGDALServer[] = { pszSpawnServer, "-stdinout", NULL }; +#else + const char* apszGDALServer[] = { pszSpawnServer, "-pipe_in", "{pipe_in}", "-pipe_out", "{pipe_out}", NULL }; + if( strstr(pszSpawnServer, "gdalserver") == NULL ) + apszGDALServer[1] = NULL; +#endif + int bCheckVersions = TRUE; + + CPLSpawnedProcess* sp; +#ifndef WIN32 + if( EQUAL(pszSpawnServer, "NO") || EQUAL(pszSpawnServer, "OFF") || + EQUAL(pszSpawnServer, "FALSE") || EQUAL(pszSpawnServer, "0") ) + { + sp = CPLSpawnAsync(GDALServerLoopForked, NULL, TRUE, TRUE, FALSE, NULL); + bCheckVersions = FALSE; + } + else +#endif + sp = CPLSpawnAsync(NULL, apszGDALServer, TRUE, TRUE, FALSE, NULL); + + if( sp == NULL ) + return NULL; + + GDALServerSpawnedProcess* ssp = + (GDALServerSpawnedProcess*)CPLMalloc(sizeof(GDALServerSpawnedProcess)); + ssp->sp = sp; + ssp->p = GDALPipeBuild(sp); + + CPLDebug("GDAL", "Create spawned process %p", ssp); + if( bCheckVersions && !GDALCheckServerVersion(ssp->p) ) + { + GDALServerSpawnAsyncFinish(ssp); + return NULL; + } + return ssp; +} + +/************************************************************************/ +/* CPLErrOnlyRet() */ +/************************************************************************/ + +static CPLErr CPLErrOnlyRet(GDALPipe* p) +{ + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + + CPLErr eRet = CE_Failure; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* RunErrorHandler() */ +/************************************************************************/ + +class GDALServerErrorDesc +{ + public: + GDALServerErrorDesc() {} + + CPLErr eErr; + int nErrNo; + CPLString osErrorMsg; +}; + +static void CPL_STDCALL RunErrorHandler(CPLErr eErr, int nErrNo, + const char* pszErrorMsg) +{ + GDALServerErrorDesc oDesc; + oDesc.eErr = eErr; + oDesc.nErrNo = nErrNo; + oDesc.osErrorMsg = pszErrorMsg; + std::vector* paoErrors = + (std::vector*) CPLGetErrorHandlerUserData(); + if( paoErrors ) + paoErrors->push_back(oDesc); +} + +/************************************************************************/ +/* RunAsyncProgress() */ +/************************************************************************/ + +static int CPL_STDCALL RunAsyncProgress(double dfComplete, + const char *pszMessage, + void *pProgressArg) +{ + /* We don't send the progress right now, since some drivers like ECW */ + /* call the progress callback from an helper thread, while calling methods */ + /* on the source dataset. So we could end up sending mixed content on the pipe */ + /* to the client. The best is to transmit the progress in a regularly called method */ + /* of the dataset, such as IReadBlock() / IRasterIO() */ + GDALServerAsyncProgress* asyncp = (GDALServerAsyncProgress*)pProgressArg; + CPLMutexHolderD(&(asyncp->hMutex)); + asyncp->bUpdated = TRUE; + asyncp->dfComplete = dfComplete; + CPLFree(asyncp->pszProgressMsg); + asyncp->pszProgressMsg = (pszMessage) ? CPLStrdup(pszMessage) : NULL; + return asyncp->bRet; +} + +/************************************************************************/ +/* RunSyncProgress() */ +/************************************************************************/ + +static int CPL_STDCALL RunSyncProgress(double dfComplete, + const char *pszMessage, + void *pProgressArg) +{ + GDALPipe* p = (GDALPipe*)pProgressArg; + if( !GDALPipeWrite(p, INSTR_Progress) || + !GDALPipeWrite(p, dfComplete) || + !GDALPipeWrite(p, pszMessage) ) + return FALSE; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return FALSE; + int bRet = FALSE; + if( !GDALPipeRead(p, &bRet) ) + return FALSE; + GDALConsumeErrors(p); + return bRet; +} + +/************************************************************************/ +/* GDALServerLoop() */ +/************************************************************************/ + +static int GDALServerLoop(GDALPipe* p, + GDALDataset* poSrcDS, + GDALProgressFunc pfnProgress, void* pProgressData) +{ + GDALDataset* poDS = NULL; + std::vector aBands; + std::vector aoErrors; + int nRet = 1; + GDALServerAsyncProgress asyncp; + memset(&asyncp, 0, sizeof(asyncp)); + asyncp.bRet = TRUE; + void* pBuffer = NULL; + int nBufferSize = 0; + + const char* pszOldVal = CPLGetConfigOption("GDAL_API_PROXY", NULL); + char* pszOldValDup = (pszOldVal) ? CPLStrdup(pszOldVal) : NULL; + CPLSetThreadLocalConfigOption("GDAL_API_PROXY", "OFF"); + + if( poSrcDS == NULL ) + CPLPushErrorHandlerEx(RunErrorHandler, &aoErrors); + + // fprintf(stderr, "[%d] started\n", (int)getpid()); + while(TRUE) + { + int instr; + if( !GDALPipeRead(p, &instr) ) + { + // fprintf(stderr, "[%d] instr failed\n", (int)getpid()); + break; + } + + // fprintf(stderr, "[%d] %s\n", (int)getpid(), (instr >= 0 && instr < INSTR_END) ? apszInstr[instr] : "unknown"); + + GDALRasterBand* poBand = NULL; + + if( instr == INSTR_EXIT ) + { + if( poSrcDS == NULL && poDS != NULL ) + { + GDALClose((GDALDatasetH)poDS); + poDS = NULL; + aBands.resize(0); + } + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, TRUE); + nRet = 0; + break; + } + else if( instr == INSTR_EXIT_FAIL ) + { + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, TRUE); + break; + } + else if( instr == INSTR_GetGDALVersion || + instr == 0x01000000 ) + { + /* Do not change this protocol ! */ + char bClientIsLSB; + char* pszClientVersion = NULL; + int nClientMajor, nClientMinor, + nClientProtocolMajor, nClientProtocolMinor, + nExtraBytes; + if( !GDALPipeRead_nolength(p, 1, &bClientIsLSB) ) + break; + if( bClientIsLSB != CPL_IS_LSB ) + { + fprintf(stderr, "Server does not understand client endianness.\n"); + break; + } + + if (!GDALPipeRead(p, &pszClientVersion) || + !GDALPipeRead(p, &nClientMajor) || + !GDALPipeRead(p, &nClientMinor) || + !GDALPipeRead(p, &nClientProtocolMajor) || + !GDALPipeRead(p, &nClientProtocolMinor) || + !GDALPipeRead(p, &nExtraBytes) ) + { + CPLFree(pszClientVersion); + break; + } + + if( nExtraBytes > 0 ) + { + void* pTemp = VSIMalloc(nExtraBytes); + if( !pTemp ) + { + CPLFree(pszClientVersion); + break; + } + if( !GDALPipeRead_nolength(p, nExtraBytes, pTemp) ) + { + CPLFree(pszClientVersion); + CPLFree(pTemp); + break; + } + CPLFree(pTemp); + } + + CPLFree(pszClientVersion); + + GDALPipeWrite(p, GDAL_RELEASE_NAME); + GDALPipeWrite(p, GDAL_VERSION_MAJOR); + GDALPipeWrite(p, GDAL_VERSION_MINOR); + GDALPipeWrite(p, GDAL_CLIENT_SERVER_PROTOCOL_MAJOR); + GDALPipeWrite(p, GDAL_CLIENT_SERVER_PROTOCOL_MINOR); + GDALPipeWrite(p, 0); /* extra bytes */ + continue; + } + else if( instr == INSTR_SetConfigOption ) + { + char *pszKey = NULL, *pszValue = NULL; + if( !GDALPipeRead(p, &pszKey) || + !GDALPipeRead(p, &pszValue) ) + { + CPLFree(pszKey); + break; + } + CPLSetConfigOption(pszKey, pszValue); + CPLFree(pszKey); + CPLFree(pszValue); + continue; + } + else if( instr == INSTR_Progress ) + { + double dfProgress; + char* pszProgressMsg = NULL; + if( !GDALPipeRead(p, &dfProgress) || + !GDALPipeRead(p, &pszProgressMsg) ) + break; + int nRet = pfnProgress(dfProgress, pszProgressMsg, pProgressData); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, nRet); + CPLFree(pszProgressMsg); + } + else if( instr == INSTR_Reset ) + { + if( poSrcDS == NULL && poDS != NULL ) + { + GDALClose((GDALDatasetH)poDS); + poDS = NULL; + MyChdirRootDirectory(); + aBands.resize(0); + } + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, TRUE); + } + else if( instr == INSTR_Open ) + { + int nAccess; + char* pszFilename = NULL; + char* pszCWD = NULL; + if( !GDALPipeRead(p, &nAccess) || + !GDALPipeRead(p, &pszFilename) || + !GDALPipeRead(p, &pszCWD) ) + { + CPLFree(pszFilename); + CPLFree(pszCWD); + break; + } + if( pszCWD != NULL ) + { + MyChdir(pszCWD); + CPLFree(pszCWD); + } + if( poSrcDS != NULL ) + poDS = poSrcDS; + else if( poDS == NULL && pszFilename != NULL ) + poDS = (GDALDataset*) GDALOpen(pszFilename, (GDALAccess)nAccess); + CPLFree(pszFilename); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, poDS != NULL); + if( poDS != NULL ) + { + CPLAssert(INSTR_END < 128); + GByte abyCaps[16]; /* 16 * 8 = 128 */ + memset(abyCaps, 0, sizeof(abyCaps)); + /* We implement all known instructions (except marker ones) */ + for(int c = 1; c < INSTR_END; c++) + { + if( c != INSTR_Band_First && c != INSTR_Band_End ) + abyCaps[c / 8] |= (1 << (c % 8)); + } + GDALPipeWrite(p, sizeof(abyCaps), abyCaps); + GDALPipeWrite(p, poDS->GetDescription() ); + GDALDriver* poDriver = poDS->GetDriver(); + if( poDriver != NULL ) + { + GDALPipeWrite(p, poDriver->GetDescription() ); + char** papszItems = poDriver->GetMetadata(); + for(int i = 0; papszItems[i] != NULL; i++ ) + { + char* pszKey = NULL; + const char* pszVal = CPLParseNameValue(papszItems[i], &pszKey ); + if( pszKey != NULL ) + { + GDALPipeWrite(p, pszKey ); + GDALPipeWrite(p, pszVal ); + CPLFree(pszKey); + } + } + GDALPipeWrite(p, (const char*)NULL); + } + else + GDALPipeWrite(p, (const char*)NULL); + + GDALPipeWrite(p, poDS->GetRasterXSize()); + GDALPipeWrite(p, poDS->GetRasterYSize()); + int nBands = poDS->GetRasterCount(); + GDALPipeWrite(p, nBands); + int i; + int bAllSame = TRUE; + GDALRasterBand* poFirstBand = NULL; + int nFBBlockXSize = 0, nFBBlockYSize = 0; + + /* Check if all bands are identical */ + for(i=0;iGetRasterBand(i+1); + if( strlen(poBand->GetDescription()) > 0 ) + { + bAllSame = FALSE; + break; + } + if( i == 0 ) + { + poFirstBand = poBand; + poBand->GetBlockSize(&nFBBlockXSize, &nFBBlockYSize); + } + else + { + int nBlockXSize, nBlockYSize; + poBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + if( poBand->GetXSize() != poFirstBand->GetXSize() || + poBand->GetYSize() != poFirstBand->GetYSize() || + poBand->GetRasterDataType() != poFirstBand->GetRasterDataType() || + nBlockXSize != nFBBlockXSize || + nBlockYSize != nFBBlockYSize ) + { + bAllSame = FALSE; + break; + } + } + } + + /* Transmit bands */ + GDALPipeWrite(p, bAllSame); + for(i=0;iGetRasterBand(i+1); + if( i > 0 && bAllSame ) + aBands.push_back(poBand); + else + GDALPipeWrite(p, aBands, poBand); + } + } + } + else if( instr == INSTR_Identify ) + { + char* pszFilename = NULL; + char* pszCWD = NULL; + if( !GDALPipeRead(p, &pszFilename) || + pszFilename == NULL || + !GDALPipeRead(p, &pszCWD) ) + { + CPLFree(pszFilename); + CPLFree(pszCWD); + break; + } + + if( pszCWD != NULL ) + { + MyChdir(pszCWD); + CPLFree(pszCWD); + } + + int bRet = GDALIdentifyDriver(pszFilename, NULL) != NULL; + CPLFree(pszFilename); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, bRet); + aoErrors.resize(0); + } + else if( instr == INSTR_Create ) + { + char* pszFilename = NULL; + char* pszCWD = NULL; + int nXSize, nYSize, nBands, nDataType; + char** papszOptions = NULL; + GDALDriver* poDriver = NULL; + if( !GDALPipeRead(p, &pszFilename) || + pszFilename == NULL || + !GDALPipeRead(p, &pszCWD) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBands) || + !GDALPipeRead(p, &nDataType) || + !GDALPipeRead(p, &papszOptions) ) + { + CPLFree(pszFilename); + CPLFree(pszCWD); + break; + } + + if( pszCWD != NULL ) + { + MyChdir(pszCWD); + CPLFree(pszCWD); + } + + const char* pszDriver = CSLFetchNameValue(papszOptions, "SERVER_DRIVER"); + CPLString osDriver; + if( pszDriver != NULL ) + { + osDriver = pszDriver; + pszDriver = osDriver.c_str(); + poDriver = (GDALDriver* )GDALGetDriverByName(pszDriver); + } + papszOptions = CSLSetNameValue(papszOptions, "SERVER_DRIVER", NULL); + if( poDriver != NULL ) + { + poDS = poDriver->Create(pszFilename, nXSize, nYSize, nBands, + (GDALDataType)nDataType, + papszOptions); + } + else + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s", + (pszDriver) ? pszDriver : "(unknown)"); + + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, poDS != NULL); + CPLFree(pszFilename); + CSLDestroy(papszOptions); + } + else if( instr == INSTR_CreateCopy ) + { + char* pszFilename = NULL; + char* pszSrcDescription = NULL; + char* pszCWD = NULL; + char** papszCreateOptions = NULL; + GDALDriver* poDriver = NULL; + int bStrict = FALSE; + + if( !GDALPipeRead(p, &pszFilename) || + pszFilename == NULL || + !GDALPipeRead(p, &pszSrcDescription) || + !GDALPipeRead(p, &pszCWD) || + !GDALPipeRead(p, &bStrict) || + !GDALPipeRead(p, &papszCreateOptions) ) + { + CPLFree(pszFilename); + CPLFree(pszSrcDescription); + CPLFree(pszCWD); + break; + } + + CPLFree(pszSrcDescription); + + if( pszCWD != NULL ) + { + MyChdir(pszCWD); + CPLFree(pszCWD); + } + + const char* pszDriver = CSLFetchNameValue(papszCreateOptions, "SERVER_DRIVER"); + CPLString osDriver; + if( pszDriver != NULL ) + { + osDriver = pszDriver; + pszDriver = osDriver.c_str(); + poDriver = (GDALDriver* )GDALGetDriverByName(pszDriver); + } + papszCreateOptions = CSLSetNameValue(papszCreateOptions, "SERVER_DRIVER", NULL); + GDALPipeWrite(p, poDriver != NULL); + if( poDriver != NULL ) + { + GDALClientDataset* poSrcDS = new GDALClientDataset(p); + if( !poSrcDS->Init(NULL, GA_ReadOnly) ) + { + delete poSrcDS; + CPLFree(pszFilename); + CSLDestroy(papszCreateOptions); + break; + } + poSrcDS->AttachAsyncProgress(&asyncp); + + poDS = poDriver->CreateCopy(pszFilename, poSrcDS, + bStrict, papszCreateOptions, + RunAsyncProgress, &asyncp); + + int bProgressRet = poSrcDS->ProcessAsyncProgress(); + GDALClose((GDALDatasetH)poSrcDS); + + if( !bProgressRet && poDS != NULL ) + { + GDALClose((GDALDatasetH)poDS); + poDS = NULL; + } + + if( !GDALEmitEXIT(p, (poDS != NULL) ? INSTR_EXIT : INSTR_EXIT_FAIL) ) + break; + } + else + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s", + (pszDriver) ? pszDriver : "(unknown)"); + + CPLFree(pszFilename); + CSLDestroy(papszCreateOptions); + } + else if( instr == INSTR_QuietDelete ) + { + char* pszFilename = NULL; + char* pszCWD = NULL; + + if( !GDALPipeRead(p, &pszFilename) || + pszFilename == NULL || + !GDALPipeRead(p, &pszCWD) ) + { + CPLFree(pszFilename); + CPLFree(pszCWD); + break; + } + + if( pszCWD != NULL ) + { + MyChdir(pszCWD); + CPLFree(pszCWD); + } + + GDALDriver::QuietDelete(pszFilename); + + GDALEmitEndOfJunkMarker(p); + CPLFree(pszFilename); + } + else if( instr == INSTR_AddBand ) + { + if( poDS == NULL ) + break; + int nType; + char** papszOptions = NULL; + if( !GDALPipeRead(p, &nType) || + !GDALPipeRead(p, &papszOptions) ) + break; + CPLErr eErr = poDS->AddBand((GDALDataType)nType, papszOptions); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr == CE_None ) + { + int nBandCount = poDS->GetRasterCount(); + GDALPipeWrite(p, aBands, poDS->GetRasterBand(nBandCount)); + } + CSLDestroy(papszOptions); + } + else if( instr == INSTR_GetGeoTransform ) + { + if( poDS == NULL ) + break; + double adfGeoTransform[6]; + CPLErr eErr = poDS->GetGeoTransform(adfGeoTransform); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr != CE_Failure ) + { + GDALPipeWrite(p, 6 * sizeof(double), adfGeoTransform); + } + } + else if( instr == INSTR_SetGeoTransform ) + { + if( poDS == NULL ) + break; + double adfGeoTransform[6]; + if( !GDALPipeRead(p, 6 * sizeof(double), adfGeoTransform) ) + break; + CPLErr eErr = poDS->SetGeoTransform(adfGeoTransform); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_GetProjectionRef ) + { + if( poDS == NULL ) + break; + const char* pszVal = poDS->GetProjectionRef(); + //GDALPipeWrite(p, strlen("some_junk\xDE"), "some_junk\xDE"); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, pszVal); + } + else if( instr == INSTR_SetProjection ) + { + if( poDS == NULL ) + break; + char* pszProjection = NULL; + if( !GDALPipeRead(p, &pszProjection) ) + break; + CPLErr eErr = poDS->SetProjection(pszProjection); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(pszProjection); + } + else if( instr == INSTR_GetGCPCount ) + { + if( poDS == NULL ) + break; + int nGCPCount = poDS->GetGCPCount(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, nGCPCount ); + } + else if( instr == INSTR_GetGCPProjection ) + { + if( poDS == NULL ) + break; + const char* pszVal = poDS->GetGCPProjection(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, pszVal); + } + else if( instr == INSTR_GetGCPs ) + { + if( poDS == NULL ) + break; + int nGCPCount = poDS->GetGCPCount(); + const GDAL_GCP* pasGCPs = poDS->GetGCPs(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, nGCPCount, pasGCPs); + } + else if( instr == INSTR_SetGCPs ) + { + if( poDS == NULL ) + break; + int nGCPCount; + GDAL_GCP* pasGCPs = NULL; + if( !GDALPipeRead(p, &nGCPCount, &pasGCPs) ) + break; + char* pszGCPProjection = NULL; + if( !GDALPipeRead(p, &pszGCPProjection) ) + break; + CPLErr eErr = poDS->SetGCPs(nGCPCount, pasGCPs, pszGCPProjection); + GDALDeinitGCPs(nGCPCount, pasGCPs); + CPLFree(pasGCPs); + CPLFree(pszGCPProjection); + + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr ); + + } + else if( instr == INSTR_GetFileList ) + { + if( poDS == NULL ) + break; + char** papszFileList = poDS->GetFileList(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, papszFileList); + CSLDestroy(papszFileList); + } + else if( instr == INSTR_FlushCache ) + { + if( poDS ) + poDS->FlushCache(); + GDALEmitEndOfJunkMarker(p); + } + /*else if( instr == INSTR_SetDescription ) + { + if( poDS == NULL ) + break; + char* pszDescription = NULL; + if( !GDALPipeRead(p, &pszDescription) ) + break; + poDS->SetDescription(pszDescription); + CPLFree(pszDescription); + GDALEmitEndOfJunkMarker(p); + }*/ + else if( instr == INSTR_GetMetadata ) + { + if( poDS == NULL ) + break; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &pszDomain) ) + break; + char** papszMD = poDS->GetMetadata(pszDomain); + GDALEmitEndOfJunkMarker(p); + CPLFree(pszDomain); + GDALPipeWrite(p, papszMD); + } + else if( instr == INSTR_GetMetadataItem ) + { + if( poDS == NULL ) + break; + char* pszName = NULL; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &pszName) || + !GDALPipeRead(p, &pszDomain) ) + { + CPLFree(pszName); + CPLFree(pszDomain); + break; + } + const char* pszVal = poDS->GetMetadataItem(pszName, pszDomain); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, pszVal); + CPLFree(pszName); + CPLFree(pszDomain); + } + else if( instr == INSTR_SetMetadata ) + { + if( poDS == NULL ) + break; + char** papszMetadata = NULL; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &papszMetadata) || + !GDALPipeRead(p, &pszDomain) ) + { + CSLDestroy(papszMetadata); + CPLFree(pszDomain); + break; + } + CPLErr eErr = poDS->SetMetadata(papszMetadata, pszDomain); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CSLDestroy(papszMetadata); + CPLFree(pszDomain); + } + else if( instr == INSTR_SetMetadataItem ) + { + if( poDS == NULL ) + break; + char* pszName = NULL; + char* pszValue = NULL; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &pszName) || + !GDALPipeRead(p, &pszValue) || + !GDALPipeRead(p, &pszDomain) ) + { + CPLFree(pszName); + CPLFree(pszValue); + CPLFree(pszDomain); + break; + } + CPLErr eErr = poDS->SetMetadataItem(pszName, pszValue, pszDomain); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(pszName); + CPLFree(pszValue); + CPLFree(pszDomain); + } + else if( instr == INSTR_IBuildOverviews ) + { + if( poDS == NULL ) + break; + char* pszResampling = NULL; + int nOverviews; + int* panOverviewList = NULL; + int nListBands; + int* panBandList = NULL; + if( !GDALPipeRead(p, &pszResampling) || + !GDALPipeRead(p, &nOverviews) || + !GDALPipeRead(p, nOverviews, &panOverviewList) || + !GDALPipeRead(p, &nListBands) || + !GDALPipeRead(p, nListBands, &panBandList) ) + { + CPLFree(pszResampling); + CPLFree(panOverviewList); + CPLFree(panBandList); + break; + } + + CPLErr eErr = poDS->BuildOverviews(pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + RunSyncProgress, p); + + CPLFree(pszResampling); + CPLFree(panOverviewList); + CPLFree(panBandList); + + if( !GDALEmitEXIT(p, (eErr != CE_Failure) ? INSTR_EXIT : INSTR_EXIT_FAIL) ) + break; + } + else if( instr == INSTR_AdviseRead ) + { + if( poDS == NULL ) + break; + int nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize; + int nDT; + int nBandCount; + int *panBandList = NULL; + char** papszOptions = NULL; + int nLength = 0; + if( !GDALPipeRead(p, &nXOff) || + !GDALPipeRead(p, &nYOff) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBufXSize) || + !GDALPipeRead(p, &nBufYSize) || + !GDALPipeRead(p, &nDT) || + !GDALPipeRead(p, &nBandCount) || + !GDALPipeRead(p, &nLength) ) + { + break; + } + + /* panBandList can be NULL, hence the following test */ + /* to check if we have band numbers to actually read */ + if( nLength != 0 ) + { + if( nLength != (int)sizeof(int) * nBandCount ) + { + break; + } + + panBandList = (int*) VSIMalloc(nLength); + if( panBandList == NULL ) + break; + + if( !GDALPipeRead_nolength(p, nLength, (void*)panBandList) ) + { + VSIFree(panBandList); + break; + } + } + + if (!GDALPipeRead(p, &papszOptions) ) + { + CPLFree(panBandList); + CSLDestroy(papszOptions); + break; + } + + CPLErr eErr = poDS->AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, + (GDALDataType)nDT, + nBandCount, panBandList, + papszOptions); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(panBandList); + CSLDestroy(papszOptions); + } + else if( instr == INSTR_IRasterIO_Read ) + { + if( poDS == NULL ) + break; + int nXOff, nYOff, nXSize, nYSize; + int nBufXSize, nBufYSize; + GDALDataType eBufType; + int nBufType; + int nBandCount; + GSpacing nPixelSpace, nLineSpace, nBandSpace; + int* panBandMap = NULL; + if( !GDALPipeRead(p, &nXOff) || + !GDALPipeRead(p, &nYOff) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBufXSize) || + !GDALPipeRead(p, &nBufYSize) || + !GDALPipeRead(p, &nBufType) || + !GDALPipeRead(p, &nBandCount) || + !GDALPipeRead(p, nBandCount, &panBandMap) || + !GDALPipeRead(p, &nPixelSpace) || + !GDALPipeRead(p, &nLineSpace) || + !GDALPipeRead(p, &nBandSpace) ) + { + CPLFree(panBandMap); + break; + } + /* Note: only combinations of nPixelSpace, nLineSpace and + nBandSpace that lead to compate band-interleaved or pixel- + interleaved buffers are valid. Other combinations will lead to segfault */ + eBufType = (GDALDataType)nBufType; + int nSize = nBufXSize * nBufYSize * nBandCount * + (GDALGetDataTypeSize(eBufType) / 8); + if( nSize > nBufferSize ) + { + nBufferSize = nSize; + pBuffer = CPLRealloc(pBuffer, nSize); + } + + CPLErr eErr = poDS->RasterIO(GF_Read, + nXOff, nYOff, nXSize, nYSize, + pBuffer, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + NULL); + CPLFree(panBandMap); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr != CE_Failure ) + GDALPipeWrite(p, nSize, pBuffer); + } + else if( instr == INSTR_IRasterIO_Write ) + { + if( poDS == NULL ) + break; + int nXOff, nYOff, nXSize, nYSize; + int nBufXSize, nBufYSize; + GDALDataType eBufType; + int nBufType; + int nBandCount; + GSpacing nPixelSpace, nLineSpace, nBandSpace; + int* panBandMap = NULL; + if( !GDALPipeRead(p, &nXOff) || + !GDALPipeRead(p, &nYOff) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBufXSize) || + !GDALPipeRead(p, &nBufYSize) || + !GDALPipeRead(p, &nBufType) || + !GDALPipeRead(p, &nBandCount) || + !GDALPipeRead(p, nBandCount, &panBandMap) || + !GDALPipeRead(p, &nPixelSpace) || + !GDALPipeRead(p, &nLineSpace) || + !GDALPipeRead(p, &nBandSpace) ) + { + CPLFree(panBandMap); + break; + } + /* Note: only combinations of nPixelSpace, nLineSpace and + nBandSpace that lead to compate band-interleaved or pixel- + interleaved buffers are valid. Other combinations will lead to segfault */ + eBufType = (GDALDataType)nBufType; + int nExpectedSize = nBufXSize * nBufYSize * nBandCount * + (GDALGetDataTypeSize(eBufType) / 8); + int nSize; + if( !GDALPipeRead(p, &nSize) ) + break; + if( nSize != nExpectedSize ) + break; + if( nSize > nBufferSize ) + { + nBufferSize = nSize; + pBuffer = CPLRealloc(pBuffer, nSize); + } + if( !GDALPipeRead_nolength(p, nSize, pBuffer) ) + break; + + CPLErr eErr = poDS->RasterIO(GF_Write, + nXOff, nYOff, nXSize, nYSize, + pBuffer, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + NULL); + CPLFree(panBandMap); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_CreateMaskBand ) + { + if( poDS == NULL ) + break; + int nFlags; + if( !GDALPipeRead(p, &nFlags) ) + break; + CPLErr eErr = poDS->CreateMaskBand(nFlags); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr > INSTR_Band_First && instr < INSTR_Band_End ) + { + int iBand; + if( !GDALPipeRead(p, &iBand) ) + break; + if( iBand < 0 || iBand >= (int)aBands.size() ) + break; + poBand = aBands[iBand]; + } + else + break; + + if( instr == INSTR_Band_FlushCache ) + { + CPLErr eErr = poBand->FlushCache(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_GetCategoryNames ) + { + char** papszCategoryNames = poBand->GetCategoryNames(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, papszCategoryNames); + } + else if( instr == INSTR_Band_SetCategoryNames ) + { + char** papszCategoryNames = NULL; + if( !GDALPipeRead(p, &papszCategoryNames) ) + break; + CPLErr eErr = poBand->SetCategoryNames(papszCategoryNames); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CSLDestroy(papszCategoryNames); + } + else if( instr == INSTR_Band_SetDescription ) + { + char* pszDescription = NULL; + if( !GDALPipeRead(p, &pszDescription) ) + break; + poBand->SetDescription(pszDescription); + CPLFree(pszDescription); + GDALEmitEndOfJunkMarker(p); + } + else if( instr == INSTR_Band_GetMetadata ) + { + char* pszDomain = NULL; + if( !GDALPipeRead(p, &pszDomain) ) + break; + char** papszMD = poBand->GetMetadata(pszDomain); + GDALEmitEndOfJunkMarker(p); + CPLFree(pszDomain); + GDALPipeWrite(p, papszMD); + } + else if( instr == INSTR_Band_GetMetadataItem ) + { + char* pszName = NULL; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &pszName) || + !GDALPipeRead(p, &pszDomain) ) + { + CPLFree(pszName); + CPLFree(pszDomain); + break; + } + const char* pszVal = poBand->GetMetadataItem(pszName, pszDomain); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, pszVal); + CPLFree(pszName); + CPLFree(pszDomain); + } + else if( instr == INSTR_Band_SetMetadata ) + { + char** papszMetadata = NULL; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &papszMetadata) || + !GDALPipeRead(p, &pszDomain) ) + { + CSLDestroy(papszMetadata); + CPLFree(pszDomain); + break; + } + CPLErr eErr = poBand->SetMetadata(papszMetadata, pszDomain); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CSLDestroy(papszMetadata); + CPLFree(pszDomain); + } + else if( instr == INSTR_Band_SetMetadataItem ) + { + char* pszName = NULL; + char* pszValue = NULL; + char* pszDomain = NULL; + if( !GDALPipeRead(p, &pszName) || + !GDALPipeRead(p, &pszValue) || + !GDALPipeRead(p, &pszDomain) ) + { + CPLFree(pszName); + CPLFree(pszValue); + CPLFree(pszDomain); + break; + } + CPLErr eErr = poBand->SetMetadataItem(pszName, pszValue, pszDomain); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(pszName); + CPLFree(pszValue); + CPLFree(pszDomain); + } + else if( instr == INSTR_Band_GetColorInterpretation ) + { + GDALColorInterp eInterp = poBand->GetColorInterpretation(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eInterp); + } + else if( instr == INSTR_Band_SetColorInterpretation ) + { + int nVal; + if( !GDALPipeRead(p, &nVal ) ) + break; + CPLErr eErr = poBand->SetColorInterpretation((GDALColorInterp)nVal); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_GetNoDataValue ) + { + int bSuccess; + double dfVal = poBand->GetNoDataValue(&bSuccess); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, bSuccess); + GDALPipeWrite(p, dfVal); + } + else if( instr == INSTR_Band_GetMinimum ) + { + int bSuccess; + double dfVal = poBand->GetMinimum(&bSuccess); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, bSuccess); + GDALPipeWrite(p, dfVal); + } + else if( instr == INSTR_Band_GetMaximum ) + { + int bSuccess; + double dfVal = poBand->GetMaximum(&bSuccess); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, bSuccess); + GDALPipeWrite(p, dfVal); + } + else if( instr == INSTR_Band_GetScale ) + { + int bSuccess; + double dfVal = poBand->GetScale(&bSuccess); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, bSuccess); + GDALPipeWrite(p, dfVal); + } + else if( instr == INSTR_Band_GetOffset ) + { + int bSuccess; + double dfVal = poBand->GetOffset(&bSuccess); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, bSuccess); + GDALPipeWrite(p, dfVal); + } + else if( instr == INSTR_Band_SetNoDataValue ) + { + double dfVal; + if( !GDALPipeRead(p, &dfVal ) ) + break; + CPLErr eErr = poBand->SetNoDataValue(dfVal); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_SetOffset ) + { + double dfVal; + if( !GDALPipeRead(p, &dfVal ) ) + break; + CPLErr eErr = poBand->SetOffset(dfVal); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_SetScale ) + { + double dfVal; + if( !GDALPipeRead(p, &dfVal ) ) + break; + CPLErr eErr = poBand->SetScale(dfVal); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_IReadBlock ) + { + int nBlockXOff, nBlockYOff; + if( !GDALPipeRead(p, &nBlockXOff) || + !GDALPipeRead(p, &nBlockYOff) ) + break; + int nBlockXSize, nBlockYSize; + poBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + int nSize = nBlockXSize * nBlockYSize * + (GDALGetDataTypeSize(poBand->GetRasterDataType()) / 8); + if( nSize > nBufferSize ) + { + nBufferSize = nSize; + pBuffer = CPLRealloc(pBuffer, nSize); + } + CPLErr eErr = poBand->ReadBlock(nBlockXOff, nBlockYOff, pBuffer); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + GDALPipeWrite(p, nSize, pBuffer); + } + else if( instr == INSTR_Band_IWriteBlock ) + { + int nBlockXOff, nBlockYOff, nSize; + if( !GDALPipeRead(p, &nBlockXOff) || + !GDALPipeRead(p, &nBlockYOff) || + !GDALPipeRead(p, &nSize) ) + break; + int nBlockXSize, nBlockYSize; + poBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + int nExpectedSize = nBlockXSize * nBlockYSize * + (GDALGetDataTypeSize(poBand->GetRasterDataType()) / 8); + if( nExpectedSize != nSize ) + break; + if( nSize > nBufferSize ) + { + nBufferSize = nSize; + pBuffer = CPLRealloc(pBuffer, nSize); + } + if( !GDALPipeRead_nolength(p, nSize, pBuffer) ) + break; + + CPLErr eErr = poBand->WriteBlock(nBlockXOff, nBlockYOff, pBuffer); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_IRasterIO_Read ) + { + int nXOff, nYOff, nXSize, nYSize; + int nBufXSize, nBufYSize; + GDALDataType eBufType; + int nBufType; + if( !GDALPipeRead(p, &nXOff) || + !GDALPipeRead(p, &nYOff) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBufXSize) || + !GDALPipeRead(p, &nBufYSize) || + !GDALPipeRead(p, &nBufType) ) + break; + eBufType = (GDALDataType)nBufType; + int nSize = nBufXSize * nBufYSize * + (GDALGetDataTypeSize(eBufType) / 8); + if( nSize > nBufferSize ) + { + nBufferSize = nSize; + pBuffer = CPLRealloc(pBuffer, nSize); + } + + CPLErr eErr = poBand->RasterIO(GF_Read, + nXOff, nYOff, nXSize, nYSize, + pBuffer, nBufXSize, nBufYSize, + eBufType, 0, 0, NULL); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + GDALPipeWrite(p, nSize, pBuffer); + } + else if( instr == INSTR_Band_IRasterIO_Write ) + { + int nXOff, nYOff, nXSize, nYSize; + int nBufXSize, nBufYSize; + GDALDataType eBufType; + int nBufType; + if( !GDALPipeRead(p, &nXOff) || + !GDALPipeRead(p, &nYOff) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBufXSize) || + !GDALPipeRead(p, &nBufYSize) || + !GDALPipeRead(p, &nBufType) ) + break; + eBufType = (GDALDataType)nBufType; + int nExpectedSize = nBufXSize * nBufYSize * + (GDALGetDataTypeSize(eBufType) / 8); + int nSize; + if( !GDALPipeRead(p, &nSize) ) + break; + if( nSize != nExpectedSize ) + break; + if( nSize > nBufferSize ) + { + nBufferSize = nSize; + pBuffer = CPLRealloc(pBuffer, nSize); + } + if( !GDALPipeRead_nolength(p, nSize, pBuffer) ) + break; + + CPLErr eErr = poBand->RasterIO(GF_Write, + nXOff, nYOff, nXSize, nYSize, + pBuffer, nBufXSize, nBufYSize, + eBufType, 0, 0, NULL); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_GetStatistics ) + { + int bApproxOK, bForce; + if( !GDALPipeRead(p, &bApproxOK) || + !GDALPipeRead(p, &bForce) ) + break; + double dfMin = 0.0, dfMax = 0.0, dfMean = 0.0, dfStdDev = 0.0; + CPLErr eErr = poBand->GetStatistics(bApproxOK, bForce, + &dfMin, &dfMax, &dfMean, &dfStdDev); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr == CE_None ) + { + GDALPipeWrite(p, dfMin); + GDALPipeWrite(p, dfMax); + GDALPipeWrite(p, dfMean); + GDALPipeWrite(p, dfStdDev); + } + } + else if( instr == INSTR_Band_ComputeStatistics ) + { + int bApproxOK; + if( !GDALPipeRead(p, &bApproxOK) ) + break; + double dfMin = 0.0, dfMax = 0.0, dfMean = 0.0, dfStdDev = 0.0; + CPLErr eErr = poBand->ComputeStatistics(bApproxOK, + &dfMin, &dfMax, &dfMean, &dfStdDev, + NULL, NULL); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr != CE_Failure ) + { + GDALPipeWrite(p, dfMin); + GDALPipeWrite(p, dfMax); + GDALPipeWrite(p, dfMean); + GDALPipeWrite(p, dfStdDev); + } + } + else if( instr == INSTR_Band_SetStatistics ) + { + double dfMin, dfMax, dfMean, dfStdDev; + if( !GDALPipeRead(p, &dfMin) || + !GDALPipeRead(p, &dfMax) || + !GDALPipeRead(p, &dfMean) || + !GDALPipeRead(p, &dfStdDev) ) + break; + CPLErr eErr = poBand->SetStatistics(dfMin, dfMax, dfMean, dfStdDev); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_ComputeRasterMinMax ) + { + int bApproxOK; + if( !GDALPipeRead(p, &bApproxOK) ) + break; + double adfMinMax[2]; + CPLErr eErr = poBand->ComputeRasterMinMax(bApproxOK, adfMinMax); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr != CE_Failure ) + { + GDALPipeWrite(p, adfMinMax[0]); + GDALPipeWrite(p, adfMinMax[1]); + } + } + else if( instr == INSTR_Band_GetHistogram ) + { + double dfMin, dfMax; + int nBuckets, bIncludeOutOfRange, bApproxOK; + if( !GDALPipeRead(p, &dfMin) || + !GDALPipeRead(p, &dfMax) || + !GDALPipeRead(p, &nBuckets) || + !GDALPipeRead(p, &bIncludeOutOfRange) || + !GDALPipeRead(p, &bApproxOK) ) + break; + GUIntBig* panHistogram = (GUIntBig*) VSIMalloc2(sizeof(GUIntBig), nBuckets); + if( panHistogram == NULL ) + break; + CPLErr eErr = poBand->GetHistogram(dfMin, dfMax, + nBuckets, panHistogram, + bIncludeOutOfRange, bApproxOK, NULL, NULL); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr != CE_Failure ) + { + GDALPipeWrite(p, nBuckets * sizeof(GUIntBig), panHistogram); + } + CPLFree(panHistogram); + } + else if( instr == INSTR_Band_GetDefaultHistogram ) + { + double dfMin, dfMax; + int nBuckets; + int bForce; + if( !GDALPipeRead(p, &bForce) ) + break; + GUIntBig* panHistogram = NULL; + CPLErr eErr = poBand->GetDefaultHistogram(&dfMin, &dfMax, + &nBuckets, &panHistogram, + bForce, NULL, NULL); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + if( eErr != CE_Failure ) + { + GDALPipeWrite(p, dfMin); + GDALPipeWrite(p, dfMax); + GDALPipeWrite(p, nBuckets); + GDALPipeWrite(p, nBuckets * sizeof(GUIntBig) , panHistogram); + } + CPLFree(panHistogram); + } + else if( instr == INSTR_Band_SetDefaultHistogram ) + { + double dfMin, dfMax; + int nBuckets; + GUIntBig* panHistogram = NULL; + if( !GDALPipeRead(p, &dfMin) || + !GDALPipeRead(p, &dfMax) || + !GDALPipeRead(p, &nBuckets) || + !GDALPipeRead(p, nBuckets, &panHistogram) ) + { + CPLFree(panHistogram); + break; + } + CPLErr eErr = poBand->SetDefaultHistogram(dfMin, dfMax, + nBuckets, panHistogram); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(panHistogram); + } + else if( instr == INSTR_Band_HasArbitraryOverviews ) + { + int nVal = poBand->HasArbitraryOverviews(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, nVal); + } + else if( instr == INSTR_Band_GetOverviewCount ) + { + int nVal = poBand->GetOverviewCount(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, nVal); + } + else if( instr == INSTR_Band_GetOverview ) + { + int iOvr; + if( !GDALPipeRead(p, &iOvr) ) + break; + GDALRasterBand* poOvrBand = poBand->GetOverview(iOvr); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, aBands, poOvrBand); + } + else if( instr == INSTR_Band_GetMaskBand ) + { + GDALRasterBand* poMaskBand = poBand->GetMaskBand(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, aBands, poMaskBand); + } + else if( instr == INSTR_Band_GetMaskFlags ) + { + int nVal = poBand->GetMaskFlags(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, nVal); + } + else if( instr == INSTR_Band_CreateMaskBand ) + { + int nFlags; + if( !GDALPipeRead(p, &nFlags) ) + break; + CPLErr eErr = poBand->CreateMaskBand(nFlags); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_Fill ) + { + double dfReal, dfImag; + if( !GDALPipeRead(p, &dfReal) || + !GDALPipeRead(p, &dfImag) ) + break; + CPLErr eErr = poBand->Fill(dfReal, dfImag); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_GetColorTable ) + { + GDALColorTable* poColorTable = poBand->GetColorTable(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, poColorTable); + } + else if( instr == INSTR_Band_SetColorTable ) + { + GDALColorTable* poColorTable = NULL; + if( !GDALPipeRead(p, &poColorTable) ) + break; + CPLErr eErr = poBand->SetColorTable(poColorTable); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + delete poColorTable; + } + else if( instr == INSTR_Band_GetUnitType ) + { + const char* pszVal = poBand->GetUnitType(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, pszVal); + } + else if( instr == INSTR_Band_SetUnitType ) + { + char* pszUnitType = NULL; + if( !GDALPipeRead(p, &pszUnitType) ) + break; + CPLErr eErr = poBand->SetUnitType(pszUnitType); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(pszUnitType); + } + else if( instr == INSTR_Band_BuildOverviews ) + { + char* pszResampling = NULL; + int nOverviews; + int* panOverviewList = NULL; + if( !GDALPipeRead(p, &pszResampling) || + !GDALPipeRead(p, &nOverviews) || + !GDALPipeRead(p, nOverviews, &panOverviewList) ) + { + CPLFree(pszResampling); + CPLFree(panOverviewList); + break; + } + CPLErr eErr = poBand->BuildOverviews(pszResampling, nOverviews, + panOverviewList, NULL, NULL); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CPLFree(pszResampling); + CPLFree(panOverviewList); + } + else if( instr == INSTR_Band_GetDefaultRAT ) + { + const GDALRasterAttributeTable* poRAT = poBand->GetDefaultRAT(); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, poRAT); + } + else if( instr == INSTR_Band_SetDefaultRAT ) + { + GDALRasterAttributeTable* poRAT = NULL; + if( !GDALPipeRead(p, &poRAT) ) + break; + CPLErr eErr = poBand->SetDefaultRAT(poRAT); + delete poRAT; + + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + } + else if( instr == INSTR_Band_AdviseRead ) + { + int nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize; + int nDT; + char** papszOptions = NULL; + if( !GDALPipeRead(p, &nXOff) || + !GDALPipeRead(p, &nYOff) || + !GDALPipeRead(p, &nXSize) || + !GDALPipeRead(p, &nYSize) || + !GDALPipeRead(p, &nBufXSize) || + !GDALPipeRead(p, &nBufYSize) || + !GDALPipeRead(p, &nDT) || + !GDALPipeRead(p, &papszOptions) ) + break; + CPLErr eErr = poBand->AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, + (GDALDataType)nDT, papszOptions); + GDALEmitEndOfJunkMarker(p); + GDALPipeWrite(p, eErr); + CSLDestroy(papszOptions); + } + + if( poSrcDS == NULL ) + { + GDALPipeWrite(p, (int)aoErrors.size()); + for(int i=0;i<(int)aoErrors.size();i++) + { + GDALPipeWrite(p, aoErrors[i].eErr); + GDALPipeWrite(p, aoErrors[i].nErrNo); + GDALPipeWrite(p, aoErrors[i].osErrorMsg); + } + aoErrors.resize(0); + } + else + GDALPipeWrite(p, 0); + } + + if( poSrcDS == NULL ) + CPLPopErrorHandler(); + + CPLSetThreadLocalConfigOption("GDAL_API_PROXY", pszOldValDup); + CPLFree(pszOldValDup); + + // fprintf(stderr, "[%d] finished = %d\n", (int)getpid(), nRet); + + if( poSrcDS == NULL && poDS != NULL ) + GDALClose((GDALDatasetH)poDS); + + CPLFree(pBuffer); + + CPLFree(asyncp.pszProgressMsg); + if( asyncp.hMutex ) + CPLDestroyMutex(asyncp.hMutex); + + return nRet; +} + +/************************************************************************/ +/* GDALServerLoop() */ +/************************************************************************/ + +int GDALServerLoop(CPL_FILE_HANDLE fin, CPL_FILE_HANDLE fout) +{ +#ifndef WIN32 + unsetenv("CPL_SHOW_MEM_STATS"); +#endif + CPLSetConfigOption("GDAL_API_PROXY", "NO"); + + GDALPipe* p = GDALPipeBuild(fin, fout); + + int nRet = GDALServerLoop(p, NULL, NULL, NULL); + + GDALPipeFree(p); + + return nRet; +} + +/************************************************************************/ +/* GDALServerLoopSocket() */ +/************************************************************************/ + +int GDALServerLoopSocket(CPL_SOCKET nSocket) +{ +#ifndef WIN32 + unsetenv("CPL_SHOW_MEM_STATS"); +#endif + CPLSetConfigOption("GDAL_API_PROXY", "NO"); + + GDALPipe* p = GDALPipeBuild(nSocket); + + int nRet = GDALServerLoop(p, NULL, NULL, NULL); + + GDALPipeFree(p); + + return nRet; +} + +/************************************************************************/ +/* GDALClientDataset() */ +/************************************************************************/ + +GDALClientDataset::GDALClientDataset(GDALServerSpawnedProcess* ssp) +{ + this->ssp = ssp; + this->p = ssp->p; + bFreeDriver = FALSE; + nGCPCount = 0; + pasGCPs = NULL; + async = NULL; + memset(abyCaps, 0, sizeof(abyCaps)); +} + +/************************************************************************/ +/* GDALClientDataset() */ +/************************************************************************/ + +GDALClientDataset::GDALClientDataset(GDALPipe* p) +{ + this->ssp = NULL; + this->p = p; + bFreeDriver = FALSE; + nGCPCount = 0; + pasGCPs = NULL; + async = NULL; + memset(abyCaps, 0, sizeof(abyCaps)); +} +/************************************************************************/ +/* ~GDALClientDataset() */ +/************************************************************************/ + +GDALClientDataset::~GDALClientDataset() +{ + FlushCache(); + + ProcessAsyncProgress(); + + std::map::iterator oIter = aoMapMetadata.begin(); + for( ; oIter != aoMapMetadata.end(); ++oIter ) + CSLDestroy(oIter->second); + + std::map< std::pair, char*>::iterator oIterItem = + aoMapMetadataItem.begin(); + for( ; oIterItem != aoMapMetadataItem.end(); ++oIterItem ) + CPLFree(oIterItem->second); + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs(nGCPCount, pasGCPs); + CPLFree(pasGCPs); + } + + if( ssp != NULL ) + GDALServerSpawnAsyncFinish(ssp); + if( bFreeDriver ) + delete poDriver; +} + +/************************************************************************/ +/* ProcessAsyncProgress() */ +/************************************************************************/ + +int GDALClientDataset::ProcessAsyncProgress() +{ + if( !async ) return TRUE; + CPLMutexHolderD(&(async->hMutex)); + if( !async->bUpdated ) return async->bRet; + async->bUpdated = FALSE; + if( !GDALPipeWrite(p, INSTR_Progress) || + !GDALPipeWrite(p, async->dfComplete) || + !GDALPipeWrite(p, async->pszProgressMsg) ) + return TRUE; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return TRUE; + + int bRet = TRUE; + if( !GDALPipeRead(p, &bRet) ) + return TRUE; + async->bRet = bRet; + GDALConsumeErrors(p); + return bRet; +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr GDALClientDataset::IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + if( !SupportsInstr(INSTR_IBuildOverviews) ) + return GDALPamDataset::IBuildOverviews(pszResampling, nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData); + + CLIENT_ENTER(); + if( nOverviews < 0 || nOverviews > 1000 || + nListBands < 0 || nListBands > GetRasterCount() ) + return CE_Failure; + + GDALPipeWriteConfigOption(p, "BIGTIFF_OVERVIEW"); + GDALPipeWriteConfigOption(p, "COMPRESS_OVERVIEW"); + GDALPipeWriteConfigOption(p, "PREDICTOR_OVERVIEW"); + GDALPipeWriteConfigOption(p, "JPEG_QUALITY_OVERVIEW"); + GDALPipeWriteConfigOption(p, "PHOTOMETRIC_OVERVIEW"); + GDALPipeWriteConfigOption(p, "USE_RRD"); + GDALPipeWriteConfigOption(p, "HFA_USE_RRD"); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_OVR_BLOCKSIZE"); + GDALPipeWriteConfigOption(p, "GTIFF_DONT_WRITE_BLOCKS"); + + if( !GDALPipeWrite(p, INSTR_IBuildOverviews) || + !GDALPipeWrite(p, pszResampling) || + !GDALPipeWrite(p, nOverviews) || + !GDALPipeWrite(p, nOverviews * sizeof(int), panOverviewList) || + !GDALPipeWrite(p, nListBands) || + !GDALPipeWrite(p, nListBands * sizeof(int), panBandList) ) + return CE_Failure; + + if( GDALServerLoop(p, NULL, pfnProgress, pProgressData) != 0 ) + { + GDALConsumeErrors(p); + return CE_Failure; + } + + GDALConsumeErrors(p); + + for(int i=0; iClearOverviewCache(); + + return CE_None; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr GDALClientDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) +{ + if( !SupportsInstr(( eRWFlag == GF_Read ) ? INSTR_IRasterIO_Read : INSTR_IRasterIO_Write ) ) + return GDALPamDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + psExtraArg ); + + CLIENT_ENTER(); + CPLErr eRet = CE_Failure; + + ProcessAsyncProgress(); + + int nDataTypeSize = GDALGetDataTypeSize(eBufType) / 8; + int bDirectCopy; + if( nPixelSpace == nDataTypeSize && + nLineSpace == nBufXSize * nDataTypeSize && + (nBandSpace == nBufYSize * nLineSpace || + (nBandSpace == 0 && nBandCount == 1)) ) + { + bDirectCopy = TRUE; + } + else if( nBandCount > 1 && + nPixelSpace == nBandCount * nDataTypeSize && + nLineSpace == nBufXSize * nPixelSpace && + nBandSpace == nBandCount ) + { + bDirectCopy = TRUE; + } + else + bDirectCopy = FALSE; + + if( eRWFlag == GF_Write ) + { + for(int i=0;iInvalidateCachedLines(); + } + + if( !GDALPipeWrite(p, ( eRWFlag == GF_Read ) ? INSTR_IRasterIO_Read : INSTR_IRasterIO_Write ) || + !GDALPipeWrite(p, nXOff) || + !GDALPipeWrite(p, nYOff) || + !GDALPipeWrite(p, nXSize) || + !GDALPipeWrite(p, nYSize) || + !GDALPipeWrite(p, nBufXSize) || + !GDALPipeWrite(p, nBufYSize) || + !GDALPipeWrite(p, eBufType) || + !GDALPipeWrite(p, nBandCount) || + !GDALPipeWrite(p, nBandCount * sizeof(int), panBandMap) ) + return CE_Failure; + + if( bDirectCopy ) + { + if( !GDALPipeWrite(p, nPixelSpace) || + !GDALPipeWrite(p, nLineSpace) || + !GDALPipeWrite(p, nBandSpace) ) + return CE_Failure; + } + else + { + if( !GDALPipeWrite(p, nPixelSpace * 0) || + !GDALPipeWrite(p, nLineSpace * 0) || + !GDALPipeWrite(p, nBandSpace * 0) ) + return CE_Failure; + } + + if( eRWFlag == GF_Read ) + { + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet != CE_Failure ) + { + int nSize; + if( !GDALPipeRead(p, &nSize) ) + return CE_Failure; + GIntBig nExpectedSize = (GIntBig)nBufXSize * nBufYSize * nBandCount * nDataTypeSize; + if( nSize != nExpectedSize ) + return CE_Failure; + if( bDirectCopy ) + { + if( !GDALPipeRead_nolength(p, nSize, pData) ) + return CE_Failure; + } + else + { + GByte* pBuf = (GByte*)VSIMalloc(nSize); + if( pBuf == NULL ) + return CE_Failure; + if( !GDALPipeRead_nolength(p, nSize, pBuf) ) + { + VSIFree(pBuf); + return CE_Failure; + } + for(int iBand=0;iBand 0 ) + { + GDALDeinitGCPs(nGCPCount, pasGCPs); + CPLFree(pasGCPs); + pasGCPs = NULL; + } + nGCPCount = 0; + + if( !GDALPipeRead(p, &nGCPCount, &pasGCPs) ) + return NULL; + + GDALConsumeErrors(p); + return pasGCPs; +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +CPLErr GDALClientDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ) +{ + if( !SupportsInstr(INSTR_SetGCPs) ) + return GDALPamDataset::SetGCPs(nGCPCount, pasGCPList, pszGCPProjection); + + CLIENT_ENTER(); + if( !GDALPipeWrite(p, INSTR_SetGCPs) || + !GDALPipeWrite(p, nGCPCount, pasGCPList) || + !GDALPipeWrite(p, pszGCPProjection) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char** GDALClientDataset::GetFileList() +{ + if( !SupportsInstr(INSTR_GetFileList) ) + return GDALPamDataset::GetFileList(); + + CLIENT_ENTER(); + if( !GDALPipeWrite(p, INSTR_GetFileList) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + char** papszFileList = NULL; + if( !GDALPipeRead(p, &papszFileList) ) + return NULL; + GDALConsumeErrors(p); + + /* If server is Windows and client is Unix, then replace backslahes */ + /* by slashes */ +#ifndef WIN32 + char** papszIter = papszFileList; + while( papszIter != NULL && *papszIter != NULL ) + { + char* pszIter = *papszIter; + char* pszBackSlash; + while( (pszBackSlash = strchr(pszIter, '\\')) != NULL ) + { + *pszBackSlash = '/'; + pszIter = pszBackSlash + 1; + } + papszIter ++; + } +#endif + + return papszFileList; +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char** GDALClientDataset::GetMetadata( const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_GetMetadata) ) + return GDALPamDataset::GetMetadata(pszDomain); + + CLIENT_ENTER(); + if( pszDomain == NULL ) + pszDomain = ""; + std::map::iterator oIter = aoMapMetadata.find(CPLString(pszDomain)); + if( oIter != aoMapMetadata.end() ) + { + CSLDestroy(oIter->second); + aoMapMetadata.erase(oIter); + } + if( !GDALPipeWrite(p, INSTR_GetMetadata) || + !GDALPipeWrite(p, pszDomain) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + char** papszMD = NULL; + if( !GDALPipeRead(p, &papszMD) ) + return NULL; + aoMapMetadata[pszDomain] = papszMD; + GDALConsumeErrors(p); + return papszMD; +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +/*void GDALClientDataset::SetDescription( const char * pszDescription ) +{ + sDescription = pszDescription; + if( !GDALPipeWrite(p, INSTR_SetDescription) || + !GDALPipeWrite(p, pszDescription) || + !GDALSkipUntilEndOfJunkMarker(p)) + return; + GDALConsumeErrors(p); +}*/ + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char* GDALClientDataset::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_GetMetadataItem) ) + return GDALPamDataset::GetMetadataItem(pszName, pszDomain); + + CLIENT_ENTER(); + if( pszDomain == NULL ) + pszDomain = ""; + std::pair oPair = + std::pair (CPLString(pszDomain), CPLString(pszName)); + std::map< std::pair, char*>::iterator oIter = + aoMapMetadataItem.find(oPair); + if( oIter != aoMapMetadataItem.end() ) + { + CPLFree(oIter->second); + aoMapMetadataItem.erase(oIter); + } + if( !GDALPipeWrite(p, INSTR_GetMetadataItem) || + !GDALPipeWrite(p, pszName) || + !GDALPipeWrite(p, pszDomain) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + char* pszItem = NULL; + if( !GDALPipeRead(p, &pszItem) ) + return NULL; + aoMapMetadataItem[oPair] = pszItem; + GDALConsumeErrors(p); + return pszItem; +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +CPLErr GDALClientDataset::SetMetadata( char ** papszMetadata, + const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_SetMetadata) ) + return GDALPamDataset::SetMetadata(papszMetadata, pszDomain); + + CLIENT_ENTER(); + if( !GDALPipeWrite(p, INSTR_SetMetadata) || + !GDALPipeWrite(p, papszMetadata) || + !GDALPipeWrite(p, pszDomain) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALClientDataset::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_SetMetadataItem) ) + return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain); + + CLIENT_ENTER(); + if( !GDALPipeWrite(p, INSTR_SetMetadataItem) || + !GDALPipeWrite(p, pszName) || + !GDALPipeWrite(p, pszValue) || + !GDALPipeWrite(p, pszDomain) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void GDALClientDataset::FlushCache() +{ + if( !SupportsInstr(INSTR_FlushCache) ) + { + GDALPamDataset::FlushCache(); + return; + } + + for(int i=0;iInvalidateCachedLines(); + + CLIENT_ENTER(); + SetPamFlags(0); + GDALPamDataset::FlushCache(); + if( !GDALPipeWrite(p, INSTR_FlushCache) || + !GDALSkipUntilEndOfJunkMarker(p) ) + return; + GDALConsumeErrors(p); +} + +/************************************************************************/ +/* AddBand() */ +/************************************************************************/ + +CPLErr GDALClientDataset::AddBand( GDALDataType eType, + char **papszOptions ) +{ + if( !SupportsInstr(INSTR_AddBand) ) + return GDALPamDataset::AddBand(eType, papszOptions); + + CLIENT_ENTER(); + if( !GDALPipeWrite(p, INSTR_AddBand) || + !GDALPipeWrite(p, eType) || + !GDALPipeWrite(p, papszOptions) ) + return CE_Failure; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + CPLErr eRet = CE_Failure; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet == CE_None ) + { + GDALRasterBand* poBand = NULL; + if( !GDALPipeRead(p, this, &poBand, abyCaps) ) + return CE_Failure; + SetBand(GetRasterCount() + 1, poBand); + } + GDALConsumeErrors(p); + return eRet; +} + + +/************************************************************************/ +/* AdviseRead() */ +/************************************************************************/ + +CPLErr GDALClientDataset::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + char **papszOptions ) +{ + if( !SupportsInstr(INSTR_AdviseRead) ) + return GDALPamDataset::AdviseRead(nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eDT, nBandCount, panBandList, + papszOptions); + + CLIENT_ENTER(); + if( !GDALPipeWrite(p, INSTR_AdviseRead) || + !GDALPipeWrite(p, nXOff) || + !GDALPipeWrite(p, nYOff) || + !GDALPipeWrite(p, nXSize) || + !GDALPipeWrite(p, nYSize) || + !GDALPipeWrite(p, nBufXSize) || + !GDALPipeWrite(p, nBufYSize) || + !GDALPipeWrite(p, eDT) || + !GDALPipeWrite(p, nBandCount) || + !GDALPipeWrite(p, panBandList ? nBandCount * sizeof(int) : 0, panBandList) || + !GDALPipeWrite(p, papszOptions) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +CPLErr GDALClientDataset::CreateMaskBand( int nFlags ) +{ + if( !SupportsInstr(INSTR_CreateMaskBand) ) + return GDALPamDataset::CreateMaskBand(nFlags); + + CLIENT_ENTER(); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_INTERNAL_MASK", bRecycleChild); + if( !GDALPipeWrite(p, INSTR_CreateMaskBand) || + !GDALPipeWrite(p, nFlags) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* GDALClientRasterBand() */ +/************************************************************************/ + +GDALClientRasterBand::GDALClientRasterBand(GDALPipe* p, int iSrvBand, + GDALClientDataset* poDS, + int nBand, GDALAccess eAccess, + int nRasterXSize, int nRasterYSize, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize, + GByte abyCapsIn[16]) +{ + this->p = p; + this->iSrvBand = iSrvBand; + this->poDS = poDS; + this->nBand = nBand; + this->eAccess = eAccess; + this->nRasterXSize = nRasterXSize; + this->nRasterYSize = nRasterYSize; + this->eDataType = eDataType; + this->nBlockXSize = nBlockXSize; + this->nBlockYSize = nBlockYSize; + papszCategoryNames = NULL; + poColorTable = NULL; + pszUnitType = NULL; + poMaskBand = NULL; + poRAT = NULL; + memcpy(abyCaps, abyCapsIn, sizeof(abyCaps)); + bEnableLineCaching = CSLTestBoolean(CPLGetConfigOption("GDAL_API_PROXY_LINE_CACHING", "YES")); + nSuccessiveLinesRead = 0; + eLastBufType = GDT_Unknown; + nLastYOff = -1; + pabyCachedLines = NULL; + eCachedBufType = GDT_Unknown; + nCachedYStart = -1; + nCachedLines = 0; + +} + +/************************************************************************/ +/* ~GDALClientRasterBand() */ +/************************************************************************/ + +GDALClientRasterBand::~GDALClientRasterBand() +{ + CSLDestroy(papszCategoryNames); + delete poColorTable; + CPLFree(pszUnitType); + delete poMaskBand; + delete poRAT; + CPLFree(pabyCachedLines); + + std::map::iterator oIter = aMapOvrBands.begin(); + for( ; oIter != aMapOvrBands.end(); ++oIter ) + delete oIter->second; + + std::map< std::pair, char*>::iterator oIterItem = + aoMapMetadataItem.begin(); + for( ; oIterItem != aoMapMetadataItem.end(); ++oIterItem ) + CPLFree(oIterItem->second); + + std::map::iterator oIterMD = aoMapMetadata.begin(); + for( ; oIterMD != aoMapMetadata.end(); ++oIterMD ) + CSLDestroy(oIterMD->second); + + for(int i=0; i < (int)apoOldMaskBands.size(); i++) + delete apoOldMaskBands[i]; +} + +/************************************************************************/ +/* CreateFakeMaskBand() */ +/************************************************************************/ + +GDALRasterBand* GDALClientRasterBand::CreateFakeMaskBand() +{ + if( poMaskBand == NULL ) + poMaskBand = new GDALAllValidMaskBand(this); + return poMaskBand; +} + +/************************************************************************/ +/* WriteInstr() */ +/************************************************************************/ + +int GDALClientRasterBand::WriteInstr(InstrEnum instr) +{ + return GDALPipeWrite(p, instr) && + GDALPipeWrite(p, iSrvBand); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::FlushCache() +{ + if( !SupportsInstr(INSTR_Band_FlushCache) ) + return GDALPamRasterBand::FlushCache(); + + InvalidateCachedLines(); + + CLIENT_ENTER(); + CPLErr eErr = GDALPamRasterBand::FlushCache(); + if( eErr == CE_None ) + { + if( !WriteInstr(INSTR_Band_FlushCache) ) + return CE_Failure; + return CPLErrOnlyRet(p); + } + return eErr; +} +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +char ** GDALClientRasterBand::GetCategoryNames() +{ + if( !SupportsInstr(INSTR_Band_GetCategoryNames) ) + return GDALPamRasterBand::GetCategoryNames(); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_GetCategoryNames) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + CSLDestroy(papszCategoryNames); + papszCategoryNames = NULL; + if( !GDALPipeRead(p, &papszCategoryNames) ) + return NULL; + GDALConsumeErrors(p); + return papszCategoryNames; +} + +/************************************************************************/ +/* SetCategoryNames() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetCategoryNames( char ** papszCategoryNames ) +{ + if( !SupportsInstr(INSTR_Band_SetCategoryNames) ) + return GDALPamRasterBand::SetCategoryNames(papszCategoryNames); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetCategoryNames) || + !GDALPipeWrite(p, papszCategoryNames) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +void GDALClientRasterBand::SetDescription( const char * pszDescription ) +{ + if( !SupportsInstr(INSTR_Band_SetDescription) ) + { + GDALPamRasterBand::SetDescription(pszDescription); + return; + } + + CLIENT_ENTER(); + sDescription = pszDescription; + if( !WriteInstr(INSTR_Band_SetDescription) || + !GDALPipeWrite(p, pszDescription) || + !GDALSkipUntilEndOfJunkMarker(p)) + return; + GDALConsumeErrors(p); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char** GDALClientRasterBand::GetMetadata( const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_Band_GetMetadata) ) + return GDALPamRasterBand::GetMetadata(pszDomain); + + CLIENT_ENTER(); + if( pszDomain == NULL ) + pszDomain = ""; + std::map::iterator oIter = aoMapMetadata.find(CPLString(pszDomain)); + if( oIter != aoMapMetadata.end() ) + { + CSLDestroy(oIter->second); + aoMapMetadata.erase(oIter); + } + if( !WriteInstr(INSTR_Band_GetMetadata) || + !GDALPipeWrite(p, pszDomain) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + char** papszMD = NULL; + if( !GDALPipeRead(p, &papszMD) ) + return NULL; + aoMapMetadata[pszDomain] = papszMD; + GDALConsumeErrors(p); + return papszMD; +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char* GDALClientRasterBand::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_Band_GetMetadataItem) ) + return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain); + + CLIENT_ENTER(); + if( pszDomain == NULL ) + pszDomain = ""; + std::pair oPair = + std::pair (CPLString(pszDomain), CPLString(pszName)); + std::map< std::pair, char*>::iterator oIter = + aoMapMetadataItem.find(oPair); + if( oIter != aoMapMetadataItem.end() ) + { + CPLFree(oIter->second); + aoMapMetadataItem.erase(oIter); + } + if( !WriteInstr(INSTR_Band_GetMetadataItem) || + !GDALPipeWrite(p, pszName) || + !GDALPipeWrite(p, pszDomain) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + char* pszItem = NULL; + if( !GDALPipeRead(p, &pszItem) ) + return NULL; + aoMapMetadataItem[oPair] = pszItem; + GDALConsumeErrors(p); + return pszItem; +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetMetadata( char ** papszMetadata, + const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_Band_SetMetadata) ) + return GDALPamRasterBand::SetMetadata(papszMetadata, pszDomain); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetMetadata) || + !GDALPipeWrite(p, papszMetadata) || + !GDALPipeWrite(p, pszDomain) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) +{ + if( !SupportsInstr(INSTR_Band_SetMetadataItem) ) + return GDALPamRasterBand::SetMetadataItem(pszName, pszValue, pszDomain); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetMetadataItem) || + !GDALPipeWrite(p, pszName) || + !GDALPipeWrite(p, pszValue) || + !GDALPipeWrite(p, pszDomain) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp GDALClientRasterBand::GetColorInterpretation() +{ + if( !SupportsInstr(INSTR_Band_GetColorInterpretation) ) + return GDALPamRasterBand::GetColorInterpretation(); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_GetColorInterpretation) ) + return GCI_Undefined; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return GCI_Undefined; + + int nInt; + if( !GDALPipeRead(p, &nInt) ) + return GCI_Undefined; + GDALConsumeErrors(p); + return (GDALColorInterp)nInt; +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetColorInterpretation(GDALColorInterp eInterp) +{ + if( !SupportsInstr(INSTR_Band_SetColorInterpretation) ) + return GDALPamRasterBand::SetColorInterpretation(eInterp); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetColorInterpretation) || + !GDALPipeWrite(p, eInterp) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* GetStatistics() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev ) +{ + if( !SupportsInstr(INSTR_Band_GetStatistics) ) + return GDALPamRasterBand::GetStatistics( + bApproxOK, bForce, pdfMin, pdfMax, pdfMean, pdfStdDev); + + CLIENT_ENTER(); + if( !bApproxOK && CSLTestBoolean(CPLGetConfigOption("GDAL_API_PROXY_FORCE_APPROX", "NO")) ) + bApproxOK = TRUE; + CPLErr eDefaultRet = CE_Failure; + if( CSLTestBoolean(CPLGetConfigOption("QGIS_HACK", "NO")) ) + { + if( pdfMin ) *pdfMin = 0; + if( pdfMax ) *pdfMax = 255; + if( pdfMean ) *pdfMean = 0; + if( pdfStdDev ) *pdfStdDev = 0; + eDefaultRet = CE_None; + } + if( !WriteInstr( INSTR_Band_GetStatistics) || + !GDALPipeWrite(p, bApproxOK) || + !GDALPipeWrite(p, bForce) ) + return eDefaultRet; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return eDefaultRet; + + CPLErr eRet = eDefaultRet; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet == CE_None ) + { + double dfMin, dfMax, dfMean, dfStdDev; + if( !GDALPipeRead(p, &dfMin) || + !GDALPipeRead(p, &dfMax) || + !GDALPipeRead(p, &dfMean) || + !GDALPipeRead(p, &dfStdDev) ) + return eDefaultRet; + if( pdfMin ) *pdfMin = dfMin; + if( pdfMax ) *pdfMax = dfMax; + if( pdfMean ) *pdfMean = dfMean; + if( pdfStdDev ) *pdfStdDev = dfStdDev; + } + else if( eDefaultRet == CE_None ) + eRet = eDefaultRet; + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* ComputeStatistics() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::ComputeStatistics( int bApproxOK, + double *pdfMin, + double *pdfMax, + double *pdfMean, + double *pdfStdDev, + GDALProgressFunc pfnProgress, + void *pProgressData ) +{ + if( !SupportsInstr(INSTR_Band_ComputeStatistics) ) + return GDALPamRasterBand::ComputeStatistics( + bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev, pfnProgress, pProgressData); + + CLIENT_ENTER(); + if( !bApproxOK && CSLTestBoolean(CPLGetConfigOption("GDAL_API_PROXY_FORCE_APPROX", "NO")) ) + bApproxOK = TRUE; + if( !WriteInstr(INSTR_Band_ComputeStatistics) || + !GDALPipeWrite(p, bApproxOK) ) + return CE_Failure; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + + CPLErr eRet = CE_Failure; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet != CE_Failure ) + { + double dfMin, dfMax, dfMean, dfStdDev; + if( !GDALPipeRead(p, &dfMin) || + !GDALPipeRead(p, &dfMax) || + !GDALPipeRead(p, &dfMean) || + !GDALPipeRead(p, &dfStdDev) ) + return CE_Failure; + if( pdfMin ) *pdfMin = dfMin; + if( pdfMax ) *pdfMax = dfMax; + if( pdfMean ) *pdfMean = dfMean; + if( pdfStdDev ) *pdfStdDev = dfStdDev; + } + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* SetStatistics() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ) +{ + if( !SupportsInstr(INSTR_Band_SetStatistics) ) + return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetStatistics) || + !GDALPipeWrite(p, dfMin) || + !GDALPipeWrite(p, dfMax) || + !GDALPipeWrite(p, dfMean) || + !GDALPipeWrite(p, dfStdDev) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* ComputeRasterMinMax() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::ComputeRasterMinMax( int bApproxOK, + double* padfMinMax ) +{ + if( !SupportsInstr(INSTR_Band_ComputeRasterMinMax) ) + return GDALPamRasterBand::ComputeRasterMinMax(bApproxOK, padfMinMax); + + CLIENT_ENTER(); + if( !bApproxOK && CSLTestBoolean(CPLGetConfigOption("GDAL_API_PROXY_FORCE_APPROX", "NO")) ) + bApproxOK = TRUE; + if( !WriteInstr(INSTR_Band_ComputeRasterMinMax) || + !GDALPipeWrite(p, bApproxOK) ) + return CE_Failure; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + + CPLErr eRet = CE_Failure; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet != CE_Failure ) + { + if( !GDALPipeRead(p, padfMinMax + 0) || + !GDALPipeRead(p, padfMinMax + 1) ) + return CE_Failure; + } + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* GetHistogram() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::GetHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram, + int bIncludeOutOfRange, + int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ) +{ + if( !SupportsInstr(INSTR_Band_GetHistogram) ) + return GDALPamRasterBand::GetHistogram( + dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK, pfnProgress, pProgressData); + + CLIENT_ENTER(); + if( !bApproxOK && CSLTestBoolean(CPLGetConfigOption("GDAL_API_PROXY_FORCE_APPROX", "NO")) ) + bApproxOK = TRUE; + CPLErr eDefaultRet = CE_Failure; + if( CSLTestBoolean(CPLGetConfigOption("QGIS_HACK", "NO")) ) + { + memset(panHistogram, 0, sizeof(GUIntBig) * nBuckets); + eDefaultRet = CE_None; + } + if( !WriteInstr(INSTR_Band_GetHistogram) || + !GDALPipeWrite(p, dfMin) || + !GDALPipeWrite(p, dfMax) || + !GDALPipeWrite(p, nBuckets) || + !GDALPipeWrite(p, bIncludeOutOfRange) || + !GDALPipeWrite(p, bApproxOK) ) + return eDefaultRet; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return eDefaultRet; + + CPLErr eRet = eDefaultRet; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet != CE_Failure ) + { + int nSize; + if( !GDALPipeRead(p, &nSize) || + nSize != nBuckets * (int)sizeof(GUIntBig) || + !GDALPipeRead_nolength(p, nSize, panHistogram) ) + return eDefaultRet; + } + else if( eDefaultRet == CE_None ) + eRet = eDefaultRet; + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* GetDefaultHistogram() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::GetDefaultHistogram( double *pdfMin, + double *pdfMax, + int *pnBuckets, + GUIntBig ** ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void *pProgressData ) +{ + if( !SupportsInstr(INSTR_Band_GetDefaultHistogram) ) + return GDALPamRasterBand::GetDefaultHistogram( + pdfMin, pdfMax, pnBuckets, ppanHistogram, bForce, pfnProgress, pProgressData); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_GetDefaultHistogram) || + !GDALPipeWrite(p, bForce) ) + return CE_Failure; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + CPLErr eRet = CE_Failure; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + if( eRet != CE_Failure ) + { + double dfMin, dfMax; + int nBuckets, nSize; + if( !GDALPipeRead(p, &dfMin) || + !GDALPipeRead(p, &dfMax) || + !GDALPipeRead(p, &nBuckets) || + !GDALPipeRead(p, &nSize) ) + return CE_Failure; + if( nSize != nBuckets * (int)sizeof(GUIntBig) ) + return CE_Failure; + if( pdfMin ) *pdfMin = dfMin; + if( pdfMax ) *pdfMax = dfMax; + if( pnBuckets ) *pnBuckets = nBuckets; + if( ppanHistogram ) + { + *ppanHistogram = (GUIntBig*)VSIMalloc(nSize); + if( *ppanHistogram == NULL ) + return CE_Failure; + if( !GDALPipeRead_nolength(p, nSize, *ppanHistogram) ) + return CE_Failure; + } + else + { + GUIntBig *panHistogram = (GUIntBig*)VSIMalloc(nSize); + if( panHistogram == NULL ) + return CE_Failure; + if( !GDALPipeRead_nolength(p, nSize, panHistogram) ) + { + CPLFree(panHistogram); + return CE_Failure; + } + CPLFree(panHistogram); + } + } + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* SetDefaultHistogram() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram ) +{ + if( !SupportsInstr(INSTR_Band_SetDefaultHistogram) ) + return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets, panHistogram); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetDefaultHistogram) || + !GDALPipeWrite(p, dfMin) || + !GDALPipeWrite(p, dfMax) || + !GDALPipeWrite(p, nBuckets) || + !GDALPipeWrite(p, nBuckets * sizeof(GUIntBig), panHistogram) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void* pImage) +{ + if( !SupportsInstr(INSTR_Band_IReadBlock) ) + return CE_Failure; + + CLIENT_ENTER(); + if( poDS != NULL ) + ((GDALClientDataset*)poDS)->ProcessAsyncProgress(); + + if( !WriteInstr(INSTR_Band_IReadBlock) || + !GDALPipeWrite(p, nBlockXOff) || + !GDALPipeWrite(p, nBlockYOff) ) + return CE_Failure; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + + CPLErr eRet = CE_Failure; + if( !GDALPipeRead(p, &eRet) ) + return eRet; + int nSize; + if( !GDALPipeRead(p, &nSize) || + nSize != nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType) / 8) || + !GDALPipeRead_nolength(p, nSize, pImage) ) + return CE_Failure; + + GDALConsumeErrors(p); + return eRet; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void* pImage) +{ + if( !SupportsInstr(INSTR_Band_IWriteBlock) ) + return CE_Failure; + + InvalidateCachedLines(); + + CLIENT_ENTER(); + int nSize = nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType) / 8); + if( !WriteInstr(INSTR_Band_IWriteBlock) || + !GDALPipeWrite(p, nBlockXOff) || + !GDALPipeWrite(p, nBlockYOff) || + !GDALPipeWrite(p, nSize, pImage) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* IRasterIO_read_internal() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::IRasterIO_read_internal( + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace) +{ + CPLErr eRet = CE_Failure; + + if( !WriteInstr(INSTR_Band_IRasterIO_Read) || + !GDALPipeWrite(p, nXOff) || + !GDALPipeWrite(p, nYOff) || + !GDALPipeWrite(p, nXSize) || + !GDALPipeWrite(p, nYSize) || + !GDALPipeWrite(p, nBufXSize) || + !GDALPipeWrite(p, nBufYSize) || + !GDALPipeWrite(p, eBufType) ) + return CE_Failure; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CE_Failure; + + if( !GDALPipeRead(p, &eRet) ) + return eRet; + + int nSize; + if( !GDALPipeRead(p, &nSize) ) + return CE_Failure; + int nDataTypeSize = GDALGetDataTypeSize(eBufType) / 8; + GIntBig nExpectedSize = (GIntBig)nBufXSize * nBufYSize * nDataTypeSize; + if( nSize != nExpectedSize ) + return CE_Failure; + if( nPixelSpace == nDataTypeSize && + nLineSpace == nBufXSize * nDataTypeSize ) + { + if( !GDALPipeRead_nolength(p, nSize, pData) ) + return CE_Failure; + } + else + { + GByte* pBuf = (GByte*)VSIMalloc(nSize); + if( pBuf == NULL ) + return CE_Failure; + if( !GDALPipeRead_nolength(p, nSize, pBuf) ) + { + VSIFree(pBuf); + return CE_Failure; + } + for(int j=0;jProcessAsyncProgress(); + + if( eRWFlag == GF_Read ) + { + /*if( GetAccess() == GA_Update ) + FlushCache();*/ + + /* Detect scanline reading pattern and read several rows in advance */ + /* to save a few client/server roundtrips */ + if( bEnableLineCaching && + nXOff == 0 && nXSize == nRasterXSize && nYSize == 1 && + nBufXSize == nXSize && nBufYSize == nYSize ) + { + int nBufTypeSize = GDALGetDataTypeSize(eBufType) / 8; + + /* Is the current line already cached ? */ + if( nCachedYStart >= 0 && + nYOff >= nCachedYStart && nYOff < nCachedYStart + nCachedLines && + eBufType == eCachedBufType ) + { + nSuccessiveLinesRead ++; + + int nCachedBufTypeSize = GDALGetDataTypeSize(eCachedBufType) / 8; + GDALCopyWords(pabyCachedLines + (nYOff - nCachedYStart) * nXSize * nCachedBufTypeSize, + eCachedBufType, nCachedBufTypeSize, + pData, eBufType, nPixelSpace, + nXSize); + nLastYOff = nYOff; + eLastBufType = eBufType; + return CE_None; + } + + if( nYOff == nLastYOff + 1 && + eBufType == eLastBufType ) + { + nSuccessiveLinesRead ++; + if( nSuccessiveLinesRead >= 2 ) + { + if( pabyCachedLines == NULL ) + { + nCachedLines = 10 * 1024 * 1024 / (nXSize * nBufTypeSize); + if( nCachedLines > 1 ) + pabyCachedLines = (GByte*) VSIMalloc( + nCachedLines * nXSize * nBufTypeSize); + } + if( pabyCachedLines != NULL ) + { + int nLinesToRead = nCachedLines; + if( nYOff + nLinesToRead > nRasterYSize ) + nLinesToRead = nRasterYSize - nYOff; + eRet = IRasterIO_read_internal( nXOff, nYOff, nXSize, nLinesToRead, + pabyCachedLines, nXSize, nLinesToRead, + eBufType, + nBufTypeSize, nBufTypeSize * nXSize ); + if( eRet == CE_None ) + { + eCachedBufType = eBufType; + nCachedYStart = nYOff; + + int nCachedBufTypeSize = GDALGetDataTypeSize(eCachedBufType) / 8; + GDALCopyWords(pabyCachedLines + (nYOff - nCachedYStart) * nXSize * nCachedBufTypeSize, + eCachedBufType, nCachedBufTypeSize, + pData, eBufType, nPixelSpace, + nXSize); + nLastYOff = nYOff; + eLastBufType = eBufType; + + return CE_None; + } + else + InvalidateCachedLines(); + } + } + } + else + InvalidateCachedLines(); + } + else + InvalidateCachedLines(); + + nLastYOff = nYOff; + eLastBufType = eBufType; + + return IRasterIO_read_internal( nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace ); + } + else + { + InvalidateCachedLines(); + + if( !WriteInstr(INSTR_Band_IRasterIO_Write) || + !GDALPipeWrite(p, nXOff) || + !GDALPipeWrite(p, nYOff) || + !GDALPipeWrite(p, nXSize) || + !GDALPipeWrite(p, nYSize) || + !GDALPipeWrite(p, nBufXSize) || + !GDALPipeWrite(p, nBufYSize) || + !GDALPipeWrite(p, eBufType) ) + return CE_Failure; + + int nDataTypeSize = GDALGetDataTypeSize(eBufType) / 8; + GIntBig nSizeBig = (GIntBig)nBufXSize * nBufYSize * nDataTypeSize; + int nSize = (int)nSizeBig; + if( nSizeBig != nSize ) + return CE_Failure; + if( nPixelSpace == nDataTypeSize && + nLineSpace == nBufXSize * nDataTypeSize ) + { + if( !GDALPipeWrite(p, nSize, pData) ) + return CE_Failure; + } + else + { + GByte* pBuf = (GByte*)VSIMalloc(nSize); + if( pBuf == NULL ) + return CE_Failure; + for(int j=0;j::iterator oIter = + aMapOvrBandsCurrent.find(iOverview); + if( oIter != aMapOvrBandsCurrent.end() ) + return oIter->second; + + if( !WriteInstr(INSTR_Band_GetOverview) || + !GDALPipeWrite(p, iOverview) ) + return NULL; + + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + GDALRasterBand* poBand = NULL; + if( !GDALPipeRead(p, (GDALClientDataset*) NULL, &poBand, abyCaps) ) + return NULL; + + GDALConsumeErrors(p); + + aMapOvrBands[iOverview] = poBand; + aMapOvrBandsCurrent[iOverview] = poBand; + return poBand; +} + +/************************************************************************/ +/* GetMaskBand() */ +/************************************************************************/ + +GDALRasterBand *GDALClientRasterBand::GetMaskBand() +{ + if( !SupportsInstr(INSTR_Band_GetMaskBand) ) + return GDALPamRasterBand::GetMaskBand(); + + CLIENT_ENTER(); + if( poMaskBand ) + return poMaskBand; + + if( !WriteInstr(INSTR_Band_GetMaskBand) ) + return CreateFakeMaskBand(); + + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return CreateFakeMaskBand(); + + GDALRasterBand* poBand = NULL; + if( !GDALPipeRead(p, (GDALClientDataset*) NULL, &poBand, abyCaps) ) + return CreateFakeMaskBand(); + + GDALConsumeErrors(p); + + poMaskBand = poBand; + return poMaskBand; +} + +/************************************************************************/ +/* GetMaskFlags() */ +/************************************************************************/ + +int GDALClientRasterBand::GetMaskFlags() +{ + if( !SupportsInstr(INSTR_Band_GetMaskFlags) ) + return GDALPamRasterBand::GetMaskFlags(); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_GetMaskFlags) ) + return 0; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return 0; + int nFlags; + if( !GDALPipeRead(p, &nFlags) ) + return 0; + GDALConsumeErrors(p); + return nFlags; +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::CreateMaskBand( int nFlags ) +{ + if( !SupportsInstr(INSTR_Band_CreateMaskBand) ) + return GDALPamRasterBand::CreateMaskBand(nFlags); + + CLIENT_ENTER(); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_INTERNAL_MASK", bRecycleChild); + if( !WriteInstr(INSTR_Band_CreateMaskBand) || + !GDALPipeWrite(p, nFlags) ) + return CE_Failure; + CPLErr eErr = CPLErrOnlyRet(p); + if( eErr == CE_None && poMaskBand != NULL ) + { + apoOldMaskBands.push_back(poMaskBand); + poMaskBand = NULL; + } + return eErr; +} + +/************************************************************************/ +/* Fill() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::Fill(double dfRealValue, double dfImaginaryValue) +{ + if( !SupportsInstr(INSTR_Band_Fill) ) + return GDALPamRasterBand::Fill(dfRealValue, dfImaginaryValue); + + InvalidateCachedLines(); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_Fill) || + !GDALPipeWrite(p, dfRealValue) || + !GDALPipeWrite(p, dfImaginaryValue) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* BuildOverviews() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::BuildOverviews( const char * pszResampling, + int nOverviews, + int * panOverviewList, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + if( !SupportsInstr(INSTR_Band_BuildOverviews) ) + return GDALPamRasterBand::BuildOverviews(pszResampling, nOverviews, panOverviewList, + pfnProgress, pProgressData); + + InvalidateCachedLines(); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_BuildOverviews) || + !GDALPipeWrite(p, pszResampling) || + !GDALPipeWrite(p, nOverviews) || + !GDALPipeWrite(p, nOverviews * sizeof(int), panOverviewList) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* GetDefaultRAT() */ +/************************************************************************/ + +GDALRasterAttributeTable *GDALClientRasterBand::GetDefaultRAT() +{ + if( !SupportsInstr(INSTR_Band_GetDefaultRAT) ) + return GDALPamRasterBand::GetDefaultRAT(); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_GetDefaultRAT) ) + return NULL; + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return NULL; + + GDALRasterAttributeTable* poNewRAT = NULL; + if( !GDALPipeRead(p, &poNewRAT) ) + return NULL; + + if( poNewRAT != NULL && poRAT != NULL ) + { + *poRAT = *poNewRAT; + delete poNewRAT; + } + else if( poNewRAT != NULL && poRAT == NULL ) + { + poRAT = poNewRAT; + } + else if( poRAT != NULL ) + { + delete poRAT; + poRAT = NULL; + } + + GDALConsumeErrors(p); + return poRAT; +} + +/************************************************************************/ +/* SetDefaultRAT() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::SetDefaultRAT( const GDALRasterAttributeTable * poRAT ) +{ + if( !SupportsInstr(INSTR_Band_SetDefaultRAT) ) + return GDALPamRasterBand::SetDefaultRAT(poRAT); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_SetDefaultRAT) || + !GDALPipeWrite(p, poRAT) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* AdviseRead() */ +/************************************************************************/ + +CPLErr GDALClientRasterBand::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ) +{ + if( !SupportsInstr(INSTR_Band_AdviseRead) ) + return GDALPamRasterBand::AdviseRead(nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + eDT, papszOptions); + + CLIENT_ENTER(); + if( !WriteInstr(INSTR_Band_AdviseRead) || + !GDALPipeWrite(p, nXOff) || + !GDALPipeWrite(p, nYOff) || + !GDALPipeWrite(p, nXSize) || + !GDALPipeWrite(p, nYSize) || + !GDALPipeWrite(p, nBufXSize) || + !GDALPipeWrite(p, nBufYSize) || + !GDALPipeWrite(p, eDT) || + !GDALPipeWrite(p, papszOptions) ) + return CE_Failure; + return CPLErrOnlyRet(p); +} + +/************************************************************************/ +/* CreateAndConnect() */ +/************************************************************************/ + +GDALClientDataset* GDALClientDataset::CreateAndConnect() +{ + GDALServerSpawnedProcess* ssp = GDALServerSpawnAsync(); + if( ssp == NULL ) + return NULL; + return new GDALClientDataset(ssp); +} + +/************************************************************************/ +/* Init() */ +/************************************************************************/ + +int GDALClientDataset::Init(const char* pszFilename, GDALAccess eAccess) +{ + // FIXME find a way of transmitting the relevant config options to the forked Open() ? + GDALPipeWriteConfigOption(p, "GTIFF_POINT_GEO_IGNORE", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_OVR_BLOCKSIZE", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", bRecycleChild); + GDALPipeWriteConfigOption(p, "GTIFF_LINEAR_UNITS", bRecycleChild); + GDALPipeWriteConfigOption(p, "GTIFF_IGNORE_READ_ERRORS", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_RENDERING_OPTIONS", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_DPI", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_LIB", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_LAYERS", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_LAYERS_OFF", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_JPEG_TO_RGB", bRecycleChild); + GDALPipeWriteConfigOption(p, "RPFTOC_FORCE_RGBA", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_NETCDF_BOTTOMUP", bRecycleChild); + GDALPipeWriteConfigOption(p, "OGR_SQLITE_SYNCHRONOUS", bRecycleChild); + + char* pszCWD = CPLGetCurrentDir(); + + if( !GDALPipeWrite(p, INSTR_Open) || + !GDALPipeWrite(p, eAccess) || + !GDALPipeWrite(p, pszFilename) || + !GDALPipeWrite(p, pszCWD)) + { + CPLFree(pszCWD); + return FALSE; + } + CPLFree(pszCWD); + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return FALSE; + int bRet = FALSE; + if( !GDALPipeRead(p, &bRet) ) + return FALSE; + + if( bRet == FALSE ) + { + GDALConsumeErrors(p); + return FALSE; + } + + if( !GDALPipeRead(p, sizeof(abyCaps), abyCaps) ) + return FALSE; + + this->eAccess = eAccess; + + char* pszDescription = NULL; + if( !GDALPipeRead(p, &pszDescription) ) + return FALSE; + if( pszDescription != NULL ) + SetDescription(pszDescription); + CPLFree(pszDescription); + + char* pszDriverName = NULL; + if( !GDALPipeRead(p, &pszDriverName) ) + return FALSE; + + if( pszDriverName != NULL ) + { + bFreeDriver = TRUE; + poDriver = new GDALDriver(); + poDriver->SetDescription(pszDriverName); + CPLFree(pszDriverName); + pszDriverName = NULL; + + while(TRUE) + { + char* pszKey = NULL, *pszVal = NULL; + if( !GDALPipeRead(p, &pszKey) ) + return FALSE; + if( pszKey == NULL ) + break; + if( !GDALPipeRead(p, &pszVal) ) + { + CPLFree(pszKey); + CPLFree(pszVal); + return FALSE; + } + poDriver->SetMetadataItem( pszKey, pszVal ); + CPLFree(pszKey); + CPLFree(pszVal); + } + } + CPLFree(pszDriverName); + + int bAllSame; + if( !GDALPipeRead(p, &nRasterXSize) || + !GDALPipeRead(p, &nRasterYSize) || + !GDALPipeRead(p, &nBands) || + !GDALPipeRead(p, &bAllSame) ) + return FALSE; + + for(int i=0;i 0 && bAllSame ) + { + GDALClientRasterBand* poFirstBand = (GDALClientRasterBand*) GetRasterBand(1); + int nBlockXSize, nBlockYSize; + poFirstBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + poBand = new GDALClientRasterBand(p, poFirstBand->GetSrvBand() + i, + this, i + 1, poFirstBand->GetAccess(), + poFirstBand->GetXSize(), + poFirstBand->GetYSize(), + poFirstBand->GetRasterDataType(), + nBlockXSize, nBlockYSize, + abyCaps); + } + else + { + if( !GDALPipeRead(p, this, &poBand, abyCaps) ) + return FALSE; + if( poBand == NULL ) + return FALSE; + } + + SetBand(i+1, poBand); + } + + GDALConsumeErrors(p); + + return TRUE; +} + +/************************************************************************/ +/* GDALClientDatasetGetFilename() */ +/************************************************************************/ + +static int IsSeparateExecutable() +{ +#ifdef WIN32 + return TRUE; +#else + const char* pszSpawnServer = CPLGetConfigOption("GDAL_API_PROXY_SERVER", "NO"); + if( EQUAL(pszSpawnServer, "NO") || EQUAL(pszSpawnServer, "OFF") || + EQUAL(pszSpawnServer, "FALSE") || EQUAL(pszSpawnServer, "0") ) + return FALSE; + else + return TRUE; +#endif +} + +const char* GDALClientDatasetGetFilename(const char* pszFilename) +{ + const char* pszSpawn; + if( EQUALN(pszFilename, "API_PROXY:", strlen("API_PROXY:")) ) + { + pszFilename += strlen("API_PROXY:"); + pszSpawn = "YES"; + } + else + { + pszSpawn = CPLGetConfigOption("GDAL_API_PROXY", "NO"); + if( EQUAL(pszSpawn, "NO") || EQUAL(pszSpawn, "OFF") || + EQUAL(pszSpawn, "FALSE") || EQUAL(pszSpawn, "0") ) + { + return NULL; + } + } + + /* Those datasets cannot work in a multi-process context */ + /* /vsistdin/ and /vsistdout/ can work on Unix in the fork() only context (i.e. GDAL_API_PROXY_SERVER undefined) */ + /* since the forked process will inherit the same descriptors as the parent */ + + if( EQUALN(pszFilename, "MEM:::", 6) || + strstr(pszFilename, "/vsimem/") != NULL || + strstr(pszFilename, "/vsimem\\") != NULL || + (strstr(pszFilename, "/vsistdout/") != NULL && IsSeparateExecutable()) || + (strstr(pszFilename, "/vsistdin/") != NULL && IsSeparateExecutable()) || + EQUALN(pszFilename,"NUMPY:::",8) ) + return NULL; + + if( !(EQUAL(pszSpawn, "YES") || EQUAL(pszSpawn, "ON") || + EQUAL(pszSpawn, "TRUE") || EQUAL(pszSpawn, "1")) ) + { + CPLString osExt(CPLGetExtension(pszFilename)); + + /* If the file extension is listed in the GDAL_API_PROXY, then */ + /* we have a match */ + char** papszTokens = CSLTokenizeString2( pszSpawn, " ,", CSLT_HONOURSTRINGS ); + if( CSLFindString(papszTokens, osExt) >= 0 ) + { + CSLDestroy(papszTokens); + return pszFilename; + } + + /* Otherwise let's suppose that driver names are listed in GDAL_API_PROXY */ + /* and check if the file extension matches the extension declared by the */ + /* driver */ + char** papszIter = papszTokens; + while( *papszIter != NULL ) + { + GDALDriverH hDriver = GDALGetDriverByName(*papszIter); + if( hDriver != NULL ) + { + const char* pszDriverExt = + GDALGetMetadataItem(hDriver, GDAL_DMD_EXTENSION, NULL); + if( pszDriverExt != NULL && EQUAL(pszDriverExt, osExt) ) + { + CSLDestroy(papszTokens); + return pszFilename; + } + } + papszIter++; + } + CSLDestroy(papszTokens); + return NULL; + } + + return pszFilename; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *GDALClientDataset::Open( GDALOpenInfo * poOpenInfo ) +{ + const char* pszFilename = + GDALClientDatasetGetFilename(poOpenInfo->pszFilename); + if( pszFilename == NULL ) + return NULL; + + CLIENT_ENTER(); + + GDALClientDataset* poDS = CreateAndConnect(); + if( poDS == NULL ) + return NULL; + + CPLErrorReset(); + if( !poDS->Init(pszFilename, poOpenInfo->eAccess) ) + { + if( CPLGetLastErrorType() == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Could not open %s", + pszFilename); + } + delete poDS; + return NULL; + } + if( poDS != NULL ) + CPLErrorReset(); + + return poDS; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int GDALClientDataset::Identify( GDALOpenInfo * poOpenInfo ) +{ + const char* pszFilename = + GDALClientDatasetGetFilename(poOpenInfo->pszFilename); + if( pszFilename == NULL ) + return FALSE; + + CLIENT_ENTER(); + + GDALServerSpawnedProcess* ssp = GDALServerSpawnAsync(); + if( ssp == NULL ) + return FALSE; + + char* pszCWD = CPLGetCurrentDir(); + + GDALPipe* p = ssp->p; + if( !GDALPipeWrite(p, INSTR_Identify) || + !GDALPipeWrite(p, pszFilename) || + !GDALPipeWrite(p, pszCWD) || + !GDALSkipUntilEndOfJunkMarker(p) ) + { + GDALServerSpawnAsyncFinish(ssp); + CPLFree(pszCWD); + return FALSE; + } + + CPLFree(pszCWD); + + int bRet; + if( !GDALPipeRead(p, &bRet) ) + { + GDALServerSpawnAsyncFinish(ssp); + return FALSE; + } + + GDALServerSpawnAsyncFinish(ssp); + return bRet; +} + +/************************************************************************/ +/* GDALClientDatasetQuietDelete() */ +/************************************************************************/ + +static int GDALClientDatasetQuietDelete(GDALPipe* p, + const char* pszFilename) +{ + char* pszCWD = CPLGetCurrentDir(); + if( !GDALPipeWrite(p, INSTR_QuietDelete) || + !GDALPipeWrite(p, pszFilename) || + !GDALPipeWrite(p, pszCWD) || + !GDALSkipUntilEndOfJunkMarker(p) ) + { + CPLFree(pszCWD); + return FALSE; + } + CPLFree(pszCWD); + GDALConsumeErrors(p); + return TRUE; +} + +/************************************************************************/ +/* mCreateCopy() */ +/************************************************************************/ + +int GDALClientDataset::mCreateCopy( const char* pszFilename, + GDALDataset* poSrcDS, + int bStrict, char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + /*if( !SupportsInstr(INSTR_CreateCopy) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "CreateCopy() not supported by server"); + return FALSE; + }*/ + + const char* pszServerDriver = + CSLFetchNameValue(papszOptions, "SERVER_DRIVER"); + if( pszServerDriver == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Creation options should contain a SERVER_DRIVER item"); + return FALSE; + } + + if( !CSLFetchBoolean(papszOptions, "APPEND_SUBDATASET", FALSE) ) + { + if( !GDALClientDatasetQuietDelete(p, pszFilename) ) + return FALSE; + } + + GDALPipeWriteConfigOption(p, "GTIFF_POINT_GEO_IGNORE", bRecycleChild); + GDALPipeWriteConfigOption(p, "GTIFF_DELETE_ON_ERROR", bRecycleChild); + GDALPipeWriteConfigOption(p, "ESRI_XML_PAM", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_TIFF_INTERNAL_MASK_TO_8BIT", bRecycleChild); + GDALPipeWriteConfigOption(p, "OGR_SQLITE_SYNCHRONOUS", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_WRITE_GEOREF_ON_IMAGE", bRecycleChild); + GDALPipeWriteConfigOption(p, "GDAL_PDF_OGC_BP_WRITE_WKT", bRecycleChild); + + char* pszCWD = CPLGetCurrentDir(); + + if( !GDALPipeWrite(p, INSTR_CreateCopy) || + !GDALPipeWrite(p, pszFilename) || + !GDALPipeWrite(p, poSrcDS->GetDescription()) || + !GDALPipeWrite(p, pszCWD) || + !GDALPipeWrite(p, bStrict) || + !GDALPipeWrite(p, papszOptions) ) + { + CPLFree(pszCWD); + return FALSE; + } + CPLFree(pszCWD); + + int bDriverOK; + if( !GDALPipeRead(p, &bDriverOK) ) + return FALSE; + + if( !bDriverOK ) + { + GDALConsumeErrors(p); + return FALSE; + } + + if( GDALServerLoop(p, + poSrcDS, + pfnProgress, pProgressData) != 0 ) + { + GDALConsumeErrors(p); + return FALSE; + } + + GDALConsumeErrors(p); + + return Init(NULL, GA_Update); +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +GDALDataset *GDALClientDataset::CreateCopy( const char * pszFilename, + GDALDataset * poSrcDS, int bStrict, + char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + CLIENT_ENTER(); + + GDALClientDataset* poDS = CreateAndConnect(); + if( poDS !=NULL && !poDS->mCreateCopy(pszFilename, poSrcDS, bStrict, + papszOptions, + pfnProgress, pProgressData) ) + { + delete poDS; + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* mCreate() */ +/************************************************************************/ + +int GDALClientDataset::mCreate( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ) +{ + /*if( !SupportsInstr(INSTR_Create) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Create() not supported by server"); + return FALSE; + }*/ + + const char* pszServerDriver = + CSLFetchNameValue(papszOptions, "SERVER_DRIVER"); + if( pszServerDriver == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Creation options should contain a SERVER_DRIVER item"); + return FALSE; + } + + if( !CSLFetchBoolean(papszOptions, "APPEND_SUBDATASET", FALSE) ) + { + if( !GDALClientDatasetQuietDelete(p, pszFilename) ) + return FALSE; + } + + GDALPipeWriteConfigOption(p,"GTIFF_POINT_GEO_IGNORE", bRecycleChild); + GDALPipeWriteConfigOption(p,"GTIFF_DELETE_ON_ERROR", bRecycleChild); + GDALPipeWriteConfigOption(p,"ESRI_XML_PAM", bRecycleChild); + GDALPipeWriteConfigOption(p,"GTIFF_DONT_WRITE_BLOCKS", bRecycleChild); + + char* pszCWD = CPLGetCurrentDir(); + + if( !GDALPipeWrite(p, INSTR_Create) || + !GDALPipeWrite(p, pszFilename) || + !GDALPipeWrite(p, pszCWD) || + !GDALPipeWrite(p, nXSize) || + !GDALPipeWrite(p, nYSize) || + !GDALPipeWrite(p, nBands) || + !GDALPipeWrite(p, eType) || + !GDALPipeWrite(p, papszOptions) ) + { + CPLFree(pszCWD); + return FALSE; + } + CPLFree(pszCWD); + if( !GDALSkipUntilEndOfJunkMarker(p) ) + return FALSE; + int bOK; + if( !GDALPipeRead(p, &bOK) ) + return FALSE; + + if( !bOK ) + { + GDALConsumeErrors(p); + return FALSE; + } + + GDALConsumeErrors(p); + + return Init(NULL, GA_Update); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset* GDALClientDataset::Create( const char * pszName, + int nXSize, int nYSize, int nBands, + GDALDataType eType, + char ** papszOptions ) +{ + CLIENT_ENTER(); + + GDALClientDataset* poDS = CreateAndConnect(); + if( poDS != NULL && !poDS->mCreate(pszName, nXSize, nYSize, nBands, + eType, papszOptions) ) + { + delete poDS; + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +CPLErr GDALClientDataset::Delete( const char * pszFilename ) +{ + pszFilename = + GDALClientDatasetGetFilename(pszFilename); + if( pszFilename == NULL ) + return CE_Failure; + + CLIENT_ENTER(); + + GDALServerSpawnedProcess* ssp = GDALServerSpawnAsync(); + if( ssp == NULL ) + return CE_Failure; + + GDALPipe* p = ssp->p; + if( !GDALClientDatasetQuietDelete(p, pszFilename) ) + { + GDALServerSpawnAsyncFinish(ssp); + return CE_Failure; + } + + GDALServerSpawnAsyncFinish(ssp); + return CE_None; +} + +/************************************************************************/ +/* GDALUnloadAPIPROXYDriver() */ +/************************************************************************/ +static GDALDriver* poAPIPROXYDriver = NULL; + +static void GDALUnloadAPIPROXYDriver(CPL_UNUSED GDALDriver* poDriver) +{ + if( bRecycleChild ) + { + /* Kill all unused descriptors */ + bRecycleChild = FALSE; + for(int i=0;i 0 ) + { + bRecycleChild = TRUE; + nMaxRecycled = MIN(atoi(pszConnPool), MAX_RECYCLED); + } + else if( CSLTestBoolean(pszConnPool) ) + { + bRecycleChild = TRUE; + nMaxRecycled = DEFAULT_RECYCLED; + } + memset(aspRecycled, 0, sizeof(aspRecycled)); + + poAPIPROXYDriver = new GDALDriver(); + + poAPIPROXYDriver->SetDescription( "API_PROXY" ); + poAPIPROXYDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poAPIPROXYDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "API_PROXY" ); + + poAPIPROXYDriver->pfnOpen = GDALClientDataset::Open; + poAPIPROXYDriver->pfnIdentify = GDALClientDataset::Identify; + poAPIPROXYDriver->pfnCreateCopy = GDALClientDataset::CreateCopy; + poAPIPROXYDriver->pfnCreate = GDALClientDataset::Create; + poAPIPROXYDriver->pfnDelete = GDALClientDataset::Delete; + poAPIPROXYDriver->pfnUnloadDriver = GDALUnloadAPIPROXYDriver; + } + return poAPIPROXYDriver; +} diff --git a/bazaar/plugin/gdal/gcore/gdalcolortable.cpp b/bazaar/plugin/gdal/gcore/gdalcolortable.cpp new file mode 100644 index 000000000..2adaad84f --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalcolortable.cpp @@ -0,0 +1,481 @@ +/****************************************************************************** + * $Id: gdalcolortable.cpp 28082 2014-12-05 18:06:30Z rouault $ + * + * Project: GDAL Core + * Purpose: Color table implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +CPL_CVSID("$Id: gdalcolortable.cpp 28082 2014-12-05 18:06:30Z rouault $"); + +/************************************************************************/ +/* GDALColorTable() */ +/************************************************************************/ + +/** + * \brief Construct a new color table. + * + * This constructor is the same as the C GDALCreateColorTable() function. + * + * @param eInterpIn the interpretation to be applied to GDALColorEntry + * values. + */ + +GDALColorTable::GDALColorTable( GDALPaletteInterp eInterpIn ) + +{ + eInterp = eInterpIn; +} + +/************************************************************************/ +/* GDALCreateColorTable() */ +/************************************************************************/ + +/** + * \brief Construct a new color table. + * + * This function is the same as the C++ method GDALColorTable::GDALColorTable() + */ +GDALColorTableH CPL_STDCALL GDALCreateColorTable( GDALPaletteInterp eInterp ) + +{ + return (GDALColorTableH) (new GDALColorTable( eInterp )); +} + + +/************************************************************************/ +/* ~GDALColorTable() */ +/************************************************************************/ + +/** + * \brief Destructor. + * + * This descructor is the same as the C GDALDestroyColorTable() function. + */ + +GDALColorTable::~GDALColorTable() + +{ +} + +/************************************************************************/ +/* GDALDestroyColorTable() */ +/************************************************************************/ + +/** + * \brief Destroys a color table. + * + * This function is the same as the C++ method GDALColorTable::~GDALColorTable() + */ +void CPL_STDCALL GDALDestroyColorTable( GDALColorTableH hTable ) + +{ + delete (GDALColorTable *) hTable; +} + +/************************************************************************/ +/* GetColorEntry() */ +/************************************************************************/ + +/** + * \brief Fetch a color entry from table. + * + * This method is the same as the C function GDALGetColorEntry(). + * + * @param i entry offset from zero to GetColorEntryCount()-1. + * + * @return pointer to internal color entry, or NULL if index is out of range. + */ + +const GDALColorEntry *GDALColorTable::GetColorEntry( int i ) const + +{ + if( i < 0 || i >= static_cast(aoEntries.size()) ) + return NULL; + else + return &aoEntries[i]; +} + +/************************************************************************/ +/* GDALGetColorEntry() */ +/************************************************************************/ + + +/** + * \brief Fetch a color entry from table. + * + * This function is the same as the C++ method GDALColorTable::GetColorEntry() + */ +const GDALColorEntry * CPL_STDCALL +GDALGetColorEntry( GDALColorTableH hTable, int i ) + +{ + VALIDATE_POINTER1( hTable, "GDALGetColorEntry", NULL ); + + return ((GDALColorTable *) hTable)->GetColorEntry( i ); +} + + +/************************************************************************/ +/* GetColorEntryAsRGB() */ +/************************************************************************/ + +/** + * \brief Fetch a table entry in RGB format. + * + * In theory this method should support translation of color palettes in + * non-RGB color spaces into RGB on the fly, but currently it only works + * on RGB color tables. + * + * This method is the same as the C function GDALGetColorEntryAsRGB(). + * + * @param i entry offset from zero to GetColorEntryCount()-1. + * + * @param poEntry the existing GDALColorEntry to be overrwritten with the RGB + * values. + * + * @return TRUE on success, or FALSE if the conversion isn't supported. + */ + +int GDALColorTable::GetColorEntryAsRGB( int i, GDALColorEntry *poEntry ) const + +{ + if( eInterp != GPI_RGB || i < 0 || i >= static_cast(aoEntries.size()) ) + return FALSE; + + *poEntry = aoEntries[i]; + return TRUE; +} + +/************************************************************************/ +/* GDALGetColorEntryAsRGB() */ +/************************************************************************/ + +/** + * \brief Fetch a table entry in RGB format. + * + * This function is the same as the C++ method GDALColorTable::GetColorEntryAsRGB() + */ +int CPL_STDCALL GDALGetColorEntryAsRGB( GDALColorTableH hTable, int i, + GDALColorEntry *poEntry ) + +{ + VALIDATE_POINTER1( hTable, "GDALGetColorEntryAsRGB", 0 ); + VALIDATE_POINTER1( poEntry, "GDALGetColorEntryAsRGB", 0 ); + + return ((GDALColorTable *) hTable)->GetColorEntryAsRGB( i, poEntry ); +} + +/************************************************************************/ +/* SetColorEntry() */ +/************************************************************************/ + +/** + * \brief Set entry in color table. + * + * Note that the passed in color entry is copied, and no internal reference + * to it is maintained. Also, the passed in entry must match the color + * interpretation of the table to which it is being assigned. + * + * The table is grown as needed to hold the supplied offset. + * + * This function is the same as the C function GDALSetColorEntry(). + * + * @param i entry offset from zero to GetColorEntryCount()-1. + * @param poEntry value to assign to table. + */ + +void GDALColorTable::SetColorEntry( int i, const GDALColorEntry * poEntry ) + +{ + if( i < 0 ) + return; + + try + { + if( i >= static_cast(aoEntries.size()) ) + { + GDALColorEntry oBlack; + oBlack.c1 = oBlack.c2 = oBlack.c3 = oBlack.c4 = 0; + aoEntries.resize(i+1, oBlack); + } + + aoEntries[i] = *poEntry; + } + catch(std::exception &e) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what()); + } +} + +/************************************************************************/ +/* GDALSetColorEntry() */ +/************************************************************************/ + +/** + * \brief Set entry in color table. + * + * This function is the same as the C++ method GDALColorTable::SetColorEntry() + */ +void CPL_STDCALL GDALSetColorEntry( GDALColorTableH hTable, int i, + const GDALColorEntry * poEntry ) + +{ + VALIDATE_POINTER0( hTable, "GDALSetColorEntry" ); + VALIDATE_POINTER0( poEntry, "GDALSetColorEntry" ); + + ((GDALColorTable *) hTable)->SetColorEntry( i, poEntry ); +} + + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +/** + * \brief Make a copy of a color table. + * + * This method is the same as the C function GDALCloneColorTable(). + */ + +GDALColorTable *GDALColorTable::Clone() const + +{ + return new GDALColorTable(*this); +} + +/************************************************************************/ +/* GDALCloneColorTable() */ +/************************************************************************/ + +/** + * \brief Make a copy of a color table. + * + * This function is the same as the C++ method GDALColorTable::Clone() + */ +GDALColorTableH CPL_STDCALL GDALCloneColorTable( GDALColorTableH hTable ) + +{ + VALIDATE_POINTER1( hTable, "GDALCloneColorTable", NULL ); + + return (GDALColorTableH) ((GDALColorTable *) hTable)->Clone(); +} + +/************************************************************************/ +/* GetColorEntryCount() */ +/************************************************************************/ + +/** + * \brief Get number of color entries in table. + * + * This method is the same as the function GDALGetColorEntryCount(). + * + * @return the number of color entries. + */ + +int GDALColorTable::GetColorEntryCount() const + +{ + return aoEntries.size(); +} + +/************************************************************************/ +/* GDALGetColorEntryCount() */ +/************************************************************************/ + +/** + * \brief Get number of color entries in table. + * + * This function is the same as the C++ method GDALColorTable::GetColorEntryCount() + */ +int CPL_STDCALL GDALGetColorEntryCount( GDALColorTableH hTable ) + +{ + VALIDATE_POINTER1( hTable, "GDALGetColorEntryCount", 0 ); + + return ((GDALColorTable *) hTable)->GetColorEntryCount(); +} + +/************************************************************************/ +/* GetPaletteInterpretation() */ +/************************************************************************/ + +/** + * \brief Fetch palette interpretation. + * + * The returned value is used to interprete the values in the GDALColorEntry. + * + * This method is the same as the C function GDALGetPaletteInterpretation(). + * + * @return palette interpretation enumeration value, usually GPI_RGB. + */ + +GDALPaletteInterp GDALColorTable::GetPaletteInterpretation() const + +{ + return eInterp; +} + +/************************************************************************/ +/* GDALGetPaltteInterpretation() */ +/************************************************************************/ + +/** + * \brief Fetch palette interpretation. + * + * This function is the same as the C++ method GDALColorTable::GetPaletteInterpretation() + */ +GDALPaletteInterp CPL_STDCALL +GDALGetPaletteInterpretation( GDALColorTableH hTable ) + +{ + VALIDATE_POINTER1( hTable, "GDALGetPaletteInterpretation", GPI_Gray ); + + return ((GDALColorTable *) hTable)->GetPaletteInterpretation(); +} + +/** + * \brief Create color ramp + * + * Automatically creates a color ramp from one color entry to + * another. It can be called several times to create multiples ramps + * in the same color table. + * + * This function is the same as the C function GDALCreateColorRamp(). + * + * @param nStartIndex index to start the ramp on the color table [0..255] + * @param psStartColor a color entry value to start the ramp + * @param nEndIndex index to end the ramp on the color table [0..255] + * @param psEndColor a color entry value to end the ramp + * @return total number of entries, -1 to report error + */ + +int GDALColorTable::CreateColorRamp( + int nStartIndex, const GDALColorEntry *psStartColor, + int nEndIndex, const GDALColorEntry *psEndColor ) +{ + /* validate indexes */ + + if( nStartIndex < 0 || nStartIndex > 255 || + nEndIndex < 0 || nEndIndex > 255 || + nStartIndex > nEndIndex ) + { + return -1; + } + + /* validate color entries */ + + if( psStartColor == NULL || psEndColor == NULL ) + { + return -1; + } + + /* calculate number of colors in-between */ + + int nColors = nEndIndex - nStartIndex; + + /* set starting color */ + + SetColorEntry( nStartIndex, psStartColor ); + + if( nColors == 0 ) + { + return GetColorEntryCount(); /* it should not proceed */ + } + + /* set ending color */ + + SetColorEntry( nEndIndex, psEndColor ); + + /* calculate the slope of the linear transformation */ + + double dfSlope1, dfSlope2, dfSlope3, dfSlope4; + + dfSlope1 = ( psEndColor->c1 - psStartColor->c1 ) / (double) nColors; + dfSlope2 = ( psEndColor->c2 - psStartColor->c2 ) / (double) nColors; + dfSlope3 = ( psEndColor->c3 - psStartColor->c3 ) / (double) nColors; + dfSlope4 = ( psEndColor->c4 - psStartColor->c4 ) / (double) nColors; + + /* loop through the new colors */ + + GDALColorEntry sColor = *psStartColor; + + int i; + + for( i = 1; i < nColors; i++ ) + { + sColor.c1 = (short) ( i * dfSlope1 + (double) psStartColor->c1 ); + sColor.c2 = (short) ( i * dfSlope2 + (double) psStartColor->c2 ); + sColor.c3 = (short) ( i * dfSlope3 + (double) psStartColor->c3 ); + sColor.c4 = (short) ( i * dfSlope4 + (double) psStartColor->c4 ); + + SetColorEntry( nStartIndex + i, &sColor ); + } + + /* return the total number of colors */ + + return GetColorEntryCount(); +} + +/************************************************************************/ +/* GDALCreateColorRamp() */ +/************************************************************************/ + +/** + * \brief Create color ramp + * + * This function is the same as the C++ method GDALColorTable::CreateColorRamp() + */ +void CPL_STDCALL +GDALCreateColorRamp( GDALColorTableH hTable, + int nStartIndex, const GDALColorEntry *psStartColor, + int nEndIndex, const GDALColorEntry *psEndColor ) +{ + VALIDATE_POINTER0( hTable, "GDALCreateColorRamp" ); + + ((GDALColorTable *) hTable)->CreateColorRamp( nStartIndex, psStartColor, + nEndIndex, psEndColor ); +} + +/************************************************************************/ +/* IsSame() */ +/************************************************************************/ + +/** + * \brief Returns if the current color table is the same as another one. + * + * @param poOtherCT other color table to be compared to. + * @return TRUE if both color tables are identical. + * @since GDAL 2.0 + */ + +int GDALColorTable::IsSame(const GDALColorTable* poOtherCT) const +{ + return aoEntries.size() == poOtherCT->aoEntries.size() && + (aoEntries.size() == 0 || + memcmp(&aoEntries[0], &poOtherCT->aoEntries[0], aoEntries.size() * sizeof(GDALColorEntry)) == 0); +} diff --git a/bazaar/plugin/gdal/gcore/gdaldataset.cpp b/bazaar/plugin/gdal/gcore/gdaldataset.cpp new file mode 100644 index 000000000..744629ea7 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaldataset.cpp @@ -0,0 +1,5760 @@ +/****************************************************************************** + * $Id: gdaldataset.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: GDAL Core + * Purpose: Base class for raster file formats. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, 2003, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_string.h" +#include "cpl_hash_set.h" +#include "cpl_multiproc.h" +#include "ogr_featurestyle.h" +#include "swq.h" +#include "ogr_gensql.h" +#include "ogr_attrind.h" +#include "ogr_p.h" +#include "ogrunionlayer.h" +#include "ograpispy.h" + +#ifdef SQLITE_ENABLED +#include "../sqlite/ogrsqliteexecutesql.h" +#endif + +#include + +CPL_CVSID("$Id: gdaldataset.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +CPL_C_START +GDALAsyncReader * +GDALGetDefaultAsyncReader( GDALDataset *poDS, + int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, char **papszOptions); +CPL_C_END + +typedef struct +{ + /* PID of the thread that mark the dataset as shared */ + /* This may not be the actual PID, but the responsiblePID */ + GIntBig nPID; + char *pszDescription; + GDALAccess eAccess; + + GDALDataset *poDS; +} SharedDatasetCtxt; + +/* Set of datasets opened as shared datasets (with GDALOpenShared) */ +/* The values in the set are of type SharedDatasetCtxt */ +static CPLHashSet* phSharedDatasetSet = NULL; + +/* Set of all datasets created in the constructor of GDALDataset */ +/* In the case of a shared dataset, memorize the PID of the thread */ +/* that marked the dataset as shared, so that we can remove it from */ +/* the phSharedDatasetSet in the destructor of the dataset, even */ +/* if GDALClose is called from a different thread */ +static std::map* poAllDatasetMap = NULL; + +static CPLMutex *hDLMutex = NULL; + +/* Static array of all datasets. Used by GDALGetOpenDatasets */ +/* Not thread-safe. See GDALGetOpenDatasets */ +static GDALDataset** ppDatasets = NULL; + +static unsigned long GDALSharedDatasetHashFunc(const void* elt) +{ + SharedDatasetCtxt* psStruct = (SharedDatasetCtxt*) elt; + return (unsigned long) (CPLHashSetHashStr(psStruct->pszDescription) ^ psStruct->eAccess ^ psStruct->nPID); +} + +static int GDALSharedDatasetEqualFunc(const void* elt1, const void* elt2) +{ + SharedDatasetCtxt* psStruct1 = (SharedDatasetCtxt*) elt1; + SharedDatasetCtxt* psStruct2 = (SharedDatasetCtxt*) elt2; + return strcmp(psStruct1->pszDescription, psStruct2->pszDescription) == 0 && + psStruct1->nPID == psStruct2->nPID && + psStruct1->eAccess == psStruct2->eAccess; +} + +static void GDALSharedDatasetFreeFunc(void* elt) +{ + SharedDatasetCtxt* psStruct = (SharedDatasetCtxt*) elt; + CPLFree(psStruct->pszDescription); + CPLFree(psStruct); +} + +/************************************************************************/ +/* Functions shared between gdalproxypool.cpp and gdaldataset.cpp */ +/************************************************************************/ + +/* The open-shared mutex must be used by the ProxyPool too */ +CPLMutex** GDALGetphDLMutex() +{ + return &hDLMutex; +} + +/* The current thread will act in the behalf of the thread of PID responsiblePID */ +void GDALSetResponsiblePIDForCurrentThread(GIntBig responsiblePID) +{ + GIntBig* pResponsiblePID = (GIntBig*) CPLGetTLS(CTLS_RESPONSIBLEPID); + if (pResponsiblePID == NULL) + { + pResponsiblePID = (GIntBig*) CPLMalloc(sizeof(GIntBig)); + CPLSetTLS(CTLS_RESPONSIBLEPID, pResponsiblePID, TRUE); + } + *pResponsiblePID = responsiblePID; +} + +/* Get the PID of the thread that the current thread will act in the behalf of */ +/* By default : the current thread acts in the behalf of itself */ +GIntBig GDALGetResponsiblePIDForCurrentThread() +{ + GIntBig* pResponsiblePID = (GIntBig*) CPLGetTLS(CTLS_RESPONSIBLEPID); + if (pResponsiblePID == NULL) + return CPLGetPID(); + return *pResponsiblePID; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* GDALDataset */ +/* ==================================================================== */ +/************************************************************************/ + +/** + * \class GDALDataset "gdal_priv.h" + * + * A dataset encapsulating one or more raster bands. Details are + * further discussed in the GDAL + * Data Model. + * + * Use GDALOpen() or GDALOpenShared() to create a GDALDataset for a named file, + * or GDALDriver::Create() or GDALDriver::CreateCopy() to create a new + * dataset. + */ + +/************************************************************************/ +/* GDALDataset() */ +/************************************************************************/ + +GDALDataset::GDALDataset() + +{ + poDriver = NULL; + eAccess = GA_ReadOnly; + nRasterXSize = 512; + nRasterYSize = 512; + nBands = 0; + papoBands = NULL; + nRefCount = 1; + bShared = FALSE; + bIsInternal = TRUE; + bSuppressOnClose = FALSE; + bReserved1 = FALSE; + bReserved2 = FALSE; + papszOpenOptions = NULL; + +/* -------------------------------------------------------------------- */ +/* Set forced caching flag. */ +/* -------------------------------------------------------------------- */ + bForceCachedIO = CSLTestBoolean( + CPLGetConfigOption( "GDAL_FORCE_CACHING", "NO") ); + + m_poStyleTable = NULL; + m_hMutex = NULL; +} + + +/************************************************************************/ +/* ~GDALDataset() */ +/************************************************************************/ + +/** + * \brief Destroy an open GDALDataset. + * + * This is the accepted method of closing a GDAL dataset and deallocating + * all resources associated with it. + * + * Equivalent of the C callable GDALClose(). Except that GDALClose() first + * decrements the reference count, and then closes only if it has dropped to + * zero. + * + * For Windows users, it is not recommended to use the delete operator on the + * dataset object because of known issues when allocating and freeing memory across + * module boundaries. Calling GDALClose() is then a better option. + */ + +GDALDataset::~GDALDataset() + +{ + int i; + + // we don't want to report destruction of datasets that + // were never really open or meant as internal + if( !bIsInternal && ( nBands != 0 || !EQUAL(GetDescription(),"") ) ) + { + if( CPLGetPID() != GDALGetResponsiblePIDForCurrentThread() ) + CPLDebug( "GDAL", + "GDALClose(%s, this=%p) (pid=%d, responsiblePID=%d)", GetDescription(), this, + (int)CPLGetPID(), + (int)GDALGetResponsiblePIDForCurrentThread() ); + else + CPLDebug( "GDAL", + "GDALClose(%s, this=%p)", GetDescription(), this ); + } + + if( bSuppressOnClose ) + VSIUnlink(GetDescription()); + +/* -------------------------------------------------------------------- */ +/* Remove dataset from the "open" dataset list. */ +/* -------------------------------------------------------------------- */ + if( !bIsInternal ) + { + CPLMutexHolderD( &hDLMutex ); + if( poAllDatasetMap ) + { + std::map::iterator oIter = poAllDatasetMap->find(this); + CPLAssert(oIter != poAllDatasetMap->end()); + GIntBig nPIDCreatorForShared = oIter->second; + poAllDatasetMap->erase(oIter); + + if (bShared && phSharedDatasetSet != NULL) + { + SharedDatasetCtxt* psStruct; + SharedDatasetCtxt sStruct; + sStruct.nPID = nPIDCreatorForShared; + sStruct.eAccess = eAccess; + sStruct.pszDescription = (char*) GetDescription(); + psStruct = (SharedDatasetCtxt*) CPLHashSetLookup(phSharedDatasetSet, &sStruct); + if (psStruct && psStruct->poDS == this) + { + CPLHashSetRemove(phSharedDatasetSet, psStruct); + } + else + { + CPLDebug("GDAL", "Should not happen. Cannot find %s, this=%p in phSharedDatasetSet", GetDescription(), this); + } + } + + if (poAllDatasetMap->size() == 0) + { + delete poAllDatasetMap; + poAllDatasetMap = NULL; + if (phSharedDatasetSet) + { + CPLHashSetDestroy(phSharedDatasetSet); + } + phSharedDatasetSet = NULL; + CPLFree(ppDatasets); + ppDatasets = NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Destroy the raster bands if they exist. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBands && papoBands != NULL; i++ ) + { + if( papoBands[i] != NULL ) + delete papoBands[i]; + } + + CPLFree( papoBands ); + + if ( m_poStyleTable ) + { + delete m_poStyleTable; + m_poStyleTable = NULL; + } + + if( m_hMutex != NULL ) + CPLDestroyMutex( m_hMutex ); + + CSLDestroy( papszOpenOptions ); +} + +/************************************************************************/ +/* AddToDatasetOpenList() */ +/************************************************************************/ + +void GDALDataset::AddToDatasetOpenList() +{ +/* -------------------------------------------------------------------- */ +/* Add this dataset to the open dataset list. */ +/* -------------------------------------------------------------------- */ + bIsInternal = FALSE; + + CPLMutexHolderD( &hDLMutex ); + + if (poAllDatasetMap == NULL) + poAllDatasetMap = new std::map; + (*poAllDatasetMap)[this] = -1; +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +/** + * \brief Flush all write cached data to disk. + * + * Any raster (or other GDAL) data written via GDAL calls, but buffered + * internally will be written to disk. + * + * The default implementation of this method just calls the FlushCache() method + * on each of the raster bands and the SyncToDisk() method + * on each of the layers. Conceptionally, calling FlushCache() on a dataset + * should include any work that might be accomplished by calling SyncToDisk() + * on layers in that dataset. + * + * Using this method does not prevent use from calling GDALClose() + * to properly close a dataset and ensure that important data not addressed + * by FlushCache() is written in the file. + * + * This method is the same as the C function GDALFlushCache(). + */ + +void GDALDataset::FlushCache() + +{ + int i; + + // This sometimes happens if a dataset is destroyed before completely + // built. + + if( papoBands != NULL ) + { + for( i = 0; i < nBands; i++ ) + { + if( papoBands[i] != NULL ) + papoBands[i]->FlushCache(); + } + } + + int nLayers = GetLayerCount(); + if( nLayers > 0 ) + { + CPLMutexHolderD( &m_hMutex ); + for( i = 0; i < nLayers ; i++ ) + { + OGRLayer *poLayer = GetLayer(i); + + if( poLayer ) + { + poLayer->SyncToDisk(); + } + } + } +} + +/************************************************************************/ +/* GDALFlushCache() */ +/************************************************************************/ + +/** + * \brief Flush all write cached data to disk. + * + * @see GDALDataset::FlushCache(). + */ + +void CPL_STDCALL GDALFlushCache( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER0( hDS, "GDALFlushCache" ); + + ((GDALDataset *) hDS)->FlushCache(); +} + +/************************************************************************/ +/* BlockBasedFlushCache() */ +/* */ +/* This helper method can be called by the */ +/* GDALDataset::FlushCache() for particular drivers to ensure */ +/* that buffers will be flushed in a manner suitable for pixel */ +/* interleaved (by block) IO. That is, if all the bands have */ +/* the same size blocks then a given block will be flushed for */ +/* all bands before proceeding to the next block. */ +/************************************************************************/ + +void GDALDataset::BlockBasedFlushCache() + +{ + GDALRasterBand *poBand1; + int nBlockXSize, nBlockYSize, iBand; + + poBand1 = GetRasterBand( 1 ); + if( poBand1 == NULL ) + { + GDALDataset::FlushCache(); + return; + } + + poBand1->GetBlockSize( &nBlockXSize, &nBlockYSize ); + +/* -------------------------------------------------------------------- */ +/* Verify that all bands match. */ +/* -------------------------------------------------------------------- */ + for( iBand = 1; iBand < nBands; iBand++ ) + { + int nThisBlockXSize, nThisBlockYSize; + GDALRasterBand *poBand = GetRasterBand( iBand+1 ); + + poBand->GetBlockSize( &nThisBlockXSize, &nThisBlockYSize ); + if( nThisBlockXSize != nBlockXSize && nThisBlockYSize != nBlockYSize ) + { + GDALDataset::FlushCache(); + return; + } + } + +/* -------------------------------------------------------------------- */ +/* Now flush writable data. */ +/* -------------------------------------------------------------------- */ + for( int iY = 0; iY < poBand1->nBlocksPerColumn; iY++ ) + { + for( int iX = 0; iX < poBand1->nBlocksPerRow; iX++ ) + { + for( iBand = 0; iBand < nBands; iBand++ ) + { + GDALRasterBand *poBand = GetRasterBand( iBand+1 ); + + CPLErr eErr; + + eErr = poBand->FlushBlock( iX, iY ); + + if( eErr != CE_None ) + return; + } + } + } +} + +/************************************************************************/ +/* RasterInitialize() */ +/* */ +/* Initialize raster size */ +/************************************************************************/ + +void GDALDataset::RasterInitialize( int nXSize, int nYSize ) + +{ + CPLAssert( nXSize > 0 && nYSize > 0 ); + + nRasterXSize = nXSize; + nRasterYSize = nYSize; +} + +/************************************************************************/ +/* AddBand() */ +/************************************************************************/ + +/** + * \brief Add a band to a dataset. + * + * This method will add a new band to the dataset if the underlying format + * supports this action. Most formats do not. + * + * Note that the new GDALRasterBand is not returned. It may be fetched + * after successful completion of the method by calling + * GDALDataset::GetRasterBand(GDALDataset::GetRasterCount()) as the newest + * band will always be the last band. + * + * @param eType the data type of the pixels in the new band. + * + * @param papszOptions a list of NAME=VALUE option strings. The supported + * options are format specific. NULL may be passed by default. + * + * @return CE_None on success or CE_Failure on failure. + */ + +CPLErr GDALDataset::AddBand( GDALDataType eType, char ** papszOptions ) + +{ + (void) eType; + (void) papszOptions; + + ReportError( CE_Failure, CPLE_NotSupported, + "Dataset does not support the AddBand() method." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALAddBand() */ +/************************************************************************/ + +/** + * \brief Add a band to a dataset. + * + * @see GDALDataset::AddBand(). + */ + +CPLErr CPL_STDCALL GDALAddBand( GDALDatasetH hDataset, + GDALDataType eType, char **papszOptions ) + +{ + VALIDATE_POINTER1( hDataset, "GDALAddBand", CE_Failure ); + + return ((GDALDataset *) hDataset)->AddBand( eType, papszOptions ); +} + +/************************************************************************/ +/* SetBand() */ +/* */ +/* Set a band in the band array, updating the band count, and */ +/* array size appropriately. */ +/************************************************************************/ + +void GDALDataset::SetBand( int nNewBand, GDALRasterBand * poBand ) + +{ +/* -------------------------------------------------------------------- */ +/* Do we need to grow the bands list? */ +/* -------------------------------------------------------------------- */ + if( nBands < nNewBand || papoBands == NULL ) { + int i; + GDALRasterBand** papoNewBands; + + if( papoBands == NULL ) + papoNewBands = (GDALRasterBand **) + VSICalloc(sizeof(GDALRasterBand*), MAX(nNewBand,nBands)); + else + papoNewBands = (GDALRasterBand **) + VSIRealloc(papoBands, sizeof(GDALRasterBand*) * + MAX(nNewBand,nBands)); + if (papoNewBands == NULL) + { + ReportError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate band array"); + return; + } + papoBands = papoNewBands; + + for( i = nBands; i < nNewBand; i++ ) + papoBands[i] = NULL; + + nBands = MAX(nBands,nNewBand); + } + +/* -------------------------------------------------------------------- */ +/* Set the band. Resetting the band is currently not permitted. */ +/* -------------------------------------------------------------------- */ + if( papoBands[nNewBand-1] != NULL ) + { + ReportError(CE_Failure, CPLE_NotSupported, + "Cannot set band %d as it is already set", nNewBand); + return; + } + + papoBands[nNewBand-1] = poBand; + +/* -------------------------------------------------------------------- */ +/* Set back reference information on the raster band. Note */ +/* that the GDALDataset is a friend of the GDALRasterBand */ +/* specifically to allow this. */ +/* -------------------------------------------------------------------- */ + poBand->nBand = nNewBand; + poBand->poDS = this; + poBand->nRasterXSize = nRasterXSize; + poBand->nRasterYSize = nRasterYSize; + poBand->eAccess = eAccess; /* default access to be same as dataset */ +} + +/************************************************************************/ +/* GetRasterXSize() */ +/************************************************************************/ + +/** + + \brief Fetch raster width in pixels. + + Equivalent of the C function GDALGetRasterXSize(). + + @return the width in pixels of raster bands in this GDALDataset. + +*/ + +int GDALDataset::GetRasterXSize() + +{ + return nRasterXSize; +} + +/************************************************************************/ +/* GDALGetRasterXSize() */ +/************************************************************************/ + +/** + * \brief Fetch raster width in pixels. + * + * @see GDALDataset::GetRasterXSize(). + */ + +int CPL_STDCALL GDALGetRasterXSize( GDALDatasetH hDataset ) + +{ + VALIDATE_POINTER1( hDataset, "GDALGetRasterXSize", 0 ); + + return ((GDALDataset *) hDataset)->GetRasterXSize(); +} + + +/************************************************************************/ +/* GetRasterYSize() */ +/************************************************************************/ + +/** + + \brief Fetch raster height in pixels. + + Equivalent of the C function GDALGetRasterYSize(). + + @return the height in pixels of raster bands in this GDALDataset. + +*/ + +int GDALDataset::GetRasterYSize() + +{ + return nRasterYSize; +} + +/************************************************************************/ +/* GDALGetRasterYSize() */ +/************************************************************************/ + +/** + * \brief Fetch raster height in pixels. + * + * @see GDALDataset::GetRasterYSize(). + */ + +int CPL_STDCALL GDALGetRasterYSize( GDALDatasetH hDataset ) + +{ + VALIDATE_POINTER1( hDataset, "GDALGetRasterYSize", 0 ); + + return ((GDALDataset *) hDataset)->GetRasterYSize(); +} + +/************************************************************************/ +/* GetRasterBand() */ +/************************************************************************/ + +/** + + \brief Fetch a band object for a dataset. + + Equivalent of the C function GDALGetRasterBand(). + + @param nBandId the index number of the band to fetch, from 1 to + GetRasterCount(). + + @return the nBandId th band object + +*/ + +GDALRasterBand * GDALDataset::GetRasterBand( int nBandId ) + +{ + if ( papoBands ) + { + if( nBandId < 1 || nBandId > nBands ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "GDALDataset::GetRasterBand(%d) - Illegal band #\n", + nBandId ); + return NULL; + } + else + return( papoBands[nBandId-1] ); + } + return NULL; +} + +/************************************************************************/ +/* GDALGetRasterBand() */ +/************************************************************************/ + +/** + * \brief Fetch a band object for a dataset. + * @see GDALDataset::GetRasterBand(). + */ + +GDALRasterBandH CPL_STDCALL GDALGetRasterBand( GDALDatasetH hDS, int nBandId ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetRasterBand", NULL ); + + return( (GDALRasterBandH) ((GDALDataset *) hDS)->GetRasterBand(nBandId) ); +} + +/************************************************************************/ +/* GetRasterCount() */ +/************************************************************************/ + +/** + * \brief Fetch the number of raster bands on this dataset. + * + * Same as the C function GDALGetRasterCount(). + * + * @return the number of raster bands. + */ + +int GDALDataset::GetRasterCount() + +{ + return papoBands ? nBands : 0; +} + +/************************************************************************/ +/* GDALGetRasterCount() */ +/************************************************************************/ + +/** + * \brief Fetch the number of raster bands on this dataset. + * + * @see GDALDataset::GetRasterCount(). + */ + +int CPL_STDCALL GDALGetRasterCount( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetRasterCount", 0 ); + + return( ((GDALDataset *) hDS)->GetRasterCount() ); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +/** + * \brief Fetch the projection definition string for this dataset. + * + * Same as the C function GDALGetProjectionRef(). + * + * The returned string defines the projection coordinate system of the + * image in OpenGIS WKT format. It should be suitable for use with the + * OGRSpatialReference class. + * + * When a projection definition is not available an empty (but not NULL) + * string is returned. + * + * @return a pointer to an internal projection reference string. It should + * not be altered, freed or expected to last for long. + * + * @see http://www.gdal.org/ogr/osr_tutorial.html + */ + +const char *GDALDataset::GetProjectionRef() + +{ + return( "" ); +} + +/************************************************************************/ +/* GDALGetProjectionRef() */ +/************************************************************************/ + +/** + * \brief Fetch the projection definition string for this dataset. + * + * @see GDALDataset::GetProjectionRef() + */ + +const char * CPL_STDCALL GDALGetProjectionRef( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetProjectionRef", NULL ); + + return( ((GDALDataset *) hDS)->GetProjectionRef() ); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +/** + * \brief Set the projection reference string for this dataset. + * + * The string should be in OGC WKT or PROJ.4 format. An error may occur + * because of incorrectly specified projection strings, because the dataset + * is not writable, or because the dataset does not support the indicated + * projection. Many formats do not support writing projections. + * + * This method is the same as the C GDALSetProjection() function. + * + * @param pszProjection projection reference string. + * + * @return CE_Failure if an error occurs, otherwise CE_None. + */ + +CPLErr GDALDataset::SetProjection( CPL_UNUSED const char * pszProjection ) +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "Dataset does not support the SetProjection() method." ); + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetProjection() */ +/************************************************************************/ + +/** + * \brief Set the projection reference string for this dataset. + * + * @see GDALDataset::SetProjection() + */ + +CPLErr CPL_STDCALL GDALSetProjection( GDALDatasetH hDS, const char * pszProjection ) + +{ + VALIDATE_POINTER1( hDS, "GDALSetProjection", CE_Failure ); + + return( ((GDALDataset *) hDS)->SetProjection(pszProjection) ); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +/** + * \brief Fetch the affine transformation coefficients. + * + * Fetches the coefficients for transforming between pixel/line (P,L) raster + * space, and projection coordinates (Xp,Yp) space. + * + * \code + * Xp = padfTransform[0] + P*padfTransform[1] + L*padfTransform[2]; + * Yp = padfTransform[3] + P*padfTransform[4] + L*padfTransform[5]; + * \endcode + * + * In a north up image, padfTransform[1] is the pixel width, and + * padfTransform[5] is the pixel height. The upper left corner of the + * upper left pixel is at position (padfTransform[0],padfTransform[3]). + * + * The default transform is (0,1,0,0,0,1) and should be returned even when + * a CE_Failure error is returned, such as for formats that don't support + * transformation to projection coordinates. + * + * This method does the same thing as the C GDALGetGeoTransform() function. + * + * @param padfTransform an existing six double buffer into which the + * transformation will be placed. + * + * @return CE_None on success, or CE_Failure if no transform can be fetched. + */ + +CPLErr GDALDataset::GetGeoTransform( double * padfTransform ) + +{ + CPLAssert( padfTransform != NULL ); + + padfTransform[0] = 0.0; /* X Origin (top left corner) */ + padfTransform[1] = 1.0; /* X Pixel size */ + padfTransform[2] = 0.0; + + padfTransform[3] = 0.0; /* Y Origin (top left corner) */ + padfTransform[4] = 0.0; + padfTransform[5] = 1.0; /* Y Pixel Size */ + + return( CE_Failure ); +} + +/************************************************************************/ +/* GDALGetGeoTransform() */ +/************************************************************************/ + +/** + * \brief Fetch the affine transformation coefficients. + * + * @see GDALDataset::GetGeoTransform() + */ + +CPLErr CPL_STDCALL GDALGetGeoTransform( GDALDatasetH hDS, double * padfTransform ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetGeoTransform", CE_Failure ); + + return( ((GDALDataset *) hDS)->GetGeoTransform(padfTransform) ); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +/** + * \brief Set the affine transformation coefficients. + * + * See GetGeoTransform() for details on the meaning of the padfTransform + * coefficients. + * + * This method does the same thing as the C GDALSetGeoTransform() function. + * + * @param padfTransform a six double buffer containing the transformation + * coefficients to be written with the dataset. + * + * @return CE_None on success, or CE_Failure if this transform cannot be + * written. + */ + +CPLErr GDALDataset::SetGeoTransform( CPL_UNUSED double * padfTransform ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetGeoTransform() not supported for this dataset." ); + + return( CE_Failure ); +} + +/************************************************************************/ +/* GDALSetGeoTransform() */ +/************************************************************************/ + +/** + * \brief Set the affine transformation coefficients. + * + * @see GDALDataset::SetGeoTransform() + */ + +CPLErr CPL_STDCALL +GDALSetGeoTransform( GDALDatasetH hDS, double * padfTransform ) + +{ + VALIDATE_POINTER1( hDS, "GDALSetGeoTransform", CE_Failure ); + + return( ((GDALDataset *) hDS)->SetGeoTransform(padfTransform) ); +} + +/************************************************************************/ +/* GetInternalHandle() */ +/************************************************************************/ + +/** + * \brief Fetch a format specific internally meaningful handle. + * + * This method is the same as the C GDALGetInternalHandle() method. + * + * @param pszHandleName the handle name desired. The meaningful names + * will be specific to the file format. + * + * @return the desired handle value, or NULL if not recognised/supported. + */ + +void *GDALDataset::GetInternalHandle( CPL_UNUSED const char * pszHandleName ) + +{ + return( NULL ); +} + +/************************************************************************/ +/* GDALGetInternalHandle() */ +/************************************************************************/ + +/** + * \brief Fetch a format specific internally meaningful handle. + * + * @see GDALDataset::GetInternalHandle() + */ + +void * CPL_STDCALL +GDALGetInternalHandle( GDALDatasetH hDS, const char * pszRequest ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetInternalHandle", NULL ); + + return( ((GDALDataset *) hDS)->GetInternalHandle(pszRequest) ); +} + +/************************************************************************/ +/* GetDriver() */ +/************************************************************************/ + +/** + * \brief Fetch the driver to which this dataset relates. + * + * This method is the same as the C GDALGetDatasetDriver() function. + * + * @return the driver on which the dataset was created with GDALOpen() or + * GDALCreate(). + */ + +GDALDriver * GDALDataset::GetDriver() + +{ + return poDriver; +} + +/************************************************************************/ +/* GDALGetDatasetDriver() */ +/************************************************************************/ + +/** + * \brief Fetch the driver to which this dataset relates. + * + * @see GDALDataset::GetDriver() + */ + +GDALDriverH CPL_STDCALL GDALGetDatasetDriver( GDALDatasetH hDataset ) + +{ + VALIDATE_POINTER1( hDataset, "GDALGetDatasetDriver", NULL ); + + return (GDALDriverH) ((GDALDataset *) hDataset)->GetDriver(); +} + +/************************************************************************/ +/* Reference() */ +/************************************************************************/ + +/** + * \brief Add one to dataset reference count. + * + * The reference is one after instantiation. + * + * This method is the same as the C GDALReferenceDataset() function. + * + * @return the post-increment reference count. + */ + +int GDALDataset::Reference() + +{ + return ++nRefCount; +} + +/************************************************************************/ +/* GDALReferenceDataset() */ +/************************************************************************/ + +/** + * \brief Add one to dataset reference count. + * + * @see GDALDataset::Reference() + */ + +int CPL_STDCALL GDALReferenceDataset( GDALDatasetH hDataset ) + +{ + VALIDATE_POINTER1( hDataset, "GDALReferenceDataset", 0 ); + + return ((GDALDataset *) hDataset)->Reference(); +} + +/************************************************************************/ +/* Dereference() */ +/************************************************************************/ + +/** + * \brief Subtract one from dataset reference count. + * + * The reference is one after instantiation. Generally when the reference + * count has dropped to zero the dataset may be safely deleted (closed). + * + * This method is the same as the C GDALDereferenceDataset() function. + * + * @return the post-decrement reference count. + */ + +int GDALDataset::Dereference() + +{ + return --nRefCount; +} + +/************************************************************************/ +/* GDALDereferenceDataset() */ +/************************************************************************/ + +/** + * \brief Subtract one from dataset reference count. + * + * @see GDALDataset::Dereference() + */ + +int CPL_STDCALL GDALDereferenceDataset( GDALDatasetH hDataset ) + +{ + VALIDATE_POINTER1( hDataset, "GDALDereferenceDataset", 0 ); + + return ((GDALDataset *) hDataset)->Dereference(); +} + +/************************************************************************/ +/* GetShared() */ +/************************************************************************/ + +/** + * \brief Returns shared flag. + * + * @return TRUE if the GDALDataset is available for sharing, or FALSE if not. + */ + +int GDALDataset::GetShared() + +{ + return bShared; +} + +/************************************************************************/ +/* MarkAsShared() */ +/************************************************************************/ + +/** + * \brief Mark this dataset as available for sharing. + */ + +void GDALDataset::MarkAsShared() + +{ + CPLAssert( !bShared ); + + bShared = TRUE; + if( bIsInternal ) + return; + + GIntBig nPID = GDALGetResponsiblePIDForCurrentThread(); + SharedDatasetCtxt* psStruct; + + /* Insert the dataset in the set of shared opened datasets */ + CPLMutexHolderD( &hDLMutex ); + if (phSharedDatasetSet == NULL) + phSharedDatasetSet = CPLHashSetNew(GDALSharedDatasetHashFunc, GDALSharedDatasetEqualFunc, GDALSharedDatasetFreeFunc); + + psStruct = (SharedDatasetCtxt*)CPLMalloc(sizeof(SharedDatasetCtxt)); + psStruct->poDS = this; + psStruct->nPID = nPID; + psStruct->eAccess = eAccess; + psStruct->pszDescription = CPLStrdup(GetDescription()); + if(CPLHashSetLookup(phSharedDatasetSet, psStruct) != NULL) + { + CPLFree(psStruct); + ReportError(CE_Failure, CPLE_AppDefined, + "An existing shared dataset already has this description. This should not happen."); + } + else + { + CPLHashSetInsert(phSharedDatasetSet, psStruct); + + (*poAllDatasetMap)[this] = nPID; + } +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +/** + * \brief Get number of GCPs. + * + * This method is the same as the C function GDALGetGCPCount(). + * + * @return number of GCPs for this dataset. Zero if there are none. + */ + +int GDALDataset::GetGCPCount() + +{ + return 0; +} + +/************************************************************************/ +/* GDALGetGCPCount() */ +/************************************************************************/ + +/** + * \brief Get number of GCPs. + * + * @see GDALDataset::GetGCPCount() + */ + +int CPL_STDCALL GDALGetGCPCount( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetGCPCount", 0 ); + + return ((GDALDataset *) hDS)->GetGCPCount(); +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +/** + * \brief Get output projection for GCPs. + * + * This method is the same as the C function GDALGetGCPProjection(). + * + * The projection string follows the normal rules from GetProjectionRef(). + * + * @return internal projection string or "" if there are no GCPs. + * It should not be altered, freed or expected to last for long. + */ + +const char *GDALDataset::GetGCPProjection() + +{ + return ""; +} + +/************************************************************************/ +/* GDALGetGCPProjection() */ +/************************************************************************/ + +/** + * \brief Get output projection for GCPs. + * + * @see GDALDataset::GetGCPProjection() + */ + +const char * CPL_STDCALL GDALGetGCPProjection( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetGCPProjection", NULL ); + + return ((GDALDataset *) hDS)->GetGCPProjection(); +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +/** + * \brief Fetch GCPs. + * + * This method is the same as the C function GDALGetGCPs(). + * + * @return pointer to internal GCP structure list. It should not be modified, + * and may change on the next GDAL call. + */ + +const GDAL_GCP *GDALDataset::GetGCPs() + +{ + return NULL; +} + +/************************************************************************/ +/* GDALGetGCPs() */ +/************************************************************************/ + +/** + * \brief Fetch GCPs. + * + * @see GDALDataset::GetGCPs() + */ + +const GDAL_GCP * CPL_STDCALL GDALGetGCPs( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetGCPs", NULL ); + + return ((GDALDataset *) hDS)->GetGCPs(); +} + + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +/** + * \brief Assign GCPs. + * + * This method is the same as the C function GDALSetGCPs(). + * + * This method assigns the passed set of GCPs to this dataset, as well as + * setting their coordinate system. Internally copies are made of the + * coordinate system and list of points, so the caller remains responsible for + * deallocating these arguments if appropriate. + * + * Most formats do not support setting of GCPs, even formats that can + * handle GCPs. These formats will return CE_Failure. + * + * @param nGCPCount number of GCPs being assigned. + * + * @param pasGCPList array of GCP structures being assign (nGCPCount in array). + * + * @param pszGCPProjection the new OGC WKT coordinate system to assign for the + * GCP output coordinates. This parameter should be "" if no output coordinate + * system is known. + * + * @return CE_None on success, CE_Failure on failure (including if action is + * not supported for this format). + */ + +CPLErr GDALDataset::SetGCPs( int nGCPCount, + const GDAL_GCP *pasGCPList, + const char * pszGCPProjection ) + +{ + (void) nGCPCount; + (void) pasGCPList; + (void) pszGCPProjection; + + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "Dataset does not support the SetGCPs() method." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetGCPs() */ +/************************************************************************/ + +/** + * \brief Assign GCPs. + * + * @see GDALDataset::SetGCPs() + */ + +CPLErr CPL_STDCALL GDALSetGCPs( GDALDatasetH hDS, int nGCPCount, + const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ) + +{ + VALIDATE_POINTER1( hDS, "GDALSetGCPs", CE_Failure ); + + return ((GDALDataset *) hDS)->SetGCPs( nGCPCount, pasGCPList, + pszGCPProjection ); +} + +/************************************************************************/ +/* BuildOverviews() */ +/************************************************************************/ + +/** + * \brief Build raster overview(s) + * + * If the operation is unsupported for the indicated dataset, then + * CE_Failure is returned, and CPLGetLastErrorNo() will return + * CPLE_NotSupported. + * + * This method is the same as the C function GDALBuildOverviews(). + * + * @param pszResampling one of "NEAREST", "GAUSS", "CUBIC", "AVERAGE", "MODE", + * "AVERAGE_MAGPHASE" or "NONE" controlling the downsampling method applied. + * @param nOverviews number of overviews to build. + * @param panOverviewList the list of overview decimation factors to build. + * @param nListBands number of bands to build overviews for in panBandList. Build + * for all bands if this is 0. + * @param panBandList list of band numbers. + * @param pfnProgress a function to call to report progress, or NULL. + * @param pProgressData application data to pass to the progress function. + * + * @return CE_None on success or CE_Failure if the operation doesn't work. + * + * For example, to build overview level 2, 4 and 8 on all bands the following + * call could be made: + *
    + *   int       anOverviewList[3] = { 2, 4, 8 };
    + *   
    + *   poDataset->BuildOverviews( "NEAREST", 3, anOverviewList, 0, NULL, 
    + *                              GDALDummyProgress, NULL );
    + * 
    + * + * @see GDALRegenerateOverviews() + */ + +CPLErr GDALDataset::BuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + CPLErr eErr; + int *panAllBandList = NULL; + + if( nListBands == 0 ) + { + nListBands = GetRasterCount(); + panAllBandList = (int *) CPLMalloc(sizeof(int) * nListBands); + for( int i = 0; i < nListBands; i++ ) + panAllBandList[i] = i+1; + + panBandList = panAllBandList; + } + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + eErr = IBuildOverviews( pszResampling, nOverviews, panOverviewList, + nListBands, panBandList, pfnProgress, pProgressData ); + + if( panAllBandList != NULL ) + CPLFree( panAllBandList ); + + return eErr; +} + +/************************************************************************/ +/* GDALBuildOverviews() */ +/************************************************************************/ + +/** + * \brief Build raster overview(s) + * + * @see GDALDataset::BuildOverviews() + */ + +CPLErr CPL_STDCALL GDALBuildOverviews( GDALDatasetH hDataset, + const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + VALIDATE_POINTER1( hDataset, "GDALBuildOverviews", CE_Failure ); + + return ((GDALDataset *) hDataset)->BuildOverviews( + pszResampling, nOverviews, panOverviewList, nListBands, panBandList, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* IBuildOverviews() */ +/* */ +/* Default implementation. */ +/************************************************************************/ + +CPLErr GDALDataset::IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + if( oOvManager.IsInitialized() ) + return oOvManager.BuildOverviews( NULL, pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); + else + { + ReportError( CE_Failure, CPLE_NotSupported, + "BuildOverviews() not supported for this dataset." ); + + return( CE_Failure ); + } +} + +/************************************************************************/ +/* IRasterIO() */ +/* */ +/* The default implementation of IRasterIO() is to pass the */ +/* request off to each band objects rasterio methods with */ +/* appropriate arguments. */ +/************************************************************************/ + +CPLErr GDALDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + int iBandIndex; + CPLErr eErr = CE_None; + const char* pszInterleave = NULL; + + CPLAssert( NULL != pData ); + + if (nXSize == nBufXSize && nYSize == nBufYSize && nBandCount > 1 && + (pszInterleave = GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE")) != NULL && + EQUAL(pszInterleave, "PIXEL")) + { + return BlockBasedRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + psExtraArg ); + } + + GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress; + void *pProgressDataGlobal = psExtraArg->pProgressData; + + for( iBandIndex = 0; + iBandIndex < nBandCount && eErr == CE_None; + iBandIndex++ ) + { + GDALRasterBand *poBand = GetRasterBand(panBandMap[iBandIndex]); + GByte *pabyBandData; + + if (poBand == NULL) + { + eErr = CE_Failure; + break; + } + + pabyBandData = ((GByte *) pData) + iBandIndex * nBandSpace; + + if( nBandCount > 1 ) + { + psExtraArg->pfnProgress = GDALScaledProgress; + psExtraArg->pProgressData = + GDALCreateScaledProgress( 1.0 * iBandIndex / nBandCount, + 1.0 * (iBandIndex + 1) / nBandCount, + pfnProgressGlobal, + pProgressDataGlobal ); + if( psExtraArg->pProgressData == NULL ) + psExtraArg->pfnProgress = NULL; + } + + eErr = poBand->IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + (void *) pabyBandData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, + psExtraArg ); + + if( nBandCount > 1 ) + GDALDestroyScaledProgress( psExtraArg->pProgressData ); + } + + psExtraArg->pfnProgress = pfnProgressGlobal; + psExtraArg->pProgressData = pProgressDataGlobal; + + return eErr; +} + +/************************************************************************/ +/* ValidateRasterIOOrAdviseReadParameters() */ +/************************************************************************/ + +CPLErr GDALDataset::ValidateRasterIOOrAdviseReadParameters( + const char* pszCallingFunc, + int* pbStopProcessingOnCENone, + int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + int nBandCount, int *panBandMap) +{ + +/* -------------------------------------------------------------------- */ +/* Some size values are "noop". Lets just return to avoid */ +/* stressing lower level functions. */ +/* -------------------------------------------------------------------- */ + if( nXSize < 1 || nYSize < 1 || nBufXSize < 1 || nBufYSize < 1 ) + { + CPLDebug( "GDAL", + "%s skipped for odd window or buffer size.\n" + " Window = (%d,%d)x%dx%d\n" + " Buffer = %dx%d\n", + pszCallingFunc, + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize ); + + *pbStopProcessingOnCENone = TRUE; + return CE_None; + } + + CPLErr eErr = CE_None; + *pbStopProcessingOnCENone = FALSE; + + if( nXOff < 0 || nXOff > INT_MAX - nXSize || nXOff + nXSize > nRasterXSize + || nYOff < 0 || nYOff > INT_MAX - nYSize || nYOff + nYSize > nRasterYSize ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Access window out of range in %s. Requested\n" + "(%d,%d) of size %dx%d on raster of %dx%d.", + pszCallingFunc, nXOff, nYOff, nXSize, nYSize, nRasterXSize, nRasterYSize ); + eErr = CE_Failure; + } + + if( panBandMap == NULL && nBandCount > GetRasterCount() ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "%s: nBandCount cannot be greater than %d", + pszCallingFunc, GetRasterCount() ); + eErr = CE_Failure; + } + + for( int i = 0; i < nBandCount && eErr == CE_None; i++ ) + { + int iBand = (panBandMap != NULL) ? panBandMap[i] : i + 1; + if( iBand < 1 || iBand > GetRasterCount() ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "%s: panBandMap[%d] = %d, this band does not exist on dataset.", + pszCallingFunc, i, iBand ); + eErr = CE_Failure; + } + + if( eErr == CE_None && GetRasterBand( iBand ) == NULL ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "%s: panBandMap[%d]=%d, this band should exist but is NULL!", + pszCallingFunc, i, iBand ); + eErr = CE_Failure; + } + } + + return eErr; +} + +/************************************************************************/ +/* RasterIO() */ +/************************************************************************/ + +/** + * \brief Read/write a region of image data from multiple bands. + * + * This method allows reading a region of one or more GDALRasterBands from + * this dataset into a buffer, or writing data from a buffer into a region + * of the GDALRasterBands. It automatically takes care of data type + * translation if the data type (eBufType) of the buffer is different than + * that of the GDALRasterBand. + * The method also takes care of image decimation / replication if the + * buffer size (nBufXSize x nBufYSize) is different than the size of the + * region being accessed (nXSize x nYSize). + * + * The nPixelSpace, nLineSpace and nBandSpace parameters allow reading into or + * writing from various organization of buffers. + * + * For highest performance full resolution data access, read and write + * on "block boundaries" as returned by GetBlockSize(), or use the + * ReadBlock() and WriteBlock() methods. + * + * This method is the same as the C GDALDatasetRasterIO() or + * GDALDatasetRasterIOEx() functions. + * + * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param pData The buffer into which the data should be read, or from which + * it should be written. This buffer must contain at least + * nBufXSize * nBufYSize * nBandCount words of type eBufType. It is organized + * in left to right,top to bottom pixel order. Spacing is controlled by the + * nPixelSpace, and nLineSpace parameters. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the pData data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nBandCount the number of bands being read or written. + * + * @param panBandMap the list of nBandCount band numbers being read/written. + * Note band numbers are 1 based. This may be NULL to select the first + * nBandCount bands. + * + * @param nPixelSpace The byte offset from the start of one pixel value in + * pData to the start of the next pixel value within a scanline. If defaulted + * (0) the size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset from the start of one scanline in + * pData to the start of the next. If defaulted (0) the size of the datatype + * eBufType * nBufXSize is used. + * + * @param nBandSpace the byte offset from the start of one bands data to the + * start of the next. If defaulted (0) the value will be + * nLineSpace * nBufYSize implying band sequential organization + * of the data buffer. + * + * @param psExtraArg (new in GDAL 2.0) pointer to a GDALRasterIOExtraArg structure with additional + * arguments to specify resampling and progress callback, or NULL for default + * behaviour. The GDAL_RASTERIO_RESAMPLING configuration option can also be defined + * to override the default resampling to one of BILINEAR, CUBIC, CUBICSPLINE, + * LANCZOS, AVERAGE or MODE. + * + * @return CE_Failure if the access fails, otherwise CE_None. + */ + +CPLErr GDALDataset::RasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + int i = 0; + int bNeedToFreeBandMap = FALSE; + CPLErr eErr = CE_None; + + GDALRasterIOExtraArg sExtraArg; + if( psExtraArg == NULL ) + { + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + psExtraArg = &sExtraArg; + } + else if( psExtraArg->nVersion != RASTERIO_EXTRA_ARG_CURRENT_VERSION ) + { + ReportError( CE_Failure, CPLE_AppDefined, + "Unhandled version of GDALRasterIOExtraArg" ); + return CE_Failure; + } + + GDALRasterIOExtraArgSetResampleAlg(psExtraArg, nXSize, nYSize, + nBufXSize, nBufYSize); + + if( NULL == pData ) + { + ReportError( CE_Failure, CPLE_AppDefined, + "The buffer into which the data should be read is null" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Do some validation of parameters. */ +/* -------------------------------------------------------------------- */ + + if( eRWFlag != GF_Read && eRWFlag != GF_Write ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "eRWFlag = %d, only GF_Read (0) and GF_Write (1) are legal.", + eRWFlag ); + return CE_Failure; + } + + int bStopProcessing = FALSE; + eErr = ValidateRasterIOOrAdviseReadParameters( "RasterIO()", &bStopProcessing, + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + nBandCount, panBandMap); + if( eErr != CE_None || bStopProcessing ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* If pixel and line spacing are defaulted assign reasonable */ +/* value assuming a packed buffer. */ +/* -------------------------------------------------------------------- */ + if( nPixelSpace == 0 ) + nPixelSpace = GDALGetDataTypeSize( eBufType ) / 8; + + if( nLineSpace == 0 ) + { + nLineSpace = nPixelSpace * nBufXSize; + } + + if( nBandSpace == 0 && nBandCount > 1 ) + { + nBandSpace = nLineSpace * nBufYSize; + } + + if( panBandMap == NULL ) + { + panBandMap = (int *) VSIMalloc2(sizeof(int), nBandCount); + if (panBandMap == NULL) + { + ReportError( CE_Failure, CPLE_OutOfMemory, + "Out of memory while allocating band map array" ); + return CE_Failure; + } + for( i = 0; i < nBandCount; i++ ) + panBandMap[i] = i+1; + + bNeedToFreeBandMap = TRUE; + } + + + int bCallLeaveReadWrite = EnterReadWrite(eRWFlag); + +/* -------------------------------------------------------------------- */ +/* We are being forced to use cached IO instead of a driver */ +/* specific implementation. */ +/* -------------------------------------------------------------------- */ + if( bForceCachedIO ) + { + eErr = + BlockBasedRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + psExtraArg ); + } + +/* -------------------------------------------------------------------- */ +/* Call the format specific function. */ +/* -------------------------------------------------------------------- */ + else if( eErr == CE_None ) + { + eErr = + IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + psExtraArg ); + } + + if( bCallLeaveReadWrite ) LeaveReadWrite(); + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + if( bNeedToFreeBandMap ) + CPLFree( panBandMap ); + + return eErr; +} + +/************************************************************************/ +/* GDALDatasetRasterIO() */ +/************************************************************************/ + +/** + * \brief Read/write a region of image data from multiple bands. + * + * Use GDALDatasetRasterIOEx() if 64 bit spacings or extra arguments (resampling + * resolution, progress callback, etc. are needed) + * + * @see GDALDataset::RasterIO() + */ + +CPLErr CPL_STDCALL +GDALDatasetRasterIO( GDALDatasetH hDS, GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + int nPixelSpace, int nLineSpace, int nBandSpace ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetRasterIO", CE_Failure ); + + GDALDataset *poDS = (GDALDataset *) hDS; + + return( poDS->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, NULL ) ); +} + +/************************************************************************/ +/* GDALDatasetRasterIOEx() */ +/************************************************************************/ + +/** + * \brief Read/write a region of image data from multiple bands. + * + * @see GDALDataset::RasterIO() + * @since GDAL 2.0 + */ + +CPLErr CPL_STDCALL +GDALDatasetRasterIOEx( GDALDatasetH hDS, GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetRasterIOEx", CE_Failure ); + + GDALDataset *poDS = (GDALDataset *) hDS; + + return( poDS->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg ) ); +} +/************************************************************************/ +/* GetOpenDatasets() */ +/************************************************************************/ + +/** + * \brief Fetch all open GDAL dataset handles. + * + * This method is the same as the C function GDALGetOpenDatasets(). + * + * NOTE: This method is not thread safe. The returned list may change + * at any time and it should not be freed. + * + * @param pnCount integer into which to place the count of dataset pointers + * being returned. + * + * @return a pointer to an array of dataset handles. + */ + +GDALDataset **GDALDataset::GetOpenDatasets( int *pnCount ) + +{ + CPLMutexHolderD( &hDLMutex ); + + if (poAllDatasetMap != NULL) + { + int i = 0; + *pnCount = poAllDatasetMap->size(); + ppDatasets = (GDALDataset**) CPLRealloc(ppDatasets, (*pnCount) * sizeof(GDALDataset*)); + std::map::iterator oIter = poAllDatasetMap->begin(); + for(; oIter != poAllDatasetMap->end(); ++oIter, i++ ) + ppDatasets[i] = oIter->first; + return ppDatasets; + } + else + { + *pnCount = 0; + return NULL; + } +} + +/************************************************************************/ +/* GDALGetOpenDatasets() */ +/************************************************************************/ + +/** + * \brief Fetch all open GDAL dataset handles. + * + * @see GDALDataset::GetOpenDatasets() + */ + +void CPL_STDCALL GDALGetOpenDatasets( GDALDatasetH **ppahDSList, int *pnCount ) + +{ + VALIDATE_POINTER0( ppahDSList, "GDALGetOpenDatasets" ); + VALIDATE_POINTER0( pnCount, "GDALGetOpenDatasets" ); + + *ppahDSList = (GDALDatasetH *) GDALDataset::GetOpenDatasets( pnCount); +} + + +/************************************************************************/ +/* GDALCleanOpenDatasetsList() */ +/************************************************************************/ + +/* Useful when called from the child of a fork(), to avoid closing */ +/* the datasets of the parent at the child termination */ +void GDALNullifyOpenDatasetsList() +{ + poAllDatasetMap = NULL; + phSharedDatasetSet = NULL; + ppDatasets = NULL; + hDLMutex = NULL; +} + +/************************************************************************/ +/* GDALGetAccess() */ +/************************************************************************/ + +/** + * \brief Return access flag + * + * @see GDALDataset::GetAccess() + */ + +int CPL_STDCALL GDALGetAccess( GDALDatasetH hDS ) +{ + VALIDATE_POINTER1( hDS, "GDALGetAccess", 0 ); + + return ((GDALDataset *) hDS)->GetAccess(); +} + +/************************************************************************/ +/* AdviseRead() */ +/************************************************************************/ + +/** + * \brief Advise driver of upcoming read requests. + * + * Some GDAL drivers operate more efficiently if they know in advance what + * set of upcoming read requests will be made. The AdviseRead() method allows + * an application to notify the driver of the region and bands of interest, + * and at what resolution the region will be read. + * + * Many drivers just ignore the AdviseRead() call, but it can dramatically + * accelerate access via some drivers. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the pData data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nBandCount the number of bands being read or written. + * + * @param panBandMap the list of nBandCount band numbers being read/written. + * Note band numbers are 1 based. This may be NULL to select the first + * nBandCount bands. + * + * @param papszOptions a list of name=value strings with special control + * options. Normally this is NULL. + * + * @return CE_Failure if the request is invalid and CE_None if it works or + * is ignored. + */ + +CPLErr GDALDataset::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + char **papszOptions ) + +{ + int iBand; + +/* -------------------------------------------------------------------- */ +/* Do some validation of parameters. */ +/* -------------------------------------------------------------------- */ + int bStopProcessing = FALSE; + CPLErr eErr = ValidateRasterIOOrAdviseReadParameters( "AdviseRead()", &bStopProcessing, + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + nBandCount, panBandMap); + if( eErr != CE_None || bStopProcessing ) + return eErr; + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + CPLErr eErr; + GDALRasterBand *poBand; + + if( panBandMap == NULL ) + poBand = GetRasterBand(iBand+1); + else + poBand = GetRasterBand(panBandMap[iBand]); + + eErr = poBand->AdviseRead( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eBufType, papszOptions ); + + if( eErr != CE_None ) + return eErr; + } + + return CE_None; +} + +/************************************************************************/ +/* GDALDatasetAdviseRead() */ +/************************************************************************/ + +/** + * \brief Advise driver of upcoming read requests. + * + * @see GDALDataset::AdviseRead() + */ +CPLErr CPL_STDCALL +GDALDatasetAdviseRead( GDALDatasetH hDS, + int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, GDALDataType eDT, + int nBandCount, int *panBandMap,char **papszOptions ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetAdviseRead", CE_Failure ); + + return ((GDALDataset *) hDS)->AdviseRead( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eDT, + nBandCount, panBandMap, + papszOptions ); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +/** + * \brief Fetch files forming dataset. + * + * Returns a list of files believed to be part of this dataset. If it returns + * an empty list of files it means there is believed to be no local file + * system files associated with the dataset (for instance a virtual dataset). + * The returned file list is owned by the caller and should be deallocated + * with CSLDestroy(). + * + * The returned filenames will normally be relative or absolute paths + * depending on the path used to originally open the dataset. The strings + * will be UTF-8 encoded. + * + * This method is the same as the C GDALGetFileList() function. + * + * @return NULL or a NULL terminated array of file names. + */ + +char **GDALDataset::GetFileList() + +{ + CPLString osMainFilename = GetDescription(); + int bMainFileReal; + VSIStatBufL sStat; + +/* -------------------------------------------------------------------- */ +/* Is the main filename even a real filesystem object? */ +/* -------------------------------------------------------------------- */ + bMainFileReal = VSIStatExL( osMainFilename, &sStat, VSI_STAT_EXISTS_FLAG ) == 0; + +/* -------------------------------------------------------------------- */ +/* Form new list. */ +/* -------------------------------------------------------------------- */ + char **papszList = NULL; + + if( bMainFileReal ) + papszList = CSLAddString( papszList, osMainFilename ); + +/* -------------------------------------------------------------------- */ +/* Do we have a known overview file? */ +/* -------------------------------------------------------------------- */ + if( oOvManager.IsInitialized() && oOvManager.poODS != NULL ) + { + char **papszOvrList = oOvManager.poODS->GetFileList(); + papszList = CSLInsertStrings( papszList, -1, papszOvrList ); + CSLDestroy( papszOvrList ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a known overview file? */ +/* -------------------------------------------------------------------- */ + if( oOvManager.HaveMaskFile() ) + { + char **papszMskList = oOvManager.poMaskDS->GetFileList(); + char **papszIter = papszMskList; + while( papszIter && *papszIter ) + { + if( CSLFindString( papszList, *papszIter ) < 0 ) + papszList = CSLAddString( papszList, *papszIter ); + papszIter ++; + } + CSLDestroy( papszMskList ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a world file? */ +/* -------------------------------------------------------------------- */ + if( bMainFileReal && + !GDALCanFileAcceptSidecarFile(osMainFilename) ) + { + const char* pszExtension = CPLGetExtension( osMainFilename ); + if( strlen(pszExtension) > 2 ) + { + // first + last + 'w' + char szDerivedExtension[4]; + szDerivedExtension[0] = pszExtension[0]; + szDerivedExtension[1] = pszExtension[strlen(pszExtension)-1]; + szDerivedExtension[2] = 'w'; + szDerivedExtension[3] = '\0'; + CPLString osWorldFilename = CPLResetExtension( osMainFilename, szDerivedExtension ); + + if (oOvManager.papszInitSiblingFiles) + { + int iSibling = CSLFindString(oOvManager.papszInitSiblingFiles, + CPLGetFilename(osWorldFilename)); + if (iSibling >= 0) + { + osWorldFilename.resize(strlen(osWorldFilename) - + strlen(oOvManager.papszInitSiblingFiles[iSibling])); + osWorldFilename += oOvManager.papszInitSiblingFiles[iSibling]; + papszList = CSLAddString( papszList, osWorldFilename ); + } + } + else if( VSIStatExL( osWorldFilename, &sStat, VSI_STAT_EXISTS_FLAG ) == 0 ) + papszList = CSLAddString( papszList, osWorldFilename ); + } + } + + return papszList; +} + +/************************************************************************/ +/* GDALGetFileList() */ +/************************************************************************/ + +/** + * \brief Fetch files forming dataset. + * + * @see GDALDataset::GetFileList() + */ + +char ** CPL_STDCALL GDALGetFileList( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALGetFileList", NULL ); + + return ((GDALDataset *) hDS)->GetFileList(); +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +/** + * \brief Adds a mask band to the dataset + * + * The default implementation of the CreateMaskBand() method is implemented + * based on similar rules to the .ovr handling implemented using the + * GDALDefaultOverviews object. A TIFF file with the extension .msk will + * be created with the same basename as the original file, and it will have + * one band. + * The mask images will be deflate compressed tiled images with the same + * block size as the original image if possible. + * + * Note that if you got a mask band with a previous call to GetMaskBand(), + * it might be invalidated by CreateMaskBand(). So you have to call GetMaskBand() + * again. + * + * @since GDAL 1.5.0 + * + * @param nFlags ignored. GMF_PER_DATASET will be assumed. + * @return CE_None on success or CE_Failure on an error. + * + * @see http://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask + * + */ +CPLErr GDALDataset::CreateMaskBand( int nFlags ) + +{ + if( oOvManager.IsInitialized() ) + { + CPLErr eErr = oOvManager.CreateMaskBand( nFlags, -1 ); + if (eErr != CE_None) + return eErr; + + /* Invalidate existing raster band masks */ + int i; + for(i=0;ibOwnMask) + delete poBand->poMask; + poBand->bOwnMask = false; + poBand->poMask = NULL; + } + + return CE_None; + } + + ReportError( CE_Failure, CPLE_NotSupported, + "CreateMaskBand() not supported for this dataset." ); + + return( CE_Failure ); +} + +/************************************************************************/ +/* GDALCreateDatasetMaskBand() */ +/************************************************************************/ + +/** + * \brief Adds a mask band to the dataset + * @see GDALDataset::CreateMaskBand() + */ +CPLErr CPL_STDCALL GDALCreateDatasetMaskBand( GDALDatasetH hDS, int nFlags ) + +{ + VALIDATE_POINTER1( hDS, "GDALCreateDatasetMaskBand", CE_Failure ); + + return ((GDALDataset *) hDS)->CreateMaskBand( nFlags ); +} + +/************************************************************************/ +/* GDALOpen() */ +/************************************************************************/ + +/** + * \brief Open a raster file as a GDALDataset. + * + * This function will try to open the passed file, or virtual dataset + * name by invoking the Open method of each registered GDALDriver in turn. + * The first successful open will result in a returned dataset. If all + * drivers fail then NULL is returned and an error is issued. + * + * Several recommendations : + *
      + *
    • If you open a dataset object with GA_Update access, it is not recommended + * to open a new dataset on the same underlying file.
    • + *
    • The returned dataset should only be accessed by one thread at a time. If you + * want to use it from different threads, you must add all necessary code (mutexes, etc.) + * to avoid concurrent use of the object. (Some drivers, such as GeoTIFF, maintain internal + * state variables that are updated each time a new block is read, thus preventing concurrent + * use.)
    • + *
    + * + * For drivers supporting the VSI virtual file API, it is possible to open + * a file in a .zip archive (see VSIInstallZipFileHandler()), in a .tar/.tar.gz/.tgz archive + * (see VSIInstallTarFileHandler()) or on a HTTP / FTP server (see VSIInstallCurlFileHandler()) + * + * In some situations (dealing with unverified data), the datasets can be opened in another + * process through the \ref gdal_api_proxy mechanism. + * + * \sa GDALOpenShared() + * \sa GDALOpenEx() + * + * @param pszFilename the name of the file to access. In the case of + * exotic drivers this may not refer to a physical file, but instead contain + * information for the driver on how to access a dataset. It should be in UTF-8 + * encoding. + * + * @param eAccess the desired access, either GA_Update or GA_ReadOnly. Many + * drivers support only read only access. + * + * @return A GDALDatasetH handle or NULL on failure. For C++ applications + * this handle can be cast to a GDALDataset *. + */ + +GDALDatasetH CPL_STDCALL +GDALOpen( const char * pszFilename, GDALAccess eAccess ) + +{ + return GDALOpenEx( pszFilename, + GDAL_OF_RASTER | + (eAccess == GA_Update ? GDAL_OF_UPDATE : 0) | + GDAL_OF_VERBOSE_ERROR, + NULL, NULL, NULL ); +} + + +/************************************************************************/ +/* GDALOpenEx() */ +/************************************************************************/ + +/** + * \brief Open a raster or vector file as a GDALDataset. + * + * This function will try to open the passed file, or virtual dataset + * name by invoking the Open method of each registered GDALDriver in turn. + * The first successful open will result in a returned dataset. If all + * drivers fail then NULL is returned and an error is issued. + * + * Several recommendations : + *
      + *
    • If you open a dataset object with GDAL_OF_UPDATE access, it is not recommended + * to open a new dataset on the same underlying file.
    • + *
    • The returned dataset should only be accessed by one thread at a time. If you + * want to use it from different threads, you must add all necessary code (mutexes, etc.) + * to avoid concurrent use of the object. (Some drivers, such as GeoTIFF, maintain internal + * state variables that are updated each time a new block is read, thus preventing concurrent + * use.)
    • + *
    + * + * For drivers supporting the VSI virtual file API, it is possible to open + * a file in a .zip archive (see VSIInstallZipFileHandler()), in a .tar/.tar.gz/.tgz archive + * (see VSIInstallTarFileHandler()) or on a HTTP / FTP server (see VSIInstallCurlFileHandler()) + * + * In some situations (dealing with unverified data), the datasets can be opened in another + * process through the \ref gdal_api_proxy mechanism. + * + * In order to reduce the need for searches through the operating system + * file system machinery, it is possible to give an optional list of files with + * the papszSiblingFiles parameter. + * This is the list of all files at the same level in the file system as the + * target file, including the target file. The filenames must not include any + * path components, are an essentially just the output of CPLReadDir() on the + * parent directory. If the target object does not have filesystem semantics + * then the file list should be NULL. + * + * @param pszFilename the name of the file to access. In the case of + * exotic drivers this may not refer to a physical file, but instead contain + * information for the driver on how to access a dataset. It should be in UTF-8 + * encoding. + * + * @param nOpenFlags a combination of GDAL_OF_ flags that may be combined through + * logical or operator. + *
      + *
    • Driver kind: GDAL_OF_RASTER for raster drivers, GDAL_OF_VECTOR for vector drivers. + * If none of the value is specified, both kinds are implied.
    • + *
    • Access mode: GDAL_OF_READONLY (exclusive)or GDAL_OF_UPDATE.
    • + *
    • Shared mode: GDAL_OF_SHARED. If set, it allows the sharing of + * GDALDataset handles for a dataset with other callers that have set GDAL_OF_SHARED. + * In particular, GDALOpenEx() will first consult its list of currently + * open and shared GDALDataset's, and if the GetDescription() name for one + * exactly matches the pszFilename passed to GDALOpenEx() it will be + * referenced and returned, if GDALOpenEx() is called from the same thread.
    • + *
    • Verbose error: GDAL_OF_VERBOSE_ERROR. If set, a failed attempt to open the + * file will lead to an error message to be reported.
    • + *
    + * + * @param papszAllowedDrivers NULL to consider all candidate drivers, or a NULL + * terminated list of strings with the driver short names that must be considered. + * + * @param papszOpenOptions NULL, or a NULL terminated list of strings with open + * options passed to candidate drivers. An option exists for all drivers, + * OVERVIEW_LEVEL=level, to select a particular overview level of a dataset. + * The level index starts at 0. The level number can be suffixed by "only" to specify that + * only this overview level must be visible, and not sub-levels. + * Open options are validated by default, and a warning is emitted in case the + * option is not recognized. In some scenarios, it might be not desirable (e.g. + * when not knowing which driver will open the file), so the special open option + * VALIDATE_OPEN_OPTIONS can be set to NO to avoid such warnings. + * + * @param papszSiblingFiles NULL, or a NULL terminated list of strings that are + * filenames that are auxiliary to the main filename. If NULL is passed, a probing + * of the file system will be done. + * + * @return A GDALDatasetH handle or NULL on failure. For C++ applications + * this handle can be cast to a GDALDataset *. + * + * @since GDAL 2.0 + */ + +GDALDatasetH CPL_STDCALL GDALOpenEx( const char* pszFilename, + unsigned int nOpenFlags, + const char* const* papszAllowedDrivers, + const char* const* papszOpenOptions, + const char* const* papszSiblingFiles ) +{ + VALIDATE_POINTER1( pszFilename, "GDALOpen", NULL ); + +/* -------------------------------------------------------------------- */ +/* In case of shared dataset, first scan the existing list to see */ +/* if it could already contain the requested dataset. */ +/* -------------------------------------------------------------------- */ + if( nOpenFlags & GDAL_OF_SHARED ) + { + if( nOpenFlags & GDAL_OF_INTERNAL ) + { + CPLError(CE_Failure, CPLE_IllegalArg, "GDAL_OF_SHARED and GDAL_OF_INTERNAL are exclusive"); + return NULL; + } + + CPLMutexHolderD( &hDLMutex ); + + if (phSharedDatasetSet != NULL) + { + GIntBig nThisPID = GDALGetResponsiblePIDForCurrentThread(); + SharedDatasetCtxt* psStruct; + SharedDatasetCtxt sStruct; + + sStruct.nPID = nThisPID; + sStruct.pszDescription = (char*) pszFilename; + sStruct.eAccess = (nOpenFlags & GDAL_OF_UPDATE) ? GA_Update : GA_ReadOnly; + psStruct = (SharedDatasetCtxt*) CPLHashSetLookup(phSharedDatasetSet, &sStruct); + if (psStruct == NULL && (nOpenFlags & GDAL_OF_UPDATE) == 0) + { + sStruct.eAccess = GA_Update; + psStruct = (SharedDatasetCtxt*) CPLHashSetLookup(phSharedDatasetSet, &sStruct); + } + if (psStruct) + { + psStruct->poDS->Reference(); + return psStruct->poDS; + } + } + } + + /* If no driver kind is specified, assume all are to be probed */ + if( (nOpenFlags & GDAL_OF_KIND_MASK) == 0 ) + nOpenFlags |= GDAL_OF_KIND_MASK; + + { + int* pnRecCount = (int*)CPLGetTLS( CTLS_GDALDATASET_REC_PROTECT_MAP ); + if( pnRecCount == NULL ) + { + pnRecCount = (int*) CPLMalloc(sizeof(int)); + *pnRecCount = 0; + CPLSetTLS( CTLS_GDALDATASET_REC_PROTECT_MAP, pnRecCount, TRUE ); + } + if( *pnRecCount == 100 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GDALOpen() called with too many recursion levels"); + return NULL; + } + (*pnRecCount) ++; + } + + int iDriver; + GDALDriverManager *poDM = GetGDALDriverManager(); + //CPLLocaleC oLocaleForcer; + + CPLErrorReset(); + CPLAssert( NULL != poDM ); + + /* Build GDALOpenInfo just now to avoid useless file stat'ing if a */ + /* shared dataset was asked before */ + GDALOpenInfo oOpenInfo(pszFilename, + nOpenFlags, + (char**) papszSiblingFiles); + oOpenInfo.papszOpenOptions = (char**) papszOpenOptions; + + for( iDriver = -1; iDriver < poDM->GetDriverCount(); iDriver++ ) + { + GDALDriver *poDriver; + GDALDataset *poDS; + + if( iDriver < 0 ) + poDriver = GDALGetAPIPROXYDriver(); + else + { + poDriver = poDM->GetDriver( iDriver ); + if (papszAllowedDrivers != NULL && + CSLFindString((char**)papszAllowedDrivers, GDALGetDriverShortName(poDriver)) == -1) + continue; + } + + if( (nOpenFlags & GDAL_OF_RASTER) != 0 && + (nOpenFlags & GDAL_OF_VECTOR) == 0 && + poDriver->GetMetadataItem(GDAL_DCAP_RASTER) == NULL ) + continue; + if( (nOpenFlags & GDAL_OF_VECTOR) != 0 && + (nOpenFlags & GDAL_OF_RASTER) == 0 && + poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == NULL ) + continue; + + /* Remove general OVERVIEW_LEVEL open options from list before */ + /* passing it to the driver, if it isn't a driver specific option already */ + char** papszTmpOpenOptions = NULL; + if( CSLFetchNameValue((char**)papszOpenOptions, "OVERVIEW_LEVEL") != NULL && + (poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST) == NULL || + CPLString(poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST)).ifind("OVERVIEW_LEVEL") == std::string::npos) ) + { + papszTmpOpenOptions = CSLDuplicate((char**)papszOpenOptions); + papszTmpOpenOptions = CSLSetNameValue(papszTmpOpenOptions, "OVERVIEW_LEVEL", NULL); + oOpenInfo.papszOpenOptions = papszTmpOpenOptions; + } + + int bIdentifyRes = + ( poDriver->pfnIdentify && poDriver->pfnIdentify(&oOpenInfo) > 0 ); + if( bIdentifyRes ) + { + GDALValidateOpenOptions( poDriver, oOpenInfo.papszOpenOptions ); + } + + if ( poDriver->pfnOpen != NULL ) + { + poDS = poDriver->pfnOpen( &oOpenInfo ); + // If we couldn't determine for sure with Identify() (it returned -1) + // but that Open() managed to open the file, post validate options. + if( poDS != NULL && poDriver->pfnIdentify && !bIdentifyRes ) + GDALValidateOpenOptions( poDriver, oOpenInfo.papszOpenOptions ); + } + else if( poDriver->pfnOpenWithDriverArg != NULL ) + { + poDS = poDriver->pfnOpenWithDriverArg( poDriver, &oOpenInfo ); + } + else + { + CSLDestroy(papszTmpOpenOptions); + oOpenInfo.papszOpenOptions = (char**) papszOpenOptions; + continue; + } + + CSLDestroy(papszTmpOpenOptions); + oOpenInfo.papszOpenOptions = (char**) papszOpenOptions; + + if( poDS != NULL ) + { + if( strlen(poDS->GetDescription()) == 0 ) + poDS->SetDescription( pszFilename ); + + if( poDS->poDriver == NULL ) + poDS->poDriver = poDriver; + + if( poDS->papszOpenOptions == NULL ) + poDS->papszOpenOptions = CSLDuplicate((char**)papszOpenOptions); + + if( !(nOpenFlags & GDAL_OF_INTERNAL) ) + { + if( CPLGetPID() != GDALGetResponsiblePIDForCurrentThread() ) + CPLDebug( "GDAL", "GDALOpen(%s, this=%p) succeeds as %s (pid=%d, responsiblePID=%d).", + pszFilename, poDS, poDriver->GetDescription(), + (int)CPLGetPID(), (int)GDALGetResponsiblePIDForCurrentThread() ); + else + CPLDebug( "GDAL", "GDALOpen(%s, this=%p) succeeds as %s.", + pszFilename, poDS, poDriver->GetDescription() ); + + poDS->AddToDatasetOpenList(); + } + + int* pnRecCount = (int*)CPLGetTLS( CTLS_GDALDATASET_REC_PROTECT_MAP ); + if( pnRecCount ) + (*pnRecCount) --; + + if( nOpenFlags & GDAL_OF_SHARED ) + { + if (strcmp(pszFilename, poDS->GetDescription()) != 0) + { + CPLError(CE_Warning, CPLE_NotSupported, + "A dataset opened by GDALOpenShared should have the same filename (%s) " + "and description (%s)", + pszFilename, poDS->GetDescription()); + } + else + { + poDS->MarkAsShared(); + } + } + + /* Deal with generic OVERVIEW_LEVEL open option, unless it is */ + /* driver specific */ + if( CSLFetchNameValue((char**) papszOpenOptions, "OVERVIEW_LEVEL") != NULL && + (poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST) == NULL || + CPLString(poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST)).ifind("OVERVIEW_LEVEL") == std::string::npos) ) + { + CPLString osVal(CSLFetchNameValue((char**) papszOpenOptions, "OVERVIEW_LEVEL")); + int nOvrLevel = atoi(osVal); + int bThisLevelOnly = osVal.ifind("only") != std::string::npos; + GDALDataset* poOvrDS = GDALCreateOverviewDataset(poDS, nOvrLevel, bThisLevelOnly, TRUE); + if( poOvrDS == NULL ) + { + if( nOpenFlags & GDAL_OF_VERBOSE_ERROR ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Cannot open overview level %d of %s", + nOvrLevel, pszFilename ); + } + GDALClose( poDS ); + poDS = NULL; + } + else + { + poDS = poOvrDS; + } + } + + return (GDALDatasetH) poDS; + } + + if( CPLGetLastErrorNo() != 0 ) + { + int* pnRecCount = (int*)CPLGetTLS( CTLS_GDALDATASET_REC_PROTECT_MAP ); + if( pnRecCount ) + (*pnRecCount) --; + + return NULL; + } + } + + if( nOpenFlags & GDAL_OF_VERBOSE_ERROR ) + { + if( oOpenInfo.bStatOK ) + CPLError( CE_Failure, CPLE_OpenFailed, + "`%s' not recognised as a supported file format.\n", + pszFilename ); + else + CPLError( CE_Failure, CPLE_OpenFailed, + "`%s' does not exist in the file system,\n" + "and is not recognised as a supported dataset name.\n", + pszFilename ); + } + + int* pnRecCount = (int*)CPLGetTLS( CTLS_GDALDATASET_REC_PROTECT_MAP ); + if( pnRecCount ) + (*pnRecCount) --; + + return NULL; +} + +/************************************************************************/ +/* GDALOpenShared() */ +/************************************************************************/ + +/** + * \brief Open a raster file as a GDALDataset. + * + * This function works the same as GDALOpen(), but allows the sharing of + * GDALDataset handles for a dataset with other callers to GDALOpenShared(). + * + * In particular, GDALOpenShared() will first consult it's list of currently + * open and shared GDALDataset's, and if the GetDescription() name for one + * exactly matches the pszFilename passed to GDALOpenShared() it will be + * referenced and returned. + * + * Starting with GDAL 1.6.0, if GDALOpenShared() is called on the same pszFilename + * from two different threads, a different GDALDataset object will be returned as + * it is not safe to use the same dataset from different threads, unless the user + * does explicitly use mutexes in its code. + * + * For drivers supporting the VSI virtual file API, it is possible to open + * a file in a .zip archive (see VSIInstallZipFileHandler()), in a .tar/.tar.gz/.tgz archive + * (see VSIInstallTarFileHandler()) or on a HTTP / FTP server (see VSIInstallCurlFileHandler()) + * + * In some situations (dealing with unverified data), the datasets can be opened in another + * process through the \ref gdal_api_proxy mechanism. + * + * \sa GDALOpen() + * \sa GDALOpenEx() + * + * @param pszFilename the name of the file to access. In the case of + * exotic drivers this may not refer to a physical file, but instead contain + * information for the driver on how to access a dataset. It should be in + * UTF-8 encoding. + * + * @param eAccess the desired access, either GA_Update or GA_ReadOnly. Many + * drivers support only read only access. + * + * @return A GDALDatasetH handle or NULL on failure. For C++ applications + * this handle can be cast to a GDALDataset *. + */ + +GDALDatasetH CPL_STDCALL +GDALOpenShared( const char *pszFilename, GDALAccess eAccess ) +{ + VALIDATE_POINTER1( pszFilename, "GDALOpenShared", NULL ); + return GDALOpenEx( pszFilename, + GDAL_OF_RASTER | + (eAccess == GA_Update ? GDAL_OF_UPDATE : 0) | + GDAL_OF_SHARED | + GDAL_OF_VERBOSE_ERROR, + NULL, NULL, NULL ); +} + +/************************************************************************/ +/* GDALClose() */ +/************************************************************************/ + +/** + * \brief Close GDAL dataset. + * + * For non-shared datasets (opened with GDALOpen()) the dataset is closed + * using the C++ "delete" operator, recovering all dataset related resources. + * For shared datasets (opened with GDALOpenShared()) the dataset is + * dereferenced, and closed only if the referenced count has dropped below 1. + * + * @param hDS The dataset to close. May be cast from a "GDALDataset *". + */ + +void CPL_STDCALL GDALClose( GDALDatasetH hDS ) + +{ + if( hDS == NULL ) + return; + + GDALDataset *poDS = (GDALDataset *) hDS; + + if (poDS->GetShared()) + { +/* -------------------------------------------------------------------- */ +/* If this file is in the shared dataset list then dereference */ +/* it, and only delete/remote it if the reference count has */ +/* dropped to zero. */ +/* -------------------------------------------------------------------- */ + if( poDS->Dereference() > 0 ) + return; + + delete poDS; + return; + } + +/* -------------------------------------------------------------------- */ +/* This is not shared dataset, so directly delete it. */ +/* -------------------------------------------------------------------- */ + delete poDS; +} + +/************************************************************************/ +/* GDALDumpOpenDataset() */ +/************************************************************************/ + +static int GDALDumpOpenSharedDatasetsForeach(void* elt, void* user_data) +{ + SharedDatasetCtxt* psStruct = (SharedDatasetCtxt*) elt; + FILE *fp = (FILE*) user_data; + const char *pszDriverName; + GDALDataset *poDS = psStruct->poDS; + + if( poDS->GetDriver() == NULL ) + pszDriverName = "DriverIsNULL"; + else + pszDriverName = poDS->GetDriver()->GetDescription(); + + poDS->Reference(); + VSIFPrintf( fp, " %d %c %-6s %7d %dx%dx%d %s\n", + poDS->Dereference(), + poDS->GetShared() ? 'S' : 'N', + pszDriverName, + (int)psStruct->nPID, + poDS->GetRasterXSize(), + poDS->GetRasterYSize(), + poDS->GetRasterCount(), + poDS->GetDescription() ); + + return TRUE; +} + + +static int GDALDumpOpenDatasetsForeach(GDALDataset* poDS, FILE *fp) +{ + const char *pszDriverName; + + /* Don't list shared datasets. They have already been listed by */ + /* GDALDumpOpenSharedDatasetsForeach */ + if (poDS->GetShared()) + return TRUE; + + if( poDS->GetDriver() == NULL ) + pszDriverName = "DriverIsNULL"; + else + pszDriverName = poDS->GetDriver()->GetDescription(); + + poDS->Reference(); + VSIFPrintf( fp, " %d %c %-6s %7d %dx%dx%d %s\n", + poDS->Dereference(), + poDS->GetShared() ? 'S' : 'N', + pszDriverName, + -1, + poDS->GetRasterXSize(), + poDS->GetRasterYSize(), + poDS->GetRasterCount(), + poDS->GetDescription() ); + + return TRUE; +} + +/** + * \brief List open datasets. + * + * Dumps a list of all open datasets (shared or not) to the indicated + * text file (may be stdout or stderr). This function is primarily intended + * to assist in debugging "dataset leaks" and reference counting issues. + * The information reported includes the dataset name, referenced count, + * shared status, driver name, size, and band count. + */ + +int CPL_STDCALL GDALDumpOpenDatasets( FILE *fp ) + +{ + VALIDATE_POINTER1( fp, "GDALDumpOpenDatasets", 0 ); + + CPLMutexHolderD( &hDLMutex ); + + if (poAllDatasetMap != NULL) + { + VSIFPrintf( fp, "Open GDAL Datasets:\n" ); + std::map::iterator oIter = poAllDatasetMap->begin(); + for(; oIter != poAllDatasetMap->end(); ++oIter ) + { + GDALDumpOpenDatasetsForeach(oIter->first, fp); + } + if (phSharedDatasetSet != NULL) + { + CPLHashSetForeach(phSharedDatasetSet, GDALDumpOpenSharedDatasetsForeach, fp); + } + return (int)poAllDatasetMap->size(); + } + else + { + return 0; + } +} + +/************************************************************************/ +/* BeginAsyncReader() */ +/************************************************************************/ + +/** + * \brief Sets up an asynchronous data request + * + * This method establish an asynchronous raster read request for the + * indicated window on the dataset into the indicated buffer. The parameters + * for windowing, buffer size, buffer type and buffer organization are similar + * to those for GDALDataset::RasterIO(); however, this call only launches + * the request and filling the buffer is accomplished via calls to + * GetNextUpdatedRegion() on the return GDALAsyncReader session object. + * + * Once all processing for the created session is complete, or if no further + * refinement of the request is required, the GDALAsyncReader object should + * be destroyed with the GDALDataset::EndAsyncReader() method. + * + * Note that the data buffer (pData) will potentially continue to be + * updated as long as the session lives, but it is not deallocated when + * the session (GDALAsyncReader) is destroyed with EndAsyncReader(). It + * should be deallocated by the application at that point. + * + * Additional information on asynchronous IO in GDAL may be found at: + * http://trac.osgeo.org/gdal/wiki/rfc24_progressive_data_support + * + * This method is the same as the C GDALBeginAsyncReader() function. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param pBuf The buffer into which the data should be read. This buffer must + * contain at least nBufXSize * nBufYSize * nBandCount words of type eBufType. + * It is organized in left to right,top to bottom pixel order. Spacing is + * controlled by the nPixelSpace, and nLineSpace parameters. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the pData data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nBandCount the number of bands being read or written. + * + * @param panBandMap the list of nBandCount band numbers being read/written. + * Note band numbers are 1 based. This may be NULL to select the first + * nBandCount bands. + * + * @param nPixelSpace The byte offset from the start of one pixel value in + * pData to the start of the next pixel value within a scanline. If defaulted + * (0) the size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset from the start of one scanline in + * pData to the start of the next. If defaulted the size of the datatype + * eBufType * nBufXSize is used. + * + * @param nBandSpace the byte offset from the start of one bands data to the + * start of the next. If defaulted (zero) the value will be + * nLineSpace * nBufYSize implying band sequential organization + * of the data buffer. + * + * @param papszOptions Driver specific control options in a string list or NULL. + * Consult driver documentation for options supported. + * + * @return The GDALAsyncReader object representing the request. + */ + +GDALAsyncReader* +GDALDataset::BeginAsyncReader(int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, char **papszOptions) +{ + // See gdaldefaultasync.cpp + + return + GDALGetDefaultAsyncReader( this, + nXOff, nYOff, nXSize, nYSize, + pBuf, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + papszOptions ); +} + +/************************************************************************/ +/* GDALBeginAsyncReader() */ +/************************************************************************/ + +GDALAsyncReaderH CPL_STDCALL +GDALBeginAsyncReader(GDALDatasetH hDS, int xOff, int yOff, + int xSize, int ySize, + void *pBuf, + int bufXSize, int bufYSize, + GDALDataType bufType, + int nBandCount, int* bandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, + char **papszOptions) + +{ + VALIDATE_POINTER1( hDS, "GDALDataset", NULL ); + return (GDALAsyncReaderH)((GDALDataset *) hDS)-> + BeginAsyncReader(xOff, yOff, + xSize, ySize, + pBuf, bufXSize, bufYSize, + bufType, nBandCount, bandMap, + nPixelSpace, nLineSpace, + nBandSpace, papszOptions); +} + +/************************************************************************/ +/* EndAsyncReader() */ +/************************************************************************/ + +/** + * End asynchronous request. + * + * This method destroys an asynchronous io request and recovers all + * resources associated with it. + * + * This method is the same as the C function GDALEndAsyncReader(). + * + * @param poARIO pointer to a GDALAsyncReader + */ + +void GDALDataset::EndAsyncReader(GDALAsyncReader *poARIO ) +{ + delete poARIO; +} + +/************************************************************************/ +/* GDALEndAsyncReader() */ +/************************************************************************/ +void CPL_STDCALL GDALEndAsyncReader(GDALDatasetH hDS, GDALAsyncReaderH hAsyncReaderH) +{ + VALIDATE_POINTER0( hDS, "GDALDataset" ); + VALIDATE_POINTER0( hAsyncReaderH, "GDALAsyncReader" ); + ((GDALDataset *) hDS) -> EndAsyncReader((GDALAsyncReader *)hAsyncReaderH); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +/** + * Drop references to any other datasets referenced by this dataset. + * + * This method should release any reference to other datasets (e.g. a VRT + * dataset to its sources), but not close the current dataset itself. + * + * If at least, one reference to a dependent dataset has been dropped, + * this method should return TRUE. Otherwise it *should* return FALSE. + * (Failure to return the proper value might result in infinite loop) + * + * This method can be called several times on a given dataset. After + * the first time, it should not do anything and return FALSE. + * + * The driver implementation may choose to destroy its raster bands, + * so be careful not to call any method on the raster bands afterwards. + * + * Basically the only safe action you can do after calling CloseDependantDatasets() + * is to call the destructor. + * + * Note: the only legitimate caller of CloseDependantDatasets() is + * GDALDriverManager::~GDALDriverManager() + * + * @return TRUE if at least one reference to another dataset has been dropped. + */ +int GDALDataset::CloseDependentDatasets() +{ + return oOvManager.CloseDependentDatasets(); +} + +/************************************************************************/ +/* ReportError() */ +/************************************************************************/ + +/** + * \brief Emits an error related to a dataset. + * + * This function is a wrapper for regular CPLError(). The only difference + * with CPLError() is that it prepends the error message with the dataset + * name. + * + * @param eErrClass one of CE_Warning, CE_Failure or CE_Fatal. + * @param err_no the error number (CPLE_*) from cpl_error.h. + * @param fmt a printf() style format string. Any additional arguments + * will be treated as arguments to fill in this format in a manner + * similar to printf(). + * + * @since GDAL 1.9.0 + */ + +void GDALDataset::ReportError(CPLErr eErrClass, int err_no, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + char szNewFmt[256]; + const char* pszDSName = GetDescription(); + if (strlen(fmt) + strlen(pszDSName) + 3 >= sizeof(szNewFmt) - 1) + pszDSName = CPLGetFilename(pszDSName); + if (pszDSName[0] != '\0' && + strlen(fmt) + strlen(pszDSName) + 3 < sizeof(szNewFmt) - 1) + { + snprintf(szNewFmt, sizeof(szNewFmt), "%s: %s", + pszDSName, fmt); + CPLErrorV( eErrClass, err_no, szNewFmt, args ); + } + else + { + CPLErrorV( eErrClass, err_no, fmt, args ); + } + va_end(args); +} + +/************************************************************************/ +/* GetDriverName() */ +/************************************************************************/ + +const char* GDALDataset::GetDriverName() +{ + if( poDriver ) + return poDriver->GetDescription(); + return ""; +} + +/************************************************************************/ +/* GDALDatasetReleaseResultSet() */ +/************************************************************************/ + +/** + \brief Release results of ExecuteSQL(). + + This function should only be used to deallocate OGRLayers resulting from + an ExecuteSQL() call on the same GDALDataset. Failure to deallocate a + results set before destroying the GDALDataset may cause errors. + + This function is the same as the C++ method GDALDataset::ReleaseResultSet() + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param poResultsSet the result of a previous ExecuteSQL() call. + +*/ +void GDALDatasetReleaseResultSet( GDALDatasetH hDS, OGRLayerH hLayer ) + +{ + VALIDATE_POINTER0( hDS, "GDALDatasetReleaseResultSet" ); + + ((GDALDataset *) hDS)->ReleaseResultSet( (OGRLayer *) hLayer ); +} + +/************************************************************************/ +/* GDALDatasetTestCapability() */ +/************************************************************************/ + +/** + \brief Test if capability is available. + + One of the following dataset capability names can be passed into this + function, and a TRUE or FALSE value will be returned indicating whether or not + the capability is available for this object. + +
      +
    • ODsCCreateLayer: True if this datasource can create new layers.

      +

    • ODsCDeleteLayer: True if this datasource can delete existing layers.

      +

    • ODsCCreateGeomFieldAfterCreateLayer: True if the layers of this + datasource support CreateGeomField() just after layer creation.

      +

    • ODsCCurveGeometries: True if this datasource supports curve geometries.

      +

    • ODsCTransactions: True if this datasource supports (efficient) transactions.

      +

    • ODsCEmulatedTransactions: True if this datasource supports transactions through emulation.

      +

    + + The \#define macro forms of the capability names should be used in preference + to the strings themselves to avoid mispelling. + + This function is the same as the C++ method GDALDataset::TestCapability() + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param pszCapability the capability to test. + + @return TRUE if capability available otherwise FALSE. + +*/ +int GDALDatasetTestCapability( GDALDatasetH hDS, const char *pszCap ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetTestCapability", 0 ); + VALIDATE_POINTER1( pszCap, "GDALDatasetTestCapability", 0 ); + + return ((GDALDataset *) hDS)->TestCapability( pszCap ); +} + +/************************************************************************/ +/* GDALDatasetGetLayerCount() */ +/************************************************************************/ + +/** + \brief Get the number of layers in this dataset. + + This function is the same as the C++ method GDALDataset::GetLayerCount() + + @since GDAL 2.0 + + @param hDS the dataset handle. + @return layer count. +*/ + +int GDALDatasetGetLayerCount( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetH", 0 ); + + return ((GDALDataset *)hDS)->GetLayerCount(); +} + +/************************************************************************/ +/* GDALDatasetGetLayer() */ +/************************************************************************/ + +/** + \brief Fetch a layer by index. + + The returned layer remains owned by the + GDALDataset and should not be deleted by the application. + + This function is the same as the C++ method GDALDataset::GetLayer() + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param iLayer a layer number between 0 and GetLayerCount()-1. + + @return the layer, or NULL if iLayer is out of range or an error occurs. +*/ + +OGRLayerH GDALDatasetGetLayer( GDALDatasetH hDS, int iLayer ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetGetLayer", NULL ); + + return (OGRLayerH) ((GDALDataset*)hDS)->GetLayer( iLayer ); +} + +/************************************************************************/ +/* GDALDatasetGetLayerByName() */ +/************************************************************************/ + +/** + \brief Fetch a layer by name. + + The returned layer remains owned by the + GDALDataset and should not be deleted by the application. + + This function is the same as the C++ method GDALDataset::GetLayerByName() + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param pszLayerName the layer name of the layer to fetch. + + @return the layer, or NULL if Layer is not found or an error occurs. +*/ + +OGRLayerH GDALDatasetGetLayerByName( GDALDatasetH hDS, const char *pszName ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetGetLayerByName", NULL ); + + return (OGRLayerH) ((GDALDataset *) hDS)->GetLayerByName( pszName ); +} + +/************************************************************************/ +/* GDALDatasetDeleteLayer() */ +/************************************************************************/ + +/** + \brief Delete the indicated layer from the datasource. + + If this function is supported + the ODsCDeleteLayer capability will test TRUE on the GDALDataset. + + This method is the same as the C++ method GDALDataset::DeleteLayer(). + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param iLayer the index of the layer to delete. + + @return OGRERR_NONE on success, or OGRERR_UNSUPPORTED_OPERATION if deleting + layers is not supported for this datasource. + +*/ +OGRErr GDALDatasetDeleteLayer( GDALDatasetH hDS, int iLayer ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetH", OGRERR_INVALID_HANDLE ); + + return ((GDALDataset *) hDS)->DeleteLayer( iLayer ); +} + +/************************************************************************/ +/* CreateLayer() */ +/************************************************************************/ + +/** +\brief This method attempts to create a new layer on the dataset with the indicated name, coordinate system, geometry type. + +The papszOptions argument +can be used to control driver specific creation options. These options are +normally documented in the format specific documentation. + + In GDAL 2.0, drivers should extend the ICreateLayer() method and not CreateLayer(). + CreateLayer() adds validation of layer creation options, before delegating the + actual work to ICreateLayer(). + + This method is the same as the C function GDALDatasetCreateLayer() and the + deprecated OGR_DS_CreateLayer(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param hDS the dataset handle + @param pszName the name for the new layer. This should ideally not +match any existing layer on the datasource. + @param poSpatialRef the coordinate system to use for the new layer, or NULL if +no coordinate system is available. + @param eGType the geometry type for the layer. Use wkbUnknown if there +are no constraints on the types geometry to be written. + @param papszOptions a StringList of name=value options. Options are driver +specific. + + @return NULL is returned on failure, or a new OGRLayer handle on success. + +Example: + +\code +#include "gdal.h" +#include "cpl_string.h" + +... + + OGRLayer *poLayer; + char **papszOptions; + + if( !poDS->TestCapability( ODsCCreateLayer ) ) + { + ... + } + + papszOptions = CSLSetNameValue( papszOptions, "DIM", "2" ); + poLayer = poDS->CreateLayer( "NewLayer", NULL, wkbUnknown, + papszOptions ); + CSLDestroy( papszOptions ); + + if( poLayer == NULL ) + { + ... + } +\endcode +*/ + +OGRLayer *GDALDataset::CreateLayer( const char * pszName, + OGRSpatialReference * poSpatialRef, + OGRwkbGeometryType eGType, + char **papszOptions ) + +{ + ValidateLayerCreationOptions( papszOptions ); + + if( OGR_GT_IsNonLinear(eGType) && !TestCapability(ODsCCurveGeometries) ) + { + eGType = OGR_GT_GetLinear(eGType); + } + + OGRLayer* poLayer = ICreateLayer(pszName, poSpatialRef, eGType, papszOptions); +#ifdef DEBUG + if( poLayer != NULL && OGR_GT_IsNonLinear(poLayer->GetGeomType()) && + !poLayer->TestCapability(OLCCurveGeometries) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Inconsistent driver: Layer geometry type is non-linear, but " + "TestCapability(OLCCurveGeometries) returns FALSE." ); + } +#endif + + return poLayer; +} + +/************************************************************************/ +/* GDALDatasetCreateLayer() */ +/************************************************************************/ + +/** +\brief This function attempts to create a new layer on the dataset with the indicated name, coordinate system, geometry type. + +The papszOptions argument +can be used to control driver specific creation options. These options are +normally documented in the format specific documentation. + + This method is the same as the C++ method GDALDataset::CreateLayer(). + + @since GDAL 2.0 + + @param hDS the dataset handle + @param pszName the name for the new layer. This should ideally not +match any existing layer on the datasource. + @param poSpatialRef the coordinate system to use for the new layer, or NULL if +no coordinate system is available. + @param eGType the geometry type for the layer. Use wkbUnknown if there +are no constraints on the types geometry to be written. + @param papszOptions a StringList of name=value options. Options are driver +specific. + + @return NULL is returned on failure, or a new OGRLayer handle on success. + +Example: + +\code +#include "gdal.h" +#include "cpl_string.h" + +... + + OGRLayer *poLayer; + char **papszOptions; + + if( !poDS->TestCapability( ODsCCreateLayer ) ) + { + ... + } + + papszOptions = CSLSetNameValue( papszOptions, "DIM", "2" ); + poLayer = poDS->CreateLayer( "NewLayer", NULL, wkbUnknown, + papszOptions ); + CSLDestroy( papszOptions ); + + if( poLayer == NULL ) + { + ... + } +\endcode +*/ + +OGRLayerH GDALDatasetCreateLayer( GDALDatasetH hDS, + const char * pszName, + OGRSpatialReferenceH hSpatialRef, + OGRwkbGeometryType eType, + char ** papszOptions ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetCreateLayer", NULL ); + + if (pszName == NULL) + { + CPLError ( CE_Failure, CPLE_ObjectNull, "Name was NULL in GDALDatasetCreateLayer"); + return 0; + } + return (OGRLayerH) ((GDALDataset *)hDS)->CreateLayer( + pszName, (OGRSpatialReference *) hSpatialRef, eType, papszOptions ); +} + + +/************************************************************************/ +/* GDALDatasetCopyLayer() */ +/************************************************************************/ + +/** + \brief Duplicate an existing layer. + + This function creates a new layer, duplicate the field definitions of the + source layer and then duplicate each features of the source layer. + The papszOptions argument + can be used to control driver specific creation options. These options are + normally documented in the format specific documentation. + The source layer may come from another dataset. + + This method is the same as the C++ method GDALDataset::CopyLayer() + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param hSrcLayer source layer. + @param pszNewName the name of the layer to create. + @param papszOptions a StringList of name=value options. Options are driver + specific. + + @return an handle to the layer, or NULL if an error occurs. +*/ +OGRLayerH GDALDatasetCopyLayer( GDALDatasetH hDS, + OGRLayerH hSrcLayer, const char *pszNewName, + char **papszOptions ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_CopyGDALDatasetCopyLayerLayer", NULL ); + VALIDATE_POINTER1( hSrcLayer, "GDALDatasetCopyLayer", NULL ); + VALIDATE_POINTER1( pszNewName, "GDALDatasetCopyLayer", NULL ); + + return (OGRLayerH) + ((GDALDataset *) hDS)->CopyLayer( (OGRLayer *) hSrcLayer, + pszNewName, papszOptions ); +} + +/************************************************************************/ +/* GDALDatasetExecuteSQL() */ +/************************************************************************/ + +/** + \brief Execute an SQL statement against the data store. + + The result of an SQL query is either NULL for statements that are in error, + or that have no results set, or an OGRLayer pointer representing a results + set from the query. Note that this OGRLayer is in addition to the layers + in the data store and must be destroyed with + ReleaseResultSet() before the dataset is closed + (destroyed). + + This method is the same as the C++ method GDALDataset::ExecuteSQL() + + For more information on the SQL dialect supported internally by OGR + review the OGR SQL document. Some drivers (ie. + Oracle and PostGIS) pass the SQL directly through to the underlying RDBMS. + + Starting with OGR 1.10, the SQLITE dialect + can also be used. + + @since GDAL 2.0 + + @param hDS the dataset handle. + @param pszStatement the SQL statement to execute. + @param hSpatialFilter geometry which represents a spatial filter. Can be NULL. + @param pszDialect allows control of the statement dialect. If set to NULL, the +OGR SQL engine will be used, except for RDBMS drivers that will use their dedicated SQL engine, +unless OGRSQL is explicitly passed as the dialect. Starting with OGR 1.10, the SQLITE dialect +can also be used. + + @return an OGRLayer containing the results of the query. Deallocate with + ReleaseResultSet(). + +*/ + +OGRLayerH GDALDatasetExecuteSQL( GDALDatasetH hDS, + const char *pszStatement, + OGRGeometryH hSpatialFilter, + const char *pszDialect ) + +{ + VALIDATE_POINTER1( hDS, "GDALDatasetExecuteSQL", NULL ); + + return (OGRLayerH) + ((GDALDataset *)hDS)->ExecuteSQL( pszStatement, + (OGRGeometry *) hSpatialFilter, + pszDialect ); +} + +/************************************************************************/ +/* GDALDatasetGetStyleTable() */ +/************************************************************************/ + +/** + \brief Returns dataset style table. + + This function is the same as the C++ method GDALDataset::GetStyleTable() + + @since GDAL 2.0 + + @param hDS the dataset handle + @return handle to a style table which should not be modified or freed by the + caller. +*/ + +OGRStyleTableH GDALDatasetGetStyleTable( GDALDatasetH hDS ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_GetStyleTable", NULL ); + + return (OGRStyleTableH) ((GDALDataset *) hDS)->GetStyleTable( ); +} + +/************************************************************************/ +/* GDALDatasetSetStyleTableDirectly() */ +/************************************************************************/ + +/** + \brief Set dataset style table. + + This function operate exactly as GDALDatasetSetStyleTable() except that it + assumes ownership of the passed table. + + This function is the same as the C++ method GDALDataset::SetStyleTableDirectly() + + @since GDAL 2.0 + + @param hDS the dataset handle + @param hStyleTable style table handle to set + +*/ + +void GDALDatasetSetStyleTableDirectly( GDALDatasetH hDS, + OGRStyleTableH hStyleTable ) + +{ + VALIDATE_POINTER0( hDS, "OGR_DS_SetStyleTableDirectly" ); + + ((GDALDataset *) hDS)->SetStyleTableDirectly( (OGRStyleTable *) hStyleTable); +} + +/************************************************************************/ +/* GDALDatasetSetStyleTable() */ +/************************************************************************/ + +/** + \brief Set dataset style table. + + This function operate exactly as GDALDatasetSetStyleTableDirectly() except that it + assumes ownership of the passed table. + + This function is the same as the C++ method GDALDataset::SetStyleTable() + + @since GDAL 2.0 + + @param hDS the dataset handle + @param hStyleTable style table handle to set + +*/ + +void GDALDatasetSetStyleTable( GDALDatasetH hDS, OGRStyleTableH hStyleTable ) + +{ + VALIDATE_POINTER0( hDS, "OGR_DS_SetStyleTable" ); + VALIDATE_POINTER0( hStyleTable, "OGR_DS_SetStyleTable" ); + + ((GDALDataset *) hDS)->SetStyleTable( (OGRStyleTable *) hStyleTable); +} + +/************************************************************************/ +/* ValidateLayerCreationOptions() */ +/************************************************************************/ + +int GDALDataset::ValidateLayerCreationOptions( const char* const* papszLCO ) +{ + const char *pszOptionList = GetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST ); + if( pszOptionList == NULL && poDriver != NULL ) + { + pszOptionList = + poDriver->GetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST ); + } + CPLString osDataset; + osDataset.Printf("dataset %s", GetDescription()); + return GDALValidateOptions( pszOptionList, papszLCO, + "layer creation option", + osDataset ); +} + +/************************************************************************/ +/* Release() */ +/************************************************************************/ + +/** + \fn OGRErr OGRDataSource::Release(); + +\brief Drop a reference to this dataset, and if the reference count drops to one close (destroy) the dataset. + +This method is the same as the C function OGRReleaseDataSource(). + +@deprecated. In GDAL 2, use GDALClose() instead + +@return OGRERR_NONE on success or an error code. +*/ + +OGRErr GDALDataset::Release() + +{ + GDALClose( (GDALDatasetH) this ); + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetRefCount() */ +/************************************************************************/ + +/** +\brief Fetch reference count. + +This method is the same as the C function OGR_DS_GetRefCount(). + +In GDAL 1.X, this method used to be in the OGRDataSource class. + +@return the current reference count for the datasource object itself. +*/ + + +int GDALDataset::GetRefCount() const + +{ + return nRefCount; +} + +/************************************************************************/ +/* GetSummaryRefCount() */ +/************************************************************************/ + +/** +\brief Fetch reference count of datasource and all owned layers. + +This method is the same as the C function OGR_DS_GetSummaryRefCount(). + +In GDAL 1.X, this method used to be in the OGRDataSource class. + +@deprecated + +@return the current summary reference count for the datasource and its layers. +*/ + +int GDALDataset::GetSummaryRefCount() const + +{ + CPLMutexHolderD( (CPLMutex**) &m_hMutex ); + int nSummaryCount = nRefCount; + int iLayer; + GDALDataset *poUseThis = (GDALDataset *) this; + + for( iLayer=0; iLayer < poUseThis->GetLayerCount(); iLayer++ ) + nSummaryCount += poUseThis->GetLayer( iLayer )->GetRefCount(); + + return nSummaryCount; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +/** +\brief This method attempts to create a new layer on the dataset with the indicated name, coordinate system, geometry type. + +This method is reserved to implementation by drivers. + +The papszOptions argument +can be used to control driver specific creation options. These options are +normally documented in the format specific documentation. + + @param pszName the name for the new layer. This should ideally not +match any existing layer on the datasource. + @param poSpatialRef the coordinate system to use for the new layer, or NULL if +no coordinate system is available. + @param eGType the geometry type for the layer. Use wkbUnknown if there +are no constraints on the types geometry to be written. + @param papszOptions a StringList of name=value options. Options are driver +specific. + + @return NULL is returned on failure, or a new OGRLayer handle on success. + + @since GDAL 2.0 +*/ + +OGRLayer *GDALDataset::ICreateLayer( const char * pszName, + OGRSpatialReference * poSpatialRef, + OGRwkbGeometryType eGType, + char **papszOptions ) + +{ + (void) eGType; + (void) poSpatialRef; + (void) pszName; + (void) papszOptions; + + CPLError( CE_Failure, CPLE_NotSupported, + "CreateLayer() not supported by this dataset." ); + + return NULL; +} + +/************************************************************************/ +/* CopyLayer() */ +/************************************************************************/ + +/** + \brief Duplicate an existing layer. + + This method creates a new layer, duplicate the field definitions of the + source layer and then duplicate each features of the source layer. + The papszOptions argument + can be used to control driver specific creation options. These options are + normally documented in the format specific documentation. + The source layer may come from another dataset. + + This method is the same as the C function GDALDatasetCopyLayer() and the + deprecated OGR_DS_CopyLayer(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param poSrcLayer source layer. + @param pszNewName the name of the layer to create. + @param papszOptions a StringList of name=value options. Options are driver + specific. + + @return an handle to the layer, or NULL if an error occurs. +*/ + +OGRLayer *GDALDataset::CopyLayer( OGRLayer *poSrcLayer, + const char *pszNewName, + char **papszOptions ) + +{ + OGRFeatureDefn *poSrcDefn = poSrcLayer->GetLayerDefn(); + OGRLayer *poDstLayer = NULL; + +/* -------------------------------------------------------------------- */ +/* Create the layer. */ +/* -------------------------------------------------------------------- */ + if( !TestCapability( ODsCCreateLayer ) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "This datasource does not support creation of layers." ); + return NULL; + } + + CPLErrorReset(); + if( poSrcDefn->GetGeomFieldCount() > 1 && + TestCapability(ODsCCreateGeomFieldAfterCreateLayer) ) + { + poDstLayer =ICreateLayer( pszNewName, NULL, wkbNone, papszOptions ); + } + else + { + poDstLayer =ICreateLayer( pszNewName, poSrcLayer->GetSpatialRef(), + poSrcDefn->GetGeomType(), papszOptions ); + } + + if( poDstLayer == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Add fields. Default to copy all fields, and make sure to */ +/* establish a mapping between indices, rather than names, in */ +/* case the target datasource has altered it (e.g. Shapefile */ +/* limited to 10 char field names). */ +/* -------------------------------------------------------------------- */ + int nSrcFieldCount = poSrcDefn->GetFieldCount(); + int nDstFieldCount = 0; + int iField, *panMap; + + // Initialize the index-to-index map to -1's + panMap = (int *) CPLMalloc( sizeof(int) * nSrcFieldCount ); + for( iField=0; iField < nSrcFieldCount; iField++) + panMap[iField] = -1; + + /* Caution : at the time of writing, the MapInfo driver */ + /* returns NULL until a field has been added */ + OGRFeatureDefn* poDstFDefn = poDstLayer->GetLayerDefn(); + if (poDstFDefn) + nDstFieldCount = poDstFDefn->GetFieldCount(); + for( iField = 0; iField < nSrcFieldCount; iField++ ) + { + OGRFieldDefn* poSrcFieldDefn = poSrcDefn->GetFieldDefn(iField); + OGRFieldDefn oFieldDefn( poSrcFieldDefn ); + + /* The field may have been already created at layer creation */ + int iDstField = -1; + if (poDstFDefn) + iDstField = poDstFDefn->GetFieldIndex(oFieldDefn.GetNameRef()); + if (iDstField >= 0) + { + panMap[iField] = iDstField; + } + else if (poDstLayer->CreateField( &oFieldDefn ) == OGRERR_NONE) + { + /* now that we've created a field, GetLayerDefn() won't return NULL */ + if (poDstFDefn == NULL) + poDstFDefn = poDstLayer->GetLayerDefn(); + + /* Sanity check : if it fails, the driver is buggy */ + if (poDstFDefn != NULL && + poDstFDefn->GetFieldCount() != nDstFieldCount + 1) + { + CPLError(CE_Warning, CPLE_AppDefined, + "The output driver has claimed to have added the %s field, but it did not!", + oFieldDefn.GetNameRef() ); + } + else + { + panMap[iField] = nDstFieldCount; + nDstFieldCount ++; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create geometry fields. */ +/* -------------------------------------------------------------------- */ + if( poSrcDefn->GetGeomFieldCount() > 1 && + TestCapability(ODsCCreateGeomFieldAfterCreateLayer) ) + { + int nSrcGeomFieldCount = poSrcDefn->GetGeomFieldCount(); + for( iField = 0; iField < nSrcGeomFieldCount; iField++ ) + { + poDstLayer->CreateGeomField( poSrcDefn->GetGeomFieldDefn(iField) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Check if the destination layer supports transactions and set a */ +/* default number of features in a single transaction. */ +/* -------------------------------------------------------------------- */ + int nGroupTransactions = 0; + if( poDstLayer->TestCapability( OLCTransactions ) ) + nGroupTransactions = 128; + +/* -------------------------------------------------------------------- */ +/* Transfer features. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poSrcLayer->ResetReading(); + + if( nGroupTransactions <= 0 ) + { + while( TRUE ) + { + OGRFeature *poDstFeature = NULL; + + poFeature = poSrcLayer->GetNextFeature(); + + if( poFeature == NULL ) + break; + + CPLErrorReset(); + poDstFeature = OGRFeature::CreateFeature( poDstLayer->GetLayerDefn() ); + + if( poDstFeature->SetFrom( poFeature, panMap, TRUE ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to translate feature " CPL_FRMT_GIB " from layer %s.\n", + poFeature->GetFID(), poSrcDefn->GetName() ); + OGRFeature::DestroyFeature( poFeature ); + CPLFree(panMap); + return poDstLayer; + } + + poDstFeature->SetFID( poFeature->GetFID() ); + + OGRFeature::DestroyFeature( poFeature ); + + CPLErrorReset(); + if( poDstLayer->CreateFeature( poDstFeature ) != OGRERR_NONE ) + { + OGRFeature::DestroyFeature( poDstFeature ); + CPLFree(panMap); + return poDstLayer; + } + + OGRFeature::DestroyFeature( poDstFeature ); + } + } + else + { + int i, bStopTransfer = FALSE, bStopTransaction = FALSE; + int nFeatCount = 0; // Number of features in the temporary array + int nFeaturesToAdd = 0; + OGRFeature **papoDstFeature = + (OGRFeature **)CPLCalloc(sizeof(OGRFeature *), nGroupTransactions); + while( !bStopTransfer ) + { +/* -------------------------------------------------------------------- */ +/* Fill the array with features */ +/* -------------------------------------------------------------------- */ + for( nFeatCount = 0; nFeatCount < nGroupTransactions; nFeatCount++ ) + { + poFeature = poSrcLayer->GetNextFeature(); + + if( poFeature == NULL ) + { + bStopTransfer = 1; + break; + } + + CPLErrorReset(); + papoDstFeature[nFeatCount] = + OGRFeature::CreateFeature( poDstLayer->GetLayerDefn() ); + + if( papoDstFeature[nFeatCount]->SetFrom( poFeature, panMap, TRUE ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to translate feature " CPL_FRMT_GIB " from layer %s.\n", + poFeature->GetFID(), poSrcDefn->GetName() ); + OGRFeature::DestroyFeature( poFeature ); + bStopTransfer = TRUE; + break; + } + + papoDstFeature[nFeatCount]->SetFID( poFeature->GetFID() ); + + OGRFeature::DestroyFeature( poFeature ); + } + nFeaturesToAdd = nFeatCount; + + CPLErrorReset(); + bStopTransaction = FALSE; + while( !bStopTransaction ) + { + bStopTransaction = TRUE; + poDstLayer->StartTransaction(); + for( i = 0; i < nFeaturesToAdd; i++ ) + { + if( poDstLayer->CreateFeature( papoDstFeature[i] ) != OGRERR_NONE ) + { + nFeaturesToAdd = i; + bStopTransfer = TRUE; + bStopTransaction = FALSE; + } + } + if( bStopTransaction ) + poDstLayer->CommitTransaction(); + else + poDstLayer->RollbackTransaction(); + } + + for( i = 0; i < nFeatCount; i++ ) + OGRFeature::DestroyFeature( papoDstFeature[i] ); + } + CPLFree(papoDstFeature); + } + + CPLFree(panMap); + + return poDstLayer; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +/** + \brief Delete the indicated layer from the datasource. + + If this method is supported + the ODsCDeleteLayer capability will test TRUE on the GDALDataset. + + This method is the same as the C function GDALDatasetDeleteLayer() and the + deprecated OGR_DS_DeleteLayer(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param iLayer the index of the layer to delete. + + @return OGRERR_NONE on success, or OGRERR_UNSUPPORTED_OPERATION if deleting + layers is not supported for this datasource. + +*/ +OGRErr GDALDataset::DeleteLayer( int iLayer ) + +{ + (void) iLayer; + CPLError( CE_Failure, CPLE_NotSupported, + "DeleteLayer() not supported by this dataset." ); + + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +/** + \brief Fetch a layer by name. + + The returned layer remains owned by the + GDALDataset and should not be deleted by the application. + + This method is the same as the C function GDALDatasetGetLayerByName() and the + deprecated OGR_DS_GetLayerByName(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param pszLayerName the layer name of the layer to fetch. + + @return the layer, or NULL if Layer is not found or an error occurs. +*/ + +OGRLayer *GDALDataset::GetLayerByName( const char *pszName ) + +{ + CPLMutexHolderD( &m_hMutex ); + + if ( ! pszName ) + return NULL; + + int i; + + /* first a case sensitive check */ + for( i = 0; i < GetLayerCount(); i++ ) + { + OGRLayer *poLayer = GetLayer(i); + + if( strcmp( pszName, poLayer->GetName() ) == 0 ) + return poLayer; + } + + /* then case insensitive */ + for( i = 0; i < GetLayerCount(); i++ ) + { + OGRLayer *poLayer = GetLayer(i); + + if( EQUAL( pszName, poLayer->GetName() ) ) + return poLayer; + } + + return NULL; +} + +/************************************************************************/ +/* ProcessSQLCreateIndex() */ +/* */ +/* The correct syntax for creating an index in our dialect of */ +/* SQL is: */ +/* */ +/* CREATE INDEX ON USING */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLCreateIndex( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + if( CSLCount(papszTokens) != 6 + || !EQUAL(papszTokens[0],"CREATE") + || !EQUAL(papszTokens[1],"INDEX") + || !EQUAL(papszTokens[2],"ON") + || !EQUAL(papszTokens[4],"USING") ) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in CREATE INDEX command.\n" + "Was '%s'\n" + "Should be of form 'CREATE INDEX ON USING '", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named layer. */ +/* -------------------------------------------------------------------- */ + int i; + OGRLayer *poLayer = NULL; + + { + CPLMutexHolderD( &m_hMutex ); + + for( i = 0; i < GetLayerCount(); i++ ) + { + poLayer = GetLayer(i); + + if( EQUAL(poLayer->GetName(),papszTokens[3]) ) + break; + } + + if( i >= GetLayerCount() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CREATE INDEX ON failed, no such layer as `%s'.", + papszTokens[3] ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + } + +/* -------------------------------------------------------------------- */ +/* Does this layer even support attribute indexes? */ +/* -------------------------------------------------------------------- */ + if( poLayer->GetIndex() == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CREATE INDEX ON not supported by this driver." ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named field. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < poLayer->GetLayerDefn()->GetFieldCount(); i++ ) + { + if( EQUAL(papszTokens[5], + poLayer->GetLayerDefn()->GetFieldDefn(i)->GetNameRef()) ) + break; + } + + CSLDestroy( papszTokens ); + + if( i >= poLayer->GetLayerDefn()->GetFieldCount() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "`%s' failed, field not found.", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Attempt to create the index. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr; + + eErr = poLayer->GetIndex()->CreateIndex( i ); + if( eErr == OGRERR_NONE ) + eErr = poLayer->GetIndex()->IndexAllFeatures( i ); + else + { + if( strlen(CPLGetLastErrorMsg()) == 0 ) + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot '%s'", pszSQLCommand); + } + + return eErr; +} + +/************************************************************************/ +/* ProcessSQLDropIndex() */ +/* */ +/* The correct syntax for droping one or more indexes in */ +/* the OGR SQL dialect is: */ +/* */ +/* DROP INDEX ON [USING ] */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLDropIndex( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + if( (CSLCount(papszTokens) != 4 && CSLCount(papszTokens) != 6) + || !EQUAL(papszTokens[0],"DROP") + || !EQUAL(papszTokens[1],"INDEX") + || !EQUAL(papszTokens[2],"ON") + || (CSLCount(papszTokens) == 6 && !EQUAL(papszTokens[4],"USING")) ) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in DROP INDEX command.\n" + "Was '%s'\n" + "Should be of form 'DROP INDEX ON
    [USING ]'", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named layer. */ +/* -------------------------------------------------------------------- */ + int i; + OGRLayer *poLayer=NULL; + + { + CPLMutexHolderD( &m_hMutex ); + + for( i = 0; i < GetLayerCount(); i++ ) + { + poLayer = GetLayer(i); + + if( EQUAL(poLayer->GetName(),papszTokens[3]) ) + break; + } + + if( i >= GetLayerCount() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CREATE INDEX ON failed, no such layer as `%s'.", + papszTokens[3] ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + } + +/* -------------------------------------------------------------------- */ +/* Does this layer even support attribute indexes? */ +/* -------------------------------------------------------------------- */ + if( poLayer->GetIndex() == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Indexes not supported by this driver." ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* If we weren't given a field name, drop all indexes. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr; + + if( CSLCount(papszTokens) == 4 ) + { + for( i = 0; i < poLayer->GetLayerDefn()->GetFieldCount(); i++ ) + { + OGRAttrIndex *poAttrIndex; + + poAttrIndex = poLayer->GetIndex()->GetFieldIndex(i); + if( poAttrIndex != NULL ) + { + eErr = poLayer->GetIndex()->DropIndex( i ); + if( eErr != OGRERR_NONE ) + { + CSLDestroy(papszTokens); + return eErr; + } + } + } + + CSLDestroy(papszTokens); + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named field. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < poLayer->GetLayerDefn()->GetFieldCount(); i++ ) + { + if( EQUAL(papszTokens[5], + poLayer->GetLayerDefn()->GetFieldDefn(i)->GetNameRef()) ) + break; + } + + CSLDestroy( papszTokens ); + + if( i >= poLayer->GetLayerDefn()->GetFieldCount() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "`%s' failed, field not found.", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Attempt to drop the index. */ +/* -------------------------------------------------------------------- */ + eErr = poLayer->GetIndex()->DropIndex( i ); + + return eErr; +} + +/************************************************************************/ +/* ProcessSQLDropTable() */ +/* */ +/* The correct syntax for dropping a table (layer) in the OGR SQL */ +/* dialect is: */ +/* */ +/* DROP TABLE */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLDropTable( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + if( CSLCount(papszTokens) != 3 + || !EQUAL(papszTokens[0],"DROP") + || !EQUAL(papszTokens[1],"TABLE") ) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in DROP TABLE command.\n" + "Was '%s'\n" + "Should be of form 'DROP TABLE
    '", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named layer. */ +/* -------------------------------------------------------------------- */ + int i; + OGRLayer *poLayer=NULL; + + for( i = 0; i < GetLayerCount(); i++ ) + { + poLayer = GetLayer(i); + + if( EQUAL(poLayer->GetName(),papszTokens[2]) ) + break; + } + + if( i >= GetLayerCount() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DROP TABLE failed, no such layer as `%s'.", + papszTokens[2] ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + + CSLDestroy( papszTokens ); + +/* -------------------------------------------------------------------- */ +/* Delete it. */ +/* -------------------------------------------------------------------- */ + + return DeleteLayer( i ); +} + +/************************************************************************/ +/* GDALDatasetParseSQLType() */ +/************************************************************************/ + +/* All arguments will be altered */ +static OGRFieldType GDALDatasetParseSQLType(char* pszType, int& nWidth, int &nPrecision) +{ + char* pszParenthesis = strchr(pszType, '('); + if (pszParenthesis) + { + nWidth = atoi(pszParenthesis + 1); + *pszParenthesis = '\0'; + char* pszComma = strchr(pszParenthesis + 1, ','); + if (pszComma) + nPrecision = atoi(pszComma + 1); + } + + OGRFieldType eType = OFTString; + if (EQUAL(pszType, "INTEGER")) + eType = OFTInteger; + else if (EQUAL(pszType, "INTEGER[]")) + eType = OFTIntegerList; + else if (EQUAL(pszType, "FLOAT") || + EQUAL(pszType, "NUMERIC") || + EQUAL(pszType, "DOUBLE") /* unofficial alias */ || + EQUAL(pszType, "REAL") /* unofficial alias */) + eType = OFTReal; + else if (EQUAL(pszType, "FLOAT[]") || + EQUAL(pszType, "NUMERIC[]") || + EQUAL(pszType, "DOUBLE[]") /* unofficial alias */ || + EQUAL(pszType, "REAL[]") /* unofficial alias */) + eType = OFTRealList; + else if (EQUAL(pszType, "CHARACTER") || + EQUAL(pszType, "TEXT") /* unofficial alias */ || + EQUAL(pszType, "STRING") /* unofficial alias */ || + EQUAL(pszType, "VARCHAR") /* unofficial alias */) + eType = OFTString; + else if (EQUAL(pszType, "TEXT[]") || + EQUAL(pszType, "STRING[]") /* unofficial alias */|| + EQUAL(pszType, "VARCHAR[]") /* unofficial alias */) + eType = OFTStringList; + else if (EQUAL(pszType, "DATE")) + eType = OFTDate; + else if (EQUAL(pszType, "TIME")) + eType = OFTTime; + else if (EQUAL(pszType, "TIMESTAMP") || + EQUAL(pszType, "DATETIME") /* unofficial alias */ ) + eType = OFTDateTime; + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unsupported column type '%s'. Defaulting to VARCHAR", + pszType); + } + return eType; +} + +/************************************************************************/ +/* ProcessSQLAlterTableAddColumn() */ +/* */ +/* The correct syntax for adding a column in the OGR SQL */ +/* dialect is: */ +/* */ +/* ALTER TABLE ADD [COLUMN] */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLAlterTableAddColumn( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + const char* pszLayerName = NULL; + const char* pszColumnName = NULL; + char* pszType = NULL; + int iTypeIndex = 0; + int nTokens = CSLCount(papszTokens); + + if( nTokens >= 7 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"ADD") + && EQUAL(papszTokens[4],"COLUMN")) + { + pszLayerName = papszTokens[2]; + pszColumnName = papszTokens[5]; + iTypeIndex = 6; + } + else if( nTokens >= 6 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"ADD")) + { + pszLayerName = papszTokens[2]; + pszColumnName = papszTokens[4]; + iTypeIndex = 5; + } + else + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in ALTER TABLE ADD COLUMN command.\n" + "Was '%s'\n" + "Should be of form 'ALTER TABLE ADD [COLUMN] '", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Merge type components into a single string if there were split */ +/* with spaces */ +/* -------------------------------------------------------------------- */ + CPLString osType; + for(int i=iTypeIndex;iCreateField( &oFieldDefn ); +} + +/************************************************************************/ +/* ProcessSQLAlterTableDropColumn() */ +/* */ +/* The correct syntax for droping a column in the OGR SQL */ +/* dialect is: */ +/* */ +/* ALTER TABLE DROP [COLUMN] */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLAlterTableDropColumn( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + const char* pszLayerName = NULL; + const char* pszColumnName = NULL; + if( CSLCount(papszTokens) == 6 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"DROP") + && EQUAL(papszTokens[4],"COLUMN")) + { + pszLayerName = papszTokens[2]; + pszColumnName = papszTokens[5]; + } + else if( CSLCount(papszTokens) == 5 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"DROP")) + { + pszLayerName = papszTokens[2]; + pszColumnName = papszTokens[4]; + } + else + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in ALTER TABLE DROP COLUMN command.\n" + "Was '%s'\n" + "Should be of form 'ALTER TABLE DROP [COLUMN] '", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named layer. */ +/* -------------------------------------------------------------------- */ + OGRLayer *poLayer = GetLayerByName(pszLayerName); + if( poLayer == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s failed, no such layer as `%s'.", + pszSQLCommand, + pszLayerName ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the field. */ +/* -------------------------------------------------------------------- */ + + int nFieldIndex = poLayer->GetLayerDefn()->GetFieldIndex(pszColumnName); + if( nFieldIndex < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s failed, no such field as `%s'.", + pszSQLCommand, + pszColumnName ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + + +/* -------------------------------------------------------------------- */ +/* Remove it. */ +/* -------------------------------------------------------------------- */ + + CSLDestroy( papszTokens ); + + return poLayer->DeleteField( nFieldIndex ); +} + +/************************************************************************/ +/* ProcessSQLAlterTableRenameColumn() */ +/* */ +/* The correct syntax for renaming a column in the OGR SQL */ +/* dialect is: */ +/* */ +/* ALTER TABLE RENAME [COLUMN] TO */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLAlterTableRenameColumn( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + const char* pszLayerName = NULL; + const char* pszOldColName = NULL; + const char* pszNewColName = NULL; + if( CSLCount(papszTokens) == 8 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"RENAME") + && EQUAL(papszTokens[4],"COLUMN") + && EQUAL(papszTokens[6],"TO")) + { + pszLayerName = papszTokens[2]; + pszOldColName = papszTokens[5]; + pszNewColName = papszTokens[7]; + } + else if( CSLCount(papszTokens) == 7 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"RENAME") + && EQUAL(papszTokens[5],"TO")) + { + pszLayerName = papszTokens[2]; + pszOldColName = papszTokens[4]; + pszNewColName = papszTokens[6]; + } + else + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in ALTER TABLE RENAME COLUMN command.\n" + "Was '%s'\n" + "Should be of form 'ALTER TABLE RENAME [COLUMN] TO '", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the named layer. */ +/* -------------------------------------------------------------------- */ + OGRLayer *poLayer = GetLayerByName(pszLayerName); + if( poLayer == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s failed, no such layer as `%s'.", + pszSQLCommand, + pszLayerName ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the field. */ +/* -------------------------------------------------------------------- */ + + int nFieldIndex = poLayer->GetLayerDefn()->GetFieldIndex(pszOldColName); + if( nFieldIndex < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s failed, no such field as `%s'.", + pszSQLCommand, + pszOldColName ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Rename column. */ +/* -------------------------------------------------------------------- */ + OGRFieldDefn* poOldFieldDefn = poLayer->GetLayerDefn()->GetFieldDefn(nFieldIndex); + OGRFieldDefn oNewFieldDefn(poOldFieldDefn); + oNewFieldDefn.SetName(pszNewColName); + + CSLDestroy( papszTokens ); + + return poLayer->AlterFieldDefn( nFieldIndex, &oNewFieldDefn, ALTER_NAME_FLAG ); +} + +/************************************************************************/ +/* ProcessSQLAlterTableAlterColumn() */ +/* */ +/* The correct syntax for altering the type of a column in the */ +/* OGR SQL dialect is: */ +/* */ +/* ALTER TABLE ALTER [COLUMN] TYPE */ +/************************************************************************/ + +OGRErr GDALDataset::ProcessSQLAlterTableAlterColumn( const char *pszSQLCommand ) + +{ + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Do some general syntax checking. */ +/* -------------------------------------------------------------------- */ + const char* pszLayerName = NULL; + const char* pszColumnName = NULL; + char* pszType = NULL; + int iTypeIndex = 0; + int nTokens = CSLCount(papszTokens); + + if( nTokens >= 8 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"ALTER") + && EQUAL(papszTokens[4],"COLUMN") + && EQUAL(papszTokens[6],"TYPE")) + { + pszLayerName = papszTokens[2]; + pszColumnName = papszTokens[5]; + iTypeIndex = 7; + } + else if( nTokens >= 7 + && EQUAL(papszTokens[0],"ALTER") + && EQUAL(papszTokens[1],"TABLE") + && EQUAL(papszTokens[3],"ALTER") + && EQUAL(papszTokens[5],"TYPE")) + { + pszLayerName = papszTokens[2]; + pszColumnName = papszTokens[4]; + iTypeIndex = 6; + } + else + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in ALTER TABLE ALTER COLUMN command.\n" + "Was '%s'\n" + "Should be of form 'ALTER TABLE ALTER [COLUMN] TYPE '", + pszSQLCommand ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Merge type components into a single string if there were split */ +/* with spaces */ +/* -------------------------------------------------------------------- */ + CPLString osType; + for(int i=iTypeIndex;iGetLayerDefn()->GetFieldIndex(pszColumnName); + if( nFieldIndex < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s failed, no such field as `%s'.", + pszSQLCommand, + pszColumnName ); + CSLDestroy( papszTokens ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Alter column. */ +/* -------------------------------------------------------------------- */ + + OGRFieldDefn* poOldFieldDefn = poLayer->GetLayerDefn()->GetFieldDefn(nFieldIndex); + OGRFieldDefn oNewFieldDefn(poOldFieldDefn); + + int nWidth = 0, nPrecision = 0; + OGRFieldType eType = GDALDatasetParseSQLType(pszType, nWidth, nPrecision); + oNewFieldDefn.SetType(eType); + oNewFieldDefn.SetWidth(nWidth); + oNewFieldDefn.SetPrecision(nPrecision); + + int nFlags = 0; + if (poOldFieldDefn->GetType() != oNewFieldDefn.GetType()) + nFlags |= ALTER_TYPE_FLAG; + if (poOldFieldDefn->GetWidth() != oNewFieldDefn.GetWidth() || + poOldFieldDefn->GetPrecision() != oNewFieldDefn.GetPrecision()) + nFlags |= ALTER_WIDTH_PRECISION_FLAG; + + CSLDestroy( papszTokens ); + + if (nFlags == 0) + return OGRERR_NONE; + else + return poLayer->AlterFieldDefn( nFieldIndex, &oNewFieldDefn, nFlags ); +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +/** + \brief Execute an SQL statement against the data store. + + The result of an SQL query is either NULL for statements that are in error, + or that have no results set, or an OGRLayer pointer representing a results + set from the query. Note that this OGRLayer is in addition to the layers + in the data store and must be destroyed with + ReleaseResultSet() before the dataset is closed + (destroyed). + + This method is the same as the C function GDALDatasetExecuteSQL() and the + deprecated OGR_DS_ExecuteSQL(). + + For more information on the SQL dialect supported internally by OGR + review the OGR SQL document. Some drivers (ie. + Oracle and PostGIS) pass the SQL directly through to the underlying RDBMS. + + Starting with OGR 1.10, the SQLITE dialect + can also be used. + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param pszStatement the SQL statement to execute. + @param poSpatialFilter geometry which represents a spatial filter. Can be NULL. + @param pszDialect allows control of the statement dialect. If set to NULL, the +OGR SQL engine will be used, except for RDBMS drivers that will use their dedicated SQL engine, +unless OGRSQL is explicitly passed as the dialect. Starting with OGR 1.10, the SQLITE dialect +can also be used. + + @return an OGRLayer containing the results of the query. Deallocate with + ReleaseResultSet(). + +*/ + +OGRLayer * GDALDataset::ExecuteSQL( const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ + return ExecuteSQL(pszStatement, poSpatialFilter, pszDialect, NULL); +} + +OGRLayer * GDALDataset::ExecuteSQL( const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect, + swq_select_parse_options* poSelectParseOptions) + +{ + swq_select *psSelectInfo = NULL; + + if( pszDialect != NULL && EQUAL(pszDialect, "SQLite") ) + { +#ifdef SQLITE_ENABLED + return OGRSQLiteExecuteSQL( this, pszStatement, poSpatialFilter, pszDialect ); +#else + CPLError(CE_Failure, CPLE_NotSupported, + "The SQLite driver needs to be compiled to support the SQLite SQL dialect"); + return NULL; +#endif + } + +/* -------------------------------------------------------------------- */ +/* Handle CREATE INDEX statements specially. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszStatement,"CREATE INDEX",12) ) + { + ProcessSQLCreateIndex( pszStatement ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Handle DROP INDEX statements specially. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszStatement,"DROP INDEX",10) ) + { + ProcessSQLDropIndex( pszStatement ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Handle DROP TABLE statements specially. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszStatement,"DROP TABLE",10) ) + { + ProcessSQLDropTable( pszStatement ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Handle ALTER TABLE statements specially. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszStatement,"ALTER TABLE",11) ) + { + char **papszTokens = CSLTokenizeString( pszStatement ); + if( CSLCount(papszTokens) >= 4 && + EQUAL(papszTokens[3],"ADD") ) + { + ProcessSQLAlterTableAddColumn( pszStatement ); + CSLDestroy(papszTokens); + return NULL; + } + else if( CSLCount(papszTokens) >= 4 && + EQUAL(papszTokens[3],"DROP") ) + { + ProcessSQLAlterTableDropColumn( pszStatement ); + CSLDestroy(papszTokens); + return NULL; + } + else if( CSLCount(papszTokens) >= 4 && + EQUAL(papszTokens[3],"RENAME") ) + { + ProcessSQLAlterTableRenameColumn( pszStatement ); + CSLDestroy(papszTokens); + return NULL; + } + else if( CSLCount(papszTokens) >= 4 && + EQUAL(papszTokens[3],"ALTER") ) + { + ProcessSQLAlterTableAlterColumn( pszStatement ); + CSLDestroy(papszTokens); + return NULL; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported ALTER TABLE command : %s", + pszStatement ); + CSLDestroy(papszTokens); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Preparse the SQL statement. */ +/* -------------------------------------------------------------------- */ + psSelectInfo = new swq_select(); + swq_custom_func_registrar* poCustomFuncRegistrar = NULL; + if( poSelectParseOptions != NULL ) + poCustomFuncRegistrar = poSelectParseOptions->poCustomFuncRegistrar; + if( psSelectInfo->preparse( pszStatement, + poCustomFuncRegistrar != NULL ) != CPLE_None ) + { + delete psSelectInfo; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* If there is no UNION ALL, build result layer. */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->poOtherSelect == NULL ) + { + return BuildLayerFromSelectInfo(psSelectInfo, + poSpatialFilter, + pszDialect, + poSelectParseOptions); + } + +/* -------------------------------------------------------------------- */ +/* Build result union layer. */ +/* -------------------------------------------------------------------- */ + int nSrcLayers = 0; + OGRLayer** papoSrcLayers = NULL; + + do + { + swq_select* psNextSelectInfo = psSelectInfo->poOtherSelect; + psSelectInfo->poOtherSelect = NULL; + + OGRLayer* poLayer = BuildLayerFromSelectInfo(psSelectInfo, + poSpatialFilter, + pszDialect, + poSelectParseOptions); + if( poLayer == NULL ) + { + /* Each source layer owns an independant select info */ + for(int i=0;ipszWHERE, + pszDialect ); + } + else + { + delete psSelectInfo; + } + DestroyParseInfo(psParseInfo); + + return poResults; +} + +/************************************************************************/ +/* DestroyParseInfo() */ +/************************************************************************/ + +void GDALDataset::DestroyParseInfo(GDALSQLParseInfo* psParseInfo ) +{ + if( psParseInfo != NULL ) + { + CPLFree( psParseInfo->sFieldList.names ); + CPLFree( psParseInfo->sFieldList.types ); + CPLFree( psParseInfo->sFieldList.table_ids ); + CPLFree( psParseInfo->sFieldList.ids ); + + /* Release the datasets we have opened with OGROpenShared() */ + /* It is safe to do that as the 'new OGRGenSQLResultsLayer' itself */ + /* has taken a reference on them, which it will release in its */ + /* destructor */ + for(int iEDS = 0; iEDS < psParseInfo->nExtraDSCount; iEDS++) + GDALClose( (GDALDatasetH)psParseInfo->papoExtraDS[iEDS] ); + CPLFree(psParseInfo->papoExtraDS); + + CPLFree(psParseInfo->pszWHERE); + + CPLFree(psParseInfo); + } +} + +/************************************************************************/ +/* BuildParseInfo() */ +/************************************************************************/ + +GDALSQLParseInfo* GDALDataset::BuildParseInfo(swq_select* psSelectInfo, + swq_select_parse_options* poSelectParseOptions) +{ + int nFIDIndex = 0; + + GDALSQLParseInfo* psParseInfo = (GDALSQLParseInfo*)CPLCalloc(1, sizeof(GDALSQLParseInfo)); + +/* -------------------------------------------------------------------- */ +/* Validate that all the source tables are recognised, count */ +/* fields. */ +/* -------------------------------------------------------------------- */ + int nFieldCount = 0, iTable, iField; + + for( iTable = 0; iTable < psSelectInfo->table_count; iTable++ ) + { + swq_table_def *psTableDef = psSelectInfo->table_defs + iTable; + OGRLayer *poSrcLayer; + GDALDataset *poTableDS = this; + + if( psTableDef->data_source != NULL ) + { + poTableDS = (GDALDataset *) + OGROpenShared( psTableDef->data_source, FALSE, NULL ); + if( poTableDS == NULL ) + { + if( strlen(CPLGetLastErrorMsg()) == 0 ) + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open secondary datasource\n" + "`%s' required by JOIN.", + psTableDef->data_source ); + + DestroyParseInfo(psParseInfo); + return NULL; + } + + /* Keep in an array to release at the end of this function */ + psParseInfo->papoExtraDS = (GDALDataset** )CPLRealloc( + psParseInfo->papoExtraDS, + sizeof(GDALDataset*) * (psParseInfo->nExtraDSCount + 1)); + psParseInfo->papoExtraDS[psParseInfo->nExtraDSCount++] = poTableDS; + } + + poSrcLayer = poTableDS->GetLayerByName( psTableDef->table_name ); + + if( poSrcLayer == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SELECT from table %s failed, no such table/featureclass.", + psTableDef->table_name ); + + DestroyParseInfo(psParseInfo); + return NULL; + } + + nFieldCount += poSrcLayer->GetLayerDefn()->GetFieldCount(); + if( iTable == 0 || (poSelectParseOptions && + poSelectParseOptions->bAddSecondaryTablesGeometryFields) ) + nFieldCount += poSrcLayer->GetLayerDefn()->GetGeomFieldCount(); + } + +/* -------------------------------------------------------------------- */ +/* Build the field list for all indicated tables. */ +/* -------------------------------------------------------------------- */ + + psParseInfo->sFieldList.table_count = psSelectInfo->table_count; + psParseInfo->sFieldList.table_defs = psSelectInfo->table_defs; + + psParseInfo->sFieldList.count = 0; + psParseInfo->sFieldList.names = (char **) CPLMalloc( sizeof(char *) * (nFieldCount+SPECIAL_FIELD_COUNT) ); + psParseInfo->sFieldList.types = (swq_field_type *) + CPLMalloc( sizeof(swq_field_type) * (nFieldCount+SPECIAL_FIELD_COUNT) ); + psParseInfo->sFieldList.table_ids = (int *) + CPLMalloc( sizeof(int) * (nFieldCount+SPECIAL_FIELD_COUNT) ); + psParseInfo->sFieldList.ids = (int *) + CPLMalloc( sizeof(int) * (nFieldCount+SPECIAL_FIELD_COUNT) ); + + for( iTable = 0; iTable < psSelectInfo->table_count; iTable++ ) + { + swq_table_def *psTableDef = psSelectInfo->table_defs + iTable; + GDALDataset *poTableDS = this; + OGRLayer *poSrcLayer; + + if( psTableDef->data_source != NULL ) + { + poTableDS = (GDALDataset *) + OGROpenShared( psTableDef->data_source, FALSE, NULL ); + CPLAssert( poTableDS != NULL ); + poTableDS->Dereference(); + } + + poSrcLayer = poTableDS->GetLayerByName( psTableDef->table_name ); + + for( iField = 0; + iField < poSrcLayer->GetLayerDefn()->GetFieldCount(); + iField++ ) + { + OGRFieldDefn *poFDefn=poSrcLayer->GetLayerDefn()->GetFieldDefn(iField); + int iOutField = psParseInfo->sFieldList.count++; + psParseInfo->sFieldList.names[iOutField] = (char *) poFDefn->GetNameRef(); + if( poFDefn->GetType() == OFTInteger ) + { + if( poFDefn->GetSubType() == OFSTBoolean ) + psParseInfo->sFieldList.types[iOutField] = SWQ_BOOLEAN; + else + psParseInfo->sFieldList.types[iOutField] = SWQ_INTEGER; + } + else if( poFDefn->GetType() == OFTInteger64 ) + { + if( poFDefn->GetSubType() == OFSTBoolean ) + psParseInfo->sFieldList.types[iOutField] = SWQ_BOOLEAN; + else + psParseInfo->sFieldList.types[iOutField] = SWQ_INTEGER64; + } + else if( poFDefn->GetType() == OFTReal ) + psParseInfo->sFieldList.types[iOutField] = SWQ_FLOAT; + else if( poFDefn->GetType() == OFTString ) + psParseInfo->sFieldList.types[iOutField] = SWQ_STRING; + else if( poFDefn->GetType() == OFTTime ) + psParseInfo->sFieldList.types[iOutField] = SWQ_TIME; + else if( poFDefn->GetType() == OFTDate ) + psParseInfo->sFieldList.types[iOutField] = SWQ_DATE; + else if( poFDefn->GetType() == OFTDateTime ) + psParseInfo->sFieldList.types[iOutField] = SWQ_TIMESTAMP; + else + psParseInfo->sFieldList.types[iOutField] = SWQ_OTHER; + + psParseInfo->sFieldList.table_ids[iOutField] = iTable; + psParseInfo->sFieldList.ids[iOutField] = iField; + } + + if( iTable == 0 || (poSelectParseOptions && + poSelectParseOptions->bAddSecondaryTablesGeometryFields) ) + { + nFIDIndex = psParseInfo->sFieldList.count; + + for( iField = 0; + iField < poSrcLayer->GetLayerDefn()->GetGeomFieldCount(); + iField++ ) + { + OGRGeomFieldDefn *poFDefn=poSrcLayer->GetLayerDefn()->GetGeomFieldDefn(iField); + int iOutField = psParseInfo->sFieldList.count++; + psParseInfo->sFieldList.names[iOutField] = (char *) poFDefn->GetNameRef(); + if( *psParseInfo->sFieldList.names[iOutField] == '\0' ) + psParseInfo->sFieldList.names[iOutField] = (char*) OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME; + psParseInfo->sFieldList.types[iOutField] = SWQ_GEOMETRY; + + psParseInfo->sFieldList.table_ids[iOutField] = iTable; + psParseInfo->sFieldList.ids[iOutField] = + GEOM_FIELD_INDEX_TO_ALL_FIELD_INDEX(poSrcLayer->GetLayerDefn(), iField); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Expand '*' in 'SELECT *' now before we add the pseudo fields */ +/* -------------------------------------------------------------------- */ + int bAlwaysPrefixWithTableName = poSelectParseOptions && + poSelectParseOptions->bAlwaysPrefixWithTableName; + if( psSelectInfo->expand_wildcard( &psParseInfo->sFieldList, + bAlwaysPrefixWithTableName) != CE_None ) + { + DestroyParseInfo(psParseInfo); + return NULL; + } + + for (iField = 0; iField < SPECIAL_FIELD_COUNT; iField++) + { + psParseInfo->sFieldList.names[psParseInfo->sFieldList.count] = (char*) SpecialFieldNames[iField]; + psParseInfo->sFieldList.types[psParseInfo->sFieldList.count] = SpecialFieldTypes[iField]; + psParseInfo->sFieldList.table_ids[psParseInfo->sFieldList.count] = 0; + psParseInfo->sFieldList.ids[psParseInfo->sFieldList.count] = nFIDIndex + iField; + psParseInfo->sFieldList.count++; + } + +/* -------------------------------------------------------------------- */ +/* Finish the parse operation. */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->parse( &psParseInfo->sFieldList, poSelectParseOptions ) != CE_None ) + { + DestroyParseInfo(psParseInfo); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Extract the WHERE expression to use separately. */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->where_expr != NULL ) + { + psParseInfo->pszWHERE = psSelectInfo->where_expr->Unparse( &psParseInfo->sFieldList, '"' ); + //CPLDebug( "OGR", "Unparse() -> %s", pszWHERE ); + } + + return psParseInfo; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +/** + \brief Release results of ExecuteSQL(). + + This method should only be used to deallocate OGRLayers resulting from + an ExecuteSQL() call on the same GDALDataset. Failure to deallocate a + results set before destroying the GDALDataset may cause errors. + + This method is the same as the C function GDALDatasetReleaseResultSet() and the + deprecated OGR_DS_ReleaseResultSet(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param poResultsSet the result of a previous ExecuteSQL() call. + +*/ +void GDALDataset::ReleaseResultSet( OGRLayer * poResultsSet ) + +{ + delete poResultsSet; +} + +/************************************************************************/ +/* GetStyleTable() */ +/************************************************************************/ + +/** + \brief Returns dataset style table. + + This method is the same as the C function GDALDatasetGetStyleTable() and the + deprecated OGR_DS_GetStyleTable(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @return pointer to a style table which should not be modified or freed by the + caller. +*/ + +OGRStyleTable *GDALDataset::GetStyleTable() +{ + return m_poStyleTable; +} + +/************************************************************************/ +/* SetStyleTableDirectly() */ +/************************************************************************/ + +/** + \brief Set dataset style table. + + This method operate exactly as SetStyleTable() except that it + assumes ownership of the passed table. + + This method is the same as the C function GDALDatasetSetStyleTableDirectly() and + the deprecated OGR_DS_SetStyleTableDirectly(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param poStyleTable pointer to style table to set + +*/ +void GDALDataset::SetStyleTableDirectly( OGRStyleTable *poStyleTable ) +{ + if ( m_poStyleTable ) + delete m_poStyleTable; + m_poStyleTable = poStyleTable; +} + +/************************************************************************/ +/* SetStyleTable() */ +/************************************************************************/ + +/** + \brief Set dataset style table. + + This method operate exactly as SetStyleTableDirectly() except + that it does not assume ownership of the passed table. + + This method is the same as the C function GDALDatasetSetStyleTable() and the + deprecated OGR_DS_SetStyleTable(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param poStyleTable pointer to style table to set + +*/ + +void GDALDataset::SetStyleTable(OGRStyleTable *poStyleTable) +{ + if ( m_poStyleTable ) + delete m_poStyleTable; + if ( poStyleTable ) + m_poStyleTable = poStyleTable->Clone(); +} + +/************************************************************************/ +/* IsGenericSQLDialect() */ +/************************************************************************/ + +int GDALDataset::IsGenericSQLDialect(const char* pszDialect) +{ + return ( pszDialect != NULL && (EQUAL(pszDialect,"OGRSQL") || + EQUAL(pszDialect,"SQLITE")) ); + +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +/** + \brief Get the number of layers in this dataset. + + This method is the same as the C function GDALDatasetGetLayerCount(), + and the deprecated OGR_DS_GetLayerCount(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @return layer count. +*/ + +int GDALDataset::GetLayerCount() +{ + return 0; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +/** + \brief Fetch a layer by index. + + The returned layer remains owned by the + GDALDataset and should not be deleted by the application. + + This method is the same as the C function GDALDatasetGetLayer() and the + deprecated OGR_DS_GetLayer(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param iLayer a layer number between 0 and GetLayerCount()-1. + + @return the layer, or NULL if iLayer is out of range or an error occurs. +*/ + +OGRLayer* GDALDataset::GetLayer(CPL_UNUSED int iLayer) +{ + return NULL; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +/** + \brief Test if capability is available. + + One of the following dataset capability names can be passed into this + method, and a TRUE or FALSE value will be returned indicating whether or not + the capability is available for this object. + +
      +
    • ODsCCreateLayer: True if this datasource can create new layers.

      +

    • ODsCDeleteLayer: True if this datasource can delete existing layers.

      +

    • ODsCCreateGeomFieldAfterCreateLayer: True if the layers of this + datasource support CreateGeomField() just after layer creation.

      +

    • ODsCCurveGeometries: True if this datasource supports curve geometries.

      +

    • ODsCTransactions: True if this datasource supports (efficient) transactions.

      +

    • ODsCEmulatedTransactions: True if this datasource supports transactions through emulation.

      +

    + + The \#define macro forms of the capability names should be used in preference + to the strings themselves to avoid mispelling. + + This method is the same as the C function GDALDatasetTestCapability() and the + deprecated OGR_DS_TestCapability(). + + In GDAL 1.X, this method used to be in the OGRDataSource class. + + @param pszCapability the capability to test. + + @return TRUE if capability available otherwise FALSE. + +*/ + +int GDALDataset::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +/** + \brief For datasources which support transactions, StartTransaction creates a transaction. + + If starting the transaction fails, will return + OGRERR_FAILURE. Datasources which do not support transactions will + always return OGRERR_UNSUPPORTED_OPERATION. + + Nested transactions are not supported. + + All changes done after the start of the transaction are definitely applied in the + datasource if CommitTransaction() is called. They may be cancelled by calling + RollbackTransaction() instead. + + At the time of writing, transactions only apply on vector layers. + + Datasets that support transactions will advertize the ODsCTransactions capability. + Use of transactions at dataset level is generally prefered to transactions at + layer level, whose scope is rarely limited to the layer from which it was started. + + In case StartTransaction() fails, neither CommitTransaction() or RollbackTransaction() + should be called. + + If an error occurs after a successful StartTransaction(), the whole + transaction may or may not be implicitely cancelled, depending on drivers. (e.g. + the PG driver will cancel it, SQLite/GPKG not). In any case, in the event of an + error, an explicit call to RollbackTransaction() should be done to keep things balanced. + + By default, when bForce is set to FALSE, only "efficient" transactions will be + attempted. Some drivers may offer an emulation of transactions, but sometimes + with significant overhead, in which case the user must explicitly allow for such + an emulation by setting bForce to TRUE. Drivers that offer emulated transactions + should advertize the ODsCEmulatedTransactions capability (and not ODsCTransactions). + + This function is the same as the C function GDALDatasetStartTransaction(). + + @param bForce can be set to TRUE if an emulation, possibly slow, of a transaction + mechanism is acceptable. + + @return OGRERR_NONE on success. + @since GDAL 2.0 +*/ +OGRErr GDALDataset::StartTransaction(CPL_UNUSED int bForce) +{ + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* GDALDatasetStartTransaction() */ +/************************************************************************/ + +/** + \brief For datasources which support transactions, StartTransaction creates a transaction. + + If starting the transaction fails, will return + OGRERR_FAILURE. Datasources which do not support transactions will + always return OGRERR_UNSUPPORTED_OPERATION. + + Nested transactions are not supported. + + All changes done after the start of the transaction are definitely applied in the + datasource if CommitTransaction() is called. They may be cancelled by calling + RollbackTransaction() instead. + + At the time of writing, transactions only apply on vector layers. + + Datasets that support transactions will advertize the ODsCTransactions capability. + Use of transactions at dataset level is generally prefered to transactions at + layer level, whose scope is rarely limited to the layer from which it was started. + + In case StartTransaction() fails, neither CommitTransaction() or RollbackTransaction() + should be called. + + If an error occurs after a successful StartTransaction(), the whole + transaction may or may not be implicitely cancelled, depending on drivers. (e.g. + the PG driver will cancel it, SQLite/GPKG not). In any case, in the event of an + error, an explicit call to RollbackTransaction() should be done to keep things balanced. + + By default, when bForce is set to FALSE, only "efficient" transactions will be + attempted. Some drivers may offer an emulation of transactions, but sometimes + with significant overhead, in which case the user must explicitly allow for such + an emulation by setting bForce to TRUE. Drivers that offer emulated transactions + should advertize the ODsCEmulatedTransactions capability (and not ODsCTransactions). + + This function is the same as the C++ method GDALDataset::StartTransaction() + + @param hDS the dataset handle. + @param bForce can be set to TRUE if an emulation, possibly slow, of a transaction + mechanism is acceptable. + + @return OGRERR_NONE on success. + @since GDAL 2.0 +*/ +OGRErr GDALDatasetStartTransaction(GDALDatasetH hDS, int bForce) +{ + VALIDATE_POINTER1( hDS, "GDALDatasetStartTransaction", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_Dataset_StartTransaction(hDS, bForce); +#endif + + return ((GDALDataset*) hDS)->StartTransaction(bForce); +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +/** + \brief For datasources which support transactions, CommitTransaction commits a transaction. + + If no transaction is active, or the commit fails, will return + OGRERR_FAILURE. Datasources which do not support transactions will + always return OGRERR_UNSUPPORTED_OPERATION. + + Depending on drivers, this may or may not abort layer sequential readings that + are active. + + This function is the same as the C function GDALDatasetCommitTransaction(). + + @return OGRERR_NONE on success. + @since GDAL 2.0 +*/ +OGRErr GDALDataset::CommitTransaction() +{ + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* GDALDatasetCommitTransaction() */ +/************************************************************************/ + +/** + \brief For datasources which support transactions, CommitTransaction commits a transaction. + + If no transaction is active, or the commit fails, will return + OGRERR_FAILURE. Datasources which do not support transactions will + always return OGRERR_UNSUPPORTED_OPERATION. + + Depending on drivers, this may or may not abort layer sequential readings that + are active. + + This function is the same as the C++ method GDALDataset::CommitTransaction() + + @return OGRERR_NONE on success. + @since GDAL 2.0 +*/ +OGRErr GDALDatasetCommitTransaction(GDALDatasetH hDS) +{ + VALIDATE_POINTER1( hDS, "GDALDatasetCommitTransaction", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_Dataset_CommitTransaction(hDS); +#endif + + return ((GDALDataset*) hDS)->CommitTransaction(); +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +/** + \brief For datasources which support transactions, RollbackTransaction will roll back a datasource to its state before the start of the current transaction. + If no transaction is active, or the rollback fails, will return + OGRERR_FAILURE. Datasources which do not support transactions will + always return OGRERR_UNSUPPORTED_OPERATION. + + This function is the same as the C function GDALDatasetRollbackTransaction(). + + @return OGRERR_NONE on success. + @since GDAL 2.0 +*/ +OGRErr GDALDataset::RollbackTransaction() +{ + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* GDALDatasetRollbackTransaction() */ +/************************************************************************/ + +/** + \brief For datasources which support transactions, RollbackTransaction will roll back a datasource to its state before the start of the current transaction. + If no transaction is active, or the rollback fails, will return + OGRERR_FAILURE. Datasources which do not support transactions will + always return OGRERR_UNSUPPORTED_OPERATION. + + This function is the same as the C++ method GDALDataset::RollbackTransaction(). + + @return OGRERR_NONE on success. + @since GDAL 2.0 +*/ +OGRErr GDALDatasetRollbackTransaction(GDALDatasetH hDS) +{ + VALIDATE_POINTER1( hDS, "GDALDatasetRollbackTransaction", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_Dataset_RollbackTransaction(hDS); +#endif + + return ((GDALDataset*) hDS)->RollbackTransaction(); +} + +/************************************************************************/ +/* EnterReadWrite() */ +/************************************************************************/ + +int GDALDataset::EnterReadWrite(GDALRWFlag eRWFlag) +{ + if( eAccess == GA_Update && (eRWFlag == GF_Write || m_hMutex != NULL) ) + { + CPLCreateOrAcquireMutex(&m_hMutex, 1000.0); + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* LeaveReadWrite() */ +/************************************************************************/ + +void GDALDataset::LeaveReadWrite() +{ + CPLReleaseMutex(m_hMutex); +} diff --git a/bazaar/plugin/gdal/gcore/gdaldefaultasync.cpp b/bazaar/plugin/gdal/gcore/gdaldefaultasync.cpp new file mode 100644 index 000000000..eef271f50 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaldefaultasync.cpp @@ -0,0 +1,318 @@ +/****************************************************************************** + * $Id: gdaldataset.cpp 16796 2009-04-17 23:35:04Z normanb $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALDefaultAsyncReader and the + * GDALAsyncReader base class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +CPL_CVSID("$Id: gdaldataset.cpp 16796 2009-04-17 23:35:04Z normanb $"); + +CPL_C_START +GDALAsyncReader * +GDALGetDefaultAsyncReader( GDALDataset* poDS, + int nXOff, int nYOff, int nXSize, int nYSize, + void *pBuf, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, char **papszOptions ); +CPL_C_END + +/************************************************************************/ +/* ==================================================================== */ +/* GDALAsyncReader */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALAsyncReader() */ +/************************************************************************/ + +GDALAsyncReader::GDALAsyncReader() +{ +} + +/************************************************************************/ +/* ~GDALAsyncReader() */ +/************************************************************************/ +GDALAsyncReader::~GDALAsyncReader() +{ +} + +/************************************************************************/ +/* GetNextUpdatedRegion() */ +/************************************************************************/ + +/** + * \fn GDALAsyncStatusType GDALAsyncReader::GetNextUpdatedRegion( double dfTimeout, int* pnBufXOff, int* pnBufYOff, int* pnBufXSize, int* pnBufXSize) = 0; + * + * \brief Get async IO update + * + * Provide an opportunity for an asynchronous IO request to update the + * image buffer and return an indication of the area of the buffer that + * has been updated. + * + * The dfTimeout parameter can be used to wait for additional data to + * become available. The timeout does not limit the amount + * of time this method may spend actually processing available data. + * + * The following return status are possible. + * - GARIO_PENDING: No imagery was altered in the buffer, but there is still + * activity pending, and the application should continue to call + * GetNextUpdatedRegion() as time permits. + * - GARIO_UPDATE: Some of the imagery has been updated, but there is still + * activity pending. + * - GARIO_ERROR: Something has gone wrong. The asynchronous request should + * be ended. + * - GARIO_COMPLETE: An update has occured and there is no more pending work + * on this request. The request should be ended and the buffer used. + * + * @param dfTimeout the number of seconds to wait for additional updates. Use + * -1 to wait indefinately, or zero to not wait at all if there is no data + * available. + * @param pnBufXOff location to return the X offset of the area of the + * request buffer that has been updated. + * @param pnBufYOff location to return the Y offset of the area of the + * request buffer that has been updated. + * @param pnBufXSize location to return the X size of the area of the + * request buffer that has been updated. + * @param pnBufYSize location to return the Y size of the area of the + * request buffer that has been updated. + * + * @return GARIO_ status, details described above. + */ + +/************************************************************************/ +/* GDALARGetNextUpdatedRegion() */ +/************************************************************************/ + +GDALAsyncStatusType CPL_STDCALL +GDALARGetNextUpdatedRegion(GDALAsyncReaderH hARIO, double timeout, + int* pnxbufoff, int* pnybufoff, + int* pnxbufsize, int* pnybufsize) +{ + VALIDATE_POINTER1(hARIO, "GDALARGetNextUpdatedRegion", GARIO_ERROR); + return ((GDALAsyncReader *)hARIO)->GetNextUpdatedRegion( + timeout, pnxbufoff, pnybufoff, pnxbufsize, pnybufsize); +} + +/************************************************************************/ +/* LockBuffer() */ +/************************************************************************/ + +/** + * \brief Lock image buffer. + * + * Locks the image buffer passed into GDALDataset::BeginAsyncReader(). + * This is useful to ensure the image buffer is not being modified while + * it is being used by the application. UnlockBuffer() should be used + * to release this lock when it is no longer needed. + * + * @param dfTimeout the time in seconds to wait attempting to lock the buffer. + * -1.0 to wait indefinately and 0 to not wait at all if it can't be + * acquired immediately. Default is -1.0 (infinite wait). + * + * @return TRUE if successful, or FALSE on an error. + */ + +int GDALAsyncReader::LockBuffer( CPL_UNUSED double dfTimeout ) +{ + return TRUE; +} + + +/************************************************************************/ +/* GDALARLockBuffer() */ +/************************************************************************/ +int CPL_STDCALL GDALARLockBuffer(GDALAsyncReaderH hARIO, double dfTimeout ) +{ + VALIDATE_POINTER1(hARIO, "GDALARLockBuffer",FALSE); + return ((GDALAsyncReader *)hARIO)->LockBuffer( dfTimeout ); +} + +/************************************************************************/ +/* UnlockBuffer() */ +/************************************************************************/ + +/** + * \brief Unlock image buffer. + * + * Releases a lock on the image buffer previously taken with LockBuffer(). + */ + +void GDALAsyncReader::UnlockBuffer() + +{ +} + +/************************************************************************/ +/* GDALARUnlockBuffer() */ +/************************************************************************/ +void CPL_STDCALL GDALARUnlockBuffer(GDALAsyncReaderH hARIO) +{ + VALIDATE_POINTER0(hARIO, "GDALARUnlockBuffer"); + ((GDALAsyncReader *)hARIO)->UnlockBuffer(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALDefaultAsyncReader */ +/* ==================================================================== */ +/************************************************************************/ + +class GDALDefaultAsyncReader : public GDALAsyncReader +{ + private: + char ** papszOptions; + + public: + GDALDefaultAsyncReader(GDALDataset* poDS, + int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, char **papszOptions); + ~GDALDefaultAsyncReader(); + + virtual GDALAsyncStatusType GetNextUpdatedRegion(double dfTimeout, + int* pnBufXOff, + int* pnBufYOff, + int* pnBufXSize, + int* pnBufYSize); +}; + +/************************************************************************/ +/* GDALGetDefaultAsyncReader() */ +/************************************************************************/ + +GDALAsyncReader * +GDALGetDefaultAsyncReader( GDALDataset* poDS, + int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, char **papszOptions) + +{ + return new GDALDefaultAsyncReader( poDS, + nXOff, nYOff, nXSize, nYSize, + pBuf, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + papszOptions ); +} + +/************************************************************************/ +/* GDALDefaultAsyncReader() */ +/************************************************************************/ + +GDALDefaultAsyncReader:: +GDALDefaultAsyncReader( GDALDataset* poDS, + int nXOff, int nYOff, + int nXSize, int nYSize, + void *pBuf, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, int nLineSpace, + int nBandSpace, char **papszOptions) + +{ + this->poDS = poDS; + this->nXOff = nXOff; + this->nYOff = nYOff; + this->nXSize = nXSize; + this->nYSize = nYSize; + this->pBuf = pBuf; + this->nBufXSize = nBufXSize; + this->nBufYSize = nBufYSize; + this->eBufType = eBufType; + this->nBandCount = nBandCount; + this->panBandMap = (int *) CPLMalloc(sizeof(int)*nBandCount); + + if( panBandMap != NULL ) + memcpy( this->panBandMap, panBandMap, sizeof(int)*nBandCount ); + else + { + for( int i = 0; i < nBandCount; i++ ) + this->panBandMap[i] = i+1; + } + + this->nPixelSpace = nPixelSpace; + this->nLineSpace = nLineSpace; + this->nBandSpace = nBandSpace; + + this->papszOptions = CSLDuplicate(papszOptions); +} + +/************************************************************************/ +/* ~GDALDefaultAsyncReader() */ +/************************************************************************/ + +GDALDefaultAsyncReader::~GDALDefaultAsyncReader() + +{ + CPLFree( panBandMap ); + CSLDestroy( papszOptions ); +} + +/************************************************************************/ +/* GetNextUpdatedRegion() */ +/************************************************************************/ + +GDALAsyncStatusType +GDALDefaultAsyncReader::GetNextUpdatedRegion(CPL_UNUSED double dfTimeout, + int* pnBufXOff, + int* pnBufYOff, + int* pnBufXSize, + int* pnBufYSize ) +{ + CPLErr eErr; + + eErr = poDS->RasterIO( GF_Read, nXOff, nYOff, nXSize, nYSize, + pBuf, nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + NULL ); + + *pnBufXOff = 0; + *pnBufYOff = 0; + *pnBufXSize = nBufXSize; + *pnBufYSize = nBufYSize; + + if( eErr == CE_None ) + return GARIO_COMPLETE; + else + return GARIO_ERROR; +} diff --git a/bazaar/plugin/gdal/gcore/gdaldefaultoverviews.cpp b/bazaar/plugin/gdal/gcore/gdaldefaultoverviews.cpp new file mode 100644 index 000000000..8a32a1709 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaldefaultoverviews.cpp @@ -0,0 +1,1125 @@ +/****************************************************************************** + * $Id: gdaldefaultoverviews.cpp 28907 2015-04-14 15:14:32Z rouault $ + * + * Project: GDAL Core + * Purpose: Helper code to implement overview and mask support for many + * drivers with no inherent format support. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, 2007, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: gdaldefaultoverviews.cpp 28907 2015-04-14 15:14:32Z rouault $"); + +/************************************************************************/ +/* GDALDefaultOverviews() */ +/************************************************************************/ + +GDALDefaultOverviews::GDALDefaultOverviews() + +{ + poDS = NULL; + poODS = NULL; + bOvrIsAux = FALSE; + + bCheckedForMask = FALSE; + bCheckedForOverviews = FALSE; + + poMaskDS = NULL; + + bOwnMaskDS = FALSE; + poBaseDS = NULL; + + papszInitSiblingFiles = NULL; + pszInitName = NULL; + bInitNameIsOVR = FALSE; +} + +/************************************************************************/ +/* ~GDALDefaultOverviews() */ +/************************************************************************/ + +GDALDefaultOverviews::~GDALDefaultOverviews() + +{ + CPLFree( pszInitName ); + CSLDestroy( papszInitSiblingFiles ); + + CloseDependentDatasets(); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int GDALDefaultOverviews::CloseDependentDatasets() +{ + int bHasDroppedRef = FALSE; + if( poODS != NULL ) + { + bHasDroppedRef = TRUE; + poODS->FlushCache(); + GDALClose( poODS ); + poODS = NULL; + } + + if( poMaskDS != NULL ) + { + if( bOwnMaskDS ) + { + bHasDroppedRef = TRUE; + poMaskDS->FlushCache(); + GDALClose( poMaskDS ); + } + poMaskDS = NULL; + } + + return bHasDroppedRef; +} + +/************************************************************************/ +/* IsInitialized() */ +/* */ +/* Returns TRUE if we are initialized. */ +/************************************************************************/ + +int GDALDefaultOverviews::IsInitialized() + +{ + OverviewScan(); + return poDS != NULL; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void GDALDefaultOverviews::Initialize( GDALDataset *poDSIn, + const char * pszBasename, + char **papszSiblingFiles, + int bNameIsOVR ) + +{ + poDS = poDSIn; + +/* -------------------------------------------------------------------- */ +/* If we were already initialized, destroy the old overview */ +/* file handle. */ +/* -------------------------------------------------------------------- */ + if( poODS != NULL ) + { + GDALClose( poODS ); + poODS = NULL; + + CPLDebug( "GDAL", "GDALDefaultOverviews::Initialize() called twice - this is odd and perhaps dangerous!" ); + } + +/* -------------------------------------------------------------------- */ +/* Store the initialization information for later use in */ +/* OverviewScan() */ +/* -------------------------------------------------------------------- */ + bCheckedForOverviews = FALSE; + + CPLFree( pszInitName ); + pszInitName = NULL; + if( pszBasename != NULL ) + pszInitName = CPLStrdup(pszBasename); + bInitNameIsOVR = bNameIsOVR; + + CSLDestroy( papszInitSiblingFiles ); + papszInitSiblingFiles = NULL; + if( papszSiblingFiles != NULL ) + papszInitSiblingFiles = CSLDuplicate(papszSiblingFiles); +} + +/************************************************************************/ +/* OverviewScan() */ +/* */ +/* This is called to scan for overview files when a first */ +/* request is made with regard to overviews. It uses the */ +/* pszInitName, bInitNameIsOVR and papszInitSiblingFiles */ +/* information that was stored at Initialization() time. */ +/************************************************************************/ + +void GDALDefaultOverviews::OverviewScan() + +{ + if( bCheckedForOverviews || poDS == NULL ) + return; + + bCheckedForOverviews = true; + + CPLDebug( "GDAL", "GDALDefaultOverviews::OverviewScan()" ); + +/* -------------------------------------------------------------------- */ +/* Open overview dataset if it exists. */ +/* -------------------------------------------------------------------- */ + int bExists; + + if( pszInitName == NULL ) + pszInitName = CPLStrdup(poDS->GetDescription()); + + if( !EQUAL(pszInitName,":::VIRTUAL:::") ) + { + if( bInitNameIsOVR ) + osOvrFilename = pszInitName; + else + { + if( !GDALCanFileAcceptSidecarFile(pszInitName) ) + return; + osOvrFilename.Printf( "%s.ovr", pszInitName ); + } + + bExists = CPLCheckForFile( (char *) osOvrFilename.c_str(), + papszInitSiblingFiles ); + +#if !defined(WIN32) + if( !bInitNameIsOVR && !bExists && !papszInitSiblingFiles ) + { + osOvrFilename.Printf( "%s.OVR", pszInitName ); + bExists = CPLCheckForFile( (char *) osOvrFilename.c_str(), + papszInitSiblingFiles ); + if( !bExists ) + osOvrFilename.Printf( "%s.ovr", pszInitName ); + } +#endif + + if( bExists ) + { + poODS = (GDALDataset*) GDALOpenEx( osOvrFilename, + GDAL_OF_RASTER | + ((poDS->GetAccess() == GA_Update) ? GDAL_OF_UPDATE : 0), + NULL, NULL, papszInitSiblingFiles ); + } + } + +/* -------------------------------------------------------------------- */ +/* We didn't find that, so try and find a corresponding aux */ +/* file. Check that we are the dependent file of the aux */ +/* file. */ +/* */ +/* We only use the .aux file for overviews if they already have */ +/* overviews existing, or if USE_RRD is set true. */ +/* -------------------------------------------------------------------- */ + if( !poODS && !EQUAL(pszInitName,":::VIRTUAL:::") ) + { + int bTryFindAssociatedAuxFile = TRUE; + if( papszInitSiblingFiles ) + { + CPLString osAuxFilename = CPLResetExtension( pszInitName, "aux"); + int iSibling = CSLFindString( papszInitSiblingFiles, + CPLGetFilename(osAuxFilename) ); + if( iSibling < 0 ) + { + osAuxFilename = pszInitName; + osAuxFilename += ".aux"; + iSibling = CSLFindString( papszInitSiblingFiles, + CPLGetFilename(osAuxFilename) ); + if( iSibling < 0 ) + bTryFindAssociatedAuxFile = FALSE; + } + } + + if (bTryFindAssociatedAuxFile) + { + poODS = GDALFindAssociatedAuxFile( pszInitName, poDS->GetAccess(), + poDS ); + } + + if( poODS ) + { + int bUseRRD = CSLTestBoolean(CPLGetConfigOption("USE_RRD","NO")); + + bOvrIsAux = TRUE; + if( GetOverviewCount(1) == 0 && !bUseRRD ) + { + bOvrIsAux = FALSE; + GDALClose( poODS ); + poODS = NULL; + } + else + { + osOvrFilename = poODS->GetDescription(); + } + } + } + +/* -------------------------------------------------------------------- */ +/* If we still don't have an overview, check to see if we have */ +/* overview metadata referencing a remote (ie. proxy) or local */ +/* subdataset overview dataset. */ +/* -------------------------------------------------------------------- */ + if( poODS == NULL ) + { + const char *pszProxyOvrFilename = + poDS->GetMetadataItem( "OVERVIEW_FILE", "OVERVIEWS" ); + + if( pszProxyOvrFilename != NULL ) + { + if( EQUALN(pszProxyOvrFilename,":::BASE:::",10) ) + { + CPLString osPath = CPLGetPath(poDS->GetDescription()); + + osOvrFilename = + CPLFormFilename( osPath, pszProxyOvrFilename+10, NULL ); + } + else + osOvrFilename = pszProxyOvrFilename; + + CPLPushErrorHandler(CPLQuietErrorHandler); + poODS = (GDALDataset *) GDALOpen(osOvrFilename,poDS->GetAccess()); + CPLPopErrorHandler(); + } + } + +/* -------------------------------------------------------------------- */ +/* If we have an overview dataset, then mark all the overviews */ +/* with the base dataset Used later for finding overviews */ +/* masks. Uggg. */ +/* -------------------------------------------------------------------- */ + if( poODS ) + { + int nOverviewCount = GetOverviewCount(1); + int iOver; + + for( iOver = 0; iOver < nOverviewCount; iOver++ ) + { + GDALRasterBand *poBand = GetOverview( 1, iOver ); + GDALDataset *poOverDS = NULL; + + if( poBand != NULL ) + poOverDS = poBand->GetDataset(); + + if( poOverDS != NULL ) + { + poOverDS->oOvManager.poBaseDS = poDS; + poOverDS->oOvManager.poDS = poOverDS; + } + } + } +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int GDALDefaultOverviews::GetOverviewCount( int nBand ) + +{ + GDALRasterBand * poBand; + + if( poODS == NULL || nBand < 1 || nBand > poODS->GetRasterCount() ) + return 0; + + poBand = poODS->GetRasterBand( nBand ); + if( poBand == NULL ) + return 0; + else + { + if( bOvrIsAux ) + return poBand->GetOverviewCount(); + else + return poBand->GetOverviewCount() + 1; + } +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand * +GDALDefaultOverviews::GetOverview( int nBand, int iOverview ) + +{ + GDALRasterBand * poBand; + + if( poODS == NULL || nBand < 1 || nBand > poODS->GetRasterCount() ) + return NULL; + + poBand = poODS->GetRasterBand( nBand ); + if( poBand == NULL ) + return NULL; + + if( bOvrIsAux ) + return poBand->GetOverview( iOverview ); + + else // TIFF case, base is overview 0. + { + if( iOverview == 0 ) + return poBand; + else if( iOverview-1 >= poBand->GetOverviewCount() ) + return NULL; + else + return poBand->GetOverview( iOverview-1 ); + } +} + +/************************************************************************/ +/* GDALOvLevelAdjust() */ +/* */ +/* Some overview levels cannot be achieved closely enough to be */ +/* recognised as the desired overview level. This function */ +/* will adjust an overview level to one that is achievable on */ +/* the given raster size. */ +/* */ +/* For instance a 1200x1200 image on which a 256 level overview */ +/* is request will end up generating a 5x5 overview. However, */ +/* this will appear to the system be a level 240 overview. */ +/* This function will adjust 256 to 240 based on knowledge of */ +/* the image size. */ +/************************************************************************/ + +int GDALOvLevelAdjust( int nOvLevel, int nXSize ) + +{ + int nOXSize = (nXSize + nOvLevel - 1) / nOvLevel; + + return (int) (0.5 + nXSize / (double) nOXSize); +} + + +int GDALOvLevelAdjust2( int nOvLevel, int nXSize, int nYSize ) + +{ + /* Select the larger dimension to have increased accuracy, but */ + /* with a slight preference to x even if (a bit) smaller than y */ + /* in an attempt to behave closer as previous behaviour */ + if( nXSize >= nYSize / 2 && !(nXSize < nYSize && nXSize < nOvLevel) ) + { + int nOXSize = (nXSize + nOvLevel - 1) / nOvLevel; + + return (int) (0.5 + nXSize / (double) nOXSize); + } + else + { + int nOYSize = (nYSize + nOvLevel - 1) / nOvLevel; + + return (int) (0.5 + nYSize / (double) nOYSize); + } +} + +/************************************************************************/ +/* GDALComputeOvFactor() */ +/************************************************************************/ + +int GDALComputeOvFactor( int nOvrXSize, int nRasterXSize, + int nOvrYSize, int nRasterYSize ) +{ + /* Select the larger dimension to have increased accuracy, but */ + /* with a slight preference to x even if (a bit) smaller than y */ + /* in an attempt to behave closer as previous behaviour */ + if( nRasterXSize >= nRasterYSize / 2 ) + { + return (int) + (0.5 + nRasterXSize / (double) nOvrXSize); + } + else + { + return (int) + (0.5 + nRasterYSize / (double) nOvrYSize); + } +} + +/************************************************************************/ +/* CleanOverviews() */ +/* */ +/* Remove all existing overviews. */ +/************************************************************************/ + +CPLErr GDALDefaultOverviews::CleanOverviews() + +{ + // Anything to do? + if( poODS == NULL ) + return CE_None; + + // Delete the overview file(s). + GDALDriver *poOvrDriver; + + poOvrDriver = poODS->GetDriver(); + GDALClose( poODS ); + poODS = NULL; + + CPLErr eErr; + if( poOvrDriver != NULL ) + eErr = poOvrDriver->Delete( osOvrFilename ); + else + eErr = CE_None; + + // Reset the saved overview filename. + if( !EQUAL(poDS->GetDescription(),":::VIRTUAL:::") ) + { + int bUseRRD = CSLTestBoolean(CPLGetConfigOption("USE_RRD","NO")); + + if( bUseRRD ) + osOvrFilename = CPLResetExtension( poDS->GetDescription(), "aux" ); + else + osOvrFilename.Printf( "%s.ovr", poDS->GetDescription() ); + } + else + osOvrFilename = ""; + + return eErr; +} + +/************************************************************************/ +/* BuildOverviewsSubDataset() */ +/************************************************************************/ + +CPLErr +GDALDefaultOverviews::BuildOverviewsSubDataset( + const char * pszPhysicalFile, + const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, void * pProgressData) + +{ + if( osOvrFilename.length() == 0 && nOverviews > 0 ) + { + int iSequence = 0; + VSIStatBufL sStatBuf; + + for( iSequence = 0; iSequence < 100; iSequence++ ) + { + osOvrFilename.Printf( "%s_%d.ovr", pszPhysicalFile, iSequence ); + if( VSIStatExL( osOvrFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG ) != 0 ) + { + CPLString osAdjustedOvrFilename; + + if( poDS->GetMOFlags() & GMO_PAM_CLASS ) + { + osAdjustedOvrFilename.Printf( ":::BASE:::%s_%d.ovr", + CPLGetFilename(pszPhysicalFile), + iSequence ); + } + else + osAdjustedOvrFilename = osOvrFilename; + + poDS->SetMetadataItem( "OVERVIEW_FILE", + osAdjustedOvrFilename, + "OVERVIEWS" ); + break; + } + } + + if( iSequence == 100 ) + osOvrFilename = ""; + } + + return BuildOverviews( NULL, pszResampling, nOverviews, panOverviewList, + nBands, panBandList, pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* BuildOverviews() */ +/************************************************************************/ + +CPLErr +GDALDefaultOverviews::BuildOverviews( + const char * pszBasename, + const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBands, int * panBandList, + GDALProgressFunc pfnProgress, void * pProgressData) + +{ + GDALRasterBand **pahBands; + CPLErr eErr; + int i; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + if( nOverviews == 0 ) + return CleanOverviews(); + +/* -------------------------------------------------------------------- */ +/* If we don't already have an overview file, we need to decide */ +/* what format to use. */ +/* -------------------------------------------------------------------- */ + if( poODS == NULL ) + { + bOvrIsAux = CSLTestBoolean(CPLGetConfigOption( "USE_RRD", "NO" )); + if( bOvrIsAux ) + { + VSIStatBufL sStatBuf; + + osOvrFilename = CPLResetExtension(poDS->GetDescription(),"aux"); + + if( VSIStatExL( osOvrFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + osOvrFilename.Printf( "%s.aux", poDS->GetDescription() ); + } + } +/* -------------------------------------------------------------------- */ +/* If we already have the overviews open, but they are */ +/* read-only, then try and reopen them read-write. */ +/* -------------------------------------------------------------------- */ + else if( poODS->GetAccess() == GA_ReadOnly ) + { + GDALClose( poODS ); + poODS = (GDALDataset *) GDALOpen( osOvrFilename, GA_Update ); + if( poODS == NULL ) + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Our TIFF overview support currently only works safely if all */ +/* bands are handled at the same time. */ +/* -------------------------------------------------------------------- */ + if( !bOvrIsAux && nBands != poDS->GetRasterCount() ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Generation of overviews in external TIFF currently only" + " supported when operating on all bands.\n" + "Operation failed.\n" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* If a basename is provided, use it to override the internal */ +/* overview filename. */ +/* -------------------------------------------------------------------- */ + if( pszBasename == NULL && osOvrFilename.length() == 0 ) + pszBasename = poDS->GetDescription(); + + if( pszBasename != NULL ) + { + if( bOvrIsAux ) + osOvrFilename.Printf( "%s.aux", pszBasename ); + else + osOvrFilename.Printf( "%s.ovr", pszBasename ); + } + +/* -------------------------------------------------------------------- */ +/* Establish which of the overview levels we already have, and */ +/* which are new. We assume that band 1 of the file is */ +/* representative. */ +/* -------------------------------------------------------------------- */ + int nNewOverviews, *panNewOverviewList = NULL; + GDALRasterBand *poBand = poDS->GetRasterBand( 1 ); + + nNewOverviews = 0; + panNewOverviewList = (int *) CPLCalloc(sizeof(int),nOverviews); + for( i = 0; i < nOverviews && poBand != NULL; i++ ) + { + int j; + + for( j = 0; j < poBand->GetOverviewCount(); j++ ) + { + int nOvFactor; + GDALRasterBand * poOverview = poBand->GetOverview( j ); + if (poOverview == NULL) + continue; + + nOvFactor = GDALComputeOvFactor(poOverview->GetXSize(), + poBand->GetXSize(), + poOverview->GetYSize(), + poBand->GetYSize()); + + if( nOvFactor == panOverviewList[i] + || nOvFactor == GDALOvLevelAdjust2( panOverviewList[i], + poBand->GetXSize(), + poBand->GetYSize() ) ) + panOverviewList[i] *= -1; + } + + if( panOverviewList[i] > 0 ) + panNewOverviewList[nNewOverviews++] = panOverviewList[i]; + } + +/* -------------------------------------------------------------------- */ +/* Build band list. */ +/* -------------------------------------------------------------------- */ + pahBands = (GDALRasterBand **) CPLCalloc(sizeof(GDALRasterBand *),nBands); + for( i = 0; i < nBands; i++ ) + pahBands[i] = poDS->GetRasterBand( panBandList[i] ); + +/* -------------------------------------------------------------------- */ +/* Build new overviews - Imagine. Keep existing file open if */ +/* we have it. But mark all overviews as in need of */ +/* regeneration, since HFAAuxBuildOverviews() doesn't actually */ +/* produce the imagery. */ +/* -------------------------------------------------------------------- */ + +#ifndef WIN32CE + + if( bOvrIsAux ) + { + if( nNewOverviews == 0 ) + { + /* if we call HFAAuxBuildOverviews() with nNewOverviews == 0 */ + /* because that there's no new, this will wipe existing */ + /* overviews (#4831) */ + eErr = CE_None; + } + else + eErr = HFAAuxBuildOverviews( osOvrFilename, poDS, &poODS, + nBands, panBandList, + nNewOverviews, panNewOverviewList, + pszResampling, + pfnProgress, pProgressData ); + + int j; + + for( j = 0; j < nOverviews; j++ ) + { + if( panOverviewList[j] > 0 ) + panOverviewList[j] *= -1; + } + } + +/* -------------------------------------------------------------------- */ +/* Build new overviews - TIFF. Close TIFF files while we */ +/* operate on it. */ +/* -------------------------------------------------------------------- */ + else +#endif /* WIN32CE */ + { + if( poODS != NULL ) + { + delete poODS; + poODS = NULL; + } + + eErr = GTIFFBuildOverviews( osOvrFilename, nBands, pahBands, + nNewOverviews, panNewOverviewList, + pszResampling, pfnProgress, pProgressData ); + + // Probe for proxy overview filename. + if( eErr == CE_Failure ) + { + const char *pszProxyOvrFilename = + poDS->GetMetadataItem("FILENAME","ProxyOverviewRequest"); + + if( pszProxyOvrFilename != NULL ) + { + osOvrFilename = pszProxyOvrFilename; + eErr = GTIFFBuildOverviews( osOvrFilename, nBands, pahBands, + nNewOverviews, panNewOverviewList, + pszResampling, + pfnProgress, pProgressData ); + } + } + + if( eErr == CE_None ) + { + poODS = (GDALDataset *) GDALOpen( osOvrFilename, GA_Update ); + if( poODS == NULL ) + eErr = CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Refresh old overviews that were listed. */ +/* -------------------------------------------------------------------- */ + GDALRasterBand **papoOverviewBands; + + papoOverviewBands = (GDALRasterBand **) + CPLCalloc(sizeof(void*),nOverviews); + + for( int iBand = 0; iBand < nBands && eErr == CE_None; iBand++ ) + { + poBand = poDS->GetRasterBand( panBandList[iBand] ); + + nNewOverviews = 0; + for( i = 0; i < nOverviews && poBand != NULL; i++ ) + { + int j; + + for( j = 0; j < poBand->GetOverviewCount(); j++ ) + { + int nOvFactor; + GDALRasterBand * poOverview = poBand->GetOverview( j ); + if (poOverview == NULL) + continue; + + int bHasNoData; + double noDataValue = poBand->GetNoDataValue(&bHasNoData); + + if (bHasNoData) + poOverview->SetNoDataValue(noDataValue); + + nOvFactor = GDALComputeOvFactor(poOverview->GetXSize(), + poBand->GetXSize(), + poOverview->GetYSize(), + poBand->GetYSize()); + + if( nOvFactor == - panOverviewList[i] + || (panOverviewList[i] < 0 && + nOvFactor == GDALOvLevelAdjust2( -panOverviewList[i], + poBand->GetXSize(), + poBand->GetYSize() )) ) + { + papoOverviewBands[nNewOverviews++] = poOverview; + break; + } + } + } + + if( nNewOverviews > 0 ) + { + eErr = GDALRegenerateOverviews( (GDALRasterBandH) poBand, + nNewOverviews, + (GDALRasterBandH*)papoOverviewBands, + pszResampling, + pfnProgress, pProgressData ); + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( papoOverviewBands ); + CPLFree( panNewOverviewList ); + CPLFree( pahBands ); + +/* -------------------------------------------------------------------- */ +/* If we have a mask file, we need to build it's overviews */ +/* too. */ +/* -------------------------------------------------------------------- */ + if( HaveMaskFile() && poMaskDS ) + { + /* Some config option are not compatible with mask overviews */ + /* so unset them, and define more sensible values */ + int bJPEG = EQUAL(CPLGetConfigOption("COMPRESS_OVERVIEW", ""), "JPEG"); + int bPHOTOMETRIC_YCBCR = EQUAL(CPLGetConfigOption("PHOTOMETRIC_OVERVIEW", ""), "YCBCR"); + if( bJPEG ) + CPLSetThreadLocalConfigOption("COMPRESS_OVERVIEW", "DEFLATE"); + if( bPHOTOMETRIC_YCBCR ) + CPLSetThreadLocalConfigOption("PHOTOMETRIC_OVERVIEW", ""); + + poMaskDS->BuildOverviews( pszResampling, nOverviews, panOverviewList, + 0, NULL, pfnProgress, pProgressData ); + + /* Restore config option */ + if( bJPEG ) + CPLSetThreadLocalConfigOption("COMPRESS_OVERVIEW", "JPEG"); + if( bPHOTOMETRIC_YCBCR ) + CPLSetThreadLocalConfigOption("PHOTOMETRIC_OVERVIEW", "YCBCR"); + + if( bOwnMaskDS ) + { + /* Reset the poMask member of main dataset bands, since it */ + /* will become invalid after poMaskDS closing */ + for( int iBand = 1; iBand <= poDS->GetRasterCount(); iBand ++ ) + { + GDALRasterBand *poBand = poDS->GetRasterBand(iBand); + if( poBand != NULL ) + poBand->InvalidateMaskBand(); + } + + GDALClose( poMaskDS ); + } + + // force next request to reread mask file. + poMaskDS = NULL; + bOwnMaskDS = FALSE; + bCheckedForMask = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If we have an overview dataset, then mark all the overviews */ +/* with the base dataset Used later for finding overviews */ +/* masks. Uggg. */ +/* -------------------------------------------------------------------- */ + if( poODS ) + { + int nOverviewCount = GetOverviewCount(1); + int iOver; + + for( iOver = 0; iOver < nOverviewCount; iOver++ ) + { + GDALRasterBand *poBand = GetOverview( 1, iOver ); + GDALDataset *poOverDS = NULL; + + if( poBand != NULL ) + poOverDS = poBand->GetDataset(); + + if (poOverDS != NULL) + { + poOverDS->oOvManager.poBaseDS = poDS; + poOverDS->oOvManager.poDS = poOverDS; + } + } + } + + return eErr; +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +CPLErr GDALDefaultOverviews::CreateMaskBand( int nFlags, int nBand ) + +{ + if( nBand < 1 ) + nFlags |= GMF_PER_DATASET; + +/* -------------------------------------------------------------------- */ +/* ensure existing file gets opened if there is one. */ +/* -------------------------------------------------------------------- */ + HaveMaskFile(); + +/* -------------------------------------------------------------------- */ +/* Try creating the mask file. */ +/* -------------------------------------------------------------------- */ + if( poMaskDS == NULL ) + { + CPLString osMskFilename; + GDALDriver *poDr = (GDALDriver *) GDALGetDriverByName( "GTiff" ); + char **papszOpt = NULL; + int nBX, nBY; + int nBands; + + if( poDr == NULL ) + return CE_Failure; + + GDALRasterBand *poTBand = poDS->GetRasterBand(1); + if( poTBand == NULL ) + return CE_Failure; + + if( nFlags & GMF_PER_DATASET ) + nBands = 1; + else + nBands = poDS->GetRasterCount(); + + + papszOpt = CSLSetNameValue( papszOpt, "COMPRESS", "DEFLATE" ); + papszOpt = CSLSetNameValue( papszOpt, "INTERLEAVE", "BAND" ); + + poTBand->GetBlockSize( &nBX, &nBY ); + + // try to create matching tile size if legal in TIFF. + if( (nBX % 16) == 0 && (nBY % 16) == 0 ) + { + papszOpt = CSLSetNameValue( papszOpt, "TILED", "YES" ); + papszOpt = CSLSetNameValue( papszOpt, "BLOCKXSIZE", + CPLString().Printf("%d",nBX) ); + papszOpt = CSLSetNameValue( papszOpt, "BLOCKYSIZE", + CPLString().Printf("%d",nBY) ); + } + + osMskFilename.Printf( "%s.msk", poDS->GetDescription() ); + poMaskDS = poDr->Create( osMskFilename, + poDS->GetRasterXSize(), + poDS->GetRasterYSize(), + nBands, GDT_Byte, papszOpt ); + CSLDestroy( papszOpt ); + + if( poMaskDS == NULL ) // presumably error already issued. + return CE_Failure; + + bOwnMaskDS = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Save the mask flags for this band. */ +/* -------------------------------------------------------------------- */ + if( nBand > poMaskDS->GetRasterCount() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create a mask band for band %d of %s,\n" + "but the .msk file has a PER_DATASET mask.", + nBand, poDS->GetDescription() ); + return CE_Failure; + } + + int iBand; + + for( iBand = 0; iBand < poDS->GetRasterCount(); iBand++ ) + { + // we write only the info for this band, unless we are + // using PER_DATASET in which case we write for all. + if( nBand != iBand + 1 && !(nFlags | GMF_PER_DATASET) ) + continue; + + poMaskDS->SetMetadataItem( + CPLString().Printf("INTERNAL_MASK_FLAGS_%d", iBand+1 ), + CPLString().Printf("%d", nFlags ) ); + } + + return CE_None; +} + +/************************************************************************/ +/* GetMaskBand() */ +/************************************************************************/ + +GDALRasterBand *GDALDefaultOverviews::GetMaskBand( int nBand ) + +{ + int nFlags = GetMaskFlags( nBand ); + + if( nFlags == 0x8000 ) // secret code meaning we don't handle this band. + return NULL; + + if( nFlags & GMF_PER_DATASET ) + return poMaskDS->GetRasterBand(1); + + if( nBand > 0 ) + return poMaskDS->GetRasterBand( nBand ); + else + return NULL; +} + +/************************************************************************/ +/* GetMaskFlags() */ +/************************************************************************/ + +int GDALDefaultOverviews::GetMaskFlags( int nBand ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch this band's metadata entry. They are of the form: */ +/* INTERNAL_MASK_FLAGS_n: flags */ +/* -------------------------------------------------------------------- */ + if( !HaveMaskFile() ) + return 0; + + const char *pszValue = + poMaskDS->GetMetadataItem( + CPLString().Printf( "INTERNAL_MASK_FLAGS_%d", MAX(nBand,1)) ); + + if( pszValue == NULL ) + return 0x8000; + else + return atoi(pszValue); +} + +/************************************************************************/ +/* HaveMaskFile() */ +/* */ +/* Check for a mask file if we haven't already done so. */ +/* Returns TRUE if we have one, otherwise FALSE. */ +/************************************************************************/ + +int GDALDefaultOverviews::HaveMaskFile( char ** papszSiblingFiles, + const char *pszBasename ) + +{ +/* -------------------------------------------------------------------- */ +/* Have we already checked for masks? */ +/* -------------------------------------------------------------------- */ + if( bCheckedForMask ) + return poMaskDS != NULL; + + if( papszSiblingFiles == NULL ) + papszSiblingFiles = papszInitSiblingFiles; + +/* -------------------------------------------------------------------- */ +/* Are we an overview? If so we need to find the corresponding */ +/* overview in the base files mask file (if there is one). */ +/* -------------------------------------------------------------------- */ + if( poBaseDS != NULL && poBaseDS->oOvManager.HaveMaskFile() ) + { + int iOver, nOverviewCount = 0; + GDALRasterBand *poBaseBand = poBaseDS->GetRasterBand(1); + GDALRasterBand *poBaseMask = NULL; + + if( poBaseBand != NULL ) + poBaseMask = poBaseBand->GetMaskBand(); + if( poBaseMask ) + nOverviewCount = poBaseMask->GetOverviewCount(); + + for( iOver = 0; iOver < nOverviewCount; iOver++ ) + { + GDALRasterBand *poOverBand = poBaseMask->GetOverview( iOver ); + if (poOverBand == NULL) + continue; + + if( poOverBand->GetXSize() == poDS->GetRasterXSize() + && poOverBand->GetYSize() == poDS->GetRasterYSize() ) + { + poMaskDS = poOverBand->GetDataset(); + break; + } + } + + bCheckedForMask = TRUE; + bOwnMaskDS = FALSE; + + CPLAssert( poMaskDS != poDS ); + + return poMaskDS != NULL; + } + +/* -------------------------------------------------------------------- */ +/* Are we even initialized? If not, we apparently don't want */ +/* to support overviews and masks. */ +/* -------------------------------------------------------------------- */ + if( poDS == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Check for .msk file. */ +/* -------------------------------------------------------------------- */ + CPLString osMskFilename; + bCheckedForMask = TRUE; + + if( pszBasename == NULL ) + pszBasename = poDS->GetDescription(); + + // Don't bother checking for masks of masks. + if( EQUAL(CPLGetExtension(pszBasename),"msk") ) + return FALSE; + + if( !GDALCanFileAcceptSidecarFile(pszBasename) ) + return FALSE; + osMskFilename.Printf( "%s.msk", pszBasename ); + + int bExists = CPLCheckForFile( (char *) osMskFilename.c_str(), + papszSiblingFiles ); + +#if !defined(WIN32) + if( !bExists && !papszSiblingFiles ) + { + osMskFilename.Printf( "%s.MSK", pszBasename ); + bExists = CPLCheckForFile( (char *) osMskFilename.c_str(), + papszSiblingFiles ); + } +#endif + + if( !bExists ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + poMaskDS = (GDALDataset *) GDALOpenEx( osMskFilename, + GDAL_OF_RASTER | + ((poDS->GetAccess() == GA_Update) ? GDAL_OF_UPDATE : 0), + NULL, NULL, papszInitSiblingFiles ); + CPLAssert( poMaskDS != poDS ); + + if( poMaskDS == NULL ) + return FALSE; + + bOwnMaskDS = TRUE; + + return TRUE; +} diff --git a/bazaar/plugin/gdal/gcore/gdaldllmain.cpp b/bazaar/plugin/gdal/gcore/gdaldllmain.cpp new file mode 100644 index 000000000..19309d8d8 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaldllmain.cpp @@ -0,0 +1,164 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL Core + * Purpose: The library set-up/clean-up routines. + * Author: Mateusz Loskot + * + ****************************************************************************** + * Copyright (c) 2010, Mateusz Loskot + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal.h" +#include "ogr_api.h" +#include "cpl_multiproc.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +static int bInGDALGlobalDestructor = FALSE; +extern "C" int CPL_DLL GDALIsInGlobalDestructor(void); + +int GDALIsInGlobalDestructor(void) +{ + return bInGDALGlobalDestructor; +} + +#ifndef _MSC_VER +void CPLFinalizeTLS(); +#endif + +/************************************************************************/ +/* GDALDestroy() */ +/************************************************************************/ + +/** Finalize GDAL/OGR library. + * + * This function calls GDALDestroyDriverManager() and OGRCleanupAll() and + * finalize Thread Local Storage variables. + * + * This function should *not* usually be explicitly called by application code + * if GDAL is dynamically linked, since it is automatically called through + * the unregistration mechanisms of dynamic library loading. + * + * Note: no GDAL/OGR code should be called after this call ! + * + * @since GDAL 2.0 + */ + +static int bGDALDestroyAlreadyCalled = FALSE; +void GDALDestroy(void) +{ + if( bGDALDestroyAlreadyCalled ) + return; + bGDALDestroyAlreadyCalled = TRUE; + + CPLDebug("GDAL", "In GDALDestroy - unloading GDAL shared library."); + bInGDALGlobalDestructor = TRUE; + GDALDestroyDriverManager(); + +#ifdef OGR_ENABLED + OGRCleanupAll(); +#endif + bInGDALGlobalDestructor = FALSE; +#ifndef _MSC_VER + CPLFinalizeTLS(); +#endif +} + +/************************************************************************/ +/* The library set-up/clean-up routines implemented with */ +/* GNU C/C++ extensions. */ +/* TODO: Is it Linux-only solution or Unix portable? */ +/************************************************************************/ +#ifdef __GNUC__ + +static void GDALInitialize(void) __attribute__ ((constructor)) ; +static void GDALDestructor(void) __attribute__ ((destructor)) ; + +/************************************************************************/ +/* Called when GDAL is loaded by loader or by dlopen(), */ +/* and before dlopen() returns. */ +/************************************************************************/ + +static void GDALInitialize(void) +{ + // nothing to do + //CPLDebug("GDAL", "Library loaded"); +#ifdef DEBUG + const char* pszLocale = CPLGetConfigOption("GDAL_LOCALE", NULL); + if( pszLocale ) + CPLsetlocale( LC_ALL, pszLocale ); +#endif +} + +/************************************************************************/ +/* Called when GDAL is unloaded by loader or by dlclose(), */ +/* and before dlclose() returns. */ +/************************************************************************/ + +static void GDALDestructor(void) +{ + if( bGDALDestroyAlreadyCalled ) + return; + if( !CSLTestBoolean(CPLGetConfigOption("GDAL_DESTROY", "YES")) ) + return; + GDALDestroy(); +} + +#endif // __GNUC__ + + +/************************************************************************/ +/* The library set-up/clean-up routine implemented as DllMain entry */ +/* point specific for Windows. */ +/************************************************************************/ +#ifdef _MSC_VER + +#include + +extern "C" int WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + UNREFERENCED_PARAMETER(hInstance); + UNREFERENCED_PARAMETER(lpReserved); + + if (dwReason == DLL_PROCESS_ATTACH) + { + // nothing to do + } + else if (dwReason == DLL_THREAD_ATTACH) + { + // nothing to do + } + else if (dwReason == DLL_THREAD_DETACH) + { + ::CPLCleanupTLS(); + } + else if (dwReason == DLL_PROCESS_DETACH) + { + GDALDestroy(); + } + + return 1; // ignroed for all reasons but DLL_PROCESS_ATTACH +} + +#endif // _MSC_VER + diff --git a/bazaar/plugin/gdal/gcore/gdaldriver.cpp b/bazaar/plugin/gdal/gcore/gdaldriver.cpp new file mode 100644 index 000000000..5b60d5114 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaldriver.cpp @@ -0,0 +1,1902 @@ +/****************************************************************************** + * $Id: gdaldriver.cpp 29207 2015-05-18 17:23:45Z mloskot $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALDriver class (and C wrappers) + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, 2000, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "ogrsf_frmts.h" + +CPL_CVSID("$Id: gdaldriver.cpp 29207 2015-05-18 17:23:45Z mloskot $"); + +CPL_C_START +const char* GDALClientDatasetGetFilename(const char* pszFilename); +CPL_C_END + +/************************************************************************/ +/* GDALDriver() */ +/************************************************************************/ + +GDALDriver::GDALDriver() + +{ + pfnOpen = NULL; + pfnCreate = NULL; + pfnDelete = NULL; + pfnCreateCopy = NULL; + pfnUnloadDriver = NULL; + pDriverData = NULL; + pfnIdentify = NULL; + pfnRename = NULL; + pfnCopyFiles = NULL; + pfnOpenWithDriverArg = NULL; + pfnCreateVectorOnly = NULL; + pfnDeleteDataSource = NULL; +} + +/************************************************************************/ +/* ~GDALDriver() */ +/************************************************************************/ + +GDALDriver::~GDALDriver() + +{ + if( pfnUnloadDriver != NULL ) + pfnUnloadDriver( this ); +} + +/************************************************************************/ +/* GDALDestroyDriver() */ +/************************************************************************/ + +/** + * \brief Destroy a GDALDriver. + * + * This is roughly equivelent to deleting the driver, but is guaranteed + * to take place in the GDAL heap. It is important this that function + * not be called on a driver that is registered with the GDALDriverManager. + * + * @param hDriver the driver to destroy. + */ + +void CPL_STDCALL GDALDestroyDriver( GDALDriverH hDriver ) + +{ + if( hDriver != NULL ) + delete ((GDALDriver *) hDriver); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +/** + * \brief Create a new dataset with this driver. + * + * What argument values are legal for particular drivers is driver specific, + * and there is no way to query in advance to establish legal values. + * + * That function will try to validate the creation option list passed to the driver + * with the GDALValidateCreationOptions() method. This check can be disabled + * by defining the configuration option GDAL_VALIDATE_CREATION_OPTIONS=NO. + * + * After you have finished working with the returned dataset, it is required + * to close it with GDALClose(). This does not only close the file handle, but + * also ensures that all the data and metadata has been written to the dataset + * (GDALFlushCache() is not sufficient for that purpose). + * + * In some situations, the new dataset can be created in another process through the + * \ref gdal_api_proxy mechanism. + * + * In GDAL 2, the arguments nXSize, nYSize and nBands can be passed to 0 when + * creating a vector-only dataset for a compatible driver. + * + * Equivelent of the C function GDALCreate(). + * + * @param pszFilename the name of the dataset to create. UTF-8 encoded. + * @param nXSize width of created raster in pixels. + * @param nYSize height of created raster in pixels. + * @param nBands number of bands. + * @param eType type of raster. + * @param papszOptions list of driver specific control parameters. + * The APPEND_SUBDATASET=YES option can be + * specified to avoid prior destruction of existing dataset. + * + * @return NULL on failure, or a new GDALDataset. + */ + +GDALDataset * GDALDriver::Create( const char * pszFilename, + int nXSize, int nYSize, int nBands, + GDALDataType eType, char ** papszOptions ) + +{ + //CPLLocaleC oLocaleForcer; + +/* -------------------------------------------------------------------- */ +/* Does this format support creation. */ +/* -------------------------------------------------------------------- */ + if( pfnCreate == NULL && pfnCreateVectorOnly == NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GDALDriver::Create() ... no create method implemented" + " for this format.\n" ); + + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Do some rudimentary argument checking. */ +/* -------------------------------------------------------------------- */ + if (nBands < 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create dataset with %d bands is illegal," + "Must be >= 0.", + nBands ); + return NULL; + } + + if( GetMetadataItem(GDAL_DCAP_RASTER) != NULL && + GetMetadataItem(GDAL_DCAP_VECTOR) == NULL && + (nXSize < 1 || nYSize < 1) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create %dx%d dataset is illegal," + "sizes must be larger than zero.", + nXSize, nYSize ); + return NULL; + } + + const char* pszClientFilename = GDALClientDatasetGetFilename(pszFilename); + if( pszClientFilename != NULL && !EQUAL(GetDescription(), "MEM") && + !EQUAL(GetDescription(), "VRT") ) + { + GDALDriver* poAPIPROXYDriver = GDALGetAPIPROXYDriver(); + if( poAPIPROXYDriver != this ) + { + if( poAPIPROXYDriver == NULL || poAPIPROXYDriver->pfnCreate == NULL ) + return NULL; + char** papszOptionsDup = CSLDuplicate(papszOptions); + papszOptionsDup = CSLAddNameValue(papszOptionsDup, "SERVER_DRIVER", + GetDescription()); + GDALDataset* poDstDS = poAPIPROXYDriver->pfnCreate( + pszClientFilename, nXSize, nYSize, nBands, + eType, papszOptionsDup); + + CSLDestroy(papszOptionsDup); + + if( poDstDS != NULL ) + { + if( poDstDS->GetDescription() == NULL + || strlen(poDstDS->GetDescription()) == 0 ) + poDstDS->SetDescription( pszFilename ); + + if( poDstDS->poDriver == NULL ) + poDstDS->poDriver = poAPIPROXYDriver; + } + + if( poDstDS != NULL || CPLGetLastErrorNo() != CPLE_NotSupported ) + return poDstDS; + } + } + +/* -------------------------------------------------------------------- */ +/* Make sure we cleanup if there is an existing dataset of this */ +/* name. But even if that seems to fail we will continue since */ +/* it might just be a corrupt file or something. */ +/* -------------------------------------------------------------------- */ + if( !CSLFetchBoolean(papszOptions, "APPEND_SUBDATASET", FALSE) ) + QuietDelete( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Validate creation options. */ +/* -------------------------------------------------------------------- */ + if (CSLTestBoolean(CPLGetConfigOption("GDAL_VALIDATE_CREATION_OPTIONS", "YES"))) + GDALValidateCreationOptions( this, papszOptions ); + +/* -------------------------------------------------------------------- */ +/* Proceed with creation. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poDS; + + CPLDebug( "GDAL", "GDALDriver::Create(%s,%s,%d,%d,%d,%s,%p)", + GetDescription(), pszFilename, nXSize, nYSize, nBands, + GDALGetDataTypeName( eType ), + papszOptions ); + + if( pfnCreate != NULL ) + { + poDS = pfnCreate( pszFilename, nXSize, nYSize, nBands, eType, + papszOptions ); + } + else + { + if( nBands > 0 ) + poDS = NULL; + else + poDS = pfnCreateVectorOnly( this, pszFilename, papszOptions ); + } + + if( poDS != NULL ) + { + if( poDS->GetDescription() == NULL + || strlen(poDS->GetDescription()) == 0 ) + poDS->SetDescription( pszFilename ); + + if( poDS->poDriver == NULL ) + poDS->poDriver = this; + + poDS->AddToDatasetOpenList(); + } + + return poDS; +} + +/************************************************************************/ +/* GDALCreate() */ +/************************************************************************/ + +/** + * \brief Create a new dataset with this driver. + * + * @see GDALDriver::Create() + */ + +GDALDatasetH CPL_DLL CPL_STDCALL +GDALCreate( GDALDriverH hDriver, const char * pszFilename, + int nXSize, int nYSize, int nBands, GDALDataType eBandType, + char ** papszOptions ) + +{ + VALIDATE_POINTER1( hDriver, "GDALCreate", NULL ); + + return( ((GDALDriver *) hDriver)->Create( pszFilename, + nXSize, nYSize, nBands, + eBandType, papszOptions ) ); +} + +/************************************************************************/ +/* DefaultCopyMasks() */ +/************************************************************************/ + +CPLErr GDALDriver::DefaultCopyMasks( GDALDataset *poSrcDS, + GDALDataset *poDstDS, + int bStrict ) + +{ + CPLErr eErr = CE_None; + + int nBands = poSrcDS->GetRasterCount(); + if (nBands == 0) + return CE_None; + + const char* papszOptions[2] = { "COMPRESSED=YES", NULL }; + +/* -------------------------------------------------------------------- */ +/* Try to copy mask if it seems appropriate. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; + eErr == CE_None && iBand < nBands; + iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + + int nMaskFlags = poSrcBand->GetMaskFlags(); + if( eErr == CE_None + && !(nMaskFlags & (GMF_ALL_VALID|GMF_PER_DATASET|GMF_ALPHA|GMF_NODATA) ) ) + { + GDALRasterBand *poDstBand = poDstDS->GetRasterBand( iBand+1 ); + if (poDstBand != NULL) + { + eErr = poDstBand->CreateMaskBand( nMaskFlags ); + if( eErr == CE_None ) + { + eErr = GDALRasterBandCopyWholeRaster( + poSrcBand->GetMaskBand(), + poDstBand->GetMaskBand(), + (char**)papszOptions, + GDALDummyProgress, NULL); + } + else if( !bStrict ) + eErr = CE_None; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Try to copy a per-dataset mask if we have one. */ +/* -------------------------------------------------------------------- */ + int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags(); + if( eErr == CE_None + && !(nMaskFlags & (GMF_ALL_VALID|GMF_ALPHA|GMF_NODATA) ) + && (nMaskFlags & GMF_PER_DATASET) ) + { + eErr = poDstDS->CreateMaskBand( nMaskFlags ); + if( eErr == CE_None ) + { + eErr = GDALRasterBandCopyWholeRaster( + poSrcDS->GetRasterBand(1)->GetMaskBand(), + poDstDS->GetRasterBand(1)->GetMaskBand(), + (char**)papszOptions, + GDALDummyProgress, NULL); + } + else if( !bStrict ) + eErr = CE_None; + } + + return eErr; +} + +/************************************************************************/ +/* DefaultCreateCopy() */ +/************************************************************************/ + +GDALDataset *GDALDriver::DefaultCreateCopy( const char * pszFilename, + GDALDataset * poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + CPLErrorReset(); + +/* -------------------------------------------------------------------- */ +/* Validate that we can create the output as requested. */ +/* -------------------------------------------------------------------- */ + int nXSize = poSrcDS->GetRasterXSize(); + int nYSize = poSrcDS->GetRasterYSize(); + int nBands = poSrcDS->GetRasterCount(); + + CPLDebug( "GDAL", "Using default GDALDriver::CreateCopy implementation." ); + + int nLayerCount = poSrcDS->GetLayerCount(); + if (nBands == 0 && nLayerCount == 0 && GetMetadataItem(GDAL_DCAP_VECTOR) == NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GDALDriver::DefaultCreateCopy does not support zero band" ); + return NULL; + } + if( poSrcDS->GetDriver() != NULL && + poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_RASTER) != NULL && + poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_VECTOR) == NULL && + GetMetadataItem(GDAL_DCAP_RASTER) == NULL && + GetMetadataItem(GDAL_DCAP_VECTOR) != NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Source driver is raster-only whereas output driver is vector-only" ); + return NULL; + } + else if( poSrcDS->GetDriver() != NULL && + poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_RASTER) == NULL && + poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_VECTOR) != NULL && + GetMetadataItem(GDAL_DCAP_RASTER) != NULL && + GetMetadataItem(GDAL_DCAP_VECTOR) == NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Source driver is vector-only whereas output driver is raster-only" ); + return NULL; + } + + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Propogate some specific structural metadata as options if it */ +/* appears to be supported by the target driver and the caller */ +/* didn't provide values. */ +/* -------------------------------------------------------------------- */ + char **papszCreateOptions = CSLDuplicate( papszOptions ); + int iOptItem; + static const char *apszOptItems[] = { + "NBITS", "IMAGE_STRUCTURE", + "PIXELTYPE", "IMAGE_STRUCTURE", + NULL }; + + for( iOptItem = 0; nBands > 0 && apszOptItems[iOptItem] != NULL; iOptItem += 2 ) + { + // does the source have this metadata item on the first band? + const char *pszValue = + poSrcDS->GetRasterBand(1)->GetMetadataItem( + apszOptItems[iOptItem], apszOptItems[iOptItem+1] ); + + if( pszValue == NULL ) + continue; + + // do not override provided value. + if( CSLFetchNameValue( papszCreateOptions, pszValue ) != NULL ) + continue; + + // Does this appear to be a supported creation option on this driver? + const char *pszOptionList = + GetMetadataItem( GDAL_DMD_CREATIONDATATYPES ); + + if( pszOptionList == NULL + || strstr(pszOptionList,apszOptItems[iOptItem]) != NULL ) + continue; + + papszCreateOptions = CSLSetNameValue( papszCreateOptions, + apszOptItems[iOptItem], + pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Create destination dataset. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poDstDS; + GDALDataType eType = GDT_Unknown; + CPLErr eErr = CE_None; + + if( nBands > 0 ) + eType = poSrcDS->GetRasterBand(1)->GetRasterDataType(); + poDstDS = Create( pszFilename, nXSize, nYSize, + nBands, eType, papszCreateOptions ); + + CSLDestroy(papszCreateOptions); + + if( poDstDS == NULL ) + return NULL; + int nDstBands = poDstDS->GetRasterCount(); + if( nDstBands != nBands ) + { + if( GetMetadataItem(GDAL_DCAP_RASTER) != NULL ) + { + /* Shouldn't happen for a well-behaved driver */ + CPLError(CE_Failure, CPLE_AppDefined, + "Output driver created only %d bands whereas %d were expected", + nDstBands, nBands); + eErr = CE_Failure; + } + nDstBands = 0; + } + +/* -------------------------------------------------------------------- */ +/* Try setting the projection and geotransform if it seems */ +/* suitable. */ +/* -------------------------------------------------------------------- */ + double adfGeoTransform[6]; + + if( nDstBands == 0 && !bStrict ) + CPLPushErrorHandler(CPLQuietErrorHandler); + + if( eErr == CE_None + && poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None + && (adfGeoTransform[0] != 0.0 + || adfGeoTransform[1] != 1.0 + || adfGeoTransform[2] != 0.0 + || adfGeoTransform[3] != 0.0 + || adfGeoTransform[4] != 0.0 + || adfGeoTransform[5] != 1.0) ) + { + eErr = poDstDS->SetGeoTransform( adfGeoTransform ); + if( !bStrict ) + eErr = CE_None; + } + + if( eErr == CE_None + && poSrcDS->GetProjectionRef() != NULL + && strlen(poSrcDS->GetProjectionRef()) > 0 ) + { + eErr = poDstDS->SetProjection( poSrcDS->GetProjectionRef() ); + if( !bStrict ) + eErr = CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Copy GCPs. */ +/* -------------------------------------------------------------------- */ + if( poSrcDS->GetGCPCount() > 0 && eErr == CE_None ) + { + eErr = poDstDS->SetGCPs( poSrcDS->GetGCPCount(), + poSrcDS->GetGCPs(), + poSrcDS->GetGCPProjection() ); + if( !bStrict ) + eErr = CE_None; + } + + if( nDstBands == 0 && !bStrict ) + CPLPopErrorHandler(); + +/* -------------------------------------------------------------------- */ +/* Copy metadata. */ +/* -------------------------------------------------------------------- */ + if( poSrcDS->GetMetadata() != NULL ) + poDstDS->SetMetadata( poSrcDS->GetMetadata() ); + +/* -------------------------------------------------------------------- */ +/* Copy transportable special domain metadata (RPCs). It would */ +/* be nice to copy geolocation, but is is pretty fragile. */ +/* -------------------------------------------------------------------- */ + char **papszMD = poSrcDS->GetMetadata( "RPC" ); + if( papszMD ) + poDstDS->SetMetadata( papszMD, "RPC" ); + +/* -------------------------------------------------------------------- */ +/* Loop copying bands. */ +/* -------------------------------------------------------------------- */ + for( int iBand = 0; + eErr == CE_None && iBand < nDstBands; + iBand++ ) + { + GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 ); + GDALRasterBand *poDstBand = poDstDS->GetRasterBand( iBand+1 ); + +/* -------------------------------------------------------------------- */ +/* Do we need to copy a colortable. */ +/* -------------------------------------------------------------------- */ + GDALColorTable *poCT; + int bSuccess; + double dfValue; + + poCT = poSrcBand->GetColorTable(); + if( poCT != NULL ) + poDstBand->SetColorTable( poCT ); + +/* -------------------------------------------------------------------- */ +/* Do we need to copy other metadata? Most of this is */ +/* non-critical, so lets not bother folks if it fails are we */ +/* are not in strict mode. */ +/* -------------------------------------------------------------------- */ + if( !bStrict ) + CPLPushErrorHandler( CPLQuietErrorHandler ); + + if( strlen(poSrcBand->GetDescription()) > 0 ) + poDstBand->SetDescription( poSrcBand->GetDescription() ); + + if( CSLCount(poSrcBand->GetMetadata()) > 0 ) + poDstBand->SetMetadata( poSrcBand->GetMetadata() ); + + dfValue = poSrcBand->GetOffset( &bSuccess ); + if( bSuccess && dfValue != 0.0 ) + poDstBand->SetOffset( dfValue ); + + dfValue = poSrcBand->GetScale( &bSuccess ); + if( bSuccess && dfValue != 1.0 ) + poDstBand->SetScale( dfValue ); + + dfValue = poSrcBand->GetNoDataValue( &bSuccess ); + if( bSuccess ) + poDstBand->SetNoDataValue( dfValue ); + + if( poSrcBand->GetColorInterpretation() != GCI_Undefined + && poSrcBand->GetColorInterpretation() + != poDstBand->GetColorInterpretation() ) + poDstBand->SetColorInterpretation( + poSrcBand->GetColorInterpretation() ); + + char** papszCatNames; + papszCatNames = poSrcBand->GetCategoryNames(); + if (0 != papszCatNames) + poDstBand->SetCategoryNames( papszCatNames ); + + if( !bStrict ) + { + CPLPopErrorHandler(); + CPLErrorReset(); + } + else + eErr = CPLGetLastErrorType(); + } + +/* -------------------------------------------------------------------- */ +/* Copy image data. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && nDstBands > 0 ) + eErr = GDALDatasetCopyWholeRaster( (GDALDatasetH) poSrcDS, + (GDALDatasetH) poDstDS, + NULL, pfnProgress, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Should we copy some masks over? */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && nDstBands > 0 ) + eErr = DefaultCopyMasks( poSrcDS, poDstDS, eErr ); + +/* -------------------------------------------------------------------- */ +/* Copy vector layers */ +/* -------------------------------------------------------------------- */ + + if( eErr == CE_None ) + { + if( nLayerCount > 0 && poDstDS->TestCapability(ODsCCreateLayer) ) + { + for( int iLayer = 0; iLayer < nLayerCount; iLayer++ ) + { + OGRLayer *poLayer = poSrcDS->GetLayer(iLayer); + + if( poLayer == NULL ) + continue; + + poDstDS->CopyLayer( poLayer, poLayer->GetName(), NULL ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Try to cleanup the output dataset if the translation failed. */ +/* -------------------------------------------------------------------- */ + if( eErr != CE_None ) + { + delete poDstDS; + Delete( pszFilename ); + return NULL; + } + else + CPLErrorReset(); + + return poDstDS; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +/** + * \brief Create a copy of a dataset. + * + * This method will attempt to create a copy of a raster dataset with the + * indicated filename, and in this drivers format. Band number, size, + * type, projection, geotransform and so forth are all to be copied from + * the provided template dataset. + * + * Note that many sequential write once formats (such as JPEG and PNG) don't + * implement the Create() method but do implement this CreateCopy() method. + * If the driver doesn't implement CreateCopy(), but does implement Create() + * then the default CreateCopy() mechanism built on calling Create() will + * be used. + * + * It is intended that CreateCopy() will often be used with a source dataset + * which is a virtual dataset allowing configuration of band types, and + * other information without actually duplicating raster data (see the VRT driver). + * This is what is done by the gdal_translate utility for example. + * + * That function will try to validate the creation option list passed to the driver + * with the GDALValidateCreationOptions() method. This check can be disabled + * by defining the configuration option GDAL_VALIDATE_CREATION_OPTIONS=NO. + * + * After you have finished working with the returned dataset, it is required + * to close it with GDALClose(). This does not only close the file handle, but + * also ensures that all the data and metadata has been written to the dataset + * (GDALFlushCache() is not sufficient for that purpose). + * + * In some situations, the new dataset can be created in another process through the + * \ref gdal_api_proxy mechanism. + * + * @param pszFilename the name for the new dataset. UTF-8 encoded. + * @param poSrcDS the dataset being duplicated. + * @param bStrict TRUE if the copy must be strictly equivelent, or more + * normally FALSE indicating that the copy may adapt as needed for the + * output format. + * @param papszOptions additional format dependent options controlling + * creation of the output file. The APPEND_SUBDATASET=YES option can be + * specified to avoid prior destruction of existing dataset. + * @param pfnProgress a function to be used to report progress of the copy. + * @param pProgressData application data passed into progress function. + * + * @return a pointer to the newly created dataset (may be read-only access). + */ + +GDALDataset *GDALDriver::CreateCopy( const char * pszFilename, + GDALDataset * poSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + //CPLLocaleC oLocaleForcer; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + const char* pszClientFilename = GDALClientDatasetGetFilename(pszFilename); + if( pszClientFilename != NULL && !EQUAL(GetDescription(), "MEM") && + !EQUAL(GetDescription(), "VRT") ) + { + GDALDriver* poAPIPROXYDriver = GDALGetAPIPROXYDriver(); + if( poAPIPROXYDriver != this ) + { + if( poAPIPROXYDriver->pfnCreateCopy == NULL ) + return NULL; + char** papszOptionsDup = CSLDuplicate(papszOptions); + papszOptionsDup = CSLAddNameValue(papszOptionsDup, "SERVER_DRIVER", + GetDescription()); + GDALDataset* poDstDS = poAPIPROXYDriver->pfnCreateCopy( + pszClientFilename, poSrcDS, bStrict, papszOptionsDup, + pfnProgress, pProgressData); + if( poDstDS != NULL ) + { + if( poDstDS->GetDescription() == NULL + || strlen(poDstDS->GetDescription()) == 0 ) + poDstDS->SetDescription( pszFilename ); + + if( poDstDS->poDriver == NULL ) + poDstDS->poDriver = poAPIPROXYDriver; + } + + CSLDestroy(papszOptionsDup); + if( poDstDS != NULL || CPLGetLastErrorNo() != CPLE_NotSupported ) + return poDstDS; + } + } + +/* -------------------------------------------------------------------- */ +/* Make sure we cleanup if there is an existing dataset of this */ +/* name. But even if that seems to fail we will continue since */ +/* it might just be a corrupt file or something. */ +/* -------------------------------------------------------------------- */ + int bAppendSubdataset = CSLFetchBoolean(papszOptions, "APPEND_SUBDATASET", FALSE); + if( !bAppendSubdataset && + CSLFetchBoolean(papszOptions, "QUIET_DELETE_ON_CREATE_COPY", TRUE) ) + QuietDelete( pszFilename ); + + char** papszOptionsToDelete = NULL; + int iIdxQuietDeleteOnCreateCopy = + CSLPartialFindString(papszOptions, "QUIET_DELETE_ON_CREATE_COPY="); + if( iIdxQuietDeleteOnCreateCopy >= 0 ) + { + if( papszOptionsToDelete == NULL ) + papszOptionsToDelete = CSLDuplicate(papszOptions); + papszOptions = CSLRemoveStrings(papszOptionsToDelete, iIdxQuietDeleteOnCreateCopy, 1, NULL); + papszOptionsToDelete = papszOptions; + } + +/* -------------------------------------------------------------------- */ +/* If _INTERNAL_DATASET=YES, the returned dataset will not be */ +/* registered in the global list of open datasets. */ +/* -------------------------------------------------------------------- */ + int iIdxInternalDataset = + CSLPartialFindString(papszOptions, "_INTERNAL_DATASET="); + int bInternalDataset = FALSE; + if( iIdxInternalDataset >= 0 ) + { + bInternalDataset = CSLFetchBoolean(papszOptions, "_INTERNAL_DATASET", FALSE); + if( papszOptionsToDelete == NULL ) + papszOptionsToDelete = CSLDuplicate(papszOptions); + papszOptions = CSLRemoveStrings(papszOptionsToDelete, iIdxInternalDataset, 1, NULL); + papszOptionsToDelete = papszOptions; + } + +/* -------------------------------------------------------------------- */ +/* Validate creation options. */ +/* -------------------------------------------------------------------- */ + if (CSLTestBoolean(CPLGetConfigOption("GDAL_VALIDATE_CREATION_OPTIONS", "YES"))) + GDALValidateCreationOptions( this, papszOptions); + +/* -------------------------------------------------------------------- */ +/* If the format provides a CreateCopy() method use that, */ +/* otherwise fallback to the internal implementation using the */ +/* Create() method. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poDstDS; + if( pfnCreateCopy != NULL && !CSLTestBoolean(CPLGetConfigOption("GDAL_DEFAULT_CREATE_COPY", "NO")) ) + { + poDstDS = pfnCreateCopy( pszFilename, poSrcDS, bStrict, papszOptions, + pfnProgress, pProgressData ); + if( poDstDS != NULL ) + { + if( poDstDS->GetDescription() == NULL + || strlen(poDstDS->GetDescription()) == 0 ) + poDstDS->SetDescription( pszFilename ); + + if( poDstDS->poDriver == NULL ) + poDstDS->poDriver = this; + + if( !bInternalDataset ) + poDstDS->AddToDatasetOpenList(); + } + } + else + { + poDstDS = DefaultCreateCopy( pszFilename, poSrcDS, bStrict, + papszOptions, pfnProgress, pProgressData ); + } + + CSLDestroy(papszOptionsToDelete); + return poDstDS; +} + +/************************************************************************/ +/* GDALCreateCopy() */ +/************************************************************************/ + +/** + * \brief Create a copy of a dataset. + * + * @see GDALDriver::CreateCopy() + */ + +GDALDatasetH CPL_STDCALL GDALCreateCopy( GDALDriverH hDriver, + const char * pszFilename, + GDALDatasetH hSrcDS, + int bStrict, char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + VALIDATE_POINTER1( hDriver, "GDALCreateCopy", NULL ); + VALIDATE_POINTER1( hSrcDS, "GDALCreateCopy", NULL ); + + return (GDALDatasetH) ((GDALDriver *) hDriver)-> + CreateCopy( pszFilename, (GDALDataset *) hSrcDS, bStrict, papszOptions, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* QuietDelete() */ +/************************************************************************/ + +/** + * \brief Delete dataset if found. + * + * This is a helper method primarily used by Create() and + * CreateCopy() to predelete any dataset of the name soon to be + * created. It will attempt to delete the named dataset if + * one is found, otherwise it does nothing. An error is only + * returned if the dataset is found but the delete fails. + * + * This is a static method and it doesn't matter what driver instance + * it is invoked on. It will attempt to discover the correct driver + * using Identify(). + * + * @param pszName the dataset name to try and delete. + * @return CE_None if the dataset does not exist, or is deleted without issues. + */ + +CPLErr GDALDriver::QuietDelete( const char *pszName ) + +{ + VSIStatBufL sStat; + int bExists = VSIStatExL(pszName, &sStat, VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0; + +#ifdef S_ISFIFO + if( bExists && S_ISFIFO(sStat.st_mode) ) + return CE_None; +#endif + + if( bExists && + VSI_ISDIR(sStat.st_mode) ) + { + /* It is not desirable to remove directories quietly */ + /* Necessary to avoid ogr_mitab_12 to destroy file created at ogr_mitab_7 */ + return CE_None; + } + + CPLPushErrorHandler(CPLQuietErrorHandler); + GDALDriver *poDriver = (GDALDriver*) GDALIdentifyDriver( pszName, NULL ); + CPLPopErrorHandler(); + + if( poDriver == NULL ) + return CE_None; + + CPLDebug( "GDAL", "QuietDelete(%s) invoking Delete()", pszName ); + + CPLErr eErr; + int bQuiet = ( !bExists && poDriver->pfnDelete == NULL && poDriver->pfnDeleteDataSource == NULL ); + if( bQuiet ) + CPLPushErrorHandler(CPLQuietErrorHandler); + eErr = poDriver->Delete( pszName ); + if( bQuiet ) + { + CPLPopErrorHandler(); + CPLErrorReset(); + eErr = CE_None; + } + return eErr; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +/** + * \brief Delete named dataset. + * + * The driver will attempt to delete the named dataset in a driver specific + * fashion. Full featured drivers will delete all associated files, + * database objects, or whatever is appropriate. The default behaviour when + * no driver specific behaviour is provided is to attempt to delete the + * passed name as a single file. + * + * It is unwise to have open dataset handles on this dataset when it is + * deleted. + * + * Equivelent of the C function GDALDeleteDataset(). + * + * @param pszFilename name of dataset to delete. + * + * @return CE_None on success, or CE_Failure if the operation fails. + */ + +CPLErr GDALDriver::Delete( const char * pszFilename ) + +{ + if( pfnDelete != NULL ) + return pfnDelete( pszFilename ); + else if( pfnDeleteDataSource != NULL ) + return pfnDeleteDataSource( this, pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Collect file list. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hDS = (GDALDataset *) GDALOpenEx(pszFilename,0,NULL,NULL,NULL); + + if( hDS == NULL ) + { + if( CPLGetLastErrorNo() == 0 ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open %s to obtain file list.", pszFilename ); + + return CE_Failure; + } + + char **papszFileList = GDALGetFileList( hDS ); + + GDALClose( hDS ); + + if( CSLCount( papszFileList ) == 0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to determine files associated with %s,\n" + "delete fails.", pszFilename ); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Delete all files. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; papszFileList[i] != NULL; i++ ) + { + if( VSIUnlink( papszFileList[i] ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Deleting %s failed:\n%s", + papszFileList[i], + VSIStrerror(errno) ); + CSLDestroy( papszFileList ); + return CE_Failure; + } + } + + CSLDestroy( papszFileList ); + + return CE_None; +} + +/************************************************************************/ +/* GDALDeleteDataset() */ +/************************************************************************/ + +/** + * \brief Delete named dataset. + * + * @see GDALDriver::Delete() + */ + +CPLErr CPL_STDCALL GDALDeleteDataset( GDALDriverH hDriver, const char * pszFilename ) + +{ + if( hDriver == NULL ) + hDriver = GDALIdentifyDriver( pszFilename, NULL ); + + if( hDriver == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No identifiable driver for %s.", + pszFilename ); + return CE_Failure; + } + + return ((GDALDriver *) hDriver)->Delete( pszFilename ); +} + +/************************************************************************/ +/* DefaultRename() */ +/* */ +/* The generic implementation based on the file list used when */ +/* there is no format specific implementation. */ +/************************************************************************/ + +CPLErr GDALDriver::DefaultRename( const char * pszNewName, + const char *pszOldName ) + +{ +/* -------------------------------------------------------------------- */ +/* Collect file list. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hDS = (GDALDataset *) GDALOpen(pszOldName,GA_ReadOnly); + + if( hDS == NULL ) + { + if( CPLGetLastErrorNo() == 0 ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open %s to obtain file list.", pszOldName ); + + return CE_Failure; + } + + char **papszFileList = GDALGetFileList( hDS ); + + GDALClose( hDS ); + + if( CSLCount( papszFileList ) == 0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to determine files associated with %s,\n" + "rename fails.", pszOldName ); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Produce a list of new filenames that correspond to the old */ +/* names. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + int i; + char **papszNewFileList = + CPLCorrespondingPaths( pszOldName, pszNewName, papszFileList ); + + if( papszNewFileList == NULL ) + return CE_Failure; + + for( i = 0; papszFileList[i] != NULL; i++ ) + { + if( CPLMoveFile( papszNewFileList[i], papszFileList[i] ) != 0 ) + { + eErr = CE_Failure; + // Try to put the ones we moved back. + for( --i; i >= 0; i-- ) + CPLMoveFile( papszFileList[i], papszNewFileList[i] ); + break; + } + } + + CSLDestroy( papszNewFileList ); + CSLDestroy( papszFileList ); + + return eErr; +} + +/************************************************************************/ +/* Rename() */ +/************************************************************************/ + +/** + * \brief Rename a dataset. + * + * Rename a dataset. This may including moving the dataset to a new directory + * or even a new filesystem. + * + * It is unwise to have open dataset handles on this dataset when it is + * being renamed. + * + * Equivelent of the C function GDALRenameDataset(). + * + * @param pszNewName new name for the dataset. + * @param pszOldName old name for the dataset. + * + * @return CE_None on success, or CE_Failure if the operation fails. + */ + +CPLErr GDALDriver::Rename( const char * pszNewName, const char *pszOldName ) + +{ + if( pfnRename != NULL ) + return pfnRename( pszNewName, pszOldName ); + else + return DefaultRename( pszNewName, pszOldName ); +} + +/************************************************************************/ +/* GDALRenameDataset() */ +/************************************************************************/ + +/** + * \brief Rename a dataset. + * + * @see GDALDriver::Rename() + */ + +CPLErr CPL_STDCALL GDALRenameDataset( GDALDriverH hDriver, + const char * pszNewName, + const char * pszOldName ) + +{ + if( hDriver == NULL ) + hDriver = GDALIdentifyDriver( pszOldName, NULL ); + + if( hDriver == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No identifiable driver for %s.", + pszOldName ); + return CE_Failure; + } + + return ((GDALDriver *) hDriver)->Rename( pszNewName, pszOldName ); +} + +/************************************************************************/ +/* DefaultCopyFiles() */ +/* */ +/* The default implementation based on file lists used when */ +/* there is no format specific implementation. */ +/************************************************************************/ + +CPLErr GDALDriver::DefaultCopyFiles( const char * pszNewName, + const char *pszOldName ) + +{ +/* -------------------------------------------------------------------- */ +/* Collect file list. */ +/* -------------------------------------------------------------------- */ + GDALDatasetH hDS = (GDALDataset *) GDALOpen(pszOldName,GA_ReadOnly); + + if( hDS == NULL ) + { + if( CPLGetLastErrorNo() == 0 ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open %s to obtain file list.", pszOldName ); + + return CE_Failure; + } + + char **papszFileList = GDALGetFileList( hDS ); + + GDALClose( hDS ); + + if( CSLCount( papszFileList ) == 0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to determine files associated with %s,\n" + "rename fails.", pszOldName ); + + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Produce a list of new filenames that correspond to the old */ +/* names. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + int i; + char **papszNewFileList = + CPLCorrespondingPaths( pszOldName, pszNewName, papszFileList ); + + if( papszNewFileList == NULL ) + return CE_Failure; + + for( i = 0; papszFileList[i] != NULL; i++ ) + { + if( CPLCopyFile( papszNewFileList[i], papszFileList[i] ) != 0 ) + { + eErr = CE_Failure; + // Try to put the ones we moved back. + for( --i; i >= 0; i-- ) + VSIUnlink( papszNewFileList[i] ); + break; + } + } + + CSLDestroy( papszNewFileList ); + CSLDestroy( papszFileList ); + + return eErr; +} + +/************************************************************************/ +/* CopyFiles() */ +/************************************************************************/ + +/** + * \brief Copy the files of a dataset. + * + * Copy all the files associated with a dataset. + * + * Equivelent of the C function GDALCopyDatasetFiles(). + * + * @param pszNewName new name for the dataset. + * @param pszOldName old name for the dataset. + * + * @return CE_None on success, or CE_Failure if the operation fails. + */ + +CPLErr GDALDriver::CopyFiles( const char * pszNewName, const char *pszOldName ) + +{ + if( pfnCopyFiles != NULL ) + return pfnCopyFiles( pszNewName, pszOldName ); + else + return DefaultCopyFiles( pszNewName, pszOldName ); +} + +/************************************************************************/ +/* GDALCopyDatasetFiles() */ +/************************************************************************/ + +/** + * \brief Copy the files of a dataset. + * + * @see GDALDriver::CopyFiles() + */ + +CPLErr CPL_STDCALL GDALCopyDatasetFiles( GDALDriverH hDriver, + const char * pszNewName, + const char * pszOldName ) + +{ + if( hDriver == NULL ) + hDriver = GDALIdentifyDriver( pszOldName, NULL ); + + if( hDriver == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No identifiable driver for %s.", + pszOldName ); + return CE_Failure; + } + + return ((GDALDriver *) hDriver)->CopyFiles( pszNewName, pszOldName ); +} + +/************************************************************************/ +/* GDALGetDriverShortName() */ +/************************************************************************/ + +/** + * \brief Return the short name of a driver + * + * This is the string that can be + * passed to the GDALGetDriverByName() function. + * + * For the GeoTIFF driver, this is "GTiff" + * + * @param hDriver the handle of the driver + * @return the short name of the driver. The + * returned string should not be freed and is owned by the driver. + */ + +const char * CPL_STDCALL GDALGetDriverShortName( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER1( hDriver, "GDALGetDriverShortName", NULL ); + + return ((GDALDriver *) hDriver)->GetDescription(); +} + +/************************************************************************/ +/* GDALGetDriverLongName() */ +/************************************************************************/ + +/** + * \brief Return the long name of a driver + * + * For the GeoTIFF driver, this is "GeoTIFF" + * + * @param hDriver the handle of the driver + * @return the long name of the driver or empty string. The + * returned string should not be freed and is owned by the driver. + */ + +const char * CPL_STDCALL GDALGetDriverLongName( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER1( hDriver, "GDALGetDriverLongName", NULL ); + + const char *pszLongName = + ((GDALDriver *) hDriver)->GetMetadataItem( GDAL_DMD_LONGNAME ); + + if( pszLongName == NULL ) + return ""; + else + return pszLongName; +} + +/************************************************************************/ +/* GDALGetDriverHelpTopic() */ +/************************************************************************/ + +/** + * \brief Return the URL to the help that describes the driver + * + * That URL is relative to the GDAL documentation directory. + * + * For the GeoTIFF driver, this is "frmt_gtiff.html" + * + * @param hDriver the handle of the driver + * @return the URL to the help that describes the driver or NULL. The + * returned string should not be freed and is owned by the driver. + */ + +const char * CPL_STDCALL GDALGetDriverHelpTopic( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER1( hDriver, "GDALGetDriverHelpTopic", NULL ); + + return ((GDALDriver *) hDriver)->GetMetadataItem( GDAL_DMD_HELPTOPIC ); +} + +/************************************************************************/ +/* GDALGetDriverCreationOptionList() */ +/************************************************************************/ + +/** + * \brief Return the list of creation options of the driver + * + * Return the list of creation options of the driver used by Create() and + * CreateCopy() as an XML string + * + * @param hDriver the handle of the driver + * @return an XML string that describes the list of creation options or + * empty string. The returned string should not be freed and is + * owned by the driver. + */ + +const char * CPL_STDCALL GDALGetDriverCreationOptionList( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER1( hDriver, "GDALGetDriverCreationOptionList", NULL ); + + const char *pszOptionList = + ((GDALDriver *) hDriver)->GetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST ); + + if( pszOptionList == NULL ) + return ""; + else + return pszOptionList; +} + +/************************************************************************/ +/* GDALValidateCreationOptions() */ +/************************************************************************/ + +/** + * \brief Validate the list of creation options that are handled by a driver + * + * This is a helper method primarily used by Create() and + * CreateCopy() to validate that the passed in list of creation options + * is compatible with the GDAL_DMD_CREATIONOPTIONLIST metadata item defined + * by some drivers. @see GDALGetDriverCreationOptionList() + * + * If the GDAL_DMD_CREATIONOPTIONLIST metadata item is not defined, this + * function will return TRUE. Otherwise it will check that the keys and values + * in the list of creation options are compatible with the capabilities declared + * by the GDAL_DMD_CREATIONOPTIONLIST metadata item. In case of incompatibility + * a (non fatal) warning will be emited and FALSE will be returned. + * + * @param hDriver the handle of the driver with whom the lists of creation option + * must be validated + * @param papszCreationOptions the list of creation options. An array of strings, + * whose last element is a NULL pointer + * @return TRUE if the list of creation options is compatible with the Create() + * and CreateCopy() method of the driver, FALSE otherwise. + */ + +int CPL_STDCALL GDALValidateCreationOptions( GDALDriverH hDriver, + char** papszCreationOptions) +{ + VALIDATE_POINTER1( hDriver, "GDALValidateCreationOptions", FALSE ); + const char *pszOptionList = + ((GDALDriver *) hDriver)->GetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST ); + CPLString osDriver; + osDriver.Printf("driver %s", ((GDALDriver *) hDriver)->GetDescription()); + char** papszOptionsToValidate = papszCreationOptions; + char** papszOptionsToFree = NULL; + if( CSLFetchNameValue( papszCreationOptions, "APPEND_SUBDATASET") ) + { + papszOptionsToValidate = papszOptionsToFree = + CSLSetNameValue(CSLDuplicate(papszCreationOptions), "APPEND_SUBDATASET", NULL); + } + int bRet = GDALValidateOptions( pszOptionList, + (const char* const* )papszOptionsToValidate, + "creation option", + osDriver); + CSLDestroy(papszOptionsToFree); + return bRet; +} + +/************************************************************************/ +/* GDALValidateOpenOptions() */ +/************************************************************************/ + +int GDALValidateOpenOptions( GDALDriverH hDriver, + const char* const* papszOpenOptions) +{ + VALIDATE_POINTER1( hDriver, "GDALValidateOpenOptions", FALSE ); + const char *pszOptionList = + ((GDALDriver *) hDriver)->GetMetadataItem( GDAL_DMD_OPENOPTIONLIST ); + CPLString osDriver; + osDriver.Printf("driver %s", ((GDALDriver *) hDriver)->GetDescription()); + return GDALValidateOptions( pszOptionList, papszOpenOptions, + "open option", + osDriver); +} + +/************************************************************************/ +/* GDALValidateOptions() */ +/************************************************************************/ + +int GDALValidateOptions( const char* pszOptionList, + const char* const* papszOptionsToValidate, + const char* pszErrorMessageOptionType, + const char* pszErrorMessageContainerName) +{ + int bRet = TRUE; + + if( papszOptionsToValidate == NULL || *papszOptionsToValidate == NULL) + return TRUE; + if( pszOptionList == NULL ) + return TRUE; + + CPLXMLNode* psNode = CPLParseXMLString(pszOptionList); + if (psNode == NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Could not parse %s list of %s. Assuming options are valid.", + pszErrorMessageOptionType, pszErrorMessageContainerName); + return TRUE; + } + + while(*papszOptionsToValidate) + { + char* pszKey = NULL; + const char* pszValue = CPLParseNameValue(*papszOptionsToValidate, &pszKey); + if (pszKey == NULL) + { + CPLError(CE_Warning, CPLE_NotSupported, + "%s '%s' is not formatted with the key=value format", + pszErrorMessageOptionType, + *papszOptionsToValidate); + bRet = FALSE; + + papszOptionsToValidate ++; + continue; + } + + if( EQUAL(pszKey, "VALIDATE_OPEN_OPTIONS") ) + { + papszOptionsToValidate ++; + continue; + } + + CPLXMLNode* psChildNode = psNode->psChild; + while(psChildNode) + { + if (EQUAL(psChildNode->pszValue, "OPTION")) + { + const char* pszOptionName = CPLGetXMLValue(psChildNode, "name", ""); + /* For option names terminated by wildcard (NITF BLOCKA option names for example) */ + if (strlen(pszOptionName) > 0 && + pszOptionName[strlen(pszOptionName) - 1] == '*' && + EQUALN(pszOptionName, pszKey, strlen(pszOptionName) - 1)) + { + break; + } + + /* For option names beginning by a wildcard */ + if( pszOptionName[0] == '*' && + strlen(pszKey) > strlen(pszOptionName) && + EQUAL( pszKey + strlen(pszKey) - strlen(pszOptionName + 1), pszOptionName + 1 ) ) + { + break; + } + + if (EQUAL(pszOptionName, pszKey) ) + { + break; + } + const char* pszAlias = CPLGetXMLValue(psChildNode, "alias", + CPLGetXMLValue(psChildNode, "deprecated_alias", "")); + if (EQUAL(pszAlias, pszKey) ) + { + CPLDebug("GDAL", "Using deprecated alias '%s'. New name is '%s'", + pszAlias, pszOptionName); + break; + } + } + psChildNode = psChildNode->psNext; + } + if (psChildNode == NULL) + { + if( !EQUAL(pszErrorMessageOptionType, "open option") || + CSLFetchBoolean((char**)papszOptionsToValidate, "VALIDATE_OPEN_OPTIONS", TRUE) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "%s does not support %s %s", + pszErrorMessageContainerName, + pszErrorMessageOptionType, + pszKey); + bRet = FALSE; + } + + CPLFree(pszKey); + papszOptionsToValidate ++; + continue; + } + +#ifdef DEBUG + CPLXMLNode* psChildSubNode = psChildNode->psChild; + while(psChildSubNode) + { + if( psChildSubNode->eType == CXT_Attribute ) + { + if( !(EQUAL(psChildSubNode->pszValue, "name") || + EQUAL(psChildSubNode->pszValue, "alias") || + EQUAL(psChildSubNode->pszValue, "deprecated_alias") || + EQUAL(psChildSubNode->pszValue, "alt_config_option") || + EQUAL(psChildSubNode->pszValue, "description") || + EQUAL(psChildSubNode->pszValue, "type") || + EQUAL(psChildSubNode->pszValue, "min") || + EQUAL(psChildSubNode->pszValue, "max") || + EQUAL(psChildSubNode->pszValue, "default") || + EQUAL(psChildSubNode->pszValue, "maxsize") || + EQUAL(psChildSubNode->pszValue, "required")) ) + { + /* Driver error */ + CPLError(CE_Warning, CPLE_NotSupported, + "%s : unhandled attribute '%s' for %s %s.", + pszErrorMessageContainerName, + psChildSubNode->pszValue, + pszKey, + pszErrorMessageOptionType); + } + } + psChildSubNode = psChildSubNode->psNext; + } +#endif + + const char* pszType = CPLGetXMLValue(psChildNode, "type", NULL); + const char* pszMin = CPLGetXMLValue(psChildNode, "min", NULL); + const char* pszMax = CPLGetXMLValue(psChildNode, "max", NULL); + if (pszType != NULL) + { + if (EQUAL(pszType, "INT") || EQUAL(pszType, "INTEGER")) + { + const char* pszValueIter = pszValue; + while (*pszValueIter) + { + if (!((*pszValueIter >= '0' && *pszValueIter <= '9') || + *pszValueIter == '+' || *pszValueIter == '-')) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s of type int.", + pszValue, pszKey, pszErrorMessageOptionType); + bRet = FALSE; + break; + } + pszValueIter++; + } + if( *pszValueIter == '0' ) + { + if( pszMin && atoi(pszValue) < atoi(pszMin) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s that should be >= %s.", + pszValue, pszKey, pszErrorMessageOptionType, pszMin); + break; + } + if( pszMax && atoi(pszValue) > atoi(pszMax) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s that should be <= %s.", + pszValue, pszKey, pszErrorMessageOptionType, pszMax); + break; + } + } + } + else if (EQUAL(pszType, "UNSIGNED INT")) + { + const char* pszValueIter = pszValue; + while (*pszValueIter) + { + if (!((*pszValueIter >= '0' && *pszValueIter <= '9') || + *pszValueIter == '+')) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s of type unsigned int.", + pszValue, pszKey, pszErrorMessageOptionType); + bRet = FALSE; + break; + } + pszValueIter++; + if( *pszValueIter == '0' ) + { + if( pszMin && atoi(pszValue) < atoi(pszMin) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s that should be >= %s.", + pszValue, pszKey, pszErrorMessageOptionType, pszMin); + break; + } + if( pszMax && atoi(pszValue) > atoi(pszMax) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s that should be <= %s.", + pszValue, pszKey, pszErrorMessageOptionType, pszMax); + break; + } + } + } + } + else if (EQUAL(pszType, "FLOAT")) + { + char* endPtr = NULL; + double dfVal = CPLStrtod(pszValue, &endPtr); + if ( !(endPtr == NULL || *endPtr == '\0') ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s of type float.", + pszValue, pszKey, pszErrorMessageOptionType); + bRet = FALSE; + } + else + { + if( pszMin && dfVal < CPLAtof(pszMin) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s that should be >= %s.", + pszValue, pszKey, pszErrorMessageOptionType, pszMin); + break; + } + if( pszMax && dfVal > CPLAtof(pszMax) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s that should be <= %s.", + pszValue, pszKey, pszErrorMessageOptionType, pszMax); + break; + } + } + } + else if (EQUAL(pszType, "BOOLEAN")) + { + if (!(EQUAL(pszValue, "ON") || EQUAL(pszValue, "TRUE") || EQUAL(pszValue, "YES") || + EQUAL(pszValue, "OFF") || EQUAL(pszValue, "FALSE") || EQUAL(pszValue, "NO"))) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s of type boolean.", + pszValue, pszKey, pszErrorMessageOptionType); + bRet = FALSE; + } + } + else if (EQUAL(pszType, "STRING-SELECT")) + { + int bMatchFound = FALSE; + CPLXMLNode* psStringSelect = psChildNode->psChild; + while(psStringSelect) + { + if (psStringSelect->eType == CXT_Element && + EQUAL(psStringSelect->pszValue, "Value")) + { + CPLXMLNode* psOptionNode = psStringSelect->psChild; + while(psOptionNode) + { + if (psOptionNode->eType == CXT_Text && + EQUAL(psOptionNode->pszValue, pszValue)) + { + bMatchFound = TRUE; + break; + } + psOptionNode = psOptionNode->psNext; + } + if (bMatchFound) + break; + } + psStringSelect = psStringSelect->psNext; + } + if (!bMatchFound) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is an unexpected value for %s %s of type string-select.", + pszValue, pszKey, pszErrorMessageOptionType); + bRet = FALSE; + } + } + else if (EQUAL(pszType, "STRING")) + { + const char* pszMaxSize = CPLGetXMLValue(psChildNode, "maxsize", NULL); + if (pszMaxSize != NULL) + { + if ((int)strlen(pszValue) > atoi(pszMaxSize)) + { + CPLError(CE_Warning, CPLE_NotSupported, + "'%s' is of size %d, whereas maximum size for %s %s is %d.", + pszValue, (int)strlen(pszValue), pszKey, + pszErrorMessageOptionType, atoi(pszMaxSize)); + bRet = FALSE; + } + } + } + else + { + /* Driver error */ + CPLError(CE_Warning, CPLE_NotSupported, + "%s : type '%s' for %s %s is not recognized.", + pszErrorMessageContainerName, + pszType, + pszKey, + pszErrorMessageOptionType); + } + } + else + { + /* Driver error */ + CPLError(CE_Warning, CPLE_NotSupported, + "%s : no type for %s %s.", + pszErrorMessageContainerName, + pszKey, + pszErrorMessageOptionType); + } + CPLFree(pszKey); + papszOptionsToValidate++; + } + + CPLDestroyXMLNode(psNode); + return bRet; +} + +/************************************************************************/ +/* GDALIdentifyDriver() */ +/************************************************************************/ + +/** + * \brief Identify the driver that can open a raster file. + * + * This function will try to identify the driver that can open the passed file + * name by invoking the Identify method of each registered GDALDriver in turn. + * The first driver that successful identifies the file name will be returned. + * If all drivers fail then NULL is returned. + * + * In order to reduce the need for such searches touch the operating system + * file system machinery, it is possible to give an optional list of files. + * This is the list of all files at the same level in the file system as the + * target file, including the target file. The filenames will not include any + * path components, are an essentially just the output of CPLReadDir() on the + * parent directory. If the target object does not have filesystem semantics + * then the file list should be NULL. + * + * @param pszFilename the name of the file to access. In the case of + * exotic drivers this may not refer to a physical file, but instead contain + * information for the driver on how to access a dataset. + * + * @param papszFileList an array of strings, whose last element is the NULL pointer. + * These strings are filenames that are auxiliary to the main filename. The passed + * value may be NULL. + * + * @return A GDALDriverH handle or NULL on failure. For C++ applications + * this handle can be cast to a GDALDriver *. + */ + + +GDALDriverH CPL_STDCALL +GDALIdentifyDriver( const char * pszFilename, + char **papszFileList ) + +{ + int iDriver; + GDALDriverManager *poDM = GetGDALDriverManager(); + GDALOpenInfo oOpenInfo( pszFilename, GA_ReadOnly, papszFileList ); + //CPLLocaleC oLocaleForcer; + + CPLErrorReset(); + CPLAssert( NULL != poDM ); + + int nDriverCount = poDM->GetDriverCount(); + + // First pass: only use drivers that have a pfnIdentify implementation + for( iDriver = -1; iDriver < nDriverCount; iDriver++ ) + { + GDALDriver *poDriver; + + if( iDriver < 0 ) + poDriver = GDALGetAPIPROXYDriver(); + else + poDriver = poDM->GetDriver( iDriver ); + + VALIDATE_POINTER1( poDriver, "GDALIdentifyDriver", NULL ); + + if( poDriver->pfnIdentify != NULL ) + { + if( poDriver->pfnIdentify( &oOpenInfo ) > 0 ) + return (GDALDriverH) poDriver; + } + } + + // Second pass: slow method + for( iDriver = -1; iDriver < nDriverCount; iDriver++ ) + { + GDALDriver *poDriver; + GDALDataset *poDS; + + if( iDriver < 0 ) + poDriver = GDALGetAPIPROXYDriver(); + else + poDriver = poDM->GetDriver( iDriver ); + + VALIDATE_POINTER1( poDriver, "GDALIdentifyDriver", NULL ); + + if( poDriver->pfnIdentify != NULL ) + { + if( poDriver->pfnIdentify( &oOpenInfo ) == 0 ) + continue; + } + + if( poDriver->pfnOpen != NULL ) + { + poDS = poDriver->pfnOpen( &oOpenInfo ); + if( poDS != NULL ) + { + delete poDS; + return (GDALDriverH) poDriver; + } + + if( CPLGetLastErrorNo() != 0 ) + return NULL; + } + else if( poDriver->pfnOpenWithDriverArg != NULL ) + { + poDS = poDriver->pfnOpenWithDriverArg( poDriver, &oOpenInfo ); + if( poDS != NULL ) + { + delete poDS; + return (GDALDriverH) poDriver; + } + + if( CPLGetLastErrorNo() != 0 ) + return NULL; + } + } + + return NULL; +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALDriver::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) + +{ + if( pszDomain == NULL || pszDomain[0] == '\0' ) + { + /* Automatically sets GDAL_DMD_EXTENSIONS from GDAL_DMD_EXTENSION */ + if( EQUAL(pszName, GDAL_DMD_EXTENSION) && + GDALMajorObject::GetMetadataItem(GDAL_DMD_EXTENSIONS) == NULL ) + { + GDALMajorObject::SetMetadataItem(GDAL_DMD_EXTENSIONS, pszValue); + } + } + return GDALMajorObject::SetMetadataItem(pszName, pszValue, pszDomain); +} diff --git a/bazaar/plugin/gdal/gcore/gdaldrivermanager.cpp b/bazaar/plugin/gdal/gcore/gdaldrivermanager.cpp new file mode 100644 index 000000000..9be7a27d2 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaldrivermanager.cpp @@ -0,0 +1,846 @@ +/****************************************************************************** + * $Id: gdaldrivermanager.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALDriverManager class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include "ogr_srs_api.h" +#include "cpl_multiproc.h" +#include "gdal_pam.h" +#include "gdal_alg_priv.h" + +#ifdef _MSC_VER +# ifdef MSVC_USE_VLD +# include +# include +# endif +#endif + +CPL_CVSID("$Id: gdaldrivermanager.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +static const char *pszUpdatableINST_DATA = +"__INST_DATA_TARGET: "; + +/************************************************************************/ +/* ==================================================================== */ +/* GDALDriverManager */ +/* ==================================================================== */ +/************************************************************************/ + +static volatile GDALDriverManager *poDM = NULL; +static CPLMutex *hDMMutex = NULL; + +CPLMutex** GDALGetphDMMutex() { return &hDMMutex; } + +/************************************************************************/ +/* GetGDALDriverManager() */ +/* */ +/* A freestanding function to get the only instance of the */ +/* GDALDriverManager. */ +/************************************************************************/ + +/** + * \brief Fetch the global GDAL driver manager. + * + * This function fetches the pointer to the singleton global driver manager. + * If the driver manager doesn't exist it is automatically created. + * + * @return pointer to the global driver manager. This should not be able + * to fail. + */ + +GDALDriverManager * GetGDALDriverManager() + +{ + if( poDM == NULL ) + { + CPLMutexHolderD( &hDMMutex ); + + if( poDM == NULL ) + poDM = new GDALDriverManager(); + } + + CPLAssert( NULL != poDM ); + + return const_cast( poDM ); +} + +/************************************************************************/ +/* GDALDriverManager() */ +/************************************************************************/ + +GDALDriverManager::GDALDriverManager() + +{ + nDrivers = 0; + papoDrivers = NULL; + + CPLAssert( poDM == NULL ); + +/* -------------------------------------------------------------------- */ +/* We want to push a location to search for data files */ +/* supporting GDAL/OGR such as EPSG csv files, S-57 definition */ +/* files, and so forth. The static pszUpdateableINST_DATA */ +/* string can be updated within the shared library or */ +/* executable during an install to point installed data */ +/* directory. If it isn't burned in here then we use the */ +/* INST_DATA macro (setup at configure time) if */ +/* available. Otherwise we don't push anything and we hope */ +/* other mechanisms such as environment variables will have */ +/* been employed. */ +/* -------------------------------------------------------------------- */ + if( CPLGetConfigOption( "GDAL_DATA", NULL ) != NULL ) + { + // this one is picked up automatically by finder initialization. + } + else if( pszUpdatableINST_DATA[19] != ' ' ) + { + CPLPushFinderLocation( pszUpdatableINST_DATA + 19 ); + } + else + { +#ifdef INST_DATA + CPLPushFinderLocation( INST_DATA ); +#endif + } +} + +/************************************************************************/ +/* ~GDALDriverManager() */ +/************************************************************************/ + +void GDALDatasetPoolPreventDestroy(); /* keep that in sync with gdalproxypool.cpp */ +void GDALDatasetPoolForceDestroy(); /* keep that in sync with gdalproxypool.cpp */ + +GDALDriverManager::~GDALDriverManager() + +{ +/* -------------------------------------------------------------------- */ +/* Cleanup any open datasets. */ +/* -------------------------------------------------------------------- */ + int i, nDSCount; + GDALDataset **papoDSList; + + /* First begin by requesting each reamining dataset to drop any reference */ + /* to other datasets */ + int bHasDroppedRef; + + /* We have to prevent the destroying of the dataset pool during this first */ + /* phase, otherwise it cause crashes with a VRT B referencing a VRT A, and if */ + /* CloseDependentDatasets() is called first on VRT A. */ + /* If we didn't do this nasty trick, due to the refCountOfDisableRefCount */ + /* mechanism that cheats the real refcount of the dataset pool, we might */ + /* destroy the dataset pool too early, leading the VRT A to */ + /* destroy itself indirectly ... Ok, I am aware this explanation does */ + /* not make any sense unless you try it under a debugger ... */ + /* When people just manipulate "top-level" dataset handles, we luckily */ + /* don't need this horrible hack, but GetOpenDatasets() expose "low-level" */ + /* datasets, which defeat some "design" of the proxy pool */ + GDALDatasetPoolPreventDestroy(); + + do + { + papoDSList = GDALDataset::GetOpenDatasets(&nDSCount); + /* If a dataset has dropped a reference, the list might have become */ + /* invalid, so go out of the loop and try again with the new valid */ + /* list */ + bHasDroppedRef = FALSE; + for(i=0;iGetDescription() ); + bHasDroppedRef = papoDSList[i]->CloseDependentDatasets(); + } + } while(bHasDroppedRef); + + /* Now let's destroy the dataset pool. Nobody should use it afterwards */ + /* if people have well released their dependent datasets above */ + GDALDatasetPoolForceDestroy(); + + /* Now close the stand-alone datasets */ + papoDSList = GDALDataset::GetOpenDatasets(&nDSCount); + for(i=0;iGetDescription(), papoDSList[i] ); + /* Destroy with delete operator rather than GDALClose() to force deletion of */ + /* datasets with multiple reference count */ + /* We could also iterate while GetOpenDatasets() returns a non NULL list */ + delete papoDSList[i]; + } + +/* -------------------------------------------------------------------- */ +/* Destroy the existing drivers. */ +/* -------------------------------------------------------------------- */ + while( GetDriverCount() > 0 ) + { + GDALDriver *poDriver = GetDriver(0); + + DeregisterDriver(poDriver); + delete poDriver; + } + + delete GDALGetAPIPROXYDriver(); + +/* -------------------------------------------------------------------- */ +/* Cleanup local memory. */ +/* -------------------------------------------------------------------- */ + VSIFree( papoDrivers ); + +/* -------------------------------------------------------------------- */ +/* Cleanup any Proxy related memory. */ +/* -------------------------------------------------------------------- */ + PamCleanProxyDB(); + +/* -------------------------------------------------------------------- */ +/* Blow away all the finder hints paths. We really shouldn't */ +/* be doing all of them, but it is currently hard to keep track */ +/* of those that actually belong to us. */ +/* -------------------------------------------------------------------- */ + CPLFinderClean(); + CPLFreeConfig(); + CPLCleanupSharedFileMutex(); + +/* -------------------------------------------------------------------- */ +/* Cleanup any memory allocated by the OGRSpatialReference */ +/* related subsystem. */ +/* -------------------------------------------------------------------- */ + OSRCleanup(); + +/* -------------------------------------------------------------------- */ +/* Cleanup VSIFileManager. */ +/* -------------------------------------------------------------------- */ + VSICleanupFileManager(); + +/* -------------------------------------------------------------------- */ +/* Cleanup thread local storage ... I hope the program is all */ +/* done with GDAL/OGR! */ +/* -------------------------------------------------------------------- */ + CPLCleanupTLS(); + +/* -------------------------------------------------------------------- */ +/* Cleanup our mutex. */ +/* -------------------------------------------------------------------- */ + if( hDMMutex ) + { + CPLDestroyMutex( hDMMutex ); + hDMMutex = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup dataset list mutex */ +/* -------------------------------------------------------------------- */ + if ( *GDALGetphDLMutex() != NULL ) + { + CPLDestroyMutex( *GDALGetphDLMutex() ); + *GDALGetphDLMutex() = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup raster block mutex */ +/* -------------------------------------------------------------------- */ + GDALRasterBlock::DestroyRBMutex(); + +/* -------------------------------------------------------------------- */ +/* Cleanup gdaltransformer.cpp mutex */ +/* -------------------------------------------------------------------- */ + GDALCleanupTransformDeserializerMutex(); + +/* -------------------------------------------------------------------- */ +/* Cleanup cpl_error.cpp mutex */ +/* -------------------------------------------------------------------- */ + CPLCleanupErrorMutex(); + +/* -------------------------------------------------------------------- */ +/* Cleanup CPLsetlocale mutex */ +/* -------------------------------------------------------------------- */ + CPLCleanupSetlocaleMutex(); + +/* -------------------------------------------------------------------- */ +/* Cleanup the master CPL mutex, which governs the creation */ +/* of all other mutexes. */ +/* -------------------------------------------------------------------- */ + CPLCleanupMasterMutex(); + +/* -------------------------------------------------------------------- */ +/* Ensure the global driver manager pointer is NULLed out. */ +/* -------------------------------------------------------------------- */ + if( poDM == this ) + poDM = NULL; +} + +/************************************************************************/ +/* GetDriverCount() */ +/************************************************************************/ + +/** + * \brief Fetch the number of registered drivers. + * + * This C analog to this is GDALGetDriverCount(). + * + * @return the number of registered drivers. + */ + +int GDALDriverManager::GetDriverCount() + +{ + return( nDrivers ); +} + +/************************************************************************/ +/* GDALGetDriverCount() */ +/************************************************************************/ + +/** + * \brief Fetch the number of registered drivers. + * + * @see GDALDriverManager::GetDriverCount() + */ + +int CPL_STDCALL GDALGetDriverCount() + +{ + return GetGDALDriverManager()->GetDriverCount(); +} + +/************************************************************************/ +/* GetDriver() */ +/************************************************************************/ + +/** + * \brief Fetch driver by index. + * + * This C analog to this is GDALGetDriver(). + * + * @param iDriver the driver index from 0 to GetDriverCount()-1. + * + * @return the driver identified by the index or NULL if the index is invalid + */ + +GDALDriver * GDALDriverManager::GetDriver( int iDriver ) + +{ + CPLMutexHolderD( &hDMMutex ); + + return GetDriver_unlocked(iDriver); +} + +/************************************************************************/ +/* GDALGetDriver() */ +/************************************************************************/ + +/** + * \brief Fetch driver by index. + * + * @see GDALDriverManager::GetDriver() + */ + +GDALDriverH CPL_STDCALL GDALGetDriver( int iDriver ) + +{ + return (GDALDriverH) GetGDALDriverManager()->GetDriver(iDriver); +} + +/************************************************************************/ +/* RegisterDriver() */ +/************************************************************************/ + +/** + * \brief Register a driver for use. + * + * The C analog is GDALRegisterDriver(). + * + * Normally this method is used by format specific C callable registration + * entry points such as GDALRegister_GTiff() rather than being called + * directly by application level code. + * + * If this driver (based on the object pointer, not short name) is already + * registered, then no change is made, and the index of the existing driver + * is returned. Otherwise the driver list is extended, and the new driver + * is added at the end. + * + * @param poDriver the driver to register. + * + * @return the index of the new installed driver. + */ + +int GDALDriverManager::RegisterDriver( GDALDriver * poDriver ) + +{ + CPLMutexHolderD( &hDMMutex ); + +/* -------------------------------------------------------------------- */ +/* If it is already registered, just return the existing */ +/* index. */ +/* -------------------------------------------------------------------- */ + if( GetDriverByName_unlocked( poDriver->GetDescription() ) != NULL ) + { + int i; + + for( i = 0; i < nDrivers; i++ ) + { + if( papoDrivers[i] == poDriver ) + { + return i; + } + } + + CPLAssert( FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise grow the list to hold the new entry. */ +/* -------------------------------------------------------------------- */ + papoDrivers = (GDALDriver **) + VSIRealloc(papoDrivers, sizeof(GDALDriver *) * (nDrivers+1)); + + papoDrivers[nDrivers] = poDriver; + nDrivers++; + + if( poDriver->pfnOpen != NULL || + poDriver->pfnOpenWithDriverArg != NULL ) + poDriver->SetMetadataItem( GDAL_DCAP_OPEN, "YES" ); + + if( poDriver->pfnCreate != NULL ) + poDriver->SetMetadataItem( GDAL_DCAP_CREATE, "YES" ); + + if( poDriver->pfnCreateCopy != NULL ) + poDriver->SetMetadataItem( GDAL_DCAP_CREATECOPY, "YES" ); + + /* Backward compability for GDAL raster out-of-tree drivers: */ + /* if a driver hasn't explicitly set a vector capability, assume it is */ + /* a raster driver (legacy OGR drivers will have DCAP_VECTOR set before */ + /* calling RegisterDriver() ) */ + if( poDriver->GetMetadataItem( GDAL_DCAP_RASTER ) == NULL && + poDriver->GetMetadataItem( GDAL_DCAP_VECTOR ) == NULL ) + { + CPLDebug("GDAL", "Assuming DCAP_RASTER for driver %s. Please fix it.", + poDriver->GetDescription() ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + } + + if( poDriver->GetMetadataItem( GDAL_DMD_OPENOPTIONLIST ) != NULL && + poDriver->pfnIdentify == NULL && + !EQUALN(poDriver->GetDescription(), "Interlis", strlen("Interlis")) ) + { + CPLDebug("GDAL", "Driver %s that defines GDAL_DMD_OPENOPTIONLIST must also " + "implement Identify(), so that it can be used", + poDriver->GetDescription() ); + } + + oMapNameToDrivers[CPLString(poDriver->GetDescription()).toupper()] = poDriver; + + int iResult = nDrivers - 1; + + return iResult; +} + +/************************************************************************/ +/* GDALRegisterDriver() */ +/************************************************************************/ + +/** + * \brief Register a driver for use. + * + * @see GDALDriverManager::GetRegisterDriver() + */ + +int CPL_STDCALL GDALRegisterDriver( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER1( hDriver, "GDALRegisterDriver", 0 ); + + return GetGDALDriverManager()->RegisterDriver( (GDALDriver *) hDriver ); +} + + +/************************************************************************/ +/* DeregisterDriver() */ +/************************************************************************/ + +/** + * \brief Deregister the passed driver. + * + * If the driver isn't found no change is made. + * + * The C analog is GDALDeregisterDriver(). + * + * @param poDriver the driver to deregister. + */ + +void GDALDriverManager::DeregisterDriver( GDALDriver * poDriver ) + +{ + int i; + CPLMutexHolderD( &hDMMutex ); + + for( i = 0; i < nDrivers; i++ ) + { + if( papoDrivers[i] == poDriver ) + break; + } + + if( i == nDrivers ) + return; + + oMapNameToDrivers.erase(CPLString(poDriver->GetDescription()).toupper()); + while( i < nDrivers-1 ) + { + papoDrivers[i] = papoDrivers[i+1]; + i++; + } + nDrivers--; +} + +/************************************************************************/ +/* GDALDeregisterDriver() */ +/************************************************************************/ + +/** + * \brief Deregister the passed driver. + * + * @see GDALDriverManager::GetDeregisterDriver() + */ + +void CPL_STDCALL GDALDeregisterDriver( GDALDriverH hDriver ) + +{ + VALIDATE_POINTER0( hDriver, "GDALDeregisterDriver" ); + + GetGDALDriverManager()->DeregisterDriver( (GDALDriver *) hDriver ); +} + + +/************************************************************************/ +/* GetDriverByName() */ +/************************************************************************/ + +/** + * \brief Fetch a driver based on the short name. + * + * The C analog is the GDALGetDriverByName() function. + * + * @param pszName the short name, such as GTiff, being searched for. + * + * @return the identified driver, or NULL if no match is found. + */ + +GDALDriver * GDALDriverManager::GetDriverByName( const char * pszName ) + +{ + CPLMutexHolderD( &hDMMutex ); + + return oMapNameToDrivers[CPLString(pszName).toupper()]; +} + +/************************************************************************/ +/* GDALGetDriverByName() */ +/************************************************************************/ + +/** + * \brief Fetch a driver based on the short name. + * + * @see GDALDriverManager::GetDriverByName() + */ + +GDALDriverH CPL_STDCALL GDALGetDriverByName( const char * pszName ) + +{ + VALIDATE_POINTER1( pszName, "GDALGetDriverByName", NULL ); + + return( GetGDALDriverManager()->GetDriverByName( pszName ) ); +} + +/************************************************************************/ +/* AutoSkipDrivers() */ +/************************************************************************/ + +/** + * \brief This method unload undesirable drivers. + * + * All drivers specified in the comma delimited list in the GDAL_SKIP + * environment variable) will be deregistered and destroyed. This method + * should normally be called after registration of standard drivers to allow + * the user a way of unloading undesired drivers. The GDALAllRegister() + * function already invokes AutoSkipDrivers() at the end, so if that functions + * is called, it should not be necessary to call this method from application + * code. + * + * Note: space separator is also accepted for backward compatibility, but some + * vector formats have spaces in their names, so it is encouraged to use comma + * to avoid issues. + */ + +void GDALDriverManager::AutoSkipDrivers() + +{ + char **apapszList[2] = { NULL, NULL }; + const char* pszGDAL_SKIP = CPLGetConfigOption( "GDAL_SKIP", NULL ); + if( pszGDAL_SKIP != NULL ) + { + /* Favour comma as a separator. If not found, then use space */ + const char* pszSep = (strchr(pszGDAL_SKIP, ',') != NULL) ? "," : " "; + apapszList[0] = CSLTokenizeStringComplex( pszGDAL_SKIP, pszSep, FALSE, FALSE); + } + const char* pszOGR_SKIP = CPLGetConfigOption( "OGR_SKIP", NULL ); + if( pszOGR_SKIP != NULL ) + { + /* OGR has always used comma as a separator */ + apapszList[1] = CSLTokenizeStringComplex(pszOGR_SKIP, ",", FALSE, FALSE); + } + + for( int j = 0; j < 2; j++ ) + { + for( int i = 0; apapszList[j] != NULL && apapszList[j][i] != NULL; i++ ) + { + GDALDriver *poDriver = GetDriverByName( apapszList[j][i] ); + + if( poDriver == NULL ) + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to find driver %s to unload from GDAL_SKIP environment variable.", + apapszList[j][i] ); + else + { + CPLDebug( "GDAL", "AutoSkipDriver(%s)", apapszList[j][i] ); + DeregisterDriver( poDriver ); + delete poDriver; + } + } + } + + CSLDestroy( apapszList[0] ); + CSLDestroy( apapszList[1] ); +} + +/************************************************************************/ +/* AutoLoadDrivers() */ +/************************************************************************/ + +/** + * \brief Auto-load GDAL drivers from shared libraries. + * + * This function will automatically load drivers from shared libraries. It + * searches the "driver path" for .so (or .dll) files that start with the + * prefix "gdal_X.so". It then tries to load them and then tries to call a + * function within them called GDALRegister_X() where the 'X' is the same as + * the remainder of the shared library basename ('X' is case sensitive), or + * failing that to call GDALRegisterMe(). + * + * There are a few rules for the driver path. If the GDAL_DRIVER_PATH + * environment variable it set, it is taken to be a list of directories to + * search separated by colons on UNIX, or semi-colons on Windows. Otherwise + * the /usr/local/lib/gdalplugins directory, and (if known) the + * lib/gdalplugins subdirectory of the gdal home directory are searched on + * UNIX and $(BINDIR)\gdalplugins on Windows. + * + * Auto loading can be completely disabled by setting the GDAL_DRIVER_PATH + * config option to "disable". + */ + +void GDALDriverManager::AutoLoadDrivers() + +{ + char **papszSearchPath = NULL; + const char *pszGDAL_DRIVER_PATH = + CPLGetConfigOption( "GDAL_DRIVER_PATH", NULL ); + if( pszGDAL_DRIVER_PATH == NULL ) + pszGDAL_DRIVER_PATH = CPLGetConfigOption( "OGR_DRIVER_PATH", NULL ); + +/* -------------------------------------------------------------------- */ +/* Allow applications to completely disable this search by */ +/* setting the driver path to the special string "disable". */ +/* -------------------------------------------------------------------- */ + if( pszGDAL_DRIVER_PATH != NULL && EQUAL(pszGDAL_DRIVER_PATH,"disable")) + { + CPLDebug( "GDAL", "GDALDriverManager::AutoLoadDrivers() disabled." ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Where should we look for stuff? */ +/* -------------------------------------------------------------------- */ + if( pszGDAL_DRIVER_PATH != NULL ) + { +#ifdef WIN32 + papszSearchPath = + CSLTokenizeStringComplex( pszGDAL_DRIVER_PATH, ";", TRUE, FALSE ); +#else + papszSearchPath = + CSLTokenizeStringComplex( pszGDAL_DRIVER_PATH, ":", TRUE, FALSE ); +#endif + } + else + { +#ifdef GDAL_PREFIX + papszSearchPath = CSLAddString( papszSearchPath, + #ifdef MACOSX_FRAMEWORK + GDAL_PREFIX "/PlugIns"); + #else + GDAL_PREFIX "/lib/gdalplugins" ); + #endif +#else + char szExecPath[1024]; + + if( CPLGetExecPath( szExecPath, sizeof(szExecPath) ) ) + { + char szPluginDir[sizeof(szExecPath)+50]; + strcpy( szPluginDir, CPLGetDirname( szExecPath ) ); + strcat( szPluginDir, "\\gdalplugins" ); + papszSearchPath = CSLAddString( papszSearchPath, szPluginDir ); + } + else + { + papszSearchPath = CSLAddString( papszSearchPath, + "/usr/local/lib/gdalplugins" ); + } +#endif + + #ifdef MACOSX_FRAMEWORK + #define num2str(x) str(x) + #define str(x) #x + papszSearchPath = CSLAddString( papszSearchPath, + "/Library/Application Support/GDAL/" + num2str(GDAL_VERSION_MAJOR) "." + num2str(GDAL_VERSION_MINOR) "/PlugIns" ); + #endif + + } + +/* -------------------------------------------------------------------- */ +/* Format the ABI version specific subdirectory to look in. */ +/* -------------------------------------------------------------------- */ + CPLString osABIVersion; + + osABIVersion.Printf( "%d.%d", GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR ); + +/* -------------------------------------------------------------------- */ +/* Scan each directory looking for files starting with gdal_ */ +/* -------------------------------------------------------------------- */ + for( int iDir = 0; iDir < CSLCount(papszSearchPath); iDir++ ) + { + char **papszFiles = NULL; + VSIStatBufL sStatBuf; + CPLString osABISpecificDir = + CPLFormFilename( papszSearchPath[iDir], osABIVersion, NULL ); + + if( VSIStatL( osABISpecificDir, &sStatBuf ) != 0 ) + osABISpecificDir = papszSearchPath[iDir]; + + papszFiles = CPLReadDir( osABISpecificDir ); + int nFileCount = CSLCount(papszFiles); + + for( int iFile = 0; iFile < nFileCount; iFile++ ) + { + char *pszFuncName; + const char *pszFilename; + const char *pszExtension = CPLGetExtension( papszFiles[iFile] ); + void *pRegister; + + if( !EQUAL(pszExtension,"dll") + && !EQUAL(pszExtension,"so") + && !EQUAL(pszExtension,"dylib") ) + continue; + + if( EQUALN(papszFiles[iFile],"gdal_",strlen("gdal_")) ) + { + pszFuncName = (char *) CPLCalloc(strlen(papszFiles[iFile])+20,1); + sprintf( pszFuncName, "GDALRegister_%s", + CPLGetBasename(papszFiles[iFile]) + strlen("gdal_") ); + } + else if ( EQUALN(papszFiles[iFile],"ogr_",strlen("ogr_")) ) + { + pszFuncName = (char *) CPLCalloc(strlen(papszFiles[iFile])+20,1); + sprintf( pszFuncName, "RegisterOGR%s", + CPLGetBasename(papszFiles[iFile]) + strlen("ogr_") ); + } + else + continue; + + pszFilename = + CPLFormFilename( osABISpecificDir, + papszFiles[iFile], NULL ); + + CPLErrorReset(); + CPLPushErrorHandler(CPLQuietErrorHandler); + pRegister = CPLGetSymbol( pszFilename, pszFuncName ); + CPLPopErrorHandler(); + if( pRegister == NULL ) + { + CPLString osLastErrorMsg(CPLGetLastErrorMsg()); + strcpy( pszFuncName, "GDALRegisterMe" ); + pRegister = CPLGetSymbol( pszFilename, pszFuncName ); + if( pRegister == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", osLastErrorMsg.c_str() ); + } + } + + if( pRegister != NULL ) + { + CPLDebug( "GDAL", "Auto register %s using %s.", + pszFilename, pszFuncName ); + + ((void (*)()) pRegister)(); + } + + CPLFree( pszFuncName ); + } + + CSLDestroy( papszFiles ); + } + + CSLDestroy( papszSearchPath ); +} + +/************************************************************************/ +/* GDALDestroyDriverManager() */ +/************************************************************************/ + +/** + * \brief Destroy the driver manager. + * + * Incidently unloads all managed drivers. + * + * NOTE: This function is not thread safe. It should not be called while + * other threads are actively using GDAL. + */ + +void CPL_STDCALL GDALDestroyDriverManager( void ) + +{ + // THREADSAFETY: We would like to lock the mutex here, but it + // needs to be reacquired within the destructor during driver + // deregistration. + if( poDM != NULL ) + delete poDM; +} + + diff --git a/bazaar/plugin/gdal/gcore/gdalexif.cpp b/bazaar/plugin/gdal/gcore/gdalexif.cpp new file mode 100644 index 000000000..f98573d4d --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalexif.cpp @@ -0,0 +1,464 @@ +/****************************************************************************** + * $Id: gdalexif.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: GDAL + * Purpose: Implements a EXIF directory reader + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2012, Even Rouault + * + * Portions Copyright (c) Her majesty the Queen in right of Canada as + * represented by the Minister of National Defence, 2006. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_port.h" +#include "cpl_error.h" +#include "cpl_string.h" +#include "cpl_vsi.h" + +#include "gdalexif.h" + +CPL_CVSID("$Id: gdalexif.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* EXIFPrintData() */ +/************************************************************************/ +static void EXIFPrintData(char* pszData, GUInt16 type, + GUInt32 count, unsigned char* data) +{ + const char* sep = ""; + char pszTemp[128]; + char* pszDataEnd = pszData; + + pszData[0]='\0'; + + switch (type) { + + case TIFF_UNDEFINED: + case TIFF_BYTE: + for(;count>0;count--) { + sprintf(pszTemp, "%s%#02x", sep, *data++), sep = " "; + if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) + break; + strcat(pszDataEnd,pszTemp); + pszDataEnd += strlen(pszDataEnd); + } + break; + + case TIFF_SBYTE: + for(;count>0;count--) { + sprintf(pszTemp, "%s%d", sep, *(char *)data++), sep = " "; + if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) + break; + strcat(pszDataEnd,pszTemp); + pszDataEnd += strlen(pszDataEnd); + } + break; + + case TIFF_ASCII: + memcpy( pszData, data, count ); + pszData[count] = '\0'; + break; + + case TIFF_SHORT: { + register GUInt16 *wp = (GUInt16*)data; + for(;count>0;count--) { + sprintf(pszTemp, "%s%u", sep, *wp++), sep = " "; + if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) + break; + strcat(pszDataEnd,pszTemp); + pszDataEnd += strlen(pszDataEnd); + } + break; + } + case TIFF_SSHORT: { + register GInt16 *wp = (GInt16*)data; + for(;count>0;count--) { + sprintf(pszTemp, "%s%d", sep, *wp++); + sep = " "; + if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) + break; + strcat(pszDataEnd,pszTemp); + pszDataEnd += strlen(pszDataEnd); + } + break; + } + case TIFF_LONG: { + register GUInt32 *lp = (GUInt32*)data; + for(;count>0;count--) { + sprintf(pszTemp, "%s%lu", sep, (unsigned long) *lp++); + sep = " "; + if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) + break; + strcat(pszDataEnd,pszTemp); + pszDataEnd += strlen(pszDataEnd); + } + break; + } + case TIFF_SLONG: { + register GInt32 *lp = (GInt32*)data; + for(;count>0;count--) { + sprintf(pszTemp, "%s%ld", sep, (long) *lp++), sep = " "; + if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) + break; + strcat(pszDataEnd,pszTemp); + pszDataEnd += strlen(pszDataEnd); + } + break; + } + case TIFF_RATIONAL: { + register GUInt32 *lp = (GUInt32*)data; + // if(bSwabflag) + // TIFFSwabArrayOfLong((GUInt32*) data, 2*count); + for(;count>0;count--) { + if( (lp[0]==0) && (lp[1] == 0) ) { + sprintf(pszTemp,"%s(0)",sep); + } + else{ + CPLsprintf(pszTemp, "%s(%g)", sep, + (double) lp[0]/ (double)lp[1]); + } + sep = " "; + lp += 2; + if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) + break; + strcat(pszDataEnd,pszTemp); + pszDataEnd += strlen(pszDataEnd); + } + break; + } + case TIFF_SRATIONAL: { + register GInt32 *lp = (GInt32*)data; + for(;count>0;count--) { + CPLsprintf(pszTemp, "%s(%g)", sep, + (float) lp[0]/ (float) lp[1]); + sep = " "; + lp += 2; + if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) + break; + strcat(pszDataEnd,pszTemp); + pszDataEnd += strlen(pszDataEnd); + } + break; + } + case TIFF_FLOAT: { + register float *fp = (float *)data; + for(;count>0;count--) { + CPLsprintf(pszTemp, "%s%g", sep, *fp++), sep = " "; + if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) + break; + strcat(pszDataEnd,pszTemp); + pszDataEnd += strlen(pszDataEnd); + } + break; + } + case TIFF_DOUBLE: { + register double *dp = (double *)data; + for(;count>0;count--) { + CPLsprintf(pszTemp, "%s%g", sep, *dp++), sep = " "; + if (strlen(pszTemp) + pszDataEnd - pszData >= MAXSTRINGLENGTH) + break; + strcat(pszDataEnd,pszTemp); + pszDataEnd += strlen(pszDataEnd); + } + break; + } + + default: + return; + } + + if (type != TIFF_ASCII && count != 0) + { + CPLError(CE_Warning, CPLE_AppDefined, "EXIF metadata truncated"); + } +} + + +/************************************************************************/ +/* EXIFExtractMetadata() */ +/* */ +/* Extract all entry from a IFD */ +/************************************************************************/ +CPLErr EXIFExtractMetadata(char**& papszMetadata, + void *fpInL, int nOffset, + int bSwabflag, int nTIFFHEADER, + int& nExifOffset, int& nInterOffset, int& nGPSOffset) +{ + GUInt16 nEntryCount; + int space; + unsigned int n,i; + char pszTemp[MAXSTRINGLENGTH]; + char pszName[128]; + + VSILFILE* fp = (VSILFILE* )fpInL; + + TIFFDirEntry *poTIFFDirEntry; + TIFFDirEntry *poTIFFDir; + const struct tagname *poExifTags ; + const struct intr_tag *poInterTags = intr_tags; + const struct gpsname *poGPSTags; + +/* -------------------------------------------------------------------- */ +/* Read number of entry in directory */ +/* -------------------------------------------------------------------- */ + if( VSIFSeekL(fp, nOffset+nTIFFHEADER, SEEK_SET) != 0 + || VSIFReadL(&nEntryCount,1,sizeof(GUInt16),fp) != sizeof(GUInt16) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error reading EXIF Directory count at %d.", + nOffset + nTIFFHEADER ); + return CE_Failure; + } + + if (bSwabflag) + TIFFSwabShort(&nEntryCount); + + // Some apps write empty directories - see bug 1523. + if( nEntryCount == 0 ) + return CE_None; + + // Some files are corrupt, a large entry count is a sign of this. + if( nEntryCount > 125 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Ignoring EXIF directory with unlikely entry count (%d).", + nEntryCount ); + return CE_Warning; + } + + poTIFFDir = (TIFFDirEntry *)CPLMalloc(nEntryCount * sizeof(TIFFDirEntry)); + +/* -------------------------------------------------------------------- */ +/* Read all directory entries */ +/* -------------------------------------------------------------------- */ + n = VSIFReadL(poTIFFDir, 1,nEntryCount*sizeof(TIFFDirEntry),fp); + if (n != nEntryCount*sizeof(TIFFDirEntry)) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Could not read all directories"); + CPLFree(poTIFFDir); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Parse all entry information in this directory */ +/* -------------------------------------------------------------------- */ + for(poTIFFDirEntry = poTIFFDir,i=nEntryCount; i > 0; i--,poTIFFDirEntry++) { + if (bSwabflag) { + TIFFSwabShort(&poTIFFDirEntry->tdir_tag); + TIFFSwabShort(&poTIFFDirEntry->tdir_type); + TIFFSwabLong (&poTIFFDirEntry->tdir_count); + TIFFSwabLong (&poTIFFDirEntry->tdir_offset); + } + +/* -------------------------------------------------------------------- */ +/* Find Tag name in table */ +/* -------------------------------------------------------------------- */ + pszName[0] = '\0'; + pszTemp[0] = '\0'; + + for (poExifTags = tagnames; poExifTags->tag; poExifTags++) + if(poExifTags->tag == poTIFFDirEntry->tdir_tag) { + CPLAssert( NULL != poExifTags && NULL != poExifTags->name ); + + strcpy(pszName, poExifTags->name); + break; + } + + + if( nOffset == nGPSOffset) { + for( poGPSTags = gpstags; poGPSTags->tag != 0xffff; poGPSTags++ ) + if( poGPSTags->tag == poTIFFDirEntry->tdir_tag ) { + CPLAssert( NULL != poGPSTags && NULL != poGPSTags->name ); + strcpy(pszName, poGPSTags->name); + break; + } + } +/* -------------------------------------------------------------------- */ +/* If the tag was not found, look into the interoperability table */ +/* -------------------------------------------------------------------- */ + if( nOffset == nInterOffset ) { + for(poInterTags = intr_tags; poInterTags->tag; poInterTags++) + if(poInterTags->tag == poTIFFDirEntry->tdir_tag) { + CPLAssert( NULL != poInterTags && NULL != poInterTags->name ); + strcpy(pszName, poInterTags->name); + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Save important directory tag offset */ +/* -------------------------------------------------------------------- */ + if( poTIFFDirEntry->tdir_tag == EXIFOFFSETTAG ) + nExifOffset=poTIFFDirEntry->tdir_offset; + if( poTIFFDirEntry->tdir_tag == INTEROPERABILITYOFFSET ) + nInterOffset=poTIFFDirEntry->tdir_offset; + if( poTIFFDirEntry->tdir_tag == GPSOFFSETTAG ) { + nGPSOffset=poTIFFDirEntry->tdir_offset; + } + +/* -------------------------------------------------------------------- */ +/* If we didn't recognise the tag just ignore it. To see all */ +/* tags comment out the continue. */ +/* -------------------------------------------------------------------- */ + if( pszName[0] == '\0' ) + { + sprintf( pszName, "EXIF_%d", poTIFFDirEntry->tdir_tag ); + continue; + } + +/* -------------------------------------------------------------------- */ +/* For UserComment we need to ignore the language binding and */ +/* just return the actual contents. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszName,"EXIF_UserComment") ) + { + poTIFFDirEntry->tdir_type = TIFF_ASCII; + + if( poTIFFDirEntry->tdir_count >= 8 ) + { + poTIFFDirEntry->tdir_count -= 8; + poTIFFDirEntry->tdir_offset += 8; + } + } + +/* -------------------------------------------------------------------- */ +/* Make some UNDEFINED or BYTE fields ASCII for readability. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszName,"EXIF_ExifVersion") + || EQUAL(pszName,"EXIF_FlashPixVersion") + || EQUAL(pszName,"EXIF_MakerNote") + || EQUAL(pszName,"GPSProcessingMethod") ) + poTIFFDirEntry->tdir_type = TIFF_ASCII; + +/* -------------------------------------------------------------------- */ +/* Print tags */ +/* -------------------------------------------------------------------- */ + int nDataWidth = TIFFDataWidth((TIFFDataType) poTIFFDirEntry->tdir_type); + space = poTIFFDirEntry->tdir_count * nDataWidth; + + /* Previous multiplication could overflow, hence this additional check */ + if (poTIFFDirEntry->tdir_count > MAXSTRINGLENGTH) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Too many bytes in tag: %u, ignoring tag.", + poTIFFDirEntry->tdir_count ); + } + else if (nDataWidth == 0 || poTIFFDirEntry->tdir_type >= TIFF_IFD ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Invalid or unhandled EXIF data type: %d, ignoring tag.", + poTIFFDirEntry->tdir_type ); + } +/* -------------------------------------------------------------------- */ +/* This is at most 4 byte data so we can read it from tdir_offset */ +/* -------------------------------------------------------------------- */ + else if (space >= 0 && space <= 4) { + + unsigned char data[4]; + memcpy(data, &poTIFFDirEntry->tdir_offset, 4); + if (bSwabflag) + { + // Unswab 32bit value, and reswab per data type. + TIFFSwabLong((GUInt32*) data); + + switch (poTIFFDirEntry->tdir_type) { + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_FLOAT: + TIFFSwabLong((GUInt32*) data); + break; + + case TIFF_SSHORT: + case TIFF_SHORT: + TIFFSwabArrayOfShort((GUInt16*) data, + poTIFFDirEntry->tdir_count); + break; + + default: + break; + } + } + + EXIFPrintData(pszTemp, + poTIFFDirEntry->tdir_type, + poTIFFDirEntry->tdir_count, data); + } +/* -------------------------------------------------------------------- */ +/* The data is being read where tdir_offset point to in the file */ +/* -------------------------------------------------------------------- */ + else if (space > 0 && space < MAXSTRINGLENGTH) + { + unsigned char *data = (unsigned char *)VSIMalloc(space); + + if (data) { + VSIFSeekL(fp,poTIFFDirEntry->tdir_offset+nTIFFHEADER,SEEK_SET); + VSIFReadL(data, 1, space, fp); + + if (bSwabflag) { + switch (poTIFFDirEntry->tdir_type) { + case TIFF_SHORT: + case TIFF_SSHORT: + TIFFSwabArrayOfShort((GUInt16*) data, + poTIFFDirEntry->tdir_count); + break; + case TIFF_LONG: + case TIFF_SLONG: + case TIFF_FLOAT: + TIFFSwabArrayOfLong((GUInt32*) data, + poTIFFDirEntry->tdir_count); + break; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + TIFFSwabArrayOfLong((GUInt32*) data, + 2*poTIFFDirEntry->tdir_count); + break; + case TIFF_DOUBLE: + TIFFSwabArrayOfDouble((double*) data, + poTIFFDirEntry->tdir_count); + break; + default: + break; + } + } + + EXIFPrintData(pszTemp, poTIFFDirEntry->tdir_type, + poTIFFDirEntry->tdir_count, data); + CPLFree(data); + } + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Invalid EXIF header size: %ld, ignoring tag.", + (long) space ); + } + + papszMetadata = CSLSetNameValue(papszMetadata, pszName, pszTemp); + } + CPLFree(poTIFFDir); + + return CE_None; +} diff --git a/bazaar/plugin/gdal/gcore/gdalexif.h b/bazaar/plugin/gdal/gcore/gdalexif.h new file mode 100644 index 000000000..91f5740fb --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalexif.h @@ -0,0 +1,241 @@ +/****************************************************************************** + * $Id: gdalexif.h 24549 2012-06-09 20:14:14Z rouault $ + * + * Project: JPEG JFIF Driver + * Purpose: Implement GDAL JPEG Support based on IJG libjpeg. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#define EXIFOFFSETTAG 0x8769 +#define INTEROPERABILITYOFFSET 0xA005 +#define GPSOFFSETTAG 0x8825 +#define MAXSTRINGLENGTH 65535 + +#ifdef RENAME_INTERNAL_LIBTIFF_SYMBOLS +#include "../frmts/gtiff/libtiff/gdal_libtiff_symbol_rename.h" +#endif + +static const struct gpsname { + GUInt16 tag; + const char* name; +} gpstags [] = { + { 0x00, "EXIF_GPSVersionID" }, + { 0x01, "EXIF_GPSLatitudeRef" }, + { 0x02, "EXIF_GPSLatitude" }, + { 0x03, "EXIF_GPSLongitudeRef" }, + { 0x04, "EXIF_GPSLongitude" }, + { 0x05, "EXIF_GPSAltitudeRef" }, + { 0x06, "EXIF_GPSAltitude" }, + { 0x07, "EXIF_GPSTimeStamp" }, + { 0x08, "EXIF_GPSSatellites" }, + { 0x09, "EXIF_GPSStatus" }, + { 0x0a, "EXIF_GPSMeasureMode" }, + { 0x0b, "EXIF_GPSDOP" }, + { 0x0c, "EXIF_GPSSpeedRef"}, + { 0x0d, "EXIF_GPSSpeed"}, + { 0x0e, "EXIF_GPSTrackRef"}, + { 0x0f, "EXIF_GPSTrack"}, + { 0x10, "EXIF_GPSImgDirectionRef"}, + { 0x11, "EXIF_GPSImgDirection"}, + { 0x12, "EXIF_GPSMapDatum"}, + { 0x13, "EXIF_GPSDestLatitudeRef"}, + { 0x14, "EXIF_GPSDestLatitude"}, + { 0x15, "EXIF_GPSDestLongitudeRef"}, + { 0x16, "EXIF_GPSDestLongitude"}, + { 0x17, "EXIF_GPSDestBearingRef"}, + { 0x18, "EXIF_GPSDestBearing"}, + { 0x19, "EXIF_GPSDestDistanceRef"}, + { 0x1a, "EXIF_GPSDestDistance"}, + { 0x1b, "EXIF_GPSProcessingMethod"}, + { 0x1c, "EXIF_GPSAreaInformation"}, + { 0x1d, "EXIF_GPSDateStamp"}, + { 0x1e, "EXIF_GPSDifferential"}, + { 0xffff, ""} +}; + +static const struct tagname { + GUInt16 tag; + const char* name; +} tagnames [] = { + +// { 0x100, "EXIF_Image_Width"}, +// { 0x101, "EXIF_Image_Length"}, + { 0x102, "EXIF_BitsPerSample"}, + { 0x103, "EXIF_Compression"}, + { 0x106, "EXIF_PhotometricInterpretation"}, + { 0x10A, "EXIF_Fill_Order"}, + { 0x10D, "EXIF_Document_Name"}, + { 0x10E, "EXIF_ImageDescription"}, + { 0x10F, "EXIF_Make"}, + { 0x110, "EXIF_Model"}, + { 0x111, "EXIF_StripOffsets"}, + { 0x112, "EXIF_Orientation"}, + { 0x115, "EXIF_SamplesPerPixel"}, + { 0x116, "EXIF_RowsPerStrip"}, + { 0x117, "EXIF_StripByteCounts"}, + { 0x11A, "EXIF_XResolution"}, + { 0x11B, "EXIF_YResolution"}, + { 0x11C, "EXIF_PlanarConfiguration"}, + { 0x128, "EXIF_ResolutionUnit"}, + { 0x12D, "EXIF_TransferFunction"}, + { 0x131, "EXIF_Software"}, + { 0x132, "EXIF_DateTime"}, + { 0x13B, "EXIF_Artist"}, + { 0x13E, "EXIF_WhitePoint"}, + { 0x13F, "EXIF_PrimaryChromaticities"}, + { 0x156, "EXIF_Transfer_Range"}, + { 0x200, "EXIF_JPEG_Proc"}, + { 0x201, "EXIF_JPEGInterchangeFormat"}, + { 0x202, "EXIF_JPEGInterchangeFormatLength"}, + { 0x211, "EXIF_YCbCrCoefficients"}, + { 0x212, "EXIF_YCbCrSubSampling"}, + { 0x213, "EXIF_YCbCrPositioning"}, + { 0x214, "EXIF_ReferenceBlackWhite"}, + { 0x828D, "EXIF_CFA_Repeat_Pattern_Dim"}, + { 0x828E, "EXIF_CFA_Pattern"}, + { 0x828F, "EXIF_Battery_Level"}, + { 0x8298, "EXIF_Copyright"}, + { 0x829A, "EXIF_ExposureTime"}, + { 0x829D, "EXIF_FNumber"}, + { 0x83BB, "EXIF_IPTC/NAA"}, +// { 0x8769, "EXIF_Offset"}, + { 0x8773, "EXIF_Inter_Color_Profile"}, + { 0x8822, "EXIF_ExposureProgram"}, + { 0x8824, "EXIF_SpectralSensitivity"}, +// { 0x8825, "EXIF_GPSOffset"}, + { 0x8827, "EXIF_ISOSpeedRatings"}, + { 0x8828, "EXIF_OECF"}, + { 0x9000, "EXIF_ExifVersion"}, + { 0x9003, "EXIF_DateTimeOriginal"}, + { 0x9004, "EXIF_DateTimeDigitized"}, + { 0x9101, "EXIF_ComponentsConfiguration"}, + { 0x9102, "EXIF_CompressedBitsPerPixel"}, + { 0x9201, "EXIF_ShutterSpeedValue"}, + { 0x9202, "EXIF_ApertureValue"}, + { 0x9203, "EXIF_BrightnessValue"}, + { 0x9204, "EXIF_ExposureBiasValue"}, + { 0x9205, "EXIF_MaxApertureValue"}, + { 0x9206, "EXIF_SubjectDistance"}, + { 0x9207, "EXIF_MeteringMode"}, + { 0x9208, "EXIF_LightSource"}, + { 0x9209, "EXIF_Flash"}, + { 0x920A, "EXIF_FocalLength"}, + { 0x9214, "EXIF_SubjectArea"}, + { 0x927C, "EXIF_MakerNote"}, + { 0x9286, "EXIF_UserComment"}, + { 0x9290, "EXIF_SubSecTime"}, + { 0x9291, "EXIF_SubSecTime_Original"}, + { 0x9292, "EXIF_SubSecTime_Digitized"}, + { 0xA000, "EXIF_FlashpixVersion"}, + { 0xA001, "EXIF_ColorSpace"}, + { 0xA002, "EXIF_PixelXDimension"}, + { 0xA003, "EXIF_PixelYDimension"}, + { 0xA004, "EXIF_RelatedSoundFile"}, +// { 0xA005, "EXIF_InteroperabilityOffset"}, + { 0xA20B, "EXIF_FlashEnergy"}, // 0x920B in TIFF/EP + { 0xA20C, "EXIF_SpatialFrequencyResponse"}, // 0x920C - - + { 0xA20E, "EXIF_FocalPlaneXResolution"}, // 0x920E - - + { 0xA20F, "EXIF_FocalPlaneYResolution"}, // 0x920F - - + { 0xA210, "EXIF_FocalPlaneResolutionUnit"}, // 0x9210 - - + { 0xA214, "EXIF_SubjectLocation"}, // 0x9214 - - + { 0xA215, "EXIF_ExposureIndex"}, // 0x9215 - - + { 0xA217, "EXIF_SensingMethod"}, // 0x9217 - - + { 0xA300, "EXIF_FileSource"}, + { 0xA301, "EXIF_SceneType"}, + { 0xA302, "EXIF_CFAPattern"}, + { 0xA401, "EXIF_CustomRendered"}, + { 0xA402, "EXIF_ExposureMode"}, + { 0XA403, "EXIF_WhiteBalance"}, + { 0xA404, "EXIF_DigitalZoomRatio"}, + { 0xA405, "EXIF_FocalLengthIn35mmFilm"}, + { 0xA406, "EXIF_SceneCaptureType"}, + { 0xA407, "EXIF_GainControl"}, + { 0xA408, "EXIF_Contrast"}, + { 0xA409, "EXIF_Saturation"}, + { 0xA40A, "EXIF_Sharpness"}, + { 0xA40B, "EXIF_DeviceSettingDescription"}, + { 0xA40C, "EXIF_SubjectDistanceRange"}, + { 0xA420, "EXIF_ImageUniqueID"}, + { 0x0000, ""} +}; + + +static const struct intr_tag { + GInt16 tag; + const char* name; +} intr_tags [] = { + + { 0x1, "EXIF_Interoperability_Index"}, + { 0x2, "EXIF_Interoperability_Version"}, + { 0x1000, "EXIF_Related_Image_File_Format"}, + { 0x1001, "EXIF_Related_Image_Width"}, + { 0x1002, "EXIF_Related_Image_Length"}, + { 0x0000, ""} +}; + +typedef enum { + TIFF_NOTYPE = 0, /* placeholder */ + TIFF_BYTE = 1, /* 8-bit unsigned integer */ + TIFF_ASCII = 2, /* 8-bit bytes w/ last byte null */ + TIFF_SHORT = 3, /* 16-bit unsigned integer */ + TIFF_LONG = 4, /* 32-bit unsigned integer */ + TIFF_RATIONAL = 5, /* 64-bit unsigned fraction */ + TIFF_SBYTE = 6, /* !8-bit signed integer */ + TIFF_UNDEFINED = 7, /* !8-bit untyped data */ + TIFF_SSHORT = 8, /* !16-bit signed integer */ + TIFF_SLONG = 9, /* !32-bit signed integer */ + TIFF_SRATIONAL = 10, /* !64-bit signed fraction */ + TIFF_FLOAT = 11, /* !32-bit IEEE floating point */ + TIFF_DOUBLE = 12, /* !64-bit IEEE floating point */ + TIFF_IFD = 13 /* %32-bit unsigned integer (offset) */ +} TIFFDataType; + +/* + * TIFF Image File Directories are comprised of a table of field + * descriptors of the form shown below. The table is sorted in + * ascending order by tag. The values associated with each entry are + * disjoint and may appear anywhere in the file (so long as they are + * placed on a word boundary). + * + * If the value is 4 bytes or less, then it is placed in the offset + * field to save space. If the value is less than 4 bytes, it is + * left-justified in the offset field. + */ +typedef struct { + GUInt16 tdir_tag; /* see below */ + GUInt16 tdir_type; /* data type; see below */ + GUInt32 tdir_count; /* number of items; length in spec */ + GUInt32 tdir_offset; /* byte offset to field data */ +} TIFFDirEntry; + +CPL_C_START +extern int TIFFDataWidth(TIFFDataType); /* table of tag datatype widths */ +extern void TIFFSwabShort(GUInt16*); +extern void TIFFSwabLong(GUInt32*); +extern void TIFFSwabDouble(double*); +extern void TIFFSwabArrayOfShort(GUInt16*, unsigned long); +extern void TIFFSwabArrayOfLong(GUInt32*, unsigned long); +extern void TIFFSwabArrayOfDouble(double*, unsigned long); +CPL_C_END + diff --git a/bazaar/plugin/gdal/gcore/gdalgeorefpamdataset.cpp b/bazaar/plugin/gdal/gcore/gdalgeorefpamdataset.cpp new file mode 100644 index 000000000..5d8c4891d --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalgeorefpamdataset.cpp @@ -0,0 +1,158 @@ +/****************************************************************************** + * $Id: gdalgeorefpamdataset.cpp 26571 2013-10-30 10:59:11Z rouault $ + * + * Project: GDAL + * Purpose: GDALPamDataset with internal storage for georeferencing, with + * priority for PAM over internal georeferencing + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdalgeorefpamdataset.h" + +/************************************************************************/ +/* GDALGeorefPamDataset() */ +/************************************************************************/ + +GDALGeorefPamDataset::GDALGeorefPamDataset() +{ + pszProjection = NULL; + nGCPCount = 0; + pasGCPList = NULL; + bGeoTransformValid = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; +} + +/************************************************************************/ +/* ~GDALGeorefPamDataset() */ +/************************************************************************/ + +GDALGeorefPamDataset::~GDALGeorefPamDataset() +{ + CPLFree( pszProjection ); + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } +} + + +/************************************************************************/ +/* GetGCPCount() */ +/* */ +/* We let PAM coordinate system override the one stored inside */ +/* our file. */ +/************************************************************************/ + +int GDALGeorefPamDataset::GetGCPCount() + +{ + int nPamGCPCount = GDALPamDataset::GetGCPCount(); + if( nGCPCount > 0 && nPamGCPCount == 0) + return nGCPCount; + else + return nPamGCPCount; +} + +/************************************************************************/ +/* GetGCPProjection() */ +/* */ +/* We let PAM coordinate system override the one stored inside */ +/* our file. */ +/************************************************************************/ + +const char *GDALGeorefPamDataset::GetGCPProjection() + +{ + const char* pszPamPrj = GDALPamDataset::GetGCPProjection(); + if( pszProjection != NULL && strlen(pszPamPrj) == 0 ) + return pszProjection; + else + return pszPamPrj; +} + +/************************************************************************/ +/* GetGCP() */ +/* */ +/* We let PAM coordinate system override the one stored inside */ +/* our file. */ +/************************************************************************/ + +const GDAL_GCP *GDALGeorefPamDataset::GetGCPs() + +{ + int nPamGCPCount = GDALPamDataset::GetGCPCount(); + if( nGCPCount > 0 && nPamGCPCount == 0) + return pasGCPList; + else + return GDALPamDataset::GetGCPs(); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/* */ +/* We let PAM coordinate system override the one stored inside */ +/* our file. */ +/************************************************************************/ + +const char *GDALGeorefPamDataset::GetProjectionRef() + +{ + const char* pszPamPrj = GDALPamDataset::GetProjectionRef(); + + if( GetGCPCount() > 0 ) + return ""; + + if( pszProjection != NULL && strlen(pszPamPrj) == 0 ) + return pszProjection; + else + return pszPamPrj; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/* */ +/* Let the PAM geotransform override the native one if it is */ +/* available. */ +/************************************************************************/ + +CPLErr GDALGeorefPamDataset::GetGeoTransform( double * padfTransform ) + +{ + CPLErr eErr = GDALPamDataset::GetGeoTransform( padfTransform ); + + if( eErr != CE_None && bGeoTransformValid ) + { + memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); + return( CE_None ); + } + else + return eErr; +} diff --git a/bazaar/plugin/gdal/gcore/gdalgeorefpamdataset.h b/bazaar/plugin/gdal/gcore/gdalgeorefpamdataset.h new file mode 100644 index 000000000..de1298e1d --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalgeorefpamdataset.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * $Id: gdalgeorefpamdataset.h 26571 2013-10-30 10:59:11Z rouault $ + * + * Project: GDAL + * Purpose: GDALPamDataset with internal storage for georeferencing, with + * priority for PAM over internal georeferencing + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_GEOREF_PAM_DATASET_H_INCLUDED +#define GDAL_GEOREF_PAM_DATASET_H_INCLUDED + +#include "gdal_pam.h" + +class CPL_DLL GDALGeorefPamDataset : public GDALPamDataset +{ + protected: + int bGeoTransformValid; + double adfGeoTransform[6]; + char *pszProjection; + int nGCPCount; + GDAL_GCP *pasGCPList; + + public: + GDALGeorefPamDataset(); + ~GDALGeorefPamDataset(); + + virtual CPLErr GetGeoTransform( double * ); + virtual const char *GetProjectionRef(); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); +}; + +#endif /* GDAL_GEOREF_PAM_DATASET_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/gcore/gdalgmlcoverage.cpp b/bazaar/plugin/gdal/gcore/gdalgmlcoverage.cpp new file mode 100644 index 000000000..e861191cf --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalgmlcoverage.cpp @@ -0,0 +1,212 @@ +/****************************************************************************** + * $Id: gdalgmlcoverage.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: GDAL + * Purpose: Generic support for GML Coverage descriptions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2006, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_string.h" +#include "cpl_minixml.h" +#include "ogr_spatialref.h" +#include "ogr_geometry.h" +#include "ogr_api.h" + +CPL_CVSID("$Id: gdalgmlcoverage.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* ParseGMLCoverageDesc() */ +/************************************************************************/ + +CPLErr GDALParseGMLCoverage( CPLXMLNode *psXML, + int *pnXSize, int *pnYSize, + double *padfGeoTransform, + char **ppszProjection ) + +{ + CPLStripXMLNamespace( psXML, NULL, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Isolate RectifiedGrid. Eventually we will need to support */ +/* other georeferencing objects. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRG = CPLSearchXMLNode( psXML, "=RectifiedGrid" ); + CPLXMLNode *psOriginPoint = NULL; + const char *pszOffset1=NULL, *pszOffset2=NULL; + + if( psRG != NULL ) + { + psOriginPoint = CPLGetXMLNode( psRG, "origin.Point" ); + if( psOriginPoint == NULL ) + psOriginPoint = CPLGetXMLNode( psRG, "origin" ); + + CPLXMLNode *psOffset1 = CPLGetXMLNode( psRG, "offsetVector" ); + if( psOffset1 != NULL ) + { + pszOffset1 = CPLGetXMLValue( psOffset1, "", NULL ); + pszOffset2 = CPLGetXMLValue( psOffset1->psNext, "=offsetVector", + NULL ); + } + } + +/* -------------------------------------------------------------------- */ +/* If we are missing any of the origin or 2 offsets then give up. */ +/* -------------------------------------------------------------------- */ + if( psRG == NULL || psOriginPoint == NULL + || pszOffset1 == NULL || pszOffset2 == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find GML RectifiedGrid, origin or offset vectors"); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Search for the GridEnvelope and derive the raster size. */ +/* -------------------------------------------------------------------- */ + char **papszLow = CSLTokenizeString( + CPLGetXMLValue( psRG, "limits.GridEnvelope.low", "")); + char **papszHigh = CSLTokenizeString( + CPLGetXMLValue( psRG, "limits.GridEnvelope.high","")); + + if( CSLCount(papszLow) < 2 || CSLCount(papszHigh) < 2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find or parse GridEnvelope.low/high." ); + return CE_Failure; + } + + if( pnXSize != NULL ) + *pnXSize = atoi(papszHigh[0]) - atoi(papszLow[0]) + 1; + if( pnYSize != NULL ) + *pnYSize = atoi(papszHigh[1]) - atoi(papszLow[1]) + 1; + + CSLDestroy( papszLow ); + CSLDestroy( papszHigh ); + +/* -------------------------------------------------------------------- */ +/* Extract origin location. */ +/* -------------------------------------------------------------------- */ + OGRPoint *poOriginGeometry = NULL; + const char *pszSRSName = NULL; + + if( psOriginPoint != NULL ) + { + int bOldWrap = FALSE; + + // old coverages (ie. WCS) just have under so we + // may need to temporarily force to + if( psOriginPoint->eType == CXT_Element + && EQUAL(psOriginPoint->pszValue,"origin") ) + { + strcpy( psOriginPoint->pszValue, "Point"); + bOldWrap = TRUE; + } + poOriginGeometry = (OGRPoint *) + OGR_G_CreateFromGMLTree( psOriginPoint ); + + if( poOriginGeometry != NULL + && wkbFlatten(poOriginGeometry->getGeometryType()) != wkbPoint ) + { + delete poOriginGeometry; + poOriginGeometry = NULL; + } + + if( bOldWrap ) + strcpy( psOriginPoint->pszValue, "origin"); + + // SRS? + pszSRSName = CPLGetXMLValue( psOriginPoint, "srsName", NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Extract offset(s) */ +/* -------------------------------------------------------------------- */ + char **papszOffset1Tokens = NULL; + char **papszOffset2Tokens = NULL; + int bSuccess = FALSE; + + papszOffset1Tokens = + CSLTokenizeStringComplex( pszOffset1, " ,", FALSE, FALSE ); + papszOffset2Tokens = + CSLTokenizeStringComplex( pszOffset2, " ,", FALSE, FALSE ); + + if( CSLCount(papszOffset1Tokens) >= 2 + && CSLCount(papszOffset2Tokens) >= 2 + && poOriginGeometry != NULL ) + { + padfGeoTransform[0] = poOriginGeometry->getX(); + padfGeoTransform[1] = CPLAtof(papszOffset1Tokens[0]); + padfGeoTransform[2] = CPLAtof(papszOffset1Tokens[1]); + padfGeoTransform[3] = poOriginGeometry->getY(); + padfGeoTransform[4] = CPLAtof(papszOffset2Tokens[0]); + padfGeoTransform[5] = CPLAtof(papszOffset2Tokens[1]); + + // offset from center of pixel. + padfGeoTransform[0] -= padfGeoTransform[1]*0.5; + padfGeoTransform[0] -= padfGeoTransform[2]*0.5; + padfGeoTransform[3] -= padfGeoTransform[4]*0.5; + padfGeoTransform[3] -= padfGeoTransform[5]*0.5; + + bSuccess = TRUE; + //bHaveGeoTransform = TRUE; + } + + CSLDestroy( papszOffset1Tokens ); + CSLDestroy( papszOffset2Tokens ); + + if( poOriginGeometry != NULL ) + delete poOriginGeometry; + +/* -------------------------------------------------------------------- */ +/* If we have gotten a geotransform, then try to interprete the */ +/* srsName. */ +/* -------------------------------------------------------------------- */ + if( bSuccess && pszSRSName != NULL + && (*ppszProjection == NULL || strlen(*ppszProjection) == 0) ) + { + if( EQUALN(pszSRSName,"epsg:",5) ) + { + OGRSpatialReference oSRS; + if( oSRS.SetFromUserInput( pszSRSName ) == OGRERR_NONE ) + oSRS.exportToWkt( ppszProjection ); + } + else if( EQUALN(pszSRSName,"urn:ogc:def:crs:",16) ) + { + OGRSpatialReference oSRS; + if( oSRS.importFromURN( pszSRSName ) == OGRERR_NONE ) + oSRS.exportToWkt( ppszProjection ); + } + else + *ppszProjection = CPLStrdup(pszSRSName); + } + + if( *ppszProjection ) + CPLDebug( "GDALJP2Metadata", + "Got projection from GML box: %s", + *ppszProjection ); + + return CE_None; +} + diff --git a/bazaar/plugin/gdal/gcore/gdaljp2abstractdataset.cpp b/bazaar/plugin/gdal/gcore/gdaljp2abstractdataset.cpp new file mode 100644 index 000000000..8f8ba3394 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaljp2abstractdataset.cpp @@ -0,0 +1,520 @@ +/****************************************************************************** + * $Id: gdaljp2abstractdataset.cpp 29131 2015-05-03 14:47:58Z rouault $ + * + * Project: GDAL + * Purpose: GDALGeorefPamDataset with helper to read georeferencing and other + * metadata from JP2Boxes + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdaljp2abstractdataset.h" +#include "gdaljp2metadata.h" +#include "ogrsf_frmts.h" +#include "gdal_mdreader.h" + +/************************************************************************/ +/* GDALJP2AbstractDataset() */ +/************************************************************************/ + +GDALJP2AbstractDataset::GDALJP2AbstractDataset() +{ + pszWldFilename = NULL; + poMemDS = NULL; + papszMetadataFiles = NULL; +} + +/************************************************************************/ +/* ~GDALJP2AbstractDataset() */ +/************************************************************************/ + +GDALJP2AbstractDataset::~GDALJP2AbstractDataset() +{ + CPLFree(pszWldFilename); + CloseDependentDatasets(); + CSLDestroy(papszMetadataFiles); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int GDALJP2AbstractDataset::CloseDependentDatasets() +{ + int bRet = GDALGeorefPamDataset::CloseDependentDatasets(); + if( poMemDS ) + { + GDALClose(poMemDS); + poMemDS = NULL; + bRet = TRUE; + } + return bRet; +} + +/************************************************************************/ +/* LoadJP2Metadata() */ +/************************************************************************/ + +void GDALJP2AbstractDataset::LoadJP2Metadata(GDALOpenInfo* poOpenInfo, + const char* pszOverideFilenameIn) +{ + const char* pszOverideFilename = pszOverideFilenameIn; + if( pszOverideFilename == NULL ) + pszOverideFilename = poOpenInfo->pszFilename; + +/* -------------------------------------------------------------------- */ +/* Check for georeferencing information. */ +/* -------------------------------------------------------------------- */ + GDALJP2Metadata oJP2Geo; + + if( (poOpenInfo->fpL != NULL && pszOverideFilenameIn == NULL && + oJP2Geo.ReadAndParse(poOpenInfo->fpL) ) || + (!(poOpenInfo->fpL != NULL && pszOverideFilenameIn == NULL) && + oJP2Geo.ReadAndParse( pszOverideFilename )) ) + { + CPLFree(pszProjection); + pszProjection = CPLStrdup(oJP2Geo.pszProjection); + bGeoTransformValid = oJP2Geo.bHaveGeoTransform; + memcpy( adfGeoTransform, oJP2Geo.adfGeoTransform, + sizeof(double) * 6 ); + nGCPCount = oJP2Geo.nGCPCount; + pasGCPList = + GDALDuplicateGCPs( oJP2Geo.nGCPCount, oJP2Geo.pasGCPList ); + + if( oJP2Geo.bPixelIsPoint ) + GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT); + if( oJP2Geo.papszRPCMD ) + GDALDataset::SetMetadata( oJP2Geo.papszRPCMD, "RPC" ); + } + +/* -------------------------------------------------------------------- */ +/* Report XML UUID box in a dedicated metadata domain */ +/* -------------------------------------------------------------------- */ + if (oJP2Geo.pszXMPMetadata) + { + char *apszMDList[2]; + apszMDList[0] = (char *) oJP2Geo.pszXMPMetadata; + apszMDList[1] = NULL; + GDALDataset::SetMetadata(apszMDList, "xml:XMP"); + } + +/* -------------------------------------------------------------------- */ +/* Do we have any XML boxes we would like to treat as special */ +/* domain metadata? (Note: the GDAL multidomain metadata XML box */ +/* has been excluded and is dealt a few lines below. */ +/* -------------------------------------------------------------------- */ + int iBox; + + for( iBox = 0; + oJP2Geo.papszGMLMetadata + && oJP2Geo.papszGMLMetadata[iBox] != NULL; + iBox++ ) + { + char *pszName = NULL; + const char *pszXML = + CPLParseNameValue( oJP2Geo.papszGMLMetadata[iBox], + &pszName ); + CPLString osDomain; + char *apszMDList[2]; + + osDomain.Printf( "xml:%s", pszName ); + apszMDList[0] = (char *) pszXML; + apszMDList[1] = NULL; + + GDALDataset::SetMetadata( apszMDList, osDomain ); + + CPLFree( pszName ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have GDAL metadata? */ +/* -------------------------------------------------------------------- */ + if( oJP2Geo.pszGDALMultiDomainMetadata != NULL ) + { + CPLXMLNode* psXMLNode = CPLParseXMLString(oJP2Geo.pszGDALMultiDomainMetadata); + if( psXMLNode ) + { + GDALMultiDomainMetadata oLocalMDMD; + oLocalMDMD.XMLInit(psXMLNode, FALSE); + char** papszDomainList = oLocalMDMD.GetDomainList(); + char** papszIter = papszDomainList; + GDALDataset::SetMetadata(oLocalMDMD.GetMetadata()); + while( papszIter && *papszIter ) + { + if( !EQUAL(*papszIter, "") && !EQUAL(*papszIter, "IMAGE_STRUCTURE") ) + { + if( GDALDataset::GetMetadata(*papszIter) != NULL ) + { + CPLDebug("GDALJP2", + "GDAL metadata overrides metadata in %s domain over metadata read from other boxes", + *papszIter); + } + GDALDataset::SetMetadata(oLocalMDMD.GetMetadata(*papszIter), *papszIter); + } + papszIter ++; + } + CPLDestroyXMLNode(psXMLNode); + } + else + CPLErrorReset(); + } + +/* -------------------------------------------------------------------- */ +/* Do we have other misc metadata (from resd box for now) ? */ +/* -------------------------------------------------------------------- */ + if( oJP2Geo.papszMetadata != NULL ) + { + char **papszMD = CSLDuplicate(GDALDataset::GetMetadata()); + + papszMD = CSLMerge( papszMD, oJP2Geo.papszMetadata ); + GDALDataset::SetMetadata( papszMD ); + + CSLDestroy( papszMD ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have XML IPR ? */ +/* -------------------------------------------------------------------- */ + if( oJP2Geo.pszXMLIPR != NULL ) + { + char* apszMD[2] = { NULL, NULL }; + apszMD[0] = oJP2Geo.pszXMLIPR; + GDALDataset::SetMetadata( apszMD, "xml:IPR" ); + } + +/* -------------------------------------------------------------------- */ +/* Check for world file. */ +/* -------------------------------------------------------------------- */ + if( !bGeoTransformValid ) + { + bGeoTransformValid |= + GDALReadWorldFile2( pszOverideFilename, NULL, + adfGeoTransform, + poOpenInfo->GetSiblingFiles(), &pszWldFilename ) + || GDALReadWorldFile2( pszOverideFilename, ".wld", + adfGeoTransform, + poOpenInfo->GetSiblingFiles(), &pszWldFilename ); + } + + GDALMDReaderManager mdreadermanager; + GDALMDReaderBase* mdreader = mdreadermanager.GetReader(poOpenInfo->pszFilename, + poOpenInfo->GetSiblingFiles(), MDR_ANY); + if(NULL != mdreader) + { + mdreader->FillMetadata(&(oMDMD)); + papszMetadataFiles = mdreader->GetMetadataFiles(); + } +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **GDALJP2AbstractDataset::GetFileList() + +{ + char **papszFileList = GDALGeorefPamDataset::GetFileList(); + + if( pszWldFilename != NULL && + CSLFindString( papszFileList, pszWldFilename ) == -1 ) + { + papszFileList = CSLAddString( papszFileList, pszWldFilename ); + } + if(NULL != papszMetadataFiles) + { + for( int i = 0; papszMetadataFiles[i] != NULL; i++ ) + { + papszFileList = CSLAddString( papszFileList, papszMetadataFiles[i] ); + } + } + return papszFileList; +} + +/************************************************************************/ +/* LoadVectorLayers() */ +/************************************************************************/ + +void GDALJP2AbstractDataset::LoadVectorLayers(int bOpenRemoteResources) +{ + char** papszGMLJP2 = GetMetadata("xml:gml.root-instance"); + if( papszGMLJP2 == NULL ) + return; + GDALDriver* poMemDriver = (GDALDriver*)GDALGetDriverByName("Memory"); + if( poMemDriver == NULL ) + return; + CPLXMLNode* psRoot = CPLParseXMLString(papszGMLJP2[0]); + if( psRoot == NULL ) + return; + CPLXMLNode* psCC = CPLGetXMLNode(psRoot, "=gmljp2:GMLJP2CoverageCollection"); + if( psCC == NULL ) + { + CPLDestroyXMLNode(psRoot); + return; + } + + // Find feature collections + CPLXMLNode* psCCChildIter = psCC->psChild; + int nLayersAtCC = 0; + int nLayersAtGC = 0; + for( ; psCCChildIter != NULL; psCCChildIter = psCCChildIter->psNext ) + { + if( psCCChildIter->eType != CXT_Element || + strcmp(psCCChildIter->pszValue, "gmljp2:featureMember") != 0 || + psCCChildIter->psChild == NULL || + psCCChildIter->psChild->eType != CXT_Element ) + continue; + + CPLXMLNode* psGCorGMLJP2Features = psCCChildIter->psChild; + int bIsGC = ( strstr(psGCorGMLJP2Features->pszValue, "GridCoverage") != NULL ); + + CPLXMLNode* psGCorGMLJP2FeaturesChildIter = psGCorGMLJP2Features->psChild; + for( ; psGCorGMLJP2FeaturesChildIter != NULL; + psGCorGMLJP2FeaturesChildIter = psGCorGMLJP2FeaturesChildIter->psNext ) + { + if( psGCorGMLJP2FeaturesChildIter->eType != CXT_Element || + strcmp(psGCorGMLJP2FeaturesChildIter->pszValue, "gmljp2:feature") != 0 || + psGCorGMLJP2FeaturesChildIter->psChild == NULL ) + continue; + + CPLXMLNode* psFC = NULL; + int bFreeFC = FALSE; + CPLString osGMLTmpFile; + + CPLXMLNode* psChild = psGCorGMLJP2FeaturesChildIter->psChild; + if( psChild->eType == CXT_Attribute && + strcmp(psChild->pszValue, "xlink:href") == 0 && + strncmp(psChild->psChild->pszValue, + "gmljp2://xml/", strlen("gmljp2://xml/")) == 0 ) + { + const char* pszBoxName = psChild->psChild->pszValue + strlen("gmljp2://xml/"); + char** papszBoxData = GetMetadata(CPLSPrintf("xml:%s", pszBoxName)); + if( papszBoxData != NULL ) + { + psFC = CPLParseXMLString(papszBoxData[0]); + bFreeFC = TRUE; + } + else + { + CPLDebug("GMLJP2", + "gmljp2:feature references %s, but no corresponding box found", + psChild->psChild->pszValue); + } + } + if( psChild->eType == CXT_Attribute && + strcmp(psChild->pszValue, "xlink:href") == 0 && + (strncmp(psChild->psChild->pszValue, "http://", strlen("http://")) == 0 || + strncmp(psChild->psChild->pszValue, "https://", strlen("https://")) == 0) ) + { + if( !bOpenRemoteResources ) + CPLDebug("GMLJP2", "Remote feature collection %s mentionned in GMLJP2 box", + psChild->psChild->pszValue); + else + osGMLTmpFile = "/vsicurl/" + CPLString(psChild->psChild->pszValue); + } + else if( psChild->eType == CXT_Element && + strstr(psChild->pszValue, "FeatureCollection") != NULL ) + { + psFC = psChild; + } + if( psFC == NULL && osGMLTmpFile.size() == 0 ) + continue; + + if( psFC != NULL ) + { + osGMLTmpFile = CPLSPrintf("/vsimem/gmljp2/%p/my.gml", this); + // Create temporary .gml file + CPLSerializeXMLTreeToFile(psFC, osGMLTmpFile); + } + + CPLDebug("GMLJP2", "Found a FeatureCollection at %s level", + (bIsGC) ? "GridCoverage" : "CoverageCollection"); + + CPLString osXSDTmpFile; + + if( psFC ) + { + // Try to localize its .xsd schema in a GMLJP2 auxiliary box + const char* pszSchemaLocation = CPLGetXMLValue(psFC, "xsi:schemaLocation", NULL); + if( pszSchemaLocation ) + { + char **papszTokens = CSLTokenizeString2( + pszSchemaLocation, " \t\n", + CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES); + + if( (CSLCount(papszTokens) % 2) == 0 ) + { + for(char** papszIter = papszTokens; *papszIter; papszIter += 2 ) + { + if( strncmp(papszIter[1], "gmljp2://xml/", strlen("gmljp2://xml/")) == 0 ) + { + const char* pszBoxName = papszIter[1] + strlen("gmljp2://xml/"); + char** papszBoxData = GetMetadata(CPLSPrintf("xml:%s", pszBoxName)); + if( papszBoxData != NULL ) + { + osXSDTmpFile = CPLSPrintf("/vsimem/gmljp2/%p/my.xsd", this); + VSIFCloseL(VSIFileFromMemBuffer(osXSDTmpFile, + (GByte*)papszBoxData[0], + strlen(papszBoxData[0]), + FALSE)); + } + else + { + CPLDebug("GMLJP2", + "Feature collection references %s, but no corresponding box found", + papszIter[1]); + } + break; + } + } + } + CSLDestroy(papszTokens); + } + if( bFreeFC ) + { + CPLDestroyXMLNode(psFC); + psFC = NULL; + } + } + + GDALDriverH hDrv = GDALIdentifyDriver(osGMLTmpFile, NULL); + GDALDriverH hGMLDrv = GDALGetDriverByName("GML"); + if( hDrv != NULL && hDrv == hGMLDrv ) + { + char* apszOpenOptions[2]; + apszOpenOptions[0] = (char*) "FORCE_SRS_DETECTION=YES"; + apszOpenOptions[1] = NULL; + GDALDataset* poTmpDS = (GDALDataset*)GDALOpenEx( osGMLTmpFile, + GDAL_OF_VECTOR, NULL, apszOpenOptions, NULL ); + if( poTmpDS ) + { + int nLayers = poTmpDS->GetLayerCount(); + for(int i=0;iCreate("", 0, 0, 0, GDT_Unknown, NULL); + OGRLayer* poSrcLyr = poTmpDS->GetLayer(i); + const char* pszLayerName; + if( bIsGC ) + pszLayerName = CPLSPrintf("FC_GridCoverage_%d_%s", + ++nLayersAtGC, poSrcLyr->GetName()); + else + pszLayerName = CPLSPrintf("FC_CoverageCollection_%d_%s", + ++nLayersAtCC, poSrcLyr->GetName()); + poMemDS->CopyLayer(poSrcLyr, pszLayerName, NULL); + } + GDALClose(poTmpDS); + + // In case we don't have a schema, a .gfs might have been generated + VSIUnlink(CPLSPrintf("/vsimem/gmljp2/%p/my.gfs", this)); + } + } + else + { + CPLDebug("GMLJP2", "No GML driver found to read feature collection"); + } + + if( strncmp(osGMLTmpFile, "/vsicurl/", strlen("/vsicurl/")) != 0 ) + VSIUnlink(osGMLTmpFile); + if( osXSDTmpFile.size() ) + VSIUnlink(osXSDTmpFile); + } + } + + // Find annotations + psCCChildIter = psCC->psChild; + int nAnnotations = 0; + for( ; psCCChildIter != NULL; psCCChildIter = psCCChildIter->psNext ) + { + if( psCCChildIter->eType != CXT_Element || + strcmp(psCCChildIter->pszValue, "gmljp2:featureMember") != 0 || + psCCChildIter->psChild == NULL || + psCCChildIter->psChild->eType != CXT_Element ) + continue; + CPLXMLNode* psGCorGMLJP2Features = psCCChildIter->psChild; + int bIsGC = ( strstr(psGCorGMLJP2Features->pszValue, "GridCoverage") != NULL ); + if( !bIsGC ) + continue; + CPLXMLNode* psGCorGMLJP2FeaturesChildIter = psGCorGMLJP2Features->psChild; + for( ; psGCorGMLJP2FeaturesChildIter != NULL; + psGCorGMLJP2FeaturesChildIter = psGCorGMLJP2FeaturesChildIter->psNext ) + { + if( psGCorGMLJP2FeaturesChildIter->eType != CXT_Element || + strcmp(psGCorGMLJP2FeaturesChildIter->pszValue, "gmljp2:annotation") != 0 || + psGCorGMLJP2FeaturesChildIter->psChild == NULL || + psGCorGMLJP2FeaturesChildIter->psChild->eType != CXT_Element || + strstr(psGCorGMLJP2FeaturesChildIter->psChild->pszValue, "kml") == NULL ) + continue; + CPLDebug("GMLJP2", "Found a KML annotation"); + + // Create temporary .kml file + CPLXMLNode* psKML = psGCorGMLJP2FeaturesChildIter->psChild; + CPLString osKMLTmpFile(CPLSPrintf("/vsimem/gmljp2/%p/my.kml", this)); + CPLSerializeXMLTreeToFile(psKML, osKMLTmpFile); + + GDALDataset* poTmpDS = (GDALDataset*)GDALOpenEx( osKMLTmpFile, + GDAL_OF_VECTOR, NULL, NULL, NULL ); + if( poTmpDS ) + { + int nLayers = poTmpDS->GetLayerCount(); + for(int i=0;iCreate("", 0, 0, 0, GDT_Unknown, NULL); + OGRLayer* poSrcLyr = poTmpDS->GetLayer(i); + const char* pszLayerName; + pszLayerName = CPLSPrintf("Annotation_%d_%s", + ++nAnnotations, poSrcLyr->GetName()); + poMemDS->CopyLayer(poSrcLyr, pszLayerName, NULL); + } + GDALClose(poTmpDS); + } + else + { + CPLDebug("GMLJP2", "No KML/LIBKML driver found to read annotation"); + } + + VSIUnlink(osKMLTmpFile); + } + } + + CPLDestroyXMLNode(psRoot); +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int GDALJP2AbstractDataset::GetLayerCount() +{ + return (poMemDS != NULL) ? poMemDS->GetLayerCount() : 0; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer* GDALJP2AbstractDataset::GetLayer(int i) +{ + return (poMemDS != NULL) ? poMemDS->GetLayer(i) : NULL; +} diff --git a/bazaar/plugin/gdal/gcore/gdaljp2abstractdataset.h b/bazaar/plugin/gdal/gcore/gdaljp2abstractdataset.h new file mode 100644 index 000000000..e1593fcd5 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaljp2abstractdataset.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * $Id: gdaljp2abstractdataset.h 29131 2015-05-03 14:47:58Z rouault $ + * + * Project: GDAL + * Purpose: GDALGeorefPamDataset with helper to read georeferencing and other + * metadata from JP2Boxes + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_JP2_ABSTRACT_DATASET_H_INCLUDED +#define GDAL_JP2_ABSTRACT_DATASET_H_INCLUDED + +#include "gdalgeorefpamdataset.h" + +class CPL_DLL GDALJP2AbstractDataset: public GDALGeorefPamDataset +{ + char* pszWldFilename; + + GDALDataset* poMemDS; + char** papszMetadataFiles; + + protected: + virtual int CloseDependentDatasets(); + + public: + GDALJP2AbstractDataset(); + ~GDALJP2AbstractDataset(); + + void LoadJP2Metadata(GDALOpenInfo* poOpenInfo, + const char* pszOverideFilename = NULL); + void LoadVectorLayers(int bOpenRemoteResources = FALSE); + + virtual char **GetFileList(void); + + virtual int GetLayerCount(); + virtual OGRLayer *GetLayer(int i); +}; + +#endif /* GDAL_JP2_ABSTRACT_DATASET_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/gcore/gdaljp2box.cpp b/bazaar/plugin/gdal/gcore/gdaljp2box.cpp new file mode 100644 index 000000000..47dd66a7d --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaljp2box.cpp @@ -0,0 +1,507 @@ +/****************************************************************************** + * $Id: gdaljp2box.cpp 28691 2015-03-08 20:07:02Z rouault $ + * + * Project: GDAL + * Purpose: GDALJP2Box Implementation - Low level JP2 box reader. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdaljp2metadata.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: gdaljp2box.cpp 28691 2015-03-08 20:07:02Z rouault $"); + +/************************************************************************/ +/* GDALJP2Box() */ +/************************************************************************/ + +GDALJP2Box::GDALJP2Box( VSILFILE *fpIn ) + +{ + fpVSIL = fpIn; + szBoxType[0] = '\0'; + nBoxOffset = -1; + nDataOffset = -1; + nBoxLength = 0; + + pabyData = NULL; +} + +/************************************************************************/ +/* ~GDALJP2Box() */ +/************************************************************************/ + +GDALJP2Box::~GDALJP2Box() + +{ + CPLFree( pabyData ); +} + +/************************************************************************/ +/* SetOffset() */ +/************************************************************************/ + +int GDALJP2Box::SetOffset( GIntBig nNewOffset ) + +{ + szBoxType[0] = '\0'; + return VSIFSeekL( fpVSIL, nNewOffset, SEEK_SET ) == 0; +} + +/************************************************************************/ +/* ReadFirst() */ +/************************************************************************/ + +int GDALJP2Box::ReadFirst() + +{ + return SetOffset(0) && ReadBox(); +} + +/************************************************************************/ +/* ReadNext() */ +/************************************************************************/ + +int GDALJP2Box::ReadNext() + +{ + return SetOffset( nBoxOffset + nBoxLength ) && ReadBox(); +} + +/************************************************************************/ +/* ReadFirstChild() */ +/************************************************************************/ + +int GDALJP2Box::ReadFirstChild( GDALJP2Box *poSuperBox ) + +{ + if( poSuperBox == NULL ) + return ReadFirst(); + + szBoxType[0] = '\0'; + if( !poSuperBox->IsSuperBox() ) + return FALSE; + + return SetOffset( poSuperBox->nDataOffset ) && ReadBox(); +} + +/************************************************************************/ +/* ReadNextChild() */ +/************************************************************************/ + +int GDALJP2Box::ReadNextChild( GDALJP2Box *poSuperBox ) + +{ + if( poSuperBox == NULL ) + return ReadNext(); + + if( !ReadNext() ) + return FALSE; + + if( nBoxOffset >= poSuperBox->nBoxOffset + poSuperBox->nBoxLength ) + { + szBoxType[0] = '\0'; + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* ReadBox() */ +/************************************************************************/ + +int GDALJP2Box::ReadBox() + +{ + GUInt32 nLBox; + GUInt32 nTBox; + + nBoxOffset = VSIFTellL( fpVSIL ); + + if( VSIFReadL( &nLBox, 4, 1, fpVSIL ) != 1 + || VSIFReadL( &nTBox, 4, 1, fpVSIL ) != 1 ) + { + return FALSE; + } + + memcpy( szBoxType, &nTBox, 4 ); + szBoxType[4] = '\0'; + + nLBox = CPL_MSBWORD32( nLBox ); + + if( nLBox != 1 ) + { + nBoxLength = nLBox; + nDataOffset = nBoxOffset + 8; + } + else + { + GByte abyXLBox[8]; + if( VSIFReadL( abyXLBox, 8, 1, fpVSIL ) != 1 ) + return FALSE; + + + if( sizeof(nBoxLength) == 8 ) + { + CPL_MSBPTR64( abyXLBox ); + memcpy( &nBoxLength, abyXLBox, 8 ); + } + else + { + CPL_MSBPTR32( abyXLBox+4 ); + memcpy( &nBoxLength, abyXLBox+4, 4 ); + } + + nDataOffset = nBoxOffset + 16; + } + + if( nBoxLength == 0 ) + { + VSIFSeekL( fpVSIL, 0, SEEK_END ); + nBoxLength = VSIFTellL( fpVSIL ) - nBoxOffset; + VSIFSeekL( fpVSIL, nDataOffset, SEEK_SET ); + } + + if( EQUAL(szBoxType,"uuid") ) + { + if( VSIFReadL( abyUUID, 16, 1, fpVSIL ) != 1 ) + return FALSE; + nDataOffset += 16; + } + + if( GetDataLength() < 0 ) + { + CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType); + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* IsSuperBox() */ +/************************************************************************/ + +int GDALJP2Box::IsSuperBox() + +{ + if( EQUAL(GetType(),"asoc") || EQUAL(GetType(),"jp2h") + || EQUAL(GetType(),"res ") ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* ReadBoxData() */ +/************************************************************************/ + +GByte *GDALJP2Box::ReadBoxData() + +{ + if( GetDataLength() > 100 * 1024 * 1024 ) + return FALSE; + + VSIFSeekL( fpVSIL, nDataOffset, SEEK_SET ); + + char *pszData = (char *) CPLMalloc((int)GetDataLength() + 1); + + if( (GIntBig) VSIFReadL( pszData, 1, (int)GetDataLength(), fpVSIL ) + != GetDataLength() ) + { + CPLFree( pszData ); + return NULL; + } + + pszData[GetDataLength()] = '\0'; + + return (GByte *) pszData; +} + +/************************************************************************/ +/* GetDataLength() */ +/************************************************************************/ + +GIntBig GDALJP2Box::GetDataLength() +{ + return nBoxLength - (nDataOffset - nBoxOffset); +} + +/************************************************************************/ +/* DumpReadable() */ +/************************************************************************/ + +int GDALJP2Box::DumpReadable( FILE *fpOut, int nIndentLevel ) + +{ + if( fpOut == NULL ) + fpOut = stdout; + + int i; + for(i=0;i 0; + oSubBox.ReadNextChild( this ) ) + { + oSubBox.DumpReadable( fpOut, nIndentLevel + 1 ); + } + } + + if( EQUAL(GetType(),"uuid") ) + { + char *pszHex = CPLBinaryToHex( 16, GetUUID() ); + for(i=0;iSetType( "uuid" ); + + poBox->AppendWritableData( 16, pabyUUID ); + poBox->AppendWritableData( nDataSize, pabyData ); + + return poBox; +} + +/************************************************************************/ +/* CreateAsocBox() */ +/************************************************************************/ + +GDALJP2Box *GDALJP2Box::CreateAsocBox( int nCount, GDALJP2Box **papoBoxes ) +{ + return CreateSuperBox("asoc", nCount, papoBoxes); +} + + +/************************************************************************/ +/* CreateAsocBox() */ +/************************************************************************/ + +GDALJP2Box *GDALJP2Box::CreateSuperBox( const char* pszType, + int nCount, GDALJP2Box **papoBoxes ) +{ + int nDataSize=0, iBox; + GByte *pabyCompositeData, *pabyNext; + +/* -------------------------------------------------------------------- */ +/* Compute size of data area of asoc box. */ +/* -------------------------------------------------------------------- */ + for( iBox = 0; iBox < nCount; iBox++ ) + nDataSize += 8 + (int) papoBoxes[iBox]->GetDataLength(); + + pabyNext = pabyCompositeData = (GByte *) CPLMalloc(nDataSize); + +/* -------------------------------------------------------------------- */ +/* Copy subboxes headers and data into buffer. */ +/* -------------------------------------------------------------------- */ + for( iBox = 0; iBox < nCount; iBox++ ) + { + GUInt32 nLBox; + + nLBox = CPL_MSBWORD32(papoBoxes[iBox]->nBoxLength); + memcpy( pabyNext, &nLBox, 4 ); + pabyNext += 4; + + memcpy( pabyNext, papoBoxes[iBox]->szBoxType, 4 ); + pabyNext += 4; + + memcpy( pabyNext, papoBoxes[iBox]->pabyData, + (int) papoBoxes[iBox]->GetDataLength() ); + pabyNext += papoBoxes[iBox]->GetDataLength(); + } + +/* -------------------------------------------------------------------- */ +/* Create asoc box. */ +/* -------------------------------------------------------------------- */ + GDALJP2Box *poAsoc = new GDALJP2Box(); + + poAsoc->SetType( pszType ); + poAsoc->SetWritableData( nDataSize, pabyCompositeData ); + + CPLFree( pabyCompositeData ); + + return poAsoc; +} + +/************************************************************************/ +/* CreateLblBox() */ +/************************************************************************/ + +GDALJP2Box *GDALJP2Box::CreateLblBox( const char *pszLabel ) + +{ + GDALJP2Box *poBox; + + poBox = new GDALJP2Box(); + poBox->SetType( "lbl " ); + poBox->SetWritableData( strlen(pszLabel)+1, (const GByte *) pszLabel ); + + return poBox; +} + +/************************************************************************/ +/* CreateLabelledXMLAssoc() */ +/************************************************************************/ + +GDALJP2Box *GDALJP2Box::CreateLabelledXMLAssoc( const char *pszLabel, + const char *pszXML ) + +{ + GDALJP2Box oLabel, oXML; + GDALJP2Box *aoList[2]; + + oLabel.SetType( "lbl " ); + oLabel.SetWritableData( strlen(pszLabel)+1, (const GByte *) pszLabel ); + + oXML.SetType( "xml " ); + oXML.SetWritableData( strlen(pszXML)+1, (const GByte *) pszXML ); + + aoList[0] = &oLabel; + aoList[1] = &oXML; + + return CreateAsocBox( 2, aoList ); +} diff --git a/bazaar/plugin/gdal/gcore/gdaljp2metadata.cpp b/bazaar/plugin/gdal/gcore/gdaljp2metadata.cpp new file mode 100644 index 000000000..9a82dedab --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaljp2metadata.cpp @@ -0,0 +1,3089 @@ +/****************************************************************************** + * $Id: gdaljp2metadata.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: GDAL + * Purpose: GDALJP2Metadata - Read GeoTIFF and/or GML georef info. + * Author: Frank Warmerdam, warmerdam@pobox.com + * Even Rouault + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2010-2015, Even Rouault + * Copyright (c) 2015, European Union Satellite Centre + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdaljp2metadata.h" +#include "cpl_string.h" +#include "cpl_minixml.h" +#include "ogr_spatialref.h" +#include "ogr_geometry.h" +#include "ogr_api.h" +#include "gt_wkt_srs_for_gdal.h" +#include "json.h" +#include "gdaljp2metadatagenerator.h" + +CPL_CVSID("$Id: gdaljp2metadata.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +static const unsigned char msi_uuid2[16] = +{0xb1,0x4b,0xf8,0xbd,0x08,0x3d,0x4b,0x43, + 0xa5,0xae,0x8c,0xd7,0xd5,0xa6,0xce,0x03}; + +static const unsigned char msig_uuid[16] = +{ 0x96,0xA9,0xF1,0xF1,0xDC,0x98,0x40,0x2D, + 0xA7,0xAE,0xD6,0x8E,0x34,0x45,0x18,0x09 }; + +static const unsigned char xmp_uuid[16] = +{ 0xBE,0x7A,0xCF,0xCB,0x97,0xA9,0x42,0xE8, + 0x9C,0x71,0x99,0x94,0x91,0xE3,0xAF,0xAC}; + +struct _GDALJP2GeoTIFFBox +{ + int nGeoTIFFSize; + GByte *pabyGeoTIFFData; +}; + +#define MAX_JP2GEOTIFF_BOXES 2 + +/************************************************************************/ +/* GDALJP2Metadata() */ +/************************************************************************/ + +GDALJP2Metadata::GDALJP2Metadata() + +{ + pszProjection = NULL; + + nGCPCount = 0; + pasGCPList = NULL; + + papszRPCMD = NULL; + + papszGMLMetadata = NULL; + papszMetadata = NULL; + + nGeoTIFFBoxesCount = 0; + pasGeoTIFFBoxes = NULL; + + nMSIGSize = 0; + pabyMSIGData = NULL; + + pszXMPMetadata = NULL; + pszGDALMultiDomainMetadata = NULL; + pszXMLIPR = NULL; + + bHaveGeoTransform = FALSE; + adfGeoTransform[0] = 0.0; + adfGeoTransform[1] = 1.0; + adfGeoTransform[2] = 0.0; + adfGeoTransform[3] = 0.0; + adfGeoTransform[4] = 0.0; + adfGeoTransform[5] = 1.0; + bPixelIsPoint = FALSE; +} + +/************************************************************************/ +/* ~GDALJP2Metadata() */ +/************************************************************************/ + +GDALJP2Metadata::~GDALJP2Metadata() + +{ + CPLFree( pszProjection ); + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + CSLDestroy(papszRPCMD); + + for( int i=0; i < nGeoTIFFBoxesCount; i++ ) + { + CPLFree( pasGeoTIFFBoxes[i].pabyGeoTIFFData ); + } + CPLFree( pasGeoTIFFBoxes ); + CPLFree( pabyMSIGData ); + CSLDestroy( papszGMLMetadata ); + CSLDestroy( papszMetadata ); + CPLFree( pszXMPMetadata ); + CPLFree( pszGDALMultiDomainMetadata ); + CPLFree( pszXMLIPR ); +} + +/************************************************************************/ +/* ReadAndParse() */ +/* */ +/* Read a JP2 file and try to collect georeferencing */ +/* information from the various available forms. Returns TRUE */ +/* if anything useful is found. */ +/************************************************************************/ + +int GDALJP2Metadata::ReadAndParse( const char *pszFilename ) + +{ + VSILFILE *fpLL; + + fpLL = VSIFOpenL( pszFilename, "rb" ); + + if( fpLL == NULL ) + { + CPLDebug( "GDALJP2Metadata", "Could not even open %s.", + pszFilename ); + + return FALSE; + } + + int bRet = ReadAndParse( fpLL ); + VSIFCloseL( fpLL ); + +/* -------------------------------------------------------------------- */ +/* If we still don't have a geotransform, look for a world */ +/* file. */ +/* -------------------------------------------------------------------- */ + if( !bHaveGeoTransform ) + { + bHaveGeoTransform = + GDALReadWorldFile( pszFilename, NULL, adfGeoTransform ) + || GDALReadWorldFile( pszFilename, ".wld", adfGeoTransform ); + bRet |= bHaveGeoTransform; + } + + return bRet; +} + + +int GDALJP2Metadata::ReadAndParse( VSILFILE *fpLL ) + +{ + ReadBoxes( fpLL ); + +/* -------------------------------------------------------------------- */ +/* Try JP2GeoTIFF, GML and finally MSIG to get something. */ +/* -------------------------------------------------------------------- */ + if( !ParseJP2GeoTIFF() && !ParseGMLCoverageDesc() ) + ParseMSIG(); + +/* -------------------------------------------------------------------- */ +/* Return success either either of projection or geotransform */ +/* or gcps. */ +/* -------------------------------------------------------------------- */ + return bHaveGeoTransform + || nGCPCount > 0 + || (pszProjection != NULL && strlen(pszProjection) > 0) + || papszRPCMD != NULL; +} + +/************************************************************************/ +/* CollectGMLData() */ +/* */ +/* Read all the asoc boxes after this node, and store the */ +/* contain xml documents along with the name from the label. */ +/************************************************************************/ + +void GDALJP2Metadata::CollectGMLData( GDALJP2Box *poGMLData ) + +{ + GDALJP2Box oChildBox( poGMLData->GetFILE() ); + + if( !oChildBox.ReadFirstChild( poGMLData ) ) + return; + + while( strlen(oChildBox.GetType()) > 0 ) + { + if( EQUAL(oChildBox.GetType(),"asoc") ) + { + GDALJP2Box oSubChildBox( oChildBox.GetFILE() ); + + char *pszLabel = NULL; + char *pszXML = NULL; + + if( !oSubChildBox.ReadFirstChild( &oChildBox ) ) + break; + + while( strlen(oSubChildBox.GetType()) > 0 ) + { + if( EQUAL(oSubChildBox.GetType(),"lbl ") ) + pszLabel = (char *)oSubChildBox.ReadBoxData(); + else if( EQUAL(oSubChildBox.GetType(),"xml ") ) + { + pszXML = (char *) oSubChildBox.ReadBoxData(); + + // Some GML data contains \0 instead of \n ! + // See http://trac.osgeo.org/gdal/ticket/5760 + if( pszXML ) + { + int nXMLLength = (int)oSubChildBox.GetDataLength(); + int i; + for(i=nXMLLength-1; i >= 0; i--) + { + if( pszXML[i] == '\0' ) + nXMLLength --; + else + break; + } + for(i=0;i 0 ) + { +#ifdef DEBUG + if (CSLTestBoolean(CPLGetConfigOption("DUMP_JP2_BOXES", "NO"))) + oBox.DumpReadable(stderr); +#endif + +/* -------------------------------------------------------------------- */ +/* Collect geotiff box. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(oBox.GetType(),"uuid") + && memcmp( oBox.GetUUID(), msi_uuid2, 16 ) == 0 ) + { + /* Erdas JPEG2000 files can in some conditions contain 2 GeoTIFF */ + /* UUID boxes. One that is correct, another one that does not contain */ + /* correct georeferencing. So let's fetch at most 2 of them */ + /* for later analysis. */ + if( nGeoTIFFBoxesCount == MAX_JP2GEOTIFF_BOXES ) + { + CPLDebug("GDALJP2", "Too many UUID GeoTIFF boxes. Ignoring this one"); + } + else + { + int nGeoTIFFSize = (int) oBox.GetDataLength(); + GByte* pabyGeoTIFFData = oBox.ReadBoxData(); + if (pabyGeoTIFFData == NULL) + { + CPLDebug("GDALJP2", "Cannot read data for UUID GeoTIFF box"); + } + else + { + pasGeoTIFFBoxes = (GDALJP2GeoTIFFBox*) CPLRealloc( + pasGeoTIFFBoxes, sizeof(GDALJP2GeoTIFFBox) * (nGeoTIFFBoxesCount + 1) ); + pasGeoTIFFBoxes[nGeoTIFFBoxesCount].nGeoTIFFSize = nGeoTIFFSize; + pasGeoTIFFBoxes[nGeoTIFFBoxesCount].pabyGeoTIFFData = pabyGeoTIFFData; + nGeoTIFFBoxesCount ++; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Collect MSIG box. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(oBox.GetType(),"uuid") + && memcmp( oBox.GetUUID(), msig_uuid, 16 ) == 0 ) + { + if( nMSIGSize == 0 ) + { + nMSIGSize = (int) oBox.GetDataLength(); + pabyMSIGData = oBox.ReadBoxData(); + + if( nMSIGSize < 70 + || pabyMSIGData == NULL + || memcmp( pabyMSIGData, "MSIG/", 5 ) != 0 ) + { + CPLFree( pabyMSIGData ); + pabyMSIGData = NULL; + nMSIGSize = 0; + } + } + else + { + CPLDebug("GDALJP2", "Too many UUID MSIG boxes. Ignoring this one"); + } + } + +/* -------------------------------------------------------------------- */ +/* Collect XMP box. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(oBox.GetType(),"uuid") + && memcmp( oBox.GetUUID(), xmp_uuid, 16 ) == 0 ) + { + if( pszXMPMetadata == NULL ) + { + pszXMPMetadata = (char*) oBox.ReadBoxData(); + } + else + { + CPLDebug("GDALJP2", "Too many UUID XMP boxes. Ignoring this one"); + } + } + +/* -------------------------------------------------------------------- */ +/* Process asoc box looking for Labelled GML data. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(oBox.GetType(),"asoc") ) + { + GDALJP2Box oSubBox( fpVSIL ); + + if( oSubBox.ReadFirstChild( &oBox ) && + EQUAL(oSubBox.GetType(),"lbl ") ) + { + char *pszLabel = (char *) oSubBox.ReadBoxData(); + if( pszLabel != NULL && EQUAL(pszLabel,"gml.data") ) + { + CollectGMLData( &oBox ); + } + CPLFree( pszLabel ); + } + } + +/* -------------------------------------------------------------------- */ +/* Process simple xml boxes. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(oBox.GetType(),"xml ") ) + { + CPLString osBoxName; + + char *pszXML = (char *) oBox.ReadBoxData(); + if( strncmp(pszXML, "", + strlen("")) == 0 ) + { + if( pszGDALMultiDomainMetadata == NULL ) + { + pszGDALMultiDomainMetadata = pszXML; + pszXML = NULL; + } + else + { + CPLDebug("GDALJP2", "Too many GDAL metadata boxes. Ignoring this one"); + } + } + else + { + osBoxName.Printf( "BOX_%d", iBox++ ); + + papszGMLMetadata = CSLSetNameValue( papszGMLMetadata, + osBoxName, pszXML ); + } + CPLFree( pszXML ); + } + +/* -------------------------------------------------------------------- */ +/* Check for a resd box in jp2h. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(oBox.GetType(),"jp2h") ) + { + GDALJP2Box oSubBox( fpVSIL ); + + for( oSubBox.ReadFirstChild( &oBox ); + strlen(oSubBox.GetType()) > 0; + oSubBox.ReadNextChild( &oBox ) ) + { + if( EQUAL(oSubBox.GetType(),"res ") ) + { + GDALJP2Box oResBox( fpVSIL ); + + oResBox.ReadFirstChild( &oSubBox ); + + // we will use either the resd or resc box, which ever + // happens to be first. Should we prefer resd? + unsigned char *pabyResData = NULL; + if( oResBox.GetDataLength() == 10 && + (pabyResData = oResBox.ReadBoxData()) != NULL ) + { + int nVertNum, nVertDen, nVertExp; + int nHorzNum, nHorzDen, nHorzExp; + + nVertNum = pabyResData[0] * 256 + pabyResData[1]; + nVertDen = pabyResData[2] * 256 + pabyResData[3]; + nHorzNum = pabyResData[4] * 256 + pabyResData[5]; + nHorzDen = pabyResData[6] * 256 + pabyResData[7]; + nVertExp = pabyResData[8]; + nHorzExp = pabyResData[9]; + + // compute in pixels/cm + double dfVertRes = + (nVertNum/(double)nVertDen) * pow(10.0,nVertExp)/100; + double dfHorzRes = + (nHorzNum/(double)nHorzDen) * pow(10.0,nHorzExp)/100; + CPLString osFormatter; + + papszMetadata = CSLSetNameValue( + papszMetadata, + "TIFFTAG_XRESOLUTION", + osFormatter.Printf("%g",dfHorzRes) ); + + papszMetadata = CSLSetNameValue( + papszMetadata, + "TIFFTAG_YRESOLUTION", + osFormatter.Printf("%g",dfVertRes) ); + papszMetadata = CSLSetNameValue( + papszMetadata, + "TIFFTAG_RESOLUTIONUNIT", + "3 (pixels/cm)" ); + + CPLFree( pabyResData ); + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Collect IPR box. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(oBox.GetType(),"jp2i") ) + { + if( pszXMLIPR == NULL ) + { + pszXMLIPR = (char*) oBox.ReadBoxData(); + CPLXMLNode* psNode = CPLParseXMLString(pszXMLIPR); + if( psNode == NULL ) + { + CPLFree(pszXMLIPR); + pszXMLIPR = NULL; + } + else + CPLDestroyXMLNode(psNode); + } + else + { + CPLDebug("GDALJP2", "Too many IPR boxes. Ignoring this one"); + } + } + + if (!oBox.ReadNext()) + break; + } + + return TRUE; +} + +/************************************************************************/ +/* ParseJP2GeoTIFF() */ +/************************************************************************/ + +int GDALJP2Metadata::ParseJP2GeoTIFF() + +{ + if(! CSLTestBoolean(CPLGetConfigOption("GDAL_USE_GEOJP2", "TRUE")) ) + return FALSE; + + int abValidProjInfo[MAX_JP2GEOTIFF_BOXES] = { FALSE }; + char* apszProjection[MAX_JP2GEOTIFF_BOXES] = { NULL }; + double aadfGeoTransform[MAX_JP2GEOTIFF_BOXES][6]; + int anGCPCount[MAX_JP2GEOTIFF_BOXES] = { 0 }; + GDAL_GCP *apasGCPList[MAX_JP2GEOTIFF_BOXES] = { NULL }; + int abPixelIsPoint[MAX_JP2GEOTIFF_BOXES] = { 0 }; + char** apapszRPCMD[MAX_JP2GEOTIFF_BOXES] = { NULL }; + + int i; + int nMax = MIN(nGeoTIFFBoxesCount, MAX_JP2GEOTIFF_BOXES); + for(i=0; i < nMax; i++) + { + /* -------------------------------------------------------------------- */ + /* Convert raw data into projection and geotransform. */ + /* -------------------------------------------------------------------- */ + aadfGeoTransform[i][0] = 0; + aadfGeoTransform[i][1] = 1; + aadfGeoTransform[i][2] = 0; + aadfGeoTransform[i][3] = 0; + aadfGeoTransform[i][4] = 0; + aadfGeoTransform[i][5] = 1; + if( GTIFWktFromMemBufEx( pasGeoTIFFBoxes[i].nGeoTIFFSize, + pasGeoTIFFBoxes[i].pabyGeoTIFFData, + &apszProjection[i], aadfGeoTransform[i], + &anGCPCount[i], &apasGCPList[i], + &abPixelIsPoint[i], &apapszRPCMD[i] ) == CE_None ) + { + if( apszProjection[i] != NULL && strlen(apszProjection[i]) != 0 ) + abValidProjInfo[i] = TRUE; + } + } + + /* Detect which box is the better one */ + int iBestIndex = -1; + for(i=0; i < nMax; i++) + { + if( abValidProjInfo[i] && iBestIndex < 0 ) + { + iBestIndex = i; + } + else if( abValidProjInfo[i] && apszProjection[i] != NULL ) + { + /* Anything else than a LOCAL_CS will probably be better */ + if( EQUALN(apszProjection[iBestIndex], "LOCAL_CS", strlen("LOCAL_CS")) ) + iBestIndex = i; + } + } + + if( iBestIndex < 0 ) + { + for(i=0; i < nMax; i++) + { + if( aadfGeoTransform[i][0] != 0 + || aadfGeoTransform[i][1] != 1 + || aadfGeoTransform[i][2] != 0 + || aadfGeoTransform[i][3] != 0 + || aadfGeoTransform[i][4] != 0 + || aadfGeoTransform[i][5] != 1 + || anGCPCount[i] > 0 + || apapszRPCMD[i] != NULL ) + { + iBestIndex = i; + } + } + } + + if( iBestIndex >= 0 ) + { + pszProjection = apszProjection[iBestIndex]; + memcpy(adfGeoTransform, aadfGeoTransform[iBestIndex], 6 * sizeof(double)); + nGCPCount = anGCPCount[iBestIndex]; + pasGCPList = apasGCPList[iBestIndex]; + bPixelIsPoint = abPixelIsPoint[iBestIndex]; + papszRPCMD = apapszRPCMD[iBestIndex]; + + if( adfGeoTransform[0] != 0 + || adfGeoTransform[1] != 1 + || adfGeoTransform[2] != 0 + || adfGeoTransform[3] != 0 + || adfGeoTransform[4] != 0 + || adfGeoTransform[5] != 1 ) + bHaveGeoTransform = TRUE; + + if( pszProjection ) + CPLDebug( "GDALJP2Metadata", + "Got projection from GeoJP2 (geotiff) box (%d): %s", + iBestIndex, pszProjection ); + } + + /* Cleanup unused boxes */ + for(i=0; i < nMax; i++) + { + if( i != iBestIndex ) + { + CPLFree( apszProjection[i] ); + if( anGCPCount[i] > 0 ) + { + GDALDeinitGCPs( anGCPCount[i], apasGCPList[i] ); + CPLFree( apasGCPList[i] ); + } + CSLDestroy( apapszRPCMD[i] ); + } + } + + return iBestIndex >= 0; +} + +/************************************************************************/ +/* ParseMSIG() */ +/************************************************************************/ + +int GDALJP2Metadata::ParseMSIG() + +{ + if( nMSIGSize < 70 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Try and extract worldfile parameters and adjust. */ +/* -------------------------------------------------------------------- */ + memcpy( adfGeoTransform + 0, pabyMSIGData + 22 + 8 * 4, 8 ); + memcpy( adfGeoTransform + 1, pabyMSIGData + 22 + 8 * 0, 8 ); + memcpy( adfGeoTransform + 2, pabyMSIGData + 22 + 8 * 2, 8 ); + memcpy( adfGeoTransform + 3, pabyMSIGData + 22 + 8 * 5, 8 ); + memcpy( adfGeoTransform + 4, pabyMSIGData + 22 + 8 * 1, 8 ); + memcpy( adfGeoTransform + 5, pabyMSIGData + 22 + 8 * 3, 8 ); + + // data is in LSB (little endian) order in file. + CPL_LSBPTR64( adfGeoTransform + 0 ); + CPL_LSBPTR64( adfGeoTransform + 1 ); + CPL_LSBPTR64( adfGeoTransform + 2 ); + CPL_LSBPTR64( adfGeoTransform + 3 ); + CPL_LSBPTR64( adfGeoTransform + 4 ); + CPL_LSBPTR64( adfGeoTransform + 5 ); + + // correct for center of pixel vs. top left of pixel + adfGeoTransform[0] -= 0.5 * adfGeoTransform[1]; + adfGeoTransform[0] -= 0.5 * adfGeoTransform[2]; + adfGeoTransform[3] -= 0.5 * adfGeoTransform[4]; + adfGeoTransform[3] -= 0.5 * adfGeoTransform[5]; + + bHaveGeoTransform = TRUE; + + return TRUE; +} + +/************************************************************************/ +/* GetDictionaryItem() */ +/************************************************************************/ + +static CPLXMLNode * +GetDictionaryItem( char **papszGMLMetadata, const char *pszURN ) + +{ + char *pszLabel; + const char *pszFragmentId = NULL; + int i; + + + if( EQUALN(pszURN,"urn:jp2k:xml:", 13) ) + pszLabel = CPLStrdup( pszURN + 13 ); + else if( EQUALN(pszURN,"urn:ogc:tc:gmljp2:xml:", 22) ) + pszLabel = CPLStrdup( pszURN + 22 ); + else if( EQUALN(pszURN,"gmljp2://xml/",13) ) + pszLabel = CPLStrdup( pszURN + 13 ); + else + pszLabel = CPLStrdup( pszURN ); + +/* -------------------------------------------------------------------- */ +/* Split out label and fragment id. */ +/* -------------------------------------------------------------------- */ + for( i = 0; pszLabel[i] != '#'; i++ ) + { + if( pszLabel[i] == '\0' ) + { + CPLFree(pszLabel); + return NULL; + } + } + + pszFragmentId = pszLabel + i + 1; + pszLabel[i] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Can we find an XML box with the desired label? */ +/* -------------------------------------------------------------------- */ + const char *pszDictionary = + CSLFetchNameValue( papszGMLMetadata, pszLabel ); + + if( pszDictionary == NULL ) + { + CPLFree(pszLabel); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try and parse the dictionary. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psDictTree = CPLParseXMLString( pszDictionary ); + + if( psDictTree == NULL ) + { + CPLFree(pszLabel); + return NULL; + } + + CPLStripXMLNamespace( psDictTree, NULL, TRUE ); + + CPLXMLNode *psDictRoot = CPLSearchXMLNode( psDictTree, "=Dictionary" ); + + if( psDictRoot == NULL ) + { + CPLDestroyXMLNode( psDictTree ); + CPLFree(pszLabel); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Search for matching id. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psEntry, *psHit = NULL; + for( psEntry = psDictRoot->psChild; + psEntry != NULL && psHit == NULL; + psEntry = psEntry->psNext ) + { + const char *pszId; + + if( psEntry->eType != CXT_Element ) + continue; + + if( !EQUAL(psEntry->pszValue,"dictionaryEntry") ) + continue; + + if( psEntry->psChild == NULL ) + continue; + + pszId = CPLGetXMLValue( psEntry->psChild, "id", "" ); + + if( EQUAL(pszId, pszFragmentId) ) + psHit = CPLCloneXMLTree( psEntry->psChild ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( pszLabel ); + CPLDestroyXMLNode( psDictTree ); + + return psHit; +} + + +/************************************************************************/ +/* GMLSRSLookup() */ +/* */ +/* Lookup an SRS in a dictionary inside this file. We will get */ +/* something like: */ +/* urn:jp2k:xml:CRSDictionary.xml#crs1112 */ +/* */ +/* We need to split the filename from the fragment id, and */ +/* lookup the fragment in the file if we can find it our */ +/* list of labelled xml boxes. */ +/************************************************************************/ + +int GDALJP2Metadata::GMLSRSLookup( const char *pszURN ) + +{ + CPLXMLNode *psDictEntry = GetDictionaryItem( papszGMLMetadata, pszURN ); + + if( psDictEntry == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Reserialize this fragment. */ +/* -------------------------------------------------------------------- */ + char *pszDictEntryXML = CPLSerializeXMLTree( psDictEntry ); + CPLDestroyXMLNode( psDictEntry ); + +/* -------------------------------------------------------------------- */ +/* Try to convert into an OGRSpatialReference. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + int bSuccess = FALSE; + + if( oSRS.importFromXML( pszDictEntryXML ) == OGRERR_NONE ) + { + CPLFree( pszProjection ); + pszProjection = NULL; + + oSRS.exportToWkt( &pszProjection ); + bSuccess = TRUE; + } + + CPLFree( pszDictEntryXML ); + + return bSuccess; +} + +/************************************************************************/ +/* ParseGMLCoverageDesc() */ +/************************************************************************/ + +int GDALJP2Metadata::ParseGMLCoverageDesc() + +{ + if(! CSLTestBoolean(CPLGetConfigOption("GDAL_USE_GMLJP2", "TRUE")) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Do we have an XML doc that is apparently a coverage */ +/* description? */ +/* -------------------------------------------------------------------- */ + const char *pszCoverage = CSLFetchNameValue( papszGMLMetadata, + "gml.root-instance" ); + + if( pszCoverage == NULL ) + return FALSE; + + CPLDebug( "GDALJP2Metadata", "Found GML Box:\n%s", pszCoverage ); + +/* -------------------------------------------------------------------- */ +/* Try parsing the XML. Wipe any namespace prefixes. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psXML = CPLParseXMLString( pszCoverage ); + + if( psXML == NULL ) + return FALSE; + + CPLStripXMLNamespace( psXML, NULL, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Isolate RectifiedGrid. Eventually we will need to support */ +/* other georeferencing objects. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRG = CPLSearchXMLNode( psXML, "=RectifiedGrid" ); + CPLXMLNode *psOriginPoint = NULL; + const char *pszOffset1=NULL, *pszOffset2=NULL; + + if( psRG != NULL ) + { + psOriginPoint = CPLGetXMLNode( psRG, "origin.Point" ); + + + CPLXMLNode *psOffset1 = CPLGetXMLNode( psRG, "offsetVector" ); + if( psOffset1 != NULL ) + { + pszOffset1 = CPLGetXMLValue( psOffset1, "", NULL ); + pszOffset2 = CPLGetXMLValue( psOffset1->psNext, "=offsetVector", + NULL ); + } + } + +/* -------------------------------------------------------------------- */ +/* If we are missing any of the origin or 2 offsets then give up. */ +/* -------------------------------------------------------------------- */ + if( psOriginPoint == NULL || pszOffset1 == NULL || pszOffset2 == NULL ) + { + CPLDestroyXMLNode( psXML ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Extract origin location. */ +/* -------------------------------------------------------------------- */ + OGRPoint *poOriginGeometry = NULL; + const char *pszSRSName = NULL; + + if( psOriginPoint != NULL ) + { + poOriginGeometry = (OGRPoint *) + OGR_G_CreateFromGMLTree( psOriginPoint ); + + if( poOriginGeometry != NULL + && wkbFlatten(poOriginGeometry->getGeometryType()) != wkbPoint ) + { + delete poOriginGeometry; + poOriginGeometry = NULL; + } + + // SRS? + pszSRSName = CPLGetXMLValue( psOriginPoint, "srsName", NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Extract offset(s) */ +/* -------------------------------------------------------------------- */ + char **papszOffset1Tokens = NULL; + char **papszOffset2Tokens = NULL; + int bSuccess = FALSE; + + papszOffset1Tokens = + CSLTokenizeStringComplex( pszOffset1, " ,", FALSE, FALSE ); + papszOffset2Tokens = + CSLTokenizeStringComplex( pszOffset2, " ,", FALSE, FALSE ); + + if( CSLCount(papszOffset1Tokens) >= 2 + && CSLCount(papszOffset2Tokens) >= 2 + && poOriginGeometry != NULL ) + { + adfGeoTransform[0] = poOriginGeometry->getX(); + adfGeoTransform[1] = CPLAtof(papszOffset1Tokens[0]); + adfGeoTransform[2] = CPLAtof(papszOffset2Tokens[0]); + adfGeoTransform[3] = poOriginGeometry->getY(); + adfGeoTransform[4] = CPLAtof(papszOffset1Tokens[1]); + adfGeoTransform[5] = CPLAtof(papszOffset2Tokens[1]); + + // offset from center of pixel. + adfGeoTransform[0] -= adfGeoTransform[1]*0.5; + adfGeoTransform[0] -= adfGeoTransform[2]*0.5; + adfGeoTransform[3] -= adfGeoTransform[4]*0.5; + adfGeoTransform[3] -= adfGeoTransform[5]*0.5; + + bSuccess = TRUE; + bHaveGeoTransform = TRUE; + } + + CSLDestroy( papszOffset1Tokens ); + CSLDestroy( papszOffset2Tokens ); + + if( poOriginGeometry != NULL ) + delete poOriginGeometry; + +/* -------------------------------------------------------------------- */ +/* If we still don't have an srsName, check for it on the */ +/* boundedBy Envelope. Some products */ +/* (ie. EuropeRasterTile23.jpx) use this as the only srsName */ +/* delivery vehicle. */ +/* -------------------------------------------------------------------- */ + if( pszSRSName == NULL ) + { + pszSRSName = + CPLGetXMLValue( psXML, + "=FeatureCollection.boundedBy.Envelope.srsName", + NULL ); + } +/* -------------------------------------------------------------------- */ +/* Examples of DGIWG_Profile_of_JPEG2000_for_Georeference_Imagery.pdf */ +/* have srsName only on RectifiedGrid element. */ +/* -------------------------------------------------------------------- */ + if( psRG != NULL && pszSRSName == NULL ) + { + pszSRSName = CPLGetXMLValue( psRG, "srsName", NULL ); + } + +/* -------------------------------------------------------------------- */ +/* If we have gotten a geotransform, then try to interprete the */ +/* srsName. */ +/* -------------------------------------------------------------------- */ + int bNeedAxisFlip = FALSE; + + OGRSpatialReference oSRS; + if( bSuccess && pszSRSName != NULL + && (pszProjection == NULL || strlen(pszProjection) == 0) ) + { + if( EQUALN(pszSRSName,"epsg:",5) ) + { + if( oSRS.SetFromUserInput( pszSRSName ) == OGRERR_NONE ) + oSRS.exportToWkt( &pszProjection ); + } + else if( (EQUALN(pszSRSName,"urn:",4) + && strstr(pszSRSName,":def:") != NULL + && oSRS.importFromURN(pszSRSName) == OGRERR_NONE) || + /* GMLJP2 v2.0 uses CRS URL instead of URN */ + /* See e.g. http://schemas.opengis.net/gmljp2/2.0/examples/minimalInstance.xml */ + (EQUALN(pszSRSName,"http://www.opengis.net/def/crs/", + strlen("http://www.opengis.net/def/crs/")) + && oSRS.importFromCRSURL(pszSRSName) == OGRERR_NONE) ) + { + oSRS.exportToWkt( &pszProjection ); + + // Per #2131 + if( oSRS.EPSGTreatsAsLatLong() || oSRS.EPSGTreatsAsNorthingEasting() ) + { + CPLDebug( "GMLJP2", "Request axis flip for SRS=%s", + pszSRSName ); + bNeedAxisFlip = TRUE; + } + } + else if( !GMLSRSLookup( pszSRSName ) ) + { + CPLDebug( "GDALJP2Metadata", + "Unable to evaluate SRSName=%s", + pszSRSName ); + } + } + + if( pszProjection ) + CPLDebug( "GDALJP2Metadata", + "Got projection from GML box: %s", + pszProjection ); + +/* -------------------------------------------------------------------- */ +/* Do we need to flip the axes? */ +/* -------------------------------------------------------------------- */ + if( bNeedAxisFlip + && CSLTestBoolean( CPLGetConfigOption( "GDAL_IGNORE_AXIS_ORIENTATION", + "FALSE" ) ) ) + { + bNeedAxisFlip = FALSE; + CPLDebug( "GMLJP2", "Suppressed axis flipping based on GDAL_IGNORE_AXIS_ORIENTATION." ); + } + + if( pszSRSName && bNeedAxisFlip ) + { + // Suppress explicit axis order in SRS definition + + OGR_SRSNode *poGEOGCS = oSRS.GetAttrNode( "GEOGCS" ); + if( poGEOGCS != NULL ) + poGEOGCS->StripNodes( "AXIS" ); + + OGR_SRSNode *poPROJCS = oSRS.GetAttrNode( "PROJCS" ); + if (poPROJCS != NULL && oSRS.EPSGTreatsAsNorthingEasting()) + poPROJCS->StripNodes( "AXIS" ); + + CPLFree(pszProjection); + oSRS.exportToWkt( &pszProjection ); + + } + + /* Some Pleiades files have explicit Easting */ + /* Northing to override default EPSG order */ + if( bNeedAxisFlip && psRG != NULL ) + { + int nAxisCount = 0; + int bFirstAxisIsEastOrLong = FALSE, bSecondAxisIsNorthOrLat = FALSE; + for(CPLXMLNode* psIter = psRG->psChild; psIter != NULL; psIter = psIter->psNext ) + { + if( psIter->eType == CXT_Element && strcmp(psIter->pszValue, "axisName") == 0 && + psIter->psChild != NULL && psIter->psChild->eType == CXT_Text ) + { + if( nAxisCount == 0 && + (EQUALN(psIter->psChild->pszValue, "EAST", 4) || + EQUALN(psIter->psChild->pszValue, "LONG", 4) ) ) + { + bFirstAxisIsEastOrLong = TRUE; + } + else if( nAxisCount == 1 && + (EQUALN(psIter->psChild->pszValue, "NORTH", 5) || + EQUALN(psIter->psChild->pszValue, "LAT", 3)) ) + { + bSecondAxisIsNorthOrLat = TRUE; + } + nAxisCount ++; + } + } + if( bFirstAxisIsEastOrLong && bSecondAxisIsNorthOrLat ) + { + CPLDebug( "GMLJP2", "Disable axis flip because of explicit axisName disabling it" ); + bNeedAxisFlip = FALSE; + } + } + + CPLDestroyXMLNode( psXML ); + psXML = NULL; + psRG = NULL; + + if( bNeedAxisFlip ) + { + double dfTemp; + + CPLDebug( "GMLJP2", + "Flipping axis orientation in GMLJP2 coverage description." ); + + dfTemp = adfGeoTransform[0]; + adfGeoTransform[0] = adfGeoTransform[3]; + adfGeoTransform[3] = dfTemp; + + int swapWith1Index = 4; + int swapWith2Index = 5; + + /* Look if we have GDAL_JP2K_ALT_OFFSETVECTOR_ORDER=TRUE as a XML comment */ + int bHasAltOffsetVectorOrderComment = + strstr(pszCoverage, "GDAL_JP2K_ALT_OFFSETVECTOR_ORDER=TRUE") != NULL; + + if( bHasAltOffsetVectorOrderComment || + CSLTestBoolean( CPLGetConfigOption( "GDAL_JP2K_ALT_OFFSETVECTOR_ORDER", + "FALSE" ) ) ) + { + swapWith1Index = 5; + swapWith2Index = 4; + CPLDebug( "GMLJP2", "Choosing alternate GML \"\" order based on " + "GDAL_JP2K_ALT_OFFSETVECTOR_ORDER." ); + } + + dfTemp = adfGeoTransform[1]; + adfGeoTransform[1] = adfGeoTransform[swapWith1Index]; + adfGeoTransform[swapWith1Index] = dfTemp; + + dfTemp = adfGeoTransform[2]; + adfGeoTransform[2] = adfGeoTransform[swapWith2Index]; + adfGeoTransform[swapWith2Index] = dfTemp; + + /* Found in autotest/gdrivers/data/ll.jp2 */ + if( adfGeoTransform[1] == 0.0 && adfGeoTransform[2] < 0.0 && + adfGeoTransform[4] > 0.0 && adfGeoTransform[5] == 0.0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "It is likely that the axis order of the GMLJP2 box is not " + "consistent with the EPSG order and that the resulting georeferencing " + "will be incorrect. Try setting GDAL_IGNORE_AXIS_ORIENTATION=TRUE if it is the case"); + } + } + + return pszProjection != NULL && bSuccess; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +void GDALJP2Metadata::SetProjection( const char *pszWKT ) + +{ + CPLFree( pszProjection ); + pszProjection = CPLStrdup(pszWKT); +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +void GDALJP2Metadata::SetGCPs( int nCount, const GDAL_GCP *pasGCPsIn ) + +{ + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + + nGCPCount = nCount; + pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPsIn); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +void GDALJP2Metadata::SetGeoTransform( double *padfGT ) + +{ + memcpy( adfGeoTransform, padfGT, sizeof(double) * 6 ); +} + +/************************************************************************/ +/* SetRPCMD() */ +/************************************************************************/ + +void GDALJP2Metadata::SetRPCMD( char** papszRPCMDIn ) + +{ + CSLDestroy( papszRPCMD ); + papszRPCMD = CSLDuplicate(papszRPCMDIn); +} + +/************************************************************************/ +/* CreateJP2GeoTIFF() */ +/************************************************************************/ + +GDALJP2Box *GDALJP2Metadata::CreateJP2GeoTIFF() + +{ +/* -------------------------------------------------------------------- */ +/* Prepare the memory buffer containing the degenerate GeoTIFF */ +/* file. */ +/* -------------------------------------------------------------------- */ + int nGTBufSize = 0; + unsigned char *pabyGTBuf = NULL; + + if( GTIFMemBufFromWktEx( pszProjection, adfGeoTransform, + nGCPCount, pasGCPList, + &nGTBufSize, &pabyGTBuf, bPixelIsPoint, + papszRPCMD ) != CE_None ) + return NULL; + + if( nGTBufSize == 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Write to a box on the JP2 file. */ +/* -------------------------------------------------------------------- */ + GDALJP2Box *poBox; + + poBox = GDALJP2Box::CreateUUIDBox( msi_uuid2, nGTBufSize, pabyGTBuf ); + + CPLFree( pabyGTBuf ); + + return poBox; +} + +/************************************************************************/ +/* GetGMLJP2GeoreferencingInfo() */ +/************************************************************************/ + +int GDALJP2Metadata::GetGMLJP2GeoreferencingInfo( int& nEPSGCode, + double adfOrigin[2], + double adfXVector[2], + double adfYVector[2], + const char*& pszComment, + CPLString& osDictBox, + int& bNeedAxisFlip ) +{ + +/* -------------------------------------------------------------------- */ +/* Try do determine a PCS or GCS code we can use. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS; + char *pszWKTCopy = (char *) pszProjection; + nEPSGCode = 0; + bNeedAxisFlip = FALSE; + + if( oSRS.importFromWkt( &pszWKTCopy ) != OGRERR_NONE ) + return FALSE; + + if( oSRS.IsProjected() ) + { + const char *pszAuthName = oSRS.GetAuthorityName( "PROJCS" ); + + if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") ) + { + nEPSGCode = atoi(oSRS.GetAuthorityCode( "PROJCS" )); + } + } + else if( oSRS.IsGeographic() ) + { + const char *pszAuthName = oSRS.GetAuthorityName( "GEOGCS" ); + + if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") ) + { + nEPSGCode = atoi(oSRS.GetAuthorityCode( "GEOGCS" )); + } + } + + // Save error state as importFromEPSGA() will call CPLReset() + int errNo = CPLGetLastErrorNo(); + CPLErr eErr = CPLGetLastErrorType(); + CPLString osLastErrorMsg = CPLGetLastErrorMsg(); + + // Determinte if we need to flix axis. Reimport from EPSG and make + // sure not to strip axis definitions to determine the axis order. + if( nEPSGCode != 0 && oSRS.importFromEPSGA(nEPSGCode) == OGRERR_NONE ) + { + if( oSRS.EPSGTreatsAsLatLong() || oSRS.EPSGTreatsAsNorthingEasting() ) + { + bNeedAxisFlip = TRUE; + } + } + + // Restore error state + CPLErrorSetState( eErr, errNo, osLastErrorMsg); + +/* -------------------------------------------------------------------- */ +/* Prepare coverage origin and offset vectors. Take axis */ +/* order into account if needed. */ +/* -------------------------------------------------------------------- */ + adfOrigin[0] = adfGeoTransform[0] + adfGeoTransform[1] * 0.5 + + adfGeoTransform[4] * 0.5; + adfOrigin[1] = adfGeoTransform[3] + adfGeoTransform[2] * 0.5 + + adfGeoTransform[5] * 0.5; + adfXVector[0] = adfGeoTransform[1]; + adfXVector[1] = adfGeoTransform[2]; + + adfYVector[0] = adfGeoTransform[4]; + adfYVector[1] = adfGeoTransform[5]; + + if( bNeedAxisFlip + && CSLTestBoolean( CPLGetConfigOption( "GDAL_IGNORE_AXIS_ORIENTATION", + "FALSE" ) ) ) + { + bNeedAxisFlip = FALSE; + CPLDebug( "GMLJP2", "Suppressed axis flipping on write based on GDAL_IGNORE_AXIS_ORIENTATION." ); + } + + pszComment = ""; + if( bNeedAxisFlip ) + { + double dfTemp; + + CPLDebug( "GMLJP2", "Flipping GML coverage axis order." ); + + dfTemp = adfOrigin[0]; + adfOrigin[0] = adfOrigin[1]; + adfOrigin[1] = dfTemp; + + if( CSLTestBoolean( CPLGetConfigOption( "GDAL_JP2K_ALT_OFFSETVECTOR_ORDER", + "FALSE" ) ) ) + { + CPLDebug( "GMLJP2", "Choosing alternate GML \"\" order based on " + "GDAL_JP2K_ALT_OFFSETVECTOR_ORDER." ); + + /* In this case the swapping is done in an "X" pattern */ + dfTemp = adfXVector[0]; + adfXVector[0] = adfYVector[1]; + adfYVector[1] = dfTemp; + + dfTemp = adfYVector[0]; + adfYVector[0] = adfXVector[1]; + adfXVector[1] = dfTemp; + + /* We add this as an XML comment so that we know we must do OffsetVector flipping on reading */ + pszComment = " \n"; + } + else + { + dfTemp = adfXVector[0]; + adfXVector[0] = adfXVector[1]; + adfXVector[1] = dfTemp; + + dfTemp = adfYVector[0]; + adfYVector[0] = adfYVector[1]; + adfYVector[1] = dfTemp; + } + } + +/* -------------------------------------------------------------------- */ +/* If we need a user defined CRSDictionary entry, prepare it */ +/* here. */ +/* -------------------------------------------------------------------- */ + if( nEPSGCode == 0 ) + { + char *pszGMLDef = NULL; + + if( oSRS.exportToXML( &pszGMLDef, NULL ) == OGRERR_NONE ) + { + char* pszWKT = NULL; + oSRS.exportToWkt(&pszWKT); + char* pszXMLEscapedWKT = CPLEscapeString(pszWKT, -1, CPLES_XML); + CPLFree(pszWKT); + osDictBox.Printf( +"\n" +" Dictionnary for cursom SRS %s\n" +" Dictionnary for custom SRS\n" +" \n" +"%s\n" +" \n" +"\n", + pszXMLEscapedWKT, pszGMLDef ); + CPLFree(pszXMLEscapedWKT); + } + CPLFree( pszGMLDef ); + } + + return TRUE; +} + +/************************************************************************/ +/* CreateGMLJP2() */ +/************************************************************************/ + +GDALJP2Box *GDALJP2Metadata::CreateGMLJP2( int nXSize, int nYSize ) + +{ +/* -------------------------------------------------------------------- */ +/* This is a backdoor to let us embed a literal gmljp2 chunk */ +/* supplied by the user as an external file. This is mostly */ +/* for preparing test files with exotic contents. */ +/* -------------------------------------------------------------------- */ + if( CPLGetConfigOption( "GMLJP2OVERRIDE", NULL ) != NULL ) + { + VSILFILE *fp = VSIFOpenL( CPLGetConfigOption( "GMLJP2OVERRIDE",""), "r" ); + char *pszGML = NULL; + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open GMLJP2OVERRIDE file." ); + return NULL; + } + + VSIFSeekL( fp, 0, SEEK_END ); + int nLength = (int) VSIFTellL( fp ); + pszGML = (char *) CPLCalloc(1,nLength+1); + VSIFSeekL( fp, 0, SEEK_SET ); + VSIFReadL( pszGML, 1, nLength, fp ); + VSIFCloseL( fp ); + + GDALJP2Box *apoGMLBoxes[2]; + + apoGMLBoxes[0] = GDALJP2Box::CreateLblBox( "gml.data" ); + apoGMLBoxes[1] = + GDALJP2Box::CreateLabelledXMLAssoc( "gml.root-instance", + pszGML ); + + GDALJP2Box *poGMLData = GDALJP2Box::CreateAsocBox( 2, apoGMLBoxes); + + delete apoGMLBoxes[0]; + delete apoGMLBoxes[1]; + + CPLFree( pszGML ); + + return poGMLData; + } + + int nEPSGCode; + double adfOrigin[2]; + double adfXVector[2]; + double adfYVector[2]; + const char* pszComment = ""; + CPLString osDictBox; + int bNeedAxisFlip = FALSE; + if( !GetGMLJP2GeoreferencingInfo( nEPSGCode, adfOrigin, + adfXVector, adfYVector, + pszComment, osDictBox, bNeedAxisFlip ) ) + { + return NULL; + } + + char szSRSName[100]; + if( nEPSGCode != 0 ) + sprintf( szSRSName, "urn:ogc:def:crs:EPSG::%d", nEPSGCode ); + else + strcpy( szSRSName, + "gmljp2://xml/CRSDictionary.gml#ogrcrs1" ); + + // Compute bounding box + double dfX1 = adfGeoTransform[0]; + double dfX2 = adfGeoTransform[0] + nXSize * adfGeoTransform[1]; + double dfX3 = adfGeoTransform[0] + nYSize * adfGeoTransform[2]; + double dfX4 = adfGeoTransform[0] + nXSize * adfGeoTransform[1] + nYSize * adfGeoTransform[2]; + double dfY1 = adfGeoTransform[3]; + double dfY2 = adfGeoTransform[3] + nXSize * adfGeoTransform[4]; + double dfY3 = adfGeoTransform[3] + nYSize * adfGeoTransform[5]; + double dfY4 = adfGeoTransform[3] + nXSize * adfGeoTransform[4] + nYSize * adfGeoTransform[5]; + double dfLCX = MIN(MIN(dfX1,dfX2),MIN(dfX3,dfX4)); + double dfLCY = MIN(MIN(dfY1,dfY2),MIN(dfY3,dfY4)); + double dfUCX = MAX(MAX(dfX1,dfX2),MAX(dfX3,dfX4)); + double dfUCY = MAX(MAX(dfY1,dfY2),MAX(dfY3,dfY4)); + if( bNeedAxisFlip ) + { + double dfTmp = dfLCX; + dfLCX = dfLCY; + dfLCY = dfTmp; + + dfTmp = dfUCX; + dfUCX = dfUCY; + dfUCY = dfTmp; + } + +/* -------------------------------------------------------------------- */ +/* For now we hardcode for a minimal instance format. */ +/* -------------------------------------------------------------------- */ + CPLString osDoc; + + osDoc.Printf( +"\n" +" \n" +" \n" +" %.15g %.15g\n" +" %.15g %.15g\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" 0 0\n" +" %d %d\n" +" \n" +" \n" +" x\n" +" y\n" +" \n" +" \n" +" %.15g %.15g\n" +" \n" +" \n" +"%s" +" %.15g %.15g\n" +" %.15g %.15g\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" gmljp2://codestream/0\n" +" Record Interleaved\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n", + szSRSName, dfLCX, dfLCY, dfUCX, dfUCY, + nXSize-1, nYSize-1, szSRSName, adfOrigin[0], adfOrigin[1], + pszComment, + szSRSName, adfXVector[0], adfXVector[1], + szSRSName, adfYVector[0], adfYVector[1] ); + +/* -------------------------------------------------------------------- */ +/* Setup the gml.data label. */ +/* -------------------------------------------------------------------- */ + GDALJP2Box *apoGMLBoxes[5]; + int nGMLBoxes = 0; + + apoGMLBoxes[nGMLBoxes++] = GDALJP2Box::CreateLblBox( "gml.data" ); + +/* -------------------------------------------------------------------- */ +/* Setup gml.root-instance. */ +/* -------------------------------------------------------------------- */ + apoGMLBoxes[nGMLBoxes++] = + GDALJP2Box::CreateLabelledXMLAssoc( "gml.root-instance", osDoc ); + +/* -------------------------------------------------------------------- */ +/* Add optional dictionary. */ +/* -------------------------------------------------------------------- */ + if( osDictBox.size() > 0 ) + apoGMLBoxes[nGMLBoxes++] = + GDALJP2Box::CreateLabelledXMLAssoc( "CRSDictionary.gml", + osDictBox ); + +/* -------------------------------------------------------------------- */ +/* Bundle gml.data boxes into an association. */ +/* -------------------------------------------------------------------- */ + GDALJP2Box *poGMLData = GDALJP2Box::CreateAsocBox( nGMLBoxes, apoGMLBoxes); + +/* -------------------------------------------------------------------- */ +/* Cleanup working boxes. */ +/* -------------------------------------------------------------------- */ + while( nGMLBoxes > 0 ) + delete apoGMLBoxes[--nGMLBoxes]; + + return poGMLData; +} + +/************************************************************************/ +/* GDALGMLJP2GetXMLRoot() */ +/************************************************************************/ + +static CPLXMLNode* GDALGMLJP2GetXMLRoot(CPLXMLNode* psNode) +{ + for( ; psNode != NULL; psNode = psNode->psNext ) + { + if( psNode->eType == CXT_Element && psNode->pszValue[0] != '?' ) + return psNode; + } + return NULL; +} + +/************************************************************************/ +/* GDALGMLJP2PatchFeatureCollectionSubstitutionGroup() */ +/************************************************************************/ + +static void GDALGMLJP2PatchFeatureCollectionSubstitutionGroup(CPLXMLNode* psRoot) +{ + /* GML 3.2 SF profile recommands the feature collection type to derive */ + /* from gml:AbstractGML to prevent it to be included in another feature */ + /* collection, but this is what we want to do. So patch that... */ + + /* */ + /* --> */ + /* */ + if( psRoot->eType == CXT_Element && + (strcmp(psRoot->pszValue, "schema") == 0 || strcmp(psRoot->pszValue, "xs:schema") == 0) ) + { + for(CPLXMLNode* psIter = psRoot->psChild; psIter != NULL; psIter = psIter->psNext) + { + if( psIter->eType == CXT_Element && + (strcmp(psIter->pszValue, "element") == 0 || strcmp(psIter->pszValue, "xs:element") == 0) && + strcmp(CPLGetXMLValue(psIter, "name", ""), "FeatureCollection") == 0 && + strcmp(CPLGetXMLValue(psIter, "substitutionGroup", ""), "gml:AbstractGML") == 0 ) + { + CPLDebug("GMLJP2", "Patching substitutionGroup=\"gml:AbstractGML\" to \"gml:AbstractFeature\""); + CPLSetXMLValue( psIter, "#substitutionGroup", "gml:AbstractFeature" ); + break; + } + } + } +} + +/************************************************************************/ +/* CreateGMLJP2V2() */ +/************************************************************************/ + +class GMLJP2V2GMLFileDesc +{ + public: + CPLString osFile; + CPLString osRemoteResource; + CPLString osNamespace; + CPLString osSchemaLocation; + int bInline; + int bParentCoverageCollection; + + GMLJP2V2GMLFileDesc(): bInline(TRUE), bParentCoverageCollection(TRUE) {} +}; + +class GMLJP2V2AnnotationDesc +{ + public: + CPLString osFile; +}; + +class GMLJP2V2MetadataDesc +{ + public: + CPLString osFile; + CPLString osContent; + CPLString osTemplateFile; + CPLString osSourceFile; + int bGDALMetadata; + int bParentCoverageCollection; + + GMLJP2V2MetadataDesc(): bGDALMetadata(FALSE), bParentCoverageCollection(TRUE) {} +}; + +class GMLJP2V2StyleDesc +{ + public: + CPLString osFile; + int bParentCoverageCollection; + + GMLJP2V2StyleDesc(): bParentCoverageCollection(TRUE) {} +}; + +class GMLJP2V2ExtensionDesc +{ + public: + CPLString osFile; + int bParentCoverageCollection; + + GMLJP2V2ExtensionDesc(): bParentCoverageCollection(TRUE) {} +}; +class GMLJP2V2BoxDesc +{ + public: + CPLString osFile; + CPLString osLabel; +}; + +GDALJP2Box *GDALJP2Metadata::CreateGMLJP2V2( int nXSize, int nYSize, + const char* pszDefFilename, + GDALDataset* poSrcDS ) + +{ + CPLString osRootGMLId = "ID_GMLJP2_0"; + CPLString osGridCoverage; + CPLString osGridCoverageFile; + int bCRSURL = TRUE; + std::vector aoMetadata; + std::vector aoAnnotations; + std::vector aoGMLFiles; + std::vector aoStyles; + std::vector aoExtensions; + std::vector aoBoxes; + +/* -------------------------------------------------------------------- */ +/* Parse definition file. */ +/* -------------------------------------------------------------------- */ + if( pszDefFilename && !EQUAL(pszDefFilename, "YES") && !EQUAL(pszDefFilename, "TRUE") ) + { + GByte* pabyContent = NULL; + if( pszDefFilename[0] != '{' ) + { + if( !VSIIngestFile( NULL, pszDefFilename, &pabyContent, NULL, -1 ) ) + return NULL; + } + +/* +{ + "#doc" : "Unless otherwise specified, all elements are optional", + + "#root_instance_doc": "Describe content of the GMLJP2CoverageCollection", + "root_instance": { + "#gml_id_doc": "Specify GMLJP2CoverageCollection id here. Default is ID_GMLJP2_0", + "gml_id": "some_gml_id", + + "#grid_coverage_file_doc": [ + "External XML file, whose root might be a GMLJP2GridCoverage, ", + "GMLJP2RectifiedGridCoverage or a GMLJP2ReferenceableGridCoverage", + "If not specified, GDAL will auto-generate a GMLJP2RectifiedGridCoverage" ], + "grid_coverage_file": "gmljp2gridcoverage.xml", + + "#crs_url_doc": [ + "true for http://www.opengis.net/def/crs/EPSG/0/XXXX CRS URL.", + "If false, use CRS URN. Default value is true" ], + "crs_url": true, + + "#metadata_doc": [ "An array of metadata items. Can be either strings, with ", + "a filename or directly inline XML content, or either ", + "a more complete description." ], + "metadata": [ + + "dcmetadata.xml", + + { + "#file_doc": "Can use relative or absolute paths. Exclusive of content, gdal_metadata and generated_metadata.", + "file": "dcmetadata.xml", + + "#gdal_metadata_doc": "Whether to serialize GDAL metadata as GDALMultiDomainMetadata", + "gdal_metadata": false, + + "#dynamic_metadata_doc": + [ "The metadata file will be generated from a template and a source file.", + "The template is a valid GMLJP2 metadata XML tree with placeholders like", + "{{{XPATH(some_xpath_expression)}}}", + "that are evalated from the source XML file. Typical use case", + "is to generate a gmljp2:eopMetadata from the XML metadata", + "provided by the image provider in their own particular format." ], + "dynamic_metadata" : + { + "template": "my_template.xml", + "source": "my_source.xml" + }, + + "#content": "Exclusive of file. Inline XML metadata content", + "content": "Some simple textual metadata", + + "#parent_node": ["Where to put the metadata.", + "Under CoverageCollection (default) or GridCoverage" ], + "parent_node": "CoverageCollection" + }, + ], + + "#annotations_doc": [ "An array of filenames, either directly KML files", + "or other vector files recognized by GDAL that ", + "will be translated on-the-fly as KML" ], + "annotations": [ + "my.kml" + ], + + "#gml_filelist_doc" :[ + "An array of GML files. Can be either GML filenames, ", + "or a more complete description" ], + "gml_filelist": [ + + "my.gml", + + { + "#file_doc": "Can use relative or absolute paths. Exclusive of remote_resource", + "file": "converted/test_0.gml", + + "#remote_resource_doc": "URL of a feature collection that must be referenced through a xlink:href", + "remote_resource": "http://svn.osgeo.org/gdal/trunk/autotest/ogr/data/expected_gml_gml32.gml", + + "#namespace_doc": ["The namespace in schemaLocation for which to substitute", + "its original schemaLocation with the one provided below.", + "Ignored for a remote_resource"], + "namespace": "http://example.com", + + "#schema_location_doc": ["Value of the substitued schemaLocation. ", + "Typically a schema box label (link)", + "Ignored for a remote_resource"], + "schema_location": "gmljp2://xml/schema_0.xsd", + + "#inline_doc": [ + "Whether to inline the content, or put it in a separate xml box. Default is true", + "Ignored for a remote_resource." ], + "inline": true, + + "#parent_node": ["Where to put the FeatureCollection.", + "Under CoverageCollection (default) or GridCoverage" ], + "parent_node": "CoverageCollection" + } + ], + + "#styles_doc: [ "An array of styles. For example SLD files" ], + "styles" : [ + { + "#file_doc": "Can use relative or absolute paths.", + "file": "my.sld", + + "#parent_node": ["Where to put the FeatureCollection.", + "Under CoverageCollection (default) or GridCoverage" ], + "parent_node": "CoverageCollection" + } + ], + + "#extensions_doc: [ "An array of extensions." ], + "extensions" : [ + { + "#file_doc": "Can use relative or absolute paths.", + "file": "my.xml", + + "#parent_node": ["Where to put the FeatureCollection.", + "Under CoverageCollection (default) or GridCoverage" ], + "parent_node": "CoverageCollection" + } + ] + }, + + "#boxes_doc": "An array to describe the content of XML asoc boxes", + "boxes": [ + { + "#file_doc": "can use relative or absolute paths. Required", + "file": "converted/test_0.xsd", + + "#label_doc": ["the label of the XML box. If not specified, will be the ", + "filename without the directory part." ], + "label": "schema_0.xsd" + } + ] +} +*/ + + json_tokener* jstok = NULL; + json_object* poObj = NULL; + + jstok = json_tokener_new(); + poObj = json_tokener_parse_ex(jstok, pabyContent ? (const char*) pabyContent : pszDefFilename, -1); + CPLFree(pabyContent); + if( jstok->err != json_tokener_success) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JSON parsing error: %s (at offset %d)", + json_tokener_error_desc(jstok->err), jstok->char_offset); + json_tokener_free(jstok); + return NULL; + } + json_tokener_free(jstok); + + json_object* poRootInstance = json_object_object_get(poObj, "root_instance"); + if( poRootInstance && json_object_get_type(poRootInstance) == json_type_object ) + { + json_object* poGMLId = json_object_object_get(poRootInstance, "gml_id"); + if( poGMLId && json_object_get_type(poGMLId) == json_type_string ) + osRootGMLId = json_object_get_string(poGMLId); + + json_object* poGridCoverageFile = json_object_object_get(poRootInstance, "grid_coverage_file"); + if( poGridCoverageFile && json_object_get_type(poGridCoverageFile) == json_type_string ) + osGridCoverageFile = json_object_get_string(poGridCoverageFile); + + json_object* poCRSURL = json_object_object_get(poRootInstance, "crs_url"); + if( poCRSURL && json_object_get_type(poCRSURL) == json_type_boolean ) + bCRSURL = json_object_get_boolean(poCRSURL); + + + json_object* poMetadatas = json_object_object_get(poRootInstance, "metadata"); + if( poMetadatas && json_object_get_type(poMetadatas) == json_type_array ) + { + for(int i=0;i 0 ) + { + CPLXMLNode* psTmp = CPLParseXMLFile(osGridCoverageFile); + if( psTmp == NULL ) + return NULL; + CPLXMLNode* psTmpRoot = GDALGMLJP2GetXMLRoot(psTmp); + if( psTmpRoot ) + { + char* pszTmp = CPLSerializeXMLTree(psTmpRoot); + osGridCoverage = pszTmp; + CPLFree(pszTmp); + } + CPLDestroyXMLNode(psTmp); + } + } + + CPLString osDictBox; + CPLString osDoc; + + if( osGridCoverage.size() == 0 ) + { +/* -------------------------------------------------------------------- */ +/* Prepare GMLJP2RectifiedGridCoverage */ +/* -------------------------------------------------------------------- */ + int nEPSGCode = 0; + double adfOrigin[2]; + double adfXVector[2]; + double adfYVector[2]; + const char* pszComment = ""; + int bNeedAxisFlip = FALSE; + if( !GetGMLJP2GeoreferencingInfo( nEPSGCode, adfOrigin, + adfXVector, adfYVector, + pszComment, osDictBox, bNeedAxisFlip ) ) + { + return NULL; + } + + char szSRSName[100] = {0}; + if( nEPSGCode != 0 ) + { + if( bCRSURL ) + sprintf( szSRSName, "http://www.opengis.net/def/crs/EPSG/0/%d", nEPSGCode ); + else + sprintf( szSRSName, "urn:ogc:def:crs:EPSG::%d", nEPSGCode ); + } + else + strcpy( szSRSName, + "gmljp2://xml/CRSDictionary.gml#ogrcrs1" ); + + osGridCoverage.Printf( +" \n" +" \n" +" \n" +" \n" +" \n" +" 0 0\n" +" %d %d\n" +" \n" +" \n" +" x\n" +" y\n" +" \n" +" \n" +" %.15g %.15g\n" +" \n" +" \n" +"%s" +" %.15g %.15g\n" +" %.15g %.15g\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" gmljp2://codestream/0\n" +" inapplicable\n" +" \n" +" \n" +" \n" +" \n", + osRootGMLId.c_str(), + osRootGMLId.c_str(), + szSRSName, + nXSize-1, nYSize-1, szSRSName, adfOrigin[0], adfOrigin[1], + pszComment, + szSRSName, adfXVector[0], adfXVector[1], + szSRSName, adfYVector[0], adfYVector[1] ); + } + +/* -------------------------------------------------------------------- */ +/* Main node. */ +/* -------------------------------------------------------------------- */ + osDoc.Printf( +//"\n" +"\n" +" \n" +" \n" +" \n" +" \n" +" gmljp2://codestream\n" +" inapplicable\n" +" \n" +" \n" +" \n" +" \n" +"%s" +" \n" +"\n", + osRootGMLId.c_str(), + osGridCoverage.c_str() ); + +/* -------------------------------------------------------------------- */ +/* Process metadata, annotations and features collections. */ +/* -------------------------------------------------------------------- */ + std::vector aosTmpFiles; + int bRootHasXLink = FALSE; + if( aoMetadata.size() || aoAnnotations.size() || aoGMLFiles.size() || + aoStyles.size() || aoExtensions.size() ) + { + CPLXMLNode* psRoot = CPLParseXMLString(osDoc); + CPLAssert(psRoot); + CPLXMLNode* psGMLJP2CoverageCollection = GDALGMLJP2GetXMLRoot(psRoot); + CPLAssert(psGMLJP2CoverageCollection); + + for( int i=0; i < (int)aoMetadata.size(); i++ ) + { + CPLXMLNode* psMetadata; + if( aoMetadata[i].osFile.size() ) + psMetadata = CPLParseXMLFile(aoMetadata[i].osFile); + else if( aoMetadata[i].osContent.size() ) + psMetadata = CPLParseXMLString(aoMetadata[i].osContent); + else if( aoMetadata[i].bGDALMetadata ) + { + psMetadata = CreateGDALMultiDomainMetadataXML(poSrcDS, TRUE); + if( psMetadata ) + { + CPLSetXMLValue(psMetadata, "#xmlns", "http://gdal.org"); + CPLXMLNode* psNewMetadata = CPLCreateXMLNode(NULL, CXT_Element, "gmljp2:metadata"); + CPLAddXMLChild(psNewMetadata, psMetadata); + psMetadata = psNewMetadata; + } + } + else + psMetadata = GDALGMLJP2GenerateMetadata(aoMetadata[i].osTemplateFile, + aoMetadata[i].osSourceFile); + if( psMetadata == NULL ) + continue; + CPLXMLNode* psMetadataRoot = GDALGMLJP2GetXMLRoot(psMetadata); + if( psMetadataRoot ) + { + if( strcmp(psMetadataRoot->pszValue, "eop:EarthObservation") == 0 ) + { + CPLXMLNode* psNewMetadata = CPLCreateXMLNode(NULL, CXT_Element, "gmljp2:eopMetadata"); + CPLAddXMLChild(psNewMetadata, CPLCloneXMLTree(psMetadataRoot)); + CPLDestroyXMLNode(psMetadata); + psMetadata = psMetadataRoot = psNewMetadata; + } + if( strcmp(psMetadataRoot->pszValue, "gmljp2:isoMetadata") != 0 && + strcmp(psMetadataRoot->pszValue, "gmljp2:eopMetadata") != 0 && + strcmp(psMetadataRoot->pszValue, "gmljp2:dcMetadata") != 0 && + strcmp(psMetadataRoot->pszValue, "gmljp2:metadata") != 0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "The metadata root node should be one of gmljp2:isoMetadata, " + "gmljp2:eopMetadata, gmljp2:dcMetadata or gmljp2:metadata"); + } + else if( aoMetadata[i].bParentCoverageCollection ) + { + /* Insert the gmlcov:metadata link as the next sibbling of */ + /* GMLJP2CoverageCollection.rangeType */ + CPLXMLNode* psRangeType = + CPLGetXMLNode(psGMLJP2CoverageCollection, "gmlcov:rangeType"); + CPLAssert(psRangeType); + CPLXMLNode* psNodeAfterWhichToInsert = psRangeType; + CPLXMLNode* psNext = psNodeAfterWhichToInsert->psNext; + while( psNext != NULL && psNext->eType == CXT_Element && + strcmp(psNext->pszValue, "gmlcov:metadata") == 0 ) + { + psNodeAfterWhichToInsert = psNext; + psNext = psNext->psNext; + } + psNodeAfterWhichToInsert->psNext = NULL; + CPLXMLNode* psGMLCovMetadata = CPLCreateXMLNode( + psGMLJP2CoverageCollection, CXT_Element, "gmlcov:metadata" ); + psGMLCovMetadata->psNext = psNext; + CPLXMLNode* psGMLJP2Metadata = CPLCreateXMLNode( + psGMLCovMetadata, CXT_Element, "gmljp2:Metadata" ); + CPLAddXMLChild( psGMLJP2Metadata, CPLCloneXMLTree(psMetadataRoot) ); + } + else + { + /* Insert the gmlcov:metadata link as the last child of */ + /* GMLJP2RectifiedGridCoverage typically */ + CPLXMLNode* psFeatureMemberOfGridCoverage = + CPLGetXMLNode(psGMLJP2CoverageCollection, "gmljp2:featureMember"); + CPLAssert(psFeatureMemberOfGridCoverage); + CPLXMLNode* psGridCoverage = psFeatureMemberOfGridCoverage->psChild; + CPLAssert(psGridCoverage); + CPLXMLNode* psGMLCovMetadata = CPLCreateXMLNode( + psGridCoverage, CXT_Element, "gmlcov:metadata" ); + CPLXMLNode* psGMLJP2Metadata = CPLCreateXMLNode( + psGMLCovMetadata, CXT_Element, "gmljp2:Metadata" ); + CPLAddXMLChild( psGMLJP2Metadata, CPLCloneXMLTree(psMetadataRoot) ); + } + } + CPLDestroyXMLNode(psMetadata); + } + + // Examples of inline or reference feature collections can be found + // in http://schemas.opengis.net/gmljp2/2.0/examples/gmljp2.xml + + for( int i=0; i < (int)aoGMLFiles.size(); i++ ) + { + // Is the file already a GML file ? + CPLXMLNode* psGMLFile = NULL; + if( aoGMLFiles[i].osFile.size() ) + { + if( EQUAL(CPLGetExtension(aoGMLFiles[i].osFile), "gml") || + EQUAL(CPLGetExtension(aoGMLFiles[i].osFile), "xml") ) + { + psGMLFile = CPLParseXMLFile(aoGMLFiles[i].osFile); + } + GDALDriverH hDrv = NULL; + if( psGMLFile == NULL ) + { + hDrv = GDALIdentifyDriver(aoGMLFiles[i].osFile, NULL); + if( hDrv == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "%s is no a GDAL recognized file", + aoGMLFiles[i].osFile.c_str()); + continue; + } + } + GDALDriverH hGMLDrv = GDALGetDriverByName("GML"); + if( psGMLFile == NULL && hDrv == hGMLDrv ) + { + // Yes, parse it + psGMLFile = CPLParseXMLFile(aoGMLFiles[i].osFile); + } + else if( psGMLFile == NULL ) + { + if( hGMLDrv == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot translate %s to GML", + aoGMLFiles[i].osFile.c_str()); + continue; + } + + // On-the-fly translation to GML 3.2 + GDALDatasetH hSrcDS = GDALOpenEx(aoGMLFiles[i].osFile, 0, NULL, NULL, NULL); + if( hSrcDS ) + { + CPLString osTmpFile = CPLSPrintf("/vsimem/gmljp2/%p/%d/%s.gml", + this, + i, + CPLGetBasename(aoGMLFiles[i].osFile)); + char* apszOptions[2]; + apszOptions[0] = (char*) "FORMAT=GML3.2"; + apszOptions[1] = NULL; + GDALDatasetH hDS = GDALCreateCopy(hGMLDrv, osTmpFile, hSrcDS, + FALSE, apszOptions, NULL, NULL); + if( hDS ) + { + GDALClose(hDS); + psGMLFile = CPLParseXMLFile(osTmpFile); + aoGMLFiles[i].osFile = osTmpFile; + VSIUnlink(osTmpFile); + aosTmpFiles.push_back(CPLResetExtension(osTmpFile, "xsd")); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Conversion of %s to GML failed", + aoGMLFiles[i].osFile.c_str()); + } + } + GDALClose(hSrcDS); + } + if( psGMLFile == NULL ) + continue; + } + + CPLXMLNode* psGMLFileRoot = psGMLFile ? GDALGMLJP2GetXMLRoot(psGMLFile) : NULL; + if( psGMLFileRoot || aoGMLFiles[i].osRemoteResource.size() ) + { + CPLXMLNode *node_f; + if( aoGMLFiles[i].bParentCoverageCollection ) + { + // Insert in gmljp2:featureMember.gmljp2:GMLJP2Features.gmljp2:feature + CPLXMLNode *node_fm = CPLCreateXMLNode( + psGMLJP2CoverageCollection, CXT_Element, "gmljp2:featureMember" ); + + CPLXMLNode *node_gf = CPLCreateXMLNode( + node_fm, CXT_Element, "gmljp2:GMLJP2Features" ); + + + CPLSetXMLValue(node_gf, "#gml:id", CPLSPrintf("%s_GMLJP2Features_%d", + osRootGMLId.c_str(), + i)); + + node_f = CPLCreateXMLNode( node_gf, CXT_Element, "gmljp2:feature" ); + } + else + { + CPLXMLNode* psFeatureMemberOfGridCoverage = + CPLGetXMLNode(psGMLJP2CoverageCollection, "gmljp2:featureMember"); + CPLAssert(psFeatureMemberOfGridCoverage); + CPLXMLNode* psGridCoverage = psFeatureMemberOfGridCoverage->psChild; + CPLAssert(psGridCoverage); + node_f = CPLCreateXMLNode( psGridCoverage, CXT_Element, "gmljp2:feature" ); + } + + if( !aoGMLFiles[i].bInline || aoGMLFiles[i].osRemoteResource.size() ) + { + if( !bRootHasXLink ) + { + bRootHasXLink = TRUE; + CPLSetXMLValue(psGMLJP2CoverageCollection, "#xmlns:xlink", + "http://www.w3.org/1999/xlink"); + } + } + + if( aoGMLFiles[i].osRemoteResource.size() ) + { + CPLSetXMLValue(node_f, "#xlink:href", + aoGMLFiles[i].osRemoteResource.c_str()); + continue; + } + + CPLString osTmpFile; + if( !aoGMLFiles[i].bInline || aoGMLFiles[i].osRemoteResource.size() ) + { + osTmpFile = CPLSPrintf("/vsimem/gmljp2/%p/%d/%s.gml", + this, + i, + CPLGetBasename(aoGMLFiles[i].osFile)); + aosTmpFiles.push_back(osTmpFile); + + GMLJP2V2BoxDesc oDesc; + oDesc.osFile = osTmpFile; + oDesc.osLabel = CPLGetFilename(oDesc.osFile); + aoBoxes.push_back(oDesc); + + CPLSetXMLValue(node_f, "#xlink:href", + CPLSPrintf("gmljp2://xml/%s", oDesc.osLabel.c_str())); + } + + if( CPLGetXMLNode(psGMLFileRoot, "xmlns") == NULL && + CPLGetXMLNode(psGMLFileRoot, "xmlns:gml") == NULL ) + { + CPLSetXMLValue(psGMLFileRoot, "#xmlns", + "http://www.opengis.net/gml/3.2"); + } + + // modify the gml id making it unique for this document + CPLXMLNode* psGMLFileGMLId = + CPLGetXMLNode(psGMLFileRoot, "gml:id"); + if( psGMLFileGMLId && psGMLFileGMLId->eType == CXT_Attribute ) + CPLSetXMLValue( psGMLFileGMLId, "", + CPLSPrintf("%s_%d_%s", + osRootGMLId.c_str(), i, + psGMLFileGMLId->psChild->pszValue) ); + psGMLFileGMLId = NULL; + //PrefixAllGMLIds(psGMLFileRoot, CPLSPrintf("%s_%d_", osRootGMLId.c_str(), i)); + + // replace schema location + CPLXMLNode* psSchemaLocation = + CPLGetXMLNode(psGMLFileRoot, "xsi:schemaLocation"); + if( psSchemaLocation && psSchemaLocation->eType == CXT_Attribute ) + { + char **papszTokens = CSLTokenizeString2( + psSchemaLocation->psChild->pszValue, " \t\n", + CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES); + CPLString osSchemaLocation; + + if( CSLCount(papszTokens) == 2 && + aoGMLFiles[i].osNamespace.size() == 0 && + aoGMLFiles[i].osSchemaLocation.size() ) + { + osSchemaLocation += papszTokens[0]; + osSchemaLocation += " "; + osSchemaLocation += aoGMLFiles[i].osSchemaLocation; + } + + else if( CSLCount(papszTokens) == 2 && + (aoGMLFiles[i].osNamespace.size() == 0 || + strcmp(papszTokens[0], aoGMLFiles[i].osNamespace) == 0) && + aoGMLFiles[i].osSchemaLocation.size() == 0 ) + { + VSIStatBufL sStat; + CPLString osXSD; + if( CSLCount(papszTokens) == 2 && + !CPLIsFilenameRelative(papszTokens[1]) && + VSIStatL(papszTokens[1], &sStat) == 0 ) + { + osXSD = papszTokens[1]; + } + else if( CSLCount(papszTokens) == 2 && + CPLIsFilenameRelative(papszTokens[1]) && + VSIStatL(CPLFormFilename(CPLGetDirname(aoGMLFiles[i].osFile), + papszTokens[1], NULL), + &sStat) == 0 ) + { + osXSD = CPLFormFilename(CPLGetDirname(aoGMLFiles[i].osFile), + papszTokens[1], NULL); + } + if( osXSD.size() ) + { + GMLJP2V2BoxDesc oDesc; + oDesc.osFile = osXSD; + oDesc.osLabel = CPLGetFilename(oDesc.osFile); + osSchemaLocation += papszTokens[0]; + osSchemaLocation += " "; + osSchemaLocation += "gmljp2://xml/"; + osSchemaLocation += oDesc.osLabel; + int j; + for( j=0; j<(int)aoBoxes.size(); j++) + { + if( aoBoxes[j].osLabel == oDesc.osLabel ) + break; + } + if( j == (int)aoBoxes.size() ) + aoBoxes.push_back(oDesc); + } + } + + else if( (CSLCount(papszTokens) % 2) == 0 ) + { + for(char** papszIter = papszTokens; *papszIter; papszIter += 2 ) + { + if( osSchemaLocation.size() ) + osSchemaLocation += " "; + if( aoGMLFiles[i].osNamespace.size() && + aoGMLFiles[i].osSchemaLocation.size() && + strcmp(papszIter[0], aoGMLFiles[i].osNamespace) == 0 ) + { + osSchemaLocation += papszIter[0]; + osSchemaLocation += " "; + osSchemaLocation += aoGMLFiles[i].osSchemaLocation; + } + else + { + osSchemaLocation += papszIter[0]; + osSchemaLocation += " "; + osSchemaLocation += papszIter[1]; + } + } + } + CSLDestroy(papszTokens); + CPLSetXMLValue( psSchemaLocation, "", osSchemaLocation); + } + + if( aoGMLFiles[i].bInline ) + CPLAddXMLChild(node_f, CPLCloneXMLTree(psGMLFileRoot)); + else + CPLSerializeXMLTreeToFile( psGMLFile, osTmpFile ); + } + CPLDestroyXMLNode(psGMLFile); + } + + // Cf http://schemas.opengis.net/gmljp2/2.0/examples/gmljp2_annotation.xml + for( int i=0; i < (int)aoAnnotations.size(); i++ ) + { + // Is the file already a KML file ? + CPLXMLNode* psKMLFile = NULL; + if( EQUAL(CPLGetExtension(aoAnnotations[i].osFile), "kml") ) + psKMLFile = CPLParseXMLFile(aoAnnotations[i].osFile); + GDALDriverH hDrv = NULL; + if( psKMLFile == NULL ) + { + hDrv = GDALIdentifyDriver(aoAnnotations[i].osFile, NULL); + if( hDrv == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s is no a GDAL recognized file", + aoAnnotations[i].osFile.c_str()); + continue; + } + } + GDALDriverH hKMLDrv = GDALGetDriverByName("KML"); + GDALDriverH hLIBKMLDrv = GDALGetDriverByName("LIBKML"); + if( psKMLFile == NULL && (hDrv == hKMLDrv || hDrv == hLIBKMLDrv) ) + { + // Yes, parse it + psKMLFile = CPLParseXMLFile(aoAnnotations[i].osFile); + } + else if( psKMLFile == NULL ) + { + if( hKMLDrv == NULL && hLIBKMLDrv == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot translate %s to KML", + aoAnnotations[i].osFile.c_str()); + continue; + } + + // On-the-fly translation to KML + GDALDatasetH hSrcDS = GDALOpenEx(aoAnnotations[i].osFile, 0, NULL, NULL, NULL); + if( hSrcDS ) + { + CPLString osTmpFile = CPLSPrintf("/vsimem/gmljp2/%p/%d/%s.kml", + this, + i, + CPLGetBasename(aoAnnotations[i].osFile)); + char* apszOptions[2]; + apszOptions[0] = NULL; + apszOptions[1] = NULL; + GDALDatasetH hDS = GDALCreateCopy(hLIBKMLDrv ? hLIBKMLDrv : hKMLDrv, + osTmpFile, hSrcDS, + FALSE, apszOptions, NULL, NULL); + if( hDS ) + { + GDALClose(hDS); + psKMLFile = CPLParseXMLFile(osTmpFile); + aoAnnotations[i].osFile = osTmpFile; + VSIUnlink(osTmpFile); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Conversion of %s to KML failed", + aoAnnotations[i].osFile.c_str()); + } + } + GDALClose(hSrcDS); + } + if( psKMLFile == NULL ) + continue; + + CPLXMLNode* psKMLFileRoot = GDALGMLJP2GetXMLRoot(psKMLFile); + if( psKMLFileRoot ) + { + CPLXMLNode* psFeatureMemberOfGridCoverage = + CPLGetXMLNode(psGMLJP2CoverageCollection, "gmljp2:featureMember"); + CPLAssert(psFeatureMemberOfGridCoverage); + CPLXMLNode* psGridCoverage = psFeatureMemberOfGridCoverage->psChild; + CPLAssert(psGridCoverage); + CPLXMLNode *psAnnotation = CPLCreateXMLNode( + psGridCoverage, CXT_Element, "gmljp2:annotation" ); + + /* Add a xsi:schemaLocation if not already present */ + if( psKMLFileRoot->eType == CXT_Element && + strcmp(psKMLFileRoot->pszValue, "kml") == 0 && + CPLGetXMLNode(psKMLFileRoot, "xsi:schemaLocation") == NULL && + strcmp(CPLGetXMLValue(psKMLFileRoot, "xmlns", ""), + "http://www.opengis.net/kml/2.2") == 0 ) + { + CPLSetXMLValue(psKMLFileRoot, "#xsi:schemaLocation", + "http://www.opengis.net/kml/2.2 http://schemas.opengis.net/kml/2.2.0/ogckml22.xsd"); + } + + CPLAddXMLChild(psAnnotation, CPLCloneXMLTree(psKMLFileRoot)); + } + CPLDestroyXMLNode(psKMLFile); + } + + // Add styles + for( int i=0; i < (int)aoStyles.size(); i++ ) + { + CPLXMLNode* psStyle = CPLParseXMLFile(aoStyles[i].osFile); + if( psStyle == NULL ) + continue; + + CPLXMLNode* psStyleRoot = GDALGMLJP2GetXMLRoot(psStyle); + if( psStyleRoot ) + { + CPLXMLNode *psGMLJP2Style; + if( aoStyles[i].bParentCoverageCollection ) + { + psGMLJP2Style = CPLCreateXMLNode( psGMLJP2CoverageCollection, CXT_Element, "gmljp2:style" ); + } + else + { + CPLXMLNode* psFeatureMemberOfGridCoverage = + CPLGetXMLNode(psGMLJP2CoverageCollection, "gmljp2:featureMember"); + CPLAssert(psFeatureMemberOfGridCoverage); + CPLXMLNode* psGridCoverage = psFeatureMemberOfGridCoverage->psChild; + CPLAssert(psGridCoverage); + psGMLJP2Style = CPLCreateXMLNode( psGridCoverage, CXT_Element, "gmljp2:style" ); + } + + // Add dummy namespace for validation purposes if needed + if( strchr(psStyleRoot->pszValue, ':') == NULL && + CPLGetXMLValue(psStyleRoot, "xmlns", NULL) == NULL ) + { + CPLSetXMLValue(psStyleRoot, "#xmlns", "http://undefined_namespace"); + } + + CPLAddXMLChild( psGMLJP2Style, CPLCloneXMLTree(psStyleRoot) ); + } + CPLDestroyXMLNode(psStyle); + } + + // Add extensions + for( int i=0; i < (int)aoExtensions.size(); i++ ) + { + CPLXMLNode* psExtension = CPLParseXMLFile(aoExtensions[i].osFile); + if( psExtension == NULL ) + continue; + + CPLXMLNode* psExtensionRoot = GDALGMLJP2GetXMLRoot(psExtension); + if( psExtensionRoot ) + { + CPLXMLNode *psGMLJP2Extension; + if( aoExtensions[i].bParentCoverageCollection ) + { + psGMLJP2Extension = CPLCreateXMLNode( psGMLJP2CoverageCollection, CXT_Element, "gmljp2:extension" ); + } + else + { + CPLXMLNode* psFeatureMemberOfGridCoverage = + CPLGetXMLNode(psGMLJP2CoverageCollection, "gmljp2:featureMember"); + CPLAssert(psFeatureMemberOfGridCoverage); + CPLXMLNode* psGridCoverage = psFeatureMemberOfGridCoverage->psChild; + CPLAssert(psGridCoverage); + psGMLJP2Extension = CPLCreateXMLNode( psGridCoverage, CXT_Element, "gmljp2:extension" ); + } + + // Add dummy namespace for validation purposes if needed + if( strchr(psExtensionRoot->pszValue, ':') == NULL && + CPLGetXMLValue(psExtensionRoot, "xmlns", NULL) == NULL ) + { + CPLSetXMLValue(psExtensionRoot, "#xmlns", "http://undefined_namespace"); + } + + CPLAddXMLChild( psGMLJP2Extension, CPLCloneXMLTree(psExtensionRoot) ); + } + CPLDestroyXMLNode(psExtension); + } + + char* pszRoot = CPLSerializeXMLTree(psRoot); + CPLDestroyXMLNode(psRoot); + psRoot = NULL; + osDoc = pszRoot; + CPLFree(pszRoot); + pszRoot = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Setup the gml.data label. */ +/* -------------------------------------------------------------------- */ + std::vector apoGMLBoxes; + + apoGMLBoxes.push_back(GDALJP2Box::CreateLblBox( "gml.data" )); + +/* -------------------------------------------------------------------- */ +/* Setup gml.root-instance. */ +/* -------------------------------------------------------------------- */ + apoGMLBoxes.push_back( + GDALJP2Box::CreateLabelledXMLAssoc( "gml.root-instance", osDoc )); + +/* -------------------------------------------------------------------- */ +/* Add optional dictionary. */ +/* -------------------------------------------------------------------- */ + if( osDictBox.size() > 0 ) + apoGMLBoxes.push_back( + GDALJP2Box::CreateLabelledXMLAssoc( "CRSDictionary.gml", + osDictBox ) ); + +/* -------------------------------------------------------------------- */ +/* Additional user specified boxes. */ +/* -------------------------------------------------------------------- */ + for( int i=0; i < (int)aoBoxes.size(); i++ ) + { + GByte* pabyContent = NULL; + if( VSIIngestFile( NULL, aoBoxes[i].osFile, &pabyContent, NULL, -1 ) ) + { + CPLXMLNode* psNode = CPLParseXMLString((const char*)pabyContent); + CPLFree(pabyContent); + pabyContent = NULL; + if( psNode ) + { + CPLXMLNode* psRoot = GDALGMLJP2GetXMLRoot(psNode); + if( psRoot ) + { + GDALGMLJP2PatchFeatureCollectionSubstitutionGroup(psRoot); + pabyContent = (GByte*) CPLSerializeXMLTree(psRoot); + apoGMLBoxes.push_back( + GDALJP2Box::CreateLabelledXMLAssoc( aoBoxes[i].osLabel, + (const char*)pabyContent ) ); + } + CPLDestroyXMLNode (psNode); + } + } + CPLFree(pabyContent); + } + +/* -------------------------------------------------------------------- */ +/* Bundle gml.data boxes into an association. */ +/* -------------------------------------------------------------------- */ + GDALJP2Box *poGMLData = GDALJP2Box::CreateAsocBox( (int)apoGMLBoxes.size(), + &apoGMLBoxes[0]); + +/* -------------------------------------------------------------------- */ +/* Cleanup working boxes. */ +/* -------------------------------------------------------------------- */ + for( int i=0; i < (int)apoGMLBoxes.size(); i++ ) + delete apoGMLBoxes[i]; + + for( int i=0; i < (int)aosTmpFiles.size(); i++ ) + { + VSIUnlink(aosTmpFiles[i]); + } + + return poGMLData; +} + +/************************************************************************/ +/* CreateGDALMultiDomainMetadataXML() */ +/************************************************************************/ + +CPLXMLNode* GDALJP2Metadata::CreateGDALMultiDomainMetadataXML( + GDALDataset* poSrcDS, + int bMainMDDomainOnly ) +{ + GDALMultiDomainMetadata oLocalMDMD; + char** papszSrcMD = CSLDuplicate(poSrcDS->GetMetadata()); + /* Remove useless metadata */ + papszSrcMD = CSLSetNameValue(papszSrcMD, GDALMD_AREA_OR_POINT, NULL); + papszSrcMD = CSLSetNameValue(papszSrcMD, "TIFFTAG_RESOLUTIONUNIT", NULL); + papszSrcMD = CSLSetNameValue(papszSrcMD, "TIFFTAG_XREpsMasterXMLNodeSOLUTION", NULL); + papszSrcMD = CSLSetNameValue(papszSrcMD, "TIFFTAG_YRESOLUTION", NULL); + papszSrcMD = CSLSetNameValue(papszSrcMD, "Corder", NULL); /* from JP2KAK */ + if( poSrcDS->GetDriver() != NULL && + EQUAL(poSrcDS->GetDriver()->GetDescription(), "JP2ECW") ) + { + papszSrcMD = CSLSetNameValue(papszSrcMD, "COMPRESSION_RATE_TARGET", NULL); + papszSrcMD = CSLSetNameValue(papszSrcMD, "COLORSPACE", NULL); + papszSrcMD = CSLSetNameValue(papszSrcMD, "VERSION", NULL); + } + + int bHasMD = FALSE; + if( papszSrcMD && *papszSrcMD ) + { + bHasMD = TRUE; + oLocalMDMD.SetMetadata(papszSrcMD); + } + CSLDestroy(papszSrcMD); + + if( !bMainMDDomainOnly ) + { + char** papszMDList = poSrcDS->GetMetadataDomainList(); + for( char** papszMDListIter = papszMDList; + papszMDListIter && *papszMDListIter; ++papszMDListIter ) + { + if( !EQUAL(*papszMDListIter, "") && + !EQUAL(*papszMDListIter, "IMAGE_STRUCTURE") && + !EQUAL(*papszMDListIter, "JPEG2000") && + !EQUALN(*papszMDListIter, "xml:BOX_", strlen("xml:BOX_")) && + !EQUAL(*papszMDListIter, "xml:gml.root-instance") && + !EQUAL(*papszMDListIter, "xml:XMP") && + !EQUAL(*papszMDListIter, "xml:IPR") ) + { + papszSrcMD = poSrcDS->GetMetadata(*papszMDListIter); + if( papszSrcMD && *papszSrcMD ) + { + bHasMD = TRUE; + oLocalMDMD.SetMetadata(papszSrcMD, *papszMDListIter); + } + } + } + CSLDestroy(papszMDList); + } + + CPLXMLNode* psMasterXMLNode = NULL; + if( bHasMD ) + { + CPLXMLNode* psXMLNode = oLocalMDMD.Serialize(); + psMasterXMLNode = CPLCreateXMLNode( NULL, CXT_Element, + "GDALMultiDomainMetadata" ); + psMasterXMLNode->psChild = psXMLNode; + } + return psMasterXMLNode; +} + +/************************************************************************/ +/* CreateGDALMultiDomainMetadataXMLBox() */ +/************************************************************************/ + +GDALJP2Box *GDALJP2Metadata::CreateGDALMultiDomainMetadataXMLBox( + GDALDataset* poSrcDS, + int bMainMDDomainOnly ) +{ + CPLXMLNode* psMasterXMLNode = CreateGDALMultiDomainMetadataXML( + poSrcDS, bMainMDDomainOnly ); + if( psMasterXMLNode == NULL ) + return NULL; + char* pszXML = CPLSerializeXMLTree(psMasterXMLNode); + CPLDestroyXMLNode(psMasterXMLNode); + + GDALJP2Box* poBox = new GDALJP2Box(); + poBox->SetType("xml "); + poBox->SetWritableData(strlen(pszXML) + 1, (const GByte*)pszXML); + CPLFree(pszXML); + + return poBox; +} + +/************************************************************************/ +/* WriteXMLBoxes() */ +/************************************************************************/ + +GDALJP2Box** GDALJP2Metadata::CreateXMLBoxes( GDALDataset* poSrcDS, + int* pnBoxes ) +{ + GDALJP2Box** papoBoxes = NULL; + *pnBoxes = 0; + char** papszMDList = poSrcDS->GetMetadataDomainList(); + for( char** papszMDListIter = papszMDList; + papszMDListIter && *papszMDListIter; ++papszMDListIter ) + { + /* Write metadata that look like originating from JP2 XML boxes */ + /* as a standalone JP2 XML box */ + if( EQUALN(*papszMDListIter, "xml:BOX_", strlen("xml:BOX_")) ) + { + char** papszSrcMD = poSrcDS->GetMetadata(*papszMDListIter); + if( papszSrcMD && *papszSrcMD ) + { + GDALJP2Box* poBox = new GDALJP2Box(); + poBox->SetType("xml "); + poBox->SetWritableData(strlen(*papszSrcMD) + 1, + (const GByte*)*papszSrcMD); + papoBoxes = (GDALJP2Box**)CPLRealloc(papoBoxes, + sizeof(GDALJP2Box*) * (*pnBoxes + 1)); + papoBoxes[(*pnBoxes) ++] = poBox; + } + } + } + CSLDestroy(papszMDList); + return papoBoxes; +} + +/************************************************************************/ +/* CreateXMPBox() */ +/************************************************************************/ + +GDALJP2Box *GDALJP2Metadata::CreateXMPBox ( GDALDataset* poSrcDS ) +{ + char** papszSrcMD = poSrcDS->GetMetadata("xml:XMP"); + GDALJP2Box* poBox = NULL; + if( papszSrcMD && * papszSrcMD ) + { + poBox = GDALJP2Box::CreateUUIDBox(xmp_uuid, + strlen(*papszSrcMD) + 1, + (const GByte*)*papszSrcMD); + } + return poBox; +} + +/************************************************************************/ +/* CreateIPRBox() */ +/************************************************************************/ + +GDALJP2Box *GDALJP2Metadata::CreateIPRBox ( GDALDataset* poSrcDS ) +{ + char** papszSrcMD = poSrcDS->GetMetadata("xml:IPR"); + GDALJP2Box* poBox = NULL; + if( papszSrcMD && * papszSrcMD ) + { + poBox = new GDALJP2Box(); + poBox->SetType("jp2i"); + poBox->SetWritableData(strlen(*papszSrcMD) + 1, + (const GByte*)*papszSrcMD); + } + return poBox; +} + +/************************************************************************/ +/* IsUUID_MSI() */ +/************************************************************************/ + +int GDALJP2Metadata::IsUUID_MSI(const GByte *abyUUID) +{ + return memcmp(abyUUID, msi_uuid2, 16) == 0; +} + +/************************************************************************/ +/* IsUUID_XMP() */ +/************************************************************************/ + +int GDALJP2Metadata::IsUUID_XMP(const GByte *abyUUID) +{ + return memcmp(abyUUID, xmp_uuid, 16) == 0; +} diff --git a/bazaar/plugin/gdal/gcore/gdaljp2metadata.h b/bazaar/plugin/gdal/gcore/gdaljp2metadata.h new file mode 100644 index 000000000..2a40e9d59 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaljp2metadata.h @@ -0,0 +1,196 @@ +/****************************************************************************** + * $Id: gdaljp2metadata.h 29210 2015-05-19 19:04:28Z rouault $ + * + * Project: GDAL + * Purpose: JP2 Box Reader (and GMLJP2 Interpreter) + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_JP2READER_H_INCLUDED +#define GDAL_JP2READER_H_INCLUDED + +#include "cpl_conv.h" +#include "cpl_vsi.h" +#include "gdal.h" +#include "gdal_priv.h" +#include "cpl_minixml.h" + +/************************************************************************/ +/* GDALJP2Box */ +/************************************************************************/ + +class CPL_DLL GDALJP2Box +{ + + VSILFILE *fpVSIL; + + char szBoxType[5]; + + GIntBig nBoxOffset; + GIntBig nBoxLength; + + GIntBig nDataOffset; + + GByte abyUUID[16]; + + GByte *pabyData; + +public: + GDALJP2Box( VSILFILE * = NULL ); + ~GDALJP2Box(); + + int SetOffset( GIntBig nNewOffset ); + int ReadBox(); + + int ReadFirst(); + int ReadNext(); + + int ReadFirstChild( GDALJP2Box *poSuperBox ); + int ReadNextChild( GDALJP2Box *poSuperBox ); + + GIntBig GetBoxOffset() const { return nBoxOffset; } + GIntBig GetBoxLength() const { return nBoxLength; } + + GIntBig GetDataOffset() const { return nDataOffset; } + GIntBig GetDataLength(); + + const char *GetType() { return szBoxType; } + + GByte *ReadBoxData(); + + int IsSuperBox(); + + int DumpReadable( FILE *, int nIndentLevel = 0 ); + + VSILFILE *GetFILE() { return fpVSIL; } + + const GByte *GetUUID() { return abyUUID; } + + // write support + void SetType( const char * ); + void SetWritableData( int nLength, const GByte *pabyData ); + void AppendWritableData( int nLength, const void *pabyDataIn ); + void AppendUInt32( GUInt32 nVal ); + void AppendUInt16( GUInt16 nVal ); + void AppendUInt8( GByte nVal ); + const GByte*GetWritableData() { return pabyData; } + + // factory methods. + static GDALJP2Box *CreateSuperBox( const char* pszType, + int nCount, GDALJP2Box **papoBoxes ); + static GDALJP2Box *CreateAsocBox( int nCount, GDALJP2Box **papoBoxes ); + static GDALJP2Box *CreateLblBox( const char *pszLabel ); + static GDALJP2Box *CreateLabelledXMLAssoc( const char *pszLabel, + const char *pszXML ); + static GDALJP2Box *CreateUUIDBox( const GByte *pabyUUID, + int nDataSize, const GByte *pabyData ); +}; + +/************************************************************************/ +/* GDALJP2Metadata */ +/************************************************************************/ + +typedef struct _GDALJP2GeoTIFFBox GDALJP2GeoTIFFBox; + +class CPL_DLL GDALJP2Metadata + +{ +private: + void CollectGMLData( GDALJP2Box * ); + int GMLSRSLookup( const char *pszURN ); + + int nGeoTIFFBoxesCount; + GDALJP2GeoTIFFBox *pasGeoTIFFBoxes; + + int nMSIGSize; + GByte *pabyMSIGData; + + int GetGMLJP2GeoreferencingInfo( int& nEPSGCode, + double adfOrigin[2], + double adfXVector[2], + double adfYVector[2], + const char*& pszComment, + CPLString& osDictBox, + int& bNeedAxisFlip ); + static CPLXMLNode* CreateGDALMultiDomainMetadataXML( + GDALDataset* poSrcDS, + int bMainMDDomainOnly ); + +public: + char **papszGMLMetadata; + + int bHaveGeoTransform; + double adfGeoTransform[6]; + int bPixelIsPoint; + + char *pszProjection; + + int nGCPCount; + GDAL_GCP *pasGCPList; + + char **papszRPCMD; + + char **papszMetadata; /* TIFFTAG_?RESOLUTION* for now from resd box */ + char *pszXMPMetadata; + char *pszGDALMultiDomainMetadata; /* as serialized XML */ + char *pszXMLIPR; /* if an IPR box with XML content has been found */ + +public: + GDALJP2Metadata(); + ~GDALJP2Metadata(); + + int ReadBoxes( VSILFILE * fpVSIL ); + + int ParseJP2GeoTIFF(); + int ParseMSIG(); + int ParseGMLCoverageDesc(); + + int ReadAndParse( VSILFILE * fpVSIL ); + int ReadAndParse( const char *pszFilename ); + + // Write oriented. + void SetProjection( const char *pszWKT ); + void SetGeoTransform( double * ); + void SetGCPs( int, const GDAL_GCP * ); + void SetRPCMD( char** papszRPCMDIn ); + + GDALJP2Box *CreateJP2GeoTIFF(); + GDALJP2Box *CreateGMLJP2( int nXSize, int nYSize ); + GDALJP2Box *CreateGMLJP2V2( int nXSize, int nYSize, + const char* pszDefFilename, + GDALDataset* poSrcDS ); + + static GDALJP2Box* CreateGDALMultiDomainMetadataXMLBox( + GDALDataset* poSrcDS, + int bMainMDDomainOnly ); + static GDALJP2Box** CreateXMLBoxes( GDALDataset* poSrcDS, + int* pnBoxes ); + static GDALJP2Box *CreateXMPBox ( GDALDataset* poSrcDS ); + static GDALJP2Box *CreateIPRBox ( GDALDataset* poSrcDS ); + static int IsUUID_MSI(const GByte *abyUUID); + static int IsUUID_XMP(const GByte *abyUUID); +}; + +#endif /* ndef GDAL_JP2READER_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/gcore/gdaljp2metadatagenerator.cpp b/bazaar/plugin/gdal/gcore/gdaljp2metadatagenerator.cpp new file mode 100644 index 000000000..904804858 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaljp2metadatagenerator.cpp @@ -0,0 +1,915 @@ +/****************************************************************************** + * $Id: gdaljp2metadatagenerator.cpp 29073 2015-04-30 11:38:01Z rouault $ + * + * Project: GDAL + * Purpose: GDALJP2Metadata: metadata generator + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2015, European Union Satellite Centre + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "gdaljp2metadatagenerator.h" + +CPL_CVSID("$Id: gdaljp2metadatagenerator.cpp 29073 2015-04-30 11:38:01Z rouault $"); + +//#define ENABLE_BRAIN_DAMAGE + +#ifdef HAVE_LIBXML2 + +#include +#include +#include +#include + +/************************************************************************/ +/* GDALGMLJP2Expr */ +/************************************************************************/ + +typedef enum +{ + GDALGMLJP2Expr_Unknown, + GDALGMLJP2Expr_XPATH, + GDALGMLJP2Expr_STRING_LITERAL, +#ifdef ENABLE_BRAIN_DAMAGE + GDALGMLJP2Expr_NUMERIC_LITERAL, + GDALGMLJP2Expr_ADD, + GDALGMLJP2Expr_SUB, + GDALGMLJP2Expr_NOT, + GDALGMLJP2Expr_AND, + GDALGMLJP2Expr_OR, + GDALGMLJP2Expr_EQ, + GDALGMLJP2Expr_NEQ, + GDALGMLJP2Expr_LT, + GDALGMLJP2Expr_LE, + GDALGMLJP2Expr_GT, + GDALGMLJP2Expr_GE, + GDALGMLJP2Expr_IF, + GDALGMLJP2Expr_CONCAT, + GDALGMLJP2Expr_EVAL, + GDALGMLJP2Expr_CAST_TO_STRING, + GDALGMLJP2Expr_CAST_TO_NUMERIC, + GDALGMLJP2Expr_SUBSTRING, + GDALGMLJP2Expr_SUBSTRING_BEFORE, + GDALGMLJP2Expr_SUBSTRING_AFTER, + GDALGMLJP2Expr_STRING_LENGTH, + GDALGMLJP2Expr_UUID +#endif +} GDALGMLJP2ExprType; + +// {{{IF(EQ(XPATH(), '5'), '', '')}}} + +class GDALGMLJP2Expr +{ + static void SkipSpaces(const char*& pszStr); + static GDALGMLJP2Expr* BuildNaryOp(const char* pszOriStr, + const char*& pszStr, int nary); + + public: + GDALGMLJP2ExprType eType; + CPLString osValue; +#ifdef ENABLE_BRAIN_DAMAGE + std::vector apoSubExpr; +#endif + + GDALGMLJP2Expr(): eType(GDALGMLJP2Expr_Unknown) {} + GDALGMLJP2Expr(const char* pszVal): eType(GDALGMLJP2Expr_STRING_LITERAL), osValue(pszVal) {} + GDALGMLJP2Expr(CPLString osVal): eType(GDALGMLJP2Expr_STRING_LITERAL), osValue(osVal) {} +#ifdef ENABLE_BRAIN_DAMAGE + GDALGMLJP2Expr(bool b): eType(GDALGMLJP2Expr_STRING_LITERAL), osValue(b ? "true" : "false") {} +#endif + ~GDALGMLJP2Expr(); + + GDALGMLJP2Expr Evaluate(xmlXPathContextPtr pXPathCtx, + xmlDocPtr pDoc); + + static GDALGMLJP2Expr* Build(const char* pszOriStr, + const char*& pszStr); + static void ReportError(const char* pszOriStr, + const char* pszStr, + const char* pszIntroMessage = "Parsing error at:\n"); +}; + +/************************************************************************/ +/* Build() */ +/************************************************************************/ + +GDALGMLJP2Expr::~GDALGMLJP2Expr() +{ +#ifdef ENABLE_BRAIN_DAMAGE + for(size_t i=0;i 40 ) + nDist = 40; + CPLString osErrMsg(pszIntroMessage); + CPLString osInvalidExpr = CPLString(pszStr - nDist).substr(0, nDist + 20); + for(int i=(int)nDist-1;i>=0;i--) + { + if( osInvalidExpr[i] == '\n' ) + { + osInvalidExpr = osInvalidExpr.substr(i+1); + nDist -= i+1; + break; + } + } + for(size_t i=nDist;iapoSubExpr.push_back(poSubExpr); + if( nary < 0 && *pszStr == ')' ) + { + break; + } + else if( nary < 0 || i < nary - 1 ) + { + if( *pszStr != ',' ) + { + ReportError(pszOriStr, pszStr); + delete poExpr; + return NULL; + } + pszStr ++; + SkipSpaces(pszStr); + } + } + if( *pszStr != ')' ) + { + ReportError(pszOriStr, pszStr); + delete poExpr; + return NULL; + } + pszStr ++; + return poExpr; +} + +#endif // ENABLE_BRAIN_DAMAGE + +/************************************************************************/ +/* Build() */ +/************************************************************************/ + +#ifdef ENABLE_BRAIN_DAMAGE + +typedef struct +{ + const char* pszOp; + GDALGMLJP2ExprType eType; + int nary; +} GDALGMLJP2Operators; + +#endif + +GDALGMLJP2Expr* GDALGMLJP2Expr::Build(const char* pszOriStr, + const char*& pszStr) +{ + if( EQUALN(pszStr, "{{{", strlen("{{{")) ) + { + pszStr += strlen("{{{"); + SkipSpaces(pszStr); + GDALGMLJP2Expr* poExpr = Build(pszOriStr, pszStr); + if( poExpr == NULL ) + return NULL; + SkipSpaces(pszStr); + if( !EQUALN(pszStr, "}}}", strlen("}}}")) ) + { + ReportError(pszOriStr, pszStr); + delete poExpr; + return NULL; + } + pszStr += strlen("}}}"); + return poExpr; + } + else if( EQUALN(pszStr, "XPATH", strlen("XPATH")) ) + { + pszStr += strlen("XPATH"); + SkipSpaces(pszStr); + if( *pszStr != '(' ) + { + ReportError(pszOriStr, pszStr); + return NULL; + } + pszStr ++; + SkipSpaces(pszStr); + CPLString osValue; + int nParenthesisIndent = 0; + char chLiteralQuote = '\0'; + while( *pszStr ) + { + if( chLiteralQuote != '\0' ) + { + if( *pszStr == chLiteralQuote ) + chLiteralQuote = '\0'; + osValue += *pszStr; + pszStr++; + } + else if( *pszStr == '\'' || *pszStr == '"' ) + { + chLiteralQuote = *pszStr; + osValue += *pszStr; + pszStr++; + } + else if( *pszStr == '(' ) + { + nParenthesisIndent ++; + osValue += *pszStr; + pszStr++; + } + else if( *pszStr == ')' ) + { + nParenthesisIndent --; + if( nParenthesisIndent < 0 ) + { + pszStr++; + GDALGMLJP2Expr* poExpr = new GDALGMLJP2Expr(); + poExpr->eType = GDALGMLJP2Expr_XPATH; + poExpr->osValue = osValue; + //CPLDebug("GMLJP2", "XPath expression '%s'", osValue.c_str()); + return poExpr; + } + osValue += *pszStr; + pszStr++; + } + else + { + osValue += *pszStr; + pszStr++; + } + } + ReportError(pszOriStr, pszStr); + return NULL; + } +#ifdef ENABLE_BRAIN_DAMAGE + else if( pszStr[0] == '\'' ) + { + pszStr ++; + CPLString osValue; + while( *pszStr ) + { + if( *pszStr == '\\' ) + { + if( pszStr[1] == '\\' ) + osValue += "\\"; + else if( pszStr[1] == '\'' ) + osValue += "\'"; + else + { + ReportError(pszOriStr, pszStr); + return NULL; + } + pszStr += 2; + } + else if( *pszStr == '\'' ) + { + pszStr ++; + GDALGMLJP2Expr* poExpr = new GDALGMLJP2Expr(); + poExpr->eType = GDALGMLJP2Expr_STRING_LITERAL; + poExpr->osValue = osValue; + return poExpr; + } + else + { + osValue += *pszStr; + pszStr ++; + } + } + ReportError(pszOriStr, pszStr); + return NULL; + } + else if( pszStr[0] == '-' || pszStr[0] == '.'|| + (pszStr[0] >= '0' && pszStr[0] <= '9') ) + { + CPLString osValue; + while( *pszStr ) + { + if( *pszStr == ' ' || *pszStr == ')' || *pszStr == ',' || *pszStr == '}' ) + { + GDALGMLJP2Expr* poExpr = new GDALGMLJP2Expr(); + poExpr->eType = GDALGMLJP2Expr_NUMERIC_LITERAL; + poExpr->osValue = osValue; + return poExpr; + } + osValue += *pszStr; + pszStr ++; + } + ReportError(pszOriStr, pszStr); + return NULL; + } + else + { + static const GDALGMLJP2Operators asOperators[] = + { + { "IF", GDALGMLJP2Expr_IF, 3 }, + { "ADD", GDALGMLJP2Expr_ADD, 2 }, + { "AND", GDALGMLJP2Expr_AND, 2 }, + { "OR", GDALGMLJP2Expr_OR, 2 }, + { "NOT", GDALGMLJP2Expr_NOT, 1 }, + { "EQ", GDALGMLJP2Expr_EQ, 2 }, + { "NEQ", GDALGMLJP2Expr_NEQ, 2 }, + { "LT", GDALGMLJP2Expr_LT, 2 }, + { "LE", GDALGMLJP2Expr_LE, 2 }, + { "GT", GDALGMLJP2Expr_GT, 2 }, + { "GE", GDALGMLJP2Expr_GE, 2 }, + { "CONCAT", GDALGMLJP2Expr_CONCAT, -1 }, + { "NUMERIC", GDALGMLJP2Expr_CAST_TO_NUMERIC, 1 }, + { "STRING_LENGTH", GDALGMLJP2Expr_STRING_LENGTH, 1 }, + { "STRING", GDALGMLJP2Expr_CAST_TO_STRING, 1 }, /* must be after */ + { "SUBSTRING_BEFORE", GDALGMLJP2Expr_SUBSTRING_BEFORE, 2 }, + { "SUBSTRING_AFTER", GDALGMLJP2Expr_SUBSTRING_AFTER, 2 }, + { "SUBSTRING", GDALGMLJP2Expr_SUBSTRING, 3 }, /* must be after */ + { "SUB", GDALGMLJP2Expr_SUB, 2 }, /* must be after */ + { "UUID", GDALGMLJP2Expr_UUID, 0}, + { "EVAL", GDALGMLJP2Expr_EVAL, 1} + }; + for(size_t i=0; ieType = GDALGMLJP2Expr_EQ; + GDALGMLJP2Expr* poTopExpr = new GDALGMLJP2Expr(); + poTopExpr->eType = GDALGMLJP2Expr_NOT; + poTopExpr->apoSubExpr.push_back(poExpr); + poExpr = poTopExpr; + } + else if( asOperators[i].eType == GDALGMLJP2Expr_GT ) + { + poExpr->eType = GDALGMLJP2Expr_LE; + GDALGMLJP2Expr* poTopExpr = new GDALGMLJP2Expr(); + poTopExpr->eType = GDALGMLJP2Expr_NOT; + poTopExpr->apoSubExpr.push_back(poExpr); + poExpr = poTopExpr; + } + else if( asOperators[i].eType == GDALGMLJP2Expr_GE ) + { + poExpr->eType = GDALGMLJP2Expr_LT; + GDALGMLJP2Expr* poTopExpr = new GDALGMLJP2Expr(); + poTopExpr->eType = GDALGMLJP2Expr_NOT; + poTopExpr->apoSubExpr.push_back(poExpr); + poExpr = poTopExpr; + } + else + poExpr->eType = asOperators[i].eType; + } + return poExpr; + } + } + ReportError(pszOriStr, pszStr); + return NULL; + } +#else + else + { + ReportError(pszOriStr, pszStr); + return NULL; + } +#endif +} + +/************************************************************************/ +/* GDALGMLJP2HexFormatter() */ +/************************************************************************/ + +static const char* GDALGMLJP2HexFormatter(GByte nVal) +{ + return CPLSPrintf("%02X", nVal); +} + +/************************************************************************/ +/* Evaluate() */ +/************************************************************************/ + +static CPLString GDALGMLJP2EvalExpr(const CPLString& osTemplate, + xmlXPathContextPtr pXPathCtx, + xmlDocPtr pDoc); + +GDALGMLJP2Expr GDALGMLJP2Expr::Evaluate(xmlXPathContextPtr pXPathCtx, + xmlDocPtr pDoc) +{ + switch(eType) + { +#ifdef ENABLE_BRAIN_DAMAGE + case GDALGMLJP2Expr_STRING_LITERAL: + case GDALGMLJP2Expr_NUMERIC_LITERAL: + return *this; +#endif + + case GDALGMLJP2Expr_XPATH: + { + xmlXPathObjectPtr pXPathObj = xmlXPathEvalExpression( + (const xmlChar*)osValue.c_str(), pXPathCtx); + if( pXPathObj == NULL ) + return GDALGMLJP2Expr(""); + + // Add result of the evaluation + CPLString osXMLRes; + if( pXPathObj->type == XPATH_STRING ) + osXMLRes = (const char*)pXPathObj->stringval; + else if( pXPathObj->type == XPATH_BOOLEAN ) + osXMLRes = pXPathObj->boolval ? "true" : "false"; + else if( pXPathObj->type == XPATH_NUMBER ) + osXMLRes = CPLSPrintf("%.16g", pXPathObj->floatval); + else if( pXPathObj->type == XPATH_NODESET ) + { + xmlNodeSetPtr pNodes = pXPathObj->nodesetval; + int nNodes = (pNodes) ? pNodes->nodeNr : 0; + for(int i=0;inodeTab[i]; + + xmlBufferPtr pBuf = xmlBufferCreate(); + xmlNodeDump(pBuf, pDoc, pCur, 2, 1); + osXMLRes += (const char*)xmlBufferContent(pBuf); + xmlBufferFree(pBuf); + } + } + + xmlXPathFreeObject(pXPathObj); + return GDALGMLJP2Expr(osXMLRes); + } +#ifdef ENABLE_BRAIN_DAMAGE + case GDALGMLJP2Expr_AND: + { + return GDALGMLJP2Expr( + apoSubExpr[0]->Evaluate(pXPathCtx,pDoc).osValue == "true" && + apoSubExpr[1]->Evaluate(pXPathCtx,pDoc).osValue == "true" ); + } + + case GDALGMLJP2Expr_OR: + { + return GDALGMLJP2Expr( + apoSubExpr[0]->Evaluate(pXPathCtx,pDoc).osValue == "true" || + apoSubExpr[1]->Evaluate(pXPathCtx,pDoc).osValue == "true" ); + } + + case GDALGMLJP2Expr_NOT: + { + return GDALGMLJP2Expr( + apoSubExpr[0]->Evaluate(pXPathCtx,pDoc).osValue != "true"); + } + + case GDALGMLJP2Expr_ADD: + { + const GDALGMLJP2Expr& oExpr1 = apoSubExpr[0]->Evaluate(pXPathCtx,pDoc); + const GDALGMLJP2Expr& oExpr2 = apoSubExpr[1]->Evaluate(pXPathCtx,pDoc); + GDALGMLJP2Expr oExpr(CPLSPrintf("%.16g", CPLAtof(oExpr1.osValue) + CPLAtof(oExpr2.osValue))); + oExpr.eType = GDALGMLJP2Expr_NUMERIC_LITERAL; + return oExpr; + } + + case GDALGMLJP2Expr_SUB: + { + const GDALGMLJP2Expr& oExpr1 = apoSubExpr[0]->Evaluate(pXPathCtx,pDoc); + const GDALGMLJP2Expr& oExpr2 = apoSubExpr[1]->Evaluate(pXPathCtx,pDoc); + GDALGMLJP2Expr oExpr(CPLSPrintf("%.16g", CPLAtof(oExpr1.osValue) - CPLAtof(oExpr2.osValue))); + oExpr.eType = GDALGMLJP2Expr_NUMERIC_LITERAL; + return oExpr; + } + + case GDALGMLJP2Expr_EQ: + { + const GDALGMLJP2Expr& oExpr1 = apoSubExpr[0]->Evaluate(pXPathCtx,pDoc); + const GDALGMLJP2Expr& oExpr2 = apoSubExpr[1]->Evaluate(pXPathCtx,pDoc); + bool bRes; + if( oExpr1.eType == GDALGMLJP2Expr_NUMERIC_LITERAL && + oExpr2.eType == GDALGMLJP2Expr_NUMERIC_LITERAL ) + { + bRes = ( CPLAtof(oExpr1.osValue) == CPLAtof(oExpr2.osValue) ); + } + else + { + bRes = (oExpr1.osValue == oExpr2.osValue ); + } + return GDALGMLJP2Expr(bRes); + } + + case GDALGMLJP2Expr_LT: + { + const GDALGMLJP2Expr& oExpr1 = apoSubExpr[0]->Evaluate(pXPathCtx,pDoc); + const GDALGMLJP2Expr& oExpr2 = apoSubExpr[1]->Evaluate(pXPathCtx,pDoc); + bool bRes; + if( oExpr1.eType == GDALGMLJP2Expr_NUMERIC_LITERAL && + oExpr2.eType == GDALGMLJP2Expr_NUMERIC_LITERAL ) + { + bRes = ( CPLAtof(oExpr1.osValue) < CPLAtof(oExpr2.osValue) ); + } + else + { + bRes = (oExpr1.osValue < oExpr2.osValue ); + } + return GDALGMLJP2Expr(bRes); + } + + case GDALGMLJP2Expr_LE: + { + const GDALGMLJP2Expr& oExpr1 = apoSubExpr[0]->Evaluate(pXPathCtx,pDoc); + const GDALGMLJP2Expr& oExpr2 = apoSubExpr[1]->Evaluate(pXPathCtx,pDoc); + bool bRes; + if( oExpr1.eType == GDALGMLJP2Expr_NUMERIC_LITERAL && + oExpr2.eType == GDALGMLJP2Expr_NUMERIC_LITERAL ) + { + bRes = ( CPLAtof(oExpr1.osValue) <= CPLAtof(oExpr2.osValue) ); + } + else + { + bRes = (oExpr1.osValue <= oExpr2.osValue ); + } + return GDALGMLJP2Expr(bRes); + } + + case GDALGMLJP2Expr_IF: + { + if( apoSubExpr[0]->Evaluate(pXPathCtx,pDoc).osValue == "true" ) + return apoSubExpr[1]->Evaluate(pXPathCtx,pDoc); + else + return apoSubExpr[2]->Evaluate(pXPathCtx,pDoc); + } + + case GDALGMLJP2Expr_EVAL: + { + return GDALGMLJP2Expr( + GDALGMLJP2EvalExpr(apoSubExpr[0]->Evaluate(pXPathCtx,pDoc).osValue,pXPathCtx,pDoc)); + } + + case GDALGMLJP2Expr_CONCAT: + { + CPLString osRet; + for(size_t i=0;iEvaluate(pXPathCtx,pDoc).osValue; + return GDALGMLJP2Expr(osRet); + } + + case GDALGMLJP2Expr_CAST_TO_NUMERIC: + { + GDALGMLJP2Expr oExpr = CPLSPrintf("%.16g", CPLAtof(apoSubExpr[0]->Evaluate(pXPathCtx,pDoc).osValue)); + oExpr.eType = GDALGMLJP2Expr_NUMERIC_LITERAL; + return oExpr; + } + + case GDALGMLJP2Expr_CAST_TO_STRING: + { + GDALGMLJP2Expr oExpr = apoSubExpr[0]->Evaluate(pXPathCtx,pDoc).osValue; + oExpr.eType = GDALGMLJP2Expr_STRING_LITERAL; + return oExpr; + } + + case GDALGMLJP2Expr_UUID: + { + CPLString osRet; + static int nCounter = 0; + srand((unsigned int)time(NULL) + nCounter); + nCounter ++; + for( int i=0; i<4; i ++ ) + osRet += GDALGMLJP2HexFormatter(rand() & 0xFF); + osRet += "-"; + osRet += GDALGMLJP2HexFormatter(rand() & 0xFF); + osRet += GDALGMLJP2HexFormatter(rand() & 0xFF); + osRet += "-"; + osRet += GDALGMLJP2HexFormatter((rand() & 0x0F) | 0x40); // set the version number bits (4 == random) + osRet += GDALGMLJP2HexFormatter(rand() & 0xFF); + osRet += "-"; + osRet += GDALGMLJP2HexFormatter((rand() & 0x3F) | 0x80); // set the variant bits + osRet += GDALGMLJP2HexFormatter(rand() & 0xFF); + osRet += "-"; + for( int i=0; i<6; i ++ ) + osRet += GDALGMLJP2HexFormatter(rand() & 0xFF); + return GDALGMLJP2Expr(osRet); + } + + case GDALGMLJP2Expr_STRING_LENGTH: + { + GDALGMLJP2Expr oExpr(CPLSPrintf("%d", + (int)strlen(apoSubExpr[0]->Evaluate(pXPathCtx,pDoc).osValue))); + oExpr.eType = GDALGMLJP2Expr_NUMERIC_LITERAL; + return oExpr; + } + + case GDALGMLJP2Expr_SUBSTRING: + { + const GDALGMLJP2Expr& oExpr1 = apoSubExpr[0]->Evaluate(pXPathCtx,pDoc); + const GDALGMLJP2Expr& oExpr2 = apoSubExpr[1]->Evaluate(pXPathCtx,pDoc); + const GDALGMLJP2Expr& oExpr3 = apoSubExpr[2]->Evaluate(pXPathCtx,pDoc); + int nStart = atoi(oExpr2.osValue); + int nLen = atoi(oExpr3.osValue); + nStart --; /* from XPath/SQL convention to C convention */ + if( nStart < 0 ) + { + nLen += nStart; + nStart = 0; + } + if( nLen < 0 ) + return GDALGMLJP2Expr(""); + if( nStart >= (int)oExpr1.osValue.size() ) + return GDALGMLJP2Expr(""); + if( nStart + nLen > (int)oExpr1.osValue.size() ) + nLen = (int)oExpr1.osValue.size() - nStart; + return GDALGMLJP2Expr(oExpr1.osValue.substr(nStart, nLen)); + } + + case GDALGMLJP2Expr_SUBSTRING_BEFORE: + { + const GDALGMLJP2Expr& oExpr1 = apoSubExpr[0]->Evaluate(pXPathCtx,pDoc); + const GDALGMLJP2Expr& oExpr2 = apoSubExpr[1]->Evaluate(pXPathCtx,pDoc); + size_t nPos = oExpr1.osValue.find(oExpr2.osValue); + if( nPos == std::string::npos ) + return GDALGMLJP2Expr(""); + return GDALGMLJP2Expr(oExpr1.osValue.substr(0, nPos)); + } + + case GDALGMLJP2Expr_SUBSTRING_AFTER: + { + const GDALGMLJP2Expr& oExpr1 = apoSubExpr[0]->Evaluate(pXPathCtx,pDoc); + const GDALGMLJP2Expr& oExpr2 = apoSubExpr[1]->Evaluate(pXPathCtx,pDoc); + size_t nPos = oExpr1.osValue.find(oExpr2.osValue); + if( nPos == std::string::npos ) + return GDALGMLJP2Expr(""); + return GDALGMLJP2Expr(oExpr1.osValue.substr(nPos + oExpr2.osValue.size())); + } +#endif + default: + CPLAssert(FALSE); + return GDALGMLJP2Expr(""); + } +} + +/************************************************************************/ +/* GDALGMLJP2EvalExpr() */ +/************************************************************************/ + +static CPLString GDALGMLJP2EvalExpr(const CPLString& osTemplate, + xmlXPathContextPtr pXPathCtx, + xmlDocPtr pDoc) +{ + CPLString osXMLRes; + size_t nPos = 0; + while( TRUE ) + { + // Get next expression + size_t nStartPos = osTemplate.find("{{{", nPos); + if( nStartPos == std::string::npos) + { + // Add terminating portion of the template + osXMLRes += osTemplate.substr(nPos); + break; + } + + // Add portion of template before the expression + osXMLRes += osTemplate.substr(nPos, nStartPos - nPos); + + const char* pszExpr = osTemplate.c_str() + nStartPos; + GDALGMLJP2Expr* poExpr = GDALGMLJP2Expr::Build(pszExpr, pszExpr); + if( poExpr == NULL ) + break; + nPos = (size_t)(pszExpr - osTemplate.c_str()); + osXMLRes += poExpr->Evaluate(pXPathCtx,pDoc).osValue; + delete poExpr; + } + return osXMLRes; +} + +/************************************************************************/ +/* GDALGMLJP2XPathErrorHandler() */ +/************************************************************************/ + +static void GDALGMLJP2XPathErrorHandler(CPL_UNUSED void * userData, + xmlErrorPtr error) +{ + if( error->domain == XML_FROM_XPATH && + error->str1 != NULL && + error->int1 < (int)strlen(error->str1) ) + { + GDALGMLJP2Expr::ReportError(error->str1, + error->str1 + error->int1, + "XPath error:\n"); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "An error occured in libxml2"); + } +} + +/************************************************************************/ +/* GDALGMLJP2RegisterNamespaces() */ +/************************************************************************/ + +static void GDALGMLJP2RegisterNamespaces(xmlXPathContextPtr pXPathCtx, + xmlNode* pNode) +{ + for(; pNode; pNode = pNode->next) + { + if( pNode->type == XML_ELEMENT_NODE) + { + if( pNode->ns != NULL && pNode->ns->prefix != NULL ) + { + //printf("%s %s\n",pNode->ns->prefix, pNode->ns->href); + if(xmlXPathRegisterNs(pXPathCtx, pNode->ns->prefix, pNode->ns->href) != 0) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Registration of namespace %s failed", + (const char*)pNode->ns->prefix); + } + } + } + + GDALGMLJP2RegisterNamespaces(pXPathCtx, pNode->children); + } +} + +/************************************************************************/ +/* GDALGMLJP2XPathIf() */ +/************************************************************************/ + +static void GDALGMLJP2XPathIf(xmlXPathParserContextPtr ctxt, int nargs) +{ + xmlXPathObjectPtr cond_val,then_val,else_val; + + CHECK_ARITY(3); + else_val = valuePop(ctxt); + then_val = valuePop(ctxt); + CAST_TO_BOOLEAN + cond_val = valuePop(ctxt); + + if( cond_val->boolval ) + { + xmlXPathFreeObject(else_val); + valuePush(ctxt, then_val); + } + else + { + xmlXPathFreeObject(then_val); + valuePush(ctxt, else_val); + } + xmlXPathFreeObject(cond_val); +} + +/************************************************************************/ +/* GDALGMLJP2XPathUUID() */ +/************************************************************************/ + +static void GDALGMLJP2XPathUUID(xmlXPathParserContextPtr ctxt, int nargs) +{ + CHECK_ARITY(0); + + CPLString osRet; + static int nCounter = 0; + srand((unsigned int)time(NULL) + nCounter); + nCounter ++; + for( int i=0; i<4; i ++ ) + osRet += GDALGMLJP2HexFormatter(rand() & 0xFF); + osRet += "-"; + osRet += GDALGMLJP2HexFormatter(rand() & 0xFF); + osRet += GDALGMLJP2HexFormatter(rand() & 0xFF); + osRet += "-"; + osRet += GDALGMLJP2HexFormatter((rand() & 0x0F) | 0x40); // set the version number bits (4 == random) + osRet += GDALGMLJP2HexFormatter(rand() & 0xFF); + osRet += "-"; + osRet += GDALGMLJP2HexFormatter((rand() & 0x3F) | 0x80); // set the variant bits + osRet += GDALGMLJP2HexFormatter(rand() & 0xFF); + osRet += "-"; + for( int i=0; i<6; i ++ ) + osRet += GDALGMLJP2HexFormatter(rand() & 0xFF); + + valuePush(ctxt, xmlXPathNewString((const xmlChar*)osRet.c_str())); +} + +#endif /* defined(LIBXML2) */ + +/************************************************************************/ +/* GDALGMLJP2GenerateMetadata() */ +/************************************************************************/ + +CPLXMLNode* GDALGMLJP2GenerateMetadata(const CPLString& osTemplateFile, + const CPLString& osSourceFile) +{ +#ifndef HAVE_LIBXML2 + return NULL; +#else + GByte* pabyStr = NULL; + if( !VSIIngestFile( NULL, osTemplateFile, &pabyStr, NULL, -1 ) ) + return NULL; + CPLString osTemplate((const char*)pabyStr); + CPLFree(pabyStr); + + if( !VSIIngestFile( NULL, osSourceFile, &pabyStr, NULL, -1 ) ) + return NULL; + CPLString osSource((const char*)pabyStr); + CPLFree(pabyStr); + + xmlDocPtr pDoc = xmlParseDoc((const xmlChar *)osSource.c_str()); + if( pDoc == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot parse %s", + osSourceFile.c_str()); + return NULL; + } + + xmlXPathContextPtr pXPathCtx = xmlXPathNewContext(pDoc); + if( pXPathCtx == NULL ) + { + xmlFreeDoc(pDoc); + return NULL; + } + + xmlXPathRegisterFunc(pXPathCtx, (const xmlChar *)"if", GDALGMLJP2XPathIf); + xmlXPathRegisterFunc(pXPathCtx, (const xmlChar *)"uuid", GDALGMLJP2XPathUUID); + + pXPathCtx->error = GDALGMLJP2XPathErrorHandler; + + GDALGMLJP2RegisterNamespaces(pXPathCtx, xmlDocGetRootElement(pDoc)); + + CPLString osXMLRes = GDALGMLJP2EvalExpr(osTemplate, pXPathCtx, pDoc); + + xmlXPathFreeContext(pXPathCtx); + xmlFreeDoc(pDoc); + + return CPLParseXMLString(osXMLRes); +#endif +} diff --git a/bazaar/plugin/gdal/gcore/gdaljp2metadatagenerator.h b/bazaar/plugin/gdal/gcore/gdaljp2metadatagenerator.h new file mode 100644 index 000000000..79b5df816 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaljp2metadatagenerator.h @@ -0,0 +1,40 @@ +/****************************************************************************** + * $Id: gdaljp2metadatagenerator.h 29048 2015-04-29 14:48:33Z rouault $ + * + * Project: GDAL + * Purpose: GDALJP2Metadata: metadata generator + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2015, European Union Satellite Centre + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_JP2METADATA_GENERATOR_H_INCLUDED +#define GDAL_JP2METADATA_GENERATOR_H_INCLUDED + + +#include "cpl_string.h" +#include "cpl_minixml.h" + +CPLXMLNode* GDALGMLJP2GenerateMetadata(const CPLString& osTemplateFile, + const CPLString& osSourceFile); + +#endif /* GDAL_JP2METADATA_GENERATOR_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/gcore/gdaljp2structure.cpp b/bazaar/plugin/gdal/gcore/gdaljp2structure.cpp new file mode 100644 index 000000000..526027e5c --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaljp2structure.cpp @@ -0,0 +1,1425 @@ +/****************************************************************************** + * $Id: gdaljp2structure.cpp 28766 2015-03-24 23:32:21Z rouault $ + * + * Project: GDAL + * Purpose: GDALJP2Stucture - Dump structure of a JP2/J2K file + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2015, European Union (European Environment Agency) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdaljp2metadata.h" + +static void AddField(CPLXMLNode* psParent, const char* pszFieldName, + int nFieldSize, const char* pszValue, + const char* pszDescription = NULL) +{ + CPLXMLNode* psField = CPLCreateXMLElementAndValue( + psParent, "Field", pszValue ); + CPLAddXMLAttributeAndValue(psField, "name", pszFieldName ); + CPLAddXMLAttributeAndValue(psField, "type", "string" ); + CPLAddXMLAttributeAndValue(psField, "size", CPLSPrintf("%d", nFieldSize ) ); + if( pszDescription ) + CPLAddXMLAttributeAndValue(psField, "description", pszDescription ); +} + +static void AddHexField(CPLXMLNode* psParent, const char* pszFieldName, + int nFieldSize, const char* pszValue, + const char* pszDescription = NULL) +{ + CPLXMLNode* psField = CPLCreateXMLElementAndValue( + psParent, "Field", pszValue ); + CPLAddXMLAttributeAndValue(psField, "name", pszFieldName ); + CPLAddXMLAttributeAndValue(psField, "type", "hexint" ); + CPLAddXMLAttributeAndValue(psField, "size", CPLSPrintf("%d", nFieldSize ) ); + if( pszDescription ) + CPLAddXMLAttributeAndValue(psField, "description", pszDescription ); +} + +static void AddField(CPLXMLNode* psParent, const char* pszFieldName, GByte nVal, + const char* pszDescription = NULL) +{ + CPLXMLNode* psField = CPLCreateXMLElementAndValue( + psParent, "Field", CPLSPrintf("%d", nVal) ); + CPLAddXMLAttributeAndValue(psField, "name", pszFieldName ); + CPLAddXMLAttributeAndValue(psField, "type", "uint8" ); + if( pszDescription ) + CPLAddXMLAttributeAndValue(psField, "description", pszDescription ); +} + +static void AddField(CPLXMLNode* psParent, const char* pszFieldName, GUInt16 nVal, + const char* pszDescription = NULL) +{ + CPLXMLNode* psField = CPLCreateXMLElementAndValue( + psParent, "Field", CPLSPrintf("%d", nVal) ); + CPLAddXMLAttributeAndValue(psField, "name", pszFieldName ); + CPLAddXMLAttributeAndValue(psField, "type", "uint16" ); + if( pszDescription ) + CPLAddXMLAttributeAndValue(psField, "description", pszDescription ); +} + +static void AddField(CPLXMLNode* psParent, const char* pszFieldName, GUInt32 nVal, + const char* pszDescription = NULL) +{ + CPLXMLNode* psField = CPLCreateXMLElementAndValue( + psParent, "Field", CPLSPrintf("%u", nVal) ); + CPLAddXMLAttributeAndValue(psField, "name", pszFieldName ); + CPLAddXMLAttributeAndValue(psField, "type", "uint32" ); + if( pszDescription ) + CPLAddXMLAttributeAndValue(psField, "description", pszDescription ); +} + +static const char* GetInterpretationOfBPC(GByte bpc) +{ + if( bpc == 255 ) + return NULL; + if( (bpc & 0x80) ) + return CPLSPrintf("Signed %d bits", 1 + (bpc & 0x7F)); + else + return CPLSPrintf("Unsigned %d bits", 1 + bpc); +} + +static const char* GetStandardFieldString(GUInt16 nVal) +{ + switch(nVal) + { + case 1: return "Codestream contains no extensions"; + case 2: return "Contains multiple composition layers"; + case 3: return "Codestream is compressed using JPEG 2000 and requires at least a Profile 0 decoder"; + case 4: return "Codestream is compressed using JPEG 2000 and requires at least a Profile 1 decoder"; + case 5: return "Codestream is compressed using JPEG 2000 unrestricted"; + case 35: return "Contains IPR metadata"; + case 67: return "Contains GMLJP2 metadata"; + default: return NULL; + } +} + +static void DumpGeoTIFFBox(CPLXMLNode* psBox, + GDALJP2Box& oBox) +{ + GIntBig nBoxDataLength = oBox.GetDataLength(); + GByte* pabyBoxData = oBox.ReadBoxData(); + GDALDriver* poVRTDriver = (GDALDriver*) GDALGetDriverByName("VRT"); + if( pabyBoxData && poVRTDriver) + { + CPLString osTmpFilename(CPLSPrintf("/vsimem/tmp_%p.tif", oBox.GetFILE())); + VSIFCloseL(VSIFileFromMemBuffer( + osTmpFilename, pabyBoxData, nBoxDataLength, TRUE) ); + CPLPushErrorHandler(CPLQuietErrorHandler); + GDALDataset* poDS = (GDALDataset*) GDALOpen(osTmpFilename, GA_ReadOnly); + CPLPopErrorHandler(); + if( poDS ) + { + CPLString osTmpVRTFilename(CPLSPrintf("/vsimem/tmp_%p.vrt", oBox.GetFILE())); + GDALDataset* poVRTDS = poVRTDriver->CreateCopy(osTmpVRTFilename, poDS, FALSE, NULL, NULL, NULL); + GDALClose(poVRTDS); + GByte* pabyXML = VSIGetMemFileBuffer( osTmpVRTFilename, NULL, FALSE ); + CPLXMLNode* psXMLVRT = CPLParseXMLString((const char*)pabyXML); + if( psXMLVRT ) + { + CPLXMLNode* psXMLContentNode = + CPLCreateXMLNode( psBox, CXT_Element, "DecodedGeoTIFF" ); + psXMLContentNode->psChild = psXMLVRT; + CPLXMLNode* psPrev = NULL; + for(CPLXMLNode* psIter = psXMLVRT->psChild; psIter; psIter = psIter->psNext) + { + if( psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "VRTRasterBand") == 0 ) + { + CPLXMLNode* psNext = psIter->psNext; + psIter->psNext = NULL; + CPLDestroyXMLNode(psIter); + if( psPrev ) + psPrev->psNext = psNext; + else + break; + psIter = psPrev; + } + psPrev = psIter; + } + CPLCreateXMLNode(psXMLVRT, CXT_Element, "VRTRasterBand"); + } + + VSIUnlink(osTmpVRTFilename); + GDALClose(poDS); + } + VSIUnlink(osTmpFilename); + } +} + +static void DumpFTYPBox(CPLXMLNode* psBox, GDALJP2Box& oBox) +{ + GIntBig nBoxDataLength = oBox.GetDataLength(); + GByte* pabyBoxData = oBox.ReadBoxData(); + if( pabyBoxData ) + { + CPLXMLNode* psDecodedContent = + CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); + GIntBig nRemainingLength = nBoxDataLength; + GByte* pabyIter = pabyBoxData; + if( nRemainingLength >= 4 ) + { + char szBranding[5]; + memcpy(szBranding, pabyIter, 4); + szBranding[4] = 0; + AddField(psDecodedContent, "BR", 4, szBranding); + pabyIter += 4; + nRemainingLength -= 4; + } + if( nRemainingLength >= 4 ) + { + GUInt32 nVal; + memcpy(&nVal, pabyIter, 4); + CPL_MSBPTR32(&nVal); + AddField(psDecodedContent, "MinV", nVal); + pabyIter += 4; + nRemainingLength -= 4; + } + int nCLIndex = 0; + while( nRemainingLength >= 4 ) + { + char szBranding[5]; + memcpy(szBranding, pabyIter, 4); + szBranding[4] = 0; + AddField(psDecodedContent, + CPLSPrintf("CL%d", nCLIndex), + 4, szBranding); + pabyIter += 4; + nRemainingLength -= 4; + nCLIndex ++; + } + if( nRemainingLength > 0 ) + CPLCreateXMLElementAndValue( + psDecodedContent, "RemainingBytes", + CPLSPrintf("%d", (int)nRemainingLength )); + } + CPLFree(pabyBoxData); +} + +static void DumpIHDRBox(CPLXMLNode* psBox, GDALJP2Box& oBox) +{ + GIntBig nBoxDataLength = oBox.GetDataLength(); + GByte* pabyBoxData = oBox.ReadBoxData(); + if( pabyBoxData ) + { + CPLXMLNode* psDecodedContent = + CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); + GIntBig nRemainingLength = nBoxDataLength; + GByte* pabyIter = pabyBoxData; + if( nRemainingLength >= 4 ) + { + GUInt32 nVal; + memcpy(&nVal, pabyIter, 4); + CPL_MSBPTR32(&nVal); + AddField(psDecodedContent, "HEIGHT", nVal); + pabyIter += 4; + nRemainingLength -= 4; + } + if( nRemainingLength >= 4 ) + { + GUInt32 nVal; + memcpy(&nVal, pabyIter, 4); + CPL_MSBPTR32(&nVal); + AddField(psDecodedContent, "WIDTH", nVal); + pabyIter += 4; + nRemainingLength -= 4; + } + if( nRemainingLength >= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + AddField(psDecodedContent, "NC", nVal); + pabyIter += 2; + nRemainingLength -= 2; + } + if( nRemainingLength >= 1 ) + { + AddField(psDecodedContent, "BPC", *pabyIter, + GetInterpretationOfBPC(*pabyIter)); + pabyIter += 1; + nRemainingLength -= 1; + } + if( nRemainingLength >= 1 ) + { + AddField(psDecodedContent, "C", *pabyIter); + pabyIter += 1; + nRemainingLength -= 1; + } + if( nRemainingLength >= 1 ) + { + AddField(psDecodedContent, "UnkC", *pabyIter); + pabyIter += 1; + nRemainingLength -= 1; + } + if( nRemainingLength >= 1 ) + { + AddField(psDecodedContent, "IPR", *pabyIter); + pabyIter += 1; + nRemainingLength -= 1; + } + if( nRemainingLength > 0 ) + CPLCreateXMLElementAndValue( + psDecodedContent, "RemainingBytes", + CPLSPrintf("%d", (int)nRemainingLength )); + } + CPLFree(pabyBoxData); +} + +static void DumpBPCCBox(CPLXMLNode* psBox, GDALJP2Box& oBox) +{ + GIntBig nBoxDataLength = oBox.GetDataLength(); + GByte* pabyBoxData = oBox.ReadBoxData(); + if( pabyBoxData ) + { + CPLXMLNode* psDecodedContent = + CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); + GIntBig nRemainingLength = nBoxDataLength; + GByte* pabyIter = pabyBoxData; + int nBPCIndex = 0; + while( nRemainingLength >= 1 ) + { + AddField(psDecodedContent, + CPLSPrintf("BPC%d", nBPCIndex), + *pabyIter, + GetInterpretationOfBPC(*pabyIter)); + nBPCIndex ++; + pabyIter += 1; + nRemainingLength -= 1; + } + } + CPLFree(pabyBoxData); +} + +static void DumpCOLRBox(CPLXMLNode* psBox, GDALJP2Box& oBox) +{ + GIntBig nBoxDataLength = oBox.GetDataLength(); + GByte* pabyBoxData = oBox.ReadBoxData(); + if( pabyBoxData ) + { + CPLXMLNode* psDecodedContent = + CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); + GIntBig nRemainingLength = nBoxDataLength; + GByte* pabyIter = pabyBoxData; + GByte nMeth; + if( nRemainingLength >= 1 ) + { + nMeth = *pabyIter; + AddField(psDecodedContent, "METH", nMeth, + (nMeth == 0) ? "Enumerated Colourspace": + (nMeth == 0) ? "Restricted ICC profile": NULL); + pabyIter += 1; + nRemainingLength -= 1; + } + if( nRemainingLength >= 1 ) + { + AddField(psDecodedContent, "PREC", *pabyIter); + pabyIter += 1; + nRemainingLength -= 1; + } + if( nRemainingLength >= 1 ) + { + AddField(psDecodedContent, "APPROX", *pabyIter); + pabyIter += 1; + nRemainingLength -= 1; + } + if( nRemainingLength >= 4 ) + { + GUInt32 nVal; + memcpy(&nVal, pabyIter, 4); + CPL_MSBPTR32(&nVal); + AddField(psDecodedContent, "EnumCS", nVal, + (nVal == 16) ? "sRGB" : + (nVal == 17) ? "greyscale": + (nVal == 18) ? "sYCC" : NULL); + pabyIter += 4; + nRemainingLength -= 4; + } + if( nRemainingLength > 0 ) + CPLCreateXMLElementAndValue( + psDecodedContent, "RemainingBytes", + CPLSPrintf("%d", (int)nRemainingLength )); + } + CPLFree(pabyBoxData); +} + +static void DumpPCLRBox(CPLXMLNode* psBox, GDALJP2Box& oBox) +{ + GIntBig nBoxDataLength = oBox.GetDataLength(); + GByte* pabyBoxData = oBox.ReadBoxData(); + if( pabyBoxData ) + { + CPLXMLNode* psDecodedContent = + CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); + GIntBig nRemainingLength = nBoxDataLength; + GByte* pabyIter = pabyBoxData; + GUInt16 NE = 0; + if( nRemainingLength >= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + NE = nVal; + AddField(psDecodedContent, "NE", nVal); + pabyIter += 2; + nRemainingLength -= 2; + } + GByte NPC = 0; + if( nRemainingLength >= 1 ) + { + NPC = *pabyIter; + AddField(psDecodedContent, "NPC", NPC); + pabyIter += 1; + nRemainingLength -= 1; + } + int b8BitOnly = TRUE; + for(int i=0;i= 1 ) + { + b8BitOnly &= (*pabyIter <= 7); + AddField(psDecodedContent, + CPLSPrintf("B%d", i), + *pabyIter, + GetInterpretationOfBPC(*pabyIter)); + pabyIter += 1; + nRemainingLength -= 1; + } + } + if( b8BitOnly ) + { + for(int j=0;j= 1 ) + { + AddField(psDecodedContent, + CPLSPrintf("C_%d_%d", j, i), + *pabyIter); + pabyIter += 1; + nRemainingLength -= 1; + } + } + } + } + if( nRemainingLength > 0 ) + CPLCreateXMLElementAndValue( + psDecodedContent, "RemainingBytes", + CPLSPrintf("%d", (int)nRemainingLength )); + } + CPLFree(pabyBoxData); +} + +static void DumpCMAPBox(CPLXMLNode* psBox, GDALJP2Box& oBox) +{ + GIntBig nBoxDataLength = oBox.GetDataLength(); + GByte* pabyBoxData = oBox.ReadBoxData(); + if( pabyBoxData ) + { + CPLXMLNode* psDecodedContent = + CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); + GIntBig nRemainingLength = nBoxDataLength; + GByte* pabyIter = pabyBoxData; + int nIndex = 0; + while( nRemainingLength >= 2 + 1 + 1 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + AddField(psDecodedContent, + CPLSPrintf("CMP%d", nIndex), + nVal); + pabyIter += 2; + nRemainingLength -= 2; + + AddField(psDecodedContent, + CPLSPrintf("MTYP%d", nIndex), + *pabyIter, + (*pabyIter == 0) ? "Direct use": + (*pabyIter == 1) ? "Palette mapping": NULL); + pabyIter += 1; + nRemainingLength -= 1; + + AddField(psDecodedContent, + CPLSPrintf("PCOL%d", nIndex), + *pabyIter); + pabyIter += 1; + nRemainingLength -= 1; + + nIndex ++; + } + if( nRemainingLength > 0 ) + CPLCreateXMLElementAndValue( + psDecodedContent, "RemainingBytes", + CPLSPrintf("%d", (int)nRemainingLength )); + } + CPLFree(pabyBoxData); +} + +static void DumpCDEFBox(CPLXMLNode* psBox, GDALJP2Box& oBox) +{ + GIntBig nBoxDataLength = oBox.GetDataLength(); + GByte* pabyBoxData = oBox.ReadBoxData(); + if( pabyBoxData ) + { + CPLXMLNode* psDecodedContent = + CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); + GIntBig nRemainingLength = nBoxDataLength; + GByte* pabyIter = pabyBoxData; + GUInt16 nChannels = 0; + if( nRemainingLength >= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + nChannels = nVal; + CPL_MSBPTR16(&nVal); + AddField(psDecodedContent, "N", nVal); + pabyIter += 2; + nRemainingLength -= 2; + } + for( int i=0; i < nChannels; i++ ) + { + if( nRemainingLength >= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + AddField(psDecodedContent, + CPLSPrintf("Cn%d", i), + nVal); + pabyIter += 2; + nRemainingLength -= 2; + } + if( nRemainingLength >= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + AddField(psDecodedContent, + CPLSPrintf("Typ%d", i), + nVal, + (nVal == 0) ? "Colour channel": + (nVal == 1) ? "Opacity channel": + (nVal == 2) ? "Premultiplied opacity": + (nVal == 65535) ? "Not specified" : NULL); + pabyIter += 2; + nRemainingLength -= 2; + } + if( nRemainingLength >= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + AddField(psDecodedContent, + CPLSPrintf("Asoc%d", i), + nVal, + (nVal == 0) ? "Associated to the whole image": + (nVal == 65535) ? "Not associated with a particular colour": + "Associated with a particular colour"); + pabyIter += 2; + nRemainingLength -= 2; + } + } + if( nRemainingLength > 0 ) + CPLCreateXMLElementAndValue( + psDecodedContent, "RemainingBytes", + CPLSPrintf("%d", (int)nRemainingLength )); + } + CPLFree(pabyBoxData); +} + +static void DumpRESxBox(CPLXMLNode* psBox, GDALJP2Box& oBox) +{ + GIntBig nBoxDataLength = oBox.GetDataLength(); + GByte* pabyBoxData = oBox.ReadBoxData(); + char chC = oBox.GetType()[3]; + if( pabyBoxData ) + { + CPLXMLNode* psDecodedContent = + CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); + GIntBig nRemainingLength = nBoxDataLength; + GByte* pabyIter = pabyBoxData; + GUInt16 nNumV = 0, nNumH = 0, nDenomV = 1, nDenomH = 1, nExpV = 0, nExpH = 0; + if( nRemainingLength >= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + nNumV = nVal; + AddField(psDecodedContent, CPLSPrintf("VR%cN", chC), nVal); + pabyIter += 2; + nRemainingLength -= 2; + } + if( nRemainingLength >= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + nDenomV = nVal; + AddField(psDecodedContent, CPLSPrintf("VR%cD", chC), nVal); + pabyIter += 2; + nRemainingLength -= 2; + } + if( nRemainingLength >= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + nNumH = nVal; + AddField(psDecodedContent, CPLSPrintf("HR%cN", chC), nVal); + pabyIter += 2; + nRemainingLength -= 2; + } + if( nRemainingLength >= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + nDenomH = nVal; + AddField(psDecodedContent, CPLSPrintf("HR%cD", chC), nVal); + pabyIter += 2; + nRemainingLength -= 2; + } + if( nRemainingLength >= 1 ) + { + AddField(psDecodedContent, CPLSPrintf("VR%cE", chC), *pabyIter); + nExpV = *pabyIter; + pabyIter += 1; + nRemainingLength -= 1; + } + if( nRemainingLength >= 1 ) + { + AddField(psDecodedContent, CPLSPrintf("HR%cE", chC), *pabyIter); + nExpH = *pabyIter; + pabyIter += 1; + nRemainingLength -= 1; + } + if( nRemainingLength == 0 ) + { + CPLCreateXMLElementAndValue(psDecodedContent, "VRes", + CPLSPrintf("%.03f", 1.0 * nNumV / nDenomV * pow(10.0, nExpV))); + CPLCreateXMLElementAndValue(psDecodedContent, "HRes", + CPLSPrintf("%.03f", 1.0 * nNumH / nDenomH * pow(10.0, nExpH))); + } + else if( nRemainingLength > 0 ) + CPLCreateXMLElementAndValue( + psDecodedContent, "RemainingBytes", + CPLSPrintf("%d", (int)nRemainingLength )); + } + CPLFree(pabyBoxData); +} + +static void DumpRREQBox(CPLXMLNode* psBox, GDALJP2Box& oBox) +{ + GIntBig nBoxDataLength = oBox.GetDataLength(); + GByte* pabyBoxData = oBox.ReadBoxData(); + if( pabyBoxData ) + { + CPLXMLNode* psDecodedContent = + CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); + GIntBig nRemainingLength = nBoxDataLength; + GByte* pabyIter = pabyBoxData; + GByte ML = 0; + if( nRemainingLength >= 1 ) + { + ML = *pabyIter; + AddField(psDecodedContent, "ML", *pabyIter); + pabyIter += 1; + nRemainingLength -= 1; + } + if( nRemainingLength >= ML ) + { + CPLString osHex("0x"); + for(int i=0;i= ML ) + { + CPLString osHex("0x"); + for(int i=0;i= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + NSF = nVal; + AddField(psDecodedContent, "NSF", nVal); + pabyIter += 2; + nRemainingLength -= 2; + } + for(int iNSF=0;iNSF= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + AddField(psDecodedContent, + CPLSPrintf("SF%d", iNSF), nVal, + GetStandardFieldString(nVal)); + pabyIter += 2; + nRemainingLength -= 2; + } + if( nRemainingLength >= ML ) + { + CPLString osHex("0x"); + for(int i=0;i= 2 ) + { + GUInt16 nVal; + memcpy(&nVal, pabyIter, 2); + CPL_MSBPTR16(&nVal); + NVF = nVal; + AddField(psDecodedContent, "NVF", nVal); + pabyIter += 2; + nRemainingLength -= 2; + } + for(int iNVF=0;iNVF= 16 ) + { + CPLString osHex("0x"); + for(int i=0;i<16;i++) + { + osHex += CPLSPrintf("%02X", *pabyIter); + pabyIter += 1; + nRemainingLength -= 1; + } + AddHexField(psDecodedContent, + CPLSPrintf("VF%d", iNVF), + (int)ML, osHex.c_str()); + } + if( nRemainingLength >= ML ) + { + CPLString osHex("0x"); + for(int i=0;i 0 ) + CPLCreateXMLElementAndValue( + psDecodedContent, "RemainingBytes", + CPLSPrintf("%d", (int)nRemainingLength )); + } + CPLFree(pabyBoxData); +} + +static CPLXMLNode* CreateMarker(CPLXMLNode* psCSBox, const char* pszName, + GIntBig nOffset, GIntBig nLength) +{ + CPLXMLNode* psMarker = CPLCreateXMLNode( psCSBox, CXT_Element, "Marker" ); + CPLAddXMLAttributeAndValue(psMarker, "name", pszName ); + CPLAddXMLAttributeAndValue(psMarker, "offset", + CPLSPrintf(CPL_FRMT_GIB, nOffset ) ); + CPLAddXMLAttributeAndValue(psMarker, "length", + CPLSPrintf(CPL_FRMT_GIB, 2 + nLength ) ); + return psMarker; +} + +static void AddError(CPLXMLNode* psParent, const char* pszErrorMsg, + GIntBig nOffset = 0) +{ + CPLXMLNode* psError = CPLCreateXMLNode( psParent, CXT_Element, "Error" ); + CPLAddXMLAttributeAndValue(psError, "message", pszErrorMsg ); + if( nOffset ) + { + CPLAddXMLAttributeAndValue(psError, "offset", + CPLSPrintf(CPL_FRMT_GIB, nOffset ) ); + } +} + +static const char* GetMarkerName(GByte byVal) +{ + switch(byVal) + { + case 0x90: return "SOT"; + case 0x51: return "SIZ"; + case 0x52: return "COD"; + case 0x53: return "COC"; + case 0x55: return "TLM"; + case 0x57: return "PLM"; + case 0x58: return "PLT"; + case 0x5C: return "QCD"; + case 0x5D: return "QCC"; + case 0x5E: return "RGN"; + case 0x5F: return "POC"; + case 0x60: return "PPM"; + case 0x61: return "PPT"; + case 0x63: return "CRG"; + case 0x64: return "COM"; + default: return CPLSPrintf("Unknown 0xFF%02X", byVal); + } +} + +/************************************************************************/ +/* DumpJPK2CodeStream() */ +/************************************************************************/ + +static CPLXMLNode* DumpJPK2CodeStream(CPLXMLNode* psBox, + VSILFILE* fp, + GIntBig nBoxDataOffset, + GIntBig nBoxDataLength) +{ + VSIFSeekL(fp, nBoxDataOffset, SEEK_SET); + GByte abyMarker[2]; + CPLXMLNode* psCSBox = CPLCreateXMLNode( psBox, CXT_Element, "JP2KCodeStream" ); + GByte* pabyMarkerData = (GByte*)CPLMalloc(65535+1); + GIntBig nNextTileOffset = 0; + while( TRUE ) + { + GIntBig nOffset = (GIntBig)VSIFTellL(fp); + if( nOffset == nBoxDataOffset + nBoxDataLength ) + break; + if( VSIFReadL(abyMarker, 2, 1, fp) != 1 ) + { + AddError(psCSBox, "Cannot read marker", nOffset); + break; + } + if( abyMarker[0] != 0xFF ) + { + AddError(psCSBox, "Not a marker", nOffset); + break; + } + if( abyMarker[1] == 0x4F ) + { + CreateMarker( psCSBox, "SOC", nOffset, 0 ); + continue; + } + if( abyMarker[1] == 0x93 ) + { + GIntBig nMarkerSize = 0; + int bBreak = FALSE; + if( nNextTileOffset == 0 ) + { + nMarkerSize = (nBoxDataOffset + nBoxDataLength - 2) - nOffset - 2; + VSIFSeekL(fp, nBoxDataOffset + nBoxDataLength - 2, SEEK_SET); + if( VSIFReadL(abyMarker, 2, 1, fp) != 1 || + abyMarker[0] != 0xFF || abyMarker[1] != 0xD9 ) + { + /* autotest/gdrivers/data/rgb16_ecwsdk.jp2 does not end */ + /* with a EOC... */ + nMarkerSize += 2; + bBreak = TRUE; + } + } + else if( nNextTileOffset >= nOffset + 2 ) + nMarkerSize = nNextTileOffset - nOffset - 2; + + CreateMarker( psCSBox, "SOD", nOffset, nMarkerSize ); + if( bBreak ) + break; + + if( nNextTileOffset && nNextTileOffset == nOffset ) + { + /* Found with Pleiades images. openjpeg doesn't like it either */ + nNextTileOffset = 0; + } + else if( nNextTileOffset && nNextTileOffset >= nOffset + 2 ) + { + VSIFSeekL(fp, nNextTileOffset, SEEK_SET); + nNextTileOffset = 0; + } + else + { + /* We have seek and check before we hit a EOC */ + CreateMarker( psCSBox, "EOC", nOffset, 0 ); + } + continue; + } + if( abyMarker[1] == 0xD9 ) + { + CreateMarker( psCSBox, "EOC", nOffset, 0 ); + continue; + } + /* Reserved markers */ + if( abyMarker[1] >= 0x30 && abyMarker[1] <= 0x3F ) + { + CreateMarker( psCSBox, CPLSPrintf("Unknown 0xFF%02X", abyMarker[1]), nOffset, 0 ); + continue; + } + + GUInt16 nMarkerSize; + if( VSIFReadL(&nMarkerSize, 2, 1, fp) != 1 ) + { + AddError(psCSBox, CPLSPrintf("Cannot read marker size of %s", GetMarkerName(abyMarker[1])), nOffset); + break; + } + CPL_MSBPTR16(&nMarkerSize); + if( nMarkerSize < 2 ) + { + AddError(psCSBox, CPLSPrintf("Invalid marker size of %s", GetMarkerName(abyMarker[1])), nOffset); + break; + } + + CPLXMLNode* psMarker = CreateMarker( psCSBox, GetMarkerName(abyMarker[1]), nOffset, nMarkerSize ); + if( VSIFReadL(pabyMarkerData, nMarkerSize - 2, 1, fp) != 1 ) + { + AddError(psMarker, "Cannot read marker data", nOffset); + break; + } + GByte* pabyMarkerDataIter = pabyMarkerData; + GUInt16 nRemainingMarkerSize = nMarkerSize - 2; + GUInt32 nLastVal = 0; + + +#define READ_MARKER_FIELD_UINT8_COMMENT(name, comment) \ + do { if( nRemainingMarkerSize >= 1 ) { \ + nLastVal = *pabyMarkerDataIter; \ + AddField(psMarker, name, *pabyMarkerDataIter, comment); \ + pabyMarkerDataIter += 1; \ + nRemainingMarkerSize -= 1; \ + } \ + else { \ + AddError(psMarker, CPLSPrintf("Cannot read field %s", name)); \ + nLastVal = 0; \ + } \ + } while(0) + +#define READ_MARKER_FIELD_UINT8(name) \ + READ_MARKER_FIELD_UINT8_COMMENT(name, NULL) + +#define READ_MARKER_FIELD_UINT16_COMMENT(name, comment) \ + do { if( nRemainingMarkerSize >= 2 ) { \ + GUInt16 nVal; \ + memcpy(&nVal, pabyMarkerDataIter, 2); \ + CPL_MSBPTR16(&nVal); \ + nLastVal = nVal; \ + AddField(psMarker, name, nVal, comment); \ + pabyMarkerDataIter += 2; \ + nRemainingMarkerSize -= 2; \ + } \ + else { \ + AddError(psMarker, CPLSPrintf("Cannot read field %s", name)); \ + nLastVal = 0; \ + } \ + } while(0) + +#define READ_MARKER_FIELD_UINT16(name) \ + READ_MARKER_FIELD_UINT16_COMMENT(name, NULL) + +#define READ_MARKER_FIELD_UINT32_COMMENT(name, comment) \ + do { if( nRemainingMarkerSize >= 4 ) { \ + GUInt32 nVal; \ + memcpy(&nVal, pabyMarkerDataIter, 4); \ + CPL_MSBPTR32(&nVal); \ + AddField(psMarker, name, nVal, comment); \ + nLastVal = nVal; \ + pabyMarkerDataIter += 4; \ + nRemainingMarkerSize -= 4; \ + } \ + else { \ + AddError(psMarker, CPLSPrintf("Cannot read field %s", name)); \ + nLastVal = 0; \ + } \ + } while(0) + +#define READ_MARKER_FIELD_UINT32(name) \ + READ_MARKER_FIELD_UINT32_COMMENT(name, NULL) + + if( abyMarker[1] == 0x90 ) /* SOT */ + { + READ_MARKER_FIELD_UINT16("Isot"); + READ_MARKER_FIELD_UINT32("Psot"); + GUInt32 PSOT = nLastVal; + READ_MARKER_FIELD_UINT8("TPsot"); + READ_MARKER_FIELD_UINT8("TNsot"); + if( nRemainingMarkerSize > 0 ) + CPLCreateXMLElementAndValue( + psMarker, "RemainingBytes", + CPLSPrintf("%d", (int)nRemainingMarkerSize )); + + if( PSOT ) + nNextTileOffset = nOffset + PSOT; + } + else if( abyMarker[1] == 0x51 ) /* SIZ */ + { + READ_MARKER_FIELD_UINT16_COMMENT("Rsiz", + (nLastVal == 0) ? "Unrestricted profile": + (nLastVal == 1) ? "Profile 0": + (nLastVal == 2) ? "Profile 1": NULL); + READ_MARKER_FIELD_UINT32("Xsiz"); + READ_MARKER_FIELD_UINT32("Ysiz"); + READ_MARKER_FIELD_UINT32("XOsiz"); + READ_MARKER_FIELD_UINT32("YOsiz"); + READ_MARKER_FIELD_UINT32("XTsiz"); + READ_MARKER_FIELD_UINT32("YTsiz"); + READ_MARKER_FIELD_UINT32("XTOSiz"); + READ_MARKER_FIELD_UINT32("YTOSiz"); + READ_MARKER_FIELD_UINT16("Csiz"); + int CSiz = nLastVal; + for(int i=0;i 0 ) + CPLCreateXMLElementAndValue( + psMarker, "RemainingBytes", + CPLSPrintf("%d", (int)nRemainingMarkerSize )); + } + else if( abyMarker[1] == 0x52 ) /* COD */ + { + int bHasPrecincts = TRUE; + if( nRemainingMarkerSize >= 1 ) { + nLastVal = *pabyMarkerDataIter; + CPLString osInterp; + if( nLastVal & 0x1 ) + { + bHasPrecincts = TRUE; + osInterp += "User defined precincts"; + } + else + osInterp += "Standard precincts"; + osInterp += ", "; + if( nLastVal & 0x2 ) + osInterp += "SOP marker segments may be used"; + else + osInterp += "No SOP marker segments"; + osInterp += ", "; + if( nLastVal & 0x4 ) + osInterp += "EPH marker segments may be used"; + else + osInterp += "No EPH marker segments"; + AddField(psMarker, "Scod", (GByte)nLastVal, osInterp.c_str()); + pabyMarkerDataIter += 1; + nRemainingMarkerSize -= 1; + } + else { + AddError(psMarker, CPLSPrintf("Cannot read field %s", "Scod")); + nLastVal = 0; + } + READ_MARKER_FIELD_UINT8_COMMENT("SGcod_Progress", + (nLastVal == 0) ? "LRCP" : + (nLastVal == 1) ? "RLCP" : + (nLastVal == 2) ? "RPCL" : + (nLastVal == 3) ? "PCRL" : + (nLastVal == 4) ? "CPRL" : NULL); + READ_MARKER_FIELD_UINT16("SGcod_NumLayers"); + READ_MARKER_FIELD_UINT8("SGcod_MCT"); + READ_MARKER_FIELD_UINT8("SPcod_NumDecompositions"); + READ_MARKER_FIELD_UINT8_COMMENT("SPcod_xcb_minus_2", CPLSPrintf("%d", 1 << (2+nLastVal))); + READ_MARKER_FIELD_UINT8_COMMENT("SPcod_ycb_minus_2", CPLSPrintf("%d", 1 << (2+nLastVal))); + if( nRemainingMarkerSize >= 1 ) { + nLastVal = *pabyMarkerDataIter; + CPLString osInterp; + if( nLastVal & 0x1 ) + osInterp += "Selective arithmetic coding bypass"; + else + osInterp += "No selective arithmetic coding bypass"; + osInterp += ", "; + if( nLastVal & 0x2 ) + osInterp += "Reset context probabilities on coding pass boundaries"; + else + osInterp += "No reset of context probabilities on coding pass boundaries"; + osInterp += ", "; + if( nLastVal & 0x4 ) + osInterp += "Termination on each coding pass"; + else + osInterp += "No termination on each coding pass"; + osInterp += ", "; + if( nLastVal & 0x8 ) + osInterp += "Vertically causal context"; + else + osInterp += "No vertically causal context"; + osInterp += ", "; + if( nLastVal & 0x10 ) + osInterp += "Predictable termination"; + else + osInterp += "No predictable termination"; + osInterp += ", "; + if( nLastVal & 0x20 ) + osInterp += "Segmentation symbols are used"; + else + osInterp += "No segmentation symbols are used"; + AddField(psMarker, "SPcod_cbstyle", (GByte)nLastVal, osInterp.c_str()); + pabyMarkerDataIter += 1; + nRemainingMarkerSize -= 1; + } + else { + AddError(psMarker, CPLSPrintf("Cannot read field %s", "SPcod_cbstyle")); + nLastVal = 0; + } + READ_MARKER_FIELD_UINT8_COMMENT("SPcod_transformation", + (nLastVal == 0) ? "9-7 irreversible": + (nLastVal == 1) ? "5-3 reversible": NULL); + if( bHasPrecincts ) + { + int i = 0; + while( nRemainingMarkerSize >= 1 ) + { + nLastVal = *pabyMarkerDataIter; + AddField(psMarker, CPLSPrintf("SPcod_Precincts%d", i), *pabyMarkerDataIter, + CPLSPrintf("PPx=%d PPy=%d: %dx%d", + nLastVal & 0xf, nLastVal >> 4, + 1 << (nLastVal & 0xf), 1 << (nLastVal >> 4))); + pabyMarkerDataIter += 1; + nRemainingMarkerSize -= 1; + i ++; + } + } + if( nRemainingMarkerSize > 0 ) + CPLCreateXMLElementAndValue( + psMarker, "RemainingBytes", + CPLSPrintf("%d", (int)nRemainingMarkerSize )); + } + else if( abyMarker[1] == 0x53 ) /* COC */ + { + } + else if( abyMarker[1] == 0x55 ) /* TLM */ + { + CPLXMLNode* psMarker = CreateMarker( psCSBox, "TLM", nOffset, nMarkerSize ); + READ_MARKER_FIELD_UINT8("Ztlm"); + int ST = 0, SP = 0; + READ_MARKER_FIELD_UINT8_COMMENT("Stlm", + CPLSPrintf("ST=%d SP=%d", + (ST = (nLastVal >> 4) & 3), + (SP = ((nLastVal >> 6) & 1)))); + int nTilePartDescLength = ST + ((SP == 0) ? 2 : 4); + int i = 0; + while( nRemainingMarkerSize >= nTilePartDescLength ) + { + if( ST == 1 ) + READ_MARKER_FIELD_UINT8(CPLSPrintf("Ttlm%d", i)); + else if( ST == 2 ) + READ_MARKER_FIELD_UINT16(CPLSPrintf("Ttlm%d", i)); + if( SP == 0 ) + READ_MARKER_FIELD_UINT16(CPLSPrintf("Ptlm%d", i)); + else + READ_MARKER_FIELD_UINT32(CPLSPrintf("Ptlm%d", i)); + i ++; + } + if( nRemainingMarkerSize > 0 ) + CPLCreateXMLElementAndValue( + psMarker, "RemainingBytes", + CPLSPrintf("%d", (int)nRemainingMarkerSize )); + } + else if( abyMarker[1] == 0x57 ) /* PLM */ + { + } + else if( abyMarker[1] == 0x58 ) /* PLT */ + { + } + else if( abyMarker[1] == 0x5C ) /* QCD */ + { + } + else if( abyMarker[1] == 0x5D ) /* QCC */ + { + } + else if( abyMarker[1] == 0x5E ) /* RGN */ + { + } + else if( abyMarker[1] == 0x5F ) /* POC */ + { + } + else if( abyMarker[1] == 0x60 ) /* PPM */ + { + } + else if( abyMarker[1] == 0x61 ) /* PPT */ + { + } + else if( abyMarker[1] == 0x63 ) /* CRG */ + { + } + else if( abyMarker[1] == 0x64 ) /* COM */ + { + READ_MARKER_FIELD_UINT16_COMMENT("Rcom", (nLastVal == 0 ) ? "Binary" : (nLastVal == 1) ? "LATIN1" : NULL); + if( nLastVal == 1 ) + { + GByte abyBackup = pabyMarkerDataIter[nRemainingMarkerSize]; + pabyMarkerDataIter[nRemainingMarkerSize] = 0; + AddField(psMarker, "COM", (int)nRemainingMarkerSize, (const char*)pabyMarkerDataIter); + pabyMarkerDataIter[nRemainingMarkerSize] = abyBackup; + } + } + + VSIFSeekL(fp, nOffset + 2 + nMarkerSize, SEEK_SET); + } + CPLFree(pabyMarkerData); + return psCSBox; +} + +/************************************************************************/ +/* GDALGetJPEG2000StructureInternal() */ +/************************************************************************/ + +static +void GDALGetJPEG2000StructureInternal(CPLXMLNode* psParent, + VSILFILE* fp, + GDALJP2Box* poParentBox, + char** papszOptions) +{ + static const char* szHex = "0123456789ABCDEF"; + GDALJP2Box oBox( fp ); + if( oBox.ReadFirstChild(poParentBox) ) + { + while( strlen(oBox.GetType()) > 0 ) + { + GIntBig nBoxDataLength = oBox.GetDataLength(); + const char* pszBoxType = oBox.GetType(); + + CPLXMLNode* psBox = CPLCreateXMLNode( psParent, CXT_Element, "JP2Box" ); + CPLAddXMLAttributeAndValue(psBox, "name", pszBoxType ); + CPLAddXMLAttributeAndValue(psBox, "box_offset", + CPLSPrintf(CPL_FRMT_GIB, oBox.GetBoxOffset() ) ); + CPLAddXMLAttributeAndValue(psBox, "box_length", + CPLSPrintf(CPL_FRMT_GIB, oBox.GetBoxLength() ) ); + CPLAddXMLAttributeAndValue(psBox, "data_offset", + CPLSPrintf(CPL_FRMT_GIB, oBox.GetDataOffset() ) ); + CPLAddXMLAttributeAndValue(psBox, "data_length", + CPLSPrintf(CPL_FRMT_GIB, nBoxDataLength ) ); + + if( oBox.IsSuperBox() ) + { + GDALGetJPEG2000StructureInternal(psBox, fp, &oBox, papszOptions); + } + else + { + if( strcmp(pszBoxType, "uuid") == 0 ) + { + char* pszBinaryContent = (char*)VSIMalloc( 2 * 16 + 1 ); + const GByte* pabyUUID = oBox.GetUUID(); + for(int i=0;i<16;i++) + { + pszBinaryContent[2*i] = szHex[pabyUUID[i] >> 4]; + pszBinaryContent[2*i+1] = szHex[pabyUUID[i] & 0xf]; + } + pszBinaryContent[2*16] = '\0'; + CPLXMLNode* psUUIDNode = + CPLCreateXMLNode( psBox, CXT_Element, "UUID" ); + if( GDALJP2Metadata::IsUUID_MSI(pabyUUID) ) + CPLAddXMLAttributeAndValue(psUUIDNode, "description", "GeoTIFF" ); + else if( GDALJP2Metadata::IsUUID_XMP(pabyUUID) ) + CPLAddXMLAttributeAndValue(psUUIDNode, "description", "XMP" ); + CPLCreateXMLNode( psUUIDNode, CXT_Text, pszBinaryContent); + VSIFree(pszBinaryContent); + } + + if( (CSLFetchBoolean(papszOptions, "BINARY_CONTENT", FALSE) || + CSLFetchBoolean(papszOptions, "ALL", FALSE) ) && + strcmp(pszBoxType, "jp2c") != 0 && + nBoxDataLength < 100 * 1024 ) + { + CPLXMLNode* psBinaryContent = CPLCreateXMLNode( psBox, CXT_Element, "BinaryContent" ); + GByte* pabyBoxData = oBox.ReadBoxData(); + int nBoxLength = (int)nBoxDataLength; + char* pszBinaryContent = (char*)VSIMalloc( 2 * nBoxLength + 1 ); + if( pabyBoxData && pszBinaryContent ) + { + for(int i=0;i> 4]; + pszBinaryContent[2*i+1] = szHex[pabyBoxData[i] & 0xf]; + } + pszBinaryContent[2*nBoxLength] = '\0'; + CPLCreateXMLNode( psBinaryContent, CXT_Text, pszBinaryContent ); + } + CPLFree(pabyBoxData); + VSIFree(pszBinaryContent); + } + + if( (CSLFetchBoolean(papszOptions, "TEXT_CONTENT", FALSE) || + CSLFetchBoolean(papszOptions, "ALL", FALSE) ) && + strcmp(pszBoxType, "jp2c") != 0 && + nBoxDataLength < 100 * 1024 ) + { + GByte* pabyBoxData = oBox.ReadBoxData(); + if( pabyBoxData ) + { + if( CPLIsUTF8((const char*)pabyBoxData, -1) && + (int)strlen((const char*)pabyBoxData) + 2 >= nBoxDataLength ) + { + CPLXMLNode* psXMLContentBox = NULL; + if( ((const char*)pabyBoxData)[0] == '<' ) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + psXMLContentBox = CPLParseXMLString((const char*)pabyBoxData); + CPLPopErrorHandler(); + } + if( psXMLContentBox ) + { + CPLXMLNode* psXMLContentNode = + CPLCreateXMLNode( psBox, CXT_Element, "XMLContent" ); + psXMLContentNode->psChild = psXMLContentBox; + } + else + { + CPLCreateXMLNode( + CPLCreateXMLNode( psBox, CXT_Element, "TextContent" ), + CXT_Text, (const char*)pabyBoxData); + } + } + } + CPLFree(pabyBoxData); + } + + if( strcmp(pszBoxType, "jp2c") == 0 ) + { + if( CSLFetchBoolean(papszOptions, "CODESTREAM", FALSE) || + CSLFetchBoolean(papszOptions, "ALL", FALSE) ) + { + DumpJPK2CodeStream(psBox, fp, + oBox.GetDataOffset(), nBoxDataLength); + } + } + else if( strcmp(pszBoxType, "uuid") == 0 && + GDALJP2Metadata::IsUUID_MSI(oBox.GetUUID()) ) + { + DumpGeoTIFFBox(psBox, oBox); + } + else if( strcmp(pszBoxType, "ftyp") == 0 ) + { + DumpFTYPBox(psBox, oBox); + } + else if( strcmp(pszBoxType, "ihdr") == 0 ) + { + DumpIHDRBox(psBox, oBox); + } + else if( strcmp(pszBoxType, "bpcc") == 0 ) + { + DumpBPCCBox(psBox, oBox); + } + else if( strcmp(pszBoxType, "colr") == 0 ) + { + DumpCOLRBox(psBox, oBox); + } + else if( strcmp(pszBoxType, "pclr") == 0 ) + { + DumpPCLRBox(psBox, oBox); + } + else if( strcmp(pszBoxType, "cmap") == 0 ) + { + DumpCMAPBox(psBox, oBox); + } + else if( strcmp(pszBoxType, "cdef") == 0 ) + { + DumpCDEFBox(psBox, oBox); + } + else if( strcmp(pszBoxType, "resc") == 0 || + strcmp(pszBoxType, "resd") == 0) + { + DumpRESxBox(psBox, oBox); + } + else if( strcmp(pszBoxType, "rreq") == 0 ) + { + DumpRREQBox(psBox, oBox); + } + } + + if (!oBox.ReadNextChild(poParentBox)) + break; + } + } +} + +/************************************************************************/ +/* GDALGetJPEG2000Structure() */ +/************************************************************************/ + +static const unsigned char jpc_header[] = {0xff,0x4f}; +static const unsigned char jp2_box_jp[] = {0x6a,0x50,0x20,0x20}; /* 'jP ' */ + +/** Dump the structure of a JPEG2000 file as a XML tree. + * + * @param pszFilename filename. + * @param papszOptions NULL terminated list of options, or NULL. + * Allowed options are BINARY_CONTENT=YES, TEXT_CONTENT=YES, + * CODESTREAM=YES, ALL=YES. + * @return XML tree (to be freed with CPLDestroyXMLNode()) or NULL in case + * of error + * @since GDAL 2.0 + */ + +CPLXMLNode* GDALGetJPEG2000Structure(const char* pszFilename, + char** papszOptions) +{ + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if( fp == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename); + return NULL; + } + GByte abyHeader[16]; + if( VSIFReadL(abyHeader, 16, 1, fp) != 1 || + (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) != 0 && + memcmp(abyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) != 0) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s is not a JPEG2000 file", pszFilename); + VSIFCloseL(fp); + return NULL; + } + + CPLXMLNode* psParent = NULL; + if( memcmp(abyHeader, jpc_header, sizeof(jpc_header)) == 0 ) + { + if( CSLFetchBoolean(papszOptions, "CODESTREAM", FALSE) || + CSLFetchBoolean(papszOptions, "ALL", FALSE) ) + { + VSIFSeekL(fp, 0, SEEK_END); + GIntBig nBoxDataLength = (GIntBig)VSIFTellL(fp); + psParent = DumpJPK2CodeStream(NULL, fp, 0, nBoxDataLength); + CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename ); + } + } + else + { + psParent = CPLCreateXMLNode( NULL, CXT_Element, "JP2File" ); + CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename ); + GDALGetJPEG2000StructureInternal(psParent, fp, NULL, papszOptions ); + } + + VSIFCloseL(fp); + return psParent; +} diff --git a/bazaar/plugin/gdal/gcore/gdalmajorobject.cpp b/bazaar/plugin/gdal/gcore/gdalmajorobject.cpp new file mode 100644 index 000000000..a94e6d920 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalmajorobject.cpp @@ -0,0 +1,424 @@ +/****************************************************************************** + * $Id: gdalmajorobject.cpp 27110 2014-03-28 21:29:20Z rouault $ + * + * Project: GDAL Core + * Purpose: Base class for objects with metadata, etc. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: gdalmajorobject.cpp 27110 2014-03-28 21:29:20Z rouault $"); + +/************************************************************************/ +/* GDALMajorObject() */ +/************************************************************************/ + +GDALMajorObject::GDALMajorObject() + +{ + nFlags = GMO_VALID; +} + +/************************************************************************/ +/* ~GDALMajorObject() */ +/************************************************************************/ + +GDALMajorObject::~GDALMajorObject() + +{ + if( (nFlags & GMO_VALID) == 0 ) + CPLDebug( "GDAL", "In ~GDALMajorObject on invalid object" ); + + nFlags &= ~GMO_VALID; +} + +/************************************************************************/ +/* GetDescription() */ +/************************************************************************/ + +/** + * \brief Fetch object description. + * + * The semantics of the returned description are specific to the derived + * type. For GDALDatasets it is the dataset name. For GDALRasterBands + * it is actually a description (if supported) or "". + * + * This method is the same as the C function GDALGetDescription(). + * + * @return non-null pointer to internal description string. + */ + +const char *GDALMajorObject::GetDescription() const + +{ + return sDescription.c_str(); +} + +/************************************************************************/ +/* GDALGetDescription() */ +/************************************************************************/ + +/** + * \brief Fetch object description. + * + * @see GDALMajorObject::GetDescription() + */ + +const char * CPL_STDCALL GDALGetDescription( GDALMajorObjectH hObject ) + +{ + VALIDATE_POINTER1( hObject, "GDALGetDescription", NULL ); + + return ((GDALMajorObject *) hObject)->GetDescription(); +} + +/************************************************************************/ +/* SetDescription() */ +/************************************************************************/ + +/** + * \brief Set object description. + * + * The semantics of the description are specific to the derived + * type. For GDALDatasets it is the dataset name. For GDALRasterBands + * it is actually a description (if supported) or "". + * + * Normally application code should not set the "description" for + * GDALDatasets. It is handled internally. + * + * This method is the same as the C function GDALSetDescription(). + */ + +void GDALMajorObject::SetDescription( const char * pszNewDesc ) + +{ + sDescription = pszNewDesc; +} + +/************************************************************************/ +/* GDALSetDescription() */ +/************************************************************************/ + +/** + * \brief Set object description. + * + * @see GDALMajorObject::SetDescription() + */ + +void CPL_STDCALL GDALSetDescription( GDALMajorObjectH hObject, const char *pszNewDesc ) + +{ + VALIDATE_POINTER0( hObject, "GDALSetDescription" ); + + ((GDALMajorObject *) hObject)->SetDescription( pszNewDesc ); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +/** + * \brief Fetch list of metadata domains. + * + * The returned string list is the list of (non-empty) metadata domains. + * + * This method does the same thing as the C function GDALGetMetadataDomainList(). + * + * @return NULL or a string list. Must be freed with CSLDestroy() + * + * @since GDAL 1.11 + */ + +char **GDALMajorObject::GetMetadataDomainList() +{ + return CSLDuplicate(oMDMD.GetDomainList()); +} + +/************************************************************************/ +/* BuildMetadataDomainList() */ +/************************************************************************/ + +/** + * \brief Helper function for custom implementations of GetMetadataDomainList() + * + * + * @param papszList initial list of domains. May be NULL. Will become invalid + * after function call (use return value) + * @param bCheckNonEmpty if TRUE, each candidate domain will be tested to be non + * empty + * @param ... NULL terminated variadic list of candidate domains. + * + * @return NULL or a string list. Must be freed with CSLDestroy() + * + * @since GDAL 1.11 + */ + +char **GDALMajorObject::BuildMetadataDomainList(char** papszList, int bCheckNonEmpty, ...) +{ + va_list args; + const char* pszDomain; + va_start(args, bCheckNonEmpty); + + while( (pszDomain = va_arg(args, const char*)) != NULL ) + { + if( CSLFindString(papszList, pszDomain) < 0 && + (!bCheckNonEmpty || GetMetadata(pszDomain) != NULL) ) + { + papszList = CSLAddString(papszList, pszDomain); + } + } + + va_end(args); + + return papszList; +} + +/************************************************************************/ +/* GDALGetMetadataDomainList() */ +/************************************************************************/ + +/** + * \brief Fetch list of metadata domains. + * + * @see GDALMajorObject::GetMetadataDomainList() + * + * @since GDAL 1.11 + */ + +char ** CPL_STDCALL +GDALGetMetadataDomainList( GDALMajorObjectH hObject) + +{ + VALIDATE_POINTER1( hObject, "GetMetadataDomainList", NULL ); + + return ((GDALMajorObject *) hObject)->GetMetadataDomainList(); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +/** + * \brief Fetch metadata. + * + * The returned string list is owned by the object, and may change at + * any time. It is formated as a "Name=value" list with the last pointer + * value being NULL. Use the the CPL StringList functions such as + * CSLFetchNameValue() to manipulate it. + * + * Note that relatively few formats return any metadata at this time. + * + * This method does the same thing as the C function GDALGetMetadata(). + * + * @param pszDomain the domain of interest. Use "" or NULL for the default + * domain. + * + * @return NULL or a string list. + */ + +char **GDALMajorObject::GetMetadata( const char * pszDomain ) + +{ + return oMDMD.GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* GDALGetMetadata() */ +/************************************************************************/ + +/** + * \brief Fetch metadata. + * + * @see GDALMajorObject::GetMetadata() + */ + +char ** CPL_STDCALL +GDALGetMetadata( GDALMajorObjectH hObject, const char * pszDomain ) + +{ + VALIDATE_POINTER1( hObject, "GDALGetMetadata", NULL ); + + return ((GDALMajorObject *) hObject)->GetMetadata(pszDomain); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +/** + * \brief Set metadata. + * + * The C function GDALSetMetadata() does the same thing as this method. + * + * @param papszMetadataIn the metadata in name=value string list format to + * apply. + * @param pszDomain the domain of interest. Use "" or NULL for the default + * domain. + * @return CE_None on success, CE_Failure on failure and CE_Warning if the + * metadata has been accepted, but is likely not maintained persistently + * by the underlying object between sessions. + */ + +CPLErr GDALMajorObject::SetMetadata( char ** papszMetadataIn, + const char * pszDomain ) + +{ + nFlags |= GMO_MD_DIRTY; + return oMDMD.SetMetadata( papszMetadataIn, pszDomain ); +} + +/************************************************************************/ +/* GDALSetMetadata() */ +/************************************************************************/ + +/** + * \brief Set metadata. + * + * @see GDALMajorObject::SetMetadata() + */ + +CPLErr CPL_STDCALL +GDALSetMetadata( GDALMajorObjectH hObject, char **papszMD, + const char *pszDomain ) + +{ + VALIDATE_POINTER1( hObject, "GDALSetMetadata", CE_Failure ); + + return ((GDALMajorObject *) hObject)->SetMetadata( papszMD, pszDomain ); +} + + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +/** + * \brief Fetch single metadata item. + * + * The C function GDALGetMetadataItem() does the same thing as this method. + * + * @param pszName the key for the metadata item to fetch. + * @param pszDomain the domain to fetch for, use NULL for the default domain. + * + * @return NULL on failure to find the key, or a pointer to an internal + * copy of the value string on success. + */ + +const char *GDALMajorObject::GetMetadataItem( const char * pszName, + const char * pszDomain ) + +{ + return oMDMD.GetMetadataItem( pszName, pszDomain ); +} + +/************************************************************************/ +/* GDALGetMetadataItem() */ +/************************************************************************/ + +/** + * \brief Fetch single metadata item. + * + * @see GDALMajorObject::GetMetadataItem() + */ + +const char * CPL_STDCALL GDALGetMetadataItem( GDALMajorObjectH hObject, + const char *pszName, + const char *pszDomain ) + +{ + VALIDATE_POINTER1( hObject, "GDALGetMetadataItem", NULL ); + + return ((GDALMajorObject *) hObject)->GetMetadataItem( pszName, pszDomain); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +/** + * \brief Set single metadata item. + * + * The C function GDALSetMetadataItem() does the same thing as this method. + * + * @param pszName the key for the metadata item to fetch. + * @param pszValue the value to assign to the key. + * @param pszDomain the domain to set within, use NULL for the default domain. + * + * @return CE_None on success, or an error code on failure. + */ + +CPLErr GDALMajorObject::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) + +{ + nFlags |= GMO_MD_DIRTY; + return oMDMD.SetMetadataItem( pszName, pszValue, pszDomain ); +} + +/************************************************************************/ +/* GDALSetMetadataItem() */ +/************************************************************************/ + +/** + * \brief Set single metadata item. + * + * @see GDALMajorObject::SetMetadataItem() + */ + +CPLErr CPL_STDCALL +GDALSetMetadataItem( GDALMajorObjectH hObject, + const char *pszName, const char *pszValue, + const char *pszDomain ) + +{ + VALIDATE_POINTER1( hObject, "GDALSetMetadataItem", CE_Failure ); + + return ((GDALMajorObject *) hObject)->SetMetadataItem( pszName, pszValue, + pszDomain ); +} + +/************************************************************************/ +/* GetMOFlags() */ +/************************************************************************/ + +int GDALMajorObject::GetMOFlags() + +{ + return nFlags; +} + +/************************************************************************/ +/* SetMOFlags() */ +/************************************************************************/ + +void GDALMajorObject::SetMOFlags( int nNewFlags ) + +{ + nFlags = nNewFlags; +} + diff --git a/bazaar/plugin/gdal/gcore/gdalmultidomainmetadata.cpp b/bazaar/plugin/gdal/gcore/gdalmultidomainmetadata.cpp new file mode 100644 index 000000000..e843304ca --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalmultidomainmetadata.cpp @@ -0,0 +1,352 @@ +/****************************************************************************** + * $Id: gdalmultidomainmetadata.cpp 29038 2015-04-28 09:03:36Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALMultiDomainMetadata class. This class + * manages metadata items for a variable list of domains. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: gdalmultidomainmetadata.cpp 29038 2015-04-28 09:03:36Z rouault $"); + +/************************************************************************/ +/* GDALMultiDomainMetadata() */ +/************************************************************************/ + +GDALMultiDomainMetadata::GDALMultiDomainMetadata() + +{ + papszDomainList = NULL; + papoMetadataLists = NULL; +} + +/************************************************************************/ +/* ~GDALMultiDomainMetadata() */ +/************************************************************************/ + +GDALMultiDomainMetadata::~GDALMultiDomainMetadata() + +{ + Clear(); +} + +/************************************************************************/ +/* Clear() */ +/************************************************************************/ + +void GDALMultiDomainMetadata::Clear() + +{ + int i, nDomainCount; + + nDomainCount = CSLCount( papszDomainList ); + CSLDestroy( papszDomainList ); + papszDomainList = NULL; + + for( i = 0; i < nDomainCount; i++ ) + { + delete papoMetadataLists[i]; + } + CPLFree( papoMetadataLists ); + papoMetadataLists = NULL; +} + + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GDALMultiDomainMetadata::GetMetadata( const char *pszDomain ) + +{ + if( pszDomain == NULL ) + pszDomain = ""; + + int iDomain = CSLFindString( papszDomainList, pszDomain ); + + if( iDomain == -1 ) + return NULL; + else + return papoMetadataLists[iDomain]->List(); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr GDALMultiDomainMetadata::SetMetadata( char **papszMetadata, + const char *pszDomain ) + +{ + if( pszDomain == NULL ) + pszDomain = ""; + + int iDomain = CSLFindString( papszDomainList, pszDomain ); + + if( iDomain == -1 ) + { + int nDomainCount; + + papszDomainList = CSLAddString( papszDomainList, pszDomain ); + nDomainCount = CSLCount( papszDomainList ); + + papoMetadataLists = (CPLStringList **) + CPLRealloc( papoMetadataLists, sizeof(void*)*(nDomainCount+1) ); + papoMetadataLists[nDomainCount] = NULL; + papoMetadataLists[nDomainCount-1] = new CPLStringList(); + iDomain = nDomainCount-1; + } + + papoMetadataLists[iDomain]->Assign( CSLDuplicate( papszMetadata ) ); + + // we want to mark name/value pair domains as being sorted for fast + // access. + if( !EQUALN(pszDomain,"xml:",4) && !EQUAL(pszDomain, "SUBDATASETS") ) + papoMetadataLists[iDomain]->Sort(); + + return CE_None; +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *GDALMultiDomainMetadata::GetMetadataItem( const char *pszName, + const char *pszDomain ) + +{ + if( pszDomain == NULL ) + pszDomain = ""; + + int iDomain = CSLFindString( papszDomainList, pszDomain ); + + if( iDomain == -1 ) + return NULL; + else + return papoMetadataLists[iDomain]->FetchNameValue( pszName ); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALMultiDomainMetadata::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ + if( pszDomain == NULL ) + pszDomain = ""; + +/* -------------------------------------------------------------------- */ +/* Create the domain if it does not already exist. */ +/* -------------------------------------------------------------------- */ + int iDomain = CSLFindString( papszDomainList, pszDomain ); + + if( iDomain == -1 ) + { + SetMetadata( NULL, pszDomain ); + iDomain = CSLFindString( papszDomainList, pszDomain ); + } + +/* -------------------------------------------------------------------- */ +/* Set the value in the domain list. */ +/* -------------------------------------------------------------------- */ + papoMetadataLists[iDomain]->SetNameValue( pszName, pszValue ); + + return CE_None; +} + +/************************************************************************/ +/* XMLInit() */ +/* */ +/* This method should be invoked on the parent of the */ +/* elements. */ +/************************************************************************/ + +int GDALMultiDomainMetadata::XMLInit( CPLXMLNode *psTree, CPL_UNUSED int bMerge ) +{ + CPLXMLNode *psMetadata; + +/* ==================================================================== */ +/* Process all elements, each for one domain. */ +/* ==================================================================== */ + for( psMetadata = psTree->psChild; + psMetadata != NULL; psMetadata = psMetadata->psNext ) + { + CPLXMLNode *psMDI; + const char *pszDomain, *pszFormat; + + if( psMetadata->eType != CXT_Element + || !EQUAL(psMetadata->pszValue,"Metadata") ) + continue; + + pszDomain = CPLGetXMLValue( psMetadata, "domain", "" ); + pszFormat = CPLGetXMLValue( psMetadata, "format", "" ); + + // Make sure we have a CPLStringList for this domain, + // without wiping out an existing one. + if( GetMetadata( pszDomain ) == NULL ) + SetMetadata( NULL, pszDomain ); + + int iDomain = CSLFindString( papszDomainList, pszDomain ); + CPLAssert( iDomain != -1 ); + + CPLStringList *poMDList = papoMetadataLists[iDomain]; + +/* -------------------------------------------------------------------- */ +/* XML format subdocuments. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszFormat,"xml") ) + { + CPLXMLNode *psSubDoc; + + /* find first non-attribute child of current element */ + psSubDoc = psMetadata->psChild; + while( psSubDoc != NULL && psSubDoc->eType == CXT_Attribute ) + psSubDoc = psSubDoc->psNext; + + char *pszDoc = CPLSerializeXMLTree( psSubDoc ); + + poMDList->Clear(); + poMDList->AddStringDirectly( pszDoc ); + } + +/* -------------------------------------------------------------------- */ +/* Name value format. */ +/* value_Text */ +/* -------------------------------------------------------------------- */ + else + { + for( psMDI = psMetadata->psChild; psMDI != NULL; + psMDI = psMDI->psNext ) + { + if( !EQUAL(psMDI->pszValue,"MDI") + || psMDI->eType != CXT_Element + || psMDI->psChild == NULL + || psMDI->psChild->psNext == NULL + || psMDI->psChild->eType != CXT_Attribute + || psMDI->psChild->psChild == NULL ) + continue; + + char* pszName = psMDI->psChild->psChild->pszValue; + char* pszValue = psMDI->psChild->psNext->pszValue; + if( pszName != NULL && pszValue != NULL ) + poMDList->SetNameValue( pszName, pszValue ); + } + } + } + + return CSLCount(papszDomainList) != 0; +} + +/************************************************************************/ +/* Serialize() */ +/************************************************************************/ + +CPLXMLNode *GDALMultiDomainMetadata::Serialize() + +{ + CPLXMLNode *psFirst = NULL; + + for( int iDomain = 0; + papszDomainList != NULL && papszDomainList[iDomain] != NULL; + iDomain++) + { + char **papszMD = papoMetadataLists[iDomain]->List(); + // Do not serialize empty domains + if( papszMD == NULL || papszMD[0] == NULL ) + continue; + + CPLXMLNode *psMD; + int bFormatXML = FALSE; + + psMD = CPLCreateXMLNode( NULL, CXT_Element, "Metadata" ); + + if( strlen( papszDomainList[iDomain] ) > 0 ) + CPLCreateXMLNode( + CPLCreateXMLNode( psMD, CXT_Attribute, "domain" ), + CXT_Text, papszDomainList[iDomain] ); + + if( EQUALN(papszDomainList[iDomain],"xml:",4) + && CSLCount(papszMD) == 1 ) + { + CPLXMLNode *psValueAsXML = CPLParseXMLString( papszMD[0] ); + if( psValueAsXML != NULL ) + { + bFormatXML = TRUE; + + CPLCreateXMLNode( + CPLCreateXMLNode( psMD, CXT_Attribute, "format" ), + CXT_Text, "xml" ); + + CPLAddXMLChild( psMD, psValueAsXML ); + } + } + + if( !bFormatXML ) + { + CPLXMLNode* psLastChild = NULL; + // To go after domain attribute + if( psMD->psChild != NULL ) + { + psLastChild = psMD->psChild; + while( psLastChild->psNext != NULL ) + psLastChild = psLastChild->psNext; + } + for( int i = 0; papszMD != NULL && papszMD[i] != NULL; i++ ) + { + const char *pszRawValue; + char *pszKey = NULL; + CPLXMLNode *psMDI; + + pszRawValue = CPLParseNameValue( papszMD[i], &pszKey ); + + psMDI = CPLCreateXMLNode( NULL, CXT_Element, "MDI" ); + if( psLastChild == NULL ) + psMD->psChild = psMDI; + else + psLastChild->psNext = psMDI; + psLastChild = psMDI; + + CPLSetXMLValue( psMDI, "#key", pszKey ); + CPLCreateXMLNode( psMDI, CXT_Text, pszRawValue ); + + CPLFree( pszKey ); + } + } + + if( psFirst == NULL ) + psFirst = psMD; + else + CPLAddXMLSibling( psFirst, psMD ); + } + + return psFirst; +} diff --git a/bazaar/plugin/gdal/gcore/gdalnodatamaskband.cpp b/bazaar/plugin/gdal/gcore/gdalnodatamaskband.cpp new file mode 100644 index 000000000..56b348bc2 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalnodatamaskband.cpp @@ -0,0 +1,288 @@ +/****************************************************************************** + * $Id: gdalnodatamaskband.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALNoDataMaskBand, a class implementing all + * a default band mask based on nodata values. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +CPL_CVSID("$Id: gdalnodatamaskband.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +/************************************************************************/ +/* GDALNoDataMaskBand() */ +/************************************************************************/ + +GDALNoDataMaskBand::GDALNoDataMaskBand( GDALRasterBand *poParent ) + +{ + poDS = NULL; + nBand = 0; + + nRasterXSize = poParent->GetXSize(); + nRasterYSize = poParent->GetYSize(); + + eDataType = GDT_Byte; + poParent->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + this->poParent = poParent; + dfNoDataValue = poParent->GetNoDataValue(); +} + +/************************************************************************/ +/* ~GDALNoDataMaskBand() */ +/************************************************************************/ + +GDALNoDataMaskBand::~GDALNoDataMaskBand() + +{ +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GDALNoDataMaskBand::IReadBlock( int nXBlockOff, int nYBlockOff, + void * pImage ) + +{ + GDALDataType eWrkDT; + +/* -------------------------------------------------------------------- */ +/* Decide on a working type. */ +/* -------------------------------------------------------------------- */ + switch( poParent->GetRasterDataType() ) + { + case GDT_Byte: + eWrkDT = GDT_Byte; + break; + + case GDT_UInt16: + case GDT_UInt32: + eWrkDT = GDT_UInt32; + break; + + case GDT_Int16: + case GDT_Int32: + case GDT_CInt16: + case GDT_CInt32: + eWrkDT = GDT_Int32; + break; + + case GDT_Float32: + case GDT_CFloat32: + eWrkDT = GDT_Float32; + break; + + case GDT_Float64: + case GDT_CFloat64: + eWrkDT = GDT_Float64; + break; + + default: + CPLAssert( FALSE ); + eWrkDT = GDT_Float64; + break; + } + +/* -------------------------------------------------------------------- */ +/* Read the image data. */ +/* -------------------------------------------------------------------- */ + GByte *pabySrc; + CPLErr eErr; + + pabySrc = (GByte *) VSIMalloc3( GDALGetDataTypeSize(eWrkDT)/8, nBlockXSize, nBlockYSize ); + if (pabySrc == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALNoDataMaskBand::IReadBlock: Out of memory for buffer." ); + return CE_Failure; + } + + + int nXSizeRequest = nBlockXSize; + if (nXBlockOff * nBlockXSize + nBlockXSize > nRasterXSize) + nXSizeRequest = nRasterXSize - nXBlockOff * nBlockXSize; + int nYSizeRequest = nBlockYSize; + if (nYBlockOff * nBlockYSize + nBlockYSize > nRasterYSize) + nYSizeRequest = nRasterYSize - nYBlockOff * nBlockYSize; + + if (nXSizeRequest != nBlockXSize || nYSizeRequest != nBlockYSize) + { + /* memset the whole buffer to avoid Valgrind warnings in case we can't */ + /* fetch a full block */ + memset(pabySrc, 0, GDALGetDataTypeSize(eWrkDT)/8 * nBlockXSize * nBlockYSize ); + } + + eErr = poParent->RasterIO( GF_Read, + nXBlockOff * nBlockXSize, nYBlockOff * nBlockYSize, + nXSizeRequest, nYSizeRequest, + pabySrc, nXSizeRequest, nYSizeRequest, + eWrkDT, 0, nBlockXSize * (GDALGetDataTypeSize(eWrkDT)/8), + NULL ); + if( eErr != CE_None ) + { + CPLFree(pabySrc); + return eErr; + } + + int bIsNoDataNan = CPLIsNan(dfNoDataValue); + +/* -------------------------------------------------------------------- */ +/* Process different cases. */ +/* -------------------------------------------------------------------- */ + int i; + switch( eWrkDT ) + { + case GDT_Byte: + { + GByte byNoData = (GByte) dfNoDataValue; + + for( i = nBlockXSize * nBlockYSize - 1; i >= 0; i-- ) + { + if( pabySrc[i] == byNoData ) + ((GByte *) pImage)[i] = 0; + else + ((GByte *) pImage)[i] = 255; + } + } + break; + + case GDT_UInt32: + { + GUInt32 nNoData = (GUInt32) dfNoDataValue; + + for( i = nBlockXSize * nBlockYSize - 1; i >= 0; i-- ) + { + if( ((GUInt32 *)pabySrc)[i] == nNoData ) + ((GByte *) pImage)[i] = 0; + else + ((GByte *) pImage)[i] = 255; + } + } + break; + + case GDT_Int32: + { + GInt32 nNoData = (GInt32) dfNoDataValue; + + for( i = nBlockXSize * nBlockYSize - 1; i >= 0; i-- ) + { + if( ((GInt32 *)pabySrc)[i] == nNoData ) + ((GByte *) pImage)[i] = 0; + else + ((GByte *) pImage)[i] = 255; + } + } + break; + + case GDT_Float32: + { + float fNoData = (float) dfNoDataValue; + + for( i = nBlockXSize * nBlockYSize - 1; i >= 0; i-- ) + { + float fVal =((float *)pabySrc)[i]; + if( bIsNoDataNan && CPLIsNan(fVal)) + ((GByte *) pImage)[i] = 0; + else if( ARE_REAL_EQUAL(fVal, fNoData) ) + ((GByte *) pImage)[i] = 0; + else + ((GByte *) pImage)[i] = 255; + } + } + break; + + case GDT_Float64: + { + for( i = nBlockXSize * nBlockYSize - 1; i >= 0; i-- ) + { + double dfVal =((double *)pabySrc)[i]; + if( bIsNoDataNan && CPLIsNan(dfVal)) + ((GByte *) pImage)[i] = 0; + else if( ARE_REAL_EQUAL(dfVal, dfNoDataValue) ) + ((GByte *) pImage)[i] = 0; + else + ((GByte *) pImage)[i] = 255; + } + } + break; + + default: + CPLAssert( FALSE ); + break; + } + + CPLFree( pabySrc ); + + return CE_None; +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr GDALNoDataMaskBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ + /* Optimization in common use case (#4488) */ + /* This avoids triggering the block cache on this band, which helps */ + /* reducing the global block cache consumption */ + if (eRWFlag == GF_Read && eBufType == GDT_Byte && + poParent->GetRasterDataType() == GDT_Byte && + nXSize == nBufXSize && nYSize == nBufYSize && + nPixelSpace == 1 && nLineSpace == nBufXSize) + { + CPLErr eErr = poParent->RasterIO( GF_Read, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + if (eErr != CE_None) + return eErr; + + GByte* pabyData = (GByte*) pData; + GByte byNoData = (GByte) dfNoDataValue; + + for( int i = nBufXSize * nBufYSize - 1; i >= 0; i-- ) + { + if( pabyData[i] == byNoData ) + pabyData[i] = 0; + else + pabyData[i] = 255; + } + return CE_None; + } + + return GDALRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace, psExtraArg ); +} diff --git a/bazaar/plugin/gdal/gcore/gdalnodatavaluesmaskband.cpp b/bazaar/plugin/gdal/gcore/gdalnodatavaluesmaskband.cpp new file mode 100644 index 000000000..42a07f134 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalnodatavaluesmaskband.cpp @@ -0,0 +1,312 @@ +/****************************************************************************** + * $Id: gdalnodatavaluesmaskband.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALNoDataValuesMaskBand, a class implementing + * a default band mask based on the NODATA_VALUES metadata item. + * A pixel is considered nodata in all bands if and only if all bands + * match the corresponding value in the NODATA_VALUES tuple + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2008-2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +CPL_CVSID("$Id: gdalnodatavaluesmaskband.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +/************************************************************************/ +/* GDALNoDataValuesMaskBand() */ +/************************************************************************/ + +GDALNoDataValuesMaskBand::GDALNoDataValuesMaskBand( GDALDataset* poDS ) + +{ + const char* pszNoDataValues = poDS->GetMetadataItem("NODATA_VALUES"); + char** papszNoDataValues = CSLTokenizeStringComplex(pszNoDataValues, " ", FALSE, FALSE); + + int i; + padfNodataValues = (double*)CPLMalloc(sizeof(double) * poDS->GetRasterCount()); + for(i=0;iGetRasterCount();i++) + { + padfNodataValues[i] = CPLAtof(papszNoDataValues[i]); + } + + CSLDestroy(papszNoDataValues); + + this->poDS = poDS; + nBand = 0; + + nRasterXSize = poDS->GetRasterXSize(); + nRasterYSize = poDS->GetRasterYSize(); + + eDataType = GDT_Byte; + poDS->GetRasterBand(1)->GetBlockSize( &nBlockXSize, &nBlockYSize ); +} + +/************************************************************************/ +/* ~GDALNoDataValuesMaskBand() */ +/************************************************************************/ + +GDALNoDataValuesMaskBand::~GDALNoDataValuesMaskBand() + +{ + CPLFree(padfNodataValues); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GDALNoDataValuesMaskBand::IReadBlock( int nXBlockOff, int nYBlockOff, + void * pImage ) + +{ + int iBand; + GDALDataType eWrkDT; + +/* -------------------------------------------------------------------- */ +/* Decide on a working type. */ +/* -------------------------------------------------------------------- */ + switch( poDS->GetRasterBand(1)->GetRasterDataType() ) + { + case GDT_Byte: + eWrkDT = GDT_Byte; + break; + + case GDT_UInt16: + case GDT_UInt32: + eWrkDT = GDT_UInt32; + break; + + case GDT_Int16: + case GDT_Int32: + case GDT_CInt16: + case GDT_CInt32: + eWrkDT = GDT_Int32; + break; + + case GDT_Float32: + case GDT_CFloat32: + eWrkDT = GDT_Float32; + break; + + case GDT_Float64: + case GDT_CFloat64: + eWrkDT = GDT_Float64; + break; + + default: + CPLAssert( FALSE ); + eWrkDT = GDT_Float64; + break; + } + +/* -------------------------------------------------------------------- */ +/* Read the image data. */ +/* -------------------------------------------------------------------- */ + GByte *pabySrc; + CPLErr eErr; + + int nBands = poDS->GetRasterCount(); + pabySrc = (GByte *) VSIMalloc3( nBands * GDALGetDataTypeSize(eWrkDT)/8, nBlockXSize, nBlockYSize ); + if (pabySrc == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALNoDataValuesMaskBand::IReadBlock: Out of memory for buffer." ); + return CE_Failure; + } + + int nXSizeRequest = nBlockXSize; + if (nXBlockOff * nBlockXSize + nBlockXSize > nRasterXSize) + nXSizeRequest = nRasterXSize - nXBlockOff * nBlockXSize; + int nYSizeRequest = nBlockYSize; + if (nYBlockOff * nBlockYSize + nBlockYSize > nRasterYSize) + nYSizeRequest = nRasterYSize - nYBlockOff * nBlockYSize; + + if (nXSizeRequest != nBlockXSize || nYSizeRequest != nBlockYSize) + { + /* memset the whole buffer to avoid Valgrind warnings in case we can't */ + /* fetch a full block */ + memset(pabySrc, 0, nBands * GDALGetDataTypeSize(eWrkDT)/8 * nBlockXSize * nBlockYSize ); + } + + int nBlockOffsetPixels = nBlockXSize * nBlockYSize; + int nBandOffsetByte = (GDALGetDataTypeSize(eWrkDT)/8) * nBlockXSize * nBlockYSize; + for(iBand=0;iBandGetRasterBand(iBand + 1)->RasterIO( + GF_Read, + nXBlockOff * nBlockXSize, nYBlockOff * nBlockYSize, + nXSizeRequest, nYSizeRequest, + pabySrc + iBand * nBandOffsetByte, nXSizeRequest, nYSizeRequest, + eWrkDT, 0, nBlockXSize * (GDALGetDataTypeSize(eWrkDT)/8), + NULL); + if( eErr != CE_None ) + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Process different cases. */ +/* -------------------------------------------------------------------- */ + int i; + switch( eWrkDT ) + { + case GDT_Byte: + { + GByte* pabyNoData = (GByte*) CPLMalloc(nBands * sizeof(GByte)); + for(iBand=0;iBand= 0; i-- ) + { + int nCountNoData = 0; + for(iBand=0;iBand= 0; i-- ) + { + int nCountNoData = 0; + for(iBand=0;iBand= 0; i-- ) + { + int nCountNoData = 0; + for(iBand=0;iBand= 0; i-- ) + { + int nCountNoData = 0; + for(iBand=0;iBand= 0; i-- ) + { + int nCountNoData = 0; + for(iBand=0;iBand + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_conv.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +CPL_CVSID("$Id: gdalopeninfo.cpp 29107 2015-05-02 11:23:26Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* GDALOpenInfo */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* GDALOpenInfo() */ +/************************************************************************/ + +GDALOpenInfo::GDALOpenInfo( const char * pszFilenameIn, int nOpenFlagsIn, + char **papszSiblingsIn ) + +{ +/* -------------------------------------------------------------------- */ +/* Ensure that C: is treated as C:\ so we can stat it on */ +/* Windows. Similar to what is done in CPLStat(). */ +/* -------------------------------------------------------------------- */ +#ifdef WIN32 + if( strlen(pszFilenameIn) == 2 && pszFilenameIn[1] == ':' ) + { + char szAltPath[10]; + + strcpy( szAltPath, pszFilenameIn ); + strcat( szAltPath, "\\" ); + pszFilename = CPLStrdup( szAltPath ); + } + else +#endif + pszFilename = CPLStrdup( pszFilenameIn ); + +/* -------------------------------------------------------------------- */ +/* Initialize. */ +/* -------------------------------------------------------------------- */ + + nHeaderBytes = 0; + nHeaderBytesTried = 0; + pabyHeader = NULL; + bIsDirectory = FALSE; + bStatOK = FALSE; + nOpenFlags = nOpenFlagsIn; + eAccess = (nOpenFlags & GDAL_OF_UPDATE) ? GA_Update : GA_ReadOnly; + fpL = NULL; + papszOpenOptions = NULL; + +#ifdef HAVE_READLINK + int bHasRetried = FALSE; +#endif + +/* -------------------------------------------------------------------- */ +/* Collect information about the file. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStat; + +#ifdef HAVE_READLINK +retry: +#endif + int bPotentialDirectory = FALSE; + + /* Check if the filename might be a directory of a special virtual file system */ + if( strncmp(pszFilename, "/vsizip/", strlen("/vsizip/")) == 0 || + strncmp(pszFilename, "/vsitar/", strlen("/vsitar/")) == 0 ) + { + const char* pszExt = CPLGetExtension(pszFilename); + if( EQUAL(pszExt, "zip") || EQUAL(pszExt, "tar") || EQUAL(pszExt, "gz") ) + bPotentialDirectory = TRUE; + } + else if( strncmp(pszFilename, "/vsicurl/", strlen("/vsicurl/")) == 0 ) + { + bPotentialDirectory = TRUE; + } + + if( bPotentialDirectory ) + { + /* For those special files, opening them with VSIFOpenL() might result */ + /* in content, even if they should be considered as directories, so */ + /* use stat */ + if( VSIStatExL( pszFilename, &sStat, + VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0 ) + { + bStatOK = TRUE; + if( VSI_ISDIR( sStat.st_mode ) ) + bIsDirectory = TRUE; + } + } + + if( !bIsDirectory ) + fpL = VSIFOpenL( pszFilename, (eAccess == GA_Update) ? "r+b" : "rb" ); + if( fpL != NULL ) + { + bStatOK = TRUE; + pabyHeader = (GByte *) CPLCalloc(1025,1); + nHeaderBytesTried = 1024; + nHeaderBytes = (int) VSIFReadL( pabyHeader, 1, nHeaderBytesTried, fpL ); + VSIRewindL( fpL ); + + /* If we cannot read anything, check if it is not a directory instead */ + if( nHeaderBytes == 0 && + VSIStatExL( pszFilename, &sStat, + VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0 && + VSI_ISDIR( sStat.st_mode ) ) + { + VSIFCloseL(fpL); + fpL = NULL; + CPLFree(pabyHeader); + pabyHeader = NULL; + bIsDirectory = TRUE; + } + } + else if( !bStatOK ) + { + if( VSIStatExL( pszFilename, &sStat, + VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0 ) + { + bStatOK = TRUE; + if( VSI_ISDIR( sStat.st_mode ) ) + bIsDirectory = TRUE; + } +#ifdef HAVE_READLINK + else if ( !bHasRetried && strncmp(pszFilename, "/vsi", strlen("/vsi")) != 0 ) + { + /* If someone creates a file with "ln -sf /vsicurl/http://download.osgeo.org/gdal/data/gtiff/utm.tif my_remote_utm.tif" */ + /* we will be able to open it by passing my_remote_utm.tif */ + /* This helps a lot for GDAL based readers that only provide file explorers to open datasets */ + char szPointerFilename[2048]; + int nBytes = readlink(pszFilename, szPointerFilename, sizeof(szPointerFilename)); + if (nBytes != -1) + { + szPointerFilename[MIN(nBytes, (int)sizeof(szPointerFilename)-1)] = 0; + CPLFree(pszFilename); + pszFilename = CPLStrdup(szPointerFilename); + papszSiblingsIn = NULL; + bHasRetried = TRUE; + goto retry; + } + } +#endif + } + +/* -------------------------------------------------------------------- */ +/* Capture sibling list either from passed in values, or by */ +/* scanning for them only if requested through GetSiblingFiles(). */ +/* -------------------------------------------------------------------- */ + if( papszSiblingsIn != NULL ) + { + papszSiblingFiles = CSLDuplicate( papszSiblingsIn ); + bHasGotSiblingFiles = TRUE; + } + else if( bStatOK && !bIsDirectory ) + { + const char* pszOptionVal = + CPLGetConfigOption( "GDAL_DISABLE_READDIR_ON_OPEN", "NO" ); + if (EQUAL(pszOptionVal, "EMPTY_DIR")) + { + papszSiblingFiles = CSLAddString( NULL, CPLGetFilename(pszFilename) ); + bHasGotSiblingFiles = TRUE; + } + else if( CSLTestBoolean(pszOptionVal) ) + { + /* skip reading the directory */ + papszSiblingFiles = NULL; + bHasGotSiblingFiles = TRUE; + } + else + { + /* will be lazy loaded */ + papszSiblingFiles = NULL; + bHasGotSiblingFiles = FALSE; + } + } + else + { + papszSiblingFiles = NULL; + bHasGotSiblingFiles = TRUE; + } +} + +/************************************************************************/ +/* ~GDALOpenInfo() */ +/************************************************************************/ + +GDALOpenInfo::~GDALOpenInfo() + +{ + VSIFree( pabyHeader ); + CPLFree( pszFilename ); + + if( fpL != NULL ) + VSIFCloseL( fpL ); + CSLDestroy( papszSiblingFiles ); +} + +/************************************************************************/ +/* GetSiblingFiles() */ +/************************************************************************/ + +char** GDALOpenInfo::GetSiblingFiles() +{ + if( bHasGotSiblingFiles ) + return papszSiblingFiles; + bHasGotSiblingFiles = TRUE; + + CPLString osDir = CPLGetDirname( pszFilename ); + papszSiblingFiles = VSIReadDir( osDir ); + + /* Small optimization to avoid unnecessary stat'ing from PAux or ENVI */ + /* drivers. The MBTiles driver needs no companion file. */ + if( papszSiblingFiles == NULL && + strncmp(pszFilename, "/vsicurl/", 9) == 0 && + EQUAL(CPLGetExtension( pszFilename ),"mbtiles") ) + { + papszSiblingFiles = CSLAddString( NULL, CPLGetFilename(pszFilename) ); + } + + return papszSiblingFiles; +} + + +/************************************************************************/ +/* TryToIngest() */ +/************************************************************************/ + +int GDALOpenInfo::TryToIngest(int nBytes) +{ + if( fpL == NULL ) + return FALSE; + if( nHeaderBytes < nHeaderBytesTried ) + return TRUE; + pabyHeader = (GByte*) CPLRealloc(pabyHeader, nBytes + 1); + memset(pabyHeader, 0, nBytes + 1); + VSIRewindL(fpL); + nHeaderBytesTried = nBytes; + nHeaderBytes = (int) VSIFReadL(pabyHeader, 1, nBytes, fpL); + VSIRewindL(fpL); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/gcore/gdaloverviewdataset.cpp b/bazaar/plugin/gdal/gcore/gdaloverviewdataset.cpp new file mode 100644 index 000000000..de93452b7 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdaloverviewdataset.cpp @@ -0,0 +1,570 @@ +/****************************************************************************** + * $Id: gdaloverviewdataset.cpp 29128 2015-05-03 13:21:49Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of a dataset overview warping class + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault, + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_proxy.h" +#include "gdal_mdreader.h" + +CPL_CVSID("$Id: gdaloverviewdataset.cpp 29128 2015-05-03 13:21:49Z rouault $"); + +/** In GDAL, GDALRasterBand::GetOverview() returns a stand-alone band, that + may have no parent dataset. This can be inconvenient in certain contexts, where + cross-band processing must be done, or when API expect a fully fledge dataset. + Furthermore even if overview band has a container dataset, that one often + fails to declare its projection, geotransform, etc... which make it somehow + useless. GDALOverviewDataset remedies to those deficiencies. +*/ + +class GDALOverviewBand; + +/* ******************************************************************** */ +/* GDALOverviewDataset */ +/* ******************************************************************** */ + +class GDALOverviewDataset : public GDALDataset +{ + private: + + friend class GDALOverviewBand; + + GDALDataset* poMainDS; + int bOwnDS; + + GDALDataset* poOvrDS; /* will be often NULL */ + int nOvrLevel; + int bThisLevelOnly; + + int nGCPCount; + GDAL_GCP *pasGCPList; + char **papszMD_RPC; + char **papszMD_GEOLOCATION; + + static void Rescale(char**& papszMD, const char* pszItem, + double dfRatio, double dfDefaultVal); + + protected: + virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int, + void *, int, int, GDALDataType, + int, int *, + GSpacing, GSpacing, GSpacing, + GDALRasterIOExtraArg* psExtraArg ); + + public: + GDALOverviewDataset(GDALDataset* poMainDS, int nOvrLevel, + int bThisLevelOnly, + int bOwnDS); + virtual ~GDALOverviewDataset(); + + virtual const char *GetProjectionRef(void); + virtual CPLErr GetGeoTransform( double * ); + + virtual int GetGCPCount(); + virtual const char *GetGCPProjection(); + virtual const GDAL_GCP *GetGCPs(); + + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + + virtual int CloseDependentDatasets(); +}; + +/* ******************************************************************** */ +/* GDALOverviewBand */ +/* ******************************************************************** */ + +class GDALOverviewBand : public GDALProxyRasterBand +{ + protected: + friend class GDALOverviewDataset; + + GDALRasterBand* poUnderlyingBand; + virtual GDALRasterBand* RefUnderlyingRasterBand(); + + public: + GDALOverviewBand(GDALOverviewDataset* poDS, int nBand); + virtual ~GDALOverviewBand(); + + virtual CPLErr FlushCache(); + + virtual int GetOverviewCount(); + virtual GDALRasterBand *GetOverview(int); +}; + +/************************************************************************/ +/* GDALCreateOverviewDataset() */ +/************************************************************************/ + +GDALDataset* GDALCreateOverviewDataset(GDALDataset* poMainDS, int nOvrLevel, + int bThisLevelOnly, int bOwnDS) +{ + /* Sanity checks */ + int nBands = poMainDS->GetRasterCount(); + if( nBands == 0 ) + return NULL; + + for(int i = 1; i<= nBands; i++ ) + { + if( poMainDS->GetRasterBand(i)->GetOverview(nOvrLevel) == NULL ) + { + return NULL; + } + if( poMainDS->GetRasterBand(i)->GetOverview(nOvrLevel)->GetXSize() != + poMainDS->GetRasterBand(1)->GetOverview(nOvrLevel)->GetXSize() || + poMainDS->GetRasterBand(i)->GetOverview(nOvrLevel)->GetYSize() != + poMainDS->GetRasterBand(1)->GetOverview(nOvrLevel)->GetYSize() ) + { + return NULL; + } + } + + return new GDALOverviewDataset(poMainDS, nOvrLevel, bThisLevelOnly, bOwnDS); +} + +/************************************************************************/ +/* GDALOverviewDataset() */ +/************************************************************************/ + +GDALOverviewDataset::GDALOverviewDataset(GDALDataset* poMainDS, + int nOvrLevel, + int bThisLevelOnly, + int bOwnDS) +{ + this->poMainDS = poMainDS; + this->nOvrLevel = nOvrLevel; + this->bOwnDS = bOwnDS; + this->bThisLevelOnly = bThisLevelOnly; + eAccess = poMainDS->GetAccess(); + nRasterXSize = poMainDS->GetRasterBand(1)->GetOverview(nOvrLevel)->GetXSize(); + nRasterYSize = poMainDS->GetRasterBand(1)->GetOverview(nOvrLevel)->GetYSize(); + poOvrDS = poMainDS->GetRasterBand(1)->GetOverview(nOvrLevel)->GetDataset(); + if( poOvrDS != NULL && poOvrDS == poMainDS ) + { + CPLDebug("GDAL", "Dataset of overview is the same as the main band. This is not expected"); + poOvrDS = NULL; + } + nBands = poMainDS->GetRasterCount(); + for(int i=0;iGetDriver() != NULL ) + { + poDriver = new GDALDriver(); + poDriver->SetDescription(poMainDS->GetDriver()->GetDescription()); + poDriver->SetMetadata(poMainDS->GetDriver()->GetMetadata()); + } + + SetDescription( poMainDS->GetDescription() ); + + CPLDebug( "GDAL", "GDALOverviewDataset(%s, this=%p) creation.", + poMainDS->GetDescription(), this ); + + papszOpenOptions = CSLDuplicate(poMainDS->GetOpenOptions()); + /* Add OVERVIEW_LEVEL if not called from GDALOpenEx() but directly */ + papszOpenOptions = CSLSetNameValue(papszOpenOptions, "OVERVIEW_LEVEL", + CPLSPrintf("%d", nOvrLevel)); +} + +/************************************************************************/ +/* ~GDALOverviewDataset() */ +/************************************************************************/ + +GDALOverviewDataset::~GDALOverviewDataset() +{ + FlushCache(); + + CloseDependentDatasets(); + + if( nGCPCount > 0 ) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + CSLDestroy(papszMD_RPC); + + CSLDestroy(papszMD_GEOLOCATION); + + delete poDriver; +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int GDALOverviewDataset::CloseDependentDatasets() +{ + int bRet = FALSE; + + if( bOwnDS ) + { + for(int i=0;ipoUnderlyingBand = NULL; + } + GDALClose( poMainDS ); + poMainDS = NULL; + bOwnDS = FALSE; + bRet = TRUE; + } + + return bRet; +} + +/************************************************************************/ +/* IRasterIO() */ +/* */ +/* The default implementation of IRasterIO() is to pass the */ +/* request off to each band objects rasterio methods with */ +/* appropriate arguments. */ +/************************************************************************/ + +CPLErr GDALOverviewDataset::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg) + +{ + int iBandIndex; + CPLErr eErr = CE_None; + + /* In case the overview bands are really linked to a dataset, then issue */ + /* the request to that dataset */ + if( poOvrDS != NULL ) + { + return poOvrDS->RasterIO( + eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, + psExtraArg); + } + + GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress; + void *pProgressDataGlobal = psExtraArg->pProgressData; + + for( iBandIndex = 0; + iBandIndex < nBandCount && eErr == CE_None; + iBandIndex++ ) + { + GDALOverviewBand *poBand = (GDALOverviewBand*) GetRasterBand(panBandMap[iBandIndex]); + GByte *pabyBandData; + + if (poBand == NULL) + { + eErr = CE_Failure; + break; + } + + pabyBandData = ((GByte *) pData) + iBandIndex * nBandSpace; + + psExtraArg->pfnProgress = GDALScaledProgress; + psExtraArg->pProgressData = + GDALCreateScaledProgress( 1.0 * iBandIndex / nBandCount, + 1.0 * (iBandIndex + 1) / nBandCount, + pfnProgressGlobal, + pProgressDataGlobal ); + + eErr = poBand->IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + (void *) pabyBandData, nBufXSize, nBufYSize, + eBufType, nPixelSpace, nLineSpace, psExtraArg ); + + GDALDestroyScaledProgress( psExtraArg->pProgressData ); + } + + psExtraArg->pfnProgress = pfnProgressGlobal; + psExtraArg->pProgressData = pProgressDataGlobal; + + return eErr; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GDALOverviewDataset::GetProjectionRef() + +{ + return poMainDS->GetProjectionRef(); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GDALOverviewDataset::GetGeoTransform( double * padfTransform ) + +{ + double adfGeoTransform[6]; + if( poMainDS->GetGeoTransform(adfGeoTransform) == CE_None ) + { + if( adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0 ) + { + adfGeoTransform[1] *= (double)poMainDS->GetRasterXSize() / nRasterXSize; + adfGeoTransform[5] *= (double)poMainDS->GetRasterYSize() / nRasterYSize; + } + else + { + /* If the x and y ratios are not equal, then we cannot really */ + /* compute a geotransform */ + double dfRatio = (double)poMainDS->GetRasterXSize() / nRasterXSize; + adfGeoTransform[1] *= dfRatio; + adfGeoTransform[2] *= dfRatio; + adfGeoTransform[4] *= dfRatio; + adfGeoTransform[5] *= dfRatio; + } + memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 ); + + return CE_None; + } + else + return CE_Failure; +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int GDALOverviewDataset::GetGCPCount() + +{ + return poMainDS->GetGCPCount(); +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *GDALOverviewDataset::GetGCPProjection() + +{ + return poMainDS->GetGCPProjection(); +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *GDALOverviewDataset::GetGCPs() + +{ + if( pasGCPList != NULL ) + return pasGCPList; + + const GDAL_GCP* pasGCPsMain = poMainDS->GetGCPs(); + if( pasGCPsMain == NULL ) + return NULL; + nGCPCount = poMainDS->GetGCPCount(); + + pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPsMain ); + for(int i = 0; i < nGCPCount; i++) + { + pasGCPList[i].dfGCPPixel *= (double)nRasterXSize / poMainDS->GetRasterXSize(); + pasGCPList[i].dfGCPLine *= (double)nRasterYSize / poMainDS->GetRasterYSize(); + } + return pasGCPList; +} + +/************************************************************************/ +/* Rescale() */ +/************************************************************************/ + +void GDALOverviewDataset::Rescale(char**& papszMD, const char* pszItem, + double dfRatio, double dfDefaultVal) +{ + double dfVal = CPLAtofM(CSLFetchNameValueDef(papszMD, pszItem, + CPLSPrintf("%.18g", dfDefaultVal))); + dfVal *= dfRatio; + papszMD = CSLSetNameValue(papszMD, pszItem, CPLSPrintf("%.18g", dfVal)); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GDALOverviewDataset::GetMetadata( const char * pszDomain ) +{ + char** papszMD; + if (poOvrDS != NULL) + { + papszMD = poOvrDS->GetMetadata(pszDomain); + if( papszMD != NULL ) + return papszMD; + } + + papszMD = poMainDS->GetMetadata(pszDomain); + + /* We may need to rescale some values from the RPC metadata domain */ + if( pszDomain != NULL && EQUAL(pszDomain, MD_DOMAIN_RPC) && papszMD != NULL ) + { + if( papszMD_RPC ) + return papszMD_RPC; + papszMD_RPC = CSLDuplicate(papszMD); + + Rescale(papszMD_RPC, RPC_LINE_OFF, + (double)nRasterYSize / poMainDS->GetRasterYSize(), 0.0); + Rescale(papszMD_RPC, RPC_LINE_SCALE, + (double)nRasterYSize / poMainDS->GetRasterYSize(), 1.0); + Rescale(papszMD_RPC, RPC_SAMP_OFF, + (double)nRasterXSize / poMainDS->GetRasterXSize(), 0.0); + Rescale(papszMD_RPC, RPC_SAMP_SCALE, + (double)nRasterXSize / poMainDS->GetRasterXSize(), 1.0); + + papszMD = papszMD_RPC; + } + + /* We may need to rescale some values from the GEOLOCATION metadata domain */ + if( pszDomain != NULL && EQUAL(pszDomain, "GEOLOCATION") && papszMD != NULL ) + { + if( papszMD_GEOLOCATION ) + return papszMD_GEOLOCATION; + papszMD_GEOLOCATION = CSLDuplicate(papszMD); + + Rescale(papszMD_GEOLOCATION, "PIXEL_OFFSET", + (double)poMainDS->GetRasterXSize() / nRasterXSize, 0.0); + Rescale(papszMD_GEOLOCATION, "LINE_OFFSET", + (double)poMainDS->GetRasterYSize() / nRasterYSize, 0.0); + + Rescale(papszMD_GEOLOCATION, "PIXEL_STEP", + (double)nRasterXSize / poMainDS->GetRasterXSize(), 1.0); + Rescale(papszMD_GEOLOCATION, "LINE_STEP", + (double)nRasterYSize / poMainDS->GetRasterYSize(), 1.0); + + papszMD = papszMD_GEOLOCATION; + } + + return papszMD; +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *GDALOverviewDataset::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if (poOvrDS != NULL) + { + const char* pszValue = poOvrDS->GetMetadataItem(pszName, pszDomain); + if( pszValue != NULL ) + return pszValue; + } + + if( pszDomain != NULL && (EQUAL(pszDomain, "RPC") || + EQUAL(pszDomain, "GEOLOCATION")) ) + { + char** papszMD = GetMetadata(pszDomain); + return CSLFetchNameValue(papszMD, pszName); + } + + return poMainDS->GetMetadataItem(pszName, pszDomain); +} + +/************************************************************************/ +/* GDALOverviewBand() */ +/************************************************************************/ + +GDALOverviewBand::GDALOverviewBand(GDALOverviewDataset* poDS, int nBand) +{ + this->poDS = poDS; + this->nBand = nBand; + poUnderlyingBand = poDS->poMainDS->GetRasterBand(nBand)->GetOverview(poDS->nOvrLevel); + nRasterXSize = poDS->nRasterXSize; + nRasterYSize = poDS->nRasterYSize; + eDataType = poUnderlyingBand->GetRasterDataType(); + poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize); +} + +/************************************************************************/ +/* ~GDALOverviewBand() */ +/************************************************************************/ + +GDALOverviewBand::~GDALOverviewBand() +{ + FlushCache(); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +CPLErr GDALOverviewBand::FlushCache() +{ + if( ((GDALOverviewDataset*)poDS)->poMainDS ) + return poUnderlyingBand->FlushCache(); + return CE_None; +} + +/************************************************************************/ +/* RefUnderlyingRasterBand() */ +/************************************************************************/ + +GDALRasterBand* GDALOverviewBand::RefUnderlyingRasterBand() +{ + if( ((GDALOverviewDataset*)poDS)->poMainDS ) + return poUnderlyingBand; + else + return NULL; +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int GDALOverviewBand::GetOverviewCount() +{ + GDALOverviewDataset* poOvrDS = (GDALOverviewDataset*)poDS; + if( poOvrDS->bThisLevelOnly ) + return 0; + GDALDataset* poMainDS = poOvrDS->poMainDS; + return poMainDS->GetRasterBand(nBand)->GetOverviewCount() - poOvrDS->nOvrLevel - 1; +} + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +GDALRasterBand *GDALOverviewBand::GetOverview(int iOvr) +{ + if( iOvr < 0 || iOvr >= GetOverviewCount() ) + return NULL; + GDALOverviewDataset* poOvrDS = (GDALOverviewDataset*)poDS; + GDALDataset* poMainDS = poOvrDS->poMainDS; + return poMainDS->GetRasterBand(nBand)->GetOverview(iOvr + poOvrDS->nOvrLevel + 1); +} diff --git a/bazaar/plugin/gdal/gcore/gdalpamdataset.cpp b/bazaar/plugin/gdal/gcore/gdalpamdataset.cpp new file mode 100644 index 000000000..494507077 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalpamdataset.cpp @@ -0,0 +1,1491 @@ +/****************************************************************************** + * $Id: gdalpamdataset.cpp 29038 2015-04-28 09:03:36Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALPamDataset, a dataset base class that + * knows how to persist auxiliary metadata into a support XML file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" + +CPL_CVSID("$Id: gdalpamdataset.cpp 29038 2015-04-28 09:03:36Z rouault $"); + +/************************************************************************/ +/* GDALPamDataset() */ +/************************************************************************/ + +/** + * \class GDALPamDataset "gdal_pam.h" + * + * A subclass of GDALDataset which introduces the ability to save and + * restore auxiliary information (coordinate system, gcps, metadata, + * etc) not supported by a file format via an "auxiliary metadata" file + * with the .aux.xml extension. + * + *

    Enabling PAM

    + * + * PAM support can be enabled (resp. disabled) in GDAL by setting the GDAL_PAM_ENABLED + * configuration option (via CPLSetConfigOption(), or the environment) to + * the value of YES (resp. NO). Note: The default value is build dependant and defaults + * to YES in Windows and Unix builds. + * + *

    PAM Proxy Files

    + * + * In order to be able to record auxiliary information about files on + * read-only media such as CDROMs or in directories where the user does not + * have write permissions, it is possible to enable the "PAM Proxy Database". + * When enabled the .aux.xml files are kept in a different directory, writable + * by the user. Overviews will also be stored in the PAM proxy directory. + * + * To enable this, set the GDAL_PAM_PROXY_DIR configuration option to be + * the name of the directory where the proxies should be kept. The configuration + * option must be set *before* the first access to PAM, because its value is cached + * for later access. + * + *

    Adding PAM to Drivers

    + * + * Drivers for physical file formats that wish to support persistent auxiliary + * metadata in addition to that for the format itself should derive their + * dataset class from GDALPamDataset instead of directly from GDALDataset. + * The raster band classes should also be derived from GDALPamRasterBand. + * + * They should also call something like this near the end of the Open() + * method: + * + * \code + * poDS->SetDescription( poOpenInfo->pszFilename ); + * poDS->TryLoadXML(); + * \endcode + * + * The SetDescription() is necessary so that the dataset will have a valid + * filename set as the description before TryLoadXML() is called. TryLoadXML() + * will look for an .aux.xml file with the same basename as the dataset and + * in the same directory. If found the contents will be loaded and kept + * track of in the GDALPamDataset and GDALPamRasterBand objects. When a + * call like GetProjectionRef() is not implemented by the format specific + * class, it will fall through to the PAM implementation which will return + * information if it was in the .aux.xml file. + * + * Drivers should also try to call the GDALPamDataset/GDALPamRasterBand + * methods as a fallback if their implementation does not find information. + * This allows using the .aux.xml for variations that can't be stored in + * the format. For instance, the GeoTIFF driver GetProjectionRef() looks + * like this: + * + * \code + * if( EQUAL(pszProjection,"") ) + * return GDALPamDataset::GetProjectionRef(); + * else + * return( pszProjection ); + * \endcode + * + * So if the geotiff header is missing, the .aux.xml file will be + * consulted. + * + * Similarly, if SetProjection() were called with a coordinate system + * not supported by GeoTIFF, the SetProjection() method should pass it on + * to the GDALPamDataset::SetProjection() method after issuing a warning + * that the information can't be represented within the file itself. + * + * Drivers for subdataset based formats will also need to declare the + * name of the physical file they are related to, and the name of their + * subdataset before calling TryLoadXML(). + * + * \code + * poDS->SetDescription( poOpenInfo->pszFilename ); + * poDS->SetPhysicalFilename( poDS->pszFilename ); + * poDS->SetSubdatasetName( osSubdatasetName ); + * + * poDS->TryLoadXML(); + * \endcode + */ + +GDALPamDataset::GDALPamDataset() + +{ + nPamFlags = 0; + psPam = NULL; + SetMOFlags( GetMOFlags() | GMO_PAM_CLASS ); +} + +/************************************************************************/ +/* ~GDALPamDataset() */ +/************************************************************************/ + +GDALPamDataset::~GDALPamDataset() + +{ + if( nPamFlags & GPF_DIRTY ) + { + CPLDebug( "GDALPamDataset", "In destructor with dirty metadata." ); + FlushCache(); + } + + PamClear(); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void GDALPamDataset::FlushCache() + +{ + GDALDataset::FlushCache(); + if( nPamFlags & GPF_DIRTY ) + TrySaveXML(); +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *GDALPamDataset::SerializeToXML( const char *pszUnused ) + +{ + CPLString oFmt; + + if( psPam == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Setup root node and attributes. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psDSTree; + + psDSTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMDataset" ); + +/* -------------------------------------------------------------------- */ +/* SRS */ +/* -------------------------------------------------------------------- */ + if( psPam->pszProjection != NULL && strlen(psPam->pszProjection) > 0 ) + CPLSetXMLValue( psDSTree, "SRS", psPam->pszProjection ); + +/* -------------------------------------------------------------------- */ +/* GeoTransform. */ +/* -------------------------------------------------------------------- */ + if( psPam->bHaveGeoTransform ) + { + CPLSetXMLValue( psDSTree, "GeoTransform", + oFmt.Printf( "%24.16e,%24.16e,%24.16e,%24.16e,%24.16e,%24.16e", + psPam->adfGeoTransform[0], + psPam->adfGeoTransform[1], + psPam->adfGeoTransform[2], + psPam->adfGeoTransform[3], + psPam->adfGeoTransform[4], + psPam->adfGeoTransform[5] ) ); + } + +/* -------------------------------------------------------------------- */ +/* Metadata. */ +/* -------------------------------------------------------------------- */ + if( psPam->bHasMetadata ) + { + CPLXMLNode *psMD; + + psMD = oMDMD.Serialize(); + if( psMD != NULL ) + { + CPLAddXMLChild( psDSTree, psMD ); + } + } + +/* -------------------------------------------------------------------- */ +/* GCPs */ +/* -------------------------------------------------------------------- */ + if( psPam->nGCPCount > 0 ) + { + GDALSerializeGCPListToXML( psDSTree, + psPam->pasGCPList, + psPam->nGCPCount, + psPam->pszGCPProjection ); + } + +/* -------------------------------------------------------------------- */ +/* Process bands. */ +/* -------------------------------------------------------------------- */ + int iBand; + + for( iBand = 0; iBand < GetRasterCount(); iBand++ ) + { + CPLXMLNode *psBandTree; + + GDALPamRasterBand *poBand = (GDALPamRasterBand *) + GetRasterBand(iBand+1); + + if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) ) + continue; + + psBandTree = poBand->SerializeToXML( pszUnused ); + + if( psBandTree != NULL ) + CPLAddXMLChild( psDSTree, psBandTree ); + } + +/* -------------------------------------------------------------------- */ +/* We don't want to return anything if we had no metadata to */ +/* attach. */ +/* -------------------------------------------------------------------- */ + if( psDSTree->psChild == NULL ) + { + CPLDestroyXMLNode( psDSTree ); + psDSTree = NULL; + } + + return psDSTree; +} + +/************************************************************************/ +/* PamInitialize() */ +/************************************************************************/ + +void GDALPamDataset::PamInitialize() + +{ +#ifdef PAM_ENABLED + static const char *pszPamDefault = "YES"; +#else + static const char *pszPamDefault = "NO"; +#endif + + if( psPam || (nPamFlags & GPF_DISABLED) ) + return; + + if( !CSLTestBoolean( CPLGetConfigOption( "GDAL_PAM_ENABLED", + pszPamDefault ) ) ) + { + nPamFlags |= GPF_DISABLED; + return; + } + + /* ERO 2011/04/13 : GPF_AUXMODE seems to be unimplemented */ + if( EQUAL( CPLGetConfigOption( "GDAL_PAM_MODE", "PAM" ), "AUX") ) + nPamFlags |= GPF_AUXMODE; + + psPam = new GDALDatasetPamInfo; + psPam->pszPamFilename = NULL; + psPam->pszProjection = NULL; + psPam->bHaveGeoTransform = FALSE; + psPam->nGCPCount = 0; + psPam->pasGCPList = NULL; + psPam->pszGCPProjection = NULL; + psPam->bHasMetadata = FALSE; + + int iBand; + + for( iBand = 0; iBand < GetRasterCount(); iBand++ ) + { + GDALPamRasterBand *poBand = (GDALPamRasterBand *) + GetRasterBand(iBand+1); + + if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) ) + continue; + + poBand->PamInitialize(); + } +} + +/************************************************************************/ +/* PamClear() */ +/************************************************************************/ + +void GDALPamDataset::PamClear() + +{ + if( psPam ) + { + CPLFree( psPam->pszPamFilename ); + CPLFree( psPam->pszProjection ); + CPLFree( psPam->pszGCPProjection ); + if( psPam->nGCPCount > 0 ) + { + GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList ); + CPLFree( psPam->pasGCPList ); + } + + delete psPam; + psPam = NULL; + } +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr GDALPamDataset::XMLInit( CPLXMLNode *psTree, const char *pszUnused ) + +{ +/* -------------------------------------------------------------------- */ +/* Check for an SRS node. */ +/* -------------------------------------------------------------------- */ + if( strlen(CPLGetXMLValue(psTree, "SRS", "")) > 0 ) + { + OGRSpatialReference oSRS; + + CPLFree( psPam->pszProjection ); + psPam->pszProjection = NULL; + + if( oSRS.SetFromUserInput( CPLGetXMLValue(psTree, "SRS", "") ) + == OGRERR_NONE ) + oSRS.exportToWkt( &(psPam->pszProjection) ); + } + +/* -------------------------------------------------------------------- */ +/* Check for a GeoTransform node. */ +/* -------------------------------------------------------------------- */ + if( strlen(CPLGetXMLValue(psTree, "GeoTransform", "")) > 0 ) + { + const char *pszGT = CPLGetXMLValue(psTree, "GeoTransform", ""); + char **papszTokens; + + papszTokens = CSLTokenizeStringComplex( pszGT, ",", FALSE, FALSE ); + if( CSLCount(papszTokens) != 6 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "GeoTransform node does not have expected six values."); + } + else + { + for( int iTA = 0; iTA < 6; iTA++ ) + psPam->adfGeoTransform[iTA] = CPLAtof(papszTokens[iTA]); + psPam->bHaveGeoTransform = TRUE; + } + + CSLDestroy( papszTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Check for GCPs. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psGCPList = CPLGetXMLNode( psTree, "GCPList" ); + + if( psGCPList != NULL ) + { + CPLFree( psPam->pszGCPProjection ); + psPam->pszGCPProjection = NULL; + + // Make sure any previous GCPs, perhaps from an .aux file, are cleared + // if we have new ones. + if( psPam->nGCPCount > 0 ) + { + GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList ); + CPLFree( psPam->pasGCPList ); + psPam->nGCPCount = 0; + psPam->pasGCPList = 0; + } + + GDALDeserializeGCPListFromXML( psGCPList, + &(psPam->pasGCPList), + &(psPam->nGCPCount), + &(psPam->pszGCPProjection) ); + } + +/* -------------------------------------------------------------------- */ +/* Apply any dataset level metadata. */ +/* -------------------------------------------------------------------- */ + oMDMD.XMLInit( psTree, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Try loading ESRI xml encoded projection */ +/* -------------------------------------------------------------------- */ + if (psPam->pszProjection == NULL) + { + char** papszXML = oMDMD.GetMetadata( "xml:ESRI" ); + if (CSLCount(papszXML) == 1) + { + CPLXMLNode *psValueAsXML = CPLParseXMLString( papszXML[0] ); + if (psValueAsXML) + { + const char* pszESRI_WKT = CPLGetXMLValue(psValueAsXML, + "=GeodataXform.SpatialReference.WKT", NULL); + if (pszESRI_WKT) + { + OGRSpatialReference* poSRS = new OGRSpatialReference(NULL); + char* pszTmp = (char*)pszESRI_WKT; + if (poSRS->importFromWkt(&pszTmp) == OGRERR_NONE && + poSRS->morphFromESRI() == OGRERR_NONE) + { + char* pszWKT = NULL; + if (poSRS->exportToWkt(&pszWKT) == OGRERR_NONE) + { + psPam->pszProjection = CPLStrdup(pszWKT); + } + CPLFree(pszWKT); + } + delete poSRS; + } + CPLDestroyXMLNode(psValueAsXML); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Process bands. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psBandTree; + + for( psBandTree = psTree->psChild; + psBandTree != NULL; psBandTree = psBandTree->psNext ) + { + if( psBandTree->eType != CXT_Element + || !EQUAL(psBandTree->pszValue,"PAMRasterBand") ) + continue; + + int nBand = atoi(CPLGetXMLValue( psBandTree, "band", "0")); + + if( nBand < 1 || nBand > GetRasterCount() ) + continue; + + GDALPamRasterBand *poBand = (GDALPamRasterBand *) + GetRasterBand(nBand); + + if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) ) + continue; + + poBand->XMLInit( psBandTree, pszUnused ); + } + +/* -------------------------------------------------------------------- */ +/* Clear dirty flag. */ +/* -------------------------------------------------------------------- */ + nPamFlags &= ~GPF_DIRTY; + + return CE_None; +} + +/************************************************************************/ +/* SetPhysicalFilename() */ +/************************************************************************/ + +void GDALPamDataset::SetPhysicalFilename( const char *pszFilename ) + +{ + PamInitialize(); + + if( psPam ) + psPam->osPhysicalFilename = pszFilename; +} + +/************************************************************************/ +/* GetPhysicalFilename() */ +/************************************************************************/ + +const char *GDALPamDataset::GetPhysicalFilename() + +{ + PamInitialize(); + + if( psPam ) + return psPam->osPhysicalFilename; + else + return ""; +} + +/************************************************************************/ +/* SetSubdatasetName() */ +/************************************************************************/ + +void GDALPamDataset::SetSubdatasetName( const char *pszSubdataset ) + +{ + PamInitialize(); + + if( psPam ) + psPam->osSubdatasetName = pszSubdataset; +} + +/************************************************************************/ +/* GetSubdatasetName() */ +/************************************************************************/ + +const char *GDALPamDataset::GetSubdatasetName() + +{ + PamInitialize(); + + if( psPam ) + return psPam->osSubdatasetName; + else + return ""; +} + +/************************************************************************/ +/* BuildPamFilename() */ +/************************************************************************/ + +const char *GDALPamDataset::BuildPamFilename() + +{ + if( psPam == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* What is the name of the physical file we are referencing? */ +/* We allow an override via the psPam->pszPhysicalFile item. */ +/* -------------------------------------------------------------------- */ + if( psPam->pszPamFilename != NULL ) + return psPam->pszPamFilename; + + const char *pszPhysicalFile = psPam->osPhysicalFilename; + + if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL ) + pszPhysicalFile = GetDescription(); + + if( strlen(pszPhysicalFile) == 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Try a proxy lookup, otherwise just add .aux.xml. */ +/* -------------------------------------------------------------------- */ + const char *pszProxyPam = PamGetProxy( pszPhysicalFile ); + if( pszProxyPam != NULL ) + psPam->pszPamFilename = CPLStrdup(pszProxyPam); + else + { + if( !GDALCanFileAcceptSidecarFile(pszPhysicalFile) ) + return NULL; + psPam->pszPamFilename = (char*) CPLMalloc(strlen(pszPhysicalFile)+10); + strcpy( psPam->pszPamFilename, pszPhysicalFile ); + strcat( psPam->pszPamFilename, ".aux.xml" ); + } + + return psPam->pszPamFilename; +} + +/************************************************************************/ +/* IsPamFilenameAPotentialSiblingFile() */ +/************************************************************************/ + +int GDALPamDataset::IsPamFilenameAPotentialSiblingFile() +{ + if (psPam == NULL) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Determine if the PAM filename is a .aux.xml file next to the */ +/* physical file, or if it comes from the ProxyDB */ +/* -------------------------------------------------------------------- */ + const char *pszPhysicalFile = psPam->osPhysicalFilename; + + if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL ) + pszPhysicalFile = GetDescription(); + + int nLenPhysicalFile = strlen(pszPhysicalFile); + int bIsSiblingPamFile = strncmp(psPam->pszPamFilename, pszPhysicalFile, + nLenPhysicalFile) == 0 && + strcmp(psPam->pszPamFilename + nLenPhysicalFile, + ".aux.xml") == 0; + + return bIsSiblingPamFile; +} + +/************************************************************************/ +/* TryLoadXML() */ +/************************************************************************/ + +CPLErr GDALPamDataset::TryLoadXML(char **papszSiblingFiles) + +{ + CPLXMLNode *psTree = NULL; + + PamInitialize(); + +/* -------------------------------------------------------------------- */ +/* Clear dirty flag. Generally when we get to this point is */ +/* from a call at the end of the Open() method, and some calls */ +/* may have already marked the PAM info as dirty (for instance */ +/* setting metadata), but really everything to this point is */ +/* reproducable, and so the PAM info shouldn't really be */ +/* thought of as dirty. */ +/* -------------------------------------------------------------------- */ + nPamFlags &= ~GPF_DIRTY; + +/* -------------------------------------------------------------------- */ +/* Try reading the file. */ +/* -------------------------------------------------------------------- */ + if( !BuildPamFilename() ) + return CE_None; + + VSIStatBufL sStatBuf; + +/* -------------------------------------------------------------------- */ +/* In case the PAM filename is a .aux.xml file next to the */ +/* physical file and we have a siblings list, then we can skip */ +/* stat'ing the filesystem. */ +/* -------------------------------------------------------------------- */ + if (papszSiblingFiles != NULL && IsPamFilenameAPotentialSiblingFile()) + { + int iSibling = CSLFindString( papszSiblingFiles, + CPLGetFilename(psPam->pszPamFilename) ); + if( iSibling >= 0 ) + { + CPLErrorReset(); + CPLPushErrorHandler( CPLQuietErrorHandler ); + psTree = CPLParseXMLFile( psPam->pszPamFilename ); + CPLPopErrorHandler(); + } + } + else + if( VSIStatExL( psPam->pszPamFilename, &sStatBuf, + VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0 + && VSI_ISREG( sStatBuf.st_mode ) ) + { + CPLErrorReset(); + CPLPushErrorHandler( CPLQuietErrorHandler ); + psTree = CPLParseXMLFile( psPam->pszPamFilename ); + CPLPopErrorHandler(); + } + +/* -------------------------------------------------------------------- */ +/* If we are looking for a subdataset, search for it's subtree */ +/* now. */ +/* -------------------------------------------------------------------- */ + if( psTree && psPam->osSubdatasetName.size() ) + { + CPLXMLNode *psSubTree; + + for( psSubTree = psTree->psChild; + psSubTree != NULL; + psSubTree = psSubTree->psNext ) + { + if( psSubTree->eType != CXT_Element + || !EQUAL(psSubTree->pszValue,"Subdataset") ) + continue; + + if( !EQUAL(CPLGetXMLValue( psSubTree, "name", "" ), + psPam->osSubdatasetName) ) + continue; + + psSubTree = CPLGetXMLNode( psSubTree, "PAMDataset" ); + break; + } + + if( psSubTree != NULL ) + psSubTree = CPLCloneXMLTree( psSubTree ); + + CPLDestroyXMLNode( psTree ); + psTree = psSubTree; + } + +/* -------------------------------------------------------------------- */ +/* If we fail, try .aux. */ +/* -------------------------------------------------------------------- */ + if( psTree == NULL ) + return TryLoadAux(papszSiblingFiles); + +/* -------------------------------------------------------------------- */ +/* Initialize ourselves from this XML tree. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + CPLString osVRTPath(CPLGetPath(psPam->pszPamFilename)); + eErr = XMLInit( psTree, osVRTPath ); + + CPLDestroyXMLNode( psTree ); + + if( eErr != CE_None ) + PamClear(); + + return eErr; +} + +/************************************************************************/ +/* TrySaveXML() */ +/************************************************************************/ + +CPLErr GDALPamDataset::TrySaveXML() + +{ + CPLXMLNode *psTree; + CPLErr eErr = CE_None; + + nPamFlags &= ~GPF_DIRTY; + + if( psPam == NULL || (nPamFlags & GPF_NOSAVE) ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Make sure we know the filename we want to store in. */ +/* -------------------------------------------------------------------- */ + if( !BuildPamFilename() ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Build the XML representation of the auxiliary metadata. */ +/* -------------------------------------------------------------------- */ + psTree = SerializeToXML( NULL ); + + if( psTree == NULL ) + { + /* If we have unset all metadata, we have to delete the PAM file */ + CPLPushErrorHandler( CPLQuietErrorHandler ); + VSIUnlink(psPam->pszPamFilename); + CPLPopErrorHandler(); + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* If we are working with a subdataset, we need to integrate */ +/* the subdataset tree within the whole existing pam tree, */ +/* after removing any old version of the same subdataset. */ +/* -------------------------------------------------------------------- */ + if( psPam->osSubdatasetName.size() != 0 ) + { + CPLXMLNode *psOldTree, *psSubTree; + + CPLErrorReset(); + CPLPushErrorHandler( CPLQuietErrorHandler ); + psOldTree = CPLParseXMLFile( psPam->pszPamFilename ); + CPLPopErrorHandler(); + + if( psOldTree == NULL ) + psOldTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMDataset" ); + + for( psSubTree = psOldTree->psChild; + psSubTree != NULL; + psSubTree = psSubTree->psNext ) + { + if( psSubTree->eType != CXT_Element + || !EQUAL(psSubTree->pszValue,"Subdataset") ) + continue; + + if( !EQUAL(CPLGetXMLValue( psSubTree, "name", "" ), + psPam->osSubdatasetName) ) + continue; + + break; + } + + if( psSubTree == NULL ) + { + psSubTree = CPLCreateXMLNode( psOldTree, CXT_Element, + "Subdataset" ); + CPLCreateXMLNode( + CPLCreateXMLNode( psSubTree, CXT_Attribute, "name" ), + CXT_Text, psPam->osSubdatasetName ); + } + + CPLXMLNode *psOldPamDataset = CPLGetXMLNode( psSubTree, "PAMDataset"); + if( psOldPamDataset != NULL ) + { + CPLRemoveXMLChild( psSubTree, psOldPamDataset ); + CPLDestroyXMLNode( psOldPamDataset ); + } + + CPLAddXMLChild( psSubTree, psTree ); + psTree = psOldTree; + } + +/* -------------------------------------------------------------------- */ +/* Try saving the auxiliary metadata. */ +/* -------------------------------------------------------------------- */ + int bSaved; + + CPLPushErrorHandler( CPLQuietErrorHandler ); + bSaved = CPLSerializeXMLTreeToFile( psTree, psPam->pszPamFilename ); + CPLPopErrorHandler(); + +/* -------------------------------------------------------------------- */ +/* If it fails, check if we have a proxy directory for auxiliary */ +/* metadata to be stored in, and try to save there. */ +/* -------------------------------------------------------------------- */ + if( bSaved ) + eErr = CE_None; + else + { + const char *pszNewPam; + const char *pszBasename = GetDescription(); + + if( psPam && psPam->osPhysicalFilename.length() > 0 ) + pszBasename = psPam->osPhysicalFilename; + + if( PamGetProxy(pszBasename) == NULL + && ((pszNewPam = PamAllocateProxy(pszBasename)) != NULL)) + { + CPLErrorReset(); + CPLFree( psPam->pszPamFilename ); + psPam->pszPamFilename = CPLStrdup(pszNewPam); + eErr = TrySaveXML(); + } + /* No way we can save into a /vsicurl resource */ + else if( strncmp(psPam->pszPamFilename, "/vsicurl", strlen("/vsicurl")) != 0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to save auxiliary information in %s.", + psPam->pszPamFilename ); + eErr = CE_Warning; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLDestroyXMLNode( psTree ); + + return eErr; +} + +/************************************************************************/ +/* CloneInfo() */ +/************************************************************************/ + +CPLErr GDALPamDataset::CloneInfo( GDALDataset *poSrcDS, int nCloneFlags ) + +{ + int bOnlyIfMissing = nCloneFlags & GCIF_ONLY_IF_MISSING; + int nSavedMOFlags = GetMOFlags(); + + PamInitialize(); + +/* -------------------------------------------------------------------- */ +/* Suppress NotImplemented error messages - mainly needed if PAM */ +/* disabled. */ +/* -------------------------------------------------------------------- */ + SetMOFlags( nSavedMOFlags | GMO_IGNORE_UNIMPLEMENTED ); + +/* -------------------------------------------------------------------- */ +/* GeoTransform */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_GEOTRANSFORM ) + { + double adfGeoTransform[6]; + + if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None ) + { + double adfOldGT[6]; + + if( !bOnlyIfMissing || GetGeoTransform( adfOldGT ) != CE_None ) + SetGeoTransform( adfGeoTransform ); + } + } + +/* -------------------------------------------------------------------- */ +/* Projection */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_PROJECTION ) + { + const char *pszWKT = poSrcDS->GetProjectionRef(); + + if( pszWKT != NULL && strlen(pszWKT) > 0 ) + { + if( !bOnlyIfMissing + || GetProjectionRef() == NULL + || strlen(GetProjectionRef()) == 0 ) + SetProjection( pszWKT ); + } + } + +/* -------------------------------------------------------------------- */ +/* GCPs */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_GCPS ) + { + if( poSrcDS->GetGCPCount() > 0 ) + { + if( !bOnlyIfMissing || GetGCPCount() == 0 ) + { + SetGCPs( poSrcDS->GetGCPCount(), + poSrcDS->GetGCPs(), + poSrcDS->GetGCPProjection() ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Metadata */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_METADATA ) + { + if( poSrcDS->GetMetadata() != NULL ) + { + if( !bOnlyIfMissing + || CSLCount(GetMetadata()) != CSLCount(poSrcDS->GetMetadata()) ) + { + SetMetadata( poSrcDS->GetMetadata() ); + } + } + if( poSrcDS->GetMetadata("RPC") != NULL ) + { + if( !bOnlyIfMissing + || CSLCount(GetMetadata("RPC")) + != CSLCount(poSrcDS->GetMetadata("RPC")) ) + { + SetMetadata( poSrcDS->GetMetadata("RPC"), "RPC" ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Process bands. */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_PROCESS_BANDS ) + { + int iBand; + + for( iBand = 0; iBand < GetRasterCount(); iBand++ ) + { + GDALPamRasterBand *poBand = (GDALPamRasterBand *) + GetRasterBand(iBand+1); + + if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) ) + continue; + + if( poSrcDS->GetRasterCount() >= iBand+1 ) + poBand->CloneInfo( poSrcDS->GetRasterBand(iBand+1), + nCloneFlags ); + else + CPLDebug( "GDALPamDataset", "Skipping CloneInfo for band not in source, this is a bit unusual!" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Copy masks. These are really copied at a lower level using */ +/* GDALDefaultOverviews, for formats with no native mask */ +/* support but this is a convenient central point to put this */ +/* for most drivers. */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_MASK ) + { + GDALDriver::DefaultCopyMasks( poSrcDS, this, FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Restore MO flags. */ +/* -------------------------------------------------------------------- */ + SetMOFlags( nSavedMOFlags ); + + return CE_None; +} + +/************************************************************************/ +/* GetFileList() */ +/* */ +/* Add .aux.xml or .aux file into file list as appropriate. */ +/************************************************************************/ + +char **GDALPamDataset::GetFileList() + +{ + VSIStatBufL sStatBuf; + char **papszFileList = GDALDataset::GetFileList(); + + if( psPam && psPam->osPhysicalFilename.size() > 0 + && CSLFindString( papszFileList, psPam->osPhysicalFilename ) == -1 ) + { + papszFileList = CSLInsertString( papszFileList, 0, + psPam->osPhysicalFilename ); + } + + if( psPam && psPam->pszPamFilename ) + { + int bAddPamFile = (nPamFlags & GPF_DIRTY); + if (!bAddPamFile) + { + if (oOvManager.GetSiblingFiles() != NULL && IsPamFilenameAPotentialSiblingFile()) + bAddPamFile = CSLFindString(oOvManager.GetSiblingFiles(), + CPLGetFilename(psPam->pszPamFilename)) >= 0; + else + bAddPamFile = VSIStatExL( psPam->pszPamFilename, &sStatBuf, + VSI_STAT_EXISTS_FLAG ) == 0; + } + if (bAddPamFile) + { + papszFileList = CSLAddString( papszFileList, psPam->pszPamFilename ); + } + } + + if( psPam && psPam->osAuxFilename.size() > 0 && + CSLFindString( papszFileList, psPam->osAuxFilename ) == -1 ) + { + papszFileList = CSLAddString( papszFileList, psPam->osAuxFilename ); + } + return papszFileList; +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +CPLErr GDALPamDataset::IBuildOverviews( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ +/* -------------------------------------------------------------------- */ +/* Initialize PAM. */ +/* -------------------------------------------------------------------- */ + PamInitialize(); + if( psPam == NULL ) + return GDALDataset::IBuildOverviews( pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* If we appear to have subdatasets and to have a physical */ +/* filename, use that physical filename to derive a name for a */ +/* new overview file. */ +/* -------------------------------------------------------------------- */ + if( oOvManager.IsInitialized() && psPam->osPhysicalFilename.length() != 0 ) + return oOvManager.BuildOverviewsSubDataset( + psPam->osPhysicalFilename, pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); + else + return GDALDataset::IBuildOverviews( pszResampling, + nOverviews, panOverviewList, + nListBands, panBandList, + pfnProgress, pProgressData ); +} + + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GDALPamDataset::GetProjectionRef() + +{ + if( psPam && psPam->pszProjection ) + return psPam->pszProjection; + else + return GDALDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr GDALPamDataset::SetProjection( const char *pszProjectionIn ) + +{ + PamInitialize(); + + if( psPam == NULL ) + return GDALDataset::SetProjection( pszProjectionIn ); + else + { + CPLFree( psPam->pszProjection ); + psPam->pszProjection = CPLStrdup( pszProjectionIn ); + MarkPamDirty(); + + return CE_None; + } +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GDALPamDataset::GetGeoTransform( double * padfTransform ) + +{ + if( psPam && psPam->bHaveGeoTransform ) + { + memcpy( padfTransform, psPam->adfGeoTransform, sizeof(double) * 6 ); + return CE_None; + } + else + return GDALDataset::GetGeoTransform( padfTransform ); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr GDALPamDataset::SetGeoTransform( double * padfTransform ) + +{ + PamInitialize(); + + if( psPam ) + { + MarkPamDirty(); + psPam->bHaveGeoTransform = TRUE; + memcpy( psPam->adfGeoTransform, padfTransform, sizeof(double) * 6 ); + return( CE_None ); + } + else + { + return GDALDataset::SetGeoTransform( padfTransform ); + } +} + +/************************************************************************/ +/* GetGCPCount() */ +/************************************************************************/ + +int GDALPamDataset::GetGCPCount() + +{ + if( psPam && psPam->nGCPCount > 0 ) + return psPam->nGCPCount; + else + return GDALDataset::GetGCPCount(); +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *GDALPamDataset::GetGCPProjection() + +{ + if( psPam && psPam->pszGCPProjection != NULL ) + return psPam->pszGCPProjection; + else + return GDALDataset::GetGCPProjection(); +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *GDALPamDataset::GetGCPs() + +{ + if( psPam && psPam->nGCPCount > 0 ) + return psPam->pasGCPList; + else + return GDALDataset::GetGCPs(); +} + +/************************************************************************/ +/* SetGCPs() */ +/************************************************************************/ + +CPLErr GDALPamDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection ) + +{ + PamInitialize(); + + if( psPam ) + { + CPLFree( psPam->pszGCPProjection ); + if( psPam->nGCPCount > 0 ) + { + GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList ); + CPLFree( psPam->pasGCPList ); + } + + psPam->pszGCPProjection = CPLStrdup(pszGCPProjection); + psPam->nGCPCount = nGCPCount; + psPam->pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPList ); + + MarkPamDirty(); + + return CE_None; + } + else + { + return GDALDataset::SetGCPs( nGCPCount, pasGCPList, pszGCPProjection ); + } +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr GDALPamDataset::SetMetadata( char **papszMetadata, + const char *pszDomain ) + +{ + PamInitialize(); + + if( psPam ) + { + psPam->bHasMetadata = TRUE; + MarkPamDirty(); + } + + return GDALDataset::SetMetadata( papszMetadata, pszDomain ); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALPamDataset::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ + PamInitialize(); + + if( psPam ) + { + psPam->bHasMetadata = TRUE; + MarkPamDirty(); + } + + return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain ); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *GDALPamDataset::GetMetadataItem( const char *pszName, + const char *pszDomain ) + +{ +/* -------------------------------------------------------------------- */ +/* A request against the ProxyOverviewRequest is a special */ +/* mechanism to request an overview filename be allocated in */ +/* the proxy pool location. The allocated name is saved as */ +/* metadata as well as being returned. */ +/* -------------------------------------------------------------------- */ + if( pszDomain != NULL && EQUAL(pszDomain,"ProxyOverviewRequest") ) + { + CPLString osPrelimOvr = GetDescription(); + osPrelimOvr += ":::OVR"; + + const char *pszProxyOvrFilename = PamAllocateProxy( osPrelimOvr ); + if( pszProxyOvrFilename == NULL ) + return NULL; + + SetMetadataItem( "OVERVIEW_FILE", pszProxyOvrFilename, "OVERVIEWS" ); + + return pszProxyOvrFilename; + } + +/* -------------------------------------------------------------------- */ +/* If the OVERVIEW_FILE metadata is requested, we intercept the */ +/* request in order to replace ":::BASE:::" with the path to */ +/* the physical file - if available. This is primarily for the */ +/* purpose of managing subdataset overview filenames as being */ +/* relative to the physical file the subdataset comes */ +/* from. (#3287). */ +/* -------------------------------------------------------------------- */ + else if( pszDomain != NULL + && EQUAL(pszDomain,"OVERVIEWS") + && EQUAL(pszName,"OVERVIEW_FILE") ) + { + const char *pszOverviewFile = + GDALDataset::GetMetadataItem( pszName, pszDomain ); + + if( pszOverviewFile == NULL + || !EQUALN(pszOverviewFile,":::BASE:::",10) ) + return pszOverviewFile; + + CPLString osPath; + + if( strlen(GetPhysicalFilename()) > 0 ) + osPath = CPLGetPath(GetPhysicalFilename()); + else + osPath = CPLGetPath(GetDescription()); + + return CPLFormFilename( osPath, pszOverviewFile + 10, NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Everything else is a pass through. */ +/* -------------------------------------------------------------------- */ + else + return GDALDataset::GetMetadataItem( pszName, pszDomain ); + +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GDALPamDataset::GetMetadata( const char *pszDomain ) + +{ +// if( pszDomain == NULL || !EQUAL(pszDomain,"ProxyOverviewRequest") ) + return GDALDataset::GetMetadata( pszDomain ); +} + +/************************************************************************/ +/* TryLoadAux() */ +/************************************************************************/ + +CPLErr GDALPamDataset::TryLoadAux(char **papszSiblingFiles) + +{ +/* -------------------------------------------------------------------- */ +/* Initialize PAM. */ +/* -------------------------------------------------------------------- */ + PamInitialize(); + if( psPam == NULL ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* What is the name of the physical file we are referencing? */ +/* We allow an override via the psPam->pszPhysicalFile item. */ +/* -------------------------------------------------------------------- */ + const char *pszPhysicalFile = psPam->osPhysicalFilename; + + if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL ) + pszPhysicalFile = GetDescription(); + + if( strlen(pszPhysicalFile) == 0 ) + return CE_None; + + if( papszSiblingFiles ) + { + CPLString osAuxFilename = CPLResetExtension( pszPhysicalFile, "aux"); + int iSibling = CSLFindString( papszSiblingFiles, + CPLGetFilename(osAuxFilename) ); + if( iSibling < 0 ) + { + osAuxFilename = pszPhysicalFile; + osAuxFilename += ".aux"; + iSibling = CSLFindString( papszSiblingFiles, + CPLGetFilename(osAuxFilename) ); + if( iSibling < 0 ) + return CE_None; + } + } + +/* -------------------------------------------------------------------- */ +/* Try to open .aux file. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poAuxDS = GDALFindAssociatedAuxFile( pszPhysicalFile, + GA_ReadOnly, this ); + + if( poAuxDS == NULL ) + return CE_None; + + psPam->osAuxFilename = poAuxDS->GetDescription(); + +/* -------------------------------------------------------------------- */ +/* Do we have an SRS on the aux file? */ +/* -------------------------------------------------------------------- */ + if( strlen(poAuxDS->GetProjectionRef()) > 0 ) + GDALPamDataset::SetProjection( poAuxDS->GetProjectionRef() ); + +/* -------------------------------------------------------------------- */ +/* Geotransform. */ +/* -------------------------------------------------------------------- */ + if( poAuxDS->GetGeoTransform( psPam->adfGeoTransform ) == CE_None ) + psPam->bHaveGeoTransform = TRUE; + +/* -------------------------------------------------------------------- */ +/* GCPs */ +/* -------------------------------------------------------------------- */ + if( poAuxDS->GetGCPCount() > 0 ) + { + psPam->nGCPCount = poAuxDS->GetGCPCount(); + psPam->pasGCPList = GDALDuplicateGCPs( psPam->nGCPCount, + poAuxDS->GetGCPs() ); + } + +/* -------------------------------------------------------------------- */ +/* Apply metadata. We likely ought to be merging this in rather */ +/* than overwriting everything that was there. */ +/* -------------------------------------------------------------------- */ + char **papszMD = poAuxDS->GetMetadata(); + if( CSLCount(papszMD) > 0 ) + { + char **papszMerged = + CSLMerge( CSLDuplicate(GetMetadata()), papszMD ); + GDALPamDataset::SetMetadata( papszMerged ); + CSLDestroy( papszMerged ); + } + + papszMD = poAuxDS->GetMetadata("XFORMS"); + if( CSLCount(papszMD) > 0 ) + { + char **papszMerged = + CSLMerge( CSLDuplicate(GetMetadata("XFORMS")), papszMD ); + GDALPamDataset::SetMetadata( papszMerged, "XFORMS" ); + CSLDestroy( papszMerged ); + } + +/* ==================================================================== */ +/* Process bands. */ +/* ==================================================================== */ + int iBand; + + for( iBand = 0; iBand < poAuxDS->GetRasterCount(); iBand++ ) + { + if( iBand >= GetRasterCount() ) + break; + + GDALRasterBand *poAuxBand = poAuxDS->GetRasterBand( iBand+1 ); + GDALRasterBand *poBand = GetRasterBand( iBand+1 ); + + papszMD = poAuxBand->GetMetadata(); + if( CSLCount(papszMD) > 0 ) + { + char **papszMerged = + CSLMerge( CSLDuplicate(poBand->GetMetadata()), papszMD ); + poBand->SetMetadata( papszMerged ); + CSLDestroy( papszMerged ); + } + + if( strlen(poAuxBand->GetDescription()) > 0 ) + poBand->SetDescription( poAuxBand->GetDescription() ); + + if( poAuxBand->GetCategoryNames() != NULL ) + poBand->SetCategoryNames( poAuxBand->GetCategoryNames() ); + + if( poAuxBand->GetColorTable() != NULL + && poBand->GetColorTable() == NULL ) + poBand->SetColorTable( poAuxBand->GetColorTable() ); + + // histograms? + double dfMin, dfMax; + int nBuckets; + GUIntBig *panHistogram=NULL; + + if( poAuxBand->GetDefaultHistogram( &dfMin, &dfMax, + &nBuckets, &panHistogram, + FALSE, NULL, NULL ) == CE_None ) + { + poBand->SetDefaultHistogram( dfMin, dfMax, nBuckets, + panHistogram ); + CPLFree( panHistogram ); + } + + // RAT + if( poAuxBand->GetDefaultRAT() != NULL ) + poBand->SetDefaultRAT( poAuxBand->GetDefaultRAT() ); + + // NoData + int bSuccess = FALSE; + double dfNoDataValue = poAuxBand->GetNoDataValue( &bSuccess ); + if( bSuccess ) + poBand->SetNoDataValue( dfNoDataValue ); + } + + GDALClose( poAuxDS ); + +/* -------------------------------------------------------------------- */ +/* Mark PAM info as clean. */ +/* -------------------------------------------------------------------- */ + nPamFlags &= ~GPF_DIRTY; + + return CE_Failure; +} diff --git a/bazaar/plugin/gdal/gcore/gdalpamproxydb.cpp b/bazaar/plugin/gdal/gcore/gdalpamproxydb.cpp new file mode 100644 index 000000000..44915190c --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalpamproxydb.cpp @@ -0,0 +1,400 @@ +/****************************************************************************** + * $Id: gdalpamproxydb.cpp 28459 2015-02-12 13:48:21Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of the GDAL PAM Proxy database interface. + * The proxy db is used to associate .aux.xml files in a temp + * directory - used for files for which aux.xml files can't be + * created (ie. read-only file systems). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "cpl_string.h" +#include "ogr_spatialref.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: gdalpamproxydb.cpp 28459 2015-02-12 13:48:21Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* GDALPamProxyDB */ +/* ==================================================================== */ +/************************************************************************/ + +class GDALPamProxyDB +{ + public: + GDALPamProxyDB() { nUpdateCounter = -1; } + + CPLString osProxyDBDir; + + int nUpdateCounter; + + std::vector aosOriginalFiles; + std::vector aosProxyFiles; + + void CheckLoadDB(); + void LoadDB(); + void SaveDB(); +}; + +static int bProxyDBInitialized = FALSE; +static GDALPamProxyDB *poProxyDB = NULL; +static CPLMutex *hProxyDBLock = NULL; + +/************************************************************************/ +/* CheckLoadDB() */ +/* */ +/* Eventually we want to check if the file has changed, and if */ +/* so, force it to be reloaded. TODO: */ +/************************************************************************/ + +void GDALPamProxyDB::CheckLoadDB() + +{ + if( nUpdateCounter == -1 ) + LoadDB(); +} + +/************************************************************************/ +/* LoadDB() */ +/* */ +/* It is assumed the caller already holds the lock. */ +/************************************************************************/ + +void GDALPamProxyDB::LoadDB() + +{ +/* -------------------------------------------------------------------- */ +/* Open the database relating original names to proxy .aux.xml */ +/* file names. */ +/* -------------------------------------------------------------------- */ + CPLString osDBName = + CPLFormFilename( osProxyDBDir, "gdal_pam_proxy", "dat" ); + VSILFILE *fpDB = VSIFOpenL( osDBName, "r" ); + + nUpdateCounter = 0; + if( fpDB == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Read header, verify and extract update counter. */ +/* -------------------------------------------------------------------- */ + GByte abyHeader[100]; + + if( VSIFReadL( abyHeader, 1, 100, fpDB ) != 100 + || strncmp( (const char *) abyHeader, "GDAL_PROXY", 10 ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Problem reading %s header - short or corrupt?", + osDBName.c_str() ); + return; + } + + nUpdateCounter = atoi((const char *) abyHeader + 10); + +/* -------------------------------------------------------------------- */ +/* Read the file in one gulp. */ +/* -------------------------------------------------------------------- */ + int nBufLength; + char *pszDBData; + + VSIFSeekL( fpDB, 0, SEEK_END ); + nBufLength = (int) (VSIFTellL(fpDB) - 100); + + pszDBData = (char *) CPLCalloc(1,nBufLength+1); + VSIFSeekL( fpDB, 100, SEEK_SET ); + VSIFReadL( pszDBData, 1, nBufLength, fpDB ); + + VSIFCloseL( fpDB ); + +/* -------------------------------------------------------------------- */ +/* Parse the list of in/out names. */ +/* -------------------------------------------------------------------- */ + int iNext = 0; + + while( iNext < nBufLength ) + { + CPLString osOriginal, osProxy; + + osOriginal.assign( pszDBData + iNext ); + + for( ; iNext < nBufLength && pszDBData[iNext] != '\0'; iNext++ ) {} + + if( iNext == nBufLength ) + break; + + iNext++; + + osProxy = osProxyDBDir; + osProxy += "/"; + osProxy += pszDBData + iNext; + + for( ; iNext < nBufLength && pszDBData[iNext] != '\0'; iNext++ ) {} + iNext++; + + aosOriginalFiles.push_back( osOriginal ); + aosProxyFiles.push_back( osProxy ); + } + + CPLFree( pszDBData ); +} + +/************************************************************************/ +/* SaveDB() */ +/************************************************************************/ + +void GDALPamProxyDB::SaveDB() + +{ +/* -------------------------------------------------------------------- */ +/* Open the database relating original names to proxy .aux.xml */ +/* file names. */ +/* -------------------------------------------------------------------- */ + CPLString osDBName = + CPLFormFilename( osProxyDBDir, "gdal_pam_proxy", "dat" ); + + void *hLock = CPLLockFile( osDBName, 1.0 ); + + // proceed even if lock fails - we need CPLBreakLockFile()! + if( hLock == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "GDALPamProxyDB::SaveDB() - Failed to lock %s file, proceeding anyways.", + osDBName.c_str() ); + } + + VSILFILE *fpDB = VSIFOpenL( osDBName, "w" ); + if( fpDB == NULL ) + { + if( hLock ) + CPLUnlockFile( hLock ); + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to save %s Pam Proxy DB.\n%s", + osDBName.c_str(), + VSIStrerror( errno ) ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Write header. */ +/* -------------------------------------------------------------------- */ + GByte abyHeader[100]; + + memset( abyHeader, ' ', sizeof(abyHeader) ); + strncpy( (char *) abyHeader, "GDAL_PROXY", 10 ); + sprintf( (char *) abyHeader + 10, "%9d", nUpdateCounter ); + + VSIFWriteL( abyHeader, 1, 100, fpDB ); + +/* -------------------------------------------------------------------- */ +/* Write names. */ +/* -------------------------------------------------------------------- */ + unsigned int i; + + for( i = 0; i < aosOriginalFiles.size(); i++ ) + { + size_t nBytesWritten; + const char *pszProxyFile; + + VSIFWriteL( aosOriginalFiles[i].c_str(), 1, + strlen(aosOriginalFiles[i].c_str())+1, fpDB ); + + pszProxyFile = CPLGetFilename(aosProxyFiles[i]); + nBytesWritten = VSIFWriteL( pszProxyFile, 1, + strlen(pszProxyFile)+1, fpDB ); + + if( nBytesWritten != strlen(pszProxyFile)+1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to write complete %s Pam Proxy DB.\n%s", + osDBName.c_str(), + VSIStrerror( errno ) ); + VSIFCloseL( fpDB ); + VSIUnlink( osDBName ); + return; + } + } + + VSIFCloseL( fpDB ); + + if( hLock ) + CPLUnlockFile( hLock ); +} + + +/************************************************************************/ +/* InitProxyDB() */ +/* */ +/* Initialize ProxyDB (if it isn't already initialized). */ +/************************************************************************/ + +static void InitProxyDB() + +{ + if( !bProxyDBInitialized ) + { + CPLMutexHolderD( &hProxyDBLock ); + + if( !bProxyDBInitialized ) + { + const char *pszProxyDir = + CPLGetConfigOption( "GDAL_PAM_PROXY_DIR", NULL ); + + if( pszProxyDir ) + { + poProxyDB = new GDALPamProxyDB(); + poProxyDB->osProxyDBDir = pszProxyDir; + } + } + + bProxyDBInitialized = TRUE; + } +} + +/************************************************************************/ +/* PamCleanProxyDB() */ +/************************************************************************/ + +void PamCleanProxyDB() + +{ + { + CPLMutexHolderD( &hProxyDBLock ); + + bProxyDBInitialized = FALSE; + + delete poProxyDB; + poProxyDB = NULL; + } + + CPLDestroyMutex( hProxyDBLock ); + hProxyDBLock = NULL; +} + +/************************************************************************/ +/* PamGetProxy() */ +/************************************************************************/ + +const char *PamGetProxy( const char *pszOriginal ) + +{ + InitProxyDB(); + + if( poProxyDB == NULL ) + return NULL; + + CPLMutexHolderD( &hProxyDBLock ); + unsigned int i; + + poProxyDB->CheckLoadDB(); + + for( i = 0; i < poProxyDB->aosOriginalFiles.size(); i++ ) + { + if( strcmp( poProxyDB->aosOriginalFiles[i], pszOriginal ) == 0 ) + return poProxyDB->aosProxyFiles[i]; + } + + return NULL; +} + +/************************************************************************/ +/* PamAllocateProxy() */ +/************************************************************************/ + +const char *PamAllocateProxy( const char *pszOriginal ) + +{ + InitProxyDB(); + + if( poProxyDB == NULL ) + return NULL; + + CPLMutexHolderD( &hProxyDBLock ); + + poProxyDB->CheckLoadDB(); + +/* -------------------------------------------------------------------- */ +/* Form the proxy filename based on the original path if */ +/* possible, but dummy out any questionable characters, path */ +/* delimiters and such. This is intended to make the proxy */ +/* name be identifiable by folks digging around in the proxy */ +/* database directory. */ +/* */ +/* We also need to be careful about length. */ +/* -------------------------------------------------------------------- */ + CPLString osRevProxyFile; + int i; + + i = strlen(pszOriginal) - 1; + while( i >= 0 && osRevProxyFile.size() < 220 ) + { + if( i > 6 && EQUALN(pszOriginal+i-5,":::OVR",6) ) + i -= 6; + + // make some effort to break long names at path delimiters. + if( (pszOriginal[i] == '/' || pszOriginal[i] == '\\') + && osRevProxyFile.size() > 200 ) + break; + + if( (pszOriginal[i] >= 'A' && pszOriginal[i] <= 'Z') + || (pszOriginal[i] >= 'a' && pszOriginal[i] <= 'z') + || (pszOriginal[i] >= '0' && pszOriginal[i] <= '9') + || pszOriginal[i] == '.' ) + osRevProxyFile += pszOriginal[i]; + else + osRevProxyFile += '_'; + + i--; + } + + CPLString osOriginal = pszOriginal; + CPLString osProxy; + CPLString osCounter; + + osProxy = poProxyDB->osProxyDBDir + "/"; + + osCounter.Printf( "%06d_", poProxyDB->nUpdateCounter++ ); + osProxy += osCounter; + + for( i = osRevProxyFile.size()-1; i >= 0; i-- ) + osProxy += osRevProxyFile[i]; + + if( osOriginal.find(":::OVR") != CPLString::npos ) + osProxy += ".ovr"; + else + osProxy += ".aux.xml"; + +/* -------------------------------------------------------------------- */ +/* Add the proxy and the original to the proxy list and resave */ +/* the database. */ +/* -------------------------------------------------------------------- */ + poProxyDB->aosOriginalFiles.push_back( osOriginal ); + poProxyDB->aosProxyFiles.push_back( osProxy ); + + poProxyDB->SaveDB(); + + return PamGetProxy( pszOriginal ); +} diff --git a/bazaar/plugin/gdal/gcore/gdalpamrasterband.cpp b/bazaar/plugin/gdal/gcore/gdalpamrasterband.cpp new file mode 100644 index 000000000..f69be1840 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalpamrasterband.cpp @@ -0,0 +1,1331 @@ +/****************************************************************************** + * $Id: gdalpamrasterband.cpp 29038 2015-04-28 09:03:36Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALPamRasterBand, a raster band base class + * that knows how to persistently store auxiliary metadata in an + * external xml file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_pam.h" +#include "gdal_rat.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: gdalpamrasterband.cpp 29038 2015-04-28 09:03:36Z rouault $"); + +/************************************************************************/ +/* GDALPamRasterBand() */ +/************************************************************************/ + +GDALPamRasterBand::GDALPamRasterBand() + +{ + psPam = NULL; + SetMOFlags( GetMOFlags() | GMO_PAM_CLASS ); +} + +/************************************************************************/ +/* ~GDALPamRasterBand() */ +/************************************************************************/ + +GDALPamRasterBand::~GDALPamRasterBand() + +{ + PamClear(); +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *GDALPamRasterBand::SerializeToXML( CPL_UNUSED const char *pszUnused ) +{ + if( psPam == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Setup root node and attributes. */ +/* -------------------------------------------------------------------- */ + CPLString oFmt; + + CPLXMLNode *psTree; + + psTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMRasterBand" ); + + if( GetBand() > 0 ) + CPLSetXMLValue( psTree, "#band", oFmt.Printf( "%d", GetBand() ) ); + +/* -------------------------------------------------------------------- */ +/* Serialize information of interest. */ +/* -------------------------------------------------------------------- */ + if( strlen(GetDescription()) > 0 ) + CPLSetXMLValue( psTree, "Description", GetDescription() ); + + if( psPam->bNoDataValueSet ) + { + if (CPLIsNan(psPam->dfNoDataValue)) + CPLSetXMLValue( psTree, "NoDataValue", "nan" ); + else + CPLSetXMLValue( psTree, "NoDataValue", + oFmt.Printf( "%.14E", psPam->dfNoDataValue ) ); + + /* hex encode real floating point values */ + if( psPam->dfNoDataValue != floor(psPam->dfNoDataValue) + || psPam->dfNoDataValue != CPLAtof(oFmt) ) + { + double dfNoDataLittleEndian; + + dfNoDataLittleEndian = psPam->dfNoDataValue; + CPL_LSBPTR64( &dfNoDataLittleEndian ); + + char *pszHexEncoding = + CPLBinaryToHex( 8, (GByte *) &dfNoDataLittleEndian ); + CPLSetXMLValue( psTree, "NoDataValue.#le_hex_equiv",pszHexEncoding); + CPLFree( pszHexEncoding ); + } + } + + if( psPam->pszUnitType != NULL ) + CPLSetXMLValue( psTree, "UnitType", psPam->pszUnitType ); + + if( psPam->dfOffset != 0.0 ) + CPLSetXMLValue( psTree, "Offset", + oFmt.Printf( "%.16g", psPam->dfOffset ) ); + + if( psPam->dfScale != 1.0 ) + CPLSetXMLValue( psTree, "Scale", + oFmt.Printf( "%.16g", psPam->dfScale ) ); + + if( psPam->eColorInterp != GCI_Undefined ) + CPLSetXMLValue( psTree, "ColorInterp", + GDALGetColorInterpretationName( psPam->eColorInterp )); + +/* -------------------------------------------------------------------- */ +/* Category names. */ +/* -------------------------------------------------------------------- */ + if( psPam->papszCategoryNames != NULL ) + { + CPLXMLNode *psCT_XML = CPLCreateXMLNode( psTree, CXT_Element, + "CategoryNames" ); + CPLXMLNode* psLastChild = NULL; + + for( int iEntry=0; psPam->papszCategoryNames[iEntry] != NULL; iEntry++) + { + CPLXMLNode *psNode = CPLCreateXMLElementAndValue( NULL, "Category", + psPam->papszCategoryNames[iEntry] ); + if( psLastChild == NULL ) + psCT_XML->psChild = psNode; + else + psLastChild->psNext = psNode; + psLastChild = psNode; + } + } + +/* -------------------------------------------------------------------- */ +/* Color Table. */ +/* -------------------------------------------------------------------- */ + if( psPam->poColorTable != NULL ) + { + CPLXMLNode *psCT_XML = CPLCreateXMLNode( psTree, CXT_Element, + "ColorTable" ); + CPLXMLNode* psLastChild = NULL; + + for( int iEntry=0; iEntry < psPam->poColorTable->GetColorEntryCount(); + iEntry++ ) + { + GDALColorEntry sEntry; + CPLXMLNode *psEntry_XML = CPLCreateXMLNode( NULL, CXT_Element, + "Entry" ); + if( psLastChild == NULL ) + psCT_XML->psChild = psEntry_XML; + else + psLastChild->psNext = psEntry_XML; + psLastChild = psEntry_XML; + + psPam->poColorTable->GetColorEntryAsRGB( iEntry, &sEntry ); + + CPLSetXMLValue( psEntry_XML, "#c1", oFmt.Printf("%d",sEntry.c1) ); + CPLSetXMLValue( psEntry_XML, "#c2", oFmt.Printf("%d",sEntry.c2) ); + CPLSetXMLValue( psEntry_XML, "#c3", oFmt.Printf("%d",sEntry.c3) ); + CPLSetXMLValue( psEntry_XML, "#c4", oFmt.Printf("%d",sEntry.c4) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Min/max. */ +/* -------------------------------------------------------------------- */ + if( psPam->bHaveMinMax ) + { + CPLSetXMLValue( psTree, "Minimum", + oFmt.Printf( "%.16g", psPam->dfMin ) ); + CPLSetXMLValue( psTree, "Maximum", + oFmt.Printf( "%.16g", psPam->dfMax ) ); + } + +/* -------------------------------------------------------------------- */ +/* Statistics */ +/* -------------------------------------------------------------------- */ + if( psPam->bHaveStats ) + { + CPLSetXMLValue( psTree, "Mean", + oFmt.Printf( "%.16g", psPam->dfMean ) ); + CPLSetXMLValue( psTree, "StandardDeviation", + oFmt.Printf( "%.16g", psPam->dfStdDev ) ); + } + +/* -------------------------------------------------------------------- */ +/* Histograms. */ +/* -------------------------------------------------------------------- */ + if( psPam->psSavedHistograms != NULL ) + CPLAddXMLChild( psTree, CPLCloneXMLTree( psPam->psSavedHistograms ) ); + +/* -------------------------------------------------------------------- */ +/* Raster Attribute Table */ +/* -------------------------------------------------------------------- */ + if( psPam->poDefaultRAT != NULL ) + { + CPLXMLNode* psSerializedRAT = psPam->poDefaultRAT->Serialize(); + if( psSerializedRAT != NULL ) + CPLAddXMLChild( psTree, psSerializedRAT ); + } + +/* -------------------------------------------------------------------- */ +/* Metadata. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psMD; + + psMD = oMDMD.Serialize(); + if( psMD != NULL ) + { + CPLAddXMLChild( psTree, psMD ); + } + +/* -------------------------------------------------------------------- */ +/* We don't want to return anything if we had no metadata to */ +/* attach. */ +/* -------------------------------------------------------------------- */ + if( psTree->psChild == NULL || psTree->psChild->psNext == NULL ) + { + CPLDestroyXMLNode( psTree ); + psTree = NULL; + } + + return psTree; +} + +/************************************************************************/ +/* PamInitialize() */ +/************************************************************************/ + +void GDALPamRasterBand::PamInitialize() + +{ + if( psPam ) + return; + + GDALPamDataset *poParentDS = (GDALPamDataset *) GetDataset(); + + if( poParentDS == NULL || !(poParentDS->GetMOFlags() & GMO_PAM_CLASS) ) + return; + + poParentDS->PamInitialize(); + if( poParentDS->psPam == NULL ) + return; + + // Often (always?) initializing our parent will have initialized us. + if( psPam != NULL ) + return; + + psPam = (GDALRasterBandPamInfo *) + CPLCalloc(sizeof(GDALRasterBandPamInfo),1); + + psPam->dfScale = 1.0; + psPam->poParentDS = poParentDS; + psPam->dfNoDataValue = -1e10; + psPam->poDefaultRAT = NULL; +} + +/************************************************************************/ +/* PamClear() */ +/************************************************************************/ + +void GDALPamRasterBand::PamClear() + +{ + if( psPam ) + { + if( psPam->poColorTable ) + delete psPam->poColorTable; + psPam->poColorTable = NULL; + + CPLFree( psPam->pszUnitType ); + CSLDestroy( psPam->papszCategoryNames ); + + if( psPam->poDefaultRAT != NULL ) + { + delete psPam->poDefaultRAT; + psPam->poDefaultRAT = NULL; + } + + if (psPam->psSavedHistograms != NULL) + { + CPLDestroyXMLNode (psPam->psSavedHistograms ); + psPam->psSavedHistograms = NULL; + } + + CPLFree( psPam ); + psPam = NULL; + } +} + +/************************************************************************/ +/* XMLInit() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::XMLInit( CPLXMLNode *psTree, CPL_UNUSED const char *pszUnused ) +{ + PamInitialize(); + +/* -------------------------------------------------------------------- */ +/* Apply any dataset level metadata. */ +/* -------------------------------------------------------------------- */ + oMDMD.XMLInit( psTree, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Collect various other items of metadata. */ +/* -------------------------------------------------------------------- */ + GDALMajorObject::SetDescription( CPLGetXMLValue( psTree, "Description", "" ) ); + + if( CPLGetXMLValue( psTree, "NoDataValue", NULL ) != NULL ) + { + const char *pszLEHex = + CPLGetXMLValue( psTree, "NoDataValue.le_hex_equiv", NULL ); + if( pszLEHex != NULL ) + { + int nBytes; + GByte *pabyBin = CPLHexToBinary( pszLEHex, &nBytes ); + if( nBytes == 8 ) + { + CPL_LSBPTR64( pabyBin ); + + GDALPamRasterBand::SetNoDataValue( *((double *) pabyBin) ); + } + else + { + GDALPamRasterBand::SetNoDataValue( + CPLAtof(CPLGetXMLValue( psTree, "NoDataValue", "0" )) ); + } + CPLFree( pabyBin ); + } + else + { + GDALPamRasterBand::SetNoDataValue( + CPLAtof(CPLGetXMLValue( psTree, "NoDataValue", "0" )) ); + } + } + + GDALPamRasterBand::SetOffset( + CPLAtof(CPLGetXMLValue( psTree, "Offset", "0.0" )) ); + GDALPamRasterBand::SetScale( + CPLAtof(CPLGetXMLValue( psTree, "Scale", "1.0" )) ); + + GDALPamRasterBand::SetUnitType( CPLGetXMLValue( psTree, "UnitType", NULL)); + + if( CPLGetXMLValue( psTree, "ColorInterp", NULL ) != NULL ) + { + const char *pszInterp = CPLGetXMLValue( psTree, "ColorInterp", NULL ); + GDALPamRasterBand::SetColorInterpretation( + GDALGetColorInterpretationByName(pszInterp)); + } + +/* -------------------------------------------------------------------- */ +/* Category names. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psTree, "CategoryNames" ) != NULL ) + { + CPLXMLNode *psEntry; + CPLStringList oCategoryNames; + + for( psEntry = CPLGetXMLNode( psTree, "CategoryNames" )->psChild; + psEntry != NULL; psEntry = psEntry->psNext ) + { + /* Don't skeep tag with empty content */ + if( psEntry->eType != CXT_Element + || !EQUAL(psEntry->pszValue,"Category") + || (psEntry->psChild != NULL && psEntry->psChild->eType != CXT_Text) ) + continue; + + oCategoryNames.AddString( + (psEntry->psChild) ? psEntry->psChild->pszValue : "" ); + } + + GDALPamRasterBand::SetCategoryNames( oCategoryNames.List() ); + } + +/* -------------------------------------------------------------------- */ +/* Collect a color table. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psTree, "ColorTable" ) != NULL ) + { + CPLXMLNode *psEntry; + GDALColorTable oTable; + int iEntry = 0; + + for( psEntry = CPLGetXMLNode( psTree, "ColorTable" )->psChild; + psEntry != NULL; psEntry = psEntry->psNext ) + { + GDALColorEntry sCEntry; + + sCEntry.c1 = (short) atoi(CPLGetXMLValue( psEntry, "c1", "0" )); + sCEntry.c2 = (short) atoi(CPLGetXMLValue( psEntry, "c2", "0" )); + sCEntry.c3 = (short) atoi(CPLGetXMLValue( psEntry, "c3", "0" )); + sCEntry.c4 = (short) atoi(CPLGetXMLValue( psEntry, "c4", "255" )); + + oTable.SetColorEntry( iEntry++, &sCEntry ); + } + + GDALPamRasterBand::SetColorTable( &oTable ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a complete set of stats? */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psTree, "Minimum" ) != NULL + && CPLGetXMLNode( psTree, "Maximum" ) != NULL ) + { + psPam->bHaveMinMax = TRUE; + psPam->dfMin = CPLAtof(CPLGetXMLValue(psTree, "Minimum","0")); + psPam->dfMax = CPLAtof(CPLGetXMLValue(psTree, "Maximum","0")); + } + + if( CPLGetXMLNode( psTree, "Mean" ) != NULL + && CPLGetXMLNode( psTree, "StandardDeviation" ) != NULL ) + { + psPam->bHaveStats = TRUE; + psPam->dfMean = CPLAtof(CPLGetXMLValue(psTree, "Mean","0")); + psPam->dfStdDev = CPLAtof(CPLGetXMLValue(psTree,"StandardDeviation","0")); + } + +/* -------------------------------------------------------------------- */ +/* Histograms */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psHist = CPLGetXMLNode( psTree, "Histograms" ); + if( psHist != NULL ) + { + CPLXMLNode *psNext = psHist->psNext; + psHist->psNext = NULL; + + if (psPam->psSavedHistograms != NULL) + { + CPLDestroyXMLNode (psPam->psSavedHistograms ); + psPam->psSavedHistograms = NULL; + } + psPam->psSavedHistograms = CPLCloneXMLTree( psHist ); + psHist->psNext = psNext; + } + +/* -------------------------------------------------------------------- */ +/* Raster Attribute Table */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRAT = CPLGetXMLNode( psTree, "GDALRasterAttributeTable" ); + if( psRAT != NULL ) + { + if( psPam->poDefaultRAT != NULL ) + { + delete psPam->poDefaultRAT; + psPam->poDefaultRAT = NULL; + } + psPam->poDefaultRAT = new GDALDefaultRasterAttributeTable(); + psPam->poDefaultRAT->XMLInit( psRAT, "" ); + } + + return CE_None; +} + +/************************************************************************/ +/* CloneInfo() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::CloneInfo( GDALRasterBand *poSrcBand, + int nCloneFlags ) + +{ + int bOnlyIfMissing = nCloneFlags & GCIF_ONLY_IF_MISSING; + int bSuccess; + int nSavedMOFlags = GetMOFlags(); + + PamInitialize(); + +/* -------------------------------------------------------------------- */ +/* Suppress NotImplemented error messages - mainly needed if PAM */ +/* disabled. */ +/* -------------------------------------------------------------------- */ + SetMOFlags( nSavedMOFlags | GMO_IGNORE_UNIMPLEMENTED ); + +/* -------------------------------------------------------------------- */ +/* Metadata */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_BAND_METADATA ) + { + if( poSrcBand->GetMetadata() != NULL ) + { + if( !bOnlyIfMissing + || CSLCount(GetMetadata()) != CSLCount(poSrcBand->GetMetadata()) ) + { + SetMetadata( poSrcBand->GetMetadata() ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Band description. */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_BAND_DESCRIPTION ) + { + if( strlen(poSrcBand->GetDescription()) > 0 ) + { + if( !bOnlyIfMissing || strlen(GetDescription()) == 0 ) + GDALPamRasterBand::SetDescription( poSrcBand->GetDescription()); + } + } + +/* -------------------------------------------------------------------- */ +/* NODATA */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_NODATA ) + { + double dfNoData = poSrcBand->GetNoDataValue( &bSuccess ); + + if( bSuccess ) + { + if( !bOnlyIfMissing + || GetNoDataValue( &bSuccess ) != dfNoData + || !bSuccess ) + GDALPamRasterBand::SetNoDataValue( dfNoData ); + } + } + +/* -------------------------------------------------------------------- */ +/* Category names */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_CATEGORYNAMES ) + { + if( poSrcBand->GetCategoryNames() != NULL ) + { + if( !bOnlyIfMissing || GetCategoryNames() == NULL ) + GDALPamRasterBand::SetCategoryNames( poSrcBand->GetCategoryNames() ); + } + } + +/* -------------------------------------------------------------------- */ +/* Offset/scale */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_SCALEOFFSET ) + { + double dfOffset = poSrcBand->GetOffset( &bSuccess ); + + if( bSuccess ) + { + if( !bOnlyIfMissing || GetOffset() != dfOffset ) + GDALPamRasterBand::SetOffset( dfOffset ); + } + + double dfScale = poSrcBand->GetScale( &bSuccess ); + + if( bSuccess ) + { + if( !bOnlyIfMissing || GetScale() != dfScale ) + GDALPamRasterBand::SetScale( dfScale ); + } + } + +/* -------------------------------------------------------------------- */ +/* Unittype. */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_UNITTYPE ) + { + if( strlen(poSrcBand->GetUnitType()) > 0 ) + { + if( !bOnlyIfMissing + || !EQUAL(GetUnitType(),poSrcBand->GetUnitType()) ) + { + GDALPamRasterBand::SetUnitType( poSrcBand->GetUnitType() ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* ColorInterp */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_COLORINTERP ) + { + if( poSrcBand->GetColorInterpretation() != GCI_Undefined ) + { + if( !bOnlyIfMissing + || poSrcBand->GetColorInterpretation() + != GetColorInterpretation() ) + GDALPamRasterBand::SetColorInterpretation( + poSrcBand->GetColorInterpretation() ); + } + } + +/* -------------------------------------------------------------------- */ +/* color table. */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_COLORTABLE ) + { + if( poSrcBand->GetColorTable() != NULL ) + { + if( !bOnlyIfMissing || GetColorTable() == NULL ) + { + GDALPamRasterBand::SetColorTable( + poSrcBand->GetColorTable() ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Raster Attribute Table. */ +/* -------------------------------------------------------------------- */ + if( nCloneFlags & GCIF_RAT ) + { + const GDALRasterAttributeTable *poRAT = poSrcBand->GetDefaultRAT(); + + if( poRAT != NULL ) + { + if( !bOnlyIfMissing || GetDefaultRAT() == NULL ) + { + GDALPamRasterBand::SetDefaultRAT( poRAT ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Restore MO flags. */ +/* -------------------------------------------------------------------- */ + SetMOFlags( nSavedMOFlags ); + + return CE_None; +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetMetadata( char **papszMetadata, + const char *pszDomain ) + +{ + PamInitialize(); + + if( psPam ) + psPam->poParentDS->MarkPamDirty(); + + return GDALRasterBand::SetMetadata( papszMetadata, pszDomain ); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetMetadataItem( const char *pszName, + const char *pszValue, + const char *pszDomain ) + +{ + PamInitialize(); + + if( psPam ) + psPam->poParentDS->MarkPamDirty(); + + return GDALRasterBand::SetMetadataItem( pszName, pszValue, pszDomain ); +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetNoDataValue( double dfNewValue ) + +{ + PamInitialize(); + + if( psPam ) + { + psPam->bNoDataValueSet = TRUE; + psPam->dfNoDataValue = dfNewValue; + psPam->poParentDS->MarkPamDirty(); + return CE_None; + } + else + return GDALRasterBand::SetNoDataValue( dfNewValue ); +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +double GDALPamRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( psPam != NULL ) + { + if( pbSuccess ) + *pbSuccess = psPam->bNoDataValueSet; + + return psPam->dfNoDataValue; + } + else + return GDALRasterBand::GetNoDataValue( pbSuccess ); +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +double GDALPamRasterBand::GetOffset( int *pbSuccess ) + +{ + if( psPam ) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return psPam->dfOffset; + } + else + return GDALRasterBand::GetOffset( pbSuccess ); +} + +/************************************************************************/ +/* SetOffset() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetOffset( double dfNewOffset ) + +{ + PamInitialize(); + + if( psPam != NULL ) + { + if( psPam->dfOffset != dfNewOffset ) + { + psPam->dfOffset = dfNewOffset; + psPam->poParentDS->MarkPamDirty(); + } + + return CE_None; + } + else + return GDALRasterBand::SetOffset( dfNewOffset ); +} + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ + +double GDALPamRasterBand::GetScale( int *pbSuccess ) + +{ + if( psPam ) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return psPam->dfScale; + } + else + return GDALRasterBand::GetScale( pbSuccess ); +} + +/************************************************************************/ +/* SetScale() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetScale( double dfNewScale ) + +{ + PamInitialize(); + + if( psPam != NULL ) + { + if( dfNewScale != psPam->dfScale ) + { + psPam->dfScale = dfNewScale; + psPam->poParentDS->MarkPamDirty(); + } + return CE_None; + } + else + return GDALRasterBand::SetScale( dfNewScale ); +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +const char *GDALPamRasterBand::GetUnitType() + +{ + if( psPam != NULL ) + { + if( psPam->pszUnitType == NULL ) + return ""; + else + return psPam->pszUnitType; + } + else + return GDALRasterBand::GetUnitType(); +} + +/************************************************************************/ +/* SetUnitType() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetUnitType( const char *pszNewValue ) + +{ + PamInitialize(); + + if( psPam ) + { + if( pszNewValue == NULL || pszNewValue[0] == '\0' ) + { + if (psPam->pszUnitType != NULL) + psPam->poParentDS->MarkPamDirty(); + CPLFree( psPam->pszUnitType ); + psPam->pszUnitType = NULL; + } + else + { + if (psPam->pszUnitType == NULL || + strcmp(psPam->pszUnitType, pszNewValue) != 0) + psPam->poParentDS->MarkPamDirty(); + CPLFree( psPam->pszUnitType ); + psPam->pszUnitType = CPLStrdup(pszNewValue); + } + + return CE_None; + } + else + return GDALRasterBand::SetUnitType( pszNewValue ); +} + +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +char **GDALPamRasterBand::GetCategoryNames() + +{ + if( psPam ) + return psPam->papszCategoryNames; + else + return GDALRasterBand::GetCategoryNames(); +} + +/************************************************************************/ +/* SetCategoryNames() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetCategoryNames( char ** papszNewNames ) + +{ + PamInitialize(); + + if( psPam ) + { + CSLDestroy( psPam->papszCategoryNames ); + psPam->papszCategoryNames = CSLDuplicate( papszNewNames ); + psPam->poParentDS->MarkPamDirty(); + return CE_None; + } + else + return GDALRasterBand::SetCategoryNames( papszNewNames ); + +} + + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable *GDALPamRasterBand::GetColorTable() + +{ + if( psPam ) + return psPam->poColorTable; + else + return GDALRasterBand::GetColorTable(); +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetColorTable( GDALColorTable *poTableIn ) + +{ + PamInitialize(); + + if( psPam ) + { + if( psPam->poColorTable != NULL ) + { + delete psPam->poColorTable; + psPam->poColorTable = NULL; + } + + if( poTableIn ) + { + psPam->poColorTable = poTableIn->Clone(); + psPam->eColorInterp = GCI_PaletteIndex; + } + + psPam->poParentDS->MarkPamDirty(); + + return CE_None; + } + else + return GDALRasterBand::SetColorTable( poTableIn ); + +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetColorInterpretation( GDALColorInterp eInterpIn ) + +{ + PamInitialize(); + + if( psPam ) + { + psPam->poParentDS->MarkPamDirty(); + + psPam->eColorInterp = eInterpIn; + + return CE_None; + } + else + return GDALRasterBand::SetColorInterpretation( eInterpIn ); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp GDALPamRasterBand::GetColorInterpretation() + +{ + if( psPam ) + return psPam->eColorInterp; + else + return GDALRasterBand::GetColorInterpretation(); +} + +/************************************************************************/ +/* SetDescription() */ +/* */ +/* We let the GDALMajorObject hold the description, but we keep */ +/* track of whether it has been changed so we know to save it. */ +/************************************************************************/ + +void GDALPamRasterBand::SetDescription( const char *pszDescription ) + +{ + PamInitialize(); + + if( psPam && strcmp(pszDescription,GetDescription()) != 0 ) + psPam->poParentDS->MarkPamDirty(); + + GDALRasterBand::SetDescription( pszDescription ); +} + +/************************************************************************/ +/* PamParseHistogram() */ +/************************************************************************/ + +int +PamParseHistogram( CPLXMLNode *psHistItem, + double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig **ppanHistogram, + CPL_UNUSED int *pbIncludeOutOfRange, + CPL_UNUSED int *pbApproxOK ) +{ + if( psHistItem == NULL ) + return FALSE; + + *pdfMin = CPLAtof(CPLGetXMLValue( psHistItem, "HistMin", "0")); + *pdfMax = CPLAtof(CPLGetXMLValue( psHistItem, "HistMax", "1")); + *pnBuckets = atoi(CPLGetXMLValue( psHistItem, "BucketCount","2")); + + if (*pnBuckets <= 0 || *pnBuckets > INT_MAX / 2) + return FALSE; + + if( ppanHistogram == NULL ) + return TRUE; + + // Fetch the histogram and use it. + int iBucket; + const char *pszHistCounts = CPLGetXMLValue( psHistItem, + "HistCounts", "" ); + + /* Sanity check to test consistency of BucketCount and HistCounts */ + if( strlen(pszHistCounts) < 2 * (size_t)(*pnBuckets) -1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "HistCounts content isn't consistent with BucketCount value"); + return FALSE; + } + + *ppanHistogram = (GUIntBig *) VSICalloc(sizeof(GUIntBig),*pnBuckets); + if (*ppanHistogram == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate memory for %d buckets", *pnBuckets); + return FALSE; + } + + for( iBucket = 0; iBucket < *pnBuckets; iBucket++ ) + { + (*ppanHistogram)[iBucket] = CPLAtoGIntBig(pszHistCounts); + + // skip to next number. + while( *pszHistCounts != '\0' && *pszHistCounts != '|' ) + pszHistCounts++; + if( *pszHistCounts == '|' ) + pszHistCounts++; + } + + return TRUE; +} + +/************************************************************************/ +/* PamFindMatchingHistogram() */ +/************************************************************************/ +CPLXMLNode * +PamFindMatchingHistogram( CPLXMLNode *psSavedHistograms, + double dfMin, double dfMax, int nBuckets, + int bIncludeOutOfRange, int bApproxOK ) + +{ + if( psSavedHistograms == NULL ) + return NULL; + + CPLXMLNode *psXMLHist; + for( psXMLHist = psSavedHistograms->psChild; + psXMLHist != NULL; psXMLHist = psXMLHist->psNext ) + { + if( psXMLHist->eType != CXT_Element + || !EQUAL(psXMLHist->pszValue,"HistItem") ) + continue; + + double dfHistMin = CPLAtof(CPLGetXMLValue( psXMLHist, "HistMin", "0")); + double dfHistMax = CPLAtof(CPLGetXMLValue( psXMLHist, "HistMax", "0")); + + if( !(ARE_REAL_EQUAL(dfHistMin, dfMin)) + || !(ARE_REAL_EQUAL(dfHistMax, dfMax)) + || atoi(CPLGetXMLValue( psXMLHist, + "BucketCount","0")) != nBuckets + || !atoi(CPLGetXMLValue( psXMLHist, + "IncludeOutOfRange","0")) != !bIncludeOutOfRange + || (!bApproxOK && atoi(CPLGetXMLValue( psXMLHist, + "Approximate","0"))) ) + + continue; + + return psXMLHist; + } + + return NULL; +} + +/************************************************************************/ +/* PamHistogramToXMLTree() */ +/************************************************************************/ + +CPLXMLNode * +PamHistogramToXMLTree( double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApprox ) + +{ + char *pszHistCounts; + int iBucket, iHistOffset; + CPLXMLNode *psXMLHist; + CPLString oFmt; + + if( nBuckets > (INT_MAX - 10) / 12 ) + return NULL; + + pszHistCounts = (char *) VSIMalloc(12 * nBuckets + 10); + if( pszHistCounts == NULL ) + return NULL; + + psXMLHist = CPLCreateXMLNode( NULL, CXT_Element, "HistItem" ); + + CPLSetXMLValue( psXMLHist, "HistMin", + oFmt.Printf( "%.16g", dfMin )); + CPLSetXMLValue( psXMLHist, "HistMax", + oFmt.Printf( "%.16g", dfMax )); + CPLSetXMLValue( psXMLHist, "BucketCount", + oFmt.Printf( "%d", nBuckets )); + CPLSetXMLValue( psXMLHist, "IncludeOutOfRange", + oFmt.Printf( "%d", bIncludeOutOfRange )); + CPLSetXMLValue( psXMLHist, "Approximate", + oFmt.Printf( "%d", bApprox )); + + iHistOffset = 0; + pszHistCounts[0] = '\0'; + for( iBucket = 0; iBucket < nBuckets; iBucket++ ) + { + sprintf( pszHistCounts + iHistOffset, CPL_FRMT_GUIB, panHistogram[iBucket] ); + if( iBucket < nBuckets-1 ) + strcat( pszHistCounts + iHistOffset, "|" ); + iHistOffset += strlen(pszHistCounts+iHistOffset); + } + + CPLSetXMLValue( psXMLHist, "HistCounts", pszHistCounts ); + CPLFree( pszHistCounts ); + + return psXMLHist; +} + +/************************************************************************/ +/* GetHistogram() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::GetHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + PamInitialize(); + + if( psPam == NULL ) + return GDALRasterBand::GetHistogram( dfMin, dfMax, + nBuckets, panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Check if we have a matching histogram. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psHistItem; + + psHistItem = PamFindMatchingHistogram( psPam->psSavedHistograms, + dfMin, dfMax, nBuckets, + bIncludeOutOfRange, bApproxOK ); + if( psHistItem != NULL ) + { + GUIntBig *panTempHist = NULL; + + if( PamParseHistogram( psHistItem, &dfMin, &dfMax, &nBuckets, + &panTempHist, + &bIncludeOutOfRange, &bApproxOK ) ) + { + memcpy( panHistogram, panTempHist, sizeof(GUIntBig) * nBuckets ); + CPLFree( panTempHist ); + return CE_None; + } + } + +/* -------------------------------------------------------------------- */ +/* We don't have an existing histogram matching the request, so */ +/* generate one manually. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + eErr = GDALRasterBand::GetHistogram( dfMin, dfMax, + nBuckets, panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Save an XML description of this histogram. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None ) + { + CPLXMLNode *psXMLHist; + + psXMLHist = PamHistogramToXMLTree( dfMin, dfMax, nBuckets, + panHistogram, + bIncludeOutOfRange, bApproxOK ); + if( psXMLHist != NULL ) + { + psPam->poParentDS->MarkPamDirty(); + + if( psPam->psSavedHistograms == NULL ) + psPam->psSavedHistograms = CPLCreateXMLNode( NULL, CXT_Element, + "Histograms" ); + + CPLAddXMLChild( psPam->psSavedHistograms, psXMLHist ); + } + } + + return eErr; +} + +/************************************************************************/ +/* SetDefaultHistogram() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetDefaultHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram) + +{ + CPLXMLNode *psNode; + + PamInitialize(); + + if( psPam == NULL ) + return GDALRasterBand::SetDefaultHistogram( dfMin, dfMax, + nBuckets, panHistogram ); + +/* -------------------------------------------------------------------- */ +/* Do we have a matching histogram we should replace? */ +/* -------------------------------------------------------------------- */ + psNode = PamFindMatchingHistogram( psPam->psSavedHistograms, + dfMin, dfMax, nBuckets, + TRUE, TRUE ); + if( psNode != NULL ) + { + /* blow this one away */ + CPLRemoveXMLChild( psPam->psSavedHistograms, psNode ); + CPLDestroyXMLNode( psNode ); + } + +/* -------------------------------------------------------------------- */ +/* Translate into a histogram XML tree. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psHistItem; + + psHistItem = PamHistogramToXMLTree( dfMin, dfMax, nBuckets, + panHistogram, TRUE, FALSE ); + if( psHistItem == NULL ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Insert our new default histogram at the front of the */ +/* histogram list so that it will be the default histogram. */ +/* -------------------------------------------------------------------- */ + psPam->poParentDS->MarkPamDirty(); + + if( psPam->psSavedHistograms == NULL ) + psPam->psSavedHistograms = CPLCreateXMLNode( NULL, CXT_Element, + "Histograms" ); + + psHistItem->psNext = psPam->psSavedHistograms->psChild; + psPam->psSavedHistograms->psChild = psHistItem; + + return CE_None; +} + +/************************************************************************/ +/* GetDefaultHistogram() */ +/************************************************************************/ + +CPLErr +GDALPamRasterBand::GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig **ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + if( psPam && psPam->psSavedHistograms != NULL ) + { + CPLXMLNode *psXMLHist; + + for( psXMLHist = psPam->psSavedHistograms->psChild; + psXMLHist != NULL; psXMLHist = psXMLHist->psNext ) + { + int bApprox, bIncludeOutOfRange; + + if( psXMLHist->eType != CXT_Element + || !EQUAL(psXMLHist->pszValue,"HistItem") ) + continue; + + if( PamParseHistogram( psXMLHist, pdfMin, pdfMax, pnBuckets, + ppanHistogram, &bIncludeOutOfRange, + &bApprox ) ) + return CE_None; + else + return CE_Failure; + } + } + + return GDALRasterBand::GetDefaultHistogram( pdfMin, pdfMax, pnBuckets, + ppanHistogram, bForce, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* GetDefaultRAT() */ +/************************************************************************/ + +GDALRasterAttributeTable *GDALPamRasterBand::GetDefaultRAT() + +{ + PamInitialize(); + + if( psPam == NULL ) + return GDALRasterBand::GetDefaultRAT(); + + return psPam->poDefaultRAT; +} + +/************************************************************************/ +/* SetDefaultRAT() */ +/************************************************************************/ + +CPLErr GDALPamRasterBand::SetDefaultRAT( const GDALRasterAttributeTable *poRAT) + +{ + PamInitialize(); + + if( psPam == NULL ) + return GDALRasterBand::SetDefaultRAT( poRAT ); + + psPam->poParentDS->MarkPamDirty(); + + if( psPam->poDefaultRAT != NULL ) + { + delete psPam->poDefaultRAT; + psPam->poDefaultRAT = NULL; + } + + if( poRAT == NULL ) + psPam->poDefaultRAT = NULL; + else + psPam->poDefaultRAT = poRAT->Clone(); + + return CE_None; +} diff --git a/bazaar/plugin/gdal/gcore/gdalproxydataset.cpp b/bazaar/plugin/gdal/gcore/gdalproxydataset.cpp new file mode 100644 index 000000000..bd6d67b1c --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalproxydataset.cpp @@ -0,0 +1,305 @@ +/****************************************************************************** + * $Id: gdalproxydataset.cpp 28899 2015-04-14 09:27:00Z rouault $ + * + * Project: GDAL Core + * Purpose: A dataset and raster band classes that act as proxy for underlying + * GDALDataset* and GDALRasterBand* + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_proxy.h" + +CPL_CVSID("$Id: gdalproxydataset.cpp 28899 2015-04-14 09:27:00Z rouault $"); + +/* ******************************************************************** */ +/* GDALProxyDataset */ +/* ******************************************************************** */ + +#define D_PROXY_METHOD_WITH_RET(retType, retErrValue, methodName, argList, argParams) \ +retType GDALProxyDataset::methodName argList \ +{ \ + retType ret; \ + GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); \ + if (poUnderlyingDataset) \ + { \ + ret = poUnderlyingDataset->methodName argParams; \ + UnrefUnderlyingDataset(poUnderlyingDataset); \ + } \ + else \ + { \ + ret = retErrValue; \ + } \ + return ret; \ +} + + +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, IRasterIO, + ( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg), + ( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, psExtraArg )) + + +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, IBuildOverviews, + ( const char *pszResampling, + int nOverviews, int *panOverviewList, + int nListBands, int *panBandList, + GDALProgressFunc pfnProgress, + void * pProgressData ), + ( pszResampling, nOverviews, panOverviewList, + nListBands, panBandList, pfnProgress, pProgressData )) + +void GDALProxyDataset::FlushCache() +{ + GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); + if (poUnderlyingDataset) + { + poUnderlyingDataset->FlushCache(); + UnrefUnderlyingDataset(poUnderlyingDataset); + } +} + +D_PROXY_METHOD_WITH_RET(char**, NULL, GetMetadataDomainList, (), ()) +D_PROXY_METHOD_WITH_RET(char**, NULL, GetMetadata, (const char * pszDomain), (pszDomain)) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetMetadata, + (char ** papszMetadata, const char * pszDomain), + (papszMetadata, pszDomain)) +D_PROXY_METHOD_WITH_RET(const char*, NULL, GetMetadataItem, + (const char * pszName, const char * pszDomain), + (pszName, pszDomain)) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetMetadataItem, + (const char * pszName, const char * pszValue, const char * pszDomain), + (pszName, pszValue, pszDomain)) + +D_PROXY_METHOD_WITH_RET(const char *, NULL, GetProjectionRef, (), ()) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetProjection, (const char* pszProjection), (pszProjection)) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, GetGeoTransform, (double* padfGeoTransform), (padfGeoTransform)) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetGeoTransform, (double* padfGeoTransform), (padfGeoTransform)) + +D_PROXY_METHOD_WITH_RET(void *, NULL, GetInternalHandle, ( const char * arg1), (arg1)) +D_PROXY_METHOD_WITH_RET(GDALDriver *, NULL, GetDriver, (), ()) +D_PROXY_METHOD_WITH_RET(char **, NULL, GetFileList, (), ()) +D_PROXY_METHOD_WITH_RET(int, 0, GetGCPCount, (), ()) +D_PROXY_METHOD_WITH_RET(const char *, NULL, GetGCPProjection, (), ()) +D_PROXY_METHOD_WITH_RET(const GDAL_GCP *, NULL, GetGCPs, (), ()) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetGCPs, + (int nGCPCount, const GDAL_GCP *pasGCPList, + const char *pszGCPProjection), + (nGCPCount, pasGCPList, pszGCPProjection)) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, AdviseRead, + ( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, + int nBandCount, int *panBandList, + char **papszOptions ), + (nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, eDT, nBandCount, panBandList, papszOptions)) +D_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, CreateMaskBand, ( int nFlags ), (nFlags)) + +/************************************************************************/ +/* UnrefUnderlyingDataset() */ +/************************************************************************/ + +void GDALProxyDataset::UnrefUnderlyingDataset(CPL_UNUSED GDALDataset* poUnderlyingDataset) +{ +} + +/* ******************************************************************** */ +/* GDALProxyRasterBand */ +/* ******************************************************************** */ + + +#define RB_PROXY_METHOD_WITH_RET(retType, retErrValue, methodName, argList, argParams) \ +retType GDALProxyRasterBand::methodName argList \ +{ \ + retType ret; \ + GDALRasterBand* poSrcBand = RefUnderlyingRasterBand(); \ + if (poSrcBand) \ + { \ + ret = poSrcBand->methodName argParams; \ + UnrefUnderlyingRasterBand(poSrcBand); \ + } \ + else \ + { \ + ret = retErrValue; \ + } \ + return ret; \ +} + + +#define RB_PROXY_METHOD_WITH_RET_WITH_INIT_BLOCK(retType, retErrValue, methodName, argList, argParams) \ +retType GDALProxyRasterBand::methodName argList \ +{ \ + retType ret; \ + GDALRasterBand* poSrcBand = RefUnderlyingRasterBand(); \ + if (poSrcBand) \ + { \ + if( !poSrcBand->InitBlockInfo() ) \ + ret = CE_Failure; \ + else \ + ret = poSrcBand->methodName argParams; \ + UnrefUnderlyingRasterBand(poSrcBand); \ + } \ + else \ + { \ + ret = retErrValue; \ + } \ + return ret; \ +} + +RB_PROXY_METHOD_WITH_RET_WITH_INIT_BLOCK(CPLErr, CE_Failure, IReadBlock, + ( int nXBlockOff, int nYBlockOff, void* pImage), + (nXBlockOff, nYBlockOff, pImage) ) +RB_PROXY_METHOD_WITH_RET_WITH_INIT_BLOCK(CPLErr, CE_Failure, IWriteBlock, + ( int nXBlockOff, int nYBlockOff, void* pImage), + (nXBlockOff, nYBlockOff, pImage) ) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, IRasterIO, + ( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ), + (eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg ) ) + +RB_PROXY_METHOD_WITH_RET(char**, NULL, GetMetadataDomainList, (), ()) +RB_PROXY_METHOD_WITH_RET(char**, NULL, GetMetadata, (const char * pszDomain), (pszDomain)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetMetadata, + (char ** papszMetadata, const char * pszDomain), + (papszMetadata, pszDomain)) +RB_PROXY_METHOD_WITH_RET(const char*, NULL, GetMetadataItem, + (const char * pszName, const char * pszDomain), + (pszName, pszDomain)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetMetadataItem, + (const char * pszName, const char * pszValue, const char * pszDomain), + (pszName, pszValue, pszDomain)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, FlushCache, (), ()) +RB_PROXY_METHOD_WITH_RET(char**, NULL, GetCategoryNames, (), ()) +RB_PROXY_METHOD_WITH_RET(double, 0, GetNoDataValue, (int *pbSuccess), (pbSuccess)) +RB_PROXY_METHOD_WITH_RET(double, 0, GetMinimum, (int *pbSuccess), (pbSuccess)) +RB_PROXY_METHOD_WITH_RET(double, 0, GetMaximum, (int *pbSuccess), (pbSuccess)) +RB_PROXY_METHOD_WITH_RET(double, 0, GetOffset, (int *pbSuccess), (pbSuccess)) +RB_PROXY_METHOD_WITH_RET(double, 0, GetScale, (int *pbSuccess), (pbSuccess)) +RB_PROXY_METHOD_WITH_RET(const char*, NULL, GetUnitType, (), ()) +RB_PROXY_METHOD_WITH_RET(GDALColorInterp, GCI_Undefined, GetColorInterpretation, (), ()) +RB_PROXY_METHOD_WITH_RET(GDALColorTable*, NULL, GetColorTable, (), ()) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, Fill, + (double dfRealValue, double dfImaginaryValue), + (dfRealValue, dfImaginaryValue)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetCategoryNames, ( char ** arg ), (arg)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetNoDataValue, ( double arg ), (arg)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetColorTable, ( GDALColorTable *arg ), (arg)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetColorInterpretation, + ( GDALColorInterp arg ), (arg)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetOffset, ( double arg ), (arg)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetScale, ( double arg ), (arg)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetUnitType, ( const char * arg ), (arg)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, GetStatistics, + ( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *padfStdDev ), + (bApproxOK, bForce, pdfMin, pdfMax, pdfMean, padfStdDev)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, ComputeStatistics, + ( int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfn, void *pProgressData ), + ( bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev, pfn, pProgressData)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetStatistics, + ( double dfMin, double dfMax, + double dfMean, double dfStdDev ), + (dfMin, dfMax, dfMean, dfStdDev)) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, ComputeRasterMinMax, + ( int arg1, double* arg2 ), (arg1, arg2)) + +RB_PROXY_METHOD_WITH_RET(int, 0, HasArbitraryOverviews, (), ()) +RB_PROXY_METHOD_WITH_RET(int, 0, GetOverviewCount, (), ()) +RB_PROXY_METHOD_WITH_RET(GDALRasterBand*, NULL, GetOverview, (int arg1), (arg1)) +RB_PROXY_METHOD_WITH_RET(GDALRasterBand*, NULL, GetRasterSampleOverview, + (GUIntBig arg1), (arg1)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, BuildOverviews, + (const char * arg1, int arg2, int *arg3, + GDALProgressFunc arg4, void * arg5), + (arg1, arg2, arg3, arg4, arg5)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, AdviseRead, + ( int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ), + (nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, eDT, papszOptions)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, GetHistogram, + ( double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfn, void *pProgressData ), + (dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, + bApproxOK, pfn, pProgressData)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, GetDefaultHistogram, + (double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig ** ppanHistogram, + int bForce, + GDALProgressFunc pfn, void *pProgressData ), + (pdfMin, pdfMax, pnBuckets, ppanHistogram, bForce, + pfn, pProgressData)) + +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetDefaultHistogram, + ( double dfMin, double dfMax, + int nBuckets, GUIntBig * panHistogram ), + (dfMin, dfMax, nBuckets, panHistogram)) + +RB_PROXY_METHOD_WITH_RET(GDALRasterAttributeTable *, NULL, + GetDefaultRAT, (), ()) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, SetDefaultRAT, + ( const GDALRasterAttributeTable * arg1), (arg1)) + +RB_PROXY_METHOD_WITH_RET(GDALRasterBand*, NULL, GetMaskBand, (), ()) +RB_PROXY_METHOD_WITH_RET(int, 0, GetMaskFlags, (), ()) +RB_PROXY_METHOD_WITH_RET(CPLErr, CE_Failure, CreateMaskBand, ( int nFlags ), (nFlags)) + +RB_PROXY_METHOD_WITH_RET(CPLVirtualMem*, NULL, GetVirtualMemAuto, + ( GDALRWFlag eRWFlag, int *pnPixelSpace, GIntBig *pnLineSpace, char **papszOptions ), + (eRWFlag, pnPixelSpace, pnLineSpace, papszOptions) ) + +/************************************************************************/ +/* UnrefUnderlyingRasterBand() */ +/************************************************************************/ + +void GDALProxyRasterBand::UnrefUnderlyingRasterBand(CPL_UNUSED GDALRasterBand* poUnderlyingRasterBand) +{ +} diff --git a/bazaar/plugin/gdal/gcore/gdalproxypool.cpp b/bazaar/plugin/gdal/gcore/gdalproxypool.cpp new file mode 100644 index 000000000..285402f52 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalproxypool.cpp @@ -0,0 +1,1335 @@ +/****************************************************************************** + * $Id: gdalproxypool.cpp 29326 2015-06-10 20:36:31Z rouault $ + * + * Project: GDAL Core + * Purpose: A dataset and raster band classes that differ the opening of the + * underlying dataset in a limited pool of opened datasets. + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_proxy.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: gdalproxypool.cpp 29326 2015-06-10 20:36:31Z rouault $"); + +/* We *must* share the same mutex as the gdaldataset.cpp file, as we are */ +/* doing GDALOpen() calls that can indirectly call GDALOpenShared() on */ +/* an auxiliary dataset ... */ +/* Then we could get dead-locks in multi-threaded use case */ + +/* ******************************************************************** */ +/* GDALDatasetPool */ +/* ******************************************************************** */ + +/* This class is a singleton that maintains a pool of opened datasets */ +/* The cache uses a LRU strategy */ + +class GDALDatasetPool; +static GDALDatasetPool* singleton = NULL; + +void GDALNullifyProxyPoolSingleton() { singleton = NULL; } + +struct _GDALProxyPoolCacheEntry +{ + GIntBig responsiblePID; + char *pszFileName; + GDALDataset *poDS; + + /* Ref count of the cached dataset */ + int refCount; + + GDALProxyPoolCacheEntry* prev; + GDALProxyPoolCacheEntry* next; +}; + +class GDALDatasetPool +{ + private: + /* Ref count of the pool singleton */ + /* Taken by "toplevel" GDALProxyPoolDataset in its constructor and released */ + /* in its destructor. See also refCountOfDisableRefCount for the difference */ + /* between toplevel and inner GDALProxyPoolDataset */ + int refCount; + + int maxSize; + int currentSize; + GDALProxyPoolCacheEntry* firstEntry; + GDALProxyPoolCacheEntry* lastEntry; + + /* This variable prevents a dataset that is going to be opened in GDALDatasetPool::_RefDataset */ + /* from increasing refCount if, during its opening, it creates a GDALProxyPoolDataset */ + /* We increment it before opening or closing a cached dataset and decrement it afterwards */ + /* The typical use case is a VRT made of simple sources that are VRT */ + /* We don't want the "inner" VRT to take a reference on the pool, otherwise there is */ + /* a high chance that this reference will not be dropped and the pool remain ghost */ + int refCountOfDisableRefCount; + + /* Caution : to be sure that we don't run out of entries, size must be at */ + /* least greater or equal than the maximum number of threads */ + GDALDatasetPool(int maxSize); + ~GDALDatasetPool(); + GDALProxyPoolCacheEntry* _RefDataset(const char* pszFileName, + GDALAccess eAccess, + char** papszOpenOptions, + int bShared); + void _CloseDataset(const char* pszFileName, GDALAccess eAccess); + + void ShowContent(); + void CheckLinks(); + + public: + static void Ref(); + static void Unref(); + static GDALProxyPoolCacheEntry* RefDataset(const char* pszFileName, + GDALAccess eAccess, + char** papszOpenOptions, + int bShared); + static void UnrefDataset(GDALProxyPoolCacheEntry* cacheEntry); + static void CloseDataset(const char* pszFileName, GDALAccess eAccess); + + static void PreventDestroy(); + static void ForceDestroy(); +}; + + +/************************************************************************/ +/* GDALDatasetPool() */ +/************************************************************************/ + +GDALDatasetPool::GDALDatasetPool(int maxSize) +{ + this->maxSize = maxSize; + currentSize = 0; + firstEntry = NULL; + lastEntry = NULL; + refCount = 0; + refCountOfDisableRefCount = 0; +} + +/************************************************************************/ +/* ~GDALDatasetPool() */ +/************************************************************************/ + +GDALDatasetPool::~GDALDatasetPool() +{ + GDALProxyPoolCacheEntry* cur = firstEntry; + GIntBig responsiblePID = GDALGetResponsiblePIDForCurrentThread(); + while(cur) + { + GDALProxyPoolCacheEntry* next = cur->next; + CPLFree(cur->pszFileName); + CPLAssert(cur->refCount == 0); + if (cur->poDS) + { + GDALSetResponsiblePIDForCurrentThread(cur->responsiblePID); + GDALClose(cur->poDS); + } + CPLFree(cur); + cur = next; + } + GDALSetResponsiblePIDForCurrentThread(responsiblePID); +} + +/************************************************************************/ +/* ShowContent() */ +/************************************************************************/ + +void GDALDatasetPool::ShowContent() +{ + GDALProxyPoolCacheEntry* cur = firstEntry; + int i = 0; + while(cur) + { + printf("[%d] pszFileName=%s, refCount=%d, responsiblePID=%d\n", + i, cur->pszFileName, cur->refCount, (int)cur->responsiblePID); + i++; + cur = cur->next; + } +} + +/************************************************************************/ +/* CheckLinks() */ +/************************************************************************/ + +void GDALDatasetPool::CheckLinks() +{ + GDALProxyPoolCacheEntry* cur = firstEntry; + int i = 0; + while(cur) + { + CPLAssert(cur == firstEntry || cur->prev->next == cur); + CPLAssert(cur == lastEntry || cur->next->prev == cur); + i++; + CPLAssert(cur->next != NULL || cur == lastEntry); + cur = cur->next; + } + CPLAssert(i == currentSize); +} + +/************************************************************************/ +/* _RefDataset() */ +/************************************************************************/ + +GDALProxyPoolCacheEntry* GDALDatasetPool::_RefDataset(const char* pszFileName, + GDALAccess eAccess, + char** papszOpenOptions, + int bShared) +{ + GDALProxyPoolCacheEntry* cur = firstEntry; + GIntBig responsiblePID = GDALGetResponsiblePIDForCurrentThread(); + GDALProxyPoolCacheEntry* lastEntryWithZeroRefCount = NULL; + + while(cur) + { + GDALProxyPoolCacheEntry* next = cur->next; + + if (strcmp(cur->pszFileName, pszFileName) == 0 && + ((bShared && cur->responsiblePID == responsiblePID) || + (!bShared && cur->refCount == 0)) ) + { + if (cur != firstEntry) + { + /* Move to begin */ + if (cur->next) + cur->next->prev = cur->prev; + else + lastEntry = cur->prev; + cur->prev->next = cur->next; + cur->prev = NULL; + firstEntry->prev = cur; + cur->next = firstEntry; + firstEntry = cur; + +#ifdef DEBUG_PROXY_POOL + CheckLinks(); +#endif + } + + cur->refCount ++; + return cur; + } + + if (cur->refCount == 0) + lastEntryWithZeroRefCount = cur; + + cur = next; + } + + if (currentSize == maxSize) + { + if (lastEntryWithZeroRefCount == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too many threads are running for the current value of the dataset pool size (%d).\n" + "or too many proxy datasets are opened in a cascaded way.\n" + "Try increasing GDAL_MAX_DATASET_POOL_SIZE.", maxSize); + return NULL; + } + + CPLFree(lastEntryWithZeroRefCount->pszFileName); + lastEntryWithZeroRefCount->pszFileName = NULL; + if (lastEntryWithZeroRefCount->poDS) + { + /* Close by pretending we are the thread that GDALOpen'ed this */ + /* dataset */ + GDALSetResponsiblePIDForCurrentThread(lastEntryWithZeroRefCount->responsiblePID); + + refCountOfDisableRefCount ++; + GDALClose(lastEntryWithZeroRefCount->poDS); + refCountOfDisableRefCount --; + + lastEntryWithZeroRefCount->poDS = NULL; + GDALSetResponsiblePIDForCurrentThread(responsiblePID); + } + + /* Recycle this entry for the to-be-openeded dataset and */ + /* moves it to the top of the list */ + if (lastEntryWithZeroRefCount->prev) + lastEntryWithZeroRefCount->prev->next = lastEntryWithZeroRefCount->next; + else { + CPLAssert(0); + } + if (lastEntryWithZeroRefCount->next) + lastEntryWithZeroRefCount->next->prev = lastEntryWithZeroRefCount->prev; + else + { + CPLAssert(lastEntryWithZeroRefCount == lastEntry); + lastEntry->prev->next = NULL; + lastEntry = lastEntry->prev; + } + lastEntryWithZeroRefCount->prev = NULL; + lastEntryWithZeroRefCount->next = firstEntry; + firstEntry->prev = lastEntryWithZeroRefCount; + cur = firstEntry = lastEntryWithZeroRefCount; +#ifdef DEBUG_PROXY_POOL + CheckLinks(); +#endif + } + else + { + /* Prepend */ + cur = (GDALProxyPoolCacheEntry*) CPLMalloc(sizeof(GDALProxyPoolCacheEntry)); + if (lastEntry == NULL) + lastEntry = cur; + cur->prev = NULL; + cur->next = firstEntry; + if (firstEntry) + firstEntry->prev = cur; + firstEntry = cur; + currentSize ++; +#ifdef DEBUG_PROXY_POOL + CheckLinks(); +#endif + } + + cur->pszFileName = CPLStrdup(pszFileName); + cur->responsiblePID = responsiblePID; + cur->refCount = 1; + + refCountOfDisableRefCount ++; + int nFlag = ((eAccess == GA_Update) ? GDAL_OF_UPDATE : GDAL_OF_READONLY) | GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR; + cur->poDS = (GDALDataset*) GDALOpenEx( pszFileName, nFlag, NULL, + (const char* const* )papszOpenOptions, NULL ); + refCountOfDisableRefCount --; + + return cur; +} + +/************************************************************************/ +/* _CloseDataset() */ +/************************************************************************/ + +void GDALDatasetPool::_CloseDataset(const char* pszFileName, GDALAccess eAccess) +{ + GDALProxyPoolCacheEntry* cur = firstEntry; + GIntBig responsiblePID = GDALGetResponsiblePIDForCurrentThread(); + + while(cur) + { + GDALProxyPoolCacheEntry* next = cur->next; + + if (strcmp(cur->pszFileName, pszFileName) == 0 && cur->refCount == 0 && + cur->poDS != NULL ) + { + /* Close by pretending we are the thread that GDALOpen'ed this */ + /* dataset */ + GDALSetResponsiblePIDForCurrentThread(cur->responsiblePID); + + refCountOfDisableRefCount ++; + GDALClose(cur->poDS); + refCountOfDisableRefCount --; + + GDALSetResponsiblePIDForCurrentThread(responsiblePID); + + cur->poDS = NULL; + cur->pszFileName[0] = '\0'; + break; + } + + cur = next; + } +} + +/************************************************************************/ +/* Ref() */ +/************************************************************************/ + +void GDALDatasetPool::Ref() +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + if (singleton == NULL) + { + int maxSize = atoi(CPLGetConfigOption("GDAL_MAX_DATASET_POOL_SIZE", "100")); + if (maxSize < 2 || maxSize > 1000) + maxSize = 100; + singleton = new GDALDatasetPool(maxSize); + } + if (singleton->refCountOfDisableRefCount == 0) + singleton->refCount++; +} + +/* keep that in sync with gdaldrivermanager.cpp */ +void GDALDatasetPool::PreventDestroy() +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + if (! singleton) + return; + singleton->refCountOfDisableRefCount ++; +} + +/* keep that in sync with gdaldrivermanager.cpp */ +void GDALDatasetPoolPreventDestroy() +{ + GDALDatasetPool::PreventDestroy(); +} + + +/************************************************************************/ +/* Unref() */ +/************************************************************************/ + +void GDALDatasetPool::Unref() +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + if (! singleton) + { + CPLAssert(0); + return; + } + if (singleton->refCountOfDisableRefCount == 0) + { + singleton->refCount--; + if (singleton->refCount == 0) + { + delete singleton; + singleton = NULL; + } + } +} + +/* keep that in sync with gdaldrivermanager.cpp */ +void GDALDatasetPool::ForceDestroy() +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + if (! singleton) + return; + singleton->refCountOfDisableRefCount --; + CPLAssert(singleton->refCountOfDisableRefCount == 0); + singleton->refCount = 0; + delete singleton; + singleton = NULL; +} + +/* keep that in sync with gdaldrivermanager.cpp */ +void GDALDatasetPoolForceDestroy() +{ + GDALDatasetPool::ForceDestroy(); +} + +/************************************************************************/ +/* RefDataset() */ +/************************************************************************/ + +GDALProxyPoolCacheEntry* GDALDatasetPool::RefDataset(const char* pszFileName, + GDALAccess eAccess, + char** papszOpenOptions, + int bShared) +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + return singleton->_RefDataset(pszFileName, eAccess, papszOpenOptions, bShared); +} + +/************************************************************************/ +/* UnrefDataset() */ +/************************************************************************/ + +void GDALDatasetPool::UnrefDataset(GDALProxyPoolCacheEntry* cacheEntry) +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + cacheEntry->refCount --; +} + +/************************************************************************/ +/* CloseDataset() */ +/************************************************************************/ + +void GDALDatasetPool::CloseDataset(const char* pszFileName, GDALAccess eAccess) +{ + CPLMutexHolderD( GDALGetphDLMutex() ); + singleton->_CloseDataset(pszFileName, eAccess); +} + +CPL_C_START + +typedef struct +{ + char* pszDomain; + char** papszMetadata; +} GetMetadataElt; + +static +unsigned long hash_func_get_metadata(const void* _elt) +{ + GetMetadataElt* elt = (GetMetadataElt*) _elt; + return CPLHashSetHashStr(elt->pszDomain); +} + +static +int equal_func_get_metadata(const void* _elt1, const void* _elt2) +{ + GetMetadataElt* elt1 = (GetMetadataElt*) _elt1; + GetMetadataElt* elt2 = (GetMetadataElt*) _elt2; + return CPLHashSetEqualStr(elt1->pszDomain, elt2->pszDomain); +} + +static +void free_func_get_metadata(void* _elt) +{ + GetMetadataElt* elt = (GetMetadataElt*) _elt; + CPLFree(elt->pszDomain); + CSLDestroy(elt->papszMetadata); +} + + +typedef struct +{ + char* pszName; + char* pszDomain; + char* pszMetadataItem; +} GetMetadataItemElt; + +static +unsigned long hash_func_get_metadata_item(const void* _elt) +{ + GetMetadataItemElt* elt = (GetMetadataItemElt*) _elt; + return CPLHashSetHashStr(elt->pszName) ^ CPLHashSetHashStr(elt->pszDomain); +} + +static +int equal_func_get_metadata_item(const void* _elt1, const void* _elt2) +{ + GetMetadataItemElt* elt1 = (GetMetadataItemElt*) _elt1; + GetMetadataItemElt* elt2 = (GetMetadataItemElt*) _elt2; + return CPLHashSetEqualStr(elt1->pszName, elt2->pszName) && + CPLHashSetEqualStr(elt1->pszDomain, elt2->pszDomain); +} + +static +void free_func_get_metadata_item(void* _elt) +{ + GetMetadataItemElt* elt = (GetMetadataItemElt*) _elt; + CPLFree(elt->pszName); + CPLFree(elt->pszDomain); + CPLFree(elt->pszMetadataItem); +} + +CPL_C_END + +/* ******************************************************************** */ +/* GDALProxyPoolDataset */ +/* ******************************************************************** */ + +/* Note : the bShared parameter must be used with caution. You can */ +/* set it to TRUE for being used as a VRT source : in that case, */ +/* VRTSimpleSource will take care of destroying it when there are no */ +/* reference to it (in VRTSimpleSource::~VRTSimpleSource()) */ +/* However this will not be registered as a genuine shared dataset, like it */ +/* would have been with MarkAsShared(). But MarkAsShared() is not usable for */ +/* GDALProxyPoolDataset objects, as they share the same description as their */ +/* underlying dataset. So *NEVER* call MarkAsShared() on a GDALProxyPoolDataset */ +/* object */ + +GDALProxyPoolDataset::GDALProxyPoolDataset(const char* pszSourceDatasetDescription, + int nRasterXSize, int nRasterYSize, + GDALAccess eAccess, int bShared, + const char * pszProjectionRef, + double * padfGeoTransform) +{ + GDALDatasetPool::Ref(); + + SetDescription(pszSourceDatasetDescription); + + this->nRasterXSize = nRasterXSize; + this->nRasterYSize = nRasterYSize; + this->eAccess = eAccess; + + this->bShared = bShared; + + this->responsiblePID = GDALGetResponsiblePIDForCurrentThread(); + + if (pszProjectionRef) + { + this->pszProjectionRef = NULL; + bHasSrcProjection = FALSE; + } + else + { + this->pszProjectionRef = CPLStrdup(pszProjectionRef); + bHasSrcProjection = TRUE; + } + if (padfGeoTransform) + { + memcpy(adfGeoTransform, padfGeoTransform,6 * sizeof(double)); + bHasSrcGeoTransform = TRUE; + } + else + { + adfGeoTransform[0] = 0; + adfGeoTransform[1] = 1; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = 0; + adfGeoTransform[4] = 0; + adfGeoTransform[5] = 1; + bHasSrcGeoTransform = FALSE; + } + + pszGCPProjection = NULL; + nGCPCount = 0; + pasGCPList = NULL; + metadataSet = NULL; + metadataItemSet = NULL; + cacheEntry = NULL; +} + +/************************************************************************/ +/* ~GDALProxyPoolDataset() */ +/************************************************************************/ + +GDALProxyPoolDataset::~GDALProxyPoolDataset() +{ + if( !bShared ) + { + GDALDatasetPool::CloseDataset(GetDescription(), eAccess); + } + /* See comment in constructor */ + /* It is not really a genuine shared dataset, so we don't */ + /* want ~GDALDataset() to try to release it from its */ + /* shared dataset hashset. This will save a */ + /* "Should not happen. Cannot find %s, this=%p in phSharedDatasetSet" debug message */ + bShared = FALSE; + + CPLFree(pszProjectionRef); + CPLFree(pszGCPProjection); + if (nGCPCount) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + } + if (metadataSet) + CPLHashSetDestroy(metadataSet); + if (metadataItemSet) + CPLHashSetDestroy(metadataItemSet); + + GDALDatasetPool::Unref(); +} + +/************************************************************************/ +/* SetOpenOptions() */ +/************************************************************************/ + +void GDALProxyPoolDataset::SetOpenOptions(char** papszOpenOptions) +{ + CPLAssert(this->papszOpenOptions == NULL); + this->papszOpenOptions = CSLDuplicate(papszOpenOptions); +} + +/************************************************************************/ +/* AddSrcBandDescription() */ +/************************************************************************/ + +void GDALProxyPoolDataset::AddSrcBandDescription( GDALDataType eDataType, int nBlockXSize, int nBlockYSize) +{ + SetBand(nBands + 1, new GDALProxyPoolRasterBand(this, nBands + 1, eDataType, nBlockXSize, nBlockYSize)); +} + +/************************************************************************/ +/* RefUnderlyingDataset() */ +/************************************************************************/ + +GDALDataset* GDALProxyPoolDataset::RefUnderlyingDataset() +{ + /* We pretend that the current thread is responsiblePID, that is */ + /* to say the thread that created that GDALProxyPoolDataset object. */ + /* This is for the case when a GDALProxyPoolDataset is created by a */ + /* thread and used by other threads. These other threads, when doing actual */ + /* IO, will come there and potentially open the underlying dataset. */ + /* By doing this, they can indirectly call GDALOpenShared() on .aux file */ + /* for example. So this call to GDALOpenShared() must occur as if it */ + /* was done by the creating thread, otherwise it will not be correctly closed afterwards... */ + /* To make a long story short : this is necessary when warping with ChunkAndWarpMulti */ + /* a VRT of GeoTIFFs that have associated .aux files */ + GIntBig curResponsiblePID = GDALGetResponsiblePIDForCurrentThread(); + GDALSetResponsiblePIDForCurrentThread(responsiblePID); + cacheEntry = GDALDatasetPool::RefDataset(GetDescription(), eAccess, papszOpenOptions, + GetShared()); + GDALSetResponsiblePIDForCurrentThread(curResponsiblePID); + if (cacheEntry != NULL) + { + if (cacheEntry->poDS != NULL) + return cacheEntry->poDS; + else + GDALDatasetPool::UnrefDataset(cacheEntry); + } + return NULL; +} + +/************************************************************************/ +/* UnrefUnderlyingDataset() */ +/************************************************************************/ + +void GDALProxyPoolDataset::UnrefUnderlyingDataset(CPL_UNUSED GDALDataset* poUnderlyingDataset) +{ + if (cacheEntry != NULL) + { + CPLAssert(cacheEntry->poDS == poUnderlyingDataset); + if (cacheEntry->poDS != NULL) + GDALDatasetPool::UnrefDataset(cacheEntry); + } +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr GDALProxyPoolDataset::SetProjection(const char* pszProjectionRef) +{ + bHasSrcProjection = FALSE; + return GDALProxyDataset::SetProjection(pszProjectionRef); +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char *GDALProxyPoolDataset::GetProjectionRef() +{ + if (bHasSrcProjection) + return pszProjectionRef; + else + return GDALProxyDataset::GetProjectionRef(); +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr GDALProxyPoolDataset::SetGeoTransform( double * padfGeoTransform ) +{ + bHasSrcGeoTransform = FALSE; + return GDALProxyDataset::SetGeoTransform(padfGeoTransform); +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GDALProxyPoolDataset::GetGeoTransform( double * padfGeoTransform ) +{ + if (bHasSrcGeoTransform) + { + memcpy(padfGeoTransform, adfGeoTransform, 6 * sizeof(double)); + return CE_None; + } + else + { + return GDALProxyDataset::GetGeoTransform(padfGeoTransform); + } +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GDALProxyPoolDataset::GetMetadata( const char * pszDomain ) +{ + if (metadataSet == NULL) + metadataSet = CPLHashSetNew(hash_func_get_metadata, + equal_func_get_metadata, + free_func_get_metadata); + + GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); + if (poUnderlyingDataset == NULL) + return NULL; + + char** papszUnderlyingMetadata = poUnderlyingDataset->GetMetadata(pszDomain); + + GetMetadataElt* pElt = (GetMetadataElt*) CPLMalloc(sizeof(GetMetadataElt)); + pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : NULL; + pElt->papszMetadata = CSLDuplicate(papszUnderlyingMetadata); + CPLHashSetInsert(metadataSet, pElt); + + UnrefUnderlyingDataset(poUnderlyingDataset); + + return pElt->papszMetadata; +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *GDALProxyPoolDataset::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if (metadataItemSet == NULL) + metadataItemSet = CPLHashSetNew(hash_func_get_metadata_item, + equal_func_get_metadata_item, + free_func_get_metadata_item); + + GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); + if (poUnderlyingDataset == NULL) + return NULL; + + const char* pszUnderlyingMetadataItem = + poUnderlyingDataset->GetMetadataItem(pszName, pszDomain); + + GetMetadataItemElt* pElt = (GetMetadataItemElt*) CPLMalloc(sizeof(GetMetadataItemElt)); + pElt->pszName = (pszName) ? CPLStrdup(pszName) : NULL; + pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : NULL; + pElt->pszMetadataItem = (pszUnderlyingMetadataItem) ? CPLStrdup(pszUnderlyingMetadataItem) : NULL; + CPLHashSetInsert(metadataItemSet, pElt); + + UnrefUnderlyingDataset(poUnderlyingDataset); + + return pElt->pszMetadataItem; +} + +/************************************************************************/ +/* GetInternalHandle() */ +/************************************************************************/ + +void *GDALProxyPoolDataset::GetInternalHandle( const char * pszRequest) +{ + CPLError(CE_Warning, CPLE_AppDefined, + "GetInternalHandle() cannot be safely called on a proxy pool dataset\n" + "as the returned value may be invalidated at any time.\n"); + return GDALProxyDataset::GetInternalHandle(pszRequest); +} + +/************************************************************************/ +/* GetGCPProjection() */ +/************************************************************************/ + +const char *GDALProxyPoolDataset::GetGCPProjection() +{ + GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); + if (poUnderlyingDataset == NULL) + return NULL; + + CPLFree(pszGCPProjection); + pszGCPProjection = NULL; + + const char* pszUnderlyingGCPProjection = poUnderlyingDataset->GetGCPProjection(); + if (pszUnderlyingGCPProjection) + pszGCPProjection = CPLStrdup(pszUnderlyingGCPProjection); + + UnrefUnderlyingDataset(poUnderlyingDataset); + + return pszGCPProjection; +} + +/************************************************************************/ +/* GetGCPs() */ +/************************************************************************/ + +const GDAL_GCP *GDALProxyPoolDataset::GetGCPs() +{ + GDALDataset* poUnderlyingDataset = RefUnderlyingDataset(); + if (poUnderlyingDataset == NULL) + return NULL; + + if (nGCPCount) + { + GDALDeinitGCPs( nGCPCount, pasGCPList ); + CPLFree( pasGCPList ); + pasGCPList = NULL; + } + + const GDAL_GCP* pasUnderlyingGCPList = poUnderlyingDataset->GetGCPs(); + nGCPCount = poUnderlyingDataset->GetGCPCount(); + if (nGCPCount) + pasGCPList = GDALDuplicateGCPs(nGCPCount, pasUnderlyingGCPList ); + + UnrefUnderlyingDataset(poUnderlyingDataset); + + return pasGCPList; +} + +/************************************************************************/ +/* GDALProxyPoolDatasetCreate() */ +/************************************************************************/ + +GDALProxyPoolDatasetH GDALProxyPoolDatasetCreate(const char* pszSourceDatasetDescription, + int nRasterXSize, int nRasterYSize, + GDALAccess eAccess, int bShared, + const char * pszProjectionRef, + double * padfGeoTransform) +{ + return (GDALProxyPoolDatasetH) + new GDALProxyPoolDataset(pszSourceDatasetDescription, + nRasterXSize, nRasterYSize, + eAccess, bShared, + pszProjectionRef, padfGeoTransform); +} + +/************************************************************************/ +/* GDALProxyPoolDatasetDelete() */ +/************************************************************************/ + +void CPL_DLL GDALProxyPoolDatasetDelete(GDALProxyPoolDatasetH hProxyPoolDataset) +{ + delete (GDALProxyPoolDataset*)hProxyPoolDataset; +} + +/************************************************************************/ +/* GDALProxyPoolDatasetAddSrcBandDescription() */ +/************************************************************************/ + +void GDALProxyPoolDatasetAddSrcBandDescription( GDALProxyPoolDatasetH hProxyPoolDataset, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize) +{ + ((GDALProxyPoolDataset*)hProxyPoolDataset)-> + AddSrcBandDescription(eDataType, nBlockXSize, nBlockYSize); +} + +/* ******************************************************************** */ +/* GDALProxyPoolRasterBand() */ +/* ******************************************************************** */ + +GDALProxyPoolRasterBand::GDALProxyPoolRasterBand(GDALProxyPoolDataset* poDS, int nBand, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize) +{ + this->poDS = poDS; + this->nBand = nBand; + this->eDataType = eDataType; + this->nRasterXSize = poDS->GetRasterXSize(); + this->nRasterYSize = poDS->GetRasterYSize(); + this->nBlockXSize = nBlockXSize; + this->nBlockYSize = nBlockYSize; + + Init(); +} + +/* ******************************************************************** */ +/* GDALProxyPoolRasterBand() */ +/* ******************************************************************** */ + +GDALProxyPoolRasterBand::GDALProxyPoolRasterBand(GDALProxyPoolDataset* poDS, + GDALRasterBand* poUnderlyingRasterBand) +{ + this->poDS = poDS; + this->nBand = poUnderlyingRasterBand->GetBand(); + this->eDataType = poUnderlyingRasterBand->GetRasterDataType(); + this->nRasterXSize = poUnderlyingRasterBand->GetXSize(); + this->nRasterYSize = poUnderlyingRasterBand->GetYSize(); + poUnderlyingRasterBand->GetBlockSize(&nBlockXSize, &nBlockYSize); + + Init(); +} + +/* ******************************************************************** */ + /* Init() */ +/* ******************************************************************** */ + +void GDALProxyPoolRasterBand::Init() +{ + metadataSet = NULL; + metadataItemSet = NULL; + pszUnitType = NULL; + papszCategoryNames = NULL; + poColorTable = NULL; + + nSizeProxyOverviewRasterBand = 0; + papoProxyOverviewRasterBand = NULL; + poProxyMaskBand = NULL; +} + +/* ******************************************************************** */ +/* ~GDALProxyPoolRasterBand() */ +/* ******************************************************************** */ +GDALProxyPoolRasterBand::~GDALProxyPoolRasterBand() +{ + if (metadataSet) + CPLHashSetDestroy(metadataSet); + if (metadataItemSet) + CPLHashSetDestroy(metadataItemSet); + CPLFree(pszUnitType); + CSLDestroy(papszCategoryNames); + if (poColorTable) + delete poColorTable; + + int i; + for(i=0;iRefUnderlyingDataset(); + if (poUnderlyingDataset == NULL) + return NULL; + + GDALRasterBand* poBand = poUnderlyingDataset->GetRasterBand(nBand); + if (poBand == NULL) + { + ((GDALProxyPoolDataset*)poDS)->UnrefUnderlyingDataset(poUnderlyingDataset); + } + + return poBand; +} + +/************************************************************************/ +/* UnrefUnderlyingRasterBand() */ +/************************************************************************/ + +void GDALProxyPoolRasterBand::UnrefUnderlyingRasterBand(GDALRasterBand* poUnderlyingRasterBand) +{ + if (poUnderlyingRasterBand) + ((GDALProxyPoolDataset*)poDS)->UnrefUnderlyingDataset(poUnderlyingRasterBand->GetDataset()); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GDALProxyPoolRasterBand::GetMetadata( const char * pszDomain ) +{ + if (metadataSet == NULL) + metadataSet = CPLHashSetNew(hash_func_get_metadata, + equal_func_get_metadata, + free_func_get_metadata); + + GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); + if (poUnderlyingRasterBand == NULL) + return NULL; + + char** papszUnderlyingMetadata = poUnderlyingRasterBand->GetMetadata(pszDomain); + + GetMetadataElt* pElt = (GetMetadataElt*) CPLMalloc(sizeof(GetMetadataElt)); + pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : NULL; + pElt->papszMetadata = CSLDuplicate(papszUnderlyingMetadata); + CPLHashSetInsert(metadataSet, pElt); + + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + + return pElt->papszMetadata; +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *GDALProxyPoolRasterBand::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if (metadataItemSet == NULL) + metadataItemSet = CPLHashSetNew(hash_func_get_metadata_item, + equal_func_get_metadata_item, + free_func_get_metadata_item); + + GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); + if (poUnderlyingRasterBand == NULL) + return NULL; + + const char* pszUnderlyingMetadataItem = + poUnderlyingRasterBand->GetMetadataItem(pszName, pszDomain); + + GetMetadataItemElt* pElt = (GetMetadataItemElt*) CPLMalloc(sizeof(GetMetadataItemElt)); + pElt->pszName = (pszName) ? CPLStrdup(pszName) : NULL; + pElt->pszDomain = (pszDomain) ? CPLStrdup(pszDomain) : NULL; + pElt->pszMetadataItem = (pszUnderlyingMetadataItem) ? CPLStrdup(pszUnderlyingMetadataItem) : NULL; + CPLHashSetInsert(metadataItemSet, pElt); + + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + + return pElt->pszMetadataItem; +} + +/* ******************************************************************** */ +/* GetCategoryNames() */ +/* ******************************************************************** */ + +char **GDALProxyPoolRasterBand::GetCategoryNames() +{ + GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); + if (poUnderlyingRasterBand == NULL) + return NULL; + + CSLDestroy(papszCategoryNames); + papszCategoryNames = NULL; + + char** papszUnderlyingCategoryNames = poUnderlyingRasterBand->GetCategoryNames(); + if (papszUnderlyingCategoryNames) + papszCategoryNames = CSLDuplicate(papszUnderlyingCategoryNames); + + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + + return papszCategoryNames; +} + +/* ******************************************************************** */ +/* GetUnitType() */ +/* ******************************************************************** */ + +const char *GDALProxyPoolRasterBand::GetUnitType() +{ + GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); + if (poUnderlyingRasterBand == NULL) + return NULL; + + CPLFree(pszUnitType); + pszUnitType = NULL; + + const char* pszUnderlyingUnitType = poUnderlyingRasterBand->GetUnitType(); + if (pszUnderlyingUnitType) + pszUnitType = CPLStrdup(pszUnderlyingUnitType); + + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + + return pszUnitType; +} + +/* ******************************************************************** */ +/* GetColorTable() */ +/* ******************************************************************** */ + +GDALColorTable *GDALProxyPoolRasterBand::GetColorTable() +{ + GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); + if (poUnderlyingRasterBand == NULL) + return NULL; + + if (poColorTable) + delete poColorTable; + poColorTable = NULL; + + GDALColorTable* poUnderlyingColorTable = poUnderlyingRasterBand->GetColorTable(); + if (poUnderlyingColorTable) + poColorTable = poUnderlyingColorTable->Clone(); + + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + + return poColorTable; +} + +/* ******************************************************************** */ +/* GetOverview() */ +/* ******************************************************************** */ + +GDALRasterBand *GDALProxyPoolRasterBand::GetOverview(int nOverviewBand) +{ + if (nOverviewBand >= 0 && nOverviewBand < nSizeProxyOverviewRasterBand) + { + if (papoProxyOverviewRasterBand[nOverviewBand]) + return papoProxyOverviewRasterBand[nOverviewBand]; + } + + GDALRasterBand* poUnderlyingRasterBand = RefUnderlyingRasterBand(); + if (poUnderlyingRasterBand == NULL) + return NULL; + + GDALRasterBand* poOverviewRasterBand = poUnderlyingRasterBand->GetOverview(nOverviewBand); + if (poOverviewRasterBand == NULL) + { + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + return NULL; + } + + if (nOverviewBand >= nSizeProxyOverviewRasterBand) + { + int i; + papoProxyOverviewRasterBand = (GDALProxyPoolOverviewRasterBand**) + CPLRealloc(papoProxyOverviewRasterBand, + sizeof(GDALProxyPoolOverviewRasterBand*) * (nOverviewBand + 1)); + for(i=nSizeProxyOverviewRasterBand; iGetMaskBand(); + + poProxyMaskBand = + new GDALProxyPoolMaskBand((GDALProxyPoolDataset*)poDS, + poMaskBand, + this); + + UnrefUnderlyingRasterBand(poUnderlyingRasterBand); + + return poProxyMaskBand; +} + +/* ******************************************************************** */ +/* GDALProxyPoolOverviewRasterBand() */ +/* ******************************************************************** */ + +GDALProxyPoolOverviewRasterBand::GDALProxyPoolOverviewRasterBand(GDALProxyPoolDataset* poDS, + GDALRasterBand* poUnderlyingOverviewBand, + GDALProxyPoolRasterBand* poMainBand, + int nOverviewBand) : + GDALProxyPoolRasterBand(poDS, poUnderlyingOverviewBand) +{ + this->poMainBand = poMainBand; + this->nOverviewBand = nOverviewBand; + + poUnderlyingMainRasterBand = NULL; + nRefCountUnderlyingMainRasterBand = 0; +} + +/* ******************************************************************** */ +/* ~GDALProxyPoolOverviewRasterBand() */ +/* ******************************************************************** */ + +GDALProxyPoolOverviewRasterBand::~GDALProxyPoolOverviewRasterBand() +{ + CPLAssert(nRefCountUnderlyingMainRasterBand == 0); +} + +/* ******************************************************************** */ +/* RefUnderlyingRasterBand() */ +/* ******************************************************************** */ + +GDALRasterBand* GDALProxyPoolOverviewRasterBand::RefUnderlyingRasterBand() +{ + poUnderlyingMainRasterBand = poMainBand->RefUnderlyingRasterBand(); + if (poUnderlyingMainRasterBand == NULL) + return NULL; + + nRefCountUnderlyingMainRasterBand ++; + return poUnderlyingMainRasterBand->GetOverview(nOverviewBand); +} + +/* ******************************************************************** */ +/* UnrefUnderlyingRasterBand() */ +/* ******************************************************************** */ + +void GDALProxyPoolOverviewRasterBand::UnrefUnderlyingRasterBand(CPL_UNUSED GDALRasterBand* poUnderlyingRasterBand) +{ + poMainBand->UnrefUnderlyingRasterBand(poUnderlyingMainRasterBand); + nRefCountUnderlyingMainRasterBand --; +} + + +/* ******************************************************************** */ +/* GDALProxyPoolMaskBand() */ +/* ******************************************************************** */ + +GDALProxyPoolMaskBand::GDALProxyPoolMaskBand(GDALProxyPoolDataset* poDS, + GDALRasterBand* poUnderlyingMaskBand, + GDALProxyPoolRasterBand* poMainBand) : + GDALProxyPoolRasterBand(poDS, poUnderlyingMaskBand) +{ + this->poMainBand = poMainBand; + + poUnderlyingMainRasterBand = NULL; + nRefCountUnderlyingMainRasterBand = 0; +} + +/* ******************************************************************** */ +/* GDALProxyPoolMaskBand() */ +/* ******************************************************************** */ + +GDALProxyPoolMaskBand::GDALProxyPoolMaskBand(GDALProxyPoolDataset* poDS, + GDALProxyPoolRasterBand* poMainBand, + GDALDataType eDataType, + int nBlockXSize, int nBlockYSize) : + GDALProxyPoolRasterBand(poDS, 1, eDataType, nBlockXSize, nBlockYSize) +{ + this->poMainBand = poMainBand; + + poUnderlyingMainRasterBand = NULL; + nRefCountUnderlyingMainRasterBand = 0; +} + +/* ******************************************************************** */ +/* ~GDALProxyPoolMaskBand() */ +/* ******************************************************************** */ + +GDALProxyPoolMaskBand::~GDALProxyPoolMaskBand() +{ + CPLAssert(nRefCountUnderlyingMainRasterBand == 0); +} + +/* ******************************************************************** */ +/* RefUnderlyingRasterBand() */ +/* ******************************************************************** */ + +GDALRasterBand* GDALProxyPoolMaskBand::RefUnderlyingRasterBand() +{ + poUnderlyingMainRasterBand = poMainBand->RefUnderlyingRasterBand(); + if (poUnderlyingMainRasterBand == NULL) + return NULL; + + nRefCountUnderlyingMainRasterBand ++; + return poUnderlyingMainRasterBand->GetMaskBand(); +} + +/* ******************************************************************** */ +/* UnrefUnderlyingRasterBand() */ +/* ******************************************************************** */ + +void GDALProxyPoolMaskBand::UnrefUnderlyingRasterBand(CPL_UNUSED GDALRasterBand* poUnderlyingRasterBand) +{ + poMainBand->UnrefUnderlyingRasterBand(poUnderlyingMainRasterBand); + nRefCountUnderlyingMainRasterBand --; +} diff --git a/bazaar/plugin/gdal/gcore/gdalrasterband.cpp b/bazaar/plugin/gdal/gcore/gdalrasterband.cpp new file mode 100644 index 000000000..2305a49c1 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalrasterband.cpp @@ -0,0 +1,5454 @@ +/****************************************************************************** + * $Id: gdalrasterband.cpp 29284 2015-06-03 13:26:10Z rouault $ + * + * Project: GDAL Core + * Purpose: Base class for format specific band class implementation. This + * base class provides default implementation for many methods. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_rat.h" +#include "cpl_string.h" + +#define SUBBLOCK_SIZE 64 +#define TO_SUBBLOCK(x) ((x) >> 6) +#define WITHIN_SUBBLOCK(x) ((x) & 0x3f) + +CPL_CVSID("$Id: gdalrasterband.cpp 29284 2015-06-03 13:26:10Z rouault $"); + +/************************************************************************/ +/* GDALRasterBand() */ +/************************************************************************/ + +/*! Constructor. Applications should never create GDALRasterBands directly. */ + +GDALRasterBand::GDALRasterBand() + +{ + poDS = NULL; + nBand = 0; + nRasterXSize = nRasterYSize = 0; + + eAccess = GA_ReadOnly; + nBlockXSize = nBlockYSize = -1; + eDataType = GDT_Byte; + + nSubBlocksPerRow = nBlocksPerRow = 0; + nSubBlocksPerColumn = nBlocksPerColumn = 0; + + bSubBlockingActive = FALSE; + papoBlocks = NULL; + + poMask = NULL; + bOwnMask = false; + nMaskFlags = 0; + + nBlockReads = 0; + bForceCachedIO = CSLTestBoolean( + CPLGetConfigOption( "GDAL_FORCE_CACHING", "NO") ); + + eFlushBlockErr = CE_None; +} + +/************************************************************************/ +/* ~GDALRasterBand() */ +/************************************************************************/ + +/*! Destructor. Applications should never destroy GDALRasterBands directly, + instead destroy the GDALDataset. */ + +GDALRasterBand::~GDALRasterBand() + +{ + FlushCache(); + + CPLFree( papoBlocks ); + + if( nBlockReads > nBlocksPerRow * nBlocksPerColumn + && nBand == 1 && poDS != NULL ) + { + CPLDebug( "GDAL", "%d block reads on %d block band 1 of %s.", + nBlockReads, nBlocksPerRow * nBlocksPerColumn, + poDS->GetDescription() ); + } + + InvalidateMaskBand(); +} + +/************************************************************************/ +/* RasterIO() */ +/************************************************************************/ + +/** + * \brief Read/write a region of image data for this band. + * + * This method allows reading a region of a GDALRasterBand into a buffer, + * or writing data from a buffer into a region of a GDALRasterBand. It + * automatically takes care of data type translation if the data type + * (eBufType) of the buffer is different than that of the GDALRasterBand. + * The method also takes care of image decimation / replication if the + * buffer size (nBufXSize x nBufYSize) is different than the size of the + * region being accessed (nXSize x nYSize). + * + * The nPixelSpace and nLineSpace parameters allow reading into or + * writing from unusually organized buffers. This is primarily used + * for buffers containing more than one bands raster data in interleaved + * format. + * + * Some formats may efficiently implement decimation into a buffer by + * reading from lower resolution overview images. + * + * For highest performance full resolution data access, read and write + * on "block boundaries" as returned by GetBlockSize(), or use the + * ReadBlock() and WriteBlock() methods. + * + * This method is the same as the C GDALRasterIO() or GDALRasterIOEx() functions. + * + * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param pData The buffer into which the data should be read, or from which + * it should be written. This buffer must contain at least nBufXSize * + * nBufYSize words of type eBufType. It is organized in left to right, + * top to bottom pixel order. Spacing is controlled by the nPixelSpace, + * and nLineSpace parameters. + * + * @param nBufXSize the width of the buffer image into which the desired region is + * to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired region is + * to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the pData data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nPixelSpace The byte offset from the start of one pixel value in + * pData to the start of the next pixel value within a scanline. If defaulted + * (0) the size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset from the start of one scanline in + * pData to the start of the next. If defaulted (0) the size of the datatype + * eBufType * nBufXSize is used. + * + * @param psExtraArg (new in GDAL 2.0) pointer to a GDALRasterIOExtraArg structure with additional + * arguments to specify resampling and progress callback, or NULL for default + * behaviour. The GDAL_RASTERIO_RESAMPLING configuration option can also be defined + * to override the default resampling to one of BILINEAR, CUBIC, CUBICSPLINE, + * LANCZOS, AVERAGE or MODE. + * + * @return CE_Failure if the access fails, otherwise CE_None. + */ + +CPLErr GDALRasterBand::RasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + GDALRasterIOExtraArg sExtraArg; + if( psExtraArg == NULL ) + { + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + psExtraArg = &sExtraArg; + } + else if( psExtraArg->nVersion != RASTERIO_EXTRA_ARG_CURRENT_VERSION ) + { + ReportError( CE_Failure, CPLE_AppDefined, + "Unhandled version of GDALRasterIOExtraArg" ); + return CE_Failure; + } + + GDALRasterIOExtraArgSetResampleAlg(psExtraArg, nXSize, nYSize, + nBufXSize, nBufYSize); + + if( NULL == pData ) + { + ReportError( CE_Failure, CPLE_AppDefined, + "The buffer into which the data should be read is null" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Some size values are "noop". Lets just return to avoid */ +/* stressing lower level functions. */ +/* -------------------------------------------------------------------- */ + if( nXSize < 1 || nYSize < 1 || nBufXSize < 1 || nBufYSize < 1 ) + { + CPLDebug( "GDAL", + "RasterIO() skipped for odd window or buffer size.\n" + " Window = (%d,%d)x%dx%d\n" + " Buffer = %dx%d\n", + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize ); + + return CE_None; + } + + if( eRWFlag == GF_Write && eFlushBlockErr != CE_None ) + { + ReportError(eFlushBlockErr, CPLE_AppDefined, + "An error occured while writing a dirty block"); + CPLErr eErr = eFlushBlockErr; + eFlushBlockErr = CE_None; + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* If pixel and line spaceing are defaulted assign reasonable */ +/* value assuming a packed buffer. */ +/* -------------------------------------------------------------------- */ + if( nPixelSpace == 0 ) + nPixelSpace = GDALGetDataTypeSize( eBufType ) / 8; + + if( nLineSpace == 0 ) + { + nLineSpace = nPixelSpace * nBufXSize; + } + +/* -------------------------------------------------------------------- */ +/* Do some validation of parameters. */ +/* -------------------------------------------------------------------- */ + if( nXOff < 0 || nXOff > INT_MAX - nXSize || nXOff + nXSize > nRasterXSize + || nYOff < 0 || nYOff > INT_MAX - nYSize || nYOff + nYSize > nRasterYSize ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Access window out of range in RasterIO(). Requested\n" + "(%d,%d) of size %dx%d on raster of %dx%d.", + nXOff, nYOff, nXSize, nYSize, nRasterXSize, nRasterYSize ); + return CE_Failure; + } + + if( eRWFlag != GF_Read && eRWFlag != GF_Write ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "eRWFlag = %d, only GF_Read (0) and GF_Write (1) are legal.", + eRWFlag ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Call the format specific function. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr; + + int bCallLeaveReadWrite = EnterReadWrite(eRWFlag); + + if( bForceCachedIO ) + eErr = GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg ); + else + eErr = IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg ) ; + + if( bCallLeaveReadWrite) LeaveReadWrite(); + + return eErr; +} + +/************************************************************************/ +/* GDALRasterIO() */ +/************************************************************************/ + +/** + * \brief Read/write a region of image data for this band. + * + * Use GDALRasterIOEx() if 64 bit spacings or extra arguments (resampling + * resolution, progress callback, etc. are needed) + * + * @see GDALRasterBand::RasterIO() + */ + +CPLErr CPL_STDCALL +GDALRasterIO( GDALRasterBandH hBand, GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, int nLineSpace ) + +{ + VALIDATE_POINTER1( hBand, "GDALRasterIO", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + + return( poBand->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, NULL) ); +} + +/************************************************************************/ +/* GDALRasterIOEx() */ +/************************************************************************/ + +/** + * \brief Read/write a region of image data for this band. + * + * @see GDALRasterBand::RasterIO() + * @since GDAL 2.0 + */ + +CPLErr CPL_STDCALL +GDALRasterIOEx( GDALRasterBandH hBand, GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + VALIDATE_POINTER1( hBand, "GDALRasterIOEx", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + + return( poBand->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, psExtraArg) ); +} +/************************************************************************/ +/* ReadBlock() */ +/************************************************************************/ + +/** + * \brief Read a block of image data efficiently. + * + * This method accesses a "natural" block from the raster band without + * resampling, or data type conversion. For a more generalized, but + * potentially less efficient access use RasterIO(). + * + * This method is the same as the C GDALReadBlock() function. + * + * See the GetLockedBlockRef() method for a way of accessing internally cached + * block oriented data without an extra copy into an application buffer. + * + * @param nXBlockOff the horizontal block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param nYBlockOff the vertical block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param pImage the buffer into which the data will be read. The buffer + * must be large enough to hold GetBlockXSize()*GetBlockYSize() words + * of type GetRasterDataType(). + * + * @return CE_None on success or CE_Failure on an error. + * + * The following code would efficiently compute a histogram of eight bit + * raster data. Note that the final block may be partial ... data beyond + * the edge of the underlying raster band in these edge blocks is of an + * undermined value. + * +
    + CPLErr GetHistogram( GDALRasterBand *poBand, GUIntBig *panHistogram )
    +
    + {
    +     int        nXBlocks, nYBlocks, nXBlockSize, nYBlockSize;
    +     int        iXBlock, iYBlock;
    +     GByte      *pabyData;
    +
    +     memset( panHistogram, 0, sizeof(GUIntBig) * 256 );
    +
    +     CPLAssert( poBand->GetRasterDataType() == GDT_Byte );
    +
    +     poBand->GetBlockSize( &nXBlockSize, &nYBlockSize );
    +     nXBlocks = (poBand->GetXSize() + nXBlockSize - 1) / nXBlockSize;
    +     nYBlocks = (poBand->GetYSize() + nYBlockSize - 1) / nYBlockSize;
    +
    +     pabyData = (GByte *) CPLMalloc(nXBlockSize * nYBlockSize);
    +
    +     for( iYBlock = 0; iYBlock < nYBlocks; iYBlock++ )
    +     {
    +         for( iXBlock = 0; iXBlock < nXBlocks; iXBlock++ )
    +         {
    +             int        nXValid, nYValid;
    +             
    +             poBand->ReadBlock( iXBlock, iYBlock, pabyData );
    +
    +             // Compute the portion of the block that is valid
    +             // for partial edge blocks.
    +             if( (iXBlock+1) * nXBlockSize > poBand->GetXSize() )
    +                 nXValid = poBand->GetXSize() - iXBlock * nXBlockSize;
    +             else
    +                 nXValid = nXBlockSize;
    +
    +             if( (iYBlock+1) * nYBlockSize > poBand->GetYSize() )
    +                 nYValid = poBand->GetYSize() - iYBlock * nYBlockSize;
    +             else
    +                 nYValid = nYBlockSize;
    +
    +             // Collect the histogram counts.
    +             for( int iY = 0; iY < nYValid; iY++ )
    +             {
    +                 for( int iX = 0; iX < nXValid; iX++ )
    +                 {
    +                     panHistogram[pabyData[iX + iY * nXBlockSize]] += 1;
    +                 }
    +             }
    +         }
    +     }
    + }
    + 
    +
    + */ + + +CPLErr GDALRasterBand::ReadBlock( int nXBlockOff, int nYBlockOff, + void * pImage ) + +{ +/* -------------------------------------------------------------------- */ +/* Validate arguments. */ +/* -------------------------------------------------------------------- */ + CPLAssert( pImage != NULL ); + + if( !InitBlockInfo() ) + return CE_Failure; + + if( nXBlockOff < 0 || nXBlockOff >= nBlocksPerRow ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nXBlockOff value (%d) in " + "GDALRasterBand::ReadBlock()\n", + nXBlockOff ); + + return( CE_Failure ); + } + + if( nYBlockOff < 0 || nYBlockOff >= nBlocksPerColumn ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nYBlockOff value (%d) in " + "GDALRasterBand::ReadBlock()\n", + nYBlockOff ); + + return( CE_Failure ); + } + +/* -------------------------------------------------------------------- */ +/* Invoke underlying implementation method. */ +/* -------------------------------------------------------------------- */ + int bCallLeaveReadWrite = EnterReadWrite(GF_Read); + CPLErr eErr = IReadBlock( nXBlockOff, nYBlockOff, pImage ); + if( bCallLeaveReadWrite) LeaveReadWrite(); + return eErr; +} + +/************************************************************************/ +/* GDALReadBlock() */ +/************************************************************************/ + +/** + * \brief Read a block of image data efficiently. + * + * @see GDALRasterBand::ReadBlock() + */ + +CPLErr CPL_STDCALL GDALReadBlock( GDALRasterBandH hBand, int nXOff, int nYOff, + void * pData ) + +{ + VALIDATE_POINTER1( hBand, "GDALReadBlock", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return( poBand->ReadBlock( nXOff, nYOff, pData ) ); +} + +/************************************************************************/ +/* IWriteBlock() */ +/* */ +/* Default internal implementation ... to be overriden by */ +/* subclasses that support writing. */ +/************************************************************************/ + +CPLErr GDALRasterBand::IWriteBlock( int, int, void * ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "WriteBlock() not supported for this dataset." ); + + return( CE_Failure ); +} + +/************************************************************************/ +/* WriteBlock() */ +/************************************************************************/ + +/** + * \brief Write a block of image data efficiently. + * + * This method accesses a "natural" block from the raster band without + * resampling, or data type conversion. For a more generalized, but + * potentially less efficient access use RasterIO(). + * + * This method is the same as the C GDALWriteBlock() function. + * + * See ReadBlock() for an example of block oriented data access. + * + * @param nXBlockOff the horizontal block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param nYBlockOff the vertical block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param pImage the buffer from which the data will be written. The buffer + * must be large enough to hold GetBlockXSize()*GetBlockYSize() words + * of type GetRasterDataType(). + * + * @return CE_None on success or CE_Failure on an error. + */ + +CPLErr GDALRasterBand::WriteBlock( int nXBlockOff, int nYBlockOff, + void * pImage ) + +{ +/* -------------------------------------------------------------------- */ +/* Validate arguments. */ +/* -------------------------------------------------------------------- */ + CPLAssert( pImage != NULL ); + + if( !InitBlockInfo() ) + return CE_Failure; + + if( nXBlockOff < 0 || nXBlockOff >= nBlocksPerRow ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nXBlockOff value (%d) in " + "GDALRasterBand::WriteBlock()\n", + nXBlockOff ); + + return( CE_Failure ); + } + + if( nYBlockOff < 0 || nYBlockOff >= nBlocksPerColumn ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nYBlockOff value (%d) in " + "GDALRasterBand::WriteBlock()\n", + nYBlockOff ); + + return( CE_Failure ); + } + + if( eAccess == GA_ReadOnly ) + { + ReportError( CE_Failure, CPLE_NoWriteAccess, + "Attempt to write to read only dataset in" + "GDALRasterBand::WriteBlock().\n" ); + + return( CE_Failure ); + } + + if( eFlushBlockErr != CE_None ) + { + ReportError(eFlushBlockErr, CPLE_AppDefined, + "An error occured while writing a dirty block"); + CPLErr eErr = eFlushBlockErr; + eFlushBlockErr = CE_None; + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Invoke underlying implementation method. */ +/* -------------------------------------------------------------------- */ + + int bCallLeaveReadWrite = EnterReadWrite(GF_Write); + CPLErr eErr = IWriteBlock( nXBlockOff, nYBlockOff, pImage ); + if( bCallLeaveReadWrite ) LeaveReadWrite(); + + return eErr; +} + +/************************************************************************/ +/* GDALWriteBlock() */ +/************************************************************************/ + +/** + * \brief Write a block of image data efficiently. + * + * @see GDALRasterBand::WriteBlock() + */ + +CPLErr CPL_STDCALL GDALWriteBlock( GDALRasterBandH hBand, int nXOff, int nYOff, + void * pData ) + +{ + VALIDATE_POINTER1( hBand, "GDALWriteBlock", CE_Failure ); + + GDALRasterBand *poBand = static_cast( hBand ); + return( poBand->WriteBlock( nXOff, nYOff, pData ) ); +} + + +/************************************************************************/ +/* GetRasterDataType() */ +/************************************************************************/ + +/** + * \brief Fetch the pixel data type for this band. + * + * This method is the same as the C function GDALGetRasterDataType(). + * + * @return the data type of pixels for this band. + */ + + +GDALDataType GDALRasterBand::GetRasterDataType() + +{ + return eDataType; +} + +/************************************************************************/ +/* GDALGetRasterDataType() */ +/************************************************************************/ + +/** + * \brief Fetch the pixel data type for this band. + * + * @see GDALRasterBand::GetRasterDataType() + */ + +GDALDataType CPL_STDCALL GDALGetRasterDataType( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterDataType", GDT_Unknown ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetRasterDataType(); +} + +/************************************************************************/ +/* GetBlockSize() */ +/************************************************************************/ + +/** + * \brief Fetch the "natural" block size of this band. + * + * GDAL contains a concept of the natural block size of rasters so that + * applications can organized data access efficiently for some file formats. + * The natural block size is the block size that is most efficient for + * accessing the format. For many formats this is simple a whole scanline + * in which case *pnXSize is set to GetXSize(), and *pnYSize is set to 1. + * + * However, for tiled images this will typically be the tile size. + * + * Note that the X and Y block sizes don't have to divide the image size + * evenly, meaning that right and bottom edge blocks may be incomplete. + * See ReadBlock() for an example of code dealing with these issues. + * + * This method is the same as the C function GDALGetBlockSize(). + * + * @param pnXSize integer to put the X block size into or NULL. + * + * @param pnYSize integer to put the Y block size into or NULL. + */ + +void GDALRasterBand::GetBlockSize( int * pnXSize, int *pnYSize ) + +{ + if( nBlockXSize <= 0 || nBlockYSize <= 0 ) + { + ReportError( CE_Failure, CPLE_AppDefined, "Invalid block dimension : %d * %d", + nBlockXSize, nBlockYSize ); + if( pnXSize != NULL ) + *pnXSize = 0; + if( pnYSize != NULL ) + *pnYSize = 0; + } + else + { + if( pnXSize != NULL ) + *pnXSize = nBlockXSize; + if( pnYSize != NULL ) + *pnYSize = nBlockYSize; + } +} + +/************************************************************************/ +/* GDALGetBlockSize() */ +/************************************************************************/ + +/** + * \brief Fetch the "natural" block size of this band. + * + * @see GDALRasterBand::GetBlockSize() + */ + +void CPL_STDCALL +GDALGetBlockSize( GDALRasterBandH hBand, int * pnXSize, int * pnYSize ) + +{ + VALIDATE_POINTER0( hBand, "GDALGetBlockSize" ); + + GDALRasterBand *poBand = static_cast(hBand); + poBand->GetBlockSize( pnXSize, pnYSize ); +} + +/************************************************************************/ +/* InitBlockInfo() */ +/************************************************************************/ + +int GDALRasterBand::InitBlockInfo() + +{ + if( papoBlocks != NULL ) + return TRUE; + + /* Do some validation of raster and block dimensions in case the driver */ + /* would have neglected to do it itself */ + if( nBlockXSize <= 0 || nBlockYSize <= 0 ) + { + ReportError( CE_Failure, CPLE_AppDefined, "Invalid block dimension : %d * %d", + nBlockXSize, nBlockYSize ); + return FALSE; + } + + if( nRasterXSize <= 0 || nRasterYSize <= 0 ) + { + ReportError( CE_Failure, CPLE_AppDefined, "Invalid raster dimension : %d * %d", + nRasterXSize, nRasterYSize ); + return FALSE; + } + + if (nBlockXSize >= 10000 || nBlockYSize >= 10000) + { + /* Check that the block size is not overflowing int capacity as it is */ + /* (reasonnably) assumed in many places (GDALRasterBlock::Internalize(), */ + /* GDALRasterBand::Fill(), many drivers...) */ + /* As 10000 * 10000 * 16 < INT_MAX, we don't need to do the multiplication in other cases */ + + int nSizeInBytes = nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType) / 8); + + GIntBig nBigSizeInBytes = (GIntBig)nBlockXSize * nBlockYSize * (GDALGetDataTypeSize(eDataType) / 8); + if ((GIntBig)nSizeInBytes != nBigSizeInBytes) + { + ReportError( CE_Failure, CPLE_NotSupported, "Too big block : %d * %d", + nBlockXSize, nBlockYSize ); + return FALSE; + } + } + + nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize); + nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize); + + if( nBlocksPerRow < SUBBLOCK_SIZE/2 ) + { + bSubBlockingActive = FALSE; + + if (nBlocksPerRow < INT_MAX / nBlocksPerColumn) + { + papoBlocks = (GDALRasterBlock **) + VSICalloc( sizeof(void*), nBlocksPerRow * nBlocksPerColumn ); + } + else + { + ReportError( CE_Failure, CPLE_NotSupported, "Too many blocks : %d x %d", + nBlocksPerRow, nBlocksPerColumn ); + return FALSE; + } + } + else + { + bSubBlockingActive = TRUE; + + nSubBlocksPerRow = DIV_ROUND_UP(nBlocksPerRow, SUBBLOCK_SIZE); + nSubBlocksPerColumn = DIV_ROUND_UP(nBlocksPerColumn, SUBBLOCK_SIZE); + + if (nSubBlocksPerRow < INT_MAX / nSubBlocksPerColumn) + { + papoBlocks = (GDALRasterBlock **) + VSICalloc( sizeof(void*), nSubBlocksPerRow * nSubBlocksPerColumn ); + } + else + { + ReportError( CE_Failure, CPLE_NotSupported, "Too many subblocks : %d x %d", + nSubBlocksPerRow, nSubBlocksPerColumn ); + return FALSE; + } + } + + if( papoBlocks == NULL ) + { + ReportError( CE_Failure, CPLE_OutOfMemory, + "Out of memory in InitBlockInfo()." ); + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* AdoptBlock() */ +/* */ +/* Add a block to the raster band's block matrix. If this */ +/* exceeds our maximum blocks for this layer, flush the oldest */ +/* block out. */ +/* */ +/* This method is protected. */ +/************************************************************************/ + +CPLErr GDALRasterBand::AdoptBlock( int nXBlockOff, int nYBlockOff, + GDALRasterBlock * poBlock ) + +{ + int nBlockIndex; + + if( !InitBlockInfo() ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Simple case without subblocking. */ +/* -------------------------------------------------------------------- */ + if( !bSubBlockingActive ) + { + nBlockIndex = nXBlockOff + nYBlockOff * nBlocksPerRow; + + if( papoBlocks[nBlockIndex] == poBlock ) + return( CE_None ); + + if( papoBlocks[nBlockIndex] != NULL ) + FlushBlock( nXBlockOff, nYBlockOff ); + + papoBlocks[nBlockIndex] = poBlock; + poBlock->Touch(); + + return( CE_None ); + } + +/* -------------------------------------------------------------------- */ +/* Identify the subblock in which our target occurs, and create */ +/* it if necessary. */ +/* -------------------------------------------------------------------- */ + int nSubBlock = TO_SUBBLOCK(nXBlockOff) + + TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; + + if( papoBlocks[nSubBlock] == NULL ) + { + const int nSubGridSize = + sizeof(GDALRasterBlock*) * SUBBLOCK_SIZE * SUBBLOCK_SIZE; + + papoBlocks[nSubBlock] = (GDALRasterBlock *) VSICalloc(1, nSubGridSize); + if( papoBlocks[nSubBlock] == NULL ) + { + ReportError( CE_Failure, CPLE_OutOfMemory, + "Out of memory in AdoptBlock()." ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Check within subblock. */ +/* -------------------------------------------------------------------- */ + GDALRasterBlock **papoSubBlockGrid = + (GDALRasterBlock **) papoBlocks[nSubBlock]; + + int nBlockInSubBlock = WITHIN_SUBBLOCK(nXBlockOff) + + WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; + + if( papoSubBlockGrid[nBlockInSubBlock] == poBlock ) + return CE_None; + + if( papoSubBlockGrid[nBlockInSubBlock] != NULL ) + FlushBlock( nXBlockOff, nYBlockOff ); + + papoSubBlockGrid[nBlockInSubBlock] = poBlock; + poBlock->Touch(); + + return CE_None; +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +/** + * \brief Flush raster data cache. + * + * This call will recover memory used to cache data blocks for this raster + * band, and ensure that new requests are referred to the underlying driver. + * + * This method is the same as the C function GDALFlushRasterCache(). + * + * @return CE_None on success. + */ + +CPLErr GDALRasterBand::FlushCache() + +{ + CPLErr eGlobalErr = eFlushBlockErr; + + if (eFlushBlockErr != CE_None) + { + ReportError(eFlushBlockErr, CPLE_AppDefined, + "An error occured while writing a dirty block"); + eFlushBlockErr = CE_None; + } + + if (papoBlocks == NULL) + return eGlobalErr; + +/* -------------------------------------------------------------------- */ +/* Flush all blocks in memory ... this case is without subblocking.*/ +/* -------------------------------------------------------------------- */ + if( !bSubBlockingActive ) + { + for( int iY = 0; iY < nBlocksPerColumn; iY++ ) + { + for( int iX = 0; iX < nBlocksPerRow; iX++ ) + { + if( papoBlocks[iX + iY*nBlocksPerRow] != NULL ) + { + CPLErr eErr; + + eErr = FlushBlock( iX, iY, eGlobalErr == CE_None ); + + if( eErr != CE_None ) + eGlobalErr = eErr; + } + } + } + return eGlobalErr; + } + +/* -------------------------------------------------------------------- */ +/* With subblocking. We can short circuit missing subblocks. */ +/* -------------------------------------------------------------------- */ + int iSBX, iSBY; + + for( iSBY = 0; iSBY < nSubBlocksPerColumn; iSBY++ ) + { + for( iSBX = 0; iSBX < nSubBlocksPerRow; iSBX++ ) + { + int nSubBlock = iSBX + iSBY * nSubBlocksPerRow; + + GDALRasterBlock **papoSubBlockGrid = + (GDALRasterBlock **) papoBlocks[nSubBlock]; + + if( papoSubBlockGrid == NULL ) + continue; + + for( int iY = 0; iY < SUBBLOCK_SIZE; iY++ ) + { + for( int iX = 0; iX < SUBBLOCK_SIZE; iX++ ) + { + if( papoSubBlockGrid[iX + iY * SUBBLOCK_SIZE] != NULL ) + { + CPLErr eErr; + + eErr = FlushBlock( iX + iSBX * SUBBLOCK_SIZE, + iY + iSBY * SUBBLOCK_SIZE, + eGlobalErr == CE_None ); + if( eErr != CE_None ) + eGlobalErr = eErr; + } + } + } + + // We might as well get rid of this grid chunk since we know + // it is now empty. + papoBlocks[nSubBlock] = NULL; + CPLFree( papoSubBlockGrid ); + } + } + + return( eGlobalErr ); +} + +/************************************************************************/ +/* GDALFlushRasterCache() */ +/************************************************************************/ + +/** + * \brief Flush raster data cache. + * + * @see GDALRasterBand::FlushCache() + */ + +CPLErr CPL_STDCALL GDALFlushRasterCache( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALFlushRasterCache", CE_Failure ); + + return ((GDALRasterBand *) hBand)->FlushCache(); +} + + +/************************************************************************/ +/* UnreferenceBlock() */ +/* */ +/* Unreference the block from our array of blocks */ +/* This method should only be called by */ +/* GDALRasterBlock::Internalize(), and under the block cache mutex */ +/************************************************************************/ + +CPLErr GDALRasterBand::UnreferenceBlock( int nXBlockOff, int nYBlockOff ) +{ + + if( !papoBlocks ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Validate the request */ +/* -------------------------------------------------------------------- */ + if( nXBlockOff < 0 || nXBlockOff >= nBlocksPerRow ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockXOff value (%d) in " + "GDALRasterBand::FlushBlock()\n", + nXBlockOff ); + + return( CE_Failure ); + } + + if( nYBlockOff < 0 || nYBlockOff >= nBlocksPerColumn ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockYOff value (%d) in " + "GDALRasterBand::FlushBlock()\n", + nYBlockOff ); + + return( CE_Failure ); + } + +/* -------------------------------------------------------------------- */ +/* Simple case for single level caches. */ +/* -------------------------------------------------------------------- */ + if( !bSubBlockingActive ) + { + int nBlockIndex = nXBlockOff + nYBlockOff * nBlocksPerRow; + + papoBlocks[nBlockIndex] = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Identify our subblock. */ +/* -------------------------------------------------------------------- */ + else + { + int nSubBlock = TO_SUBBLOCK(nXBlockOff) + + TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; + + if( papoBlocks[nSubBlock] == NULL ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Check within subblock. */ +/* -------------------------------------------------------------------- */ + GDALRasterBlock **papoSubBlockGrid = + (GDALRasterBlock **) papoBlocks[nSubBlock]; + + int nBlockInSubBlock = WITHIN_SUBBLOCK(nXBlockOff) + + WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; + + papoSubBlockGrid[nBlockInSubBlock] = NULL; + } + + return CE_None; +} + +/************************************************************************/ +/* FlushBlock() */ +/* */ +/* Flush a block out of the block cache. If it has been */ +/* modified write it to disk. If no specific tile is */ +/* indicated, write the oldest tile. */ +/* */ +/* Protected method. */ +/************************************************************************/ + +CPLErr GDALRasterBand::FlushBlock( int nXBlockOff, int nYBlockOff, + int bWriteDirtyBlock ) + +{ + int nBlockIndex; + GDALRasterBlock *poBlock = NULL; + + if( !papoBlocks ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Validate the request */ +/* -------------------------------------------------------------------- */ + if( nXBlockOff < 0 || nXBlockOff >= nBlocksPerRow ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockXOff value (%d) in " + "GDALRasterBand::FlushBlock()\n", + nXBlockOff ); + + return( CE_Failure ); + } + + if( nYBlockOff < 0 || nYBlockOff >= nBlocksPerColumn ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockYOff value (%d) in " + "GDALRasterBand::FlushBlock()\n", + nYBlockOff ); + + return( CE_Failure ); + } + +/* -------------------------------------------------------------------- */ +/* Simple case for single level caches. */ +/* -------------------------------------------------------------------- */ + if( !bSubBlockingActive ) + { + nBlockIndex = nXBlockOff + nYBlockOff * nBlocksPerRow; + + GDALRasterBlock::SafeLockBlock( papoBlocks + nBlockIndex ); + + poBlock = papoBlocks[nBlockIndex]; + papoBlocks[nBlockIndex] = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Identify our subblock. */ +/* -------------------------------------------------------------------- */ + else + { + int nSubBlock = TO_SUBBLOCK(nXBlockOff) + + TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; + + if( papoBlocks[nSubBlock] == NULL ) + return CE_None; + +/* -------------------------------------------------------------------- */ +/* Check within subblock. */ +/* -------------------------------------------------------------------- */ + GDALRasterBlock **papoSubBlockGrid = + (GDALRasterBlock **) papoBlocks[nSubBlock]; + + int nBlockInSubBlock = WITHIN_SUBBLOCK(nXBlockOff) + + WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; + + GDALRasterBlock::SafeLockBlock( papoSubBlockGrid + nBlockInSubBlock ); + + poBlock = papoSubBlockGrid[nBlockInSubBlock]; + papoSubBlockGrid[nBlockInSubBlock] = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Is the target block dirty? If so we need to write it. */ +/* -------------------------------------------------------------------- */ + CPLErr eErr = CE_None; + + if( poBlock == NULL ) + return CE_None; + + poBlock->Detach(); + + if( bWriteDirtyBlock && poBlock->GetDirty() ) + eErr = poBlock->Write(); + +/* -------------------------------------------------------------------- */ +/* Deallocate the block; */ +/* -------------------------------------------------------------------- */ + poBlock->DropLock(); + delete poBlock; + + return eErr; +} + +/************************************************************************/ +/* TryGetLockedBlockRef() */ +/************************************************************************/ + +/** + * \brief Try fetching block ref. + * + * This method will returned the requested block (locked) if it is already + * in the block cache for the layer. If not, NULL is returned. + * + * If a non-NULL value is returned, then a lock for the block will have been + * acquired on behalf of the caller. It is absolutely imperative that the + * caller release this lock (with GDALRasterBlock::DropLock()) or else + * severe problems may result. + * + * @param nXBlockOff the horizontal block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param nYBlockOff the vertical block offset, with zero indicating + * the top most block, 1 the next block and so forth. + * + * @return NULL if block not available, or locked block pointer. + */ + +GDALRasterBlock *GDALRasterBand::TryGetLockedBlockRef( int nXBlockOff, + int nYBlockOff ) + +{ + int nBlockIndex = 0; + + if( !InitBlockInfo() ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Validate the request */ +/* -------------------------------------------------------------------- */ + if( nXBlockOff < 0 || nXBlockOff >= nBlocksPerRow ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockXOff value (%d) in " + "GDALRasterBand::TryGetLockedBlockRef()\n", + nXBlockOff ); + + return( NULL ); + } + + if( nYBlockOff < 0 || nYBlockOff >= nBlocksPerColumn ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockYOff value (%d) in " + "GDALRasterBand::TryGetLockedBlockRef()\n", + nYBlockOff ); + + return( NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Simple case for single level caches. */ +/* -------------------------------------------------------------------- */ + if( !bSubBlockingActive ) + { + nBlockIndex = nXBlockOff + nYBlockOff * nBlocksPerRow; + + GDALRasterBlock::SafeLockBlock( papoBlocks + nBlockIndex ); + + return papoBlocks[nBlockIndex]; + } + +/* -------------------------------------------------------------------- */ +/* Identify our subblock. */ +/* -------------------------------------------------------------------- */ + int nSubBlock = TO_SUBBLOCK(nXBlockOff) + + TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; + + if( papoBlocks[nSubBlock] == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Check within subblock. */ +/* -------------------------------------------------------------------- */ + GDALRasterBlock **papoSubBlockGrid = + (GDALRasterBlock **) papoBlocks[nSubBlock]; + + int nBlockInSubBlock = WITHIN_SUBBLOCK(nXBlockOff) + + WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; + + GDALRasterBlock::SafeLockBlock( papoSubBlockGrid + nBlockInSubBlock ); + + return papoSubBlockGrid[nBlockInSubBlock]; +} + +/************************************************************************/ +/* GetLockedBlockRef() */ +/************************************************************************/ + +/** + * \brief Fetch a pointer to an internally cached raster block. + * + * This method will returned the requested block (locked) if it is already + * in the block cache for the layer. If not, the block will be read from + * the driver, and placed in the layer block cached, then returned. If an + * error occurs reading the block from the driver, a NULL value will be + * returned. + * + * If a non-NULL value is returned, then a lock for the block will have been + * acquired on behalf of the caller. It is absolutely imperative that the + * caller release this lock (with GDALRasterBlock::DropLock()) or else + * severe problems may result. + * + * Note that calling GetLockedBlockRef() on a previously uncached band will + * enable caching. + * + * @param nXBlockOff the horizontal block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param nYBlockOff the vertical block offset, with zero indicating + * the top most block, 1 the next block and so forth. + * + * @param bJustInitialize If TRUE the block will be allocated and initialized, + * but not actually read from the source. This is useful when it will just + * be completely set and written back. + * + * @return pointer to the block object, or NULL on failure. + */ + +GDALRasterBlock * GDALRasterBand::GetLockedBlockRef( int nXBlockOff, + int nYBlockOff, + int bJustInitialize ) + +{ + GDALRasterBlock *poBlock = NULL; + +/* -------------------------------------------------------------------- */ +/* Try and fetch from cache. */ +/* -------------------------------------------------------------------- */ + poBlock = TryGetLockedBlockRef( nXBlockOff, nYBlockOff ); + +/* -------------------------------------------------------------------- */ +/* If we didn't find it in our memory cache, instantiate a */ +/* block (potentially load from disk) and "adopt" it into the */ +/* cache. */ +/* -------------------------------------------------------------------- */ + if( poBlock == NULL ) + { + if( !InitBlockInfo() ) + return( NULL ); + + /* -------------------------------------------------------------------- */ + /* Validate the request */ + /* -------------------------------------------------------------------- */ + if( nXBlockOff < 0 || nXBlockOff >= nBlocksPerRow ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockXOff value (%d) in " + "GDALRasterBand::GetLockedBlockRef()\n", + nXBlockOff ); + + return( NULL ); + } + + if( nYBlockOff < 0 || nYBlockOff >= nBlocksPerColumn ) + { + ReportError( CE_Failure, CPLE_IllegalArg, + "Illegal nBlockYOff value (%d) in " + "GDALRasterBand::GetLockedBlockRef()\n", + nYBlockOff ); + + return( NULL ); + } + + poBlock = new GDALRasterBlock( this, nXBlockOff, nYBlockOff ); + + poBlock->AddLock(); + + /* allocate data space */ + if( poBlock->Internalize() != CE_None ) + { + poBlock->DropLock(); + delete poBlock; + return( NULL ); + } + + if ( AdoptBlock( nXBlockOff, nYBlockOff, poBlock ) != CE_None ) + { + poBlock->DropLock(); + delete poBlock; + return( NULL ); + } + + if( !bJustInitialize + && IReadBlock(nXBlockOff,nYBlockOff,poBlock->GetDataRef()) != CE_None) + { + poBlock->DropLock(); + FlushBlock( nXBlockOff, nYBlockOff ); + ReportError( CE_Failure, CPLE_AppDefined, + "IReadBlock failed at X offset %d, Y offset %d", + nXBlockOff, nYBlockOff ); + return( NULL ); + } + + if( !bJustInitialize ) + { + nBlockReads++; + if( nBlockReads == nBlocksPerRow * nBlocksPerColumn + 1 + && nBand == 1 && poDS != NULL ) + { + CPLDebug( "GDAL", "Potential thrashing on band %d of %s.", + nBand, poDS->GetDescription() ); + } + } + } + + return poBlock; +} + +/************************************************************************/ +/* Fill() */ +/************************************************************************/ + +/** + * \brief Fill this band with a constant value. + * + * GDAL makes no guarantees + * about what values pixels in newly created files are set to, so this + * method can be used to clear a band to a specified "default" value. + * The fill value is passed in as a double but this will be converted + * to the underlying type before writing to the file. An optional + * second argument allows the imaginary component of a complex + * constant value to be specified. + * + * This method is the same as the C function GDALFillRaster(). + * + * @param dfRealValue Real component of fill value + * @param dfImaginaryValue Imaginary component of fill value, defaults to zero + * + * @return CE_Failure if the write fails, otherwise CE_None + */ +CPLErr GDALRasterBand::Fill(double dfRealValue, double dfImaginaryValue) { + + // General approach is to construct a source block of the file's + // native type containing the appropriate value and then copy this + // to each block in the image via the RasterBlock cache. Using + // the cache means we avoid file I/O if it's not necessary, at the + // expense of some extra memcpy's (since we write to the + // RasterBlock cache, which is then at some point written to the + // underlying file, rather than simply directly to the underlying + // file.) + + // Check we can write to the file + if( eAccess == GA_ReadOnly ) { + ReportError(CE_Failure, CPLE_NoWriteAccess, + "Attempt to write to read only dataset in" + "GDALRasterBand::Fill().\n" ); + return CE_Failure; + } + + // Make sure block parameters are set + if( !InitBlockInfo() ) + return CE_Failure; + + // Allocate the source block + int blockSize = nBlockXSize * nBlockYSize; + int elementSize = GDALGetDataTypeSize(eDataType) / 8; + int blockByteSize = blockSize * elementSize; + unsigned char* srcBlock = (unsigned char*) VSIMalloc(blockByteSize); + if (srcBlock == NULL) { + ReportError(CE_Failure, CPLE_OutOfMemory, + "GDALRasterBand::Fill(): Out of memory " + "allocating %d bytes.\n", blockByteSize); + return CE_Failure; + } + + // Initialize the source block + double complexSrc[2] = { dfRealValue, dfImaginaryValue }; + GDALCopyWords(complexSrc, GDT_CFloat64, 0, + srcBlock, eDataType, elementSize, blockSize); + + int bCallLeaveReadWrite = EnterReadWrite(GF_Write); + + // Write block to block cache + for (int j = 0; j < nBlocksPerColumn; ++j) { + for (int i = 0; i < nBlocksPerRow; ++i) { + GDALRasterBlock* destBlock = GetLockedBlockRef(i, j, TRUE); + if (destBlock == NULL) { + ReportError(CE_Failure, CPLE_OutOfMemory, + "GDALRasterBand::Fill(): Error " + "while retrieving cache block.\n"); + VSIFree(srcBlock); + return CE_Failure; + } + if (destBlock->GetDataRef() == NULL) + { + destBlock->DropLock(); + VSIFree(srcBlock); + return CE_Failure; + } + memcpy(destBlock->GetDataRef(), srcBlock, blockByteSize); + destBlock->MarkDirty(); + destBlock->DropLock(); + } + } + + if( bCallLeaveReadWrite ) LeaveReadWrite(); + + // Free up the source block + VSIFree(srcBlock); + + return CE_None; +} + + +/************************************************************************/ +/* GDALFillRaster() */ +/************************************************************************/ + +/** + * \brief Fill this band with a constant value. + * + * @see GDALRasterBand::Fill() + */ +CPLErr CPL_STDCALL GDALFillRaster(GDALRasterBandH hBand, double dfRealValue, + double dfImaginaryValue) +{ + VALIDATE_POINTER1( hBand, "GDALFillRaster", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->Fill(dfRealValue, dfImaginaryValue); +} + +/************************************************************************/ +/* GetAccess() */ +/************************************************************************/ + +/** + * \brief Find out if we have update permission for this band. + * + * This method is the same as the C function GDALGetRasterAccess(). + * + * @return Either GA_Update or GA_ReadOnly. + */ + +GDALAccess GDALRasterBand::GetAccess() + +{ + return eAccess; +} + +/************************************************************************/ +/* GDALGetRasterAccess() */ +/************************************************************************/ + +/** + * \brief Find out if we have update permission for this band. + * + * @see GDALRasterBand::GetAccess() + */ + +GDALAccess CPL_STDCALL GDALGetRasterAccess( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterAccess", GA_ReadOnly ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetAccess(); +} + +/************************************************************************/ +/* GetCategoryNames() */ +/************************************************************************/ + +/** + * \brief Fetch the list of category names for this raster. + * + * The return list is a "StringList" in the sense of the CPL functions. + * That is a NULL terminated array of strings. Raster values without + * associated names will have an empty string in the returned list. The + * first entry in the list is for raster values of zero, and so on. + * + * The returned stringlist should not be altered or freed by the application. + * It may change on the next GDAL call, so please copy it if it is needed + * for any period of time. + * + * This method is the same as the C function GDALGetRasterCategoryNames(). + * + * @return list of names, or NULL if none. + */ + +char **GDALRasterBand::GetCategoryNames() + +{ + return NULL; +} + +/************************************************************************/ +/* GDALGetRasterCategoryNames() */ +/************************************************************************/ + +/** + * \brief Fetch the list of category names for this raster. + * + * @see GDALRasterBand::GetCategoryNames() + */ + +char ** CPL_STDCALL GDALGetRasterCategoryNames( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterCategoryNames", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetCategoryNames(); +} + +/************************************************************************/ +/* SetCategoryNames() */ +/************************************************************************/ + +/** + * \brief Set the category names for this band. + * + * See the GetCategoryNames() method for more on the interpretation of + * category names. + * + * This method is the same as the C function GDALSetRasterCategoryNames(). + * + * @param papszNames the NULL terminated StringList of category names. May + * be NULL to just clear the existing list. + * + * @return CE_None on success of CE_Failure on failure. If unsupported + * by the driver CE_Failure is returned, but no error message is reported. + */ + +CPLErr GDALRasterBand::SetCategoryNames( CPL_UNUSED char ** papszNames ) +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetCategoryNames() not supported for this dataset." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetCategoryNames() */ +/************************************************************************/ + +/** + * \brief Set the category names for this band. + * + * @see GDALRasterBand::SetCategoryNames() + */ + +CPLErr CPL_STDCALL +GDALSetRasterCategoryNames( GDALRasterBandH hBand, char ** papszNames ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterCategoryNames", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetCategoryNames( papszNames ); +} + +/************************************************************************/ +/* GetNoDataValue() */ +/************************************************************************/ + +/** + * \brief Fetch the no data value for this band. + * + * If there is no out of data value, an out of range value will generally + * be returned. The no data value for a band is generally a special marker + * value used to mark pixels that are not valid data. Such pixels should + * generally not be displayed, nor contribute to analysis operations. + * + * This method is the same as the C function GDALGetRasterNoDataValue(). + * + * @param pbSuccess pointer to a boolean to use to indicate if a value + * is actually associated with this layer. May be NULL (default). + * + * @return the nodata value for this band. + */ + +double GDALRasterBand::GetNoDataValue( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = FALSE; + + return -1e10; +} + +/************************************************************************/ +/* GDALGetRasterNoDataValue() */ +/************************************************************************/ + +/** + * \brief Fetch the no data value for this band. + * + * @see GDALRasterBand::GetNoDataValue() + */ + +double CPL_STDCALL +GDALGetRasterNoDataValue( GDALRasterBandH hBand, int *pbSuccess ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterNoDataValue", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetNoDataValue( pbSuccess ); +} + +/************************************************************************/ +/* SetNoDataValue() */ +/************************************************************************/ + +/** + * \brief Set the no data value for this band. + * + * To clear the nodata value, just set it with an "out of range" value. + * Complex band no data values must have an imagery component of zero. + * + * This method is the same as the C function GDALSetRasterNoDataValue(). + * + * @param dfNoData the value to set. + * + * @return CE_None on success, or CE_Failure on failure. If unsupported + * by the driver, CE_Failure is returned by no error message will have + * been emitted. + */ + +CPLErr GDALRasterBand::SetNoDataValue( CPL_UNUSED double dfNoData ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetNoDataValue() not supported for this dataset." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetRasterNoDataValue() */ +/************************************************************************/ + +/** + * \brief Set the no data value for this band. + * + * @see GDALRasterBand::SetNoDataValue() + */ + +CPLErr CPL_STDCALL +GDALSetRasterNoDataValue( GDALRasterBandH hBand, double dfValue ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterNoDataValue", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetNoDataValue( dfValue ); +} + +/************************************************************************/ +/* GetMaximum() */ +/************************************************************************/ + +/** + * \brief Fetch the maximum value for this band. + * + * For file formats that don't know this intrinsically, the maximum supported + * value for the data type will generally be returned. + * + * This method is the same as the C function GDALGetRasterMaximum(). + * + * @param pbSuccess pointer to a boolean to use to indicate if the + * returned value is a tight maximum or not. May be NULL (default). + * + * @return the maximum raster value (excluding no data pixels) + */ + +double GDALRasterBand::GetMaximum( int *pbSuccess ) + +{ + const char *pszValue = NULL; + + if( (pszValue = GetMetadataItem("STATISTICS_MAXIMUM")) != NULL ) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return CPLAtofM(pszValue); + } + + if( pbSuccess != NULL ) + *pbSuccess = FALSE; + + switch( eDataType ) + { + case GDT_Byte: + { + const char* pszPixelType = GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + if (pszPixelType != NULL && EQUAL(pszPixelType, "SIGNEDBYTE")) + return 127; + else + return 255; + } + + case GDT_UInt16: + return 65535; + + case GDT_Int16: + case GDT_CInt16: + return 32767; + + case GDT_Int32: + case GDT_CInt32: + return 2147483647.0; + + case GDT_UInt32: + return 4294967295.0; + + case GDT_Float32: + case GDT_CFloat32: + return 4294967295.0; /* not actually accurate */ + + case GDT_Float64: + case GDT_CFloat64: + return 4294967295.0; /* not actually accurate */ + + default: + return 4294967295.0; /* not actually accurate */ + } +} + +/************************************************************************/ +/* GDALGetRasterMaximum() */ +/************************************************************************/ + +/** + * \brief Fetch the maximum value for this band. + * + * @see GDALRasterBand::GetMaximum() + */ + +double CPL_STDCALL +GDALGetRasterMaximum( GDALRasterBandH hBand, int *pbSuccess ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterMaximum", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetMaximum( pbSuccess ); +} + +/************************************************************************/ +/* GetMinimum() */ +/************************************************************************/ + +/** + * \brief Fetch the minimum value for this band. + * + * For file formats that don't know this intrinsically, the minimum supported + * value for the data type will generally be returned. + * + * This method is the same as the C function GDALGetRasterMinimum(). + * + * @param pbSuccess pointer to a boolean to use to indicate if the + * returned value is a tight minimum or not. May be NULL (default). + * + * @return the minimum raster value (excluding no data pixels) + */ + +double GDALRasterBand::GetMinimum( int *pbSuccess ) + +{ + const char *pszValue = NULL; + + if( (pszValue = GetMetadataItem("STATISTICS_MINIMUM")) != NULL ) + { + if( pbSuccess != NULL ) + *pbSuccess = TRUE; + + return CPLAtofM(pszValue); + } + + if( pbSuccess != NULL ) + *pbSuccess = FALSE; + + switch( eDataType ) + { + case GDT_Byte: + { + const char* pszPixelType = GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + if (pszPixelType != NULL && EQUAL(pszPixelType, "SIGNEDBYTE")) + return -128; + else + return 0; + } + + case GDT_UInt16: + return 0; + + case GDT_Int16: + return -32768; + + case GDT_Int32: + return -2147483648.0; + + case GDT_UInt32: + return 0; + + case GDT_Float32: + return -4294967295.0; /* not actually accurate */ + + case GDT_Float64: + return -4294967295.0; /* not actually accurate */ + + default: + return -4294967295.0; /* not actually accurate */ + } +} + +/************************************************************************/ +/* GDALGetRasterMinimum() */ +/************************************************************************/ + +/** + * \brief Fetch the minimum value for this band. + * + * @see GDALRasterBand::GetMinimum() + */ + +double CPL_STDCALL +GDALGetRasterMinimum( GDALRasterBandH hBand, int *pbSuccess ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterMinimum", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetMinimum( pbSuccess ); +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +/** + * \brief How should this band be interpreted as color? + * + * GCI_Undefined is returned when the format doesn't know anything + * about the color interpretation. + * + * This method is the same as the C function + * GDALGetRasterColorInterpretation(). + * + * @return color interpretation value for band. + */ + +GDALColorInterp GDALRasterBand::GetColorInterpretation() + +{ + return GCI_Undefined; +} + +/************************************************************************/ +/* GDALGetRasterColorInterpretation() */ +/************************************************************************/ + +/** + * \brief How should this band be interpreted as color? + * + * @see GDALRasterBand::GetColorInterpretation() + */ + +GDALColorInterp CPL_STDCALL +GDALGetRasterColorInterpretation( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterColorInterpretation", GCI_Undefined ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetColorInterpretation(); +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +/** + * \brief Set color interpretation of a band. + * + * This method is the same as the C function GDALSetRasterColorInterpretation(). + * + * @param eColorInterp the new color interpretation to apply to this band. + * + * @return CE_None on success or CE_Failure if method is unsupported by format. + */ + +CPLErr GDALRasterBand::SetColorInterpretation( GDALColorInterp eColorInterp) + +{ + (void) eColorInterp; + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetColorInterpretation() not supported for this dataset." ); + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetRasterColorInterpretation() */ +/************************************************************************/ + +/** + * \brief Set color interpretation of a band. + * + * @see GDALRasterBand::SetColorInterpretation() + */ + +CPLErr CPL_STDCALL +GDALSetRasterColorInterpretation( GDALRasterBandH hBand, + GDALColorInterp eColorInterp ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterColorInterpretation", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetColorInterpretation(eColorInterp); +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +/** + * \brief Fetch the color table associated with band. + * + * If there is no associated color table, the return result is NULL. The + * returned color table remains owned by the GDALRasterBand, and can't + * be depended on for long, nor should it ever be modified by the caller. + * + * This method is the same as the C function GDALGetRasterColorTable(). + * + * @return internal color table, or NULL. + */ + +GDALColorTable *GDALRasterBand::GetColorTable() + +{ + return NULL; +} + +/************************************************************************/ +/* GDALGetRasterColorTable() */ +/************************************************************************/ + +/** + * \brief Fetch the color table associated with band. + * + * @see GDALRasterBand::GetColorTable() + */ + +GDALColorTableH CPL_STDCALL GDALGetRasterColorTable( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterColorTable", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return (GDALColorTableH)poBand->GetColorTable(); +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +/** + * \brief Set the raster color table. + * + * The driver will make a copy of all desired data in the colortable. It + * remains owned by the caller after the call. + * + * This method is the same as the C function GDALSetRasterColorTable(). + * + * @param poCT the color table to apply. This may be NULL to clear the color + * table (where supported). + * + * @return CE_None on success, or CE_Failure on failure. If the action is + * unsupported by the driver, a value of CE_Failure is returned, but no + * error is issued. + */ + +CPLErr GDALRasterBand::SetColorTable( GDALColorTable * poCT ) + +{ + (void) poCT; + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetColorTable() not supported for this dataset." ); + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetRasterColorTable() */ +/************************************************************************/ + +/** + * \brief Set the raster color table. + * + * @see GDALRasterBand::SetColorTable() + */ + +CPLErr CPL_STDCALL +GDALSetRasterColorTable( GDALRasterBandH hBand, GDALColorTableH hCT ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterColorTable", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetColorTable( static_cast(hCT) ); +} + +/************************************************************************/ +/* HasArbitraryOverviews() */ +/************************************************************************/ + +/** + * \brief Check for arbitrary overviews. + * + * This returns TRUE if the underlying datastore can compute arbitrary + * overviews efficiently, such as is the case with OGDI over a network. + * Datastores with arbitrary overviews don't generally have any fixed + * overviews, but the RasterIO() method can be used in downsampling mode + * to get overview data efficiently. + * + * This method is the same as the C function GDALHasArbitraryOverviews(), + * + * @return TRUE if arbitrary overviews available (efficiently), otherwise + * FALSE. + */ + +int GDALRasterBand::HasArbitraryOverviews() + +{ + return FALSE; +} + +/************************************************************************/ +/* GDALHasArbitraryOverviews() */ +/************************************************************************/ + +/** + * \brief Check for arbitrary overviews. + * + * @see GDALRasterBand::HasArbitraryOverviews() + */ + +int CPL_STDCALL GDALHasArbitraryOverviews( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALHasArbitraryOverviews", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->HasArbitraryOverviews(); +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +/** + * \brief Return the number of overview layers available. + * + * This method is the same as the C function GDALGetOverviewCount(). + * + * @return overview count, zero if none. + */ + +int GDALRasterBand::GetOverviewCount() + +{ + if( poDS != NULL && poDS->oOvManager.IsInitialized() ) + return poDS->oOvManager.GetOverviewCount( nBand ); + else + return 0; +} + +/************************************************************************/ +/* GDALGetOverviewCount() */ +/************************************************************************/ + +/** + * \brief Return the number of overview layers available. + * + * @see GDALRasterBand::GetOverviewCount() + */ + +int CPL_STDCALL GDALGetOverviewCount( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetOverviewCount", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetOverviewCount(); +} + + +/************************************************************************/ +/* GetOverview() */ +/************************************************************************/ + +/** + * \brief Fetch overview raster band object. + * + * This method is the same as the C function GDALGetOverview(). + * + * @param i overview index between 0 and GetOverviewCount()-1. + * + * @return overview GDALRasterBand. + */ + +GDALRasterBand * GDALRasterBand::GetOverview( int i ) + +{ + if( poDS != NULL && poDS->oOvManager.IsInitialized() ) + return poDS->oOvManager.GetOverview( nBand, i ); + else + return NULL; +} + +/************************************************************************/ +/* GDALGetOverview() */ +/************************************************************************/ + +/** + * \brief Fetch overview raster band object. + * + * @see GDALRasterBand::GetOverview() + */ + +GDALRasterBandH CPL_STDCALL GDALGetOverview( GDALRasterBandH hBand, int i ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetOverview", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return (GDALRasterBandH) poBand->GetOverview(i); +} + +/************************************************************************/ +/* GetRasterSampleOverview() */ +/************************************************************************/ + +/** + * \brief Fetch best sampling overview. + * + * Returns the most reduced overview of the given band that still satisfies + * the desired number of samples. This function can be used with zero + * as the number of desired samples to fetch the most reduced overview. + * The same band as was passed in will be returned if it has not overviews, + * or if none of the overviews have enough samples. + * + * This method is the same as the C functions GDALGetRasterSampleOverview() + * and GDALGetRasterSampleOverviewEx(). + * + * @param nDesiredSamples the returned band will have at least this many + * pixels. + * + * @return optimal overview or the band itself. + */ + +GDALRasterBand *GDALRasterBand::GetRasterSampleOverview( GUIntBig nDesiredSamples ) + +{ + double dfBestSamples = 0; + GDALRasterBand *poBestBand = this; + + dfBestSamples = GetXSize() * (double)GetYSize(); + + for( int iOverview = 0; iOverview < GetOverviewCount(); iOverview++ ) + { + GDALRasterBand *poOBand = GetOverview( iOverview ); + double dfOSamples = 0; + + if (poOBand == NULL) + continue; + + dfOSamples = poOBand->GetXSize() * (double)poOBand->GetYSize(); + + if( dfOSamples < dfBestSamples && dfOSamples > nDesiredSamples ) + { + dfBestSamples = dfOSamples; + poBestBand = poOBand; + } + } + + return poBestBand; +} + +/************************************************************************/ +/* GDALGetRasterSampleOverview() */ +/************************************************************************/ + +/** + * \brief Fetch best sampling overview. + * + * Use GDALGetRasterSampleOverviewEx() to be able to specify more than 2 + * billion samples. + * + * @see GDALRasterBand::GetRasterSampleOverview() + * @see GDALGetRasterSampleOverviewEx() + */ + +GDALRasterBandH CPL_STDCALL +GDALGetRasterSampleOverview( GDALRasterBandH hBand, int nDesiredSamples ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterSampleOverview", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return (GDALRasterBandH) + poBand->GetRasterSampleOverview( nDesiredSamples < 0 ? 0 : (GUIntBig)nDesiredSamples ); +} + +/************************************************************************/ +/* GDALGetRasterSampleOverviewEx() */ +/************************************************************************/ + +/** + * \brief Fetch best sampling overview. + * + * @see GDALRasterBand::GetRasterSampleOverview() + * @since GDAL 2.0 + */ + +GDALRasterBandH CPL_STDCALL +GDALGetRasterSampleOverviewEx( GDALRasterBandH hBand, GUIntBig nDesiredSamples ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterSampleOverviewEx", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return (GDALRasterBandH) + poBand->GetRasterSampleOverview( nDesiredSamples ); +} + +/************************************************************************/ +/* BuildOverviews() */ +/************************************************************************/ + +/** + * \brief Build raster overview(s) + * + * If the operation is unsupported for the indicated dataset, then + * CE_Failure is returned, and CPLGetLastErrorNo() will return + * CPLE_NotSupported. + * + * WARNING: It is not possible to build overviews for a single band in + * TIFF format, and thus this method does not work for TIFF format, or any + * formats that use the default overview building in TIFF format. Instead + * it is necessary to build overviews on the dataset as a whole using + * GDALDataset::BuildOverviews(). That makes this method pretty useless + * from a practical point of view. + * + * @param pszResampling one of "NEAREST", "GAUSS", "CUBIC", "AVERAGE", "MODE", + * "AVERAGE_MAGPHASE" or "NONE" controlling the downsampling method applied. + * @param nOverviews number of overviews to build. + * @param panOverviewList the list of overview decimation factors to build. + * @param pfnProgress a function to call to report progress, or NULL. + * @param pProgressData application data to pass to the progress function. + * + * @return CE_None on success or CE_Failure if the operation doesn't work. + */ + +CPLErr GDALRasterBand::BuildOverviews( const char * pszResampling, + int nOverviews, + int * panOverviewList, + GDALProgressFunc pfnProgress, + void * pProgressData ) + +{ + (void) pszResampling; + (void) nOverviews; + (void) panOverviewList; + (void) pfnProgress; + (void) pProgressData; + + ReportError( CE_Failure, CPLE_NotSupported, + "BuildOverviews() not supported for this dataset." ); + + return( CE_Failure ); +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +/** + * \brief Fetch the raster value offset. + * + * This value (in combination with the GetScale() value) is used to + * transform raw pixel values into the units returned by GetUnits(). + * For example this might be used to store elevations in GUInt16 bands + * with a precision of 0.1, and starting from -100. + * + * Units value = (raw value * scale) + offset + * + * For file formats that don't know this intrinsically a value of zero + * is returned. + * + * This method is the same as the C function GDALGetRasterOffset(). + * + * @param pbSuccess pointer to a boolean to use to indicate if the + * returned value is meaningful or not. May be NULL (default). + * + * @return the raster offset. + */ + +double GDALRasterBand::GetOffset( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = FALSE; + + return 0.0; +} + +/************************************************************************/ +/* GDALGetRasterOffset() */ +/************************************************************************/ + +/** + * \brief Fetch the raster value offset. + * + * @see GDALRasterBand::GetOffset() + */ + +double CPL_STDCALL GDALGetRasterOffset( GDALRasterBandH hBand, int *pbSuccess ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterOffset", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetOffset( pbSuccess ); +} + +/************************************************************************/ +/* SetOffset() */ +/************************************************************************/ + +/** + * \brief Set scaling offset. + * + * Very few formats implement this method. When not implemented it will + * issue a CPLE_NotSupported error and return CE_Failure. + * + * This method is the same as the C function GDALSetRasterOffset(). + * + * @param dfNewOffset the new offset. + * + * @return CE_None or success or CE_Failure on failure. + */ + +CPLErr GDALRasterBand::SetOffset( CPL_UNUSED double dfNewOffset ) +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetOffset() not supported on this raster band." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetRasterOffset() */ +/************************************************************************/ + +/** + * \brief Set scaling offset. + * + * @see GDALRasterBand::SetOffset() + */ + +CPLErr CPL_STDCALL +GDALSetRasterOffset( GDALRasterBandH hBand, double dfNewOffset ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterOffset", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetOffset( dfNewOffset ); +} + +/************************************************************************/ +/* GetScale() */ +/************************************************************************/ + +/** + * \brief Fetch the raster value scale. + * + * This value (in combination with the GetOffset() value) is used to + * transform raw pixel values into the units returned by GetUnits(). + * For example this might be used to store elevations in GUInt16 bands + * with a precision of 0.1, and starting from -100. + * + * Units value = (raw value * scale) + offset + * + * For file formats that don't know this intrinsically a value of one + * is returned. + * + * This method is the same as the C function GDALGetRasterScale(). + * + * @param pbSuccess pointer to a boolean to use to indicate if the + * returned value is meaningful or not. May be NULL (default). + * + * @return the raster scale. + */ + +double GDALRasterBand::GetScale( int *pbSuccess ) + +{ + if( pbSuccess != NULL ) + *pbSuccess = FALSE; + + return 1.0; +} + +/************************************************************************/ +/* GDALGetRasterScale() */ +/************************************************************************/ + +/** + * \brief Fetch the raster value scale. + * + * @see GDALRasterBand::GetScale() + */ + +double CPL_STDCALL GDALGetRasterScale( GDALRasterBandH hBand, int *pbSuccess ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterScale", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetScale( pbSuccess ); +} + +/************************************************************************/ +/* SetScale() */ +/************************************************************************/ + +/** + * \brief Set scaling ratio. + * + * Very few formats implement this method. When not implemented it will + * issue a CPLE_NotSupported error and return CE_Failure. + * + * This method is the same as the C function GDALSetRasterScale(). + * + * @param dfNewScale the new scale. + * + * @return CE_None or success or CE_Failure on failure. + */ + +CPLErr GDALRasterBand::SetScale( CPL_UNUSED double dfNewScale ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetScale() not supported on this raster band." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetRasterScale() */ +/************************************************************************/ + +/** + * \brief Set scaling ratio. + * + * @see GDALRasterBand::SetScale() + */ + +CPLErr CPL_STDCALL +GDALSetRasterScale( GDALRasterBandH hBand, double dfNewOffset ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterScale", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetScale( dfNewOffset ); +} + +/************************************************************************/ +/* GetUnitType() */ +/************************************************************************/ + +/** + * \brief Return raster unit type. + * + * Return a name for the units of this raster's values. For instance, it + * might be "m" for an elevation model in meters, or "ft" for feet. If no + * units are available, a value of "" will be returned. The returned string + * should not be modified, nor freed by the calling application. + * + * This method is the same as the C function GDALGetRasterUnitType(). + * + * @return unit name string. + */ + +const char *GDALRasterBand::GetUnitType() + +{ + return ""; +} + +/************************************************************************/ +/* GDALGetRasterUnitType() */ +/************************************************************************/ + +/** + * \brief Return raster unit type. + * + * @see GDALRasterBand::GetUnitType() + */ + +const char * CPL_STDCALL GDALGetRasterUnitType( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterUnitType", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetUnitType(); +} + +/************************************************************************/ +/* SetUnitType() */ +/************************************************************************/ + +/** + * \brief Set unit type. + * + * Set the unit type for a raster band. Values should be one of + * "" (the default indicating it is unknown), "m" indicating meters, + * or "ft" indicating feet, though other nonstandard values are allowed. + * + * This method is the same as the C function GDALSetRasterUnitType(). + * + * @param pszNewValue the new unit type value. + * + * @return CE_None on success or CE_Failure if not succuessful, or + * unsupported. + */ + +CPLErr GDALRasterBand::SetUnitType( CPL_UNUSED const char *pszNewValue ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetUnitType() not supported on this raster band." ); + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetRasterUnitType() */ +/************************************************************************/ + +/** + * \brief Set unit type. + * + * @see GDALRasterBand::SetUnitType() + * + * @since GDAL 1.8.0 + */ + +CPLErr CPL_STDCALL GDALSetRasterUnitType( GDALRasterBandH hBand, const char *pszNewValue ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterUnitType", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetUnitType(pszNewValue); +} + +/************************************************************************/ +/* GetXSize() */ +/************************************************************************/ + +/** + * \brief Fetch XSize of raster. + * + * This method is the same as the C function GDALGetRasterBandXSize(). + * + * @return the width in pixels of this band. + */ + +int GDALRasterBand::GetXSize() + +{ + return nRasterXSize; +} + +/************************************************************************/ +/* GDALGetRasterBandXSize() */ +/************************************************************************/ + +/** + * \brief Fetch XSize of raster. + * + * @see GDALRasterBand::GetXSize() + */ + +int CPL_STDCALL GDALGetRasterBandXSize( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterBandXSize", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetXSize(); +} + +/************************************************************************/ +/* GetYSize() */ +/************************************************************************/ + +/** + * \brief Fetch YSize of raster. + * + * This method is the same as the C function GDALGetRasterBandYSize(). + * + * @return the height in pixels of this band. + */ + +int GDALRasterBand::GetYSize() + +{ + return nRasterYSize; +} + +/************************************************************************/ +/* GDALGetRasterBandYSize() */ +/************************************************************************/ + +/** + * \brief Fetch YSize of raster. + * + * @see GDALRasterBand::GetYSize() + */ + +int CPL_STDCALL GDALGetRasterBandYSize( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterBandYSize", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetYSize(); +} + +/************************************************************************/ +/* GetBand() */ +/************************************************************************/ + +/** + * \brief Fetch the band number. + * + * This method returns the band that this GDALRasterBand objects represents + * within it's dataset. This method may return a value of 0 to indicate + * GDALRasterBand objects without an apparently relationship to a dataset, + * such as GDALRasterBands serving as overviews. + * + * This method is the same as the C function GDALGetBandNumber(). + * + * @return band number (1+) or 0 if the band number isn't known. + */ + +int GDALRasterBand::GetBand() + +{ + return nBand; +} + +/************************************************************************/ +/* GDALGetBandNumber() */ +/************************************************************************/ + +/** + * \brief Fetch the band number. + * + * @see GDALRasterBand::GetBand() + */ + +int CPL_STDCALL GDALGetBandNumber( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetBandNumber", 0 ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetBand(); +} + +/************************************************************************/ +/* GetDataset() */ +/************************************************************************/ + +/** + * \brief Fetch the owning dataset handle. + * + * Note that some GDALRasterBands are not considered to be a part of a dataset, + * such as overviews or other "freestanding" bands. + * + * This method is the same as the C function GDALGetBandDataset(). + * + * @return the pointer to the GDALDataset to which this band belongs, or + * NULL if this cannot be determined. + */ + +GDALDataset *GDALRasterBand::GetDataset() + +{ + return poDS; +} + +/************************************************************************/ +/* GDALGetBandDataset() */ +/************************************************************************/ + +/** + * \brief Fetch the owning dataset handle. + * + * @see GDALRasterBand::GetDataset() + */ + +GDALDatasetH CPL_STDCALL GDALGetBandDataset( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetBandDataset", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return (GDALDatasetH) poBand->GetDataset(); +} + +/************************************************************************/ +/* GetHistogram() */ +/************************************************************************/ + +/** + * \brief Compute raster histogram. + * + * Note that the bucket size is (dfMax-dfMin) / nBuckets. + * + * For example to compute a simple 256 entry histogram of eight bit data, + * the following would be suitable. The unusual bounds are to ensure that + * bucket boundaries don't fall right on integer values causing possible errors + * due to rounding after scaling. +
    +    GUIntBig anHistogram[256];
    +
    +    poBand->GetHistogram( -0.5, 255.5, 256, anHistogram, FALSE, FALSE, 
    +                          GDALDummyProgress, NULL );
    +
    + * + * Note that setting bApproxOK will generally result in a subsampling of the + * file, and will utilize overviews if available. It should generally + * produce a representative histogram for the data that is suitable for use + * in generating histogram based luts for instance. Generally bApproxOK is + * much faster than an exactly computed histogram. + * + * This method is the same as the C functions GDALGetRasterHistogram() and + * GDALGetRasterHistogramEx(). + * + * @param dfMin the lower bound of the histogram. + * @param dfMax the upper bound of the histogram. + * @param nBuckets the number of buckets in panHistogram. + * @param panHistogram array into which the histogram totals are placed. + * @param bIncludeOutOfRange if TRUE values below the histogram range will + * mapped into panHistogram[0], and values above will be mapped into + * panHistogram[nBuckets-1] otherwise out of range values are discarded. + * @param bApproxOK TRUE if an approximate, or incomplete histogram OK. + * @param pfnProgress function to report progress to completion. + * @param pProgressData application data to pass to pfnProgress. + * + * @return CE_None on success, or CE_Failure if something goes wrong. + */ + +CPLErr GDALRasterBand::GetHistogram( double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + CPLAssert( NULL != panHistogram ); + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* If we have overviews, use them for the histogram. */ +/* -------------------------------------------------------------------- */ + if( bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews() ) + { + // FIXME: should we use the most reduced overview here or use some + // minimum number of samples like GDALRasterBand::ComputeStatistics() + // does? + GDALRasterBand *poBestOverview = GetRasterSampleOverview( 0 ); + + if( poBestOverview != this ) + { + return poBestOverview->GetHistogram( dfMin, dfMax, nBuckets, + panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); + } + } + +/* -------------------------------------------------------------------- */ +/* Read actual data and build histogram. */ +/* -------------------------------------------------------------------- */ + if( !pfnProgress( 0.0, "Compute Histogram", pProgressData ) ) + { + ReportError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + + GDALRasterIOExtraArg sExtraArg; + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + + const double dfScale = nBuckets / (dfMax - dfMin); + memset( panHistogram, 0, sizeof(GUIntBig) * nBuckets ); + + int bGotNoDataValue; + const double dfNoDataValue = GetNoDataValue( &bGotNoDataValue ); + bGotNoDataValue = bGotNoDataValue && !CPLIsNan(dfNoDataValue); + /* Not advertized. May be removed at any time. Just as a provision if the */ + /* old behaviour made sense somethimes... */ + bGotNoDataValue = bGotNoDataValue && + !CSLTestBoolean(CPLGetConfigOption("GDAL_NODATA_IN_HISTOGRAM", "NO")); + + const char* pszPixelType = GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + int bSignedByte = (pszPixelType != NULL && EQUAL(pszPixelType, "SIGNEDBYTE")); + + if ( bApproxOK && HasArbitraryOverviews() ) + { +/* -------------------------------------------------------------------- */ +/* Figure out how much the image should be reduced to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + void *pData; + int nXReduced, nYReduced; + double dfReduction = sqrt( + (double)nRasterXSize * nRasterYSize / GDALSTAT_APPROX_NUMSAMPLES ); + + if ( dfReduction > 1.0 ) + { + nXReduced = (int)( nRasterXSize / dfReduction ); + nYReduced = (int)( nRasterYSize / dfReduction ); + + // Catch the case of huge resizing ratios here + if ( nXReduced == 0 ) + nXReduced = 1; + if ( nYReduced == 0 ) + nYReduced = 1; + } + else + { + nXReduced = nRasterXSize; + nYReduced = nRasterYSize; + } + + pData = + CPLMalloc(GDALGetDataTypeSize(eDataType)/8 * nXReduced * nYReduced); + + CPLErr eErr = IRasterIO( GF_Read, 0, 0, nRasterXSize, nRasterYSize, pData, + nXReduced, nYReduced, eDataType, 0, 0, &sExtraArg ); + if ( eErr != CE_None ) + { + CPLFree(pData); + return eErr; + } + + /* this isn't the fastest way to do this, but is easier for now */ + for( int iY = 0; iY < nYReduced; iY++ ) + { + for( int iX = 0; iX < nXReduced; iX++ ) + { + int iOffset = iX + iY * nXReduced; + int nIndex; + double dfValue = 0.0; + + switch( eDataType ) + { + case GDT_Byte: + { + if (bSignedByte) + dfValue = ((signed char *)pData)[iOffset]; + else + dfValue = ((GByte *)pData)[iOffset]; + break; + } + case GDT_UInt16: + dfValue = ((GUInt16 *)pData)[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *)pData)[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *)pData)[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *)pData)[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_Float64: + dfValue = ((double *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_CInt16: + { + double dfReal = ((GInt16 *)pData)[iOffset*2]; + double dfImag = ((GInt16 *)pData)[iOffset*2+1]; + if ( CPLIsNan(dfReal) || CPLIsNan(dfImag) ) + continue; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + case GDT_CInt32: + { + double dfReal = ((GInt32 *)pData)[iOffset*2]; + double dfImag = ((GInt32 *)pData)[iOffset*2+1]; + if ( CPLIsNan(dfReal) || CPLIsNan(dfImag) ) + continue; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + case GDT_CFloat32: + { + double dfReal = ((float *)pData)[iOffset*2]; + double dfImag = ((float *)pData)[iOffset*2+1]; + if ( CPLIsNan(dfReal) || CPLIsNan(dfImag) ) + continue; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + case GDT_CFloat64: + { + double dfReal = ((double *)pData)[iOffset*2]; + double dfImag = ((double *)pData)[iOffset*2+1]; + if ( CPLIsNan(dfReal) || CPLIsNan(dfImag) ) + continue; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + default: + CPLAssert( FALSE ); + } + + if( bGotNoDataValue && ARE_REAL_EQUAL(dfValue, dfNoDataValue) ) + continue; + + nIndex = (int) floor((dfValue - dfMin) * dfScale); + + if( nIndex < 0 ) + { + if( bIncludeOutOfRange ) + panHistogram[0]++; + } + else if( nIndex >= nBuckets ) + { + if( bIncludeOutOfRange ) + panHistogram[nBuckets-1]++; + } + else + { + panHistogram[nIndex]++; + } + } + } + + CPLFree( pData ); + } + + else // No arbitrary overviews + { + int nSampleRate; + + if( !InitBlockInfo() ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Figure out the ratio of blocks we will read to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + + if ( bApproxOK ) + { + nSampleRate = + (int) MAX(1,sqrt((double) nBlocksPerRow * nBlocksPerColumn)); + } + else + nSampleRate = 1; + +/* -------------------------------------------------------------------- */ +/* Read the blocks, and add to histogram. */ +/* -------------------------------------------------------------------- */ + for( int iSampleBlock = 0; + iSampleBlock < nBlocksPerRow * nBlocksPerColumn; + iSampleBlock += nSampleRate ) + { + int iXBlock, iYBlock, nXCheck, nYCheck; + GDALRasterBlock *poBlock; + void* pData; + + if( !pfnProgress( iSampleBlock + / ((double)nBlocksPerRow * nBlocksPerColumn), + "Compute Histogram", pProgressData ) ) + return CE_Failure; + + iYBlock = iSampleBlock / nBlocksPerRow; + iXBlock = iSampleBlock - nBlocksPerRow * iYBlock; + + poBlock = GetLockedBlockRef( iXBlock, iYBlock ); + if( poBlock == NULL ) + return CE_Failure; + if( poBlock->GetDataRef() == NULL ) + { + poBlock->DropLock(); + return CE_Failure; + } + + pData = poBlock->GetDataRef(); + + if( (iXBlock+1) * nBlockXSize > GetXSize() ) + nXCheck = GetXSize() - iXBlock * nBlockXSize; + else + nXCheck = nBlockXSize; + + if( (iYBlock+1) * nBlockYSize > GetYSize() ) + nYCheck = GetYSize() - iYBlock * nBlockYSize; + else + nYCheck = nBlockYSize; + + /* this is a special case for a common situation */ + if( eDataType == GDT_Byte && !bSignedByte + && dfScale == 1.0 && (dfMin >= -0.5 && dfMin <= 0.5) + && nYCheck == nBlockYSize && nXCheck == nBlockXSize + && nBuckets == 256 ) + { + int nPixels = nXCheck * nYCheck; + GByte *pabyData = (GByte *) pData; + + for( int i = 0; i < nPixels; i++ ) + if (! (bGotNoDataValue && (pabyData[i] == (GByte)dfNoDataValue))) + { + panHistogram[pabyData[i]]++; + } + + poBlock->DropLock(); + continue; /* to next sample block */ + } + + /* this isn't the fastest way to do this, but is easier for now */ + for( int iY = 0; iY < nYCheck; iY++ ) + { + for( int iX = 0; iX < nXCheck; iX++ ) + { + int iOffset = iX + iY * nBlockXSize; + int nIndex; + double dfValue = 0.0; + + switch( eDataType ) + { + case GDT_Byte: + { + if (bSignedByte) + dfValue = ((signed char *) pData)[iOffset]; + else + dfValue = ((GByte *) pData)[iOffset]; + break; + } + case GDT_UInt16: + dfValue = ((GUInt16 *) pData)[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *) pData)[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *) pData)[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *) pData)[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *) pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_Float64: + dfValue = ((double *) pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_CInt16: + { + double dfReal = + ((GInt16 *) pData)[iOffset*2]; + double dfImag = + ((GInt16 *) pData)[iOffset*2+1]; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + case GDT_CInt32: + { + double dfReal = + ((GInt32 *) pData)[iOffset*2]; + double dfImag = + ((GInt32 *) pData)[iOffset*2+1]; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + case GDT_CFloat32: + { + double dfReal = + ((float *) pData)[iOffset*2]; + double dfImag = + ((float *) pData)[iOffset*2+1]; + if ( CPLIsNan(dfReal) || CPLIsNan(dfImag) ) + continue; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + case GDT_CFloat64: + { + double dfReal = + ((double *) pData)[iOffset*2]; + double dfImag = + ((double *) pData)[iOffset*2+1]; + if ( CPLIsNan(dfReal) || CPLIsNan(dfImag) ) + continue; + dfValue = sqrt( dfReal * dfReal + dfImag * dfImag ); + } + break; + default: + CPLAssert( FALSE ); + return CE_Failure; + } + + if( bGotNoDataValue && ARE_REAL_EQUAL(dfValue, dfNoDataValue) ) + continue; + + nIndex = (int) floor((dfValue - dfMin) * dfScale); + + if( nIndex < 0 ) + { + if( bIncludeOutOfRange ) + panHistogram[0]++; + } + else if( nIndex >= nBuckets ) + { + if( bIncludeOutOfRange ) + panHistogram[nBuckets-1]++; + } + else + { + panHistogram[nIndex]++; + } + } + } + + poBlock->DropLock(); + } + } + + pfnProgress( 1.0, "Compute Histogram", pProgressData ); + + return CE_None; +} + +/************************************************************************/ +/* GDALGetRasterHistogram() */ +/************************************************************************/ + +/** + * \brief Compute raster histogram. + * + * Use GDALGetRasterHistogramEx() instead to get correct counts for values + * exceeding 2 billion. + * + * @see GDALRasterBand::GetHistogram() + * @see GDALGetRasterHistogramEx() + */ + +CPLErr CPL_STDCALL +GDALGetRasterHistogram( GDALRasterBandH hBand, + double dfMin, double dfMax, + int nBuckets, int *panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterHistogram", CE_Failure ); + VALIDATE_POINTER1( panHistogram, "GDALGetRasterHistogram", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + + GUIntBig* panHistogramTemp = (GUIntBig*)VSIMalloc2(sizeof(GUIntBig), nBuckets); + if( panHistogramTemp == NULL ) + { + poBand->ReportError( CE_Failure, CPLE_OutOfMemory, + "Out of memory in GDALGetRasterHistogram()." ); + return CE_Failure; + } + + CPLErr eErr = poBand->GetHistogram( dfMin, dfMax, nBuckets, panHistogramTemp, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); + + if( eErr == CE_None ) + { + for(int i=0;i INT_MAX ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Count for bucket %d, which is " CPL_FRMT_GUIB " exceeds maximum 32 bit value", + i, panHistogramTemp[i]); + panHistogram[i] = INT_MAX; + } + else + panHistogram[i] = (int)panHistogramTemp[i]; + } + } + + CPLFree(panHistogramTemp); + + return eErr; +} + +/************************************************************************/ +/* GDALGetRasterHistogramEx() */ +/************************************************************************/ + +/** + * \brief Compute raster histogram. + * + * @see GDALRasterBand::GetHistogram() + * + * @since GDAL 2.0 + */ + +CPLErr CPL_STDCALL +GDALGetRasterHistogramEx( GDALRasterBandH hBand, + double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram, + int bIncludeOutOfRange, int bApproxOK, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterHistogramEx", CE_Failure ); + VALIDATE_POINTER1( panHistogram, "GDALGetRasterHistogramEx", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + + return poBand->GetHistogram( dfMin, dfMax, nBuckets, panHistogram, + bIncludeOutOfRange, bApproxOK, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* GetDefaultHistogram() */ +/************************************************************************/ + +/** + * \brief Fetch default raster histogram. + * + * The default method in GDALRasterBand will compute a default histogram. This + * method is overriden by derived classes (such as GDALPamRasterBand, VRTDataset, HFADataset...) + * that may be able to fetch efficiently an already stored histogram. + * + * This method is the same as the C functions GDALGetDefaultHistogram() and + * GDALGetDefaultHistogramEx(). + * + * @param pdfMin pointer to double value that will contain the lower bound of the histogram. + * @param pdfMax pointer to double value that will contain the upper bound of the histogram. + * @param pnBuckets pointer to int value that will contain the number of buckets in *ppanHistogram. + * @param ppanHistogram pointer to array into which the histogram totals are placed. To be freed with VSIFree + * @param bForce TRUE to force the computation. If FALSE and no default histogram is available, the method will return CE_Warning + * @param pfnProgress function to report progress to completion. + * @param pProgressData application data to pass to pfnProgress. + * + * @return CE_None on success, CE_Failure if something goes wrong, or + * CE_Warning if no default histogram is available. + */ + +CPLErr + GDALRasterBand::GetDefaultHistogram( double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig **ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + CPLAssert( NULL != pnBuckets ); + CPLAssert( NULL != ppanHistogram ); + CPLAssert( NULL != pdfMin ); + CPLAssert( NULL != pdfMax ); + + *pnBuckets = 0; + *ppanHistogram = NULL; + + if( !bForce ) + return CE_Warning; + + int nBuckets = 256; + + const char* pszPixelType = GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + int bSignedByte = (pszPixelType != NULL && EQUAL(pszPixelType, "SIGNEDBYTE")); + + if( GetRasterDataType() == GDT_Byte && !bSignedByte) + { + *pdfMin = -0.5; + *pdfMax = 255.5; + } + else + { + CPLErr eErr = CE_Failure; + double dfHalfBucket = 0; + + eErr = GetStatistics( TRUE, TRUE, pdfMin, pdfMax, NULL, NULL ); + dfHalfBucket = (*pdfMax - *pdfMin) / (2 * (nBuckets - 1)); + *pdfMin -= dfHalfBucket; + *pdfMax += dfHalfBucket; + + if( eErr != CE_None ) + return eErr; + } + + *ppanHistogram = (GUIntBig *) VSICalloc(sizeof(GUIntBig), nBuckets); + if( *ppanHistogram == NULL ) + { + ReportError( CE_Failure, CPLE_OutOfMemory, + "Out of memory in InitBlockInfo()." ); + return CE_Failure; + } + + *pnBuckets = nBuckets; + return GetHistogram( *pdfMin, *pdfMax, *pnBuckets, *ppanHistogram, + TRUE, FALSE, pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* GDALGetDefaultHistogram() */ +/************************************************************************/ + +/** + * \brief Fetch default raster histogram. + * + * Use GDALGetRasterHistogramEx() instead to get correct counts for values + * exceeding 2 billion. + * + * @see GDALRasterBand::GDALGetDefaultHistogram() + * @see GDALGetRasterHistogramEx() + */ + +CPLErr CPL_STDCALL GDALGetDefaultHistogram( GDALRasterBandH hBand, + double *pdfMin, double *pdfMax, + int *pnBuckets, int **ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetDefaultHistogram", CE_Failure ); + VALIDATE_POINTER1( pdfMin, "GDALGetDefaultHistogram", CE_Failure ); + VALIDATE_POINTER1( pdfMax, "GDALGetDefaultHistogram", CE_Failure ); + VALIDATE_POINTER1( pnBuckets, "GDALGetDefaultHistogram", CE_Failure ); + VALIDATE_POINTER1( ppanHistogram, "GDALGetDefaultHistogram", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + GUIntBig* panHistogramTemp = NULL; + CPLErr eErr = poBand->GetDefaultHistogram( pdfMin, pdfMax, + pnBuckets, &panHistogramTemp, bForce, pfnProgress, pProgressData ); + if( eErr == CE_None ) + { + int nBuckets = *pnBuckets; + *ppanHistogram = (int*) VSIMalloc2(sizeof(int), nBuckets); + if( *ppanHistogram == NULL ) + { + poBand->ReportError( CE_Failure, CPLE_OutOfMemory, + "Out of memory in GDALGetDefaultHistogram()." ); + VSIFree(panHistogramTemp); + return CE_Failure; + } + + for(int i=0;i INT_MAX ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Count for bucket %d, which is " CPL_FRMT_GUIB " exceeds maximum 32 bit value", + i, panHistogramTemp[i]); + (*ppanHistogram)[i] = INT_MAX; + } + else + (*ppanHistogram)[i] = (int)panHistogramTemp[i]; + } + + CPLFree(panHistogramTemp); + } + else + *ppanHistogram = NULL; + + return eErr; +} + +/************************************************************************/ +/* GDALGetDefaultHistogramEx() */ +/************************************************************************/ + +/** + * \brief Fetch default raster histogram. + * + * @see GDALRasterBand::GetDefaultHistogram() + * + * @since GDAL 2.0 + */ + +CPLErr CPL_STDCALL GDALGetDefaultHistogramEx( GDALRasterBandH hBand, + double *pdfMin, double *pdfMax, + int *pnBuckets, GUIntBig **ppanHistogram, + int bForce, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetDefaultHistogram", CE_Failure ); + VALIDATE_POINTER1( pdfMin, "GDALGetDefaultHistogram", CE_Failure ); + VALIDATE_POINTER1( pdfMax, "GDALGetDefaultHistogram", CE_Failure ); + VALIDATE_POINTER1( pnBuckets, "GDALGetDefaultHistogram", CE_Failure ); + VALIDATE_POINTER1( ppanHistogram, "GDALGetDefaultHistogram", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetDefaultHistogram( pdfMin, pdfMax, + pnBuckets, ppanHistogram, bForce, pfnProgress, pProgressData ); +} +/************************************************************************/ +/* AdviseRead() */ +/************************************************************************/ + +/** + * \brief Advise driver of upcoming read requests. + * + * Some GDAL drivers operate more efficiently if they know in advance what + * set of upcoming read requests will be made. The AdviseRead() method allows + * an application to notify the driver of the region of interest, + * and at what resolution the region will be read. + * + * Many drivers just ignore the AdviseRead() call, but it can dramatically + * accelerate access via some drivers. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the pData data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param papszOptions a list of name=value strings with special control + * options. Normally this is NULL. + * + * @return CE_Failure if the request is invalid and CE_None if it works or + * is ignored. + */ + +CPLErr GDALRasterBand::AdviseRead( + CPL_UNUSED int nXOff, CPL_UNUSED int nYOff, + CPL_UNUSED int nXSize, CPL_UNUSED int nYSize, + CPL_UNUSED int nBufXSize, CPL_UNUSED int nBufYSize, + CPL_UNUSED GDALDataType eBufType, CPL_UNUSED char **papszOptions ) +{ + return CE_None; +} + +/************************************************************************/ +/* GDALRasterAdviseRead() */ +/************************************************************************/ + + +/** + * \brief Advise driver of upcoming read requests. + * + * @see GDALRasterBand::AdviseRead() + */ + +CPLErr CPL_STDCALL +GDALRasterAdviseRead( GDALRasterBandH hBand, + int nXOff, int nYOff, int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eDT, char **papszOptions ) + +{ + VALIDATE_POINTER1( hBand, "GDALRasterAdviseRead", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->AdviseRead( nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eDT, papszOptions ); +} + +/************************************************************************/ +/* GetStatistics() */ +/************************************************************************/ + +/** + * \brief Fetch image statistics. + * + * Returns the minimum, maximum, mean and standard deviation of all + * pixel values in this band. If approximate statistics are sufficient, + * the bApproxOK flag can be set to true in which case overviews, or a + * subset of image tiles may be used in computing the statistics. + * + * If bForce is FALSE results will only be returned if it can be done + * quickly (ie. without scanning the data). If bForce is FALSE and + * results cannot be returned efficiently, the method will return CE_Warning + * but no warning will have been issued. This is a non-standard use of + * the CE_Warning return value to indicate "nothing done". + * + * Note that file formats using PAM (Persistent Auxiliary Metadata) services + * will generally cache statistics in the .pam file allowing fast fetch + * after the first request. + * + * This method is the same as the C function GDALGetRasterStatistics(). + * + * @param bApproxOK If TRUE statistics may be computed based on overviews + * or a subset of all tiles. + * + * @param bForce If FALSE statistics will only be returned if it can + * be done without rescanning the image. + * + * @param pdfMin Location into which to load image minimum (may be NULL). + * + * @param pdfMax Location into which to load image maximum (may be NULL).- + * + * @param pdfMean Location into which to load image mean (may be NULL). + * + * @param pdfStdDev Location into which to load image standard deviation + * (may be NULL). + * + * @return CE_None on success, CE_Warning if no values returned, + * CE_Failure if an error occurs. + */ + +CPLErr GDALRasterBand::GetStatistics( int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev ) + +{ + double dfMin=0.0, dfMax=0.0; + +/* -------------------------------------------------------------------- */ +/* Do we already have metadata items for the requested values? */ +/* -------------------------------------------------------------------- */ + if( (pdfMin == NULL || GetMetadataItem("STATISTICS_MINIMUM") != NULL) + && (pdfMax == NULL || GetMetadataItem("STATISTICS_MAXIMUM") != NULL) + && (pdfMean == NULL || GetMetadataItem("STATISTICS_MEAN") != NULL) + && (pdfStdDev == NULL || GetMetadataItem("STATISTICS_STDDEV") != NULL) ) + { + if( pdfMin != NULL ) + *pdfMin = CPLAtofM(GetMetadataItem("STATISTICS_MINIMUM")); + if( pdfMax != NULL ) + *pdfMax = CPLAtofM(GetMetadataItem("STATISTICS_MAXIMUM")); + if( pdfMean != NULL ) + *pdfMean = CPLAtofM(GetMetadataItem("STATISTICS_MEAN")); + if( pdfStdDev != NULL ) + *pdfStdDev = CPLAtofM(GetMetadataItem("STATISTICS_STDDEV")); + + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Does the driver already know the min/max? */ +/* -------------------------------------------------------------------- */ + if( bApproxOK && pdfMean == NULL && pdfStdDev == NULL ) + { + int bSuccessMin, bSuccessMax; + + dfMin = GetMinimum( &bSuccessMin ); + dfMax = GetMaximum( &bSuccessMax ); + + if( bSuccessMin && bSuccessMax ) + { + if( pdfMin != NULL ) + *pdfMin = dfMin; + if( pdfMax != NULL ) + *pdfMax = dfMax; + return CE_None; + } + } + +/* -------------------------------------------------------------------- */ +/* Either return without results, or force computation. */ +/* -------------------------------------------------------------------- */ + if( !bForce ) + return CE_Warning; + else + return ComputeStatistics( bApproxOK, + pdfMin, pdfMax, pdfMean, pdfStdDev, + GDALDummyProgress, NULL ); +} + +/************************************************************************/ +/* GDALGetRasterStatistics() */ +/************************************************************************/ + +/** + * \brief Fetch image statistics. + * + * @see GDALRasterBand::GetStatistics() + */ + +CPLErr CPL_STDCALL GDALGetRasterStatistics( + GDALRasterBandH hBand, int bApproxOK, int bForce, + double *pdfMin, double *pdfMax, double *pdfMean, double *pdfStdDev ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetRasterStatistics", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetStatistics( + bApproxOK, bForce, pdfMin, pdfMax, pdfMean, pdfStdDev ); +} + +/************************************************************************/ +/* ComputeStatistics() */ +/************************************************************************/ + +/** + * \brief Compute image statistics. + * + * Returns the minimum, maximum, mean and standard deviation of all + * pixel values in this band. If approximate statistics are sufficient, + * the bApproxOK flag can be set to true in which case overviews, or a + * subset of image tiles may be used in computing the statistics. + * + * Once computed, the statistics will generally be "set" back on the + * raster band using SetStatistics(). + * + * This method is the same as the C function GDALComputeRasterStatistics(). + * + * @param bApproxOK If TRUE statistics may be computed based on overviews + * or a subset of all tiles. + * + * @param pdfMin Location into which to load image minimum (may be NULL). + * + * @param pdfMax Location into which to load image maximum (may be NULL).- + * + * @param pdfMean Location into which to load image mean (may be NULL). + * + * @param pdfStdDev Location into which to load image standard deviation + * (may be NULL). + * + * @param pfnProgress a function to call to report progress, or NULL. + * + * @param pProgressData application data to pass to the progress function. + * + * @return CE_None on success, or CE_Failure if an error occurs or processing + * is terminated by the user. + */ + +CPLErr +GDALRasterBand::ComputeStatistics( int bApproxOK, + double *pdfMin, double *pdfMax, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* If we have overview bands, use them for statistics. */ +/* -------------------------------------------------------------------- */ + if( bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews() ) + { + GDALRasterBand *poBand; + + poBand = GetRasterSampleOverview( GDALSTAT_APPROX_NUMSAMPLES ); + + if( poBand != this ) + return poBand->ComputeStatistics( FALSE, + pdfMin, pdfMax, + pdfMean, pdfStdDev, + pfnProgress, pProgressData ); + } + +/* -------------------------------------------------------------------- */ +/* Read actual data and compute statistics. */ +/* -------------------------------------------------------------------- */ + double dfMin = 0.0, dfMax = 0.0; + int bGotNoDataValue, bFirstValue = TRUE; + /* Using Welford algorithm ( http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance ) */ + /* to compute standard deviation in a more numerically robust way than */ + /* the difference of the sum of square values with the square of the sum */ + /* dfMean and dfM2 are updated at each sample */ + /* dfM2 is the sum of square of differences to the current mean */ + double dfMean = 0.0, dfM2 = 0.0; + GIntBig nSampleCount = 0; + + GDALRasterIOExtraArg sExtraArg; + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + + if( !pfnProgress( 0.0, "Compute Statistics", pProgressData ) ) + { + ReportError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + + const double dfNoDataValue = GetNoDataValue( &bGotNoDataValue ); + bGotNoDataValue = bGotNoDataValue && !CPLIsNan(dfNoDataValue); + + const char* pszPixelType = GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + int bSignedByte = (pszPixelType != NULL && EQUAL(pszPixelType, "SIGNEDBYTE")); + + if ( bApproxOK && HasArbitraryOverviews() ) + { +/* -------------------------------------------------------------------- */ +/* Figure out how much the image should be reduced to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + void *pData; + int nXReduced, nYReduced; + double dfReduction = sqrt( + (double)nRasterXSize * nRasterYSize / GDALSTAT_APPROX_NUMSAMPLES ); + + if ( dfReduction > 1.0 ) + { + nXReduced = (int)( nRasterXSize / dfReduction ); + nYReduced = (int)( nRasterYSize / dfReduction ); + + // Catch the case of huge resizing ratios here + if ( nXReduced == 0 ) + nXReduced = 1; + if ( nYReduced == 0 ) + nYReduced = 1; + } + else + { + nXReduced = nRasterXSize; + nYReduced = nRasterYSize; + } + + pData = + CPLMalloc(GDALGetDataTypeSize(eDataType)/8 * nXReduced * nYReduced); + + CPLErr eErr = IRasterIO( GF_Read, 0, 0, nRasterXSize, nRasterYSize, pData, + nXReduced, nYReduced, eDataType, 0, 0, &sExtraArg ); + if ( eErr != CE_None ) + { + CPLFree(pData); + return eErr; + } + + /* this isn't the fastest way to do this, but is easier for now */ + for( int iY = 0; iY < nYReduced; iY++ ) + { + for( int iX = 0; iX < nXReduced; iX++ ) + { + int iOffset = iX + iY * nXReduced; + double dfValue = 0.0; + + switch( eDataType ) + { + case GDT_Byte: + { + if (bSignedByte) + dfValue = ((signed char *)pData)[iOffset]; + else + dfValue = ((GByte *)pData)[iOffset]; + break; + } + case GDT_UInt16: + dfValue = ((GUInt16 *)pData)[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *)pData)[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *)pData)[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *)pData)[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_Float64: + dfValue = ((double *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_CInt16: + dfValue = ((GInt16 *)pData)[iOffset*2]; + break; + case GDT_CInt32: + dfValue = ((GInt32 *)pData)[iOffset*2]; + break; + case GDT_CFloat32: + dfValue = ((float *)pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + case GDT_CFloat64: + dfValue = ((double *)pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + default: + CPLAssert( FALSE ); + } + + if( bGotNoDataValue && ARE_REAL_EQUAL(dfValue, dfNoDataValue) ) + continue; + + if( bFirstValue ) + { + dfMin = dfMax = dfValue; + bFirstValue = FALSE; + } + else + { + dfMin = MIN(dfMin,dfValue); + dfMax = MAX(dfMax,dfValue); + } + + nSampleCount++; + double dfDelta = dfValue - dfMean; + dfMean += dfDelta / nSampleCount; + dfM2 += dfDelta * (dfValue - dfMean); + } + } + + CPLFree( pData ); + } + + else // No arbitrary overviews + { + int nSampleRate; + + if( !InitBlockInfo() ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Figure out the ratio of blocks we will read to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + if ( bApproxOK ) + { + nSampleRate = + (int)MAX( 1, sqrt((double)nBlocksPerRow * nBlocksPerColumn) ); + } + else + nSampleRate = 1; + + for( int iSampleBlock = 0; + iSampleBlock < nBlocksPerRow * nBlocksPerColumn; + iSampleBlock += nSampleRate ) + { + int iXBlock, iYBlock, nXCheck, nYCheck; + GDALRasterBlock *poBlock; + void* pData; + + iYBlock = iSampleBlock / nBlocksPerRow; + iXBlock = iSampleBlock - nBlocksPerRow * iYBlock; + + poBlock = GetLockedBlockRef( iXBlock, iYBlock ); + if( poBlock == NULL ) + continue; + if( poBlock->GetDataRef() == NULL ) + { + poBlock->DropLock(); + continue; + } + + pData = poBlock->GetDataRef(); + + if( (iXBlock+1) * nBlockXSize > GetXSize() ) + nXCheck = GetXSize() - iXBlock * nBlockXSize; + else + nXCheck = nBlockXSize; + + if( (iYBlock+1) * nBlockYSize > GetYSize() ) + nYCheck = GetYSize() - iYBlock * nBlockYSize; + else + nYCheck = nBlockYSize; + + /* this isn't the fastest way to do this, but is easier for now */ + for( int iY = 0; iY < nYCheck; iY++ ) + { + for( int iX = 0; iX < nXCheck; iX++ ) + { + int iOffset = iX + iY * nBlockXSize; + double dfValue = 0.0; + + switch( eDataType ) + { + case GDT_Byte: + { + if (bSignedByte) + dfValue = ((signed char *)pData)[iOffset]; + else + dfValue = ((GByte *)pData)[iOffset]; + break; + } + case GDT_UInt16: + dfValue = ((GUInt16 *)pData)[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *)pData)[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *)pData)[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *)pData)[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_Float64: + dfValue = ((double *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_CInt16: + dfValue = ((GInt16 *)pData)[iOffset*2]; + break; + case GDT_CInt32: + dfValue = ((GInt32 *)pData)[iOffset*2]; + break; + case GDT_CFloat32: + dfValue = ((float *)pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + case GDT_CFloat64: + dfValue = ((double *)pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + default: + CPLAssert( FALSE ); + } + + if( bGotNoDataValue && ARE_REAL_EQUAL(dfValue, dfNoDataValue) ) + continue; + + if( bFirstValue ) + { + dfMin = dfMax = dfValue; + bFirstValue = FALSE; + } + else + { + dfMin = MIN(dfMin,dfValue); + dfMax = MAX(dfMax,dfValue); + } + + nSampleCount++; + double dfDelta = dfValue - dfMean; + dfMean += dfDelta / nSampleCount; + dfM2 += dfDelta * (dfValue - dfMean); + } + } + + poBlock->DropLock(); + + if ( !pfnProgress(iSampleBlock + / ((double)(nBlocksPerRow*nBlocksPerColumn)), + "Compute Statistics", pProgressData) ) + { + ReportError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + } + } + + if( !pfnProgress( 1.0, "Compute Statistics", pProgressData ) ) + { + ReportError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Save computed information. */ +/* -------------------------------------------------------------------- */ + double dfStdDev = sqrt(dfM2 / nSampleCount); + + if( nSampleCount > 0 ) + SetStatistics( dfMin, dfMax, dfMean, dfStdDev ); + +/* -------------------------------------------------------------------- */ +/* Record results. */ +/* -------------------------------------------------------------------- */ + if( pdfMin != NULL ) + *pdfMin = dfMin; + if( pdfMax != NULL ) + *pdfMax = dfMax; + + if( pdfMean != NULL ) + *pdfMean = dfMean; + + if( pdfStdDev != NULL ) + *pdfStdDev = dfStdDev; + + if( nSampleCount > 0 ) + return CE_None; + else + { + ReportError( CE_Failure, CPLE_AppDefined, + "Failed to compute statistics, no valid pixels found in sampling." ); + return CE_Failure; + } +} + +/************************************************************************/ +/* GDALComputeRasterStatistics() */ +/************************************************************************/ + +/** + * \brief Compute image statistics. + * + * @see GDALRasterBand::ComputeStatistics() + */ + +CPLErr CPL_STDCALL GDALComputeRasterStatistics( + GDALRasterBandH hBand, int bApproxOK, + double *pdfMin, double *pdfMax, double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, void *pProgressData ) + +{ + VALIDATE_POINTER1( hBand, "GDALComputeRasterStatistics", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + + return poBand->ComputeStatistics( + bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev, + pfnProgress, pProgressData ); +} + +/************************************************************************/ +/* SetStatistics() */ +/************************************************************************/ + +/** + * \brief Set statistics on band. + * + * This method can be used to store min/max/mean/standard deviation + * statistics on a raster band. + * + * The default implementation stores them as metadata, and will only work + * on formats that can save arbitrary metadata. This method cannot detect + * whether metadata will be properly saved and so may return CE_None even + * if the statistics will never be saved. + * + * This method is the same as the C function GDALSetRasterStatistics(). + * + * @param dfMin minimum pixel value. + * + * @param dfMax maximum pixel value. + * + * @param dfMean mean (average) of all pixel values. + * + * @param dfStdDev Standard deviation of all pixel values. + * + * @return CE_None on success or CE_Failure on failure. + */ + +CPLErr GDALRasterBand::SetStatistics( double dfMin, double dfMax, + double dfMean, double dfStdDev ) + +{ + char szValue[128] = { 0 }; + + CPLsprintf( szValue, "%.14g", dfMin ); + SetMetadataItem( "STATISTICS_MINIMUM", szValue ); + + CPLsprintf( szValue, "%.14g", dfMax ); + SetMetadataItem( "STATISTICS_MAXIMUM", szValue ); + + CPLsprintf( szValue, "%.14g", dfMean ); + SetMetadataItem( "STATISTICS_MEAN", szValue ); + + CPLsprintf( szValue, "%.14g", dfStdDev ); + SetMetadataItem( "STATISTICS_STDDEV", szValue ); + + return CE_None; +} + +/************************************************************************/ +/* GDALSetRasterStatistics() */ +/************************************************************************/ + +/** + * \brief Set statistics on band. + * + * @see GDALRasterBand::SetStatistics() + */ + +CPLErr CPL_STDCALL GDALSetRasterStatistics( + GDALRasterBandH hBand, + double dfMin, double dfMax, double dfMean, double dfStdDev ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetRasterStatistics", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetStatistics( dfMin, dfMax, dfMean, dfStdDev ); +} + +/************************************************************************/ +/* ComputeRasterMinMax() */ +/************************************************************************/ + +/** + * \brief Compute the min/max values for a band. + * + * If approximate is OK, then the band's GetMinimum()/GetMaximum() will + * be trusted. If it doesn't work, a subsample of blocks will be read to + * get an approximate min/max. If the band has a nodata value it will + * be excluded from the minimum and maximum. + * + * If bApprox is FALSE, then all pixels will be read and used to compute + * an exact range. + * + * This method is the same as the C function GDALComputeRasterMinMax(). + * + * @param bApproxOK TRUE if an approximate (faster) answer is OK, otherwise + * FALSE. + * @param adfMinMax the array in which the minimum (adfMinMax[0]) and the + * maximum (adfMinMax[1]) are returned. + * + * @return CE_None on success or CE_Failure on failure. + */ + + +CPLErr GDALRasterBand::ComputeRasterMinMax( int bApproxOK, + double adfMinMax[2] ) +{ + double dfMin = 0.0; + double dfMax = 0.0; + +/* -------------------------------------------------------------------- */ +/* Does the driver already know the min/max? */ +/* -------------------------------------------------------------------- */ + if( bApproxOK ) + { + int bSuccessMin, bSuccessMax; + + dfMin = GetMinimum( &bSuccessMin ); + dfMax = GetMaximum( &bSuccessMax ); + + if( bSuccessMin && bSuccessMax ) + { + adfMinMax[0] = dfMin; + adfMinMax[1] = dfMax; + return CE_None; + } + } + +/* -------------------------------------------------------------------- */ +/* If we have overview bands, use them for min/max. */ +/* -------------------------------------------------------------------- */ + if ( bApproxOK && GetOverviewCount() > 0 && !HasArbitraryOverviews() ) + { + GDALRasterBand *poBand = + GetRasterSampleOverview( GDALSTAT_APPROX_NUMSAMPLES ); + + if ( poBand != this ) + return poBand->ComputeRasterMinMax( FALSE, adfMinMax ); + } + +/* -------------------------------------------------------------------- */ +/* Read actual data and compute minimum and maximum. */ +/* -------------------------------------------------------------------- */ + int bGotNoDataValue, bFirstValue = TRUE; + + const double dfNoDataValue = GetNoDataValue( &bGotNoDataValue ); + bGotNoDataValue = bGotNoDataValue && !CPLIsNan(dfNoDataValue); + + const char* pszPixelType = GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); + int bSignedByte = (pszPixelType != NULL && EQUAL(pszPixelType, "SIGNEDBYTE")); + + GDALRasterIOExtraArg sExtraArg; + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + + if ( bApproxOK && HasArbitraryOverviews() ) + { +/* -------------------------------------------------------------------- */ +/* Figure out how much the image should be reduced to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + void *pData; + int nXReduced, nYReduced; + double dfReduction = sqrt( + (double)nRasterXSize * nRasterYSize / GDALSTAT_APPROX_NUMSAMPLES ); + + if ( dfReduction > 1.0 ) + { + nXReduced = (int)( nRasterXSize / dfReduction ); + nYReduced = (int)( nRasterYSize / dfReduction ); + + // Catch the case of huge resizing ratios here + if ( nXReduced == 0 ) + nXReduced = 1; + if ( nYReduced == 0 ) + nYReduced = 1; + } + else + { + nXReduced = nRasterXSize; + nYReduced = nRasterYSize; + } + + pData = + CPLMalloc(GDALGetDataTypeSize(eDataType)/8 * nXReduced * nYReduced); + + CPLErr eErr = IRasterIO( GF_Read, 0, 0, nRasterXSize, nRasterYSize, pData, + nXReduced, nYReduced, eDataType, 0, 0, &sExtraArg ); + if ( eErr != CE_None ) + { + CPLFree(pData); + return eErr; + } + + /* this isn't the fastest way to do this, but is easier for now */ + for( int iY = 0; iY < nYReduced; iY++ ) + { + for( int iX = 0; iX < nXReduced; iX++ ) + { + int iOffset = iX + iY * nXReduced; + double dfValue = 0.0; + + switch( eDataType ) + { + case GDT_Byte: + { + if (bSignedByte) + dfValue = ((signed char *)pData)[iOffset]; + else + dfValue = ((GByte *)pData)[iOffset]; + break; + } + case GDT_UInt16: + dfValue = ((GUInt16 *)pData)[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *)pData)[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *)pData)[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *)pData)[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_Float64: + dfValue = ((double *)pData)[iOffset]; + if (CPLIsNan(dfValue)) + continue; + break; + case GDT_CInt16: + dfValue = ((GInt16 *)pData)[iOffset*2]; + break; + case GDT_CInt32: + dfValue = ((GInt32 *)pData)[iOffset*2]; + break; + case GDT_CFloat32: + dfValue = ((float *)pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + case GDT_CFloat64: + dfValue = ((double *)pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + default: + CPLAssert( FALSE ); + } + + if( bGotNoDataValue && ARE_REAL_EQUAL(dfValue, dfNoDataValue) ) + continue; + + if( bFirstValue ) + { + dfMin = dfMax = dfValue; + bFirstValue = FALSE; + } + else + { + dfMin = MIN(dfMin,dfValue); + dfMax = MAX(dfMax,dfValue); + } + } + } + + CPLFree( pData ); + } + + else // No arbitrary overviews + { + int nSampleRate; + + if( !InitBlockInfo() ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Figure out the ratio of blocks we will read to get an */ +/* approximate value. */ +/* -------------------------------------------------------------------- */ + if ( bApproxOK ) + { + nSampleRate = + (int) MAX(1,sqrt((double) nBlocksPerRow * nBlocksPerColumn)); + } + else + nSampleRate = 1; + + for( int iSampleBlock = 0; + iSampleBlock < nBlocksPerRow * nBlocksPerColumn; + iSampleBlock += nSampleRate ) + { + int iXBlock, iYBlock, nXCheck, nYCheck; + GDALRasterBlock *poBlock; + void* pData; + + iYBlock = iSampleBlock / nBlocksPerRow; + iXBlock = iSampleBlock - nBlocksPerRow * iYBlock; + + poBlock = GetLockedBlockRef( iXBlock, iYBlock ); + if( poBlock == NULL ) + continue; + if( poBlock->GetDataRef() == NULL ) + { + poBlock->DropLock(); + continue; + } + + pData = poBlock->GetDataRef(); + + if( (iXBlock+1) * nBlockXSize > GetXSize() ) + nXCheck = GetXSize() - iXBlock * nBlockXSize; + else + nXCheck = nBlockXSize; + + if( (iYBlock+1) * nBlockYSize > GetYSize() ) + nYCheck = GetYSize() - iYBlock * nBlockYSize; + else + nYCheck = nBlockYSize; + + /* this isn't the fastest way to do this, but is easier for now */ + for( int iY = 0; iY < nYCheck; iY++ ) + { + for( int iX = 0; iX < nXCheck; iX++ ) + { + int iOffset = iX + iY * nBlockXSize; + double dfValue = 0.0; + + switch( eDataType ) + { + case GDT_Byte: + { + if (bSignedByte) + dfValue = ((signed char *) pData)[iOffset]; + else + dfValue = ((GByte *) pData)[iOffset]; + break; + } + case GDT_UInt16: + dfValue = ((GUInt16 *) pData)[iOffset]; + break; + case GDT_Int16: + dfValue = ((GInt16 *) pData)[iOffset]; + break; + case GDT_UInt32: + dfValue = ((GUInt32 *) pData)[iOffset]; + break; + case GDT_Int32: + dfValue = ((GInt32 *) pData)[iOffset]; + break; + case GDT_Float32: + dfValue = ((float *) pData)[iOffset]; + if( CPLIsNan(dfValue) ) + continue; + break; + case GDT_Float64: + dfValue = ((double *) pData)[iOffset]; + if( CPLIsNan(dfValue) ) + continue; + break; + case GDT_CInt16: + dfValue = ((GInt16 *) pData)[iOffset*2]; + break; + case GDT_CInt32: + dfValue = ((GInt32 *) pData)[iOffset*2]; + break; + case GDT_CFloat32: + dfValue = ((float *) pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + case GDT_CFloat64: + dfValue = ((double *) pData)[iOffset*2]; + if( CPLIsNan(dfValue) ) + continue; + break; + default: + CPLAssert( FALSE ); + } + + if( bGotNoDataValue && ARE_REAL_EQUAL(dfValue, dfNoDataValue) ) + continue; + + if( bFirstValue ) + { + dfMin = dfMax = dfValue; + bFirstValue = FALSE; + } + else + { + dfMin = MIN(dfMin,dfValue); + dfMax = MAX(dfMax,dfValue); + } + } + } + + poBlock->DropLock(); + } + } + + adfMinMax[0] = dfMin; + adfMinMax[1] = dfMax; + + if (bFirstValue) + { + ReportError( CE_Failure, CPLE_AppDefined, + "Failed to compute min/max, no valid pixels found in sampling." ); + return CE_Failure; + } + else + { + return CE_None; + } +} + +/************************************************************************/ +/* GDALComputeRasterMinMax() */ +/************************************************************************/ + +/** + * \brief Compute the min/max values for a band. + * + * @see GDALRasterBand::ComputeRasterMinMax() + */ + +void CPL_STDCALL +GDALComputeRasterMinMax( GDALRasterBandH hBand, int bApproxOK, + double adfMinMax[2] ) + +{ + VALIDATE_POINTER0( hBand, "GDALComputeRasterMinMax" ); + + GDALRasterBand *poBand = static_cast(hBand); + poBand->ComputeRasterMinMax( bApproxOK, adfMinMax ); +} + +/************************************************************************/ +/* SetDefaultHistogram() */ +/************************************************************************/ + +/* FIXME : add proper documentation */ +/** + * \brief Set default histogram. + * + * This method is the same as the C function GDALSetDefaultHistogram() and + * GDALSetDefaultHistogramEx() + */ +CPLErr GDALRasterBand::SetDefaultHistogram( CPL_UNUSED double dfMin, + CPL_UNUSED double dfMax, + CPL_UNUSED int nBuckets, + CPL_UNUSED GUIntBig *panHistogram ) + +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetDefaultHistogram() not implemented for this format." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetDefaultHistogram() */ +/************************************************************************/ + +/** + * \brief Set default histogram. + * + * Use GDALSetRasterHistogramEx() instead to be able to set counts exceeding + * 2 billion. + * + * @see GDALRasterBand::SetDefaultHistogram() + * @see GDALSetRasterHistogramEx() + */ + +CPLErr CPL_STDCALL GDALSetDefaultHistogram( GDALRasterBandH hBand, + double dfMin, double dfMax, + int nBuckets, int *panHistogram ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetDefaultHistogram", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + + GUIntBig* panHistogramTemp = (GUIntBig*) VSIMalloc2(sizeof(GUIntBig), nBuckets); + if( panHistogramTemp == NULL ) + { + poBand->ReportError( CE_Failure, CPLE_OutOfMemory, + "Out of memory in GDALSetDefaultHistogram()." ); + return CE_Failure; + } + + for(int i=0;iSetDefaultHistogram( dfMin, dfMax, nBuckets, panHistogramTemp ); + + CPLFree(panHistogramTemp); + + return eErr; +} + +/************************************************************************/ +/* GDALSetDefaultHistogramEx() */ +/************************************************************************/ + +/** + * \brief Set default histogram. + * + * @see GDALRasterBand::SetDefaultHistogram() + * + * @since GDAL 2.0 + */ + +CPLErr CPL_STDCALL GDALSetDefaultHistogramEx( GDALRasterBandH hBand, + double dfMin, double dfMax, + int nBuckets, GUIntBig *panHistogram ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetDefaultHistogramEx", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->SetDefaultHistogram( dfMin, dfMax, nBuckets, panHistogram ); +} + +/************************************************************************/ +/* GetDefaultRAT() */ +/************************************************************************/ + +/** + * \brief Fetch default Raster Attribute Table. + * + * A RAT will be returned if there is a default one associated with the + * band, otherwise NULL is returned. The returned RAT is owned by the + * band and should not be deleted by the application. + * + * This method is the same as the C function GDALGetDefaultRAT(). + * + * @return NULL, or a pointer to an internal RAT owned by the band. + */ + +GDALRasterAttributeTable *GDALRasterBand::GetDefaultRAT() + +{ + return NULL; +} + +/************************************************************************/ +/* GDALGetDefaultRAT() */ +/************************************************************************/ + +/** + * \brief Fetch default Raster Attribute Table. + * + * @see GDALRasterBand::GetDefaultRAT() + */ + +GDALRasterAttributeTableH CPL_STDCALL GDALGetDefaultRAT( GDALRasterBandH hBand) + +{ + VALIDATE_POINTER1( hBand, "GDALGetDefaultRAT", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return (GDALRasterAttributeTableH) poBand->GetDefaultRAT(); +} + +/************************************************************************/ +/* SetDefaultRAT() */ +/************************************************************************/ + +/** + * \brief Set default Raster Attribute Table. + * + * Associates a default RAT with the band. If not implemented for the + * format a CPLE_NotSupported error will be issued. If successful a copy + * of the RAT is made, the original remains owned by the caller. + * + * This method is the same as the C function GDALSetDefaultRAT(). + * + * @param poRAT the RAT to assign to the band. + * + * @return CE_None on success or CE_Failure if unsupported or otherwise + * failing. + */ + +CPLErr GDALRasterBand::SetDefaultRAT( CPL_UNUSED const GDALRasterAttributeTable *poRAT ) +{ + if( !(GetMOFlags() & GMO_IGNORE_UNIMPLEMENTED) ) + ReportError( CE_Failure, CPLE_NotSupported, + "SetDefaultRAT() not implemented for this format." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALSetDefaultRAT() */ +/************************************************************************/ + +/** + * \brief Set default Raster Attribute Table. + * + * @see GDALRasterBand::GDALSetDefaultRAT() + */ + +CPLErr CPL_STDCALL GDALSetDefaultRAT( GDALRasterBandH hBand, + GDALRasterAttributeTableH hRAT ) + +{ + VALIDATE_POINTER1( hBand, "GDALSetDefaultRAT", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + + return poBand->SetDefaultRAT( + static_cast(hRAT) ); +} + +/************************************************************************/ +/* GetMaskBand() */ +/************************************************************************/ + +/** + * \brief Return the mask band associated with the band. + * + * The GDALRasterBand class includes a default implementation of GetMaskBand() that + * returns one of four default implementations : + *
      + *
    • If a corresponding .msk file exists it will be used for the mask band.
    • + *
    • If the dataset has a NODATA_VALUES metadata item, an instance of the + * new GDALNoDataValuesMaskBand class will be returned. + * GetMaskFlags() will return GMF_NODATA | GMF_PER_DATASET. @since GDAL 1.6.0
    • + *
    • If the band has a nodata value set, an instance of the new + * GDALNodataMaskRasterBand class will be returned. + * GetMaskFlags() will return GMF_NODATA.
    • + *
    • If there is no nodata value, but the dataset has an alpha band that seems + * to apply to this band (specific rules yet to be determined) and that is + * of type GDT_Byte then that alpha band will be returned, and the flags + * GMF_PER_DATASET and GMF_ALPHA will be returned in the flags.
    • + *
    • If neither of the above apply, an instance of the new GDALAllValidRasterBand + * class will be returned that has 255 values for all pixels. + * The null flags will return GMF_ALL_VALID.
    • + *
    + * + * Note that the GetMaskBand() should always return a GDALRasterBand mask, even if it is only + * an all 255 mask with the flags indicating GMF_ALL_VALID. + * + * This method is the same as the C function GDALGetMaskBand(). + * + * @return a valid mask band. + * + * @since GDAL 1.5.0 + * + * @see http://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask + * + */ +GDALRasterBand *GDALRasterBand::GetMaskBand() + +{ + if( poMask != NULL ) + return poMask; + +/* -------------------------------------------------------------------- */ +/* Check for a mask in a .msk file. */ +/* -------------------------------------------------------------------- */ + GDALDataset *poDS = GetDataset(); + + if( poDS != NULL && poDS->oOvManager.HaveMaskFile() ) + { + poMask = poDS->oOvManager.GetMaskBand( nBand ); + if( poMask != NULL ) + { + nMaskFlags = poDS->oOvManager.GetMaskFlags( nBand ); + return poMask; + } + } + +/* -------------------------------------------------------------------- */ +/* Check for NODATA_VALUES metadata. */ +/* -------------------------------------------------------------------- */ + if (poDS != NULL) + { + const char* pszNoDataValues = poDS->GetMetadataItem("NODATA_VALUES"); + if (pszNoDataValues != NULL) + { + char** papszNoDataValues = CSLTokenizeStringComplex(pszNoDataValues, " ", FALSE, FALSE); + + /* Make sure we have as many values as bands */ + if (CSLCount(papszNoDataValues) == poDS->GetRasterCount() && poDS->GetRasterCount() != 0) + { + /* Make sure that all bands have the same data type */ + /* This is cleraly not a fundamental condition, just a condition to make implementation */ + /* easier. */ + int i; + GDALDataType eDT = GDT_Unknown; + for(i=0;iGetRasterCount();i++) + { + if (i == 0) + eDT = poDS->GetRasterBand(1)->GetRasterDataType(); + else if (eDT != poDS->GetRasterBand(i + 1)->GetRasterDataType()) + { + break; + } + } + if (i == poDS->GetRasterCount()) + { + nMaskFlags = GMF_NODATA | GMF_PER_DATASET; + poMask = new GDALNoDataValuesMaskBand ( poDS ); + bOwnMask = true; + CSLDestroy(papszNoDataValues); + return poMask; + } + else + { + ReportError(CE_Warning, CPLE_AppDefined, + "All bands should have the same type in order the NODATA_VALUES metadata item to be used as a mask."); + } + } + else + { + ReportError(CE_Warning, CPLE_AppDefined, + "NODATA_VALUES metadata item doesn't have the same number of values as the number of bands.\n" + "Ignoring it for mask."); + } + + CSLDestroy(papszNoDataValues); + } + } + +/* -------------------------------------------------------------------- */ +/* Check for nodata case. */ +/* -------------------------------------------------------------------- */ + int bHaveNoData; + + GetNoDataValue( &bHaveNoData ); + + if( bHaveNoData ) + { + nMaskFlags = GMF_NODATA; + poMask = new GDALNoDataMaskBand( this ); + bOwnMask = true; + return poMask; + } + +/* -------------------------------------------------------------------- */ +/* Check for alpha case. */ +/* -------------------------------------------------------------------- */ + if( poDS != NULL + && poDS->GetRasterCount() == 2 + && this == poDS->GetRasterBand(1) + && poDS->GetRasterBand(2)->GetColorInterpretation() == GCI_AlphaBand + && poDS->GetRasterBand(2)->GetRasterDataType() == GDT_Byte ) + { + nMaskFlags = GMF_ALPHA | GMF_PER_DATASET; + poMask = poDS->GetRasterBand(2); + return poMask; + } + + if( poDS != NULL + && poDS->GetRasterCount() == 4 + && (this == poDS->GetRasterBand(1) + || this == poDS->GetRasterBand(2) + || this == poDS->GetRasterBand(3)) + && poDS->GetRasterBand(4)->GetColorInterpretation() == GCI_AlphaBand ) + { + if( poDS->GetRasterBand(4)->GetRasterDataType() == GDT_Byte ) + { + nMaskFlags = GMF_ALPHA | GMF_PER_DATASET; + poMask = poDS->GetRasterBand(4); + return poMask; + } + else if( poDS->GetRasterBand(4)->GetRasterDataType() == GDT_UInt16 ) + { + nMaskFlags = GMF_ALPHA | GMF_PER_DATASET; + poMask = new GDALRescaledAlphaBand( poDS->GetRasterBand(4) ); + bOwnMask = true; + return poMask; + } + } + +/* -------------------------------------------------------------------- */ +/* Fallback to all valid case. */ +/* -------------------------------------------------------------------- */ + nMaskFlags = GMF_ALL_VALID; + poMask = new GDALAllValidMaskBand( this ); + bOwnMask = true; + + return poMask; +} + +/************************************************************************/ +/* GDALGetMaskBand() */ +/************************************************************************/ + +/** + * \brief Return the mask band associated with the band. + * + * @see GDALRasterBand::GetMaskBand() + */ + +GDALRasterBandH CPL_STDCALL GDALGetMaskBand( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetMaskBand", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetMaskBand(); +} + +/************************************************************************/ +/* GetMaskFlags() */ +/************************************************************************/ + +/** + * \brief Return the status flags of the mask band associated with the band. + * + * The GetMaskFlags() method returns an bitwise OR-ed set of status flags with + * the following available definitions that may be extended in the future: + *
      + *
    • GMF_ALL_VALID(0x01): There are no invalid pixels, all mask values will be 255. + * When used this will normally be the only flag set.
    • + *
    • GMF_PER_DATASET(0x02): The mask band is shared between all bands on the dataset.
    • + *
    • GMF_ALPHA(0x04): The mask band is actually an alpha band and may have values + * other than 0 and 255.
    • + *
    • GMF_NODATA(0x08): Indicates the mask is actually being generated from nodata values. + * (mutually exclusive of GMF_ALPHA)
    • + *
    + * + * The GDALRasterBand class includes a default implementation of GetMaskBand() that + * returns one of four default implementations : + *
      + *
    • If a corresponding .msk file exists it will be used for the mask band.
    • + *
    • If the dataset has a NODATA_VALUES metadata item, an instance of the + * new GDALNoDataValuesMaskBand class will be returned. + * GetMaskFlags() will return GMF_NODATA | GMF_PER_DATASET. @since GDAL 1.6.0
    • + *
    • If the band has a nodata value set, an instance of the new + * GDALNodataMaskRasterBand class will be returned. + * GetMaskFlags() will return GMF_NODATA.
    • + *
    • If there is no nodata value, but the dataset has an alpha band that seems + * to apply to this band (specific rules yet to be determined) and that is + * of type GDT_Byte then that alpha band will be returned, and the flags + * GMF_PER_DATASET and GMF_ALPHA will be returned in the flags.
    • + *
    • If neither of the above apply, an instance of the new GDALAllValidRasterBand + * class will be returned that has 255 values for all pixels. + * The null flags will return GMF_ALL_VALID.
    • + *
    + * + * This method is the same as the C function GDALGetMaskFlags(). + * + * @since GDAL 1.5.0 + * + * @return a valid mask band. + * + * @see http://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask + * + */ +int GDALRasterBand::GetMaskFlags() + +{ + // If we don't have a band yet, force this now so that the masks value + // will be initialized. + + if( poMask == NULL ) + GetMaskBand(); + + return nMaskFlags; +} + +/************************************************************************/ +/* GDALGetMaskFlags() */ +/************************************************************************/ + +/** + * \brief Return the status flags of the mask band associated with the band. + * + * @see GDALRasterBand::GetMaskFlags() + */ + +int CPL_STDCALL GDALGetMaskFlags( GDALRasterBandH hBand ) + +{ + VALIDATE_POINTER1( hBand, "GDALGetMaskFlags", GMF_ALL_VALID ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->GetMaskFlags(); +} + +/************************************************************************/ +/* InvalidateMaskBand() */ +/************************************************************************/ + +void GDALRasterBand::InvalidateMaskBand() +{ + if (bOwnMask) + delete poMask; + bOwnMask = false; + nMaskFlags = 0; + poMask = NULL; +} + +/************************************************************************/ +/* CreateMaskBand() */ +/************************************************************************/ + +/** + * \brief Adds a mask band to the current band + * + * The default implementation of the CreateMaskBand() method is implemented + * based on similar rules to the .ovr handling implemented using the + * GDALDefaultOverviews object. A TIFF file with the extension .msk will + * be created with the same basename as the original file, and it will have + * as many bands as the original image (or just one for GMF_PER_DATASET). + * The mask images will be deflate compressed tiled images with the same + * block size as the original image if possible. + * + * Note that if you got a mask band with a previous call to GetMaskBand(), + * it might be invalidated by CreateMaskBand(). So you have to call GetMaskBand() + * again. + * + * This method is the same as the C function GDALCreateMaskBand(). + * + * @since GDAL 1.5.0 + * + * @return CE_None on success or CE_Failure on an error. + * + * @see http://trac.osgeo.org/gdal/wiki/rfc15_nodatabitmask + * + */ + +CPLErr GDALRasterBand::CreateMaskBand( int nFlags ) + +{ + if( poDS != NULL && poDS->oOvManager.IsInitialized() ) + { + CPLErr eErr = poDS->oOvManager.CreateMaskBand( nFlags, nBand ); + if (eErr != CE_None) + return eErr; + + InvalidateMaskBand(); + + return CE_None; + } + + ReportError( CE_Failure, CPLE_NotSupported, + "CreateMaskBand() not supported for this band." ); + + return CE_Failure; +} + +/************************************************************************/ +/* GDALCreateMaskBand() */ +/************************************************************************/ + +/** + * \brief Adds a mask band to the current band + * + * @see GDALRasterBand::CreateMaskBand() + */ + +CPLErr CPL_STDCALL GDALCreateMaskBand( GDALRasterBandH hBand, int nFlags ) + +{ + VALIDATE_POINTER1( hBand, "GDALCreateMaskBand", CE_Failure ); + + GDALRasterBand *poBand = static_cast(hBand); + return poBand->CreateMaskBand( nFlags ); +} + +/************************************************************************/ +/* GetIndexColorTranslationTo() */ +/************************************************************************/ + +/** + * \brief Compute translation table for color tables. + * + * When the raster band has a palette index, it may be useful to compute + * the "translation" of this palette to the palette of another band. + * The translation tries to do exact matching first, and then approximate + * matching if no exact matching is possible. + * This method returns a table such that table[i] = j where i is an index + * of the 'this' rasterband and j the corresponding index for the reference + * rasterband. + * + * This method is thought as internal to GDAL and is used for drivers + * like RPFTOC. + * + * The implementation only supports 1-byte palette rasterbands. + * + * @param poReferenceBand the raster band + * @param pTranslationTable an already allocated translation table (at least 256 bytes), + * or NULL to let the method allocate it + * @param pApproximateMatching a pointer to a flag that is set if the matching + * is approximate. May be NULL. + * + * @return a translation table if the two bands are palette index and that they do + * not match or NULL in other cases. + * The table must be freed with CPLFree if NULL was passed for pTranslationTable. + */ + +unsigned char* GDALRasterBand::GetIndexColorTranslationTo(GDALRasterBand* poReferenceBand, + unsigned char* pTranslationTable, + int* pApproximateMatching ) +{ + if (poReferenceBand == NULL) + return NULL; + + if (poReferenceBand->GetColorInterpretation() == GCI_PaletteIndex && + GetColorInterpretation() == GCI_PaletteIndex && + poReferenceBand->GetRasterDataType() == GDT_Byte && + GetRasterDataType() == GDT_Byte) + { + GDALColorTable* srcColorTable = GetColorTable(); + GDALColorTable* destColorTable = poReferenceBand->GetColorTable(); + if (srcColorTable != NULL && destColorTable != NULL) + { + int nEntries = srcColorTable->GetColorEntryCount(); + int nRefEntries = destColorTable->GetColorEntryCount(); + int bHasNoDataValueSrc; + int noDataValueSrc = (int)GetNoDataValue(&bHasNoDataValueSrc); + int bHasNoDataValueRef; + int noDataValueRef = (int)poReferenceBand->GetNoDataValue(&bHasNoDataValueRef); + int samePalette; + int i, j; + + if (pApproximateMatching) + *pApproximateMatching = FALSE; + + if (nEntries == nRefEntries && bHasNoDataValueSrc == bHasNoDataValueRef && + (bHasNoDataValueSrc == FALSE || noDataValueSrc == noDataValueRef)) + { + samePalette = TRUE; + for(i=0;iGetColorEntry(i); + const GDALColorEntry* entryRef = destColorTable->GetColorEntry(i); + if (entry->c1 != entryRef->c1 || + entry->c2 != entryRef->c2 || + entry->c3 != entryRef->c3) + { + samePalette = FALSE; + } + } + } + else + { + samePalette = FALSE; + } + if (samePalette == FALSE) + { + if (pTranslationTable == NULL) + pTranslationTable = (unsigned char*)CPLMalloc(256); + + /* Trying to remap the product palette on the subdataset palette */ + for(i=0;iGetColorEntry(i); + for(j=0;jGetColorEntry(j); + if (entry->c1 == entryRef->c1 && + entry->c2 == entryRef->c2 && + entry->c3 == entryRef->c3) + { + pTranslationTable[i] = (unsigned char) j; + break; + } + } + if (j == nEntries) + { + /* No exact match. Looking for closest color now... */ + int best_j = 0; + int best_distance = 0; + if (pApproximateMatching) + *pApproximateMatching = TRUE; + for(j=0;jGetColorEntry(j); + int distance = (entry->c1 - entryRef->c1) * (entry->c1 - entryRef->c1) + + (entry->c2 - entryRef->c2) * (entry->c2 - entryRef->c2) + + (entry->c3 - entryRef->c3) * (entry->c3 - entryRef->c3); + if (j == 0 || distance < best_distance) + { + best_j = j; + best_distance = distance; + } + } + pTranslationTable[i] = (unsigned char) best_j; + } + } + if (bHasNoDataValueRef && bHasNoDataValueSrc) + pTranslationTable[noDataValueSrc] = (unsigned char) noDataValueRef; + + return pTranslationTable; + } + } + } + return NULL; +} + +/************************************************************************/ +/* SetFlushBlockErr() */ +/************************************************************************/ + +/** + * \brief Store that an error occured while writing a dirty block. + * + * This function stores the fact that an error occured while writing a dirty + * block from GDALRasterBlock::FlushCacheBlock(). Indeed when dirty blocks are + * flushed when the block cache get full, it is not convenient/possible to + * report that a dirty block could not be written correctly. This function + * remembers the error and re-issue it from GDALRasterBand::FlushCache(), + * GDALRasterBand::WriteBlock() and GDALRasterBand::RasterIO(), which are + * places where the user can easily match the error with the relevant dataset. + */ + +void GDALRasterBand::SetFlushBlockErr( CPLErr eErr ) +{ + eFlushBlockErr = eErr; +} + +/************************************************************************/ +/* ReportError() */ +/************************************************************************/ + +/** + * \brief Emits an error related to a raster band. + * + * This function is a wrapper for regular CPLError(). The only difference + * with CPLError() is that it prepends the error message with the dataset + * name and the band number. + * + * @param eErrClass one of CE_Warning, CE_Failure or CE_Fatal. + * @param err_no the error number (CPLE_*) from cpl_error.h. + * @param fmt a printf() style format string. Any additional arguments + * will be treated as arguments to fill in this format in a manner + * similar to printf(). + * + * @since GDAL 1.9.0 + */ + +void GDALRasterBand::ReportError(CPLErr eErrClass, int err_no, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + char szNewFmt[256]; + const char* pszDSName = poDS ? poDS->GetDescription() : ""; + if (strlen(fmt) + strlen(pszDSName) + 20 >= sizeof(szNewFmt) - 1) + pszDSName = CPLGetFilename(pszDSName); + if (pszDSName[0] != '\0' && + strlen(fmt) + strlen(pszDSName) + 20 < sizeof(szNewFmt) - 1) + { + snprintf(szNewFmt, sizeof(szNewFmt), "%s, band %d: %s", + pszDSName, GetBand(), fmt); + CPLErrorV( eErrClass, err_no, szNewFmt, args ); + } + else + { + CPLErrorV( eErrClass, err_no, fmt, args ); + } + va_end(args); +} + + +/************************************************************************/ +/* GetVirtualMemAuto() */ +/************************************************************************/ + +/** \brief Create a CPLVirtualMem object from a GDAL raster band object. + * + * Only supported on Linux for now. + * + * This method allows creating a virtual memory object for a GDALRasterBand, + * that exposes the whole image data as a virtual array. + * + * The default implementation relies on GDALRasterBandGetVirtualMem(), but specialized + * implementation, such as for raw files, may also directly use mechanisms of the + * operating system to create a view of the underlying file into virtual memory + * ( CPLVirtualMemFileMapNew() ) + * + * At the time of writing, the GeoTIFF driver and "raw" drivers (EHdr, ...) offer + * a specialized implementation with direct file mapping, provided that some + * requirements are met : + * - for all drivers, the dataset must be backed by a "real" file in the file + * system, and the byte ordering of multi-byte datatypes (Int16, etc.) + * must match the native ordering of the CPU. + * - in addition, for the GeoTIFF driver, the GeoTIFF file must be uncompressed, scanline + * oriented (i.e. not tiled). Strips must be organized in the file in sequential + * order, and be equally spaced (which is generally the case). Only power-of-two + * bit depths are supported (8 for GDT_Bye, 16 for GDT_Int16/GDT_UInt16, + * 32 for GDT_Float32 and 64 for GDT_Float64) + * + * The pointer returned remains valid until CPLVirtualMemFree() is called. + * CPLVirtualMemFree() must be called before the raster band object is destroyed. + * + * If p is such a pointer and base_type the type matching GDALGetRasterDataType(), + * the element of image coordinates (x, y) can be accessed with + * *(base_type*) ((GByte*)p + x * *pnPixelSpace + y * *pnLineSpace) + * + * This method is the same as the C GDALGetVirtualMemAuto() function. + * + * @param eRWFlag Either GF_Read to read the band, or GF_Write to + * read/write the band. + * + * @param pnPixelSpace Output parameter giving the byte offset from the start of one pixel value in + * the buffer to the start of the next pixel value within a scanline. + * + * @param pnLineSpace Output parameter giving the byte offset from the start of one scanline in + * the buffer to the start of the next. + * + * @param papszOptions NULL terminated list of options. + * If a specialized implementation exists, defining USE_DEFAULT_IMPLEMENTATION=YES + * will cause the default implementation to be used. + * When requiring or falling back to the default implementation, the following + * options are available : CACHE_SIZE (in bytes, defaults to 40 MB), + * PAGE_SIZE_HINT (in bytes), + * SINGLE_THREAD ("FALSE" / "TRUE", defaults to FALSE) + * + * @return a virtual memory object that must be unreferenced by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ + +CPLVirtualMem *GDALRasterBand::GetVirtualMemAuto( GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ) +{ + int nPixelSpace = GDALGetDataTypeSize(eDataType) / 8; + GIntBig nLineSpace = (GIntBig)nRasterXSize * nPixelSpace; + if( pnPixelSpace ) + *pnPixelSpace = nPixelSpace; + if( pnLineSpace ) + *pnLineSpace = nLineSpace; + size_t nCacheSize = atoi(CSLFetchNameValueDef(papszOptions, + "CACHE_SIZE", "40000000")); + size_t nPageSizeHint = atoi(CSLFetchNameValueDef(papszOptions, + "PAGE_SIZE_HINT", "0")); + int bSingleThreadUsage = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, + "SINGLE_THREAD", "FALSE")); + return GDALRasterBandGetVirtualMem( (GDALRasterBandH) this, + eRWFlag, + 0, 0, nRasterXSize, nRasterYSize, + nRasterXSize, nRasterYSize, + eDataType, + nPixelSpace, nLineSpace, + nCacheSize, + nPageSizeHint, + bSingleThreadUsage, + papszOptions ); +} + +/************************************************************************/ +/* GDALGetVirtualMemAuto() */ +/************************************************************************/ + +/** + * \brief Create a CPLVirtualMem object from a GDAL raster band object. + * + * @see GDALRasterBand::GetVirtualMemAuto() + */ + +CPLVirtualMem * GDALGetVirtualMemAuto( GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int *pnPixelSpace, + GIntBig *pnLineSpace, + char **papszOptions ) +{ + VALIDATE_POINTER1( hBand, "GDALGetVirtualMemAuto", NULL ); + + GDALRasterBand *poBand = static_cast(hBand); + + return poBand->GetVirtualMemAuto(eRWFlag, pnPixelSpace, + pnLineSpace, papszOptions); +} + + +/************************************************************************/ +/* EnterReadWrite() */ +/************************************************************************/ + +int GDALRasterBand::EnterReadWrite(GDALRWFlag eRWFlag) +{ + if( poDS != NULL ) + return poDS->EnterReadWrite(eRWFlag); + return FALSE; +} + +/************************************************************************/ +/* LeaveReadWrite() */ +/************************************************************************/ + +void GDALRasterBand::LeaveReadWrite() +{ + if( poDS != NULL ) + poDS->LeaveReadWrite(); +} diff --git a/bazaar/plugin/gdal/gcore/gdalrasterblock.cpp b/bazaar/plugin/gdal/gcore/gdalrasterblock.cpp new file mode 100644 index 000000000..bc89766c0 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalrasterblock.cpp @@ -0,0 +1,883 @@ +/****************************************************************************** + * $Id: gdalrasterblock.cpp 29334 2015-06-14 17:30:54Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALRasterBlock class and related global + * raster block cache management. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: gdalrasterblock.cpp 29334 2015-06-14 17:30:54Z rouault $"); + +static int bCacheMaxInitialized = FALSE; +static GIntBig nCacheMax = 40 * 1024*1024; +static volatile GIntBig nCacheUsed = 0; + +static GDALRasterBlock *poOldest = NULL; /* tail */ +static GDALRasterBlock *poNewest = NULL; /* head */ + +#if 0 +static CPLMutex *hRBLock = NULL; +#define INITIALIZE_LOCK CPLMutexHolderD( &hRBLock ) +#define TAKE_LOCK CPLMutexHolderOptionalLockD( hRBLock ) +#define DESTROY_LOCK CPLDestroyMutex( hRBLock ) +#else + +static CPLLock* hRBLock = NULL; +static int bDebugContention = FALSE; +static CPLLockType GetLockType() +{ + static int nLockType = -1; + if( nLockType < 0 ) + { + const char* pszLockType = CPLGetConfigOption("GDAL_RB_LOCK_TYPE", "ADAPTIVE"); + if( EQUAL(pszLockType, "ADAPTIVE") ) + nLockType = LOCK_ADAPTIVE_MUTEX; + else if( EQUAL(pszLockType, "RECURSIVE") ) + nLockType = LOCK_RECURSIVE_MUTEX; + else if( EQUAL(pszLockType, "SPIN") ) + nLockType = LOCK_SPIN; + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "GDAL_RB_LOCK_TYPE=%s not supported. Falling back to ADAPTIVE", + pszLockType); + nLockType = LOCK_ADAPTIVE_MUTEX; + } + bDebugContention = CSLTestBoolean(CPLGetConfigOption("GDAL_RB_LOCK_DEBUG_CONTENTION", "NO")); + } + return (CPLLockType) nLockType; +} + +#define INITIALIZE_LOCK CPLLockHolderD( &hRBLock, GetLockType() ); \ + CPLLockSetDebugPerf(hRBLock, bDebugContention) +#define TAKE_LOCK CPLLockHolderOptionalLockD( hRBLock ) +#define DESTROY_LOCK CPLDestroyLock( hRBLock ) + +#endif + +//#define ENABLE_DEBUG + +/************************************************************************/ +/* GDALSetCacheMax() */ +/************************************************************************/ + +/** + * \brief Set maximum cache memory. + * + * This function sets the maximum amount of memory that GDAL is permitted + * to use for GDALRasterBlock caching. The unit of the value is bytes. + * + * The maximum value is 2GB, due to the use of a signed 32 bit integer. + * Use GDALSetCacheMax64() to be able to set a higher value. + * + * @param nNewSizeInBytes the maximum number of bytes for caching. + */ + +void CPL_STDCALL GDALSetCacheMax( int nNewSizeInBytes ) + +{ + GDALSetCacheMax64(nNewSizeInBytes); +} + + +/************************************************************************/ +/* GDALSetCacheMax64() */ +/************************************************************************/ + +/** + * \brief Set maximum cache memory. + * + * This function sets the maximum amount of memory that GDAL is permitted + * to use for GDALRasterBlock caching. The unit of the value is bytes. + * + * Note: On 32 bit platforms, the maximum amount of memory that can be addressed + * by a process might be 2 GB or 3 GB, depending on the operating system + * capabilities. This function will not make any attempt to check the + * consistency of the passed value with the effective capabilities of the OS. + * + * @param nNewSizeInBytes the maximum number of bytes for caching. + * + * @since GDAL 1.8.0 + */ + +void CPL_STDCALL GDALSetCacheMax64( GIntBig nNewSizeInBytes ) + +{ + bCacheMaxInitialized = TRUE; + nCacheMax = nNewSizeInBytes; + +/* -------------------------------------------------------------------- */ +/* Flush blocks till we are under the new limit or till we */ +/* can't seem to flush anymore. */ +/* -------------------------------------------------------------------- */ + while( nCacheUsed > nCacheMax ) + { + GIntBig nOldCacheUsed = nCacheUsed; + + GDALFlushCacheBlock(); + + if( nCacheUsed == nOldCacheUsed ) + break; + } +} + +/************************************************************************/ +/* GDALGetCacheMax() */ +/************************************************************************/ + +/** + * \brief Get maximum cache memory. + * + * Gets the maximum amount of memory available to the GDALRasterBlock + * caching system for caching GDAL read/write imagery. + * + * The first type this function is called, it will read the GDAL_CACHEMAX + * configuation option to initialize the maximum cache memory. + * + * This function cannot return a value higher than 2 GB. Use + * GDALGetCacheMax64() to get a non-truncated value. + * + * @return maximum in bytes. + */ + +int CPL_STDCALL GDALGetCacheMax() +{ + GIntBig nRes = GDALGetCacheMax64(); + if (nRes > INT_MAX) + { + static int bHasWarned = FALSE; + if (!bHasWarned) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cache max value doesn't fit on a 32 bit integer. " + "Call GDALGetCacheMax64() instead"); + bHasWarned = TRUE; + } + nRes = INT_MAX; + } + return (int)nRes; +} + +/************************************************************************/ +/* GDALGetCacheMax64() */ +/************************************************************************/ + +/** + * \brief Get maximum cache memory. + * + * Gets the maximum amount of memory available to the GDALRasterBlock + * caching system for caching GDAL read/write imagery. + * + * The first type this function is called, it will read the GDAL_CACHEMAX + * configuation option to initialize the maximum cache memory. + * + * @return maximum in bytes. + * + * @since GDAL 1.8.0 + */ + +GIntBig CPL_STDCALL GDALGetCacheMax64() +{ + if( !bCacheMaxInitialized ) + { + { + INITIALIZE_LOCK; + } + const char* pszCacheMax = CPLGetConfigOption("GDAL_CACHEMAX",NULL); + bCacheMaxInitialized = TRUE; + if( pszCacheMax != NULL ) + { + GIntBig nNewCacheMax = (GIntBig)CPLScanUIntBig(pszCacheMax, strlen(pszCacheMax)); + if( nNewCacheMax < 100000 ) + { + if (nNewCacheMax < 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Invalid value for GDAL_CACHEMAX. Using default value."); + return nCacheMax; + } + nNewCacheMax *= 1024 * 1024; + } + nCacheMax = nNewCacheMax; + } + } + + return nCacheMax; +} + +/************************************************************************/ +/* GDALGetCacheUsed() */ +/************************************************************************/ + +/** + * \brief Get cache memory used. + * + * @return the number of bytes of memory currently in use by the + * GDALRasterBlock memory caching. + */ + +int CPL_STDCALL GDALGetCacheUsed() +{ + if (nCacheUsed > INT_MAX) + { + static int bHasWarned = FALSE; + if (!bHasWarned) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cache used value doesn't fit on a 32 bit integer. " + "Call GDALGetCacheUsed64() instead"); + bHasWarned = TRUE; + } + return INT_MAX; + } + return (int)nCacheUsed; +} + +/************************************************************************/ +/* GDALGetCacheUsed64() */ +/************************************************************************/ + +/** + * \brief Get cache memory used. + * + * @return the number of bytes of memory currently in use by the + * GDALRasterBlock memory caching. + * + * @since GDAL 1.8.0 + */ + +GIntBig CPL_STDCALL GDALGetCacheUsed64() +{ + return nCacheUsed; +} + +/************************************************************************/ +/* GDALFlushCacheBlock() */ +/* */ +/* The workhorse of cache management! */ +/************************************************************************/ + +/** + * \brief Try to flush one cached raster block + * + * This function will search the first unlocked raster block and will + * flush it to release the associated memory. + * + * @return TRUE if one block was flushed, FALSE if there are no cached blocks + * or if they are currently locked. + */ +int CPL_STDCALL GDALFlushCacheBlock() + +{ + return GDALRasterBlock::FlushCacheBlock(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* GDALRasterBlock */ +/* ==================================================================== */ +/************************************************************************/ + +/** + * \class GDALRasterBlock "gdal_priv.h" + * + * GDALRasterBlock objects hold one block of raster data for one band + * that is currently stored in the GDAL raster cache. The cache holds + * some blocks of raster data for zero or more GDALRasterBand objects + * across zero or more GDALDataset objects in a global raster cache with + * a least recently used (LRU) list and an upper cache limit (see + * GDALSetCacheMax()) under which the cache size is normally kept. + * + * Some blocks in the cache may be modified relative to the state on disk + * (they are marked "Dirty") and must be flushed to disk before they can + * be discarded. Other (Clean) blocks may just be discarded if their memory + * needs to be recovered. + * + * In normal situations applications do not interact directly with the + * GDALRasterBlock - instead it it utilized by the RasterIO() interfaces + * to implement caching. + * + * Some driver classes are implemented in a fashion that completely avoids + * use of the GDAL raster cache (and GDALRasterBlock) though this is not very + * common. + */ + +/************************************************************************/ +/* FlushCacheBlock() */ +/* */ +/* Note, if we have alot of blocks locked for a long time, this */ +/* method is going to get slow because it will have to traverse */ +/* the linked list a long ways looking for a flushing */ +/* candidate. It might help to re-touch locked blocks to push */ +/* them to the top of the list. */ +/************************************************************************/ + +/** + * \brief Attempt to flush at least one block from the cache. + * + * This static method is normally used to recover memory when a request + * for a new cache block would put cache memory use over the established + * limit. + * + * C++ analog to the C function GDALFlushCacheBlock(). + * + * @param bDirtyBlocksOnly Only flushes dirty blocks. + * @return TRUE if successful or FALSE if no flushable block is found. + */ + +int GDALRasterBlock::FlushCacheBlock(int bDirtyBlocksOnly) + +{ + GDALRasterBlock *poTarget; + + { + INITIALIZE_LOCK; + poTarget = poOldest; + + while( poTarget != NULL && (poTarget->GetLockCount() > 0 || + (bDirtyBlocksOnly && !poTarget->GetDirty())) ) + poTarget = poTarget->poPrevious; + + if( poTarget == NULL ) + return FALSE; + + poTarget->Detach_unlocked(); + poTarget->GetBand()->UnreferenceBlock(poTarget->GetXOff(),poTarget->GetYOff()); + } + + if( poTarget->GetDirty() ) + { + CPLErr eErr = poTarget->Write(); + if( eErr != CE_None ) + { + /* Save the error for later reporting */ + poTarget->GetBand()->SetFlushBlockErr(eErr); + } + } + delete poTarget; + + return TRUE; +} + +/************************************************************************/ +/* FlushDirtyBlocks() */ +/************************************************************************/ + +/** + * \brief Flush all dirty blocks from cache. + * + * This static method is normally used to recover memory and is especially + * useful when doing multi-threaded code that can trigger the block cache. + * + * Due to the current design of the block cache, dirty blocks belonging to a same + * dataset could be pushed simultanously to the IWriteBlock() method of that + * dataset from different threads, causing races. + * + * Calling this method before that code can help workarounding that issue, + * in a multiple readers, one writer scenario. + * + * @since GDAL 2.0 + */ + +void GDALRasterBlock::FlushDirtyBlocks() + +{ + while( FlushCacheBlock(TRUE) ) + { + /* go on */ + } +} + +/************************************************************************/ +/* GDALRasterBlock() */ +/************************************************************************/ + +/** + * @brief GDALRasterBlock Constructor + * + * Normally only called from GDALRasterBand::GetLockedBlockRef(). + * + * @param poBandIn the raster band used as source of raster block + * being constructed. + * + * @param nXOffIn the horizontal block offset, with zero indicating + * the left most block, 1 the next block and so forth. + * + * @param nYOffIn the vertical block offset, with zero indicating + * the top most block, 1 the next block and so forth. + */ + +GDALRasterBlock::GDALRasterBlock( GDALRasterBand *poBandIn, + int nXOffIn, int nYOffIn ) + +{ + CPLAssert( NULL != poBandIn ); + + poBand = poBandIn; + + poBand->GetBlockSize( &nXSize, &nYSize ); + eType = poBand->GetRasterDataType(); + pData = NULL; + bDirty = FALSE; + nLockCount = 0; + + poNext = poPrevious = NULL; + + nXOff = nXOffIn; + nYOff = nYOffIn; + bMustDetach = TRUE; +} + +/************************************************************************/ +/* ~GDALRasterBlock() */ +/************************************************************************/ + +/** + * Block destructor. + * + * Normally called from GDALRasterBand::FlushBlock(). + */ + +GDALRasterBlock::~GDALRasterBlock() + +{ + Detach(); + + if( pData != NULL ) + { + VSIFree( pData ); + } + + CPLAssert( nLockCount == 0 ); + +#ifdef ENABLE_DEBUG + Verify(); +#endif +} + +/************************************************************************/ +/* Detach() */ +/************************************************************************/ + +/** + * Remove block from cache. + * + * This method removes the current block from the linked list used to keep + * track of all cached blocks in order of age. It does not affect whether + * the block is referenced by a GDALRasterBand nor does it destroy or flush + * the block. + */ + +void GDALRasterBlock::Detach() + +{ + if( bMustDetach ) + { + TAKE_LOCK; + Detach_unlocked(); + } +} + +void GDALRasterBlock::Detach_unlocked() +{ + if( poOldest == this ) + poOldest = poPrevious; + + if( poNewest == this ) + { + poNewest = poNext; + } + + if( poPrevious != NULL ) + poPrevious->poNext = poNext; + + if( poNext != NULL ) + poNext->poPrevious = poPrevious; + + poPrevious = NULL; + poNext = NULL; + bMustDetach = FALSE; + + if( pData ) + nCacheUsed -= GetBlockSize(); + +#ifdef ENABLE_DEBUG + Verify(); +#endif +} + +/************************************************************************/ +/* Verify() */ +/************************************************************************/ + +/** + * Confirms (via assertions) that the block cache linked list is in a + * consistent state. + */ + +void GDALRasterBlock::Verify() + +{ + TAKE_LOCK; + + CPLAssert( (poNewest == NULL && poOldest == NULL) + || (poNewest != NULL && poOldest != NULL) ); + + if( poNewest != NULL ) + { + CPLAssert( poNewest->poPrevious == NULL ); + CPLAssert( poOldest->poNext == NULL ); + + GDALRasterBlock* poLast = NULL; + for( GDALRasterBlock *poBlock = poNewest; + poBlock != NULL; + poBlock = poBlock->poNext ) + { + CPLAssert( poBlock->poPrevious == poLast ); + + poLast = poBlock; + } + + CPLAssert( poOldest == poLast ); + } +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +/** + * Force writing of the current block, if dirty. + * + * The block is written using GDALRasterBand::IWriteBlock() on it's + * corresponding band object. Even if the write fails the block will + * be marked clean. + * + * @return CE_None otherwise the error returned by IWriteBlock(). + */ + +CPLErr GDALRasterBlock::Write() + +{ + if( !GetDirty() ) + return CE_None; + + if( poBand == NULL ) + return CE_Failure; + + MarkClean(); + + if (poBand->eFlushBlockErr == CE_None) + { + int bCallLeaveReadWrite = poBand->EnterReadWrite(GF_Write); + CPLErr eErr = poBand->IWriteBlock( nXOff, nYOff, pData ); + if( bCallLeaveReadWrite ) poBand->LeaveReadWrite(); + return eErr; + } + else + return poBand->eFlushBlockErr; +} + +/************************************************************************/ +/* Touch() */ +/************************************************************************/ + +/** + * Push block to top of LRU (least-recently used) list. + * + * This method is normally called when a block is used to keep track + * that it has been recently used. + */ + +void GDALRasterBlock::Touch() + +{ + TAKE_LOCK; + Touch_unlocked(); +} + + +void GDALRasterBlock::Touch_unlocked() + +{ + if( poNewest == this ) + return; + + // In theory, we shouldn't try to touch a block that has been detached + CPLAssert(bMustDetach); + if( !bMustDetach ) + { + if( pData ) + nCacheUsed += GetBlockSize(); + + bMustDetach = TRUE; + } + + if( poOldest == this ) + poOldest = this->poPrevious; + + if( poPrevious != NULL ) + poPrevious->poNext = poNext; + + if( poNext != NULL ) + poNext->poPrevious = poPrevious; + + poPrevious = NULL; + poNext = poNewest; + + if( poNewest != NULL ) + { + CPLAssert( poNewest->poPrevious == NULL ); + poNewest->poPrevious = this; + } + poNewest = this; + + if( poOldest == NULL ) + { + CPLAssert( poPrevious == NULL && poNext == NULL ); + poOldest = this; + } +#ifdef ENABLE_DEBUG + Verify(); +#endif +} + +/************************************************************************/ +/* Internalize() */ +/************************************************************************/ + +/** + * Allocate memory for block. + * + * This method allocates memory for the block, and attempts to flush other + * blocks, if necessary, to bring the total cache size back within the limits. + * The newly allocated block is touched and will be considered most recently + * used in the LRU list. + * + * @return CE_None on success or CE_Failure if memory allocation fails. + */ + +CPLErr GDALRasterBlock::Internalize() + +{ + void *pNewData = NULL; + int nSizeInBytes; + + CPLAssert( pData == NULL ); + + // This call will initialize the hRBLock mutex. Other call places can + // only be called if we have go through there. + GIntBig nCurCacheMax = GDALGetCacheMax64(); + + /* No risk of overflow as it is checked in GDALRasterBand::InitBlockInfo() */ + nSizeInBytes = GetBlockSize(); + +/* -------------------------------------------------------------------- */ +/* Flush old blocks if we are nearing our memory limit. */ +/* -------------------------------------------------------------------- */ + int bFirstIter = TRUE; + int bLoopAgain; + do + { + bLoopAgain = FALSE; + GDALRasterBlock* apoBlocksToFree[64]; + int nBlocksToFree = 0; + { + TAKE_LOCK; + + if( bFirstIter ) + nCacheUsed += nSizeInBytes; + GDALRasterBlock *poTarget = poOldest; + while( nCacheUsed > nCurCacheMax ) + { + while( poTarget != NULL && poTarget->GetLockCount() > 0 ) + poTarget = poTarget->poPrevious; + + if( poTarget != NULL ) + { + GDALRasterBlock* _poPrevious = poTarget->poPrevious; + + poTarget->Detach_unlocked(); + poTarget->GetBand()->UnreferenceBlock(poTarget->GetXOff(),poTarget->GetYOff()); + + apoBlocksToFree[nBlocksToFree++] = poTarget; + if( poTarget->GetDirty() ) + { + // Only free one dirty block at a time so that + // other dirty blocks of other bands with the same coordinates + // can be found with TryGetLockedBlock() + bLoopAgain = ( nCacheUsed > nCurCacheMax ); + break; + } + if( nBlocksToFree == 64 ) + { + CPLDebug("GDAL", "More than 64 blocks are flagged to be flushed. Not trying more"); + break; + } + + poTarget = _poPrevious; + } + else + break; + } + + /* -------------------------------------------------------------------- */ + /* Add this block to the list. */ + /* -------------------------------------------------------------------- */ + if( !bLoopAgain ) + Touch_unlocked(); + } + + bFirstIter = FALSE; + + /* Now free blocks we have detached and removed from their band */ + for(int i=0;iGetDirty() ) + { + CPLErr eErr = poBlock->Write(); + if( eErr != CE_None ) + { + /* Save the error for later reporting */ + poBlock->GetBand()->SetFlushBlockErr(eErr); + } + } + + /* Try to recycle the data of an existing block */ + void* pDataBlock = poBlock->pData; + if( pNewData == NULL && pDataBlock != NULL && + poBlock->GetBlockSize() >= nSizeInBytes ) + { + pNewData = pDataBlock; + poBlock->pData = NULL; + } + + delete poBlock; + } + } + while(bLoopAgain); + + if( pNewData == NULL ) + { + pNewData = VSIMalloc( nSizeInBytes ); + if( pNewData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALRasterBlock::Internalize : Out of memory allocating %d bytes.", + nSizeInBytes); + return( CE_Failure ); + } + } + + pData = pNewData; + + return( CE_None ); +} + +/************************************************************************/ +/* MarkDirty() */ +/************************************************************************/ + +/** + * Mark the block as modified. + * + * A dirty block is one that has been modified and will need to be written + * to disk before it can be flushed. + */ + +void GDALRasterBlock::MarkDirty() + +{ + bDirty = TRUE; +} + + +/************************************************************************/ +/* MarkClean() */ +/************************************************************************/ + +/** + * Mark the block as unmodified. + * + * A dirty block is one that has been modified and will need to be written + * to disk before it can be flushed. + */ + +void GDALRasterBlock::MarkClean() + +{ + bDirty = FALSE; +} + +/************************************************************************/ +/* SafeLockBlock() */ +/************************************************************************/ + +/** + * \brief Safely lock block. + * + * This method locks a GDALRasterBlock (and touches it) in a thread-safe + * manner. The global block cache mutex is held while locking the block, + * in order to avoid race conditions with other threads that might be + * trying to expire the block at the same time. The block pointer may be + * safely NULL, in which case this method does nothing. + * + * @param ppBlock Pointer to the block pointer to try and lock/touch. + */ + +int GDALRasterBlock::SafeLockBlock( GDALRasterBlock ** ppBlock ) + +{ + CPLAssert( NULL != ppBlock ); + + TAKE_LOCK; + + if( *ppBlock != NULL ) + { + (*ppBlock)->AddLock(); + (*ppBlock)->Touch_unlocked(); + + return TRUE; + } + else + return FALSE; +} + +/************************************************************************/ +/* DestroyRBMutex() */ +/************************************************************************/ + +void GDALRasterBlock::DestroyRBMutex() +{ + if( hRBLock != NULL ) + DESTROY_LOCK; + hRBLock = NULL; +} diff --git a/bazaar/plugin/gdal/gcore/gdalrescaledalphaband.cpp b/bazaar/plugin/gdal/gcore/gdalrescaledalphaband.cpp new file mode 100644 index 000000000..c48fb345c --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalrescaledalphaband.cpp @@ -0,0 +1,149 @@ +/****************************************************************************** + * $Id: gdalrescaledalphaband.cpp 28053 2014-12-04 09:31:07Z rouault $ + * + * Project: GDAL Core + * Purpose: Implementation of GDALRescaledAlphaBand, a class implementing + * a band mask based from a non-GDT_Byte alpha band + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" + +CPL_CVSID("$Id: gdalrescaledalphaband.cpp 28053 2014-12-04 09:31:07Z rouault $"); + +/************************************************************************/ +/* GDALRescaledAlphaBand() */ +/************************************************************************/ + +GDALRescaledAlphaBand::GDALRescaledAlphaBand( GDALRasterBand *poParent ) + +{ + CPLAssert(poParent->GetRasterDataType() == GDT_UInt16); + + poDS = NULL; + nBand = 0; + + nRasterXSize = poParent->GetXSize(); + nRasterYSize = poParent->GetYSize(); + + eDataType = GDT_Byte; + poParent->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + this->poParent = poParent; + + pTemp = NULL; +} + +/************************************************************************/ +/* ~GDALRescaledAlphaBand() */ +/************************************************************************/ + +GDALRescaledAlphaBand::~GDALRescaledAlphaBand() + +{ + VSIFree(pTemp); +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GDALRescaledAlphaBand::IReadBlock( int nXBlockOff, int nYBlockOff, + void * pImage ) +{ + int nXSizeRequest = nBlockXSize; + if (nXBlockOff * nBlockXSize + nBlockXSize > nRasterXSize) + nXSizeRequest = nRasterXSize - nXBlockOff * nBlockXSize; + int nYSizeRequest = nBlockYSize; + if (nYBlockOff * nBlockYSize + nBlockYSize > nRasterYSize) + nYSizeRequest = nRasterYSize - nYBlockOff * nBlockYSize; + + GDALRasterIOExtraArg sExtraArg; + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + + return IRasterIO(GF_Read, nXBlockOff * nBlockXSize, nYBlockOff * nBlockYSize, + nXSizeRequest, nYSizeRequest, pImage, + nXSizeRequest, nYSizeRequest, GDT_Byte, + 1, nBlockXSize, &sExtraArg); +} + +/************************************************************************/ +/* IRasterIO() */ +/************************************************************************/ + +CPLErr GDALRescaledAlphaBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, + GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ + /* Optimization in common use case */ + /* This avoids triggering the block cache on this band, which helps */ + /* reducing the global block cache consumption */ + if (eRWFlag == GF_Read && eBufType == GDT_Byte && + nXSize == nBufXSize && nYSize == nBufYSize && + nPixelSpace == 1) + { + if( pTemp == NULL ) + { + pTemp = VSIMalloc2( sizeof(GUInt16), nRasterXSize ); + if (pTemp == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALRescaledAlphaBand::IReadBlock: Out of memory for buffer." ); + return CE_Failure; + } + } + for(int j = 0; j < nBufYSize; j++ ) + { + CPLErr eErr = poParent->RasterIO( GF_Read, nXOff, nYOff + j, nXSize, 1, + pTemp, nBufXSize, 1, + GDT_UInt16, + 0, 0, NULL ); + if (eErr != CE_None) + return eErr; + + GByte* pabyImage = ((GByte*) pData) + j * nLineSpace; + GUInt16* pSrc = (GUInt16 *)pTemp; + + for( int i = 0; i < nBufXSize; i++ ) + { + /* In case the dynamics was actually 0-255 and not 0-65535 as */ + /* expected, we want to make sure non-zero alpha will still be non-zero */ + if( pSrc[i] > 0 && pSrc[i] < 257 ) + pabyImage[i] = 1; + else + pabyImage[i] = (pSrc[i] * 255) / 65535; + } + } + return CE_None; + } + + return GDALRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace, psExtraArg ); +} diff --git a/bazaar/plugin/gdal/gcore/gdalsse_priv.h b/bazaar/plugin/gdal/gcore/gdalsse_priv.h new file mode 100644 index 000000000..e323e87df --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalsse_priv.h @@ -0,0 +1,571 @@ +/****************************************************************************** + * $Id: gdalsse_priv.h 28877 2015-04-08 23:11:36Z rouault $ + * + * Project: GDAL + * Purpose: SSE2 helper + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDALSSE_PRIV_H_INCLUDED +#define GDALSSE_PRIV_H_INCLUDED + +/* We restrict to 64bit processors because they are guaranteed to have SSE2 */ +/* Could possibly be used too on 32bit, but we would need to check at runtime */ +#if (defined(__x86_64) || defined(_M_X64)) && !defined(USE_SSE2_EMULATION) + +/* Requires SSE2 */ +#include +#include + +class XMMReg2Double +{ + public: + __m128d xmm; + + XMMReg2Double() {} + XMMReg2Double(double val) { xmm = _mm_load_sd (&val); } + XMMReg2Double(const XMMReg2Double& other) : xmm(other.xmm) {} + + static inline XMMReg2Double Zero() + { + XMMReg2Double reg; + reg.Zeroize(); + return reg; + } + + static inline XMMReg2Double Load2Val(const double* ptr) + { + XMMReg2Double reg; + reg.nsLoad2Val(ptr); + return reg; + } + + static inline XMMReg2Double Load2Val(const float* ptr) + { + XMMReg2Double reg; + reg.nsLoad2Val(ptr); + return reg; + } + + static inline XMMReg2Double Load2ValAligned(const double* ptr) + { + XMMReg2Double reg; + reg.nsLoad2ValAligned(ptr); + return reg; + } + + static inline XMMReg2Double Load2Val(const unsigned char* ptr) + { + XMMReg2Double reg; + reg.nsLoad2Val(ptr); + return reg; + } + + static inline XMMReg2Double Load2Val(const short* ptr) + { + XMMReg2Double reg; + reg.nsLoad2Val(ptr); + return reg; + } + + static inline XMMReg2Double Load2Val(const unsigned short* ptr) + { + XMMReg2Double reg; + reg.nsLoad2Val(ptr); + return reg; + } + + inline void nsLoad2Val(const double* ptr) + { + xmm = _mm_loadu_pd(ptr); + } + + inline void nsLoad2ValAligned(const double* pval) + { + xmm = _mm_load_pd(pval); + } + + inline void nsLoad2Val(const float* pval) + { + __m128 temp1 = _mm_load_ss(pval); + __m128 temp2 = _mm_load_ss(pval + 1); + temp1 = _mm_shuffle_ps(temp1, temp2, _MM_SHUFFLE(1,0,1,0)); + temp1 = _mm_shuffle_ps(temp1, temp1, _MM_SHUFFLE(3,3,2,0)); + xmm = _mm_cvtps_pd(temp1); + } + + inline void nsLoad2Val(const unsigned char* ptr) + { + __m128i xmm_i = _mm_cvtsi32_si128(*(unsigned short*)(ptr)); + xmm_i = _mm_unpacklo_epi8(xmm_i, _mm_setzero_si128()); + xmm_i = _mm_unpacklo_epi16(xmm_i, _mm_setzero_si128()); + xmm = _mm_cvtepi32_pd(xmm_i); + } + + inline void nsLoad2Val(const short* ptr) + { + int i; + memcpy(&i, ptr, 4); + __m128i xmm_i = _mm_cvtsi32_si128(i); + xmm_i = _mm_unpacklo_epi16(xmm_i,xmm_i); /* 0|0|0|0|0|0|b|a --> 0|0|0|0|b|b|a|a */ + xmm_i = _mm_srai_epi32(xmm_i, 16); /* 0|0|0|0|b|b|a|a --> 0|0|0|0|sign(b)|b|sign(a)|a */ + xmm = _mm_cvtepi32_pd(xmm_i); + } + + inline void nsLoad2Val(const unsigned short* ptr) + { + int i; + memcpy(&i, ptr, 4); + __m128i xmm_i = _mm_cvtsi32_si128(i); + xmm_i = _mm_unpacklo_epi16(xmm_i,xmm_i); /* 0|0|0|0|0|0|b|a --> 0|0|0|0|b|b|a|a */ + xmm_i = _mm_srli_epi32(xmm_i, 16); /* 0|0|0|0|b|b|a|a --> 0|0|0|0|0|b|0|a */ + xmm = _mm_cvtepi32_pd(xmm_i); + } + + static inline void Load4Val(const unsigned char* ptr, XMMReg2Double& low, XMMReg2Double& high) + { + __m128i xmm_i = _mm_cvtsi32_si128(*(int*)(ptr)); + xmm_i = _mm_unpacklo_epi8(xmm_i, _mm_setzero_si128()); + xmm_i = _mm_unpacklo_epi16(xmm_i, _mm_setzero_si128()); + low.xmm = _mm_cvtepi32_pd(xmm_i); + high.xmm = _mm_cvtepi32_pd(_mm_shuffle_epi32(xmm_i,_MM_SHUFFLE(3,2,3,2))); + } + + static inline void Load4Val(const short* ptr, XMMReg2Double& low, XMMReg2Double& high) + { + low.nsLoad2Val(ptr); + high.nsLoad2Val(ptr+2); + } + + static inline void Load4Val(const unsigned short* ptr, XMMReg2Double& low, XMMReg2Double& high) + { + low.nsLoad2Val(ptr); + high.nsLoad2Val(ptr+2); + } + + static inline void Load4Val(const double* ptr, XMMReg2Double& low, XMMReg2Double& high) + { + low.nsLoad2Val(ptr); + high.nsLoad2Val(ptr+2); + } + + static inline void Load4Val(const float* ptr, XMMReg2Double& low, XMMReg2Double& high) + { + __m128 temp1 = _mm_loadu_ps(ptr); + __m128 temp2 = _mm_shuffle_ps(temp1, temp1, _MM_SHUFFLE(3,2,3,2)); + low.xmm = _mm_cvtps_pd(temp1); + high.xmm = _mm_cvtps_pd(temp2); + } + + inline void Zeroize() + { + xmm = _mm_setzero_pd(); + } + + inline const XMMReg2Double& operator= (const XMMReg2Double& other) + { + xmm = other.xmm; + return *this; + } + + inline const XMMReg2Double& operator+= (const XMMReg2Double& other) + { + xmm = _mm_add_pd(xmm, other.xmm); + return *this; + } + + inline XMMReg2Double operator+ (const XMMReg2Double& other) + { + XMMReg2Double ret; + ret.xmm = _mm_add_pd(xmm, other.xmm); + return ret; + } + + inline XMMReg2Double operator- (const XMMReg2Double& other) + { + XMMReg2Double ret; + ret.xmm = _mm_sub_pd(xmm, other.xmm); + return ret; + } + + inline XMMReg2Double operator* (const XMMReg2Double& other) + { + XMMReg2Double ret; + ret.xmm = _mm_mul_pd(xmm, other.xmm); + return ret; + } + + inline const XMMReg2Double& operator*= (const XMMReg2Double& other) + { + xmm = _mm_mul_pd(xmm, other.xmm); + return *this; + } + + inline void AddLowAndHigh() + { + __m128d xmm2; + xmm2 = _mm_shuffle_pd(xmm,xmm,_MM_SHUFFLE2(0,1)); /* transfer high word into low word of xmm2 */ + xmm = _mm_add_pd(xmm, xmm2); + } + + inline void Store2Double(double* pval) + { + _mm_storeu_pd(pval, xmm); + } + + inline void Store2DoubleAligned(double* pval) + { + _mm_store_pd(pval, xmm); + } + + inline operator double () const + { + double val; + _mm_store_sd(&val, xmm); + return val; + } +}; + +#else + +#warning "Software emulation of SSE2 !" + +class XMMReg2Double +{ + public: + double low; + double high; + + XMMReg2Double() {} + XMMReg2Double(double val) { low = val; high = 0.0; } + XMMReg2Double(const XMMReg2Double& other) : low(other.low), high(other.high) {} + + static inline XMMReg2Double Zero() + { + XMMReg2Double reg; + reg.Zeroize(); + return reg; + } + + static inline XMMReg2Double Load2Val(const double* ptr) + { + XMMReg2Double reg; + reg.nsLoad2Val(ptr); + return reg; + } + + static inline XMMReg2Double Load2ValAligned(const double* ptr) + { + XMMReg2Double reg; + reg.nsLoad2ValAligned(ptr); + return reg; + } + + static inline XMMReg2Double Load2Val(const float* ptr) + { + XMMReg2Double reg; + reg.nsLoad2Val(ptr); + return reg; + } + + static inline XMMReg2Double Load2Val(const unsigned char* ptr) + { + XMMReg2Double reg; + reg.nsLoad2Val(ptr); + return reg; + } + + static inline XMMReg2Double Load2Val(const short* ptr) + { + XMMReg2Double reg; + reg.nsLoad2Val(ptr); + return reg; + } + + inline void nsLoad2Val(const double* pval) + { + low = pval[0]; + high = pval[1]; + } + + inline void nsLoad2ValAligned(const double* pval) + { + low = pval[0]; + high = pval[1]; + } + + inline void nsLoad2Val(const float* pval) + { + low = pval[0]; + high = pval[1]; + } + + inline void nsLoad2Val(const unsigned char* ptr) + { + low = ptr[0]; + high = ptr[1]; + } + + inline void nsLoad2Val(const short* ptr) + { + low = ptr[0]; + high = ptr[1]; + } + + inline void nsLoad2Val(const unsigned short* ptr) + { + low = ptr[0]; + high = ptr[1]; + } + + static inline void Load4Val(const unsigned char* ptr, XMMReg2Double& low, XMMReg2Double& high) + { + low.low = ptr[0]; + low.high = ptr[1]; + high.low = ptr[2]; + high.high = ptr[3]; + } + + static inline void Load4Val(const short* ptr, XMMReg2Double& low, XMMReg2Double& high) + { + low.nsLoad2Val(ptr); + high.nsLoad2Val(ptr+2); + } + + static inline void Load4Val(const unsigned short* ptr, XMMReg2Double& low, XMMReg2Double& high) + { + low.nsLoad2Val(ptr); + high.nsLoad2Val(ptr+2); + } + + static inline void Load4Val(const double* ptr, XMMReg2Double& low, XMMReg2Double& high) + { + low.nsLoad2Val(ptr); + high.nsLoad2Val(ptr+2); + } + + static inline void Load4Val(const float* ptr, XMMReg2Double& low, XMMReg2Double& high) + { + low.nsLoad2Val(ptr); + high.nsLoad2Val(ptr+2); + } + + inline void Zeroize() + { + low = 0.0; + high = 0.0; + } + + inline const XMMReg2Double& operator= (const XMMReg2Double& other) + { + low = other.low; + high = other.high; + return *this; + } + + inline const XMMReg2Double& operator+= (const XMMReg2Double& other) + { + low += other.low; + high += other.high; + return *this; + } + + inline XMMReg2Double operator+ (const XMMReg2Double& other) + { + XMMReg2Double ret; + ret.low = low + other.low; + ret.high = high + other.high; + return ret; + } + + inline XMMReg2Double operator- (const XMMReg2Double& other) + { + XMMReg2Double ret; + ret.low = low - other.low; + ret.high = high - other.high; + return ret; + } + + inline XMMReg2Double operator* (const XMMReg2Double& other) + { + XMMReg2Double ret; + ret.low = low * other.low; + ret.high = high * other.high; + return ret; + } + + inline const XMMReg2Double& operator*= (const XMMReg2Double& other) + { + low *= other.low; + high *= other.high; + return *this; + } + + inline void AddLowAndHigh() + { + double add = low + high; + low = add; + high = add; + } + + inline void Store2Double(double* pval) + { + pval[0] = low; + pval[1] = high; + } + + inline void Store2DoubleAligned(double* pval) + { + pval[0] = low; + pval[1] = high; + } + + inline operator double () const + { + return low; + } +}; + +#endif /* defined(__x86_64) || defined(_M_X64) */ + +class XMMReg4Double +{ + public: + XMMReg2Double low, high; + + XMMReg4Double() {} + XMMReg4Double(const XMMReg4Double& other) : low(other.low), high(other.high) {} + + static inline XMMReg4Double Zero() + { + XMMReg4Double reg; + reg.low.Zeroize(); + reg.high.Zeroize(); + return reg; + } + + static inline XMMReg4Double Load4Val(const unsigned char* ptr) + { + XMMReg4Double reg; + XMMReg2Double::Load4Val(ptr, reg.low, reg.high); + return reg; + } + + static inline XMMReg4Double Load4Val(const short* ptr) + { + XMMReg4Double reg; + reg.low.nsLoad2Val(ptr); + reg.high.nsLoad2Val(ptr+2); + return reg; + } + + static inline XMMReg4Double Load4Val(const unsigned short* ptr) + { + XMMReg4Double reg; + reg.low.nsLoad2Val(ptr); + reg.high.nsLoad2Val(ptr+2); + return reg; + } + + static inline XMMReg4Double Load4Val(const double* ptr) + { + XMMReg4Double reg; + reg.low.nsLoad2Val(ptr); + reg.high.nsLoad2Val(ptr+2); + return reg; + } + + static inline XMMReg4Double Load4ValAligned(const double* ptr) + { + XMMReg4Double reg; + reg.low.nsLoad2ValAligned(ptr); + reg.high.nsLoad2ValAligned(ptr+2); + return reg; + } + + static inline XMMReg4Double Load4Val(const float* ptr) + { + XMMReg4Double reg; + XMMReg2Double::Load4Val(ptr, reg.low, reg.high); + return reg; + } + + inline const XMMReg4Double& operator= (const XMMReg4Double& other) + { + low = other.low; + high = other.high; + return *this; + } + + inline const XMMReg4Double& operator+= (const XMMReg4Double& other) + { + low += other.low; + high += other.high; + return *this; + } + + inline XMMReg4Double operator+ (const XMMReg4Double& other) + { + XMMReg4Double ret; + ret.low = low + other.low; + ret.high = high + other.high; + return ret; + } + + inline XMMReg4Double operator- (const XMMReg4Double& other) + { + XMMReg4Double ret; + ret.low = low - other.low; + ret.high = high - other.high; + return ret; + } + + inline XMMReg4Double operator* (const XMMReg4Double& other) + { + XMMReg4Double ret; + ret.low = low * other.low; + ret.high = high * other.high; + return ret; + } + + inline const XMMReg4Double& operator*= (const XMMReg4Double& other) + { + low *= other.low; + high *= other.high; + return *this; + } + + inline void AddLowAndHigh() + { + low = low + high; + low.AddLowAndHigh(); + } + + inline XMMReg2Double& GetLow() + { + return low; + } +}; + +#endif /* GDALSSE_PRIV_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/gcore/gdalvirtualmem.cpp b/bazaar/plugin/gdal/gcore/gdalvirtualmem.cpp new file mode 100644 index 000000000..a827143e9 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/gdalvirtualmem.cpp @@ -0,0 +1,1558 @@ +/********************************************************************** + * $Id: gdalvirtualmem.cpp 27921 2014-11-05 12:58:23Z rouault $ + * + * Name: gdalvirtualmem.cpp + * Project: GDAL + * Purpose: Dataset and rasterband exposed as a virtual memory mapping. + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal.h" +#include "cpl_conv.h" +#include "cpl_virtualmem.h" + +/* To be changed if we go to 64-bit RasterIO coordinates and spacing */ +typedef int coord_type; +typedef int spacing_type; + +/************************************************************************/ +/* GDALVirtualMem */ +/************************************************************************/ + +class GDALVirtualMem +{ + GDALDatasetH hDS; + GDALRasterBandH hBand; + coord_type nXOff; + coord_type nYOff; + /*int nXSize; + int nYSize;*/ + coord_type nBufXSize; + coord_type nBufYSize; + GDALDataType eBufType; + int nBandCount; + int* panBandMap; + int nPixelSpace; + GIntBig nLineSpace; + GIntBig nBandSpace; + + int bIsCompact; + int bIsBandSequential; + + int IsCompact() const { return bIsCompact; } + int IsBandSequential() const { return bIsBandSequential; } + + void GetXYBand( size_t nOffset, coord_type& x, coord_type& y, int& band ) const; + size_t GetOffset(coord_type x, coord_type y, int band) const; + int GotoNextPixel(coord_type& x, coord_type& y, int& band) const; + + void DoIOBandSequential( GDALRWFlag eRWFlag, size_t nOffset, + void* pPage, size_t nBytes ) const; + void DoIOPixelInterleaved( GDALRWFlag eRWFlag, size_t nOffset, + void* pPage, size_t nBytes ) const; + +public: + GDALVirtualMem( GDALDatasetH hDS, + GDALRasterBandH hBand, + coord_type nXOff, coord_type nYOff, + coord_type nXSize, coord_type nYSize, + coord_type nBufXSize, coord_type nBufYSize, + GDALDataType eBufType, + int nBandCount, const int* panBandMapIn, + int nPixelSpace, + GIntBig nLineSpace, + GIntBig nBandSpace ); + ~GDALVirtualMem(); + + static void FillCacheBandSequential(CPLVirtualMem* ctxt, size_t nOffset, + void* pPageToFill, + size_t nToFill, void* pUserData); + static void SaveFromCacheBandSequential(CPLVirtualMem* ctxt, size_t nOffset, + const void* pPageToBeEvicted, + size_t nToEvicted, void* pUserData); + + static void FillCachePixelInterleaved(CPLVirtualMem* ctxt, size_t nOffset, + void* pPageToFill, + size_t nToFill, void* pUserData); + static void SaveFromCachePixelInterleaved(CPLVirtualMem* ctxt, size_t nOffset, + const void* pPageToBeEvicted, + size_t nToEvicted, void* pUserData); + + static void Destroy(void* pUserData); +}; + +/************************************************************************/ +/* GDALVirtualMem() */ +/************************************************************************/ + +GDALVirtualMem::GDALVirtualMem( GDALDatasetH hDS, + GDALRasterBandH hBand, + coord_type nXOff, coord_type nYOff, + CPL_UNUSED coord_type nXSize, + CPL_UNUSED coord_type nYSize, + coord_type nBufXSize, coord_type nBufYSize, + GDALDataType eBufType, + int nBandCount, const int* panBandMapIn, + int nPixelSpace, + GIntBig nLineSpace, + GIntBig nBandSpace ) : + hDS(hDS), hBand(hBand), nXOff(nXOff), nYOff(nYOff), /*nXSize(nXSize), nYSize(nYSize),*/ + nBufXSize(nBufXSize), nBufYSize(nBufYSize), eBufType(eBufType), + nBandCount(nBandCount), nPixelSpace(nPixelSpace), nLineSpace(nLineSpace), + nBandSpace(nBandSpace) +{ + if( hDS != NULL ) + { + if( panBandMapIn ) + { + panBandMap = (int*) CPLMalloc(nBandCount * sizeof(int)); + memcpy(panBandMap, panBandMapIn, nBandCount * sizeof(int)); + } + else + { + panBandMap = (int*) CPLMalloc(nBandCount * sizeof(int)); + for(int i=0;i= nBufYSize * nLineSpace ); +} + +/************************************************************************/ +/* ~GDALVirtualMem() */ +/************************************************************************/ + +GDALVirtualMem::~GDALVirtualMem() +{ + CPLFree(panBandMap); +} + +/************************************************************************/ +/* GetXYBand() */ +/************************************************************************/ + +void GDALVirtualMem::GetXYBand( size_t nOffset, coord_type& x, coord_type& y, int& band ) const +{ + if( IsBandSequential() ) + { + if( nBandCount == 1 ) + band = 0; + else + band = (int)(nOffset / nBandSpace); + y = (coord_type)((nOffset - band * nBandSpace) / nLineSpace); + x = (coord_type)((nOffset - band * nBandSpace - y * nLineSpace) / nPixelSpace); + } + else + { + y = (coord_type)(nOffset / nLineSpace); + x = (coord_type)((nOffset - y * nLineSpace) / nPixelSpace); + if( nBandCount == 1 ) + band = 0; + else + band = (int)((nOffset - y * nLineSpace - x * nPixelSpace) / nBandSpace); + } +} + +/************************************************************************/ +/* GotoNextPixel() */ +/************************************************************************/ + +int GDALVirtualMem::GotoNextPixel(coord_type& x, coord_type& y, int& band) const +{ + if( IsBandSequential() ) + { + x++; + if( x == nBufXSize ) + { + x = 0; + y ++; + } + if( y == nBufYSize ) + { + y = 0; + band ++; + if (band == nBandCount) + return FALSE; + } + } + else + { + band ++; + if( band == nBandCount ) + { + band = 0; + x ++; + } + if( x == nBufXSize ) + { + x = 0; + y ++; + if(y == nBufYSize) + return FALSE; + } + } + return TRUE; +} + +/************************************************************************/ +/* GetOffset() */ +/************************************************************************/ + +size_t GDALVirtualMem::GetOffset(coord_type x, coord_type y, int band) const +{ + return (size_t)(x * nPixelSpace + y * nLineSpace + band * nBandSpace); +} + +/************************************************************************/ +/* DoIOPixelInterleaved() */ +/************************************************************************/ + +void GDALVirtualMem::DoIOPixelInterleaved( GDALRWFlag eRWFlag, + const size_t nOffset, void* pPage, size_t nBytes ) const +{ + coord_type x, y; + int band; + + GetXYBand(nOffset, x, y, band); + /*fprintf(stderr, "eRWFlag=%d, nOffset=%d, x=%d, y=%d, band=%d\n", + eRWFlag, (int)nOffset, x, y, band);*/ + + if( eRWFlag == GF_Read && !IsCompact() ) + memset(pPage, 0, nBytes); + + if( band >= nBandCount ) + { + band = nBandCount - 1; + if( !GotoNextPixel(x, y, band) ) + return; + } + else if( x >= nBufXSize ) + { + x = nBufXSize - 1; + band = nBandCount - 1; + if( !GotoNextPixel(x, y, band) ) + return; + } + + size_t nOffsetRecompute = GetOffset(x, y, band); + CPLAssert(nOffsetRecompute >= nOffset); + size_t nOffsetShift = nOffsetRecompute - nOffset; + if( nOffsetShift >= nBytes ) + return; + + // If we don't start at the first band for that given pixel, load/store + // the remaining bands + if( band > 0 ) + { + size_t nEndOffsetEndOfPixel = GetOffset(x, y, nBandCount); + int bandEnd; + // Check that we have enough space to load/store until last band + // Should be always OK unless the number of bands is really huge + if( nEndOffsetEndOfPixel - nOffset > nBytes ) + { + // Not enough space: find last possible band + coord_type xEnd, yEnd; + GetXYBand(nOffset + nBytes, xEnd, yEnd, bandEnd); + CPLAssert(x == xEnd); + CPLAssert(y == yEnd); + } + else + bandEnd = nBandCount; + + // Finish reading/writing the remaining bands for that pixel + GDALDatasetRasterIO( hDS, eRWFlag, + nXOff + x, nYOff + y, 1, 1, + (char*)pPage + nOffsetShift, + 1, 1, eBufType, + bandEnd - band, panBandMap + band, + nPixelSpace, (spacing_type)nLineSpace, (spacing_type)nBandSpace ); + + if( bandEnd < nBandCount ) + return; + + band = nBandCount - 1; + if( !GotoNextPixel(x, y, band) ) + return; + nOffsetRecompute = GetOffset(x, y, 0); + nOffsetShift = nOffsetRecompute - nOffset; + if( nOffsetShift >= nBytes ) + return; + } + + // Is there enough place to store/load up to the end of current line ? + size_t nEndOffsetEndOfLine = GetOffset(nBufXSize-1, y, nBandCount); + if( nEndOffsetEndOfLine - nOffset > nBytes ) + { + // No : read/write as many pixels on this line as possible + coord_type xEnd, yEnd; + int bandEnd; + GetXYBand(nOffset + nBytes, xEnd, yEnd, bandEnd); + CPLAssert(y == yEnd); + + if( x < xEnd ) + { + GDALDatasetRasterIO( hDS, eRWFlag, + nXOff + x, nYOff + y, xEnd - x, 1, + (char*) pPage + nOffsetShift, + xEnd - x, 1, eBufType, + nBandCount, panBandMap, + nPixelSpace, (spacing_type)nLineSpace, (spacing_type)nBandSpace ); + } + + // Are there partial bands to read/write for the last pixel ? + if( bandEnd > 0 ) + { + x = xEnd; + nOffsetRecompute = GetOffset(x, y, 0); + nOffsetShift = nOffsetRecompute - nOffset; + if( nOffsetShift >= nBytes ) + return; + + if( bandEnd >= nBandCount ) + bandEnd = nBandCount; + + GDALDatasetRasterIO( hDS, eRWFlag, + nXOff + x, nYOff + y, 1, 1, + (char*) pPage + nOffsetShift, + 1, 1, eBufType, + bandEnd, panBandMap, + nPixelSpace, (spacing_type)nLineSpace, (spacing_type)nBandSpace ); + } + + return; + } + + // Yes, enough place to read/write until end of line + if( x > 0 || nBytes - nOffsetShift < (size_t)nLineSpace ) + { + GDALDatasetRasterIO( hDS, eRWFlag, + nXOff + x, nYOff + y, nBufXSize - x, 1, + (char*)pPage + nOffsetShift, + nBufXSize - x, 1, eBufType, + nBandCount, panBandMap, + nPixelSpace, (spacing_type)nLineSpace, (spacing_type)nBandSpace ); + + // Go to beginning of next line + x = nBufXSize - 1; + band = nBandCount - 1; + if( !GotoNextPixel(x, y, band) ) + return; + nOffsetRecompute = GetOffset(x, y, 0); + nOffsetShift = nOffsetRecompute - nOffset; + if( nOffsetShift >= nBytes ) + return; + } + + // How many whole lines can we store/load ? + coord_type nLineCount = (coord_type)((nBytes - nOffsetShift) / nLineSpace); + if( y + nLineCount > nBufYSize ) + nLineCount = nBufYSize - y; + if( nLineCount > 0 ) + { + GDALDatasetRasterIO( hDS, eRWFlag, + nXOff + 0, nYOff + y, nBufXSize, nLineCount, + (GByte*) pPage + nOffsetShift, + nBufXSize, nLineCount, eBufType, + nBandCount, panBandMap, + nPixelSpace, (spacing_type)nLineSpace, (spacing_type)nBandSpace ); + + y += nLineCount; + if( y == nBufYSize ) + return; + nOffsetRecompute = GetOffset(x, y, 0); + nOffsetShift = nOffsetRecompute - nOffset; + } + + if( nOffsetShift < nBytes ) + { + DoIOPixelInterleaved( eRWFlag, nOffsetRecompute, + (char*) pPage + nOffsetShift, nBytes - nOffsetShift ); + } +} + +/************************************************************************/ +/* DoIOPixelInterleaved() */ +/************************************************************************/ + +void GDALVirtualMem::DoIOBandSequential( GDALRWFlag eRWFlag, + const size_t nOffset, void* pPage, size_t nBytes ) const +{ + coord_type x, y; + int band; + + GetXYBand(nOffset, x, y, band); + /*fprintf(stderr, "eRWFlag=%d, nOffset=%d, x=%d, y=%d, band=%d\n", + eRWFlag, (int)nOffset, x, y, band);*/ + + if( eRWFlag == GF_Read && !IsCompact() ) + memset(pPage, 0, nBytes); + + if( x >= nBufXSize ) + { + x = nBufXSize - 1; + if( !GotoNextPixel(x, y, band) ) + return; + } + else if( y >= nBufYSize ) + { + x = nBufXSize - 1; + y = nBufYSize - 1; + if( !GotoNextPixel(x, y, band) ) + return; + } + + size_t nOffsetRecompute = GetOffset(x, y, band); + CPLAssert(nOffsetRecompute >= nOffset); + size_t nOffsetShift = nOffsetRecompute - nOffset; + if( nOffsetShift >= nBytes ) + return; + + // Is there enough place to store/load up to the end of current line ? + size_t nEndOffsetEndOfLine = GetOffset(nBufXSize, y, band); + if( nEndOffsetEndOfLine - nOffset > nBytes ) + { + // No : read/write as many pixels on this line as possible + coord_type xEnd, yEnd; + int bandEnd; + GetXYBand(nOffset + nBytes, xEnd, yEnd, bandEnd); + CPLAssert(y == yEnd); + CPLAssert(band == bandEnd); + GDALRasterIO( (hBand) ? hBand : GDALGetRasterBand(hDS, panBandMap[band]), eRWFlag, + nXOff + x, nYOff + y, xEnd - x, 1, + (char*)pPage + nOffsetShift, + xEnd - x, 1, eBufType, + nPixelSpace, (spacing_type)nLineSpace ); + + return; + } + + // Yes, enough place to read/write until end of line + if( x > 0 || nBytes - nOffsetShift < (size_t)nLineSpace ) + { + GDALRasterIO( (hBand) ? hBand : GDALGetRasterBand(hDS, panBandMap[band]), eRWFlag, + nXOff + x, nYOff + y, nBufXSize - x, 1, + (char*)pPage + nOffsetShift, + nBufXSize - x, 1, eBufType, + nPixelSpace, (spacing_type)nLineSpace ); + + // Go to beginning of next line + x = nBufXSize - 1; + if( !GotoNextPixel(x, y, band) ) + return; + nOffsetRecompute = GetOffset(x, y, band); + nOffsetShift = nOffsetRecompute - nOffset; + if( nOffsetShift >= nBytes ) + return; + } + + // How many whole lines can we store/load ? + coord_type nLineCount = (coord_type)((nBytes - nOffsetShift) / nLineSpace); + if( y + nLineCount > nBufYSize ) + nLineCount = nBufYSize - y; + if( nLineCount > 0 ) + { + GDALRasterIO( (hBand) ? hBand : GDALGetRasterBand(hDS, panBandMap[band]), eRWFlag, + nXOff + 0, nYOff + y, nBufXSize, nLineCount, + (GByte*) pPage + nOffsetShift, + nBufXSize, nLineCount, eBufType, + nPixelSpace, (spacing_type)nLineSpace ); + + y += nLineCount; + if( y == nBufYSize ) + { + y = 0; + band ++; + if( band == nBandCount ) + return; + } + nOffsetRecompute = GetOffset(x, y, band); + nOffsetShift = nOffsetRecompute - nOffset; + } + + if( nOffsetShift < nBytes ) + { + DoIOBandSequential( eRWFlag, nOffsetRecompute, + (char*) pPage + nOffsetShift, nBytes - nOffsetShift ); + } +} + +/************************************************************************/ +/* FillCacheBandSequential() */ +/************************************************************************/ + +void GDALVirtualMem::FillCacheBandSequential(CPLVirtualMem* ctxt, + size_t nOffset, + void* pPageToFill, + size_t nToFill, + void* pUserData) +{ + const GDALVirtualMem* psParms = (const GDALVirtualMem* )pUserData; + (void)ctxt; + psParms->DoIOBandSequential(GF_Read, nOffset, pPageToFill, nToFill); +} + +/************************************************************************/ +/* SaveFromCacheBandSequential() */ +/************************************************************************/ + +void GDALVirtualMem::SaveFromCacheBandSequential(CPLVirtualMem* ctxt, + size_t nOffset, + const void* pPageToBeEvicted, + size_t nToEvicted, + void* pUserData) +{ + const GDALVirtualMem* psParms = (const GDALVirtualMem* )pUserData; + (void)ctxt; + psParms->DoIOBandSequential(GF_Write, nOffset, (void*)pPageToBeEvicted, nToEvicted); +} + +/************************************************************************/ +/* FillCachePixelInterleaved() */ +/************************************************************************/ + +void GDALVirtualMem::FillCachePixelInterleaved(CPLVirtualMem* ctxt, + size_t nOffset, + void* pPageToFill, + size_t nToFill, + void* pUserData) +{ + const GDALVirtualMem* psParms = (const GDALVirtualMem* )pUserData; + (void)ctxt; + psParms->DoIOPixelInterleaved(GF_Read, nOffset, pPageToFill, nToFill); +} + +/************************************************************************/ +/* SaveFromCachePixelInterleaved() */ +/************************************************************************/ + +void GDALVirtualMem::SaveFromCachePixelInterleaved(CPLVirtualMem* ctxt, + size_t nOffset, + const void* pPageToBeEvicted, + size_t nToEvicted, + void* pUserData) +{ + const GDALVirtualMem* psParms = (const GDALVirtualMem* )pUserData; + (void)ctxt; + psParms->DoIOPixelInterleaved(GF_Write, nOffset, (void*)pPageToBeEvicted, nToEvicted); +} + +/************************************************************************/ +/* Destroy() */ +/************************************************************************/ + +void GDALVirtualMem::Destroy(void* pUserData) +{ + GDALVirtualMem* psParams = (GDALVirtualMem*) pUserData; + delete psParams; +} + +/************************************************************************/ +/* GDALCheckBandParameters() */ +/************************************************************************/ + +static int GDALCheckBandParameters( GDALDatasetH hDS, + int nBandCount, int* panBandMap ) +{ + if( nBandCount == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "nBandCount == 0"); + return FALSE; + } + + if( panBandMap != NULL ) + { + for(int i=0;i GDALGetRasterCount(hDS) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "panBandMap[%d]=%d", + i, panBandMap[i]); + return FALSE; + } + } + } + else if( nBandCount > GDALGetRasterCount(hDS) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "nBandCount > GDALGetRasterCount(hDS)"); + return FALSE; + } + return TRUE; +} + +/************************************************************************/ +/* GDALGetVirtualMem() */ +/************************************************************************/ + +static CPLVirtualMem* GDALGetVirtualMem( GDALDatasetH hDS, + GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + coord_type nXOff, coord_type nYOff, + coord_type nXSize, coord_type nYSize, + coord_type nBufXSize, coord_type nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, + GIntBig nLineSpace, + GIntBig nBandSpace, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + char **papszOptions ) +{ + CPLVirtualMem* view; + GDALVirtualMem* psParams; + GUIntBig nReqMem; + (void) papszOptions; + + if( nXSize != nBufXSize || nYSize != nBufYSize ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "nXSize != nBufXSize || nYSize != nBufYSize"); + return NULL; + } + + int nRasterXSize = (hDS) ? GDALGetRasterXSize(hDS) : GDALGetRasterBandXSize(hBand); + int nRasterYSize = (hDS) ? GDALGetRasterYSize(hDS) : GDALGetRasterBandYSize(hBand); + + if( nXOff < 0 || nYOff < 0 || + nXSize == 0 || nYSize == 0 || + nBufXSize < 0 || nBufYSize < 0 || + nXOff + nXSize > nRasterXSize || + nYOff + nYSize > nRasterYSize ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid window request"); + return NULL; + } + + if( nPixelSpace < 0 || nLineSpace < 0 || nBandSpace < 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "nPixelSpace < 0 || nLineSpace < 0 || nBandSpace < 0"); + return NULL; + } + + if( hDS != NULL && !GDALCheckBandParameters(hDS, nBandCount, panBandMap ) ) + return NULL; + + int nDataTypeSize = GDALGetDataTypeSize(eBufType) / 8; + if( nPixelSpace == 0 ) + nPixelSpace = nDataTypeSize; + if( nLineSpace == 0 ) + nLineSpace = (GIntBig)nBufXSize * nPixelSpace; + if( nBandSpace == 0 ) + nBandSpace = (GIntBig)nBufYSize * nLineSpace; + + // OFFSET = offset(x,y,band) = x * nPixelSpace + y * nLineSpace + band * nBandSpace + // where 0 <= x < nBufXSize and 0 <= y < nBufYSize and 0 <= band < nBandCount + // if nPixelSpace, nLineSpace and nBandSpace can have arbitrary values, there's + // no way of finding a unique(x,y,band) solution. We need to restrict the + // space of possibilities strongly. + // if nBandSpace >= nBufYSize * nLineSpace and nLineSpace >= nBufXSize * nPixelSpace, INTERLEAVE = BAND + // band = OFFSET / nBandSpace + // y = (OFFSET - band * nBandSpace) / nLineSpace + // x = (OFFSET - band * nBandSpace - y * nLineSpace) / nPixelSpace + // else if nPixelSpace >= nBandCount * nBandSpace and nLineSpace >= nBufXSize * nPixelSpace, INTERLEAVE = PIXEL + // y = OFFSET / nLineSpace + // x = (OFFSET - y * nLineSpace) / nPixelSpace + // band = (OFFSET - y * nLineSpace - x * nPixelSpace) / nBandSpace + + if( nDataTypeSize == 0 || /* to please Coverity. not needed */ + nLineSpace < (GIntBig)nBufXSize * nPixelSpace || + (nBandCount > 1 && + (nBandSpace == nPixelSpace || + (nBandSpace < nPixelSpace && + (nBandSpace < nDataTypeSize || nPixelSpace < nBandCount * nBandSpace)) || + (nBandSpace > nPixelSpace && + (nPixelSpace < nDataTypeSize || nBandSpace < nBufYSize * nLineSpace)))) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Only pixel interleaving or band interleaving are supported"); + return NULL; + } + + /* Avoid odd spacings that would complicate I/O operations */ + /* Ensuring they are multiple of nDataTypeSize should be fine, because */ + /* the page size is a power of 2 that is also a multiple of nDataTypeSize */ + if( (nPixelSpace % nDataTypeSize) != 0 || + (nLineSpace % nDataTypeSize) != 0 || + (nBandSpace % nDataTypeSize) != 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported spacing"); + return NULL; + } + + int bIsBandSequential = ( nBandSpace >= nBufYSize * nLineSpace ); + if( bIsBandSequential ) + nReqMem = nBandCount * nBandSpace; + else + nReqMem = nBufYSize * nLineSpace; + if( nReqMem != (GUIntBig)(size_t)nReqMem ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot reserve " CPL_FRMT_GUIB " bytes", nReqMem); + return NULL; + } + + psParams = new GDALVirtualMem(hDS, hBand, nXOff, nYOff, + nXSize, nYSize, + nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, + nLineSpace, + nBandSpace); + + view = CPLVirtualMemNew((size_t)nReqMem, + nCacheSize, + nPageSizeHint, + bSingleThreadUsage, + (eRWFlag == GF_Read) ? VIRTUALMEM_READONLY_ENFORCED : VIRTUALMEM_READWRITE, + (bIsBandSequential) ? GDALVirtualMem::FillCacheBandSequential : + GDALVirtualMem::FillCachePixelInterleaved, + (bIsBandSequential) ? GDALVirtualMem::SaveFromCacheBandSequential : + GDALVirtualMem::SaveFromCachePixelInterleaved, + GDALVirtualMem::Destroy, + psParams); + + if( view == NULL ) + { + delete psParams; + } + + return view; +} + +/************************************************************************/ +/* GDALDatasetGetVirtualMem() */ +/************************************************************************/ + +/** Create a CPLVirtualMem object from a GDAL dataset object. + * + * Only supported on Linux for now. + * + * This method allows creating a virtual memory object for a region of one + * or more GDALRasterBands from this dataset. The content of the virtual + * memory object is automatically filled from dataset content when a virtual + * memory page is first accessed, and it is released (or flushed in case of a + * "dirty" page) when the cache size limit has been reached. + * + * The pointer to access the virtual memory object is obtained with + * CPLVirtualMemGetAddr(). It remains valid until CPLVirtualMemFree() is called. + * CPLVirtualMemFree() must be called before the dataset object is destroyed. + * + * If p is such a pointer and base_type the C type matching eBufType, for default + * values of spacing parameters, the element of image coordinates (x, y) + * (relative to xOff, yOff) for band b can be accessed with + * ((base_type*)p)[x + y * nBufXSize + (b-1)*nBufXSize*nBufYSize]. + * + * Note that the mechanism used to transparently fill memory pages when they are + * accessed is the same (but in a controlled way) than what occurs when a memory + * error occurs in a program. Debugging software will generally interrupt program + * execution when that happens. If needed, CPLVirtualMemPin() can be used to avoid + * that by ensuring memory pages are allocated before being accessed. + * + * The size of the region that can be mapped as a virtual memory object depends + * on hardware and operating system limitations. + * On Linux AMD64 platforms, the maximum value is 128 TB. + * On Linux x86 platforms, the maximum value is 2 GB. + * + * Data type translation is automatically done if the data type + * (eBufType) of the buffer is different than + * that of the GDALRasterBand. + * + * Image decimation / replication is currently not supported, i.e. if the + * size of the region being accessed (nXSize x nYSize) is different from the + * buffer size (nBufXSize x nBufYSize). + * + * The nPixelSpace, nLineSpace and nBandSpace parameters allow reading into or + * writing from various organization of buffers. Arbitrary values for the spacing + * parameters are not supported. Those values must be multiple of the size of the + * buffer data type, and must be either band sequential organization (typically + * nPixelSpace = GDALGetDataTypeSize(eBufType) / 8, nLineSpace = nPixelSpace * nBufXSize, + * nBandSpace = nLineSpace * nBufYSize), or pixel-interleaved organization + * (typically nPixelSpace = nBandSpace * nBandCount, nLineSpace = nPixelSpace * nBufXSize, + * nBandSpace = GDALGetDataTypeSize(eBufType) / 8) + * + * @param hDS Dataset object + * + * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nBandCount the number of bands being read or written. + * + * @param panBandMap the list of nBandCount band numbers being read/written. + * Note band numbers are 1 based. This may be NULL to select the first + * nBandCount bands. + * + * @param nPixelSpace The byte offset from the start of one pixel value in + * the buffer to the start of the next pixel value within a scanline. If defaulted + * (0) the size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset from the start of one scanline in + * the buffer to the start of the next. If defaulted (0) the size of the datatype + * eBufType * nBufXSize is used. + * + * @param nBandSpace the byte offset from the start of one bands data to the + * start of the next. If defaulted (0) the value will be + * nLineSpace * nBufYSize implying band sequential organization + * of the data buffer. + * + * @param nCacheSize size in bytes of the maximum memory that will be really + * allocated (must ideally fit into RAM) + * + * @param nPageSizeHint hint for the page size. Must be a multiple of the + * system page size, returned by CPLGetPageSize(). + * Minimum value is generally 4096. Might be set to 0 to + * let the function determine a default page size. + * + * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads + * that will access the virtual memory mapping. This can + * optimize performance a bit. If set to FALSE, + * CPLVirtualMemDeclareThread() must be called. + * + * @param papszOptions NULL terminated list of options. Unused for now. + * + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ + +CPLVirtualMem* GDALDatasetGetVirtualMem( GDALDatasetH hDS, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + int nPixelSpace, + GIntBig nLineSpace, + GIntBig nBandSpace, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + char **papszOptions ) +{ + return GDALGetVirtualMem( hDS, NULL, eRWFlag, nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, nBandSpace, + nCacheSize, nPageSizeHint, bSingleThreadUsage, + papszOptions ); +} + +/************************************************************************/ +/* GDALRasterBandGetVirtualMem() */ +/************************************************************************/ + +/** Create a CPLVirtualMem object from a GDAL raster band object. + * + * Only supported on Linux for now. + * + * This method allows creating a virtual memory object for a region of a + * GDALRasterBand. The content of the virtual + * memory object is automatically filled from dataset content when a virtual + * memory page is first accessed, and it is released (or flushed in case of a + * "dirty" page) when the cache size limit has been reached. + * + * The pointer to access the virtual memory object is obtained with + * CPLVirtualMemGetAddr(). It remains valid until CPLVirtualMemFree() is called. + * CPLVirtualMemFree() must be called before the raster band object is destroyed. + * + * If p is such a pointer and base_type the C type matching eBufType, for default + * values of spacing parameters, the element of image coordinates (x, y) + * (relative to xOff, yOff) can be accessed with + * ((base_type*)p)[x + y * nBufXSize]. + * + * Note that the mechanism used to transparently fill memory pages when they are + * accessed is the same (but in a controlled way) than what occurs when a memory + * error occurs in a program. Debugging software will generally interrupt program + * execution when that happens. If needed, CPLVirtualMemPin() can be used to avoid + * that by ensuring memory pages are allocated before being accessed. + * + * The size of the region that can be mapped as a virtual memory object depends + * on hardware and operating system limitations. + * On Linux AMD64 platforms, the maximum value is 128 TB. + * On Linux x86 platforms, the maximum value is 2 GB. + * + * Data type translation is automatically done if the data type + * (eBufType) of the buffer is different than + * that of the GDALRasterBand. + * + * Image decimation / replication is currently not supported, i.e. if the + * size of the region being accessed (nXSize x nYSize) is different from the + * buffer size (nBufXSize x nBufYSize). + * + * The nPixelSpace and nLineSpace parameters allow reading into or + * writing from various organization of buffers. Arbitrary values for the spacing + * parameters are not supported. Those values must be multiple of the size of the + * buffer data type and must be such that nLineSpace >= nPixelSpace * nBufXSize. + * + * @param hBand Rasterband object + * + * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param nBufXSize the width of the buffer image into which the desired region + * is to be read, or from which it is to be written. + * + * @param nBufYSize the height of the buffer image into which the desired + * region is to be read, or from which it is to be written. + * + * @param eBufType the type of the pixel values in the data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nPixelSpace The byte offset from the start of one pixel value in + * the buffer to the start of the next pixel value within a scanline. If defaulted + * (0) the size of the datatype eBufType is used. + * + * @param nLineSpace The byte offset from the start of one scanline in + * the buffer to the start of the next. If defaulted (0) the size of the datatype + * eBufType * nBufXSize is used. + * + * @param nCacheSize size in bytes of the maximum memory that will be really + * allocated (must ideally fit into RAM) + * + * @param nPageSizeHint hint for the page size. Must be a multiple of the + * system page size, returned by CPLGetPageSize(). + * Minimum value is generally 4096. Might be set to 0 to + * let the function determine a default page size. + * + * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads + * that will access the virtual memory mapping. This can + * optimize performance a bit. If set to FALSE, + * CPLVirtualMemDeclareThread() must be called. + * + * @param papszOptions NULL terminated list of options. Unused for now. + * + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ + +CPLVirtualMem* GDALRasterBandGetVirtualMem( GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nPixelSpace, + GIntBig nLineSpace, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + char **papszOptions ) +{ + return GDALGetVirtualMem( NULL, hBand, eRWFlag, nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, eBufType, + 1, NULL, + nPixelSpace, nLineSpace, 0, + nCacheSize, nPageSizeHint, bSingleThreadUsage, + papszOptions ); +} + +/************************************************************************/ +/* GDALTiledVirtualMem */ +/************************************************************************/ + +class GDALTiledVirtualMem +{ + GDALDatasetH hDS; + GDALRasterBandH hBand; + int nXOff; + int nYOff; + int nXSize; + int nYSize; + int nTileXSize; + int nTileYSize; + GDALDataType eBufType; + int nBandCount; + int* panBandMap; + GDALTileOrganization eTileOrganization; + + void DoIO( GDALRWFlag eRWFlag, size_t nOffset, + void* pPage, size_t nBytes ) const; + +public: + GDALTiledVirtualMem( GDALDatasetH hDS, + GDALRasterBandH hBand, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + int nBandCount, const int* panBandMapIn, + GDALTileOrganization eTileOrganization ); + ~GDALTiledVirtualMem(); + + static void FillCache(CPLVirtualMem* ctxt, size_t nOffset, + void* pPageToFill, + size_t nPageSize, void* pUserData); + static void SaveFromCache(CPLVirtualMem* ctxt, size_t nOffset, + const void* pPageToBeEvicted, + size_t nToEvicted, void* pUserData); + + static void Destroy(void* pUserData); +}; + +/************************************************************************/ +/* GDALTiledVirtualMem() */ +/************************************************************************/ + +GDALTiledVirtualMem::GDALTiledVirtualMem( GDALDatasetH hDS, + GDALRasterBandH hBand, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + int nBandCount, const int* panBandMapIn, + GDALTileOrganization eTileOrganization ): + hDS(hDS), hBand(hBand), nXOff(nXOff), nYOff(nYOff), nXSize(nXSize), nYSize(nYSize), + nTileXSize(nTileXSize), nTileYSize(nTileYSize), eBufType(eBufType), + nBandCount(nBandCount), eTileOrganization(eTileOrganization) +{ + if( hDS != NULL ) + { + if( panBandMapIn ) + { + panBandMap = (int*) CPLMalloc(nBandCount * sizeof(int)); + memcpy(panBandMap, panBandMapIn, nBandCount * sizeof(int)); + } + else + { + panBandMap = (int*) CPLMalloc(nBandCount * sizeof(int)); + for(int i=0;iDoIO(GF_Read, nOffset, pPageToFill, nToFill); +} + +/************************************************************************/ +/* SaveFromCache() */ +/************************************************************************/ + +void GDALTiledVirtualMem::SaveFromCache(CPLVirtualMem* ctxt, size_t nOffset, + const void* pPageToBeEvicted, + size_t nToEvicted, void* pUserData) +{ + const GDALTiledVirtualMem* psParms = (const GDALTiledVirtualMem* )pUserData; + (void)ctxt; + psParms->DoIO(GF_Write, nOffset, (void*)pPageToBeEvicted, nToEvicted); +} + +/************************************************************************/ +/* Destroy() */ +/************************************************************************/ + +void GDALTiledVirtualMem::Destroy(void* pUserData) +{ + GDALTiledVirtualMem* psParams = (GDALTiledVirtualMem*) pUserData; + delete psParams; +} + +/************************************************************************/ +/* GDALGetTiledVirtualMem() */ +/************************************************************************/ + +static CPLVirtualMem* GDALGetTiledVirtualMem( GDALDatasetH hDS, + GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + GDALTileOrganization eTileOrganization, + size_t nCacheSize, + int bSingleThreadUsage, + char **papszOptions ) +{ + CPLVirtualMem* view; + GDALTiledVirtualMem* psParams; + (void) papszOptions; + + size_t nPageSize = CPLGetPageSize(); + if( nPageSize == 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GDALGetTiledVirtualMem() unsupported on this operating system / configuration"); + return NULL; + } + + int nRasterXSize = (hDS) ? GDALGetRasterXSize(hDS) : GDALGetRasterBandXSize(hBand); + int nRasterYSize = (hDS) ? GDALGetRasterYSize(hDS) : GDALGetRasterBandYSize(hBand); + + if( nXOff < 0 || nYOff < 0 || + nTileXSize <= 0 || nTileYSize <= 0 || + nXOff + nXSize > nRasterXSize || + nYOff + nYSize > nRasterYSize ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid window request"); + return NULL; + } + + if( hDS != NULL && !GDALCheckBandParameters(hDS, nBandCount, panBandMap ) ) + return NULL; + + int nDataTypeSize = GDALGetDataTypeSize(eBufType) / 8; + int nTilesPerRow = (nXSize + nTileXSize - 1) / nTileXSize; + int nTilesPerCol = (nYSize + nTileYSize - 1) / nTileYSize; + GUIntBig nReqMem = (GUIntBig)nTilesPerRow * nTilesPerCol * + nTileXSize * nTileYSize * nBandCount * nDataTypeSize; + if( nReqMem != (GUIntBig)(size_t)nReqMem ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot reserve " CPL_FRMT_GUIB " bytes", nReqMem); + return NULL; + } + + size_t nPageSizeHint = nTileXSize * nTileYSize * nDataTypeSize; + if( eTileOrganization != GTO_BSQ ) + nPageSizeHint *= nBandCount; + if( (nPageSizeHint % nPageSize) != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Tile dimensions incompatible with page size"); + return NULL; + } + + psParams = new GDALTiledVirtualMem(hDS, hBand, nXOff, nYOff, + nXSize, nYSize, + nTileXSize, nTileYSize, + eBufType, + nBandCount, panBandMap, + eTileOrganization); + + view = CPLVirtualMemNew((size_t)nReqMem, + nCacheSize, + nPageSizeHint, + bSingleThreadUsage, + (eRWFlag == GF_Read) ? VIRTUALMEM_READONLY_ENFORCED : VIRTUALMEM_READWRITE, + GDALTiledVirtualMem::FillCache, + GDALTiledVirtualMem::SaveFromCache, + GDALTiledVirtualMem::Destroy, + psParams); + + if( view == NULL ) + { + delete psParams; + } + else if( CPLVirtualMemGetPageSize(view) != nPageSizeHint ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Did not get expected page size : %d vs %d", + (int)CPLVirtualMemGetPageSize(view), (int)nPageSizeHint); + CPLVirtualMemFree(view); + return NULL; + } + + return view; +} + +/************************************************************************/ +/* GDALDatasetGetTiledVirtualMem() */ +/************************************************************************/ + +/** Create a CPLVirtualMem object from a GDAL dataset object, with tiling + * organization + * + * Only supported on Linux for now. + * + * This method allows creating a virtual memory object for a region of one + * or more GDALRasterBands from this dataset. The content of the virtual + * memory object is automatically filled from dataset content when a virtual + * memory page is first accessed, and it is released (or flushed in case of a + * "dirty" page) when the cache size limit has been reached. + * + * Contrary to GDALDatasetGetVirtualMem(), pixels will be organized by tiles + * instead of scanlines. Different ways of organizing pixel within/accross tiles + * can be selected with the eTileOrganization parameter. + * + * If nXSize is not a multiple of nTileXSize or nYSize is not a multiple of + * nTileYSize, partial tiles will exists at the right and/or bottom of the region + * of interest. Those partial tiles will also have nTileXSize * nTileYSize dimension, + * with padding pixels. + * + * The pointer to access the virtual memory object is obtained with + * CPLVirtualMemGetAddr(). It remains valid until CPLVirtualMemFree() is called. + * CPLVirtualMemFree() must be called before the dataset object is destroyed. + * + * If p is such a pointer and base_type the C type matching eBufType, for default + * values of spacing parameters, the element of image coordinates (x, y) + * (relative to xOff, yOff) for band b can be accessed with : + * - for eTileOrganization = GTO_TIP, ((base_type*)p)[tile_number(x,y)*nBandCount*tile_size + offset_in_tile(x,y)*nBandCount + (b-1)]. + * - for eTileOrganization = GTO_BIT, ((base_type*)p)[(tile_number(x,y)*nBandCount + (b-1)) * tile_size + offset_in_tile(x,y)]. + * - for eTileOrganization = GTO_BSQ, ((base_type*)p)[(tile_number(x,y) + (b-1)*nTilesCount) * tile_size + offset_in_tile(x,y)]. + * + * where nTilesPerRow = ceil(nXSize / nTileXSize) + * nTilesPerCol = ceil(nYSize / nTileYSize) + * nTilesCount = nTilesPerRow * nTilesPerCol + * tile_number(x,y) = (y / nTileYSize) * nTilesPerRow + (x / nTileXSize) + * offset_in_tile(x,y) = (y % nTileYSize) * nTileXSize + (x % nTileXSize) + * tile_size = nTileXSize * nTileYSize + * + * Note that for a single band request, all tile organizations are equivalent. + * + * Note that the mechanism used to transparently fill memory pages when they are + * accessed is the same (but in a controlled way) than what occurs when a memory + * error occurs in a program. Debugging software will generally interrupt program + * execution when that happens. If needed, CPLVirtualMemPin() can be used to avoid + * that by ensuring memory pages are allocated before being accessed. + * + * The size of the region that can be mapped as a virtual memory object depends + * on hardware and operating system limitations. + * On Linux AMD64 platforms, the maximum value is 128 TB. + * On Linux x86 platforms, the maximum value is 2 GB. + * + * Data type translation is automatically done if the data type + * (eBufType) of the buffer is different than + * that of the GDALRasterBand. + * + * @param hDS Dataset object + * + * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param nTileXSize the width of the tiles. + * + * @param nTileYSize the height of the tiles. + * + * @param eBufType the type of the pixel values in the data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nBandCount the number of bands being read or written. + * + * @param panBandMap the list of nBandCount band numbers being read/written. + * Note band numbers are 1 based. This may be NULL to select the first + * nBandCount bands. + * + * @param eTileOrganization tile organization. + * + * @param nCacheSize size in bytes of the maximum memory that will be really + * allocated (must ideally fit into RAM) + * + * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads + * that will access the virtual memory mapping. This can + * optimize performance a bit. If set to FALSE, + * CPLVirtualMemDeclareThread() must be called. + * + * @param papszOptions NULL terminated list of options. Unused for now. + * + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ + +CPLVirtualMem* GDALDatasetGetTiledVirtualMem( GDALDatasetH hDS, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + int nBandCount, int* panBandMap, + GDALTileOrganization eTileOrganization, + size_t nCacheSize, + int bSingleThreadUsage, + char **papszOptions ) +{ + return GDALGetTiledVirtualMem( hDS, NULL, eRWFlag, nXOff, nYOff, + nXSize, nYSize, nTileXSize, nTileYSize, + eBufType, nBandCount, panBandMap, + eTileOrganization, + nCacheSize, bSingleThreadUsage, papszOptions ); +} + +/************************************************************************/ +/* GDALRasterBandGetTiledVirtualMem() */ +/************************************************************************/ + +/** Create a CPLVirtualMem object from a GDAL rasterband object, with tiling + * organization + * + * Only supported on Linux for now. + * + * This method allows creating a virtual memory object for a region of one + * GDALRasterBand. The content of the virtual + * memory object is automatically filled from dataset content when a virtual + * memory page is first accessed, and it is released (or flushed in case of a + * "dirty" page) when the cache size limit has been reached. + * + * Contrary to GDALDatasetGetVirtualMem(), pixels will be organized by tiles + * instead of scanlines. + * + * If nXSize is not a multiple of nTileXSize or nYSize is not a multiple of + * nTileYSize, partial tiles will exists at the right and/or bottom of the region + * of interest. Those partial tiles will also have nTileXSize * nTileYSize dimension, + * with padding pixels. + * + * The pointer to access the virtual memory object is obtained with + * CPLVirtualMemGetAddr(). It remains valid until CPLVirtualMemFree() is called. + * CPLVirtualMemFree() must be called before the raster band object is destroyed. + * + * If p is such a pointer and base_type the C type matching eBufType, for default + * values of spacing parameters, the element of image coordinates (x, y) + * (relative to xOff, yOff) can be accessed with : + * ((base_type*)p)[tile_number(x,y)*tile_size + offset_in_tile(x,y)]. + * + * where nTilesPerRow = ceil(nXSize / nTileXSize) + * nTilesCount = nTilesPerRow * nTilesPerCol + * tile_number(x,y) = (y / nTileYSize) * nTilesPerRow + (x / nTileXSize) + * offset_in_tile(x,y) = (y % nTileYSize) * nTileXSize + (x % nTileXSize) + * tile_size = nTileXSize * nTileYSize + * + * Note that the mechanism used to transparently fill memory pages when they are + * accessed is the same (but in a controlled way) than what occurs when a memory + * error occurs in a program. Debugging software will generally interrupt program + * execution when that happens. If needed, CPLVirtualMemPin() can be used to avoid + * that by ensuring memory pages are allocated before being accessed. + * + * The size of the region that can be mapped as a virtual memory object depends + * on hardware and operating system limitations. + * On Linux AMD64 platforms, the maximum value is 128 TB. + * On Linux x86 platforms, the maximum value is 2 GB. + * + * Data type translation is automatically done if the data type + * (eBufType) of the buffer is different than + * that of the GDALRasterBand. + * + * @param hBand Rasterband object + * + * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to + * write a region of data. + * + * @param nXOff The pixel offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the left side. + * + * @param nYOff The line offset to the top left corner of the region + * of the band to be accessed. This would be zero to start from the top. + * + * @param nXSize The width of the region of the band to be accessed in pixels. + * + * @param nYSize The height of the region of the band to be accessed in lines. + * + * @param nTileXSize the width of the tiles. + * + * @param nTileYSize the height of the tiles. + * + * @param eBufType the type of the pixel values in the data buffer. The + * pixel values will automatically be translated to/from the GDALRasterBand + * data type as needed. + * + * @param nCacheSize size in bytes of the maximum memory that will be really + * allocated (must ideally fit into RAM) + * + * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads + * that will access the virtual memory mapping. This can + * optimize performance a bit. If set to FALSE, + * CPLVirtualMemDeclareThread() must be called. + * + * @param papszOptions NULL terminated list of options. Unused for now. + * + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ + +CPLVirtualMem* GDALRasterBandGetTiledVirtualMem( GDALRasterBandH hBand, + GDALRWFlag eRWFlag, + int nXOff, int nYOff, + int nXSize, int nYSize, + int nTileXSize, int nTileYSize, + GDALDataType eBufType, + size_t nCacheSize, + int bSingleThreadUsage, + char **papszOptions ) +{ + return GDALGetTiledVirtualMem( NULL, hBand, eRWFlag, nXOff, nYOff, + nXSize, nYSize, nTileXSize, nTileYSize, + eBufType, 1, NULL, + GTO_BSQ, + nCacheSize, bSingleThreadUsage, papszOptions ); +} diff --git a/bazaar/plugin/gdal/gcore/jp2dump.cpp b/bazaar/plugin/gdal/gcore/jp2dump.cpp new file mode 100644 index 000000000..d6cdff51c --- /dev/null +++ b/bazaar/plugin/gdal/gcore/jp2dump.cpp @@ -0,0 +1,57 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL + * Purpose: Small test utility for dumping jpeg2000 boxes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdaljp2metadata.h" + +int main( int argc, char ** argv ) + +{ + VSILFILE *fp; + + if( argc < 2 ) + { + printf( "Usage: jp2dump \n" ); + exit( 2 ); + } + + fp = VSIFOpenL( argv[1], "rb" ); + if( fp == NULL ) + { + perror( "open" ); + exit( 1 ); + } + + GDALJP2Box oBox(fp); + + oBox.ReadFirst(); + do + { + oBox.DumpReadable( stdout ); + } while( oBox.ReadNext() ); +} diff --git a/bazaar/plugin/gdal/gcore/makefile.vc b/bazaar/plugin/gdal/gcore/makefile.vc new file mode 100644 index 000000000..a70bb7ba4 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/makefile.vc @@ -0,0 +1,49 @@ + +OBJ = gdalopeninfo.obj gdaldrivermanager.obj gdaldriver.obj \ + gdaldataset.obj gdalrasterband.obj gdal_misc.obj \ + rasterio.obj gdalrasterblock.obj gdal_rat.obj \ + gdalcolortable.obj overview.obj gdaldefaultoverviews.obj \ + gdalmajorobject.obj gdalpamdataset.obj gdalpamrasterband.obj \ + gdaljp2metadata.obj gdaljp2box.obj gdalgmlcoverage.obj \ + gdalmultidomainmetadata.obj gdalpamproxydb.obj \ + gdalallvalidmaskband.obj gdalnodatamaskband.obj \ + gdalproxydataset.obj gdalproxypool.obj \ + gdalnodatavaluesmaskband.obj gdaldefaultasync.obj \ + gdaldllmain.obj gdalexif.obj gdalclientserver.obj \ + gdalgeorefpamdataset.obj gdaljp2abstractdataset.obj \ + gdalvirtualmem.obj gdaloverviewdataset.obj gdalrescaledalphaband.obj \ + gdaljp2structure.obj gdal_mdreader.obj gdaljp2metadatagenerator.obj + +RES = Version.res + +GDAL_ROOT = .. + +!INCLUDE ..\nmake.opt + +EXTRAFLAGS = $(PAM_SETTING) -I..\frmts\gtiff -I..\frmts\mem -I..\frmts\vrt -I..\ogr\ogrsf_frmts\generic -I../ogr/ogrsf_frmts/geojson -I..\ogr\ogrsf_frmts\geojson\libjson $(SQLITEDEF) + +!IFDEF SQLITE_LIB +SQLITEDEF = -DSQLITE_ENABLED +!ENDIF + +!IFDEF LIBXML2_INC +EXTRAFLAGS = $(EXTRAFLAGS) -DHAVE_LIBXML2 $(LIBXML2_INC) +!ENDIF + +default: $(OBJ) $(RES) mdreader_dir + +clean: + -del *.obj *.res + cd mdreader + $(MAKE) /f makefile.vc clean + cd .. + +Version.res: + rc -fo Version.res -r -I..\port -I..\ogr Version.rc + +gdal_misc.obj: gdal_misc.cpp gdal_version.h + +mdreader_dir: + cd mdreader + $(MAKE) /f makefile.vc + cd .. diff --git a/bazaar/plugin/gdal/gcore/mdreader/GNUmakefile b/bazaar/plugin/gdal/gcore/mdreader/GNUmakefile new file mode 100644 index 000000000..0bcd0401a --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/GNUmakefile @@ -0,0 +1,28 @@ + +include ../../GDALmake.opt + +OBJ = reader_digital_globe.o reader_geo_eye.o reader_orb_view.o \ + reader_pleiades.o reader_rdk1.o reader_landsat.o \ + reader_spot.o reader_rapid_eye.o reader_alos.o reader_eros.o \ + reader_kompsat.o + +UP_OBJ = $(addprefix ../,$(OBJ)) + +default: $(UP_OBJ:.o=.$(OBJ_EXT)) + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) + +../%.$(OBJ_EXT): %.c + $(CC) $(GDAL_INCLUDE) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< + +../%.$(OBJ_EXT): %.cpp + $(CXX) $(GDAL_INCLUDE) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< + +%.$(OBJ_EXT): %.c + $(CC) $(GDAL_INCLUDE) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< + +%.$(OBJ_EXT): %.cpp + $(CXX) $(GDAL_INCLUDE) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< + +clean: + $(RM) *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/gcore/mdreader/makefile.vc b/bazaar/plugin/gdal/gcore/mdreader/makefile.vc new file mode 100644 index 000000000..76d69d5b8 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/makefile.vc @@ -0,0 +1,16 @@ +OBJ = reader_digital_globe.obj reader_geo_eye.obj reader_orb_view.obj \ + reader_pleiades.obj reader_rdk1.obj reader_landsat.obj \ + reader_spot.obj reader_rapid_eye.obj reader_alos.obj \ + reader_eros.obj reader_kompsat.obj + +GDAL_ROOT = ..\.. + +!INCLUDE ..\..\nmake.opt + +EXTRAFLAGS = -I..\..\port -I..\..\ogr -I..\ + +default: $(OBJ) + xcopy /D /Y *.obj ..\ + +clean: + -del *.obj diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_alos.cpp b/bazaar/plugin/gdal/gcore/mdreader/reader_alos.cpp new file mode 100644 index 000000000..6456f2b03 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_alos.cpp @@ -0,0 +1,405 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL Core + * Purpose: Read metadata from Alos imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "reader_alos.h" + +/** + * GDALMDReaderALOS() + */ +GDALMDReaderALOS::GDALMDReaderALOS(const char *pszPath, + char **papszSiblingFiles) : GDALMDReaderBase(pszPath, papszSiblingFiles) +{ + CPLString osDirName = CPLGetDirname(pszPath); + CPLString osBaseName = CPLGetBasename(pszPath); + + const char* pszIMDSourceFilename = CPLFormFilename(osDirName, "summary", ".txt"); + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszIMDSourceFilename; + } + else + { + pszIMDSourceFilename = CPLFormFilename( osDirName, "SUMMARY", ".TXT"); + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszIMDSourceFilename; + } + } + + const char *pszHDRFileName; + if( osBaseName.size() >= 6 ) + { + // check if this is separate band or whole image + // test without 6 symbols + pszHDRFileName = CPLFormFilename(osDirName, CPLSPrintf("HDR%s", + osBaseName + 6), "txt"); + if (CPLCheckForFile((char*)pszHDRFileName, papszSiblingFiles)) + { + m_osHDRSourceFilename = pszHDRFileName; + } + else + { + pszHDRFileName = CPLFormFilename(osDirName, CPLSPrintf("HDR%s", + osBaseName + 6), "TXT"); + if (CPLCheckForFile((char*)pszHDRFileName, papszSiblingFiles)) + { + m_osHDRSourceFilename = pszHDRFileName; + } + } + } + + // test without 3 symbols + if( osBaseName.size() >= 3 && m_osHDRSourceFilename.empty()) + { + pszHDRFileName = CPLFormFilename(osDirName, CPLSPrintf("HDR%s", + osBaseName + 3), "txt"); + if (CPLCheckForFile((char*)pszHDRFileName, papszSiblingFiles)) + { + m_osHDRSourceFilename = pszHDRFileName; + } + else + { + pszHDRFileName = CPLFormFilename(osDirName, CPLSPrintf("HDR%s", + osBaseName + 3), "TXT"); + if (CPLCheckForFile((char*)pszHDRFileName, papszSiblingFiles)) + { + m_osHDRSourceFilename = pszHDRFileName; + } + } + } + + // test without 6 symbols + const char *pszRPCFileName; + if( osBaseName.size() >= 6 ) + { + pszRPCFileName = CPLFormFilename(osDirName, CPLSPrintf("RPC%s", + osBaseName + 6), "txt"); + if (CPLCheckForFile((char*)pszRPCFileName, papszSiblingFiles)) + { + m_osRPBSourceFilename = pszRPCFileName; + } + else + { + pszRPCFileName = CPLFormFilename(osDirName, CPLSPrintf("RPC%s", + osBaseName + 6), "TXT"); + if (CPLCheckForFile((char*)pszRPCFileName, papszSiblingFiles)) + { + m_osRPBSourceFilename = pszRPCFileName; + } + } + } + + // test without 3 symbols + if( osBaseName.size() >= 3 && m_osRPBSourceFilename.empty()) + { + pszRPCFileName = CPLFormFilename(osDirName, CPLSPrintf("RPC%s", + osBaseName + 3), "txt"); + if (CPLCheckForFile((char*)pszRPCFileName, papszSiblingFiles)) + { + m_osRPBSourceFilename = pszRPCFileName; + } + else + { + pszRPCFileName = CPLFormFilename(osDirName, CPLSPrintf("RPC%s", + osBaseName + 3), "TXT"); + if (CPLCheckForFile((char*)pszRPCFileName, papszSiblingFiles)) + { + m_osRPBSourceFilename = pszRPCFileName; + } + } + } + + if(m_osIMDSourceFilename.size()) + CPLDebug( "MDReaderALOS", "IMD Filename: %s", + m_osIMDSourceFilename.c_str() ); + if(m_osHDRSourceFilename.size()) + CPLDebug( "MDReaderALOS", "HDR Filename: %s", + m_osHDRSourceFilename.c_str() ); + if(m_osRPBSourceFilename.size()) + CPLDebug( "MDReaderALOS", "RPB Filename: %s", + m_osRPBSourceFilename.c_str() ); +} + +/** + * ~GDALMDReaderALOS() + */ +GDALMDReaderALOS::~GDALMDReaderALOS() +{ +} + +/** + * HasRequiredFiles() + */ +const bool GDALMDReaderALOS::HasRequiredFiles() const +{ + if (!m_osIMDSourceFilename.empty()) + return true; + + if(!m_osHDRSourceFilename.empty() && !m_osRPBSourceFilename.empty()) + return true; + + return false; +} + +/** + * GetMetadataFiles() + */ +char** GDALMDReaderALOS::GetMetadataFiles() const +{ + char **papszFileList = NULL; + if(!m_osIMDSourceFilename.empty()) + papszFileList= CSLAddString( papszFileList, m_osIMDSourceFilename ); + if(!m_osHDRSourceFilename.empty()) + papszFileList= CSLAddString( papszFileList, m_osHDRSourceFilename ); + if(!m_osRPBSourceFilename.empty()) + papszFileList= CSLAddString( papszFileList, m_osRPBSourceFilename ); + + return papszFileList; +} + +/** + * LoadMetadata() + */ +void GDALMDReaderALOS::LoadMetadata() +{ + if(m_bIsMetadataLoad) + return; + + if(!m_osIMDSourceFilename.empty()) + { + m_papszIMDMD = CSLLoad(m_osIMDSourceFilename); + } + + if(!m_osHDRSourceFilename.empty()) + { + if(NULL == m_papszIMDMD) + { + m_papszIMDMD = CSLLoad(m_osHDRSourceFilename); + } + else + { + char** papszHDR = CSLLoad(m_osHDRSourceFilename); + m_papszIMDMD = CSLMerge(m_papszIMDMD, papszHDR); + CSLDestroy(papszHDR); + } + } + + m_papszRPCMD = LoadRPCTxtFile(); + + m_papszDEFAULTMD = CSLAddNameValue(m_papszDEFAULTMD, MD_NAME_MDTYPE, "ALOS"); + + m_bIsMetadataLoad = true; + + const char* pszSatId1 = CSLFetchNameValue(m_papszIMDMD, "Lbi_Satellite"); + const char* pszSatId2 = CSLFetchNameValue(m_papszIMDMD, "Lbi_Sensor"); + if(NULL != pszSatId1 && NULL != pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLSPrintf( "%s %s", + CPLStripQuotes(pszSatId1).c_str(), + CPLStripQuotes(pszSatId2).c_str())); + } + else if(NULL != pszSatId1 && NULL == pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLStripQuotes(pszSatId1)); + } + else if(NULL == pszSatId1 && NULL != pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLStripQuotes(pszSatId2)); + } + + + const char* pszCloudCover = CSLFetchNameValue(m_papszIMDMD, + "Img_CloudQuantityOfAllImage"); + if(NULL != pszCloudCover) + { + int nCC = atoi(pszCloudCover); + if(nCC >= 99) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_CLOUDCOVER, + MD_CLOUDCOVER_NA); + } + else + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_CLOUDCOVER, CPLSPrintf("%d", nCC * 10)); + } + } + + const char* pszDate = CSLFetchNameValue(m_papszIMDMD, + "Img_SceneCenterDateTime"); + + if(NULL != pszDate) + { + char buffer[80]; + time_t timeMid = GetAcquisitionTimeFromString(CPLStripQuotes(pszDate)); + strftime (buffer, 80, MD_DATETIMEFORMAT, localtime(&timeMid)); + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, buffer); + } + else + { + pszDate = CSLFetchNameValue(m_papszIMDMD, "Lbi_ObservationDate"); + if(NULL != pszDate) + { + const char* pszTime = "00:00:00.000"; + + char buffer[80]; + time_t timeMid = GetAcquisitionTimeFromString(CPLSPrintf( "%s %s", + CPLStripQuotes(pszDate).c_str(), + CPLStripQuotes(pszTime).c_str())); + strftime (buffer, 80, MD_DATETIMEFORMAT, localtime(&timeMid)); + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, buffer); + } + } +} + +static const char *apszRPCTXT20ValItems[] = +{ + RPC_LINE_NUM_COEFF, + RPC_LINE_DEN_COEFF, + RPC_SAMP_NUM_COEFF, + RPC_SAMP_DEN_COEFF, + NULL +}; + +/** + * LoadRPCTxtFile + */ +char** GDALMDReaderALOS::LoadRPCTxtFile() +{ + if(m_osRPBSourceFilename.empty()) + return NULL; + + char** papszLines = CSLLoad(m_osRPBSourceFilename); + if(NULL == papszLines) + return NULL; + + const char* pszFirstRow = papszLines[0]; + char** papszRPB = NULL; + if(NULL != pszFirstRow) + { + char buff[50] = {0}; + int nOffset = 0; + CPLStrlcpy(buff, pszFirstRow + nOffset, 7); + nOffset += 6; + papszRPB = CSLAddNameValue(papszRPB, RPC_LINE_OFF, buff); + + CPLStrlcpy(buff, pszFirstRow + nOffset, 6); + nOffset += 5; + papszRPB = CSLAddNameValue(papszRPB, RPC_SAMP_OFF, buff); + + CPLStrlcpy(buff, pszFirstRow + nOffset, 9); + nOffset += 8; + papszRPB = CSLAddNameValue(papszRPB, RPC_LAT_OFF, buff); + + CPLStrlcpy(buff, pszFirstRow + nOffset, 10); + nOffset += 9; + papszRPB = CSLAddNameValue(papszRPB, RPC_LONG_OFF, buff); + + CPLStrlcpy(buff, pszFirstRow + nOffset, 6); + nOffset += 5; + papszRPB = CSLAddNameValue(papszRPB, RPC_HEIGHT_OFF, buff); + + CPLStrlcpy(buff, pszFirstRow + nOffset, 7); + nOffset += 6; + papszRPB = CSLAddNameValue(papszRPB, RPC_LINE_SCALE, buff); + + CPLStrlcpy(buff, pszFirstRow + nOffset, 6); + nOffset += 5; + papszRPB = CSLAddNameValue(papszRPB, RPC_SAMP_SCALE, buff); + + CPLStrlcpy(buff, pszFirstRow + nOffset, 9); + nOffset += 8; + papszRPB = CSLAddNameValue(papszRPB, RPC_LAT_SCALE, buff); + + CPLStrlcpy(buff, pszFirstRow + nOffset, 10); + nOffset += 9; + papszRPB = CSLAddNameValue(papszRPB, RPC_LONG_SCALE, buff); + + CPLStrlcpy(buff, pszFirstRow + nOffset, 6); + nOffset += 5; + papszRPB = CSLAddNameValue(papszRPB, RPC_HEIGHT_SCALE, buff); + + int i, j; + for( i = 0; apszRPCTXT20ValItems[i] != NULL; i++ ) + { + CPLString value; + for( j = 1; j < 21; j++ ) + { + CPLStrlcpy(buff, pszFirstRow + nOffset, 13); + nOffset += 12; + + value = value + " " + CPLString(buff); + } + papszRPB = CSLAddNameValue(papszRPB, apszRPCTXT20ValItems[i], value); + } + } + CSLDestroy(papszLines); + + return papszRPB; +} + +/** + * GetAcqisitionTimeFromString() + */ +const time_t GDALMDReaderALOS::GetAcquisitionTimeFromString( + const char* pszDateTime) +{ + if(NULL == pszDateTime) + return 0; + + int iYear; + int iMonth; + int iDay; + int iHours; + int iMin; + int iSec; + + int r = sscanf ( pszDateTime, "%4d%2d%2d %d:%d:%d.%*d", + &iYear, &iMonth, &iDay, &iHours, &iMin, &iSec); + + if (r != 6) + return 0; + + struct tm tmDateTime; + tmDateTime.tm_sec = iSec; + tmDateTime.tm_min = iMin; + tmDateTime.tm_hour = iHours; + tmDateTime.tm_mday = iDay; + tmDateTime.tm_mon = iMonth - 1; + tmDateTime.tm_year = iYear - 1900; + tmDateTime.tm_isdst = -1; + + return mktime(&tmDateTime); +} diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_alos.h b/bazaar/plugin/gdal/gcore/mdreader/reader_alos.h new file mode 100644 index 000000000..4eefb51dc --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_alos.h @@ -0,0 +1,69 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL Core + * Purpose: Read metadata from Alos imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef READER_ALOS_H_INCLUDED +#define READER_ALOS_H_INCLUDED + +#include "reader_pleiades.h" + +/** +Metadata reader for ALOS + +TIFF filename: IMG-sssssssssssssss-pppppppp.tif or + IMG-01-sssssssssssssss-pppppppp.tif + IMG-02-sssssssssssssss-pppppppp.tif +Metadata filename: summary.txt +RPC filename: RPC-sssssssssssssss-pppppppp.txt + +Common metadata (from metadata filename): + AcquisitionDateTime: Img_SceneCenterDateTime or Lbi_ObservationDate + SatelliteId: Lbi_Satellite + CloudCover: Img_CloudQuantityOfAllImage +*/ + +class GDALMDReaderALOS: public GDALMDReaderBase +{ +public: + GDALMDReaderALOS(const char *pszPath, char **papszSiblingFiles); + virtual ~GDALMDReaderALOS(); + virtual const bool HasRequiredFiles() const; + virtual char** GetMetadataFiles() const; +protected: + virtual void LoadMetadata(); + char** LoadRPCTxtFile(); + virtual const time_t GetAcquisitionTimeFromString(const char* pszDateTime); +protected: + CPLString m_osIMDSourceFilename; + CPLString m_osHDRSourceFilename; + CPLString m_osRPBSourceFilename; +}; + +#endif // READER_ALOS_H_INCLUDED + diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_digital_globe.cpp b/bazaar/plugin/gdal/gcore/mdreader/reader_digital_globe.cpp new file mode 100644 index 000000000..4d3cfab0e --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_digital_globe.cpp @@ -0,0 +1,280 @@ +/****************************************************************************** + * $Id: reader_digital_globe.cpp 29145 2015-05-04 10:00:40Z rouault $ + * + * Project: GDAL Core + * Purpose: Read metadata from DigitalGlobe imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015, NextGIS info@nextgis.ru + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "reader_digital_globe.h" + +CPL_CVSID("$Id: reader_digital_globe.cpp 29145 2015-05-04 10:00:40Z rouault $"); + +/** + * GDALMDReaderDigitalGlobe() + */ +GDALMDReaderDigitalGlobe::GDALMDReaderDigitalGlobe(const char *pszPath, + char **papszSiblingFiles) : GDALMDReaderBase(pszPath, papszSiblingFiles) +{ + m_osIMDSourceFilename = GDALFindAssociatedFile( pszPath, "IMD", + papszSiblingFiles, 0 ); + m_osRPBSourceFilename = GDALFindAssociatedFile( pszPath, "RPB", + papszSiblingFiles, 0 ); + m_osXMLSourceFilename = GDALFindAssociatedFile( pszPath, "XML", + papszSiblingFiles, 0 ); + + if( m_osIMDSourceFilename.size() ) + CPLDebug( "MDReaderDigitalGlobe", "IMD Filename: %s", + m_osIMDSourceFilename.c_str() ); + if( m_osRPBSourceFilename.size() ) + CPLDebug( "MDReaderDigitalGlobe", "RPB Filename: %s", + m_osRPBSourceFilename.c_str() ); + if( m_osXMLSourceFilename.size() ) + CPLDebug( "MDReaderDigitalGlobe", "XML Filename: %s", + m_osXMLSourceFilename.c_str() ); +} + +/** + * ~GDALMDReaderDigitalGlobe() + */ +GDALMDReaderDigitalGlobe::~GDALMDReaderDigitalGlobe() +{ + +} + +/** + * HasRequiredFiles() + */ +const bool GDALMDReaderDigitalGlobe::HasRequiredFiles() const +{ + if (!m_osIMDSourceFilename.empty()) + return true; + if (!m_osRPBSourceFilename.empty()) + return true; + + // check + if(!m_osXMLSourceFilename.empty() && + GDALCheckFileHeader(m_osXMLSourceFilename, "")) + return true; + + return false; +} + +/** + * LoadMetadata() + */ +void GDALMDReaderDigitalGlobe::LoadMetadata() +{ + if(m_bIsMetadataLoad) + return; + + if (!m_osIMDSourceFilename.empty()) + { + m_papszIMDMD = GDALLoadIMDFile( m_osIMDSourceFilename ); + } + + if(!m_osRPBSourceFilename.empty()) + { + m_papszRPCMD = GDALLoadRPBFile( m_osRPBSourceFilename ); + } + + if((NULL == m_papszIMDMD || NULL == m_papszRPCMD) && !m_osXMLSourceFilename.empty()) + { + CPLXMLNode* psNode = CPLParseXMLFile(m_osXMLSourceFilename); + + if(psNode != NULL) + { + CPLXMLNode* psisdNode = psNode->psNext; + if(psisdNode != NULL) + { + if( m_papszIMDMD == NULL ) + m_papszIMDMD = LoadIMDXmlNode( CPLSearchXMLNode(psisdNode, + "IMD") ); + if( m_papszRPCMD == NULL ) + m_papszRPCMD = LoadRPBXmlNode( CPLSearchXMLNode(psisdNode, + "RPB") ); + } + CPLDestroyXMLNode(psNode); + } + } + + m_papszDEFAULTMD = CSLAddNameValue(m_papszDEFAULTMD, MD_NAME_MDTYPE, "DG"); + + m_bIsMetadataLoad = true; + + if(NULL == m_papszIMDMD) + { + return; + } + //extract imagery metadata + const char* pszSatId = CSLFetchNameValue(m_papszIMDMD, "IMAGE.SATID"); + if(NULL != pszSatId) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, + CPLStripQuotes(pszSatId)); + } + else + { + pszSatId = CSLFetchNameValue(m_papszIMDMD, "IMAGE_1.SATID"); + if(NULL != pszSatId) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, + CPLStripQuotes(pszSatId)); + } + } + + const char* pszCloudCover = CSLFetchNameValue(m_papszIMDMD, + "IMAGE.CLOUDCOVER"); + if(NULL != pszCloudCover) + { + double fCC = CPLAtofM(pszCloudCover); + if(fCC < 0) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_CLOUDCOVER, + MD_CLOUDCOVER_NA); + } + else + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_CLOUDCOVER, CPLSPrintf("%d", int(fCC * 100))); + } + } + else + { + pszCloudCover = CSLFetchNameValue(m_papszIMDMD, "IMAGE_1.cloudCover"); + if(NULL != pszCloudCover) + { + double fCC = CPLAtofM(pszCloudCover); + if(fCC < 0) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_CLOUDCOVER, + MD_CLOUDCOVER_NA); + } + else + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_CLOUDCOVER, CPLSPrintf("%d", int(fCC * 100))); + } + } + } + + const char* pszDateTime = CSLFetchNameValue(m_papszIMDMD, + "IMAGE.FIRSTLINETIME"); + if(NULL != pszDateTime) + { + time_t timeStart = GetAcquisitionTimeFromString(pszDateTime); + char szMidDateTime[80]; + strftime (szMidDateTime, 80, MD_DATETIMEFORMAT, localtime(&timeStart)); + + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, + szMidDateTime); + } + else + { + pszDateTime = CSLFetchNameValue(m_papszIMDMD, "IMAGE_1.firstLineTime"); + if(NULL != pszDateTime) + { + time_t timeStart = GetAcquisitionTimeFromString(pszDateTime); + char szMidDateTime[80]; + strftime (szMidDateTime, 80, MD_DATETIMEFORMAT, localtime(&timeStart)); + + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, + szMidDateTime); + } + } +} + +/** + * GetMetadataFiles() + */ +char** GDALMDReaderDigitalGlobe::GetMetadataFiles() const +{ + char **papszFileList = NULL; + if(!m_osIMDSourceFilename.empty()) + papszFileList = CSLAddString( papszFileList, m_osIMDSourceFilename ); + if(!m_osRPBSourceFilename.empty()) + papszFileList = CSLAddString( papszFileList, m_osRPBSourceFilename ); + if(!m_osXMLSourceFilename.empty()) + papszFileList = CSLAddString( papszFileList, m_osXMLSourceFilename ); + + return papszFileList; +} + +/** + * GDALLoadIMDXmlNode() + */ +char** GDALMDReaderDigitalGlobe::LoadIMDXmlNode(CPLXMLNode* psNode) +{ + if(NULL == psNode) + return NULL; + char** papszList = NULL; + return ReadXMLToList(psNode->psChild, papszList); +} + +/** + * GDALLoadRPBXmlNode() + */ +static const char *apszRPBMap[] = { + RPC_LINE_OFF, "image.lineOffset", + RPC_SAMP_OFF, "image.sampOffset", + RPC_LAT_OFF, "image.latOffset", + RPC_LONG_OFF, "image.longOffset", + RPC_HEIGHT_OFF, "image.heightOffset", + RPC_LINE_SCALE, "image.lineScale", + RPC_SAMP_SCALE, "image.sampScale", + RPC_LAT_SCALE, "image.latScale", + RPC_LONG_SCALE, "image.longScale", + RPC_HEIGHT_SCALE, "image.heightScale", + RPC_LINE_NUM_COEFF, "image.lineNumCoefList.lineNumCoef", + RPC_LINE_DEN_COEFF, "image.lineDenCoefList.lineDenCoef", + RPC_SAMP_NUM_COEFF, "image.sampNumCoefList.sampNumCoef", + RPC_SAMP_DEN_COEFF, "image.sampDenCoefList.sampDenCoef", + NULL, NULL }; + +char** GDALMDReaderDigitalGlobe::LoadRPBXmlNode(CPLXMLNode* psNode) +{ + if(NULL == psNode) + return NULL; + char** papszList = NULL; + papszList = ReadXMLToList(psNode->psChild, papszList); + + if(NULL == papszList) + return NULL; + + char** papszRPB = NULL; + for( int i = 0; apszRPBMap[i] != NULL; i += 2 ) + { + papszRPB = CSLAddNameValue(papszRPB, apszRPBMap[i], + CSLFetchNameValue(papszList, apszRPBMap[i + 1])); + } + + CSLDestroy(papszList); + + return papszRPB; +} diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_digital_globe.h b/bazaar/plugin/gdal/gcore/mdreader/reader_digital_globe.h new file mode 100644 index 000000000..3cdd6b053 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_digital_globe.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * $Id: reader_digital_globe.h 29190 2015-05-13 21:40:30Z bishop $ + * + * Project: GDAL Core + * Purpose: Read metadata from DigitalGlobe imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015, NextGIS info@nextgis.ru + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef READER_DIGITAL_GLOBE_H_INCLUDED +#define READER_DIGITAL_GLOBE_H_INCLUDED + +#include "../gdal_mdreader.h" + +/** +@brief Metadata reader for DigitalGlobe + +TIFF filename: aaaaaaaaaa.tif +Metadata filename: aaaaaaaaaa.IMD +RPC filename: aaaaaaaaaa.RPB + +Common metadata (from metadata filename): + SatelliteId: satId + CloudCover: cloudCover + AcquisitionDateTime: earliestAcqTime, latestAcqTime + +OR +Metadata and RPC filename: aaaaaaaaaa.XML +Common metadata (from metadata filename): + SatelliteId: SATID + CloudCover: CLOUDCOVER + AcquisitionDateTime: EARLIESTACQTIME, LATESTACQTIME + +*/ + +class GDALMDReaderDigitalGlobe: public GDALMDReaderBase +{ +public: + GDALMDReaderDigitalGlobe(const char *pszPath, char **papszSiblingFiles); + virtual ~GDALMDReaderDigitalGlobe(); + virtual const bool HasRequiredFiles() const; + virtual char** GetMetadataFiles() const; +protected: + virtual void LoadMetadata(); + char** LoadRPBXmlNode(CPLXMLNode* psNode); + char** LoadIMDXmlNode(CPLXMLNode* psNode); +protected: + CPLString m_osXMLSourceFilename; + CPLString m_osIMDSourceFilename; + CPLString m_osRPBSourceFilename; +}; + +#endif //READER_DIGITAL_GLOBE_H_INCLUDED diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_eros.cpp b/bazaar/plugin/gdal/gcore/mdreader/reader_eros.cpp new file mode 100644 index 000000000..af7680acf --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_eros.cpp @@ -0,0 +1,286 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL Core + * Purpose: Read metadata from EROS imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015, NextGIS info@nextgis.ru + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "reader_eros.h" + +/** + * GDALMDReaderEROS() + */ +GDALMDReaderEROS::GDALMDReaderEROS(const char *pszPath, + char **papszSiblingFiles) : GDALMDReaderBase(pszPath, papszSiblingFiles) +{ + CPLString osBaseName = CPLGetBasename(pszPath); + CPLString osDirName = CPLGetDirname(pszPath); + char szMetadataName[512] = {0}; + const char* pszPassFileName; + size_t i; + if( osBaseName.size() > 511 ) + return; + for(i = 0; i < osBaseName.size(); i++) + { + if(EQUALN(osBaseName + i, ".", 1)) + { + pszPassFileName = CPLFormFilename( osDirName, szMetadataName, + "pass" ); + if (CPLCheckForFile((char*)pszPassFileName, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszPassFileName; + break; + } + else + { + pszPassFileName = CPLFormFilename( osDirName, szMetadataName, + "PASS" ); + if (CPLCheckForFile((char*)pszPassFileName, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszPassFileName; + break; + } + } + } + szMetadataName[i] = osBaseName[i]; + } + + if(m_osIMDSourceFilename.empty()) + { + pszPassFileName = CPLFormFilename( osDirName, szMetadataName, "pass" ); + if (CPLCheckForFile((char*)pszPassFileName, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszPassFileName; + } + else + { + pszPassFileName = CPLFormFilename( osDirName, szMetadataName, "PASS" ); + if (CPLCheckForFile((char*)pszPassFileName, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszPassFileName; + } + } + } + + const char* pszRPCFileName = CPLFormFilename( osDirName, szMetadataName, + "rpc" ); + if (CPLCheckForFile((char*)pszRPCFileName, papszSiblingFiles)) + { + m_osRPBSourceFilename = pszRPCFileName; + } + else + { + pszRPCFileName = CPLFormFilename( osDirName, szMetadataName, "RPC" ); + if (CPLCheckForFile((char*)pszRPCFileName, papszSiblingFiles)) + { + m_osRPBSourceFilename = pszRPCFileName; + } + } + + if(m_osIMDSourceFilename.size()) + CPLDebug( "MDReaderEROS", "IMD Filename: %s", + m_osIMDSourceFilename.c_str() ); + if(m_osRPBSourceFilename.size()) + CPLDebug( "MDReaderEROS", "RPB Filename: %s", + m_osRPBSourceFilename.c_str() ); +} + +/** + * ~GDALMDReaderEROS() + */ +GDALMDReaderEROS::~GDALMDReaderEROS() +{ +} + +/** + * HasRequiredFiles() + */ +const bool GDALMDReaderEROS::HasRequiredFiles() const +{ + if (!m_osIMDSourceFilename.empty()) + return true; + if (!m_osRPBSourceFilename.empty()) + return true; + + return false; +} + +/** + * GetMetadataFiles() + */ +char** GDALMDReaderEROS::GetMetadataFiles() const +{ + char **papszFileList = NULL; + if(!m_osIMDSourceFilename.empty()) + papszFileList= CSLAddString( papszFileList, m_osIMDSourceFilename ); + if(!m_osRPBSourceFilename.empty()) + papszFileList = CSLAddString( papszFileList, m_osRPBSourceFilename ); + return papszFileList; +} + +/** + * LoadMetadata() + */ +void GDALMDReaderEROS::LoadMetadata() +{ + if(m_bIsMetadataLoad) + return; + + if(!m_osIMDSourceFilename.empty()) + { + m_papszIMDMD = LoadImdTxtFile(); + } + + if(!m_osRPBSourceFilename.empty()) + { + m_papszRPCMD = GDALLoadRPCFile( m_osRPBSourceFilename ); + } + + m_papszDEFAULTMD = CSLAddNameValue(m_papszDEFAULTMD, MD_NAME_MDTYPE, "EROS"); + + m_bIsMetadataLoad = true; + + const char* pszSatId1 = CSLFetchNameValue(m_papszIMDMD, "satellite"); + const char* pszSatId2 = CSLFetchNameValue(m_papszIMDMD, "camera"); + if(NULL != pszSatId1 && NULL != pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLSPrintf( "%s %s", + CPLStripQuotes(pszSatId1).c_str(), + CPLStripQuotes(pszSatId2).c_str())); + } + else if(NULL != pszSatId1 && NULL == pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLStripQuotes(pszSatId1)); + } + else if(NULL == pszSatId1 && NULL != pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLStripQuotes(pszSatId2)); + } + + const char* pszCloudCover = CSLFetchNameValue(m_papszIMDMD, + "overall_cc"); + if(NULL != pszCloudCover) + { + int nCC = atoi(pszCloudCover); + if(nCC > 100 || nCC < 0) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_CLOUDCOVER, + MD_CLOUDCOVER_NA); + } + else + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_CLOUDCOVER, CPLSPrintf("%d", nCC)); + } + } + + const char* pszDate = CSLFetchNameValue(m_papszIMDMD, "sweep_start_utc"); + if(NULL != pszDate) + { + char buffer[80]; + time_t timeMid = GetAcquisitionTimeFromString(CPLStripQuotes(pszDate)); + strftime (buffer, 80, MD_DATETIMEFORMAT, localtime(&timeMid)); + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, buffer); + } + +} + +/** + * LoadImdTxtFile + */ +char** GDALMDReaderEROS::LoadImdTxtFile() +{ + char** papszLines = CSLLoad(m_osIMDSourceFilename); + if(NULL == papszLines) + return NULL; + + char** papszIMD = NULL; + char szName[22]; + int i, j; + + for(i = 0; papszLines[i] != NULL; i++) + { + const char *pszLine = papszLines[i]; + for(j = 0; j < 21; j++) + { + if(pszLine[j] == ' ') + { + break; + } + szName[j] = pszLine[j]; + } + + if(j > 0) + { + szName[j] = 0; + papszIMD = CSLAddNameValue(papszIMD, szName, pszLine + 20); + } + } + + CSLDestroy(papszLines); + + return papszIMD; +} + +/** + * GetAcqisitionTimeFromString() + */ +const time_t GDALMDReaderEROS::GetAcquisitionTimeFromString( + const char* pszDateTime) +{ + if(NULL == pszDateTime) + return 0; + + int iYear; + int iMonth; + int iDay; + int iHours; + int iMin; + int iSec; + + // exampe: sweep_start_utc 2013-04-22,11:35:02.50724 + + int r = sscanf ( pszDateTime, "%d-%d-%d,%d:%d:%d.%*d", + &iYear, &iMonth, &iDay, &iHours, &iMin, &iSec); + + if (r != 6) + return 0; + + struct tm tmDateTime; + tmDateTime.tm_sec = iSec; + tmDateTime.tm_min = iMin; + tmDateTime.tm_hour = iHours; + tmDateTime.tm_mday = iDay; + tmDateTime.tm_mon = iMonth - 1; + tmDateTime.tm_year = iYear - 1900; + tmDateTime.tm_isdst = -1; + + return mktime(&tmDateTime); +} diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_eros.h b/bazaar/plugin/gdal/gcore/mdreader/reader_eros.h new file mode 100644 index 000000000..3eeca110d --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_eros.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL Core + * Purpose: Read metadata from EROS imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015, NextGIS info@nextgis.ru + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef READER_EROS_H_INCLUDED +#define READER_EROS_H_INCLUDED + +#include "../gdal_mdreader.h" + +/** +@brief Metadata reader for EROS + +TIFF filename: aaaaaaa.bb.ccc.tif +Metadata filename: aaaaaaa.pass + +Common metadata (from metadata filename): + SatelliteId: satellite + AcquisitionDateTime: sweep_start_utc, sweep_end_utc +*/ + +class GDALMDReaderEROS: public GDALMDReaderBase +{ +public: + GDALMDReaderEROS(const char *pszPath, char **papszSiblingFiles); + virtual ~GDALMDReaderEROS(); + virtual const bool HasRequiredFiles() const; + virtual char** GetMetadataFiles() const; +protected: + virtual void LoadMetadata(); + char** LoadImdTxtFile(); + virtual const time_t GetAcquisitionTimeFromString(const char* pszDateTime); +protected: + CPLString m_osIMDSourceFilename; + CPLString m_osRPBSourceFilename; +}; + +#endif // READER_EROS_H_INCLUDED + diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_geo_eye.cpp b/bazaar/plugin/gdal/gcore/mdreader/reader_geo_eye.cpp new file mode 100644 index 000000000..41132f6c0 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_geo_eye.cpp @@ -0,0 +1,366 @@ +/****************************************************************************** + * $Id: reader_geo_eye.cpp 29245 2015-05-24 16:46:56Z rouault $ + * + * Project: GDAL Core + * Purpose: Read metadata from GeoEye imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015, NextGIS info@nextgis.ru + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "reader_geo_eye.h" + +CPL_CVSID("$Id: reader_geo_eye.cpp 29245 2015-05-24 16:46:56Z rouault $"); + +/** + * GDALMDReaderGeoEye() + */ +GDALMDReaderGeoEye::GDALMDReaderGeoEye(const char *pszPath, + char **papszSiblingFiles) : GDALMDReaderBase(pszPath, papszSiblingFiles) +{ + + const char* pszBaseName = CPLGetBasename(pszPath); + const char* pszDirName = CPLGetDirname(pszPath); + size_t nBaseNameLen = strlen(pszBaseName); + if( nBaseNameLen > 511 ) + return; + + // get _metadata.txt file + + // split file name by _rgb_ or _pan_ + char szMetadataName[512] = {0}; + size_t i; + for(i = 0; i < nBaseNameLen; i++) + { + szMetadataName[i] = pszBaseName[i]; + if(EQUALN(pszBaseName + i, "_rgb_", 5) || EQUALN(pszBaseName + i, "_pan_", 5)) + { + break; + } + } + + // form metadata file name + CPLStrlcpy(szMetadataName + i, "_metadata.txt", 14); + const char* pszIMDSourceFilename = CPLFormFilename( pszDirName, + szMetadataName, NULL ); + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszIMDSourceFilename; + } + else + { + CPLStrlcpy(szMetadataName + i, "_METADATA.TXT", 14); + pszIMDSourceFilename = CPLFormFilename( pszDirName, szMetadataName, NULL ); + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszIMDSourceFilename; + } + } + + // get _rpc.txt file + + const char* pszRPBSourceFilename = CPLFormFilename( pszDirName, + CPLSPrintf("%s_rpc", + pszBaseName), + "txt" ); + if (CPLCheckForFile((char*)pszRPBSourceFilename, papszSiblingFiles)) + { + m_osRPBSourceFilename = pszRPBSourceFilename; + } + else + { + pszRPBSourceFilename = CPLFormFilename( pszDirName, CPLSPrintf("%s_RPC", + pszBaseName), "TXT" ); + if (CPLCheckForFile((char*)pszRPBSourceFilename, papszSiblingFiles)) + { + m_osRPBSourceFilename = pszRPBSourceFilename; + } + } + + if( m_osIMDSourceFilename.size() ) + CPLDebug( "MDReaderGeoEye", "IMD Filename: %s", + m_osIMDSourceFilename.c_str() ); + if( m_osRPBSourceFilename.size() ) + CPLDebug( "MDReaderGeoEye", "RPB Filename: %s", + m_osRPBSourceFilename.c_str() ); +} + +/** + * ~GDALMDReaderGeoEye() + */ +GDALMDReaderGeoEye::~GDALMDReaderGeoEye() +{ +} + +/** + * HasRequiredFiles() + */ +const bool GDALMDReaderGeoEye::HasRequiredFiles() const +{ + if (!m_osIMDSourceFilename.empty()) + return true; + + if (!m_osRPBSourceFilename.empty()) + return true; + + return false; +} + +/** + * GetMetadataFiles() + */ +char** GDALMDReaderGeoEye::GetMetadataFiles() const +{ + char **papszFileList = NULL; + if(!m_osIMDSourceFilename.empty()) + papszFileList= CSLAddString( papszFileList, m_osIMDSourceFilename ); + if(!m_osRPBSourceFilename.empty()) + papszFileList = CSLAddString( papszFileList, m_osRPBSourceFilename ); + + return papszFileList; +} + +/** + * LoadMetadata() + */ +void GDALMDReaderGeoEye::LoadMetadata() +{ + if(m_bIsMetadataLoad) + return; + + if (!m_osIMDSourceFilename.empty()) + { + m_papszIMDMD = LoadIMDWktFile( ); + } + + if(!m_osRPBSourceFilename.empty()) + { + m_papszRPCMD = GDALLoadRPCFile( m_osRPBSourceFilename ); + } + + m_papszDEFAULTMD = CSLAddNameValue(m_papszDEFAULTMD, MD_NAME_MDTYPE, "GE"); + + m_bIsMetadataLoad = true; + + if(NULL == m_papszIMDMD) + { + return; + } + + //extract imagery metadata + const char* pszSatId = CSLFetchNameValue(m_papszIMDMD, + "Source Image Metadata.Sensor"); + if(NULL != pszSatId) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, + CPLStripQuotes(pszSatId)); + } + + const char* pszCloudCover = CSLFetchNameValue(m_papszIMDMD, + "Source Image Metadata.Percent Cloud Cover"); + if(NULL != pszCloudCover) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_CLOUDCOVER, pszCloudCover); + } + + const char* pszDateTime = CSLFetchNameValue(m_papszIMDMD, + "Source Image Metadata.Acquisition Date/Time"); + + if(NULL != pszDateTime) + { + char buffer[80]; + time_t timeMid = GetAcquisitionTimeFromString(pszDateTime); + + strftime (buffer, 80, MD_DATETIMEFORMAT, localtime(&timeMid)); + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, buffer); + } +} + +/** + * GetAcqisitionTimeFromString() + */ +const time_t GDALMDReaderGeoEye::GetAcquisitionTimeFromString( + const char* pszDateTime) +{ + if(NULL == pszDateTime) + return 0; + + int iYear; + int iMonth; + int iDay; + int iHours; + int iMin; + int iSec = 0; + +// string exampe: Acquisition Date/Time: 2006-03-01 11:08 GMT + + int r = sscanf ( pszDateTime, "%d-%d-%d %d:%d GMT", &iYear, &iMonth, + &iDay, &iHours, &iMin); + + if (r != 5) + return 0; + + struct tm tmDateTime; + tmDateTime.tm_sec = iSec; + tmDateTime.tm_min = iMin; + tmDateTime.tm_hour = iHours; + tmDateTime.tm_mday = iDay; + tmDateTime.tm_mon = iMonth - 1; + tmDateTime.tm_year = iYear - 1900; + tmDateTime.tm_isdst = -1; + + return mktime(&tmDateTime); +} + +/** + * LoadWKTIMDFile() + */ +char** GDALMDReaderGeoEye::LoadIMDWktFile() const +{ + char** papszResultList = NULL; + char** papszLines = CSLLoad( m_osIMDSourceFilename ); + bool bBeginSection = false; + CPLString osSection; + CPLString osKeyLevel1; + CPLString osKeyLevel2; + CPLString osKeyLevel3; + int nLevel = 0; + int nSpaceCount; + + if( papszLines == NULL ) + return NULL; + + for( int i = 0; papszLines[i] != NULL; i++ ) + { + // skip section (=== or ---) lines + + if(EQUALN( papszLines[i], "===",3)) + { + bBeginSection = true; + continue; + } + + if(EQUALN( papszLines[i], "---",3) || CPLStrnlen(papszLines[i], 512) == 0) + continue; + + // check the metadata level + nSpaceCount = 0; + for(int j = 0; j < 11; j++) + { + if(papszLines[i][j] != ' ') + break; + nSpaceCount++; + } + + if(nSpaceCount % 3 != 0) + continue; // not a metadata item + nLevel = nSpaceCount / 3; + + const char *pszValue; + char *pszKey = NULL; + pszValue = CPLParseNameValue(papszLines[i], &pszKey); + + if(NULL != pszValue && CPLStrnlen(pszValue, 512) > 0) + { + + CPLString osCurrentKey; + if(nLevel == 0) + { + osCurrentKey = CPLOPrintf("%s", pszKey); + } + else if(nLevel == 1) + { + osCurrentKey = osKeyLevel1 + "." + + CPLOPrintf("%s", pszKey + nSpaceCount); + } + else if(nLevel == 2) + { + osCurrentKey = osKeyLevel1 + "." + + osKeyLevel2 + "." + CPLOPrintf("%s", pszKey + nSpaceCount); + } + else if(nLevel == 3) + { + osCurrentKey = osKeyLevel1 + "." + + osKeyLevel2 + "." + osKeyLevel3 + "." + + CPLOPrintf("%s", pszKey + nSpaceCount); + } + + if(!osSection.empty()) + { + osCurrentKey = osSection + "." + osCurrentKey; + } + + papszResultList = CSLAddNameValue(papszResultList, osCurrentKey, pszValue); + } + + if(NULL != pszKey && CPLStrnlen(pszKey, 512) > 0) + { + if(bBeginSection) + { + osSection = CPLOPrintf("%s", pszKey); + bBeginSection = false; + } + else if(nLevel == 0) + { + osKeyLevel1 = CPLOPrintf("%s", pszKey); + } + else if(nLevel == 1) + { + osKeyLevel2 = CPLOPrintf("%s", pszKey + nSpaceCount); + } + else if(nLevel == 2) + { + osKeyLevel3 = CPLOPrintf("%s", pszKey + nSpaceCount); + } + } + else + { + if(bBeginSection) + { + osSection = CPLOPrintf("%s", papszLines[i]); + bBeginSection = false; + } + else if(nLevel == 0) + { + osKeyLevel1 = CPLOPrintf("%s", papszLines[i]); + } + else if(nLevel == 1) + { + osKeyLevel2 = CPLOPrintf("%s", papszLines[i] + nSpaceCount); + } + else if(nLevel == 2) + { + osKeyLevel3 = CPLOPrintf("%s", papszLines[i]+ nSpaceCount); + } + } + + CPLFree( pszKey ); + } + + CSLDestroy( papszLines ); + + return papszResultList; +} diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_geo_eye.h b/bazaar/plugin/gdal/gcore/mdreader/reader_geo_eye.h new file mode 100644 index 000000000..1d6f26c8a --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_geo_eye.h @@ -0,0 +1,67 @@ +/****************************************************************************** + * $Id: reader_geo_eye.h 29190 2015-05-13 21:40:30Z bishop $ + * + * Project: GDAL Core + * Purpose: Read metadata from GeoEye imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015, NextGIS info@nextgis.ru + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef READER_GEO_EYE_H_INCLUDED +#define READER_GEO_EYE_H_INCLUDED + +#include "../gdal_mdreader.h" + +/** +@brief Metadata reader for Geo Eye + +TIFF filename: aaaaaaaaaa.tif +Metadata filename: *_metadata* +RPC filename: aaaaaaaaaa_rpc.txt + +Common metadata (from metadata filename): + SatelliteId: Sensor + CloudCover: Percent Cloud Cover + AcquisitionDateTime: Acquisition Date/Time + +*/ + +class GDALMDReaderGeoEye: public GDALMDReaderBase +{ +public: + GDALMDReaderGeoEye(const char *pszPath, char **papszSiblingFiles); + virtual ~GDALMDReaderGeoEye(); + virtual const bool HasRequiredFiles() const; + virtual char** GetMetadataFiles() const; +protected: + virtual void LoadMetadata(); + virtual const time_t GetAcquisitionTimeFromString(const char* pszDateTime); + char **LoadRPCWktFile() const; + char **LoadIMDWktFile() const; +protected: + CPLString m_osIMDSourceFilename; + CPLString m_osRPBSourceFilename; +}; + +#endif // READER_GEO_EYE_H_INCLUDED diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_kompsat.cpp b/bazaar/plugin/gdal/gcore/mdreader/reader_kompsat.cpp new file mode 100644 index 000000000..0f8ae664e --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_kompsat.cpp @@ -0,0 +1,277 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL Core + * Purpose: Read metadata from Kompsat imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "reader_kompsat.h" + +/** + * GDALMDReaderKompsat() + */ +GDALMDReaderKompsat::GDALMDReaderKompsat(const char *pszPath, + char **papszSiblingFiles) : GDALMDReaderBase(pszPath, papszSiblingFiles) +{ + m_osIMDSourceFilename = GDALFindAssociatedFile( pszPath, "TXT", + papszSiblingFiles, 0 ); + m_osRPBSourceFilename = GDALFindAssociatedFile( pszPath, "RPC", + papszSiblingFiles, 0 ); + + if(m_osIMDSourceFilename.size()) + CPLDebug( "MDReaderDigitalGlobe", "IMD Filename: %s", + m_osIMDSourceFilename.c_str() ); + if(m_osRPBSourceFilename.size()) + CPLDebug( "MDReaderDigitalGlobe", "RPB Filename: %s", + m_osRPBSourceFilename.c_str() ); +} + +/** + * ~GDALMDReaderKompsat() + */ +GDALMDReaderKompsat::~GDALMDReaderKompsat() +{ +} + +/** + * HasRequiredFiles() + */ +const bool GDALMDReaderKompsat::HasRequiredFiles() const +{ + if (!m_osIMDSourceFilename.empty() && !m_osRPBSourceFilename.empty()) + return true; + + return false; +} + +/** + * GetMetadataFiles() + */ +char** GDALMDReaderKompsat::GetMetadataFiles() const +{ + char **papszFileList = NULL; + if(!m_osIMDSourceFilename.empty()) + papszFileList= CSLAddString( papszFileList, m_osIMDSourceFilename ); + if(!m_osRPBSourceFilename.empty()) + papszFileList= CSLAddString( papszFileList, m_osRPBSourceFilename ); + + return papszFileList; +} + +/** + * LoadMetadata() + */ +void GDALMDReaderKompsat::LoadMetadata() +{ + if(m_bIsMetadataLoad) + return; + + if(!m_osIMDSourceFilename.empty()) + { + m_papszIMDMD = ReadTxtToList( ); + } + + if(!m_osRPBSourceFilename.empty()) + { + m_papszRPCMD = GDALLoadRPCFile( m_osRPBSourceFilename ); + } + + m_papszDEFAULTMD = CSLAddNameValue(m_papszDEFAULTMD, MD_NAME_MDTYPE, "KARI"); + + m_bIsMetadataLoad = true; + + const char* pszSatId1 = CSLFetchNameValue(m_papszIMDMD, "AUX_SATELLITE_NAME"); + const char* pszSatId2 = CSLFetchNameValue(m_papszIMDMD, "AUX_SATELLITE_SENSOR"); + if(NULL != pszSatId1 && NULL != pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLSPrintf( "%s %s", + CPLStripQuotes(pszSatId1).c_str(), + CPLStripQuotes(pszSatId2).c_str())); + } + else if(NULL != pszSatId1 && NULL == pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLStripQuotes(pszSatId1)); + } + else if(NULL == pszSatId1 && NULL != pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLStripQuotes(pszSatId2)); + } + + const char* pszCloudCover = CSLFetchNameValue(m_papszIMDMD, + "AUX_CLOUD_STATUS"); + if(NULL != pszCloudCover) + { + int nCC = atoi(pszCloudCover); + if(nCC > 100 || nCC < 0) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_CLOUDCOVER, + MD_CLOUDCOVER_NA); + } + else + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_CLOUDCOVER, CPLSPrintf("%d", nCC)); + } + } + + const char* pszDate = CSLFetchNameValue(m_papszIMDMD, + "AUX_STRIP_ACQ_DATE_UT"); + if(NULL != pszDate) + { + const char* pszTime = CSLFetchNameValue(m_papszIMDMD, + "AUX_STRIP_ACQ_START_UT"); + + if(NULL == pszTime) + pszTime = "000000.000000"; + + char buffer[80]; + time_t timeMid = GetAcquisitionTimeFromString(CPLSPrintf( "%sT%s", + pszDate, pszTime)); + strftime (buffer, 80, MD_DATETIMEFORMAT, localtime(&timeMid)); + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, buffer); + } +} + +/** + * ReadTxtToList + */ +char** GDALMDReaderKompsat::ReadTxtToList() +{ + char** papszLines = CSLLoad(m_osIMDSourceFilename); + if(NULL == papszLines) + return NULL; + + char** papszIMD = NULL; + char szName[512]; + int i; + size_t j; + CPLString soGroupName; + + for(i = 0; papszLines[i] != NULL; i++) + { + const char *pszLine = papszLines[i]; + + //check if this is begin block + if(EQUALN(pszLine, "BEGIN_", 6)) + { + for(j = 6; j < CPLStrnlen(pszLine, 512); j++) + { + if(EQUALN(pszLine + j, "_BLOCK", 6)) + { + szName[j - 6] = 0; + break; + } + szName[j - 6] = pszLine[j]; + } + + soGroupName = szName; + + continue; + } + + //check if this is end block + if(EQUALN(pszLine, "END_", 4)) + { + soGroupName.clear(); // we don't expect subblocks + continue; + } + + //get name and value + for(j = 0; j < CPLStrnlen(pszLine, 512); j++) + { + if(pszLine[j] == '\t') + { + if(soGroupName.empty() || j > 0) + { + szName[j] = 0; + j++; + break; + } + else + { + continue; + } + } + szName[j] = pszLine[j]; + } + + // trim + while( pszLine[j] == ' ' && pszLine[j] != 0) j++; + + if(soGroupName.empty()) + { + papszIMD = CSLAddNameValue(papszIMD, szName, pszLine + j); + } + else + { + papszIMD = CSLAddNameValue(papszIMD, CPLSPrintf("%s.%s", + soGroupName.c_str(), szName), pszLine + j); + } + + } + + CSLDestroy(papszLines); + + return papszIMD; +} + +/** + * GetAcqisitionTimeFromString() + */ +const time_t GDALMDReaderKompsat::GetAcquisitionTimeFromString( + const char* pszDateTime) +{ + if(NULL == pszDateTime) + return 0; + + int iYear; + int iMonth; + int iDay; + int iHours; + int iMin; + int iSec; + + int r = sscanf ( pszDateTime, "%4d%2d%2dT%2d%2d%2d.%*s", + &iYear, &iMonth, &iDay, &iHours, &iMin, &iSec); + + if (r != 6) + return 0; + + struct tm tmDateTime; + tmDateTime.tm_sec = iSec; + tmDateTime.tm_min = iMin; + tmDateTime.tm_hour = iHours; + tmDateTime.tm_mday = iDay; + tmDateTime.tm_mon = iMonth - 1; + tmDateTime.tm_year = iYear - 1900; + tmDateTime.tm_isdst = -1; + + return mktime(&tmDateTime); +} diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_kompsat.h b/bazaar/plugin/gdal/gcore/mdreader/reader_kompsat.h new file mode 100644 index 000000000..d5306aeeb --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_kompsat.h @@ -0,0 +1,65 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL Core + * Purpose: Read metadata from Kompsat imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef READER_KOMPSAT_H_INCLUDED +#define READER_KOMPSAT_H_INCLUDED + +#include "reader_pleiades.h" + +/** +@brief Metadata reader for Kompsat + +TIFF filename: aaaaaaaaaa.tif +Metadata filename: aaaaaaaaaa.eph aaaaaaaaaa.txt +RPC filename: aaaaaaaaaa.rpc + +Common metadata (from metadata filename): + SatelliteId: AUX_SATELLITE_NAME + AcquisitionDateTime: IMG_ACQISITION_START_TIME, IMG_ACQISITION_END_TIME +*/ + +class GDALMDReaderKompsat: public GDALMDReaderBase +{ +public: + GDALMDReaderKompsat(const char *pszPath, char **papszSiblingFiles); + virtual ~GDALMDReaderKompsat(); + virtual const bool HasRequiredFiles() const; + virtual char** GetMetadataFiles() const; +protected: + virtual void LoadMetadata(); + char** ReadTxtToList(); + virtual const time_t GetAcquisitionTimeFromString(const char* pszDateTime); +protected: + CPLString m_osIMDSourceFilename; + CPLString m_osRPBSourceFilename; +}; + +#endif // READER_KOMPSAT_H_INCLUDED + diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_landsat.cpp b/bazaar/plugin/gdal/gcore/mdreader/reader_landsat.cpp new file mode 100644 index 000000000..56c8e7f61 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_landsat.cpp @@ -0,0 +1,195 @@ +/****************************************************************************** + * $Id: reader_landsat.cpp 29245 2015-05-24 16:46:56Z rouault $ + * + * Project: GDAL Core + * Purpose: Read metadata from Landsat imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "reader_landsat.h" + +CPL_CVSID("$Id: reader_landsat.cpp 29245 2015-05-24 16:46:56Z rouault $"); + +/** + * GDALMDReaderLandsat() + */ +GDALMDReaderLandsat::GDALMDReaderLandsat(const char *pszPath, + char **papszSiblingFiles) : GDALMDReaderBase(pszPath, papszSiblingFiles) +{ + const char* pszBaseName = CPLGetBasename(pszPath); + const char* pszDirName = CPLGetDirname(pszPath); + size_t nBaseNameLen = strlen(pszBaseName); + if( nBaseNameLen > 511 ) + return; + + // split file name by _B or _b + char szMetadataName[512] = {0}; + size_t i; + for(i = 0; i < nBaseNameLen; i++) + { + szMetadataName[i] = pszBaseName[i]; + if(EQUALN(pszBaseName + i, "_B", 2) || EQUALN(pszBaseName + i, "_b", 2)) + { + break; + } + } + + // form metadata file name + CPLStrlcpy(szMetadataName + i, "_MTL.txt", 9); + + const char* pszIMDSourceFilename = CPLFormFilename( pszDirName, + szMetadataName, NULL ); + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszIMDSourceFilename; + } + else + { + CPLStrlcpy(szMetadataName + i, "_MTL.TXT", 9); + pszIMDSourceFilename = CPLFormFilename( pszDirName, szMetadataName, NULL ); + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszIMDSourceFilename; + } + } + + if( m_osIMDSourceFilename.size() ) + CPLDebug( "MDReaderLandsat", "IMD Filename: %s", + m_osIMDSourceFilename.c_str() ); +} + +/** + * ~GDALMDReaderLandsat() + */ +GDALMDReaderLandsat::~GDALMDReaderLandsat() +{ +} + +/** + * HasRequiredFiles() + */ +const bool GDALMDReaderLandsat::HasRequiredFiles() const +{ + if (!m_osIMDSourceFilename.empty()) + return true; + + return false; +} + +/** + * GetMetadataFiles() + */ +char** GDALMDReaderLandsat::GetMetadataFiles() const +{ + char **papszFileList = NULL; + if(!m_osIMDSourceFilename.empty()) + papszFileList= CSLAddString( papszFileList, m_osIMDSourceFilename ); + + return papszFileList; +} + +/** + * LoadMetadata() + */ +void GDALMDReaderLandsat::LoadMetadata() +{ + if(m_bIsMetadataLoad) + return; + + if (!m_osIMDSourceFilename.empty()) + { + m_papszIMDMD = GDALLoadIMDFile( m_osIMDSourceFilename ); + } + + m_papszDEFAULTMD = CSLAddNameValue(m_papszDEFAULTMD, MD_NAME_MDTYPE, "ODL"); + + m_bIsMetadataLoad = true; + + // date/time + // DATE_ACQUIRED = 2013-04-07 + // SCENE_CENTER_TIME = 15:47:03.0882620Z + + // L1_METADATA_FILE.PRODUCT_METADATA.SPACECRAFT_ID + const char* pszSatId = CSLFetchNameValue(m_papszIMDMD, + "L1_METADATA_FILE.PRODUCT_METADATA.SPACECRAFT_ID"); + if(NULL != pszSatId) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_SATELLITE, + CPLStripQuotes(pszSatId)); + } + + // L1_METADATA_FILE.IMAGE_ATTRIBUTES.CLOUD_COVER + const char* pszCloudCover = CSLFetchNameValue(m_papszIMDMD, + "L1_METADATA_FILE.IMAGE_ATTRIBUTES.CLOUD_COVER"); + if(NULL != pszCloudCover) + { + double fCC = CPLAtofM(pszCloudCover); + if(fCC < 0) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_CLOUDCOVER, + MD_CLOUDCOVER_NA); + } + else + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_CLOUDCOVER, CPLSPrintf("%d", int(fCC))); + } + } + + // L1_METADATA_FILE.PRODUCT_METADATA.ACQUISITION_DATE + // L1_METADATA_FILE.PRODUCT_METADATA.SCENE_CENTER_SCAN_TIME + + // L1_METADATA_FILE.PRODUCT_METADATA.DATE_ACQUIRED + // L1_METADATA_FILE.PRODUCT_METADATA.SCENE_CENTER_TIME + + const char* pszDate = CSLFetchNameValue(m_papszIMDMD, + "L1_METADATA_FILE.PRODUCT_METADATA.ACQUISITION_DATE"); + if(NULL == pszDate) + { + pszDate = CSLFetchNameValue(m_papszIMDMD, + "L1_METADATA_FILE.PRODUCT_METADATA.DATE_ACQUIRED"); + } + + if(NULL != pszDate) + { + const char* pszTime = CSLFetchNameValue(m_papszIMDMD, + "L1_METADATA_FILE.PRODUCT_METADATA.SCENE_CENTER_SCAN_TIME"); + if(NULL == pszTime) + { + pszTime = CSLFetchNameValue(m_papszIMDMD, + "L1_METADATA_FILE.PRODUCT_METADATA.SCENE_CENTER_TIME"); + } + if(NULL == pszTime) + pszTime = "00:00:00.000000Z"; + + char buffer[80]; + time_t timeMid = GetAcquisitionTimeFromString(CPLSPrintf( "%sT%s", + pszDate, pszTime)); + strftime (buffer, 80, MD_DATETIMEFORMAT, localtime(&timeMid)); + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, buffer); + } + +} diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_landsat.h b/bazaar/plugin/gdal/gcore/mdreader/reader_landsat.h new file mode 100644 index 000000000..e54ce43e1 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_landsat.h @@ -0,0 +1,65 @@ +/****************************************************************************** + * $Id: reader_landsat.h 29190 2015-05-13 21:40:30Z bishop $ + * + * Project: GDAL Core + * Purpose: Read metadata from Landsat imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef READER_LANDSAT_H_INCLUDED +#define READER_LANDSAT_H_INCLUDED + +#include "../gdal_mdreader.h" + +/** +@brief Metadata reader for Landsat + +TIFF filename: xxxxxx_aaa.tif +Metadata filename: xxxxxx_MTL.txt +RPC filename: + +Common metadata (from metadata filename): + SatelliteId: SPACECRAFT_ID + CloudCover: CLOUD_COVER (Landsat 8) + AcquisitionDateTime: ACQUISITION_DATE, + SCENE_CENTER_SCAN_TIME (Landsat 5,7) or + DATE_ACQUIRED, SCENE_CENTER_TIME (Landsat 8); + +*/ + +class GDALMDReaderLandsat: public GDALMDReaderBase +{ +public: + GDALMDReaderLandsat(const char *pszPath, char **papszSiblingFiles); + virtual ~GDALMDReaderLandsat(); + virtual const bool HasRequiredFiles() const; + virtual char** GetMetadataFiles() const; +protected: + virtual void LoadMetadata(); +protected: + CPLString m_osIMDSourceFilename; +}; + +#endif // READER_LANDSAT_H_INCLUDED diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_orb_view.cpp b/bazaar/plugin/gdal/gcore/mdreader/reader_orb_view.cpp new file mode 100644 index 000000000..c39ead2d0 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_orb_view.cpp @@ -0,0 +1,161 @@ +/****************************************************************************** + * $Id: reader_orb_view.cpp 29145 2015-05-04 10:00:40Z rouault $ + * + * Project: GDAL Core + * Purpose: Read metadata from OrbView imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "reader_orb_view.h" + +CPL_CVSID("$Id: reader_orb_view.cpp 29145 2015-05-04 10:00:40Z rouault $"); + +/** + * GDALMDReaderOrbView() + */ +GDALMDReaderOrbView::GDALMDReaderOrbView(const char *pszPath, + char **papszSiblingFiles) : GDALMDReaderBase(pszPath, papszSiblingFiles) +{ + m_osIMDSourceFilename = GDALFindAssociatedFile( pszPath, "PVL", + papszSiblingFiles, 0 ); + + const char* pszBaseName = CPLGetBasename(pszPath); + const char* pszDirName = CPLGetDirname(pszPath); + + const char* pszRPBSourceFilename = CPLFormFilename( pszDirName, + CPLSPrintf("%s_rpc", + pszBaseName), + "txt" ); + if (CPLCheckForFile((char*)pszRPBSourceFilename, papszSiblingFiles)) + { + m_osRPBSourceFilename = pszRPBSourceFilename; + } + else + { + pszRPBSourceFilename = CPLFormFilename( pszDirName, CPLSPrintf("%s_RPC", + pszBaseName), "TXT" ); + if (CPLCheckForFile((char*)pszRPBSourceFilename, papszSiblingFiles)) + { + m_osRPBSourceFilename = pszRPBSourceFilename; + } + } + + if( m_osIMDSourceFilename.size() ) + CPLDebug( "MDReaderOrbView", "IMD Filename: %s", + m_osIMDSourceFilename.c_str() ); + if( m_osRPBSourceFilename.size() ) + CPLDebug( "MDReaderOrbView", "RPB Filename: %s", + m_osRPBSourceFilename.c_str() ); +} + +/** + * ~GDALMDReaderOrbView() + */ +GDALMDReaderOrbView::~GDALMDReaderOrbView() +{ +} + +/** + * HasRequiredFiles() + */ +const bool GDALMDReaderOrbView::HasRequiredFiles() const +{ + if (!m_osIMDSourceFilename.empty() && !m_osRPBSourceFilename.empty()) + return true; + + return false; +} + +/** + * GetMetadataFiles() + */ +char** GDALMDReaderOrbView::GetMetadataFiles() const +{ + char **papszFileList = NULL; + if(!m_osIMDSourceFilename.empty()) + papszFileList= CSLAddString( papszFileList, m_osIMDSourceFilename ); + if(!m_osRPBSourceFilename.empty()) + papszFileList = CSLAddString( papszFileList, m_osRPBSourceFilename ); + + return papszFileList; +} + +/** + * LoadMetadata() + */ +void GDALMDReaderOrbView::LoadMetadata() +{ + if(m_bIsMetadataLoad) + return; + + if (!m_osIMDSourceFilename.empty()) + { + m_papszIMDMD = GDALLoadIMDFile( m_osIMDSourceFilename ); + } + + if(!m_osRPBSourceFilename.empty()) + { + m_papszRPCMD = GDALLoadRPCFile( m_osRPBSourceFilename ); + } + + m_papszDEFAULTMD = CSLAddNameValue(m_papszDEFAULTMD, MD_NAME_MDTYPE, "OV"); + + m_bIsMetadataLoad = true; + + if(NULL == m_papszIMDMD) + { + return; + } + + //extract imagery metadata + const char* pszSatId = CSLFetchNameValue(m_papszIMDMD, + "sensorInfo.satelliteName"); + if(NULL != pszSatId) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, + CPLStripQuotes(pszSatId)); + } + + const char* pszCloudCover = CSLFetchNameValue(m_papszIMDMD, + "productInfo.productCloudCoverPercentage"); + if(NULL != pszCloudCover) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_CLOUDCOVER, pszCloudCover); + } + + const char* pszDateTime = CSLFetchNameValue(m_papszIMDMD, + "inputImageInfo.firstLineAcquisitionDateTime"); + + if(NULL != pszDateTime) + { + char buffer[80]; + time_t timeMid = GetAcquisitionTimeFromString(pszDateTime); + strftime (buffer, 80, MD_DATETIMEFORMAT, localtime(&timeMid)); + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, buffer); + } +} diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_orb_view.h b/bazaar/plugin/gdal/gcore/mdreader/reader_orb_view.h new file mode 100644 index 000000000..22c727e87 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_orb_view.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * $Id: reader_orb_view.h 29190 2015-05-13 21:40:30Z bishop $ + * + * Project: GDAL Core + * Purpose: Read metadata from OrbView imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef READER_ORB_VIEW_H_INCLUDED +#define READER_ORB_VIEW_H_INCLUDED + +#include "../gdal_mdreader.h" + +/** +@brief Metadata reader for OrbView + +TIFF filename: aaaaaaaaa.tif +Metadata filename: aaaaaaaaa.pvl +RPC filename: aaaaaaaaa_rpc.txt + +Common metadata (from metadata filename): + SatelliteId: sensorInfo.satelliteName + CloudCover: productInfo.productCloudCoverPercentage + AcquisitionDateTime: inputImageInfo.firstLineAcquisitionDateTime +*/ +class GDALMDReaderOrbView: public GDALMDReaderBase +{ +public: + GDALMDReaderOrbView(const char *pszPath, char **papszSiblingFiles); + virtual ~GDALMDReaderOrbView(); + virtual const bool HasRequiredFiles() const; + virtual char** GetMetadataFiles() const; +protected: + virtual void LoadMetadata(); +protected: + CPLString m_osIMDSourceFilename; + CPLString m_osRPBSourceFilename; +}; + +#endif // READER_ORB_VIEW_H_INCLUDED + diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_pleiades.cpp b/bazaar/plugin/gdal/gcore/mdreader/reader_pleiades.cpp new file mode 100644 index 000000000..160fa0cf7 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_pleiades.cpp @@ -0,0 +1,329 @@ +/****************************************************************************** + * $Id: reader_pleiades.cpp 29245 2015-05-24 16:46:56Z rouault $ + * + * Project: GDAL Core + * Purpose: Read metadata from Pleiades imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "reader_pleiades.h" + +CPL_CVSID("$Id: reader_pleiades.cpp 29245 2015-05-24 16:46:56Z rouault $"); + +/** + * GDALMDReaderPleiades() + */ +GDALMDReaderPleiades::GDALMDReaderPleiades(const char *pszPath, + char **papszSiblingFiles) : GDALMDReaderBase(pszPath, papszSiblingFiles) +{ + const char* pszBaseName = CPLGetBasename(pszPath); + size_t nBaseNameLen = strlen(pszBaseName); + if( nBaseNameLen < 4 || nBaseNameLen > 511 ) + return; + + const char* pszDirName = CPLGetDirname(pszPath); + + const char* pszIMDSourceFilename = CPLFormFilename( pszDirName, + CPLSPrintf("DIM_%s", pszBaseName + 4), "XML" ); + const char* pszRPBSourceFilename = CPLFormFilename( pszDirName, + CPLSPrintf("RPC_%s", pszBaseName + 4), "XML" ); + + // find last underline + char sBaseName[512]; + int nLastUnderline = 0; + for(size_t i = 4; i < nBaseNameLen; i++) + { + sBaseName[i - 4] = pszBaseName[i]; + if(pszBaseName[i] == '_') + nLastUnderline = i - 4; + } + + sBaseName[nLastUnderline] = 0; + + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszIMDSourceFilename; + } + else + { + pszIMDSourceFilename = CPLFormFilename( pszDirName, CPLSPrintf("DIM_%s", + sBaseName), "XML" ); + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszIMDSourceFilename; + } + } + + if (CPLCheckForFile((char*)pszRPBSourceFilename, papszSiblingFiles)) + { + m_osRPBSourceFilename = pszRPBSourceFilename; + } + else + { + pszRPBSourceFilename = CPLFormFilename( pszDirName, CPLSPrintf("RPC_%s", + sBaseName), "XML" ); + if (CPLCheckForFile((char*)pszRPBSourceFilename, papszSiblingFiles)) + { + m_osRPBSourceFilename = pszRPBSourceFilename; + } + } + + if( m_osIMDSourceFilename.size() ) + CPLDebug( "MDReaderPleiades", "IMD Filename: %s", + m_osIMDSourceFilename.c_str() ); + if( m_osRPBSourceFilename.size() ) + CPLDebug( "MDReaderPleiades", "RPB Filename: %s", + m_osRPBSourceFilename.c_str() ); +} + +/** + * ~GDALMDReaderPleiades() + */ +GDALMDReaderPleiades::~GDALMDReaderPleiades() +{ +} + +/** + * HasRequiredFiles() + */ +const bool GDALMDReaderPleiades::HasRequiredFiles() const +{ + if (!m_osIMDSourceFilename.empty()) + return true; + if (!m_osRPBSourceFilename.empty()) + return true; + + return false; +} + +/** + * GetMetadataFiles() + */ +char** GDALMDReaderPleiades::GetMetadataFiles() const +{ + char **papszFileList = NULL; + if(!m_osIMDSourceFilename.empty()) + papszFileList= CSLAddString( papszFileList, m_osIMDSourceFilename ); + if(!m_osRPBSourceFilename.empty()) + papszFileList = CSLAddString( papszFileList, m_osRPBSourceFilename ); + + return papszFileList; +} + +/** + * LoadMetadata() + */ +void GDALMDReaderPleiades::LoadMetadata() +{ + if(m_bIsMetadataLoad) + return; + + if (!m_osIMDSourceFilename.empty()) + { + CPLXMLNode* psNode = CPLParseXMLFile(m_osIMDSourceFilename); + + if(psNode != NULL) + { + CPLXMLNode* psisdNode = CPLSearchXMLNode(psNode, "=Dimap_Document"); + + if(psisdNode != NULL) + { + m_papszIMDMD = ReadXMLToList(psisdNode->psChild, m_papszIMDMD); + } + CPLDestroyXMLNode(psNode); + } + } + + if(!m_osRPBSourceFilename.empty()) + { + m_papszRPCMD = LoadRPCXmlFile( ); + } + + m_papszDEFAULTMD = CSLAddNameValue(m_papszDEFAULTMD, MD_NAME_MDTYPE, "DIMAP"); + + m_bIsMetadataLoad = true; + + if(NULL == m_papszIMDMD) + { + return; + } + + //extract imagery metadata + int nCounter = -1; + const char* pszSatId1 = CSLFetchNameValue(m_papszIMDMD, + "Dataset_Sources.Source_Identification.Strip_Source.MISSION"); + if(NULL == pszSatId1) + { + nCounter = 1; + for(int i = 0; i < 5; i++) + { + pszSatId1 = CSLFetchNameValue(m_papszIMDMD, + CPLSPrintf("Dataset_Sources.Source_Identification_%d.Strip_Source.MISSION", + nCounter)); + if(NULL != pszSatId1) + break; + nCounter++; + } + } + + + const char* pszSatId2; + if(nCounter == -1) + pszSatId2 = CSLFetchNameValue(m_papszIMDMD, + "Dataset_Sources.Source_Identification.Strip_Source.MISSION_INDEX"); + else + pszSatId2 = CSLFetchNameValue(m_papszIMDMD, CPLSPrintf( + "Dataset_Sources.Source_Identification_%d.Strip_Source.MISSION_INDEX", + nCounter)); + + if(NULL != pszSatId1 && NULL != pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLSPrintf( "%s %s", + CPLStripQuotes(pszSatId1).c_str(), + CPLStripQuotes(pszSatId2).c_str())); + } + else if(NULL != pszSatId1 && NULL == pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLStripQuotes(pszSatId1)); + } + else if(NULL == pszSatId1 && NULL != pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLStripQuotes(pszSatId2)); + } + + + const char* pszDate; + if(nCounter == -1) + pszDate = CSLFetchNameValue(m_papszIMDMD, + "Dataset_Sources.Source_Identification.Strip_Source.IMAGING_DATE"); + else + pszDate = CSLFetchNameValue(m_papszIMDMD, CPLSPrintf( + "Dataset_Sources.Source_Identification_%d.Strip_Source.IMAGING_DATE", + nCounter)); + + if(NULL != pszDate) + { + const char* pszTime; + if(nCounter == -1) + pszTime = CSLFetchNameValue(m_papszIMDMD, + "Dataset_Sources.Source_Identification.Strip_Source.IMAGING_TIME"); + else + pszTime = CSLFetchNameValue(m_papszIMDMD, CPLSPrintf( + "Dataset_Sources.Source_Identification_%d.Strip_Source.IMAGING_TIME", + nCounter)); + + if(NULL == pszTime) + pszTime = "00:00:00.0Z"; + + char buffer[80]; + time_t timeMid = GetAcquisitionTimeFromString(CPLSPrintf( "%sT%s", + pszDate, pszTime)); + strftime (buffer, 80, MD_DATETIMEFORMAT, localtime(&timeMid)); + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, buffer); + } + + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_CLOUDCOVER, + MD_CLOUDCOVER_NA); +} + +/** + * LoadRPCXmlFile() + */ + +static const char *apszRPBMap[] = { + RPC_LINE_OFF, "RFM_Validity.LINE_OFF", + RPC_SAMP_OFF, "RFM_Validity.SAMP_OFF", + RPC_LAT_OFF, "RFM_Validity.LAT_OFF", + RPC_LONG_OFF, "RFM_Validity.LONG_OFF", + RPC_HEIGHT_OFF, "RFM_Validity.HEIGHT_OFF", + RPC_LINE_SCALE, "RFM_Validity.LINE_SCALE", + RPC_SAMP_SCALE, "RFM_Validity.SAMP_SCALE", + RPC_LAT_SCALE, "RFM_Validity.LAT_SCALE", + RPC_LONG_SCALE, "RFM_Validity.LONG_SCALE", + RPC_HEIGHT_SCALE, "RFM_Validity.HEIGHT_SCALE", + NULL, NULL }; + +static const char *apszRPCTXT20ValItems[] = +{ + RPC_LINE_NUM_COEFF, + RPC_LINE_DEN_COEFF, + RPC_SAMP_NUM_COEFF, + RPC_SAMP_DEN_COEFF, + NULL +}; + +char** GDALMDReaderPleiades::LoadRPCXmlFile() +{ + CPLXMLNode* pNode = CPLParseXMLFile(m_osRPBSourceFilename); + + if(NULL == pNode) + return NULL; + + // search Global_RFM + char** papszRawRPCList = NULL; + CPLXMLNode* pGRFMNode = CPLSearchXMLNode(pNode, "=Global_RFM"); + + if(pGRFMNode != NULL) + { + papszRawRPCList = ReadXMLToList(pGRFMNode->psChild, papszRawRPCList); + } + + if( NULL == papszRawRPCList ) + { + CPLDestroyXMLNode(pNode); + return NULL; + } + + // format list + char** papszRPB = NULL; + int i, j; + for( i = 0; apszRPBMap[i] != NULL; i += 2 ) + { + papszRPB = CSLAddNameValue(papszRPB, apszRPBMap[i], + CSLFetchNameValue(papszRawRPCList, apszRPBMap[i + 1])); + } + + // merge coefficients + for( i = 0; apszRPCTXT20ValItems[i] != NULL; i++ ) + { + CPLString value; + for( j = 1; j < 21; j++ ) + { + const char* pszValue = CSLFetchNameValue(papszRawRPCList, + CPLSPrintf("Inverse_Model.%s_%d", apszRPCTXT20ValItems[i], j)); // Direct_Model + if(NULL != pszValue) + value = value + " " + CPLString(pszValue); + } + papszRPB = CSLAddNameValue(papszRPB, apszRPCTXT20ValItems[i], value); + } + + CSLDestroy(papszRawRPCList); + CPLDestroyXMLNode(pNode); + return papszRPB; +} diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_pleiades.h b/bazaar/plugin/gdal/gcore/mdreader/reader_pleiades.h new file mode 100644 index 000000000..cdcba6b2b --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_pleiades.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * $Id: reader_pleiades.h 29190 2015-05-13 21:40:30Z bishop $ + * + * Project: GDAL Core + * Purpose: Read metadata from Pleiades imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef READER_PLEIADES_H_INCLUDED +#define READER_PLEIADES_H_INCLUDED + +#include "../gdal_mdreader.h" + +/** +@brief Metadata reader for Pleiades + +TIFF filename: IMG_xxxxxx.tif +Metadata filename: DIM_xxxxxx.XML +RPC filename: RPC_xxxxxx.XML + +Common metadata (from metadata filename): + SatelliteId: MISSION, MISSION_INDEX + AcquisitionDateTime: IMAGING_DATE, IMAGING_TIME + +*/ + +class GDALMDReaderPleiades: public GDALMDReaderBase +{ +public: + GDALMDReaderPleiades(const char *pszPath, char **papszSiblingFiles); + virtual ~GDALMDReaderPleiades(); + virtual const bool HasRequiredFiles() const; + virtual char** GetMetadataFiles() const; +protected: + virtual void LoadMetadata(); + char** LoadRPCXmlFile(); +protected: + CPLString m_osIMDSourceFilename; + CPLString m_osRPBSourceFilename; +}; + +#endif // READER_PLEIADES_H_INCLUDED diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_rapid_eye.cpp b/bazaar/plugin/gdal/gcore/mdreader/reader_rapid_eye.cpp new file mode 100644 index 000000000..beefa373c --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_rapid_eye.cpp @@ -0,0 +1,155 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL Core + * Purpose: Read metadata from RapidEye imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "reader_rapid_eye.h" + +/** + * GDALMDReaderRapidEye() + */ +GDALMDReaderRapidEye::GDALMDReaderRapidEye(const char *pszPath, + char **papszSiblingFiles) : GDALMDReaderBase(pszPath, papszSiblingFiles) +{ + const char* pszDirName = CPLGetDirname(pszPath); + const char* pszBaseName = CPLGetBasename(pszPath); + + const char* pszIMDSourceFilename = CPLFormFilename( pszDirName, + CPLSPrintf("%s_metadata", + pszBaseName), "xml" ); + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osXMLSourceFilename = pszIMDSourceFilename; + } + else + { + pszIMDSourceFilename = CPLFormFilename( pszDirName, + CPLSPrintf("%s_METADATA", + pszBaseName), "XML" ); + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osXMLSourceFilename = pszIMDSourceFilename; + } + } + + if(m_osXMLSourceFilename.size()) + CPLDebug( "MDReaderRapidEye", "XML Filename: %s", + m_osXMLSourceFilename.c_str() ); +} + +/** + * ~GDALMDReaderRapidEye() + */ +GDALMDReaderRapidEye::~GDALMDReaderRapidEye() +{ +} + +/** + * HasRequiredFiles() + */ +const bool GDALMDReaderRapidEye::HasRequiredFiles() const +{ + // check re:EarthObservation + if (!m_osXMLSourceFilename.empty() && + GDALCheckFileHeader(m_osXMLSourceFilename, "re:EarthObservation")) + return true; + + return false; +} + +/** + * GetMetadataFiles() + */ +char** GDALMDReaderRapidEye::GetMetadataFiles() const +{ + char **papszFileList = NULL; + if(!m_osXMLSourceFilename.empty()) + papszFileList= CSLAddString( papszFileList, m_osXMLSourceFilename ); + + return papszFileList; +} + +/** + * LoadMetadata() + */ +void GDALMDReaderRapidEye::LoadMetadata() +{ + if(m_bIsMetadataLoad) + return; + + CPLXMLNode* psNode = CPLParseXMLFile(m_osXMLSourceFilename); + + if(psNode != NULL) + { + CPLXMLNode* pRootNode = CPLSearchXMLNode(psNode, "=re:EarthObservation"); + + if(pRootNode != NULL) + { + m_papszIMDMD = ReadXMLToList(pRootNode->psChild, m_papszIMDMD); + } + CPLDestroyXMLNode(psNode); + } + + m_papszDEFAULTMD = CSLAddNameValue(m_papszDEFAULTMD, MD_NAME_MDTYPE, "RE"); + + m_bIsMetadataLoad = true; + + if(NULL == m_papszIMDMD) + { + return; + } + + //extract imagery metadata + const char* pszSatId = CSLFetchNameValue(m_papszIMDMD, + "gml:using.eop:EarthObservationEquipment.eop:platform.eop:Platform.eop:serialIdentifier"); + if(NULL != pszSatId) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLStripQuotes(pszSatId)); + } + + const char* pszDateTime = CSLFetchNameValue(m_papszIMDMD, + "gml:using.eop:EarthObservationEquipment.eop:acquisitionParameters.re:Acquisition.re:acquisitionDateTime"); + if(NULL != pszDateTime) + { + char buffer[80]; + time_t timeMid = GetAcquisitionTimeFromString(pszDateTime); + strftime (buffer, 80, MD_DATETIMEFORMAT, localtime(&timeMid)); + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, buffer); + } + + const char* pszCC = CSLFetchNameValue(m_papszIMDMD, + "gml:resultOf.re:EarthObservationResult.opt:cloudCoverPercentage"); + if(NULL != pszSatId) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_CLOUDCOVER, pszCC); + } + +} diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_rapid_eye.h b/bazaar/plugin/gdal/gcore/mdreader/reader_rapid_eye.h new file mode 100644 index 000000000..bf94321ca --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_rapid_eye.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL Core + * Purpose: Read metadata from RapidEye imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef READER_RAPID_EYE_H_INCLUDED +#define READER_RAPID_EYE_H_INCLUDED + +#include "../gdal_mdreader.h" + +/** +@brief Metadata reader for RapidEye + +TIFF filename: aaaaaaaa.tif +Metadata filename: aaaaaaaa_metadata.xml +RPC filename: + +Common metadata (from metadata filename): + SatelliteId: eop:serialIdentifier + CloudCover: opt:cloudCoverPercentage + AcquisitionDateTime: re:acquisitionDateTime +*/ + +class GDALMDReaderRapidEye: public GDALMDReaderBase +{ +public: + GDALMDReaderRapidEye(const char *pszPath, char **papszSiblingFiles); + virtual ~GDALMDReaderRapidEye(); + virtual const bool HasRequiredFiles() const; + virtual char** GetMetadataFiles() const; +protected: + virtual void LoadMetadata(); +protected: + CPLString m_osXMLSourceFilename; +}; + +#endif // READER_RAPID_EYE_H_INCLUDED + diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_rdk1.cpp b/bazaar/plugin/gdal/gcore/mdreader/reader_rdk1.cpp new file mode 100644 index 000000000..19d797552 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_rdk1.cpp @@ -0,0 +1,210 @@ +/****************************************************************************** + * $Id: reader_rdk1.cpp 29145 2015-05-04 10:00:40Z rouault $ + * + * Project: GDAL Core + * Purpose: Read metadata from Resurs-DK1 imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "reader_rdk1.h" + +CPL_CVSID("$Id: reader_rdk1.cpp 29145 2015-05-04 10:00:40Z rouault $"); + +/** + * GDALMDReaderResursDK1() + */ +GDALMDReaderResursDK1::GDALMDReaderResursDK1(const char *pszPath, + char **papszSiblingFiles) : GDALMDReaderBase(pszPath, papszSiblingFiles) +{ + m_osXMLSourceFilename = GDALFindAssociatedFile( pszPath, "XML", + papszSiblingFiles, 0 ); + if( m_osXMLSourceFilename.size() ) + CPLDebug( "MDReaderResursDK1", "XML Filename: %s", + m_osXMLSourceFilename.c_str() ); +} + +/** + * ~GDALMDReaderResursDK1() + */ +GDALMDReaderResursDK1::~GDALMDReaderResursDK1() +{ +} + +/** + * HasRequiredFiles() + */ +const bool GDALMDReaderResursDK1::HasRequiredFiles() const +{ + // check + if (!m_osXMLSourceFilename.empty() && + GDALCheckFileHeader(m_osXMLSourceFilename, "")) + return true; + + return false; +} + +/** + * GetMetadataFiles() + */ +char** GDALMDReaderResursDK1::GetMetadataFiles() const +{ + char **papszFileList = NULL; + if(!m_osXMLSourceFilename.empty()) + papszFileList= CSLAddString( papszFileList, m_osXMLSourceFilename ); + + return papszFileList; +} + +/** + * LoadMetadata() + */ +void GDALMDReaderResursDK1::LoadMetadata() +{ + if(m_bIsMetadataLoad) + return; + + if (!m_osXMLSourceFilename.empty()) + { + CPLXMLNode* psNode = CPLParseXMLFile(m_osXMLSourceFilename); + + if(psNode != NULL) + { + CPLXMLNode* pMSPRootNode = CPLSearchXMLNode(psNode, "=MSP_ROOT"); + + if(pMSPRootNode != NULL) + { + m_papszIMDMD = ReadXMLToList(pMSPRootNode, m_papszIMDMD, "MSP_ROOT"); + } + CPLDestroyXMLNode(psNode); + } + } + + m_papszDEFAULTMD = CSLAddNameValue(m_papszDEFAULTMD, MD_NAME_MDTYPE, "MSP"); + + m_bIsMetadataLoad = true; + + if(NULL == m_papszIMDMD) + { + return; + } + + //extract imagery metadata + const char* pszSatId = CSLFetchNameValue(m_papszIMDMD, "MSP_ROOT.cCodeKA"); + if(NULL != pszSatId) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_SATELLITE, + CPLStripQuotes(pszSatId)); + } + + + const char* pszDate = CSLFetchNameValue(m_papszIMDMD, + "MSP_ROOT.Normal.dSceneDate"); + + if(NULL != pszDate) + { + const char* pszTime = CSLFetchNameValue(m_papszIMDMD, + "MSP_ROOT.Normal.tSceneTime"); + if(NULL == pszTime) + pszTime = "00:00:00.000000"; + + char buffer[80]; + time_t timeMid = GetAcquisitionTimeFromString(CPLSPrintf( "%s %s", + pszDate, pszTime)); + strftime (buffer, 80, MD_DATETIMEFORMAT, localtime(&timeMid)); + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, buffer); + } + + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_CLOUDCOVER, + MD_CLOUDCOVER_NA); +} + +/** + * GetAcqisitionTimeFromString() + */ +const time_t GDALMDReaderResursDK1::GetAcquisitionTimeFromString( + const char* pszDateTime) +{ + if(NULL == pszDateTime) + return 0; + + int iYear; + int iMonth; + int iDay; + int iHours; + int iMin; + int iSec; + +// string exampe: +// tSceneTime = 10:21:36.000000 +// dSceneDate = 16/9/2008 +// + + int r = sscanf ( pszDateTime, "%d/%d/%d %d:%d:%d.%*s", + &iDay, &iMonth, &iYear, &iHours, &iMin, &iSec); + + if (r != 6) + return 0; + + struct tm tmDateTime; + tmDateTime.tm_sec = iSec; + tmDateTime.tm_min = iMin; + tmDateTime.tm_hour = iHours; + tmDateTime.tm_mday = iDay; + tmDateTime.tm_mon = iMonth - 1; + tmDateTime.tm_year = iYear - 1900; + tmDateTime.tm_isdst = -1; + + return mktime(&tmDateTime) - 10800; // int UTC+3 MSK +} + +char** GDALMDReaderResursDK1::AddXMLNameValueToList(char** papszList, + const char *pszName, + const char *pszValue) +{ + char** papszTokens = CSLTokenizeString2( pszValue, "\n", + CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + + for(int i = 0; papszTokens[i] != NULL; i++ ) + { + + char** papszSubTokens = CSLTokenizeString2( papszTokens[i], "=", + CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES ); + if(CSLCount(papszSubTokens) < 2) + { + CSLDestroy( papszSubTokens ); + continue; + } + + papszList = CSLAddNameValue(papszList, CPLSPrintf("%s.%s", pszName, + papszSubTokens[0]), + papszSubTokens[1]); + CSLDestroy( papszSubTokens ); + } + + CSLDestroy( papszTokens ); + + return papszList; +} diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_rdk1.h b/bazaar/plugin/gdal/gcore/mdreader/reader_rdk1.h new file mode 100644 index 000000000..1859e0c0c --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_rdk1.h @@ -0,0 +1,65 @@ +/****************************************************************************** + * $Id: reader_rdk1.h 29190 2015-05-13 21:40:30Z bishop $ + * + * Project: GDAL Core + * Purpose: Read metadata from Resurs-DK1 imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef READER_RDK1_H_INCLUDED +#define READER_RDK1_H_INCLUDED + +#include "../gdal_mdreader.h" + +/** +@brief Metadata reader for RDK1 + +TIFF filename: aaaaaaaaaa.tif +Metadata filename: aaaaaaaaaa.xml +RPC filename: + +Common metadata (from metadata filename): + SatelliteId: cCodeKA + AcquisitionDateTime: dSceneDate, tSceneTime +*/ + +class GDALMDReaderResursDK1: public GDALMDReaderBase +{ +public: + GDALMDReaderResursDK1(const char *pszPath, char **papszSiblingFiles); + virtual ~GDALMDReaderResursDK1(); + virtual const bool HasRequiredFiles() const; + virtual char** GetMetadataFiles() const; +protected: + virtual void LoadMetadata(); + virtual const time_t GetAcquisitionTimeFromString(const char* pszDateTime); + virtual char** AddXMLNameValueToList(char** papszList, const char *pszName, + const char *pszValue); +protected: + CPLString m_osXMLSourceFilename; +}; + +#endif // READER_RDK1_H_INCLUDED + diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_spot.cpp b/bazaar/plugin/gdal/gcore/mdreader/reader_spot.cpp new file mode 100644 index 000000000..f56276a24 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_spot.cpp @@ -0,0 +1,315 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL Core + * Purpose: Read metadata from Spot imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "reader_spot.h" + +/** + * GDALMDReaderSpot() + */ +GDALMDReaderSpot::GDALMDReaderSpot(const char *pszPath, + char **papszSiblingFiles) : GDALMDReaderPleiades(pszPath, papszSiblingFiles) +{ + const char* pszIMDSourceFilename; + const char* pszDirName = CPLGetDirname(pszPath); + + if(m_osIMDSourceFilename.empty()) + { + pszIMDSourceFilename = CPLFormFilename( pszDirName, "METADATA.DIM", NULL ); + + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszIMDSourceFilename; + } + else + { + pszIMDSourceFilename = CPLFormFilename( pszDirName, "metadata.dim", NULL ); + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszIMDSourceFilename; + } + } + } + + // if the file name ended on METADATA.DIM + // Linux specific + // example: R2_CAT_091028105025131_1\METADATA.DIM + if(m_osIMDSourceFilename.empty()) + { + if(EQUAL(CPLGetFilename(pszPath), "IMAGERY.TIF")) + { + pszIMDSourceFilename = CPLSPrintf( "%s\\METADATA.DIM", + CPLGetPath(pszPath)); + + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszIMDSourceFilename; + } + else + { + pszIMDSourceFilename = CPLSPrintf( "%s\\metadata.dim", + CPLGetPath(pszPath)); + if (CPLCheckForFile((char*)pszIMDSourceFilename, papszSiblingFiles)) + { + m_osIMDSourceFilename = pszIMDSourceFilename; + } + } + } + } + + if(m_osIMDSourceFilename.size()) + CPLDebug( "MDReaderSpot", "IMD Filename: %s", + m_osIMDSourceFilename.c_str() ); +} + +/** + * ~GDALMDReaderSpot() + */ +GDALMDReaderSpot::~GDALMDReaderSpot() +{ +} + +/** + * LoadMetadata() + */ +void GDALMDReaderSpot::LoadMetadata() +{ + if(m_bIsMetadataLoad) + return; + + if (!m_osIMDSourceFilename.empty()) + { + CPLXMLNode* psNode = CPLParseXMLFile(m_osIMDSourceFilename); + + if(psNode != NULL) + { + CPLXMLNode* psisdNode = CPLSearchXMLNode(psNode, "=Dimap_Document"); + + if(psisdNode != NULL) + { + m_papszIMDMD = ReadXMLToList(psisdNode->psChild, m_papszIMDMD); + } + CPLDestroyXMLNode(psNode); + } + } + + m_papszDEFAULTMD = CSLAddNameValue(m_papszDEFAULTMD, MD_NAME_MDTYPE, "DIMAP"); + + m_bIsMetadataLoad = true; + + if(NULL == m_papszIMDMD) + { + return; + } + + //extract imagery metadata + int nCounter = -1; + const char* pszSatId1 = CSLFetchNameValue(m_papszIMDMD, + "Dataset_Sources.Source_Information.Scene_Source.MISSION"); + if(NULL == pszSatId1) + { + nCounter = 1; + for(int i = 0; i < 5; i++) + { + pszSatId1 = CSLFetchNameValue(m_papszIMDMD, + CPLSPrintf("Dataset_Sources.Source_Information_%d.Scene_Source.MISSION", + nCounter)); + if(NULL != pszSatId1) + break; + nCounter++; + } + } + + + const char* pszSatId2; + if(nCounter == -1) + pszSatId2 = CSLFetchNameValue(m_papszIMDMD, + "Dataset_Sources.Source_Information.Scene_Source.MISSION_INDEX"); + else + pszSatId2 = CSLFetchNameValue(m_papszIMDMD, CPLSPrintf( + "Dataset_Sources.Source_Information_%d.Scene_Source.MISSION_INDEX", + nCounter)); + + if(NULL != pszSatId1 && NULL != pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLSPrintf( "%s %s", + CPLStripQuotes(pszSatId1).c_str(), + CPLStripQuotes(pszSatId2).c_str())); + } + else if(NULL != pszSatId1 && NULL == pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLStripQuotes(pszSatId1)); + } + else if(NULL == pszSatId1 && NULL != pszSatId2) + { + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_SATELLITE, CPLStripQuotes(pszSatId2)); + } + + + const char* pszDate; + if(nCounter == -1) + pszDate = CSLFetchNameValue(m_papszIMDMD, + "Dataset_Sources.Source_Information.Scene_Source.IMAGING_DATE"); + else + pszDate = CSLFetchNameValue(m_papszIMDMD, CPLSPrintf( + "Dataset_Sources.Source_Information_%d.Scene_Source.IMAGING_DATE", + nCounter)); + + if(NULL != pszDate) + { + const char* pszTime; + if(nCounter == -1) + pszTime = CSLFetchNameValue(m_papszIMDMD, + "Dataset_Sources.Source_Information.Scene_Source.IMAGING_TIME"); + else + pszTime = CSLFetchNameValue(m_papszIMDMD, CPLSPrintf( + "Dataset_Sources.Source_Information_%d.Scene_Source.IMAGING_TIME", + nCounter)); + + if(NULL == pszTime) + pszTime = "00:00:00.0Z"; + + char buffer[80]; + time_t timeMid = GetAcquisitionTimeFromString(CPLSPrintf( "%sT%s", + pszDate, pszTime)); + strftime (buffer, 80, MD_DATETIMEFORMAT, localtime(&timeMid)); + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, + MD_NAME_ACQDATETIME, buffer); + } + + m_papszIMAGERYMD = CSLAddNameValue(m_papszIMAGERYMD, MD_NAME_CLOUDCOVER, + MD_CLOUDCOVER_NA); +} + + +/** + * ReadXMLToList() + */ +char** GDALMDReaderSpot::ReadXMLToList(CPLXMLNode* psNode, char** papszList, + const char* pszName) +{ + if(NULL == psNode) + return papszList; + + if (psNode->eType == CXT_Text) + { + if(!EQUAL(pszName, "")) + return AddXMLNameValueToList(papszList, pszName, psNode->pszValue); + } + + if (psNode->eType == CXT_Element && !EQUAL(psNode->pszValue, "Data_Strip")) + { + int nAddIndex = 0; + bool bReset = false; + for(CPLXMLNode* psChildNode = psNode->psChild; NULL != psChildNode; + psChildNode = psChildNode->psNext) + { + if (psChildNode->eType == CXT_Element) + { + // check name duplicates + if(NULL != psChildNode->psNext) + { + if(bReset) + { + bReset = false; + nAddIndex = 0; + } + + if(EQUAL(psChildNode->pszValue, psChildNode->psNext->pszValue)) + { + nAddIndex++; + } + else + { // the name changed + + if(nAddIndex > 0) + { + bReset = true; + nAddIndex++; + } + } + } + else + { + if(nAddIndex > 0) + { + nAddIndex++; + } + } + + char szName[512]; + if(nAddIndex > 0) + { + CPLsnprintf( szName, 511, "%s_%d", psChildNode->pszValue, + nAddIndex); + } + else + { + CPLStrlcpy(szName, psChildNode->pszValue, 511); + } + + char szNameNew[512]; + if(CPLStrnlen( pszName, 511 ) > 0) //if no prefix just set name to node name + { + CPLsnprintf( szNameNew, 511, "%s.%s", pszName, szName ); + } + else + { + CPLsnprintf( szNameNew, 511, "%s.%s", psNode->pszValue, szName ); + } + + papszList = ReadXMLToList(psChildNode, papszList, szNameNew); + } + else + { + // Text nodes should always have name + if(EQUAL(pszName, "")) + { + papszList = ReadXMLToList(psChildNode, papszList, psNode->pszValue); + } + else + { + papszList = ReadXMLToList(psChildNode, papszList, pszName); + } + } + } + } + + // proceed next only on top level + + if(NULL != psNode->psNext && EQUAL(pszName, "")) + { + papszList = ReadXMLToList(psNode->psNext, papszList, pszName); + } + + return papszList; +} diff --git a/bazaar/plugin/gdal/gcore/mdreader/reader_spot.h b/bazaar/plugin/gdal/gcore/mdreader/reader_spot.h new file mode 100644 index 000000000..c8809c39f --- /dev/null +++ b/bazaar/plugin/gdal/gcore/mdreader/reader_spot.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * $Id$ + * + * Project: GDAL Core + * Purpose: Read metadata from Spot imagery. + * Author: Alexander Lisovenko + * Author: Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2014-2015 NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef READER_SPOT_H_INCLUDED +#define READER_SPOT_H_INCLUDED + +#include "reader_pleiades.h" + +/** +@brief Metadata reader for Spot + +TIFF filename: aaaaaaaaaa.tif +Metadata filename: METADATA.DIM +RPC filename: + +Common metadata (from metadata filename): + SatelliteId: MISSION, MISSION_INDEX + AcquisitionDateTime: IMAGING_DATE, IMAGING_TIME +*/ + +class GDALMDReaderSpot: public GDALMDReaderPleiades +{ +public: + GDALMDReaderSpot(const char *pszPath, char **papszSiblingFiles); + virtual ~GDALMDReaderSpot(); +protected: + virtual void LoadMetadata(); + virtual char** ReadXMLToList(CPLXMLNode* psNode, char** papszList, + const char* pszName = ""); +}; + +#endif // READER_SPOT_H_INCLUDED + diff --git a/bazaar/plugin/gdal/gcore/overview.cpp b/bazaar/plugin/gdal/gcore/overview.cpp new file mode 100644 index 000000000..4dbda98cb --- /dev/null +++ b/bazaar/plugin/gdal/gcore/overview.cpp @@ -0,0 +1,3117 @@ +/****************************************************************************** + * $Id: overview.cpp 28142 2014-12-14 20:09:42Z goatbar $ + * + * Project: GDAL Core + * Purpose: Helper code to implement overview support in different drivers. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdalwarper.h" + +CPL_CVSID("$Id: overview.cpp 28142 2014-12-14 20:09:42Z goatbar $"); + +/************************************************************************/ +/* GDALResampleChunk32R_Near() */ +/************************************************************************/ + +template +static CPLErr +GDALResampleChunk32R_NearT( double dfXRatioDstToSrc, + double dfYRatioDstToSrc, + GDALDataType eWrkDataType, + T * pChunk, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, + int nDstXOff, int nDstXOff2, + int nDstYOff, int nDstYOff2, + GDALRasterBand * poOverview) + +{ + CPLErr eErr = CE_None; + + int nDstXWidth = nDstXOff2 - nDstXOff; + +/* -------------------------------------------------------------------- */ +/* Allocate scanline buffer. */ +/* -------------------------------------------------------------------- */ + + T* pDstScanline = (T *) VSIMalloc(nDstXWidth * (GDALGetDataTypeSize(eWrkDataType) / 8)); + int* panSrcXOff = (int*)VSIMalloc(nDstXWidth * sizeof(int)); + + if( pDstScanline == NULL || panSrcXOff == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALResampleChunk32R: Out of memory for line buffer." ); + VSIFree(pDstScanline); + VSIFree(panSrcXOff); + return CE_Failure; + } + +/* ==================================================================== */ +/* Precompute inner loop constants. */ +/* ==================================================================== */ + int iDstPixel; + for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ ) + { + int nSrcXOff; + + nSrcXOff = + (int) (0.5 + iDstPixel * dfXRatioDstToSrc); + if ( nSrcXOff < nChunkXOff ) + nSrcXOff = nChunkXOff; + + panSrcXOff[iDstPixel - nDstXOff] = nSrcXOff; + } + +/* ==================================================================== */ +/* Loop over destination scanlines. */ +/* ==================================================================== */ + for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ ) + { + T *pSrcScanline; + int nSrcYOff; + + nSrcYOff = (int) (0.5 + iDstLine * dfYRatioDstToSrc); + if ( nSrcYOff < nChunkYOff ) + nSrcYOff = nChunkYOff; + + pSrcScanline = pChunk + ((nSrcYOff-nChunkYOff) * nChunkXSize) - nChunkXOff; + +/* -------------------------------------------------------------------- */ +/* Loop over destination pixels */ +/* -------------------------------------------------------------------- */ + for( iDstPixel = 0; iDstPixel < nDstXWidth; iDstPixel++ ) + { + pDstScanline[iDstPixel] = pSrcScanline[panSrcXOff[iDstPixel]]; + } + + eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXWidth, 1, + pDstScanline, nDstXWidth, 1, eWrkDataType, + 0, 0, NULL ); + } + + CPLFree( pDstScanline ); + CPLFree( panSrcXOff ); + + return eErr; +} + +static CPLErr +GDALResampleChunk32R_Near( double dfXRatioDstToSrc, + double dfYRatioDstToSrc, + CPL_UNUSED double dfSrcXDelta, + CPL_UNUSED double dfSrcYDelta, + GDALDataType eWrkDataType, + void * pChunk, + CPL_UNUSED GByte * pabyChunkNodataMask_unused, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, CPL_UNUSED int nChunkYSize, + int nDstXOff, int nDstXOff2, + int nDstYOff, int nDstYOff2, + GDALRasterBand * poOverview, + CPL_UNUSED const char * pszResampling_unused, + CPL_UNUSED int bHasNoData_unused, + CPL_UNUSED float fNoDataValue_unused, + CPL_UNUSED GDALColorTable* poColorTable_unused, + CPL_UNUSED GDALDataType eSrcDataType) +{ + if (eWrkDataType == GDT_Byte) + return GDALResampleChunk32R_NearT(dfXRatioDstToSrc, + dfYRatioDstToSrc, + eWrkDataType, + (GByte *) pChunk, + nChunkXOff, nChunkXSize, + nChunkYOff, + nDstXOff, nDstXOff2, + nDstYOff, nDstYOff2, + poOverview); + else if (eWrkDataType == GDT_UInt16) + return GDALResampleChunk32R_NearT(dfXRatioDstToSrc, + dfYRatioDstToSrc, + eWrkDataType, + (GInt16 *) pChunk, + nChunkXOff, nChunkXSize, + nChunkYOff, + nDstXOff, nDstXOff2, + nDstYOff, nDstYOff2, + poOverview); + else if (eWrkDataType == GDT_Float32) + return GDALResampleChunk32R_NearT(dfXRatioDstToSrc, + dfYRatioDstToSrc, + eWrkDataType, + (float *) pChunk, + nChunkXOff, nChunkXSize, + nChunkYOff, + nDstXOff, nDstXOff2, + nDstYOff, nDstYOff2, + poOverview); + + CPLAssert(0); + return CE_Failure; +} + +/************************************************************************/ +/* GDALFindBestEntry() */ +/************************************************************************/ + +static int GDALFindBestEntry(int nEntryCount, const GDALColorEntry* aEntries, + int nR, int nG, int nB) +{ + int i; + int nMinDist = 0; + int iBestEntry = 0; + for(i=0;i +static CPLErr +GDALResampleChunk32R_AverageT( double dfXRatioDstToSrc, + double dfYRatioDstToSrc, + GDALDataType eWrkDataType, + T* pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + int nDstXOff, int nDstXOff2, + int nDstYOff, int nDstYOff2, + GDALRasterBand * poOverview, + const char * pszResampling, + int bHasNoData, float fNoDataValue, + GDALColorTable* poColorTable) +{ + CPLErr eErr = CE_None; + + int bBit2Grayscale = EQUALN(pszResampling,"AVERAGE_BIT2GRAYSCALE",13); + if (bBit2Grayscale) + poColorTable = NULL; + + int nOXSize, nOYSize; + T *pDstScanline; + + T tNoDataValue = (T)fNoDataValue; + if (!bHasNoData) + tNoDataValue = 0; + + nOXSize = poOverview->GetXSize(); + nOYSize = poOverview->GetYSize(); + + int nChunkRightXOff = nChunkXOff + nChunkXSize; + int nChunkBottomYOff = nChunkYOff + nChunkYSize; + int nDstXWidth = nDstXOff2 - nDstXOff; + +/* -------------------------------------------------------------------- */ +/* Allocate scanline buffer. */ +/* -------------------------------------------------------------------- */ + + pDstScanline = (T *) VSIMalloc(nDstXWidth * (GDALGetDataTypeSize(eWrkDataType) / 8)); + int* panSrcXOffShifted = (int*)VSIMalloc(2 * nDstXWidth * sizeof(int)); + + if( pDstScanline == NULL || panSrcXOffShifted == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALResampleChunk32R: Out of memory for line buffer." ); + VSIFree(pDstScanline); + VSIFree(panSrcXOffShifted); + return CE_Failure; + } + + int nEntryCount = 0; + GDALColorEntry* aEntries = NULL; + if (poColorTable) + { + int i; + nEntryCount = poColorTable->GetColorEntryCount(); + aEntries = (GDALColorEntry* )CPLMalloc(sizeof(GDALColorEntry) * nEntryCount); + for(i=0;iGetColorEntryAsRGB(i, &aEntries[i]); + } + } + +/* ==================================================================== */ +/* Precompute inner loop constants. */ +/* ==================================================================== */ + int iDstPixel; + int bSrcXSpacingIsTwo = TRUE; + for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ ) + { + int nSrcXOff, nSrcXOff2; + + nSrcXOff = + (int) (0.5 + iDstPixel * dfXRatioDstToSrc); + if ( nSrcXOff < nChunkXOff ) + nSrcXOff = nChunkXOff; + nSrcXOff2 = (int) + (0.5 + (iDstPixel+1)* dfXRatioDstToSrc); + if( nSrcXOff2 == nSrcXOff ) + nSrcXOff2 ++; + + if( nSrcXOff2 > nChunkRightXOff || (dfXRatioDstToSrc > 1 && iDstPixel == nOXSize-1) ) + { + if( nSrcXOff == nChunkRightXOff && nChunkRightXOff - 1 >= nChunkXOff ) + nSrcXOff = nChunkRightXOff - 1; + nSrcXOff2 = nChunkRightXOff; + } + + panSrcXOffShifted[2 * (iDstPixel - nDstXOff)] = nSrcXOff - nChunkXOff; + panSrcXOffShifted[2 * (iDstPixel - nDstXOff) + 1] = nSrcXOff2 - nChunkXOff; + if (nSrcXOff2 - nSrcXOff != 2) + bSrcXSpacingIsTwo = FALSE; + } + +/* ==================================================================== */ +/* Loop over destination scanlines. */ +/* ==================================================================== */ + for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ ) + { + int nSrcYOff, nSrcYOff2 = 0; + + nSrcYOff = (int) (0.5 + iDstLine * dfYRatioDstToSrc); + if ( nSrcYOff < nChunkYOff ) + nSrcYOff = nChunkYOff; + + nSrcYOff2 = + (int) (0.5 + (iDstLine+1) * dfYRatioDstToSrc); + if( nSrcYOff2 == nSrcYOff ) + nSrcYOff2 ++; + + if( nSrcYOff2 > nChunkBottomYOff || (dfYRatioDstToSrc > 1 && iDstLine == nOYSize-1) ) + { + if( nSrcYOff == nChunkBottomYOff && nChunkBottomYOff - 1 >= nChunkYOff ) + nSrcYOff = nChunkBottomYOff - 1; + nSrcYOff2 = nChunkBottomYOff; + } + if( nSrcYOff2 <= nSrcYOff ) + CPLDebug("GDAL", "nSrcYOff=%d nSrcYOff2=%d", nSrcYOff, nSrcYOff2); + +/* -------------------------------------------------------------------- */ +/* Loop over destination pixels */ +/* -------------------------------------------------------------------- */ + if (poColorTable == NULL) + { + if (bSrcXSpacingIsTwo && nSrcYOff2 == nSrcYOff + 2 && + pabyChunkNodataMask == NULL && (eWrkDataType == GDT_Byte || eWrkDataType == GDT_UInt16)) + { + /* Optimized case : no nodata, overview by a factor of 2 and regular x and y src spacing */ + T* pSrcScanlineShifted = pChunk + panSrcXOffShifted[0] + (nSrcYOff - nChunkYOff) * nChunkXSize; + for( iDstPixel = 0; iDstPixel < nDstXWidth; iDstPixel++ ) + { + Tsum nTotal; + + nTotal = pSrcScanlineShifted[0]; + nTotal += pSrcScanlineShifted[1]; + nTotal += pSrcScanlineShifted[nChunkXSize]; + nTotal += pSrcScanlineShifted[1+nChunkXSize]; + + pDstScanline[iDstPixel] = (T) ((nTotal + 2) / 4); + pSrcScanlineShifted += 2; + } + } + else + { + nSrcYOff -= nChunkYOff; + nSrcYOff2 -= nChunkYOff; + + for( iDstPixel = 0; iDstPixel < nDstXWidth; iDstPixel++ ) + { + int nSrcXOff = panSrcXOffShifted[2 * iDstPixel], + nSrcXOff2 = panSrcXOffShifted[2 * iDstPixel + 1]; + + T val; + Tsum dfTotal = 0; + int nCount = 0, iX, iY; + + for( iY = nSrcYOff; iY < nSrcYOff2; iY++ ) + { + for( iX = nSrcXOff; iX < nSrcXOff2; iX++ ) + { + val = pChunk[iX + iY *nChunkXSize]; + if (pabyChunkNodataMask == NULL || + pabyChunkNodataMask[iX + iY *nChunkXSize]) + { + dfTotal += val; + nCount++; + } + } + } + + if( nCount == 0 ) + pDstScanline[iDstPixel] = tNoDataValue; + else if (eWrkDataType == GDT_Byte || eWrkDataType == GDT_UInt16) + pDstScanline[iDstPixel] = (T) ((dfTotal + nCount / 2) / nCount); + else + pDstScanline[iDstPixel] = (T) (dfTotal / nCount); + } + } + } + else + { + nSrcYOff -= nChunkYOff; + nSrcYOff2 -= nChunkYOff; + + for( iDstPixel = 0; iDstPixel < nDstXWidth; iDstPixel++ ) + { + int nSrcXOff = panSrcXOffShifted[2 * iDstPixel], + nSrcXOff2 = panSrcXOffShifted[2 * iDstPixel + 1]; + + T val; + int nTotalR = 0, nTotalG = 0, nTotalB = 0; + int nCount = 0, iX, iY; + + for( iY = nSrcYOff; iY < nSrcYOff2; iY++ ) + { + for( iX = nSrcXOff; iX < nSrcXOff2; iX++ ) + { + val = pChunk[iX + iY *nChunkXSize]; + if (bHasNoData == FALSE || val != tNoDataValue) + { + int nVal = (int)val; + if (nVal >= 0 && nVal < nEntryCount) + { + nTotalR += aEntries[nVal].c1; + nTotalG += aEntries[nVal].c2; + nTotalB += aEntries[nVal].c3; + nCount++; + } + } + } + } + + if( nCount == 0 ) + pDstScanline[iDstPixel] = tNoDataValue; + else + { + int nR = (nTotalR + nCount / 2) / nCount, + nG = (nTotalG + nCount / 2) / nCount, + nB = (nTotalB + nCount / 2) / nCount; + pDstScanline[iDstPixel] = (T)GDALFindBestEntry( + nEntryCount, aEntries, nR, nG, nB); + } + } + } + + eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXWidth, 1, + pDstScanline, nDstXWidth, 1, eWrkDataType, + 0, 0, NULL ); + } + + CPLFree( pDstScanline ); + CPLFree( aEntries ); + CPLFree( panSrcXOffShifted ); + + return eErr; +} + +static CPLErr +GDALResampleChunk32R_Average( double dfXRatioDstToSrc, double dfYRatioDstToSrc, + CPL_UNUSED double dfSrcXDelta, + CPL_UNUSED double dfSrcYDelta, + GDALDataType eWrkDataType, + void * pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + int nDstXOff, int nDstXOff2, + int nDstYOff, int nDstYOff2, + GDALRasterBand * poOverview, + const char * pszResampling, + int bHasNoData, float fNoDataValue, + GDALColorTable* poColorTable, + CPL_UNUSED GDALDataType eSrcDataType) +{ + if (eWrkDataType == GDT_Byte) + return GDALResampleChunk32R_AverageT(dfXRatioDstToSrc, dfYRatioDstToSrc, + eWrkDataType, + (GByte *) pChunk, + pabyChunkNodataMask, + nChunkXOff, nChunkXSize, + nChunkYOff, nChunkYSize, + nDstXOff, nDstXOff2, + nDstYOff, nDstYOff2, + poOverview, + pszResampling, + bHasNoData, fNoDataValue, + poColorTable); + else if (eWrkDataType == GDT_UInt16 && dfXRatioDstToSrc * dfYRatioDstToSrc < 65536 ) + return GDALResampleChunk32R_AverageT(dfXRatioDstToSrc, dfYRatioDstToSrc, + eWrkDataType, + (GUInt16 *) pChunk, + pabyChunkNodataMask, + nChunkXOff, nChunkXSize, + nChunkYOff, nChunkYSize, + nDstXOff, nDstXOff2, + nDstYOff, nDstYOff2, + poOverview, + pszResampling, + bHasNoData, fNoDataValue, + poColorTable); + else if (eWrkDataType == GDT_Float32) + return GDALResampleChunk32R_AverageT(dfXRatioDstToSrc, dfYRatioDstToSrc, + eWrkDataType, + (float *) pChunk, + pabyChunkNodataMask, + nChunkXOff, nChunkXSize, + nChunkYOff, nChunkYSize, + nDstXOff, nDstXOff2, + nDstYOff, nDstYOff2, + poOverview, + pszResampling, + bHasNoData, fNoDataValue, + poColorTable); + + CPLAssert(0); + return CE_Failure; +} + +/************************************************************************/ +/* GDALResampleChunk32R_Gauss() */ +/************************************************************************/ + +static CPLErr +GDALResampleChunk32R_Gauss( double dfXRatioDstToSrc, double dfYRatioDstToSrc, + CPL_UNUSED double dfSrcXDelta, + CPL_UNUSED double dfSrcYDelta, + CPL_UNUSED GDALDataType eWrkDataType, + void * pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + int nDstXOff, int nDstXOff2, + int nDstYOff, int nDstYOff2, + GDALRasterBand * poOverview, + CPL_UNUSED const char * pszResampling, + int bHasNoData, float fNoDataValue, + GDALColorTable* poColorTable, + CPL_UNUSED GDALDataType eSrcDataType) + +{ + CPLErr eErr = CE_None; + + float * pafChunk = (float*) pChunk; + +/* -------------------------------------------------------------------- */ +/* Create the filter kernel and allocate scanline buffer. */ +/* -------------------------------------------------------------------- */ + int nOXSize, nOYSize; + float *pafDstScanline; + int nGaussMatrixDim = 3; + const int *panGaussMatrix; + static const int anGaussMatrix3x3[] ={ + 1,2,1, + 2,4,2, + 1,2,1 + }; + static const int anGaussMatrix5x5[] = { + 1,4,6,4,1, + 4,16,24,16,4, + 6,24,36,24,6, + 4,16,24,16,4, + 1,4,6,4,1}; + static const int anGaussMatrix7x7[] = { + 1,6,15,20,15,6,1, + 6,36,90,120,90,36,6, + 15,90,225,300,225,90,15, + 20,120,300,400,300,120,20, + 15,90,225,300,225,90,15, + 6,36,90,120,90,36,6, + 1,6,15,20,15,6,1}; + + nOXSize = poOverview->GetXSize(); + nOYSize = poOverview->GetYSize(); + int nResYFactor = (int) (0.5 + dfYRatioDstToSrc); + + // matrix for gauss filter + if(nResYFactor <= 2 ) + { + panGaussMatrix = anGaussMatrix3x3; + nGaussMatrixDim=3; + } + else if (nResYFactor <= 4) + { + panGaussMatrix = anGaussMatrix5x5; + nGaussMatrixDim=5; + } + else + { + panGaussMatrix = anGaussMatrix7x7; + nGaussMatrixDim=7; + } + + pafDstScanline = (float *) VSIMalloc((nDstXOff2 - nDstXOff) * sizeof(float)); + if( pafDstScanline == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALResampleChunk32R: Out of memory for line buffer." ); + return CE_Failure; + } + + int nEntryCount = 0; + GDALColorEntry* aEntries = NULL; + if (poColorTable) + { + int i; + nEntryCount = poColorTable->GetColorEntryCount(); + aEntries = (GDALColorEntry* )CPLMalloc(sizeof(GDALColorEntry) * nEntryCount); + for(i=0;iGetColorEntryAsRGB(i, &aEntries[i]); + } + } + + int nChunkRightXOff = nChunkXOff + nChunkXSize; + int nChunkBottomYOff = nChunkYOff + nChunkYSize; + +/* ==================================================================== */ +/* Loop over destination scanlines. */ +/* ==================================================================== */ + for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ ) + { + float *pafSrcScanline; + GByte *pabySrcScanlineNodataMask; + int nSrcYOff, nSrcYOff2 = 0, iDstPixel; + + nSrcYOff = (int) (0.5 + iDstLine * dfYRatioDstToSrc); + nSrcYOff2 = (int) (0.5 + (iDstLine+1) * dfYRatioDstToSrc) + 1; + + if( nSrcYOff < nChunkYOff ) + { + nSrcYOff = nChunkYOff; + nSrcYOff2++; + } + + int iSizeY = nSrcYOff2 - nSrcYOff; + nSrcYOff = nSrcYOff + iSizeY/2 - nGaussMatrixDim/2; + nSrcYOff2 = nSrcYOff + nGaussMatrixDim; + int nYShiftGaussMatrix = 0; + if(nSrcYOff < 0) + { + nYShiftGaussMatrix = -nSrcYOff; + nSrcYOff = 0; + } + + + if( nSrcYOff2 > nChunkBottomYOff || (dfYRatioDstToSrc > 1 && iDstLine == nOYSize-1) ) + nSrcYOff2 = nChunkBottomYOff; + + pafSrcScanline = pafChunk + ((nSrcYOff-nChunkYOff) * nChunkXSize); + if (pabyChunkNodataMask != NULL) + pabySrcScanlineNodataMask = pabyChunkNodataMask + ((nSrcYOff-nChunkYOff) * nChunkXSize); + else + pabySrcScanlineNodataMask = NULL; + +/* -------------------------------------------------------------------- */ +/* Loop over destination pixels */ +/* -------------------------------------------------------------------- */ + for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ ) + { + int nSrcXOff, nSrcXOff2; + + nSrcXOff = (int) (0.5 + iDstPixel * dfXRatioDstToSrc); + nSrcXOff2 = (int)(0.5 + (iDstPixel+1) * dfXRatioDstToSrc) + 1; + + int iSizeX = nSrcXOff2 - nSrcXOff; + nSrcXOff = nSrcXOff + iSizeX/2 - nGaussMatrixDim/2; + nSrcXOff2 = nSrcXOff + nGaussMatrixDim; + int nXShiftGaussMatrix = 0; + if(nSrcXOff < 0) + { + nXShiftGaussMatrix = -nSrcXOff; + nSrcXOff = 0; + } + + if( nSrcXOff2 > nChunkRightXOff || (dfXRatioDstToSrc > 1 && iDstPixel == nOXSize-1) ) + nSrcXOff2 = nChunkRightXOff; + + if (poColorTable == NULL) + { + double dfTotal = 0.0, val; + int nCount = 0, iX, iY; + int i = 0,j = 0; + const int *panLineWeight = panGaussMatrix + + nYShiftGaussMatrix * nGaussMatrixDim + nXShiftGaussMatrix; + + for( j=0, iY = nSrcYOff; iY < nSrcYOff2; + iY++, j++, panLineWeight += nGaussMatrixDim ) + { + for( i=0, iX = nSrcXOff; iX < nSrcXOff2; iX++,++i ) + { + val = pafSrcScanline[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize]; + if (pabySrcScanlineNodataMask == NULL || + pabySrcScanlineNodataMask[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize]) + { + int nWeight = panLineWeight[i]; + dfTotal += val * nWeight; + nCount += nWeight; + } + } + } + + if (bHasNoData && nCount == 0) + { + pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue; + } + else + { + if( nCount == 0 ) + pafDstScanline[iDstPixel - nDstXOff] = 0.0; + else + pafDstScanline[iDstPixel - nDstXOff] = (float) (dfTotal / nCount); + } + } + else + { + double val; + int nTotalR = 0, nTotalG = 0, nTotalB = 0; + int nTotalWeight = 0, iX, iY; + int i = 0,j = 0; + const int *panLineWeight = panGaussMatrix + + nYShiftGaussMatrix * nGaussMatrixDim + nXShiftGaussMatrix; + + for( j=0, iY = nSrcYOff; iY < nSrcYOff2; + iY++, j++, panLineWeight += nGaussMatrixDim ) + { + for( i=0, iX = nSrcXOff; iX < nSrcXOff2; iX++,++i ) + { + val = pafSrcScanline[iX-nChunkXOff+(iY-nSrcYOff)*nChunkXSize]; + if (bHasNoData == FALSE || val != fNoDataValue) + { + int nVal = (int)val; + if (nVal >= 0 && nVal < nEntryCount) + { + int nWeight = panLineWeight[i]; + nTotalR += aEntries[nVal].c1 * nWeight; + nTotalG += aEntries[nVal].c2 * nWeight; + nTotalB += aEntries[nVal].c3 * nWeight; + nTotalWeight += nWeight; + } + } + } + } + + if (bHasNoData && nTotalWeight == 0) + { + pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue; + } + else + { + if( nTotalWeight == 0 ) + pafDstScanline[iDstPixel - nDstXOff] = 0.0; + else + { + int nR = (nTotalR + nTotalWeight / 2) / nTotalWeight, + nG = (nTotalG + nTotalWeight / 2) / nTotalWeight, + nB = (nTotalB + nTotalWeight / 2) / nTotalWeight; + pafDstScanline[iDstPixel - nDstXOff] = + (float) GDALFindBestEntry( + nEntryCount, aEntries, nR, nG, nB); + } + } + } + + } + + eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXOff2 - nDstXOff, 1, + pafDstScanline, nDstXOff2 - nDstXOff, 1, GDT_Float32, + 0, 0, NULL ); + } + + CPLFree( pafDstScanline ); + CPLFree( aEntries ); + + return eErr; +} + +/************************************************************************/ +/* GDALResampleChunk32R_Mode() */ +/************************************************************************/ + +static CPLErr +GDALResampleChunk32R_Mode( double dfXRatioDstToSrc, double dfYRatioDstToSrc, + CPL_UNUSED double dfSrcXDelta, + CPL_UNUSED double dfSrcYDelta, + CPL_UNUSED GDALDataType eWrkDataType, + void * pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + int nDstXOff, int nDstXOff2, + int nDstYOff, int nDstYOff2, + GDALRasterBand * poOverview, + CPL_UNUSED const char * pszResampling, + int bHasNoData, float fNoDataValue, + GDALColorTable* poColorTable, + GDALDataType eSrcDataType) + +{ + CPLErr eErr = CE_None; + + float * pafChunk = (float*) pChunk; + +/* -------------------------------------------------------------------- */ +/* Create the filter kernel and allocate scanline buffer. */ +/* -------------------------------------------------------------------- */ + int nOXSize, nOYSize; + float *pafDstScanline; + + nOXSize = poOverview->GetXSize(); + nOYSize = poOverview->GetYSize(); + + pafDstScanline = (float *) VSIMalloc((nDstXOff2 - nDstXOff) * sizeof(float)); + if( pafDstScanline == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALResampleChunk32R: Out of memory for line buffer." ); + return CE_Failure; + } + + int nEntryCount = 0; + GDALColorEntry* aEntries = NULL; + if (poColorTable) + { + int i; + nEntryCount = poColorTable->GetColorEntryCount(); + aEntries = (GDALColorEntry* )CPLMalloc(sizeof(GDALColorEntry) * nEntryCount); + for(i=0;iGetColorEntryAsRGB(i, &aEntries[i]); + } + } + + int nMaxNumPx = 0; + float* pafVals = NULL; + int* panSums = NULL; + + int nChunkRightXOff = nChunkXOff + nChunkXSize; + int nChunkBottomYOff = nChunkYOff + nChunkYSize; + +/* ==================================================================== */ +/* Loop over destination scanlines. */ +/* ==================================================================== */ + for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ ) + { + float *pafSrcScanline; + GByte *pabySrcScanlineNodataMask; + int nSrcYOff, nSrcYOff2 = 0, iDstPixel; + + nSrcYOff = (int) (0.5 + iDstLine * dfYRatioDstToSrc); + if ( nSrcYOff < nChunkYOff ) + nSrcYOff = nChunkYOff; + + nSrcYOff2 = + (int) (0.5 + (iDstLine+1) * dfYRatioDstToSrc); + if( nSrcYOff2 == nSrcYOff ) + nSrcYOff2 ++; + + if( nSrcYOff2 > nChunkBottomYOff || (dfYRatioDstToSrc > 1 && iDstLine == nOYSize-1) ) + { + if( nSrcYOff == nChunkBottomYOff && nChunkBottomYOff - 1 >= nChunkYOff ) + nSrcYOff = nChunkBottomYOff - 1; + nSrcYOff2 = nChunkBottomYOff; + } + + pafSrcScanline = pafChunk + ((nSrcYOff-nChunkYOff) * nChunkXSize); + if (pabyChunkNodataMask != NULL) + pabySrcScanlineNodataMask = pabyChunkNodataMask + ((nSrcYOff-nChunkYOff) * nChunkXSize); + else + pabySrcScanlineNodataMask = NULL; + +/* -------------------------------------------------------------------- */ +/* Loop over destination pixels */ +/* -------------------------------------------------------------------- */ + for( iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ ) + { + int nSrcXOff, nSrcXOff2; + + nSrcXOff = + (int) (0.5 + iDstPixel * dfXRatioDstToSrc); + if ( nSrcXOff < nChunkXOff ) + nSrcXOff = nChunkXOff; + nSrcXOff2 = (int) + (0.5 + (iDstPixel+1) * dfXRatioDstToSrc); + if( nSrcXOff2 == nSrcXOff ) + nSrcXOff2 ++; + + if( nSrcXOff2 > nChunkRightXOff || (dfXRatioDstToSrc > 1 && iDstPixel == nOXSize-1) ) + { + if( nSrcXOff == nChunkRightXOff && nChunkRightXOff - 1 >= nChunkXOff ) + nSrcXOff = nChunkRightXOff - 1; + nSrcXOff2 = nChunkRightXOff; + } + + if (eSrcDataType != GDT_Byte || nEntryCount > 256) + { + /* I'm not sure how much sense it makes to run a majority + filter on floating point data, but here it is for the sake + of compatability. It won't look right on RGB images by the + nature of the filter. */ + int nNumPx = (nSrcYOff2-nSrcYOff)*(nSrcXOff2-nSrcXOff); + int iMaxInd = 0, iMaxVal = -1, iY, iX; + + if (nNumPx > nMaxNumPx) + { + pafVals = (float*) CPLRealloc(pafVals, nNumPx * sizeof(float)); + panSums = (int*) CPLRealloc(panSums, nNumPx * sizeof(int)); + nMaxNumPx = nNumPx; + } + + for( iY = nSrcYOff; iY < nSrcYOff2; ++iY ) + { + int iTotYOff = (iY-nSrcYOff)*nChunkXSize-nChunkXOff; + for( iX = nSrcXOff; iX < nSrcXOff2; ++iX ) + { + if (pabySrcScanlineNodataMask == NULL || + pabySrcScanlineNodataMask[iX+iTotYOff]) + { + float fVal = pafSrcScanline[iX+iTotYOff]; + int i; + + //Check array for existing entry + for( i = 0; i < iMaxInd; ++i ) + if( pafVals[i] == fVal + && ++panSums[i] > panSums[iMaxVal] ) + { + iMaxVal = i; + break; + } + + //Add to arr if entry not already there + if( i == iMaxInd ) + { + pafVals[iMaxInd] = fVal; + panSums[iMaxInd] = 1; + + if( iMaxVal < 0 ) + iMaxVal = iMaxInd; + + ++iMaxInd; + } + } + } + } + + if( iMaxVal == -1 ) + pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue; + else + pafDstScanline[iDstPixel - nDstXOff] = pafVals[iMaxVal]; + } + else /* if (eSrcDataType == GDT_Byte && nEntryCount < 256) */ + { + /* So we go here for a paletted or non-paletted byte band */ + /* The input values are then between 0 and 255 */ + int anVals[256], nMaxVal = 0, iMaxInd = -1, iY, iX; + + memset(anVals, 0, 256*sizeof(int)); + + for( iY = nSrcYOff; iY < nSrcYOff2; ++iY ) + { + int iTotYOff = (iY-nSrcYOff)*nChunkXSize-nChunkXOff; + for( iX = nSrcXOff; iX < nSrcXOff2; ++iX ) + { + float val = pafSrcScanline[iX+iTotYOff]; + if (bHasNoData == FALSE || val != fNoDataValue) + { + int nVal = (int) val; + if ( ++anVals[nVal] > nMaxVal) + { + //Sum the density + //Is it the most common value so far? + iMaxInd = nVal; + nMaxVal = anVals[nVal]; + } + } + } + } + + if( iMaxInd == -1 ) + pafDstScanline[iDstPixel - nDstXOff] = fNoDataValue; + else + pafDstScanline[iDstPixel - nDstXOff] = (float)iMaxInd; + } + } + + eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXOff2 - nDstXOff, 1, + pafDstScanline, nDstXOff2 - nDstXOff, 1, GDT_Float32, + 0, 0, NULL ); + } + + CPLFree( pafDstScanline ); + CPLFree( aEntries ); + CPLFree( pafVals ); + CPLFree( panSums ); + + return eErr; +} + +/************************************************************************/ +/* GDALResampleConvolutionHorizontal() */ +/************************************************************************/ + +template static inline double GDALResampleConvolutionHorizontal( + const T* pChunk, const double* padfWeights, int nSrcPixelCount) +{ + double dfVal1 = 0.0, dfVal2 = 0.0; + int i = 0; + for(;i+3 static inline void GDALResampleConvolutionHorizontalWithMask( + const T* pChunk, const GByte* pabyMask, + const double* padfWeights, int nSrcPixelCount, + double& dfVal, double &dfWeightSum) +{ + dfVal = 0; + dfWeightSum = 0; + int i = 0; + for(;i+3 static inline void GDALResampleConvolutionHorizontal_3rows( + const T* pChunkRow1, const T* pChunkRow2, const T* pChunkRow3, + const double* padfWeights, int nSrcPixelCount, + double& dfRes1, double& dfRes2, double& dfRes3) +{ + double dfVal1 = 0.0, dfVal2 = 0.0, + dfVal3 = 0.0, dfVal4 = 0.0, + dfVal5 = 0.0, dfVal6 = 0.0; + int i = 0; + for(;i+3 static inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows( + const T* pChunkRow1, const T* pChunkRow2, const T* pChunkRow3, + const double* padfWeights, int nSrcPixelCount, + double& dfRes1, double& dfRes2, double& dfRes3) +{ + GDALResampleConvolutionHorizontal_3rows( + pChunkRow1, pChunkRow2, pChunkRow3, + padfWeights, nSrcPixelCount, + dfRes1, dfRes2, dfRes3); +} + +/************************************************************************/ +/* GDALResampleConvolutionVertical() */ +/************************************************************************/ + +template static inline double GDALResampleConvolutionVertical( + const T* pChunk, int nStride, const double* padfWeights, int nSrcLineCount) +{ + double dfVal1 = 0.0, dfVal2 = 0.0; + int i = 0, j = 0; + for(;i+3 static inline void GDALResampleConvolutionVertical_2cols( + const T* pChunk, int nStride, const double* padfWeights, int nSrcLineCount, + double& dfRes1, double& dfRes2) +{ + double dfVal1 = 0.0, dfVal2 = 0.0, + dfVal3 = 0.0, dfVal4 = 0.0; + int i = 0, j = 0; + for(;i+3 + +/************************************************************************/ +/* GDALResampleConvolutionHorizontalSSE2 */ +/************************************************************************/ + +template static inline double GDALResampleConvolutionHorizontalSSE2( + const T* pChunk, const double* padfWeightsAligned, int nSrcPixelCount) +{ + XMMReg4Double v_acc1 = XMMReg4Double::Zero(); + XMMReg4Double v_acc2 = XMMReg4Double::Zero(); + int i = 0; + for(;i+7 */ +/************************************************************************/ + +template<> inline double GDALResampleConvolutionHorizontal( + const GByte* pChunk, const double* padfWeightsAligned, int nSrcPixelCount) +{ + return GDALResampleConvolutionHorizontalSSE2(pChunk, padfWeightsAligned, nSrcPixelCount); +} + +template<> inline double GDALResampleConvolutionHorizontal( + const GUInt16* pChunk, const double* padfWeightsAligned, int nSrcPixelCount) +{ + return GDALResampleConvolutionHorizontalSSE2(pChunk, padfWeightsAligned, nSrcPixelCount); +} + +/************************************************************************/ +/* GDALResampleConvolutionHorizontalWithMaskSSE2 */ +/************************************************************************/ + +template static inline void GDALResampleConvolutionHorizontalWithMaskSSE2( + const T* pChunk, const GByte* pabyMask, + const double* padfWeightsAligned, int nSrcPixelCount, + double& dfVal, double &dfWeightSum) +{ + int i = 0; + XMMReg4Double v_acc = XMMReg4Double::Zero(), + v_acc_weight = XMMReg4Double::Zero(); + for(;i+3 */ +/************************************************************************/ + +template<> inline void GDALResampleConvolutionHorizontalWithMask( + const GByte* pChunk, const GByte* pabyMask, + const double* padfWeightsAligned, int nSrcPixelCount, + double& dfVal, double &dfWeightSum) +{ + GDALResampleConvolutionHorizontalWithMaskSSE2(pChunk, pabyMask, + padfWeightsAligned, + nSrcPixelCount, + dfVal, dfWeightSum); +} + +template<> inline void GDALResampleConvolutionHorizontalWithMask( + const GUInt16* pChunk, const GByte* pabyMask, + const double* padfWeightsAligned, int nSrcPixelCount, + double& dfVal, double &dfWeightSum) +{ + GDALResampleConvolutionHorizontalWithMaskSSE2(pChunk, pabyMask, + padfWeightsAligned, + nSrcPixelCount, + dfVal, dfWeightSum); +} + +/************************************************************************/ +/* GDALResampleConvolutionHorizontal_3rows_SSE2 */ +/************************************************************************/ + +template static inline void GDALResampleConvolutionHorizontal_3rows_SSE2( + const T* pChunkRow1, const T* pChunkRow2, const T* pChunkRow3, + const double* padfWeightsAligned, int nSrcPixelCount, + double& dfRes1, double& dfRes2, double& dfRes3) +{ + XMMReg4Double v_acc1 = XMMReg4Double::Zero(), + v_acc2 = XMMReg4Double::Zero(), + v_acc3 = XMMReg4Double::Zero(); + int i = 0; + for(;i+7 */ +/************************************************************************/ + +template<> inline void GDALResampleConvolutionHorizontal_3rows( + const GByte* pChunkRow1, const GByte* pChunkRow2, const GByte* pChunkRow3, + const double* padfWeightsAligned, int nSrcPixelCount, + double& dfRes1, double& dfRes2, double& dfRes3) +{ + GDALResampleConvolutionHorizontal_3rows_SSE2(pChunkRow1, pChunkRow2, pChunkRow3, + padfWeightsAligned, nSrcPixelCount, + dfRes1, dfRes2, dfRes3); +} + +template<> inline void GDALResampleConvolutionHorizontal_3rows( + const GUInt16* pChunkRow1, const GUInt16* pChunkRow2, const GUInt16* pChunkRow3, + const double* padfWeightsAligned, int nSrcPixelCount, + double& dfRes1, double& dfRes2, double& dfRes3) +{ + GDALResampleConvolutionHorizontal_3rows_SSE2(pChunkRow1, pChunkRow2, pChunkRow3, + padfWeightsAligned, nSrcPixelCount, + dfRes1, dfRes2, dfRes3); +} + +/************************************************************************/ +/* GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2 */ +/************************************************************************/ + +template static inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2( + const T* pChunkRow1, const T* pChunkRow2, const T* pChunkRow3, + const double* padfWeightsAligned, int nSrcPixelCount, + double& dfRes1, double& dfRes2, double& dfRes3) +{ + XMMReg4Double v_acc1 = XMMReg4Double::Zero(), + v_acc2 = XMMReg4Double::Zero(), + v_acc3 = XMMReg4Double::Zero(); + int i = 0; + for(;i+3 */ +/************************************************************************/ + +template<> inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows( + const GByte* pChunkRow1, const GByte* pChunkRow2, const GByte* pChunkRow3, + const double* padfWeightsAligned, int nSrcPixelCount, + double& dfRes1, double& dfRes2, double& dfRes3) +{ + GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2(pChunkRow1, pChunkRow2, pChunkRow3, + padfWeightsAligned, nSrcPixelCount, + dfRes1, dfRes2, dfRes3); +} + +template<> inline void GDALResampleConvolutionHorizontalPixelCountLess8_3rows( + const GUInt16* pChunkRow1, const GUInt16* pChunkRow2, const GUInt16* pChunkRow3, + const double* padfWeightsAligned, int nSrcPixelCount, + double& dfRes1, double& dfRes2, double& dfRes3) +{ + GDALResampleConvolutionHorizontalPixelCountLess8_3rows_SSE2(pChunkRow1, pChunkRow2, pChunkRow3, + padfWeightsAligned, nSrcPixelCount, + dfRes1, dfRes2, dfRes3); +} + +#endif /* USE_SSE2 */ + +/************************************************************************/ +/* GDALResampleChunk32R_Convolution() */ +/************************************************************************/ + +template static CPLErr +GDALResampleChunk32R_ConvolutionT( double dfXRatioDstToSrc, double dfYRatioDstToSrc, + double dfSrcXDelta, + double dfSrcYDelta, + T * pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + int nDstXOff, int nDstXOff2, + int nDstYOff, int nDstYOff2, + GDALRasterBand * poOverview, + int bHasNoData, + float fNoDataValue, + FilterFuncType pfnFilterFunc, + FilterFunc4ValuesType pfnFilterFunc4Values, + int nKernelRadius ) + +{ + + CPLErr eErr = CE_None; + + if (!bHasNoData) + fNoDataValue = 0.0f; + +/* -------------------------------------------------------------------- */ +/* Allocate work buffers. */ +/* -------------------------------------------------------------------- */ + int nDstXSize = nDstXOff2 - nDstXOff; + + double dfXScale = 1.0 / dfXRatioDstToSrc; + double dfXScaleWeight = ( dfXScale >= 1.0 ) ? 1.0 : dfXScale; + double dfXScaledRadius = nKernelRadius / dfXScaleWeight; + double dfYScale = 1.0 / dfYRatioDstToSrc; + double dfYScaleWeight = ( dfYScale >= 1.0 ) ? 1.0 : dfYScale; + double dfYScaledRadius = nKernelRadius / dfYScaleWeight; + + float* pafDstScanline = (float *) VSIMalloc(nDstXSize * sizeof(float)); + + /* Temporary array to store result of horizontal filter */ + double* padfHorizontalFiltered = (double*) VSIMalloc(nChunkYSize * nDstXSize * sizeof(double)); + + /* To store convolution coefficients */ + double* padfWeightsAlloc = (double*) CPLMalloc((int)( + 2 + 2 * MAX(dfXScaledRadius, dfYScaledRadius) + 0.5 + 1 /* for alignment*/) * sizeof(double)); + + GByte* pabyChunkNodataMaskHorizontalFiltered = NULL; + if( pabyChunkNodataMask ) + pabyChunkNodataMaskHorizontalFiltered = (GByte*) VSIMalloc(nChunkYSize * nDstXSize); + if( pafDstScanline == NULL || padfHorizontalFiltered == NULL || + padfWeightsAlloc == NULL || (pabyChunkNodataMask != NULL && pabyChunkNodataMaskHorizontalFiltered == NULL) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALResampleChunk32R_ConvolutionT: Out of memory for work buffers." ); + VSIFree(pafDstScanline); + VSIFree(padfHorizontalFiltered); + VSIFree(padfWeightsAlloc); + VSIFree(pabyChunkNodataMaskHorizontalFiltered); + return CE_Failure; + } + + /* Make sure we are aligned on 16 bits */ + double* padfWeights = padfWeightsAlloc; + if( (((size_t)padfWeights) % 16) != 0 ) + padfWeights ++; + +/* ==================================================================== */ +/* Fist pass: horizontal filter */ +/* ==================================================================== */ + int nChunkRightXOff = nChunkXOff + nChunkXSize; +#ifdef USE_SSE2 + int bSrcPixelCountLess8 = dfXScaledRadius < 4; +#endif + for( int iDstPixel = nDstXOff; iDstPixel < nDstXOff2; iDstPixel++ ) + { + double dfSrcPixel = (iDstPixel+0.5)*dfXRatioDstToSrc + dfSrcXDelta; + int nSrcPixelStart = (int)floor(dfSrcPixel - dfXScaledRadius + 0.5); + int nSrcPixelStop = (int)(dfSrcPixel + dfXScaledRadius + 0.5); + if( nSrcPixelStart < nChunkXOff ) + nSrcPixelStart = nChunkXOff; + if( nSrcPixelStop > nChunkRightXOff ) + nSrcPixelStop = nChunkRightXOff; +#if 0 + if( nSrcPixelStart < nChunkXOff && nChunkXOff > 0 ) + { + printf("truncated iDstPixel = %d\n", iDstPixel); + } + if( nSrcPixelStop > nChunkRightXOff && nChunkRightXOff < nSrcWidth ) + { + printf("truncated iDstPixel = %d\n", iDstPixel); + } +#endif + int nSrcPixelCount = nSrcPixelStop - nSrcPixelStart; + double dfWeightSum = 0.0; + + /* Compute convolution coefficients */ + int nSrcPixel = nSrcPixelStart; + double dfX = dfXScaleWeight * (nSrcPixel - dfSrcPixel + 0.5); + for( ; nSrcPixel + 3 < nSrcPixelStop; nSrcPixel+=4) + { + padfWeights[nSrcPixel - nSrcPixelStart] = dfX; + dfX += dfXScaleWeight; + padfWeights[nSrcPixel+1 - nSrcPixelStart] = dfX; + dfX += dfXScaleWeight; + padfWeights[nSrcPixel+2 - nSrcPixelStart] = dfX; + dfX += dfXScaleWeight; + padfWeights[nSrcPixel+3 - nSrcPixelStart] = dfX; + dfX += dfXScaleWeight; + dfWeightSum += pfnFilterFunc4Values(padfWeights + nSrcPixel - nSrcPixelStart); + } + for( ; nSrcPixel < nSrcPixelStop; nSrcPixel++, dfX += dfXScaleWeight) + { + double dfWeight = pfnFilterFunc(dfX); + padfWeights[nSrcPixel - nSrcPixelStart] = dfWeight; + dfWeightSum += dfWeight; + } + + if( pabyChunkNodataMask == NULL ) + { + if( dfWeightSum != 0 ) + { + double dfInvWeightSum = 1.0 / dfWeightSum; + for(int i=0;i 0.0 ) + { + padfHorizontalFiltered[nTempOffset] = dfVal / dfWeightSum; + pabyChunkNodataMaskHorizontalFiltered[nTempOffset] = 1; + } + else + { + padfHorizontalFiltered[nTempOffset] = 0.0; + pabyChunkNodataMaskHorizontalFiltered[nTempOffset] = 0; + } + } + } + } + +/* ==================================================================== */ +/* Second pass: vertical filter */ +/* ==================================================================== */ + int nChunkBottomYOff = nChunkYOff + nChunkYSize; + for( int iDstLine = nDstYOff; iDstLine < nDstYOff2; iDstLine++ ) + { + double dfSrcLine = (iDstLine+0.5)*dfYRatioDstToSrc + dfSrcYDelta; + int nSrcLineStart = (int)floor(dfSrcLine - dfYScaledRadius + 0.5); + int nSrcLineStop = (int)(dfSrcLine + dfYScaledRadius + 0.5); + if( nSrcLineStart < nChunkYOff ) + nSrcLineStart = nChunkYOff; + if( nSrcLineStop > nChunkBottomYOff ) + nSrcLineStop = nChunkBottomYOff; +#if 0 + if( nSrcLineStart < nChunkYOff && + nChunkYOff > 0 ) + { + printf("truncated iDstLine = %d\n", iDstLine); + } + if( nSrcLineStop > nChunkBottomYOff && nChunkBottomYOff < nSrcHeight ) + { + printf("truncated iDstLine = %d\n", iDstLine); + } +#endif + int nSrcLineCount = nSrcLineStop - nSrcLineStart; + double dfWeightSum = 0.0; + + /* Compute convolution coefficients */ + int nSrcLine = nSrcLineStart; + double dfY = dfYScaleWeight * (nSrcLine - dfSrcLine + 0.5); + for( ; nSrcLine + 3 < nSrcLineStop; nSrcLine+=4, dfY += 4 * dfYScaleWeight) + { + padfWeights[nSrcLine - nSrcLineStart] = dfY; + padfWeights[nSrcLine+1 - nSrcLineStart] = dfY + dfYScaleWeight; + padfWeights[nSrcLine+2 - nSrcLineStart] = dfY + 2 * dfYScaleWeight; + padfWeights[nSrcLine+3 - nSrcLineStart] = dfY + 3 * dfYScaleWeight; + dfWeightSum += pfnFilterFunc4Values(padfWeights + nSrcLine - nSrcLineStart); + } + for( ; nSrcLine < nSrcLineStop; nSrcLine++, dfY += dfYScaleWeight) + { + double dfWeight = pfnFilterFunc(dfY); + padfWeights[nSrcLine - nSrcLineStart] = dfWeight; + dfWeightSum += dfWeight; + } + + if( pabyChunkNodataMask == NULL ) + { + if( dfWeightSum != 0 ) + { + double dfInvWeightSum = 1.0 / dfWeightSum; + for(int i=0;i 0.0 ) + { + pafDstScanline[iFilteredPixelOff] = (float)(dfVal / dfWeightSum); + } + else + { + pafDstScanline[iFilteredPixelOff] = fNoDataValue; + } + } + } + + eErr = poOverview->RasterIO( GF_Write, nDstXOff, iDstLine, nDstXSize, 1, + pafDstScanline, nDstXSize, 1, GDT_Float32, + 0, 0, NULL ); + } + + VSIFree( padfWeightsAlloc ); + VSIFree( padfHorizontalFiltered ); + VSIFree( pafDstScanline ); + VSIFree(pabyChunkNodataMaskHorizontalFiltered); + + return eErr; +} + +static CPLErr GDALResampleChunk32R_Convolution( + double dfXRatioDstToSrc, double dfYRatioDstToSrc, + double dfSrcXDelta, + double dfSrcYDelta, + GDALDataType eWrkDataType, + void * pChunk, + GByte * pabyChunkNodataMask, + int nChunkXOff, int nChunkXSize, + int nChunkYOff, int nChunkYSize, + int nDstXOff, int nDstXOff2, + int nDstYOff, int nDstYOff2, + GDALRasterBand * poOverview, + const char * pszResampling, + int bHasNoData, float fNoDataValue, + CPL_UNUSED GDALColorTable* poColorTable_unused, + CPL_UNUSED GDALDataType eSrcDataType) +{ + GDALResampleAlg eResample; + if( EQUAL(pszResampling, "BILINEAR") ) + eResample = GRA_Bilinear; + else if( EQUAL(pszResampling, "CUBIC") ) + eResample = GRA_Cubic; + else if( EQUAL(pszResampling, "CUBICSPLINE") ) + eResample = GRA_CubicSpline; + else if( EQUAL(pszResampling, "LANCZOS") ) + eResample = GRA_Lanczos; + else + { + CPLAssert(0); + return CE_Failure; + } + int nKernelRadius = GWKGetFilterRadius(eResample); + FilterFuncType pfnFilterFunc = GWKGetFilterFunc(eResample); + FilterFunc4ValuesType pfnFilterFunc4Values = GWKGetFilterFunc4Values(eResample); + + if (eWrkDataType == GDT_Byte) + return GDALResampleChunk32R_ConvolutionT(dfXRatioDstToSrc, dfYRatioDstToSrc, + dfSrcXDelta, dfSrcYDelta, + (GByte *) pChunk, pabyChunkNodataMask, + nChunkXOff, nChunkXSize, + nChunkYOff, nChunkYSize, + nDstXOff, nDstXOff2, + nDstYOff, nDstYOff2, + poOverview, + bHasNoData, fNoDataValue, + pfnFilterFunc, + pfnFilterFunc4Values, + nKernelRadius); + else if (eWrkDataType == GDT_UInt16) + return GDALResampleChunk32R_ConvolutionT(dfXRatioDstToSrc, dfYRatioDstToSrc, + dfSrcXDelta, dfSrcYDelta, + (GUInt16 *) pChunk, pabyChunkNodataMask, + nChunkXOff, nChunkXSize, + nChunkYOff, nChunkYSize, + nDstXOff, nDstXOff2, + nDstYOff, nDstYOff2, + poOverview, + bHasNoData, fNoDataValue, + pfnFilterFunc, + pfnFilterFunc4Values, + nKernelRadius); + else if (eWrkDataType == GDT_Float32) + return GDALResampleChunk32R_ConvolutionT(dfXRatioDstToSrc, dfYRatioDstToSrc, + dfSrcXDelta, dfSrcYDelta, + (float *) pChunk, pabyChunkNodataMask, + nChunkXOff, nChunkXSize, + nChunkYOff, nChunkYSize, + nDstXOff, nDstXOff2, + nDstYOff, nDstYOff2, + poOverview, + bHasNoData, fNoDataValue, + pfnFilterFunc, + pfnFilterFunc4Values, + nKernelRadius); + + CPLAssert(0); + return CE_Failure; +} + +/************************************************************************/ +/* GDALResampleChunkC32R() */ +/************************************************************************/ + +static CPLErr +GDALResampleChunkC32R( int nSrcWidth, int nSrcHeight, + float * pafChunk, int nChunkYOff, int nChunkYSize, + int nDstYOff, int nDstYOff2, + GDALRasterBand * poOverview, + const char * pszResampling ) + +{ + int nOXSize, nOYSize; + float *pafDstScanline; + CPLErr eErr = CE_None; + + nOXSize = poOverview->GetXSize(); + nOYSize = poOverview->GetYSize(); + double dfXRatioDstToSrc = (double)nSrcWidth / nOXSize; + double dfYRatioDstToSrc = (double)nSrcHeight / nOYSize; + + pafDstScanline = (float *) VSIMalloc(nOXSize * sizeof(float) * 2); + if( pafDstScanline == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALResampleChunkC32R: Out of memory for line buffer." ); + return CE_Failure; + } + +/* ==================================================================== */ +/* Loop over destination scanlines. */ +/* ==================================================================== */ + for( int iDstLine = nDstYOff; iDstLine < nDstYOff2 && eErr == CE_None; iDstLine++ ) + { + float *pafSrcScanline; + int nSrcYOff, nSrcYOff2, iDstPixel; + + nSrcYOff = (int) (0.5 + iDstLine * dfYRatioDstToSrc); + if( nSrcYOff < nChunkYOff ) + nSrcYOff = nChunkYOff; + + nSrcYOff2 = (int) (0.5 + (iDstLine+1) * dfYRatioDstToSrc); + if( nSrcYOff2 == nSrcYOff ) + nSrcYOff2 ++; + + if( nSrcYOff2 > nSrcHeight || iDstLine == nOYSize-1 ) + { + if( nSrcYOff == nSrcHeight && nSrcHeight - 1 >= nChunkYOff ) + nSrcYOff = nSrcHeight - 1; + nSrcYOff2 = nSrcHeight; + } + if( nSrcYOff2 > nChunkYOff + nChunkYSize ) + nSrcYOff2 = nChunkYOff + nChunkYSize; + + pafSrcScanline = pafChunk + ((nSrcYOff-nChunkYOff) * nSrcWidth) * 2; + +/* -------------------------------------------------------------------- */ +/* Loop over destination pixels */ +/* -------------------------------------------------------------------- */ + for( iDstPixel = 0; iDstPixel < nOXSize; iDstPixel++ ) + { + int nSrcXOff, nSrcXOff2; + + nSrcXOff = (int) (0.5 + iDstPixel * dfXRatioDstToSrc); + nSrcXOff2 = (int) + (0.5 + (iDstPixel+1) * dfXRatioDstToSrc); + if( nSrcXOff2 == nSrcXOff ) + nSrcXOff2 ++; + if( nSrcXOff2 > nSrcWidth || iDstPixel == nOXSize-1 ) + { + if( nSrcXOff == nSrcWidth && nSrcWidth - 1 >= 0 ) + nSrcXOff = nSrcWidth - 1; + nSrcXOff2 = nSrcWidth; + } + + if( EQUALN(pszResampling,"NEAR",4) ) + { + pafDstScanline[iDstPixel*2] = pafSrcScanline[nSrcXOff*2]; + pafDstScanline[iDstPixel*2+1] = pafSrcScanline[nSrcXOff*2+1]; + } + else if( EQUAL(pszResampling,"AVERAGE_MAGPHASE") ) + { + double dfTotalR = 0.0, dfTotalI = 0.0, dfTotalM = 0.0; + int nCount = 0, iX, iY; + + for( iY = nSrcYOff; iY < nSrcYOff2; iY++ ) + { + for( iX = nSrcXOff; iX < nSrcXOff2; iX++ ) + { + double dfR, dfI; + + dfR = pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2]; + dfI = pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2+1]; + dfTotalR += dfR; + dfTotalI += dfI; + dfTotalM += sqrt( dfR*dfR + dfI*dfI ); + nCount++; + } + } + + CPLAssert( nCount > 0 ); + if( nCount == 0 ) + { + pafDstScanline[iDstPixel*2] = 0.0; + pafDstScanline[iDstPixel*2+1] = 0.0; + } + else + { + double dfM, dfDesiredM, dfRatio=1.0; + + pafDstScanline[iDstPixel*2 ] = (float) (dfTotalR/nCount); + pafDstScanline[iDstPixel*2+1] = (float) (dfTotalI/nCount); + + dfM = sqrt(pafDstScanline[iDstPixel*2 ]*pafDstScanline[iDstPixel*2 ] + + pafDstScanline[iDstPixel*2+1]*pafDstScanline[iDstPixel*2+1]); + dfDesiredM = dfTotalM / nCount; + if( dfM != 0.0 ) + dfRatio = dfDesiredM / dfM; + + pafDstScanline[iDstPixel*2 ] *= (float) dfRatio; + pafDstScanline[iDstPixel*2+1] *= (float) dfRatio; + } + } + else if( EQUALN(pszResampling,"AVER",4) ) + { + double dfTotalR = 0.0, dfTotalI = 0.0; + int nCount = 0, iX, iY; + + for( iY = nSrcYOff; iY < nSrcYOff2; iY++ ) + { + for( iX = nSrcXOff; iX < nSrcXOff2; iX++ ) + { + dfTotalR += pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2]; + dfTotalI += pafSrcScanline[iX*2+(iY-nSrcYOff)*nSrcWidth*2+1]; + nCount++; + } + } + + CPLAssert( nCount > 0 ); + if( nCount == 0 ) + { + pafDstScanline[iDstPixel*2] = 0.0; + pafDstScanline[iDstPixel*2+1] = 0.0; + } + else + { + pafDstScanline[iDstPixel*2 ] = (float) (dfTotalR/nCount); + pafDstScanline[iDstPixel*2+1] = (float) (dfTotalI/nCount); + } + } + } + + eErr = poOverview->RasterIO( GF_Write, 0, iDstLine, nOXSize, 1, + pafDstScanline, nOXSize, 1, GDT_CFloat32, + 0, 0, NULL ); + } + + CPLFree( pafDstScanline ); + + return eErr; +} + +/************************************************************************/ +/* GDALRegenerateCascadingOverviews() */ +/* */ +/* Generate a list of overviews in order from largest to */ +/* smallest, computing each from the next larger. */ +/************************************************************************/ + +static CPLErr +GDALRegenerateCascadingOverviews( + GDALRasterBand *poSrcBand, int nOverviews, GDALRasterBand **papoOvrBands, + const char * pszResampling, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ +/* -------------------------------------------------------------------- */ +/* First, we must put the overviews in order from largest to */ +/* smallest. */ +/* -------------------------------------------------------------------- */ + int i, j; + + for( i = 0; i < nOverviews-1; i++ ) + { + for( j = 0; j < nOverviews - i - 1; j++ ) + { + + if( papoOvrBands[j]->GetXSize() + * (float) papoOvrBands[j]->GetYSize() < + papoOvrBands[j+1]->GetXSize() + * (float) papoOvrBands[j+1]->GetYSize() ) + { + GDALRasterBand * poTempBand; + + poTempBand = papoOvrBands[j]; + papoOvrBands[j] = papoOvrBands[j+1]; + papoOvrBands[j+1] = poTempBand; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Count total pixels so we can prepare appropriate scaled */ +/* progress functions. */ +/* -------------------------------------------------------------------- */ + double dfTotalPixels = 0.0; + + for( i = 0; i < nOverviews; i++ ) + { + dfTotalPixels += papoOvrBands[i]->GetXSize() + * (double) papoOvrBands[i]->GetYSize(); + } + +/* -------------------------------------------------------------------- */ +/* Generate all the bands. */ +/* -------------------------------------------------------------------- */ + double dfPixelsProcessed = 0.0; + + for( i = 0; i < nOverviews; i++ ) + { + void *pScaledProgressData; + double dfPixels; + GDALRasterBand *poBaseBand; + CPLErr eErr; + + if( i == 0 ) + poBaseBand = poSrcBand; + else + poBaseBand = papoOvrBands[i-1]; + + dfPixels = papoOvrBands[i]->GetXSize() + * (double) papoOvrBands[i]->GetYSize(); + + pScaledProgressData = GDALCreateScaledProgress( + dfPixelsProcessed / dfTotalPixels, + (dfPixelsProcessed + dfPixels) / dfTotalPixels, + pfnProgress, pProgressData ); + + eErr = GDALRegenerateOverviews( (GDALRasterBandH) poBaseBand, + 1, (GDALRasterBandH *) papoOvrBands+i, + pszResampling, + GDALScaledProgress, + pScaledProgressData ); + GDALDestroyScaledProgress( pScaledProgressData ); + + if( eErr != CE_None ) + return eErr; + + dfPixelsProcessed += dfPixels; + + /* we only do the bit2grayscale promotion on the base band */ + if( EQUALN(pszResampling,"AVERAGE_BIT2GRAYSCALE",13) ) + pszResampling = "AVERAGE"; + } + + return CE_None; +} + +/************************************************************************/ +/* GDALGetResampleFunction() */ +/************************************************************************/ + +GDALResampleFunction GDALGetResampleFunction(const char* pszResampling, + int* pnRadius) +{ + if( pnRadius ) *pnRadius = 0; + if( EQUALN(pszResampling,"NEAR",4) ) + return GDALResampleChunk32R_Near; + else if( EQUALN(pszResampling,"AVER",4) ) + return GDALResampleChunk32R_Average; + else if( EQUALN(pszResampling,"GAUSS",5) ) + { + if( pnRadius ) *pnRadius = 1; + return GDALResampleChunk32R_Gauss; + } + else if( EQUALN(pszResampling,"MODE",4) ) + return GDALResampleChunk32R_Mode; + else if( EQUAL(pszResampling,"CUBIC") ) + { + if( pnRadius ) *pnRadius = GWKGetFilterRadius(GRA_Cubic); + return GDALResampleChunk32R_Convolution; + } + else if( EQUAL(pszResampling,"CUBICSPLINE") ) + { + if( pnRadius ) *pnRadius = GWKGetFilterRadius(GRA_CubicSpline); + return GDALResampleChunk32R_Convolution; + } + else if( EQUAL(pszResampling,"LANCZOS") ) + { + if( pnRadius ) *pnRadius = GWKGetFilterRadius(GRA_Lanczos); + return GDALResampleChunk32R_Convolution; + } + else if( EQUAL(pszResampling,"BILINEAR") ) + { + if( pnRadius ) *pnRadius = GWKGetFilterRadius(GRA_Bilinear); + return GDALResampleChunk32R_Convolution; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "GDALGetResampleFunction: Unsupported resampling method \"%s\".", + pszResampling ); + return NULL; + } +} + +/************************************************************************/ +/* GDALGetOvrWorkDataType() */ +/************************************************************************/ + +GDALDataType GDALGetOvrWorkDataType(const char* pszResampling, + GDALDataType eSrcDataType) +{ + if( (EQUALN(pszResampling,"NEAR",4) || + EQUALN(pszResampling,"AVER",4) || + EQUAL(pszResampling,"CUBIC") || + EQUAL(pszResampling,"CUBICSPLINE") || + EQUAL(pszResampling,"LANCZOS") || + EQUAL(pszResampling,"BILINEAR")) && + eSrcDataType == GDT_Byte) + return GDT_Byte; + else if( (EQUALN(pszResampling,"NEAR",4) || + EQUALN(pszResampling,"AVER",4) || + EQUAL(pszResampling,"CUBIC") || + EQUAL(pszResampling,"CUBICSPLINE") || + EQUAL(pszResampling,"LANCZOS") || + EQUAL(pszResampling,"BILINEAR")) && + eSrcDataType == GDT_UInt16) + return GDT_UInt16; + else + return GDT_Float32; +} + +/************************************************************************/ +/* GDALRegenerateOverviews() */ +/************************************************************************/ + +/** + * \brief Generate downsampled overviews. + * + * This function will generate one or more overview images from a base + * image using the requested downsampling algorithm. It's primary use + * is for generating overviews via GDALDataset::BuildOverviews(), but it + * can also be used to generate downsampled images in one file from another + * outside the overview architecture. + * + * The output bands need to exist in advance. + * + * The full set of resampling algorithms is documented in + * GDALDataset::BuildOverviews(). + * + * This function will honour properly NODATA_VALUES tuples (special dataset metadata) so + * that only a given RGB triplet (in case of a RGB image) will be considered as the + * nodata value and not each value of the triplet independantly per band. + * + * @param hSrcBand the source (base level) band. + * @param nOverviewCount the number of downsampled bands being generated. + * @param pahOvrBands the list of downsampled bands to be generated. + * @param pszResampling Resampling algorithm (eg. "AVERAGE"). + * @param pfnProgress progress report function. + * @param pProgressData progress function callback data. + * @return CE_None on success or CE_Failure on failure. + */ +CPLErr +GDALRegenerateOverviews( GDALRasterBandH hSrcBand, + int nOverviewCount, GDALRasterBandH *pahOvrBands, + const char * pszResampling, + GDALProgressFunc pfnProgress, void * pProgressData ) + +{ + GDALRasterBand *poSrcBand = (GDALRasterBand *) hSrcBand; + GDALRasterBand **papoOvrBands = (GDALRasterBand **) pahOvrBands; + int nFullResYChunk, nWidth, nHeight; + int nFRXBlockSize, nFRYBlockSize; + GDALDataType eType; + int bHasNoData; + float fNoDataValue; + GDALColorTable* poColorTable = NULL; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + if( EQUAL(pszResampling,"NONE") ) + return CE_None; + + int nKernelRadius; + GDALResampleFunction pfnResampleFn = GDALGetResampleFunction(pszResampling, + &nKernelRadius); + if (pfnResampleFn == NULL) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Check color tables... */ +/* -------------------------------------------------------------------- */ + if ((EQUALN(pszResampling,"AVER",4) + || EQUALN(pszResampling,"MODE",4) + || EQUALN(pszResampling,"GAUSS",5)) && + poSrcBand->GetColorInterpretation() == GCI_PaletteIndex) + { + poColorTable = poSrcBand->GetColorTable(); + if (poColorTable != NULL) + { + if (poColorTable->GetPaletteInterpretation() != GPI_RGB) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Computing overviews on palette index raster bands " + "with a palette whose color interpreation is not RGB " + "will probably lead to unexpected results."); + poColorTable = NULL; + } + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Computing overviews on palette index raster bands " + "without a palette will probably lead to unexpected results."); + } + } + // Not ready yet + else if( (EQUAL(pszResampling,"CUBIC") || + EQUAL(pszResampling,"CUBICSPLINE") || + EQUAL(pszResampling,"LANCZOS") || + EQUAL(pszResampling,"BILINEAR") ) + && poSrcBand->GetColorInterpretation() == GCI_PaletteIndex ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Computing %s overviews on palette index raster bands " + "will probably lead to unexpected results.", pszResampling); + } + + + /* If we have a nodata mask and we are doing something more complicated */ + /* than nearest neighbouring, we have to fetch to nodata mask */ + + GDALRasterBand* poMaskBand = NULL; + int nMaskFlags = 0; + int bUseNoDataMask = FALSE; + + if( !EQUALN(pszResampling,"NEAR",4) ) + { + /* Special case if we are the alpha band. We want it to be considered */ + /* as the mask band to avoid alpha=0 to be taken into account in average */ + /* computation */ + if( poSrcBand->GetColorInterpretation() == GCI_AlphaBand ) + { + poMaskBand = poSrcBand; + nMaskFlags = GMF_ALPHA | GMF_PER_DATASET; + } + else + { + poMaskBand = poSrcBand->GetMaskBand(); + nMaskFlags = poSrcBand->GetMaskFlags(); + } + + bUseNoDataMask = ((nMaskFlags & GMF_ALL_VALID) == 0); + } + +/* -------------------------------------------------------------------- */ +/* If we are operating on multiple overviews, and using */ +/* averaging, lets do them in cascading order to reduce the */ +/* amount of computation. */ +/* -------------------------------------------------------------------- */ + + /* In case the mask made be computed from another band of the dataset, */ + /* we can't use cascaded generation, as the computation of the overviews */ + /* of the band used for the mask band may not have yet occured (#3033) */ + if( (EQUALN(pszResampling,"AVER",4) | + EQUALN(pszResampling,"GAUSS",5) || + EQUAL(pszResampling,"CUBIC") || + EQUAL(pszResampling,"CUBICSPLINE") || + EQUAL(pszResampling,"LANCZOS") || + EQUAL(pszResampling,"BILINEAR")) && nOverviewCount > 1 + && !(bUseNoDataMask && nMaskFlags != GMF_NODATA)) + return GDALRegenerateCascadingOverviews( poSrcBand, + nOverviewCount, papoOvrBands, + pszResampling, + pfnProgress, + pProgressData ); + +/* -------------------------------------------------------------------- */ +/* Setup one horizontal swath to read from the raw buffer. */ +/* -------------------------------------------------------------------- */ + void *pChunk; + GByte *pabyChunkNodataMask = NULL; + + poSrcBand->GetBlockSize( &nFRXBlockSize, &nFRYBlockSize ); + + if( nFRYBlockSize < 16 || nFRYBlockSize > 256 ) + nFullResYChunk = 64; + else + nFullResYChunk = nFRYBlockSize; + + if( GDALDataTypeIsComplex( poSrcBand->GetRasterDataType() ) ) + eType = GDT_CFloat32; + else + eType = GDALGetOvrWorkDataType(pszResampling, poSrcBand->GetRasterDataType()); + + nWidth = poSrcBand->GetXSize(); + nHeight = poSrcBand->GetYSize(); + + int nMaxOvrFactor = 1; + for( int iOverview = 0; iOverview < nOverviewCount; iOverview ++ ) + { + int nDstWidth = papoOvrBands[iOverview]->GetXSize(); + int nDstHeight = papoOvrBands[iOverview]->GetYSize(); + nMaxOvrFactor = MAX( nMaxOvrFactor, (int)((double)nWidth / nDstWidth + 0.5) ); + nMaxOvrFactor = MAX( nMaxOvrFactor, (int)((double)nHeight / nDstHeight + 0.5) ); + } + int nMaxChunkYSizeQueried = nFullResYChunk + 2 * nKernelRadius * nMaxOvrFactor; + + pChunk = + VSIMalloc3((GDALGetDataTypeSize(eType)/8), nMaxChunkYSizeQueried, nWidth ); + if (bUseNoDataMask) + { + pabyChunkNodataMask = (GByte *) + (GByte*) VSIMalloc2( nMaxChunkYSizeQueried, nWidth ); + } + + if( pChunk == NULL || (bUseNoDataMask && pabyChunkNodataMask == NULL)) + { + CPLFree(pChunk); + CPLFree(pabyChunkNodataMask); + CPLError( CE_Failure, CPLE_OutOfMemory, + "Out of memory in GDALRegenerateOverviews()." ); + + return CE_Failure; + } + + fNoDataValue = (float) poSrcBand->GetNoDataValue(&bHasNoData); + +/* -------------------------------------------------------------------- */ +/* Loop over image operating on chunks. */ +/* -------------------------------------------------------------------- */ + int nChunkYOff = 0; + CPLErr eErr = CE_None; + + for( nChunkYOff = 0; + nChunkYOff < nHeight && eErr == CE_None; + nChunkYOff += nFullResYChunk ) + { + if( !pfnProgress( nChunkYOff / (double) nHeight, + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + + if( nFullResYChunk + nChunkYOff > nHeight ) + nFullResYChunk = nHeight - nChunkYOff; + + int nChunkYOffQueried = nChunkYOff - nKernelRadius * nMaxOvrFactor; + int nChunkYSizeQueried = nFullResYChunk + 2 * nKernelRadius * nMaxOvrFactor; + if( nChunkYOffQueried < 0 ) + { + nChunkYSizeQueried += nChunkYOffQueried; + nChunkYOffQueried = 0; + } + if( nChunkYOffQueried + nChunkYSizeQueried > nHeight ) + nChunkYSizeQueried = nHeight - nChunkYOffQueried; + + /* read chunk */ + if (eErr == CE_None) + eErr = poSrcBand->RasterIO( GF_Read, 0, nChunkYOffQueried, nWidth, nChunkYSizeQueried, + pChunk, nWidth, nChunkYSizeQueried, eType, + 0, 0, NULL ); + if (eErr == CE_None && bUseNoDataMask) + eErr = poMaskBand->RasterIO( GF_Read, 0, nChunkYOffQueried, nWidth, nChunkYSizeQueried, + pabyChunkNodataMask, nWidth, nChunkYSizeQueried, GDT_Byte, + 0, 0, NULL ); + + /* special case to promote 1bit data to 8bit 0/255 values */ + if( EQUAL(pszResampling,"AVERAGE_BIT2GRAYSCALE") ) + { + int i; + + if (eType == GDT_Float32) + { + float* pafChunk = (float*)pChunk; + for( i = nChunkYSizeQueried*nWidth - 1; i >= 0; i-- ) + { + if( pafChunk[i] == 1.0 ) + pafChunk[i] = 255.0; + } + } + else if (eType == GDT_Byte) + { + GByte* pabyChunk = (GByte*)pChunk; + for( i = nChunkYSizeQueried*nWidth - 1; i >= 0; i-- ) + { + if( pabyChunk[i] == 1 ) + pabyChunk[i] = 255; + } + } + else if (eType == GDT_UInt16) + { + GUInt16* pasChunk = (GUInt16*)pChunk; + for( i = nChunkYSizeQueried*nWidth - 1; i >= 0; i-- ) + { + if( pasChunk[i] == 1 ) + pasChunk[i] = 255; + } + } + else { + CPLAssert(0); + } + } + else if( EQUAL(pszResampling,"AVERAGE_BIT2GRAYSCALE_MINISWHITE") ) + { + int i; + + if (eType == GDT_Float32) + { + float* pafChunk = (float*)pChunk; + for( i = nChunkYSizeQueried*nWidth - 1; i >= 0; i-- ) + { + if( pafChunk[i] == 1.0 ) + pafChunk[i] = 0.0; + else if( pafChunk[i] == 0.0 ) + pafChunk[i] = 255.0; + } + } + else if (eType == GDT_Byte) + { + GByte* pabyChunk = (GByte*)pChunk; + for( i = nChunkYSizeQueried*nWidth - 1; i >= 0; i-- ) + { + if( pabyChunk[i] == 1 ) + pabyChunk[i] = 0; + else if( pabyChunk[i] == 0 ) + pabyChunk[i] = 255; + } + } + else if (eType == GDT_UInt16) + { + GUInt16* pasChunk = (GUInt16*)pChunk; + for( i = nChunkYSizeQueried*nWidth - 1; i >= 0; i-- ) + { + if( pasChunk[i] == 1 ) + pasChunk[i] = 0; + else if( pasChunk[i] == 0 ) + pasChunk[i] = 255; + } + } + else { + CPLAssert(0); + } + } + + for( int iOverview = 0; iOverview < nOverviewCount && eErr == CE_None; iOverview++ ) + { + int nDstWidth = papoOvrBands[iOverview]->GetXSize(); + int nDstHeight = papoOvrBands[iOverview]->GetYSize(); + + double dfXRatioDstToSrc = (double)nWidth / nDstWidth; + double dfYRatioDstToSrc = (double)nHeight / nDstHeight; + +/* -------------------------------------------------------------------- */ +/* Figure out the line to start writing to, and the first line */ +/* to not write to. In theory this approach should ensure that */ +/* every output line will be written if all input chunks are */ +/* processed. */ +/* -------------------------------------------------------------------- */ + int nDstYOff = (int) (0.5 + nChunkYOff/dfYRatioDstToSrc); + int nDstYOff2 = (int) + (0.5 + (nChunkYOff+nFullResYChunk)/dfYRatioDstToSrc); + + if( nChunkYOff + nFullResYChunk == nHeight ) + nDstYOff2 = nDstHeight; + //CPLDebug("GDAL", "nDstYOff=%d, nDstYOff2=%d", nDstYOff, nDstYOff2); + + if( eType == GDT_Byte || eType == GDT_UInt16 || eType == GDT_Float32 ) + eErr = pfnResampleFn(dfXRatioDstToSrc, dfYRatioDstToSrc, + 0.0, 0.0, + eType, + pChunk, + pabyChunkNodataMask, + 0, nWidth, + nChunkYOffQueried, nChunkYSizeQueried, + 0, nDstWidth, + nDstYOff, nDstYOff2, + papoOvrBands[iOverview], pszResampling, + bHasNoData, fNoDataValue, poColorTable, + poSrcBand->GetRasterDataType()); + else + eErr = GDALResampleChunkC32R(nWidth, nHeight, + (float*)pChunk, + nChunkYOffQueried, nChunkYSizeQueried, + nDstYOff, nDstYOff2, + papoOvrBands[iOverview], pszResampling); + } + } + + VSIFree( pChunk ); + VSIFree( pabyChunkNodataMask ); + +/* -------------------------------------------------------------------- */ +/* Renormalized overview mean / stddev if needed. */ +/* -------------------------------------------------------------------- */ + if( eErr == CE_None && EQUAL(pszResampling,"AVERAGE_MP") ) + { + GDALOverviewMagnitudeCorrection( (GDALRasterBandH) poSrcBand, + nOverviewCount, + (GDALRasterBandH *) papoOvrBands, + GDALDummyProgress, NULL ); + } + +/* -------------------------------------------------------------------- */ +/* It can be important to flush out data to overviews. */ +/* -------------------------------------------------------------------- */ + for( int iOverview = 0; + eErr == CE_None && iOverview < nOverviewCount; + iOverview++ ) + { + eErr = papoOvrBands[iOverview]->FlushCache(); + } + + if (eErr == CE_None) + pfnProgress( 1.0, NULL, pProgressData ); + + return eErr; +} + + + +/************************************************************************/ +/* GDALRegenerateOverviewsMultiBand() */ +/************************************************************************/ + +/** + * \brief Variant of GDALRegenerateOverviews, specialy dedicated for generating + * compressed pixel-interleaved overviews (JPEG-IN-TIFF for example) + * + * This function will generate one or more overview images from a base + * image using the requested downsampling algorithm. It's primary use + * is for generating overviews via GDALDataset::BuildOverviews(), but it + * can also be used to generate downsampled images in one file from another + * outside the overview architecture. + * + * The output bands need to exist in advance and share the same characteristics + * (type, dimensions) + * + * The resampling algorithms supported for the moment are "NEAREST", "AVERAGE" + * and "GAUSS" + * + * The pseudo-algorithm used by the function is : + * for each overview + * iterate on lines of the source by a step of deltay + * iterate on columns of the source by a step of deltax + * read the source data of size deltax * deltay for all the bands + * generate the corresponding overview block for all the bands + * + * This function will honour properly NODATA_VALUES tuples (special dataset metadata) so + * that only a given RGB triplet (in case of a RGB image) will be considered as the + * nodata value and not each value of the triplet independantly per band. + * + * @param nBands the number of bands, size of papoSrcBands and size of + * first dimension of papapoOverviewBands + * @param papoSrcBands the list of source bands to downsample + * @param nOverviews the number of downsampled overview levels being generated. + * @param papapoOverviewBands bidimension array of bands. First dimension is indexed + * by nBands. Second dimension is indexed by nOverviews. + * @param pszResampling Resampling algorithm ("NEAREST", "AVERAGE" or "GAUSS"). + * @param pfnProgress progress report function. + * @param pProgressData progress function callback data. + * @return CE_None on success or CE_Failure on failure. + */ + +CPLErr +GDALRegenerateOverviewsMultiBand(int nBands, GDALRasterBand** papoSrcBands, + int nOverviews, + GDALRasterBand*** papapoOverviewBands, + const char * pszResampling, + GDALProgressFunc pfnProgress, void * pProgressData ) +{ + CPLErr eErr = CE_None; + int iOverview, iBand; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + if( EQUAL(pszResampling,"NONE") ) + return CE_None; + + /* Sanity checks */ + if (!EQUALN(pszResampling, "NEAR", 4) && + !EQUAL(pszResampling, "AVERAGE") && + !EQUAL(pszResampling, "GAUSS") && + !EQUAL(pszResampling, "CUBIC") && + !EQUAL(pszResampling,"CUBICSPLINE") && + !EQUAL(pszResampling,"LANCZOS") && + !EQUAL(pszResampling,"BILINEAR")) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GDALRegenerateOverviewsMultiBand: pszResampling='%s' not supported", pszResampling); + return CE_Failure; + } + + int nKernelRadius; + GDALResampleFunction pfnResampleFn = GDALGetResampleFunction(pszResampling, + &nKernelRadius); + if (pfnResampleFn == NULL) + return CE_Failure; + + int nSrcWidth = papoSrcBands[0]->GetXSize(); + int nSrcHeight = papoSrcBands[0]->GetYSize(); + GDALDataType eDataType = papoSrcBands[0]->GetRasterDataType(); + for(iBand=1;iBandGetXSize() != nSrcWidth || + papoSrcBands[iBand]->GetYSize() != nSrcHeight) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GDALRegenerateOverviewsMultiBand: all the source bands must have the same dimensions"); + return CE_Failure; + } + if (papoSrcBands[iBand]->GetRasterDataType() != eDataType) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GDALRegenerateOverviewsMultiBand: all the source bands must have the same data type"); + return CE_Failure; + } + } + + for(iOverview=0;iOverviewGetXSize(); + int nDstHeight = papapoOverviewBands[0][iOverview]->GetYSize(); + for(iBand=1;iBandGetXSize() != nDstWidth || + papapoOverviewBands[iBand][iOverview]->GetYSize() != nDstHeight) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GDALRegenerateOverviewsMultiBand: all the overviews bands of the same level must have the same dimensions"); + return CE_Failure; + } + if (papapoOverviewBands[iBand][iOverview]->GetRasterDataType() != eDataType) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GDALRegenerateOverviewsMultiBand: all the overviews bands must have the same data type as the source bands"); + return CE_Failure; + } + } + } + + /* First pass to compute the total number of pixels to read */ + double dfTotalPixelCount = 0; + for(iOverview=0;iOverviewGetXSize(); + nSrcHeight = papoSrcBands[0]->GetYSize(); + + int nDstWidth = papapoOverviewBands[0][iOverview]->GetXSize(); + /* Try to use previous level of overview as the source to compute */ + /* the next level */ + if (iOverview > 0 && papapoOverviewBands[0][iOverview - 1]->GetXSize() > nDstWidth) + { + nSrcWidth = papapoOverviewBands[0][iOverview - 1]->GetXSize(); + nSrcHeight = papapoOverviewBands[0][iOverview - 1]->GetYSize(); + } + + dfTotalPixelCount += (double)nSrcWidth * nSrcHeight; + } + + nSrcWidth = papoSrcBands[0]->GetXSize(); + nSrcHeight = papoSrcBands[0]->GetYSize(); + + GDALDataType eWrkDataType = GDALGetOvrWorkDataType(pszResampling, eDataType); + + /* If we have a nodata mask and we are doing something more complicated */ + /* than nearest neighbouring, we have to fetch to nodata mask */ + int bUseNoDataMask = (!EQUALN(pszResampling,"NEAR",4) && + (papoSrcBands[0]->GetMaskFlags() & GMF_ALL_VALID) == 0); + + int* pabHasNoData = (int*)CPLMalloc(nBands * sizeof(int)); + float* pafNoDataValue = (float*)CPLMalloc(nBands * sizeof(float)); + + for(iBand=0;iBandGetNoDataValue(&pabHasNoData[iBand]); + } + + /* Second pass to do the real job ! */ + double dfCurPixelCount = 0; + for(iOverview=0;iOverviewGetBlockSize(&nDstBlockXSize, &nDstBlockYSize); + nDstWidth = papapoOverviewBands[0][iOverview]->GetXSize(); + nDstHeight = papapoOverviewBands[0][iOverview]->GetYSize(); + + /* Try to use previous level of overview as the source to compute */ + /* the next level */ + if (iOverview > 0 && papapoOverviewBands[0][iOverview - 1]->GetXSize() > nDstWidth) + { + nSrcWidth = papapoOverviewBands[0][iOverview - 1]->GetXSize(); + nSrcHeight = papapoOverviewBands[0][iOverview - 1]->GetYSize(); + iSrcOverview = iOverview - 1; + } + + double dfXRatioDstToSrc = (double)nSrcWidth / nDstWidth; + double dfYRatioDstToSrc = (double)nSrcHeight / nDstHeight; + + /* Compute the maximum chunck size of the source such as it will match the size of */ + /* a block of the overview */ + int nFullResXChunk = 1 + (int)(nDstBlockXSize * dfXRatioDstToSrc); + int nFullResYChunk = 1 + (int)(nDstBlockYSize * dfYRatioDstToSrc); + + int nOvrFactor = MAX( (int)(0.5 + dfXRatioDstToSrc), + (int)(0.5 + dfYRatioDstToSrc) ); + if( nOvrFactor == 0 ) nOvrFactor = 1; + int nFullResXChunkQueried = nFullResXChunk + 2 * nKernelRadius * nOvrFactor; + int nFullResYChunkQueried = nFullResYChunk + 2 * nKernelRadius * nOvrFactor; + + void** papaChunk = (void**) CPLMalloc(nBands * sizeof(void*)); + GByte* pabyChunkNoDataMask = NULL; + for(iBand=0;iBand= 0) + CPLFree(papaChunk[iBand]); + CPLFree(papaChunk); + CPLFree(pabHasNoData); + CPLFree(pafNoDataValue); + + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALRegenerateOverviewsMultiBand: Out of memory." ); + return CE_Failure; + } + } + if (bUseNoDataMask) + { + pabyChunkNoDataMask = (GByte*) VSIMalloc2(nFullResXChunkQueried, nFullResYChunkQueried); + if( pabyChunkNoDataMask == NULL ) + { + for(iBand=0;iBand nSrcHeight || nDstYOff + nDstYCount == nDstHeight) + nChunkYOff2 = nSrcHeight; + int nYCount = nChunkYOff2 - nChunkYOff; + CPLAssert(nYCount <= nFullResYChunk); + + int nChunkYOffQueried = nChunkYOff - nKernelRadius * nOvrFactor; + int nChunkYSizeQueried = nYCount + 2 * nKernelRadius * nOvrFactor; + if( nChunkYOffQueried < 0 ) + { + nChunkYSizeQueried += nChunkYOffQueried; + nChunkYOffQueried = 0; + } + if( nChunkYSizeQueried + nChunkYOffQueried > nSrcHeight ) + nChunkYSizeQueried = nSrcHeight - nChunkYOffQueried; + CPLAssert(nChunkYSizeQueried <= nFullResYChunkQueried); + + if( !pfnProgress( dfCurPixelCount / dfTotalPixelCount, + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + eErr = CE_Failure; + } + + int nDstXOff; + /* Iterate on destination overview, block by block */ + for( nDstXOff = 0; nDstXOff < nDstWidth && eErr == CE_None; nDstXOff += nDstBlockXSize ) + { + int nDstXCount; + if (nDstXOff + nDstBlockXSize <= nDstWidth) + nDstXCount = nDstBlockXSize; + else + nDstXCount = nDstWidth - nDstXOff; + + int nChunkXOff = (int) (0.5 + nDstXOff * dfXRatioDstToSrc); + int nChunkXOff2 = (int) (0.5 + (nDstXOff + nDstXCount) * dfXRatioDstToSrc); + if( nChunkXOff2 > nSrcWidth || nDstXOff + nDstXCount == nDstWidth) + nChunkXOff2 = nSrcWidth; + int nXCount = nChunkXOff2 - nChunkXOff; + CPLAssert(nXCount <= nFullResXChunk); + + int nChunkXOffQueried = nChunkXOff - nKernelRadius * nOvrFactor; + int nChunkXSizeQueried = nXCount + 2 * nKernelRadius * nOvrFactor; + if( nChunkXOffQueried < 0 ) + { + nChunkXSizeQueried += nChunkXOffQueried; + nChunkXOffQueried = 0; + } + if( nChunkXSizeQueried + nChunkXOffQueried > nSrcWidth ) + nChunkXSizeQueried = nSrcWidth - nChunkXOffQueried; + CPLAssert(nChunkXSizeQueried <= nFullResXChunkQueried); + /*CPLDebug("GDAL", "Reading (%dx%d -> %dx%d) for output (%dx%d -> %dx%d)", + nChunkXOff, nChunkYOff, nXCount, nYCount, + nDstXOff, nDstYOff, nDstXCount, nDstYCount);*/ + + /* Read the source buffers for all the bands */ + for(iBand=0;iBandRasterIO( GF_Read, + nChunkXOffQueried, nChunkYOffQueried, + nChunkXSizeQueried, nChunkYSizeQueried, + papaChunk[iBand], + nChunkXSizeQueried, nChunkYSizeQueried, + eWrkDataType, 0, 0, NULL ); + } + + if (bUseNoDataMask && eErr == CE_None) + { + GDALRasterBand* poSrcBand; + if (iSrcOverview == -1) + poSrcBand = papoSrcBands[0]; + else + poSrcBand = papapoOverviewBands[0][iSrcOverview]; + eErr = poSrcBand->GetMaskBand()->RasterIO( GF_Read, + nChunkXOffQueried, nChunkYOffQueried, + nChunkXSizeQueried, nChunkYSizeQueried, + pabyChunkNoDataMask, + nChunkXSizeQueried, nChunkYSizeQueried, + GDT_Byte, 0, 0, NULL ); + } + + /* Compute the resulting overview block */ + for(iBand=0;iBandFlushCache(); + } + CPLFree(papaChunk); + CPLFree(pabyChunkNoDataMask); + + } + + CPLFree(pabHasNoData); + CPLFree(pafNoDataValue); + + if (eErr == CE_None) + pfnProgress( 1.0, NULL, pProgressData ); + + return eErr; +} + + +/************************************************************************/ +/* GDALComputeBandStats() */ +/************************************************************************/ + +CPLErr CPL_STDCALL +GDALComputeBandStats( GDALRasterBandH hSrcBand, + int nSampleStep, + double *pdfMean, double *pdfStdDev, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + VALIDATE_POINTER1( hSrcBand, "GDALComputeBandStats", CE_Failure ); + + GDALRasterBand *poSrcBand = (GDALRasterBand *) hSrcBand; + int iLine, nWidth, nHeight; + GDALDataType eType = poSrcBand->GetRasterDataType(); + GDALDataType eWrkType; + int bComplex; + float *pafData; + double dfSum=0.0, dfSum2=0.0; + int nSamples = 0; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + + nWidth = poSrcBand->GetXSize(); + nHeight = poSrcBand->GetYSize(); + + if( nSampleStep >= nHeight || nSampleStep < 1 ) + nSampleStep = 1; + + bComplex = GDALDataTypeIsComplex(eType); + if( bComplex ) + { + pafData = (float *) VSIMalloc(nWidth * 2 * sizeof(float)); + eWrkType = GDT_CFloat32; + } + else + { + pafData = (float *) VSIMalloc(nWidth * sizeof(float)); + eWrkType = GDT_Float32; + } + + if( pafData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALComputeBandStats: Out of memory for buffer." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Loop over all sample lines. */ +/* -------------------------------------------------------------------- */ + for( iLine = 0; iLine < nHeight; iLine += nSampleStep ) + { + int iPixel; + + if( !pfnProgress( iLine / (double) nHeight, + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + CPLFree( pafData ); + return CE_Failure; + } + + CPLErr eErr = poSrcBand->RasterIO( GF_Read, 0, iLine, nWidth, 1, + pafData, nWidth, 1, eWrkType, + 0, 0, NULL ); + if ( eErr != CE_None ) + { + CPLFree( pafData ); + return eErr; + } + + for( iPixel = 0; iPixel < nWidth; iPixel++ ) + { + float fValue; + + if( bComplex ) + { + // Compute the magnitude of the complex value. + + fValue = (float) + sqrt(pafData[iPixel*2 ] * pafData[iPixel*2 ] + + pafData[iPixel*2+1] * pafData[iPixel*2+1]); + } + else + { + fValue = pafData[iPixel]; + } + + dfSum += fValue; + dfSum2 += fValue * fValue; + } + + nSamples += nWidth; + } + + if( !pfnProgress( 1.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + CPLFree( pafData ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Produce the result values. */ +/* -------------------------------------------------------------------- */ + if( pdfMean != NULL ) + *pdfMean = dfSum / nSamples; + + if( pdfStdDev != NULL ) + { + double dfMean = dfSum / nSamples; + + *pdfStdDev = sqrt((dfSum2 / nSamples) - (dfMean * dfMean)); + } + + CPLFree( pafData ); + + return CE_None; +} + +/************************************************************************/ +/* GDALOverviewMagnitudeCorrection() */ +/* */ +/* Correct the mean and standard deviation of the overviews of */ +/* the given band to match the base layer approximately. */ +/************************************************************************/ + +CPLErr +GDALOverviewMagnitudeCorrection( GDALRasterBandH hBaseBand, + int nOverviewCount, + GDALRasterBandH *pahOverviews, + GDALProgressFunc pfnProgress, + void *pProgressData ) + +{ + VALIDATE_POINTER1( hBaseBand, "GDALOverviewMagnitudeCorrection", CE_Failure ); + + CPLErr eErr; + double dfOrigMean, dfOrigStdDev; + +/* -------------------------------------------------------------------- */ +/* Compute mean/stddev for source raster. */ +/* -------------------------------------------------------------------- */ + eErr = GDALComputeBandStats( hBaseBand, 2, &dfOrigMean, &dfOrigStdDev, + pfnProgress, pProgressData ); + + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Loop on overview bands. */ +/* -------------------------------------------------------------------- */ + int iOverview; + + for( iOverview = 0; iOverview < nOverviewCount; iOverview++ ) + { + GDALRasterBand *poOverview = (GDALRasterBand *)pahOverviews[iOverview]; + double dfOverviewMean, dfOverviewStdDev; + double dfGain; + + eErr = GDALComputeBandStats( pahOverviews[iOverview], 1, + &dfOverviewMean, &dfOverviewStdDev, + pfnProgress, pProgressData ); + + if( eErr != CE_None ) + return eErr; + + if( dfOrigStdDev < 0.0001 ) + dfGain = 1.0; + else + dfGain = dfOrigStdDev / dfOverviewStdDev; + +/* -------------------------------------------------------------------- */ +/* Apply gain and offset. */ +/* -------------------------------------------------------------------- */ + GDALDataType eWrkType, eType = poOverview->GetRasterDataType(); + int iLine, nWidth, nHeight, bComplex; + float *pafData; + + nWidth = poOverview->GetXSize(); + nHeight = poOverview->GetYSize(); + + bComplex = GDALDataTypeIsComplex(eType); + if( bComplex ) + { + pafData = (float *) VSIMalloc2(nWidth, 2 * sizeof(float)); + eWrkType = GDT_CFloat32; + } + else + { + pafData = (float *) VSIMalloc2(nWidth, sizeof(float)); + eWrkType = GDT_Float32; + } + + if( pafData == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "GDALOverviewMagnitudeCorrection: Out of memory for buffer." ); + return CE_Failure; + } + + for( iLine = 0; iLine < nHeight; iLine++ ) + { + int iPixel; + + if( !pfnProgress( iLine / (double) nHeight, + NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + CPLFree( pafData ); + return CE_Failure; + } + + poOverview->RasterIO( GF_Read, 0, iLine, nWidth, 1, + pafData, nWidth, 1, eWrkType, + 0, 0, NULL ); + + for( iPixel = 0; iPixel < nWidth; iPixel++ ) + { + if( bComplex ) + { + pafData[iPixel*2] *= (float) dfGain; + pafData[iPixel*2+1] *= (float) dfGain; + } + else + { + pafData[iPixel] = (float) + ((pafData[iPixel]-dfOverviewMean)*dfGain + dfOrigMean); + + } + } + + poOverview->RasterIO( GF_Write, 0, iLine, nWidth, 1, + pafData, nWidth, 1, eWrkType, + 0, 0, NULL ); + } + + if( !pfnProgress( 1.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); + CPLFree( pafData ); + return CE_Failure; + } + + CPLFree( pafData ); + } + + return CE_None; +} diff --git a/bazaar/plugin/gdal/gcore/rasterio.cpp b/bazaar/plugin/gdal/gcore/rasterio.cpp new file mode 100644 index 000000000..af16a6af3 --- /dev/null +++ b/bazaar/plugin/gdal/gcore/rasterio.cpp @@ -0,0 +1,3965 @@ +/****************************************************************************** + * $Id: rasterio.cpp 29161 2015-05-06 10:18:19Z rouault $ + * + * Project: GDAL Core + * Purpose: Contains default implementation of GDALRasterBand::IRasterIO() + * and supporting functions of broader utility. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gdal_priv.h" +#include "gdal_vrt.h" +#include "vrtdataset.h" +#include "memdataset.h" +#include "gdalwarper.h" + +// Define a list of "C++" compilers that have broken template support or +// broken scoping so we can fall back on the legacy implementation of +// GDALCopyWords +#define NOT_BROKEN_COMPILER \ + (!(defined(_MSC_VER) && _MSC_VER <= 1200) && !defined(__BORLANDC__) && \ + !defined(__SUNPRO_CC)) + +#if NOT_BROKEN_COMPILER +#include +#include + +// For now, work around MSVC++ 6.0's broken template support. If this value +// is not defined, the old GDALCopyWords implementation is used. +#define USE_NEW_COPYWORDS 1 +#endif + + +CPL_CVSID("$Id: rasterio.cpp 29161 2015-05-06 10:18:19Z rouault $"); + +/************************************************************************/ +/* IRasterIO() */ +/* */ +/* Default internal implementation of RasterIO() ... utilizes */ +/* the Block access methods to satisfy the request. This would */ +/* normally only be overridden by formats with overviews. */ +/************************************************************************/ + +CPLErr GDALRasterBand::IRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + int nBandDataSize = GDALGetDataTypeSize( eDataType ) / 8; + int nBufDataSize = GDALGetDataTypeSize( eBufType ) / 8; + GByte *pabySrcBlock = NULL; + GDALRasterBlock *poBlock = NULL; + int nLBlockX=-1, nLBlockY=-1, iBufYOff, iBufXOff, iSrcY; + + if( eRWFlag == GF_Write && eFlushBlockErr != CE_None ) + { + CPLError(eFlushBlockErr, CPLE_AppDefined, + "An error occured while writing a dirty block"); + CPLErr eErr = eFlushBlockErr; + eFlushBlockErr = CE_None; + return eErr; + } + +/* ==================================================================== */ +/* A common case is the data requested with the destination */ +/* is packed, and the block width is the raster width. */ +/* ==================================================================== */ + if( nPixelSpace == nBufDataSize + && nLineSpace == nPixelSpace * nXSize + && nBlockXSize == GetXSize() + && nBufXSize == nXSize + && nBufYSize == nYSize ) + { +// printf( "IRasterIO(%d,%d,%d,%d) rw=%d case 1\n", +// nXOff, nYOff, nXSize, nYSize, +// (int) eRWFlag ); + CPLErr eErr = CE_None; + + for( iBufYOff = 0; iBufYOff < nBufYSize; iBufYOff++ ) + { + int nSrcByteOffset; + + iSrcY = iBufYOff + nYOff; + + if( iSrcY < nLBlockY * nBlockYSize + || iSrcY >= (nLBlockY+1) * nBlockYSize ) + { + nLBlockY = iSrcY / nBlockYSize; + int bJustInitialize = + eRWFlag == GF_Write + && nXOff == 0 && nXSize == nBlockXSize + && nYOff <= nLBlockY * nBlockYSize + && nYOff + nYSize >= (nLBlockY+1) * nBlockYSize; + + /* Is this a partial tile at right and/or bottom edges of */ + /* the raster, and that is going to be completely written ? */ + /* If so, don't load it from storage, but zeroized it so that */ + /* the content outsize of the validity area is initialized */ + int bMemZeroBuffer = FALSE; + if( eRWFlag == GF_Write && !bJustInitialize && + nXOff == 0 && nXSize == nBlockXSize && + nYOff <= nLBlockY * nBlockYSize && + nYOff + nYSize == GetYSize() && + (nLBlockY+1) * nBlockYSize > GetYSize() ) + { + bJustInitialize = TRUE; + bMemZeroBuffer = TRUE; + } + + if( poBlock ) + poBlock->DropLock(); + + poBlock = GetLockedBlockRef( 0, nLBlockY, bJustInitialize ); + if( poBlock == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GetBlockRef failed at X block offset %d, " + "Y block offset %d", 0, nLBlockY ); + eErr = CE_Failure; + break; + } + + if( eRWFlag == GF_Write ) + poBlock->MarkDirty(); + + pabySrcBlock = (GByte *) poBlock->GetDataRef(); + if( pabySrcBlock == NULL ) + { + poBlock->DropLock(); + eErr = CE_Failure; + break; + } + if( bMemZeroBuffer ) + { + memset(pabySrcBlock, 0, + nBandDataSize * nBlockXSize * nBlockYSize); + } + } + + nSrcByteOffset = ((iSrcY-nLBlockY*nBlockYSize)*nBlockXSize + nXOff) + * nBandDataSize; + + if( eDataType == eBufType ) + { + if( eRWFlag == GF_Read ) + memcpy( ((GByte *) pData) + (GPtrDiff_t)iBufYOff * nLineSpace, + pabySrcBlock + nSrcByteOffset, + (size_t)nLineSpace ); + else + memcpy( pabySrcBlock + nSrcByteOffset, + ((GByte *) pData) + (GPtrDiff_t)iBufYOff * nLineSpace, + (size_t)nLineSpace ); + } + else + { + /* type to type conversion */ + + if( eRWFlag == GF_Read ) + GDALCopyWords( pabySrcBlock + nSrcByteOffset, + eDataType, nBandDataSize, + ((GByte *) pData) + (GPtrDiff_t)iBufYOff * nLineSpace, + eBufType, (int)nPixelSpace, nBufXSize ); + else + GDALCopyWords( ((GByte *) pData) + (GPtrDiff_t)iBufYOff * nLineSpace, + eBufType, (int)nPixelSpace, + pabySrcBlock + nSrcByteOffset, + eDataType, nBandDataSize, nBufXSize ); + } + + if( psExtraArg->pfnProgress != NULL && + !psExtraArg->pfnProgress(1.0 * (iBufYOff + 1) / nBufYSize, "", + psExtraArg->pProgressData) ) + { + eErr = CE_Failure; + break; + } + } + + if( poBlock ) + poBlock->DropLock(); + + return eErr; + } + +/* ==================================================================== */ +/* Do we have overviews that would be appropriate to satisfy */ +/* this request? */ +/* ==================================================================== */ + if( (nBufXSize < nXSize || nBufYSize < nYSize) + && GetOverviewCount() > 0 && eRWFlag == GF_Read ) + { + int nOverview; + GDALRasterIOExtraArg sExtraArg; + + GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg); + + nOverview = + GDALBandGetBestOverviewLevel2(this, nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, &sExtraArg); + if (nOverview >= 0) + { + GDALRasterBand* poOverviewBand = GetOverview(nOverview); + if (poOverviewBand == NULL) + return CE_Failure; + + return poOverviewBand->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, &sExtraArg ); + } + } + + + if( eRWFlag == GF_Read && + nBufXSize < nXSize / 100 && nBufYSize < nYSize / 100 && + nPixelSpace == nBufDataSize && + nLineSpace == nPixelSpace * nBufXSize && + CSLTestBoolean(CPLGetConfigOption("GDAL_NO_COSTLY_OVERVIEW", "NO")) ) + { + memset(pData, 0, (size_t)(nLineSpace * nBufYSize)); + return CE_None; + } + + +/* ==================================================================== */ +/* The second case when we don't need subsample data but likely */ +/* need data type conversion. */ +/* ==================================================================== */ + int iSrcX; + + if ( /* nPixelSpace == nBufDataSize + && */ nXSize == nBufXSize + && nYSize == nBufYSize ) + { +// printf( "IRasterIO(%d,%d,%d,%d) rw=%d case 2\n", +// nXOff, nYOff, nXSize, nYSize, +// (int) eRWFlag ); + +/* -------------------------------------------------------------------- */ +/* Loop over buffer computing source locations. */ +/* -------------------------------------------------------------------- */ + int nLBlockXStart, nXSpanEnd; + + // Calculate starting values out of loop + nLBlockXStart = nXOff / nBlockXSize; + nXSpanEnd = nBufXSize + nXOff; + + int nYInc = 0; + for( iBufYOff = 0, iSrcY = nYOff; iBufYOff < nBufYSize; iBufYOff+=nYInc, iSrcY+=nYInc ) + { + GPtrDiff_t iBufOffset; + GPtrDiff_t iSrcOffset; + int nXSpan; + + iBufOffset = (GPtrDiff_t)iBufYOff * (GPtrDiff_t)nLineSpace; + nLBlockY = iSrcY / nBlockYSize; + nLBlockX = nLBlockXStart; + iSrcX = nXOff; + while( iSrcX < nXSpanEnd ) + { + int nXSpanSize; + + nXSpan = (nLBlockX + 1) * nBlockXSize; + nXSpan = ( ( nXSpan < nXSpanEnd )?nXSpan:nXSpanEnd ) - iSrcX; + nXSpanSize = nXSpan * (int)nPixelSpace; + + int bJustInitialize = + eRWFlag == GF_Write + && nYOff <= nLBlockY * nBlockYSize + && nYOff + nYSize >= (nLBlockY+1) * nBlockYSize + && nXOff <= nLBlockX * nBlockXSize + && nXOff + nXSize >= (nLBlockX+1) * nBlockXSize; + + /* Is this a partial tile at right and/or bottom edges of */ + /* the raster, and that is going to be completely written ? */ + /* If so, don't load it from storage, but zeroized it so that */ + /* the content outsize of the validity area is initialized */ + int bMemZeroBuffer = FALSE; + if( eRWFlag == GF_Write && !bJustInitialize && + nXOff <= nLBlockX * nBlockXSize && + nYOff <= nLBlockY * nBlockYSize && + (nXOff + nXSize >= (nLBlockX+1) * nBlockXSize || + (nXOff + nXSize == GetXSize() && (nLBlockX+1) * nBlockXSize > GetXSize())) && + (nYOff + nYSize >= (nLBlockY+1) * nBlockYSize || + (nYOff + nYSize == GetYSize() && (nLBlockY+1) * nBlockYSize > GetYSize())) ) + { + bJustInitialize = TRUE; + bMemZeroBuffer = TRUE; + } +// printf( "bJustInitialize = %d (%d,%d,%d,%d)\n", +// bJustInitialize, +// nYOff, nYSize, +// nLBlockY, nBlockYSize ); +// bJustInitialize = FALSE; + + +/* -------------------------------------------------------------------- */ +/* Ensure we have the appropriate block loaded. */ +/* -------------------------------------------------------------------- */ + poBlock = GetLockedBlockRef( nLBlockX, nLBlockY, bJustInitialize ); + if( !poBlock ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GetBlockRef failed at X block offset %d, " + "Y block offset %d", nLBlockX, nLBlockY ); + return( CE_Failure ); + } + + if( eRWFlag == GF_Write ) + poBlock->MarkDirty(); + + pabySrcBlock = (GByte *) poBlock->GetDataRef(); + if( pabySrcBlock == NULL ) + { + poBlock->DropLock(); + return CE_Failure; + } + if( bMemZeroBuffer ) + { + memset(pabySrcBlock, 0, + nBandDataSize * nBlockXSize * nBlockYSize); + } +/* -------------------------------------------------------------------- */ +/* Copy over this chunk of data. */ +/* -------------------------------------------------------------------- */ + iSrcOffset = ((GPtrDiff_t)iSrcX - (GPtrDiff_t)nLBlockX*nBlockXSize + + ((GPtrDiff_t)(iSrcY) - (GPtrDiff_t)nLBlockY*nBlockYSize) * nBlockXSize)*nBandDataSize; + /* Fill up as many rows as possible for the loaded block */ + int kmax = MIN(nBlockYSize - (iSrcY % nBlockYSize), nBufYSize - iBufYOff); + for(int k=0; kDropLock(); + poBlock = NULL; + } + + /* Compute the increment to go on a block boundary */ + nYInc = nBlockYSize - (iSrcY % nBlockYSize); + + if( psExtraArg->pfnProgress != NULL && + !psExtraArg->pfnProgress(1.0 * MIN(nBufYSize, iBufYOff + nYInc) / nBufYSize, "", + psExtraArg->pProgressData) ) + { + return CE_Failure; + } + } + + return CE_None; + } + +/* ==================================================================== */ +/* Loop reading required source blocks to satisfy output */ +/* request. This is the most general implementation. */ +/* ==================================================================== */ + + double dfXOff, dfYOff, dfXSize, dfYSize; + if( psExtraArg->bFloatingPointWindowValidity ) + { + dfXOff = psExtraArg->dfXOff; + dfYOff = psExtraArg->dfYOff; + dfXSize = psExtraArg->dfXSize; + dfYSize = psExtraArg->dfYSize; + CPLAssert(dfXOff - nXOff < 1.0); + CPLAssert(dfYOff - nYOff < 1.0); + CPLAssert(nXSize - dfXSize < 1.0); + CPLAssert(nYSize - dfYSize < 1.0); + } + else + { + dfXOff = nXOff; + dfYOff = nYOff; + dfXSize = nXSize; + dfYSize = nYSize; + } +/* -------------------------------------------------------------------- */ +/* Compute stepping increment. */ +/* -------------------------------------------------------------------- */ + double dfSrcXInc, dfSrcYInc; + dfSrcXInc = dfXSize / (double) nBufXSize; + dfSrcYInc = dfYSize / (double) nBufYSize; + CPLErr eErr = CE_None; + +// printf( "IRasterIO(%d,%d,%d,%d) rw=%d case 3\n", +// nXOff, nYOff, nXSize, nYSize, +// (int) eRWFlag ); + if (eRWFlag == GF_Write) + { +/* -------------------------------------------------------------------- */ +/* Write case */ +/* Loop over raster window computing source locations in the buffer. */ +/* -------------------------------------------------------------------- */ + int iDstX, iDstY; + GByte* pabyDstBlock = NULL; + + for( iDstY = nYOff; iDstY < nYOff + nYSize; iDstY ++) + { + GPtrDiff_t iBufOffset, iDstOffset; + iBufYOff = (int)((iDstY - nYOff) / dfSrcYInc); + + for( iDstX = nXOff; iDstX < nXOff + nXSize; iDstX ++) + { + iBufXOff = (int)((iDstX - nXOff) / dfSrcXInc); + iBufOffset = (GPtrDiff_t)iBufYOff * (GPtrDiff_t)nLineSpace + iBufXOff * (GPtrDiff_t)nPixelSpace; + + // FIXME: this code likely doesn't work if the dirty block gets flushed + // to disk before being completely written. + // In the meantime, bJustInitalize should probably be set to FALSE + // even if it is not ideal performance wise, and for lossy compression + + /* -------------------------------------------------------------------- */ + /* Ensure we have the appropriate block loaded. */ + /* -------------------------------------------------------------------- */ + if( iDstX < nLBlockX * nBlockXSize + || iDstX >= (nLBlockX+1) * nBlockXSize + || iDstY < nLBlockY * nBlockYSize + || iDstY >= (nLBlockY+1) * nBlockYSize ) + { + nLBlockX = iDstX / nBlockXSize; + nLBlockY = iDstY / nBlockYSize; + + int bJustInitialize = + nYOff <= nLBlockY * nBlockYSize + && nYOff + nYSize >= (nLBlockY+1) * nBlockYSize + && nXOff <= nLBlockX * nBlockXSize + && nXOff + nXSize >= (nLBlockX+1) * nBlockXSize; + /*int bMemZeroBuffer = FALSE; + if( !bJustInitialize && + nXOff <= nLBlockX * nBlockXSize && + nYOff <= nLBlockY * nBlockYSize && + (nXOff + nXSize >= (nLBlockX+1) * nBlockXSize || + (nXOff + nXSize == GetXSize() && (nLBlockX+1) * nBlockXSize > GetXSize())) && + (nYOff + nYSize >= (nLBlockY+1) * nBlockYSize || + (nYOff + nYSize == GetYSize() && (nLBlockY+1) * nBlockYSize > GetYSize())) ) + { + bJustInitialize = TRUE; + bMemZeroBuffer = TRUE; + }*/ + if( poBlock != NULL ) + poBlock->DropLock(); + + poBlock = GetLockedBlockRef( nLBlockX, nLBlockY, + bJustInitialize ); + if( poBlock == NULL ) + { + return( CE_Failure ); + } + + poBlock->MarkDirty(); + + pabyDstBlock = (GByte *) poBlock->GetDataRef(); + if( pabyDstBlock == NULL ) + { + poBlock->DropLock(); + return CE_Failure; + } + /*if( bMemZeroBuffer ) + { + memset(pabyDstBlock, 0, + nBandDataSize * nBlockXSize * nBlockYSize); + }*/ + } + + /* -------------------------------------------------------------------- */ + /* Copy over this pixel of data. */ + /* -------------------------------------------------------------------- */ + iDstOffset = ((GPtrDiff_t)iDstX - (GPtrDiff_t)nLBlockX*nBlockXSize + + ((GPtrDiff_t)iDstY - (GPtrDiff_t)nLBlockY*nBlockYSize) * nBlockXSize)*nBandDataSize; + + if( eDataType == eBufType ) + { + memcpy( pabyDstBlock + iDstOffset, + ((GByte *) pData) + iBufOffset, nBandDataSize ); + } + else + { + /* type to type conversion ... ouch, this is expensive way + of handling single words */ + + GDALCopyWords( ((GByte *) pData) + iBufOffset, eBufType, 0, + pabyDstBlock + iDstOffset, eDataType, 0, + 1 ); + } + } + + if( psExtraArg->pfnProgress != NULL && + !psExtraArg->pfnProgress(1.0 * (iDstY - nYOff + 1) / nYSize, "", + psExtraArg->pProgressData) ) + { + eErr = CE_Failure; + break; + } + } + } + else + { + if( psExtraArg->eResampleAlg != GRIORA_NearestNeighbour ) + { + if( (psExtraArg->eResampleAlg == GRIORA_Cubic || + psExtraArg->eResampleAlg == GRIORA_CubicSpline || + psExtraArg->eResampleAlg == GRIORA_Bilinear || + psExtraArg->eResampleAlg == GRIORA_Lanczos) && + GetColorTable() != NULL ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Resampling method not supported on paletted band. " + "Falling back to nearest neighbour"); + } + else if( psExtraArg->eResampleAlg == GRIORA_Gauss && + GDALDataTypeIsComplex( eDataType ) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Resampling method not supported on complex data type band. " + "Falling back to nearest neighbour"); + } + else + { + return RasterIOResampled( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nPixelSpace, nLineSpace, + psExtraArg ); + } + } + + double dfSrcX, dfSrcY; + int nLimitBlockY = 0; + int bByteCopy = ( eDataType == eBufType && nBandDataSize == 1); + int nStartBlockX = -nBlockXSize; + +/* -------------------------------------------------------------------- */ +/* Read case */ +/* Loop over buffer computing source locations. */ +/* -------------------------------------------------------------------- */ + for( iBufYOff = 0; iBufYOff < nBufYSize; iBufYOff++ ) + { + GPtrDiff_t iBufOffset, iSrcOffset; + + dfSrcY = (iBufYOff+0.5) * dfSrcYInc + dfYOff; + dfSrcX = 0.5 * dfSrcXInc + dfXOff; + iSrcY = (int) dfSrcY; + + iBufOffset = (GPtrDiff_t)iBufYOff * (GPtrDiff_t)nLineSpace; + + if( iSrcY >= nLimitBlockY ) + { + nLBlockY = iSrcY / nBlockYSize; + nLimitBlockY = (nLBlockY + 1) * nBlockYSize; + nStartBlockX = -nBlockXSize; /* make sure a new block is loaded */ + } + else if( (int)dfSrcX < nStartBlockX ) + nStartBlockX = -nBlockXSize; /* make sure a new block is loaded */ + + GPtrDiff_t iSrcOffsetCst = (iSrcY - nLBlockY*nBlockYSize) * (GPtrDiff_t)nBlockXSize; + + for( iBufXOff = 0; iBufXOff < nBufXSize; iBufXOff++, dfSrcX += dfSrcXInc ) + { + iSrcX = (int) dfSrcX; + int nDiffX = iSrcX - nStartBlockX; + + /* -------------------------------------------------------------------- */ + /* Ensure we have the appropriate block loaded. */ + /* -------------------------------------------------------------------- */ + if( nDiffX >= nBlockXSize ) + { + nLBlockX = iSrcX / nBlockXSize; + nStartBlockX = nLBlockX * nBlockXSize; + nDiffX = iSrcX - nStartBlockX; + + if( poBlock != NULL ) + poBlock->DropLock(); + + poBlock = GetLockedBlockRef( nLBlockX, nLBlockY, + FALSE ); + if( poBlock == NULL ) + { + eErr = CE_Failure; + break; + } + + pabySrcBlock = (GByte *) poBlock->GetDataRef(); + if( pabySrcBlock == NULL ) + { + eErr = CE_Failure; + break; + } + } + + /* -------------------------------------------------------------------- */ + /* Copy over this pixel of data. */ + /* -------------------------------------------------------------------- */ + iSrcOffset = ((GPtrDiff_t)nDiffX + iSrcOffsetCst)*nBandDataSize; + + if( bByteCopy ) + { + ((GByte *) pData)[iBufOffset] = pabySrcBlock[iSrcOffset]; + } + else if( eDataType == eBufType ) + { + memcpy( ((GByte *) pData) + iBufOffset, + pabySrcBlock + iSrcOffset, nBandDataSize ); + } + else + { + /* type to type conversion ... ouch, this is expensive way + of handling single words */ + GDALCopyWords( pabySrcBlock + iSrcOffset, eDataType, 0, + ((GByte *) pData) + iBufOffset, eBufType, 0, + 1 ); + } + + iBufOffset += (int)nPixelSpace; + } + if( eErr == CE_Failure ) + break; + + if( psExtraArg->pfnProgress != NULL && + !psExtraArg->pfnProgress(1.0 * (iBufYOff + 1) / nBufYSize, "", + psExtraArg->pProgressData) ) + { + eErr = CE_Failure; + break; + } + } + } + + if( poBlock != NULL ) + poBlock->DropLock(); + + return( eErr ); +} + +/************************************************************************/ +/* GDALRasterIOTransformer() */ +/************************************************************************/ + +typedef struct +{ + double dfXOff, dfYOff; + double dfXRatioDstToSrc, dfYRatioDstToSrc; +} GDALRasterIOTransformerStruct; + +static int GDALRasterIOTransformer( void *pTransformerArg, + CPL_UNUSED int bDstToSrc, int nPointCount, + double *x, double *y, CPL_UNUSED double *z, + int *panSuccess ) +{ + CPLAssert(bDstToSrc); + GDALRasterIOTransformerStruct* psParams = (GDALRasterIOTransformerStruct*) pTransformerArg; + for(int i = 0; i < nPointCount; i++) + { + x[i] = x[i] * psParams->dfXRatioDstToSrc + psParams->dfXOff; + y[i] = y[i] * psParams->dfYRatioDstToSrc + psParams->dfYOff; + panSuccess[i] = TRUE; + } + return TRUE; +} + +/************************************************************************/ +/* RasterIOResampled() */ +/************************************************************************/ + +CPLErr GDALRasterBand::RasterIOResampled( CPL_UNUSED GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) +{ + CPLErr eErr = CE_None; + + // Determine if we use warping resampling or overview resampling + int bUseWarp; + if( !GDALDataTypeIsComplex( eDataType ) ) + bUseWarp = FALSE; + else + bUseWarp = TRUE; + + double dfXOff, dfYOff, dfXSize, dfYSize; + if( psExtraArg->bFloatingPointWindowValidity ) + { + dfXOff = psExtraArg->dfXOff; + dfYOff = psExtraArg->dfYOff; + dfXSize = psExtraArg->dfXSize; + dfYSize = psExtraArg->dfYSize; + CPLAssert(dfXOff - nXOff < 1.0); + CPLAssert(dfYOff - nYOff < 1.0); + CPLAssert(nXSize - dfXSize < 1.0); + CPLAssert(nYSize - dfYSize < 1.0); + } + else + { + dfXOff = nXOff; + dfYOff = nYOff; + dfXSize = nXSize; + dfYSize = nYSize; + } + + double dfXRatioDstToSrc = dfXSize / nBufXSize; + double dfYRatioDstToSrc = dfYSize / nBufYSize; + + /* Determine the coordinates in the "virtual" output raster to see */ + /* if there are not integers, in which case we will use them as a shift */ + /* so that subwindow extracts give the exact same results as entire raster */ + /* scaling */ + double dfDestXOff = dfXOff / dfXRatioDstToSrc; + int bHasXOffVirtual = FALSE; + int nDestXOffVirtual = 0; + if( fabs(dfDestXOff - (int)(dfDestXOff + 0.5)) < 1e-8 ) + { + bHasXOffVirtual = TRUE; + dfXOff = nXOff; + nDestXOffVirtual = (int)(dfDestXOff + 0.5); + } + + double dfDestYOff = dfYOff / dfYRatioDstToSrc; + int bHasYOffVirtual = FALSE; + int nDestYOffVirtual = 0; + if( fabs(dfDestYOff - (int)(dfDestYOff + 0.5)) < 1e-8 ) + { + bHasYOffVirtual = TRUE; + dfYOff = nYOff; + nDestYOffVirtual = (int)(dfDestYOff + 0.5); + } + + /* Create a MEM dataset that wraps the output buffer */ + GDALDataset* poMEMDS = MEMDataset::Create("", nDestXOffVirtual + nBufXSize, + nDestYOffVirtual + nBufYSize, 0, + eBufType, NULL); + char szBuffer[64]; + int nRet; + + nRet = CPLPrintPointer(szBuffer, (GByte*)pData - nPixelSpace * nDestXOffVirtual + - nLineSpace * nDestYOffVirtual, sizeof(szBuffer)); + szBuffer[nRet] = 0; + char** papszOptions = CSLSetNameValue(NULL, "DATAPOINTER", szBuffer); + + papszOptions = CSLSetNameValue(papszOptions, "PIXELOFFSET", + CPLSPrintf(CPL_FRMT_GIB, (GIntBig)nPixelSpace)); + + papszOptions = CSLSetNameValue(papszOptions, "LINEOFFSET", + CPLSPrintf(CPL_FRMT_GIB, (GIntBig)nLineSpace)); + + poMEMDS->AddBand(eBufType, papszOptions); + CSLDestroy(papszOptions); + GDALRasterBandH hMEMBand = (GDALRasterBandH)poMEMDS->GetRasterBand(1); + + /* Do the resampling */ + if( bUseWarp ) + { + VRTDatasetH hVRTDS = NULL; + GDALRasterBandH hVRTBand = NULL; + if( GetDataset() == NULL ) + { + /* Create VRT dataset that wraps the whole dataset */ + hVRTDS = VRTCreate(nRasterXSize, nRasterYSize); + VRTAddBand( hVRTDS, eDataType, NULL ); + hVRTBand = GDALGetRasterBand(hVRTDS, 1); + VRTAddSimpleSource( (VRTSourcedRasterBandH)hVRTBand, + (GDALRasterBandH)this, + 0, 0, + nRasterXSize, nRasterYSize, + 0, 0, + nRasterXSize, nRasterYSize, + NULL, VRT_NODATA_UNSET ); + + /* Add a mask band if needed */ + if( GetMaskFlags() != GMF_ALL_VALID ) + { + ((GDALDataset*)hVRTDS)->CreateMaskBand(0); + VRTSourcedRasterBand* poVRTMaskBand = + (VRTSourcedRasterBand*)(((GDALRasterBand*)hVRTBand)->GetMaskBand()); + poVRTMaskBand-> + AddMaskBandSource( this, + 0, 0, + nRasterXSize, nRasterYSize, + 0, 0, + nRasterXSize, nRasterYSize); + } + } + + GDALWarpOptions* psWarpOptions = GDALCreateWarpOptions(); + psWarpOptions->eResampleAlg = (GDALResampleAlg)psExtraArg->eResampleAlg; + psWarpOptions->hSrcDS = (GDALDatasetH) (hVRTDS ? hVRTDS : GetDataset()); + psWarpOptions->hDstDS = (GDALDatasetH) poMEMDS; + psWarpOptions->nBandCount = 1; + int nSrcBandNumber = (hVRTDS ? 1 : nBand); + int nDstBandNumber = 1; + psWarpOptions->panSrcBands = &nSrcBandNumber; + psWarpOptions->panDstBands = &nDstBandNumber; + psWarpOptions->pfnProgress = psExtraArg->pfnProgress ? + psExtraArg->pfnProgress : GDALDummyProgress; + psWarpOptions->pProgressArg = psExtraArg->pProgressData; + psWarpOptions->pfnTransformer = GDALRasterIOTransformer; + GDALRasterIOTransformerStruct sTransformer; + sTransformer.dfXOff = (bHasXOffVirtual) ? 0 : dfXOff; + sTransformer.dfYOff = (bHasYOffVirtual) ? 0 : dfYOff; + sTransformer.dfXRatioDstToSrc = dfXRatioDstToSrc; + sTransformer.dfYRatioDstToSrc = dfYRatioDstToSrc; + psWarpOptions->pTransformerArg = &sTransformer; + + GDALWarpOperationH hWarpOperation = GDALCreateWarpOperation(psWarpOptions); + eErr = GDALChunkAndWarpImage( hWarpOperation, + nDestXOffVirtual, nDestYOffVirtual, + nBufXSize, nBufYSize ); + GDALDestroyWarpOperation( hWarpOperation ); + + psWarpOptions->panSrcBands = NULL; + psWarpOptions->panDstBands = NULL; + GDALDestroyWarpOptions( psWarpOptions ); + + if( hVRTDS ) + GDALClose(hVRTDS); + } + else + { + const char* pszResampling = + (psExtraArg->eResampleAlg == GRIORA_Bilinear) ? "BILINEAR" : + (psExtraArg->eResampleAlg == GRIORA_Cubic) ? "CUBIC" : + (psExtraArg->eResampleAlg == GRIORA_CubicSpline) ? "CUBICSPLINE" : + (psExtraArg->eResampleAlg == GRIORA_Lanczos) ? "LANCZOS" : + (psExtraArg->eResampleAlg == GRIORA_Average) ? "AVERAGE" : + (psExtraArg->eResampleAlg == GRIORA_Mode) ? "MODE" : + (psExtraArg->eResampleAlg == GRIORA_Gauss) ? "GAUSS" : "UNKNOWN"; + + int nKernelRadius; + GDALResampleFunction pfnResampleFunc = + GDALGetResampleFunction(pszResampling, &nKernelRadius); + CPLAssert(pfnResampleFunc); + GDALDataType eWrkDataType = + GDALGetOvrWorkDataType(pszResampling, eDataType); + int bHasNoData = FALSE; + float fNoDataValue = (float) GetNoDataValue(&bHasNoData); + if( !bHasNoData ) + fNoDataValue = 0.0f; + + int nDstBlockXSize = nBufXSize; + int nDstBlockYSize = nBufYSize; + int nFullResXChunk, nFullResYChunk; + while(TRUE) + { + nFullResXChunk = 3 + (int)(nDstBlockXSize * dfXRatioDstToSrc); + nFullResYChunk = 3 + (int)(nDstBlockYSize * dfYRatioDstToSrc); + if( (nDstBlockXSize == 1 && nDstBlockYSize == 1) || + ((GIntBig)nFullResXChunk * nFullResYChunk <= 1024 * 1024) ) + break; + /* When operating on the full width of a raster whose block width is */ + /* the raster width, prefer doing chunks in height */ + if( nFullResXChunk >= nXSize && nXSize == nBlockXSize && nDstBlockYSize > 1 ) + nDstBlockYSize /= 2; + /* Otherwise cut the maximal dimension */ + else if( nDstBlockXSize > 1 && nFullResXChunk > nFullResYChunk ) + nDstBlockXSize /= 2; + else + nDstBlockYSize /= 2; + } + + int nOvrFactor = MAX( (int)(0.5 + dfXRatioDstToSrc), + (int)(0.5 + dfYRatioDstToSrc) ); + if( nOvrFactor == 0 ) nOvrFactor = 1; + int nFullResXSizeQueried = nFullResXChunk + 2 * nKernelRadius * nOvrFactor; + int nFullResYSizeQueried = nFullResYChunk + 2 * nKernelRadius * nOvrFactor; + + void * pChunk = + VSIMalloc3((GDALGetDataTypeSize(eWrkDataType)/8), + nFullResXSizeQueried, nFullResYSizeQueried ); + GByte * pabyChunkNoDataMask = NULL; + + GDALRasterBand* poMaskBand = NULL; + int nMaskFlags = 0; + int bUseNoDataMask = FALSE; + + poMaskBand = GetMaskBand(); + nMaskFlags = GetMaskFlags(); + + bUseNoDataMask = ((nMaskFlags & GMF_ALL_VALID) == 0); + if (bUseNoDataMask) + { + pabyChunkNoDataMask = (GByte *) + (GByte*) VSIMalloc2( nFullResXSizeQueried, nFullResYSizeQueried ); + } + if( pChunk == NULL || (bUseNoDataMask && pabyChunkNoDataMask == NULL) ) + { + GDALClose(poMEMDS); + CPLFree(pChunk); + CPLFree(pabyChunkNoDataMask); + CPLError( CE_Failure, CPLE_OutOfMemory, + "Out of memory in RasterIO()." ); + return CE_Failure; + } + + int nTotalBlocks = ((nBufXSize + nDstBlockXSize - 1) / nDstBlockXSize) * + ((nBufYSize + nDstBlockYSize - 1) / nDstBlockYSize); + int nBlocksDone = 0; + + int nDstYOff; + for( nDstYOff = 0; nDstYOff < nBufYSize && eErr == CE_None; + nDstYOff += nDstBlockYSize ) + { + int nDstYCount; + if (nDstYOff + nDstBlockYSize <= nBufYSize) + nDstYCount = nDstBlockYSize; + else + nDstYCount = nBufYSize - nDstYOff; + + int nChunkYOff = nYOff + (int) (nDstYOff * dfYRatioDstToSrc); + int nChunkYOff2 = nYOff + 1 + (int) ceil((nDstYOff + nDstYCount) * dfYRatioDstToSrc); + if( nChunkYOff2 > nRasterYSize ) + nChunkYOff2 = nRasterYSize; + int nYCount = nChunkYOff2 - nChunkYOff; + CPLAssert(nYCount <= nFullResYChunk); + + int nChunkYOffQueried = nChunkYOff - nKernelRadius * nOvrFactor; + int nChunkYSizeQueried = nYCount + 2 * nKernelRadius * nOvrFactor; + if( nChunkYOffQueried < 0 ) + { + nChunkYSizeQueried += nChunkYOffQueried; + nChunkYOffQueried = 0; + } + if( nChunkYSizeQueried + nChunkYOffQueried > nRasterYSize ) + nChunkYSizeQueried = nRasterYSize - nChunkYOffQueried; + CPLAssert(nChunkYSizeQueried <= nFullResYSizeQueried); + + int nDstXOff; + for( nDstXOff = 0; nDstXOff < nBufXSize && eErr == CE_None; + nDstXOff += nDstBlockXSize ) + { + int nDstXCount; + if (nDstXOff + nDstBlockXSize <= nBufXSize) + nDstXCount = nDstBlockXSize; + else + nDstXCount = nBufXSize - nDstXOff; + + int nChunkXOff = nXOff + (int) (nDstXOff * dfXRatioDstToSrc); + int nChunkXOff2 = nXOff + 1 + (int) ceil((nDstXOff + nDstXCount) * dfXRatioDstToSrc); + if( nChunkXOff2 > nRasterXSize ) + nChunkXOff2 = nRasterXSize; + int nXCount = nChunkXOff2 - nChunkXOff; + CPLAssert(nXCount <= nFullResXChunk); + + int nChunkXOffQueried = nChunkXOff - nKernelRadius * nOvrFactor; + int nChunkXSizeQueried = nXCount + 2 * nKernelRadius * nOvrFactor; + if( nChunkXOffQueried < 0 ) + { + nChunkXSizeQueried += nChunkXOffQueried; + nChunkXOffQueried = 0; + } + if( nChunkXSizeQueried + nChunkXOffQueried > nRasterXSize ) + nChunkXSizeQueried = nRasterXSize - nChunkXOffQueried; + CPLAssert(nChunkXSizeQueried <= nFullResXSizeQueried); + + /* Read the source buffers */ + eErr = RasterIO( GF_Read, + nChunkXOffQueried, nChunkYOffQueried, + nChunkXSizeQueried, nChunkYSizeQueried, + pChunk, + nChunkXSizeQueried, nChunkYSizeQueried, + eWrkDataType, 0, 0, NULL ); + + int bSkipResample = FALSE; + int bNoDataMaskFullyOpaque = FALSE; + if (eErr == CE_None && bUseNoDataMask) + { + eErr = poMaskBand->RasterIO( GF_Read, + nChunkXOffQueried, + nChunkYOffQueried, + nChunkXSizeQueried, + nChunkYSizeQueried, + pabyChunkNoDataMask, + nChunkXSizeQueried, + nChunkYSizeQueried, + GDT_Byte, 0, 0, NULL ); + + /* Optimizations if mask if fully opaque or transparent */ + int nPixels = nChunkXSizeQueried * nChunkYSizeQueried; + GByte bVal = pabyChunkNoDataMask[0]; + int i = 1; + for( ; i < nPixels; i++ ) + { + if( pabyChunkNoDataMask[i] != bVal ) + break; + } + if( i == nPixels ) + { + if( bVal == 0 ) + { + for(int j=0;jpfnProgress != NULL && + !psExtraArg->pfnProgress(1.0 * nBlocksDone / nTotalBlocks, "", + psExtraArg->pProgressData) ) + { + eErr = CE_Failure; + } + } + } + + CPLFree(pChunk); + CPLFree(pabyChunkNoDataMask); + } + + GDALClose(poMEMDS); + + return eErr; +} + +/************************************************************************/ +/* GDALSwapWords() */ +/************************************************************************/ + +/** + * Byte swap words in-place. + * + * This function will byte swap a set of 2, 4 or 8 byte words "in place" in + * a memory array. No assumption is made that the words being swapped are + * word aligned in memory. Use the CPL_LSB and CPL_MSB macros from cpl_port.h + * to determine if the current platform is big endian or little endian. Use + * The macros like CPL_SWAP32() to byte swap single values without the overhead + * of a function call. + * + * @param pData pointer to start of data buffer. + * @param nWordSize size of words being swapped in bytes. Normally 2, 4 or 8. + * @param nWordCount the number of words to be swapped in this call. + * @param nWordSkip the byte offset from the start of one word to the start of + * the next. For packed buffers this is the same as nWordSize. + */ + +void CPL_STDCALL GDALSwapWords( void *pData, int nWordSize, int nWordCount, + int nWordSkip ) + +{ + if (nWordCount > 0) + VALIDATE_POINTER0( pData , "GDALSwapWords" ); + + int i; + GByte *pabyData = (GByte *) pData; + + switch( nWordSize ) + { + case 1: + break; + + case 2: + CPLAssert( nWordSkip >= 2 || nWordCount == 1 ); + for( i = 0; i < nWordCount; i++ ) + { + GByte byTemp; + + byTemp = pabyData[0]; + pabyData[0] = pabyData[1]; + pabyData[1] = byTemp; + + pabyData += nWordSkip; + } + break; + + case 4: + CPLAssert( nWordSkip >= 4 || nWordCount == 1 ); + for( i = 0; i < nWordCount; i++ ) + { + GByte byTemp; + + byTemp = pabyData[0]; + pabyData[0] = pabyData[3]; + pabyData[3] = byTemp; + + byTemp = pabyData[1]; + pabyData[1] = pabyData[2]; + pabyData[2] = byTemp; + + pabyData += nWordSkip; + } + break; + + case 8: + CPLAssert( nWordSkip >= 8 || nWordCount == 1 ); + for( i = 0; i < nWordCount; i++ ) + { + GByte byTemp; + + byTemp = pabyData[0]; + pabyData[0] = pabyData[7]; + pabyData[7] = byTemp; + + byTemp = pabyData[1]; + pabyData[1] = pabyData[6]; + pabyData[6] = byTemp; + + byTemp = pabyData[2]; + pabyData[2] = pabyData[5]; + pabyData[5] = byTemp; + + byTemp = pabyData[3]; + pabyData[3] = pabyData[4]; + pabyData[4] = byTemp; + + pabyData += nWordSkip; + } + break; + + default: + CPLAssert( FALSE ); + } +} + +#ifdef USE_NEW_COPYWORDS +// Place the new GDALCopyWords helpers in an anonymous namespace +namespace { +/************************************************************************/ +/* GetDataLimits() */ +/************************************************************************/ +/** + * Compute the limits of values that can be placed in Tout in terms of + * Tin. Usually used for output clamping, when the output data type's + * limits are stable relative to the input type (i.e. no roundoff error). + * + * @param tMaxValue the returned maximum value + * @param tMinValue the returned minimum value + */ + +template +inline void GetDataLimits(Tin &tMaxValue, Tin &tMinValue) +{ + tMaxValue = std::numeric_limits::max(); + tMinValue = std::numeric_limits::min(); + + // Compute the actual minimum value of Tout in terms of Tin. + if (std::numeric_limits::is_signed && std::numeric_limits::is_integer) + { + // the minimum value is less than zero + if (std::numeric_limits::digits < std::numeric_limits::digits || + !std::numeric_limits::is_integer) + { + // Tout is smaller than Tin, so we need to clamp values in input + // to the range of Tout's min/max values + if (std::numeric_limits::is_signed) + { + tMinValue = static_cast(std::numeric_limits::min()); + } + tMaxValue = static_cast(std::numeric_limits::max()); + } + } + else if (std::numeric_limits::is_integer) + { + // the output is unsigned, so we just need to determine the max + if (std::numeric_limits::digits <= std::numeric_limits::digits) + { + // Tout is smaller than Tin, so we need to clamp the input values + // to the range of Tout's max + tMaxValue = static_cast(std::numeric_limits::max()); + } + tMinValue = 0; + } + +} + +/************************************************************************/ +/* ClampValue() */ +/************************************************************************/ +/** + * Clamp values of type T to a specified range + * + * @param tValue the value + * @param tMax the max value + * @param tMin the min value + */ +template +inline T ClampValue(const T tValue, const T tMax, const T tMin) +{ + return tValue > tMax ? tMax : + tValue < tMin ? tMin : tValue; +} + +/************************************************************************/ +/* CopyWord() */ +/************************************************************************/ +/** + * Copy a single word, optionally rounding if appropriate (i.e. going + * from the float to the integer case). Note that this is the function + * you should specialize if you're adding a new data type. + * + * @param tValueIn value of type Tin; the input value to be converted + * @param tValueOut value of type Tout; the output value + */ + +template +inline void CopyWord(const Tin tValueIn, Tout &tValueOut) +{ + Tin tMaxVal, tMinVal; + GetDataLimits(tMaxVal, tMinVal); + tValueOut = static_cast(ClampValue(tValueIn, tMaxVal, tMinVal)); +} + +template +inline void CopyWord(const Tin tValueIn, float &fValueOut) +{ + fValueOut = (float) tValueIn; +} + +template +inline void CopyWord(const Tin tValueIn, double &dfValueOut) +{ + dfValueOut = tValueIn; +} + +inline void CopyWord(const double dfValueIn, double &dfValueOut) +{ + dfValueOut = dfValueIn; +} + +inline void CopyWord(const float fValueIn, float &fValueOut) +{ + fValueOut = fValueIn; +} + +inline void CopyWord(const float fValueIn, double &dfValueOut) +{ + dfValueOut = fValueIn; +} + +inline void CopyWord(const double dfValueIn, float &fValueOut) +{ + fValueOut = static_cast(dfValueIn); +} + +template +inline void CopyWord(const float fValueIn, Tout &tValueOut) +{ + float fMaxVal, fMinVal; + GetDataLimits(fMaxVal, fMinVal); + tValueOut = static_cast( + ClampValue(fValueIn + 0.5f, fMaxVal, fMinVal)); +} + +template +inline void CopyWord(const double dfValueIn, Tout &tValueOut) +{ + double dfMaxVal, dfMinVal; + GetDataLimits(dfMaxVal, dfMinVal); + tValueOut = static_cast( + ClampValue(dfValueIn + 0.5, dfMaxVal, dfMinVal)); +} + +inline void CopyWord(const double dfValueIn, int &nValueOut) +{ + double dfMaxVal, dfMinVal; + GetDataLimits(dfMaxVal, dfMinVal); + double dfValue = dfValueIn >= 0.0 ? dfValueIn + 0.5 : + dfValueIn - 0.5; + nValueOut = static_cast( + ClampValue(dfValue, dfMaxVal, dfMinVal)); +} + +inline void CopyWord(const float fValueIn, short &nValueOut) +{ + float fMaxVal, fMinVal; + GetDataLimits(fMaxVal, fMinVal); + float fValue = fValueIn >= 0.0f ? fValueIn + 0.5f : + fValueIn - 0.5f; + nValueOut = static_cast( + ClampValue(fValue, fMaxVal, fMinVal)); +} + +inline void CopyWord(const double dfValueIn, short &nValueOut) +{ + double dfMaxVal, dfMinVal; + GetDataLimits(dfMaxVal, dfMinVal); + double dfValue = dfValueIn > 0.0 ? dfValueIn + 0.5 : + dfValueIn - 0.5; + nValueOut = static_cast( + ClampValue(dfValue, dfMaxVal, dfMinVal)); +} + +// Roundoff occurs for Float32 -> int32 for max/min. Overload CopyWord +// specifically for this case. +inline void CopyWord(const float fValueIn, int &nValueOut) +{ + if (fValueIn >= static_cast(std::numeric_limits::max())) + { + nValueOut = std::numeric_limits::max(); + } + else if (fValueIn <= static_cast(std::numeric_limits::min())) + { + nValueOut = std::numeric_limits::min(); + } + else + { + nValueOut = static_cast(fValueIn > 0.0f ? + fValueIn + 0.5f : fValueIn - 0.5f); + } +} + +// Roundoff occurs for Float32 -> uint32 for max. Overload CopyWord +// specifically for this case. +inline void CopyWord(const float fValueIn, unsigned int &nValueOut) +{ + if (fValueIn >= static_cast(std::numeric_limits::max())) + { + nValueOut = std::numeric_limits::max(); + } + else if (fValueIn <= static_cast(std::numeric_limits::min())) + { + nValueOut = std::numeric_limits::min(); + } + else + { + nValueOut = static_cast(fValueIn + 0.5f); + } +} + +/************************************************************************/ +/* GDALCopyWordsT() */ +/************************************************************************/ +/** + * Template function, used to copy data from pSrcData into buffer + * pDstData, with stride nSrcPixelStride in the source data and + * stride nDstPixelStride in the destination data. This template can + * deal with the case where the input data type is real or complex and + * the output is real. + * + * @param pSrcData the source data buffer + * @param nSrcPixelStride the stride, in the buffer pSrcData for pixels + * of interest. + * @param pDstData the destination buffer. + * @param nDstPixelStride the stride in the buffer pDstData for pixels of + * interest. + * @param nWordCount the total number of pixel words to copy + * + * @code + * // Assume an input buffer of type GUInt16 named pBufferIn + * GByte *pBufferOut = new GByte[numBytesOut]; + * GDALCopyWordsT(pSrcData, 2, pDstData, 1, numBytesOut); + * @code + * @note + * This is a private function, and should not be exposed outside of rasterio.cpp. + * External users should call the GDALCopyWords driver function. + * @note + */ + +template +static void GDALCopyWordsT(const Tin* const pSrcData, int nSrcPixelStride, + Tout* const pDstData, int nDstPixelStride, + int nWordCount) +{ + std::ptrdiff_t nDstOffset = 0; + + const char* const pSrcDataPtr = reinterpret_cast(pSrcData); + char* const pDstDataPtr = reinterpret_cast(pDstData); + for (std::ptrdiff_t n = 0; n < nWordCount; n++) + { + const Tin tValue = *reinterpret_cast(pSrcDataPtr + (n * nSrcPixelStride)); + Tout* const pOutPixel = reinterpret_cast(pDstDataPtr + nDstOffset); + + CopyWord(tValue, *pOutPixel); + + nDstOffset += nDstPixelStride; + } +} + +/************************************************************************/ +/* GDALCopyWordsComplexT() */ +/************************************************************************/ +/** + * Template function, used to copy data from pSrcData into buffer + * pDstData, with stride nSrcPixelStride in the source data and + * stride nDstPixelStride in the destination data. Deals with the + * complex case, where input is complex and output is complex. + * + * @param pSrcData the source data buffer + * @param nSrcPixelStride the stride, in the buffer pSrcData for pixels + * of interest. + * @param pDstData the destination buffer. + * @param nDstPixelStride the stride in the buffer pDstData for pixels of + * interest. + * @param nWordCount the total number of pixel words to copy + * + */ +template +inline void GDALCopyWordsComplexT(const Tin* const pSrcData, int nSrcPixelStride, + Tout* const pDstData, int nDstPixelStride, + int nWordCount) +{ + std::ptrdiff_t nDstOffset = 0; + const char* const pSrcDataPtr = reinterpret_cast(pSrcData); + char* const pDstDataPtr = reinterpret_cast(pDstData); + + // Determine the minimum and maximum value we can have based + // on the constraints of Tin and Tout. + Tin tMaxValue, tMinValue; + GetDataLimits(tMaxValue, tMinValue); + + for (std::ptrdiff_t n = 0; n < nWordCount; n++) + { + const Tin* const pPixelIn = reinterpret_cast(pSrcDataPtr + n * nSrcPixelStride); + Tout* const pPixelOut = reinterpret_cast(pDstDataPtr + nDstOffset); + + CopyWord(pPixelIn[0], pPixelOut[0]); + CopyWord(pPixelIn[1], pPixelOut[1]); + + nDstOffset += nDstPixelStride; + } +} + +/************************************************************************/ +/* GDALCopyWordsComplexOutT() */ +/************************************************************************/ +/** + * Template function, used to copy data from pSrcData into buffer + * pDstData, with stride nSrcPixelStride in the source data and + * stride nDstPixelStride in the destination data. Deals with the + * case where the value is real coming in, but complex going out. + * + * @param pSrcData the source data buffer + * @param nSrcPixelStride the stride, in the buffer pSrcData for pixels + * of interest, in bytes. + * @param pDstData the destination buffer. + * @param nDstPixelStride the stride in the buffer pDstData for pixels of + * interest, in bytes. + * @param nWordCount the total number of pixel words to copy + * + */ +template +inline void GDALCopyWordsComplexOutT(const Tin* const pSrcData, int nSrcPixelStride, + Tout* const pDstData, int nDstPixelStride, + int nWordCount) +{ + std::ptrdiff_t nDstOffset = 0; + + const Tout tOutZero = static_cast(0); + + const char* const pSrcDataPtr = reinterpret_cast(pSrcData); + char* const pDstDataPtr = reinterpret_cast(pDstData); + + for (std::ptrdiff_t n = 0; n < nWordCount; n++) + { + const Tin tValue = *reinterpret_cast(pSrcDataPtr + n * nSrcPixelStride); + Tout* const pPixelOut = reinterpret_cast(pDstDataPtr + nDstOffset); + CopyWord(tValue, *pPixelOut); + + pPixelOut[1] = tOutZero; + + nDstOffset += nDstPixelStride; + } +} + +/************************************************************************/ +/* GDALCopyWordsFromT() */ +/************************************************************************/ +/** + * Template driver function. Given the input type T, call the appropriate + * GDALCopyWordsT function template for the desired output type. You should + * never call this function directly (call GDALCopyWords instead). + * + * @param pSrcData source data buffer + * @param nSrcPixelStride pixel stride in input buffer, in pixel words + * @param bInComplex input is complex + * @param pDstData destination data buffer + * @param eDstType destination data type + * @param nDstPixelStride pixel stride in output buffer, in pixel words + * @param nWordCount number of pixel words to be copied + */ +template +inline void GDALCopyWordsFromT(const T* const pSrcData, int nSrcPixelStride, bool bInComplex, + void *pDstData, GDALDataType eDstType, int nDstPixelStride, + int nWordCount) +{ + switch (eDstType) + { + case GDT_Byte: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_UInt16: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_Int16: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_UInt32: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_Int32: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_Float32: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_Float64: + GDALCopyWordsT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + break; + case GDT_CInt16: + if (bInComplex) + { + GDALCopyWordsComplexT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + else // input is not complex, so we need to promote to a complex buffer + { + GDALCopyWordsComplexOutT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + break; + case GDT_CInt32: + if (bInComplex) + { + GDALCopyWordsComplexT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + else // input is not complex, so we need to promote to a complex buffer + { + GDALCopyWordsComplexOutT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + break; + case GDT_CFloat32: + if (bInComplex) + { + GDALCopyWordsComplexT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + else // input is not complex, so we need to promote to a complex buffer + { + GDALCopyWordsComplexOutT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + break; + case GDT_CFloat64: + if (bInComplex) + { + GDALCopyWordsComplexT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + else // input is not complex, so we need to promote to a complex buffer + { + GDALCopyWordsComplexOutT(pSrcData, nSrcPixelStride, + static_cast(pDstData), nDstPixelStride, + nWordCount); + } + break; + case GDT_Unknown: + default: + CPLAssert(FALSE); + } +} + +} // end anonymous namespace +#endif + +/************************************************************************/ +/* GDALReplicateWord() */ +/************************************************************************/ + +void GDALReplicateWord(void *pSrcData, GDALDataType eSrcType, + void *pDstData, GDALDataType eDstType, int nDstPixelStride, + int nWordCount) +{ +/* ----------------------------------------------------------------------- */ +/* Special case when the source data is always the same value */ +/* (for VRTSourcedRasterBand::IRasterIO and VRTDerivedRasterBand::IRasterIO*/ +/* for example) */ +/* ----------------------------------------------------------------------- */ + /* Let the general translation case do the necessary conversions */ + /* on the first destination element */ + GDALCopyWords(pSrcData, eSrcType, 0, + pDstData, eDstType, nDstPixelStride, + 1 ); + + /* Now copy the first element to the nWordCount - 1 following destination */ + /* elements */ + nWordCount--; + GByte *pabyDstWord = ((GByte *)pDstData) + nDstPixelStride; + + switch (eDstType) + { + case GDT_Byte: + { + if (nDstPixelStride == 1) + { + if (nWordCount > 0) + memset(pabyDstWord, *(GByte*)pDstData, nWordCount); + } + else + { + GByte valSet = *(GByte*)pDstData; + while(nWordCount--) + { + *pabyDstWord = valSet; + pabyDstWord += nDstPixelStride; + } + } + break; + } + +#define CASE_DUPLICATE_SIMPLE(enum_type, c_type) \ + case enum_type:\ + { \ + c_type valSet = *(c_type*)pDstData; \ + while(nWordCount--) \ + { \ + *(c_type*)pabyDstWord = valSet; \ + pabyDstWord += nDstPixelStride; \ + } \ + break; \ + } + + CASE_DUPLICATE_SIMPLE(GDT_UInt16, GUInt16) + CASE_DUPLICATE_SIMPLE(GDT_Int16, GInt16) + CASE_DUPLICATE_SIMPLE(GDT_UInt32, GUInt32) + CASE_DUPLICATE_SIMPLE(GDT_Int32, GInt32) + CASE_DUPLICATE_SIMPLE(GDT_Float32,float) + CASE_DUPLICATE_SIMPLE(GDT_Float64,double) + +#define CASE_DUPLICATE_COMPLEX(enum_type, c_type) \ + case enum_type:\ + { \ + c_type valSet1 = ((c_type*)pDstData)[0]; \ + c_type valSet2 = ((c_type*)pDstData)[1]; \ + while(nWordCount--) \ + { \ + ((c_type*)pabyDstWord)[0] = valSet1; \ + ((c_type*)pabyDstWord)[1] = valSet2; \ + pabyDstWord += nDstPixelStride; \ + } \ + break; \ + } + + CASE_DUPLICATE_COMPLEX(GDT_CInt16, GInt16) + CASE_DUPLICATE_COMPLEX(GDT_CInt32, GInt32) + CASE_DUPLICATE_COMPLEX(GDT_CFloat32, float) + CASE_DUPLICATE_COMPLEX(GDT_CFloat64, double) + + default: + CPLAssert( FALSE ); + } +} + +/************************************************************************/ +/* GDALCopyWords() */ +/************************************************************************/ + +/** + * Copy pixel words from buffer to buffer. + * + * This function is used to copy pixel word values from one memory buffer + * to another, with support for conversion between data types, and differing + * step factors. The data type conversion is done using the normal GDAL + * rules. Values assigned to a lower range integer type are clipped. For + * instance assigning GDT_Int16 values to a GDT_Byte buffer will cause values + * less the 0 to be set to 0, and values larger than 255 to be set to 255. + * Assignment from floating point to integer uses default C type casting + * semantics. Assignment from non-complex to complex will result in the + * imaginary part being set to zero on output. Assigment from complex to + * non-complex will result in the complex portion being lost and the real + * component being preserved (not magnitidue!). + * + * No assumptions are made about the source or destination words occuring + * on word boundaries. It is assumed that all values are in native machine + * byte order. + * + * @param pSrcData Pointer to source data to be converted. + * @param eSrcType the source data type (see GDALDataType enum) + * @param nSrcPixelStride Source pixel stride (i.e. distance between 2 words), in bytes + * @param pDstData Pointer to buffer where destination data should go + * @param eDstType the destination data type (see GDALDataType enum) + * @param nDstPixelStride Destination pixel stride (i.e. distance between 2 words), in bytes + * @param nWordCount number of words to be copied + * + * + * @note + * When adding a new data type to GDAL, you must do the following to + * support it properly within the GDALCopyWords function: + * 1. Add the data type to the switch on eSrcType in GDALCopyWords. + * This should invoke the appropriate GDALCopyWordsFromT wrapper. + * 2. Add the data type to the switch on eDstType in GDALCopyWordsFromT. + * This should call the appropriate GDALCopyWordsT template. + * 3. If appropriate, overload the appropriate CopyWord template in the + * above namespace. This will ensure that any conversion issues are + * handled (cases like the float -> int32 case, where the min/max) + * values are subject to roundoff error. + */ + +void CPL_STDCALL +GDALCopyWords( void * pSrcData, GDALDataType eSrcType, int nSrcPixelStride, + void * pDstData, GDALDataType eDstType, int nDstPixelStride, + int nWordCount ) + +{ + // Deal with the case where we're replicating a single word into the + // provided buffer + if (nSrcPixelStride == 0 && nWordCount > 1) + { + GDALReplicateWord(pSrcData, eSrcType, pDstData, eDstType, nDstPixelStride, nWordCount); + return; + } + +#ifdef USE_NEW_COPYWORDS + + int nSrcDataTypeSize = GDALGetDataTypeSize(eSrcType) / 8; + // Let memcpy() handle the case where we're copying a packed buffer + // of pixels. + if (eSrcType == eDstType && nSrcPixelStride == nDstPixelStride && + nSrcPixelStride == nSrcDataTypeSize) + { + memcpy(pDstData, pSrcData, nWordCount * nSrcDataTypeSize); + return; + } + + // Handle the more general case -- deals with conversion of data types + // directly. + switch (eSrcType) + { + case GDT_Byte: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_UInt16: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_Int16: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_UInt32: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_Int32: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_Float32: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_Float64: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, false, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_CInt16: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, true, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_CInt32: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, true, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_CFloat32: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, true, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_CFloat64: + GDALCopyWordsFromT(static_cast(pSrcData), nSrcPixelStride, true, + pDstData, eDstType, nDstPixelStride, + nWordCount); + break; + case GDT_Unknown: + default: + CPLAssert(FALSE); + } + +#else // undefined USE_NEW_COPYWORDS +/* -------------------------------------------------------------------- */ +/* Special case when no data type translation is required. */ +/* -------------------------------------------------------------------- */ + if( eSrcType == eDstType ) + { + int nWordSize = GDALGetDataTypeSize(eSrcType)/8; + int i; + + // contiguous blocks. + if( nWordSize == nSrcPixelStride && nWordSize == nDstPixelStride ) + { + memcpy( pDstData, pSrcData, nSrcPixelStride * nWordCount ); + return; + } + + GByte *pabySrc = (GByte *) pSrcData; + GByte *pabyDst = (GByte *) pDstData; + + // Moving single bytes. + if( nWordSize == 1 ) + { + if (nWordCount > 100) + { +/* ==================================================================== */ +/* Optimization for high number of words to transfer and some */ +/* typical source and destination pixel spacing : we unroll the */ +/* loop. */ +/* ==================================================================== */ +#define ASSIGN(_nSrcPixelStride, _nDstPixelStride, _k) \ + pabyDst[_nDstPixelStride * _k] = pabySrc[_nSrcPixelStride * _k] +#define ASSIGN_LOOP(_nSrcPixelStride, _nDstPixelStride) \ + for( i = nWordCount / 16 ; i != 0; i-- ) \ + { \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 0); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 1); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 2); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 3); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 4); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 5); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 6); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 7); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 8); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 9); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 10); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 11); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 12); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 13); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 14); \ + ASSIGN(_nSrcPixelStride, _nDstPixelStride, 15); \ + pabyDst += _nDstPixelStride * 16; \ + pabySrc += _nSrcPixelStride * 16; \ + } \ + nWordCount = nWordCount % 16; + + if (nSrcPixelStride == 3 && nDstPixelStride == 1) + { + ASSIGN_LOOP(3, 1) + } + else if (nSrcPixelStride == 1 && nDstPixelStride == 3) + { + ASSIGN_LOOP(1, 3) + } + else if (nSrcPixelStride == 4 && nDstPixelStride == 1) + { + ASSIGN_LOOP(4, 1) + } + else if (nSrcPixelStride == 1 && nDstPixelStride == 4) + { + ASSIGN_LOOP(1, 4) + } + else if (nSrcPixelStride == 3 && nDstPixelStride == 4) + { + ASSIGN_LOOP(3, 4) + } + else if (nSrcPixelStride == 4 && nDstPixelStride == 3) + { + ASSIGN_LOOP(4, 3) + } + } + + for( i = nWordCount; i != 0; i-- ) + { + *pabyDst = *pabySrc; + pabyDst += nDstPixelStride; + pabySrc += nSrcPixelStride; + } + } + else if (nWordSize == 2) + { + for( i = nWordCount; i != 0; i-- ) + { + *(short*)pabyDst = *(short*)pabySrc; + pabyDst += nDstPixelStride; + pabySrc += nSrcPixelStride; + } + } + else if (nWordSize == 4) + { + for( i = nWordCount; i != 0; i-- ) + { + *(int*)pabyDst = *(int*)pabySrc; + pabyDst += nDstPixelStride; + pabySrc += nSrcPixelStride; + } + } + else if (nWordSize == 8) + { + for( i = nWordCount; i != 0; i-- ) + { + ((int*)pabyDst)[0] = ((int*)pabySrc)[0]; + ((int*)pabyDst)[1] = ((int*)pabySrc)[1]; + pabyDst += nDstPixelStride; + pabySrc += nSrcPixelStride; + } + } + else if (nWordSize == 16) + { + for( i = nWordCount; i != 0; i-- ) + { + ((int*)pabyDst)[0] = ((int*)pabySrc)[0]; + ((int*)pabyDst)[1] = ((int*)pabySrc)[1]; + ((int*)pabyDst)[2] = ((int*)pabySrc)[2]; + ((int*)pabyDst)[3] = ((int*)pabySrc)[3]; + pabyDst += nDstPixelStride; + pabySrc += nSrcPixelStride; + } + } + else + { + CPLAssert(FALSE); + } + + return; + } + +/* ==================================================================== */ +/* General translation case */ +/* ==================================================================== */ + for( int iWord = 0; iWord < nWordCount; iWord++ ) + { + void *pSrcWord, *pDstWord; + double dfPixelValue=0.0, dfPixelValueI=0.0; + + pSrcWord = static_cast(pSrcData) + iWord * nSrcPixelStride; + pDstWord = static_cast(pDstData) + iWord * nDstPixelStride; + +/* -------------------------------------------------------------------- */ +/* Fetch source value based on data type. */ +/* -------------------------------------------------------------------- */ + switch( eSrcType ) + { + case GDT_Byte: + { + GByte byVal = *static_cast(pSrcWord); + switch( eDstType ) + { + case GDT_UInt16: + *static_cast(pDstWord) = byVal; + continue; + case GDT_Int16: + *static_cast(pDstWord) = byVal; + continue; + case GDT_UInt32: + *static_cast(pDstWord) = byVal; + continue; + case GDT_Int32: + *static_cast(pDstWord) = byVal; + continue; + case GDT_CInt16: + { + GInt16 *panDstWord = static_cast(pDstWord); + panDstWord[0] = byVal; + panDstWord[1] = 0; + continue; + } + case GDT_CInt32: + { + GInt32 *panDstWord = static_cast(pDstWord); + panDstWord[0] = byVal; + panDstWord[1] = 0; + continue; + } + default: + break; + } + dfPixelValue = byVal; + } + break; + + case GDT_UInt16: + { + GUInt16 nVal = *static_cast(pSrcWord); + switch( eDstType ) + { + case GDT_Byte: + { + GByte byVal; + if( nVal > 255 ) + byVal = 255; + else + byVal = static_cast(nVal); + *static_cast(pDstWord) = byVal; + continue; + } + case GDT_Int16: + if( nVal > 32767 ) + nVal = 32767; + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt32: + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int32: + *static_cast(pDstWord) = nVal; + continue; + case GDT_CInt16: + { + GInt16 *panDstWord = static_cast(pDstWord); + if( nVal > 32767 ) + nVal = 32767; + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + case GDT_CInt32: + { + GInt32 *panDstWord = static_cast(pDstWord); + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + default: + break; + } + dfPixelValue = nVal; + } + break; + + case GDT_Int16: + { + GInt16 nVal = *static_cast(pSrcWord); + switch( eDstType ) + { + case GDT_Byte: + { + GByte byVal; + if( nVal > 255 ) + byVal = 255; + else if (nVal < 0) + byVal = 0; + else + byVal = static_cast(nVal); + *static_cast(pDstWord) = byVal; + continue; + } + case GDT_UInt16: + if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt32: + if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int32: + *static_cast(pDstWord) = nVal; + continue; + case GDT_CInt16: + { + GInt16 *panDstWord = static_cast(pDstWord); + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + case GDT_CInt32: + { + GInt32 *panDstWord = static_cast(pDstWord); + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + default: + break; + } + dfPixelValue = nVal; + } + break; + + case GDT_Int32: + { + GInt32 nVal = *static_cast(pSrcWord); + switch( eDstType ) + { + case GDT_Byte: + { + GByte byVal; + if( nVal > 255 ) + byVal = 255; + else if (nVal < 0) + byVal = 0; + else + byVal = nVal; + *static_cast(pDstWord) = byVal; + continue; + } + case GDT_UInt16: + if( nVal > 65535 ) + nVal = 65535; + else if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int16: + if( nVal > 32767 ) + nVal = 32767; + else if( nVal < -32768) + nVal = -32768; + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt32: + if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_CInt16: + { + GInt16 *panDstWord = static_cast(pDstWord); + if( nVal > 32767 ) + nVal = 32767; + else if( nVal < -32768) + nVal = -32768; + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + case GDT_CInt32: + { + GInt32 *panDstWord = static_cast(pDstWord); + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + default: + break; + } + dfPixelValue = nVal; + } + break; + + case GDT_UInt32: + { + GUInt32 nVal = *static_cast(pSrcWord); + switch( eDstType ) + { + case GDT_Byte: + { + GByte byVal; + if( nVal > 255 ) + byVal = 255; + else + byVal = nVal; + *static_cast(pDstWord) = byVal; + continue; + } + case GDT_UInt16: + if( nVal > 65535 ) + nVal = 65535; + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int16: + if( nVal > 32767 ) + nVal = 32767; + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int32: + if( nVal > 2147483647UL ) + nVal = 2147483647UL; + *static_cast(pDstWord) = nVal; + continue; + case GDT_CInt16: + { + GInt16 *panDstWord = static_cast(pDstWord); + if( nVal > 32767 ) + nVal = 32767; + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + case GDT_CInt32: + { + GInt32 *panDstWord = static_cast(pDstWord); + if( nVal > 2147483647UL ) + nVal = 2147483647UL; + panDstWord[0] = nVal; + panDstWord[1] = 0; + continue; + } + default: + break; + } + dfPixelValue = nVal; + } + break; + + case GDT_CInt16: + { + GInt16 *panSrcWord = static_cast(pSrcWord); + GInt16 nVal = panSrcWord[0]; + switch( eDstType ) + { + case GDT_Byte: + { + GByte byVal; + if( nVal > 255 ) + byVal = 255; + else if (nVal < 0) + byVal = 0; + else + byVal = static_cast(nVal); + *static_cast(pDstWord) = byVal; + continue; + } + case GDT_Int16: + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt16: + if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt32: + if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int32: + *static_cast(pDstWord) = nVal; + continue; + case GDT_CInt32: + { + GInt32 *panDstWord = static_cast(pDstWord); + panDstWord[0] = panSrcWord[0]; + panDstWord[1] = panSrcWord[1]; + continue; + } + default: + break; + } + dfPixelValue = panSrcWord[0]; + dfPixelValueI = panSrcWord[1]; + } + break; + + case GDT_CInt32: + { + GInt32 *panSrcWord = static_cast(pSrcWord); + GInt32 nVal = panSrcWord[0]; + switch( eDstType ) + { + case GDT_Byte: + { + GByte byVal; + if( nVal > 255 ) + byVal = 255; + else if (nVal < 0) + byVal = 0; + else + byVal = nVal; + *static_cast(pDstWord) = byVal; + continue; + } + case GDT_Int16: + if( nVal > 32767 ) + nVal = 32767; + else if( nVal < -32768) + nVal = -32768; + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt16: + if( nVal > 65535 ) + nVal = 65535; + else if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_UInt32: + if( nVal < 0 ) + nVal = 0; + *static_cast(pDstWord) = nVal; + continue; + case GDT_Int32: + *static_cast(pDstWord) = nVal; + continue; + case GDT_CInt16: + { + GInt16 *panDstWord = static_cast(pDstWord); + if( nVal > 32767 ) + nVal = 32767; + else if( nVal < -32768) + nVal = -32768; + panDstWord[0] = nVal; + nVal = panSrcWord[1]; + if( nVal > 32767 ) + nVal = 32767; + else if( nVal < -32768) + nVal = -32768; + panDstWord[1] = nVal; + continue; + } + default: + break; + } + dfPixelValue = panSrcWord[0]; + dfPixelValueI = panSrcWord[1]; + } + break; + + case GDT_Float32: + { + float fVal = *static_cast(pSrcWord); + dfPixelValue = fVal; + } + break; + + case GDT_Float64: + { + dfPixelValue = *static_cast(pSrcWord); + } + break; + + case GDT_CFloat32: + { + float *pafSrcWord = static_cast(pSrcWord); + dfPixelValue = pafSrcWord[0]; + dfPixelValueI = pafSrcWord[1]; + } + break; + + case GDT_CFloat64: + { + double *padfSrcWord = static_cast(pSrcWord); + dfPixelValue = padfSrcWord[0]; + dfPixelValueI = padfSrcWord[1]; + } + break; + + default: + CPLAssert( FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Set the destination pixel, doing range clipping as needed. */ +/* -------------------------------------------------------------------- */ + switch( eDstType ) + { + case GDT_Byte: + { + GByte *pabyDstWord = static_cast(pDstWord); + + dfPixelValue += (float) 0.5; + + if( dfPixelValue < 0.0 ) + *pabyDstWord = 0; + else if( dfPixelValue > 255.0 ) + *pabyDstWord = 255; + else + *pabyDstWord = (GByte) dfPixelValue; + } + break; + + case GDT_UInt16: + { + GUInt16 nVal; + + dfPixelValue += 0.5; + + if( dfPixelValue < 0.0 ) + nVal = 0; + else if( dfPixelValue > 65535.0 ) + nVal = 65535; + else + nVal = (GUInt16) dfPixelValue; + + *static_cast(pDstWord) = nVal; + } + break; + + case GDT_Int16: + { + GInt16 nVal; + + dfPixelValue += 0.5; + + if( dfPixelValue < -32768 ) + nVal = -32768; + else if( dfPixelValue > 32767 ) + nVal = 32767; + else + nVal = (GInt16) floor(dfPixelValue); + + *static_cast(pDstWord) = nVal; + } + break; + + case GDT_UInt32: + { + GUInt32 nVal; + + dfPixelValue += 0.5; + + if( dfPixelValue < 0 ) + nVal = 0; + else if( dfPixelValue > 4294967295U ) + nVal = 4294967295U; + else + nVal = (GInt32) dfPixelValue; + + *static_cast(pDstWord) = nVal; + } + break; + + case GDT_Int32: + { + GInt32 nVal; + + dfPixelValue += 0.5; + + if( dfPixelValue < -2147483648.0 ) + nVal = INT_MIN; + else if( dfPixelValue > 2147483647 ) + nVal = 2147483647; + else + nVal = (GInt32) floor(dfPixelValue); + + *static_cast(pDstWord) = nVal; + } + break; + + case GDT_Float32: + { + *static_cast(pDstWord) = static_cast(dfPixelValue); + } + break; + + case GDT_Float64: + *static_cast(pDstWord) = dfPixelValue; + break; + + case GDT_CInt16: + { + GInt16 nVal; + GInt16 *panDstWord = static_cast(pDstWord); + + dfPixelValue += 0.5; + dfPixelValueI += 0.5; + + if( dfPixelValue < -32768 ) + nVal = -32768; + else if( dfPixelValue > 32767 ) + nVal = 32767; + else + nVal = (GInt16) floor(dfPixelValue); + panDstWord[0] = nVal; + + if( dfPixelValueI < -32768 ) + nVal = -32768; + else if( dfPixelValueI > 32767 ) + nVal = 32767; + else + nVal = (GInt16) floor(dfPixelValueI); + panDstWord[1] = nVal; + } + break; + + case GDT_CInt32: + { + GInt32 nVal; + GInt32 *panDstWord = static_cast(pDstWord); + + dfPixelValue += 0.5; + dfPixelValueI += 0.5; + + if( dfPixelValue < -2147483648.0 ) + nVal = INT_MIN; + else if( dfPixelValue > 2147483647 ) + nVal = 2147483647; + else + nVal = (GInt32) floor(dfPixelValue); + + panDstWord[0] = nVal; + + if( dfPixelValueI < -2147483648.0 ) + nVal = INT_MIN; + else if( dfPixelValueI > 2147483647 ) + nVal = 2147483647; + else + nVal = (GInt32) floor(dfPixelValueI); + + panDstWord[1] = nVal; + } + break; + + case GDT_CFloat32: + { + float *pafDstWord = static_cast(pDstWord); + pafDstWord[0] = static_cast(dfPixelValue); + pafDstWord[1] = static_cast(dfPixelValueI); + } + break; + + case GDT_CFloat64: + { + double *padfDstWord = static_cast(pDstWord); + padfDstWord[0] = dfPixelValue; + padfDstWord[1] = dfPixelValueI; + } + break; + + default: + CPLAssert( FALSE ); + } + } /* next iWord */ +#endif // defined USE_NEW_COPYWORDS +} + +/************************************************************************/ +/* GDALCopyBits() */ +/************************************************************************/ + +/** + * Bitwise word copying. + * + * A function for moving sets of partial bytes around. Loosely + * speaking this is a bitswise analog to GDALCopyWords(). + * + * It copies nStepCount "words" where each word is nBitCount bits long. + * The nSrcStep and nDstStep are the number of bits from the start of one + * word to the next (same as nBitCount if they are packed). The nSrcOffset + * and nDstOffset are the offset into the source and destination buffers + * to start at, also measured in bits. + * + * All bit offsets are assumed to start from the high order bit in a byte + * (ie. most significant bit first). Currently this function is not very + * optimized, but it may be improved for some common cases in the future + * as needed. + * + * @param pabySrcData the source data buffer. + * @param nSrcOffset the offset (in bits) in pabySrcData to the start of the + * first word to copy. + * @param nSrcStep the offset in bits from the start one source word to the + * start of the next. + * @param pabyDstData the destination data buffer. + * @param nDstOffset the offset (in bits) in pabyDstData to the start of the + * first word to copy over. + * @param nDstStep the offset in bits from the start one word to the + * start of the next. + * @param nBitCount the number of bits in a word to be copied. + * @param nStepCount the number of words to copy. + */ + +void GDALCopyBits( const GByte *pabySrcData, int nSrcOffset, int nSrcStep, + GByte *pabyDstData, int nDstOffset, int nDstStep, + int nBitCount, int nStepCount ) + +{ + VALIDATE_POINTER0( pabySrcData, "GDALCopyBits" ); + + int iStep; + int iBit; + + for( iStep = 0; iStep < nStepCount; iStep++ ) + { + for( iBit = 0; iBit < nBitCount; iBit++ ) + { + if( pabySrcData[nSrcOffset>>3] + & (0x80 >>(nSrcOffset & 7)) ) + pabyDstData[nDstOffset>>3] |= (0x80 >> (nDstOffset & 7)); + else + pabyDstData[nDstOffset>>3] &= ~(0x80 >> (nDstOffset & 7)); + + + nSrcOffset++; + nDstOffset++; + } + + nSrcOffset += (nSrcStep - nBitCount); + nDstOffset += (nDstStep - nBitCount); + } +} + +/************************************************************************/ +/* GDALGetBestOverviewLevel() */ +/* */ +/* Returns the best overview level to satisfy the query or -1 if none */ +/* Also updates nXOff, nYOff, nXSize, nYSize and psExtraArg when */ +/* returning a valid overview level */ +/************************************************************************/ + +int GDALBandGetBestOverviewLevel(GDALRasterBand* poBand, + int &nXOff, int &nYOff, + int &nXSize, int &nYSize, + int nBufXSize, int nBufYSize ) +{ + return GDALBandGetBestOverviewLevel2(poBand, nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, NULL); +} + +int GDALBandGetBestOverviewLevel2(GDALRasterBand* poBand, + int &nXOff, int &nYOff, + int &nXSize, int &nYSize, + int nBufXSize, int nBufYSize, + GDALRasterIOExtraArg* psExtraArg ) +{ + double dfDesiredResolution; +/* -------------------------------------------------------------------- */ +/* Compute the desired resolution. The resolution is */ +/* based on the least reduced axis, and represents the number */ +/* of source pixels to one destination pixel. */ +/* -------------------------------------------------------------------- */ + if( (nXSize / (double) nBufXSize) < (nYSize / (double) nBufYSize ) + || nBufYSize == 1 ) + dfDesiredResolution = nXSize / (double) nBufXSize; + else + dfDesiredResolution = nYSize / (double) nBufYSize; + +/* -------------------------------------------------------------------- */ +/* Find the overview level that largest resolution value (most */ +/* downsampled) that is still less than (or only a little more) */ +/* downsampled than the request. */ +/* -------------------------------------------------------------------- */ + int nOverviewCount = poBand->GetOverviewCount(); + GDALRasterBand* poBestOverview = NULL; + double dfBestResolution = 0; + int nBestOverviewLevel = -1; + + for( int iOverview = 0; iOverview < nOverviewCount; iOverview++ ) + { + GDALRasterBand *poOverview = poBand->GetOverview( iOverview ); + if (poOverview == NULL) + continue; + + double dfResolution; + + // What resolution is this? + if( (poBand->GetXSize() / (double) poOverview->GetXSize()) + < (poBand->GetYSize() / (double) poOverview->GetYSize()) ) + dfResolution = + poBand->GetXSize() / (double) poOverview->GetXSize(); + else + dfResolution = + poBand->GetYSize() / (double) poOverview->GetYSize(); + + // Is it nearly the requested resolution and better (lower) than + // the current best resolution? + if( dfResolution >= dfDesiredResolution * 1.2 + || dfResolution <= dfBestResolution ) + continue; + + // Ignore AVERAGE_BIT2GRAYSCALE overviews for RasterIO purposes. + const char *pszResampling = + poOverview->GetMetadataItem( "RESAMPLING" ); + + if( pszResampling != NULL && EQUALN(pszResampling,"AVERAGE_BIT2",12)) + continue; + + // OK, this is our new best overview. + poBestOverview = poOverview; + nBestOverviewLevel = iOverview; + dfBestResolution = dfResolution; + } + +/* -------------------------------------------------------------------- */ +/* If we didn't find an overview that helps us, just return */ +/* indicating failure and the full resolution image will be used. */ +/* -------------------------------------------------------------------- */ + if( nBestOverviewLevel < 0 ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Recompute the source window in terms of the selected */ +/* overview. */ +/* -------------------------------------------------------------------- */ + int nOXOff, nOYOff, nOXSize, nOYSize; + double dfXRes, dfYRes; + + dfXRes = poBand->GetXSize() / (double) poBestOverview->GetXSize(); + dfYRes = poBand->GetYSize() / (double) poBestOverview->GetYSize(); + + nOXOff = MIN(poBestOverview->GetXSize()-1,(int) (nXOff/dfXRes+0.5)); + nOYOff = MIN(poBestOverview->GetYSize()-1,(int) (nYOff/dfYRes+0.5)); + nOXSize = MAX(1,(int) (nXSize/dfXRes + 0.5)); + nOYSize = MAX(1,(int) (nYSize/dfYRes + 0.5)); + if( nOXOff + nOXSize > poBestOverview->GetXSize() ) + nOXSize = poBestOverview->GetXSize() - nOXOff; + if( nOYOff + nOYSize > poBestOverview->GetYSize() ) + nOYSize = poBestOverview->GetYSize() - nOYOff; + + nXOff = nOXOff; + nYOff = nOYOff; + nXSize = nOXSize; + nYSize = nOYSize; + + if( psExtraArg && psExtraArg->bFloatingPointWindowValidity ) + { + psExtraArg->dfXOff /= dfXRes; + psExtraArg->dfXSize /= dfXRes; + psExtraArg->dfYOff /= dfYRes; + psExtraArg->dfYSize /= dfYRes; + } + + return nBestOverviewLevel; +} + + +/************************************************************************/ +/* OverviewRasterIO() */ +/* */ +/* Special work function to utilize available overviews to */ +/* more efficiently satisfy downsampled requests. It will */ +/* return CE_Failure if there are no appropriate overviews */ +/* available but it doesn't emit any error messages. */ +/************************************************************************/ + +CPLErr GDALRasterBand::OverviewRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + GSpacing nPixelSpace, GSpacing nLineSpace, + GDALRasterIOExtraArg* psExtraArg ) + + +{ + int nOverview; + GDALRasterIOExtraArg sExtraArg; + + GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg); + + nOverview = + GDALBandGetBestOverviewLevel2(this, nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, &sExtraArg); + if (nOverview < 0) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Recast the call in terms of the new raster layer. */ +/* -------------------------------------------------------------------- */ + GDALRasterBand* poOverviewBand = GetOverview(nOverview); + if (poOverviewBand == NULL) + return CE_Failure; + + return poOverviewBand->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, eBufType, + nPixelSpace, nLineSpace, &sExtraArg ); +} + +/************************************************************************/ +/* GetBestOverviewLevel() */ +/* */ +/* Returns the best overview level to satisfy the query or -1 if none */ +/* Also updates nXOff, nYOff, nXSize, nYSize when returning a valid */ +/* overview level */ +/************************************************************************/ + +static +int GDALDatasetGetBestOverviewLevel(GDALDataset* poDS, + int &nXOff, int &nYOff, + int &nXSize, int &nYSize, + int nBufXSize, int nBufYSize, + int nBandCount, int *panBandMap) +{ + int iBand, iOverview; + int nOverviewCount = 0; + GDALRasterBand *poFirstBand = NULL; + + if (nBandCount == 0) + return -1; + +/* -------------------------------------------------------------------- */ +/* Check that all bands have the same number of overviews and */ +/* that they have all the same size and block dimensions */ +/* -------------------------------------------------------------------- */ + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterBand *poBand = poDS->GetRasterBand( panBandMap[iBand] ); + if (iBand == 0) + { + poFirstBand = poBand; + nOverviewCount = poBand->GetOverviewCount(); + } + else if (nOverviewCount != poBand->GetOverviewCount()) + { + CPLDebug( "GDAL", + "GDALDataset::GetBestOverviewLevel() ... " + "mismatched overview count, use std method." ); + return -1; + } + else + { + for(iOverview = 0; iOverview < nOverviewCount; iOverview++) + { + GDALRasterBand* poOvrBand = + poBand->GetOverview(iOverview); + GDALRasterBand* poOvrFirstBand = + poFirstBand->GetOverview(iOverview); + if ( poOvrBand == NULL || poOvrFirstBand == NULL) + continue; + + if ( poOvrFirstBand->GetXSize() != poOvrBand->GetXSize() || + poOvrFirstBand->GetYSize() != poOvrBand->GetYSize() ) + { + CPLDebug( "GDAL", + "GDALDataset::GetBestOverviewLevel() ... " + "mismatched overview sizes, use std method." ); + return -1; + } + int nBlockXSizeFirst=0, nBlockYSizeFirst=0; + int nBlockXSizeCurrent=0, nBlockYSizeCurrent=0; + poOvrFirstBand->GetBlockSize(&nBlockXSizeFirst, &nBlockYSizeFirst); + poOvrBand->GetBlockSize(&nBlockXSizeCurrent, &nBlockYSizeCurrent); + if (nBlockXSizeFirst != nBlockXSizeCurrent || + nBlockYSizeFirst != nBlockYSizeCurrent) + { + CPLDebug( "GDAL", + "GDALDataset::GetBestOverviewLevel() ... " + "mismatched block sizes, use std method." ); + return -1; + } + } + } + } + + return GDALBandGetBestOverviewLevel2(poFirstBand, + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, NULL); +} + +/************************************************************************/ +/* BlockBasedRasterIO() */ +/* */ +/* This convenience function implements a dataset level */ +/* RasterIO() interface based on calling down to fetch blocks, */ +/* much like the GDALRasterBand::IRasterIO(), but it handles */ +/* all bands at once, so that a format driver that handles a */ +/* request for different bands of the same block efficiently */ +/* (ie. without re-reading interleaved data) will efficiently. */ +/* */ +/* This method is intended to be called by an overridden */ +/* IRasterIO() method in the driver specific GDALDataset */ +/* derived class. */ +/* */ +/* Default internal implementation of RasterIO() ... utilizes */ +/* the Block access methods to satisfy the request. This would */ +/* normally only be overridden by formats with overviews. */ +/* */ +/* To keep things relatively simple, this method does not */ +/* currently take advantage of some special cases addressed in */ +/* GDALRasterBand::IRasterIO(), so it is likely best to only */ +/* call it when you know it will help. That is in cases where */ +/* data is at 1:1 to the buffer, and you know the driver is */ +/* implementing interleaved IO efficiently on a block by block */ +/* basis. Overviews will be used when possible. */ +/************************************************************************/ + +CPLErr +GDALDataset::BlockBasedRasterIO( GDALRWFlag eRWFlag, + int nXOff, int nYOff, int nXSize, int nYSize, + void * pData, int nBufXSize, int nBufYSize, + GDALDataType eBufType, + int nBandCount, int *panBandMap, + GSpacing nPixelSpace, GSpacing nLineSpace, + GSpacing nBandSpace, + GDALRasterIOExtraArg* psExtraArg ) + +{ + GByte **papabySrcBlock = NULL; + GDALRasterBlock *poBlock = NULL; + GDALRasterBlock **papoBlocks = NULL; + int nLBlockX=-1, nLBlockY=-1, iBufYOff, iBufXOff, iSrcY, iBand; + int nBlockXSize=1, nBlockYSize=1; + CPLErr eErr = CE_None; + GDALDataType eDataType = GDT_Byte; + + CPLAssert( NULL != pData ); + +/* -------------------------------------------------------------------- */ +/* Ensure that all bands share a common block size and data type. */ +/* -------------------------------------------------------------------- */ + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterBand *poBand = GetRasterBand( panBandMap[iBand] ); + int nThisBlockXSize, nThisBlockYSize; + + if( iBand == 0 ) + { + poBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); + eDataType = poBand->GetRasterDataType(); + } + else + { + poBand->GetBlockSize( &nThisBlockXSize, &nThisBlockYSize ); + if( nThisBlockXSize != nBlockXSize + || nThisBlockYSize != nBlockYSize ) + { + CPLDebug( "GDAL", + "GDALDataset::BlockBasedRasterIO() ... " + "mismatched block sizes, use std method." ); + return GDALDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, + nBandSpace, psExtraArg ); + } + + if( eDataType != poBand->GetRasterDataType() + && (nXSize != nBufXSize || nYSize != nBufYSize) ) + { + CPLDebug( "GDAL", + "GDALDataset::BlockBasedRasterIO() ... " + "mismatched band data types, use std method." ); + return GDALDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, + nBandSpace, psExtraArg ); + } + } + } + +/* ==================================================================== */ +/* In this special case at full resolution we step through in */ +/* blocks, turning the request over to the per-band */ +/* IRasterIO(), but ensuring that all bands of one block are */ +/* called before proceeding to the next. */ +/* ==================================================================== */ + + if( nXSize == nBufXSize && nYSize == nBufYSize ) + { + GDALRasterIOExtraArg sDummyExtraArg; + INIT_RASTERIO_EXTRA_ARG(sDummyExtraArg); + + int nChunkYSize, nChunkXSize, nChunkXOff, nChunkYOff; + + for( iBufYOff = 0; iBufYOff < nBufYSize; iBufYOff += nChunkYSize ) + { + nChunkYSize = nBlockYSize; + nChunkYOff = iBufYOff + nYOff; + nChunkYSize = nBlockYSize - (nChunkYOff % nBlockYSize); + if( nChunkYSize == 0 ) + nChunkYSize = nBlockYSize; + if( nChunkYOff + nChunkYSize > nYOff + nYSize ) + nChunkYSize = (nYOff + nYSize) - nChunkYOff; + + for( iBufXOff = 0; iBufXOff < nBufXSize; iBufXOff += nChunkXSize ) + { + nChunkXSize = nBlockXSize; + nChunkXOff = iBufXOff + nXOff; + nChunkXSize = nBlockXSize - (nChunkXOff % nBlockXSize); + if( nChunkXSize == 0 ) + nChunkXSize = nBlockXSize; + if( nChunkXOff + nChunkXSize > nXOff + nXSize ) + nChunkXSize = (nXOff + nXSize) - nChunkXOff; + + GByte *pabyChunkData; + + pabyChunkData = ((GByte *) pData) + + iBufXOff * nPixelSpace + + (GPtrDiff_t)iBufYOff * nLineSpace; + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterBand *poBand = GetRasterBand(panBandMap[iBand]); + + eErr = + poBand->GDALRasterBand::IRasterIO( + eRWFlag, nChunkXOff, nChunkYOff, + nChunkXSize, nChunkYSize, + pabyChunkData + (GPtrDiff_t)iBand * nBandSpace, + nChunkXSize, nChunkYSize, eBufType, + nPixelSpace, nLineSpace, &sDummyExtraArg ); + if( eErr != CE_None ) + return eErr; + } + } + + if( psExtraArg->pfnProgress != NULL && + !psExtraArg->pfnProgress(1.0 * MAX(nBufYSize,iBufYOff + nChunkYSize) / nBufYSize, "", psExtraArg->pProgressData) ) + { + return CE_Failure; + } + } + + return CE_None; + } + + /* Below code is not compatible with that case. It would need a complete */ + /* separate code like done in GDALRasterBand::IRasterIO. */ + if (eRWFlag == GF_Write && (nBufXSize < nXSize || nBufYSize < nYSize)) + { + return GDALDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, + nBandSpace, psExtraArg ); + } + + /* We could have a smarter implementation, but that will do for now */ + if( psExtraArg->eResampleAlg != GRIORA_NearestNeighbour && + (nBufXSize != nXSize || nBufYSize != nYSize) ) + { + return GDALDataset::IRasterIO( eRWFlag, + nXOff, nYOff, nXSize, nYSize, + pData, nBufXSize, nBufYSize, + eBufType, + nBandCount, panBandMap, + nPixelSpace, nLineSpace, + nBandSpace, psExtraArg ); + } + +/* ==================================================================== */ +/* Loop reading required source blocks to satisfy output */ +/* request. This is the most general implementation. */ +/* ==================================================================== */ + + int nBandDataSize = GDALGetDataTypeSize( eDataType ) / 8; + + papabySrcBlock = (GByte **) CPLCalloc(sizeof(GByte*),nBandCount); + papoBlocks = (GDALRasterBlock **) CPLCalloc(sizeof(void*),nBandCount); + +/* -------------------------------------------------------------------- */ +/* Select an overview level if appropriate. */ +/* -------------------------------------------------------------------- */ + int nOverviewLevel = GDALDatasetGetBestOverviewLevel (this, + nXOff, nYOff, nXSize, nYSize, + nBufXSize, nBufYSize, + nBandCount, panBandMap); + if (nOverviewLevel >= 0) + { + GetRasterBand(panBandMap[0])->GetOverview(nOverviewLevel)-> + GetBlockSize( &nBlockXSize, &nBlockYSize ); + } + +/* -------------------------------------------------------------------- */ +/* Compute stepping increment. */ +/* -------------------------------------------------------------------- */ + double dfSrcX, dfSrcY, dfSrcXInc, dfSrcYInc; + + dfSrcXInc = nXSize / (double) nBufXSize; + dfSrcYInc = nYSize / (double) nBufYSize; + +/* -------------------------------------------------------------------- */ +/* Loop over buffer computing source locations. */ +/* -------------------------------------------------------------------- */ + for( iBufYOff = 0; iBufYOff < nBufYSize; iBufYOff++ ) + { + GPtrDiff_t iBufOffset; + int iSrcOffset; + + dfSrcY = (iBufYOff+0.5) * dfSrcYInc + nYOff; + iSrcY = (int) dfSrcY; + + iBufOffset = (GPtrDiff_t)iBufYOff * (GPtrDiff_t)nLineSpace; + + for( iBufXOff = 0; iBufXOff < nBufXSize; iBufXOff++ ) + { + int iSrcX; + + dfSrcX = (iBufXOff+0.5) * dfSrcXInc + nXOff; + + iSrcX = (int) dfSrcX; + + // FIXME: this code likely doesn't work if the dirty block gets flushed + // to disk before being completely written. + // In the meantime, bJustInitalize should probably be set to FALSE + // even if it is not ideal performance wise, and for lossy compression + +/* -------------------------------------------------------------------- */ +/* Ensure we have the appropriate block loaded. */ +/* -------------------------------------------------------------------- */ + if( iSrcX < nLBlockX * nBlockXSize + || iSrcX >= (nLBlockX+1) * nBlockXSize + || iSrcY < nLBlockY * nBlockYSize + || iSrcY >= (nLBlockY+1) * nBlockYSize ) + { + nLBlockX = iSrcX / nBlockXSize; + nLBlockY = iSrcY / nBlockYSize; + + int bJustInitialize = + eRWFlag == GF_Write + && nYOff <= nLBlockY * nBlockYSize + && nYOff + nYSize >= (nLBlockY+1) * nBlockYSize + && nXOff <= nLBlockX * nBlockXSize + && nXOff + nXSize >= (nLBlockX+1) * nBlockXSize; + /*int bMemZeroBuffer = FALSE; + if( eRWFlag == GF_Write && !bJustInitialize && + nXOff <= nLBlockX * nBlockXSize && + nYOff <= nLBlockY * nBlockYSize && + (nXOff + nXSize >= (nLBlockX+1) * nBlockXSize || + (nXOff + nXSize == GetRasterXSize() && (nLBlockX+1) * nBlockXSize > GetRasterXSize())) && + (nYOff + nYSize >= (nLBlockY+1) * nBlockYSize || + (nYOff + nYSize == GetRasterYSize() && (nLBlockY+1) * nBlockYSize > GetRasterYSize())) ) + { + bJustInitialize = TRUE; + bMemZeroBuffer = TRUE; + }*/ + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GDALRasterBand *poBand = GetRasterBand( panBandMap[iBand]); + if (nOverviewLevel >= 0) + poBand = poBand->GetOverview(nOverviewLevel); + poBlock = poBand->GetLockedBlockRef( nLBlockX, nLBlockY, + bJustInitialize ); + if( poBlock == NULL ) + { + eErr = CE_Failure; + goto CleanupAndReturn; + } + + if( eRWFlag == GF_Write ) + poBlock->MarkDirty(); + + if( papoBlocks[iBand] != NULL ) + papoBlocks[iBand]->DropLock(); + + papoBlocks[iBand] = poBlock; + + papabySrcBlock[iBand] = (GByte *) poBlock->GetDataRef(); + if( papabySrcBlock[iBand] == NULL ) + { + eErr = CE_Failure; + goto CleanupAndReturn; + } + /*if( bMemZeroBuffer ) + { + memset(papabySrcBlock[iBand], 0, + nBandDataSize * nBlockXSize * nBlockYSize); + }*/ + } + } + +/* -------------------------------------------------------------------- */ +/* Copy over this pixel of data. */ +/* -------------------------------------------------------------------- */ + iSrcOffset = ((GPtrDiff_t)iSrcX - (GPtrDiff_t)nLBlockX*nBlockXSize + + ((GPtrDiff_t)iSrcY - (GPtrDiff_t)nLBlockY*nBlockYSize) * nBlockXSize)*nBandDataSize; + + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + GByte *pabySrcBlock = papabySrcBlock[iBand]; + GPtrDiff_t iBandBufOffset = iBufOffset + (GPtrDiff_t)iBand * (GPtrDiff_t)nBandSpace; + + if( eDataType == eBufType ) + { + if( eRWFlag == GF_Read ) + memcpy( ((GByte *) pData) + iBandBufOffset, + pabySrcBlock + iSrcOffset, nBandDataSize ); + else + memcpy( pabySrcBlock + iSrcOffset, + ((GByte *)pData) + iBandBufOffset, nBandDataSize ); + } + else + { + /* type to type conversion ... ouch, this is expensive way + of handling single words */ + + if( eRWFlag == GF_Read ) + GDALCopyWords( pabySrcBlock + iSrcOffset, eDataType, 0, + ((GByte *) pData) + iBandBufOffset, + eBufType, 0, 1 ); + else + GDALCopyWords( ((GByte *) pData) + iBandBufOffset, + eBufType, 0, + pabySrcBlock + iSrcOffset, eDataType, 0, + 1 ); + } + } + + iBufOffset += (int)nPixelSpace; + } + } + +/* -------------------------------------------------------------------- */ +/* CleanupAndReturn. */ +/* -------------------------------------------------------------------- */ + CleanupAndReturn: + CPLFree( papabySrcBlock ); + if( papoBlocks != NULL ) + { + for( iBand = 0; iBand < nBandCount; iBand++ ) + { + if( papoBlocks[iBand] != NULL ) + papoBlocks[iBand]->DropLock(); + } + CPLFree( papoBlocks ); + } + + return( CE_None ); +} + +/************************************************************************/ +/* GDALCopyWholeRasterGetSwathSize() */ +/************************************************************************/ + +static void GDALCopyWholeRasterGetSwathSize(GDALRasterBand *poSrcPrototypeBand, + GDALRasterBand *poDstPrototypeBand, + int nBandCount, + int bDstIsCompressed, int bInterleave, + int* pnSwathCols, int *pnSwathLines) +{ + GDALDataType eDT = poDstPrototypeBand->GetRasterDataType(); + int nSrcBlockXSize, nSrcBlockYSize; + int nBlockXSize, nBlockYSize; + + int nXSize = poSrcPrototypeBand->GetXSize(); + int nYSize = poSrcPrototypeBand->GetYSize(); + + poSrcPrototypeBand->GetBlockSize( &nSrcBlockXSize, &nSrcBlockYSize ); + poDstPrototypeBand->GetBlockSize( &nBlockXSize, &nBlockYSize ); + + int nMaxBlockXSize = MAX(nBlockXSize, nSrcBlockXSize); + int nMaxBlockYSize = MAX(nBlockYSize, nSrcBlockYSize); + + int nPixelSize = (GDALGetDataTypeSize(eDT) / 8); + if( bInterleave) + nPixelSize *= nBandCount; + + // aim for one row of blocks. Do not settle for less. + int nSwathCols = nXSize; + int nSwathLines = nBlockYSize; + + const char* pszSrcCompression = + poSrcPrototypeBand->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE"); + +/* -------------------------------------------------------------------- */ +/* What will our swath size be? */ +/* -------------------------------------------------------------------- */ + /* When writing interleaved data in a compressed format, we want to be sure */ + /* that each block will only be written once, so the swath size must not be */ + /* greater than the block cache. */ + const char* pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", NULL); + int nTargetSwathSize; + if( pszSwathSize != NULL ) + nTargetSwathSize = atoi(pszSwathSize); + else + { + /* As a default, take one 1/4 of the cache size */ + nTargetSwathSize = (int)MIN(INT_MAX, GDALGetCacheMax64() / 4); + + /* but if the minimum idal swath buf size is less, then go for it to */ + /* avoid unnecessarily abusing RAM usage */ + GIntBig nIdealSwathBufSize = (GIntBig)nSwathCols * nSwathLines * nPixelSize; + if( pszSrcCompression != NULL && EQUAL(pszSrcCompression, "JPEG2000") && + (!bDstIsCompressed || ((nSrcBlockXSize % nBlockXSize) == 0 && (nSrcBlockYSize % nBlockYSize) == 0)) ) + { + nIdealSwathBufSize = MAX(nIdealSwathBufSize, + (GIntBig)nSwathCols * nSrcBlockYSize * nPixelSize); + } + if( nTargetSwathSize > nIdealSwathBufSize ) + nTargetSwathSize = (int)nIdealSwathBufSize; + } + + if (nTargetSwathSize < 1000000) + nTargetSwathSize = 1000000; + + /* But let's check that */ + if (bDstIsCompressed && bInterleave && nTargetSwathSize > GDALGetCacheMax64()) + { + CPLError(CE_Warning, CPLE_AppDefined, + "When translating into a compressed interleave format, the block cache size (" CPL_FRMT_GIB ") " + "should be at least the size of the swath (%d) (GDAL_SWATH_SIZE config. option)", GDALGetCacheMax64(), nTargetSwathSize); + } + +#define IS_DIVIDER_OF(x,y) ((y)%(x) == 0) +#define ROUND_TO(x,y) (((x)/(y))*(y)) + + /* if both input and output datasets are tiled, that the tile dimensions */ + /* are "compatible", try to stick to a swath dimension that is a multiple */ + /* of input and output block dimensions */ + if (nBlockXSize != nXSize && nSrcBlockXSize != nXSize && + IS_DIVIDER_OF(nBlockXSize, nMaxBlockXSize) && + IS_DIVIDER_OF(nSrcBlockXSize, nMaxBlockXSize) && + IS_DIVIDER_OF(nBlockYSize, nMaxBlockYSize) && + IS_DIVIDER_OF(nSrcBlockYSize, nMaxBlockYSize)) + { + if (((GIntBig)nMaxBlockXSize) * nMaxBlockYSize * nPixelSize <= + (GIntBig)nTargetSwathSize) + { + nSwathCols = nTargetSwathSize / (nMaxBlockYSize * nPixelSize); + nSwathCols = ROUND_TO(nSwathCols, nMaxBlockXSize); + if (nSwathCols == 0) + nSwathCols = nMaxBlockXSize; + if (nSwathCols > nXSize) + nSwathCols = nXSize; + nSwathLines = nMaxBlockYSize; + + if (((GIntBig)nSwathCols) * nSwathLines * nPixelSize > + (GIntBig)nTargetSwathSize) + { + nSwathCols = nXSize; + nSwathLines = nBlockYSize; + } + } + } + + int nMemoryPerCol = nSwathCols * nPixelSize; + + /* Do the computation on a big int since for example when translating */ + /* the JPL WMS layer, we overflow 32 bits*/ + GIntBig nSwathBufSize = (GIntBig)nMemoryPerCol * nSwathLines; + if (nSwathBufSize > (GIntBig)nTargetSwathSize) + { + nSwathLines = nTargetSwathSize / nMemoryPerCol; + if (nSwathLines == 0) + nSwathLines = 1; + + CPLDebug( "GDAL", + "GDALCopyWholeRasterGetSwathSize(): adjusting to %d line swath " + "since requirement (" CPL_FRMT_GIB " bytes) exceed target swath size (%d bytes) (GDAL_SWATH_SIZE config. option)", + nSwathLines, (GIntBig)nBlockYSize * nMemoryPerCol, nTargetSwathSize); + } + // If we are processing single scans, try to handle several at once. + // If we are handling swaths already, only grow the swath if a row + // of blocks is substantially less than our target buffer size. + else if( nSwathLines == 1 + || nMemoryPerCol * nSwathLines < nTargetSwathSize / 10 ) + { + nSwathLines = MIN(nYSize,MAX(1,nTargetSwathSize/nMemoryPerCol)); + + /* If possible try to align to source and target block height */ + if ((nSwathLines % nMaxBlockYSize) != 0 && nSwathLines > nMaxBlockYSize && + IS_DIVIDER_OF(nBlockYSize, nMaxBlockYSize) && + IS_DIVIDER_OF(nSrcBlockYSize, nMaxBlockYSize)) + nSwathLines = ROUND_TO(nSwathLines, nMaxBlockYSize); + } + + if( pszSrcCompression != NULL && EQUAL(pszSrcCompression, "JPEG2000") && + (!bDstIsCompressed || + (IS_DIVIDER_OF(nBlockXSize, nSrcBlockXSize) && + IS_DIVIDER_OF(nBlockYSize, nSrcBlockYSize))) ) + { + /* Typical use case: converting from Pleaiades that is 2048x2048 tiled */ + if (nSwathLines < nSrcBlockYSize) + { + nSwathLines = nSrcBlockYSize; + + /* Number of pixels that can be read/write simultaneously */ + nSwathCols = nTargetSwathSize / (nSrcBlockXSize * nPixelSize); + nSwathCols = ROUND_TO(nSwathCols, nSrcBlockXSize); + if (nSwathCols == 0) + nSwathCols = nSrcBlockXSize; + if (nSwathCols > nXSize) + nSwathCols = nXSize; + + CPLDebug( "GDAL", + "GDALCopyWholeRasterGetSwathSize(): because of compression and too high block,\n" + "use partial width at one time"); + } + else if ((nSwathLines % nSrcBlockYSize) != 0) + { + /* Round on a multiple of nSrcBlockYSize */ + nSwathLines = ROUND_TO(nSwathLines, nSrcBlockYSize); + CPLDebug( "GDAL", + "GDALCopyWholeRasterGetSwathSize(): because of compression, \n" + "round nSwathLines to block height : %d", nSwathLines); + } + } + else if (bDstIsCompressed) + { + if (nSwathLines < nBlockYSize) + { + nSwathLines = nBlockYSize; + + /* Number of pixels that can be read/write simultaneously */ + nSwathCols = nTargetSwathSize / (nSwathLines * nPixelSize); + nSwathCols = ROUND_TO(nSwathCols, nBlockXSize); + if (nSwathCols == 0) + nSwathCols = nBlockXSize; + if (nSwathCols > nXSize) + nSwathCols = nXSize; + + CPLDebug( "GDAL", + "GDALCopyWholeRasterGetSwathSize(): because of compression and too high block,\n" + "use partial width at one time"); + } + else if ((nSwathLines % nBlockYSize) != 0) + { + /* Round on a multiple of nBlockYSize */ + nSwathLines = ROUND_TO(nSwathLines, nBlockYSize); + CPLDebug( "GDAL", + "GDALCopyWholeRasterGetSwathSize(): because of compression, \n" + "round nSwathLines to block height : %d", nSwathLines); + } + } + + *pnSwathCols = nSwathCols; + *pnSwathLines = nSwathLines; +} + +/************************************************************************/ +/* GDALDatasetCopyWholeRaster() */ +/************************************************************************/ + +/** + * \brief Copy all dataset raster data. + * + * This function copies the complete raster contents of one dataset to + * another similarly configured dataset. The source and destination + * dataset must have the same number of bands, and the same width + * and height. The bands do not have to have the same data type. + * + * This function is primarily intended to support implementation of + * driver specific CreateCopy() functions. It implements efficient copying, + * in particular "chunking" the copy in substantial blocks and, if appropriate, + * performing the transfer in a pixel interleaved fashion. + * + * Currently the only papszOptions value supported are : "INTERLEAVE=PIXEL" + * to force pixel interleaved operation and "COMPRESSED=YES" to force alignment + * on target dataset block sizes to achieve best compression. More options may be supported in + * the future. + * + * @param hSrcDS the source dataset + * @param hDstDS the destination dataset + * @param papszOptions transfer hints in "StringList" Name=Value format. + * @param pfnProgress progress reporting function. + * @param pProgressData callback data for progress function. + * + * @return CE_None on success, or CE_Failure on failure. + */ + +CPLErr CPL_STDCALL GDALDatasetCopyWholeRaster( + GDALDatasetH hSrcDS, GDALDatasetH hDstDS, char **papszOptions, + GDALProgressFunc pfnProgress, void *pProgressData ) + +{ + VALIDATE_POINTER1( hSrcDS, "GDALDatasetCopyWholeRaster", CE_Failure ); + VALIDATE_POINTER1( hDstDS, "GDALDatasetCopyWholeRaster", CE_Failure ); + + GDALDataset *poSrcDS = (GDALDataset *) hSrcDS; + GDALDataset *poDstDS = (GDALDataset *) hDstDS; + CPLErr eErr = CE_None; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* Confirm the datasets match in size and band counts. */ +/* -------------------------------------------------------------------- */ + int nXSize = poDstDS->GetRasterXSize(), + nYSize = poDstDS->GetRasterYSize(), + nBandCount = poDstDS->GetRasterCount(); + + if( poSrcDS->GetRasterXSize() != nXSize + || poSrcDS->GetRasterYSize() != nYSize + || poSrcDS->GetRasterCount() != nBandCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Input and output dataset sizes or band counts do not\n" + "match in GDALDatasetCopyWholeRaster()" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Report preliminary (0) progress. */ +/* -------------------------------------------------------------------- */ + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Get our prototype band, and assume the others are similarly */ +/* configured. */ +/* -------------------------------------------------------------------- */ + if( nBandCount == 0 ) + return CE_None; + + GDALRasterBand *poSrcPrototypeBand = poSrcDS->GetRasterBand(1); + GDALRasterBand *poDstPrototypeBand = poDstDS->GetRasterBand(1); + GDALDataType eDT = poDstPrototypeBand->GetRasterDataType(); + +/* -------------------------------------------------------------------- */ +/* Do we want to try and do the operation in a pixel */ +/* interleaved fashion? */ +/* -------------------------------------------------------------------- */ + int bInterleave = FALSE; + const char *pszInterleave = NULL; + + pszInterleave = poSrcDS->GetMetadataItem( "INTERLEAVE", "IMAGE_STRUCTURE"); + if( pszInterleave != NULL + && (EQUAL(pszInterleave,"PIXEL") || EQUAL(pszInterleave,"LINE")) ) + bInterleave = TRUE; + + pszInterleave = poDstDS->GetMetadataItem( "INTERLEAVE", "IMAGE_STRUCTURE"); + if( pszInterleave != NULL + && (EQUAL(pszInterleave,"PIXEL") || EQUAL(pszInterleave,"LINE")) ) + bInterleave = TRUE; + + pszInterleave = CSLFetchNameValue( papszOptions, "INTERLEAVE" ); + if( pszInterleave != NULL + && (EQUAL(pszInterleave,"PIXEL") || EQUAL(pszInterleave,"LINE")) ) + bInterleave = TRUE; + else if( pszInterleave != NULL && EQUAL(pszInterleave,"BAND") ) + bInterleave = FALSE; + + /* If the destination is compressed, we must try to write blocks just once, to save */ + /* disk space (GTiff case for example), and to avoid data loss (JPEG compression for example) */ + int bDstIsCompressed = FALSE; + const char* pszDstCompressed= CSLFetchNameValue( papszOptions, "COMPRESSED" ); + if (pszDstCompressed != NULL && CSLTestBoolean(pszDstCompressed)) + bDstIsCompressed = TRUE; + +/* -------------------------------------------------------------------- */ +/* What will our swath size be? */ +/* -------------------------------------------------------------------- */ + + int nSwathCols, nSwathLines; + GDALCopyWholeRasterGetSwathSize(poSrcPrototypeBand, + poDstPrototypeBand, + nBandCount, + bDstIsCompressed, bInterleave, + &nSwathCols, &nSwathLines); + + int nPixelSize = (GDALGetDataTypeSize(eDT) / 8); + if( bInterleave) + nPixelSize *= nBandCount; + + void *pSwathBuf = VSIMalloc3(nSwathCols, nSwathLines, nPixelSize ); + if( pSwathBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Failed to allocate %d*%d*%d byte swath buffer in\n" + "GDALDatasetCopyWholeRaster()", + nSwathCols, nSwathLines, nPixelSize ); + return CE_Failure; + } + + CPLDebug( "GDAL", + "GDALDatasetCopyWholeRaster(): %d*%d swaths, bInterleave=%d", + nSwathCols, nSwathLines, bInterleave ); + + if( nSwathCols == nXSize && poSrcDS->GetDriver() != NULL && + EQUAL(poSrcDS->GetDriver()->GetDescription(), "ECW") ) + { + poSrcDS->AdviseRead(0, 0, nXSize, nYSize, nXSize, nYSize, eDT, nBandCount, NULL, NULL); + } + +/* ==================================================================== */ +/* Band oriented (uninterleaved) case. */ +/* ==================================================================== */ + if( !bInterleave ) + { + int iBand, iX, iY; + + GDALRasterIOExtraArg sExtraArg; + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + + int nTotalBlocks = nBandCount * + ((nYSize + nSwathLines - 1) / nSwathLines) * + ((nXSize + nSwathCols - 1) / nSwathCols); + int nBlocksDone = 0; + + for( iBand = 0; iBand < nBandCount && eErr == CE_None; iBand++ ) + { + int nBand = iBand+1; + + for( iY = 0; iY < nYSize && eErr == CE_None; iY += nSwathLines ) + { + int nThisLines = nSwathLines; + + if( iY + nThisLines > nYSize ) + nThisLines = nYSize - iY; + + for( iX = 0; iX < nXSize && eErr == CE_None; iX += nSwathCols ) + { + int nThisCols = nSwathCols; + + if( iX + nThisCols > nXSize ) + nThisCols = nXSize - iX; + + sExtraArg.pfnProgress = GDALScaledProgress; + sExtraArg.pProgressData = + GDALCreateScaledProgress( nBlocksDone / (double)nTotalBlocks, + (nBlocksDone + 0.5) / (double)nTotalBlocks, + pfnProgress, + pProgressData ); + if( sExtraArg.pProgressData == NULL ) + sExtraArg.pfnProgress = NULL; + + eErr = poSrcDS->RasterIO( GF_Read, + iX, iY, nThisCols, nThisLines, + pSwathBuf, nThisCols, nThisLines, + eDT, 1, &nBand, + 0, 0, 0, &sExtraArg ); + + GDALDestroyScaledProgress( sExtraArg.pProgressData ); + + if( eErr == CE_None ) + eErr = poDstDS->RasterIO( GF_Write, + iX, iY, nThisCols, nThisLines, + pSwathBuf, nThisCols, nThisLines, + eDT, 1, &nBand, + 0, 0, 0, NULL ); + nBlocksDone ++; + if( eErr == CE_None + && !pfnProgress( nBlocksDone / (double)nTotalBlocks, + NULL, pProgressData ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + } + } + } + +/* ==================================================================== */ +/* Pixel interleaved case. */ +/* ==================================================================== */ + else /* if( bInterleave ) */ + { + int iY, iX; + + GDALRasterIOExtraArg sExtraArg; + INIT_RASTERIO_EXTRA_ARG(sExtraArg); + + int nTotalBlocks = ((nYSize + nSwathLines - 1) / nSwathLines) * + ((nXSize + nSwathCols - 1) / nSwathCols); + int nBlocksDone = 0; + + for( iY = 0; iY < nYSize && eErr == CE_None; iY += nSwathLines ) + { + int nThisLines = nSwathLines; + + if( iY + nThisLines > nYSize ) + nThisLines = nYSize - iY; + + for( iX = 0; iX < nXSize && eErr == CE_None; iX += nSwathCols ) + { + int nThisCols = nSwathCols; + + if( iX + nThisCols > nXSize ) + nThisCols = nXSize - iX; + + sExtraArg.pfnProgress = GDALScaledProgress; + sExtraArg.pProgressData = + GDALCreateScaledProgress( nBlocksDone / (double)nTotalBlocks, + (nBlocksDone + 0.5) / (double)nTotalBlocks, + pfnProgress, + pProgressData ); + if( sExtraArg.pProgressData == NULL ) + sExtraArg.pfnProgress = NULL; + + eErr = poSrcDS->RasterIO( GF_Read, + iX, iY, nThisCols, nThisLines, + pSwathBuf, nThisCols, nThisLines, + eDT, nBandCount, NULL, + 0, 0, 0, &sExtraArg ); + + GDALDestroyScaledProgress( sExtraArg.pProgressData ); + + if( eErr == CE_None ) + eErr = poDstDS->RasterIO( GF_Write, + iX, iY, nThisCols, nThisLines, + pSwathBuf, nThisCols, nThisLines, + eDT, nBandCount, NULL, + 0, 0, 0, NULL ); + + nBlocksDone ++; + if( eErr == CE_None + && !pfnProgress( nBlocksDone / (double)nTotalBlocks, + NULL, pProgressData ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( pSwathBuf ); + + return eErr; +} + + +/************************************************************************/ +/* GDALRasterBandCopyWholeRaster() */ +/************************************************************************/ + +/** + * \brief Copy all raster band raster data. + * + * This function copies the complete raster contents of one band to + * another similarly configured band. The source and destination + * bands must have the same width and height. The bands do not have + * to have the same data type. + * + * It implements efficient copying, in particular "chunking" the copy in + * substantial blocks. + * + * Currently the only papszOptions value supported is : "COMPRESSED=YES" to + * force alignment on target dataset block sizes to achieve best compression. + * More options may be supported in the future. + * + * @param hSrcBand the source band + * @param hDstBand the destination band + * @param papszOptions transfer hints in "StringList" Name=Value format. + * @param pfnProgress progress reporting function. + * @param pProgressData callback data for progress function. + * + * @return CE_None on success, or CE_Failure on failure. + */ + +CPLErr CPL_STDCALL GDALRasterBandCopyWholeRaster( + GDALRasterBandH hSrcBand, GDALRasterBandH hDstBand, char **papszOptions, + GDALProgressFunc pfnProgress, void *pProgressData ) + +{ + VALIDATE_POINTER1( hSrcBand, "GDALRasterBandCopyWholeRaster", CE_Failure ); + VALIDATE_POINTER1( hDstBand, "GDALRasterBandCopyWholeRaster", CE_Failure ); + + GDALRasterBand *poSrcBand = (GDALRasterBand *) hSrcBand; + GDALRasterBand *poDstBand = (GDALRasterBand *) hDstBand; + CPLErr eErr = CE_None; + + if( pfnProgress == NULL ) + pfnProgress = GDALDummyProgress; + +/* -------------------------------------------------------------------- */ +/* Confirm the datasets match in size and band counts. */ +/* -------------------------------------------------------------------- */ + int nXSize = poSrcBand->GetXSize(), + nYSize = poSrcBand->GetYSize(); + + if( poDstBand->GetXSize() != nXSize + || poDstBand->GetYSize() != nYSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Input and output band sizes do not\n" + "match in GDALRasterBandCopyWholeRaster()" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Report preliminary (0) progress. */ +/* -------------------------------------------------------------------- */ + if( !pfnProgress( 0.0, NULL, pProgressData ) ) + { + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + return CE_Failure; + } + + GDALDataType eDT = poDstBand->GetRasterDataType(); + + /* If the destination is compressed, we must try to write blocks just once, to save */ + /* disk space (GTiff case for example), and to avoid data loss (JPEG compression for example) */ + int bDstIsCompressed = FALSE; + const char* pszDstCompressed= CSLFetchNameValue( papszOptions, "COMPRESSED" ); + if (pszDstCompressed != NULL && CSLTestBoolean(pszDstCompressed)) + bDstIsCompressed = TRUE; + +/* -------------------------------------------------------------------- */ +/* What will our swath size be? */ +/* -------------------------------------------------------------------- */ + + int nSwathCols, nSwathLines; + GDALCopyWholeRasterGetSwathSize(poSrcBand, + poDstBand, + 1, + bDstIsCompressed, FALSE, + &nSwathCols, &nSwathLines); + + int nPixelSize = (GDALGetDataTypeSize(eDT) / 8); + + void *pSwathBuf = VSIMalloc3(nSwathCols, nSwathLines, nPixelSize ); + if( pSwathBuf == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Failed to allocate %d*%d*%d byte swath buffer in\n" + "GDALRasterBandCopyWholeRaster()", + nSwathCols, nSwathLines, nPixelSize ); + return CE_Failure; + } + + CPLDebug( "GDAL", + "GDALRasterBandCopyWholeRaster(): %d*%d swaths", + nSwathCols, nSwathLines ); + +/* ==================================================================== */ +/* Band oriented (uninterleaved) case. */ +/* ==================================================================== */ + + int iX, iY; + + for( iY = 0; iY < nYSize && eErr == CE_None; iY += nSwathLines ) + { + int nThisLines = nSwathLines; + + if( iY + nThisLines > nYSize ) + nThisLines = nYSize - iY; + + for( iX = 0; iX < nXSize && eErr == CE_None; iX += nSwathCols ) + { + int nThisCols = nSwathCols; + + if( iX + nThisCols > nXSize ) + nThisCols = nXSize - iX; + + eErr = poSrcBand->RasterIO( GF_Read, + iX, iY, nThisCols, nThisLines, + pSwathBuf, nThisCols, nThisLines, + eDT, 0, 0, NULL ); + + if( eErr == CE_None ) + eErr = poDstBand->RasterIO( GF_Write, + iX, iY, nThisCols, nThisLines, + pSwathBuf, nThisCols, nThisLines, + eDT, 0, 0, NULL ); + + if( eErr == CE_None + && !pfnProgress( + (iY+nThisLines) / (float) (nYSize), + NULL, pProgressData ) ) + { + eErr = CE_Failure; + CPLError( CE_Failure, CPLE_UserInterrupt, + "User terminated CreateCopy()" ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( pSwathBuf ); + + return eErr; +} + + +/************************************************************************/ +/* GDALCopyRasterIOExtraArg () */ +/************************************************************************/ + +void GDALCopyRasterIOExtraArg(GDALRasterIOExtraArg* psDestArg, + GDALRasterIOExtraArg* psSrcArg) +{ + INIT_RASTERIO_EXTRA_ARG(*psDestArg); + if( psSrcArg ) + { + psDestArg->eResampleAlg = psSrcArg->eResampleAlg; + psDestArg->pfnProgress = psSrcArg->pfnProgress; + psDestArg->pProgressData = psSrcArg->pProgressData; + psDestArg->bFloatingPointWindowValidity = psSrcArg->bFloatingPointWindowValidity; + if( psSrcArg->bFloatingPointWindowValidity ) + { + psDestArg->dfXOff = psSrcArg->dfXOff; + psDestArg->dfYOff = psSrcArg->dfYOff; + psDestArg->dfXSize = psSrcArg->dfXSize; + psDestArg->dfYSize = psSrcArg->dfYSize; + } + } +} diff --git a/bazaar/plugin/gdal/gdal.cpp b/bazaar/plugin/gdal/gdal.cpp new file mode 100644 index 000000000..d7755111a --- /dev/null +++ b/bazaar/plugin/gdal/gdal.cpp @@ -0,0 +1,444 @@ +#include "gdal.h" + +#define uint8 tif_uint8 +#define uint16 tif_uint16 +#define int16 tif_int16 +#define int32 tif_int32 +#define uint32 tif_uint32 + +#include +#include + +#undef uint8 +#undef uint16 +#undef int16 +#undef int32 +#undef uint32 + +/* +GDALDriver *CreateGTIFFDriver(); + +extern "C" TIFF* XTIFFOpen(const char* name, const char* mode) +{ + return TIFFFileStreamOpen(name, mode); +} + +GLOBAL_VARP(One, GTIFFDriver, = CreateGTIFFDriver()); + +EXITBLOCK { + VSICleanupFileManager(); +} +*/ +Gdal::Block::Block(Size size_, GDALDataType type_, byte pixel_bytes_) +: size(size_) +, type(type_) +, pixel_bytes(pixel_bytes_) +{ + bytes.Alloc(pixel_bytes * size.cx * size.cy); +} + +#define CONVERT_LOOP(type, src_convert) \ + { \ + const type *src = (const type *)raw_src; \ + const type *end = src + count; \ + for(; src < end; src++, out++) \ + *out = (src_convert); \ + break; \ + } + + +void Gdal::Block::GetInt(int *out, int x, int y, int count) const +{ + ASSERT(x >= 0 && x + count <= size.cx); + ASSERT(y >= 0 && y < size.cy); + int offset = (x + y * size.cx) * pixel_bytes; + const byte *raw_src = &bytes[offset]; + switch(type) { + case GDT_Byte: CONVERT_LOOP(byte, *src); + case GDT_UInt16: CONVERT_LOOP(uint16, *src); + case GDT_Int16: CONVERT_LOOP(int16, *src); + case GDT_UInt32: CONVERT_LOOP(uint32, (int)*src); + case GDT_Int32: CONVERT_LOOP(int32, (int)*src); + case GDT_Float32: CONVERT_LOOP(float, fround((double)*src)); + case GDT_Float64: CONVERT_LOOP(double, fround(*src)); + case GDT_CInt16: CONVERT_LOOP(Point16, src->x); + case GDT_CInt32: CONVERT_LOOP(Point, src->x); + case GDT_CFloat32: CONVERT_LOOP(Point_, fround((double)src->x)); + case GDT_CFloat64: CONVERT_LOOP(Pointf, fround(src->x)); + default: { + Fill(out, out + count, (int)Null); + RLOG("unsupported GDAL data type: " << (int)type); + NEVER(); + break; + } + } +} + +void Gdal::Block::GetDouble(double *out, int x, int y, int count) const +{ + ASSERT(x >= 0 && x + count <= size.cx); + ASSERT(y >= 0 && y < size.cy); + int offset = (x + y * size.cx) * pixel_bytes; + const byte *raw_src = &bytes[offset]; + switch(type) { + case GDT_Byte: CONVERT_LOOP(byte, *src); + case GDT_UInt16: CONVERT_LOOP(uint16, *src); + case GDT_Int16: CONVERT_LOOP(int16, *src); + case GDT_UInt32: CONVERT_LOOP(uint32, (int)*src); + case GDT_Int32: CONVERT_LOOP(int32, (int)*src); + case GDT_Float32: CONVERT_LOOP(float, (double)*src); + case GDT_Float64: CONVERT_LOOP(double, *src); + case GDT_CInt16: CONVERT_LOOP(Point16, src->x); + case GDT_CInt32: CONVERT_LOOP(Point, src->x); + case GDT_CFloat32: CONVERT_LOOP(Point_, (double)src->x); + case GDT_CFloat64: CONVERT_LOOP(Pointf, src->x); + default: { + Fill(out, out + count, (double)Null); + RLOG("unsupported GDAL data type: " << (int)type); + NEVER(); + break; + } + } +} + +Gdal::Band::Band(Gdal& owner_, GDALRasterBand *band_) +: owner(owner_), band(band_) +{ + size.cx = band->GetXSize(); + size.cy = band->GetYSize(); + band->GetBlockSize(&block_size.cx, &block_size.cy); + block_count = idivceil(size, block_size); + type = band->GetRasterDataType(); + pixel_bytes = (GDALGetDataTypeSize(type) + 7) >> 3; + complex = GDALDataTypeIsComplex(type); + int nodata_flag; + nodata_value = band->GetNoDataValue(&nodata_flag); + is_nodata = !!nodata_flag; + is_stat = false; + nodata_int = fround(nodata_value); + blocks.Alloc(block_count.cx * block_count.cy); +} + +double Gdal::Band::GetMinimum() const +{ + int success; + double min = band->GetMinimum(&success); + if(success) + return min; + CalcStatistics(); + return stat_min; +} + +double Gdal::Band::GetMaximum() const +{ + int success; + double max = band->GetMaximum(&success); + if(success) + return max; + CalcStatistics(); + return stat_max; +} + +void Gdal::Band::GetInt(int *out, int x, int y, int count) const +{ + int y_in_block = y % block_size.cy; + int block_y = y / block_size.cy; + int block_offset = block_y * block_count.cx; + int block_x_min = x / block_size.cx; + int block_x_max = (x + count - 1) / block_size.cx; + int x_in_block = x - block_size.cx * block_x_min; + for(int block_x = block_x_min; block_x <= block_x_max; block_x++) { + int fill_count = min(count, block_size.cx - x_in_block); + int block_index = block_x + block_offset; + if(!blocks[block_index]) + LoadBlock(x, block_y); + if(blocks[block_index]) { + blocks[block_index]->GetInt(out, x_in_block, y_in_block, fill_count); + if(is_nodata) { + for(int *p = out, *e = out + fill_count; p < e; p++) + if(*p == nodata_int) + *p = Null; + } + } + else + Fill(out, out + fill_count, (int)Null); + x_in_block = 0; + count -= fill_count; + x += fill_count; + } +} + +void Gdal::Band::GetDouble(double *out, int x, int y, int count) const +{ + int y_in_block = y % block_size.cy; + int block_y = y / block_size.cy; + int block_offset = block_y * block_count.cx; + int block_x_min = x / block_size.cx; + int block_x_max = (x + count - 1) / block_size.cx; + int x_in_block = x - block_size.cx * block_x_min; + for(int block_x = block_x_min; block_x <= block_x_max; block_x++) { + int fill_count = min(count, block_size.cx - x_in_block); + int block_index = block_x + block_offset; + if(!blocks[block_index]) + LoadBlock(block_x, block_y); + if(blocks[block_index]) { + blocks[block_index]->GetDouble(out, x_in_block, y_in_block, fill_count); + if(is_nodata) { + for(double *p = out, *e = out + fill_count; p < e; p++) + if(*p == nodata_value) + *p = Null; + } + } + else + Fill(out, out + fill_count, (double)Null); + x_in_block = 0; + count -= fill_count; + out += fill_count; + } +} + +int Gdal::Band::GetInt(int x, int y) const +{ + int out; + GetInt(&out, x, y, 1); + return out; +} + +double Gdal::Band::GetDouble(int x, int y) const +{ + double out; + GetDouble(&out, x, y, 1); + return out; +} + +double Gdal::Band::GetIntAt(Pointf pt) const +{ + Pointf src = pt * owner.GetInverseTransform(); + int x = ffloor(src.x); + int y = ffloor(src.y); + if(x < 0 || x >= size.cx || y < 0 || y >= size.cy) + return Null; + return GetInt(x, y); +} + +double Gdal::Band::GetDoubleAt(Pointf pt) const +{ + Pointf src = pt * owner.GetInverseTransform(); + int x = ffloor(src.x); + int y = ffloor(src.y); + if(x < 0 || x >= size.cx || y < 0 || y >= size.cy) + return Null; + return GetDouble(x, y); +} + +void Gdal::Band::LoadBlock(int column, int row) const +{ + ASSERT(column >= 0 && column < block_count.cx); + ASSERT(row >= 0 && row < block_count.cy); + int block_index = column + row * block_count.cx; + if(blocks[block_index]) + return; + Point block_offset(block_size.cx * column, block_size.cy * row); + One new_block = new Block(block_size, type, pixel_bytes); + band->ReadBlock(column, row, new_block->bytes); + blocks[block_index] = pick(new_block); +} + +void Gdal::Band::CalcStatistics() const +{ + if(is_stat) + return; + is_stat = true; + stat_min = stat_max = Null; + bool done_stat = false; + Buffer row(block_size.cx); + for(int by = 0; by < block_count.cy; by++) + for(int bx = 0; bx < block_count.cx; bx++) { + LoadBlock(bx, by); + Size valid_size = min(block_size, size - Size(bx, by) * block_size); + const One& block = blocks[bx + by * block_count.cx]; + if(block) + for(int r = 0; r < valid_size.cy; r++) { + block->GetDouble(row, 0, r, valid_size.cx); + for(const double *p = row, *e = p + valid_size.cx; p < e; p++) + if(!done_stat) { + stat_min = stat_max = *p; + done_stat = true; + } + else { + if(*p < stat_min) stat_min = *p; + if(*p > stat_max) stat_max = *p; + } + } + } +} + +Gdal::Gdal() +{ + dataset = NULL; +} + +Gdal::~Gdal() +{ + Close(); +} + +bool Gdal::Open(const char *fn) +{ + ONCELOCK { + MemoryIgnoreLeaksBegin(); + +// GDALRegister_GDB(); + GDALRegister_GTiff(); + GDALRegister_GXF(); +// GDALRegister_OGDI(); + GDALRegister_HFA(); + GDALRegister_AAIGrid(); + GDALRegister_AIGrid(); +// GDALRegister_AIGrid2(); + GDALRegister_CEOS(); + GDALRegister_SAR_CEOS(); +// GDALRegister_SDTS(); + GDALRegister_ELAS(); + GDALRegister_EHdr(); + GDALRegister_GenBin(); + GDALRegister_PAux(); + GDALRegister_ENVI(); + GDALRegister_DOQ1(); + GDALRegister_DOQ2(); + GDALRegister_DTED(); + GDALRegister_MFF(); + GDALRegister_HKV(); +// GDALRegister_PNG(); + GDALRegister_JPEG(); +// GDALRegister_JPEG2000(); +// GDALRegister_JP2KAK(); + GDALRegister_MEM(); + GDALRegister_JDEM(); +// GDALRegister_GRASS(); + GDALRegister_PNM(); + GDALRegister_GIF(); + GDALRegister_Envisat(); +// GDALRegister_FITS(); +// GDALRegister_ECW(); +// GDALRegister_JP2ECW(); +// GDALRegister_ECW_JP2ECW(); + GDALRegister_FujiBAS(); + GDALRegister_FIT(); + GDALRegister_VRT(); + GDALRegister_USGSDEM(); + GDALRegister_FAST(); +// GDALRegister_HDF4(); +// GDALRegister_HDF4Image(); + GDALRegister_L1B(); +// GDALRegister_LDF(); + GDALRegister_BSB(); + GDALRegister_XPM(); + GDALRegister_BMP(); + GDALRegister_GSC(); + GDALRegister_NITF(); + GDALRegister_RPFTOC(); +// GDALRegister_MrSID(); +// GDALRegister_PCIDSK(); + GDALRegister_BT(); +// GDALRegister_DODS(); +// GDALRegister_GMT(); +// GDALRegister_netCDF(); + GDALRegister_LAN(); + GDALRegister_CPG(); + GDALRegister_AirSAR(); + GDALRegister_RS2(); + GDALRegister_ILWIS(); + GDALRegister_PCRaster(); + GDALRegister_IDA(); + GDALRegister_NDF(); + GDALRegister_RMF(); +// GDALRegister_HDF5(); +// GDALRegister_HDF5Image(); +// GDALRegister_MSGN(); +// GDALRegister_MSG(); + GDALRegister_RIK(); + GDALRegister_Leveller(); + GDALRegister_SGI(); + GDALRegister_SRTMHGT(); + GDALRegister_DIPEx(); + GDALRegister_ISIS3(); + GDALRegister_ISIS2(); + GDALRegister_PDS(); +// GDALRegister_IDRISI(); + GDALRegister_Terragen(); + GDALRegister_WCS(); +// GDALRegister_WMS(); + GDALRegister_HTTP(); +// GDALRegister_SDE(); + GDALRegister_GSAG(); + GDALRegister_GSBG(); + GDALRegister_GS7BG(); + GDALRegister_GRIB(); + GDALRegister_INGR(); + GDALRegister_ERS(); + GDALRegister_PALSARJaxa(); + GDALRegister_DIMAP(); + GDALRegister_GFF(); + GDALRegister_COSAR(); + GDALRegister_TSX(); +// GDALRegister_ADRG(); + GDALRegister_COASP(); + GDALRegister_BLX(); + GDALRegister_LCP(); +// GDALRegister_PGCHIP(); +// GDALRegister_TMS(); + GDALRegister_EIR(); +// GDALRegister_GEOR(); + + MemoryIgnoreLeaksEnd(); + } + + Close(); + + MemoryIgnoreLeaksBegin(); + dataset = (GDALDataset *) GDALOpen(fn, GA_ReadOnly); + MemoryIgnoreLeaksEnd(); + if(!dataset) + return false; + int nbands = dataset->GetRasterCount(); + for(int i = 1; i <= nbands; i++) + bands.Add(new Band(*this, dataset->GetRasterBand(i))); + double affine[6]; + dataset->GetGeoTransform(affine); + transform.a.x = affine[0]; + transform.x.x = affine[1]; + transform.y.x = affine[2]; + transform.a.y = affine[3]; + transform.x.y = affine[4]; + transform.y.y = affine[5]; + if(fabs(Determinant(transform)) <= 1e-10) + inverse_transform = Matrixf_1(); + else + inverse_transform = MatrixfInverse(transform); + return true; +} + +void Gdal::Close() +{ + bands.Clear(); + if(dataset) { + GDALClose((GDALDatasetH) dataset); + dataset = NULL; + } +} + +String Gdal::GetProjection() const +{ + return dataset->GetProjectionRef(); +} + +Size Gdal::GetPixelSize() const +{ + return Size(dataset->GetRasterXSize(), dataset->GetRasterYSize()); +} + +Rectf Gdal::GetExtent() const +{ + return Rectf(Sizef(GetPixelSize())) * GetTransform(); +} diff --git a/bazaar/plugin/gdal/gdal.h b/bazaar/plugin/gdal/gdal.h new file mode 100644 index 000000000..37d605046 --- /dev/null +++ b/bazaar/plugin/gdal/gdal.h @@ -0,0 +1,104 @@ +#ifndef _gdal_gdal_h_ +#define _gdal_gdal_h_ + +#include +#include + +#include +#include + +class GDALDriver; +class GDALDataset; +class GDALRasterBand; + +using namespace Upp; + +class Gdal { +public: + class Block { + public: + Block(Size size, GDALDataType type, byte pixel_bytes); + + void GetInt(int *out, int x, int y, int count) const; + void GetDouble(double *out, int x, int y, int count) const; + + public: + Size size; + GDALDataType type; + byte pixel_bytes; + Buffer bytes; + }; + + class Band { + public: + Band(Gdal& owner, GDALRasterBand *band); + + Size GetSize() const { return size; } + Size GetBlockSize() const { return block_size; } + + GDALDataType GetType() const { return type; } + int GetBytes() const { return pixel_bytes; } + bool IsComplex() const { return complex; } + + double GetMinimum() const; + double GetMaximum() const; + + void GetInt(int *out, int x, int y, int count) const; + void GetDouble(double *out, int x, int y, int count) const; + int GetInt(int x, int y) const; + double GetDouble(int x, int y) const; + + double GetIntAt(Pointf pt) const; + double GetDoubleAt(Pointf pt) const; + + protected: + void LoadBlock(int column, int row) const; + void CalcStatistics() const; + + private: + Gdal& owner; + GDALRasterBand *band; + Size size; + Size block_size; + GDALDataType type; + bool complex; + bool is_nodata; + mutable bool is_stat; + mutable double stat_min; + mutable double stat_max; + byte pixel_bytes; + Size block_count; + int nodata_int; + double nodata_value; + mutable Buffer< One > blocks; + }; + +public: + Gdal(); + ~Gdal(); + + bool Open(const char *filename); + bool IsOpen() const { return dataset; } + String GetFileName() const { return filename; } + void Close(); + + String GetProjection() const; + + Size GetPixelSize() const; + Rectf GetExtent() const; + const Matrixf& GetTransform() const { return transform; } + const Matrixf& GetInverseTransform() const { return inverse_transform; } + + int GetBandCount() const { return bands.GetCount(); } + const Band& GetBand(int band) const { return bands[band]; } + const Band& operator [] (int band) const { return bands[band]; } + +private: + String filename; + GDALDataset *dataset; + Array bands; + Matrixf transform; + Matrixf inverse_transform; +}; + +#endif diff --git a/bazaar/plugin/gdal/gdal.upp b/bazaar/plugin/gdal/gdal.upp new file mode 100644 index 000000000..7e545d78e --- /dev/null +++ b/bazaar/plugin/gdal/gdal.upp @@ -0,0 +1,15 @@ +noblitz; + +options(BUILDER_OPTION) NOWARNINGS; + +include + gcore, + port, + ogr; + +file + gdal.h, + gdal.cpp, + import.ext, + cpl_config.h; + diff --git a/bazaar/plugin/gdal/import.ext b/bazaar/plugin/gdal/import.ext new file mode 100644 index 000000000..9d3941dc6 --- /dev/null +++ b/bazaar/plugin/gdal/import.ext @@ -0,0 +1,96 @@ +files *.cpp *.c; + +exclude + port/vsipreload.cpp + ogr/generate_encoding_table.c + alg/gdal_nrgcrs.c + */sqlite/* + */sqlite3/* + */pg/* + */sde/* + */sosi/* + */tiger/* + */vfk/* + */shape/* + */libkml/* + */dgn/* + */arcobjects/* + */dods/* + */dwg/* + */filegdb/* + */fme/* + */gpkg/* + */grass/* + */idb/* + */ingres/* + */ili/* + */mdb/* + */mysql/* + */kml/* + */nas/* + */idrisi/* + */xlsx/* + */xls/* + */oci/* + */ogdi/* + */ods/* + */gpkg/* + */osm/* + port/cpl_vsil_simple.cpp + port/cpl_win32ce_api.cpp + frmts/bgp/* + frmts/dds/* + frmts/ecw/* + frmts/epsilon/* + frmts/fits/* + frmts/georaster/* + frmts/gta/* + frmts/hdf4/* + frmts/hdf5/* + frmts/iso8211/* + frmts/jp2kak/* + frmts/jpipkak/* + frmts/jpeg2000/* + frmts/kea/* + frmts/jpegls/* + frmts/mrsid_lidar/* + frmts/msgn/* + frmts/mrsid/* + frmts/bpg/* + frmts/openjpeg/* + frmts/msg/* + frmts/netcdf/* + frmts/msg/* + frmts/pgchip/* + frmts/postgisraster/* + frmts/rasdaman/* + frmts/wms/* + frmts/webp/* + frmts/pdf/* + frmts/pcidsk/* + + */ogr_capi_test.c* + */test_geo_utils.c* + */s57dump.c* + */ntfdump.c* + */jp2dump.c* + */sdts2shp.c* + */nitfdump.c* + */hfatest.c* + */envisat_dump.c* + */dumpgeo.c* + */dted_test.c* + */ceostest.c* + */bsb2raw.c* + */aitest.c* + + */cpl_recode_iconv.c* + */ogrjmllayer.c* + */ogrgeojsonreader.c* + */ograpispy.c* + */nitf_gcprpc.c* + */gt_jpeg_copy.c* + */gdalrasterfpolygonenumerator.c* +; + +includes *.h *.hpp; diff --git a/bazaar/plugin/gdal/init b/bazaar/plugin/gdal/init new file mode 100644 index 000000000..62f59f0c8 --- /dev/null +++ b/bazaar/plugin/gdal/init @@ -0,0 +1,3 @@ +#ifndef _gdal_icpp_init_stub +#define _gdal_icpp_init_stub +#endif diff --git a/bazaar/plugin/gdal/ogr/Doxyfile b/bazaar/plugin/gdal/ogr/Doxyfile new file mode 100644 index 000000000..f47028bd9 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/Doxyfile @@ -0,0 +1,1241 @@ +# Doxyfile 1.4.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = OGR + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. + +SHOW_DIRECTORIES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the progam writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . \ + ogrsf_frmts \ + ogrsf_frmts/generic \ + ogrsf_frmts/geojson/ogrgeojsonwriter.cpp \ + ogrsf_frmts/kml/ogr2kmlgeometry.cpp \ + ../port + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm + +FILE_PATTERNS = *.h \ + *.cpp \ + *.c \ + *.dox + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = . + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. +# +# http://trac.osgeo.org/gdal/ticket/2723 + +REFERENCES_LINK_SOURCE = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = ../doc/gdal_footer.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = HAVE_DLFCN_H \ + CPL_DLL \ + CPL_C_START \ + CPL_C_END \ + __cplusplus \ + DOXYGEN_SKIP \ + HAVE_CURL + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = CPL_PRINT_FUNC_FORMAT + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/local/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/bazaar/plugin/gdal/ogr/GNUmakefile b/bazaar/plugin/gdal/ogr/GNUmakefile new file mode 100644 index 000000000..d4d4045ae --- /dev/null +++ b/bazaar/plugin/gdal/ogr/GNUmakefile @@ -0,0 +1,92 @@ + +include ../GDALmake.opt + +include ./file.lst + +INST_H_FILES = ogr_core.h ogr_feature.h ogr_geometry.h ogr_p.h \ + ogr_spatialref.h ogr_srs_api.h ogrsf_frmts/ogrsf_frmts.h \ + ogr_featurestyle.h ogr_api.h ogr_geocoding.h + +ifeq ($(HAVE_GEOS),yes) +CPPFLAGS := -DHAVE_GEOS=1 $(GEOS_CFLAGS) $(CPPFLAGS) +endif + +ifeq ($(OGR_ENABLED),yes) +CXXFLAGS := -DHAVE_MITAB $(CXXFLAGS) +endif + +ifeq ($(HAVE_EXPAT),yes) +CPPFLAGS := -DHAVE_EXPAT $(EXPAT_INCLUDE) $(CPPFLAGS) +endif + +ifeq ($(LIBZ_SETTING),internal) +ZLIB_XTRA_OPT = -I../frmts/zlib +else +ZLIB_XTRA_OPT = +endif + +CPPFLAGS := -Iogrsf_frmts -Iogrsf_frmts/mem -I. $(PROJ_INCLUDE) $(PROJ_FLAGS) $(CPPFLAGS) $(ZLIB_XTRA_OPT) + +default: lib + +all: lib + +clean: + rm -f html/* + (cd ogrsf_frmts; $(MAKE) clean) + $(RM) *.o + +very-clean: clean + rm -rf html rtf + +lib: sublibs $(OBJ:.o=.$(OBJ_EXT)) + +$(OBJ): ogr_feature.h ogr_geometry.h swq.h + +obj: $(OBJ) + +ifeq ($(OGR_ENABLED),yes) + +sublibs: + (cd ogrsf_frmts; $(MAKE)) + +else + +sublibs: + (cd ogrsf_frmts/mitab; $(MAKE)) + (cd ogrsf_frmts/generic; $(MAKE)) + +endif + +docs: + echo "This target does not exist anymore. Use top-level" + +install-docs: + echo "This target does not exist anymore. Use top-level" + +gdalso: $(GDAL_SLIB) + +$(GDAL_SLIB): + (cd ..; $(MAKE) check-lib) + +web-update: docs + echo "This target does not exist anymore. Use top-level" + +install: + for f in $(INST_H_FILES) ; \ + do $(INSTALL_DATA) $$f $(DESTDIR)$(INST_INCLUDE) ; \ + done + +# The sed substition below workarounds a bug with gcc 4.1 -O2 (checked on 64bit platforms) +# that produces buggy compiled code. +# Seen on gcc 4.1.2-27ubuntu1 (Ubuntu 10.04) (not the default compiler) and gcc-4.1.2-48.el5 (CentOS 5.5) +# (default compiler...) +# The memset isn't necessary at all with a non-buggy compiler, but I've found +# that it helps gcc 4.1 generating correct code here... +parser: + bison -p swq -d -oswq_parser.cpp swq_parser.y + sed "s/yytype_int16 yyssa\[YYINITDEPTH\];/yytype_int16 yyssa[YYINITDEPTH]; \/\* workaround bug with gcc 4.1 -O2 \*\/ memset(yyssa, 0, sizeof(yyssa));/" < swq_parser.cpp > swq_parser.cpp.tmp + mv swq_parser.cpp.tmp swq_parser.cpp + +osr_cs_wkt_parser: + bison --no-lines -p osr_cs_wkt_ -d -oosr_cs_wkt_parser.c osr_cs_wkt_grammar.y diff --git a/bazaar/plugin/gdal/ogr/file.lst b/bazaar/plugin/gdal/ogr/file.lst new file mode 100644 index 000000000..d9db14e6e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/file.lst @@ -0,0 +1,57 @@ +OBJ = ogrgeometryfactory.o \ + ogrpoint.o \ + ogrcurve.o \ + ogrlinestring.o \ + ogrlinearring.o \ + ogrpolygon.o \ + ogrutils.o \ + ogrgeometry.o \ + ogrgeometrycollection.o \ + ogrmultipolygon.o \ + ogrsurface.o \ + ogrmultipoint.o \ + ogrmultilinestring.o \ + ogrcircularstring.o \ + ogrcompoundcurve.o \ + ogrcurvepolygon.o \ + ogrcurvecollection.o \ + ogrmulticurve.o \ + ogrmultisurface.o \ + ogr_api.o \ + ogrfeature.o \ + ogrfeaturedefn.o \ + ogrfeaturequery.o\ + ogrfeaturestyle.o \ + ogrfielddefn.o \ + ogrspatialreference.o \ + ogr_srsnode.o \ + ogr_srs_proj4.o \ + ogr_fromepsg.o \ + ogrct.o \ + ogr_opt.o \ + ogr_srs_esri.o \ + ogr_srs_pci.o \ + ogr_srs_usgs.o \ + ogr_srs_dict.o \ + ogr_srs_panorama.o \ + ogr_srs_ozi.o \ + ogr_srs_erm.o \ + swq.o \ + swq_expr_node.o \ + swq_parser.o \ + swq_select.o \ + swq_op_registrar.o \ + swq_op_general.o \ + ogr_srs_validate.o \ + ogr_srs_xml.o \ + ograssemblepolygon.o \ + ogr2gmlgeometry.o \ + gml2ogrgeometry.o \ + ogr_expat.o \ + ogrpgeogeometry.o \ + ogrgeomediageometry.o \ + ogr_geocoding.o \ + osr_cs_wkt.o \ + osr_cs_wkt_parser.o \ + ogrgeomfielddefn.o \ + ograpispy.o diff --git a/bazaar/plugin/gdal/ogr/generate_encoding_table.c b/bazaar/plugin/gdal/ogr/generate_encoding_table.c new file mode 100644 index 000000000..8c8b09244 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/generate_encoding_table.c @@ -0,0 +1,222 @@ +/****************************************************************************** + * $Id: generate_encoding_table.c 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OGR + * Purpose: Generate a mapping table from a 1-byte encoding to unicode, + * for ogr_expat.cpp + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include +#include +#include +#include + +static unsigned utf8decode(const char* p, const char* end, int* len) +{ + unsigned char c = *(unsigned char*)p; + if (c < 0x80) { + *len = 1; + return c; +#if ERRORS_TO_CP1252 + } else if (c < 0xa0) { + *len = 1; + return cp1252[c-0x80]; +#endif + } else if (c < 0xc2) { + goto FAIL; + } + if (p+1 >= end || (p[1]&0xc0) != 0x80) goto FAIL; + if (c < 0xe0) { + *len = 2; + return + ((p[0] & 0x1f) << 6) + + ((p[1] & 0x3f)); + } else if (c == 0xe0) { + if (((unsigned char*)p)[1] < 0xa0) goto FAIL; + goto UTF8_3; +#if STRICT_RFC3629 + } else if (c == 0xed) { + // RFC 3629 says surrogate chars are illegal. + if (((unsigned char*)p)[1] >= 0xa0) goto FAIL; + goto UTF8_3; + } else if (c == 0xef) { + // 0xfffe and 0xffff are also illegal characters + if (((unsigned char*)p)[1]==0xbf && + ((unsigned char*)p)[2]>=0xbe) goto FAIL; + goto UTF8_3; +#endif + } else if (c < 0xf0) { + UTF8_3: + if (p+2 >= end || (p[2]&0xc0) != 0x80) goto FAIL; + *len = 3; + return + ((p[0] & 0x0f) << 12) + + ((p[1] & 0x3f) << 6) + + ((p[2] & 0x3f)); + } else if (c == 0xf0) { + if (((unsigned char*)p)[1] < 0x90) goto FAIL; + goto UTF8_4; + } else if (c < 0xf4) { + UTF8_4: + if (p+3 >= end || (p[2]&0xc0) != 0x80 || (p[3]&0xc0) != 0x80) goto FAIL; + *len = 4; +#if STRICT_RFC3629 + // RFC 3629 says all codes ending in fffe or ffff are illegal: + if ((p[1]&0xf)==0xf && + ((unsigned char*)p)[2] == 0xbf && + ((unsigned char*)p)[3] >= 0xbe) goto FAIL; +#endif + return + ((p[0] & 0x07) << 18) + + ((p[1] & 0x3f) << 12) + + ((p[2] & 0x3f) << 6) + + ((p[3] & 0x3f)); + } else if (c == 0xf4) { + if (((unsigned char*)p)[1] > 0x8f) goto FAIL; // after 0x10ffff + goto UTF8_4; + } else { + FAIL: + *len = 1; +#if ERRORS_TO_ISO8859_1 + return c; +#else + return 0xfffd; // Unicode REPLACEMENT CHARACTER +#endif + } +} + +int main(int argc, char* argv[]) +{ + iconv_t sConv; + const char* pszSrcEncoding; + const char* pszDstEncoding = "UTF-8"; + int i; + int nLastIdentical = -1; + + if( argc != 2 ) + { + fprintf(stderr, "Usage: generate_encoding_table encoding_name\n"); + return 1; + } + + pszSrcEncoding = argv[1]; + + sConv = iconv_open( pszDstEncoding, pszSrcEncoding ); + + if ( sConv == (iconv_t)-1 ) + { + fprintf(stderr, + "Recode from %s to %s failed with the error: \"%s\".", + pszSrcEncoding, pszDstEncoding, strerror(errno) ); + return 1; + } + + for(i = 0; i < 256; i++) + { + char szSrcBuf[2] = {(char)i, 0}; + char szDstBuf[5] = {0,0,0,0,0}; + char *pszSrcBuf = szSrcBuf; + char *pszDstBuf = szDstBuf; + size_t nSrcLen = strlen( szSrcBuf ); + size_t nDstLen = sizeof(szDstBuf); + size_t nConverted = + iconv( sConv, &pszSrcBuf, &nSrcLen, &pszDstBuf, &nDstLen ); + + int nUnicode = -1; + if( nConverted == -1 ) + { + if ( errno == EILSEQ ) + { + /* fprintf(stderr, "EILSEQ for %d\n", i); */ + } + + else if ( errno == E2BIG ) + { + fprintf(stderr, "E2BIG for %d\n", i); + return 1; + } + else + { + fprintf(stderr, "other error for %d\n", i); + return 1; + } + } + else + { + int len; + nUnicode = utf8decode(szDstBuf, szDstBuf + strlen(szDstBuf), &len); + if( nUnicode == 0xfffd ) + nUnicode = -1; + } + + if( nLastIdentical >= 0 && i != nUnicode ) + { + if( nLastIdentical + 1 == i ) + printf("info->map[0x%02X] = 0x%02X;\n", nLastIdentical, nLastIdentical); + else + { + printf("for(i = 0x%02X; i < 0x%02X; i++)\n", nLastIdentical, i); + printf(" info->map[i] = i;\n"); + } + nLastIdentical = -1; + } + + if( nUnicode < 0 ) + printf("info->map[0x%02X] = -1;\n", i); + else if (nUnicode <= 0xFF ) + { + if( i == nUnicode ) + { + if( nLastIdentical < 0 ) + nLastIdentical = i; + } + else + printf("info->map[0x%02X] = 0x%02X;\n", i, nUnicode); + } + else if (nUnicode <= 0xFFFF ) + printf("info->map[0x%02X] = 0x%04X;\n", i, nUnicode); + else if (nUnicode <= 0xFFFFFF ) + printf("info->map[0x%02X] = 0x%06X;\n", i, nUnicode); + else + printf("info->map[0x%02X] = 0x%08X;\n", i, nUnicode); + } + + if( nLastIdentical >= 0 ) + { + if( nLastIdentical + 1 == i ) + printf("info->map[0x%02X] = 0x%02X;\n", nLastIdentical, nLastIdentical); + else + { + printf("for(i = 0x%02X; i < 0x%02X; i++)\n", nLastIdentical, i); + printf(" info->map[i] = i;\n"); + } + nLastIdentical = -1; + } + + iconv_close( sConv ); + + return 0; +} diff --git a/bazaar/plugin/gdal/ogr/gml2ogrgeometry.cpp b/bazaar/plugin/gdal/ogr/gml2ogrgeometry.cpp new file mode 100644 index 000000000..7ab4e002e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/gml2ogrgeometry.cpp @@ -0,0 +1,3404 @@ +/****************************************************************************** + * $Id: gml2ogrgeometry.cpp 28751 2015-03-20 15:21:49Z rouault $ + * + * Project: GML Reader + * Purpose: Code to translate between GML and OGR geometry forms. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2009-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ***************************************************************************** + * + * Independent Security Audit 2003/04/17 Andrey Kiselev: + * Completed audit of this module. All functions may be used without buffer + * overflows and stack corruptions with any kind of input data. + * + * Security Audit 2003/03/28 warmerda: + * Completed security audit. I believe that this module may be safely used + * to parse, arbitrary GML potentially provided by a hostile source without + * compromising the system. + * + */ + +#include "cpl_minixml.h" +#include "ogr_geometry.h" +#include "ogr_api.h" +#include "cpl_error.h" +#include "cpl_string.h" +#include +#include "ogr_p.h" +#include "ogrsf_frmts/xplane/ogr_xplane_geo_utils.h" + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + + +/************************************************************************/ +/* GMLGetCoordTokenPos() */ +/************************************************************************/ + +static const char* GMLGetCoordTokenPos(const char* pszStr, + const char** ppszNextToken) +{ + char ch; + while(TRUE) + { + ch = *pszStr; + if (ch == '\0') + { + *ppszNextToken = NULL; + return NULL; + } + else if (!(ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' || ch == ',')) + break; + pszStr ++; + } + + const char* pszToken = pszStr; + while((ch = *pszStr) != '\0') + { + if (ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' || ch == ',') + { + *ppszNextToken = pszStr; + return pszToken; + } + pszStr ++; + } + *ppszNextToken = NULL; + return pszToken; +} + +/************************************************************************/ +/* BareGMLElement() */ +/* */ +/* Returns the passed string with any namespace prefix */ +/* stripped off. */ +/************************************************************************/ + +static const char *BareGMLElement( const char *pszInput ) + +{ + const char *pszReturn; + + pszReturn = strchr( pszInput, ':' ); + if( pszReturn == NULL ) + pszReturn = pszInput; + else + pszReturn++; + + return pszReturn; +} + +/************************************************************************/ +/* FindBareXMLChild() */ +/* */ +/* Find a child node with the indicated "bare" name, that is */ +/* after any namespace qualifiers have been stripped off. */ +/************************************************************************/ + +static const CPLXMLNode *FindBareXMLChild( const CPLXMLNode *psParent, + const char *pszBareName ) + +{ + const CPLXMLNode *psCandidate = psParent->psChild; + + while( psCandidate != NULL ) + { + if( psCandidate->eType == CXT_Element + && EQUAL(BareGMLElement(psCandidate->pszValue), pszBareName) ) + return psCandidate; + + psCandidate = psCandidate->psNext; + } + + return NULL; +} + +/************************************************************************/ +/* GetElementText() */ +/************************************************************************/ + +static const char *GetElementText( const CPLXMLNode *psElement ) + +{ + if( psElement == NULL ) + return NULL; + + const CPLXMLNode *psChild = psElement->psChild; + + while( psChild != NULL ) + { + if( psChild->eType == CXT_Text ) + return psChild->pszValue; + + psChild = psChild->psNext; + } + + return NULL; +} + +/************************************************************************/ +/* GetChildElement() */ +/************************************************************************/ + +static const CPLXMLNode *GetChildElement( const CPLXMLNode *psElement ) + +{ + if( psElement == NULL ) + return NULL; + + const CPLXMLNode *psChild = psElement->psChild; + + while( psChild != NULL ) + { + if( psChild->eType == CXT_Element ) + return psChild; + + psChild = psChild->psNext; + } + + return NULL; +} + +/************************************************************************/ +/* GetElementOrientation() */ +/* Returns true for positive orientation. */ +/************************************************************************/ + +int GetElementOrientation( const CPLXMLNode *psElement ) +{ + if( psElement == NULL ) + return TRUE; + + const CPLXMLNode *psChild = psElement->psChild; + + while( psChild != NULL ) + { + if( psChild->eType == CXT_Attribute && + EQUAL(psChild->pszValue,"orientation") ) + return EQUAL(psChild->psChild->pszValue,"+"); + + psChild = psChild->psNext; + } + + return TRUE; +} + +/************************************************************************/ +/* AddPoint() */ +/* */ +/* Add a point to the passed geometry. */ +/************************************************************************/ + +static int AddPoint( OGRGeometry *poGeometry, + double dfX, double dfY, double dfZ, int nDimension ) + +{ + OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType()); + if( eType == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + if( !poPoint->IsEmpty() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "More than one coordinate for element."); + return FALSE; + } + + poPoint->setX( dfX ); + poPoint->setY( dfY ); + if( nDimension == 3 ) + poPoint->setZ( dfZ ); + + return TRUE; + } + + else if( eType == wkbLineString || + eType == wkbCircularString ) + { + if( nDimension == 3 ) + ((OGRSimpleCurve *) poGeometry)->addPoint( dfX, dfY, dfZ ); + else + ((OGRSimpleCurve *) poGeometry)->addPoint( dfX, dfY ); + + return TRUE; + } + + else + { + CPLAssert( FALSE ); + return FALSE; + } +} + +/************************************************************************/ +/* ParseGMLCoordinates() */ +/************************************************************************/ + +static int ParseGMLCoordinates( const CPLXMLNode *psGeomNode, OGRGeometry *poGeometry, + int nSRSDimension ) + +{ + const CPLXMLNode *psCoordinates = FindBareXMLChild( psGeomNode, "coordinates" ); + int iCoord = 0; + +/* -------------------------------------------------------------------- */ +/* Handle case. */ +/* Note that we don't do a strict validation, so we accept and */ +/* sometimes generate output whereas we should just reject it. */ +/* -------------------------------------------------------------------- */ + if( psCoordinates != NULL ) + { + const char *pszCoordString = GetElementText( psCoordinates ); + + const char *pszDecimal = CPLGetXMLValue( (CPLXMLNode*) psCoordinates, "decimal", NULL); + char chDecimal = '.'; + if( pszDecimal != NULL ) + { + if( strlen(pszDecimal) != 1 || (pszDecimal[0] >= '0' && pszDecimal[0] <= '9') ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Wrong value for decimal attribute"); + return FALSE; + } + chDecimal = pszDecimal[0]; + } + + const char *pszCS = CPLGetXMLValue( (CPLXMLNode*) psCoordinates, "cs", NULL); + char chCS = ','; + if( pszCS != NULL ) + { + if( strlen(pszCS) != 1 || (pszCS[0] >= '0' && pszCS[0] <= '9') ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Wrong value for cs attribute"); + return FALSE; + } + chCS = pszCS[0]; + } + const char *pszTS = CPLGetXMLValue( (CPLXMLNode*) psCoordinates, "ts", NULL); + char chTS = ' '; + if( pszTS != NULL ) + { + if( strlen(pszTS) != 1 || (pszTS[0] >= '0' && pszTS[0] <= '9') ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Wrong value for tes attribute"); + return FALSE; + } + chTS = pszTS[0]; + } + + if( pszCoordString == NULL ) + { + poGeometry->empty(); + return TRUE; + } + + while( *pszCoordString != '\0' ) + { + double dfX, dfY, dfZ = 0.0; + int nDimension = 2; + + // parse out 2 or 3 tuple. + if( chDecimal == '.' ) + dfX = OGRFastAtof( pszCoordString ); + else + dfX = CPLAtofDelim( pszCoordString, chDecimal) ; + while( *pszCoordString != '\0' + && *pszCoordString != chCS + && !isspace((unsigned char)*pszCoordString) ) + pszCoordString++; + + if( *pszCoordString == '\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Corrupt value." ); + return FALSE; + } + else if( chCS == ',' && pszCS == NULL && isspace((unsigned char)*pszCoordString) ) + { + /* In theory, the coordinates inside a coordinate tuple should be */ + /* separated by a comma. However it has been found in the wild */ + /* that the coordinates are in rare cases separated by a space, and the tuples by a comma */ + /* See https://52north.org/twiki/bin/view/Processing/WPS-IDWExtension-ObservationCollectionExample */ + /* or http://agisdemo.faa.gov/aixmServices/getAllFeaturesByLocatorId?locatorId=DFW */ + chCS = ' '; + chTS = ','; + } + + pszCoordString++; + if( chDecimal == '.' ) + dfY = OGRFastAtof( pszCoordString ); + else + dfY = CPLAtofDelim( pszCoordString, chDecimal) ; + while( *pszCoordString != '\0' + && *pszCoordString != chCS + && *pszCoordString != chTS + && !isspace((unsigned char)*pszCoordString) ) + pszCoordString++; + + if( *pszCoordString == chCS ) + { + pszCoordString++; + if( chDecimal == '.' ) + dfZ = OGRFastAtof( pszCoordString ); + else + dfZ = CPLAtofDelim( pszCoordString, chDecimal) ; + nDimension = 3; + while( *pszCoordString != '\0' + && *pszCoordString != chCS + && *pszCoordString != chTS + && !isspace((unsigned char)*pszCoordString) ) + pszCoordString++; + } + + if( *pszCoordString == chTS ) + { + pszCoordString++; + } + + while( isspace((unsigned char)*pszCoordString) ) + pszCoordString++; + + if( !AddPoint( poGeometry, dfX, dfY, dfZ, nDimension ) ) + return FALSE; + + iCoord++; + } + + return iCoord > 0; + } + +/* -------------------------------------------------------------------- */ +/* Is this a "pos"? GML 3 construct. */ +/* Parse if it exist a series of pos elements (this would allow */ +/* the correct parsing of gml3.1.1 geomtries such as linestring */ +/* defined with pos elements. */ +/* -------------------------------------------------------------------- */ + const CPLXMLNode *psPos; + + int bHasFoundPosElement = FALSE; + for( psPos = psGeomNode->psChild; + psPos != NULL; + psPos = psPos->psNext ) + { + if( psPos->eType != CXT_Element ) + continue; + + const char* pszSubElement = BareGMLElement(psPos->pszValue); + + if( EQUAL(pszSubElement, "pointProperty") ) + { + const CPLXMLNode *psPointPropertyIter; + for( psPointPropertyIter = psPos->psChild; + psPointPropertyIter != NULL; + psPointPropertyIter = psPointPropertyIter->psNext ) + { + if( psPointPropertyIter->eType != CXT_Element ) + continue; + + const char* pszBareElement = BareGMLElement(psPointPropertyIter->pszValue); + if (EQUAL(pszBareElement,"Point") || EQUAL(pszBareElement,"ElevatedPoint") ) + { + OGRPoint oPoint; + if( ParseGMLCoordinates( psPointPropertyIter, &oPoint, nSRSDimension ) ) + { + int bSuccess = AddPoint( poGeometry, oPoint.getX(), + oPoint.getY(), oPoint.getZ(), + oPoint.getCoordinateDimension() ); + if (bSuccess) + bHasFoundPosElement = TRUE; + else + return FALSE; + } + } + } + + if( psPos->psChild && psPos->psChild->eType == CXT_Attribute && + psPos->psChild->psNext == NULL && + strcmp(psPos->psChild->pszValue, "xlink:href") == 0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot resolve xlink:href='%s'. Try setting GML_SKIP_RESOLVE_ELEMS=NONE", + psPos->psChild->psChild->pszValue); + } + + continue; + } + + if( !EQUAL(pszSubElement,"pos") ) + continue; + + const char* pszPos = GetElementText( psPos ); + if (pszPos == NULL) + { + poGeometry->empty(); + return TRUE; + } + + const char* pszCur = pszPos; + const char* pszX = (pszCur != NULL) ? + GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; + const char* pszY = (pszCur != NULL) ? + GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; + const char* pszZ = (pszCur != NULL) ? + GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; + + if (pszY == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Did not get 2+ values in %s tuple.", + pszPos ? pszPos : "" ); + return FALSE; + } + + double dfX = OGRFastAtof(pszX); + double dfY = OGRFastAtof(pszY); + double dfZ = (pszZ != NULL) ? OGRFastAtof(pszZ) : 0.0; + int bSuccess = AddPoint( poGeometry, dfX, dfY, dfZ, (pszZ != NULL) ? 3 : 2 ); + + if (bSuccess) + bHasFoundPosElement = TRUE; + else + return FALSE; + } + + if (bHasFoundPosElement) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Is this a "posList"? GML 3 construct (SF profile). */ +/* -------------------------------------------------------------------- */ + const CPLXMLNode *psPosList = FindBareXMLChild( psGeomNode, "posList" ); + + if( psPosList != NULL ) + { + int bSuccess = FALSE; + int nDimension = 2; + + /* Try to detect the presence of an srsDimension attribute */ + /* This attribute is only availabe for gml3.1.1 but not */ + /* available for gml3.1 SF*/ + const char* pszSRSDimension = CPLGetXMLValue( (CPLXMLNode*) psPosList, "srsDimension", NULL); + /* If not found at the posList level, try on the enclosing element */ + if (pszSRSDimension == NULL) + pszSRSDimension = CPLGetXMLValue( (CPLXMLNode*) psGeomNode, "srsDimension", NULL); + if (pszSRSDimension != NULL) + nDimension = atoi(pszSRSDimension); + else if( nSRSDimension != 0 ) /* or use one coming from a still higher level element (#5606) */ + nDimension = nSRSDimension; + + if (nDimension != 2 && nDimension != 3) + { + CPLError( CE_Failure, CPLE_AppDefined, + "srsDimension = %d not supported", nDimension); + return FALSE; + } + + const char* pszPosList = GetElementText( psPosList ); + if (pszPosList == NULL) + { + poGeometry->empty(); + return TRUE; + } + + const char* pszCur = pszPosList; + while (TRUE) + { + const char* pszX = GMLGetCoordTokenPos(pszCur, &pszCur); + if (pszX == NULL && bSuccess) + break; + const char* pszY = (pszCur != NULL) ? + GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; + const char* pszZ = (nDimension == 3 && pszCur != NULL) ? + GMLGetCoordTokenPos(pszCur, &pszCur) : NULL; + + if (pszY == NULL || (nDimension == 3 && pszZ == NULL)) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Did not get at least %d values or invalid number of \n" + "set of coordinates %s", + nDimension, pszPosList ? pszPosList : ""); + return FALSE; + } + + double dfX = OGRFastAtof(pszX); + double dfY = OGRFastAtof(pszY); + double dfZ = (pszZ != NULL) ? OGRFastAtof(pszZ) : 0.0; + bSuccess = AddPoint( poGeometry, dfX, dfY, dfZ, nDimension ); + + if (bSuccess == FALSE || pszCur == NULL) + break; + } + + return bSuccess; + } + + +/* -------------------------------------------------------------------- */ +/* Handle form with a list of items each with an , */ +/* and element. */ +/* -------------------------------------------------------------------- */ + const CPLXMLNode *psCoordNode; + + for( psCoordNode = psGeomNode->psChild; + psCoordNode != NULL; + psCoordNode = psCoordNode->psNext ) + { + if( psCoordNode->eType != CXT_Element + || !EQUAL(BareGMLElement(psCoordNode->pszValue),"coord") ) + continue; + + const CPLXMLNode *psXNode, *psYNode, *psZNode; + double dfX, dfY, dfZ = 0.0; + int nDimension = 2; + + psXNode = FindBareXMLChild( psCoordNode, "X" ); + psYNode = FindBareXMLChild( psCoordNode, "Y" ); + psZNode = FindBareXMLChild( psCoordNode, "Z" ); + + if( psXNode == NULL || psYNode == NULL + || GetElementText(psXNode) == NULL + || GetElementText(psYNode) == NULL + || (psZNode != NULL && GetElementText(psZNode) == NULL) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Corrupt element, missing or element?" ); + return FALSE; + } + + dfX = OGRFastAtof( GetElementText(psXNode) ); + dfY = OGRFastAtof( GetElementText(psYNode) ); + + if( psZNode != NULL && GetElementText(psZNode) != NULL ) + { + dfZ = OGRFastAtof( GetElementText(psZNode) ); + nDimension = 3; + } + + if( !AddPoint( poGeometry, dfX, dfY, dfZ, nDimension ) ) + return FALSE; + + iCoord++; + } + + return iCoord > 0.0; +} + +#ifdef HAVE_GEOS +/************************************************************************/ +/* GML2FaceExtRing() */ +/* */ +/* Identifies the "good" Polygon whithin the collection returned */ +/* by GEOSPolygonize() */ +/* short rationale: GEOSPolygonize() will possibily return a */ +/* collection of many Polygons; only one is the "good" one, */ +/* (including both exterior- and interior-rings) */ +/* any other simply represents a single "hole", and should be */ +/* consequently ignored at all. */ +/************************************************************************/ + +static OGRPolygon *GML2FaceExtRing( OGRGeometry *poGeom ) +{ + OGRPolygon *poPolygon = NULL; + int bError = FALSE; + OGRGeometryCollection *poColl = (OGRGeometryCollection *)poGeom; + int iCount = poColl->getNumGeometries(); + int iExterior = 0; + int iInterior = 0; + + for( int ig = 0; ig < iCount; ig++) + { + /* a collection of Polygons is expected to be found */ + OGRGeometry * poChild = (OGRGeometry*)poColl->getGeometryRef(ig); + if( poChild == NULL) + { + bError = TRUE; + continue; + } + if( wkbFlatten( poChild->getGeometryType()) == wkbPolygon ) + { + OGRPolygon *poPg = (OGRPolygon *)poChild; + if( poPg->getNumInteriorRings() > 0 ) + iExterior++; + else + iInterior++; + } + else + bError = TRUE; + } + + if( bError == FALSE && iCount > 0 ) + { + if( iCount == 1 && iExterior == 0 && iInterior == 1) + { + /* there is a single Polygon within the collection */ + OGRPolygon * poPg = (OGRPolygon*)poColl->getGeometryRef(0 ); + poPolygon = (OGRPolygon *)poPg->clone(); + } + else + { + if( iExterior == 1 && iInterior == iCount - 1 ) + { + /* searching the unique Polygon containing holes */ + for ( int ig = 0; ig < iCount; ig++) + { + OGRPolygon * poPg = (OGRPolygon*)poColl->getGeometryRef(ig); + if( poPg->getNumInteriorRings() > 0 ) + poPolygon = (OGRPolygon *)poPg->clone(); + } + } + } + } + + return poPolygon; +} +#endif + +/************************************************************************/ +/* GML2OGRGeometry_AddToCompositeCurve() */ +/************************************************************************/ + +static +int GML2OGRGeometry_AddToCompositeCurve(OGRCompoundCurve* poCC, + OGRGeometry* poGeom, + int& bChildrenAreAllLineString) +{ + if( poGeom == NULL || + !OGR_GT_IsCurve(poGeom->getGeometryType()) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CompositeCurve: Got %.500s geometry as Member instead of a curve.", + poGeom ? poGeom->getGeometryName() : "NULL" ); + return FALSE; + } + + /* Crazy but allowed by GML: composite in composite */ + if( wkbFlatten(poGeom->getGeometryType()) == wkbCompoundCurve ) + { + OGRCompoundCurve* poCCChild = (OGRCompoundCurve* ) poGeom; + while( poCCChild->getNumCurves() != 0 ) + { + OGRCurve* poCurve = poCCChild->stealCurve(0); + if( wkbFlatten(poCurve->getGeometryType()) != wkbLineString ) + bChildrenAreAllLineString = FALSE; + if( poCC->addCurveDirectly( poCurve ) != OGRERR_NONE ) + { + delete poCurve; + return FALSE; + } + } + delete poCCChild; + } + else + { + if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) + bChildrenAreAllLineString = FALSE; + + if( poCC->addCurveDirectly( (OGRCurve*)poGeom ) != OGRERR_NONE ) + { + return FALSE; + } + } + return TRUE; +} + +/************************************************************************/ +/* GML2OGRGeometry_AddToCompositeCurve() */ +/************************************************************************/ + +static +int GML2OGRGeometry_AddToMultiSurface(OGRMultiSurface* poMS, + OGRGeometry*& poGeom, + const char* pszMemberElement, + int& bChildrenAreAllPolygons) +{ + if (poGeom == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s", + pszMemberElement ); + return FALSE; + } + + OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType()); + if( eType == wkbPolygon || eType == wkbCurvePolygon ) + { + if( eType != wkbPolygon ) + bChildrenAreAllPolygons = FALSE; + + if( poMS->addGeometryDirectly( poGeom ) != OGRERR_NONE ) + { + return FALSE; + } + } + else if (eType == wkbMultiPolygon || eType == wkbMultiSurface) + { + OGRMultiSurface* poMS2 = (OGRMultiSurface*) poGeom; + int i; + for(i=0;igetNumGeometries();i++) + { + if( wkbFlatten(poMS2->getGeometryRef(i)->getGeometryType()) != wkbPolygon ) + bChildrenAreAllPolygons = FALSE; + + if( poMS->addGeometry(poMS2->getGeometryRef(i)) != OGRERR_NONE ) + { + return FALSE; + } + } + delete poGeom; + poGeom = NULL; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as %s.", + poGeom->getGeometryName(), pszMemberElement ); + return FALSE; + } + return TRUE; +} + +/************************************************************************/ +/* GML2OGRGeometry_XMLNode() */ +/* */ +/* Translates the passed XMLnode and it's children into an */ +/* OGRGeometry. This is used recursively for geometry */ +/* collections. */ +/************************************************************************/ + +static +OGRGeometry *GML2OGRGeometry_XMLNode_Internal( const CPLXMLNode *psNode, + int bGetSecondaryGeometryOption, + int nRecLevel, + int nSRSDimension, + const char* pszSRSName, + int bIgnoreGSG = FALSE, + int bOrientation = TRUE, + int bFaceHoleNegative = FALSE ); + +OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode, + int bGetSecondaryGeometryOption, + int nRecLevel, + int nSRSDimension, + int bIgnoreGSG, + int bOrientation, + int bFaceHoleNegative ) + +{ + return GML2OGRGeometry_XMLNode_Internal(psNode, + bGetSecondaryGeometryOption, + nRecLevel, nSRSDimension, + NULL, + bIgnoreGSG, bOrientation, + bFaceHoleNegative); +} + +static +OGRGeometry *GML2OGRGeometry_XMLNode_Internal( const CPLXMLNode *psNode, + int bGetSecondaryGeometryOption, + int nRecLevel, + int nSRSDimension, + const char* pszSRSName, + int bIgnoreGSG, + int bOrientation, + int bFaceHoleNegative ) +{ + const int bCastToLinearTypeIfPossible = TRUE; /* hard-coded for now */ + + if( psNode != NULL && strcmp(psNode->pszValue, "?xml") == 0 ) + psNode = psNode->psNext; + while( psNode != NULL && psNode->eType == CXT_Comment ) + psNode = psNode->psNext; + if( psNode == NULL ) + return NULL; + + const char* pszSRSDimension = CPLGetXMLValue( (CPLXMLNode*) psNode, "srsDimension", NULL); + if( pszSRSDimension != NULL ) + nSRSDimension = atoi(pszSRSDimension); + + if( pszSRSName == NULL ) + pszSRSName = CPLGetXMLValue( (CPLXMLNode*) psNode, "srsName", NULL); + + const char *pszBaseGeometry = BareGMLElement( psNode->pszValue ); + if (bGetSecondaryGeometryOption < 0) + bGetSecondaryGeometryOption = CSLTestBoolean(CPLGetConfigOption("GML_GET_SECONDARY_GEOM", "NO")); + int bGetSecondaryGeometry = bIgnoreGSG ? FALSE : bGetSecondaryGeometryOption; + + /* Arbitrary value, but certainly large enough for reasonable usages ! */ + if( nRecLevel == 32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many recursion levels (%d) while parsing GML geometry.", + nRecLevel ); + return NULL; + } + + if( bGetSecondaryGeometry ) + if( !( EQUAL(pszBaseGeometry,"directedEdge") || + EQUAL(pszBaseGeometry,"TopoCurve") ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Polygon / PolygonPatch / Triangle / Rectangle */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Polygon") || + EQUAL(pszBaseGeometry,"PolygonPatch") || + EQUAL(pszBaseGeometry,"Triangle") || + EQUAL(pszBaseGeometry,"Rectangle")) + { + const CPLXMLNode *psChild; + + // Find outer ring. + psChild = FindBareXMLChild( psNode, "outerBoundaryIs" ); + if (psChild == NULL) + psChild = FindBareXMLChild( psNode, "exterior"); + + psChild = GetChildElement(psChild); + if( psChild == NULL ) + { + /* is invalid GML2, but valid GML3, so be tolerant */ + return new OGRPolygon(); + } + + // Translate outer ring and add to polygon. + OGRGeometry* poGeom = + GML2OGRGeometry_XMLNode_Internal( psChild, + bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, + pszSRSName ); + if( poGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid exterior ring"); + return NULL; + } + + if( !OGR_GT_IsCurve(poGeom->getGeometryType()) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s: Got %.500s geometry as outerBoundaryIs.", + pszBaseGeometry, poGeom->getGeometryName() ); + delete poGeom; + return NULL; + } + + if( wkbFlatten(poGeom->getGeometryType()) == wkbLineString && + !EQUAL(poGeom->getGeometryName(), "LINEARRING") ) + { + poGeom = OGRCurve::CastToLinearRing((OGRCurve*)poGeom); + } + + OGRCurvePolygon *poCP; + int bIsPolygon; + if( EQUAL(poGeom->getGeometryName(), "LINEARRING") ) + { + poCP = new OGRPolygon(); + bIsPolygon = TRUE; + } + else + { + poCP = new OGRCurvePolygon(); + bIsPolygon = FALSE; + } + + if( poCP->addRingDirectly( (OGRCurve*)poGeom ) != OGRERR_NONE ) + { + delete poCP; + delete poGeom; + return NULL; + } + + // Find all inner rings + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && (EQUAL(BareGMLElement(psChild->pszValue),"innerBoundaryIs") || + EQUAL(BareGMLElement(psChild->pszValue),"interior"))) + { + const CPLXMLNode* psInteriorChild = GetChildElement(psChild); + if (psInteriorChild != NULL) + poGeom = + GML2OGRGeometry_XMLNode_Internal( psInteriorChild, + bGetSecondaryGeometryOption, + nRecLevel + 1, + nSRSDimension, + pszSRSName ); + else + poGeom = NULL; + if (poGeom == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid interior ring"); + delete poCP; + return NULL; + } + + if( !OGR_GT_IsCurve(poGeom->getGeometryType()) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s: Got %.500s geometry as innerBoundaryIs.", + pszBaseGeometry, poGeom->getGeometryName() ); + delete poCP; + delete poGeom; + return NULL; + } + + if( bIsPolygon ) + { + if( !EQUAL(poGeom->getGeometryName(), "LINEARRING") ) + { + if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) + { + OGRLineString* poLS = (OGRLineString*)poGeom; + poGeom = OGRCurve::CastToLinearRing(poLS); + } + else + { + /* Might fail if some rings are not closed */ + /* We used to be tolerant about that with Polygon */ + /* but we have become stricter with CurvePolygon */ + poCP = OGRSurface::CastToCurvePolygon( (OGRPolygon*)poCP ); + if( poCP == NULL ) + { + delete poGeom; + return NULL; + } + bIsPolygon = FALSE; + } + } + } + else + { + if( EQUAL(poGeom->getGeometryName(), "LINEARRING") ) + poGeom = OGRCurve::CastToLineString( (OGRCurve*)poGeom ); + } + if( poCP->addRingDirectly( (OGRCurve*)poGeom ) != OGRERR_NONE ) + { + delete poCP; + delete poGeom; + return NULL; + } + } + } + + return poCP; + } + +/* -------------------------------------------------------------------- */ +/* LinearRing */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"LinearRing") ) + { + OGRLinearRing *poLinearRing = new OGRLinearRing(); + + if( !ParseGMLCoordinates( psNode, poLinearRing, nSRSDimension ) ) + { + delete poLinearRing; + return NULL; + } + + return poLinearRing; + } + +/* -------------------------------------------------------------------- */ +/* Ring GML3 */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Ring") ) + { + OGRCurve* poRing = NULL; + OGRCompoundCurve *poCC = NULL; + int bChildrenAreAllLineString = TRUE; + const CPLXMLNode *psChild; + + int bLastCurveWasApproximateArc = FALSE; + int bLastCurveWasApproximateArcInvertedAxisOrder = FALSE; + double dfLastCurveApproximateArcRadius = 0; + + for( psChild = psNode->psChild; + psChild != NULL; psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"curveMember") ) + { + const CPLXMLNode* psCurveChild = GetChildElement(psChild); + OGRGeometry* poGeom; + if (psCurveChild != NULL) + poGeom = + GML2OGRGeometry_XMLNode_Internal( psCurveChild, + bGetSecondaryGeometryOption, + nRecLevel + 1, + nSRSDimension, + pszSRSName ); + else + { + if( psChild->psChild && psChild->psChild->eType == CXT_Attribute && + psChild->psChild->psNext == NULL && + strcmp(psChild->psChild->pszValue, "xlink:href") == 0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot resolve xlink:href='%s'. Try setting GML_SKIP_RESOLVE_ELEMS=NONE", + psChild->psChild->psChild->pszValue); + } + + poGeom = NULL; + } + + // try to join multiline string to one linestring + if( poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ) + { + poGeom = OGRGeometryFactory::forceToLineString( poGeom, false ); + } + + if( poGeom == NULL + || !OGR_GT_IsCurve(poGeom->getGeometryType()) ) + { + delete poGeom; + delete poRing; + delete poCC; + return NULL; + } + + if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) + bChildrenAreAllLineString = FALSE; + + /* Ad-hoc logic to handle nicely connecting ArcByCenterPoint */ + /* with consecutive curves, as found in some AIXM files */ + int bIsApproximateArc = FALSE; + const CPLXMLNode* psChild2, *psChild3; + if( strcmp(psCurveChild->pszValue, "Curve") == 0 && + (psChild2 = GetChildElement(psCurveChild)) != NULL && + strcmp(psChild2->pszValue, "segments") == 0 && + (psChild3 = GetChildElement(psChild2)) != NULL && + strcmp(psChild3->pszValue, "ArcByCenterPoint") == 0 ) + { + const CPLXMLNode* psRadius = FindBareXMLChild( psChild3, "radius"); + if( psRadius && psRadius->eType == CXT_Element ) + { + double dfRadius = CPLAtof(CPLGetXMLValue((CPLXMLNode*)psRadius, NULL, "0")); + const char* pszUnits = CPLGetXMLValue((CPLXMLNode*)psRadius, "uom", NULL); + int bSRSUnitIsDeegree = FALSE; + int bInvertedAxisOrder = FALSE; + if( pszSRSName != NULL ) + { + OGRSpatialReference oSRS; + if( oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE ) + { + if( oSRS.IsGeographic() ) + { + bInvertedAxisOrder = oSRS.EPSGTreatsAsLatLong(); + bSRSUnitIsDeegree = fabs(oSRS.GetAngularUnits(NULL) - CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8; + } + } + } + if( bSRSUnitIsDeegree && pszUnits != NULL && + (EQUAL(pszUnits, "m") || EQUAL(pszUnits, "nm") || + EQUAL(pszUnits, "mi") || EQUAL(pszUnits, "ft")) ) + { + bIsApproximateArc = TRUE; + if( EQUAL(pszUnits, "nm") ) + dfRadius *= CPLAtof(SRS_UL_INTL_NAUT_MILE_CONV); + else if( EQUAL(pszUnits, "mi") ) + dfRadius *= CPLAtof(SRS_UL_INTL_STAT_MILE_CONV); + else if( EQUAL(pszUnits, "ft") ) + dfRadius *= CPLAtof(SRS_UL_INTL_FOOT_CONV); + dfLastCurveApproximateArcRadius = dfRadius; + bLastCurveWasApproximateArcInvertedAxisOrder = bInvertedAxisOrder; + } + } + } + + if( poCC == NULL && poRing == NULL ) + poRing = (OGRCurve*)poGeom; + else + { + if( poCC == NULL ) + { + poCC = new OGRCompoundCurve(); + if( poCC->addCurveDirectly(poRing) != OGRERR_NONE ) + { + delete poGeom; + delete poRing; + delete poCC; + return NULL; + } + poRing = NULL; + } + + if( bIsApproximateArc ) + { + if( poGeom->getGeometryType() == wkbLineString ) + { + OGRCurve* poPreviousCurve = poCC->getCurve(poCC->getNumCurves()-1); + OGRLineString* poLS = (OGRLineString*)poGeom; + if( poPreviousCurve->getNumPoints() >= 2 && poLS->getNumPoints() >= 2 ) + { + OGRPoint p, p2; + poPreviousCurve->EndPoint(&p); + poLS->StartPoint(&p2); + double dfDistance; + if( bLastCurveWasApproximateArcInvertedAxisOrder ) + dfDistance = OGRXPlane_Distance(p.getX(), p.getY(), p2.getX(), p2.getY()); + else + dfDistance = OGRXPlane_Distance(p.getY(), p.getX(), p2.getY(), p2.getX()); + //CPLDebug("OGR", "%f %f\n", dfDistance, dfLastCurveApproximateArcRadius / 10 ); + if( dfDistance < dfLastCurveApproximateArcRadius / 5 ) + { + CPLDebug("OGR", "Moving approximate start of ArcByCenterPoint to end of previous curve"); + poLS->setPoint(0, &p); + } + } + } + } + else if( bLastCurveWasApproximateArc ) + { + OGRCurve* poPreviousCurve = poCC->getCurve(poCC->getNumCurves()-1); + if( poPreviousCurve->getGeometryType() == wkbLineString ) + { + OGRLineString* poLS = (OGRLineString*)poPreviousCurve; + if( poLS->getNumPoints() >= 2 && ((OGRCurve*)poGeom)->getNumPoints() >= 2 ) + { + OGRPoint p, p2; + ((OGRCurve*)poGeom)->StartPoint(&p); + poLS->EndPoint(&p2); + double dfDistance; + if( bLastCurveWasApproximateArcInvertedAxisOrder ) + dfDistance = OGRXPlane_Distance(p.getX(), p.getY(), p2.getX(), p2.getY()); + else + dfDistance = OGRXPlane_Distance(p.getY(), p.getX(), p2.getY(), p2.getX()); + //CPLDebug("OGR", "%f %f\n", dfDistance, dfLastCurveApproximateArcRadius / 10 ); + // "A-311 WHEELER AFB OAHU, HI.xml" needs more than 10% + if( dfDistance < dfLastCurveApproximateArcRadius / 5 ) + { + CPLDebug("OGR", "Moving approximate end of last ArcByCenterPoint to start of current curve"); + poLS->setPoint(poLS->getNumPoints()-1, &p); + } + } + } + } + + if( poCC->addCurveDirectly((OGRCurve*)poGeom) != OGRERR_NONE ) + { + delete poGeom; + delete poCC; + return NULL; + } + } + + bLastCurveWasApproximateArc = bIsApproximateArc; + } + } + + if( poRing ) + { + if( poRing->getNumPoints() < 2 || !poRing->get_IsClosed() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Non-closed ring"); + delete poRing; + return NULL; + } + return poRing; + } + + if( poCC == NULL ) + return NULL; + + else if( bCastToLinearTypeIfPossible && bChildrenAreAllLineString ) + { + return OGRCurve::CastToLinearRing(poCC); + } + else + { + if( poCC->getNumPoints() < 2 || !poCC->get_IsClosed() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Non-closed ring"); + delete poCC; + return NULL; + } + return poCC; + } + } + +/* -------------------------------------------------------------------- */ +/* LineString */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"LineString") + || EQUAL(pszBaseGeometry,"LineStringSegment") + || EQUAL(pszBaseGeometry,"GeodesicString") ) + { + OGRLineString *poLine = new OGRLineString(); + + if( !ParseGMLCoordinates( psNode, poLine, nSRSDimension ) ) + { + delete poLine; + return NULL; + } + + return poLine; + } + +#if 0 +/* -------------------------------------------------------------------- */ +/* Arc/Circle : we approximate them by linear segments */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Arc") || + EQUAL(pszBaseGeometry,"Circle") ) + { + OGRLineString *poLine = new OGRLineString(); + + if( !ParseGMLCoordinates( psNode, poLine, nSRSDimension ) || + poLine->getNumPoints() != 3 ) + { + delete poLine; + return NULL; + } + double x0 = poLine->getX(0); + double y0 = poLine->getY(0); + double x1 = poLine->getX(1); + double y1 = poLine->getY(1); + double x2 = poLine->getX(2); + double y2 = poLine->getY(2); + double dx01 = x1 - x0; + double dy01 = y1 - y0; + double dx12 = x2 - x1; + double dy12 = y2 - y1; + double c01 = dx01 * (x0 + x1) / 2 + dy01 * (y0 + y1) / 2; + double c12 = dx12 * (x1 + x2) / 2 + dy12 * (y1 + y2) / 2; + double det = dx01 * dy12 - dx12 * dy01; + if (det == 0) + { + return poLine; + } + double cx = (c01 * dy12 - c12 * dy01) / det; + double cy = (- c01 * dx12 + c12 * dx01) / det; + + double alpha0 = atan2(y0 - cy, x0 - cx); + double alpha1 = atan2(y1 - cy, x1 - cx); + double alpha2 = atan2(y2 - cy, x2 - cx); + double alpha3; + double R = sqrt((x0 - cx) * (x0 - cx) + (y0 - cy) * (y0 - cy)); + + /* if det is negative, the orientation if clockwise */ + if (det < 0) + { + if (alpha1 > alpha0) + alpha1 -= 2 * PI; + if (alpha2 > alpha1) + alpha2 -= 2 * PI; + alpha3 = alpha0 - 2 * PI; + } + else + { + if (alpha1 < alpha0) + alpha1 += 2 * PI; + if (alpha2 < alpha1) + alpha2 += 2 * PI; + alpha3 = alpha0 + 2 * PI; + } + + CPLAssert((alpha0 <= alpha1 && alpha1 <= alpha2 && alpha2 <= alpha3) || + (alpha0 >= alpha1 && alpha1 >= alpha2 && alpha2 >= alpha3)); + + int nSign = (det >= 0) ? 1 : -1; + + double alpha, dfRemainder; + double dfStep = CPLAtof(CPLGetConfigOption("OGR_ARC_STEPSIZE","4")) / 180 * PI; + + // make sure the segments are not too short + double dfMinStepLength = CPLAtof( CPLGetConfigOption("OGR_ARC_MINLENGTH","0") ); + if ( dfMinStepLength > 0.0 && dfStep * R < dfMinStepLength ) + { + CPLDebug( "GML", "Increasing arc step to %lf° (was %lf° with segment length %lf at radius %lf; min segment length is %lf)", + dfMinStepLength * 180.0 / PI / R, + dfStep * 180.0 / PI, + dfStep * R, + R, + dfMinStepLength ); + dfStep = dfMinStepLength / R; + } + + if (dfStep < 4. / 180 * PI) + { + CPLDebug( "GML", "Increasing arc step to %lf° (was %lf° with length %lf at radius %lf).", + 4. / 180 * PI, + dfStep * 180.0 / PI, + dfStep * R, + R ); + dfStep = 4. / 180 * PI; + } + + poLine->setNumPoints(0); + + dfStep *= nSign; + + dfRemainder = fmod(alpha1 - alpha0, dfStep) / 2.0; + + poLine->addPoint(x0, y0); + + for(alpha = alpha0 + dfStep + dfRemainder; (alpha + dfRemainder - alpha1) * nSign < 0; alpha += dfStep) + { + poLine->addPoint(cx + R * cos(alpha), cy + R * sin(alpha)); + } + + poLine->addPoint(x1, y1); + + dfRemainder = fmod(alpha2 - alpha1, dfStep) / 2.0; + + for(alpha = alpha1 + dfStep + dfRemainder; (alpha + dfRemainder - alpha2) * nSign < 0; alpha += dfStep) + { + poLine->addPoint(cx + R * cos(alpha), cy + R * sin(alpha)); + } + + if (EQUAL(pszBaseGeometry,"Circle")) + { + for(alpha = alpha2; (alpha - alpha3) * nSign < 0; alpha += dfStep) + { + poLine->addPoint(cx + R * cos(alpha), cy + R * sin(alpha)); + } + poLine->addPoint(x0, y0); + } + else + { + poLine->addPoint(x2, y2); + } + + return poLine; + } +#endif +/* -------------------------------------------------------------------- */ +/* Arc */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Arc") ) + { + OGRCircularString *poCC = new OGRCircularString(); + + if( !ParseGMLCoordinates( psNode, poCC, nSRSDimension ) ) + { + delete poCC; + return NULL; + } + + if( poCC->getNumPoints() != 3 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Bad number of points in Arc"); + delete poCC; + return NULL; + } + + return poCC; + } + +/* -------------------------------------------------------------------- */ +/* ArcString */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"ArcString") ) + { + OGRCircularString *poCC = new OGRCircularString(); + + if( !ParseGMLCoordinates( psNode, poCC, nSRSDimension ) ) + { + delete poCC; + return NULL; + } + + if( poCC->getNumPoints() < 3 || (poCC->getNumPoints() % 2) != 1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Bad number of points in ArcString"); + delete poCC; + return NULL; + } + + return poCC; + } + +/* -------------------------------------------------------------------- */ +/* Circle */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Circle") ) + { + OGRLineString *poLine = new OGRLineString(); + + if( !ParseGMLCoordinates( psNode, poLine, nSRSDimension ) ) + { + delete poLine; + return NULL; + } + + if( poLine->getNumPoints() != 3 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Bad number of points in Circle"); + delete poLine; + return NULL; + } + + double R, cx, cy, alpha0, alpha1, alpha2; + if( !OGRGeometryFactory::GetCurveParmeters( + poLine->getX(0), poLine->getY(0), + poLine->getX(1), poLine->getY(1), + poLine->getX(2), poLine->getY(2), + R, cx, cy, alpha0, alpha1, alpha2 ) ) + { + delete poLine; + return NULL; + } + + OGRCircularString *poCC = new OGRCircularString(); + OGRPoint p; + poLine->getPoint(0, &p); + poCC->addPoint(&p); + poLine->getPoint(1, &p); + poCC->addPoint(&p); + poLine->getPoint(2, &p); + poCC->addPoint(&p); + double alpha4 = (alpha2 > alpha0) ? alpha0 + 2 * M_PI : alpha0 - 2 * M_PI; + double alpha3 = (alpha2 + alpha4) / 2; + double x = cx + R * cos(alpha3); + double y = cy + R * sin(alpha3); + if( poCC->getCoordinateDimension() == 3 ) + poCC->addPoint( x, y, p.getZ() ); + else + poCC->addPoint( x, y ); + poLine->getPoint(0, &p); + poCC->addPoint(&p); + delete poLine; + return poCC; + } + +/* -------------------------------------------------------------------- */ +/* ArcByBulge */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"ArcByBulge") ) + { + const CPLXMLNode *psChild; + + psChild = FindBareXMLChild( psNode, "bulge"); + if( psChild == NULL || psChild->eType != CXT_Element ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing bulge element." ); + return NULL; + } + double dfBulge = CPLAtof(psChild->psChild->pszValue); + + psChild = FindBareXMLChild( psNode, "normal"); + if( psChild == NULL || psChild->eType != CXT_Element ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing normal element." ); + return NULL; + } + double dfNormal = CPLAtof(psChild->psChild->pszValue); + + + OGRLineString* poLS = new OGRLineString(); + if( !ParseGMLCoordinates( psNode, poLS, nSRSDimension ) ) + { + delete poLS; + return NULL; + } + + if( poLS->getNumPoints() != 2 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Bad number of points in ArcByBulge"); + delete poLS; + return NULL; + } + + OGRCircularString *poCC = new OGRCircularString(); + OGRPoint p; + poLS->getPoint(0, &p); + poCC->addPoint(&p); + + double dfMidX = (poLS->getX(0) + poLS->getX(1)) / 2; + double dfMidY = (poLS->getY(0) + poLS->getY(1)) / 2; + double dfDirX = (poLS->getX(1) - poLS->getX(0)) / 2; + double dfDirY = (poLS->getY(1) - poLS->getY(0)) / 2; + double dfNormX = -dfDirY; + double dfNormY = dfDirX; + double dfNorm = sqrt(dfNormX * dfNormX + dfNormY * dfNormY); + if( dfNorm ) + { + dfNormX /= dfNorm; + dfNormY /= dfNorm; + } + double dfNewX = dfMidX + dfNormX * dfBulge * dfNormal; + double dfNewY = dfMidY + dfNormY * dfBulge * dfNormal; + + if( poCC->getCoordinateDimension() == 3 ) + poCC->addPoint( dfNewX, dfNewY, p.getZ() ); + else + poCC->addPoint( dfNewX, dfNewY ); + + poLS->getPoint(1, &p); + poCC->addPoint(&p); + + delete poLS; + return poCC; + } + +/* -------------------------------------------------------------------- */ +/* ArcByCenterPoint */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"ArcByCenterPoint") ) + { + const CPLXMLNode *psChild; + + psChild = FindBareXMLChild( psNode, "radius"); + if( psChild == NULL || psChild->eType != CXT_Element ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing radius element." ); + return NULL; + } + double dfRadius = CPLAtof(CPLGetXMLValue((CPLXMLNode*)psChild, NULL, "0")); + const char* pszUnits = CPLGetXMLValue((CPLXMLNode*)psChild, "uom", NULL); + + psChild = FindBareXMLChild( psNode, "startAngle"); + if( psChild == NULL || psChild->eType != CXT_Element ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing startAngle element." ); + return NULL; + } + double dfStartAngle = CPLAtof(CPLGetXMLValue((CPLXMLNode*)psChild, NULL, "0")); + + psChild = FindBareXMLChild( psNode, "endAngle"); + if( psChild == NULL || psChild->eType != CXT_Element ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing endAngle element." ); + return NULL; + } + double dfEndAngle = CPLAtof(CPLGetXMLValue((CPLXMLNode*)psChild, NULL, "0")); + + OGRPoint p; + if( !ParseGMLCoordinates( psNode, &p, nSRSDimension ) ) + { + return NULL; + } + + int bSRSUnitIsDeegree = FALSE; + int bInvertedAxisOrder = FALSE; + if( pszSRSName != NULL ) + { + OGRSpatialReference oSRS; + if( oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE ) + { + if( oSRS.IsGeographic() ) + { + bInvertedAxisOrder = oSRS.EPSGTreatsAsLatLong(); + bSRSUnitIsDeegree = fabs(oSRS.GetAngularUnits(NULL) - CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8; + } + } + } + + double dfCenterX = p.getX(); + double dfCenterY = p.getY(); + + if( bSRSUnitIsDeegree && pszUnits != NULL && + (EQUAL(pszUnits, "m") || EQUAL(pszUnits, "nm") || + EQUAL(pszUnits, "mi") || EQUAL(pszUnits, "ft")) ) + { + OGRLineString* poLS = new OGRLineString(); + double dfStep = CPLAtof(CPLGetConfigOption("OGR_ARC_STEPSIZE","4")); + double dfDistance = dfRadius; + if( EQUAL(pszUnits, "nm") ) + dfDistance *= CPLAtof(SRS_UL_INTL_NAUT_MILE_CONV); + else if( EQUAL(pszUnits, "mi") ) + dfDistance *= CPLAtof(SRS_UL_INTL_STAT_MILE_CONV); + else if( EQUAL(pszUnits, "ft") ) + dfDistance *= CPLAtof(SRS_UL_INTL_FOOT_CONV); + double dfSign = (dfStartAngle < dfEndAngle) ? 1 : -1; + for(double dfAngle = dfStartAngle; (dfAngle - dfEndAngle) * dfSign < 0; dfAngle += dfSign * dfStep) + { + double dfLong, dfLat; + if( bInvertedAxisOrder ) + { + OGRXPlane_ExtendPosition(dfCenterX, dfCenterY, + dfDistance, 90-dfAngle, /* not sure of angle conversion here...*/ + &dfLat, &dfLong); + p.setY( dfLat ); + p.setX( dfLong ); + } + else + { + OGRXPlane_ExtendPosition(dfCenterY, dfCenterX, + dfDistance, 90-dfAngle, + &dfLat, &dfLong); + p.setX( dfLong ); + p.setY( dfLat ); + } + poLS->addPoint(&p); + } + + double dfLong, dfLat; + if( bInvertedAxisOrder ) + { + OGRXPlane_ExtendPosition(dfCenterX, dfCenterY, + dfDistance, 90-dfEndAngle, /* not sure of angle conversion here...*/ + &dfLat, &dfLong); + p.setY( dfLat ); + p.setX( dfLong ); + } + else + { + OGRXPlane_ExtendPosition(dfCenterY, dfCenterX, + dfDistance, 90-dfEndAngle, + &dfLat, &dfLong); + p.setX( dfLong ); + p.setY( dfLat ); + } + poLS->addPoint(&p); + + return poLS; + } + + OGRCircularString *poCC = new OGRCircularString(); + p.setX( dfCenterX + dfRadius * cos(dfStartAngle * M_PI / 180.0) ); + p.setY( dfCenterY + dfRadius * sin(dfStartAngle * M_PI / 180.0) ); + poCC->addPoint(&p); + p.setX( dfCenterX + dfRadius * cos((dfStartAngle+dfEndAngle)/2 * M_PI / 180.0) ); + p.setY( dfCenterY + dfRadius * sin((dfStartAngle+dfEndAngle)/2 * M_PI / 180.0) ); + poCC->addPoint(&p); + p.setX( dfCenterX + dfRadius * cos(dfEndAngle * M_PI / 180.0) ); + p.setY( dfCenterY + dfRadius * sin(dfEndAngle * M_PI / 180.0) ); + poCC->addPoint(&p); + return poCC; + } + +/* -------------------------------------------------------------------- */ +/* CircleByCenterPoint */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"CircleByCenterPoint") ) + { + const CPLXMLNode *psChild; + + psChild = FindBareXMLChild( psNode, "radius"); + if( psChild == NULL || psChild->eType != CXT_Element ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing radius element." ); + return NULL; + } + double dfRadius = CPLAtof(CPLGetXMLValue((CPLXMLNode*)psChild, NULL, "0")); + const char* pszUnits = CPLGetXMLValue((CPLXMLNode*)psChild, "uom", NULL); + + OGRPoint p; + if( !ParseGMLCoordinates( psNode, &p, nSRSDimension ) ) + { + return NULL; + } + + int bSRSUnitIsDeegree = FALSE; + int bInvertedAxisOrder = FALSE; + if( pszSRSName != NULL ) + { + OGRSpatialReference oSRS; + if( oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE ) + { + if( oSRS.IsGeographic() ) + { + bInvertedAxisOrder = oSRS.EPSGTreatsAsLatLong(); + bSRSUnitIsDeegree = fabs(oSRS.GetAngularUnits(NULL) - CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8; + } + } + } + + double dfCenterX = p.getX(); + double dfCenterY = p.getY(); + + if( bSRSUnitIsDeegree && pszUnits != NULL && + (EQUAL(pszUnits, "m") || EQUAL(pszUnits, "nm") || + EQUAL(pszUnits, "mi") || EQUAL(pszUnits, "ft")) ) + { + OGRLineString* poLS = new OGRLineString(); + double dfStep = CPLAtof(CPLGetConfigOption("OGR_ARC_STEPSIZE","4")); + double dfDistance = dfRadius; + if( EQUAL(pszUnits, "nm") ) + dfDistance *= CPLAtof(SRS_UL_INTL_NAUT_MILE_CONV); + else if( EQUAL(pszUnits, "mi") ) + dfDistance *= CPLAtof(SRS_UL_INTL_STAT_MILE_CONV); + else if( EQUAL(pszUnits, "ft") ) + dfDistance *= CPLAtof(SRS_UL_INTL_FOOT_CONV); + for(double dfAngle = 0; dfAngle < 360; dfAngle += dfStep) + { + double dfLong, dfLat; + if( bInvertedAxisOrder ) + { + OGRXPlane_ExtendPosition(dfCenterX, dfCenterY, + dfDistance, dfAngle, + &dfLat, &dfLong); + p.setY( dfLat ); + p.setX( dfLong ); + } + else + { + OGRXPlane_ExtendPosition(dfCenterY, dfCenterX, + dfDistance, dfAngle, + &dfLat, &dfLong); + p.setX( dfLong ); + p.setY( dfLat ); + } + poLS->addPoint(&p); + } + poLS->getPoint(0, &p); + poLS->addPoint(&p); + return poLS; + } + + OGRCircularString *poCC = new OGRCircularString(); + p.setX( dfCenterX - dfRadius ); + p.setY( dfCenterY ); + poCC->addPoint(&p); + p.setX( dfCenterX + dfRadius); + p.setY( dfCenterY ); + poCC->addPoint(&p); + p.setX( dfCenterX - dfRadius ); + p.setY( dfCenterY ); + poCC->addPoint(&p); + return poCC; + } + +/* -------------------------------------------------------------------- */ +/* PointType */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"PointType") + || EQUAL(pszBaseGeometry,"Point") + || EQUAL(pszBaseGeometry,"ConnectionPoint") ) + { + OGRPoint *poPoint = new OGRPoint(); + + if( !ParseGMLCoordinates( psNode, poPoint, nSRSDimension ) ) + { + delete poPoint; + return NULL; + } + + return poPoint; + } + +/* -------------------------------------------------------------------- */ +/* Box */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"BoxType") || EQUAL(pszBaseGeometry,"Box") ) + { + OGRLineString oPoints; + + if( !ParseGMLCoordinates( psNode, &oPoints, nSRSDimension ) ) + return NULL; + + if( oPoints.getNumPoints() < 2 ) + return NULL; + + OGRLinearRing *poBoxRing = new OGRLinearRing(); + OGRPolygon *poBoxPoly = new OGRPolygon(); + + poBoxRing->setNumPoints( 5 ); + poBoxRing->setPoint( + 0, oPoints.getX(0), oPoints.getY(0), oPoints.getZ(0) ); + poBoxRing->setPoint( + 1, oPoints.getX(1), oPoints.getY(0), oPoints.getZ(0) ); + poBoxRing->setPoint( + 2, oPoints.getX(1), oPoints.getY(1), oPoints.getZ(1) ); + poBoxRing->setPoint( + 3, oPoints.getX(0), oPoints.getY(1), oPoints.getZ(0) ); + poBoxRing->setPoint( + 4, oPoints.getX(0), oPoints.getY(0), oPoints.getZ(0) ); + + poBoxPoly->addRingDirectly( poBoxRing ); + + return poBoxPoly; + } + +/* -------------------------------------------------------------------- */ +/* Envelope */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Envelope") ) + { + const CPLXMLNode* psLowerCorner = FindBareXMLChild( psNode, "lowerCorner"); + const CPLXMLNode* psUpperCorner = FindBareXMLChild( psNode, "upperCorner"); + if( psLowerCorner == NULL || psUpperCorner == NULL ) + return NULL; + const char* pszLowerCorner = GetElementText(psLowerCorner); + const char* pszUpperCorner = GetElementText(psUpperCorner); + if( pszLowerCorner == NULL || pszUpperCorner == NULL ) + return NULL; + char** papszLowerCorner = CSLTokenizeString(pszLowerCorner); + char** papszUpperCorner = CSLTokenizeString(pszUpperCorner); + int nTokenCountLC = CSLCount(papszLowerCorner); + int nTokenCountUC = CSLCount(papszUpperCorner); + if( nTokenCountLC < 2 || nTokenCountUC < 2 ) + { + CSLDestroy(papszLowerCorner); + CSLDestroy(papszUpperCorner); + return NULL; + } + + double dfLLX = CPLAtof(papszLowerCorner[0]); + double dfLLY = CPLAtof(papszLowerCorner[1]); + double dfURX = CPLAtof(papszUpperCorner[0]); + double dfURY = CPLAtof(papszUpperCorner[1]); + CSLDestroy(papszLowerCorner); + CSLDestroy(papszUpperCorner); + + OGRLinearRing *poEnvelopeRing = new OGRLinearRing(); + OGRPolygon *poPoly = new OGRPolygon(); + + poEnvelopeRing->setNumPoints( 5 ); + poEnvelopeRing->setPoint(0, dfLLX, dfLLY); + poEnvelopeRing->setPoint(1, dfURX, dfLLY); + poEnvelopeRing->setPoint(2, dfURX, dfURY); + poEnvelopeRing->setPoint(3, dfLLX, dfURY); + poEnvelopeRing->setPoint(4, dfLLX, dfLLY); + poPoly->addRingDirectly(poEnvelopeRing ); + + return poPoly; + } + +/* ------------------------const CPLXMLNode *psChild;-------------------------------------------- */ +/* MultiPolygon / MultiSurface / CompositeSurface */ +/* */ +/* For CompositeSurface, this is a very rough approximation to deal with*/ +/* it as a MultiPolygon, because it can several faces of a 3D volume... */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"MultiPolygon") || + EQUAL(pszBaseGeometry,"MultiSurface") || + EQUAL(pszBaseGeometry,"CompositeSurface") ) + { + const CPLXMLNode *psChild; + OGRMultiSurface* poMS; + if( EQUAL(pszBaseGeometry,"MultiPolygon") ) + poMS = new OGRMultiPolygon(); + else + poMS = new OGRMultiSurface(); + int bReconstructTopology = FALSE; + int bChildrenAreAllPolygons = TRUE; + + // Iterate over children + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + const char* pszMemberElement = BareGMLElement(psChild->pszValue); + if( psChild->eType == CXT_Element + && (EQUAL(pszMemberElement,"polygonMember") || + EQUAL(pszMemberElement,"surfaceMember")) ) + { + const CPLXMLNode* psSurfaceChild = GetChildElement(psChild); + + if (psSurfaceChild != NULL) + { + /* Cf #5421 where there are PolygonPatch with only inner rings */ + const CPLXMLNode* psPolygonPatch = GetChildElement(GetChildElement(psSurfaceChild)); + if( psPolygonPatch != NULL && + psPolygonPatch->eType == CXT_Element && + EQUAL(BareGMLElement(psPolygonPatch->pszValue),"PolygonPatch") && + GetChildElement(psPolygonPatch) != NULL && + EQUAL(BareGMLElement(GetChildElement(psPolygonPatch)->pszValue),"interior") ) + { + // Find all inner rings + for( const CPLXMLNode* psChild2 = psPolygonPatch->psChild; + psChild2 != NULL; + psChild2 = psChild2->psNext ) + { + if( psChild2->eType == CXT_Element + && (EQUAL(BareGMLElement(psChild2->pszValue),"interior"))) + { + const CPLXMLNode* psInteriorChild = GetChildElement(psChild2); + OGRLinearRing* poRing; + if (psInteriorChild != NULL) + poRing = (OGRLinearRing *) + GML2OGRGeometry_XMLNode_Internal( + psInteriorChild, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + else + poRing = NULL; + if (poRing == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid interior ring"); + delete poMS; + return NULL; + } + if( !EQUAL(poRing->getGeometryName(),"LINEARRING") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s: Got %.500s geometry as innerBoundaryIs instead of LINEARRING.", + pszBaseGeometry, poRing->getGeometryName() ); + delete poRing; + delete poMS; + return NULL; + } + + bReconstructTopology = TRUE; + OGRPolygon *poPolygon = new OGRPolygon(); + poPolygon->addRingDirectly( poRing ); + poMS->addGeometryDirectly( poPolygon ); + } + } + } + else + { + OGRGeometry* poGeom = + GML2OGRGeometry_XMLNode_Internal( psSurfaceChild, + bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( !GML2OGRGeometry_AddToMultiSurface(poMS, poGeom, + pszMemberElement, + bChildrenAreAllPolygons) ) + { + delete poGeom; + delete poMS; + return NULL; + } + } + } + } + else if (psChild->eType == CXT_Element + && EQUAL(pszMemberElement,"surfaceMembers") ) + { + const CPLXMLNode *psChild2; + for( psChild2 = psChild->psChild; + psChild2 != NULL; + psChild2 = psChild2->psNext ) + { + pszMemberElement = BareGMLElement(psChild2->pszValue); + if( psChild2->eType == CXT_Element + && (EQUAL(pszMemberElement,"Surface") || + EQUAL(pszMemberElement,"Polygon") || + EQUAL(pszMemberElement,"PolygonPatch") || + EQUAL(pszMemberElement,"CompositeSurface")) ) + { + OGRGeometry* poGeom = GML2OGRGeometry_XMLNode_Internal( + psChild2, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( !GML2OGRGeometry_AddToMultiSurface(poMS, poGeom, + pszMemberElement, + bChildrenAreAllPolygons) ) + { + delete poGeom; + delete poMS; + return NULL; + } + } + } + } + } + + if( bReconstructTopology && bChildrenAreAllPolygons ) + { + OGRMultiPolygon* poMPoly; + if( wkbFlatten(poMS->getGeometryType()) == wkbMultiSurface ) + poMPoly = OGRMultiSurface::CastToMultiPolygon(poMS); + else + poMPoly = (OGRMultiPolygon*)poMS; + CPLAssert(poMPoly); /* that should not fail really ! */ + int nPolygonCount = poMPoly->getNumGeometries(); + OGRGeometry** papoPolygons = new OGRGeometry*[ nPolygonCount ]; + for(int i=0;igetGeometryRef(0); + poMPoly->removeGeometry(0, FALSE); + } + delete poMPoly; + int bResultValidGeometry = FALSE; + OGRGeometry* poRet = OGRGeometryFactory::organizePolygons( + papoPolygons, nPolygonCount, &bResultValidGeometry ); + delete[] papoPolygons; + return poRet; + } + else + { + if( bCastToLinearTypeIfPossible && + wkbFlatten(poMS->getGeometryType()) == wkbMultiSurface && + bChildrenAreAllPolygons ) + { + return OGRMultiSurface::CastToMultiPolygon(poMS); + } + else + return poMS; + } + } + +/* -------------------------------------------------------------------- */ +/* MultiPoint */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"MultiPoint") ) + { + const CPLXMLNode *psChild; + OGRMultiPoint *poMP = new OGRMultiPoint(); + + // collect points. + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"pointMember") ) + { + const CPLXMLNode* psPointChild = GetChildElement(psChild); + OGRPoint *poPoint; + + if (psPointChild != NULL) + { + poPoint = (OGRPoint *) + GML2OGRGeometry_XMLNode_Internal( psPointChild, + bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( poPoint == NULL + || wkbFlatten(poPoint->getGeometryType()) != wkbPoint ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MultiPoint: Got %.500s geometry as pointMember instead of POINT", + poPoint ? poPoint->getGeometryName() : "NULL" ); + delete poPoint; + delete poMP; + return NULL; + } + + poMP->addGeometryDirectly( poPoint ); + } + } + else if (psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"pointMembers") ) + { + const CPLXMLNode *psChild2; + for( psChild2 = psChild->psChild; + psChild2 != NULL; + psChild2 = psChild2->psNext ) + { + if( psChild2->eType == CXT_Element + && (EQUAL(BareGMLElement(psChild2->pszValue),"Point")) ) + { + OGRGeometry* poGeom = GML2OGRGeometry_XMLNode_Internal( + psChild2, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if (poGeom == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid %s", + BareGMLElement(psChild2->pszValue)); + delete poMP; + return NULL; + } + + if (wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + { + poMP->addGeometryDirectly( (OGRPoint *)poGeom ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as pointMember instead of POINT.", + poGeom->getGeometryName() ); + delete poGeom; + delete poMP; + return NULL; + } + } + } + } + } + + return poMP; + } + +/* -------------------------------------------------------------------- */ +/* MultiLineString */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"MultiLineString") ) + { + const CPLXMLNode *psChild; + OGRMultiLineString *poMLS = new OGRMultiLineString(); + + // collect lines + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"lineStringMember") ) + { + const CPLXMLNode* psLineStringChild = GetChildElement(psChild); + OGRGeometry *poGeom; + + if (psLineStringChild != NULL) + poGeom = GML2OGRGeometry_XMLNode_Internal( psLineStringChild, + bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + else + poGeom = NULL; + if( poGeom == NULL + || wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MultiLineString: Got %.500s geometry as Member instead of LINESTRING.", + poGeom ? poGeom->getGeometryName() : "NULL" ); + delete poGeom; + delete poMLS; + return NULL; + } + + poMLS->addGeometryDirectly( poGeom ); + } + } + + return poMLS; + } + + +/* -------------------------------------------------------------------- */ +/* MultiCurve */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"MultiCurve") ) + { + const CPLXMLNode *psChild; + OGRMultiCurve *poMC = new OGRMultiCurve(); + int bChildrenAreAllLineString = TRUE; + + // collect curveMembers + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"curveMember") ) + { + const CPLXMLNode *psChild2 = GetChildElement(psChild); + if( psChild2 != NULL ) /* empty curveMember is valid */ + { + OGRGeometry* poGeom = GML2OGRGeometry_XMLNode_Internal( + psChild2, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( poGeom == NULL || + !OGR_GT_IsCurve(poGeom->getGeometryType()) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MultiCurve: Got %.500s geometry as Member instead of a curve.", + poGeom ? poGeom->getGeometryName() : "NULL" ); + if( poGeom != NULL ) delete poGeom; + delete poMC; + return NULL; + } + + if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) + bChildrenAreAllLineString = FALSE; + + if( poMC->addGeometryDirectly( poGeom ) != OGRERR_NONE ) + { + delete poGeom; + } + } + } + else if (psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"curveMembers") ) + { + const CPLXMLNode *psChild2; + for( psChild2 = psChild->psChild; + psChild2 != NULL; + psChild2 = psChild2->psNext ) + { + if( psChild2->eType == CXT_Element ) + { + OGRGeometry* poGeom = GML2OGRGeometry_XMLNode_Internal( + psChild2, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if (poGeom == NULL || + !OGR_GT_IsCurve(poGeom->getGeometryType()) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MultiCurve: Got %.500s geometry as Member instead of a curve.", + poGeom ? poGeom->getGeometryName() : "NULL" ); + if( poGeom != NULL ) delete poGeom; + delete poMC; + return NULL; + } + + if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) + bChildrenAreAllLineString = FALSE; + + if( poMC->addGeometryDirectly( poGeom ) != OGRERR_NONE ) + { + delete poGeom; + } + } + } + } + } + + if( bCastToLinearTypeIfPossible && bChildrenAreAllLineString ) + { + return OGRMultiCurve::CastToMultiLineString(poMC); + } + else + return poMC; + } + + +/* -------------------------------------------------------------------- */ +/* CompositeCurve */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"CompositeCurve") ) + { + const CPLXMLNode *psChild; + OGRCompoundCurve *poCC = new OGRCompoundCurve(); + int bChildrenAreAllLineString = TRUE; + + // collect curveMembers + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"curveMember") ) + { + const CPLXMLNode *psChild2 = GetChildElement(psChild); + if( psChild2 != NULL ) /* empty curveMember is valid */ + { + OGRGeometry*poGeom = GML2OGRGeometry_XMLNode_Internal( + psChild2, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( !GML2OGRGeometry_AddToCompositeCurve(poCC, poGeom, + bChildrenAreAllLineString) ) + { + delete poGeom; + delete poCC; + return NULL; + } + } + } + else if (psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"curveMembers") ) + { + const CPLXMLNode *psChild2; + for( psChild2 = psChild->psChild; + psChild2 != NULL; + psChild2 = psChild2->psNext ) + { + if( psChild2->eType == CXT_Element ) + { + OGRGeometry* poGeom = GML2OGRGeometry_XMLNode_Internal( + psChild2, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( !GML2OGRGeometry_AddToCompositeCurve(poCC, poGeom, + bChildrenAreAllLineString) ) + { + delete poGeom; + delete poCC; + return NULL; + } + } + } + } + } + + if( bCastToLinearTypeIfPossible && bChildrenAreAllLineString ) + { + return OGRCurve::CastToLineString(poCC); + } + else + return poCC; + } + +/* -------------------------------------------------------------------- */ +/* Curve */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Curve") ) + { + const CPLXMLNode *psChild; + + psChild = FindBareXMLChild( psNode, "segments"); + if( psChild == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GML3 Curve geometry lacks segments element." ); + return NULL; + } + + OGRGeometry *poGeom; + + poGeom = GML2OGRGeometry_XMLNode_Internal( + psChild, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( poGeom == NULL || + !OGR_GT_IsCurve(poGeom->getGeometryType()) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Curve: Got %.500s geometry as Member instead of segments.", + poGeom ? poGeom->getGeometryName() : "NULL" ); + if( poGeom != NULL ) delete poGeom; + return NULL; + } + + return poGeom; + } + +/* -------------------------------------------------------------------- */ +/* segments */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"segments") ) + { + const CPLXMLNode *psChild; + OGRCurve* poCurve = NULL; + OGRCompoundCurve *poCC = NULL; + int bChildrenAreAllLineString = TRUE; + + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + + { + if( psChild->eType == CXT_Element + /*&& (EQUAL(BareGMLElement(psChild->pszValue),"LineStringSegment") || + EQUAL(BareGMLElement(psChild->pszValue),"GeodesicString") || + EQUAL(BareGMLElement(psChild->pszValue),"Arc") || + EQUAL(BareGMLElement(psChild->pszValue),"Circle"))*/ ) + { + OGRGeometry *poGeom; + + poGeom = GML2OGRGeometry_XMLNode_Internal( + psChild, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( poGeom == NULL || + !OGR_GT_IsCurve(poGeom->getGeometryType()) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "segments: Got %.500s geometry as Member instead of curve.", + poGeom ? poGeom->getGeometryName() : "NULL" ); + delete poGeom; + delete poCurve; + delete poCC; + return NULL; + } + + if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) + bChildrenAreAllLineString = FALSE; + + if( poCC == NULL && poCurve == NULL ) + poCurve = (OGRCurve*)poGeom; + else + { + if( poCC == NULL ) + { + poCC = new OGRCompoundCurve(); + if( poCC->addCurveDirectly(poCurve) != OGRERR_NONE ) + { + delete poGeom; + delete poCurve; + delete poCC; + return NULL; + } + poCurve = NULL; + } + + if( poCC->addCurveDirectly((OGRCurve*)poGeom) != OGRERR_NONE ) + { + delete poGeom; + delete poCC; + return NULL; + } + } + } + } + + if( poCurve != NULL ) + return poCurve; + if( poCC == NULL ) + return NULL; + + if( bCastToLinearTypeIfPossible && bChildrenAreAllLineString ) + { + return OGRCurve::CastToLineString(poCC); + } + else + { + return poCC; + } + } + +/* -------------------------------------------------------------------- */ +/* MultiGeometry */ +/* CAUTION: OGR < 1.8.0 produced GML with GeometryCollection, which is */ +/* not a valid GML 2 keyword! The right name is MultiGeometry. Let's be */ +/* tolerant with the non compliant files we produced... */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"MultiGeometry") || + EQUAL(pszBaseGeometry,"GeometryCollection") ) + { + const CPLXMLNode *psChild; + OGRGeometryCollection *poGC = new OGRGeometryCollection(); + + // collect geoms + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"geometryMember") ) + { + const CPLXMLNode* psGeometryChild = GetChildElement(psChild); + OGRGeometry *poGeom; + + if (psGeometryChild != NULL) + { + poGeom = GML2OGRGeometry_XMLNode_Internal( + psGeometryChild, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( poGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GeometryCollection: Failed to get geometry in geometryMember" ); + delete poGeom; + delete poGC; + return NULL; + } + + poGC->addGeometryDirectly( poGeom ); + } + } + } + + return poGC; + } + +/* -------------------------------------------------------------------- */ +/* Directed Edge */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"directedEdge") ) + { + const CPLXMLNode *psEdge, + *psdirectedNode, + *psNodeElement, + *pspointProperty, + *psPoint, + *psCurveProperty, + *psCurve; + int bEdgeOrientation = TRUE, + bNodeOrientation = TRUE; + OGRGeometry *poGeom; + OGRLineString *poLineString; + OGRPoint *poPositiveNode = NULL, *poNegativeNode = NULL; + OGRMultiPoint *poMP; + + bEdgeOrientation = GetElementOrientation(psNode); + + //collect edge + psEdge = FindBareXMLChild(psNode,"Edge"); + if( psEdge == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to get Edge element in directedEdge" ); + return NULL; + } + + if( bGetSecondaryGeometry ) + { + psdirectedNode = FindBareXMLChild(psEdge,"directedNode"); + if( psdirectedNode == NULL ) goto nonode; + + bNodeOrientation = GetElementOrientation( psdirectedNode ); + + psNodeElement = FindBareXMLChild(psdirectedNode,"Node"); + if( psNodeElement == NULL ) goto nonode; + + pspointProperty = FindBareXMLChild(psNodeElement,"pointProperty"); + if( pspointProperty == NULL ) + pspointProperty = FindBareXMLChild(psNodeElement,"connectionPointProperty"); + if( pspointProperty == NULL ) goto nonode; + + psPoint = FindBareXMLChild(pspointProperty,"Point"); + if( psPoint == NULL ) + psPoint = FindBareXMLChild(pspointProperty,"ConnectionPoint"); + if( psPoint == NULL ) goto nonode; + + poGeom = GML2OGRGeometry_XMLNode_Internal( + psPoint, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName, TRUE ); + if( poGeom == NULL + || wkbFlatten(poGeom->getGeometryType()) != wkbPoint ) + { +/* CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as Member instead of POINT.", + poGeom ? poGeom->getGeometryName() : "NULL" );*/ + if( poGeom != NULL) delete poGeom; + goto nonode; + } + + if( ( bNodeOrientation == bEdgeOrientation ) != bOrientation ) + poPositiveNode = (OGRPoint *)poGeom; + else + poNegativeNode = (OGRPoint *)poGeom; + + // look for the other node + psdirectedNode = psdirectedNode->psNext; + while( psdirectedNode != NULL && + !EQUAL( psdirectedNode->pszValue, "directedNode" ) ) + psdirectedNode = psdirectedNode->psNext; + if( psdirectedNode == NULL ) goto nonode; + + if( GetElementOrientation( psdirectedNode ) == bNodeOrientation ) + goto nonode; + + psNodeElement = FindBareXMLChild(psEdge,"Node"); + if( psNodeElement == NULL ) goto nonode; + + pspointProperty = FindBareXMLChild(psNodeElement,"pointProperty"); + if( pspointProperty == NULL ) + pspointProperty = FindBareXMLChild(psNodeElement,"connectionPointProperty"); + if( pspointProperty == NULL ) goto nonode; + + psPoint = FindBareXMLChild(pspointProperty,"Point"); + if( psPoint == NULL ) + psPoint = FindBareXMLChild(pspointProperty,"ConnectionPoint"); + if( psPoint == NULL ) goto nonode; + + poGeom = GML2OGRGeometry_XMLNode_Internal( + psPoint, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName, TRUE ); + if( poGeom == NULL + || wkbFlatten(poGeom->getGeometryType()) != wkbPoint ) + { +/* CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as Member instead of POINT.", + poGeom ? poGeom->getGeometryName() : "NULL" );*/ + if( poGeom != NULL) delete poGeom; + goto nonode; + } + + if( ( bNodeOrientation == bEdgeOrientation ) != bOrientation ) + poNegativeNode = (OGRPoint *)poGeom; + else + poPositiveNode = (OGRPoint *)poGeom; + + poMP = new OGRMultiPoint(); + poMP->addGeometryDirectly( poNegativeNode ); + poMP->addGeometryDirectly( poPositiveNode ); + + return poMP; + + nonode:; + } + + // collect curveproperty + psCurveProperty = FindBareXMLChild(psEdge,"curveProperty"); + if( psCurveProperty == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "directedEdge: Failed to get curveProperty in Edge" ); + return NULL; + } + + psCurve = FindBareXMLChild(psCurveProperty,"LineString"); + if( psCurve == NULL ) + psCurve = FindBareXMLChild(psCurveProperty,"Curve"); + if( psCurve == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "directedEdge: Failed to get LineString or Curve tag in curveProperty" ); + return NULL; + } + + poLineString = (OGRLineString *)GML2OGRGeometry_XMLNode_Internal( + psCurve, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName, TRUE ); + if( poLineString == NULL + || wkbFlatten(poLineString->getGeometryType()) != wkbLineString ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as Member instead of LINESTRING.", + poLineString ? poLineString->getGeometryName() : "NULL" ); + if( poLineString != NULL ) + delete poLineString; + return NULL; + } + + if( bGetSecondaryGeometry ) + { + // choose a point based on the orientation + poNegativeNode = new OGRPoint(); + poPositiveNode = new OGRPoint(); + if( bEdgeOrientation == bOrientation ) + { + poLineString->StartPoint( poNegativeNode ); + poLineString->EndPoint( poPositiveNode ); + } + else + { + poLineString->StartPoint( poPositiveNode ); + poLineString->EndPoint( poNegativeNode ); + } + delete poLineString; + + poMP = new OGRMultiPoint(); + poMP->addGeometryDirectly( poNegativeNode ); + poMP->addGeometryDirectly( poPositiveNode ); + + return poMP; + } + + // correct orientation of the line string + if( bEdgeOrientation != bOrientation ) + { + int iStartCoord = 0, iEndCoord = poLineString->getNumPoints() - 1; + OGRPoint *poTempStartPoint = new OGRPoint(); + OGRPoint *poTempEndPoint = new OGRPoint(); + while( iStartCoord < iEndCoord ) + { + poLineString->getPoint( iStartCoord, poTempStartPoint ); + poLineString->getPoint( iEndCoord, poTempEndPoint ); + poLineString->setPoint( iStartCoord, poTempEndPoint ); + poLineString->setPoint( iEndCoord, poTempStartPoint ); + iStartCoord++; + iEndCoord--; + } + delete poTempStartPoint; + delete poTempEndPoint; + } + return poLineString; + } + +/* -------------------------------------------------------------------- */ +/* TopoCurve */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"TopoCurve") ) + { + const CPLXMLNode *psChild; + OGRMultiLineString *poMLS = NULL; + OGRMultiPoint *poMP = NULL; + + if( bGetSecondaryGeometry ) + poMP = new OGRMultiPoint(); + else + poMLS = new OGRMultiLineString(); + + // collect directedEdges + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"directedEdge")) + { + OGRGeometry *poGeom; + + poGeom = GML2OGRGeometry_XMLNode_Internal( + psChild, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( poGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to get geometry in directedEdge" ); + delete poGeom; + if( bGetSecondaryGeometry ) + delete poMP; + else + delete poMLS; + return NULL; + } + + //Add the two points corresponding to the two nodes to poMP + if( bGetSecondaryGeometry && + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint ) + { + //TODO: TopoCurve geometries with more than one + // directedEdge elements were not tested. + if( poMP->getNumGeometries() <= 0 || + !(poMP->getGeometryRef( poMP->getNumGeometries() - 1 )->Equals(((OGRMultiPoint *)poGeom)->getGeometryRef( 0 ) ) )) + { + poMP->addGeometry( + ( (OGRMultiPoint *)poGeom )->getGeometryRef( 0 ) ); + } + poMP->addGeometry( + ( (OGRMultiPoint *)poGeom )->getGeometryRef( 1 ) ); + delete poGeom; + } + else if( !bGetSecondaryGeometry && + wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) + { + poMLS->addGeometryDirectly( poGeom ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Got %.500s geometry as Member instead of %s.", + poGeom ? poGeom->getGeometryName() : "NULL", + bGetSecondaryGeometry?"MULTIPOINT":"LINESTRING"); + delete poGeom; + if( bGetSecondaryGeometry ) + delete poMP; + else + delete poMLS; + return NULL; + } + } + } + + if( bGetSecondaryGeometry ) + return poMP; + else + return poMLS; + } + +/* -------------------------------------------------------------------- */ +/* TopoSurface */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"TopoSurface") ) + { + /****************************************************************/ + /* applying the FaceHoleNegative = FALSE rules */ + /* */ + /* - each is expected to represent a MultiPolygon */ + /* - each is expected to represent a distinct Polygon, */ + /* this including any possible Interior Ring (holes); */ + /* orientation="+/-" plays no role at all to identify "holes" */ + /* - each within a may indifferently represent */ + /* an element of the Exterior or Interior Boundary; relative */ + /* order of is absolutely irrelevant. */ + /****************************************************************/ + /* Contributor: Alessandro Furieri, a.furieri@lqt.it */ + /* Developed for Faunalia (http://www.faunalia.it) */ + /* with funding from Regione Toscana - */ + /* Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE */ + /****************************************************************/ + if(bFaceHoleNegative != TRUE) + { + if( bGetSecondaryGeometry ) + return NULL; + +#ifndef HAVE_GEOS + static int bWarningAlreadyEmitted = FALSE; + if (!bWarningAlreadyEmitted) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Interpreating that GML TopoSurface geometry requires GDAL to be built with GEOS support.\n" + "As a workaround, you can try defining the GML_FACE_HOLE_NEGATIVE configuration option\n" + "to YES, so that the 'old' interpretation algorithm is used. But be warned that\n" + "the result might be incorrect.\n"); + bWarningAlreadyEmitted = TRUE; + } + return NULL; +#else + const CPLXMLNode *psChild, *psFaceChild, *psDirectedEdgeChild; + OGRMultiPolygon *poTS = new OGRMultiPolygon(); + + // collect directed faces + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"directedFace") ) + { + // collect next face (psChild->psChild) + psFaceChild = GetChildElement(psChild); + + while( psFaceChild != NULL && + !(psFaceChild->eType == CXT_Element && + EQUAL(BareGMLElement(psFaceChild->pszValue),"Face")) ) + psFaceChild = psFaceChild->psNext; + + if( psFaceChild == NULL ) + continue; + + OGRMultiLineString *poCollectedGeom = new OGRMultiLineString(); + + // collect directed edges of the face + for( psDirectedEdgeChild = psFaceChild->psChild; + psDirectedEdgeChild != NULL; + psDirectedEdgeChild = psDirectedEdgeChild->psNext ) + { + if( psDirectedEdgeChild->eType == CXT_Element && + EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue),"directedEdge") ) + { + OGRGeometry *poEdgeGeom; + + poEdgeGeom = GML2OGRGeometry_XMLNode_Internal( psDirectedEdgeChild, + bGetSecondaryGeometryOption, + nRecLevel + 1, + nSRSDimension, + pszSRSName, + TRUE ); + + if( poEdgeGeom == NULL || + wkbFlatten(poEdgeGeom->getGeometryType()) != wkbLineString ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to get geometry in directedEdge" ); + delete poEdgeGeom; + delete poCollectedGeom; + delete poTS; + return NULL; + } + + poCollectedGeom->addGeometryDirectly( poEdgeGeom ); + } + } + + OGRGeometry *poFaceCollectionGeom = NULL; + OGRPolygon *poFaceGeom = NULL; + +//#ifdef HAVE_GEOS + poFaceCollectionGeom = poCollectedGeom->Polygonize(); + if( poFaceCollectionGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to assemble Edges in Face" ); + delete poCollectedGeom; + delete poTS; + return NULL; + } + + poFaceGeom = GML2FaceExtRing( poFaceCollectionGeom ); +//#else +// poFaceGeom = (OGRPolygon*) OGRBuildPolygonFromEdges( +// (OGRGeometryH) poCollectedGeom, +// FALSE, TRUE, 0, NULL); +//#endif + + if( poFaceGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to build Polygon for Face" ); + delete poCollectedGeom; + delete poTS; + return NULL; + } + else + { + int iCount = poTS->getNumGeometries(); + if( iCount == 0) + { + /* inserting the first Polygon */ + poTS->addGeometryDirectly( poFaceGeom ); + } + else + { + /* using Union to add the current Polygon */ + OGRGeometry *poUnion = poTS->Union( poFaceGeom ); + delete poFaceGeom; + delete poTS; + if( poUnion == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed Union for TopoSurface" ); + return NULL; + } + if( wkbFlatten( poUnion->getGeometryType()) == wkbPolygon ) + { + /* forcing to be a MultiPolygon */ + poTS = new OGRMultiPolygon(); + poTS->addGeometryDirectly(poUnion); + } + else if( wkbFlatten( poUnion->getGeometryType()) == wkbMultiPolygon ) + poTS = (OGRMultiPolygon *)poUnion; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unexpected geometry type resulting from Union for TopoSurface" ); + delete poUnion; + return NULL; + } + } + } + delete poFaceCollectionGeom; + delete poCollectedGeom; + } + } + + return poTS; +#endif // HAVE_GEOS + } + + /****************************************************************/ + /* applying the FaceHoleNegative = TRUE rules */ + /* */ + /* - each is expected to represent a MultiPolygon */ + /* - any declaring orientation="+" is expected to */ + /* represent an Exterior Ring (no holes are allowed) */ + /* - any declaring orientation="-" is expected to */ + /* represent an Interior Ring (hole) belonging to the latest */ + /* Exterior Ring. */ + /* - within the same are expected to be */ + /* arranged in geometrically adjacent and consecutive */ + /* sequence. */ + /****************************************************************/ + if( bGetSecondaryGeometry ) + return NULL; + const CPLXMLNode *psChild, *psFaceChild, *psDirectedEdgeChild; + int bFaceOrientation = TRUE; + OGRPolygon *poTS = new OGRPolygon(); + + // collect directed faces + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"directedFace") ) + { + bFaceOrientation = GetElementOrientation(psChild); + + // collect next face (psChild->psChild) + psFaceChild = GetChildElement(psChild); + while( psFaceChild != NULL && + !EQUAL(BareGMLElement(psFaceChild->pszValue),"Face") ) + psFaceChild = psFaceChild->psNext; + + if( psFaceChild == NULL ) + continue; + + OGRLinearRing *poFaceGeom = new OGRLinearRing(); + + // collect directed edges of the face + for( psDirectedEdgeChild = psFaceChild->psChild; + psDirectedEdgeChild != NULL; + psDirectedEdgeChild = psDirectedEdgeChild->psNext ) + { + if( psDirectedEdgeChild->eType == CXT_Element && + EQUAL(BareGMLElement(psDirectedEdgeChild->pszValue),"directedEdge") ) + { + OGRGeometry *poEdgeGeom; + + poEdgeGeom = GML2OGRGeometry_XMLNode_Internal( psDirectedEdgeChild, + bGetSecondaryGeometryOption, + nRecLevel + 1, + nSRSDimension, + pszSRSName, + TRUE, + bFaceOrientation ); + + if( poEdgeGeom == NULL || + wkbFlatten(poEdgeGeom->getGeometryType()) != wkbLineString ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to get geometry in directedEdge" ); + delete poEdgeGeom; + delete poFaceGeom; + delete poTS; + return NULL; + } + + OGRLineString *poLS; + OGRLineString *poAddLS; + if( !bFaceOrientation ) + { + poLS = (OGRLineString *)poEdgeGeom; + poAddLS = (OGRLineString *)poFaceGeom; + if( poAddLS->getNumPoints() < 2 ) + { + /* skip it */ + } + else if( poLS->getNumPoints() > 0 + && fabs(poLS->getX(poLS->getNumPoints()-1) + - poAddLS->getX(0)) < 1e-14 + && fabs(poLS->getY(poLS->getNumPoints()-1) + - poAddLS->getY(0)) < 1e-14 + && fabs(poLS->getZ(poLS->getNumPoints()-1) + - poAddLS->getZ(0)) < 1e-14) + { + // Skip the first point of the new linestring to avoid + // invalidate duplicate points + poLS->addSubLineString( poAddLS, 1 ); + } + else + { + // Add the whole new line string + poLS->addSubLineString( poAddLS ); + } + poFaceGeom->empty(); + } + poLS = (OGRLineString *)poFaceGeom; + poAddLS = (OGRLineString *)poEdgeGeom; + if( poAddLS->getNumPoints() < 2 ) + { + /* skip it */ + } + else if( poLS->getNumPoints() > 0 + && fabs(poLS->getX(poLS->getNumPoints()-1) + - poAddLS->getX(0)) < 1e-14 + && fabs(poLS->getY(poLS->getNumPoints()-1) + - poAddLS->getY(0)) < 1e-14 + && fabs(poLS->getZ(poLS->getNumPoints()-1) + - poAddLS->getZ(0)) < 1e-14) + { + // Skip the first point of the new linestring to avoid + // invalidate duplicate points + poLS->addSubLineString( poAddLS, 1 ); + } + else + { + // Add the whole new line string + poLS->addSubLineString( poAddLS ); + } + delete poEdgeGeom; + } + } + +/* if( poFaceGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to get Face geometry in directedFace" ); + delete poFaceGeom; + return NULL; + }*/ + + poTS->addRingDirectly( poFaceGeom ); + } + } + +/* if( poTS == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to get TopoSurface geometry" ); + delete poTS; + return NULL; + }*/ + + return poTS; + } + +/* -------------------------------------------------------------------- */ +/* Surface */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Surface") ) + { + const CPLXMLNode *psChild; + + // Find outer ring. + psChild = FindBareXMLChild( psNode, "patches" ); + if( psChild == NULL ) + psChild = FindBareXMLChild( psNode, "polygonPatches" ); + if( psChild == NULL ) + psChild = FindBareXMLChild( psNode, "trianglePatches" ); + + psChild = GetChildElement(psChild); + if( psChild == NULL ) + { + /* and are valid GML */ + return new OGRPolygon(); + } + + OGRMultiSurface* poMS = NULL; + OGRGeometry* poResult = NULL; + for( ; psChild != NULL; psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && (EQUAL(BareGMLElement(psChild->pszValue),"PolygonPatch") || + EQUAL(BareGMLElement(psChild->pszValue),"Triangle") || + EQUAL(BareGMLElement(psChild->pszValue),"Rectangle"))) + { + OGRGeometry *poGeom = + GML2OGRGeometry_XMLNode_Internal( + psChild, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( poGeom == NULL ) + { + delete poResult; + return NULL; + } + + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + + if( poResult == NULL ) + poResult = poGeom; + else + { + if( poMS == NULL ) + { + if( wkbFlatten(poResult->getGeometryType()) == wkbPolygon && + eGeomType == wkbPolygon ) + poMS = new OGRMultiPolygon(); + else + poMS = new OGRMultiSurface(); +#ifdef DEBUG + OGRErr eErr = +#endif + poMS->addGeometryDirectly( poResult ); + CPLAssert(eErr == OGRERR_NONE); + poResult = poMS; + } + else if( eGeomType != wkbPolygon && + wkbFlatten(poMS->getGeometryType()) == wkbMultiPolygon ) + { + poMS = OGRMultiPolygon::CastToMultiSurface((OGRMultiPolygon*)poMS); + poResult = poMS; + } +#ifdef DEBUG + OGRErr eErr = +#endif + poMS->addGeometryDirectly( poGeom ); + CPLAssert(eErr == OGRERR_NONE); + } + } + } + + return poResult; + } + +/* -------------------------------------------------------------------- */ +/* TriangulatedSurface */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"TriangulatedSurface") || + EQUAL(pszBaseGeometry,"Tin") ) + { + const CPLXMLNode *psChild; + OGRGeometry *poResult = NULL; + + // Find trianglePatches + psChild = FindBareXMLChild( psNode, "trianglePatches" ); + if (psChild == NULL) + psChild = FindBareXMLChild( psNode, "patches" ); + + psChild = GetChildElement(psChild); + if( psChild == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing for %s.", pszBaseGeometry ); + return NULL; + } + + for( ; psChild != NULL; psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Element + && EQUAL(BareGMLElement(psChild->pszValue),"Triangle") ) + { + OGRPolygon *poPolygon = (OGRPolygon *) + GML2OGRGeometry_XMLNode_Internal( + psChild, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( poPolygon == NULL ) + return NULL; + + if( poResult == NULL ) + poResult = poPolygon; + else if( wkbFlatten(poResult->getGeometryType()) == wkbPolygon ) + { + OGRMultiPolygon *poMP = new OGRMultiPolygon(); + poMP->addGeometryDirectly( poResult ); + poMP->addGeometryDirectly( poPolygon ); + poResult = poMP; + } + else + { + ((OGRMultiPolygon *) poResult)->addGeometryDirectly( poPolygon ); + } + } + } + + return poResult; + } + +/* -------------------------------------------------------------------- */ +/* Solid */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"Solid") ) + { + const CPLXMLNode *psChild; + OGRGeometry* poGeom; + + // Find exterior element + psChild = FindBareXMLChild( psNode, "exterior"); + + psChild = GetChildElement(psChild); + if( psChild == NULL ) + { + /* and are valid GML */ + return new OGRPolygon(); + } + + // Get the geometry inside + poGeom = GML2OGRGeometry_XMLNode_Internal( + psChild, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + if( poGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid exterior element"); + delete poGeom; + return NULL; + } + + psChild = FindBareXMLChild( psNode, "interior"); + if( psChild != NULL ) + { + static int bWarnedOnce = FALSE; + if (!bWarnedOnce) + { + CPLError( CE_Warning, CPLE_AppDefined, + " elements of are ignored"); + bWarnedOnce = TRUE; + } + } + + return poGeom; + } + +/* -------------------------------------------------------------------- */ +/* OrientableSurface */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"OrientableSurface") ) + { + const CPLXMLNode *psChild; + + // Find baseSurface. + psChild = FindBareXMLChild( psNode, "baseSurface" ); + + psChild = GetChildElement(psChild); + if( psChild == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing for OrientableSurface." ); + return NULL; + } + + return GML2OGRGeometry_XMLNode_Internal( + psChild, bGetSecondaryGeometryOption, + nRecLevel + 1, nSRSDimension, pszSRSName ); + } + +/* -------------------------------------------------------------------- */ +/* SimplePolygon, SimpleRectangle, SimpleTriangle */ +/* (GML 3.3 compact encoding) */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"SimplePolygon") || + EQUAL(pszBaseGeometry,"SimpleRectangle") || + EQUAL(pszBaseGeometry,"SimpleTriangle") ) + { + OGRLinearRing *poRing = new OGRLinearRing(); + + if( !ParseGMLCoordinates( psNode, poRing, nSRSDimension ) ) + { + delete poRing; + return NULL; + } + + poRing->closeRings(); + + OGRPolygon* poPolygon = new OGRPolygon(); + poPolygon->addRingDirectly(poRing); + return poPolygon; + } + +/* -------------------------------------------------------------------- */ +/* SimpleMultiPoint (GML 3.3 compact encoding) */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszBaseGeometry,"SimpleMultiPoint") ) + { + OGRLineString *poLS = new OGRLineString(); + + if( !ParseGMLCoordinates( psNode, poLS, nSRSDimension ) ) + { + delete poLS; + return NULL; + } + + OGRMultiPoint* poMP = new OGRMultiPoint(); + int nPoints = poLS->getNumPoints(); + for(int i = 0; i < nPoints; i++) + { + OGRPoint* poPoint = new OGRPoint(); + poLS->getPoint(i, poPoint); + poMP->addGeometryDirectly(poPoint); + } + delete poLS; + return poMP; + } + + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised geometry type <%.500s>.", + pszBaseGeometry ); + + return NULL; +} + +/************************************************************************/ +/* OGR_G_CreateFromGMLTree() */ +/************************************************************************/ + +OGRGeometryH OGR_G_CreateFromGMLTree( const CPLXMLNode *psTree ) + +{ + return (OGRGeometryH) GML2OGRGeometry_XMLNode( psTree, -1 ); +} + +/************************************************************************/ +/* OGR_G_CreateFromGML() */ +/************************************************************************/ + +/** + * \brief Create geometry from GML. + * + * This method translates a fragment of GML containing only the geometry + * portion into a corresponding OGRGeometry. There are many limitations + * on the forms of GML geometries supported by this parser, but they are + * too numerous to list here. + * + * The following GML2 elements are parsed : Point, LineString, Polygon, + * MultiPoint, MultiLineString, MultiPolygon, MultiGeometry. + * + * (OGR >= 1.8.0) The following GML3 elements are parsed : Surface, MultiSurface, + * PolygonPatch, Triangle, Rectangle, Curve, MultiCurve, CompositeCurve, + * LineStringSegment, Arc, Circle, CompositeSurface, OrientableSurface, Solid, + * Tin, TriangulatedSurface. + * + * Arc and Circle elements are stroked to linestring, by using a + * 4 degrees step, unless the user has overridden the value with the + * OGR_ARC_STEPSIZE configuration variable. + * + * The C++ method OGRGeometryFactory::createFromGML() is the same as this function. + * + * @param pszGML The GML fragment for the geometry. + * + * @return a geometry on succes, or NULL on error. + */ + +OGRGeometryH OGR_G_CreateFromGML( const char *pszGML ) + +{ + if( pszGML == NULL || strlen(pszGML) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GML Geometry is empty in OGR_G_CreateFromGML()." ); + return NULL; + } + +/* ------------------------------------------------------------ -------- */ +/* Try to parse the XML snippet using the MiniXML API. If this */ +/* fails, we assume the minixml api has already posted a CPL */ +/* error, and just return NULL. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psGML = CPLParseXMLString( pszGML ); + + if( psGML == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Convert geometry recursively. */ +/* -------------------------------------------------------------------- */ + OGRGeometry *poGeometry; + + /* Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer() and GMLReader::GMLReader() */ + int bFaceHoleNegative = CSLTestBoolean(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")); + poGeometry = GML2OGRGeometry_XMLNode( psGML, -1, 0, 0, FALSE, TRUE, bFaceHoleNegative ); + + CPLDestroyXMLNode( psGML ); + + return (OGRGeometryH) poGeometry; +} + + diff --git a/bazaar/plugin/gdal/ogr/index.dox b/bazaar/plugin/gdal/ogr/index.dox new file mode 100644 index 000000000..6705509d5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/index.dox @@ -0,0 +1,114 @@ +/*! \mainpage OGR Simple Feature Library + +The OGR Simple Features Library is a C++ +open source library (and commandline +tools) providing read (and sometimes write) access to a variety of vector file +formats including ESRI Shapefiles, S-57, SDTS, PostGIS, Oracle Spatial, +and Mapinfo mid/mif and TAB formats.

    + +OGR is a part of the GDAL library.

    + +

    Resources

    + + + +

    Download

    + +

    Ready to Use Executables

    + +The best way to get OGR utilities +in ready-to-use form is to download the latest +FWTools kit +for your platform. While large, these include builds of the OGR utilities +with lots of optional components built-in. Once downloaded follow the +included instructions to setup your path and other environment variables +correctly, and then you can use the various OGR utilities from the command +line. The kits also include OpenEV, +a viewer that will display OGR supported vector files.

    + +

    Source

    + +The source code for this effort is intended to be available as OpenSource +using an X Consortium style license. The OGR library is currently a +loosely coupled subcomponent of the +GDAL library, so you get all +of GDAL for the "price" of OGR. +See the GDAL Download and +Building pages for +details on getting the source and building it.

    + +

    Bug Reporting

    + +GDAL/OGR bugs +can be reported, and +can be +listed using Trac.

    + +

    Mailing Lists

    + +A gdal-announce mailing list subscription is a low volume way of keeping track of major +developments with the GDAL/OGR project. + +The gdal-dev@lists.osgeo.org +mailing list can be used for discussion of development and user issues related +to OGR and related technologies. Subscriptions can be done, and archives +reviewed on +the web.

    + +

    Alternative Bindings for the OGR API

    + +In addition to the C++ API primarily addressed in the online documentation, +there is also a slightly less complete C API implemented on top of the C++ +API, and access available from Python.

    + +The C API is primarily intended to provide a less fragile API since slight +changes in the C++ API (such as const correctness changes) can cause changes +in method and class signatures that prevent use of new DLLs with older clients. +The C API is also generally easy to call from other languages which allow call +out to DLLs functions, such as Visual Basic, or Delphi. The API can be +explored in the ogr_api.h include file. +The gdal/ogr/ogr_capi_test.c is a small sample program demonstrating use +of the C API.

    + +The Python API isn't really well documented at this time, but parallels the +C/C++ APIs. The interface classes can be browsed in the pymod/ogr.py +(simple features) and pymod/osr.py +(coordinate systems) python modules. The pymod/samples/assemblepoly.py +sample script is one demonstration of using the python API.

    + +*/ diff --git a/bazaar/plugin/gdal/ogr/makefile.vc b/bazaar/plugin/gdal/ogr/makefile.vc new file mode 100644 index 000000000..746e58896 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/makefile.vc @@ -0,0 +1,74 @@ + +LINKFLAGS = /Zi /MTd +EXTRAFLAGS = -DWIN32 -I. -Iogrsf_frmts -Iogrsf_frmts\mem -I..\frmts\gtiff\libgeotiff -I..\frmts\zlib \ + $(PROJ_FLAGS) $(PROJ_INCLUDE) $(GEOS_CFLAGS) + +GDAL_ROOT = .. + +!INCLUDE ..\nmake.opt + +!IFDEF INCLUDE_OGR_FRMTS +EXTRAFLAGS = $(EXTRAFLAGS) -DOGR_ENABLED -DHAVE_MITAB +!ELSE +EXTRAFLAGS = $(EXTRAFLAGS) -DHAVE_MITAB +!ENDIF + +!IFDEF EXPAT_DIR +EXTRAFLAGS = $(EXTRAFLAGS) $(EXPAT_INCLUDE) -DHAVE_EXPAT +!ENDIF + + +OGR_FRMTS = ogrsf_frmts\ogrsf_frmts.lib ogrsf_frmts\ogrsf_frmts_sup.lib +OBJ_OGR = ogrgeometryfactory.obj ogrpoint.obj ogrcurve.obj ogrsurface.obj ogr_api.obj \ + ogrlinestring.obj ogrpolygon.obj ogrlinearring.obj \ + ogrutils.obj ogrgeometry.obj ogrgeometrycollection.obj \ + ogrmultipolygon.obj ogrmultilinestring.obj ogr_opt.obj \ + ogrmultipoint.obj ogrcircularstring.obj ogrcompoundcurve.obj \ + ogrcurvepolygon.obj ogrcurvecollection.obj ogrmultisurface.obj \ + ogrmulticurve.obj ogrfeature.obj ogrfeaturedefn.obj \ + ogrfielddefn.obj ogr_srsnode.obj ogrspatialreference.obj \ + ogr_srs_proj4.obj ogr_fromepsg.obj ogrct.obj \ + ogrfeaturestyle.obj ogr_srs_esri.obj ogrfeaturequery.obj \ + ogr_srs_validate.obj ogr_srs_xml.obj ograssemblepolygon.obj \ + ogr2gmlgeometry.obj gml2ogrgeometry.obj ogr_srs_pci.obj \ + ogr_srs_usgs.obj ogr_srs_dict.obj ogr_srs_panorama.obj \ + ogr_srs_ozi.obj ogr_srs_erm.obj ogr_expat.obj \ + swq.obj swq_parser.obj swq_select.obj swq_op_registrar.obj \ + swq_op_general.obj swq_expr_node.obj ogrpgeogeometry.obj \ + ogrgeomediageometry.obj ogr_geocoding.obj osr_cs_wkt.obj \ + osr_cs_wkt_parser.obj ogrgeomfielddefn.obj ograpispy.obj + +default: ogr.lib + +ogrsf_frmts\ogrsf_frmts.lib: + +sublibs: frmts + +frmts: + cd ogrsf_frmts + $(MAKE) /f makefile.vc + cd .. + +all: frmts default + +ogr.lib: $(OBJ_OGR) + lib /nologo /out:ogr.lib $(OBJ_OGR) + +install: default + +clean: + -del *.obj + -del *.lib + -del *.pdb + -del *.ilk + +allclean: clean + cd ogrsf_frmts + $(MAKE) /f makefile.vc clean + cd .. + +# special override to avoid some warnings with this generated code. + +swq_parser.obj: swq_parser.cpp + $(CC) $(CFLAGS) $(SOFTWARNFLAGS) /c $*.cpp + diff --git a/bazaar/plugin/gdal/ogr/ogr2gmlgeometry.cpp b/bazaar/plugin/gdal/ogr/ogr2gmlgeometry.cpp new file mode 100644 index 000000000..b1304a9fd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr2gmlgeometry.cpp @@ -0,0 +1,1142 @@ +/****************************************************************************** + * $Id: ogr2gmlgeometry.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: GML Translator + * Purpose: Code to translate OGRGeometry to GML string representation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ***************************************************************************** + * + * Independent Security Audit 2003/04/17 Andrey Kiselev: + * Completed audit of this module. All functions may be used without buffer + * overflows and stack corruptions if caller could be trusted. + * + * Security Audit 2003/03/28 warmerda: + * Completed security audit. I believe that this module may be safely used + * to generate GML from arbitrary but well formed OGRGeomety objects that + * come from a potentially hostile source, but through a trusted OGR importer + * without compromising the system. + * + */ + +#include "cpl_minixml.h" +#include "ogr_geometry.h" +#include "ogr_api.h" +#include "ogr_p.h" +#include "cpl_error.h" +#include "cpl_conv.h" + +#define SRSDIM_LOC_GEOMETRY (1 << 0) +#define SRSDIM_LOC_POSLIST (1 << 1) + +/************************************************************************/ +/* MakeGMLCoordinate() */ +/************************************************************************/ + +static void MakeGMLCoordinate( char *pszTarget, + double x, double y, double z, int b3D ) + +{ + OGRMakeWktCoordinate( pszTarget, x, y, z, b3D ? 3 : 2 ); + while( *pszTarget != '\0' ) + { + if( *pszTarget == ' ' ) + *pszTarget = ','; + pszTarget++; + } + +#ifdef notdef + if( !b3D ) + { + if( x == (int) x && y == (int) y ) + sprintf( pszTarget, "%d,%d", (int) x, (int) y ); + else if( fabs(x) < 370 && fabs(y) < 370 ) + CPLsprintf( pszTarget, "%.16g,%.16g", x, y ); + else if( fabs(x) > 100000000.0 || fabs(y) > 100000000.0 ) + CPLsprintf( pszTarget, "%.16g,%.16g", x, y ); + else + CPLsprintf( pszTarget, "%.3f,%.3f", x, y ); + } + else + { + if( x == (int) x && y == (int) y && z == (int) z ) + sprintf( pszTarget, "%d,%d,%d", (int) x, (int) y, (int) z ); + else if( fabs(x) < 370 && fabs(y) < 370 ) + CPLsprintf( pszTarget, "%.16g,%.16g,%.16g", x, y, z ); + else if( fabs(x) > 100000000.0 || fabs(y) > 100000000.0 + || fabs(z) > 100000000.0 ) + CPLsprintf( pszTarget, "%.16g,%.16g,%.16g", x, y, z ); + else + CPLsprintf( pszTarget, "%.3f,%.3f,%.3f", x, y, z ); + } +#endif +} + +/************************************************************************/ +/* _GrowBuffer() */ +/************************************************************************/ + +static void _GrowBuffer( int nNeeded, char **ppszText, int *pnMaxLength ) + +{ + if( nNeeded+1 >= *pnMaxLength ) + { + *pnMaxLength = MAX(*pnMaxLength * 2,nNeeded+1); + *ppszText = (char *) CPLRealloc(*ppszText, *pnMaxLength); + } +} + +/************************************************************************/ +/* AppendString() */ +/************************************************************************/ + +static void AppendString( char **ppszText, int *pnLength, int *pnMaxLength, + const char *pszTextToAppend ) + +{ + _GrowBuffer( *pnLength + strlen(pszTextToAppend) + 1, + ppszText, pnMaxLength ); + + strcat( *ppszText + *pnLength, pszTextToAppend ); + *pnLength += strlen( *ppszText + *pnLength ); +} + + +/************************************************************************/ +/* AppendCoordinateList() */ +/************************************************************************/ + +static void AppendCoordinateList( OGRLineString *poLine, + char **ppszText, int *pnLength, + int *pnMaxLength ) + +{ + char szCoordinate[256]; + int b3D = wkbHasZ(poLine->getGeometryType()); + + *pnLength += strlen(*ppszText + *pnLength); + _GrowBuffer( *pnLength + 20, ppszText, pnMaxLength ); + + strcat( *ppszText + *pnLength, "" ); + *pnLength += strlen(*ppszText + *pnLength); + + + for( int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++ ) + { + MakeGMLCoordinate( szCoordinate, + poLine->getX(iPoint), + poLine->getY(iPoint), + poLine->getZ(iPoint), + b3D ); + _GrowBuffer( *pnLength + strlen(szCoordinate)+1, + ppszText, pnMaxLength ); + + if( iPoint != 0 ) + strcat( *ppszText + *pnLength, " " ); + + strcat( *ppszText + *pnLength, szCoordinate ); + *pnLength += strlen(*ppszText + *pnLength); + } + + _GrowBuffer( *pnLength + 20, ppszText, pnMaxLength ); + strcat( *ppszText + *pnLength, "" ); + *pnLength += strlen(*ppszText + *pnLength); +} + +/************************************************************************/ +/* OGR2GMLGeometryAppend() */ +/************************************************************************/ + +static int OGR2GMLGeometryAppend( OGRGeometry *poGeometry, + char **ppszText, int *pnLength, + int *pnMaxLength, + int bIsSubGeometry ) + +{ + +/* -------------------------------------------------------------------- */ +/* Check for Spatial Reference System attached to given geometry */ +/* -------------------------------------------------------------------- */ + + // Buffer for srsName attribute (srsName="...") + char szAttributes[30] = { 0 }; + int nAttrsLength = 0; + + const OGRSpatialReference* poSRS = NULL; + poSRS = poGeometry->getSpatialReference(); + + if( NULL != poSRS && !bIsSubGeometry ) + { + const char* pszAuthName = NULL; + const char* pszAuthCode = NULL; + const char* pszTarget = NULL; + + if (poSRS->IsProjected()) + pszTarget = "PROJCS"; + else + pszTarget = "GEOGCS"; + + pszAuthName = poSRS->GetAuthorityName( pszTarget ); + if( NULL != pszAuthName ) + { + if( EQUAL( pszAuthName, "EPSG" ) ) + { + pszAuthCode = poSRS->GetAuthorityCode( pszTarget ); + if( NULL != pszAuthCode && strlen(pszAuthCode) < 10 ) + { + sprintf( szAttributes, " srsName=\"%s:%s\"", + pszAuthName, pszAuthCode ); + + nAttrsLength = strlen(szAttributes); + } + } + } + } + + OGRwkbGeometryType eType = poGeometry->getGeometryType(); + OGRwkbGeometryType eFType = wkbFlatten(eType); + +/* -------------------------------------------------------------------- */ +/* 2D Point */ +/* -------------------------------------------------------------------- */ + if( eType == wkbPoint ) + { + char szCoordinate[256]; + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + MakeGMLCoordinate( szCoordinate, + poPoint->getX(), poPoint->getY(), 0.0, FALSE ); + + _GrowBuffer( *pnLength + strlen(szCoordinate) + 60 + nAttrsLength, + ppszText, pnMaxLength ); + + sprintf( *ppszText + *pnLength, + "%s", + szAttributes, szCoordinate ); + + *pnLength += strlen( *ppszText + *pnLength ); + } +/* -------------------------------------------------------------------- */ +/* 3D Point */ +/* -------------------------------------------------------------------- */ + else if( eType == wkbPoint25D ) + { + char szCoordinate[256]; + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + MakeGMLCoordinate( szCoordinate, + poPoint->getX(), poPoint->getY(), poPoint->getZ(), + TRUE ); + + _GrowBuffer( *pnLength + strlen(szCoordinate) + 70 + nAttrsLength, + ppszText, pnMaxLength ); + + sprintf( *ppszText + *pnLength, + "%s", + szAttributes, szCoordinate ); + + *pnLength += strlen( *ppszText + *pnLength ); + } + +/* -------------------------------------------------------------------- */ +/* LineString and LinearRing */ +/* -------------------------------------------------------------------- */ + else if( eFType == wkbLineString ) + { + int bRing = EQUAL(poGeometry->getGeometryName(),"LINEARRING"); + + // Buffer for tag name + srsName attribute if set + const size_t nLineTagLength = 16; + char* pszLineTagName = NULL; + pszLineTagName = (char *) CPLMalloc( nLineTagLength + nAttrsLength + 1 ); + + if( bRing ) + { + sprintf( pszLineTagName, "", szAttributes ); + + AppendString( ppszText, pnLength, pnMaxLength, + pszLineTagName ); + } + else + { + sprintf( pszLineTagName, "", szAttributes ); + + AppendString( ppszText, pnLength, pnMaxLength, + pszLineTagName ); + } + + // FREE TAG BUFFER + CPLFree( pszLineTagName ); + + AppendCoordinateList( (OGRLineString *) poGeometry, + ppszText, pnLength, pnMaxLength ); + + if( bRing ) + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + else + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + +/* -------------------------------------------------------------------- */ +/* Polygon */ +/* -------------------------------------------------------------------- */ + else if( eFType == wkbPolygon ) + { + OGRPolygon *poPolygon = (OGRPolygon *) poGeometry; + + // Buffer for polygon tag name + srsName attribute if set + const size_t nPolyTagLength = 13; + char* pszPolyTagName = NULL; + pszPolyTagName = (char *) CPLMalloc( nPolyTagLength + nAttrsLength + 1 ); + + // Compose Polygon tag with or without srsName attribute + sprintf( pszPolyTagName, "", szAttributes ); + + AppendString( ppszText, pnLength, pnMaxLength, + pszPolyTagName ); + + // FREE TAG BUFFER + CPLFree( pszPolyTagName ); + + // Don't add srsName to polygon rings + + if( poPolygon->getExteriorRing() != NULL ) + { + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + + if( !OGR2GMLGeometryAppend( poPolygon->getExteriorRing(), + ppszText, pnLength, pnMaxLength, + TRUE ) ) + { + return FALSE; + } + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + + for( int iRing = 0; iRing < poPolygon->getNumInteriorRings(); iRing++ ) + { + OGRLinearRing *poRing = poPolygon->getInteriorRing(iRing); + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + + if( !OGR2GMLGeometryAppend( poRing, ppszText, pnLength, + pnMaxLength, TRUE ) ) + return FALSE; + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + +/* -------------------------------------------------------------------- */ +/* MultiPolygon, MultiLineString, MultiPoint, MultiGeometry */ +/* -------------------------------------------------------------------- */ + else if( eFType == wkbMultiPolygon + || eFType == wkbMultiLineString + || eFType == wkbMultiPoint + || eFType == wkbGeometryCollection ) + { + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeometry; + int iMember; + const char *pszElemClose = NULL; + const char *pszMemberElem = NULL; + + // Buffer for opening tag + srsName attribute + char* pszElemOpen = NULL; + + if( eFType == wkbMultiPolygon ) + { + pszElemOpen = (char *) CPLMalloc( 13 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiPolygon%s>", szAttributes ); + + pszElemClose = "MultiPolygon>"; + pszMemberElem = "polygonMember>"; + } + else if( eFType == wkbMultiLineString ) + { + pszElemOpen = (char *) CPLMalloc( 16 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiLineString%s>", szAttributes ); + + pszElemClose = "MultiLineString>"; + pszMemberElem = "lineStringMember>"; + } + else if( eFType == wkbMultiPoint ) + { + pszElemOpen = (char *) CPLMalloc( 11 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiPoint%s>", szAttributes ); + + pszElemClose = "MultiPoint>"; + pszMemberElem = "pointMember>"; + } + else + { + pszElemOpen = (char *) CPLMalloc( 19 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiGeometry%s>", szAttributes ); + + pszElemClose = "MultiGeometry>"; + pszMemberElem = "geometryMember>"; + } + + AppendString( ppszText, pnLength, pnMaxLength, "getNumGeometries(); iMember++) + { + OGRGeometry *poMember = poGC->getGeometryRef( iMember ); + + AppendString( ppszText, pnLength, pnMaxLength, "getEnvelope( &sEnvelope ); + + if( sEnvelope.MinX == 0 && sEnvelope.MaxX == 0 + && sEnvelope.MaxX == 0 && sEnvelope.MaxY == 0 ) + { + /* there is apparently a special way of representing a null box + geometry ... we should use it here eventually. */ + + return NULL; + } + + psBox = CPLCreateXMLNode( NULL, CXT_Element, "gml:Box" ); + +/* -------------------------------------------------------------------- */ +/* Add minxy coordinate. */ +/* -------------------------------------------------------------------- */ + psCoord = CPLCreateXMLNode( psBox, CXT_Element, "gml:coord" ); + + MakeGMLCoordinate( szCoordinate, sEnvelope.MinX, sEnvelope.MinY, 0.0, + FALSE ); + pszY = strstr(szCoordinate,",") + 1; + pszY[-1] = '\0'; + + CPLCreateXMLElementAndValue( psCoord, "gml:X", szCoordinate ); + CPLCreateXMLElementAndValue( psCoord, "gml:Y", pszY ); + +/* -------------------------------------------------------------------- */ +/* Add maxxy coordinate. */ +/* -------------------------------------------------------------------- */ + psCoord = CPLCreateXMLNode( psBox, CXT_Element, "gml:coord" ); + + MakeGMLCoordinate( szCoordinate, sEnvelope.MaxX, sEnvelope.MaxY, 0.0, + FALSE ); + pszY = strstr(szCoordinate,",") + 1; + pszY[-1] = '\0'; + + CPLCreateXMLElementAndValue( psCoord, "gml:X", szCoordinate ); + CPLCreateXMLElementAndValue( psCoord, "gml:Y", pszY ); + + return psBox; +} + + +/************************************************************************/ +/* AppendGML3CoordinateList() */ +/************************************************************************/ + +static void AppendGML3CoordinateList( const OGRSimpleCurve *poLine, int bCoordSwap, + char **ppszText, int *pnLength, + int *pnMaxLength, int nSRSDimensionLocFlags ) + +{ + char szCoordinate[256]; + int b3D = wkbHasZ(poLine->getGeometryType()); + + *pnLength += strlen(*ppszText + *pnLength); + _GrowBuffer( *pnLength + 40, ppszText, pnMaxLength ); + + if (b3D && (nSRSDimensionLocFlags & SRSDIM_LOC_POSLIST) != 0) + strcat( *ppszText + *pnLength, "" ); + else + strcat( *ppszText + *pnLength, "" ); + *pnLength += strlen(*ppszText + *pnLength); + + + for( int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++ ) + { + if (bCoordSwap) + OGRMakeWktCoordinate( szCoordinate, + poLine->getY(iPoint), + poLine->getX(iPoint), + poLine->getZ(iPoint), + b3D ? 3 : 2 ); + else + OGRMakeWktCoordinate( szCoordinate, + poLine->getX(iPoint), + poLine->getY(iPoint), + poLine->getZ(iPoint), + b3D ? 3 : 2 ); + _GrowBuffer( *pnLength + strlen(szCoordinate)+1, + ppszText, pnMaxLength ); + + if( iPoint != 0 ) + strcat( *ppszText + *pnLength, " " ); + + strcat( *ppszText + *pnLength, szCoordinate ); + *pnLength += strlen(*ppszText + *pnLength); + } + + _GrowBuffer( *pnLength + 20, ppszText, pnMaxLength ); + strcat( *ppszText + *pnLength, "" ); + *pnLength += strlen(*ppszText + *pnLength); +} + +/************************************************************************/ +/* OGR2GML3GeometryAppend() */ +/************************************************************************/ + +static int OGR2GML3GeometryAppend( const OGRGeometry *poGeometry, + const OGRSpatialReference* poParentSRS, + char **ppszText, int *pnLength, + int *pnMaxLength, + int bIsSubGeometry, + int bLongSRS, + int bLineStringAsCurve, + const char* pszGMLId, + int nSRSDimensionLocFlags, + int bForceLineStringAsLinearRing ) + +{ + +/* -------------------------------------------------------------------- */ +/* Check for Spatial Reference System attached to given geometry */ +/* -------------------------------------------------------------------- */ + + // Buffer for srsName, srsDimension and gml:id attributes (srsName="..." gml:id="...") + char szAttributes[256]; + int nAttrsLength = 0; + + szAttributes[0] = 0; + + const OGRSpatialReference* poSRS = NULL; + if (poParentSRS) + poSRS = poParentSRS; + else + poParentSRS = poSRS = poGeometry->getSpatialReference(); + + int bCoordSwap = FALSE; + + if( NULL != poSRS ) + { + const char* pszAuthName = NULL; + const char* pszAuthCode = NULL; + const char* pszTarget = NULL; + + if (poSRS->IsProjected()) + pszTarget = "PROJCS"; + else + pszTarget = "GEOGCS"; + + pszAuthName = poSRS->GetAuthorityName( pszTarget ); + if( NULL != pszAuthName ) + { + if( EQUAL( pszAuthName, "EPSG" ) ) + { + pszAuthCode = poSRS->GetAuthorityCode( pszTarget ); + if( NULL != pszAuthCode && strlen(pszAuthCode) < 10 ) + { + if (bLongSRS && !(((OGRSpatialReference*)poSRS)->EPSGTreatsAsLatLong() || + ((OGRSpatialReference*)poSRS)->EPSGTreatsAsNorthingEasting())) + { + OGRSpatialReference oSRS; + if (oSRS.importFromEPSGA(atoi(pszAuthCode)) == OGRERR_NONE) + { + if (oSRS.EPSGTreatsAsLatLong() || oSRS.EPSGTreatsAsNorthingEasting()) + bCoordSwap = TRUE; + } + } + + if (!bIsSubGeometry) + { + if (bLongSRS) + { + snprintf( szAttributes, sizeof(szAttributes), + " srsName=\"urn:ogc:def:crs:%s::%s\"", + pszAuthName, pszAuthCode ); + } + else + { + snprintf( szAttributes, sizeof(szAttributes), + " srsName=\"%s:%s\"", + pszAuthName, pszAuthCode ); + } + + nAttrsLength = strlen(szAttributes); + } + } + } + } + } + + if( (nSRSDimensionLocFlags & SRSDIM_LOC_GEOMETRY) != 0 && + wkbHasZ(poGeometry->getGeometryType()) ) + { + strcat(szAttributes, " srsDimension=\"3\""); + nAttrsLength = strlen(szAttributes); + + nSRSDimensionLocFlags &= ~SRSDIM_LOC_GEOMETRY; + } + + if (pszGMLId != NULL && nAttrsLength + 9 + strlen(pszGMLId) + 1 < sizeof(szAttributes)) + { + strcat(szAttributes, " gml:id=\""); + strcat(szAttributes, pszGMLId); + strcat(szAttributes, "\""); + nAttrsLength = strlen(szAttributes); + } + + OGRwkbGeometryType eType = poGeometry->getGeometryType(); + OGRwkbGeometryType eFType = wkbFlatten(eType); + +/* -------------------------------------------------------------------- */ +/* 2D Point */ +/* -------------------------------------------------------------------- */ + if( eType == wkbPoint ) + { + char szCoordinate[256]; + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + if (bCoordSwap) + OGRMakeWktCoordinate( szCoordinate, + poPoint->getY(), poPoint->getX(), 0.0, 2 ); + else + OGRMakeWktCoordinate( szCoordinate, + poPoint->getX(), poPoint->getY(), 0.0, 2 ); + + _GrowBuffer( *pnLength + strlen(szCoordinate) + 60 + nAttrsLength, + ppszText, pnMaxLength ); + + sprintf( *ppszText + *pnLength, + "%s", + szAttributes, szCoordinate ); + + *pnLength += strlen( *ppszText + *pnLength ); + } +/* -------------------------------------------------------------------- */ +/* 3D Point */ +/* -------------------------------------------------------------------- */ + else if( eType == wkbPoint25D ) + { + char szCoordinate[256]; + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + if (bCoordSwap) + OGRMakeWktCoordinate( szCoordinate, + poPoint->getY(), poPoint->getX(), poPoint->getZ(), 3 ); + else + OGRMakeWktCoordinate( szCoordinate, + poPoint->getX(), poPoint->getY(), poPoint->getZ(), 3 ); + + _GrowBuffer( *pnLength + strlen(szCoordinate) + 70 + nAttrsLength, + ppszText, pnMaxLength ); + + sprintf( *ppszText + *pnLength, + "%s", + szAttributes, szCoordinate ); + + *pnLength += strlen( *ppszText + *pnLength ); + } + +/* -------------------------------------------------------------------- */ +/* LineString and LinearRing */ +/* -------------------------------------------------------------------- */ + else if( eFType == wkbLineString ) + { + int bRing = EQUAL(poGeometry->getGeometryName(),"LINEARRING") || + bForceLineStringAsLinearRing; + if (!bRing && bLineStringAsCurve) + { + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + AppendGML3CoordinateList( (OGRLineString *) poGeometry, bCoordSwap, + ppszText, pnLength, pnMaxLength, nSRSDimensionLocFlags ); + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + else + { + // Buffer for tag name + srsName attribute if set + const size_t nLineTagLength = 16; + char* pszLineTagName = NULL; + pszLineTagName = (char *) CPLMalloc( nLineTagLength + nAttrsLength + 1 ); + + if( bRing ) + { + /* LinearRing isn't supposed to have srsName attribute according to GML3 SF-0 */ + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + else + { + sprintf( pszLineTagName, "", szAttributes ); + + AppendString( ppszText, pnLength, pnMaxLength, + pszLineTagName ); + } + + // FREE TAG BUFFER + CPLFree( pszLineTagName ); + + AppendGML3CoordinateList( (OGRLineString *) poGeometry, bCoordSwap, + ppszText, pnLength, pnMaxLength, nSRSDimensionLocFlags ); + + if( bRing ) + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + else + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + } + +/* -------------------------------------------------------------------- */ +/* ArcString or Circle */ +/* -------------------------------------------------------------------- */ + else if( eFType == wkbCircularString ) + { + AppendString( ppszText, pnLength, pnMaxLength, + "getNumPoints() == 3 && + poSC->getX(0) == poSC->getX(2) && + poSC->getY(0) == poSC->getY(2) ) + { + double dfMidX = (poSC->getX(0) + poSC->getX(1)) / 2; + double dfMidY = (poSC->getY(0) + poSC->getY(1)) / 2; + double dfDirX = (poSC->getX(1) - poSC->getX(0)) / 2; + double dfDirY = (poSC->getY(1) - poSC->getY(0)) / 2; + double dfNormX = -dfDirY; + double dfNormY = dfDirX; + double dfNewX = dfMidX + dfNormX; + double dfNewY = dfMidY + dfNormY; + OGRLineString* poLS = new OGRLineString(); + OGRPoint p; + poSC->getPoint(0, &p); + poLS->addPoint(&p); + poSC->getPoint(1, &p); + if( poSC->getCoordinateDimension() == 3 ) + poLS->addPoint(dfNewX, dfNewY, p.getZ()); + else + poLS->addPoint(dfNewX, dfNewY); + poLS->addPoint(&p); + AppendString( ppszText, pnLength, pnMaxLength, + ">" ); + AppendGML3CoordinateList( poLS, bCoordSwap, + ppszText, pnLength, pnMaxLength, nSRSDimensionLocFlags ); + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + delete poLS; + } + else + { + AppendString( ppszText, pnLength, pnMaxLength, + ">" ); + AppendGML3CoordinateList( poSC, bCoordSwap, + ppszText, pnLength, pnMaxLength, nSRSDimensionLocFlags ); + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + } + +/* -------------------------------------------------------------------- */ +/* CompositeCurve */ +/* -------------------------------------------------------------------- */ + else if( eFType == wkbCompoundCurve ) + { + AppendString( ppszText, pnLength, pnMaxLength, + ""); + + OGRCompoundCurve* poCC = (OGRCompoundCurve*)poGeometry; + for(int i=0;igetNumCurves();i++) + { + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + if( !OGR2GML3GeometryAppend( poCC->getCurve(i), poSRS, ppszText, pnLength, + pnMaxLength, TRUE, bLongSRS, + bLineStringAsCurve, + NULL, nSRSDimensionLocFlags, FALSE) ) + return FALSE; + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + +/* -------------------------------------------------------------------- */ +/* Polygon */ +/* -------------------------------------------------------------------- */ + else if( eFType == wkbPolygon || eFType == wkbCurvePolygon ) + { + OGRCurvePolygon *poCP = (OGRCurvePolygon *) poGeometry; + + // Buffer for polygon tag name + srsName attribute if set + const size_t nPolyTagLength = 13; + char* pszPolyTagName = NULL; + pszPolyTagName = (char *) CPLMalloc( nPolyTagLength + nAttrsLength + 1 ); + + // Compose Polygon tag with or without srsName attribute + sprintf( pszPolyTagName, "", szAttributes ); + + AppendString( ppszText, pnLength, pnMaxLength, + pszPolyTagName ); + + // FREE TAG BUFFER + CPLFree( pszPolyTagName ); + + // Don't add srsName to polygon rings + + if( poCP->getExteriorRingCurve() != NULL ) + { + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + + if( !OGR2GML3GeometryAppend( poCP->getExteriorRingCurve(), poSRS, + ppszText, pnLength, pnMaxLength, + TRUE, bLongSRS, bLineStringAsCurve, + NULL, nSRSDimensionLocFlags, TRUE) ) + { + return FALSE; + } + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + + for( int iRing = 0; iRing < poCP->getNumInteriorRings(); iRing++ ) + { + OGRCurve *poRing = poCP->getInteriorRingCurve(iRing); + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + + if( !OGR2GML3GeometryAppend( poRing, poSRS, ppszText, pnLength, + pnMaxLength, TRUE, bLongSRS, + bLineStringAsCurve, + NULL, nSRSDimensionLocFlags, TRUE) ) + return FALSE; + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + +/* -------------------------------------------------------------------- */ +/* MultiSurface, MultiCurve, MultiPoint, MultiGeometry */ +/* -------------------------------------------------------------------- */ + else if( eFType == wkbMultiPolygon + || eFType == wkbMultiSurface + || eFType == wkbMultiLineString + || eFType == wkbMultiCurve + || eFType == wkbMultiPoint + || eFType == wkbGeometryCollection ) + { + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeometry; + int iMember; + const char *pszElemClose = NULL; + const char *pszMemberElem = NULL; + + // Buffer for opening tag + srsName attribute + char* pszElemOpen = NULL; + + if( eFType == wkbMultiPolygon || eFType == wkbMultiSurface ) + { + pszElemOpen = (char *) CPLMalloc( 13 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiSurface%s>", szAttributes ); + + pszElemClose = "MultiSurface>"; + pszMemberElem = "surfaceMember>"; + } + else if( eFType == wkbMultiLineString || eFType == wkbMultiCurve ) + { + pszElemOpen = (char *) CPLMalloc( 16 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiCurve%s>", szAttributes ); + + pszElemClose = "MultiCurve>"; + pszMemberElem = "curveMember>"; + } + else if( eFType == wkbMultiPoint ) + { + pszElemOpen = (char *) CPLMalloc( 11 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiPoint%s>", szAttributes ); + + pszElemClose = "MultiPoint>"; + pszMemberElem = "pointMember>"; + } + else + { + pszElemOpen = (char *) CPLMalloc( 19 + nAttrsLength + 1 ); + sprintf( pszElemOpen, "MultiGeometry%s>", szAttributes ); + + pszElemClose = "MultiGeometry>"; + pszMemberElem = "geometryMember>"; + } + + AppendString( ppszText, pnLength, pnMaxLength, "getNumGeometries(); iMember++) + { + OGRGeometry *poMember = poGC->getGeometryRef( iMember ); + + AppendString( ppszText, pnLength, pnMaxLength, " + *

  • FORMAT=GML3. Otherwise it will default to GML 2.1.2 output. + *
  • GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3) To use gml:Curve element for linestrings. + * Otherwise gml:LineString will be used . + *
  • GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3) Default to YES. If YES, SRS with EPSG authority will + * be written with the "urn:ogc:def:crs:EPSG::" prefix. + * In the case, if the SRS is a geographic SRS without explicit AXIS order, but that the same SRS authority code + * imported with ImportFromEPSGA() should be treated as lat/long, then the function will take care of coordinate order swapping. + * If set to NO, SRS with EPSG authority will be written with the "EPSG:" prefix, even if they are in lat/long order. + *
  • GMLID=astring. If specified, a gml:id attribute will be written in the top-level geometry element with the provided value. + * Required for GML 3.2 compatibility. + *
  • SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for FORMAT=GML3, GDAL >= 2.0) Default to POSLIST. + * For 2.5D geometries, define the location where to attach the srsDimension attribute. + * There are diverging implementations. Some put in on the <gml:posList> element, other + * on the top geometry element. + * + * + * Note that curve geometries like CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, + * MULTICURVE or MULTISURFACE are not supported in GML 2. + * + * This method is the same as the C++ method OGRGeometry::exportToGML(). + * + * @param hGeometry handle to the geometry. + * @param papszOptions NULL-terminated list of options. + * @return A GML fragment or NULL in case of error. + * + * @since OGR 1.8.0 + */ + +char *OGR_G_ExportToGMLEx( OGRGeometryH hGeometry, char** papszOptions ) + +{ + char *pszText; + int nLength = 0, nMaxLength = 1; + + if( hGeometry == NULL ) + return CPLStrdup( "" ); + + pszText = (char *) CPLMalloc(nMaxLength); + pszText[0] = '\0'; + + const char* pszFormat = CSLFetchNameValue(papszOptions, "FORMAT"); + if (pszFormat && EQUAL(pszFormat, "GML3")) + { + const char* pszLineStringElement = CSLFetchNameValue(papszOptions, "GML3_LINESTRING_ELEMENT"); + int bLineStringAsCurve = (pszLineStringElement && EQUAL(pszLineStringElement, "curve")); + int bLongSRS = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "GML3_LONGSRS", "YES")); + const char* pszGMLId = CSLFetchNameValue(papszOptions, "GMLID"); + const char* pszSRSDimensionLoc = CSLFetchNameValueDef(papszOptions,"SRSDIMENSION_LOC","POSLIST"); + char** papszSRSDimensionLoc = CSLTokenizeString2(pszSRSDimensionLoc, ",", 0); + int nSRSDimensionLocFlags = 0; + for(int i=0; papszSRSDimensionLoc[i] != NULL; i++) + { + if( EQUAL(papszSRSDimensionLoc[i], "POSLIST") ) + nSRSDimensionLocFlags |= SRSDIM_LOC_POSLIST; + else if( EQUAL(papszSRSDimensionLoc[i], "GEOMETRY") ) + nSRSDimensionLocFlags |= SRSDIM_LOC_GEOMETRY; + else + CPLDebug("OGR", "Unrecognized location for srsDimension : %s", + papszSRSDimensionLoc[i]); + } + CSLDestroy(papszSRSDimensionLoc); + if( !OGR2GML3GeometryAppend( (OGRGeometry *) hGeometry, NULL, &pszText, + &nLength, &nMaxLength, FALSE, bLongSRS, + bLineStringAsCurve, pszGMLId, nSRSDimensionLocFlags, FALSE )) + { + CPLFree( pszText ); + return NULL; + } + else + return pszText; + } + + if( !OGR2GMLGeometryAppend( (OGRGeometry *) hGeometry, &pszText, + &nLength, &nMaxLength, FALSE )) + { + CPLFree( pszText ); + return NULL; + } + else + return pszText; +} diff --git a/bazaar/plugin/gdal/ogr/ogr_api.cpp b/bazaar/plugin/gdal/ogr/ogr_api.cpp new file mode 100644 index 000000000..194ae4d8d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_api.cpp @@ -0,0 +1,1204 @@ +/****************************************************************************** + * $Id: ogr_api.cpp 28039 2014-11-30 18:24:59Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: C API Functions that don't correspond one-to-one with C++ + * methods, such as the "simplified" geometry access functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_api.h" +#include "cpl_error.h" + +static int bNonLinearGeometriesEnabled = TRUE; + +/************************************************************************/ +/* OGR_G_GetPointCount() */ +/************************************************************************/ +/** + * \brief Fetch number of points from a geometry. + * + * Only wkbPoint[25D] or wkbLineString[25D] may return a valid value. + * Other geometry types will silently return 0. + * + * @param hGeom handle to the geometry from which to get the number of points. + * @return the number of points. + */ + +int OGR_G_GetPointCount( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetPointCount", 0 ); + + OGRwkbGeometryType eGType = wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()); + if( eGType == wkbPoint ) + return 1; + else if( OGR_GT_IsCurve(eGType) ) + return ((OGRCurve *) hGeom)->getNumPoints(); + else + { + // autotest/pymod/ogrtest.py calls this method on any geometry. So keep silent + //CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + return 0; + } +} + +/************************************************************************/ +/* OGR_G_SetPointCount() */ +/************************************************************************/ +/** + * \brief Set number of points in a geometry. + * + * This method primary exists to preset the number of points in a linestring + * geometry before setPoint() is used to assign them to avoid reallocating + * the array larger with each call to addPoint(). + * + * @param nNewPointCount the new number of points for geometry. + */ + +void OGR_G_SetPointCount( OGRGeometryH hGeom, int nNewPointCount ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_SetPointCount" ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbLineString: + case wkbCircularString: + { + OGRLineString *poLine = (OGRLineString *) hGeom; + poLine->setNumPoints( nNewPointCount ); + break; + } + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + break; + } +} + +/************************************************************************/ +/* OGR_G_GetX() */ +/************************************************************************/ +/** + * \brief Fetch the x coordinate of a point from a geometry. + * + * @param hGeom handle to the geometry from which to get the x coordinate. + * @param i point to get the x coordinate. + * @return the X coordinate of this point. + */ + +double OGR_G_GetX( OGRGeometryH hGeom, int i ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetX", 0 ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbPoint: + { + if( i == 0 ) + return ((OGRPoint *) hGeom)->getX(); + else + { + CPLError(CE_Failure, CPLE_NotSupported, "Only i == 0 is supported"); + return 0.0; + } + } + + case wkbLineString: + case wkbCircularString: + { + OGRLineString* poLS = (OGRLineString *) hGeom; + if (i < 0 || i >= poLS->getNumPoints()) + { + CPLError(CE_Failure, CPLE_NotSupported, "Index out of bounds"); + return 0.0; + } + return poLS->getX( i ); + } + + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + return 0.0; + } +} + +/************************************************************************/ +/* OGR_G_GetY() */ +/************************************************************************/ +/** + * \brief Fetch the x coordinate of a point from a geometry. + * + * @param hGeom handle to the geometry from which to get the y coordinate. + * @param i point to get the Y coordinate. + * @return the Y coordinate of this point. + */ + +double OGR_G_GetY( OGRGeometryH hGeom, int i ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetY", 0 ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbPoint: + { + if( i == 0 ) + return ((OGRPoint *) hGeom)->getY(); + else + { + CPLError(CE_Failure, CPLE_NotSupported, "Only i == 0 is supported"); + return 0.0; + } + } + + case wkbLineString: + case wkbCircularString: + { + OGRLineString* poLS = (OGRLineString *) hGeom; + if (i < 0 || i >= poLS->getNumPoints()) + { + CPLError(CE_Failure, CPLE_NotSupported, "Index out of bounds"); + return 0.0; + } + return poLS->getY( i ); + } + + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + return 0.0; + } +} + +/************************************************************************/ +/* OGR_G_GetZ() */ +/************************************************************************/ +/** + * \brief Fetch the z coordinate of a point from a geometry. + * + * @param hGeom handle to the geometry from which to get the Z coordinate. + * @param i point to get the Z coordinate. + * @return the Z coordinate of this point. + */ + +double OGR_G_GetZ( OGRGeometryH hGeom, int i ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetZ", 0 ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbPoint: + { + if( i == 0 ) + return ((OGRPoint *) hGeom)->getZ(); + else + { + CPLError(CE_Failure, CPLE_NotSupported, "Only i == 0 is supported"); + return 0.0; + } + } + + case wkbLineString: + case wkbCircularString: + { + OGRLineString* poLS = (OGRLineString *) hGeom; + if (i < 0 || i >= poLS->getNumPoints()) + { + CPLError(CE_Failure, CPLE_NotSupported, "Index out of bounds"); + return 0.0; + } + return poLS->getZ( i ); + } + + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + return 0.0; + } +} + +/************************************************************************/ +/* OGR_G_GetPoints() */ +/************************************************************************/ + +/** + * \brief Returns all points of line string. + * + * This method copies all points into user arrays. The user provides the + * stride between 2 consecutives elements of the array. + * + * On some CPU architectures, care must be taken so that the arrays are properly aligned. + * + * @param hGeom handle to the geometry from which to get the coordinates. + * @param pabyX a buffer of at least (sizeof(double) * nXStride * nPointCount) bytes, may be NULL. + * @param nXStride the number of bytes between 2 elements of pabyX. + * @param pabyY a buffer of at least (sizeof(double) * nYStride * nPointCount) bytes, may be NULL. + * @param nYStride the number of bytes between 2 elements of pabyY. + * @param pabyZ a buffer of at last size (sizeof(double) * nZStride * nPointCount) bytes, may be NULL. + * @param nZStride the number of bytes between 2 elements of pabyZ. + * + * @return the number of points + * + * @since OGR 1.9.0 + */ + +int OGR_G_GetPoints( OGRGeometryH hGeom, + void* pabyX, int nXStride, + void* pabyY, int nYStride, + void* pabyZ, int nZStride) +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetPoints", 0 ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbPoint: + { + if (pabyX) *((double*)pabyX) = ((OGRPoint *)hGeom)->getX(); + if (pabyY) *((double*)pabyY) = ((OGRPoint *)hGeom)->getY(); + if (pabyZ) *((double*)pabyZ) = ((OGRPoint *)hGeom)->getZ(); + return 1; + } + break; + + case wkbLineString: + case wkbCircularString: + { + OGRLineString* poLS = (OGRLineString *) hGeom; + poLS->getPoints(pabyX, nXStride, pabyY, nYStride, pabyZ, nZStride); + return poLS->getNumPoints(); + } + break; + + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + return 0; + break; + } +} + + +/************************************************************************/ +/* OGR_G_GetPoint() */ +/************************************************************************/ + +/** + * \brief Fetch a point in line string or a point geometry. + * + * @param hGeom handle to the geometry from which to get the coordinates. + * @param i the vertex to fetch, from 0 to getNumPoints()-1, zero for a point. + * @param pdfX value of x coordinate. + * @param pdfY value of y coordinate. + * @param pdfZ value of z coordinate. + */ + +void OGR_G_GetPoint( OGRGeometryH hGeom, int i, + double *pdfX, double *pdfY, double *pdfZ ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_GetPoint" ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbPoint: + { + if( i == 0 ) + { + *pdfX = ((OGRPoint *)hGeom)->getX(); + *pdfY = ((OGRPoint *)hGeom)->getY(); + if( pdfZ != NULL ) + *pdfZ = ((OGRPoint *)hGeom)->getZ(); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, "Only i == 0 is supported"); + } + } + break; + + case wkbLineString: + case wkbCircularString: + { + OGRLineString* poLS = (OGRLineString *) hGeom; + if (i < 0 || i >= poLS->getNumPoints()) + { + CPLError(CE_Failure, CPLE_NotSupported, "Index out of bounds"); + *pdfX = *pdfY = 0; + if( pdfZ != NULL ) + *pdfZ = 0; + } + else + { + *pdfX = poLS->getX( i ); + *pdfY = poLS->getY( i ); + if( pdfZ != NULL ) + *pdfZ = poLS->getZ( i ); + } + } + break; + + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + break; + } +} + +/************************************************************************/ +/* OGR_G_SetPoint() */ +/************************************************************************/ +/** + * \brief Assign all points in a point or a line string geometry. + * + * This method clear any existing points assigned to this geometry, + * and assigns a whole new set. + * + * @param hGeom handle to the geometry to set the coordinates. + * @param nPointsIn number of points being passed in padfX and padfY. + * @param padfX list of X coordinates of points being assigned. + * @param nXStride the number of bytes between 2 elements of pabyX. + * @param padfY list of Y coordinates of points being assigned. + * @param nYStride the number of bytes between 2 elements of pabyY. + * @param padfZ list of Z coordinates of points being assigned (defaults to NULL for 2D objects). + * @param nZStride the number of bytes between 2 elements of pabyZ. + */ + +void CPL_DLL OGR_G_SetPoints( OGRGeometryH hGeom, int nPointsIn, + void* pabyX, int nXStride, + void* pabyY, int nYStride, + void* pabyZ, int nZStride ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_SetPoints" ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbPoint: + { + ((OGRPoint *) hGeom)->setX( pabyX ? *( (double *)pabyX ) : 0.0 ); + ((OGRPoint *) hGeom)->setY( pabyY ? *( (double *)pabyY ) : 0.0 ); + ((OGRPoint *) hGeom)->setZ( pabyZ ? *( (double *)pabyZ ) : 0.0 ); + break; + } + case wkbLineString: + case wkbCircularString: + { + OGRLineString* poLine = (OGRLineString *) hGeom; + + if( nXStride == 0 && nYStride == 0 && nZStride == 0 ) + { + poLine->setPoints( nPointsIn, (double *)pabyX, (double *)pabyY, (double *)pabyZ ); + } + else + { + double x, y, z; + x = y = z = 0; + poLine->setNumPoints( nPointsIn ); + + for (int i = 0; i < nPointsIn; ++i) + { + if( pabyX ) x = *(double*)((char*)pabyX + i * nXStride); + if( pabyY ) y = *(double*)((char*)pabyY + i * nYStride); + if( pabyZ ) z = *(double*)((char*)pabyZ + i * nZStride); + + poLine->setPoint( i, x, y, z ); + } + } + break; + } + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + break; + } +} + +/************************************************************************/ +/* OGR_G_SetPoint() */ +/************************************************************************/ +/** + * \brief Set the location of a vertex in a point or linestring geometry. + * + * If iPoint is larger than the number of existing + * points in the linestring, the point count will be increased to + * accommodate the request. + * + * @param hGeom handle to the geometry to add a vertex to. + * @param i the index of the vertex to assign (zero based) or + * zero for a point. + * @param dfX input X coordinate to assign. + * @param dfY input Y coordinate to assign. + * @param dfZ input Z coordinate to assign (defaults to zero). + */ + +void OGR_G_SetPoint( OGRGeometryH hGeom, int i, + double dfX, double dfY, double dfZ ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_SetPoint" ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbPoint: + { + if( i == 0 ) + { + ((OGRPoint *) hGeom)->setX( dfX ); + ((OGRPoint *) hGeom)->setY( dfY ); + ((OGRPoint *) hGeom)->setZ( dfZ ); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, "Only i == 0 is supported"); + } + } + break; + + case wkbLineString: + case wkbCircularString: + { + if (i < 0) + { + CPLError(CE_Failure, CPLE_NotSupported, "Index out of bounds"); + return; + } + ((OGRLineString *) hGeom)->setPoint( i, dfX, dfY, dfZ ); + break; + } + + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + break; + } +} + +/************************************************************************/ +/* OGR_G_SetPoint_2D() */ +/************************************************************************/ +/** + * \brief Set the location of a vertex in a point or linestring geometry. + * + * If iPoint is larger than the number of existing + * points in the linestring, the point count will be increased to + * accommodate the request. + * + * @param hGeom handle to the geometry to add a vertex to. + * @param i the index of the vertex to assign (zero based) or + * zero for a point. + * @param dfX input X coordinate to assign. + * @param dfY input Y coordinate to assign. + */ + +void OGR_G_SetPoint_2D( OGRGeometryH hGeom, int i, + double dfX, double dfY ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_SetPoint_2D" ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbPoint: + { + if( i == 0 ) + { + ((OGRPoint *) hGeom)->setX( dfX ); + ((OGRPoint *) hGeom)->setY( dfY ); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, "Only i == 0 is supported"); + } + } + break; + + case wkbLineString: + case wkbCircularString: + { + if (i < 0) + { + CPLError(CE_Failure, CPLE_NotSupported, "Index out of bounds"); + return; + } + ((OGRLineString *) hGeom)->setPoint( i, dfX, dfY ); + break; + } + + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + break; + } +} + +/************************************************************************/ +/* OGR_G_AddPoint() */ +/************************************************************************/ +/** + * \brief Add a point to a geometry (line string or point). + * + * The vertex count of the line string is increased by one, and assigned from + * the passed location value. + * + * @param hGeom handle to the geometry to add a point to. + * @param dfX x coordinate of point to add. + * @param dfY y coordinate of point to add. + * @param dfZ z coordinate of point to add. + */ + +void OGR_G_AddPoint( OGRGeometryH hGeom, + double dfX, double dfY, double dfZ ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_AddPoint" ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbPoint: + { + ((OGRPoint *) hGeom)->setX( dfX ); + ((OGRPoint *) hGeom)->setY( dfY ); + ((OGRPoint *) hGeom)->setZ( dfZ ); + } + break; + + case wkbLineString: + case wkbCircularString: + ((OGRLineString *) hGeom)->addPoint( dfX, dfY, dfZ ); + break; + + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + break; + } +} + +/************************************************************************/ +/* OGR_G_AddPoint() */ +/************************************************************************/ +/** + * \brief Add a point to a geometry (line string or point). + * + * The vertex count of the line string is increased by one, and assigned from + * the passed location value. + * + * @param hGeom handle to the geometry to add a point to. + * @param dfX x coordinate of point to add. + * @param dfY y coordinate of point to add. + */ + +void OGR_G_AddPoint_2D( OGRGeometryH hGeom, + double dfX, double dfY ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_AddPoint_2D" ); + + switch( wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + case wkbPoint: + { + ((OGRPoint *) hGeom)->setX( dfX ); + ((OGRPoint *) hGeom)->setY( dfY ); + } + break; + + case wkbLineString: + case wkbCircularString: + ((OGRLineString *) hGeom)->addPoint( dfX, dfY ); + break; + + default: + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + break; + } +} + +/************************************************************************/ +/* OGR_G_GetGeometryCount() */ +/************************************************************************/ +/** + * \brief Fetch the number of elements in a geometry or number of geometries in container. + * + * Only geometries of type wkbPolygon[25D], wkbMultiPoint[25D], wkbMultiLineString[25D], + * wkbMultiPolygon[25D] or wkbGeometryCollection[25D] may return a valid value. + * Other geometry types will silently return 0. + * + * For a polygon, the returned number is the number of rings (exterior ring + interior rings). + * + * @param hGeom single geometry or geometry container from which to get + * the number of elements. + * @return the number of elements. + */ + +int OGR_G_GetGeometryCount( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetGeometryCount", 0 ); + + OGRwkbGeometryType eType = wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()); + if( OGR_GT_IsSubClassOf(eType, wkbCurvePolygon) ) + { + if( ((OGRCurvePolygon *)hGeom)->getExteriorRingCurve() == NULL ) + return 0; + else + return ((OGRCurvePolygon *)hGeom)->getNumInteriorRings() + 1; + } + else if( OGR_GT_IsSubClassOf(eType, wkbCompoundCurve) ) + return ((OGRCompoundCurve *)hGeom)->getNumCurves(); + else if( OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) ) + return ((OGRGeometryCollection *)hGeom)->getNumGeometries(); + else + { + // autotest/pymod/ogrtest.py calls this method on any geometry. So keep silent + //CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + return 0; + } +} + +/************************************************************************/ +/* OGR_G_GetGeometryRef() */ +/************************************************************************/ + +/** + * \brief Fetch geometry from a geometry container. + * + * This function returns an handle to a geometry within the container. + * The returned geometry remains owned by the container, and should not be + * modified. The handle is only valid untill the next change to the + * geometry container. Use OGR_G_Clone() to make a copy. + * + * This function relates to the SFCOM + * IGeometryCollection::get_Geometry() method. + * + * This function is the same as the CPP method + * OGRGeometryCollection::getGeometryRef(). + * + * For a polygon, OGR_G_GetGeometryRef(iSubGeom) returns the exterior ring + * if iSubGeom == 0, and the interior rings for iSubGeom > 0. + * + * @param hGeom handle to the geometry container from which to get a + * geometry from. + * @param iSubGeom the index of the geometry to fetch, between 0 and + * getNumGeometries() - 1. + * @return handle to the requested geometry. + */ + +OGRGeometryH OGR_G_GetGeometryRef( OGRGeometryH hGeom, int iSubGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetGeometryRef", NULL ); + + OGRwkbGeometryType eType = wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()); + if( OGR_GT_IsSubClassOf(eType, wkbCurvePolygon) ) + { + if( iSubGeom == 0 ) + return (OGRGeometryH) + ((OGRCurvePolygon *)hGeom)->getExteriorRingCurve(); + else + return (OGRGeometryH) + ((OGRCurvePolygon *)hGeom)->getInteriorRingCurve(iSubGeom-1); + } + else if( OGR_GT_IsSubClassOf(eType, wkbCompoundCurve) ) + return (OGRGeometryH) ((OGRCompoundCurve *)hGeom)->getCurve(iSubGeom); + else if( OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) ) + return (OGRGeometryH) ((OGRGeometryCollection *)hGeom)->getGeometryRef( iSubGeom ); + else + { + CPLError(CE_Failure, CPLE_NotSupported, "Incompatible geometry for operation"); + return 0; + } +} + +/************************************************************************/ +/* OGR_G_AddGeometry() */ +/************************************************************************/ + +/** + * \brief Add a geometry to a geometry container. + * + * Some subclasses of OGRGeometryCollection restrict the types of geometry + * that can be added, and may return an error. The passed geometry is cloned + * to make an internal copy. + * + * There is no SFCOM analog to this method. + * + * This function is the same as the CPP method + * OGRGeometryCollection::addGeometry. + * + * For a polygon, hNewSubGeom must be a linearring. If the polygon is empty, + * the first added subgeometry will be the exterior ring. The next ones will be + * the interior rings. + * + * @param hGeom existing geometry container. + * @param hNewSubGeom geometry to add to the container. + * + * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if + * the geometry type is illegal for the type of existing geometry. + */ + +OGRErr OGR_G_AddGeometry( OGRGeometryH hGeom, OGRGeometryH hNewSubGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_AddGeometry", OGRERR_UNSUPPORTED_OPERATION ); + VALIDATE_POINTER1( hNewSubGeom, "OGR_G_AddGeometry", OGRERR_UNSUPPORTED_OPERATION ); + + OGRErr eErr = OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + + OGRwkbGeometryType eType = wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()); + if( OGR_GT_IsSubClassOf(eType, wkbCurvePolygon) ) + { + if( OGR_GT_IsCurve( wkbFlatten(((OGRGeometry *) hNewSubGeom)->getGeometryType()) ) ) + eErr = ((OGRCurvePolygon *)hGeom)->addRing( (OGRCurve *) hNewSubGeom ); + } + else if( OGR_GT_IsSubClassOf(eType, wkbCompoundCurve) ) + { + if( OGR_GT_IsCurve( wkbFlatten(((OGRGeometry *) hNewSubGeom)->getGeometryType()) ) ) + eErr = ((OGRCompoundCurve *)hGeom)->addCurve( (OGRCurve *) hNewSubGeom ); + } + else if( OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) ) + { + eErr = ((OGRGeometryCollection *)hGeom)->addGeometry( + (OGRGeometry *) hNewSubGeom ); + } + + return eErr; +} + +/************************************************************************/ +/* OGR_G_AddGeometryDirectly() */ +/************************************************************************/ +/** + * \brief Add a geometry directly to an existing geometry container. + * + * Some subclasses of OGRGeometryCollection restrict the types of geometry + * that can be added, and may return an error. Ownership of the passed + * geometry is taken by the container rather than cloning as addGeometry() + * does. + * + * This function is the same as the CPP method + * OGRGeometryCollection::addGeometryDirectly. + * + * There is no SFCOM analog to this method. + * + * For a polygon, hNewSubGeom must be a linearring. If the polygon is empty, + * the first added subgeometry will be the exterior ring. The next ones will be + * the interior rings. + * + * @param hGeom existing geometry. + * @param hNewSubGeom geometry to add to the existing geometry. + * + * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if + * the geometry type is illegal for the type of geometry container. + */ + +OGRErr OGR_G_AddGeometryDirectly( OGRGeometryH hGeom, + OGRGeometryH hNewSubGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_AddGeometryDirectly", OGRERR_UNSUPPORTED_OPERATION ); + VALIDATE_POINTER1( hNewSubGeom, "OGR_G_AddGeometryDirectly", OGRERR_UNSUPPORTED_OPERATION ); + + OGRErr eErr = OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + + OGRwkbGeometryType eType = wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()); + if( OGR_GT_IsSubClassOf(eType, wkbCurvePolygon) ) + { + if( OGR_GT_IsCurve( wkbFlatten(((OGRGeometry *) hNewSubGeom)->getGeometryType()) ) ) + eErr = ((OGRCurvePolygon *)hGeom)->addRingDirectly( (OGRCurve *) hNewSubGeom ); + } + else if( OGR_GT_IsSubClassOf(eType, wkbCompoundCurve) ) + { + if( OGR_GT_IsCurve( wkbFlatten(((OGRGeometry *) hNewSubGeom)->getGeometryType()) ) ) + eErr = ((OGRCompoundCurve *)hGeom)->addCurveDirectly( (OGRCurve *) hNewSubGeom ); + } + else if( OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) ) + { + eErr = ((OGRGeometryCollection *)hGeom)->addGeometryDirectly( + (OGRGeometry *) hNewSubGeom ); + } + + if( eErr != OGRERR_NONE ) + delete (OGRGeometry*)hNewSubGeom; + + return eErr; +} + +/************************************************************************/ +/* OGR_G_RemoveGeometry() */ +/************************************************************************/ + +/** + * \brief Remove a geometry from an exiting geometry container. + * + * Removing a geometry will cause the geometry count to drop by one, and all + * "higher" geometries will shuffle down one in index. + * + * There is no SFCOM analog to this method. + * + * This function is the same as the CPP method + * OGRGeometryCollection::removeGeometry(). + * + * @param hGeom the existing geometry to delete from. + * @param iGeom the index of the geometry to delete. A value of -1 is a + * special flag meaning that all geometries should be removed. + * + * @param bDelete if TRUE the geometry will be destroyed, otherwise it will + * not. The default is TRUE as the existing geometry is considered to own the + * geometries in it. + * + * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is + * out of range. + */ + +OGRErr OGR_G_RemoveGeometry( OGRGeometryH hGeom, int iGeom, int bDelete ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_RemoveGeometry", 0 ); + + OGRwkbGeometryType eType = wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()); + if( OGR_GT_IsSubClassOf(eType, wkbCurvePolygon) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OGR_G_RemoveGeometry() not supported on polygons yet." ); + return OGRERR_UNSUPPORTED_OPERATION; + } + else if( OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) ) + { + return ((OGRGeometryCollection *)hGeom)->removeGeometry( iGeom,bDelete); + } + else + { + return OGRERR_UNSUPPORTED_OPERATION; + } +} + +/************************************************************************/ +/* OGR_G_Length() */ +/************************************************************************/ + +/** + * \brief Compute length of a geometry. + * + * Computes the length for OGRCurve or MultiCurve objects. + * Undefined for all other geometry types (returns zero). + * + * This function utilizes the C++ get_Length() method. + * + * @param hGeom the geometry to operate on. + * @return the lenght or 0.0 for unsupported geometry types. + * + * @since OGR 1.8.0 + */ + +double OGR_G_Length( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetLength", 0 ); + + double dfLength; + + OGRwkbGeometryType eType = wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()); + if( OGR_GT_IsCurve(eType) ) + { + dfLength = ((OGRCurve *) hGeom)->get_Length(); + } + else if( OGR_GT_IsSubClassOf(eType, wkbMultiCurve) || + eType == wkbGeometryCollection ) + { + dfLength = ((OGRGeometryCollection *) hGeom)->get_Length(); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "OGR_G_Length() called against a non-curve geometry type." ); + dfLength = 0.0; + } + + return dfLength; +} + +/************************************************************************/ +/* OGR_G_Area() */ +/************************************************************************/ + +/** + * \brief Compute geometry area. + * + * Computes the area for an OGRLinearRing, OGRPolygon or OGRMultiPolygon. + * Undefined for all other geometry types (returns zero). + * + * This function utilizes the C++ get_Area() methods such as + * OGRPolygon::get_Area(). + * + * @param hGeom the geometry to operate on. + * @return the area or 0.0 for unsupported geometry types. + * + * @since OGR 1.8.0 + */ + +double OGR_G_Area( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_Area", 0 ); + + double dfArea; + + OGRwkbGeometryType eType = wkbFlatten(((OGRGeometry *) hGeom)->getGeometryType()); + if( OGR_GT_IsSurface(eType) ) + { + dfArea = ((OGRSurface *) hGeom)->get_Area(); + } + else if( OGR_GT_IsCurve(eType) ) + { + dfArea = ((OGRCurve *) hGeom)->get_Area(); + } + else if( OGR_GT_IsSubClassOf(eType, wkbMultiSurface) || + eType == wkbGeometryCollection ) + { + dfArea = ((OGRGeometryCollection *) hGeom)->get_Area(); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "OGR_G_Area() called against non-surface geometry type." ); + + dfArea = 0.0; + } + + return dfArea; +} + +/** + * \brief Compute geometry area (deprecated) + * + * @deprecated + * @see OGR_G_Area() + */ +double OGR_G_GetArea( OGRGeometryH hGeom ) + +{ + return OGR_G_Area( hGeom ); +} + +#ifndef OGR_ENABLED +OGRGeometryH OGR_G_CreateGeometryFromJson( const char* ) +{ + return NULL; +} + +char* OGR_G_ExportToKML( OGRGeometryH, const char* pszAltitudeMode ) +{ + return NULL; +} +#endif + +/************************************************************************/ +/* OGR_G_HasCurveGeometry() */ +/************************************************************************/ + +/** + * \brief Returns if this geometry is or has curve geometry. + * + * Returns if a geometry is or has CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, + * MULTICURVE or MULTISURFACE in it. +* + * If bLookForNonLinear is set to TRUE, it will be actually looked if the + * geometry or its subgeometries are or contain a non-linear geometry in them. In which + * case, if the method returns TRUE, it means that OGR_G_GetLinearGeometry() would + * return an approximate version of the geometry. Otherwise, OGR_G_GetLinearGeometry() + * would do a conversion, but with just converting container type, like + * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or MULTISURFACE -> MULTIPOLYGON, + * resulting in a "loss-less" conversion. + * + * This function is the same as C++ method OGRGeometry::hasCurveGeometry(). + * + * @param hGeom the geometry to operate on. + * @param bLookForNonLinear set it to TRUE to check if the geometry is or contains + * a CIRCULARSTRING. + * @return TRUE if this geometry is or has curve geometry. + * + * @since GDAL 2.0 + */ + +int OGR_G_HasCurveGeometry( OGRGeometryH hGeom, int bLookForNonLinear ) +{ + VALIDATE_POINTER1( hGeom, "OGR_G_HasCurveGeometry", FALSE ); + return ((OGRGeometry *) hGeom)->hasCurveGeometry(bLookForNonLinear); +} + +/************************************************************************/ +/* OGR_G_GetLinearGeometry() */ +/************************************************************************/ + +/** + * \brief Return, possibly approximate, linear version of this geometry. + * + * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, + * MULTICURVE or MULTISURFACE in it, by approximating curve geometries. + * + * The ownership of the returned geometry belongs to the caller. + * + * The reverse function is OGR_G_GetCurveGeometry(). + * + * This method relates to the ISO SQL/MM Part 3 ICurve::CurveToLine() and + * CurvePolygon::CurvePolyToPoly() methods. + * + * This function is the same as C++ method OGRGeometry::getLinearGeometry(). + * + * @param hGeom the geometry to operate on. + * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the + * arc, zero to use the default setting. + * @param papszOptions options as a null-terminated list of strings or NULL. + * See OGRGeometryFactory::curveToLineString() for valid options. + * + * @return a new geometry. + * + * @since GDAL 2.0 + */ + +OGRGeometryH CPL_DLL OGR_G_GetLinearGeometry( OGRGeometryH hGeom, + double dfMaxAngleStepSizeDegrees, + char** papszOptions ) +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetLinearGeometry", NULL ); + return (OGRGeometryH) ((OGRGeometry *) hGeom)->getLinearGeometry(dfMaxAngleStepSizeDegrees, + (const char* const*)papszOptions ); +} + +/************************************************************************/ +/* OGR_G_GetCurveGeometry() */ +/************************************************************************/ + +/** + * \brief Return curve version of this geometry. + * + * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, + * MULTICURVE or MULTISURFACE in it, by de-approximating linear into curve geometries. + * + * If the geometry has no curve portion, the returned geometry will be a clone + * of it. + * + * The ownership of the returned geometry belongs to the caller. + * + * The reverse function is OGR_G_GetLinearGeometry(). + * + * This function is the same as C++ method OGRGeometry::getCurveGeometry(). + * + * @param hGeom the geometry to operate on. + * @param papszOptions options as a null-terminated list of strings. + * Unused for now. Must be set to NULL. + * + * @return a new geometry. + * + * @since GDAL 2.0 + */ + +OGRGeometryH CPL_DLL OGR_G_GetCurveGeometry( OGRGeometryH hGeom, + char** papszOptions ) +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetCurveGeometry", NULL ); + return (OGRGeometryH) ((OGRGeometry *) hGeom)->getCurveGeometry((const char* const*)papszOptions); +} + +/************************************************************************/ +/* OGR_G_Value() */ +/************************************************************************/ +/** + * \brief Fetch point at given distance along curve. + * + * This function relates to the SF COM ICurve::get_Value() method. + * + * This function is the same as the C++ method OGRCurve::Value(). + * + * @param hGeom curve geometry. + * @param dfDistance distance along the curve at which to sample position. + * This distance should be between zero and get_Length() + * for this curve. + * @return a point or NULL. + * + * @since GDAL 2.0 + */ + +OGRGeometryH OGR_G_Value( OGRGeometryH hGeom, double dfDistance ) +{ + VALIDATE_POINTER1( hGeom, "OGR_G_Value", NULL ); + + if( OGR_GT_IsCurve(((OGRGeometry *) hGeom)->getGeometryType()) ) + { + OGRPoint* p = new OGRPoint(); + ((OGRCurve *) hGeom)->Value(dfDistance, p); + return (OGRGeometryH)p; + } + else + { + return NULL; + } +} + +/************************************************************************/ +/* OGRSetNonLinearGeometriesEnabledFlag() */ +/************************************************************************/ + +/** + * \brief Set flag to enable/disable returning non-linear geometries in the C API. + * + * This flag has only an effect on the OGR_F_GetGeometryRef(), OGR_F_GetGeomFieldRef(), + * OGR_L_GetGeomType(), OGR_GFld_GetType() and OGR_FD_GetGeomType() C API, and + * corresponding methods in the SWIG bindings. It is meant as making it simple + * for applications using the OGR C API not to have to deal with non-linear geometries, + * even if such geometries might be returned by drivers. In which case, they + * will be transformed into their closest linear geometry, by doing linear + * approximation, with OGR_G_ForceTo(). + * + * Libraries should generally *not* use that method, since that could interfere + * with other libraries or applications. + * + * Note that it *does* not affect the behaviour of the C++ API. + * + * @param bFlag TRUE if non-linear geometries might be returned (default value). + * FALSE to ask for non-linear geometries to be approximated as linear geometries. + * + * @return a point or NULL. + * + * @since GDAL 2.0 + */ + +void OGRSetNonLinearGeometriesEnabledFlag(int bFlag) +{ + bNonLinearGeometriesEnabled = bFlag; +} + +/************************************************************************/ +/* OGRGetNonLinearGeometriesEnabledFlag() */ +/************************************************************************/ + +/** + * \brief Get flag to enable/disable returning non-linear geometries in the C API. + * + * return TRUE if non-linear geometries might be returned (default value is TRUE) + * + * @since GDAL 2.0 + * @see OGRSetNonLinearGeometriesEnabledFlag() + */ +int OGRGetNonLinearGeometriesEnabledFlag(void) +{ + return bNonLinearGeometriesEnabled; +} diff --git a/bazaar/plugin/gdal/ogr/ogr_api.h b/bazaar/plugin/gdal/ogr/ogr_api.h new file mode 100644 index 000000000..5c9fca4b4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_api.h @@ -0,0 +1,625 @@ +/****************************************************************************** + * $Id: ogr_api.h 28900 2015-04-14 09:40:34Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: C API for OGR Geometry, Feature, Layers, DataSource and drivers. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef OGR_API_H_INCLUDED +#define OGR_API_H_INCLUDED + +/** + * \file ogr_api.h + * + * C API and defines for OGRFeature, OGRGeometry, and OGRDataSource + * related classes. + * + * See also: ogr_geometry.h, ogr_feature.h, ogrsf_frmts.h, ogr_featurestyle.h + */ + +#include "cpl_progress.h" +#include "cpl_minixml.h" +#include "ogr_core.h" + +CPL_C_START + +/* -------------------------------------------------------------------- */ +/* Geometry related functions (ogr_geometry.h) */ +/* -------------------------------------------------------------------- */ +#ifdef DEBUG +typedef struct OGRGeometryHS *OGRGeometryH; +#else +typedef void *OGRGeometryH; +#endif + +#ifndef _DEFINED_OGRSpatialReferenceH +#define _DEFINED_OGRSpatialReferenceH + +#ifdef DEBUG +typedef struct OGRSpatialReferenceHS *OGRSpatialReferenceH; +typedef struct OGRCoordinateTransformationHS *OGRCoordinateTransformationH; +#else +typedef void *OGRSpatialReferenceH; +typedef void *OGRCoordinateTransformationH; +#endif + +#endif + +struct _CPLXMLNode; + +/* From base OGRGeometry class */ + +OGRErr CPL_DLL OGR_G_CreateFromWkb( unsigned char *, OGRSpatialReferenceH, + OGRGeometryH *, int ); +OGRErr CPL_DLL OGR_G_CreateFromWkt( char **, OGRSpatialReferenceH, + OGRGeometryH * ); +OGRErr CPL_DLL OGR_G_CreateFromFgf( unsigned char *, OGRSpatialReferenceH, + OGRGeometryH *, int, int * ); +void CPL_DLL OGR_G_DestroyGeometry( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_CreateGeometry( OGRwkbGeometryType ); +OGRGeometryH CPL_DLL +OGR_G_ApproximateArcAngles( + double dfCenterX, double dfCenterY, double dfZ, + double dfPrimaryRadius, double dfSecondaryAxis, double dfRotation, + double dfStartAngle, double dfEndAngle, + double dfMaxAngleStepSizeDegrees ); + +OGRGeometryH CPL_DLL OGR_G_ForceToPolygon( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_ForceToLineString( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_ForceToMultiPolygon( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_ForceToMultiPoint( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_ForceToMultiLineString( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_ForceTo( OGRGeometryH hGeom, + OGRwkbGeometryType eTargetType, + char** papszOptions ); + +int CPL_DLL OGR_G_GetDimension( OGRGeometryH ); +int CPL_DLL OGR_G_GetCoordinateDimension( OGRGeometryH ); +void CPL_DLL OGR_G_SetCoordinateDimension( OGRGeometryH, int ); +OGRGeometryH CPL_DLL OGR_G_Clone( OGRGeometryH ); +void CPL_DLL OGR_G_GetEnvelope( OGRGeometryH, OGREnvelope * ); +void CPL_DLL OGR_G_GetEnvelope3D( OGRGeometryH, OGREnvelope3D * ); +OGRErr CPL_DLL OGR_G_ImportFromWkb( OGRGeometryH, unsigned char *, int ); +OGRErr CPL_DLL OGR_G_ExportToWkb( OGRGeometryH, OGRwkbByteOrder, unsigned char*); +OGRErr CPL_DLL OGR_G_ExportToIsoWkb( OGRGeometryH, OGRwkbByteOrder, unsigned char*); +int CPL_DLL OGR_G_WkbSize( OGRGeometryH hGeom ); +OGRErr CPL_DLL OGR_G_ImportFromWkt( OGRGeometryH, char ** ); +OGRErr CPL_DLL OGR_G_ExportToWkt( OGRGeometryH, char ** ); +OGRErr CPL_DLL OGR_G_ExportToIsoWkt( OGRGeometryH, char ** ); +OGRwkbGeometryType CPL_DLL OGR_G_GetGeometryType( OGRGeometryH ); +const char CPL_DLL *OGR_G_GetGeometryName( OGRGeometryH ); +void CPL_DLL OGR_G_DumpReadable( OGRGeometryH, FILE *, const char * ); +void CPL_DLL OGR_G_FlattenTo2D( OGRGeometryH ); +void CPL_DLL OGR_G_CloseRings( OGRGeometryH ); + +OGRGeometryH CPL_DLL OGR_G_CreateFromGML( const char * ); +char CPL_DLL *OGR_G_ExportToGML( OGRGeometryH ); +char CPL_DLL *OGR_G_ExportToGMLEx( OGRGeometryH, char** papszOptions ); + +OGRGeometryH CPL_DLL OGR_G_CreateFromGMLTree( const CPLXMLNode * ); +CPLXMLNode CPL_DLL *OGR_G_ExportToGMLTree( OGRGeometryH ); +CPLXMLNode CPL_DLL *OGR_G_ExportEnvelopeToGMLTree( OGRGeometryH ); + +char CPL_DLL *OGR_G_ExportToKML( OGRGeometryH, const char* pszAltitudeMode ); + +char CPL_DLL *OGR_G_ExportToJson( OGRGeometryH ); +char CPL_DLL *OGR_G_ExportToJsonEx( OGRGeometryH, char** papszOptions ); +OGRGeometryH CPL_DLL OGR_G_CreateGeometryFromJson( const char* ); + +void CPL_DLL OGR_G_AssignSpatialReference( OGRGeometryH, + OGRSpatialReferenceH ); +OGRSpatialReferenceH CPL_DLL OGR_G_GetSpatialReference( OGRGeometryH ); +OGRErr CPL_DLL OGR_G_Transform( OGRGeometryH, OGRCoordinateTransformationH ); +OGRErr CPL_DLL OGR_G_TransformTo( OGRGeometryH, OGRSpatialReferenceH ); + +OGRGeometryH CPL_DLL OGR_G_Simplify( OGRGeometryH hThis, double tolerance ); +OGRGeometryH CPL_DLL OGR_G_SimplifyPreserveTopology( OGRGeometryH hThis, double tolerance ); + +void CPL_DLL OGR_G_Segmentize(OGRGeometryH hGeom, double dfMaxLength ); +int CPL_DLL OGR_G_Intersects( OGRGeometryH, OGRGeometryH ); +int CPL_DLL OGR_G_Equals( OGRGeometryH, OGRGeometryH ); +/*int CPL_DLL OGR_G_EqualsExact( OGRGeometryH, OGRGeometryH, double );*/ +int CPL_DLL OGR_G_Disjoint( OGRGeometryH, OGRGeometryH ); +int CPL_DLL OGR_G_Touches( OGRGeometryH, OGRGeometryH ); +int CPL_DLL OGR_G_Crosses( OGRGeometryH, OGRGeometryH ); +int CPL_DLL OGR_G_Within( OGRGeometryH, OGRGeometryH ); +int CPL_DLL OGR_G_Contains( OGRGeometryH, OGRGeometryH ); +int CPL_DLL OGR_G_Overlaps( OGRGeometryH, OGRGeometryH ); + +OGRGeometryH CPL_DLL OGR_G_Boundary( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_ConvexHull( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_Buffer( OGRGeometryH, double, int ); +OGRGeometryH CPL_DLL OGR_G_Intersection( OGRGeometryH, OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_Union( OGRGeometryH, OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_UnionCascaded( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_PointOnSurface( OGRGeometryH ); +/*OGRGeometryH CPL_DLL OGR_G_Polygonize( OGRGeometryH *, int);*/ +/*OGRGeometryH CPL_DLL OGR_G_Polygonizer_getCutEdges( OGRGeometryH *, int);*/ +/*OGRGeometryH CPL_DLL OGR_G_LineMerge( OGRGeometryH );*/ + +OGRGeometryH CPL_DLL OGR_G_Difference( OGRGeometryH, OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_SymDifference( OGRGeometryH, OGRGeometryH ); +double CPL_DLL OGR_G_Distance( OGRGeometryH, OGRGeometryH ); +double CPL_DLL OGR_G_Length( OGRGeometryH ); +double CPL_DLL OGR_G_Area( OGRGeometryH ); +int CPL_DLL OGR_G_Centroid( OGRGeometryH, OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_Value( OGRGeometryH, double dfDistance ); + +void CPL_DLL OGR_G_Empty( OGRGeometryH ); +int CPL_DLL OGR_G_IsEmpty( OGRGeometryH ); +int CPL_DLL OGR_G_IsValid( OGRGeometryH ); +/*char CPL_DLL *OGR_G_IsValidReason( OGRGeometryH );*/ +int CPL_DLL OGR_G_IsSimple( OGRGeometryH ); +int CPL_DLL OGR_G_IsRing( OGRGeometryH ); + +OGRGeometryH CPL_DLL OGR_G_Polygonize( OGRGeometryH ); + +/* backward compatibility (non-standard methods) */ +int CPL_DLL OGR_G_Intersect( OGRGeometryH, OGRGeometryH ) CPL_WARN_DEPRECATED("Non standard method. Use OGR_G_Intersects() instead"); +int CPL_DLL OGR_G_Equal( OGRGeometryH, OGRGeometryH ) CPL_WARN_DEPRECATED("Non standard method. Use OGR_G_Equals() instead"); +OGRGeometryH CPL_DLL OGR_G_SymmetricDifference( OGRGeometryH, OGRGeometryH ) CPL_WARN_DEPRECATED("Non standard method. Use OGR_G_SymDifference() instead"); +double CPL_DLL OGR_G_GetArea( OGRGeometryH ) CPL_WARN_DEPRECATED("Non standard method. Use OGR_G_Area() instead"); +OGRGeometryH CPL_DLL OGR_G_GetBoundary( OGRGeometryH ) CPL_WARN_DEPRECATED("Non standard method. Use OGR_G_Boundary() instead"); + +/* Methods for getting/setting vertices in points, line strings and rings */ +int CPL_DLL OGR_G_GetPointCount( OGRGeometryH ); +int CPL_DLL OGR_G_GetPoints( OGRGeometryH hGeom, + void* pabyX, int nXStride, + void* pabyY, int nYStride, + void* pabyZ, int nZStride); +double CPL_DLL OGR_G_GetX( OGRGeometryH, int ); +double CPL_DLL OGR_G_GetY( OGRGeometryH, int ); +double CPL_DLL OGR_G_GetZ( OGRGeometryH, int ); +void CPL_DLL OGR_G_GetPoint( OGRGeometryH, int iPoint, + double *, double *, double * ); +void CPL_DLL OGR_G_SetPointCount( OGRGeometryH hGeom, int nNewPointCount ); +void CPL_DLL OGR_G_SetPoint( OGRGeometryH, int iPoint, + double, double, double ); +void CPL_DLL OGR_G_SetPoint_2D( OGRGeometryH, int iPoint, + double, double ); +void CPL_DLL OGR_G_AddPoint( OGRGeometryH, double, double, double ); +void CPL_DLL OGR_G_AddPoint_2D( OGRGeometryH, double, double ); +void CPL_DLL OGR_G_SetPoints( OGRGeometryH hGeom, int nPointsIn, + void* pabyX, int nXStride, + void* pabyY, int nYStride, + void* pabyZ, int nZStride ); + +/* Methods for getting/setting rings and members collections */ + +int CPL_DLL OGR_G_GetGeometryCount( OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_G_GetGeometryRef( OGRGeometryH, int ); +OGRErr CPL_DLL OGR_G_AddGeometry( OGRGeometryH, OGRGeometryH ); +OGRErr CPL_DLL OGR_G_AddGeometryDirectly( OGRGeometryH, OGRGeometryH ); +OGRErr CPL_DLL OGR_G_RemoveGeometry( OGRGeometryH, int, int ); + +int CPL_DLL OGR_G_HasCurveGeometry( OGRGeometryH, int bLookForNonLinear ); +OGRGeometryH CPL_DLL OGR_G_GetLinearGeometry( OGRGeometryH hGeom, + double dfMaxAngleStepSizeDegrees, + char** papszOptions); +OGRGeometryH CPL_DLL OGR_G_GetCurveGeometry( OGRGeometryH hGeom, + char** papszOptions ); + +OGRGeometryH CPL_DLL OGRBuildPolygonFromEdges( OGRGeometryH hLinesAsCollection, + int bBestEffort, + int bAutoClose, + double dfTolerance, + OGRErr * peErr ); + +OGRErr CPL_DLL OGRSetGenerate_DB2_V72_BYTE_ORDER( + int bGenerate_DB2_V72_BYTE_ORDER ); + +int CPL_DLL OGRGetGenerate_DB2_V72_BYTE_ORDER(void); + +void CPL_DLL OGRSetNonLinearGeometriesEnabledFlag(int bFlag); +int CPL_DLL OGRGetNonLinearGeometriesEnabledFlag(void); + +/* -------------------------------------------------------------------- */ +/* Feature related (ogr_feature.h) */ +/* -------------------------------------------------------------------- */ + +#ifdef DEBUG +typedef struct OGRFieldDefnHS *OGRFieldDefnH; +typedef struct OGRFeatureDefnHS *OGRFeatureDefnH; +typedef struct OGRFeatureHS *OGRFeatureH; +typedef struct OGRStyleTableHS *OGRStyleTableH; +#else +typedef void *OGRFieldDefnH; +typedef void *OGRFeatureDefnH; +typedef void *OGRFeatureH; +typedef void *OGRStyleTableH; +#endif +typedef struct OGRGeomFieldDefnHS *OGRGeomFieldDefnH; + +/* OGRFieldDefn */ + +OGRFieldDefnH CPL_DLL OGR_Fld_Create( const char *, OGRFieldType ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL OGR_Fld_Destroy( OGRFieldDefnH ); + +void CPL_DLL OGR_Fld_SetName( OGRFieldDefnH, const char * ); +const char CPL_DLL *OGR_Fld_GetNameRef( OGRFieldDefnH ); +OGRFieldType CPL_DLL OGR_Fld_GetType( OGRFieldDefnH ); +void CPL_DLL OGR_Fld_SetType( OGRFieldDefnH, OGRFieldType ); +OGRFieldSubType CPL_DLL OGR_Fld_GetSubType( OGRFieldDefnH ); +void CPL_DLL OGR_Fld_SetSubType( OGRFieldDefnH, OGRFieldSubType ); +OGRJustification CPL_DLL OGR_Fld_GetJustify( OGRFieldDefnH ); +void CPL_DLL OGR_Fld_SetJustify( OGRFieldDefnH, OGRJustification ); +int CPL_DLL OGR_Fld_GetWidth( OGRFieldDefnH ); +void CPL_DLL OGR_Fld_SetWidth( OGRFieldDefnH, int ); +int CPL_DLL OGR_Fld_GetPrecision( OGRFieldDefnH ); +void CPL_DLL OGR_Fld_SetPrecision( OGRFieldDefnH, int ); +void CPL_DLL OGR_Fld_Set( OGRFieldDefnH, const char *, OGRFieldType, + int, int, OGRJustification ); +int CPL_DLL OGR_Fld_IsIgnored( OGRFieldDefnH hDefn ); +void CPL_DLL OGR_Fld_SetIgnored( OGRFieldDefnH hDefn, int ); +int CPL_DLL OGR_Fld_IsNullable( OGRFieldDefnH hDefn ); +void CPL_DLL OGR_Fld_SetNullable( OGRFieldDefnH hDefn, int ); +const char CPL_DLL *OGR_Fld_GetDefault( OGRFieldDefnH hDefn ); +void CPL_DLL OGR_Fld_SetDefault( OGRFieldDefnH hDefn, const char* ); +int CPL_DLL OGR_Fld_IsDefaultDriverSpecific( OGRFieldDefnH hDefn ); + +const char CPL_DLL *OGR_GetFieldTypeName( OGRFieldType ); +const char CPL_DLL *OGR_GetFieldSubTypeName( OGRFieldSubType ); +int CPL_DLL OGR_AreTypeSubTypeCompatible( OGRFieldType eType, + OGRFieldSubType eSubType ); + +/* OGRGeomFieldDefnH */ + +OGRGeomFieldDefnH CPL_DLL OGR_GFld_Create( const char *, OGRwkbGeometryType ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL OGR_GFld_Destroy( OGRGeomFieldDefnH ); + +void CPL_DLL OGR_GFld_SetName( OGRGeomFieldDefnH, const char * ); +const char CPL_DLL *OGR_GFld_GetNameRef( OGRGeomFieldDefnH ); + +OGRwkbGeometryType CPL_DLL OGR_GFld_GetType( OGRGeomFieldDefnH ); +void CPL_DLL OGR_GFld_SetType( OGRGeomFieldDefnH, OGRwkbGeometryType ); + +OGRSpatialReferenceH CPL_DLL OGR_GFld_GetSpatialRef( OGRGeomFieldDefnH ); +void CPL_DLL OGR_GFld_SetSpatialRef( OGRGeomFieldDefnH, + OGRSpatialReferenceH hSRS ); + +int CPL_DLL OGR_GFld_IsNullable( OGRGeomFieldDefnH hDefn ); +void CPL_DLL OGR_GFld_SetNullable( OGRGeomFieldDefnH hDefn, int ); + +int CPL_DLL OGR_GFld_IsIgnored( OGRGeomFieldDefnH hDefn ); +void CPL_DLL OGR_GFld_SetIgnored( OGRGeomFieldDefnH hDefn, int ); + +/* OGRFeatureDefn */ + +OGRFeatureDefnH CPL_DLL OGR_FD_Create( const char * ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL OGR_FD_Destroy( OGRFeatureDefnH ); +void CPL_DLL OGR_FD_Release( OGRFeatureDefnH ); +const char CPL_DLL *OGR_FD_GetName( OGRFeatureDefnH ); +int CPL_DLL OGR_FD_GetFieldCount( OGRFeatureDefnH ); +OGRFieldDefnH CPL_DLL OGR_FD_GetFieldDefn( OGRFeatureDefnH, int ); +int CPL_DLL OGR_FD_GetFieldIndex( OGRFeatureDefnH, const char * ); +void CPL_DLL OGR_FD_AddFieldDefn( OGRFeatureDefnH, OGRFieldDefnH ); +OGRErr CPL_DLL OGR_FD_DeleteFieldDefn( OGRFeatureDefnH hDefn, int iField ); +OGRErr CPL_DLL OGR_FD_ReorderFieldDefns( OGRFeatureDefnH hDefn, int* panMap ); +OGRwkbGeometryType CPL_DLL OGR_FD_GetGeomType( OGRFeatureDefnH ); +void CPL_DLL OGR_FD_SetGeomType( OGRFeatureDefnH, OGRwkbGeometryType ); +int CPL_DLL OGR_FD_IsGeometryIgnored( OGRFeatureDefnH ); +void CPL_DLL OGR_FD_SetGeometryIgnored( OGRFeatureDefnH, int ); +int CPL_DLL OGR_FD_IsStyleIgnored( OGRFeatureDefnH ); +void CPL_DLL OGR_FD_SetStyleIgnored( OGRFeatureDefnH, int ); +int CPL_DLL OGR_FD_Reference( OGRFeatureDefnH ); +int CPL_DLL OGR_FD_Dereference( OGRFeatureDefnH ); +int CPL_DLL OGR_FD_GetReferenceCount( OGRFeatureDefnH ); + +int CPL_DLL OGR_FD_GetGeomFieldCount( OGRFeatureDefnH hFDefn ); +OGRGeomFieldDefnH CPL_DLL OGR_FD_GetGeomFieldDefn( OGRFeatureDefnH hFDefn, + int i ); +int CPL_DLL OGR_FD_GetGeomFieldIndex( OGRFeatureDefnH hFDefn, + const char *pszName); + +void CPL_DLL OGR_FD_AddGeomFieldDefn( OGRFeatureDefnH hFDefn, + OGRGeomFieldDefnH hGFldDefn); +OGRErr CPL_DLL OGR_FD_DeleteGeomFieldDefn( OGRFeatureDefnH hFDefn, + int iGeomField ); +int CPL_DLL OGR_FD_IsSame( OGRFeatureDefnH hFDefn, + OGRFeatureDefnH hOtherFDefn ); +/* OGRFeature */ + +OGRFeatureH CPL_DLL OGR_F_Create( OGRFeatureDefnH ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL OGR_F_Destroy( OGRFeatureH ); +OGRFeatureDefnH CPL_DLL OGR_F_GetDefnRef( OGRFeatureH ); + +OGRErr CPL_DLL OGR_F_SetGeometryDirectly( OGRFeatureH, OGRGeometryH ); +OGRErr CPL_DLL OGR_F_SetGeometry( OGRFeatureH, OGRGeometryH ); +OGRGeometryH CPL_DLL OGR_F_GetGeometryRef( OGRFeatureH ); +OGRGeometryH CPL_DLL OGR_F_StealGeometry( OGRFeatureH ); +OGRFeatureH CPL_DLL OGR_F_Clone( OGRFeatureH ); +int CPL_DLL OGR_F_Equal( OGRFeatureH, OGRFeatureH ); + +int CPL_DLL OGR_F_GetFieldCount( OGRFeatureH ); +OGRFieldDefnH CPL_DLL OGR_F_GetFieldDefnRef( OGRFeatureH, int ); +int CPL_DLL OGR_F_GetFieldIndex( OGRFeatureH, const char * ); + +int CPL_DLL OGR_F_IsFieldSet( OGRFeatureH, int ); +void CPL_DLL OGR_F_UnsetField( OGRFeatureH, int ); +OGRField CPL_DLL *OGR_F_GetRawFieldRef( OGRFeatureH, int ); + +int CPL_DLL OGR_F_GetFieldAsInteger( OGRFeatureH, int ); +GIntBig CPL_DLL OGR_F_GetFieldAsInteger64( OGRFeatureH, int ); +double CPL_DLL OGR_F_GetFieldAsDouble( OGRFeatureH, int ); +const char CPL_DLL *OGR_F_GetFieldAsString( OGRFeatureH, int ); +const int CPL_DLL *OGR_F_GetFieldAsIntegerList( OGRFeatureH, int, int * ); +const GIntBig CPL_DLL *OGR_F_GetFieldAsInteger64List( OGRFeatureH, int, int * ); +const double CPL_DLL *OGR_F_GetFieldAsDoubleList( OGRFeatureH, int, int * ); +char CPL_DLL **OGR_F_GetFieldAsStringList( OGRFeatureH, int ); +GByte CPL_DLL *OGR_F_GetFieldAsBinary( OGRFeatureH, int, int * ); +int CPL_DLL OGR_F_GetFieldAsDateTime( OGRFeatureH, int, int *, int *, int *, + int *, int *, int *, int * ); +int CPL_DLL OGR_F_GetFieldAsDateTimeEx( OGRFeatureH hFeat, int iField, + int *pnYear, int *pnMonth, int *pnDay, + int *pnHour, int *pnMinute, float *pfSecond, + int *pnTZFlag ); + +void CPL_DLL OGR_F_SetFieldInteger( OGRFeatureH, int, int ); +void CPL_DLL OGR_F_SetFieldInteger64( OGRFeatureH, int, GIntBig ); +void CPL_DLL OGR_F_SetFieldDouble( OGRFeatureH, int, double ); +void CPL_DLL OGR_F_SetFieldString( OGRFeatureH, int, const char * ); +void CPL_DLL OGR_F_SetFieldIntegerList( OGRFeatureH, int, int, int * ); +void CPL_DLL OGR_F_SetFieldInteger64List( OGRFeatureH, int, int, const GIntBig * ); +void CPL_DLL OGR_F_SetFieldDoubleList( OGRFeatureH, int, int, double * ); +void CPL_DLL OGR_F_SetFieldStringList( OGRFeatureH, int, char ** ); +void CPL_DLL OGR_F_SetFieldRaw( OGRFeatureH, int, OGRField * ); +void CPL_DLL OGR_F_SetFieldBinary( OGRFeatureH, int, int, GByte * ); +void CPL_DLL OGR_F_SetFieldDateTime( OGRFeatureH, int, + int, int, int, int, int, int, int ); +void CPL_DLL OGR_F_SetFieldDateTimeEx( OGRFeatureH, int, + int, int, int, int, int, float, int ); + +int CPL_DLL OGR_F_GetGeomFieldCount( OGRFeatureH hFeat ); +OGRGeomFieldDefnH CPL_DLL OGR_F_GetGeomFieldDefnRef( OGRFeatureH hFeat, + int iField ); +int CPL_DLL OGR_F_GetGeomFieldIndex( OGRFeatureH hFeat, + const char *pszName); + +OGRGeometryH CPL_DLL OGR_F_GetGeomFieldRef( OGRFeatureH hFeat, + int iField ); +OGRErr CPL_DLL OGR_F_SetGeomFieldDirectly( OGRFeatureH hFeat, + int iField, + OGRGeometryH hGeom ); +OGRErr CPL_DLL OGR_F_SetGeomField( OGRFeatureH hFeat, + int iField, OGRGeometryH hGeom ); + +GIntBig CPL_DLL OGR_F_GetFID( OGRFeatureH ); +OGRErr CPL_DLL OGR_F_SetFID( OGRFeatureH, GIntBig ); +void CPL_DLL OGR_F_DumpReadable( OGRFeatureH, FILE * ); +OGRErr CPL_DLL OGR_F_SetFrom( OGRFeatureH, OGRFeatureH, int ); +OGRErr CPL_DLL OGR_F_SetFromWithMap( OGRFeatureH, OGRFeatureH, int , int * ); + +const char CPL_DLL *OGR_F_GetStyleString( OGRFeatureH ); +void CPL_DLL OGR_F_SetStyleString( OGRFeatureH, const char * ); +void CPL_DLL OGR_F_SetStyleStringDirectly( OGRFeatureH, char * ); +OGRStyleTableH CPL_DLL OGR_F_GetStyleTable( OGRFeatureH ); +void CPL_DLL OGR_F_SetStyleTableDirectly( OGRFeatureH, OGRStyleTableH ); +void CPL_DLL OGR_F_SetStyleTable( OGRFeatureH, OGRStyleTableH ); + +void CPL_DLL OGR_F_FillUnsetWithDefault( OGRFeatureH hFeat, + int bNotNullableOnly, + char** papszOptions ); +int CPL_DLL OGR_F_Validate( OGRFeatureH, int nValidateFlags, int bEmitError ); + +/* -------------------------------------------------------------------- */ +/* ogrsf_frmts.h */ +/* -------------------------------------------------------------------- */ + +#ifdef DEBUG +typedef struct OGRLayerHS *OGRLayerH; +typedef struct OGRDataSourceHS *OGRDataSourceH; +typedef struct OGRDriverHS *OGRSFDriverH; +#else +typedef void *OGRLayerH; +typedef void *OGRDataSourceH; +typedef void *OGRSFDriverH; +#endif + +/* OGRLayer */ + +const char CPL_DLL* OGR_L_GetName( OGRLayerH ); +OGRwkbGeometryType CPL_DLL OGR_L_GetGeomType( OGRLayerH ); +OGRGeometryH CPL_DLL OGR_L_GetSpatialFilter( OGRLayerH ); +void CPL_DLL OGR_L_SetSpatialFilter( OGRLayerH, OGRGeometryH ); +void CPL_DLL OGR_L_SetSpatialFilterRect( OGRLayerH, + double, double, double, double ); +void CPL_DLL OGR_L_SetSpatialFilterEx( OGRLayerH, int iGeomField, + OGRGeometryH hGeom ); +void CPL_DLL OGR_L_SetSpatialFilterRectEx( OGRLayerH, int iGeomField, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); +OGRErr CPL_DLL OGR_L_SetAttributeFilter( OGRLayerH, const char * ); +void CPL_DLL OGR_L_ResetReading( OGRLayerH ); +OGRFeatureH CPL_DLL OGR_L_GetNextFeature( OGRLayerH ); +OGRErr CPL_DLL OGR_L_SetNextByIndex( OGRLayerH, GIntBig ); +OGRFeatureH CPL_DLL OGR_L_GetFeature( OGRLayerH, GIntBig ); +OGRErr CPL_DLL OGR_L_SetFeature( OGRLayerH, OGRFeatureH ); +OGRErr CPL_DLL OGR_L_CreateFeature( OGRLayerH, OGRFeatureH ); +OGRErr CPL_DLL OGR_L_DeleteFeature( OGRLayerH, GIntBig ); +OGRFeatureDefnH CPL_DLL OGR_L_GetLayerDefn( OGRLayerH ); +OGRSpatialReferenceH CPL_DLL OGR_L_GetSpatialRef( OGRLayerH ); +int CPL_DLL OGR_L_FindFieldIndex( OGRLayerH, const char *, int bExactMatch ); +GIntBig CPL_DLL OGR_L_GetFeatureCount( OGRLayerH, int ); +OGRErr CPL_DLL OGR_L_GetExtent( OGRLayerH, OGREnvelope *, int ); +OGRErr CPL_DLL OGR_L_GetExtentEx( OGRLayerH, int iGeomField, + OGREnvelope *psExtent, int bForce ); +int CPL_DLL OGR_L_TestCapability( OGRLayerH, const char * ); +OGRErr CPL_DLL OGR_L_CreateField( OGRLayerH, OGRFieldDefnH, int ); +OGRErr CPL_DLL OGR_L_CreateGeomField( OGRLayerH hLayer, + OGRGeomFieldDefnH hFieldDefn, int bForce ); +OGRErr CPL_DLL OGR_L_DeleteField( OGRLayerH, int iField ); +OGRErr CPL_DLL OGR_L_ReorderFields( OGRLayerH, int* panMap ); +OGRErr CPL_DLL OGR_L_ReorderField( OGRLayerH, int iOldFieldPos, int iNewFieldPos ); +OGRErr CPL_DLL OGR_L_AlterFieldDefn( OGRLayerH, int iField, OGRFieldDefnH hNewFieldDefn, int nFlags ); +OGRErr CPL_DLL OGR_L_StartTransaction( OGRLayerH ); +OGRErr CPL_DLL OGR_L_CommitTransaction( OGRLayerH ); +OGRErr CPL_DLL OGR_L_RollbackTransaction( OGRLayerH ); +int CPL_DLL OGR_L_Reference( OGRLayerH ); +int CPL_DLL OGR_L_Dereference( OGRLayerH ); +int CPL_DLL OGR_L_GetRefCount( OGRLayerH ); +OGRErr CPL_DLL OGR_L_SyncToDisk( OGRLayerH ); +GIntBig CPL_DLL OGR_L_GetFeaturesRead( OGRLayerH ); +const char CPL_DLL *OGR_L_GetFIDColumn( OGRLayerH ); +const char CPL_DLL *OGR_L_GetGeometryColumn( OGRLayerH ); +OGRStyleTableH CPL_DLL OGR_L_GetStyleTable( OGRLayerH ); +void CPL_DLL OGR_L_SetStyleTableDirectly( OGRLayerH, OGRStyleTableH ); +void CPL_DLL OGR_L_SetStyleTable( OGRLayerH, OGRStyleTableH ); +OGRErr CPL_DLL OGR_L_SetIgnoredFields( OGRLayerH, const char** ); +OGRErr CPL_DLL OGR_L_Intersection( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); +OGRErr CPL_DLL OGR_L_Union( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); +OGRErr CPL_DLL OGR_L_SymDifference( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); +OGRErr CPL_DLL OGR_L_Identity( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); +OGRErr CPL_DLL OGR_L_Update( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); +OGRErr CPL_DLL OGR_L_Clip( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); +OGRErr CPL_DLL OGR_L_Erase( OGRLayerH, OGRLayerH, OGRLayerH, char**, GDALProgressFunc, void * ); + +/* OGRDataSource */ + +void CPL_DLL OGR_DS_Destroy( OGRDataSourceH ); +const char CPL_DLL *OGR_DS_GetName( OGRDataSourceH ); +int CPL_DLL OGR_DS_GetLayerCount( OGRDataSourceH ); +OGRLayerH CPL_DLL OGR_DS_GetLayer( OGRDataSourceH, int ); +OGRLayerH CPL_DLL OGR_DS_GetLayerByName( OGRDataSourceH, const char * ); +OGRErr CPL_DLL OGR_DS_DeleteLayer( OGRDataSourceH, int ); +OGRSFDriverH CPL_DLL OGR_DS_GetDriver( OGRDataSourceH ); +OGRLayerH CPL_DLL OGR_DS_CreateLayer( OGRDataSourceH, const char *, + OGRSpatialReferenceH, OGRwkbGeometryType, + char ** ); +OGRLayerH CPL_DLL OGR_DS_CopyLayer( OGRDataSourceH, OGRLayerH, const char *, + char ** ); +int CPL_DLL OGR_DS_TestCapability( OGRDataSourceH, const char * ); +OGRLayerH CPL_DLL OGR_DS_ExecuteSQL( OGRDataSourceH, const char *, + OGRGeometryH, const char * ); +void CPL_DLL OGR_DS_ReleaseResultSet( OGRDataSourceH, OGRLayerH ); +int CPL_DLL OGR_DS_Reference( OGRDataSourceH ); +int CPL_DLL OGR_DS_Dereference( OGRDataSourceH ); +int CPL_DLL OGR_DS_GetRefCount( OGRDataSourceH ); +int CPL_DLL OGR_DS_GetSummaryRefCount( OGRDataSourceH ); +OGRErr CPL_DLL OGR_DS_SyncToDisk( OGRDataSourceH ); +OGRStyleTableH CPL_DLL OGR_DS_GetStyleTable( OGRDataSourceH ); +void CPL_DLL OGR_DS_SetStyleTableDirectly( OGRDataSourceH, OGRStyleTableH ); +void CPL_DLL OGR_DS_SetStyleTable( OGRDataSourceH, OGRStyleTableH ); + +/* OGRSFDriver */ + +const char CPL_DLL *OGR_Dr_GetName( OGRSFDriverH ); +OGRDataSourceH CPL_DLL OGR_Dr_Open( OGRSFDriverH, const char *, int ) CPL_WARN_UNUSED_RESULT; +int CPL_DLL OGR_Dr_TestCapability( OGRSFDriverH, const char * ); +OGRDataSourceH CPL_DLL OGR_Dr_CreateDataSource( OGRSFDriverH, const char *, + char ** ) CPL_WARN_UNUSED_RESULT; +OGRDataSourceH CPL_DLL OGR_Dr_CopyDataSource( OGRSFDriverH, OGRDataSourceH, + const char *, char ** ) CPL_WARN_UNUSED_RESULT; +OGRErr CPL_DLL OGR_Dr_DeleteDataSource( OGRSFDriverH, const char * ); + +/* OGRSFDriverRegistrar */ + +OGRDataSourceH CPL_DLL OGROpen( const char *, int, OGRSFDriverH * ) CPL_WARN_UNUSED_RESULT; +OGRDataSourceH CPL_DLL OGROpenShared( const char *, int, OGRSFDriverH * ) CPL_WARN_UNUSED_RESULT; +OGRErr CPL_DLL OGRReleaseDataSource( OGRDataSourceH ); +void CPL_DLL OGRRegisterDriver( OGRSFDriverH ); +void CPL_DLL OGRDeregisterDriver( OGRSFDriverH ); +int CPL_DLL OGRGetDriverCount(void); +OGRSFDriverH CPL_DLL OGRGetDriver( int ); +OGRSFDriverH CPL_DLL OGRGetDriverByName( const char * ); +int CPL_DLL OGRGetOpenDSCount(void); +OGRDataSourceH CPL_DLL OGRGetOpenDS( int iDS ); + + +/* note: this is also declared in ogrsf_frmts.h */ +void CPL_DLL OGRRegisterAll(void); +void CPL_DLL OGRCleanupAll(void); + +/* -------------------------------------------------------------------- */ +/* ogrsf_featurestyle.h */ +/* -------------------------------------------------------------------- */ + +#ifdef DEBUG +typedef struct OGRStyleMgrHS *OGRStyleMgrH; +typedef struct OGRStyleToolHS *OGRStyleToolH; +#else +typedef void *OGRStyleMgrH; +typedef void *OGRStyleToolH; +#endif + +/* OGRStyleMgr */ + +OGRStyleMgrH CPL_DLL OGR_SM_Create(OGRStyleTableH hStyleTable) CPL_WARN_UNUSED_RESULT; +void CPL_DLL OGR_SM_Destroy(OGRStyleMgrH hSM); + +const char CPL_DLL *OGR_SM_InitFromFeature(OGRStyleMgrH hSM, + OGRFeatureH hFeat); +int CPL_DLL OGR_SM_InitStyleString(OGRStyleMgrH hSM, + const char *pszStyleString); +int CPL_DLL OGR_SM_GetPartCount(OGRStyleMgrH hSM, + const char *pszStyleString); +OGRStyleToolH CPL_DLL OGR_SM_GetPart(OGRStyleMgrH hSM, int nPartId, + const char *pszStyleString); +int CPL_DLL OGR_SM_AddPart(OGRStyleMgrH hSM, OGRStyleToolH hST); +int CPL_DLL OGR_SM_AddStyle(OGRStyleMgrH hSM, const char *pszStyleName, + const char *pszStyleString); + +/* OGRStyleTool */ + +OGRStyleToolH CPL_DLL OGR_ST_Create(OGRSTClassId eClassId) CPL_WARN_UNUSED_RESULT; +void CPL_DLL OGR_ST_Destroy(OGRStyleToolH hST); + +OGRSTClassId CPL_DLL OGR_ST_GetType(OGRStyleToolH hST); + +OGRSTUnitId CPL_DLL OGR_ST_GetUnit(OGRStyleToolH hST); +void CPL_DLL OGR_ST_SetUnit(OGRStyleToolH hST, OGRSTUnitId eUnit, + double dfGroundPaperScale); + +const char CPL_DLL *OGR_ST_GetParamStr(OGRStyleToolH hST, int eParam, int *bValueIsNull); +int CPL_DLL OGR_ST_GetParamNum(OGRStyleToolH hST, int eParam, int *bValueIsNull); +double CPL_DLL OGR_ST_GetParamDbl(OGRStyleToolH hST, int eParam, int *bValueIsNull); +void CPL_DLL OGR_ST_SetParamStr(OGRStyleToolH hST, int eParam, const char *pszValue); +void CPL_DLL OGR_ST_SetParamNum(OGRStyleToolH hST, int eParam, int nValue); +void CPL_DLL OGR_ST_SetParamDbl(OGRStyleToolH hST, int eParam, double dfValue); +const char CPL_DLL *OGR_ST_GetStyleString(OGRStyleToolH hST); + +int CPL_DLL OGR_ST_GetRGBFromString(OGRStyleToolH hST, const char *pszColor, + int *pnRed, int *pnGreen, int *pnBlue, + int *pnAlpha); + +/* OGRStyleTable */ + +OGRStyleTableH CPL_DLL OGR_STBL_Create( void ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL OGR_STBL_Destroy( OGRStyleTableH hSTBL ); +int CPL_DLL OGR_STBL_AddStyle( OGRStyleTableH hStyleTable, + const char *pszName, + const char *pszStyleString); +int CPL_DLL OGR_STBL_SaveStyleTable( OGRStyleTableH hStyleTable, + const char *pszFilename ); +int CPL_DLL OGR_STBL_LoadStyleTable( OGRStyleTableH hStyleTable, + const char *pszFilename ); +const char CPL_DLL *OGR_STBL_Find( OGRStyleTableH hStyleTable, const char *pszName ); +void CPL_DLL OGR_STBL_ResetStyleStringReading( OGRStyleTableH hStyleTable ); +const char CPL_DLL *OGR_STBL_GetNextStyle( OGRStyleTableH hStyleTable); +const char CPL_DLL *OGR_STBL_GetLastStyleName( OGRStyleTableH hStyleTable); + +CPL_C_END + +#endif /* ndef OGR_API_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogr_apitut.dox b/bazaar/plugin/gdal/ogr/ogr_apitut.dox new file mode 100644 index 000000000..2e0d62dfb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_apitut.dox @@ -0,0 +1,1091 @@ +/*! \page ogr_apitut OGR API Tutorial + +This document is intended to document using the OGR C++ classes to read +and write data from a file. It is strongly advised that the read first +review the OGR Architecture document describing +the key classes and their roles in OGR. + +It also includes code snippets for the corresponding functions in C and Python. + +\section ogr_apitut_read Reading From OGR + +For purposes of demonstrating reading with OGR, we will construct a small +utility for dumping point layers from an OGR data source to stdout in +comma-delimited format. + +Initially it is necessary to register all the format drivers that are desired. +This is normally accomplished by calling GDALAllRegister() which registers +all format drivers built into GDAL/OGR. + +In C++ : +\code +#include "ogrsf_frmts.h" + +int main() + +{ + GDALAllRegister(); +\endcode + +In C : +\code +#include "gdal.h" + +int main() + +{ + GDALAllRegister(); +\endcode + +Next we need to open the input OGR datasource. Datasources can be files, +RDBMSes, directories full of files, or even remote web services depending on +the driver being used. However, the datasource name is always a single +string. In this case we are hardcoded to open a particular shapefile. +The second argument (GDAL_OF_VECTOR) tells the OGROpen() method +that we want a vector driver to be use and that don't require update access. +On failure NULL is returned, and +we report an error. + +In C++ : +\code + GDALDataset *poDS; + + poDS = (GDALDataset*) GDALOpenEx( "point.shp", GDAL_OF_VECTOR, NULL, NULL, NULL ); + if( poDS == NULL ) + { + printf( "Open failed.\n" ); + exit( 1 ); + } +\endcode + +In C : +\code + GDALDatasetH hDS; + + hDS = GDALOpenEx( "point.shp", GDAL_OF_VECTOR, NULL, NULL, NULL ); + if( hDS == NULL ) + { + printf( "Open failed.\n" ); + exit( 1 ); + } +\endcode + +A GDALDataset can potentially have many layers associated with it. The +number of layers available can be queried with GDALDataset::GetLayerCount() +and individual layers fetched by index using GDALDataset::GetLayer(). +However, we will just fetch the layer by name. + +In C++ : +\code + OGRLayer *poLayer; + + poLayer = poDS->GetLayerByName( "point" ); +\endcode + +In C : +\code + OGRLayerH hLayer; + + hLayer = GDALDatasetGetLayerByName( hDS, "point" ); +\endcode + +Now we want to start reading features from the layer. Before we start we +could assign an attribute or spatial filter to the layer to restrict the set +of feature we get back, but for now we are interested in getting all features. + +While it isn't strictly necessary in this +circumstance since we are starting fresh with the layer, it is often wise +to call OGRLayer::ResetReading() to ensure we are starting at the beginning of +the layer. We iterate through all the features in the layer using +OGRLayer::GetNextFeature(). It will return NULL when we run out of features. + +In C++ : +\code + OGRFeature *poFeature; + + poLayer->ResetReading(); + while( (poFeature = poLayer->GetNextFeature()) != NULL ) + { +\endcode + +In C : +\code + OGRFeatureH hFeature; + + OGR_L_ResetReading(hLayer); + while( (hFeature = OGR_L_GetNextFeature(hLayer)) != NULL ) + { +\endcode + +In order to dump all the attribute fields of the feature, it is helpful +to get the OGRFeatureDefn. This is an object, associated with the layer, +containing the definitions of all the fields. We loop over all the fields, +and fetch and report the attributes based on their type. + +In C++ : +\code + OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn(); + int iField; + + for( iField = 0; iField < poFDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn( iField ); + + if( poFieldDefn->GetType() == OFTInteger ) + printf( "%d,", poFeature->GetFieldAsInteger( iField ) ); + else if( poFieldDefn->GetType() == OFTInteger64 ) + printf( CPL_FRMT_GIB ",", poFeature->GetFieldAsInteger64( iField ) ); + else if( poFieldDefn->GetType() == OFTReal ) + printf( "%.3f,", poFeature->GetFieldAsDouble(iField) ); + else if( poFieldDefn->GetType() == OFTString ) + printf( "%s,", poFeature->GetFieldAsString(iField) ); + else + printf( "%s,", poFeature->GetFieldAsString(iField) ); + } +\endcode + +In C : +\code + OGRFeatureDefnH hFDefn = OGR_L_GetLayerDefn(hLayer); + int iField; + + for( iField = 0; iField < OGR_FD_GetFieldCount(hFDefn); iField++ ) + { + OGRFieldDefnH hFieldDefn = OGR_FD_GetFieldDefn( hFDefn, iField ); + + if( OGR_Fld_GetType(hFieldDefn) == OFTInteger ) + printf( "%d,", OGR_F_GetFieldAsInteger( hFeature, iField ) ); + else if( OGR_Fld_GetType(hFieldDefn) == OFTInteger64 ) + printf( CPL_FRMT_GIB ",", OGR_F_GetFieldAsInteger64( hFeature, iField ) ); + else if( OGR_Fld_GetType(hFieldDefn) == OFTReal ) + printf( "%.3f,", OGR_F_GetFieldAsDouble( hFeature, iField) ); + else if( OGR_Fld_GetType(hFieldDefn) == OFTString ) + printf( "%s,", OGR_F_GetFieldAsString( hFeature, iField) ); + else + printf( "%s,", OGR_F_GetFieldAsString( hFeature, iField) ); + } +\endcode + +There are a few more field types than those explicitly handled above, but +a reasonable representation of them can be fetched with the +OGRFeature::GetFieldAsString() method. In fact we could shorten the above +by using OGRFeature::GetFieldAsString() for all the types. + +Next we want to extract the geometry from the feature, and write out the point +geometry x and y. Geometries are returned as a generic OGRGeometry pointer. +We then determine the specific geometry type, and if it is a point, we +cast it to point and operate on it. If it is something else we write +placeholders. + +In C++ : +\code + OGRGeometry *poGeometry; + + poGeometry = poFeature->GetGeometryRef(); + if( poGeometry != NULL + && wkbFlatten(poGeometry->getGeometryType()) == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + printf( "%.3f,%3.f\n", poPoint->getX(), poPoint->getY() ); + } + else + { + printf( "no point geometry\n" ); + } +\endcode + +In C : +\code + OGRGeometryH hGeometry; + + hGeometry = OGR_F_GetGeometryRef(hFeature); + if( hGeometry != NULL + && wkbFlatten(OGR_G_GetGeometryType(hGeometry)) == wkbPoint ) + { + printf( "%.3f,%3.f\n", OGR_G_GetX(hGeometry, 0), OGR_G_GetY(hGeometry, 0) ); + } + else + { + printf( "no point geometry\n" ); + } +\endcode + +The wkbFlatten() macro is used above to convert the type for a wkbPoint25D +(a point with a z coordinate) into the base 2D geometry type code (wkbPoint). +For each 2D geometry type there is a corresponding 2.5D type code. The 2D +and 2.5D geometry cases are handled by the same C++ class, so our code will +handle 2D or 3D cases properly. + +Starting with OGR 1.11, + +several geometry fields can be associated to a feature. + +In C++ : +\code + OGRGeometry *poGeometry; + int iGeomField; + int nGeomFieldCount; + + nGeomFieldCount = poFeature->GetGeomFieldCount(); + for(iGeomField = 0; iGeomField < nGeomFieldCount; iGeomField ++ ) + { + poGeometry = poFeature->GetGeomFieldRef(iGeomField); + if( poGeometry != NULL + && wkbFlatten(poGeometry->getGeometryType()) == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + printf( "%.3f,%3.f\n", poPoint->getX(), poPoint->getY() ); + } + else + { + printf( "no point geometry\n" ); + } + } +\endcode + +In C : +\code + OGRGeometryH hGeometry; + int iGeomField; + int nGeomFieldCount; + + nGeomFieldCount = OGR_F_GetGeomFieldCount(hFeature); + for(iGeomField = 0; iGeomField < nGeomFieldCount; iGeomField ++ ) + { + hGeometry = OGR_F_GetGeomFieldRef(hFeature, iGeomField); + if( hGeometry != NULL + && wkbFlatten(OGR_G_GetGeometryType(hGeometry)) == wkbPoint ) + { + printf( "%.3f,%3.f\n", OGR_G_GetX(hGeometry, 0), + OGR_G_GetY(hGeometry, 0) ); + } + else + { + printf( "no point geometry\n" ); + } + } +\endcode + +In Python: +\code + nGeomFieldCount = feat.GetGeomFieldCount() + for iGeomField in range(nGeomFieldCount): + geom = feat.GetGeomFieldRef(iGeomField) + if geom is not None and geom.GetGeometryType() == ogr.wkbPoint: + print "%.3f, %.3f" % ( geom.GetX(), geom.GetY() ) + else: + print "no point geometry\n" +\endcode + +Note that OGRFeature::GetGeometryRef() and OGRFeature::GetGeomFieldRef() +return a pointer to +the internal geometry owned by the OGRFeature. There we don't actually +deleted the return geometry. However, the OGRLayer::GetNextFeature() method +returns a copy of the feature that is now owned by us. So at the end of +use we must free the feature. We could just "delete" it, but this can cause +problems in windows builds where the GDAL DLL has a different "heap" from the +main program. To be on the safe side we use a GDAL function to delete the +feature. + +In C++ : +\code + OGRFeature::DestroyFeature( poFeature ); + } +\endcode + +In C : +\code + OGR_F_Destroy( hFeature ); + } +\endcode + +The OGRLayer returned by GDALDataset::GetLayerByName() is also a reference +to an internal layer owned by the GDALDataset so we don't need to delete +it. But we do need to delete the datasource in order to close the input file. +Once again we do this with a custom delete method to avoid special win32 +heap issus. + +In C/C++ : +\code + GDALClose( poDS ); +} +\endcode + +All together our program looks like this. + +In C++ : +\code +#include "ogrsf_frmts.h" + +int main() + +{ + GDALAllRegister(); + + GDALDataset *poDS; + + poDS = (GDALDataset*) GDALOpenEx( "point.shp", GDAL_OF_VECTOR, NULL, NULL, NULL ); + if( poDS == NULL ) + { + printf( "Open failed.\n" ); + exit( 1 ); + } + + OGRLayer *poLayer; + + poLayer = poDS->GetLayerByName( "point" ); + + OGRFeature *poFeature; + + poLayer->ResetReading(); + while( (poFeature = poLayer->GetNextFeature()) != NULL ) + { + OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn(); + int iField; + + for( iField = 0; iField < poFDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn( iField ); + + if( poFieldDefn->GetType() == OFTInteger ) + printf( "%d,", poFeature->GetFieldAsInteger( iField ) ); + else if( poFieldDefn->GetType() == OFTInteger64 ) + printf( CPL_FRMT_GIB ",", poFeature->GetFieldAsInteger64( iField ) ); + else if( poFieldDefn->GetType() == OFTReal ) + printf( "%.3f,", poFeature->GetFieldAsDouble(iField) ); + else if( poFieldDefn->GetType() == OFTString ) + printf( "%s,", poFeature->GetFieldAsString(iField) ); + else + printf( "%s,", poFeature->GetFieldAsString(iField) ); + } + + OGRGeometry *poGeometry; + + poGeometry = poFeature->GetGeometryRef(); + if( poGeometry != NULL + && wkbFlatten(poGeometry->getGeometryType()) == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + printf( "%.3f,%3.f\n", poPoint->getX(), poPoint->getY() ); + } + else + { + printf( "no point geometry\n" ); + } + OGRFeature::DestroyFeature( poFeature ); + } + + GDALClose( poDS ); +} +\endcode + +In C : +\code +#include "gdal.h" + +int main() + +{ + GDALAllRegister(); + + GDALDatasetH hDS; + OGRLayerH hLayer; + OGRFeatureH hFeature; + + hDS = GDALOpenEx( "point.shp", GDAL_OF_VECTOR, NULL, NULL, NULL ); + if( hDS == NULL ) + { + printf( "Open failed.\n" ); + exit( 1 ); + } + + hLayer = GDALDatasetGetLayerByName( hDS, "point" ); + + OGR_L_ResetReading(hLayer); + while( (hFeature = OGR_L_GetNextFeature(hLayer)) != NULL ) + { + OGRFeatureDefnH hFDefn; + int iField; + OGRGeometryH hGeometry; + + hFDefn = OGR_L_GetLayerDefn(hLayer); + + for( iField = 0; iField < OGR_FD_GetFieldCount(hFDefn); iField++ ) + { + OGRFieldDefnH hFieldDefn = OGR_FD_GetFieldDefn( hFDefn, iField ); + + if( OGR_Fld_GetType(hFieldDefn) == OFTInteger ) + printf( "%d,", OGR_F_GetFieldAsInteger( hFeature, iField ) ); + else if( OGR_Fld_GetType(hFieldDefn) == OFTInteger64 ) + printf( CPL_FRMT_GIB ",", OGR_F_GetFieldAsInteger64( hFeature, iField ) ); + else if( OGR_Fld_GetType(hFieldDefn) == OFTReal ) + printf( "%.3f,", OGR_F_GetFieldAsDouble( hFeature, iField) ); + else if( OGR_Fld_GetType(hFieldDefn) == OFTString ) + printf( "%s,", OGR_F_GetFieldAsString( hFeature, iField) ); + else + printf( "%s,", OGR_F_GetFieldAsString( hFeature, iField) ); + } + + hGeometry = OGR_F_GetGeometryRef(hFeature); + if( hGeometry != NULL + && wkbFlatten(OGR_G_GetGeometryType(hGeometry)) == wkbPoint ) + { + printf( "%.3f,%3.f\n", OGR_G_GetX(hGeometry, 0), OGR_G_GetY(hGeometry, 0) ); + } + else + { + printf( "no point geometry\n" ); + } + + OGR_F_Destroy( hFeature ); + } + + GDALClose( hDS ); +} +\endcode + +In Python: +\code +import sys +from osgeo import gdal + +ds = gdal.OpenEx( "point.shp", gdal.OF_VECTOR ) +if ds is None: + print "Open failed.\n" + sys.exit( 1 ) + +lyr = ds.GetLayerByName( "point" ) + +lyr.ResetReading() + +for feat in lyr: + + feat_defn = lyr.GetLayerDefn() + for i in range(feat_defn.GetFieldCount()): + field_defn = feat_defn.GetFieldDefn(i) + + # Tests below can be simplified with just : + # print feat.GetField(i) + if field_defn.GetType() == ogr.OFTInteger or field_defn.GetType() == ogr.OFTInteger64: + print "%d" % feat.GetFieldAsInteger64(i) + elif field_defn.GetType() == ogr.OFTReal: + print "%.3f" % feat.GetFieldAsDouble(i) + elif field_defn.GetType() == ogr.OFTString: + print "%s" % feat.GetFieldAsString(i) + else: + print "%s" % feat.GetFieldAsString(i) + + geom = feat.GetGeometryRef() + if geom is not None and geom.GetGeometryType() == ogr.wkbPoint: + print "%.3f, %.3f" % ( geom.GetX(), geom.GetY() ) + else: + print "no point geometry\n" + +ds = None +\endcode + +\section ogr_apitut_write Writing To OGR + +As an example of writing through OGR, we will do roughly the opposite of the +above. A short program that reads comma separated values from input text +will be written to a point shapefile via OGR. + +As usual, we start by registering all the drivers, and then fetch the +Shapefile driver as we will need it to create our output file. + +In C++ : +\code +#include "ogrsf_frmts.h" + +int main() +{ + const char *pszDriverName = "ESRI Shapefile"; + GDALDriver *poDriver; + + GDALAllRegister(); + + poDriver = GetGDALDriverManager()->GetDriverByName(pszDriverName ); + if( poDriver == NULL ) + { + printf( "%s driver not available.\n", pszDriverName ); + exit( 1 ); + } +\endcode + +In C : +\code +#include "ogr_api.h" + +int main() +{ + const char *pszDriverName = "ESRI Shapefile"; + GDALDriver *poDriver; + + GDALAllRegister(); + + poDriver = (GDALDriver*) GDALGetDriverByName(pszDriverName ); + if( poDriver == NULL ) + { + printf( "%s driver not available.\n", pszDriverName ); + exit( 1 ); + } +\endcode + +Next we create the datasource. The ESRI Shapefile driver allows us to create +a directory full of shapefiles, or a single shapefile as a datasource. In +this case we will explicitly create a single file by including the extension +in the name. Other drivers behave differently. +The second, third, fourth and fifth argument are related to raster dimensions +(in case the driver has raster capabilities). The last argument to +the call is a list of option values, but we will just be using defaults in +this case. Details of the options supported are also format specific. + +In C ++ : +\code + GDALDataset *poDS; + + poDS = poDriver->Create( "point_out.shp", 0, 0, 0, GDT_Unknown, NULL ); + if( poDS == NULL ) + { + printf( "Creation of output file failed.\n" ); + exit( 1 ); + } +\endcode + +In C : +\code + GDALDatasetH hDS; + + hDS = GDALCreate( hDriver, "point_out.shp", 0, 0, 0, GDT_Unknown, NULL ); + if( hDS == NULL ) + { + printf( "Creation of output file failed.\n" ); + exit( 1 ); + } +\endcode + +Now we create the output layer. In this case since the datasource is a +single file, we can only have one layer. We pass wkbPoint to specify the +type of geometry supported by this layer. In this case we aren't passing +any coordinate system information or other special layer creation options. + +In C++ : +\code + OGRLayer *poLayer; + + poLayer = poDS->CreateLayer( "point_out", NULL, wkbPoint, NULL ); + if( poLayer == NULL ) + { + printf( "Layer creation failed.\n" ); + exit( 1 ); + } +\endcode + +In C : +\code + OGRLayerH hLayer; + + hLayer = GDALDatasetCreateLayer( hDS, "point_out", NULL, wkbPoint, NULL ); + if( hLayer == NULL ) + { + printf( "Layer creation failed.\n" ); + exit( 1 ); + } +\endcode + +Now that the layer exists, we need to create any attribute fields that should +appear on the layer. Fields must be added to the layer before any features +are written. To create a field we initialize an OGRField object with the +information about the field. In the case of Shapefiles, the field width and +precision is significant in the creation of the output .dbf file, so we +set it specifically, though generally the defaults are OK. For this example +we will just have one attribute, a name string associated with the x,y point. + +Note that the template OGRField we pass to CreateField() is copied internally. +We retain ownership of the object. + +In C++: +\code + OGRFieldDefn oField( "Name", OFTString ); + + oField.SetWidth(32); + + if( poLayer->CreateField( &oField ) != OGRERR_NONE ) + { + printf( "Creating Name field failed.\n" ); + exit( 1 ); + } +\endcode + +In C: +\code + OGRFieldDefnH hFieldDefn; + + hFieldDefn = OGR_Fld_Create( "Name", OFTString ); + + OGR_Fld_SetWidth( hFieldDefn, 32); + + if( OGR_L_CreateField( hLayer, hFieldDefn, TRUE ) != OGRERR_NONE ) + { + printf( "Creating Name field failed.\n" ); + exit( 1 ); + } + + OGR_Fld_Destroy(hFieldDefn); +\endcode + +The following snipping loops reading lines of the form "x,y,name" from stdin, +and parsing them. + +In C++ and in C : +\code + double x, y; + char szName[33]; + + while( !feof(stdin) + && fscanf( stdin, "%lf,%lf,%32s", &x, &y, szName ) == 3 ) + { +\endcode + +To write a feature to disk, we must create a local OGRFeature, set attributes +and attach geometry before trying to write it to the layer. It is +imperative that this feature be instantiated from the OGRFeatureDefn +associated with the layer it will be written to. + +In C++ : +\code + OGRFeature *poFeature; + + poFeature = OGRFeature::CreateFeature( poLayer->GetLayerDefn() ); + poFeature->SetField( "Name", szName ); +\endcode + +In C : +\code + OGRFeatureH hFeature; + + hFeature = OGR_F_Create( OGR_L_GetLayerDefn( hLayer ) ); + OGR_F_SetFieldString( hFeature, OGR_F_GetFieldIndex(hFeature, "Name"), szName ); +\endcode + +We create a local geometry object, and assign its copy (indirectly) to the feature. +The OGRFeature::SetGeometryDirectly() differs from OGRFeature::SetGeometry() +in that the direct method gives ownership of the geometry to the feature. +This is generally more efficient as it avoids an extra deep object copy +of the geometry. + +In C++ : +\code + OGRPoint pt; + pt.setX( x ); + pt.setY( y ); + + poFeature->SetGeometry( &pt ); +\endcode + +In C : +\code + OGRGeometryH hPt; + hPt = OGR_G_CreateGeometry(wkbPoint); + OGR_G_SetPoint_2D(hPt, 0, x, y); + + OGR_F_SetGeometry( hFeature, hPt ); + OGR_G_DestroyGeometry(hPt); +\endcode + +Now we create a feature in the file. The OGRLayer::CreateFeature() does not +take ownership of our feature so we clean it up when done with it. + +In C++ : +\code + if( poLayer->CreateFeature( poFeature ) != OGRERR_NONE ) + { + printf( "Failed to create feature in shapefile.\n" ); + exit( 1 ); + } + + OGRFeature::DestroyFeature( poFeature ); + } +\endcode + +In C : +\code + if( OGR_L_CreateFeature( hLayer, hFeature ) != OGRERR_NONE ) + { + printf( "Failed to create feature in shapefile.\n" ); + exit( 1 ); + } + + OGR_F_Destroy( hFeature ); + } +\endcode + +Finally we need to close down the datasource in order to ensure headers +are written out in an orderly way and all resources are recovered. + +In C/C++ : +\code + GDALClose( poDS ); +} +\endcode + +The same program all in one block looks like this: + +In C++ : +\code +#include "ogrsf_frmts.h" + +int main() +{ + const char *pszDriverName = "ESRI Shapefile"; + GDALDriver *poDriver; + + GDALAllRegister(); + + poDriver = GetGDALDriverManager()->GetDriverByName(pszDriverName ); + if( poDriver == NULL ) + { + printf( "%s driver not available.\n", pszDriverName ); + exit( 1 ); + } + + GDALDataset *poDS; + + poDS = poDriver->Create( "point_out.shp", 0, 0, 0, GDT_Unknown, NULL ); + if( poDS == NULL ) + { + printf( "Creation of output file failed.\n" ); + exit( 1 ); + } + + OGRLayer *poLayer; + + poLayer = poDS->CreateLayer( "point_out", NULL, wkbPoint, NULL ); + if( poLayer == NULL ) + { + printf( "Layer creation failed.\n" ); + exit( 1 ); + } + + OGRFieldDefn oField( "Name", OFTString ); + + oField.SetWidth(32); + + if( poLayer->CreateField( &oField ) != OGRERR_NONE ) + { + printf( "Creating Name field failed.\n" ); + exit( 1 ); + } + + double x, y; + char szName[33]; + + while( !feof(stdin) + && fscanf( stdin, "%lf,%lf,%32s", &x, &y, szName ) == 3 ) + { + OGRFeature *poFeature; + + poFeature = OGRFeature::CreateFeature( poLayer->GetLayerDefn() ); + poFeature->SetField( "Name", szName ); + + OGRPoint pt; + + pt.setX( x ); + pt.setY( y ); + + poFeature->SetGeometry( &pt ); + + if( poLayer->CreateFeature( poFeature ) != OGRERR_NONE ) + { + printf( "Failed to create feature in shapefile.\n" ); + exit( 1 ); + } + + OGRFeature::DestroyFeature( poFeature ); + } + + GDALClose( poDS ); +} +\endcode + +In C : +\code +#include "gdal.h" + +int main() +{ + const char *pszDriverName = "ESRI Shapefile"; + GDALDriverH hDriver; + GDALDatasetH hDS; + OGRLayerH hLayer; + OGRFieldDefnH hFieldDefn; + double x, y; + char szName[33]; + + GDALAllRegister(); + + hDriver = GDALGetDriverByName( pszDriverName ); + if( hDriver == NULL ) + { + printf( "%s driver not available.\n", pszDriverName ); + exit( 1 ); + } + + hDS = GDALCreate( hDriver, "point_out.shp", 0, 0, 0, GDT_Unknown, NULL ); + if( hDS == NULL ) + { + printf( "Creation of output file failed.\n" ); + exit( 1 ); + } + + hLayer = GDALDatasetCreateLayer( hDS, "point_out", NULL, wkbPoint, NULL ); + if( hLayer == NULL ) + { + printf( "Layer creation failed.\n" ); + exit( 1 ); + } + + hFieldDefn = OGR_Fld_Create( "Name", OFTString ); + + OGR_Fld_SetWidth( hFieldDefn, 32); + + if( OGR_L_CreateField( hLayer, hFieldDefn, TRUE ) != OGRERR_NONE ) + { + printf( "Creating Name field failed.\n" ); + exit( 1 ); + } + + OGR_Fld_Destroy(hFieldDefn); + + while( !feof(stdin) + && fscanf( stdin, "%lf,%lf,%32s", &x, &y, szName ) == 3 ) + { + OGRFeatureH hFeature; + OGRGeometryH hPt; + + hFeature = OGR_F_Create( OGR_L_GetLayerDefn( hLayer ) ); + OGR_F_SetFieldString( hFeature, OGR_F_GetFieldIndex(hFeature, "Name"), szName ); + + hPt = OGR_G_CreateGeometry(wkbPoint); + OGR_G_SetPoint_2D(hPt, 0, x, y); + + OGR_F_SetGeometry( hFeature, hPt ); + OGR_G_DestroyGeometry(hPt); + + if( OGR_L_CreateFeature( hLayer, hFeature ) != OGRERR_NONE ) + { + printf( "Failed to create feature in shapefile.\n" ); + exit( 1 ); + } + + OGR_F_Destroy( hFeature ); + } + + GDALClose( hDS ); +} +\endcode + +In Python : +\code +import sys +from osgeo import gdal +from osgeo import ogr +import string + +driverName = "ESRI Shapefile" +drv = gdal.GetDriverByName( driverName ) +if drv is None: + print "%s driver not available.\n" % driverName + sys.exit( 1 ) + +ds = drv.Create( "point_out.shp", 0, 0, 0, gdal.GDT_Unknown ) +if ds is None: + print "Creation of output file failed.\n" + sys.exit( 1 ) + +lyr = ds.CreateLayer( "point_out", None, ogr.wkbPoint ) +if lyr is None: + print "Layer creation failed.\n" + sys.exit( 1 ) + +field_defn = ogr.FieldDefn( "Name", ogr.OFTString ) +field_defn.SetWidth( 32 ) + +if lyr.CreateField ( field_defn ) != 0: + print "Creating Name field failed.\n" + sys.exit( 1 ) + +# Expected format of user input: x y name +linestring = raw_input() +linelist = string.split(linestring) + +while len(linelist) == 3: + x = float(linelist[0]) + y = float(linelist[1]) + name = linelist[2] + + feat = ogr.Feature( lyr.GetLayerDefn()) + feat.SetField( "Name", name ) + + pt = ogr.Geometry(ogr.wkbPoint) + pt.SetPoint_2D(0, x, y) + + feat.SetGeometry(pt) + + if lyr.CreateFeature(feat) != 0: + print "Failed to create feature in shapefile.\n" + sys.exit( 1 ) + + feat.Destroy() + + linestring = raw_input() + linelist = string.split(linestring) + +ds = None +\endcode + +Starting with OGR 1.11, + +several geometry fields can be associated to a feature. This capability +is just available for a few file formats, such as PostGIS. + +To create such datasources, geometry fields must be first created. +Spatial reference system objects can be associated to each geometry field. + +In C++ : +\code + OGRGeomFieldDefn oPointField( "PointField", wkbPoint ); + OGRSpatialReference* poSRS = new OGRSpatialReference(); + poSRS->importFromEPSG(4326); + oPointField.SetSpatialRef(poSRS); + poSRS->Release(); + + if( poLayer->CreateGeomField( &oPointField ) != OGRERR_NONE ) + { + printf( "Creating field PointField failed.\n" ); + exit( 1 ); + } + + OGRGeomFieldDefn oFieldPoint2( "PointField2", wkbPoint ); + poSRS = new OGRSpatialReference(); + poSRS->importFromEPSG(32631); + oPointField2.SetSpatialRef(poSRS); + poSRS->Release(); + + if( poLayer->CreateGeomField( &oPointField2 ) != OGRERR_NONE ) + { + printf( "Creating field PointField2 failed.\n" ); + exit( 1 ); + } +\endcode + +In C : +\code + OGRGeomFieldDefnH hPointField; + OGRGeomFieldDefnH hPointField2; + OGRSpatialReferenceH hSRS; + + hPointField = OGR_GFld_Create( "PointField", wkbPoint ); + hSRS = OSRNewSpatialReference( NULL ); + OSRImportFromEPSG(hSRS, 4326); + OGR_GFld_SetSpatialRef(hPointField, hSRS); + OSRRelease(hSRS); + + if( OGR_L_CreateGeomField( hLayer, hPointField ) != OGRERR_NONE ) + { + printf( "Creating field PointField failed.\n" ); + exit( 1 ); + } + + OGR_GFld_Destroy( hPointField ); + + hPointField2 = OGR_GFld_Create( "PointField2", wkbPoint ); + OSRImportFromEPSG(hSRS, 32631); + OGR_GFld_SetSpatialRef(hPointField2, hSRS); + OSRRelease(hSRS); + + if( OGR_L_CreateGeomField( hLayer, hPointField2 ) != OGRERR_NONE ) + { + printf( "Creating field PointField2 failed.\n" ); + exit( 1 ); + } + + OGR_GFld_Destroy( hPointField2 ); +\endcode + +To write a feature to disk, we must create a local OGRFeature, set attributes +and attach geometries before trying to write it to the layer. It is +imperative that this feature be instantiated from the OGRFeatureDefn +associated with the layer it will be written to. + +In C++ : +\code + OGRFeature *poFeature; + OGRGeometry *poGeometry; + char* pszWKT; + + poFeature = OGRFeature::CreateFeature( poLayer->GetLayerDefn() ); + + pszWKT = (char*) "POINT (2 49)"; + OGRGeometryFactory::createFromWkt( &pszWKT, NULL, &poGeometry ); + poFeature->SetGeomFieldDirectly( "PointField", poGeometry ); + + pszWKT = (char*) "POINT (500000 4500000)"; + OGRGeometryFactory::createFromWkt( &pszWKT, NULL, &poGeometry ); + poFeature->SetGeomFieldDirectly( "PointField2", poGeometry ); + + if( poLayer->CreateFeature( poFeature ) != OGRERR_NONE ) + { + printf( "Failed to create feature.\n" ); + exit( 1 ); + } + + OGRFeature::DestroyFeature( poFeature ); +\endcode + +In C : +\code + OGRFeatureH hFeature; + OGRGeometryH hGeometry; + char* pszWKT; + + poFeature = OGR_F_Create( OGR_L_GetLayerDefn(hLayer) ); + + pszWKT = (char*) "POINT (2 49)"; + OGR_G_CreateFromWkt( &pszWKT, NULL, &hGeometry ); + OGR_F_SetGeomFieldDirectly( hFeature, + OGR_F_GetGeomFieldIndex(hFeature, "PointField"), hGeometry ); + + pszWKT = (char*) "POINT (500000 4500000)"; + OGR_G_CreateFromWkt( &pszWKT, NULL, &hGeometry ); + OGR_F_SetGeomFieldDirectly( hFeature, + OGR_F_GetGeomFieldIndex(hFeature, "PointField2"), hGeometry ); + + if( OGR_L_CreateFeature( hFeature ) != OGRERR_NONE ) + { + printf( "Failed to create feature.\n" ); + exit( 1 ); + } + + OGR_F_Destroy( hFeature ); +\endcode + +In Python : +\code + feat = ogr.Feature( lyr.GetLayerDefn() ) + + feat.SetGeomFieldDirectly( "PointField", + ogr.CreateGeometryFromWkt( "POINT (2 49)" ) ) + feat.SetGeomFieldDirectly( "PointField2", + ogr.CreateGeometryFromWkt( "POINT (500000 4500000)" ) ) + + if lyr.CreateFeature( feat ) != 0 ) + { + print( "Failed to create feature.\n" ); + sys.exit( 1 ); + } +\endcode +*/ diff --git a/bazaar/plugin/gdal/ogr/ogr_arch.dox b/bazaar/plugin/gdal/ogr/ogr_arch.dox new file mode 100644 index 000000000..f55253672 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_arch.dox @@ -0,0 +1,255 @@ +/*! \page ogr_arch OGR Architecture + +This document is intended to document the OGR classes. The OGR +classes are intended to be generic (not specific to OLE DB or COM or +Windows) but are used as a foundation for implementing OLE DB Provider +support, as well as client side support for SFCOM. It is intended that +these same OGR classes could be used by an implementation of SFCORBA +for instance or used directly by C++ programs wanting to use an OpenGIS +simple features inspired API.

    + +Because OGR is modelled on the OpenGIS simple features data model, it is +very helpful to review the SFCOM, or other simple features interface +specifications which can be retrieved from the +Open Geospatial Consortium web site. Data +types, and method names are modelled on those from the interface +specifications.

    + +\section ogr_arch_overview Class Overview + +

      +
    • Geometry (ogr_geometry.h): +The geometry classes (OGRGeometry, etc) encapsulate the OpenGIS model vector +data as well as providing some geometry operations, and translation to/from +well known binary and text format. A geometry includes a spatial reference +system (projection).

      + +

    • Spatial Reference +(ogr_spatialref.h): +An OGRSpatialReference encapsulates the definition of a projection and +datum.

      + +

    • Feature (ogr_feature.h): +The OGRFeature encapsulates the definition of a whole feature, that is a +geometry and a set of attributes.

      + +

    • Feature Class Definition +(ogr_feature.h): +The OGRFeatureDefn class captures the schema (set of field definitions) for +a group of related features (normally a whole layer).

      + +

    • Layer +(ogrsf_frmts.h): +OGRLayer is an abstract base class represent a layer of features in an +GDALDataset.

      + +

    • Dataset +(gdal_priv.h): +A GDALDataset is an abstract base class +representing a file or database containing one or more OGRLayer +objects.

      + +

    • Drivers +(gdal_priv.h): +A GDALDriver represents a translator for a specific +format, opening GDALDataset objects. All available drivers are managed +by the GDALDriverManager.

      + +

    + +\section ogr_arch_geometry Geometry + +The geometry classes are represent various kinds of vector geometry. All +the geometry classes derived from OGRGeometry which defines the common +services of all geometries. Types of geometry include OGRPoint, OGRLineString, +OGRPolygon, OGRGeometryCollection, OGRMultiPolygon, OGRMultiPoint, +and OGRMultiLineString. + +GDAL 2.0 extends those geometry type with non-linear geometries with the +OGRCircularString, OGRCompoundCurve, OGRCurvePolygon, OGRMultiCurve and +OGRMultiSurface classes. + +Additional intermediate abstract base classes contain functionality that +could eventually be implemented by other geometry types. These include +OGRCurve (base class for OGRLineString) and OGRSurface (base class for +OGRPolygon). Some intermediate interfaces modelled in the simple features +abstract model and SFCOM are not modelled in OGR at this time. In most +cases the methods are aggregated into other classes.

    + +The OGRGeometryFactory is used to convert well known text, and well known +binary format data into geometries. These are predefined ASCII and binary +formats for representing all the types of simple features geometries.

    + +In a manner based on the geometry object in SFCOM, the OGRGeometry includes +a reference to an OGRSpatialReference object, defining the spatial reference +system of that geometry. This is normally a reference to a shared +spatial reference object with reference counting for each of the +OGRGeometry objects using it.

    + +Many of the spatial analysis methods (such as computing overlaps and so +forth) are not implemented at this time for OGRGeometry.

    + +While it is theoretically possible to derive other or more specific +geometry classes from the existing OGRGeometry classes, this isn't an +aspect that has been well thought out. In particular, it would be +possible to create specialized classes using the OGRGeometryFactory without +modifying it.

    + +\subsection ogr_arch_geometry_compat_curves Compatibility issues with GDAL 2.0 non-linear geometries + +Generic mechanisms have been introduced so that creating or modifying a feature +with a non-linear geometry in a layer of a driver that does not support it will +transform that geometry in the closest matching linear geometry. + +On the other side, when retrieving data from the OGR C API, the OGRSetNonLinearGeometriesEnabledFlag() +function can be used, so that geometries and layer geometry type returned are +also converted to their linear approximation if necessary. + +\section ogr_arch_srs Spatial Reference + +The OGRSpatialReference class is intended to store an OpenGIS Spatial +Reference System definition. Currently local, geographic and projected +coordinate systems are supported. Vertical coordinate systems, geocentric +coordinate systems, and compound (horizontal + vertical) coordinate systems +are as well supported in recent GDAL versions.

    + +The spatial coordinate system data model is inherited from the OpenGIS +Well Known Text format. A simple form of this is defined in the +Simple Features specifications. A more sophisticated form is found in +the Coordinate Transformation specification. The OGRSpatialReference is +built on the features of the Coordinate Transformation specification but +is intended to be compatible with the earlier simple features form.

    + +There is also an associated OGRCoordinateTransformation class that +encapsulates use of PROJ.4 for converting between different coordinate +systems. There is a tutorial available +describing how to use the OGRSpatialReference class.

    + +\section ogr_arch_feature Feature / Feature Definition + +The OGRGeometry captures the geometry of a vector feature ... the +spatial position/region of a feature. The OGRFeature contains this +geometry, and adds feature attributes, feature id, +and a feature class identifier. Starting with OGR 1.11, + +several geometries can be associated to a OGRFeature.

    + +The set of attributes, their types, names and so forth is represented +via the OGRFeatureDefn class. One OGRFeatureDefn normally exists for a +layer of features. The same definition is shared in a reference counted +manner by the feature of that type (or feature class).

    + +The feature id (FID) of a feature is intended to be a unique identifier for +the feature within the layer it is a member of. Freestanding features, or +features not yet written to a layer may have a null (OGRNullFID) feature id. +The feature ids are modelled in OGR as a 64-bit integer (GDAL 2.0 or later); +however, this is not sufficiently expressive to model the natural feature ids in some +formats. For instance, the GML feature id is a string.

    + +The feature class +also contains an indicator of the types of geometry allowed for that feature +class (returned as an OGRwkbGeometryType from OGRFeatureDefn::GetGeomType()). +If this is wkbUnknown then any type of geometry is allowed. This implies that +features in a given layer can potentially be of different geometry types +though they will always share a common attribute schema.

    + +Starting with OGR 1.11, several geometry fields can be associated to a feature +class. Each geometry field has its own indicator of geometry type allowed, +returned by OGRGeomFieldDefn::GetType(), and its spatial reference system, +returned by OGRGeomFieldDefn::GetSpatialRef(). + +The OGRFeatureDefn also contains a feature class name (normally +used as a layer name).

    + + +\section ogr_arch_layer Layer + +An OGRLayer represents a layer of features within a data source. All +features in an OGRLayer share a common schema and are of the same +OGRFeatureDefn. An OGRLayer class also contains methods for reading +features from the data source. +The OGRLayer can be thought of as a gateway for reading and +writing features from an underlying data source, normally a file format. +In SFCOM and other table based simple features implementation an OGRLayer +represents a spatial table.

    + +The OGRLayer includes methods for sequential and random reading and writing. +Read access (via the +OGRLayer::GetNextFeature() method) normally reads all features, one at a +time sequentially; however, it can be limited to return features intersecting +a particular geographic region by installing a spatial filter on the +OGRLayer (via the OGRLayer::SetSpatialFilter() method).

    + +One flaw in the +current OGR architecture is that the spatial filter is set directly on +the OGRLayer which is intended to be the only representative of a given +layer in a data source. This means it isn't possible to have multiple +read operations active at one time with different spatial filters on +each. This aspect may be revised in the future to introduce an OGRLayerView +class or something similar.

    + +Another question that might arise is why the OGRLayer and OGRFeatureDefn +classes are distinct. An OGRLayer always has a one-to-one relationship to +an OGRFeatureDefn, so why not amalgamate the classes. There are two reasons: + +

      + +
    1. As defined now OGRFeature and OGRFeatureDefn don't depend on OGRLayer, +so they can exist independently in memory without regard to a particular +layer in a data store.

      + +

    2. The SF CORBA model does not have a concept of a layer with a single +fixed schema the way that the SFCOM and SFSQL models do. The fact that +features belong to a feature collection that is potentially not directly +related to their current feature grouping may be important to implementing +SFCORBA support using OGR. + +
    + +The OGRLayer class is an abstract base class. An implementation is +expected to be subclassed for each file format driver implemented. +OGRLayers are normally owned directly by their GDALDataset, and aren't +instantiated or destroyed directly.

    + +\section ogr_arch_dataset Dataset + +A GDALDataset represents a set of OGRLayer objects. This usually +represents a single file, set of files, database or gateway. A GDALDataset +has a list of OGRLayers which it owns but can return references to.

    + +GDALDataset is an abstract base class. An implementation is +expected to be subclassed for each file format driver implemented. +GDALDataset objects are not normally instantiated directly but rather with +the assistance of an GDALDriver. Deleting an GDALDataset closes access +to the underlying persistent data source, but does not normally result in +deletion of that file.

    + +A GDALDataset has a name (usually a filename) that can be used to reopen +the data source with a GDALDriver.

    + +The GDALDataset also has support for executing a datasource specific +command, normally a form of SQL. This is accomplished via the +GDALDataset::ExecuteSQL() method. While some datasources (such as PostGIS +and Oracle) pass the SQL through to an underlying database, OGR also includes +support for evaluating a subset of the SQL SELECT statement against any +datasource.

    + +\section ogr_arch_drivers Drivers + +A GDALDriver object is instantiated for each file format supported. +The GDALDriver objects are registered with the GDALDriverManager, a +singleton class that is normally used to open new datasets.

    + +It is intended that a new GDALDriver object is instanciated and define function +pointers for operations like Identify(), Open() for each +file format to be supported (along with a file format specific +GDALDataset, and OGRLayer classes).

    + +On application startup registration +functions are normally called for each desired file format. These functions +instantiate the appropriate GDALDriver objects, and register them with the +GDALDriverManager. When a dataset is to be opened, the driver manager +will normally try each GDALDataset in turn, until one succeeds, returning +a GDALDataset object.

    + +*/ diff --git a/bazaar/plugin/gdal/ogr/ogr_capi_test.c b/bazaar/plugin/gdal/ogr/ogr_capi_test.c new file mode 100644 index 000000000..ba01988db --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_capi_test.c @@ -0,0 +1,251 @@ +/********************************************************************** + * $Id: ogr_capi_test.c 10645 2007-01-18 02:22:39Z warmerdam $ + ********************************************************************** + * Copyright (c) 2003, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * + * Test the OGR C API (ogr_api.h) + * + * Compile using: + * + * gcc -g ogr_capi_test.c `gdal-config --libs` `gdal-config --cflags` \ + * -o ogr_capi_test + * + ****************************************************************************/ + +#include "ogr_api.h" + +int OGRCDump(const char *pszFname); +int OGRCCreate(const char *pszFname); + +/********************************************************************** + * main() + **********************************************************************/ +int main(int argc, char *argv[]) +{ + + if (argc == 3 && EQUAL(argv[1], "dump")) + { + return OGRCDump(argv[2]); + } + else if (argc == 3 && EQUAL(argv[1], "create")) + { + return OGRCCreate(argv[2]); + } + else + { + printf("Usage: ogr_capi_test \n"); + } + + return 0; +} + +/********************************************************************** + * OGRCDump() + * + * Open a dataset using OGR and dump all its layers. + * + **********************************************************************/ + +int OGRCDump(const char *pszFname) +{ + OGRDataSourceH datasource; + int i, numLayers; + + /* Register all OGR drivers */ + OGRRegisterAll(); + + /* Open data source */ + datasource = OGROpen(pszFname, 0 /* bUpdate */, NULL); + + if (datasource == NULL) + { + printf("Unable to open %s\n", pszFname); + return -1; + } + + /* Loop through layers and dump their contents */ + + numLayers = OGR_DS_GetLayerCount(datasource); + for(i=0; i + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef OGR_CORE_H_INCLUDED +#define OGR_CORE_H_INCLUDED + +#include "cpl_port.h" +#include "gdal_version.h" + +/** + * \file + * + * Core portability services for cross-platform OGR code. + */ + +/** + * Simple container for a bounding region. + */ + +#if defined(__cplusplus) && !defined(CPL_SUPRESS_CPLUSPLUS) +class CPL_DLL OGREnvelope +{ + public: + OGREnvelope() : MinX(0.0), MaxX(0.0), MinY(0.0), MaxY(0.0) + { + } + + OGREnvelope(const OGREnvelope& oOther) : + MinX(oOther.MinX),MaxX(oOther.MaxX), MinY(oOther.MinY), MaxY(oOther.MaxY) + { + } + + double MinX; + double MaxX; + double MinY; + double MaxY; + +/* See http://trac.osgeo.org/gdal/ticket/5299 for details on this pragma */ +#if ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && !defined(_MSC_VER)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + int IsInit() const { return MinX != 0 || MinY != 0 || MaxX != 0 || MaxY != 0; } + +#if ((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && !defined(_MSC_VER)) +#pragma GCC diagnostic pop +#endif + + void Merge( OGREnvelope const& sOther ) { + if( IsInit() ) + { + MinX = MIN(MinX,sOther.MinX); + MaxX = MAX(MaxX,sOther.MaxX); + MinY = MIN(MinY,sOther.MinY); + MaxY = MAX(MaxY,sOther.MaxY); + } + else + { + MinX = sOther.MinX; + MaxX = sOther.MaxX; + MinY = sOther.MinY; + MaxY = sOther.MaxY; + } + } + void Merge( double dfX, double dfY ) { + if( IsInit() ) + { + MinX = MIN(MinX,dfX); + MaxX = MAX(MaxX,dfX); + MinY = MIN(MinY,dfY); + MaxY = MAX(MaxY,dfY); + } + else + { + MinX = MaxX = dfX; + MinY = MaxY = dfY; + } + } + + void Intersect( OGREnvelope const& sOther ) { + if(Intersects(sOther)) + { + if( IsInit() ) + { + MinX = MAX(MinX,sOther.MinX); + MaxX = MIN(MaxX,sOther.MaxX); + MinY = MAX(MinY,sOther.MinY); + MaxY = MIN(MaxY,sOther.MaxY); + } + else + { + MinX = sOther.MinX; + MaxX = sOther.MaxX; + MinY = sOther.MinY; + MaxY = sOther.MaxY; + } + } + else + { + MinX = 0; + MaxX = 0; + MinY = 0; + MaxY = 0; + } + } + + int Intersects(OGREnvelope const& other) const + { + return MinX <= other.MaxX && MaxX >= other.MinX && + MinY <= other.MaxY && MaxY >= other.MinY; + } + + int Contains(OGREnvelope const& other) const + { + return MinX <= other.MinX && MinY <= other.MinY && + MaxX >= other.MaxX && MaxY >= other.MaxY; + } +}; +#else +typedef struct +{ + double MinX; + double MaxX; + double MinY; + double MaxY; +} OGREnvelope; +#endif + + +/** + * Simple container for a bounding region in 3D. + */ + +#if defined(__cplusplus) && !defined(CPL_SURESS_CPLUSPLUS) +class CPL_DLL OGREnvelope3D : public OGREnvelope +{ + public: + OGREnvelope3D() : OGREnvelope(), MinZ(0.0), MaxZ(0.0) + { + } + + OGREnvelope3D(const OGREnvelope3D& oOther) : + OGREnvelope(oOther), + MinZ(oOther.MinZ), MaxZ(oOther.MaxZ) + { + } + + double MinZ; + double MaxZ; + + int IsInit() const { return MinX != 0 || MinY != 0 || MaxX != 0 || MaxY != 0 || MinZ != 0 || MaxZ != 0; } + void Merge( OGREnvelope3D const& sOther ) { + if( IsInit() ) + { + MinX = MIN(MinX,sOther.MinX); + MaxX = MAX(MaxX,sOther.MaxX); + MinY = MIN(MinY,sOther.MinY); + MaxY = MAX(MaxY,sOther.MaxY); + MinZ = MIN(MinZ,sOther.MinZ); + MaxZ = MAX(MaxZ,sOther.MaxZ); + } + else + { + MinX = sOther.MinX; + MaxX = sOther.MaxX; + MinY = sOther.MinY; + MaxY = sOther.MaxY; + MinZ = sOther.MinZ; + MaxZ = sOther.MaxZ; + } + } + void Merge( double dfX, double dfY, double dfZ ) { + if( IsInit() ) + { + MinX = MIN(MinX,dfX); + MaxX = MAX(MaxX,dfX); + MinY = MIN(MinY,dfY); + MaxY = MAX(MaxY,dfY); + MinZ = MIN(MinZ,dfZ); + MaxZ = MAX(MaxZ,dfZ); + } + else + { + MinX = MaxX = dfX; + MinY = MaxY = dfY; + MinZ = MaxZ = dfZ; + } + } + + void Intersect( OGREnvelope3D const& sOther ) { + if(Intersects(sOther)) + { + if( IsInit() ) + { + MinX = MAX(MinX,sOther.MinX); + MaxX = MIN(MaxX,sOther.MaxX); + MinY = MAX(MinY,sOther.MinY); + MaxY = MIN(MaxY,sOther.MaxY); + MinZ = MAX(MinZ,sOther.MinZ); + MaxZ = MIN(MaxZ,sOther.MaxZ); + } + else + { + MinX = sOther.MinX; + MaxX = sOther.MaxX; + MinY = sOther.MinY; + MaxY = sOther.MaxY; + MinZ = sOther.MinZ; + MaxZ = sOther.MaxZ; + } + } + else + { + MinX = 0; + MaxX = 0; + MinY = 0; + MaxY = 0; + MinZ = 0; + MaxZ = 0; + } + } + + int Intersects(OGREnvelope3D const& other) const + { + return MinX <= other.MaxX && MaxX >= other.MinX && + MinY <= other.MaxY && MaxY >= other.MinY && + MinZ <= other.MaxZ && MaxZ >= other.MinZ; + } + + int Contains(OGREnvelope3D const& other) const + { + return MinX <= other.MinX && MinY <= other.MinY && + MaxX >= other.MaxX && MaxY >= other.MaxY && + MinZ <= other.MinZ && MaxZ >= other.MaxZ; + } +}; +#else +typedef struct +{ + double MinX; + double MaxX; + double MinY; + double MaxY; + double MinZ; + double MaxZ; +} OGREnvelope3D; +#endif + + +CPL_C_START + +void CPL_DLL *OGRMalloc( size_t ); +void CPL_DLL *OGRCalloc( size_t, size_t ); +void CPL_DLL *OGRRealloc( void *, size_t ); +char CPL_DLL *OGRStrdup( const char * ); +void CPL_DLL OGRFree( void * ); + +typedef int OGRErr; + +#define OGRERR_NONE 0 +#define OGRERR_NOT_ENOUGH_DATA 1 /* not enough data to deserialize */ +#define OGRERR_NOT_ENOUGH_MEMORY 2 +#define OGRERR_UNSUPPORTED_GEOMETRY_TYPE 3 +#define OGRERR_UNSUPPORTED_OPERATION 4 +#define OGRERR_CORRUPT_DATA 5 +#define OGRERR_FAILURE 6 +#define OGRERR_UNSUPPORTED_SRS 7 +#define OGRERR_INVALID_HANDLE 8 +#define OGRERR_NON_EXISTING_FEATURE 9 /* added in GDAL 2.0 */ + +typedef int OGRBoolean; + +/* -------------------------------------------------------------------- */ +/* ogr_geometry.h related definitions. */ +/* -------------------------------------------------------------------- */ + +/** + * List of well known binary geometry types. These are used within the BLOBs + * but are also returned from OGRGeometry::getGeometryType() to identify the + * type of a geometry object. + */ +typedef enum +{ + wkbUnknown = 0, /**< unknown type, non-standard */ + + wkbPoint = 1, /**< 0-dimensional geometric object, standard WKB */ + wkbLineString = 2, /**< 1-dimensional geometric object with linear + * interpolation between Points, standard WKB */ + wkbPolygon = 3, /**< planar 2-dimensional geometric object defined + * by 1 exterior boundary and 0 or more interior + * boundaries, standard WKB */ + wkbMultiPoint = 4, /**< GeometryCollection of Points, standard WKB */ + wkbMultiLineString = 5, /**< GeometryCollection of LineStrings, standard WKB */ + wkbMultiPolygon = 6, /**< GeometryCollection of Polygons, standard WKB */ + wkbGeometryCollection = 7, /**< geometric object that is a collection of 1 + or more geometric objects, standard WKB */ + + wkbCircularString = 8, /**< one or more circular arc segments connected end to end, + * ISO SQL/MM Part 3. GDAL >= 2.0 */ + wkbCompoundCurve = 9, /**< sequence of contiguous curves, ISO SQL/MM Part 3. GDAL >= 2.0 */ + wkbCurvePolygon = 10, /**< planar surface, defined by 1 exterior boundary + * and zero or more interior boundaries, that are curves. + * ISO SQL/MM Part 3. GDAL >= 2.0 */ + wkbMultiCurve = 11, /**< GeometryCollection of Curves, ISO SQL/MM Part 3. GDAL >= 2.0 */ + wkbMultiSurface = 12, /**< GeometryCollection of Surfaces, ISO SQL/MM Part 3. GDAL >= 2.0 */ + + wkbNone = 100, /**< non-standard, for pure attribute records */ + wkbLinearRing = 101, /**< non-standard, just for createGeometry() */ + + wkbCircularStringZ = 1008, /**< wkbCircularString with Z component. ISO SQL/MM Part 3. GDAL >= 2.0 */ + wkbCompoundCurveZ = 1009, /**< wkbCompoundCurve with Z component. ISO SQL/MM Part 3. GDAL >= 2.0 */ + wkbCurvePolygonZ = 1010, /**< wkbCurvePolygon with Z component. ISO SQL/MM Part 3. GDAL >= 2.0 */ + wkbMultiCurveZ = 1011, /**< wkbMultiCurve with Z component. ISO SQL/MM Part 3. GDAL >= 2.0 */ + wkbMultiSurfaceZ = 1012, /**< wkbMultiSurface with Z component. ISO SQL/MM Part 3. GDAL >= 2.0 */ + + wkbPoint25D = 0x80000001, /**< 2.5D extension as per 99-402 */ + wkbLineString25D = 0x80000002, /**< 2.5D extension as per 99-402 */ + wkbPolygon25D = 0x80000003, /**< 2.5D extension as per 99-402 */ + wkbMultiPoint25D = 0x80000004, /**< 2.5D extension as per 99-402 */ + wkbMultiLineString25D = 0x80000005, /**< 2.5D extension as per 99-402 */ + wkbMultiPolygon25D = 0x80000006, /**< 2.5D extension as per 99-402 */ + wkbGeometryCollection25D = 0x80000007 /**< 2.5D extension as per 99-402 */ + +} OGRwkbGeometryType; + +/* Outside of OGRwkbGeometryType since they are abstract types */ +#define wkbCurve ((OGRwkbGeometryType)13) /**< Curve (abstract type). SF-SQL 1.2 */ +#define wkbSurface ((OGRwkbGeometryType)14) /**< Surface (abstract type). SF-SQL 1.2 */ + +/** + * Output variants of WKB we support. + * + * 99-402 was a short-lived extension to SFSQL 1.1 that used a high-bit flag + * to indicate the presence of Z coordiantes in a WKB geometry. + * + * SQL/MM Part 3 and SFSQL 1.2 use offsets of 1000 (Z), 2000 (M) and 3000 (ZM) + * to indicate the present of higher dimensional coordinates in a WKB geometry. + * Reference: + * 09-009_Committee_Draft_ISOIEC_CD_13249-3_SQLMM_Spatial.pdf, + * ISO/IEC JTC 1/SC 32 N 1820, ISO/IEC CD 13249-3:201x(E), Date: 2009-01-16. + * The codes are also found in §8.2.3 of + * OGC 06-103r4 "OpenGIS® Implementation Standard for Geographic information - Simple feature access - Part 1: Common architecture", v1.2.1 + */ +typedef enum +{ + wkbVariantOldOgc, /**< Old-style 99-402 extended dimension (Z) WKB types */ + wkbVariantIso, /**< SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB types */ + wkbVariantPostGIS1 /**< PostGIS 1.X has different codes for CurvePolygon, MultiCurve and MultiSurface */ +} OGRwkbVariant; + + +/** @deprecated in GDAL 2.0. Use wkbHasZ() or wkbSetZ() instead */ +#ifndef GDAL_COMPILATION +#define wkb25DBit 0x80000000 +#endif + +/** Return the 2D geometry type corresponding to the specified geometry type */ +#define wkbFlatten(x) OGR_GT_Flatten((OGRwkbGeometryType)(x)) + +/** Return if the geometry type is a 3D geometry type + * @since GDAL 2.0 + */ +#define wkbHasZ(x) OGR_GT_HasZ(x) + +/** Return the 3D geometry type corresponding to the specified geometry type. + * @since GDAL 2.0 + */ +#define wkbSetZ(x) OGR_GT_SetZ(x) + +#define ogrZMarker 0x21125711 + +const char CPL_DLL * OGRGeometryTypeToName( OGRwkbGeometryType eType ); +OGRwkbGeometryType CPL_DLL OGRMergeGeometryTypes( OGRwkbGeometryType eMain, + OGRwkbGeometryType eExtra ); +OGRwkbGeometryType CPL_DLL OGRMergeGeometryTypesEx( OGRwkbGeometryType eMain, + OGRwkbGeometryType eExtra, + int bAllowPromotingToCurves ); +OGRwkbGeometryType CPL_DLL OGR_GT_Flatten( OGRwkbGeometryType eType ); +OGRwkbGeometryType CPL_DLL OGR_GT_SetZ( OGRwkbGeometryType eType ); +OGRwkbGeometryType CPL_DLL OGR_GT_SetModifier( OGRwkbGeometryType eType, int bSetZ, int bSetM ); +int CPL_DLL OGR_GT_HasZ( OGRwkbGeometryType eType ); +int CPL_DLL OGR_GT_IsSubClassOf( OGRwkbGeometryType eType, + OGRwkbGeometryType eSuperType ); +int CPL_DLL OGR_GT_IsCurve( OGRwkbGeometryType ); +int CPL_DLL OGR_GT_IsSurface( OGRwkbGeometryType ); +int CPL_DLL OGR_GT_IsNonLinear( OGRwkbGeometryType ); +OGRwkbGeometryType CPL_DLL OGR_GT_GetCollection( OGRwkbGeometryType eType ); +OGRwkbGeometryType CPL_DLL OGR_GT_GetCurve( OGRwkbGeometryType eType ); +OGRwkbGeometryType CPL_DLL OGR_GT_GetLinear( OGRwkbGeometryType eType ); + +typedef enum +{ + wkbXDR = 0, /* MSB/Sun/Motoroloa: Most Significant Byte First */ + wkbNDR = 1 /* LSB/Intel/Vax: Least Significant Byte First */ +} OGRwkbByteOrder; + +#ifndef NO_HACK_FOR_IBM_DB2_V72 +# define HACK_FOR_IBM_DB2_V72 +#endif + +#ifdef HACK_FOR_IBM_DB2_V72 +# define DB2_V72_FIX_BYTE_ORDER(x) ((((x) & 0x31) == (x)) ? (OGRwkbByteOrder) ((x) & 0x1) : (x)) +# define DB2_V72_UNFIX_BYTE_ORDER(x) ((unsigned char) (OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER ? ((x) | 0x30) : (x))) +#else +# define DB2_V72_FIX_BYTE_ORDER(x) (x) +# define DB2_V72_UNFIX_BYTE_ORDER(x) (x) +#endif + +/** Alter field name. + * Used by OGR_L_AlterFieldDefn(). + */ +#define ALTER_NAME_FLAG 0x1 + +/** Alter field type. + * Used by OGR_L_AlterFieldDefn(). + */ +#define ALTER_TYPE_FLAG 0x2 + +/** Alter field width and precision. + * Used by OGR_L_AlterFieldDefn(). + */ +#define ALTER_WIDTH_PRECISION_FLAG 0x4 + +/** Alter field NOT NULL constraint. + * Used by OGR_L_AlterFieldDefn(). + * @since GDAL 2.0 + */ +#define ALTER_NULLABLE_FLAG 0x8 + +/** Alter field DEFAULT value. + * Used by OGR_L_AlterFieldDefn(). + * @since GDAL 2.0 + */ +#define ALTER_DEFAULT_FLAG 0x10 + +/** Alter all parameters of field definition. + * Used by OGR_L_AlterFieldDefn(). + */ +#define ALTER_ALL_FLAG (ALTER_NAME_FLAG | ALTER_TYPE_FLAG | ALTER_WIDTH_PRECISION_FLAG | ALTER_NULLABLE_FLAG | ALTER_DEFAULT_FLAG) + + +/** Validate that fields respect not-null constraints. + * Used by OGR_F_Validate(). + * @since GDAL 2.0 + */ +#define OGR_F_VAL_NULL 0x00000001 + +/** Validate that geometries respect geometry column type. + * Used by OGR_F_Validate(). + * @since GDAL 2.0 + */ +#define OGR_F_VAL_GEOM_TYPE 0x00000002 + +/** Validate that (string) fields respect field width. + * Used by OGR_F_Validate(). + * @since GDAL 2.0 + */ +#define OGR_F_VAL_WIDTH 0x00000004 + +/** Allow fields that are null when there's an associated default value. + * This can be used for drivers where the low-level layers will automatically set the + * field value to the associated default value. + * This flag only makes sense if OGR_F_VAL_NULL is set too. + * Used by OGR_F_Validate(). + * @since GDAL 2.0 + */ +#define OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT 0x00000008 + +/** Enable all validation tests. + * Used by OGR_F_Validate(). + * @since GDAL 2.0 + */ +#define OGR_F_VAL_ALL 0xFFFFFFFF + +/************************************************************************/ +/* ogr_feature.h related definitions. */ +/************************************************************************/ + +/** + * List of feature field types. This list is likely to be extended in the + * future ... avoid coding applications based on the assumption that all + * field types can be known. + */ + +typedef enum +{ + /** Simple 32bit integer */ OFTInteger = 0, + /** List of 32bit integers */ OFTIntegerList = 1, + /** Double Precision floating point */ OFTReal = 2, + /** List of doubles */ OFTRealList = 3, + /** String of ASCII chars */ OFTString = 4, + /** Array of strings */ OFTStringList = 5, + /** deprecated */ OFTWideString = 6, + /** deprecated */ OFTWideStringList = 7, + /** Raw Binary data */ OFTBinary = 8, + /** Date */ OFTDate = 9, + /** Time */ OFTTime = 10, + /** Date and Time */ OFTDateTime = 11, + /** Single 64bit integer */ OFTInteger64 = 12, + /** List of 64bit integers */ OFTInteger64List = 13, + OFTMaxType = 13 +} OGRFieldType; + +/** + * List of field subtypes. A subtype represents a hint, a restriction of the + * main type, that is not strictly necessary to consult. + * This list is likely to be extended in the + * future ... avoid coding applications based on the assumption that all + * field types can be known. + * Most subtypes only make sense for a restricted set of main types. + * @since GDAL 2.0 + */ +typedef enum +{ + /** No subtype. This is the default value */ OFSTNone = 0, + /** Boolean integer. Only valid for OFTInteger and OFTIntegerList.*/ + OFSTBoolean = 1, + /** Signed 16-bit integer. Only valid for OFTInteger and OFTIntegerList. */ + OFSTInt16 = 2, + /** Single precision (32 bit) floatint point. Only valid for OFTReal and OFTRealList. */ + OFSTFloat32 = 3, + OFSTMaxSubType = 3 +} OGRFieldSubType; + +/** + * Display justification for field values. + */ + +typedef enum +{ + OJUndefined = 0, + OJLeft = 1, + OJRight = 2 +} OGRJustification; + +#define OGRNullFID -1 +#define OGRUnsetMarker -21121 + +/************************************************************************/ +/* OGRField */ +/************************************************************************/ + +/** + * OGRFeature field attribute value union. + */ + +typedef union { + int Integer; + GIntBig Integer64; + double Real; + char *String; + + struct { + int nCount; + int *paList; + } IntegerList; + + struct { + int nCount; + GIntBig *paList; + } Integer64List; + + struct { + int nCount; + double *paList; + } RealList; + + struct { + int nCount; + char **paList; + } StringList; + + struct { + int nCount; + GByte *paData; + } Binary; + + struct { + int nMarker1; + int nMarker2; + } Set; + + struct { + GInt16 Year; + GByte Month; + GByte Day; + GByte Hour; + GByte Minute; + GByte TZFlag; /* 0=unknown, 1=localtime(ambiguous), + 100=GMT, 104=GMT+1, 80=GMT-5, etc */ + GByte Reserved; /* must be set to 0 */ + float Second; /* with millisecond accuracy. at the end of the structure, so as to keep it 12 bytes on 32 bit */ + } Date; +} OGRField; + +#define OGR_GET_MS(floatingpoint_sec) (int)(((floatingpoint_sec) - (int)(floatingpoint_sec)) * 1000 + 0.5) + +int CPL_DLL OGRParseDate( const char *pszInput, OGRField *psOutput, + int nOptions ); + +/* -------------------------------------------------------------------- */ +/* Constants from ogrsf_frmts.h for capabilities. */ +/* -------------------------------------------------------------------- */ +#define OLCRandomRead "RandomRead" +#define OLCSequentialWrite "SequentialWrite" +#define OLCRandomWrite "RandomWrite" +#define OLCFastSpatialFilter "FastSpatialFilter" +#define OLCFastFeatureCount "FastFeatureCount" +#define OLCFastGetExtent "FastGetExtent" +#define OLCCreateField "CreateField" +#define OLCDeleteField "DeleteField" +#define OLCReorderFields "ReorderFields" +#define OLCAlterFieldDefn "AlterFieldDefn" +#define OLCTransactions "Transactions" +#define OLCDeleteFeature "DeleteFeature" +#define OLCFastSetNextByIndex "FastSetNextByIndex" +#define OLCStringsAsUTF8 "StringsAsUTF8" +#define OLCIgnoreFields "IgnoreFields" +#define OLCCreateGeomField "CreateGeomField" +#define OLCCurveGeometries "CurveGeometries" + +#define ODsCCreateLayer "CreateLayer" +#define ODsCDeleteLayer "DeleteLayer" +#define ODsCCreateGeomFieldAfterCreateLayer "CreateGeomFieldAfterCreateLayer" +#define ODsCCurveGeometries "CurveGeometries" +#define ODsCTransactions "Transactions" +#define ODsCEmulatedTransactions "EmulatedTransactions" + +#define ODrCCreateDataSource "CreateDataSource" +#define ODrCDeleteDataSource "DeleteDataSource" + +/* -------------------------------------------------------------------- */ +/* Layer metadata items. */ +/* -------------------------------------------------------------------- */ +/** Capability set to YES as metadata on a layer that has features with + * 64 bit identifiers. + @since GDAL 2.0 + */ +#define OLMD_FID64 "OLMD_FID64" + +/************************************************************************/ +/* ogr_featurestyle.h related definitions. */ +/************************************************************************/ + +/** + * OGRStyleTool derived class types (returned by GetType()). + */ + +typedef enum ogr_style_tool_class_id +{ + OGRSTCNone = 0, + OGRSTCPen = 1, + OGRSTCBrush = 2, + OGRSTCSymbol = 3, + OGRSTCLabel = 4, + OGRSTCVector = 5 +} OGRSTClassId; + +/** + * List of units supported by OGRStyleTools. + */ +typedef enum ogr_style_tool_units_id +{ + OGRSTUGround = 0, + OGRSTUPixel = 1, + OGRSTUPoints = 2, + OGRSTUMM = 3, + OGRSTUCM = 4, + OGRSTUInches = 5 +} OGRSTUnitId; + +/** + * List of parameters for use with OGRStylePen. + */ +typedef enum ogr_style_tool_param_pen_id +{ + OGRSTPenColor = 0, + OGRSTPenWidth = 1, + OGRSTPenPattern = 2, + OGRSTPenId = 3, + OGRSTPenPerOffset = 4, + OGRSTPenCap = 5, + OGRSTPenJoin = 6, + OGRSTPenPriority = 7, + OGRSTPenLast = 8 + +} OGRSTPenParam; + +/** + * List of parameters for use with OGRStyleBrush. + */ +typedef enum ogr_style_tool_param_brush_id +{ + OGRSTBrushFColor = 0, + OGRSTBrushBColor = 1, + OGRSTBrushId = 2, + OGRSTBrushAngle = 3, + OGRSTBrushSize = 4, + OGRSTBrushDx = 5, + OGRSTBrushDy = 6, + OGRSTBrushPriority = 7, + OGRSTBrushLast = 8 + +} OGRSTBrushParam; + + +/** + * List of parameters for use with OGRStyleSymbol. + */ +typedef enum ogr_style_tool_param_symbol_id +{ + OGRSTSymbolId = 0, + OGRSTSymbolAngle = 1, + OGRSTSymbolColor = 2, + OGRSTSymbolSize = 3, + OGRSTSymbolDx = 4, + OGRSTSymbolDy = 5, + OGRSTSymbolStep = 6, + OGRSTSymbolPerp = 7, + OGRSTSymbolOffset = 8, + OGRSTSymbolPriority = 9, + OGRSTSymbolFontName = 10, + OGRSTSymbolOColor = 11, + OGRSTSymbolLast = 12 + +} OGRSTSymbolParam; + +/** + * List of parameters for use with OGRStyleLabel. + */ +typedef enum ogr_style_tool_param_label_id +{ + OGRSTLabelFontName = 0, + OGRSTLabelSize = 1, + OGRSTLabelTextString = 2, + OGRSTLabelAngle = 3, + OGRSTLabelFColor = 4, + OGRSTLabelBColor = 5, + OGRSTLabelPlacement = 6, + OGRSTLabelAnchor = 7, + OGRSTLabelDx = 8, + OGRSTLabelDy = 9, + OGRSTLabelPerp = 10, + OGRSTLabelBold = 11, + OGRSTLabelItalic = 12, + OGRSTLabelUnderline = 13, + OGRSTLabelPriority = 14, + OGRSTLabelStrikeout = 15, + OGRSTLabelStretch = 16, + OGRSTLabelAdjHor = 17, + OGRSTLabelAdjVert = 18, + OGRSTLabelHColor = 19, + OGRSTLabelOColor = 20, + OGRSTLabelLast = 21 + +} OGRSTLabelParam; + +/* ------------------------------------------------------------------- */ +/* Version checking */ +/* -------------------------------------------------------------------- */ + +/* Note to developers : please keep this section in sync with gdal.h */ + +#ifndef GDAL_VERSION_INFO_DEFINED +#define GDAL_VERSION_INFO_DEFINED +const char CPL_DLL * CPL_STDCALL GDALVersionInfo( const char * ); +#endif + +#ifndef GDAL_CHECK_VERSION + +/** Return TRUE if GDAL library version at runtime matches nVersionMajor.nVersionMinor. + + The purpose of this method is to ensure that calling code will run with the GDAL + version it is compiled for. It is primarly intented for external plugins. + + @param nVersionMajor Major version to be tested against + @param nVersionMinor Minor version to be tested against + @param pszCallingComponentName If not NULL, in case of version mismatch, the method + will issue a failure mentionning the name of + the calling component. + */ +int CPL_DLL CPL_STDCALL GDALCheckVersion( int nVersionMajor, int nVersionMinor, + const char* pszCallingComponentName); + +/** Helper macro for GDALCheckVersion */ +#define GDAL_CHECK_VERSION(pszCallingComponentName) \ + GDALCheckVersion(GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR, pszCallingComponentName) + +#endif + +CPL_C_END + +#endif /* ndef OGR_CORE_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogr_drivertut.dox b/bazaar/plugin/gdal/ogr/ogr_drivertut.dox new file mode 100644 index 000000000..cbd1e3570 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_drivertut.dox @@ -0,0 +1,420 @@ +#ifndef DOXYGEN_SKIP +/* $Id: gdal_drivertut.dox,v 1.4 2006/10/18 13:26:45 mloskot Exp $ */ +#endif /* DOXYGEN_SKIP */ + +/*! +\page ogr_drivertut OGR Driver Implementation Tutorial + +\section odt_overall Overall Approach + +In general new formats are added to OGR by implementing format specific +drivers with intanciating a GDALDriver and subclasses of GDALDatasetand OGRLayer. The +GDALDriver instance is registered with the GDALDriverManager at runtime. + +Before following this tutorial to implement an OGR driver, please review +the OGR Architecture document carefully. + +The tutorial will be based on implementing a simple ascii point format. + +\section odt_toc Contents + +

      +
    1. \ref odt_driver_ro +
    2. \ref odt_datasource_bro +
    3. \ref odt_layer_bro +
    + +\section odt_driver_ro Implementing GDALDriver + +The format specific driver class is implemented as a instance of GDALDriver. +One instance of the driver will normally be created, and registered with +the GDALDriverManager. The instantiation of the driver is normally +handled by a global C callable registration function, similar to the +following placed in the same file as the driver class. + +\code +void RegisterOGRSPF() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "SPF" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SPF" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Long name for SPF driver" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "spf" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_spf.html" ); + + poDriver->pfnOpen = OGRSPFDriverOpen; + poDriver->pfnIdentify = OGRSPFDriverIdentify; + poDriver->pfnCreate = OGRSPFDriverCreate; + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} +\endcode + +The SetDescription() sets the name of the driver. This name is specified +on the commandline when creating datasources so it is generally good to keep +it short and without any special characters or spaces. + +SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ) is specified to indicate that the driver +will handle vector data. + +SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ) is specified to indicate that the +driver can deal with files opened with the VSI*L GDAL API. +Otherwise this metadata item should not be defined. + +The driver declaration generally looks something like this for a +format with read or read and update access (the Open() method) and creation +support (the Create() method). + +\code + +static GDALDataset* OGRSPFDriverOpen(GDALOpenInfo* poOpenInfo); +static int OGRSPFDriverIdentify(GDALOpenInfo* poOpenInfo); +static GDALDataset* OGRSPFDriverCreate(const char* pszName, int nXSize, int nYSize, + int nBands, GDALDataType eDT, char** papszOptions); +\endcode + + +The Open() method is called by GDALOpenEx(). It should quietly return NULL if +the passed +filename is not of the format supported by the driver. If it is the target +format, then a new GDALDataset object for the dataset should be returned. + +It is common for the Open() method to be delegated to an Open() method on +the actual format's GDALDataset class. + +\code +static GDALDataset *OGRSPFDriverOpen( GDALOpenInfo* poOpenInfo ) +{ + if( !OGRSPFDriverIdentify(poOpenInfo) ) + return NULL; + + OGRSPFDataSource *poDS = new OGRSPFDataSource(); + if( !poDS->Open( poOpenInfo->pszFilename, poOpenInfo->eAcces == GA_Update ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} +\endcode + +The Identify() method is implemented as such : + +\code +static int OGRSPFDriverIdentify( GDALOpenInfo* poOpenInfo ) +{ +// -------------------------------------------------------------------- +// Does this appear to be an .spf file? +// -------------------------------------------------------------------- + return EQUAL( CPLGetExtension(poOpenInfo->pszFilename), "spf" ); +} +\endcode + + +Examples of the Create() method is left for the section on creation and update. + +\section odt_datasource_bro Basic Read Only Data Source + +We will start implementing a minimal read-only datasource. No attempt is +made to optimize operations, and default implementations of many methods +inherited from GDALDataset are used. + +The primary responsibility of the datasource is to manage the list of layers. +In the case of the SPF format a datasource is a single file representing one +layer so there is at most one layer. The "name" of a datasource should +generally be the name passed to the Open() method. + +The Open() method below is not overriding a base class method, but we have +it to implement the open operation delegated by the driver class. + +For this simple case we provide a stub TestCapability() that returns FALSE +for all extended capabilities. The TestCapability() method is pure virtual, +so it does need to be implemented. + +\code +class OGRSPFDataSource : public GDALDataset +{ + OGRSPFLayer **papoLayers; + int nLayers; + + public: + OGRSPFDataSource(); + ~OGRSPFDataSource(); + + int Open( const char * pszFilename, int bUpdate ); + + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ) { return FALSE; } +}; +\endcode + +The constructor is a simple initializer to a default state. The Open() will +take care of actually attaching it to a file. The destructor is responsible +for orderly cleanup of layers. + +\code +OGRSPFDataSource::OGRSPFDataSource() + +{ + papoLayers = NULL; + nLayers = 0; +} + +OGRSPFDataSource::~OGRSPFDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); +} +\endcode + +The Open() method is the most important one on the datasource, though +in this particular instance it passes most of it's work off to the +OGRSPFLayer constructor if it believes the file is of the desired format. + +Note that Open() methods should try and determine that a file isn't of the +identified format as efficiently as possible, since many drivers may be +invoked with files of the wrong format before the correct driver is +reached. In this particular Open() we just test the file extension but this +is generally a poor way of identifying a file format. If available, checking +"magic header values" or something similar is preferrable. + +In the case of the SPF format, update in place is not supported, +so we always fail if bUpdate is FALSE. + +\code +int OGRSPFDataSource::Open( const char *pszFilename, int bUpdate ) + +{ + if( bUpdate ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Update access not supported by the SPF driver." ); + return FALSE; + } + +// -------------------------------------------------------------------- +// Create a corresponding layer. +// -------------------------------------------------------------------- + nLayers = 1; + papoLayers = (OGRSPFLayer **) CPLMalloc(sizeof(void*)); + + papoLayers[0] = new OGRSPFLayer( pszFilename ); + + pszName = CPLStrdup( pszFilename ); + + return TRUE; +} +\endcode + +A GetLayer() method also needs to be implemented. Since the layer list +is created in the Open() this is just a lookup with some safety testing. + +\code +OGRLayer *OGRSPFDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} +\endcode + +\section odt_layer_bro Read Only Layer + +The OGRSPFLayer is implements layer semantics for an .spf file. It provides +access to a set of feature objects in a consistent coordinate system +with a particular set of attribute columns. Our class definition looks like +this: + +\code +class OGRSPFLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + + FILE *fp; + + int nNextFID; + + public: + OGRSPFLayer( const char *pszFilename ); + ~OGRSPFLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ) { return FALSE; } +}; +\endcode + +The layer constructor is responsible for initialization. The most important +initialization is setting up the OGRFeatureDefn for the layer. This defines +the list of fields and their types, the geometry type and the coordinate +system for the layer. In the SPF format the set of fields is fixed - a +single string field and we have no coordinate system info to set. + +Pay particular attention to the reference counting of the OGRFeatureDefn. +As OGRFeature's for this layer will also take a reference to this definition +it is important that we also establish a reference on behalf of the layer +itself. + +\code +OGRSPFLayer::OGRSPFLayer( const char *pszFilename ) + +{ + nNextFID = 0; + + poFeatureDefn = new OGRFeatureDefn( CPLGetBasename( pszFilename ) ); + SetDescription(poFeatureDefn->GetName()); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldTemplate( "Name", OFTString ); + + poFeatureDefn->AddFieldDefn( &oFieldTemplate ); + + fp = VSIFOpenL( pszFilename, "r" ); + if( fp == NULL ) + return; +} +\endcode + +Note that the destructor uses Release() on the OGRFeatureDefn. This will +destroy the feature definition if the reference count drops to zero, but if +the application is still holding onto a feature from this layer, then that +feature will hold a reference to the feature definition and it will not +be destroyed here (which is good!). + +\code +OGRSPFLayer::~OGRSPFLayer() + +{ + poFeatureDefn->Release(); + if( fp != NULL ) + VSIFCloseL( fp ); +} +\endcode + +The GetNextFeature() method is usually the work horse of OGRLayer +implementations. It is responsible for reading the next feature according +to the current spatial and attribute filters installed. + +The while() loop is present to loop until we find a satisfactory +feature. The first section of code is for parsing a single line of +the SPF text file and establishing the x, y and name for the line. + +\code +OGRFeature *OGRSPFLayer::GetNextFeature() + +{ + // -------------------------------------------------------------------- + // Loop till we find a feature matching our requirements. + // -------------------------------------------------------------------- + while( TRUE ) + { + const char *pszLine; + const char *pszName; + + pszLine = CPLReadLineL( fp ); + + // Are we at end of file (out of features)? + if( pszLine == NULL ) + return NULL; + + double dfX; + double dfY; + + dfX = atof(pszLine); + + pszLine = strstr(pszLine,"|"); + if( pszLine == NULL ) + continue; // we should issue an error! + else + pszLine++; + + dfY = atof(pszLine); + + pszLine = strstr(pszLine,"|"); + if( pszLine == NULL ) + continue; // we should issue an error! + else + pszName = pszLine+1; + +\endcode + +The next section turns the x, y and name into a feature. Also note that +we assign a linearly incremented feature id. In our case we started at +zero for the first feature, though some drivers start at 1. + +\code + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY ) ); + poFeature->SetField( 0, pszName ); + poFeature->SetFID( nNextFID++ ); +\endcode + +Next we check if the feature matches our current attribute or +spatial filter if we have them. Methods on the OGRLayer base class +support maintain filters in the OGRLayer member fields m_poFilterGeom +(spatial filter) and m_poAttrQuery (attribute filter) so we can just use +these values here if they are non-NULL. The following test is essentially +"stock" and done the same in all formats. Some formats also do some +spatial filtering ahead of time using a spatial index. + +If the feature meets our criteria we return it. Otherwise we destroy it, +and return to the top of the loop to fetch another to try. + +\code + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} +\endcode + +While in the middle of reading a feature set from a layer, or at any other +time the application can call ResetReading() which is intended to restart +reading at the beginning of the feature set. We implement this by seeking +back to the beginning of the file, and resetting our feature id counter. + +\code +void OGRSPFLayer::ResetReading() + +{ + VSIFSeekL( fp, 0, SEEK_SET ); + nNextFID = 0; +} +\endcode + +In this implementation we do not provide a custom implementation for the +GetFeature() method. This means an attempt to read a particular feature +by it's feature id will result in many calls to GetNextFeature() till the +desired feature is found. However, in a sequential text format like spf +there is little else we could do anyway. + +There! We have completed a simple read-only feature file format driver. + +*/ diff --git a/bazaar/plugin/gdal/ogr/ogr_expat.cpp b/bazaar/plugin/gdal/ogr/ogr_expat.cpp new file mode 100644 index 000000000..6e90d09f5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_expat.cpp @@ -0,0 +1,192 @@ +/****************************************************************************** + * $Id: ogr_expat.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OGR + * Purpose: Convenience function for parsing with Expat library + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifdef HAVE_EXPAT + +#include "ogr_expat.h" +#include "cpl_error.h" + +CPL_CVSID("$Id: ogr_expat.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define OGR_EXPAT_MAX_ALLOWED_ALLOC 10000000 + +/************************************************************************/ +/* OGRExpatMalloc() */ +/************************************************************************/ + +static void* OGRExpatMalloc(size_t size) +{ + if (size < OGR_EXPAT_MAX_ALLOWED_ALLOC) + return malloc(size); + else + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Expat tried to malloc %d bytes. File probably corrupted", (int)size); + return NULL; + } +} + +/************************************************************************/ +/* OGRExpatRealloc() */ +/************************************************************************/ + +static void* OGRExpatRealloc(void *ptr, size_t size) +{ + if (size < OGR_EXPAT_MAX_ALLOWED_ALLOC) + return realloc(ptr, size); + else + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Expat tried to realloc %d bytes. File probably corrupted", (int)size); + free(ptr); + return NULL; + } +} + +/************************************************************************/ +/* FillWINDOWS1252() */ +/************************************************************************/ + +static void FillWINDOWS1252(XML_Encoding *info) +{ + /* Map CP1252 bytes to Unicode values */ + int i; + for(i=0;i<0x80;i++) + info->map[i] = i; + + info->map[0x80] = 0x20AC; + info->map[0x81] = -1; + info->map[0x82] = 0x201A; + info->map[0x83] = 0x0192; + info->map[0x84] = 0x201E; + info->map[0x85] = 0x2026; + info->map[0x86] = 0x2020; + info->map[0x87] = 0x2021; + info->map[0x88] = 0x02C6; + info->map[0x89] = 0x2030; + info->map[0x8A] = 0x0160; + info->map[0x8B] = 0x2039; + info->map[0x8C] = 0x0152; + info->map[0x8D] = -1; + info->map[0x8E] = 0x017D; + info->map[0x8F] = -1; + info->map[0x90] = -1; + info->map[0x91] = 0x2018; + info->map[0x92] = 0x2019; + info->map[0x93] = 0x201C; + info->map[0x94] = 0x201D; + info->map[0x95] = 0x2022; + info->map[0x96] = 0x2013; + info->map[0x97] = 0x2014; + info->map[0x98] = 0x02DC; + info->map[0x99] = 0x2122; + info->map[0x9A] = 0x0161; + info->map[0x9B] = 0x203A; + info->map[0x9C] = 0x0153; + info->map[0x9D] = -1; + info->map[0x9E] = 0x017E; + info->map[0x9F] = 0x0178; + + for(i=0xA0;i<=0xFF;i++) + info->map[i] = i; +} + +/************************************************************************/ +/* FillISO885915() */ +/************************************************************************/ + +static void FillISO885915(XML_Encoding *info) +{ + /* Map ISO-8859-15 bytes to Unicode values */ + /* Generated by generate_encoding_table.c */ + int i; + for(i = 0x00; i < 0xA4; i++) + info->map[i] = i; + info->map[0xA4] = 0x20AC; + info->map[0xA5] = 0xA5; + info->map[0xA6] = 0x0160; + info->map[0xA7] = 0xA7; + info->map[0xA8] = 0x0161; + for(i = 0xA9; i < 0xB4; i++) + info->map[i] = i; + info->map[0xB4] = 0x017D; + for(i = 0xB5; i < 0xB8; i++) + info->map[i] = i; + info->map[0xB8] = 0x017E; + for(i = 0xB9; i < 0xBC; i++) + info->map[i] = i; + info->map[0xBC] = 0x0152; + info->map[0xBD] = 0x0153; + info->map[0xBE] = 0x0178; + for(i = 0xBF; i < 0x100; i++) + info->map[i] = i; +} + +/************************************************************************/ +/* OGRExpatUnknownEncodingHandler() */ +/************************************************************************/ + +static int OGRExpatUnknownEncodingHandler (CPL_UNUSED void *unused_encodingHandlerData, + const XML_Char *name, + XML_Encoding *info) +{ + if( EQUAL(name, "WINDOWS-1252") ) + FillWINDOWS1252(info); + else if( EQUAL(name, "ISO-8859-15") ) + FillISO885915(info); + else + return XML_STATUS_ERROR; + + info->data = NULL; + info->convert = NULL; + info->release = NULL; + + return XML_STATUS_OK; +} + +/************************************************************************/ +/* OGRCreateExpatXMLParser() */ +/************************************************************************/ + +XML_Parser OGRCreateExpatXMLParser() +{ + XML_Memory_Handling_Suite memsuite; + memsuite.malloc_fcn = OGRExpatMalloc; + memsuite.realloc_fcn = OGRExpatRealloc; + memsuite.free_fcn = free; + XML_Parser hParser = XML_ParserCreate_MM(NULL, &memsuite, NULL); + + XML_SetUnknownEncodingHandler(hParser, + OGRExpatUnknownEncodingHandler, + NULL); + + return hParser; +} + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogr_expat.h b/bazaar/plugin/gdal/ogr/ogr_expat.h new file mode 100644 index 000000000..c08ced04e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_expat.h @@ -0,0 +1,58 @@ +/****************************************************************************** + * $Id: ogr_expat.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OGR + * Purpose: Convenience function for parsing with Expat library + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef OGR_EXPATH_INCLUDED +#define OGR_EXPATH_INCLUDED + +#ifdef HAVE_EXPAT + +#include "cpl_port.h" +#include + +/* Compatibility stuff for expat >= 1.95.0 and < 1.95.7 */ +#ifndef XMLCALL +#define XMLCALL +#endif +#ifndef XML_STATUS_OK +#define XML_STATUS_OK 1 +#define XML_STATUS_ERROR 0 +#endif + +/* XML_StopParser only available for expat >= 1.95.8 */ +#if !defined(XML_MAJOR_VERSION) || (XML_MAJOR_VERSION * 10000 + XML_MINOR_VERSION * 100 + XML_MICRO_VERSION) < 19508 +#define XML_StopParser(parser, resumable) +#warning "Expat version is too old and does not have XML_StopParser. Corrupted files could hang OGR" +#endif + +/* Only for internal use ! */ +XML_Parser CPL_DLL OGRCreateExpatXMLParser(void); + +#endif /* HAVE_EXPAT */ + +#endif /* OGR_EXPATH_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogr_feature.h b/bazaar/plugin/gdal/ogr/ogr_feature.h new file mode 100644 index 000000000..f413ad952 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_feature.h @@ -0,0 +1,469 @@ +/****************************************************************************** + * $Id: ogr_feature.h 28968 2015-04-21 19:00:02Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Class for representing a whole feature, and layer schemas. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_FEATURE_H_INCLUDED +#define _OGR_FEATURE_H_INCLUDED + +#include "ogr_geometry.h" +#include "ogr_featurestyle.h" +#include "cpl_atomic_ops.h" + +/** + * \file ogr_feature.h + * + * Simple feature classes. + */ + +/************************************************************************/ +/* OGRFieldDefn */ +/************************************************************************/ + +/** + * Definition of an attribute of an OGRFeatureDefn. A field is described by : + *
      + *
    • a name. See SetName() / GetNameRef()
    • + *
    • a type: OFTString, OFTInteger, OFTReal, ... See SetType() / GetType()
    • + *
    • a subtype (optional): OFSTBoolean, ... See SetSubType() / GetSubType()
    • + *
    • a width (optional): maximal number of characters. See SetWidth() / GetWidth()
    • + *
    • a precision (optional): number of digits after decimal point. See SetPrecision() / GetPrecision()
    • + *
    • a NOT NULL constraint (optional). See SetNullable() / IsNullable()
    • + *
    • a default value (optional). See SetDefault() / GetDefault()
    • + *
    • a boolean to indicate whether it should be ignored when retrieving features. See SetIgnored() / IsIgnored()
    • + *
    + */ + +class CPL_DLL OGRFieldDefn +{ + private: + char *pszName; + OGRFieldType eType; + OGRJustification eJustify; + int nWidth; /* zero is variable */ + int nPrecision; + char *pszDefault; + + int bIgnore; + OGRFieldSubType eSubType; + + int bNullable; + + void Initialize( const char *, OGRFieldType ); + + public: + OGRFieldDefn( const char *, OGRFieldType ); + OGRFieldDefn( OGRFieldDefn * ); + ~OGRFieldDefn(); + + void SetName( const char * ); + const char *GetNameRef() { return pszName; } + + OGRFieldType GetType() { return eType; } + void SetType( OGRFieldType eTypeIn ); + static const char *GetFieldTypeName( OGRFieldType ); + + OGRFieldSubType GetSubType() { return eSubType; } + void SetSubType( OGRFieldSubType eSubTypeIn ); + static const char *GetFieldSubTypeName( OGRFieldSubType ); + + OGRJustification GetJustify() { return eJustify; } + void SetJustify( OGRJustification eJustifyIn ) + { eJustify = eJustifyIn; } + + int GetWidth() { return nWidth; } + void SetWidth( int nWidthIn ) { nWidth = MAX(0,nWidthIn); } + + int GetPrecision() { return nPrecision; } + void SetPrecision( int nPrecisionIn ) + { nPrecision = nPrecisionIn; } + + void Set( const char *, OGRFieldType, int = 0, int = 0, + OGRJustification = OJUndefined ); + + void SetDefault( const char* ); + const char *GetDefault() const; + int IsDefaultDriverSpecific() const; + + int IsIgnored() { return bIgnore; } + void SetIgnored( int bIgnoreIn ) { bIgnore = bIgnoreIn; } + + int IsNullable() const { return bNullable; } + void SetNullable( int bNullableIn ) { bNullable = bNullableIn; } + + int IsSame( const OGRFieldDefn * ) const; +}; + +/************************************************************************/ +/* OGRGeomFieldDefn */ +/************************************************************************/ + +/** + * Definition of a geometry field of an OGRFeatureDefn. A geometry field is + * described by : + *
      + *
    • a name. See SetName() / GetNameRef()
    • + *
    • a type: wkbPoint, wkbLineString, ... See SetType() / GetType()
    • + *
    • a spatial reference system (optional). See SetSpatialRef() / GetSpatialRef()
    • + *
    • a NOT NULL constraint (optional). See SetNullable() / IsNullable()
    • + *
    • a boolean to indicate whether it should be ignored when retrieving features. See SetIgnored() / IsIgnored()
    • + *
    + * + * @since OGR 1.11 + */ + +class CPL_DLL OGRGeomFieldDefn +{ +protected: + char *pszName; + OGRwkbGeometryType eGeomType; /* all values possible except wkbNone */ + OGRSpatialReference* poSRS; + + int bIgnore; + int bNullable; + + void Initialize( const char *, OGRwkbGeometryType ); + +public: + OGRGeomFieldDefn(const char *pszNameIn, + OGRwkbGeometryType eGeomTypeIn); + OGRGeomFieldDefn( OGRGeomFieldDefn * ); + virtual ~OGRGeomFieldDefn(); + + void SetName( const char * ); + const char *GetNameRef() { return pszName; } + + OGRwkbGeometryType GetType() { return eGeomType; } + void SetType( OGRwkbGeometryType eTypeIn ); + + virtual OGRSpatialReference* GetSpatialRef(); + void SetSpatialRef(OGRSpatialReference* poSRSIn); + + int IsIgnored() { return bIgnore; } + void SetIgnored( int bIgnoreIn ) { bIgnore = bIgnoreIn; } + + int IsNullable() const { return bNullable; } + void SetNullable( int bNullableIn ) { bNullable = bNullableIn; } + + int IsSame( OGRGeomFieldDefn * ); +}; + +/************************************************************************/ +/* OGRFeatureDefn */ +/************************************************************************/ + +/** + * Definition of a feature class or feature layer. + * + * This object contains schema information for a set of OGRFeatures. In + * table based systems, an OGRFeatureDefn is essentially a layer. In more + * object oriented approaches (such as SF CORBA) this can represent a class + * of features but doesn't necessarily relate to all of a layer, or just one + * layer. + * + * This object also can contain some other information such as a name and + * potentially other metadata. + * + * It is essentially a collection of field descriptions (OGRFieldDefn class). + * Starting with GDAL 1.11, in addition to attribute fields, it can also + * contain multiple geometry fields (OGRGeomFieldDefn class). + * + * It is reasonable for different translators to derive classes from + * OGRFeatureDefn with additional translator specific information. + */ + +class CPL_DLL OGRFeatureDefn +{ + protected: + volatile int nRefCount; + + int nFieldCount; + OGRFieldDefn **papoFieldDefn; + + int nGeomFieldCount; + OGRGeomFieldDefn **papoGeomFieldDefn; + + char *pszFeatureClassName; + + int bIgnoreStyle; + + public: + OGRFeatureDefn( const char * pszName = NULL ); + virtual ~OGRFeatureDefn(); + + virtual const char *GetName(); + + virtual int GetFieldCount(); + virtual OGRFieldDefn *GetFieldDefn( int i ); + virtual int GetFieldIndex( const char * ); + + virtual void AddFieldDefn( OGRFieldDefn * ); + virtual OGRErr DeleteFieldDefn( int iField ); + virtual OGRErr ReorderFieldDefns( int* panMap ); + + virtual int GetGeomFieldCount(); + virtual OGRGeomFieldDefn *GetGeomFieldDefn( int i ); + virtual int GetGeomFieldIndex( const char * ); + + virtual void AddGeomFieldDefn( OGRGeomFieldDefn *, int bCopy = TRUE ); + virtual OGRErr DeleteGeomFieldDefn( int iGeomField ); + + virtual OGRwkbGeometryType GetGeomType(); + virtual void SetGeomType( OGRwkbGeometryType ); + + virtual OGRFeatureDefn *Clone(); + + int Reference() { return CPLAtomicInc(&nRefCount); } + int Dereference() { return CPLAtomicDec(&nRefCount); } + int GetReferenceCount() { return nRefCount; } + void Release(); + + virtual int IsGeometryIgnored(); + virtual void SetGeometryIgnored( int bIgnore ); + virtual int IsStyleIgnored() { return bIgnoreStyle; } + virtual void SetStyleIgnored( int bIgnore ) { bIgnoreStyle = bIgnore; } + + virtual int IsSame( OGRFeatureDefn * poOtherFeatureDefn ); + + static OGRFeatureDefn *CreateFeatureDefn( const char *pszName = NULL ); + static void DestroyFeatureDefn( OGRFeatureDefn * ); +}; + +/************************************************************************/ +/* OGRFeature */ +/************************************************************************/ + +/** + * A simple feature, including geometry and attributes. + */ + +class CPL_DLL OGRFeature +{ + private: + + GIntBig nFID; + OGRFeatureDefn *poDefn; + OGRGeometry **papoGeometries; + OGRField *pauFields; + + protected: + char * m_pszStyleString; + OGRStyleTable *m_poStyleTable; + char * m_pszTmpFieldValue; + + public: + OGRFeature( OGRFeatureDefn * ); + virtual ~OGRFeature(); + + OGRFeatureDefn *GetDefnRef() { return poDefn; } + + OGRErr SetGeometryDirectly( OGRGeometry * ); + OGRErr SetGeometry( OGRGeometry * ); + OGRGeometry *GetGeometryRef(); + OGRGeometry *StealGeometry(); + + int GetGeomFieldCount() + { return poDefn->GetGeomFieldCount(); } + OGRGeomFieldDefn *GetGeomFieldDefnRef( int iField ) + { return poDefn->GetGeomFieldDefn(iField); } + int GetGeomFieldIndex( const char * pszName) + { return poDefn->GetGeomFieldIndex(pszName); } + + OGRGeometry* GetGeomFieldRef(int iField); + OGRGeometry* StealGeometry(int iField); + OGRGeometry* GetGeomFieldRef(const char* pszFName); + OGRErr SetGeomFieldDirectly( int iField, OGRGeometry * ); + OGRErr SetGeomField( int iField, OGRGeometry * ); + + OGRFeature *Clone(); + virtual OGRBoolean Equal( OGRFeature * poFeature ); + + int GetFieldCount() { return poDefn->GetFieldCount(); } + OGRFieldDefn *GetFieldDefnRef( int iField ) + { return poDefn->GetFieldDefn(iField); } + int GetFieldIndex( const char * pszName) + { return poDefn->GetFieldIndex(pszName);} + + int IsFieldSet( int iField ); + + void UnsetField( int iField ); + + OGRField *GetRawFieldRef( int i ) { return pauFields + i; } + + int GetFieldAsInteger( int i ); + GIntBig GetFieldAsInteger64( int i ); + double GetFieldAsDouble( int i ); + const char *GetFieldAsString( int i ); + const int *GetFieldAsIntegerList( int i, int *pnCount ); + const GIntBig *GetFieldAsInteger64List( int i, int *pnCount ); + const double *GetFieldAsDoubleList( int i, int *pnCount ); + char **GetFieldAsStringList( int i ); + GByte *GetFieldAsBinary( int i, int *pnCount ); + int GetFieldAsDateTime( int i, + int *pnYear, int *pnMonth, int *pnDay, + int *pnHour, int *pnMinute, int *pnSecond, + int *pnTZFlag ); + int GetFieldAsDateTime( int i, + int *pnYear, int *pnMonth, int *pnDay, + int *pnHour, int *pnMinute, float *pfSecond, + int *pnTZFlag ); + + int GetFieldAsInteger( const char *pszFName ) + { return GetFieldAsInteger( GetFieldIndex(pszFName) ); } + GIntBig GetFieldAsInteger64( const char *pszFName ) + { return GetFieldAsInteger64( GetFieldIndex(pszFName) ); } + double GetFieldAsDouble( const char *pszFName ) + { return GetFieldAsDouble( GetFieldIndex(pszFName) ); } + const char *GetFieldAsString( const char *pszFName ) + { return GetFieldAsString( GetFieldIndex(pszFName) ); } + const int *GetFieldAsIntegerList( const char *pszFName, + int *pnCount ) + { return GetFieldAsIntegerList( GetFieldIndex(pszFName), + pnCount ); } + const GIntBig *GetFieldAsInteger64List( const char *pszFName, + int *pnCount ) + { return GetFieldAsInteger64List( GetFieldIndex(pszFName), + pnCount ); } + const double *GetFieldAsDoubleList( const char *pszFName, + int *pnCount ) + { return GetFieldAsDoubleList( GetFieldIndex(pszFName), + pnCount ); } + char **GetFieldAsStringList( const char *pszFName ) + { return GetFieldAsStringList(GetFieldIndex(pszFName)); } + + void SetField( int i, int nValue ); + void SetField( int i, GIntBig nValue ); + void SetField( int i, double dfValue ); + void SetField( int i, const char * pszValue ); + void SetField( int i, int nCount, int * panValues ); + void SetField( int i, int nCount, const GIntBig * panValues ); + void SetField( int i, int nCount, double * padfValues ); + void SetField( int i, char ** papszValues ); + void SetField( int i, OGRField * puValue ); + void SetField( int i, int nCount, GByte * pabyBinary ); + void SetField( int i, int nYear, int nMonth, int nDay, + int nHour=0, int nMinute=0, float fSecond=0.f, + int nTZFlag = 0 ); + + void SetField( const char *pszFName, int nValue ) + { SetField( GetFieldIndex(pszFName), nValue ); } + void SetField( const char *pszFName, GIntBig nValue ) + { SetField( GetFieldIndex(pszFName), nValue ); } + void SetField( const char *pszFName, double dfValue ) + { SetField( GetFieldIndex(pszFName), dfValue ); } + void SetField( const char *pszFName, const char * pszValue) + { SetField( GetFieldIndex(pszFName), pszValue ); } + void SetField( const char *pszFName, int nCount, + int * panValues ) + { SetField(GetFieldIndex(pszFName),nCount,panValues);} + void SetField( const char *pszFName, int nCount, + const GIntBig * panValues ) + { SetField(GetFieldIndex(pszFName),nCount,panValues);} + void SetField( const char *pszFName, int nCount, + double * padfValues ) + {SetField(GetFieldIndex(pszFName),nCount,padfValues);} + void SetField( const char *pszFName, char ** papszValues ) + { SetField( GetFieldIndex(pszFName), papszValues); } + void SetField( const char *pszFName, OGRField * puValue ) + { SetField( GetFieldIndex(pszFName), puValue ); } + void SetField( const char *pszFName, + int nYear, int nMonth, int nDay, + int nHour=0, int nMinute=0, float fSecond=0.f, + int nTZFlag = 0 ) + { SetField( GetFieldIndex(pszFName), + nYear, nMonth, nDay, + nHour, nMinute, fSecond, nTZFlag ); } + + GIntBig GetFID() { return nFID; } + virtual OGRErr SetFID( GIntBig nFIDIn ); + + void DumpReadable( FILE *, char** papszOptions = NULL ); + + OGRErr SetFrom( OGRFeature *, int = TRUE); + OGRErr SetFrom( OGRFeature *, int *, int = TRUE ); + OGRErr SetFieldsFrom( OGRFeature *, int *, int = TRUE ); + + OGRErr RemapFields( OGRFeatureDefn *poNewDefn, + int *panRemapSource ); + OGRErr RemapGeomFields( OGRFeatureDefn *poNewDefn, + int *panRemapSource ); + + int Validate( int nValidateFlags, + int bEmitError ); + void FillUnsetWithDefault(int bNotNullableOnly, + char** papszOptions ); + + virtual const char *GetStyleString(); + virtual void SetStyleString( const char * ); + virtual void SetStyleStringDirectly( char * ); + virtual OGRStyleTable *GetStyleTable() { return m_poStyleTable; } + virtual void SetStyleTable(OGRStyleTable *poStyleTable); + virtual void SetStyleTableDirectly(OGRStyleTable *poStyleTable); + + static OGRFeature *CreateFeature( OGRFeatureDefn * ); + static void DestroyFeature( OGRFeature * ); +}; + +/************************************************************************/ +/* OGRFeatureQuery */ +/************************************************************************/ + +class OGRLayer; +class swq_expr_node; +class swq_custom_func_registrar; + +class CPL_DLL OGRFeatureQuery +{ + private: + OGRFeatureDefn *poTargetDefn; + void *pSWQExpr; + + char **FieldCollector( void *, char ** ); + + GIntBig *EvaluateAgainstIndices( swq_expr_node*, OGRLayer *, GIntBig& nFIDCount); + + int CanUseIndex( swq_expr_node*, OGRLayer * ); + + public: + OGRFeatureQuery(); + ~OGRFeatureQuery(); + + OGRErr Compile( OGRFeatureDefn *, const char *, + int bCheck = TRUE, swq_custom_func_registrar* poCustomFuncRegistrar = NULL ); + int Evaluate( OGRFeature * ); + + GIntBig *EvaluateAgainstIndices( OGRLayer *, OGRErr * ); + + int CanUseIndex( OGRLayer * ); + + char **GetUsedFields(); + + void *GetSWQExpr() { return pSWQExpr; } +}; + +#endif /* ndef _OGR_FEATURE_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogr_feature_style.html b/bazaar/plugin/gdal/ogr/ogr_feature_style.html new file mode 100644 index 000000000..3f95cc3c5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_feature_style.html @@ -0,0 +1,1316 @@ + + + + OGR - Feature Style Specification + + + +
    +

    +OGR - Feature Style Specification

    + +
    +

    DRAFT V0.014 - 2011-07-24

    +
    + +
    +

    REVISION HISTORY

    +
      +
    • Version 0.014 - 2011-07-24 - Even Rouault
      + Mention the escaping of double-quote characters in the text string of a LABEL (ticket #3675) +
    • Version 0.013 - 2008-07-29 - Daniel Morissette
      + Added 'o:' for font point symbol outline color (ticket #2509) +
    • Version 0.012 - 2008-07-21 - Daniel Morissette
      + Added 'o:' for text outline color and updated 'b:' to be specifically + a filled label background box (ticket #2480) +
    • Version 0.011 - 2008-02-28 - Tamas Szekeres
      + Note about OGR SQL to transfer the style between the data sources +
    • Version 0.010 - 2006-09-23- Andrey Kiselev
      + Added label styles 'w', 'st', 'h', 'm:h', 'm:a', 'p:{10,11,12}' +
    • Version 0.009 - 2005-03-11- Frank Warmerdam
      + Remove reference to OGRWin, move into ogr distribution +
    • Version 0.008 - 2001-03-21- Frank Warmerdam
      + Fix minor typos (h:12pt instead of s:12pt in examples) +
    • Version 0.008 - 2000-07-15 - Stephane Villeneuve
      + Remove style table in Layer. Add forecolor and backcolor to brush. +
    • Version 0.007 - 2000-06-22 - Daniel Morissette
      + Fixed typo and added offset param for PEN. +
    • Version 0.006 - 2000-06-20 - Daniel Morissette
      + Added the OGR-Win idea and made small changes here and there. +
    • Version 0.005 - 2000-06-12 - Daniel Morissette
      + Allow passing of comma-delimited list of names in PEN's "id" parameter.
      + Defined system-independent pen style names. +
    • Version 0.004 - 2000-06-09 - Stephane Villeneuve
      + Added PEN cap and join parameters
      + More clearly defined the API +
    • Version 0.003 - 2000-02-15 - Daniel Morissette
      + First kind-of-complete version. +

      + +

    + +
    +

    +1. Overview

    +This document defines the way feature style information (i.e. colors, line +width, symbols, etc.) should be handled at the various levels in OGR. +

    +1.1 Style is a property of Feature object

    +Conceptually, the feature style should be seen as a property of a feature. +Even though some systems would store style information in a special attribute, +in OGR it is more consistent to see the style as a property just the same +way the geometry of a feature is also a property. +

    This does not prevent us from storing the style information in an attribute +when writing to some formats that have no provision for styles (e.g. E00). +But then at the time such a dataset is opened through OGR, the name of +the attribute that contains style information should either be specified +in some metadata, or be specified by the user. +

    Also, in the SFCOM interface, the style information will be stored in +an attribute just like the geometry is. Later in this document, we will +define a kind of "WKT" format to be used when storing style information +in text form. +

    +1.2 Feature Styles can be stored at 2 levels

    +The style defines the way a feature should be drawn, but it is very common +to have several features that share the same style. In those cases, instead +of duplicating the style information on each feature, we will provide a +more efficient way to share style information. +

    There are actually 2 levels at which style information can be found: +

      +
    • +At the dataset level:
    • + +
        +
      • +A dataset can have a default style that applies to all features.
      • + +
      • +It can also have a table of pre-defined styles that can then be referred +to by the layers or by the individual features.
      • + +
        The mechanism for that is defined further down in this document.
      + +
    • +At the feature level (in the OGRFeature):
    • + +
      • +By default, a feature inherits the style information from the dataset.
      • + +
      • +A feature can (and should in most cases) be linked to a style in the dataset's table of styles. This can save lots of storage space +when the same styles are reused often.
      • + +
      • +Finally, a feature can have its own complete style definition.
      • +
      +
    +It should be possible to have style information stored at one or more of +the various levels while working on a given dataset. The level(s) where +the style is actually stored will depend on the most efficient approach +for the format we are dealing with. +

    However, all that stuff should be transparent to the OGR client that +does not want to worry about the details and a single call to a method +such as OGRFeature::GetStyleString() should hide all the magic and always +return the right information. + +

    +1.3 Drawing Tools

    +We define a small set of drawing tools that are used to build style definitions: +
      +
    • +PEN: For linear styles
    • + +
    • +BRUSH: For filling areas
    • + +
    • +SYMBOL: Point symbols
    • + +
    • +LABEL: For annotations
    • +
    +Each drawing tool can take a number of parameters, all optional. The style +syntax is built in a way that a system that cannot support all possible +parameters can safely skip and ignore the parameters it does not support. +This will also make it easy to extend the specification in the future without +breaking existing code or applications. +

    A style can use a single tool, or use a combination of one or more tools. +By combining the use of several tools in a style, one can build virtually +any type of graphical representation. For instance, the SYMBOL tool can +be used to place spaced symbols along a line. Also, the LABEL tool can +be used to place text on a point, stretch it along a line, or even, by +combining the PEN tool with the LABEL tool, use the line as a leader to +the text label, and draw the text string on the last vertex of the line. +

    Of course only few systems can support all that. But the intention here +is to have a style specification that is powerful and flexible enough to +allow all types of formats to exchange style information with the least +possible loss. +

    +1.4 Feature attributes can be used by style definitions

    +In some cases, it might be useful for a style definition to refer to an +attribute field on the feature for a given tool parameter's value instead +of having a hardcoded value inside the style itself. +

    Example of this are text angle, text string, etc... these values change +for every single text label, but we can share the rest of the label style +at the layer level if we lookup the angle and text string in an attribute +on each feature. +

    The syntax of the style string provides a way that any parameter value +can be either a constant value, or a lookup to an attribute field. +

    +1.5 Tool parameter units

    +Several parameter values can be expressed in different measurement units +depending on the file format you are dealing with. For instance, some systems +express line width, or text height in points, other in pixels, and others +use ground units. In order to accommodate all that, all parameters can be +specified in one of the following units systems: +
      +
    • +g: Map Ground Units (whatever the map coordinate units are)
    • + +
    • +px: Pixels
    • + +
    • +pt: Points (1/72 Inch)
    • + +
    • +mm: Millimeters
    • + +
    • +cm: Centimeters
    • + +
    • +in: Inches
    • +
    +Some tools will have to be provided at the OGR client level to simplify +the conversion of any value from one units system to another. This would +imply that the OGR client has to specify a map scale so that conversions +from ground units to paper/pixel units can be performed. +

    +


    +

    +2. Feature Style String

    +As was mentioned earlier, styles definitions will usually be stored as +strings, either in a per layer (or per dataset) table, or directly in the +features. +

    +2.1 Examples

    +The best way to get familiar with something is by example. +

    Here we go with some style definition strings: +

      A 5 pixels wide red line:
    +     "PEN(c:#FF0000,w:5px)"
    +
    +  A polygon filled in blue, with a black outline:
    +     "BRUSH(fc:#0000FF);PEN(c:#000000)"
    +
    +  A point symbol:
    +     "SYMBOL(c:#00FF00,id:"points.sym-45,ogr-sym-7")"
    +
    +  A text label, taking the text string from the "text_string" 
    +  attribute field:
    +     "LABEL(f:"Times New Roman",s:12pt,t:{text_string})"
    +Here is what a style table that contains all the above styles could look +like: +
        road:      PEN(c:#FF0000,w:5px)
    +    lake:      BRUSH(fc:#0000FF);PEN(c:#000000)
    +    campsite:  SYMBOL(c:#00FF00,id:"points.sym-45,ogr-sym-7")
    +    label:     LABEL(f:"Times New Roman",s:12pt,t:{text_string})
    + +And then individual features could refer to styles from the table above +using the "@" character followed by the style name in their style property. +

    For instance, a feature with its style set to "@road" would be drawn +as a red line. +

    +2.2 Style String Syntax

    +Each feature object has a style property (a string): +
  • + + + +
    +
    +  <style_property> = "<style_def>" | "" | "@<style_name>" | "{<field_name>}"
    +
    + +
      +
    • +"<style_def>" is defined later in this section.
    • + +
    • +An empty style property means that the feature directly inherits its style +from the layer it is in.
    • + +
    • +"@<style_name>" is a reference to a predefined style in the layer or +the dataset's style table. The layer's table is looked up first, and if +style_name is not found there then the dataset's table will be looked up.
    • + +
    • +Finally, "{<field_name>}" means that the style property should be read +from the specified attribute field.
    • +
    +The <style_def> is the real style definition. It is a combination of +1 or more style parts separated by semicolons. Each style_part uses a drawing +tool to define a portion of the complete graphical representation: + + + + +
    +
    +  <style_def> =    <style_part>[;<style_part>[;...]]
    +
    +  <style_part> =   <tool_name>([<tool_param>[,<tool_param>[,...]]])
    +
    +  <tool_name> =    name of a drawing tool, for now: PEN | BRUSH | SYMBOL | LABEL 
    +
    +  <tool_param> =   <param_name>:<param_value>
    +
    +  <param_name> =   see list of parameters names for each drawing tool
    +
    +  <param_value> =  <value> | <value><units>
    +
    +  <value> =        "<string_value>" | <numeric_value> | {<field_name>}
    +
    +  <units> =        g | px | pt | mm | cm | in
    +
    + +

    By default, style parts are drawn in the order that they appear in the +style_def string unless each part is assigned a different level parameter +value (see the level parameter definition). +

    All drawing tool parameters are optional. So it is legal to have a style_part +with an empty drawing tool parameter list (e.g. "PEN()"). For each parameter +that does not have any specified value, it is up to the client application +to use its own default value. This document provides advisory default values +for most parameters, but it is not mandatory for an application to use +those default value. +

    When {<field_name>} is used for a tool_param value, several options +are available with respect to the units. The units can be specified after +the field name as in PEN(c:#FF0000,w:{line_width}pt) or can be left unspecified +as in PEN(c:#FF0000,w:{line_width}). In the first case, the default units +will be points (pt), but if the attribute field line_width contains a value +followed by a units abbreviation (e.g. "5px") then the units specified +in the attribute fields have precedence (in this case pixels). Note that +the attribute field does not have to contain a units value and probably +won't in most cases, it is just an optional feature to be able to override +the default units from inside an attribute field's value. +

    +2.3 Pen Tool Parameters

    +Applicable geometry types: +
      +
    • +Point: When applied to a point, a pen tool can only define the color and +the size of the point to draw.
    • + +
    • +Polyline: This is the most obvious case.
    • + +
    • +Polygon: Defines the way the outline of a polygon should be drawn.
    • +
    +Here is the current list of PEN tool parameters, while this is sufficient +to cover all the cases that we have encountered so far, new parameters +might be added in the future to handle new types of graphical representation. +Note again that all parameters are optional: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    param_nameDescription
    cPen Color, expressed in hexadecimal (#RRGGBB[AA]) +
    + [AA] the last 2 digits define the alpha channel value, with 0 being transparent and FF being opaque. The default is FF (opaque) +
    + Suggested default: black (c:#000000) +
    + Example: PEN(c:#FF0000), or PEN(C:#FF0000FF) +
    + Predefined color names may be allowed in future versions of the specification.
    wPen Width - Expressed in a valid unit type (g, px, pt, mm, cm, +in) +
    + Suggested default: 1 pixel +
    + Examples: PEN(c:#FF0000,w:5px), PEN(w:3pt), PEN(w:50g)
    pPattern - To create dash lines. A list of pen-down/pen-up distances +
    + Examples: +
    + = PEN(c:#FF0000,w:2px,p:"4px +5px") - short-dash line +
    + = PEN(c:#FF0000,w:2px,p:"10px +5px") - long-dash line +
    + = PEN(c:#FF0000,w:2px,p:"10px +5px 4px 5px") - long/short dash line
    idComma-delimited list of Pen Names or Ids - For systems that +identify pens with a name or an id. The names in the comma-delimited list +of ids are scanned until one is recognized by the target system. +

    +Pen Ids can be either system-specific ids (see further below) or be one +of the pre-defined OGR pen ids for well known line patterns. The id +parameter should always include one of the OGR ids at the end of the +comma-delimited list of ids so that an application never has to rely on +understanding system-specific ids. +

    +Here is the current list of OGR pen ids (this could grow over time):
    +

      +
    • ogr-pen-0: solid (the default when no id is provided) +
    • ogr-pen-1: null pen (invisible) +
    • ogr-pen-2: dash +
    • ogr-pen-3: short-dash +
    • ogr-pen-4: long-dash +
    • ogr-pen-5: dot +line +
    • ogr-pen-6: dash-dot +line +
    • ogr-pen-7: dash-dot-dot +line +
    • ogr-pen-8: alternate-line (sets every other pixel) + +
    +

    +System-specific ids are very +likely to be meaningful only to that specific system that created them. +The ids should start with the system's name, followed by a dash (-), +followed by whatever information is meaningful to that system (a number, +a name, a filename, etc.).
    +e.g. "mapinfo-5", or "mysoft-lines.sym-123", or "othersystems-funnyline" +

    +System-specific ids are allowed in order to prevent loss of information when +dealing with data from systems that store line patterns in external files +or that have their own pre-defined set of line styles. (To do a MapInfo MIF +to TAB translation without any loss for instance.) +

    Examples:
    +PEN(c:#00FF00,id:"ogr-pen-0") - simple solid line
    +PEN(c:#00FF00,id:"mapinfo-5,ogr-pen-7") - corresponds to MapInfo's Pen #5, and a system that can't understand MapInfo pens falls back on the default "ogr-pen-7" pen (dot-dot line). +

    capPen Cap - Set the shape of end points of lines.
    +b=BUTT The ends of the line don't extend beyond the end points. This is the default.
    +r=ROUND Terminate lines with a circle whose diameter is equal to the line width.
    +p=PROJECTING Similar to BUTT, but the ends of the line extend by half of line width beyond the end points.
    jPen Join - Set the shape of the join point (vertex) of lines.
    +m=MITER Extend the outer edge of the lines until they touch. This is the default.
    +r=ROUNDED Join lines with an arc whose center is at the join point and whose diameter is equal to the line width.
    +b=BEVEL Join the lines with butt end caps and fill the resulting triangular notch at the join position.
    dpPerpendicular Offset - Offset from the line center
    + If the offset is negative then the pen will be drawn left of the main segment and right otherwise. +
    lPriority Level - Numeric value defining the order in which style +parts should be drawn. Lower priority style parts are drawn first, and +higher priority ones are drawn on top. +
    + If priority level is unspecified, the default is 1.
    + +

    +2.4 Brush Tool Parameters

    +Applicable geometry types: +
      +
    • +Point: Not applicable.
    • + +
    • +Polyline: Not applicable.
    • + +
    • +Polygon: Defines the way the surface of a polygon is filled.
    • +
    +Here is the current list of BRUSH tool parameters. Note again that this +list can eventually grow and that all parameters are optional: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    param_nameDescription
    fc +

    Brush ForeColor, expressed in hexadecimal (#RRGGBB[AA]) +
    + [AA] the last 2 digits define the alpha channel value, with 0 being transparent and FF being opaque. The default for [AA] is FF (opaque)
    + Suggested default brush color: 50% grey (c:#808080) +
    + Example: BRUSH(fc:#FF0000) +
    + Predefined color names may be allowed in future versions of the specification.

    +
    bc +

    Brush BackColor, expressed in hexadecimal (#RRGGBB[AA]) +
    + [AA] the last 2 digits define the alpha channel value, with 0 being transparent and FF being opaque. The default for [AA] is FF (opaque)
    + Suggested default brush color: 50% grey (c:#808080) +
    + Example: BRUSH(bc:#FF0000) +
    + Predefined color names may be allowed in future versions of the specification.

    +
    id +

    Brush Name or Brush Id - Comma-delimited list of brush names or ids. The names in the comma-delimited list +of ids are scanned until one is recognized by the target system. +

    +

    +Brush Ids can be either system-specific ids (see further below) or be one +of the pre-defined OGR brush ids for well known brush patterns. The id +parameter should always include one of the OGR ids at the end of the +comma-delimited list of ids so that an application never has to rely on +understanding system-specific ids. +

    +Here is the current list of OGR brush ids (this could grow over time):
    +

      +
    • ogr-brush-0: solid (the default when no id is provided) +
    • ogr-brush-1: null brush (transparent - no fill) +
    • ogr-brush-2: horizontal hatch /* ------ */ +
    • ogr-brush-3: vertical hatch /* |||||| */ +
    • ogr-brush-4: fdiagonal hatch /* \\\\\\ */ +
    • ogr-brush-5: bdiagonal hatch /* ////// */ +
    • ogr-brush-6: cross hatch /* ++++++ */ +
    • ogr-brush-7: diagcross hatch /* xxxxxx */ +
    +

    +Like with Pen Ids, system-specific brush ids are very +likely to be meaningful only to that specific system that created them. +The ids should start with the system's name, followed by a dash (-), +followed by whatever information is meaningful to that system (a number, +a name, a filename, etc.). +

    The following conventions will be used for common system-specific brush ids:

    +
      +
    • "bmp-filename.bmp" for Windows BMP patterns +
    • ??? any others, e.g. vector symbols, WMF, ??? + +
    + +
    aAngle - Rotation angle (in degrees, counterclockwise) to apply +to the brush pattern.
    sSize or Scaling Factor - Numeric value with or without units. +
    If units are specified, then this value is the absolute size to draw +the brush or symbol. +
    If no units are specified then it is taken as a scaling factor relative +to the symbol's default size.
    dx, dySpacing - If filling an area using point symbols, these values +will define the spacing to use between them. "dx" is the horizontal distance +between the center of 2 adjacent symbols and "dy" is the vertical distance. +
    The default is to use the symbol's MBR width for dx, and the symbol's +height for dy.
    lPriority Level - Numeric value defining the order in which style +parts should be drawn. Lower priority style parts are drawn first, and +higher priority ones are drawn on top. +
    If priority level is unspecified, the default is 1.
    + +

    +2.5 Symbol Tool Parameters

    +Applicable geometry types: +
      +
    • +Point: Place a symbol at the point's location
    • + +
    • +Polyline: Place symbols along the polyline, either at each vertex, or equally +spaced.
    • + +
    • +Polygon: Place the symbols on the outline of the polygon.
    • +
    +Here is the current list of SYMBOL tool parameters. Note again that this +list can eventually grow and that all parameters are optional: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    param_nameDescription
    id +

    Symbol Name or Id - Comma-delimited list of symbol names or ids. The names in the comma-delimited list +of ids are scanned until one is recognized by the target system. +

    +

    +Symbol Ids can be either system-specific ids (see further below) or be one +of the pre-defined OGR symbol ids for well known symbols. The id +parameter should always include one of the OGR ids at the end of the +comma-delimited list of ids so that an application never has to rely on +understanding system-specific ids. +

    +Here is the current list of OGR symbol ids (this could grow over time):
    +

      +
    • ogr-sym-0: cross (+) +
    • ogr-sym-1: diagcross (X) +
    • ogr-sym-2: circle (not filled) +
    • ogr-sym-3: circle (filled) +
    • ogr-sym-4: square (not filled) +
    • ogr-sym-5: square (filled) +
    • ogr-sym-6: triangle (not filled) +
    • ogr-sym-7: triangle (filled) +
    • ogr-sym-8: star (not filled) +
    • ogr-sym-9: star (filled) +
    • ogr-sym-10: vertical bar (can be rotated using angle attribute to produce diag bar) +
    • ??? should any other common be included ??? +
    +

    +Like with Pen Ids, system-specific symbol ids are very +likely to be meaningful only to that specific system that created them. +The ids should start with the system's name, followed by a dash (-), +followed by whatever information is meaningful to that system (a number, +a name, a filename, etc.). +

    The following conventions will be used for common system-specific symbol ids:

    +
      +
    • "bmp-filename.bmp" for Windows BMP symbols +
    • ??? any others, e.g. vector symbols, WMF, ??? + +
    +
    aAngle - Rotation angle (in degrees, counterclockwise) to apply +to the symbol.
    c +

    Symbol Color, expressed in hexadecimal (#RRGGBB[AA]) +
    + [AA] the last 2 digits define the alpha channel value, with 0 being transparent and FF being opaque. The default for [AA] is FF (opaque)
    + Suggested default symbol color: black (c:#000000) +
    + Example: SYMBOL(c:#FF0000) +
    + Predefined color names may be allowed in future versions of the specification.

    +
    o +

    Symbol Outline Color, expressed in hexadecimal (#RRGGBB[AA]), no outline if not set. +

    sSize or Scaling Factor - Numeric value with or without units. +
    If units are specified, then this value is the absolute size to draw +the symbol. +
    If no units are specified then it is taken as a scaling factor relative +to the symbol's default size.
    dx, dyX and Y offset of the symbol's insertion point. +
    Applies to point geometries, and to symbols placed at each vertex of +a polyline.
    ds, dp, diSpacing - For symbols spaced along a line. +
    "ds" is the step to use when placing symbols along the line. +
    By default, symbols applied to a feature with a line geometry are placed +at each vertex, but setting "ds" triggers the placement of symbols at an +equal distance along the line. "ds" has no effect for a feature with a +point geometry. +
    "dp" can be used together with "ds" to specify the perpendicular distance +between the symbols' center and the line along which they're placed. +
    Finally, "di" can be used to specify an initial offset from the beginning +of the line. +
    Example: +
    SYMBOL(id:123, s:5, di:5px, ds:50px)
    lPriority Level - Numeric value defining the order in which style +parts should be drawn. Lower priority style parts are drawn first, and +higher priority ones are drawn on top. +
    If priority level is unspecified, the default is 1.
    + +

    +2.6 Label Tool Parameters

    +Applicable geometry types: +
      +
    • +Point: Place a text label at the point's location
    • + +
    • +Polyline: Place text along the polyline.
    • + +
    • +Polygon: Place a label at the centroid of the polygon. All parameters behave +exactly as if the geometry was a point located at the polygon's centroid.
    • +
    +Here is the current list of LABEL tool parameters. Note again that that +this list can eventually grow and all parameters are optional: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    param_nameDescription
    fFont Name - +
    + Comma-delimited list of fonts names. works like the HTML FONT tag: the list of font names is scanned until a supported font name is encountered. +
    + Example: LABEL(f:"Arial, Helvetica", s:12pt, t:"Hello World!") + +
    sFont Size - Numeric value with units.
    tText String - Can be a constant string, or a reference to an +attribute field's value. If a double-quote character is present in the string, +it is escaped with a antislash (\) character before it. +
    + Examples: +
    + LABEL(f:"Arial, Helvetica", s:12pt, t:"Hello World!") +
    + LABEL(f:"Arial, Helvetica", s:12pt, t:"Hello World with escaped double-quote(\") character!") +
    + LABEL(f:"Arial, Helvetica", s:12pt, t:{text_value})
    aAngle - Rotation angle (in degrees, counterclockwise).
    cText Foreground Color, expressed in hexadecimal (#RRGGBB[AA]) +
    + Suggested default: black (c:#000000) +
    + Predefined color names may be allowed in future versions of the specification.
    bText Background Color - Color of the filled box to draw behind the label, expressed in hexadecimal (#RRGGBB[AA]), no box drawn if not set.
    oText Outline Color - Color of the text outline (halo in MapInfo terminology), expressed in hexadecimal (#RRGGBB[AA]), no outline if not set.
    hShadow Color - Color of the text shadow, expressed in hexadecimal (#RRGGBB[AA]), no shadow if not set.
    wStretch - The stretch factor changes the width of all + characters in the font by factor percent. For example, setting + factor to 150 results in all characters in the font being 1.5 + times (i.e. 150%) wider. The default stretch factor is 100.
    stStrike out text
    mLabel Placement Mode - How is the text drawn relative to the +feature's geometry. +
    + "m:p" - The default, simple label attached to a point or to the first +vertex of a polyline. +
    + "m:l" - Text is attached to the last vertex of a polyline. A PEN tool +can be combined with this LABEL tool to draw the polyline as a leader to +the label. +
    + "m:s" - Stretch text string along polyline, with an equal spacing between +each character. +
    + "m:m" - Place text as a single label at the middle of a polyline (based +on total line length). +
    + "m:w" - One word per line segment in a polyline. +
    +
    + "m:h" - Every word of text attached to polyline is placed horizontally +in its segment, anchor point is a center of segment. +
    +
    + "m:a" - Every word of text attached to polyline is stretched to fit +the segment of polyline and placed along that segment. The anchor point is +a start of a segment. +
    +
    p + + + + + +
    Anchor Position - A value from 1 to 12 defining the + label's position relative to the point to which it is + attached. There are four vertical alignment modes: + baseline, center, top and bottom; + and three horizontal modes: left, center and + right. See the scheme:
    +
    dx, dyX and Y offset of the label's insertion point. +
    + Applies to text placed on a point, or at each vertex of a polyline.
    dpPerpendicular Offset - For labels placed along a line. +
    + "dp" specifies the perpendicular distance between the label and the +line along which it is placed. If the offset is negative then the label will be shifted left of the main segment and right otherwise.
    boBold - +
    + If specified, then text will be bold.
    itItalic -
    unUnderline -
    lPriority Level - Numeric value defining the order in which style +parts should be drawn. Lower priority style parts are drawn first, and +higher priority ones are drawn on top. +
    + If priority level is unspecified, the default is 1.
    + +

    +2.7 Styles Table Format

    +For file formats that support tables of styles, then the predefined styles +would be stored in that format. +

    For file formats that do not support tables of styles, then we might +want to store them in a text file with a .ofs (OGR Feature Styles) extension and the same basename as the dataset. +This would apply to formats like ArcView Shapes. +

    Here is what one of those .OFS files could look like: +

    +    OFS-Version: 1.0
    +    StyleField: "style"
    +
    +    DefaultStyle: PEN(C:#000000FF)
    +    road:      PEN(c:#FF0000,w:5px)
    +    lake:      BRUSH(fc:#0000FF);PEN(c:#000000)
    +    campsite:  SYMBOL(c:#00FF00,id:"points.sym-45,ogr-sym-7")
    +    label:     LABEL(f:"Times New Roman",s:12pt,t:{text_string})
    + +

    +

    The first line is a signature with a version number. Must be present +

    The second line (StyleField: "style") is the name of the attribute field in which the Feature Style String is stored for each object in the corresponding layer. This is optional, if not set, then the objects in the layer will all share the same style defined in DefaultStyle. +

    The third line (DefaultStyle:...) defines the default style that applies by default to all objects that have no explicit style. +

    Then the list of style definitions follow. +

      +

    2.8 Suggestions, possible extensions

    +

    2.8.1 Global list of ids for pen, brush, and symbol ids

    +

    There have been discussions about defining a global list of pen, brush, and symbol ids together with a table containing the definition of each code. This would work similarly to the EPSG projection codes, where the table is updated when new codes are added. This would have had some advantages, mainly to provide a common set of codes to replace the vendor-dependent pen, brush and symbol ids. However on the other hand, this mechanism would be redundant with the current approach which is to use a feature style definition language.

    +

    2.8.2 Use of custom brushes in pen definitions

    +

    In some cases one might want a pen's pattern to be a brush. To achieve this, a PEN() definition would have to contain a BRUSH() definition (e.g. PEN(c:"#FF00FFFF",BRUSH(id:"ogr-brush-4")) or PEN(c:"#FF00FFFF",pen-brush,id:"ogr-brush-4") or ???) but this is currently impossible with the current specs. +

    Since there is no current need for custom brush patterns in pen (no file formats that we know of support that), we won't include it now but may do it later. In the meantime, one can use the "gp" (geometric pattern) pen parameter to allow specifying one of the OGR brushes as a pen brush pattern. +

      +

    +

    2.9 Using OGR SQL to transfer the style between the data sources

    +

    We can use the OGR_STYLE special field to extract the feature level style, and ogr2ogr can be used to transfer the style string between the data sources according to the following example:

    +
      ogr2ogr -f "ESRI Shapefile" -sql "select *, OGR_STYLE from rivers" rivers.shp rivers.tab
    +

    Without specifying the length of the style field the output driver may truncate the length to a default value. Therefore it may be necessary to specify the target length manually, like:

    +
      ogr2ogr -f "ESRI Shapefile" -sql "select *, CAST(OGR_STYLE AS character(255)) from rivers" rivers.shp rivers.tab
    +

    OGR is aware of using the OGR_STYLE field if exists and OGRFeature::GetStyleString will return the value of this field if no style string have been specified programmatically.

    +

      +

    +


    +

    +3. OGR Support Classes

    +The complete set of classes/API functions is still to be determined. +

    Here are some rough ideas: +

      +
    • +Add a 'char *pszStyleString;' member to the OGRFeature, together with OGRFeature::GetStyleString() +and SetStyleString() methods.
    • + +
    • +Add a OGRStyleTable in the Dataset and in the Layer, The both are +using StringList to store the StyleTable, ex of a entry "@name, "PEN(color:#122334)"
    • + +
    • +Add a char **GetStyleTable() and GBool SetStyleTable(char **) in Dataset +and in Layer
    • + +
    • +The OGRStyle class knows how to parse a style string,
    • +
    + +
    +    typedef enum ogr_style_tool_class_id
    +    {
    +       OGRSTCNone,
    +       OGRSTCPen,
    +       OGRSTCBrush,
    +       OGRSTCSymbol,
    +       OGRSTCLabel
    +    } OGRSTClassId;
    +
    +    typedef enum ogr_style_tool_units_id
    +    {
    +       OGRSTUGround,
    +       OGRSTUPixel,
    +       OGRSTUPoints,
    +       OGRSTUMM,
    +       OGRSTUCM,
    +       OGRSTUInches
    +    } OGRSTUnitId;
    +
    +    typedef enum ogr_style_tool_param_pen_id
    +    {  
    +      OGRSTPenColor = 0,                   
    +      OGRSTPenWidth,                   
    +      OGRSTPenPattern,
    +      OGRSTPenId,
    +      OGRSTPenPerOffset,
    +      OGRSTPenCap,
    +      OGRSTPenJoin,
    +      OGRSTPenPriority,
    +      OGRSTPenLast
    +              
    +    } OGRSTPenParam;
    +
    +    typedef enum ogr_style_tool_param_brush_id
    +    {  
    +      OGRSTBrushFColor = 0,                   
    +      OGRSTBrushBColor,                   
    +      OGRSTBrushId,
    +      OGRSTBrushAngle,                   
    +      OGRSTBrushSize,
    +      OGRSTBrushDx,
    +      OGRSTBrushDy,
    +      OGRSTBrushPriority,
    +      OGRSTBrushLast
    +              
    +    } OGRSTBrushParam;
    +
    +
    +
    +    typedef enum ogr_style_tool_param_symbol_id
    +    {  
    +      OGRSTSymbolId = 0,
    +      OGRSTSymbolAngle,
    +      OGRSTSymbolColor,
    +      OGRSTSymbolSize,
    +      OGRSTSymbolDx,
    +      OGRSTSymbolDy,
    +      OGRSTSymbolStep,
    +      OGRSTSymbolPerp,
    +      OGRSTSymbolOffset,
    +      OGRSTSymbolPriority,
    +      OGRSTSymbolLast
    +              
    +    } OGRSTSymbolParam;
    +
    +    typedef enum ogr_style_tool_param_label_id
    +    {  
    +      OGRSTLabelFontName = 0,
    +      OGRSTLabelSize,
    +      OGRSTLabelTextString,
    +      OGRSTLabelAngle,
    +      OGRSTLabelFColor,
    +      OGRSTLabelBColor,
    +      OGRSTLabelPlacement,
    +      OGRSTLabelAnchor,
    +      OGRSTLabelDx,
    +      OGRSTLabelDy,
    +      OGRSTLabelPerp,
    +      OGRSTLabelBold,
    +      OGRSTLabelItalic,
    +      OGRSTLabelUnderline,
    +      OGRSTLabelPriority,
    +      OGRSTLabelLast
    +              
    +    } OGRSTLabelParam;
    +
    +    //Every time a pszStyleString given in parameter is NULL, 
    +      the StyleString defined in the Mgr will be use.
    +
    +class OGRStyleMgr
    +{
    +    OGRStyleMgr(OGRStyleTable *poDataSetStyleTable =NULL);
    + 
    +    GBool SetFeatureStyleString(OGRFeature *,const char *pszStyleString=NULL,
    +				GBool bNoMatching = FALSE);
    +    /*it will set in the given feature the pszStyleString with 
    +	    the style or will set the style name found in 
    +            dataset StyleTable (if bNoMatching == FALSE)*/
    +	      
    +    const char *InitFromFeature(OGRFeature *);
    +    GBool InitStyleString(const char *pszStyleString = NULL);
    +    
    +    const char *GetStyleName(const char *pszStyleString= NULL);
    +    const char *GetStyleByName(const char *pszStyleName);
    +    
    +    GBool AddStyle(const char *pszStyleName, const char *pszStyleString=NULL);
    +    
    +    const char *GetStyleString(OGRFeature * = NULL);
    + 
    +    GBool AddPart(OGRStyleTool *);
    +    GBool AddPart(const char *);
    +
    +    int GetPartCount(const char *pszStyleString = NULL);
    +    OGRStyleTool *GetPart(int hPartId, const char *pszStyleString = NULL);
    +        
    +    OGRStyleTable *GetDataSetStyleTable(){return m_poDataSetStyleTable;}
    +    
    +    OGRStyleTool *CreateStyleToolFromStyleString(const char *pszStyleString);
    +
    +}; 
    +
    +class OGRStyleTool
    +{
    +    OGRStyleTool(OGRSTClassId eClassId);
    +    GBool GetRGBFromString(const char *pszColor, int &nRed, int &nGreen, 
    +			   int &nBlue);
    +    int   GetSpecificId(const char *pszId, const char *pszWanted); 
    +    OGRSTClassId GetType();
    +    void SetUnit(OGRSTUnitId,double dfScale = 1.0); //the dfScale will be
    +         //used if we are working with Ground Unit ( ground = paper * scale);
    +    OGRSTUnitId GetUnit(){return m_eUnit;}   
    +    void SetStyleString(const char *pszStyleString);
    +};
    +
    +class OGRStylePen : public OGRStyleTool
    +{
    +   /**********************************************************************/
    +    /* Explicit fct for all parameters defined in the Drawing tools  Pen */
    +    /**********************************************************************/
    +     
    +    const char *Color(GBool &bDefault)
    +    void SetColor(const char *pszColor)
    +    double Width(GBool &bDefault)
    +    void SetWidth(double dfWidth)
    +    const char *Pattern(GBool &bDefault)
    +    void SetPattern(const char *pszPattern)
    +    const char *Id(GBool &bDefault)
    +    void SetId(const char *pszId)
    +    double PerpendicularOffset(GBool &bDefault)
    +    void SetPerpendicularOffset(double dfPerp)
    +    const char *Cap(GBool &bDefault)
    +    void SetCap(const char *pszCap)
    +    const char *Join(GBool &bDefault)
    +    void SetJoin(const char *pszJoin)
    +    int  Priority(GBool &bDefault)
    +    void SetPriority(int nPriority)
    +    /*****************************************************************/
    +
    +
    +    inline const char *GetParamStr(OGRSTPenParam eParam, GBool &bValueIsNull)
    +    inline int GetParamNum(OGRSTPenParam eParam,GBool &bValueIsNull)
    +    inline double GetParamDbl(OGRSTPenParam eParam,GBool &bValueIsNull)
    +    inline void SetParamStr(OGRSTPenParam eParam, 
    +	                        const char *pszParamString)
    +    inline void SetParamNum(OGRSTPenParam eParam, int nParam)
    +    inline void SetParamDbl(OGRSTPenParam eParam, double dfParam)
    +   
    +};
    +
    +
    +class OGRStyleBrush : public OGRStyleTool
    +{
    +
    +    /*a Explicit fct for all parameters defined in the Drawing tools Brush */
    +
    +    const char *ForeColor(GBool &bDefault)
    +    void SetForeColor(const char *pszColor)
    +    const char *BackColor(GBool &bDefault)
    +    void SetBackColor(const char *pszColor)
    +    const char *Id(GBool &bDefault)
    +    void  SetId(const char *pszId)
    +    double Angle(GBool &bDefault)
    +    void SetAngle(double dfAngle)
    +    double Size(GBool &bDefault)
    +    void SetSize(double dfSize)
    +    double SpacingX(GBool &bDefault)
    +    void SetSpacingX(double dfX)
    +    double SpacingY(GBool &bDefault)
    +    void SetSpacingY(double dfY)
    +    int  Priority(GBool &bDefault)
    +    void SetPriority(int nPriority)
    +    
    +    /*****************************************************************/
    +    
    +};
    +class OGRStyleSymbol : public OGRStyleTool
    +{
    +    /*****************************************************************/
    +    /* Explicit fct for all parameters defined in the Drawing tools */
    +    /*****************************************************************/
    +    
    +    const char *Id(GBool &bDefault)
    +    void  SetId(const char *pszId)
    +    double Angle(GBool &bDefault)
    +    void SetAngle(double dfAngle)
    +    const char *Color(GBool &bDefault)
    +    void SetColor(const char *pszColor)
    +    double Size(GBool &bDefault)
    +    void SetSize(double dfSize)
    +    double SpacingX(GBool &bDefault)
    +    void SetSpacingX(double dfX)
    +    double SpacingY(GBool &bDefault)
    +    void SetSpacingY(double dfY)
    +    double Step(GBool &bDefault)
    +    void SetStep(double dfStep)
    +    double Offset(GBool &bDefault)
    +    void SetOffset(double dfOffset)
    +    double Perp(GBool &bDefault)
    +    void SetPerp(double dfPerp)
    +    int  Priority(GBool &bDefault)
    +    void SetPriority(int nPriority)
    +    
    +    /*****************************************************************/
    +    
    +};
    +
    +class OGRStyleLabel : public OGRStyleTool
    +{
    +
    +    /*****************************************************************/
    +    /* Explicit fct for all parameters defined in the Drawing tools */
    +    /*****************************************************************/
    +    
    +    const char *FontName(GBool &bDefault)
    +    void  SetFontName(const char *pszFontName)
    +    double Size(GBool &bDefault)
    +    void SetSize(double dfSize)
    +    const char *TextString(GBool &bDefault)
    +    void SetTextString(const char *pszTextString)
    +    double Angle(GBool &bDefault)
    +    void SetAngle(double dfAngle)
    +    const char *ForeColor(GBool &bDefault)
    +    void SetForColor(const char *pszForColor)
    +    const char *BackColor(GBool &bDefault)
    +    void SetBackColor(const char *pszBackColor)
    +    const char *Placement(GBool &bDefault)
    +    void SetPlacement(const char *pszPlacement)
    +    int  Anchor(GBool &bDefault)
    +    void SetAnchor(int nAnchor)
    +    double SpacingX(GBool &bDefault)
    +    void SetSpacingX(double dfX)
    +    double SpacingY(GBool &bDefault)
    +    void SetSpacingY(double dfY)
    +    double Perp(GBool &bDefault)
    +    void SetPerp(double dfPerp)
    +    GBool Bold(GBool &bDefault)
    +    void SetBold(GBool bBold)
    +    GBool Italic(GBool &bDefault)
    +    void SetItalic(GBool bItalic)
    +    GBool Underline(GBool &bDefault)
    +    void SetUnderline(GBool bUnderline)
    +    int  Priority(GBool &bDefault)
    +    void SetPriority(int nPriority)
    +    /*****************************************************************/
    +    
    +};
    +
    +class OGRStyleTable
    +{
    +    
    +    OGRStyleTable();
    +
    +    GBool AddStyle(const char *pszName,const char *pszStyleString);
    +    GBool RemoveStyle(const char *pszName);
    +    GBool ModifyStyle(const char *pszName, const char *pszStyleString);
    +    
    +    GBool SaveStyleTable(const char *pszFilename);
    +    GBool LoadStyleTable(const char *pszFilename);
    +    const char *Find(const char *pszStyleString);
    +    GBool IsExist(const char *pszName);
    +    const char *GetStyleName(const char *pszName);
    +    void  Print(FILE *fpOut);
    +    void  Clear();
    +};
    +
    +
    +Usage examples:
    +
    +   OGRStyleTable oStyleTable;
    +
    +   OGRStyleMgr   *poStyleMgr = new OGRStyleMgr(&oStyleTable);
    +
    +   // Create a New style in the style table
    +
    +   if (poStyleMgr->AddStyle("@Name","PEN(c:#123456;w:10px);BRUSH(c:345678)"))
    +   {
    +     poStyleMgr->SetFeatureStyleString(poFeature,"@Name",TRUE) or
    +     poStyleMgr->SetFeatureStyleString(poFeature,"PEN(c:#123456,w:10px);BRUSH(c:345678)",FALSE)
    +   }
    +
    +   oStyleTable->SaveStyleTable("ttt.tbl");
    +   
    +
    +   // Create a New style in the style table
    +
    +   poStyleMgr->InitStyleString();
    +   poStyleMgr->AddPart("PEN(c:#123456,w:10px)");
    +   poStyleMgr->AddPart("BRUSH(c:345678)");
    +   poStyleMgr->AddStyle("@Name");
    +   poStyleMgr->SetFeatureStyleString(poFeature,"@Name",TRUE);
    +   oStyleTable->SaveStyleTable("ttt.tbl");
    +   
    +
    +   // Create a New style in the style table
    +
    +   OGRStyleTool  *poStylePen = new OGRStylePen;
    +
    +   poStylePen->SetColor("#123456");
    +   poStylePen->SetUnit(OGRSTUPixel);
    +   poStylePen->SetWidth(10.0);
    +   poStyleMgr->AddPart(poStylePen);
    +   
    +   delete poStylePen;
    +
    +
    +   // Reading a style;
    +
    +   OGRStyleTool  *poStyleTool;
    +
    +   poStyleMgr->GetStyleString(poFeature);
    +   
    +   for (i=0;iGetPartCount();i++)
    +   {
    +      poStyleTool = GetPart(i);
    +      switch(poStyleTool->GetType())
    +      {
    +         case OGRSTCPen:
    +            poStylePen = (OGRStylePen *)poStyleTool;
    +            pszColor = poStylePen->Color(bDefault);
    +            if (bDefault == FALSE)
    +              poStylePen->GetRGBFromString(pszColor, nRed, nGree, 
    +	                                   nBlue, nTrans);
    +            else
    +              // Color not defined;
    +
    +            dfWidth = poStylePen->Width(bDefault);
    +            if (bDefault == FALSE)
    +              // Use dfWidth
    +            else
    +              // dfWidth not defined                
    +     
    +           :
    +           :
    +       }
    +    }
    +
    +
    + +

      +

    +

    +


    + diff --git a/bazaar/plugin/gdal/ogr/ogr_featurestyle.h b/bazaar/plugin/gdal/ogr/ogr_featurestyle.h new file mode 100644 index 000000000..8bdb04cb0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_featurestyle.h @@ -0,0 +1,466 @@ +/****************************************************************************** + * $Id: ogr_featurestyle.h 19442 2010-04-18 00:02:37Z mloskot $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Define of Feature Representation + * Author: Stephane Villeneuve, stephane.v@videtron.ca + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef OGR_FEATURESTYLE_INCLUDE +#define OGR_FEATURESTYLE_INCLUDE + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_core.h" + +class OGRFeature; + +/** + * \file ogr_featurestyle.h + * + * Simple feature style classes. + */ + +/* + * All OGRStyleTool param lists are defined in ogr_core.h. + */ + +typedef enum ogr_style_type +{ + OGRSTypeString, + OGRSTypeDouble, + OGRSTypeInteger, + OGRSTypeBoolean +} OGRSType; + +typedef struct ogr_style_param +{ + int eParam; + const char *pszToken; + GBool bGeoref; + OGRSType eType; +} OGRStyleParamId; + + +typedef struct ogr_style_value +{ + char *pszValue; + double dfValue; + int nValue; // Used for both integer and boolean types + GBool bValid; + OGRSTUnitId eUnit; +} OGRStyleValue; + + +//Everytime a pszStyleString gived in parameter is NULL, +// the StyleString defined in the Mgr will be use. +/** + * This class represents a style table + */ +class CPL_DLL OGRStyleTable +{ + private: + char **m_papszStyleTable; + + CPLString osLastRequestedStyleName; + int iNextStyle; + + public: + OGRStyleTable(); + ~OGRStyleTable(); + GBool AddStyle(const char *pszName,const char *pszStyleString); + GBool RemoveStyle(const char *pszName); + GBool ModifyStyle(const char *pszName, const char *pszStyleString); + + GBool SaveStyleTable(const char *pszFilename); + GBool LoadStyleTable(const char *pszFilename); + const char *Find(const char *pszStyleString); + GBool IsExist(const char *pszName); + const char *GetStyleName(const char *pszName); + void Print(FILE *fpOut); + void Clear(); + OGRStyleTable *Clone(); + void ResetStyleStringReading(); + const char *GetNextStyle(); + const char *GetLastStyleName(); +}; + + +class OGRStyleTool; + +/** + * This class represents a style manager + */ +class CPL_DLL OGRStyleMgr +{ + private: + OGRStyleTable *m_poDataSetStyleTable; + char *m_pszStyleString; + + public: + OGRStyleMgr(OGRStyleTable *poDataSetStyleTable = NULL); + ~OGRStyleMgr(); + + GBool SetFeatureStyleString(OGRFeature *,const char *pszStyleString=NULL, + GBool bNoMatching = FALSE); + /*it will set in the gived feature the pszStyleString with + the style or will set the style name found in + dataset StyleTable (if bNoMatching == FALSE)*/ + + const char *InitFromFeature(OGRFeature *); + GBool InitStyleString(const char *pszStyleString = NULL); + + const char *GetStyleName(const char *pszStyleString= NULL); + const char *GetStyleByName(const char *pszStyleName); + + GBool AddStyle(const char *pszStyleName, const char *pszStyleString=NULL); + + const char *GetStyleString(OGRFeature * = NULL); + + GBool AddPart(OGRStyleTool *); + GBool AddPart(const char *); + + int GetPartCount(const char *pszStyleString = NULL); + OGRStyleTool *GetPart(int hPartId, const char *pszStyleString = NULL); + + /*It could have a reference counting processus for the OGRStyleTable, if + needed */ + + OGRStyleTable *GetDataSetStyleTable(){return m_poDataSetStyleTable;} + + OGRStyleTool *CreateStyleToolFromStyleString(const char *pszStyleString); + +}; + +/** + * This class represents a style tool + */ +class CPL_DLL OGRStyleTool +{ + private: + GBool m_bModified; + GBool m_bParsed; + double m_dfScale; + OGRSTUnitId m_eUnit; + OGRSTClassId m_eClassId; + char *m_pszStyleString; + + virtual GBool Parse() = 0; + + protected: + GBool Parse(const OGRStyleParamId* pasStyle, + OGRStyleValue* pasValue, + int nCount); + + public: + + OGRStyleTool(){} + OGRStyleTool(OGRSTClassId eClassId); + virtual ~OGRStyleTool(); + + GBool GetRGBFromString(const char *pszColor, int &nRed, int &nGreen, + int &nBlue, int &nTransparence); + int GetSpecificId(const char *pszId, const char *pszWanted); + + GBool IsStyleModified() {return m_bModified;} + void StyleModified() {m_bModified = TRUE;} + + GBool IsStyleParsed() {return m_bParsed;} + void StyleParsed() {m_bParsed = TRUE;} + + OGRSTClassId GetType(); + + void SetInternalInputUnitFromParam(char *pszString); + + void SetUnit(OGRSTUnitId,double dfScale = 1.0); //the dfScale will be + //used if we are working with Ground Unit ( ground = paper * scale); + + OGRSTUnitId GetUnit(){return m_eUnit;} + + /* It's existe two way to set the parameters in the Style, with generic +methodes (using a defined enumeration) or with the reel method specific +for Each style tools.*/ + + virtual const char *GetStyleString() = 0; + void SetStyleString(const char *pszStyleString); + const char *GetStyleString(const OGRStyleParamId *pasStyleParam , + OGRStyleValue *pasStyleValue, int nSize); + + const char *GetParamStr(const OGRStyleParamId &sStyleParam , + OGRStyleValue &sStyleValue, + GBool &bValueIsNull); + + int GetParamNum(const OGRStyleParamId &sStyleParam , + OGRStyleValue &sStyleValue, + GBool &bValueIsNull); + + double GetParamDbl(const OGRStyleParamId &sStyleParam , + OGRStyleValue &sStyleValue, + GBool &bValueIsNull); + + void SetParamStr(const OGRStyleParamId &sStyleParam , + OGRStyleValue &sStyleValue, + const char *pszParamString); + + void SetParamNum(const OGRStyleParamId &sStyleParam , + OGRStyleValue &sStyleValue, + int nParam); + + void SetParamDbl(const OGRStyleParamId &sStyleParam , + OGRStyleValue &sStyleValue, + double dfParam); + + double ComputeWithUnit(double, OGRSTUnitId); + int ComputeWithUnit(int , OGRSTUnitId); + +}; + +/** + * This class represents a style pen + */ +class CPL_DLL OGRStylePen : public OGRStyleTool +{ + private: + + OGRStyleValue *m_pasStyleValue; + + GBool Parse(); + + public: + + OGRStylePen(); + virtual ~OGRStylePen(); + + /**********************************************************************/ + /* Explicit fct for all parameters defined in the Drawing tools Pen */ + /**********************************************************************/ + + const char *Color(GBool &bDefault){return GetParamStr(OGRSTPenColor,bDefault);} + void SetColor(const char *pszColor){SetParamStr(OGRSTPenColor,pszColor);} + double Width(GBool &bDefault){return GetParamDbl(OGRSTPenWidth,bDefault);} + void SetWidth(double dfWidth){SetParamDbl(OGRSTPenWidth,dfWidth);} + const char *Pattern(GBool &bDefault){return (const char *)GetParamStr(OGRSTPenPattern,bDefault);} + void SetPattern(const char *pszPattern){SetParamStr(OGRSTPenPattern,pszPattern);} + const char *Id(GBool &bDefault){return GetParamStr(OGRSTPenId,bDefault);} + void SetId(const char *pszId){SetParamStr(OGRSTPenId,pszId);} + double PerpendicularOffset(GBool &bDefault){return GetParamDbl(OGRSTPenPerOffset,bDefault);} + void SetPerpendicularOffset(double dfPerp){SetParamDbl(OGRSTPenPerOffset,dfPerp);} + const char *Cap(GBool &bDefault){return GetParamStr(OGRSTPenCap,bDefault);} + void SetCap(const char *pszCap){SetParamStr(OGRSTPenCap,pszCap);} + const char *Join(GBool &bDefault){return GetParamStr(OGRSTPenJoin,bDefault);} + void SetJoin(const char *pszJoin){SetParamStr(OGRSTPenJoin,pszJoin);} + int Priority(GBool &bDefault){return GetParamNum(OGRSTPenPriority,bDefault);} + void SetPriority(int nPriority){SetParamNum(OGRSTPenPriority,nPriority);} + + /*****************************************************************/ + + const char *GetParamStr(OGRSTPenParam eParam, GBool &bValueIsNull); + int GetParamNum(OGRSTPenParam eParam,GBool &bValueIsNull); + double GetParamDbl(OGRSTPenParam eParam,GBool &bValueIsNull); + void SetParamStr(OGRSTPenParam eParam, const char *pszParamString); + void SetParamNum(OGRSTPenParam eParam, int nParam); + void SetParamDbl(OGRSTPenParam eParam, double dfParam); + const char *GetStyleString(); +}; + +/** + * This class represents a style brush + */ +class CPL_DLL OGRStyleBrush : public OGRStyleTool +{ + private: + + OGRStyleValue *m_pasStyleValue; + + GBool Parse(); + + public: + + OGRStyleBrush(); + virtual ~OGRStyleBrush(); + + /* Explicit fct for all parameters defined in the Drawing tools Brush */ + + const char *ForeColor(GBool &bDefault){return GetParamStr(OGRSTBrushFColor,bDefault);} + void SetForeColor(const char *pszColor){SetParamStr(OGRSTBrushFColor,pszColor);} + const char *BackColor(GBool &bDefault){return GetParamStr(OGRSTBrushBColor,bDefault);} + void SetBackColor(const char *pszColor){SetParamStr(OGRSTBrushBColor,pszColor);} + const char *Id(GBool &bDefault){ return GetParamStr(OGRSTBrushId,bDefault);} + void SetId(const char *pszId){SetParamStr(OGRSTBrushId,pszId);} + double Angle(GBool &bDefault){return GetParamDbl(OGRSTBrushAngle,bDefault);} + void SetAngle(double dfAngle){SetParamDbl(OGRSTBrushAngle,dfAngle );} + double Size(GBool &bDefault){return GetParamDbl(OGRSTBrushSize,bDefault);} + void SetSize(double dfSize){SetParamDbl(OGRSTBrushSize,dfSize );} + double SpacingX(GBool &bDefault){return GetParamDbl(OGRSTBrushDx,bDefault);} + void SetSpacingX(double dfX){SetParamDbl(OGRSTBrushDx,dfX );} + double SpacingY(GBool &bDefault){return GetParamDbl(OGRSTBrushDy,bDefault);} + void SetSpacingY(double dfY){SetParamDbl(OGRSTBrushDy,dfY );} + int Priority(GBool &bDefault){ return GetParamNum(OGRSTBrushPriority,bDefault);} + void SetPriority(int nPriority){ SetParamNum(OGRSTBrushPriority,nPriority);} + + + /*****************************************************************/ + + const char *GetParamStr(OGRSTBrushParam eParam, GBool &bValueIsNull); + int GetParamNum(OGRSTBrushParam eParam,GBool &bValueIsNull); + double GetParamDbl(OGRSTBrushParam eParam,GBool &bValueIsNull); + void SetParamStr(OGRSTBrushParam eParam, const char *pszParamString); + void SetParamNum(OGRSTBrushParam eParam, int nParam); + void SetParamDbl(OGRSTBrushParam eParam, double dfParam); + const char *GetStyleString(); +}; + +/** + * This class represents a style symbol + */ +class CPL_DLL OGRStyleSymbol : public OGRStyleTool +{ + private: + + OGRStyleValue *m_pasStyleValue; + + GBool Parse(); + + public: + + OGRStyleSymbol(); + virtual ~OGRStyleSymbol(); + + /*****************************************************************/ + /* Explicit fct for all parameters defined in the Drawing tools */ + /*****************************************************************/ + + const char *Id(GBool &bDefault){return GetParamStr(OGRSTSymbolId,bDefault);} + void SetId(const char *pszId){ SetParamStr(OGRSTSymbolId,pszId);} + double Angle(GBool &bDefault){ return GetParamDbl(OGRSTSymbolAngle,bDefault);} + void SetAngle(double dfAngle){SetParamDbl(OGRSTSymbolAngle,dfAngle );} + const char *Color(GBool &bDefault){return GetParamStr(OGRSTSymbolColor,bDefault);} + void SetColor(const char *pszColor){SetParamStr(OGRSTSymbolColor,pszColor);} + double Size(GBool &bDefault){ return GetParamDbl(OGRSTSymbolSize,bDefault);} + void SetSize(double dfSize){ SetParamDbl(OGRSTSymbolSize,dfSize );} + double SpacingX(GBool &bDefault){return GetParamDbl(OGRSTSymbolDx,bDefault);} + void SetSpacingX(double dfX){SetParamDbl(OGRSTSymbolDx,dfX );} + double SpacingY(GBool &bDefault){return GetParamDbl(OGRSTSymbolDy,bDefault);} + void SetSpacingY(double dfY){SetParamDbl(OGRSTSymbolDy,dfY );} + double Step(GBool &bDefault){return GetParamDbl(OGRSTSymbolStep,bDefault);} + void SetStep(double dfStep){SetParamDbl(OGRSTSymbolStep,dfStep );} + double Offset(GBool &bDefault){return GetParamDbl(OGRSTSymbolOffset,bDefault);} + void SetOffset(double dfOffset){SetParamDbl(OGRSTSymbolOffset,dfOffset );} + double Perp(GBool &bDefault){return GetParamDbl(OGRSTSymbolPerp,bDefault);} + void SetPerp(double dfPerp){SetParamDbl(OGRSTSymbolPerp,dfPerp );} + int Priority(GBool &bDefault){return GetParamNum(OGRSTSymbolPriority,bDefault);} + void SetPriority(int nPriority){SetParamNum(OGRSTSymbolPriority,nPriority);} + const char *FontName(GBool &bDefault) + {return GetParamStr(OGRSTSymbolFontName,bDefault);} + void SetFontName(const char *pszFontName) + {SetParamStr(OGRSTSymbolFontName,pszFontName);} + const char *OColor(GBool &bDefault){return GetParamStr(OGRSTSymbolOColor,bDefault);} + void SetOColor(const char *pszColor){SetParamStr(OGRSTSymbolOColor,pszColor);} + + /*****************************************************************/ + + const char *GetParamStr(OGRSTSymbolParam eParam, GBool &bValueIsNull); + int GetParamNum(OGRSTSymbolParam eParam,GBool &bValueIsNull); + double GetParamDbl(OGRSTSymbolParam eParam,GBool &bValueIsNull); + void SetParamStr(OGRSTSymbolParam eParam, const char *pszParamString); + void SetParamNum(OGRSTSymbolParam eParam, int nParam); + void SetParamDbl(OGRSTSymbolParam eParam, double dfParam); + const char *GetStyleString(); +}; + +/** + * This class represents a style label + */ +class CPL_DLL OGRStyleLabel : public OGRStyleTool +{ + private: + + OGRStyleValue *m_pasStyleValue; + + GBool Parse(); + + public: + + OGRStyleLabel(); + virtual ~OGRStyleLabel(); + + /*****************************************************************/ + /* Explicit fct for all parameters defined in the Drawing tools */ + /*****************************************************************/ + + const char *FontName(GBool &bDefault){return GetParamStr(OGRSTLabelFontName,bDefault);} + void SetFontName(const char *pszFontName){SetParamStr(OGRSTLabelFontName,pszFontName);} + double Size(GBool &bDefault){return GetParamDbl(OGRSTLabelSize,bDefault);} + void SetSize(double dfSize){SetParamDbl(OGRSTLabelSize,dfSize);} + const char *TextString(GBool &bDefault){return GetParamStr(OGRSTLabelTextString,bDefault);} + void SetTextString(const char *pszTextString){SetParamStr(OGRSTLabelTextString,pszTextString);} + double Angle(GBool &bDefault){return GetParamDbl(OGRSTLabelAngle,bDefault);} + void SetAngle(double dfAngle){SetParamDbl(OGRSTLabelAngle,dfAngle);} + const char *ForeColor(GBool &bDefault){return GetParamStr(OGRSTLabelFColor,bDefault);} + void SetForColor(const char *pszForColor){SetParamStr(OGRSTLabelFColor,pszForColor);} + const char *BackColor(GBool &bDefault){return GetParamStr(OGRSTLabelBColor,bDefault);} + void SetBackColor(const char *pszBackColor){SetParamStr(OGRSTLabelBColor,pszBackColor);} + const char *Placement(GBool &bDefault){return GetParamStr(OGRSTLabelPlacement,bDefault);} + void SetPlacement(const char *pszPlacement){SetParamStr(OGRSTLabelPlacement,pszPlacement);} + int Anchor(GBool &bDefault){return GetParamNum(OGRSTLabelAnchor,bDefault);} + void SetAnchor(int nAnchor){SetParamNum(OGRSTLabelAnchor,nAnchor);} + double SpacingX(GBool &bDefault){return GetParamDbl(OGRSTLabelDx,bDefault);} + void SetSpacingX(double dfX){SetParamDbl(OGRSTLabelDx,dfX);} + double SpacingY(GBool &bDefault){return GetParamDbl(OGRSTLabelDy,bDefault);} + void SetSpacingY(double dfY){SetParamDbl(OGRSTLabelDy,dfY);} + double Perp(GBool &bDefault){return GetParamDbl(OGRSTLabelPerp,bDefault);} + void SetPerp(double dfPerp){SetParamDbl(OGRSTLabelPerp,dfPerp);} + GBool Bold(GBool &bDefault){return GetParamNum(OGRSTLabelBold,bDefault);} + void SetBold(GBool bBold){SetParamNum(OGRSTLabelBold,bBold);} + GBool Italic(GBool &bDefault){return GetParamNum(OGRSTLabelItalic,bDefault);} + void SetItalic(GBool bItalic){SetParamNum(OGRSTLabelItalic,bItalic);} + GBool Underline(GBool &bDefault){return GetParamNum(OGRSTLabelUnderline,bDefault);} + void SetUnderline(GBool bUnderline){SetParamNum(OGRSTLabelUnderline,bUnderline);} + int Priority(GBool &bDefault){return GetParamNum(OGRSTLabelPriority,bDefault);} + void SetPriority(int nPriority){SetParamNum(OGRSTLabelPriority,nPriority);} + GBool Strikeout(GBool &bDefault){return GetParamNum(OGRSTLabelStrikeout,bDefault);} + void SetStrikeout(GBool bStrikeout){SetParamNum(OGRSTLabelStrikeout,bStrikeout);} + double Stretch(GBool &bDefault){return GetParamDbl(OGRSTLabelStretch,bDefault);} + void SetStretch(double dfStretch){SetParamDbl(OGRSTLabelStretch,dfStretch);} + const char *AdjustmentHor(GBool &bDefault){return GetParamStr(OGRSTLabelAdjHor,bDefault);} + void SetAdjustmentHor(const char *pszAdjustmentHor){SetParamStr(OGRSTLabelAdjHor,pszAdjustmentHor);} + const char *AdjustmentVert(GBool &bDefault){return GetParamStr(OGRSTLabelAdjVert,bDefault);} + void SetAdjustmentVert(const char *pszAdjustmentVert){SetParamStr(OGRSTLabelAdjHor,pszAdjustmentVert);} + const char *ShadowColor(GBool &bDefault){return GetParamStr(OGRSTLabelHColor,bDefault);} + void SetShadowColor(const char *pszShadowColor){SetParamStr(OGRSTLabelHColor,pszShadowColor);} + const char *OutlineColor(GBool &bDefault){return GetParamStr(OGRSTLabelOColor,bDefault);} + void SetOutlineColor(const char *pszOutlineColor){SetParamStr(OGRSTLabelOColor,pszOutlineColor);} + + /*****************************************************************/ + + const char *GetParamStr(OGRSTLabelParam eParam, GBool &bValueIsNull); + int GetParamNum(OGRSTLabelParam eParam,GBool &bValueIsNull); + double GetParamDbl(OGRSTLabelParam eParam,GBool &bValueIsNull); + void SetParamStr(OGRSTLabelParam eParam, const char *pszParamString); + void SetParamNum(OGRSTLabelParam eParam, int nParam); + void SetParamDbl(OGRSTLabelParam eParam, double dfParam); + const char *GetStyleString(); +}; + +#endif /* OGR_FEATURESTYLE_INCLUDE */ diff --git a/bazaar/plugin/gdal/ogr/ogr_fromepsg.cpp b/bazaar/plugin/gdal/ogr/ogr_fromepsg.cpp new file mode 100644 index 000000000..232e3dfa8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_fromepsg.cpp @@ -0,0 +1,2799 @@ +/****************************************************************************** + * $Id: ogr_fromepsg.cpp 28565 2015-02-27 10:26:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Generate an OGRSpatialReference object based on an EPSG + * PROJCS, or GEOGCS code. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "ogr_p.h" +#include "cpl_csv.h" +#include + +CPL_CVSID("$Id: ogr_fromepsg.cpp 28565 2015-02-27 10:26:21Z rouault $"); + +#ifndef PI +# define PI 3.14159265358979323846 +#endif + +void OGRPrintDouble( char * pszStrBuf, double dfValue ); + +static const char *papszDatumEquiv[] = +{ + "Militar_Geographische_Institut", + "Militar_Geographische_Institute", + "World_Geodetic_System_1984", + "WGS_1984", + "WGS_72_Transit_Broadcast_Ephemeris", + "WGS_1972_Transit_Broadcast_Ephemeris", + "World_Geodetic_System_1972", + "WGS_1972", + "European_Terrestrial_Reference_System_89", + "European_Reference_System_1989", + NULL +}; + +/************************************************************************/ +/* OGREPSGDatumNameMassage() */ +/* */ +/* Massage an EPSG datum name into WMT format. Also transform */ +/* specific exception cases into WKT versions. */ +/************************************************************************/ + +void OGREPSGDatumNameMassage( char ** ppszDatum ) + +{ + int i, j; + char *pszDatum = *ppszDatum; + + if (pszDatum[0] == '\0') + return; + +/* -------------------------------------------------------------------- */ +/* Translate non-alphanumeric values to underscores. */ +/* -------------------------------------------------------------------- */ + for( i = 0; pszDatum[i] != '\0'; i++ ) + { + if( pszDatum[i] != '+' + && !(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z') + && !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z') + && !(pszDatum[i] >= '0' && pszDatum[i] <= '9') ) + { + pszDatum[i] = '_'; + } + } + +/* -------------------------------------------------------------------- */ +/* Remove repeated and trailing underscores. */ +/* -------------------------------------------------------------------- */ + for( i = 1, j = 0; pszDatum[i] != '\0'; i++ ) + { + if( pszDatum[j] == '_' && pszDatum[i] == '_' ) + continue; + + pszDatum[++j] = pszDatum[i]; + } + if( pszDatum[j] == '_' ) + pszDatum[j] = '\0'; + else + pszDatum[j+1] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Search for datum equivelences. Specific massaged names get */ +/* mapped to OpenGIS specified names. */ +/* -------------------------------------------------------------------- */ + for( i = 0; papszDatumEquiv[i] != NULL; i += 2 ) + { + if( EQUAL(*ppszDatum,papszDatumEquiv[i]) ) + { + CPLFree( *ppszDatum ); + *ppszDatum = CPLStrdup( papszDatumEquiv[i+1] ); + break; + } + } +} + +/************************************************************************/ +/* EPSGAngleStringToDD() */ +/* */ +/* Convert an angle in the specified units to decimal degrees. */ +/************************************************************************/ + +static double +EPSGAngleStringToDD( const char * pszAngle, int nUOMAngle ) + +{ + double dfAngle; + + if( nUOMAngle == 9110 ) /* DDD.MMSSsss */ + { + char *pszDecimal; + + dfAngle = ABS(atoi(pszAngle)); + pszDecimal = (char *) strchr(pszAngle,'.'); + if( pszDecimal != NULL && strlen(pszDecimal) > 1 ) + { + char szMinutes[3]; + char szSeconds[64]; + + szMinutes[0] = pszDecimal[1]; + if( pszDecimal[2] >= '0' && pszDecimal[2] <= '9' ) + szMinutes[1] = pszDecimal[2]; + else + szMinutes[1] = '0'; + + szMinutes[2] = '\0'; + dfAngle += atoi(szMinutes) / 60.0; + + if( strlen(pszDecimal) > 3 ) + { + szSeconds[0] = pszDecimal[3]; + if( pszDecimal[4] >= '0' && pszDecimal[4] <= '9' ) + { + szSeconds[1] = pszDecimal[4]; + szSeconds[2] = '.'; + strncpy( szSeconds+3, pszDecimal + 5, sizeof(szSeconds)-3 ); + szSeconds[sizeof(szSeconds)-1] = 0; + } + else + { + szSeconds[1] = '0'; + szSeconds[2] = '\0'; + } + dfAngle += CPLAtof(szSeconds) / 3600.0; + } + } + + if( pszAngle[0] == '-' ) + dfAngle *= -1; + } + else if( nUOMAngle == 9105 || nUOMAngle == 9106 ) /* grad */ + { + dfAngle = 180 * (CPLAtof(pszAngle ) / 200); + } + else if( nUOMAngle == 9101 ) /* radians */ + { + dfAngle = 180 * (CPLAtof(pszAngle ) / PI); + } + else if( nUOMAngle == 9103 ) /* arc-minute */ + { + dfAngle = CPLAtof(pszAngle) / 60; + } + else if( nUOMAngle == 9104 ) /* arc-second */ + { + dfAngle = CPLAtof(pszAngle) / 3600; + } + else /* decimal degrees ... some cases missing but seeminly never used */ + { + CPLAssert( nUOMAngle == 9102 || nUOMAngle == 0 ); + + dfAngle = CPLAtof(pszAngle ); + } + + return( dfAngle ); +} + +/************************************************************************/ +/* EPSGGetUOMAngleInfo() */ +/************************************************************************/ + +int EPSGGetUOMAngleInfo( int nUOMAngleCode, + char **ppszUOMName, + double * pdfInDegrees ) + +{ + const char *pszUOMName = NULL; + double dfInDegrees = 1.0; + const char *pszFilename; + char szSearchKey[24]; + + /* We do a special override of some of the DMS formats name */ + /* This will also solve accuracy problems when computing */ + /* the dfInDegree value from the CSV values (#3643) */ + if( nUOMAngleCode == 9102 || nUOMAngleCode == 9107 + || nUOMAngleCode == 9108 || nUOMAngleCode == 9110 + || nUOMAngleCode == 9122 ) + { + if( ppszUOMName != NULL ) + *ppszUOMName = CPLStrdup("degree"); + if( pdfInDegrees != NULL ) + *pdfInDegrees = 1.0; + return TRUE; + } + + pszFilename = CSVFilename( "unit_of_measure.csv" ); + + sprintf( szSearchKey, "%d", nUOMAngleCode ); + pszUOMName = CSVGetField( pszFilename, + "UOM_CODE", szSearchKey, CC_Integer, + "UNIT_OF_MEAS_NAME" ); + +/* -------------------------------------------------------------------- */ +/* If the file is found, read from there. Note that FactorC is */ +/* an empty field for any of the DMS style formats, and in this */ +/* case we really want to return the default InDegrees value */ +/* (1.0) from above. */ +/* -------------------------------------------------------------------- */ + if( pszUOMName != NULL ) + { + double dfFactorB, dfFactorC; + + dfFactorB = + CPLAtof(CSVGetField( pszFilename, + "UOM_CODE", szSearchKey, CC_Integer, + "FACTOR_B" )); + + dfFactorC = + CPLAtof(CSVGetField( pszFilename, + "UOM_CODE", szSearchKey, CC_Integer, + "FACTOR_C" )); + + if( dfFactorC != 0.0 ) + dfInDegrees = (dfFactorB / dfFactorC) * (180.0 / PI); + + // For some reason, (FactorB) is not very precise in EPSG, use + // a more exact form for grads. + if( nUOMAngleCode == 9105 ) + dfInDegrees = 180.0 / 200.0; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise handle a few well known units directly. */ +/* -------------------------------------------------------------------- */ + else + { + switch( nUOMAngleCode ) + { + case 9101: + pszUOMName = "radian"; + dfInDegrees = 180.0 / PI; + break; + + case 9102: + case 9107: + case 9108: + case 9110: + case 9122: + pszUOMName = "degree"; + dfInDegrees = 1.0; + break; + + case 9103: + pszUOMName = "arc-minute"; + dfInDegrees = 1 / 60.0; + break; + + case 9104: + pszUOMName = "arc-second"; + dfInDegrees = 1 / 3600.0; + break; + + case 9105: + pszUOMName = "grad"; + dfInDegrees = 180.0 / 200.0; + break; + + case 9106: + pszUOMName = "gon"; + dfInDegrees = 180.0 / 200.0; + break; + + case 9109: + pszUOMName = "microradian"; + dfInDegrees = 180.0 / (3.14159265358979 * 1000000.0); + break; + + default: + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Return to caller. */ +/* -------------------------------------------------------------------- */ + if( ppszUOMName != NULL ) + { + if( pszUOMName != NULL ) + *ppszUOMName = CPLStrdup( pszUOMName ); + else + *ppszUOMName = NULL; + } + + if( pdfInDegrees != NULL ) + *pdfInDegrees = dfInDegrees; + + return( TRUE ); +} + +/************************************************************************/ +/* EPSGGetUOMLengthInfo() */ +/* */ +/* Note: This function should eventually also know how to */ +/* lookup length aliases in the UOM_LE_ALIAS table. */ +/************************************************************************/ + +static int +EPSGGetUOMLengthInfo( int nUOMLengthCode, + char **ppszUOMName, + double * pdfInMeters ) + +{ + char **papszUnitsRecord; + char szSearchKey[24]; + int iNameField; + +#define UOM_FILENAME CSVFilename( "unit_of_measure.csv" ) + +/* -------------------------------------------------------------------- */ +/* We short cut meter to save work in the most common case. */ +/* -------------------------------------------------------------------- */ + if( nUOMLengthCode == 9001 ) + { + if( ppszUOMName != NULL ) + *ppszUOMName = CPLStrdup( "metre" ); + if( pdfInMeters != NULL ) + *pdfInMeters = 1.0; + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Search the units database for this unit. If we don't find */ +/* it return failure. */ +/* -------------------------------------------------------------------- */ + sprintf( szSearchKey, "%d", nUOMLengthCode ); + papszUnitsRecord = + CSVScanFileByName( UOM_FILENAME, "UOM_CODE", szSearchKey, CC_Integer ); + + if( papszUnitsRecord == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get the name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszUOMName != NULL ) + { + iNameField = CSVGetFileFieldId( UOM_FILENAME, "UNIT_OF_MEAS_NAME" ); + *ppszUOMName = CPLStrdup( CSLGetField(papszUnitsRecord, iNameField) ); + } + +/* -------------------------------------------------------------------- */ +/* Get the A and B factor fields, and create the multiplicative */ +/* factor. */ +/* -------------------------------------------------------------------- */ + if( pdfInMeters != NULL ) + { + int iBFactorField, iCFactorField; + + iBFactorField = CSVGetFileFieldId( UOM_FILENAME, "FACTOR_B" ); + iCFactorField = CSVGetFileFieldId( UOM_FILENAME, "FACTOR_C" ); + + if( CPLAtof(CSLGetField(papszUnitsRecord, iCFactorField)) > 0.0 ) + *pdfInMeters = CPLAtof(CSLGetField(papszUnitsRecord,iBFactorField)) + / CPLAtof(CSLGetField(papszUnitsRecord, iCFactorField)); + else + *pdfInMeters = 0.0; + } + + return( TRUE ); +} + +/************************************************************************/ +/* EPSGNegateString() */ +/************************************************************************/ + +static void EPSGNegateString(CPLString& osValue) +{ + if( osValue.compare("0") == 0 ) + return; + if( osValue[0] == '-' ) + { + osValue = osValue.substr(1); + return; + } + if( osValue[0] == '+' ) + { + osValue[0] = '-'; + return; + } + osValue = "-" + osValue; +} + +/************************************************************************/ +/* EPSGGetWGS84Transform() */ +/* */ +/* The following code attempts to find a bursa-wolf */ +/* transformation from this GeogCS to WGS84 (4326). */ +/* */ +/* Faults: */ +/* o I think there are codes other than 9603 and 9607 that */ +/* return compatible, or easily transformed parameters. */ +/* o Only the first path from the given GeogCS is checked due */ +/* to limitations in the CSV API. */ +/************************************************************************/ + +int EPSGGetWGS84Transform( int nGeogCS, std::vector& asTransform ) + +{ + int nMethodCode, iDXField, iField; + char szCode[32]; + const char *pszFilename; + char **papszLine; + +/* -------------------------------------------------------------------- */ +/* Fetch the line from the GCS table. */ +/* -------------------------------------------------------------------- */ + pszFilename = CSVFilename("gcs.override.csv"); + sprintf( szCode, "%d", nGeogCS ); + papszLine = CSVScanFileByName( pszFilename, + "COORD_REF_SYS_CODE", + szCode, CC_Integer ); + if( papszLine == NULL ) + { + pszFilename = CSVFilename("gcs.csv"); + sprintf( szCode, "%d", nGeogCS ); + papszLine = CSVScanFileByName( pszFilename, + "COORD_REF_SYS_CODE", + szCode, CC_Integer ); + } + + if( papszLine == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Verify that the method code is one of our accepted ones. */ +/* -------------------------------------------------------------------- */ + nMethodCode = + atoi(CSLGetField( papszLine, + CSVGetFileFieldId(pszFilename, + "COORD_OP_METHOD_CODE"))); + if( nMethodCode != 9603 && nMethodCode != 9607 && nMethodCode != 9606 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Fetch the transformation parameters. */ +/* -------------------------------------------------------------------- */ + iDXField = CSVGetFileFieldId(pszFilename, "DX"); + if (iDXField < 0 || CSLCount(papszLine) < iDXField + 7) + return FALSE; + + asTransform.resize(0); + for( iField = 0; iField < 7; iField++ ) + { + const char* pszValue = papszLine[iDXField+iField]; + if( pszValue[0] ) + asTransform.push_back(pszValue); + else + asTransform.push_back("0"); + } + +/* -------------------------------------------------------------------- */ +/* 9607 - coordinate frame rotation has reverse signs on the */ +/* rotational coefficients. Fix up now since we internal */ +/* operate according to method 9606 (position vector 7-parameter). */ +/* -------------------------------------------------------------------- */ + if( nMethodCode == 9607 ) + { + EPSGNegateString(asTransform[3]); + EPSGNegateString(asTransform[4]); + EPSGNegateString(asTransform[5]); + } + + return TRUE; +} + +/************************************************************************/ +/* EPSGGetPMInfo() */ +/* */ +/* Get the offset between a given prime meridian and Greenwich */ +/* in degrees. */ +/************************************************************************/ + +static int +EPSGGetPMInfo( int nPMCode, char ** ppszName, double *pdfOffset ) + +{ + char szSearchKey[24]; + int nUOMAngle; + +#define PM_FILENAME CSVFilename("prime_meridian.csv") + +/* -------------------------------------------------------------------- */ +/* Use a special short cut for Greenwich, since it is so common. */ +/* -------------------------------------------------------------------- */ + /* FIXME? Where does 7022 come from ? Let's keep it just in case */ + /* 8901 is the official current code for Greenwich */ + if( nPMCode == 7022 /* PM_Greenwich */ || nPMCode == 8901 ) + { + if( pdfOffset != NULL ) + *pdfOffset = 0.0; + if( ppszName != NULL ) + *ppszName = CPLStrdup( "Greenwich" ); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Search the database for the corresponding datum code. */ +/* -------------------------------------------------------------------- */ + sprintf( szSearchKey, "%d", nPMCode ); + + nUOMAngle = + atoi(CSVGetField( PM_FILENAME, + "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer, + "UOM_CODE" ) ); + if( nUOMAngle < 1 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get the PM offset. */ +/* -------------------------------------------------------------------- */ + if( pdfOffset != NULL ) + { + *pdfOffset = + EPSGAngleStringToDD( + CSVGetField( PM_FILENAME, + "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer, + "GREENWICH_LONGITUDE" ), + nUOMAngle ); + } + +/* -------------------------------------------------------------------- */ +/* Get the name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszName != NULL ) + *ppszName = + CPLStrdup( + CSVGetField( PM_FILENAME, + "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer, + "PRIME_MERIDIAN_NAME" )); + + return( TRUE ); +} + +/************************************************************************/ +/* EPSGGetGCSInfo() */ +/* */ +/* Fetch the datum, and prime meridian related to a particular */ +/* GCS. */ +/************************************************************************/ + +static int +EPSGGetGCSInfo( int nGCSCode, char ** ppszName, + int * pnDatum, char **ppszDatumName, + int * pnPM, int *pnEllipsoid, int *pnUOMAngle, + int * pnCoordSysCode ) + +{ + char szSearchKey[24]; + int nDatum, nPM, nUOMAngle, nEllipsoid; + const char *pszFilename; + + +/* -------------------------------------------------------------------- */ +/* Search the database for the corresponding datum code. */ +/* -------------------------------------------------------------------- */ + pszFilename = CSVFilename("gcs.override.csv"); + sprintf( szSearchKey, "%d", nGCSCode ); + + nDatum = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer, + "DATUM_CODE" ) ); + + if( nDatum < 1 ) + { + pszFilename = CSVFilename("gcs.csv"); + sprintf( szSearchKey, "%d", nGCSCode ); + + nDatum = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer, + "DATUM_CODE" ) ); + } + + if( nDatum < 1 ) + return FALSE; + + if( pnDatum != NULL ) + *pnDatum = nDatum; + +/* -------------------------------------------------------------------- */ +/* Get the PM. */ +/* -------------------------------------------------------------------- */ + nPM = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer, + "PRIME_MERIDIAN_CODE" ) ); + + if( nPM < 1 ) + return FALSE; + + if( pnPM != NULL ) + *pnPM = nPM; + +/* -------------------------------------------------------------------- */ +/* Get the Ellipsoid. */ +/* -------------------------------------------------------------------- */ + nEllipsoid = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer, + "ELLIPSOID_CODE" ) ); + + if( nEllipsoid < 1 ) + return FALSE; + + if( pnEllipsoid != NULL ) + *pnEllipsoid = nEllipsoid; + +/* -------------------------------------------------------------------- */ +/* Get the angular units. */ +/* -------------------------------------------------------------------- */ + nUOMAngle = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer, + "UOM_CODE" ) ); + + if( nUOMAngle < 1 ) + return FALSE; + + if( pnUOMAngle != NULL ) + *pnUOMAngle = nUOMAngle; + +/* -------------------------------------------------------------------- */ +/* Get the name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszName != NULL ) + *ppszName = + CPLStrdup(CSVGetField( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer, + "COORD_REF_SYS_NAME" )); + +/* -------------------------------------------------------------------- */ +/* Get the datum name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszDatumName != NULL ) + *ppszDatumName = + CPLStrdup(CSVGetField( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer, + "DATUM_NAME" )); + +/* -------------------------------------------------------------------- */ +/* Get the CoordSysCode */ +/* -------------------------------------------------------------------- */ + int nCSC; + + nCSC = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer, + "COORD_SYS_CODE" ) ); + + if( pnCoordSysCode != NULL ) + *pnCoordSysCode = nCSC; + + return( TRUE ); +} + +/************************************************************************/ +/* OSRGetEllipsoidInfo() */ +/************************************************************************/ + +/** + * Fetch info about an ellipsoid. + * + * This helper function will return ellipsoid parameters corresponding to EPSG + * code provided. Axes are always returned in meters. Semi major computed + * based on inverse flattening where that is provided. + * + * @param nCode EPSG code of the requested ellipsoid + * + * @param ppszName pointer to string where ellipsoid name will be returned. It + * is caller responsibility to free this string after using with CPLFree(). + * + * @param pdfSemiMajor pointer to variable where semi major axis will be + * returned. + * + * @param pdfInvFlattening pointer to variable where inverse flattening will + * be returned. + * + * @return OGRERR_NONE on success or an error code in case of failure. + **/ + +OGRErr +OSRGetEllipsoidInfo( int nCode, char ** ppszName, + double * pdfSemiMajor, double * pdfInvFlattening ) + +{ + char szSearchKey[24]; + double dfSemiMajor, dfToMeters = 1.0; + int nUOMLength; + +/* -------------------------------------------------------------------- */ +/* Get the semi major axis. */ +/* -------------------------------------------------------------------- */ + snprintf( szSearchKey, sizeof(szSearchKey), "%d", nCode ); + szSearchKey[sizeof(szSearchKey) - 1] = '\n'; + + dfSemiMajor = + CPLAtof(CSVGetField( CSVFilename("ellipsoid.csv" ), + "ELLIPSOID_CODE", szSearchKey, CC_Integer, + "SEMI_MAJOR_AXIS" ) ); + if( dfSemiMajor == 0.0 ) + return OGRERR_UNSUPPORTED_SRS; + +/* -------------------------------------------------------------------- */ +/* Get the translation factor into meters. */ +/* -------------------------------------------------------------------- */ + nUOMLength = atoi(CSVGetField( CSVFilename("ellipsoid.csv" ), + "ELLIPSOID_CODE", szSearchKey, CC_Integer, + "UOM_CODE" )); + EPSGGetUOMLengthInfo( nUOMLength, NULL, &dfToMeters ); + + dfSemiMajor *= dfToMeters; + + if( pdfSemiMajor != NULL ) + *pdfSemiMajor = dfSemiMajor; + +/* -------------------------------------------------------------------- */ +/* Get the semi-minor if requested. If the Semi-minor axis */ +/* isn't available, compute it based on the inverse flattening. */ +/* -------------------------------------------------------------------- */ + if( pdfInvFlattening != NULL ) + { + *pdfInvFlattening = + CPLAtof(CSVGetField( CSVFilename("ellipsoid.csv" ), + "ELLIPSOID_CODE", szSearchKey, CC_Integer, + "INV_FLATTENING" )); + + if( *pdfInvFlattening == 0.0 ) + { + double dfSemiMinor; + + dfSemiMinor = + CPLAtof(CSVGetField( CSVFilename("ellipsoid.csv" ), + "ELLIPSOID_CODE", szSearchKey, CC_Integer, + "SEMI_MINOR_AXIS" )) * dfToMeters; + + if( dfSemiMajor == 0.0 ) + *pdfInvFlattening = 0.0; + else + *pdfInvFlattening = OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor); + } + } + +/* -------------------------------------------------------------------- */ +/* Get the name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszName != NULL ) + *ppszName = + CPLStrdup(CSVGetField( CSVFilename("ellipsoid.csv" ), + "ELLIPSOID_CODE", szSearchKey, CC_Integer, + "ELLIPSOID_NAME" )); + + return OGRERR_NONE; +} + +#define CoLatConeAxis 1036 /* see #4223 */ +#define NatOriginLat 8801 +#define NatOriginLong 8802 +#define NatOriginScaleFactor 8805 +#define FalseEasting 8806 +#define FalseNorthing 8807 +#define ProjCenterLat 8811 +#define ProjCenterLong 8812 +#define Azimuth 8813 +#define AngleRectifiedToSkewedGrid 8814 +#define InitialLineScaleFactor 8815 +#define ProjCenterEasting 8816 +#define ProjCenterNorthing 8817 +#define PseudoStdParallelLat 8818 +#define PseudoStdParallelScaleFactor 8819 +#define FalseOriginLat 8821 +#define FalseOriginLong 8822 +#define StdParallel1Lat 8823 +#define StdParallel2Lat 8824 +#define FalseOriginEasting 8826 +#define FalseOriginNorthing 8827 +#define SphericalOriginLat 8828 +#define SphericalOriginLong 8829 +#define InitialLongitude 8830 +#define ZoneWidth 8831 +#define PolarLatStdParallel 8832 +#define PolarLongOrigin 8833 + +/************************************************************************/ +/* EPSGGetProjTRFInfo() */ +/* */ +/* Transform a PROJECTION_TRF_CODE into a projection method, */ +/* and a set of parameters. The parameters identify will */ +/* depend on the returned method, but they will all have been */ +/* normalized into degrees and meters. */ +/************************************************************************/ + +static int +EPSGGetProjTRFInfo( int nPCS, int * pnProjMethod, + int *panParmIds, double * padfProjParms ) + +{ + int nProjMethod, i; + double adfProjParms[7]; + char szTRFCode[16]; + CPLString osFilename; + +/* -------------------------------------------------------------------- */ +/* Get the proj method. If this fails to return a meaningful */ +/* number, then the whole function fails. */ +/* -------------------------------------------------------------------- */ + osFilename = CSVFilename( "pcs.override.csv" ); + sprintf( szTRFCode, "%d", nPCS ); + nProjMethod = + atoi( CSVGetField( osFilename, + "COORD_REF_SYS_CODE", szTRFCode, CC_Integer, + "COORD_OP_METHOD_CODE" ) ); + if( nProjMethod == 0 ) + { + osFilename = CSVFilename( "pcs.csv" ); + sprintf( szTRFCode, "%d", nPCS ); + nProjMethod = + atoi( CSVGetField( osFilename, + "COORD_REF_SYS_CODE", szTRFCode, CC_Integer, + "COORD_OP_METHOD_CODE" ) ); + } + + if( nProjMethod == 0 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get the parameters for this projection. */ +/* -------------------------------------------------------------------- */ + + for( i = 0; i < 7; i++ ) + { + char szParamUOMID[32], szParamValueID[32], szParamCodeID[32]; + char *pszValue; + int nUOM; + + sprintf( szParamCodeID, "PARAMETER_CODE_%d", i+1 ); + sprintf( szParamUOMID, "PARAMETER_UOM_%d", i+1 ); + sprintf( szParamValueID, "PARAMETER_VALUE_%d", i+1 ); + + if( panParmIds != NULL ) + panParmIds[i] = + atoi(CSVGetField( osFilename, "COORD_REF_SYS_CODE", szTRFCode, + CC_Integer, szParamCodeID )); + + nUOM = atoi(CSVGetField( osFilename, "COORD_REF_SYS_CODE", szTRFCode, + CC_Integer, szParamUOMID )); + pszValue = CPLStrdup( + CSVGetField( osFilename, "COORD_REF_SYS_CODE", szTRFCode, + CC_Integer, szParamValueID )); + + // there is a bug in the EPSG 6.2.2 database for PCS 2935 and 2936 + // such that they have foot units for the scale factor. Avoid this. + if( (panParmIds[i] == NatOriginScaleFactor + || panParmIds[i] == InitialLineScaleFactor + || panParmIds[i] == PseudoStdParallelScaleFactor) + && nUOM < 9200 ) + nUOM = 9201; + + if( nUOM >= 9100 && nUOM < 9200 ) + adfProjParms[i] = EPSGAngleStringToDD( pszValue, nUOM ); + else if( nUOM > 9000 && nUOM < 9100 ) + { + double dfInMeters; + + if( !EPSGGetUOMLengthInfo( nUOM, NULL, &dfInMeters ) ) + dfInMeters = 1.0; + adfProjParms[i] = CPLAtof(pszValue) * dfInMeters; + } + else if( EQUAL(pszValue,"") ) /* null field */ + { + adfProjParms[i] = 0.0; + } + else /* really we should consider looking up other scaling factors */ + { + if( nUOM != 9201 ) + CPLDebug( "OGR", + "Non-unity scale factor units! (UOM=%d, PCS=%d)", + nUOM, nPCS ); + adfProjParms[i] = CPLAtof(pszValue); + } + + CPLFree( pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Transfer requested data into passed variables. */ +/* -------------------------------------------------------------------- */ + if( pnProjMethod != NULL ) + *pnProjMethod = nProjMethod; + + if( padfProjParms != NULL ) + { + for( i = 0; i < 7; i++ ) + padfProjParms[i] = adfProjParms[i]; + } + + return TRUE; +} + + +/************************************************************************/ +/* EPSGGetPCSInfo() */ +/************************************************************************/ + +static int +EPSGGetPCSInfo( int nPCSCode, char **ppszEPSGName, + int *pnUOMLengthCode, int *pnUOMAngleCode, + int *pnGeogCS, int *pnTRFCode, int *pnCoordSysCode ) + +{ + char **papszRecord; + char szSearchKey[24]; + const char *pszFilename; + +/* -------------------------------------------------------------------- */ +/* Search the units database for this unit. If we don't find */ +/* it return failure. */ +/* -------------------------------------------------------------------- */ + pszFilename = CSVFilename( "pcs.csv" ); + sprintf( szSearchKey, "%d", nPCSCode ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer ); + + if( papszRecord == NULL ) + { + pszFilename = CSVFilename( "pcs.override.csv" ); + sprintf( szSearchKey, "%d", nPCSCode ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer ); + + } + + if( papszRecord == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get the name, if requested. */ +/* -------------------------------------------------------------------- */ + if( ppszEPSGName != NULL ) + { + CPLString osPCSName = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "COORD_REF_SYS_NAME")); + + const char *pszDeprecated = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "DEPRECATED") ); + + if( pszDeprecated != NULL && *pszDeprecated == '1' ) + osPCSName += " (deprecated)"; + + *ppszEPSGName = CPLStrdup(osPCSName); + } + +/* -------------------------------------------------------------------- */ +/* Get the UOM Length code, if requested. */ +/* -------------------------------------------------------------------- */ + if( pnUOMLengthCode != NULL ) + { + const char *pszValue; + + pszValue = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"UOM_CODE")); + if( atoi(pszValue) > 0 ) + *pnUOMLengthCode = atoi(pszValue); + else + *pnUOMLengthCode = 0; + } + +/* -------------------------------------------------------------------- */ +/* Get the UOM Angle code, if requested. */ +/* -------------------------------------------------------------------- */ + if( pnUOMAngleCode != NULL ) + { + const char *pszValue; + + pszValue = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"UOM_ANGLE_CODE") ); + + if( atoi(pszValue) > 0 ) + *pnUOMAngleCode = atoi(pszValue); + else + *pnUOMAngleCode = 0; + } + +/* -------------------------------------------------------------------- */ +/* Get the GeogCS (Datum with PM) code, if requested. */ +/* -------------------------------------------------------------------- */ + if( pnGeogCS != NULL ) + { + const char *pszValue; + + pszValue = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"SOURCE_GEOGCRS_CODE")); + if( atoi(pszValue) > 0 ) + *pnGeogCS = atoi(pszValue); + else + *pnGeogCS = 0; + } + +/* -------------------------------------------------------------------- */ +/* Get the GeogCS (Datum with PM) code, if requested. */ +/* -------------------------------------------------------------------- */ + if( pnTRFCode != NULL ) + { + const char *pszValue; + + pszValue = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"COORD_OP_CODE")); + + + if( atoi(pszValue) > 0 ) + *pnTRFCode = atoi(pszValue); + else + *pnTRFCode = 0; + } + +/* -------------------------------------------------------------------- */ +/* Get the CoordSysCode */ +/* -------------------------------------------------------------------- */ + int nCSC; + + nCSC = atoi(CSVGetField( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer, + "COORD_SYS_CODE" ) ); + + if( pnCoordSysCode != NULL ) + *pnCoordSysCode = nCSC; + + return TRUE; +} + +/************************************************************************/ +/* SetEPSGAxisInfo() */ +/************************************************************************/ + +static OGRErr SetEPSGAxisInfo( OGRSpatialReference *poSRS, + const char *pszTargetKey, + int nCoordSysCode ) + +{ +/* -------------------------------------------------------------------- */ +/* Special cases for well known and common values. We short */ +/* circuit these to save time doing file lookups. */ +/* -------------------------------------------------------------------- */ + // Conventional and common Easting/Northing values. + if( nCoordSysCode >= 4400 && nCoordSysCode <= 4410 ) + { + return + poSRS->SetAxes( pszTargetKey, + "Easting", OAO_East, + "Northing", OAO_North ); + } + + // Conventional and common Easting/Northing values. + if( nCoordSysCode >= 6400 && nCoordSysCode <= 6423 ) + { + return + poSRS->SetAxes( pszTargetKey, + "Latitude", OAO_North, + "Longitude", OAO_East ); + } + +/* -------------------------------------------------------------------- */ +/* Get the definition from the coordinate_axis.csv file. */ +/* -------------------------------------------------------------------- */ + char **papszRecord; + char **papszAxis1=NULL, **papszAxis2=NULL; + char szSearchKey[24]; + const char *pszFilename; + + pszFilename = CSVFilename( "coordinate_axis.csv" ); + sprintf( szSearchKey, "%d", nCoordSysCode ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_SYS_CODE", + szSearchKey, CC_Integer ); + + if( papszRecord != NULL ) + { + papszAxis1 = CSLDuplicate( papszRecord ); + papszRecord = CSVGetNextLine( pszFilename ); + if( CSLCount(papszRecord) > 0 + && EQUAL(papszRecord[0],papszAxis1[0]) ) + papszAxis2 = CSLDuplicate( papszRecord ); + } + + if( papszAxis2 == NULL ) + { + CSLDestroy( papszAxis1 ); + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find entries for COORD_SYS_CODE %d in coordinate_axis.csv", + nCoordSysCode ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Confirm the records are complete, and work out which columns */ +/* are which. */ +/* -------------------------------------------------------------------- */ + int iAxisOrientationField, iAxisAbbrevField, iAxisOrderField; + int iAxisNameCodeField; + + iAxisOrientationField = + CSVGetFileFieldId( pszFilename, "coord_axis_orientation" ); + iAxisAbbrevField = + CSVGetFileFieldId( pszFilename, "coord_axis_abbreviation" ); + iAxisOrderField = + CSVGetFileFieldId( pszFilename, "coord_axis_order" ); + iAxisNameCodeField = + CSVGetFileFieldId( pszFilename, "coord_axis_name_code" ); + + /* Check that all fields are available and that the axis_order field */ + /* is the one with highest index */ + if ( !( iAxisOrientationField >= 0 && + iAxisOrientationField < iAxisOrderField && + iAxisAbbrevField >= 0 && + iAxisAbbrevField < iAxisOrderField && + iAxisOrderField >= 0 && + iAxisNameCodeField >= 0 && + iAxisNameCodeField < iAxisOrderField ) ) + { + CSLDestroy( papszAxis1 ); + CSLDestroy( papszAxis2 ); + CPLError( CE_Failure, CPLE_AppDefined, + "coordinate_axis.csv corrupted" ); + return OGRERR_FAILURE; + } + + if( CSLCount(papszAxis1) < iAxisOrderField+1 + || CSLCount(papszAxis2) < iAxisOrderField+1 ) + { + CSLDestroy( papszAxis1 ); + CSLDestroy( papszAxis2 ); + CPLError( CE_Failure, CPLE_AppDefined, + "Axis records appear incomplete for COORD_SYS_CODE %d in coordinate_axis.csv", + nCoordSysCode ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Do we need to switch the axes around? */ +/* -------------------------------------------------------------------- */ + if( atoi(papszAxis2[iAxisOrderField]) < atoi(papszAxis1[iAxisOrderField]) ) + { + papszRecord = papszAxis1; + papszAxis1 = papszAxis2; + papszAxis2 = papszRecord; + } + +/* -------------------------------------------------------------------- */ +/* Work out axis enumeration values. */ +/* -------------------------------------------------------------------- */ + OGRAxisOrientation eOAxis1 = OAO_Other, eOAxis2 = OAO_Other; + int iAO; + static int anCodes[7] = { -1, 9907, 9909, 9906, 9908, -1, -1 }; + + for( iAO = 0; iAO < 7; iAO++ ) + { + if( EQUAL(papszAxis1[iAxisOrientationField], + OSRAxisEnumToName((OGRAxisOrientation) iAO)) ) + eOAxis1 = (OGRAxisOrientation) iAO; + if( EQUAL(papszAxis2[iAxisOrientationField], + OSRAxisEnumToName((OGRAxisOrientation) iAO)) ) + eOAxis2 = (OGRAxisOrientation) iAO; + + if( eOAxis1 == OAO_Other + && anCodes[iAO] == atoi(papszAxis1[iAxisNameCodeField]) ) + eOAxis1 = (OGRAxisOrientation) iAO; + if( eOAxis2 == OAO_Other + && anCodes[iAO] == atoi(papszAxis2[iAxisNameCodeField]) ) + eOAxis2 = (OGRAxisOrientation) iAO; + } + +/* -------------------------------------------------------------------- */ +/* Work out the axis name. We try to expand the abbreviation */ +/* to a longer name. */ +/* -------------------------------------------------------------------- */ + const char *apszAxisName[2]; + apszAxisName[0] = papszAxis1[iAxisAbbrevField]; + apszAxisName[1] = papszAxis2[iAxisAbbrevField]; + + for( iAO = 0; iAO < 2; iAO++ ) + { + if( EQUAL(apszAxisName[iAO],"N") ) + apszAxisName[iAO] = "Northing"; + else if( EQUAL(apszAxisName[iAO],"E") ) + apszAxisName[iAO] = "Easting"; + else if( EQUAL(apszAxisName[iAO],"S") ) + apszAxisName[iAO] = "Southing"; + else if( EQUAL(apszAxisName[iAO],"W") ) + apszAxisName[iAO] = "Westing"; + } + +/* -------------------------------------------------------------------- */ +/* Set the axes. */ +/* -------------------------------------------------------------------- */ + OGRErr eResult; + eResult = poSRS->SetAxes( pszTargetKey, + apszAxisName[0], eOAxis1, + apszAxisName[1], eOAxis2 ); + + CSLDestroy( papszAxis1 ); + CSLDestroy( papszAxis2 ); + + return eResult; +} + +/************************************************************************/ +/* SetEPSGGeogCS() */ +/* */ +/* FLAWS: */ +/* o Units are all hardcoded. */ +/************************************************************************/ + +static OGRErr SetEPSGGeogCS( OGRSpatialReference * poSRS, int nGeogCS ) + +{ + int nDatumCode, nPMCode, nUOMAngle, nEllipsoidCode, nCSC; + char *pszGeogCSName = NULL, *pszDatumName = NULL, *pszEllipsoidName = NULL; + char *pszPMName = NULL, *pszAngleName = NULL; + double dfPMOffset, dfSemiMajor, dfInvFlattening; + double dfAngleInDegrees, dfAngleInRadians; + + if( !EPSGGetGCSInfo( nGeogCS, &pszGeogCSName, + &nDatumCode, &pszDatumName, + &nPMCode, &nEllipsoidCode, &nUOMAngle, &nCSC ) ) + return OGRERR_UNSUPPORTED_SRS; + + if( !EPSGGetPMInfo( nPMCode, &pszPMName, &dfPMOffset ) ) + { + CPLFree( pszDatumName ); + CPLFree( pszGeogCSName ); + return OGRERR_UNSUPPORTED_SRS; + } + + OGREPSGDatumNameMassage( &pszDatumName ); + + if( OSRGetEllipsoidInfo( nEllipsoidCode, &pszEllipsoidName, + &dfSemiMajor, &dfInvFlattening ) != OGRERR_NONE ) + { + CPLFree( pszDatumName ); + CPLFree( pszGeogCSName ); + CPLFree( pszPMName ); + return OGRERR_UNSUPPORTED_SRS; + } + + if( !EPSGGetUOMAngleInfo( nUOMAngle, &pszAngleName, &dfAngleInDegrees ) ) + { + pszAngleName = CPLStrdup("degree"); + dfAngleInDegrees = 1.0; + nUOMAngle = -1; + } + + if( dfAngleInDegrees == 1.0 ) + dfAngleInRadians = CPLAtof(SRS_UA_DEGREE_CONV); + else + dfAngleInRadians = CPLAtof(SRS_UA_DEGREE_CONV) * dfAngleInDegrees; + + poSRS->SetGeogCS( pszGeogCSName, pszDatumName, + pszEllipsoidName, dfSemiMajor, dfInvFlattening, + pszPMName, dfPMOffset, + pszAngleName, dfAngleInRadians ); + + std::vector asBursaTransform; + if( EPSGGetWGS84Transform( nGeogCS, asBursaTransform ) ) + { + OGR_SRSNode *poWGS84; + + poWGS84 = new OGR_SRSNode( "TOWGS84" ); + + for( int iCoeff = 0; iCoeff < 7; iCoeff++ ) + { + poWGS84->AddChild( new OGR_SRSNode( asBursaTransform[iCoeff].c_str() ) ); + } + + poSRS->GetAttrNode( "DATUM" )->AddChild( poWGS84 ); + } + + poSRS->SetAuthority( "GEOGCS", "EPSG", nGeogCS ); + poSRS->SetAuthority( "DATUM", "EPSG", nDatumCode ); + poSRS->SetAuthority( "SPHEROID", "EPSG", nEllipsoidCode ); + poSRS->SetAuthority( "PRIMEM", "EPSG", nPMCode ); + + if( nUOMAngle > 0 ) + poSRS->SetAuthority( "GEOGCS|UNIT", "EPSG", nUOMAngle ); + + CPLFree( pszAngleName ); + CPLFree( pszDatumName ); + CPLFree( pszEllipsoidName ); + CPLFree( pszGeogCSName ); + CPLFree( pszPMName ); + +/* -------------------------------------------------------------------- */ +/* Set axes */ +/* -------------------------------------------------------------------- */ + if( nCSC > 0 ) + { + SetEPSGAxisInfo( poSRS, "GEOGCS", nCSC ); + CPLErrorReset(); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_FetchParm() */ +/* */ +/* Fetch a parameter from the parm list, based on it's EPSG */ +/* parameter code. */ +/************************************************************************/ + +static double OGR_FetchParm( double *padfProjParms, + int *panParmIds, + int nTargetId, + CPL_UNUSED double dfFromGreenwich ) +{ + int i; + double dfResult; + +/* -------------------------------------------------------------------- */ +/* Set default in meters/degrees. */ +/* -------------------------------------------------------------------- */ + switch( nTargetId ) + { + case NatOriginScaleFactor: + case InitialLineScaleFactor: + case PseudoStdParallelScaleFactor: + dfResult = 1.0; + break; + + case AngleRectifiedToSkewedGrid: + dfResult = 90.0; + break; + + default: + dfResult = 0.0; + } + +/* -------------------------------------------------------------------- */ +/* Try to find actual value in parameter list. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < 7; i++ ) + { + if( panParmIds[i] == nTargetId ) + { + dfResult = padfProjParms[i]; + break; + } + } + +/* -------------------------------------------------------------------- */ +/* EPSG longitudes are relative to greenwich. The follow code */ +/* could be used to make them relative to the prime meridian of */ +/* the associated GCS if that was appropriate. However, the */ +/* SetNormProjParm() method expects longitudes relative to */ +/* greenwich, so there is nothing for us to do. */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + switch( nTargetId ) + { + case NatOriginLong: + case ProjCenterLong: + case FalseOriginLong: + case SphericalOriginLong: + case InitialLongitude: + // Note that the EPSG values are already relative to greenwich. + // This shift is really making it relative to the provided prime + // meridian, so that when SetTM() and company the correction back + // ends up back relative to greenwich. + dfResult = dfResult + dfFromGreenwich; + break; + + default: + ; + } +#endif + + return dfResult; +} + +#define OGR_FP(x) OGR_FetchParm( adfProjParms, anParmIds, (x), \ + dfFromGreenwich ) + +/************************************************************************/ +/* SetEPSGProjCS() */ +/************************************************************************/ + +static OGRErr SetEPSGProjCS( OGRSpatialReference * poSRS, int nPCSCode ) + +{ + int nGCSCode, nUOMAngleCode, nUOMLength, nTRFCode, nProjMethod=0; + int anParmIds[7], nCSC = 0; + char *pszPCSName = NULL, *pszUOMLengthName = NULL; + double adfProjParms[7], dfInMeters, dfFromGreenwich; + OGRErr nErr; + OGR_SRSNode *poNode; + + if( !EPSGGetPCSInfo( nPCSCode, &pszPCSName, &nUOMLength, &nUOMAngleCode, + &nGCSCode, &nTRFCode, &nCSC ) ) + { + CPLFree(pszPCSName); + return OGRERR_UNSUPPORTED_SRS; + } + + poSRS->SetNode( "PROJCS", pszPCSName ); + +/* -------------------------------------------------------------------- */ +/* Set GEOGCS. */ +/* -------------------------------------------------------------------- */ + nErr = SetEPSGGeogCS( poSRS, nGCSCode ); + if( nErr != OGRERR_NONE ) + { + CPLFree(pszPCSName); + return nErr; + } + + dfFromGreenwich = poSRS->GetPrimeMeridian(); + +/* -------------------------------------------------------------------- */ +/* Set linear units. */ +/* -------------------------------------------------------------------- */ + if( !EPSGGetUOMLengthInfo( nUOMLength, &pszUOMLengthName, &dfInMeters ) ) + { + CPLFree(pszPCSName); + return OGRERR_UNSUPPORTED_SRS; + } + + poSRS->SetLinearUnits( pszUOMLengthName, dfInMeters ); + poSRS->SetAuthority( "PROJCS|UNIT", "EPSG", nUOMLength ); + + CPLFree( pszUOMLengthName ); + CPLFree( pszPCSName ); + +/* -------------------------------------------------------------------- */ +/* Set projection and parameters. */ +/* -------------------------------------------------------------------- */ + if( !EPSGGetProjTRFInfo( nPCSCode, &nProjMethod, anParmIds, adfProjParms )) + return OGRERR_UNSUPPORTED_SRS; + + switch( nProjMethod ) + { + case 9801: + case 9817: /* really LCC near conformal */ + poSRS->SetLCC1SP( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), + OGR_FP( NatOriginScaleFactor ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + case 9802: + poSRS->SetLCC( OGR_FP( StdParallel1Lat ), OGR_FP( StdParallel2Lat ), + OGR_FP( FalseOriginLat ), OGR_FP( FalseOriginLong ), + OGR_FP( FalseOriginEasting ), + OGR_FP( FalseOriginNorthing )); + break; + + case 9803: + poSRS->SetLCCB( OGR_FP( StdParallel1Lat ), OGR_FP( StdParallel2Lat ), + OGR_FP( FalseOriginLat ), OGR_FP( FalseOriginLong ), + OGR_FP( FalseOriginEasting ), + OGR_FP( FalseOriginNorthing )); + break; + + case 9805: + poSRS->SetMercator2SP( OGR_FP( StdParallel1Lat ), + OGR_FP( NatOriginLat ), OGR_FP(NatOriginLong), + OGR_FP( FalseEasting ), OGR_FP(FalseNorthing) ); + + break; + + case 9804: + case 9841: /* Mercator 1SP (Spherical) */ + case 1024: /* Google Mercator */ + poSRS->SetMercator( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), + OGR_FP( NatOriginScaleFactor ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + + if( nProjMethod == 1024 || nProjMethod == 9841 ) // override hack for google mercator. + { + poSRS->SetExtension( "PROJCS", "PROJ4", + "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs" ); + } + break; + + case 9806: + poSRS->SetCS( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + case 9807: + poSRS->SetTM( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), + OGR_FP( NatOriginScaleFactor ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + case 9808: + poSRS->SetTMSO( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), + OGR_FP( NatOriginScaleFactor ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + case 9809: + poSRS->SetOS( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), + OGR_FP( NatOriginScaleFactor ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + case 9810: + poSRS->SetPS( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), + OGR_FP( NatOriginScaleFactor ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + case 9811: + poSRS->SetNZMG( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + case 9812: + case 9813: + poSRS->SetHOM( OGR_FP( ProjCenterLat ), OGR_FP( ProjCenterLong ), + OGR_FP( Azimuth ), + OGR_FP( AngleRectifiedToSkewedGrid ), + OGR_FP( InitialLineScaleFactor ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + + poNode = poSRS->GetAttrNode( "PROJECTION" )->GetChild( 0 ); + if( nProjMethod == 9813 ) + poNode->SetValue( SRS_PT_LABORDE_OBLIQUE_MERCATOR ); + break; + + case 9814: + /* NOTE: This is no longer used! Swiss Oblique Mercator gets + ** implemented using 9815 instead. + */ + poSRS->SetSOC( OGR_FP( ProjCenterLat ), OGR_FP( ProjCenterLong ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + case 9815: + poSRS->SetHOMAC( OGR_FP( ProjCenterLat ), OGR_FP( ProjCenterLong ), + OGR_FP( Azimuth ), + OGR_FP( AngleRectifiedToSkewedGrid ), + OGR_FP( InitialLineScaleFactor ), + OGR_FP( ProjCenterEasting ), + OGR_FP( ProjCenterNorthing ) ); + break; + + case 9816: + poSRS->SetTMG( OGR_FP( FalseOriginLat ), OGR_FP( FalseOriginLong ), + OGR_FP( FalseOriginEasting ), + OGR_FP( FalseOriginNorthing ) ); + break; + + case 9818: + poSRS->SetPolyconic( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + case 1041: /* used by EPSG:5514 */ + case 9819: + { + double dfCenterLong = OGR_FP( ProjCenterLong ); + + if( dfCenterLong == 0.0 ) // See ticket #2559 + dfCenterLong = OGR_FP( PolarLongOrigin ); + + double dfAzimuth = OGR_FP( CoLatConeAxis ); // See ticket #4223 + if( dfAzimuth == 0.0 ) + dfAzimuth = OGR_FP( Azimuth ); + + poSRS->SetKrovak( OGR_FP( ProjCenterLat ), dfCenterLong, + dfAzimuth, + OGR_FP( PseudoStdParallelLat ), + OGR_FP( PseudoStdParallelScaleFactor ), + OGR_FP( ProjCenterEasting ), + OGR_FP( ProjCenterNorthing ) ); + } + break; + + case 9820: + case 1027: /* used by EPSG:2163, 3408, 3409, 3973 and 3974 */ + poSRS->SetLAEA( OGR_FP( NatOriginLat ), OGR_FP( NatOriginLong ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + case 9821: /* DEPREACTED : this is the spherical form, and really needs different + equations which give different results but PROJ.4 doesn't + seem to support the spherical form. */ + poSRS->SetLAEA( OGR_FP( SphericalOriginLat ), + OGR_FP( SphericalOriginLong ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + case 9822: /* Albers (Conic) Equal Area */ + poSRS->SetACEA( OGR_FP( StdParallel1Lat ), + OGR_FP( StdParallel2Lat ), + OGR_FP( FalseOriginLat ), + OGR_FP( FalseOriginLong ), + OGR_FP( FalseOriginEasting ), + OGR_FP( FalseOriginNorthing ) ); + break; + + case 9823: /* Equidistant Cylindrical / Plate Carre / Equirectangular */ + case 9842: + case 1028: + case 1029: + poSRS->SetEquirectangular( OGR_FP( NatOriginLat ), + OGR_FP( NatOriginLong ), + 0.0, 0.0 ); + break; + + case 9829: /* Polar Stereographic (Variant B) */ + poSRS->SetPS( OGR_FP( PolarLatStdParallel ), OGR_FP(PolarLongOrigin), + 1.0, + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + case 9834: /* Lambert Cylindrical Equal Area (Spherical) bug #2659 */ + poSRS->SetCEA( OGR_FP( StdParallel1Lat ), OGR_FP( NatOriginLong ), + OGR_FP( FalseEasting ), OGR_FP( FalseNorthing ) ); + break; + + default: + CPLDebug( "EPSG", "No WKT support for projection method %d.", + nProjMethod ); + return OGRERR_UNSUPPORTED_SRS; + } + +/* -------------------------------------------------------------------- */ +/* Set overall PCS authority code. */ +/* -------------------------------------------------------------------- */ + poSRS->SetAuthority( "PROJCS", "EPSG", nPCSCode ); + +/* -------------------------------------------------------------------- */ +/* Set axes */ +/* -------------------------------------------------------------------- */ + if( nCSC > 0 ) + { + SetEPSGAxisInfo( poSRS, "PROJCS", nCSC ); + CPLErrorReset(); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetEPSGVertCS() */ +/************************************************************************/ + +static OGRErr SetEPSGVertCS( OGRSpatialReference * poSRS, int nVertCSCode ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch record from the vertcs.csv or override file. */ +/* -------------------------------------------------------------------- */ + char **papszRecord; + char szSearchKey[24]; + const char *pszFilename; + + pszFilename = CSVFilename( "vertcs.override.csv" ); + sprintf( szSearchKey, "%d", nVertCSCode ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer ); + + if( papszRecord == NULL ) + { + pszFilename = CSVFilename( "vertcs.csv" ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer ); + + } + + if( papszRecord == NULL ) + return OGRERR_UNSUPPORTED_SRS; + + +/* -------------------------------------------------------------------- */ +/* Setup the basic VERT_CS. */ +/* -------------------------------------------------------------------- */ + poSRS->SetVertCS( + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "COORD_REF_SYS_NAME")), + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "DATUM_NAME")) ); + +/* -------------------------------------------------------------------- */ +/* Should we add a geoidgrids extension node? */ +/* -------------------------------------------------------------------- */ + const char *pszMethod = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"COORD_OP_METHOD_CODE_1")); + if( pszMethod && EQUAL(pszMethod,"9665") ) + { + const char *pszParm11 = + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"PARM_1_1")); + + poSRS->SetExtension( "VERT_CS|VERT_DATUM", "PROJ4_GRIDS", pszParm11 ); + } + +/* -------------------------------------------------------------------- */ +/* Setup the VERT_DATUM node. */ +/* -------------------------------------------------------------------- */ + poSRS->SetAuthority( "VERT_CS|VERT_DATUM", "EPSG", + atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "DATUM_CODE"))) ); + +/* -------------------------------------------------------------------- */ +/* Set linear units. */ +/* -------------------------------------------------------------------- */ + char *pszUOMLengthName = NULL; + double dfInMeters; + int nUOM_CODE = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "UOM_CODE"))); + + if( !EPSGGetUOMLengthInfo( nUOM_CODE, &pszUOMLengthName, &dfInMeters ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to lookup UOM CODE %d", nUOM_CODE ); + } + else + { + poSRS->SetTargetLinearUnits( "VERT_CS", pszUOMLengthName, dfInMeters ); + poSRS->SetAuthority( "VERT_CS|UNIT", "EPSG", nUOM_CODE ); + + CPLFree( pszUOMLengthName ); + } + +/* -------------------------------------------------------------------- */ +/* Set overall authority code. */ +/* -------------------------------------------------------------------- */ + poSRS->SetAuthority( "VERT_CS", "EPSG", nVertCSCode ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetEPSGCompdCS() */ +/************************************************************************/ + +static OGRErr SetEPSGCompdCS( OGRSpatialReference * poSRS, int nCCSCode ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch record from the compdcs.csv or override file. */ +/* -------------------------------------------------------------------- */ + char **papszRecord = NULL; + char szSearchKey[24]; + const char *pszFilename; + + sprintf( szSearchKey, "%d", nCCSCode ); + +// So far no override file needed. +// pszFilename = CSVFilename( "compdcs.override.csv" ); +// papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", +// szSearchKey, CC_Integer ); + + //if( papszRecord == NULL ) + { + pszFilename = CSVFilename( "compdcs.csv" ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer ); + + } + + if( papszRecord == NULL ) + return OGRERR_UNSUPPORTED_SRS; + +/* -------------------------------------------------------------------- */ +/* Fetch subinformation now before anything messes with the */ +/* last loaded record. */ +/* -------------------------------------------------------------------- */ + int nPCSCode = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "CMPD_HORIZCRS_CODE"))); + int nVertCSCode = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "CMPD_VERTCRS_CODE"))); + +/* -------------------------------------------------------------------- */ +/* Set the COMPD_CS node with a name. */ +/* -------------------------------------------------------------------- */ + poSRS->SetNode( "COMPD_CS", + CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "COORD_REF_SYS_NAME")) ); + +/* -------------------------------------------------------------------- */ +/* Lookup the the projected coordinate system. Can the */ +/* horizontal CRS be a GCS? */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oPCS; + OGRErr eErr; + + eErr = SetEPSGProjCS( &oPCS, nPCSCode ); + if( eErr != OGRERR_NONE ) + { + // perhaps it is a GCS? + eErr = SetEPSGGeogCS( &oPCS, nPCSCode ); + } + + if( eErr != OGRERR_NONE ) + { + return eErr; + } + + poSRS->GetRoot()->AddChild( + oPCS.GetRoot()->Clone() ); + +/* -------------------------------------------------------------------- */ +/* Lookup the VertCS. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oVertCS; + eErr = SetEPSGVertCS( &oVertCS, nVertCSCode ); + if( eErr != OGRERR_NONE ) + return eErr; + + poSRS->GetRoot()->AddChild( + oVertCS.GetRoot()->Clone() ); + +/* -------------------------------------------------------------------- */ +/* Set overall authority code. */ +/* -------------------------------------------------------------------- */ + poSRS->SetAuthority( "COMPD_CS", "EPSG", nCCSCode ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetEPSGGeocCS() */ +/************************************************************************/ + +static OGRErr SetEPSGGeocCS( OGRSpatialReference * poSRS, int nGCSCode ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch record from the geoccs.csv or override file. */ +/* -------------------------------------------------------------------- */ + char **papszRecord = NULL; + char szSearchKey[24]; + const char *pszFilename; + + sprintf( szSearchKey, "%d", nGCSCode ); + +// So far no override file needed. +// pszFilename = CSVFilename( "compdcs.override.csv" ); +// papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", +// szSearchKey, CC_Integer ); + + //if( papszRecord == NULL ) + { + pszFilename = CSVFilename( "geoccs.csv" ); + papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE", + szSearchKey, CC_Integer ); + + } + + if( papszRecord == NULL ) + return OGRERR_UNSUPPORTED_SRS; + +/* -------------------------------------------------------------------- */ +/* Set the GEOCCS node with a name. */ +/* -------------------------------------------------------------------- */ + poSRS->Clear(); + poSRS->SetGeocCS( CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "COORD_REF_SYS_NAME")) ); + +/* -------------------------------------------------------------------- */ +/* Get datum related information. */ +/* -------------------------------------------------------------------- */ + int nDatumCode, nEllipsoidCode, nPMCode; + char *pszDatumName; + + nDatumCode = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "DATUM_CODE"))); + + pszDatumName = + CPLStrdup( CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"DATUM_NAME") ) ); + OGREPSGDatumNameMassage( &pszDatumName ); + + + nEllipsoidCode = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "ELLIPSOID_CODE"))); + + nPMCode = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "PRIME_MERIDIAN_CODE"))); + +/* -------------------------------------------------------------------- */ +/* Get prime meridian information. */ +/* -------------------------------------------------------------------- */ + char *pszPMName = NULL; + double dfPMOffset = 0.0; + + if( !EPSGGetPMInfo( nPMCode, &pszPMName, &dfPMOffset ) ) + { + CPLFree( pszDatumName ); + return OGRERR_UNSUPPORTED_SRS; + } + +/* -------------------------------------------------------------------- */ +/* Get the ellipsoid information. */ +/* -------------------------------------------------------------------- */ + char *pszEllipsoidName = NULL; + double dfSemiMajor, dfInvFlattening; + + if( OSRGetEllipsoidInfo( nEllipsoidCode, &pszEllipsoidName, + &dfSemiMajor, &dfInvFlattening ) != OGRERR_NONE ) + { + CPLFree( pszDatumName ); + CPLFree( pszPMName ); + return OGRERR_UNSUPPORTED_SRS; + } + +/* -------------------------------------------------------------------- */ +/* Setup the spheroid. */ +/* -------------------------------------------------------------------- */ + char szValue[128]; + + OGR_SRSNode *poSpheroid = new OGR_SRSNode( "SPHEROID" ); + poSpheroid->AddChild( new OGR_SRSNode( pszEllipsoidName ) ); + + OGRPrintDouble( szValue, dfSemiMajor ); + poSpheroid->AddChild( new OGR_SRSNode(szValue) ); + + OGRPrintDouble( szValue, dfInvFlattening ); + poSpheroid->AddChild( new OGR_SRSNode(szValue) ); + + CPLFree( pszEllipsoidName ); + +/* -------------------------------------------------------------------- */ +/* Setup the Datum. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poDatum = new OGR_SRSNode( "DATUM" ); + poDatum->AddChild( new OGR_SRSNode(pszDatumName) ); + poDatum->AddChild( poSpheroid ); + + poSRS->GetRoot()->AddChild( poDatum ); + + CPLFree( pszDatumName ); + +/* -------------------------------------------------------------------- */ +/* Setup the prime meridian. */ +/* -------------------------------------------------------------------- */ + if( dfPMOffset == 0.0 ) + strcpy( szValue, "0" ); + else + OGRPrintDouble( szValue, dfPMOffset ); + + OGR_SRSNode *poPM = new OGR_SRSNode( "PRIMEM" ); + poPM->AddChild( new OGR_SRSNode( pszPMName ) ); + poPM->AddChild( new OGR_SRSNode( szValue ) ); + + poSRS->GetRoot()->AddChild( poPM ); + + CPLFree( pszPMName ); + +/* -------------------------------------------------------------------- */ +/* Should we try to lookup a datum transform? */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + if( EPSGGetWGS84Transform( nGeogCS, adfBursaTransform ) ) + { + OGR_SRSNode *poWGS84; + char szValue[100]; + + poWGS84 = new OGR_SRSNode( "TOWGS84" ); + + for( int iCoeff = 0; iCoeff < 7; iCoeff++ ) + { + CPLsprintf( szValue, "%g", adfBursaTransform[iCoeff] ); + poWGS84->AddChild( new OGR_SRSNode( szValue ) ); + } + + poSRS->GetAttrNode( "DATUM" )->AddChild( poWGS84 ); + } +#endif + +/* -------------------------------------------------------------------- */ +/* Set linear units. */ +/* -------------------------------------------------------------------- */ + char *pszUOMLengthName = NULL; + double dfInMeters = 1.0; + int nUOMLength = atoi(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename, + "UOM_CODE"))); + + if( !EPSGGetUOMLengthInfo( nUOMLength, &pszUOMLengthName, &dfInMeters ) ) + { + return OGRERR_UNSUPPORTED_SRS; + } + + poSRS->SetLinearUnits( pszUOMLengthName, dfInMeters ); + poSRS->SetAuthority( "GEOCCS|UNIT", "EPSG", nUOMLength ); + + CPLFree( pszUOMLengthName ); + +/* -------------------------------------------------------------------- */ +/* Set axes */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poAxis = new OGR_SRSNode( "AXIS" ); + + poAxis->AddChild( new OGR_SRSNode( "Geocentric X" ) ); + poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(OAO_Other) ) ); + + poSRS->GetRoot()->AddChild( poAxis ); + + poAxis = new OGR_SRSNode( "AXIS" ); + + poAxis->AddChild( new OGR_SRSNode( "Geocentric Y" ) ); + poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(OAO_Other) ) ); + + poSRS->GetRoot()->AddChild( poAxis ); + + poAxis = new OGR_SRSNode( "AXIS" ); + + poAxis->AddChild( new OGR_SRSNode( "Geocentric Z" ) ); + poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(OAO_North) ) ); + + poSRS->GetRoot()->AddChild( poAxis ); + +/* -------------------------------------------------------------------- */ +/* Set the authority codes. */ +/* -------------------------------------------------------------------- */ + poSRS->SetAuthority( "DATUM", "EPSG", nDatumCode ); + poSRS->SetAuthority( "SPHEROID", "EPSG", nEllipsoidCode ); + poSRS->SetAuthority( "PRIMEM", "EPSG", nPMCode ); + +// if( nUOMAngle > 0 ) +// poSRS->SetAuthority( "GEOGCS|UNIT", "EPSG", nUOMAngle ); + + poSRS->SetAuthority( "GEOCCS", "EPSG", nGCSCode ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importFromEPSG() */ +/************************************************************************/ + +/** + * \brief Initialize SRS based on EPSG GCS or PCS code. + * + * This method will initialize the spatial reference based on the + * passed in EPSG GCS or PCS code. The coordinate system definitions + * are normally read from the EPSG derived support files such as + * pcs.csv, gcs.csv, pcs.override.csv, gcs.override.csv and falling + * back to search for a PROJ.4 epsg init file or a definition in epsg.wkt. + * + * These support files are normally searched for in /usr/local/share/gdal + * or in the directory identified by the GDAL_DATA configuration option. + * See CPLFindFile() for details. + * + * This method is relatively expensive, and generally involves quite a bit + * of text file scanning. Reasonable efforts should be made to avoid calling + * it many times for the same coordinate system. + * + * This method is similar to importFromEPSGA() except that EPSG preferred + * axis ordering will *not* be applied for geographic coordinate systems. + * EPSG normally defines geographic coordinate systems to use lat/long + * contrary to typical GIS use). Since OGR 1.10.0, EPSG preferred + * axis ordering will also *not* be applied for projected coordinate systems + * that use northing/easting order. + * + * This method is the same as the C function OSRImportFromEPSG(). + * + * @param nCode a GCS or PCS code from the horizontal coordinate system table. + * + * @return OGRERR_NONE on success, or an error code on failure. + */ + +OGRErr OGRSpatialReference::importFromEPSG( int nCode ) + +{ + OGRErr eErr = importFromEPSGA( nCode ); + + // Strip any GCS axis settings found. + if( eErr == OGRERR_NONE ) + { + OGR_SRSNode *poGEOGCS = GetAttrNode( "GEOGCS" ); + + if( poGEOGCS != NULL ) + poGEOGCS->StripNodes( "AXIS" ); + + OGR_SRSNode *poPROJCS = GetAttrNode( "PROJCS" ); + if (poPROJCS != NULL && EPSGTreatsAsNorthingEasting()) + poPROJCS->StripNodes( "AXIS" ); + } + + return eErr; +} + +/************************************************************************/ +/* OSRImportFromEPSG() */ +/************************************************************************/ + +/** + * \brief Initialize SRS based on EPSG GCS or PCS code. + * + * This function is the same as OGRSpatialReference::importFromEPSG(). + */ + +OGRErr CPL_STDCALL OSRImportFromEPSG( OGRSpatialReferenceH hSRS, int nCode ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromEPSG", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromEPSG( nCode ); +} + +/************************************************************************/ +/* importFromEPSGA() */ +/************************************************************************/ + +/** + * \brief Initialize SRS based on EPSG GCS or PCS code. + * + * This method will initialize the spatial reference based on the + * passed in EPSG GCS or PCS code. + * + * This method is similar to importFromEPSG() except that EPSG preferred + * axis ordering *will* be applied for geographic and projected coordinate systems. + * EPSG normally defines geographic coordinate systems to use lat/long, and + * also there are also a few projected coordinate systems that use northing/easting + * order contrary to typical GIS use). See OGRSpatialReference::importFromEPSG() + * for more details on operation of this method. + * + * This method is the same as the C function OSRImportFromEPSGA(). + * + * @param nCode a GCS or PCS code from the horizontal coordinate system table. + * + * @return OGRERR_NONE on success, or an error code on failure. + */ + +OGRErr OGRSpatialReference::importFromEPSGA( int nCode ) + +{ + OGRErr eErr; + + bNormInfoSet = FALSE; + +/* -------------------------------------------------------------------- */ +/* Clear any existing definition. */ +/* -------------------------------------------------------------------- */ + if( GetRoot() != NULL ) + { + delete poRoot; + poRoot = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Verify that we can find the required filename(s). */ +/* -------------------------------------------------------------------- */ + if( CSVScanFileByName( CSVFilename( "gcs.csv" ), + "COORD_REF_SYS_CODE", + "4269", CC_Integer ) == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open EPSG support file %s.\n" + "Try setting the GDAL_DATA environment variable to point to the\n" + "directory containing EPSG csv files.", + CSVFilename( "gcs.csv" ) ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Try this as various sorts of objects till one works. */ +/* -------------------------------------------------------------------- */ + eErr = SetEPSGGeogCS( this, nCode ); + if( eErr == OGRERR_UNSUPPORTED_SRS ) + eErr = SetEPSGProjCS( this, nCode ); + if( eErr == OGRERR_UNSUPPORTED_SRS ) + eErr = SetEPSGVertCS( this, nCode ); + if( eErr == OGRERR_UNSUPPORTED_SRS ) + eErr = SetEPSGCompdCS( this, nCode ); + if( eErr == OGRERR_UNSUPPORTED_SRS ) + eErr = SetEPSGGeocCS( this, nCode ); + +/* -------------------------------------------------------------------- */ +/* If we get it as an unsupported code, try looking it up in */ +/* the epsg.wkt coordinate system dictionary. */ +/* -------------------------------------------------------------------- */ + if( eErr == OGRERR_UNSUPPORTED_SRS ) + { + char szCode[32]; + sprintf( szCode, "%d", nCode ); + eErr = importFromDict( "epsg.wkt", szCode ); + } + +/* -------------------------------------------------------------------- */ +/* If we get it as an unsupported code, try looking it up in */ +/* the PROJ.4 support file(s). */ +/* -------------------------------------------------------------------- */ + if( eErr == OGRERR_UNSUPPORTED_SRS ) + { + char szWrkDefn[100]; + char *pszNormalized; + + sprintf( szWrkDefn, "+init=epsg:%d", nCode ); + + pszNormalized = OCTProj4Normalize( szWrkDefn ); + + if( strstr(pszNormalized,"proj=") != NULL ) + eErr = importFromProj4( pszNormalized ); + + CPLFree( pszNormalized ); + } + +/* -------------------------------------------------------------------- */ +/* Push in authority information if we were successful, and it */ +/* is not already present. */ +/* -------------------------------------------------------------------- */ + const char *pszAuthName; + + if( IsProjected() ) + pszAuthName = GetAuthorityName( "PROJCS" ); + else + pszAuthName = GetAuthorityName( "GEOGCS" ); + + + if( eErr == OGRERR_NONE && pszAuthName == NULL ) + { + if( IsProjected() ) + SetAuthority( "PROJCS", "EPSG", nCode ); + else if( IsGeographic() ) + SetAuthority( "GEOGCS", "EPSG", nCode ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise officially issue an error message. */ +/* -------------------------------------------------------------------- */ + if( eErr == OGRERR_UNSUPPORTED_SRS ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "EPSG PCS/GCS code %d not found in EPSG support files. Is this a valid\nEPSG coordinate system?", + nCode ); + } + +/* -------------------------------------------------------------------- */ +/* To the extent possible, we want to return the results in as */ +/* close to standard OGC format as possible, so we fixup the */ +/* ordering. */ +/* -------------------------------------------------------------------- */ + if( eErr == OGRERR_NONE ) + { + eErr = FixupOrdering(); + } + + return eErr; +} + +/************************************************************************/ +/* OSRImportFromEPSGA() */ +/************************************************************************/ + +/** + * \brief Initialize SRS based on EPSG GCS or PCS code. + * + * This function is the same as OGRSpatialReference::importFromEPSGA(). + */ + +OGRErr CPL_STDCALL OSRImportFromEPSGA( OGRSpatialReferenceH hSRS, int nCode ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromEPSGA", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromEPSGA( nCode ); +} + +/************************************************************************/ +/* SetStatePlane() */ +/************************************************************************/ + +/** + * \brief Set State Plane projection definition. + * + * This will attempt to generate a complete definition of a state plane + * zone based on generating the entire SRS from the EPSG tables. If the + * EPSG tables are unavailable, it will produce a stubbed LOCAL_CS definition + * and return OGRERR_FAILURE. + * + * This method is the same as the C function OSRSetStatePlaneWithUnits(). + * + * @param nZone State plane zone number, in the USGS numbering scheme (as + * dinstinct from the Arc/Info and Erdas numbering scheme. + * + * @param bNAD83 TRUE if the NAD83 zone definition should be used or FALSE + * if the NAD27 zone definition should be used. + * + * @param pszOverrideUnitName Linear unit name to apply overriding the + * legal definition for this zone. + * + * @param dfOverrideUnit Linear unit conversion factor to apply overriding + * the legal definition for this zone. + * + * @return OGRERR_NONE on success, or OGRERR_FAILURE on failure, mostly likely + * due to the EPSG tables not being accessable. + */ + +OGRErr OGRSpatialReference::SetStatePlane( int nZone, int bNAD83, + const char *pszOverrideUnitName, + double dfOverrideUnit ) + +{ + int nAdjustedId; + int nPCSCode; + char szID[32]; + +/* -------------------------------------------------------------------- */ +/* Get the index id from stateplane.csv. */ +/* -------------------------------------------------------------------- */ + if( bNAD83 ) + nAdjustedId = nZone; + else + nAdjustedId = nZone + 10000; + +/* -------------------------------------------------------------------- */ +/* Turn this into a PCS code. We assume there will only be one */ +/* PCS corresponding to each Proj_ code since the proj code */ +/* already effectively indicates NAD27 or NAD83. */ +/* -------------------------------------------------------------------- */ + sprintf( szID, "%d", nAdjustedId ); + nPCSCode = + atoi( CSVGetField( CSVFilename( "stateplane.csv" ), + "ID", szID, CC_Integer, + "EPSG_PCS_CODE" ) ); + if( nPCSCode < 1 ) + { + char szName[128]; + static int bFailureReported = FALSE; + + if( !bFailureReported ) + { + bFailureReported = TRUE; + CPLError( CE_Warning, CPLE_OpenFailed, + "Unable to find state plane zone in stateplane.csv,\n" + "likely because the GDAL data files cannot be found. Using\n" + "incomplete definition of state plane zone.\n" ); + } + + Clear(); + if( bNAD83 ) + { + sprintf( szName, "State Plane Zone %d / NAD83", nZone ); + SetLocalCS( szName ); + SetLinearUnits( SRS_UL_METER, 1.0 ); + } + else + { + sprintf( szName, "State Plane Zone %d / NAD27", nZone ); + SetLocalCS( szName ); + SetLinearUnits( SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV) ); + } + + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Define based on a full EPSG definition of the zone. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr = importFromEPSG( nPCSCode ); + + if( eErr != OGRERR_NONE ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Apply units override if required. */ +/* */ +/* We will need to adjust the linear projection parameter to */ +/* match the provided units, and clear the authority code. */ +/* -------------------------------------------------------------------- */ + if( dfOverrideUnit != 0.0 + && fabs(dfOverrideUnit - GetLinearUnits()) > 0.0000000001 ) + { + double dfFalseEasting = GetNormProjParm( SRS_PP_FALSE_EASTING ); + double dfFalseNorthing= GetNormProjParm( SRS_PP_FALSE_NORTHING); + OGR_SRSNode *poPROJCS; + + SetLinearUnits( pszOverrideUnitName, dfOverrideUnit ); + + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + poPROJCS = GetAttrNode( "PROJCS" ); + if( poPROJCS != NULL && poPROJCS->FindChild( "AUTHORITY" ) != -1 ) + { + poPROJCS->DestroyChild( poPROJCS->FindChild( "AUTHORITY" ) ); + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetStatePlane() */ +/************************************************************************/ + +/** + * \brief Set State Plane projection definition. + * + * This function is the same as OGRSpatialReference::SetStatePlane(). + */ + +OGRErr OSRSetStatePlane( OGRSpatialReferenceH hSRS, int nZone, int bNAD83 ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetStatePlane", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetStatePlane( nZone, bNAD83 ); +} + +/************************************************************************/ +/* OSRSetStatePlaneWithUnits() */ +/************************************************************************/ + +/** + * \brief Set State Plane projection definition. + * + * This function is the same as OGRSpatialReference::SetStatePlane(). + */ + +OGRErr OSRSetStatePlaneWithUnits( OGRSpatialReferenceH hSRS, + int nZone, int bNAD83, + const char *pszOverrideUnitName, + double dfOverrideUnit ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetStatePlaneWithUnits", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetStatePlane( nZone, bNAD83, + pszOverrideUnitName, + dfOverrideUnit ); +} + +/************************************************************************/ +/* GetEPSGGeogCS() */ +/* */ +/* Try to establish what the EPSG code for this coordinate */ +/* systems GEOGCS might be. Returns -1 if no reasonable guess */ +/* can be made. */ +/* */ +/* TODO: We really need to do some name lookups. */ +/************************************************************************/ + +int OGRSpatialReference::GetEPSGGeogCS() + +{ + const char *pszAuthName = GetAuthorityName( "GEOGCS" ); + +/* -------------------------------------------------------------------- */ +/* Do we already have it? */ +/* -------------------------------------------------------------------- */ + if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") ) + return atoi(GetAuthorityCode( "GEOGCS" )); + +/* -------------------------------------------------------------------- */ +/* Get the datum and geogcs names. */ +/* -------------------------------------------------------------------- */ + const char *pszGEOGCS = GetAttrValue( "GEOGCS" ); + const char *pszDatum = GetAttrValue( "DATUM" ); + + // We can only operate on coordinate systems with a geogcs. + if( pszGEOGCS == NULL || pszDatum == NULL ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Is this a "well known" geographic coordinate system? */ +/* -------------------------------------------------------------------- */ + int bWGS, bNAD; + + bWGS = strstr(pszGEOGCS,"WGS") != NULL + || strstr(pszDatum, "WGS") + || strstr(pszGEOGCS,"World Geodetic System") + || strstr(pszGEOGCS,"World_Geodetic_System") + || strstr(pszDatum, "World Geodetic System") + || strstr(pszDatum, "World_Geodetic_System"); + + bNAD = strstr(pszGEOGCS,"NAD") != NULL + || strstr(pszDatum, "NAD") + || strstr(pszGEOGCS,"North American") + || strstr(pszGEOGCS,"North_American") + || strstr(pszDatum, "North American") + || strstr(pszDatum, "North_American"); + + if( bWGS && (strstr(pszGEOGCS,"84") || strstr(pszDatum,"84")) ) + return 4326; + + if( bWGS && (strstr(pszGEOGCS,"72") || strstr(pszDatum,"72")) ) + return 4322; + + if( bNAD && (strstr(pszGEOGCS,"83") || strstr(pszDatum,"83")) ) + return 4269; + + if( bNAD && (strstr(pszGEOGCS,"27") || strstr(pszDatum,"27")) ) + return 4267; + +/* -------------------------------------------------------------------- */ +/* If we know the datum, associate the most likely GCS with */ +/* it. */ +/* -------------------------------------------------------------------- */ + pszAuthName = GetAuthorityName( "GEOGCS|DATUM" ); + + if( pszAuthName != NULL + && EQUAL(pszAuthName,"epsg") + && GetPrimeMeridian() == 0.0 ) + { + int nDatum = atoi(GetAuthorityCode("GEOGCS|DATUM")); + + if( nDatum >= 6000 && nDatum <= 6999 ) + return nDatum - 2000; + } + + return -1; +} + +/************************************************************************/ +/* AutoIdentifyEPSG() */ +/************************************************************************/ + +/** + * \brief Set EPSG authority info if possible. + * + * This method inspects a WKT definition, and adds EPSG authority nodes + * where an aspect of the coordinate system can be easily and safely + * corresponded with an EPSG identifier. In practice, this method will + * evolve over time. In theory it can add authority nodes for any object + * (ie. spheroid, datum, GEOGCS, units, and PROJCS) that could have an + * authority node. Mostly this is useful to inserting appropriate + * PROJCS codes for common formulations (like UTM n WGS84). + * + * If it success the OGRSpatialReference is updated in place, and the + * method return OGRERR_NONE. If the method fails to identify the + * general coordinate system OGRERR_UNSUPPORTED_SRS is returned but no + * error message is posted via CPLError(). + * + * This method is the same as the C function OSRAutoIdentifyEPSG(). + * + * @return OGRERR_NONE or OGRERR_UNSUPPORTED_SRS. + */ + +OGRErr OGRSpatialReference::AutoIdentifyEPSG() + +{ +/* -------------------------------------------------------------------- */ +/* Do we have a GEOGCS node, but no authority? If so, try */ +/* guessing it. */ +/* -------------------------------------------------------------------- */ + if( (IsProjected() || IsGeographic()) + && GetAuthorityCode( "GEOGCS" ) == NULL ) + { + int nGCS = GetEPSGGeogCS(); + if( nGCS != -1 ) + SetAuthority( "GEOGCS", "EPSG", nGCS ); + } + +/* -------------------------------------------------------------------- */ +/* Is this a UTM coordinate system with a common GEOGCS? */ +/* -------------------------------------------------------------------- */ + int nZone, bNorth; + if( (nZone = GetUTMZone( &bNorth )) != 0 + && GetAuthorityCode( "PROJCS") == NULL ) + { + const char *pszAuthName, *pszAuthCode; + + pszAuthName = GetAuthorityName( "PROJCS|GEOGCS" ); + pszAuthCode = GetAuthorityCode( "PROJCS|GEOGCS" ); + + if( pszAuthName == NULL || pszAuthCode == NULL ) + { + /* don't exactly recognise datum */ + } + else if( EQUAL(pszAuthName,"EPSG") && atoi(pszAuthCode) == 4326 ) + { // WGS84 + if( bNorth ) + SetAuthority( "PROJCS", "EPSG", 32600 + nZone ); + else + SetAuthority( "PROJCS", "EPSG", 32700 + nZone ); + } + else if( EQUAL(pszAuthName,"EPSG") && atoi(pszAuthCode) == 4267 + && nZone >= 3 && nZone <= 22 && bNorth ) + SetAuthority( "PROJCS", "EPSG", 26700 + nZone ); // NAD27 + else if( EQUAL(pszAuthName,"EPSG") && atoi(pszAuthCode) == 4269 + && nZone >= 3 && nZone <= 23 && bNorth ) + SetAuthority( "PROJCS", "EPSG", 26900 + nZone ); // NAD83 + else if( EQUAL(pszAuthName,"EPSG") && atoi(pszAuthCode) == 4322 ) + { // WGS72 + if( bNorth ) + SetAuthority( "PROJCS", "EPSG", 32200 + nZone ); + else + SetAuthority( "PROJCS", "EPSG", 32300 + nZone ); + } + } + +/* -------------------------------------------------------------------- */ +/* Return. */ +/* -------------------------------------------------------------------- */ + if( IsProjected() && GetAuthorityCode("PROJCS") != NULL ) + return OGRERR_NONE; + else if( IsGeographic() && GetAuthorityCode("GEOGCS") != NULL ) + return OGRERR_NONE; + else + return OGRERR_UNSUPPORTED_SRS; +} + +/************************************************************************/ +/* OSRAutoIdentifyEPSG() */ +/************************************************************************/ + +/** + * \brief Set EPSG authority info if possible. + * + * This function is the same as OGRSpatialReference::AutoIdentifyEPSG(). + */ + +OGRErr OSRAutoIdentifyEPSG( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRAutoIdentifyEPSG", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->AutoIdentifyEPSG(); +} + +/************************************************************************/ +/* EPSGTreatsAsLatLong() */ +/************************************************************************/ + +/** + * \brief This method returns TRUE if EPSG feels this geographic coordinate + * system should be treated as having lat/long coordinate ordering. + * + * Currently this returns TRUE for all geographic coordinate systems + * with an EPSG code set, and AXIS values set defining it as lat, long. + * Note that coordinate systems with an EPSG code and no axis settings + * will be assumed to not be lat/long. + * + * FALSE will be returned for all coordinate systems that are not geographic, + * or that do not have an EPSG code set. + * + * This method is the same as the C function OSREPSGTreatsAsLatLong(). + * + * @return TRUE or FALSE. + */ + +int OGRSpatialReference::EPSGTreatsAsLatLong() + +{ + if( !IsGeographic() ) + return FALSE; + + const char *pszAuth = GetAuthorityName( "GEOGCS" ); + + if( pszAuth == NULL || !EQUAL(pszAuth,"EPSG") ) + return FALSE; + + OGR_SRSNode *poFirstAxis = GetAttrNode( "GEOGCS|AXIS" ); + + if( poFirstAxis == NULL ) + return FALSE; + + if( poFirstAxis->GetChildCount() >= 2 + && EQUAL(poFirstAxis->GetChild(1)->GetValue(),"NORTH") ) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* OSREPSGTreatsAsLatLong() */ +/************************************************************************/ + +/** + * \brief This function returns TRUE if EPSG feels this geographic coordinate + * system should be treated as having lat/long coordinate ordering. + * + * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong(). + */ + +int OSREPSGTreatsAsLatLong( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSREPSGTreatsAsLatLong", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->EPSGTreatsAsLatLong(); +} + +/************************************************************************/ +/* EPSGTreatsAsNorthingEasting() */ +/************************************************************************/ + +/** + * \brief This method returns TRUE if EPSG feels this projected coordinate + * system should be treated as having northing/easting coordinate ordering. + * + * Currently this returns TRUE for all projected coordinate systems + * with an EPSG code set, and AXIS values set defining it as northing, easting. + * + * FALSE will be returned for all coordinate systems that are not projected, + * or that do not have an EPSG code set. + * + * This method is the same as the C function EPSGTreatsAsNorthingEasting(). + * + * @return TRUE or FALSE. + * + * @since OGR 1.10.0 + */ + +int OGRSpatialReference::EPSGTreatsAsNorthingEasting() + +{ + if( !IsProjected() ) + return FALSE; + + const char *pszAuth = GetAuthorityName( "PROJCS" ); + + if( pszAuth == NULL || !EQUAL(pszAuth,"EPSG") ) + return FALSE; + + OGR_SRSNode *poFirstAxis = GetAttrNode( "PROJCS|AXIS" ); + + if( poFirstAxis == NULL ) + return FALSE; + + if( poFirstAxis->GetChildCount() >= 2 + && EQUAL(poFirstAxis->GetChild(1)->GetValue(),"NORTH") ) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* OSREPSGTreatsAsNorthingEasting() */ +/************************************************************************/ + +/** + * \brief This function returns TRUE if EPSG feels this geographic coordinate + * system should be treated as having northing/easting coordinate ordering. + * + * This function is the same as OGRSpatialReference::EPSGTreatsAsNorthingEasting(). + * + * @since OGR 1.10.0 + */ + +int OSREPSGTreatsAsNorthingEasting( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSREPSGTreatsAsNorthingEasting", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->EPSGTreatsAsNorthingEasting(); +} diff --git a/bazaar/plugin/gdal/ogr/ogr_geocoding.cpp b/bazaar/plugin/gdal/ogr/ogr_geocoding.cpp new file mode 100644 index 000000000..b29e76d4e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_geocoding.cpp @@ -0,0 +1,1569 @@ +/****************************************************************************** + * $Id: ogr_geocoding.cpp 28459 2015-02-12 13:48:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Client of geocoding service. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_http.h" +#include "cpl_multiproc.h" +#include "cpl_minixml.h" + +/* Emulation of gettimeofday() for Windows */ +#ifdef WIN32 + +#include +#include + +/* Recent mingw define struct timezone */ +#if !(defined(__GNUC__) && defined(_TIMEZONE_DEFINED)) +struct timezone +{ + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of DST correction */ +}; +#endif + +#define MICROSEC_IN_SEC 1000000 + +static +int OGR_gettimeofday(struct timeval *tv, struct timezone *tzIgnored) +{ + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + + /* In 100-nanosecond intervals since January 1, 1601 (UTC). */ + GUIntBig nVal = (((GUIntBig)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + nVal /= 10; /* to microseconds */ + /* There are 11 644 473 600 seconds between 1601 and 1970 */ + nVal -= ((GUIntBig)116444736) * 100 * MICROSEC_IN_SEC; + tv->tv_sec = (long)(nVal / MICROSEC_IN_SEC); + tv->tv_usec = (long)(nVal % MICROSEC_IN_SEC); + + return 0; +} + +#define gettimeofday OGR_gettimeofday + +#else +#include +#endif + + +#include "ogr_geocoding.h" +#include "ogr_mem.h" +#include "ogrsf_frmts.h" + +CPL_CVSID("$Id: ogr_geocoding.cpp 28459 2015-02-12 13:48:21Z rouault $"); + +struct _OGRGeocodingSessionHS +{ + char* pszCacheFilename; + char* pszGeocodingService; + char* pszEmail; + char* pszUserName; + char* pszKey; + char* pszApplication; + char* pszLanguage; + char* pszQueryTemplate; + char* pszReverseQueryTemplate; + int bReadCache; + int bWriteCache; + double dfDelayBetweenQueries; + OGRDataSource* poDS; +}; + +static CPLMutex* hMutex = NULL; +static double dfLastQueryTimeStampOSMNominatim = 0.0; +static double dfLastQueryTimeStampMapQuestNominatim = 0.0; + +#define OSM_NOMINATIM_QUERY "http://nominatim.openstreetmap.org/search?q=%s&format=xml&polygon_text=1" +#define MAPQUEST_NOMINATIM_QUERY "http://open.mapquestapi.com/nominatim/v1/search.php?q=%s&format=xml" +#define YAHOO_QUERY "http://where.yahooapis.com/geocode?q=%s" +#define GEONAMES_QUERY "http://api.geonames.org/search?q=%s&style=LONG" +#define BING_QUERY "http://dev.virtualearth.net/REST/v1/Locations?q=%s&o=xml" + +#define OSM_NOMINATIM_REVERSE_QUERY "http://nominatim.openstreetmap.org/reverse?format=xml&lat={lat}&lon={lon}" +#define MAPQUEST_NOMINATIM_REVERSE_QUERY "http://open.mapquestapi.com/nominatim/v1/reverse.php?format=xml&lat={lat}&lon={lon}" +#define YAHOO_REVERSE_QUERY "http://where.yahooapis.com/geocode?q={lat},{lon}&gflags=R" +#define GEONAMES_REVERSE_QUERY "http://api.geonames.org/findNearby?lat={lat}&lng={lon}&style=LONG" +#define BING_REVERSE_QUERY "http://dev.virtualearth.net/REST/v1/Locations/{lat},{lon}?includeEntityTypes=countryRegion&o=xml" + +#define CACHE_LAYER_NAME "ogr_geocode_cache" +#define DEFAULT_CACHE_SQLITE "ogr_geocode_cache.sqlite" +#define DEFAULT_CACHE_CSV "ogr_geocode_cache.csv" + +#define FIELD_URL "url" +#define FIELD_BLOB "blob" + +#ifdef OGR_ENABLED + +/************************************************************************/ +/* OGRGeocodeGetParameter() */ +/************************************************************************/ + +static +const char* OGRGeocodeGetParameter(char** papszOptions, const char* pszKey, + const char* pszDefaultValue) +{ + const char* pszRet = CSLFetchNameValue(papszOptions, pszKey); + if( pszRet != NULL ) + return pszRet; + + return CPLGetConfigOption(CPLSPrintf("OGR_GEOCODE_%s", pszKey), + pszDefaultValue); +} + +/************************************************************************/ +/* OGRGeocodeHasStringValidFormat() */ +/************************************************************************/ + +/* Checks that pszQueryTemplate has one and only one occurence of %s in it. */ +static +int OGRGeocodeHasStringValidFormat(const char* pszQueryTemplate) +{ + const char* pszIter = pszQueryTemplate; + int bValidFormat = TRUE; + int bFoundPctS = FALSE; + while( *pszIter != '\0' ) + { + if( *pszIter == '%' ) + { + if( pszIter[1] == '%' ) + { + pszIter ++; + } + else if( pszIter[1] == 's' ) + { + if( bFoundPctS ) + { + bValidFormat = FALSE; + break; + } + bFoundPctS = TRUE; + } + else + { + bValidFormat = FALSE; + break; + } + } + pszIter ++; + } + if( !bFoundPctS ) + bValidFormat = FALSE; + return bValidFormat; +} + +#endif /* #ifdef OGR_ENABLED */ + +/************************************************************************/ +/* OGRGeocodeCreateSession() */ +/************************************************************************/ + +/** + * \brief Creates a session handle for geocoding requests. + * + * Available papszOptions values: + *
      + *
    • "CACHE_FILE" : Defaults to "ogr_geocode_cache.sqlite" (or otherwise + * "ogr_geocode_cache.csv" if the SQLite driver isn't + * available). Might be any CSV, SQLite or PostgreSQL + * datasource. + *
    • "READ_CACHE" : "TRUE" (default) or "FALSE" + *
    • "WRITE_CACHE" : "TRUE" (default) or "FALSE" + *
    • "SERVICE": "OSM_NOMINATIM" + * (default), "MAPQUEST_NOMINATIM", + * "YAHOO", + * "GEONAMES", + * "BING" or + * other value. + * Note: "YAHOO" is no longer available as a free service. + *
    • "EMAIL": used by OSM_NOMINATIM. Optional, but recommended. + *
    • "USERNAME": used by GEONAMES. Compulsory in that case. + *
    • "KEY": used by BING. Compulsory in that case. + *
    • "APPLICATION": used to set the User-Agent MIME header. Defaults + * to GDAL/OGR version string. + *
    • "LANGUAGE": used to set the Accept-Language MIME header. Preferred + * language order for showing search results. + *
    • "DELAY": minimum delay, in second, between 2 consecutive queries. + * Defaults to 1.0. + *
    • "QUERY_TEMPLATE": URL template for GET requests. Must contain one + * and only one occurence of %%s in it. If not specified, for + * SERVICE=OSM_NOMINATIM, MAPQUEST_NOMINATIM, YAHOO, GEONAMES or BING, + * the URL template is hard-coded. + *
    • "REVERSE_QUERY_TEMPLATE": URL template for GET requests for reverse + * geocoding. Must contain one and only one occurence of {lon} and {lat} in it. + * If not specified, for SERVICE=OSM_NOMINATIM, MAPQUEST_NOMINATIM, YAHOO, + * GEONAMES or BING, the URL template is hard-coded. + *
    + * + * All the above options can also be set by defining the configuration option + * of the same name, prefixed by OGR_GEOCODE_. For example "OGR_GEOCODE_SERVICE" + * for the "SERVICE" option. + * + * @param papszOptions NULL, or a NULL-terminated list of string options. + * + * @return an handle that should be freed with OGRGeocodeDestroySession(), or NULL + * in case of failure. + * + * @since GDAL 1.10 + */ + +OGRGeocodingSessionH OGRGeocodeCreateSession(char** papszOptions) +{ +#ifdef OGR_ENABLED + OGRGeocodingSessionH hSession = + (OGRGeocodingSessionH)CPLCalloc(1, sizeof(_OGRGeocodingSessionHS)); + + const char* pszCacheFilename = OGRGeocodeGetParameter(papszOptions, + "CACHE_FILE", + DEFAULT_CACHE_SQLITE); + CPLString osExt = CPLGetExtension(pszCacheFilename); + if( !(EQUALN(pszCacheFilename, "PG:", 3) || + EQUAL(osExt, "csv") || EQUAL(osExt, "sqlite")) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Only .csv, .sqlite or PG: datasources are handled for now."); + OGRGeocodeDestroySession(hSession); + return NULL; + } + hSession->pszCacheFilename = CPLStrdup(pszCacheFilename); + + hSession->bReadCache = CSLTestBoolean( + OGRGeocodeGetParameter(papszOptions, "READ_CACHE", "TRUE")); + hSession->bWriteCache = CSLTestBoolean( + OGRGeocodeGetParameter(papszOptions, "WRITE_CACHE", "TRUE")); + + const char* pszGeocodingService = OGRGeocodeGetParameter(papszOptions, + "SERVICE", + "OSM_NOMINATIM"); + hSession->pszGeocodingService = CPLStrdup(pszGeocodingService); + + const char* pszEmail = OGRGeocodeGetParameter(papszOptions, "EMAIL", NULL); + hSession->pszEmail = pszEmail ? CPLStrdup(pszEmail) : NULL; + + const char* pszUserName = OGRGeocodeGetParameter(papszOptions, "USERNAME", NULL); + hSession->pszUserName = pszUserName ? CPLStrdup(pszUserName) : NULL; + + const char* pszKey = OGRGeocodeGetParameter(papszOptions, "KEY", NULL); + hSession->pszKey = pszKey ? CPLStrdup(pszKey) : NULL; + + if( EQUAL(pszGeocodingService, "GEONAMES") && pszUserName == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GEONAMES service requires USERNAME to be specified."); + OGRGeocodeDestroySession(hSession); + return NULL; + } + else if( EQUAL(pszGeocodingService, "BING") && pszKey == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "BING service requires KEY to be specified."); + OGRGeocodeDestroySession(hSession); + return NULL; + } + + const char* pszApplication = OGRGeocodeGetParameter(papszOptions, + "APPLICATION", + GDALVersionInfo("")); + hSession->pszApplication = CPLStrdup(pszApplication); + + const char* pszLanguage = OGRGeocodeGetParameter(papszOptions, + "LANGUAGE", + NULL); + hSession->pszLanguage = pszLanguage ? CPLStrdup(pszLanguage) : NULL; + + const char* pszDelayBetweenQueries = OGRGeocodeGetParameter(papszOptions, + "DELAY", "1.0"); + hSession->dfDelayBetweenQueries = CPLAtofM(pszDelayBetweenQueries); + + const char* pszQueryTemplateDefault = NULL; + if( EQUAL(pszGeocodingService, "OSM_NOMINATIM") ) + pszQueryTemplateDefault = OSM_NOMINATIM_QUERY; + else if( EQUAL(pszGeocodingService, "MAPQUEST_NOMINATIM") ) + pszQueryTemplateDefault = MAPQUEST_NOMINATIM_QUERY; + else if( EQUAL(pszGeocodingService, "YAHOO") ) + pszQueryTemplateDefault = YAHOO_QUERY; + else if( EQUAL(pszGeocodingService, "GEONAMES") ) + pszQueryTemplateDefault = GEONAMES_QUERY; + else if( EQUAL(pszGeocodingService, "BING") ) + pszQueryTemplateDefault = BING_QUERY; + const char* pszQueryTemplate = OGRGeocodeGetParameter(papszOptions, + "QUERY_TEMPLATE", + pszQueryTemplateDefault); + + if( pszQueryTemplate != NULL && + !OGRGeocodeHasStringValidFormat(pszQueryTemplate) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "QUERY_TEMPLATE value has an invalid format"); + OGRGeocodeDestroySession(hSession); + return NULL; + } + + hSession->pszQueryTemplate = + pszQueryTemplate ? CPLStrdup(pszQueryTemplate) : NULL; + + const char* pszReverseQueryTemplateDefault = NULL; + if( EQUAL(pszGeocodingService, "OSM_NOMINATIM") ) + pszReverseQueryTemplateDefault = OSM_NOMINATIM_REVERSE_QUERY; + else if( EQUAL(pszGeocodingService, "MAPQUEST_NOMINATIM") ) + pszReverseQueryTemplateDefault = MAPQUEST_NOMINATIM_REVERSE_QUERY; + else if( EQUAL(pszGeocodingService, "YAHOO") ) + pszReverseQueryTemplateDefault = YAHOO_REVERSE_QUERY; + else if( EQUAL(pszGeocodingService, "GEONAMES") ) + pszReverseQueryTemplateDefault = GEONAMES_REVERSE_QUERY; + else if( EQUAL(pszGeocodingService, "BING") ) + pszReverseQueryTemplateDefault = BING_REVERSE_QUERY; + const char* pszReverseQueryTemplate = OGRGeocodeGetParameter(papszOptions, + "REVERSE_QUERY_TEMPLATE", + pszReverseQueryTemplateDefault); + + if( pszReverseQueryTemplate != NULL && + (strstr(pszReverseQueryTemplate, "{lat}") == NULL || + strstr(pszReverseQueryTemplate, "{lon}") == NULL) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "REVERSE_QUERY_TEMPLATE value has an invalid format"); + OGRGeocodeDestroySession(hSession); + return NULL; + } + + hSession->pszReverseQueryTemplate = + (pszReverseQueryTemplate) ? CPLStrdup(pszReverseQueryTemplate) : NULL; + + return hSession; +#else + CPLError(CE_Failure, CPLE_NotSupported, "Requires OGR support"); + return NULL; +#endif +} + +/************************************************************************/ +/* OGRGeocodeDestroySession() */ +/************************************************************************/ + +/** + * \brief Destroys a session handle for geocoding requests. + + * @param hSession the handle to destroy. + * + * @since GDAL 1.10 + */ +void OGRGeocodeDestroySession(OGRGeocodingSessionH hSession) +{ +#ifdef OGR_ENABLED + if( hSession == NULL ) + return; + CPLFree(hSession->pszCacheFilename); + CPLFree(hSession->pszGeocodingService); + CPLFree(hSession->pszEmail); + CPLFree(hSession->pszUserName); + CPLFree(hSession->pszKey); + CPLFree(hSession->pszApplication); + CPLFree(hSession->pszLanguage); + CPLFree(hSession->pszQueryTemplate); + CPLFree(hSession->pszReverseQueryTemplate); + if( hSession->poDS ) + OGRReleaseDataSource((OGRDataSourceH) hSession->poDS); + CPLFree(hSession); +#endif +} + +#ifdef OGR_ENABLED + +/************************************************************************/ +/* OGRGeocodeGetCacheLayer() */ +/************************************************************************/ + +static OGRLayer* OGRGeocodeGetCacheLayer(OGRGeocodingSessionH hSession, + int bCreateIfNecessary, + int* pnIdxBlob) +{ + OGRDataSource* poDS = hSession->poDS; + CPLString osExt = CPLGetExtension(hSession->pszCacheFilename); + + if( poDS == NULL ) + { + if( OGRGetDriverCount() == 0 ) + OGRRegisterAll(); + + char* pszOldVal = CPLGetConfigOption("OGR_SQLITE_SYNCHRONOUS", NULL) ? + CPLStrdup(CPLGetConfigOption("OGR_SQLITE_SYNCHRONOUS", NULL)) : NULL; + CPLSetThreadLocalConfigOption("OGR_SQLITE_SYNCHRONOUS", "OFF"); + + poDS = (OGRDataSource*) OGROpen(hSession->pszCacheFilename, TRUE, NULL); + if( poDS == NULL && + EQUAL(hSession->pszCacheFilename, DEFAULT_CACHE_SQLITE) ) + { + poDS = (OGRDataSource*) OGROpen(DEFAULT_CACHE_CSV, TRUE, NULL); + if( poDS != NULL ) + { + CPLFree(hSession->pszCacheFilename); + hSession->pszCacheFilename = CPLStrdup(DEFAULT_CACHE_CSV); + CPLDebug("OGR", "Switch geocode cache file to %s", + hSession->pszCacheFilename); + osExt = "csv"; + } + } + + if( bCreateIfNecessary && poDS == NULL && + !EQUALN(hSession->pszCacheFilename, "PG:", 3) ) + { + OGRSFDriverH hDriver = OGRGetDriverByName(osExt); + if( hDriver == NULL && + EQUAL(hSession->pszCacheFilename, DEFAULT_CACHE_SQLITE) ) + { + CPLFree(hSession->pszCacheFilename); + hSession->pszCacheFilename = CPLStrdup(DEFAULT_CACHE_CSV); + CPLDebug("OGR", "Switch geocode cache file to %s", + hSession->pszCacheFilename); + osExt = "csv"; + hDriver = OGRGetDriverByName(osExt); + } + if( hDriver != NULL ) + { + char** papszOptions = NULL; + if( EQUAL(osExt, "SQLITE") ) + { + papszOptions = CSLAddNameValue(papszOptions, + "METADATA", "FALSE"); + } + + poDS = (OGRDataSource*) OGR_Dr_CreateDataSource( + hDriver, hSession->pszCacheFilename, papszOptions); + + if( poDS == NULL && + (EQUAL(osExt, "SQLITE") || EQUAL(osExt, "CSV"))) + { + CPLFree(hSession->pszCacheFilename); + hSession->pszCacheFilename = CPLStrdup( + CPLSPrintf("/vsimem/%s.%s", + CACHE_LAYER_NAME, osExt.c_str())); + CPLDebug("OGR", "Switch geocode cache file to %s", + hSession->pszCacheFilename); + poDS = (OGRDataSource*) OGR_Dr_CreateDataSource( + hDriver, hSession->pszCacheFilename, papszOptions); + } + + CSLDestroy(papszOptions); + } + } + + CPLSetThreadLocalConfigOption("OGR_SQLITE_SYNCHRONOUS", pszOldVal); + + if( poDS == NULL ) + return NULL; + + hSession->poDS = poDS; + } + + CPLPushErrorHandler(CPLQuietErrorHandler); + OGRLayer* poLayer = poDS->GetLayerByName(CACHE_LAYER_NAME); + CPLPopErrorHandler(); + + if( bCreateIfNecessary && poLayer == NULL ) + { + char** papszOptions = NULL; + if( EQUAL(osExt, "SQLITE") ) + { + papszOptions = CSLAddNameValue(papszOptions, "COMPRESS_COLUMNS", + FIELD_BLOB); + } + poLayer = poDS->CreateLayer( + CACHE_LAYER_NAME, NULL, wkbNone, papszOptions); + CSLDestroy(papszOptions); + + if( poLayer != NULL ) + { + OGRFieldDefn oFieldDefnURL(FIELD_URL, OFTString); + poLayer->CreateField(&oFieldDefnURL); + OGRFieldDefn oFieldDefnBlob(FIELD_BLOB, OFTString); + poLayer->CreateField(&oFieldDefnBlob); + if( EQUAL(osExt, "SQLITE") || + EQUALN(hSession->pszCacheFilename, "PG:", 3) ) + { + const char* pszSQL = + CPLSPrintf( "CREATE INDEX idx_%s_%s ON %s(%s)", + FIELD_URL, poLayer->GetName(), + poLayer->GetName(), FIELD_URL ); + poDS->ExecuteSQL(pszSQL, NULL, NULL); + } + } + } + + int nIdxBlob = -1; + if( poLayer == NULL || + poLayer->GetLayerDefn()->GetFieldIndex(FIELD_URL) < 0 || + (nIdxBlob = poLayer->GetLayerDefn()->GetFieldIndex(FIELD_BLOB)) < 0 ) + { + return NULL; + } + + if( pnIdxBlob ) + *pnIdxBlob = nIdxBlob; + + return poLayer; +} + +/************************************************************************/ +/* OGRGeocodeGetFromCache() */ +/************************************************************************/ + +static char* OGRGeocodeGetFromCache(OGRGeocodingSessionH hSession, + const char* pszURL) +{ + CPLMutexHolderD(&hMutex); + + int nIdxBlob = -1; + OGRLayer* poLayer = OGRGeocodeGetCacheLayer(hSession, FALSE, &nIdxBlob); + if( poLayer == NULL ) + return NULL; + + char* pszSQLEscapedURL = CPLEscapeString(pszURL, -1, CPLES_SQL); + poLayer->SetAttributeFilter(CPLSPrintf("%s='%s'", FIELD_URL, pszSQLEscapedURL)); + CPLFree(pszSQLEscapedURL); + + char* pszRet = NULL; + OGRFeature* poFeature = poLayer->GetNextFeature(); + if( poFeature != NULL ) + { + if( poFeature->IsFieldSet(nIdxBlob) ) + pszRet = CPLStrdup(poFeature->GetFieldAsString(nIdxBlob)); + OGRFeature::DestroyFeature(poFeature); + } + + return pszRet; +} + +/************************************************************************/ +/* OGRGeocodePutIntoCache() */ +/************************************************************************/ + +static int OGRGeocodePutIntoCache(OGRGeocodingSessionH hSession, + const char* pszURL, + const char* pszContent) +{ + CPLMutexHolderD(&hMutex); + + int nIdxBlob = -1; + OGRLayer* poLayer = OGRGeocodeGetCacheLayer(hSession, TRUE, &nIdxBlob); + if( poLayer == NULL ) + return FALSE; + + OGRFeature* poFeature = new OGRFeature(poLayer->GetLayerDefn()); + poFeature->SetField(FIELD_URL, pszURL); + poFeature->SetField(FIELD_BLOB, pszContent); + int bRet = poLayer->CreateFeature(poFeature) == OGRERR_NONE; + delete poFeature; + + return bRet; +} + +/************************************************************************/ +/* OGRGeocodeMakeRawLayer() */ +/************************************************************************/ + +static OGRLayerH OGRGeocodeMakeRawLayer(const char* pszContent) +{ + OGRMemLayer* poLayer = new OGRMemLayer( "result", NULL, wkbNone ); + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + OGRFieldDefn oFieldDefnRaw("raw", OFTString); + poLayer->CreateField(&oFieldDefnRaw); + OGRFeature* poFeature = new OGRFeature(poFDefn); + poFeature->SetField("raw", pszContent); + poLayer->CreateFeature(poFeature); + delete poFeature; + return (OGRLayerH) poLayer; +} + +/************************************************************************/ +/* OGRGeocodeBuildLayerNominatim() */ +/************************************************************************/ + +static OGRLayerH OGRGeocodeBuildLayerNominatim(CPLXMLNode* psSearchResults, + CPL_UNUSED const char* pszContent, + int bAddRawFeature) +{ + OGRMemLayer* poLayer = new OGRMemLayer( "place", NULL, wkbUnknown ); + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + + CPLXMLNode* psPlace = psSearchResults->psChild; + /* First iteration to add fields */ + while( psPlace != NULL ) + { + if( psPlace->eType == CXT_Element && + (strcmp(psPlace->pszValue, "place") == 0 || /* Nominatim */ + strcmp(psPlace->pszValue, "geoname") == 0 /* Geonames */) ) + { + CPLXMLNode* psChild = psPlace->psChild; + while( psChild != NULL ) + { + const char* pszName = psChild->pszValue; + if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) && + poFDefn->GetFieldIndex(pszName) < 0 && + strcmp(pszName, "geotext") != 0 ) + { + OGRFieldDefn oFieldDefn(pszName, OFTString); + if( strcmp(pszName, "place_rank") == 0 ) + { + oFieldDefn.SetType(OFTInteger); + } + else if( strcmp(pszName, "lat") == 0 ) + { + oFieldDefn.SetType(OFTReal); + } + else if( strcmp(pszName, "lon") == 0 || /* Nominatim */ + strcmp(pszName, "lng") == 0 /* Geonames */ ) + { + oFieldDefn.SetType(OFTReal); + } + poLayer->CreateField(&oFieldDefn); + } + psChild = psChild->psNext; + } + } + psPlace = psPlace->psNext; + } + + if( bAddRawFeature ) + { + OGRFieldDefn oFieldDefnRaw("raw", OFTString); + poLayer->CreateField(&oFieldDefnRaw); + } + + psPlace = psSearchResults->psChild; + while( psPlace != NULL ) + { + if( psPlace->eType == CXT_Element && + (strcmp(psPlace->pszValue, "place") == 0 || /* Nominatim */ + strcmp(psPlace->pszValue, "geoname") == 0 /* Geonames */) ) + { + int bFoundLat = FALSE, bFoundLon = FALSE; + double dfLat = 0.0, dfLon = 0.0; + + /* Iteration to fill the feature */ + OGRFeature* poFeature = new OGRFeature(poFDefn); + CPLXMLNode* psChild = psPlace->psChild; + while( psChild != NULL ) + { + int nIdx; + const char* pszName = psChild->pszValue; + const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL); + if( !(psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) ) + { + // do nothing + } + else if( (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0 ) + { + if( pszVal != NULL ) + { + poFeature->SetField(nIdx, pszVal); + if( strcmp(pszName, "lat") == 0 ) + { + bFoundLat = TRUE; + dfLat = CPLAtofM(pszVal); + } + else if( strcmp(pszName, "lon") == 0 || /* Nominatim */ + strcmp(pszName, "lng") == 0 /* Geonames */ ) + { + bFoundLon = TRUE; + dfLon = CPLAtofM(pszVal); + } + } + } + else if( strcmp(pszName, "geotext") == 0 ) + { + char* pszWKT = (char*) pszVal; + if( pszWKT != NULL ) + { + OGRGeometry* poGeometry = NULL; + OGRGeometryFactory::createFromWkt(&pszWKT, NULL, + &poGeometry); + if( poGeometry ) + poFeature->SetGeometryDirectly(poGeometry); + } + } + psChild = psChild->psNext; + } + + if( bAddRawFeature ) + { + CPLXMLNode* psOldNext = psPlace->psNext; + psPlace->psNext = NULL; + char* pszXML = CPLSerializeXMLTree(psPlace); + psPlace->psNext = psOldNext; + + poFeature->SetField("raw", pszXML); + CPLFree(pszXML); + } + + /* If we didn't found an explicit geometry, build it from */ + /* the 'lon' and 'lat' attributes. */ + if( poFeature->GetGeometryRef() == NULL && bFoundLon && bFoundLat ) + poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat)); + + poLayer->CreateFeature(poFeature); + delete poFeature; + } + psPlace = psPlace->psNext; + } + return (OGRLayerH) poLayer; +} + +/************************************************************************/ +/* OGRGeocodeReverseBuildLayerNominatim() */ +/************************************************************************/ + +static OGRLayerH OGRGeocodeReverseBuildLayerNominatim(CPLXMLNode* psReverseGeocode, + const char* pszContent, + int bAddRawFeature) +{ + CPLXMLNode* psResult = CPLGetXMLNode(psReverseGeocode, "result"); + CPLXMLNode* psAddressParts = CPLGetXMLNode(psReverseGeocode, "addressparts"); + if( psResult == NULL || psAddressParts == NULL ) + { + return NULL; + } + + OGRMemLayer* poLayer = new OGRMemLayer( "result", NULL, wkbNone ); + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + + int bFoundLat = FALSE, bFoundLon = FALSE; + double dfLat = 0.0, dfLon = 0.0; + + /* First iteration to add fields */ + CPLXMLNode* psChild = psResult->psChild; + while( psChild != NULL ) + { + const char* pszName = psChild->pszValue; + const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL); + if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) && + poFDefn->GetFieldIndex(pszName) < 0 ) + { + OGRFieldDefn oFieldDefn(pszName, OFTString); + if( strcmp(pszName, "lat") == 0 ) + { + if( pszVal != NULL ) + { + bFoundLat = TRUE; + dfLat = CPLAtofM(pszVal); + } + oFieldDefn.SetType(OFTReal); + } + else if( strcmp(pszName, "lon") == 0 ) + { + if( pszVal != NULL ) + { + bFoundLon = TRUE; + dfLon = CPLAtofM(pszVal); + } + oFieldDefn.SetType(OFTReal); + } + poLayer->CreateField(&oFieldDefn); + } + psChild = psChild->psNext; + } + + OGRFieldDefn oFieldDefn("display_name", OFTString); + poLayer->CreateField(&oFieldDefn); + + psChild = psAddressParts->psChild; + while( psChild != NULL ) + { + const char* pszName = psChild->pszValue; + if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) && + poFDefn->GetFieldIndex(pszName) < 0 ) + { + OGRFieldDefn oFieldDefn(pszName, OFTString); + poLayer->CreateField(&oFieldDefn); + } + psChild = psChild->psNext; + } + + if( bAddRawFeature ) + { + OGRFieldDefn oFieldDefnRaw("raw", OFTString); + poLayer->CreateField(&oFieldDefnRaw); + } + + /* Second iteration to fill the feature */ + OGRFeature* poFeature = new OGRFeature(poFDefn); + psChild = psResult->psChild; + while( psChild != NULL ) + { + int nIdx; + const char* pszName = psChild->pszValue; + const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL); + if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) && + (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0 ) + { + if( pszVal != NULL ) + poFeature->SetField(nIdx, pszVal); + } + psChild = psChild->psNext; + } + + const char* pszVal = CPLGetXMLValue(psResult, NULL, NULL); + if( pszVal != NULL ) + poFeature->SetField("display_name", pszVal); + + psChild = psAddressParts->psChild; + while( psChild != NULL ) + { + int nIdx; + const char* pszName = psChild->pszValue; + const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL); + if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) && + (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0 ) + { + if( pszVal != NULL ) + poFeature->SetField(nIdx, pszVal); + } + psChild = psChild->psNext; + } + + if( bAddRawFeature ) + { + poFeature->SetField("raw", pszContent); + } + + /* If we didn't found an explicit geometry, build it from */ + /* the 'lon' and 'lat' attributes. */ + if( poFeature->GetGeometryRef() == NULL && bFoundLon && bFoundLat ) + poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat)); + + poLayer->CreateFeature(poFeature); + delete poFeature; + + return (OGRLayerH) poLayer; +} + +/************************************************************************/ +/* OGRGeocodeBuildLayerYahoo() */ +/************************************************************************/ + +static OGRLayerH OGRGeocodeBuildLayerYahoo(CPLXMLNode* psResultSet, + CPL_UNUSED const char* pszContent, + int bAddRawFeature) +{ + OGRMemLayer* poLayer = new OGRMemLayer( "place", NULL, wkbPoint ); + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + + /* First iteration to add fields */ + CPLXMLNode* psPlace = psResultSet->psChild; + while( psPlace != NULL ) + { + if( psPlace->eType == CXT_Element && + strcmp(psPlace->pszValue, "Result") == 0 ) + { + CPLXMLNode* psChild = psPlace->psChild; + while( psChild != NULL ) + { + const char* pszName = psChild->pszValue; + if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) && + poFDefn->GetFieldIndex(pszName) < 0 ) + { + OGRFieldDefn oFieldDefn(pszName, OFTString); + if( strcmp(pszName, "latitude") == 0 ) + { + oFieldDefn.SetType(OFTReal); + } + else if( strcmp(pszName, "longitude") == 0 ) + { + oFieldDefn.SetType(OFTReal); + } + poLayer->CreateField(&oFieldDefn); + } + psChild = psChild->psNext; + } + } + + psPlace = psPlace->psNext; + } + + OGRFieldDefn oFieldDefnDisplayName("display_name", OFTString); + poLayer->CreateField(&oFieldDefnDisplayName); + + if( bAddRawFeature ) + { + OGRFieldDefn oFieldDefnRaw("raw", OFTString); + poLayer->CreateField(&oFieldDefnRaw); + } + + psPlace = psResultSet->psChild; + while( psPlace != NULL ) + { + if( psPlace->eType == CXT_Element && + strcmp(psPlace->pszValue, "Result") == 0 ) + { + int bFoundLat = FALSE, bFoundLon = FALSE; + double dfLat = 0.0, dfLon = 0.0; + + /* Second iteration to fill the feature */ + OGRFeature* poFeature = new OGRFeature(poFDefn); + CPLXMLNode* psChild = psPlace->psChild; + while( psChild != NULL ) + { + int nIdx; + const char* pszName = psChild->pszValue; + const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL); + if( !(psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) ) + { + // do nothing + } + else if( (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0 ) + { + if( pszVal != NULL ) + { + poFeature->SetField(nIdx, pszVal); + if( strcmp(pszName, "latitude") == 0 ) + { + bFoundLat = TRUE; + dfLat = CPLAtofM(pszVal); + } + else if( strcmp(pszName, "longitude") == 0 ) + { + bFoundLon = TRUE; + dfLon = CPLAtofM(pszVal); + } + } + } + psChild = psChild->psNext; + } + + CPLString osDisplayName; + for(int i=1;;i++) + { + int nIdx = poFDefn->GetFieldIndex(CPLSPrintf("line%d", i)); + if( nIdx < 0 ) + break; + if( poFeature->IsFieldSet(nIdx) ) + { + if( osDisplayName.size() ) + osDisplayName += ", "; + osDisplayName += poFeature->GetFieldAsString(nIdx); + } + } + poFeature->SetField("display_name", osDisplayName.c_str()); + + if( bAddRawFeature ) + { + CPLXMLNode* psOldNext = psPlace->psNext; + psPlace->psNext = NULL; + char* pszXML = CPLSerializeXMLTree(psPlace); + psPlace->psNext = psOldNext; + + poFeature->SetField("raw", pszXML); + CPLFree(pszXML); + } + + /* Build geometry from the 'lon' and 'lat' attributes. */ + if( bFoundLon && bFoundLat ) + poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat)); + + poLayer->CreateFeature(poFeature); + delete poFeature; + } + psPlace = psPlace->psNext; + } + return (OGRLayerH) poLayer; +} + +/************************************************************************/ +/* OGRGeocodeBuildLayerBing() */ +/************************************************************************/ + +static OGRLayerH OGRGeocodeBuildLayerBing (CPLXMLNode* psResponse, + CPL_UNUSED const char* pszContent, + int bAddRawFeature) +{ + CPLXMLNode* psResources = CPLGetXMLNode(psResponse, "ResourceSets.ResourceSet.Resources"); + if( psResources == NULL ) + return NULL; + + OGRMemLayer* poLayer = new OGRMemLayer( "place", NULL, wkbPoint ); + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + + /* First iteration to add fields */ + CPLXMLNode* psPlace = psResources->psChild; + while( psPlace != NULL ) + { + if( psPlace->eType == CXT_Element && + strcmp(psPlace->pszValue, "Location") == 0 ) + { + CPLXMLNode* psChild = psPlace->psChild; + while( psChild != NULL ) + { + const char* pszName = psChild->pszValue; + if( (psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) && + strcmp(pszName, "BoundingBox") != 0 && + strcmp(pszName, "GeocodePoint") != 0 && + poFDefn->GetFieldIndex(pszName) < 0 ) + { + if( psChild->psChild != NULL && + psChild->psChild->eType == CXT_Element ) + { + CPLXMLNode* psSubChild = psChild->psChild; + while( psSubChild != NULL ) + { + pszName = psSubChild->pszValue; + if( (psSubChild->eType == CXT_Element || + psSubChild->eType == CXT_Attribute) && + poFDefn->GetFieldIndex(pszName) < 0 ) + { + OGRFieldDefn oFieldDefn(pszName, OFTString); + if( strcmp(pszName, "Latitude") == 0 ) + { + oFieldDefn.SetType(OFTReal); + } + else if( strcmp(pszName, "Longitude") == 0 ) + { + oFieldDefn.SetType(OFTReal); + } + poLayer->CreateField(&oFieldDefn); + } + psSubChild = psSubChild->psNext; + } + } + else + { + OGRFieldDefn oFieldDefn(pszName, OFTString); + poLayer->CreateField(&oFieldDefn); + } + } + psChild = psChild->psNext; + } + } + psPlace = psPlace->psNext; + } + + if( bAddRawFeature ) + { + OGRFieldDefn oFieldDefnRaw("raw", OFTString); + poLayer->CreateField(&oFieldDefnRaw); + } + + /* Iteration to fill the feature */ + psPlace = psResources->psChild; + while( psPlace != NULL ) + { + if( psPlace->eType == CXT_Element && + strcmp(psPlace->pszValue, "Location") == 0 ) + { + int bFoundLat = FALSE, bFoundLon = FALSE; + double dfLat = 0.0, dfLon = 0.0; + + OGRFeature* poFeature = new OGRFeature(poFDefn); + CPLXMLNode* psChild = psPlace->psChild; + while( psChild != NULL ) + { + int nIdx; + const char* pszName = psChild->pszValue; + const char* pszVal = CPLGetXMLValue(psChild, NULL, NULL); + if( !(psChild->eType == CXT_Element || psChild->eType == CXT_Attribute) ) + { + // do nothing + } + else if( (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0 ) + { + if( pszVal != NULL ) + poFeature->SetField(nIdx, pszVal); + } + else if( strcmp(pszName, "BoundingBox") != 0 && + strcmp(pszName, "GeocodePoint") != 0 && + psChild->psChild != NULL && + psChild->psChild->eType == CXT_Element ) + { + CPLXMLNode* psSubChild = psChild->psChild; + while( psSubChild != NULL ) + { + pszName = psSubChild->pszValue; + pszVal = CPLGetXMLValue(psSubChild, NULL, NULL); + if( (psSubChild->eType == CXT_Element || + psSubChild->eType == CXT_Attribute) && + (nIdx = poFDefn->GetFieldIndex(pszName)) >= 0 ) + { + if( pszVal != NULL ) + { + poFeature->SetField(nIdx, pszVal); + if( strcmp(pszName, "Latitude") == 0 ) + { + bFoundLat = TRUE; + dfLat = CPLAtofM(pszVal); + } + else if( strcmp(pszName, "Longitude") == 0 ) + { + bFoundLon = TRUE; + dfLon = CPLAtofM(pszVal); + } + } + } + psSubChild = psSubChild->psNext; + } + } + psChild = psChild->psNext; + } + + if( bAddRawFeature ) + { + CPLXMLNode* psOldNext = psPlace->psNext; + psPlace->psNext = NULL; + char* pszXML = CPLSerializeXMLTree(psPlace); + psPlace->psNext = psOldNext; + + poFeature->SetField("raw", pszXML); + CPLFree(pszXML); + } + + /* Build geometry from the 'lon' and 'lat' attributes. */ + if( bFoundLon && bFoundLat ) + poFeature->SetGeometryDirectly(new OGRPoint(dfLon, dfLat)); + + poLayer->CreateFeature(poFeature); + delete poFeature; + } + psPlace = psPlace->psNext; + } + return (OGRLayerH) poLayer; +} + +/************************************************************************/ +/* OGRGeocodeBuildLayer() */ +/************************************************************************/ + +static OGRLayerH OGRGeocodeBuildLayer(const char* pszContent, + int bAddRawFeature) +{ + OGRLayerH hLayer = NULL; + CPLXMLNode* psRoot = CPLParseXMLString( pszContent ); + if( psRoot != NULL ) + { + CPLXMLNode* psSearchResults; + CPLXMLNode* psReverseGeocode; + CPLXMLNode* psGeonames; + CPLXMLNode* psResultSet; + CPLXMLNode* psResponse; + if( (psSearchResults = + CPLSearchXMLNode(psRoot, "=searchresults")) != NULL ) + hLayer = OGRGeocodeBuildLayerNominatim(psSearchResults, + pszContent, + bAddRawFeature); + else if( (psReverseGeocode = + CPLSearchXMLNode(psRoot, "=reversegeocode")) != NULL ) + hLayer = OGRGeocodeReverseBuildLayerNominatim(psReverseGeocode, + pszContent, + bAddRawFeature); + else if( (psGeonames = + CPLSearchXMLNode(psRoot, "=geonames")) != NULL ) + hLayer = OGRGeocodeBuildLayerNominatim(psGeonames, + pszContent, + bAddRawFeature); + else if( (psResultSet = + CPLSearchXMLNode(psRoot, "=ResultSet")) != NULL ) + hLayer = OGRGeocodeBuildLayerYahoo(psResultSet, + pszContent, + bAddRawFeature); + else if( (psResponse = + CPLSearchXMLNode(psRoot, "=Response")) != NULL ) + hLayer = OGRGeocodeBuildLayerBing (psResponse, + pszContent, + bAddRawFeature); + CPLDestroyXMLNode( psRoot ); + } + if( hLayer == NULL && bAddRawFeature ) + hLayer = OGRGeocodeMakeRawLayer(pszContent); + return hLayer; +} + +/************************************************************************/ +/* OGRGeocodeCommon() */ +/************************************************************************/ + +static OGRLayerH OGRGeocodeCommon(OGRGeocodingSessionH hSession, + CPLString osURL, + char** papszOptions) +{ + /* Only documented to work with OSM Nominatim. */ + if( hSession->pszLanguage != NULL ) + { + osURL += "&accept-language="; + osURL += hSession->pszLanguage; + } + + const char* pszExtraQueryParameters = OGRGeocodeGetParameter( + papszOptions, "EXTRA_QUERY_PARAMETERS", NULL); + if( pszExtraQueryParameters != NULL ) + { + osURL += "&"; + osURL += pszExtraQueryParameters; + } + + CPLString osURLWithEmail = osURL; + if( EQUAL(hSession->pszGeocodingService, "OSM_NOMINATIM") && + hSession->pszEmail != NULL ) + { + char* pszEscapedEmail = CPLEscapeString(hSession->pszEmail, + -1, CPLES_URL); + osURLWithEmail = osURL + "&email=" + pszEscapedEmail; + CPLFree(pszEscapedEmail); + } + else if( EQUAL(hSession->pszGeocodingService, "GEONAMES") && + hSession->pszUserName != NULL ) + { + char* pszEscaped = CPLEscapeString(hSession->pszUserName, + -1, CPLES_URL); + osURLWithEmail = osURL + "&username=" + pszEscaped; + CPLFree(pszEscaped); + } + else if( EQUAL(hSession->pszGeocodingService, "BING") && + hSession->pszKey != NULL ) + { + char* pszEscaped = CPLEscapeString(hSession->pszKey, + -1, CPLES_URL); + osURLWithEmail = osURL + "&key=" + pszEscaped; + CPLFree(pszEscaped); + } + + int bAddRawFeature = + CSLTestBoolean(OGRGeocodeGetParameter(papszOptions, "RAW_FEATURE", "NO")); + + OGRLayerH hLayer = NULL; + + char* pszCachedResult = NULL; + if( hSession->bReadCache ) + pszCachedResult = OGRGeocodeGetFromCache(hSession, osURL); + if( pszCachedResult == NULL ) + { + CPLHTTPResult* psResult; + + double* pdfLastQueryTime = NULL; + if( EQUAL(hSession->pszGeocodingService, "OSM_NOMINATIM") ) + pdfLastQueryTime = &dfLastQueryTimeStampOSMNominatim; + else if( EQUAL(hSession->pszGeocodingService, "MAPQUEST_NOMINATIM") ) + pdfLastQueryTime = &dfLastQueryTimeStampMapQuestNominatim; + + char** papszHTTPOptions = NULL; + CPLString osHeaders; + osHeaders = "User-Agent: "; + osHeaders += hSession->pszApplication; + if( hSession->pszLanguage != NULL ) + { + osHeaders += "\r\nAccept-Language: "; + osHeaders += hSession->pszLanguage; + } + papszHTTPOptions = CSLAddNameValue(papszHTTPOptions, "HEADERS", + osHeaders.c_str()); + + if( pdfLastQueryTime != NULL ) + { + CPLMutexHolderD(&hMutex); + struct timeval tv; + + gettimeofday(&tv, NULL); + double dfCurrentTime = tv.tv_sec + tv.tv_usec / 1e6; + if( dfCurrentTime < *pdfLastQueryTime + + hSession->dfDelayBetweenQueries ) + { + CPLSleep(*pdfLastQueryTime + hSession->dfDelayBetweenQueries - + dfCurrentTime); + } + + psResult = CPLHTTPFetch( osURLWithEmail, papszHTTPOptions ); + + gettimeofday(&tv, NULL); + *pdfLastQueryTime = tv.tv_sec + tv.tv_usec / 1e6; + } + else + psResult = CPLHTTPFetch( osURLWithEmail, papszHTTPOptions ); + + CSLDestroy(papszHTTPOptions); + papszHTTPOptions = NULL; + + if( psResult == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Query '%s' failed", osURLWithEmail.c_str()); + } + else + { + const char* pszResult = (const char*) psResult->pabyData; + if( pszResult != NULL ) + { + if( hSession->bWriteCache ) + OGRGeocodePutIntoCache(hSession, osURL, pszResult); + hLayer = OGRGeocodeBuildLayer(pszResult, bAddRawFeature); + } + CPLHTTPDestroyResult(psResult); + } + } + else + { + hLayer = OGRGeocodeBuildLayer(pszCachedResult, bAddRawFeature); + CPLFree(pszCachedResult); + } + + return hLayer; +} + +#endif /* #ifdef OGR_ENABLED */ + +/************************************************************************/ +/* OGRGeocode() */ +/************************************************************************/ + +/** + * \brief Runs a geocoding request. + * + * If the result is not found in cache, a GET request will be sent to resolve + * the query. + * + * Note: most online services have Term of Uses. You are kindly requested + * to read and follow them. For the OpenStreetMap Nominatim service, this + * implementation will make sure that no more than one request is sent by + * second, but there might be other restrictions that you must follow by other + * means. + * + * In case of success, the return of this function is a OGR layer that contain + * zero, one or several features matching the query. Note that the geometry of the + * features is not necessarily a point. The returned layer must be freed with + * OGRGeocodeFreeResult(). + * + * Note: this function is also available as the SQL + * ogr_geocode() + * function of the SQL SQLite dialect. + * + * The list of recognized options is : + *
      + *
    • ADDRESSDETAILS=0 or 1: Include a breakdown of the address into elements + * Defaults to 1. (Known to work with OSM and MapQuest Nominatim) + *
    • COUNTRYCODES=code1,code2,...codeN: Limit search results to a specific + * country (or a list of countries). The codes must fellow ISO 3166-1, i.e. + * gb for United Kingdom, de for Germany, etc.. (Known to work with OSM and MapQuest Nominatim) + *
    • LIMIT=number: the number of records to return. Unlimited if not specified. + * (Known to work with OSM and MapQuest Nominatim) + *
    • RAW_FEATURE=YES: to specify that a 'raw' field must be added to the returned + * feature with the raw XML content. + *
    • EXTRA_QUERY_PARAMETERS=params: additionnal parameters for the GET request. + *
    + * + * @param hSession the geocoding session handle. + * @param pszQuery the string to geocode. + * @param papszStructuredQuery unused for now. Must be NULL. + * @param papszOptions a list of options or NULL. + * + * @return a OGR layer with the result(s), or NULL in case of error. + * The returned layer must be freed with OGRGeocodeFreeResult(). + * + * @since GDAL 1.10 + */ +OGRLayerH OGRGeocode(OGRGeocodingSessionH hSession, + const char* pszQuery, + char** papszStructuredQuery, + char** papszOptions) +{ +#ifdef OGR_ENABLED + VALIDATE_POINTER1( hSession, "OGRGeocode", NULL ); + if( (pszQuery == NULL && papszStructuredQuery == NULL) || + (pszQuery != NULL && papszStructuredQuery != NULL) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Only one of pszQuery or papszStructuredQuery must be set."); + return NULL; + } + + if( papszStructuredQuery != NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "papszStructuredQuery not yet supported."); + return NULL; + } + + if( hSession->pszQueryTemplate == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "QUERY_TEMPLATE parameter not defined"); + return NULL; + } + + char* pszEscapedQuery = CPLEscapeString(pszQuery, -1, CPLES_URL); + CPLString osURL = CPLSPrintf(hSession->pszQueryTemplate, pszEscapedQuery); + CPLFree(pszEscapedQuery); + + if( EQUAL(hSession->pszGeocodingService, "OSM_NOMINATIM") || + EQUAL(hSession->pszGeocodingService, "MAPQUEST_NOMINATIM") ) + { + const char* pszAddressDetails = OGRGeocodeGetParameter(papszOptions, "ADDRESSDETAILS", "1"); + osURL += "&addressdetails="; + osURL += pszAddressDetails; + + const char* pszCountryCodes = OGRGeocodeGetParameter(papszOptions, "COUNTRYCODES", NULL); + if( pszCountryCodes != NULL ) + { + osURL += "&countrycodes="; + osURL += pszCountryCodes; + } + + const char* pszLimit = OGRGeocodeGetParameter(papszOptions, "LIMIT", NULL); + if( pszLimit != NULL && *pszLimit != '\0' ) + { + osURL += "&limit="; + osURL += pszLimit; + } + } + + return OGRGeocodeCommon(hSession, osURL, papszOptions); +#else + return NULL; +#endif +} + +#ifdef OGR_ENABLED + +/************************************************************************/ +/* OGRGeocodeReverseSubstitute() */ +/************************************************************************/ + +static CPLString OGRGeocodeReverseSubstitute(CPLString osURL, + double dfLon, double dfLat) +{ + size_t iPos = osURL.find("{lon}"); + if( iPos != std::string::npos ) + { + CPLString osEnd(osURL.substr(iPos + 5)); + osURL = osURL.substr(0,iPos); + osURL += CPLSPrintf("%.8f", dfLon); + osURL += osEnd; + } + + iPos = osURL.find("{lat}"); + if( iPos != std::string::npos ) + { + CPLString osEnd(osURL.substr(iPos + 5)); + osURL = osURL.substr(0,iPos); + osURL += CPLSPrintf("%.8f", dfLat); + osURL += osEnd; + } + + return osURL; +} + +#endif /* #ifdef OGR_ENABLED */ + +/************************************************************************/ +/* OGRGeocodeReverse() */ +/************************************************************************/ + +/** + * \brief Runs a reverse geocoding request. + * + * If the result is not found in cache, a GET request will be sent to resolve + * the query. + * + * Note: most online services have Term of Uses. You are kindly requested + * to read and follow them. For the OpenStreetMap Nominatim service, this + * implementation will make sure that no more than one request is sent by + * second, but there might be other restrictions that you must follow by other + * means. + * + * In case of success, the return of this function is a OGR layer that contain + * zero, one or several features matching the query. The returned layer must be freed with + * OGRGeocodeFreeResult(). + * + * Note: this function is also available as the SQL + * ogr_geocode_reverse() + * function of the SQL SQLite dialect. + * + * The list of recognized options is : + *
      + *
    • ZOOM=a_level: to query a specific zoom level. Only understood by the OSM Nominatim service. + *
    • RAW_FEATURE=YES: to specify that a 'raw' field must be added to the returned + * feature with the raw XML content. + *
    • EXTRA_QUERY_PARAMETERS=params: additionnal parameters for the GET request + * for reverse geocoding. + *
    + * + * @param hSession the geocoding session handle. + * @param dfLon the longitude. + * @param dfLat the latitude. + * @param papszOptions a list of options or NULL. + * + * @return a OGR layer with the result(s), or NULL in case of error. + * The returned layer must be freed with OGRGeocodeFreeResult(). + * + * @since GDAL 1.10 + */ +OGRLayerH OGRGeocodeReverse(OGRGeocodingSessionH hSession, + double dfLon, double dfLat, + char** papszOptions) +{ +#ifdef OGR_ENABLED + VALIDATE_POINTER1( hSession, "OGRGeocodeReverse", NULL ); + + if( hSession->pszReverseQueryTemplate == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "REVERSE_QUERY_TEMPLATE parameter not defined"); + return NULL; + } + + CPLString osURL = hSession->pszReverseQueryTemplate; + osURL = OGRGeocodeReverseSubstitute(osURL, dfLon, dfLat); + + if( EQUAL(hSession->pszGeocodingService, "OSM_NOMINATIM") ) + { + const char* pszZoomLevel = OGRGeocodeGetParameter(papszOptions, "ZOOM", NULL); + if( pszZoomLevel != NULL ) + { + osURL = osURL + "&zoom=" + pszZoomLevel; + } + } + + return OGRGeocodeCommon(hSession, osURL, papszOptions); +#else + return NULL; +#endif +} + +/************************************************************************/ +/* OGRGeocodeFreeResult() */ +/************************************************************************/ + +/** + * \brief Destroys the result of a geocoding request. + * + * @param hLayer the layer returned by OGRGeocode() or OGRGeocodeReverse() + * to destroy. + * + * @since GDAL 1.10 + */ +void OGRGeocodeFreeResult(OGRLayerH hLayer) +{ +#ifdef OGR_ENABLED + delete (OGRLayer*) hLayer; +#endif +} diff --git a/bazaar/plugin/gdal/ogr/ogr_geocoding.h b/bazaar/plugin/gdal/ogr/ogr_geocoding.h new file mode 100644 index 000000000..2e5125afe --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_geocoding.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * $Id: ogr_geocoding.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Client of geocoding service. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GEOCODING_H_INCLUDED +#define _OGR_GEOCODING_H_INCLUDED + +#include "cpl_port.h" +#include "ogr_api.h" + +/** + * \file ogr_geocoding.h + * + * C API for geocoding client. + */ + +CPL_C_START + +typedef struct _OGRGeocodingSessionHS *OGRGeocodingSessionH; + +OGRGeocodingSessionH CPL_DLL OGRGeocodeCreateSession(char** papszOptions); + +void CPL_DLL OGRGeocodeDestroySession(OGRGeocodingSessionH hSession); + +OGRLayerH CPL_DLL OGRGeocode(OGRGeocodingSessionH hSession, + const char* pszQuery, + char** papszStructuredQuery, + char** papszOptions); + +OGRLayerH CPL_DLL OGRGeocodeReverse(OGRGeocodingSessionH hSession, + double dfLon, double dfLat, + char** papszOptions); + +void CPL_DLL OGRGeocodeFreeResult(OGRLayerH hLayer); + +CPL_C_END + +#endif // _OGR_GEOCODING_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogr_geometry.h b/bazaar/plugin/gdal/ogr/ogr_geometry.h new file mode 100644 index 000000000..cfbcfc1a7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_geometry.h @@ -0,0 +1,1292 @@ +/****************************************************************************** + * $Id: ogr_geometry.h 28123 2014-12-10 19:27:55Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Classes for manipulating simple features that is not specific + * to a particular interface technology. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GEOMETRY_H_INCLUDED +#define _OGR_GEOMETRY_H_INCLUDED + +#include "ogr_core.h" +#include "ogr_spatialref.h" + +/** + * \file ogr_geometry.h + * + * Simple feature geometry classes. + */ + +/** + * Simple container for a position. + */ +class OGRRawPoint +{ + public: + OGRRawPoint() + { + x = y = 0.0; + } + + OGRRawPoint(double x, double y) : x(x), y(y) {} + double x; + double y; +}; + +typedef struct GEOSGeom_t *GEOSGeom; +typedef struct GEOSContextHandle_HS *GEOSContextHandle_t; + +class OGRPoint; +class OGRCurve; +class OGRCompoundCurve; +class OGRLinearRing; +class OGRLineString; +class OGRSurface; +class OGRCurvePolygon; +class OGRPolygon; +class OGRMultiSurface; +class OGRMultiPolygon; +class OGRMultiCurve; +class OGRMultiLineString; + +typedef OGRLineString* (*OGRCurveCasterToLineString)(OGRCurve*); +typedef OGRLinearRing* (*OGRCurveCasterToLinearRing)(OGRCurve*); + +typedef OGRPolygon* (*OGRSurfaceCasterToPolygon)(OGRSurface*); +typedef OGRCurvePolygon* (*OGRSurfaceCasterToCurvePolygon)(OGRSurface*); + +/************************************************************************/ +/* OGRGeometry */ +/************************************************************************/ + +/** + * Abstract base class for all geometry classes. + * + * Some spatial analysis methods require that OGR is built on the GEOS library + * to work properly. The precise meaning of methods that describe spatial relationships + * between geometries is described in the SFCOM, or other simple features interface + * specifications, like "OpenGIS® Implementation Specification for + * Geographic information - Simple feature access - Part 1: Common architecture" + * (OGC 06-103r4) + * + * In GDAL 2.0, the hierarchy of classes has been extended with + * + * (working draft) ISO SQL/MM Part 3 (ISO/IEC 13249-3) curve geometries : + * CIRCULARSTRING (OGRCircularString), COMPOUNDCURVE (OGRCompoundCurve), + * CURVEPOLYGON (OGRCurvePolygon), MULTICURVE (OGRMultiCurve) and MULTISURFACE (OGRMultiSurface). + * + */ + +class CPL_DLL OGRGeometry +{ + private: + OGRSpatialReference * poSRS; // may be NULL + + protected: + friend class OGRCurveCollection; + + int nCoordDimension; + + OGRErr importPreambuleFromWkt( char ** ppszInput, + int* pbHasZ, int* pbHasM ); + OGRErr importCurveCollectionFromWkt( char ** ppszInput, + int bAllowEmptyComponent, + int bAllowLineString, + int bAllowCurve, + int bAllowCompoundCurve, + OGRErr (*pfnAddCurveDirectly)(OGRGeometry* poSelf, OGRCurve* poCurve) ); + OGRErr importPreambuleFromWkb( unsigned char * pabyData, + int nSize, + OGRwkbByteOrder& eByteOrder, + OGRBoolean& b3D, + OGRwkbVariant eWkbVariant ); + OGRErr importPreambuleOfCollectionFromWkb( + unsigned char * pabyData, + int& nSize, + int& nDataOffset, + OGRwkbByteOrder& eByteOrder, + int nMinSubGeomSize, + int& nGeomCount, + OGRwkbVariant eWkbVariant ); + public: + OGRGeometry(); + virtual ~OGRGeometry(); + + // standard IGeometry + virtual int getDimension() const = 0; + virtual int getCoordinateDimension() const; + virtual OGRBoolean IsEmpty() const = 0; + virtual OGRBoolean IsValid() const; + virtual OGRBoolean IsSimple() const; + virtual OGRBoolean IsRing() const; + virtual void empty() = 0; + virtual OGRGeometry *clone() const = 0; + virtual void getEnvelope( OGREnvelope * psEnvelope ) const = 0; + virtual void getEnvelope( OGREnvelope3D * psEnvelope ) const = 0; + + // IWks Interface + virtual int WkbSize() const = 0; + virtual OGRErr importFromWkb( unsigned char *, int=-1, OGRwkbVariant=wkbVariantOldOgc )=0; + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOldOgc ) const = 0; + virtual OGRErr importFromWkt( char ** ppszInput ) = 0; + virtual OGRErr exportToWkt( char ** ppszDstText, OGRwkbVariant=wkbVariantOldOgc ) const = 0; + + // non-standard + virtual OGRwkbGeometryType getGeometryType() const = 0; + OGRwkbGeometryType getIsoGeometryType() const; + virtual const char *getGeometryName() const = 0; + virtual void dumpReadable( FILE *, const char * = NULL, char** papszOptions = NULL ) const; + virtual void flattenTo2D() = 0; + virtual char * exportToGML( const char* const * papszOptions = NULL ) const; + virtual char * exportToKML() const; + virtual char * exportToJson() const; + + static GEOSContextHandle_t createGEOSContext(); + static void freeGEOSContext(GEOSContextHandle_t hGEOSCtxt); + virtual GEOSGeom exportToGEOS(GEOSContextHandle_t hGEOSCtxt) const; + virtual OGRBoolean hasCurveGeometry(int bLookForNonLinear = FALSE) const; + virtual OGRGeometry* getCurveGeometry(const char* const* papszOptions = NULL) const; + virtual OGRGeometry* getLinearGeometry(double dfMaxAngleStepSizeDegrees = 0, + const char* const* papszOptions = NULL) const; + + virtual void closeRings(); + + virtual void setCoordinateDimension( int nDimension ); + + void assignSpatialReference( OGRSpatialReference * poSR ); + OGRSpatialReference *getSpatialReference( void ) const { return poSRS; } + + virtual OGRErr transform( OGRCoordinateTransformation *poCT ) = 0; + OGRErr transformTo( OGRSpatialReference *poSR ); + + virtual void segmentize(double dfMaxLength); + + // ISpatialRelation + virtual OGRBoolean Intersects( const OGRGeometry * ) const; + virtual OGRBoolean Equals( OGRGeometry * ) const = 0; + virtual OGRBoolean Disjoint( const OGRGeometry * ) const; + virtual OGRBoolean Touches( const OGRGeometry * ) const; + virtual OGRBoolean Crosses( const OGRGeometry * ) const; + virtual OGRBoolean Within( const OGRGeometry * ) const; + virtual OGRBoolean Contains( const OGRGeometry * ) const; + virtual OGRBoolean Overlaps( const OGRGeometry * ) const; +// virtual OGRBoolean Relate( const OGRGeometry *, const char * ) const; + + virtual OGRGeometry *Boundary() const; + virtual double Distance( const OGRGeometry * ) const; + virtual OGRGeometry *ConvexHull() const; + virtual OGRGeometry *Buffer( double dfDist, int nQuadSegs = 30 ) const; + virtual OGRGeometry *Intersection( const OGRGeometry *) const; + virtual OGRGeometry *Union( const OGRGeometry * ) const; + virtual OGRGeometry *UnionCascaded() const; + virtual OGRGeometry *Difference( const OGRGeometry * ) const; + virtual OGRGeometry *SymDifference( const OGRGeometry * ) const; + virtual OGRErr Centroid( OGRPoint * poPoint ) const; + virtual OGRGeometry *Simplify(double dTolerance) const; + OGRGeometry *SimplifyPreserveTopology(double dTolerance) const; + + virtual OGRGeometry *Polygonize() const; + + // backward compatibility to non-standard method names. + OGRBoolean Intersect( OGRGeometry * ) const CPL_WARN_DEPRECATED("Non standard method. Use Intersects() instead"); + OGRBoolean Equal( OGRGeometry * ) const CPL_WARN_DEPRECATED("Non standard method. Use Equals() instead"); + virtual OGRGeometry *SymmetricDifference( const OGRGeometry * ) const CPL_WARN_DEPRECATED("Non standard method. Use SymDifference() instead"); + virtual OGRGeometry *getBoundary() const CPL_WARN_DEPRECATED("Non standard method. Use Boundary() instead"); + + // Special HACK for DB2 7.2 support + static int bGenerate_DB2_V72_BYTE_ORDER; + + virtual void swapXY(); + + static OGRGeometry* CastToIdentity(OGRGeometry* poGeom) { return poGeom; } + static OGRGeometry* CastToError(OGRGeometry* poGeom); +}; + +/************************************************************************/ +/* OGRPoint */ +/************************************************************************/ + +/** + * Point class. + * + * Implements SFCOM IPoint methods. + */ + +class CPL_DLL OGRPoint : public OGRGeometry +{ + double x; + double y; + double z; + + public: + OGRPoint(); + OGRPoint( double x, double y ); + OGRPoint( double x, double y, double z ); + virtual ~OGRPoint(); + + // IWks Interface + virtual int WkbSize() const; + virtual OGRErr importFromWkb( unsigned char *, int=-1, OGRwkbVariant=wkbVariantOldOgc ); + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOldOgc ) const; + virtual OGRErr importFromWkt( char ** ); + virtual OGRErr exportToWkt( char ** ppszDstText, OGRwkbVariant=wkbVariantOldOgc ) const; + + // IGeometry + virtual int getDimension() const; + virtual int getCoordinateDimension() const; + virtual OGRGeometry *clone() const; + virtual void empty(); + virtual void getEnvelope( OGREnvelope * psEnvelope ) const; + virtual void getEnvelope( OGREnvelope3D * psEnvelope ) const; + virtual OGRBoolean IsEmpty() const; + + // IPoint + double getX() const { return x; } + double getY() const { return y; } + double getZ() const { return z; } + + // Non standard + virtual void setCoordinateDimension( int nDimension ); + void setX( double xIn ) { x = xIn; if (nCoordDimension <= 0) nCoordDimension = 2; } + void setY( double yIn ) { y = yIn; if (nCoordDimension <= 0) nCoordDimension = 2; } + void setZ( double zIn ) { z = zIn; nCoordDimension=3; } + + // ISpatialRelation + virtual OGRBoolean Equals( OGRGeometry * ) const; + virtual OGRBoolean Intersects( const OGRGeometry * ) const; + virtual OGRBoolean Within( const OGRGeometry * ) const; + + // Non standard from OGRGeometry + virtual const char *getGeometryName() const; + virtual OGRwkbGeometryType getGeometryType() const; + virtual OGRErr transform( OGRCoordinateTransformation *poCT ); + virtual void flattenTo2D(); + + virtual void swapXY(); +}; + +/************************************************************************/ +/* OGRPointIterator */ +/************************************************************************/ + +/** + * Interface for a point iterator. + * + * @since GDAL 2.0 + */ + +class CPL_DLL OGRPointIterator +{ + public: + virtual ~OGRPointIterator(); + virtual OGRBoolean getNextPoint(OGRPoint* p) = 0; + + static void destroy(OGRPointIterator*); +}; + +/************************************************************************/ +/* OGRCurve */ +/************************************************************************/ + +/** + * Abstract curve base class for OGRLineString, OGRCircularString and + * OGRCompoundCurve + */ + +class CPL_DLL OGRCurve : public OGRGeometry +{ + protected: + OGRCurve(); + + virtual OGRCurveCasterToLineString GetCasterToLineString() const = 0; + virtual OGRCurveCasterToLinearRing GetCasterToLinearRing() const = 0; + + friend class OGRCurvePolygon; + friend class OGRCompoundCurve; + virtual int ContainsPoint( const OGRPoint* p ) const; + virtual double get_AreaOfCurveSegments() const = 0; + + public: + virtual ~OGRCurve(); + + // ICurve methods + virtual double get_Length() const = 0; + virtual void StartPoint(OGRPoint *) const = 0; + virtual void EndPoint(OGRPoint *) const = 0; + virtual int get_IsClosed() const; + virtual void Value( double, OGRPoint * ) const = 0; + virtual OGRLineString* CurveToLine(double dfMaxAngleStepSizeDegrees = 0, + const char* const* papszOptions = NULL) const = 0; + virtual int getDimension() const; + + // non standard + virtual int getNumPoints() const = 0; + virtual OGRPointIterator* getPointIterator() const = 0; + virtual OGRBoolean IsConvex() const; + virtual double get_Area() const = 0; + + static OGRCompoundCurve* CastToCompoundCurve(OGRCurve* puCurve); + static OGRLineString* CastToLineString(OGRCurve* poCurve); + static OGRLinearRing* CastToLinearRing(OGRCurve* poCurve); +}; + +/************************************************************************/ +/* OGRSimpleCurve */ +/************************************************************************/ + +/** + * Abstract curve base class for OGRLineString and OGRCircularString + * + * Note: this class does not exist in SQL/MM standard and exists for + * implementation convenience. + * + * @since GDAL 2.0 + */ + +class CPL_DLL OGRSimpleCurve: public OGRCurve +{ + protected: + friend class OGRGeometry; + + int nPointCount; + OGRRawPoint *paoPoints; + double *padfZ; + + void Make3D(); + void Make2D(); + + OGRErr importFromWKTListOnly( char ** ppszInput, int bHasZ, int bHasM, + OGRRawPoint*& paoPointsIn, int& nMaxPoints, + double*& padfZIn ); + + virtual double get_LinearArea() const; + + OGRSimpleCurve(); + + public: + virtual ~OGRSimpleCurve(); + + // IWks Interface + virtual int WkbSize() const; + virtual OGRErr importFromWkb( unsigned char *, int = -1, OGRwkbVariant=wkbVariantOldOgc ); + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOldOgc ) const; + virtual OGRErr importFromWkt( char ** ); + virtual OGRErr exportToWkt( char ** ppszDstText, OGRwkbVariant=wkbVariantOldOgc ) const; + + // IGeometry interface + virtual OGRGeometry *clone() const; + virtual void empty(); + virtual void getEnvelope( OGREnvelope * psEnvelope ) const; + virtual void getEnvelope( OGREnvelope3D * psEnvelope ) const; + virtual OGRBoolean IsEmpty() const; + + // ICurve methods + virtual double get_Length() const; + virtual void StartPoint(OGRPoint *) const; + virtual void EndPoint(OGRPoint *) const; + virtual void Value( double, OGRPoint * ) const; + virtual double Project(const OGRPoint *) const; + virtual OGRLineString* getSubLine(double, double, int) const; + + // ILineString methods + virtual int getNumPoints() const { return nPointCount; } + void getPoint( int, OGRPoint * ) const; + double getX( int i ) const { return paoPoints[i].x; } + double getY( int i ) const { return paoPoints[i].y; } + double getZ( int i ) const; + + // ISpatialRelation + virtual OGRBoolean Equals( OGRGeometry * ) const; + + // non standard. + virtual void setCoordinateDimension( int nDimension ); + void setNumPoints( int nNewPointCount, int bZeroizeNewContent = TRUE ); + void setPoint( int, OGRPoint * ); + void setPoint( int, double, double ); + void setZ( int, double ); + void setPoint( int, double, double, double ); + void setPoints( int, OGRRawPoint *, double * = NULL ); + void setPoints( int, double * padfX, double * padfY, + double *padfZIn = NULL ); + void addPoint( OGRPoint * ); + void addPoint( double, double ); + void addPoint( double, double, double ); + + void getPoints( OGRRawPoint *, double * = NULL ) const; + void getPoints( void* pabyX, int nXStride, + void* pabyY, int nYStride, + void* pabyZ = NULL, int nZStride = 0 ) const; + + void addSubLineString( const OGRLineString *, + int nStartVertex = 0, int nEndVertex = -1 ); + void reversePoints( void ); + virtual OGRPointIterator* getPointIterator() const; + + // non-standard from OGRGeometry + virtual OGRErr transform( OGRCoordinateTransformation *poCT ); + virtual void flattenTo2D(); + virtual void segmentize(double dfMaxLength); + + virtual void swapXY(); +}; + +/************************************************************************/ +/* OGRLineString */ +/************************************************************************/ + +/** + * Concrete representation of a multi-vertex line. + * + * Note: for implementation convenience, we make it inherit from OGRSimpleCurve whereas + * SFSQL and SQL/MM only make it inherits from OGRCurve. + */ + +class CPL_DLL OGRLineString : public OGRSimpleCurve +{ + protected: + static OGRLineString* TransferMembersAndDestroy( + OGRLineString* poSrc, + OGRLineString* poDst); + + static OGRLinearRing* CastToLinearRing(OGRLineString* poLS); + + virtual OGRCurveCasterToLineString GetCasterToLineString() const; + virtual OGRCurveCasterToLinearRing GetCasterToLinearRing() const; + + virtual double get_AreaOfCurveSegments() const; + + public: + OGRLineString(); + virtual ~OGRLineString(); + + virtual OGRLineString* CurveToLine(double dfMaxAngleStepSizeDegrees = 0, + const char* const* papszOptions = NULL) const; + virtual OGRGeometry* getCurveGeometry(const char* const* papszOptions = NULL) const; + virtual double get_Area() const; + + // non-standard from OGRGeometry + virtual OGRwkbGeometryType getGeometryType() const; + virtual const char *getGeometryName() const; +}; + +/************************************************************************/ +/* OGRLinearRing */ +/************************************************************************/ + +/** + * Concrete representation of a closed ring. + * + * This class is functionally equivelent to an OGRLineString, but has a + * separate identity to maintain alignment with the OpenGIS simple feature + * data model. It exists to serve as a component of an OGRPolygon. + * + * The OGRLinearRing has no corresponding free standing well known binary + * representation, so importFromWkb() and exportToWkb() will not actually + * work. There is a non-standard GDAL WKT representation though. + * + * Because OGRLinearRing is not a "proper" free standing simple features + * object, it cannot be directly used on a feature via SetGeometry(), and + * cannot genearally be used with GEOS for operations like Intersects(). + * Instead the polygon should be used, or the OGRLinearRing should be + * converted to an OGRLineString for such operations. + * + * Note: this class exists in SFSQL 1.2, but not in ISO SQL/MM Part 3. + */ + +class CPL_DLL OGRLinearRing : public OGRLineString +{ + protected: + friend class OGRPolygon; + + // These are not IWks compatible ... just a convenience for OGRPolygon. + virtual int _WkbSize( int b3D ) const; + virtual OGRErr _importFromWkb( OGRwkbByteOrder, int b3D, + unsigned char *, int=-1 ); + virtual OGRErr _exportToWkb( OGRwkbByteOrder, int b3D, + unsigned char * ) const; + + static OGRLineString* CastToLineString(OGRLinearRing* poLR); + + virtual OGRCurveCasterToLineString GetCasterToLineString() const; + virtual OGRCurveCasterToLinearRing GetCasterToLinearRing() const; + + public: + OGRLinearRing(); + OGRLinearRing( OGRLinearRing * ); + virtual ~OGRLinearRing(); + + // Non standard. + virtual const char *getGeometryName() const; + virtual OGRGeometry *clone() const; + virtual int isClockwise() const; + virtual void reverseWindingOrder(); + virtual void closeRings(); + OGRBoolean isPointInRing(const OGRPoint* pt, int bTestEnvelope = TRUE) const; + OGRBoolean isPointOnRingBoundary(const OGRPoint* pt, int bTestEnvelope = TRUE) const; + + // IWks Interface - Note this isnt really a first class object + // for the purposes of WKB form. These methods always fail since this + // object cant be serialized on its own. + virtual int WkbSize() const; + virtual OGRErr importFromWkb( unsigned char *, int=-1, OGRwkbVariant=wkbVariantOldOgc ); + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOldOgc ) const; +}; + +/************************************************************************/ +/* OGRCircularString */ +/************************************************************************/ + +/** + * Concrete representation of a circular string, that is to say a curve made + * of one or several arc circles. + * + * Note: for implementation convenience, we make it inherit from OGRSimpleCurve whereas + * SQL/MM only makes it inherits from OGRCurve. + * + * Compatibility: ISO SQL/MM Part 3. + * + * @since GDAL 2.0 + */ + +class CPL_DLL OGRCircularString : public OGRSimpleCurve +{ + private: + void ExtendEnvelopeWithCircular( OGREnvelope * psEnvelope ) const; + OGRBoolean IsValidFast() const; + int IsFullCircle( double& cx, double& cy, double& square_R ) const; + + protected: + virtual OGRCurveCasterToLineString GetCasterToLineString() const; + virtual OGRCurveCasterToLinearRing GetCasterToLinearRing() const; + virtual int ContainsPoint( const OGRPoint* p ) const; + virtual double get_AreaOfCurveSegments() const; + + public: + OGRCircularString(); + virtual ~OGRCircularString(); + + // IWks Interface + virtual OGRErr importFromWkb( unsigned char *, int = -1, OGRwkbVariant=wkbVariantOldOgc ); + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOldOgc ) const; + virtual OGRErr importFromWkt( char ** ); + virtual OGRErr exportToWkt( char ** ppszDstText, OGRwkbVariant=wkbVariantOldOgc ) const; + + // IGeometry interface + virtual OGRBoolean IsValid() const; + virtual void getEnvelope( OGREnvelope * psEnvelope ) const; + virtual void getEnvelope( OGREnvelope3D * psEnvelope ) const; + + // ICurve methods + virtual double get_Length() const; + virtual OGRLineString* CurveToLine(double dfMaxAngleStepSizeDegrees = 0, + const char* const* papszOptions = NULL) const; + virtual void Value( double, OGRPoint * ) const; + virtual double get_Area() const; + + // non-standard from OGRGeometry + virtual OGRwkbGeometryType getGeometryType() const; + virtual const char *getGeometryName() const; + virtual void segmentize(double dfMaxLength); + virtual OGRBoolean hasCurveGeometry(int bLookForNonLinear = FALSE) const; + virtual OGRGeometry* getLinearGeometry(double dfMaxAngleStepSizeDegrees = 0, + const char* const* papszOptions = NULL) const; +}; + +/************************************************************************/ +/* OGRCurveCollection */ +/************************************************************************/ + +/** + * Utility class to store a collection of curves. Used as a member of + * OGRCompoundCurve and OGRCurvePolygon. + * + * This class is only exported because of linking issues. It should never + * be directly used. + * + * @since GDAL 2.0 + */ + +class CPL_DLL OGRCurveCollection +{ + protected: + friend class OGRCompoundCurve; + friend class OGRCurvePolygon; + friend class OGRPolygon; + + int nCurveCount; + OGRCurve **papoCurves; + + public: + OGRCurveCollection(); + ~OGRCurveCollection(); + + void empty(OGRGeometry* poGeom); + OGRBoolean IsEmpty() const; + void getEnvelope( OGREnvelope * psEnvelope ) const; + void getEnvelope( OGREnvelope3D * psEnvelope ) const; + + OGRErr addCurveDirectly( OGRGeometry* poGeom, OGRCurve* poCurve, + int bNeedRealloc ); + int WkbSize() const; + OGRErr importPreambuleFromWkb( OGRGeometry* poGeom, + unsigned char * pabyData, + int& nSize, + int& nDataOffset, + OGRwkbByteOrder& eByteOrder, + int nMinSubGeomSize, + OGRwkbVariant eWkVariant ); + OGRErr importBodyFromWkb( OGRGeometry* poGeom, + unsigned char * pabyData, + int nSize, + int nDataOffset, + int bAcceptCompoundCurve, + OGRErr (*pfnAddCurveDirectlyFromWkb)(OGRGeometry* poGeom, OGRCurve* poCurve), + OGRwkbVariant eWkVariant ); + OGRErr exportToWkt( const OGRGeometry* poGeom, char ** ppszDstText ) const; + OGRErr exportToWkb( const OGRGeometry* poGeom, OGRwkbByteOrder, + unsigned char *, OGRwkbVariant eWkbVariant ) const; + OGRBoolean Equals(OGRCurveCollection *poOCC) const; + void setCoordinateDimension( OGRGeometry* poGeom, int nNewDimension ); + int getNumCurves() const; + OGRCurve *getCurve( int ); + const OGRCurve *getCurve( int ) const; + OGRCurve *stealCurve( int ); + OGRErr transform( OGRGeometry* poGeom, + OGRCoordinateTransformation *poCT ); + void flattenTo2D(OGRGeometry* poGeom); + void segmentize(double dfMaxLength); + void swapXY(); + OGRBoolean hasCurveGeometry(int bLookForNonLinear) const; +}; + +/************************************************************************/ +/* OGRCompoundCurve */ +/************************************************************************/ + +/** + * Concrete representation of a compound curve, made of curves: OGRLineString + * and OGRCircularString. Each curve is connected by its first point to + * the last point of the previous curve. + * + * Compatibility: ISO SQL/MM Part 3. + * + * @since GDAL 2.0 + */ + +class CPL_DLL OGRCompoundCurve : public OGRCurve +{ + private: + OGRCurveCollection oCC; + + OGRErr addCurveDirectlyInternal( OGRCurve* poCurve, + double dfToleranceEps, + int bNeedRealloc ); + static OGRErr addCurveDirectlyFromWkt( OGRGeometry* poSelf, OGRCurve* poCurve ); + static OGRErr addCurveDirectlyFromWkb( OGRGeometry* poSelf, OGRCurve* poCurve ); + OGRLineString* CurveToLineInternal(double dfMaxAngleStepSizeDegrees, + const char* const* papszOptions, + int bIsLinearRing) const; + + protected: + static OGRLineString* CastToLineString(OGRCompoundCurve* poCC); + static OGRLinearRing* CastToLinearRing(OGRCompoundCurve* poCC); + + virtual OGRCurveCasterToLineString GetCasterToLineString() const; + virtual OGRCurveCasterToLinearRing GetCasterToLinearRing() const; + + public: + OGRCompoundCurve(); + virtual ~OGRCompoundCurve(); + + // IWks Interface + virtual int WkbSize() const; + virtual OGRErr importFromWkb( unsigned char *, int = -1, OGRwkbVariant=wkbVariantOldOgc ); + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOldOgc ) const; + virtual OGRErr importFromWkt( char ** ); + virtual OGRErr exportToWkt( char ** ppszDstText, OGRwkbVariant=wkbVariantOldOgc ) const; + + // IGeometry interface + virtual OGRGeometry *clone() const; + virtual void empty(); + virtual void getEnvelope( OGREnvelope * psEnvelope ) const; + virtual void getEnvelope( OGREnvelope3D * psEnvelope ) const; + virtual OGRBoolean IsEmpty() const; + + // ICurve methods + virtual double get_Length() const; + virtual void StartPoint(OGRPoint *) const; + virtual void EndPoint(OGRPoint *) const; + virtual void Value( double, OGRPoint * ) const; + virtual OGRLineString* CurveToLine(double dfMaxAngleStepSizeDegrees = 0, + const char* const* papszOptions = NULL) const; + + virtual int getNumPoints() const; + virtual double get_AreaOfCurveSegments() const; + virtual double get_Area() const; + + // ISpatialRelation + virtual OGRBoolean Equals( OGRGeometry * ) const; + + // ICompoundCurve method + int getNumCurves() const; + OGRCurve *getCurve( int ); + const OGRCurve *getCurve( int ) const; + + // non standard. + virtual void setCoordinateDimension( int nDimension ); + + OGRErr addCurve( OGRCurve*, double dfToleranceEps = 1e-14 ); + OGRErr addCurveDirectly( OGRCurve*, double dfToleranceEps = 1e-14 ); + OGRCurve *stealCurve( int ); + virtual OGRPointIterator* getPointIterator() const; + + // non-standard from OGRGeometry + virtual OGRwkbGeometryType getGeometryType() const; + virtual const char *getGeometryName() const; + virtual OGRErr transform( OGRCoordinateTransformation *poCT ); + virtual void flattenTo2D(); + virtual void segmentize(double dfMaxLength); + virtual OGRBoolean hasCurveGeometry(int bLookForNonLinear = FALSE) const; + virtual OGRGeometry* getLinearGeometry(double dfMaxAngleStepSizeDegrees = 0, + const char* const* papszOptions = NULL) const; + + virtual void swapXY(); +}; + +/************************************************************************/ +/* OGRSurface */ +/************************************************************************/ + +/** + * Abstract base class for 2 dimensional objects like polygons or curve polygons. + */ + +class CPL_DLL OGRSurface : public OGRGeometry +{ + protected: + + virtual OGRSurfaceCasterToPolygon GetCasterToPolygon() const = 0; + virtual OGRSurfaceCasterToCurvePolygon GetCasterToCurvePolygon() const = 0; + + public: + virtual double get_Area() const = 0; + virtual OGRErr PointOnSurface( OGRPoint * poPoint ) const = 0; + + static OGRPolygon* CastToPolygon(OGRSurface* poSurface); + static OGRCurvePolygon* CastToCurvePolygon(OGRSurface* poSurface); +}; + + +/************************************************************************/ +/* OGRCurvePolygon */ +/************************************************************************/ + +/** + * Concrete class representing curve polygons. + * + * Note that curve polygons consist of one outer (curve) ring, and zero or + * more inner rings. A curve polygon cannot represent disconnected + * regions (such as multiple islands in a political body). The + * OGRMultiSurface must be used for this. + * + * Compatibility: ISO SQL/MM Part 3. + * + * @since GDAL 2.0 + */ + +class CPL_DLL OGRCurvePolygon : public OGRSurface +{ + private: + OGRBoolean ContainsPoint( const OGRPoint* p ) const; + virtual int checkRing( OGRCurve * poNewRing ) const; + OGRErr addRingDirectlyInternal( OGRCurve* poCurve, int bNeedRealloc ); + static OGRErr addCurveDirectlyFromWkt( OGRGeometry* poSelf, OGRCurve* poCurve ); + static OGRErr addCurveDirectlyFromWkb( OGRGeometry* poSelf, OGRCurve* poCurve ); + + protected: + friend class OGRPolygon; + OGRCurveCollection oCC; + + static OGRPolygon* CastToPolygon(OGRCurvePolygon* poCP); + + virtual OGRSurfaceCasterToPolygon GetCasterToPolygon() const; + virtual OGRSurfaceCasterToCurvePolygon GetCasterToCurvePolygon() const; + + public: + OGRCurvePolygon(); + virtual ~OGRCurvePolygon(); + + // Non standard (OGRGeometry). + virtual const char *getGeometryName() const; + virtual OGRwkbGeometryType getGeometryType() const; + virtual OGRGeometry *clone() const; + virtual void empty(); + virtual OGRErr transform( OGRCoordinateTransformation *poCT ); + virtual void flattenTo2D(); + virtual OGRBoolean IsEmpty() const; + virtual void segmentize(double dfMaxLength); + virtual OGRBoolean hasCurveGeometry(int bLookForNonLinear = FALSE) const; + virtual OGRGeometry* getLinearGeometry(double dfMaxAngleStepSizeDegrees = 0, + const char* const* papszOptions = NULL) const; + + // ISurface Interface + virtual double get_Area() const; + virtual int PointOnSurface( OGRPoint * poPoint ) const; + + // IWks Interface + virtual int WkbSize() const; + virtual OGRErr importFromWkb( unsigned char *, int = -1, OGRwkbVariant=wkbVariantOldOgc ); + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOldOgc ) const; + virtual OGRErr importFromWkt( char ** ); + virtual OGRErr exportToWkt( char ** ppszDstText, OGRwkbVariant eWkbVariant = wkbVariantOldOgc ) const; + + // IGeometry + virtual int getDimension() const; + virtual void getEnvelope( OGREnvelope * psEnvelope ) const; + virtual void getEnvelope( OGREnvelope3D * psEnvelope ) const; + + // ICurvePolygon + virtual OGRPolygon* CurvePolyToPoly(double dfMaxAngleStepSizeDegrees = 0, + const char* const* papszOptions = NULL) const; + + // ISpatialRelation + virtual OGRBoolean Equals( OGRGeometry * ) const; + virtual OGRBoolean Intersects( const OGRGeometry * ) const; + virtual OGRBoolean Contains( const OGRGeometry * ) const; + + // Non standard + virtual void setCoordinateDimension( int nDimension ); + + OGRErr addRing( OGRCurve * ); + OGRErr addRingDirectly( OGRCurve * ); + + OGRCurve *getExteriorRingCurve(); + const OGRCurve *getExteriorRingCurve() const; + int getNumInteriorRings() const; + OGRCurve *getInteriorRingCurve( int ); + const OGRCurve *getInteriorRingCurve( int ) const; + + OGRCurve *stealExteriorRingCurve(); + + virtual void swapXY(); +}; + +/************************************************************************/ +/* OGRPolygon */ +/************************************************************************/ + +/** + * Concrete class representing polygons. + * + * Note that the OpenGIS simple features polygons consist of one outer + * ring (linearring), and zero or more inner rings. A polygon cannot represent disconnected + * regions (such as multiple islands in a political body). The + * OGRMultiPolygon must be used for this. + */ + +class CPL_DLL OGRPolygon : public OGRCurvePolygon +{ + protected: + friend class OGRMultiSurface; + + virtual int checkRing( OGRCurve * poNewRing ) const; + OGRErr importFromWKTListOnly( char ** ppszInput, int bHasZ, int bHasM, + OGRRawPoint*& paoPoints, int& nMaxPoints, + double*& padfZ ); + + static OGRCurvePolygon* CastToCurvePolygon(OGRPolygon* poPoly); + + virtual OGRSurfaceCasterToPolygon GetCasterToPolygon() const; + virtual OGRSurfaceCasterToCurvePolygon GetCasterToCurvePolygon() const; + + public: + OGRPolygon(); + virtual ~OGRPolygon(); + + // Non standard (OGRGeometry). + virtual const char *getGeometryName() const; + virtual OGRwkbGeometryType getGeometryType() const; + virtual OGRBoolean hasCurveGeometry(int bLookForNonLinear = FALSE) const; + virtual OGRGeometry* getCurveGeometry(const char* const* papszOptions = NULL) const; + virtual OGRGeometry* getLinearGeometry(double dfMaxAngleStepSizeDegrees = 0, + const char* const* papszOptions = NULL) const; + + // ISurface Interface + virtual int PointOnSurface( OGRPoint * poPoint ) const; + + // IWks Interface + virtual int WkbSize() const; + virtual OGRErr importFromWkb( unsigned char *, int = -1, OGRwkbVariant=wkbVariantOldOgc ); + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOldOgc ) const; + virtual OGRErr importFromWkt( char ** ); + virtual OGRErr exportToWkt( char ** ppszDstText, OGRwkbVariant=wkbVariantOldOgc ) const; + + // ICurvePolygon + virtual OGRPolygon* CurvePolyToPoly(double dfMaxAngleStepSizeDegrees = 0, + const char* const* papszOptions = NULL) const; + + OGRLinearRing *getExteriorRing(); + const OGRLinearRing *getExteriorRing() const; + OGRLinearRing *getInteriorRing( int ); + const OGRLinearRing *getInteriorRing( int ) const; + + OGRLinearRing *stealExteriorRing(); + OGRLinearRing *stealInteriorRing(int); + + OGRBoolean IsPointOnSurface( const OGRPoint * ) const; + + virtual void closeRings(); +}; + +/************************************************************************/ +/* OGRGeometryCollection */ +/************************************************************************/ + +/** + * A collection of 1 or more geometry objects. + * + * All geometries must share a common spatial reference system, and + * Subclasses may impose additional restrictions on the contents. + */ + +class CPL_DLL OGRGeometryCollection : public OGRGeometry +{ + OGRErr importFromWkbInternal( unsigned char * pabyData, int nSize, int nRecLevel, + OGRwkbVariant ); + OGRErr importFromWktInternal( char **ppszInput, int nRecLevel ); + + protected: + int nGeomCount; + OGRGeometry **papoGeoms; + + OGRErr exportToWktInternal( char ** ppszDstText, + OGRwkbVariant eWkbVariant, + const char* pszSkipPrefix ) const; + virtual OGRBoolean isCompatibleSubType( OGRwkbGeometryType ) const; + + static OGRGeometryCollection* TransferMembersAndDestroy(OGRGeometryCollection* poSrc, + OGRGeometryCollection* poDst); + + public: + OGRGeometryCollection(); + virtual ~OGRGeometryCollection(); + + // Non standard (OGRGeometry). + virtual const char *getGeometryName() const; + virtual OGRwkbGeometryType getGeometryType() const; + virtual OGRGeometry *clone() const; + virtual void empty(); + virtual OGRErr transform( OGRCoordinateTransformation *poCT ); + virtual void flattenTo2D(); + virtual OGRBoolean IsEmpty() const; + virtual void segmentize(double dfMaxLength); + virtual OGRBoolean hasCurveGeometry(int bLookForNonLinear = FALSE) const; + virtual OGRGeometry* getCurveGeometry(const char* const* papszOptions = NULL) const; + virtual OGRGeometry* getLinearGeometry(double dfMaxAngleStepSizeDegrees = 0, const char* const* papszOptions = NULL) const; + + // IWks Interface + virtual int WkbSize() const; + virtual OGRErr importFromWkb( unsigned char *, int = -1, OGRwkbVariant=wkbVariantOldOgc ); + virtual OGRErr exportToWkb( OGRwkbByteOrder, unsigned char *, OGRwkbVariant=wkbVariantOldOgc ) const; + virtual OGRErr importFromWkt( char ** ); + virtual OGRErr exportToWkt( char ** ppszDstText, OGRwkbVariant=wkbVariantOldOgc ) const; + + virtual double get_Length() const; + virtual double get_Area() const; + + // IGeometry methods + virtual int getDimension() const; + virtual void getEnvelope( OGREnvelope * psEnvelope ) const; + virtual void getEnvelope( OGREnvelope3D * psEnvelope ) const; + + // IGeometryCollection + int getNumGeometries() const; + OGRGeometry *getGeometryRef( int ); + const OGRGeometry *getGeometryRef( int ) const; + + // ISpatialRelation + virtual OGRBoolean Equals( OGRGeometry * ) const; + + // Non standard + virtual void setCoordinateDimension( int nDimension ); + virtual OGRErr addGeometry( const OGRGeometry * ); + virtual OGRErr addGeometryDirectly( OGRGeometry * ); + virtual OGRErr removeGeometry( int iIndex, int bDelete = TRUE ); + + void closeRings(); + + virtual void swapXY(); +}; + +/************************************************************************/ +/* OGRMultiSurface */ +/************************************************************************/ + +/** + * A collection of non-overlapping OGRSurface. + * + * @since GDAL 2.0 + */ + +class CPL_DLL OGRMultiSurface : public OGRGeometryCollection +{ + protected: + virtual OGRBoolean isCompatibleSubType( OGRwkbGeometryType ) const; + + public: + OGRMultiSurface(); + virtual ~OGRMultiSurface(); + + // Non standard (OGRGeometry). + virtual const char *getGeometryName() const; + virtual OGRwkbGeometryType getGeometryType() const; + virtual OGRErr importFromWkt( char ** ); + virtual OGRErr exportToWkt( char **, OGRwkbVariant=wkbVariantOldOgc ) const; + + // IMultiSurface methods + virtual OGRErr PointOnSurface( OGRPoint * poPoint ) const; + + // IGeometry methods + virtual int getDimension() const; + + // Non standard + virtual OGRBoolean hasCurveGeometry(int bLookForNonLinear = FALSE) const; + + static OGRMultiPolygon* CastToMultiPolygon(OGRMultiSurface* poMS); +}; + +/************************************************************************/ +/* OGRMultiPolygon */ +/************************************************************************/ + +/** + * A collection of non-overlapping OGRPolygon. + */ + +class CPL_DLL OGRMultiPolygon : public OGRMultiSurface +{ + protected: + virtual OGRBoolean isCompatibleSubType( OGRwkbGeometryType ) const; + + public: + OGRMultiPolygon(); + virtual ~OGRMultiPolygon(); + + // Non standard (OGRGeometry). + virtual const char *getGeometryName() const; + virtual OGRwkbGeometryType getGeometryType() const; + virtual OGRErr exportToWkt( char **, OGRwkbVariant=wkbVariantOldOgc ) const; + + // IMultiSurface methods + virtual OGRErr PointOnSurface( OGRPoint * poPoint ) const; + + // Non standard + virtual OGRBoolean hasCurveGeometry(int bLookForNonLinear = FALSE) const; + + static OGRMultiSurface* CastToMultiSurface(OGRMultiPolygon* poMP); +}; + +/************************************************************************/ +/* OGRMultiPoint */ +/************************************************************************/ + +/** + * A collection of OGRPoint. + */ + +class CPL_DLL OGRMultiPoint : public OGRGeometryCollection +{ + private: + OGRErr importFromWkt_Bracketed( char **, int bHasM, int bHasZ ); + + protected: + virtual OGRBoolean isCompatibleSubType( OGRwkbGeometryType ) const; + + public: + OGRMultiPoint(); + virtual ~OGRMultiPoint(); + + // Non standard (OGRGeometry). + virtual const char *getGeometryName() const; + virtual OGRwkbGeometryType getGeometryType() const; + virtual OGRErr importFromWkt( char ** ); + virtual OGRErr exportToWkt( char **, OGRwkbVariant=wkbVariantOldOgc ) const; + + // IGeometry methods + virtual int getDimension() const; + + // Non standard + virtual OGRBoolean hasCurveGeometry(int bLookForNonLinear = FALSE) const; +}; + +/************************************************************************/ +/* OGRMultiCurve */ +/************************************************************************/ + +/** + * A collection of OGRCurve. + * + * @since GDAL 2.0 + */ + +class CPL_DLL OGRMultiCurve : public OGRGeometryCollection +{ + protected: + static OGRErr addCurveDirectlyFromWkt( OGRGeometry* poSelf, OGRCurve* poCurve ); + virtual OGRBoolean isCompatibleSubType( OGRwkbGeometryType ) const; + + public: + OGRMultiCurve(); + virtual ~OGRMultiCurve(); + + // Non standard (OGRGeometry). + virtual const char *getGeometryName() const; + virtual OGRwkbGeometryType getGeometryType() const; + virtual OGRErr importFromWkt( char ** ); + virtual OGRErr exportToWkt( char **, OGRwkbVariant=wkbVariantOldOgc ) const; + + // IGeometry methods + virtual int getDimension() const; + + // Non standard + virtual OGRBoolean hasCurveGeometry(int bLookForNonLinear = FALSE) const; + + static OGRMultiLineString* CastToMultiLineString(OGRMultiCurve* poMC); +}; + +/************************************************************************/ +/* OGRMultiLineString */ +/************************************************************************/ + +/** + * A collection of OGRLineString. + */ + +class CPL_DLL OGRMultiLineString : public OGRMultiCurve +{ + protected: + virtual OGRBoolean isCompatibleSubType( OGRwkbGeometryType ) const; + + public: + OGRMultiLineString(); + virtual ~OGRMultiLineString(); + + // Non standard (OGRGeometry). + virtual const char *getGeometryName() const; + virtual OGRwkbGeometryType getGeometryType() const; + virtual OGRErr exportToWkt( char **, OGRwkbVariant=wkbVariantOldOgc ) const; + + // Non standard + virtual OGRBoolean hasCurveGeometry(int bLookForNonLinear = FALSE) const; + + static OGRMultiCurve* CastToMultiCurve(OGRMultiLineString* poMLS); +}; + + +/************************************************************************/ +/* OGRGeometryFactory */ +/************************************************************************/ + +/** + * Create geometry objects from well known text/binary. + */ + +class CPL_DLL OGRGeometryFactory +{ + static OGRErr createFromFgfInternal( unsigned char *pabyData, + OGRSpatialReference * poSR, + OGRGeometry **ppoReturn, + int nBytes, + int *pnBytesConsumed, + int nRecLevel ); + public: + static OGRErr createFromWkb( unsigned char *, OGRSpatialReference *, + OGRGeometry **, int = -1, OGRwkbVariant=wkbVariantOldOgc ); + static OGRErr createFromWkt( char **, OGRSpatialReference *, + OGRGeometry ** ); + static OGRErr createFromFgf( unsigned char *, OGRSpatialReference *, + OGRGeometry **, int = -1, int * = NULL ); + static OGRGeometry *createFromGML( const char * ); + static OGRGeometry *createFromGEOS( GEOSContextHandle_t hGEOSCtxt, GEOSGeom ); + + static void destroyGeometry( OGRGeometry * ); + static OGRGeometry *createGeometry( OGRwkbGeometryType ); + + static OGRGeometry * forceToPolygon( OGRGeometry * ); + static OGRGeometry * forceToLineString( OGRGeometry *, bool bOnlyInOrder = true ); + static OGRGeometry * forceToMultiPolygon( OGRGeometry * ); + static OGRGeometry * forceToMultiPoint( OGRGeometry * ); + static OGRGeometry * forceToMultiLineString( OGRGeometry * ); + + static OGRGeometry * forceTo( OGRGeometry* poGeom, + OGRwkbGeometryType eTargetType, + const char*const* papszOptions = NULL ); + + static OGRGeometry * organizePolygons( OGRGeometry **papoPolygons, + int nPolygonCount, + int *pbResultValidGeometry, + const char **papszOptions = NULL); + static int haveGEOS(); + + static OGRGeometry* transformWithOptions( const OGRGeometry* poSrcGeom, + OGRCoordinateTransformation *poCT, + char** papszOptions ); + + static OGRGeometry* + approximateArcAngles( double dfX, double dfY, double dfZ, + double dfPrimaryRadius, double dfSecondaryAxis, + double dfRotation, + double dfStartAngle, double dfEndAngle, + double dfMaxAngleStepSizeDegrees ); + + static int GetCurveParmeters(double x0, double y0, + double x1, double y1, + double x2, double y2, + double& R, double& cx, double& cy, + double& alpha0, double& alpha1, double& alpha2 ); + static OGRLineString* curveToLineString( double x0, double y0, double z0, + double x1, double y1, double z1, + double x2, double y2, double z2, + int bHasZ, + double dfMaxAngleStepSizeDegrees, + const char*const* papszOptions = NULL ); + static OGRCurve* curveFromLineString(const OGRLineString* poLS, + const char*const* papszOptions = NULL); +}; + +OGRwkbGeometryType CPL_DLL OGRFromOGCGeomType( const char *pszGeomType ); +const char CPL_DLL * OGRToOGCGeomType( OGRwkbGeometryType eGeomType ); + +/* Prepared geometry API (needs GEOS >= 3.1.0) */ +typedef struct _OGRPreparedGeometry OGRPreparedGeometry; +int OGRHasPreparedGeometrySupport(); +OGRPreparedGeometry* OGRCreatePreparedGeometry( const OGRGeometry* poGeom ); +void OGRDestroyPreparedGeometry( OGRPreparedGeometry* poPreparedGeom ); +int OGRPreparedGeometryIntersects( const OGRPreparedGeometry* poPreparedGeom, + const OGRGeometry* poOtherGeom ); + +#endif /* ndef _OGR_GEOMETRY_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogr_geos.h b/bazaar/plugin/gdal/ogr/ogr_geos.h new file mode 100644 index 000000000..f9d24cf79 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_geos.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * $Id: ogr_geos.h 27483 2014-06-30 20:49:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Definitions related to support for use of GEOS in OGR. + * This file is only intended to be pulled in by OGR implementation + * code directly accessing GEOS. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef OGR_GEOS_H_INCLUDED +#define OGR_GEOS_H_INCLUDED + +#ifdef HAVE_GEOS +// To avoid accidental use of non reentrant GEOS API. +// (check only effective in GEOS >= 3.5) +# define GEOS_USE_ONLY_R_API + +# include +#else + +namespace geos { + class Geometry; +}; + +#endif + +#endif /* ndef OGR_GEOS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogr_opt.cpp b/bazaar/plugin/gdal/ogr/ogr_opt.cpp new file mode 100644 index 000000000..e7c70c9d5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_opt.cpp @@ -0,0 +1,620 @@ +/****************************************************************************** + * $Id: ogr_opt.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: OpenGIS Simple Features + * Purpose: Functions for getting list of projection types, and their parms. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_srs_api.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogr_opt.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +static const char *papszParameterDefinitions[] = { + SRS_PP_CENTRAL_MERIDIAN, "Central Meridian", "Long", "0.0", + SRS_PP_SCALE_FACTOR, "Scale Factor", "Ratio", "1.0", + SRS_PP_STANDARD_PARALLEL_1, "Standard Parallel 1", "Lat", "0.0", + SRS_PP_STANDARD_PARALLEL_2, "Standard Parallel 2", "Lat", "0.0", + SRS_PP_LONGITUDE_OF_CENTER, "Longitude of Center", "Long", "0.0", + SRS_PP_LATITUDE_OF_CENTER, "Latitude of Center", "Lat", "0.0", + SRS_PP_LONGITUDE_OF_ORIGIN, "Longitude of Origin", "Long", "0.0", + SRS_PP_LATITUDE_OF_ORIGIN, "Latitude of Origin", "Lat", "0.0", + SRS_PP_FALSE_EASTING, "False Easting", "m", "0.0", + SRS_PP_FALSE_NORTHING, "False Northing", "m", "0.0", + SRS_PP_AZIMUTH, "Azimuth", "Angle", "0.0", + SRS_PP_LONGITUDE_OF_POINT_1,"Longitude of Point 1", "Long", "0.0", + SRS_PP_LATITUDE_OF_POINT_1, "Latitude of Point 1", "Lat", "0.0", + SRS_PP_LONGITUDE_OF_POINT_2,"Longitude of Point 2", "Long", "0.0", + SRS_PP_LATITUDE_OF_POINT_2, "Latitude of Point 2", "Lat", "0.0", + SRS_PP_LONGITUDE_OF_POINT_3,"Longitude of Point 3", "Long", "0.0", + SRS_PP_LATITUDE_OF_POINT_3, "Latitude of Point 3", "Lat", "0.0", + SRS_PP_RECTIFIED_GRID_ANGLE,"Rectified Grid Angle", "Angle", "0.0", + SRS_PP_SATELLITE_HEIGHT, "Satellite Height", "m", "35785831.0", + NULL +}; + +static const char *papszProjectionDefinitions[] = { + + "*", + SRS_PT_TRANSVERSE_MERCATOR, + "Transverse Mercator", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED, + "Transverse Mercator (South Oriented)", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_TUNISIA_MINING_GRID, + "Tunisia Mining Grid", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_ALBERS_CONIC_EQUAL_AREA, + "Albers Conic Equal Area", + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_AZIMUTHAL_EQUIDISTANT, + "Azimuthal Equidistant", + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_CYLINDRICAL_EQUAL_AREA, + "Cylindrical Equal Area", + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_CASSINI_SOLDNER, + "Cassini/Soldner", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_EQUIDISTANT_CONIC, + "Equidistant Conic", + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_BONNE, + "Bonne", + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_ECKERT_I, + "Eckert I", + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_ECKERT_II, + "Eckert II", + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_ECKERT_III, + "Eckert III", + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_ECKERT_IV, + "Eckert IV", + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_ECKERT_V, + "Eckert V", + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_ECKERT_VI, + "Eckert VI", + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_EQUIRECTANGULAR, + "Equirectangular", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_GAUSSSCHREIBERTMERCATOR, + "Gauss-Schreiber Transverse Mercator", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_GALL_STEREOGRAPHIC, + "Gall Stereographic", + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_GOODE_HOMOLOSINE, + "Goode Homolosine", + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_IGH, + "Interrupted Goode Homolosine", + + "*", + SRS_PT_GEOSTATIONARY_SATELLITE, + "Geostationary Satellite", + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SATELLITE_HEIGHT, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_GNOMONIC, + "Gnomonic", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_HOTINE_OBLIQUE_MERCATOR, + "Hotine Oblique Mercator", + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_AZIMUTH, + SRS_PP_RECTIFIED_GRID_ANGLE, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN, + "Hotine Oblique Mercator Two Point Natural Origin", + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LATITUDE_OF_POINT_1, + SRS_PP_LONGITUDE_OF_POINT_1, + SRS_PP_LATITUDE_OF_POINT_2, + SRS_PP_LONGITUDE_OF_POINT_2, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA, + "Lambert Azimuthal Equal Area", + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP, + "Lambert Conformal Conic (2SP)", + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP, + "Lambert Conformal Conic (1SP)", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM, + "Lambert Conformal Conic (2SP - Belgium)", + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_MILLER_CYLINDRICAL, + "Miller Cylindrical", + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_MERCATOR_1SP, + "Mercator (1SP)", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_MERCATOR_2SP, + "Mercator (2SP)", + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_MOLLWEIDE, + "Mollweide", + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_NEW_ZEALAND_MAP_GRID, + "New Zealand Map Grid", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_OBLIQUE_STEREOGRAPHIC, + "Oblique Stereographic", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_ORTHOGRAPHIC, + "Orthographic", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_POLYCONIC, + "Polyconic", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_POLAR_STEREOGRAPHIC, + "Polar Stereographic", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_ROBINSON, + "Robinson", + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_SINUSOIDAL, + "Sinusoidal", + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*" + SRS_PT_STEREOGRAPHIC, + "Stereographic", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*" + SRS_PT_TWO_POINT_EQUIDISTANT, + "Two Point Equidistant", + SRS_PP_LATITUDE_OF_1ST_POINT, + SRS_PP_LONGITUDE_OF_1ST_POINT, + SRS_PP_LATITUDE_OF_2ND_POINT, + SRS_PP_LONGITUDE_OF_2ND_POINT, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_VANDERGRINTEN, + "Van Der Grinten", + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*" + SRS_PT_KROVAK, + "Krovak", + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_AZIMUTH, + SRS_PP_PSEUDO_STD_PARALLEL_1, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_IMW_POLYCONIC, + "International Map of the World Polyconic", + SRS_PP_LATITUDE_OF_1ST_POINT, + SRS_PP_LATITUDE_OF_2ND_POINT, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_WAGNER_I, + "Wagner I (Kavraisky VI)", + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_WAGNER_II, + "Wagner II", + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_WAGNER_III, + "Wagner III", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_WAGNER_IV, + "Wagner IV", + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_WAGNER_V, + "Wagner V", + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_WAGNER_VI, + "Wagner VI", + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_WAGNER_VII, + "Wagner VII", + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + + "*", + SRS_PT_QSC, + "Quadrilateralized Spherical Cube", + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + + NULL +}; + + + + +/************************************************************************/ +/* OPTGetProjectionMethods() */ +/************************************************************************/ + +/** + * Fetch list of possible projection methods. + * + * @return Returns NULL terminated list of projection methods. This should + * be freed with CSLDestroy() when no longer needed. + */ + +char **OPTGetProjectionMethods() + +{ + int i; + char **papszList = NULL; + + for( i = 1; papszProjectionDefinitions[i] != NULL; i++ ) + { + if( EQUAL(papszProjectionDefinitions[i-1],"*") ) + papszList = CSLAddString(papszList,papszProjectionDefinitions[i]); + } + + return papszList; +} + +/************************************************************************/ +/* OPTGetParameterList() */ +/************************************************************************/ + +/** + * Fetch the parameters for a given projection method. + * + * @param pszProjectionMethod internal name of projection methods to fetch + * the parameters for, such as "Transverse_Mercator" + * (SRS_PT_TRANSVERSE_MERCATOR). + * + * @param ppszUserName pointer in which to return a user visible name for + * the projection name. The returned string should not be modified or + * freed by the caller. Legal to pass in NULL if user name not required. + * + * @return returns a NULL terminated list of internal parameter names that + * should be freed by the caller when no longer needed. Returns NULL if + * projection method is unknown. + */ + +char **OPTGetParameterList( const char *pszProjectionMethod, + char ** ppszUserName ) + +{ + char **papszList = NULL; + int i; + + for( i = 1; papszProjectionDefinitions[i] != NULL; i++ ) + { + if( papszProjectionDefinitions[i-1][0] == '*' + && EQUAL(papszProjectionDefinitions[i],pszProjectionMethod) ) + { + i++; + + if( ppszUserName != NULL ) + *ppszUserName = (char *)papszProjectionDefinitions[i]; + + i++; + while( papszProjectionDefinitions[i] != NULL + && papszProjectionDefinitions[i][0] != '*' ) + { + papszList = CSLAddString( papszList, + papszProjectionDefinitions[i] ); + i++; + } + if( papszList == NULL) /* IGH has no parameter, so return an empty list instead of NULL */ + papszList = (char**) CPLCalloc(1, sizeof(char*)); + return papszList; + } + } + + return NULL; +} + +/************************************************************************/ +/* OPTGetParameterInfo() */ +/************************************************************************/ + +/** + * Fetch information about a single parameter of a projection method. + * + * @param pszProjectionMethod name of projection method for which the parameter + * applies. Not currently used, but in the future this could affect defaults. + * This is the internal projection method name, such as "Tranverse_Mercator". + * + * @param pszParameterName name of the parameter to fetch information about. + * This is the internal name such as "central_meridian" + * (SRS_PP_CENTRAL_MERIDIAN). + * + * @param ppszUserName location at which to return the user visible name for + * the parameter. This pointer may be NULL to skip the user name. The + * returned name should not be modified or freed. + * + * @param ppszType location at which to return the parameter type for + * the parameter. This pointer may be NULL to skip. The returned type + * should not be modified or freed. The type values are described above. + * + * @param pdfDefaultValue location at which to put the default value for + * this parameter. The pointer may be NULL. + * + * @return TRUE if parameter found, or FALSE otherwise. + */ + +int OPTGetParameterInfo( const char * pszProjectionMethod, + const char * pszParameterName, + char ** ppszUserName, + char ** ppszType, + double *pdfDefaultValue ) + +{ + int i; + + (void) pszProjectionMethod; + + for( i = 0; papszParameterDefinitions[i] != NULL; i += 4 ) + { + if( EQUAL(papszParameterDefinitions[i],pszParameterName) ) + { + if( ppszUserName != NULL ) + *ppszUserName = (char *)papszParameterDefinitions[i+1]; + if( ppszType != NULL ) + *ppszType = (char *)papszParameterDefinitions[i+2]; + if( pdfDefaultValue != NULL ) + *pdfDefaultValue = CPLAtof(papszParameterDefinitions[i+3]); + + return TRUE; + } + } + + return FALSE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogr_p.h b/bazaar/plugin/gdal/ogr/ogr_p.h new file mode 100644 index 000000000..69a4daf0c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_p.h @@ -0,0 +1,163 @@ +/****************************************************************************** + * $Id: ogr_p.h 28900 2015-04-14 09:40:34Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Some private helper functions and stuff for OGR implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef OGR_P_H_INCLUDED +#define OGR_P_H_INCLUDED + +/* -------------------------------------------------------------------- */ +/* Include the common portability library ... lets us do lots */ +/* of stuff easily. */ +/* -------------------------------------------------------------------- */ + +#include "cpl_string.h" +#include "cpl_conv.h" +#include "cpl_minixml.h" + +#include "ogr_core.h" +#include "ogr_geometry.h" + +/* A default name for the default geometry column, instead of '' */ +#define OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME "_ogr_geometry_" + +#ifdef CPL_MSB +# define OGR_SWAP(x) (x == wkbNDR) +#else +# define OGR_SWAP(x) (x == wkbXDR) +#endif + +/* PostGIS 1.X has non standard codes for the following geometry types */ +#define POSTGIS15_CURVEPOLYGON 13 /* instead of 10 */ +#define POSTGIS15_MULTICURVE 14 /* instead of 11 */ +#define POSTGIS15_MULTISURFACE 15 /* instead of 12 */ + +/* Has been deprecated. Can only be used in very specific circumstances */ +#ifdef GDAL_COMPILATION +#define wkb25DBitInternalUse 0x80000000 +#endif + +/* -------------------------------------------------------------------- */ +/* helper function for parsing well known text format vector objects.*/ +/* -------------------------------------------------------------------- */ + +#ifdef _OGR_GEOMETRY_H_INCLUDED +#define OGR_WKT_TOKEN_MAX 64 + +const char CPL_DLL * OGRWktReadToken( const char * pszInput, char * pszToken ); + +const char CPL_DLL * OGRWktReadPoints( const char * pszInput, + OGRRawPoint **ppaoPoints, + double **ppadfZ, + int * pnMaxPoints, + int * pnReadPoints ); + +void CPL_DLL OGRMakeWktCoordinate( char *, double, double, double, int ); + +#endif + +void OGRFormatDouble( char *pszBuffer, int nBufferLen, double dfVal, char chDecimalSep, int nPrecision = 15 ); + +/* -------------------------------------------------------------------- */ +/* Date-time parsing and processing functions */ +/* -------------------------------------------------------------------- */ + +/* Internal use by OGR drivers only, CPL_DLL is just there in case */ +/* they are compiled as plugins */ +int CPL_DLL OGRGetDayOfWeek(int day, int month, int year); +int CPL_DLL OGRParseXMLDateTime( const char* pszXMLDateTime, + OGRField* psField ); +int CPL_DLL OGRParseRFC822DateTime( const char* pszRFC822DateTime, + OGRField* psField ); +char CPL_DLL * OGRGetRFC822DateTime(const OGRField* psField); +char CPL_DLL * OGRGetXMLDateTime(const OGRField* psField); +char CPL_DLL * OGRGetXML_UTF8_EscapedString(const char* pszString); + +int OGRCompareDate( OGRField *psFirstTuple, + OGRField *psSecondTuple ); /* used by ogr_gensql.cpp and ogrfeaturequery.cpp */ + +/* General utility option processing. */ +int CPL_DLL OGRGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv, int nOptions ); + +/************************************************************************/ +/* Support for special attributes (feature query and selection) */ +/************************************************************************/ +#define SPF_FID 0 +#define SPF_OGR_GEOMETRY 1 +#define SPF_OGR_STYLE 2 +#define SPF_OGR_GEOM_WKT 3 +#define SPF_OGR_GEOM_AREA 4 +#define SPECIAL_FIELD_COUNT 5 + +extern const char* SpecialFieldNames[SPECIAL_FIELD_COUNT]; + +#ifdef _SWQ_H_INCLUDED_ +extern const swq_field_type SpecialFieldTypes[SPECIAL_FIELD_COUNT]; +#endif + +/************************************************************************/ +/* Some SRS related stuff, search in SRS data files. */ +/************************************************************************/ + +OGRErr CPL_DLL OSRGetEllipsoidInfo( int, char **, double *, double *); + +/* Fast atof function */ +double OGRFastAtof(const char* pszStr); + +OGRErr CPL_DLL OGRCheckPermutation(int* panPermutation, int nSize); + +/* GML related */ + +OGRGeometry *GML2OGRGeometry_XMLNode( const CPLXMLNode *psNode, + int bGetSecondaryGeometryOption, + int nRecLevel = 0, + int nSRSDimension = 0, + int bIgnoreGSG = FALSE, + int bOrientation = TRUE, + int bFaceHoleNegative = FALSE); + +/************************************************************************/ +/* PostGIS EWKB encoding */ +/************************************************************************/ + +OGRGeometry CPL_DLL *OGRGeometryFromEWKB( GByte *pabyWKB, int nLength, int* pnSRID, + int bIsPostGIS1_EWKB ); +OGRGeometry CPL_DLL *OGRGeometryFromHexEWKB( const char *pszBytea, int* pnSRID, + int bIsPostGIS1_EWKB ); +char CPL_DLL * OGRGeometryToHexEWKB( OGRGeometry * poGeometry, int nSRSId, + int bIsPostGIS1_EWKB ); + +/************************************************************************/ +/* WKB Type Handling encoding */ +/************************************************************************/ + +OGRErr OGRReadWKBGeometryType( unsigned char * pabyData, + OGRwkbVariant wkbVariant, + OGRwkbGeometryType *eGeometryType, OGRBoolean *b3D ); + +#endif /* ndef OGR_P_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogr_spatialref.h b/bazaar/plugin/gdal/ogr/ogr_spatialref.h new file mode 100644 index 000000000..d3ec94442 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_spatialref.h @@ -0,0 +1,633 @@ +/****************************************************************************** + * $Id: ogr_spatialref.h 28972 2015-04-22 10:39:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Classes for manipulating spatial reference systems in a + * platform non-specific manner. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SPATIALREF_H_INCLUDED +#define _OGR_SPATIALREF_H_INCLUDED + +#include "ogr_srs_api.h" + +/** + * \file ogr_spatialref.h + * + * Coordinate systems services. + */ + +/************************************************************************/ +/* OGR_SRSNode */ +/************************************************************************/ + +/** + * Objects of this class are used to represent value nodes in the parsed + * representation of the WKT SRS format. For instance UNIT["METER",1] + * would be rendered into three OGR_SRSNodes. The root node would have a + * value of UNIT, and two children, the first with a value of METER, and the + * second with a value of 1. + * + * Normally application code just interacts with the OGRSpatialReference + * object, which uses the OGR_SRSNode to implement it's data structure; + * however, this class is user accessable for detailed access to components + * of an SRS definition. + */ + +class CPL_DLL OGR_SRSNode +{ + char *pszValue; + + OGR_SRSNode **papoChildNodes; + OGR_SRSNode *poParent; + + int nChildren; + + int NeedsQuoting() const; + OGRErr importFromWkt( char **, int nRecLevel, int* pnNodes ); + + public: + OGR_SRSNode(const char * = NULL); + ~OGR_SRSNode(); + + int IsLeafNode() const { return nChildren == 0; } + + int GetChildCount() const { return nChildren; } + OGR_SRSNode *GetChild( int ); + const OGR_SRSNode *GetChild( int ) const; + + OGR_SRSNode *GetNode( const char * ); + const OGR_SRSNode *GetNode( const char * ) const; + + void InsertChild( OGR_SRSNode *, int ); + void AddChild( OGR_SRSNode * ); + int FindChild( const char * ) const; + void DestroyChild( int ); + void ClearChildren(); + void StripNodes( const char * ); + + const char *GetValue() const { return pszValue; } + void SetValue( const char * ); + + void MakeValueSafe(); + OGRErr FixupOrdering(); + + OGR_SRSNode *Clone() const; + + OGRErr importFromWkt( char ** ); + OGRErr exportToWkt( char ** ) const; + OGRErr exportToPrettyWkt( char **, int = 1) const; + + OGRErr applyRemapper( const char *pszNode, + char **papszSrcValues, + char **papszDstValues, + int nStepSize = 1, + int bChildOfHit = FALSE ); +}; + +/************************************************************************/ +/* OGRSpatialReference */ +/************************************************************************/ + +/** + * This class respresents a OpenGIS Spatial Reference System, and contains + * methods for converting between this object organization and well known + * text (WKT) format. This object is reference counted as one instance of + * the object is normally shared between many OGRGeometry objects. + * + * Normally application code can fetch needed parameter values for this + * SRS using GetAttrValue(), but in special cases the underlying parse tree + * (or OGR_SRSNode objects) can be accessed more directly. + * + * See the tutorial for more information on + * how to use this class. + */ + +class CPL_DLL OGRSpatialReference +{ + double dfFromGreenwich; + double dfToMeter; + double dfToDegrees; + + OGR_SRSNode *poRoot; + + int nRefCount; + int bNormInfoSet; + + static OGRErr Validate(OGR_SRSNode *poRoot); + static OGRErr ValidateAuthority(OGR_SRSNode *poRoot); + static OGRErr ValidateAxis(OGR_SRSNode *poRoot); + static OGRErr ValidateUnit(OGR_SRSNode *poRoot); + static OGRErr ValidateVertDatum(OGR_SRSNode *poRoot); + static OGRErr ValidateProjection( OGR_SRSNode* poRoot ); + static int IsAliasFor( const char *, const char * ); + void GetNormInfo() const; + + OGRErr importFromURNPart(const char* pszAuthority, + const char* pszCode, + const char* pszURN); + public: + OGRSpatialReference(const OGRSpatialReference&); + OGRSpatialReference(const char * = NULL); + + virtual ~OGRSpatialReference(); + + static void DestroySpatialReference(OGRSpatialReference* poSRS); + + OGRSpatialReference &operator=(const OGRSpatialReference&); + + int Reference(); + int Dereference(); + int GetReferenceCount() const { return nRefCount; } + void Release(); + + OGRSpatialReference *Clone() const; + OGRSpatialReference *CloneGeogCS() const; + + void dumpReadable(); + OGRErr exportToWkt( char ** ) const; + OGRErr exportToPrettyWkt( char **, int = FALSE) const; + OGRErr exportToProj4( char ** ) const; + OGRErr exportToPCI( char **, char **, double ** ) const; + OGRErr exportToUSGS( long *, long *, double **, long * ) const; + OGRErr exportToXML( char **, const char * = NULL ) const; + OGRErr exportToPanorama( long *, long *, long *, long *, + double * ) const; + OGRErr exportToERM( char *pszProj, char *pszDatum, char *pszUnits ); + OGRErr exportToMICoordSys( char ** ) const; + + OGRErr importFromWkt( char ** ); + OGRErr importFromProj4( const char * ); + OGRErr importFromEPSG( int ); + OGRErr importFromEPSGA( int ); + OGRErr importFromESRI( char ** ); + OGRErr importFromPCI( const char *, const char * = NULL, + double * = NULL ); +#define USGS_ANGLE_DECIMALDEGREES 0 +#define USGS_ANGLE_PACKEDDMS TRUE /* 1 */ +#define USGS_ANGLE_RADIANS 2 + OGRErr importFromUSGS( long iProjSys, long iZone, + double *padfPrjParams, long iDatum, + int nUSGSAngleFormat = USGS_ANGLE_PACKEDDMS ); + OGRErr importFromPanorama( long, long, long, double* ); + OGRErr importFromOzi( const char * const* papszLines ); + OGRErr importFromWMSAUTO( const char *pszAutoDef ); + OGRErr importFromXML( const char * ); + OGRErr importFromDict( const char *pszDict, const char *pszCode ); + OGRErr importFromURN( const char * ); + OGRErr importFromCRSURL( const char * ); + OGRErr importFromERM( const char *pszProj, const char *pszDatum, + const char *pszUnits ); + OGRErr importFromUrl( const char * ); + OGRErr importFromMICoordSys( const char * ); + + OGRErr morphToESRI(); + OGRErr morphFromESRI(); + + OGRErr Validate(); + OGRErr StripCTParms( OGR_SRSNode * = NULL ); + OGRErr StripVertical(); + OGRErr FixupOrdering(); + OGRErr Fixup(); + + int EPSGTreatsAsLatLong(); + int EPSGTreatsAsNorthingEasting(); + const char *GetAxis( const char *pszTargetKey, int iAxis, + OGRAxisOrientation *peOrientation ) const; + OGRErr SetAxes( const char *pszTargetKey, + const char *pszXAxisName, + OGRAxisOrientation eXAxisOrientation, + const char *pszYAxisName, + OGRAxisOrientation eYAxisOrientation ); + + // Machinary for accessing parse nodes + OGR_SRSNode *GetRoot() { return poRoot; } + const OGR_SRSNode *GetRoot() const { return poRoot; } + void SetRoot( OGR_SRSNode * ); + + OGR_SRSNode *GetAttrNode(const char *); + const OGR_SRSNode *GetAttrNode(const char *) const; + const char *GetAttrValue(const char *, int = 0) const; + + OGRErr SetNode( const char *, const char * ); + OGRErr SetNode( const char *, double ); + + OGRErr SetLinearUnitsAndUpdateParameters( const char *pszName, + double dfInMeters ); + OGRErr SetLinearUnits( const char *pszName, double dfInMeters ); + OGRErr SetTargetLinearUnits( const char *pszTargetKey, + const char *pszName, double dfInMeters ); + double GetLinearUnits( char ** = NULL ) const; + double GetTargetLinearUnits( const char *pszTargetKey, + char ** ppszRetName = NULL ) const; + + OGRErr SetAngularUnits( const char *pszName, double dfInRadians ); + double GetAngularUnits( char ** = NULL ) const; + + double GetPrimeMeridian( char ** = NULL ) const; + + int IsGeographic() const; + int IsProjected() const; + int IsGeocentric() const; + int IsLocal() const; + int IsVertical() const; + int IsCompound() const; + int IsSameGeogCS( const OGRSpatialReference * ) const; + int IsSameVertCS( const OGRSpatialReference * ) const; + int IsSame( const OGRSpatialReference * ) const; + + void Clear(); + OGRErr SetLocalCS( const char * ); + OGRErr SetProjCS( const char * ); + OGRErr SetProjection( const char * ); + OGRErr SetGeocCS( const char * pszGeocName ); + OGRErr SetGeogCS( const char * pszGeogName, + const char * pszDatumName, + const char * pszEllipsoidName, + double dfSemiMajor, double dfInvFlattening, + const char * pszPMName = NULL, + double dfPMOffset = 0.0, + const char * pszUnits = NULL, + double dfConvertToRadians = 0.0 ); + OGRErr SetWellKnownGeogCS( const char * ); + OGRErr CopyGeogCSFrom( const OGRSpatialReference * poSrcSRS ); + OGRErr SetVertCS( const char *pszVertCSName, + const char *pszVertDatumName, + int nVertDatumClass = 2005 ); + OGRErr SetCompoundCS( const char *pszName, + const OGRSpatialReference *poHorizSRS, + const OGRSpatialReference *poVertSRS ); + + OGRErr SetFromUserInput( const char * ); + + OGRErr SetTOWGS84( double, double, double, + double = 0.0, double = 0.0, double = 0.0, + double = 0.0 ); + OGRErr GetTOWGS84( double *padfCoef, int nCoeff = 7 ) const; + + double GetSemiMajor( OGRErr * = NULL ) const; + double GetSemiMinor( OGRErr * = NULL ) const; + double GetInvFlattening( OGRErr * = NULL ) const; + + OGRErr SetAuthority( const char * pszTargetKey, + const char * pszAuthority, + int nCode ); + + OGRErr AutoIdentifyEPSG(); + int GetEPSGGeogCS(); + + const char *GetAuthorityCode( const char * pszTargetKey ) const; + const char *GetAuthorityName( const char * pszTargetKey ) const; + + const char *GetExtension( const char *pszTargetKey, + const char *pszName, + const char *pszDefault = NULL ) const; + OGRErr SetExtension( const char *pszTargetKey, + const char *pszName, + const char *pszValue ); + + int FindProjParm( const char *pszParameter, + const OGR_SRSNode *poPROJCS=NULL ) const; + OGRErr SetProjParm( const char *, double ); + double GetProjParm( const char *, double =0.0, OGRErr* = NULL ) const; + + OGRErr SetNormProjParm( const char *, double ); + double GetNormProjParm( const char *, double=0.0, OGRErr* =NULL)const; + + static int IsAngularParameter( const char * ); + static int IsLongitudeParameter( const char * ); + static int IsLinearParameter( const char * ); + + /** Albers Conic Equal Area */ + OGRErr SetACEA( double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Azimuthal Equidistant */ + OGRErr SetAE( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Bonne */ + OGRErr SetBonne( double dfStdP1, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + + /** Cylindrical Equal Area */ + OGRErr SetCEA( double dfStdP1, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + + /** Cassini-Soldner */ + OGRErr SetCS( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Equidistant Conic */ + OGRErr SetEC( double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Eckert I-VI */ + OGRErr SetEckert( int nVariation, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + + OGRErr SetEckertIV( double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + + OGRErr SetEckertVI( double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + + /** Equirectangular */ + OGRErr SetEquirectangular(double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + /** Equirectangular generalized form : */ + OGRErr SetEquirectangular2( double dfCenterLat, double dfCenterLong, + double dfPseudoStdParallel1, + double dfFalseEasting, double dfFalseNorthing ); + + /** Geostationary Satellite */ + OGRErr SetGEOS( double dfCentralMeridian, double dfSatelliteHeight, + double dfFalseEasting, double dfFalseNorthing ); + + /** Goode Homolosine */ + OGRErr SetGH( double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + + /** Interrupted Goode Homolosine */ + OGRErr SetIGH(); + + /** Gall Stereograpic */ + OGRErr SetGS( double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + + /** Gauss Schreiber Transverse Mercator */ + OGRErr SetGaussSchreiberTMercator(double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + + /** Gnomonic */ + OGRErr SetGnomonic(double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Hotine Oblique Mercator */ + OGRErr SetHOM( double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + + OGRErr SetHOM2PNO( double dfCenterLat, + double dfLat1, double dfLong1, + double dfLat2, double dfLong2, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + + OGRErr SetOM( double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + + /** Hotine Oblique Mercator Azimuth Center / Variant B */ + OGRErr SetHOMAC( double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + + /** International Map of the World Polyconic */ + OGRErr SetIWMPolyconic( double dfLat1, double dfLat2, + double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ); + + /** Krovak Oblique Conic Conformal */ + OGRErr SetKrovak( double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfPseudoStdParallelLat, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + + /** Lambert Azimuthal Equal-Area */ + OGRErr SetLAEA( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Lambert Conformal Conic */ + OGRErr SetLCC( double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Lambert Conformal Conic 1SP */ + OGRErr SetLCC1SP( double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + + /** Lambert Conformal Conic (Belgium) */ + OGRErr SetLCCB( double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Miller Cylindrical */ + OGRErr SetMC( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Mercator */ + OGRErr SetMercator( double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + + OGRErr SetMercator2SP( double dfStdP1, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Mollweide */ + OGRErr SetMollweide( double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + + /** New Zealand Map Grid */ + OGRErr SetNZMG( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Oblique Stereographic */ + OGRErr SetOS( double dfOriginLat, double dfCMeridian, + double dfScale, + double dfFalseEasting,double dfFalseNorthing); + + /** Orthographic */ + OGRErr SetOrthographic( double dfCenterLat, double dfCenterLong, + double dfFalseEasting,double dfFalseNorthing); + + /** Polyconic */ + OGRErr SetPolyconic( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Polar Stereographic */ + OGRErr SetPS( double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing); + + /** Robinson */ + OGRErr SetRobinson( double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Sinusoidal */ + OGRErr SetSinusoidal( double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Stereographic */ + OGRErr SetStereographic( double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting,double dfFalseNorthing); + + /** Swiss Oblique Cylindrical */ + OGRErr SetSOC( double dfLatitudeOfOrigin, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + + /** Transverse Mercator */ + OGRErr SetTM( double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + + /** Transverse Mercator variants. */ + OGRErr SetTMVariant( const char *pszVariantName, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + + /** Tunesia Mining Grid */ + OGRErr SetTMG( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Transverse Mercator (South Oriented) */ + OGRErr SetTMSO( double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + + /** Two Point Equidistant */ + OGRErr SetTPED( double dfLat1, double dfLong1, + double dfLat2, double dfLong2, + double dfFalseEasting, double dfFalseNorthing ); + + /** VanDerGrinten */ + OGRErr SetVDG( double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + + /** Universal Transverse Mercator */ + OGRErr SetUTM( int nZone, int bNorth = TRUE ); + int GetUTMZone( int *pbNorth = NULL ) const; + + /** Wagner I -- VII */ + OGRErr SetWagner( int nVariation, double dfCenterLat, + double dfFalseEasting, double dfFalseNorthing ); + + /** Quadrilateralized Spherical Cube */ + OGRErr SetQSC(double dfCenterLat, double dfCenterLong); + + /** State Plane */ + OGRErr SetStatePlane( int nZone, int bNAD83 = TRUE, + const char *pszOverrideUnitName = NULL, + double dfOverrideUnit = 0.0 ); + + OGRErr ImportFromESRIStatePlaneWKT( + int nCode, const char* pszDatumName, const char* pszUnitsName, + int nPCSCode, const char* pszCSName = 0 ); + OGRErr ImportFromESRIWisconsinWKT( + const char* pszPrjName, double dfCentralMeridian, double dfLatOfOrigin, + const char* pszUnitsName, const char* pszCSName = 0 ); + + static OGRSpatialReference* GetWGS84SRS(); +}; + +/************************************************************************/ +/* OGRCoordinateTransformation */ +/* */ +/* This is really just used as a base class for a private */ +/* implementation. */ +/************************************************************************/ + +/** + * Interface for transforming between coordinate systems. + * + * Currently, the only implementation within OGR is OGRProj4CT, which + * requires the PROJ.4 library to be available at run-time. + * + * Also, see OGRCreateCoordinateTransformation() for creating transformations. + */ + +class CPL_DLL OGRCoordinateTransformation +{ +public: + virtual ~OGRCoordinateTransformation() {} + + static void DestroyCT(OGRCoordinateTransformation* poCT); + + // From CT_CoordinateTransformation + + /** Fetch internal source coordinate system. */ + virtual OGRSpatialReference *GetSourceCS() = 0; + + /** Fetch internal target coordinate system. */ + virtual OGRSpatialReference *GetTargetCS() = 0; + + // From CT_MathTransform + + /** + * Transform points from source to destination space. + * + * This method is the same as the C function OCTTransform(). + * + * The method TransformEx() allows extended success information to + * be captured indicating which points failed to transform. + * + * @param nCount number of points to transform. + * @param x array of nCount X vertices, modified in place. + * @param y array of nCount Y vertices, modified in place. + * @param z array of nCount Z vertices, modified in place. + * @return TRUE on success, or FALSE if some or all points fail to + * transform. + */ + virtual int Transform( int nCount, + double *x, double *y, double *z = NULL ) = 0; + + /** + * Transform points from source to destination space. + * + * This method is the same as the C function OCTTransformEx(). + * + * @param nCount number of points to transform. + * @param x array of nCount X vertices, modified in place. + * @param y array of nCount Y vertices, modified in place. + * @param z array of nCount Z vertices, modified in place. + * @param pabSuccess array of per-point flags set to TRUE if that point + * transforms, or FALSE if it does not. + * + * @return TRUE if some or all points transform successfully, or FALSE if + * if none transform. + */ + virtual int TransformEx( int nCount, + double *x, double *y, double *z = NULL, + int *pabSuccess = NULL ) = 0; + +}; + +OGRCoordinateTransformation CPL_DLL * +OGRCreateCoordinateTransformation( OGRSpatialReference *poSource, + OGRSpatialReference *poTarget ); + +#endif /* ndef _OGR_SPATIALREF_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogr_sql.dox b/bazaar/plugin/gdal/ogr/ogr_sql.dox new file mode 100644 index 000000000..3b640bce7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_sql.dox @@ -0,0 +1,674 @@ +/*! \page ogr_sql OGR SQL + +The GDALDataset supports executing commands against a datasource via the +GDALDataset::ExecuteSQL() method. While in theory +any sort of command could be handled this way, in practice the mechanism is +used to provide a subset of SQL SELECT capability to applications. This +page discusses the generic SQL implementation implemented within OGR, and +issue with driver specific SQL support.

    + +Since GDAL/OGR 1.10, an alternate "dialect", the SQLite dialect, can be used +instead of the OGRSQL dialect. Refer to the +SQLite SQL dialect page for more details.

    + +The OGRLayer class also supports applying an attribute query filter to +features returned using the OGRLayer::SetAttributeFilter() method. The +syntax for the attribute filter is the same as the WHERE clause in the +OGR SQL SELECT statement. So everything here with regard to the WHERE +clause applies in the context of the SetAttributeFilter() method.

    + +NOTE: OGR SQL has been reimplemented for GDAL/OGR 1.8.0. Many features +discussed below, notably arithmetic expressions, and expressions in the +field list, were not support in GDAL/OGR 1.7.x and earlier. See RFC 28 for +details of the new features in GDAL/OGR 1.8.0. + +\section ogr_sql_select SELECT + +The SELECT statement is used to fetch layer features (analogous to table +rows in an RDBMS) with the result of the query represented as a temporary layer +of features. The layers of the datasource are analogous to tables in an +RDBMS and feature attributes are analogous to column values. The simplest +form of OGR SQL SELECT statement looks like this: + +\code +SELECT * FROM polylayer +\endcode + +In this case all features are fetched from the layer named "polylayer", and +all attributes of those features are returned. This is essentially +equivalent to accessing the layer directly. In this example the "*" +is the list of fields to fetch from the layer, with "*" meaning that all +fields should be fetched. + +This slightly more sophisticated form still pulls all features from the layer +but the schema will only contain the EAS_ID and PROP_VALUE attributes. Any +other attributes would be discarded. + +\code +SELECT eas_id, prop_value FROM polylayer +\endcode + +A much more ambitious SELECT, restricting the features fetched with a +WHERE clause, and sorting the results might look like: + +\code +SELECT * from polylayer WHERE prop_value > 220000.0 ORDER BY prop_value DESC +\endcode + +This select statement will produce a table with just one feature, with one +attribute (named something like "count_eas_id") containing the number of +distinct values of the eas_id attribute. + +\code +SELECT COUNT(DISTINCT eas_id) FROM polylayer +\endcode + +\subsection ogr_sql_flist_ops Field List Operators + +The field list is a comma separate list of the fields to be carried into +the output features from the source layer. They will appear on output features +in the order they appear on in the field list, so the field list may be used +to re-order the fields.

    + +A special form of the field list uses the DISTINCT keyword. This returns a +list of all the distinct values of the named attribute. When the DISTINCT +keyword is used, only one attribute may appear in the field list. The DISTINCT +keyword may be used against any type of field. Currently the distinctness +test against a string value is case insensitive in OGR SQL. The result of +a SELECT with a DISTINCT keyword is a layer with one column (named the same +as the field operated on), and one feature per distinct value. Geometries +are discarded. The distinct values are assembled in memory, so alot of +memory may be used for datasets with a large number of distinct values. + +\code +SELECT DISTINCT areacode FROM polylayer +\endcode + +There are also several summarization operators that may be applied to columns. +When a summarization operator is applied to any field, then all fields must +have summarization operators applied. The summarization operators are +COUNT (a count of instances), AVG (numerical average), SUM (numerical sum), +MIN (lexical or numerical minimum), and MAX (lexical or numerical maximum). +This example produces a variety of sumarization information on parcel +property values: + +\code +SELECT MIN(prop_value), MAX(prop_value), AVG(prop_value), SUM(prop_value), + COUNT(prop_value) FROM polylayer WHERE prov_name = 'Ontario' +\endcode + +It is also possible to apply the COUNT() operator to a DISTINCT SELECT to get +a count of distinct values, for instance: + +\code +SELECT COUNT(DISTINCT areacode) FROM polylayer +\endcode + +Note: prior to OGR 1.9.0, null values were counted in COUNT(column_name) or +COUNT(DISTINCT column_name), which was not conformant with the SQL standard. Since +OGR 1.9.0, only non-null values are counted. + +As a special case, the COUNT() operator can be given a "*" argument instead +of a field name which is a short form for count all the records. + +\code +SELECT COUNT(*) FROM polylayer +\endcode + + +Field names can also be prefixed by a table name though this is only +really meaningful when performing joins. It is further demonstrated in +the JOIN section. + +Field definitions can also be complex expressions using arithmetic, and +functional operators. However, the DISTINCT keyword, and summarization +operators like MIN, MAX, AVG and SUM may not be applied to expression fields. +Starting with GDAL 2.0, boolean resulting expressions (comparisons, logical +operators) can also be used. + +\code +SELECT cost+tax from invoice +\endcode + +or + +\code +SELECT CONCAT(owner_first_name,' ',owner_last_name) from properties +\endcode + +\subsubsection ogr_sql_functions Functions + +Starting with OGR 1.8.2, the SUBSTR function can be used to extract a substring from a string. +Its syntax is the following one : SUBSTR(string_expr, start_offset [, length]). It extracts a substring of string_expr, +starting at offset start_offset (1 being the first character of string_expr, 2 the second one, etc...). +If start_offset is a negative value, the substring is extracted from the end of the string (-1 is the +last character of the string, -2 the character before the last character, ...). +If length is specified, up to length characters are extracted from the string. Otherwise the +remainder of the string is extracted. + +Note: for the time being, the character as considered to be equivalent to bytes, which may not be +appropriate for multi-byte encodings like UTF-8. + +\code +SELECT SUBSTR('abcdef',1,2) FROM xxx --> 'ab' +SELECT SUBSTR('abcdef',4) FROM xxx --> 'def' +SELECT SUBSTR('abcdef',-2) FROM xxx --> 'ef' +\endcode + +Starting with OGR 2.0, the hstore_get_value() function can be used to extract +a value associate to a key from a HSTORE string, formatted like 'key=>value,other_key=>other_value,...' + +\code +SELECT hstore_get_value('a => b, "key with space"=> "value with space"', 'key with space') FROM xxx --> 'value with space' +\endcode + +\subsubsection ogr_sql_fname_alias Using the field name alias + +OGR SQL supports renaming the fields following the SQL92 specification by +using the AS keyword according to the following example: + +\code +SELECT *, OGR_STYLE AS STYLE FROM polylayer +\endcode + +The field name alias can be used as the last operation in the column specification. +Therefore we cannot rename the fields inside an operator, but we can +rename whole column expression, like these two: + +\code +SELECT COUNT(areacode) AS "count" FROM polylayer +SELECT dollars/100.0 AS cents FROM polylayer +\endcode + +\subsubsection ogr_sql_ftype_cast Changing the type of the fields + +Starting with GDAL 1.6.0, OGR SQL supports changing the type of the columns by using the SQL92 compliant CAST +operator according to the following example: + +\code +SELECT *, CAST(OGR_STYLE AS character(255)) FROM rivers +\endcode + +Currently casting to the following target types are supported: + +

      +
    1. boolean (GDAL >= 2.0) +
    2. character(field_length). By default, field_length=1. +
    3. float(field_length) +
    4. numeric(field_length, field_precision) +
    5. smallint(field_length) : 16 bit signed integer (GDAL >= 2.0) +
    6. integer(field_length) +
    7. bigint(field_length), 64 bit integer, extension to SQL92 (GDAL >= 2.0) +
    8. date(field_length) +
    9. time(field_length) +
    10. timestamp(field_length) +
    11. geometry, geometry(geometry_type), geometry(geometry_type,epsg_code) +
    + +Specifying the field_length and/or the field_precision is optional. An +explicit value of zero can be used as the width for character() to indicate +variable width. Conversion to the 'integer list', 'double list' +and 'string list' OGR data types are not supported, which doesn't conform to +the SQL92 specification. + +While the CAST operator can be applied anywhere in an expression, including +in a WHERE clause, the detailed control of output field format is only +supported if the CAST operator is the "outer most" operators on a field +in the field definition list. In other contexts it is still useful to +convert between numeric, string and date data types. + +Starting with OGR 1.11, casting a WKT string to a geometry is allowed. +geometry_type can be POINT[Z], LINESTRING[Z], POLYGON[Z], MULTIPOINT[Z], +MULTILINESTRING[Z], MULTIPOLYGON[Z], GEOMETRYCOLLECTION[Z] or GEOMETRY[Z]. + +\subsubsection ogr_sql_quot String literals and identifiers quoting + +Starting with GDAL 2.0 (see RFC 52 - Strict OGR SQL quoting), +strict SQL92 rules are applied regarding string literals +and identifiers quoting. + +String literals (constants) must be surrounded with single-quote characters. e.g. +WHERE a_field = 'a_value' + +Identifiers (column names and tables names) can be used unquoted if they don't +contain special characters or are not a SQL reserved keyword. Otherwise they must +be surroundered with double-quote characters. e.g. WHERE "from" = 5 + +\subsection ogr_sql_where WHERE + +The argument to the WHERE clause is a logical expression used select records +from the source layer. In addition to its use within the WHERE statement, +the WHERE clause handling is also used for OGR attribute queries on regular +layers via OGRLayer::SetAttributeFilter(). + +In addition to the arithmetic and other functional operators available in +expressions in the field selection clause of the SELECT statement, in the +WHERE context logical operators are also available and the evaluated value +of the expression should be logical (true or false). + +The available logical operators are +=, +!=, +<>, +<, +>, +<=, +>=, +LIKE and +ILIKE, +BETWEEN and +IN. +Most of the operators are self explanitory, but is is worth nothing that +!= is the same as <>, the string equality is +case insensitive, but the <, >, <= and >= operators are case sensitive. Both the LIKE and ILIKE operators are case insensitive. + +The value argument to the LIKE operator is a pattern against which +the value string is matched. In this pattern percent (%) matches any number of +characters, and underscore ( _ ) matches any one character. An optional ESCAPE escape_char +clause can be added so that the percent or underscore characters can be searched +as regular characters, by being preceded with the escape_char. + +\code + String Pattern Matches? + ------ ------- -------- + Alberta ALB% Yes + Alberta _lberta Yes + St. Alberta _lberta No + St. Alberta %lberta Yes + Robarts St. %Robarts% Yes + 12345 123%45 Yes + 123.45 12?45 No + N0N 1P0 %N0N% Yes + L4C 5E2 %N0N% No +\endcode + +The IN takes a list of values as it's argument and tests the attribute +value for membership in the provided set. + +\code + Value Value Set Matches? + ------ ------- -------- + 321 IN (456,123) No + 'Ontario' IN ('Ontario','BC') Yes + 'Ont' IN ('Ontario','BC') No + 1 IN (0,2,4,6) No +\endcode + +The syntax of the BETWEEN operator is "field_name BETWEEN value1 AND value2" +and it is equivalent to "field_name >= value1 AND field_name <= value2". + +In addition to the above binary operators, there are additional operators +for testing if a field is null or not. These are the IS NULL +and IS NOT NULL operators. + +Basic field tests can be combined in more complicated predicates using logical +operators include AND, OR, and the unary logical NOT. +Subexpressions should be bracketed to make precedence +clear. Some more complicated predicates are: + +\code +SELECT * FROM poly WHERE (prop_value >= 100000) AND (prop_value < 200000) +SELECT * FROM poly WHERE NOT (area_code LIKE 'N0N%') +SELECT * FROM poly WHERE (prop_value IS NOT NULL) AND (prop_value < 100000) +\endcode + +\subsection ogr_sql_where_limits WHERE Limitations + +
      +
    1. Fields must all come from the primary table (the one listed in the FROM +clause). +
    2. All string comparisons are case insensitive except for <, >, <= and >=. +
    + +\subsection ogr_sql_order_by ORDER BY + +The ORDER BY clause is used force the returned features to be reordered +into sorted order (ascending or descending) on one of the field values. +Ascending (increasing) order is the default if neither the ASC or DESC keyword +is provided. For example: + +\code +SELECT * FROM property WHERE class_code = 7 ORDER BY prop_value DESC +SELECT * FROM property ORDER BY prop_value +SELECT * FROM property ORDER BY prop_value ASC +SELECT DISTINCT zip_code FROM property ORDER BY zip_code +\endcode + +Note that ORDER BY clauses cause two passes through the feature set. One to +build an in-memory table of field values corresponded with feature ids, and +a second pass to fetch the features by feature id in the sorted order. For +formats which cannot efficiently randomly read features by feature id this can +be a very expensive operation. + +Sorting of string field values is case sensitive, not case insensitive like in +most other parts of OGR SQL. + +\subsection ogr_sql_joins JOINs + +OGR SQL supports a limited form of one to one JOIN. This allows records from +a secondary table to be looked up based on a shared key between it and the +primary table being queried. For instance, a table of city locations might +include a nation_id column that can be used as a reference into a +secondary nation table to fetch a nation name. A joined query might +look like: + +\code +SELECT city.*, nation.name FROM city + LEFT JOIN nation ON city.nation_id = nation.id +\endcode + +This query would result in a table with all the fields from the city table, +and an additional "nation.name" field with the nation name pulled from the +nation table by looking for the record in the nation table that has the "id" +field with the same value as the city.nation_id field. + +Joins introduce a number of additional issues. One is the concept of table +qualifiers on field names. For instance, referring to city.nation_id instead +of just nation_id to indicate the nation_id field from the city layer. The +table name qualifiers may only be used in the field list, and within the +ON clause of the join. + +Wildcards are also somewhat more involved. All fields from the primary table +(city in this case) and the secondary table (nation in this +case) may be selected using the usual * wildcard. But the fields of +just one of the primary or secondary table may be selected by prefixing the +asterix with the table name. + +The field names in the resulting query layer will be qualified by the table +name, if the table name is given as a qualifier in the field list. In addition +field names will be qualified with a table name if they would conflict with +earlier fields. For instance, the following select would result might result +in a results set with a name, nation_id, nation.nation_id and +nation.name field if the city and nation tables both have the +nation_id and name fieldnames. + +\code +SELECT * FROM city LEFT JOIN nation ON city.nation_id = nation.nation_id +\endcode + +On the other hand if the nation table had a continent_id field, but +the city table did not, then that field would not need to be qualified in +the result set. However, if the selected instead looked like the following +statement, all result fields would be qualified by the table name. + +\code +SELECT city.*, nation.* FROM city + LEFT JOIN nation ON city.nation_id = nation.nation_id +\endcode + +In the above examples, the nation table was found in the same +datasource as the city table. However, the OGR join support +includes the ability to join against a table in a different data source, +potentially of a different format. This is indicated by qualifying the +secondary table name with a datasource name. In this case the secondary +datasource is opened using normal OGR semantics and utilized to access the +secondary table until the query result is no longer needed. + +\code +SELECT * FROM city + LEFT JOIN '/usr2/data/nation.dbf'.nation ON city.nation_id = nation.nation_id +\endcode + +While not necessarily very useful, it is also possible to introduce table +aliases to simplify some SELECT statements. This can also be useful to +disambiguate situations where tables of the same name are being used from +different data sources. For instance, if the actual +tables names were messy we might want to do something like: + +\code +SELECT c.name, n.name FROM project_615_city c + LEFT JOIN '/usr2/data/project_615_nation.dbf'.project_615_nation n + ON c.nation_id = n.nation_id +\endcode + +It is possible to do multiple joins in a single query. + +\code +SELECT city.name, prov.name, nation.name FROM city + LEFT JOIN province ON city.prov_id = province.id + LEFT JOIN nation ON city.nation_id = nation.id +\endcode + +Before GDAL 2.0, the expression after ON should necessarily be of the form +"{primary_table}.{field_name} = {secondary_table}.{field_name}", and in that +order. +Starting with GDAL 2.0, it is possible to use a more complex boolean expression, +involving multiple comparison operators, but with the restrictions mentionned +in the below "JOIN limitations" section. In particular, in case of multiple joins (3 tables +or more) the fields compared in a JOIN must belong to the primary table (the one +after FROM) and the table of the active JOIN. + +\subsection ogr_sql_join_limits JOIN Limitations + +
      +
    1. Joins can be very expensive operations if the secondary table is not +indexed on the key field being used. +
    2. Joined fields may not be used in WHERE clauses, or ORDER BY clauses +at this time. The join is essentially evaluated after all primary table +subsetting is complete, and after the ORDER BY pass. +
    3. Joined fields may not be used as keys in later joins. So you could not +use the province id in a city to lookup the province record, and then use a +nation id from the province id to lookup the nation record. This is a sensible +thing to want and could be implemented, but is not currently supported. +
    4. Datasource names for joined tables are evaluated relative to the +current processes working directory, not the path to the primary datasource. +
    5. These are not true LEFT or RIGHT joins in the RDBMS sense. Whether +or not a secondary record exists for the join key or not, one and only one +copy of the primary record is returned in the result set. If a secondary +record cannot be found, the secondary derived fields will be NULL. If more +than one matching secondary field is found only the first will be used. +
    + +\section ogr_sql_union_all UNION ALL + +(OGR >= 1.10.0) + +The SQL engine can deal with several SELECT combined with +UNION ALL. The effect of UNION ALL is to concatenate the rows returned by the right SELECT +statement to the rows returned by the left SELECT statement. + +\code +[(] SELECT field_list FROM first_layer [WHERE where_expr] [)] +UNION ALL [(] SELECT field_list FROM second_layer [WHERE where_expr] [)] +[UNION ALL [(] SELECT field_list FROM third_layer [WHERE where_expr] [)]]* +\endcode + +\subsection ogr_sql_union_all_restrictions UNION ALL restrictions + +The processing of UNION ALL in OGR differs from the SQL standard, in which it accepts +that the columns from the various SELECT are not identical. In that case, it will return +a super-set of all the fields from each SELECT statement. + +There is also a restriction : ORDER BY can only be specified for each SELECT, and +not at the level of the result of the union. + +\section ogr_sql_special_fields SPECIAL FIELDS + +The OGR SQL query processor treats some of the attributes of the features as +built-in special fields can be used in the SQL statements likewise the +other fields. These fields can be placed in the select list, the WHERE clause +and the ORDER BY clause respectively. The special field will not be included +in the result by default but it may be explicitly included by adding it to +the select list. +When accessing the field values the special fields will take precedence over +the other fields with the same names in the data source. + +\subsection ogr_sql_fid FID + +Normally the feature id is a special property of a feature and not treated +as an attribute of the feature. In some cases it is convenient to be able to +utilize the feature id in queries and result sets as a regular field. To do +so use the name FID. The field wildcard expansions will not include +the feature id, but it may be explicitly included using a syntax like: + +\code +SELECT FID, * FROM nation +\endcode + +\subsection ogr_sql_geometry OGR_GEOMETRY + +Some of the data sources (like MapInfo tab) can handle geometries of different +types within the same layer. The OGR_GEOMETRY special field represents +the geometry type returned by OGRGeometry::getGeometryName() and can be used to +distinguish the various types. By using this field one can select particular +types of the geometries like: + +\code +SELECT * FROM nation WHERE OGR_GEOMETRY='POINT' OR OGR_GEOMETRY='POLYGON' +\endcode + +\subsection ogr_sql_geom_wkt OGR_GEOM_WKT + +The Well Known Text representation of the geometry can also be used as +a special field. To select the WKT of the geometry OGR_GEOM_WKT +might be included in the select list, like: + +\code +SELECT OGR_GEOM_WKT, * FROM nation +\endcode + +Using the OGR_GEOM_WKT and the LIKE operator in the WHERE +clause we can get similar effect as using OGR_GEOMETRY: + +\code +SELECT OGR_GEOM_WKT, * FROM nation WHERE OGR_GEOM_WKT + LIKE 'POINT%' OR OGR_GEOM_WKT LIKE 'POLYGON%' +\endcode + +\subsection ogr_sql_geom_area OGR_GEOM_AREA + +(Since GDAL 1.7.0) + +The OGR_GEOM_AREA special field returns the area of the feature's +geometry computed by the OGRSurface::get_Area() method. For +OGRGeometryCollection and OGRMultiPolygon the value is the sum of the +areas of its members. For non-surface geometries the returned area is 0.0. + +For example, to select only polygon features larger than a given area: + +\code +SELECT * FROM nation WHERE OGR_GEOM_AREA > 10000000 +\endcode + +\subsection ogr_sql_style OGR_STYLE + +The OGR_STYLE special field represents the style string of the feature +returned by OGRFeature::GetStyleString(). By using this field and the +LIKE operator the result of the query can be filtered by the style. +For example we can select the annotation features as: + +\code +SELECT * FROM nation WHERE OGR_STYLE LIKE 'LABEL%' +\endcode + +\section ogr_sql_create_index CREATE INDEX + +Some OGR SQL drivers support creating of attribute indexes. Currently +this includes the Shapefile driver. An index accelerates very simple +attribute queries of the form fieldname = value, which is what +is used by the JOIN capability. To create an attribute index on +the nation_id field of the nation table a command like this would be used: + +\code +CREATE INDEX ON nation USING nation_id +\endcode + +\subsection ogr_sql_index_limits Index Limitations + +
      +
    1. Indexes are not maintained dynamically when new features are added to or +removed from a layer. +
    2. Very long strings (longer than 256 characters?) cannot currently be +indexed. +
    3. To recreate an index it is necessary to drop all indexes on a layer and +then recreate all the indexes. +
    4. Indexes are not used in any complex queries. Currently the only +query the will accelerate is a simple "field = value" query. +
    + +\section ogr_sql_drop_index DROP INDEX + +The OGR SQL DROP INDEX command can be used to drop all indexes on a particular +table, or just the index for a particular column. + +\code +DROP INDEX ON nation USING nation_id +DROP INDEX ON nation +\endcode + +\section ogr_sql_alter_table ALTER TABLE + +(OGR >= 1.9.0) + +The following OGR SQL ALTER TABLE commands can be used. + +
      +
    1. "ALTER TABLE tablename ADD [COLUMN] columnname columntype" to add a new field. Supported if the layer declares the OLCCreateField capability. +
    2. "ALTER TABLE tablename RENAME [COLUMN] oldcolumnname TO newcolumnname" to rename an existing field. Supported if the layer declares the OLCAlterFieldDefn capability. +
    3. "ALTER TABLE tablename ALTER [COLUMN] columnname TYPE columntype" to change the type of an existing field. Supported if the layer declares the OLCAlterFieldDefn capability. +
    4. "ALTER TABLE tablename DROP [COLUMN] columnname" to delete an existing field. Supported if the layer declares the OLCDeleteField capability. +
    + +The columntype value follows the syntax of the types supported by the CAST operator described above. + +\code +ALTER TABLE nation ADD COLUMN myfield integer +ALTER TABLE nation RENAME COLUMN myfield TO myfield2 +ALTER TABLE nation ALTER COLUMN myfield2 TYPE character(15) +ALTER TABLE nation DROP COLUMN myfield2 +\endcode + +\section ogr_sql_drop_table DROP TABLE + +(OGR >= 1.9.0) + +The OGR SQL DROP TABLE command can be used to delete a table. This is only +supported on datasources that declare the ODsCDeleteLayer capability. + +\code +DROP TABLE nation +\endcode + +\section ogr_sql_exec_sql ExecuteSQL() + +SQL is executed against an GDALDataset, not against a specific layer. The +call looks like this: + +\code +OGRLayer * GDALDataset::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); +\endcode + +The pszDialect argument is in theory intended to allow for support of +different command languages against a provider, but for now applications +should always pass an empty (not NULL) string to get the default dialect.

    + +The poSpatialFilter argument is a geometry used to select a bounding rectangle +for features to be returned in a manner similar to the +OGRLayer::SetSpatialFilter() method. It may be NULL for no special spatial +restriction.

    + +The result of an ExecuteSQL() call is usually a temporary OGRLayer representing +the results set from the statement. This is the case for a SELECT statement +for instance. The returned temporary layer should be released with +GDALDataset::ReleaseResultsSet() method when no longer needed. Failure +to release it before the datasource is destroyed may result in a crash.

    + +\section ogr_sql_non_ogr_sql Non-OGR SQL + +All OGR drivers for database systems: MySQL, +PostgreSQL and PostGIS (PG), +Oracle (OCI), +SQLite, +ODBC, +ESRI Personal Geodatabase (PGeo) and +MS SQL Spatial (MSSQLSpatial), +override the GDALDataset::ExecuteSQL() function with dedicated implementation +and, by default, pass the SQL statements directly to the underlying RDBMS. +In these cases the SQL syntax varies in some particulars from OGR SQL. +Also, anything possible in SQL can then be accomplished for these particular +databases. Only the result of SQL WHERE statements will be returned as +layers.

    + +*/ diff --git a/bazaar/plugin/gdal/ogr/ogr_sql_sqlite.dox b/bazaar/plugin/gdal/ogr/ogr_sql_sqlite.dox new file mode 100644 index 000000000..7ba029289 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_sql_sqlite.dox @@ -0,0 +1,307 @@ +/*! \page ogr_sql_sqlite SQLite SQL dialect + +Since GDAL/OGR 1.10, the SQLite "dialect" can be used as an alternate SQL dialect to the +OGR SQL dialect. +This assumes that GDAL/OGR is built with support for SQLite (>= 3.6), and preferably +with Spatialite support too to benefit from spatial functions.

    + +The SQLite dialect may be used with any OGR datasource, like the OGR SQL dialect. It +is available through the GDALDataset::ExecuteSQL() method by specifying the pszDialect to +"SQLITE". For the ogrinfo or ogr2ogr +utility, you must specify the "-dialect SQLITE" option.

    + +This is mainly aimed to execute SELECT statements, but, for datasources that support +update, INSERT/UPDATE/DELETE statements can also be run.

    + +The syntax of the SQL statements is fully the one of the SQLite SQL engine. You can +refer to the following pages: +

    + +\section ogr_sql_sqlite_select SELECT statement + +The SELECT statement is used to fetch layer features (analogous to table +rows in an RDBMS) with the result of the query represented as a temporary layer +of features. The layers of the datasource are analogous to tables in an +RDBMS and feature attributes are analogous to column values. The simplest +form of OGR SQLITE SELECT statement looks like this: + +\code +SELECT * FROM polylayer +\endcode + +More complex statements can of course be used, including WHERE, JOIN, USING, GROUP BY, +ORDER BY, sub SELECT, ...

    + +The table names that can be used are the layer names available in the datasource on +which the ExecuteSQL() method is called.

    + +Similarly to OGRSQL, it is also possible to refer to layers of other datasources with +the following syntax : "other_datasource_name"."layer_name".

    + +\code +SELECT p.*, NAME FROM poly p JOIN "idlink.dbf"."idlink" il USING (eas_id) +\endcode + +The column names that can be used in the result column list, in WHERE, JOIN, ... clauses +are the field names of the layers. Expressions, SQLite functions can also be used, +spatial functions, etc...

    + +The conditions on fields expressed in WHERE clauses, or in JOINs are +translated, as far as possible, as attribute filters that are applied on the +underlying OGR layers. Joins can be very expensive operations if the secondary table is not +indexed on the key field being used.

    + +\subsection ogr_sql_sqlite_geometry Geometry field + +The GEOMETRY special field represents the geometry of the feature +returned by OGRFeature::GetGeometryRef(). It can be explicitly specified +in the result column list of a SELECT, and is automatically selected if the +* wildcard is used.

    + +For OGR layers that have a non-empty geometry column name (generally for RDBMS datasources), +as returned by OGRLayer::GetGeometryColumn(), the name of the geometry special field +in the SQL statement will be the name of the geometry column of the underlying OGR layer.

    + +\code +SELECT EAS_ID, GEOMETRY FROM poly + +returns: + +OGRFeature(SELECT):0 + EAS_ID (Real) = 168 + POLYGON ((479819.84375 4765180.5,479690.1875 4765259.5,[...],479819.84375 4765180.5)) +\endcode + +\code +SELECT * FROM poly + +returns: + +OGRFeature(SELECT):0 + AREA (Real) = 215229.266 + EAS_ID (Real) = 168 + PRFEDEA (String) = 35043411 + POLYGON ((479819.84375 4765180.5,479690.1875 4765259.5,[...],479819.84375 4765180.5)) +\endcode + +\subsection ogr_sql_sqlite_style OGR_STYLE special field + +The OGR_STYLE special field represents the style string of the feature +returned by OGRFeature::GetStyleString(). By using this field and the +LIKE operator the result of the query can be filtered by the style. +For example we can select the annotation features as: + +\code +SELECT * FROM nation WHERE OGR_STYLE LIKE 'LABEL%' +\endcode + +\subsection ogr_sql_sqlite_spatialite Spatialite SQL functions + +When GDAL/OGR is build with support for the Spatialite library, +a lot of extra SQL functions, +in particular spatial functions, can be used in results column fields, WHERE clauses, etc....

    + +\code +SELECT EAS_ID, ST_Area(GEOMETRY) AS area FROM poly WHERE + ST_Intersects(GEOMETRY, BuildCircleMbr(479750.6875,4764702.0,100)) + +returns: + +OGRFeature(SELECT):0 + EAS_ID (Real) = 169 + area (Real) = 101429.9765625 + +OGRFeature(SELECT):1 + EAS_ID (Real) = 165 + area (Real) = 596610.3359375 + +OGRFeature(SELECT):2 + EAS_ID (Real) = 170 + area (Real) = 5268.8125 +\endcode + +\subsection ogr_sql_sqlite_datasource_function OGR datasource SQL functions + +The ogr_datasource_load_layers(datasource_name[, update_mode[, prefix]]) +function can be used to automatically load all the layers of a datasource as +VirtualOGR tables.

    + +\code +sqlite> SELECT load_extension('libgdal.so'); + +sqlite> SELECT load_extension('libspatialite.so'); + +sqlite> SELECT ogr_datasource_load_layers('poly.shp'); +1 +sqlite> SELECT * FROM sqlite_master; +table|poly|poly|0|CREATE VIRTUAL TABLE "poly" USING VirtualOGR('poly.shp', 0, 'poly') +\endcode + +\subsection ogr_sql_sqlite_layer_function OGR layer SQL functions + +The following SQL functions are available and operate on a layer name : +ogr_layer_Extent(), ogr_layer_SRID(), +ogr_layer_GeometryType() and ogr_layer_FeatureCount()

    + +\code +SELECT ogr_layer_Extent('poly'), ogr_layer_SRID('poly') AS srid, + ogr_layer_GeometryType('poly') AS geomtype, ogr_layer_FeatureCount('poly') AS count + +returns: + +OGRFeature(SELECT):0 + srid (Integer) = 40004 + geomtype (String) = POLYGON + count (Integer) = 10 + POLYGON ((478315.53125 4762880.5,481645.3125 4762880.5,481645.3125 4765610.5,478315.53125 4765610.5,478315.53125 4762880.5)) +\endcode + +\subsection ogr_sql_sqlite_compression_functions OGR compression functions + +ogr_deflate(text_or_blob[, compression_level]) returns a binary blob +compressed with the ZLib deflate algorithm. See CPLZLibDeflate()

    + +ogr_inflate(compressed_blob) returns the decompressed binary blob, +from a blob compressed with the ZLib deflate algorithm. +If the decompressed binary is a string, use +CAST(ogr_inflate(compressed_blob) AS VARCHAR). See CPLZLibInflate().

    + +\subsubsection ogr_sql_sqlite_other_functions Other functions + +Starting with OGR 2.0, the hstore_get_value() function can be used to extract +a value associate to a key from a HSTORE string, formatted like "key=>value,other_key=>other_value,..." + +\code +SELECT hstore_get_value('a => b, "key with space"=> "value with space"', 'key with space') --> 'value with space' +\endcode + +\subsection ogr_sql_sqlite_ogr_geocode_function OGR geocoding functions + +The following SQL functions are available : ogr_geocode(...) and ogr_geocode_reverse(...).

    + +ogr_geocode(name_to_geocode [, field_to_return [, option1 [, option2, ...]]]) where +name_to_geocode is a literal or a column name that must be geocoded. field_to_return if specified can be "geometry" for +the geometry (default), or a field name of the layer returned by OGRGeocode(). The special field "raw" can also be used +to return the raw response (XML string) of the geocoding service. +option1, option2, etc.. must be of the key=value format, and are options understood +by OGRGeocodeCreateSession() or OGRGeocode().

    + +This function internally uses the OGRGeocode() API. Refer to it for more details. + +\code +SELECT ST_Centroid(ogr_geocode('Paris')) + +returns: + +OGRFeature(SELECT):0 + POINT (2.342878767069653 48.85661793020374) + +\endcode + +\code +ogrinfo cities.csv -dialect sqlite -sql "SELECT *, ogr_geocode(city, 'country') AS country, ST_Centroid(ogr_geocode(city)) FROM cities" + +returns: + +OGRFeature(SELECT):0 + id (Real) = 1 + city (String) = Paris + country (String) = France métropolitaine + POINT (2.342878767069653 48.85661793020374) + +OGRFeature(SELECT):1 + id (Real) = 2 + city (String) = London + country (String) = United Kingdom + POINT (-0.109369427546499 51.500506667319407) + +OGRFeature(SELECT):2 + id (Real) = 3 + city (String) = Rennes + country (String) = France métropolitaine + POINT (-1.68185153381778 48.111663929761093) + +OGRFeature(SELECT):3 + id (Real) = 4 + city (String) = Strasbourg + country (String) = France métropolitaine + POINT (7.767762859150757 48.571233274141846) + +OGRFeature(SELECT):4 + id (Real) = 5 + city (String) = New York + country (String) = United States of America + POINT (-73.938140243499049 40.663799577449979) + +OGRFeature(SELECT):5 + id (Real) = 6 + city (String) = Berlin + country (String) = Deutschland + POINT (13.402306623451983 52.501470321410636) + +OGRFeature(SELECT):6 + id (Real) = 7 + city (String) = Beijing + country (String) = 中åŽäººæ°‘共和国 + POINT (116.391195 39.9064702) + +OGRFeature(SELECT):7 + id (Real) = 8 + city (String) = Brasilia + country (String) = Brasil + POINT (-52.830435216371839 -10.828214867369699) + +OGRFeature(SELECT):8 + id (Real) = 9 + city (String) = Moscow + country (String) = РоÑÑийÑÐºÐ°Ñ Ð¤ÐµÐ´ÐµÑ€Ð°Ñ†Ð¸Ñ + POINT (37.367988106866868 55.556208255649558) +\endcode + +ogr_geocode_reverse(longitude, latitude, field_to_return [, option1 [, option2, ...]]) where +longitude, latitude is the coordinate to query. field_to_return must be a field name of the layer +returned by OGRGeocodeReverse() (for example 'display_name'). The special field "raw" can also be used +to return the raw response (XML string) of the geocoding service. +option1, option2, etc.. must be of the key=value format, and are options understood +by OGRGeocodeCreateSession() or OGRGeocodeReverse().

    + +ogr_geocode_reverse(geometry, field_to_return [, option1 [, option2, ...]]) is also accepted +as an alternate syntax where geometry is a (Spatialite) point geometry.

    + +This function internally uses the OGRGeocodeReverse() API. Refer to it for more details. + +\subsection ogr_sql_sqlite_spatial_index Spatialite spatial index + +Spatialite spatial index mechanism can be triggered by making sure a spatial index +virtual table is mentioned in the SQL (of the form idx_layername_geometrycolumn), or +by using the more recent SpatialIndex from the VirtualSpatialIndex extension. In which +case, a in-memory RTree will be built to be used to speed up the spatial queries. + +For example, a spatial intersection between 2 layers, by using a spatial index on one +of the layers to limit the number of actual geometry intersection computations : + +\code +SELECT city_name, region_name FROM cities, regions WHERE + ST_Area(ST_Intersection(cities.geometry, regions.geometry)) > 0 AND + regions.rowid IN ( + SELECT pkid FROM idx_regions_geometry WHERE + xmax >= MbrMinX(cities.geometry) AND xmin <= MbrMaxX(cities.geometry) AND + ymax >= MbrMinY(cities.geometry) AND ymin <= MbrMaxY(cities.geometry)) +\endcode + +or more elegantly : + +\code +SELECT city_name, region_name FROM cities, regions WHERE + ST_Area(ST_Intersection(cities.geometry, regions.geometry)) > 0 AND + regions.rowid IN ( + SELECT rowid FROM SpatialIndex WHERE + f_table_name = 'regions' AND search_frame = cities.geometry) +\endcode + +*/ diff --git a/bazaar/plugin/gdal/ogr/ogr_srs_api.h b/bazaar/plugin/gdal/ogr/ogr_srs_api.h new file mode 100644 index 000000000..afb08ee09 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srs_api.h @@ -0,0 +1,763 @@ +/****************************************************************************** + * $Id: ogr_srs_api.h 28972 2015-04-22 10:39:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: C API and constant declarations for OGR Spatial References. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SRS_API_H_INCLUDED +#define _OGR_SRS_API_H_INCLUDED + +#ifndef SWIG +#include "ogr_core.h" + +CPL_C_START + +/** + * \file ogr_srs_api.h + * + * C spatial reference system services and defines. + * + * See also: ogr_spatialref.h + */ + +/* -------------------------------------------------------------------- */ +/* Axis orientations (corresponds to CS_AxisOrientationEnum). */ +/* -------------------------------------------------------------------- */ +typedef enum { + OAO_Other=0, + OAO_North=1, + OAO_South=2, + OAO_East=3, + OAO_West=4, + OAO_Up=5, + OAO_Down=6 +} OGRAxisOrientation; + +const char CPL_DLL *OSRAxisEnumToName( OGRAxisOrientation eOrientation ); + +/* -------------------------------------------------------------------- */ +/* Datum types (corresponds to CS_DatumType). */ +/* -------------------------------------------------------------------- */ + +typedef enum { + ODT_HD_Min=1000, + ODT_HD_Other=1000, + ODT_HD_Classic=1001, + ODT_HD_Geocentric=1002, + ODT_HD_Max=1999, + ODT_VD_Min=2000, + ODT_VD_Other=2000, + ODT_VD_Orthometric=2001, + ODT_VD_Ellipsoidal=2002, + ODT_VD_AltitudeBarometric=2003, + ODT_VD_Normal=2004, + ODT_VD_GeoidModelDerived=2005, + ODT_VD_Depth=2006, + ODT_VD_Max=2999, + ODT_LD_Min=10000, + ODT_LD_Max=32767 +} OGRDatumType; + +#endif // ndef SWIG + +/* ==================================================================== */ +/* Some standard WKT geographic coordinate systems. */ +/* ==================================================================== */ + +#define SRS_WKT_WGS84 "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4326\"]]" + +/* ==================================================================== */ +/* Some "standard" strings. */ +/* ==================================================================== */ + +#define SRS_PT_ALBERS_CONIC_EQUAL_AREA \ + "Albers_Conic_Equal_Area" +#define SRS_PT_AZIMUTHAL_EQUIDISTANT "Azimuthal_Equidistant" +#define SRS_PT_CASSINI_SOLDNER "Cassini_Soldner" +#define SRS_PT_CYLINDRICAL_EQUAL_AREA "Cylindrical_Equal_Area" +#define SRS_PT_BONNE "Bonne" +#define SRS_PT_ECKERT_I "Eckert_I" +#define SRS_PT_ECKERT_II "Eckert_II" +#define SRS_PT_ECKERT_III "Eckert_III" +#define SRS_PT_ECKERT_IV "Eckert_IV" +#define SRS_PT_ECKERT_V "Eckert_V" +#define SRS_PT_ECKERT_VI "Eckert_VI" +#define SRS_PT_EQUIDISTANT_CONIC \ + "Equidistant_Conic" +#define SRS_PT_EQUIRECTANGULAR "Equirectangular" +#define SRS_PT_GALL_STEREOGRAPHIC \ + "Gall_Stereographic" +#define SRS_PT_GAUSSSCHREIBERTMERCATOR \ + "Gauss_Schreiber_Transverse_Mercator" +#define SRS_PT_GEOSTATIONARY_SATELLITE \ + "Geostationary_Satellite" +#define SRS_PT_GOODE_HOMOLOSINE "Goode_Homolosine" +#define SRS_PT_IGH "Interrupted_Goode_Homolosine" +#define SRS_PT_GNOMONIC "Gnomonic" +#define SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER \ + "Hotine_Oblique_Mercator_Azimuth_Center" +#define SRS_PT_HOTINE_OBLIQUE_MERCATOR \ + "Hotine_Oblique_Mercator" +#define SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN \ + "Hotine_Oblique_Mercator_Two_Point_Natural_Origin" +#define SRS_PT_LABORDE_OBLIQUE_MERCATOR \ + "Laborde_Oblique_Mercator" +#define SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP \ + "Lambert_Conformal_Conic_1SP" +#define SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP \ + "Lambert_Conformal_Conic_2SP" +#define SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM \ + "Lambert_Conformal_Conic_2SP_Belgium" +#define SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA \ + "Lambert_Azimuthal_Equal_Area" +#define SRS_PT_MERCATOR_1SP "Mercator_1SP" +#define SRS_PT_MERCATOR_2SP "Mercator_2SP" +// Mercator_Auxiliary_Sphere is used used by ESRI to mean EPSG:3875 +#define SRS_PT_MERCATOR_AUXILIARY_SPHERE \ + "Mercator_Auxiliary_Sphere" +#define SRS_PT_MILLER_CYLINDRICAL "Miller_Cylindrical" +#define SRS_PT_MOLLWEIDE "Mollweide" +#define SRS_PT_NEW_ZEALAND_MAP_GRID \ + "New_Zealand_Map_Grid" +#define SRS_PT_OBLIQUE_STEREOGRAPHIC \ + "Oblique_Stereographic" +#define SRS_PT_ORTHOGRAPHIC "Orthographic" +#define SRS_PT_POLAR_STEREOGRAPHIC \ + "Polar_Stereographic" +#define SRS_PT_POLYCONIC "Polyconic" +#define SRS_PT_ROBINSON "Robinson" +#define SRS_PT_SINUSOIDAL "Sinusoidal" +#define SRS_PT_STEREOGRAPHIC "Stereographic" +#define SRS_PT_SWISS_OBLIQUE_CYLINDRICAL \ + "Swiss_Oblique_Cylindrical" +#define SRS_PT_TRANSVERSE_MERCATOR \ + "Transverse_Mercator" +#define SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED \ + "Transverse_Mercator_South_Orientated" + +/* special mapinfo variants on Transverse Mercator */ +#define SRS_PT_TRANSVERSE_MERCATOR_MI_21 \ + "Transverse_Mercator_MapInfo_21" +#define SRS_PT_TRANSVERSE_MERCATOR_MI_22 \ + "Transverse_Mercator_MapInfo_22" +#define SRS_PT_TRANSVERSE_MERCATOR_MI_23 \ + "Transverse_Mercator_MapInfo_23" +#define SRS_PT_TRANSVERSE_MERCATOR_MI_24 \ + "Transverse_Mercator_MapInfo_24" +#define SRS_PT_TRANSVERSE_MERCATOR_MI_25 \ + "Transverse_Mercator_MapInfo_25" + +#define SRS_PT_TUNISIA_MINING_GRID \ + "Tunisia_Mining_Grid" +#define SRS_PT_TWO_POINT_EQUIDISTANT \ + "Two_Point_Equidistant" +#define SRS_PT_VANDERGRINTEN "VanDerGrinten" +#define SRS_PT_KROVAK "Krovak" +#define SRS_PT_IMW_POLYCONIC "International_Map_of_the_World_Polyconic" +#define SRS_PT_WAGNER_I "Wagner_I" +#define SRS_PT_WAGNER_II "Wagner_II" +#define SRS_PT_WAGNER_III "Wagner_III" +#define SRS_PT_WAGNER_IV "Wagner_IV" +#define SRS_PT_WAGNER_V "Wagner_V" +#define SRS_PT_WAGNER_VI "Wagner_VI" +#define SRS_PT_WAGNER_VII "Wagner_VII" +#define SRS_PT_QSC "Quadrilateralized_Spherical_Cube" +#define SRS_PT_AITOFF "Aitoff" +#define SRS_PT_WINKEL_I "Winkel_I" +#define SRS_PT_WINKEL_II "Winkel_II" +#define SRS_PT_WINKEL_TRIPEL "Winkel_Tripel" +#define SRS_PT_CRASTER_PARABOLIC "Craster_Parabolic" +#define SRS_PT_LOXIMUTHAL "Loximuthal" +#define SRS_PT_QUARTIC_AUTHALIC "Quartic_Authalic" + +#define SRS_PP_CENTRAL_MERIDIAN "central_meridian" +#define SRS_PP_SCALE_FACTOR "scale_factor" +#define SRS_PP_STANDARD_PARALLEL_1 "standard_parallel_1" +#define SRS_PP_STANDARD_PARALLEL_2 "standard_parallel_2" +#define SRS_PP_PSEUDO_STD_PARALLEL_1 "pseudo_standard_parallel_1" +#define SRS_PP_LONGITUDE_OF_CENTER "longitude_of_center" +#define SRS_PP_LATITUDE_OF_CENTER "latitude_of_center" +#define SRS_PP_LONGITUDE_OF_ORIGIN "longitude_of_origin" +#define SRS_PP_LATITUDE_OF_ORIGIN "latitude_of_origin" +#define SRS_PP_FALSE_EASTING "false_easting" +#define SRS_PP_FALSE_NORTHING "false_northing" +#define SRS_PP_AZIMUTH "azimuth" +#define SRS_PP_LONGITUDE_OF_POINT_1 "longitude_of_point_1" +#define SRS_PP_LATITUDE_OF_POINT_1 "latitude_of_point_1" +#define SRS_PP_LONGITUDE_OF_POINT_2 "longitude_of_point_2" +#define SRS_PP_LATITUDE_OF_POINT_2 "latitude_of_point_2" +#define SRS_PP_LONGITUDE_OF_POINT_3 "longitude_of_point_3" +#define SRS_PP_LATITUDE_OF_POINT_3 "latitude_of_point_3" +#define SRS_PP_RECTIFIED_GRID_ANGLE "rectified_grid_angle" +#define SRS_PP_LANDSAT_NUMBER "landsat_number" +#define SRS_PP_PATH_NUMBER "path_number" +#define SRS_PP_PERSPECTIVE_POINT_HEIGHT "perspective_point_height" +#define SRS_PP_SATELLITE_HEIGHT "satellite_height" +#define SRS_PP_FIPSZONE "fipszone" +#define SRS_PP_ZONE "zone" +#define SRS_PP_LATITUDE_OF_1ST_POINT "Latitude_Of_1st_Point" +#define SRS_PP_LONGITUDE_OF_1ST_POINT "Longitude_Of_1st_Point" +#define SRS_PP_LATITUDE_OF_2ND_POINT "Latitude_Of_2nd_Point" +#define SRS_PP_LONGITUDE_OF_2ND_POINT "Longitude_Of_2nd_Point" + +#define SRS_UL_METER "Meter" +#define SRS_UL_FOOT "Foot (International)" /* or just "FOOT"? */ +#define SRS_UL_FOOT_CONV "0.3048" +#define SRS_UL_US_FOOT "Foot_US" /* or "US survey foot" from EPSG */ +#define SRS_UL_US_FOOT_CONV "0.3048006096012192" +#define SRS_UL_NAUTICAL_MILE "Nautical Mile" +#define SRS_UL_NAUTICAL_MILE_CONV "1852.0" +#define SRS_UL_LINK "Link" /* Based on US Foot */ +#define SRS_UL_LINK_CONV "0.20116684023368047" +#define SRS_UL_CHAIN "Chain" /* based on US Foot */ +#define SRS_UL_CHAIN_CONV "20.116684023368047" +#define SRS_UL_ROD "Rod" /* based on US Foot */ +#define SRS_UL_ROD_CONV "5.02921005842012" +#define SRS_UL_LINK_Clarke "Link_Clarke" +#define SRS_UL_LINK_Clarke_CONV "0.2011661949" + +#define SRS_UL_KILOMETER "Kilometer" +#define SRS_UL_KILOMETER_CONV "1000." +#define SRS_UL_DECIMETER "Decimeter" +#define SRS_UL_DECIMETER_CONV "0.1" +#define SRS_UL_CENTIMETER "Centimeter" +#define SRS_UL_CENTIMETER_CONV "0.01" +#define SRS_UL_MILLIMETER "Millimeter" +#define SRS_UL_MILLIMETER_CONV "0.001" +#define SRS_UL_INTL_NAUT_MILE "Nautical_Mile_International" +#define SRS_UL_INTL_NAUT_MILE_CONV "1852.0" +#define SRS_UL_INTL_INCH "Inch_International" +#define SRS_UL_INTL_INCH_CONV "0.0254" +#define SRS_UL_INTL_FOOT "Foot_International" +#define SRS_UL_INTL_FOOT_CONV "0.3048" +#define SRS_UL_INTL_YARD "Yard_International" +#define SRS_UL_INTL_YARD_CONV "0.9144" +#define SRS_UL_INTL_STAT_MILE "Statute_Mile_International" +#define SRS_UL_INTL_STAT_MILE_CONV "1609.344" +#define SRS_UL_INTL_FATHOM "Fathom_International" +#define SRS_UL_INTL_FATHOM_CONV "1.8288" +#define SRS_UL_INTL_CHAIN "Chain_International" +#define SRS_UL_INTL_CHAIN_CONV "20.1168" +#define SRS_UL_INTL_LINK "Link_International" +#define SRS_UL_INTL_LINK_CONV "0.201168" +#define SRS_UL_US_INCH "Inch_US_Surveyor" +#define SRS_UL_US_INCH_CONV "0.025400050800101603" +#define SRS_UL_US_YARD "Yard_US_Surveyor" +#define SRS_UL_US_YARD_CONV "0.914401828803658" +#define SRS_UL_US_CHAIN "Chain_US_Surveyor" +#define SRS_UL_US_CHAIN_CONV "20.11684023368047" +#define SRS_UL_US_STAT_MILE "Statute_Mile_US_Surveyor" +#define SRS_UL_US_STAT_MILE_CONV "1609.347218694437" +#define SRS_UL_INDIAN_YARD "Yard_Indian" +#define SRS_UL_INDIAN_YARD_CONV "0.91439523" +#define SRS_UL_INDIAN_FOOT "Foot_Indian" +#define SRS_UL_INDIAN_FOOT_CONV "0.30479841" +#define SRS_UL_INDIAN_CHAIN "Chain_Indian" +#define SRS_UL_INDIAN_CHAIN_CONV "20.11669506" + +#define SRS_UA_DEGREE "degree" +#define SRS_UA_DEGREE_CONV "0.0174532925199433" +#define SRS_UA_RADIAN "radian" + +#define SRS_PM_GREENWICH "Greenwich" + +#define SRS_DN_NAD27 "North_American_Datum_1927" +#define SRS_DN_NAD83 "North_American_Datum_1983" +#define SRS_DN_WGS72 "WGS_1972" +#define SRS_DN_WGS84 "WGS_1984" + +#define SRS_WGS84_SEMIMAJOR 6378137.0 +#define SRS_WGS84_INVFLATTENING 298.257223563 + +#ifndef SWIG +/* -------------------------------------------------------------------- */ +/* C Wrappers for C++ objects and methods. */ +/* -------------------------------------------------------------------- */ +#ifndef _DEFINED_OGRSpatialReferenceH +#define _DEFINED_OGRSpatialReferenceH + +#ifdef DEBUG +typedef struct OGRSpatialReferenceHS *OGRSpatialReferenceH; +typedef struct OGRCoordinateTransformationHS *OGRCoordinateTransformationH; +#else +typedef void *OGRSpatialReferenceH; +typedef void *OGRCoordinateTransformationH; +#endif + +#endif + + +OGRSpatialReferenceH CPL_DLL CPL_STDCALL + OSRNewSpatialReference( const char * /* = NULL */); +OGRSpatialReferenceH CPL_DLL CPL_STDCALL OSRCloneGeogCS( OGRSpatialReferenceH ); +OGRSpatialReferenceH CPL_DLL CPL_STDCALL OSRClone( OGRSpatialReferenceH ); +void CPL_DLL CPL_STDCALL OSRDestroySpatialReference( OGRSpatialReferenceH ); + +int CPL_DLL OSRReference( OGRSpatialReferenceH ); +int CPL_DLL OSRDereference( OGRSpatialReferenceH ); +void CPL_DLL OSRRelease( OGRSpatialReferenceH ); + +OGRErr CPL_DLL OSRValidate( OGRSpatialReferenceH ); +OGRErr CPL_DLL OSRFixupOrdering( OGRSpatialReferenceH ); +OGRErr CPL_DLL OSRFixup( OGRSpatialReferenceH ); +OGRErr CPL_DLL OSRStripCTParms( OGRSpatialReferenceH ); + +OGRErr CPL_DLL CPL_STDCALL OSRImportFromEPSG( OGRSpatialReferenceH, int ); +OGRErr CPL_DLL CPL_STDCALL OSRImportFromEPSGA( OGRSpatialReferenceH, int ); +OGRErr CPL_DLL OSRImportFromWkt( OGRSpatialReferenceH, char ** ); +OGRErr CPL_DLL OSRImportFromProj4( OGRSpatialReferenceH, const char *); +OGRErr CPL_DLL OSRImportFromESRI( OGRSpatialReferenceH, char **); +OGRErr CPL_DLL OSRImportFromPCI( OGRSpatialReferenceH hSRS, const char *, + const char *, double * ); +OGRErr CPL_DLL OSRImportFromUSGS( OGRSpatialReferenceH, + long, long, double *, long); +OGRErr CPL_DLL OSRImportFromXML( OGRSpatialReferenceH, const char * ); +OGRErr CPL_DLL OSRImportFromDict( OGRSpatialReferenceH, const char *, + const char * ); +OGRErr CPL_DLL OSRImportFromPanorama( OGRSpatialReferenceH, long, long, long, + double * ); +OGRErr CPL_DLL OSRImportFromOzi( OGRSpatialReferenceH , const char * const *); +OGRErr CPL_DLL OSRImportFromMICoordSys( OGRSpatialReferenceH, const char *); +OGRErr CPL_DLL OSRImportFromERM( OGRSpatialReferenceH, + const char *, const char *, const char * ); +OGRErr CPL_DLL OSRImportFromUrl( OGRSpatialReferenceH, const char * ); + +OGRErr CPL_DLL CPL_STDCALL OSRExportToWkt( OGRSpatialReferenceH, char ** ); +OGRErr CPL_DLL CPL_STDCALL OSRExportToPrettyWkt( OGRSpatialReferenceH, char **, int); +OGRErr CPL_DLL CPL_STDCALL OSRExportToProj4( OGRSpatialReferenceH, char **); +OGRErr CPL_DLL OSRExportToPCI( OGRSpatialReferenceH, char **, char **, + double ** ); +OGRErr CPL_DLL OSRExportToUSGS( OGRSpatialReferenceH, long *, long *, + double **, long * ); +OGRErr CPL_DLL OSRExportToXML( OGRSpatialReferenceH, char **, const char * ); +OGRErr CPL_DLL OSRExportToPanorama( OGRSpatialReferenceH, long *, long *, + long *, long *, double * ); +OGRErr CPL_DLL OSRExportToMICoordSys( OGRSpatialReferenceH, char ** ); +OGRErr CPL_DLL OSRExportToERM( OGRSpatialReferenceH, char *, char *, char * ); + +OGRErr CPL_DLL OSRMorphToESRI( OGRSpatialReferenceH ); +OGRErr CPL_DLL OSRMorphFromESRI( OGRSpatialReferenceH ); + +OGRErr CPL_DLL CPL_STDCALL OSRSetAttrValue( OGRSpatialReferenceH hSRS, + const char * pszNodePath, + const char * pszNewNodeValue ); +const char CPL_DLL * CPL_STDCALL OSRGetAttrValue( OGRSpatialReferenceH hSRS, + const char * pszName, int iChild /* = 0 */ ); + +OGRErr CPL_DLL OSRSetAngularUnits( OGRSpatialReferenceH, const char *, double ); +double CPL_DLL OSRGetAngularUnits( OGRSpatialReferenceH, char ** ); +OGRErr CPL_DLL OSRSetLinearUnits( OGRSpatialReferenceH, const char *, double ); +OGRErr CPL_DLL OSRSetTargetLinearUnits( OGRSpatialReferenceH, const char *, const char *, double ); +OGRErr CPL_DLL OSRSetLinearUnitsAndUpdateParameters( + OGRSpatialReferenceH, const char *, double ); +double CPL_DLL OSRGetLinearUnits( OGRSpatialReferenceH, char ** ); +double CPL_DLL OSRGetTargetLinearUnits( OGRSpatialReferenceH, const char *, char ** ); + +double CPL_DLL OSRGetPrimeMeridian( OGRSpatialReferenceH, char ** ); + +int CPL_DLL OSRIsGeographic( OGRSpatialReferenceH ); +int CPL_DLL OSRIsLocal( OGRSpatialReferenceH ); +int CPL_DLL OSRIsProjected( OGRSpatialReferenceH ); +int CPL_DLL OSRIsCompound( OGRSpatialReferenceH ); +int CPL_DLL OSRIsGeocentric( OGRSpatialReferenceH ); +int CPL_DLL OSRIsVertical( OGRSpatialReferenceH ); +int CPL_DLL OSRIsSameGeogCS( OGRSpatialReferenceH, OGRSpatialReferenceH ); +int CPL_DLL OSRIsSameVertCS( OGRSpatialReferenceH, OGRSpatialReferenceH ); +int CPL_DLL OSRIsSame( OGRSpatialReferenceH, OGRSpatialReferenceH ); + +OGRErr CPL_DLL OSRSetLocalCS( OGRSpatialReferenceH hSRS, const char *pszName ); +OGRErr CPL_DLL OSRSetProjCS( OGRSpatialReferenceH hSRS, const char * pszName ); +OGRErr CPL_DLL OSRSetGeocCS( OGRSpatialReferenceH hSRS, const char * pszName ); +OGRErr CPL_DLL OSRSetWellKnownGeogCS( OGRSpatialReferenceH hSRS, + const char * pszName ); +OGRErr CPL_DLL CPL_STDCALL OSRSetFromUserInput( OGRSpatialReferenceH hSRS, + const char * ); +OGRErr CPL_DLL OSRCopyGeogCSFrom( OGRSpatialReferenceH hSRS, + OGRSpatialReferenceH hSrcSRS ); +OGRErr CPL_DLL OSRSetTOWGS84( OGRSpatialReferenceH hSRS, + double, double, double, + double, double, double, double ); +OGRErr CPL_DLL OSRGetTOWGS84( OGRSpatialReferenceH hSRS, double *, int ); + + +OGRErr CPL_DLL OSRSetCompoundCS( OGRSpatialReferenceH hSRS, + const char *pszName, + OGRSpatialReferenceH hHorizSRS, + OGRSpatialReferenceH hVertSRS ); +OGRErr CPL_DLL OSRSetGeogCS( OGRSpatialReferenceH hSRS, + const char * pszGeogName, + const char * pszDatumName, + const char * pszEllipsoidName, + double dfSemiMajor, double dfInvFlattening, + const char * pszPMName /* = NULL */, + double dfPMOffset /* = 0.0 */, + const char * pszUnits /* = NULL */, + double dfConvertToRadians /* = 0.0 */ ); + +OGRErr CPL_DLL OSRSetVertCS( OGRSpatialReferenceH hSRS, + const char * pszVertCSName, + const char * pszVertDatumName, + int nVertDatumType ); + +double CPL_DLL OSRGetSemiMajor( OGRSpatialReferenceH, OGRErr * /* = NULL */ ); +double CPL_DLL OSRGetSemiMinor( OGRSpatialReferenceH, OGRErr * /* = NULL */ ); +double CPL_DLL OSRGetInvFlattening( OGRSpatialReferenceH, OGRErr * /*=NULL*/); + +OGRErr CPL_DLL OSRSetAuthority( OGRSpatialReferenceH hSRS, + const char * pszTargetKey, + const char * pszAuthority, + int nCode ); +const char CPL_DLL *OSRGetAuthorityCode( OGRSpatialReferenceH hSRS, + const char * pszTargetKey ); +const char CPL_DLL *OSRGetAuthorityName( OGRSpatialReferenceH hSRS, + const char * pszTargetKey ); +OGRErr CPL_DLL OSRSetProjection( OGRSpatialReferenceH, const char * ); +OGRErr CPL_DLL OSRSetProjParm( OGRSpatialReferenceH, const char *, double ); +double CPL_DLL OSRGetProjParm( OGRSpatialReferenceH hSRS, + const char * pszParmName, + double dfDefault /* = 0.0 */, + OGRErr * /* = NULL */ ); +OGRErr CPL_DLL OSRSetNormProjParm( OGRSpatialReferenceH, const char *, double); +double CPL_DLL OSRGetNormProjParm( OGRSpatialReferenceH hSRS, + const char * pszParmName, + double dfDefault /* = 0.0 */, + OGRErr * /* = NULL */ ); + +OGRErr CPL_DLL OSRSetUTM( OGRSpatialReferenceH hSRS, int nZone, int bNorth ); +int CPL_DLL OSRGetUTMZone( OGRSpatialReferenceH hSRS, int *pbNorth ); +OGRErr CPL_DLL OSRSetStatePlane( OGRSpatialReferenceH hSRS, + int nZone, int bNAD83 ); +OGRErr CPL_DLL OSRSetStatePlaneWithUnits( OGRSpatialReferenceH hSRS, + int nZone, int bNAD83, + const char *pszOverrideUnitName, + double dfOverrideUnit ); +OGRErr CPL_DLL OSRAutoIdentifyEPSG( OGRSpatialReferenceH hSRS ); + +int CPL_DLL OSREPSGTreatsAsLatLong( OGRSpatialReferenceH hSRS ); +int CPL_DLL OSREPSGTreatsAsNorthingEasting( OGRSpatialReferenceH hSRS ); +const char CPL_DLL *OSRGetAxis( OGRSpatialReferenceH hSRS, + const char *pszTargetKey, int iAxis, + OGRAxisOrientation *peOrientation ); +OGRErr CPL_DLL OSRSetAxes( const char *pszTargetKey, + const char *pszXAxisName, + OGRAxisOrientation eXAxisOrientation, + const char *pszYAxisName, + OGRAxisOrientation eYAxisOrientation ); +/** Albers Conic Equal Area */ +OGRErr CPL_DLL OSRSetACEA( OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Azimuthal Equidistant */ +OGRErr CPL_DLL OSRSetAE( OGRSpatialReferenceH hSRS, double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Bonne */ +OGRErr CPL_DLL OSRSetBonne(OGRSpatialReferenceH hSRS, + double dfStandardParallel, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + +/** Cylindrical Equal Area */ +OGRErr CPL_DLL OSRSetCEA( OGRSpatialReferenceH hSRS, double dfStdP1, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + +/** Cassini-Soldner */ +OGRErr CPL_DLL OSRSetCS( OGRSpatialReferenceH hSRS, double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Equidistant Conic */ +OGRErr CPL_DLL OSRSetEC( OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Eckert I-VI */ +OGRErr CPL_DLL OSRSetEckert( OGRSpatialReferenceH hSRS, int nVariation, + double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + +/** Eckert IV */ +OGRErr CPL_DLL OSRSetEckertIV( OGRSpatialReferenceH hSRS, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + +/** Eckert VI */ +OGRErr CPL_DLL OSRSetEckertVI( OGRSpatialReferenceH hSRS, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + +/** Equirectangular */ +OGRErr CPL_DLL OSRSetEquirectangular(OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Equirectangular generalized form */ +OGRErr CPL_DLL OSRSetEquirectangular2( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfPseudoStdParallel1, + double dfFalseEasting, + double dfFalseNorthing ); + +/** Gall Stereograpic */ +OGRErr CPL_DLL OSRSetGS( OGRSpatialReferenceH hSRS, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + +/** Goode Homolosine */ +OGRErr CPL_DLL OSRSetGH( OGRSpatialReferenceH hSRS, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + +/** Interrupted Goode Homolosine */ +OGRErr CPL_DLL OSRSetIGH( OGRSpatialReferenceH hSRS ); + +/** GEOS - Geostationary Satellite View */ +OGRErr CPL_DLL OSRSetGEOS( OGRSpatialReferenceH hSRS, + double dfCentralMeridian, double dfSatelliteHeight, + double dfFalseEasting, double dfFalseNorthing ); + +/** Gauss Schreiber Transverse Mercator */ +OGRErr CPL_DLL OSRSetGaussSchreiberTMercator( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ); +/** Gnomonic */ +OGRErr CPL_DLL OSRSetGnomonic(OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Oblique Mercator (aka HOM (variant B) */ +OGRErr CPL_DLL OSRSetOM( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + +/** Hotine Oblique Mercator using azimuth angle */ +OGRErr CPL_DLL OSRSetHOM( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + +/** Hotine Oblique Mercator using two points on centerline */ +OGRErr CPL_DLL OSRSetHOM2PNO( OGRSpatialReferenceH hSRS, double dfCenterLat, + double dfLat1, double dfLong1, + double dfLat2, double dfLong2, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + +/** International Map of the World Polyconic */ +OGRErr CPL_DLL OSRSetIWMPolyconic( OGRSpatialReferenceH hSRS, + double dfLat1, double dfLat2, + double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ); + +/** Krovak Oblique Conic Conformal */ +OGRErr CPL_DLL OSRSetKrovak( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfPseudoStdParallelLat, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + +/** Lambert Azimuthal Equal-Area */ +OGRErr CPL_DLL OSRSetLAEA( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Lambert Conformal Conic */ +OGRErr CPL_DLL OSRSetLCC( OGRSpatialReferenceH hSRS, + double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Lambert Conformal Conic 1SP */ +OGRErr CPL_DLL OSRSetLCC1SP( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + +/** Lambert Conformal Conic (Belgium) */ +OGRErr CPL_DLL OSRSetLCCB( OGRSpatialReferenceH hSRS, + double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Miller Cylindrical */ +OGRErr CPL_DLL OSRSetMC( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Mercator */ +OGRErr CPL_DLL OSRSetMercator( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + +/** Mollweide */ +OGRErr CPL_DLL OSRSetMollweide( OGRSpatialReferenceH hSRS, + double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ); + +/** New Zealand Map Grid */ +OGRErr CPL_DLL OSRSetNZMG( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Oblique Stereographic */ +OGRErr CPL_DLL OSRSetOS( OGRSpatialReferenceH hSRS, + double dfOriginLat, double dfCMeridian, + double dfScale, + double dfFalseEasting,double dfFalseNorthing); + +/** Orthographic */ +OGRErr CPL_DLL OSRSetOrthographic( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing); + +/** Polyconic */ +OGRErr CPL_DLL OSRSetPolyconic( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Polar Stereographic */ +OGRErr CPL_DLL OSRSetPS( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing); + +/** Robinson */ +OGRErr CPL_DLL OSRSetRobinson( OGRSpatialReferenceH hSRS, + double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Sinusoidal */ +OGRErr CPL_DLL OSRSetSinusoidal( OGRSpatialReferenceH hSRS, + double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ); + +/** Stereographic */ +OGRErr CPL_DLL OSRSetStereographic( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing); + +/** Swiss Oblique Cylindrical */ +OGRErr CPL_DLL OSRSetSOC( OGRSpatialReferenceH hSRS, + double dfLatitudeOfOrigin, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ); + +/** Transverse Mercator + * + * Special processing available for Transverse Mercator with GDAL >= 1.10 and PROJ >= 4.8 : + * see OGRSpatialReference::exportToProj4(). + */ + +OGRErr CPL_DLL OSRSetTM( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + +/** Transverse Mercator variant */ +OGRErr CPL_DLL OSRSetTMVariant( + OGRSpatialReferenceH hSRS, const char *pszVariantName, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + +/** Tunesia Mining Grid */ +OGRErr CPL_DLL OSRSetTMG( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Transverse Mercator (South Oriented) */ +OGRErr CPL_DLL OSRSetTMSO( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ); + +/** VanDerGrinten */ +OGRErr CPL_DLL OSRSetVDG( OGRSpatialReferenceH hSRS, + double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ); + +/** Wagner I -- VII */ +OGRErr CPL_DLL OSRSetWagner( OGRSpatialReferenceH hSRS, int nVariation, + double dfFalseEasting, + double dfFalseNorthing ); + +/** Quadrilateralized Spherical Cube */ +OGRErr CPL_DLL OSRSetQSC( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong ); + +double CPL_DLL OSRCalcInvFlattening( double dfSemiMajor, double dfSemiMinor ); +double CPL_DLL OSRCalcSemiMinorFromInvFlattening( double dfSemiMajor, double dfInvFlattening ); + +void CPL_DLL OSRCleanup( void ); + +/* -------------------------------------------------------------------- */ +/* OGRCoordinateTransform C API. */ +/* -------------------------------------------------------------------- */ +OGRCoordinateTransformationH CPL_DLL CPL_STDCALL +OCTNewCoordinateTransformation( OGRSpatialReferenceH hSourceSRS, + OGRSpatialReferenceH hTargetSRS ); +void CPL_DLL CPL_STDCALL + OCTDestroyCoordinateTransformation( OGRCoordinateTransformationH ); + +int CPL_DLL CPL_STDCALL +OCTTransform( OGRCoordinateTransformationH hCT, + int nCount, double *x, double *y, double *z ); + +int CPL_DLL CPL_STDCALL +OCTTransformEx( OGRCoordinateTransformationH hCT, + int nCount, double *x, double *y, double *z, + int *pabSuccess ); + +/* this is really private to OGR. */ +char *OCTProj4Normalize( const char *pszProj4Src ); + +void OCTCleanupProjMutex( void ); + +/* -------------------------------------------------------------------- */ +/* Projection transform dictionary query. */ +/* -------------------------------------------------------------------- */ + +char CPL_DLL ** OPTGetProjectionMethods( void ); +char CPL_DLL ** OPTGetParameterList( const char * pszProjectionMethod, + char ** ppszUserName ); +int CPL_DLL OPTGetParameterInfo( const char * pszProjectionMethod, + const char * pszParameterName, + char ** ppszUserName, + char ** ppszType, + double *pdfDefaultValue ); + +CPL_C_END + +#endif /* ndef SWIG */ + +#endif /* ndef _OGR_SRS_API_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogr_srs_dict.cpp b/bazaar/plugin/gdal/ogr/ogr_srs_dict.cpp new file mode 100644 index 000000000..f928ad44a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srs_dict.cpp @@ -0,0 +1,134 @@ +/****************************************************************************** + * $Id: ogr_srs_dict.cpp 11881 2007-08-13 18:03:48Z mloskot $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implement importFromDict() method to read a WKT SRS from a + * coordinate system dictionary in a simple text format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogr_srs_dict.cpp 11881 2007-08-13 18:03:48Z mloskot $"); + + +/************************************************************************/ +/* importFromDict() */ +/************************************************************************/ + +/** + * Read SRS from WKT dictionary. + * + * This method will attempt to find the indicated coordinate system identity + * in the indicated dictionary file. If found, the WKT representation is + * imported and used to initialize this OGRSpatialReference. + * + * More complete information on the format of the dictionary files can + * be found in the epsg.wkt file in the GDAL data tree. The dictionary + * files are searched for in the "GDAL" domain using CPLFindFile(). Normally + * this results in searching /usr/local/share/gdal or somewhere similar. + * + * This method is the same as the C function OSRImportFromDict(). + * + * @param pszDictFile the name of the dictionary file to load. + * + * @param pszCode the code to lookup in the dictionary. + * + * @return OGRERR_NONE on success, or OGRERR_SRS_UNSUPPORTED if the code isn't + * found, and OGRERR_SRS_FAILURE if something more dramatic goes wrong. + */ + +OGRErr OGRSpatialReference::importFromDict( const char *pszDictFile, + const char *pszCode ) + +{ + const char *pszFilename; + FILE *fp; + OGRErr eErr = OGRERR_UNSUPPORTED_SRS; + +/* -------------------------------------------------------------------- */ +/* Find and open file. */ +/* -------------------------------------------------------------------- */ + pszFilename = CPLFindFile( "gdal", pszDictFile ); + if( pszFilename == NULL ) + return OGRERR_UNSUPPORTED_SRS; + + fp = VSIFOpen( pszFilename, "rb" ); + if( fp == NULL ) + return OGRERR_UNSUPPORTED_SRS; + +/* -------------------------------------------------------------------- */ +/* Process lines. */ +/* -------------------------------------------------------------------- */ + const char *pszLine; + + while( (pszLine = CPLReadLine(fp)) != NULL ) + + { + if( pszLine[0] == '#' ) + /* do nothing */; + + else if( EQUALN(pszLine,"include ",8) ) + { + eErr = importFromDict( pszLine + 8, pszCode ); + if( eErr != OGRERR_UNSUPPORTED_SRS ) + break; + } + + else if( strstr(pszLine,",") == NULL ) + /* do nothing */; + + else if( EQUALN(pszLine,pszCode,strlen(pszCode)) + && pszLine[strlen(pszCode)] == ',' ) + { + char *pszWKT = (char *) pszLine + strlen(pszCode)+1; + + eErr = importFromWkt( &pszWKT ); + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + VSIFClose( fp ); + + return eErr; +} + +/************************************************************************/ +/* OSRImportFromDict() */ +/************************************************************************/ + +OGRErr OSRImportFromDict( OGRSpatialReferenceH hSRS, + const char *pszDictFile, + const char *pszCode ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromDict", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromDict( pszDictFile, + pszCode ); +} diff --git a/bazaar/plugin/gdal/ogr/ogr_srs_erm.cpp b/bazaar/plugin/gdal/ogr/ogr_srs_erm.cpp new file mode 100644 index 000000000..62a6e8ee8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srs_erm.cpp @@ -0,0 +1,344 @@ +/****************************************************************************** + * $Id: ogr_srs_erm.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implement ERMapper projection conversions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogr_srs_erm.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* OSRImportFromERM() */ +/************************************************************************/ + +/** + * \brief Create OGR WKT from ERMapper projection definitions. + * + * This function is the same as OGRSpatialReference::importFromERM(). + */ + +OGRErr OSRImportFromERM( OGRSpatialReferenceH hSRS, const char *pszProj, + const char *pszDatum, const char *pszUnits ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromERM", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromERM( pszProj, + pszDatum, + pszUnits ); +} + +/************************************************************************/ +/* importFromERM() */ +/************************************************************************/ + +/** + * Create OGR WKT from ERMapper projection definitions. + * + * Generates an OGRSpatialReference definition from an ERMapper datum + * and projection name. Based on the ecw_cs.wkt dictionary file from + * gdal/data. + * + * @param pszProj the projection name, such as "NUTM11" or "GEOGRAPHIC". + * @param pszDatum the datum name, such as "NAD83". + * @param pszUnits the linear units "FEET" or "METERS". + * + * @return OGRERR_NONE on success or OGRERR_UNSUPPORTED_SRS if not found. + */ + +OGRErr OGRSpatialReference::importFromERM( const char *pszProj, + const char *pszDatum, + const char *pszUnits ) + +{ + Clear(); + +/* -------------------------------------------------------------------- */ +/* do we have projection and datum? */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszProj,"RAW") ) + return OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* Do we have an EPSG coordinate system? */ +/* -------------------------------------------------------------------- */ + + if( EQUALN(pszProj,"EPSG:",5) ) + return importFromEPSG( atoi(pszProj+5) ); + + + if( EQUALN(pszDatum,"EPSG:",5) ) + return importFromEPSG( atoi(pszDatum+5) ); + +/* -------------------------------------------------------------------- */ +/* Set projection if we have it. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr; + + if( EQUAL(pszProj,"GEODETIC") ) + { + } + else + { + eErr = importFromDict( "ecw_cs.wkt", pszProj ); + if( eErr != OGRERR_NONE ) + return eErr; + + if( EQUAL(pszUnits,"FEET") ) + SetLinearUnits( SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV)); + else + SetLinearUnits( SRS_UL_METER, 1.0 ); + } + +/* -------------------------------------------------------------------- */ +/* Set the geogcs. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oGeogCS; + + eErr = oGeogCS.importFromDict( "ecw_cs.wkt", pszDatum ); + if( eErr != OGRERR_NONE ) + { + Clear(); + return eErr; + } + + if( !IsLocal() ) + CopyGeogCSFrom( &oGeogCS ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRExportToERM() */ +/************************************************************************/ +/** + * \brief Convert coordinate system to ERMapper format. + * + * This function is the same as OGRSpatialReference::exportToERM(). + */ +OGRErr OSRExportToERM( OGRSpatialReferenceH hSRS, + char *pszProj, char *pszDatum, char *pszUnits ) + +{ + VALIDATE_POINTER1( hSRS, "OSRExportToERM", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->exportToERM( pszProj, pszDatum, + pszUnits ); +} + +/************************************************************************/ +/* exportToERM() */ +/************************************************************************/ + +/** + * Convert coordinate system to ERMapper format. + * + * @param pszProj 32 character buffer to receive projection name. + * @param pszDatum 32 character buffer to recieve datum name. + * @param pszUnits 32 character buffer to receive units name. + * + * @return OGRERR_NONE on success, OGRERR_SRS_UNSUPPORTED if not translation is + * found, or OGRERR_FAILURE on other failures. + */ + +OGRErr OGRSpatialReference::exportToERM( char *pszProj, char *pszDatum, + char *pszUnits ) + +{ + strcpy( pszProj, "RAW" ); + strcpy( pszDatum, "RAW" ); + strcpy( pszUnits, "METERS" ); + + if( !IsProjected() && !IsGeographic() ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Try to find the EPSG code. */ +/* -------------------------------------------------------------------- */ + int nEPSGCode = 0; + + if( IsProjected() ) + { + const char *pszAuthName = GetAuthorityName( "PROJCS" ); + + if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") ) + { + nEPSGCode = atoi(GetAuthorityCode( "PROJCS" )); + } + } + else if( IsGeographic() ) + { + const char *pszAuthName = GetAuthorityName( "GEOGCS" ); + + if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") ) + { + nEPSGCode = atoi(GetAuthorityCode( "GEOGCS" )); + } + } + +/* -------------------------------------------------------------------- */ +/* Is our GEOGCS name already defined in ecw_cs.wkt? */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRSWork; + const char *pszWKTDatum = GetAttrValue( "DATUM" ); + + if( pszWKTDatum != NULL + && oSRSWork.importFromDict( "ecw_cs.wkt", pszWKTDatum ) == OGRERR_NONE) + { + strncpy( pszDatum, pszWKTDatum, 32 ); + pszDatum[31] = '\0'; + } + +/* -------------------------------------------------------------------- */ +/* Is this a "well known" geographic coordinate system? */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszDatum,"RAW") ) + { + int nEPSGGCSCode = GetEPSGGeogCS(); + + if( nEPSGGCSCode == 4326 ) + strcpy( pszDatum, "WGS84" ); + + else if( nEPSGGCSCode == 4322 ) + strcpy( pszDatum, "WGS72DOD" ); + + else if( nEPSGGCSCode == 4267 ) + strcpy( pszDatum, "NAD27" ); + + else if( nEPSGGCSCode == 4269 ) + strcpy( pszDatum, "NAD83" ); + + else if( nEPSGGCSCode == 4277 ) + strcpy( pszDatum, "OSGB36" ); + + else if( nEPSGGCSCode == 4278 ) + strcpy( pszDatum, "OSGB78" ); + + else if( nEPSGGCSCode == 4201 ) + strcpy( pszDatum, "ADINDAN" ); + + else if( nEPSGGCSCode == 4202 ) + strcpy( pszDatum, "AGD66" ); + + else if( nEPSGGCSCode == 4203 ) + strcpy( pszDatum, "AGD84" ); + + else if( nEPSGGCSCode == 4209 ) + strcpy( pszDatum, "ARC1950" ); + + else if( nEPSGGCSCode == 4210 ) + strcpy( pszDatum, "ARC1960" ); + + else if( nEPSGGCSCode == 4275 ) + strcpy( pszDatum, "NTF" ); + + else if( nEPSGGCSCode == 4283 ) + strcpy( pszDatum, "GDA94" ); + + else if( nEPSGGCSCode == 4284 ) + strcpy( pszDatum, "PULKOVO" ); + } + +/* -------------------------------------------------------------------- */ +/* Are we working with a geographic (geodetic) coordinate system? */ +/* -------------------------------------------------------------------- */ + + if( IsGeographic() ) + { + if( EQUAL(pszDatum,"RAW") ) + return OGRERR_UNSUPPORTED_SRS; + else + { + strcpy( pszProj, "GEODETIC" ); + return OGRERR_NONE; + } + } + +/* -------------------------------------------------------------------- */ +/* Is this a UTM projection? */ +/* -------------------------------------------------------------------- */ + int bNorth, nZone; + + nZone = GetUTMZone( &bNorth ); + if( nZone > 0 ) + { + if( EQUAL(pszDatum,"GDA94") && !bNorth && nZone >= 48 && nZone <= 58) + { + sprintf( pszProj, "MGA%02d", nZone ); + } + else + { + if( bNorth ) + sprintf( pszProj, "NUTM%02d", nZone ); + else + sprintf( pszProj, "SUTM%02d", nZone ); + } + } + +/* -------------------------------------------------------------------- */ +/* Is our PROJCS name already defined in ecw_cs.wkt? */ +/* -------------------------------------------------------------------- */ + else + { + const char *pszPROJCS = GetAttrValue( "PROJCS" ); + + if( pszPROJCS != NULL + && oSRSWork.importFromDict( "ecw_cs.wkt", pszPROJCS ) == OGRERR_NONE + && oSRSWork.IsProjected() ) + { + strncpy( pszProj, pszPROJCS, 32 ); + pszProj[31] = '\0'; + } + } + +/* -------------------------------------------------------------------- */ +/* If we have not translated it yet, but we have an EPSG code */ +/* then use EPSG:n notation. */ +/* -------------------------------------------------------------------- */ + if( (EQUAL(pszDatum,"RAW") || EQUAL(pszProj,"RAW")) && nEPSGCode != 0 ) + { + sprintf( pszProj, "EPSG:%d", nEPSGCode ); + sprintf( pszDatum, "EPSG:%d", nEPSGCode ); + } + +/* -------------------------------------------------------------------- */ +/* Handle the units. */ +/* -------------------------------------------------------------------- */ + double dfUnits = GetLinearUnits(); + + if( fabs(dfUnits-0.3048) < 0.0001 ) + strcpy( pszUnits, "FEET" ); + else + strcpy( pszUnits, "METERS" ); + + if( EQUAL(pszProj,"RAW") ) + return OGRERR_UNSUPPORTED_SRS; + else + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogr_srs_esri.cpp b/bazaar/plugin/gdal/ogr/ogr_srs_esri.cpp new file mode 100644 index 000000000..e662fff6a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srs_esri.cpp @@ -0,0 +1,2489 @@ +/****************************************************************************** + * $Id: ogr_srs_esri.cpp 29116 2015-05-02 20:39:55Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: OGRSpatialReference translation to/from ESRI .prj definitions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * Copyright (c) 2013, Kyle Shannon + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "ogr_p.h" +#include "cpl_csv.h" +#include "cpl_multiproc.h" + +#include "ogr_srs_esri_names.h" + +CPL_CVSID("$Id: ogr_srs_esri.cpp 29116 2015-05-02 20:39:55Z rouault $"); + +void SetNewName( OGRSpatialReference* pOgr, const char* keyName, const char* newName ); +int RemapImgWGSProjcsName(OGRSpatialReference* pOgr, const char* pszProjCSName, + const char* pszProgCSName); +int RemapImgUTMNames(OGRSpatialReference* pOgr, const char* pszProjCSName, + const char* pszProgCSName, char **mappingTable); +int RemapNameBasedOnKeyName(OGRSpatialReference* pOgr, const char* pszName, + const char* pszkeyName, char **mappingTable); +int RemapNamesBasedOnTwo(OGRSpatialReference* pOgr, const char* name1, const char* name2, + char **mappingTable, long nTableStepSize, + char** pszkeyNames, long nKeys); +int RemapPValuesBasedOnProjCSAndPName(OGRSpatialReference* pOgr, + const char* pszProgCSName, char **mappingTable); +int RemapPNamesBasedOnProjCSAndPName(OGRSpatialReference* pOgr, + const char* pszProgCSName, char **mappingTable); +int DeleteParamBasedOnPrjName( OGRSpatialReference* pOgr, + const char* pszProjectionName, char **mappingTable); +int AddParamBasedOnPrjName( OGRSpatialReference* pOgr, + const char* pszProjectionName, char **mappingTable); +int RemapGeogCSName(OGRSpatialReference* pOgr, const char *pszGeogCSName); + +static int FindCodeFromDict( const char* pszDictFile, const char* CSName, char* code ); + +static const char *apszProjMapping[] = { + "Albers", SRS_PT_ALBERS_CONIC_EQUAL_AREA, + "Cassini", SRS_PT_CASSINI_SOLDNER, + "Equidistant_Cylindrical", SRS_PT_EQUIRECTANGULAR, + "Plate_Carree", SRS_PT_EQUIRECTANGULAR, + "Hotine_Oblique_Mercator_Azimuth_Natural_Origin", + SRS_PT_HOTINE_OBLIQUE_MERCATOR, + "Lambert_Conformal_Conic", SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP, + "Lambert_Conformal_Conic", SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP, + "Van_der_Grinten_I", SRS_PT_VANDERGRINTEN, + SRS_PT_TRANSVERSE_MERCATOR, SRS_PT_TRANSVERSE_MERCATOR, + "Gauss_Kruger", SRS_PT_TRANSVERSE_MERCATOR, + "Mercator", SRS_PT_MERCATOR_1SP, + NULL, NULL }; + +static const char *apszAlbersMapping[] = { + SRS_PP_CENTRAL_MERIDIAN, SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_LATITUDE_OF_ORIGIN, SRS_PP_LATITUDE_OF_CENTER, + "Central_Parallel", SRS_PP_LATITUDE_OF_CENTER, + NULL, NULL }; + +static const char *apszECMapping[] = { + SRS_PP_CENTRAL_MERIDIAN, SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_LATITUDE_OF_ORIGIN, SRS_PP_LATITUDE_OF_CENTER, + NULL, NULL }; + +static const char *apszMercatorMapping[] = { + SRS_PP_STANDARD_PARALLEL_1, SRS_PP_LATITUDE_OF_ORIGIN, + NULL, NULL }; + +static const char *apszPolarStereographicMapping[] = { + SRS_PP_STANDARD_PARALLEL_1, SRS_PP_LATITUDE_OF_ORIGIN, + NULL, NULL }; + +static const char *apszOrthographicMapping[] = { + "Longitude_Of_Center", SRS_PP_CENTRAL_MERIDIAN, + "Latitude_Of_Center", SRS_PP_LATITUDE_OF_ORIGIN, + NULL, NULL }; + +static const char *apszLambertConformalConicMapping[] = { + "Central_Parallel", SRS_PP_LATITUDE_OF_ORIGIN, + NULL, NULL }; + +static char **papszDatumMapping = NULL; +static CPLMutex* hDatumMappingMutex = NULL; + +static const char *apszDefaultDatumMapping[] = { + "6267", "North_American_1927", SRS_DN_NAD27, + "6269", "North_American_1983", SRS_DN_NAD83, + NULL, NULL, NULL }; + +static const char *apszSpheroidMapping[] = { + "WGS_84", "WGS_1984", + "WGS_72", "WGS_1972", + "GRS_1967_Modified", "GRS_1967_Truncated", + "Krassowsky_1940", "Krasovsky_1940", + "Everest_1830_1937_Adjustment", "Everest_Adjustment_1937", + NULL, NULL }; + +static const char *apszUnitMapping[] = { + "Meter", "meter", + "Meter", "metre", + "Foot", "foot", + "Foot", "feet", + "Foot", "international_feet", + "Foot_US", SRS_UL_US_FOOT, + "Foot_Clarke", "clarke_feet", + "Degree", "degree", + "Degree", "degrees", + "Degree", SRS_UA_DEGREE, + "Radian", SRS_UA_RADIAN, + NULL, NULL }; + +/* -------------------------------------------------------------------- */ +/* Table relating USGS and ESRI state plane zones. */ +/* -------------------------------------------------------------------- */ +static const int anUsgsEsriZones[] = +{ + 101, 3101, + 102, 3126, + 201, 3151, + 202, 3176, + 203, 3201, + 301, 3226, + 302, 3251, + 401, 3276, + 402, 3301, + 403, 3326, + 404, 3351, + 405, 3376, + 406, 3401, + 407, 3426, + 501, 3451, + 502, 3476, + 503, 3501, + 600, 3526, + 700, 3551, + 901, 3601, + 902, 3626, + 903, 3576, + 1001, 3651, + 1002, 3676, + 1101, 3701, + 1102, 3726, + 1103, 3751, + 1201, 3776, + 1202, 3801, + 1301, 3826, + 1302, 3851, + 1401, 3876, + 1402, 3901, + 1501, 3926, + 1502, 3951, + 1601, 3976, + 1602, 4001, + 1701, 4026, + 1702, 4051, + 1703, 6426, + 1801, 4076, + 1802, 4101, + 1900, 4126, + 2001, 4151, + 2002, 4176, + 2101, 4201, + 2102, 4226, + 2103, 4251, + 2111, 6351, + 2112, 6376, + 2113, 6401, + 2201, 4276, + 2202, 4301, + 2203, 4326, + 2301, 4351, + 2302, 4376, + 2401, 4401, + 2402, 4426, + 2403, 4451, + 2500, 0, + 2501, 4476, + 2502, 4501, + 2503, 4526, + 2600, 0, + 2601, 4551, + 2602, 4576, + 2701, 4601, + 2702, 4626, + 2703, 4651, + 2800, 4676, + 2900, 4701, + 3001, 4726, + 3002, 4751, + 3003, 4776, + 3101, 4801, + 3102, 4826, + 3103, 4851, + 3104, 4876, + 3200, 4901, + 3301, 4926, + 3302, 4951, + 3401, 4976, + 3402, 5001, + 3501, 5026, + 3502, 5051, + 3601, 5076, + 3602, 5101, + 3701, 5126, + 3702, 5151, + 3800, 5176, + 3900, 0, + 3901, 5201, + 3902, 5226, + 4001, 5251, + 4002, 5276, + 4100, 5301, + 4201, 5326, + 4202, 5351, + 4203, 5376, + 4204, 5401, + 4205, 5426, + 4301, 5451, + 4302, 5476, + 4303, 5501, + 4400, 5526, + 4501, 5551, + 4502, 5576, + 4601, 5601, + 4602, 5626, + 4701, 5651, + 4702, 5676, + 4801, 5701, + 4802, 5726, + 4803, 5751, + 4901, 5776, + 4902, 5801, + 4903, 5826, + 4904, 5851, + 5001, 6101, + 5002, 6126, + 5003, 6151, + 5004, 6176, + 5005, 6201, + 5006, 6226, + 5007, 6251, + 5008, 6276, + 5009, 6301, + 5010, 6326, + 5101, 5876, + 5102, 5901, + 5103, 5926, + 5104, 5951, + 5105, 5976, + 5201, 6001, + 5200, 6026, + 5200, 6076, + 5201, 6051, + 5202, 6051, + 5300, 0, + 5400, 0 +}; + +/* -------------------------------------------------------------------- */ +/* Datum Mapping functions and definitions */ +/* -------------------------------------------------------------------- */ +/* TODO adapt existing code and test */ +#define DM_IDX_EPSG_CODE 0 +#define DM_IDX_ESRI_NAME 1 +#define DM_IDX_EPSG_NAME 2 +#define DM_ELT_SIZE 3 + +#define DM_GET_EPSG_CODE(map, i) map[(i)*DM_ELT_SIZE + DM_IDX_EPSG_CODE] +#define DM_GET_ESRI_NAME(map, i) map[(i)*DM_ELT_SIZE + DM_IDX_ESRI_NAME] +#define DM_GET_EPSG_NAME(map, i) map[(i)*DM_ELT_SIZE + DM_IDX_EPSG_NAME] + +char *DMGetEPSGCode(int i) { return DM_GET_EPSG_CODE(papszDatumMapping, i); } +char *DMGetESRIName(int i) { return DM_GET_ESRI_NAME(papszDatumMapping, i); } +char *DMGetEPSGName(int i) { return DM_GET_EPSG_NAME(papszDatumMapping, i); } + + +void OGREPSGDatumNameMassage( char ** ppszDatum ); + +/************************************************************************/ +/* ESRIToUSGSZone() */ +/* */ +/* Convert ESRI style state plane zones to USGS style state */ +/* plane zones. */ +/************************************************************************/ + +static int ESRIToUSGSZone( int nESRIZone ) + +{ + int nPairs = sizeof(anUsgsEsriZones) / (2*sizeof(int)); + int i; + + for( i = 0; i < nPairs; i++ ) + { + if( anUsgsEsriZones[i*2+1] == nESRIZone ) + return anUsgsEsriZones[i*2]; + } + + return 0; +} + +/************************************************************************/ +/* MorphNameToESRI() */ +/* */ +/* Make name ESRI compatible. Convert spaces and special */ +/* characters to underscores and then strip down. */ +/************************************************************************/ + +static void MorphNameToESRI( char ** ppszName ) + +{ + int i, j; + char *pszName = *ppszName; + + if (pszName[0] == '\0') + return; + +/* -------------------------------------------------------------------- */ +/* Translate non-alphanumeric values to underscores. */ +/* -------------------------------------------------------------------- */ + for( i = 0; pszName[i] != '\0'; i++ ) + { + if( pszName[i] != '+' + && !(pszName[i] >= 'A' && pszName[i] <= 'Z') + && !(pszName[i] >= 'a' && pszName[i] <= 'z') + && !(pszName[i] >= '0' && pszName[i] <= '9') ) + { + pszName[i] = '_'; + } + } + +/* -------------------------------------------------------------------- */ +/* Remove repeated and trailing underscores. */ +/* -------------------------------------------------------------------- */ + for( i = 1, j = 0; pszName[i] != '\0'; i++ ) + { + if( pszName[j] == '_' && pszName[i] == '_' ) + continue; + + pszName[++j] = pszName[i]; + } + if( pszName[j] == '_' ) + pszName[j] = '\0'; + else + pszName[j+1] = '\0'; +} + +/************************************************************************/ +/* CleanESRIDatumMappingTable() */ +/************************************************************************/ + +CPL_C_START +void CleanupESRIDatumMappingTable() + +{ + if( papszDatumMapping == NULL ) + return; + + if( papszDatumMapping != (char **) apszDefaultDatumMapping ) + { + CSLDestroy( papszDatumMapping ); + papszDatumMapping = NULL; + } + + if( hDatumMappingMutex != NULL ) + { + CPLDestroyMutex(hDatumMappingMutex); + hDatumMappingMutex = NULL; + } +} +CPL_C_END + +/************************************************************************/ +/* InitDatumMappingTable() */ +/************************************************************************/ + +static void InitDatumMappingTable() + +{ + CPLMutexHolderD(&hDatumMappingMutex); + if( papszDatumMapping != NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Try to open the datum.csv file. */ +/* -------------------------------------------------------------------- */ + const char *pszFilename = CSVFilename("gdal_datum.csv"); + FILE * fp = VSIFOpen( pszFilename, "rb" ); + +/* -------------------------------------------------------------------- */ +/* Use simple default set if we can't find the file. */ +/* -------------------------------------------------------------------- */ + if( fp == NULL ) + { + papszDatumMapping = (char **)apszDefaultDatumMapping; + return; + } + +/* -------------------------------------------------------------------- */ +/* Figure out what fields we are interested in. */ +/* -------------------------------------------------------------------- */ + char **papszFieldNames = CSVReadParseLine( fp ); + int nDatumCodeField = CSLFindString( papszFieldNames, "DATUM_CODE" ); + int nEPSGNameField = CSLFindString( papszFieldNames, "DATUM_NAME" ); + int nESRINameField = CSLFindString( papszFieldNames, "ESRI_DATUM_NAME" ); + + CSLDestroy( papszFieldNames ); + + if( nDatumCodeField == -1 || nEPSGNameField == -1 || nESRINameField == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find required field in gdal_datum.csv in InitDatumMappingTable(), using default table setup." ); + + papszDatumMapping = (char **)apszDefaultDatumMapping; + VSIFClose( fp ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Read each line, adding a detail line for each. */ +/* -------------------------------------------------------------------- */ + int nMappingCount = 0; + const int nMaxDatumMappings = 1000; + char **papszFields; + papszDatumMapping = (char **)CPLCalloc(sizeof(char*),nMaxDatumMappings*3); + + for( papszFields = CSVReadParseLine( fp ); + papszFields != NULL; + papszFields = CSVReadParseLine( fp ) ) + { + int nFieldCount = CSLCount(papszFields); + + CPLAssert( nMappingCount+1 < nMaxDatumMappings ); + + if( MAX(nEPSGNameField,MAX(nDatumCodeField,nESRINameField)) + < nFieldCount + && nMaxDatumMappings > nMappingCount+1 ) + { + papszDatumMapping[nMappingCount*3+0] = + CPLStrdup( papszFields[nDatumCodeField] ); + papszDatumMapping[nMappingCount*3+1] = + CPLStrdup( papszFields[nESRINameField] ); + papszDatumMapping[nMappingCount*3+2] = + CPLStrdup( papszFields[nEPSGNameField] ); + OGREPSGDatumNameMassage( &(papszDatumMapping[nMappingCount*3+2]) ); + + nMappingCount++; + } + CSLDestroy( papszFields ); + } + + VSIFClose( fp ); + + papszDatumMapping[nMappingCount*3+0] = NULL; + papszDatumMapping[nMappingCount*3+1] = NULL; + papszDatumMapping[nMappingCount*3+2] = NULL; +} + + +/************************************************************************/ +/* OSRImportFromESRI() */ +/************************************************************************/ + +/** + * \brief Import coordinate system from ESRI .prj format(s). + * + * This function is the same as the C++ method OGRSpatialReference::importFromESRI() + */ +OGRErr OSRImportFromESRI( OGRSpatialReferenceH hSRS, char **papszPrj ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromESRI", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromESRI( papszPrj ); +} + +/************************************************************************/ +/* OSR_GDV() */ +/* */ +/* Fetch a particular parameter out of the parameter list, or */ +/* the indicated default if it isn't available. This is a */ +/* helper function for importFromESRI(). */ +/************************************************************************/ + +static double OSR_GDV( char **papszNV, const char * pszField, + double dfDefaultValue ) + +{ + int iLine; + + if( papszNV == NULL || papszNV[0] == NULL ) + return dfDefaultValue; + + if( EQUALN(pszField,"PARAM_",6) ) + { + int nOffset; + + for( iLine = 0; + papszNV[iLine] != NULL && !EQUALN(papszNV[iLine],"Paramet",7); + iLine++ ) {} + + for( nOffset=atoi(pszField+6); + papszNV[iLine] != NULL && nOffset > 0; + iLine++ ) + { + if( strlen(papszNV[iLine]) > 0 ) + nOffset--; + } + + while( papszNV[iLine] != NULL && strlen(papszNV[iLine]) == 0 ) + iLine++; + + if( papszNV[iLine] != NULL ) + { + char **papszTokens, *pszLine = papszNV[iLine]; + double dfValue; + + int i; + + // Trim comments. + for( i=0; pszLine[i] != '\0'; i++ ) + { + if( pszLine[i] == '/' && pszLine[i+1] == '*' ) + pszLine[i] = '\0'; + } + + papszTokens = CSLTokenizeString(papszNV[iLine]); + if( CSLCount(papszTokens) == 3 ) + { + /* http://agdcftp1.wr.usgs.gov/pub/projects/lcc/akcan_lcc/akcan.tar.gz contains */ + /* weird values for the second. Ignore it and the result looks correct */ + double dfSecond = CPLAtof(papszTokens[2]); + if (dfSecond < 0.0 || dfSecond >= 60.0) + dfSecond = 0.0; + + dfValue = ABS(CPLAtof(papszTokens[0])) + + CPLAtof(papszTokens[1]) / 60.0 + + dfSecond / 3600.0; + + if( CPLAtof(papszTokens[0]) < 0.0 ) + dfValue *= -1; + } + else if( CSLCount(papszTokens) > 0 ) + dfValue = CPLAtof(papszTokens[0]); + else + dfValue = dfDefaultValue; + + CSLDestroy( papszTokens ); + + return dfValue; + } + else + return dfDefaultValue; + } + else + { + for( iLine = 0; + papszNV[iLine] != NULL && + !EQUALN(papszNV[iLine],pszField,strlen(pszField)); + iLine++ ) {} + + if( papszNV[iLine] == NULL ) + return dfDefaultValue; + else + return CPLAtof( papszNV[iLine] + strlen(pszField) ); + } +} + +/************************************************************************/ +/* OSR_GDS() */ +/************************************************************************/ + +static CPLString OSR_GDS( char **papszNV, const char * pszField, + const char *pszDefaultValue ) + +{ + int iLine; + + if( papszNV == NULL || papszNV[0] == NULL ) + return pszDefaultValue; + + for( iLine = 0; + papszNV[iLine] != NULL && + !EQUALN(papszNV[iLine],pszField,strlen(pszField)); + iLine++ ) {} + + if( papszNV[iLine] == NULL ) + return pszDefaultValue; + else + { + CPLString osResult; + char **papszTokens; + + papszTokens = CSLTokenizeString(papszNV[iLine]); + + if( CSLCount(papszTokens) > 1 ) + osResult = papszTokens[1]; + else + osResult = pszDefaultValue; + + CSLDestroy( papszTokens ); + return osResult; + } +} + +/************************************************************************/ +/* importFromESRI() */ +/************************************************************************/ + +/** + * \brief Import coordinate system from ESRI .prj format(s). + * + * This function will read the text loaded from an ESRI .prj file, and + * translate it into an OGRSpatialReference definition. This should support + * many (but by no means all) old style (Arc/Info 7.x) .prj files, as well + * as the newer pseudo-OGC WKT .prj files. Note that new style .prj files + * are in OGC WKT format, but require some manipulation to correct datum + * names, and units on some projection parameters. This is addressed within + * importFromESRI() by an automatical call to morphFromESRI(). + * + * Currently only GEOGRAPHIC, UTM, STATEPLANE, GREATBRITIAN_GRID, ALBERS, + * EQUIDISTANT_CONIC, TRANSVERSE (mercator), POLAR, MERCATOR and POLYCONIC + * projections are supported from old style files. + * + * At this time there is no equivelent exportToESRI() method. Writing old + * style .prj files is not supported by OGRSpatialReference. However the + * morphToESRI() and exportToWkt() methods can be used to generate output + * suitable to write to new style (Arc 8) .prj files. + * + * This function is the equilvelent of the C function OSRImportFromESRI(). + * + * @param papszPrj NULL terminated list of strings containing the definition. + * + * @return OGRERR_NONE on success or an error code in case of failure. + */ + +OGRErr OGRSpatialReference::importFromESRI( char **papszPrj ) + +{ + if( papszPrj == NULL || papszPrj[0] == NULL ) + return OGRERR_CORRUPT_DATA; + +/* -------------------------------------------------------------------- */ +/* ArcGIS and related products now use a varient of Well Known */ +/* Text. Try to recognise this and ingest it. WKT is usually */ +/* all on one line, but we will accept multi-line formats and */ +/* concatenate. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(papszPrj[0],"GEOGCS",6) + || EQUALN(papszPrj[0],"PROJCS",6) + || EQUALN(papszPrj[0],"LOCAL_CS",8) ) + { + char *pszWKT, *pszWKT2; + OGRErr eErr; + int i; + + pszWKT = CPLStrdup(papszPrj[0]); + for( i = 1; papszPrj[i] != NULL; i++ ) + { + pszWKT = (char *) + CPLRealloc(pszWKT,strlen(pszWKT)+strlen(papszPrj[i])+1); + strcat( pszWKT, papszPrj[i] ); + } + pszWKT2 = pszWKT; + eErr = importFromWkt( &pszWKT2 ); + CPLFree( pszWKT ); + + if( eErr == OGRERR_NONE ) + eErr = morphFromESRI(); + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Operate on the basis of the projection name. */ +/* -------------------------------------------------------------------- */ + CPLString osProj = OSR_GDS( papszPrj, "Projection", "" ); + + if( EQUAL(osProj,"") ) + { + CPLDebug( "OGR_ESRI", "Can't find Projection\n" ); + return OGRERR_CORRUPT_DATA; + } + + else if( EQUAL(osProj,"GEOGRAPHIC") ) + { + } + + else if( EQUAL(osProj,"utm") ) + { + if( (int) OSR_GDV( papszPrj, "zone", 0.0 ) != 0 ) + { + double dfYShift = OSR_GDV( papszPrj, "Yshift", 0.0 ); + + SetUTM( (int) OSR_GDV( papszPrj, "zone", 0.0 ), + dfYShift == 0.0 ); + } + else + { + double dfCentralMeridian, dfRefLat; + int nZone; + + dfCentralMeridian = OSR_GDV( papszPrj, "PARAM_1", 0.0 ); + dfRefLat = OSR_GDV( papszPrj, "PARAM_2", 0.0 ); + + nZone = (int) ((dfCentralMeridian+183) / 6.0 + 0.0000001); + SetUTM( nZone, dfRefLat >= 0.0 ); + } + } + + else if( EQUAL(osProj,"STATEPLANE") ) + { + int nZone = (int) OSR_GDV( papszPrj, "zone", 0.0 ); + if( nZone != 0 ) + nZone = ESRIToUSGSZone( nZone ); + else + nZone = (int) OSR_GDV( papszPrj, "fipszone", 0.0 ); + + if( nZone != 0 ) + { + if( EQUAL(OSR_GDS( papszPrj, "Datum", "NAD83"),"NAD27") ) + SetStatePlane( nZone, FALSE ); + else + SetStatePlane( nZone, TRUE ); + } + } + + else if( EQUAL(osProj,"GREATBRITIAN_GRID") + || EQUAL(osProj,"GREATBRITAIN_GRID") ) + { + const char *pszWkt = + "PROJCS[\"OSGB 1936 / British National Grid\",GEOGCS[\"OSGB 1936\",DATUM[\"OSGB_1936\",SPHEROID[\"Airy 1830\",6377563.396,299.3249646]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",49],PARAMETER[\"central_meridian\",-2],PARAMETER[\"scale_factor\",0.999601272],PARAMETER[\"false_easting\",400000],PARAMETER[\"false_northing\",-100000],UNIT[\"metre\",1]]"; + + importFromWkt( (char **) &pszWkt ); + } + + else if( EQUAL(osProj,"ALBERS") ) + { + SetACEA( OSR_GDV( papszPrj, "PARAM_1", 0.0 ), + OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_4", 0.0 ), + OSR_GDV( papszPrj, "PARAM_3", 0.0 ), + OSR_GDV( papszPrj, "PARAM_5", 0.0 ), + OSR_GDV( papszPrj, "PARAM_6", 0.0 ) ); + } + + else if( EQUAL(osProj,"LAMBERT") ) + { + SetLCC( OSR_GDV( papszPrj, "PARAM_1", 0.0 ), + OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_4", 0.0 ), + OSR_GDV( papszPrj, "PARAM_3", 0.0 ), + OSR_GDV( papszPrj, "PARAM_5", 0.0 ), + OSR_GDV( papszPrj, "PARAM_6", 0.0 ) ); + } + + else if( EQUAL(osProj,"LAMBERT_AZIMUTHAL") ) + { + SetLAEA( OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_1", 0.0 ), + OSR_GDV( papszPrj, "PARAM_3", 0.0 ), + OSR_GDV( papszPrj, "PARAM_4", 0.0 ) ); + } + + else if( EQUAL(osProj,"EQUIDISTANT_CONIC") ) + { + int nStdPCount = (int) OSR_GDV( papszPrj, "PARAM_1", 0.0 ); + + if( nStdPCount == 1 ) + { + SetEC( OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_4", 0.0 ), + OSR_GDV( papszPrj, "PARAM_3", 0.0 ), + OSR_GDV( papszPrj, "PARAM_5", 0.0 ), + OSR_GDV( papszPrj, "PARAM_6", 0.0 ) ); + } + else + { + SetEC( OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_3", 0.0 ), + OSR_GDV( papszPrj, "PARAM_5", 0.0 ), + OSR_GDV( papszPrj, "PARAM_4", 0.0 ), + OSR_GDV( papszPrj, "PARAM_5", 0.0 ), + OSR_GDV( papszPrj, "PARAM_7", 0.0 ) ); + } + } + + else if( EQUAL(osProj,"TRANSVERSE") ) + { + SetTM( OSR_GDV( papszPrj, "PARAM_3", 0.0 ), + OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_1", 0.0 ), + OSR_GDV( papszPrj, "PARAM_4", 0.0 ), + OSR_GDV( papszPrj, "PARAM_5", 0.0 ) ); + } + + else if( EQUAL(osProj,"POLAR") ) + { + SetPS( OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_1", 0.0 ), + 1.0, + OSR_GDV( papszPrj, "PARAM_3", 0.0 ), + OSR_GDV( papszPrj, "PARAM_4", 0.0 ) ); + } + + else if( EQUAL(osProj,"MERCATOR") ) + { + SetMercator( OSR_GDV( papszPrj, "PARAM_1", 0.0 ), + OSR_GDV( papszPrj, "PARAM_0", 0.0 ), + 1.0, + OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_3", 0.0 ) ); + } + + else if( EQUAL(osProj, SRS_PT_MERCATOR_AUXILIARY_SPHERE) ) + { + // This is EPSG:3875 Pseudo Mercator. We might as well import it from + // the EPSG spec. + CPLString osAuxiliarySphereType; + importFromEPSG(3857); + } + + else if( EQUAL(osProj,"POLYCONIC") ) + { + SetPolyconic( OSR_GDV( papszPrj, "PARAM_2", 0.0 ), + OSR_GDV( papszPrj, "PARAM_1", 0.0 ), + OSR_GDV( papszPrj, "PARAM_3", 0.0 ), + OSR_GDV( papszPrj, "PARAM_4", 0.0 ) ); + } + + else + { + CPLDebug( "OGR_ESRI", "Unsupported projection: %s", osProj.c_str() ); + SetLocalCS( osProj ); + } + +/* -------------------------------------------------------------------- */ +/* Try to translate the datum/spheroid. */ +/* -------------------------------------------------------------------- */ + if( !IsLocal() && GetAttrNode( "GEOGCS" ) == NULL ) + { + CPLString osDatum; + + osDatum = OSR_GDS( papszPrj, "Datum", ""); + + if( EQUAL(osDatum,"NAD27") || EQUAL(osDatum,"NAD83") + || EQUAL(osDatum,"WGS84") || EQUAL(osDatum,"WGS72") ) + { + SetWellKnownGeogCS( osDatum ); + } + else if( EQUAL( osDatum, "EUR" ) + || EQUAL( osDatum, "ED50" ) ) + { + SetWellKnownGeogCS( "EPSG:4230" ); + } + else if( EQUAL( osDatum, "GDA94" ) ) + { + SetWellKnownGeogCS( "EPSG:4283" ); + } + else + { + CPLString osSpheroid; + + osSpheroid = OSR_GDS( papszPrj, "Spheroid", ""); + + if( EQUAL(osSpheroid,"INT1909") + || EQUAL(osSpheroid,"INTERNATIONAL1909") ) + { + OGRSpatialReference oGCS; + oGCS.importFromEPSG( 4022 ); + CopyGeogCSFrom( &oGCS ); + } + else if( EQUAL(osSpheroid,"AIRY") ) + { + OGRSpatialReference oGCS; + oGCS.importFromEPSG( 4001 ); + CopyGeogCSFrom( &oGCS ); + } + else if( EQUAL(osSpheroid,"CLARKE1866") ) + { + OGRSpatialReference oGCS; + oGCS.importFromEPSG( 4008 ); + CopyGeogCSFrom( &oGCS ); + } + else if( EQUAL(osSpheroid,"GRS80") ) + { + OGRSpatialReference oGCS; + oGCS.importFromEPSG( 4019 ); + CopyGeogCSFrom( &oGCS ); + } + else if( EQUAL(osSpheroid,"KRASOVSKY") + || EQUAL(osSpheroid,"KRASSOVSKY") + || EQUAL(osSpheroid,"KRASSOWSKY") ) + { + OGRSpatialReference oGCS; + oGCS.importFromEPSG( 4024 ); + CopyGeogCSFrom( &oGCS ); + } + else if( EQUAL(osSpheroid,"Bessel") ) + { + OGRSpatialReference oGCS; + oGCS.importFromEPSG( 4004 ); + CopyGeogCSFrom( &oGCS ); + } + else + { + // If we don't know, default to WGS84 so there is something there. + SetWellKnownGeogCS( "WGS84" ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Linear units translation */ +/* -------------------------------------------------------------------- */ + if( IsLocal() || IsProjected() ) + { + CPLString osValue; + double dfOldUnits = GetLinearUnits(); + + osValue = OSR_GDS( papszPrj, "Units", "" ); + if( EQUAL(osValue, "" ) ) + SetLinearUnitsAndUpdateParameters( SRS_UL_METER, 1.0 ); + else if( EQUAL(osValue,"FEET") ) + SetLinearUnitsAndUpdateParameters( SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV) ); + else if( CPLAtof(osValue) != 0.0 ) + SetLinearUnitsAndUpdateParameters( "user-defined", + 1.0 / CPLAtof(osValue) ); + else + SetLinearUnitsAndUpdateParameters( osValue, 1.0 ); + + // If we have reset the linear units we should clear any authority + // nodes on the PROJCS. This especially applies to state plane + // per bug 1697 + double dfNewUnits = GetLinearUnits(); + if( dfOldUnits != 0.0 + && (dfNewUnits / dfOldUnits < 0.9999999 + || dfNewUnits / dfOldUnits > 1.0000001) ) + { + if( GetRoot()->FindChild( "AUTHORITY" ) != -1 ) + GetRoot()->DestroyChild(GetRoot()->FindChild( "AUTHORITY" )); + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* morphToESRI() */ +/************************************************************************/ +/** + * \brief Convert in place to ESRI WKT format. + * + * The value nodes of this coordinate system are modified in various manners + * more closely map onto the ESRI concept of WKT format. This includes + * renaming a variety of projections and arguments, and stripping out + * nodes note recognised by ESRI (like AUTHORITY and AXIS). + * + * This does the same as the C function OSRMorphToESRI(). + * + * @return OGRERR_NONE unless something goes badly wrong. + */ + +OGRErr OGRSpatialReference::morphToESRI() + +{ + OGRErr eErr; + +/* -------------------------------------------------------------------- */ +/* Fixup ordering, missing linear units, etc. */ +/* -------------------------------------------------------------------- */ + eErr = Fixup(); + if( eErr != OGRERR_NONE ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Strip all CT parameters (AXIS, AUTHORITY, TOWGS84, etc). */ +/* -------------------------------------------------------------------- */ + eErr = StripCTParms(); + if( eErr != OGRERR_NONE ) + return eErr; + + if( GetRoot() == NULL ) + return OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* There is a special case for Hotine Oblique Mercator to split */ +/* out the case with an angle to rectified grid. Bug 423 */ +/* -------------------------------------------------------------------- */ + const char *pszProjection = GetAttrValue("PROJECTION"); + + if( pszProjection != NULL + && EQUAL(pszProjection,SRS_PT_HOTINE_OBLIQUE_MERCATOR) + && fabs(GetProjParm(SRS_PP_AZIMUTH, 0.0 )-90) < 0.0001 + && fabs(GetProjParm(SRS_PP_RECTIFIED_GRID_ANGLE, 0.0 )-90) < 0.0001 ) + { + SetNode( "PROJCS|PROJECTION", + "Hotine_Oblique_Mercator_Azimuth_Center" ); + + /* ideally we should strip out of the rectified_grid_angle */ + // strip off rectified_grid_angle -- I hope it is 90! + OGR_SRSNode *poPROJCS = GetAttrNode( "PROJCS" ); + int iRGAChild = FindProjParm( "rectified_grid_angle", poPROJCS ); + if( iRGAChild != -1 ) + poPROJCS->DestroyChild( iRGAChild); + + pszProjection = GetAttrValue("PROJECTION"); + } + +/* -------------------------------------------------------------------- */ +/* Polar_Stereographic maps to ESRI codes */ +/* Stereographic_South_Pole or Stereographic_North_Pole based */ +/* on latitude. */ +/* -------------------------------------------------------------------- */ + if( pszProjection != NULL + && ( EQUAL(pszProjection,SRS_PT_POLAR_STEREOGRAPHIC) )) + { + if( GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) < 0.0 ) + { + SetNode( "PROJCS|PROJECTION", + "Stereographic_South_Pole" ); + pszProjection = GetAttrValue("PROJECTION"); + } + else + { + SetNode( "PROJCS|PROJECTION", + "Stereographic_North_Pole" ); + pszProjection = GetAttrValue("PROJECTION"); + } + } + +/* -------------------------------------------------------------------- */ +/* OBLIQUE_STEREOGRAPHIC maps to ESRI Double_Stereographic */ +/* -------------------------------------------------------------------- */ + if( pszProjection != NULL + && ( EQUAL(pszProjection,SRS_PT_OBLIQUE_STEREOGRAPHIC) )) + { + SetNode( "PROJCS|PROJECTION", "Double_Stereographic" ); + } + +/* -------------------------------------------------------------------- */ +/* Translate PROJECTION keywords that are misnamed. */ +/* -------------------------------------------------------------------- */ + GetRoot()->applyRemapper( "PROJECTION", + (char **)apszProjMapping+1, + (char **)apszProjMapping, 2 ); + pszProjection = GetAttrValue("PROJECTION"); + +/* -------------------------------------------------------------------- */ +/* Translate DATUM keywords that are misnamed. */ +/* -------------------------------------------------------------------- */ + InitDatumMappingTable(); + + GetRoot()->applyRemapper( "DATUM", + papszDatumMapping+2, papszDatumMapping+1, 3 ); + + const char *pszProjCSName = NULL; + const char *pszGcsName = NULL; + OGR_SRSNode *poProjCS = NULL; + OGR_SRSNode *poProjCSNodeChild = NULL; + +/* -------------------------------------------------------------------- */ +/* Very specific handling for some well known geographic */ +/* coordinate systems. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poGeogCS = GetAttrNode( "GEOGCS" ); + if( poGeogCS != NULL ) + { + const char *pszGeogCSName = poGeogCS->GetChild(0)->GetValue(); + const char *pszAuthName = GetAuthorityName("GEOGCS"); + const char *pszUTMPrefix = NULL; + int nGCSCode = -1; + + if( pszAuthName != NULL && EQUAL(pszAuthName,"EPSG") ) + nGCSCode = atoi(GetAuthorityCode("GEOGCS")); + + if( nGCSCode == 4326 + || EQUAL(pszGeogCSName,"WGS84") + || EQUAL(pszGeogCSName,"WGS 84") ) + { + poGeogCS->GetChild(0)->SetValue( "GCS_WGS_1984" ); + pszUTMPrefix = "WGS_1984"; + } + else if( nGCSCode == 4267 + || EQUAL(pszGeogCSName,"NAD27") + || EQUAL(pszGeogCSName,"NAD 27") ) + { + poGeogCS->GetChild(0)->SetValue( "GCS_North_American_1927" ); + pszUTMPrefix = "NAD_1927"; + } + else if( nGCSCode == 4269 + || EQUAL(pszGeogCSName,"NAD83") + || EQUAL(pszGeogCSName,"NAD 83") ) + { + poGeogCS->GetChild(0)->SetValue( "GCS_North_American_1983" ); + pszUTMPrefix = "NAD_1983"; + } + else if( nGCSCode == 4167 + || EQUAL(pszGeogCSName,"NZGD2000") + || EQUAL(pszGeogCSName,"NZGD 2000") ) + { + poGeogCS->GetChild(0)->SetValue( "GCS_NZGD_2000" ); + pszUTMPrefix = "NZGD_2000"; + } + else if( nGCSCode == 4272 + || EQUAL(pszGeogCSName,"NZGD49") + || EQUAL(pszGeogCSName,"NZGD 49") ) + { + poGeogCS->GetChild(0)->SetValue( "GCS_New_Zealand_1949" ); + pszUTMPrefix = "NZGD_1949"; + } + +/* -------------------------------------------------------------------- */ +/* Force Unnamed to Unknown for most common locations. */ +/* -------------------------------------------------------------------- */ + static const char *apszUnknownMapping[] = { + "Unknown", "Unnamed", + NULL, NULL + }; + + GetRoot()->applyRemapper( "PROJCS", + (char **)apszUnknownMapping+1, + (char **)apszUnknownMapping+0, 2 ); + GetRoot()->applyRemapper( "GEOGCS", + (char **)apszUnknownMapping+1, + (char **)apszUnknownMapping+0, 2 ); + GetRoot()->applyRemapper( "DATUM", + (char **)apszUnknownMapping+1, + (char **)apszUnknownMapping+0, 2 ); + GetRoot()->applyRemapper( "SPHEROID", + (char **)apszUnknownMapping+1, + (char **)apszUnknownMapping+0, 2 ); + GetRoot()->applyRemapper( "PRIMEM", + (char **)apszUnknownMapping+1, + (char **)apszUnknownMapping+0, 2 ); + +/* -------------------------------------------------------------------- */ +/* If the PROJCS name is unset, use the PROJECTION name in */ +/* place of unknown, or unnamed. At the request of Peng Gao. */ +/* -------------------------------------------------------------------- */ + if( (poProjCS = GetAttrNode( "PROJCS" )) != NULL ) + poProjCSNodeChild = poProjCS->GetChild(0); + + if( poProjCSNodeChild ) + { + pszProjCSName = poProjCSNodeChild->GetValue(); + char *pszNewValue = CPLStrdup(pszProjCSName); + MorphNameToESRI( &pszNewValue ); + poProjCSNodeChild->SetValue( pszNewValue ); + CPLFree( pszNewValue ); + pszProjCSName = poProjCSNodeChild->GetValue(); + } + + if( pszProjCSName != NULL + && ( EQUAL(pszProjCSName,"unnamed") + || EQUAL(pszProjCSName,"unknown") + || EQUAL(pszProjCSName,"") ) ) + { + if( GetAttrValue( "PROJECTION", 0 ) != NULL ) + { + pszProjCSName = GetAttrValue( "PROJECTION", 0 ); + poProjCSNodeChild->SetValue( pszProjCSName ); + } + } + +/* -------------------------------------------------------------------- */ +/* Prepare very specific PROJCS names for UTM coordinate */ +/* systems. */ +/* -------------------------------------------------------------------- */ + int bNorth = 0; + int nZone = 0; + + /* get zone from name first */ + if( pszProjCSName && EQUALN(pszProjCSName, "UTM Zone ", 9) ) + { + nZone = atoi(pszProjCSName+9); + if( strstr(pszProjCSName, "North") ) + bNorth = 1; + } + + /* if can not get from the name, from the parameters */ + if( nZone <= 0 ) + nZone = GetUTMZone( &bNorth ); + + if( nZone > 0 && pszUTMPrefix ) + { + char szUTMName[128]; + if( bNorth ) + sprintf( szUTMName, "%s_UTM_Zone_%dN", pszUTMPrefix, nZone ); + else + sprintf( szUTMName, "%s_UTM_Zone_%dS", pszUTMPrefix, nZone ); + + if( poProjCSNodeChild ) + poProjCSNodeChild->SetValue( szUTMName ); + } + } + +/* -------------------------------------------------------------------- */ +/* Translate UNIT keywords that are misnamed, or even the wrong */ +/* case. */ +/* -------------------------------------------------------------------- */ + GetRoot()->applyRemapper( "UNIT", + (char **)apszUnitMapping+1, + (char **)apszUnitMapping, 2 ); + +/* -------------------------------------------------------------------- */ +/* reset constants for decimal degrees to the exact string ESRI */ +/* expects when encountered to ensure a matchup. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poUnit = GetAttrNode( "GEOGCS|UNIT" ); + + if( poUnit != NULL && poUnit->GetChildCount() >= 2 + && ABS(GetAngularUnits()-0.0174532925199433) < 0.00000000001 ) + { + poUnit->GetChild(0)->SetValue("Degree"); + poUnit->GetChild(1)->SetValue("0.017453292519943295"); + } + +/* -------------------------------------------------------------------- */ +/* Make sure we reproduce US Feet exactly too. */ +/* -------------------------------------------------------------------- */ + poUnit = GetAttrNode( "PROJCS|UNIT" ); + + if( poUnit != NULL && poUnit->GetChildCount() >= 2 + && ABS(GetLinearUnits()- 0.30480060960121924) < 0.000000000000001) + { + poUnit->GetChild(0)->SetValue("Foot_US"); + poUnit->GetChild(1)->SetValue("0.30480060960121924"); + } + +/* -------------------------------------------------------------------- */ +/* Remap parameters used for Albers and Mercator. */ +/* -------------------------------------------------------------------- */ + pszProjection = GetAttrValue("PROJECTION"); + poProjCS = GetAttrNode( "PROJCS" ); + + if( pszProjection != NULL && EQUAL(pszProjection,"Albers") ) + GetRoot()->applyRemapper( + "PARAMETER", (char **)apszAlbersMapping + 1, + (char **)apszAlbersMapping + 0, 2 ); + + if( pszProjection != NULL + && (EQUAL(pszProjection,SRS_PT_EQUIDISTANT_CONIC) || + EQUAL(pszProjection,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) || + EQUAL(pszProjection,SRS_PT_AZIMUTHAL_EQUIDISTANT) || + EQUAL(pszProjection,SRS_PT_SINUSOIDAL) || + EQUAL(pszProjection,SRS_PT_ROBINSON) ) ) + GetRoot()->applyRemapper( + "PARAMETER", (char **)apszECMapping + 1, + (char **)apszECMapping + 0, 2 ); + + if( pszProjection != NULL && EQUAL(pszProjection,"Mercator") ) + GetRoot()->applyRemapper( + "PARAMETER", + (char **)apszMercatorMapping + 1, + (char **)apszMercatorMapping + 0, 2 ); + + if( pszProjection != NULL + && EQUALN(pszProjection,"Stereographic_",14) + && EQUALN(pszProjection+strlen(pszProjection)-5,"_Pole",5) ) + GetRoot()->applyRemapper( + "PARAMETER", + (char **)apszPolarStereographicMapping + 1, + (char **)apszPolarStereographicMapping + 0, 2 ); + + if( pszProjection != NULL && EQUAL(pszProjection,"Plate_Carree") ) + if(FindProjParm( SRS_PP_STANDARD_PARALLEL_1, poProjCS ) < 0) + GetRoot()->applyRemapper( + "PARAMETER", + (char **)apszPolarStereographicMapping + 1, + (char **)apszPolarStereographicMapping + 0, 2 ); + +/* -------------------------------------------------------------------- */ +/* ESRI's Equidistant_Cylindrical does not support the */ +/* latitude_of_origin keyword. */ +/* -------------------------------------------------------------------- */ + if( pszProjection != NULL + && EQUAL(pszProjection,"Equidistant_Cylindrical") ) + { + if( GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0) != 0.0 ) + { + CPLDebug( "OGR_ESRI", "Equirectangular with non-zero latitude of origin - not supported." ); + } + else + { + OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS"); + if( poPROJCS ) + poPROJCS->DestroyChild( + FindProjParm( SRS_PP_LATITUDE_OF_ORIGIN ) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Convert SPHEROID name to use underscores instead of spaces. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poSpheroid; + OGR_SRSNode *poSpheroidChild = NULL; + poSpheroid = GetAttrNode( "SPHEROID" ); + if( poSpheroid != NULL ) + poSpheroidChild = poSpheroid->GetChild(0); + + if( poSpheroidChild != NULL ) + { +// char *pszNewValue = CPLStrdup(RemapSpheroidName(poSpheroidChild->GetValue())); + char *pszNewValue = CPLStrdup(poSpheroidChild->GetValue()); + + MorphNameToESRI( &pszNewValue ); + + poSpheroidChild->SetValue( pszNewValue ); + CPLFree( pszNewValue ); + + GetRoot()->applyRemapper( "SPHEROID", + (char **) apszSpheroidMapping+0, + (char **) apszSpheroidMapping+1, 2 ); + } + + if( poSpheroid != NULL ) + poSpheroidChild = poSpheroid->GetChild(2); + + if( poSpheroidChild != NULL ) + { + const char * dfValue = poSpheroidChild->GetValue(); + for( int i = 0; apszInvFlatteningMapping[i] != NULL; i += 2 ) + { + if( EQUALN(apszInvFlatteningMapping[i], dfValue, strlen(apszInvFlatteningMapping[i]) )) + { + poSpheroidChild->SetValue( apszInvFlatteningMapping[i+1] ); + break; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Try to insert a D_ in front of the datum name. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poDatum; + + poDatum = GetAttrNode( "DATUM" ); + if( poDatum != NULL ) + poDatum = poDatum->GetChild(0); + + if( poDatum != NULL ) + { + const char* pszDatumName = poDatum->GetValue(); + if( !EQUALN(pszDatumName, "D_",2) ) + { + char *pszNewValue; + + pszNewValue = (char *) CPLMalloc(strlen(poDatum->GetValue())+3); + strcpy( pszNewValue, "D_" ); + strcat( pszNewValue, poDatum->GetValue() ); + poDatum->SetValue( pszNewValue ); + CPLFree( pszNewValue ); + } + } + +/* -------------------------------------------------------------------- */ +/* final check names */ +/* -------------------------------------------------------------------- */ + if( poProjCSNodeChild ) + pszProjCSName = poProjCSNodeChild->GetValue(); + + if( pszProjCSName ) + { + pszGcsName = GetAttrValue( "GEOGCS" ); + if(pszGcsName && !EQUALN( pszGcsName, "GCS_", 4 ) ) + { + char* newGcsName = (char *) CPLMalloc(strlen(pszGcsName) + 5); + strcpy( newGcsName, "GCS_" ); + strcat(newGcsName, pszGcsName); + SetNewName( this, "GEOGCS", newGcsName ); + CPLFree( newGcsName ); + pszGcsName = GetAttrValue("GEOGCS" ); + } + RemapGeogCSName(this, pszGcsName); + + // Specific processing and remapping + pszProjection = GetAttrValue("PROJECTION"); + if(pszProjection) + { + if(EQUAL(pszProjection,"Lambert_Conformal_Conic")) + { + if(FindProjParm( SRS_PP_STANDARD_PARALLEL_2, poProjCS ) < 0 ) + { + int iChild = FindProjParm( SRS_PP_LATITUDE_OF_ORIGIN, poProjCS ); + int iChild1 = FindProjParm( SRS_PP_STANDARD_PARALLEL_1, poProjCS ); + if( iChild >= 0 && iChild1 < 0 ) + { + const OGR_SRSNode *poParameter = poProjCS->GetChild(iChild); + if( poParameter ) + { + OGR_SRSNode *poNewParm = new OGR_SRSNode( "PARAMETER" ); + poNewParm->AddChild( new OGR_SRSNode( "standard_parallel_1" ) ); + poNewParm->AddChild( new OGR_SRSNode( poParameter->GetChild(1)->GetValue() ) ); + poProjCS->AddChild( poNewParm ); + } + } + } + } + + if(EQUAL(pszProjection,"Plate_Carree")) + { + int iChild = FindProjParm( SRS_PP_STANDARD_PARALLEL_1, poProjCS ); + if(iChild < 0) + iChild = FindProjParm( SRS_PP_PSEUDO_STD_PARALLEL_1, poProjCS ); + + if(iChild >= 0) + { + const OGR_SRSNode *poParameter = poProjCS->GetChild(iChild); + if(!EQUAL(poParameter->GetChild(1)->GetValue(), "0.0") && !EQUAL(poParameter->GetChild(1)->GetValue(), "0")) + { + SetNode( "PROJCS|PROJECTION", "Equidistant_Cylindrical" ); + pszProjection = GetAttrValue("PROJECTION"); + } + } + } + DeleteParamBasedOnPrjName( this, pszProjection, (char **)apszDeleteParametersBasedOnProjection); + AddParamBasedOnPrjName( this, pszProjection, (char **)apszAddParametersBasedOnProjection); + RemapPValuesBasedOnProjCSAndPName( this, pszProjection, (char **)apszParamValueMapping); + RemapPNamesBasedOnProjCSAndPName( this, pszProjection, (char **)apszParamNameMapping); + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRMorphToESRI() */ +/************************************************************************/ + +/** + * \brief Convert in place to ESRI WKT format. + * + * This function is the same as the C++ method OGRSpatialReference::morphToESRI() + */ +OGRErr OSRMorphToESRI( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRMorphToESRI", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->morphToESRI(); +} + +/************************************************************************/ +/* morphFromESRI() */ +/* */ +/* modify this definition from the ESRI definition of WKT to */ +/* the "Standard" definition. */ +/************************************************************************/ + +/** + * \brief Convert in place from ESRI WKT format. + * + * The value notes of this coordinate system are modified in various manners + * to adhere more closely to the WKT standard. This mostly involves + * translating a variety of ESRI names for projections, arguments and + * datums to "standard" names, as defined by Adam Gawne-Cain's reference + * translation of EPSG to WKT for the CT specification. + * + * Starting with GDAL 1.9.0, missing parameters in TOWGS84, DATUM or GEOGCS + * nodes can be added to the WKT, comparing existing WKT parameters to GDAL's + * databases. Note that this optional procedure is very conservative and should + * not introduce false information into the WKT defintion (altough caution + * should be advised when activating it). Needs the Configuration Option + * GDAL_FIX_ESRI_WKT be set to one of the following values (TOWGS84 is + * recommended for proper datum shift calculations): + * + * GDAL_FIX_ESRI_WKT values + * + * + * + * + *
      TOWGS84   + * Adds missing TOWGS84 parameters (necessary for datum transformations), + * based on named datum and spheroid values.
      DATUM   + * Adds EPSG AUTHORITY nodes and sets SPHEROID name to OGR spec.
      GEOGCS   + * Adds EPSG AUTHORITY nodes and sets GEOGCS, DATUM and SPHEROID + * names to OGR spec. Effectively replaces GEOGCS node with the result of + * importFromEPSG(n), using EPSG code n corresponding to the existing GEOGCS. + * Does not impact PROJCS values.
    + * + * This does the same as the C function OSRMorphFromESRI(). + * + * @return OGRERR_NONE unless something goes badly wrong. + */ + +OGRErr OGRSpatialReference::morphFromESRI() + +{ + OGRErr eErr = OGRERR_NONE; + OGR_SRSNode *poDatum; + char *pszDatumOrig = NULL; + + if( GetRoot() == NULL ) + return OGRERR_NONE; + + InitDatumMappingTable(); + +/* -------------------------------------------------------------------- */ +/* Save original datum name for later */ +/* -------------------------------------------------------------------- */ + poDatum = GetAttrNode( "DATUM" ); + if( poDatum != NULL ) + { + poDatum = poDatum->GetChild(0); + pszDatumOrig = CPLStrdup( poDatum->GetValue() ); + } + +/* -------------------------------------------------------------------- */ +/* Translate DATUM keywords that are oddly named. */ +/* -------------------------------------------------------------------- */ + GetRoot()->applyRemapper( "DATUM", + (char **)papszDatumMapping+1, + (char **)papszDatumMapping+2, 3 ); + +/* -------------------------------------------------------------------- */ +/* Try to remove any D_ in front of the datum name. */ +/* -------------------------------------------------------------------- */ + poDatum = GetAttrNode( "DATUM" ); + if( poDatum != NULL ) + poDatum = poDatum->GetChild(0); + + if( poDatum != NULL ) + { + if( EQUALN(poDatum->GetValue(),"D_",2) ) + { + char *pszNewValue = CPLStrdup( poDatum->GetValue() + 2 ); + poDatum->SetValue( pszNewValue ); + CPLFree( pszNewValue ); + } + } + +/* -------------------------------------------------------------------- */ +/* Translate some SPHEROID keywords that are oddly named. */ +/* -------------------------------------------------------------------- */ + GetRoot()->applyRemapper( "SPHEROID", + (char **)apszSpheroidMapping+1, + (char **)apszSpheroidMapping+0, 2 ); + +/* -------------------------------------------------------------------- */ +/* Split Lambert_Conformal_Conic into 1SP or 2SP form. */ +/* */ +/* See bugzilla.remotesensing.org/show_bug.cgi?id=187 */ +/* */ +/* We decide based on whether it has 2SPs. We used to assume */ +/* 1SP if it had a scale factor but that turned out to be a */ +/* poor test. */ +/* -------------------------------------------------------------------- */ + const char *pszProjection = GetAttrValue("PROJECTION"); + + if( pszProjection != NULL + && EQUAL(pszProjection,"Lambert_Conformal_Conic") ) + { + if( GetProjParm( SRS_PP_STANDARD_PARALLEL_1, 1000.0 ) != 1000.0 + && GetProjParm( SRS_PP_STANDARD_PARALLEL_2, 1000.0 ) != 1000.0 ) + SetNode( "PROJCS|PROJECTION", + SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP ); + else + SetNode( "PROJCS|PROJECTION", + SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP ); + + pszProjection = GetAttrValue("PROJECTION"); + } + +/* -------------------------------------------------------------------- */ +/* If we are remapping Hotine_Oblique_Mercator_Azimuth_Center */ +/* add a rectified_grid_angle parameter - to match the azimuth */ +/* I guess. */ +/* -------------------------------------------------------------------- */ + if( pszProjection != NULL + && EQUAL(pszProjection,"Hotine_Oblique_Mercator_Azimuth_Center") ) + { + SetProjParm( SRS_PP_RECTIFIED_GRID_ANGLE , + GetProjParm( SRS_PP_AZIMUTH, 0.0 ) ); + FixupOrdering(); + } + +/* -------------------------------------------------------------------- */ +/* Remap Albers, Mercator and Polar Stereographic parameters. */ +/* -------------------------------------------------------------------- */ + if( pszProjection != NULL && EQUAL(pszProjection,"Albers") ) + GetRoot()->applyRemapper( + "PARAMETER", (char **)apszAlbersMapping + 0, + (char **)apszAlbersMapping + 1, 2 ); + + if( pszProjection != NULL + && (EQUAL(pszProjection,SRS_PT_EQUIDISTANT_CONIC) || + EQUAL(pszProjection,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) || + EQUAL(pszProjection,SRS_PT_AZIMUTHAL_EQUIDISTANT) || + EQUAL(pszProjection,SRS_PT_SINUSOIDAL) || + EQUAL(pszProjection,SRS_PT_ROBINSON) ) ) + GetRoot()->applyRemapper( + "PARAMETER", (char **)apszECMapping + 0, + (char **)apszECMapping + 1, 2 ); + + if( pszProjection != NULL && EQUAL(pszProjection,"Mercator") ) + GetRoot()->applyRemapper( + "PARAMETER", + (char **)apszMercatorMapping + 0, + (char **)apszMercatorMapping + 1, 2 ); + + if( pszProjection != NULL && EQUAL(pszProjection,"Orthographic") ) + GetRoot()->applyRemapper( + "PARAMETER", (char **)apszOrthographicMapping + 0, + (char **)apszOrthographicMapping + 1, 2 ); + + if( pszProjection != NULL + && EQUALN(pszProjection,"Stereographic_",14) + && EQUALN(pszProjection+strlen(pszProjection)-5,"_Pole",5) ) + GetRoot()->applyRemapper( + "PARAMETER", + (char **)apszPolarStereographicMapping + 0, + (char **)apszPolarStereographicMapping + 1, 2 ); + +/* -------------------------------------------------------------------- */ +/* Remap south and north polar stereographic to one value. */ +/* -------------------------------------------------------------------- */ + if( pszProjection != NULL + && EQUALN(pszProjection,"Stereographic_",14) + && EQUALN(pszProjection+strlen(pszProjection)-5,"_Pole",5) ) + { + SetNode( "PROJCS|PROJECTION", SRS_PT_POLAR_STEREOGRAPHIC ); + pszProjection = GetAttrValue("PROJECTION"); + } + +/* -------------------------------------------------------------------- */ +/* Remap Double_Stereographic to Oblique_Stereographic. */ +/* -------------------------------------------------------------------- */ + if( pszProjection != NULL + && EQUAL(pszProjection,"Double_Stereographic") ) + { + SetNode( "PROJCS|PROJECTION", SRS_PT_OBLIQUE_STEREOGRAPHIC ); + pszProjection = GetAttrValue("PROJECTION"); + } + +/* -------------------------------------------------------------------- */ +/* Remap Equidistant_Cylindrical parameter. It is same as */ +/* Stereographic */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + if( pszProjection != NULL && EQUAL(pszProjection,"Equidistant_Cylindrical") ) + GetRoot()->applyRemapper( + "PARAMETER", + (char **)apszPolarStereographicMapping + 0, + (char **)apszPolarStereographicMapping + 1, 2 ); +#endif + + /* + ** Handle the value of Central_Parallel -> latitude_of_center. + ** See ticket #3191. Other mappings probably need to be added. + */ + if( pszProjection != NULL && + ( EQUAL( pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP ) || + EQUAL( pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP ) ) ) + { + GetRoot()->applyRemapper( + "PARAMETER", (char **)apszLambertConformalConicMapping + 0, + (char **)apszLambertConformalConicMapping + 1, 2 ); + + /* LCC 1SP has duplicated parameters Standard_Parallel_1 and Latitude_Of_Origin */ + /* http://trac.osgeo.org/gdal/ticket/2072 */ + if( EQUAL( pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP ) ) + { + OGR_SRSNode *poPROJCS = GetAttrNode( "PROJCS" ); + int iSP1Child = FindProjParm( "Standard_Parallel_1", poPROJCS ); + int iLatOrigChild = FindProjParm( "Latitude_Of_Origin", poPROJCS ); + if( iSP1Child != -1 && iLatOrigChild != 1 ) + { + /* Do a sanity check before removing Standard_Parallel_1 */ + if( EQUAL(poPROJCS->GetChild(iSP1Child)->GetValue(), + poPROJCS->GetChild(iLatOrigChild)->GetValue()) ) + { + poPROJCS->DestroyChild( iSP1Child ); + } + } + } + + } + +/* -------------------------------------------------------------------- */ +/* Translate PROJECTION keywords that are misnamed. */ +/* -------------------------------------------------------------------- */ + GetRoot()->applyRemapper( "PROJECTION", + (char **)apszProjMapping, + (char **)apszProjMapping+1, 2 ); + +/* -------------------------------------------------------------------- */ +/* Translate DATUM keywords that are misnamed. */ +/* -------------------------------------------------------------------- */ + InitDatumMappingTable(); + + GetRoot()->applyRemapper( "DATUM", + (char **)papszDatumMapping+1, + (char **)papszDatumMapping+2, 3 ); + +/* -------------------------------------------------------------------- */ +/* Special case for Peru96 related SRS that should use the */ +/* Peru96 DATUM, but in ESRI world, both Peru96 and SIRGAS-Chile */ +/* are translated as D_SIRGAS-Chile. */ +/* -------------------------------------------------------------------- */ + int bPeru96Datum = FALSE; + if( poDatum != NULL && EQUAL(poDatum->GetValue(), "SIRGAS_Chile") ) + { + const char* pszSRSName = GetAttrValue("PROJCS"); + if( pszSRSName == NULL ) + pszSRSName = GetAttrValue("GEOGCS"); + if( strstr(pszSRSName, "Peru96") ) + { + bPeru96Datum = TRUE; + poDatum->SetValue( "Peru96" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Fix TOWGS84, DATUM or GEOGCS */ +/* -------------------------------------------------------------------- */ + /* TODO test more ESRI WKT; also add PROJCS */ + + /* Check GDAL_FIX_ESRI_WKT config option (default=NO); if YES, set to DATUM */ + const char *pszFixWktConfig=CPLGetConfigOption( "GDAL_FIX_ESRI_WKT", "NO" ); + if ( EQUAL(pszFixWktConfig,"YES") ) + pszFixWktConfig = "DATUM"; + + if( !EQUAL(pszFixWktConfig, "NO") && poDatum != NULL ) + { + CPLDebug( "OGR_ESRI", + "morphFromESRI() looking for missing TOWGS84, datum=%s, config=%s", + pszDatumOrig, pszFixWktConfig ); + + /* Special case for WGS84 and other common GCS? */ + + for( int i = 0; DMGetESRIName(i) != NULL; i++ ) + { + /* we found the ESRI datum name in the map */ + if( EQUAL(DMGetESRIName(i),pszDatumOrig) ) + { + const char *pszFilename = NULL; + char **papszRecord = NULL; + + /* look for GEOGCS corresponding to this datum */ + pszFilename = CSVFilename("gcs.csv"); + papszRecord = CSVScanFileByName( pszFilename, "DATUM_CODE", + DMGetEPSGCode(i), CC_Integer ); + if ( papszRecord != NULL ) + { + /* skip the SIRGAS-Chile record for Peru96 related SRS */ + if( bPeru96Datum && EQUAL(CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"DATUM_NAME")), "SIRGAS-Chile") ) + continue; + + /* make sure we got a valid EPSG code and it is not DEPRECATED */ + int nGeogCS = atoi( CSLGetField( papszRecord, + CSVGetFileFieldId(pszFilename,"COORD_REF_SYS_CODE")) ); + // int bDeprecated = atoi( CSLGetField( papszRecord, + // CSVGetFileFieldId(pszFilename,"DEPRECATED")) ); + + CPLDebug( "OGR_ESRI", "morphFromESRI() got GEOGCS node #%d", nGeogCS ); + + // if ( nGeogCS >= 1 && bDeprecated == 0 ) + if ( nGeogCS >= 1 ) + { + OGRSpatialReference oSRSTemp; + if ( oSRSTemp.importFromEPSG( nGeogCS ) == OGRERR_NONE ) + { + /* make clone of GEOGCS and strip CT parms for testing */ + OGRSpatialReference *poSRSTemp2 = NULL; + int bIsSame = FALSE; + char *pszOtherValue = NULL; + double dfThisValue, dfOtherValue; + OGR_SRSNode *poNode = NULL; + + poSRSTemp2 = oSRSTemp.CloneGeogCS(); + poSRSTemp2->StripCTParms(); + bIsSame = this->IsSameGeogCS( poSRSTemp2 ); + exportToWkt ( &pszOtherValue ); + CPLDebug( "OGR_ESRI", + "morphFromESRI() got SRS %s, matching: %d", + pszOtherValue, bIsSame ); + CPLFree( pszOtherValue ); + delete poSRSTemp2; + + /* clone GEOGCS from original if they match and if allowed */ + if ( EQUAL(pszFixWktConfig,"GEOGCS") + && bIsSame ) + { + this->CopyGeogCSFrom( &oSRSTemp ); + CPLDebug( "OGR_ESRI", + "morphFromESRI() cloned GEOGCS from EPSG:%d", + nGeogCS ); + /* exit loop */ + break; + } + /* else try to copy only DATUM or TOWGS84 + we got here either because of config option or + GEOGCS are not strictly equal */ + else if ( EQUAL(pszFixWktConfig,"GEOGCS") || + EQUAL(pszFixWktConfig,"DATUM") || + EQUAL(pszFixWktConfig,"TOWGS84") ) + { + /* test for matching SPHEROID, because there can be 2 datums with same ESRI name + but different spheroids (e.g. EPSG:4618 and EPSG:4291) - see bug #4345 */ + /* instead of testing for matching SPHEROID name (which can be error-prone), test + for matching parameters (semi-major and inverse flattening ) - see bug #4673 */ + bIsSame = TRUE; + dfThisValue = this->GetSemiMajor(); + dfOtherValue = oSRSTemp.GetSemiMajor(); + if ( ABS( dfThisValue - dfOtherValue ) > 0.01 ) + bIsSame = FALSE; + CPLDebug( "OGR_ESRI", + "morphFromESRI() SemiMajor: this = %.15g other = %.15g", + dfThisValue, dfOtherValue ); + dfThisValue = this->GetInvFlattening(); + dfOtherValue = oSRSTemp.GetInvFlattening(); + if ( ABS( dfThisValue - dfOtherValue ) > 0.0001 ) + bIsSame = FALSE; + CPLDebug( "OGR_ESRI", + "morphFromESRI() InvFlattening: this = %g other = %g", + dfThisValue, dfOtherValue ); + + if ( bIsSame ) + { + /* test for matching PRIMEM, because there can be 2 datums with same ESRI name + but different prime meridian (e.g. EPSG:4218 and EPSG:4802) - see bug #4378 */ + /* instead of testing for matching PRIMEM name (which can be error-prone), test + for matching value - see bug #4673 */ + dfThisValue = this->GetPrimeMeridian(); + dfOtherValue = oSRSTemp.GetPrimeMeridian(); + CPLDebug( "OGR_ESRI", + "morphFromESRI() PRIMEM: this = %.15g other = %.15g", + dfThisValue, dfOtherValue ); + if ( ABS( dfThisValue - dfOtherValue ) > 0.0001 ) + bIsSame = FALSE; + } + + /* found a matching spheroid */ + if ( bIsSame ) + { + /* clone DATUM */ + if ( EQUAL(pszFixWktConfig,"GEOGCS") || + EQUAL(pszFixWktConfig,"DATUM") ) + { + OGR_SRSNode *poGeogCS = this->GetAttrNode( "GEOGCS" ); + const OGR_SRSNode *poDatumOther = oSRSTemp.GetAttrNode( "DATUM" ); + if ( poGeogCS && poDatumOther ) + { + /* make sure we preserve the position of the DATUM node */ + int nPos = poGeogCS->FindChild( "DATUM" ); + if ( nPos >= 0 ) + { + poGeogCS->DestroyChild( nPos ); + poGeogCS->InsertChild( poDatumOther->Clone(), nPos ); + CPLDebug( "OGR_ESRI", + "morphFromESRI() cloned DATUM from EPSG:%d", + nGeogCS ); + } + } + } + /* just copy TOWGS84 */ + else if ( EQUAL(pszFixWktConfig,"TOWGS84") ) + { + poNode=oSRSTemp.GetAttrNode( "DATUM|TOWGS84" ); + if ( poNode ) + { + poNode=poNode->Clone(); + GetAttrNode( "DATUM" )->AddChild( poNode ); + CPLDebug( "OGR_ESRI", + "morphFromESRI() found missing TOWGS84 from EPSG:%d", + nGeogCS ); + } + } + /* exit loop */ + break; + } + } + } + } + } + } + } + } + + CPLFree( pszDatumOrig ); + + return eErr; +} + +/************************************************************************/ +/* OSRMorphFromESRI() */ +/************************************************************************/ + +/** + * \brief Convert in place from ESRI WKT format. + * + * This function is the same as the C++ method OGRSpatialReference::morphFromESRI() + */ +OGRErr OSRMorphFromESRI( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRMorphFromESRI", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->morphFromESRI(); +} + +/************************************************************************/ +/* SetNewName() */ +/* */ +/* Set an esri name */ +/************************************************************************/ +void SetNewName( OGRSpatialReference* pOgr, const char* keyName, const char* newName ) +{ + OGR_SRSNode *poNode = pOgr->GetAttrNode( keyName ); + OGR_SRSNode *poNodeChild = NULL; + if(poNode) + poNodeChild = poNode->GetChild(0); + if( poNodeChild) + poNodeChild->SetValue( newName); +} + +/************************************************************************/ +/* RemapImgWGSProjcsName() */ +/* */ +/* Convert Img projcs names to ESRI style */ +/************************************************************************/ +int RemapImgWGSProjcsName( OGRSpatialReference* pOgr, const char* pszProjCSName, const char* pszProgCSName ) +{ + if(EQUAL(pszProgCSName, "WGS_1972") || EQUAL(pszProgCSName, "WGS_1984") ) + { + char* newName = (char *) CPLMalloc(strlen(pszProjCSName) + 10); + sprintf( newName, "%s_", pszProgCSName ); + strcat(newName, pszProjCSName); + SetNewName( pOgr, "PROJCS", newName ); + CPLFree( newName ); + return 1; + } + return -1; +} + +/************************************************************************/ +/* RemapImgUTMNames() */ +/* */ +/* Convert Img UTM names to ESRI style */ +/************************************************************************/ + +int RemapImgUTMNames( OGRSpatialReference* pOgr, const char* pszProjCSName, const char* pszProgCSName, + char **mappingTable ) +{ + long i; + long iIndex = -1; + for( i = 0; mappingTable[i] != NULL; i += 5 ) + { + if( EQUAL(pszProjCSName, mappingTable[i]) ) + { + long j = i; + while(mappingTable[j] != NULL && EQUAL(mappingTable[i], mappingTable[j])) + { + if( EQUAL(pszProgCSName, mappingTable[j+1]) ) + { + iIndex = j; + break; + } + j += 5; + } + if (iIndex >= 0) + break; + } + } + if(iIndex >= 0) + { + OGR_SRSNode *poNode = pOgr->GetAttrNode( "PROJCS" ); + OGR_SRSNode *poNodeChild = NULL; + if(poNode) + poNodeChild = poNode->GetChild(0); + if( poNodeChild && strlen(poNodeChild->GetValue()) > 0 ) + poNodeChild->SetValue( mappingTable[iIndex+2]); + + poNode = pOgr->GetAttrNode( "GEOGCS" ); + poNodeChild = NULL; + if(poNode) + poNodeChild = poNode->GetChild(0); + if( poNodeChild && strlen(poNodeChild->GetValue()) > 0 ) + poNodeChild->SetValue( mappingTable[iIndex+3]); + + poNode = pOgr->GetAttrNode( "DATUM" ); + poNodeChild = NULL; + if(poNode) + poNodeChild = poNode->GetChild(0); + if( poNodeChild && strlen(poNodeChild->GetValue()) > 0 ) + poNodeChild->SetValue( mappingTable[iIndex+4]); + } + return iIndex; +} + +/************************************************************************/ +/* RemapNameBasedOnKeyName() */ +/* */ +/* Convert a name to ESRI style name */ +/************************************************************************/ + +int RemapNameBasedOnKeyName( OGRSpatialReference* pOgr, const char* pszName, const char* pszkeyName, + char **mappingTable ) +{ + long i; + long iIndex = -1; + for( i = 0; mappingTable[i] != NULL; i += 2 ) + { + if( EQUAL(pszName, mappingTable[i]) ) + { + iIndex = i; + break; + } + } + if(iIndex >= 0) + { + OGR_SRSNode *poNode = pOgr->GetAttrNode( pszkeyName ); + OGR_SRSNode *poNodeChild = NULL; + if(poNode) + poNodeChild = poNode->GetChild(0); + if( poNodeChild && strlen(poNodeChild->GetValue()) > 0 ) + poNodeChild->SetValue( mappingTable[iIndex+1]); + } + return iIndex; +} + +/************************************************************************/ +/* RemapNamesBasedOnTwo() */ +/* */ +/* Convert a name to ESRI style name */ +/************************************************************************/ + +int RemapNamesBasedOnTwo( OGRSpatialReference* pOgr, const char* name1, const char* name2, + char **mappingTable, long nTableStepSize, + char** pszkeyNames, long nKeys ) +{ + long i, n, n1; + long iIndex = -1; + for( i = 0; mappingTable[i] != NULL; i += nTableStepSize ) + { + n = strlen(name1); + n1 = strlen(mappingTable[i]); + if( EQUALN(name1, mappingTable[i], n1<=n? n1 : n) ) + { + long j = i; + while(mappingTable[j] != NULL && EQUAL(mappingTable[i], mappingTable[j])) + { + if( EQUALN(name2, mappingTable[j+1], strlen(mappingTable[j+1])) ) + { + iIndex = j; + break; + } + j += 3; + } + if (iIndex >= 0) + break; + } + } + if(iIndex >= 0) + { + for( i = 0; i < nKeys; i ++ ) + { + OGR_SRSNode *poNode = pOgr->GetAttrNode( pszkeyNames[i] ); + OGR_SRSNode *poNodeChild = NULL; + if(poNode) + poNodeChild = poNode->GetChild(0); + if( poNodeChild && strlen(poNodeChild->GetValue()) > 0 ) + poNodeChild->SetValue( mappingTable[iIndex+i+2]); + } + + } + return iIndex; +} + +/************************************************************************/ +/* RemapPValuesBasedOnProjCSAndPName() */ +/* */ +/* Convert a parameters to ESRI style name */ +/************************************************************************/ + +int RemapPValuesBasedOnProjCSAndPName( OGRSpatialReference* pOgr, const char* pszProgCSName, + char **mappingTable ) +{ + long ret = 0; + OGR_SRSNode *poPROJCS = pOgr->GetAttrNode( "PROJCS" ); + for( int i = 0; mappingTable[i] != NULL; i += 4 ) + { + while( mappingTable[i] != NULL && EQUALN(pszProgCSName, mappingTable[i], strlen(mappingTable[i])) ) + { + OGR_SRSNode *poParm; + const char* pszParamName = mappingTable[i+1]; + const char* pszParamValue = mappingTable[i+2]; + for( int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ ) + { + poParm = poPROJCS->GetChild( iChild ); + + if( EQUAL(poParm->GetValue(),"PARAMETER") + && poParm->GetChildCount() == 2 + && EQUAL(poParm->GetChild(0)->GetValue(),pszParamName) + && EQUALN(poParm->GetChild(1)->GetValue(),pszParamValue, strlen(pszParamValue) ) ) + { + poParm->GetChild(1)->SetValue( mappingTable[i+3] ); + break; + } + } + ret ++; + i += 4; + } + if (ret > 0) + break; + } + return ret; +} + +/************************************************************************/ +/* RemapPNamesBasedOnProjCSAndPName() */ +/* */ +/* Convert a parameters to ESRI style name */ +/************************************************************************/ + +int RemapPNamesBasedOnProjCSAndPName( OGRSpatialReference* pOgr, const char* pszProgCSName, + char **mappingTable ) +{ + long ret = 0; + OGR_SRSNode *poPROJCS = pOgr->GetAttrNode( "PROJCS" ); + for( int i = 0; mappingTable[i] != NULL; i += 3 ) + { + while( mappingTable[i] != NULL && EQUALN(pszProgCSName, mappingTable[i], strlen(mappingTable[i])) ) + { + OGR_SRSNode *poParm; + const char* pszParamName = mappingTable[i+1]; + for( int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ ) + { + poParm = poPROJCS->GetChild( iChild ); + + if( EQUAL(poParm->GetValue(),"PARAMETER") + && poParm->GetChildCount() == 2 + && EQUAL(poParm->GetChild(0)->GetValue(),pszParamName) ) + { + poParm->GetChild(0)->SetValue( mappingTable[i+2] ); + break; + } + } + ret ++; + i += 3; + } + if (ret > 0) + break; + } + return ret; +} + +/************************************************************************/ +/* DeleteParamBasedOnPrjName */ +/* */ +/* Delete non-ESRI parameters */ +/************************************************************************/ +int DeleteParamBasedOnPrjName( OGRSpatialReference* pOgr, const char* pszProjectionName, + char **mappingTable ) +{ + long iIndex = -1, ret = -1; + for( int i = 0; mappingTable[i] != NULL; i += 2 ) + { + if( EQUALN(pszProjectionName, mappingTable[i], strlen(mappingTable[i])) ) + { + OGR_SRSNode *poPROJCS = pOgr->GetAttrNode( "PROJCS" ); + OGR_SRSNode *poParm; + const char* pszParamName = mappingTable[i+1]; + iIndex = -1; + for( int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ ) + { + poParm = poPROJCS->GetChild( iChild ); + + if( EQUAL(poParm->GetValue(),"PARAMETER") + && poParm->GetChildCount() == 2 + && EQUAL(poParm->GetChild(0)->GetValue(),pszParamName) ) + { + iIndex = iChild; + break; + } + } + if(iIndex >= 0) + { + poPROJCS->DestroyChild( iIndex ); + ret ++; + } + } + } + return ret; +} +/************************************************************************/ +/* AddParamBasedOnPrjName() */ +/* */ +/* Add ESRI style parameters */ +/************************************************************************/ +int AddParamBasedOnPrjName( OGRSpatialReference* pOgr, const char* pszProjectionName, + char **mappingTable ) +{ + long ret = -1; + OGR_SRSNode *poPROJCS = pOgr->GetAttrNode( "PROJCS" ); + for( int i = 0; mappingTable[i] != NULL; i += 3 ) + { + if( EQUALN(pszProjectionName, mappingTable[i], strlen(mappingTable[i])) ) + { + OGR_SRSNode *poParm; + int exist = 0; + for( int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ ) + { + poParm = poPROJCS->GetChild( iChild ); + + if( EQUAL(poParm->GetValue(),"PARAMETER") + && poParm->GetChildCount() == 2 + && EQUAL(poParm->GetChild(0)->GetValue(),mappingTable[i+1]) ) + exist = 1; + } + if(!exist) + { + poParm = new OGR_SRSNode( "PARAMETER" ); + poParm->AddChild( new OGR_SRSNode( mappingTable[i+1] ) ); + poParm->AddChild( new OGR_SRSNode( mappingTable[i+2] ) ); + poPROJCS->AddChild( poParm ); + ret ++; + } + } + } + return ret; +} + +/************************************************************************/ +/* RemapGeogCSName() */ +/* */ +/* Convert names to ESRI style */ +/************************************************************************/ +int RemapGeogCSName( OGRSpatialReference* pOgr, const char *pszGeogCSName ) +{ + static const char *keyNamesG[] = { + "GEOGCS"}; + int ret = -1; + + const char* pszUnitName = pOgr->GetAttrValue( "GEOGCS|UNIT"); + if(pszUnitName) + ret = RemapNamesBasedOnTwo( pOgr, pszGeogCSName+4, pszUnitName, (char**)apszGcsNameMappingBasedOnUnit, 3, (char**)keyNamesG, 1); + if(ret < 0) + { + const char* pszPrimeName = pOgr->GetAttrValue("PRIMEM"); + if(pszPrimeName) + ret = RemapNamesBasedOnTwo( pOgr, pszGeogCSName+4, pszPrimeName, (char**)apszGcsNameMappingBasedPrime, 3, (char**)keyNamesG, 1); + if(ret < 0) + ret = RemapNameBasedOnKeyName( pOgr, pszGeogCSName+4, "GEOGCS", (char**)apszGcsNameMapping); + } + if(ret < 0) + { + const char* pszProjCS = pOgr->GetAttrValue( "PROJCS" ); + ret = RemapNamesBasedOnTwo( pOgr, pszProjCS, pszGeogCSName, (char**)apszGcsNameMappingBasedOnProjCS, 3, (char**)keyNamesG, 1); + } + return ret; +} + +/************************************************************************/ +/* ImportFromESRIStatePlaneWKT() */ +/* */ +/* Search a ESRI State Plane WKT and import it. */ +/************************************************************************/ + +OGRErr OGRSpatialReference::ImportFromESRIStatePlaneWKT( int code, const char* datumName, const char* unitsName, int pcsCode, const char* csName ) +{ + int i; + long searchCode = -1; + + /* if the CS name is known */ + if (code == 0 && !datumName && !unitsName && pcsCode == 32767 && csName) + { + char codeS[10]; + if (FindCodeFromDict( "esri_StatePlane_extra.wkt", csName, codeS ) != OGRERR_NONE) + return OGRERR_FAILURE; + return importFromDict( "esri_StatePlane_extra.wkt", codeS); + } + + /* Find state plane prj str by pcs code only */ + if( code == 0 && !datumName && pcsCode != 32767 ) + { + + int unitCode = 1; + if( EQUAL(unitsName, "international_feet") ) + unitCode = 3; + else if( strstr(unitsName, "feet") || strstr(unitsName, "foot") ) + unitCode = 2; + for(i=0; statePlanePcsCodeToZoneCode[i] != 0; i+=2) + { + if( pcsCode == statePlanePcsCodeToZoneCode[i] ) + { + searchCode = statePlanePcsCodeToZoneCode[i+1]; + int unitIndex = searchCode % 10; + if( (unitCode == 1 && !(unitIndex == 0 || unitIndex == 1)) + || (unitCode == 2 && !(unitIndex == 2 || unitIndex == 3 || unitIndex == 4 )) + || (unitCode == 3 && !(unitIndex == 5 || unitIndex == 6 )) ) + { + searchCode -= unitIndex; + switch (unitIndex) + { + case 0: + case 3: + case 5: + if(unitCode == 2) + searchCode += 3; + else if(unitCode == 3) + searchCode += 5; + break; + case 1: + case 2: + case 6: + if(unitCode == 1) + searchCode += 1; + if(unitCode == 2) + searchCode += 2; + else if(unitCode == 3) + searchCode += 6; + break; + case 4: + if(unitCode == 2) + searchCode += 4; + break; + } + } + break; + } + } + } + else /* Find state plane prj str by all inputs. */ + { + /* Need to have a specail EPSG-ESRI zone code mapping first. */ + for(i=0; statePlaneZoneMapping[i] != 0; i+=3) + { + if( code == statePlaneZoneMapping[i] + && (statePlaneZoneMapping[i+1] == -1 || pcsCode == statePlaneZoneMapping[i+1])) + { + code = statePlaneZoneMapping[i+2]; + break; + } + } + searchCode = (long)code * 10; + if(EQUAL(datumName, "HARN")) + { + if( EQUAL(unitsName, "international_feet") ) + searchCode += 5; + else if( strstr(unitsName, "feet") || strstr(unitsName, "foot") ) + searchCode += 3; + } + else if(strstr(datumName, "NAD") && strstr(datumName, "83")) + { + if( EQUAL(unitsName, "meters") ) + searchCode += 1; + else if( EQUAL(unitsName, "international_feet") ) + searchCode += 6; + else if( strstr(unitsName, "feet") || strstr(unitsName, "foot") ) + searchCode += 2; + } + else if(strstr(datumName, "NAD") && strstr(datumName, "27") && !EQUAL(unitsName, "meters")) + { + searchCode += 4; + } + else + searchCode = -1; + } + if(searchCode > 0) + { + char codeS[10]; + sprintf(codeS, "%d", (int)searchCode); + return importFromDict( "esri_StatePlane_extra.wkt", codeS); + } + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* ImportFromESRIWisconsinWKT() */ +/* */ +/* Search a ESRI State Plane WKT and import it. */ +/************************************************************************/ + +OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT( const char* prjName, double centralMeridian, double latOfOrigin, const char* unitsName, const char* csName ) +{ + /* if the CS name is known */ + if (!prjName && !unitsName && csName) + { + char codeS[10]; + if (FindCodeFromDict( "esri_Wisconsin_extra.wkt", csName, codeS ) != OGRERR_NONE) + return OGRERR_FAILURE; + return importFromDict( "esri_Wisconsin_extra.wkt", codeS); + } + double* tableWISCRS; + if(EQUALN(prjName, "Lambert_Conformal_Conic", 22)) + tableWISCRS = apszWISCRS_LCC_meter; + else if(EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR)) + tableWISCRS = apszWISCRS_TM_meter; + else + return OGRERR_FAILURE; + int k = -1; + for(int i=0; tableWISCRS[i] != 0; i+=3) + { + if( fabs(centralMeridian - tableWISCRS[i]) <= 0.0000000001 && fabs(latOfOrigin - tableWISCRS[i+1]) <= 0.0000000001) + { + k = (long)tableWISCRS[i+2]; + break; + } + } + if(k > 0) + { + if(!EQUAL(unitsName, "meters")) + k += 100; + char codeS[10]; + sprintf(codeS, "%d", k); + return importFromDict( "esri_Wisconsin_extra.wkt", codeS); + } + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* FindCodeFromDict() */ +/* */ +/* Find the code from a dict file. */ +/************************************************************************/ +static int FindCodeFromDict( const char* pszDictFile, const char* CSName, char* code ) +{ + const char *pszFilename; + FILE *fp; + OGRErr eErr = OGRERR_UNSUPPORTED_SRS; + +/* -------------------------------------------------------------------- */ +/* Find and open file. */ +/* -------------------------------------------------------------------- */ + pszFilename = CPLFindFile( "gdal", pszDictFile ); + if( pszFilename == NULL ) + return OGRERR_UNSUPPORTED_SRS; + + fp = VSIFOpen( pszFilename, "rb" ); + if( fp == NULL ) + return OGRERR_UNSUPPORTED_SRS; + +/* -------------------------------------------------------------------- */ +/* Process lines. */ +/* -------------------------------------------------------------------- */ + const char *pszLine; + + while( (pszLine = CPLReadLine(fp)) != NULL ) + + { + if( pszLine[0] == '#' ) + /* do nothing */; + + else if( strstr(pszLine,CSName) ) + { + const char* pComma = strchr(pszLine, ','); + if( pComma ) + { + strncpy( code, pszLine, pComma - pszLine); + code[pComma - pszLine] = '\0'; + eErr = OGRERR_NONE; + } + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + VSIFClose( fp ); + + return eErr; +} diff --git a/bazaar/plugin/gdal/ogr/ogr_srs_esri_names.h b/bazaar/plugin/gdal/ogr/ogr_srs_esri_names.h new file mode 100644 index 000000000..964aeabcc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srs_esri_names.h @@ -0,0 +1,717 @@ +static const char *apszGcsNameMapping[] = { +"North_American_Datum_1983", "GCS_North_American_1983", +"North_American_Datum_1927", "GCS_North_American_1927", +"NAD27_CONUS", "GCS_North_American_1927", +"Reseau_Geodesique_de_Nouvelle_Caledonie_1991-93", "GCS_RGNC_1991-93", +"Reseau_Geodesique_de_la_Polynesie_Francaise", "GCS_RGPF", +"Rauenberg_1983", "GCS_RD/83", +"Phillipine_Reference_System_1992", "GCS_PRS_1992", +"Potsdam_1983", "GCS_PD/83", +"Datum_Geodesi_Nasional_1995", "GCS_DGN_1995", +"Islands_Network_1993", "GCS_ISN_1993", +"Institut_Geographique_du_Congo_Belge_1955", "GCS_IGCB_1955", +"IGC_1962_Arc_of_the_6th_Parallel_South", "GCS_IGC_1962_6th_Parallel_South", +"Jamaica_2001", "GCS_JAD_2001", +"European_Libyan_1979", "GCS_European_Libyan_Datum_1979", +"Madrid_1870", "GCS_Madrid_1870_Madrid", +"Azores_Occidental_Islands_1939", "GCS_Azores_Occidental_1939", +"Azores_Central_Islands_1948", "GCS_Azores_Central_1948", +"Azores_Oriental_Islands_1940", "GCS_Azores_Oriental_1940", +"Lithuania_1994", "GCS_LKS_1994", +"Libyan_Geodetic_Datum_2006", "GCS_LGD2006", +//"Lisbon", "GCS_Lisbon_Lisbon", +"Stockholm_1938", "GCS_RT38", +"Latvia_1992", "GCS_LKS_1992", +"Azores_Oriental_Islands_1995", "GCS_Azores_Oriental_1995", +"Azores_Central_Islands_1948", "GCS_Azores_Central_1948", +"Azores_Central_Islands_1995", "GCS_Azores_Central_1995", +"ATF", "GCS_ATF_Paris", +//"ITRF_2000", "GCS_MONREF_1997", +"Faroe_Datum_1954", "GCS_FD_1954", +"Vietnam_2000", "GCS_VN_2000", +//"Belge_1950", "GCS_Belge_1950_Brussels", +"Qatar_1948", "GCS_Qatar_1948", +"Qatar", "GCS_Qatar_1974", +"Kuwait_Utility", "GCS_KUDAMS", +"ED_1950_16", "GCS_European_1950", +"SAD_1969_Mean", "GCS_South_American_1969", +"Sphere_of_Radius_6370997m", "GCS_Sphere_ARC_INFO", +"Australian_Geodetic_1966", "GCS_Australian_1966", +"Australian_Geodetic_1984", "GCS_Australian_1984", +"AGD84", "GCS_Australian_1984", +"AGD66", "GCS_Australian_1966", +"Rome_1940", "GCS_Monte_Mario", +"Tokyo_Japan", "GCS_Tokyo", +"Graciosa_Base_SW_1948_1", "GCS_Graciosa_Base_SW_1948", +"Datum_Lisboa_Bessel_1", "GCS_Datum_Lisboa_Bessel", +"Datum_Lisboa_Hayford_1", "GCS_Datum_Lisboa_Hayford", +"Observatorio_Metereo_1939_Grupo_Ocidental", "GCS_Observ_Meteorologico_1939", +"Porto_Santo_1936_1", "GCS_Porto_Santo_1936", +"Sao_Braz_1", "GCS_Sao_Braz", +"GDA94", "GCS_GDA_1994", +"HARN", "GCS_North_American_1983_HARN", +"NAD83_HARN", "GCS_North_American_1983_HARN", +"Voirol_1875", "GCS_Voirol_1875", +"Voirol_1960", "GCS_Voirol_Unifie_1960", +"Ain_el_Abd_1970_Bahrain", "GCS_Ain_el_Abd_1970", +"ED_1950_ED77", "GCS_European_1950_ED77", +"Naparima_1955_2", "GCS_Naparima_1955", +"Aratu_Brazil_Campos_Espirito_Santo_and_Santos_basins", "GCS_Aratu", +"Camacupa_Angola_1", "GCS_Camacupa", +"Cape_1", "GCS_Cape", +"Carthage_Tunisia", "GCS_Carthage", +"Deir_ez_Zor_2", "GCS_Deir_ez_Zor", +"Old_Egyptian_1907", "GCS_Egypt_1907", +"PSAD56", "GCS_Provisional_S_American_1956", +"Indian 1975", "GCS_Indian_1975", +"Indian_1960_1", "GCS_Indian_1960", +"Kalianpur_1937_1", "GCS_Kalianpur_1937", +"Kertau_1948", "GCS_Kertau", +"Kertau_1968", "GCS_Kertau", +"Luzon", "GCS_Luzon_1911", +"Malongo_1987_1", "GCS_Malongo_1987", +"Minna_Cameroon", "GCS_Minna", +"Mporaloko_1", "GCS_Mporaloko", +"Nahrwan_Oman", "GCS_Nahrwan_1967", +"Naparima_BWI", "GCS_Naparima_1972", +"Geodetic_Datum_1949", "GCS_New_Zealand_1949", +"Qatar_National", "GCS_Qatar_1974", +"SAD_1969_Mean", "GCS_South_American_1969", +"Tananarive_Observatory_1925", "GCS_Tananarive_1925", +"Tananarive", "GCS_Tananarive_1925", +"Ireland_1965", "GCS_TM65", +"DE_DHDN_whole_country_2001_to_ETRS89", "GCS_Deutsches_Hauptdreiecksnetz", +"Belge_1972_1", "GCS_Belge_1972", +"WGS_72", "GCS_WGS_1972", +"JGD2000", "GCS_JGD_2000", +"NZGD49", "GCS_New_Zealand_1949", +"CH1903_1", "GCS_CH1903", +"DE_42/83_to_ETRS89", "GCS_Pulkovo_1942", +"DE_42_83_to_ETRS89", "GCS_Pulkovo_1942", +"Amersfoort_1", "GCS_Amersfoort", +"CH1903+_L+T1997", "GCS_CH1903+", +"Ord_Survey_G_Britain_1936", "GCS_OSGB_1936", +"European_Datum_1950", "GCS_European_1950", +"Geocentric_Datum_of_Australia_1994", "GCS_GDA_1994", +"NAD83_High_Accuracy_Regional_Network", "GCS_North_American_1983_HARN", +"Bogota_1975", "GCS_Bogota", +"North_American_Datum_1927_CGQ77", "GCS_NAD_1927_CGQ77", +"North_American_Datum_1927_1976", "GCS_NAD_1927_Definition_1976", +"European_Datum_1950_1977", "GCS_European_1950_ED77", +"WGS_1972_Transit_Broadcast_Ephemeris", "GCS_WGS_1972_BE", +"Greek_Geodetic_Reference_System_1987", "GCS_GGRS_1987", +"Militar_Geographische_Institute", "GCS_MGI", +"ED50", "GCS_European_1950", +"ETRS89", "GCS_ETRS_1989", +NULL, NULL}; + +static const char *apszGcsNameMappingBasedOnProjCS[] = { +"EUREF_FIN_TM35FIN", "GCS_ETRS_1989", "GCS_EUREF_FIN", +"Nord_Maroc_Degree", "GCS_Merchich", "GCS_Merchich_Degree", +"Sahara_Degree", "GCS_Merchich", "GCS_Merchich_Degree", +"Sud_Maroc_Degree", "GCS_Merchich", "GCS_Merchich_Degree", +"Merchich_Degree_UTM_Zone_28N", "GCS_Merchich", "GCS_Merchich_Degree", +"Lambert_Conformal_Conic", "GCS_Merchich", "GCS_Merchich_Degree", +"UTM", "GCS_Merchich", "GCS_Merchich_Degree", +"UTM_Zone_28_Northern_Hemisphere", "GCS_Merchich", "GCS_Merchich_Degree", +"Portuguese_National_Grid", "GCS_Lisbon", "GCS_Lisbon_Lisbon", +"Belge_Lambert_1950", "GCS_Belge_1950", "GCS_Belge_1950_Brussels", +"MONREF_1997_UTM_Zone_46N", "GCS_ITRF_2000", "GCS_MONREF_1997", +"MONREF_1997_UTM_Zone_47N", "GCS_ITRF_2000", "GCS_MONREF_1997", +NULL, NULL, NULL}; + + + +static const char *apszGcsNameMappingBasedOnUnit[] = { +"Voirol_Unifie_1960", "Degree", "GCS_Voirol_Unifie_1960_Degree", +"Voirol_1960", "Degree", "GCS_Voirol_Unifie_1960_Degree", +"Voirol 1960", "Degree", "GCS_Voirol_Unifie_1960_Degree", +"Voirol_1875", "Degree", "GCS_Voirol_1875_Degree", +"Voirol 1875", "Degree", "GCS_Voirol_1875_Degree", +"NTF", "Grad", "GCS_NTF_Paris", +NULL, NULL, NULL}; + +static const char *apszGcsNameMappingBasedPrime[] = { +"Bern_1898", "Bern", "GCS_Bern_1898_Bern", +"Madrid_1870", "Madrid", "GCS_Madrid_1870_Madrid", +"MGI", "Ferro", "GCS_MGI_Ferro", +"MGI", "Stockholm", "GCS_RT38_Stockholm", +"Monte_Mario", "Rome", "GCS_Monte_Mario_Rome", +"NGO_1948", "Oslo", "GCS_NGO_1948_Oslo", +"S_JTSK", "Ferro", "GCS_S_JTSK_Ferro", +"Stockholm_1938", "Stockholm", "GCS_RT38_Stockholm", +NULL, NULL, NULL}; + +static const char *apszInvFlatteningMapping[] = { +"293.464999999", "293.465", +"293.465000003", "293.465", +"293.465073361", "293.465", +"293.466020000", "293.46602", +"293.466021293", "293.46602", +"293.4663077168286", "293.466307656", +"293.4664236085404", "293.466307656", +"294.2606763690", "294.260676369", +"294.9786981999", "294.9786982", +"294.978698213", "294.9786982", +"295.9999999999", "296.0", +"297.0000000000", "297.0", +"297.0000000284", "297.0", +"297.0000535480", "297.0", +"298.2499972761", "298.25", +"298.2500000654", "298.25", +"298.2500112226", "298.25", +"298.256999999", "298.257", +"298.2600000000", "298.26", +"298.2571643544962", "298.257223563", +"298.25716435449", "298.257222101", +"298.257222096042", "298.257222101", +"298.25722210100", "298.257222101", +"298.25722356299", "298.257223563", +"298.25722356300", "298.257223563", +"298.25999858999", "298.26", +"298.2684109950054", "298.268410995005", +"298.2999", "298.3", +"298.3000", "298.3", +"299.1527033239203", "299.1528128", +"299.15281280000", "299.1528128", +"299.15281283", "299.1528128", +"299.15281310607", "299.1528128", +"299.15281327254", "299.1528128", +"299.32496460000", "299.3249646", +"299.32496405862", "299.3249646", +"299.32497531503", "299.3249646", +"300.80158474106", "300.8017", +"300.80169943849", "300.8017", +"300.80169999999", "300.8017", +"300.80170000000", "300.8017", +"300.80170009712", "300.8017", +NULL, NULL}; + +static const char *apszParamValueMapping[] = { +"Cassini", "false_easting", "283799.9999", "283800.0", +"Cassini", "false_easting", "132033.9199", "132033.92", +"Cassini", "false_northing", "214499.9999", "214500.0", +"Cassini", "false_northing", "62565.9599", "62565.95", +"Transverse_Mercator", "false_easting", "499999.1331", "500000.0", +"Transverse_Mercator", "false_easting", "299999.4798609", "300000.0", +"Transverse_Mercator", "false_northing", "399999.30648", "400000.0", +"Transverse_Mercator", "false_northing", "499999.1331", "500000.0", +"Transverse_Mercator", "central_meridian","51.21666666666668", "51.21666666666667", +"Transverse_Mercator", "Scale_Factor", "0.999601272", "0.9996012717", +"Lambert_Conformal_Conic", "central_meridian", "-90.33333333333334", "-90.33333333333333", +"Lambert_Conformal_Conic", "central_meridian", "-76.83333333333334", "-76.83333333333333", +"Krovak", "longitude_of_center", "24.83333333333334", "24.83333333333333", +"Hotine_Oblique_Mercator_Azimuth_Center", "longitude_of_center", "7.439583333333334", "7.439583333333333", +"Hotine_Oblique_Mercator_Azimuth_Center", "latitude_of_center", "46.95240555555557", "46.95240555555556", +NULL, NULL, NULL, NULL}; + +static const char *apszParamNameMapping[] = { +"Lambert_Azimuthal_Equal_Area", "longitude_of_center", "Central_Meridian", +"Lambert_Azimuthal_Equal_Area", "Latitude_Of_Center", "Latitude_Of_Origin", +"Miller_Cylindrical", "longitude_of_center", "Central_Meridian", +"Gnomonic", "central_meridian", "Longitude_Of_Center", +"Gnomonic", "latitude_of_origin", "Latitude_Of_Center", +"Orthographic", "central_meridian", "Longitude_Of_Center", +"Orthographic", "latitude_of_origin", "Latitude_Of_Center", +"New_Zealand_Map_Grid", "central_meridian", "Longitude_Of_Origin", +"Hotine_Oblique_Mercator_Two_Point_Natural_Origin", "latitude_of_point_1", "Latitude_Of_1st_Point", +"Hotine_Oblique_Mercator_Two_Point_Natural_Origin", "longitude_of_point_1", "Longitude_Of_1st_Point", +"Hotine_Oblique_Mercator_Two_Point_Natural_Origin", "latitude_of_point_2", "Latitude_Of_2nd_Point", +"Hotine_Oblique_Mercator_Two_Point_Natural_Origin", "longitude_of_point_2", "Longitude_Of_2nd_Point", +NULL, NULL, NULL}; + +static const char *apszDeleteParametersBasedOnProjection[] = { +"Stereographic_South_Pole", "scale_factor", +"Stereographic_North_Pole", "scale_factor", +"Mercator", "scale_factor", +"Miller_Cylindrical", "latitude_of_center", +"Equidistant_Cylindrical", "pseudo_standard_parallel_1", +"Equidistant_Cylindrical", "latitude_of_origin", +"Plate_Carree", "latitude_of_origin", +"Plate_Carree", "pseudo_standard_parallel_1", +"Plate_Carree", "standard_parallel_1", +"Hotine_Oblique_Mercator_Azimuth_Center", "rectified_grid_angle", +"Hotine_Oblique_Mercator_Azimuth_Natural_Origin", "rectified_grid_angle", +NULL, NULL}; + +static const char *apszAddParametersBasedOnProjection[] = { +"Cassini", "scale_factor", "1.0", +"Mercator", "standard_parallel_1", "0.0", +NULL, NULL, NULL}; + +static int statePlaneZoneMapping[] = { +/* old zone code, prj code, new zone code */ + 3126, -1, 101, + 3151, -1, 102, + 3176, -1, 202, + 3201, -1, 203, + 3226, -1, 301, + 3251, -1, 302, + 3326, -1, 403, + 3351, -1, 404, + 3376, 26945, 405, + 3426, -1, 407, + 3451, -1, 501, + 3476, -1, 502, + 3526, -1, 600, + 3551, -1, 700, + 3576, -1, 903, + 3626, -1, 902, + 3651, -1, 1001, + 3676, -1, 1002, + 3726, -1, 1102, + 3751, -1, 1103, + 3776, -1, 1201, + 3801, -1, 1202, + 3826, -1, 1301, + 3851, -1, 1302, + 3876, -1, 1401, + 3926, -1, 1501, + 3951, -1, 1502, + 3976, -1, 1601, + 4026, -1, 1701, + 6426, -1, 1703, + 4076, -1, 1801, + 4101, -1, 1802, + 4126, -1, 1900, + 4151, -1, 2001, + 4176, -1, 2002, + 4226, -1, 2102, + 4251, -1, 2103, + 6351, -1, 2111, + 6376, -1, 2112, + 6401, -1, 2113, + 4276, -1, 2201, + 4326, -1, 2203, + 4351, -1, 2301, + 4376, -1, 2302, + 4400, 32045, 3400, + 4401, -1, 2401, + 4426, -1, 2402, + 4451, -1, 2403, + 4476, 32100, 2500, + 4476, -1, 2501, + 4701, 32111, 2900, + 4801, 2260, 3101, + 4801, 32115, 3101, + 4526, -1, 2503, + 4551, -1, 2601, + 4576, -1, 2602, + 4626, -1, 2702, + 4651, -1, 2703, + 4676, -1, 2800, + 4726, -1, 3001, + 4751, -1, 3002, + 4776, -1, 3003, + 4826, -1, 3102, + 4851, -1, 3103, + 4876, -1, 3104, + 4926, -1, 3301, + 4951, -1, 3302, + 4976, -1, 3401, + 5026, -1, 3501, + 5051, -1, 3502, + 5076, -1, 3601, + 5126, -1, 3701, + 5151, -1, 3702, + 5176, -1, 3800, + 5226, -1, 3902, + 5251, -1, 4001, + 5276, -1, 4002, + 5301, -1, 4100, + 5326, -1, 4201, + 5351, -1, 4202, + 5376, -1, 4203, + 5401, -1, 4204, + 5426, -1, 4205, + 5451, -1, 4301, + 5476, -1, 4302, + 5501, -1, 4303, + 5526, -1, 4400, + 5551, -1, 4501, + 5576, -1, 4502, + 5601, -1, 4601, + 5626, -1, 4602, + 5651, -1, 4701, + 5676, -1, 4702, + 5701, -1, 4801, + 5726, -1, 4802, + 5751, -1, 4803, + 5776, -1, 4901, + 5801, -1, 4902, + 5826, -1, 4903, + 5851, -1, 4904, + 6101, -1, 5001, + 6126, -1, 5002, + 6151, -1, 5003, + 6176, -1, 5004, + 6201, -1, 5005, + 6226, -1, 5006, + 6251, -1, 5007, + 6276, -1, 5008, + 6301, -1, 5009, + 6326, -1, 5010, + 5876, -1, 5101, + 5901, -1, 5102, + 5926, -1, 5103, + 5951, -1, 5104, + 5976, -1, 5105, + 6001, -1, 5201, + 6026, -1, 5200, + 6076, -1, 5200, + 6051, -1, 5202, + 0, 0, 0 + }; + +/* This is not a complete mapping. Need to add more. */ +static int statePlanePcsCodeToZoneCode[] = { +/* pcs code, state plane prj str index*/ +2222, 2016, +2223, 2026, +2224, 2036, +2225, 4012, +2226, 4022, +2227, 4032, +2228, 4042, +2229, 4052, +2230, 4062, +2231, 5012, +2232, 5022, +2233, 5032, +2234, 6002, +2235, 7002, +2236, 9012, +2237, 9022, +2238, 9032, +2239, 10012, +2240, 10022, +2241, 11012, +2242, 11022, +2243, 11032, +2251, 21116, +2252, 21126, +2253, 21136, +2256, 25006, +2265, 33016, +2266, 33026, +2965, 13012, +2966, 13022, +2246, 16012, +2247, 16022, +2248, 19002, +2249, 20012, +2250, 20022, +2254, 23012, +2255, 23022, +2257, 30012, +2258, 30022, +2259, 30032, +2260, 31012, +2261, 31022, +2262, 31032, +2263, 31042, +2264, 32002, +2267, 35012, +2268, 35022, +2269, 36016, +2270, 36026, +2271, 37012, +2272, 37022, +2273, 39006, +2274, 41002, +2275, 42012, +2276, 42022, +2277, 42032, +2278, 42042, +2279, 42052, +2280, 43016, +2281, 43026, +2282, 43036, +2283, 45012, +2284, 45022, +2285, 46012, +2286, 46022, +2287, 48012, +2288, 48022, +2289, 48032, +2867, 2015, +2868, 2025, +2869, 2035, +2896, 21115, +2897, 21125, +2898, 21135, +2901, 25005, +2909, 33015, +2910, 33025, +2913, 36015, +2914, 36025, +2921, 43015, +2922, 43025, +2923, 43035, +2870, 4013, +2871, 4023, +2872, 4033, +2873, 4043, +2874, 4053, +2875, 4063, +2876, 5013, +2877, 5023, +2878, 5033, +2879, 6003, +2880, 7003, +2881, 9013, +2882, 9023, +2883, 9033, +2884, 10013, +2885, 10023, +2886, 11013, +2887, 11023, +2888, 11033, +2967, 13013, +2968, 13023, +2891, 16013, +2892, 16023, +2893, 19003, +2894, 20013, +2895, 20023, +2899, 23013, +2900, 23023, +2902, 30013, +2903, 30023, +2904, 30033, +2905, 31013, +2906, 31023, +2907, 31033, +2908, 31043, +2911, 35013, +2912, 35023, +2915, 41003, +2916, 42013, +2917, 42023, +2918, 42033, +2919, 42043, +2920, 42053, +2924, 45013, +2925, 45023, +2926, 46013, +2927, 46023, +2928, 48013, +2929, 48023, +2930, 48033, +// following are state systems (not complete) +2964, 102965, +2991, 102991, +2992, 102992, +2993, 102993, +2994, 102994, +// following are NAD 1983 SPCS Zone +26929, 1011, +26930, 1021, +26931, 50011, +26932, 50021, +26933, 50031, +26934, 50041, +26935, 50051, +26936, 50061, +26937, 50071, +26938, 50081, +26939, 50091, +26940, 50101, +26948, 2011, +26949, 2021, +26950, 2031, +26951, 3011, +26952, 3021, +26941, 4011, +26942, 4021, +26943, 4031, +26944, 4041, +26945, 4051, +26946, 4061, +26953, 5011, +26954, 5021, +26955, 5031, +26956, 6001, +26957, 7001, +26958, 9011, +26959, 9021, +26960, 9031, +26966, 10011, +26967, 10021, +26961, 51011, +26962, 51021, +26963, 51031, +26964, 51041, +26965, 51051, +26968, 11011, +26969, 11021, +26970, 11031, +26971, 12011, +26972, 12021, +26973, 13011, +26974, 13021, +26975, 14011, +26976, 14021, +26977, 15011, +26978, 15021, +26979, 16011, +26980, 16021, +26981, 17011, +26982, 17021, +26983, 18011, +26984, 18021, +26985, 19001, +26986, 20011, +26987, 20021, +26988, 21111, +26989, 21121, +26990, 21131, +26991, 22011, +26992, 22021, +26993, 22031, +26994, 23011, +26995, 23021, +26996, 24011, +26997, 24021, +26998, 24031, +32100, 25001, +32104, 26001, +32107, 27011, +32108, 27021, +32109, 27031, +32110, 28001, +32111, 29001, +32112, 30011, +32113, 30021, +32114, 30031, +32115, 31011, +32116, 31021, +32117, 31031, +32118, 31041, +32119, 32001, +32120, 33011, +32121, 33021, +32122, 34011, +32123, 34021, +32124, 35011, +32125, 35021, +32126, 36011, +32127, 36021, +32128, 37011, +32129, 37021, +32130, 38001, +32133, 39001, +32134, 40011, +32135, 40021, +32136, 41001, +32137, 42011, +32138, 42021, +32139, 42031, +32140, 42041, +32141, 42051, +32142, 43011, +32143, 43021, +32144, 43031, +32145, 44001, +32146, 45011, +32147, 45021, +32148, 46011, +32149, 46021, +32150, 47011, +32151, 47021, +32152, 48011, +32153, 48021, +32154, 48031, +32155, 49011, +32156, 49021, +32157, 49031, +32158, 49041, +32161, 52000, +65161, 54001, +0, 0 +}; + +/* ==================================================================== */ +/* WISCRS Table */ +/* ==================================================================== */ +static double apszWISCRS_LCC_meter[] = { +// Central_Meridian, Latitude_Of_Origin, SR code + -91.1527777777, 46.6696483772, 103303.0, + -92.4577777777, 45.8987148658, 103306.0, + -91.2944444444, 44.9778568986, 103308.0, + -89.3944444444, 43.4625466458, 103310.0, + -90.9388888888, 43.2000556050, 103311.0, + -89.4222222222, 43.0695160375, 103312.0, + -91.2888888888, 45.8722811263, 103317.0, + -89.8388888888, 42.6375622769, 103322.0, + -89.2416666666, 43.8070001177, 103323.0, + -89.8388888888, 42.6375622769, 103332.0, + -89.0333333333, 45.1542371052, 103333.0, + -89.7700000000, 44.9009044236, 103336.0, + -89.2416666666, 43.8070001177, 103338.0, + -90.6416666666, 44.0000739286, 103341.0, + -89.5444444444, 45.7042237702, 103343.0, + -92.2277777777, 44.6361488719, 103346.0, + -92.2277777777, 44.6361488719, 103347.0, + -89.5000000000, 44.4168239752, 103349.0, + -90.4305555555, 43.3223129275, 103352.0, + -91.1166666666, 45.9000991313, 103356.0, + -90.4833333333, 45.1778220858, 103360.0, + -90.7833333333, 43.5750329397, 103362.0, + -89.4888888888, 46.0778440905, 103363.0, + -88.5416666667, 42.6694620969, 103364.0, + -91.7833333333, 45.9612198333, 103365.0, + -89.2416666666, 44.1139440458, 103369.0, + -90.0000000000, 44.3625954694, 103371.0, + 0.0, 0,0, 0,0 +}; + +static double apszWISCRS_TM_meter[] = { +// Central_Meridian, Latitude_Of_Origin, SR code + -90.0000000000, 43.3666666666, 103300.0, + -90.6222222222, 45.7061111111, 103301.0, + -91.8500000000, 45.1333333333, 103302.0, + -88.0000000000, 43.0000000000, 103304.0, + -91.7972222222, 43.4813888888, 103305.0, + -88.5000000000, 42.7194444444, 103307.0, + -90.7083333333, 43.6000000000, 103309.0, + -88.7750000000, 41.4722222222, 103313.0, + -87.2722222222, 44.4000000000, 103314.0, + -91.9166666666, 45.8833333333, 103315.0, + -91.8944444444, 44.4083333333, 103316.0, + -88.1416666666, 45.4388888888, 103318.0, + -88.5000000000, 42.7194444444, 103319.0, + -88.6333333333, 44.0055555556, 103320.0, + -90.8000000000, 41.4111111111, 103321.0, + -90.1611111111, 42.5388888888, 103324.0, + -90.2555555555, 45.4333333333, 103325.0, + -90.8442965194, 44.2533351277, 103326.0, + -88.7750000000, 41.4722222222, 103327.0, + -90.0000000000, 43.3666666666, 103328.0, + -87.8944444444, 42.2166666666, 103329.0, + -87.5500000000, 43.2666666666, 103330.0, + -91.3166666666, 43.4511111111, 103331.0, + -89.7333333333, 44.8444444444, 103334.0, + -87.5500000000, 43.2666666666, 103335.0, + -87.7111111111, 44.6916666666, 103337.0, + -88.4166666666, 44.7166666666, 103339.0, + -87.8944444444, 42.2166666666, 103340.0, + -87.9083333333, 44.3972222222, 103342.0, + -88.5000000000, 42.7194444444, 103344.0, + -87.8944444444, 42.2166666666, 103345.0, + -92.6333333333, 44.6611111111, 103348.0, + -90.4888888889, 44.5555555556, 103350.0, + -87.8944444444, 42.2166666666, 103351.0, + -89.0722222222, 41.9444444444, 103353.0, + -91.0666666666, 43.9194444444, 103354.0, + -89.9000000000, 42.8194444444, 103355.0, + -88.6055555556, 44.0361111111, 103357.0, + -87.5500000000, 43.2666666666, 103358.0, + -92.6333333333, 44.0361111111, 103359.0, + -91.3666666666, 43.1611111111, 103361.0, + -88.0638888888, 42.9180555555, 103366.0, + -88.2250000000, 42.5694444444, 103367.0, + -88.8166666666, 43.4202777777, 103368.0, + -88.5000000000, 42.7194444444, 103370.0, + 0.0, 0,0, 0,0 +}; diff --git a/bazaar/plugin/gdal/ogr/ogr_srs_ozi.cpp b/bazaar/plugin/gdal/ogr/ogr_srs_ozi.cpp new file mode 100644 index 000000000..73e5a3978 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srs_ozi.cpp @@ -0,0 +1,509 @@ +/****************************************************************************** + * $Id: ogr_srs_ozi.cpp 28972 2015-04-22 10:39:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: OGRSpatialReference translation from OziExplorer + * georeferencing information. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2009, Andrey Kiselev + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "cpl_conv.h" +#include "cpl_csv.h" + +CPL_CVSID("$Id: ogr_srs_ozi.cpp 28972 2015-04-22 10:39:11Z rouault $"); + +/************************************************************************/ +/* OSRImportFromOzi() */ +/************************************************************************/ + +/** + * Import coordinate system from OziExplorer projection definition. + * + * This function will import projection definition in style, used by + * OziExplorer software. + * + * Note: another version of this function with a different signature existed + * in GDAL 1.X. + * + * @param hSRS spatial reference object. + * @param papszLines Map file lines. This is an array of strings containing + * the whole OziExplorer .MAP file. The array is terminated by a NULL pointer. + * + * @return OGRERR_NONE on success or an error code in case of failure. + * + * @since OGR 2.0 + */ + +OGRErr OSRImportFromOzi( OGRSpatialReferenceH hSRS, + const char * const* papszLines ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromOzi", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromOzi( papszLines ); +} + +/************************************************************************/ +/* importFromOzi() */ +/************************************************************************/ + +/** + * Import coordinate system from OziExplorer projection definition. + * + * This method will import projection definition in style, used by + * OziExplorer software. + * + * @param papszLines Map file lines. This is an array of strings containing + * the whole OziExplorer .MAP file. The array is terminated by a NULL pointer. + * + * @return OGRERR_NONE on success or an error code in case of failure. + * + * @since OGR 1.10 + */ + +OGRErr OGRSpatialReference::importFromOzi( const char * const* papszLines ) +{ + int iLine; + const char *pszDatum, *pszProj = NULL, *pszProjParms = NULL; + + Clear(); + + int nLines = CSLCount((char**)papszLines); + if( nLines < 5 ) + return OGRERR_NOT_ENOUGH_DATA; + + pszDatum = papszLines[4]; + + for ( iLine = 5; iLine < nLines; iLine++ ) + { + if ( EQUALN(papszLines[iLine], "Map Projection", 14) ) + { + pszProj = papszLines[iLine]; + } + else if ( EQUALN(papszLines[iLine], "Projection Setup", 16) ) + { + pszProjParms = papszLines[iLine]; + } + } + + if ( ! ( pszDatum && pszProj && pszProjParms ) ) + return OGRERR_NOT_ENOUGH_DATA; + +/* -------------------------------------------------------------------- */ +/* Operate on the basis of the projection name. */ +/* -------------------------------------------------------------------- */ + char **papszProj = CSLTokenizeStringComplex( pszProj, ",", TRUE, TRUE ); + char **papszProjParms = CSLTokenizeStringComplex( pszProjParms, ",", + TRUE, TRUE ); + char **papszDatum = NULL; + + if (CSLCount(papszProj) < 2) + { + goto not_enough_data; + } + + if ( EQUALN(papszProj[1], "Latitude/Longitude", 18) ) + { + } + + else if ( EQUALN(papszProj[1], "Mercator", 8) ) + { + if (CSLCount(papszProjParms) < 6) goto not_enough_data; + double dfScale = CPLAtof(papszProjParms[3]); + if (papszProjParms[3][0] == 0) dfScale = 1; /* if unset, default to scale = 1 */ + SetMercator( CPLAtof(papszProjParms[1]), CPLAtof(papszProjParms[2]), + dfScale, + CPLAtof(papszProjParms[4]), CPLAtof(papszProjParms[5]) ); + } + + else if ( EQUALN(papszProj[1], "Transverse Mercator", 19) ) + { + if (CSLCount(papszProjParms) < 6) goto not_enough_data; + SetTM( CPLAtof(papszProjParms[1]), CPLAtof(papszProjParms[2]), + CPLAtof(papszProjParms[3]), + CPLAtof(papszProjParms[4]), CPLAtof(papszProjParms[5]) ); + } + + else if ( EQUALN(papszProj[1], "Lambert Conformal Conic", 23) ) + { + if (CSLCount(papszProjParms) < 8) goto not_enough_data; + SetLCC( CPLAtof(papszProjParms[6]), CPLAtof(papszProjParms[7]), + CPLAtof(papszProjParms[1]), CPLAtof(papszProjParms[2]), + CPLAtof(papszProjParms[4]), CPLAtof(papszProjParms[5]) ); + } + + else if ( EQUALN(papszProj[1], "Sinusoidal", 10) ) + { + if (CSLCount(papszProjParms) < 6) goto not_enough_data; + SetSinusoidal( CPLAtof(papszProjParms[2]), + CPLAtof(papszProjParms[4]), CPLAtof(papszProjParms[5]) ); + } + + else if ( EQUALN(papszProj[1], "Albers Equal Area", 17) ) + { + if (CSLCount(papszProjParms) < 8) goto not_enough_data; + SetACEA( CPLAtof(papszProjParms[6]), CPLAtof(papszProjParms[7]), + CPLAtof(papszProjParms[1]), CPLAtof(papszProjParms[2]), + CPLAtof(papszProjParms[4]), CPLAtof(papszProjParms[5]) ); + } + + else if ( EQUALN(papszProj[1], "(UTM) Universal Transverse Mercator", 35) && nLines > 5 ) + { + /* Look for the UTM zone in the calibration point data */ + for ( iLine = 5; iLine < nLines; iLine++ ) + { + if ( EQUALN(papszLines[iLine], "Point", 5) ) + { + char **papszTok = NULL; + papszTok = CSLTokenizeString2( papszLines[iLine], ",", + CSLT_ALLOWEMPTYTOKENS + | CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES ); + if ( CSLCount(papszTok) < 17 + || EQUAL(papszTok[2], "") + || EQUAL(papszTok[13], "") + || EQUAL(papszTok[14], "") + || EQUAL(papszTok[15], "") + || EQUAL(papszTok[16], "") ) + { + CSLDestroy(papszTok); + continue; + } + SetUTM( CPLAtofM(papszTok[13]), EQUAL(papszTok[16], "N") ); + CSLDestroy(papszTok); + break; + } + } + if ( iLine == nLines ) /* Try to guess the UTM zone */ + { + float fMinLongitude = INT_MAX; + float fMaxLongitude = INT_MIN; + float fMinLatitude = INT_MAX; + float fMaxLatitude = INT_MIN; + int bFoundMMPLL = FALSE; + for ( iLine = 5; iLine < nLines; iLine++ ) + { + if ( EQUALN(papszLines[iLine], "MMPLL", 5) ) + { + char **papszTok = NULL; + papszTok = CSLTokenizeString2( papszLines[iLine], ",", + CSLT_ALLOWEMPTYTOKENS + | CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES ); + if ( CSLCount(papszTok) < 4 ) + { + CSLDestroy(papszTok); + continue; + } + float fLongitude = CPLAtofM(papszTok[2]); + float fLatitude = CPLAtofM(papszTok[3]); + CSLDestroy(papszTok); + + bFoundMMPLL = TRUE; + + if ( fMinLongitude > fLongitude ) + fMinLongitude = fLongitude; + if ( fMaxLongitude < fLongitude ) + fMaxLongitude = fLongitude; + if ( fMinLatitude > fLatitude ) + fMinLatitude = fLatitude; + if ( fMaxLatitude < fLatitude ) + fMaxLatitude = fLatitude; + } + } + float fMedianLatitude = ( fMinLatitude + fMaxLatitude ) / 2; + float fMedianLongitude = ( fMinLongitude + fMaxLongitude ) / 2; + if ( bFoundMMPLL && fMaxLatitude <= 90 ) + { + int nUtmZone; + if ( fMedianLatitude >= 56 && fMedianLatitude <= 64 && + fMedianLongitude >= 3 && fMedianLongitude <= 12 ) + nUtmZone = 32; /* Norway exception */ + else if ( fMedianLatitude >= 72 && fMedianLatitude <= 84 && + fMedianLongitude >= 0 && fMedianLongitude <= 42 ) + nUtmZone = (int) ((fMedianLongitude + 3 ) / 12) * 2 + 31; /* Svalbard exception */ + else + nUtmZone = (int) ((fMedianLongitude + 180 ) / 6) + 1; + SetUTM( nUtmZone, fMedianLatitude >= 0 ); + } + else + CPLDebug( "OSR_Ozi", "UTM Zone not found"); + } + } + + else if ( EQUALN(papszProj[1], "(I) France Zone I", 17) ) + { + SetLCC1SP( 49.5, 2.337229167, 0.99987734, 600000, 1200000 ); + } + + else if ( EQUALN(papszProj[1], "(II) France Zone II", 19) ) + { + SetLCC1SP( 46.8, 2.337229167, 0.99987742, 600000, 2200000 ); + } + + else if ( EQUALN(papszProj[1], "(III) France Zone III", 21) ) + { + SetLCC1SP( 44.1, 2.337229167, 0.99987750, 600000, 3200000 ); + } + + else if ( EQUALN(papszProj[1], "(IV) France Zone IV", 19) ) + { + SetLCC1SP( 42.165, 2.337229167, 0.99994471, 234.358, 4185861.369 ); + } + +/* + * Note : The following projections have not been implemented yet + * + */ + +/* + else if ( EQUALN(papszProj[1], "(BNG) British National Grid", 27) ) + { + } + + else if ( EQUALN(papszProj[1], "(IG) Irish Grid", 15) ) + { + } + + else if ( EQUALN(papszProj[1], "(NZG) New Zealand Grid", 22) ) + { + } + + else if ( EQUALN(papszProj[1], "(NZTM2) New Zealand TM 2000", 27) ) + { + } + + else if ( EQUALN(papszProj[1], "(SG) Swedish Grid", 27) ) + { + } + + else if ( EQUALN(papszProj[1], "(SUI) Swiss Grid", 26) ) + { + } + + else if ( EQUALN(papszProj[1], "(A)Lambert Azimuthual Equal Area", 32) ) + { + } + + else if ( EQUALN(papszProj[1], "(EQC) Equidistant Conic", 23) ) + { + } + + else if ( EQUALN(papszProj[1], "Polyconic (American)", 20) ) + { + } + + else if ( EQUALN(papszProj[1], "Van Der Grinten", 15) ) + { + } + + else if ( EQUALN(papszProj[1], "Vertical Near-Sided Perspective", 31) ) + { + } + + else if ( EQUALN(papszProj[1], "(WIV) Wagner IV", 15) ) + { + } + + else if ( EQUALN(papszProj[1], "Bonne", 5) ) + { + } + + else if ( EQUALN(papszProj[1], "(MT0) Montana State Plane Zone 2500", 35) ) + { + } + + else if ( EQUALN(papszProj[1], "ITA1) Italy Grid Zone 1", 23) ) + { + } + + else if ( EQUALN(papszProj[1], "ITA2) Italy Grid Zone 2", 23) ) + { + } + + else if ( EQUALN(papszProj[1], "(VICMAP-TM) Victoria Aust.(pseudo AMG)", 38) ) + { + } + + else if ( EQUALN(papszProj[1], "VICGRID) Victoria Australia", 27) ) + { + } + + else if ( EQUALN(papszProj[1], "(VG94) VICGRID94 Victoria Australia", 35) ) + { + } + + else if ( EQUALN(papszProj[1], "Gnomonic", 8) ) + { + } +*/ + + else + { + CPLDebug( "OSR_Ozi", "Unsupported projection: \"%s\"", papszProj[1] ); + SetLocalCS( CPLString().Printf("\"Ozi\" projection \"%s\"", + papszProj[1]) ); + } + +/* -------------------------------------------------------------------- */ +/* Try to translate the datum/spheroid. */ +/* -------------------------------------------------------------------- */ + papszDatum = CSLTokenizeString2( pszDatum, ",", + CSLT_ALLOWEMPTYTOKENS + | CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES ); + if ( papszDatum == NULL) + goto not_enough_data; + + if ( !IsLocal() ) + { + +/* -------------------------------------------------------------------- */ +/* Verify that we can find the CSV file containing the datums */ +/* -------------------------------------------------------------------- */ + if( CSVScanFileByName( CSVFilename( "ozi_datum.csv" ), + "EPSG_DATUM_CODE", + "4326", CC_Integer ) == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open OZI support file %s.\n" + "Try setting the GDAL_DATA environment variable to point\n" + "to the directory containing OZI csv files.", + CSVFilename( "ozi_datum.csv" ) ); + goto other_error; + } + +/* -------------------------------------------------------------------- */ +/* Search for matching datum */ +/* -------------------------------------------------------------------- */ + const char *pszOziDatum = CSVFilename( "ozi_datum.csv" ); + CPLString osDName = CSVGetField( pszOziDatum, "NAME", papszDatum[0], + CC_ApproxString, "NAME" ); + if( strlen(osDName) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find datum %s in ozi_datum.csv.", + papszDatum[0] ); + goto other_error; + } + + int nDatumCode = atoi( CSVGetField( pszOziDatum, "NAME", papszDatum[0], + CC_ApproxString, "EPSG_DATUM_CODE" ) ); + + if ( nDatumCode > 0 ) // There is a matching EPSG code + { + OGRSpatialReference oGCS; + oGCS.importFromEPSG( nDatumCode ); + CopyGeogCSFrom( &oGCS ); + } + else // We use the parameters from the CSV files + { + CPLString osEllipseCode = CSVGetField( pszOziDatum, "NAME", papszDatum[0], + CC_ApproxString, "ELLIPSOID_CODE" ); + double dfDeltaX = CPLAtof(CSVGetField( pszOziDatum, "NAME", papszDatum[0], + CC_ApproxString, "DELTAX" ) ); + double dfDeltaY = CPLAtof(CSVGetField( pszOziDatum, "NAME", papszDatum[0], + CC_ApproxString, "DELTAY" ) ); + double dfDeltaZ = CPLAtof(CSVGetField( pszOziDatum, "NAME", papszDatum[0], + CC_ApproxString, "DELTAZ" ) ); + + + /* -------------------------------------------------------------------- */ + /* Verify that we can find the CSV file containing the ellipsoids */ + /* -------------------------------------------------------------------- */ + if( CSVScanFileByName( CSVFilename( "ozi_ellips.csv" ), + "ELLIPSOID_CODE", + "20", CC_Integer ) == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open OZI support file %s.\n" + "Try setting the GDAL_DATA environment variable to point\n" + "to the directory containing OZI csv files.", + CSVFilename( "ozi_ellips.csv" ) ); + goto other_error; + } + + /* -------------------------------------------------------------------- */ + /* Lookup the ellipse code. */ + /* -------------------------------------------------------------------- */ + const char *pszOziEllipse = CSVFilename( "ozi_ellips.csv" ); + + CPLString osEName = CSVGetField( pszOziEllipse, "ELLIPSOID_CODE", osEllipseCode, + CC_ApproxString, "NAME" ); + if( strlen(osEName) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find ellipsoid %s in ozi_ellips.csv.", + osEllipseCode.c_str() ); + goto other_error; + } + + double dfA = CPLAtof(CSVGetField( pszOziEllipse, "ELLIPSOID_CODE", osEllipseCode, + CC_ApproxString, "A" )); + double dfInvF = CPLAtof(CSVGetField( pszOziEllipse, "ELLIPSOID_CODE", osEllipseCode, + CC_ApproxString, "INVF" )); + + /* -------------------------------------------------------------------- */ + /* Create geographic coordinate system. */ + /* -------------------------------------------------------------------- */ + + SetGeogCS( osDName, osDName, osEName, dfA, dfInvF ); + SetTOWGS84( dfDeltaX, dfDeltaY, dfDeltaZ ); + + } + } + +/* -------------------------------------------------------------------- */ +/* Grid units translation */ +/* -------------------------------------------------------------------- */ + if( IsLocal() || IsProjected() ) + SetLinearUnits( SRS_UL_METER, 1.0 ); + + FixupOrdering(); + + CSLDestroy(papszProj); + CSLDestroy(papszProjParms); + CSLDestroy(papszDatum); + + return OGRERR_NONE; + +not_enough_data: + + CSLDestroy(papszProj); + CSLDestroy(papszProjParms); + CSLDestroy(papszDatum); + + return OGRERR_NOT_ENOUGH_DATA; + +other_error: + + CSLDestroy(papszProj); + CSLDestroy(papszProjParms); + CSLDestroy(papszDatum); + + return OGRERR_FAILURE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogr_srs_panorama.cpp b/bazaar/plugin/gdal/ogr/ogr_srs_panorama.cpp new file mode 100644 index 000000000..5acef79dc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srs_panorama.cpp @@ -0,0 +1,838 @@ +/****************************************************************************** + * $Id: ogr_srs_panorama.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: OGRSpatialReference translation to/from "Panorama" GIS + * georeferencing information (also know as GIS "Integration"). + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2005, Andrey Kiselev + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "ogr_p.h" +#include "cpl_conv.h" +#include "cpl_csv.h" + +CPL_CVSID("$Id: ogr_srs_panorama.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +#define TO_DEGREES 57.2957795130823208766 +#define TO_RADIANS 0.017453292519943295769 + +// XXX: this macro computes zone number from the central meridian parameter. +// Note, that "Panorama" parameters are set in radians. +// In degrees it means formulae: +// +// zone = (central_meridian + 3) / 6 +// +#define TO_ZONE(x) (((x) + 0.05235987755982989) / 0.1047197551196597 + 0.5) + +/************************************************************************/ +/* "Panorama" projection codes. */ +/************************************************************************/ + +#define PAN_PROJ_NONE -1L +#define PAN_PROJ_TM 1L // Gauss-Kruger (Transverse Mercator) +#define PAN_PROJ_LCC 2L // Lambert Conformal Conic 2SP +#define PAN_PROJ_STEREO 5L // Stereographic +#define PAN_PROJ_AE 6L // Azimuthal Equidistant (Postel) +#define PAN_PROJ_MERCAT 8L // Mercator +#define PAN_PROJ_POLYC 10L // Polyconic +#define PAN_PROJ_PS 13L // Polar Stereographic +#define PAN_PROJ_GNOMON 15L // Gnomonic +#define PAN_PROJ_UTM 17L // Universal Transverse Mercator (UTM) +#define PAN_PROJ_WAG1 18L // Wagner I (Kavraisky VI) +#define PAN_PROJ_MOLL 19L // Mollweide +#define PAN_PROJ_EC 20L // Equidistant Conic +#define PAN_PROJ_LAEA 24L // Lambert Azimuthal Equal Area +#define PAN_PROJ_EQC 27L // Equirectangular +#define PAN_PROJ_CEA 28L // Cylindrical Equal Area (Lambert) +#define PAN_PROJ_IMWP 29L // International Map of the World Polyconic +#define PAN_PROJ_MILLER 34L // Miller +/************************************************************************/ +/* "Panorama" datum codes. */ +/************************************************************************/ + +#define PAN_DATUM_NONE -1L +#define PAN_DATUM_PULKOVO42 1L // Pulkovo 1942 +#define PAN_DATUM_WGS84 2L // WGS84 + +/************************************************************************/ +/* "Panorama" ellipsod codes. */ +/************************************************************************/ + +#define PAN_ELLIPSOID_NONE -1L +#define PAN_ELLIPSOID_KRASSOVSKY 1L // Krassovsky, 1940 +#define PAN_ELLIPSOID_WGS72 2L // WGS, 1972 +#define PAN_ELLIPSOID_INT1924 3L // International, 1924 (Hayford, 1909) +#define PAN_ELLIPSOID_CLARCKE1880 4L // Clarke, 1880 +#define PAN_ELLIPSOID_CLARCKE1866 5L // Clarke, 1866 (NAD1927) +#define PAN_ELLIPSOID_EVEREST1830 6L // Everest, 1830 +#define PAN_ELLIPSOID_BESSEL1841 7L // Bessel, 1841 +#define PAN_ELLIPSOID_AIRY1830 8L // Airy, 1830 +#define PAN_ELLIPSOID_WGS84 9L // WGS, 1984 (GPS) + +/************************************************************************/ +/* Correspondence between "Panorama" and EPSG datum codes. */ +/************************************************************************/ + +static const long aoDatums[] = +{ + 0, + 4284, // Pulkovo, 1942 + 4326, // WGS, 1984, + 4277, // OSGB 1936 (British National Grid) + 0, + 0, + 0, + 0, + 0, + 4200 // Pulkovo, 1995 +}; + +#define NUMBER_OF_DATUMS (long)(sizeof(aoDatums)/sizeof(aoDatums[0])) + +/************************************************************************/ +/* Correspondence between "Panorama" and EPSG ellipsoid codes. */ +/************************************************************************/ + +static const long aoEllips[] = +{ + 0, + 7024, // Krassovsky, 1940 + 7043, // WGS, 1972 + 7022, // International, 1924 (Hayford, 1909) + 7034, // Clarke, 1880 + 7008, // Clarke, 1866 (NAD1927) + 7015, // Everest, 1830 + 7004, // Bessel, 1841 + 7001, // Airy, 1830 + 7030, // WGS, 1984 (GPS) + 0, // FIXME: PZ90.02 + 7019, // GRS, 1980 (NAD1983) + 7022, // International, 1924 (Hayford, 1909) XXX? + 7036, // South American, 1969 + 7021, // Indonesian, 1974 + 7020, // Helmert 1906 + 0, // FIXME: Fisher 1960 + 0, // FIXME: Fisher 1968 + 0, // FIXME: Haff 1960 + 7042, // Everest, 1830 + 7003 // Australian National, 1965 +}; + +#define NUMBER_OF_ELLIPSOIDS (sizeof(aoEllips)/sizeof(aoEllips[0])) + +/************************************************************************/ +/* OSRImportFromPanorama() */ +/************************************************************************/ + +OGRErr OSRImportFromPanorama( OGRSpatialReferenceH hSRS, + long iProjSys, long iDatum, long iEllips, + double *padfPrjParams ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromPanorama", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromPanorama( iProjSys, + iDatum,iEllips, + padfPrjParams ); +} + +/************************************************************************/ +/* importFromPanorama() */ +/************************************************************************/ + +/** + * Import coordinate system from "Panorama" GIS projection definition. + * + * This method will import projection definition in style, used by + * "Panorama" GIS. + * + * This function is the equivalent of the C function OSRImportFromPanorama(). + * + * @param iProjSys Input projection system code, used in GIS "Panorama". + * + *

    Supported Projections

    + *
    + *      1:  Gauss-Kruger (Transverse Mercator)
    + *      2:  Lambert Conformal Conic 2SP
    + *      5:  Stereographic
    + *      6:  Azimuthal Equidistant (Postel)
    + *      8:  Mercator
    + *      10: Polyconic
    + *      13: Polar Stereographic
    + *      15: Gnomonic
    + *      17: Universal Transverse Mercator (UTM)
    + *      18: Wagner I (Kavraisky VI)
    + *      19: Mollweide
    + *      20: Equidistant Conic
    + *      24: Lambert Azimuthal Equal Area
    + *      27: Equirectangular
    + *      28: Cylindrical Equal Area (Lambert)
    + *      29: International Map of the World Polyconic
    + * 
    + * + * @param iDatum Input coordinate system. + * + *

    Supported Datums

    + *
    + *       1: Pulkovo, 1942
    + *       2: WGS, 1984
    + *       3: OSGB 1936 (British National Grid)
    + *       9: Pulkovo, 1995
    + * 
    + * + * @param iEllips Input spheroid. + * + *

    Supported Spheroids

    + *
    + *       1: Krassovsky, 1940
    + *       2: WGS, 1972
    + *       3: International, 1924 (Hayford, 1909)
    + *       4: Clarke, 1880
    + *       5: Clarke, 1866 (NAD1927)
    + *       6: Everest, 1830
    + *       7: Bessel, 1841
    + *       8: Airy, 1830
    + *       9: WGS, 1984 (GPS)
    + * 
    + * + * @param padfPrjParams Array of 8 coordinate system parameters: + * + *
    + *      [0]  Latitude of the first standard parallel (radians)
    + *      [1]  Latitude of the second standard parallel (radians)
    + *      [2]  Latitude of center of projection (radians)
    + *      [3]  Longitude of center of projection (radians)
    + *      [4]  Scaling factor
    + *      [5]  False Easting
    + *      [6]  False Northing
    + *      [7]  Zone number
    + * 
    + * + * Particular projection uses different parameters, unused ones may be set to + * zero. If NULL supplied instead of array pointer default values will be used + * (i.e., zeroes). + * + * @return OGRERR_NONE on success or an error code in case of failure. + */ + +OGRErr OGRSpatialReference::importFromPanorama( long iProjSys, long iDatum, + long iEllips, + double *padfPrjParams ) + +{ + Clear(); + +/* -------------------------------------------------------------------- */ +/* Use safe defaults if projection parameters are not supplied. */ +/* -------------------------------------------------------------------- */ + int bProjAllocated = FALSE; + + if( padfPrjParams == NULL ) + { + int i; + + padfPrjParams = (double *)CPLMalloc( 8 * sizeof(double) ); + if ( !padfPrjParams ) + return OGRERR_NOT_ENOUGH_MEMORY; + for ( i = 0; i < 7; i++ ) + padfPrjParams[i] = 0.0; + bProjAllocated = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Operate on the basis of the projection code. */ +/* -------------------------------------------------------------------- */ + switch ( iProjSys ) + { + case PAN_PROJ_NONE: + break; + + case PAN_PROJ_UTM: + { + long nZone; + + if ( padfPrjParams[7] == 0.0 ) + nZone = (long)TO_ZONE(padfPrjParams[3]); + else + nZone = (long) padfPrjParams[7]; + + // XXX: no way to determine south hemisphere. Always assume + // nothern hemisphere. + SetUTM( nZone, TRUE ); + } + break; + + case PAN_PROJ_WAG1: + SetWagner( 1, 0.0, + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_MERCAT: + SetMercator( TO_DEGREES * padfPrjParams[0], + TO_DEGREES * padfPrjParams[3], + padfPrjParams[4], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_PS: + SetPS( TO_DEGREES * padfPrjParams[2], + TO_DEGREES * padfPrjParams[3], + padfPrjParams[4], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_POLYC: + SetPolyconic( TO_DEGREES * padfPrjParams[2], + TO_DEGREES * padfPrjParams[3], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_EC: + SetEC( TO_DEGREES * padfPrjParams[0], + TO_DEGREES * padfPrjParams[1], + TO_DEGREES * padfPrjParams[2], + TO_DEGREES * padfPrjParams[3], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_LCC: + SetLCC( TO_DEGREES * padfPrjParams[0], + TO_DEGREES * padfPrjParams[1], + TO_DEGREES * padfPrjParams[2], + TO_DEGREES * padfPrjParams[3], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_TM: + { + // XXX: we need zone number to compute false easting + // parameter, because usually it is not contained in the + // "Panorama" projection definition. + // FIXME: what to do with negative values? + long nZone; + double dfCenterLong; + + if ( padfPrjParams[7] == 0.0 ) + { + nZone = (long)TO_ZONE(padfPrjParams[3]); + dfCenterLong = TO_DEGREES * padfPrjParams[3]; + } + else + { + nZone = (long) padfPrjParams[7]; + dfCenterLong = 6 * nZone - 3; + } + + padfPrjParams[5] = nZone * 1000000.0 + 500000.0; + padfPrjParams[4] = 1.0; + SetTM( TO_DEGREES * padfPrjParams[2], + dfCenterLong, + padfPrjParams[4], + padfPrjParams[5], padfPrjParams[6] ); + } + break; + + case PAN_PROJ_STEREO: + SetStereographic( TO_DEGREES * padfPrjParams[2], + TO_DEGREES * padfPrjParams[3], + padfPrjParams[4], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_AE: + SetAE( TO_DEGREES * padfPrjParams[0], + TO_DEGREES * padfPrjParams[3], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_GNOMON: + SetGnomonic( TO_DEGREES * padfPrjParams[2], + TO_DEGREES * padfPrjParams[3], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_MOLL: + SetMollweide( TO_DEGREES * padfPrjParams[3], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_LAEA: + SetLAEA( TO_DEGREES * padfPrjParams[0], + TO_DEGREES * padfPrjParams[3], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_EQC: + SetEquirectangular( TO_DEGREES * padfPrjParams[0], + TO_DEGREES * padfPrjParams[3], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_CEA: + SetCEA( TO_DEGREES * padfPrjParams[0], + TO_DEGREES * padfPrjParams[3], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_IMWP: + SetIWMPolyconic( TO_DEGREES * padfPrjParams[0], + TO_DEGREES * padfPrjParams[1], + TO_DEGREES * padfPrjParams[3], + padfPrjParams[5], padfPrjParams[6] ); + break; + + case PAN_PROJ_MILLER: + SetMC(TO_DEGREES * padfPrjParams[5], + TO_DEGREES * padfPrjParams[4], + padfPrjParams[6], padfPrjParams[7]); + break; + + default: + CPLDebug( "OSR_Panorama", "Unsupported projection: %ld", iProjSys ); + SetLocalCS( CPLString().Printf("\"Panorama\" projection number %ld", + iProjSys) ); + break; + + } + +/* -------------------------------------------------------------------- */ +/* Try to translate the datum/spheroid. */ +/* -------------------------------------------------------------------- */ + + if ( !IsLocal() ) + { + if ( iDatum > 0 && iDatum < NUMBER_OF_DATUMS && aoDatums[iDatum] ) + { + OGRSpatialReference oGCS; + oGCS.importFromEPSG( aoDatums[iDatum] ); + CopyGeogCSFrom( &oGCS ); + } + + else if ( iEllips > 0 + && iEllips < (long)NUMBER_OF_ELLIPSOIDS + && aoEllips[iEllips] ) + { + char *pszName = NULL; + double dfSemiMajor, dfInvFlattening; + + if ( OSRGetEllipsoidInfo( aoEllips[iEllips], &pszName, + &dfSemiMajor, &dfInvFlattening ) == OGRERR_NONE ) + { + SetGeogCS( CPLString().Printf( + "Unknown datum based upon the %s ellipsoid", + pszName ), + CPLString().Printf( + "Not specified (based on %s spheroid)", pszName ), + pszName, dfSemiMajor, dfInvFlattening, + NULL, 0.0, NULL, 0.0 ); + SetAuthority( "SPHEROID", "EPSG", aoEllips[iEllips] ); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to lookup ellipsoid code %ld, likely due to" + " missing GDAL gcs.csv\n" + " file. Falling back to use Pulkovo 42.", iEllips ); + SetWellKnownGeogCS( "EPSG:4284" ); + } + + if ( pszName ) + CPLFree( pszName ); + } + + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Wrong datum code %ld. Supported datums are 1--%ld only.\n" + "Falling back to use Pulkovo 42.", + iDatum, NUMBER_OF_DATUMS - 1 ); + SetWellKnownGeogCS( "EPSG:4284" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Grid units translation */ +/* -------------------------------------------------------------------- */ + if( IsLocal() || IsProjected() ) + SetLinearUnits( SRS_UL_METER, 1.0 ); + + FixupOrdering(); + + if ( bProjAllocated && padfPrjParams ) + CPLFree( padfPrjParams ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRExportToPanorama() */ +/************************************************************************/ + +OGRErr OSRExportToPanorama( OGRSpatialReferenceH hSRS, + long *piProjSys, long *piDatum, long *piEllips, + long *piZone, double *padfPrjParams ) + +{ + VALIDATE_POINTER1( hSRS, "OSRExportToPanorama", CE_Failure ); + VALIDATE_POINTER1( piProjSys, "OSRExportToPanorama", CE_Failure ); + VALIDATE_POINTER1( piDatum, "OSRExportToPanorama", CE_Failure ); + VALIDATE_POINTER1( piEllips, "OSRExportToPanorama", CE_Failure ); + VALIDATE_POINTER1( padfPrjParams, "OSRExportToPanorama", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->exportToPanorama( piProjSys, + piDatum, piEllips, + piZone, + padfPrjParams ); +} + +/************************************************************************/ +/* exportToPanorama() */ +/************************************************************************/ + +/** + * Export coordinate system in "Panorama" GIS projection definition. + * + * This method is the equivalent of the C function OSRExportToPanorama(). + * + * @param piProjSys Pointer to variable, where the projection system code will + * be returned. + * + * @param piDatum Pointer to variable, where the coordinate system code will + * be returned. + * + * @param piEllips Pointer to variable, where the spheroid code will be + * returned. + * + * @param piZone Pointer to variable, where the zone for UTM projection + * system will be returned. + * + * @param padfPrjParams an existing 7 double buffer into which the + * projection parameters will be placed. See importFromPanorama() + * for the list of parameters. + * + * @return OGRERR_NONE on success or an error code on failure. + */ + +OGRErr OGRSpatialReference::exportToPanorama( long *piProjSys, long *piDatum, + long *piEllips, long *piZone, + double *padfPrjParams ) const + +{ + CPLAssert( padfPrjParams ); + + const char *pszProjection = GetAttrValue("PROJECTION"); + +/* -------------------------------------------------------------------- */ +/* Fill all projection parameters with zero. */ +/* -------------------------------------------------------------------- */ + int i; + + *piDatum = 0L; + *piEllips = 0L; + *piZone = 0L; + for ( i = 0; i < 7; i++ ) + padfPrjParams[i] = 0.0; + +/* ==================================================================== */ +/* Handle the projection definition. */ +/* ==================================================================== */ + if( IsLocal() ) + *piProjSys = PAN_PROJ_NONE; + + else if( pszProjection == NULL ) + { +#ifdef DEBUG + CPLDebug( "OSR_Panorama", + "Empty projection definition, considered as Geographic" ); +#endif + *piProjSys = PAN_PROJ_NONE; + } + + else if( EQUAL(pszProjection, SRS_PT_MERCATOR_1SP) ) + { + *piProjSys = PAN_PROJ_MERCAT; + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[0] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + padfPrjParams[4] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC) ) + { + *piProjSys = PAN_PROJ_PS; + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[2] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + padfPrjParams[4] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_POLYCONIC) ) + { + *piProjSys = PAN_PROJ_POLYC; + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[2] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_EQUIDISTANT_CONIC) ) + { + *piProjSys = PAN_PROJ_EC; + padfPrjParams[0] = + TO_RADIANS * GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ); + padfPrjParams[1] = + TO_RADIANS * GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ); + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[2] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ) + { + *piProjSys = PAN_PROJ_LCC; + padfPrjParams[0] = + TO_RADIANS * GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ); + padfPrjParams[1] = + TO_RADIANS * GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ); + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[2] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR) ) + { + int bNorth; + + *piZone = GetUTMZone( &bNorth ); + + if( *piZone != 0 ) + { + *piProjSys = PAN_PROJ_UTM; + if( !bNorth ) + *piZone = - *piZone; + } + else + { + *piProjSys = PAN_PROJ_TM; + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[2] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + padfPrjParams[4] = + GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ); + padfPrjParams[5] = + GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = + GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + } + + else if( EQUAL(pszProjection, SRS_PT_WAGNER_I) ) + { + *piProjSys = PAN_PROJ_WAG1; + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_STEREOGRAPHIC) ) + { + *piProjSys = PAN_PROJ_STEREO; + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[2] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + padfPrjParams[4] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_AZIMUTHAL_EQUIDISTANT) ) + { + *piProjSys = PAN_PROJ_AE; + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ); + padfPrjParams[0] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_GNOMONIC) ) + { + *piProjSys = PAN_PROJ_GNOMON; + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[2] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_MOLLWEIDE) ) + { + *piProjSys = PAN_PROJ_MOLL; + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) + { + *piProjSys = PAN_PROJ_LAEA; + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[0] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR) ) + { + *piProjSys = PAN_PROJ_EQC; + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[0] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_CYLINDRICAL_EQUAL_AREA) ) + { + *piProjSys = PAN_PROJ_CEA; + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[2] = + TO_RADIANS * GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_IMW_POLYCONIC) ) + { + *piProjSys = PAN_PROJ_IMWP; + padfPrjParams[3] = + TO_RADIANS * GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + padfPrjParams[0] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_1ST_POINT, 0.0 ); + padfPrjParams[1] = + TO_RADIANS * GetNormProjParm( SRS_PP_LATITUDE_OF_2ND_POINT, 0.0 ); + padfPrjParams[5] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + padfPrjParams[6] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + // Projection unsupported by "Panorama" GIS + else + { + CPLDebug( "OSR_Panorama", + "Projection \"%s\" unsupported by \"Panorama\" GIS. " + "Geographic system will be used.", pszProjection ); + *piProjSys = PAN_PROJ_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Translate the datum. */ +/* -------------------------------------------------------------------- */ + const char *pszDatum = GetAttrValue( "DATUM" ); + + if ( pszDatum == NULL ) + { + *piDatum = PAN_DATUM_NONE; + *piEllips = PAN_ELLIPSOID_NONE; + } + else if ( EQUAL( pszDatum, "Pulkovo_1942" ) ) + { + *piDatum = PAN_DATUM_PULKOVO42; + *piEllips = PAN_ELLIPSOID_KRASSOVSKY; + } + else if( EQUAL( pszDatum, SRS_DN_WGS84 ) ) + { + *piDatum = PAN_DATUM_WGS84; + *piEllips = PAN_ELLIPSOID_WGS84; + } + + // If not found well known datum, translate ellipsoid + else + { + double dfSemiMajor = GetSemiMajor(); + double dfInvFlattening = GetInvFlattening(); + size_t i; + +#ifdef DEBUG + CPLDebug( "OSR_Panorama", + "Datum \"%s\" unsupported by \"Panorama\" GIS. " + "Trying to translate an ellipsoid definition.", pszDatum ); +#endif + + for ( i = 0; i < NUMBER_OF_ELLIPSOIDS; i++ ) + { + if ( aoEllips[i] ) + { + double dfSM = 0.0; + double dfIF = 1.0; + + if ( OSRGetEllipsoidInfo( aoEllips[i], NULL, + &dfSM, &dfIF ) == OGRERR_NONE + && CPLIsEqual(dfSemiMajor, dfSM) + && CPLIsEqual(dfInvFlattening, dfIF) ) + { + *piEllips = i; + break; + } + } + } + + if ( i == NUMBER_OF_ELLIPSOIDS ) // Didn't found matches. + { +#ifdef DEBUG + CPLDebug( "OSR_Panorama", + "Ellipsoid \"%s\" unsupported by \"Panorama\" GIS.", + pszDatum ); +#endif + *piDatum = PAN_DATUM_NONE; + *piEllips = PAN_ELLIPSOID_NONE; + } + } + + return OGRERR_NONE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogr_srs_pci.cpp b/bazaar/plugin/gdal/ogr/ogr_srs_pci.cpp new file mode 100644 index 000000000..69dcd49ad --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srs_pci.cpp @@ -0,0 +1,1324 @@ +/****************************************************************************** + * $Id: ogr_srs_pci.cpp 28565 2015-02-27 10:26:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: OGRSpatialReference translation to/from PCI georeferencing + * information. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2003, Andrey Kiselev + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "ogr_p.h" +#include "cpl_conv.h" +#include "cpl_csv.h" + +CPL_CVSID("$Id: ogr_srs_pci.cpp 28565 2015-02-27 10:26:21Z rouault $"); + +typedef struct +{ + const char *pszPCIDatum; + int nEPSGCode; +} PCIDatums; + +static const PCIDatums asDatums[] = +{ + { "D-01", 4267 }, // NAD27 (USA, NADCON) + { "D-03", 4267 }, // NAD27 (Canada, NTv1) + { "D-02", 4269 }, // NAD83 (USA, NADCON) + { "D-04", 4269 }, // NAD83 (Canada, NTv1) + { "D000", 4326 }, // WGS 1984 + { "D001", 4322 }, // WGS 1972 + { "D008", 4296 }, // Sudan + { "D013", 4601 }, // Antigua Island Astro 1943 + { "D029", 4202 }, // Australian Geodetic 1966 + { "D030", 4203 }, // Australian Geodetic 1984 + { "D033", 4216 }, // Bermuda 1957 + { "D034", 4165 }, // Bissau + { "D036", 4219 }, // Bukit Rimpah + { "D038", 4221 }, // Campo Inchauspe + { "D040", 4222 }, // Cape + { "D042", 4223 }, // Carthage + { "D044", 4224 }, // Chua Astro + { "D045", 4225 }, // Corrego Alegre + { "D046", 4155 }, // Dabola (Guinea) + { "D066", 4272 }, // Geodetic Datum 1949 (New Zealand) + { "D071", 4255 }, // Herat North (Afghanistan) + { "D077", 4239 }, // Indian 1954 (Thailand, Vietnam) + { "D078", 4240 }, // Indian 1975 (Thailand) + { "D083", 4244 }, // Kandawala (Sri Lanka) + { "D085", 4245 }, // Kertau 1948 (West Malaysia & Singapore) + { "D088", 4250 }, // Leigon (Ghana) + { "D089", 4251 }, // Liberia 1964 (Liberia) + { "D092", 4256 }, // Mahe 1971 (Mahe Island) + { "D093", 4262 }, // Massawa (Ethiopia (Eritrea)) + { "D094", 4261 }, // Merchich (Morocco) + { "D098", 4604 }, // Montserrat Island Astro 1958 (Montserrat (Leeward Islands)) + { "D110", 4267 }, // NAD27 / Alaska + { "D139", 4282 }, // Pointe Noire 1948 (Congo) + { "D140", 4615 }, // Porto Santo 1936 (Porto Santo, Madeira Islands) + { "D151", 4139 }, // Puerto Rico (Puerto Rico, Virgin Islands) + { "D153", 4287 }, // Qornoq (Greenland (South)) + { "D158", 4292 }, // Sapper Hill 1943 (East Falkland Island) + { "D159", 4293 }, // Schwarzeck (Namibia) + { "D160", 4616 }, // Selvagem Grande 1938 (Salvage Islands) + { "D176", 4297 }, // Tananarive Observatory 1925 (Madagascar) + { "D177", 4298 }, // Timbalai 1948 (Brunei, East Malaysia (Sabah, Sarawak)) + { "D187", 4309 }, // Yacare (Uruguay) + { "D188", 4311 }, // Zanderij (Suriname) + { "D401", 4124 }, // RT90 (Sweden) + { "D501", 4312 }, // MGI (Hermannskogel, Austria) + { NULL, 0 } +}; + +static const PCIDatums asEllips[] = +{ + { "E000", 7008 }, // Clarke, 1866 (NAD1927) + { "E001", 7034 }, // Clarke, 1880 + { "E002", 7004 }, // Bessel, 1841 + { "E004", 7022 }, // International, 1924 (Hayford, 1909) + { "E005", 7043 }, // WGS, 1972 + { "E006", 7042 }, // Everest, 1830 + { "E008", 7019 }, // GRS, 1980 (NAD1983) + { "E009", 7001 }, // Airy, 1830 + { "E010", 7018 }, // Modified Everest + { "E011", 7002 }, // Modified Airy + { "E012", 7030 }, // WGS, 1984 (GPS) + { "E014", 7003 }, // Australian National, 1965 + { "E015", 7024 }, // Krassovsky, 1940 + { "E016", 7053 }, // Hough + { "E019", 7052 }, // normal sphere + { "E333", 7046 }, // Bessel 1841 (Japan By Law) + { "E900", 7006 }, // Bessel, 1841 (Namibia) + { "E901", 7044 }, // Everest, 1956 + { "E902", 7056 }, // Everest, 1969 + { "E903", 7016 }, // Everest (Sabah & Sarawak) + { "E904", 7020 }, // Helmert, 1906 + { "E907", 7036 }, // South American, 1969 + { "E910", 7041 }, // ATS77 + { NULL, 0 } +}; + +/************************************************************************/ +/* OSRImportFromPCI() */ +/************************************************************************/ + +/** + * \brief Import coordinate system from PCI projection definition. + * + * This function is the same as OGRSpatialReference::importFromPCI(). + */ + +OGRErr OSRImportFromPCI( OGRSpatialReferenceH hSRS, const char *pszProj, + const char *pszUnits, double *padfPrjParams ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromPCI", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromPCI( pszProj, + pszUnits, + padfPrjParams ); +} + +/************************************************************************/ +/* importFromPCI() */ +/************************************************************************/ + +/** + * \brief Import coordinate system from PCI projection definition. + * + * PCI software uses 16-character string to specify coordinate system + * and datum/ellipsoid. You should supply at least this string to the + * importFromPCI() function. + * + * This function is the equivalent of the C function OSRImportFromPCI(). + * + * @param pszProj NULL terminated string containing the definition. Looks + * like "pppppppppppp Ennn" or "pppppppppppp Dnnn", where "pppppppppppp" is + * a projection code, "Ennn" is an ellipsoid code, "Dnnn" --- a datum code. + * + * @param pszUnits Grid units code ("DEGREE" or "METRE"). If NULL "METRE" will + * be used. + * + * @param padfPrjParams Array of 17 coordinate system parameters: + * + * [0] Spheroid semi major axis + * [1] Spheroid semi minor axis + * [2] Reference Longitude + * [3] Reference Latitude + * [4] First Standard Parallel + * [5] Second Standard Parallel + * [6] False Easting + * [7] False Northing + * [8] Scale Factor + * [9] Height above sphere surface + * [10] Longitude of 1st point on center line + * [11] Latitude of 1st point on center line + * [12] Longitude of 2nd point on center line + * [13] Latitude of 2nd point on center line + * [14] Azimuth east of north for center line + * [15] Landsat satellite number + * [16] Landsat path number + * + * Particular projection uses different parameters, unused ones may be set to + * zero. If NULL suppliet instead of array pointer default values will be + * used (i.e., zeroes). + * + * @return OGRERR_NONE on success or an error code in case of failure. + */ + +OGRErr OGRSpatialReference::importFromPCI( const char *pszProj, + const char *pszUnits, + double *padfPrjParams ) + +{ + Clear(); + + if( pszProj == NULL || CPLStrnlen(pszProj, 16) < 16 ) + return OGRERR_CORRUPT_DATA; + + CPLDebug( "OSR_PCI", "Trying to import projection \"%s\"", pszProj ); + +/* -------------------------------------------------------------------- */ +/* Use safe defaults if projection parameters are not supplied. */ +/* -------------------------------------------------------------------- */ + int bProjAllocated = FALSE; + + if( padfPrjParams == NULL ) + { + int i; + + padfPrjParams = (double *)CPLMalloc( 17 * sizeof(double) ); + if ( !padfPrjParams ) + return OGRERR_NOT_ENOUGH_MEMORY; + for ( i = 0; i < 17; i++ ) + padfPrjParams[i] = 0.0; + bProjAllocated = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Extract and "normalize" the earthmodel to look like E001, */ +/* D-02 or D109. */ +/* -------------------------------------------------------------------- */ + char szEarthModel[5]; + const char *pszEM; + int bIsNAD27 = FALSE; + + strcpy( szEarthModel, "" ); + pszEM = pszProj + strlen(pszProj) - 1; + while( pszEM != pszProj ) + { + if( *pszEM == 'e' || *pszEM == 'E' || *pszEM == 'd' || *pszEM == 'D' ) + { + int nCode = atoi(pszEM+1); + + if( nCode >= -99 && nCode <= 999 ) + sprintf( szEarthModel, "%c%03d", toupper(*pszEM), nCode ); + + break; + } + + pszEM--; + } + + if( EQUAL(pszEM,"E000") + || EQUAL(pszEM,"D-01") + || EQUAL(pszEM,"D-03") + || EQUAL(pszEM,"D-07") + || EQUAL(pszEM,"D-09") + || EQUAL(pszEM,"D-11") + || EQUAL(pszEM,"D-13") + || EQUAL(pszEM,"D-17") ) + bIsNAD27 = TRUE; + +/* -------------------------------------------------------------------- */ +/* Operate on the basis of the projection name. */ +/* -------------------------------------------------------------------- */ + if( EQUALN( pszProj, "LONG/LAT", 8 ) ) + { + } + + else if( EQUALN( pszProj, "METER", 5 ) + || EQUALN( pszProj, "METRE", 5 ) ) + { + SetLocalCS( "METER" ); + SetLinearUnits( "METER", 1.0 ); + } + + else if( EQUALN( pszProj, "FEET", 4 ) + || EQUALN( pszProj, "FOOT", 4 ) ) + { + SetLocalCS( "FEET" ); + SetLinearUnits( "FEET", CPLAtof(SRS_UL_FOOT_CONV) ); + } + + else if( EQUALN( pszProj, "ACEA", 4 ) ) + { + SetACEA( padfPrjParams[4], padfPrjParams[5], + padfPrjParams[3], padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "AE", 2 ) ) + { + SetAE( padfPrjParams[3], padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "CASS ", 5 ) ) + { + SetCS( padfPrjParams[3], padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "EC", 2 ) ) + { + SetEC( padfPrjParams[4], padfPrjParams[5], + padfPrjParams[3], padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "ER", 2 ) ) + { + // PCI and GCTP don't support natural origin lat. + SetEquirectangular2( 0.0, padfPrjParams[2], + padfPrjParams[3], + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "GNO", 3 ) ) + { + SetGnomonic( padfPrjParams[3], padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + // FIXME: GVNP --- General Vertical Near- Side Perspective skipped + + // FIXME: GOOD -- our Goode's is not the interrupted version from pci + + else if( EQUALN( pszProj, "LAEA", 4 ) ) + { + SetLAEA( padfPrjParams[3], padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "LCC ", 4 ) ) + { + SetLCC( padfPrjParams[4], padfPrjParams[5], + padfPrjParams[3], padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "LCC_1SP ", 7 ) ) + { + SetLCC1SP( padfPrjParams[3], padfPrjParams[2], + padfPrjParams[8], + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "MC", 2 ) ) + { + SetMC( padfPrjParams[3], padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "MER", 3 ) ) + { + SetMercator( padfPrjParams[3], padfPrjParams[2], + (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0, + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "OG", 2 ) ) + { + SetOrthographic( padfPrjParams[3], padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "OM ", 3 ) ) + { + if( padfPrjParams[10] == 0.0 + && padfPrjParams[11] == 0.0 + && padfPrjParams[12] == 0.0 + && padfPrjParams[13] == 0.0 ) + { + SetHOM( padfPrjParams[3], padfPrjParams[2], + padfPrjParams[14], + padfPrjParams[14], // use azimuth for grid angle + padfPrjParams[8], + padfPrjParams[6], padfPrjParams[7] ); + } + else + { + SetHOM2PNO( padfPrjParams[3], + padfPrjParams[11], padfPrjParams[10], + padfPrjParams[13], padfPrjParams[12], + padfPrjParams[8], + padfPrjParams[6], padfPrjParams[7] ); + } + } + + else if( EQUALN( pszProj, "PC", 2 ) ) + { + SetPolyconic( padfPrjParams[3], padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "PS", 2 ) ) + { + SetPS( padfPrjParams[3], padfPrjParams[2], + (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0, + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "ROB", 3 ) ) + { + SetRobinson( padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "SGDO", 4 ) ) + { + SetOS( padfPrjParams[3], padfPrjParams[2], + (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0, + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "SG", 2 ) ) + { + SetStereographic( padfPrjParams[3], padfPrjParams[2], + (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0, + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "SIN", 3 ) ) + { + SetSinusoidal( padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + // FIXME: SOM --- Space Oblique Mercator skipped + + else if( EQUALN( pszProj, "SPCS", 4 ) ) + { + int iZone; + + iZone = CPLScanLong( (char *)pszProj + 5, 4 ); + + SetStatePlane( iZone, !bIsNAD27 ); + SetLinearUnitsAndUpdateParameters( SRS_UL_METER, 1.0 ); + } + + else if( EQUALN( pszProj, "SPIF", 4 ) ) + { + int iZone; + + iZone = CPLScanLong( (char *)pszProj + 5, 4 ); + + SetStatePlane( iZone, !bIsNAD27 ); + SetLinearUnitsAndUpdateParameters( SRS_UL_FOOT, + CPLAtof(SRS_UL_FOOT_CONV) ); + } + + else if( EQUALN( pszProj, "SPAF", 4 ) ) + { + int iZone; + + iZone = CPLScanLong( (char *)pszProj + 5, 4 ); + + SetStatePlane( iZone, !bIsNAD27 ); + SetLinearUnitsAndUpdateParameters( SRS_UL_US_FOOT, + CPLAtof(SRS_UL_US_FOOT_CONV) ); + } + + else if( EQUALN( pszProj, "TM", 2 ) ) + { + SetTM( padfPrjParams[3], padfPrjParams[2], + (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0, + padfPrjParams[6], padfPrjParams[7] ); + } + + else if( EQUALN( pszProj, "UTM", 3 ) ) + { + int iZone, bNorth = TRUE; + + iZone = CPLScanLong( (char *)pszProj + 4, 5 );; + if ( iZone < 0 ) + { + iZone = -iZone; + bNorth = FALSE; + } + + // Check for a zone letter. PCI uses, accidentally, MGRS + // type row lettering in its UTM projection + char byZoneID = 0; + + if( strlen(pszProj) > 10 && pszProj[10] != ' ' ) + byZoneID = pszProj[10]; + + // Determine if the MGRS zone falls above or below the equator + if (byZoneID != 0 ) + { + CPLDebug("OSR_PCI", "Found MGRS zone in UTM projection string: %c", + byZoneID); + + if (byZoneID >= 'N' && byZoneID <= 'X') + { + bNorth = TRUE; + } + else if (byZoneID >= 'C' && byZoneID <= 'M') + { + bNorth = FALSE; + } + else + { + // yikes, most likely we got something that was not really + // an MGRS zone code so we ignore it. + } + } + + SetUTM( iZone, bNorth ); + } + + else if( EQUALN( pszProj, "VDG", 3 ) ) + { + SetVDG( padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + + else + { + CPLDebug( "OSR_PCI", "Unsupported projection: %s", pszProj ); + SetLocalCS( pszProj ); + } + +/* ==================================================================== */ +/* Translate the datum/spheroid. */ +/* ==================================================================== */ + +/* -------------------------------------------------------------------- */ +/* We have an earthmodel string, look it up in the datum list. */ +/* -------------------------------------------------------------------- */ + if( strlen(szEarthModel) > 0 + && (poRoot == NULL || IsProjected() || IsGeographic()) ) + { + const PCIDatums *pasDatum = asDatums; + + // Search for matching datum + while ( pasDatum->pszPCIDatum ) + { + if( EQUALN( szEarthModel, pasDatum->pszPCIDatum, 4 ) ) + { + OGRSpatialReference oGCS; + oGCS.importFromEPSG( pasDatum->nEPSGCode ); + CopyGeogCSFrom( &oGCS ); + break; + } + pasDatum++; + } + +/* -------------------------------------------------------------------- */ +/* If we did not find a datum definition in our incode epsg */ +/* lookup table, then try fetching from the pci_datum.txt */ +/* file. */ +/* -------------------------------------------------------------------- */ + char **papszDatumDefn = NULL; + + if( !pasDatum->pszPCIDatum && szEarthModel[0] == 'D' ) + { + const char *pszDatumCSV = CSVFilename( "pci_datum.txt" ); + FILE *fp = NULL; + + if( pszDatumCSV ) + fp = VSIFOpen( pszDatumCSV, "r" ); + + if( fp != NULL ) + { + char **papszLineItems = NULL; + + while( (papszLineItems = CSVReadParseLine( fp )) != NULL ) + { + if( CSLCount(papszLineItems) > 3 + && EQUALN(papszLineItems[0],szEarthModel,4) ) + { + papszDatumDefn = papszLineItems; + strncpy( szEarthModel, papszLineItems[2], 4 ); + break; + } + CSLDestroy( papszLineItems ); + } + + VSIFClose( fp ); + } + } + +/* -------------------------------------------------------------------- */ +/* If not, look in the ellipsoid/EPSG matching list. */ +/* -------------------------------------------------------------------- */ + if ( !pasDatum->pszPCIDatum ) // No matching; search for ellipsoids + { + char *pszName = NULL; + double dfSemiMajor = 0.0; + double dfInvFlattening = 0.0; + int nEPSGCode = 0; + + pasDatum = asEllips; + + while ( pasDatum->pszPCIDatum ) + { + if( EQUALN( szEarthModel, pasDatum->pszPCIDatum, 4 ) ) + { + nEPSGCode = pasDatum->nEPSGCode; + OSRGetEllipsoidInfo( pasDatum->nEPSGCode, &pszName, + &dfSemiMajor, &dfInvFlattening ); + break; + + } + pasDatum++; + } + +/* -------------------------------------------------------------------- */ +/* If we don't find it in that list, do a lookup in the */ +/* pci_ellips.txt file. */ +/* -------------------------------------------------------------------- */ + if( !pasDatum->pszPCIDatum && szEarthModel[0] == 'E' ) + { + const char *pszCSV = CSVFilename( "pci_ellips.txt" ); + FILE *fp = NULL; + + if( pszCSV ) + fp = VSIFOpen( pszCSV, "r" ); + + if( fp != NULL ) + { + char **papszLineItems = NULL; + + while( (papszLineItems = CSVReadParseLine( fp )) != NULL ) + { + if( CSLCount(papszLineItems) > 3 + && EQUALN(papszLineItems[0],szEarthModel,4) ) + { + dfSemiMajor = CPLAtof( papszLineItems[2] ); + double dfSemiMinor = CPLAtof( papszLineItems[3] ); + dfInvFlattening = OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor); + break; + } + CSLDestroy( papszLineItems ); + } + CSLDestroy( papszLineItems ); + + VSIFClose( fp ); + } + } + +/* -------------------------------------------------------------------- */ +/* Custom spheroid? */ +/* -------------------------------------------------------------------- */ + if( dfSemiMajor == 0.0 && EQUALN(szEarthModel,"E999",4) + && padfPrjParams[0] != 0.0 ) + { + dfSemiMajor = padfPrjParams[0]; + double dfSemiMinor = padfPrjParams[1]; + dfInvFlattening = OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor); + } + +/* -------------------------------------------------------------------- */ +/* If nothing else, fall back to WGS84 parameters. */ +/* -------------------------------------------------------------------- */ + if( dfSemiMajor == 0.0 ) + { + dfSemiMajor = SRS_WGS84_SEMIMAJOR; + dfInvFlattening = SRS_WGS84_INVFLATTENING; + } + +/* -------------------------------------------------------------------- */ +/* Now try to put this all together into a GEOGCS definition. */ +/* -------------------------------------------------------------------- */ + CPLString osGCSName, osDatumName, osEllipseName; + + if( pszName ) + osEllipseName = pszName; + else + osEllipseName.Printf( "Unknown - PCI %s", szEarthModel ); + CPLFree( pszName ); + + if( papszDatumDefn ) + osDatumName = papszDatumDefn[1]; + else + osDatumName.Printf( "Unknown - PCI %s", szEarthModel ); + osGCSName = osDatumName; + + SetGeogCS( osGCSName, osDatumName, osEllipseName, + dfSemiMajor, dfInvFlattening ); + + // Do we have an ellipsoid EPSG code? + if( nEPSGCode != 0 ) + SetAuthority( "SPHEROID", "EPSG", nEPSGCode ); + + // Do we have 7 datum shift parameters? + if( CSLCount(papszDatumDefn) >= 15 + && CPLAtof(papszDatumDefn[14]) != 0.0 ) + { + double dfScale = CPLAtof(papszDatumDefn[14]); + + // we want scale in parts per million off 1.0 + // but pci uses a mix of forms. + if( dfScale >= 0.999 && dfScale <= 1.001 ) + dfScale = (dfScale-1.0) * 1000000.0; + + SetTOWGS84( CPLAtof(papszDatumDefn[3]), + CPLAtof(papszDatumDefn[4]), + CPLAtof(papszDatumDefn[5]), + CPLAtof(papszDatumDefn[11]), + CPLAtof(papszDatumDefn[12]), + CPLAtof(papszDatumDefn[13]), + dfScale ); + } + + // Do we have 7 datum shift parameters? + else if( CSLCount(papszDatumDefn) == 11 + && (CPLAtof(papszDatumDefn[3]) != 0.0 + || CPLAtof(papszDatumDefn[4]) != 0.0 + || CPLAtof(papszDatumDefn[5]) != 0.0 ) ) + { + SetTOWGS84( CPLAtof(papszDatumDefn[3]), + CPLAtof(papszDatumDefn[4]), + CPLAtof(papszDatumDefn[5]) ); + } + } + + CSLDestroy(papszDatumDefn); + } + +/* -------------------------------------------------------------------- */ +/* Grid units translation */ +/* -------------------------------------------------------------------- */ + if( (IsLocal() || IsProjected()) && pszUnits ) + { + if( EQUAL( pszUnits, "METRE" ) ) + SetLinearUnits( SRS_UL_METER, 1.0 ); + else if( EQUAL( pszUnits, "DEGREE" ) ) + SetAngularUnits( SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV) ); + else + SetLinearUnits( SRS_UL_METER, 1.0 ); + } + + FixupOrdering(); + + if ( bProjAllocated && padfPrjParams ) + CPLFree( padfPrjParams ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRExportToPCI() */ +/************************************************************************/ +/** + * \brief Export coordinate system in PCI projection definition. + * + * This function is the same as OGRSpatialReference::exportToPCI(). + */ +OGRErr OSRExportToPCI( OGRSpatialReferenceH hSRS, + char **ppszProj, char **ppszUnits, + double **ppadfPrjParams ) + +{ + VALIDATE_POINTER1( hSRS, "OSRExportToPCI", CE_Failure ); + + *ppszProj = NULL; + *ppszUnits = NULL; + *ppadfPrjParams = NULL; + + return ((OGRSpatialReference *) hSRS)->exportToPCI( ppszProj, ppszUnits, + ppadfPrjParams ); +} + +/************************************************************************/ +/* exportToPCI() */ +/************************************************************************/ + +/** + * \brief Export coordinate system in PCI projection definition. + * + * Converts the loaded coordinate reference system into PCI projection + * definition to the extent possible. The strings returned in ppszProj, + * ppszUnits and ppadfPrjParams array should be deallocated by the caller + * with CPLFree() when no longer needed. + * + * LOCAL_CS coordinate systems are not translatable. An empty string + * will be returned along with OGRERR_NONE. + * + * This method is the equivelent of the C function OSRExportToPCI(). + * + * @param ppszProj pointer to which dynamically allocated PCI projection + * definition will be assigned. + * + * @param ppszUnits pointer to which dynamically allocated units definition + * will be assigned. + * + * @param ppadfPrjParams pointer to which dynamically allocated array of + * 17 projection parameters will be assigned. See importFromPCI() for the list + * of parameters. + * + * @return OGRERR_NONE on success or an error code on failure. + */ + +OGRErr OGRSpatialReference::exportToPCI( char **ppszProj, char **ppszUnits, + double **ppadfPrjParams ) const + +{ + const char *pszProjection = GetAttrValue("PROJECTION"); + +/* -------------------------------------------------------------------- */ +/* Fill all projection parameters with zero. */ +/* -------------------------------------------------------------------- */ + int i; + + *ppadfPrjParams = (double *)CPLMalloc( 17 * sizeof(double) ); + for ( i = 0; i < 17; i++ ) + (*ppadfPrjParams)[i] = 0.0; + +/* -------------------------------------------------------------------- */ +/* Get the prime meridian info. */ +/* -------------------------------------------------------------------- */ +#if 0 + const OGR_SRSNode *poPRIMEM = GetAttrNode( "PRIMEM" ); + double dfFromGreenwich = 0.0; + + if( poPRIMEM != NULL && poPRIMEM->GetChildCount() >= 2 + && CPLAtof(poPRIMEM->GetChild(1)->GetValue()) != 0.0 ) + { + dfFromGreenwich = CPLAtof(poPRIMEM->GetChild(1)->GetValue()); + } +#endif + +/* ==================================================================== */ +/* Handle the projection definition. */ +/* ==================================================================== */ + char szProj[17]; + + memset( szProj, 0, sizeof(szProj) ); + + if( IsLocal() ) + { + if( GetLinearUnits() > 0.30479999 && GetLinearUnits() < 0.3048010 ) + CPLPrintStringFill( szProj, "FEET", 17 ); + else + CPLPrintStringFill( szProj, "METER", 17 ); + } + + else if( pszProjection == NULL ) + { + CPLPrintStringFill( szProj, "LONG/LAT", 16 ); + } + + else if( EQUAL(pszProjection, SRS_PT_ALBERS_CONIC_EQUAL_AREA) ) + { + CPLPrintStringFill( szProj, "ACEA", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[4] = + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ); + (*ppadfPrjParams)[5] = + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_AZIMUTHAL_EQUIDISTANT) ) + { + CPLPrintStringFill( szProj, "AE", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_CASSINI_SOLDNER) ) + { + CPLPrintStringFill( szProj, "CASS", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_EQUIDISTANT_CONIC) ) + { + CPLPrintStringFill( szProj, "EC", 16 ); + (*ppadfPrjParams)[2] = + GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ); + (*ppadfPrjParams)[4] = + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ); + (*ppadfPrjParams)[5] = + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR) ) + { + CPLPrintStringFill( szProj, "ER", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_GNOMONIC) ) + { + CPLPrintStringFill( szProj, "GNO", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) + { + CPLPrintStringFill( szProj, "LAEA", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ) + { + CPLPrintStringFill( szProj, "LCC", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[4] = + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ); + (*ppadfPrjParams)[5] = + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) ) + { + CPLPrintStringFill( szProj, "LCC_1SP", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[8] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_MILLER_CYLINDRICAL) ) + { + CPLPrintStringFill( szProj, "MC", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_MERCATOR_1SP) ) + { + CPLPrintStringFill( szProj, "MER", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + (*ppadfPrjParams)[8] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC) ) + { + CPLPrintStringFill( szProj, "OG", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_HOTINE_OBLIQUE_MERCATOR) ) + { + CPLPrintStringFill( szProj, "OM", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER,0.0); + (*ppadfPrjParams)[3] = GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0); + (*ppadfPrjParams)[14] = GetNormProjParm( SRS_PP_AZIMUTH, 0.0); + // note we are ignoring rectified_grid_angle which has no pci analog. + (*ppadfPrjParams)[8] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 0.0); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN) ) + { + CPLPrintStringFill( szProj, "OM", 16 ); + (*ppadfPrjParams)[3] = GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0); + (*ppadfPrjParams)[11] = GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_1,0.0); + (*ppadfPrjParams)[10] = GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_1,0.0); + (*ppadfPrjParams)[13] = GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_2,0.0); + (*ppadfPrjParams)[12] = GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_2,0.0); + (*ppadfPrjParams)[8] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 0.0); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_POLYCONIC) ) + { + CPLPrintStringFill( szProj, "PC", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC) ) + { + CPLPrintStringFill( szProj, "PS", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + (*ppadfPrjParams)[8] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_ROBINSON) ) + { + CPLPrintStringFill( szProj, "ROB", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_OBLIQUE_STEREOGRAPHIC) ) + { + CPLPrintStringFill( szProj, "SGDO", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + (*ppadfPrjParams)[8] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_STEREOGRAPHIC) ) + { + CPLPrintStringFill( szProj, "SG", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + (*ppadfPrjParams)[8] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_SINUSOIDAL) ) + { + CPLPrintStringFill( szProj, "SIN", 16 ); + (*ppadfPrjParams)[2] = + GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR) ) + { + int bNorth; + int nZone = GetUTMZone( &bNorth ); + + if( nZone != 0 ) + { + CPLPrintStringFill( szProj, "UTM", 16 ); + if( bNorth ) + CPLPrintInt32( szProj + 5, nZone, 4 ); + else + CPLPrintInt32( szProj + 5, -nZone, 4 ); + } + else + { + CPLPrintStringFill( szProj, "TM", 16 ); + (*ppadfPrjParams)[2] = + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[3] = + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0); + (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0); + (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0); + } + } + + else if( EQUAL(pszProjection, SRS_PT_VANDERGRINTEN) ) + { + CPLPrintStringFill( szProj, "VDG", 16 ); + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + // Projection unsupported by PCI + else + { + CPLDebug( "OSR_PCI", + "Projection \"%s\" unsupported by PCI. " + "PIXEL value will be used.", pszProjection ); + CPLPrintStringFill( szProj, "PIXEL", 16 ); + } + +/* ==================================================================== */ +/* Translate the earth model. */ +/* ==================================================================== */ + +/* -------------------------------------------------------------------- */ +/* Is this a well known datum? */ +/* -------------------------------------------------------------------- */ + const char *pszDatum = GetAttrValue( "DATUM" ); + char szEarthModel[5]; + + memset( szEarthModel, 0, sizeof(szEarthModel) ); + + if( pszDatum == NULL || strlen(pszDatum) == 0 ) + /* do nothing */; + else if( EQUAL( pszDatum, SRS_DN_NAD27 ) ) + CPLPrintStringFill( szEarthModel, "D-01", 4 ); + + else if( EQUAL( pszDatum, SRS_DN_NAD83 ) ) + CPLPrintStringFill( szEarthModel, "D-02", 4 ); + + else if( EQUAL( pszDatum, SRS_DN_WGS84 ) ) + CPLPrintStringFill( szEarthModel, "D000", 4 ); + +/* -------------------------------------------------------------------- */ +/* If not a very well known datum, try for an EPSG based */ +/* translation. */ +/* -------------------------------------------------------------------- */ + if( szEarthModel[0] == '\0' ) + { + const char *pszAuthority = GetAuthorityName("GEOGCS"); + + if( pszAuthority && EQUAL(pszAuthority,"EPSG") ) + { + int nGCS_EPSG = atoi(GetAuthorityCode("GEOGCS")); + int i; + + for( i = 0; asDatums[i].nEPSGCode != 0; i++ ) + { + if( asDatums[i].nEPSGCode == nGCS_EPSG ) + { + strncpy( szEarthModel, asDatums[i].pszPCIDatum, 5 ); + break; + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* If we haven't found something yet, try translating the */ +/* ellipsoid. */ +/* -------------------------------------------------------------------- */ + if( szEarthModel[0] == '\0' ) + { + double dfSemiMajor = GetSemiMajor(); + double dfInvFlattening = GetInvFlattening(); + + const PCIDatums *pasDatum = asEllips; + + while ( pasDatum->pszPCIDatum ) + { + double dfSM; + double dfIF; + + if ( OSRGetEllipsoidInfo( pasDatum->nEPSGCode, NULL, + &dfSM, &dfIF ) == OGRERR_NONE + && CPLIsEqual( dfSemiMajor, dfSM ) + && CPLIsEqual( dfInvFlattening, dfIF ) ) + { + CPLPrintStringFill( szEarthModel, pasDatum->pszPCIDatum, 4 ); + break; + } + + pasDatum++; + } + + // Try to find in pci_ellips.txt + if( szEarthModel[0] == '\0' ) + { + const char *pszCSV = CSVFilename( "pci_ellips.txt" ); + FILE *fp = NULL; + double dfSemiMinor = OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening); + + if( pszCSV ) + fp = VSIFOpen( pszCSV, "r" ); + + if( fp != NULL ) + { + char **papszLineItems = NULL; + + while( (papszLineItems = CSVReadParseLine( fp )) != NULL ) + { + if( CSLCount(papszLineItems) >= 4 + && CPLIsEqual(dfSemiMajor,CPLAtof(papszLineItems[2])) + && CPLIsEqual(dfSemiMinor,CPLAtof(papszLineItems[3])) ) + { + strncpy( szEarthModel, papszLineItems[0], 5 ); + break; + } + + CSLDestroy( papszLineItems ); + } + + CSLDestroy( papszLineItems ); + VSIFClose( fp ); + } + } + + // custom ellipsoid parameters + if( szEarthModel[0] == '\0' ) + { + CPLPrintStringFill( szEarthModel, "E999", 4 ); + (*ppadfPrjParams)[0] = dfSemiMajor; + (*ppadfPrjParams)[1] = OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening); + } + } + +/* -------------------------------------------------------------------- */ +/* If we have a non-parameteric ellipsoid, scan the */ +/* pci_datum.txt for a match. */ +/* -------------------------------------------------------------------- */ + if( szEarthModel[0] == 'E' + && !EQUAL(szEarthModel,"E999") + && pszDatum != NULL ) + { + const char *pszDatumCSV = CSVFilename( "pci_datum.txt" ); + FILE *fp = NULL; + double adfTOWGS84[7]; + int bHaveTOWGS84; + + bHaveTOWGS84 = (GetTOWGS84( adfTOWGS84, 7 ) == OGRERR_NONE); + + if( pszDatumCSV ) + fp = VSIFOpen( pszDatumCSV, "r" ); + + if( fp != NULL ) + { + char **papszLineItems = NULL; + + while( (papszLineItems = CSVReadParseLine( fp )) != NULL ) + { + // Compare based on datum name. This is mostly for + // PCI round-tripping. We won't usually get exact matches + // from other sources. + if( CSLCount(papszLineItems) > 3 + && EQUAL(papszLineItems[1],pszDatum) + && EQUAL(papszLineItems[2],szEarthModel) ) + { + strncpy( szEarthModel, papszLineItems[0], 5 ); + break; + } + + int bTOWGS84Match = bHaveTOWGS84; + + if( CSLCount(papszLineItems) < 11 ) + bTOWGS84Match = FALSE; + + if( bTOWGS84Match + && (!CPLIsEqual(adfTOWGS84[0],CPLAtof(papszLineItems[3])) + || !CPLIsEqual(adfTOWGS84[1],CPLAtof(papszLineItems[4])) + || !CPLIsEqual(adfTOWGS84[2],CPLAtof(papszLineItems[5])))) + bTOWGS84Match = FALSE; + + if( bTOWGS84Match && CSLCount(papszLineItems) >= 15 + && (!CPLIsEqual(adfTOWGS84[3],CPLAtof(papszLineItems[11])) + || !CPLIsEqual(adfTOWGS84[4],CPLAtof(papszLineItems[12])) + || !CPLIsEqual(adfTOWGS84[5],CPLAtof(papszLineItems[13])))) + bTOWGS84Match = FALSE; + + if( bTOWGS84Match && CSLCount(papszLineItems) >= 15 ) + { + double dfScale = CPLAtof(papszLineItems[14]); + + // convert to parts per million if is a 1 based scaling. + if( dfScale >= 0.999 && dfScale <= 1.001 ) + dfScale = (dfScale-1.0) * 1000000.0; + + if( !CPLIsEqual(adfTOWGS84[6],dfScale) ) + bTOWGS84Match = FALSE; + } + + if( bTOWGS84Match && CSLCount(papszLineItems) < 15 + && (!CPLIsEqual(adfTOWGS84[3],0.0) + || !CPLIsEqual(adfTOWGS84[4],0.0) + || !CPLIsEqual(adfTOWGS84[5],0.0) + || !CPLIsEqual(adfTOWGS84[6],0.0)) ) + bTOWGS84Match = FALSE; + + if( bTOWGS84Match ) + { + strncpy( szEarthModel, papszLineItems[0], 5 ); + break; + } + + CSLDestroy( papszLineItems ); + } + + CSLDestroy( papszLineItems ); + VSIFClose( fp ); + } + } + + CPLPrintStringFill( szProj + 12, szEarthModel, 4 ); + + CPLDebug( "OSR_PCI", "Translated as '%s'", szProj ); + +/* -------------------------------------------------------------------- */ +/* Translate the linear units. */ +/* -------------------------------------------------------------------- */ + const char *pszUnits; + + if( EQUALN( szProj, "LONG/LAT", 8 ) ) + pszUnits = "DEGREE"; + else + pszUnits = "METRE"; + +/* -------------------------------------------------------------------- */ +/* Report results. */ +/* -------------------------------------------------------------------- */ + szProj[16] = '\0'; + *ppszProj = CPLStrdup( szProj ); + + *ppszUnits = CPLStrdup( pszUnits ); + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogr_srs_proj4.cpp b/bazaar/plugin/gdal/ogr/ogr_srs_proj4.cpp new file mode 100644 index 000000000..1a6db83cb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srs_proj4.cpp @@ -0,0 +1,2675 @@ +/****************************************************************************** + * $Id: ogr_srs_proj4.cpp 29114 2015-05-02 20:19:13Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: OGRSpatialReference interface to PROJ.4. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2013, Even Rouault + * Copyright (c) 2014, Kyle Shannon + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "ogr_p.h" +#include "cpl_conv.h" +#include + +extern int EPSGGetWGS84Transform( int nGeogCS, std::vector& asTransform ); + +CPL_CVSID("$Id: ogr_srs_proj4.cpp 29114 2015-05-02 20:19:13Z rouault $"); + +/* -------------------------------------------------------------------- */ +/* The following list comes from osrs/proj/src/pj_ellps.c */ +/* ... please update from time to time. */ +/* -------------------------------------------------------------------- */ +static const char *ogr_pj_ellps[] = { +"MERIT", "a=6378137.0", "rf=298.257", "MERIT 1983", +"SGS85", "a=6378136.0", "rf=298.257", "Soviet Geodetic System 85", +"GRS80", "a=6378137.0", "rf=298.257222101", "GRS 1980(IUGG, 1980)", +"IAU76", "a=6378140.0", "rf=298.257", "IAU 1976", +"airy", "a=6377563.396", "b=6356256.910", "Airy 1830", +"APL4.9", "a=6378137.0.", "rf=298.25", "Appl. Physics. 1965", +"NWL9D", "a=6378145.0.", "rf=298.25", "Naval Weapons Lab., 1965", +"mod_airy", "a=6377340.189", "b=6356034.446", "Modified Airy", +"andrae", "a=6377104.43", "rf=300.0", "Andrae 1876 (Den., Iclnd.)", +"aust_SA", "a=6378160.0", "rf=298.25", "Australian Natl & S. Amer. 1969", +"GRS67", "a=6378160.0", "rf=298.2471674270", "GRS 67(IUGG 1967)", +"bessel", "a=6377397.155", "rf=299.1528128", "Bessel 1841", +"bess_nam", "a=6377483.865", "rf=299.1528128", "Bessel 1841 (Namibia)", +"clrk66", "a=6378206.4", "b=6356583.8", "Clarke 1866", +"clrk80", "a=6378249.145", "rf=293.4663", "Clarke 1880 mod.", +"CPM", "a=6375738.7", "rf=334.29", "Comm. des Poids et Mesures 1799", +"delmbr", "a=6376428.", "rf=311.5", "Delambre 1810 (Belgium)", +"engelis", "a=6378136.05", "rf=298.2566", "Engelis 1985", +"evrst30", "a=6377276.345", "rf=300.8017", "Everest 1830", +"evrst48", "a=6377304.063", "rf=300.8017", "Everest 1948", +"evrst56", "a=6377301.243", "rf=300.8017", "Everest 1956", +"evrst69", "a=6377295.664", "rf=300.8017", "Everest 1969", +"evrstSS", "a=6377298.556", "rf=300.8017", "Everest (Sabah & Sarawak)", +"fschr60", "a=6378166.", "rf=298.3", "Fischer (Mercury Datum) 1960", +"fschr60m", "a=6378155.", "rf=298.3", "Modified Fischer 1960", +"fschr68", "a=6378150.", "rf=298.3", "Fischer 1968", +"helmert", "a=6378200.", "rf=298.3", "Helmert 1906", +"hough", "a=6378270.0", "rf=297.", "Hough", +"intl", "a=6378388.0", "rf=297.", "International 1909 (Hayford)", +"krass", "a=6378245.0", "rf=298.3", "Krassovsky, 1942", +"kaula", "a=6378163.", "rf=298.24", "Kaula 1961", +"lerch", "a=6378139.", "rf=298.257", "Lerch 1979", +"mprts", "a=6397300.", "rf=191.", "Maupertius 1738", +"new_intl", "a=6378157.5", "b=6356772.2", "New International 1967", +"plessis", "a=6376523.", "b=6355863.", "Plessis 1817 (France)", +"SEasia", "a=6378155.0", "b=6356773.3205", "Southeast Asia", +"walbeck", "a=6376896.0", "b=6355834.8467", "Walbeck", +"WGS60", "a=6378165.0", "rf=298.3", "WGS 60", +"WGS66", "a=6378145.0", "rf=298.25", "WGS 66", +"WGS72", "a=6378135.0", "rf=298.26", "WGS 72", +"WGS84", "a=6378137.0", "rf=298.257223563", "WGS 84", +"sphere", "a=6370997.0", "b=6370997.0", "Normal Sphere (r=6370997)", +0, 0, 0, 0, +}; + +typedef struct +{ + const char* pszPJ; + const char* pszOGR; + int nEPSG; + int nGCS; +} OGRProj4Datum; + +/* Derived from proj/src/pj_datum.c */ +/* WGS84, NAD27 and NAD83 are directly hard-coded in the code */ +static const OGRProj4Datum ogr_pj_datums[] = { + { "GGRS87", "Greek_Geodetic_Reference_System_1987", 4121, 6121}, + { "potsdam", "Deutsches_Hauptdreiecksnetz", 4314, 6314}, + { "carthage", "Carthage", 4223, 6223}, + { "hermannskogel", "Militar_Geographische_Institut", 4312, 6312}, + { "ire65", "TM65", 4299, 6299}, + { "nzgd49", "New_Zealand_Geodetic_Datum_1949", 4272, 6272}, + { "OSGB36", "OSGB_1936", 4277, 6277} +}; + +typedef struct +{ + const char* pszProj4PMName; + const char* pszWKTPMName; + const char* pszFromGreenwich; + int nPMCode; +} OGRProj4PM; + +/* Derived from pj_datums.c */ +static const OGRProj4PM ogr_pj_pms [] = { + { "greenwich", "Greenwich", "0dE", 8901 }, + { "lisbon", "Lisbon", "9d07'54.862\"W", 8902 }, + { "paris", "Paris", "2d20'14.025\"E", 8903 }, + { "bogota", "Bogota", "74d04'51.3\"W", 8904 }, + { "madrid", "Madrid", "3d41'16.58\"W", 8905 }, + { "rome", "Rome", "12d27'8.4\"E", 8906 }, + { "bern", "Bern", "7d26'22.5\"E", 8907 }, + { "jakarta", "Jakarta", "106d48'27.79\"E", 8908 }, + { "ferro", "Ferro", "17d40'W", 8909 }, + { "brussels", "Brussels", "4d22'4.71\"E", 8910 }, + { "stockholm", "Stockholm", "18d3'29.8\"E", 8911 }, + { "athens", "Athens", "23d42'58.815\"E", 8912 }, + { "oslo", "Oslo", "10d43'22.5\"E", 8913 } +}; + +static const char* OGRGetProj4Datum(const char* pszDatum, + int nEPSGDatum) +{ + unsigned int i; + for(i=0;i 0 ) + papszTokens = CSLAddString( papszTokens, pszStart ); + + CPLFree( pszFullWrk ); + + return papszTokens; +} + + +/************************************************************************/ +/* OSRImportFromProj4() */ +/************************************************************************/ +/** + * \brief Import PROJ.4 coordinate string. + * + * This function is the same as OGRSpatialReference::importFromProj4(). + */ +OGRErr OSRImportFromProj4( OGRSpatialReferenceH hSRS, const char *pszProj4 ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromProj4", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromProj4( pszProj4 ); +} + +/************************************************************************/ +/* OSR_GDV() */ +/* */ +/* Fetch a particular parameter out of the parameter list, or */ +/* the indicated default if it isn't available. This is a */ +/* helper function for importFromProj4(). */ +/************************************************************************/ + +static double OSR_GDV( char **papszNV, const char * pszField, + double dfDefaultValue ) + +{ + const char * pszValue; + + pszValue = CSLFetchNameValue( papszNV, pszField ); + + // special hack to use k_0 if available. + if( pszValue == NULL && EQUAL(pszField,"k") ) + pszValue = CSLFetchNameValue( papszNV, "k_0" ); + + if( pszValue == NULL ) + return dfDefaultValue; + else + return CPLDMSToDec(pszValue); +} + +/************************************************************************/ +/* importFromProj4() */ +/************************************************************************/ + +/** + * \brief Import PROJ.4 coordinate string. + * + * The OGRSpatialReference is initialized from the passed PROJ.4 style + * coordinate system string. In addition to many +proj formulations which + * have OGC equivelents, it is also possible to import "+init=epsg:n" style + * definitions. These are passed to importFromEPSG(). Other init strings + * (such as the state plane zones) are not currently supported. + * + * Example: + * pszProj4 = "+proj=utm +zone=11 +datum=WGS84" + * + * Some parameters, such as grids, recognised by PROJ.4 may not be well + * understood and translated into the OGRSpatialReference model. It is possible + * to add the +wktext parameter which is a special keyword that OGR recognises + * as meaning "embed the entire PROJ.4 string in the WKT and use it literally + * when converting back to PROJ.4 format". + * + * For example: + * "+proj=nzmg +lat_0=-41 +lon_0=173 +x_0=2510000 +y_0=6023150 +ellps=intl + * +units=m +nadgrids=nzgd2kgrid0005.gsb +wktext" + * + * will be translated as : + * \code + * PROJCS["unnamed", + * GEOGCS["International 1909 (Hayford)", + * DATUM["unknown", + * SPHEROID["intl",6378388,297]], + * PRIMEM["Greenwich",0], + * UNIT["degree",0.0174532925199433]], + * PROJECTION["New_Zealand_Map_Grid"], + * PARAMETER["latitude_of_origin",-41], + * PARAMETER["central_meridian",173], + * PARAMETER["false_easting",2510000], + * PARAMETER["false_northing",6023150], + * UNIT["Meter",1], + * EXTENSION["PROJ4","+proj=nzmg +lat_0=-41 +lon_0=173 +x_0=2510000 + * +y_0=6023150 +ellps=intl +units=m +nadgrids=nzgd2kgrid0005.gsb +wktext"]] + * \endcode + * + * Special processing for 'etmerc' (GDAL >= 1.10 ): if +proj=etmerc is found + * in the passed string, the SRS built will use the WKT representation for a + * standard Transverse Mercator, but will aso include a PROJ4 EXTENSION node to + * preserve the etmerc projection method. + * + * For example: + * "+proj=etmerc +lat_0=0 +lon_0=9 +k=0.9996 +units=m +x_0=500000 +datum=WGS84" + * + * will be translated as : + * \code + * PROJCS["unnamed", + * GEOGCS["WGS 84", + * DATUM["WGS_1984", + * SPHEROID["WGS 84",6378137,298.257223563, + * AUTHORITY["EPSG","7030"]], + * TOWGS84[0,0,0,0,0,0,0], + * AUTHORITY["EPSG","6326"]], + * PRIMEM["Greenwich",0, + * AUTHORITY["EPSG","8901"]], + * UNIT["degree",0.0174532925199433, + * AUTHORITY["EPSG","9108"]], + * AUTHORITY["EPSG","4326"]], + * PROJECTION["Transverse_Mercator"], + * PARAMETER["latitude_of_origin",0], + * PARAMETER["central_meridian",9], + * PARAMETER["scale_factor",0.9996], + * PARAMETER["false_easting",500000], + * PARAMETER["false_northing",0], + * UNIT["Meter",1], + * EXTENSION["PROJ4","+proj=etmerc +lat_0=0 +lon_0=9 +k=0.9996 +units=m +x_0=500000 +datum=WGS84 +nodefs"]] + * \endcode + * + * This method is the equivalent of the C function OSRImportFromProj4(). + * + * @param pszProj4 the PROJ.4 style string. + * + * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure. + */ + +OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 ) + +{ + char **papszNV = NULL; + char **papszTokens; + int i; + char *pszCleanCopy; + int bAddProj4Extension = FALSE; + +/* -------------------------------------------------------------------- */ +/* Clear any existing definition. */ +/* -------------------------------------------------------------------- */ + Clear(); + +/* -------------------------------------------------------------------- */ +/* Strip any newlines or other "funny" stuff that might occur */ +/* if this string just came from reading a file. */ +/* -------------------------------------------------------------------- */ + pszCleanCopy = CPLStrdup( pszProj4 ); + for( i = 0; pszCleanCopy[i] != '\0'; i++ ) + { + if( pszCleanCopy[i] == 10 + || pszCleanCopy[i] == 13 + || pszCleanCopy[i] == 9 ) + pszCleanCopy[i] = ' '; + } + +/* -------------------------------------------------------------------- */ +/* Try to normalize the definition. This should expand +init= */ +/* clauses and so forth. */ +/* -------------------------------------------------------------------- */ + char *pszNormalized; + + pszNormalized = OCTProj4Normalize( pszCleanCopy ); + + /* Workaround proj.4 bug (#239) by manually re-adding no_off/no_uoff */ + if( strstr(pszCleanCopy, "+no_off") != NULL && + strstr(pszNormalized, "+no_off") == NULL ) + { + char* pszTmp = CPLStrdup(CPLSPrintf("%s +no_off", pszNormalized)); + CPLFree(pszNormalized); + pszNormalized = pszTmp; + } + else if( strstr(pszCleanCopy, "+no_uoff") != NULL && + strstr(pszNormalized, "+no_uoff") == NULL ) + { + char* pszTmp = CPLStrdup(CPLSPrintf("%s +no_uoff", pszNormalized)); + CPLFree(pszNormalized); + pszNormalized = pszTmp; + } + + CPLFree( pszCleanCopy ); + +/* -------------------------------------------------------------------- */ +/* If we have an EPSG based init string, and no existing +proj */ +/* portion then try to normalize into into a PROJ.4 string. */ +/* -------------------------------------------------------------------- */ + if( strstr(pszNormalized,"init=epsg:") != NULL + && strstr(pszNormalized,"proj=") == NULL ) + { + OGRErr eErr; + const char *pszNumber = strstr(pszNormalized,"init=epsg:") + 10; + + eErr = importFromEPSG( atoi(pszNumber) ); + if( eErr == OGRERR_NONE ) + { + CPLFree( pszNormalized ); + return eErr; + } + } + +/* -------------------------------------------------------------------- */ +/* Parse the PROJ.4 string into a cpl_string.h style name/value */ +/* list. */ +/* -------------------------------------------------------------------- */ + papszTokens = OSRProj4Tokenize( pszNormalized ); + CPLFree( pszNormalized ); + + for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; i++ ) + { + char *pszEqual = strstr(papszTokens[i],"="); + + if( pszEqual == NULL ) + papszNV = CSLAddNameValue(papszNV, papszTokens[i], "" ); + else + { + pszEqual[0] = '\0'; + papszNV = CSLAddNameValue( papszNV, papszTokens[i], pszEqual+1 ); + } + } + + CSLDestroy( papszTokens ); + +/* -------------------------------------------------------------------- */ +/* Extract the prime meridian, if there is one set. */ +/* -------------------------------------------------------------------- */ + const char *pszPM = CSLFetchNameValue( papszNV, "pm" ); + double dfFromGreenwich = 0.0; + /* int nPMCode = -1; */ + + if( pszPM != NULL ) + { + const OGRProj4PM* psProj4PM = OGRGetProj4PMFromProj4Name(pszPM); + if (psProj4PM) + { + dfFromGreenwich = CPLDMSToDec(psProj4PM->pszFromGreenwich); + pszPM = psProj4PM->pszWKTPMName; + /* nPMCode = psProj4PM->nPMCode; */ + } + else + { + dfFromGreenwich = CPLDMSToDec( pszPM ); + pszPM = "unnamed"; + } + } + else + pszPM = "Greenwich"; + +/* -------------------------------------------------------------------- */ +/* Operate on the basis of the projection name. */ +/* -------------------------------------------------------------------- */ + const char *pszProj = CSLFetchNameValue(papszNV,"proj"); + + if( pszProj == NULL ) + { + CPLDebug( "OGR_PROJ4", "Can't find +proj= in:\n%s", pszProj4 ); + CSLDestroy( papszNV ); + return OGRERR_CORRUPT_DATA; + } + + else if( EQUAL(pszProj,"longlat") || EQUAL(pszProj,"latlong") ) + { + } + + else if( EQUAL(pszProj,"geocent") ) + { + SetGeocCS( "Geocentric" ); + } + + else if( EQUAL(pszProj,"bonne") ) + { + SetBonne( OSR_GDV( papszNV, "lat_1", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"cass") ) + { + SetCS( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"nzmg") ) + { + SetNZMG( OSR_GDV( papszNV, "lat_0", -41.0 ), + OSR_GDV( papszNV, "lon_0", 173.0 ), + OSR_GDV( papszNV, "x_0", 2510000.0 ), + OSR_GDV( papszNV, "y_0", 6023150.0 ) ); + } + + else if( EQUAL(pszProj,"cea") ) + { + SetCEA( OSR_GDV( papszNV, "lat_ts", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"tmerc") ) + { + const char *pszAxis = CSLFetchNameValue( papszNV, "axis" ); + + if( pszAxis == NULL || !EQUAL(pszAxis,"wsu") ) + SetTM( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + else + SetTMSO( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + /* For etmerc, we translate it into standard TM for the WKT */ + /* point of view, but make sure that the original proj.4 */ + /* definition is preserved for accurate reprojection */ + else if( EQUAL(pszProj,"etmerc") && + CSLFetchNameValue( papszNV, "axis" ) == NULL ) + { + bAddProj4Extension = TRUE; + + SetTM( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"utm") ) + { + SetUTM( (int) OSR_GDV( papszNV, "zone", 0.0 ), + (int) OSR_GDV( papszNV, "south", 1.0 ) ); + } + + else if( EQUAL(pszProj,"merc") /* 2SP form */ + && OSR_GDV(papszNV, "lat_ts", 1000.0) < 999.0 ) + { + SetMercator2SP( OSR_GDV( papszNV, "lat_ts", 0.0 ), + 0.0, + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"merc") ) /* 1SP form */ + { + SetMercator( 0.0, + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"stere") + && ABS(OSR_GDV( papszNV, "lat_0", 0.0 ) - 90) < 0.001 ) + { + SetPS( OSR_GDV( papszNV, "lat_ts", 90.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"stere") + && ABS(OSR_GDV( papszNV, "lat_0", 0.0 ) + 90) < 0.001 ) + { + SetPS( OSR_GDV( papszNV, "lat_ts", -90.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"sterea") ) + { + SetOS( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"stere") ) + { + SetStereographic( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"eqc") ) + { + if( OSR_GDV( papszNV, "lat_ts", 0.0 ) != 0.0 ) + SetEquirectangular2( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "lat_ts", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + else + SetEquirectangular( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"gstmerc") ) + { + SetGaussSchreiberTMercator( OSR_GDV( papszNV, "lat_0", -21.116666667 ), + OSR_GDV( papszNV, "lon_0", 55.53333333309), + OSR_GDV( papszNV, "k_0", 1.0 ), + OSR_GDV( papszNV, "x_0", 160000.000 ), + OSR_GDV( papszNV, "y_0", 50000.000 ) ); + } + + else if( EQUAL(pszProj,"gnom") ) + { + SetGnomonic( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"ortho") ) + { + SetOrthographic( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"laea") ) + { + SetLAEA( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"aeqd") ) + { + SetAE( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"eqdc") ) + { + SetEC( OSR_GDV( papszNV, "lat_1", 0.0 ), + OSR_GDV( papszNV, "lat_2", 0.0 ), + OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"mill") ) + { + SetMC( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"moll") ) + { + SetMollweide( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"eck1") || EQUAL(pszProj,"eck2") || EQUAL(pszProj,"eck3") || + EQUAL(pszProj,"eck4") || EQUAL(pszProj,"eck5") || EQUAL(pszProj,"eck6")) + { + SetEckert( pszProj[3] - '0', + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"poly") ) + { + SetPolyconic( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"aea") ) + { + SetACEA( OSR_GDV( papszNV, "lat_1", 0.0 ), + OSR_GDV( papszNV, "lat_2", 0.0 ), + OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"robin") ) + { + SetRobinson( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"vandg") ) + { + SetVDG( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"sinu") ) + { + SetSinusoidal( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"gall") ) + { + SetGS( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"goode") ) + { + SetGH( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"igh") ) + { + SetIGH(); + } + + else if( EQUAL(pszProj,"geos") ) + { + SetGEOS( OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "h", 35785831.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"lcc") ) + { + if( OSR_GDV(papszNV, "lat_0", 0.0 ) + == OSR_GDV(papszNV, "lat_1", 0.0 ) && + CSLFetchNameValue( papszNV, "lat_2" ) == NULL ) + { + /* 1SP form */ + SetLCC1SP( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "k_0", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + else + { + /* 2SP form */ + SetLCC( OSR_GDV( papszNV, "lat_1", 0.0 ), + OSR_GDV( papszNV, "lat_2", 0.0 ), + OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + } + + else if( EQUAL(pszProj,"omerc") ) + { + if( CSLFetchNameValue(papszNV,"no_uoff") != NULL + || CSLFetchNameValue(papszNV,"no_off") != NULL ) + { + /* From PJ_omerc, when alpha is defined but not gamma */ + /* the default gama value is alpha */ + /* if (alp || gam) { + if (alp) { + gamma0 = asin(sin(alpha_c) / D); + if (!gam) + gamma = alpha_c; */ + SetHOM( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lonc", 0.0 ), + OSR_GDV( papszNV, "alpha", 0.0 ), + OSR_GDV( papszNV, "gamma", OSR_GDV( papszNV, "alpha", 0.0 ) ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + else + { + SetHOMAC( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lonc", 0.0 ), + OSR_GDV( papszNV, "alpha", 0.0 ), + OSR_GDV( papszNV, "gamma", OSR_GDV( papszNV, "alpha", 0.0 ) ), + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + } + + else if( EQUAL(pszProj,"somerc") ) + { + SetHOMAC( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + 90.0, 90.0, + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"krovak") ) + { + SetKrovak( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "alpha", 0.0 ), + 0.0, // pseudo_standard_parallel_1 + OSR_GDV( papszNV, "k", 1.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj, "iwm_p") ) + { + SetIWMPolyconic( OSR_GDV( papszNV, "lat_1", 0.0 ), + OSR_GDV( papszNV, "lat_2", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj, "wag1") ) + { + SetWagner( 1, 0.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj, "wag2") ) + { + SetWagner( 2, 0.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj, "wag3") ) + { + SetWagner( 3, + OSR_GDV( papszNV, "lat_ts", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj, "wag4") ) + { + SetWagner( 4, 0.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj, "wag5") ) + { + SetWagner( 5, 0.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj, "wag6") ) + { + SetWagner( 6, 0.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj, "wag7") ) + { + SetWagner( 7, 0.0, + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"qsc") ) + { + SetQSC( OSR_GDV( papszNV, "lat_0", 0.0 ), + OSR_GDV( papszNV, "lon_0", 0.0 ) ); + } + + else if( EQUAL(pszProj,"tpeqd") ) + { + SetTPED( OSR_GDV( papszNV, "lat_1", 0.0 ), + OSR_GDV( papszNV, "lon_1", 0.0 ), + OSR_GDV( papszNV, "lat_2", 0.0 ), + OSR_GDV( papszNV, "lon_2", 0.0 ), + OSR_GDV( papszNV, "x_0", 0.0 ), + OSR_GDV( papszNV, "y_0", 0.0 ) ); + } + + else if( strstr(pszProj4,"wktext") != NULL ) + { + // Fake out a projected coordinate system for otherwise + // unrecognised projections for which we are already planning + // to embed the actual PROJ.4 string via extension node. + SetProjection( "custom_proj4" ); + } + + else + { + CPLDebug( "OGR_PROJ4", "Unsupported projection: %s", pszProj ); + CSLDestroy( papszNV ); + return OGRERR_CORRUPT_DATA; + } + +/* -------------------------------------------------------------------- */ +/* Try to translate the datum. */ +/* -------------------------------------------------------------------- */ + const char *pszValue; + int bFullyDefined = FALSE; + + pszValue = CSLFetchNameValue(papszNV, "datum"); + if( pszValue == NULL ) + { + /* do nothing */ + } + else if( (EQUAL(pszValue,"NAD27") || EQUAL(pszValue,"NAD83") + || EQUAL(pszValue,"WGS84") || EQUAL(pszValue,"WGS72")) + && dfFromGreenwich == 0.0 ) + { + SetWellKnownGeogCS( pszValue ); + bFullyDefined = TRUE; + } + else + { + unsigned int i; + for(i=0;i= 7 ) + SetTOWGS84( CPLAtof(papszToWGS84[0]), + CPLAtof(papszToWGS84[1]), + CPLAtof(papszToWGS84[2]), + CPLAtof(papszToWGS84[3]), + CPLAtof(papszToWGS84[4]), + CPLAtof(papszToWGS84[5]), + CPLAtof(papszToWGS84[6]) ); + else if( CSLCount(papszToWGS84) >= 3 ) + SetTOWGS84( CPLAtof(papszToWGS84[0]), + CPLAtof(papszToWGS84[1]), + CPLAtof(papszToWGS84[2]) ); + else + CPLError( CE_Warning, CPLE_AppDefined, + "Seemingly corrupt +towgs84 option (%s), ignoring.", + pszValue ); + + CSLDestroy(papszToWGS84); + } + +/* -------------------------------------------------------------------- */ +/* Handle nadgrids via an extension node. */ +/* -------------------------------------------------------------------- */ + pszValue = CSLFetchNameValue(papszNV, "nadgrids"); + if( pszValue != NULL ) + { + SetExtension( "DATUM", "PROJ4_GRIDS", pszValue ); + FixupOrdering(); + } + +/* -------------------------------------------------------------------- */ +/* Linear units translation */ +/* -------------------------------------------------------------------- */ + if( IsProjected() || IsLocal() || IsGeocentric() ) + { + pszValue = CSLFetchNameValue(papszNV, "to_meter"); + + if( pszValue != NULL && CPLAtofM(pszValue) > 0.0 ) + { + double dfValue = CPLAtofM(pszValue); + + if( fabs(dfValue - CPLAtof(SRS_UL_US_FOOT_CONV)) < 0.00000001 ) + SetLinearUnits( SRS_UL_US_FOOT, CPLAtof(SRS_UL_US_FOOT_CONV) ); + else if( fabs(dfValue - CPLAtof(SRS_UL_FOOT_CONV)) < 0.00000001 ) + SetLinearUnits( SRS_UL_FOOT, CPLAtof(SRS_UL_FOOT_CONV) ); + else if( dfValue == 1.0 ) + SetLinearUnits( SRS_UL_METER, 1.0 ); + else + SetLinearUnits( "unknown", CPLAtofM(pszValue) ); + } + /* + ** All units reported by cs2cs -lu are supported, fall back to meter. + */ + else if( (pszValue = CSLFetchNameValue(papszNV, "units")) != NULL ) + { + if( EQUAL(pszValue,"meter" ) || EQUAL(pszValue,"m") || EQUAL(pszValue,"metre") ) + SetLinearUnits( SRS_UL_METER, 1.0 ); + /* + ** Leave as 'kilometre' instead of SRS_UL_KILOMETER due to + ** historical usage + */ + else if( EQUAL( pszValue,"km") ) + SetLinearUnits( "kilometre", + CPLAtof( SRS_UL_KILOMETER_CONV ) ); + else if( EQUAL( pszValue,"us-ft" ) ) + SetLinearUnits( SRS_UL_US_FOOT, + CPLAtof( SRS_UL_US_FOOT_CONV ) ); + /* + ** Leave as 'Foot (International)' or SRS_UL_FOOT instead of + ** SRS_UL_INTL_FOOT due to historical usage + */ + else if( EQUAL( pszValue,"ft" ) ) + SetLinearUnits( SRS_UL_FOOT, + CPLAtof( SRS_UL_FOOT_CONV) ); + else if( EQUAL( pszValue,"yd" ) ) + SetLinearUnits( SRS_UL_INTL_YARD, + CPLAtof( SRS_UL_INTL_YARD_CONV ) ); + else if( EQUAL( pszValue,"us-yd" ) ) + SetLinearUnits( SRS_UL_US_YARD, + CPLAtof( SRS_UL_US_YARD_CONV ) ); + else if( EQUAL( pszValue,"dm" ) ) + SetLinearUnits( SRS_UL_DECIMETER, + CPLAtof( SRS_UL_DECIMETER_CONV ) ); + else if( EQUAL( pszValue,"cm" ) ) + SetLinearUnits( SRS_UL_CENTIMETER, + CPLAtof( SRS_UL_CENTIMETER_CONV ) ); + else if( EQUAL( pszValue,"mm" ) ) + SetLinearUnits( SRS_UL_MILLIMETER, + CPLAtof( SRS_UL_MILLIMETER_CONV ) ); + else if( EQUAL( pszValue,"kmi" ) ) + SetLinearUnits( SRS_UL_INTL_NAUT_MILE, + CPLAtof( SRS_UL_INTL_NAUT_MILE_CONV ) ); + else if( EQUAL( pszValue,"in" ) ) + SetLinearUnits( SRS_UL_INTL_INCH, + CPLAtof( SRS_UL_INTL_INCH_CONV ) ); + else if( EQUAL( pszValue,"mi" ) ) + SetLinearUnits( SRS_UL_INTL_STAT_MILE, + CPLAtof( SRS_UL_INTL_STAT_MILE_CONV ) ); + else if( EQUAL( pszValue,"fath" ) ) + SetLinearUnits( SRS_UL_INTL_FATHOM, + CPLAtof( SRS_UL_INTL_FATHOM_CONV ) ); + else if( EQUAL( pszValue,"ch" ) ) + SetLinearUnits( SRS_UL_INTL_CHAIN, + CPLAtof( SRS_UL_INTL_CHAIN_CONV ) ); + else if( EQUAL( pszValue,"link" ) ) + SetLinearUnits( SRS_UL_INTL_LINK, + CPLAtof( SRS_UL_INTL_LINK_CONV ) ); + else if( EQUAL( pszValue,"us-in" ) ) + SetLinearUnits( SRS_UL_US_INCH, + CPLAtof( SRS_UL_US_INCH_CONV ) ); + else if( EQUAL( pszValue, "us-ch" ) ) + SetLinearUnits( SRS_UL_US_CHAIN, + CPLAtof( SRS_UL_US_CHAIN_CONV ) ); + else if( EQUAL( pszValue, "us-mi" ) ) + SetLinearUnits( SRS_UL_US_STAT_MILE, + CPLAtof( SRS_UL_US_STAT_MILE_CONV ) ); + else if( EQUAL( pszValue, "ind-yd" ) ) + SetLinearUnits( SRS_UL_INDIAN_YARD, + CPLAtof( SRS_UL_INDIAN_YARD_CONV ) ); + else if( EQUAL( pszValue, "ind-ft" ) ) + SetLinearUnits( SRS_UL_INDIAN_FOOT, + CPLAtof( SRS_UL_INDIAN_FOOT_CONV ) ); + else if( EQUAL( pszValue, "ind-ch" ) ) + SetLinearUnits( SRS_UL_INDIAN_CHAIN, + CPLAtof( SRS_UL_INDIAN_CHAIN_CONV ) ); + else // This case is untranslatable. Should add all proj.4 unts + SetLinearUnits( pszValue, 1.0 ); + } + } + +/* -------------------------------------------------------------------- */ +/* Adjust linear parameters into PROJCS units if the linear */ +/* units are not meters. */ +/* -------------------------------------------------------------------- */ + if( GetLinearUnits() != 1.0 && IsProjected() ) + { + OGR_SRSNode *poPROJCS = GetAttrNode( "PROJCS" ); + int i; + + for( i = 0; i < poPROJCS->GetChildCount(); i++ ) + { + OGR_SRSNode *poParm = poPROJCS->GetChild(i); + if( !EQUAL(poParm->GetValue(),"PARAMETER") + || poParm->GetChildCount() != 2 ) + continue; + + const char *pszParmName = poParm->GetChild(0)->GetValue(); + + if( IsLinearParameter(pszParmName) ) + SetNormProjParm(pszParmName,GetProjParm(pszParmName)); + } + } + +/* -------------------------------------------------------------------- */ +/* Handle geoidgrids via an extension node and COMPD_CS. */ +/* -------------------------------------------------------------------- */ + pszValue = CSLFetchNameValue(papszNV, "geoidgrids"); + OGR_SRSNode *poVERT_CS = NULL; + if( pszValue != NULL ) + { + OGR_SRSNode *poHorizSRS = GetRoot()->Clone(); + + Clear(); + + CPLString osName = poHorizSRS->GetChild(0)->GetValue(); + osName += " + "; + osName += "Unnamed Vertical Datum"; + + SetNode( "COMPD_CS", osName ); + GetRoot()->AddChild( poHorizSRS ); + + poVERT_CS = new OGR_SRSNode( "VERT_CS" ); + GetRoot()->AddChild( poVERT_CS ); + poVERT_CS->AddChild( new OGR_SRSNode( "Unnamed" ) ); + + CPLString osTarget = GetRoot()->GetValue(); + osTarget += "|VERT_CS|VERT_DATUM"; + + SetNode( osTarget, "Unnamed" ); + + poVERT_CS->GetChild(1)->AddChild( new OGR_SRSNode( "2005" ) ); + SetExtension( osTarget, "PROJ4_GRIDS", pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Handle vertical units. */ +/* -------------------------------------------------------------------- */ + if( poVERT_CS != NULL ) + { + const char *pszUnitName = NULL; + const char *pszUnitConv = NULL; + + pszValue = CSLFetchNameValue(papszNV, "vto_meter"); + + if( pszValue != NULL && CPLAtofM(pszValue) > 0.0 ) + { + double dfValue = CPLAtofM(pszValue); + + if( fabs(dfValue - CPLAtof(SRS_UL_US_FOOT_CONV)) < 0.00000001 ) + { + pszUnitName = SRS_UL_US_FOOT; + pszUnitConv = SRS_UL_US_FOOT_CONV; + } + else if( fabs(dfValue - CPLAtof(SRS_UL_FOOT_CONV)) < 0.00000001 ) + { + pszUnitName = SRS_UL_FOOT; + pszUnitConv = SRS_UL_FOOT_CONV; + } + else if( dfValue == 1.0 ) + { + pszUnitName = SRS_UL_METER; + pszUnitConv = "1.0"; + } + else + { + pszUnitName = "unknown"; + pszUnitConv = pszValue; + } + } + else if( (pszValue = CSLFetchNameValue(papszNV, "vunits")) != NULL ) + { + if( EQUAL(pszValue,"meter" ) || EQUAL(pszValue,"m") || EQUAL(pszValue,"metre") ) + { + pszUnitName = SRS_UL_METER; + pszUnitConv = "1.0"; + } + else if( EQUAL(pszValue,"us-ft" ) ) + { + pszUnitName = SRS_UL_US_FOOT; + pszUnitConv = SRS_UL_US_FOOT_CONV; + } + else if( EQUAL(pszValue,"ft" ) ) + { + pszUnitName = SRS_UL_FOOT; + pszUnitConv = SRS_UL_FOOT_CONV; + } + else if( EQUAL(pszValue,"yd" ) ) + { + pszUnitName = "Yard"; + pszUnitConv = "0.9144"; + } + else if( EQUAL(pszValue,"us-yd" ) ) + { + pszUnitName = "US Yard"; + pszUnitConv = "0.914401828803658"; + } + } + + if( pszUnitName != NULL ) + { + OGR_SRSNode *poUnits; + + poUnits = new OGR_SRSNode( "UNIT" ); + poUnits->AddChild( new OGR_SRSNode( pszUnitName ) ); + poUnits->AddChild( new OGR_SRSNode( pszUnitConv ) ); + + poVERT_CS->AddChild( poUnits ); + } + } + + /* Add AXIS to VERT_CS node */ + if( poVERT_CS != NULL ) + { + OGR_SRSNode *poAxis = new OGR_SRSNode( "AXIS" ); + + poAxis->AddChild( new OGR_SRSNode( "Up" ) ); + poAxis->AddChild( new OGR_SRSNode( "UP" ) ); + + poVERT_CS->AddChild( poAxis ); + } + +/* -------------------------------------------------------------------- */ +/* do we want to insert a PROJ.4 EXTENSION item? */ +/* -------------------------------------------------------------------- */ + if( strstr(pszProj4,"wktext") != NULL || bAddProj4Extension ) + SetExtension( GetRoot()->GetValue(), "PROJ4", pszProj4 ); + +/* -------------------------------------------------------------------- */ +/* Preserve authority (for example IGNF) */ +/* -------------------------------------------------------------------- */ + const char *pszINIT = CSLFetchNameValue(papszNV,"init"); + const char *pszColumn; + if( pszINIT != NULL && (pszColumn = strchr(pszINIT, ':')) != NULL && + GetRoot()->FindChild( "AUTHORITY" ) < 0 ) + { + CPLString osAuthority; + osAuthority.assign(pszINIT, pszColumn - pszINIT); + OGR_SRSNode* poAuthNode = new OGR_SRSNode( "AUTHORITY" ); + poAuthNode->AddChild( new OGR_SRSNode( osAuthority ) ); + poAuthNode->AddChild( new OGR_SRSNode( pszColumn + 1 ) ); + + GetRoot()->AddChild( poAuthNode ); + } + + CSLDestroy( papszNV ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* LinearToProj4() */ +/************************************************************************/ + +static const char *LinearToProj4( double dfLinearConv, + const char *pszLinearUnits ) + +{ + if( dfLinearConv == 1.0 ) + return "m"; + + else if( dfLinearConv == 1000.0 ) + return "km"; + + else if( dfLinearConv == 0.0254 ) + return "in"; + + else if( EQUAL(pszLinearUnits,SRS_UL_FOOT) + || fabs(dfLinearConv - CPLAtof(SRS_UL_FOOT_CONV)) < 0.000000001 ) + return "ft"; + + else if( EQUAL(pszLinearUnits,"IYARD") || dfLinearConv == 0.9144 ) + return "yd"; + + else if( dfLinearConv == 0.914401828803658 ) + return "us-yd"; + + else if( dfLinearConv == 0.001 ) + return "mm"; + + else if( dfLinearConv == 0.01 ) + return "cm"; + + else if( EQUAL(pszLinearUnits,SRS_UL_US_FOOT) + || fabs(dfLinearConv - CPLAtof(SRS_UL_US_FOOT_CONV)) < 0.00000001 ) + return "us-ft"; + + else if( EQUAL(pszLinearUnits,SRS_UL_NAUTICAL_MILE) ) + return "kmi"; + + else if( EQUAL(pszLinearUnits,"Mile") + || EQUAL(pszLinearUnits,"IMILE") ) + return "mi"; + else + return NULL; +} + + +/************************************************************************/ +/* OSRExportToProj4() */ +/************************************************************************/ +/** + * \brief Export coordinate system in PROJ.4 format. + * + * This function is the same as OGRSpatialReference::exportToProj4(). + */ +OGRErr CPL_STDCALL OSRExportToProj4( OGRSpatialReferenceH hSRS, + char ** ppszReturn ) + +{ + VALIDATE_POINTER1( hSRS, "OSRExportToProj4", CE_Failure ); + + *ppszReturn = NULL; + + return ((OGRSpatialReference *) hSRS)->exportToProj4( ppszReturn ); +} + +/************************************************************************/ +/* exportToProj4() */ +/************************************************************************/ + +#define SAFE_PROJ4_STRCAT(szNewStr) do { \ + if(CPLStrlcat(szProj4, szNewStr, sizeof(szProj4)) >= sizeof(szProj4)) { \ + CPLError(CE_Failure, CPLE_AppDefined, "String overflow when formatting proj.4 string"); \ + *ppszProj4 = CPLStrdup(""); \ + return OGRERR_FAILURE; \ + } } while(0); + +/** + * \brief Export coordinate system in PROJ.4 format. + * + * Converts the loaded coordinate reference system into PROJ.4 format + * to the extent possible. The string returned in ppszProj4 should be + * deallocated by the caller with CPLFree() when no longer needed. + * + * LOCAL_CS coordinate systems are not translatable. An empty string + * will be returned along with OGRERR_NONE. + * + * Special processing for Transverse Mercator with GDAL >= 1.10 and PROJ >= 4.8 : + * if the OSR_USE_ETMERC configuration option is set to YES, the PROJ.4 + * definition built from the SRS will use the 'etmerc' projection method, + * rather than the default 'tmerc'. This will give better accuracy (at the + * expense of computational speed) when reprojection occurs near the edges + * of the validity area for the projection. + * + * This method is the equivelent of the C function OSRExportToProj4(). + * + * @param ppszProj4 pointer to which dynamically allocated PROJ.4 definition + * will be assigned. + * + * @return OGRERR_NONE on success or an error code on failure. + */ + +OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const + +{ + char szProj4[512]; + const char *pszProjection = GetAttrValue("PROJECTION"); + + szProj4[0] = '\0'; + + if( GetRoot() == NULL ) + { + *ppszProj4 = CPLStrdup(""); + CPLError( CE_Failure, CPLE_NotSupported, + "No translation for an empty SRS to PROJ.4 format is known."); + return OGRERR_UNSUPPORTED_SRS; + } + +/* -------------------------------------------------------------------- */ +/* Do we have a PROJ.4 override definition? */ +/* -------------------------------------------------------------------- */ + const char *pszPredefProj4 = GetExtension( GetRoot()->GetValue(), + "PROJ4", NULL ); + if( pszPredefProj4 != NULL ) + { + *ppszProj4 = CPLStrdup( pszPredefProj4 ); + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Get the prime meridian info. */ +/* -------------------------------------------------------------------- */ + const OGR_SRSNode *poPRIMEM = GetAttrNode( "PRIMEM" ); + double dfFromGreenwich = 0.0; + + if( poPRIMEM != NULL && poPRIMEM->GetChildCount() >= 2 + && CPLAtof(poPRIMEM->GetChild(1)->GetValue()) != 0.0 ) + { + dfFromGreenwich = CPLAtof(poPRIMEM->GetChild(1)->GetValue()); + } + +/* ==================================================================== */ +/* Handle the projection definition. */ +/* ==================================================================== */ + + if( pszProjection == NULL && IsGeographic() ) + { + CPLsprintf( szProj4+strlen(szProj4), "+proj=longlat " ); + } + else if( IsGeocentric() ) + { + CPLsprintf( szProj4+strlen(szProj4), "+proj=geocent " ); + } + + else if( pszProjection == NULL && !IsGeographic() ) + { + // LOCAL_CS, or incompletely initialized coordinate systems. + *ppszProj4 = CPLStrdup(""); + return OGRERR_NONE; + } + else if( EQUAL(pszProjection,SRS_PT_CYLINDRICAL_EQUAL_AREA) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=cea +lon_0=%.16g +lat_ts=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_BONNE) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=bonne +lon_0=%.16g +lat_1=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_CASSINI_SOLDNER) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=cass +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_NEW_ZEALAND_MAP_GRID) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=nzmg +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR) || + EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_21) || + EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_22) || + EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_23) || + EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_24) || + EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_25) ) + { + int bNorth; + int nZone = GetUTMZone( &bNorth ); + + if( CSLTestBoolean(CPLGetConfigOption("OSR_USE_ETMERC", "FALSE")) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=etmerc +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + else if( nZone != 0 ) + { + if( bNorth ) + CPLsprintf( szProj4+strlen(szProj4), "+proj=utm +zone=%d ", + nZone ); + else + CPLsprintf( szProj4+strlen(szProj4),"+proj=utm +zone=%d +south ", + nZone ); + } + else + CPLsprintf( szProj4+strlen(szProj4), + "+proj=tmerc +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=tmerc +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g +axis=wsu ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_MERCATOR_1SP) ) + { + if( GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0) == 0.0 ) + CPLsprintf( szProj4+strlen(szProj4), + "+proj=merc +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + else if( GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0) == 1.0 ) + CPLsprintf( szProj4+strlen(szProj4), + "+proj=merc +lon_0=%.16g +lat_ts=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Mercator_1SP with scale != 1.0 and latitude of origin != 0, not supported by PROJ.4." ); + *ppszProj4 = CPLStrdup(""); + return OGRERR_UNSUPPORTED_SRS; + } + } + + else if( EQUAL(pszProjection,SRS_PT_MERCATOR_2SP) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=merc +lon_0=%.16g +lat_ts=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_MERCATOR_AUXILIARY_SPHERE) ) + { + // This is EPSG:3875 Pseudo Mercator. No point in trying to parse the + // rest of the parameters, since we know pretty much everything at this + // stage. + CPLsprintf( szProj4+strlen(szProj4), + "+proj=merc +a=%.16g +b=%.16g +lat_ts=%.16g" + " +lon_0=%.16g +x_0=%.16g +y_0=%.16g +k=%.16g +units=m" + " +nadgrids=@null +wktext +no_defs", + GetSemiMajor(), GetSemiMajor(), + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0) ); + *ppszProj4 = CPLStrdup( szProj4 ); + + return OGRERR_NONE; + } + + else if( EQUAL(pszProjection,SRS_PT_OBLIQUE_STEREOGRAPHIC) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=sterea +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", +// "+proj=stere +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_STEREOGRAPHIC) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=stere +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_POLAR_STEREOGRAPHIC) ) + { + if( GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0) >= 0.0 ) + CPLsprintf( szProj4+strlen(szProj4), + "+proj=stere +lat_0=90 +lat_ts=%.16g +lon_0=%.16g " + "+k=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,90.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + else + CPLsprintf( szProj4+strlen(szProj4), + "+proj=stere +lat_0=-90 +lat_ts=%.16g +lon_0=%.16g " + "+k=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,-90.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_EQUIRECTANGULAR) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=eqc +lat_ts=%.16g +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_GAUSSSCHREIBERTMERCATOR) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=gstmerc +lat_0=%.16g +lon_0=%.16g" + " +k_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,-21.116666667), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,55.53333333309), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,160000.000), + GetNormProjParm(SRS_PP_FALSE_NORTHING,50000.000) ); + } + + else if( EQUAL(pszProjection,SRS_PT_GNOMONIC) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=gnom +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_ORTHOGRAPHIC) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=ortho +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=laea +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_AZIMUTHAL_EQUIDISTANT) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=aeqd +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_EQUIDISTANT_CONIC) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=eqdc +lat_0=%.16g +lon_0=%.16g +lat_1=%.16g +lat_2=%.16g" + " +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER,0.0), + GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0), + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_MILLER_CYLINDRICAL) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=mill +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +R_A ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_MOLLWEIDE) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=moll +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_ECKERT_I) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=eck1 +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_ECKERT_II) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=eck2 +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_ECKERT_III) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=eck3 +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_ECKERT_IV) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=eck4 +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_ECKERT_V) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=eck5 +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_ECKERT_VI) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=eck6 +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_POLYCONIC) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=poly +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_ALBERS_CONIC_EQUAL_AREA) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=aea +lat_1=%.16g +lat_2=%.16g +lat_0=%.16g +lon_0=%.16g" + " +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0), + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_ROBINSON) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=robin +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_VANDERGRINTEN) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=vandg +lon_0=%.16g +x_0=%.16g +y_0=%.16g +R_A ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_SINUSOIDAL) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=sinu +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_GALL_STEREOGRAPHIC) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=gall +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_GOODE_HOMOLOSINE) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=goode +lon_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_IGH) ) + { + CPLsprintf( szProj4+strlen(szProj4), "+proj=igh " ); + } + + else if( EQUAL(pszProjection,SRS_PT_GEOSTATIONARY_SATELLITE) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=geos +lon_0=%.16g +h=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SATELLITE_HEIGHT,35785831.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) + || EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=lcc +lat_1=%.16g +lat_2=%.16g +lat_0=%.16g +lon_0=%.16g" + " +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0), + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=lcc +lat_1=%.16g +lat_0=%.16g +lon_0=%.16g" + " +k_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_HOTINE_OBLIQUE_MERCATOR) ) + { + /* special case for swiss oblique mercator : see bug 423 */ + if( fabs(GetNormProjParm(SRS_PP_AZIMUTH,0.0) - 90.0) < 0.0001 + && fabs(GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,0.0)-90.0) < 0.0001 ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=somerc +lat_0=%.16g +lon_0=%.16g" + " +k_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + else + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=omerc +lat_0=%.16g +lonc=%.16g +alpha=%.16g" + " +k=%.16g +x_0=%.16g +y_0=%.16g +no_uoff ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_AZIMUTH,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + + // RSO variant - http://trac.osgeo.org/proj/ticket/62 + // Note that gamma is only supported by PROJ 4.8.0 and later. + if( GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,1000.0) != 1000.0 ) + { + CPLsprintf( szProj4+strlen(szProj4), "+gamma=%.16g ", + GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,1000.0)); + } + } + } + + else if( EQUAL(pszProjection,SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER)) + { + /* special case for swiss oblique mercator : see bug 423 */ + if( fabs(GetNormProjParm(SRS_PP_AZIMUTH,0.0) - 90.0) < 0.0001 + && fabs(GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,0.0)-90.0) < 0.0001 ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=somerc +lat_0=%.16g +lon_0=%.16g" + " +k_0=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + else + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=omerc +lat_0=%.16g +lonc=%.16g +alpha=%.16g" + " +k=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_AZIMUTH,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + + // RSO variant - http://trac.osgeo.org/proj/ticket/62 + // Note that gamma is only supported by PROJ 4.8.0 and later. + if( GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,1000.0) != 1000.0 ) + { + CPLsprintf( szProj4+strlen(szProj4), "+gamma=%.16g ", + GetNormProjParm(SRS_PP_RECTIFIED_GRID_ANGLE,1000.0)); + } + } + } + + else if( EQUAL(pszProjection, + SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN) ) + { + // Not really clear which of Point_1/1st_Point convention is the + // "normalized" one, so accept both + CPLsprintf( szProj4+strlen(szProj4), + "+proj=omerc +lat_0=%.16g" + " +lon_1=%.16g +lat_1=%.16g +lon_2=%.16g +lat_2=%.16g" + " +k=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_1,GetNormProjParm(SRS_PP_LONGITUDE_OF_1ST_POINT,0.0)), + GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_1,GetNormProjParm(SRS_PP_LATITUDE_OF_1ST_POINT,0.0)), + GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_2,GetNormProjParm(SRS_PP_LONGITUDE_OF_2ND_POINT,0.0)), + GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_2,GetNormProjParm(SRS_PP_LATITUDE_OF_2ND_POINT,0.0)), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_KROVAK) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=krovak +lat_0=%.16g +lon_0=%.16g +alpha=%.16g" + " +k=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER,0.0), + GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0), + GetNormProjParm(SRS_PP_AZIMUTH,0.0), + GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_TWO_POINT_EQUIDISTANT) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=tpeqd +lat_1=%.16g +lon_1=%.16g " + "+lat_2=%.16g +lon_2=%.16g " + "+x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_1ST_POINT,0.0), + GetNormProjParm(SRS_PP_LONGITUDE_OF_1ST_POINT,0.0), + GetNormProjParm(SRS_PP_LATITUDE_OF_2ND_POINT,0.0), + GetNormProjParm(SRS_PP_LONGITUDE_OF_2ND_POINT,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection, SRS_PT_IMW_POLYCONIC) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=iwm_p +lat_1=%.16g +lat_2=%.16g +lon_0=%.16g " + "+x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_1ST_POINT, 0.0), + GetNormProjParm(SRS_PP_LATITUDE_OF_2ND_POINT, 0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) ); + } + + else if( EQUAL(pszProjection, SRS_PT_WAGNER_I) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=wag1 +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) ); + } + + else if( EQUAL(pszProjection, SRS_PT_WAGNER_II) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=wag2 +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) ); + } + + else if( EQUAL(pszProjection, SRS_PT_WAGNER_III) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=wag3 +lat_ts=%.16g +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) ); + } + + else if( EQUAL(pszProjection, SRS_PT_WAGNER_IV) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=wag4 +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) ); + } + + else if( EQUAL(pszProjection, SRS_PT_WAGNER_V) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=wag5 +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) ); + } + + else if( EQUAL(pszProjection, SRS_PT_WAGNER_VI) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=wag6 +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) ); + } + + else if( EQUAL(pszProjection, SRS_PT_WAGNER_VII) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=wag7 +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_QSC) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=qsc +lat_0=%.16g +lon_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0) ); + } + + /* Note: This never really gets used currently. See bug 423 */ + else if( EQUAL(pszProjection,SRS_PT_SWISS_OBLIQUE_CYLINDRICAL) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=somerc +lat_0=%.16g +lon_0=%.16g" + " +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + else if( EQUAL(pszProjection,SRS_PT_AITOFF) ) + { + //+lat_ts=0.0 + CPLsprintf( szProj4+strlen(szProj4), + "+proj=aitoff +lat_0=%.16g +lon_0=%.16g" + " +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + + else if( EQUAL(pszProjection,SRS_PT_WINKEL_I) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=wink1 +lat_0=%.16g +lon_0=%.16g lat_ts=%.16g" + " +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,45.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0)); + } + + else if( EQUAL(pszProjection,SRS_PT_WINKEL_II) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=wink2 +lat_0=%.16g +lon_0=%.16g +lat_1=%.16g" + " +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,40.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0)); + } + + else if( EQUAL(pszProjection,SRS_PT_WINKEL_TRIPEL) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=wintri +lat_0=%.16g +lon_0=%.16g +lat_1=%.16g" + " +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,40.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0)); + } + + else if( EQUAL(pszProjection,SRS_PT_CRASTER_PARABOLIC) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=crast +lat_0=%.16g +lon_0=%.16g" + " +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0)); + } + + else if( EQUAL(pszProjection,SRS_PT_LOXIMUTHAL) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=loxim +lon_0=%.16g +lat_1=%.16g" + " +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,40.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0)); + } + + else if( EQUAL(pszProjection,SRS_PT_QUARTIC_AUTHALIC) ) + { + CPLsprintf( szProj4+strlen(szProj4), + "+proj=qua_aut +lat_0=%.16g +lon_0=%.16g" + " +x_0=%.16g +y_0=%.16g ", + GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), + GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), + GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0)); + } + + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "No translation for %s to PROJ.4 format is known.", + pszProjection ); + *ppszProj4 = CPLStrdup(""); + return OGRERR_UNSUPPORTED_SRS; + } + +/* -------------------------------------------------------------------- */ +/* Handle earth model. For now we just always emit the user */ +/* defined ellipsoid parameters. */ +/* -------------------------------------------------------------------- */ + double dfSemiMajor = GetSemiMajor(); + double dfInvFlattening = GetInvFlattening(); + const char *pszPROJ4Ellipse = NULL; + const char *pszDatum = GetAttrValue("DATUM"); + + if( ABS(dfSemiMajor-6378249.145) < 0.01 + && ABS(dfInvFlattening-293.465) < 0.0001 ) + { + pszPROJ4Ellipse = "clrk80"; /* Clark 1880 */ + } + else if( ABS(dfSemiMajor-6378245.0) < 0.01 + && ABS(dfInvFlattening-298.3) < 0.0001 ) + { + pszPROJ4Ellipse = "krass"; /* Krassovsky */ + } + else if( ABS(dfSemiMajor-6378388.0) < 0.01 + && ABS(dfInvFlattening-297.0) < 0.0001 ) + { + pszPROJ4Ellipse = "intl"; /* International 1924 */ + } + else if( ABS(dfSemiMajor-6378160.0) < 0.01 + && ABS(dfInvFlattening-298.25) < 0.0001 ) + { + pszPROJ4Ellipse = "aust_SA"; /* Australian */ + } + else if( ABS(dfSemiMajor-6377397.155) < 0.01 + && ABS(dfInvFlattening-299.1528128) < 0.0001 ) + { + pszPROJ4Ellipse = "bessel"; /* Bessel 1841 */ + } + else if( ABS(dfSemiMajor-6377483.865) < 0.01 + && ABS(dfInvFlattening-299.1528128) < 0.0001 ) + { + pszPROJ4Ellipse = "bess_nam"; /* Bessel 1841 (Namibia / Schwarzeck)*/ + } + else if( ABS(dfSemiMajor-6378160.0) < 0.01 + && ABS(dfInvFlattening-298.247167427) < 0.0001 ) + { + pszPROJ4Ellipse = "GRS67"; /* GRS 1967 */ + } + else if( ABS(dfSemiMajor-6378137) < 0.01 + && ABS(dfInvFlattening-298.257222101) < 0.000001 ) + { + pszPROJ4Ellipse = "GRS80"; /* GRS 1980 */ + } + else if( ABS(dfSemiMajor-6378206.4) < 0.01 + && ABS(dfInvFlattening-294.9786982) < 0.0001 ) + { + pszPROJ4Ellipse = "clrk66"; /* Clarke 1866 */ + } + else if( ABS(dfSemiMajor-6377340.189) < 0.01 + && ABS(dfInvFlattening-299.3249646) < 0.0001 ) + { + pszPROJ4Ellipse = "mod_airy"; /* Modified Airy */ + } + else if( ABS(dfSemiMajor-6377563.396) < 0.01 + && ABS(dfInvFlattening-299.3249646) < 0.0001 ) + { + pszPROJ4Ellipse = "airy"; /* Airy */ + } + else if( ABS(dfSemiMajor-6378200) < 0.01 + && ABS(dfInvFlattening-298.3) < 0.0001 ) + { + pszPROJ4Ellipse = "helmert"; /* Helmert 1906 */ + } + else if( ABS(dfSemiMajor-6378155) < 0.01 + && ABS(dfInvFlattening-298.3) < 0.0001 ) + { + pszPROJ4Ellipse = "fschr60m"; /* Modified Fischer 1960 */ + } + else if( ABS(dfSemiMajor-6377298.556) < 0.01 + && ABS(dfInvFlattening-300.8017) < 0.0001 ) + { + pszPROJ4Ellipse = "evrstSS"; /* Everest (Sabah & Sarawak) */ + } + else if( ABS(dfSemiMajor-6378165.0) < 0.01 + && ABS(dfInvFlattening-298.3) < 0.0001 ) + { + pszPROJ4Ellipse = "WGS60"; + } + else if( ABS(dfSemiMajor-6378145.0) < 0.01 + && ABS(dfInvFlattening-298.25) < 0.0001 ) + { + pszPROJ4Ellipse = "WGS66"; + } + else if( ABS(dfSemiMajor-6378135.0) < 0.01 + && ABS(dfInvFlattening-298.26) < 0.0001 ) + { + pszPROJ4Ellipse = "WGS72"; + } + else if( ABS(dfSemiMajor-6378137.0) < 0.01 + && ABS(dfInvFlattening-298.257223563) < 0.000001 ) + { + pszPROJ4Ellipse = "WGS84"; + } + else if( pszDatum != NULL && EQUAL(pszDatum,"North_American_Datum_1927") ) + { +// pszPROJ4Ellipse = "clrk66:+datum=nad27"; /* NAD 27 */ + pszPROJ4Ellipse = "clrk66"; + } + else if( pszDatum != NULL && EQUAL(pszDatum,"North_American_Datum_1983") ) + { +// pszPROJ4Ellipse = "GRS80:+datum=nad83"; /* NAD 83 */ + pszPROJ4Ellipse = "GRS80"; + } + + char szEllipseDef[128]; + + if( pszPROJ4Ellipse == NULL ) + CPLsprintf( szEllipseDef, "+a=%.16g +b=%.16g ", + GetSemiMajor(), GetSemiMinor() ); + else + CPLsprintf( szEllipseDef, "+ellps=%s ", + pszPROJ4Ellipse ); + +/* -------------------------------------------------------------------- */ +/* Translate the datum. */ +/* -------------------------------------------------------------------- */ + const char *pszPROJ4Datum = NULL; + const OGR_SRSNode *poTOWGS84 = GetAttrNode( "TOWGS84" ); + char szTOWGS84[256]; + int nEPSGDatum = -1; + const char *pszAuthority; + int nEPSGGeogCS = -1; + const char *pszGeogCSAuthority; + const char *pszProj4Grids = GetExtension( "DATUM", "PROJ4_GRIDS" ); + + pszAuthority = GetAuthorityName( "DATUM" ); + + if( pszAuthority != NULL && EQUAL(pszAuthority,"EPSG") ) + nEPSGDatum = atoi(GetAuthorityCode( "DATUM" )); + + pszGeogCSAuthority = GetAuthorityName( "GEOGCS" ); + + if( pszGeogCSAuthority != NULL && EQUAL(pszGeogCSAuthority,"EPSG") ) + nEPSGGeogCS = atoi(GetAuthorityCode( "GEOGCS" )); + + if( pszDatum == NULL ) + /* nothing */; + + else if( EQUAL(pszDatum,SRS_DN_NAD27) || nEPSGDatum == 6267 ) + pszPROJ4Datum = "NAD27"; + + else if( EQUAL(pszDatum,SRS_DN_NAD83) || nEPSGDatum == 6269 ) + pszPROJ4Datum = "NAD83"; + + else if( EQUAL(pszDatum,SRS_DN_WGS84) || nEPSGDatum == 6326 ) + pszPROJ4Datum = "WGS84"; + + else if( (pszPROJ4Datum = OGRGetProj4Datum(pszDatum, nEPSGDatum)) != NULL ) + { + /* nothing */ + } + + if( pszProj4Grids != NULL ) + { + SAFE_PROJ4_STRCAT( szEllipseDef ); + szEllipseDef[0] = '\0'; + SAFE_PROJ4_STRCAT( "+nadgrids=" ); + SAFE_PROJ4_STRCAT( pszProj4Grids ); + SAFE_PROJ4_STRCAT( " " ); + pszPROJ4Datum = NULL; + } + + if( pszPROJ4Datum == NULL + || CSLTestBoolean(CPLGetConfigOption("OVERRIDE_PROJ_DATUM_WITH_TOWGS84", "YES")) ) + { + if( poTOWGS84 != NULL ) + { + int iChild; + if( poTOWGS84->GetChildCount() >= 3 + && (poTOWGS84->GetChildCount() < 7 + || (EQUAL(poTOWGS84->GetChild(3)->GetValue(),"") + && EQUAL(poTOWGS84->GetChild(4)->GetValue(),"") + && EQUAL(poTOWGS84->GetChild(5)->GetValue(),"") + && EQUAL(poTOWGS84->GetChild(6)->GetValue(),""))) ) + { + SAFE_PROJ4_STRCAT( szEllipseDef ); + szEllipseDef[0] = '\0'; + SAFE_PROJ4_STRCAT( "+towgs84="); + for(iChild = 0; iChild < 3; iChild ++) + { + if (iChild > 0 ) SAFE_PROJ4_STRCAT( "," ); + SAFE_PROJ4_STRCAT( poTOWGS84->GetChild(iChild)->GetValue() ); + } + SAFE_PROJ4_STRCAT( " " ); + pszPROJ4Datum = NULL; + } + else if( poTOWGS84->GetChildCount() >= 7) + { + SAFE_PROJ4_STRCAT( szEllipseDef ); + szEllipseDef[0] = '\0'; + SAFE_PROJ4_STRCAT( "+towgs84="); + for(iChild = 0; iChild < 7; iChild ++) + { + if (iChild > 0 ) SAFE_PROJ4_STRCAT( "," ); + SAFE_PROJ4_STRCAT( poTOWGS84->GetChild(iChild)->GetValue() ); + } + SAFE_PROJ4_STRCAT( " " ); + pszPROJ4Datum = NULL; + } + } + + // If we don't know the datum, trying looking up TOWGS84 parameters + // based on the EPSG GCS code. + else if( nEPSGGeogCS != -1 && pszPROJ4Datum == NULL ) + { + std::vector asBursaTransform; + if( EPSGGetWGS84Transform( nEPSGGeogCS, asBursaTransform ) ) + { + CPLsprintf( szTOWGS84, "+towgs84=%s,%s,%s,%s,%s,%s,%s", + asBursaTransform[0].c_str(), + asBursaTransform[1].c_str(), + asBursaTransform[2].c_str(), + asBursaTransform[3].c_str(), + asBursaTransform[4].c_str(), + asBursaTransform[5].c_str(), + asBursaTransform[6].c_str() ); + SAFE_PROJ4_STRCAT( szEllipseDef ); + szEllipseDef[0] = '\0'; + + SAFE_PROJ4_STRCAT( szTOWGS84 ); + SAFE_PROJ4_STRCAT( " " ); + pszPROJ4Datum = NULL; + } + } + } + + if( pszPROJ4Datum != NULL ) + { + SAFE_PROJ4_STRCAT( "+datum=" ); + SAFE_PROJ4_STRCAT( pszPROJ4Datum ); + SAFE_PROJ4_STRCAT( " " ); + } + else // The ellipsedef may already have been appended and will now + // be empty, otherwise append now. + { + SAFE_PROJ4_STRCAT( szEllipseDef ); + szEllipseDef[0] = '\0'; + } + +/* -------------------------------------------------------------------- */ +/* Is there prime meridian info to apply? */ +/* -------------------------------------------------------------------- */ + if( poPRIMEM != NULL && poPRIMEM->GetChildCount() >= 2 + && CPLAtof(poPRIMEM->GetChild(1)->GetValue()) != 0.0 ) + { + const char *pszAuthority = GetAuthorityName( "PRIMEM" ); + char szPMValue[128]; + int nCode = -1; + + if( pszAuthority != NULL && EQUAL(pszAuthority,"EPSG") ) + nCode = atoi(GetAuthorityCode( "PRIMEM" )); + + const OGRProj4PM* psProj4PM = NULL; + if (nCode > 0) + psProj4PM = OGRGetProj4PMFromCode(nCode); + if (psProj4PM == NULL) + psProj4PM = OGRGetProj4PMFromVal(dfFromGreenwich); + + if (psProj4PM != NULL) + { + strcpy( szPMValue, psProj4PM->pszProj4PMName ); + } + else + { + CPLsprintf( szPMValue, "%.16g", dfFromGreenwich ); + } + + SAFE_PROJ4_STRCAT( "+pm=" ); + SAFE_PROJ4_STRCAT( szPMValue ); + SAFE_PROJ4_STRCAT( " " ); + } + +/* -------------------------------------------------------------------- */ +/* Handle linear units. */ +/* -------------------------------------------------------------------- */ + const char *pszPROJ4Units=NULL; + char *pszLinearUnits = NULL; + double dfLinearConv; + + dfLinearConv = GetLinearUnits( &pszLinearUnits ); + + if( strstr(szProj4,"longlat") != NULL ) + pszPROJ4Units = NULL; + else + { + pszPROJ4Units = LinearToProj4( dfLinearConv, pszLinearUnits ); + + if( pszPROJ4Units == NULL ) + { + char szLinearConv[128]; + CPLsprintf( szLinearConv, "%.16g", dfLinearConv ); + SAFE_PROJ4_STRCAT( "+to_meter=" ); + SAFE_PROJ4_STRCAT( szLinearConv ); + SAFE_PROJ4_STRCAT( " " ); + } + } + + if( pszPROJ4Units != NULL ) + { + SAFE_PROJ4_STRCAT( "+units="); + SAFE_PROJ4_STRCAT( pszPROJ4Units ); + SAFE_PROJ4_STRCAT( " " ); + } + +/* -------------------------------------------------------------------- */ +/* If we have vertical datum grids, attach them to the proj.4 string.*/ +/* -------------------------------------------------------------------- */ + const char *pszProj4Geoids = GetExtension( "VERT_DATUM", "PROJ4_GRIDS" ); + + if( pszProj4Geoids != NULL ) + { + SAFE_PROJ4_STRCAT( "+geoidgrids=" ); + SAFE_PROJ4_STRCAT( pszProj4Geoids ); + SAFE_PROJ4_STRCAT( " " ); + } + +/* -------------------------------------------------------------------- */ +/* Handle vertical units, but only if we have them. */ +/* -------------------------------------------------------------------- */ + const OGR_SRSNode *poVERT_CS = GetRoot()->GetNode( "VERT_CS" ); + const OGR_SRSNode *poVUNITS = NULL; + + if( poVERT_CS != NULL ) + poVUNITS = poVERT_CS->GetNode( "UNIT" ); + + if( poVUNITS != NULL && poVUNITS->GetChildCount() >= 2 ) + { + pszPROJ4Units = NULL; + + dfLinearConv = CPLAtof( poVUNITS->GetChild(1)->GetValue() ); + + pszPROJ4Units = LinearToProj4( dfLinearConv, + poVUNITS->GetChild(0)->GetValue() ); + + if( pszPROJ4Units == NULL ) + { + char szLinearConv[128]; + CPLsprintf( szLinearConv, "%.16g", dfLinearConv ); + SAFE_PROJ4_STRCAT( "+vto_meter=" ); + SAFE_PROJ4_STRCAT( szLinearConv ); + SAFE_PROJ4_STRCAT( " " ); + } + else + { + SAFE_PROJ4_STRCAT( "+vunits="); + SAFE_PROJ4_STRCAT( pszPROJ4Units ); + SAFE_PROJ4_STRCAT( " " ); + } + } + +/* -------------------------------------------------------------------- */ +/* Add the no_defs flag to ensure that no values from */ +/* proj_def.dat are implicitly used with our definitions. */ +/* -------------------------------------------------------------------- */ + SAFE_PROJ4_STRCAT( "+no_defs " ); + + *ppszProj4 = CPLStrdup( szProj4 ); + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogr_srs_usgs.cpp b/bazaar/plugin/gdal/ogr/ogr_srs_usgs.cpp new file mode 100644 index 000000000..20fc4deba --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srs_usgs.cpp @@ -0,0 +1,1201 @@ +/****************************************************************************** + * $Id: ogr_srs_usgs.cpp 28565 2015-02-27 10:26:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: OGRSpatialReference translation to/from USGS georeferencing + * information (used in GCTP package). + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ****************************************************************************** + * Copyright (c) 2004, Andrey Kiselev + * Copyright (c) 2008-2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "ogr_p.h" +#include "cpl_conv.h" +#include "cpl_csv.h" + +CPL_CVSID("$Id: ogr_srs_usgs.cpp 28565 2015-02-27 10:26:21Z rouault $"); + +/************************************************************************/ +/* GCTP projection codes. */ +/************************************************************************/ + +#define GEO 0L // Geographic +#define UTM 1L // Universal Transverse Mercator (UTM) +#define SPCS 2L // State Plane Coordinates +#define ALBERS 3L // Albers Conical Equal Area +#define LAMCC 4L // Lambert Conformal Conic +#define MERCAT 5L // Mercator +#define PS 6L // Polar Stereographic +#define POLYC 7L // Polyconic +#define EQUIDC 8L // Equidistant Conic +#define TM 9L // Transverse Mercator +#define STEREO 10L // Stereographic +#define LAMAZ 11L // Lambert Azimuthal Equal Area +#define AZMEQD 12L // Azimuthal Equidistant +#define GNOMON 13L // Gnomonic +#define ORTHO 14L // Orthographic +#define GVNSP 15L // General Vertical Near-Side Perspective +#define SNSOID 16L // Sinusiodal +#define EQRECT 17L // Equirectangular +#define MILLER 18L // Miller Cylindrical +#define VGRINT 19L // Van der Grinten +#define HOM 20L // (Hotine) Oblique Mercator +#define ROBIN 21L // Robinson +#define SOM 22L // Space Oblique Mercator (SOM) +#define ALASKA 23L // Alaska Conformal +#define GOODE 24L // Interrupted Goode Homolosine +#define MOLL 25L // Mollweide +#define IMOLL 26L // Interrupted Mollweide +#define HAMMER 27L // Hammer +#define WAGIV 28L // Wagner IV +#define WAGVII 29L // Wagner VII +#define OBEQA 30L // Oblated Equal Area +#define ISINUS1 31L // Integerized Sinusoidal Grid (the same as 99) +#define CEA 97L // Cylindrical Equal Area (Grid corners set + // in meters for EASE grid) +#define BCEA 98L // Cylindrical Equal Area (Grid corners set + // in DMS degs for EASE grid) +#define ISINUS 99L // Integerized Sinusoidal Grid + // (added by Raj Gejjagaraguppe ARC for MODIS) + +/************************************************************************/ +/* GCTP ellipsoid codes. */ +/************************************************************************/ + +#define CLARKE1866 0L +#define CLARKE1880 1L +#define BESSEL 2L +#define INTERNATIONAL1967 3L +#define INTERNATIONAL1909 4L +#define WGS72 5L +#define EVEREST 6L +#define WGS66 7L +#define GRS1980 8L +#define AIRY 9L +#define MODIFIED_EVEREST 10L +#define MODIFIED_AIRY 11L +#define WGS84 12L +#define SOUTHEAST_ASIA 13L +#define AUSTRALIAN_NATIONAL 14L +#define KRASSOVSKY 15L +#define HOUGH 16L +#define MERCURY1960 17L +#define MODIFIED_MERCURY 18L +#define SPHERE 19L + +/************************************************************************/ +/* Correspondence between GCTP and EPSG ellipsoid codes. */ +/************************************************************************/ + +static const long aoEllips[] = +{ + 7008, // Clarke, 1866 (NAD1927) + 7034, // Clarke, 1880 + 7004, // Bessel, 1841 + 0,// FIXME: New International, 1967 --- skipped + 7022, // International, 1924 (Hayford, 1909) XXX? + 7043, // WGS, 1972 + 7042, // Everest, 1830 + 7025, // FIXME: WGS, 1966 + 7019, // GRS, 1980 (NAD1983) + 7001, // Airy, 1830 + 7018, // Modified Everest + 7002, // Modified Airy + 7030, // WGS, 1984 (GPS) + 0,// FIXME: Southeast Asia --- skipped + 7003, // Australian National, 1965 + 7024, // Krassovsky, 1940 + 7053, // Hough + 0,// FIXME: Mercury, 1960 --- skipped + 0,// FIXME: Modified Mercury, 1968 --- skipped + 7047, // Sphere, rad 6370997 m (normal sphere) + 7006, // Bessel, 1841 (Namibia) + 7016, // Everest (Sabah & Sarawak) + 7044, // Everest, 1956 + 7056, // Everest, Malaysia 1969 + 7018, // Everest, Malay & Singapr 1948 + 0,// FIXME: Everest, Pakistan --- skipped + 7022, // Hayford (International 1924) XXX? + 7020, // Helmert 1906 + 7021, // Indonesian, 1974 + 7036, // South American, 1969 + 0// FIXME: WGS 60 --- skipped +}; + +#define NUMBER_OF_ELLIPSOIDS (int)(sizeof(aoEllips)/sizeof(aoEllips[0])) + +/************************************************************************/ +/* OSRImportFromUSGS() */ +/************************************************************************/ + +/** + * \brief Import coordinate system from USGS projection definition. + * + * This function is the same as OGRSpatialReference::importFromUSGS(). + */ +OGRErr OSRImportFromUSGS( OGRSpatialReferenceH hSRS, long iProjsys, + long iZone, double *padfPrjParams, long iDatum ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromUSGS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromUSGS( iProjsys, iZone, + padfPrjParams, + iDatum ); +} + +static double OGRSpatialReferenceUSGSUnpackNoOp(double dfVal) +{ + return dfVal; +} + +static double OGRSpatialReferenceUSGSUnpackRadian(double dfVal) +{ + return (dfVal * 180.0 / M_PI); +} + +/************************************************************************/ +/* importFromUSGS() */ +/************************************************************************/ + +/** + * \brief Import coordinate system from USGS projection definition. + * + * This method will import projection definition in style, used by USGS GCTP + * software. GCTP operates on angles in packed DMS format (see + * CPLDecToPackedDMS() function for details), so all angle values (latitudes, + * longitudes, azimuths, etc.) specified in the padfPrjParams array should + * be in the packed DMS format, unless bAnglesInPackedDMSFormat is set to FALSE. + * + * This function is the equivalent of the C function OSRImportFromUSGS(). + * Note that the bAnglesInPackedDMSFormat parameter is only present in the C++ + * method. The C function assumes bAnglesInPackedFormat = TRUE. + * + * @param iProjSys Input projection system code, used in GCTP. + * + * @param iZone Input zone for UTM and State Plane projection systems. For + * Southern Hemisphere UTM use a negative zone code. iZone ignored for all + * other projections. + * + * @param padfPrjParams Array of 15 coordinate system parameters. These + * parameters differs for different projections. + * + *

    Projection Transformation Package Projection Parameters

    + *
    + * ----------------------------------------------------------------------------
    + *                         |                    Array Element                  
    + *  Code & Projection Id   |---------------------------------------------------
    + *                         |   0  |   1  |  2   |  3   |   4   |    5    |6 | 7
    + * ----------------------------------------------------------------------------
    + *  0 Geographic           |      |      |      |      |       |         |  |  
    + *  1 U T M                |Lon/Z |Lat/Z |      |      |       |         |  |  
    + *  2 State Plane          |      |      |      |      |       |         |  |  
    + *  3 Albers Equal Area    |SMajor|SMinor|STDPR1|STDPR2|CentMer|OriginLat|FE|FN
    + *  4 Lambert Conformal C  |SMajor|SMinor|STDPR1|STDPR2|CentMer|OriginLat|FE|FN
    + *  5 Mercator             |SMajor|SMinor|      |      |CentMer|TrueScale|FE|FN
    + *  6 Polar Stereographic  |SMajor|SMinor|      |      |LongPol|TrueScale|FE|FN
    + *  7 Polyconic            |SMajor|SMinor|      |      |CentMer|OriginLat|FE|FN
    + *  8 Equid. Conic A       |SMajor|SMinor|STDPAR|      |CentMer|OriginLat|FE|FN
    + *    Equid. Conic B       |SMajor|SMinor|STDPR1|STDPR2|CentMer|OriginLat|FE|FN
    + *  9 Transverse Mercator  |SMajor|SMinor|Factor|      |CentMer|OriginLat|FE|FN
    + * 10 Stereographic        |Sphere|      |      |      |CentLon|CenterLat|FE|FN
    + * 11 Lambert Azimuthal    |Sphere|      |      |      |CentLon|CenterLat|FE|FN
    + * 12 Azimuthal            |Sphere|      |      |      |CentLon|CenterLat|FE|FN
    + * 13 Gnomonic             |Sphere|      |      |      |CentLon|CenterLat|FE|FN
    + * 14 Orthographic         |Sphere|      |      |      |CentLon|CenterLat|FE|FN
    + * 15 Gen. Vert. Near Per  |Sphere|      |Height|      |CentLon|CenterLat|FE|FN
    + * 16 Sinusoidal           |Sphere|      |      |      |CentMer|         |FE|FN
    + * 17 Equirectangular      |Sphere|      |      |      |CentMer|TrueScale|FE|FN
    + * 18 Miller Cylindrical   |Sphere|      |      |      |CentMer|         |FE|FN
    + * 19 Van der Grinten      |Sphere|      |      |      |CentMer|OriginLat|FE|FN
    + * 20 Hotin Oblique Merc A |SMajor|SMinor|Factor|      |       |OriginLat|FE|FN
    + *    Hotin Oblique Merc B |SMajor|SMinor|Factor|AziAng|AzmthPt|OriginLat|FE|FN
    + * 21 Robinson             |Sphere|      |      |      |CentMer|         |FE|FN
    + * 22 Space Oblique Merc A |SMajor|SMinor|      |IncAng|AscLong|         |FE|FN
    + *    Space Oblique Merc B |SMajor|SMinor|Satnum|Path  |       |         |FE|FN
    + * 23 Alaska Conformal     |SMajor|SMinor|      |      |       |         |FE|FN
    + * 24 Interrupted Goode    |Sphere|      |      |      |       |         |  |  
    + * 25 Mollweide            |Sphere|      |      |      |CentMer|         |FE|FN
    + * 26 Interrupt Mollweide  |Sphere|      |      |      |       |         |  |  
    + * 27 Hammer               |Sphere|      |      |      |CentMer|         |FE|FN
    + * 28 Wagner IV            |Sphere|      |      |      |CentMer|         |FE|FN
    + * 29 Wagner VII           |Sphere|      |      |      |CentMer|         |FE|FN
    + * 30 Oblated Equal Area   |Sphere|      |Shapem|Shapen|CentLon|CenterLat|FE|FN
    + * ----------------------------------------------------------------------------
    + * 
    + *       ----------------------------------------------------
    + *                               |      Array Element       |
    + *         Code & Projection Id  |---------------------------
    + *                               |  8  |  9 |  10 | 11 | 12 |  
    + *       ----------------------------------------------------
    + *        0 Geographic           |     |    |     |    |    |
    + *        1 U T M                |     |    |     |    |    |
    + *        2 State Plane          |     |    |     |    |    |
    + *        3 Albers Equal Area    |     |    |     |    |    |
    + *        4 Lambert Conformal C  |     |    |     |    |    |
    + *        5 Mercator             |     |    |     |    |    |
    + *        6 Polar Stereographic  |     |    |     |    |    |
    + *        7 Polyconic            |     |    |     |    |    |
    + *        8 Equid. Conic A       |zero |    |     |    |    |   
    + *          Equid. Conic B       |one  |    |     |    |    |
    + *        9 Transverse Mercator  |     |    |     |    |    |
    + *       10 Stereographic        |     |    |     |    |    |
    + *       11 Lambert Azimuthal    |     |    |     |    |    |    
    + *       12 Azimuthal            |     |    |     |    |    |    
    + *       13 Gnomonic             |     |    |     |    |    |
    + *       14 Orthographic         |     |    |     |    |    |
    + *       15 Gen. Vert. Near Per  |     |    |     |    |    |
    + *       16 Sinusoidal           |     |    |     |    |    |
    + *       17 Equirectangular      |     |    |     |    |    |
    + *       18 Miller Cylindrical   |     |    |     |    |    |
    + *       19 Van der Grinten      |     |    |     |    |    |
    + *       20 Hotin Oblique Merc A |Long1|Lat1|Long2|Lat2|zero|   
    + *          Hotin Oblique Merc B |     |    |     |    |one |
    + *       21 Robinson             |     |    |     |    |    |
    + *       22 Space Oblique Merc A |PSRev|LRat|PFlag|    |zero|    
    + *          Space Oblique Merc B |     |    |     |    |one |
    + *       23 Alaska Conformal     |     |    |     |    |    |
    + *       24 Interrupted Goode    |     |    |     |    |    |
    + *       25 Mollweide            |     |    |     |    |    |
    + *       26 Interrupt Mollweide  |     |    |     |    |    |
    + *       27 Hammer               |     |    |     |    |    |
    + *       28 Wagner IV            |     |    |     |    |    |
    + *       29 Wagner VII           |     |    |     |    |    |
    + *       30 Oblated Equal Area   |Angle|    |     |    |    |
    + *       ----------------------------------------------------
    + *
    + *   where
    + *
    + *    Lon/Z     Longitude of any point in the UTM zone or zero.  If zero,
    + *              a zone code must be specified.
    + *    Lat/Z     Latitude of any point in the UTM zone or zero.  If zero, a
    + *              zone code must be specified.
    + *    SMajor    Semi-major axis of ellipsoid.  If zero, Clarke 1866 in meters
    + *              is assumed.
    + *    SMinor    Eccentricity squared of the ellipsoid if less than zero,
    + *              if zero, a spherical form is assumed, or if greater than
    + *              zero, the semi-minor axis of ellipsoid.
    + *    Sphere    Radius of reference sphere.  If zero, 6370997 meters is used.
    + *    STDPAR    Latitude of the standard parallel
    + *    STDPR1    Latitude of the first standard parallel
    + *    STDPR2    Latitude of the second standard parallel
    + *    CentMer   Longitude of the central meridian
    + *    OriginLat Latitude of the projection origin
    + *    FE        False easting in the same units as the semi-major axis
    + *    FN        False northing in the same units as the semi-major axis
    + *    TrueScale Latitude of true scale
    + *    LongPol   Longitude down below pole of map
    + *    Factor    Scale factor at central meridian (Transverse Mercator) or
    + *              center of projection (Hotine Oblique Mercator)
    + *    CentLon   Longitude of center of projection
    + *    CenterLat Latitude of center of projection
    + *    Height    Height of perspective point
    + *    Long1     Longitude of first point on center line (Hotine Oblique
    + *              Mercator, format A)
    + *    Long2     Longitude of second point on center line (Hotine Oblique
    + *              Mercator, format A)
    + *    Lat1      Latitude of first point on center line (Hotine Oblique
    + *              Mercator, format A)
    + *    Lat2      Latitude of second point on center line (Hotine Oblique
    + *              Mercator, format A)
    + *    AziAng    Azimuth angle east of north of center line (Hotine Oblique
    + *              Mercator, format B)
    + *    AzmthPt   Longitude of point on central meridian where azimuth occurs
    + *              (Hotine Oblique Mercator, format B)
    + *    IncAng    Inclination of orbit at ascending node, counter-clockwise
    + *              from equator (SOM, format A)
    + *    AscLong   Longitude of ascending orbit at equator (SOM, format A)
    + *    PSRev     Period of satellite revolution in minutes (SOM, format A)
    + *    LRat      Landsat ratio to compensate for confusion at northern end
    + *              of orbit (SOM, format A -- use 0.5201613)
    + *    PFlag     End of path flag for Landsat:  0 = start of path,
    + *              1 = end of path (SOM, format A)
    + *    Satnum    Landsat Satellite Number (SOM, format B)
    + *    Path      Landsat Path Number (Use WRS-1 for Landsat 1, 2 and 3 and
    + *              WRS-2 for Landsat 4, 5 and 6.)  (SOM, format B)
    + *    Shapem    Oblated Equal Area oval shape parameter m
    + *    Shapen    Oblated Equal Area oval shape parameter n
    + *    Angle     Oblated Equal Area oval rotation angle
    + *
    + * Array elements 13 and 14 are set to zero. All array elements with blank
    + * fields are set to zero too.
    + * 
    + * + * @param iDatum Input spheroid.

    + * + * If the datum code is negative, the first two values in the parameter array + * (parm) are used to define the values as follows: + * + *

      + * + *
    • If padfPrjParams[0] is a non-zero value and padfPrjParams[1] is + * greater than one, the semimajor axis is set to padfPrjParams[0] and + * the semiminor axis is set to padfPrjParams[1]. + * + *
    • If padfPrjParams[0] is nonzero and padfPrjParams[1] is greater than + * zero but less than or equal to one, the semimajor axis is set to + * padfPrjParams[0] and the semiminor axis is computed from the eccentricity + * squared value padfPrjParams[1]:

      + * + * semiminor = sqrt(1.0 - ES) * semimajor

      + * + * where

      + * + * ES = eccentricity squared + * + *

    • If padfPrjParams[0] is nonzero and padfPrjParams[1] is equal to zero, + * the semimajor axis and semiminor axis are set to padfPrjParams[0]. + * + *
    • If padfPrjParams[0] equals zero and padfPrjParams[1] is greater than + * zero, the default Clarke 1866 is used to assign values to the semimajor + * axis and semiminor axis. + * + *
    • If padfPrjParams[0] and padfPrjParams[1] equals zero, the semimajor + * axis is set to 6370997.0 and the semiminor axis is set to zero. + * + *
    + * + * If a datum code is zero or greater, the semimajor and semiminor axis are + * defined by the datum code as found in the following table: + * + *

    Supported Datums

    + *
    + *       0: Clarke 1866 (default)
    + *       1: Clarke 1880
    + *       2: Bessel
    + *       3: International 1967
    + *       4: International 1909
    + *       5: WGS 72
    + *       6: Everest
    + *       7: WGS 66
    + *       8: GRS 1980/WGS 84
    + *       9: Airy
    + *      10: Modified Everest
    + *      11: Modified Airy
    + *      12: Walbeck
    + *      13: Southeast Asia
    + *      14: Australian National
    + *      15: Krassovsky
    + *      16: Hough
    + *      17: Mercury 1960
    + *      18: Modified Mercury 1968
    + *      19: Sphere of Radius 6370997 meters
    + * 
    + * + * @param nUSGSAngleFormat one of USGS_ANGLE_DECIMALDEGREES, USGS_ANGLE_PACKEDDMS, or USGS_ANGLE_RADIANS (default is USGS_ANGLE_PACKEDDMS). + * + * @return OGRERR_NONE on success or an error code in case of failure. + */ + +OGRErr OGRSpatialReference::importFromUSGS( long iProjSys, long iZone, + double *padfPrjParams, + long iDatum, + int nUSGSAngleFormat ) + +{ + if( !padfPrjParams ) + return OGRERR_CORRUPT_DATA; + + double (*pfnUnpackAnglesFn)(double) = NULL; + + if (nUSGSAngleFormat == USGS_ANGLE_DECIMALDEGREES ) + pfnUnpackAnglesFn = OGRSpatialReferenceUSGSUnpackNoOp; + else if (nUSGSAngleFormat == USGS_ANGLE_RADIANS ) + pfnUnpackAnglesFn = OGRSpatialReferenceUSGSUnpackRadian; + else + pfnUnpackAnglesFn = CPLPackedDMSToDec; + +/* -------------------------------------------------------------------- */ +/* Operate on the basis of the projection code. */ +/* -------------------------------------------------------------------- */ + switch ( iProjSys ) + { + case GEO: + break; + + case UTM: + { + int bNorth = TRUE; + + if ( !iZone ) + { + if ( padfPrjParams[2] != 0.0 ) + iZone = (long) padfPrjParams[2]; + else if (padfPrjParams[0] != 0.0 && padfPrjParams[1] != 0.0) + { + iZone = (long)(((pfnUnpackAnglesFn(padfPrjParams[0]) + + 180.0) / 6.0) + 1.0); + if ( pfnUnpackAnglesFn(padfPrjParams[0]) < 0 ) + bNorth = FALSE; + } + } + + if ( iZone < 0 ) + { + iZone = -iZone; + bNorth = FALSE; + } + SetUTM( iZone, bNorth ); + } + break; + + case SPCS: + { + int bNAD83 = TRUE; + + if ( iDatum == 0 ) + bNAD83 = FALSE; + else if ( iDatum != 8 ) + CPLError( CE_Warning, CPLE_AppDefined, + "Wrong datum for State Plane projection %d. " + "Should be 0 or 8.", (int) iDatum ); + + SetStatePlane( iZone, bNAD83 ); + } + break; + + case ALBERS: + SetACEA( pfnUnpackAnglesFn(padfPrjParams[2]), + pfnUnpackAnglesFn(padfPrjParams[3]), + pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + case LAMCC: + SetLCC( pfnUnpackAnglesFn(padfPrjParams[2]), + pfnUnpackAnglesFn(padfPrjParams[3]), + pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + case MERCAT: + SetMercator( pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + 1.0, + padfPrjParams[6], padfPrjParams[7] ); + break; + + case PS: + SetPS( pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + 1.0, + padfPrjParams[6], padfPrjParams[7] ); + + break; + + case POLYC: + SetPolyconic( pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + case EQUIDC: + if ( padfPrjParams[8] ) + { + SetEC( pfnUnpackAnglesFn(padfPrjParams[2]), + pfnUnpackAnglesFn(padfPrjParams[3]), + pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + } + else + { + SetEC( pfnUnpackAnglesFn(padfPrjParams[2]), + pfnUnpackAnglesFn(padfPrjParams[2]), + pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + } + break; + + case TM: + SetTM( pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + break; + + case STEREO: + SetStereographic( pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + 1.0, + padfPrjParams[6], padfPrjParams[7] ); + break; + + case LAMAZ: + SetLAEA( pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + case AZMEQD: + SetAE( pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + case GNOMON: + SetGnomonic( pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + case ORTHO: + SetOrthographic( pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + // FIXME: GVNSP --- General Vertical Near-Side Perspective skipped + + case SNSOID: + SetSinusoidal( pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + case EQRECT: + SetEquirectangular2( 0.0, + pfnUnpackAnglesFn(padfPrjParams[4]), + pfnUnpackAnglesFn(padfPrjParams[5]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + case MILLER: + SetMC( pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + case VGRINT: + SetVDG( pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + case HOM: + if ( padfPrjParams[12] ) + { + SetHOM( pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[4]), + pfnUnpackAnglesFn(padfPrjParams[3]), + 0.0, padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + else + { + SetHOM2PNO( pfnUnpackAnglesFn(padfPrjParams[5]), + pfnUnpackAnglesFn(padfPrjParams[9]), + pfnUnpackAnglesFn(padfPrjParams[8]), + pfnUnpackAnglesFn(padfPrjParams[11]), + pfnUnpackAnglesFn(padfPrjParams[10]), + padfPrjParams[2], + padfPrjParams[6], padfPrjParams[7] ); + } + break; + + case ROBIN: + SetRobinson( pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + // FIXME: SOM --- Space Oblique Mercator skipped + + // FIXME: ALASKA --- Alaska Conformal skipped + + // FIXME: GOODE --- Interrupted Goode skipped + + case MOLL: + SetMollweide( pfnUnpackAnglesFn(padfPrjParams[4]), + padfPrjParams[6], padfPrjParams[7] ); + break; + + // FIXME: IMOLL --- Interrupted Mollweide skipped + + // FIXME: HAMMER --- Hammer skipped + + case WAGIV: + SetWagner( 4, 0.0, padfPrjParams[6], padfPrjParams[7] ); + break; + + case WAGVII: + SetWagner( 7, 0.0, padfPrjParams[6], padfPrjParams[7] ); + break; + + // FIXME: OBEQA --- Oblated Equal Area skipped + + // FIXME: ISINUS1 --- Integerized Sinusoidal Grid (the same as 99) skipped + + // FIXME: CEA --- Cylindrical Equal Area skipped (Grid corners set in meters for EASE grid) + + // FIXME: BCEA --- Cylindrical Equal Area skipped (Grid corners set in DMS degs for EASE grid) + + // FIXME: ISINUS --- Integrized Sinusoidal skipped + + default: + CPLDebug( "OSR_USGS", "Unsupported projection: %ld", iProjSys ); + SetLocalCS( CPLString().Printf("GCTP projection number %ld", iProjSys) ); + break; + + } + +/* -------------------------------------------------------------------- */ +/* Try to translate the datum/spheroid. */ +/* -------------------------------------------------------------------- */ + + if ( !IsLocal() ) + { + char *pszName = NULL; + double dfSemiMajor, dfInvFlattening; + + if ( iDatum < 0 ) // Use specified ellipsoid parameters + { + if ( padfPrjParams[0] > 0.0 ) + { + if ( padfPrjParams[1] > 1.0 ) + { + dfInvFlattening = OSRCalcInvFlattening(padfPrjParams[0], padfPrjParams[1] ); + } + else if ( padfPrjParams[1] > 0.0 ) + { + dfInvFlattening = + 1.0 / ( 1.0 - sqrt(1.0 - padfPrjParams[1]) ); + } + else + dfInvFlattening = 0.0; + + SetGeogCS( "Unknown datum based upon the custom spheroid", + "Not specified (based on custom spheroid)", + "Custom spheroid", padfPrjParams[0], dfInvFlattening, + NULL, 0, NULL, 0 ); + } + else if ( padfPrjParams[1] > 0.0 ) // Clarke 1866 + { + if ( OSRGetEllipsoidInfo( 7008, &pszName, &dfSemiMajor, + &dfInvFlattening ) == OGRERR_NONE ) + { + SetGeogCS( CPLString().Printf( + "Unknown datum based upon the %s ellipsoid", + pszName ), + CPLString().Printf( + "Not specified (based on %s spheroid)", + pszName ), + pszName, dfSemiMajor, dfInvFlattening, + NULL, 0.0, NULL, 0.0 ); + SetAuthority( "SPHEROID", "EPSG", 7008 ); + } + } + else // Sphere, rad 6370997 m + { + if ( OSRGetEllipsoidInfo( 7047, &pszName, &dfSemiMajor, + &dfInvFlattening ) == OGRERR_NONE ) + { + SetGeogCS( CPLString().Printf( + "Unknown datum based upon the %s ellipsoid", + pszName ), + CPLString().Printf( + "Not specified (based on %s spheroid)", + pszName ), + pszName, dfSemiMajor, dfInvFlattening, + NULL, 0.0, NULL, 0.0 ); + SetAuthority( "SPHEROID", "EPSG", 7047 ); + } + } + + } + else if ( iDatum < NUMBER_OF_ELLIPSOIDS && aoEllips[iDatum] ) + { + if( OSRGetEllipsoidInfo( aoEllips[iDatum], &pszName, + &dfSemiMajor, &dfInvFlattening ) == OGRERR_NONE ) + { + SetGeogCS( CPLString().Printf("Unknown datum based upon the %s ellipsoid", + pszName ), + CPLString().Printf( "Not specified (based on %s spheroid)", + pszName ), + pszName, dfSemiMajor, dfInvFlattening, + NULL, 0.0, NULL, 0.0 ); + SetAuthority( "SPHEROID", "EPSG", aoEllips[iDatum] ); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to lookup datum code %d, likely due to missing GDAL gcs.csv\n" + " file. Falling back to use WGS84.", + (int) iDatum ); + SetWellKnownGeogCS("WGS84" ); + } + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Wrong datum code %d. Supported datums 0--%d only.\n" + "Setting WGS84 as a fallback.", + (int) iDatum, NUMBER_OF_ELLIPSOIDS ); + SetWellKnownGeogCS( "WGS84" ); + } + + if ( pszName ) + CPLFree( pszName ); + } + +/* -------------------------------------------------------------------- */ +/* Grid units translation */ +/* -------------------------------------------------------------------- */ + if( IsLocal() || IsProjected() ) + SetLinearUnits( SRS_UL_METER, 1.0 ); + + FixupOrdering(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRExportToUSGS() */ +/************************************************************************/ +/** + * \brief Export coordinate system in USGS GCTP projection definition. + * + * This function is the same as OGRSpatialReference::exportToUSGS(). + */ + +OGRErr OSRExportToUSGS( OGRSpatialReferenceH hSRS, + long *piProjSys, long *piZone, + double **ppadfPrjParams, long *piDatum ) + +{ + VALIDATE_POINTER1( hSRS, "OSRExportToUSGS", CE_Failure ); + + *ppadfPrjParams = NULL; + + return ((OGRSpatialReference *) hSRS)->exportToUSGS( piProjSys, piZone, + ppadfPrjParams, + piDatum ); +} + +/************************************************************************/ +/* exportToUSGS() */ +/************************************************************************/ + +/** + * \brief Export coordinate system in USGS GCTP projection definition. + * + * This method is the equivalent of the C function OSRExportToUSGS(). + * + * @param piProjSys Pointer to variable, where the projection system code will + * be returned. + * + * @param piZone Pointer to variable, where the zone for UTM and State Plane + * projection systems will be returned. + * + * @param ppadfPrjParams Pointer to which dynamically allocated array of + * 15 projection parameters will be assigned. See importFromUSGS() for + * the list of parameters. Caller responsible to free this array. + * + * @param piDatum Pointer to variable, where the datum code will + * be returned. + * + * @return OGRERR_NONE on success or an error code on failure. + */ + +OGRErr OGRSpatialReference::exportToUSGS( long *piProjSys, long *piZone, + double **ppadfPrjParams, + long *piDatum ) const + +{ + const char *pszProjection = GetAttrValue("PROJECTION"); + +/* -------------------------------------------------------------------- */ +/* Fill all projection parameters with zero. */ +/* -------------------------------------------------------------------- */ + int i; + + *ppadfPrjParams = (double *)CPLMalloc( 15 * sizeof(double) ); + for ( i = 0; i < 15; i++ ) + (*ppadfPrjParams)[i] = 0.0; + + *piZone = 0L; + +/* ==================================================================== */ +/* Handle the projection definition. */ +/* ==================================================================== */ + if( IsLocal() ) + *piProjSys = GEO; + + else if( pszProjection == NULL ) + { +#ifdef DEBUG + CPLDebug( "OSR_USGS", + "Empty projection definition, considered as Geographic" ); +#endif + *piProjSys = GEO; + } + + else if( EQUAL(pszProjection, SRS_PT_ALBERS_CONIC_EQUAL_AREA) ) + { + *piProjSys = ALBERS; + (*ppadfPrjParams)[2] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) ); + (*ppadfPrjParams)[3] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) ); + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ) + { + *piProjSys = LAMCC; + (*ppadfPrjParams)[2] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) ); + (*ppadfPrjParams)[3] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) ); + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_MERCATOR_1SP) ) + { + *piProjSys = MERCAT; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC) ) + { + *piProjSys = PS; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_POLYCONIC) ) + { + *piProjSys = POLYC; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_EQUIDISTANT_CONIC) ) + { + *piProjSys = EQUIDC; + (*ppadfPrjParams)[2] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) ); + (*ppadfPrjParams)[3] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) ); + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + (*ppadfPrjParams)[8] = 1.0; + } + + else if( EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR) ) + { + int bNorth; + + *piZone = GetUTMZone( &bNorth ); + + if( *piZone != 0 ) + { + *piProjSys = UTM; + if( !bNorth ) + *piZone = - *piZone; + } + else + { + *piProjSys = TM; + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ); + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + (*ppadfPrjParams)[6] = + GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = + GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + } + + else if( EQUAL(pszProjection, SRS_PT_STEREOGRAPHIC) ) + { + *piProjSys = STEREO; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) + { + *piProjSys = LAMAZ; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_AZIMUTHAL_EQUIDISTANT) ) + { + *piProjSys = AZMEQD; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_GNOMONIC) ) + { + *piProjSys = GNOMON; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC) ) + { + *piProjSys = ORTHO; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_SINUSOIDAL) ) + { + *piProjSys = SNSOID; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR) ) + { + *piProjSys = EQRECT; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_MILLER_CYLINDRICAL) ) + { + *piProjSys = MILLER; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_VANDERGRINTEN) ) + { + *piProjSys = VGRINT; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_HOTINE_OBLIQUE_MERCATOR) ) + { + *piProjSys = HOM; + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ); + (*ppadfPrjParams)[3] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_AZIMUTH, 0.0 ) ); + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + (*ppadfPrjParams)[12] = 1.0; + } + + else if( EQUAL(pszProjection, + SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN) ) + { + *piProjSys = HOM; + (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 ); + (*ppadfPrjParams)[5] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + (*ppadfPrjParams)[8] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LONGITUDE_OF_POINT_1, 0.0 ) ); + (*ppadfPrjParams)[9] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_POINT_1, 0.0 ) ); + (*ppadfPrjParams)[10] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LONGITUDE_OF_POINT_2, 0.0 ) ); + (*ppadfPrjParams)[11] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LATITUDE_OF_POINT_2, 0.0 ) ); + (*ppadfPrjParams)[12] = 0.0; + } + + else if( EQUAL(pszProjection, SRS_PT_ROBINSON) ) + { + *piProjSys = ROBIN; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_MOLLWEIDE) ) + { + *piProjSys = MOLL; + (*ppadfPrjParams)[4] = CPLDecToPackedDMS( + GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) ); + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_WAGNER_IV) ) + { + *piProjSys = WAGIV; + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + else if( EQUAL(pszProjection, SRS_PT_WAGNER_VII) ) + { + *piProjSys = WAGVII; + (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 ); + (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 ); + } + + // Projection unsupported by GCTP + else + { + CPLDebug( "OSR_USGS", + "Projection \"%s\" unsupported by USGS GCTP. " + "Geographic system will be used.", pszProjection ); + *piProjSys = GEO; + } + +/* -------------------------------------------------------------------- */ +/* Translate the datum. */ +/* -------------------------------------------------------------------- */ + const char *pszDatum = GetAttrValue( "DATUM" ); + + if ( pszDatum ) + { + if( EQUAL( pszDatum, SRS_DN_NAD27 ) ) + *piDatum = CLARKE1866; + + else if( EQUAL( pszDatum, SRS_DN_NAD83 ) ) + *piDatum = GRS1980; + + else if( EQUAL( pszDatum, SRS_DN_WGS84 ) ) + *piDatum = WGS84; + + // If not found well known datum, translate ellipsoid + else + { + double dfSemiMajor = GetSemiMajor(); + double dfInvFlattening = GetInvFlattening(); + +#ifdef DEBUG + CPLDebug( "OSR_USGS", + "Datum \"%s\" unsupported by USGS GCTP. " + "Try to translate ellipsoid definition.", pszDatum ); +#endif + + for ( i = 0; i < NUMBER_OF_ELLIPSOIDS; i++ ) + { + double dfSM; + double dfIF; + + if ( OSRGetEllipsoidInfo( aoEllips[i], NULL, + &dfSM, &dfIF ) == OGRERR_NONE + && CPLIsEqual( dfSemiMajor, dfSM ) + && CPLIsEqual( dfInvFlattening, dfIF ) ) + { + *piDatum = i; + break; + } + } + + if ( i == NUMBER_OF_ELLIPSOIDS ) // Didn't found matches; set + { // custom ellipsoid parameters +#ifdef DEBUG + CPLDebug( "OSR_USGS", + "Ellipsoid \"%s\" unsupported by USGS GCTP. " + "Custom ellipsoid definition will be used.", + pszDatum ); +#endif + *piDatum = -1; + (*ppadfPrjParams)[0] = dfSemiMajor; + if ( ABS( dfInvFlattening ) < 0.000000000001 ) + { + (*ppadfPrjParams)[1] = dfSemiMajor; + } + else + { + (*ppadfPrjParams)[1] = + dfSemiMajor * (1.0 - 1.0/dfInvFlattening); + } + } + } + } + else + *piDatum = -1; + + return OGRERR_NONE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogr_srs_validate.cpp b/bazaar/plugin/gdal/ogr/ogr_srs_validate.cpp new file mode 100644 index 000000000..17dbada2d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srs_validate.cpp @@ -0,0 +1,1458 @@ +/****************************************************************************** + * $Id: ogr_srs_validate.cpp 27975 2014-11-17 12:37:48Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implementation of the OGRSpatialReference::Validate() method and + * related infrastructure. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "ogr_p.h" +#include "osr_cs_wkt.h" + +CPL_CVSID("$Id: ogr_srs_validate.cpp 27975 2014-11-17 12:37:48Z rouault $"); + +/* why would fipszone and zone be paramers when they relate to a composite + projection which renders done into a non-zoned projection? */ + +static const char *papszParameters[] = +{ + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_ORIGIN, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + SRS_PP_AZIMUTH, + SRS_PP_LONGITUDE_OF_POINT_1, + SRS_PP_LATITUDE_OF_POINT_1, + SRS_PP_LONGITUDE_OF_POINT_2, + SRS_PP_LATITUDE_OF_POINT_2, + SRS_PP_LONGITUDE_OF_POINT_3, + SRS_PP_LATITUDE_OF_POINT_3, + SRS_PP_LANDSAT_NUMBER, + SRS_PP_PATH_NUMBER, + SRS_PP_PERSPECTIVE_POINT_HEIGHT, + SRS_PP_FIPSZONE, + SRS_PP_ZONE, + SRS_PP_RECTIFIED_GRID_ANGLE, + SRS_PP_SATELLITE_HEIGHT, + SRS_PP_PSEUDO_STD_PARALLEL_1, + SRS_PP_LATITUDE_OF_1ST_POINT, + SRS_PP_LONGITUDE_OF_1ST_POINT, + SRS_PP_LATITUDE_OF_2ND_POINT, + SRS_PP_LONGITUDE_OF_2ND_POINT, + NULL +}; + +// the following projection lists are incomplete. they will likely +// change after the CT RPF response. Examples show alternate forms with +// underscores instead of spaces. Should we use the EPSG names were available? +// Plate-Caree has an accent in the spec! + +static const char *papszProjectionSupported[] = +{ + SRS_PT_CASSINI_SOLDNER, + SRS_PT_BONNE, + SRS_PT_EQUIDISTANT_CONIC, + SRS_PT_EQUIRECTANGULAR, + SRS_PT_ECKERT_I, + SRS_PT_ECKERT_II, + SRS_PT_ECKERT_III, + SRS_PT_ECKERT_IV, + SRS_PT_ECKERT_V, + SRS_PT_ECKERT_VI, + SRS_PT_MERCATOR_1SP, + SRS_PT_MERCATOR_2SP, + SRS_PT_MOLLWEIDE, + SRS_PT_ROBINSON, + SRS_PT_ALBERS_CONIC_EQUAL_AREA, + SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP, + SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP, + SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM, + SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA, + SRS_PT_TRANSVERSE_MERCATOR, + SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED, + SRS_PT_OBLIQUE_STEREOGRAPHIC, + SRS_PT_POLAR_STEREOGRAPHIC, + SRS_PT_HOTINE_OBLIQUE_MERCATOR, + SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN, + SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER, + SRS_PT_LABORDE_OBLIQUE_MERCATOR, + SRS_PT_SWISS_OBLIQUE_CYLINDRICAL, + SRS_PT_AZIMUTHAL_EQUIDISTANT, + SRS_PT_MILLER_CYLINDRICAL, + SRS_PT_NEW_ZEALAND_MAP_GRID, + SRS_PT_SINUSOIDAL, + SRS_PT_STEREOGRAPHIC, + SRS_PT_GNOMONIC, + SRS_PT_GALL_STEREOGRAPHIC, + SRS_PT_ORTHOGRAPHIC, + SRS_PT_POLYCONIC, + SRS_PT_VANDERGRINTEN, + SRS_PT_GEOSTATIONARY_SATELLITE, + SRS_PT_TWO_POINT_EQUIDISTANT, + SRS_PT_IMW_POLYCONIC, + SRS_PT_WAGNER_I, + SRS_PT_WAGNER_II, + SRS_PT_WAGNER_III, + SRS_PT_WAGNER_IV, + SRS_PT_WAGNER_V, + SRS_PT_WAGNER_VI, + SRS_PT_WAGNER_VII, + SRS_PT_QSC, + SRS_PT_GAUSSSCHREIBERTMERCATOR, + SRS_PT_KROVAK, + SRS_PT_CYLINDRICAL_EQUAL_AREA, + SRS_PT_GOODE_HOMOLOSINE, + SRS_PT_IGH, + NULL +}; + +static const char *papszProjectionUnsupported[] = +{ + SRS_PT_NEW_ZEALAND_MAP_GRID, + SRS_PT_TUNISIA_MINING_GRID, + NULL +}; + +/* +** List of supported projections with the PARAMETERS[] acceptable for each. +*/ +static const char *papszProjWithParms[] = { + + SRS_PT_TRANSVERSE_MERCATOR, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_TUNISIA_MINING_GRID, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_ALBERS_CONIC_EQUAL_AREA, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_AZIMUTHAL_EQUIDISTANT, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_BONNE, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_CYLINDRICAL_EQUAL_AREA, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_CASSINI_SOLDNER, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_EQUIDISTANT_CONIC, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_ECKERT_I, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_ECKERT_II, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_ECKERT_III, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_ECKERT_IV, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_ECKERT_V, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_ECKERT_VI, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_EQUIRECTANGULAR, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_GALL_STEREOGRAPHIC, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_GNOMONIC, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_HOTINE_OBLIQUE_MERCATOR, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_AZIMUTH, + SRS_PP_RECTIFIED_GRID_ANGLE, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_AZIMUTH, + SRS_PP_RECTIFIED_GRID_ANGLE, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LATITUDE_OF_POINT_1, + SRS_PP_LONGITUDE_OF_POINT_1, + SRS_PP_LATITUDE_OF_POINT_2, + SRS_PP_LONGITUDE_OF_POINT_2 + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_STANDARD_PARALLEL_2, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_MILLER_CYLINDRICAL, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_MERCATOR_1SP, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_MERCATOR_2SP, + SRS_PP_STANDARD_PARALLEL_1, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_MOLLWEIDE, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_NEW_ZEALAND_MAP_GRID, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_ORTHOGRAPHIC, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_POLYCONIC, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_POLAR_STEREOGRAPHIC, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_ROBINSON, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_SINUSOIDAL, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_STEREOGRAPHIC, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_SWISS_OBLIQUE_CYLINDRICAL, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_OBLIQUE_STEREOGRAPHIC, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_VANDERGRINTEN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_GEOSTATIONARY_SATELLITE, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SATELLITE_HEIGHT, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_KROVAK, + SRS_PP_LATITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_AZIMUTH, + SRS_PP_PSEUDO_STD_PARALLEL_1, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_TWO_POINT_EQUIDISTANT, + SRS_PP_LATITUDE_OF_1ST_POINT, + SRS_PP_LONGITUDE_OF_1ST_POINT, + SRS_PP_LATITUDE_OF_2ND_POINT, + SRS_PP_LONGITUDE_OF_2ND_POINT, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_IMW_POLYCONIC, + SRS_PP_LATITUDE_OF_1ST_POINT, + SRS_PP_LATITUDE_OF_2ND_POINT, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_WAGNER_I, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_WAGNER_II, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_WAGNER_III, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_WAGNER_IV, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_WAGNER_V, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_WAGNER_VI, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_WAGNER_VII, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_QSC, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + NULL, + + SRS_PT_GAUSSSCHREIBERTMERCATOR, + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_SCALE_FACTOR, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_GOODE_HOMOLOSINE, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_FALSE_EASTING, + SRS_PP_FALSE_NORTHING, + NULL, + + SRS_PT_IGH, + NULL, + + NULL +}; + +static const char *papszAliasGroupList[] = { + SRS_PP_LATITUDE_OF_ORIGIN, + SRS_PP_LATITUDE_OF_CENTER, + NULL, + SRS_PP_CENTRAL_MERIDIAN, + SRS_PP_LONGITUDE_OF_CENTER, + SRS_PP_LONGITUDE_OF_ORIGIN, + NULL, + NULL +}; + + +/************************************************************************/ +/* Validate() */ +/************************************************************************/ + +/** + * \brief Validate SRS tokens. + * + * This method attempts to verify that the spatial reference system is + * well formed, and consists of known tokens. The validation is not + * comprehensive. + * + * This method is the same as the C function OSRValidate(). + * + * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is + * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed, + * but contains non-standard PROJECTION[] values. + */ + +OGRErr OGRSpatialReference::Validate() + +{ +/* -------------------------------------------------------------------- */ +/* Validate root node. */ +/* -------------------------------------------------------------------- */ + if( poRoot == NULL ) + { + CPLDebug( "OGRSpatialReference::Validate", + "No root pointer.\n" ); + return OGRERR_CORRUPT_DATA; + } + + OGRErr eErr = Validate(poRoot); + + /* Even if hand-validation has succeeded, try a more formal validation */ + /* using the CT spec grammar */ + static int bUseCTGrammar = -1; + if( bUseCTGrammar < 0 ) + bUseCTGrammar = CSLTestBoolean(CPLGetConfigOption("OSR_USE_CT_GRAMMAR", "TRUE")); + + if( eErr == OGRERR_NONE && bUseCTGrammar ) + { + osr_cs_wkt_parse_context sContext; + char* pszWKT = NULL; + + exportToWkt(&pszWKT); + + sContext.pszInput = pszWKT; + sContext.pszLastSuccess = pszWKT; + sContext.pszNext = pszWKT; + sContext.szErrorMsg[0] = '\0'; + + if( osr_cs_wkt_parse(&sContext) != 0 ) + { + CPLDebug( "OGRSpatialReference::Validate", "%s", + sContext.szErrorMsg ); + eErr = OGRERR_CORRUPT_DATA; + } + + CPLFree(pszWKT); + } + return eErr; +} + + +OGRErr OGRSpatialReference::Validate(OGR_SRSNode *poRoot) +{ + if( !EQUAL(poRoot->GetValue(),"GEOGCS") + && !EQUAL(poRoot->GetValue(),"PROJCS") + && !EQUAL(poRoot->GetValue(),"LOCAL_CS") + && !EQUAL(poRoot->GetValue(),"GEOCCS") + && !EQUAL(poRoot->GetValue(),"VERT_CS") + && !EQUAL(poRoot->GetValue(),"COMPD_CS")) + { + CPLDebug( "OGRSpatialReference::Validate", + "Unrecognised root node `%s'\n", + poRoot->GetValue() ); + return OGRERR_CORRUPT_DATA; + } + +/* -------------------------------------------------------------------- */ +/* For a COMPD_CS, validate subparameters and head & tail cs */ +/* -------------------------------------------------------------------- */ + if( EQUAL(poRoot->GetValue(),"COMPD_CS") ) + { + OGR_SRSNode *poNode; + int i; + + for( i = 1; i < poRoot->GetChildCount(); i++ ) + { + poNode = poRoot->GetChild(i); + + if( EQUAL(poNode->GetValue(),"GEOGCS") || + EQUAL(poNode->GetValue(),"PROJCS") || + EQUAL(poNode->GetValue(),"LOCAL_CS") || + EQUAL(poNode->GetValue(),"GEOCCS") || + EQUAL(poNode->GetValue(),"VERT_CS") || + EQUAL(poNode->GetValue(),"COMPD_CS") ) + { + OGRErr eErr = Validate(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else if( EQUAL(poNode->GetValue(),"AUTHORITY") ) + { + OGRErr eErr = ValidateAuthority(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else if( EQUAL(poNode->GetValue(),"EXTENSION") ) + { + // We do not try to control the sub-organization of + // EXTENSION nodes. + } + else + { + CPLDebug( "OGRSpatialReference::Validate", + "Unexpected child for COMPD_CS `%s'.\n", + poNode->GetValue() ); + + return OGRERR_CORRUPT_DATA; + } + } + + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Validate VERT_CS */ +/* -------------------------------------------------------------------- */ + if( EQUAL(poRoot->GetValue(),"VERT_CS") ) + { + OGR_SRSNode *poNode; + int i; + int bGotVertDatum = FALSE; + int bGotUnit = FALSE; + int nCountAxis = 0; + + for( i = 1; i < poRoot->GetChildCount(); i++ ) + { + poNode = poRoot->GetChild(i); + + if( EQUAL(poNode->GetValue(),"VERT_DATUM") ) + { + OGRErr eErr = ValidateVertDatum(poNode); + if (eErr != OGRERR_NONE) + return eErr; + bGotVertDatum = TRUE; + } + else if( EQUAL(poNode->GetValue(),"UNIT") ) + { + OGRErr eErr = ValidateUnit(poNode); + if (eErr != OGRERR_NONE) + return eErr; + bGotUnit = TRUE; + } + else if( EQUAL(poNode->GetValue(),"AXIS") ) + { + OGRErr eErr = ValidateAxis(poNode); + if (eErr != OGRERR_NONE) + return eErr; + nCountAxis ++; + } + else if( EQUAL(poNode->GetValue(),"AUTHORITY") ) + { + OGRErr eErr = ValidateAuthority(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else + { + CPLDebug( "OGRSpatialReference::Validate", + "Unexpected child for VERT_CS `%s'.\n", + poNode->GetValue() ); + + return OGRERR_CORRUPT_DATA; + } + } + + if (!bGotVertDatum) + { + CPLDebug( "OGRSpatialReference::Validate", + "No VERT_DATUM child in VERT_CS.\n" ); + + return OGRERR_CORRUPT_DATA; + } + + if (!bGotUnit) + { + CPLDebug( "OGRSpatialReference::Validate", + "No UNIT child in VERT_CS.\n" ); + + return OGRERR_CORRUPT_DATA; + } + + if (nCountAxis > 1) + { + CPLDebug( "OGRSpatialReference::Validate", + "Too many AXIS children in VERT_CS.\n" ); + + return OGRERR_CORRUPT_DATA; + } + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Validate GEOCCS */ +/* -------------------------------------------------------------------- */ + if( EQUAL(poRoot->GetValue(),"GEOCCS") ) + { + OGR_SRSNode *poNode; + int i; + int bGotDatum = FALSE; + int bGotPrimeM = FALSE; + int bGotUnit = FALSE; + int nCountAxis = 0; + + for( i = 1; i < poRoot->GetChildCount(); i++ ) + { + poNode = poRoot->GetChild(i); + + if( EQUAL(poNode->GetValue(),"DATUM") ) + { + bGotDatum = TRUE; + } + else if( EQUAL(poNode->GetValue(),"PRIMEM") ) + { + bGotPrimeM = TRUE; + + if( poNode->GetChildCount() < 2 + || poNode->GetChildCount() > 3 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "PRIMEM has wrong number of children (%d)," + "not 2 or 3 as expected.\n", + poNode->GetChildCount() ); + + return OGRERR_CORRUPT_DATA; + } + } + else if( EQUAL(poNode->GetValue(),"UNIT") ) + { + OGRErr eErr = ValidateUnit(poNode); + if (eErr != OGRERR_NONE) + return eErr; + bGotUnit = TRUE; + } + else if( EQUAL(poNode->GetValue(),"AXIS") ) + { + OGRErr eErr = ValidateAxis(poNode); + if (eErr != OGRERR_NONE) + return eErr; + nCountAxis ++; + } + else if( EQUAL(poNode->GetValue(),"AUTHORITY") ) + { + OGRErr eErr = ValidateAuthority(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else + { + CPLDebug( "OGRSpatialReference::Validate", + "Unexpected child for GEOCCS `%s'.\n", + poNode->GetValue() ); + + return OGRERR_CORRUPT_DATA; + } + } + + if (!bGotDatum) + { + CPLDebug( "OGRSpatialReference::Validate", + "No DATUM child in GEOCCS.\n" ); + + return OGRERR_CORRUPT_DATA; + } + + if (!bGotPrimeM) + { + CPLDebug( "OGRSpatialReference::Validate", + "No PRIMEM child in GEOCCS.\n" ); + + return OGRERR_CORRUPT_DATA; + } + + if (!bGotUnit) + { + CPLDebug( "OGRSpatialReference::Validate", + "No UNIT child in GEOCCS.\n" ); + + return OGRERR_CORRUPT_DATA; + } + + if (nCountAxis != 0 && nCountAxis != 3 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "Wrong number of AXIS children in GEOCCS.\n" ); + + return OGRERR_CORRUPT_DATA; + } + } + +/* -------------------------------------------------------------------- */ +/* For a PROJCS, validate subparameters (other than GEOGCS). */ +/* -------------------------------------------------------------------- */ + if( EQUAL(poRoot->GetValue(),"PROJCS") ) + { + OGR_SRSNode *poNode; + int i; + + for( i = 1; i < poRoot->GetChildCount(); i++ ) + { + poNode = poRoot->GetChild(i); + + if( EQUAL(poNode->GetValue(),"GEOGCS") ) + { + /* validated elsewhere */ + } + else if( EQUAL(poNode->GetValue(),"UNIT") ) + { + OGRErr eErr = ValidateUnit(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else if( EQUAL(poNode->GetValue(),"PARAMETER") ) + { + if( poNode->GetChildCount() != 2 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "PARAMETER has wrong number of children (%d)," + "not 2 as expected.\n", + poNode->GetChildCount() ); + + return OGRERR_CORRUPT_DATA; + } + else if( CSLFindString( (char **)papszParameters, + poNode->GetChild(0)->GetValue()) == -1) + { + CPLDebug( "OGRSpatialReference::Validate", + "Unrecognised PARAMETER `%s'.\n", + poNode->GetChild(0)->GetValue() ); + + return OGRERR_UNSUPPORTED_SRS; + } + } + else if( EQUAL(poNode->GetValue(),"PROJECTION") ) + { + if( poNode->GetChildCount() != 1 && poNode->GetChildCount() != 2 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "PROJECTION has wrong number of children (%d)," + "not 1 or 2 as expected.\n", + poNode->GetChildCount() ); + + return OGRERR_CORRUPT_DATA; + } + else if( CSLFindString( (char **)papszProjectionSupported, + poNode->GetChild(0)->GetValue()) == -1 + && CSLFindString( (char **)papszProjectionUnsupported, + poNode->GetChild(0)->GetValue()) == -1) + { + CPLDebug( "OGRSpatialReference::Validate", + "Unrecognised PROJECTION `%s'.\n", + poNode->GetChild(0)->GetValue() ); + + return OGRERR_UNSUPPORTED_SRS; + } + else if( CSLFindString( (char **)papszProjectionSupported, + poNode->GetChild(0)->GetValue()) == -1) + { + CPLDebug( "OGRSpatialReference::Validate", + "Unsupported, but recognised PROJECTION `%s'.\n", + poNode->GetChild(0)->GetValue() ); + + return OGRERR_UNSUPPORTED_SRS; + } + + if (poNode->GetChildCount() == 2) + { + poNode = poNode->GetChild(1); + if( EQUAL(poNode->GetValue(),"AUTHORITY") ) + { + OGRErr eErr = ValidateAuthority(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else + { + CPLDebug( "OGRSpatialReference::Validate", + "Unexpected child for PROJECTION `%s'.\n", + poNode->GetValue() ); + + return OGRERR_CORRUPT_DATA; + } + } + } + else if( EQUAL(poNode->GetValue(),"AUTHORITY") ) + { + OGRErr eErr = ValidateAuthority(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else if( EQUAL(poNode->GetValue(),"AXIS") ) + { + OGRErr eErr = ValidateAxis(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else if( EQUAL(poNode->GetValue(),"EXTENSION") ) + { + // We do not try to control the sub-organization of + // EXTENSION nodes. + } + else + { + CPLDebug( "OGRSpatialReference::Validate", + "Unexpected child for PROJCS `%s'.\n", + poNode->GetValue() ); + + return OGRERR_CORRUPT_DATA; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Validate GEOGCS if found. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poGEOGCS = poRoot->GetNode( "GEOGCS" ); + + if( poGEOGCS != NULL ) + { + OGR_SRSNode *poNode; + int i; + + for( i = 1; i < poGEOGCS->GetChildCount(); i++ ) + { + poNode = poGEOGCS->GetChild(i); + + if( EQUAL(poNode->GetValue(),"DATUM") ) + { + /* validated elsewhere */ + } + else if( EQUAL(poNode->GetValue(),"PRIMEM") ) + { + if( poNode->GetChildCount() < 2 + || poNode->GetChildCount() > 3 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "PRIMEM has wrong number of children (%d)," + "not 2 or 3 as expected.\n", + poNode->GetChildCount() ); + + return OGRERR_CORRUPT_DATA; + } + } + else if( EQUAL(poNode->GetValue(),"UNIT") ) + { + OGRErr eErr = ValidateUnit(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else if( EQUAL(poNode->GetValue(),"AXIS") ) + { + OGRErr eErr = ValidateAxis(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else if( EQUAL(poNode->GetValue(),"EXTENSION") ) + { + // We do not try to control the sub-organization of + // EXTENSION nodes. + } + else if( EQUAL(poNode->GetValue(),"AUTHORITY") ) + { + OGRErr eErr = ValidateAuthority(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else + { + CPLDebug( "OGRSpatialReference::Validate", + "Unexpected child for GEOGCS `%s'.\n", + poNode->GetValue() ); + + return OGRERR_CORRUPT_DATA; + } + } + + if( poGEOGCS->GetNode("DATUM") == NULL ) + { + CPLDebug( "OGRSpatialReference::Validate", + "No DATUM child in GEOGCS.\n" ); + + return OGRERR_CORRUPT_DATA; + } + } + +/* -------------------------------------------------------------------- */ +/* Validate DATUM/SPHEROID. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poDATUM = poRoot->GetNode( "DATUM" ); + + if( poDATUM != NULL ) + { + OGR_SRSNode *poSPHEROID; + int bGotSpheroid = FALSE; + int i; + + if( poDATUM->GetChildCount() == 0 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "DATUM has no children." ); + + return OGRERR_CORRUPT_DATA; + } + + for( i = 1; i < poDATUM->GetChildCount(); i++ ) + { + OGR_SRSNode *poNode; + poNode = poDATUM->GetChild(i); + + if( EQUAL(poNode->GetValue(),"SPHEROID") ) + { + poSPHEROID = poDATUM->GetChild(1); + bGotSpheroid = TRUE; + + if( poSPHEROID->GetChildCount() != 3 + && poSPHEROID->GetChildCount() != 4 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "SPHEROID has wrong number of children (%d)," + "not 3 or 4 as expected.\n", + poSPHEROID->GetChildCount() ); + + return OGRERR_CORRUPT_DATA; + } + else if( CPLAtof(poSPHEROID->GetChild(1)->GetValue()) == 0.0 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "SPHEROID semi-major axis is zero (%s)!\n", + poSPHEROID->GetChild(1)->GetValue() ); + return OGRERR_CORRUPT_DATA; + } + } + else if( EQUAL(poNode->GetValue(),"AUTHORITY") ) + { + OGRErr eErr = ValidateAuthority(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else if( EQUAL(poNode->GetValue(),"TOWGS84") ) + { + if( poNode->GetChildCount() != 3 + && poNode->GetChildCount() != 7) + { + CPLDebug( "OGRSpatialReference::Validate", + "TOWGS84 has wrong number of children (%d), not 3 or 7.\n", + poNode->GetChildCount() ); + return OGRERR_CORRUPT_DATA; + } + } + else if( EQUAL(poNode->GetValue(),"EXTENSION") ) + { + // We do not try to control the sub-organization of + // EXTENSION nodes. + } + else + { + CPLDebug( "OGRSpatialReference::Validate", + "Unexpected child for DATUM `%s'.\n", + poNode->GetValue() ); + + return OGRERR_CORRUPT_DATA; + } + } + + if( !bGotSpheroid ) + { + CPLDebug( "OGRSpatialReference::Validate", + "No SPHEROID child in DATUM.\n" ); + + return OGRERR_CORRUPT_DATA; + } + } + +/* -------------------------------------------------------------------- */ +/* If this is projected, try to validate the detailed set of */ +/* parameters used for the projection. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr; + + eErr = ValidateProjection(poRoot); + if( eErr != OGRERR_NONE ) + return eErr; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRValidate() */ +/************************************************************************/ +/** + * \brief Validate SRS tokens. + * + * This function is the same as the C++ method OGRSpatialReference::Validate(). + */ +OGRErr OSRValidate( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRValidate", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->Validate(); +} + +/************************************************************************/ +/* IsAliasFor() */ +/************************************************************************/ + +/** + * \brief Return whether the first string passed in an acceptable alias for the + * second string according to the AliasGroupList + * + * @param pszParm1 first string + * @param pszParm2 second string + * + * @return TRUE if both strings are aliases according to the AliasGroupList, FALSE otherwise + */ +int OGRSpatialReference::IsAliasFor( const char *pszParm1, + const char *pszParm2 ) + +{ + int iGroup; + +/* -------------------------------------------------------------------- */ +/* Look for a group containing pszParm1. */ +/* -------------------------------------------------------------------- */ + for( iGroup = 0; papszAliasGroupList[iGroup] != NULL; iGroup++ ) + { + int i; + + for( i = iGroup; papszAliasGroupList[i] != NULL; i++ ) + { + if( EQUAL(pszParm1,papszAliasGroupList[i]) ) + break; + } + + if( papszAliasGroupList[i] == NULL ) + iGroup = i; + else + break; + } + +/* -------------------------------------------------------------------- */ +/* Does this group also contain pszParm2? */ +/* -------------------------------------------------------------------- */ + while( papszAliasGroupList[iGroup] != NULL ) + { + if( EQUAL(papszAliasGroupList[iGroup++],pszParm2) ) + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* ValidateProjection() */ +/************************************************************************/ + +/** + * \brief Validate the current PROJECTION's arguments. + * + * @return OGRERR_NONE if the PROJECTION's arguments validate, an error code + * otherwise + */ +OGRErr OGRSpatialReference::ValidateProjection(OGR_SRSNode *poRoot) +{ + OGR_SRSNode *poPROJCS = poRoot->GetNode( "PROJCS" ); + + if( poPROJCS == NULL ) + return OGRERR_NONE; + + if( poPROJCS->GetNode( "PROJECTION" ) == NULL ) + { + CPLDebug( "OGRSpatialReference::Validate", + "PROJCS does not have PROJECTION subnode." ); + return OGRERR_CORRUPT_DATA; + } + +/* -------------------------------------------------------------------- */ +/* Find the matching group in the proj and parms table. */ +/* -------------------------------------------------------------------- */ + const char *pszProjection; + int iOffset; + + pszProjection = poPROJCS->GetNode("PROJECTION")->GetChild(0)->GetValue(); + + for( iOffset = 0; + papszProjWithParms[iOffset] != NULL + && !EQUAL(papszProjWithParms[iOffset],pszProjection); ) + { + while( papszProjWithParms[iOffset] != NULL ) + iOffset++; + iOffset++; + } + + if( papszProjWithParms[iOffset] == NULL ) + return OGRERR_UNSUPPORTED_SRS; + + iOffset++; + +/* -------------------------------------------------------------------- */ +/* Check all parameters, and verify they are in the permitted */ +/* list. */ +/* -------------------------------------------------------------------- */ + int iNode; + + for( iNode = 0; iNode < poPROJCS->GetChildCount(); iNode++ ) + { + OGR_SRSNode *poParm = poPROJCS->GetChild(iNode); + int i; + const char *pszParmName; + + if( !EQUAL(poParm->GetValue(),"PARAMETER") ) + continue; + + pszParmName = poParm->GetChild(0)->GetValue(); + + for( i = iOffset; papszProjWithParms[i] != NULL; i++ ) + { + if( EQUAL(papszProjWithParms[i],pszParmName) ) + break; + } + + /* This parameter is not an exact match, is it an alias? */ + if( papszProjWithParms[i] == NULL ) + { + for( i = iOffset; papszProjWithParms[i] != NULL; i++ ) + { + if( IsAliasFor(papszProjWithParms[i],pszParmName) ) + break; + } + + if( papszProjWithParms[i] == NULL ) + { + CPLDebug( "OGRSpatialReference::Validate", + "PARAMETER %s for PROJECTION %s is not permitted.", + pszParmName, pszProjection ); + return OGRERR_CORRUPT_DATA; + } + else + { + CPLDebug( "OGRSpatialReference::Validate", + "PARAMETER %s for PROJECTION %s is an alias for %s.", + pszParmName, pszProjection, + papszProjWithParms[i] ); + return OGRERR_CORRUPT_DATA; + } + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ValidateVertDatum() */ +/************************************************************************/ + +/** + * \brief Validate the current VERT_DATUM's arguments. + * + * @return OGRERR_NONE if the VERT_DATUM's arguments validate, an error code + * otherwise + */ +OGRErr OGRSpatialReference::ValidateVertDatum(OGR_SRSNode *poRoot) +{ + if ( !EQUAL(poRoot->GetValue(), "VERT_DATUM") ) + return OGRERR_NONE; + + if (poRoot->GetChildCount() < 2 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "Invalid number of children : %d", poRoot->GetChildCount() ); + return OGRERR_CORRUPT_DATA; + } + + if (atoi(poRoot->GetChild(1)->GetValue()) == 0) + { + CPLDebug( "OGRSpatialReference::Validate", + "Invalid value for datum type (%s) : must be a number\n", + poRoot->GetChild(1)->GetValue()); + return OGRERR_CORRUPT_DATA; + } + + OGR_SRSNode *poNode; + int i; + + for( i = 2; i < poRoot->GetChildCount(); i++ ) + { + poNode = poRoot->GetChild(i); + + if( EQUAL(poNode->GetValue(),"AUTHORITY") ) + { + OGRErr eErr = ValidateAuthority(poNode); + if (eErr != OGRERR_NONE) + return eErr; + } + else if( EQUAL(poNode->GetValue(),"EXTENSION") ) + { + // We do not try to control the sub-organization of + // EXTENSION nodes. + } + else + { + CPLDebug( "OGRSpatialReference::Validate", + "Unexpected child for VERT_DATUM `%s'.\n", + poNode->GetValue() ); + + return OGRERR_CORRUPT_DATA; + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ValidateAuthority() */ +/************************************************************************/ + +/** + * \brief Validate the current AUTHORITY's arguments. + * + * @return OGRERR_NONE if the AUTHORITY's arguments validate, an error code + * otherwise + */ +OGRErr OGRSpatialReference::ValidateAuthority(OGR_SRSNode *poRoot) +{ + if ( !EQUAL(poRoot->GetValue(), "AUTHORITY") ) + return OGRERR_NONE; + + if( poRoot->GetChildCount() != 2 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "AUTHORITY has wrong number of children (%d), not 2.\n", + poRoot->GetChildCount() ); + return OGRERR_CORRUPT_DATA; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ValidateAxis() */ +/************************************************************************/ + +/** + * \brief Validate the current AXIS's arguments. + * + * @return OGRERR_NONE if the AXIS's arguments validate, an error code + * otherwise + */ +OGRErr OGRSpatialReference::ValidateAxis(OGR_SRSNode *poRoot) +{ + if ( !EQUAL(poRoot->GetValue(), "AXIS") ) + return OGRERR_NONE; + + if( poRoot->GetChildCount() != 2 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "AXIS has wrong number of children (%d), not 2.\n", + poRoot->GetChildCount() ); + return OGRERR_CORRUPT_DATA; + } + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* ValidateUnit() */ +/************************************************************************/ + +/** + * \brief Validate the current UNIT's arguments. + * + * @return OGRERR_NONE if the UNIT's arguments validate, an error code + * otherwise + */ +OGRErr OGRSpatialReference::ValidateUnit(OGR_SRSNode *poRoot) +{ + if ( !EQUAL(poRoot->GetValue(), "UNIT") ) + return OGRERR_NONE; + + if( poRoot->GetChildCount() != 2 + && poRoot->GetChildCount() != 3 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "UNIT has wrong number of children (%d), not 2.\n", + poRoot->GetChildCount() ); + return OGRERR_CORRUPT_DATA; + } + else if( CPLAtof(poRoot->GetChild(1)->GetValue()) == 0.0 ) + { + CPLDebug( "OGRSpatialReference::Validate", + "UNIT does not appear to have meaningful" + "coefficient (%s).\n", + poRoot->GetChild(1)->GetValue() ); + return OGRERR_CORRUPT_DATA; + } + + return OGRERR_NONE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogr_srs_xml.cpp b/bazaar/plugin/gdal/ogr/ogr_srs_xml.cpp new file mode 100644 index 000000000..77bb66338 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srs_xml.cpp @@ -0,0 +1,1344 @@ +/****************************************************************************** + * $Id: ogr_srs_xml.cpp 28767 2015-03-25 10:48:08Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: OGRSpatialReference interface to OGC XML (014r4). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam (warmerdam@pobox.com) + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "ogr_p.h" +#include "cpl_minixml.h" +#include "cpl_multiproc.h" + +/************************************************************************/ +/* parseURN() */ +/* */ +/* Parses requested sections out of URN. The passed in URN */ +/* *is* altered but the returned values point into the */ +/* original string. */ +/************************************************************************/ + +static int parseURN( char *pszURN, + const char **ppszObjectType, + const char **ppszAuthority, + const char **ppszCode, + const char **ppszVersion = NULL ) + +{ + int i; + + if( ppszObjectType != NULL ) + *ppszObjectType = ""; + if( ppszAuthority != NULL ) + *ppszAuthority = ""; + if( ppszCode != NULL ) + *ppszCode = ""; + if( ppszVersion != NULL ) + *ppszVersion = ""; + +/* -------------------------------------------------------------------- */ +/* Verify prefix. */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(pszURN,"urn:ogc:def:",12) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Extract object type */ +/* -------------------------------------------------------------------- */ + if( ppszObjectType != NULL ) + *ppszObjectType = (const char *) pszURN + 12; + + i = 12; + while( pszURN[i] != ':' && pszURN[i] != '\0' ) + i++; + + if( pszURN[i] == '\0' ) + return FALSE; + + pszURN[i] = '\0'; + i++; + +/* -------------------------------------------------------------------- */ +/* Extract authority */ +/* -------------------------------------------------------------------- */ + if( ppszAuthority != NULL ) + *ppszAuthority = (char *) pszURN + i; + + while( pszURN[i] != ':' && pszURN[i] != '\0' ) + i++; + + if( pszURN[i] == '\0' ) + return FALSE; + + pszURN[i] = '\0'; + i++; + +/* -------------------------------------------------------------------- */ +/* Extract version */ +/* -------------------------------------------------------------------- */ + if( ppszVersion != NULL ) + *ppszVersion = (char *) pszURN + i; + + while( pszURN[i] != ':' && pszURN[i] != '\0' ) + i++; + + if( pszURN[i] == '\0' ) + return FALSE; + + pszURN[i] = '\0'; + i++; + +/* -------------------------------------------------------------------- */ +/* Extract code. */ +/* -------------------------------------------------------------------- */ + if( ppszCode != NULL ) + *ppszCode = (char *) pszURN + i; + + return TRUE; +} + +/************************************************************************/ +/* addURN() */ +/************************************************************************/ + +static void addURN( CPLXMLNode *psTarget, + const char *pszAuthority, + const char *pszObjectType, + int nCode, + const char *pszVersion = "" ) + +{ + char szURN[200]; + + if( pszVersion == NULL ) + pszVersion = ""; + + CPLAssert( strlen(pszAuthority)+strlen(pszObjectType) < sizeof(szURN)-30 ); + + sprintf( szURN, "urn:ogc:def:%s:%s:%s:", + pszObjectType, pszAuthority, pszVersion ); + + if( nCode != 0 ) + sprintf( szURN + strlen(szURN), "%d", nCode ); + + CPLCreateXMLNode( + CPLCreateXMLNode( psTarget, CXT_Attribute, "xlink:href" ), + CXT_Text, szURN ); +} + +/************************************************************************/ +/* AddValueIDWithURN() */ +/* */ +/* Adds element of the form id" */ +/************************************************************************/ + +static CPLXMLNode * +AddValueIDWithURN( CPLXMLNode *psTarget, + const char *pszElement, + const char *pszAuthority, + const char *pszObjectType, + int nCode, + const char *pszVersion = "" ) + +{ + CPLXMLNode *psElement; + + psElement = CPLCreateXMLNode( psTarget, CXT_Element, pszElement ); + addURN( psElement, pszAuthority, pszObjectType, nCode, pszVersion ); + + return psElement; +} + +/************************************************************************/ +/* addAuthorityIDBlock() */ +/* */ +/* Creates a structure like: */ +/* */ +/* code */ +/* */ +/************************************************************************/ +static CPLXMLNode *addAuthorityIDBlock( CPLXMLNode *psTarget, + const char *pszElement, + const char *pszAuthority, + const char *pszObjectType, + int nCode, + const char *pszVersion = "" ) + +{ + char szURN[200]; + +/* -------------------------------------------------------------------- */ +/* Prepare partial URN without the actual code. */ +/* -------------------------------------------------------------------- */ + if( pszVersion == NULL ) + pszVersion = ""; + + CPLAssert( strlen(pszAuthority)+strlen(pszObjectType) < sizeof(szURN)-30 ); + + sprintf( szURN, "urn:ogc:def:%s:%s:%s:", + pszObjectType, pszAuthority, pszVersion ); + +/* -------------------------------------------------------------------- */ +/* Prepare the base name, eg. . */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psElement = + CPLCreateXMLNode( psTarget, CXT_Element, pszElement ); + +/* -------------------------------------------------------------------- */ +/* Prepare the name element. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode * psName = + CPLCreateXMLNode( psElement, CXT_Element, "gml:name" ); + +/* -------------------------------------------------------------------- */ +/* Prepare the codespace attribute. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLNode( + CPLCreateXMLNode( psName, CXT_Attribute, "codeSpace" ), + CXT_Text, szURN ); + +/* -------------------------------------------------------------------- */ +/* Attach code value to name node. */ +/* -------------------------------------------------------------------- */ + char szCode[32]; + sprintf( szCode, "%d", nCode ); + + CPLCreateXMLNode( psName, CXT_Text, szCode ); + + return psElement; +} + +/************************************************************************/ +/* addGMLId() */ +/************************************************************************/ + +static void addGMLId( CPLXMLNode *psParent ) +{ + static CPLMutex *hGMLIdMutex = NULL; + CPLMutexHolderD( &hGMLIdMutex ); + + /* CPLXMLNode *psId; */ + static int nNextGMLId = 1; + char szIdText[40]; + + sprintf( szIdText, "ogrcrs%d", nNextGMLId++ ); + + /* psId = */ + CPLCreateXMLNode( + CPLCreateXMLNode( psParent, CXT_Attribute, "gml:id" ), + CXT_Text, szIdText ); +} + +/************************************************************************/ +/* exportAuthorityToXML() */ +/************************************************************************/ + +static CPLXMLNode *exportAuthorityToXML( const OGR_SRSNode *poAuthParent, + const char *pszTagName, + CPLXMLNode *psXMLParent, + const char *pszObjectType, + int bUseSubName = TRUE ) + +{ + const OGR_SRSNode *poAuthority; + +/* -------------------------------------------------------------------- */ +/* Get authority node from parent. */ +/* -------------------------------------------------------------------- */ + if( poAuthParent->FindChild( "AUTHORITY" ) == -1 ) + return NULL; + + poAuthority = poAuthParent->GetChild( + poAuthParent->FindChild( "AUTHORITY" )); + +/* -------------------------------------------------------------------- */ +/* Create identification. */ +/* -------------------------------------------------------------------- */ + const char *pszCode, *pszCodeSpace, *pszEdition; + + pszCode = poAuthority->GetChild(1)->GetValue(); + pszCodeSpace = poAuthority->GetChild(0)->GetValue(); + pszEdition = NULL; + + if( bUseSubName ) + return addAuthorityIDBlock( psXMLParent, pszTagName, pszCodeSpace, + pszObjectType, atoi(pszCode), pszEdition ); + else + return AddValueIDWithURN( psXMLParent, pszTagName, pszCodeSpace, + pszObjectType, atoi(pszCode), pszEdition ); + +} + +/************************************************************************/ +/* addProjArg() */ +/************************************************************************/ + +static void addProjArg( const OGRSpatialReference *poSRS, CPLXMLNode *psBase, + const char *pszMeasureType, double dfDefault, + int nParameterID, const char *pszWKTName ) + +{ + CPLXMLNode *psNode, *psValue; + + psNode = CPLCreateXMLNode( psBase, CXT_Element, "gml:usesValue" ); + +/* -------------------------------------------------------------------- */ +/* Handle the UOM. */ +/* -------------------------------------------------------------------- */ + const char *pszUOMValue; + + if( EQUAL(pszMeasureType,"Angular") ) + pszUOMValue = "urn:ogc:def:uom:EPSG::9102"; + else + pszUOMValue = "urn:ogc:def:uom:EPSG::9001"; + + psValue = CPLCreateXMLNode( psNode, CXT_Element, "gml:value" ); + + CPLCreateXMLNode( + CPLCreateXMLNode( psValue, CXT_Attribute, "uom" ), + CXT_Text, pszUOMValue ); + +/* -------------------------------------------------------------------- */ +/* Add the parameter value itself. */ +/* -------------------------------------------------------------------- */ + double dfParmValue + = poSRS->GetNormProjParm( pszWKTName, dfDefault, NULL ); + + CPLCreateXMLNode( psValue, CXT_Text, + CPLString().Printf( "%.16g", dfParmValue ) ); + +/* -------------------------------------------------------------------- */ +/* Add the valueOfParameter. */ +/* -------------------------------------------------------------------- */ + AddValueIDWithURN( psNode, "gml:valueOfParameter", "EPSG", "parameter", + nParameterID ); +} + +/************************************************************************/ +/* addAxis() */ +/* */ +/* Added the element and down. */ +/************************************************************************/ + +static CPLXMLNode *addAxis( CPLXMLNode *psXMLParent, + const char *pszAxis, // "Lat", "Long", "E" or "N" + const OGR_SRSNode * /* poUnitsSrc */ ) + +{ + CPLXMLNode *psAxisXML; + + psAxisXML = + CPLCreateXMLNode( + CPLCreateXMLNode( psXMLParent, CXT_Element, "gml:usesAxis" ), + CXT_Element, "gml:CoordinateSystemAxis" ); + addGMLId( psAxisXML ); + + if( EQUAL(pszAxis,"Lat") ) + { + CPLCreateXMLNode( + CPLCreateXMLNode( psAxisXML, CXT_Attribute, "gml:uom" ), + CXT_Text, "urn:ogc:def:uom:EPSG::9102" ); + + CPLCreateXMLElementAndValue( psAxisXML, "gml:name", + "Geodetic latitude" ); + addAuthorityIDBlock( psAxisXML, "gml:axisID", "EPSG", "axis", 9901 ); + CPLCreateXMLElementAndValue( psAxisXML, "gml:axisAbbrev", "Lat" ); + CPLCreateXMLElementAndValue( psAxisXML, "gml:axisDirection", "north" ); + } + else if( EQUAL(pszAxis,"Long") ) + { + CPLCreateXMLNode( + CPLCreateXMLNode( psAxisXML, CXT_Attribute, "gml:uom" ), + CXT_Text, "urn:ogc:def:uom:EPSG::9102" ); + + CPLCreateXMLElementAndValue( psAxisXML, "gml:name", + "Geodetic longitude" ); + addAuthorityIDBlock( psAxisXML, "gml:axisID", "EPSG", "axis", 9902 ); + CPLCreateXMLElementAndValue( psAxisXML, "gml:axisAbbrev", "Lon" ); + CPLCreateXMLElementAndValue( psAxisXML, "gml:axisDirection", "east" ); + } + else if( EQUAL(pszAxis,"E") ) + { + CPLCreateXMLNode( + CPLCreateXMLNode( psAxisXML, CXT_Attribute, "gml:uom" ), + CXT_Text, "urn:ogc:def:uom:EPSG::9001" ); + + CPLCreateXMLElementAndValue( psAxisXML, "gml:name", "Easting" ); + addAuthorityIDBlock( psAxisXML, "gml:axisID", "EPSG", "axis", 9906 ); + CPLCreateXMLElementAndValue( psAxisXML, "gml:axisAbbrev", "E" ); + CPLCreateXMLElementAndValue( psAxisXML, "gml:axisDirection", "east" ); + } + else if( EQUAL(pszAxis,"N") ) + { + CPLCreateXMLNode( + CPLCreateXMLNode( psAxisXML, CXT_Attribute, "gml:uom" ), + CXT_Text, "urn:ogc:def:uom:EPSG::9001" ); + + CPLCreateXMLElementAndValue( psAxisXML, "gml:name", "Northing" ); + addAuthorityIDBlock( psAxisXML, "gml:axisID", "EPSG", "axis", 9907 ); + CPLCreateXMLElementAndValue( psAxisXML, "gml:axisAbbrev", "N" ); + CPLCreateXMLElementAndValue( psAxisXML, "gml:axisDirection", "north" ); + } + else + { + CPLAssert( FALSE ); + } + + return psAxisXML; +} + +/************************************************************************/ +/* exportGeogCSToXML() */ +/************************************************************************/ + +static CPLXMLNode *exportGeogCSToXML( const OGRSpatialReference *poSRS ) + +{ + CPLXMLNode *psGCS_XML; + const OGR_SRSNode *poGeogCS = poSRS->GetAttrNode( "GEOGCS" ); + + if( poGeogCS == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Establish initial infrastructure. */ +/* -------------------------------------------------------------------- */ + psGCS_XML = CPLCreateXMLNode( NULL, CXT_Element, "gml:GeographicCRS" ); + addGMLId( psGCS_XML ); + +/* -------------------------------------------------------------------- */ +/* Attach symbolic name (srsName). */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( psGCS_XML, "gml:srsName", + poGeogCS->GetChild(0)->GetValue() ); + +/* -------------------------------------------------------------------- */ +/* Does the overall coordinate system have an authority? If so */ +/* attach as an identification section. */ +/* -------------------------------------------------------------------- */ + exportAuthorityToXML( poGeogCS, "gml:srsID", psGCS_XML, "crs" ); + +/* -------------------------------------------------------------------- */ +/* Insert a big whack of fixed stuff defining the */ +/* ellipsoidalCS. Basically this defines the axes and their */ +/* units. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psECS; + + psECS = CPLCreateXMLNode( + CPLCreateXMLNode( psGCS_XML, CXT_Element, "gml:usesEllipsoidalCS" ), + CXT_Element, "gml:EllipsoidalCS" ); + + addGMLId( psECS ); + + CPLCreateXMLElementAndValue( psECS, "gml:csName", "ellipsoidal" ); + + addAuthorityIDBlock( psECS, "gml:csID", "EPSG", "cs", 6402 ); + + addAxis( psECS, "Lat", NULL ); + addAxis( psECS, "Long", NULL ); + +/* -------------------------------------------------------------------- */ +/* Start with the datum. */ +/* -------------------------------------------------------------------- */ + const OGR_SRSNode *poDatum = poGeogCS->GetNode( "DATUM" ); + CPLXMLNode *psDatumXML; + + if( poDatum == NULL ) + { + CPLDestroyXMLNode( psGCS_XML ); + return NULL; + } + + psDatumXML = CPLCreateXMLNode( + CPLCreateXMLNode( psGCS_XML, CXT_Element, "gml:usesGeodeticDatum" ), + CXT_Element, "gml:GeodeticDatum" ); + + addGMLId( psDatumXML ); + +/* -------------------------------------------------------------------- */ +/* Set the datumName. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( psDatumXML, "gml:datumName", + poDatum->GetChild(0)->GetValue() ); + +/* -------------------------------------------------------------------- */ +/* Set authority id info if available. */ +/* -------------------------------------------------------------------- */ + exportAuthorityToXML( poDatum, "gml:datumID", psDatumXML, "datum" ); + +/* -------------------------------------------------------------------- */ +/* Setup prime meridian information. */ +/* -------------------------------------------------------------------- */ + const OGR_SRSNode *poPMNode = poGeogCS->GetNode( "PRIMEM" ); + CPLXMLNode *psPM; + char *pszPMName = (char* ) "Greenwich"; + double dfPMOffset = poSRS->GetPrimeMeridian( &pszPMName ); + + psPM = CPLCreateXMLNode( + CPLCreateXMLNode( psDatumXML, CXT_Element, "gml:usesPrimeMeridian" ), + CXT_Element, "gml:PrimeMeridian" ); + + addGMLId( psPM ); + + CPLCreateXMLElementAndValue( psPM, "gml:meridianName", pszPMName ); + + if( poPMNode ) + exportAuthorityToXML( poPMNode, "gml:meridianID", psPM, "meridian" ); + + CPLXMLNode *psAngle; + + psAngle = + CPLCreateXMLNode( + CPLCreateXMLNode( psPM, CXT_Element, "gml:greenwichLongitude" ), + CXT_Element, "gml:angle" ); + + CPLCreateXMLNode( CPLCreateXMLNode( psAngle, CXT_Attribute, "uom" ), + CXT_Text, "urn:ogc:def:uom:EPSG::9102" ); + + CPLCreateXMLNode( psAngle, CXT_Text, + CPLString().Printf( "%.16g", dfPMOffset ) ); + +/* -------------------------------------------------------------------- */ +/* Translate the ellipsoid. */ +/* -------------------------------------------------------------------- */ + const OGR_SRSNode *poEllipsoid = poDatum->GetNode( "SPHEROID" ); + + if( poEllipsoid != NULL ) + { + CPLXMLNode *psEllipseXML; + + psEllipseXML = + CPLCreateXMLNode( + CPLCreateXMLNode(psDatumXML,CXT_Element,"gml:usesEllipsoid" ), + CXT_Element, "gml:Ellipsoid" ); + + addGMLId( psEllipseXML ); + + CPLCreateXMLElementAndValue( psEllipseXML, "gml:ellipsoidName", + poEllipsoid->GetChild(0)->GetValue() ); + + exportAuthorityToXML( poEllipsoid, "gml:ellipsoidID", psEllipseXML, + "ellipsoid"); + + CPLXMLNode *psParmXML; + + psParmXML = CPLCreateXMLNode( psEllipseXML, CXT_Element, + "gml:semiMajorAxis" ); + + CPLCreateXMLNode( CPLCreateXMLNode(psParmXML,CXT_Attribute,"uom"), + CXT_Text, "urn:ogc:def:uom:EPSG::9001" ); + + CPLCreateXMLNode( psParmXML, CXT_Text, + poEllipsoid->GetChild(1)->GetValue() ); + + psParmXML = + CPLCreateXMLNode( + CPLCreateXMLNode( psEllipseXML, CXT_Element, + "gml:secondDefiningParameter" ), + CXT_Element, "gml:inverseFlattening" ); + + CPLCreateXMLNode( CPLCreateXMLNode(psParmXML,CXT_Attribute,"uom"), + CXT_Text, "urn:ogc:def:uom:EPSG::9201" ); + + CPLCreateXMLNode( psParmXML, CXT_Text, + poEllipsoid->GetChild(2)->GetValue() ); + } + + return psGCS_XML; +} + +/************************************************************************/ +/* exportProjCSToXML() */ +/************************************************************************/ + +static CPLXMLNode *exportProjCSToXML( const OGRSpatialReference *poSRS ) + +{ + const OGR_SRSNode *poProjCS = poSRS->GetAttrNode( "PROJCS" ); + + if( poProjCS == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Establish initial infrastructure. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psCRS_XML; + + psCRS_XML = CPLCreateXMLNode( NULL, CXT_Element, "gml:ProjectedCRS" ); + addGMLId( psCRS_XML ); + +/* -------------------------------------------------------------------- */ +/* Attach symbolic name (a name in a nameset). */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( psCRS_XML, "gml:srsName", + poProjCS->GetChild(0)->GetValue() ); + +/* -------------------------------------------------------------------- */ +/* Add authority info if we have it. */ +/* -------------------------------------------------------------------- */ + exportAuthorityToXML( poProjCS, "gml:srsID", psCRS_XML, "crs" ); + +/* -------------------------------------------------------------------- */ +/* Use the GEOGCS as a */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psBaseCRSXML = + CPLCreateXMLNode( psCRS_XML, CXT_Element, "gml:baseCRS" ); + + CPLAddXMLChild( psBaseCRSXML, exportGeogCSToXML( poSRS ) ); + +/* -------------------------------------------------------------------- */ +/* Our projected coordinate system is "defined by Conversion". */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psDefinedBy; + + psDefinedBy = CPLCreateXMLNode( psCRS_XML, CXT_Element, + "gml:definedByConversion" ); + +/* -------------------------------------------------------------------- */ +/* Projections are handled as ParameterizedTransformations. */ +/* -------------------------------------------------------------------- */ + const char *pszProjection = poSRS->GetAttrValue("PROJECTION"); + CPLXMLNode *psConv; + + psConv = CPLCreateXMLNode( psDefinedBy, CXT_Element, "gml:Conversion"); + addGMLId( psConv ); + + CPLCreateXMLNode(CPLCreateXMLNode(psConv, CXT_Element, "gml:coordinateOperationName"), + CXT_Text, pszProjection); + +/* -------------------------------------------------------------------- */ +/* Transverse Mercator */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR) ) + { + AddValueIDWithURN( psConv, "gml:usesMethod", "EPSG", "method", + 9807 ); + + addProjArg( poSRS, psConv, "Angular", 0.0, + 8801, SRS_PP_LATITUDE_OF_ORIGIN ); + addProjArg( poSRS, psConv, "Angular", 0.0, + 8802, SRS_PP_CENTRAL_MERIDIAN ); + addProjArg( poSRS, psConv, "Unitless", 1.0, + 8805, SRS_PP_SCALE_FACTOR ); + addProjArg( poSRS, psConv, "Linear", 0.0, + 8806, SRS_PP_FALSE_EASTING ); + addProjArg( poSRS, psConv, "Linear", 0.0, + 8807, SRS_PP_FALSE_NORTHING ); + } + +/* -------------------------------------------------------------------- */ +/* Lambert Conformal Conic */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) ) + { + AddValueIDWithURN( psConv, "gml:usesMethod", "EPSG", "method", + 9801 ); + + addProjArg( poSRS, psConv, "Angular", 0.0, + 8801, SRS_PP_LATITUDE_OF_ORIGIN ); + addProjArg( poSRS, psConv, "Angular", 0.0, + 8802, SRS_PP_CENTRAL_MERIDIAN ); + addProjArg( poSRS, psConv, "Unitless", 1.0, + 8805, SRS_PP_SCALE_FACTOR ); + addProjArg( poSRS, psConv, "Linear", 0.0, + 8806, SRS_PP_FALSE_EASTING ); + addProjArg( poSRS, psConv, "Linear", 0.0, + 8807, SRS_PP_FALSE_NORTHING ); + } + + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unhandled projection method %s", pszProjection); + } + +/* -------------------------------------------------------------------- */ +/* Define the cartesian coordinate system. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psCCS; + + psCCS = + CPLCreateXMLNode( + CPLCreateXMLNode( psCRS_XML, CXT_Element, "gml:usesCartesianCS" ), + CXT_Element, "gml:CartesianCS" ); + + addGMLId( psCCS ); + + CPLCreateXMLElementAndValue( psCCS, "gml:csName", "Cartesian" ); + addAuthorityIDBlock( psCCS, "gml:csID", "EPSG", "cs", 4400 ); + addAxis( psCCS, "E", NULL ); + addAxis( psCCS, "N", NULL ); + + return psCRS_XML; +} + +/************************************************************************/ +/* exportToXML() */ +/************************************************************************/ + +/** + * \brief Export coordinate system in XML format. + * + * Converts the loaded coordinate reference system into XML format + * to the extent possible. The string returned in ppszRawXML should be + * deallocated by the caller with CPLFree() when no longer needed. + * + * LOCAL_CS coordinate systems are not translatable. An empty string + * will be returned along with OGRERR_NONE. + * + * This method is the equivelent of the C function OSRExportToXML(). + * + * @param ppszRawXML pointer to which dynamically allocated XML definition + * will be assigned. + * @param pszDialect currently ignored. The dialect used is GML based. + * + * @return OGRERR_NONE on success or an error code on failure. + */ + +OGRErr OGRSpatialReference::exportToXML( char **ppszRawXML, + CPL_UNUSED const char * pszDialect ) const +{ + CPLXMLNode *psXMLTree = NULL; + + if( IsGeographic() ) + { + psXMLTree = exportGeogCSToXML( this ); + } + else if( IsProjected() ) + { + psXMLTree = exportProjCSToXML( this ); + } + else + return OGRERR_UNSUPPORTED_SRS; + + *ppszRawXML = CPLSerializeXMLTree( psXMLTree ); + CPLDestroyXMLNode( psXMLTree ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRExportToXML() */ +/************************************************************************/ +/** + * \brief Export coordinate system in XML format. + * + * This function is the same as OGRSpatialReference::exportToXML(). + */ + +OGRErr OSRExportToXML( OGRSpatialReferenceH hSRS, char **ppszRawXML, + const char *pszDialect ) + +{ + VALIDATE_POINTER1( hSRS, "OSRExportToXML", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->exportToXML( ppszRawXML, + pszDialect ); +} + +#ifdef notdef +/************************************************************************/ +/* importXMLUnits() */ +/************************************************************************/ + +static void importXMLUnits( CPLXMLNode *psSrcXML, const char *pszClass, + OGRSpatialReference *poSRS, const char *pszTarget) + +{ + const char *pszUnitName, *pszUnitsPer; + OGR_SRSNode *poNode = poSRS->GetAttrNode( pszTarget ); + OGR_SRSNode *poUnits; + + CPLAssert( EQUAL(pszClass,"AngularUnit") + || EQUAL(pszClass,"LinearUnit") ); + + psSrcXML = CPLGetXMLNode( psSrcXML, pszClass ); + if( psSrcXML == NULL ) + goto DefaultTarget; + + pszUnitName = CPLGetXMLValue( psSrcXML, "NameSet.name", "unnamed" ); + if( EQUAL(pszClass,"AngularUnit") ) + pszUnitsPer = CPLGetXMLValue( psSrcXML, "radiansPerUnit", NULL ); + else + pszUnitsPer = CPLGetXMLValue( psSrcXML, "metresPerUnit", NULL ); + + if( pszUnitsPer == NULL ) + { + CPLDebug( "OGR_SRS_XML", + "Missing PerUnit value for %s.", + pszClass ); + goto DefaultTarget; + } + + if( poNode == NULL ) + { + CPLDebug( "OGR_SRS_XML", "Can't find %s in importXMLUnits.", + pszTarget ); + goto DefaultTarget; + } + + if( poNode->FindChild("UNIT") != -1 ) + { + poUnits = poNode->GetChild( poNode->FindChild( "UNIT" ) ); + poUnits->GetChild(0)->SetValue( pszUnitName ); + poUnits->GetChild(1)->SetValue( pszUnitsPer ); + } + else + { + poUnits = new OGR_SRSNode( "UNIT" ); + poUnits->AddChild( new OGR_SRSNode( pszUnitName ) ); + poUnits->AddChild( new OGR_SRSNode( pszUnitsPer ) ); + + poNode->AddChild( poUnits ); + } + return; + + DefaultTarget: + poUnits = new OGR_SRSNode( "UNIT" ); + if( EQUAL(pszClass,"AngularUnit") ) + { + poUnits->AddChild( new OGR_SRSNode( SRS_UA_DEGREE ) ); + poUnits->AddChild( new OGR_SRSNode( SRS_UA_DEGREE_CONV ) ); + } + else + { + poUnits->AddChild( new OGR_SRSNode( SRS_UL_METER ) ); + poUnits->AddChild( new OGR_SRSNode( "1.0" ) ); + } + + poNode->AddChild( poUnits ); +} +#endif + +/************************************************************************/ +/* importXMLAuthority() */ +/************************************************************************/ + +static void importXMLAuthority( CPLXMLNode *psSrcXML, + OGRSpatialReference *poSRS, + const char *pszSourceKey, + const char *pszTargetKey ) + +{ + CPLXMLNode *psIDNode = CPLGetXMLNode( psSrcXML, pszSourceKey ); + CPLXMLNode *psNameNode = CPLGetXMLNode( psIDNode, "name" ); + CPLXMLNode *psCodeSpace = CPLGetXMLNode( psNameNode, "codeSpace" ); + const char *pszAuthority, *pszCode; + char *pszURN; + int nCode = 0; + + if( psIDNode == NULL || psNameNode == NULL || psCodeSpace == NULL ) + return; + + pszURN = CPLStrdup(CPLGetXMLValue( psCodeSpace, "", "" )); + if( !parseURN( pszURN, NULL, &pszAuthority, &pszCode ) ) + { + CPLFree( pszURN ); + return; + } + + if( strlen(pszCode) == 0 ) + pszCode = (char *) CPLGetXMLValue( psNameNode, "", "" ); + + if( pszCode != NULL ) + nCode = atoi(pszCode); + + if( nCode != 0 ) + poSRS->SetAuthority( pszTargetKey, pszAuthority, nCode ); + + CPLFree( pszURN ); +} + +/************************************************************************/ +/* ParseOGCDefURN() */ +/* */ +/* Parse out fields from a URN of the form: */ +/* urn:ogc:def:parameter:EPSG:6.3:9707 */ +/************************************************************************/ + +static int ParseOGCDefURN( const char *pszURN, + CPLString *poObjectType, + CPLString *poAuthority, + CPLString *poVersion, + CPLString *poValue ) + +{ + if( poObjectType != NULL ) + *poObjectType = ""; + + if( poAuthority != NULL ) + *poAuthority = ""; + + if( poVersion != NULL ) + *poVersion = ""; + + if( poValue != NULL ) + *poValue = ""; + + if( pszURN == NULL || !EQUALN(pszURN,"urn:ogc:def:",12) ) + return FALSE; + + char **papszTokens = CSLTokenizeStringComplex( pszURN + 12, ":", + FALSE, TRUE ); + + if( CSLCount(papszTokens) != 4 ) + { + CSLDestroy( papszTokens ); + return FALSE; + } + + if( poObjectType != NULL ) + *poObjectType = papszTokens[0]; + + if( poAuthority != NULL ) + *poAuthority = papszTokens[1]; + + if( poVersion != NULL ) + *poVersion = papszTokens[2]; + + if( poValue != NULL ) + *poValue = papszTokens[3]; + + CSLDestroy( papszTokens ); + return TRUE; +} + +/************************************************************************/ +/* getEPSGObjectCodeValue() */ +/* */ +/* Fetch a code value from the indicated node. Should work on */ +/* something of the form or */ +/* something of the form n. */ +/************************************************************************/ + +static int getEPSGObjectCodeValue( CPLXMLNode *psNode, + const char *pszEPSGObjectType, /*"method" */ + int nDefault ) + +{ + if( psNode == NULL ) + return nDefault; + + CPLString osObjectType, osAuthority, osValue; + const char* pszHrefVal; + + pszHrefVal = CPLGetXMLValue( psNode, "xlink:href", NULL ); + if (pszHrefVal == NULL) + pszHrefVal = CPLGetXMLValue( psNode, "href", NULL ); + + if( !ParseOGCDefURN( pszHrefVal, + &osObjectType, &osAuthority, NULL, &osValue ) ) + return nDefault; + + if( !EQUAL(osAuthority,"EPSG") + || !EQUAL(osObjectType, pszEPSGObjectType) ) + return nDefault; + + if( strlen(osValue) > 0 ) + return atoi(osValue); + + const char *pszValue = CPLGetXMLValue( psNode, "", NULL); + if( pszValue != NULL ) + return atoi(pszValue); + else + return nDefault; +} + +/************************************************************************/ +/* getProjectionParm() */ +/************************************************************************/ + +static double getProjectionParm( CPLXMLNode *psRootNode, + int nParameterCode, + const char * /*pszMeasureType */, + double dfDefault ) + +{ + CPLXMLNode *psUsesParameter; + + for( psUsesParameter = psRootNode->psChild; + psUsesParameter != NULL; + psUsesParameter = psUsesParameter->psNext ) + { + if( psUsesParameter->eType != CXT_Element ) + continue; + + if( !EQUAL(psUsesParameter->pszValue,"usesParameterValue") + && !EQUAL(psUsesParameter->pszValue,"usesValue") ) + continue; + + if( getEPSGObjectCodeValue( CPLGetXMLNode(psUsesParameter, + "valueOfParameter"), + "parameter", 0 ) == nParameterCode ) + { + const char *pszValue = CPLGetXMLValue( psUsesParameter, "value", + NULL ); + + if( pszValue != NULL ) + return CPLAtof(pszValue); + else + return dfDefault; + } + } + + return dfDefault; +} + +/************************************************************************/ +/* getNormalizedValue() */ +/* */ +/* Parse a node to get it's numerical value, and then normalize */ +/* into meters of degrees depending on the measure type. */ +/************************************************************************/ + +static double getNormalizedValue( CPLXMLNode *psNode, const char *pszPath, + const char * /*pszMeasure*/, + double dfDefault ) + +{ + CPLXMLNode *psTargetNode; + CPLXMLNode *psValueNode; + + if( pszPath == NULL || strlen(pszPath) == 0 ) + psTargetNode = psNode; + else + psTargetNode = CPLGetXMLNode( psNode, pszPath ); + + if( psTargetNode == NULL ) + return dfDefault; + + for( psValueNode = psTargetNode->psChild; + psValueNode != NULL && psValueNode->eType != CXT_Text; + psValueNode = psValueNode->psNext ) {} + + if( psValueNode == NULL ) + return dfDefault; + + // Add normalization later. + + return CPLAtof(psValueNode->pszValue); +} + +/************************************************************************/ +/* importGeogCSFromXML() */ +/************************************************************************/ + +static OGRErr importGeogCSFromXML( OGRSpatialReference *poSRS, + CPLXMLNode *psCRS ) + +{ + const char *pszGeogName, *pszDatumName, *pszEllipsoidName, *pszPMName; + double dfSemiMajor, dfInvFlattening, dfPMOffset = 0.0; + +/* -------------------------------------------------------------------- */ +/* Set the GEOGCS name from the srsName. */ +/* -------------------------------------------------------------------- */ + pszGeogName = + CPLGetXMLValue( psCRS, "srsName", "Unnamed GeogCS" ); + +/* -------------------------------------------------------------------- */ +/* If we don't seem to have a detailed coordinate system */ +/* definition, check if we can define based on an EPSG code. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psDatum; + + psDatum = CPLGetXMLNode( psCRS, "usesGeodeticDatum.GeodeticDatum" ); + + if( psDatum == NULL ) + { + OGRSpatialReference oIdSRS; + + oIdSRS.SetLocalCS( "dummy" ); + importXMLAuthority( psCRS, &oIdSRS, "srsID", "LOCAL_CS" ); + + if( oIdSRS.GetAuthorityCode( "LOCAL_CS" ) != NULL + && oIdSRS.GetAuthorityName( "LOCAL_CS" ) != NULL + && EQUAL(oIdSRS.GetAuthorityName("LOCAL_CS"),"EPSG") ) + { + return poSRS->importFromEPSG( + atoi(oIdSRS.GetAuthorityCode("LOCAL_CS")) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Get datum name. */ +/* -------------------------------------------------------------------- */ + pszDatumName = + CPLGetXMLValue( psDatum, "datumName", "Unnamed Datum" ); + +/* -------------------------------------------------------------------- */ +/* Get ellipsoid information. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psE; + + psE = CPLGetXMLNode( psDatum, "usesEllipsoid.Ellipsoid" ); + pszEllipsoidName = + CPLGetXMLValue( psE, "ellipsoidName", "Unnamed Ellipsoid" ); + + dfSemiMajor = getNormalizedValue( psE, "semiMajorAxis", "Linear", + SRS_WGS84_SEMIMAJOR ); + + dfInvFlattening = + getNormalizedValue( psE, "secondDefiningParameter.inverseFlattening", + "Unitless", 0.0 ); + + if( dfInvFlattening == 0.0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Ellipsoid inverseFlattening corrupt or missing." ); + return OGRERR_CORRUPT_DATA; + } + +/* -------------------------------------------------------------------- */ +/* Get the prime meridian. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psPM; + + psPM = CPLGetXMLNode( psDatum, "usesPrimeMeridian.PrimeMeridian" ); + if( psPM == NULL ) + { + pszPMName = "Greenwich"; + dfPMOffset = 0.0; + } + else + { + pszPMName = CPLGetXMLValue( psPM, "meridianName", + "Unnamed Prime Meridian"); + dfPMOffset = + getNormalizedValue( psPM, "greenwichLongitude.angle", + "Angular", 0.0 ); + } + +/* -------------------------------------------------------------------- */ +/* Set the geographic definition. */ +/* -------------------------------------------------------------------- */ + poSRS->SetGeogCS( pszGeogName, pszDatumName, + pszEllipsoidName, dfSemiMajor, dfInvFlattening, + pszPMName, dfPMOffset ); + +/* -------------------------------------------------------------------- */ +/* Look for angular units. We don't check that all axes match */ +/* at this time. */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + CPLXMLNode *psAxis; + + psAxis = CPLGetXMLNode( psGeo2DCRS, + "EllipsoidalCoordinateSystem.CoordinateAxis" ); + importXMLUnits( psAxis, "AngularUnit", poSRS, "GEOGCS" ); +#endif + +/* -------------------------------------------------------------------- */ +/* Can we set authorities for any of the levels? */ +/* -------------------------------------------------------------------- */ + importXMLAuthority( psCRS, poSRS, "srsID", "GEOGCS" ); + importXMLAuthority( psDatum, poSRS, "datumID", "GEOGCS|DATUM" ); + importXMLAuthority( psE, poSRS, "ellipsoidID", + "GEOGCS|DATUM|SPHEROID" ); + importXMLAuthority( psDatum, poSRS, + "usesPrimeMeridian.PrimeMeridian.meridianID", + "GEOGCS|PRIMEM" ); + + poSRS->Fixup(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importProjCSFromXML() */ +/************************************************************************/ + +static OGRErr importProjCSFromXML( OGRSpatialReference *poSRS, + CPLXMLNode *psCRS ) + +{ + CPLXMLNode *psSubXML; + OGRErr eErr; + +/* -------------------------------------------------------------------- */ +/* Setup the PROJCS node with a name. */ +/* -------------------------------------------------------------------- */ + poSRS->SetProjCS( CPLGetXMLValue( psCRS, "srsName", "Unnamed" ) ); + +/* -------------------------------------------------------------------- */ +/* Get authority information if available. If we got it, and */ +/* we seem to be lacking inline definition values, try and */ +/* define according to the EPSG code for the PCS. */ +/* -------------------------------------------------------------------- */ + importXMLAuthority( psCRS, poSRS, "srsID", "PROJCS" ); + + if( poSRS->GetAuthorityCode( "PROJCS" ) != NULL + && poSRS->GetAuthorityName( "PROJCS" ) != NULL + && EQUAL(poSRS->GetAuthorityName("PROJCS"),"EPSG") + && (CPLGetXMLNode( psCRS, "definedByConversion.Conversion" ) == NULL + || CPLGetXMLNode( psCRS, "baseCRS.GeographicCRS" ) == NULL) ) + { + return poSRS->importFromEPSG( atoi(poSRS->GetAuthorityCode("PROJCS")) ); + } + +/* -------------------------------------------------------------------- */ +/* Try to set the GEOGCS info. */ +/* -------------------------------------------------------------------- */ + + psSubXML = CPLGetXMLNode( psCRS, "baseCRS.GeographicCRS" ); + if( psSubXML != NULL ) + { + eErr = importGeogCSFromXML( poSRS, psSubXML ); + if( eErr != OGRERR_NONE ) + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Get the conversion node. It should be the only child of the */ +/* definedByConversion node. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psConv = NULL; + + psConv = CPLGetXMLNode( psCRS, "definedByConversion.Conversion" ); + if( psConv == NULL || psConv->eType != CXT_Element ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find a conversion node under the definedByConversion\n" + "node of the ProjectedCRS." ); + return OGRERR_CORRUPT_DATA; + } + +/* -------------------------------------------------------------------- */ +/* Determine the conversion method in effect. */ +/* -------------------------------------------------------------------- */ + int nMethod = getEPSGObjectCodeValue( CPLGetXMLNode( psConv, "usesMethod"), + "method", 0 ); + +/* -------------------------------------------------------------------- */ +/* Transverse Mercator. */ +/* -------------------------------------------------------------------- */ + if( nMethod == 9807 ) + { + poSRS->SetTM( + getProjectionParm( psConv, 8801, "Angular", 0.0 ), + getProjectionParm( psConv, 8802, "Angular", 0.0 ), + getProjectionParm( psConv, 8805, "Unitless", 1.0 ), + getProjectionParm( psConv, 8806, "Linear", 0.0 ), + getProjectionParm( psConv, 8807, "Linear", 0.0 ) ); + } + +/* -------------------------------------------------------------------- */ +/* Didn't recognise? */ +/* -------------------------------------------------------------------- */ + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Conversion method %d not recognised.", + nMethod ); + return OGRERR_CORRUPT_DATA; + } + + +/* -------------------------------------------------------------------- */ +/* Cleanup and return. */ +/* -------------------------------------------------------------------- */ + poSRS->Fixup(); + + // Need to get linear units here! + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importFromXML() */ +/************************************************************************/ + +/** + * \brief Import coordinate system from XML format (GML only currently). + * + * This method is the same as the C function OSRImportFromXML() + * @param pszXML XML string to import + * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure. + */ +OGRErr OGRSpatialReference::importFromXML( const char *pszXML ) + +{ + CPLXMLNode *psTree; + OGRErr eErr = OGRERR_UNSUPPORTED_SRS; + + this->Clear(); + +/* -------------------------------------------------------------------- */ +/* Parse the XML. */ +/* -------------------------------------------------------------------- */ + psTree = CPLParseXMLString( pszXML ); + + if( psTree == NULL ) + return OGRERR_CORRUPT_DATA; + + CPLStripXMLNamespace( psTree, "gml", TRUE ); + +/* -------------------------------------------------------------------- */ +/* Import according to the root node type. We walk through */ +/* root elements as there is sometimes prefix stuff like */ +/* . */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psNode = psTree; + + for( psNode = psTree; psNode != NULL; psNode = psNode->psNext ) + { + if( EQUAL(psNode->pszValue,"GeographicCRS") ) + { + eErr = importGeogCSFromXML( this, psNode ); + break; + } + + else if( EQUAL(psNode->pszValue,"ProjectedCRS") ) + { + eErr = importProjCSFromXML( this, psNode ); + break; + } + } + + CPLDestroyXMLNode( psTree ); + + return eErr; +} + +/************************************************************************/ +/* OSRImportFromXML() */ +/************************************************************************/ + +/** + * \brief Import coordinate system from XML format (GML only currently). + * + * This function is the same as OGRSpatialReference::importFromXML(). + */ +OGRErr OSRImportFromXML( OGRSpatialReferenceH hSRS, const char *pszXML ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromXML", CE_Failure ); + VALIDATE_POINTER1( pszXML, "OSRImportFromXML", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromXML( pszXML ); +} diff --git a/bazaar/plugin/gdal/ogr/ogr_srsnode.cpp b/bazaar/plugin/gdal/ogr/ogr_srsnode.cpp new file mode 100644 index 000000000..efde2f06d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogr_srsnode.cpp @@ -0,0 +1,972 @@ +/****************************************************************************** + * $Id: ogr_srsnode.cpp 27975 2014-11-17 12:37:48Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGR_SRSNode class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogr_srsnode.cpp 27975 2014-11-17 12:37:48Z rouault $"); + +/************************************************************************/ +/* OGR_SRSNode() */ +/************************************************************************/ + +/** + * Constructor. + * + * @param pszValueIn this optional parameter can be used to initialize + * the value of the node upon creation. If omitted the node will be created + * with a value of "". Newly created OGR_SRSNodes have no children. + */ + +OGR_SRSNode::OGR_SRSNode( const char * pszValueIn ) + +{ + pszValue = CPLStrdup( pszValueIn ); + + nChildren = 0; + papoChildNodes = NULL; + + poParent = NULL; +} + +/************************************************************************/ +/* ~OGR_SRSNode() */ +/************************************************************************/ + +OGR_SRSNode::~OGR_SRSNode() + +{ + CPLFree( pszValue ); + + ClearChildren(); +} + +/************************************************************************/ +/* ClearChildren() */ +/************************************************************************/ + +void OGR_SRSNode::ClearChildren() + +{ + for( int i = 0; i < nChildren; i++ ) + { + delete papoChildNodes[i]; + } + + CPLFree( papoChildNodes ); + + papoChildNodes = NULL; + nChildren = 0; +} + +/************************************************************************/ +/* GetChildCount() */ +/************************************************************************/ + +/** + * \fn int OGR_SRSNode::GetChildCount() const; + * + * Get number of children nodes. + * + * @return 0 for leaf nodes, or the number of children nodes. + */ + +/************************************************************************/ +/* GetChild() */ +/************************************************************************/ + +/** + * Fetch requested child. + * + * @param iChild the index of the child to fetch, from 0 to + * GetChildCount() - 1. + * + * @return a pointer to the child OGR_SRSNode, or NULL if there is no such + * child. + */ + +OGR_SRSNode *OGR_SRSNode::GetChild( int iChild ) + +{ + if( iChild < 0 || iChild >= nChildren ) + return NULL; + else + return papoChildNodes[iChild]; +} + +const OGR_SRSNode *OGR_SRSNode::GetChild( int iChild ) const + +{ + if( iChild < 0 || iChild >= nChildren ) + return NULL; + else + return papoChildNodes[iChild]; +} + +/************************************************************************/ +/* GetNode() */ +/************************************************************************/ + +/** + * Find named node in tree. + * + * This method does a pre-order traversal of the node tree searching for + * a node with this exact value (case insensitive), and returns it. Leaf + * nodes are not considered, under the assumption that they are just + * attribute value nodes. + * + * If a node appears more than once in the tree (such as UNIT for instance), + * the first encountered will be returned. Use GetNode() on a subtree to be + * more specific. + * + * @param pszName the name of the node to search for. + * + * @return a pointer to the node found, or NULL if none. + */ + +OGR_SRSNode *OGR_SRSNode::GetNode( const char * pszName ) + +{ + int i; + + if( this == NULL ) + return NULL; + + if( nChildren > 0 && EQUAL(pszName,pszValue) ) + return this; + +/* -------------------------------------------------------------------- */ +/* First we check the immediate children so we will get an */ +/* immediate child in preference to a subchild. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nChildren; i++ ) + { + if( EQUAL(papoChildNodes[i]->pszValue,pszName) + && papoChildNodes[i]->nChildren > 0 ) + return papoChildNodes[i]; + } + +/* -------------------------------------------------------------------- */ +/* Then get each child to check their children. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nChildren; i++ ) + { + OGR_SRSNode *poNode; + + poNode = papoChildNodes[i]->GetNode( pszName ); + if( poNode != NULL ) + return poNode; + } + + return NULL; +} + +const OGR_SRSNode *OGR_SRSNode::GetNode( const char * pszName ) const + +{ + return ((OGR_SRSNode *) this)->GetNode( pszName ); +} + +/************************************************************************/ +/* AddChild() */ +/************************************************************************/ + +/** + * Add passed node as a child of target node. + * + * Note that ownership of the passed node is assumed by the node on which + * the method is invoked ... use the Clone() method if the original is to + * be preserved. New children are always added at the end of the list. + * + * @param poNew the node to add as a child. + */ + +void OGR_SRSNode::AddChild( OGR_SRSNode * poNew ) + +{ + InsertChild( poNew, nChildren ); +} + +/************************************************************************/ +/* InsertChild() */ +/************************************************************************/ + +/** + * Insert the passed node as a child of target node, at the indicated + * position. + * + * Note that ownership of the passed node is assumed by the node on which + * the method is invoked ... use the Clone() method if the original is to + * be preserved. All existing children at location iChild and beyond are + * push down one space to make space for the new child. + * + * @param poNew the node to add as a child. + * @param iChild position to insert, use 0 to insert at the beginning. + */ + +void OGR_SRSNode::InsertChild( OGR_SRSNode * poNew, int iChild ) + +{ + if( iChild > nChildren ) + iChild = nChildren; + + nChildren++; + papoChildNodes = (OGR_SRSNode **) + CPLRealloc( papoChildNodes, sizeof(void*) * nChildren ); + + memmove( papoChildNodes + iChild + 1, papoChildNodes + iChild, + sizeof(void*) * (nChildren - iChild - 1) ); + + papoChildNodes[iChild] = poNew; + poNew->poParent = this; +} + +/************************************************************************/ +/* DestroyChild() */ +/************************************************************************/ + +/** + * Remove a child node, and it's subtree. + * + * Note that removing a child node will result in children after it + * being renumbered down one. + * + * @param iChild the index of the child. + */ + +void OGR_SRSNode::DestroyChild( int iChild ) + +{ + if( iChild < 0 || iChild >= nChildren ) + return; + + delete papoChildNodes[iChild]; + while( iChild < nChildren-1 ) + { + papoChildNodes[iChild] = papoChildNodes[iChild+1]; + iChild++; + } + + nChildren--; +} + +/************************************************************************/ +/* FindChild() */ +/************************************************************************/ + +/** + * Find the index of the child matching the given string. + * + * Note that the node value must match pszValue with the exception of + * case. The comparison is case insensitive. + * + * @param pszValue the node value being searched for. + * + * @return the child index, or -1 on failure. + */ + +int OGR_SRSNode::FindChild( const char * pszValue ) const + +{ + for( int i = 0; i < nChildren; i++ ) + { + if( EQUAL(papoChildNodes[i]->pszValue,pszValue) ) + return i; + } + + return -1; +} + +/************************************************************************/ +/* GetValue() */ +/************************************************************************/ + +/** + * \fn const char *OGR_SRSNode::GetValue() const; + * + * Fetch value string for this node. + * + * @return A non-NULL string is always returned. The returned pointer is to + * the internal value of this node, and should not be modified, or freed. + */ + +/************************************************************************/ +/* SetValue() */ +/************************************************************************/ + +/** + * Set the node value. + * + * @param pszNewValue the new value to assign to this node. The passed + * string is duplicated and remains the responsibility of the caller. + */ + +void OGR_SRSNode::SetValue( const char * pszNewValue ) + +{ + CPLFree( pszValue ); + pszValue = CPLStrdup( pszNewValue ); +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +/** + * Make a duplicate of this node, and it's children. + * + * @return a new node tree, which becomes the responsiblity of the caller. + */ + +OGR_SRSNode *OGR_SRSNode::Clone() const + +{ + OGR_SRSNode *poNew; + + poNew = new OGR_SRSNode( pszValue ); + + for( int i = 0; i < nChildren; i++ ) + { + poNew->AddChild( papoChildNodes[i]->Clone() ); + } + + return poNew; +} + +/************************************************************************/ +/* NeedsQuoting() */ +/* */ +/* Does this node need to be quoted when it is exported to Wkt? */ +/************************************************************************/ + +int OGR_SRSNode::NeedsQuoting() const + +{ + // non-terminals are never quoted. + if( GetChildCount() != 0 ) + return FALSE; + + // As per bugzilla bug 201, the OGC spec says the authority code + // needs to be quoted even though it appears well behaved. + if( poParent != NULL && EQUAL(poParent->GetValue(),"AUTHORITY") ) + return TRUE; + + // As per bugzilla bug 294, the OGC spec says the direction + // values for the AXIS keywords should *not* be quoted. + if( poParent != NULL && EQUAL(poParent->GetValue(),"AXIS") + && this != poParent->GetChild(0) ) + return FALSE; + + // Strings starting with e or E are not valid numeric values, so they + // need quoting, like in AXIS["E",EAST] + if( (pszValue[0] == 'e' || pszValue[0] == 'E') ) + return TRUE; + + // Non-numeric tokens are generally quoted while clean numeric values + // are generally not. + for( int i = 0; pszValue[i] != '\0'; i++ ) + { + if( (pszValue[i] < '0' || pszValue[i] > '9') + && pszValue[i] != '.' + && pszValue[i] != '-' && pszValue[i] != '+' + && pszValue[i] != 'e' && pszValue[i] != 'E' ) + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* exportToWkt() */ +/************************************************************************/ + +/** + * Convert this tree of nodes into WKT format. + * + * Note that the returned WKT string should be freed with OGRFree() or + * CPLFree() when no longer needed. It is the responsibility of the caller. + * + * @param ppszResult the resulting string is returned in this pointer. + * + * @return currently OGRERR_NONE is always returned, but the future it + * is possible error conditions will develop. + */ + + +OGRErr OGR_SRSNode::exportToWkt( char ** ppszResult ) const + +{ + char **papszChildrenWkt = NULL; + int nLength = strlen(pszValue)+4; + int i; + +/* -------------------------------------------------------------------- */ +/* Build a list of the WKT format for the children. */ +/* -------------------------------------------------------------------- */ + papszChildrenWkt = (char **) CPLCalloc(sizeof(char*),(nChildren+1)); + + for( i = 0; i < nChildren; i++ ) + { + papoChildNodes[i]->exportToWkt( papszChildrenWkt + i ); + nLength += strlen(papszChildrenWkt[i]) + 1; + } + +/* -------------------------------------------------------------------- */ +/* Allocate the result string. */ +/* -------------------------------------------------------------------- */ + *ppszResult = (char *) CPLMalloc(nLength); + *ppszResult[0] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Capture this nodes value. We put it in double quotes if */ +/* this is a leaf node, otherwise we assume it is a well formed */ +/* node name. */ +/* -------------------------------------------------------------------- */ + if( NeedsQuoting() ) + { + strcat( *ppszResult, "\"" ); + strcat( *ppszResult, pszValue ); /* should we do quoting? */ + strcat( *ppszResult, "\"" ); + } + else + strcat( *ppszResult, pszValue ); + +/* -------------------------------------------------------------------- */ +/* Add the children strings with appropriate brackets and commas. */ +/* -------------------------------------------------------------------- */ + if( nChildren > 0 ) + strcat( *ppszResult, "[" ); + + for( i = 0; i < nChildren; i++ ) + { + strcat( *ppszResult, papszChildrenWkt[i] ); + if( i == nChildren-1 ) + strcat( *ppszResult, "]" ); + else + strcat( *ppszResult, "," ); + } + + CSLDestroy( papszChildrenWkt ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* exportToPrettyWkt() */ +/************************************************************************/ + +OGRErr OGR_SRSNode::exportToPrettyWkt( char ** ppszResult, int nDepth ) const + +{ + char **papszChildrenWkt = NULL; + int nLength = strlen(pszValue)+4; + int i; + +/* -------------------------------------------------------------------- */ +/* Build a list of the WKT format for the children. */ +/* -------------------------------------------------------------------- */ + papszChildrenWkt = (char **) CPLCalloc(sizeof(char*),(nChildren+1)); + + for( i = 0; i < nChildren; i++ ) + { + papoChildNodes[i]->exportToPrettyWkt( papszChildrenWkt + i, + nDepth + 1); + nLength += strlen(papszChildrenWkt[i]) + 2 + nDepth*4; + } + +/* -------------------------------------------------------------------- */ +/* Allocate the result string. */ +/* -------------------------------------------------------------------- */ + *ppszResult = (char *) CPLMalloc(nLength); + *ppszResult[0] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Capture this nodes value. We put it in double quotes if */ +/* this is a leaf node, otherwise we assume it is a well formed */ +/* node name. */ +/* -------------------------------------------------------------------- */ + if( NeedsQuoting() ) + { + strcat( *ppszResult, "\"" ); + strcat( *ppszResult, pszValue ); /* should we do quoting? */ + strcat( *ppszResult, "\"" ); + } + else + strcat( *ppszResult, pszValue ); + +/* -------------------------------------------------------------------- */ +/* Add the children strings with appropriate brackets and commas. */ +/* -------------------------------------------------------------------- */ + if( nChildren > 0 ) + strcat( *ppszResult, "[" ); + + for( i = 0; i < nChildren; i++ ) + { + if( papoChildNodes[i]->GetChildCount() > 0 ) + { + int j; + + strcat( *ppszResult, "\n" ); + for( j = 0; j < 4*nDepth; j++ ) + strcat( *ppszResult, " " ); + } + strcat( *ppszResult, papszChildrenWkt[i] ); + if( i < nChildren-1 ) + strcat( *ppszResult, "," ); + } + + if( nChildren > 0 ) + { + if( (*ppszResult)[strlen(*ppszResult)-1] == ',' ) + (*ppszResult)[strlen(*ppszResult)-1] = '\0'; + + strcat( *ppszResult, "]" ); + } + + CSLDestroy( papszChildrenWkt ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importFromWkt() */ +/************************************************************************/ + +/** + * Import from WKT string. + * + * This method will wipe the existing children and value of this node, and + * reassign them based on the contents of the passed WKT string. Only as + * much of the input string as needed to construct this node, and it's + * children is consumed from the input string, and the input string pointer + * is then updated to point to the remaining (unused) input. + * + * @param ppszInput Pointer to pointer to input. The pointer is updated to + * point to remaining unused input text. + * + * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it + * fails for any reason. + */ + +OGRErr OGR_SRSNode::importFromWkt( char ** ppszInput ) + +{ + int nNodes = 0; + return importFromWkt( ppszInput, 0, &nNodes ); +} + +OGRErr OGR_SRSNode::importFromWkt( char ** ppszInput, int nRecLevel, int* pnNodes ) + +{ + const char *pszInput = *ppszInput; + int bInQuotedString = FALSE; + + /* Sanity checks */ + if( nRecLevel == 10 ) + { + return OGRERR_CORRUPT_DATA; + } + if( *pnNodes == 1000 ) + { + return OGRERR_CORRUPT_DATA; + } + +/* -------------------------------------------------------------------- */ +/* Clear any existing children of this node. */ +/* -------------------------------------------------------------------- */ + ClearChildren(); + +/* -------------------------------------------------------------------- */ +/* Read the ``value'' for this node. */ +/* -------------------------------------------------------------------- */ + char szToken[512]; + int nTokenLen = 0; + + while( *pszInput != '\0' && nTokenLen < (int) sizeof(szToken)-1 ) + { + if( *pszInput == '"' ) + { + bInQuotedString = !bInQuotedString; + } + else if( !bInQuotedString + && (*pszInput == '[' || *pszInput == ']' || *pszInput == ',' + || *pszInput == '(' || *pszInput == ')' ) ) + { + break; + } + else if( !bInQuotedString + && (*pszInput == ' ' || *pszInput == '\t' + || *pszInput == 10 || *pszInput == 13) ) + { + /* just skip over whitespace */ + } + else + { + szToken[nTokenLen++] = *pszInput; + } + + pszInput++; + } + + if( *pszInput == '\0' || nTokenLen == sizeof(szToken) - 1 ) + return OGRERR_CORRUPT_DATA; + + szToken[nTokenLen++] = '\0'; + SetValue( szToken ); + +/* -------------------------------------------------------------------- */ +/* Read children, if we have a sublist. */ +/* -------------------------------------------------------------------- */ + if( *pszInput == '[' || *pszInput == '(' ) + { + do + { + OGR_SRSNode *poNewChild; + OGRErr eErr; + + pszInput++; // Skip bracket or comma. + + poNewChild = new OGR_SRSNode(); + + (*pnNodes) ++; + eErr = poNewChild->importFromWkt( (char **) &pszInput, nRecLevel + 1, pnNodes ); + if( eErr != OGRERR_NONE ) + { + delete poNewChild; + return eErr; + } + + AddChild( poNewChild ); + + // swallow whitespace + while( isspace(*pszInput) ) + pszInput++; + + } while( *pszInput == ',' ); + + if( *pszInput != ')' && *pszInput != ']' ) + return OGRERR_CORRUPT_DATA; + + pszInput++; + } + + *ppszInput = (char *) pszInput; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* MakeValueSafe() */ +/************************************************************************/ + +/** + * Massage value string, stripping special characters so it will be a + * database safe string. + * + * The operation is also applies to all subnodes of the current node. + */ + + +void OGR_SRSNode::MakeValueSafe() + +{ + int i, j; + +/* -------------------------------------------------------------------- */ +/* First process subnodes. */ +/* -------------------------------------------------------------------- */ + for( int iChild = 0; iChild < GetChildCount(); iChild++ ) + { + GetChild(iChild)->MakeValueSafe(); + } + +/* -------------------------------------------------------------------- */ +/* Skip numeric nodes. */ +/* -------------------------------------------------------------------- */ + if( (pszValue[0] >= '0' && pszValue[0] <= '9') || pszValue[0] != '.' ) + return; + +/* -------------------------------------------------------------------- */ +/* Translate non-alphanumeric values to underscores. */ +/* -------------------------------------------------------------------- */ + for( i = 0; pszValue[i] != '\0'; i++ ) + { + if( !(pszValue[i] >= 'A' && pszValue[i] <= 'Z') + && !(pszValue[i] >= 'a' && pszValue[i] <= 'z') + && !(pszValue[i] >= '0' && pszValue[i] <= '9') ) + { + pszValue[i] = '_'; + } + } + +/* -------------------------------------------------------------------- */ +/* Remove repeated and trailing underscores. */ +/* -------------------------------------------------------------------- */ + for( i = 1, j = 0; pszValue[i] != '\0'; i++ ) + { + if( pszValue[j] == '_' && pszValue[i] == '_' ) + continue; + + pszValue[++j] = pszValue[i]; + } + + if( pszValue[j] == '_' ) + pszValue[j] = '\0'; + else + pszValue[j+1] = '\0'; +} + +/************************************************************************/ +/* applyRemapper() */ +/************************************************************************/ + +/** + * Remap node values matching list. + * + * Remap the value of this node or any of it's children if it matches + * one of the values in the source list to the corresponding value from + * the destination list. If the pszNode value is set, only do so if the + * parent node matches that value. Even if a replacement occurs, searching + * continues. + * + * @param pszNode Restrict remapping to children of this type of node + * (eg. "PROJECTION") + * @param papszSrcValues a NULL terminated array of source string. If the + * node value matches one of these (case insensitive) then replacement occurs. + * @param papszDstValues an array of destination strings. On a match, the + * one corresponding to a source value will be used to replace a node. + * @param nStepSize increment when stepping through source and destination + * arrays, allowing source and destination arrays to be one interleaved array + * for instances. Defaults to 1. + * @param bChildOfHit Only TRUE if we the current node is the child of a match, + * and so needs to be set. Application code would normally pass FALSE for this + * argument. + * + * @return returns OGRERR_NONE unless something bad happens. There is no + * indication returned about whether any replacement occured. + */ + +OGRErr OGR_SRSNode::applyRemapper( const char *pszNode, + char **papszSrcValues, + char **papszDstValues, + int nStepSize, int bChildOfHit ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Scan for value, and replace if our parent was a "hit". */ +/* -------------------------------------------------------------------- */ + if( bChildOfHit || pszNode == NULL ) + { + for( i = 0; papszSrcValues[i] != NULL; i += nStepSize ) + { + if( EQUAL(papszSrcValues[i],pszValue) && + ! EQUAL(papszDstValues[i],"") ) + { + SetValue( papszDstValues[i] ); + break; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Are the the target node? */ +/* -------------------------------------------------------------------- */ + if( pszNode != NULL ) + bChildOfHit = EQUAL(pszValue,pszNode); + +/* -------------------------------------------------------------------- */ +/* Recurse */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < GetChildCount(); i++ ) + { + GetChild(i)->applyRemapper( pszNode, papszSrcValues, + papszDstValues, nStepSize, bChildOfHit ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* StripNodes() */ +/************************************************************************/ + +/** + * Strip child nodes matching name. + * + * Removes any decendent nodes of this node that match the given name. + * Of course children of removed nodes are also discarded. + * + * @param pszName the name for nodes that should be removed. + */ + +void OGR_SRSNode::StripNodes( const char * pszName ) + +{ +/* -------------------------------------------------------------------- */ +/* Strip any children matching this name. */ +/* -------------------------------------------------------------------- */ + while( FindChild( pszName ) >= 0 ) + DestroyChild( FindChild( pszName ) ); + +/* -------------------------------------------------------------------- */ +/* Recurse */ +/* -------------------------------------------------------------------- */ + for( int i = 0; i < GetChildCount(); i++ ) + GetChild(i)->StripNodes( pszName ); +} + +/************************************************************************/ +/* FixupOrdering() */ +/************************************************************************/ + +/* EXTENSION ... being a OSR extension... is arbitrary placed before the AUTHORITY */ +static const char * const apszPROJCSRule[] = +{ "PROJCS", "GEOGCS", "PROJECTION", "PARAMETER", "UNIT", "AXIS", "EXTENSION", "AUTHORITY", + NULL }; + +static const char * const apszDATUMRule[] = +{ "DATUM", "SPHEROID", "TOWGS84", "EXTENSION", "AUTHORITY", NULL }; + +static const char * const apszGEOGCSRule[] = +{ "GEOGCS", "DATUM", "PRIMEM", "UNIT", "AXIS", "EXTENSION", "AUTHORITY", NULL }; + +static const char * const apszGEOCCSRule[] = +{ "GEOCCS", "DATUM", "PRIMEM", "UNIT", "AXIS", "AUTHORITY", NULL }; + +static const char * const apszVERTCSRule[] = +{ "VERT_CS", "VERT_DATUM", "UNIT", "AXIS", "EXTENSION", "AUTHORITY", NULL }; + +static const char * const *apszOrderingRules[] = { + apszPROJCSRule, apszGEOGCSRule, apszDATUMRule, apszGEOCCSRule, apszVERTCSRule, NULL }; + +/** + * Correct parameter ordering to match CT Specification. + * + * Some mechanisms to create WKT using OGRSpatialReference, and some + * imported WKT fail to maintain the order of parameters required according + * to the BNF definitions in the OpenGIS SF-SQL and CT Specifications. This + * method attempts to massage things back into the required order. + * + * This method will reorder the children of the node it is invoked on and + * then recurse to all children to fix up their children. + * + * @return OGRERR_NONE on success or an error code if something goes + * wrong. + */ + +OGRErr OGR_SRSNode::FixupOrdering() + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Recurse ordering children. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < GetChildCount(); i++ ) + GetChild(i)->FixupOrdering(); + + if( GetChildCount() < 3 ) + return OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* Is this a node for which an ordering rule exists? */ +/* -------------------------------------------------------------------- */ + const char * const * papszRule = NULL; + + for( i = 0; apszOrderingRules[i] != NULL; i++ ) + { + if( EQUAL(apszOrderingRules[i][0],pszValue) ) + { + papszRule = apszOrderingRules[i] + 1; + break; + } + } + + if( papszRule == NULL ) + return OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* If we have a rule, apply it. We create an array */ +/* (panChildPr) with the priority code for each child (derived */ +/* from the rule) and we then bubble sort based on this. */ +/* -------------------------------------------------------------------- */ + int *panChildKey = (int *) CPLCalloc(sizeof(int),GetChildCount()); + + for( i = 1; i < GetChildCount(); i++ ) + { + panChildKey[i] = CSLFindString( (char**) papszRule, + GetChild(i)->GetValue() ); + if( panChildKey[i] == -1 ) + { + CPLDebug( "OGRSpatialReference", + "Found unexpected key %s when trying to order SRS nodes.", + GetChild(i)->GetValue() ); + } + } + +/* -------------------------------------------------------------------- */ +/* Sort - Note we don't try to do anything with the first child */ +/* which we assume is a name string. */ +/* -------------------------------------------------------------------- */ + int j, bChange = TRUE; + + for( i = 1; bChange && i < GetChildCount()-1; i++ ) + { + bChange = FALSE; + for( j = 1; j < GetChildCount()-i; j++ ) + { + if( panChildKey[j] == -1 || panChildKey[j+1] == -1 ) + continue; + + if( panChildKey[j] > panChildKey[j+1] ) + { + OGR_SRSNode *poTemp = papoChildNodes[j]; + int nKeyTemp = panChildKey[j]; + + papoChildNodes[j] = papoChildNodes[j+1]; + papoChildNodes[j+1] = poTemp; + + nKeyTemp = panChildKey[j]; + panChildKey[j] = panChildKey[j+1]; + panChildKey[j+1] = nKeyTemp; + + bChange = TRUE; + } + } + } + + CPLFree( panChildKey ); + + return OGRERR_NONE; +} + + diff --git a/bazaar/plugin/gdal/ogr/ograpispy.cpp b/bazaar/plugin/gdal/ogr/ograpispy.cpp new file mode 100644 index 000000000..b0f396967 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ograpispy.cpp @@ -0,0 +1,1085 @@ +/****************************************************************************** + * $Id: ograpispy.cpp 28808 2015-03-28 15:01:36Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: OGR C API "Spy" + * Author: Even Rouault, even.rouault at spatialys.com + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_port.h" + +#include +#include +#include + +#include "ograpispy.h" + +#include "gdal.h" +#include "ogr_spatialref.h" +#include "ogr_geometry.h" +#include "ogr_feature.h" +#include "ogrsf_frmts.h" +#include "cpl_string.h" + +#ifdef OGRAPISPY_ENABLED + +int bOGRAPISpyEnabled = FALSE; +static CPLString osSnapshotPath, osSpyFile; +static FILE* fpSpyFile = NULL; +extern "C" int CPL_DLL GDALIsInGlobalDestructor(void); + +class LayerDescription +{ + public: + int iLayer; + + LayerDescription(): iLayer(-1) {} + LayerDescription(int iLayer): iLayer(iLayer) {} +}; + +class DatasetDescription +{ + public: + int iDS; + std::map oMapLayer; + + DatasetDescription() : iDS(-1) {} + DatasetDescription(int iDS) : iDS(iDS) {} + ~DatasetDescription(); +}; + +class FeatureDefnDescription +{ + public: + OGRFeatureDefnH hFDefn; + int iUniqueNumber; + std::map oMapFieldDefn; + std::map oMapGeomFieldDefn; + + FeatureDefnDescription(): hFDefn(NULL), iUniqueNumber(-1) {} + FeatureDefnDescription(OGRFeatureDefnH hFDefn, int iUniqueNumber): hFDefn(hFDefn), iUniqueNumber(iUniqueNumber) {} + void Free(); +}; + +static std::map oMapDS; +static std::map oGlobalMapLayer; +static OGRLayerH hLayerGetNextFeature = NULL; +static OGRLayerH hLayerGetLayerDefn = NULL; +static int bDeferGetFieldCount = FALSE; +static int nGetNextFeatureCalls = 0; +static std::set aoSetCreatedDS; +static std::map oMapFDefn; +static std::map oGlobalMapGeomFieldDefn; +static std::map oGlobalMapFieldDefn; + +void FeatureDefnDescription::Free() +{ + { + std::map::iterator oIter = oMapGeomFieldDefn.begin(); + for(; oIter != oMapGeomFieldDefn.end(); ++oIter) + oGlobalMapGeomFieldDefn.erase(oIter->first); + } + { + std::map::iterator oIter = oMapFieldDefn.begin(); + for(; oIter != oMapFieldDefn.end(); ++oIter) + oGlobalMapFieldDefn.erase(oIter->first); + } +} + +DatasetDescription::~DatasetDescription() +{ + std::map::iterator oIter = oMapLayer.begin(); + for(; oIter != oMapLayer.end(); ++oIter) + oGlobalMapLayer.erase(oIter->first); +} + +static void OGRAPISpyFileReopen() +{ + if( fpSpyFile == NULL ) + { + fpSpyFile = fopen(osSpyFile, "ab"); + if( fpSpyFile == NULL ) + fpSpyFile = stderr; + } +} + +static void OGRAPISpyFileClose() +{ + if( fpSpyFile != stdout && fpSpyFile != stderr ) + { + fclose(fpSpyFile); + fpSpyFile = NULL; + } +} + +static int OGRAPISpyEnabled() +{ + const char* pszSpyFile = CPLGetConfigOption("OGR_API_SPY_FILE", NULL); + bOGRAPISpyEnabled = (pszSpyFile != NULL); + if( !bOGRAPISpyEnabled ) + { + osSpyFile.resize(0); + aoSetCreatedDS.clear(); + return FALSE; + } + if( osSpyFile.size() ) + return TRUE; + + osSpyFile = pszSpyFile; + + const char* pszSnapshotPath = CPLGetConfigOption("OGR_API_SPY_SNAPSHOT_PATH", "."); + if( EQUAL(pszSnapshotPath, "NO") ) + osSnapshotPath = ""; + else + osSnapshotPath = pszSnapshotPath; + + if( EQUAL(pszSpyFile, "stdout") ) + fpSpyFile = stdout; + else if( EQUAL(pszSpyFile, "stderr") ) + fpSpyFile = stderr; + else + fpSpyFile = fopen(pszSpyFile, "wb"); + if( fpSpyFile == NULL ) + fpSpyFile = stderr; + + fprintf(fpSpyFile, "# This file is generated by the OGR_API_SPY mechanism.\n"); + fprintf(fpSpyFile, "from osgeo import ogr\n"); + fprintf(fpSpyFile, "from osgeo import osr\n"); + fprintf(fpSpyFile, "import os\n"); + fprintf(fpSpyFile, "import shutil\n"); + fprintf(fpSpyFile, "os.access\n"); // to make pyflakes happy in case it's unused later + fprintf(fpSpyFile, "shutil.copy\n"); // same here + fprintf(fpSpyFile, "\n"); + + return TRUE; +} + +static CPLString OGRAPISpyGetOptions(char** papszOptions) +{ + CPLString options; + if( papszOptions == NULL ) + { + options = "[]"; + } + else + { + options = "["; + for(char** papszIter = papszOptions; *papszIter != NULL; papszIter++) + { + if( papszIter != papszOptions ) + options += ", "; + options += "'"; + options += *papszIter; + options += "'"; + } + options += "]"; + } + + return options; +} + +static CPLString OGRAPISpyGetString(const char* pszStr) +{ + if( pszStr == NULL ) + return "None"; + CPLString osRet = "'"; + while( *pszStr ) + { + if( *pszStr == '\'' ) + osRet += "\\'"; + else if( *pszStr == '\\' ) + osRet += "\\\\"; + else + osRet += *pszStr; + pszStr ++; + } + osRet += "'"; + return osRet; +} + +static CPLString OGRAPISpyGetDSVar(OGRDataSourceH hDS) +{ + if( hDS && oMapDS.find(hDS) == oMapDS.end() ) + { + int i = (int)oMapDS.size() + 1; + oMapDS[hDS] = DatasetDescription(i); + } + return CPLSPrintf("ds%d", (hDS) ? oMapDS[hDS].iDS : 0); +} + +static CPLString OGRAPISpyGetLayerVar(OGRLayerH hLayer) +{ + return oGlobalMapLayer[hLayer]; +} + +static CPLString OGRAPISpyGetAndRegisterLayerVar(OGRDataSourceH hDS, + OGRLayerH hLayer) +{ + DatasetDescription& dd = oMapDS[hDS]; + if( hLayer && dd.oMapLayer.find(hLayer) == dd.oMapLayer.end() ) + { + int i = (int)dd.oMapLayer.size() + 1; + dd.oMapLayer[hLayer] = i; + oGlobalMapLayer[hLayer] = OGRAPISpyGetDSVar(hDS) + "_" + CPLSPrintf("lyr%d", i); + } + return OGRAPISpyGetDSVar(hDS) + "_" + + CPLSPrintf("lyr%d", (hLayer) ? dd.oMapLayer[hLayer].iLayer : 0); +} + +static CPLString OGRAPISpyGetSRS(OGRSpatialReferenceH hSpatialRef) +{ + if (hSpatialRef == NULL) + return "None"; + + char* pszWKT = NULL; + ((OGRSpatialReference*)hSpatialRef)->exportToWkt(&pszWKT); + const char* pszRet = CPLSPrintf("osr.SpatialReference(\"\"\"%s\"\"\")", pszWKT); + CPLFree(pszWKT); + return pszRet; +} + +static CPLString OGRAPISpyGetGeom(OGRGeometryH hGeom) +{ + if (hGeom == NULL) + return "None"; + + char* pszWKT = NULL; + ((OGRGeometry*)hGeom)->exportToWkt(&pszWKT); + const char* pszRet = CPLSPrintf("ogr.CreateGeometryFromWkt('%s')", pszWKT); + CPLFree(pszWKT); + return pszRet; +} + +#define casePrefixOgrDot(x) case x: return "ogr." #x; + +static CPLString OGRAPISpyGetGeomType(OGRwkbGeometryType eType) +{ + switch(eType) + { + casePrefixOgrDot(wkbUnknown) + casePrefixOgrDot(wkbPoint) + casePrefixOgrDot(wkbLineString) + casePrefixOgrDot(wkbPolygon) + casePrefixOgrDot(wkbMultiPoint) + casePrefixOgrDot(wkbMultiLineString) + casePrefixOgrDot(wkbMultiPolygon) + casePrefixOgrDot(wkbGeometryCollection) + casePrefixOgrDot(wkbCircularString) + casePrefixOgrDot(wkbCompoundCurve) + casePrefixOgrDot(wkbCurvePolygon) + casePrefixOgrDot(wkbMultiCurve) + casePrefixOgrDot(wkbMultiSurface) + casePrefixOgrDot(wkbNone) + casePrefixOgrDot(wkbLinearRing) + casePrefixOgrDot(wkbCircularStringZ) + casePrefixOgrDot(wkbCompoundCurveZ) + casePrefixOgrDot(wkbCurvePolygonZ) + casePrefixOgrDot(wkbMultiCurveZ) + casePrefixOgrDot(wkbMultiSurfaceZ) + casePrefixOgrDot(wkbPoint25D) + casePrefixOgrDot(wkbLineString25D) + casePrefixOgrDot(wkbPolygon25D) + casePrefixOgrDot(wkbMultiPoint25D) + casePrefixOgrDot(wkbMultiLineString25D) + casePrefixOgrDot(wkbMultiPolygon25D) + casePrefixOgrDot(wkbGeometryCollection25D) + } + return "error"; +} + +static CPLString OGRAPISpyGetFieldType(OGRFieldType eType) +{ + switch(eType) + { + casePrefixOgrDot(OFTInteger) + casePrefixOgrDot(OFTInteger64) + casePrefixOgrDot(OFTIntegerList) + casePrefixOgrDot(OFTInteger64List) + casePrefixOgrDot(OFTReal) + casePrefixOgrDot(OFTRealList) + casePrefixOgrDot(OFTString) + casePrefixOgrDot(OFTStringList) + casePrefixOgrDot(OFTWideString) + casePrefixOgrDot(OFTWideStringList) + casePrefixOgrDot(OFTBinary) + casePrefixOgrDot(OFTDate) + casePrefixOgrDot(OFTTime) + casePrefixOgrDot(OFTDateTime) + } + return "error"; +} + +static CPLString OGRAPISpyGetFeatureDefnVar(OGRFeatureDefnH hFDefn) +{ + std::map::iterator oIter = oMapFDefn.find(hFDefn); + int i; + if( oIter == oMapFDefn.end() ) + { + i = (int)oMapFDefn.size() + 1; + oMapFDefn[hFDefn] = FeatureDefnDescription(hFDefn, i); + ((OGRFeatureDefn*)hFDefn)->Reference(); // so that we can check when they are no longer used + } + else + i = oIter->second.iUniqueNumber; + return CPLSPrintf("fdefn%d", i); +} + +static void OGRAPISpyFlushDefered() +{ + OGRAPISpyFileReopen(); + if( hLayerGetLayerDefn != NULL ) + { + OGRFeatureDefnH hDefn = (OGRFeatureDefnH)(((OGRLayer*)hLayerGetLayerDefn)->GetLayerDefn()); + fprintf(fpSpyFile, "%s = %s.GetLayerDefn()\n", + OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), + OGRAPISpyGetLayerVar(hLayerGetLayerDefn).c_str()); + + if( bDeferGetFieldCount ) + { + fprintf(fpSpyFile, "%s.GetFieldCount()\n", + OGRAPISpyGetFeatureDefnVar(hDefn).c_str()); + bDeferGetFieldCount = FALSE; + } + + hLayerGetLayerDefn = NULL; + } + + if( nGetNextFeatureCalls == 1) + { + fprintf(fpSpyFile, "%s.GetNextFeature()\n", + OGRAPISpyGetLayerVar(hLayerGetNextFeature).c_str()); + hLayerGetNextFeature = NULL; + nGetNextFeatureCalls = 0; + } + else if( nGetNextFeatureCalls > 0) + { + fprintf(fpSpyFile, "for i in range(%d):\n", nGetNextFeatureCalls); + fprintf(fpSpyFile, " %s.GetNextFeature()\n", + OGRAPISpyGetLayerVar(hLayerGetNextFeature).c_str()); + hLayerGetNextFeature = NULL; + nGetNextFeatureCalls = 0; + } +} + +int OGRAPISpyOpenTakeSnapshot(const char* pszName, int bUpdate) +{ + if( !OGRAPISpyEnabled() || !bUpdate || osSnapshotPath.size() == 0 || + aoSetCreatedDS.find(pszName) != aoSetCreatedDS.end() ) + return -1; + OGRAPISpyFlushDefered(); + + VSIStatBufL sStat; + if( VSIStatL( pszName, &sStat ) == 0 ) + { + GDALDatasetH hDS = GDALOpenEx(pszName, GDAL_OF_VECTOR, NULL, NULL, NULL); + if( hDS ) + { + char** papszFileList = ((GDALDataset*)hDS)->GetFileList(); + GDALClose(hDS); + if( papszFileList ) + { + int i = 1; + CPLString osBaseDir; + CPLString osSrcDir; + CPLString osWorkingDir; + while(TRUE) + { + osBaseDir = CPLFormFilename(osSnapshotPath, + CPLSPrintf("snapshot_%d", i), NULL ); + if( VSIStatL( osBaseDir, &sStat ) != 0 ) + break; + i++; + } + VSIMkdir( osBaseDir, 0777 ); + osSrcDir = CPLFormFilename( osBaseDir, "source", NULL ); + VSIMkdir( osSrcDir, 0777 ); + osWorkingDir = CPLFormFilename( osBaseDir, "working", NULL ); + VSIMkdir( osWorkingDir, 0777 ); + fprintf(fpSpyFile, "# Take snapshot of %s\n", pszName); + fprintf(fpSpyFile, "try:\n"); + fprintf(fpSpyFile, " shutil.rmtree('%s')\n", osWorkingDir.c_str()); + fprintf(fpSpyFile, "except:\n"); + fprintf(fpSpyFile, " pass\n"); + fprintf(fpSpyFile, "os.mkdir('%s')\n", osWorkingDir.c_str()); + for(char** papszIter = papszFileList; *papszIter; papszIter++) + { + CPLString osSnapshotSrcFile = CPLFormFilename( + osSrcDir, CPLGetFilename(*papszIter), NULL); + CPLString osSnapshotWorkingFile = CPLFormFilename( + osWorkingDir, CPLGetFilename(*papszIter), NULL); + CPLCopyFile( osSnapshotSrcFile, *papszIter ); + CPLCopyFile( osSnapshotWorkingFile, *papszIter ); + fprintf(fpSpyFile, "shutil.copy('%s', '%s')\n", + osSnapshotSrcFile.c_str(), + osSnapshotWorkingFile.c_str()); + } + CSLDestroy(papszFileList); + return i; + } + } + } + return -1; +} + +void OGRAPISpyOpen(const char* pszName, int bUpdate, int iSnapshot, GDALDatasetH* phDS) +{ + if( !OGRAPISpyEnabled() ) return; + OGRAPISpyFlushDefered(); + + CPLString osName; + if( iSnapshot > 0 ) + { + CPLString osBaseDir = CPLFormFilename(osSnapshotPath, + CPLSPrintf("snapshot_%d", iSnapshot), NULL ); + CPLString osWorkingDir = CPLFormFilename( osBaseDir, "working", NULL ); + osName = CPLFormFilename(osWorkingDir, CPLGetFilename(pszName), NULL); + pszName = osName.c_str(); + + if( *phDS != NULL ) + { + GDALClose( (GDALDatasetH) *phDS ); + *phDS = GDALOpenEx(pszName, GDAL_OF_VECTOR | GDAL_OF_UPDATE, NULL, NULL, NULL); + } + } + + if( *phDS != NULL ) + fprintf(fpSpyFile, "%s = ", OGRAPISpyGetDSVar((OGRDataSourceH) *phDS).c_str()); + fprintf(fpSpyFile, "ogr.Open(%s, update = %d)\n", + OGRAPISpyGetString(pszName).c_str(), bUpdate); + OGRAPISpyFileClose(); +} + +void OGRAPISpyPreClose(OGRDataSourceH hDS) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "ds%d = None\n", oMapDS[hDS].iDS); + oMapDS.erase(hDS); + OGRAPISpyFileClose(); +} + +void OGRAPISpyPostClose(CPL_UNUSED OGRDataSourceH hDS) +{ + if( !GDALIsInGlobalDestructor() ) + { + std::map::iterator oIter = + oMapFDefn.begin(); + std::vector oArray; + for(; oIter != oMapFDefn.end(); ++oIter) + { + FeatureDefnDescription& featureDefnDescription = oIter->second; + if( ((OGRFeatureDefn*)featureDefnDescription.hFDefn)->GetReferenceCount() == 1 ) + { + oArray.push_back(featureDefnDescription.hFDefn); + } + } + for(size_t i = 0; i < oArray.size(); i++) + { + FeatureDefnDescription& featureDefnDescription = oMapFDefn[oArray[i]]; + ((OGRFeatureDefn*)featureDefnDescription.hFDefn)->Release(); + featureDefnDescription.Free(); + oMapFDefn.erase(oArray[i]); + } + } +} + +void OGRAPISpyCreateDataSource(OGRSFDriverH hDriver, const char* pszName, + char** papszOptions, OGRDataSourceH hDS) +{ + if( !OGRAPISpyEnabled() ) return; + OGRAPISpyFlushDefered(); + if( hDS != NULL ) + fprintf(fpSpyFile, "%s = ", OGRAPISpyGetDSVar(hDS).c_str()); + fprintf(fpSpyFile, "ogr.GetDriverByName('%s').CreateDataSource(%s, options = %s)\n", + GDALGetDriverShortName((GDALDriverH)hDriver), + OGRAPISpyGetString(pszName).c_str(), + OGRAPISpyGetOptions(papszOptions).c_str()); + if( hDS != NULL ) + { + aoSetCreatedDS.insert(pszName); + } + OGRAPISpyFileClose(); +} + +void OGRAPISpyDeleteDataSource(OGRSFDriverH hDriver, const char* pszName) +{ + if( !OGRAPISpyEnabled() ) return; + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "ogr.GetDriverByName('%s').DeleteDataSource(%s)\n", + GDALGetDriverShortName((GDALDriverH)hDriver), + OGRAPISpyGetString(pszName).c_str()); + aoSetCreatedDS.erase(pszName); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_DS_GetLayer( OGRDataSourceH hDS, int iLayer, OGRLayerH hLayer ) +{ + OGRAPISpyFlushDefered(); + if( hLayer != NULL ) + fprintf(fpSpyFile, "%s = ", + OGRAPISpyGetAndRegisterLayerVar(hDS, hLayer).c_str()); + fprintf(fpSpyFile, "%s.GetLayer(%d)\n", + OGRAPISpyGetDSVar(hDS).c_str(), + iLayer); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_DS_GetLayerCount( OGRDataSourceH hDS ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.GetLayerCount()\n", OGRAPISpyGetDSVar(hDS).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_DS_GetLayerByName( OGRDataSourceH hDS, const char* pszLayerName, + OGRLayerH hLayer ) +{ + OGRAPISpyFlushDefered(); + if( hLayer != NULL ) + fprintf(fpSpyFile, "%s = ", + OGRAPISpyGetAndRegisterLayerVar(hDS, hLayer).c_str()); + fprintf(fpSpyFile, "%s.GetLayerByName(%s)\n", + OGRAPISpyGetDSVar(hDS).c_str(), + OGRAPISpyGetString(pszLayerName).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_DS_ExecuteSQL( OGRDataSourceH hDS, + const char *pszStatement, + OGRGeometryH hSpatialFilter, + const char *pszDialect, + OGRLayerH hLayer) +{ + OGRAPISpyFlushDefered(); + if( hLayer != NULL ) + fprintf(fpSpyFile, "%s = ", + OGRAPISpyGetAndRegisterLayerVar(hDS, hLayer).c_str()); + fprintf(fpSpyFile, "%s.ExecuteSQL(%s, %s, %s)\n", + OGRAPISpyGetDSVar(hDS).c_str(), + OGRAPISpyGetString(pszStatement).c_str(), + OGRAPISpyGetGeom(hSpatialFilter).c_str(), + OGRAPISpyGetString(pszDialect).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_DS_ReleaseResultSet( OGRDataSourceH hDS, OGRLayerH hLayer) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.ReleaseResultSet(%s)\n", + OGRAPISpyGetDSVar(hDS).c_str(), + (hLayer) ? OGRAPISpyGetLayerVar(hLayer).c_str() : "None"); + + DatasetDescription& dd = oMapDS[hDS]; + dd.oMapLayer.erase(hLayer); + oGlobalMapLayer.erase(hLayer); + + OGRAPISpyFileClose(); +} + +void OGRAPISpy_DS_CreateLayer( OGRDataSourceH hDS, + const char * pszName, + OGRSpatialReferenceH hSpatialRef, + OGRwkbGeometryType eType, + char ** papszOptions, + OGRLayerH hLayer) +{ + OGRAPISpyFlushDefered(); + if( hLayer != NULL ) + fprintf(fpSpyFile, "%s = ", + OGRAPISpyGetAndRegisterLayerVar(hDS, hLayer).c_str()); + fprintf(fpSpyFile, "%s.CreateLayer(%s, srs = %s, geom_type = %s, options = %s)\n", + OGRAPISpyGetDSVar(hDS).c_str(), + OGRAPISpyGetString(pszName).c_str(), + OGRAPISpyGetSRS(hSpatialRef).c_str(), + OGRAPISpyGetGeomType(eType).c_str(), + OGRAPISpyGetOptions(papszOptions).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_DS_DeleteLayer( OGRDataSourceH hDS, int iLayer ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.DeleteLayer(%d)\n", + OGRAPISpyGetDSVar(hDS).c_str(), iLayer); + // Should perhaps remove from the maps + OGRAPISpyFileClose(); +} + +void OGRAPISpy_Dataset_StartTransaction( GDALDatasetH hDS, int bForce ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.StartTransaction(%d)\n", + OGRAPISpyGetDSVar((OGRDataSourceH)hDS).c_str(), bForce); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_Dataset_CommitTransaction( GDALDatasetH hDS ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.CommitTransaction()\n", + OGRAPISpyGetDSVar((OGRDataSourceH)hDS).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_Dataset_RollbackTransaction( GDALDatasetH hDS ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.RollbackTransaction()\n", + OGRAPISpyGetDSVar((OGRDataSourceH)hDS).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_GetFeatureCount( OGRLayerH hLayer, int bForce ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.GetFeatureCount(force = %d)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), bForce); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_GetExtent( OGRLayerH hLayer, int bForce ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.GetExtent(force = %d)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), bForce); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_GetExtentEx( OGRLayerH hLayer, int iGeomField, int bForce ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.GetExtent(geom_field = %d, force = %d)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), iGeomField, bForce); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_SetAttributeFilter( OGRLayerH hLayer, const char* pszFilter ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.SetAttributeFilter(%s)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), + OGRAPISpyGetString(pszFilter).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_GetFeature( OGRLayerH hLayer, GIntBig nFeatureId ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.GetFeature(" CPL_FRMT_GIB ")\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), nFeatureId); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_SetNextByIndex( OGRLayerH hLayer, GIntBig nIndex ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.SetNextByIndex(" CPL_FRMT_GIB ")\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), nIndex); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_GetNextFeature( OGRLayerH hLayer ) +{ + if( hLayerGetNextFeature != hLayer ) + { + OGRAPISpyFlushDefered(); + OGRAPISpyFileClose(); + } + hLayerGetNextFeature = hLayer; + nGetNextFeatureCalls++; +} + +static void OGRAPISpyDumpFeature( OGRFeatureH hFeat ) +{ + OGRFeature* poFeature = (OGRFeature*) hFeat; + + fprintf(fpSpyFile, "f = ogr.Feature(%s)\n", + OGRAPISpyGetFeatureDefnVar((OGRFeatureDefnH)(poFeature->GetDefnRef())).c_str()); + if( poFeature->GetFID() != -1 ) + fprintf(fpSpyFile, "f.SetFID(" CPL_FRMT_GIB ")\n", poFeature->GetFID()); + int i; + for(i = 0; i < poFeature->GetFieldCount(); i++) + { + if( poFeature->IsFieldSet(i) ) + { + switch( poFeature->GetFieldDefnRef(i)->GetType()) + { + case OFTInteger: fprintf(fpSpyFile, "f.SetField(%d, %d)\n", i, + poFeature->GetFieldAsInteger(i)); break; + case OFTReal: fprintf(fpSpyFile, "%s", CPLSPrintf("f.SetField(%d, %.16g)\n", i, + poFeature->GetFieldAsDouble(i))); break; + case OFTString: fprintf(fpSpyFile, "f.SetField(%d, %s)\n", i, + OGRAPISpyGetString(poFeature->GetFieldAsString(i)).c_str()); break; + default: fprintf(fpSpyFile, "f.SetField(%d, %s) #FIXME\n", i, + OGRAPISpyGetString(poFeature->GetFieldAsString(i)).c_str()); break; + } + } + } + for(i = 0; i < poFeature->GetGeomFieldCount(); i++) + { + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(i); + if( poGeom != NULL ) + { + fprintf(fpSpyFile, "f.SetGeomField(%d, %s)\n", i, OGRAPISpyGetGeom( + (OGRGeometryH)poGeom ).c_str() ); + } + } + const char* pszStyleString = poFeature->GetStyleString(); + if( pszStyleString != NULL ) + fprintf(fpSpyFile, "f.SetStyleString(%s)\n", + OGRAPISpyGetString(pszStyleString).c_str() ); +} + +void OGRAPISpy_L_SetFeature( OGRLayerH hLayer, OGRFeatureH hFeat ) +{ + OGRAPISpyFlushDefered(); + OGRAPISpyDumpFeature(hFeat); + fprintf(fpSpyFile, "%s.SetFeature(f)\n", + OGRAPISpyGetLayerVar(hLayer).c_str()); + fprintf(fpSpyFile, "f = None\n"); /* in case layer defn is changed afterwards */ + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_CreateFeature( OGRLayerH hLayer, OGRFeatureH hFeat ) +{ + OGRAPISpyFlushDefered(); + OGRAPISpyDumpFeature(hFeat); + fprintf(fpSpyFile, "%s.CreateFeature(f)\n", + OGRAPISpyGetLayerVar(hLayer).c_str()); + fprintf(fpSpyFile, "f = None\n"); /* in case layer defn is changed afterwards */ + OGRAPISpyFileClose(); +} + +static void OGRAPISpyDumpFieldDefn( OGRFieldDefn* poFieldDefn ) +{ + fprintf(fpSpyFile, "fd = ogr.FieldDefn(%s, %s)\n", + OGRAPISpyGetString(poFieldDefn->GetNameRef()).c_str(), + OGRAPISpyGetFieldType(poFieldDefn->GetType()).c_str()); + if( poFieldDefn->GetWidth() > 0 ) + fprintf(fpSpyFile, "fd.SetWidth(%d)\n", poFieldDefn->GetWidth() ); + if( poFieldDefn->GetPrecision() > 0 ) + fprintf(fpSpyFile, "fd.SetPrecision(%d)\n", poFieldDefn->GetPrecision() ); + if( !poFieldDefn->IsNullable() ) + fprintf(fpSpyFile, "fd.SetNullable(0)\n"); + if( poFieldDefn->GetDefault() != NULL ) + fprintf(fpSpyFile, "fd.SetDefault(%s)\n", + OGRAPISpyGetString(poFieldDefn->GetDefault()).c_str()); +} + +void OGRAPISpy_L_CreateField( OGRLayerH hLayer, OGRFieldDefnH hField, + int bApproxOK ) +{ + OGRAPISpyFlushDefered(); + OGRFieldDefn* poFieldDefn = (OGRFieldDefn*) hField; + OGRAPISpyDumpFieldDefn(poFieldDefn); + fprintf(fpSpyFile, "%s.CreateField(fd, approx_ok = %d)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), bApproxOK); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_DeleteField( OGRLayerH hLayer, int iField ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.DeleteField(%d)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), iField); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_ReorderFields( OGRLayerH hLayer, int* panMap ) +{ + OGRAPISpyFlushDefered(); + OGRLayer* poLayer = (OGRLayer*) hLayer; + fprintf(fpSpyFile, "%s.ReorderFields([", + OGRAPISpyGetLayerVar(hLayer).c_str()); + for( int i = 0; i < poLayer->GetLayerDefn()->GetFieldCount(); i++ ) + { + if( i > 0 ) fprintf(fpSpyFile, ", "); + fprintf(fpSpyFile, "%d", panMap[i]); + } + fprintf(fpSpyFile, "])\n"); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_ReorderField( OGRLayerH hLayer, int iOldFieldPos, + int iNewFieldPos ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.ReorderField(%d, %d)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), iOldFieldPos, iNewFieldPos); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_AlterFieldDefn( OGRLayerH hLayer, int iField, + OGRFieldDefnH hNewFieldDefn, int nFlags ) +{ + OGRAPISpyFlushDefered(); + OGRFieldDefn* poFieldDefn = (OGRFieldDefn*) hNewFieldDefn; + OGRAPISpyDumpFieldDefn(poFieldDefn); + fprintf(fpSpyFile, "%s.AlterFieldDefn(%d, fd, %d)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), iField, nFlags); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_CreateGeomField( OGRLayerH hLayer, OGRGeomFieldDefnH hField, + int bApproxOK ) +{ + OGRAPISpyFlushDefered(); + OGRGeomFieldDefn* poGeomFieldDefn = (OGRGeomFieldDefn*) hField; + fprintf(fpSpyFile, "geom_fd = ogr.GeomFieldDefn(%s, %s)\n", + OGRAPISpyGetString(poGeomFieldDefn->GetNameRef()).c_str(), + OGRAPISpyGetGeomType(poGeomFieldDefn->GetType()).c_str()); + if( poGeomFieldDefn->GetSpatialRef() != NULL ) + fprintf(fpSpyFile, "geom_fd.SetSpatialRef(%s)\n", OGRAPISpyGetSRS( + (OGRSpatialReferenceH)poGeomFieldDefn->GetSpatialRef()).c_str() ); + if( !poGeomFieldDefn->IsNullable() ) + fprintf(fpSpyFile, "geom_fd.SetNullable(0)\n"); + fprintf(fpSpyFile, "%s.CreateGeomField(geom_fd, approx_ok = %d)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), bApproxOK); + OGRAPISpyFileClose(); +} + +static void OGRAPISpy_L_Op( OGRLayerH hLayer, const char* pszMethod ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.%s()\n", OGRAPISpyGetLayerVar(hLayer).c_str(), pszMethod); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_StartTransaction( OGRLayerH hLayer ) { OGRAPISpy_L_Op(hLayer, "StartTransaction"); } +void OGRAPISpy_L_CommitTransaction( OGRLayerH hLayer ) { OGRAPISpy_L_Op(hLayer, "CommitTransaction"); } +void OGRAPISpy_L_RollbackTransaction( OGRLayerH hLayer ) { OGRAPISpy_L_Op(hLayer, "RollbackTransaction"); } + +void OGRAPISpy_L_GetLayerDefn( OGRLayerH hLayer ) +{ + if( hLayer != hLayerGetLayerDefn ) + { + OGRAPISpyFlushDefered(); + hLayerGetLayerDefn = hLayer; + OGRAPISpyFileClose(); + } +} + +void OGRAPISpy_L_GetSpatialRef( OGRLayerH hLayer ) { OGRAPISpy_L_Op(hLayer, "GetSpatialRef"); } +void OGRAPISpy_L_GetSpatialFilter( OGRLayerH hLayer ) { OGRAPISpy_L_Op(hLayer, "GetSpatialFilter"); } +void OGRAPISpy_L_ResetReading( OGRLayerH hLayer ) { OGRAPISpy_L_Op(hLayer, "ResetReading"); } +void OGRAPISpy_L_SyncToDisk( OGRLayerH hLayer ) { OGRAPISpy_L_Op(hLayer, "SyncToDisk"); } +void OGRAPISpy_L_GetFIDColumn( OGRLayerH hLayer ) { OGRAPISpy_L_Op(hLayer, "GetFIDColumn"); } +void OGRAPISpy_L_GetGeometryColumn( OGRLayerH hLayer ) { OGRAPISpy_L_Op(hLayer, "GetGeometryColumn"); } +void OGRAPISpy_L_GetName( OGRLayerH hLayer ) { OGRAPISpy_L_Op(hLayer, "GetName"); } +void OGRAPISpy_L_GetGeomType( OGRLayerH hLayer ) { OGRAPISpy_L_Op(hLayer, "GetGeomType"); } + +void OGRAPISpy_L_FindFieldIndex( OGRLayerH hLayer, const char *pszFieldName, + int bExactMatch ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.FindFieldIndex(%s, %d)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), + OGRAPISpyGetString(pszFieldName).c_str(), bExactMatch); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_TestCapability( OGRLayerH hLayer, const char* pszCap ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.TestCapability(%s)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), + OGRAPISpyGetString(pszCap).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_SetSpatialFilter( OGRLayerH hLayer, OGRGeometryH hGeom ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.SetSpatialFilter(%s)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), + OGRAPISpyGetGeom(hGeom).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_SetSpatialFilterEx( OGRLayerH hLayer, int iGeomField, + OGRGeometryH hGeom ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.SetSpatialFilter(%d, %s)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), + iGeomField, + OGRAPISpyGetGeom(hGeom).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_SetSpatialFilterRect( OGRLayerH hLayer, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s", CPLSPrintf("%s.SetSpatialFilterRect(%.16g, %.16g, %.16g, %.16g)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), + dfMinX, dfMinY, dfMaxX, dfMaxY)); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_SetSpatialFilterRectEx( OGRLayerH hLayer, int iGeomField, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY) +{ + + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s", CPLSPrintf("%s.SetSpatialFilterRect(%d, " + "%.16g, %.16g, %.16g, %.16g)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), + iGeomField, + dfMinX, dfMinY, dfMaxX, dfMaxY)); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_DeleteFeature( OGRLayerH hLayer, GIntBig nFID ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.DeleteFeature(" CPL_FRMT_GIB ")\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), nFID); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_L_SetIgnoredFields( OGRLayerH hLayer, + const char** papszIgnoredFields ) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.SetIgnoredFields(%s)\n", + OGRAPISpyGetLayerVar(hLayer).c_str(), + OGRAPISpyGetOptions((char**)papszIgnoredFields).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_FD_GetGeomType(OGRFeatureDefnH hDefn) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.GetGeomType()\n", + OGRAPISpyGetFeatureDefnVar(hDefn).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_FD_GetFieldCount(OGRFeatureDefnH hDefn) +{ + if( hLayerGetLayerDefn != NULL && + (OGRFeatureDefnH)(((OGRLayer*)hLayerGetLayerDefn)->GetLayerDefn()) == hDefn ) + { + bDeferGetFieldCount = TRUE; + } + else + { + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.GetFieldCount()\n", + OGRAPISpyGetFeatureDefnVar(hDefn).c_str()); + OGRAPISpyFileClose(); + } +} + +void OGRAPISpy_FD_GetFieldDefn(OGRFeatureDefnH hDefn, int iField, + OGRFieldDefnH hField) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s_fielddefn%d = %s.GetFieldDefn(%d)\n", + OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), + iField, + OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), + iField); + + std::map::iterator oIter = + oGlobalMapFieldDefn.find(hField); + if( oIter == oGlobalMapFieldDefn.end() ) + { + oMapFDefn[hDefn].oMapFieldDefn[hField] = iField; + oGlobalMapFieldDefn[hField] = CPLSPrintf("%s_fielddefn%d", + OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), + iField); + } + + OGRAPISpyFileClose(); +} + +void OGRAPISpy_FD_GetFieldIndex(OGRFeatureDefnH hDefn, const char* pszFieldName) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.GetFieldIndex(%s)\n", + OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), + OGRAPISpyGetString(pszFieldName).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_Fld_GetXXXX(OGRFieldDefnH hField, const char* pszOp) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.%s()\n", + oGlobalMapFieldDefn[hField].c_str(), pszOp); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_FD_GetGeomFieldCount(OGRFeatureDefnH hDefn) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.GetGeomFieldCount()\n", + OGRAPISpyGetFeatureDefnVar(hDefn).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_FD_GetGeomFieldDefn(OGRFeatureDefnH hDefn, int iGeomField, + OGRGeomFieldDefnH hGeomField) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s_geomfielddefn%d = %s.GetGeomFieldDefn(%d)\n", + OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), + iGeomField, + OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), + iGeomField); + + std::map::iterator oIter = + oGlobalMapGeomFieldDefn.find(hGeomField); + if( oIter == oGlobalMapGeomFieldDefn.end() ) + { + oMapFDefn[hDefn].oMapGeomFieldDefn[hGeomField] = iGeomField; + oGlobalMapGeomFieldDefn[hGeomField] = CPLSPrintf("%s_geomfielddefn%d", + OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), + iGeomField); + } + + OGRAPISpyFileClose(); +} + +void OGRAPISpy_FD_GetGeomFieldIndex(OGRFeatureDefnH hDefn, const char* pszFieldName) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.GetGeomFieldIndex(%s)\n", + OGRAPISpyGetFeatureDefnVar(hDefn).c_str(), + OGRAPISpyGetString(pszFieldName).c_str()); + OGRAPISpyFileClose(); +} + +void OGRAPISpy_GFld_GetXXXX(OGRGeomFieldDefnH hGeomField, const char* pszOp) +{ + OGRAPISpyFlushDefered(); + fprintf(fpSpyFile, "%s.%s()\n", + oGlobalMapGeomFieldDefn[hGeomField].c_str(), pszOp); + OGRAPISpyFileClose(); +} + +#endif /* OGRAPISPY_ENABLED */ diff --git a/bazaar/plugin/gdal/ogr/ograpispy.h b/bazaar/plugin/gdal/ogr/ograpispy.h new file mode 100644 index 000000000..1b7174d61 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ograpispy.h @@ -0,0 +1,173 @@ +/****************************************************************************** + * $Id: ograpispy.h 28807 2015-03-28 14:46:31Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: OGR C API "Spy" + * Author: Even Rouault, even.rouault at spatialys.com + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRAPISPY_H_INCLUDED +#define _OGRAPISPY_H_INCLUDED + +#include "gdal.h" + +/** + * \file ograpispy.h + * + * OGR C API spy. + * + * If GDAL is compiled with OGRAPISPY_ENABLED defined (which is the case for a + * DEBUG build), a mechanism to trace calls to the OGR *C* API is available + * (calls to the C++ API will not be traced) + * + * Provided there is compile-time support, the mechanism must also be enabled at + * runtime by setting the OGR_API_SPY_FILE configuration option + * to a file where the calls to the OGR C API will be dumped (stdout and stderr + * are recognized as special strings to name the standard output and error files). + * The traced calls are outputted as a OGR Python script. + * + * Only calls that may have side-effects to the behaviour of drivers are traced. + * + * If a file-based datasource is open in update mode, a snapshot of its initial + * state is stored in a 'snapshot' directory, and then a copy of it is made as + * the working datasource. That way, the generated script can be executed in a + * reproducible way. The path for snapshots is the current working directory by + * default, and can be changed by setting the OGR_API_SPY_SNAPSHOT_PATH + * configuration option. If it is set to NO, the snapshot feature will be disabled. + * The reliability of snapshoting relies on if the dataset correctly implements + * GetFileList() (for multi-file datasources) + * + * @since GDAL 2.0 + */ + + +#ifdef DEBUG +#define OGRAPISPY_ENABLED +#endif + +#ifdef OGRAPISPY_ENABLED + +CPL_C_START + +extern int bOGRAPISpyEnabled; + +int OGRAPISpyOpenTakeSnapshot(const char* pszName, int bUpdate); +void OGRAPISpyOpen(const char* pszName, int bUpdate, int iSnapshot, + GDALDatasetH* phDS); +void OGRAPISpyPreClose(OGRDataSourceH hDS); +void OGRAPISpyPostClose(OGRDataSourceH hDS); +void OGRAPISpyCreateDataSource(OGRSFDriverH hDriver, const char* pszName, + char** papszOptions, OGRDataSourceH hDS); +void OGRAPISpyDeleteDataSource(OGRSFDriverH hDriver, const char* pszName); + +void OGRAPISpy_DS_GetLayerCount( OGRDataSourceH hDS ); +void OGRAPISpy_DS_GetLayer( OGRDataSourceH hDS, int iLayer, OGRLayerH hLayer ); +void OGRAPISpy_DS_GetLayerByName( OGRDataSourceH hDS, const char* pszLayerName, + OGRLayerH hLayer ); +void OGRAPISpy_DS_ExecuteSQL( OGRDataSourceH hDS, + const char *pszStatement, + OGRGeometryH hSpatialFilter, + const char *pszDialect, + OGRLayerH hLayer); +void OGRAPISpy_DS_ReleaseResultSet( OGRDataSourceH hDS, OGRLayerH hLayer); + +void OGRAPISpy_DS_CreateLayer( OGRDataSourceH hDS, + const char * pszName, + OGRSpatialReferenceH hSpatialRef, + OGRwkbGeometryType eType, + char ** papszOptions, + OGRLayerH hLayer); +void OGRAPISpy_DS_DeleteLayer( OGRDataSourceH hDS, int iLayer ); + +void OGRAPISpy_Dataset_StartTransaction( GDALDatasetH hDS, int bForce ); +void OGRAPISpy_Dataset_CommitTransaction( GDALDatasetH hDS ); +void OGRAPISpy_Dataset_RollbackTransaction( GDALDatasetH hDS ); + +void OGRAPISpy_L_GetFeatureCount( OGRLayerH hLayer, int bForce ); +void OGRAPISpy_L_GetExtent( OGRLayerH hLayer, int bForce ); +void OGRAPISpy_L_GetExtentEx( OGRLayerH hLayer, int iGeomField, int bForce ); +void OGRAPISpy_L_SetAttributeFilter( OGRLayerH hLayer, const char* pszFilter ); +void OGRAPISpy_L_GetFeature( OGRLayerH hLayer, GIntBig nFeatureId ); +void OGRAPISpy_L_SetNextByIndex( OGRLayerH hLayer, GIntBig nIndex ); +void OGRAPISpy_L_GetNextFeature( OGRLayerH hLayer ); +void OGRAPISpy_L_SetFeature( OGRLayerH hLayer, OGRFeatureH hFeat ); +void OGRAPISpy_L_CreateFeature( OGRLayerH hLayer, OGRFeatureH hFeat ); +void OGRAPISpy_L_CreateField( OGRLayerH hLayer, OGRFieldDefnH hField, + int bApproxOK ); +void OGRAPISpy_L_DeleteField( OGRLayerH hLayer, int iField ); +void OGRAPISpy_L_ReorderFields( OGRLayerH hLayer, int* panMap ); +void OGRAPISpy_L_ReorderField( OGRLayerH hLayer, int iOldFieldPos, + int iNewFieldPos ); +void OGRAPISpy_L_AlterFieldDefn( OGRLayerH hLayer, int iField, + OGRFieldDefnH hNewFieldDefn, + int nFlags ); +void OGRAPISpy_L_CreateGeomField( OGRLayerH hLayer, OGRGeomFieldDefnH hField, + int bApproxOK ); +void OGRAPISpy_L_StartTransaction( OGRLayerH hLayer ); +void OGRAPISpy_L_CommitTransaction( OGRLayerH hLayer ); +void OGRAPISpy_L_RollbackTransaction( OGRLayerH hLayer ); +void OGRAPISpy_L_GetLayerDefn( OGRLayerH hLayer ); +void OGRAPISpy_L_FindFieldIndex( OGRLayerH hLayer, const char *pszFieldName, + int bExactMatch ); +void OGRAPISpy_L_GetSpatialRef( OGRLayerH hLayer ); +void OGRAPISpy_L_TestCapability( OGRLayerH hLayer, const char* pszCap ); +void OGRAPISpy_L_GetSpatialFilter( OGRLayerH hLayer ); +void OGRAPISpy_L_SetSpatialFilter( OGRLayerH hLayer, OGRGeometryH hGeom ); +void OGRAPISpy_L_SetSpatialFilterEx( OGRLayerH hLayer, int iGeomField, + OGRGeometryH hGeom ); +void OGRAPISpy_L_SetSpatialFilterRect( OGRLayerH hLayer, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY); +void OGRAPISpy_L_SetSpatialFilterRectEx( OGRLayerH hLayer, int iGeomField, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY); +void OGRAPISpy_L_ResetReading( OGRLayerH hLayer ); +void OGRAPISpy_L_SyncToDisk( OGRLayerH hLayer ); +void OGRAPISpy_L_DeleteFeature( OGRLayerH hLayer, GIntBig nFID ); +void OGRAPISpy_L_GetFIDColumn( OGRLayerH hLayer ); +void OGRAPISpy_L_GetGeometryColumn( OGRLayerH hLayer ); +void OGRAPISpy_L_GetName( OGRLayerH hLayer ); +void OGRAPISpy_L_GetGeomType( OGRLayerH hLayer ); +void OGRAPISpy_L_SetIgnoredFields( OGRLayerH hLayer, + const char** papszIgnoredFields ); + +void OGRAPISpy_FD_GetGeomType(OGRFeatureDefnH hDefn); +void OGRAPISpy_FD_GetFieldCount(OGRFeatureDefnH hDefn); +void OGRAPISpy_FD_GetFieldDefn(OGRFeatureDefnH hDefn, int iField, + OGRFieldDefnH hGeomField); +void OGRAPISpy_FD_GetFieldIndex(OGRFeatureDefnH hDefn, const char* pszFieldName); + +void OGRAPISpy_Fld_GetXXXX(OGRFieldDefnH hField, const char* pszOp); + +void OGRAPISpy_FD_GetGeomFieldCount(OGRFeatureDefnH hDefn); +void OGRAPISpy_FD_GetGeomFieldDefn(OGRFeatureDefnH hDefn, int iGeomField, + OGRGeomFieldDefnH hGeomField); +void OGRAPISpy_FD_GetGeomFieldIndex(OGRFeatureDefnH hDefn, const char* pszFieldName); +void OGRAPISpy_GFld_GetXXXX(OGRGeomFieldDefnH hGeomField, const char* pszOp); + +CPL_C_END + +#endif /* OGRAPISPY_ENABLED */ + +#endif /* _OGRAPISPY_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ograssemblepolygon.cpp b/bazaar/plugin/gdal/ogr/ograssemblepolygon.cpp new file mode 100644 index 000000000..f7482492f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ograssemblepolygon.cpp @@ -0,0 +1,369 @@ +/****************************************************************************** + * $Id: ograssemblepolygon.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: S-57 Reader + * Purpose: Implements polygon assembly from a bunch of arcs. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ogr_geometry.h" +#include "ogr_api.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ograssemblepolygon.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* CheckPoints() */ +/* */ +/* Check if two points are closer than the current best */ +/* distance. Update the current best distance if they are. */ +/************************************************************************/ + +static int CheckPoints( OGRLineString *poLine1, int iPoint1, + OGRLineString *poLine2, int iPoint2, + double *pdfDistance ) + +{ + double dfDeltaX, dfDeltaY, dfDistance; + + if( pdfDistance == NULL || *pdfDistance == 0 ) + return poLine1->getX(iPoint1) == poLine2->getX(iPoint2) + && poLine1->getY(iPoint1) == poLine2->getY(iPoint2); + + dfDeltaX = poLine1->getX(iPoint1) - poLine2->getX(iPoint2); + dfDeltaY = poLine1->getY(iPoint1) - poLine2->getY(iPoint2); + + dfDeltaX = ABS(dfDeltaX); + dfDeltaY = ABS(dfDeltaY); + + if( dfDeltaX > *pdfDistance || dfDeltaY > *pdfDistance ) + return FALSE; + + dfDistance = sqrt(dfDeltaX*dfDeltaX + dfDeltaY*dfDeltaY); + + if( dfDistance < *pdfDistance ) + { + *pdfDistance = dfDistance; + return TRUE; + } + else + return FALSE; +} + +/************************************************************************/ +/* AddEdgeToRing() */ +/************************************************************************/ + +static void AddEdgeToRing( OGRLinearRing * poRing, OGRLineString * poLine, + int bReverse ) + +{ +/* -------------------------------------------------------------------- */ +/* Establish order and range of traverse. */ +/* -------------------------------------------------------------------- */ + int iStart=0, iEnd=0, iStep=0; + int nVertToAdd = poLine->getNumPoints(); + + if( !bReverse ) + { + iStart = 0; + iEnd = nVertToAdd - 1; + iStep = 1; + } + else + { + iStart = nVertToAdd - 1; + iEnd = 0; + iStep = -1; + } + +/* -------------------------------------------------------------------- */ +/* Should we skip a repeating vertex? */ +/* -------------------------------------------------------------------- */ + if( poRing->getNumPoints() > 0 + && CheckPoints( poRing, poRing->getNumPoints()-1, + poLine, iStart, NULL ) ) + { + iStart += iStep; + } + + poRing->addSubLineString( poLine, iStart, iEnd ); +} + +/************************************************************************/ +/* OGRBuildPolygonFromEdges() */ +/************************************************************************/ + +/** + * Build a ring from a bunch of arcs. + * + * @param hLines handle to an OGRGeometryCollection (or OGRMultiLineString) containing the line string geometries to be built into rings. + * @param bBestEffort not yet implemented???. + * @param bAutoClose indicates if the ring should be close when first and + * last points of the ring are the same. + * @param dfTolerance tolerance into which two arcs are considered + * close enough to be joined. + * @param peErr OGRERR_NONE on success, or OGRERR_FAILURE on failure. + * @return an handle to the new geometry, a polygon. + * + */ + +OGRGeometryH OGRBuildPolygonFromEdges( OGRGeometryH hLines, + int bBestEffort, + int bAutoClose, + double dfTolerance, + OGRErr * peErr ) + +{ + if( hLines == NULL ) + { + if (peErr != NULL) + *peErr = OGRERR_NONE; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Check for the case of a geometrycollection that can be */ +/* promoted to MultiLineString. */ +/* -------------------------------------------------------------------- */ + OGRGeometry* poGeom = (OGRGeometry*) hLines; + if( wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection ) + { + int iGeom; + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom; + + for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ ) + { + if( wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType()) + != wkbLineString ) + { + if (peErr != NULL) + *peErr = OGRERR_FAILURE; + CPLError(CE_Failure, CPLE_NotSupported, + "The geometry collection contains non line string geometries"); + return NULL; + } + } + } + else if (wkbFlatten(poGeom->getGeometryType()) != wkbMultiLineString ) + { + if (peErr != NULL) + *peErr = OGRERR_FAILURE; + CPLError(CE_Failure, CPLE_NotSupported, + "The passed geometry is not an OGRGeometryCollection (or OGRMultiLineString) " + "containing line string geometries"); + return NULL; + } + + int bSuccess = TRUE; + OGRGeometryCollection *poLines = (OGRGeometryCollection *) hLines; + std::vector aoRings; + + (void) bBestEffort; + +/* -------------------------------------------------------------------- */ +/* Setup array of line markers indicating if they have been */ +/* added to a ring yet. */ +/* -------------------------------------------------------------------- */ + int nEdges = poLines->getNumGeometries(); + int *panEdgeConsumed, nRemainingEdges = nEdges; + + panEdgeConsumed = (int *) CPLCalloc(sizeof(int),nEdges); + +/* ==================================================================== */ +/* Loop generating rings. */ +/* ==================================================================== */ + while( nRemainingEdges > 0 ) + { + int iEdge; + OGRLineString *poLine; + +/* -------------------------------------------------------------------- */ +/* Find the first unconsumed edge. */ +/* -------------------------------------------------------------------- */ + for( iEdge = 0; panEdgeConsumed[iEdge]; iEdge++ ) {} + + poLine = (OGRLineString *) poLines->getGeometryRef(iEdge); + + panEdgeConsumed[iEdge] = TRUE; + nRemainingEdges--; + + if (poLine->getNumPoints() < 2) + { + continue; + } + +/* -------------------------------------------------------------------- */ +/* Start a new ring, copying in the current line directly */ +/* -------------------------------------------------------------------- */ + OGRLinearRing *poRing = new OGRLinearRing(); + + AddEdgeToRing( poRing, poLine, FALSE ); + +/* ==================================================================== */ +/* Loop adding edges to this ring until we make a whole pass */ +/* within finding anything to add. */ +/* ==================================================================== */ + int bWorkDone = TRUE; + double dfBestDist = dfTolerance; + + while( !CheckPoints(poRing,0,poRing,poRing->getNumPoints()-1,NULL) + && nRemainingEdges > 0 + && bWorkDone ) + { + int iBestEdge = -1, bReverse = FALSE; + + bWorkDone = FALSE; + dfBestDist = dfTolerance; + + // We consider linking the end to the beginning. If this is + // closer than any other option we will just close the loop. + + //CheckPoints(poRing,0,poRing,poRing->getNumPoints()-1,&dfBestDist); + + // Find unused edge with end point closest to our loose end. + for( iEdge = 0; iEdge < nEdges; iEdge++ ) + { + if( panEdgeConsumed[iEdge] ) + continue; + + poLine = (OGRLineString *) poLines->getGeometryRef(iEdge); + if (poLine->getNumPoints() < 2) + continue; + + if( CheckPoints(poLine,0,poRing,poRing->getNumPoints()-1, + &dfBestDist) ) + { + iBestEdge = iEdge; + bReverse = FALSE; + } + if( CheckPoints(poLine,poLine->getNumPoints()-1, + poRing,poRing->getNumPoints()-1, + &dfBestDist) ) + { + iBestEdge = iEdge; + bReverse = TRUE; + } + + // if we use exact comparison, jump now + if (dfTolerance == 0.0 && iBestEdge != -1) break; + } + + // We found one within tolerance - add it. + if( iBestEdge != -1 ) + { + poLine = (OGRLineString *) + poLines->getGeometryRef(iBestEdge); + + AddEdgeToRing( poRing, poLine, bReverse ); + + panEdgeConsumed[iBestEdge] = TRUE; + nRemainingEdges--; + bWorkDone = TRUE; + } + } + +/* -------------------------------------------------------------------- */ +/* Did we fail to complete the ring? */ +/* -------------------------------------------------------------------- */ + dfBestDist = dfTolerance; + + if( !CheckPoints(poRing,0,poRing,poRing->getNumPoints()-1, + &dfBestDist) ) + { + CPLDebug( "OGR", + "Failed to close ring %d.\n" + "End Points are: (%.8f,%.7f) and (%.7f,%.7f)\n", + (int)aoRings.size(), + poRing->getX(0), poRing->getY(0), + poRing->getX(poRing->getNumPoints()-1), + poRing->getY(poRing->getNumPoints()-1) ); + + bSuccess = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Do we need to auto-close this ring? */ +/* -------------------------------------------------------------------- */ + if( bAutoClose + && !CheckPoints(poRing,0,poRing,poRing->getNumPoints()-1,NULL) ) + { + poRing->addPoint( poRing->getX(0), + poRing->getY(0), + poRing->getZ(0)); + } + + aoRings.push_back(poRing); + } /* next ring */ + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + CPLFree( panEdgeConsumed ); + +/* -------------------------------------------------------------------- */ +/* Identify exterior ring - it will be the largest. #3610 */ +/* -------------------------------------------------------------------- */ + double maxarea = 0.0; + int maxring = -1, rn; + OGREnvelope tenv; + + for (rn = 0; rn < (int)aoRings.size(); ++rn) + { + aoRings[rn]->getEnvelope(&tenv); + double tarea = (tenv.MaxX - tenv.MinX) * (tenv.MaxY - tenv.MinY); + if (tarea > maxarea) + { + maxarea = tarea; + maxring = rn; + } + } + + OGRPolygon *poPolygon = new OGRPolygon(); + + if (maxring != -1) + { + poPolygon->addRingDirectly(aoRings[maxring]); + for (rn = 0; rn < (int)aoRings.size(); ++rn) + { + if (rn == maxring) continue; + poPolygon->addRingDirectly(aoRings[rn]); + } + } + + if( peErr != NULL ) + { + if( bSuccess ) + *peErr = OGRERR_NONE; + else + *peErr = OGRERR_FAILURE; + } + + return (OGRGeometryH) poPolygon; +} + + + diff --git a/bazaar/plugin/gdal/ogr/ogrcircularstring.cpp b/bazaar/plugin/gdal/ogr/ogrcircularstring.cpp new file mode 100644 index 000000000..5221d57db --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrcircularstring.cpp @@ -0,0 +1,715 @@ +/****************************************************************************** + * $Id: ogrcircularstring.cpp 28005 2014-11-26 10:04:43Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRCircularString geometry class. + * Author: Even Rouault, even dot rouault at spatialys dot com + * + ****************************************************************************** + * Copyright (c) 2010, 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" +#include +#include + +CPL_CVSID("$Id"); + +/************************************************************************/ +/* OGRCircularString() */ +/************************************************************************/ + +/** + * \brief Create an empty circular string. + */ + +OGRCircularString::OGRCircularString() + +{ +} + +/************************************************************************/ +/* ~OGRCircularString() */ +/************************************************************************/ + +OGRCircularString::~OGRCircularString() + +{ +} + +/************************************************************************/ +/* getGeometryType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRCircularString::getGeometryType() const + +{ + if( getCoordinateDimension() == 3 ) + return wkbCircularStringZ; + else + return wkbCircularString; +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRCircularString::getGeometryName() const + +{ + return "CIRCULARSTRING"; +} + +/************************************************************************/ +/* importFromWkb() */ +/* */ +/* Initialize from serialized stream in well known binary */ +/* format. */ +/************************************************************************/ + +OGRErr OGRCircularString::importFromWkb( unsigned char * pabyData, + int nSize, + OGRwkbVariant eWkbVariant ) + +{ + OGRErr eErr = OGRSimpleCurve::importFromWkb(pabyData, nSize, eWkbVariant); + if (eErr == OGRERR_NONE) + { + if (!IsValidFast()) + { + empty(); + return OGRERR_CORRUPT_DATA; + } + } + return eErr; +} + +/************************************************************************/ +/* exportToWkb() */ +/* */ +/* Build a well known binary representation of this object. */ +/************************************************************************/ + +OGRErr OGRCircularString::exportToWkb( OGRwkbByteOrder eByteOrder, + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const + +{ + if (!IsValidFast()) + { + return OGRERR_FAILURE; + } + + if( eWkbVariant == wkbVariantOldOgc ) /* does not make sense for new geometries, so patch it */ + eWkbVariant = wkbVariantIso; + return OGRSimpleCurve::exportToWkb(eByteOrder, pabyData, eWkbVariant); +} + +/************************************************************************/ +/* importFromWkt() */ +/* */ +/* Instantiate from well known text format. Currently this is */ +/* `CIRCULARSTRING [Z] ( x y [z], x y [z], ...)', */ +/************************************************************************/ + +OGRErr OGRCircularString::importFromWkt( char ** ppszInput ) + +{ + OGRErr eErr = OGRSimpleCurve::importFromWkt(ppszInput); + if (eErr == OGRERR_NONE) + { + if (!IsValidFast()) + { + empty(); + return OGRERR_CORRUPT_DATA; + } + } + return eErr; +} + +/************************************************************************/ +/* exportToWkt() */ +/************************************************************************/ + +OGRErr OGRCircularString::exportToWkt( char ** ppszDstText, + CPL_UNUSED OGRwkbVariant eWkbVariant ) const + +{ + if (!IsValidFast()) + { + return OGRERR_FAILURE; + } + + return OGRSimpleCurve::exportToWkt(ppszDstText, wkbVariantIso); +} + +/************************************************************************/ +/* get_Length() */ +/* */ +/* For now we return a simple euclidian 2D distance. */ +/************************************************************************/ + +double OGRCircularString::get_Length() const + +{ + double dfLength = 0.0; + double R, cx, cy, alpha0, alpha1, alpha2; + int i; + for(i=0;i quadrantEnd ) + { + int tmp = quadrantStart; + quadrantStart = quadrantEnd; + quadrantEnd = tmp; + } + /* Transition trough quadrants in counter-clock wise direction */ + for( int j=quadrantStart+1; j<=quadrantEnd; j++) + { + switch( ((j+8)%4) ) + { + case 0: + psEnvelope->MaxX = MAX(psEnvelope->MaxX, cx + R); + break; + case 1: + psEnvelope->MaxY = MAX(psEnvelope->MaxY, cy + R); + break; + case 2: + psEnvelope->MinX = MIN(psEnvelope->MinX, cx - R); + break; + case 3: + psEnvelope->MinY = MIN(psEnvelope->MaxY, cy - R); + break; + default: + CPLAssert(FALSE); + break; + } + } + } + } +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRCircularString::getEnvelope( OGREnvelope * psEnvelope ) const + +{ + OGRSimpleCurve::getEnvelope(psEnvelope); + ExtendEnvelopeWithCircular(psEnvelope); +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRCircularString::getEnvelope( OGREnvelope3D * psEnvelope ) const + +{ + OGRSimpleCurve::getEnvelope(psEnvelope); + ExtendEnvelopeWithCircular(psEnvelope); +} + +/************************************************************************/ +/* OGRCircularString::segmentize() */ +/************************************************************************/ + +void OGRCircularString::segmentize( double dfMaxLength ) +{ + if( !IsValidFast() || nPointCount == 0 ) + return; + + /* So as to make sure that the same line followed in both directions */ + /* result in the same segmentized line */ + if ( paoPoints[0].x < paoPoints[nPointCount - 1].x || + (paoPoints[0].x == paoPoints[nPointCount - 1].x && + paoPoints[0].y < paoPoints[nPointCount - 1].y) ) + { + reversePoints(); + segmentize(dfMaxLength); + reversePoints(); + } + + std::vector aoRawPoint; + std::vector adfZ; + for(int i=0;i dfMaxLength || dfSegmentLength2 > dfMaxLength ) + { + int nIntermediatePoints = 1 + 2 * (int)floor(dfSegmentLength1 / dfMaxLength / 2); + double dfStep = (alpha1 - alpha0) / (nIntermediatePoints + 1); + for(int j=1;j<=nIntermediatePoints;j++) + { + double alpha = alpha0 + dfStep * j; + double x = cx + R * cos(alpha), y = cy + R * sin(alpha); + aoRawPoint.push_back(OGRRawPoint(x,y)); + if( padfZ ) + { + double z = padfZ[i] + (padfZ[i+1] - padfZ[i]) * (alpha - alpha0) / (alpha1 - alpha0); + adfZ.push_back(z); + } + } + } + aoRawPoint.push_back(OGRRawPoint(x1,y1)); + if( padfZ ) + adfZ.push_back(padfZ[i+1]); + + if( dfSegmentLength1 > dfMaxLength || dfSegmentLength2 > dfMaxLength ) + { + int nIntermediatePoints = 1 + 2 * (int)floor(dfSegmentLength2 / dfMaxLength / 2); + double dfStep = (alpha2 - alpha1) / (nIntermediatePoints + 1); + for(int j=1;j<=nIntermediatePoints;j++) + { + double alpha = alpha1 + dfStep * j; + double x = cx + R * cos(alpha), y = cy + R * sin(alpha); + aoRawPoint.push_back(OGRRawPoint(x,y)); + if( padfZ ) + { + double z = padfZ[i+1] + (padfZ[i+2] - padfZ[i+1]) * (alpha - alpha1) / (alpha2 - alpha1); + adfZ.push_back(z); + } + } + } + } + else + { + /* It is a straight line */ + double dfSegmentLength1 = sqrt((x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)); + double dfSegmentLength2 = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); + if( dfSegmentLength1 > dfMaxLength || dfSegmentLength2 > dfMaxLength ) + { + int nIntermediatePoints = 1 + 2 * (int)ceil(dfSegmentLength1 / dfMaxLength / 2); + for(int j=1;j<=nIntermediatePoints;j++) + { + aoRawPoint.push_back(OGRRawPoint( + x0 + j * (x1-x0) / (nIntermediatePoints + 1), + y0 + j * (y1-y0) / (nIntermediatePoints + 1))); + if( padfZ ) + adfZ.push_back(padfZ[i] + j * (padfZ[i+1]-padfZ[i]) / (nIntermediatePoints + 1)); + } + } + + aoRawPoint.push_back(OGRRawPoint(x1,y1)); + if( padfZ ) + adfZ.push_back(padfZ[i+1]); + + if( dfSegmentLength1 > dfMaxLength || dfSegmentLength2 > dfMaxLength ) + { + int nIntermediatePoints = 1 + 2 * (int)ceil(dfSegmentLength2 / dfMaxLength / 2); + for(int j=1;j<=nIntermediatePoints;j++) + { + aoRawPoint.push_back(OGRRawPoint( + x1 + j * (x2-x1) / (nIntermediatePoints + 1), + y1 + j * (y2-y1) / (nIntermediatePoints + 1))); + if( padfZ ) + adfZ.push_back(padfZ[i+1] + j * (padfZ[i+2]-padfZ[i+1]) / (nIntermediatePoints + 1)); + } + } + } + } + aoRawPoint.push_back(paoPoints[nPointCount-1]); + if( padfZ ) + adfZ.push_back(padfZ[nPointCount-1]); + + CPLAssert(aoRawPoint.size() == 0 || (aoRawPoint.size() >= 3 && (aoRawPoint.size() % 2) == 1)); + if( padfZ ) + { + CPLAssert(adfZ.size() == aoRawPoint.size()); + } + + /* Is there actually something to modify ? */ + if( nPointCount < (int)aoRawPoint.size() ) + { + nPointCount = (int)aoRawPoint.size(); + paoPoints = (OGRRawPoint *) + OGRRealloc(paoPoints, sizeof(OGRRawPoint) * nPointCount); + memcpy(paoPoints, &aoRawPoint[0], sizeof(OGRRawPoint) * nPointCount); + if( padfZ ) + { + padfZ = (double*) OGRRealloc(padfZ, sizeof(double) * aoRawPoint.size()); + memcpy(padfZ, &adfZ[0], sizeof(double) * nPointCount); + } + } +} + +/************************************************************************/ +/* Value() */ +/* */ +/* Get an interpolated point at some distance along the curve. */ +/************************************************************************/ + +void OGRCircularString::Value( double dfDistance, OGRPoint * poPoint ) const + +{ + double dfLength = 0; + int i; + + if( dfDistance < 0 ) + { + StartPoint( poPoint ); + return; + } + + for(i=0;i 0) + { + if( (dfLength <= dfDistance) && ((dfLength + dfSegLength) >= + dfDistance) ) + { + double dfRatio; + + dfRatio = (dfDistance - dfLength) / dfSegLength; + + double alpha = alpha0 * (1 - dfRatio) + alpha2 * dfRatio; + double x = cx + R * cos(alpha), y = cy + R * sin(alpha); + + poPoint->setX( x ); + poPoint->setY( y ); + + if( getCoordinateDimension() == 3 ) + poPoint->setZ( padfZ[i] * (1 - dfRatio) + + padfZ[i+2] * dfRatio ); + + return; + } + + dfLength += dfSegLength; + } + } + else + { + /* It is a straight line */ + double dfSegLength = sqrt((x2-x0)*(x2-x0)+(y2-y0)*(y2-y0)); + if (dfSegLength > 0) + { + if( (dfLength <= dfDistance) && ((dfLength + dfSegLength) >= + dfDistance) ) + { + double dfRatio; + + dfRatio = (dfDistance - dfLength) / dfSegLength; + + poPoint->setX( paoPoints[i].x * (1 - dfRatio) + + paoPoints[i+2].x * dfRatio ); + poPoint->setY( paoPoints[i].y * (1 - dfRatio) + + paoPoints[i+2].y * dfRatio ); + + if( getCoordinateDimension() == 3 ) + poPoint->setZ( padfZ[i] * (1 - dfRatio) + + padfZ[i+2] * dfRatio ); + + return; + } + + dfLength += dfSegLength; + } + } + } + + EndPoint( poPoint ); +} + +/************************************************************************/ +/* CurveToLine() */ +/************************************************************************/ + +OGRLineString* OGRCircularString::CurveToLine(double dfMaxAngleStepSizeDegrees, + const char* const* papszOptions) const +{ + OGRLineString* poLine = new OGRLineString(); + poLine->assignSpatialReference(getSpatialReference()); + + int bHasZ = (getCoordinateDimension() == 3); + int i; + for(i=0;iaddSubLineString(poArc, (i == 0) ? 0 : 1); + delete poArc; + } + return poLine; +} + +/************************************************************************/ +/* IsValidFast() */ +/************************************************************************/ + +OGRBoolean OGRCircularString::IsValidFast( ) const + +{ + if (nPointCount == 1 || nPointCount == 2 || + (nPointCount >= 3 && (nPointCount % 2) == 0)) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad number of points in circular string : %d", nPointCount); + return FALSE; + } + return TRUE; +} + +/************************************************************************/ +/* IsValid() */ +/************************************************************************/ + +OGRBoolean OGRCircularString::IsValid( ) const + +{ + return IsValidFast() && OGRGeometry::IsValid(); +} + +/************************************************************************/ +/* hasCurveGeometry() */ +/************************************************************************/ + +OGRBoolean OGRCircularString::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const +{ + return TRUE; +} + +/************************************************************************/ +/* getLinearGeometry() */ +/************************************************************************/ + +OGRGeometry* OGRCircularString::getLinearGeometry(double dfMaxAngleStepSizeDegrees, + const char* const* papszOptions) const +{ + return CurveToLine(dfMaxAngleStepSizeDegrees, papszOptions); +} + +/************************************************************************/ +/* GetCasterToLineString() */ +/************************************************************************/ + +OGRCurveCasterToLineString OGRCircularString::GetCasterToLineString() const { + return (OGRCurveCasterToLineString) OGRGeometry::CastToError; +} + +/************************************************************************/ +/* GetCasterToLinearRing() */ +/************************************************************************/ + +OGRCurveCasterToLinearRing OGRCircularString::GetCasterToLinearRing() const { + return (OGRCurveCasterToLinearRing) OGRGeometry::CastToError; +} + +/************************************************************************/ +/* IsFullCircle() */ +/************************************************************************/ + +int OGRCircularString::IsFullCircle( double& cx, double& cy, double& square_R ) const +{ + if( getNumPoints() == 3 && get_IsClosed() ) + { + double x0 = getX(0); + double y0 = getY(0); + double x1 = getX(1); + double y1 = getY(1); + cx = (x0 + x1) / 2; + cy = (y0 + y1) / 2; + square_R = (x1-cx)*(x1-cx)+(y1-cy)*(y1-cy); + return TRUE; + } + /* Full circle defined by 2 arcs ? */ + else if( getNumPoints() == 5 && get_IsClosed() ) + { + double R_1, cx_1, cy_1, alpha0_1, alpha1_1, alpha2_1; + double R_2, cx_2, cy_2, alpha0_2, alpha1_2, alpha2_2; + if( OGRGeometryFactory::GetCurveParmeters( + getX(0), getY(0), + getX(1), getY(1), + getX(2), getY(2), + R_1, cx_1, cy_1, alpha0_1, alpha1_1, alpha2_1) && + OGRGeometryFactory::GetCurveParmeters( + getX(2), getY(2), + getX(3), getY(3), + getX(4), getY(4), + R_2, cx_2, cy_2, alpha0_2, alpha1_2, alpha2_2) && + fabs(R_1-R_2) < 1e-10 && + fabs(cx_1-cx_2) < 1e-10 && + fabs(cy_1-cy_2) < 1e-10 && + (alpha2_1 - alpha0_1) * (alpha2_2 - alpha0_2) > 0 ) + { + cx = cx_1; + cy = cy_1; + square_R = R_1 * R_1; + return TRUE; + } + } + return FALSE; +} + +/************************************************************************/ +/* get_AreaOfCurveSegments() */ +/************************************************************************/ + +double OGRCircularString::get_AreaOfCurveSegments() const +{ + double dfArea = 0; + for( int i=0; i < getNumPoints() - 2; i += 2 ) + { + double x0 = getX(i), y0 = getY(i), + x1 = getX(i+1), y1 = getY(i+1), + x2 = getX(i+2), y2 = getY(i+2); + double R, cx, cy, alpha0, alpha1, alpha2; + if( OGRGeometryFactory::GetCurveParmeters(x0, y0, x1, y1, x2, y2, + R, cx, cy, alpha0, alpha1, alpha2)) + { + double delta_alpha01 = alpha1 - alpha0; /* should be <= PI in absolute value */ + double delta_alpha12 = alpha2 - alpha1; /* same */ + /* This is my maths, but wikipedia confirms it... */ + /* Cf http://en.wikipedia.org/wiki/Circular_segment */ + dfArea += 0.5 * R * R * fabs( delta_alpha01 - sin(delta_alpha01) + + delta_alpha12 - sin(delta_alpha12) ); + } + } + return dfArea; +} + +/************************************************************************/ +/* get_Area() */ +/************************************************************************/ + +double OGRCircularString::get_Area() const +{ + double cx, cy, square_R; + + if( IsEmpty() || !get_IsClosed() ) + return 0; + + if( IsFullCircle(cx, cy, square_R) ) + { + return M_PI * square_R; + } + + /* Optimization for convex rings */ + if( IsConvex() ) + { + /* Compute area of shape without the circular segments */ + double dfArea = get_LinearArea(); + + /* Add the area of the spherical segments */ + dfArea += get_AreaOfCurveSegments(); + + return dfArea; + } + else + { + OGRLineString* poLS = CurveToLine(); + double dfArea = poLS->get_Area(); + delete poLS; + + return dfArea; + } +} + +/************************************************************************/ +/* ContainsPoint() */ +/************************************************************************/ + +int OGRCircularString::ContainsPoint( const OGRPoint* p ) const +{ + double cx, cy, square_R; + if( IsFullCircle(cx, cy, square_R) ) + { + double square_dist = (p->getX()- cx)*(p->getX()- cx)+ + (p->getY()- cy)*(p->getY()- cy); + return square_dist <= square_R; + } + return -1; +} diff --git a/bazaar/plugin/gdal/ogr/ogrcompoundcurve.cpp b/bazaar/plugin/gdal/ogr/ogrcompoundcurve.cpp new file mode 100644 index 000000000..dad2a82d8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrcompoundcurve.cpp @@ -0,0 +1,800 @@ +/****************************************************************************** + * $Id: ogrcompoundcurve.cpp 27960 2014-11-14 18:31:32Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRCompoundCurve geometry class. + * Author: Even Rouault, even dot rouault at spatialys dot com + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" +#include + +CPL_CVSID("$Id"); + +/************************************************************************/ +/* OGRCompoundCurve() */ +/************************************************************************/ + +/** + * \brief Create an empty compound curve. + */ + +OGRCompoundCurve::OGRCompoundCurve() + +{ +} + +/************************************************************************/ +/* ~OGRCompoundCurve() */ +/************************************************************************/ + +OGRCompoundCurve::~OGRCompoundCurve() + +{ +} + +/************************************************************************/ +/* getGeometryType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRCompoundCurve::getGeometryType() const + +{ + if( getCoordinateDimension() == 3 ) + return wkbCompoundCurveZ; + else + return wkbCompoundCurve; +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRCompoundCurve::getGeometryName() const + +{ + return "COMPOUNDCURVE"; +} + +/************************************************************************/ +/* WkbSize() */ +/************************************************************************/ +int OGRCompoundCurve::WkbSize() const +{ + return oCC.WkbSize(); +} + +/************************************************************************/ +/* addCurveDirectlyFromWkt() */ +/************************************************************************/ + +OGRErr OGRCompoundCurve::addCurveDirectlyFromWkb( OGRGeometry* poSelf, + OGRCurve* poCurve ) +{ + OGRCompoundCurve* poCC = (OGRCompoundCurve*)poSelf; + return poCC->addCurveDirectlyInternal( poCurve, 1e-14, FALSE ); +} + +/************************************************************************/ +/* importFromWkb() */ +/************************************************************************/ + +OGRErr OGRCompoundCurve::importFromWkb( unsigned char * pabyData, + int nSize, + OGRwkbVariant eWkbVariant ) +{ + OGRwkbByteOrder eByteOrder; + int nDataOffset = 0; + OGRErr eErr = oCC.importPreambuleFromWkb(this, pabyData, nSize, nDataOffset, + eByteOrder, 9, eWkbVariant); + if( eErr >= 0 ) + return eErr; + + return oCC.importBodyFromWkb(this, pabyData, nSize, nDataOffset, + FALSE /* bAcceptCompoundCurve */, addCurveDirectlyFromWkb, + eWkbVariant); +} + +/************************************************************************/ +/* exportToWkb() */ +/************************************************************************/ +OGRErr OGRCompoundCurve::exportToWkb( OGRwkbByteOrder eByteOrder, + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const +{ + if( eWkbVariant == wkbVariantOldOgc ) /* does not make sense for new geometries, so patch it */ + eWkbVariant = wkbVariantIso; + return oCC.exportToWkb(this, eByteOrder, pabyData, eWkbVariant); +} + +/************************************************************************/ +/* addCurveDirectlyFromWkt() */ +/************************************************************************/ + +OGRErr OGRCompoundCurve::addCurveDirectlyFromWkt( OGRGeometry* poSelf, OGRCurve* poCurve ) +{ + return ((OGRCompoundCurve*)poSelf)->addCurveDirectly(poCurve); +} + +/************************************************************************/ +/* importFromWkt() */ +/************************************************************************/ + +OGRErr OGRCompoundCurve::importFromWkt( char ** ppszInput ) +{ + return importCurveCollectionFromWkt( ppszInput, + FALSE, /* bAllowEmptyComponent */ + TRUE, /* bAllowLineString */ + TRUE, /* bAllowCurve */ + FALSE, /* bAllowCompoundCurve */ + addCurveDirectlyFromWkt ); +} + +/************************************************************************/ +/* exportToWkt() */ +/************************************************************************/ +OGRErr OGRCompoundCurve::exportToWkt( char ** ppszDstText, + CPL_UNUSED OGRwkbVariant eWkbVariant ) const + +{ + return oCC.exportToWkt(this, ppszDstText); +} + +/************************************************************************/ +/* clone() */ +/************************************************************************/ + +OGRGeometry *OGRCompoundCurve::clone() const +{ + OGRCompoundCurve *poNewCC; + + poNewCC = new OGRCompoundCurve; + poNewCC->assignSpatialReference( getSpatialReference() ); + + for( int i = 0; i < oCC.nCurveCount; i++ ) + { + poNewCC->addCurve( oCC.papoCurves[i] ); + } + + return poNewCC; +} + +/************************************************************************/ +/* empty() */ +/************************************************************************/ + +void OGRCompoundCurve::empty() +{ + oCC.empty(this); +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRCompoundCurve::getEnvelope( OGREnvelope * psEnvelope ) const +{ + oCC.getEnvelope(psEnvelope); +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRCompoundCurve::getEnvelope( OGREnvelope3D * psEnvelope ) const +{ + oCC.getEnvelope(psEnvelope); +} + +/************************************************************************/ +/* IsEmpty() */ +/************************************************************************/ + +OGRBoolean OGRCompoundCurve::IsEmpty() const +{ + return oCC.IsEmpty(); +} + +/************************************************************************/ +/* get_Length() */ +/* */ +/* For now we return a simple euclidian 2D distance. */ +/************************************************************************/ + +double OGRCompoundCurve::get_Length() const +{ + double dfLength = 0.0; + for( int iGeom = 0; iGeom < oCC.nCurveCount; iGeom++ ) + dfLength += oCC.papoCurves[iGeom]->get_Length(); + return dfLength; +} + +/************************************************************************/ +/* StartPoint() */ +/************************************************************************/ + +void OGRCompoundCurve::StartPoint(OGRPoint *p) const +{ + CPLAssert(oCC.nCurveCount > 0); + oCC.papoCurves[0]->StartPoint(p); +} + +/************************************************************************/ +/* EndPoint() */ +/************************************************************************/ + +void OGRCompoundCurve::EndPoint(OGRPoint *p) const +{ + CPLAssert(oCC.nCurveCount > 0); + oCC.papoCurves[oCC.nCurveCount-1]->EndPoint(p); +} + +/************************************************************************/ +/* Value() */ +/************************************************************************/ + +void OGRCompoundCurve::Value( double dfDistance, OGRPoint *poPoint ) const +{ + + if( dfDistance < 0 ) + { + StartPoint( poPoint ); + return; + } + + double dfLength = 0; + for( int iGeom = 0; iGeom < oCC.nCurveCount; iGeom++ ) + { + double dfSegLength = oCC.papoCurves[iGeom]->get_Length(); + if (dfSegLength > 0) + { + if( (dfLength <= dfDistance) && ((dfLength + dfSegLength) >= + dfDistance) ) + { + oCC.papoCurves[iGeom]->Value(dfDistance - dfLength, poPoint); + + return; + } + + dfLength += dfSegLength; + } + } + + EndPoint( poPoint ); +} + +/************************************************************************/ +/* CurveToLineInternal() */ +/************************************************************************/ + +OGRLineString* OGRCompoundCurve::CurveToLineInternal(double dfMaxAngleStepSizeDegrees, + const char* const* papszOptions, + int bIsLinearRing) const +{ + OGRLineString* poLine; + if( bIsLinearRing ) + poLine = new OGRLinearRing(); + else + poLine = new OGRLineString(); + poLine->assignSpatialReference(getSpatialReference()); + for( int iGeom = 0; iGeom < oCC.nCurveCount; iGeom++ ) + { + OGRLineString* poSubLS = oCC.papoCurves[iGeom]->CurveToLine(dfMaxAngleStepSizeDegrees, + papszOptions); + poLine->addSubLineString(poSubLS, (iGeom == 0) ? 0 : 1); + delete poSubLS; + } + return poLine; +} + +/************************************************************************/ +/* CurveToLine() */ +/************************************************************************/ + +OGRLineString* OGRCompoundCurve::CurveToLine(double dfMaxAngleStepSizeDegrees, + const char* const* papszOptions) const +{ + return CurveToLineInternal(dfMaxAngleStepSizeDegrees, papszOptions, FALSE); +} + +/************************************************************************/ +/* Equals() */ +/************************************************************************/ + +OGRBoolean OGRCompoundCurve::Equals( OGRGeometry *poOther ) const +{ + OGRCompoundCurve *poOCC = (OGRCompoundCurve *) poOther; + + if( poOCC == this ) + return TRUE; + + if( poOther->getGeometryType() != getGeometryType() ) + return FALSE; + + return oCC.Equals(&(poOCC->oCC)); +} + +/************************************************************************/ +/* setCoordinateDimension() */ +/************************************************************************/ + +void OGRCompoundCurve::setCoordinateDimension( int nNewDimension ) +{ + oCC.setCoordinateDimension( this, nNewDimension ); +} + +/************************************************************************/ +/* getNumCurves() */ +/************************************************************************/ + +/** + * \brief Return the number of curves. + * + * Note that the number of curves making this compound curve. + * + * Relates to the ISO SQL/MM ST_NumCurves() function. + * + * @return number of curves. + */ + +int OGRCompoundCurve::getNumCurves() const +{ + return oCC.nCurveCount; +} + +/************************************************************************/ +/* getCurve() */ +/************************************************************************/ + +/** + * \brief Fetch reference to indicated internal ring. + * + * Note that the returned curve pointer is to an internal data object of + * the OGRCompoundCurve. It should not be modified or deleted by the application, + * and the pointer is only valid till the polygon is next modified. Use + * the OGRGeometry::clone() method to make a separate copy within the + * application. + * + * Relates to the ISO SQL/MM ST_CurveN() function. + * + * @param iRing curve index from 0 to getNumCurves() - 1. + * + * @return pointer to curve. May be NULL. + */ + +OGRCurve *OGRCompoundCurve::getCurve( int i ) +{ + return oCC.getCurve(i); +} + +/************************************************************************/ +/* getCurve() */ +/************************************************************************/ + +/** + * \brief Fetch reference to indicated internal ring. + * + * Note that the returned curve pointer is to an internal data object of + * the OGRCompoundCurve. It should not be modified or deleted by the application, + * and the pointer is only valid till the polygon is next modified. Use + * the OGRGeometry::clone() method to make a separate copy within the + * application. + * + * Relates to the ISO SQL/MM ST_CurveN() function. + * + * @param iRing curve index from 0 to getNumCurves() - 1. + * + * @return pointer to curve. May be NULL. + */ + +const OGRCurve *OGRCompoundCurve::getCurve( int i ) const +{ + return oCC.getCurve(i); +} + +/************************************************************************/ +/* stealCurve() */ +/************************************************************************/ + +OGRCurve* OGRCompoundCurve::stealCurve( int i ) +{ + return oCC.stealCurve(i); +} + +/************************************************************************/ +/* addCurve() */ +/************************************************************************/ + +/** + * \brief Add a curve to the container. + * + * The passed geometry is cloned to make an internal copy. + * + * There is no ISO SQL/MM analog to this method. + * + * This method is the same as the C function OGR_G_AddGeometry(). + * + * @param poCurve geometry to add to the container. + * @param dfToleranceEps tolerance when checking that the first point of a + * segment matches then end point of the previous one. + * Default value: 1e-14. + * + * @return OGRERR_NONE if successful, or OGRERR_FAILURE in case of error + * (for example if curves are not contiguous) + */ + +OGRErr OGRCompoundCurve::addCurve( OGRCurve* poCurve, double dfToleranceEps ) +{ + OGRCurve* poClonedCurve = (OGRCurve*)poCurve->clone(); + OGRErr eErr = addCurveDirectly( poClonedCurve, dfToleranceEps ); + if( eErr != OGRERR_NONE ) + delete poClonedCurve; + return eErr; +} + +/************************************************************************/ +/* addCurveDirectly() */ +/************************************************************************/ + +/** + * \brief Add a curve directly to the container. + * + * Ownership of the passed geometry is taken by the container rather than + * cloning as addCurve() does. + * + * There is no ISO SQL/MM analog to this method. + * + * This method is the same as the C function OGR_G_AddGeometryDirectly(). + * + * @param poCurve geometry to add to the container. + * @param dfToleranceEps tolerance when checking that the first point of a + * segment matches then end point of the previous one. + * Default value: 1e-14. + * + * @return OGRERR_NONE if successful, or OGRERR_FAILURE in case of error + * (for example if curves are not contiguous) + */ +OGRErr OGRCompoundCurve::addCurveDirectly( OGRCurve* poCurve, + double dfToleranceEps ) +{ + return addCurveDirectlyInternal(poCurve, dfToleranceEps, TRUE ); +} + +OGRErr OGRCompoundCurve::addCurveDirectlyInternal( OGRCurve* poCurve, + double dfToleranceEps, + int bNeedRealloc ) +{ + if( poCurve->getNumPoints() == 1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid curve: not enough points"); + return OGRERR_FAILURE; + } + + OGRwkbGeometryType eCurveType = wkbFlatten(poCurve->getGeometryType()); + if( EQUAL(poCurve->getGeometryName(), "LINEARRING") ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Linearring not allowed."); + return OGRERR_FAILURE; + } + else if( eCurveType == wkbCompoundCurve ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot add a compound curve inside a compound curve"); + return OGRERR_FAILURE; + } + + if( oCC.nCurveCount > 0 ) + { + if( oCC.papoCurves[oCC.nCurveCount-1]->IsEmpty() || + poCurve->IsEmpty() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Non contiguous curves"); + return OGRERR_FAILURE; + } + + OGRPoint end, start; + oCC.papoCurves[oCC.nCurveCount-1]->EndPoint(&end); + poCurve->StartPoint(&start); + if( fabs(end.getX() - start.getX()) > dfToleranceEps || + fabs(end.getY() - start.getY()) > dfToleranceEps || + fabs(end.getZ() - start.getZ()) > dfToleranceEps ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Non contiguous curves"); + return OGRERR_FAILURE; + } + ((OGRSimpleCurve*)poCurve)->setPoint(0, &end); /* patch so that it matches exactly */ + } + + return oCC.addCurveDirectly(this, poCurve, bNeedRealloc); +} + +/************************************************************************/ +/* transform() */ +/************************************************************************/ + +OGRErr OGRCompoundCurve::transform( OGRCoordinateTransformation *poCT ) +{ + return oCC.transform(this, poCT); +} + +/************************************************************************/ +/* flattenTo2D() */ +/************************************************************************/ + +void OGRCompoundCurve::flattenTo2D() +{ + oCC.flattenTo2D(this); +} + +/************************************************************************/ +/* segmentize() */ +/************************************************************************/ + +void OGRCompoundCurve::segmentize(double dfMaxLength) +{ + oCC.segmentize(dfMaxLength); +} + +/************************************************************************/ +/* swapXY() */ +/************************************************************************/ + +void OGRCompoundCurve::swapXY() +{ + oCC.swapXY(); +} + +/************************************************************************/ +/* hasCurveGeometry() */ +/************************************************************************/ + +OGRBoolean OGRCompoundCurve::hasCurveGeometry(int bLookForNonLinear) const +{ + if( bLookForNonLinear ) + { + return oCC.hasCurveGeometry(bLookForNonLinear); + } + else + return TRUE; +} + +/************************************************************************/ +/* getLinearGeometry() */ +/************************************************************************/ + +OGRGeometry* OGRCompoundCurve::getLinearGeometry(double dfMaxAngleStepSizeDegrees, + const char* const* papszOptions) const +{ + return CurveToLine(dfMaxAngleStepSizeDegrees, papszOptions); +} + +/************************************************************************/ +/* getNumPoints() */ +/************************************************************************/ + +int OGRCompoundCurve::getNumPoints() const +{ + int nPoints = 0; + for( int i = 0; i < oCC.nCurveCount; i++ ) + { + nPoints += oCC.papoCurves[i]->getNumPoints(); + if( i != 0 ) + nPoints --; + } + return nPoints; +} + +/************************************************************************/ +/* OGRCompoundCurvePointIterator */ +/************************************************************************/ + +class OGRCompoundCurvePointIterator: public OGRPointIterator +{ + const OGRCompoundCurve *poCC; + int iCurCurve; + OGRPointIterator *poCurveIter; + + public: + OGRCompoundCurvePointIterator(const OGRCompoundCurve* poCC) : + poCC(poCC), iCurCurve(0), poCurveIter(NULL) {} + ~OGRCompoundCurvePointIterator() { delete poCurveIter; } + + virtual OGRBoolean getNextPoint(OGRPoint* p); +}; + +/************************************************************************/ +/* getNextPoint() */ +/************************************************************************/ + +OGRBoolean OGRCompoundCurvePointIterator::getNextPoint(OGRPoint* p) +{ + if( iCurCurve == poCC->getNumCurves() ) + return FALSE; + if( poCurveIter == NULL ) + poCurveIter = poCC->getCurve(0)->getPointIterator(); + if( !poCurveIter->getNextPoint(p) ) + { + iCurCurve ++; + if( iCurCurve == poCC->getNumCurves() ) + return FALSE; + delete poCurveIter; + poCurveIter = poCC->getCurve(iCurCurve)->getPointIterator(); + /* skip first point */ + return poCurveIter->getNextPoint(p) && + poCurveIter->getNextPoint(p); + } + return TRUE; +} + +/************************************************************************/ +/* getPointIterator() */ +/************************************************************************/ + +OGRPointIterator* OGRCompoundCurve::getPointIterator() const +{ + return new OGRCompoundCurvePointIterator(this); +} + +/************************************************************************/ +/* CastToLineString() */ +/************************************************************************/ + +OGRLineString* OGRCompoundCurve::CastToLineString(OGRCompoundCurve* poCC) +{ + for(int i=0;ioCC.nCurveCount;i++) + { + poCC->oCC.papoCurves[i] = OGRCurve::CastToLineString(poCC->oCC.papoCurves[i]); + if( poCC->oCC.papoCurves[i] == NULL ) + { + delete poCC; + return NULL; + } + } + + if( poCC->oCC.nCurveCount == 1 ) + { + OGRLineString* poLS = (OGRLineString*) poCC->oCC.papoCurves[0]; + poLS->assignSpatialReference(poCC->getSpatialReference()); + poCC->oCC.papoCurves[0] = NULL; + delete poCC; + return poLS; + } + + OGRLineString* poLS = poCC->CurveToLineInternal(0, NULL, FALSE); + delete poCC; + return poLS; +} + +/************************************************************************/ +/* CastToLinearRing() */ +/************************************************************************/ + +/** + * \brief Cast to linear ring. + * + * The passed in geometry is consumed and a new one returned (or NULL in case + * of failure) + * + * @param poCC the input geometry - ownership is passed to the method. + * @return new geometry. + */ + +OGRLinearRing* OGRCompoundCurve::CastToLinearRing(OGRCompoundCurve* poCC) +{ + for(int i=0;ioCC.nCurveCount;i++) + { + poCC->oCC.papoCurves[i] = OGRCurve::CastToLineString(poCC->oCC.papoCurves[i]); + if( poCC->oCC.papoCurves[i] == NULL ) + { + delete poCC; + return NULL; + } + } + + if( poCC->oCC.nCurveCount == 1 ) + { + OGRLinearRing* poLR = OGRCurve::CastToLinearRing( poCC->oCC.papoCurves[0] ); + if( poLR != NULL ) + { + poLR->assignSpatialReference(poCC->getSpatialReference()); + } + poCC->oCC.papoCurves[0] = NULL; + delete poCC; + return poLR; + } + + OGRLinearRing* poLR = (OGRLinearRing*)poCC->CurveToLineInternal(0, NULL, TRUE); + delete poCC; + return poLR; +} + +/************************************************************************/ +/* GetCasterToLineString() */ +/************************************************************************/ + +OGRCurveCasterToLineString OGRCompoundCurve::GetCasterToLineString() const { + return (OGRCurveCasterToLineString) OGRCompoundCurve::CastToLineString; +} + +/************************************************************************/ +/* GetCasterToLinearRing() */ +/************************************************************************/ + +OGRCurveCasterToLinearRing OGRCompoundCurve::GetCasterToLinearRing() const { + return (OGRCurveCasterToLinearRing) OGRCompoundCurve::CastToLinearRing; +} + +/************************************************************************/ +/* get_Area() */ +/************************************************************************/ + +double OGRCompoundCurve::get_Area() const +{ + if( IsEmpty() || !get_IsClosed() ) + return 0; + + /* Optimization for convex rings */ + if( IsConvex() ) + { + /* Compute area of shape without the circular segments */ + OGRPointIterator* poIter = getPointIterator(); + OGRLineString oLS; + oLS.setNumPoints( getNumPoints() ); + OGRPoint p; + for(int i = 0; poIter->getNextPoint(&p); i++ ) + { + oLS.setPoint( i, p.getX(), p.getY() ); + } + double dfArea = oLS.get_Area(); + delete poIter; + + /* Add the area of the spherical segments */ + dfArea += get_AreaOfCurveSegments(); + + return dfArea; + } + else + { + OGRLineString* poLS = CurveToLine(); + double dfArea = poLS->get_Area(); + delete poLS; + + return dfArea; + } +} + +/************************************************************************/ +/* get_AreaOfCurveSegments() */ +/************************************************************************/ + +double OGRCompoundCurve::get_AreaOfCurveSegments() const +{ + double dfArea = 0; + for( int i = 0; i < getNumCurves(); i++ ) + { + const OGRCurve* poPart = getCurve(i); + dfArea += poPart->get_AreaOfCurveSegments(); + } + return dfArea; +} diff --git a/bazaar/plugin/gdal/ogr/ogrct.cpp b/bazaar/plugin/gdal/ogr/ogrct.cpp new file mode 100644 index 000000000..905aaca4d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrct.cpp @@ -0,0 +1,1057 @@ +/****************************************************************************** + * $Id: ogrct.cpp 28459 2015-02-12 13:48:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRSCoordinateTransformation class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "cpl_port.h" +#include "cpl_error.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" + +#ifdef PROJ_STATIC +#include "proj_api.h" +#endif + +CPL_CVSID("$Id: ogrct.cpp 28459 2015-02-12 13:48:21Z rouault $"); + +/* ==================================================================== */ +/* PROJ.4 interface stuff. */ +/* ==================================================================== */ +#ifndef PROJ_STATIC +typedef struct { double u, v; } projUV; + +#define projPJ void * +#define projCtx void * +#define RAD_TO_DEG 57.29577951308232 +#define DEG_TO_RAD .0174532925199432958 + +#else + +#if PJ_VERSION < 480 +#define projCtx void * +#endif + +#endif + +static CPLMutex *hPROJMutex = NULL; + +static projPJ (*pfn_pj_init_plus)(const char *) = NULL; +static projPJ (*pfn_pj_init)(int, char**) = NULL; +static void (*pfn_pj_free)(projPJ) = NULL; +static int (*pfn_pj_transform)(projPJ, projPJ, long, int, + double *, double *, double * ) = NULL; +static int *(*pfn_pj_get_errno_ref)(void) = NULL; +static char *(*pfn_pj_strerrno)(int) = NULL; +static char *(*pfn_pj_get_def)(projPJ,int) = NULL; +static void (*pfn_pj_dalloc)(void *) = NULL; + +static projPJ (*pfn_pj_init_plus_ctx)( projCtx, const char * ) = NULL; +static int (*pfn_pj_ctx_get_errno)( projCtx ) = NULL; +static projCtx (*pfn_pj_ctx_alloc)(void) = NULL; +static void (*pfn_pj_ctx_free)( projCtx ) = NULL; + +#if (defined(WIN32) || defined(WIN32CE)) && !defined(__MINGW32__) +# define LIBNAME "proj.dll" +#elif defined(__MINGW32__) +// XXX: If PROJ.4 library was properly built using libtool in Cygwin or MinGW +// environments it has the interface version number embedded in the file name +// (it is CURRENT-AGE number). If DLL came somewhere else (e.g. from MSVC +// build) it can be named either way, so use PROJSO environment variable to +// specify the right library name. By default assume that in Cygwin/MinGW all +// components were buit in the same way. +# define LIBNAME "libproj-0.dll" +#elif defined(__CYGWIN__) +# define LIBNAME "cygproj-0.dll" +#elif defined(__APPLE__) +# define LIBNAME "libproj.dylib" +#else +# define LIBNAME "libproj.so" +#endif + +/************************************************************************/ +/* OCTCleanupProjMutex() */ +/************************************************************************/ + +void OCTCleanupProjMutex() +{ + if( hPROJMutex != NULL ) + { + CPLDestroyMutex(hPROJMutex); + hPROJMutex = NULL; + } +} + +/************************************************************************/ +/* OGRProj4CT */ +/************************************************************************/ + +class OGRProj4CT : public OGRCoordinateTransformation +{ + OGRSpatialReference *poSRSSource; + void *psPJSource; + int bSourceLatLong; + double dfSourceToRadians; + int bSourceWrap; + double dfSourceWrapLong; + + + OGRSpatialReference *poSRSTarget; + void *psPJTarget; + int bTargetLatLong; + double dfTargetFromRadians; + int bTargetWrap; + double dfTargetWrapLong; + + int bIdentityTransform; + + int nErrorCount; + + int bCheckWithInvertProj; + double dfThreshold; + + projCtx pjctx; + + int InitializeNoLock( OGRSpatialReference *poSource, + OGRSpatialReference *poTarget ); + + int nMaxCount; + double *padfOriX; + double *padfOriY; + double *padfOriZ; + double *padfTargetX; + double *padfTargetY; + double *padfTargetZ; + +public: + OGRProj4CT(); + virtual ~OGRProj4CT(); + + int Initialize( OGRSpatialReference *poSource, + OGRSpatialReference *poTarget ); + + virtual OGRSpatialReference *GetSourceCS(); + virtual OGRSpatialReference *GetTargetCS(); + virtual int Transform( int nCount, + double *x, double *y, double *z = NULL ); + virtual int TransformEx( int nCount, + double *x, double *y, double *z = NULL, + int *panSuccess = NULL ); + +}; + +/************************************************************************/ +/* GetProjLibraryName() */ +/************************************************************************/ + +static const char* GetProjLibraryName() +{ + const char *pszLibName = LIBNAME; +#if !defined(WIN32CE) + if( CPLGetConfigOption("PROJSO",NULL) != NULL ) + pszLibName = CPLGetConfigOption("PROJSO",NULL); +#endif + return pszLibName; +} + +/************************************************************************/ +/* LoadProjLibrary() */ +/************************************************************************/ + +static int LoadProjLibrary_unlocked() + +{ + static int bTriedToLoad = FALSE; + const char *pszLibName; + + if( bTriedToLoad ) + return( pfn_pj_transform != NULL ); + + bTriedToLoad = TRUE; + + pszLibName = GetProjLibraryName(); + +#ifdef PROJ_STATIC + pfn_pj_init = pj_init; + pfn_pj_init_plus = pj_init_plus; + pfn_pj_free = pj_free; + pfn_pj_transform = pj_transform; + pfn_pj_get_errno_ref = (int *(*)(void)) pj_get_errno_ref; + pfn_pj_strerrno = pj_strerrno; + pfn_pj_dalloc = pj_dalloc; +#if PJ_VERSION >= 446 + pfn_pj_get_def = pj_get_def; +#endif +#if PJ_VERSION >= 480 + pfn_pj_ctx_alloc = pj_ctx_alloc; + pfn_pj_ctx_free = pj_ctx_free; + pfn_pj_init_plus_ctx = pj_init_plus_ctx; + pfn_pj_ctx_get_errno = pj_ctx_get_errno; +#endif +#else + CPLPushErrorHandler( CPLQuietErrorHandler ); + + pfn_pj_init = (projPJ (*)(int, char**)) CPLGetSymbol( pszLibName, + "pj_init" ); + CPLPopErrorHandler(); + + if( pfn_pj_init == NULL ) + return( FALSE ); + + pfn_pj_init_plus = (projPJ (*)(const char *)) + CPLGetSymbol( pszLibName, "pj_init_plus" ); + pfn_pj_free = (void (*)(projPJ)) + CPLGetSymbol( pszLibName, "pj_free" ); + pfn_pj_transform = (int (*)(projPJ,projPJ,long,int,double*, + double*,double*)) + CPLGetSymbol( pszLibName, "pj_transform" ); + pfn_pj_get_errno_ref = (int *(*)(void)) + CPLGetSymbol( pszLibName, "pj_get_errno_ref" ); + pfn_pj_strerrno = (char *(*)(int)) + CPLGetSymbol( pszLibName, "pj_strerrno" ); + + CPLPushErrorHandler( CPLQuietErrorHandler ); + pfn_pj_get_def = (char *(*)(projPJ,int)) + CPLGetSymbol( pszLibName, "pj_get_def" ); + pfn_pj_dalloc = (void (*)(void*)) + CPLGetSymbol( pszLibName, "pj_dalloc" ); + + /* PROJ 4.8.0 symbols */ + pfn_pj_ctx_alloc = (projCtx (*)( void )) + CPLGetSymbol( pszLibName, "pj_ctx_alloc" ); + pfn_pj_ctx_free = (void (*)( projCtx )) + CPLGetSymbol( pszLibName, "pj_ctx_free" ); + pfn_pj_init_plus_ctx = (projPJ (*)( projCtx, const char * )) + CPLGetSymbol( pszLibName, "pj_init_plus_ctx" ); + pfn_pj_ctx_get_errno = (int (*)( projCtx )) + CPLGetSymbol( pszLibName, "pj_ctx_get_errno" ); + + CPLPopErrorHandler(); + CPLErrorReset(); +#endif + + if (pfn_pj_ctx_alloc != NULL && + pfn_pj_ctx_free != NULL && + pfn_pj_init_plus_ctx != NULL && + pfn_pj_ctx_get_errno != NULL && + CSLTestBoolean(CPLGetConfigOption("USE_PROJ_480_FEATURES", "YES"))) + { + CPLDebug("OGRCT", "PROJ >= 4.8.0 features enabled"); + } + else + { + pfn_pj_ctx_alloc = NULL; + pfn_pj_ctx_free = NULL; + pfn_pj_init_plus_ctx = NULL; + pfn_pj_ctx_get_errno = NULL; + } + + if( pfn_pj_transform == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to load %s, but couldn't find pj_transform.\n" + "Please upgrade to PROJ 4.1.2 or later.", + pszLibName ); + + return FALSE; + } + + return( TRUE ); +} + +static int LoadProjLibrary() + +{ + CPLMutexHolderD( &hPROJMutex ); + return LoadProjLibrary_unlocked(); +} + +/************************************************************************/ +/* OCTProj4Normalize() */ +/* */ +/* This function is really just here since we already have all */ +/* the code to load libproj.so. It is intended to "normalize" */ +/* a proj.4 definition, expanding +init= definitions and so */ +/* forth as possible. */ +/************************************************************************/ + +char *OCTProj4Normalize( const char *pszProj4Src ) + +{ + char *pszNewProj4Def, *pszCopy; + projPJ psPJSource = NULL; + + CPLMutexHolderD( &hPROJMutex ); + + if( !LoadProjLibrary_unlocked() || pfn_pj_dalloc == NULL || pfn_pj_get_def == NULL ) + return CPLStrdup( pszProj4Src ); + + CPLLocaleC oLocaleEnforcer; + + psPJSource = pfn_pj_init_plus( pszProj4Src ); + + if( psPJSource == NULL ) + return CPLStrdup( pszProj4Src ); + + pszNewProj4Def = pfn_pj_get_def( psPJSource, 0 ); + + pfn_pj_free( psPJSource ); + + if( pszNewProj4Def == NULL ) + return CPLStrdup( pszProj4Src ); + + pszCopy = CPLStrdup( pszNewProj4Def ); + pfn_pj_dalloc( pszNewProj4Def ); + + return pszCopy; +} + +/************************************************************************/ +/* OCTDestroyCoordinateTransformation() */ +/************************************************************************/ + +/** + * \brief OGRCoordinateTransformation destructor. + * + * This function is the same as OGRCoordinateTransformation::DestroyCT() + * + * @param hCT the object to delete + */ + +void CPL_STDCALL +OCTDestroyCoordinateTransformation( OGRCoordinateTransformationH hCT ) + +{ + delete (OGRCoordinateTransformation *) hCT; +} + +/************************************************************************/ +/* DestroyCT() */ +/************************************************************************/ + +/** + * \brief OGRCoordinateTransformation destructor. + * + * This function is the same as OGRCoordinateTransformation::~OGRCoordinateTransformation() + * and OCTDestroyCoordinateTransformation() + * + * This static method will destroy a OGRCoordinateTransformation. It is + * equivalent to calling delete on the object, but it ensures that the + * deallocation is properly executed within the OGR libraries heap on + * platforms where this can matter (win32). + * + * @param poCT the object to delete + * + * @since GDAL 1.7.0 + */ + +void OGRCoordinateTransformation::DestroyCT(OGRCoordinateTransformation* poCT) +{ + delete poCT; +} + +/************************************************************************/ +/* OGRCreateCoordinateTransformation() */ +/************************************************************************/ + +/** + * Create transformation object. + * + * This is the same as the C function OCTNewCoordinateTransformation(). + * + * Input spatial reference system objects are assigned + * by copy (calling clone() method) and no ownership transfer occurs. + * + * The delete operator, or OCTDestroyCoordinateTransformation() should + * be used to destroy transformation objects. + * + * The PROJ.4 library must be available at run-time. + * + * @param poSource source spatial reference system. + * @param poTarget target spatial reference system. + * @return NULL on failure or a ready to use transformation object. + */ + +OGRCoordinateTransformation* +OGRCreateCoordinateTransformation( OGRSpatialReference *poSource, + OGRSpatialReference *poTarget ) + +{ + OGRProj4CT *poCT; + + if( pfn_pj_init == NULL && !LoadProjLibrary() ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unable to load PROJ.4 library (%s), creation of\n" + "OGRCoordinateTransformation failed.", + GetProjLibraryName() ); + return NULL; + } + + poCT = new OGRProj4CT(); + + if( !poCT->Initialize( poSource, poTarget ) ) + { + delete poCT; + return NULL; + } + else + { + return poCT; + } +} + +/************************************************************************/ +/* OCTNewCoordinateTransformation() */ +/************************************************************************/ + +/** + * Create transformation object. + * + * This is the same as the C++ function OGRCreateCoordinateTransformation(). + * + * Input spatial reference system objects are assigned + * by copy (calling clone() method) and no ownership transfer occurs. + * + * OCTDestroyCoordinateTransformation() should + * be used to destroy transformation objects. + * + * The PROJ.4 library must be available at run-time. + * + * @param hSourceSRS source spatial reference system. + * @param hTargetSRS target spatial reference system. + * @return NULL on failure or a ready to use transformation object. + */ + +OGRCoordinateTransformationH CPL_STDCALL +OCTNewCoordinateTransformation( + OGRSpatialReferenceH hSourceSRS, OGRSpatialReferenceH hTargetSRS ) + +{ + return (OGRCoordinateTransformationH) + OGRCreateCoordinateTransformation( + (OGRSpatialReference *) hSourceSRS, + (OGRSpatialReference *) hTargetSRS ); +} + +/************************************************************************/ +/* OGRProj4CT() */ +/************************************************************************/ + +OGRProj4CT::OGRProj4CT() + +{ + poSRSSource = NULL; + poSRSTarget = NULL; + psPJSource = NULL; + psPJTarget = NULL; + + bIdentityTransform = FALSE; + nErrorCount = 0; + + bCheckWithInvertProj = FALSE; + dfThreshold = 0; + + nMaxCount = 0; + padfOriX = NULL; + padfOriY = NULL; + padfOriZ = NULL; + padfTargetX = NULL; + padfTargetY = NULL; + padfTargetZ = NULL; + + if (pfn_pj_ctx_alloc != NULL) + pjctx = pfn_pj_ctx_alloc(); + else + pjctx = NULL; +} + +/************************************************************************/ +/* ~OGRProj4CT() */ +/************************************************************************/ + +OGRProj4CT::~OGRProj4CT() + +{ + if( poSRSSource != NULL ) + { + if( poSRSSource->Dereference() <= 0 ) + delete poSRSSource; + } + + if( poSRSTarget != NULL ) + { + if( poSRSTarget->Dereference() <= 0 ) + delete poSRSTarget; + } + + if (pjctx != NULL) + { + pfn_pj_ctx_free(pjctx); + + if( psPJSource != NULL ) + pfn_pj_free( psPJSource ); + + if( psPJTarget != NULL ) + pfn_pj_free( psPJTarget ); + } + else + { + CPLMutexHolderD( &hPROJMutex ); + + if( psPJSource != NULL ) + pfn_pj_free( psPJSource ); + + if( psPJTarget != NULL ) + pfn_pj_free( psPJTarget ); + } + + CPLFree(padfOriX); + CPLFree(padfOriY); + CPLFree(padfOriZ); + CPLFree(padfTargetX); + CPLFree(padfTargetY); + CPLFree(padfTargetZ); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +int OGRProj4CT::Initialize( OGRSpatialReference * poSourceIn, + OGRSpatialReference * poTargetIn ) + +{ + CPLLocaleC oLocaleEnforcer; + if (pjctx != NULL) + { + return InitializeNoLock(poSourceIn, poTargetIn); + } + + CPLMutexHolderD( &hPROJMutex ); + return InitializeNoLock(poSourceIn, poTargetIn); +} + +/************************************************************************/ +/* InitializeNoLock() */ +/************************************************************************/ + +int OGRProj4CT::InitializeNoLock( OGRSpatialReference * poSourceIn, + OGRSpatialReference * poTargetIn ) + +{ + if( poSourceIn == NULL || poTargetIn == NULL ) + return FALSE; + + poSRSSource = poSourceIn->Clone(); + poSRSTarget = poTargetIn->Clone(); + + bSourceLatLong = poSRSSource->IsGeographic(); + bTargetLatLong = poSRSTarget->IsGeographic(); + +/* -------------------------------------------------------------------- */ +/* Setup source and target translations to radians for lat/long */ +/* systems. */ +/* -------------------------------------------------------------------- */ + dfSourceToRadians = DEG_TO_RAD; + bSourceWrap = FALSE; + dfSourceWrapLong = 0.0; + + if( bSourceLatLong ) + { + OGR_SRSNode *poUNITS = poSRSSource->GetAttrNode( "GEOGCS|UNIT" ); + if( poUNITS && poUNITS->GetChildCount() >= 2 ) + { + dfSourceToRadians = CPLAtof(poUNITS->GetChild(1)->GetValue()); + if( dfSourceToRadians == 0.0 ) + dfSourceToRadians = DEG_TO_RAD; + } + } + + dfTargetFromRadians = RAD_TO_DEG; + bTargetWrap = FALSE; + dfTargetWrapLong = 0.0; + + if( bTargetLatLong ) + { + OGR_SRSNode *poUNITS = poSRSTarget->GetAttrNode( "GEOGCS|UNIT" ); + if( poUNITS && poUNITS->GetChildCount() >= 2 ) + { + double dfTargetToRadians = CPLAtof(poUNITS->GetChild(1)->GetValue()); + if( dfTargetToRadians != 0.0 ) + dfTargetFromRadians = 1 / dfTargetToRadians; + } + } + +/* -------------------------------------------------------------------- */ +/* Preliminary logic to setup wrapping. */ +/* -------------------------------------------------------------------- */ + const char *pszCENTER_LONG; + + if( CPLGetConfigOption( "CENTER_LONG", NULL ) != NULL ) + { + bSourceWrap = bTargetWrap = TRUE; + dfSourceWrapLong = dfTargetWrapLong = + CPLAtof(CPLGetConfigOption( "CENTER_LONG", "" )); + CPLDebug( "OGRCT", "Wrap at %g.", dfSourceWrapLong ); + } + + pszCENTER_LONG = poSRSSource->GetExtension( "GEOGCS", "CENTER_LONG" ); + if( pszCENTER_LONG != NULL ) + { + dfSourceWrapLong = CPLAtof(pszCENTER_LONG); + bSourceWrap = TRUE; + CPLDebug( "OGRCT", "Wrap source at %g.", dfSourceWrapLong ); + } + + pszCENTER_LONG = poSRSTarget->GetExtension( "GEOGCS", "CENTER_LONG" ); + if( pszCENTER_LONG != NULL ) + { + dfTargetWrapLong = CPLAtof(pszCENTER_LONG); + bTargetWrap = TRUE; + CPLDebug( "OGRCT", "Wrap target at %g.", dfTargetWrapLong ); + } + + bCheckWithInvertProj = CSLTestBoolean(CPLGetConfigOption( "CHECK_WITH_INVERT_PROJ", "NO" )); + + /* The threshold is rather experimental... Works well with the cases of ticket #2305 */ + if (bSourceLatLong) + dfThreshold = CPLAtof(CPLGetConfigOption( "THRESHOLD", ".1" )); + else + /* 1 works well for most projections, except for +proj=aeqd that requires */ + /* a tolerance of 10000 */ + dfThreshold = CPLAtof(CPLGetConfigOption( "THRESHOLD", "10000" )); + +/* -------------------------------------------------------------------- */ +/* Establish PROJ.4 handle for source if projection. */ +/* -------------------------------------------------------------------- */ + // OGRThreadSafety: The following variable is not a thread safety issue + // since the only issue is incrementing while accessing which at worse + // means debug output could be one "increment" late. + static int nDebugReportCount = 0; + + char *pszSrcProj4Defn = NULL; + + if( poSRSSource->exportToProj4( &pszSrcProj4Defn ) != OGRERR_NONE ) + { + CPLFree( pszSrcProj4Defn ); + return FALSE; + } + + if( strlen(pszSrcProj4Defn) == 0 ) + { + CPLFree( pszSrcProj4Defn ); + CPLError( CE_Failure, CPLE_AppDefined, + "No PROJ.4 translation for source SRS, coordinate\n" + "transformation initialization has failed." ); + return FALSE; + } + + if (pjctx) + psPJSource = pfn_pj_init_plus_ctx( pjctx, pszSrcProj4Defn ); + else + psPJSource = pfn_pj_init_plus( pszSrcProj4Defn ); + + if( psPJSource == NULL ) + { + if( pjctx != NULL) + { + int pj_errno = pfn_pj_ctx_get_errno(pjctx); + + /* pfn_pj_strerrno not yet thread-safe in PROJ 4.8.0 */ + CPLMutexHolderD(&hPROJMutex); + CPLError( CE_Failure, CPLE_NotSupported, + "Failed to initialize PROJ.4 with `%s'.\n%s", + pszSrcProj4Defn, pfn_pj_strerrno(pj_errno) ); + } + else if( pfn_pj_get_errno_ref != NULL + && pfn_pj_strerrno != NULL ) + { + int *p_pj_errno = pfn_pj_get_errno_ref(); + + CPLError( CE_Failure, CPLE_NotSupported, + "Failed to initialize PROJ.4 with `%s'.\n%s", + pszSrcProj4Defn, pfn_pj_strerrno(*p_pj_errno) ); + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Failed to initialize PROJ.4 with `%s'.\n", + pszSrcProj4Defn ); + } + } + + if( nDebugReportCount < 10 ) + CPLDebug( "OGRCT", "Source: %s", pszSrcProj4Defn ); + + if( psPJSource == NULL ) + { + CPLFree( pszSrcProj4Defn ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Establish PROJ.4 handle for target if projection. */ +/* -------------------------------------------------------------------- */ + + char *pszDstProj4Defn = NULL; + + if( poSRSTarget->exportToProj4( &pszDstProj4Defn ) != OGRERR_NONE ) + { + CPLFree( pszSrcProj4Defn ); + CPLFree( pszDstProj4Defn ); + return FALSE; + } + + if( strlen(pszDstProj4Defn) == 0 ) + { + CPLFree( pszSrcProj4Defn ); + CPLFree( pszDstProj4Defn ); + CPLError( CE_Failure, CPLE_AppDefined, + "No PROJ.4 translation for destination SRS, coordinate\n" + "transformation initialization has failed." ); + return FALSE; + } + + if (pjctx) + psPJTarget = pfn_pj_init_plus_ctx( pjctx, pszDstProj4Defn ); + else + psPJTarget = pfn_pj_init_plus( pszDstProj4Defn ); + + if( psPJTarget == NULL ) + CPLError( CE_Failure, CPLE_NotSupported, + "Failed to initialize PROJ.4 with `%s'.", + pszDstProj4Defn ); + + if( nDebugReportCount < 10 ) + { + CPLDebug( "OGRCT", "Target: %s", pszDstProj4Defn ); + nDebugReportCount++; + } + + if( psPJTarget == NULL ) + { + CPLFree( pszSrcProj4Defn ); + CPLFree( pszDstProj4Defn ); + return FALSE; + } + + /* Determine if we really have a transformation to do */ + bIdentityTransform = (strcmp(pszSrcProj4Defn, pszDstProj4Defn) == 0); + + /* In case of identity transform, under the following conditions, */ + /* we can also avoid transforming from deegrees <--> radians. */ + if( bIdentityTransform && bSourceLatLong && !bSourceWrap && + bTargetLatLong && !bTargetWrap && + abs(dfSourceToRadians * dfTargetFromRadians - 1.0) < 1e-10 ) + { + /*bSourceLatLong = FALSE; + bTargetLatLong = FALSE;*/ + } + + CPLFree( pszSrcProj4Defn ); + CPLFree( pszDstProj4Defn ); + + return TRUE; +} + +/************************************************************************/ +/* GetSourceCS() */ +/************************************************************************/ + +OGRSpatialReference *OGRProj4CT::GetSourceCS() + +{ + return poSRSSource; +} + +/************************************************************************/ +/* GetTargetCS() */ +/************************************************************************/ + +OGRSpatialReference *OGRProj4CT::GetTargetCS() + +{ + return poSRSTarget; +} + +/************************************************************************/ +/* Transform() */ +/* */ +/* This is a small wrapper for the extended transform version. */ +/************************************************************************/ + +int OGRProj4CT::Transform( int nCount, double *x, double *y, double *z ) + +{ + int *pabSuccess = (int *) CPLMalloc(sizeof(int) * nCount ); + int bOverallSuccess, i; + + bOverallSuccess = TransformEx( nCount, x, y, z, pabSuccess ); + + for( i = 0; i < nCount; i++ ) + { + if( !pabSuccess[i] ) + { + bOverallSuccess = FALSE; + break; + } + } + + CPLFree( pabSuccess ); + + return bOverallSuccess; +} + +/************************************************************************/ +/* OCTTransform() */ +/************************************************************************/ + +int CPL_STDCALL OCTTransform( OGRCoordinateTransformationH hTransform, + int nCount, double *x, double *y, double *z ) + +{ + VALIDATE_POINTER1( hTransform, "OCTTransform", FALSE ); + + return ((OGRCoordinateTransformation*) hTransform)-> + Transform( nCount, x, y,z ); +} + +/************************************************************************/ +/* TransformEx() */ +/************************************************************************/ + +int OGRProj4CT::TransformEx( int nCount, double *x, double *y, double *z, + int *pabSuccess ) + +{ + int err, i; + +/* -------------------------------------------------------------------- */ +/* Potentially transform to radians. */ +/* -------------------------------------------------------------------- */ + if( bSourceLatLong ) + { + if( bSourceWrap ) + { + for( i = 0; i < nCount; i++ ) + { + if( x[i] != HUGE_VAL && y[i] != HUGE_VAL ) + { + if( x[i] < dfSourceWrapLong - 180.0 ) + x[i] += 360.0; + else if( x[i] > dfSourceWrapLong + 180 ) + x[i] -= 360.0; + } + } + } + + for( i = 0; i < nCount; i++ ) + { + if( x[i] != HUGE_VAL ) + { + x[i] *= dfSourceToRadians; + y[i] *= dfSourceToRadians; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Do the transformation using PROJ.4. */ +/* -------------------------------------------------------------------- */ + if( !bIdentityTransform && pjctx == NULL ) + { + /* The mutex has already been created */ + CPLAssert(hPROJMutex != NULL); + CPLAcquireMutex(hPROJMutex, 1000.0); + } + + if( bIdentityTransform ) + err = 0; + else if (bCheckWithInvertProj) + { + /* For some projections, we cannot detect if we are trying to reproject */ + /* coordinates outside the validity area of the projection. So let's do */ + /* the reverse reprojection and compare with the source coordinates */ + if (nCount > nMaxCount) + { + nMaxCount = nCount; + padfOriX = (double*) CPLRealloc(padfOriX, sizeof(double)*nCount); + padfOriY = (double*) CPLRealloc(padfOriY, sizeof(double)*nCount); + padfOriZ = (double*) CPLRealloc(padfOriZ, sizeof(double)*nCount); + padfTargetX = (double*) CPLRealloc(padfTargetX, sizeof(double)*nCount); + padfTargetY = (double*) CPLRealloc(padfTargetY, sizeof(double)*nCount); + padfTargetZ = (double*) CPLRealloc(padfTargetZ, sizeof(double)*nCount); + } + memcpy(padfOriX, x, sizeof(double)*nCount); + memcpy(padfOriY, y, sizeof(double)*nCount); + if (z) + { + memcpy(padfOriZ, z, sizeof(double)*nCount); + } + err = pfn_pj_transform( psPJSource, psPJTarget, nCount, 1, x, y, z ); + if (err == 0) + { + memcpy(padfTargetX, x, sizeof(double)*nCount); + memcpy(padfTargetY, y, sizeof(double)*nCount); + if (z) + { + memcpy(padfTargetZ, z, sizeof(double)*nCount); + } + + err = pfn_pj_transform( psPJTarget, psPJSource , nCount, 1, + padfTargetX, padfTargetY, (z) ? padfTargetZ : NULL); + if (err == 0) + { + for( i = 0; i < nCount; i++ ) + { + if ( x[i] != HUGE_VAL && y[i] != HUGE_VAL && + (fabs(padfTargetX[i] - padfOriX[i]) > dfThreshold || + fabs(padfTargetY[i] - padfOriY[i]) > dfThreshold) ) + { + x[i] = HUGE_VAL; + y[i] = HUGE_VAL; + } + } + } + } + } + else + { + err = pfn_pj_transform( psPJSource, psPJTarget, nCount, 1, x, y, z ); + } + +/* -------------------------------------------------------------------- */ +/* Try to report an error through CPL. Get proj.4 error string */ +/* if possible. Try to avoid reporting thousands of error */ +/* ... suppress further error reporting on this OGRProj4CT if we */ +/* have already reported 20 errors. */ +/* -------------------------------------------------------------------- */ + if( err != 0 ) + { + if( pabSuccess ) + memset( pabSuccess, 0, sizeof(int) * nCount ); + + if( ++nErrorCount < 20 ) + { + if (pjctx != NULL) + /* pfn_pj_strerrno not yet thread-safe in PROJ 4.8.0 */ + CPLAcquireMutex(hPROJMutex, 1000.0); + + const char *pszError = NULL; + if( pfn_pj_strerrno != NULL ) + pszError = pfn_pj_strerrno( err ); + + if( pszError == NULL ) + CPLError( CE_Failure, CPLE_AppDefined, + "Reprojection failed, err = %d", + err ); + else + CPLError( CE_Failure, CPLE_AppDefined, "%s", pszError ); + + if (pjctx != NULL) + /* pfn_pj_strerrno not yet thread-safe in PROJ 4.8.0 */ + CPLReleaseMutex(hPROJMutex); + } + else if( nErrorCount == 20 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Reprojection failed, err = %d, further errors will be suppressed on the transform object.", + err ); + } + + if (pjctx == NULL) + CPLReleaseMutex(hPROJMutex); + return FALSE; + } + + if( !bIdentityTransform && pjctx == NULL ) + CPLReleaseMutex(hPROJMutex); + +/* -------------------------------------------------------------------- */ +/* Potentially transform back to degrees. */ +/* -------------------------------------------------------------------- */ + if( bTargetLatLong ) + { + for( i = 0; i < nCount; i++ ) + { + if( x[i] != HUGE_VAL && y[i] != HUGE_VAL ) + { + x[i] *= dfTargetFromRadians; + y[i] *= dfTargetFromRadians; + } + } + + if( bTargetWrap ) + { + for( i = 0; i < nCount; i++ ) + { + if( x[i] != HUGE_VAL && y[i] != HUGE_VAL ) + { + if( x[i] < dfTargetWrapLong - 180.0 ) + x[i] += 360.0; + else if( x[i] > dfTargetWrapLong + 180 ) + x[i] -= 360.0; + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Establish error information if pabSuccess provided. */ +/* -------------------------------------------------------------------- */ + if( pabSuccess ) + { + for( i = 0; i < nCount; i++ ) + { + if( x[i] == HUGE_VAL || y[i] == HUGE_VAL ) + pabSuccess[i] = FALSE; + else + pabSuccess[i] = TRUE; + } + } + + return TRUE; +} + +/************************************************************************/ +/* OCTTransformEx() */ +/************************************************************************/ + +int CPL_STDCALL OCTTransformEx( OGRCoordinateTransformationH hTransform, + int nCount, double *x, double *y, double *z, + int *pabSuccess ) + +{ + VALIDATE_POINTER1( hTransform, "OCTTransformEx", FALSE ); + + return ((OGRCoordinateTransformation*) hTransform)-> + TransformEx( nCount, x, y, z, pabSuccess ); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrcurve.cpp b/bazaar/plugin/gdal/ogr/ogrcurve.cpp new file mode 100644 index 000000000..ff7238a74 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrcurve.cpp @@ -0,0 +1,390 @@ +/****************************************************************************** + * $Id: ogrcurve.cpp 28004 2014-11-26 09:58:44Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRCurve geometry class. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrcurve.cpp 28004 2014-11-26 09:58:44Z rouault $"); + +/************************************************************************/ +/* OGRCurve() */ +/************************************************************************/ + +OGRCurve::OGRCurve() +{ +} + +/************************************************************************/ +/* ~OGRCurve() */ +/************************************************************************/ + +OGRCurve::~OGRCurve() +{ +} + +/************************************************************************/ +/* getDimension() */ +/************************************************************************/ + +int OGRCurve::getDimension() const + +{ + return 1; +} + +/************************************************************************/ +/* get_IsClosed() */ +/************************************************************************/ + +/** + * \brief Return TRUE if curve is closed. + * + * Tests if a curve is closed. A curve is closed if its start point is + * equal to its end point. + * + * This method relates to the SFCOM ICurve::get_IsClosed() method. + * + * @return TRUE if closed, else FALSE. + */ + +int OGRCurve::get_IsClosed() const + +{ + OGRPoint oStartPoint, oEndPoint; + + StartPoint( &oStartPoint ); + EndPoint( &oEndPoint ); + + if( oStartPoint.getX() == oEndPoint.getX() + && oStartPoint.getY() == oEndPoint.getY() ) + { + return TRUE; + } + else + { + return FALSE; + } +} + +/** + * \fn double OGRCurve::get_Length() const; + * + * \brief Returns the length of the curve. + * + * This method relates to the SFCOM ICurve::get_Length() method. + * + * @return the length of the curve, zero if the curve hasn't been + * initialized. + */ + +/** + * \fn void OGRCurve::StartPoint( OGRPoint * poPoint ) const; + * + * \brief Return the curve start point. + * + * This method relates to the SF COM ICurve::get_StartPoint() method. + * + * @param poPoint the point to be assigned the start location. + */ + +/** + * \fn void OGRCurve::EndPoint( OGRPoint * poPoint ) const; + * + * \brief Return the curve end point. + * + * This method relates to the SF COM ICurve::get_EndPoint() method. + * + * @param poPoint the point to be assigned the end location. + */ + +/** + * \fn void OGRCurve::Value( double dfDistance, OGRPoint * poPoint ) const; + * + * \brief Fetch point at given distance along curve. + * + * This method relates to the SF COM ICurve::get_Value() method. + * + * This function is the same as the C function OGR_G_Value(). + * + * @param dfDistance distance along the curve at which to sample position. + * This distance should be between zero and get_Length() + * for this curve. + * @param poPoint the point to be assigned the curve position. + */ + +/** + * \fn OGRLineString* OGRCurve::CurveToLine( double dfMaxAngleStepSizeDegrees, const char* const* papszOptions ) const; + * + * \brief Return a linestring from a curve geometry. + * + * The returned geometry is a new instance whose ownership belongs to the caller. + * + * If the dfMaxAngleStepSizeDegrees is zero, then a default value will be + * used. This is currently 4 degrees unless the user has overridden the + * value with the OGR_ARC_STEPSIZE configuration variable. + * + * This method relates to the ISO SQL/MM Part 3 ICurve::CurveToLine() method. + * + * This function is the same as C function OGR_G_CurveToLine(). + * + * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the + * arc, zero to use the default setting. + * @param papszOptions options as a null-terminated list of strings or NULL. + * See OGRGeometryFactory::curveToLineString() for valid options. + * + * @return a line string approximating the curve + * + * @since GDAL 2.0 + */ + +/** + * \fn int OGRCurve::getNumPoints() const; + * + * \brief Return the number of points of a curve geometry. + * + * + * This method, as a method of OGRCurve, does not relate to a standard. + * For circular strings or linestrings, it returns the number of points, + * conforming to SF COM NumPoints(). + * For compound curves, it returns the sum of the number of points of each + * of its components (non including intermediate starting/ending points of + * the different parts). + * + * @return the number of points of the curve. + * + * @since GDAL 2.0 + */ + +/** + * \fn OGRPointIterator* OGRCurve::getPointIterator() const; + * + * \brief Returns a point iterator over the curve. + * + * The curve must not be modified while an iterator exists on it. + * + * The iterator must be destroyed with OGRPointIterator::destroy(). + * + * @return a point iterator over the curve. + * + * @since GDAL 2.0 + */ + +/** + * \fn double OGRCurve::get_Area() const; + * + * \brief Get the area of the (closed) curve. + * + * This method is designed to be used by OGRCurvePolygon::get_Area(). + * + * @return the area of the feature in square units of the spatial reference + * system in use. + * + * @since GDAL 2.0 + */ + +/** + * \fn double OGRCurve::get_AreaOfCurveSegments() const; + * + * \brief Get the area of the purely curve portions of a (closed) curve. + * + * This method is designed to be used on a closed convex curve. + * + * @return the area of the feature in square units of the spatial reference + * system in use. + * + * @since GDAL 2.0 + */ + +/************************************************************************/ +/* IsConvex() */ +/************************************************************************/ + +/** + * \brief Returns if a (closed) curve forms a convex shape. + * + * @return TRUE if the curve forms a convex shape. + * + * @since GDAL 2.0 + */ + +OGRBoolean OGRCurve::IsConvex() const +{ + OGRBoolean bRet = TRUE; + OGRPointIterator* poPointIter = getPointIterator(); + OGRPoint p1, p2, p3; + if( poPointIter->getNextPoint(&p1) && + poPointIter->getNextPoint(&p2) ) + { + while( poPointIter->getNextPoint(&p3) ) + { + double crossproduct = (p2.getX() - p1.getX()) * (p3.getY() - p2.getY()) - + (p2.getY() - p1.getY()) * (p3.getX() - p2.getX()); + if( crossproduct > 0 ) + { + bRet = FALSE; + break; + } + p1.setX(p2.getX()); + p1.setY(p2.getY()); + p2.setX(p3.getX()); + p2.setY(p3.getY()); + } + } + delete poPointIter; + return bRet; +} + +/************************************************************************/ +/* CastToCompoundCurve() */ +/************************************************************************/ + +/** + * \brief Cast to compound curve + * + * The passed in geometry is consumed and a new one returned (or NULL in case + * of failure) + * + * @param poCurve the input geometry - ownership is passed to the method. + * @return new geometry + * + * @since GDAL 2.0 + */ + +OGRCompoundCurve* OGRCurve::CastToCompoundCurve(OGRCurve* poCurve) +{ + OGRCompoundCurve* poCC = new OGRCompoundCurve(); + if( poCurve->getGeometryType() == wkbLineString ) + poCurve = CastToLineString(poCurve); + if( !poCurve->IsEmpty() && poCC->addCurveDirectly(poCurve) != OGRERR_NONE ) + { + delete poCC; + delete poCurve; + return NULL; + } + poCC->assignSpatialReference(poCurve->getSpatialReference()); + return poCC; +} + +/************************************************************************/ +/* CastToLineString() */ +/************************************************************************/ + +/** + * \brief Cast to linestring + * + * The passed in geometry is consumed and a new one returned (or NULL in case + * of failure) + * + * @param poCurve the input geometry - ownership is passed to the method. + * @return new geometry. + * + * @since GDAL 2.0 + */ + +OGRLineString* OGRCurve::CastToLineString(OGRCurve* poCurve) +{ + OGRCurveCasterToLineString pfn = poCurve->GetCasterToLineString(); + return pfn(poCurve); +} + +/************************************************************************/ +/* CastToLinearRing() */ +/************************************************************************/ + +/** + * \brief Cast to linear ring + * + * The passed in geometry is consumed and a new one returned (or NULL in case + * of failure) + * + * @param poCurve the input geometry - ownership is passed to the method. + * @return new geometry. + * + * @since GDAL 2.0 + */ + +OGRLinearRing* OGRCurve::CastToLinearRing(OGRCurve* poCurve) +{ + OGRCurveCasterToLinearRing pfn = poCurve->GetCasterToLinearRing(); + return pfn(poCurve); +} + +/************************************************************************/ +/* ContainsPoint() */ +/************************************************************************/ + +/** + * \brief Returns if a point is contained in a (closed) curve. + * + * Final users should use OGRGeometry::Contains() instead. + * + * @param p the point to test + * @return TRUE if it is inside the curve, FALSE otherwise or -1 if unknown. + * + * @since GDAL 2.0 + */ + +int OGRCurve::ContainsPoint( CPL_UNUSED const OGRPoint* p ) const +{ + return -1; +} + +/************************************************************************/ +/* ~OGRPointIterator() */ +/************************************************************************/ + +OGRPointIterator::~OGRPointIterator() +{ +} + +/** + * \fn OGRBoolean OGRPointIterator::getNextPoint(OGRPoint* p); + * + * \brief Returns the next point followed by the iterator. + * + * @param p point to fill. + * + * @return TRUE in case of success, or FALSE if the end of the curve is reached. + * + * @since GDAL 2.0 + */ + +/************************************************************************/ +/* destroy() */ +/************************************************************************/ + +/** + * \brief Destroys a point iterator. + * + * @since GDAL 2.0 + */ +void OGRPointIterator::destroy(OGRPointIterator* poIter) +{ + delete poIter; +} diff --git a/bazaar/plugin/gdal/ogr/ogrcurvecollection.cpp b/bazaar/plugin/gdal/ogr/ogrcurvecollection.cpp new file mode 100644 index 000000000..519423baa --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrcurvecollection.cpp @@ -0,0 +1,599 @@ +/****************************************************************************** + * $Id: ogrcurvecollection.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRCurveCollection class. + * Author: Even Rouault, even dot rouault at spatialys dot com + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" +#include + +CPL_CVSID("$Id"); + +/************************************************************************/ +/* OGRCurveCollection() */ +/************************************************************************/ + +OGRCurveCollection::OGRCurveCollection() + +{ + nCurveCount = 0; + papoCurves = NULL; +} + +/************************************************************************/ +/* ~OGRCurveCollection() */ +/************************************************************************/ + +OGRCurveCollection::~OGRCurveCollection() + +{ + empty(NULL); +} + +/************************************************************************/ +/* WkbSize() */ +/************************************************************************/ + +int OGRCurveCollection::WkbSize() const +{ + int nSize = 9; + + for( int i = 0; i < nCurveCount; i++ ) + { + nSize += papoCurves[i]->WkbSize(); + } + + return nSize; +} + +/************************************************************************/ +/* addCurveDirectly() */ +/************************************************************************/ + +OGRErr OGRCurveCollection::addCurveDirectly( OGRGeometry* poGeom, + OGRCurve* poCurve, + int bNeedRealloc ) +{ + if( poCurve->getCoordinateDimension() == 3 && poGeom->getCoordinateDimension() != 3 ) + poGeom->setCoordinateDimension(3); + else if( poCurve->getCoordinateDimension() != 3 && poGeom->getCoordinateDimension() == 3 ) + poCurve->setCoordinateDimension(3); + + if( bNeedRealloc ) + { + papoCurves = (OGRCurve **) OGRRealloc( papoCurves, + sizeof(OGRCurve*) * (nCurveCount+1) ); + } + + papoCurves[nCurveCount] = poCurve; + + nCurveCount++; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importPreambuleFromWkb() */ +/************************************************************************/ + +OGRErr OGRCurveCollection::importPreambuleFromWkb( OGRGeometry* poGeom, + unsigned char * pabyData, + int& nSize, + int& nDataOffset, + OGRwkbByteOrder& eByteOrder, + int nMinSubGeomSize, + OGRwkbVariant eWkbVariant ) +{ + OGRErr eErr = poGeom->importPreambuleOfCollectionFromWkb( + pabyData, + nSize, + nDataOffset, + eByteOrder, + nMinSubGeomSize, + nCurveCount, + eWkbVariant ); + if( eErr >= 0 ) + return eErr; + + papoCurves = (OGRCurve **) VSIMalloc2(sizeof(void*), nCurveCount); + if (nCurveCount != 0 && papoCurves == NULL) + { + nCurveCount = 0; + return OGRERR_NOT_ENOUGH_MEMORY; + } + + return -1; +} + +/************************************************************************/ +/* importBodyFromWkb() */ +/************************************************************************/ + +OGRErr OGRCurveCollection::importBodyFromWkb( OGRGeometry* poGeom, + unsigned char * pabyData, + int nSize, + int nDataOffset, + int bAcceptCompoundCurve, + OGRErr (*pfnAddCurveDirectlyFromWkb)(OGRGeometry* poGeom, OGRCurve* poCurve), + OGRwkbVariant eWkbVariant ) +{ + +/* -------------------------------------------------------------------- */ +/* Get the Geoms. */ +/* -------------------------------------------------------------------- */ + int nIter = nCurveCount; + nCurveCount = 0; + for( int iGeom = 0; iGeom < nIter; iGeom++ ) + { + OGRErr eErr; + OGRGeometry* poSubGeom = NULL; + + /* Parses sub-geometry */ + unsigned char* pabySubData = pabyData + nDataOffset; + if( nSize < 9 && nSize != -1 ) + return OGRERR_NOT_ENOUGH_DATA; + + OGRwkbGeometryType eSubGeomType; + OGRBoolean bIs3D; + if ( OGRReadWKBGeometryType( pabySubData, eWkbVariant, &eSubGeomType, &bIs3D ) != OGRERR_NONE ) + return OGRERR_FAILURE; + + if( (eSubGeomType != wkbCompoundCurve && OGR_GT_IsCurve(eSubGeomType)) || + (bAcceptCompoundCurve && eSubGeomType == wkbCompoundCurve) ) + { + eErr = OGRGeometryFactory:: + createFromWkb( pabySubData, NULL, + &poSubGeom, nSize, eWkbVariant ); + } + else + { + CPLDebug("OGR", "Cannot add geometry of type (%d) to geometry of type (%d)", + eSubGeomType, poGeom->getGeometryType()); + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + + if( eErr == OGRERR_NONE ) + eErr = pfnAddCurveDirectlyFromWkb(poGeom, (OGRCurve*)poSubGeom); + if( eErr != OGRERR_NONE ) + { + delete poSubGeom; + return eErr; + } + + int nSubGeomWkbSize = poSubGeom->WkbSize(); + if( nSize != -1 ) + nSize -= nSubGeomWkbSize; + + nDataOffset += nSubGeomWkbSize; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* exportToWkt() */ +/************************************************************************/ + +OGRErr OGRCurveCollection::exportToWkt( const OGRGeometry* poGeom, + char ** ppszDstText ) const + +{ + char **papszGeoms; + int iGeom, nCumulativeLength = 0; + OGRErr eErr; + + if( nCurveCount == 0 ) + { + CPLString osEmpty; + if( poGeom->getCoordinateDimension() == 3 ) + osEmpty.Printf("%s Z EMPTY",poGeom->getGeometryName()); + else + osEmpty.Printf("%s EMPTY",poGeom->getGeometryName()); + *ppszDstText = CPLStrdup(osEmpty); + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Build a list of strings containing the stuff for each Geom. */ +/* -------------------------------------------------------------------- */ + papszGeoms = (char **) CPLCalloc(sizeof(char *),nCurveCount); + + for( iGeom = 0; iGeom < nCurveCount; iGeom++ ) + { + eErr = papoCurves[iGeom]->exportToWkt( &(papszGeoms[iGeom]), wkbVariantIso ); + if( eErr != OGRERR_NONE ) + goto error; + + nCumulativeLength += strlen(papszGeoms[iGeom]); + } + +/* -------------------------------------------------------------------- */ +/* Allocate the right amount of space for the aggregated string */ +/* -------------------------------------------------------------------- */ + *ppszDstText = (char *) VSIMalloc(nCumulativeLength + nCurveCount + + strlen(poGeom->getGeometryName()) + 10); + + if( *ppszDstText == NULL ) + { + eErr = OGRERR_NOT_ENOUGH_MEMORY; + goto error; + } + +/* -------------------------------------------------------------------- */ +/* Build up the string, freeing temporary strings as we go. */ +/* -------------------------------------------------------------------- */ + strcpy( *ppszDstText, poGeom->getGeometryName() ); + if( poGeom->getCoordinateDimension() == 3 ) + strcat( *ppszDstText, " Z" ); + strcat( *ppszDstText, " (" ); + nCumulativeLength = strlen(*ppszDstText); + + for( iGeom = 0; iGeom < nCurveCount; iGeom++ ) + { + if( iGeom > 0 ) + (*ppszDstText)[nCumulativeLength++] = ','; + + /* We must strip the explicit "LINESTRING " prefix */ + int nSkip = 0; + if( !papoCurves[iGeom]->IsEmpty() && + EQUALN(papszGeoms[iGeom], "LINESTRING ", strlen("LINESTRING ")) ) + { + nSkip = strlen("LINESTRING "); + if( EQUALN(papszGeoms[iGeom] + nSkip, "Z ", 2) ) + nSkip += 2; + } + + int nGeomLength = strlen(papszGeoms[iGeom] + nSkip); + memcpy( *ppszDstText + nCumulativeLength, papszGeoms[iGeom] + nSkip, nGeomLength ); + nCumulativeLength += nGeomLength; + VSIFree( papszGeoms[iGeom] ); + } + + (*ppszDstText)[nCumulativeLength++] = ')'; + (*ppszDstText)[nCumulativeLength] = '\0'; + + CPLFree( papszGeoms ); + + return OGRERR_NONE; + +error: + for( iGeom = 0; iGeom < nCurveCount; iGeom++ ) + CPLFree( papszGeoms[iGeom] ); + CPLFree( papszGeoms ); + return eErr; +} + +/************************************************************************/ +/* exportToWkb() */ +/************************************************************************/ + +OGRErr OGRCurveCollection::exportToWkb( const OGRGeometry* poGeom, + OGRwkbByteOrder eByteOrder, + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const +{ + int nOffset; + +/* -------------------------------------------------------------------- */ +/* Set the byte order. */ +/* -------------------------------------------------------------------- */ + pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER((unsigned char) eByteOrder); + +/* -------------------------------------------------------------------- */ +/* Set the geometry feature type, ensuring that 3D flag is */ +/* preserved. */ +/* -------------------------------------------------------------------- */ + GUInt32 nGType = poGeom->getIsoGeometryType(); + if( eWkbVariant == wkbVariantPostGIS1 ) + { + int bIs3D = wkbHasZ((OGRwkbGeometryType)nGType); + nGType = wkbFlatten(nGType); + if( nGType == wkbCurvePolygon ) + nGType = POSTGIS15_CURVEPOLYGON; + if( bIs3D ) + nGType = (OGRwkbGeometryType)(nGType | wkb25DBitInternalUse); /* yes we explicitly set wkb25DBit */ + } + + if( eByteOrder == wkbNDR ) + nGType = CPL_LSBWORD32( nGType ); + else + nGType = CPL_MSBWORD32( nGType ); + + memcpy( pabyData + 1, &nGType, 4 ); + +/* -------------------------------------------------------------------- */ +/* Copy in the raw data. */ +/* -------------------------------------------------------------------- */ + if( OGR_SWAP( eByteOrder ) ) + { + int nCount; + + nCount = CPL_SWAP32( nCurveCount ); + memcpy( pabyData+5, &nCount, 4 ); + } + else + { + memcpy( pabyData+5, &nCurveCount, 4 ); + } + + nOffset = 9; + +/* ==================================================================== */ +/* Serialize each of the Geoms. */ +/* ==================================================================== */ + for( int iGeom = 0; iGeom < nCurveCount; iGeom++ ) + { + papoCurves[iGeom]->exportToWkb( eByteOrder, pabyData + nOffset, eWkbVariant ); + + nOffset += papoCurves[iGeom]->WkbSize(); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* empty() */ +/************************************************************************/ + +void OGRCurveCollection::empty(OGRGeometry* poGeom) +{ + if( papoCurves != NULL ) + { + for( int i = 0; i < nCurveCount; i++ ) + { + delete papoCurves[i]; + } + OGRFree( papoCurves ); + } + + nCurveCount = 0; + papoCurves = NULL; + if( poGeom ) + poGeom->setCoordinateDimension(2); +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRCurveCollection::getEnvelope( OGREnvelope * psEnvelope ) const +{ + OGREnvelope3D oEnv3D; + getEnvelope(&oEnv3D); + psEnvelope->MinX = oEnv3D.MinX; + psEnvelope->MinY = oEnv3D.MinY; + psEnvelope->MaxX = oEnv3D.MaxX; + psEnvelope->MaxY = oEnv3D.MaxY; +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRCurveCollection::getEnvelope( OGREnvelope3D * psEnvelope ) const +{ + OGREnvelope3D oGeomEnv; + int bExtentSet = FALSE; + + for( int iGeom = 0; iGeom < nCurveCount; iGeom++ ) + { + if (!papoCurves[iGeom]->IsEmpty()) + { + if (!bExtentSet) + { + papoCurves[iGeom]->getEnvelope( psEnvelope ); + bExtentSet = TRUE; + } + else + { + papoCurves[iGeom]->getEnvelope( &oGeomEnv ); + psEnvelope->Merge( oGeomEnv ); + } + } + } + + if (!bExtentSet) + { + psEnvelope->MinX = psEnvelope->MinY = psEnvelope->MinZ = 0; + psEnvelope->MaxX = psEnvelope->MaxY = psEnvelope->MaxZ = 0; + } +} + +/************************************************************************/ +/* IsEmpty() */ +/************************************************************************/ + +OGRBoolean OGRCurveCollection::IsEmpty() const +{ + return nCurveCount == 0; +} + +/************************************************************************/ +/* Equals() */ +/************************************************************************/ + +OGRBoolean OGRCurveCollection::Equals( OGRCurveCollection *poOCC ) const +{ + if( getNumCurves() != poOCC->getNumCurves() ) + return FALSE; + + // we should eventually test the SRS. + + for( int iGeom = 0; iGeom < nCurveCount; iGeom++ ) + { + if( !getCurve(iGeom)->Equals(poOCC->getCurve(iGeom)) ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* setCoordinateDimension() */ +/************************************************************************/ + +void OGRCurveCollection::setCoordinateDimension( OGRGeometry* poGeom, + int nNewDimension ) +{ + for( int iGeom = 0; iGeom < nCurveCount; iGeom++ ) + { + papoCurves[iGeom]->setCoordinateDimension( nNewDimension ); + } + + poGeom->OGRGeometry::setCoordinateDimension( nNewDimension ); +} + +/************************************************************************/ +/* getNumCurves() */ +/************************************************************************/ + +int OGRCurveCollection::getNumCurves() const +{ + return nCurveCount; +} + +/************************************************************************/ +/* getCurve() */ +/************************************************************************/ + +OGRCurve *OGRCurveCollection::getCurve( int i ) +{ + if( i < 0 || i >= nCurveCount ) + return NULL; + return papoCurves[i]; +} + +/************************************************************************/ +/* getCurve() */ +/************************************************************************/ + +const OGRCurve *OGRCurveCollection::getCurve( int i ) const +{ + if( i < 0 || i >= nCurveCount ) + return NULL; + return papoCurves[i]; +} + +/************************************************************************/ +/* stealCurve() */ +/************************************************************************/ + +OGRCurve* OGRCurveCollection::stealCurve( int i ) +{ + if( i < 0 || i >= nCurveCount ) + return NULL; + OGRCurve* poRet = papoCurves[i]; + if( i < nCurveCount - 1 ) + { + memmove(papoCurves + i, papoCurves + i + 1, (nCurveCount - i - 1) * sizeof(OGRCurve*)); + } + nCurveCount --; + return poRet; +} + +/************************************************************************/ +/* transform() */ +/************************************************************************/ + +OGRErr OGRCurveCollection::transform( OGRGeometry* poGeom, + OGRCoordinateTransformation *poCT ) +{ +#ifdef DISABLE_OGRGEOM_TRANSFORM + return OGRERR_FAILURE; +#else + for( int iGeom = 0; iGeom < nCurveCount; iGeom++ ) + { + OGRErr eErr; + + eErr = papoCurves[iGeom]->transform( poCT ); + if( eErr != OGRERR_NONE ) + { + if( iGeom != 0 ) + { + CPLDebug("OGR", + "OGRCurveCollection::transform() failed for a geometry other\n" + "than the first, meaning some geometries are transformed\n" + "and some are not!\n" ); + + return OGRERR_FAILURE; + } + + return eErr; + } + } + + poGeom->assignSpatialReference( poCT->GetTargetCS() ); + + return OGRERR_NONE; +#endif +} + +/************************************************************************/ +/* flattenTo2D() */ +/************************************************************************/ + +void OGRCurveCollection::flattenTo2D(OGRGeometry* poGeom) +{ + for( int i = 0; i < nCurveCount; i++ ) + papoCurves[i]->flattenTo2D(); + + poGeom->setCoordinateDimension(2); +} + +/************************************************************************/ +/* segmentize() */ +/************************************************************************/ + +void OGRCurveCollection::segmentize(double dfMaxLength) +{ + for( int i = 0; i < nCurveCount; i++ ) + papoCurves[i]->segmentize(dfMaxLength); +} + +/************************************************************************/ +/* swapXY() */ +/************************************************************************/ + +void OGRCurveCollection::swapXY() +{ + for( int i = 0; i < nCurveCount; i++ ) + papoCurves[i]->swapXY(); +} + +/************************************************************************/ +/* hasCurveGeometry() */ +/************************************************************************/ + +OGRBoolean OGRCurveCollection::hasCurveGeometry(int bLookForNonLinear) const +{ + for( int i = 0; i < nCurveCount; i++ ) + { + if( papoCurves[i]->hasCurveGeometry(bLookForNonLinear) ) + return TRUE; + } + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrcurvepolygon.cpp b/bazaar/plugin/gdal/ogr/ogrcurvepolygon.cpp new file mode 100644 index 000000000..5edd9637f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrcurvepolygon.cpp @@ -0,0 +1,717 @@ +/****************************************************************************** + * $Id$ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRCurvePolygon geometry class. + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" +#include "ogr_geos.h" +#include "ogr_api.h" + +CPL_CVSID("$Id$"); + +/************************************************************************/ +/* OGRCurvePolygon() */ +/************************************************************************/ + +/** + * \brief Create an empty curve polygon. + */ + +OGRCurvePolygon::OGRCurvePolygon() + +{ +} + +/************************************************************************/ +/* ~OGRCurvePolygon() */ +/************************************************************************/ + +OGRCurvePolygon::~OGRCurvePolygon() + +{ +} + +/************************************************************************/ +/* clone() */ +/************************************************************************/ + +OGRGeometry *OGRCurvePolygon::clone() const + +{ + OGRCurvePolygon *poNewPolygon; + + poNewPolygon = (OGRCurvePolygon*) + OGRGeometryFactory::createGeometry(getGeometryType()); + poNewPolygon->assignSpatialReference( getSpatialReference() ); + + for( int i = 0; i < oCC.nCurveCount; i++ ) + { + poNewPolygon->addRing( oCC.papoCurves[i] ); + } + + return poNewPolygon; +} + +/************************************************************************/ +/* empty() */ +/************************************************************************/ + +void OGRCurvePolygon::empty() + +{ + oCC.empty(this); +} + +/************************************************************************/ +/* getGeometryType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRCurvePolygon::getGeometryType() const + +{ + if( nCoordDimension == 3 ) + return wkbCurvePolygonZ; + else + return wkbCurvePolygon; +} + +/************************************************************************/ +/* getDimension() */ +/************************************************************************/ + +int OGRCurvePolygon::getDimension() const + +{ + return 2; +} + +/************************************************************************/ +/* flattenTo2D() */ +/************************************************************************/ + +void OGRCurvePolygon::flattenTo2D() + +{ + oCC.flattenTo2D(this); +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRCurvePolygon::getGeometryName() const + +{ + return "CURVEPOLYGON"; +} + +/************************************************************************/ +/* getExteriorRingCurve() */ +/************************************************************************/ + +/** + * \brief Fetch reference to external polygon ring. + * + * Note that the returned ring pointer is to an internal data object of + * the OGRCurvePolygon. It should not be modified or deleted by the application, + * and the pointer is only valid till the polygon is next modified. Use + * the OGRGeometry::clone() method to make a separate copy within the + * application. + * + * Relates to the SFCOM IPolygon::get_ExteriorRing() method. + * + * @return pointer to external ring. May be NULL if the OGRCurvePolygon is empty. + */ + +OGRCurve *OGRCurvePolygon::getExteriorRingCurve() + +{ + return oCC.getCurve(0); +} + +const OGRCurve *OGRCurvePolygon::getExteriorRingCurve() const + +{ + return oCC.getCurve(0); +} + +/************************************************************************/ +/* getNumInteriorRings() */ +/************************************************************************/ + +/** + * \brief Fetch the number of internal rings. + * + * Relates to the SFCOM IPolygon::get_NumInteriorRings() method. + * + * @return count of internal rings, zero or more. + */ + + +int OGRCurvePolygon::getNumInteriorRings() const + +{ + if( oCC.nCurveCount > 0 ) + return oCC.nCurveCount-1; + else + return 0; +} + +/************************************************************************/ +/* getInteriorRingCurve() */ +/************************************************************************/ + +/** + * \brief Fetch reference to indicated internal ring. + * + * Note that the returned ring pointer is to an internal data object of + * the OGRCurvePolygon. It should not be modified or deleted by the application, + * and the pointer is only valid till the polygon is next modified. Use + * the OGRGeometry::clone() method to make a separate copy within the + * application. + * + * Relates to the SFCOM IPolygon::get_InternalRing() method. + * + * @param iRing internal ring index from 0 to getNumInternalRings() - 1. + * + * @return pointer to interior ring. May be NULL. + */ + +OGRCurve *OGRCurvePolygon::getInteriorRingCurve( int iRing ) + +{ + return oCC.getCurve(iRing + 1); +} + +const OGRCurve *OGRCurvePolygon::getInteriorRingCurve( int iRing ) const + +{ + return oCC.getCurve(iRing + 1); +} + +/************************************************************************/ +/* stealExteriorRingCurve() */ +/************************************************************************/ + +/** + * \brief "Steal" reference to external ring. + * + * After the call to that function, only call to stealInteriorRing() or + * destruction of the OGRCurvePolygon is valid. Other operations may crash. + * + * @return pointer to external ring. May be NULL if the OGRCurvePolygon is empty. + */ + +OGRCurve *OGRCurvePolygon::stealExteriorRingCurve() +{ + if( oCC.nCurveCount == 0 ) + return NULL; + OGRCurve *poRet = oCC.papoCurves[0]; + oCC.papoCurves[0] = NULL; + return poRet; +} + +/************************************************************************/ +/* addRing() */ +/************************************************************************/ + +/** + * \brief Add a ring to a polygon. + * + * If the polygon has no external ring (it is empty) this will be used as + * the external ring, otherwise it is used as an internal ring. The passed + * OGRCurve remains the responsibility of the caller (an internal copy + * is made). + * + * This method has no SFCOM analog. + * + * @param poNewRing ring to be added to the polygon. + * @return OGRERR_NONE in case of success + */ + +OGRErr OGRCurvePolygon::addRing( OGRCurve * poNewRing ) + +{ + OGRCurve* poNewRingCloned = (OGRCurve* )poNewRing->clone(); + OGRErr eErr = addRingDirectly(poNewRingCloned); + if( eErr != OGRERR_NONE ) + delete poNewRingCloned; + return eErr; +} + +/************************************************************************/ +/* checkRing() */ +/************************************************************************/ + +int OGRCurvePolygon::checkRing( OGRCurve * poNewRing ) const +{ + if( !poNewRing->IsEmpty() && !poNewRing->get_IsClosed() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Non closed ring."); + return FALSE; + } + + if( wkbFlatten(poNewRing->getGeometryType()) == wkbLineString ) + { + if( poNewRing->getNumPoints() == 0 || poNewRing->getNumPoints() < 4 ) + { + return FALSE; + } + + if( EQUAL(poNewRing->getGeometryName(), "LINEARRING") ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Linearring not allowed."); + return FALSE; + } + } + + return TRUE; +} + +/************************************************************************/ +/* addRingDirectly() */ +/************************************************************************/ + +/** + * \brief Add a ring to a polygon. + * + * If the polygon has no external ring (it is empty) this will be used as + * the external ring, otherwise it is used as an internal ring. Ownership + * of the passed ring is assumed by the OGRCurvePolygon, but otherwise this + * method operates the same as OGRCurvePolygon::AddRing(). + * + * This method has no SFCOM analog. + * + * @param poNewRing ring to be added to the polygon. + * @return OGRERR_NONE in case of success + */ + +OGRErr OGRCurvePolygon::addRingDirectly( OGRCurve * poNewRing ) +{ + return addRingDirectlyInternal( poNewRing, TRUE ); +} + +OGRErr OGRCurvePolygon::addRingDirectlyInternal( OGRCurve* poNewRing, + int bNeedRealloc ) +{ + if( !checkRing(poNewRing) ) + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + + return oCC.addCurveDirectly(this, poNewRing, bNeedRealloc); +} + +/************************************************************************/ +/* WkbSize() */ +/* */ +/* Return the size of this object in well known binary */ +/* representation including the byte order, and type information. */ +/************************************************************************/ + +int OGRCurvePolygon::WkbSize() const + +{ + return oCC.WkbSize(); +} + +/************************************************************************/ +/* addCurveDirectlyFromWkt() */ +/************************************************************************/ + +OGRErr OGRCurvePolygon::addCurveDirectlyFromWkb( OGRGeometry* poSelf, + OGRCurve* poCurve ) +{ + OGRCurvePolygon* poCP = (OGRCurvePolygon*)poSelf; + return poCP->addRingDirectlyInternal( poCurve, FALSE ); +} + +/************************************************************************/ +/* importFromWkb() */ +/* */ +/* Initialize from serialized stream in well known binary */ +/* format. */ +/************************************************************************/ + +OGRErr OGRCurvePolygon::importFromWkb( unsigned char * pabyData, + int nSize, + OGRwkbVariant eWkbVariant ) + +{ + OGRwkbByteOrder eByteOrder; + int nDataOffset = 0; + OGRErr eErr = oCC.importPreambuleFromWkb(this, pabyData, nSize, nDataOffset, + eByteOrder, 9, eWkbVariant); + if( eErr >= 0 ) + return eErr; + + return oCC.importBodyFromWkb(this, pabyData, nSize, nDataOffset, + TRUE /* bAcceptCompoundCurve */, addCurveDirectlyFromWkb, + eWkbVariant); +} + +/************************************************************************/ +/* exportToWkb() */ +/* */ +/* Build a well known binary representation of this object. */ +/************************************************************************/ + +OGRErr OGRCurvePolygon::exportToWkb( OGRwkbByteOrder eByteOrder, + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const + +{ + if( eWkbVariant == wkbVariantOldOgc ) /* does not make sense for new geometries, so patch it */ + eWkbVariant = wkbVariantIso; + return oCC.exportToWkb(this, eByteOrder, pabyData, eWkbVariant); +} + +/************************************************************************/ +/* addCurveDirectlyFromWkt() */ +/************************************************************************/ + +OGRErr OGRCurvePolygon::addCurveDirectlyFromWkt( OGRGeometry* poSelf, OGRCurve* poCurve ) +{ + return ((OGRCurvePolygon*)poSelf)->addRingDirectly(poCurve); +} + +/************************************************************************/ +/* importFromWkt() */ +/* */ +/* Instantiate from well known text format. */ +/************************************************************************/ + +OGRErr OGRCurvePolygon::importFromWkt( char ** ppszInput ) + +{ + return importCurveCollectionFromWkt( ppszInput, + FALSE, /* bAllowEmptyComponent */ + TRUE, /* bAllowLineString */ + TRUE, /* bAllowCurve */ + TRUE, /* bAllowCompoundCurve */ + addCurveDirectlyFromWkt ); +} + +/************************************************************************/ +/* exportToWkt() */ +/************************************************************************/ + +OGRErr OGRCurvePolygon::exportToWkt( char ** ppszDstText, + CPL_UNUSED OGRwkbVariant eWkbVariant ) const + +{ + return oCC.exportToWkt(this, ppszDstText); +} + +/************************************************************************/ +/* CurvePolyToPoly() */ +/************************************************************************/ + +/** + * \brief Return a polygon from a curve polygon. + * + * This method is the same as C function OGR_G_CurvePolyToPoly(). + * + * The returned geometry is a new instance whose ownership belongs to the caller. + * + * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the + * arc, zero to use the default setting. + * @param papszOptions options as a null-terminated list of strings. + * Unused for now. Must be set to NULL. + * + * @return a linestring + * + * @since OGR 2.0 + */ + +OGRPolygon* OGRCurvePolygon::CurvePolyToPoly(double dfMaxAngleStepSizeDegrees, + const char* const* papszOptions) const +{ + OGRPolygon* poPoly = new OGRPolygon(); + poPoly->assignSpatialReference(getSpatialReference()); + for( int iRing = 0; iRing < oCC.nCurveCount; iRing++ ) + { + OGRLineString* poLS = oCC.papoCurves[iRing]->CurveToLine(dfMaxAngleStepSizeDegrees, + papszOptions); + poPoly->addRingDirectly(OGRCurve::CastToLinearRing(poLS)); + } + return poPoly; +} + +/************************************************************************/ +/* hasCurveGeometry() */ +/************************************************************************/ + +OGRBoolean OGRCurvePolygon::hasCurveGeometry(int bLookForNonLinear) const +{ + if( bLookForNonLinear ) + { + return oCC.hasCurveGeometry(bLookForNonLinear); + } + else + return TRUE; +} + +/************************************************************************/ +/* getLinearGeometry() */ +/************************************************************************/ + +OGRGeometry* OGRCurvePolygon::getLinearGeometry(double dfMaxAngleStepSizeDegrees, + const char* const* papszOptions) const +{ + return CurvePolyToPoly(dfMaxAngleStepSizeDegrees, papszOptions); +} + +/************************************************************************/ +/* PointOnSurface() */ +/************************************************************************/ + +int OGRCurvePolygon::PointOnSurface( OGRPoint *poPoint ) const + +{ + OGRPolygon* poPoly = CurvePolyToPoly(); + int ret = poPoly->PointOnSurface(poPoint); + delete poPoly; + return ret; +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRCurvePolygon::getEnvelope( OGREnvelope * psEnvelope ) const + +{ + oCC.getEnvelope(psEnvelope); +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRCurvePolygon::getEnvelope( OGREnvelope3D * psEnvelope ) const + +{ + oCC.getEnvelope(psEnvelope); +} + +/************************************************************************/ +/* Equal() */ +/************************************************************************/ + +OGRBoolean OGRCurvePolygon::Equals( OGRGeometry * poOther ) const + +{ + OGRCurvePolygon *poOPoly = (OGRCurvePolygon *) poOther; + + if( poOPoly == this ) + return TRUE; + + if( poOther->getGeometryType() != getGeometryType() ) + return FALSE; + + if ( IsEmpty() && poOther->IsEmpty() ) + return TRUE; + + return oCC.Equals( &(poOPoly->oCC) ); +} + +/************************************************************************/ +/* transform() */ +/************************************************************************/ + +OGRErr OGRCurvePolygon::transform( OGRCoordinateTransformation *poCT ) + +{ + return oCC.transform(this, poCT); +} + +/************************************************************************/ +/* get_Area() */ +/************************************************************************/ + +double OGRCurvePolygon::get_Area() const + +{ + double dfArea = 0.0; + + if( getExteriorRingCurve() != NULL ) + { + int iRing; + + dfArea = getExteriorRingCurve()->get_Area(); + + for( iRing = 0; iRing < getNumInteriorRings(); iRing++ ) + { + dfArea -= getInteriorRingCurve(iRing)->get_Area(); + } + } + + return dfArea; +} + +/************************************************************************/ +/* setCoordinateDimension() */ +/************************************************************************/ + +void OGRCurvePolygon::setCoordinateDimension( int nNewDimension ) + +{ + oCC.setCoordinateDimension(this, nNewDimension); +} + + +/************************************************************************/ +/* IsEmpty() */ +/************************************************************************/ + +OGRBoolean OGRCurvePolygon::IsEmpty( ) const +{ + return oCC.IsEmpty(); +} + +/************************************************************************/ +/* segmentize() */ +/************************************************************************/ + +void OGRCurvePolygon::segmentize( double dfMaxLength ) +{ + oCC.segmentize(dfMaxLength); +} + +/************************************************************************/ +/* swapXY() */ +/************************************************************************/ + +void OGRCurvePolygon::swapXY() +{ + oCC.swapXY(); +} + +/************************************************************************/ +/* ContainsPoint() */ +/************************************************************************/ + +OGRBoolean OGRCurvePolygon::ContainsPoint( const OGRPoint* p ) const +{ + if( getExteriorRingCurve() != NULL && + getNumInteriorRings() == 0 ) + { + int nRet = getExteriorRingCurve()->ContainsPoint(p); + if( nRet >= 0 ) + return nRet; + } + return OGRGeometry::Contains(p); +} + +/************************************************************************/ +/* Contains() */ +/************************************************************************/ + +OGRBoolean OGRCurvePolygon::Contains( const OGRGeometry *poOtherGeom ) const + +{ + if( !IsEmpty() && poOtherGeom != NULL && + wkbFlatten(poOtherGeom->getGeometryType()) == wkbPoint ) + { + return ContainsPoint((OGRPoint*)poOtherGeom); + } + else + return OGRGeometry::Contains(poOtherGeom); +} + +/************************************************************************/ +/* Intersects() */ +/************************************************************************/ + +OGRBoolean OGRCurvePolygon::Intersects( const OGRGeometry *poOtherGeom ) const + +{ + if( !IsEmpty() && poOtherGeom != NULL && + wkbFlatten(poOtherGeom->getGeometryType()) == wkbPoint ) + { + return ContainsPoint((OGRPoint*)poOtherGeom); + } + else + return OGRGeometry::Intersects(poOtherGeom); +} + +/************************************************************************/ +/* CastToPolygon() */ +/************************************************************************/ + +/** + * \brief Convert to polygon. + * + * This method should only be called if the curve polygon actually only contains + * instances of OGRLineString. This can be verified if hasCurveGeometry(TRUE) + * returns FALSE. It is not intended to approximate curve polygons. For that + * use getLinearGeometry(). + * + * The passed in geometry is consumed and a new one returned (or NULL in case + * of failure). + * + * @param poMS the input geometry - ownership is passed to the method. + * @return new geometry. + */ + +OGRPolygon* OGRCurvePolygon::CastToPolygon(OGRCurvePolygon* poCP) +{ + for(int i=0;ioCC.nCurveCount;i++) + { + poCP->oCC.papoCurves[i] = OGRCurve::CastToLinearRing(poCP->oCC.papoCurves[i]); + if( poCP->oCC.papoCurves[i] == NULL ) + { + delete poCP; + return NULL; + } + } + OGRPolygon* poPoly = new OGRPolygon(); + poPoly->setCoordinateDimension(poCP->getCoordinateDimension()); + poPoly->assignSpatialReference(poCP->getSpatialReference()); + poPoly->oCC.nCurveCount = poCP->oCC.nCurveCount; + poPoly->oCC.papoCurves = poCP->oCC.papoCurves; + poCP->oCC.nCurveCount = 0; + poCP->oCC.papoCurves = NULL; + delete poCP; + return poPoly; +} + +/************************************************************************/ +/* GetCasterToPolygon() */ +/************************************************************************/ + +OGRSurfaceCasterToPolygon OGRCurvePolygon::GetCasterToPolygon() const { + return (OGRSurfaceCasterToPolygon) OGRCurvePolygon::CastToPolygon; +} + +/************************************************************************/ +/* OGRSurfaceCasterToCurvePolygon() */ +/************************************************************************/ + +OGRSurfaceCasterToCurvePolygon OGRCurvePolygon::GetCasterToCurvePolygon() const { + return (OGRSurfaceCasterToCurvePolygon) OGRGeometry::CastToIdentity; +} diff --git a/bazaar/plugin/gdal/ogr/ogrfeature.cpp b/bazaar/plugin/gdal/ogr/ogrfeature.cpp new file mode 100644 index 000000000..a1cd3a6ca --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrfeature.cpp @@ -0,0 +1,5152 @@ +/****************************************************************************** + * $Id: ogrfeature.cpp 28900 2015-04-14 09:40:34Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRFeature class implementation. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_feature.h" +#include "ogr_api.h" +#include "ogr_p.h" +#include "cpl_time.h" +#include +#include + +CPL_CVSID("$Id: ogrfeature.cpp 28900 2015-04-14 09:40:34Z rouault $"); + +/************************************************************************/ +/* OGRFeature() */ +/************************************************************************/ + +/** + * \brief Constructor + * + * Note that the OGRFeature will increment the reference count of it's + * defining OGRFeatureDefn. Destruction of the OGRFeatureDefn before + * destruction of all OGRFeatures that depend on it is likely to result in + * a crash. + * + * This method is the same as the C function OGR_F_Create(). + * + * @param poDefnIn feature class (layer) definition to which the feature will + * adhere. + */ + +OGRFeature::OGRFeature( OGRFeatureDefn * poDefnIn ) + +{ + m_pszStyleString = NULL; + m_poStyleTable = NULL; + m_pszTmpFieldValue = NULL; + poDefnIn->Reference(); + poDefn = poDefnIn; + + nFID = OGRNullFID; + + // Allocate array of fields and initialize them to the unset special value + pauFields = (OGRField *) CPLMalloc( poDefn->GetFieldCount() * + sizeof(OGRField) ); + + papoGeometries = (OGRGeometry **) CPLCalloc( poDefn->GetGeomFieldCount(), + sizeof(OGRGeometry*) ); + + for( int i = 0; i < poDefn->GetFieldCount(); i++ ) + { + pauFields[i].Set.nMarker1 = OGRUnsetMarker; + pauFields[i].Set.nMarker2 = OGRUnsetMarker; + } +} + +/************************************************************************/ +/* OGR_F_Create() */ +/************************************************************************/ +/** + * \brief Feature factory. + * + * Note that the OGRFeature will increment the reference count of it's + * defining OGRFeatureDefn. Destruction of the OGRFeatureDefn before + * destruction of all OGRFeatures that depend on it is likely to result in + * a crash. + * + * This function is the same as the C++ method OGRFeature::OGRFeature(). + * + * @param hDefn handle to the feature class (layer) definition to + * which the feature will adhere. + * + * @return an handle to the new feature object with null fields and + * no geometry. + */ + +OGRFeatureH OGR_F_Create( OGRFeatureDefnH hDefn ) + +{ + VALIDATE_POINTER1( hDefn, "OGR_F_Create", NULL ); + + return (OGRFeatureH) new OGRFeature( (OGRFeatureDefn *) hDefn ); +} + +/************************************************************************/ +/* ~OGRFeature() */ +/************************************************************************/ + +OGRFeature::~OGRFeature() + +{ + int i; + + int nFieldcount = poDefn->GetFieldCount(); + for( i = 0; i < nFieldcount; i++ ) + { + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn(i); + + if( !IsFieldSet(i) ) + continue; + + switch( poFDefn->GetType() ) + { + case OFTString: + if( pauFields[i].String != NULL ) + VSIFree( pauFields[i].String ); + break; + + case OFTBinary: + if( pauFields[i].Binary.paData != NULL ) + VSIFree( pauFields[i].Binary.paData ); + break; + + case OFTStringList: + CSLDestroy( pauFields[i].StringList.paList ); + break; + + case OFTIntegerList: + case OFTInteger64List: + case OFTRealList: + CPLFree( pauFields[i].IntegerList.paList ); + break; + + default: + // should add support for wide strings. + break; + } + } + + int nGeomFieldCount = poDefn->GetGeomFieldCount(); + for( i = 0; i < nGeomFieldCount; i++ ) + { + delete papoGeometries[i]; + } + + poDefn->Release(); + + CPLFree( pauFields ); + CPLFree( papoGeometries ); + CPLFree(m_pszStyleString); + CPLFree(m_pszTmpFieldValue); +} + +/************************************************************************/ +/* OGR_F_Destroy() */ +/************************************************************************/ +/** + * \brief Destroy feature + * + * The feature is deleted, but within the context of the GDAL/OGR heap. + * This is necessary when higher level applications use GDAL/OGR from a + * DLL and they want to delete a feature created within the DLL. If the + * delete is done in the calling application the memory will be freed onto + * the application heap which is inappropriate. + * + * This function is the same as the C++ method OGRFeature::DestroyFeature(). + * + * @param hFeat handle to the feature to destroy. + */ + +void OGR_F_Destroy( OGRFeatureH hFeat ) + +{ + delete (OGRFeature *) hFeat; +} + +/************************************************************************/ +/* CreateFeature() */ +/************************************************************************/ + +/** + * \brief Feature factory. + * + * This is essentially a feature factory, useful for + * applications creating features but wanting to ensure they + * are created out of the OGR/GDAL heap. + * + * This method is the same as the C function OGR_F_Create(). + * + * @param poDefn Feature definition defining schema. + * + * @return new feature object with null fields and no geometry. May be + * deleted with delete. + */ + +OGRFeature *OGRFeature::CreateFeature( OGRFeatureDefn *poDefn ) + +{ + return new OGRFeature( poDefn ); +} + +/************************************************************************/ +/* DestroyFeature() */ +/************************************************************************/ + +/** + * \brief Destroy feature + * + * The feature is deleted, but within the context of the GDAL/OGR heap. + * This is necessary when higher level applications use GDAL/OGR from a + * DLL and they want to delete a feature created within the DLL. If the + * delete is done in the calling application the memory will be freed onto + * the application heap which is inappropriate. + * + * This method is the same as the C function OGR_F_Destroy(). + * + * @param poFeature the feature to delete. + */ + +void OGRFeature::DestroyFeature( OGRFeature *poFeature ) + +{ + delete poFeature; +} + +/************************************************************************/ +/* GetDefnRef() */ +/************************************************************************/ + +/** + * \fn OGRFeatureDefn *OGRFeature::GetDefnRef(); + * + * \brief Fetch feature definition. + * + * This method is the same as the C function OGR_F_GetDefnRef(). + * + * @return a reference to the feature definition object. + */ + +/************************************************************************/ +/* OGR_F_GetDefnRef() */ +/************************************************************************/ + +/** + * \brief Fetch feature definition. + * + * This function is the same as the C++ method OGRFeature::GetDefnRef(). + * + * @param hFeat handle to the feature to get the feature definition from. + * + * @return an handle to the feature definition object on which feature + * depends. + */ + +OGRFeatureDefnH OGR_F_GetDefnRef( OGRFeatureH hFeat ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetDefnRef", NULL ); + + return (OGRFeatureDefnH) ((OGRFeature *) hFeat)->GetDefnRef(); +} + +/************************************************************************/ +/* SetGeometryDirectly() */ +/************************************************************************/ + +/** + * \brief Set feature geometry. + * + * This method updates the features geometry, and operate exactly as + * SetGeometry(), except that this method assumes ownership of the + * passed geometry (even in case of failure of that function). + * + * This method is the same as the C function OGR_F_SetGeometryDirectly(). + * + * @param poGeomIn new geometry to apply to feature. Passing NULL value here + * is correct and it will result in deallocation of currently assigned geometry + * without assigning new one. + * + * @return OGRERR_NONE if successful, or OGR_UNSUPPORTED_GEOMETRY_TYPE if + * the geometry type is illegal for the OGRFeatureDefn (checking not yet + * implemented). + */ + +OGRErr OGRFeature::SetGeometryDirectly( OGRGeometry * poGeomIn ) + +{ + if( GetGeomFieldCount() > 0 ) + return SetGeomFieldDirectly(0, poGeomIn); + else + { + delete poGeomIn; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* OGR_F_SetGeometryDirectly() */ +/************************************************************************/ + +/** + * \brief Set feature geometry. + * + * This function updates the features geometry, and operate exactly as + * SetGeometry(), except that this function assumes ownership of the + * passed geometry (even in case of failure of that function). + * + * This function is the same as the C++ method + * OGRFeature::SetGeometryDirectly. + * + * @param hFeat handle to the feature on which to apply the geometry. + * @param hGeom handle to the new geometry to apply to feature. + * + * @return OGRERR_NONE if successful, or OGR_UNSUPPORTED_GEOMETRY_TYPE if + * the geometry type is illegal for the OGRFeatureDefn (checking not yet + * implemented). + */ + +OGRErr OGR_F_SetGeometryDirectly( OGRFeatureH hFeat, OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_SetGeometryDirectly", CE_Failure ); + + return ((OGRFeature *) hFeat)->SetGeometryDirectly((OGRGeometry *) hGeom); +} + +/************************************************************************/ +/* SetGeometry() */ +/************************************************************************/ + +/** + * \brief Set feature geometry. + * + * This method updates the features geometry, and operate exactly as + * SetGeometryDirectly(), except that this method does not assume ownership + * of the passed geometry, but instead makes a copy of it. + * + * This method is the same as the C function OGR_F_SetGeometry(). + * + * @param poGeomIn new geometry to apply to feature. Passing NULL value here + * is correct and it will result in deallocation of currently assigned geometry + * without assigning new one. + * + * @return OGRERR_NONE if successful, or OGR_UNSUPPORTED_GEOMETRY_TYPE if + * the geometry type is illegal for the OGRFeatureDefn (checking not yet + * implemented). + */ + +OGRErr OGRFeature::SetGeometry( OGRGeometry * poGeomIn ) + +{ + if( GetGeomFieldCount() > 0 ) + return SetGeomField(0, poGeomIn); + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* OGR_F_SetGeometry() */ +/************************************************************************/ + +/** + * \brief Set feature geometry. + * + * This function updates the features geometry, and operate exactly as + * SetGeometryDirectly(), except that this function does not assume ownership + * of the passed geometry, but instead makes a copy of it. + * + * This function is the same as the C++ OGRFeature::SetGeometry(). + * + * @param hFeat handle to the feature on which new geometry is applied to. + * @param hGeom handle to the new geometry to apply to feature. + * + * @return OGRERR_NONE if successful, or OGR_UNSUPPORTED_GEOMETRY_TYPE if + * the geometry type is illegal for the OGRFeatureDefn (checking not yet + * implemented). + */ + +OGRErr OGR_F_SetGeometry( OGRFeatureH hFeat, OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_SetGeometry", CE_Failure ); + + return ((OGRFeature *) hFeat)->SetGeometry((OGRGeometry *) hGeom); +} + +/************************************************************************/ +/* StealGeometry() */ +/************************************************************************/ + +/** + * \brief Take away ownership of geometry. + * + * Fetch the geometry from this feature, and clear the reference to the + * geometry on the feature. This is a mechanism for the application to + * take over ownship of the geometry from the feature without copying. + * Sort of an inverse to SetGeometryDirectly(). + * + * After this call the OGRFeature will have a NULL geometry. + * + * @return the pointer to the geometry. + */ + +OGRGeometry *OGRFeature::StealGeometry() + +{ + if( GetGeomFieldCount() > 0 ) + { + OGRGeometry *poReturn = papoGeometries[0]; + papoGeometries[0] = NULL; + return poReturn; + } + else + return NULL; +} + +OGRGeometry *OGRFeature::StealGeometry(int iGeomField) + +{ + if( iGeomField >= 0 && iGeomField < GetGeomFieldCount() ) + { + OGRGeometry *poReturn = papoGeometries[iGeomField]; + papoGeometries[iGeomField] = NULL; + return poReturn; + } + else + return NULL; +} + +/************************************************************************/ +/* OGR_F_StealGeometry() */ +/************************************************************************/ + +/** + * \brief Take away ownership of geometry. + * + * Fetch the geometry from this feature, and clear the reference to the + * geometry on the feature. This is a mechanism for the application to + * take over ownship of the geometry from the feature without copying. + * Sort of an inverse to OGR_FSetGeometryDirectly(). + * + * After this call the OGRFeature will have a NULL geometry. + * + * @return the pointer to the geometry. + */ + +OGRGeometryH OGR_F_StealGeometry( OGRFeatureH hFeat ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_StealGeometry", NULL ); + + return (OGRGeometryH) ((OGRFeature *) hFeat)->StealGeometry(); +} + +/************************************************************************/ +/* GetGeometryRef() */ +/************************************************************************/ + +/** + * \fn OGRGeometry *OGRFeature::GetGeometryRef(); + * + * \brief Fetch pointer to feature geometry. + * + * This method is the same as the C function OGR_F_GetGeometryRef(). + * + * Starting with GDAL 1.11, this is equivalent to calling + * OGRFeature::GetGeomFieldRef(0). + * + * @return pointer to internal feature geometry. This object should + * not be modified. + */ +OGRGeometry *OGRFeature::GetGeometryRef() + +{ + if( GetGeomFieldCount() > 0 ) + return GetGeomFieldRef(0); + else + return NULL; +} + +/************************************************************************/ +/* OGR_F_GetGeometryRef() */ +/************************************************************************/ + +/** + * \brief Fetch an handle to feature geometry. + * + * This function is the same as the C++ method OGRFeature::GetGeometryRef(). + * + * @param hFeat handle to the feature to get geometry from. + * @return an handle to internal feature geometry. This object should + * not be modified. + */ + +OGRGeometryH OGR_F_GetGeometryRef( OGRFeatureH hFeat ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetGeometryRef", NULL ); + + OGRFeature* poFeature = (OGRFeature *) hFeat; + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + + if( !OGRGetNonLinearGeometriesEnabledFlag() && poGeom != NULL && + OGR_GT_IsNonLinear(poGeom->getGeometryType()) ) + { + OGRwkbGeometryType eTargetType = OGR_GT_GetLinear(poGeom->getGeometryType()); + poGeom = OGRGeometryFactory::forceTo(poFeature->StealGeometry(), eTargetType); + poFeature->SetGeomFieldDirectly(0, poGeom); + } + + return (OGRGeometryH)poGeom; +} + + +/************************************************************************/ +/* GetGeomFieldRef() */ +/************************************************************************/ + +/** + * \brief Fetch pointer to feature geometry. + * + * This method is the same as the C function OGR_F_GetGeomFieldRef(). + * + * @param iField geometry field to get. + * + * @return pointer to internal feature geometry. This object should + * not be modified. + * + * @since GDAL 1.11 + */ +OGRGeometry *OGRFeature::GetGeomFieldRef(int iField) + +{ + if( iField < 0 || iField >= GetGeomFieldCount() ) + return NULL; + else + return papoGeometries[iField]; +} + +/************************************************************************/ +/* GetGeomFieldRef() */ +/************************************************************************/ + +/** + * \brief Fetch pointer to feature geometry. + * + * @param pszFName name of geometry field to get. + * + * @return pointer to internal feature geometry. This object should + * not be modified. + * + * @since GDAL 1.11 + */ +OGRGeometry *OGRFeature::GetGeomFieldRef(const char* pszFName) + +{ + int iField = GetGeomFieldIndex(pszFName); + if( iField < 0 ) + return NULL; + else + return papoGeometries[iField]; +} + +/************************************************************************/ +/* OGR_F_GetGeomFieldRef() */ +/************************************************************************/ + +/** + * \brief Fetch an handle to feature geometry. + * + * This function is the same as the C++ method OGRFeature::GetGeomFieldRef(). + * + * @param hFeat handle to the feature to get geometry from. + * @param iField geometry field to get. + * @return an handle to internal feature geometry. This object should + * not be modified. + * + * @since GDAL 1.11 + */ + +OGRGeometryH OGR_F_GetGeomFieldRef( OGRFeatureH hFeat, int iField ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetGeomFieldRef", NULL ); + + OGRFeature* poFeature = (OGRFeature *) hFeat; + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(iField); + + if( !OGRGetNonLinearGeometriesEnabledFlag() && poGeom != NULL && + OGR_GT_IsNonLinear(poGeom->getGeometryType()) ) + { + OGRwkbGeometryType eTargetType = OGR_GT_GetLinear(poGeom->getGeometryType()); + poGeom = OGRGeometryFactory::forceTo(poFeature->StealGeometry(iField), eTargetType); + poFeature->SetGeomFieldDirectly(iField, poGeom); + } + + return (OGRGeometryH)poGeom; +} + +/************************************************************************/ +/* SetGeomFieldDirectly() */ +/************************************************************************/ + +/** + * \brief Set feature geometry of a specified geometry field. + * + * This method updates the features geometry, and operate exactly as + * SetGeomField(), except that this method assumes ownership of the + * passed geometry (even in case of failure of that function). + * + * This method is the same as the C function OGR_F_SetGeomFieldDirectly(). + * + * @param iField geometry field to set. + * @param poGeomIn new geometry to apply to feature. Passing NULL value here + * is correct and it will result in deallocation of currently assigned geometry + * without assigning new one. + * + * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is invalid, + * or OGR_UNSUPPORTED_GEOMETRY_TYPE if the geometry type is illegal for the + * OGRFeatureDefn (checking not yet implemented). + * + * @since GDAL 1.11 + */ + +OGRErr OGRFeature::SetGeomFieldDirectly( int iField, OGRGeometry * poGeomIn ) + +{ + if( iField < 0 || iField >= GetGeomFieldCount() ) + { + delete poGeomIn; + return OGRERR_FAILURE; + } + + delete papoGeometries[iField]; + papoGeometries[iField] = poGeomIn; + + // I should be verifying that the geometry matches the defn's type. + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_F_SetGeomFieldDirectly() */ +/************************************************************************/ + +/** + * \brief Set feature geometry of a specified geometry field. + * + * This function updates the features geometry, and operate exactly as + * SetGeomField(), except that this function assumes ownership of the + * passed geometry (even in case of failure of that function). + * + * This function is the same as the C++ method + * OGRFeature::SetGeomFieldDirectly. + * + * @param hFeat handle to the feature on which to apply the geometry. + * @param iField geometry field to set. + * @param hGeom handle to the new geometry to apply to feature. + * + * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is invalid, + * or OGR_UNSUPPORTED_GEOMETRY_TYPE if the geometry type is illegal for the + * OGRFeatureDefn (checking not yet implemented). + * + * @since GDAL 1.11 + */ + +OGRErr OGR_F_SetGeomFieldDirectly( OGRFeatureH hFeat, int iField, + OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_SetGeomFieldDirectly", CE_Failure ); + + return ((OGRFeature *) hFeat)->SetGeomFieldDirectly(iField, + (OGRGeometry *) hGeom); +} + +/************************************************************************/ +/* SetGeomField() */ +/************************************************************************/ + +/** + * \brief Set feature geometry of a specified geometry field. + * + * This method updates the features geometry, and operate exactly as + * SetGeomFieldDirectly(), except that this method does not assume ownership + * of the passed geometry, but instead makes a copy of it. + * + * This method is the same as the C function OGR_F_SetGeomField(). + * + * @param iField geometry field to set. + * @param poGeomIn new geometry to apply to feature. Passing NULL value here + * is correct and it will result in deallocation of currently assigned geometry + * without assigning new one. + * + * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is invalid, + * or OGR_UNSUPPORTED_GEOMETRY_TYPE if the geometry type is illegal for the + * OGRFeatureDefn (checking not yet implemented). + * + * @since GDAL 1.11 + */ + +OGRErr OGRFeature::SetGeomField( int iField, OGRGeometry * poGeomIn ) + +{ + if( iField < 0 || iField >= GetGeomFieldCount() ) + return OGRERR_FAILURE; + + delete papoGeometries[iField]; + + if( poGeomIn != NULL ) + papoGeometries[iField] = poGeomIn->clone(); + else + papoGeometries[iField] = NULL; + + // I should be verifying that the geometry matches the defn's type. + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_F_SetGeomField() */ +/************************************************************************/ + +/** + * \brief Set feature geometry of a specified geometry field. + * + * This function updates the features geometry, and operate exactly as + * SetGeometryDirectly(), except that this function does not assume ownership + * of the passed geometry, but instead makes a copy of it. + * + * This function is the same as the C++ OGRFeature::SetGeomField(). + * + * @param hFeat handle to the feature on which new geometry is applied to. + * @param iField geometry field to set. + * @param hGeom handle to the new geometry to apply to feature. + * + * @return OGRERR_NONE if successful, or OGR_UNSUPPORTED_GEOMETRY_TYPE if + * the geometry type is illegal for the OGRFeatureDefn (checking not yet + * implemented). + */ + +OGRErr OGR_F_SetGeomField( OGRFeatureH hFeat, int iField, OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_SetGeomField", CE_Failure ); + + return ((OGRFeature *) hFeat)->SetGeomField(iField, (OGRGeometry *) hGeom); +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +/** + * \brief Duplicate feature. + * + * The newly created feature is owned by the caller, and will have it's own + * reference to the OGRFeatureDefn. + * + * This method is the same as the C function OGR_F_Clone(). + * + * @return new feature, exactly matching this feature. + */ + +OGRFeature *OGRFeature::Clone() + +{ + int i; + OGRFeature *poNew = new OGRFeature( poDefn ); + + for( i = 0; i < poDefn->GetFieldCount(); i++ ) + { + poNew->SetField( i, pauFields + i ); + } + for( i = 0; i < poDefn->GetGeomFieldCount(); i++ ) + { + poNew->SetGeomField( i, papoGeometries[i] ); + } + + if( GetStyleString() != NULL ) + poNew->SetStyleString(GetStyleString()); + + poNew->SetFID( GetFID() ); + + return poNew; +} + +/************************************************************************/ +/* OGR_F_Clone() */ +/************************************************************************/ + +/** + * \brief Duplicate feature. + * + * The newly created feature is owned by the caller, and will have it's own + * reference to the OGRFeatureDefn. + * + * This function is the same as the C++ method OGRFeature::Clone(). + * + * @param hFeat handle to the feature to clone. + * @return an handle to the new feature, exactly matching this feature. + */ + +OGRFeatureH OGR_F_Clone( OGRFeatureH hFeat ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_Clone", NULL ); + + return (OGRFeatureH) ((OGRFeature *) hFeat)->Clone(); +} + +/************************************************************************/ +/* GetFieldCount() */ +/************************************************************************/ + +/** + * \fn int OGRFeature::GetFieldCount(); + * + * \brief Fetch number of fields on this feature. + * This will always be the same + * as the field count for the OGRFeatureDefn. + * + * This method is the same as the C function OGR_F_GetFieldCount(). + * + * @return count of fields. + */ + +/************************************************************************/ +/* OGR_F_GetFieldCount() */ +/************************************************************************/ + +/** + * \brief Fetch number of fields on this feature + * This will always be the same + * as the field count for the OGRFeatureDefn. + * + * This function is the same as the C++ method OGRFeature::GetFieldCount(). + * + * @param hFeat handle to the feature to get the fields count from. + * @return count of fields. + */ + +int OGR_F_GetFieldCount( OGRFeatureH hFeat ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldCount", 0 ); + + return ((OGRFeature *) hFeat)->GetFieldCount(); +} + +/************************************************************************/ +/* GetFieldDefnRef() */ +/************************************************************************/ + +/** + * \fn OGRFieldDefn *OGRFeature::GetFieldDefnRef( int iField ); + * + * \brief Fetch definition for this field. + * + * This method is the same as the C function OGR_F_GetFieldDefnRef(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the field definition (from the OGRFeatureDefn). This is an + * internal reference, and should not be deleted or modified. + */ + +/************************************************************************/ +/* OGR_F_GetFieldDefnRef() */ +/************************************************************************/ + +/** + * \brief Fetch definition for this field. + * + * This function is the same as the C++ method OGRFeature::GetFieldDefnRef(). + * + * @param hFeat handle to the feature on which the field is found. + * @param i the field to fetch, from 0 to GetFieldCount()-1. + * + * @return an handle to the field definition (from the OGRFeatureDefn). + * This is an internal reference, and should not be deleted or modified. + */ + +OGRFieldDefnH OGR_F_GetFieldDefnRef( OGRFeatureH hFeat, int i ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldDefnRef", NULL ); + + if( i < 0 || i >= ((OGRFeature *) hFeat)->GetFieldCount() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid index : %d", i); + return NULL; + } + + return (OGRFieldDefnH) ((OGRFeature *) hFeat)->GetFieldDefnRef(i); +} + +/************************************************************************/ +/* GetFieldIndex() */ +/************************************************************************/ + +/** + * \fn int OGRFeature::GetFieldIndex( const char * pszName ); + * + * \brief Fetch the field index given field name. + * + * This is a cover for the OGRFeatureDefn::GetFieldIndex() method. + * + * This method is the same as the C function OGR_F_GetFieldIndex(). + * + * @param pszName the name of the field to search for. + * + * @return the field index, or -1 if no matching field is found. + */ + +/************************************************************************/ +/* OGR_F_GetFieldIndex() */ +/************************************************************************/ + +/** + * \brief Fetch the field index given field name. + * + * This is a cover for the OGRFeatureDefn::GetFieldIndex() method. + * + * This function is the same as the C++ method OGRFeature::GetFieldIndex(). + * + * @param hFeat handle to the feature on which the field is found. + * @param pszName the name of the field to search for. + * + * @return the field index, or -1 if no matching field is found. + */ + +int OGR_F_GetFieldIndex( OGRFeatureH hFeat, const char *pszName ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldIndex", 0 ); + + return ((OGRFeature *) hFeat)->GetFieldIndex( pszName ); +} + + +/************************************************************************/ +/* GetGeomFieldCount() */ +/************************************************************************/ + +/** + * \fn int OGRFeature::GetGeomFieldCount(); + * + * \brief Fetch number of geometry fields on this feature. + * This will always be the same + * as the geometry field count for the OGRFeatureDefn. + * + * This method is the same as the C function OGR_F_GetGeomFieldCount(). + * + * @return count of geometry fields. + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_F_GetGeomFieldCount() */ +/************************************************************************/ + +/** + * \brief Fetch number of geometry fields on this feature + * This will always be the same + * as the geometry field count for the OGRFeatureDefn. + * + * This function is the same as the C++ method OGRFeature::GetGeomFieldCount(). + * + * @param hFeat handle to the feature to get the geometry fields count from. + * @return count of geometry fields. + * + * @since GDAL 1.11 + */ + +int OGR_F_GetGeomFieldCount( OGRFeatureH hFeat ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetGeomFieldCount", 0 ); + + return ((OGRFeature *) hFeat)->GetGeomFieldCount(); +} + +/************************************************************************/ +/* GetGeomFieldDefnRef() */ +/************************************************************************/ + +/** + * \fn OGRGeomFieldDefn *OGRFeature::GetGeomFieldDefnRef( int iGeomField ); + * + * \brief Fetch definition for this geometry field. + * + * This method is the same as the C function OGR_F_GetGeomFieldDefnRef(). + * + * @param iGeomField the field to fetch, from 0 to GetGeomFieldCount()-1. + * + * @return the field definition (from the OGRFeatureDefn). This is an + * internal reference, and should not be deleted or modified. + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_F_GetGeomFieldDefnRef() */ +/************************************************************************/ + +/** + * \brief Fetch definition for this geometry field. + * + * This function is the same as the C++ method OGRFeature::GetGeomFieldDefnRef(). + * + * @param hFeat handle to the feature on which the field is found. + * @param i the field to fetch, from 0 to GetGeomFieldCount()-1. + * + * @return an handle to the field definition (from the OGRFeatureDefn). + * This is an internal reference, and should not be deleted or modified. + * + * @since GDAL 1.11 + */ + +OGRGeomFieldDefnH OGR_F_GetGeomFieldDefnRef( OGRFeatureH hFeat, int i ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetGeomFieldDefnRef", NULL ); + + return (OGRGeomFieldDefnH) ((OGRFeature *) hFeat)->GetGeomFieldDefnRef(i); +} + +/************************************************************************/ +/* GetGeomFieldIndex() */ +/************************************************************************/ + +/** + * \fn int OGRFeature::GetGeomFieldIndex( const char * pszName ); + * + * \brief Fetch the geometry field index given geometry field name. + * + * This is a cover for the OGRFeatureDefn::GetGeomFieldIndex() method. + * + * This method is the same as the C function OGR_F_GetGeomFieldIndex(). + * + * @param pszName the name of the geometry field to search for. + * + * @return the geometry field index, or -1 if no matching geometry field is found. + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_F_GetGeomFieldIndex() */ +/************************************************************************/ + +/** + * \brief Fetch the geometry field index given geometry field name. + * + * This is a cover for the OGRFeatureDefn::GetGeomFieldIndex() method. + * + * This function is the same as the C++ method OGRFeature::GetGeomFieldIndex(). + * + * @param hFeat handle to the feature on which the geometry field is found. + * @param pszName the name of the geometry field to search for. + * + * @return the geometry field index, or -1 if no matching geometry field is found. + * + * @since GDAL 1.11 + */ + +int OGR_F_GetGeomFieldIndex( OGRFeatureH hFeat, const char *pszName ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetGeomFieldIndex", 0 ); + + return ((OGRFeature *) hFeat)->GetGeomFieldIndex( pszName ); +} + + +/************************************************************************/ +/* IsFieldSet() */ +/************************************************************************/ + +/** + * \fn int OGRFeature::IsFieldSet( int iField ) const; + * + * \brief Test if a field has ever been assigned a value or not. + * + * This method is the same as the C function OGR_F_IsFieldSet(). + * + * @param iField the field to test. + * + * @return TRUE if the field has been set, otherwise false. + */ + +int OGRFeature::IsFieldSet( int iField ) + +{ + int iSpecialField = iField - poDefn->GetFieldCount(); + if (iSpecialField >= 0) + { + // special field value accessors + switch (iSpecialField) + { + case SPF_FID: + return ((OGRFeature *)this)->GetFID() != OGRNullFID; + + case SPF_OGR_GEOM_WKT: + case SPF_OGR_GEOMETRY: + return GetGeomFieldCount() > 0 && papoGeometries[0] != NULL; + + case SPF_OGR_STYLE: + return ((OGRFeature *)this)->GetStyleString() != NULL; + + case SPF_OGR_GEOM_AREA: + if( GetGeomFieldCount() == 0 || papoGeometries[0] == NULL ) + return FALSE; + + return OGR_G_Area((OGRGeometryH)papoGeometries[0]) != 0.0; + + default: + return FALSE; + } + } + else + { + return pauFields[iField].Set.nMarker1 != OGRUnsetMarker + || pauFields[iField].Set.nMarker2 != OGRUnsetMarker; + } +} + +/************************************************************************/ +/* OGR_F_IsFieldSet() */ +/************************************************************************/ + +/** + * \brief Test if a field has ever been assigned a value or not. + * + * This function is the same as the C++ method OGRFeature::IsFieldSet(). + * + * @param hFeat handle to the feature on which the field is. + * @param iField the field to test. + * + * @return TRUE if the field has been set, otherwise false. + */ + +int OGR_F_IsFieldSet( OGRFeatureH hFeat, int iField ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_IsFieldSet", 0 ); + + OGRFeature* poFeature = (OGRFeature* )hFeat; + + if (iField < 0 || iField >= poFeature->GetFieldCount()) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid index : %d", iField); + return FALSE; + } + + return poFeature->IsFieldSet( iField ); +} + +/************************************************************************/ +/* UnsetField() */ +/************************************************************************/ + +/** + * \brief Clear a field, marking it as unset. + * + * This method is the same as the C function OGR_F_UnsetField(). + * + * @param iField the field to unset. + */ + +void OGRFeature::UnsetField( int iField ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL || !IsFieldSet(iField) ) + return; + + switch( poFDefn->GetType() ) + { + case OFTRealList: + case OFTIntegerList: + case OFTInteger64List: + CPLFree( pauFields[iField].IntegerList.paList ); + break; + + case OFTStringList: + CSLDestroy( pauFields[iField].StringList.paList ); + break; + + case OFTString: + CPLFree( pauFields[iField].String ); + break; + + case OFTBinary: + CPLFree( pauFields[iField].Binary.paData ); + break; + + default: + break; + } + + pauFields[iField].Set.nMarker1 = OGRUnsetMarker; + pauFields[iField].Set.nMarker2 = OGRUnsetMarker; +} + +/************************************************************************/ +/* OGR_F_UnsetField() */ +/************************************************************************/ + +/** + * \brief Clear a field, marking it as unset. + * + * This function is the same as the C++ method OGRFeature::UnsetField(). + * + * @param hFeat handle to the feature on which the field is. + * @param iField the field to unset. + */ + +void OGR_F_UnsetField( OGRFeatureH hFeat, int iField ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_UnsetField" ); + + ((OGRFeature *) hFeat)->UnsetField( iField ); +} + +/************************************************************************/ +/* GetRawFieldRef() */ +/************************************************************************/ + +/** + * \fn OGRField *OGRFeature::GetRawFieldRef( int iField ); + * + * \brief Fetch a pointer to the internal field value given the index. + * + * This method is the same as the C function OGR_F_GetRawFieldRef(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the returned pointer is to an internal data structure, and should + * not be freed, or modified. + */ + +/************************************************************************/ +/* OGR_F_GetRawFieldRef() */ +/************************************************************************/ + +/** + * \brief Fetch an handle to the internal field value given the index. + * + * This function is the same as the C++ method OGRFeature::GetRawFieldRef(). + * + * @param hFeat handle to the feature on which field is found. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the returned handle is to an internal data structure, and should + * not be freed, or modified. + */ + +OGRField *OGR_F_GetRawFieldRef( OGRFeatureH hFeat, int iField ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetRawFieldRef", NULL ); + + return ((OGRFeature *)hFeat)->GetRawFieldRef( iField ); +} + +/************************************************************************/ +/* GetFieldAsInteger() */ +/************************************************************************/ + +/** + * \brief Fetch field value as integer. + * + * OFTString features will be translated using atoi(). OFTReal fields + * will be cast to integer. OFTInteger64 are demoted to 32 bit, with + * clamping if out-of-range. Other field types, or errors will result in + * a return value of zero. + * + * This method is the same as the C function OGR_F_GetFieldAsInteger(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the field value. + */ + +int OGRFeature::GetFieldAsInteger( int iField ) + +{ + int iSpecialField = iField - poDefn->GetFieldCount(); + if (iSpecialField >= 0) + { + // special field value accessors + switch (iSpecialField) + { + case SPF_FID: + { + int nVal = (nFID > INT_MAX) ? INT_MAX : (nFID < INT_MIN) ? INT_MIN : (int) nFID; + if( (GIntBig)nVal != nFID ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Integer overflow occured when trying to return 64bit integer. " + "Use GetFieldAsInteger64() instead"); + } + return nVal; + } + + case SPF_OGR_GEOM_AREA: + if( GetGeomFieldCount() == 0 || papoGeometries[0] == NULL ) + return 0; + return (int)OGR_G_Area((OGRGeometryH)papoGeometries[0]); + + default: + return 0; + } + } + + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return 0; + + if( !IsFieldSet(iField) ) + return 0; + + OGRFieldType eType = poFDefn->GetType(); + if( eType == OFTInteger ) + return pauFields[iField].Integer; + else if( eType == OFTInteger64 ) + { + GIntBig nVal64 = pauFields[iField].Integer64; + int nVal = (nVal64 > INT_MAX) ? INT_MAX : (nVal64 < INT_MIN) ? INT_MIN : (int) nVal64; + if( (GIntBig)nVal != nVal64 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Integer overflow occured when trying to return 64bit integer. " + "Use GetFieldAsInteger64() instead"); + } + return nVal; + } + else if( eType == OFTReal ) + return (int) pauFields[iField].Real; + else if( eType == OFTString ) + { + if( pauFields[iField].String == NULL ) + return 0; + else + return atoi(pauFields[iField].String); + } + else + return 0; +} + +/************************************************************************/ +/* OGR_F_GetFieldAsInteger() */ +/************************************************************************/ + +/** + * \brief Fetch field value as integer. + * + * OFTString features will be translated using atoi(). OFTReal fields + * will be cast to integer. Other field types, or errors will result in + * a return value of zero. + * + * This function is the same as the C++ method OGRFeature::GetFieldAsInteger(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the field value. + */ + +int OGR_F_GetFieldAsInteger( OGRFeatureH hFeat, int iField ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldAsInteger", 0 ); + + return ((OGRFeature *)hFeat)->GetFieldAsInteger(iField); +} + +/************************************************************************/ +/* GetFieldAsInteger64() */ +/************************************************************************/ + +/** + * \brief Fetch field value as integer 64 bit. + * + * OFTInteger are promoted to 64 bit. + * OFTString features will be translated using CPLAtoGIntBig(). OFTReal fields + * will be cast to integer. Other field types, or errors will result in + * a return value of zero. + * + * This method is the same as the C function OGR_F_GetFieldAsInteger64(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the field value. + * @since GDAL 2.0 + */ + +GIntBig OGRFeature::GetFieldAsInteger64( int iField ) + +{ + int iSpecialField = iField - poDefn->GetFieldCount(); + if (iSpecialField >= 0) + { + // special field value accessors + switch (iSpecialField) + { + case SPF_FID: + return nFID; + + case SPF_OGR_GEOM_AREA: + if( GetGeomFieldCount() == 0 || papoGeometries[0] == NULL ) + return 0; + return (int)OGR_G_Area((OGRGeometryH)papoGeometries[0]); + + default: + return 0; + } + } + + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return 0; + + if( !IsFieldSet(iField) ) + return 0; + + OGRFieldType eType = poFDefn->GetType(); + if( eType == OFTInteger ) + return (GIntBig) pauFields[iField].Integer; + else if( eType == OFTInteger64 ) + return pauFields[iField].Integer64; + else if( eType == OFTReal ) + return (GIntBig) pauFields[iField].Real; + else if( eType == OFTString ) + { + if( pauFields[iField].String == NULL ) + return 0; + else + { + return CPLAtoGIntBigEx(pauFields[iField].String, TRUE, NULL); + } + } + else + return 0; +} + +/************************************************************************/ +/* OGR_F_GetFieldAsInteger64() */ +/************************************************************************/ + +/** + * \brief Fetch field value as integer 64 bit. + * + * OFTInteger are promoted to 64 bit. + * OFTString features will be translated using CPLAtoGIntBig(). OFTReal fields + * will be cast to integer. Other field types, or errors will result in + * a return value of zero. + * + * This function is the same as the C++ method OGRFeature::GetFieldAsInteger64(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the field value. + * @since GDAL 2.0 + */ + +GIntBig OGR_F_GetFieldAsInteger64( OGRFeatureH hFeat, int iField ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldAsInteger64", 0 ); + + return ((OGRFeature *)hFeat)->GetFieldAsInteger64(iField); +} + +/************************************************************************/ +/* GetFieldAsDouble() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a double. + * + * OFTString features will be translated using CPLAtof(). OFTInteger and OFTInteger64 fields + * will be cast to double. Other field types, or errors will result in + * a return value of zero. + * + * This method is the same as the C function OGR_F_GetFieldAsDouble(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the field value. + */ + +double OGRFeature::GetFieldAsDouble( int iField ) + +{ + int iSpecialField = iField - poDefn->GetFieldCount(); + if (iSpecialField >= 0) + { + // special field value accessors + switch (iSpecialField) + { + case SPF_FID: + return (double)GetFID(); + + case SPF_OGR_GEOM_AREA: + if( GetGeomFieldCount() == 0 || papoGeometries[0] == NULL ) + return 0.0; + return OGR_G_Area((OGRGeometryH)papoGeometries[0]); + + default: + return 0.0; + } + } + + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return 0.0; + + if( !IsFieldSet(iField) ) + return 0.0; + + OGRFieldType eType = poFDefn->GetType(); + if( eType == OFTReal ) + return pauFields[iField].Real; + else if( eType == OFTInteger ) + return pauFields[iField].Integer; + else if( eType == OFTInteger64 ) + return (double) pauFields[iField].Integer64; + else if( eType == OFTString ) + { + if( pauFields[iField].String == NULL ) + return 0; + else + return CPLAtof(pauFields[iField].String); + } + else + return 0.0; +} + +/************************************************************************/ +/* OGR_F_GetFieldAsDouble() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a double. + * + * OFTString features will be translated using CPLAtof(). OFTInteger fields + * will be cast to double. Other field types, or errors will result in + * a return value of zero. + * + * This function is the same as the C++ method OGRFeature::GetFieldAsDouble(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the field value. + */ + +double OGR_F_GetFieldAsDouble( OGRFeatureH hFeat, int iField ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldAsDouble", 0 ); + + return ((OGRFeature *)hFeat)->GetFieldAsDouble(iField); +} + +/************************************************************************/ +/* GetFieldAsString() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a string. + * + * OFTReal and OFTInteger fields will be translated to string using + * sprintf(), but not necessarily using the established formatting rules. + * Other field types, or errors will result in a return value of zero. + * + * This method is the same as the C function OGR_F_GetFieldAsString(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the field value. This string is internal, and should not be + * modified, or freed. Its lifetime may be very brief. + */ + +const char *OGRFeature::GetFieldAsString( int iField ) + +{ +#define TEMP_BUFFER_SIZE 80 + char szTempBuffer[TEMP_BUFFER_SIZE]; + + CPLFree(m_pszTmpFieldValue); + m_pszTmpFieldValue = NULL; + + int iSpecialField = iField - poDefn->GetFieldCount(); + if (iSpecialField >= 0) + { + // special field value accessors + switch (iSpecialField) + { + case SPF_FID: + snprintf( szTempBuffer, TEMP_BUFFER_SIZE, CPL_FRMT_GIB, GetFID() ); + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + + case SPF_OGR_GEOMETRY: + if( GetGeomFieldCount() > 0 && papoGeometries[0] != NULL ) + return papoGeometries[0]->getGeometryName(); + else + return ""; + + case SPF_OGR_STYLE: + if( GetStyleString() == NULL ) + return ""; + else + return GetStyleString(); + + case SPF_OGR_GEOM_WKT: + { + if( GetGeomFieldCount() == 0 || papoGeometries[0] == NULL ) + return ""; + + if (papoGeometries[0]->exportToWkt( &m_pszTmpFieldValue ) == OGRERR_NONE ) + return m_pszTmpFieldValue; + else + return ""; + } + + case SPF_OGR_GEOM_AREA: + if( GetGeomFieldCount() == 0 || papoGeometries[0] == NULL ) + return ""; + + CPLsnprintf( szTempBuffer, TEMP_BUFFER_SIZE, "%.16g", + OGR_G_Area((OGRGeometryH)papoGeometries[0]) ); + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + + default: + return ""; + } + } + + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return ""; + + if( !IsFieldSet(iField) ) + return ""; + + OGRFieldType eType = poFDefn->GetType(); + if( eType == OFTString ) + { + if( pauFields[iField].String == NULL ) + return ""; + else + return pauFields[iField].String; + } + else if( eType == OFTInteger ) + { + snprintf( szTempBuffer, TEMP_BUFFER_SIZE, + "%d", pauFields[iField].Integer ); + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + } + else if( eType == OFTInteger64 ) + { + snprintf( szTempBuffer, TEMP_BUFFER_SIZE, + CPL_FRMT_GIB, pauFields[iField].Integer64 ); + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + } + else if( eType == OFTReal ) + { + char szFormat[64]; + + if( poFDefn->GetWidth() != 0 ) + { + snprintf( szFormat, sizeof(szFormat), "%%.%df", + poFDefn->GetPrecision() ); + } + else + strcpy( szFormat, "%.15g" ); + + CPLsnprintf( szTempBuffer, TEMP_BUFFER_SIZE, + szFormat, pauFields[iField].Real ); + + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + } + else if( eType == OFTDateTime ) + { + int ms = OGR_GET_MS(pauFields[iField].Date.Second); + if( ms != 0 ) + snprintf( szTempBuffer, TEMP_BUFFER_SIZE, + "%04d/%02d/%02d %02d:%02d:%06.3f", + pauFields[iField].Date.Year, + pauFields[iField].Date.Month, + pauFields[iField].Date.Day, + pauFields[iField].Date.Hour, + pauFields[iField].Date.Minute, + pauFields[iField].Date.Second ); + else /* default format */ + snprintf( szTempBuffer, TEMP_BUFFER_SIZE, + "%04d/%02d/%02d %02d:%02d:%02d", + pauFields[iField].Date.Year, + pauFields[iField].Date.Month, + pauFields[iField].Date.Day, + pauFields[iField].Date.Hour, + pauFields[iField].Date.Minute, + (int)pauFields[iField].Date.Second ); + + + if( pauFields[iField].Date.TZFlag > 1 ) + { + int nOffset = (pauFields[iField].Date.TZFlag - 100) * 15; + int nHours = (int) (nOffset / 60); // round towards zero + int nMinutes = ABS(nOffset - nHours * 60); + + if( nOffset < 0 ) + { + strcat( szTempBuffer, "-" ); + nHours = ABS(nHours); + } + else + strcat( szTempBuffer, "+" ); + + if( nMinutes == 0 ) + snprintf( szTempBuffer+strlen(szTempBuffer), + TEMP_BUFFER_SIZE-strlen(szTempBuffer), "%02d", nHours ); + else + snprintf( szTempBuffer+strlen(szTempBuffer), + TEMP_BUFFER_SIZE-strlen(szTempBuffer), "%02d%02d", nHours, nMinutes ); + } + + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + } + else if( eType == OFTDate ) + { + snprintf( szTempBuffer, TEMP_BUFFER_SIZE, "%04d/%02d/%02d", + pauFields[iField].Date.Year, + pauFields[iField].Date.Month, + pauFields[iField].Date.Day ); + + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + } + else if( eType == OFTTime ) + { + int ms = OGR_GET_MS(pauFields[iField].Date.Second); + if( ms != 0 ) + snprintf( szTempBuffer, TEMP_BUFFER_SIZE, "%02d:%02d:%06.3f", + pauFields[iField].Date.Hour, + pauFields[iField].Date.Minute, + pauFields[iField].Date.Second ); + else + snprintf( szTempBuffer, TEMP_BUFFER_SIZE, "%02d:%02d:%02d", + pauFields[iField].Date.Hour, + pauFields[iField].Date.Minute, + (int)pauFields[iField].Date.Second ); + + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + } + else if( eType == OFTIntegerList ) + { + char szItem[32]; + int i, nCount = pauFields[iField].IntegerList.nCount; + + snprintf( szTempBuffer, TEMP_BUFFER_SIZE, "(%d:", nCount ); + for( i = 0; i < nCount; i++ ) + { + snprintf( szItem, sizeof(szItem), "%d", + pauFields[iField].IntegerList.paList[i] ); + if( strlen(szTempBuffer) + strlen(szItem) + 6 + >= sizeof(szTempBuffer) ) + { + break; + } + + if( i > 0 ) + strcat( szTempBuffer, "," ); + + strcat( szTempBuffer, szItem ); + } + + if( i < nCount ) + strcat( szTempBuffer, ",...)" ); + else + strcat( szTempBuffer, ")" ); + + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + } + else if( eType == OFTInteger64List ) + { + char szItem[32]; + int i, nCount = pauFields[iField].Integer64List.nCount; + + snprintf( szTempBuffer, TEMP_BUFFER_SIZE, "(%d:", nCount ); + for( i = 0; i < nCount; i++ ) + { + snprintf( szItem, sizeof(szItem), CPL_FRMT_GIB, + pauFields[iField].Integer64List.paList[i] ); + if( strlen(szTempBuffer) + strlen(szItem) + 6 + >= sizeof(szTempBuffer) ) + { + break; + } + + if( i > 0 ) + strcat( szTempBuffer, "," ); + + strcat( szTempBuffer, szItem ); + } + + if( i < nCount ) + strcat( szTempBuffer, ",...)" ); + else + strcat( szTempBuffer, ")" ); + + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + } + else if( eType == OFTRealList ) + { + char szItem[40]; + char szFormat[64]; + int i, nCount = pauFields[iField].RealList.nCount; + + if( poFDefn->GetWidth() != 0 ) + { + snprintf( szFormat, sizeof(szFormat), "%%%d.%df", + poFDefn->GetWidth(), poFDefn->GetPrecision() ); + } + else + strcpy( szFormat, "%.16g" ); + + snprintf( szTempBuffer, TEMP_BUFFER_SIZE, "(%d:", nCount ); + for( i = 0; i < nCount; i++ ) + { + CPLsnprintf( szItem, sizeof(szItem), szFormat, + pauFields[iField].RealList.paList[i] ); + if( strlen(szTempBuffer) + strlen(szItem) + 6 + >= sizeof(szTempBuffer) ) + { + break; + } + + if( i > 0 ) + strcat( szTempBuffer, "," ); + + strcat( szTempBuffer, szItem ); + } + + if( i < nCount ) + strcat( szTempBuffer, ",...)" ); + else + strcat( szTempBuffer, ")" ); + + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + } + else if( eType == OFTStringList ) + { + int i, nCount = pauFields[iField].StringList.nCount; + + snprintf( szTempBuffer, TEMP_BUFFER_SIZE, "(%d:", nCount ); + for( i = 0; i < nCount; i++ ) + { + const char *pszItem = pauFields[iField].StringList.paList[i]; + + if( strlen(szTempBuffer) + strlen(pszItem) + 6 + >= sizeof(szTempBuffer) ) + { + break; + } + + if( i > 0 ) + strcat( szTempBuffer, "," ); + + strcat( szTempBuffer, pszItem ); + } + + if( i < nCount ) + strcat( szTempBuffer, ",...)" ); + else + strcat( szTempBuffer, ")" ); + + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + } + else if( eType == OFTBinary ) + { + int nCount = pauFields[iField].Binary.nCount; + char *pszHex; + + if( nCount > (int) sizeof(szTempBuffer) / 2 - 4 ) + nCount = sizeof(szTempBuffer) / 2 - 4; + + pszHex = CPLBinaryToHex( nCount, pauFields[iField].Binary.paData ); + + memcpy( szTempBuffer, pszHex, 2 * nCount ); + szTempBuffer[nCount*2] = '\0'; + if( nCount < pauFields[iField].Binary.nCount ) + strcat( szTempBuffer, "..." ); + + CPLFree( pszHex ); + + return m_pszTmpFieldValue = CPLStrdup( szTempBuffer ); + } + else + return ""; +#undef TEMP_BUFFER_SIZE +} + +/************************************************************************/ +/* OGR_F_GetFieldAsString() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a string. + * + * OFTReal and OFTInteger fields will be translated to string using + * sprintf(), but not necessarily using the established formatting rules. + * Other field types, or errors will result in a return value of zero. + * + * This function is the same as the C++ method OGRFeature::GetFieldAsString(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the field value. This string is internal, and should not be + * modified, or freed. Its lifetime may be very brief. + */ + +const char *OGR_F_GetFieldAsString( OGRFeatureH hFeat, int iField ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldAsString", NULL ); + + return ((OGRFeature *)hFeat)->GetFieldAsString(iField); +} + +/************************************************************************/ +/* GetFieldAsIntegerList() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a list of integers. + * + * Currently this method only works for OFTIntegerList fields. + * + * This method is the same as the C function OGR_F_GetFieldAsIntegerList(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pnCount an integer to put the list count (number of integers) into. + * + * @return the field value. This list is internal, and should not be + * modified, or freed. Its lifetime may be very brief. If *pnCount is zero + * on return the returned pointer may be NULL or non-NULL. + */ + +const int *OGRFeature::GetFieldAsIntegerList( int iField, int *pnCount ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn != NULL && IsFieldSet(iField) && + poFDefn->GetType() == OFTIntegerList ) + { + if( pnCount != NULL ) + *pnCount = pauFields[iField].IntegerList.nCount; + + return pauFields[iField].IntegerList.paList; + } + else + { + if( pnCount != NULL ) + *pnCount = 0; + + return NULL; + } +} + +/************************************************************************/ +/* OGR_F_GetFieldAsIntegerList() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a list of integers. + * + * Currently this function only works for OFTIntegerList fields. + * + * This function is the same as the C++ method + * OGRFeature::GetFieldAsIntegerList(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pnCount an integer to put the list count (number of integers) into. + * + * @return the field value. This list is internal, and should not be + * modified, or freed. Its lifetime may be very brief. If *pnCount is zero + * on return the returned pointer may be NULL or non-NULL. + */ + +const int *OGR_F_GetFieldAsIntegerList( OGRFeatureH hFeat, int iField, + int *pnCount ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldAsIntegerList", NULL ); + + return ((OGRFeature *)hFeat)->GetFieldAsIntegerList(iField, pnCount); +} + +/************************************************************************/ +/* GetFieldAsInteger64List() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a list of 64 bit integers. + * + * Currently this method only works for OFTInteger64List fields. + * + * This method is the same as the C function OGR_F_GetFieldAsInteger64List(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pnCount an integer to put the list count (number of integers) into. + * + * @return the field value. This list is internal, and should not be + * modified, or freed. Its lifetime may be very brief. If *pnCount is zero + * on return the returned pointer may be NULL or non-NULL. + * @since GDAL 2.0 + */ + +const GIntBig *OGRFeature::GetFieldAsInteger64List( int iField, int *pnCount ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn != NULL && IsFieldSet(iField) && + poFDefn->GetType() == OFTInteger64List ) + { + if( pnCount != NULL ) + *pnCount = pauFields[iField].Integer64List.nCount; + + return pauFields[iField].Integer64List.paList; + } + else + { + if( pnCount != NULL ) + *pnCount = 0; + + return NULL; + } +} + +/************************************************************************/ +/* OGR_F_GetFieldAsInteger64List() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a list of 64 bit integers. + * + * Currently this function only works for OFTInteger64List fields. + * + * This function is the same as the C++ method + * OGRFeature::GetFieldAsInteger64List(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pnCount an integer to put the list count (number of integers) into. + * + * @return the field value. This list is internal, and should not be + * modified, or freed. Its lifetime may be very brief. If *pnCount is zero + * on return the returned pointer may be NULL or non-NULL. + * @since GDAL 2.0 + */ + +const GIntBig *OGR_F_GetFieldAsInteger64List( OGRFeatureH hFeat, int iField, + int *pnCount ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldAsInteger64List", NULL ); + + return ((OGRFeature *)hFeat)->GetFieldAsInteger64List(iField, pnCount); +} + +/************************************************************************/ +/* GetFieldAsDoubleList() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a list of doubles. + * + * Currently this method only works for OFTRealList fields. + * + * This method is the same as the C function OGR_F_GetFieldAsDoubleList(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pnCount an integer to put the list count (number of doubles) into. + * + * @return the field value. This list is internal, and should not be + * modified, or freed. Its lifetime may be very brief. If *pnCount is zero + * on return the returned pointer may be NULL or non-NULL. + */ + +const double *OGRFeature::GetFieldAsDoubleList( int iField, int *pnCount ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn != NULL && IsFieldSet(iField) && + poFDefn->GetType() == OFTRealList ) + { + if( pnCount != NULL ) + *pnCount = pauFields[iField].RealList.nCount; + + return pauFields[iField].RealList.paList; + } + else + { + if( pnCount != NULL ) + *pnCount = 0; + + return NULL; + } +} + +/************************************************************************/ +/* OGR_F_GetFieldAsDoubleList() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a list of doubles. + * + * Currently this function only works for OFTRealList fields. + * + * This function is the same as the C++ method + * OGRFeature::GetFieldAsDoubleList(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pnCount an integer to put the list count (number of doubles) into. + * + * @return the field value. This list is internal, and should not be + * modified, or freed. Its lifetime may be very brief. If *pnCount is zero + * on return the returned pointer may be NULL or non-NULL. + */ + +const double *OGR_F_GetFieldAsDoubleList( OGRFeatureH hFeat, int iField, + int *pnCount ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldAsDoubleList", NULL ); + + return ((OGRFeature *)hFeat)->GetFieldAsDoubleList(iField, pnCount); +} + +/************************************************************************/ +/* GetFieldAsStringList() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a list of strings. + * + * Currently this method only works for OFTStringList fields. + * + * The returned list is terminated by a NULL pointer. The number of + * elements can also be calculated using CSLCount(). + * + * This method is the same as the C function OGR_F_GetFieldAsStringList(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the field value. This list is internal, and should not be + * modified, or freed. Its lifetime may be very brief. + */ + +char **OGRFeature::GetFieldAsStringList( int iField ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return NULL; + + if( !IsFieldSet(iField) ) + return NULL; + + if( poFDefn->GetType() == OFTStringList ) + { + return pauFields[iField].StringList.paList; + } + else + { + return NULL; + } +} + +/************************************************************************/ +/* OGR_F_GetFieldAsStringList() */ +/************************************************************************/ + +/** + * \brief Fetch field value as a list of strings. + * + * Currently this method only works for OFTStringList fields. + * + * The returned list is terminated by a NULL pointer. The number of + * elements can also be calculated using CSLCount(). + * + * This function is the same as the C++ method + * OGRFeature::GetFieldAsStringList(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * + * @return the field value. This list is internal, and should not be + * modified, or freed. Its lifetime may be very brief. + */ + +char **OGR_F_GetFieldAsStringList( OGRFeatureH hFeat, int iField ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldAsStringList", NULL ); + + return ((OGRFeature *)hFeat)->GetFieldAsStringList(iField); +} + +/************************************************************************/ +/* GetFieldAsBinary() */ +/************************************************************************/ + +/** + * \brief Fetch field value as binary data. + * + * This method only works for OFTBinary and OFTString fields. + * + * This method is the same as the C function OGR_F_GetFieldAsBinary(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pnBytes location to put the number of bytes returned. + * + * @return the field value. This data is internal, and should not be + * modified, or freed. Its lifetime may be very brief. + */ + +GByte *OGRFeature::GetFieldAsBinary( int iField, int *pnBytes ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + *pnBytes = 0; + + if( poFDefn == NULL ) + return NULL; + + if( !IsFieldSet(iField) ) + return NULL; + + if( poFDefn->GetType() == OFTBinary ) + { + *pnBytes = pauFields[iField].Binary.nCount; + return pauFields[iField].Binary.paData; + } + else if( poFDefn->GetType() == OFTString ) + { + *pnBytes = (int)strlen(pauFields[iField].String); + return (GByte*)pauFields[iField].String; + } + else + { + return NULL; + } +} + +/************************************************************************/ +/* OGR_F_GetFieldAsBinary() */ +/************************************************************************/ + +/** + * \brief Fetch field value as binary. + * + * This method only works for OFTBinary and OFTString fields. + * + * This function is the same as the C++ method + * OGRFeature::GetFieldAsBinary(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pnBytes location to place count of bytes returned. + * + * @return the field value. This list is internal, and should not be + * modified, or freed. Its lifetime may be very brief. + */ + +GByte *OGR_F_GetFieldAsBinary( OGRFeatureH hFeat, int iField, int *pnBytes ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldAsBinary", NULL ); + VALIDATE_POINTER1( pnBytes, "OGR_F_GetFieldAsBinary", NULL ); + + return ((OGRFeature *)hFeat)->GetFieldAsBinary(iField,pnBytes); +} + +/************************************************************************/ +/* GetFieldAsDateTime() */ +/************************************************************************/ + +/** + * \brief Fetch field value as date and time. + * + * Currently this method only works for OFTDate, OFTTime and OFTDateTime fields. + * + * This method is the same as the C function OGR_F_GetFieldAsDateTime(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pnYear (including century) + * @param pnMonth (1-12) + * @param pnDay (1-31) + * @param pnHour (0-23) + * @param pnMinute (0-59) + * @param pfSecond (0-59 with millisecond accuracy) + * @param pnTZFlag (0=unknown, 1=localtime, 100=GMT, see data model for details) + * + * @return TRUE on success or FALSE on failure. + */ + +int OGRFeature::GetFieldAsDateTime( int iField, + int *pnYear, int *pnMonth, int *pnDay, + int *pnHour, int *pnMinute, float *pfSecond, + int *pnTZFlag ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return FALSE; + + if( !IsFieldSet(iField) ) + return FALSE; + + if( poFDefn->GetType() == OFTDate + || poFDefn->GetType() == OFTTime + || poFDefn->GetType() == OFTDateTime ) + + { + if( pnYear ) + *pnYear = pauFields[iField].Date.Year; + if( pnMonth ) + *pnMonth = pauFields[iField].Date.Month; + if( pnDay ) + *pnDay = pauFields[iField].Date.Day; + if( pnHour ) + *pnHour = pauFields[iField].Date.Hour; + if( pnMinute ) + *pnMinute = pauFields[iField].Date.Minute; + if( pfSecond ) + *pfSecond = pauFields[iField].Date.Second; + if( pnTZFlag ) + *pnTZFlag = pauFields[iField].Date.TZFlag; + + return TRUE; + } + else + { + return FALSE; + } +} + +int OGRFeature::GetFieldAsDateTime( int iField, + int *pnYear, int *pnMonth, int *pnDay, + int *pnHour, int *pnMinute, int *pnSecond, + int *pnTZFlag ) +{ + float fSecond; + int nRet = GetFieldAsDateTime( iField, pnYear, pnMonth, pnDay, pnHour, pnMinute, + &fSecond, pnTZFlag); + if( pnSecond ) *pnSecond = (int)fSecond; + return nRet; +} + +/************************************************************************/ +/* OGR_F_GetFieldAsDateTime() */ +/************************************************************************/ + +/** + * \brief Fetch field value as date and time. + * + * Currently this method only works for OFTDate, OFTTime and OFTDateTime fields. + * + * This function is the same as the C++ method + * OGRFeature::GetFieldAsDateTime(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pnYear (including century) + * @param pnMonth (1-12) + * @param pnDay (1-31) + * @param pnHour (0-23) + * @param pnMinute (0-59) + * @param pnSecond (0-59) + * @param pnTZFlag (0=unknown, 1=localtime, 100=GMT, see data model for details) + * + * @return TRUE on success or FALSE on failure. + * + * @see Use OGR_F_GetFieldAsDateTimeEx() for second with millisecond accuracy. + */ + +int OGR_F_GetFieldAsDateTime( OGRFeatureH hFeat, int iField, + int *pnYear, int *pnMonth, int *pnDay, + int *pnHour, int *pnMinute, int *pnSecond, + int *pnTZFlag ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldAsDateTime", 0 ); + + float fSecond; + int nRet =((OGRFeature *)hFeat)->GetFieldAsDateTime( iField, + pnYear, pnMonth, pnDay, + pnHour, pnMinute,&fSecond, + pnTZFlag ); + if( pnSecond ) *pnSecond = (int)fSecond; + return nRet; +} + +/************************************************************************/ +/* OGR_F_GetFieldAsDateTimeEx() */ +/************************************************************************/ + +/** + * \brief Fetch field value as date and time. + * + * Currently this method only works for OFTDate, OFTTime and OFTDateTime fields. + * + * This function is the same as the C++ method + * OGRFeature::GetFieldAsDateTime(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pnYear (including century) + * @param pnMonth (1-12) + * @param pnDay (1-31) + * @param pnHour (0-23) + * @param pnMinute (0-59) + * @param pfSecond (0-59 with millisecond accuracy) + * @param pnTZFlag (0=unknown, 1=localtime, 100=GMT, see data model for details) + * + * @return TRUE on success or FALSE on failure. + * @since GDAL 2.0 + */ + +int OGR_F_GetFieldAsDateTimeEx( OGRFeatureH hFeat, int iField, + int *pnYear, int *pnMonth, int *pnDay, + int *pnHour, int *pnMinute, float *pfSecond, + int *pnTZFlag ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFieldAsDateTimeEx", 0 ); + + return ((OGRFeature *)hFeat)->GetFieldAsDateTime( iField, + pnYear, pnMonth, pnDay, + pnHour, pnMinute,pfSecond, + pnTZFlag ); +} + +/************************************************************************/ +/* OGRFeatureGetIntegerValue() */ +/************************************************************************/ + +static int OGRFeatureGetIntegerValue(OGRFieldDefn *poFDefn, int nValue) +{ + if( poFDefn->GetSubType() == OFSTBoolean && nValue != 0 && nValue != 1 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Only 0 or 1 should be passed for a OFSTBoolean subtype. " + "Considering this non-zero value as 1."); + nValue = 1; + } + else if( poFDefn->GetSubType() == OFSTInt16 ) + { + if( nValue < -32768 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Out-of-range value for a OFSTInt16 subtype. " + "Considering this value as -32768."); + nValue = -32768; + } + else if( nValue > 32767 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Out-of-range value for a OFSTInt16 subtype. " + "Considering this value as 32767."); + nValue = 32767; + } + } + return nValue; +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +/** + * \brief Set field to integer value. + * + * OFTInteger, OFTInteger64 and OFTReal fields will be set directly. OFTString fields + * will be assigned a string representation of the value, but not necessarily + * taking into account formatting constraints on this field. Other field + * types may be unaffected. + * + * This method is the same as the C function OGR_F_SetFieldInteger(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param nValue the value to assign. + */ + +void OGRFeature::SetField( int iField, int nValue ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return; + + OGRFieldType eType = poFDefn->GetType(); + if( eType == OFTInteger ) + { + pauFields[iField].Integer = OGRFeatureGetIntegerValue(poFDefn, nValue); + pauFields[iField].Set.nMarker2 = 0; + } + else if( eType == OFTInteger64 ) + { + pauFields[iField].Integer64 = OGRFeatureGetIntegerValue(poFDefn, nValue); + } + else if( eType == OFTReal ) + { + pauFields[iField].Real = nValue; + } + else if( eType == OFTIntegerList ) + { + SetField( iField, 1, &nValue ); + } + else if( eType == OFTInteger64List ) + { + GIntBig nVal64 = nValue; + SetField( iField, 1, &nVal64 ); + } + else if( eType == OFTRealList ) + { + double dfValue = nValue; + SetField( iField, 1, &dfValue ); + } + else if( eType == OFTString ) + { + char szTempBuffer[64]; + + sprintf( szTempBuffer, "%d", nValue ); + + if( IsFieldSet( iField) ) + CPLFree( pauFields[iField].String ); + + pauFields[iField].String = CPLStrdup( szTempBuffer ); + } + else + { + /* do nothing for other field types */ + } +} + +/************************************************************************/ +/* OGR_F_SetFieldInteger() */ +/************************************************************************/ + +/** + * \brief Set field to integer value. + * + * OFTInteger, OFTInteger64 and OFTReal fields will be set directly. OFTString fields + * will be assigned a string representation of the value, but not necessarily + * taking into account formatting constraints on this field. Other field + * types may be unaffected. + * + * This function is the same as the C++ method OGRFeature::SetField(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param nValue the value to assign. + */ + +void OGR_F_SetFieldInteger( OGRFeatureH hFeat, int iField, int nValue ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetFieldInteger" ); + + ((OGRFeature *)hFeat)->SetField( iField, nValue ); +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +/** + * \brief Set field to 64 bit integer value. + * + * OFTInteger, OFTInteger64 and OFTReal fields will be set directly. OFTString fields + * will be assigned a string representation of the value, but not necessarily + * taking into account formatting constraints on this field. Other field + * types may be unaffected. + * + * This method is the same as the C function OGR_F_SetFieldInteger64(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param nValue the value to assign. + * @since GDAL 2.0 + */ + +void OGRFeature::SetField( int iField, GIntBig nValue ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return; + + OGRFieldType eType = poFDefn->GetType(); + if( eType == OFTInteger ) + { + int nVal32 = (nValue < INT_MIN ) ? INT_MIN : (nValue > INT_MAX) ? INT_MAX : (int)nValue; + if( (GIntBig)nVal32 != nValue ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Integer overflow occured when trying to set 32bit field."); + } + SetField(iField, nVal32); + } + else if( eType == OFTInteger64 ) + { + pauFields[iField].Integer64 = nValue; + } + else if( eType == OFTReal ) + { + pauFields[iField].Real = (double) nValue; + } + else if( eType == OFTIntegerList ) + { + int nVal32 = (nValue < INT_MIN ) ? INT_MIN : (nValue > INT_MAX) ? INT_MAX : (int)nValue; + if( (GIntBig)nVal32 != nValue ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Integer overflow occured when trying to set 32bit field."); + } + SetField( iField, 1, &nVal32 ); + } + else if( eType == OFTInteger64List ) + { + SetField( iField, 1, &nValue ); + } + else if( eType == OFTRealList ) + { + double dfValue = (double) nValue; + SetField( iField, 1, &dfValue ); + } + else if( eType == OFTString ) + { + char szTempBuffer[64]; + + sprintf( szTempBuffer, CPL_FRMT_GIB, nValue ); + + if( IsFieldSet( iField) ) + CPLFree( pauFields[iField].String ); + + pauFields[iField].String = CPLStrdup( szTempBuffer ); + } + else + { + /* do nothing for other field types */ + } +} + +/************************************************************************/ +/* OGR_F_SetFieldInteger64() */ +/************************************************************************/ + +/** + * \brief Set field to 64 bit integer value. + * + * OFTInteger, OFTInteger64 and OFTReal fields will be set directly. OFTString fields + * will be assigned a string representation of the value, but not necessarily + * taking into account formatting constraints on this field. Other field + * types may be unaffected. + * + * This function is the same as the C++ method OGRFeature::SetField(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param nValue the value to assign. + * @since GDAL 2.0 + */ + +void OGR_F_SetFieldInteger64( OGRFeatureH hFeat, int iField, GIntBig nValue ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetFieldInteger64" ); + + ((OGRFeature *)hFeat)->SetField( iField, nValue ); +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +/** + * \brief Set field to double value. + * + * OFTInteger, OFTInteger64 and OFTReal fields will be set directly. OFTString fields + * will be assigned a string representation of the value, but not necessarily + * taking into account formatting constraints on this field. Other field + * types may be unaffected. + * + * This method is the same as the C function OGR_F_SetFieldDouble(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param dfValue the value to assign. + */ + +void OGRFeature::SetField( int iField, double dfValue ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return; + + OGRFieldType eType = poFDefn->GetType(); + if( eType == OFTReal ) + { + /*if( poFDefn->GetSubType() == OFSTFloat32 && dfValue != (double)(float)dfValue ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Passed value cannot be exactly representing as a single-precision floating point value."); + dfValue = (double)(float)dfValue; + }*/ + pauFields[iField].Real = dfValue; + } + else if( eType == OFTInteger ) + { + pauFields[iField].Integer = (int) dfValue; + pauFields[iField].Set.nMarker2 = 0; + } + else if( eType == OFTInteger64 ) + { + pauFields[iField].Integer64 = (GIntBig) dfValue; + } + else if( eType == OFTRealList ) + { + SetField( iField, 1, &dfValue ); + } + else if( eType == OFTIntegerList ) + { + int nValue = (int) dfValue; + SetField( iField, 1, &nValue ); + } + else if( eType == OFTInteger64List ) + { + GIntBig nValue = (GIntBig) dfValue; + SetField( iField, 1, &nValue ); + } + else if( eType == OFTString ) + { + char szTempBuffer[128]; + + CPLsprintf( szTempBuffer, "%.16g", dfValue ); + + if( IsFieldSet( iField) ) + CPLFree( pauFields[iField].String ); + + pauFields[iField].String = CPLStrdup( szTempBuffer ); + } + else + { + /* do nothing for other field types */ + } +} + +/************************************************************************/ +/* OGR_F_SetFieldDouble() */ +/************************************************************************/ + +/** + * \brief Set field to double value. + * + * OFTInteger, OFTInteger64 and OFTReal fields will be set directly. OFTString fields + * will be assigned a string representation of the value, but not necessarily + * taking into account formatting constraints on this field. Other field + * types may be unaffected. + * + * This function is the same as the C++ method OGRFeature::SetField(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param dfValue the value to assign. + */ + +void OGR_F_SetFieldDouble( OGRFeatureH hFeat, int iField, double dfValue ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetFieldDouble" ); + + ((OGRFeature *)hFeat)->SetField( iField, dfValue ); +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +/** + * \brief Set field to string value. + * + * OFTInteger fields will be set based on an atoi() conversion of the string. + * OFTInteger64 fields will be set based on an CPLAtoGIntBig() conversion of the string. + * OFTReal fields will be set based on an CPLAtof() conversion of the string. + * Other field types may be unaffected. + * + * This method is the same as the C function OGR_F_SetFieldString(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pszValue the value to assign. + */ + +void OGRFeature::SetField( int iField, const char * pszValue ) + +{ + static int bWarn = -1; + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + char *pszLast = NULL; + + if( bWarn < 0 ) + bWarn = CSLTestBoolean( CPLGetConfigOption( "OGR_SETFIELD_NUMERIC_WARNING", "YES" ) ); + + if( poFDefn == NULL ) + return; + + OGRFieldType eType = poFDefn->GetType(); + if( eType == OFTString ) + { + if( IsFieldSet(iField) ) + CPLFree( pauFields[iField].String ); + + pauFields[iField].String = CPLStrdup( pszValue ); + } + else if( eType == OFTInteger ) + { + errno = 0; /* As allowed by C standard, some systems like MSVC doesn't reset errno */ + long nVal = strtol(pszValue, &pszLast, 10); + nVal = OGRFeatureGetIntegerValue(poFDefn, nVal); + pauFields[iField].Integer = (nVal > INT_MAX) ? INT_MAX : (nVal < INT_MIN) ? INT_MIN : (int) nVal; + if( bWarn && (errno == ERANGE || nVal != (long)pauFields[iField].Integer || !pszLast || *pszLast ) ) + CPLError(CE_Warning, CPLE_AppDefined, + "Value '%s' of field %s.%s parsed incompletely to integer %d.", + pszValue, poDefn->GetName(), poFDefn->GetNameRef(), pauFields[iField].Integer ); + pauFields[iField].Set.nMarker2 = OGRUnsetMarker; + } + else if( eType == OFTInteger64 ) + { + pauFields[iField].Integer64 = CPLAtoGIntBigEx(pszValue, bWarn, NULL); + } + else if( eType == OFTReal ) + { + pauFields[iField].Real = CPLStrtod(pszValue, &pszLast); + if( bWarn && ( !pszLast || *pszLast ) ) + CPLError(CE_Warning, CPLE_AppDefined, + "Value '%s' of field %s.%s parsed incompletely to real %.16g.", + pszValue, poDefn->GetName(), poFDefn->GetNameRef(), pauFields[iField].Real ); + } + else if( eType == OFTDate + || eType == OFTTime + || eType == OFTDateTime ) + { + OGRField sWrkField; + + if( OGRParseDate( pszValue, &sWrkField, 0 ) ) + memcpy( pauFields+iField, &sWrkField, sizeof(sWrkField)); + } + else if( eType == OFTIntegerList + || eType == OFTInteger64List + || eType == OFTRealList ) + { + char **papszValueList = NULL; + + if( pszValue[0] == '(' && strchr(pszValue,':') != NULL ) + { + papszValueList = CSLTokenizeString2( + pszValue, ",:()", 0 ); + } + + if( CSLCount(papszValueList) == 0 + || atoi(papszValueList[0]) != CSLCount(papszValueList)-1 ) + { + /* do nothing - the count does not match entries */ + } + else if( eType == OFTIntegerList ) + { + int i, nCount = atoi(papszValueList[0]); + std::vector anValues; + if( nCount == CSLCount(papszValueList)-1 ) + { + for( i=0; i < nCount; i++ ) + { + errno = 0; /* As allowed by C standard, some systems like MSVC doesn't reset errno */ + int nVal = atoi(papszValueList[i+1]); + if( errno == ERANGE ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "32 bit integer overflow when converting %s", + pszValue); + } + anValues.push_back( nVal ); + } + SetField( iField, nCount, &(anValues[0]) ); + } + } + else if( eType == OFTInteger64List ) + { + int i, nCount = atoi(papszValueList[0]); + std::vector anValues; + if( nCount == CSLCount(papszValueList)-1 ) + { + for( i=0; i < nCount; i++ ) + { + GIntBig nVal = CPLAtoGIntBigEx(papszValueList[i+1], TRUE, NULL); + anValues.push_back( nVal ); + } + SetField( iField, nCount, &(anValues[0]) ); + } + } + else if( eType == OFTRealList ) + { + int i, nCount = atoi(papszValueList[0]); + std::vector adfValues; + if( nCount == CSLCount(papszValueList)-1 ) + { + for( i=0; i < nCount; i++ ) + adfValues.push_back( CPLAtof(papszValueList[i+1]) ); + SetField( iField, nCount, &(adfValues[0]) ); + } + } + + CSLDestroy(papszValueList); + } + else if ( eType == OFTStringList ) + { + if( pszValue && *pszValue ) + { + if( pszValue[0] == '(' && strchr(pszValue,':') != NULL ) + { + char** papszValueList = CSLTokenizeString2( + pszValue, ",:()", 0 ); + int i, nCount = atoi(papszValueList[0]); + std::vector aosValues; + if( nCount == CSLCount(papszValueList)-1 ) + { + for( i=0; i < nCount; i++ ) + aosValues.push_back( papszValueList[i+1] ); + aosValues.push_back( NULL ); + SetField( iField, &(aosValues[0]) ); + } + CSLDestroy(papszValueList); + } + else + { + const char *papszValues[2] = { pszValue, 0 }; + SetField( iField, (char **) papszValues ); + } + } + } + else + { + /* do nothing for other field types */; + } +} + +/************************************************************************/ +/* OGR_F_SetFieldString() */ +/************************************************************************/ + +/** + * \brief Set field to string value. + * + * OFTInteger fields will be set based on an atoi() conversion of the string. + * OFTInteger64 fields will be set based on an CPLAtoGIntBig() conversion of the string. + * OFTReal fields will be set based on an CPLAtof() conversion of the string. + * Other field types may be unaffected. + * + * This function is the same as the C++ method OGRFeature::SetField(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param pszValue the value to assign. + */ + +void OGR_F_SetFieldString( OGRFeatureH hFeat, int iField, const char *pszValue) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetFieldString" ); + + ((OGRFeature *)hFeat)->SetField( iField, pszValue ); +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +/** + * \brief Set field to list of integers value. + * + * This method currently on has an effect of OFTIntegerList, OFTInteger64List + * and OFTRealList fields. + * + * This method is the same as the C function OGR_F_SetFieldIntegerList(). + * + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param nCount the number of values in the list being assigned. + * @param panValues the values to assign. + */ + +void OGRFeature::SetField( int iField, int nCount, int *panValues ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return; + + if( poFDefn->GetType() == OFTIntegerList ) + { + OGRField uField; + int *panValuesMod = NULL; + + if( poFDefn->GetSubType() == OFSTBoolean || poFDefn->GetSubType() == OFSTInt16 ) + { + for( int i=0;iGetType() == OFTInteger64List ) + { + std::vector anValues; + + for( int i=0; i < nCount; i++ ) + anValues.push_back( panValues[i] ); + + SetField( iField, nCount, &anValues[0] ); + } + else if( poFDefn->GetType() == OFTRealList ) + { + std::vector adfValues; + + for( int i=0; i < nCount; i++ ) + adfValues.push_back( (double) panValues[i] ); + + SetField( iField, nCount, &adfValues[0] ); + } + else if( (poFDefn->GetType() == OFTInteger || + poFDefn->GetType() == OFTInteger64 || + poFDefn->GetType() == OFTReal) + && nCount == 1 ) + { + SetField( iField, panValues[0] ); + } +} + +/************************************************************************/ +/* OGR_F_SetFieldIntegerList() */ +/************************************************************************/ + +/** + * \brief Set field to list of integers value. + * + * This function currently on has an effect of OFTIntegerList, OFTInteger64List + * and OFTRealList fields. + * + * This function is the same as the C++ method OGRFeature::SetField(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param nCount the number of values in the list being assigned. + * @param panValues the values to assign. + */ + +void OGR_F_SetFieldIntegerList( OGRFeatureH hFeat, int iField, + int nCount, int *panValues ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetFieldIntegerList" ); + + ((OGRFeature *)hFeat)->SetField( iField, nCount, panValues ); +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +/** + * \brief Set field to list of 64 bit integers value. + * + * This method currently on has an effect of OFTIntegerList, OFTInteger64List + * and OFTRealList fields. + * + * This method is the same as the C function OGR_F_SetFieldIntege64rList(). + * + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param nCount the number of values in the list being assigned. + * @param panValues the values to assign. + * @since GDAL 2.0 + */ + +void OGRFeature::SetField( int iField, int nCount, const GIntBig *panValues ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return; + + if( poFDefn->GetType() == OFTIntegerList ) + { + std::vector anValues; + + for( int i=0; i < nCount; i++ ) + { + GIntBig nValue = panValues[i]; + int nVal32 = (nValue < INT_MIN ) ? INT_MIN : (nValue > INT_MAX) ? INT_MAX : (int)nValue; + if( (GIntBig)nVal32 != nValue ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Integer overflow occured when trying to set 32bit field."); + } + anValues.push_back( nVal32 ); + } + + SetField( iField, nCount, &anValues[0] ); + } + else if( poFDefn->GetType() == OFTInteger64List ) + { + OGRField uField; + uField.Integer64List.nCount = nCount; + uField.Set.nMarker2 = 0; + uField.Integer64List.paList = (GIntBig*) panValues; + + SetField( iField, &uField ); + } + else if( poFDefn->GetType() == OFTRealList ) + { + std::vector adfValues; + + for( int i=0; i < nCount; i++ ) + adfValues.push_back( (double) panValues[i] ); + + SetField( iField, nCount, &adfValues[0] ); + } + else if( (poFDefn->GetType() == OFTInteger || + poFDefn->GetType() == OFTInteger64 || + poFDefn->GetType() == OFTReal) + && nCount == 1 ) + { + SetField( iField, panValues[0] ); + } +} + +/************************************************************************/ +/* OGR_F_SetFieldInteger64List() */ +/************************************************************************/ + +/** + * \brief Set field to list of 64 bit integers value. + * + * This function currently on has an effect of OFTIntegerList, OFTInteger64List + * and OFTRealList fields. + * + * This function is the same as the C++ method OGRFeature::SetField(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param nCount the number of values in the list being assigned. + * @param panValues the values to assign. + * @since GDAL 2.0 + */ + +void OGR_F_SetFieldInteger64List( OGRFeatureH hFeat, int iField, + int nCount, const GIntBig *panValues ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetFieldInteger64List" ); + + ((OGRFeature *)hFeat)->SetField( iField, nCount, panValues ); +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +/** + * \brief Set field to list of doubles value. + * + * This method currently on has an effect of OFTIntegerList, OFTInteger64List, + * OFTRealList fields. + * + * This method is the same as the C function OGR_F_SetFieldDoubleList(). + * + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param nCount the number of values in the list being assigned. + * @param padfValues the values to assign. + */ + +void OGRFeature::SetField( int iField, int nCount, double * padfValues ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return; + + if( poFDefn->GetType() == OFTRealList ) + { + OGRField uField; + + uField.RealList.nCount = nCount; + uField.Set.nMarker2 = 0; + uField.RealList.paList = padfValues; + + SetField( iField, &uField ); + } + else if( poFDefn->GetType() == OFTIntegerList ) + { + std::vector anValues; + + for( int i=0; i < nCount; i++ ) + anValues.push_back( (int) padfValues[i] ); + + SetField( iField, nCount, &anValues[0] ); + } + else if( poFDefn->GetType() == OFTInteger64List ) + { + std::vector anValues; + + for( int i=0; i < nCount; i++ ) + anValues.push_back( (GIntBig) padfValues[i] ); + + SetField( iField, nCount, &anValues[0] ); + } + else if( (poFDefn->GetType() == OFTInteger || + poFDefn->GetType() == OFTInteger64 || + poFDefn->GetType() == OFTReal) + && nCount == 1 ) + { + SetField( iField, padfValues[0] ); + } +} + +/************************************************************************/ +/* OGR_F_SetFieldDoubleList() */ +/************************************************************************/ + +/** + * \brief Set field to list of doubles value. + * + * This function currently on has an effect of OFTIntegerList, OFTInteger64List, + * OFTRealList fields. + * + * This function is the same as the C++ method OGRFeature::SetField(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param nCount the number of values in the list being assigned. + * @param padfValues the values to assign. + */ + +void OGR_F_SetFieldDoubleList( OGRFeatureH hFeat, int iField, + int nCount, double *padfValues ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetFieldDoubleList" ); + + ((OGRFeature *)hFeat)->SetField( iField, nCount, padfValues ); +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +/** + * \brief Set field to list of strings value. + * + * This method currently on has an effect of OFTStringList fields. + * + * This method is the same as the C function OGR_F_SetFieldStringList(). + * + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param papszValues the values to assign. + */ + +void OGRFeature::SetField( int iField, char ** papszValues ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return; + + if( poFDefn->GetType() == OFTStringList ) + { + OGRField uField; + + uField.StringList.nCount = CSLCount(papszValues); + uField.Set.nMarker2 = 0; + uField.StringList.paList = papszValues; + + SetField( iField, &uField ); + } +} + +/************************************************************************/ +/* OGR_F_SetFieldStringList() */ +/************************************************************************/ + +/** + * \brief Set field to list of strings value. + * + * This function currently on has an effect of OFTStringList fields. + * + * This function is the same as the C++ method OGRFeature::SetField(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param papszValues the values to assign. + */ + +void OGR_F_SetFieldStringList( OGRFeatureH hFeat, int iField, + char ** papszValues ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetFieldStringList" ); + + ((OGRFeature *)hFeat)->SetField( iField, papszValues ); +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +/** + * \brief Set field to binary data. + * + * This method currently on has an effect of OFTBinary fields. + * + * This method is the same as the C function OGR_F_SetFieldBinary(). + * + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param nBytes bytes of data being set. + * @param pabyData the raw data being applied. + */ + +void OGRFeature::SetField( int iField, int nBytes, GByte *pabyData ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return; + + if( poFDefn->GetType() == OFTBinary ) + { + OGRField uField; + + uField.Binary.nCount = nBytes; + uField.Set.nMarker2 = 0; + uField.Binary.paData = pabyData; + + SetField( iField, &uField ); + } + else if( poFDefn->GetType() == OFTString ) + { + char* pszStr = (char*)CPLMalloc(nBytes + 1); + memcpy(pszStr, pabyData, nBytes); + pszStr[nBytes] = 0; + SetField( iField, pszStr ); + CPLFree( pszStr); + } +} + +/************************************************************************/ +/* OGR_F_SetFieldBinary() */ +/************************************************************************/ + +/** + * \brief Set field to binary data. + * + * This function currently on has an effect of OFTBinary fields. + * + * This function is the same as the C++ method OGRFeature::SetField(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param nBytes the number of bytes in pabyData array. + * @param pabyData the data to apply. + */ + +void OGR_F_SetFieldBinary( OGRFeatureH hFeat, int iField, + int nBytes, GByte *pabyData ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetFieldBinary" ); + + ((OGRFeature *)hFeat)->SetField( iField, nBytes, pabyData ); +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +/** + * \brief Set field to date. + * + * This method currently only has an effect for OFTDate, OFTTime and OFTDateTime + * fields. + * + * This method is the same as the C function OGR_F_SetFieldDateTime(). + * + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param nYear (including century) + * @param nMonth (1-12) + * @param nDay (1-31) + * @param nHour (0-23) + * @param nMinute (0-59) + * @param fSecond (0-59, with millisecond accuracy) + * @param nTZFlag (0=unknown, 1=localtime, 100=GMT, see data model for details) + */ + +void OGRFeature::SetField( int iField, int nYear, int nMonth, int nDay, + int nHour, int nMinute, float fSecond, + int nTZFlag ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return; + + if( poFDefn->GetType() == OFTDate + || poFDefn->GetType() == OFTTime + || poFDefn->GetType() == OFTDateTime ) + { + if( (GInt16)nYear != nYear ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Years < -32768 or > 32767 are not supported"); + return; + } + + pauFields[iField].Date.Year = (GInt16)nYear; + pauFields[iField].Date.Month = (GByte)nMonth; + pauFields[iField].Date.Day = (GByte)nDay; + pauFields[iField].Date.Hour = (GByte)nHour; + pauFields[iField].Date.Minute = (GByte)nMinute; + pauFields[iField].Date.Second = fSecond; + pauFields[iField].Date.TZFlag = (GByte)nTZFlag; + } +} + +/************************************************************************/ +/* OGR_F_SetFieldDateTime() */ +/************************************************************************/ + +/** + * \brief Set field to datetime. + * + * This method currently only has an effect for OFTDate, OFTTime and OFTDateTime + * fields. + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param nYear (including century) + * @param nMonth (1-12) + * @param nDay (1-31) + * @param nHour (0-23) + * @param nMinute (0-59) + * @param nSecond (0-59) + * @param nTZFlag (0=unknown, 1=localtime, 100=GMT, see data model for details) + * + * @see Use OGR_F_SetFieldDateTimeEx() for second with millisecond accuracy. + */ + +void OGR_F_SetFieldDateTime( OGRFeatureH hFeat, int iField, + int nYear, int nMonth, int nDay, + int nHour, int nMinute, int nSecond, + int nTZFlag ) + + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetFieldDateTime" ); + + ((OGRFeature *)hFeat)->SetField( iField, nYear, nMonth, nDay, + nHour, nMinute, nSecond, nTZFlag ); +} + +/************************************************************************/ +/* OGR_F_SetFieldDateTimeEx() */ +/************************************************************************/ + +/** + * \brief Set field to datetime. + * + * This method currently only has an effect for OFTDate, OFTTime and OFTDateTime + * fields. + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to set, from 0 to GetFieldCount()-1. + * @param nYear (including century) + * @param nMonth (1-12) + * @param nDay (1-31) + * @param nHour (0-23) + * @param nMinute (0-59) + * @param fSecond (0-59, with millisecond accuracy) + * @param nTZFlag (0=unknown, 1=localtime, 100=GMT, see data model for details) + * + * @since GDAL 2.0 + */ + +void OGR_F_SetFieldDateTimeEx( OGRFeatureH hFeat, int iField, + int nYear, int nMonth, int nDay, + int nHour, int nMinute, float fSecond, + int nTZFlag ) + + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetFieldDateTimeEx" ); + + ((OGRFeature *)hFeat)->SetField( iField, nYear, nMonth, nDay, + nHour, nMinute, fSecond, nTZFlag ); +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +/** + * \brief Set field. + * + * The passed value OGRField must be of exactly the same type as the + * target field, or an application crash may occur. The passed value + * is copied, and will not be affected. It remains the responsibility of + * the caller. + * + * This method is the same as the C function OGR_F_SetFieldRaw(). + * + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param puValue the value to assign. + */ + +void OGRFeature::SetField( int iField, OGRField * puValue ) + +{ + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn( iField ); + + if( poFDefn == NULL ) + return; + + if( poFDefn->GetType() == OFTInteger ) + { + pauFields[iField] = *puValue; + } + else if( poFDefn->GetType() == OFTInteger64 ) + { + pauFields[iField] = *puValue; + } + else if( poFDefn->GetType() == OFTInteger64 ) + { + pauFields[iField] = *puValue; + } + else if( poFDefn->GetType() == OFTReal ) + { + pauFields[iField] = *puValue; + } + else if( poFDefn->GetType() == OFTString ) + { + if( IsFieldSet( iField ) ) + CPLFree( pauFields[iField].String ); + + if( puValue->String == NULL ) + pauFields[iField].String = NULL; + else if( puValue->Set.nMarker1 == OGRUnsetMarker + && puValue->Set.nMarker2 == OGRUnsetMarker ) + pauFields[iField] = *puValue; + else + pauFields[iField].String = CPLStrdup( puValue->String ); + } + else if( poFDefn->GetType() == OFTDate + || poFDefn->GetType() == OFTTime + || poFDefn->GetType() == OFTDateTime ) + { + memcpy( pauFields+iField, puValue, sizeof(OGRField) ); + } + else if( poFDefn->GetType() == OFTIntegerList ) + { + int nCount = puValue->IntegerList.nCount; + + if( IsFieldSet( iField ) ) + CPLFree( pauFields[iField].IntegerList.paList ); + + if( puValue->Set.nMarker1 == OGRUnsetMarker + && puValue->Set.nMarker2 == OGRUnsetMarker ) + { + pauFields[iField] = *puValue; + } + else + { + pauFields[iField].IntegerList.paList = + (int *) CPLMalloc(sizeof(int) * nCount); + memcpy( pauFields[iField].IntegerList.paList, + puValue->IntegerList.paList, + sizeof(int) * nCount ); + pauFields[iField].IntegerList.nCount = nCount; + } + } + else if( poFDefn->GetType() == OFTInteger64List ) + { + int nCount = puValue->Integer64List.nCount; + + if( IsFieldSet( iField ) ) + CPLFree( pauFields[iField].Integer64List.paList ); + + if( puValue->Set.nMarker1 == OGRUnsetMarker + && puValue->Set.nMarker2 == OGRUnsetMarker ) + { + pauFields[iField] = *puValue; + } + else + { + pauFields[iField].Integer64List.paList = + (GIntBig *) CPLMalloc(sizeof(GIntBig) * nCount); + memcpy( pauFields[iField].Integer64List.paList, + puValue->Integer64List.paList, + sizeof(GIntBig) * nCount ); + pauFields[iField].Integer64List.nCount = nCount; + } + } + else if( poFDefn->GetType() == OFTRealList ) + { + int nCount = puValue->RealList.nCount; + + if( IsFieldSet( iField ) ) + CPLFree( pauFields[iField].RealList.paList ); + + if( puValue->Set.nMarker1 == OGRUnsetMarker + && puValue->Set.nMarker2 == OGRUnsetMarker ) + { + pauFields[iField] = *puValue; + } + else + { + pauFields[iField].RealList.paList = + (double *) CPLMalloc(sizeof(double) * nCount); + memcpy( pauFields[iField].RealList.paList, + puValue->RealList.paList, + sizeof(double) * nCount ); + pauFields[iField].RealList.nCount = nCount; + } + } + else if( poFDefn->GetType() == OFTStringList ) + { + if( IsFieldSet( iField ) ) + CSLDestroy( pauFields[iField].StringList.paList ); + + if( puValue->Set.nMarker1 == OGRUnsetMarker + && puValue->Set.nMarker2 == OGRUnsetMarker ) + { + pauFields[iField] = *puValue; + } + else + { + pauFields[iField].StringList.paList = + CSLDuplicate( puValue->StringList.paList ); + + pauFields[iField].StringList.nCount = puValue->StringList.nCount; + CPLAssert( CSLCount(puValue->StringList.paList) + == puValue->StringList.nCount ); + } + } + else if( poFDefn->GetType() == OFTBinary ) + { + if( IsFieldSet( iField ) ) + CPLFree( pauFields[iField].Binary.paData ); + + if( puValue->Set.nMarker1 == OGRUnsetMarker + && puValue->Set.nMarker2 == OGRUnsetMarker ) + { + pauFields[iField] = *puValue; + } + else + { + pauFields[iField].Binary.nCount = puValue->Binary.nCount; + pauFields[iField].Binary.paData = + (GByte *) CPLMalloc(puValue->Binary.nCount); + memcpy( pauFields[iField].Binary.paData, + puValue->Binary.paData, + puValue->Binary.nCount ); + } + } + else + { + /* do nothing for other field types */ + } +} + +/************************************************************************/ +/* OGR_F_SetFieldRaw() */ +/************************************************************************/ + +/** + * \brief Set field. + * + * The passed value OGRField must be of exactly the same type as the + * target field, or an application crash may occur. The passed value + * is copied, and will not be affected. It remains the responsibility of + * the caller. + * + * This function is the same as the C++ method OGRFeature::SetField(). + * + * @param hFeat handle to the feature that owned the field. + * @param iField the field to fetch, from 0 to GetFieldCount()-1. + * @param psValue handle on the value to assign. + */ + +void OGR_F_SetFieldRaw( OGRFeatureH hFeat, int iField, OGRField *psValue ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetFieldRaw" ); + + ((OGRFeature *)hFeat)->SetField( iField, psValue ); +} + +/************************************************************************/ +/* DumpReadable() */ +/************************************************************************/ + +/** + * \brief Dump this feature in a human readable form. + * + * This dumps the attributes, and geometry; however, it doesn't definition + * information (other than field types and names), nor does it report the + * geometry spatial reference system. + * + * A few options can be defined to change the default dump : + *
      + *
    • DISPLAY_FIELDS=NO : to hide the dump of the attributes
    • + *
    • DISPLAY_STYLE=NO : to hide the dump of the style string
    • + *
    • DISPLAY_GEOMETRY=NO : to hide the dump of the geometry
    • + *
    • DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry
    • + *
    + * + * This method is the same as the C function OGR_F_DumpReadable(). + * + * @param fpOut the stream to write to, such as stdout. If NULL stdout will + * be used. + * @param papszOptions NULL terminated list of options (may be NULL) + */ + +void OGRFeature::DumpReadable( FILE * fpOut, char** papszOptions ) + +{ + if( fpOut == NULL ) + fpOut = stdout; + + fprintf( fpOut, "OGRFeature(%s):" CPL_FRMT_GIB "\n", poDefn->GetName(), GetFID() ); + + const char* pszDisplayFields = + CSLFetchNameValue(papszOptions, "DISPLAY_FIELDS"); + if (pszDisplayFields == NULL || CSLTestBoolean(pszDisplayFields)) + { + for( int iField = 0; iField < GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn(iField); + + const char* pszType = (poFDefn->GetSubType() != OFSTNone) ? + CPLSPrintf("%s(%s)", + poFDefn->GetFieldTypeName( poFDefn->GetType() ), + poFDefn->GetFieldSubTypeName(poFDefn->GetSubType())) : + poFDefn->GetFieldTypeName( poFDefn->GetType() ); + + fprintf( fpOut, " %s (%s) = ", + poFDefn->GetNameRef(), + pszType ); + + if( IsFieldSet( iField ) ) + fprintf( fpOut, "%s\n", GetFieldAsString( iField ) ); + else + fprintf( fpOut, "(null)\n" ); + + } + } + + + if( GetStyleString() != NULL ) + { + const char* pszDisplayStyle = + CSLFetchNameValue(papszOptions, "DISPLAY_STYLE"); + if (pszDisplayStyle == NULL || CSLTestBoolean(pszDisplayStyle)) + { + fprintf( fpOut, " Style = %s\n", GetStyleString() ); + } + } + + int nGeomFieldCount = GetGeomFieldCount(); + if( nGeomFieldCount > 0 ) + { + const char* pszDisplayGeometry = + CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY"); + if ( ! (pszDisplayGeometry != NULL && EQUAL(pszDisplayGeometry, "NO") ) ) + { + for( int iField = 0; iField < nGeomFieldCount; iField++ ) + { + OGRGeomFieldDefn *poFDefn = poDefn->GetGeomFieldDefn(iField); + + if( papoGeometries[iField] != NULL ) + { + fprintf( fpOut, " " ); + if( strlen(poFDefn->GetNameRef()) > 0 && GetGeomFieldCount() > 1 ) + fprintf( fpOut, "%s = ", poFDefn->GetNameRef() ); + papoGeometries[iField]->dumpReadable( fpOut, "", papszOptions ); + } + } + } + } + + fprintf( fpOut, "\n" ); +} + +/************************************************************************/ +/* OGR_F_DumpReadable() */ +/************************************************************************/ + +/** + * \brief Dump this feature in a human readable form. + * + * This dumps the attributes, and geometry; however, it doesn't definition + * information (other than field types and names), nor does it report the + * geometry spatial reference system. + * + * This function is the same as the C++ method OGRFeature::DumpReadable(). + * + * @param hFeat handle to the feature to dump. + * @param fpOut the stream to write to, such as strout. + */ + +void OGR_F_DumpReadable( OGRFeatureH hFeat, FILE *fpOut ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_DumpReadable" ); + + ((OGRFeature *) hFeat)->DumpReadable( fpOut ); +} + +/************************************************************************/ +/* GetFID() */ +/************************************************************************/ + +/** + * \fn GIntBig OGRFeature::GetFID(); + * + * \brief Get feature identifier. + * + * This method is the same as the C function OGR_F_GetFID(). + * Note: since GDAL 2.0, this method returns a GIntBig (previously a long) + * + * @return feature id or OGRNullFID if none has been assigned. + */ + +/************************************************************************/ +/* OGR_F_GetFID() */ +/************************************************************************/ + +/** + * \brief Get feature identifier. + * + * This function is the same as the C++ method OGRFeature::GetFID(). + * Note: since GDAL 2.0, this method returns a GIntBig (previously a long) + * + * @param hFeat handle to the feature from which to get the feature + * identifier. + * @return feature id or OGRNullFID if none has been assigned. + */ + +GIntBig OGR_F_GetFID( OGRFeatureH hFeat ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetFID", 0 ); + + return ((OGRFeature *) hFeat)->GetFID(); +} + +/************************************************************************/ +/* SetFID() */ +/************************************************************************/ + +/** + * \brief Set the feature identifier. + * + * For specific types of features this operation may fail on illegal + * features ids. Generally it always succeeds. Feature ids should be + * greater than or equal to zero, with the exception of OGRNullFID (-1) + * indicating that the feature id is unknown. + * + * This method is the same as the C function OGR_F_SetFID(). + * + * @param nFID the new feature identifier value to assign. + * + * @return On success OGRERR_NONE, or on failure some other value. + */ + +OGRErr OGRFeature::SetFID( GIntBig nFID ) + +{ + this->nFID = nFID; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_F_SetFID() */ +/************************************************************************/ + +/** + * \brief Set the feature identifier. + * + * For specific types of features this operation may fail on illegal + * features ids. Generally it always succeeds. Feature ids should be + * greater than or equal to zero, with the exception of OGRNullFID (-1) + * indicating that the feature id is unknown. + * + * This function is the same as the C++ method OGRFeature::SetFID(). + * + * @param hFeat handle to the feature to set the feature id to. + * @param nFID the new feature identifier value to assign. + * + * @return On success OGRERR_NONE, or on failure some other value. + */ + +OGRErr OGR_F_SetFID( OGRFeatureH hFeat, GIntBig nFID ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_SetFID", CE_Failure ); + + return ((OGRFeature *) hFeat)->SetFID(nFID); +} + +/************************************************************************/ +/* Equal() */ +/************************************************************************/ + +/** + * \brief Test if two features are the same. + * + * Two features are considered equal if the share them (pointer equality) + * same OGRFeatureDefn, have the same field values, and the same geometry + * (as tested by OGRGeometry::Equal()) as well as the same feature id. + * + * This method is the same as the C function OGR_F_Equal(). + * + * @param poFeature the other feature to test this one against. + * + * @return TRUE if they are equal, otherwise FALSE. + */ + +OGRBoolean OGRFeature::Equal( OGRFeature * poFeature ) + +{ + if( poFeature == this ) + return TRUE; + + if( GetFID() != poFeature->GetFID() ) + return FALSE; + + if( GetDefnRef() != poFeature->GetDefnRef() ) + return FALSE; + + int i; + int nFields = GetDefnRef()->GetFieldCount(); + for(i=0; iIsFieldSet(i) ) + return FALSE; + + if( !IsFieldSet(i) ) + continue; + + switch (GetDefnRef()->GetFieldDefn(i)->GetType() ) + { + case OFTInteger: + if( GetFieldAsInteger(i) != + poFeature->GetFieldAsInteger(i) ) + return FALSE; + break; + + case OFTInteger64: + if( GetFieldAsInteger64(i) != + poFeature->GetFieldAsInteger64(i) ) + return FALSE; + break; + + case OFTReal: + if( GetFieldAsDouble(i) != + poFeature->GetFieldAsDouble(i) ) + return FALSE; + break; + + case OFTString: + if ( strcmp(GetFieldAsString(i), + poFeature->GetFieldAsString(i)) != 0 ) + return FALSE; + break; + + case OFTIntegerList: + { + int nCount1, nCount2; + const int* pnList1 = GetFieldAsIntegerList(i, &nCount1); + const int* pnList2 = + poFeature->GetFieldAsIntegerList(i, &nCount2); + if( nCount1 != nCount2 ) + return FALSE; + int j; + for(j=0;jGetFieldAsInteger64List(i, &nCount2); + if( nCount1 != nCount2 ) + return FALSE; + int j; + for(j=0;jGetFieldAsDoubleList(i, &nCount2); + if( nCount1 != nCount2 ) + return FALSE; + int j; + for(j=0;jGetFieldAsStringList(i); + nCount1 = CSLCount(papszList1); + nCount2 = CSLCount(papszList2); + if( nCount1 != nCount2 ) + return FALSE; + int j; + for(j=0;jGetFieldAsDateTime(i, &nYear2, &nMonth2, &nDay2, + &nHour2, &nMinute2, &fSecond2, &nTZFlag2); + + if( !(nYear1 == nYear2 && nMonth1 == nMonth2 && + nDay1 == nDay2 && nHour1 == nHour2 && + nMinute1 == nMinute2 && fSecond1 == fSecond2 && + nTZFlag1 == nTZFlag2) ) + return FALSE; + break; + } + + case OFTBinary: + { + int nCount1, nCount2; + GByte* pabyData1 = GetFieldAsBinary(i, &nCount1); + GByte* pabyData2 = poFeature->GetFieldAsBinary(i, &nCount2); + if( nCount1 != nCount2 ) + return FALSE; + if( memcmp(pabyData1, pabyData2, nCount1) != 0 ) + return FALSE; + break; + } + + default: + if( strcmp(GetFieldAsString(i), + poFeature->GetFieldAsString(i)) != 0 ) + return FALSE; + break; + } + } + + int nGeomFieldCount = GetGeomFieldCount(); + for( i = 0; i < nGeomFieldCount; i ++ ) + { + OGRGeometry* poThisGeom = GetGeomFieldRef(i); + OGRGeometry* poOtherGeom = poFeature->GetGeomFieldRef(i); + + if( poThisGeom == NULL && poOtherGeom != NULL ) + return FALSE; + + if( poThisGeom != NULL && poOtherGeom == NULL ) + return FALSE; + + if( poThisGeom != NULL && poOtherGeom != NULL + && (!poThisGeom->Equals( poOtherGeom ) ) ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* OGR_F_Equal() */ +/************************************************************************/ + +/** + * \brief Test if two features are the same. + * + * Two features are considered equal if the share them (handle equality) + * same OGRFeatureDefn, have the same field values, and the same geometry + * (as tested by OGR_G_Equal()) as well as the same feature id. + * + * This function is the same as the C++ method OGRFeature::Equal(). + * + * @param hFeat handle to one of the feature. + * @param hOtherFeat handle to the other feature to test this one against. + * + * @return TRUE if they are equal, otherwise FALSE. + */ + +int OGR_F_Equal( OGRFeatureH hFeat, OGRFeatureH hOtherFeat ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_Equal", 0 ); + VALIDATE_POINTER1( hOtherFeat, "OGR_F_Equal", 0 ); + + return ((OGRFeature *) hFeat)->Equal( (OGRFeature *) hOtherFeat ); +} + + +/************************************************************************/ +/* SetFrom() */ +/************************************************************************/ + +/** + * \brief Set one feature from another. + * + * Overwrite the contents of this feature from the geometry and attributes + * of another. The poSrcFeature does not need to have the same + * OGRFeatureDefn. Field values are copied by corresponding field names. + * Field types do not have to exactly match. SetField() method conversion + * rules will be applied as needed. + * + * This method is the same as the C function OGR_F_SetFrom(). + * + * @param poSrcFeature the feature from which geometry, and field values will + * be copied. + * + * @param bForgiving TRUE if the operation should continue despite lacking + * output fields matching some of the source fields. + * + * @return OGRERR_NONE if the operation succeeds, even if some values are + * not transferred, otherwise an error code. + */ + +OGRErr OGRFeature::SetFrom( OGRFeature * poSrcFeature, int bForgiving ) + +{ +/* -------------------------------------------------------------------- */ +/* Retrieve the field ids by name. */ +/* -------------------------------------------------------------------- */ + int iField, *panMap; + OGRErr eErr; + + panMap = (int *) VSIMalloc( sizeof(int) * poSrcFeature->GetFieldCount() ); + for( iField = 0; iField < poSrcFeature->GetFieldCount(); iField++ ) + { + panMap[iField] = GetFieldIndex( + poSrcFeature->GetFieldDefnRef(iField)->GetNameRef() ); + + if( panMap[iField] == -1 ) + { + if( bForgiving ) + continue; + else + { + VSIFree(panMap); + return OGRERR_FAILURE; + } + } + } + + eErr = SetFrom( poSrcFeature, panMap, bForgiving ); + + VSIFree(panMap); + + return eErr; +} + +/************************************************************************/ +/* OGR_F_SetFrom() */ +/************************************************************************/ + +/** + * \brief Set one feature from another. + * + * Overwrite the contents of this feature from the geometry and attributes + * of another. The hOtherFeature does not need to have the same + * OGRFeatureDefn. Field values are copied by corresponding field names. + * Field types do not have to exactly match. OGR_F_SetField*() function + * conversion rules will be applied as needed. + * + * This function is the same as the C++ method OGRFeature::SetFrom(). + * + * @param hFeat handle to the feature to set to. + * @param hOtherFeat handle to the feature from which geometry, + * and field values will be copied. + * + * @param bForgiving TRUE if the operation should continue despite lacking + * output fields matching some of the source fields. + * + * @return OGRERR_NONE if the operation succeeds, even if some values are + * not transferred, otherwise an error code. + */ + +OGRErr OGR_F_SetFrom( OGRFeatureH hFeat, OGRFeatureH hOtherFeat, + int bForgiving ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_SetFrom", CE_Failure ); + VALIDATE_POINTER1( hOtherFeat, "OGR_F_SetFrom", CE_Failure ); + + return ((OGRFeature *) hFeat)->SetFrom( (OGRFeature *) hOtherFeat, + bForgiving ); +} + +/************************************************************************/ +/* SetFrom() */ +/************************************************************************/ + +/** + * \brief Set one feature from another. + * + * Overwrite the contents of this feature from the geometry and attributes + * of another. The poSrcFeature does not need to have the same + * OGRFeatureDefn. Field values are copied according to the provided indices + * map. Field types do not have to exactly match. SetField() method + * conversion rules will be applied as needed. This is more efficient than + * OGR_F_SetFrom() in that this doesn't lookup the fields by their names. + * Particularly useful when the field names don't match. + * + * This method is the same as the C function OGR_F_SetFromWithMap(). + * + * @param poSrcFeature the feature from which geometry, and field values will + * be copied. + * + * @param panMap Array of the indices of the feature's fields + * stored at the corresponding index of the source feature's fields. A value of + * -1 should be used to ignore the source's field. The array should not be NULL + * and be as long as the number of fields in the source feature. + * + * @param bForgiving TRUE if the operation should continue despite lacking + * output fields matching some of the source fields. + * + * @return OGRERR_NONE if the operation succeeds, even if some values are + * not transferred, otherwise an error code. + */ + +OGRErr OGRFeature::SetFrom( OGRFeature * poSrcFeature, int *panMap , + int bForgiving ) + +{ + OGRErr eErr; + + if( poSrcFeature == this ) + return OGRERR_FAILURE; + + SetFID( OGRNullFID ); + +/* -------------------------------------------------------------------- */ +/* Set the geometry. */ +/* -------------------------------------------------------------------- */ + if( GetGeomFieldCount() == 1 ) + { + OGRGeomFieldDefn* poGFieldDefn = GetGeomFieldDefnRef(0); + + int iSrc = poSrcFeature->GetGeomFieldIndex( + poGFieldDefn->GetNameRef()); + if( iSrc >= 0 ) + SetGeomField( 0, poSrcFeature->GetGeomFieldRef(iSrc) ); + else + /* whatever the geometry field names are. For backward compatibility */ + SetGeomField( 0, poSrcFeature->GetGeomFieldRef(0) ); + } + else + { + int i; + for(i = 0; i < GetGeomFieldCount(); i++) + { + OGRGeomFieldDefn* poGFieldDefn = GetGeomFieldDefnRef(i); + + int iSrc = poSrcFeature->GetGeomFieldIndex( + poGFieldDefn->GetNameRef()); + if( iSrc >= 0 ) + SetGeomField( i, poSrcFeature->GetGeomFieldRef(iSrc) ); + else + SetGeomField( i, NULL ); + } + } + +/* -------------------------------------------------------------------- */ +/* Copy feature style string. */ +/* -------------------------------------------------------------------- */ + SetStyleString( poSrcFeature->GetStyleString() ); + +/* -------------------------------------------------------------------- */ +/* Set the fields by name. */ +/* -------------------------------------------------------------------- */ + + eErr = SetFieldsFrom( poSrcFeature, panMap, bForgiving ); + if( eErr != OGRERR_NONE ) + return eErr; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_F_SetFromWithMap() */ +/************************************************************************/ + +/** + * \brief Set one feature from another. + * + * Overwrite the contents of this feature from the geometry and attributes + * of another. The hOtherFeature does not need to have the same + * OGRFeatureDefn. Field values are copied according to the provided indices + * map. Field types do not have to exactly match. OGR_F_SetField*() function + * conversion rules will be applied as needed. This is more efficient than + * OGR_F_SetFrom() in that this doesn't lookup the fields by their names. + * Particularly useful when the field names don't match. + * + * This function is the same as the C++ method OGRFeature::SetFrom(). + * + * @param hFeat handle to the feature to set to. + * @param hOtherFeat handle to the feature from which geometry, + * and field values will be copied. + * + * @param panMap Array of the indices of the destination feature's fields + * stored at the corresponding index of the source feature's fields. A value of + * -1 should be used to ignore the source's field. The array should not be NULL + * and be as long as the number of fields in the source feature. + * + * @param bForgiving TRUE if the operation should continue despite lacking + * output fields matching some of the source fields. + * + * @return OGRERR_NONE if the operation succeeds, even if some values are + * not transferred, otherwise an error code. + */ + +OGRErr OGR_F_SetFromWithMap( OGRFeatureH hFeat, OGRFeatureH hOtherFeat, + int bForgiving, int *panMap ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_SetFrom", CE_Failure ); + VALIDATE_POINTER1( hOtherFeat, "OGR_F_SetFrom", CE_Failure ); + VALIDATE_POINTER1( panMap, "OGR_F_SetFrom", CE_Failure); + + return ((OGRFeature *) hFeat)->SetFrom( (OGRFeature *) hOtherFeat, + panMap, bForgiving ); +} + +/************************************************************************/ +/* SetFieldsFrom() */ +/************************************************************************/ + +/** + * \brief Set fields from another feature. + * + * Overwrite the fields of this feature from the attributes of + * another. The FID and the style string are not set. The poSrcFeature + * does not need to have the same OGRFeatureDefn. Field values are + * copied according to the provided indices map. Field types do not + * have to exactly match. SetField() method conversion rules will be + * applied as needed. This is more efficient than OGR_F_SetFrom() in + * that this doesn't lookup the fields by their names. Particularly + * useful when the field names don't match. + * + * @param poSrcFeature the feature from which geometry, and field values will + * be copied. + * + * @param panMap Array of the indices of the feature's fields + * stored at the corresponding index of the source feature's fields. A value of + * -1 should be used to ignore the source's field. The array should not be NULL + * and be as long as the number of fields in the source feature. + * + * @param bForgiving TRUE if the operation should continue despite lacking + * output fields matching some of the source fields. + * + * @return OGRERR_NONE if the operation succeeds, even if some values are + * not transferred, otherwise an error code. + */ + +OGRErr OGRFeature::SetFieldsFrom( OGRFeature * poSrcFeature, int *panMap , + int bForgiving ) + +{ + int iField, iDstField; + + for( iField = 0; iField < poSrcFeature->GetFieldCount(); iField++ ) + { + iDstField = panMap[iField]; + + if( iDstField < 0 ) + continue; + + if( GetFieldCount() <= iDstField ) + return OGRERR_FAILURE; + + if( !poSrcFeature->IsFieldSet(iField) ) + { + UnsetField( iDstField ); + continue; + } + + switch( poSrcFeature->GetFieldDefnRef(iField)->GetType() ) + { + case OFTInteger: + SetField( iDstField, poSrcFeature->GetFieldAsInteger( iField ) ); + break; + + case OFTInteger64: + SetField( iDstField, poSrcFeature->GetFieldAsInteger64( iField ) ); + break; + + case OFTReal: + SetField( iDstField, poSrcFeature->GetFieldAsDouble( iField ) ); + break; + + case OFTString: + SetField( iDstField, poSrcFeature->GetFieldAsString( iField ) ); + break; + + case OFTIntegerList: + { + if (GetFieldDefnRef(iDstField)->GetType() == OFTString) + { + SetField( iDstField, poSrcFeature->GetFieldAsString(iField) ); + } + else + { + int nCount; + const int *panValues = poSrcFeature->GetFieldAsIntegerList( iField, &nCount); + SetField( iDstField, nCount, (int*) panValues ); + } + } + break; + + case OFTInteger64List: + { + if (GetFieldDefnRef(iDstField)->GetType() == OFTString) + { + SetField( iDstField, poSrcFeature->GetFieldAsString(iField) ); + } + else + { + int nCount; + const GIntBig *panValues = poSrcFeature->GetFieldAsInteger64List( iField, &nCount); + SetField( iDstField, nCount, panValues ); + } + } + break; + + case OFTRealList: + { + if (GetFieldDefnRef(iDstField)->GetType() == OFTString) + { + SetField( iDstField, poSrcFeature->GetFieldAsString(iField) ); + } + else + { + int nCount; + const double *padfValues = poSrcFeature->GetFieldAsDoubleList( iField, &nCount); + SetField( iDstField, nCount, (double*) padfValues ); + } + } + break; + + case OFTDate: + case OFTDateTime: + case OFTTime: + if (GetFieldDefnRef(iDstField)->GetType() == OFTDate || + GetFieldDefnRef(iDstField)->GetType() == OFTTime || + GetFieldDefnRef(iDstField)->GetType() == OFTDateTime) + { + SetField( iDstField, poSrcFeature->GetRawFieldRef( iField ) ); + } + else if (GetFieldDefnRef(iDstField)->GetType() == OFTString) + { + SetField( iDstField, poSrcFeature->GetFieldAsString( iField ) ); + } + else if( !bForgiving ) + return OGRERR_FAILURE; + break; + + default: + if( poSrcFeature->GetFieldDefnRef(iField)->GetType() + == GetFieldDefnRef(iDstField)->GetType() ) + { + SetField( iDstField, poSrcFeature->GetRawFieldRef(iField) ); + } + else if (GetFieldDefnRef(iDstField)->GetType() == OFTString) + { + SetField( iDstField, poSrcFeature->GetFieldAsString( iField ) ); + } + else if( !bForgiving ) + return OGRERR_FAILURE; + break; + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetStyleString() */ +/************************************************************************/ + +/** + * \brief Fetch style string for this feature. + * + * Set the OGR Feature Style Specification for details on the format of + * this string, and ogr_featurestyle.h for services available to parse it. + * + * This method is the same as the C function OGR_F_GetStyleString(). + * + * @return a reference to a representation in string format, or NULL if + * there isn't one. + */ + +const char *OGRFeature::GetStyleString() +{ + int iStyleFieldIndex; + + if (m_pszStyleString) + return m_pszStyleString; + + iStyleFieldIndex = GetFieldIndex("OGR_STYLE"); + if (iStyleFieldIndex >= 0) + return GetFieldAsString(iStyleFieldIndex); + + return NULL; +} + +/************************************************************************/ +/* OGR_F_GetStyleString() */ +/************************************************************************/ + +/** + * \brief Fetch style string for this feature. + * + * Set the OGR Feature Style Specification for details on the format of + * this string, and ogr_featurestyle.h for services available to parse it. + * + * This function is the same as the C++ method OGRFeature::GetStyleString(). + * + * @param hFeat handle to the feature to get the style from. + * @return a reference to a representation in string format, or NULL if + * there isn't one. + */ + +const char *OGR_F_GetStyleString( OGRFeatureH hFeat ) +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetStyleString", NULL ); + + return ((OGRFeature *)hFeat)->GetStyleString(); +} + +/************************************************************************/ +/* SetStyleString() */ +/************************************************************************/ + +/** + * \brief Set feature style string. + * This method operate exactly as + * OGRFeature::SetStyleStringDirectly() except that it does not assume + * ownership of the passed string, but instead makes a copy of it. + * + * This method is the same as the C function OGR_F_SetStyleString(). + * + * @param pszString the style string to apply to this feature, cannot be NULL. + */ + +void OGRFeature::SetStyleString(const char *pszString) +{ + if (m_pszStyleString) + { + CPLFree(m_pszStyleString); + m_pszStyleString = NULL; + } + + if( pszString ) + m_pszStyleString = CPLStrdup(pszString); +} + +/************************************************************************/ +/* OGR_F_SetStyleString() */ +/************************************************************************/ + +/** + * \brief Set feature style string. + * This method operate exactly as + * OGR_F_SetStyleStringDirectly() except that it does not assume ownership + * of the passed string, but instead makes a copy of it. + * + * This function is the same as the C++ method OGRFeature::SetStyleString(). + * + * @param hFeat handle to the feature to set style to. + * @param pszStyle the style string to apply to this feature, cannot be NULL. + */ + +void OGR_F_SetStyleString( OGRFeatureH hFeat, const char *pszStyle ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetStyleString" ); + + ((OGRFeature *)hFeat)->SetStyleString( pszStyle ); +} + +/************************************************************************/ +/* SetStyleStringDirectly() */ +/************************************************************************/ + +/** + * \brief Set feature style string. + * This method operate exactly as + * OGRFeature::SetStyleString() except that it assumes ownership of the passed + * string. + * + * This method is the same as the C function OGR_F_SetStyleStringDirectly(). + * + * @param pszString the style string to apply to this feature, cannot be NULL. + */ + +void OGRFeature::SetStyleStringDirectly(char *pszString) +{ + if (m_pszStyleString) + CPLFree(m_pszStyleString); + m_pszStyleString = pszString; +} + +/************************************************************************/ +/* OGR_F_SetStyleStringDirectly() */ +/************************************************************************/ + +/** + * \brief Set feature style string. + * This method operate exactly as + * OGR_F_SetStyleString() except that it assumes ownership of the passed + * string. + * + * This function is the same as the C++ method + * OGRFeature::SetStyleStringDirectly(). + * + * @param hFeat handle to the feature to set style to. + * @param pszStyle the style string to apply to this feature, cannot be NULL. + */ + +void OGR_F_SetStyleStringDirectly( OGRFeatureH hFeat, char *pszStyle ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetStyleStringDirectly" ); + + ((OGRFeature *)hFeat)->SetStyleStringDirectly( pszStyle ); +} + +//************************************************************************/ +/* SetStyleTable() */ +/************************************************************************/ +void OGRFeature::SetStyleTable(OGRStyleTable *poStyleTable) +{ + if ( m_poStyleTable ) + delete m_poStyleTable; + m_poStyleTable = ( poStyleTable ) ? poStyleTable->Clone() : NULL; +} + +//************************************************************************/ +/* SetStyleTableDirectly() */ +/************************************************************************/ + +void OGRFeature::SetStyleTableDirectly(OGRStyleTable *poStyleTable) +{ + if ( m_poStyleTable ) + delete m_poStyleTable; + m_poStyleTable = poStyleTable; +} + +/************************************************************************/ +/* RemapFields() */ +/* */ +/* This is used to transform a feature "in place" from one */ +/* feature defn to another with minimum work. */ +/************************************************************************/ + +OGRErr OGRFeature::RemapFields( OGRFeatureDefn *poNewDefn, + int *panRemapSource ) + +{ + int iDstField; + OGRField *pauNewFields; + + if( poNewDefn == NULL ) + poNewDefn = poDefn; + + pauNewFields = (OGRField *) CPLCalloc( poNewDefn->GetFieldCount(), + sizeof(OGRField) ); + + for( iDstField = 0; iDstField < poDefn->GetFieldCount(); iDstField++ ) + { + if( panRemapSource[iDstField] == -1 ) + { + pauNewFields[iDstField].Set.nMarker1 = OGRUnsetMarker; + pauNewFields[iDstField].Set.nMarker2 = OGRUnsetMarker; + } + else + { + memcpy( pauNewFields + iDstField, + pauFields + panRemapSource[iDstField], + sizeof(OGRField) ); + } + } + + /* + ** We really should be freeing memory for old columns that + ** are no longer present. We don't for now because it is a bit messy + ** and would take too long to test. + */ + +/* -------------------------------------------------------------------- */ +/* Apply new definition and fields. */ +/* -------------------------------------------------------------------- */ + CPLFree( pauFields ); + pauFields = pauNewFields; + + poDefn = poNewDefn; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* RemapGeomFields() */ +/* */ +/* This is used to transform a feature "in place" from one */ +/* feature defn to another with minimum work. */ +/************************************************************************/ + +OGRErr OGRFeature::RemapGeomFields( OGRFeatureDefn *poNewDefn, + int *panRemapSource ) + +{ + int iDstField; + OGRGeometry** papoNewGeomFields; + + if( poNewDefn == NULL ) + poNewDefn = poDefn; + + papoNewGeomFields = (OGRGeometry **) CPLCalloc( poNewDefn->GetGeomFieldCount(), + sizeof(OGRGeometry*) ); + + for( iDstField = 0; iDstField < poDefn->GetGeomFieldCount(); iDstField++ ) + { + if( panRemapSource[iDstField] == -1 ) + { + papoNewGeomFields[iDstField] = NULL; + } + else + { + papoNewGeomFields[iDstField] = + papoGeometries[panRemapSource[iDstField]]; + } + } + + /* + ** We really should be freeing memory for old columns that + ** are no longer present. We don't for now because it is a bit messy + ** and would take too long to test. + */ + +/* -------------------------------------------------------------------- */ +/* Apply new definition and fields. */ +/* -------------------------------------------------------------------- */ + CPLFree( papoGeometries ); + papoGeometries = papoNewGeomFields; + + poDefn = poNewDefn; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_F_GetStyleTable() */ +/************************************************************************/ + +OGRStyleTableH OGR_F_GetStyleTable( OGRFeatureH hFeat ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_GetStyleTable", NULL ); + + return (OGRStyleTableH) ((OGRFeature *) hFeat)->GetStyleTable( ); +} + +/************************************************************************/ +/* OGR_F_SetStyleTableDirectly() */ +/************************************************************************/ + +void OGR_F_SetStyleTableDirectly( OGRFeatureH hFeat, + OGRStyleTableH hStyleTable ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetStyleTableDirectly" ); + + ((OGRFeature *) hFeat)->SetStyleTableDirectly( (OGRStyleTable *) hStyleTable); +} + +/************************************************************************/ +/* OGR_F_SetStyleTable() */ +/************************************************************************/ + +void OGR_F_SetStyleTable( OGRFeatureH hFeat, + OGRStyleTableH hStyleTable ) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_SetStyleTable" ); + VALIDATE_POINTER0( hStyleTable, "OGR_F_SetStyleTable" ); + + ((OGRFeature *) hFeat)->SetStyleTable( (OGRStyleTable *) hStyleTable); +} + +/************************************************************************/ +/* FillUnsetWithDefault() */ +/************************************************************************/ + +/** + * \brief Fill unset fields with default values that might be defined. + * + * This method is the same as the C function OGR_F_FillUnsetWithDefault(). + * + * @param bNotNullableOnly if we should fill only unset fields with a not-null + * constraint. + * @param papszOptions unused currently. Must be set to NULL. + * @since GDAL 2.0 + */ + +void OGRFeature::FillUnsetWithDefault(int bNotNullableOnly, + CPL_UNUSED char** papszOptions) +{ + int nFieldCount = poDefn->GetFieldCount(); + for(int i = 0; i < nFieldCount; i ++ ) + { + if( IsFieldSet(i) ) + continue; + if( bNotNullableOnly && poDefn->GetFieldDefn(i)->IsNullable() ) + continue; + const char* pszDefault = poDefn->GetFieldDefn(i)->GetDefault(); + OGRFieldType eType = poDefn->GetFieldDefn(i)->GetType(); + if( pszDefault != NULL ) + { + if( eType == OFTDate || eType == OFTTime || eType == OFTDateTime ) + { + if( EQUALN(pszDefault, "CURRENT", strlen("CURRENT")) ) + { + time_t t = time(NULL); + struct tm brokendown; + CPLUnixTimeToYMDHMS(t, &brokendown); + SetField(i, brokendown.tm_year + 1900, + brokendown.tm_mon + 1, + brokendown.tm_mday, + brokendown.tm_hour, + brokendown.tm_min, + brokendown.tm_sec, + 100 ); + } + else + { + int nYear, nMonth, nDay, nHour, nMinute; + float fSecond; + if( sscanf(pszDefault, "'%d/%d/%d %d:%d:%f'", + &nYear, &nMonth, &nDay, + &nHour, &nMinute, &fSecond) == 6 ) + { + SetField(i, nYear, nMonth, nDay, nHour, nMinute, + fSecond, 100 ); + } + } + } + else if( eType == OFTString && + pszDefault[0] == '\'' && + pszDefault[strlen(pszDefault)-1] == '\'' ) + { + CPLString osDefault(pszDefault + 1); + osDefault.resize(osDefault.size()-1); + char* pszTmp = CPLUnescapeString(osDefault, NULL, CPLES_SQL); + SetField(i, pszTmp); + CPLFree(pszTmp); + } + else + SetField(i, pszDefault); + } + } +} + +/************************************************************************/ +/* OGR_F_FillUnsetWithDefault() */ +/************************************************************************/ + +/** + * \brief Fill unset fields with default values that might be defined. + * + * This function is the same as the C++ method OGRFeature::FillUnsetWithDefault(). + * + * @param hFeat handle to the feature. + * @param bNotNullableOnly if we should fill only unset fields with a not-null + * constraint. + * @param papszOptions unused currently. Must be set to NULL. + * @since GDAL 2.0 + */ + +void OGR_F_FillUnsetWithDefault( OGRFeatureH hFeat, + int bNotNullableOnly, + char** papszOptions) + +{ + VALIDATE_POINTER0( hFeat, "OGR_F_FillUnsetWithDefault" ); + + ((OGRFeature *) hFeat)->FillUnsetWithDefault( bNotNullableOnly, papszOptions ); +} + +/************************************************************************/ +/* Validate() */ +/************************************************************************/ + +/** + * \brief Validate that a feature meets constraints of its schema. + * + * The scope of test is specified with the nValidateFlags parameter. + * + * Regarding OGR_F_VAL_WIDTH, the test is done assuming the string width must + * be interpreted as the number of UTF-8 characters. Some drivers might interpret + * the width as the number of bytes instead. So this test is rather conservative + * (if it fails, then it will fail for all interpretations). + * + * This method is the same as the C function OGR_F_Validate(). + * + * @param nValidateFlags OGR_F_VAL_ALL or combination of OGR_F_VAL_NULL, + * OGR_F_VAL_GEOM_TYPE, OGR_F_VAL_WIDTH and OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT + * with '|' operator + * @param bEmitError TRUE if a CPLError() must be emitted when a check fails + * @return TRUE if all enabled validation tests pass. + * @since GDAL 2.0 + */ + +int OGRFeature::Validate( int nValidateFlags, int bEmitError ) + +{ + int bRet = TRUE; + + int i; + int nGeomFieldCount = poDefn->GetGeomFieldCount(); + for(i = 0; i < nGeomFieldCount; i ++ ) + { + if( (nValidateFlags & OGR_F_VAL_NULL) && + !poDefn->GetGeomFieldDefn(i)->IsNullable() && + GetGeomFieldRef(i) == NULL ) + { + bRet = FALSE; + if( bEmitError ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Geometry field %s has a NULL content which is not allowed", + poDefn->GetGeomFieldDefn(i)->GetNameRef()); + } + } + if( (nValidateFlags & OGR_F_VAL_GEOM_TYPE) && + poDefn->GetGeomFieldDefn(i)->GetType() != wkbUnknown ) + { + OGRGeometry* poGeom = GetGeomFieldRef(i); + if( poGeom != NULL ) + { + OGRwkbGeometryType eType = poDefn->GetGeomFieldDefn(i)->GetType(); + OGRwkbGeometryType eFType = poGeom->getGeometryType(); + if( (eType == wkbSetZ(wkbUnknown) && !wkbHasZ(eFType)) || + (eType != wkbSetZ(wkbUnknown) && eFType != eType) ) + { + bRet = FALSE; + if( bEmitError ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Geometry field %s has a %s geometry whereas %s is expected", + poDefn->GetGeomFieldDefn(i)->GetNameRef(), + OGRGeometryTypeToName(eFType), + OGRGeometryTypeToName(eType)); + } + } + } + } + } + int nFieldCount = poDefn->GetFieldCount(); + for(i = 0; i < nFieldCount; i ++ ) + { + if( (nValidateFlags & OGR_F_VAL_NULL) && + !poDefn->GetFieldDefn(i)->IsNullable() && + !IsFieldSet(i) && + (!(nValidateFlags & OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT) || + poDefn->GetFieldDefn(i)->GetDefault() == NULL)) + { + bRet = FALSE; + if( bEmitError ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Field %s has a NULL content which is not allowed", + poDefn->GetFieldDefn(i)->GetNameRef()); + } + } + if( (nValidateFlags & OGR_F_VAL_WIDTH) && + poDefn->GetFieldDefn(i)->GetWidth() > 0 && + poDefn->GetFieldDefn(i)->GetType() == OFTString && + IsFieldSet(i) && + CPLIsUTF8(GetFieldAsString(i), -1) && + CPLStrlenUTF8(GetFieldAsString(i)) > poDefn->GetFieldDefn(i)->GetWidth()) + { + bRet = FALSE; + if( bEmitError ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Field %s has a %d UTF-8 characters whereas a maximum of %d is allowed", + poDefn->GetFieldDefn(i)->GetNameRef(), + CPLStrlenUTF8(GetFieldAsString(i)), + poDefn->GetFieldDefn(i)->GetWidth()); + } + } + } + + return bRet; +} + +/************************************************************************/ +/* OGR_F_Validate() */ +/************************************************************************/ + +/** + * \brief Validate that a feature meets constraints of its schema. + * + * The scope of test is specified with the nValidateFlags parameter. + * + * Regarding OGR_F_VAL_WIDTH, the test is done assuming the string width must + * be interpreted as the number of UTF-8 characters. Some drivers might interpret + * the width as the number of bytes instead. So this test is rather conservative + * (if it fails, then it will fail for all interpretations). + * + * This function is the same as the C++ method + * OGRFeature::Validate(). + * + * @param hFeat handle to the feature to validate. + * @param nValidateFlags OGR_F_VAL_ALL or combination of OGR_F_VAL_NULL, + * OGR_F_VAL_GEOM_TYPE, OGR_F_VAL_WIDTH and OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT + * with '|' operator + * @param bEmitError TRUE if a CPLError() must be emitted when a check fails + * @return TRUE if all enabled validation tests pass. + * @since GDAL 2.0 + */ + +int OGR_F_Validate( OGRFeatureH hFeat, int nValidateFlags, int bEmitError ) + +{ + VALIDATE_POINTER1( hFeat, "OGR_F_Validate", FALSE ); + + return ((OGRFeature *) hFeat)->Validate( nValidateFlags, bEmitError ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrfeaturedefn.cpp b/bazaar/plugin/gdal/ogr/ogrfeaturedefn.cpp new file mode 100644 index 000000000..d690d4b92 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrfeaturedefn.cpp @@ -0,0 +1,1397 @@ +/****************************************************************************** + * $Id: ogrfeaturedefn.cpp 28807 2015-03-28 14:46:31Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRFeatureDefn class implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_feature.h" +#include "ogr_api.h" +#include "ogr_p.h" +#include "ograpispy.h" + +CPL_CVSID("$Id: ogrfeaturedefn.cpp 28807 2015-03-28 14:46:31Z rouault $"); + +/************************************************************************/ +/* OGRFeatureDefn() */ +/************************************************************************/ + +/** + * \brief Constructor. + * + * The OGRFeatureDefn maintains a reference count, but this starts at + * zero. It is mainly intended to represent a count of OGRFeature's + * based on this definition. + * + * This method is the same as the C function OGR_FD_Create(). + * + * @param pszName the name to be assigned to this layer/class. It does not + * need to be unique. + */ + +OGRFeatureDefn::OGRFeatureDefn( const char * pszName ) + +{ + pszFeatureClassName = CPLStrdup( pszName ); + nRefCount = 0; + nFieldCount = 0; + papoFieldDefn = NULL; + nGeomFieldCount = 1; + papoGeomFieldDefn = (OGRGeomFieldDefn**) CPLMalloc(sizeof(OGRGeomFieldDefn*)); + papoGeomFieldDefn[0] = new OGRGeomFieldDefn("", wkbUnknown); + bIgnoreStyle = FALSE; +} + +/************************************************************************/ +/* OGR_FD_Create() */ +/************************************************************************/ +/** + * \brief Create a new feature definition object to hold the field definitions. + * + * The OGRFeatureDefn maintains a reference count, but this starts at + * zero, and should normally be incremented by the owner. + * + * This function is the same as the C++ method + * OGRFeatureDefn::OGRFeatureDefn(). + * + * @param pszName the name to be assigned to this layer/class. It does not + * need to be unique. + * @return handle to the newly created feature definition. + */ + +OGRFeatureDefnH OGR_FD_Create( const char *pszName ) + +{ + return (OGRFeatureDefnH) new OGRFeatureDefn( pszName ); +} + + +/************************************************************************/ +/* ~OGRFeatureDefn() */ +/************************************************************************/ + +OGRFeatureDefn::~OGRFeatureDefn() + +{ + if( nRefCount != 0 ) + { + CPLDebug( "OGRFeatureDefn", + "OGRFeatureDefn %s with a ref count of %d deleted!\n", + pszFeatureClassName, nRefCount ); + } + + CPLFree( pszFeatureClassName ); + + for( int i = 0; i < nFieldCount; i++ ) + { + delete papoFieldDefn[i]; + } + + CPLFree( papoFieldDefn ); + + for( int i = 0; i < nGeomFieldCount; i++ ) + { + delete papoGeomFieldDefn[i]; + } + + CPLFree( papoGeomFieldDefn ); +} + +/************************************************************************/ +/* OGR_FD_Destroy() */ +/************************************************************************/ +/** + * \brief Destroy a feature definition object and release all memory associated with it. + * + * This function is the same as the C++ method + * OGRFeatureDefn::~OGRFeatureDefn(). + * + * @param hDefn handle to the feature definition to be destroyed. + */ + +void OGR_FD_Destroy( OGRFeatureDefnH hDefn ) + +{ + delete (OGRFeatureDefn *) hDefn; +} + +/************************************************************************/ +/* Release() */ +/************************************************************************/ + +/** + * \fn void OGRFeatureDefn::Release(); + * + * \brief Drop a reference to this object, and destroy if no longer referenced. + */ + +void OGRFeatureDefn::Release() + +{ + CPLAssert( NULL != this ); + + if( Dereference() <= 0 ) + delete this; +} + +/************************************************************************/ +/* OGR_FD_Release() */ +/************************************************************************/ + +/** + * \brief Drop a reference, and destroy if unreferenced. + * + * This function is the same as the C++ method OGRFeatureDefn::Release(). + * + * @param hDefn handle to the feature definition to be released. + */ + +void OGR_FD_Release( OGRFeatureDefnH hDefn ) + +{ + ((OGRFeatureDefn *) hDefn)->Release(); +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +/** + * \fn OGRFeatureDefn *OGRFeatureDefn::Clone(); + * + * \brief Create a copy of this feature definition. + * + * Creates a deep copy of the feature definition. + * + * @return the copy. + */ + +OGRFeatureDefn *OGRFeatureDefn::Clone() + +{ + int i; + OGRFeatureDefn *poCopy; + + poCopy = new OGRFeatureDefn( GetName() ); + + GetFieldCount(); + for( i = 0; i < nFieldCount; i++ ) + poCopy->AddFieldDefn( GetFieldDefn( i ) ); + + /* There is a default geometry field created at OGRFeatureDefn instanciation */ + poCopy->DeleteGeomFieldDefn(0); + GetGeomFieldCount(); + for( i = 0; i < nGeomFieldCount; i++ ) + poCopy->AddGeomFieldDefn( GetGeomFieldDefn( i ) ); + + return poCopy; +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +/** + * \fn const char *OGRFeatureDefn::GetName(); + * + * \brief Get name of this OGRFeatureDefn. + * + * This method is the same as the C function OGR_FD_GetName(). + * + * @return the name. This name is internal and should not be modified, or + * freed. + */ +const char * OGRFeatureDefn::GetName() +{ + return pszFeatureClassName; +} + +/************************************************************************/ +/* OGR_FD_GetName() */ +/************************************************************************/ +/** + * \brief Get name of the OGRFeatureDefn passed as an argument. + * + * This function is the same as the C++ method OGRFeatureDefn::GetName(). + * + * @param hDefn handle to the feature definition to get the name from. + * @return the name. This name is internal and should not be modified, or + * freed. + */ + +const char *OGR_FD_GetName( OGRFeatureDefnH hDefn ) + +{ + return ((OGRFeatureDefn *) hDefn)->GetName(); +} + +/************************************************************************/ +/* GetFieldCount() */ +/************************************************************************/ + +/** + * \fn int OGRFeatureDefn::GetFieldCount(); + * + * \brief Fetch number of fields on this feature. + * + * This method is the same as the C function OGR_FD_GetFieldCount(). + * @return count of fields. + */ + +int OGRFeatureDefn::GetFieldCount() +{ + return nFieldCount; +} + +/************************************************************************/ +/* OGR_FD_GetFieldCount() */ +/************************************************************************/ + +/** + * \brief Fetch number of fields on the passed feature definition. + * + * This function is the same as the C++ OGRFeatureDefn::GetFieldCount(). + * + * @param hDefn handle to the feature definition to get the fields count from. + * @return count of fields. + */ + +int OGR_FD_GetFieldCount( OGRFeatureDefnH hDefn ) + +{ +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_FD_GetFieldCount(hDefn); +#endif + + return ((OGRFeatureDefn *) hDefn)->GetFieldCount(); +} + +/************************************************************************/ +/* GetFieldDefn() */ +/************************************************************************/ + +/** + * \brief Fetch field definition. + * + * This method is the same as the C function OGR_FD_GetFieldDefn(). + * + * Starting with GDAL 1.7.0, this method will also issue an error if the index + * is not valid. + * + * @param iField the field to fetch, between 0 and GetFieldCount()-1. + * + * @return a pointer to an internal field definition object or NULL if invalid index. + * This object should not be modified or freed by the application. + */ + +OGRFieldDefn *OGRFeatureDefn::GetFieldDefn( int iField ) + +{ + if( iField < 0 || iField >= GetFieldCount() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid index : %d", iField); + return NULL; + } + + return papoFieldDefn[iField]; +} + +/************************************************************************/ +/* OGR_FD_GetFieldDefn() */ +/************************************************************************/ + +/** + * \brief Fetch field definition of the passed feature definition. + * + * This function is the same as the C++ method + * OGRFeatureDefn::GetFieldDefn(). + * + * Starting with GDAL 1.7.0, this method will also issue an error if the index + * is not valid. + * + * @param hDefn handle to the feature definition to get the field definition + * from. + * @param iField the field to fetch, between 0 and GetFieldCount()-1. + * + * @return an handle to an internal field definition object or NULL if invalid index. + * This object should not be modified or freed by the application. + */ + +OGRFieldDefnH OGR_FD_GetFieldDefn( OGRFeatureDefnH hDefn, int iField ) + +{ + OGRFieldDefnH hFieldDefnH = (OGRFieldDefnH) ((OGRFeatureDefn *) hDefn)->GetFieldDefn( iField ); +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_FD_GetFieldDefn(hDefn, iField, hFieldDefnH); +#endif + + return hFieldDefnH; +} + +/************************************************************************/ +/* AddFieldDefn() */ +/************************************************************************/ + +/** + * \brief Add a new field definition. + * + * To add a new field definition to a layer definition, do not use this + * function directly, but use OGRLayer::CreateField() instead. + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. The OGRFieldDefn + * passed in is copied, and remains the responsibility of the caller. + * + * This method is the same as the C function OGR_FD_AddFieldDefn(). + * + * @param poNewDefn the definition of the new field. + */ + +void OGRFeatureDefn::AddFieldDefn( OGRFieldDefn * poNewDefn ) + +{ + GetFieldCount(); + papoFieldDefn = (OGRFieldDefn **) + CPLRealloc( papoFieldDefn, sizeof(void*)*(nFieldCount+1) ); + + papoFieldDefn[nFieldCount] = new OGRFieldDefn( poNewDefn ); + nFieldCount++; +} + +/************************************************************************/ +/* OGR_FD_AddFieldDefn() */ +/************************************************************************/ + +/** + * \brief Add a new field definition to the passed feature definition. + * + * To add a new field definition to a layer definition, do not use this + * function directly, but use OGR_L_CreateField() instead. + * + * This function should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. The OGRFieldDefn + * passed in is copied, and remains the responsibility of the caller. + * + * This function is the same as the C++ method OGRFeatureDefn::AddFieldDefn(). + * + * @param hDefn handle to the feature definition to add the field definition + * to. + * @param hNewField handle to the new field definition. + */ + +void OGR_FD_AddFieldDefn( OGRFeatureDefnH hDefn, OGRFieldDefnH hNewField ) + +{ + ((OGRFeatureDefn *) hDefn)->AddFieldDefn( (OGRFieldDefn *) hNewField ); +} + +/************************************************************************/ +/* DeleteFieldDefn() */ +/************************************************************************/ + +/** + * \brief Delete an existing field definition. + * + * To delete an existing field definition from a layer definition, do not use this + * function directly, but use OGRLayer::DeleteField() instead. + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. + * + * This method is the same as the C function OGR_FD_DeleteFieldDefn(). + * + * @param iField the index of the field defintion. + * @return OGRERR_NONE in case of success. + * @since OGR 1.9.0 + */ + +OGRErr OGRFeatureDefn::DeleteFieldDefn( int iField ) + +{ + if (iField < 0 || iField >= GetFieldCount()) + return OGRERR_FAILURE; + + delete papoFieldDefn[iField]; + papoFieldDefn[iField] = NULL; + + if (iField < nFieldCount - 1) + { + memmove(papoFieldDefn + iField, + papoFieldDefn + iField + 1, + (nFieldCount - 1 - iField) * sizeof(void*)); + } + + nFieldCount--; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_FD_DeleteFieldDefn() */ +/************************************************************************/ + +/** + * \brief Delete an existing field definition. + * + * To delete an existing field definition from a layer definition, do not use this + * function directly, but use OGR_L_DeleteField() instead. + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. + * + * This method is the same as the C++ method OGRFeatureDefn::DeleteFieldDefn(). + * + * @param hDefn handle to the feature definition. + * @param iField the index of the field defintion. + * @return OGRERR_NONE in case of success. + * @since OGR 1.9.0 + */ + +OGRErr OGR_FD_DeleteFieldDefn( OGRFeatureDefnH hDefn, int iField ) + +{ + return ((OGRFeatureDefn *) hDefn)->DeleteFieldDefn( iField ); +} + +/************************************************************************/ +/* ReorderFieldDefns() */ +/************************************************************************/ + +/** + * \brief Reorder the field definitions in the array of the feature definition + * + * To reorder the field definitions in a layer definition, do not use this + * function directly, but use OGR_L_ReorderFields() instead. + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. + * + * This method is the same as the C function OGR_FD_ReorderFieldDefns(). + * + * @param panMap an array of GetFieldCount() elements which + * is a permutation of [0, GetFieldCount()-1]. panMap is such that, + * for each field definition at position i after reordering, + * its position before reordering was panMap[i]. + * @return OGRERR_NONE in case of success. + * @since OGR 1.9.0 + */ + +OGRErr OGRFeatureDefn::ReorderFieldDefns( int* panMap ) + +{ + if (GetFieldCount() == 0) + return OGRERR_NONE; + + OGRErr eErr = OGRCheckPermutation(panMap, nFieldCount); + if (eErr != OGRERR_NONE) + return eErr; + + OGRFieldDefn** papoFieldDefnNew = (OGRFieldDefn**) + CPLMalloc(sizeof(OGRFieldDefn*) * nFieldCount); + + for(int i=0;iReorderFieldDefns( panMap ); +} + + +/************************************************************************/ +/* GetGeomFieldCount() */ +/************************************************************************/ + +/** + * \fn int OGRFeatureDefn::GetGeomFieldCount(); + * + * \brief Fetch number of geometry fields on this feature. + * + * This method is the same as the C function OGR_FD_GetGeomFieldCount(). + * @return count of geometry fields. + * + * @since GDAL 1.11 + */ +int OGRFeatureDefn::GetGeomFieldCount() +{ + return nGeomFieldCount; +} + +/************************************************************************/ +/* OGR_FD_GetGeomFieldCount() */ +/************************************************************************/ + +/** + * \brief Fetch number of geometry fields on the passed feature definition. + * + * This function is the same as the C++ OGRFeatureDefn::GetGeomFieldCount(). + * + * @param hDefn handle to the feature definition to get the fields count from. + * @return count of geometry fields. + * + * @since GDAL 1.11 + */ + +int OGR_FD_GetGeomFieldCount( OGRFeatureDefnH hDefn ) + +{ +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_FD_GetGeomFieldCount(hDefn); +#endif + + return ((OGRFeatureDefn *) hDefn)->GetGeomFieldCount(); +} + +/************************************************************************/ +/* GetGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Fetch geometry field definition. + * + * This method is the same as the C function OGR_FD_GetGeomFieldDefn(). + * + * @param iGeomField the geometry field to fetch, between 0 and GetGeomFieldCount()-1. + * + * @return a pointer to an internal field definition object or NULL if invalid index. + * This object should not be modified or freed by the application. + * + * @since GDAL 1.11 + */ + +OGRGeomFieldDefn *OGRFeatureDefn::GetGeomFieldDefn( int iGeomField ) + +{ + if( iGeomField < 0 || iGeomField >= GetGeomFieldCount() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid index : %d", iGeomField); + return NULL; + } + + return papoGeomFieldDefn[iGeomField]; +} + +/************************************************************************/ +/* OGR_FD_GetGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Fetch geometry field definition of the passed feature definition. + * + * This function is the same as the C++ method + * OGRFeatureDefn::GetGeomFieldDefn(). + * + * @param hDefn handle to the feature definition to get the field definition + * from. + * @param iGeomField the geometry field to fetch, between 0 and GetGeomFieldCount()-1. + * + * @return an handle to an internal field definition object or NULL if invalid index. + * This object should not be modified or freed by the application. + * + * @since GDAL 1.11 + */ + +OGRGeomFieldDefnH OGR_FD_GetGeomFieldDefn( OGRFeatureDefnH hDefn, int iGeomField ) + +{ + OGRGeomFieldDefnH hGeomField = + (OGRGeomFieldDefnH) ((OGRFeatureDefn *) hDefn)->GetGeomFieldDefn( iGeomField ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_FD_GetGeomFieldDefn(hDefn, iGeomField, hGeomField); +#endif + + return hGeomField; +} + +/************************************************************************/ +/* AddGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Add a new geometry field definition. + * + * To add a new geometry field definition to a layer definition, do not use this + * function directly, but use OGRLayer::CreateGeomField() instead. + * + * This method does an internal copy of the passed geometry field definition, + * unless bCopy is set to FALSE (in which case it takes ownership of the + * field definition. + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. The OGRGeomFieldDefn + * passed in is copied, and remains the responsibility of the caller. + * + * This method is the same as the C function OGR_FD_AddGeomFieldDefn(). + * + * @param poNewDefn the definition of the new geometry field. + * @param bCopy whether poNewDefn should be copied. + * + * @since GDAL 1.11 + */ + +void OGRFeatureDefn::AddGeomFieldDefn( OGRGeomFieldDefn * poNewDefn, + int bCopy ) +{ + GetGeomFieldCount(); + papoGeomFieldDefn = (OGRGeomFieldDefn **) + CPLRealloc( papoGeomFieldDefn, sizeof(void*)*(nGeomFieldCount+1) ); + + papoGeomFieldDefn[nGeomFieldCount] = (bCopy) ? + new OGRGeomFieldDefn( poNewDefn ) : poNewDefn; + nGeomFieldCount++; +} + +/************************************************************************/ +/* OGR_FD_AddGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Add a new field definition to the passed feature definition. + * + * To add a new field definition to a layer definition, do not use this + * function directly, but use OGR_L_CreateGeomField() instead. + * + * This function should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. The OGRGeomFieldDefn + * passed in is copied, and remains the responsibility of the caller. + * + * This function is the same as the C++ method OGRFeatureDefn::AddGeomFieldDefn(). + * + * @param hDefn handle to the feature definition to add the geometry field definition + * to. + * @param hNewGeomField handle to the new field definition. + * + * @since GDAL 1.11 + */ + +void OGR_FD_AddGeomFieldDefn( OGRFeatureDefnH hDefn, OGRGeomFieldDefnH hNewGeomField ) + +{ + ((OGRFeatureDefn *) hDefn)->AddGeomFieldDefn( (OGRGeomFieldDefn *) hNewGeomField ); +} + +/************************************************************************/ +/* DeleteGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Delete an existing geometry field definition. + * + * To delete an existing field definition from a layer definition, do not use this + * function directly, but use OGRLayer::DeleteGeomField() instead. + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. + * + * This method is the same as the C function OGR_FD_DeleteGeomFieldDefn(). + * + * @param iGeomField the index of the geometry field defintion. + * @return OGRERR_NONE in case of success. + * + * @since GDAL 1.11 + */ + +OGRErr OGRFeatureDefn::DeleteGeomFieldDefn( int iGeomField ) + +{ + if (iGeomField < 0 || iGeomField >= GetGeomFieldCount()) + return OGRERR_FAILURE; + + delete papoGeomFieldDefn[iGeomField]; + papoGeomFieldDefn[iGeomField] = NULL; + + if (iGeomField < nGeomFieldCount - 1) + { + memmove(papoGeomFieldDefn + iGeomField, + papoGeomFieldDefn + iGeomField + 1, + (nGeomFieldCount - 1 - iGeomField) * sizeof(void*)); + } + + nGeomFieldCount--; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_FD_DeleteGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Delete an existing geometry field definition. + * + * To delete an existing geometry field definition from a layer definition, do not use this + * function directly, but use OGR_L_DeleteGeomField() instead (*not implemented yet*) + * + * This method should only be called while there are no OGRFeature + * objects in existance based on this OGRFeatureDefn. + * + * This method is the same as the C++ method OGRFeatureDefn::DeleteGeomFieldDefn(). + * + * @param hDefn handle to the feature definition. + * @param iGeomField the index of the geometry field defintion. + * @return OGRERR_NONE in case of success. + * + * @since GDAL 1.11 + */ + +OGRErr OGR_FD_DeleteGeomFieldDefn( OGRFeatureDefnH hDefn, int iGeomField ) + +{ + return ((OGRFeatureDefn *) hDefn)->DeleteGeomFieldDefn( iGeomField ); +} + + +/************************************************************************/ +/* GetGeomFieldIndex() */ +/************************************************************************/ + +/** + * \brief Find geometry field by name. + * + * The geometry field index of the first geometry field matching the passed + * field name (case insensitively) is returned. + * + * This method is the same as the C function OGR_FD_GetGeomFieldIndex(). + * + * @param pszGeomFieldName the geometry field name to search for. + * + * @return the geometry field index, or -1 if no match found. + */ + + +int OGRFeatureDefn::GetGeomFieldIndex( const char * pszGeomFieldName ) + +{ + GetGeomFieldCount(); + for( int i = 0; i < nGeomFieldCount; i++ ) + { + if( EQUAL(pszGeomFieldName, GetGeomFieldDefn(i)->GetNameRef() ) ) + return i; + } + + return -1; +} + +/************************************************************************/ +/* OGR_FD_GetGeomFieldIndex() */ +/************************************************************************/ +/** + * \brief Find geometry field by name. + * + * The geometry field index of the first geometry field matching the passed + * field name (case insensitively) is returned. + * + * This function is the same as the C++ method OGRFeatureDefn::GetGeomFieldIndex. + * + * @param hDefn handle to the feature definition to get field index from. + * @param pszGeomFieldName the geometry field name to search for. + * + * @return the geometry field index, or -1 if no match found. + */ + +int OGR_FD_GetGeomFieldIndex( OGRFeatureDefnH hDefn, + const char *pszGeomFieldName ) + +{ +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_FD_GetGeomFieldIndex(hDefn, pszGeomFieldName); +#endif + + return ((OGRFeatureDefn *)hDefn)->GetGeomFieldIndex( pszGeomFieldName ); +} + + +/************************************************************************/ +/* GetGeomType() */ +/************************************************************************/ + +/** + * \fn OGRwkbGeometryType OGRFeatureDefn::GetGeomType(); + * + * \brief Fetch the geometry base type. + * + * Note that some drivers are unable to determine a specific geometry + * type for a layer, in which case wkbUnknown is returned. A value of + * wkbNone indicates no geometry is available for the layer at all. + * Many drivers do not properly mark the geometry + * type as 25D even if some or all geometries are in fact 25D. A few (broken) + * drivers return wkbPolygon for layers that also include wkbMultiPolygon. + * + * Starting with GDAL 1.11, this method returns GetGeomFieldDefn(0)->GetType(). + * + * This method is the same as the C function OGR_FD_GetGeomType(). + * + * @return the base type for all geometry related to this definition. + */ +OGRwkbGeometryType OGRFeatureDefn::GetGeomType() +{ + if( GetGeomFieldCount() == 0 ) + return wkbNone; + OGRwkbGeometryType eType = GetGeomFieldDefn(0)->GetType(); + if( eType == (wkbUnknown | wkb25DBitInternalUse) && CSLTestBoolean(CPLGetConfigOption("QGIS_HACK", "NO")) ) + eType = wkbUnknown; + return eType; +} + +/************************************************************************/ +/* OGR_FD_GetGeomType() */ +/************************************************************************/ +/** + * \brief Fetch the geometry base type of the passed feature definition. + * + * This function is the same as the C++ method OGRFeatureDefn::GetGeomType(). + * + * Starting with GDAL 1.11, this method returns GetGeomFieldDefn(0)->GetType(). + * + * @param hDefn handle to the feature definition to get the geometry type from. + * @return the base type for all geometry related to this definition. + */ + +OGRwkbGeometryType OGR_FD_GetGeomType( OGRFeatureDefnH hDefn ) + +{ + OGRwkbGeometryType eType = ((OGRFeatureDefn *) hDefn)->GetGeomType(); + if( OGR_GT_IsNonLinear(eType) && !OGRGetNonLinearGeometriesEnabledFlag() ) + { + eType = OGR_GT_GetLinear(eType); + } +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_FD_GetGeomType(hDefn); +#endif + + return eType; +} + +/************************************************************************/ +/* SetGeomType() */ +/************************************************************************/ + +/** + * \brief Assign the base geometry type for this layer. + * + * All geometry objects using this type must be of the defined type or + * a derived type. The default upon creation is wkbUnknown which allows for + * any geometry type. The geometry type should generally not be changed + * after any OGRFeatures have been created against this definition. + * + * This method is the same as the C function OGR_FD_SetGeomType(). + * + * Starting with GDAL 1.11, this method calls GetGeomFieldDefn(0)->SetType(). + * + * @param eNewType the new type to assign. + */ + +void OGRFeatureDefn::SetGeomType( OGRwkbGeometryType eNewType ) + +{ + if( GetGeomFieldCount() > 0 ) + { + if( GetGeomFieldCount() == 1 && eNewType == wkbNone ) + DeleteGeomFieldDefn(0); + else + GetGeomFieldDefn(0)->SetType(eNewType); + } + else if( eNewType != wkbNone ) + { + OGRGeomFieldDefn oGeomFieldDefn( "", eNewType ); + AddGeomFieldDefn(&oGeomFieldDefn); + } +} + +/************************************************************************/ +/* OGR_FD_SetGeomType() */ +/************************************************************************/ + +/** + * \brief Assign the base geometry type for the passed layer (the same as the feature definition). + * + * All geometry objects using this type must be of the defined type or + * a derived type. The default upon creation is wkbUnknown which allows for + * any geometry type. The geometry type should generally not be changed + * after any OGRFeatures have been created against this definition. + * + * This function is the same as the C++ method OGRFeatureDefn::SetGeomType(). + * + * Starting with GDAL 1.11, this method calls GetGeomFieldDefn(0)->SetType(). + * + * @param hDefn handle to the layer or feature definition to set the geometry + * type to. + * @param eType the new type to assign. + */ + +void OGR_FD_SetGeomType( OGRFeatureDefnH hDefn, OGRwkbGeometryType eType ) + +{ + ((OGRFeatureDefn *) hDefn)->SetGeomType( eType ); +} + +/************************************************************************/ +/* Reference() */ +/************************************************************************/ + +/** + * \fn int OGRFeatureDefn::Reference(); + * + * \brief Increments the reference count by one. + * + * The reference count is used keep track of the number of OGRFeature + * objects referencing this definition. + * + * This method is the same as the C function OGR_FD_Reference(). + * + * @return the updated reference count. + */ + +/************************************************************************/ +/* OGR_FD_Reference() */ +/************************************************************************/ +/** + * \brief Increments the reference count by one. + * + * The reference count is used keep track of the number of OGRFeature + * objects referencing this definition. + * + * This function is the same as the C++ method OGRFeatureDefn::Reference(). + * + * @param hDefn handle to the feature definition on witch OGRFeature are + * based on. + * @return the updated reference count. + */ + +int OGR_FD_Reference( OGRFeatureDefnH hDefn ) + +{ + return ((OGRFeatureDefn *) hDefn)->Reference(); +} + +/************************************************************************/ +/* Dereference() */ +/************************************************************************/ + +/** + * \fn int OGRFeatureDefn::Dereference(); + * + * \brief Decrements the reference count by one. + * + * This method is the same as the C function OGR_FD_Dereference(). + * + * @return the updated reference count. + */ + +/************************************************************************/ +/* OGR_FD_Dereference() */ +/************************************************************************/ + +/** + * \brief Decrements the reference count by one. + * + * This function is the same as the C++ method OGRFeatureDefn::Dereference(). + * + * @param hDefn handle to the feature definition on witch OGRFeature are + * based on. + * @return the updated reference count. + */ + +int OGR_FD_Dereference( OGRFeatureDefnH hDefn ) + +{ + return ((OGRFeatureDefn *) hDefn)->Dereference(); +} + +/************************************************************************/ +/* GetReferenceCount() */ +/************************************************************************/ + +/** + * \fn int OGRFeatureDefn::GetReferenceCount(); + * + * \brief Fetch current reference count. + * + * This method is the same as the C function OGR_FD_GetReferenceCount(). + * + * @return the current reference count. + */ + +/************************************************************************/ +/* OGR_FD_GetReferenceCount() */ +/************************************************************************/ + +/** + * \brief Fetch current reference count. + * + * This function is the same as the C++ method + * OGRFeatureDefn::GetReferenceCount(). + * + * @param hDefn handle to the feature definition on witch OGRFeature are + * based on. + * @return the current reference count. + */ + +int OGR_FD_GetReferenceCount( OGRFeatureDefnH hDefn ) + +{ + return ((OGRFeatureDefn *) hDefn)->GetReferenceCount(); +} + +/************************************************************************/ +/* GetFieldIndex() */ +/************************************************************************/ + +/** + * \brief Find field by name. + * + * The field index of the first field matching the passed field name (case + * insensitively) is returned. + * + * This method is the same as the C function OGR_FD_GetFieldIndex(). + * + * @param pszFieldName the field name to search for. + * + * @return the field index, or -1 if no match found. + */ + + +int OGRFeatureDefn::GetFieldIndex( const char * pszFieldName ) + +{ + GetFieldCount(); + for( int i = 0; i < nFieldCount; i++ ) + { + if( EQUAL(pszFieldName, GetFieldDefn(i)->GetNameRef() ) ) + return i; + } + + return -1; +} + +/************************************************************************/ +/* OGR_FD_GetFieldIndex() */ +/************************************************************************/ +/** + * \brief Find field by name. + * + * The field index of the first field matching the passed field name (case + * insensitively) is returned. + * + * This function is the same as the C++ method OGRFeatureDefn::GetFieldIndex. + * + * @param hDefn handle to the feature definition to get field index from. + * @param pszFieldName the field name to search for. + * + * @return the field index, or -1 if no match found. + */ + +int OGR_FD_GetFieldIndex( OGRFeatureDefnH hDefn, const char *pszFieldName ) + +{ +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_FD_GetFieldIndex(hDefn, pszFieldName); +#endif + + return ((OGRFeatureDefn *)hDefn)->GetFieldIndex( pszFieldName ); +} + +/************************************************************************/ +/* IsGeometryIgnored() */ +/************************************************************************/ + +/** + * \fn int OGRFeatureDefn::IsGeometryIgnored(); + * + * \brief Determine whether the geometry can be omitted when fetching features + * + * This method is the same as the C function OGR_FD_IsGeometryIgnored(). + * + * Starting with GDAL 1.11, this method returns GetGeomFieldDefn(0)->IsIgnored(). + * + * @return ignore state + */ + +int OGRFeatureDefn::IsGeometryIgnored() +{ + if( GetGeomFieldCount() == 0 ) + return FALSE; + return GetGeomFieldDefn(0)->IsIgnored(); +} + +/************************************************************************/ +/* OGR_FD_IsGeometryIgnored() */ +/************************************************************************/ + +/** + * \brief Determine whether the geometry can be omitted when fetching features + * + * This function is the same as the C++ method + * OGRFeatureDefn::IsGeometryIgnored(). + * + * Starting with GDAL 1.11, this method returns GetGeomFieldDefn(0)->IsIgnored(). + * + * @param hDefn handle to the feature definition on witch OGRFeature are + * based on. + * @return ignore state + */ + +int OGR_FD_IsGeometryIgnored( OGRFeatureDefnH hDefn ) +{ + return ((OGRFeatureDefn *) hDefn)->IsGeometryIgnored(); +} + +/************************************************************************/ +/* SetGeometryIgnored() */ +/************************************************************************/ + +/** + * \fn void OGRFeatureDefn::SetGeometryIgnored( int bIgnore ); + * + * \brief Set whether the geometry can be omitted when fetching features + * + * This method is the same as the C function OGR_FD_SetGeometryIgnored(). + * + * Starting with GDAL 1.11, this method calls GetGeomFieldDefn(0)->SetIgnored(). + * + * @param bIgnore ignore state + */ + +void OGRFeatureDefn::SetGeometryIgnored( int bIgnore ) +{ + if( GetGeomFieldCount() > 0 ) + GetGeomFieldDefn(0)->SetIgnored(bIgnore); +} + +/************************************************************************/ +/* OGR_FD_SetGeometryIgnored() */ +/************************************************************************/ + +/** + * \brief Set whether the geometry can be omitted when fetching features + * + * This function is the same as the C++ method + * OGRFeatureDefn::SetGeometryIgnored(). + * + * Starting with GDAL 1.11, this method calls GetGeomFieldDefn(0)->SetIgnored(). + * + * @param hDefn handle to the feature definition on witch OGRFeature are + * based on. + * @param bIgnore ignore state + */ + +void OGR_FD_SetGeometryIgnored( OGRFeatureDefnH hDefn, int bIgnore ) +{ + ((OGRFeatureDefn *) hDefn)->SetGeometryIgnored( bIgnore ); +} + +/************************************************************************/ +/* IsStyleIgnored() */ +/************************************************************************/ + +/** + * \fn int OGRFeatureDefn::IsStyleIgnored(); + * + * \brief Determine whether the style can be omitted when fetching features + * + * This method is the same as the C function OGR_FD_IsStyleIgnored(). + * + * @return ignore state + */ + +/************************************************************************/ +/* OGR_FD_IsStyleIgnored() */ +/************************************************************************/ + +/** + * \brief Determine whether the style can be omitted when fetching features + * + * This function is the same as the C++ method + * OGRFeatureDefn::IsStyleIgnored(). + * + * @param hDefn handle to the feature definition on which OGRFeature are + * based on. + * @return ignore state + */ + +int OGR_FD_IsStyleIgnored( OGRFeatureDefnH hDefn ) +{ + return ((OGRFeatureDefn *) hDefn)->IsStyleIgnored(); +} + +/************************************************************************/ +/* SetStyleIgnored() */ +/************************************************************************/ + +/** + * \fn void OGRFeatureDefn::SetStyleIgnored( int bIgnore ); + * + * \brief Set whether the style can be omitted when fetching features + * + * This method is the same as the C function OGR_FD_SetStyleIgnored(). + * + * @param bIgnore ignore state + */ + +/************************************************************************/ +/* OGR_FD_SetStyleIgnored() */ +/************************************************************************/ + +/** + * \brief Set whether the style can be omitted when fetching features + * + * This function is the same as the C++ method + * OGRFeatureDefn::SetStyleIgnored(). + * + * @param hDefn handle to the feature definition on witch OGRFeature are + * based on. + * @param bIgnore ignore state + */ + +void OGR_FD_SetStyleIgnored( OGRFeatureDefnH hDefn, int bIgnore ) +{ + ((OGRFeatureDefn *) hDefn)->SetStyleIgnored( bIgnore ); +} + +/************************************************************************/ +/* CreateFeatureDefn() */ +/************************************************************************/ + +OGRFeatureDefn *OGRFeatureDefn::CreateFeatureDefn( const char *pszName ) + +{ + return new OGRFeatureDefn( pszName ); +} + +/************************************************************************/ +/* DestroyFeatureDefn() */ +/************************************************************************/ + +void OGRFeatureDefn::DestroyFeatureDefn( OGRFeatureDefn *poDefn ) + +{ + delete poDefn; +} + +/************************************************************************/ +/* IsSame() */ +/************************************************************************/ + +/** + * \brief Test if the feature definition is identical to the other one. + * + * @param poOtherFeatureDefn the other feature definition to compare to. + * @return TRUE if the feature definition is identical to the other one. + */ + +int OGRFeatureDefn::IsSame( OGRFeatureDefn * poOtherFeatureDefn ) +{ + if (strcmp(GetName(), poOtherFeatureDefn->GetName()) == 0 && + GetFieldCount() == poOtherFeatureDefn->GetFieldCount() && + GetGeomFieldCount() == poOtherFeatureDefn->GetGeomFieldCount()) + { + int i; + for(i=0;iGetFieldDefn(i); + if (!poFldDefn->IsSame(poOtherFldDefn)) + { + return FALSE; + } + } + for(i=0;iGetGeomFieldDefn(i); + if (!poGFldDefn->IsSame(poOtherGFldDefn)) + { + return FALSE; + } + } + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* OGR_FD_IsSame() */ +/************************************************************************/ + +/** + * \brief Test if the feature definition is identical to the other one. + * + * @param hFDefn handle to the feature definition on witch OGRFeature are + * based on. + * @param hOtherFDefn handle to the other feature definition to compare to. + * @return TRUE if the feature definition is identical to the other one. + * + * @since OGR 1.11 + */ + +int OGR_FD_IsSame( OGRFeatureDefnH hFDefn, OGRFeatureDefnH hOtherFDefn ) +{ + VALIDATE_POINTER1( hFDefn, "OGR_FD_IsSame", FALSE ); + VALIDATE_POINTER1( hOtherFDefn, "OGR_FD_IsSame", FALSE ); + return ((OGRFeatureDefn*)hFDefn)->IsSame((OGRFeatureDefn*)hOtherFDefn); +} diff --git a/bazaar/plugin/gdal/ogr/ogrfeaturequery.cpp b/bazaar/plugin/gdal/ogr/ogrfeaturequery.cpp new file mode 100644 index 000000000..f77b3cbd0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrfeaturequery.cpp @@ -0,0 +1,744 @@ +/****************************************************************************** + * $Id: ogrfeaturequery.cpp 28968 2015-04-21 19:00:02Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implementation of simple SQL WHERE style attributes queries + * for OGRFeatures. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "swq.h" +#include "ogr_feature.h" +#include "ogr_p.h" +#include "ogr_attrind.h" + +CPL_CVSID("$Id: ogrfeaturequery.cpp 28968 2015-04-21 19:00:02Z rouault $"); + +/************************************************************************/ +/* Support for special attributes (feature query and selection) */ +/************************************************************************/ + +const char* SpecialFieldNames[SPECIAL_FIELD_COUNT] += {"FID", "OGR_GEOMETRY", "OGR_STYLE", "OGR_GEOM_WKT", "OGR_GEOM_AREA"}; +const swq_field_type SpecialFieldTypes[SPECIAL_FIELD_COUNT] += {SWQ_INTEGER, SWQ_STRING, SWQ_STRING, SWQ_STRING, SWQ_FLOAT}; + +/************************************************************************/ +/* OGRFeatureQuery() */ +/************************************************************************/ + +OGRFeatureQuery::OGRFeatureQuery() + +{ + poTargetDefn = NULL; + pSWQExpr = NULL; +} + +/************************************************************************/ +/* ~OGRFeatureQuery() */ +/************************************************************************/ + +OGRFeatureQuery::~OGRFeatureQuery() + +{ + delete (swq_expr_node *) pSWQExpr; +} + +/************************************************************************/ +/* Parse */ +/************************************************************************/ + +OGRErr OGRFeatureQuery::Compile( OGRFeatureDefn *poDefn, + const char * pszExpression, + int bCheck, + swq_custom_func_registrar* poCustomFuncRegistrar ) + +{ +/* -------------------------------------------------------------------- */ +/* Clear any existing expression. */ +/* -------------------------------------------------------------------- */ + if( pSWQExpr != NULL ) + { + delete (swq_expr_node *) pSWQExpr; + pSWQExpr = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Build list of fields. */ +/* -------------------------------------------------------------------- */ + char **papszFieldNames; + swq_field_type *paeFieldTypes; + int iField; + int nFieldCount = poDefn->GetFieldCount() + SPECIAL_FIELD_COUNT + + poDefn->GetGeomFieldCount(); + + papszFieldNames = (char **) + CPLMalloc(sizeof(char *) * nFieldCount ); + paeFieldTypes = (swq_field_type *) + CPLMalloc(sizeof(swq_field_type) * nFieldCount ); + + for( iField = 0; iField < poDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poField = poDefn->GetFieldDefn( iField ); + + papszFieldNames[iField] = (char *) poField->GetNameRef(); + + switch( poField->GetType() ) + { + case OFTInteger: + { + if( poField->GetSubType() == OFSTBoolean ) + paeFieldTypes[iField] = SWQ_BOOLEAN; + else + paeFieldTypes[iField] = SWQ_INTEGER; + break; + } + + case OFTInteger64: + { + if( poField->GetSubType() == OFSTBoolean ) + paeFieldTypes[iField] = SWQ_BOOLEAN; + else + paeFieldTypes[iField] = SWQ_INTEGER64; + break; + } + + case OFTReal: + paeFieldTypes[iField] = SWQ_FLOAT; + break; + + case OFTString: + paeFieldTypes[iField] = SWQ_STRING; + break; + + case OFTDate: + case OFTTime: + case OFTDateTime: + paeFieldTypes[iField] = SWQ_TIMESTAMP; + break; + + default: + paeFieldTypes[iField] = SWQ_OTHER; + break; + } + } + + iField = 0; + while (iField < SPECIAL_FIELD_COUNT) + { + papszFieldNames[poDefn->GetFieldCount() + iField] = (char *) SpecialFieldNames[iField]; + paeFieldTypes[poDefn->GetFieldCount() + iField] = SpecialFieldTypes[iField]; + ++iField; + } + + for( iField = 0; iField < poDefn->GetGeomFieldCount(); iField++ ) + { + OGRGeomFieldDefn *poField = poDefn->GetGeomFieldDefn( iField ); + int iDstField = poDefn->GetFieldCount() + SPECIAL_FIELD_COUNT + iField; + + papszFieldNames[iDstField] = (char *) poField->GetNameRef(); + if( *papszFieldNames[iDstField] == '\0' ) + papszFieldNames[iDstField] = (char*) OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME; + paeFieldTypes[iDstField] = SWQ_GEOMETRY; + } + +/* -------------------------------------------------------------------- */ +/* Try to parse. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr = OGRERR_NONE; + CPLErr eCPLErr; + + poTargetDefn = poDefn; + eCPLErr = swq_expr_compile( pszExpression, nFieldCount, + papszFieldNames, paeFieldTypes, + bCheck, + poCustomFuncRegistrar, + (swq_expr_node **) &pSWQExpr ); + if( eCPLErr != CE_None ) + { + eErr = OGRERR_CORRUPT_DATA; + pSWQExpr = NULL; + } + + CPLFree( papszFieldNames ); + CPLFree( paeFieldTypes ); + + + return eErr; +} + +/************************************************************************/ +/* OGRFeatureFetcher() */ +/************************************************************************/ + +static swq_expr_node *OGRFeatureFetcher( swq_expr_node *op, void *pFeatureIn ) + +{ + OGRFeature *poFeature = (OGRFeature *) pFeatureIn; + swq_expr_node *poRetNode = NULL; + + if( op->field_type == SWQ_GEOMETRY ) + { + int iField = op->field_index - (poFeature->GetFieldCount() + SPECIAL_FIELD_COUNT); + poRetNode = new swq_expr_node( poFeature->GetGeomFieldRef(iField) ); + return poRetNode; + } + + switch( op->field_type ) + { + case SWQ_INTEGER: + case SWQ_BOOLEAN: + poRetNode = new swq_expr_node( + poFeature->GetFieldAsInteger(op->field_index) ); + break; + + case SWQ_INTEGER64: + poRetNode = new swq_expr_node( + poFeature->GetFieldAsInteger64(op->field_index) ); + break; + + case SWQ_FLOAT: + poRetNode = new swq_expr_node( + poFeature->GetFieldAsDouble(op->field_index) ); + break; + + default: + poRetNode = new swq_expr_node( + poFeature->GetFieldAsString(op->field_index) ); + break; + } + + poRetNode->is_null = !(poFeature->IsFieldSet(op->field_index)); + + return poRetNode; +} + +/************************************************************************/ +/* Evaluate() */ +/************************************************************************/ + +int OGRFeatureQuery::Evaluate( OGRFeature *poFeature ) + +{ + if( pSWQExpr == NULL ) + return FALSE; + + swq_expr_node *poResult; + + poResult = ((swq_expr_node *) pSWQExpr)->Evaluate( OGRFeatureFetcher, + (void *) poFeature ); + + if( poResult == NULL ) + return FALSE; + + int bLogicalResult = FALSE; + if( poResult->field_type == SWQ_INTEGER || + poResult->field_type == SWQ_INTEGER64 || + poResult->field_type == SWQ_BOOLEAN ) + bLogicalResult = (int)poResult->int_value; + + delete poResult; + + return bLogicalResult; +} + +/************************************************************************/ +/* CanUseIndex() */ +/************************************************************************/ + +int OGRFeatureQuery::CanUseIndex( OGRLayer *poLayer ) +{ + swq_expr_node *psExpr = (swq_expr_node *) pSWQExpr; + +/* -------------------------------------------------------------------- */ +/* Do we have an index on the targetted layer? */ +/* -------------------------------------------------------------------- */ + if ( poLayer->GetIndex() == FALSE ) + return FALSE; + + return CanUseIndex( psExpr, poLayer ); +} + +int OGRFeatureQuery::CanUseIndex( swq_expr_node *psExpr, + OGRLayer *poLayer ) +{ + OGRAttrIndex *poIndex; + +/* -------------------------------------------------------------------- */ +/* Does the expression meet our requirements? */ +/* -------------------------------------------------------------------- */ + if( psExpr == NULL || + psExpr->eNodeType != SNT_OPERATION ) + return FALSE; + + if ((psExpr->nOperation == SWQ_OR || psExpr->nOperation == SWQ_AND) && + psExpr->nSubExprCount == 2) + { + return CanUseIndex( psExpr->papoSubExpr[0], poLayer ) && + CanUseIndex( psExpr->papoSubExpr[1], poLayer ); + } + + if( !(psExpr->nOperation == SWQ_EQ || psExpr->nOperation == SWQ_IN) + || psExpr->nSubExprCount < 2 ) + return FALSE; + + swq_expr_node *poColumn = psExpr->papoSubExpr[0]; + swq_expr_node *poValue = psExpr->papoSubExpr[1]; + + if( poColumn->eNodeType != SNT_COLUMN + || poValue->eNodeType != SNT_CONSTANT ) + return FALSE; + + poIndex = poLayer->GetIndex()->GetFieldIndex( poColumn->field_index ); + if( poIndex == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* OK, we have an index */ +/* -------------------------------------------------------------------- */ + return TRUE; +} + +/************************************************************************/ +/* EvaluateAgainstIndices() */ +/* */ +/* Attempt to return a list of FIDs matching the given */ +/* attribute query conditions utilizing attribute indices. */ +/* Returns NULL if the result cannot be computed from the */ +/* available indices, or an "OGRNullFID" terminated list of */ +/* FIDs if it can. */ +/* */ +/* For now we only support equality tests on a single indexed */ +/* attribute field. Eventually we should make this support */ +/* multi-part queries with ranges. */ +/************************************************************************/ + +static int CompareGIntBig(const void *pa, const void *pb) +{ + GIntBig a = *((const GIntBig*)pa); + GIntBig b = *((const GIntBig*)pb); + if( a < b ) + return -1; + else if( a > b ) + return 1; + else + return 0; +} + +GIntBig *OGRFeatureQuery::EvaluateAgainstIndices( OGRLayer *poLayer, + OGRErr *peErr ) + +{ + swq_expr_node *psExpr = (swq_expr_node *) pSWQExpr; + + if( peErr != NULL ) + *peErr = OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* Do we have an index on the targetted layer? */ +/* -------------------------------------------------------------------- */ + if ( poLayer->GetIndex() == NULL ) + return NULL; + + GIntBig nFIDCount = 0; + return EvaluateAgainstIndices(psExpr, poLayer, nFIDCount); +} + +/* The input arrays must be sorted ! */ +static +GIntBig* OGRORGIntBigArray(GIntBig panFIDList1[], GIntBig nFIDCount1, + GIntBig panFIDList2[], GIntBig nFIDCount2, + GIntBig& nFIDCount) +{ + GIntBig nMaxCount = nFIDCount1 + nFIDCount2; + GIntBig* panFIDList = (GIntBig*) CPLMalloc((size_t)(nMaxCount+1) * sizeof(GIntBig)); + nFIDCount = 0; + + GIntBig i1 = 0, i2 =0; + for(;i1eNodeType != SNT_OPERATION ) + return NULL; + + if ((psExpr->nOperation == SWQ_OR || psExpr->nOperation == SWQ_AND) && + psExpr->nSubExprCount == 2) + { + GIntBig nFIDCount1 = 0, nFIDCount2 = 0; + GIntBig* panFIDList1 = EvaluateAgainstIndices( psExpr->papoSubExpr[0], poLayer, nFIDCount1 ); + GIntBig* panFIDList2 = panFIDList1 == NULL ? NULL : + EvaluateAgainstIndices( psExpr->papoSubExpr[1], poLayer, nFIDCount2 ); + GIntBig* panFIDList = NULL; + if (panFIDList1 != NULL && panFIDList2 != NULL) + { + if (psExpr->nOperation == SWQ_OR ) + panFIDList = OGRORGIntBigArray(panFIDList1, nFIDCount1, + panFIDList2, nFIDCount2, nFIDCount); + else if (psExpr->nOperation == SWQ_AND ) + panFIDList = OGRANDGIntBigArray(panFIDList1, nFIDCount1, + panFIDList2, nFIDCount2, nFIDCount); + + } + CPLFree(panFIDList1); + CPLFree(panFIDList2); + return panFIDList; + } + + if( !(psExpr->nOperation == SWQ_EQ || psExpr->nOperation == SWQ_IN) + || psExpr->nSubExprCount < 2 ) + return NULL; + + swq_expr_node *poColumn = psExpr->papoSubExpr[0]; + swq_expr_node *poValue = psExpr->papoSubExpr[1]; + + if( poColumn->eNodeType != SNT_COLUMN + || poValue->eNodeType != SNT_CONSTANT ) + return NULL; + + poIndex = poLayer->GetIndex()->GetFieldIndex( poColumn->field_index ); + if( poIndex == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* OK, we have an index, now we need to query it. */ +/* -------------------------------------------------------------------- */ + OGRField sValue; + OGRFieldDefn *poFieldDefn; + + poFieldDefn = poLayer->GetLayerDefn()->GetFieldDefn(poColumn->field_index); + +/* -------------------------------------------------------------------- */ +/* Handle the case of an IN operation. */ +/* -------------------------------------------------------------------- */ + if (psExpr->nOperation == SWQ_IN) + { + int nLength; + GIntBig *panFIDs = NULL; + int iIN; + + for( iIN = 1; iIN < psExpr->nSubExprCount; iIN++ ) + { + switch( poFieldDefn->GetType() ) + { + case OFTInteger: + if (psExpr->papoSubExpr[iIN]->field_type == SWQ_FLOAT) + sValue.Integer = (int) psExpr->papoSubExpr[iIN]->float_value; + else + sValue.Integer = (int) psExpr->papoSubExpr[iIN]->int_value; + break; + + case OFTInteger64: + if (psExpr->papoSubExpr[iIN]->field_type == SWQ_FLOAT) + sValue.Integer64 = (GIntBig) psExpr->papoSubExpr[iIN]->float_value; + else + sValue.Integer64 = psExpr->papoSubExpr[iIN]->int_value; + break; + + case OFTReal: + sValue.Real = psExpr->papoSubExpr[iIN]->float_value; + break; + + case OFTString: + sValue.String = psExpr->papoSubExpr[iIN]->string_value; + break; + + default: + CPLAssert( FALSE ); + return NULL; + } + + int nFIDCount32 = 0; + panFIDs = poIndex->GetAllMatches( &sValue, panFIDs, &nFIDCount32, &nLength ); + nFIDCount = nFIDCount32; + } + + if (nFIDCount > 1) + { + /* the returned FIDs are expected to be in sorted order */ + qsort(panFIDs, (size_t)nFIDCount, sizeof(GIntBig), CompareGIntBig); + } + return panFIDs; + } + +/* -------------------------------------------------------------------- */ +/* Handle equality test. */ +/* -------------------------------------------------------------------- */ + switch( poFieldDefn->GetType() ) + { + case OFTInteger: + if (poValue->field_type == SWQ_FLOAT) + sValue.Integer = (int) poValue->float_value; + else + sValue.Integer = (int) poValue->int_value; + break; + + case OFTInteger64: + if (poValue->field_type == SWQ_FLOAT) + sValue.Integer64 = (GIntBig) poValue->float_value; + else + sValue.Integer64 = poValue->int_value; + break; + + case OFTReal: + sValue.Real = poValue->float_value; + break; + + case OFTString: + sValue.String = poValue->string_value; + break; + + default: + CPLAssert( FALSE ); + return NULL; + } + + int nLength = 0; + int nFIDCount32 = 0; + GIntBig* panFIDs = poIndex->GetAllMatches( &sValue, NULL, &nFIDCount32, &nLength ); + nFIDCount = nFIDCount32; + if (nFIDCount > 1) + { + /* the returned FIDs are expected to be in sorted order */ + qsort(panFIDs, (size_t)nFIDCount, sizeof(GIntBig), CompareGIntBig); + } + return panFIDs; +} + +/************************************************************************/ +/* OGRFieldCollector() */ +/* */ +/* Helper function for recursing through tree to satisfy */ +/* GetUsedFields(). */ +/************************************************************************/ + +char **OGRFeatureQuery::FieldCollector( void *pBareOp, + char **papszList ) + +{ + swq_expr_node *op = (swq_expr_node *) pBareOp; + +/* -------------------------------------------------------------------- */ +/* References to tables other than the primarily are currently */ +/* unsupported. Error out. */ +/* -------------------------------------------------------------------- */ + if( op->eNodeType == SNT_COLUMN ) + { + if( op->table_index != 0 ) + { + CSLDestroy( papszList ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Add the field name into our list if it is not already there. */ +/* -------------------------------------------------------------------- */ + const char *pszFieldName; + + if( op->field_index >= poTargetDefn->GetFieldCount() + && op->field_index < poTargetDefn->GetFieldCount() + SPECIAL_FIELD_COUNT) + pszFieldName = SpecialFieldNames[op->field_index - poTargetDefn->GetFieldCount()]; + else if( op->field_index >= 0 + && op->field_index < poTargetDefn->GetFieldCount() ) + pszFieldName = + poTargetDefn->GetFieldDefn(op->field_index)->GetNameRef(); + else + { + CSLDestroy( papszList ); + return NULL; + } + + if( CSLFindString( papszList, pszFieldName ) == -1 ) + papszList = CSLAddString( papszList, pszFieldName ); + } + +/* -------------------------------------------------------------------- */ +/* Add in fields from subexpressions. */ +/* -------------------------------------------------------------------- */ + if( op->eNodeType == SNT_OPERATION ) + { + for( int iSubExpr = 0; iSubExpr < op->nSubExprCount; iSubExpr++ ) + { + papszList = FieldCollector( op->papoSubExpr[iSubExpr], papszList ); + } + } + + return papszList; +} + +/************************************************************************/ +/* GetUsedFields() */ +/************************************************************************/ + +/** + * Returns lists of fields in expression. + * + * All attribute fields are used in the expression of this feature + * query are returned as a StringList of field names. This function would + * primarily be used within drivers to recognise special case conditions + * depending only on attribute fields that can be very efficiently + * fetched. + * + * NOTE: If any fields in the expression are from tables other than the + * primary table then NULL is returned indicating an error. In succesful + * use, no non-empty expression should return an empty list. + * + * @return list of field names. Free list with CSLDestroy() when no longer + * required. + */ + +char **OGRFeatureQuery::GetUsedFields( ) + +{ + if( pSWQExpr == NULL ) + return NULL; + + + return FieldCollector( pSWQExpr, NULL ); +} + + + diff --git a/bazaar/plugin/gdal/ogr/ogrfeaturestyle.cpp b/bazaar/plugin/gdal/ogr/ogrfeaturestyle.cpp new file mode 100644 index 000000000..22fc55e6b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrfeaturestyle.cpp @@ -0,0 +1,2956 @@ +/****************************************************************************** + * $Id: ogrfeaturestyle.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Feature Representation string API + * Author: Stephane Villeneuve, stephane.v@videotron.ca + * + ****************************************************************************** + * Copyright (c) 2000-2001, Stephane Villeneuve + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_feature.h" +#include "ogr_featurestyle.h" +#include "ogr_api.h" + +CPL_CVSID("$Id: ogrfeaturestyle.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +CPL_C_START +void OGRFeatureStylePuller() {} +CPL_C_END + +/****************************************************************************/ +/* Class Parameter (used in the String) */ +/* */ +/* The order of all parameter MUST be the same than in the definition */ +/****************************************************************************/ +static const OGRStyleParamId asStylePen[] = +{ + {OGRSTPenColor,"c",FALSE,OGRSTypeString}, + {OGRSTPenWidth,"w",TRUE,OGRSTypeDouble}, + {OGRSTPenPattern,"p",FALSE,OGRSTypeString}, // georefed,but multiple times. + {OGRSTPenId,"id",FALSE,OGRSTypeString}, + {OGRSTPenPerOffset,"dp",TRUE,OGRSTypeDouble}, + {OGRSTPenCap,"cap",FALSE,OGRSTypeString}, + {OGRSTPenJoin,"j",FALSE,OGRSTypeString}, + {OGRSTPenPriority, "l", FALSE, OGRSTypeInteger} +}; + +static const OGRStyleParamId asStyleBrush[] = +{ + {OGRSTBrushFColor,"fc",FALSE,OGRSTypeString}, + {OGRSTBrushBColor,"bc",FALSE,OGRSTypeString}, + {OGRSTBrushId,"id",FALSE,OGRSTypeString}, + {OGRSTBrushAngle,"a",FALSE,OGRSTypeDouble}, + {OGRSTBrushSize,"s",TRUE,OGRSTypeDouble}, + {OGRSTBrushDx,"dx",TRUE,OGRSTypeDouble}, + {OGRSTBrushDy,"dy",TRUE,OGRSTypeDouble}, + {OGRSTBrushPriority,"l",FALSE,OGRSTypeInteger} +}; + +static const OGRStyleParamId asStyleSymbol[] = +{ + {OGRSTSymbolId,"id",FALSE,OGRSTypeString}, + {OGRSTSymbolAngle,"a",FALSE,OGRSTypeDouble}, + {OGRSTSymbolColor,"c",FALSE,OGRSTypeString}, + {OGRSTSymbolSize,"s",TRUE,OGRSTypeDouble}, + {OGRSTSymbolDx,"dx",TRUE,OGRSTypeDouble}, + {OGRSTSymbolDy,"dy",TRUE,OGRSTypeDouble}, + {OGRSTSymbolStep,"ds",TRUE,OGRSTypeDouble}, + {OGRSTSymbolPerp,"dp",TRUE,OGRSTypeDouble}, + {OGRSTSymbolOffset,"di",TRUE,OGRSTypeDouble}, + {OGRSTSymbolPriority,"l",FALSE,OGRSTypeInteger}, + {OGRSTSymbolFontName,"f",FALSE,OGRSTypeString}, + {OGRSTSymbolOColor,"o",FALSE,OGRSTypeString} +}; + +static const OGRStyleParamId asStyleLabel[] = +{ + {OGRSTLabelFontName,"f",FALSE,OGRSTypeString}, + {OGRSTLabelSize,"s",TRUE,OGRSTypeDouble}, + {OGRSTLabelTextString,"t",FALSE, OGRSTypeString}, + {OGRSTLabelAngle,"a",FALSE,OGRSTypeDouble}, + {OGRSTLabelFColor,"c",FALSE,OGRSTypeString}, + {OGRSTLabelBColor,"b",FALSE,OGRSTypeString}, + {OGRSTLabelPlacement,"m",FALSE, OGRSTypeString}, + {OGRSTLabelAnchor,"p",FALSE,OGRSTypeInteger}, + {OGRSTLabelDx,"dx",TRUE,OGRSTypeDouble}, + {OGRSTLabelDy,"dy",TRUE,OGRSTypeDouble}, + {OGRSTLabelPerp,"dp",TRUE,OGRSTypeDouble}, + {OGRSTLabelBold,"bo",FALSE,OGRSTypeBoolean}, + {OGRSTLabelItalic,"it",FALSE,OGRSTypeBoolean}, + {OGRSTLabelUnderline,"un",FALSE, OGRSTypeBoolean}, + {OGRSTLabelPriority,"l",FALSE, OGRSTypeInteger}, + {OGRSTLabelStrikeout,"st",FALSE, OGRSTypeBoolean}, + {OGRSTLabelStretch,"w",FALSE, OGRSTypeDouble}, + {OGRSTLabelAdjHor,"ah",FALSE, OGRSTypeString}, + {OGRSTLabelAdjVert,"av",FALSE, OGRSTypeString}, + {OGRSTLabelHColor,"h",FALSE,OGRSTypeString}, + {OGRSTLabelOColor,"o",FALSE,OGRSTypeString} +}; + +/* ======================================================================== */ +/* OGRStyleMgr */ +/* ======================================================================== */ + +/****************************************************************************/ +/* OGRStyleMgr::OGRStyleMgr(OGRStyleTable *poDataSetStyleTable) */ +/* */ +/****************************************************************************/ +/** + * \brief Constructor. + * + * This method is the same as the C function OGR_SM_Create() + * + * @param poDataSetStyleTable (currently unused, reserved for future use), pointer + * to OGRStyleTable. Pass NULL for now. + */ +OGRStyleMgr::OGRStyleMgr(OGRStyleTable *poDataSetStyleTable) +{ + m_poDataSetStyleTable = poDataSetStyleTable; + m_pszStyleString = NULL; +} + +/************************************************************************/ +/* OGR_SM_Create() */ +/************************************************************************/ +/** + * \brief OGRStyleMgr factory. + * + * This function is the same as the C++ method OGRStyleMgr::OGRStyleMgr(). + * + * @param hStyleTable pointer to OGRStyleTable or NULL if not working with + * a style table. + * + * @return an handle to the new style manager object. + */ + +OGRStyleMgrH OGR_SM_Create( OGRStyleTableH hStyleTable ) + +{ + return (OGRStyleMgrH) new OGRStyleMgr( (OGRStyleTable *) hStyleTable ); +} + + +/****************************************************************************/ +/* OGRStyleMgr::~OGRStyleMgr() */ +/* */ +/****************************************************************************/ +/** + * \brief Destructor. + * + * This method is the same as the C function OGR_SM_Destroy() + */ +OGRStyleMgr::~OGRStyleMgr() +{ + if ( m_pszStyleString ) + CPLFree(m_pszStyleString); +} + +/************************************************************************/ +/* OGR_SM_Destroy() */ +/************************************************************************/ +/** + * \brief Destroy Style Manager + * + * This function is the same as the C++ method OGRStyleMgr::~OGRStyleMgr(). + * + * @param hSM handle to the style manager to destroy. + */ + +void OGR_SM_Destroy( OGRStyleMgrH hSM ) + +{ + delete (OGRStyleMgr *) hSM; +} + + +/****************************************************************************/ +/* GBool OGRStyleMgr::SetFeatureStyleString(OGRFeature *poFeature, */ +/* char *pszStyleString, */ +/* GBool bNoMatching) */ +/* Set the gived representation to the feature, */ +/* if bNoMatching == TRUE, don't try to find it in the styletable */ +/* otherwize, we will use the name defined in the styletable */ +/****************************************************************************/ + +/** + * \brief Set a style in a feature + * + * @param poFeature the feature object to store the style in + * @param pszStyleString the style to store + * @param bNoMatching TRUE to lookup the style in the style table and + * add the name to the feature + * + * @return TRUE on success, FALSE on error. + */ + +GBool OGRStyleMgr::SetFeatureStyleString(OGRFeature *poFeature, + const char *pszStyleString, + GBool bNoMatching) +{ + const char *pszName; + if (poFeature == FALSE) + return FALSE; + + if (pszStyleString == NULL) + poFeature->SetStyleString(""); + else if (bNoMatching == TRUE) + poFeature->SetStyleString(pszStyleString); + else if ((pszName = GetStyleName(pszStyleString)) != NULL) + poFeature->SetStyleString(pszName); + else + poFeature->SetStyleString(pszStyleString); + + return TRUE; +} + +/****************************************************************************/ +/* const char *OGRStyleMgr::InitFromFeature(OGRFeature *) */ +/* */ +/****************************************************************************/ + +/** + * \brief Initialize style manager from the style string of a feature. + * + * This method is the same as the C function OGR_SM_InitFromFeature(). + * + * @param poFeature feature object from which to read the style. + * + * @return a reference to the style string read from the feature, or NULL + * in case of error.. + */ + +const char *OGRStyleMgr::InitFromFeature(OGRFeature *poFeature) +{ + CPLFree(m_pszStyleString); + m_pszStyleString = NULL; + + if (poFeature) + InitStyleString(poFeature->GetStyleString()); + else + m_pszStyleString = NULL; + + return m_pszStyleString; + +} + +/************************************************************************/ +/* OGR_SM_InitFromFeature() */ +/************************************************************************/ + +/** + * \brief Initialize style manager from the style string of a feature. + * + * This function is the same as the C++ method + * OGRStyleMgr::InitFromFeature(). + * + * @param hSM handle to the style manager. + * @param hFeat handle to the new feature from which to read the style. + * + * @return a reference to the style string read from the feature, or NULL + * in case of error. + */ + +const char *OGR_SM_InitFromFeature(OGRStyleMgrH hSM, + OGRFeatureH hFeat) + +{ + VALIDATE_POINTER1( hSM, "OGR_SM_InitFromFeature", NULL ); + VALIDATE_POINTER1( hFeat, "OGR_SM_InitFromFeature", NULL ); + + return ((OGRStyleMgr *) hSM)->InitFromFeature((OGRFeature *)hFeat); +} + +/****************************************************************************/ +/* GBool OGRStyleMgr::InitStyleString(char *pszStyleString) */ +/* */ +/****************************************************************************/ + +/** + * \brief Initialize style manager from the style string. + * + * This method is the same as the C function OGR_SM_InitStyleString(). + * + * @param pszStyleString the style string to use (can be NULL). + * + * @return TRUE on success, FALSE on errors. + */ +GBool OGRStyleMgr::InitStyleString(const char *pszStyleString) +{ + CPLFree(m_pszStyleString); + m_pszStyleString = NULL; + + if (pszStyleString && pszStyleString[0] == '@') + m_pszStyleString = CPLStrdup(GetStyleByName(pszStyleString)); + else + m_pszStyleString = NULL; + + if (m_pszStyleString == NULL && pszStyleString) + m_pszStyleString = CPLStrdup(pszStyleString); + + + + return TRUE; +} + +/************************************************************************/ +/* OGR_SM_InitStyleString() */ +/************************************************************************/ + +/** + * \brief Initialize style manager from the style string. + * + * This function is the same as the C++ method OGRStyleMgr::InitStyleString(). + * + * @param hSM handle to the style manager. + * @param pszStyleString the style string to use (can be NULL). + * + * @return TRUE on success, FALSE on errors. + */ + +int OGR_SM_InitStyleString(OGRStyleMgrH hSM, const char *pszStyleString) + +{ + VALIDATE_POINTER1( hSM, "OGR_SM_InitStyleString", FALSE ); + + return ((OGRStyleMgr *) hSM)->InitStyleString(pszStyleString); +} + + +/****************************************************************************/ +/* const char *OGRStyleMgr::GetStyleName(const char *pszStyleString) */ +/* */ +/****************************************************************************/ + +/** + * \brief Get the name of a style from the style table. + * + * @param pszStyleString the style to search for, or NULL to use the style + * currently stored in the manager. + * + * @return The name if found, or NULL on error. + */ + +const char *OGRStyleMgr::GetStyleName(const char *pszStyleString) +{ + + // SECURITY: the unit and the value for all parameter should be the same, + // a text comparaison is executed . + + const char *pszStyle; + + if (pszStyleString) + pszStyle = pszStyleString; + else + pszStyle = m_pszStyleString; + + if (pszStyle) + { + if (m_poDataSetStyleTable) + return m_poDataSetStyleTable->GetStyleName(pszStyle); + } + return NULL; +} +/****************************************************************************/ +/* const char *OGRStyleMgr::GetStyleByName(const char *pszStyleName) */ +/* */ +/****************************************************************************/ + +/** + * \brief find a style in the current style table. + * + * + * @param pszStyleName the name of the style to add. + * + * @return the style string matching the name or NULL if not found or error. + */ +const char *OGRStyleMgr::GetStyleByName(const char *pszStyleName) +{ + if (m_poDataSetStyleTable) + { + return m_poDataSetStyleTable->Find(pszStyleName); + } + return NULL; +} + +/****************************************************************************/ +/* GBool OGRStyleMgr::AddStyle(char *pszStyleName, */ +/* char *pszStyleString) */ +/* */ +/****************************************************************************/ + +/** + * \brief Add a style to the current style table. + * + * This method is the same as the C function OGR_SM_AddStyle(). + * + * @param pszStyleName the name of the style to add. + * @param pszStyleString the style string to use, or NULL to use the style + * stored in the manager. + * + * @return TRUE on success, FALSE on errors. + */ + +GBool OGRStyleMgr::AddStyle(const char *pszStyleName, + const char *pszStyleString) +{ + const char *pszStyle; + + if (pszStyleString) + pszStyle = pszStyleString; + else + pszStyle = m_pszStyleString; + + if (m_poDataSetStyleTable) + { + return m_poDataSetStyleTable->AddStyle(pszStyleName, pszStyle); + } + return FALSE; +} + + +/************************************************************************/ +/* OGR_SM_AddStyle() */ +/************************************************************************/ + +/** + * \brief Add a style to the current style table. + * + * This function is the same as the C++ method OGRStyleMgr::AddStyle(). + * + * @param hSM handle to the style manager. + * @param pszStyleName the name of the style to add. + * @param pszStyleString the style string to use, or NULL to use the style + * stored in the manager. + * + * @return TRUE on success, FALSE on errors. + */ + +int OGR_SM_AddStyle(OGRStyleMgrH hSM, const char *pszStyleName, + const char *pszStyleString) +{ + VALIDATE_POINTER1( hSM, "OGR_SM_AddStyle", FALSE ); + VALIDATE_POINTER1( pszStyleName, "OGR_SM_AddStyle", FALSE ); + + return ((OGRStyleMgr *) hSM)->AddStyle( pszStyleName, pszStyleString); +} + + +/****************************************************************************/ +/* const char *OGRStyleMgr::GetStyleString(OGRFeature *) */ +/* */ +/****************************************************************************/ + +/** + * \brief Get the style string from the style manager. + * + * @param poFeature feature object from which to read the style or NULL to + * get the style string stored in the manager. + * + * @return the style string stored in the feature or the style string stored + * in the style manager if poFeature is NULL + * + * NOTE: this method will call OGRStyleMgr::InitFromFeature() if poFeature is + * not NULL and replace the style string stored in the style manager + */ + +const char *OGRStyleMgr::GetStyleString(OGRFeature *poFeature) +{ + if (poFeature == NULL) + return m_pszStyleString; + else + return InitFromFeature(poFeature); +} + +/****************************************************************************/ +/* GBool OGRStyleMgr::AddPart(const char *pszPart) */ +/* Add a new part in the current style */ +/****************************************************************************/ + +/** + * \brief Add a part (style string) to the current style. + * + * @param pszPart the style string defining the part to add. + * + * @return TRUE on success, FALSE on errors. + */ + +GBool OGRStyleMgr::AddPart(const char *pszPart) +{ + char *pszTmp; + if (pszPart) + { + if (m_pszStyleString) + { + pszTmp = CPLStrdup(CPLString().Printf("%s;%s",m_pszStyleString, + pszPart)); + CPLFree(m_pszStyleString); + m_pszStyleString = pszTmp; + } + else + { + pszTmp= CPLStrdup(CPLString().Printf("%s",pszPart)); + CPLFree(m_pszStyleString); + m_pszStyleString = pszTmp; + } + return TRUE; + } + + return FALSE; +} + +/****************************************************************************/ +/* GBool OGRStyleMgr::AddPart(OGRStyleTool *) */ +/* Add a new part in the current style */ +/****************************************************************************/ + +/** + * \brief Add a part (style tool) to the current style. + * + * This method is the same as the C function OGR_SM_AddPart(). + * + * @param poStyleTool the style tool defining the part to add. + * + * @return TRUE on success, FALSE on errors. + */ + +GBool OGRStyleMgr::AddPart(OGRStyleTool *poStyleTool) +{ + char *pszTmp; + if (poStyleTool && poStyleTool->GetStyleString()) + { + if (m_pszStyleString) + { + pszTmp = CPLStrdup(CPLString().Printf("%s;%s",m_pszStyleString, + poStyleTool->GetStyleString())); + CPLFree(m_pszStyleString); + m_pszStyleString = pszTmp; + } + else + { + pszTmp= CPLStrdup(CPLString().Printf("%s", + poStyleTool->GetStyleString())); + CPLFree(m_pszStyleString); + m_pszStyleString = pszTmp; + } + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* OGR_SM_AddPart() */ +/************************************************************************/ + +/** + * \brief Add a part (style tool) to the current style. + * + * This function is the same as the C++ method OGRStyleMgr::AddPart(). + * + * @param hSM handle to the style manager. + * @param hST the style tool defining the part to add. + * + * @return TRUE on success, FALSE on errors. + */ + +int OGR_SM_AddPart(OGRStyleMgrH hSM, OGRStyleToolH hST) + +{ + VALIDATE_POINTER1( hSM, "OGR_SM_InitStyleString", FALSE ); + VALIDATE_POINTER1( hST, "OGR_SM_InitStyleString", FALSE ); + + return ((OGRStyleMgr *) hSM)->AddPart((OGRStyleTool *)hST); +} + + +/****************************************************************************/ +/* int OGRStyleMgr::GetPartCount(const char *pszStyleString) */ +/* return the number of part in the stylestring */ +/* FIXME: this function should actually parse style string instead of simple*/ +/* semicolon counting, we should not count broken and empty parts. */ +/****************************************************************************/ + +/** + * \brief Get the number of parts in a style. + * + * This method is the same as the C function OGR_SM_GetPartCount(). + * + * @param pszStyleString (optional) the style string on which to operate. + * If NULL then the current style string stored in the style manager is used. + * + * @return the number of parts (style tools) in the style. + */ + +int OGRStyleMgr::GetPartCount(const char *pszStyleString) +{ + const char *pszPart; + int nPartCount = 1; + const char *pszString; + const char *pszStrTmp; + + if (pszStyleString != NULL) + pszString = pszStyleString; + else + pszString = m_pszStyleString; + + if (pszString == NULL) + return 0; + + pszStrTmp = pszString; + // Search for parts separated by semicolons not counting the possible + // semicolon at the and of string. + while ((pszPart = strstr(pszStrTmp, ";")) != NULL && pszPart[1] != '\0') + { + pszStrTmp = &pszPart[1]; + nPartCount++; + } + return nPartCount; +} + +/************************************************************************/ +/* OGR_SM_GetPartCount() */ +/************************************************************************/ + +/** + * \brief Get the number of parts in a style. + * + * This function is the same as the C++ method OGRStyleMgr::GetPartCount(). + * + * @param hSM handle to the style manager. + * @param pszStyleString (optional) the style string on which to operate. + * If NULL then the current style string stored in the style manager is used. + * + * @return the number of parts (style tools) in the style. + */ + +int OGR_SM_GetPartCount(OGRStyleMgrH hSM, const char *pszStyleString) + +{ + VALIDATE_POINTER1( hSM, "OGR_SM_InitStyleString", FALSE ); + + return ((OGRStyleMgr *) hSM)->GetPartCount(pszStyleString); +} + + +/****************************************************************************/ +/* OGRStyleTool *OGRStyleMgr::GetPart(int nPartId, */ +/* const char *pszStyleString) */ +/* */ +/* Return a StyleTool of the type of the wanted part, could return NULL */ +/****************************************************************************/ + +/** + * \brief Fetch a part (style tool) from the current style. + * + * This method is the same as the C function OGR_SM_GetPart(). + * + * This method instanciates a new object that should be freed with OGR_ST_Destroy(). + * + * @param nPartId the part number (0-based index). + * @param pszStyleString (optional) the style string on which to operate. + * If NULL then the current style string stored in the style manager is used. + * + * @return OGRStyleTool of the requested part (style tools) or NULL on error. + */ + +OGRStyleTool *OGRStyleMgr::GetPart(int nPartId, + const char *pszStyleString) +{ + char **papszStyleString; + const char *pszStyle; + const char *pszString; + OGRStyleTool *poStyleTool = NULL; + + if (pszStyleString) + pszStyle = pszStyleString; + else + pszStyle = m_pszStyleString; + + if (pszStyle == NULL) + return NULL; + + papszStyleString = CSLTokenizeString2(pszStyle, ";", + CSLT_HONOURSTRINGS + | CSLT_PRESERVEQUOTES + | CSLT_PRESERVEESCAPES ); + + pszString = CSLGetField( papszStyleString, nPartId ); + + if ( strlen(pszString) > 0 ) + { + poStyleTool = CreateStyleToolFromStyleString(pszString); + if ( poStyleTool ) + poStyleTool->SetStyleString(pszString); + } + + CSLDestroy( papszStyleString ); + + return poStyleTool; +} + +/************************************************************************/ +/* OGR_SM_GetPart() */ +/************************************************************************/ + +/** + * \brief Fetch a part (style tool) from the current style. + * + * This function is the same as the C++ method OGRStyleMgr::GetPart(). + * + * This function instanciates a new object that should be freed with OGR_ST_Destroy(). + * + * @param hSM handle to the style manager. + * @param nPartId the part number (0-based index). + * @param pszStyleString (optional) the style string on which to operate. + * If NULL then the current style string stored in the style manager is used. + * + * @return OGRStyleToolH of the requested part (style tools) or NULL on error. + */ + +OGRStyleToolH OGR_SM_GetPart(OGRStyleMgrH hSM, int nPartId, + const char *pszStyleString) + +{ + VALIDATE_POINTER1( hSM, "OGR_SM_InitStyleString", NULL ); + + return (OGRStyleToolH) ((OGRStyleMgr *) hSM)->GetPart(nPartId, pszStyleString); +} + + +/****************************************************************************/ +/* OGRStyleTool *CreateStyleToolFromStyleString(const char *pszStyleString) */ +/* */ +/* create a Style tool from the gived StyleString, it should contain only a */ +/* part of a StyleString */ +/****************************************************************************/ +OGRStyleTool *OGRStyleMgr::CreateStyleToolFromStyleString(const char * + pszStyleString) +{ + char **papszToken = CSLTokenizeString2(pszStyleString,"();", + CSLT_HONOURSTRINGS + | CSLT_PRESERVEQUOTES + | CSLT_PRESERVEESCAPES ); + OGRStyleTool *poStyleTool; + + if (CSLCount(papszToken) <2) + poStyleTool = NULL; + else if (EQUAL(papszToken[0],"PEN")) + poStyleTool = new OGRStylePen(); + else if (EQUAL(papszToken[0],"BRUSH")) + poStyleTool = new OGRStyleBrush(); + else if (EQUAL(papszToken[0],"SYMBOL")) + poStyleTool = new OGRStyleSymbol(); + else if (EQUAL(papszToken[0],"LABEL")) + poStyleTool = new OGRStyleLabel(); + else + poStyleTool = NULL; + + CSLDestroy( papszToken ); + + return poStyleTool; +} + +/* ======================================================================== */ +/* OGRStyleTable */ +/* Object Used to manage and store a styletable */ +/* ======================================================================== */ + + +/****************************************************************************/ +/* OGRStyleTable::OGRStyleTable() */ +/* */ +/****************************************************************************/ +OGRStyleTable::OGRStyleTable() +{ + m_papszStyleTable = NULL; + iNextStyle = 0; +} + +/************************************************************************/ +/* OGR_STBL_Create() */ +/************************************************************************/ +/** + * \brief OGRStyleTable factory. + * + * This function is the same as the C++ method OGRStyleTable::OGRStyleTable(). + * + * + * @return an handle to the new style table object. + */ + +OGRStyleTableH OGR_STBL_Create( void ) + +{ + return (OGRStyleTableH) new OGRStyleTable( ); +} + +/****************************************************************************/ +/* void OGRStyleTable::Clear() */ +/* */ +/****************************************************************************/ + +/** + * \brief Clear a style table. + * + */ + +void OGRStyleTable::Clear() +{ + if (m_papszStyleTable) + CSLDestroy(m_papszStyleTable); + m_papszStyleTable = NULL; +} + +/****************************************************************************/ +/* OGRStyleTable::~OGRStyleTable() */ +/* */ +/****************************************************************************/ +OGRStyleTable::~OGRStyleTable() +{ + Clear(); +} + + +/************************************************************************/ +/* OGR_STBL_Destroy() */ +/************************************************************************/ +/** + * \brief Destroy Style Table + * + * @param hSTBL handle to the style table to destroy. + */ + +void OGR_STBL_Destroy( OGRStyleTableH hSTBL ) + +{ + delete (OGRStyleTable *) hSTBL; +} + +/****************************************************************************/ +/* const char *OGRStyleTable::GetStyleName(const char *pszStyleString) */ +/* */ +/* return the Name of a gived stylestring otherwise NULL */ +/****************************************************************************/ + +/** + * \brief Get style name by style string. + * + * @param pszStyleString the style string to look up. + * + * @return the Name of the matching style string or NULL on error. + */ + +const char *OGRStyleTable::GetStyleName(const char *pszStyleString) +{ + int i; + const char *pszStyleStringBegin; + + for (i=0;iAddStyle( pszName, pszStyleString ); +} + +/****************************************************************************/ +/* GBool OGRStyleTable::RemoveStyle(char *pszName) */ +/* */ +/* Remove the gived style in the table based on the name, return TRUE */ +/* on success otherwise FALSE */ +/****************************************************************************/ + +/** + * \brief Remove a style in the table by its name. + * + * @param pszName the name of the style to remove. + * + * @return TRUE on success, FALSE on error + */ + +GBool OGRStyleTable::RemoveStyle(const char *pszName) +{ + int nPos; + if ((nPos = IsExist(pszName)) != -1) + { + m_papszStyleTable = CSLRemoveStrings(m_papszStyleTable,nPos,1,NULL); + return TRUE; + } + return FALSE; +} + +/****************************************************************************/ +/* GBool OGRStyleTable::ModifyStyle(char *pszName, */ +/* char *pszStyleString) */ +/* */ +/* Modify the gived style, if the style doesn't exist, it will be added */ +/* return TRUE on success otherwise return FALSE */ +/****************************************************************************/ + +/** + * \brief Modify a style in the table by its name + * If the style does not exist, it will be added. + * + * @param pszName the name of the style to modify. + * @param pszStyleString the style string. + * + * @return TRUE on success, FALSE on error + */ + +GBool OGRStyleTable::ModifyStyle(const char *pszName, + const char * pszStyleString) +{ + if (pszName == NULL || pszStyleString == NULL) + return FALSE; + + RemoveStyle(pszName); + return AddStyle(pszName, pszStyleString); + +} + +/****************************************************************************/ +/* GBool OGRStyleTable::SaveStyleTable(char *) */ +/* */ +/* Save the StyleTable in the gived file, return TRUE on success */ +/* otherwise return FALSE */ +/****************************************************************************/ + +/** + * \brief Save a style table to a file. + * + * @param pszFilename the name of the file to save to. + * + * @return TRUE on success, FALSE on error + */ + +GBool OGRStyleTable::SaveStyleTable(const char *pszFilename) +{ + if (pszFilename == NULL) + return FALSE; + + if (CSLSave(m_papszStyleTable,pszFilename) == 0) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* OGR_STBL_SaveStyleTable() */ +/************************************************************************/ + +/** + * \brief Save a style table to a file. + * + * This function is the same as the C++ method OGRStyleTable::SaveStyleTable(). + * + * @param hStyleTable handle to the style table. + * @param pszFilename the name of the file to save to. + * + * @return TRUE on success, FALSE on error + */ + +int OGR_STBL_SaveStyleTable( OGRStyleTableH hStyleTable, + const char *pszFilename ) +{ + VALIDATE_POINTER1( hStyleTable, "OGR_STBL_SaveStyleTable", FALSE ); + VALIDATE_POINTER1( pszFilename, "OGR_STBL_SaveStyleTable", FALSE ); + + return ((OGRStyleTable *) hStyleTable)->SaveStyleTable( pszFilename ); +} + +/****************************************************************************/ +/* GBool OGRStyleTable::LoadStyleTable(char *) */ +/* */ +/* Read the Style table from a file, return TRUE on success */ +/* otherwise return FALSE */ +/****************************************************************************/ + +/** + * \brief Load a style table from a file. + * + * @param pszFilename the name of the file to load from. + * + * @return TRUE on success, FALSE on error + */ + +GBool OGRStyleTable::LoadStyleTable(const char *pszFilename) +{ + if (pszFilename == NULL) + return FALSE; + + CSLDestroy(m_papszStyleTable); + + m_papszStyleTable = CSLLoad(pszFilename); + + if (m_papszStyleTable == NULL) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* OGR_STBL_LoadStyleTable() */ +/************************************************************************/ + +/** + * \brief Load a style table from a file. + * + * This function is the same as the C++ method OGRStyleTable::LoadStyleTable(). + * + * @param hStyleTable handle to the style table. + * @param pszFilename the name of the file to load from. + * + * @return TRUE on success, FALSE on error + */ + +int OGR_STBL_LoadStyleTable( OGRStyleTableH hStyleTable, + const char *pszFilename ) +{ + VALIDATE_POINTER1( hStyleTable, "OGR_STBL_LoadStyleTable", FALSE ); + VALIDATE_POINTER1( pszFilename, "OGR_STBL_LoadStyleTable", FALSE ); + + return ((OGRStyleTable *) hStyleTable)->LoadStyleTable( pszFilename ); +} + +/****************************************************************************/ +/* const char *OGRStyleTable::Find(const char *pszName) */ +/* */ +/* return the StyleString based on the gived name, */ +/* otherwise return NULL */ +/****************************************************************************/ + +/** + * \brief Get a style string by name. + * + * @param pszName the name of the style string to find. + * + * @return the style string matching the name, NULL if not found or error. + */ + +const char *OGRStyleTable::Find(const char *pszName) +{ + const char *pszDash = NULL; + const char *pszOutput = NULL; + + int nPos; + if ((nPos = IsExist(pszName)) != -1) + { + + pszOutput = CSLGetField(m_papszStyleTable,nPos); + + pszDash = strstr(pszOutput,":"); + + if (pszDash) + return &pszDash[1]; + } + return NULL; +} + +/************************************************************************/ +/* OGR_STBL_Find() */ +/************************************************************************/ + +/** + * \brief Get a style string by name. + * + * This function is the same as the C++ method OGRStyleTable::Find(). + * + * @param hStyleTable handle to the style table. + * @param pszName the name of the style string to find. + * + * @return the style string matching the name or NULL if not found or error. + */ + +const char *OGR_STBL_Find( OGRStyleTableH hStyleTable, const char *pszName ) +{ + VALIDATE_POINTER1( hStyleTable, "OGR_STBL_Find", FALSE ); + VALIDATE_POINTER1( pszName, "OGR_STBL_Find", FALSE ); + + return ((OGRStyleTable *) hStyleTable)->Find( pszName ); +} + +/****************************************************************************/ +/* OGRStyleTable::Print(FILE *fpOut) */ +/* */ +/****************************************************************************/ + +/** + * \brief Print a style table to a FILE pointer. + * + * @param fpOut the FILE pointer to print to. + * + */ + +void OGRStyleTable::Print(FILE *fpOut) +{ + + VSIFPrintf(fpOut,"#OFS-Version: 1.0\n"); + VSIFPrintf(fpOut,"#StyleField: style\n"); + if (m_papszStyleTable) + { + CSLPrint(m_papszStyleTable,fpOut); + } +} + +/****************************************************************************/ +/* int OGRStyleTable::IsExist(const char *pszName) */ +/* */ +/* return a index of the style in the table otherwise return -1 */ +/****************************************************************************/ + +/** + * \brief Get the index of a style in the table by its name. + * + * @param pszName the name to look for. + * + * @return The index of the style if found, -1 if not found or error. + */ + +int OGRStyleTable::IsExist(const char *pszName) +{ + int i; + int nCount; + const char *pszNewString; + + if (pszName == NULL) + return -1; + + nCount = CSLCount(m_papszStyleTable); + pszNewString = CPLSPrintf("%s:",pszName); + + for (i=0;im_papszStyleTable = CSLDuplicate( m_papszStyleTable ); + + return poNew; +} + +/************************************************************************/ +/* ResetStyleStringReading() */ +/************************************************************************/ + +void OGRStyleTable::ResetStyleStringReading() + +{ + iNextStyle = 0; +} + +/************************************************************************/ +/* OGR_STBL_ResetStyleStringReading() */ +/************************************************************************/ + +/** + * \brief Reset the next style pointer to 0 + * + * This function is the same as the C++ method + * OGRStyleTable::ResetStyleStringReading(). + * + * @param hStyleTable handle to the style table. + * + */ + +void OGR_STBL_ResetStyleStringReading( OGRStyleTableH hStyleTable ) +{ + VALIDATE_POINTER0( hStyleTable, "OGR_STBL_ResetStyleStringReading" ); + + ((OGRStyleTable *) hStyleTable)->ResetStyleStringReading(); +} + +/************************************************************************/ +/* GetNextStyle() */ +/************************************************************************/ + +const char *OGRStyleTable::GetNextStyle() +{ + const char *pszDash = NULL; + const char *pszOutput = NULL; + + while( iNextStyle < CSLCount(m_papszStyleTable) ) + { + + if ( NULL == (pszOutput = CSLGetField(m_papszStyleTable,iNextStyle++))) + continue; + + pszDash = strstr(pszOutput,":"); + + int nColon; + + osLastRequestedStyleName = pszOutput; + nColon = osLastRequestedStyleName.find( ':' ); + if( nColon != -1 ) + osLastRequestedStyleName = + osLastRequestedStyleName.substr(0,nColon); + + if (pszDash) + return pszDash + 1; + } + return NULL; +} + +/************************************************************************/ +/* OGR_STBL_GetNextStyle() */ +/************************************************************************/ + +/** + * \brief Get the next style string from the table. + * + * This function is the same as the C++ method OGRStyleTable::GetNextStyle(). + * + * @param hStyleTable handle to the style table. + * + * @return the next style string or NULL on error. + */ + +const char *OGR_STBL_GetNextStyle( OGRStyleTableH hStyleTable) +{ + VALIDATE_POINTER1( hStyleTable, "OGR_STBL_GetNextStyle", NULL ); + + return ((OGRStyleTable *) hStyleTable)->GetNextStyle(); +} + +/************************************************************************/ +/* GetLastStyleName() */ +/************************************************************************/ + +const char *OGRStyleTable::GetLastStyleName() +{ + return osLastRequestedStyleName; +} + +/************************************************************************/ +/* OGR_STBL_GetLastStyleName() */ +/************************************************************************/ + +/** + * Get the style name of the last style string fetched with + * OGR_STBL_GetNextStyle. + * + * This function is the same as the C++ method OGRStyleTable::GetStyleName(). + * + * @param hStyleTable handle to the style table. + * + * @return the Name of the last style string or NULL on error. + */ + +const char *OGR_STBL_GetLastStyleName( OGRStyleTableH hStyleTable) +{ + VALIDATE_POINTER1( hStyleTable, "OGR_STBL_GetLastStyleName", NULL ); + + return ((OGRStyleTable *) hStyleTable)->GetLastStyleName(); +} + + +/****************************************************************************/ +/* OGRStyleTool::OGRStyleTool() */ +/* */ +/****************************************************************************/ +OGRStyleTool::OGRStyleTool(OGRSTClassId eClassId) +{ + m_eClassId = eClassId; + m_dfScale = 1.0; + m_eUnit = OGRSTUMM; + m_pszStyleString = NULL; + m_bModified = FALSE; + m_bParsed = FALSE; +} + +/************************************************************************/ +/* OGR_ST_Create() */ +/************************************************************************/ +/** + * \brief OGRStyleTool factory. + * + * This function is a constructor for OGRStyleTool derived classes. + * + * @param eClassId subclass of style tool to create. One of OGRSTCPen (1), + * OGRSTCBrush (2), OGRSTCSymbol (3) or OGRSTCLabel (4). + * + * @return an handle to the new style tool object or NULL if the creation + * failed. + */ + +OGRStyleToolH OGR_ST_Create( OGRSTClassId eClassId ) + +{ + switch( eClassId ) + { + case OGRSTCPen: + return (OGRStyleToolH) new OGRStylePen(); + case OGRSTCBrush: + return (OGRStyleToolH) new OGRStyleBrush(); + case OGRSTCSymbol: + return (OGRStyleToolH) new OGRStyleSymbol(); + case OGRSTCLabel: + return (OGRStyleToolH) new OGRStyleLabel(); + default: + return NULL; + } +} + +/****************************************************************************/ +/* OGRStyleTool::~OGRStyleTool() */ +/* */ +/****************************************************************************/ +OGRStyleTool::~OGRStyleTool() +{ + CPLFree(m_pszStyleString); +} + +/************************************************************************/ +/* OGR_ST_Destroy() */ +/************************************************************************/ +/** + * \brief Destroy Style Tool + * + * @param hST handle to the style tool to destroy. + */ + +void OGR_ST_Destroy( OGRStyleToolH hST ) + +{ + delete (OGRStyleTool *) hST; +} + + +/****************************************************************************/ +/* void OGRStyleTool::SetStyleString(const char *pszStyleString) */ +/* */ +/****************************************************************************/ +void OGRStyleTool::SetStyleString(const char *pszStyleString) +{ + m_pszStyleString = CPLStrdup(pszStyleString); +} + +/****************************************************************************/ +/*const char *OGRStyleTool::GetStyleString( OGRStyleParamId *pasStyleParam ,*/ +/* OGRStyleValue *pasStyleValue, int nSize) */ +/* */ +/****************************************************************************/ +const char *OGRStyleTool::GetStyleString(const OGRStyleParamId *pasStyleParam, + OGRStyleValue *pasStyleValue, + int nSize) +{ + if (IsStyleModified()) + { + int i; + GBool bFound; + const char *pszClass; + // FIXME: we should use CPLString instead of static buffer: + char szCurrent[8192]; + szCurrent[0] = '\0'; + + CPLFree(m_pszStyleString); + + switch (GetType()) + { + case OGRSTCPen: + pszClass = "PEN("; + break; + case OGRSTCBrush: + pszClass = "BRUSH("; + break; + case OGRSTCSymbol: + pszClass = "SYMBOL("; + break; + case OGRSTCLabel: + pszClass = "LABEL("; + break; + default: + pszClass = "UNKNOWN("; + } + + strcat(szCurrent,pszClass); + + bFound = FALSE; + for (i=0;i< nSize;i++) + { + if (pasStyleValue[i].bValid == FALSE) + continue; + + if (bFound) + strcat(szCurrent,","); + bFound = TRUE; + + strcat(szCurrent,pasStyleParam[i].pszToken); + switch (pasStyleParam[i].eType) + { + case OGRSTypeString: + strcat(szCurrent,":"); + strcat(szCurrent,pasStyleValue[i].pszValue); + break; + case OGRSTypeDouble: + strcat(szCurrent,CPLString().Printf(":%f",pasStyleValue[i].dfValue)); + break; + case OGRSTypeInteger: + strcat(szCurrent,CPLString().Printf(":%d",pasStyleValue[i].nValue)); + break; + default: + break; + } + if (pasStyleParam[i].bGeoref) + switch (pasStyleValue[i].eUnit) + { + case OGRSTUGround: + strcat(szCurrent,"g"); + break; + case OGRSTUPixel: + strcat(szCurrent,"px"); + break; + case OGRSTUPoints: + strcat(szCurrent,"pt"); + break; + case OGRSTUCM: + strcat(szCurrent,"cm"); + break; + case OGRSTUInches: + strcat(szCurrent,"in"); + break; + case OGRSTUMM: + //strcat(szCurrent,"mm"); + default: + break; //imp + } + } + strcat(szCurrent,")"); + + m_pszStyleString = CPLStrdup(szCurrent); + + m_bModified = FALSE; + } + + return m_pszStyleString; +} + +/************************************************************************/ +/* GetRGBFromString() */ +/************************************************************************/ + +GBool OGRStyleTool::GetRGBFromString(const char *pszColor, int &nRed, + int &nGreen ,int & nBlue, + int &nTransparance) +{ + int nCount=0; + + nTransparance = 255; + + // FIXME: should we really use sscanf here? + if (pszColor) + nCount = sscanf(pszColor,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue, + &nTransparance); + + if (nCount >=3) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetSpecificId() */ +/* */ +/* return -1, if the wanted type is not found, ex: */ +/* if you want ogr-pen value, pszWanted should be ogr-pen(case */ +/* sensitive) */ +/************************************************************************/ + +int OGRStyleTool::GetSpecificId(const char *pszId, const char *pszWanted) +{ + const char *pszRealWanted = pszWanted; + const char *pszFound; + int nValue = -1; + + if (pszWanted == NULL || strlen(pszWanted) == 0) + pszRealWanted = "ogr-pen"; + + if (pszId == NULL) + return -1; + + if ((pszFound = strstr(pszId, pszRealWanted)) != NULL) + { + // We found the string, it could be no value after it, use default one + nValue = 0; + + if (pszFound[strlen(pszRealWanted)] == '-' ) + nValue =atoi(&pszFound[strlen(pszRealWanted)+1]); + } + + return nValue; + +} + +/************************************************************************/ +/* GetType() */ +/************************************************************************/ +OGRSTClassId OGRStyleTool::GetType() +{ + return m_eClassId; +} + +/************************************************************************/ +/* OGR_ST_GetType() */ +/************************************************************************/ +/** + * \brief Determine type of Style Tool + * + * @param hST handle to the style tool. + * + * @return the style tool type, one of OGRSTCPen (1), OGRSTCBrush (2), + * OGRSTCSymbol (3) or OGRSTCLabel (4). Returns OGRSTCNone (0) if the + * OGRStyleToolH is invalid. + */ + +OGRSTClassId OGR_ST_GetType( OGRStyleToolH hST ) + +{ + VALIDATE_POINTER1( hST, "OGR_ST_GetType", OGRSTCNone ); + return ((OGRStyleTool *) hST)->GetType(); +} + + +/************************************************************************/ +/* OGR_ST_GetUnit() */ +/************************************************************************/ +/** + * \brief Get Style Tool units + * + * @param hST handle to the style tool. + * + * @return the style tool units. + */ + +OGRSTUnitId OGR_ST_GetUnit( OGRStyleToolH hST ) + +{ + VALIDATE_POINTER1( hST, "OGR_ST_GetUnit", OGRSTUGround ); + return ((OGRStyleTool *) hST)->GetUnit(); +} + + +/************************************************************************/ +/* SetUnit() */ +/************************************************************************/ +void OGRStyleTool::SetUnit(OGRSTUnitId eUnit,double dfScale) +{ + m_dfScale = dfScale; + m_eUnit = eUnit; +} + +/************************************************************************/ +/* OGR_ST_SetUnit() */ +/************************************************************************/ +/** + * \brief Set Style Tool units + * + * This function is the same as OGRStyleTool::SetUnit() + * + * @param hST handle to the style tool. + * @param eUnit the new unit. + * @param dfGroundPaperScale ground to paper scale factor. + * + */ + +void OGR_ST_SetUnit( OGRStyleToolH hST, OGRSTUnitId eUnit, + double dfGroundPaperScale ) + +{ + VALIDATE_POINTER0( hST, "OGR_ST_SetUnit" ); + ((OGRStyleTool *) hST)->SetUnit(eUnit, dfGroundPaperScale); +} + +/************************************************************************/ +/* Parse() */ +/************************************************************************/ +GBool OGRStyleTool::Parse(const OGRStyleParamId *pasStyle, + OGRStyleValue *pasValue, + int nCount) +{ + char **papszToken; // Token to contains StyleString Type and content + char **papszToken2; // Token that will contains StyleString elements + + + OGRSTUnitId eLastUnit; + + if (IsStyleParsed() == TRUE) + return TRUE; + + StyleParsed(); + + if (m_pszStyleString == NULL) + return FALSE; + + // Tokenize the String to get the Type and the content + // Example: Type(elem1:val2,elem2:val2) + papszToken = CSLTokenizeString2(m_pszStyleString,"()", + CSLT_HONOURSTRINGS + | CSLT_PRESERVEQUOTES + | CSLT_PRESERVEESCAPES ); + + if (CSLCount(papszToken) > 2 || CSLCount(papszToken) == 0) + { + CSLDestroy( papszToken ); + CPLError(CE_Failure, CPLE_AppDefined, + "Error in the format of the StyleTool %s\n",m_pszStyleString); + return FALSE; + } + + // Tokenize the content of the StyleString to get paired components in it. + papszToken2 = CSLTokenizeString2( papszToken[1], ",", + CSLT_HONOURSTRINGS + | CSLT_PRESERVEQUOTES + | CSLT_PRESERVEESCAPES ); + + // Valid that we have the right StyleString for this feature type. + switch (GetType()) + { + case OGRSTCPen: + if (!EQUAL(papszToken[0],"PEN")) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error in the Type of StyleTool %s should be a PEN Type\n", + papszToken[0]); + CSLDestroy( papszToken ); + CSLDestroy( papszToken2 ); + return FALSE; + } + break; + case OGRSTCBrush: + if (!EQUAL(papszToken[0],"BRUSH")) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error in the Type of StyleTool %s should be a BRUSH Type\n", + papszToken[0]); + CSLDestroy( papszToken ); + CSLDestroy( papszToken2 ); + return FALSE; + } + break; + case OGRSTCSymbol: + if (!EQUAL(papszToken[0],"SYMBOL")) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error in the Type of StyleTool %s should be a SYMBOL Type\n", + papszToken[0]); + CSLDestroy( papszToken ); + CSLDestroy( papszToken2 ); + return FALSE; + } + break; + case OGRSTCLabel: + if (!EQUAL(papszToken[0],"LABEL")) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error in the Type of StyleTool %s should be a LABEL Type\n", + papszToken[0]); + CSLDestroy( papszToken ); + CSLDestroy( papszToken2 ); + return FALSE; + } + break; + default: + CPLError(CE_Failure, CPLE_AppDefined, + "Error in the Type of StyleTool, Type undetermined\n"); + CSLDestroy( papszToken ); + CSLDestroy( papszToken2 ); + return FALSE; + break; + } + + //////////////////////////////////////////////////////////////////////// + // Here we will loop on each element in the StyleString. If it's + // a valid element, we will add it in the StyleTool with + // SetParamStr(). + // + // It's important to note that the SetInternalUnit...() is use to update + // the unit of the StyleTool param (m_eUnit). + // See OGRStyleTool::SetParamStr(). + // There's a StyleTool unit (m_eUnit), which is the output unit, and each + // parameter of the style have its own unit value (the input unit). Here we + // set m_eUnit to the input unit and in SetParamStr(), we will use this + // value to set the input unit. Then after the loop we will reset m_eUnit + // to it's original value. (Yes it's a side effect / black magic) + // + // The pasStyle variable is a global variable passed in argument to the + // function. See at the top of this file the four OGRStyleParamId + // variable. They are used to register the valid parameter of each + // StyleTool. + //////////////////////////////////////////////////////////////////////// + + // Save Scale and output Units because the parsing code will alter + // the values + eLastUnit = m_eUnit; + double dSavedScale = m_dfScale; + int i, nElements = CSLCount(papszToken2); + + for ( i = 0; i < nElements; i++ ) + { + char **papszStylePair = + CSLTokenizeString2( papszToken2[i], ":", + CSLT_HONOURSTRINGS + | CSLT_STRIPLEADSPACES + | CSLT_STRIPENDSPACES + | CSLT_ALLOWEMPTYTOKENS ); + + int j, nTokens = CSLCount(papszStylePair); + + if ( nTokens < 1 || nTokens > 2 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Error in the StyleTool String %s", m_pszStyleString ); + CPLError( CE_Warning, CPLE_AppDefined, + "Malformed element #%d (\"%s\") skipped", + i, papszToken2[i] ); + CSLDestroy(papszStylePair); + continue; + } + + for ( j = 0; j < nCount; j++ ) + { + if ( EQUAL(pasStyle[j].pszToken, papszStylePair[0]) ) + { + if (nTokens == 2 && pasStyle[j].bGeoref == TRUE) + SetInternalInputUnitFromParam(papszStylePair[1]); + + // Set either the actual value of style parameter or "1" + // for boolean parameters which do not have values. + // "1" means that boolean parameter is present in the style + // string. + OGRStyleTool::SetParamStr( pasStyle[j], pasValue[j], + (nTokens == 2) ? papszStylePair[1] : "1" ); + + break; + } + } + + CSLDestroy( papszStylePair ); + } + + m_eUnit = eLastUnit; + m_dfScale = dSavedScale; + + CSLDestroy(papszToken2); + CSLDestroy(papszToken); + + return TRUE; +} + +/************************************************************************/ +/* SetInternalInputUnitFromParam() */ +/************************************************************************/ + +void OGRStyleTool::SetInternalInputUnitFromParam(char *pszString) +{ + + char *pszUnit; + + if (pszString == NULL) + return; + pszUnit = strstr(pszString,"g"); + if (pszUnit) + { + SetUnit(OGRSTUGround); + pszUnit[0]= '\0'; + return; + } + pszUnit = strstr(pszString,"px"); + if (pszUnit) + { + SetUnit(OGRSTUPixel); + pszUnit[0]= '\0'; + return; + } + pszUnit = strstr(pszString,"pt"); + if (pszUnit) + { + SetUnit(OGRSTUPoints); + pszUnit[0]= '\0'; + return; + } + pszUnit = strstr(pszString,"mm"); + if (pszUnit) + { + SetUnit(OGRSTUMM); + pszUnit[0]= '\0'; + return; + } + pszUnit = strstr(pszString,"cm"); + if (pszUnit) + { + SetUnit(OGRSTUCM); + pszUnit[0]= '\0'; + return; + } + pszUnit = strstr(pszString,"in"); + if (pszUnit) + { + SetUnit(OGRSTUInches); + pszUnit[0]= '\0'; + return; + } + + SetUnit(OGRSTUMM); +} + +/************************************************************************/ +/* ComputeWithUnit() */ +/************************************************************************/ +double OGRStyleTool::ComputeWithUnit(double dfValue, OGRSTUnitId eInputUnit) +{ + OGRSTUnitId eOutputUnit = GetUnit(); + + double dfNewValue = dfValue; // dfValue in Meter; + + + if (eOutputUnit == eInputUnit) + return dfValue; + + switch (eInputUnit) + { + case OGRSTUGround: + dfNewValue = dfValue / m_dfScale; + break; + case OGRSTUPixel: + dfNewValue = dfValue / (72.0 * 39.37); + break; + case OGRSTUPoints: + dfNewValue =dfValue / (72.0 * 39.37); + break; + case OGRSTUMM: + dfNewValue = 0.001 * dfValue; + break; + case OGRSTUCM: + dfNewValue = 0.01 * dfValue; + break; + case OGRSTUInches: + dfNewValue = dfValue / 39.37; + break; + default: + break; //imp + } + + switch (eOutputUnit) + { + case OGRSTUGround: + dfNewValue *= m_dfScale; + break; + case OGRSTUPixel: + dfNewValue *= (72.0 * 39.37); + break; + case OGRSTUPoints: + dfNewValue *= (72.0 * 39.37); + break; + case OGRSTUMM: + dfNewValue *= 1000.0; + break; + case OGRSTUCM: + dfNewValue *= 100.0; + break; + case OGRSTUInches: + dfNewValue *= 39.37; + break; + default: + break; // imp + } + return dfNewValue; +} + +/************************************************************************/ +/* ComputeWithUnit() */ +/************************************************************************/ +int OGRStyleTool::ComputeWithUnit(int nValue, OGRSTUnitId eUnit) +{ + return (int) ComputeWithUnit((double )nValue, eUnit); +} + +/************************************************************************/ +/* GetParamStr() */ +/************************************************************************/ +const char *OGRStyleTool::GetParamStr(const OGRStyleParamId &sStyleParam , + OGRStyleValue &sStyleValue, + GBool &bValueIsNull) +{ + + if (!Parse()) + { + bValueIsNull = TRUE; + return NULL; + } + + bValueIsNull = !sStyleValue.bValid; + + if (bValueIsNull == TRUE) + return NULL; + + switch (sStyleParam.eType) + { + + // if sStyleParam.bGeoref == TRUE , need to convert to output value; + case OGRSTypeString: + return sStyleValue.pszValue; + case OGRSTypeDouble: + if (sStyleParam.bGeoref) + return CPLSPrintf("%f",ComputeWithUnit(sStyleValue.dfValue, + sStyleValue.eUnit)); + else + return CPLSPrintf("%f",sStyleValue.dfValue); + + case OGRSTypeInteger: + if (sStyleParam.bGeoref) + return CPLSPrintf("%d",ComputeWithUnit(sStyleValue.nValue, + sStyleValue.eUnit)); + else + return CPLSPrintf("%d",sStyleValue.nValue); + case OGRSTypeBoolean: + return CPLSPrintf("%d",sStyleValue.nValue); + default: + bValueIsNull = TRUE; + return NULL; + } +} + +/****************************************************************************/ +/* int OGRStyleTool::GetParamNum(OGRStyleParamId sStyleParam , */ +/* OGRStyleValue sStyleValue, */ +/* GBool &bValueIsNull) */ +/* */ +/****************************************************************************/ +int OGRStyleTool::GetParamNum(const OGRStyleParamId &sStyleParam , + OGRStyleValue &sStyleValue, + GBool &bValueIsNull) +{ + return (int)GetParamDbl(sStyleParam,sStyleValue,bValueIsNull); +} + +/****************************************************************************/ +/* double OGRStyleTool::GetParamDbl(OGRStyleParamId sStyleParam , */ +/* OGRStyleValue sStyleValue, */ +/* GBool &bValueIsNull) */ +/* */ +/****************************************************************************/ +double OGRStyleTool::GetParamDbl(const OGRStyleParamId &sStyleParam , + OGRStyleValue &sStyleValue, + GBool &bValueIsNull) +{ + if (!Parse()) + { + bValueIsNull = TRUE; + return 0; + } + + bValueIsNull = !sStyleValue.bValid; + + if (bValueIsNull == TRUE) + return 0; + + switch (sStyleParam.eType) + { + + // if sStyleParam.bGeoref == TRUE , need to convert to output value; + case OGRSTypeString: + if (sStyleParam.bGeoref) + return ComputeWithUnit(CPLAtof(sStyleValue.pszValue), + sStyleValue.eUnit); + else + return CPLAtof(sStyleValue.pszValue); + case OGRSTypeDouble: + if (sStyleParam.bGeoref) + return ComputeWithUnit(sStyleValue.dfValue, + sStyleValue.eUnit); + else + return sStyleValue.dfValue; + case OGRSTypeInteger: + if (sStyleParam.bGeoref) + return (double)ComputeWithUnit(sStyleValue.nValue, + sStyleValue.eUnit); + else + return (double)sStyleValue.nValue; + case OGRSTypeBoolean: + return (double)sStyleValue.nValue; + default: + bValueIsNull = TRUE; + return 0; + } +} + +/****************************************************************************/ +/* void OGRStyleTool::SetParamStr(OGRStyleParamId &sStyleParam , */ +/* OGRStyleValue &sStyleValue, */ +/* const char *pszParamString) */ +/* */ +/****************************************************************************/ +void OGRStyleTool::SetParamStr(const OGRStyleParamId &sStyleParam , + OGRStyleValue &sStyleValue, + const char *pszParamString) +{ + Parse(); + StyleModified(); + sStyleValue.bValid = TRUE; + sStyleValue.eUnit = GetUnit(); + switch (sStyleParam.eType) + { + + // if sStyleParam.bGeoref == TRUE , need to convert to output value; + case OGRSTypeString: + sStyleValue.pszValue = CPLStrdup(pszParamString); + break; + case OGRSTypeDouble: + sStyleValue.dfValue = CPLAtof(pszParamString); + break; + case OGRSTypeInteger: + case OGRSTypeBoolean: + sStyleValue.nValue = atoi(pszParamString); + break; + default: + sStyleValue.bValid = FALSE; + break; + } +} + +/****************************************************************************/ +/* void OGRStyleTool::SetParamNum(OGRStyleParamId &sStyleParam , */ +/* OGRStyleValue &sStyleValue, */ +/* int nParam) */ +/* */ +/****************************************************************************/ +void OGRStyleTool::SetParamNum(const OGRStyleParamId &sStyleParam , + OGRStyleValue &sStyleValue, + int nParam) +{ + Parse(); + StyleModified(); + sStyleValue.bValid = TRUE; + sStyleValue.eUnit = GetUnit(); + switch (sStyleParam.eType) + { + + // if sStyleParam.bGeoref == TRUE , need to convert to output value; + case OGRSTypeString: + sStyleValue.pszValue = CPLStrdup(CPLString().Printf("%d",nParam)); + break; + case OGRSTypeDouble: + sStyleValue.dfValue = (double)nParam; + break; + case OGRSTypeInteger: + case OGRSTypeBoolean: + sStyleValue.nValue = nParam; + break; + default: + sStyleValue.bValid = FALSE; + break; + } +} + +/****************************************************************************/ +/* void OGRStyleTool::SetParamDbl(OGRStyleParamId &sStyleParam , */ +/* OGRStyleValue &sStyleValue, */ +/* double dfParam) */ +/* */ +/****************************************************************************/ +void OGRStyleTool::SetParamDbl(const OGRStyleParamId &sStyleParam , + OGRStyleValue &sStyleValue, + double dfParam) +{ + Parse(); + StyleModified(); + sStyleValue.bValid = TRUE; + sStyleValue.eUnit = GetUnit(); + switch (sStyleParam.eType) + { + + // if sStyleParam.bGeoref == TRUE , need to convert to output value; + case OGRSTypeString: + sStyleValue.pszValue = CPLStrdup(CPLString().Printf("%f",dfParam)); + break; + case OGRSTypeDouble: + sStyleValue.dfValue = dfParam; + break; + case OGRSTypeInteger: + case OGRSTypeBoolean: + sStyleValue.nValue = (int)dfParam; + break; + default: + sStyleValue.bValid = FALSE; + break; + } +} + +/************************************************************************/ +/* OGR_ST_GetParamStr() */ +/************************************************************************/ +/** + * \brief Get Style Tool parameter value as string + * + * Maps to the OGRStyleTool subclasses' GetParamStr() methods. + * + * @param hST handle to the style tool. + * @param eParam the parameter id from the enumeration corresponding to the + * type of this style tool (one of the OGRSTPenParam, OGRSTBrushParam, + * OGRSTSymbolParam or OGRSTLabelParam enumerations) + * @param bValueIsNull pointer to an integer that will be set to TRUE or FALSE + * to indicate whether the parameter value is NULL. + * + * @return the parameter value as string and sets bValueIsNull. + */ + +const char *OGR_ST_GetParamStr( OGRStyleToolH hST, int eParam, int *bValueIsNull ) +{ + GBool bIsNull = TRUE; + const char *pszVal = ""; + + VALIDATE_POINTER1( hST, "OGR_ST_GetParamStr", "" ); + VALIDATE_POINTER1( bValueIsNull, "OGR_ST_GetParamStr", "" ); + + switch( ((OGRStyleTool *) hST)->GetType() ) + { + case OGRSTCPen: + pszVal = ((OGRStylePen *) hST)->GetParamStr((OGRSTPenParam)eParam, + bIsNull); + break; + case OGRSTCBrush: + pszVal = ((OGRStyleBrush *) hST)->GetParamStr((OGRSTBrushParam)eParam, + bIsNull); + break; + case OGRSTCSymbol: + pszVal = ((OGRStyleSymbol *) hST)->GetParamStr((OGRSTSymbolParam)eParam, + bIsNull); + break; + case OGRSTCLabel: + pszVal = ((OGRStyleLabel *) hST)->GetParamStr((OGRSTLabelParam)eParam, + bIsNull); + break; + default: + break; + } + + *bValueIsNull = bIsNull; + return pszVal; +} + +/************************************************************************/ +/* OGR_ST_GetParamNum() */ +/************************************************************************/ +/** + * \brief Get Style Tool parameter value as an integer + * + * Maps to the OGRStyleTool subclasses' GetParamNum() methods. + * + * @param hST handle to the style tool. + * @param eParam the parameter id from the enumeration corresponding to the + * type of this style tool (one of the OGRSTPenParam, OGRSTBrushParam, + * OGRSTSymbolParam or OGRSTLabelParam enumerations) + * @param bValueIsNull pointer to an integer that will be set to TRUE or FALSE + * to indicate whether the parameter value is NULL. + * + * @return the parameter value as integer and sets bValueIsNull. + */ + +int OGR_ST_GetParamNum( OGRStyleToolH hST, int eParam, int *bValueIsNull ) +{ + GBool bIsNull = TRUE; + int nVal = 0; + + VALIDATE_POINTER1( hST, "OGR_ST_GetParamNum", 0 ); + VALIDATE_POINTER1( bValueIsNull, "OGR_ST_GetParamNum", 0 ); + + switch( ((OGRStyleTool *) hST)->GetType() ) + { + case OGRSTCPen: + nVal = ((OGRStylePen *) hST)->GetParamNum((OGRSTPenParam)eParam, + bIsNull); + break; + case OGRSTCBrush: + nVal = ((OGRStyleBrush *) hST)->GetParamNum((OGRSTBrushParam)eParam, + bIsNull); + break; + case OGRSTCSymbol: + nVal = ((OGRStyleSymbol *) hST)->GetParamNum((OGRSTSymbolParam)eParam, + bIsNull); + break; + case OGRSTCLabel: + nVal = ((OGRStyleLabel *) hST)->GetParamNum((OGRSTLabelParam)eParam, + bIsNull); + break; + default: + break; + } + + *bValueIsNull = bIsNull; + return nVal; +} + +/************************************************************************/ +/* OGR_ST_GetParamDbl() */ +/************************************************************************/ +/** + * \brief Get Style Tool parameter value as a double + * + * Maps to the OGRStyleTool subclasses' GetParamDbl() methods. + * + * @param hST handle to the style tool. + * @param eParam the parameter id from the enumeration corresponding to the + * type of this style tool (one of the OGRSTPenParam, OGRSTBrushParam, + * OGRSTSymbolParam or OGRSTLabelParam enumerations) + * @param bValueIsNull pointer to an integer that will be set to TRUE or FALSE + * to indicate whether the parameter value is NULL. + * + * @return the parameter value as double and sets bValueIsNull. + */ + +double OGR_ST_GetParamDbl( OGRStyleToolH hST, int eParam, int *bValueIsNull ) +{ + GBool bIsNull = TRUE; + double dfVal = 0.0; + + VALIDATE_POINTER1( hST, "OGR_ST_GetParamDbl", 0.0 ); + VALIDATE_POINTER1( bValueIsNull, "OGR_ST_GetParamDbl", 0.0 ); + + switch( ((OGRStyleTool *) hST)->GetType() ) + { + case OGRSTCPen: + dfVal = ((OGRStylePen *) hST)->GetParamDbl((OGRSTPenParam)eParam, + bIsNull); + break; + case OGRSTCBrush: + dfVal = ((OGRStyleBrush *) hST)->GetParamDbl((OGRSTBrushParam)eParam, + bIsNull); + break; + case OGRSTCSymbol: + dfVal = ((OGRStyleSymbol *) hST)->GetParamDbl((OGRSTSymbolParam)eParam, + bIsNull); + break; + case OGRSTCLabel: + dfVal = ((OGRStyleLabel *) hST)->GetParamDbl((OGRSTLabelParam)eParam, + bIsNull); + break; + default: + break; + } + + *bValueIsNull = bIsNull; + return dfVal; +} + + +/************************************************************************/ +/* OGR_ST_SetParamStr() */ +/************************************************************************/ +/** + * \brief Set Style Tool parameter value from a string + * + * Maps to the OGRStyleTool subclasses' SetParamStr() methods. + * + * @param hST handle to the style tool. + * @param eParam the parameter id from the enumeration corresponding to the + * type of this style tool (one of the OGRSTPenParam, OGRSTBrushParam, + * OGRSTSymbolParam or OGRSTLabelParam enumerations) + * @param pszValue the new parameter value + * + */ + +void OGR_ST_SetParamStr( OGRStyleToolH hST, int eParam, const char *pszValue ) +{ + VALIDATE_POINTER0( hST, "OGR_ST_SetParamStr" ); + VALIDATE_POINTER0( pszValue, "OGR_ST_SetParamStr" ); + + switch( ((OGRStyleTool *) hST)->GetType() ) + { + case OGRSTCPen: + ((OGRStylePen *) hST)->SetParamStr((OGRSTPenParam)eParam, + pszValue); + break; + case OGRSTCBrush: + ((OGRStyleBrush *) hST)->SetParamStr((OGRSTBrushParam)eParam, + pszValue); + break; + case OGRSTCSymbol: + ((OGRStyleSymbol *) hST)->SetParamStr((OGRSTSymbolParam)eParam, + pszValue); + break; + case OGRSTCLabel: + ((OGRStyleLabel *) hST)->SetParamStr((OGRSTLabelParam)eParam, + pszValue); + break; + default: + break; + } +} + + +/************************************************************************/ +/* OGR_ST_SetParamNum() */ +/************************************************************************/ +/** + * \brief Set Style Tool parameter value from an integer + * + * Maps to the OGRStyleTool subclasses' SetParamNum() methods. + * + * @param hST handle to the style tool. + * @param eParam the parameter id from the enumeration corresponding to the + * type of this style tool (one of the OGRSTPenParam, OGRSTBrushParam, + * OGRSTSymbolParam or OGRSTLabelParam enumerations) + * @param nValue the new parameter value + * + */ + +void OGR_ST_SetParamNum( OGRStyleToolH hST, int eParam, int nValue ) +{ + VALIDATE_POINTER0( hST, "OGR_ST_SetParamNum" ); + + switch( ((OGRStyleTool *) hST)->GetType() ) + { + case OGRSTCPen: + ((OGRStylePen *) hST)->SetParamNum((OGRSTPenParam)eParam, + nValue); + break; + case OGRSTCBrush: + ((OGRStyleBrush *) hST)->SetParamNum((OGRSTBrushParam)eParam, + nValue); + break; + case OGRSTCSymbol: + ((OGRStyleSymbol *) hST)->SetParamNum((OGRSTSymbolParam)eParam, + nValue); + break; + case OGRSTCLabel: + ((OGRStyleLabel *) hST)->SetParamNum((OGRSTLabelParam)eParam, + nValue); + break; + default: + break; + } +} + +/************************************************************************/ +/* OGR_ST_SetParamDbl() */ +/************************************************************************/ +/** + * \brief Set Style Tool parameter value from a double + * + * Maps to the OGRStyleTool subclasses' SetParamDbl() methods. + * + * @param hST handle to the style tool. + * @param eParam the parameter id from the enumeration corresponding to the + * type of this style tool (one of the OGRSTPenParam, OGRSTBrushParam, + * OGRSTSymbolParam or OGRSTLabelParam enumerations) + * @param dfValue the new parameter value + * + */ + +void OGR_ST_SetParamDbl( OGRStyleToolH hST, int eParam, double dfValue ) +{ + VALIDATE_POINTER0( hST, "OGR_ST_SetParamDbl" ); + + switch( ((OGRStyleTool *) hST)->GetType() ) + { + case OGRSTCPen: + ((OGRStylePen *) hST)->SetParamDbl((OGRSTPenParam)eParam, + dfValue); + break; + case OGRSTCBrush: + ((OGRStyleBrush *) hST)->SetParamDbl((OGRSTBrushParam)eParam, + dfValue); + break; + case OGRSTCSymbol: + ((OGRStyleSymbol *) hST)->SetParamDbl((OGRSTSymbolParam)eParam, + dfValue); + break; + case OGRSTCLabel: + ((OGRStyleLabel *) hST)->SetParamDbl((OGRSTLabelParam)eParam, + dfValue); + break; + default: + break; + } +} + + +/************************************************************************/ +/* OGR_ST_GetStyleString() */ +/************************************************************************/ +/** + * \brief Get the style string for this Style Tool + * + * Maps to the OGRStyleTool subclasses' GetStyleString() methods. + * + * @param hST handle to the style tool. + * + * @return the style string for this style tool or "" if the hST is invalid. + */ + +const char *OGR_ST_GetStyleString( OGRStyleToolH hST ) +{ + const char *pszVal = ""; + + VALIDATE_POINTER1( hST, "OGR_ST_GetStyleString", "" ); + + switch( ((OGRStyleTool *) hST)->GetType() ) + { + case OGRSTCPen: + pszVal = ((OGRStylePen *) hST)->GetStyleString(); + break; + case OGRSTCBrush: + pszVal = ((OGRStyleBrush *) hST)->GetStyleString(); + break; + case OGRSTCSymbol: + pszVal = ((OGRStyleSymbol *) hST)->GetStyleString(); + break; + case OGRSTCLabel: + pszVal = ((OGRStyleLabel *) hST)->GetStyleString(); + break; + default: + break; + } + + return pszVal; +} + +/************************************************************************/ +/* OGR_ST_GetRGBFromString() */ +/************************************************************************/ +/** + * \brief Return the r,g,b,a components of a color encoded in \#RRGGBB[AA] format + * + * Maps to OGRStyleTool::GetRGBFromString(). + * + * @param hST handle to the style tool. + * @param pszColor the color to parse + * @param pnRed pointer to an int in which the red value will be returned + * @param pnGreen pointer to an int in which the green value will be returned + * @param pnBlue pointer to an int in which the blue value will be returned + * @param pnAlpha pointer to an int in which the (optional) alpha value will + * be returned + * + * @return TRUE if the color could be succesfully parsed, or FALSE in case of + * errors. + */ + +int OGR_ST_GetRGBFromString( OGRStyleToolH hST, const char *pszColor, + int *pnRed, int *pnGreen, int *pnBlue, + int *pnAlpha ) +{ + + VALIDATE_POINTER1( hST, "OGR_ST_GetRGBFromString", FALSE ); + VALIDATE_POINTER1( pnRed, "OGR_ST_GetRGBFromString", FALSE ); + VALIDATE_POINTER1( pnGreen, "OGR_ST_GetRGBFromString", FALSE ); + VALIDATE_POINTER1( pnBlue, "OGR_ST_GetRGBFromString", FALSE ); + VALIDATE_POINTER1( pnAlpha, "OGR_ST_GetRGBFromString", FALSE ); + + return ((OGRStyleTool *) hST)->GetRGBFromString(pszColor, *pnRed, *pnGreen, + *pnBlue, *pnAlpha ); +} + + +/* ======================================================================== */ +/* OGRStylePen */ +/* Specific parameter (Set/Get) for the StylePen */ +/* ======================================================================== */ + + +/****************************************************************************/ +/* OGRStylePen::OGRStylePen() */ +/* */ +/****************************************************************************/ +OGRStylePen::OGRStylePen() : OGRStyleTool(OGRSTCPen) +{ + m_pasStyleValue = (OGRStyleValue *)CPLCalloc(OGRSTPenLast, + sizeof(OGRStyleValue)); +} + + + + +/****************************************************************************/ +/* OGRStylePen::~OGRStylePen() */ +/* */ +/****************************************************************************/ +OGRStylePen::~OGRStylePen() +{ + for (int i = 0; i < OGRSTPenLast; i++) + { + if (m_pasStyleValue[i].pszValue != NULL) + { + CPLFree(m_pasStyleValue[i].pszValue); + m_pasStyleValue[i].pszValue = NULL; + } + } + + CPLFree(m_pasStyleValue); +} + +/************************************************************************/ +/* OGRStylePen::Parse() */ +/************************************************************************/ +GBool OGRStylePen::Parse() + +{ + return OGRStyleTool::Parse(asStylePen,m_pasStyleValue,(int)OGRSTPenLast); +} + +/************************************************************************/ +/* GetParamStr() */ +/************************************************************************/ +const char *OGRStylePen::GetParamStr(OGRSTPenParam eParam, GBool &bValueIsNull) +{ + return OGRStyleTool::GetParamStr(asStylePen[eParam], + m_pasStyleValue[eParam], + bValueIsNull); +} + +/************************************************************************/ +/* GetParamNum() */ +/************************************************************************/ +int OGRStylePen::GetParamNum(OGRSTPenParam eParam,GBool &bValueIsNull) +{ + return OGRStyleTool::GetParamNum(asStylePen[eParam], + m_pasStyleValue[eParam],bValueIsNull); +} + +/************************************************************************/ +/* GetParamDbl() */ +/************************************************************************/ +double OGRStylePen::GetParamDbl(OGRSTPenParam eParam,GBool &bValueIsNull) +{ + return OGRStyleTool::GetParamDbl(asStylePen[eParam], + m_pasStyleValue[eParam],bValueIsNull); +} + +/************************************************************************/ +/* SetParamStr() */ +/************************************************************************/ + +void OGRStylePen::SetParamStr(OGRSTPenParam eParam, const char *pszParamString) +{ + OGRStyleTool::SetParamStr(asStylePen[eParam],m_pasStyleValue[eParam], + pszParamString); +} + +/************************************************************************/ +/* SetParamNum() */ +/************************************************************************/ +void OGRStylePen::SetParamNum(OGRSTPenParam eParam, int nParam) +{ + OGRStyleTool::SetParamNum(asStylePen[eParam], + m_pasStyleValue[eParam],nParam); +} + +/************************************************************************/ +/* SetParamDbl() */ +/************************************************************************/ +void OGRStylePen::SetParamDbl(OGRSTPenParam eParam, double dfParam) +{ + OGRStyleTool::SetParamDbl(asStylePen[eParam], + m_pasStyleValue[eParam],dfParam); +} + +/************************************************************************/ +/* GetStyleString() */ +/************************************************************************/ +const char *OGRStylePen::GetStyleString() +{ + return OGRStyleTool::GetStyleString(asStylePen,m_pasStyleValue, + (int)OGRSTPenLast); +} + +/****************************************************************************/ +/* OGRStyleBrush::OGRStyleBrush() */ +/* */ +/****************************************************************************/ +OGRStyleBrush::OGRStyleBrush() : OGRStyleTool(OGRSTCBrush) +{ + m_pasStyleValue = (OGRStyleValue *)CPLCalloc(OGRSTBrushLast, + sizeof(OGRStyleValue)); +} + +/****************************************************************************/ +/* OGRStyleBrush::~OGRStyleBrush() */ +/* */ +/****************************************************************************/ +OGRStyleBrush::~OGRStyleBrush() +{ + for (int i = 0; i < OGRSTBrushLast; i++) + { + if (m_pasStyleValue[i].pszValue != NULL) + { + CPLFree(m_pasStyleValue[i].pszValue); + m_pasStyleValue[i].pszValue = NULL; + } + } + + CPLFree(m_pasStyleValue); +} + +/************************************************************************/ +/* Parse() */ +/************************************************************************/ +GBool OGRStyleBrush::Parse() +{ + return OGRStyleTool::Parse(asStyleBrush,m_pasStyleValue, + (int)OGRSTBrushLast); +} + +/************************************************************************/ +/* GetParamStr() */ +/************************************************************************/ +const char *OGRStyleBrush::GetParamStr(OGRSTBrushParam eParam, GBool &bValueIsNull) +{ + return OGRStyleTool::GetParamStr(asStyleBrush[eParam], + m_pasStyleValue[eParam], + bValueIsNull); +} + +/************************************************************************/ +/* GetParamNum() */ +/************************************************************************/ +int OGRStyleBrush::GetParamNum(OGRSTBrushParam eParam,GBool &bValueIsNull) +{ + return OGRStyleTool::GetParamNum(asStyleBrush[eParam], + m_pasStyleValue[eParam],bValueIsNull); +} + +/************************************************************************/ +/* GetParamDbl() */ +/************************************************************************/ +double OGRStyleBrush::GetParamDbl(OGRSTBrushParam eParam,GBool &bValueIsNull) +{ + return OGRStyleTool::GetParamDbl(asStyleBrush[eParam], + m_pasStyleValue[eParam],bValueIsNull); +} + +/************************************************************************/ +/* SetParamStr() */ +/************************************************************************/ +void OGRStyleBrush::SetParamStr(OGRSTBrushParam eParam, const char *pszParamString) +{ + OGRStyleTool::SetParamStr(asStyleBrush[eParam],m_pasStyleValue[eParam], + pszParamString); +} + +/************************************************************************/ +/* SetParamNum() */ +/************************************************************************/ +void OGRStyleBrush::SetParamNum(OGRSTBrushParam eParam, int nParam) +{ + OGRStyleTool::SetParamNum(asStyleBrush[eParam], + m_pasStyleValue[eParam],nParam); +} + +/************************************************************************/ +/* SetParamDbl() */ +/************************************************************************/ +void OGRStyleBrush::SetParamDbl(OGRSTBrushParam eParam, double dfParam) +{ + OGRStyleTool::SetParamDbl(asStyleBrush[eParam], + m_pasStyleValue[eParam],dfParam); +} + +/************************************************************************/ +/* GetStyleString() */ +/************************************************************************/ +const char *OGRStyleBrush::GetStyleString() +{ + return OGRStyleTool::GetStyleString(asStyleBrush,m_pasStyleValue, + (int)OGRSTBrushLast); +} + +/****************************************************************************/ +/* OGRStyleSymbol::OGRStyleSymbol() */ +/****************************************************************************/ +OGRStyleSymbol::OGRStyleSymbol() : OGRStyleTool(OGRSTCSymbol) +{ + m_pasStyleValue = (OGRStyleValue *)CPLCalloc(OGRSTSymbolLast, + sizeof(OGRStyleValue)); +} + +/****************************************************************************/ +/* OGRStyleSymbol::~OGRStyleSymbol() */ +/* */ +/****************************************************************************/ +OGRStyleSymbol::~OGRStyleSymbol() +{ + for (int i = 0; i < OGRSTSymbolLast; i++) + { + if (m_pasStyleValue[i].pszValue != NULL) + { + CPLFree(m_pasStyleValue[i].pszValue); + m_pasStyleValue[i].pszValue = NULL; + } + } + + CPLFree(m_pasStyleValue); +} + +/************************************************************************/ +/* Parse() */ +/************************************************************************/ +GBool OGRStyleSymbol::Parse() +{ + return OGRStyleTool::Parse(asStyleSymbol,m_pasStyleValue, + (int)OGRSTSymbolLast); +} + +/************************************************************************/ +/* GetParamStr() */ +/************************************************************************/ +const char *OGRStyleSymbol::GetParamStr(OGRSTSymbolParam eParam, GBool &bValueIsNull) +{ return OGRStyleTool::GetParamStr(asStyleSymbol[eParam], + m_pasStyleValue[eParam], + bValueIsNull); +} +/************************************************************************/ +/* GetParamNum() */ +/************************************************************************/ +int OGRStyleSymbol::GetParamNum(OGRSTSymbolParam eParam,GBool &bValueIsNull) +{ return OGRStyleTool::GetParamNum(asStyleSymbol[eParam], + m_pasStyleValue[eParam],bValueIsNull); +} +/************************************************************************/ +/* GetParamDbl() */ +/************************************************************************/ +double OGRStyleSymbol::GetParamDbl(OGRSTSymbolParam eParam,GBool &bValueIsNull) +{ return OGRStyleTool::GetParamDbl(asStyleSymbol[eParam], + m_pasStyleValue[eParam],bValueIsNull); +} + +/************************************************************************/ +/* SetParamStr() */ +/************************************************************************/ +void OGRStyleSymbol::SetParamStr(OGRSTSymbolParam eParam, const char *pszParamString) +{ OGRStyleTool::SetParamStr(asStyleSymbol[eParam],m_pasStyleValue[eParam], + pszParamString); +} + +/************************************************************************/ +/* SetParamNum() */ +/************************************************************************/ +void OGRStyleSymbol::SetParamNum(OGRSTSymbolParam eParam, int nParam) +{ OGRStyleTool::SetParamNum(asStyleSymbol[eParam], + m_pasStyleValue[eParam],nParam); +} + +/************************************************************************/ +/* SetParamDbl() */ +/************************************************************************/ +void OGRStyleSymbol::SetParamDbl(OGRSTSymbolParam eParam, double dfParam) + { OGRStyleTool::SetParamDbl(asStyleSymbol[eParam], + m_pasStyleValue[eParam],dfParam); + } +/************************************************************************/ +/* GetStyleString() */ +/************************************************************************/ +const char *OGRStyleSymbol::GetStyleString() +{ + return OGRStyleTool::GetStyleString(asStyleSymbol,m_pasStyleValue, + (int)OGRSTSymbolLast); +} + + +/****************************************************************************/ +/* OGRStyleLabel::OGRStyleLabel() */ +/* */ +/****************************************************************************/ +OGRStyleLabel::OGRStyleLabel() : OGRStyleTool(OGRSTCLabel) +{ + m_pasStyleValue = (OGRStyleValue *)CPLCalloc(OGRSTLabelLast, + sizeof(OGRStyleValue)); +} + +/****************************************************************************/ +/* OGRStyleLabel::~OGRStyleLabel() */ +/* */ +/****************************************************************************/ +OGRStyleLabel::~OGRStyleLabel() +{ + for (int i = 0; i < OGRSTLabelLast; i++) + { + if (m_pasStyleValue[i].pszValue != NULL) + { + CPLFree(m_pasStyleValue[i].pszValue); + m_pasStyleValue[i].pszValue = NULL; + } + } + + CPLFree(m_pasStyleValue); +} + +/************************************************************************/ +/* Parse() */ +/************************************************************************/ +GBool OGRStyleLabel::Parse() +{ return OGRStyleTool::Parse(asStyleLabel,m_pasStyleValue, + (int)OGRSTLabelLast); +} + +/************************************************************************/ +/* GetParamStr() */ +/************************************************************************/ +const char *OGRStyleLabel::GetParamStr(OGRSTLabelParam eParam, GBool &bValueIsNull) +{ return OGRStyleTool::GetParamStr(asStyleLabel[eParam], + m_pasStyleValue[eParam], + bValueIsNull); +} +/************************************************************************/ +/* GetParamNum() */ +/************************************************************************/ +int OGRStyleLabel::GetParamNum(OGRSTLabelParam eParam,GBool &bValueIsNull) +{ return OGRStyleTool::GetParamNum(asStyleLabel[eParam], + m_pasStyleValue[eParam],bValueIsNull); +} +/************************************************************************/ +/* GetParamDbl() */ +/************************************************************************/ +double OGRStyleLabel::GetParamDbl(OGRSTLabelParam eParam,GBool &bValueIsNull) +{ return OGRStyleTool::GetParamDbl(asStyleLabel[eParam], + m_pasStyleValue[eParam],bValueIsNull); +} +/************************************************************************/ +/* SetParamStr() */ +/************************************************************************/ +void OGRStyleLabel::SetParamStr(OGRSTLabelParam eParam, const char *pszParamString) +{ OGRStyleTool::SetParamStr(asStyleLabel[eParam],m_pasStyleValue[eParam], + pszParamString); +} +/************************************************************************/ +/* SetParamNum() */ +/************************************************************************/ +void OGRStyleLabel::SetParamNum(OGRSTLabelParam eParam, int nParam) +{ OGRStyleTool::SetParamNum(asStyleLabel[eParam], + m_pasStyleValue[eParam],nParam); +} + +/************************************************************************/ +/* SetParamDbl() */ +/************************************************************************/ +void OGRStyleLabel::SetParamDbl(OGRSTLabelParam eParam, double dfParam) +{ OGRStyleTool::SetParamDbl(asStyleLabel[eParam], + m_pasStyleValue[eParam],dfParam); +} +/************************************************************************/ +/* GetStyleString() */ +/************************************************************************/ +const char *OGRStyleLabel::GetStyleString() +{ return OGRStyleTool::GetStyleString(asStyleLabel,m_pasStyleValue, + (int)OGRSTLabelLast); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrfielddefn.cpp b/bazaar/plugin/gdal/ogr/ogrfielddefn.cpp new file mode 100644 index 000000000..20e615a29 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrfielddefn.cpp @@ -0,0 +1,1216 @@ +/****************************************************************************** + * $Id: ogrfielddefn.cpp 28806 2015-03-28 14:37:47Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRFieldDefn class implementation. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_feature.h" +#include "ogr_api.h" +#include "ogr_p.h" +#include "ograpispy.h" + +CPL_CVSID("$Id: ogrfielddefn.cpp 28806 2015-03-28 14:37:47Z rouault $"); + +/************************************************************************/ +/* OGRFieldDefn() */ +/************************************************************************/ + +/** + * \brief Constructor. + * + * By default, fields have no width, precision, are nullable and not ignored. + * + * @param pszNameIn the name of the new field. + * @param eTypeIn the type of the new field. + */ + +OGRFieldDefn::OGRFieldDefn( const char * pszNameIn, OGRFieldType eTypeIn ) + +{ + Initialize( pszNameIn, eTypeIn ); +} + +/************************************************************************/ +/* OGRFieldDefn() */ +/************************************************************************/ + +/** + * \brief Constructor. + * + * Create by cloning an existing field definition. + * + * @param poPrototype the field definition to clone. + */ + +OGRFieldDefn::OGRFieldDefn( OGRFieldDefn *poPrototype ) + +{ + Initialize( poPrototype->GetNameRef(), poPrototype->GetType() ); + + SetJustify( poPrototype->GetJustify() ); + SetWidth( poPrototype->GetWidth() ); + SetPrecision( poPrototype->GetPrecision() ); + SetSubType( poPrototype->GetSubType() ); + SetNullable( poPrototype->IsNullable() ); + SetDefault( poPrototype->GetDefault() ); +} + +/************************************************************************/ +/* OGR_Fld_Create() */ +/************************************************************************/ +/** + * \brief Create a new field definition. + * + * By default, fields have no width, precision, are nullable and not ignored. + * + * This function is the same as the CPP method OGRFieldDefn::OGRFieldDefn(). + * + * @param pszName the name of the new field definition. + * @param eType the type of the new field definition. + * @return handle to the new field definition. + */ + +OGRFieldDefnH OGR_Fld_Create( const char *pszName, OGRFieldType eType ) + +{ + return (OGRFieldDefnH) (new OGRFieldDefn(pszName,eType)); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void OGRFieldDefn::Initialize( const char * pszNameIn, OGRFieldType eTypeIn ) + +{ + pszName = CPLStrdup( pszNameIn ); + eType = eTypeIn; + eJustify = OJUndefined; + + nWidth = 0; // should these be defined in some particular way + nPrecision = 0; // for numbers? + + pszDefault = NULL; + bIgnore = FALSE; + eSubType = OFSTNone; + bNullable = TRUE; +} + +/************************************************************************/ +/* ~OGRFieldDefn() */ +/************************************************************************/ + +OGRFieldDefn::~OGRFieldDefn() + +{ + CPLFree( pszName ); + CPLFree( pszDefault ); +} + +/************************************************************************/ +/* OGR_Fld_Destroy() */ +/************************************************************************/ +/** + * \brief Destroy a field definition. + * + * @param hDefn handle to the field definition to destroy. + */ + +void OGR_Fld_Destroy( OGRFieldDefnH hDefn ) + +{ + delete (OGRFieldDefn *) hDefn; +} + +/************************************************************************/ +/* SetName() */ +/************************************************************************/ + +/** + * \brief Reset the name of this field. + * + * This method is the same as the C function OGR_Fld_SetName(). + * + * @param pszNameIn the new name to apply. + */ + +void OGRFieldDefn::SetName( const char * pszNameIn ) + +{ + CPLFree( pszName ); + pszName = CPLStrdup( pszNameIn ); +} + +/************************************************************************/ +/* OGR_Fld_SetName() */ +/************************************************************************/ +/** + * \brief Reset the name of this field. + * + * This function is the same as the CPP method OGRFieldDefn::SetName(). + * + * @param hDefn handle to the field definition to apply the new name to. + * @param pszName the new name to apply. + */ + +void OGR_Fld_SetName( OGRFieldDefnH hDefn, const char *pszName ) + +{ + ((OGRFieldDefn *) hDefn)->SetName( pszName ); +} + +/************************************************************************/ +/* GetNameRef() */ +/************************************************************************/ + +/** + * \fn const char *OGRFieldDefn::GetNameRef(); + * + * \brief Fetch name of this field. + * + * This method is the same as the C function OGR_Fld_GetNameRef(). + * + * @return pointer to an internal name string that should not be freed or + * modified. + */ + +/************************************************************************/ +/* OGR_Fld_GetNameRef() */ +/************************************************************************/ +/** + * \brief Fetch name of this field. + * + * This function is the same as the CPP method OGRFieldDefn::GetNameRef(). + * + * @param hDefn handle to the field definition. + * @return the name of the field definition. + * + */ + +const char *OGR_Fld_GetNameRef( OGRFieldDefnH hDefn ) + +{ + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_Fld_GetXXXX(hDefn, "GetNameRef"); +#endif + + return ((OGRFieldDefn *) hDefn)->GetNameRef(); +} + +/************************************************************************/ +/* GetType() */ +/************************************************************************/ + +/** + * \fn OGRFieldType OGRFieldDefn::GetType(); + * + * \brief Fetch type of this field. + * + * This method is the same as the C function OGR_Fld_GetType(). + * + * @return field type. + */ + +/************************************************************************/ +/* OGR_Fld_GetType() */ +/************************************************************************/ +/** + * \brief Fetch type of this field. + * + * This function is the same as the CPP method OGRFieldDefn::GetType(). + * + * @param hDefn handle to the field definition to get type from. + * @return field type. + */ + +OGRFieldType OGR_Fld_GetType( OGRFieldDefnH hDefn ) + +{ + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_Fld_GetXXXX(hDefn, "GetType"); +#endif + + return ((OGRFieldDefn *) hDefn)->GetType(); +} + +/************************************************************************/ +/* SetType() */ +/************************************************************************/ + +/** + * \brief Set the type of this field. + * This should never be done to an OGRFieldDefn + * that is already part of an OGRFeatureDefn. + * + * This method is the same as the C function OGR_Fld_SetType(). + * + * @param eType the new field type. + */ + +void OGRFieldDefn::SetType( OGRFieldType eTypeIn ) +{ + if( !OGR_AreTypeSubTypeCompatible(eTypeIn, eSubType) ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Type and subtype of field definition are not compatible. Reseting to OFSTNone"); + eSubType = OFSTNone; + } + eType = eTypeIn; +} + + +/************************************************************************/ +/* OGR_Fld_SetType() */ +/************************************************************************/ +/** + * \brief Set the type of this field. + * This should never be done to an OGRFieldDefn + * that is already part of an OGRFeatureDefn. + * + * This function is the same as the CPP method OGRFieldDefn::SetType(). + * + * @param hDefn handle to the field definition to set type to. + * @param eType the new field type. + */ + +void OGR_Fld_SetType( OGRFieldDefnH hDefn, OGRFieldType eType ) + +{ + ((OGRFieldDefn *) hDefn)->SetType( eType ); +} + +/************************************************************************/ +/* GetSubType() */ +/************************************************************************/ + +/** + * \fn OGRFieldSubType OGRFieldDefn::GetSubType(); + * + * \brief Fetch subtype of this field. + * + * This method is the same as the C function OGR_Fld_GetSubType(). + * + * @return field subtype. + * @since GDAL 2.0 + */ + +/************************************************************************/ +/* OGR_Fld_GetSubType() */ +/************************************************************************/ +/** + * \brief Fetch subtype of this field. + * + * This function is the same as the CPP method OGRFieldDefn::GetSubType(). + * + * @param hDefn handle to the field definition to get subtype from. + * @return field subtype. + * @since GDAL 2.0 + */ + +OGRFieldSubType OGR_Fld_GetSubType( OGRFieldDefnH hDefn ) + +{ + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_Fld_GetXXXX(hDefn, "GetSubType"); +#endif + + return ((OGRFieldDefn *) hDefn)->GetSubType(); +} + +/************************************************************************/ +/* SetSubType() */ +/************************************************************************/ + +/** + * \brief Set the subtype of this field. + * This should never be done to an OGRFieldDefn + * that is already part of an OGRFeatureDefn. + * + * This method is the same as the C function OGR_Fld_SetSubType(). + * + * @param eSubType the new field subtype. + * @since GDAL 2.0 + */ +void OGRFieldDefn::SetSubType( OGRFieldSubType eSubTypeIn ) +{ + if( !OGR_AreTypeSubTypeCompatible(eType, eSubTypeIn) ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Type and subtype of field definition are not compatible. Reseting to OFSTNone"); + eSubType = OFSTNone; + } + else + { + eSubType = eSubTypeIn; + } +} + +/************************************************************************/ +/* OGR_Fld_SetSubType() */ +/************************************************************************/ +/** + * \brief Set the subtype of this field. + * This should never be done to an OGRFieldDefn + * that is already part of an OGRFeatureDefn. + * + * This function is the same as the CPP method OGRFieldDefn::SetSubType(). + * + * @param hDefn handle to the field definition to set type to. + * @param eSubType the new field subtype. + * @since GDAL 2.0 + */ + +void OGR_Fld_SetSubType( OGRFieldDefnH hDefn, OGRFieldSubType eSubType ) + +{ + ((OGRFieldDefn *) hDefn)->SetSubType( eSubType ); +} + +/************************************************************************/ +/* SetDefault() */ +/************************************************************************/ + +/** + * \brief Set default field value. + * + * The default field value is taken into account by drivers (generally those with + * a SQL interface) that support it at field creation time. OGR will generally not + * automatically set the default field value to null fields by itself when calling + * OGRFeature::CreateFeature() / OGRFeature::SetFeature(), but will let the + * low-level layers to do the job. So retrieving the feature from the layer is + * recommended. + * + * The accepted values are NULL, a numeric value, a litteral value enclosed + * between single quote characters (and inner single quote characters escaped by + * repetition of the single quote character), + * CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE or + * a driver specific expression (that might be ignored by other drivers). + * For a datetime literal value, format should be 'YYYY/MM/DD HH:MM:SS[.sss]' + * (considered as UTC time). + * + * Drivers that support writing DEFAULT clauses will advertize the + * GDAL_DCAP_DEFAULT_FIELDS driver metadata item. + * + * This function is the same as the C function OGR_Fld_SetDefault(). + * + * @param pszDefault new default field value or NULL pointer. + * + * @since GDAL 2.0 + */ + +void OGRFieldDefn::SetDefault( const char* pszDefaultIn ) + +{ + CPLFree(pszDefault); + pszDefault = NULL; + + if( pszDefaultIn && pszDefaultIn[0] == '\'' ) + { + if( pszDefaultIn[strlen(pszDefaultIn)-1] != '\'' ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Incorrectly quoted string literal"); + return; + } + const char* pszPtr = pszDefaultIn + 1; + for(; *pszPtr != '\0'; pszPtr ++ ) + { + if( *pszPtr == '\'' ) + { + if( pszPtr[1] == '\0' ) + break; + if( pszPtr[1] != '\'' ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Incorrectly quoted string literal"); + return; + } + pszPtr ++; + } + } + if( *pszPtr == '\0' ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Incorrectly quoted string literal"); + return; + } + } + + pszDefault = pszDefaultIn ? CPLStrdup(pszDefaultIn) : NULL; +} + +/************************************************************************/ +/* OGR_Fld_SetDefault() */ +/************************************************************************/ + +/** + * \brief Set default field value. + * + * The default field value is taken into account by drivers (generally those with + * a SQL interface) that support it at field creation time. OGR will generally not + * automatically set the default field value to null fields by itself when calling + * OGRFeature::CreateFeature() / OGRFeature::SetFeature(), but will let the + * low-level layers to do the job. So retrieving the feature from the layer is + * recommended. + * + * The accepted values are NULL, a numeric value, a litteral value enclosed + * between single quote characters (and inner single quote characters escaped by + * repetition of the single quote character), + * CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE or + * a driver specific expression (that might be ignored by other drivers). + * For a datetime literal value, format should be 'YYYY/MM/DD HH:MM:SS[.sss]' + * (considered as UTC time). + * + * Drivers that support writing DEFAULT clauses will advertize the + * GDAL_DCAP_DEFAULT_FIELDS driver metadata item. + * + * This function is the same as the C++ method OGRFieldDefn::SetDefault(). + * + * @param hDefn handle to the field definition. + * @param pszDefault new default field value or NULL pointer. + * + * @since GDAL 2.0 + */ + +void CPL_DLL OGR_Fld_SetDefault( OGRFieldDefnH hDefn, const char* pszDefault ) +{ + ((OGRFieldDefn *) hDefn)->SetDefault( pszDefault ); +} + +/************************************************************************/ +/* GetDefault() */ +/************************************************************************/ + +/** + * \brief Get default field value. + * + * This function is the same as the C function OGR_Fld_GetDefault(). + * + * @return default field value or NULL. + * @since GDAL 2.0 + */ + +const char* OGRFieldDefn::GetDefault() const + +{ + return pszDefault; +} + +/************************************************************************/ +/* OGR_Fld_GetDefault() */ +/************************************************************************/ + +/** + * \brief Get default field value. + * + * This function is the same as the C++ method OGRFieldDefn::GetDefault(). + * + * @param hDefn handle to the field definition. + * @return default field value or NULL. + * @since GDAL 2.0 + */ + +const char *OGR_Fld_GetDefault( OGRFieldDefnH hDefn ) +{ + return ((OGRFieldDefn *) hDefn)->GetDefault(); +} + +/************************************************************************/ +/* IsDefaultDriverSpecific() */ +/************************************************************************/ + +/** + * \brief Returns whether the default value is driver specific. + * + * Driver specific default values are those that are *not* NULL, a numeric value, + * a litteral value enclosed between single quote characters, CURRENT_TIMESTAMP, + * CURRENT_TIME, CURRENT_DATE or datetime literal value. + * + * This method is the same as the C function OGR_Fld_IsDefaultDriverSpecific(). + * + * @return TRUE if the default value is driver specific. + * @since GDAL 2.0 + */ + +int OGRFieldDefn::IsDefaultDriverSpecific() const +{ + if( pszDefault == NULL ) + return FALSE; + + if( EQUAL(pszDefault, "NULL") || + EQUAL(pszDefault, "CURRENT_TIMESTAMP") || + EQUAL(pszDefault, "CURRENT_TIME") || + EQUAL(pszDefault, "CURRENT_DATE") ) + return FALSE; + + if( pszDefault[0] == '\'' && pszDefault[strlen(pszDefault)-1] == '\'' ) + return FALSE; + + char* pszEnd = NULL; + CPLStrtod(pszDefault, &pszEnd); + if( *pszEnd == '\0' ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* OGR_Fld_IsDefaultDriverSpecific() */ +/************************************************************************/ + +/** + * \brief Returns whether the default value is driver specific. + * + * Driver specific default values are those that are *not* NULL, a numeric value, + * a litteral value enclosed between single quote characters, CURRENT_TIMESTAMP, + * CURRENT_TIME, CURRENT_DATE or datetime literal value. + * + * This function is the same as the C++ method OGRFieldDefn::IsDefaultDriverSpecific(). + * + * @param hDefn handle to the field definition + * @return TRUE if the default value is driver specific. + * @since GDAL 2.0 + */ + +int OGR_Fld_IsDefaultDriverSpecific( OGRFieldDefnH hDefn ) +{ + return ((OGRFieldDefn *) hDefn)->IsDefaultDriverSpecific(); +} + +/************************************************************************/ +/* GetFieldTypeName() */ +/************************************************************************/ + +/** + * \brief Fetch human readable name for a field type. + * + * This static method is the same as the C function OGR_GetFieldTypeName(). + * + * @param eType the field type to get name for. + * + * @return pointer to an internal static name string. It should not be + * modified or freed. + */ + +const char * OGRFieldDefn::GetFieldTypeName( OGRFieldType eType ) + +{ + switch( eType ) + { + case OFTInteger: + return "Integer"; + + case OFTInteger64: + return "Integer64"; + + case OFTReal: + return "Real"; + + case OFTString: + return "String"; + + case OFTIntegerList: + return "IntegerList"; + + case OFTInteger64List: + return "Integer64List"; + + case OFTRealList: + return "RealList"; + + case OFTStringList: + return "StringList"; + + case OFTBinary: + return "Binary"; + + case OFTDate: + return "Date"; + + case OFTTime: + return "Time"; + + case OFTDateTime: + return "DateTime"; + + default: + return "(unknown)"; + } +} + +/************************************************************************/ +/* OGR_GetFieldTypeName() */ +/************************************************************************/ +/** + * \brief Fetch human readable name for a field type. + * + * This function is the same as the CPP method + * OGRFieldDefn::GetFieldTypeName(). + * + * @param eType the field type to get name for. + * @return the name. + */ + +const char *OGR_GetFieldTypeName( OGRFieldType eType ) + +{ + return OGRFieldDefn::GetFieldTypeName( eType ); +} + +/************************************************************************/ +/* GetFieldSubTypeName() */ +/************************************************************************/ + +/** + * \brief Fetch human readable name for a field subtype. + * + * This static method is the same as the C function OGR_GetFieldSubTypeName(). + * + * @param eSubType the field subtype to get name for. + * + * @return pointer to an internal static name string. It should not be + * modified or freed. + * + * @since GDAL 2.0 + */ + +const char * OGRFieldDefn::GetFieldSubTypeName( OGRFieldSubType eSubType ) + +{ + switch( eSubType ) + { + case OFSTNone: + return "None"; + + case OFSTBoolean: + return "Boolean"; + + case OFSTInt16: + return "Int16"; + + case OFSTFloat32: + return "Float32"; + + default: + return "(unknown)"; + } +} + +/************************************************************************/ +/* OGR_GetFieldSubTypeName() */ +/************************************************************************/ +/** + * \brief Fetch human readable name for a field subtype. + * + * This function is the same as the CPP method + * OGRFieldDefn::GetFieldSubTypeName(). + * + * @param eSubType the field subtype to get name for. + * @return the name. + * + * @since GDAL 2.0 + */ + +const char *OGR_GetFieldSubTypeName( OGRFieldSubType eSubType ) + +{ + return OGRFieldDefn::GetFieldSubTypeName( eSubType ); +} + +/************************************************************************/ +/* OGR_IsValidTypeAndSubType() */ +/************************************************************************/ +/** + * \brief Return if type and subtype are compatible + * + * @param eType the field type. + * @param eSubType the field subtype. + * @return TRUE if type and subtype are compatible + * + * @since GDAL 2.0 + */ + +int OGR_AreTypeSubTypeCompatible( OGRFieldType eType, OGRFieldSubType eSubType ) +{ + if( eSubType == OFSTNone ) + return TRUE; + if( eSubType == OFSTBoolean || eSubType == OFSTInt16 ) + return eType == OFTInteger || eType == OFTIntegerList; + if( eSubType == OFSTFloat32 ) + return eType == OFTReal || eType == OFTRealList; + return FALSE; +} + +/************************************************************************/ +/* GetJustify() */ +/************************************************************************/ + +/** + * \fn OGRJustification OGRFieldDefn::GetJustify(); + * + * \brief Get the justification for this field. + * + * Note: no driver is know to use the concept of field justification. + * + * This method is the same as the C function OGR_Fld_GetJustify(). + * + * @return the justification. + */ + +/************************************************************************/ +/* OGR_Fld_GetJustify() */ +/************************************************************************/ +/** + * \brief Get the justification for this field. + * + * This function is the same as the CPP method OGRFieldDefn::GetJustify(). + * + * Note: no driver is know to use the concept of field justification. + * + * @param hDefn handle to the field definition to get justification from. + * @return the justification. + */ + +OGRJustification OGR_Fld_GetJustify( OGRFieldDefnH hDefn ) + +{ + return ((OGRFieldDefn *) hDefn)->GetJustify(); +} + +/************************************************************************/ +/* SetJustify() */ +/************************************************************************/ + +/** + * \fn void OGRFieldDefn::SetJustify( OGRJustification eJustify ); + * + * \brief Set the justification for this field. + * + * Note: no driver is know to use the concept of field justification. + * + * This method is the same as the C function OGR_Fld_SetJustify(). + * + * @param eJustify the new justification. + */ + +/************************************************************************/ +/* OGR_Fld_SetJustify() */ +/************************************************************************/ +/** + * \brief Set the justification for this field. + * + * Note: no driver is know to use the concept of field justification. + * + * This function is the same as the CPP method OGRFieldDefn::SetJustify(). + * + * @param hDefn handle to the field definition to set justification to. + * @param eJustify the new justification. + */ + +void OGR_Fld_SetJustify( OGRFieldDefnH hDefn, OGRJustification eJustify ) + +{ + ((OGRFieldDefn *) hDefn)->SetJustify( eJustify ); +} + +/************************************************************************/ +/* GetWidth() */ +/************************************************************************/ + +/** + * \fn int OGRFieldDefn::GetWidth(); + * + * \brief Get the formatting width for this field. + * + * This method is the same as the C function OGR_Fld_GetWidth(). + * + * @return the width, zero means no specified width. + */ + +/************************************************************************/ +/* OGR_Fld_GetWidth() */ +/************************************************************************/ +/** + * \brief Get the formatting width for this field. + * + * This function is the same as the CPP method OGRFieldDefn::GetWidth(). + * + * @param hDefn handle to the field definition to get width from. + * @return the width, zero means no specified width. + */ + +int OGR_Fld_GetWidth( OGRFieldDefnH hDefn ) + +{ + return ((OGRFieldDefn *) hDefn)->GetWidth(); +} + +/************************************************************************/ +/* SetWidth() */ +/************************************************************************/ + +/** + * \fn void OGRFieldDefn::SetWidth( int nWidth ); + * + * \brief Set the formatting width for this field in characters. + * + * This method is the same as the C function OGR_Fld_SetWidth(). + * + * @param nWidth the new width. + */ + +/************************************************************************/ +/* OGR_Fld_SetWidth() */ +/************************************************************************/ +/** + * \brief Set the formatting width for this field in characters. + * + * This function is the same as the CPP method OGRFieldDefn::SetWidth(). + * + * @param hDefn handle to the field definition to set width to. + * @param nNewWidth the new width. + */ + +void OGR_Fld_SetWidth( OGRFieldDefnH hDefn, int nNewWidth ) + +{ + ((OGRFieldDefn *) hDefn)->SetWidth( nNewWidth ); +} + +/************************************************************************/ +/* GetPrecision() */ +/************************************************************************/ + +/** + * \fn int OGRFieldDefn::GetPrecision(); + * + * \brief Get the formatting precision for this field. + * This should normally be + * zero for fields of types other than OFTReal. + * + * This method is the same as the C function OGR_Fld_GetPrecision(). + * + * @return the precision. + */ + +/************************************************************************/ +/* OGR_Fld_GetPrecision() */ +/************************************************************************/ +/** + * \brief Get the formatting precision for this field. + * This should normally be + * zero for fields of types other than OFTReal. + * + * This function is the same as the CPP method OGRFieldDefn::GetPrecision(). + * + * @param hDefn handle to the field definition to get precision from. + * @return the precision. + */ + +int OGR_Fld_GetPrecision( OGRFieldDefnH hDefn ) + +{ + return ((OGRFieldDefn *) hDefn)->GetPrecision(); +} + +/************************************************************************/ +/* SetPrecision() */ +/************************************************************************/ + +/** + * \fn void OGRFieldDefn::SetPrecision( int nPrecision ); + * + * \brief Set the formatting precision for this field in characters. + * + * This should normally be zero for fields of types other than OFTReal. + * + * This method is the same as the C function OGR_Fld_SetPrecision(). + * + * @param nPrecision the new precision. + */ + +/************************************************************************/ +/* OGR_Fld_SetPrecision() */ +/************************************************************************/ +/** + * \brief Set the formatting precision for this field in characters. + * + * This should normally be zero for fields of types other than OFTReal. + * + * This function is the same as the CPP method OGRFieldDefn::SetPrecision(). + * + * @param hDefn handle to the field definition to set precision to. + * @param nPrecision the new precision. + */ + +void OGR_Fld_SetPrecision( OGRFieldDefnH hDefn, int nPrecision ) + +{ + ((OGRFieldDefn *) hDefn)->SetPrecision( nPrecision ); +} + +/************************************************************************/ +/* Set() */ +/************************************************************************/ + +/** + * \brief Set defining parameters for a field in one call. + * + * This method is the same as the C function OGR_Fld_Set(). + * + * @param pszNameIn the new name to assign. + * @param eTypeIn the new type (one of the OFT values like OFTInteger). + * @param nWidthIn the preferred formatting width. Defaults to zero indicating + * undefined. + * @param nPrecisionIn number of decimals places for formatting, defaults to + * zero indicating undefined. + * @param eJustifyIn the formatting justification (OJLeft or OJRight), defaults + * to OJUndefined. + */ + +void OGRFieldDefn::Set( const char *pszNameIn, + OGRFieldType eTypeIn, + int nWidthIn, int nPrecisionIn, + OGRJustification eJustifyIn ) +{ + SetName( pszNameIn ); + SetType( eTypeIn ); + SetWidth( nWidthIn ); + SetPrecision( nPrecisionIn ); + SetJustify( eJustifyIn ); +} + +/************************************************************************/ +/* OGR_Fld_Set() */ +/************************************************************************/ +/** + * \brief Set defining parameters for a field in one call. + * + * This function is the same as the CPP method OGRFieldDefn::Set(). + * + * @param hDefn handle to the field definition to set to. + * @param pszNameIn the new name to assign. + * @param eTypeIn the new type (one of the OFT values like OFTInteger). + * @param nWidthIn the preferred formatting width. Defaults to zero indicating + * undefined. + * @param nPrecisionIn number of decimals places for formatting, defaults to + * zero indicating undefined. + * @param eJustifyIn the formatting justification (OJLeft or OJRight), defaults + * to OJUndefined. + */ + +void OGR_Fld_Set( OGRFieldDefnH hDefn, const char *pszNameIn, + OGRFieldType eTypeIn, + int nWidthIn, int nPrecisionIn, + OGRJustification eJustifyIn ) + +{ + ((OGRFieldDefn *) hDefn)->Set( pszNameIn, eTypeIn, nWidthIn, + nPrecisionIn, eJustifyIn ); +} + +/************************************************************************/ +/* IsIgnored() */ +/************************************************************************/ + +/** + * \fn int OGRFieldDefn::IsIgnored(); + * + * \brief Return whether this field should be omitted when fetching features + * + * This method is the same as the C function OGR_Fld_IsIgnored(). + * + * @return ignore state + */ + +/************************************************************************/ +/* OGR_Fld_IsIgnored() */ +/************************************************************************/ + +/** + * \brief Return whether this field should be omitted when fetching features + * + * This method is the same as the C++ method OGRFieldDefn::IsIgnored(). + * + * @param hDefn handle to the field definition + * @return ignore state + */ + +int OGR_Fld_IsIgnored( OGRFieldDefnH hDefn ) +{ + return ((OGRFieldDefn *) hDefn)->IsIgnored(); +} + +/************************************************************************/ +/* SetIgnored() */ +/************************************************************************/ + +/** + * \fn void OGRFieldDefn::SetIgnored( int ignore ); + * + * \brief Set whether this field should be omitted when fetching features + * + * This method is the same as the C function OGR_Fld_SetIgnored(). + * + * @param ignore ignore state + */ + +/************************************************************************/ +/* OGR_Fld_SetIgnored() */ +/************************************************************************/ + +/** + * \brief Set whether this field should be omitted when fetching features + * + * This method is the same as the C++ method OGRFieldDefn::SetIgnored(). + * + * @param hDefn handle to the field definition + * @param ignore ignore state + */ + +void OGR_Fld_SetIgnored( OGRFieldDefnH hDefn, int ignore ) +{ + ((OGRFieldDefn *) hDefn)->SetIgnored( ignore ); +} + +/************************************************************************/ +/* IsSame() */ +/************************************************************************/ + +/** + * \brief Test if the field definition is identical to the other one. + * + * @param poOtherFieldDefn the other field definition to compare to. + * @return TRUE if the field definition is identical to the other one. + */ + +int OGRFieldDefn::IsSame( const OGRFieldDefn * poOtherFieldDefn ) const +{ + return (strcmp(pszName, poOtherFieldDefn->pszName) == 0 && + eType == poOtherFieldDefn->eType && + eSubType == poOtherFieldDefn->eSubType && + nWidth == poOtherFieldDefn->nWidth && + nPrecision == poOtherFieldDefn->nPrecision && + bNullable == poOtherFieldDefn->bNullable); +} + +/************************************************************************/ +/* IsNullable() */ +/************************************************************************/ + +/** + * \fn int OGRFieldDefn::IsNullable() const + * + * \brief Return whether this field can receive null values. + * + * By default, fields are nullable. + * + * Even if this method returns FALSE (i.e not-nullable field), it doesn't mean + * that OGRFeature::IsFieldSet() will necessary return TRUE, as fields can be + * temporary unset and null/not-null validation is usually done when + * OGRLayer::CreateFeature()/SetFeature() is called. + * + * This method is the same as the C function OGR_Fld_IsNullable(). + * + * @return TRUE if the field is authorized to be null. + * @since GDAL 2.0 + */ + +/************************************************************************/ +/* OGR_Fld_IsNullable() */ +/************************************************************************/ + +/** + * \brief Return whether this field can receive null values. + * + * By default, fields are nullable. + * + * Even if this method returns FALSE (i.e not-nullable field), it doesn't mean + * that OGRFeature::IsFieldSet() will necessary return TRUE, as fields can be + * temporary unset and null/not-null validation is usually done when + * OGRLayer::CreateFeature()/SetFeature() is called. + * + * This method is the same as the C++ method OGRFieldDefn::IsNullable(). + * + * @param hDefn handle to the field definition + * @return TRUE if the field is authorized to be null. + * @since GDAL 2.0 + */ + +int OGR_Fld_IsNullable( OGRFieldDefnH hDefn ) +{ + return ((OGRFieldDefn *) hDefn)->IsNullable(); +} + +/************************************************************************/ +/* SetNullable() */ +/************************************************************************/ + +/** + * \fn void OGRFieldDefn::SetNullable( int bNullableIn ); + * + * \brief Set whether this field can receive null values. + * + * By default, fields are nullable, so this method is generally called with FALSE + * to set a not-null constraint. + * + * Drivers that support writing not-null constraint will advertize the + * GDAL_DCAP_NOTNULL_FIELDS driver metadata item. + * + * This method is the same as the C function OGR_Fld_SetNullable(). + * + * @param bNullableIn FALSE if the field must have a not-null constraint. + * @since GDAL 2.0 + */ + +/************************************************************************/ +/* OGR_Fld_SetNullable() */ +/************************************************************************/ + +/** + * \brief Set whether this field can receive null values. + * + * By default, fields are nullable, so this method is generally called with FALSE + * to set a not-null constraint. + * + * Drivers that support writing not-null constraint will advertize the + * GDAL_DCAP_NOTNULL_FIELDS driver metadata item. + * + * This method is the same as the C++ method OGRFieldDefn::SetNullable(). + * + * @param hDefn handle to the field definition + * @param bNullableIn FALSE if the field must have a not-null constraint. + * @since GDAL 2.0 + */ + +void OGR_Fld_SetNullable( OGRFieldDefnH hDefn, int bNullableIn ) +{ + ((OGRFieldDefn *) hDefn)->SetNullable( bNullableIn ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrgeomediageometry.cpp b/bazaar/plugin/gdal/ogr/ogrgeomediageometry.cpp new file mode 100644 index 000000000..6b6c962c4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrgeomediageometry.cpp @@ -0,0 +1,415 @@ +/****************************************************************************** + * $Id: ogrgeomediageometry.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements decoder of geomedia geometry blobs + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrgeomediageometry.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrgeomediageometry.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +#define GEOMEDIA_POINT 0xC0 +#define GEOMEDIA_ORIENTED_POINT 0xC8 +#define GEOMEDIA_POLYLINE 0xC2 +#define GEOMEDIA_POLYGON 0xC3 +#define GEOMEDIA_BOUNDARY 0xC5 +#define GEOMEDIA_COLLECTION 0xC6 +#define GEOMEDIA_MULTILINE 0xCB +#define GEOMEDIA_MULTIPOLYGON 0xCC + +/************************************************************************/ +/* OGRCreateFromGeomedia() */ +/************************************************************************/ + +OGRErr OGRCreateFromGeomedia( GByte *pabyGeom, + OGRGeometry **ppoGeom, + int nBytes ) + +{ + *ppoGeom = NULL; + + if( nBytes < 16 ) + return OGRERR_FAILURE; + + if( !(pabyGeom[1] == 0xFF && pabyGeom[2] == 0xD2 && pabyGeom[3] == 0x0F) ) + return OGRERR_FAILURE; + + int nGeomType = pabyGeom[0]; + pabyGeom += 16; + nBytes -= 16; + + if( nGeomType == GEOMEDIA_POINT || + nGeomType == GEOMEDIA_ORIENTED_POINT ) + { + if (nBytes < 3 * 8) + return OGRERR_FAILURE; + + double dfX, dfY, dfZ; + memcpy(&dfX, pabyGeom, 8); + CPL_LSBPTR64(&dfX); + memcpy(&dfY, pabyGeom + 8, 8); + CPL_LSBPTR64(&dfY); + memcpy(&dfZ, pabyGeom + 16, 8); + CPL_LSBPTR64(&dfZ); + + *ppoGeom = new OGRPoint( dfX, dfY, dfZ ); + + return OGRERR_NONE; + } + else if ( nGeomType == GEOMEDIA_POLYLINE ) + { + if (nBytes < 4) + return OGRERR_FAILURE; + + int nPoints; + memcpy(&nPoints, pabyGeom, 4); + CPL_LSBPTR32(&nPoints); + + pabyGeom += 4; + nBytes -= 4; + + if (nPoints < 0 || nPoints > INT_MAX / 24 || nBytes < nPoints * 24) + return OGRERR_FAILURE; + + OGRLineString* poLS = new OGRLineString(); + poLS->setNumPoints(nPoints); + int i; + for(i=0;isetPoint(i, dfX, dfY, dfZ); + + pabyGeom += 24; + } + + *ppoGeom = poLS; + + return OGRERR_NONE; + } + else if ( nGeomType == GEOMEDIA_POLYGON ) + { + if (nBytes < 4) + return OGRERR_FAILURE; + + int nPoints; + memcpy(&nPoints, pabyGeom, 4); + CPL_LSBPTR32(&nPoints); + + pabyGeom += 4; + nBytes -= 4; + + if (nPoints < 0 || nPoints > INT_MAX / 24 || nBytes < nPoints * 24) + return OGRERR_FAILURE; + + OGRLinearRing* poRing = new OGRLinearRing(); + poRing->setNumPoints(nPoints); + int i; + for(i=0;isetPoint(i, dfX, dfY, dfZ); + + pabyGeom += 24; + } + + OGRPolygon* poPoly = new OGRPolygon(); + poPoly->addRingDirectly(poRing); + *ppoGeom = poPoly; + + return OGRERR_NONE; + } + else if ( nGeomType == GEOMEDIA_BOUNDARY ) + { + if (nBytes < 4) + return OGRERR_FAILURE; + + int nExteriorSize; + memcpy(&nExteriorSize, pabyGeom, 4); + CPL_LSBPTR32(&nExteriorSize); + + pabyGeom += 4; + nBytes -= 4; + + if (nBytes < nExteriorSize) + return OGRERR_FAILURE; + + OGRGeometry* poExteriorGeom = NULL; + if (OGRCreateFromGeomedia( pabyGeom, &poExteriorGeom, nExteriorSize ) != OGRERR_NONE) + return OGRERR_FAILURE; + + if ( wkbFlatten( poExteriorGeom->getGeometryType() ) != wkbPolygon ) + { + delete poExteriorGeom; + return OGRERR_FAILURE; + } + + pabyGeom += nExteriorSize; + nBytes -= nExteriorSize; + + if (nBytes < 4) + { + delete poExteriorGeom; + return OGRERR_FAILURE; + } + + int nInteriorSize; + memcpy(&nInteriorSize, pabyGeom, 4); + CPL_LSBPTR32(&nInteriorSize); + + pabyGeom += 4; + nBytes -= 4; + + if (nBytes < nInteriorSize) + { + delete poExteriorGeom; + return OGRERR_FAILURE; + } + + OGRGeometry* poInteriorGeom = NULL; + if (OGRCreateFromGeomedia( pabyGeom, &poInteriorGeom, nInteriorSize ) != OGRERR_NONE) + { + delete poExteriorGeom; + return OGRERR_FAILURE; + } + + OGRwkbGeometryType interiorGeomType = wkbFlatten( poInteriorGeom->getGeometryType() ); + if ( interiorGeomType == wkbPolygon ) + { + ((OGRPolygon*)poExteriorGeom)->addRing(((OGRPolygon*)poInteriorGeom)->getExteriorRing()); + } + else if ( interiorGeomType == wkbMultiPolygon ) + { + int numGeom = ((OGRMultiPolygon*)poInteriorGeom)->getNumGeometries(); + for ( int i = 0; i < numGeom; ++i ) + { + OGRPolygon* poInteriorPolygon = + (OGRPolygon*)((OGRMultiPolygon*)poInteriorGeom)->getGeometryRef(i); + ((OGRPolygon*)poExteriorGeom)->addRing( poInteriorPolygon->getExteriorRing() ); + } + } + else + { + delete poExteriorGeom; + delete poInteriorGeom; + return OGRERR_FAILURE; + } + + delete poInteriorGeom; + *ppoGeom = poExteriorGeom; + + return OGRERR_NONE; + } + else if ( nGeomType == GEOMEDIA_COLLECTION || + nGeomType == GEOMEDIA_MULTILINE || + nGeomType == GEOMEDIA_MULTIPOLYGON ) + { + if (nBytes < 4) + return OGRERR_FAILURE; + + int i; + int nParts; + memcpy(&nParts, pabyGeom, 4); + CPL_LSBPTR32(&nParts); + + pabyGeom += 4; + nBytes -= 4; + + if (nParts < 0 || nParts > INT_MAX / (4 + 16) || nBytes < nParts * (4 + 16)) + return OGRERR_FAILURE; + + /* Can this collection be considered as a multipolyline or multipolygon ? */ + if ( nGeomType == GEOMEDIA_COLLECTION ) + { + GByte* pabyGeomBackup = pabyGeom; + int nBytesBackup = nBytes; + + int bAllPolyline = TRUE; + int bAllPolygon = TRUE; + + for(i=0;igetGeometryType()) == wkbMultiPolygon && + wkbFlatten(poSubGeom->getGeometryType()) == wkbLineString) + { + OGRPolygon* poPoly = new OGRPolygon(); + OGRLinearRing* poRing = new OGRLinearRing(); + poRing->addSubLineString((OGRLineString*)poSubGeom); + poPoly->addRingDirectly(poRing); + delete poSubGeom; + poSubGeom = poPoly; + } + + if (poColl->addGeometryDirectly(poSubGeom) != OGRERR_NONE) + { + delete poSubGeom; + } + } + + pabyGeom += nSubBytes; + nBytes -= nSubBytes; + } + + *ppoGeom = poColl; + + return OGRERR_NONE; + } + else + { + CPLDebug("GEOMEDIA", "Unhandled type %d", nGeomType); + } + + return OGRERR_FAILURE; +} + + +/************************************************************************/ +/* OGRGetGeomediaSRS() */ +/************************************************************************/ + +OGRSpatialReference* OGRGetGeomediaSRS(OGRFeature* poFeature) +{ + if (poFeature == NULL) + return NULL; + + int nGeodeticDatum = poFeature->GetFieldAsInteger("GeodeticDatum"); + int nEllipsoid = poFeature->GetFieldAsInteger("Ellipsoid"); + int nProjAlgorithm = poFeature->GetFieldAsInteger("ProjAlgorithm"); + + if (nGeodeticDatum == 17 && nEllipsoid == 22) + { + if (nProjAlgorithm == 12) + { + OGRSpatialReference* poSRS = new OGRSpatialReference(); + + const char* pszDescription = poFeature->GetFieldAsString("Description"); + if (pszDescription && pszDescription[0] != 0) + poSRS->SetNode( "PROJCS", pszDescription ); + poSRS->SetWellKnownGeogCS("WGS84"); + + double dfStdP1 = poFeature->GetFieldAsDouble("StandPar1"); + double dfStdP2 = poFeature->GetFieldAsDouble("StandPar2"); + double dfCenterLat = poFeature->GetFieldAsDouble("LatOfOrigin"); + double dfCenterLong = poFeature->GetFieldAsDouble("LonOfOrigin"); + double dfFalseEasting = poFeature->GetFieldAsDouble("FalseX"); + double dfFalseNorthing = poFeature->GetFieldAsDouble("FalseY"); + poSRS->SetACEA( dfStdP1, dfStdP2, + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); + return poSRS; + } + } + + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrgeomediageometry.h b/bazaar/plugin/gdal/ogr/ogrgeomediageometry.h new file mode 100644 index 000000000..efc906d78 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrgeomediageometry.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * $Id: ogrgeomediageometry.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements decoder of geomedia geometry blobs + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GEOMEDIAGEOMETRY_H_INCLUDED +#define _OGR_GEOMEDIAGEOMETRY_H_INCLUDED + +#include "ogr_geometry.h" +#include "ogr_spatialref.h" +#include "ogr_feature.h" + +OGRErr OGRCreateFromGeomedia( GByte *pabyGeom, + OGRGeometry **ppoGeom, + int nBytes ); + +OGRSpatialReference* OGRGetGeomediaSRS(OGRFeature* poFeature); + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrgeometry.cpp b/bazaar/plugin/gdal/ogr/ogrgeometry.cpp new file mode 100644 index 000000000..fb350bc65 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrgeometry.cpp @@ -0,0 +1,5371 @@ +/****************************************************************************** + * $Id: ogrgeometry.cpp 28548 2015-02-24 17:30:15Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements a few base methods on OGRGeometry. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_api.h" +#include "ogr_p.h" +#include "ogr_geos.h" +#include "cpl_multiproc.h" +#include + +CPL_CVSID("$Id: ogrgeometry.cpp 28548 2015-02-24 17:30:15Z rouault $"); + +int OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = FALSE; + +#ifdef HAVE_GEOS +static void OGRGEOSErrorHandler(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + CPLErrorV( CE_Failure, CPLE_AppDefined, fmt, args ); + va_end(args); +} + +static void OGRGEOSWarningHandler(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + CPLErrorV( CE_Warning, CPLE_AppDefined, fmt, args ); + va_end(args); +} +#endif + +/************************************************************************/ +/* OGRGeometry() */ +/************************************************************************/ + +OGRGeometry::OGRGeometry() + +{ + poSRS = NULL; + nCoordDimension = 2; +} + +/************************************************************************/ +/* ~OGRGeometry() */ +/************************************************************************/ + +OGRGeometry::~OGRGeometry() + +{ + if( poSRS != NULL ) + poSRS->Release(); +} + +/************************************************************************/ +/* dumpReadable() */ +/************************************************************************/ + +/** + * \brief Dump geometry in well known text format to indicated output file. + * + * A few options can be defined to change the default dump : + *
      + *
    • DISPLAY_GEOMETRY=NO : to hide the dump of the geometry
    • + *
    • DISPLAY_GEOMETRY=WKT or YES (default) : dump the geometry as a WKT
    • + *
    • DISPLAY_GEOMETRY=SUMMARY : to get only a summary of the geometry
    • + *
    + * + * This method is the same as the C function OGR_G_DumpReadable(). + * + * @param fp the text file to write the geometry to. + * @param pszPrefix the prefix to put on each line of output. + * @param papszOptions NULL terminated list of options (may be NULL) + */ + +void OGRGeometry::dumpReadable( FILE * fp, const char * pszPrefix, char** papszOptions ) const + +{ + char *pszWkt = NULL; + + if( pszPrefix == NULL ) + pszPrefix = ""; + + if( fp == NULL ) + fp = stdout; + + const char* pszDisplayGeometry = + CSLFetchNameValue(papszOptions, "DISPLAY_GEOMETRY"); + if (pszDisplayGeometry != NULL && EQUAL(pszDisplayGeometry, "SUMMARY")) + { + OGRLineString *poLine; + OGRCurvePolygon *poPoly; + OGRCurve *poRing; + OGRGeometryCollection *poColl; + fprintf( fp, "%s%s : ", pszPrefix, getGeometryName() ); + switch( getGeometryType() ) + { + case wkbUnknown: + case wkbNone: + case wkbPoint: + case wkbPoint25D: + fprintf( fp, "\n"); + break; + case wkbLineString: + case wkbLineString25D: + case wkbCircularString: + case wkbCircularStringZ: + poLine = (OGRLineString*)this; + fprintf( fp, "%d points\n", poLine->getNumPoints() ); + break; + case wkbPolygon: + case wkbPolygon25D: + case wkbCurvePolygon: + case wkbCurvePolygonZ: + { + int ir; + int nRings; + poPoly = (OGRCurvePolygon*)this; + poRing = poPoly->getExteriorRingCurve(); + nRings = poPoly->getNumInteriorRings(); + if (poRing == NULL) + fprintf( fp, "empty"); + else + { + fprintf( fp, "%d points", poRing->getNumPoints() ); + if( wkbFlatten(poRing->getGeometryType()) == wkbCompoundCurve ) + { + fprintf( fp, " ("); + poRing->dumpReadable(fp, NULL, papszOptions); + fprintf( fp, ")"); + } + if (nRings) + { + fprintf( fp, ", %d inner rings (", nRings); + for( ir = 0; ir < nRings; ir++) + { + poRing = poPoly->getInteriorRingCurve(ir); + if (ir) + fprintf( fp, ", "); + fprintf( fp, "%d points", poRing->getNumPoints() ); + if( wkbFlatten(poRing->getGeometryType()) == wkbCompoundCurve ) + { + fprintf( fp, " ("); + poRing->dumpReadable(fp, NULL, papszOptions); + fprintf( fp, ")"); + } + } + fprintf( fp, ")"); + } + } + fprintf( fp, "\n"); + break; + } + case wkbCompoundCurve: + case wkbCompoundCurveZ: + { + OGRCompoundCurve* poCC = (OGRCompoundCurve* )this; + if( poCC->getNumCurves() == 0 ) + fprintf( fp, "empty"); + else + { + for(int i=0;igetNumCurves();i++) + { + if (i) + fprintf( fp, ", "); + fprintf( fp, "%s (%d points)", + poCC->getCurve(i)->getGeometryName(), + poCC->getCurve(i)->getNumPoints() ); + } + } + break; + } + + case wkbMultiPoint: + case wkbMultiLineString: + case wkbMultiPolygon: + case wkbMultiCurve: + case wkbMultiSurface: + case wkbGeometryCollection: + case wkbMultiPoint25D: + case wkbMultiLineString25D: + case wkbMultiPolygon25D: + case wkbMultiCurveZ: + case wkbMultiSurfaceZ: + case wkbGeometryCollection25D: + { + int ig; + poColl = (OGRGeometryCollection*)this; + fprintf( fp, "%d geometries:\n", poColl->getNumGeometries() ); + for ( ig = 0; ig < poColl->getNumGeometries(); ig++) + { + OGRGeometry * poChild = (OGRGeometry*)poColl->getGeometryRef(ig); + fprintf( fp, "%s", pszPrefix); + poChild->dumpReadable( fp, pszPrefix, papszOptions ); + } + break; + } + case wkbLinearRing: + break; + } + } + else if (pszDisplayGeometry == NULL || CSLTestBoolean(pszDisplayGeometry) || + EQUAL(pszDisplayGeometry, "WKT")) + { + if( exportToWkt( &pszWkt ) == OGRERR_NONE ) + { + fprintf( fp, "%s%s\n", pszPrefix, pszWkt ); + CPLFree( pszWkt ); + } + } +} + +/************************************************************************/ +/* OGR_G_DumpReadable() */ +/************************************************************************/ +/** + * \brief Dump geometry in well known text format to indicated output file. + * + * This method is the same as the CPP method OGRGeometry::dumpReadable. + * + * @param hGeom handle on the geometry to dump. + * @param fp the text file to write the geometry to. + * @param pszPrefix the prefix to put on each line of output. + */ + +void OGR_G_DumpReadable( OGRGeometryH hGeom, FILE *fp, const char *pszPrefix ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_DumpReadable" ); + + ((OGRGeometry *) hGeom)->dumpReadable( fp, pszPrefix ); +} + +/************************************************************************/ +/* assignSpatialReference() */ +/************************************************************************/ + +/** + * \fn void OGRGeometry::assignSpatialReference( OGRSpatialReference * poSR ); + * + * \brief Assign spatial reference to this object. + * + * Any existing spatial reference + * is replaced, but under no circumstances does this result in the object + * being reprojected. It is just changing the interpretation of the existing + * geometry. Note that assigning a spatial reference increments the + * reference count on the OGRSpatialReference, but does not copy it. + * + * This is similar to the SFCOM IGeometry::put_SpatialReference() method. + * + * This method is the same as the C function OGR_G_AssignSpatialReference(). + * + * @param poSR new spatial reference system to apply. + */ + +void OGRGeometry::assignSpatialReference( OGRSpatialReference * poSR ) + +{ + if( poSRS != NULL ) + poSRS->Release(); + + poSRS = poSR; + if( poSRS != NULL ) + poSRS->Reference(); +} + +/************************************************************************/ +/* OGR_G_AssignSpatialReference() */ +/************************************************************************/ +/** + * \brief Assign spatial reference to this object. + * + * Any existing spatial reference + * is replaced, but under no circumstances does this result in the object + * being reprojected. It is just changing the interpretation of the existing + * geometry. Note that assigning a spatial reference increments the + * reference count on the OGRSpatialReference, but does not copy it. + * + * This is similar to the SFCOM IGeometry::put_SpatialReference() method. + * + * This function is the same as the CPP method + * OGRGeometry::assignSpatialReference. + * + * @param hGeom handle on the geometry to apply the new spatial reference + * system. + * @param hSRS handle on the new spatial reference system to apply. + */ + +void OGR_G_AssignSpatialReference( OGRGeometryH hGeom, + OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_AssignSpatialReference" ); + + ((OGRGeometry *) hGeom)->assignSpatialReference( (OGRSpatialReference *) + hSRS ); +} + +/************************************************************************/ +/* Intersects() */ +/************************************************************************/ + +/** + * \brief Do these features intersect? + * + * Determines whether two geometries intersect. If GEOS is enabled, then + * this is done in rigerous fashion otherwise TRUE is returned if the + * envelopes (bounding boxes) of the two features overlap. + * + * The poOtherGeom argument may be safely NULL, but in this case the method + * will always return TRUE. That is, a NULL geometry is treated as being + * everywhere. + * + * This method is the same as the C function OGR_G_Intersects(). + * + * @param poOtherGeom the other geometry to test against. + * + * @return TRUE if the geometries intersect, otherwise FALSE. + */ + +OGRBoolean OGRGeometry::Intersects( const OGRGeometry *poOtherGeom ) const + +{ + OGREnvelope oEnv1, oEnv2; + + if( this == NULL || poOtherGeom == NULL ) + return TRUE; + + this->getEnvelope( &oEnv1 ); + poOtherGeom->getEnvelope( &oEnv2 ); + + if( oEnv1.MaxX < oEnv2.MinX + || oEnv1.MaxY < oEnv2.MinY + || oEnv2.MaxX < oEnv1.MinX + || oEnv2.MaxY < oEnv1.MinY ) + return FALSE; + +#ifndef HAVE_GEOS + + // Without GEOS we assume that envelope overlap is equivelent to + // actual intersection. + return TRUE; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); + + OGRBoolean bResult = FALSE; + if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) + { + if( GEOSIntersects_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ) != 0 ) + bResult = TRUE; + else + bResult = FALSE; + } + + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); + + return bResult; +#endif /* HAVE_GEOS */ +} + +// Old API compatibility function. + +OGRBoolean OGRGeometry::Intersect( OGRGeometry *poOtherGeom ) const + +{ + return Intersects( poOtherGeom ); +} + +/************************************************************************/ +/* OGR_G_Intersects() */ +/************************************************************************/ +/** + * \brief Do these features intersect? + * + * Currently this is not implemented in a rigerous fashion, and generally + * just tests whether the envelopes of the two features intersect. Eventually + * this will be made rigerous. + * + * This function is the same as the CPP method OGRGeometry::Intersects. + * + * @param hGeom handle on the first geometry. + * @param hOtherGeom handle on the other geometry to test against. + * + * @return TRUE if the geometries intersect, otherwise FALSE. + */ + +int OGR_G_Intersects( OGRGeometryH hGeom, OGRGeometryH hOtherGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_Intersects", FALSE ); + VALIDATE_POINTER1( hOtherGeom, "OGR_G_Intersects", FALSE ); + + return ((OGRGeometry *) hGeom)->Intersects( (const OGRGeometry *) hOtherGeom ); +} + +int OGR_G_Intersect( OGRGeometryH hGeom, OGRGeometryH hOtherGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_Intersect", FALSE ); + VALIDATE_POINTER1( hOtherGeom, "OGR_G_Intersect", FALSE ); + + return ((OGRGeometry *) hGeom)->Intersects( (const OGRGeometry *) hOtherGeom ); +} + +/************************************************************************/ +/* transformTo() */ +/************************************************************************/ + +/** + * \brief Transform geometry to new spatial reference system. + * + * This method will transform the coordinates of a geometry from + * their current spatial reference system to a new target spatial + * reference system. Normally this means reprojecting the vectors, + * but it could include datum shifts, and changes of units. + * + * This method will only work if the geometry already has an assigned + * spatial reference system, and if it is transformable to the target + * coordinate system. + * + * Because this method requires internal creation and initialization of an + * OGRCoordinateTransformation object it is significantly more expensive to + * use this method to transform many geometries than it is to create the + * OGRCoordinateTransformation in advance, and call transform() with that + * transformation. This method exists primarily for convenience when only + * transforming a single geometry. + * + * This method is the same as the C function OGR_G_TransformTo(). + * + * @param poSR spatial reference system to transform to. + * + * @return OGRERR_NONE on success, or an error code. + */ + +OGRErr OGRGeometry::transformTo( OGRSpatialReference *poSR ) + +{ +#ifdef DISABLE_OGRGEOM_TRANSFORM + return OGRERR_FAILURE; +#else + OGRCoordinateTransformation *poCT; + OGRErr eErr; + + if( getSpatialReference() == NULL || poSR == NULL ) + return OGRERR_FAILURE; + + poCT = OGRCreateCoordinateTransformation( getSpatialReference(), poSR ); + if( poCT == NULL ) + return OGRERR_FAILURE; + + eErr = transform( poCT ); + + delete poCT; + + return eErr; +#endif +} + +/************************************************************************/ +/* OGR_G_TransformTo() */ +/************************************************************************/ +/** + * \brief Transform geometry to new spatial reference system. + * + * This function will transform the coordinates of a geometry from + * their current spatial reference system to a new target spatial + * reference system. Normally this means reprojecting the vectors, + * but it could include datum shifts, and changes of units. + * + * This function will only work if the geometry already has an assigned + * spatial reference system, and if it is transformable to the target + * coordinate system. + * + * Because this function requires internal creation and initialization of an + * OGRCoordinateTransformation object it is significantly more expensive to + * use this function to transform many geometries than it is to create the + * OGRCoordinateTransformation in advance, and call transform() with that + * transformation. This function exists primarily for convenience when only + * transforming a single geometry. + * + * This function is the same as the CPP method OGRGeometry::transformTo. + * + * @param hGeom handle on the geometry to apply the transform to. + * @param hSRS handle on the spatial reference system to apply. + * + * @return OGRERR_NONE on success, or an error code. + */ + +OGRErr OGR_G_TransformTo( OGRGeometryH hGeom, OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_TransformTo", OGRERR_FAILURE ); + + return ((OGRGeometry *) hGeom)->transformTo((OGRSpatialReference *) hSRS); +} + +/** + * \fn OGRErr OGRGeometry::transform( OGRCoordinateTransformation *poCT ); + * + * \brief Apply arbitrary coordinate transformation to geometry. + * + * This method will transform the coordinates of a geometry from + * their current spatial reference system to a new target spatial + * reference system. Normally this means reprojecting the vectors, + * but it could include datum shifts, and changes of units. + * + * Note that this method does not require that the geometry already + * have a spatial reference system. It will be assumed that they can + * be treated as having the source spatial reference system of the + * OGRCoordinateTransformation object, and the actual SRS of the geometry + * will be ignored. On successful completion the output OGRSpatialReference + * of the OGRCoordinateTransformation will be assigned to the geometry. + * + * This method is the same as the C function OGR_G_Transform(). + * + * @param poCT the transformation to apply. + * + * @return OGRERR_NONE on success or an error code. + */ + +/************************************************************************/ +/* OGR_G_Transform() */ +/************************************************************************/ +/** + * \brief Apply arbitrary coordinate transformation to geometry. + * + * This function will transform the coordinates of a geometry from + * their current spatial reference system to a new target spatial + * reference system. Normally this means reprojecting the vectors, + * but it could include datum shifts, and changes of units. + * + * Note that this function does not require that the geometry already + * have a spatial reference system. It will be assumed that they can + * be treated as having the source spatial reference system of the + * OGRCoordinateTransformation object, and the actual SRS of the geometry + * will be ignored. On successful completion the output OGRSpatialReference + * of the OGRCoordinateTransformation will be assigned to the geometry. + * + * This function is the same as the CPP method OGRGeometry::transform. + * + * @param hGeom handle on the geometry to apply the transform to. + * @param hTransform handle on the transformation to apply. + * + * @return OGRERR_NONE on success or an error code. + */ + +OGRErr OGR_G_Transform( OGRGeometryH hGeom, + OGRCoordinateTransformationH hTransform ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_Transform", OGRERR_FAILURE ); + + return ((OGRGeometry *) hGeom)->transform( + (OGRCoordinateTransformation *) hTransform ); +} + +/** + * \fn int OGRGeometry::getDimension() const; + * + * \brief Get the dimension of this object. + * + * This method corresponds to the SFCOM IGeometry::GetDimension() method. + * It indicates the dimension of the object, but does not indicate the + * dimension of the underlying space (as indicated by + * OGRGeometry::getCoordinateDimension()). + * + * This method is the same as the C function OGR_G_GetDimension(). + * + * @return 0 for points, 1 for lines and 2 for surfaces. + */ + + +/** + * \brief Get the geometry type that conforms with ISO SQL/MM Part3 + * + * @return the geometry type that conforms with ISO SQL/MM Part3 + */ +OGRwkbGeometryType OGRGeometry::getIsoGeometryType() const +{ + OGRwkbGeometryType nGType = wkbFlatten(getGeometryType()); + + if ( getCoordinateDimension() == 3 ) + nGType = (OGRwkbGeometryType)(nGType + 1000); + + return nGType; +} + +/************************************************************************/ +/* OGRGeometry::segmentize() */ +/************************************************************************/ +/** + * + * \brief Modify the geometry such it has no segment longer then the given distance. + * + * Interpolated points will have Z and M values (if needed) set to 0. + * Distance computation is performed in 2d only + * + * This function is the same as the C function OGR_G_Segmentize() + * + * @param dfMaxLength the maximum distance between 2 points after segmentization + */ + +void OGRGeometry::segmentize( CPL_UNUSED double dfMaxLength ) +{ + /* Do nothing */ +} + +/************************************************************************/ +/* OGR_G_Segmentize() */ +/************************************************************************/ + +/** + * + * \brief Modify the geometry such it has no segment longer then the given distance. + * + * Interpolated points will have Z and M values (if needed) set to 0. + * Distance computation is performed in 2d only + * + * This function is the same as the CPP method OGRGeometry::segmentize(). + * + * @param hGeom handle on the geometry to segmentize + * @param dfMaxLength the maximum distance between 2 points after segmentization + */ + +void CPL_DLL OGR_G_Segmentize(OGRGeometryH hGeom, double dfMaxLength ) +{ + VALIDATE_POINTER0( hGeom, "OGR_G_Segmentize" ); + + if (dfMaxLength <= 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "dfMaxLength must be strictly positive"); + return; + } + ((OGRGeometry *) hGeom)->segmentize( dfMaxLength ); +} + +/************************************************************************/ +/* OGR_G_GetDimension() */ +/************************************************************************/ +/** + * + * \brief Get the dimension of this geometry. + * + * This function corresponds to the SFCOM IGeometry::GetDimension() method. + * It indicates the dimension of the geometry, but does not indicate the + * dimension of the underlying space (as indicated by + * OGR_G_GetCoordinateDimension() function). + * + * This function is the same as the CPP method OGRGeometry::getDimension(). + * + * @param hGeom handle on the geometry to get the dimension from. + * @return 0 for points, 1 for lines and 2 for surfaces. + */ + +int OGR_G_GetDimension( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetDimension", 0 ); + + return ((OGRGeometry *) hGeom)->getDimension(); +} + +/************************************************************************/ +/* getCoordinateDimension() */ +/************************************************************************/ +/** + * \brief Get the dimension of the coordinates in this object. + * + * This method corresponds to the SFCOM IGeometry::GetDimension() method. + * + * This method is the same as the C function OGR_G_GetCoordinateDimension(). + * + * @return in practice this will return 2 or 3. It can also return 0 in the + * case of an empty point. + */ + +int OGRGeometry::getCoordinateDimension() const + +{ + return nCoordDimension; +} + +/************************************************************************/ +/* OGR_G_GetCoordinateDimension() */ +/************************************************************************/ +/** + * + * \brief Get the dimension of the coordinates in this geometry. + * + * This function corresponds to the SFCOM IGeometry::GetDimension() method. + * + * This function is the same as the CPP method + * OGRGeometry::getCoordinateDimension(). + * + * @param hGeom handle on the geometry to get the dimension of the + * coordinates from. + * + * @return in practice this will return 2 or 3. It can also return 0 in the + * case of an empty point. + */ + +int OGR_G_GetCoordinateDimension( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetCoordinateDimension", 0 ); + + return ((OGRGeometry *) hGeom)->getCoordinateDimension(); +} + +/************************************************************************/ +/* setCoordinateDimension() */ +/************************************************************************/ + +/** + * \brief Set the coordinate dimension. + * + * This method sets the explicit coordinate dimension. Setting the coordinate + * dimension of a geometry to 2 should zero out any existing Z values. Setting + * the dimension of a geometry collection will not necessarily affect the + * children geometries. + * + * @param nNewDimension New coordinate dimension value, either 2 or 3. + */ + +void OGRGeometry::setCoordinateDimension( int nNewDimension ) + +{ + nCoordDimension = nNewDimension; +} + +/************************************************************************/ +/* OGR_G_SetCoordinateDimension() */ +/************************************************************************/ + +/** + * \brief Set the coordinate dimension. + * + * This method sets the explicit coordinate dimension. Setting the coordinate + * dimension of a geometry to 2 should zero out any existing Z values. Setting + * the dimension of a geometry collection will not necessarily affect the + * children geometries. + * + * @param hGeom handle on the geometry to set the dimension of the + * coordinates. + * @param nNewDimension New coordinate dimension value, either 2 or 3. + */ + +void OGR_G_SetCoordinateDimension( OGRGeometryH hGeom, int nNewDimension) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_SetCoordinateDimension" ); + + ((OGRGeometry *) hGeom)->setCoordinateDimension( nNewDimension ); +} + +/** + * \fn int OGRGeometry::Equals( OGRGeometry *poOtherGeom ) const; + * + * \brief Returns TRUE if two geometries are equivalent. + * + * This method is the same as the C function OGR_G_Equals(). + * + * @return TRUE if equivalent or FALSE otherwise. + */ + + +// Backward compatibility method. + +int OGRGeometry::Equal( OGRGeometry *poOtherGeom ) const +{ + return Equals( poOtherGeom ); +} + +/************************************************************************/ +/* OGR_G_Equals() */ +/************************************************************************/ + +/** + * \brief Returns TRUE if two geometries are equivalent. + * + * This function is the same as the CPP method OGRGeometry::Equals() method. + * + * @param hGeom handle on the first geometry. + * @param hOther handle on the other geometry to test against. + * @return TRUE if equivalent or FALSE otherwise. + */ + +int OGR_G_Equals( OGRGeometryH hGeom, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_Equals", FALSE ); + + if (hGeom == NULL) { + CPLError ( CE_Failure, CPLE_ObjectNull, "hGeom was NULL in OGR_G_Equals"); + return 0; + } + + if (hOther == NULL) { + CPLError ( CE_Failure, CPLE_ObjectNull, "hOther was NULL in OGR_G_Equals"); + return 0; + } + + return ((OGRGeometry *) hGeom)->Equals( (OGRGeometry *) hOther ); +} + +int OGR_G_Equal( OGRGeometryH hGeom, OGRGeometryH hOther ) + +{ + if (hGeom == NULL) { + CPLError ( CE_Failure, CPLE_ObjectNull, "hGeom was NULL in OGR_G_Equal"); + return 0; + } + + if (hOther == NULL) { + CPLError ( CE_Failure, CPLE_ObjectNull, "hOther was NULL in OGR_G_Equal"); + return 0; + } + + return ((OGRGeometry *) hGeom)->Equals( (OGRGeometry *) hOther ); +} + + +/** + * \fn int OGRGeometry::WkbSize() const; + * + * \brief Returns size of related binary representation. + * + * This method returns the exact number of bytes required to hold the + * well known binary representation of this geometry object. Its computation + * may be slightly expensive for complex geometries. + * + * This method relates to the SFCOM IWks::WkbSize() method. + * + * This method is the same as the C function OGR_G_WkbSize(). + * + * @return size of binary representation in bytes. + */ + +/************************************************************************/ +/* OGR_G_WkbSize() */ +/************************************************************************/ +/** + * \brief Returns size of related binary representation. + * + * This function returns the exact number of bytes required to hold the + * well known binary representation of this geometry object. Its computation + * may be slightly expensive for complex geometries. + * + * This function relates to the SFCOM IWks::WkbSize() method. + * + * This function is the same as the CPP method OGRGeometry::WkbSize(). + * + * @param hGeom handle on the geometry to get the binary size from. + * @return size of binary representation in bytes. + */ + +int OGR_G_WkbSize( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_WkbSize", 0 ); + + return ((OGRGeometry *) hGeom)->WkbSize(); +} + +/** + * \fn void OGRGeometry::getEnvelope(OGREnvelope *psEnvelope) const; + * + * \brief Computes and returns the bounding envelope for this geometry in the passed psEnvelope structure. + * + * This method is the same as the C function OGR_G_GetEnvelope(). + * + * @param psEnvelope the structure in which to place the results. + */ + +/************************************************************************/ +/* OGR_G_GetEnvelope() */ +/************************************************************************/ +/** + * \brief Computes and returns the bounding envelope for this geometry in the passed psEnvelope structure. + * + * This function is the same as the CPP method OGRGeometry::getEnvelope(). + * + * @param hGeom handle of the geometry to get envelope from. + * @param psEnvelope the structure in which to place the results. + */ + +void OGR_G_GetEnvelope( OGRGeometryH hGeom, OGREnvelope *psEnvelope ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_GetEnvelope" ); + + ((OGRGeometry *) hGeom)->getEnvelope( psEnvelope ); +} + +/** + * \fn void OGRGeometry::getEnvelope(OGREnvelope3D *psEnvelope) const; + * + * \brief Computes and returns the bounding envelope (3D) for this geometry in the passed psEnvelope structure. + * + * This method is the same as the C function OGR_G_GetEnvelope3D(). + * + * @param psEnvelope the structure in which to place the results. + * + * @since OGR 1.9.0 + */ + +/************************************************************************/ +/* OGR_G_GetEnvelope3D() */ +/************************************************************************/ +/** + * \brief Computes and returns the bounding envelope (3D) for this geometry in the passed psEnvelope structure. + * + * This function is the same as the CPP method OGRGeometry::getEnvelope(). + * + * @param hGeom handle of the geometry to get envelope from. + * @param psEnvelope the structure in which to place the results. + * + * @since OGR 1.9.0 + */ + +void OGR_G_GetEnvelope3D( OGRGeometryH hGeom, OGREnvelope3D *psEnvelope ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_GetEnvelope3D" ); + + ((OGRGeometry *) hGeom)->getEnvelope( psEnvelope ); +} + +/** + * \fn OGRErr OGRGeometry::importFromWkb( unsigned char * pabyData, int nSize, OGRwkbVariant eWkbVariant =wkbVariantOldOgc ); + * + * \brief Assign geometry from well known binary data. + * + * The object must have already been instantiated as the correct derived + * type of geometry object to match the binaries type. This method is used + * by the OGRGeometryFactory class, but not normally called by application + * code. + * + * This method relates to the SFCOM IWks::ImportFromWKB() method. + * + * This method is the same as the C function OGR_G_ImportFromWkb(). + * + * @param pabyData the binary input data. + * @param nSize the size of pabyData in bytes, or zero if not known. + * @param eWkbVariant if wkbVariantPostGIS1, special interpretation is done for curve geometries code + * + * @return OGRERR_NONE if all goes well, otherwise any of + * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or + * OGRERR_CORRUPT_DATA may be returned. + */ + +/************************************************************************/ +/* OGR_G_ImportFromWkb() */ +/************************************************************************/ +/** + * \brief Assign geometry from well known binary data. + * + * The object must have already been instantiated as the correct derived + * type of geometry object to match the binaries type. + * + * This function relates to the SFCOM IWks::ImportFromWKB() method. + * + * This function is the same as the CPP method OGRGeometry::importFromWkb(). + * + * @param hGeom handle on the geometry to assign the well know binary data to. + * @param pabyData the binary input data. + * @param nSize the size of pabyData in bytes, or zero if not known. + * + * @return OGRERR_NONE if all goes well, otherwise any of + * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or + * OGRERR_CORRUPT_DATA may be returned. + */ + +OGRErr OGR_G_ImportFromWkb( OGRGeometryH hGeom, + unsigned char *pabyData, int nSize ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_ImportFromWkb", OGRERR_FAILURE ); + + return ((OGRGeometry *) hGeom)->importFromWkb( pabyData, nSize ); +} + +/** + * \fn OGRErr OGRGeometry::exportToWkb( OGRwkbByteOrder eByteOrder, + unsigned char * pabyData, + OGRwkbVariant eWkbVariant=wkbVariantOldOgc ) const + * + * \brief Convert a geometry into well known binary format. + * + * This method relates to the SFCOM IWks::ExportToWKB() method. + * + * This method is the same as the C function OGR_G_ExportToWkb() or OGR_G_ExportToIsoWkb(), + * depending on the value of eWkbVariant. + * + * @param eByteOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order + * respectively. + * @param pabyData a buffer into which the binary representation is + * written. This buffer must be at least + * OGRGeometry::WkbSize() byte in size. + * @param eWkbVariant What standard to use when exporting geometries with + * three dimensions (or more). The default wkbVariantOldOgc is + * the historical OGR variant. wkbVariantIso is the + * variant defined in ISO SQL/MM and adopted by OGC + * for SFSQL 1.2. + * + * @return Currently OGRERR_NONE is always returned. + */ + +/************************************************************************/ +/* OGR_G_ExportToWkb() */ +/************************************************************************/ +/** + * \brief Convert a geometry well known binary format + * + * This function relates to the SFCOM IWks::ExportToWKB() method. + * + * For backward compatibility purposes, it exports the Old-style 99-402 + * extended dimension (Z) WKB types for types Point, LineString, Polygon, + * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection. + * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkb(). + * + * This function is the same as the CPP method OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *, OGRwkbVariant) + * with eWkbVariant = wkbVariantOldOgc. + * + * @param hGeom handle on the geometry to convert to a well know binary + * data from. + * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order + * respectively. + * @param pabyDstBuffer a buffer into which the binary representation is + * written. This buffer must be at least + * OGR_G_WkbSize() byte in size. + * + * @return Currently OGRERR_NONE is always returned. + */ + +OGRErr OGR_G_ExportToWkb( OGRGeometryH hGeom, OGRwkbByteOrder eOrder, + unsigned char *pabyDstBuffer ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_ExportToWkb", OGRERR_FAILURE ); + + return ((OGRGeometry *) hGeom)->exportToWkb( eOrder, pabyDstBuffer ); +} + +/************************************************************************/ +/* OGR_G_ExportToIsoWkb() */ +/************************************************************************/ +/** + * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known binary format + * + * This function relates to the SFCOM IWks::ExportToWKB() method. + * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB types + * + * This function is the same as the CPP method OGRGeometry::exportToWkb(OGRwkbByteOrder, unsigned char *, OGRwkbVariant) + * with eWkbVariant = wkbVariantIso. + * + * @param hGeom handle on the geometry to convert to a well know binary + * data from. + * @param eOrder One of wkbXDR or wkbNDR indicating MSB or LSB byte order + * respectively. + * @param pabyDstBuffer a buffer into which the binary representation is + * written. This buffer must be at least + * OGR_G_WkbSize() byte in size. + * + * @return Currently OGRERR_NONE is always returned. + * + * @since GDAL 2.0 + */ + +OGRErr OGR_G_ExportToIsoWkb( OGRGeometryH hGeom, OGRwkbByteOrder eOrder, + unsigned char *pabyDstBuffer ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_ExportToIsoWkb", OGRERR_FAILURE ); + + return ((OGRGeometry *) hGeom)->exportToWkb( eOrder, pabyDstBuffer, wkbVariantIso ); +} + +/** + * \fn OGRErr OGRGeometry::importFromWkt( char ** ppszInput ); + * + * \brief Assign geometry from well known text data. + * + * The object must have already been instantiated as the correct derived + * type of geometry object to match the text type. This method is used + * by the OGRGeometryFactory class, but not normally called by application + * code. + * + * This method relates to the SFCOM IWks::ImportFromWKT() method. + * + * This method is the same as the C function OGR_G_ImportFromWkt(). + * + * @param ppszInput pointer to a pointer to the source text. The pointer is + * updated to pointer after the consumed text. + * + * @return OGRERR_NONE if all goes well, otherwise any of + * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or + * OGRERR_CORRUPT_DATA may be returned. + */ + +/************************************************************************/ +/* OGR_G_ImportFromWkt() */ +/************************************************************************/ +/** + * \brief Assign geometry from well known text data. + * + * The object must have already been instantiated as the correct derived + * type of geometry object to match the text type. + * + * This function relates to the SFCOM IWks::ImportFromWKT() method. + * + * This function is the same as the CPP method OGRGeometry::importFromWkt(). + * + * @param hGeom handle on the geometry to assign well know text data to. + * @param ppszSrcText pointer to a pointer to the source text. The pointer is + * updated to pointer after the consumed text. + * + * @return OGRERR_NONE if all goes well, otherwise any of + * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or + * OGRERR_CORRUPT_DATA may be returned. + */ + +OGRErr OGR_G_ImportFromWkt( OGRGeometryH hGeom, char ** ppszSrcText ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_ImportFromWkt", OGRERR_FAILURE ); + + return ((OGRGeometry *) hGeom)->importFromWkt( ppszSrcText ); +} + + +/************************************************************************/ +/* importPreambuleFromWkt() */ +/************************************************************************/ + +/* Returns -1 if processing must continue */ +OGRErr OGRGeometry::importPreambuleFromWkt( char ** ppszInput, + int* pbHasZ, int* pbHasM ) +{ + char szToken[OGR_WKT_TOKEN_MAX]; + const char *pszInput = *ppszInput; + +/* -------------------------------------------------------------------- */ +/* Clear existing Geoms. */ +/* -------------------------------------------------------------------- */ + empty(); + +/* -------------------------------------------------------------------- */ +/* Read and verify the type keyword, and ensure it matches the */ +/* actual type of this container. */ +/* -------------------------------------------------------------------- */ + pszInput = OGRWktReadToken( pszInput, szToken ); + + if( !EQUAL(szToken,getGeometryName()) ) + return OGRERR_CORRUPT_DATA; + +/* -------------------------------------------------------------------- */ +/* Check for EMPTY ... */ +/* -------------------------------------------------------------------- */ + const char *pszPreScan; + int bHasZ = FALSE, bHasM = FALSE; + + pszPreScan = OGRWktReadToken( pszInput, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + *ppszInput = (char *) pszPreScan; + empty(); + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Check for Z, M or ZM. Will ignore the Measure */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(szToken,"Z") ) + { + bHasZ = TRUE; + } + else if( EQUAL(szToken,"M") ) + { + bHasM = TRUE; + } + else if( EQUAL(szToken,"ZM") ) + { + bHasZ = TRUE; + bHasM = TRUE; + } + *pbHasZ = bHasZ; + *pbHasM = bHasM; + + if (bHasZ || bHasM) + { + pszInput = pszPreScan; + pszPreScan = OGRWktReadToken( pszInput, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + *ppszInput = (char *) pszPreScan; + empty(); + if( bHasZ ) + setCoordinateDimension(3); + + /* FIXME?: In theory we should store the M presence */ + /* if we want to allow round-trip with ExportToWKT v1.2 */ + return OGRERR_NONE; + } + } + + if( !EQUAL(szToken,"(") ) + return OGRERR_CORRUPT_DATA; + + if ( !bHasZ && !bHasM ) + { + /* Test for old-style XXXXXXXXX(EMPTY) */ + pszPreScan = OGRWktReadToken( pszPreScan, szToken ); + if( EQUAL(szToken,"EMPTY") ) + { + pszPreScan = OGRWktReadToken( pszPreScan, szToken ); + + if( EQUAL(szToken,",") ) + { + /* This is OK according to SFSQL SPEC. */ + } + else if( !EQUAL(szToken,")") ) + return OGRERR_CORRUPT_DATA; + else + { + *ppszInput = (char *) pszPreScan; + empty(); + return OGRERR_NONE; + } + } + } + + *ppszInput = (char*) pszInput; + + return -1; +} + + +/** + * \fn OGRErr OGRGeometry::exportToWkt( char ** ppszDstText, OGRwkbVariant eWkbVariant = wkbVariantOldOgc ) const; + * + * \brief Convert a geometry into well known text format. + * + * This method relates to the SFCOM IWks::ExportToWKT() method. + * + * This method is the same as the C function OGR_G_ExportToWkt(). + * + * @param ppszDstText a text buffer is allocated by the program, and assigned + * to the passed pointer. After use, *ppszDstText should be + * freed with OGRFree(). + * @param eWkbVariant the specification that must be conformed too : + * - wbkVariantOgc for old-style 99-402 extended dimension (Z) WKB types + * - wbkVariantIso for SFSQL 1.2 and ISO SQL/MM Part 3 + * + * @return Currently OGRERR_NONE is always returned. + */ + +/************************************************************************/ +/* OGR_G_ExportToWkt() */ +/************************************************************************/ + +/** + * \brief Convert a geometry into well known text format. + * + * This function relates to the SFCOM IWks::ExportToWKT() method. + * + * For backward compatibility purposes, it exports the Old-style 99-402 + * extended dimension (Z) WKB types for types Point, LineString, Polygon, + * MultiPoint, MultiLineString, MultiPolygon and GeometryCollection. + * For other geometry types, it is equivalent to OGR_G_ExportToIsoWkt(). + * + * This function is the same as the CPP method OGRGeometry::exportToWkt(). + * + * @param hGeom handle on the geometry to convert to a text format from. + * @param ppszSrcText a text buffer is allocated by the program, and assigned + * to the passed pointer. After use, *ppszDstText should be + * freed with OGRFree(). + * + * @return Currently OGRERR_NONE is always returned. + */ + +OGRErr OGR_G_ExportToWkt( OGRGeometryH hGeom, char **ppszSrcText ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_ExportToWkt", OGRERR_FAILURE ); + + return ((OGRGeometry *) hGeom)->exportToWkt( ppszSrcText ); +} + +/************************************************************************/ +/* OGR_G_ExportToIsoWkt() */ +/************************************************************************/ + +/** + * \brief Convert a geometry into SFSQL 1.2 / ISO SQL/MM Part 3 well known text format + * + * This function relates to the SFCOM IWks::ExportToWKT() method. + * It exports the SFSQL 1.2 and ISO SQL/MM Part 3 extended dimension (Z&M) WKB types + * + * This function is the same as the CPP method OGRGeometry::exportToWkt(,wkbVariantIso). + * + * @param hGeom handle on the geometry to convert to a text format from. + * @param ppszSrcText a text buffer is allocated by the program, and assigned + * to the passed pointer. After use, *ppszDstText should be + * freed with OGRFree(). + * + * @return Currently OGRERR_NONE is always returned. + * + * @since GDAL 2.0 + */ + +OGRErr OGR_G_ExportToIsoWkt( OGRGeometryH hGeom, char **ppszSrcText ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_ExportToIsoWkt", OGRERR_FAILURE ); + + return ((OGRGeometry *) hGeom)->exportToWkt( ppszSrcText, wkbVariantIso ); +} + +/** + * \fn OGRwkbGeometryType OGRGeometry::getGeometryType() const; + * + * \brief Fetch geometry type. + * + * Note that the geometry type may include the 2.5D flag. To get a 2D + * flattened version of the geometry type apply the wkbFlatten() macro + * to the return result. + * + * This method is the same as the C function OGR_G_GetGeometryType(). + * + * @return the geometry type code. + */ + +/************************************************************************/ +/* OGR_G_GetGeometryType() */ +/************************************************************************/ +/** + * \brief Fetch geometry type. + * + * Note that the geometry type may include the 2.5D flag. To get a 2D + * flattened version of the geometry type apply the wkbFlatten() macro + * to the return result. + * + * This function is the same as the CPP method OGRGeometry::getGeometryType(). + * + * @param hGeom handle on the geometry to get type from. + * @return the geometry type code. + */ + +OGRwkbGeometryType OGR_G_GetGeometryType( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetGeometryType", wkbUnknown ); + + return ((OGRGeometry *) hGeom)->getGeometryType(); +} + +/** + * \fn const char * OGRGeometry::getGeometryName() const; + * + * \brief Fetch WKT name for geometry type. + * + * There is no SFCOM analog to this method. + * + * This method is the same as the C function OGR_G_GetGeometryName(). + * + * @return name used for this geometry type in well known text format. The + * returned pointer is to a static internal string and should not be modified + * or freed. + */ + +/************************************************************************/ +/* OGR_G_GetGeometryName() */ +/************************************************************************/ +/** + * \brief Fetch WKT name for geometry type. + * + * There is no SFCOM analog to this function. + * + * This function is the same as the CPP method OGRGeometry::getGeometryName(). + * + * @param hGeom handle on the geometry to get name from. + * @return name used for this geometry type in well known text format. + */ + +const char *OGR_G_GetGeometryName( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetGeometryName", "" ); + + return ((OGRGeometry *) hGeom)->getGeometryName(); +} + +/** + * \fn OGRGeometry *OGRGeometry::clone() const; + * + * \brief Make a copy of this object. + * + * This method relates to the SFCOM IGeometry::clone() method. + * + * This method is the same as the C function OGR_G_Clone(). + * + * @return a new object instance with the same geometry, and spatial + * reference system as the original. + */ + +/************************************************************************/ +/* OGR_G_Clone() */ +/************************************************************************/ +/** + * \brief Make a copy of this object. + * + * This function relates to the SFCOM IGeometry::clone() method. + * + * This function is the same as the CPP method OGRGeometry::clone(). + * + * @param hGeom handle on the geometry to clone from. + * @return an handle on the copy of the geometry with the spatial + * reference system as the original. + */ + +OGRGeometryH OGR_G_Clone( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_Clone", NULL ); + + return (OGRGeometryH) ((OGRGeometry *) hGeom)->clone(); +} + +/** + * \fn OGRSpatialReference *OGRGeometry::getSpatialReference(); + * + * \brief Returns spatial reference system for object. + * + * This method relates to the SFCOM IGeometry::get_SpatialReference() method. + * + * This method is the same as the C function OGR_G_GetSpatialReference(). + * + * @return a reference to the spatial reference object. The object may be + * shared with many geometry objects, and should not be modified. + */ + +/************************************************************************/ +/* OGR_G_GetSpatialReference() */ +/************************************************************************/ +/** + * \brief Returns spatial reference system for geometry. + * + * This function relates to the SFCOM IGeometry::get_SpatialReference() method. + * + * This function is the same as the CPP method + * OGRGeometry::getSpatialReference(). + * + * @param hGeom handle on the geometry to get spatial reference from. + * @return a reference to the spatial reference geometry. + */ + +OGRSpatialReferenceH OGR_G_GetSpatialReference( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_GetSpatialReference", NULL ); + + return (OGRSpatialReferenceH) + ((OGRGeometry *) hGeom)->getSpatialReference(); +} + +/** + * \fn void OGRGeometry::empty(); + * + * \brief Clear geometry information. + * This restores the geometry to it's initial + * state after construction, and before assignment of actual geometry. + * + * This method relates to the SFCOM IGeometry::Empty() method. + * + * This method is the same as the C function OGR_G_Empty(). + */ + +/************************************************************************/ +/* OGR_G_Empty() */ +/************************************************************************/ +/** + * \brief Clear geometry information. + * This restores the geometry to it's initial + * state after construction, and before assignment of actual geometry. + * + * This function relates to the SFCOM IGeometry::Empty() method. + * + * This function is the same as the CPP method OGRGeometry::empty(). + * + * @param hGeom handle on the geometry to empty. + */ + +void OGR_G_Empty( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_Empty" ); + + ((OGRGeometry *) hGeom)->empty(); +} + +/** + * \fn OGRBoolean OGRGeometry::IsEmpty() const; + * + * \brief Returns TRUE (non-zero) if the object has no points. + * + * Normally this + * returns FALSE except between when an object is instantiated and points + * have been assigned. + * + * This method relates to the SFCOM IGeometry::IsEmpty() method. + * + * @return TRUE if object is empty, otherwise FALSE. + */ + +/************************************************************************/ +/* OGR_G_IsEmpty() */ +/************************************************************************/ + +/** + * \brief Test if the geometry is empty. + * + * This method is the same as the CPP method OGRGeometry::IsEmpty(). + * + * @param hGeom The Geometry to test. + * + * @return TRUE if the geometry has no points, otherwise FALSE. + */ + +int OGR_G_IsEmpty( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_IsEmpty", TRUE ); + + return ((OGRGeometry *) hGeom)->IsEmpty(); +} + +/************************************************************************/ +/* IsValid() */ +/************************************************************************/ + +/** + * \brief Test if the geometry is valid. + * + * This method is the same as the C function OGR_G_IsValid(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always return + * FALSE. + * + * + * @return TRUE if the geometry has no points, otherwise FALSE. + */ + +OGRBoolean +OGRGeometry::IsValid( ) const + +{ +#ifndef HAVE_GEOS + + return FALSE; + +#else + + OGRBoolean bResult = FALSE; + GEOSGeom hThisGeosGeom = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + + if( hThisGeosGeom != NULL ) + { + bResult = GEOSisValid_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + } + freeGEOSContext( hGEOSCtxt ); + + return bResult; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_IsValid() */ +/************************************************************************/ + +/** + * \brief Test if the geometry is valid. + * + * This function is the same as the C++ method OGRGeometry::IsValid(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always return + * FALSE. + * + * @param hGeom The Geometry to test. + * + * @return TRUE if the geometry has no points, otherwise FALSE. + */ + +int OGR_G_IsValid( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_IsValid", FALSE ); + + return ((OGRGeometry *) hGeom)->IsValid(); +} + +/************************************************************************/ +/* IsSimple() */ +/************************************************************************/ + +/** + * \brief Test if the geometry is simple. + * + * This method is the same as the C function OGR_G_IsSimple(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always return + * FALSE. + * + * + * @return TRUE if the geometry has no points, otherwise FALSE. + */ + +OGRBoolean +OGRGeometry::IsSimple( ) const + +{ +#ifndef HAVE_GEOS + + return FALSE; + +#else + + OGRBoolean bResult = FALSE; + GEOSGeom hThisGeosGeom = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + + if( hThisGeosGeom != NULL ) + { + bResult = GEOSisSimple_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + } + freeGEOSContext( hGEOSCtxt ); + + return bResult; + +#endif /* HAVE_GEOS */ +} + + +/** + * \brief Returns TRUE if the geometry is simple. + * + * Returns TRUE if the geometry has no anomalous geometric points, such + * as self intersection or self tangency. The description of each + * instantiable geometric class will include the specific conditions that + * cause an instance of that class to be classified as not simple. + * + * This function is the same as the c++ method OGRGeometry::IsSimple() method. + * + * If OGR is built without the GEOS library, this function will always return + * FALSE. + * + * @param hGeom The Geometry to test. + * + * @return TRUE if object is simple, otherwise FALSE. + */ + +int OGR_G_IsSimple( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_IsSimple", TRUE ); + + return ((OGRGeometry *) hGeom)->IsSimple(); +} + +/************************************************************************/ +/* IsRing() */ +/************************************************************************/ + +/** + * \brief Test if the geometry is a ring + * + * This method is the same as the C function OGR_G_IsRing(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always return + * FALSE. + * + * + * @return TRUE if the geometry has no points, otherwise FALSE. + */ + +OGRBoolean +OGRGeometry::IsRing( ) const + +{ +#ifndef HAVE_GEOS + + return FALSE; + +#else + + OGRBoolean bResult = FALSE; + GEOSGeom hThisGeosGeom = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + + if( hThisGeosGeom != NULL ) + { + bResult = GEOSisRing_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + } + freeGEOSContext( hGEOSCtxt ); + + return bResult; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_IsRing() */ +/************************************************************************/ + +/** + * \brief Test if the geometry is a ring + * + * This function is the same as the C++ method OGRGeometry::IsRing(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always return + * FALSE. + * + * @param hGeom The Geometry to test. + * + * @return TRUE if the geometry has no points, otherwise FALSE. + */ + +int OGR_G_IsRing( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_IsRing", FALSE ); + + return ((OGRGeometry *) hGeom)->IsRing(); +} + +/************************************************************************/ +/* OGRFromOGCGeomType() */ +/* Map OGCgeometry format type to corresponding */ +/* OGR constants. */ +/************************************************************************/ + +#define EQUALN_CST(var, cst) EQUALN(var, cst, strlen(cst)) + +OGRwkbGeometryType OGRFromOGCGeomType( const char *pszGeomType ) +{ + OGRwkbGeometryType eType; + int bConvertTo3D = FALSE; + if( *pszGeomType != '\0' ) + { + char ch = pszGeomType[strlen(pszGeomType)-1]; + if( ch == 'z' || ch == 'Z' ) + { + bConvertTo3D = TRUE; + } + } + if ( EQUALN_CST(pszGeomType, "POINT") ) + eType = wkbPoint; + else if ( EQUALN_CST(pszGeomType, "LINESTRING") ) + eType = wkbLineString; + else if ( EQUALN_CST(pszGeomType, "POLYGON") ) + eType = wkbPolygon; + else if ( EQUALN_CST(pszGeomType, "MULTIPOINT") ) + eType = wkbMultiPoint; + else if ( EQUALN_CST(pszGeomType, "MULTILINESTRING") ) + eType = wkbMultiLineString; + else if ( EQUALN_CST(pszGeomType, "MULTIPOLYGON") ) + eType = wkbMultiPolygon; + else if ( EQUALN_CST(pszGeomType, "GEOMETRYCOLLECTION") ) + eType = wkbGeometryCollection; + else if ( EQUALN_CST(pszGeomType, "CIRCULARSTRING") ) + eType = wkbCircularString; + else if ( EQUALN_CST(pszGeomType, "COMPOUNDCURVE") ) + eType = wkbCompoundCurve; + else if ( EQUALN_CST(pszGeomType, "CURVEPOLYGON") ) + eType = wkbCurvePolygon; + else if ( EQUALN_CST(pszGeomType, "MULTICURVE") ) + eType = wkbMultiCurve; + else if ( EQUALN_CST(pszGeomType, "MULTISURFACE") ) + eType = wkbMultiSurface; + else + eType = wkbUnknown; + + if( bConvertTo3D ) + eType = wkbSetZ(eType); + + return eType; +} + +/************************************************************************/ +/* OGRToOGCGeomType() */ +/* Map OGR geometry format constants to corresponding */ +/* OGC geometry type */ +/************************************************************************/ + +const char * OGRToOGCGeomType( OGRwkbGeometryType eGeomType ) +{ + switch ( wkbFlatten(eGeomType) ) + { + case wkbUnknown: + return "GEOMETRY"; + case wkbPoint: + return "POINT"; + case wkbLineString: + return "LINESTRING"; + case wkbPolygon: + return "POLYGON"; + case wkbMultiPoint: + return "MULTIPOINT"; + case wkbMultiLineString: + return "MULTILINESTRING"; + case wkbMultiPolygon: + return "MULTIPOLYGON"; + case wkbGeometryCollection: + return "GEOMETRYCOLLECTION"; + case wkbCircularString: + return "CIRCULARSTRING"; + case wkbCompoundCurve: + return "COMPOUNDCURVE"; + case wkbCurvePolygon: + return "CURVEPOLYGON"; + case wkbMultiCurve: + return "MULTICURVE"; + case wkbMultiSurface: + return "MULTISURFACE"; + default: + return ""; + } +} + +/************************************************************************/ +/* OGRGeometryTypeToName() */ +/************************************************************************/ + +/** + * \brief Fetch a human readable name corresponding to an OGRwkbGeometryType value. + * The returned value should not be modified, or freed by the application. + * + * This function is C callable. + * + * @param eType the geometry type. + * + * @return internal human readable string, or NULL on failure. + */ + +const char *OGRGeometryTypeToName( OGRwkbGeometryType eType ) + +{ + bool b2D = wkbFlatten(eType) == eType; + + switch( wkbFlatten(eType) ) + { + case wkbUnknown: + if( b2D ) + return "Unknown (any)"; + else + return "3D Unknown (any)"; + + case wkbPoint: + if( b2D ) + return "Point"; + else + return "3D Point"; + + case wkbLineString: + if( b2D ) + return "Line String"; + else + return "3D Line String"; + + case wkbPolygon: + if( b2D ) + return "Polygon"; + else + return "3D Polygon"; + + case wkbMultiPoint: + if( b2D ) + return "Multi Point"; + else + return "3D Multi Point"; + + case wkbMultiLineString: + if( b2D ) + return "Multi Line String"; + else + return "3D Multi Line String"; + + case wkbMultiPolygon: + if( b2D ) + return "Multi Polygon"; + else + return "3D Multi Polygon"; + + case wkbGeometryCollection: + if( b2D ) + return "Geometry Collection"; + else + return "3D Geometry Collection"; + + case wkbCircularString: + if( b2D ) + return "Circular String"; + else + return "3D Circular String"; + + case wkbCompoundCurve: + if( b2D ) + return "Compound Curve"; + else + return "3D Compound Curve"; + + case wkbCurvePolygon: + if( b2D ) + return "Curve Polygon"; + else + return "3D Curve Polygon"; + + case wkbMultiCurve: + if( b2D ) + return "Multi Curve"; + else + return "3D Multi Curve"; + + case wkbMultiSurface: + if( b2D ) + return "Multi Surface"; + else + return "3D Multi Surface"; + + case wkbNone: + return "None"; + + default: + { + // OGRThreadSafety: This static is judged to be a very low risk + // for thread safety because it is only used in case of error, + // and the worst that can happen is reporting the wrong code + // in the generated message. + static char szWorkName[33]; + sprintf( szWorkName, "Unrecognised: %d", (int) eType ); + return szWorkName; + } + } +} + +/************************************************************************/ +/* OGRMergeGeometryTypes() */ +/************************************************************************/ + +/** + * \brief Find common geometry type. + * + * Given two geometry types, find the most specific common + * type. Normally used repeatedly with the geometries in a + * layer to try and establish the most specific geometry type + * that can be reported for the layer. + * + * NOTE: wkbUnknown is the "worst case" indicating a mixture of + * geometry types with nothing in common but the base geometry + * type. wkbNone should be used to indicate that no geometries + * have been encountered yet, and means the first geometry + * encounted will establish the preliminary type. + * + * @param eMain the first input geometry type. + * @param eExtra the second input geometry type. + * + * @return the merged geometry type. + */ + +OGRwkbGeometryType +OGRMergeGeometryTypes( OGRwkbGeometryType eMain, + OGRwkbGeometryType eExtra ) + +{ + return OGRMergeGeometryTypesEx(eMain, eExtra, FALSE); +} + +/** + * \brief Find common geometry type. + * + * Given two geometry types, find the most specific common + * type. Normally used repeatedly with the geometries in a + * layer to try and establish the most specific geometry type + * that can be reported for the layer. + * + * NOTE: wkbUnknown is the "worst case" indicating a mixture of + * geometry types with nothing in common but the base geometry + * type. wkbNone should be used to indicate that no geometries + * have been encountered yet, and means the first geometry + * encounted will establish the preliminary type. + * + * If bAllowPromotingToCurves is set to TRUE, mixing Polygon and CurvePolygon + * will return CurvePolygon. Mixing LineString, CircularString, CompoundCurve + * will return CompoundCurve. Mixing MultiPolygon and MultiSurface will return + * MultiSurface. Mixing MultiCurve and MultiLineString will return MultiCurve. + * + * @param eMain the first input geometry type. + * @param eExtra the second input geometry type. + * @param bAllowPromotingToCurves determine if promotion to curve type must be done. + * + * @return the merged geometry type. + * + * @since GDAL 2.0 + */ + +OGRwkbGeometryType +OGRMergeGeometryTypesEx( OGRwkbGeometryType eMain, + OGRwkbGeometryType eExtra, + int bAllowPromotingToCurves ) + +{ + int bHasZ; + OGRwkbGeometryType eFMain = wkbFlatten(eMain); + OGRwkbGeometryType eFExtra = wkbFlatten(eExtra); + + bHasZ = ( eFMain != eMain || eFExtra != eExtra ); + + if( eFMain == wkbUnknown || eFExtra == wkbUnknown ) + return OGR_GT_SetModifier(wkbUnknown, bHasZ, FALSE); + + if( eFMain == wkbNone ) + return eExtra; + + if( eFExtra == wkbNone ) + return eMain; + + if( eFMain == eFExtra ) + { + return OGR_GT_SetModifier(eFMain, bHasZ, FALSE); + } + + if( bAllowPromotingToCurves ) + { + if( OGR_GT_IsCurve(eFMain) && OGR_GT_IsCurve(eFExtra) ) + return OGR_GT_SetModifier(wkbCompoundCurve, bHasZ, FALSE); + + if( OGR_GT_IsSubClassOf(eFMain, eFExtra) ) + return OGR_GT_SetModifier(eFExtra, bHasZ, FALSE); + + if( OGR_GT_IsSubClassOf(eFExtra, eFMain) ) + return OGR_GT_SetModifier(eFMain, bHasZ, FALSE); + } + + // Both are geometry collections. + if( OGR_GT_IsSubClassOf(eFMain, wkbGeometryCollection) && + OGR_GT_IsSubClassOf(eFExtra, wkbGeometryCollection) ) + { + return OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, FALSE); + } + + // Nothing apparently in common. + return OGR_GT_SetModifier(wkbUnknown, bHasZ, FALSE); +} + +/** + * \fn void OGRGeometry::flattenTo2D(); + * + * \brief Convert geometry to strictly 2D. + * In a sense this converts all Z coordinates + * to 0.0. + * + * This method is the same as the C function OGR_G_FlattenTo2D(). + */ + +/************************************************************************/ +/* OGR_G_FlattenTo2D() */ +/************************************************************************/ +/** + * \brief Convert geometry to strictly 2D. + * In a sense this converts all Z coordinates + * to 0.0. + * + * This function is the same as the CPP method OGRGeometry::flattenTo2D(). + * + * @param hGeom handle on the geometry to convert. + */ + +void OGR_G_FlattenTo2D( OGRGeometryH hGeom ) + +{ + ((OGRGeometry *) hGeom)->flattenTo2D(); +} + +/************************************************************************/ +/* exportToGML() */ +/************************************************************************/ + +/** + * \fn char *OGRGeometry::exportToGML( const char* const * papszOptions = NULL ) const; + * + * \brief Convert a geometry into GML format. + * + * The GML geometry is expressed directly in terms of GML basic data + * types assuming the this is available in the gml namespace. The returned + * string should be freed with CPLFree() when no longer required. + * + * The supported options in OGR 1.8.0 are : + *
      + *
    • FORMAT=GML3. Otherwise it will default to GML 2.1.2 output. + *
    • GML3_LINESTRING_ELEMENT=curve. (Only valid for FORMAT=GML3) To use gml:Curve element for linestrings. + * Otherwise gml:LineString will be used . + *
    • GML3_LONGSRS=YES/NO. (Only valid for FORMAT=GML3) Default to YES. If YES, SRS with EPSG authority will + * be written with the "urn:ogc:def:crs:EPSG::" prefix. + * In the case, if the SRS is a geographic SRS without explicit AXIS order, but that the same SRS authority code + * imported with ImportFromEPSGA() should be treated as lat/long, then the function will take care of coordinate order swapping. + * If set to NO, SRS with EPSG authority will be written with the "EPSG:" prefix, even if they are in lat/long order. + *
    + * + * This method is the same as the C function OGR_G_ExportToGMLEx(). + * + * @param papszOptions NULL-terminated list of options. + * @return A GML fragment or NULL in case of error. + */ + +char *OGRGeometry::exportToGML( const char* const * papszOptions ) const +{ + return OGR_G_ExportToGMLEx( (OGRGeometryH) this, (char**)papszOptions ); +} + +/************************************************************************/ +/* exportToKML() */ +/************************************************************************/ + +/** + * \fn char *OGRGeometry::exportToKML() const; + * + * \brief Convert a geometry into KML format. + * + * The returned string should be freed with CPLFree() when no longer required. + * + * This method is the same as the C function OGR_G_ExportToKML(). + * + * @return A KML fragment or NULL in case of error. + */ + +char *OGRGeometry::exportToKML() const +{ +#ifndef _WIN32_WCE +#ifdef OGR_ENABLED + return OGR_G_ExportToKML( (OGRGeometryH) this, NULL ); +#else + CPLError( CE_Failure, CPLE_AppDefined, + "OGRGeometry::exportToKML() not supported in builds without OGR drivers." ); + return NULL; +#endif +#else + CPLError( CE_Failure, CPLE_AppDefined, + "OGRGeometry::exportToKML() not supported in the WinCE build." ); + return NULL; +#endif +} + +/************************************************************************/ +/* exportToJson() */ +/************************************************************************/ + +/** + * \fn char *OGRGeometry::exportToJson() const; + * + * \brief Convert a geometry into GeoJSON format. + * + * The returned string should be freed with CPLFree() when no longer required. + * + * This method is the same as the C function OGR_G_ExportToJson(). + * + * @return A GeoJSON fragment or NULL in case of error. + */ + +char *OGRGeometry::exportToJson() const +{ +#ifndef _WIN32_WCE +#ifdef OGR_ENABLED + OGRGeometry* poGeometry = const_cast(this); + return OGR_G_ExportToJson( (OGRGeometryH) (poGeometry) ); +#else + CPLError( CE_Failure, CPLE_AppDefined, + "OGRGeometry::exportToJson() not supported in builds without OGR drivers." ); + return NULL; +#endif +#else + CPLError( CE_Failure, CPLE_AppDefined, + "OGRGeometry::exportToJson() not supported in the WinCE build." ); + return NULL; +#endif +} + +/************************************************************************/ +/* OGRSetGenerate_DB2_V72_BYTE_ORDER() */ +/************************************************************************/ + +/** + * \brief Special entry point to enable the hack for generating DB2 V7.2 style WKB. + * + * DB2 seems to have placed (and require) an extra 0x30 or'ed with the byte order in + * WKB. This entry point is used to turn on or off the + * generation of such WKB. + */ +OGRErr OGRSetGenerate_DB2_V72_BYTE_ORDER( int bGenerate_DB2_V72_BYTE_ORDER ) + +{ +#if defined(HACK_FOR_IBM_DB2_V72) + OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER = bGenerate_DB2_V72_BYTE_ORDER; + return OGRERR_NONE; +#else + if( bGenerate_DB2_V72_BYTE_ORDER ) + return OGRERR_FAILURE; + else + return OGRERR_NONE; +#endif +} +/************************************************************************/ +/* OGRGetGenerate_DB2_V72_BYTE_ORDER() */ +/* */ +/* This is a special entry point to get the value of static flag */ +/* OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER. */ +/************************************************************************/ +int OGRGetGenerate_DB2_V72_BYTE_ORDER() +{ + return OGRGeometry::bGenerate_DB2_V72_BYTE_ORDER; +} + +/************************************************************************/ +/* createGEOSContext() */ +/************************************************************************/ + +GEOSContextHandle_t OGRGeometry::createGEOSContext() +{ +#ifndef HAVE_GEOS + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; +#else + return initGEOS_r( OGRGEOSWarningHandler, OGRGEOSErrorHandler ); +#endif +} + +/************************************************************************/ +/* freeGEOSContext() */ +/************************************************************************/ + +void OGRGeometry::freeGEOSContext(GEOSContextHandle_t hGEOSCtxt) +{ +#ifdef HAVE_GEOS + if( hGEOSCtxt != NULL ) + { + finishGEOS_r( hGEOSCtxt ); + } +#endif +} + +/************************************************************************/ +/* exportToGEOS() */ +/************************************************************************/ + +GEOSGeom OGRGeometry::exportToGEOS(GEOSContextHandle_t hGEOSCtxt) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + + if( hGEOSCtxt == NULL ) + return NULL; + + /* POINT EMPTY is exported to WKB as if it were POINT(0 0) */ + /* so that particular case is necessary */ + if (wkbFlatten(getGeometryType()) == wkbPoint && + IsEmpty()) + { + return GEOSGeomFromWKT_r(hGEOSCtxt, "POINT EMPTY"); + } + + GEOSGeom hGeom = NULL; + size_t nDataSize; + unsigned char *pabyData = NULL; + + const OGRGeometry* poLinearGeom = (hasCurveGeometry()) ? getLinearGeometry() : this; + nDataSize = poLinearGeom->WkbSize(); + pabyData = (unsigned char *) CPLMalloc(nDataSize); + if( poLinearGeom->exportToWkb( wkbNDR, pabyData ) == OGRERR_NONE ) + hGeom = GEOSGeomFromWKB_buf_r( hGEOSCtxt, pabyData, nDataSize ); + + CPLFree( pabyData ); + + if( poLinearGeom != this ) + delete poLinearGeom; + + return hGeom; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* hasCurveGeometry() */ +/************************************************************************/ + +/** + * \brief Returns if this geometry is or has curve geometry. + * + * Returns if a geometry is, contains or may contain a CIRCULARSTRING, COMPOUNDCURVE, + * CURVEPOLYGON, MULTICURVE or MULTISURFACE. + * + * If bLookForNonLinear is set to TRUE, it will be actually looked if the + * geometry or its subgeometries are or contain a non-linear geometry in them. In which + * case, if the method returns TRUE, it means that getLinearGeometry() would + * return an approximate version of the geometry. Otherwise, getLinearGeometry() + * would do a conversion, but with just converting container type, like + * COMPOUNDCURVE -> LINESTRING, MULTICURVE -> MULTILINESTRING or MULTISURFACE -> MULTIPOLYGON, + * resulting in a "loss-less" conversion. + * + * This method is the same as the C function OGR_G_HasCurveGeometry(). + * + * @param bLookForNonLinear set it to TRUE to check if the geometry is or contains + * a CIRCULARSTRING. + * + * @return TRUE if this geometry is or has curve geometry. + * + * @since GDAL 2.0 + */ + +OGRBoolean OGRGeometry::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const +{ + return FALSE; +} + +/************************************************************************/ +/* getLinearGeometry() */ +/************************************************************************/ + +/** + * \brief Return, possibly approximate, non-curve version of this geometry. + * + * Returns a geometry that has no CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, + * MULTICURVE or MULTISURFACE in it, by approximating curve geometries. + * + * The ownership of the returned geometry belongs to the caller. + * + * The reverse method is OGRGeometry::getCurveGeometry(). + * + * This method is the same as the C function OGR_G_GetLinearGeometry(). + * + * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the + * arc, zero to use the default setting. + * @param papszOptions options as a null-terminated list of strings. + * See OGRGeometryFactory::curveToLineString() for valid options. + * + * @return a new geometry. + * + * @since GDAL 2.0 + */ + +OGRGeometry* OGRGeometry::getLinearGeometry(CPL_UNUSED double dfMaxAngleStepSizeDegrees, + CPL_UNUSED const char* const* papszOptions) const +{ + return clone(); +} + +/************************************************************************/ +/* getCurveGeometry() */ +/************************************************************************/ + +/** + * \brief Return curve version of this geometry. + * + * Returns a geometry that has possibly CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, + * MULTICURVE or MULTISURFACE in it, by de-approximating curve geometries. + * + * If the geometry has no curve portion, the returned geometry will be a clone + * of it. + * + * The ownership of the returned geometry belongs to the caller. + * + * The reverse method is OGRGeometry::getLinearGeometry(). + * + * This function is the same as C function OGR_G_GetCurveGeometry(). + * + * @param papszOptions options as a null-terminated list of strings. + * Unused for now. Must be set to NULL. + * + * @return a new geometry. + * + * @since GDAL 2.0 + */ + +OGRGeometry* OGRGeometry::getCurveGeometry(CPL_UNUSED const char* const* papszOptions) const +{ + return clone(); +} + +/************************************************************************/ +/* Distance() */ +/************************************************************************/ + +/** + * \brief Compute distance between two geometries. + * + * Returns the shortest distance between the two geometries. The distance is + * expressed into the same unit as the coordinates of the geometries. + * + * This method is the same as the C function OGR_G_Distance(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param poOtherGeom the other geometry to compare against. + * + * @return the distance between the geometries or -1 if an error occurs. + */ + +double OGRGeometry::Distance( const OGRGeometry *poOtherGeom ) const + +{ + if( NULL == poOtherGeom ) + { + CPLDebug( "OGR", "OGRGeometry::Distance called with NULL geometry pointer" ); + return -1.0; + } + +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return -1.0; + +#else + + // GEOSGeom is a pointer + GEOSGeom hThis = NULL; + GEOSGeom hOther = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hOther = poOtherGeom->exportToGEOS(hGEOSCtxt); + hThis = exportToGEOS(hGEOSCtxt); + + int bIsErr = 0; + double dfDistance = 0.0; + + if( hThis != NULL && hOther != NULL ) + { + bIsErr = GEOSDistance_r( hGEOSCtxt, hThis, hOther, &dfDistance ); + } + + GEOSGeom_destroy_r( hGEOSCtxt, hThis ); + GEOSGeom_destroy_r( hGEOSCtxt, hOther ); + freeGEOSContext( hGEOSCtxt ); + + if ( bIsErr > 0 ) + { + return dfDistance; + } + + /* Calculations error */ + return -1.0; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Distance() */ +/************************************************************************/ +/** + * \brief Compute distance between two geometries. + * + * Returns the shortest distance between the two geometries. The distance is + * expressed into the same unit as the coordinates of the geometries. + * + * This function is the same as the C++ method OGRGeometry::Distance(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hFirst the first geometry to compare against. + * @param hOther the other geometry to compare against. + * + * @return the distance between the geometries or -1 if an error occurs. + */ + + +double OGR_G_Distance( OGRGeometryH hFirst, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hFirst, "OGR_G_Distance", 0.0 ); + + return ((OGRGeometry *) hFirst)->Distance( (OGRGeometry *) hOther ); +} + +/************************************************************************/ +/* OGRGeometryRebuildCurves() */ +/************************************************************************/ + +static OGRGeometry* OGRGeometryRebuildCurves(const OGRGeometry* poGeom, + const OGRGeometry* poOtherGeom, + OGRGeometry* poOGRProduct) +{ + if( poOGRProduct != NULL && + wkbFlatten(poOGRProduct->getGeometryType()) != wkbPoint && + (poGeom->hasCurveGeometry() || + (poOtherGeom && poOtherGeom->hasCurveGeometry())) ) + { + OGRGeometry* poCurveGeom = poOGRProduct->getCurveGeometry(); + delete poOGRProduct; + return poCurveGeom; + } + return poOGRProduct; +} + +/************************************************************************/ +/* ConvexHull() */ +/************************************************************************/ + +/** + * \brief Compute convex hull. + * + * A new geometry object is created and returned containing the convex + * hull of the geometry on which the method is invoked. + * + * This method is the same as the C function OGR_G_ConvexHull(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @return a newly allocated geometry now owned by the caller, or NULL on failure. + */ + +OGRGeometry *OGRGeometry::ConvexHull() const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + + GEOSGeom hGeosGeom = NULL; + GEOSGeom hGeosHull = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hGeosGeom = exportToGEOS(hGEOSCtxt); + if( hGeosGeom != NULL ) + { + hGeosHull = GEOSConvexHull_r( hGEOSCtxt, hGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosGeom ); + + if( hGeosHull != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosHull); + if( poOGRProduct != NULL && getSpatialReference() != NULL ) + poOGRProduct->assignSpatialReference(getSpatialReference()); + poOGRProduct = OGRGeometryRebuildCurves(this, NULL, poOGRProduct); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosHull); + } + } + freeGEOSContext( hGEOSCtxt ); + + return poOGRProduct; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_ConvexHull() */ +/************************************************************************/ +/** + * \brief Compute convex hull. + * + * A new geometry object is created and returned containing the convex + * hull of the geometry on which the method is invoked. + * + * This function is the same as the C++ method OGRGeometry::ConvexHull(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hTarget The Geometry to calculate the convex hull of. + * + * @return a handle to a newly allocated geometry now owned by the caller, + * or NULL on failure. + */ + +OGRGeometryH OGR_G_ConvexHull( OGRGeometryH hTarget ) + +{ + VALIDATE_POINTER1( hTarget, "OGR_G_ConvexHull", NULL ); + + return (OGRGeometryH) ((OGRGeometry *) hTarget)->ConvexHull(); +} + +/************************************************************************/ +/* Boundary() */ +/************************************************************************/ + +/** + * \brief Compute boundary. + * + * A new geometry object is created and returned containing the boundary + * of the geometry on which the method is invoked. + * + * This method is the same as the C function OGR_G_Boundary(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @return a newly allocated geometry now owned by the caller, or NULL on failure. + * + * @since OGR 1.8.0 + */ + +OGRGeometry *OGRGeometry::Boundary() const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + + GEOSGeom hGeosGeom = NULL; + GEOSGeom hGeosProduct = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hGeosGeom = exportToGEOS(hGEOSCtxt); + if( hGeosGeom != NULL ) + { + hGeosProduct = GEOSBoundary_r( hGEOSCtxt, hGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosGeom ); + + if( hGeosProduct != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL ) + poOGRProduct->assignSpatialReference(getSpatialReference()); + poOGRProduct = OGRGeometryRebuildCurves(this, NULL, poOGRProduct); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); + } + } + freeGEOSContext( hGEOSCtxt ); + + return poOGRProduct; + +#endif /* HAVE_GEOS */ +} + + +/** + * \brief Compute boundary (deprecated) + * + * @deprecated + * + * @see Boundary() + */ +OGRGeometry *OGRGeometry::getBoundary() const + +{ + return Boundary(); +} + +/************************************************************************/ +/* OGR_G_Boundary() */ +/************************************************************************/ +/** + * \brief Compute boundary. + * + * A new geometry object is created and returned containing the boundary + * of the geometry on which the method is invoked. + * + * This function is the same as the C++ method OGR_G_Boundary(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hTarget The Geometry to calculate the boundary of. + * + * @return a handle to a newly allocated geometry now owned by the caller, + * or NULL on failure. + * + * @since OGR 1.8.0 + */ +OGRGeometryH OGR_G_Boundary( OGRGeometryH hTarget ) + +{ + VALIDATE_POINTER1( hTarget, "OGR_G_Boundary", NULL ); + + return (OGRGeometryH) ((OGRGeometry *) hTarget)->Boundary(); +} + +/** + * \brief Compute boundary (deprecated) + * + * @deprecated + * + * @see OGR_G_Boundary() + */ +OGRGeometryH OGR_G_GetBoundary( OGRGeometryH hTarget ) + +{ + VALIDATE_POINTER1( hTarget, "OGR_G_GetBoundary", NULL ); + + return (OGRGeometryH) ((OGRGeometry *) hTarget)->Boundary(); +} + +/************************************************************************/ +/* Buffer() */ +/************************************************************************/ + +/** + * \brief Compute buffer of geometry. + * + * Builds a new geometry containing the buffer region around the geometry + * on which it is invoked. The buffer is a polygon containing the region within + * the buffer distance of the original geometry. + * + * Some buffer sections are properly described as curves, but are converted to + * approximate polygons. The nQuadSegs parameter can be used to control how many + * segements should be used to define a 90 degree curve - a quadrant of a circle. + * A value of 30 is a reasonable default. Large values result in large numbers + * of vertices in the resulting buffer geometry while small numbers reduce the + * accuracy of the result. + * + * This method is the same as the C function OGR_G_Buffer(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param dfDist the buffer distance to be applied. Should be expressed into + * the same unit as the coordinates of the geometry. + * + * @param nQuadSegs the number of segments used to approximate a 90 degree (quadrant) of + * curvature. + * + * @return the newly created geometry, or NULL if an error occurs. + */ + +OGRGeometry *OGRGeometry::Buffer( double dfDist, int nQuadSegs ) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + + GEOSGeom hGeosGeom = NULL; + GEOSGeom hGeosProduct = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hGeosGeom = exportToGEOS(hGEOSCtxt); + if( hGeosGeom != NULL ) + { + hGeosProduct = GEOSBuffer_r( hGEOSCtxt, hGeosGeom, dfDist, nQuadSegs ); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosGeom ); + + if( hGeosProduct != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL ) + poOGRProduct->assignSpatialReference(getSpatialReference()); + poOGRProduct = OGRGeometryRebuildCurves(this, NULL, poOGRProduct); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); + } + } + freeGEOSContext(hGEOSCtxt); + + return poOGRProduct; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Buffer() */ +/************************************************************************/ + +/** + * \brief Compute buffer of geometry. + * + * Builds a new geometry containing the buffer region around the geometry + * on which it is invoked. The buffer is a polygon containing the region within + * the buffer distance of the original geometry. + * + * Some buffer sections are properly described as curves, but are converted to + * approximate polygons. The nQuadSegs parameter can be used to control how many + * segements should be used to define a 90 degree curve - a quadrant of a circle. + * A value of 30 is a reasonable default. Large values result in large numbers + * of vertices in the resulting buffer geometry while small numbers reduce the + * accuracy of the result. + * + * This function is the same as the C++ method OGRGeometry::Buffer(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hTarget the geometry. + * @param dfDist the buffer distance to be applied. Should be expressed into + * the same unit as the coordinates of the geometry. + * + * @param nQuadSegs the number of segments used to approximate a 90 degree + * (quadrant) of curvature. + * + * @return the newly created geometry, or NULL if an error occurs. + */ + +OGRGeometryH OGR_G_Buffer( OGRGeometryH hTarget, double dfDist, int nQuadSegs ) + +{ + VALIDATE_POINTER1( hTarget, "OGR_G_Buffer", NULL ); + + return (OGRGeometryH) ((OGRGeometry *) hTarget)->Buffer( dfDist, nQuadSegs ); +} + +/************************************************************************/ +/* Intersection() */ +/************************************************************************/ + +/** + * \brief Compute intersection. + * + * Generates a new geometry which is the region of intersection of the + * two geometries operated on. The Intersects() method can be used to test if + * two geometries intersect. + * + * This method is the same as the C function OGR_G_Intersection(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param poOtherGeom the other geometry intersected with "this" geometry. + * + * @return a new geometry representing the intersection or NULL if there is + * no intersection or an error occurs. + */ + +OGRGeometry *OGRGeometry::Intersection( const OGRGeometry *poOtherGeom ) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + GEOSGeom hGeosProduct = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) + { + hGeosProduct = GEOSIntersection_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); + + if( hGeosProduct != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL && + poOtherGeom->getSpatialReference() != NULL && + poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()) ) + { + poOGRProduct->assignSpatialReference(getSpatialReference()); + } + poOGRProduct = OGRGeometryRebuildCurves(this, poOtherGeom, poOGRProduct); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); + } + } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); + + return poOGRProduct; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Intersection() */ +/************************************************************************/ + +/** + * \brief Compute intersection. + * + * Generates a new geometry which is the region of intersection of the + * two geometries operated on. The OGR_G_Intersects() function can be used to + * test if two geometries intersect. + * + * This function is the same as the C++ method OGRGeometry::Intersection(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry. + * @param hOther the other geometry. + * + * @return a new geometry representing the intersection or NULL if there is + * no intersection or an error occurs. + */ + +OGRGeometryH OGR_G_Intersection( OGRGeometryH hThis, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_Intersection", NULL ); + + return (OGRGeometryH) + ((OGRGeometry *) hThis)->Intersection( (OGRGeometry *) hOther ); +} + +/************************************************************************/ +/* Union() */ +/************************************************************************/ + +/** + * \brief Compute union. + * + * Generates a new geometry which is the region of union of the + * two geometries operated on. + * + * This method is the same as the C function OGR_G_Union(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param poOtherGeom the other geometry unioned with "this" geometry. + * + * @return a new geometry representing the union or NULL if an error occurs. + */ + +OGRGeometry *OGRGeometry::Union( const OGRGeometry *poOtherGeom ) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + GEOSGeom hGeosProduct = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) + { + hGeosProduct = GEOSUnion_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); + + if( hGeosProduct != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL && + poOtherGeom->getSpatialReference() != NULL && + poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()) ) + { + poOGRProduct->assignSpatialReference(getSpatialReference()); + } + poOGRProduct = OGRGeometryRebuildCurves(this, poOtherGeom, poOGRProduct); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); + } + } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); + + return poOGRProduct; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Union() */ +/************************************************************************/ + +/** + * \brief Compute union. + * + * Generates a new geometry which is the region of union of the + * two geometries operated on. + * + * This function is the same as the C++ method OGRGeometry::Union(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry. + * @param hOther the other geometry. + * + * @return a new geometry representing the union or NULL if an error occurs. + */ + +OGRGeometryH OGR_G_Union( OGRGeometryH hThis, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_Union", NULL ); + + return (OGRGeometryH) + ((OGRGeometry *) hThis)->Union( (OGRGeometry *) hOther ); +} + +/************************************************************************/ +/* UnionCascaded() */ +/************************************************************************/ + +/** + * \brief Compute union using cascading. + * + * This method is the same as the C function OGR_G_UnionCascaded(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @return a new geometry representing the union or NULL if an error occurs. + * + * @since OGR 1.8.0 + */ + +OGRGeometry *OGRGeometry::UnionCascaded() const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; +#else + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hGeosProduct = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL ) + { + hGeosProduct = GEOSUnionCascaded_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + + if( hGeosProduct != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL ) + poOGRProduct->assignSpatialReference(getSpatialReference()); + poOGRProduct = OGRGeometryRebuildCurves(this, NULL, poOGRProduct); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); + } + } + freeGEOSContext( hGEOSCtxt ); + + return poOGRProduct; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_UnionCascaded() */ +/************************************************************************/ + +/** + * \brief Compute union using cascading. + * + * This function is the same as the C++ method OGRGeometry::UnionCascaded(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry. + * + * @return a new geometry representing the union or NULL if an error occurs. + */ + +OGRGeometryH OGR_G_UnionCascaded( OGRGeometryH hThis ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_UnionCascaded", NULL ); + + return (OGRGeometryH) + ((OGRGeometry *) hThis)->UnionCascaded(); +} + +/************************************************************************/ +/* Difference() */ +/************************************************************************/ + +/** + * \brief Compute difference. + * + * Generates a new geometry which is the region of this geometry with the + * region of the second geometry removed. + * + * This method is the same as the C function OGR_G_Difference(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param poOtherGeom the other geometry removed from "this" geometry. + * + * @return a new geometry representing the difference or NULL if the + * difference is empty or an error occurs. + */ + +OGRGeometry *OGRGeometry::Difference( const OGRGeometry *poOtherGeom ) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + GEOSGeom hGeosProduct = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) + { + hGeosProduct = GEOSDifference_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); + + if( hGeosProduct != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL && + poOtherGeom->getSpatialReference() != NULL && + poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()) ) + { + poOGRProduct->assignSpatialReference(getSpatialReference()); + } + poOGRProduct = OGRGeometryRebuildCurves(this, poOtherGeom, poOGRProduct); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); + } + } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); + + return poOGRProduct; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Difference() */ +/************************************************************************/ + +/** + * \brief Compute difference. + * + * Generates a new geometry which is the region of this geometry with the + * region of the other geometry removed. + * + * This function is the same as the C++ method OGRGeometry::Difference(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry. + * @param hOther the other geometry. + * + * @return a new geometry representing the difference or NULL if the + * difference is empty or an error occurs. + */ + +OGRGeometryH OGR_G_Difference( OGRGeometryH hThis, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_Difference", NULL ); + + return (OGRGeometryH) + ((OGRGeometry *) hThis)->Difference( (OGRGeometry *) hOther ); +} + +/************************************************************************/ +/* SymDifference() */ +/************************************************************************/ + +/** + * \brief Compute symmetric difference. + * + * Generates a new geometry which is the symmetric difference of this + * geometry and the second geometry passed into the method. + * + * This method is the same as the C function OGR_G_SymDifference(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param poOtherGeom the other geometry. + * + * @return a new geometry representing the symmetric difference or NULL if the + * difference is empty or an error occurs. + * + * @since OGR 1.8.0 + */ + +OGRGeometry * +OGRGeometry::SymDifference( const OGRGeometry *poOtherGeom ) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + GEOSGeom hGeosProduct = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) + { + hGeosProduct = GEOSSymDifference_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); + + if( hGeosProduct != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct); + if( poOGRProduct != NULL && getSpatialReference() != NULL && + poOtherGeom->getSpatialReference() != NULL && + poOtherGeom->getSpatialReference()->IsSame(getSpatialReference()) ) + { + poOGRProduct->assignSpatialReference(getSpatialReference()); + } + poOGRProduct = OGRGeometryRebuildCurves(this, poOtherGeom, poOGRProduct); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); + } + } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); + + return poOGRProduct; + +#endif /* HAVE_GEOS */ +} + + +/** + * \brief Compute symmetric difference (deprecated) + * + * @deprecated + * + * @see OGRGeometry::SymDifference() + */ +OGRGeometry * +OGRGeometry::SymmetricDifference( const OGRGeometry *poOtherGeom ) const + +{ + return SymDifference( poOtherGeom ); +} + +/************************************************************************/ +/* OGR_G_SymDifference() */ +/************************************************************************/ + +/** + * \brief Compute symmetric difference. + * + * Generates a new geometry which is the symmetric difference of this + * geometry and the other geometry. + * + * This function is the same as the C++ method OGRGeometry::SymmetricDifference(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry. + * @param hOther the other geometry. + * + * @return a new geometry representing the symmetric difference or NULL if the + * difference is empty or an error occurs. + * + * @since OGR 1.8.0 + */ + +OGRGeometryH OGR_G_SymDifference( OGRGeometryH hThis, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_SymDifference", NULL ); + + return (OGRGeometryH) + ((OGRGeometry *) hThis)->SymDifference( (OGRGeometry *) hOther ); +} + +/** + * \brief Compute symmetric difference (deprecated) + * + * @deprecated + * + * @see OGR_G_SymmetricDifference() + */ +OGRGeometryH OGR_G_SymmetricDifference( OGRGeometryH hThis, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_SymmetricDifference", NULL ); + + return (OGRGeometryH) + ((OGRGeometry *) hThis)->SymDifference( (OGRGeometry *) hOther ); +} + +/************************************************************************/ +/* Disjoint() */ +/************************************************************************/ + +/** + * \brief Test for disjointness. + * + * Tests if this geometry and the other passed into the method are disjoint. + * + * This method is the same as the C function OGR_G_Disjoint(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param poOtherGeom the geometry to compare to this geometry. + * + * @return TRUE if they are disjoint, otherwise FALSE. + */ + +OGRBoolean +OGRGeometry::Disjoint( const OGRGeometry *poOtherGeom ) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return FALSE; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + OGRBoolean bResult = FALSE; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) + { + bResult = GEOSDisjoint_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); + } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); + + return bResult; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Disjoint() */ +/************************************************************************/ + +/** + * \brief Test for disjointness. + * + * Tests if this geometry and the other geometry are disjoint. + * + * This function is the same as the C++ method OGRGeometry::Disjoint(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry to compare. + * @param hOther the other geometry to compare. + * + * @return TRUE if they are disjoint, otherwise FALSE. + */ +int OGR_G_Disjoint( OGRGeometryH hThis, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_Disjoint", FALSE ); + + return ((OGRGeometry *) hThis)->Disjoint( (OGRGeometry *) hOther ); +} + +/************************************************************************/ +/* Touches() */ +/************************************************************************/ + +/** + * \brief Test for touching. + * + * Tests if this geometry and the other passed into the method are touching. + * + * This method is the same as the C function OGR_G_Touches(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param poOtherGeom the geometry to compare to this geometry. + * + * @return TRUE if they are touching, otherwise FALSE. + */ + +OGRBoolean +OGRGeometry::Touches( const OGRGeometry *poOtherGeom ) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return FALSE; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + OGRBoolean bResult = FALSE; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); + + if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) + { + bResult = GEOSTouches_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); + } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); + + return bResult; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Touches() */ +/************************************************************************/ +/** + * \brief Test for touching. + * + * Tests if this geometry and the other geometry are touching. + * + * This function is the same as the C++ method OGRGeometry::Touches(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry to compare. + * @param hOther the other geometry to compare. + * + * @return TRUE if they are touching, otherwise FALSE. + */ + +int OGR_G_Touches( OGRGeometryH hThis, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_Touches", FALSE ); + + return ((OGRGeometry *) hThis)->Touches( (OGRGeometry *) hOther ); +} + +/************************************************************************/ +/* Crosses() */ +/************************************************************************/ + +/** + * \brief Test for crossing. + * + * Tests if this geometry and the other passed into the method are crossing. + * + * This method is the same as the C function OGR_G_Crosses(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param poOtherGeom the geometry to compare to this geometry. + * + * @return TRUE if they are crossing, otherwise FALSE. + */ + +OGRBoolean +OGRGeometry::Crosses( const OGRGeometry *poOtherGeom ) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return FALSE; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + OGRBoolean bResult = FALSE; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); + + if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) + { + bResult = GEOSCrosses_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); + } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); + + return bResult; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Crosses() */ +/************************************************************************/ +/** + * \brief Test for crossing. + * + * Tests if this geometry and the other geometry are crossing. + * + * This function is the same as the C++ method OGRGeometry::Crosses(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry to compare. + * @param hOther the other geometry to compare. + * + * @return TRUE if they are crossing, otherwise FALSE. + */ + +int OGR_G_Crosses( OGRGeometryH hThis, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_Crosses", FALSE ); + + return ((OGRGeometry *) hThis)->Crosses( (OGRGeometry *) hOther ); +} + +/************************************************************************/ +/* Within() */ +/************************************************************************/ + +/** + * \brief Test for containment. + * + * Tests if actual geometry object is within the passed geometry. + * + * This method is the same as the C function OGR_G_Within(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param poOtherGeom the geometry to compare to this geometry. + * + * @return TRUE if poOtherGeom is within this geometry, otherwise FALSE. + */ + +OGRBoolean +OGRGeometry::Within( const OGRGeometry *poOtherGeom ) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return FALSE; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + OGRBoolean bResult = FALSE; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) + { + bResult = GEOSWithin_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); + } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); + + return bResult; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Within() */ +/************************************************************************/ + +/** + * \brief Test for containment. + * + * Tests if this geometry is within the other geometry. + * + * This function is the same as the C++ method OGRGeometry::Within(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry to compare. + * @param hOther the other geometry to compare. + * + * @return TRUE if hThis is within hOther, otherwise FALSE. + */ +int OGR_G_Within( OGRGeometryH hThis, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_Within", FALSE ); + + return ((OGRGeometry *) hThis)->Within( (OGRGeometry *) hOther ); +} + +/************************************************************************/ +/* Contains() */ +/************************************************************************/ + +/** + * \brief Test for containment. + * + * Tests if actual geometry object contains the passed geometry. + * + * This method is the same as the C function OGR_G_Contains(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param poOtherGeom the geometry to compare to this geometry. + * + * @return TRUE if poOtherGeom contains this geometry, otherwise FALSE. + */ + +OGRBoolean +OGRGeometry::Contains( const OGRGeometry *poOtherGeom ) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return FALSE; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + OGRBoolean bResult = FALSE; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) + { + bResult = GEOSContains_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); + } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); + + return bResult; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Contains() */ +/************************************************************************/ + +/** + * \brief Test for containment. + * + * Tests if this geometry contains the other geometry. + * + * This function is the same as the C++ method OGRGeometry::Contains(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry to compare. + * @param hOther the other geometry to compare. + * + * @return TRUE if hThis contains hOther geometry, otherwise FALSE. + */ +int OGR_G_Contains( OGRGeometryH hThis, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_Contains", FALSE ); + + return ((OGRGeometry *) hThis)->Contains( (OGRGeometry *) hOther ); +} + +/************************************************************************/ +/* Overlaps() */ +/************************************************************************/ + +/** + * \brief Test for overlap. + * + * Tests if this geometry and the other passed into the method overlap, that is + * their intersection has a non-zero area. + * + * This method is the same as the C function OGR_G_Overlaps(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param poOtherGeom the geometry to compare to this geometry. + * + * @return TRUE if they are overlapping, otherwise FALSE. + */ + +OGRBoolean +OGRGeometry::Overlaps( const OGRGeometry *poOtherGeom ) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return FALSE; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + OGRBoolean bResult = FALSE; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hOtherGeosGeom = poOtherGeom->exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL && hOtherGeosGeom != NULL ) + { + bResult = GEOSOverlaps_r( hGEOSCtxt, hThisGeosGeom, hOtherGeosGeom ); + } + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + freeGEOSContext( hGEOSCtxt ); + + return bResult; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Overlaps() */ +/************************************************************************/ +/** + * \brief Test for overlap. + * + * Tests if this geometry and the other geometry overlap, that is their + * intersection has a non-zero area. + * + * This function is the same as the C++ method OGRGeometry::Overlaps(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry to compare. + * @param hOther the other geometry to compare. + * + * @return TRUE if they are overlapping, otherwise FALSE. + */ + +int OGR_G_Overlaps( OGRGeometryH hThis, OGRGeometryH hOther ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_Overlaps", FALSE ); + + return ((OGRGeometry *) hThis)->Overlaps( (OGRGeometry *) hOther ); +} + +/************************************************************************/ +/* closeRings() */ +/************************************************************************/ + +/** + * \brief Force rings to be closed. + * + * If this geometry, or any contained geometries has polygon rings that + * are not closed, they will be closed by adding the starting point at + * the end. + */ + +void OGRGeometry::closeRings() + +{ +} + +/************************************************************************/ +/* OGR_G_CloseRings() */ +/************************************************************************/ + +/** + * \brief Force rings to be closed. + * + * If this geometry, or any contained geometries has polygon rings that + * are not closed, they will be closed by adding the starting point at + * the end. + * + * @param hGeom handle to the geometry. + */ + +void OGR_G_CloseRings( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER0( hGeom, "OGR_G_CloseRings" ); + + ((OGRGeometry *) hGeom)->closeRings(); +} + +/************************************************************************/ +/* Centroid() */ +/************************************************************************/ + +/** + * \brief Compute the geometry centroid. + * + * The centroid location is applied to the passed in OGRPoint object. + * The centroid is not necessarily within the geometry. + * + * This method relates to the SFCOM ISurface::get_Centroid() method + * however the current implementation based on GEOS can operate on other + * geometry types such as multipoint, linestring, geometrycollection such as + * multipolygons. + * OGC SF SQL 1.1 defines the operation for surfaces (polygons). + * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces (multipolygons). + * + * This function is the same as the C function OGR_G_Centroid(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @return OGRERR_NONE on success or OGRERR_FAILURE on error. + * + * @since OGR 1.8.0 as a OGRGeometry method (previously was restricted to OGRPolygon) + */ + +int OGRGeometry::Centroid( OGRPoint *poPoint ) const + +{ + if( poPoint == NULL ) + return OGRERR_FAILURE; + +#ifndef HAVE_GEOS + // notdef ... not implemented yet. + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return OGRERR_FAILURE; + +#else + + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + + if( hThisGeosGeom != NULL ) + { + hOtherGeosGeom = GEOSGetCentroid_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + + if( hOtherGeosGeom == NULL ) + { + freeGEOSContext( hGEOSCtxt ); + return OGRERR_FAILURE; + } + + OGRGeometry *poCentroidGeom = + OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom ); + + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + + if (poCentroidGeom == NULL) + { + freeGEOSContext( hGEOSCtxt ); + return OGRERR_FAILURE; + } + if (wkbFlatten(poCentroidGeom->getGeometryType()) != wkbPoint) + { + delete poCentroidGeom; + freeGEOSContext( hGEOSCtxt ); + return OGRERR_FAILURE; + } + + if( poCentroidGeom != NULL && getSpatialReference() != NULL ) + poCentroidGeom->assignSpatialReference(getSpatialReference()); + + OGRPoint *poCentroid = (OGRPoint *) poCentroidGeom; + if( !poCentroid->IsEmpty() ) + { + poPoint->setX( poCentroid->getX() ); + poPoint->setY( poCentroid->getY() ); + } + else + { + poPoint->empty(); + } + + delete poCentroidGeom; + + freeGEOSContext( hGEOSCtxt ); + return OGRERR_NONE; + } + else + { + freeGEOSContext( hGEOSCtxt ); + return OGRERR_FAILURE; + } + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Centroid() */ +/************************************************************************/ + +/** + * \brief Compute the geometry centroid. + * + * The centroid location is applied to the passed in OGRPoint object. + * The centroid is not necessarily within the geometry. + * + * This method relates to the SFCOM ISurface::get_Centroid() method + * however the current implementation based on GEOS can operate on other + * geometry types such as multipoint, linestring, geometrycollection such as + * multipolygons. + * OGC SF SQL 1.1 defines the operation for surfaces (polygons). + * SQL/MM-Part 3 defines the operation for surfaces and multisurfaces (multipolygons). + * + * This function is the same as the C++ method OGRGeometry::Centroid(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @return OGRERR_NONE on success or OGRERR_FAILURE on error. + */ + +int OGR_G_Centroid( OGRGeometryH hGeom, OGRGeometryH hCentroidPoint ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_Centroid", OGRERR_FAILURE ); + + OGRGeometry *poGeom = ((OGRGeometry *) hGeom); + OGRPoint *poCentroid = ((OGRPoint *) hCentroidPoint); + + if( poCentroid == NULL ) + return OGRERR_FAILURE; + + if( wkbFlatten(poCentroid->getGeometryType()) != wkbPoint ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Passed wrong geometry type as centroid argument." ); + return OGRERR_FAILURE; + } + + return poGeom->Centroid( poCentroid ); +} + +/************************************************************************/ +/* OGR_G_PointOnSurface() */ +/************************************************************************/ + +/** + * \brief Returns a point guaranteed to lie on the surface. + * + * This method relates to the SFCOM ISurface::get_PointOnSurface() method + * however the current implementation based on GEOS can operate on other + * geometry types than the types that are supported by SQL/MM-Part 3 : + * surfaces (polygons) and multisurfaces (multipolygons). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hGeom the geometry to operate on. + * @return a point guaranteed to lie on the surface or NULL if an error + * occured. + * + * @since OGR 1.10 + */ + +OGRGeometryH OGR_G_PointOnSurface( OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER1( hGeom, "OGR_G_PointOnSurface", NULL ); + +#ifndef HAVE_GEOS + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; +#else + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hOtherGeosGeom = NULL; + OGRGeometry* poThis = (OGRGeometry*) hGeom; + + GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext(); + hThisGeosGeom = poThis->exportToGEOS(hGEOSCtxt); + + if( hThisGeosGeom != NULL ) + { + hOtherGeosGeom = GEOSPointOnSurface_r( hGEOSCtxt, hThisGeosGeom ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + + if( hOtherGeosGeom == NULL ) + { + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } + + OGRGeometry *poInsidePointGeom = (OGRGeometry *) + OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hOtherGeosGeom ); + + GEOSGeom_destroy_r( hGEOSCtxt, hOtherGeosGeom ); + + if (poInsidePointGeom == NULL) + { + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } + if (wkbFlatten(poInsidePointGeom->getGeometryType()) != wkbPoint) + { + delete poInsidePointGeom; + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } + + if( poInsidePointGeom != NULL && poThis->getSpatialReference() != NULL ) + poInsidePointGeom->assignSpatialReference(poThis->getSpatialReference()); + + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return (OGRGeometryH) poInsidePointGeom; + } + else + { + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } +#endif +} + +/************************************************************************/ +/* Simplify() */ +/************************************************************************/ + +/** + * \brief Simplify the geometry. + * + * This function is the same as the C function OGR_G_Simplify(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param dTolerance the distance tolerance for the simplification. + * + * @return the simplified geometry or NULL if an error occurs. + * + * @since OGR 1.8.0 + */ + +OGRGeometry *OGRGeometry::Simplify(double dTolerance) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hGeosProduct = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL ) + { + hGeosProduct = GEOSSimplify_r( hGEOSCtxt, hThisGeosGeom, dTolerance ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + if( hGeosProduct != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct ); + if( poOGRProduct != NULL && getSpatialReference() != NULL ) + poOGRProduct->assignSpatialReference(getSpatialReference()); + poOGRProduct = OGRGeometryRebuildCurves(this, NULL, poOGRProduct); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); + } + } + freeGEOSContext( hGEOSCtxt ); + return poOGRProduct; + +#endif /* HAVE_GEOS */ + +} + +/************************************************************************/ +/* OGR_G_Simplify() */ +/************************************************************************/ + +/** + * \brief Compute a simplified geometry. + * + * This function is the same as the C++ method OGRGeometry::Simplify(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry. + * @param dTolerance the distance tolerance for the simplification. + * + * @return the simplified geometry or NULL if an error occurs. + * + * @since OGR 1.8.0 + */ + +OGRGeometryH OGR_G_Simplify( OGRGeometryH hThis, double dTolerance ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_Simplify", NULL ); + return (OGRGeometryH) ((OGRGeometry *) hThis)->Simplify( dTolerance ); +} + +/************************************************************************/ +/* SimplifyPreserveTopology() */ +/************************************************************************/ + +/** + * \brief Simplify the geometry while preserving topology. + * + * This function is the same as the C function OGR_G_SimplifyPreserveTopology(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param dTolerance the distance tolerance for the simplification. + * + * @return the simplified geometry or NULL if an error occurs. + * + * @since OGR 1.9.0 + */ + +OGRGeometry *OGRGeometry::SimplifyPreserveTopology(double dTolerance) const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hGeosProduct = NULL; + OGRGeometry *poOGRProduct = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + if( hThisGeosGeom != NULL ) + { + hGeosProduct = GEOSTopologyPreserveSimplify_r( hGEOSCtxt, hThisGeosGeom, dTolerance ); + GEOSGeom_destroy_r( hGEOSCtxt, hThisGeosGeom ); + if( hGeosProduct != NULL ) + { + poOGRProduct = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosProduct ); + if( poOGRProduct != NULL && getSpatialReference() != NULL ) + poOGRProduct->assignSpatialReference(getSpatialReference()); + poOGRProduct = OGRGeometryRebuildCurves(this, NULL, poOGRProduct); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosProduct ); + } + } + freeGEOSContext( hGEOSCtxt ); + return poOGRProduct; + +#endif /* HAVE_GEOS */ + +} + +/************************************************************************/ +/* OGR_G_SimplifyPreserveTopology() */ +/************************************************************************/ + +/** + * \brief Simplify the geometry while preserving topology. + * + * This function is the same as the C++ method OGRGeometry::SimplifyPreserveTopology(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hThis the geometry. + * @param dTolerance the distance tolerance for the simplification. + * + * @return the simplified geometry or NULL if an error occurs. + * + * @since OGR 1.9.0 + */ + +OGRGeometryH OGR_G_SimplifyPreserveTopology( OGRGeometryH hThis, double dTolerance ) + +{ + VALIDATE_POINTER1( hThis, "OGR_G_SimplifyPreserveTopology", NULL ); + return (OGRGeometryH) ((OGRGeometry *) hThis)->SimplifyPreserveTopology( dTolerance ); +} + +/************************************************************************/ +/* Polygonize() */ +/************************************************************************/ +/* Contributor: Alessandro Furieri, a.furieri@lqt.it */ +/* Developed for Faunalia (http://www.faunalia.it) with funding from */ +/* Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED */ +/* AMBIENTALE */ +/************************************************************************/ + +/** + * \brief Polygonizes a set of sparse edges. + * + * A new geometry object is created and returned containing a collection + * of reassembled Polygons: NULL will be returned if the input collection + * doesn't corresponds to a MultiLinestring, or when reassembling Edges + * into Polygons is impossible due to topogical inconsistencies. + * + * This method is the same as the C function OGR_G_Polygonize(). + * + * This method is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this method will always fail, + * issuing a CPLE_NotSupported error. + * + * @return a newly allocated geometry now owned by the caller, or NULL on failure. + * + * @since OGR 1.9.0 + */ + +OGRGeometry *OGRGeometry::Polygonize() const + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + + OGRGeometryCollection *poColl = NULL; + if( wkbFlatten(getGeometryType()) == wkbGeometryCollection || + wkbFlatten(getGeometryType()) == wkbMultiLineString ) + poColl = (OGRGeometryCollection *)this; + else + return NULL; + + int iCount = poColl->getNumGeometries(); + + GEOSGeom *hGeosGeomList = NULL; + GEOSGeom hGeosPolygs = NULL; + OGRGeometry *poPolygsOGRGeom = NULL; + int bError = FALSE; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + + hGeosGeomList = new GEOSGeom [iCount]; + for ( int ig = 0; ig < iCount; ig++) + { + GEOSGeom hGeosGeom = NULL; + OGRGeometry * poChild = (OGRGeometry*)poColl->getGeometryRef(ig); + if( poChild == NULL || + wkbFlatten(poChild->getGeometryType()) != wkbLineString ) + bError = TRUE; + else + { + hGeosGeom = poChild->exportToGEOS(hGEOSCtxt); + if( hGeosGeom == NULL) + bError = TRUE; + } + *(hGeosGeomList + ig) = hGeosGeom; + } + + if( bError == FALSE ) + { + hGeosPolygs = GEOSPolygonize_r( hGEOSCtxt, hGeosGeomList, iCount ); + + if( hGeosPolygs != NULL ) + { + poPolygsOGRGeom = OGRGeometryFactory::createFromGEOS(hGEOSCtxt, hGeosPolygs); + if( poPolygsOGRGeom != NULL && getSpatialReference() != NULL ) + poPolygsOGRGeom->assignSpatialReference(getSpatialReference()); + GEOSGeom_destroy_r( hGEOSCtxt, hGeosPolygs); + } + } + + for ( int ig = 0; ig < iCount; ig++) + { + GEOSGeom hGeosGeom = *(hGeosGeomList + ig); + if( hGeosGeom != NULL) + GEOSGeom_destroy_r( hGEOSCtxt, hGeosGeom ); + } + delete [] hGeosGeomList; + freeGEOSContext( hGEOSCtxt ); + + return poPolygsOGRGeom; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* OGR_G_Polygonize() */ +/************************************************************************/ +/** + * \brief Polygonizes a set of sparse edges. + * + * A new geometry object is created and returned containing a collection + * of reassembled Polygons: NULL will be returned if the input collection + * doesn't corresponds to a MultiLinestring, or when reassembling Edges + * into Polygons is impossible due to topogical inconsistencies. + * + * This function is the same as the C++ method OGRGeometry::Polygonize(). + * + * This function is built on the GEOS library, check it for the definition + * of the geometry operation. + * If OGR is built without the GEOS library, this function will always fail, + * issuing a CPLE_NotSupported error. + * + * @param hTarget The Geometry to be polygonized. + * + * @return a handle to a newly allocated geometry now owned by the caller, + * or NULL on failure. + * + * @since OGR 1.9.0 + */ + +OGRGeometryH OGR_G_Polygonize( OGRGeometryH hTarget ) + +{ + VALIDATE_POINTER1( hTarget, "OGR_G_Polygonize", NULL ); + + return (OGRGeometryH) ((OGRGeometry *) hTarget)->Polygonize(); +} + +/************************************************************************/ +/* swapXY() */ +/************************************************************************/ + +/** + * \brief Swap x and y coordinates. + * + * @since OGR 1.8.0 + */ + +void OGRGeometry::swapXY() + +{ +} + +/************************************************************************/ +/* Prepared geometry API */ +/************************************************************************/ + +/* GEOS >= 3.1.0 for prepared geometries */ +#if defined(HAVE_GEOS) +#define HAVE_GEOS_PREPARED_GEOMETRY +#endif + +#ifdef HAVE_GEOS_PREPARED_GEOMETRY +struct _OGRPreparedGeometry +{ + GEOSContextHandle_t hGEOSCtxt; + GEOSGeom hGEOSGeom; + const GEOSPreparedGeometry* poPreparedGEOSGeom; +}; +#endif + +/************************************************************************/ +/* OGRHasPreparedGeometrySupport() */ +/************************************************************************/ + +int OGRHasPreparedGeometrySupport() +{ +#ifdef HAVE_GEOS_PREPARED_GEOMETRY + return TRUE; +#else + return FALSE; +#endif +} + +/************************************************************************/ +/* OGRCreatePreparedGeometry() */ +/************************************************************************/ + +OGRPreparedGeometry* OGRCreatePreparedGeometry( const OGRGeometry* poGeom ) +{ +#ifdef HAVE_GEOS_PREPARED_GEOMETRY + GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext(); + GEOSGeom hGEOSGeom = poGeom->exportToGEOS(hGEOSCtxt); + if( hGEOSGeom == NULL ) + { + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } + const GEOSPreparedGeometry* poPreparedGEOSGeom = GEOSPrepare_r(hGEOSCtxt, hGEOSGeom); + if( poPreparedGEOSGeom == NULL ) + { + GEOSGeom_destroy_r( hGEOSCtxt, hGEOSGeom ); + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } + + OGRPreparedGeometry* poPreparedGeom = new OGRPreparedGeometry; + poPreparedGeom->hGEOSCtxt = hGEOSCtxt; + poPreparedGeom->hGEOSGeom = hGEOSGeom; + poPreparedGeom->poPreparedGEOSGeom = poPreparedGEOSGeom; + + return poPreparedGeom; +#else + return NULL; +#endif +} + +/************************************************************************/ +/* OGRDestroyPreparedGeometry() */ +/************************************************************************/ + +void OGRDestroyPreparedGeometry( OGRPreparedGeometry* poPreparedGeom ) +{ +#ifdef HAVE_GEOS_PREPARED_GEOMETRY + if( poPreparedGeom != NULL ) + { + GEOSPreparedGeom_destroy_r(poPreparedGeom->hGEOSCtxt, poPreparedGeom->poPreparedGEOSGeom); + GEOSGeom_destroy_r( poPreparedGeom->hGEOSCtxt, poPreparedGeom->hGEOSGeom ); + OGRGeometry::freeGEOSContext( poPreparedGeom->hGEOSCtxt ); + delete poPreparedGeom; + } +#endif +} + +/************************************************************************/ +/* OGRPreparedGeometryIntersects() */ +/************************************************************************/ + +int OGRPreparedGeometryIntersects( const OGRPreparedGeometry* poPreparedGeom, + const OGRGeometry* poOtherGeom ) +{ +#ifdef HAVE_GEOS_PREPARED_GEOMETRY + if( poPreparedGeom == NULL || poOtherGeom == NULL ) + return FALSE; + + GEOSGeom hGEOSOtherGeom = poOtherGeom->exportToGEOS(poPreparedGeom->hGEOSCtxt); + if( hGEOSOtherGeom == NULL ) + return FALSE; + + int bRet = GEOSPreparedIntersects_r(poPreparedGeom->hGEOSCtxt, + poPreparedGeom->poPreparedGEOSGeom, + hGEOSOtherGeom); + GEOSGeom_destroy_r( poPreparedGeom->hGEOSCtxt, hGEOSOtherGeom ); + + return bRet; +#else + return FALSE; +#endif +} + +/************************************************************************/ +/* OGRGeometryFromEWKB() */ +/************************************************************************/ + +/* Flags for creating WKB format for PostGIS */ +#define WKBZOFFSET 0x80000000 +#define WKBMOFFSET 0x40000000 +#define WKBSRIDFLAG 0x20000000 +#define WKBBBOXFLAG 0x10000000 + +OGRGeometry *OGRGeometryFromEWKB( GByte *pabyWKB, int nLength, int* pnSRID, + int bIsPostGIS1_EWKB ) + +{ + OGRGeometry *poGeometry = NULL; + unsigned int ewkbFlags = 0; + + if (nLength < 5) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid EWKB content : %d bytes", nLength ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Detect XYZM variant of PostGIS EWKB */ +/* */ +/* OGR does not support parsing M coordinate, */ +/* so we return NULL geometry. */ +/* -------------------------------------------------------------------- */ + memcpy(&ewkbFlags, pabyWKB+1, 4); + OGRwkbByteOrder eByteOrder = (pabyWKB[0] == 0 ? wkbXDR : wkbNDR); + if( OGR_SWAP( eByteOrder ) ) + ewkbFlags= CPL_SWAP32(ewkbFlags); + + if (ewkbFlags & WKBMOFFSET) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Reading EWKB with 4-dimensional coordinates (XYZM) is not supported" ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* PostGIS EWKB format includes an SRID, but this won't be */ +/* understood by OGR, so if the SRID flag is set, we remove the */ +/* SRID (bytes at offset 5 to 8). */ +/* -------------------------------------------------------------------- */ + if( nLength > 9 && + ((pabyWKB[0] == 0 /* big endian */ && (pabyWKB[1] & 0x20) ) + || (pabyWKB[0] != 0 /* little endian */ && (pabyWKB[4] & 0x20))) ) + { + if( pnSRID ) + { + memcpy(pnSRID, pabyWKB+5, 4); + if( OGR_SWAP( eByteOrder ) ) + *pnSRID = CPL_SWAP32(*pnSRID); + } + memmove( pabyWKB+5, pabyWKB+9, nLength-9 ); + nLength -= 4; + if( pabyWKB[0] == 0 ) + pabyWKB[1] &= (~0x20); + else + pabyWKB[4] &= (~0x20); + } + +/* -------------------------------------------------------------------- */ +/* Try to ingest the geometry. */ +/* -------------------------------------------------------------------- */ + OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeometry, nLength, + (bIsPostGIS1_EWKB) ? wkbVariantPostGIS1 : wkbVariantOldOgc ); + + return poGeometry; +} + +/************************************************************************/ +/* OGRGeometryFromHexEWKB() */ +/************************************************************************/ + +OGRGeometry *OGRGeometryFromHexEWKB( const char *pszBytea, int* pnSRID, + int bIsPostGIS1_EWKB ) + +{ + GByte *pabyWKB; + int nWKBLength=0; + OGRGeometry *poGeometry; + + if( pszBytea == NULL ) + return NULL; + + pabyWKB = CPLHexToBinary(pszBytea, &nWKBLength); + + poGeometry = OGRGeometryFromEWKB(pabyWKB, nWKBLength, pnSRID, bIsPostGIS1_EWKB); + + CPLFree(pabyWKB); + + return poGeometry; +} + +/************************************************************************/ +/* OGRGeometryToHexEWKB() */ +/************************************************************************/ + +char* OGRGeometryToHexEWKB( OGRGeometry * poGeometry, int nSRSId, + int bIsPostGIS1_EWKB ) +{ + GByte *pabyWKB; + char *pszTextBuf; + char *pszTextBufCurrent; + char *pszHex; + + int nWkbSize = poGeometry->WkbSize(); + pabyWKB = (GByte *) CPLMalloc(nWkbSize); + + if( poGeometry->exportToWkb( wkbNDR, pabyWKB, + bIsPostGIS1_EWKB ? wkbVariantPostGIS1 : wkbVariantOldOgc ) != OGRERR_NONE ) + { + CPLFree( pabyWKB ); + return CPLStrdup(""); + } + + /* When converting to hex, each byte takes 2 hex characters. In addition + we add in 8 characters to represent the SRID integer in hex, and + one for a null terminator */ + + int pszSize = nWkbSize*2 + 8 + 1; + pszTextBuf = (char *) CPLMalloc(pszSize); + pszTextBufCurrent = pszTextBuf; + + /* Convert the 1st byte, which is the endianess flag, to hex. */ + pszHex = CPLBinaryToHex( 1, pabyWKB ); + strcpy(pszTextBufCurrent, pszHex ); + CPLFree ( pszHex ); + pszTextBufCurrent += 2; + + /* Next, get the geom type which is bytes 2 through 5 */ + GUInt32 geomType; + memcpy( &geomType, pabyWKB+1, 4 ); + + /* Now add the SRID flag if an SRID is provided */ + if (nSRSId > 0) + { + /* Change the flag to wkbNDR (little) endianess */ + GUInt32 nGSrsFlag = CPL_LSBWORD32( WKBSRIDFLAG ); + /* Apply the flag */ + geomType = geomType | nGSrsFlag; + } + + /* Now write the geom type which is 4 bytes */ + pszHex = CPLBinaryToHex( 4, (GByte*) &geomType ); + strcpy(pszTextBufCurrent, pszHex ); + CPLFree ( pszHex ); + pszTextBufCurrent += 8; + + /* Now include SRID if provided */ + if (nSRSId > 0) + { + /* Force the srsid to wkbNDR (little) endianess */ + GUInt32 nGSRSId = CPL_LSBWORD32( nSRSId ); + pszHex = CPLBinaryToHex( sizeof(nGSRSId),(GByte*) &nGSRSId ); + strcpy(pszTextBufCurrent, pszHex ); + CPLFree ( pszHex ); + pszTextBufCurrent += 8; + } + + /* Copy the rest of the data over - subtract + 5 since we already copied 5 bytes above */ + pszHex = CPLBinaryToHex( nWkbSize - 5, pabyWKB + 5 ); + strcpy(pszTextBufCurrent, pszHex ); + CPLFree ( pszHex ); + + CPLFree( pabyWKB ); + + return pszTextBuf; +} + +/** + * \fn void OGRGeometry::segmentize(double dfMaxLength); + * + * \brief Add intermediate vertices to a geometry. + * + * This method modifies the geometry to add intermediate vertices if necessary + * so that the maximum length between 2 consecutives vertices is lower than + * dfMaxLength. + * + * @param dfMaxLength maximum length between 2 consecutives vertices. + */ + + +/************************************************************************/ +/* importPreambuleFromWkb() */ +/************************************************************************/ + +OGRErr OGRGeometry::importPreambuleFromWkb( unsigned char * pabyData, + int nSize, + OGRwkbByteOrder& eByteOrder, + OGRBoolean& b3D, + OGRwkbVariant eWkbVariant ) +{ + if( nSize < 9 && nSize != -1 ) + return OGRERR_NOT_ENOUGH_DATA; + +/* -------------------------------------------------------------------- */ +/* Get the byte order byte. */ +/* -------------------------------------------------------------------- */ + eByteOrder = DB2_V72_FIX_BYTE_ORDER((OGRwkbByteOrder) *pabyData); + if (!( eByteOrder == wkbXDR || eByteOrder == wkbNDR )) + return OGRERR_CORRUPT_DATA; + +/* -------------------------------------------------------------------- */ +/* Get the geometry feature type. */ +/* -------------------------------------------------------------------- */ + OGRwkbGeometryType eGeometryType; + OGRErr err = OGRReadWKBGeometryType( pabyData, eWkbVariant, &eGeometryType, &b3D ); + + if( err != OGRERR_NONE || eGeometryType != wkbFlatten(getGeometryType()) ) + return OGRERR_CORRUPT_DATA; + + return -1; +} + +/************************************************************************/ +/* importPreambuleOfCollectionFromWkb() */ +/* */ +/* Utility method for OGRSimpleCurve, OGRCompoundCurve, */ +/* OGRCurvePolygon and OGRGeometryCollection. */ +/************************************************************************/ + +OGRErr OGRGeometry::importPreambuleOfCollectionFromWkb( unsigned char * pabyData, + int& nSize, + int& nDataOffset, + OGRwkbByteOrder& eByteOrder, + int nMinSubGeomSize, + int& nGeomCount, + OGRwkbVariant eWkbVariant ) +{ + nGeomCount = 0; + OGRBoolean b3D = FALSE; + + OGRErr eErr = importPreambuleFromWkb( pabyData, nSize, eByteOrder, b3D, eWkbVariant ); + if( eErr >= 0 ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Clear existing Geoms. */ +/* -------------------------------------------------------------------- */ + empty(); + + if( b3D ) + setCoordinateDimension(3); + +/* -------------------------------------------------------------------- */ +/* Get the sub-geometry count. */ +/* -------------------------------------------------------------------- */ + memcpy( &nGeomCount, pabyData + 5, 4 ); + + if( OGR_SWAP( eByteOrder ) ) + nGeomCount = CPL_SWAP32(nGeomCount); + + if (nGeomCount < 0 || nGeomCount > INT_MAX / 4) + { + nGeomCount = 0; + return OGRERR_CORRUPT_DATA; + } + + /* Each ring has a minimum of nMinSubGeomSize bytes */ + if (nSize != -1 && nSize - 9 < nGeomCount * nMinSubGeomSize) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Length of input WKB is too small" ); + nGeomCount = 0; + return OGRERR_NOT_ENOUGH_DATA; + } + + nDataOffset = 9; + if( nSize != -1 ) + nSize -= nDataOffset; + + return -1; +} + +/************************************************************************/ +/* importCurveCollectionFromWkt() */ +/* */ +/* Utility method for OGRCompoundCurve, OGRCurvePolygon and */ +/* OGRMultiCurve. */ +/************************************************************************/ + +OGRErr OGRGeometry::importCurveCollectionFromWkt( char ** ppszInput, + int bAllowEmptyComponent, + int bAllowLineString, + int bAllowCurve, + int bAllowCompoundCurve, + OGRErr (*pfnAddCurveDirectly)(OGRGeometry* poSelf, OGRCurve* poCurve) ) + +{ + int bHasZ = FALSE, bHasM = FALSE; + OGRErr eErr = importPreambuleFromWkt(ppszInput, &bHasZ, &bHasM); + if( eErr >= 0 ) + return eErr; + + if( bHasZ ) + setCoordinateDimension(3); + + char szToken[OGR_WKT_TOKEN_MAX]; + const char *pszInput = *ppszInput; + eErr = OGRERR_NONE; + + /* Skip first '(' */ + pszInput = OGRWktReadToken( pszInput, szToken ); + +/* ==================================================================== */ +/* Read each curve in turn. Note that we try to reuse the same */ +/* point list buffer from curve to curve to cut down on */ +/* allocate/deallocate overhead. */ +/* ==================================================================== */ + OGRRawPoint *paoPoints = NULL; + int nMaxPoints = 0; + double *padfZ = NULL; + + do + { + + /* -------------------------------------------------------------------- */ + /* Get the first token, which should be the geometry type. */ + /* -------------------------------------------------------------------- */ + const char* pszInputBefore = pszInput; + pszInput = OGRWktReadToken( pszInput, szToken ); + + /* -------------------------------------------------------------------- */ + /* Do the import. */ + /* -------------------------------------------------------------------- */ + OGRCurve* poCurve = NULL; + if (EQUAL(szToken,"(")) + { + OGRLineString* poLine = new OGRLineString(); + poCurve = poLine; + pszInput = pszInputBefore; + eErr = poLine->importFromWKTListOnly( (char**)&pszInput, bHasZ, bHasM, + paoPoints, nMaxPoints, padfZ ); + } + else if (bAllowEmptyComponent && EQUAL(szToken, "EMPTY") ) + { + poCurve = new OGRLineString(); + } + /* We accept LINESTRING() but this is an extension to the BNF, also */ + /* accepted by PostGIS */ + else if ( (bAllowLineString && EQUAL(szToken,"LINESTRING")) || + (bAllowCurve && !EQUAL(szToken,"LINESTRING") && + !EQUAL(szToken,"COMPOUNDCURVE") && OGR_GT_IsCurve(OGRFromOGCGeomType(szToken))) || + (bAllowCompoundCurve && EQUAL(szToken,"COMPOUNDCURVE")) ) + { + OGRGeometry* poGeom = NULL; + pszInput = pszInputBefore; + eErr = OGRGeometryFactory::createFromWkt( (char **) &pszInput, + NULL, &poGeom ); + poCurve = (OGRCurve*) poGeom; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s", szToken); + eErr = OGRERR_CORRUPT_DATA; + } + + if( eErr == OGRERR_NONE ) + eErr = pfnAddCurveDirectly( this, poCurve ); + if( eErr != OGRERR_NONE ) + { + delete poCurve; + break; + } + +/* -------------------------------------------------------------------- */ +/* Read the delimeter following the surface. */ +/* -------------------------------------------------------------------- */ + pszInput = OGRWktReadToken( pszInput, szToken ); + + } while( szToken[0] == ',' && eErr == OGRERR_NONE ); + + CPLFree( paoPoints ); + CPLFree( padfZ ); + +/* -------------------------------------------------------------------- */ +/* freak if we don't get a closing bracket. */ +/* -------------------------------------------------------------------- */ + + if( eErr != OGRERR_NONE ) + return eErr; + + if( szToken[0] != ')' ) + return OGRERR_CORRUPT_DATA; + + *ppszInput = (char *) pszInput; + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_GT_Flatten() */ +/************************************************************************/ +/** + * \brief Returns the 2D geometry type corresponding to the passed geometry type. + * + * This function is intended to work with geometry types as old-style 99-402 + * extended dimension (Z) WKB types, as well as with newer SFSQL 1.2 and + * ISO SQL/MM Part 3 extended dimension (Z&M) WKB types. + * + * @param eType Input geometry type + * + * @return 2D geometry type corresponding to the passed geometry type. + * + * @since GDAL 2.0 + */ + +OGRwkbGeometryType OGR_GT_Flatten( OGRwkbGeometryType eType ) +{ + eType = (OGRwkbGeometryType) (eType & (~wkb25DBitInternalUse)); + if( eType >= 1001 && eType < 2000 ) /* ISO Z */ + return (OGRwkbGeometryType) (eType - 1000); + if( eType >= 2000 && eType < 3000 ) /* ISO M */ + return (OGRwkbGeometryType) (eType - 2000); + if( eType >= 3000 && eType < 4000 ) /* ISO ZM */ + return (OGRwkbGeometryType) (eType - 3000); + return eType; +} + +/************************************************************************/ +/* OGR_GT_HasZ() */ +/************************************************************************/ +/** + * \brief Return if the geometry type is a 3D geometry type. + * + * @param eType Input geometry type + * + * @return TRUE if the geometry type is a 3D geometry type. + * + * @since GDAL 2.0 + */ + +int OGR_GT_HasZ( OGRwkbGeometryType eType ) +{ + if( eType & wkb25DBitInternalUse ) + return TRUE; + if( eType >= 1001 && eType < 2000 ) + return TRUE; + return FALSE; +} + +/************************************************************************/ +/* OGR_GT_SetZ() */ +/************************************************************************/ +/** + * \brief Returns the 3D geometry type corresponding to the passed geometry type. + * + * @param eType Input geometry type + * + * @return 3D geometry type corresponding to the passed geometry type. + * + * @since GDAL 2.0 + */ + +OGRwkbGeometryType OGR_GT_SetZ( OGRwkbGeometryType eType ) +{ + if( OGR_GT_HasZ(eType) || eType == wkbNone ) + return eType; + if( eType >= wkbUnknown && eType <= wkbGeometryCollection ) + return (OGRwkbGeometryType)(eType | wkb25DBitInternalUse); + else + return (OGRwkbGeometryType)(eType + 1000); +} + +/************************************************************************/ +/* OGR_GT_SetModifier() */ +/************************************************************************/ +/** + * \brief Returns a 2D or 3D geometry type depending on parameter. + * + * @param eType Input geometry type + * @param bHasZ TRUE if the output geometry type must be 3D. + * @param bHasM Must be set to FALSE currently. + * + * @return Output geometry type. + * + * @since GDAL 2.0 + */ + +OGRwkbGeometryType OGR_GT_SetModifier( OGRwkbGeometryType eType, int bHasZ, + CPL_UNUSED int bHasM ) +{ + if( bHasZ ) + return OGR_GT_SetZ(eType); + else + return wkbFlatten(eType); +} + +/************************************************************************/ +/* OGR_GT_IsSubClassOf) */ +/************************************************************************/ +/** + * \brief Returns if a type is a subclass of another one + * + * @param eType Type. + * @param eSuperType Super type + * + * @return TRUE if eType is a subclass of eSuperType. + * + * @since GDAL 2.0 + */ + +int OGR_GT_IsSubClassOf( OGRwkbGeometryType eType, + OGRwkbGeometryType eSuperType ) +{ + eSuperType = wkbFlatten(eSuperType); + eType = wkbFlatten(eType); + + if( eSuperType == eType || eSuperType == wkbUnknown ) + return TRUE; + + if( eSuperType == wkbGeometryCollection ) + return eType == wkbMultiPoint || eType == wkbMultiLineString || + eType == wkbMultiPolygon || eType == wkbMultiCurve || + eType == wkbMultiSurface; + + if( eSuperType == wkbCurvePolygon ) + return eType == wkbPolygon; + + if( eSuperType == wkbMultiCurve ) + return eType == wkbMultiLineString; + + if( eSuperType == wkbMultiSurface ) + return eType == wkbMultiPolygon; + + if( eSuperType == wkbCurve ) + return eType == wkbLineString || eType == wkbCircularString || + eType == wkbCompoundCurve; + + if( eSuperType == wkbSurface ) + return eType == wkbCurvePolygon || eType == wkbPolygon; + + return FALSE; +} + +/************************************************************************/ +/* OGR_GT_GetCollection() */ +/************************************************************************/ +/** + * \brief Returns the collection type that can contain the passed geometry type + * + * Handled conversions are : wkbNone->wkbNone, wkbPoint -> wkbMultiPoint, + * wkbLineString->wkbMultiLineString, wkbPolygon->wkbMultiPolygon, + * wkbCircularString->wkbMultiCurve, wkbCompoundCurve->wkbMultiCurve, + * wkbCurvePolygon->wkbMultiSurface. + * In other cases, wkbUnknown is returned + * + * Passed Z flag is preserved. + * + * @param eType Input geometry type + * + * @return the collection type that can contain the passed geometry type or wkbUnknown + * + * @since GDAL 2.0 + */ + +OGRwkbGeometryType OGR_GT_GetCollection( OGRwkbGeometryType eType ) +{ + if( eType == wkbNone ) + return wkbNone; + OGRwkbGeometryType eFGType = wkbFlatten(eType); + if( eFGType == wkbPoint ) + eType = wkbMultiPoint; + + else if( eFGType == wkbLineString ) + eType = wkbMultiLineString; + + else if( eFGType == wkbPolygon ) + eType = wkbMultiPolygon; + + else if( OGR_GT_IsCurve(eFGType) ) + eType = wkbMultiCurve; + + else if( OGR_GT_IsSurface(eFGType) ) + eType = wkbMultiSurface; + + else + return wkbUnknown; + + if( wkbHasZ(eType) ) + eType = wkbSetZ(eType); + + return eType; +} + +/************************************************************************/ +/* OGR_GT_GetCurve() */ +/************************************************************************/ +/** + * \brief Returns the curve geometry type that can contain the passed geometry type + * + * Handled conversions are : wkbPolygon -> wkbCurvePolygon, + * wkbLineString->wkbCompoundCurve, wkbMultiPolygon->wkbMultiSurface + * and wkbMultiLineString->wkbMultiCurve. + * In other cases, the passed geometry is returned. + * + * Passed Z flag is preserved. + * + * @param eType Input geometry type + * + * @return the curve type that can contain the passed geometry type + * + * @since GDAL 2.0 + */ + +OGRwkbGeometryType OGR_GT_GetCurve( OGRwkbGeometryType eType ) +{ + OGRwkbGeometryType eFGType = wkbFlatten(eType); + + if( eFGType == wkbLineString ) + eType = wkbCompoundCurve; + + else if( eFGType == wkbPolygon ) + eType = wkbCurvePolygon; + + else if( eFGType == wkbMultiLineString ) + eType = wkbMultiCurve; + + else if( eFGType == wkbMultiPolygon ) + eType = wkbMultiSurface; + + if( wkbHasZ(eType) ) + eType = wkbSetZ(eType); + + return eType; +} + +/************************************************************************/ +/* OGR_GT_GetLinear() */ +/************************************************************************/ +/** + * \brief Returns the non-curve geometry type that can contain the passed geometry type + * + * Handled conversions are : wkbCurvePolygon -> wkbPolygon, + * wkbCircularString->wkbLineString, wkbCompoundCurve->wkbLineString, + * wkbMultiSurface->wkbMultiPolygon and wkbMultiCurve->wkbMultiLineString. + * In other cases, the passed geometry is returned. + * + * Passed Z flag is preserved. + * + * @param eType Input geometry type + * + * @return the non-curve type that can contain the passed geometry type + * + * @since GDAL 2.0 + */ + +OGRwkbGeometryType OGR_GT_GetLinear( OGRwkbGeometryType eType ) +{ + OGRwkbGeometryType eFGType = wkbFlatten(eType); + + if( OGR_GT_IsCurve(eFGType) ) + eType = wkbLineString; + + else if( OGR_GT_IsSurface(eFGType) ) + eType = wkbPolygon; + + else if( eFGType == wkbMultiCurve ) + eType = wkbMultiLineString; + + else if( eFGType == wkbMultiSurface ) + eType = wkbMultiPolygon; + + if( wkbHasZ(eType) ) + eType = wkbSetZ(eType); + + return eType; +} + +/************************************************************************/ +/* OGR_GT_IsCurve() */ +/************************************************************************/ + +/** + * \brief Return if a geometry type is an instance of Curve + * + * Such geometry type are wkbLineString, wkbCircularString, wkbCompoundCurve + * and their 3D variant. + * + * @param eGeomType the geometry type + * @return TRUE if the geometry type is an instance of Curve + * + * @since GDAL 2.0 + */ + +int OGR_GT_IsCurve( OGRwkbGeometryType eGeomType ) +{ + return OGR_GT_IsSubClassOf( eGeomType, wkbCurve ); +} + +/************************************************************************/ +/* OGR_GT_IsSurface() */ +/************************************************************************/ + +/** + * \brief Return if a geometry type is an instance of Surface + * + * Such geometry type are wkbCurvePolygon and wkbPolygon + * and their 3D variant. + * + * @param eGeomType the geometry type + * @return TRUE if the geometry type is an instance of Surface + * + * @since GDAL 2.0 + */ + +int OGR_GT_IsSurface( OGRwkbGeometryType eGeomType ) +{ + return OGR_GT_IsSubClassOf( eGeomType, wkbSurface ); +} + +/************************************************************************/ +/* OGR_GT_IsNonLinear() */ +/************************************************************************/ + +/** + * \brief Return if a geometry type is a non-linear geometry type. + * + * Such geometry type are wkbCircularString, wkbCompoundCurve, wkbCurvePolygon, + * wkbMultiCurve, wkbMultiSurface and their 3D variant. + * + * @param eGeomType the geometry type + * @return TRUE if the geometry type is a non-linear geometry type. + * + * @since GDAL 2.0 + */ + +int OGR_GT_IsNonLinear( OGRwkbGeometryType eGeomType ) +{ + OGRwkbGeometryType eFGeomType = wkbFlatten(eGeomType); + return eFGeomType == wkbCircularString || eFGeomType == wkbCompoundCurve || + eFGeomType == wkbCurvePolygon || eFGeomType == wkbMultiCurve || + eFGeomType == wkbMultiSurface; +} + +/************************************************************************/ +/* CastToError() */ +/************************************************************************/ + +OGRGeometry* OGRGeometry::CastToError(OGRGeometry* poGeom) +{ + CPLError(CE_Failure, CPLE_AppDefined, + "%s found. Conversion impossible", poGeom->getGeometryName()); + delete poGeom; + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrgeometrycollection.cpp b/bazaar/plugin/gdal/ogr/ogrgeometrycollection.cpp new file mode 100644 index 000000000..d629a5361 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrgeometrycollection.cpp @@ -0,0 +1,1135 @@ +/****************************************************************************** + * $Id: ogrgeometrycollection.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRGeometryCollection class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" +#include "ogr_api.h" + +CPL_CVSID("$Id: ogrgeometrycollection.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +/************************************************************************/ +/* OGRGeometryCollection() */ +/************************************************************************/ + +/** + * \brief Create an empty geometry collection. + */ + +OGRGeometryCollection::OGRGeometryCollection() + +{ + nGeomCount = 0; + papoGeoms = NULL; +} + +/************************************************************************/ +/* ~OGRGeometryCollection() */ +/************************************************************************/ + +OGRGeometryCollection::~OGRGeometryCollection() + +{ + empty(); +} + +/************************************************************************/ +/* empty() */ +/************************************************************************/ + +void OGRGeometryCollection::empty() + +{ + if( papoGeoms != NULL ) + { + for( int i = 0; i < nGeomCount; i++ ) + { + delete papoGeoms[i]; + } + OGRFree( papoGeoms ); + } + + nGeomCount = 0; + papoGeoms = NULL; + nCoordDimension = 2; +} + +/************************************************************************/ +/* clone() */ +/************************************************************************/ + +OGRGeometry *OGRGeometryCollection::clone() const + +{ + OGRGeometryCollection *poNewGC; + + poNewGC = (OGRGeometryCollection*) + OGRGeometryFactory::createGeometry(getGeometryType()); + poNewGC->assignSpatialReference( getSpatialReference() ); + + for( int i = 0; i < nGeomCount; i++ ) + { + poNewGC->addGeometry( papoGeoms[i] ); + } + + return poNewGC; +} + +/************************************************************************/ +/* getGeometryType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRGeometryCollection::getGeometryType() const + +{ + if( getCoordinateDimension() == 3 ) + return wkbGeometryCollection25D; + else + return wkbGeometryCollection; +} + +/************************************************************************/ +/* getDimension() */ +/************************************************************************/ + +int OGRGeometryCollection::getDimension() const + +{ + int nDimension = 0; + /* FIXME? Not sure if it is really appropriate to take the max in case */ + /* of geometries of different dimension... */ + for( int i = 0; i < nGeomCount; i++ ) + { + int nSubGeomDimension = papoGeoms[i]->getDimension(); + if( nSubGeomDimension > nDimension ) + { + nDimension = nSubGeomDimension; + if( nDimension == 2 ) + break; + } + } + return nDimension; +} + +/************************************************************************/ +/* flattenTo2D() */ +/************************************************************************/ + +void OGRGeometryCollection::flattenTo2D() + +{ + for( int i = 0; i < nGeomCount; i++ ) + papoGeoms[i]->flattenTo2D(); + + nCoordDimension = 2; +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRGeometryCollection::getGeometryName() const + +{ + return "GEOMETRYCOLLECTION"; +} + +/************************************************************************/ +/* getNumGeometries() */ +/************************************************************************/ + +/** + * \brief Fetch number of geometries in container. + * + * This method relates to the SFCOM IGeometryCollect::get_NumGeometries() + * method. + * + * @return count of children geometries. May be zero. + */ + +int OGRGeometryCollection::getNumGeometries() const + +{ + return nGeomCount; +} + +/************************************************************************/ +/* getGeometryRef() */ +/************************************************************************/ + +/** + * \brief Fetch geometry from container. + * + * This method returns a pointer to an geometry within the container. The + * returned geometry remains owned by the container, and should not be + * modified. The pointer is only valid untill the next change to the + * geometry container. Use IGeometry::clone() to make a copy. + * + * This method relates to the SFCOM IGeometryCollection::get_Geometry() method. + * + * @param i the index of the geometry to fetch, between 0 and + * getNumGeometries() - 1. + * @return pointer to requested geometry. + */ + +OGRGeometry * OGRGeometryCollection::getGeometryRef( int i ) + +{ + if( i < 0 || i >= nGeomCount ) + return NULL; + else + return papoGeoms[i]; +} + +const OGRGeometry * OGRGeometryCollection::getGeometryRef( int i ) const + +{ + if( i < 0 || i >= nGeomCount ) + return NULL; + else + return papoGeoms[i]; +} + +/************************************************************************/ +/* addGeometry() */ +/* */ +/* Add a new geometry to a collection. Subclasses should */ +/* override this to verify the type of the new geometry, and */ +/* then call this method to actually add it. */ +/************************************************************************/ + +/** + * \brief Add a geometry to the container. + * + * Some subclasses of OGRGeometryCollection restrict the types of geometry + * that can be added, and may return an error. The passed geometry is cloned + * to make an internal copy. + * + * There is no SFCOM analog to this method. + * + * This method is the same as the C function OGR_G_AddGeometry(). + * + * @param poNewGeom geometry to add to the container. + * + * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if + * the geometry type is illegal for the type of geometry container. + */ + +OGRErr OGRGeometryCollection::addGeometry( const OGRGeometry * poNewGeom ) + +{ + OGRGeometry *poClone = poNewGeom->clone(); + OGRErr eErr; + + eErr = addGeometryDirectly( poClone ); + if( eErr != OGRERR_NONE ) + delete poClone; + + return eErr; +} + +/************************************************************************/ +/* addGeometryDirectly() */ +/* */ +/* Add a new geometry to a collection. Subclasses should */ +/* override this to verify the type of the new geometry, and */ +/* then call this method to actually add it. */ +/************************************************************************/ + +/** + * \brief Add a geometry directly to the container. + * + * Some subclasses of OGRGeometryCollection restrict the types of geometry + * that can be added, and may return an error. Ownership of the passed + * geometry is taken by the container rather than cloning as addGeometry() + * does. + * + * This method is the same as the C function OGR_G_AddGeometryDirectly(). + * + * There is no SFCOM analog to this method. + * + * @param poNewGeom geometry to add to the container. + * + * @return OGRERR_NONE if successful, or OGRERR_UNSUPPORTED_GEOMETRY_TYPE if + * the geometry type is illegal for the type of geometry container. + */ + +OGRErr OGRGeometryCollection::addGeometryDirectly( OGRGeometry * poNewGeom ) + +{ + if( !isCompatibleSubType(poNewGeom->getGeometryType()) ) + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + + if( poNewGeom->getCoordinateDimension() == 3 && getCoordinateDimension() != 3 ) + setCoordinateDimension(3); + else if( poNewGeom->getCoordinateDimension() != 3 && getCoordinateDimension() == 3 ) + poNewGeom->setCoordinateDimension(3); + + papoGeoms = (OGRGeometry **) OGRRealloc( papoGeoms, + sizeof(void*) * (nGeomCount+1) ); + + papoGeoms[nGeomCount] = poNewGeom; + + nGeomCount++; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* removeGeometry() */ +/************************************************************************/ + +/** + * \brief Remove a geometry from the container. + * + * Removing a geometry will cause the geometry count to drop by one, and all + * "higher" geometries will shuffle down one in index. + * + * There is no SFCOM analog to this method. + * + * This method is the same as the C function OGR_G_RemoveGeometry(). + * + * @param iGeom the index of the geometry to delete. A value of -1 is a + * special flag meaning that all geometries should be removed. + * + * @param bDelete if TRUE the geometry will be deallocated, otherwise it will + * not. The default is TRUE as the container is considered to own the + * geometries in it. + * + * @return OGRERR_NONE if successful, or OGRERR_FAILURE if the index is + * out of range. + */ + +OGRErr OGRGeometryCollection::removeGeometry( int iGeom, int bDelete ) + +{ + if( iGeom < -1 || iGeom >= nGeomCount ) + return OGRERR_FAILURE; + + // Special case. + if( iGeom == -1 ) + { + while( nGeomCount > 0 ) + removeGeometry( nGeomCount-1, bDelete ); + return OGRERR_NONE; + } + + if( bDelete ) + delete papoGeoms[iGeom]; + + memmove( papoGeoms + iGeom, papoGeoms + iGeom + 1, + sizeof(void*) * (nGeomCount-iGeom-1) ); + + nGeomCount--; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* WkbSize() */ +/* */ +/* Return the size of this object in well known binary */ +/* representation including the byte order, and type information. */ +/************************************************************************/ + +int OGRGeometryCollection::WkbSize() const + +{ + int nSize = 9; + + for( int i = 0; i < nGeomCount; i++ ) + { + nSize += papoGeoms[i]->WkbSize(); + } + + return nSize; +} + +/************************************************************************/ +/* importFromWkbInternal() */ +/************************************************************************/ + +OGRErr OGRGeometryCollection::importFromWkbInternal( unsigned char * pabyData, + int nSize, int nRecLevel, + OGRwkbVariant eWkbVariant ) + +{ + OGRwkbByteOrder eByteOrder; + int nDataOffset; + + /* Arbitrary value, but certainly large enough for reasonable usages ! */ + if( nRecLevel == 32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many recursiong level (%d) while parsing WKB geometry.", + nRecLevel ); + return OGRERR_CORRUPT_DATA; + } + + nGeomCount = 0; + OGRErr eErr = importPreambuleOfCollectionFromWkb( pabyData, + nSize, + nDataOffset, + eByteOrder, + 9, + nGeomCount, + eWkbVariant ); + if( eErr >= 0 ) + return eErr; + + papoGeoms = (OGRGeometry **) VSIMalloc2(sizeof(void*), nGeomCount); + if (nGeomCount != 0 && papoGeoms == NULL) + { + nGeomCount = 0; + return OGRERR_NOT_ENOUGH_MEMORY; + } + +/* -------------------------------------------------------------------- */ +/* Get the Geoms. */ +/* -------------------------------------------------------------------- */ + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + OGRErr eErr; + OGRGeometry* poSubGeom = NULL; + + /* Parses sub-geometry */ + unsigned char* pabySubData = pabyData + nDataOffset; + if( nSize < 9 && nSize != -1 ) + return OGRERR_NOT_ENOUGH_DATA; + + OGRwkbGeometryType eSubGeomType; + OGRBoolean bIs3D; + eErr = OGRReadWKBGeometryType( pabySubData, eWkbVariant, &eSubGeomType, &bIs3D ); + if( eErr != OGRERR_NONE ) + return eErr; + + if( !isCompatibleSubType(eSubGeomType) ) + { + nGeomCount = iGeom; + CPLDebug("OGR", "Cannot add geometry of type (%d) to geometry of type (%d)", + eSubGeomType, getGeometryType()); + return OGRERR_CORRUPT_DATA; + } + + if( OGR_GT_IsSubClassOf(eSubGeomType, wkbGeometryCollection) ) + { + poSubGeom = OGRGeometryFactory::createGeometry( eSubGeomType ); + eErr = ((OGRGeometryCollection*)poSubGeom)-> + importFromWkbInternal( pabySubData, nSize, nRecLevel + 1, eWkbVariant ); + } + else + { + eErr = OGRGeometryFactory:: + createFromWkb( pabySubData, NULL, + &poSubGeom, nSize, eWkbVariant ); + } + + if( eErr != OGRERR_NONE ) + { + nGeomCount = iGeom; + delete poSubGeom; + return eErr; + } + + papoGeoms[iGeom] = poSubGeom; + + if (papoGeoms[iGeom]->getCoordinateDimension() == 3) + nCoordDimension = 3; + + int nSubGeomWkbSize = papoGeoms[iGeom]->WkbSize(); + if( nSize != -1 ) + nSize -= nSubGeomWkbSize; + + nDataOffset += nSubGeomWkbSize; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importFromWkb() */ +/* */ +/* Initialize from serialized stream in well known binary */ +/* format. */ +/************************************************************************/ + +OGRErr OGRGeometryCollection::importFromWkb( unsigned char * pabyData, + int nSize, + OGRwkbVariant eWkbVariant ) + +{ + return importFromWkbInternal(pabyData, nSize, 0, eWkbVariant); +} + +/************************************************************************/ +/* exportToWkb() */ +/* */ +/* Build a well known binary representation of this object. */ +/************************************************************************/ + +OGRErr OGRGeometryCollection::exportToWkb( OGRwkbByteOrder eByteOrder, + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const + +{ + int nOffset; + + if( eWkbVariant == wkbVariantOldOgc && + (wkbFlatten(getGeometryType()) == wkbMultiCurve || + wkbFlatten(getGeometryType()) == wkbMultiSurface) ) /* does not make sense for new geometries, so patch it */ + { + eWkbVariant = wkbVariantIso; + } + +/* -------------------------------------------------------------------- */ +/* Set the byte order. */ +/* -------------------------------------------------------------------- */ + pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER((unsigned char) eByteOrder); + +/* -------------------------------------------------------------------- */ +/* Set the geometry feature type, ensuring that 3D flag is */ +/* preserved. */ +/* -------------------------------------------------------------------- */ + GUInt32 nGType = getGeometryType(); + + if ( eWkbVariant == wkbVariantIso ) + nGType = getIsoGeometryType(); + else if( eWkbVariant == wkbVariantPostGIS1 ) + { + int bIs3D = wkbHasZ((OGRwkbGeometryType)nGType); + nGType = wkbFlatten(nGType); + if( nGType == wkbMultiCurve ) + nGType = POSTGIS15_MULTICURVE; + else if( nGType == wkbMultiSurface ) + nGType = POSTGIS15_MULTISURFACE; + if( bIs3D ) + nGType = (OGRwkbGeometryType)(nGType | wkb25DBitInternalUse); /* yes we explicitly set wkb25DBit */ + } + + if( eByteOrder == wkbNDR ) + nGType = CPL_LSBWORD32( nGType ); + else + nGType = CPL_MSBWORD32( nGType ); + + memcpy( pabyData + 1, &nGType, 4 ); + +/* -------------------------------------------------------------------- */ +/* Copy in the raw data. */ +/* -------------------------------------------------------------------- */ + if( OGR_SWAP( eByteOrder ) ) + { + int nCount; + + nCount = CPL_SWAP32( nGeomCount ); + memcpy( pabyData+5, &nCount, 4 ); + } + else + { + memcpy( pabyData+5, &nGeomCount, 4 ); + } + + nOffset = 9; + +/* ==================================================================== */ +/* Serialize each of the Geoms. */ +/* ==================================================================== */ + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + papoGeoms[iGeom]->exportToWkb( eByteOrder, pabyData + nOffset, eWkbVariant ); + + nOffset += papoGeoms[iGeom]->WkbSize(); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importFromWktInternal() */ +/************************************************************************/ + +OGRErr OGRGeometryCollection::importFromWktInternal( char ** ppszInput, int nRecLevel ) + +{ + /* Arbitrary value, but certainly large enough for reasonable usages ! */ + if( nRecLevel == 32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many recursiong level (%d) while parsing WKT geometry.", + nRecLevel ); + return OGRERR_CORRUPT_DATA; + } + + int bHasZ = FALSE, bHasM = FALSE; + OGRErr eErr = importPreambuleFromWkt(ppszInput, &bHasZ, &bHasM); + if( eErr >= 0 ) + return eErr; + + char szToken[OGR_WKT_TOKEN_MAX]; + const char *pszInput = *ppszInput; + + /* Skip first '(' */ + pszInput = OGRWktReadToken( pszInput, szToken ); + +/* ==================================================================== */ +/* Read each subgeometry in turn. */ +/* ==================================================================== */ + do + { + OGRGeometry *poGeom = NULL; + OGRErr eErr; + + /* -------------------------------------------------------------------- */ + /* Get the first token, which should be the geometry type. */ + /* -------------------------------------------------------------------- */ + OGRWktReadToken( pszInput, szToken ); + + /* -------------------------------------------------------------------- */ + /* Do the import. */ + /* -------------------------------------------------------------------- */ + if (EQUAL(szToken,"GEOMETRYCOLLECTION")) + { + poGeom = new OGRGeometryCollection(); + eErr = ((OGRGeometryCollection*)poGeom)-> + importFromWktInternal( (char **) &pszInput, nRecLevel + 1 ); + } + else + eErr = OGRGeometryFactory::createFromWkt( (char **) &pszInput, + NULL, &poGeom ); + + if( eErr == OGRERR_NONE ) + eErr = addGeometryDirectly( poGeom ); + if( eErr != OGRERR_NONE ) + { + delete poGeom; + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Read the delimeter following the ring. */ +/* -------------------------------------------------------------------- */ + + pszInput = OGRWktReadToken( pszInput, szToken ); + + } while( szToken[0] == ',' ); + +/* -------------------------------------------------------------------- */ +/* freak if we don't get a closing bracket. */ +/* -------------------------------------------------------------------- */ + if( szToken[0] != ')' ) + return OGRERR_CORRUPT_DATA; + + *ppszInput = (char *) pszInput; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importFromWkt() */ +/************************************************************************/ + +OGRErr OGRGeometryCollection::importFromWkt( char ** ppszInput ) + +{ + return importFromWktInternal(ppszInput, 0); +} + +/************************************************************************/ +/* exportToWkt() */ +/* */ +/* Translate this structure into it's well known text format */ +/* equivelent. This could be made alot more CPU efficient! */ +/************************************************************************/ + +OGRErr OGRGeometryCollection::exportToWkt( char ** ppszDstText, + OGRwkbVariant eWkbVariant ) const +{ + return exportToWktInternal(ppszDstText, eWkbVariant, NULL); +} + +OGRErr OGRGeometryCollection::exportToWktInternal( char ** ppszDstText, + OGRwkbVariant eWkbVariant, + const char* pszSkipPrefix ) const + +{ + char **papszGeoms; + int iGeom, nCumulativeLength = 0; + OGRErr eErr; + int bMustWriteComma = FALSE; + +/* -------------------------------------------------------------------- */ +/* Build a list of strings containing the stuff for each Geom. */ +/* -------------------------------------------------------------------- */ + papszGeoms = (nGeomCount) ? (char **) CPLCalloc(sizeof(char *),nGeomCount) : NULL; + + for( iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + eErr = papoGeoms[iGeom]->exportToWkt( &(papszGeoms[iGeom]), eWkbVariant ); + if( eErr != OGRERR_NONE ) + goto error; + + int nSkip = 0; + if( pszSkipPrefix != NULL && + EQUALN(papszGeoms[iGeom], pszSkipPrefix, strlen(pszSkipPrefix)) && + papszGeoms[iGeom][strlen(pszSkipPrefix)] == ' ' ) + { + nSkip = strlen(pszSkipPrefix) + 1; + if( EQUALN(papszGeoms[iGeom] + nSkip, "Z ", 2) ) + nSkip += 2; + + /* skip empty subgeoms */ + if( papszGeoms[iGeom][nSkip] != '(' ) + { + CPLDebug( "OGR", "OGRGeometryCollection::exportToWkt() - skipping %s.", + papszGeoms[iGeom] ); + CPLFree( papszGeoms[iGeom] ); + papszGeoms[iGeom] = NULL; + continue; + } + } + + nCumulativeLength += strlen(papszGeoms[iGeom] + nSkip); + } + +/* -------------------------------------------------------------------- */ +/* Return XXXXXXXXXXXXXXX EMPTY if we get no valid line string. */ +/* -------------------------------------------------------------------- */ + if( nCumulativeLength == 0 ) + { + CPLFree( papszGeoms ); + CPLString osEmpty; + if( getCoordinateDimension() == 3 && eWkbVariant == wkbVariantIso ) + osEmpty.Printf("%s Z EMPTY",getGeometryName()); + else + osEmpty.Printf("%s EMPTY",getGeometryName()); + *ppszDstText = CPLStrdup(osEmpty); + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Allocate the right amount of space for the aggregated string */ +/* -------------------------------------------------------------------- */ + *ppszDstText = (char *) VSIMalloc(nCumulativeLength + nGeomCount + 25); + + if( *ppszDstText == NULL ) + { + eErr = OGRERR_NOT_ENOUGH_MEMORY; + goto error; + } + +/* -------------------------------------------------------------------- */ +/* Build up the string, freeing temporary strings as we go. */ +/* -------------------------------------------------------------------- */ + strcpy( *ppszDstText, getGeometryName() ); + if( getCoordinateDimension() == 3 && eWkbVariant == wkbVariantIso ) + strcat( *ppszDstText, " Z" ); + strcat( *ppszDstText, " (" ); + nCumulativeLength = strlen(*ppszDstText); + + for( iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + if( papszGeoms[iGeom] == NULL ) + continue; + + if( bMustWriteComma ) + (*ppszDstText)[nCumulativeLength++] = ','; + bMustWriteComma = TRUE; + + int nSkip = 0; + if( pszSkipPrefix != NULL && + EQUALN(papszGeoms[iGeom], pszSkipPrefix, strlen(pszSkipPrefix)) && + papszGeoms[iGeom][strlen(pszSkipPrefix)] == ' ' ) + { + nSkip = strlen(pszSkipPrefix) + 1; + if( EQUALN(papszGeoms[iGeom] + nSkip, "Z ", 2) ) + nSkip += 2; + } + + int nGeomLength = strlen(papszGeoms[iGeom] + nSkip); + memcpy( *ppszDstText + nCumulativeLength, papszGeoms[iGeom] + nSkip, nGeomLength ); + nCumulativeLength += nGeomLength; + VSIFree( papszGeoms[iGeom] ); + } + + (*ppszDstText)[nCumulativeLength++] = ')'; + (*ppszDstText)[nCumulativeLength] = '\0'; + + CPLFree( papszGeoms ); + + return OGRERR_NONE; + +error: + for( iGeom = 0; iGeom < nGeomCount; iGeom++ ) + CPLFree( papszGeoms[iGeom] ); + CPLFree( papszGeoms ); + return eErr; +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRGeometryCollection::getEnvelope( OGREnvelope * psEnvelope ) const + +{ + OGREnvelope3D oEnv3D; + getEnvelope(&oEnv3D); + psEnvelope->MinX = oEnv3D.MinX; + psEnvelope->MinY = oEnv3D.MinY; + psEnvelope->MaxX = oEnv3D.MaxX; + psEnvelope->MaxY = oEnv3D.MaxY; +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRGeometryCollection::getEnvelope( OGREnvelope3D * psEnvelope ) const + +{ + OGREnvelope3D oGeomEnv; + int bExtentSet = FALSE; + + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + if (!papoGeoms[iGeom]->IsEmpty()) + { + if (!bExtentSet) + { + papoGeoms[iGeom]->getEnvelope( psEnvelope ); + bExtentSet = TRUE; + } + else + { + papoGeoms[iGeom]->getEnvelope( &oGeomEnv ); + psEnvelope->Merge( oGeomEnv ); + } + } + } + + if (!bExtentSet) + { + psEnvelope->MinX = psEnvelope->MinY = psEnvelope->MinZ = 0; + psEnvelope->MaxX = psEnvelope->MaxY = psEnvelope->MaxZ = 0; + } +} + +/************************************************************************/ +/* Equals() */ +/************************************************************************/ + +OGRBoolean OGRGeometryCollection::Equals( OGRGeometry * poOther ) const + +{ + OGRGeometryCollection *poOGC = (OGRGeometryCollection *) poOther; + + if( poOGC == this ) + return TRUE; + + if( poOther->getGeometryType() != getGeometryType() ) + return FALSE; + + if ( IsEmpty() && poOther->IsEmpty() ) + return TRUE; + + if( getNumGeometries() != poOGC->getNumGeometries() ) + return FALSE; + + // we should eventually test the SRS. + + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + if( !getGeometryRef(iGeom)->Equals(poOGC->getGeometryRef(iGeom)) ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* transform() */ +/************************************************************************/ + +OGRErr OGRGeometryCollection::transform( OGRCoordinateTransformation *poCT ) + +{ +#ifdef DISABLE_OGRGEOM_TRANSFORM + return OGRERR_FAILURE; +#else + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + OGRErr eErr; + + eErr = papoGeoms[iGeom]->transform( poCT ); + if( eErr != OGRERR_NONE ) + { + if( iGeom != 0 ) + { + CPLDebug("OGR", + "OGRGeometryCollection::transform() failed for a geometry other\n" + "than the first, meaning some geometries are transformed\n" + "and some are not!\n" ); + + return OGRERR_FAILURE; + } + + return eErr; + } + } + + assignSpatialReference( poCT->GetTargetCS() ); + + return OGRERR_NONE; +#endif +} + +/************************************************************************/ +/* closeRings() */ +/************************************************************************/ + +void OGRGeometryCollection::closeRings() + +{ + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + if( wkbFlatten(papoGeoms[iGeom]->getGeometryType()) == wkbPolygon ) + ((OGRPolygon *) papoGeoms[iGeom])->closeRings(); + } +} + +/************************************************************************/ +/* setCoordinateDimension() */ +/************************************************************************/ + +void OGRGeometryCollection::setCoordinateDimension( int nNewDimension ) + +{ + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + papoGeoms[iGeom]->setCoordinateDimension( nNewDimension ); + } + + OGRGeometry::setCoordinateDimension( nNewDimension ); +} + + +/************************************************************************/ +/* get_Length() */ +/************************************************************************/ + +/** + * \brief Compute the length of a multicurve. + * + * The length is computed as the sum of the length of all members + * in this collection. + * + * @note No warning will be issued if a member of the collection does not + * support the get_Length method. + * + * @return computed area. + */ + +double OGRGeometryCollection::get_Length() const +{ + double dfLength = 0.0; + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + OGRGeometry* geom = papoGeoms[iGeom]; + OGRwkbGeometryType eType = wkbFlatten(geom->getGeometryType()); + if( OGR_GT_IsCurve(eType) ) + { + dfLength += ((OGRCurve *) geom)->get_Length(); + } + else if( OGR_GT_IsSubClassOf(eType, wkbMultiCurve) || + eType == wkbGeometryCollection ) + { + dfLength += ((OGRGeometryCollection *) geom)->get_Length(); + } + } + + return dfLength; +} + +/************************************************************************/ +/* get_Area() */ +/************************************************************************/ + +/** + * \brief Compute area of geometry collection. + * + * The area is computed as the sum of the areas of all members + * in this collection. + * + * @note No warning will be issued if a member of the collection does not + * support the get_Area method. + * + * @return computed area. + */ + +double OGRGeometryCollection::get_Area() const +{ + double dfArea = 0.0; + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + OGRGeometry* geom = papoGeoms[iGeom]; + OGRwkbGeometryType eType = wkbFlatten(geom->getGeometryType()); + if( OGR_GT_IsSurface(eType) ) + { + dfArea += ((OGRSurface *) geom)->get_Area(); + } + else if( OGR_GT_IsCurve(eType) ) + { + dfArea += ((OGRCurve *) geom)->get_Area(); + } + else if( OGR_GT_IsSubClassOf(eType, wkbMultiSurface) || + eType == wkbGeometryCollection ) + { + dfArea += ((OGRGeometryCollection *) geom)->get_Area(); + } + } + + return dfArea; +} + +/************************************************************************/ +/* IsEmpty() */ +/************************************************************************/ + +OGRBoolean OGRGeometryCollection::IsEmpty( ) const +{ + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + if (papoGeoms[iGeom]->IsEmpty() == FALSE) + return FALSE; + return TRUE; +} + +/************************************************************************/ +/* OGRGeometryCollection::segmentize() */ +/************************************************************************/ + +void OGRGeometryCollection::segmentize( double dfMaxLength ) +{ + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + papoGeoms[iGeom]->segmentize(dfMaxLength); +} + +/************************************************************************/ +/* swapXY() */ +/************************************************************************/ + +void OGRGeometryCollection::swapXY() +{ + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + papoGeoms[iGeom]->swapXY(); +} + +/************************************************************************/ +/* isCompatibleSubType() */ +/************************************************************************/ + +OGRBoolean OGRGeometryCollection::isCompatibleSubType( OGRwkbGeometryType ) const +{ + /* We accept all geometries as sub-geometries */ + return TRUE; +} + +/************************************************************************/ +/* hasCurveGeometry() */ +/************************************************************************/ + +OGRBoolean OGRGeometryCollection::hasCurveGeometry(int bLookForNonLinear) const +{ + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + if( papoGeoms[iGeom]->hasCurveGeometry(bLookForNonLinear) ) + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* getLinearGeometry() */ +/************************************************************************/ + +OGRGeometry* OGRGeometryCollection::getLinearGeometry(double dfMaxAngleStepSizeDegrees, + const char* const* papszOptions) const +{ + OGRGeometryCollection* poGC = (OGRGeometryCollection*) + OGRGeometryFactory::createGeometry(OGR_GT_GetLinear(getGeometryType())); + poGC->assignSpatialReference( getSpatialReference() ); + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + OGRGeometry* poSubGeom = papoGeoms[iGeom]->getLinearGeometry(dfMaxAngleStepSizeDegrees, + papszOptions); + poGC->addGeometryDirectly( poSubGeom ); + } + return poGC; +} + +/************************************************************************/ +/* getCurveGeometry() */ +/************************************************************************/ + +OGRGeometry* OGRGeometryCollection::getCurveGeometry(const char* const* papszOptions) const +{ + OGRGeometryCollection* poGC = (OGRGeometryCollection*) + OGRGeometryFactory::createGeometry(OGR_GT_GetCurve(getGeometryType())); + poGC->assignSpatialReference( getSpatialReference() ); + int bHasCurveGeometry = FALSE; + for( int iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + OGRGeometry* poSubGeom = papoGeoms[iGeom]->getCurveGeometry(papszOptions); + if( poSubGeom->hasCurveGeometry() ) + bHasCurveGeometry = TRUE; + poGC->addGeometryDirectly( poSubGeom ); + } + if( !bHasCurveGeometry ) + { + delete poGC; + return clone(); + } + return poGC; +} + +/************************************************************************/ +/* TransferMembersAndDestroy() */ +/************************************************************************/ + +OGRGeometryCollection* OGRGeometryCollection::TransferMembersAndDestroy( + OGRGeometryCollection* poSrc, + OGRGeometryCollection* poDst) +{ + poDst->assignSpatialReference(poSrc->getSpatialReference()); + poDst->setCoordinateDimension(poSrc->getCoordinateDimension()); + poDst->nGeomCount = poSrc->nGeomCount; + poDst->papoGeoms = poSrc->papoGeoms; + poSrc->nGeomCount = 0; + poSrc->papoGeoms = NULL; + delete poSrc; + return poDst; +} diff --git a/bazaar/plugin/gdal/ogr/ogrgeometryfactory.cpp b/bazaar/plugin/gdal/ogr/ogrgeometryfactory.cpp new file mode 100644 index 000000000..0cb3691d8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrgeometryfactory.cpp @@ -0,0 +1,4281 @@ +/****************************************************************************** + * $Id: ogrgeometryfactory.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Factory for converting geometry to and from well known binary + * format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_api.h" +#include "ogr_p.h" +#include "ogr_geos.h" + +CPL_CVSID("$Id: ogrgeometryfactory.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +/************************************************************************/ +/* createFromWkb() */ +/************************************************************************/ + +/** + * \brief Create a geometry object of the appropriate type from it's well known binary representation. + * + * Note that if nBytes is passed as zero, no checking can be done on whether + * the pabyData is sufficient. This can result in a crash if the input + * data is corrupt. This function returns no indication of the number of + * bytes from the data source actually used to represent the returned + * geometry object. Use OGRGeometry::WkbSize() on the returned geometry to + * establish the number of bytes it required in WKB format. + * + * Also note that this is a static method, and that there + * is no need to instantiate an OGRGeometryFactory object. + * + * The C function OGR_G_CreateFromWkb() is the same as this method. + * + * @param pabyData pointer to the input BLOB data. + * @param poSR pointer to the spatial reference to be assigned to the + * created geometry object. This may be NULL. + * @param ppoReturn the newly created geometry object will be assigned to the + * indicated pointer on return. This will be NULL in case + * of failure. If not NULL, *ppoReturn should be freed with + * OGRGeometryFactory::destroyGeometry() after use. + * @param nBytes the number of bytes available in pabyData, or -1 if it isn't + * known. + * + * @return OGRERR_NONE if all goes well, otherwise any of + * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or + * OGRERR_CORRUPT_DATA may be returned. + */ + +OGRErr OGRGeometryFactory::createFromWkb(unsigned char *pabyData, + OGRSpatialReference * poSR, + OGRGeometry **ppoReturn, + int nBytes, + OGRwkbVariant eWkbVariant ) + +{ + OGRwkbGeometryType eGeometryType; + OGRwkbByteOrder eByteOrder; + OGRErr eErr; + OGRGeometry *poGeom; + + *ppoReturn = NULL; + + if( nBytes < 9 && nBytes != -1 ) + return OGRERR_NOT_ENOUGH_DATA; + +/* -------------------------------------------------------------------- */ +/* Get the byte order byte. The extra tests are to work around */ +/* bug sin the WKB of DB2 v7.2 as identified by Safe Software. */ +/* -------------------------------------------------------------------- */ + eByteOrder = DB2_V72_FIX_BYTE_ORDER((OGRwkbByteOrder) *pabyData); + + + if( eByteOrder != wkbXDR && eByteOrder != wkbNDR ) + { + CPLDebug( "OGR", + "OGRGeometryFactory::createFromWkb() - got corrupt data.\n" + "%02X%02X%02X%02X%02X%02X%02X%02X%02X\n", + pabyData[0], + pabyData[1], + pabyData[2], + pabyData[3], + pabyData[4], + pabyData[5], + pabyData[6], + pabyData[7], + pabyData[8]); + return OGRERR_CORRUPT_DATA; + } + +/* -------------------------------------------------------------------- */ +/* Get the geometry feature type. For now we assume that */ +/* geometry type is between 0 and 255 so we only have to fetch */ +/* one byte. */ +/* -------------------------------------------------------------------- */ + + OGRBoolean bIs3D; + OGRErr err = OGRReadWKBGeometryType( pabyData, eWkbVariant, &eGeometryType, &bIs3D ); + + if( err != OGRERR_NONE ) + return err; + + +/* -------------------------------------------------------------------- */ +/* Instantiate a geometry of the appropriate type, and */ +/* initialize from the input stream. */ +/* -------------------------------------------------------------------- */ + poGeom = createGeometry( eGeometryType ); + + if( poGeom == NULL ) + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + +/* -------------------------------------------------------------------- */ +/* Import from binary. */ +/* -------------------------------------------------------------------- */ + eErr = poGeom->importFromWkb( pabyData, nBytes, eWkbVariant ); + +/* -------------------------------------------------------------------- */ +/* Assign spatial reference system. */ +/* -------------------------------------------------------------------- */ + if( eErr == OGRERR_NONE ) + { + if ( poGeom->hasCurveGeometry() && + CSLTestBoolean(CPLGetConfigOption("OGR_STROKE_CURVE", "FALSE")) ) + { + OGRGeometry* poNewGeom = poGeom->getLinearGeometry(); + delete poGeom; + poGeom = poNewGeom; + } + poGeom->assignSpatialReference( poSR ); + *ppoReturn = poGeom; + } + else + { + delete poGeom; + } + + return eErr; +} + +/************************************************************************/ +/* OGR_G_CreateFromWkb() */ +/************************************************************************/ +/** + * \brief Create a geometry object of the appropriate type from it's well known binary representation. + * + * Note that if nBytes is passed as zero, no checking can be done on whether + * the pabyData is sufficient. This can result in a crash if the input + * data is corrupt. This function returns no indication of the number of + * bytes from the data source actually used to represent the returned + * geometry object. Use OGR_G_WkbSize() on the returned geometry to + * establish the number of bytes it required in WKB format. + * + * The OGRGeometryFactory::createFromWkb() CPP method is the same as this + * function. + * + * @param pabyData pointer to the input BLOB data. + * @param hSRS handle to the spatial reference to be assigned to the + * created geometry object. This may be NULL. + * @param phGeometry the newly created geometry object will + * be assigned to the indicated handle on return. This will be NULL in case + * of failure. If not NULL, *phGeometry should be freed with + * OGR_G_DestroyGeometry() after use. + * @param nBytes the number of bytes of data available in pabyData, or -1 + * if it is not known, but assumed to be sufficient. + * + * @return OGRERR_NONE if all goes well, otherwise any of + * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or + * OGRERR_CORRUPT_DATA may be returned. + */ + +OGRErr CPL_DLL OGR_G_CreateFromWkb( unsigned char *pabyData, + OGRSpatialReferenceH hSRS, + OGRGeometryH *phGeometry, + int nBytes ) + +{ + return OGRGeometryFactory::createFromWkb( pabyData, + (OGRSpatialReference *) hSRS, + (OGRGeometry **) phGeometry, + nBytes ); +} + +/************************************************************************/ +/* createFromWkt() */ +/************************************************************************/ + +/** + * \brief Create a geometry object of the appropriate type from it's well known text representation. + * + * The C function OGR_G_CreateFromWkt() is the same as this method. + * + * @param ppszData input zero terminated string containing well known text + * representation of the geometry to be created. The pointer + * is updated to point just beyond that last character consumed. + * @param poSR pointer to the spatial reference to be assigned to the + * created geometry object. This may be NULL. + * @param ppoReturn the newly created geometry object will be assigned to the + * indicated pointer on return. This will be NULL if the + * method fails. If not NULL, *ppoReturn should be freed with + * OGRGeometryFactory::destroyGeometry() after use. + * + * Example: + * + *
    + *    const char* wkt= "POINT(0 0)";
    + *  
    + *    // cast because OGR_G_CreateFromWkt will move the pointer 
    + *    char* pszWkt = (char*) wkt;
    + *    OGRSpatialReferenceH ref = OSRNewSpatialReference(NULL);
    + *    OGRGeometryH new_geom;
    + *    OGRErr err = OGR_G_CreateFromWkt(&pszWkt, ref, &new_geom);
    + *  
    + * + * + * + * @return OGRERR_NONE if all goes well, otherwise any of + * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or + * OGRERR_CORRUPT_DATA may be returned. + */ + +OGRErr OGRGeometryFactory::createFromWkt(char **ppszData, + OGRSpatialReference * poSR, + OGRGeometry **ppoReturn ) + +{ + OGRErr eErr; + char szToken[OGR_WKT_TOKEN_MAX]; + char *pszInput = *ppszData; + OGRGeometry *poGeom; + + *ppoReturn = NULL; + +/* -------------------------------------------------------------------- */ +/* Get the first token, which should be the geometry type. */ +/* -------------------------------------------------------------------- */ + if( OGRWktReadToken( pszInput, szToken ) == NULL ) + return OGRERR_CORRUPT_DATA; + +/* -------------------------------------------------------------------- */ +/* Instantiate a geometry of the appropriate type. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(szToken,"POINT") ) + { + poGeom = new OGRPoint(); + } + + else if( EQUAL(szToken,"LINESTRING") ) + { + poGeom = new OGRLineString(); + } + + else if( EQUAL(szToken,"POLYGON") ) + { + poGeom = new OGRPolygon(); + } + + else if( EQUAL(szToken,"GEOMETRYCOLLECTION") ) + { + poGeom = new OGRGeometryCollection(); + } + + else if( EQUAL(szToken,"MULTIPOLYGON") ) + { + poGeom = new OGRMultiPolygon(); + } + + else if( EQUAL(szToken,"MULTIPOINT") ) + { + poGeom = new OGRMultiPoint(); + } + + else if( EQUAL(szToken,"MULTILINESTRING") ) + { + poGeom = new OGRMultiLineString(); + } + + else if( EQUAL(szToken,"CIRCULARSTRING") ) + { + poGeom = new OGRCircularString(); + } + + else if( EQUAL(szToken,"COMPOUNDCURVE") ) + { + poGeom = new OGRCompoundCurve(); + } + + else if( EQUAL(szToken,"CURVEPOLYGON") ) + { + poGeom = new OGRCurvePolygon(); + } + + else if( EQUAL(szToken,"MULTICURVE") ) + { + poGeom = new OGRMultiCurve(); + } + + else if( EQUAL(szToken,"MULTISURFACE") ) + { + poGeom = new OGRMultiSurface(); + } + + else + { + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + +/* -------------------------------------------------------------------- */ +/* Do the import. */ +/* -------------------------------------------------------------------- */ + eErr = poGeom->importFromWkt( &pszInput ); + +/* -------------------------------------------------------------------- */ +/* Assign spatial reference system. */ +/* -------------------------------------------------------------------- */ + if( eErr == OGRERR_NONE ) + { + if ( poGeom->hasCurveGeometry() && + CSLTestBoolean(CPLGetConfigOption("OGR_STROKE_CURVE", "FALSE")) ) + { + OGRGeometry* poNewGeom = poGeom->getLinearGeometry(); + delete poGeom; + poGeom = poNewGeom; + } + poGeom->assignSpatialReference( poSR ); + *ppoReturn = poGeom; + *ppszData = pszInput; + } + else + { + delete poGeom; + } + + return eErr; +} + +/************************************************************************/ +/* OGR_G_CreateFromWkt() */ +/************************************************************************/ +/** + * \brief Create a geometry object of the appropriate type from it's well known text representation. + * + * The OGRGeometryFactory::createFromWkt CPP method is the same as this + * function. + * + * @param ppszData input zero terminated string containing well known text + * representation of the geometry to be created. The pointer + * is updated to point just beyond that last character consumed. + * @param hSRS handle to the spatial reference to be assigned to the + * created geometry object. This may be NULL. + * @param phGeometry the newly created geometry object will be assigned to the + * indicated handle on return. This will be NULL if the + * method fails. If not NULL, *phGeometry should be freed with + * OGR_G_DestroyGeometry() after use. + * + * @return OGRERR_NONE if all goes well, otherwise any of + * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or + * OGRERR_CORRUPT_DATA may be returned. + */ + +OGRErr CPL_DLL OGR_G_CreateFromWkt( char **ppszData, + OGRSpatialReferenceH hSRS, + OGRGeometryH *phGeometry ) + +{ + return OGRGeometryFactory::createFromWkt( ppszData, + (OGRSpatialReference *) hSRS, + (OGRGeometry **) phGeometry ); +} + +/************************************************************************/ +/* createGeometry() */ +/************************************************************************/ + +/** + * \brief Create an empty geometry of desired type. + * + * This is equivalent to allocating the desired geometry with new, but + * the allocation is guaranteed to take place in the context of the + * GDAL/OGR heap. + * + * This method is the same as the C function OGR_G_CreateGeometry(). + * + * @param eGeometryType the type code of the geometry class to be instantiated. + * + * @return the newly create geometry or NULL on failure. Should be freed with + * OGRGeometryFactory::destroyGeometry() after use. + */ + +OGRGeometry * +OGRGeometryFactory::createGeometry( OGRwkbGeometryType eGeometryType ) + +{ + switch( wkbFlatten(eGeometryType) ) + { + case wkbPoint: + return new OGRPoint(); + + case wkbLineString: + return new OGRLineString(); + + case wkbPolygon: + return new OGRPolygon(); + + case wkbGeometryCollection: + return new OGRGeometryCollection(); + + case wkbMultiPolygon: + return new OGRMultiPolygon(); + + case wkbMultiPoint: + return new OGRMultiPoint(); + + case wkbMultiLineString: + return new OGRMultiLineString(); + + case wkbLinearRing: + return new OGRLinearRing(); + + case wkbCircularString: + return new OGRCircularString(); + + case wkbCompoundCurve: + return new OGRCompoundCurve(); + + case wkbCurvePolygon: + return new OGRCurvePolygon(); + + case wkbMultiCurve: + return new OGRMultiCurve(); + + case wkbMultiSurface: + return new OGRMultiSurface(); + + default: + return NULL; + } +} + +/************************************************************************/ +/* OGR_G_CreateGeometry() */ +/************************************************************************/ +/** + * \brief Create an empty geometry of desired type. + * + * This is equivalent to allocating the desired geometry with new, but + * the allocation is guaranteed to take place in the context of the + * GDAL/OGR heap. + * + * This function is the same as the CPP method + * OGRGeometryFactory::createGeometry. + * + * @param eGeometryType the type code of the geometry to be created. + * + * @return handle to the newly create geometry or NULL on failure. Should be freed with + * OGR_G_DestroyGeometry() after use. + */ + +OGRGeometryH OGR_G_CreateGeometry( OGRwkbGeometryType eGeometryType ) + +{ + return (OGRGeometryH) OGRGeometryFactory::createGeometry( eGeometryType ); +} + + +/************************************************************************/ +/* destroyGeometry() */ +/************************************************************************/ + +/** + * \brief Destroy geometry object. + * + * Equivalent to invoking delete on a geometry, but it guaranteed to take + * place within the context of the GDAL/OGR heap. + * + * This method is the same as the C function OGR_G_DestroyGeometry(). + * + * @param poGeom the geometry to deallocate. + */ + +void OGRGeometryFactory::destroyGeometry( OGRGeometry *poGeom ) + +{ + delete poGeom; +} + + +/************************************************************************/ +/* OGR_G_DestroyGeometry() */ +/************************************************************************/ +/** + * \brief Destroy geometry object. + * + * Equivalent to invoking delete on a geometry, but it guaranteed to take + * place within the context of the GDAL/OGR heap. + * + * This function is the same as the CPP method + * OGRGeometryFactory::destroyGeometry. + * + * @param hGeom handle to the geometry to delete. + */ + +void OGR_G_DestroyGeometry( OGRGeometryH hGeom ) + +{ + OGRGeometryFactory::destroyGeometry( (OGRGeometry *) hGeom ); +} + +/************************************************************************/ +/* forceToPolygon() */ +/************************************************************************/ + +/** + * \brief Convert to polygon. + * + * Tries to force the provided geometry to be a polygon. This effects a change + * on multipolygons. + * Starting with GDAL 2.0, curve polygons or closed curves will be changed to polygons. + * The passed in geometry is consumed and a new one returned (or potentially the same one). + * + * @param poGeom the input geometry - ownership is passed to the method. + * @return new geometry. + */ + +OGRGeometry *OGRGeometryFactory::forceToPolygon( OGRGeometry *poGeom ) + +{ + if( poGeom == NULL ) + return NULL; + + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + + if( eGeomType == wkbCurvePolygon ) + { + if( !poGeom->hasCurveGeometry(TRUE) ) + return OGRSurface::CastToPolygon(((OGRCurvePolygon*)poGeom)); + + OGRPolygon* poPoly = ((OGRCurvePolygon*)poGeom)->CurvePolyToPoly(); + delete poGeom; + return poPoly; + } + + if( OGR_GT_IsCurve(eGeomType) && + ((OGRCurve*)poGeom)->getNumPoints() >= 3 && + ((OGRCurve*)poGeom)->get_IsClosed() ) + { + OGRPolygon *poPolygon = new OGRPolygon(); + poPolygon->assignSpatialReference(poGeom->getSpatialReference()); + + if( !poGeom->hasCurveGeometry(TRUE) ) + { + poPolygon->addRingDirectly( OGRCurve::CastToLinearRing((OGRCurve*)poGeom) ); + } + else + { + OGRLineString* poLS = ((OGRCurve*)poGeom)->CurveToLine(); + poPolygon->addRingDirectly( OGRCurve::CastToLinearRing(poLS) ); + delete poGeom; + } + return poPolygon; + } + + if( eGeomType != wkbGeometryCollection + && eGeomType != wkbMultiPolygon + && eGeomType != wkbMultiSurface ) + return poGeom; + + // build an aggregated polygon from all the polygon rings in the container. + OGRPolygon *poPolygon = new OGRPolygon(); + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom; + if( poGeom->hasCurveGeometry() ) + { + OGRGeometryCollection *poNewGC = (OGRGeometryCollection *) poGC->getLinearGeometry(); + delete poGC; + poGeom = poGC = poNewGC; + } + + poPolygon->assignSpatialReference(poGeom->getSpatialReference()); + int iGeom; + + for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ ) + { + if( wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType()) + != wkbPolygon ) + continue; + + OGRPolygon *poOldPoly = (OGRPolygon *) poGC->getGeometryRef(iGeom); + int iRing; + + if( poOldPoly->getExteriorRing() == NULL ) + continue; + + poPolygon->addRingDirectly( poOldPoly->stealExteriorRing() ); + + for( iRing = 0; iRing < poOldPoly->getNumInteriorRings(); iRing++ ) + poPolygon->addRingDirectly( poOldPoly->stealInteriorRing( iRing ) ); + } + + delete poGC; + + return poPolygon; +} + +/************************************************************************/ +/* OGR_G_ForceToPolygon() */ +/************************************************************************/ + +/** + * \brief Convert to polygon. + * + * This function is the same as the C++ method + * OGRGeometryFactory::forceToPolygon(). + * + * @param hGeom handle to the geometry to convert (ownership surrendered). + * @return the converted geometry (ownership to caller). + * + * @since GDAL/OGR 1.8.0 + */ + +OGRGeometryH OGR_G_ForceToPolygon( OGRGeometryH hGeom ) + +{ + return (OGRGeometryH) + OGRGeometryFactory::forceToPolygon( (OGRGeometry *) hGeom ); +} + +/************************************************************************/ +/* forceToMultiPolygon() */ +/************************************************************************/ + +/** + * \brief Convert to multipolygon. + * + * Tries to force the provided geometry to be a multipolygon. Currently + * this just effects a change on polygons. The passed in geometry is + * consumed and a new one returned (or potentially the same one). + * + * @return new geometry. + */ + +OGRGeometry *OGRGeometryFactory::forceToMultiPolygon( OGRGeometry *poGeom ) + +{ + if( poGeom == NULL ) + return NULL; + + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + +/* -------------------------------------------------------------------- */ +/* If this is already a MultiPolygon, nothing to do */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbMultiPolygon ) + { + return poGeom; + } + +/* -------------------------------------------------------------------- */ +/* If this is already a MultiSurface with compatible content, */ +/* just cast */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbMultiSurface && + !((OGRMultiSurface*)poGeom)->hasCurveGeometry(TRUE) ) + { + return OGRMultiSurface::CastToMultiPolygon((OGRMultiSurface*)poGeom); + } + +/* -------------------------------------------------------------------- */ +/* Check for the case of a geometrycollection that can be */ +/* promoted to MultiPolygon. */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbGeometryCollection || + eGeomType == wkbMultiSurface ) + { + int iGeom; + int bAllPoly = TRUE; + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom; + if( poGeom->hasCurveGeometry() ) + { + OGRGeometryCollection *poNewGC = (OGRGeometryCollection *) poGC->getLinearGeometry(); + delete poGC; + poGeom = poGC = poNewGC; + } + + for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ ) + { + OGRwkbGeometryType eSubGeomType = wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType()); + if( eSubGeomType != wkbPolygon ) + bAllPoly = FALSE; + } + + if( !bAllPoly ) + return poGeom; + + OGRMultiPolygon *poMP = new OGRMultiPolygon(); + poMP->assignSpatialReference(poGeom->getSpatialReference()); + + while( poGC->getNumGeometries() > 0 ) + { + poMP->addGeometryDirectly( poGC->getGeometryRef(0) ); + poGC->removeGeometry( 0, FALSE ); + } + + delete poGC; + + return poMP; + } + + if( eGeomType == wkbCurvePolygon ) + { + OGRPolygon* poPoly = ((OGRCurvePolygon*)poGeom)->CurvePolyToPoly(); + OGRMultiPolygon *poMP = new OGRMultiPolygon(); + poMP->assignSpatialReference(poGeom->getSpatialReference()); + poMP->addGeometryDirectly( poPoly ); + delete poGeom; + return poMP; + } + +/* -------------------------------------------------------------------- */ +/* Eventually we should try to split the polygon into component */ +/* island polygons. But thats alot of work and can be put off. */ +/* -------------------------------------------------------------------- */ + if( eGeomType != wkbPolygon ) + return poGeom; + + OGRMultiPolygon *poMP = new OGRMultiPolygon(); + poMP->assignSpatialReference(poGeom->getSpatialReference()); + poMP->addGeometryDirectly( poGeom ); + + return poMP; +} + +/************************************************************************/ +/* OGR_G_ForceToMultiPolygon() */ +/************************************************************************/ + +/** + * \brief Convert to multipolygon. + * + * This function is the same as the C++ method + * OGRGeometryFactory::forceToMultiPolygon(). + * + * @param hGeom handle to the geometry to convert (ownership surrendered). + * @return the converted geometry (ownership to caller). + * + * @since GDAL/OGR 1.8.0 + */ + +OGRGeometryH OGR_G_ForceToMultiPolygon( OGRGeometryH hGeom ) + +{ + return (OGRGeometryH) + OGRGeometryFactory::forceToMultiPolygon( (OGRGeometry *) hGeom ); +} + +/************************************************************************/ +/* forceToMultiPoint() */ +/************************************************************************/ + +/** + * \brief Convert to multipoint. + * + * Tries to force the provided geometry to be a multipoint. Currently + * this just effects a change on points or collection of points. + * The passed in geometry is + * consumed and a new one returned (or potentially the same one). + * + * @return new geometry. + */ + +OGRGeometry *OGRGeometryFactory::forceToMultiPoint( OGRGeometry *poGeom ) + +{ + if( poGeom == NULL ) + return NULL; + + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + +/* -------------------------------------------------------------------- */ +/* If this is already a MultiPoint, nothing to do */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbMultiPoint ) + { + return poGeom; + } + +/* -------------------------------------------------------------------- */ +/* Check for the case of a geometrycollection that can be */ +/* promoted to MultiPoint. */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbGeometryCollection ) + { + int iGeom; + int bAllPoint = TRUE; + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom; + + for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ ) + { + if( wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType()) + != wkbPoint ) + bAllPoint = FALSE; + } + + if( !bAllPoint ) + return poGeom; + + OGRMultiPoint *poMP = new OGRMultiPoint(); + poMP->assignSpatialReference(poGeom->getSpatialReference()); + + while( poGC->getNumGeometries() > 0 ) + { + poMP->addGeometryDirectly( poGC->getGeometryRef(0) ); + poGC->removeGeometry( 0, FALSE ); + } + + delete poGC; + + return poMP; + } + + if( eGeomType != wkbPoint ) + return poGeom; + + OGRMultiPoint *poMP = new OGRMultiPoint(); + poMP->assignSpatialReference(poGeom->getSpatialReference()); + poMP->addGeometryDirectly( poGeom ); + + return poMP; +} + +/************************************************************************/ +/* OGR_G_ForceToMultiPoint() */ +/************************************************************************/ + +/** + * \brief Convert to multipoint. + * + * This function is the same as the C++ method + * OGRGeometryFactory::forceToMultiPoint(). + * + * @param hGeom handle to the geometry to convert (ownership surrendered). + * @return the converted geometry (ownership to caller). + * + * @since GDAL/OGR 1.8.0 + */ + +OGRGeometryH OGR_G_ForceToMultiPoint( OGRGeometryH hGeom ) + +{ + return (OGRGeometryH) + OGRGeometryFactory::forceToMultiPoint( (OGRGeometry *) hGeom ); +} + +/************************************************************************/ +/* forceToMultiLinestring() */ +/************************************************************************/ + +/** + * \brief Convert to multilinestring. + * + * Tries to force the provided geometry to be a multilinestring. + * + * - linestrings are placed in a multilinestring. + * - circularstrings and compoundcurves will be approximated and placed in a + * multilinestring. + * - geometry collections will be converted to multilinestring if they only + * contain linestrings. + * - polygons will be changed to a collection of linestrings (one per ring). + * - curvepolygons will be approximated and changed to a collection of linestrings (one per ring). + * + * The passed in geometry is + * consumed and a new one returned (or potentially the same one). + * + * @return new geometry. + */ + +OGRGeometry *OGRGeometryFactory::forceToMultiLineString( OGRGeometry *poGeom ) + +{ + if( poGeom == NULL ) + return NULL; + + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + +/* -------------------------------------------------------------------- */ +/* If this is already a MultiLineString, nothing to do */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbMultiLineString ) + { + return poGeom; + } + +/* -------------------------------------------------------------------- */ +/* Check for the case of a geometrycollection that can be */ +/* promoted to MultiLineString. */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbGeometryCollection ) + { + int iGeom; + int bAllLines = TRUE; + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom; + if( poGeom->hasCurveGeometry() ) + { + OGRGeometryCollection *poNewGC = (OGRGeometryCollection *) poGC->getLinearGeometry(); + delete poGC; + poGeom = poGC = poNewGC; + } + + for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ ) + { + if( poGC->getGeometryRef(iGeom)->getGeometryType() != wkbLineString ) + bAllLines = FALSE; + } + + if( !bAllLines ) + return poGeom; + + OGRMultiLineString *poMP = new OGRMultiLineString(); + poMP->assignSpatialReference(poGeom->getSpatialReference()); + + while( poGC->getNumGeometries() > 0 ) + { + poMP->addGeometryDirectly( poGC->getGeometryRef(0) ); + poGC->removeGeometry( 0, FALSE ); + } + + delete poGC; + + return poMP; + } + +/* -------------------------------------------------------------------- */ +/* Turn a linestring into a multilinestring. */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbLineString ) + { + OGRMultiLineString *poMP = new OGRMultiLineString(); + poMP->assignSpatialReference(poGeom->getSpatialReference()); + poMP->addGeometryDirectly( poGeom ); + return poMP; + } + +/* -------------------------------------------------------------------- */ +/* Convert polygons into a multilinestring. */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbPolygon || eGeomType == wkbCurvePolygon ) + { + OGRMultiLineString *poMP = new OGRMultiLineString(); + OGRPolygon *poPoly; + if( eGeomType == wkbPolygon ) + poPoly = (OGRPolygon *) poGeom; + else + { + poPoly = ((OGRCurvePolygon*) poGeom)->CurvePolyToPoly(); + delete poGeom; + poGeom = poPoly; + } + int iRing; + + poMP->assignSpatialReference(poGeom->getSpatialReference()); + + for( iRing = 0; iRing < poPoly->getNumInteriorRings()+1; iRing++ ) + { + OGRLineString *poNewLS, *poLR; + + if( iRing == 0 ) + { + poLR = poPoly->getExteriorRing(); + if( poLR == NULL ) + break; + } + else + poLR = poPoly->getInteriorRing(iRing-1); + + if (poLR == NULL || poLR->getNumPoints() == 0) + continue; + + poNewLS = new OGRLineString(); + poNewLS->addSubLineString( poLR ); + poMP->addGeometryDirectly( poNewLS ); + } + + delete poPoly; + + return poMP; + } + +/* -------------------------------------------------------------------- */ +/* Convert multi-polygons into a multilinestring. */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbMultiPolygon || eGeomType == wkbMultiSurface ) + { + OGRMultiLineString *poMP = new OGRMultiLineString(); + OGRMultiPolygon *poMPoly; + if( eGeomType == wkbMultiPolygon ) + poMPoly = (OGRMultiPolygon *) poGeom; + else + { + poMPoly = (OGRMultiPolygon *) poGeom->getLinearGeometry(); + delete poGeom; + poGeom = poMPoly; + } + int iPoly; + + poMP->assignSpatialReference(poGeom->getSpatialReference()); + + for( iPoly = 0; iPoly < poMPoly->getNumGeometries(); iPoly++ ) + { + OGRPolygon *poPoly = (OGRPolygon*) poMPoly->getGeometryRef(iPoly); + int iRing; + + for( iRing = 0; iRing < poPoly->getNumInteriorRings()+1; iRing++ ) + { + OGRLineString *poNewLS, *poLR; + + if( iRing == 0 ) + { + poLR = poPoly->getExteriorRing(); + if( poLR == NULL ) + break; + } + else + poLR = poPoly->getInteriorRing(iRing-1); + + if (poLR == NULL || poLR->getNumPoints() == 0) + continue; + + poNewLS = new OGRLineString(); + poNewLS->addSubLineString( poLR ); + poMP->addGeometryDirectly( poNewLS ); + } + } + delete poMPoly; + + return poMP; + } + +/* -------------------------------------------------------------------- */ +/* If it's a curve line, approximate it and wrap in a multilinestring */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbCircularString || + eGeomType == wkbCompoundCurve ) + { + OGRMultiLineString *poMP = new OGRMultiLineString(); + poMP->assignSpatialReference(poGeom->getSpatialReference()); + poMP->addGeometryDirectly( ((OGRCurve*)poGeom)->CurveToLine() ); + delete poGeom; + return poMP; + } + +/* -------------------------------------------------------------------- */ +/* If this is already a MultiCurve with compatible content, */ +/* just cast */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbMultiCurve && + !((OGRMultiCurve*)poGeom)->hasCurveGeometry(TRUE) ) + { + return OGRMultiCurve::CastToMultiLineString((OGRMultiCurve*)poGeom); + } + +/* -------------------------------------------------------------------- */ +/* If it's a multicurve, call getLinearGeometry() */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbMultiCurve ) + { + OGRGeometry* poNewGeom = poGeom->getLinearGeometry(); + CPLAssert( wkbFlatten(poNewGeom->getGeometryType()) == wkbMultiLineString ); + delete poGeom; + return (OGRMultiLineString*) poNewGeom; + } + + return poGeom; +} + +/************************************************************************/ +/* OGR_G_ForceToMultiLineString() */ +/************************************************************************/ + +/** + * \brief Convert to multilinestring. + * + * This function is the same as the C++ method + * OGRGeometryFactory::forceToMultiLineString(). + * + * @param hGeom handle to the geometry to convert (ownership surrendered). + * @return the converted geometry (ownership to caller). + * + * @since GDAL/OGR 1.8.0 + */ + +OGRGeometryH OGR_G_ForceToMultiLineString( OGRGeometryH hGeom ) + +{ + return (OGRGeometryH) + OGRGeometryFactory::forceToMultiLineString( (OGRGeometry *) hGeom ); +} + +/************************************************************************/ +/* organizePolygons() */ +/************************************************************************/ + +typedef struct _sPolyExtended sPolyExtended; + +struct _sPolyExtended +{ + OGRPolygon* poPolygon; + OGREnvelope sEnvelope; + OGRLinearRing* poExteriorRing; + OGRPoint poAPoint; + int nInitialIndex; + int bIsTopLevel; + OGRPolygon* poEnclosingPolygon; + double dfArea; + int bIsCW; +}; + +static int OGRGeometryFactoryCompareArea(const void* p1, const void* p2) +{ + const sPolyExtended* psPoly1 = (const sPolyExtended*) p1; + const sPolyExtended* psPoly2 = (const sPolyExtended*) p2; + if (psPoly2->dfArea < psPoly1->dfArea) + return -1; + else if (psPoly2->dfArea > psPoly1->dfArea) + return 1; + else + return 0; +} + +static int OGRGeometryFactoryCompareByIndex(const void* p1, const void* p2) +{ + const sPolyExtended* psPoly1 = (const sPolyExtended*) p1; + const sPolyExtended* psPoly2 = (const sPolyExtended*) p2; + if (psPoly1->nInitialIndex < psPoly2->nInitialIndex) + return -1; + else if (psPoly1->nInitialIndex > psPoly2->nInitialIndex) + return 1; + else + return 0; +} + +#define N_CRITICAL_PART_NUMBER 100 + +typedef enum +{ + METHOD_NORMAL, + METHOD_SKIP, + METHOD_ONLY_CCW, + METHOD_CCW_INNER_JUST_AFTER_CW_OUTER +} OrganizePolygonMethod; + +/** + * \brief Organize polygons based on geometries. + * + * Analyse a set of rings (passed as simple polygons), and based on a + * geometric analysis convert them into a polygon with inner rings, + * (or a MultiPolygon if dealing with more than one polygon) that follow the + * OGC Simple Feature specification. + * + * All the input geometries must be OGRPolygons with only a valid exterior + * ring (at least 4 points) and no interior rings. + * + * The passed in geometries become the responsibility of the method, but the + * papoPolygons "pointer array" remains owned by the caller. + * + * For faster computation, a polygon is considered to be inside + * another one if a single point of its external ring is included into the other one. + * (unless 'OGR_DEBUG_ORGANIZE_POLYGONS' configuration option is set to TRUE. + * In that case, a slower algorithm that tests exact topological relationships + * is used if GEOS is available.) + * + * In cases where a big number of polygons is passed to this function, the default processing + * may be really slow. You can skip the processing by adding METHOD=SKIP + * to the option list (the result of the function will be a multi-polygon with all polygons + * as toplevel polygons) or only make it analyze counterclockwise polygons by adding + * METHOD=ONLY_CCW to the option list if you can assume that the outline + * of holes is counterclockwise defined (this is the convention for example in shapefiles, + * Personal Geodatabases or File Geodatabases). + * + * For FileGDB, in most cases, but not always, a faster method than ONLY_CCW can be used. It is + * CCW_INNER_JUST_AFTER_CW_OUTER. When using it, inner rings are assumed to be + * counterclockwise oriented, and following immediately the outer ring (clockwise + * oriented) that they belong to. If that assumption is not met, an inner ring + * could be attached to the wrong outer ring, so this method must be used + * with care. + * + * If the OGR_ORGANIZE_POLYGONS configuration option is defined, its value will override + * the value of the METHOD option of papszOptions (useful to modify the behaviour of the + * shapefile driver) + * + * @param papoPolygons array of geometry pointers - should all be OGRPolygons. + * Ownership of the geometries is passed, but not of the array itself. + * @param nPolygonCount number of items in papoPolygons + * @param pbIsValidGeometry value will be set TRUE if result is valid or + * FALSE otherwise. + * @param papszOptions a list of strings for passing options + * + * @return a single resulting geometry (either OGRPolygon or OGRMultiPolygon). + */ + +OGRGeometry* OGRGeometryFactory::organizePolygons( OGRGeometry **papoPolygons, + int nPolygonCount, + int *pbIsValidGeometry, + const char** papszOptions ) +{ + int bUseFastVersion; + int i, j; + OGRGeometry* geom = NULL; + OrganizePolygonMethod method = METHOD_NORMAL; + +/* -------------------------------------------------------------------- */ +/* Trivial case of a single polygon. */ +/* -------------------------------------------------------------------- */ + if (nPolygonCount == 1) + { + geom = papoPolygons[0]; + papoPolygons[0] = NULL; + + if( pbIsValidGeometry ) + *pbIsValidGeometry = TRUE; + + return geom; + } + + if (CSLTestBoolean(CPLGetConfigOption("OGR_DEBUG_ORGANIZE_POLYGONS", "NO"))) + { + /* -------------------------------------------------------------------- */ + /* A wee bit of a warning. */ + /* -------------------------------------------------------------------- */ + static int firstTime = 1; + if (!haveGEOS() && firstTime) + { + CPLDebug("OGR", + "In OGR_DEBUG_ORGANIZE_POLYGONS mode, GDAL should be built with GEOS support enabled in order " + "OGRGeometryFactory::organizePolygons to provide reliable results on complex polygons."); + firstTime = 0; + } + bUseFastVersion = !haveGEOS(); + } + else + { + bUseFastVersion = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Setup per polygon envelope and area information. */ +/* -------------------------------------------------------------------- */ + sPolyExtended* asPolyEx = new sPolyExtended[nPolygonCount]; + + int go_on = TRUE; + int bMixedUpGeometries = FALSE; + int bNonPolygon = FALSE; + int bFoundCCW = FALSE; + + const char* pszMethodValue = CSLFetchNameValue( (char**)papszOptions, "METHOD" ); + const char* pszMethodValueOption = CPLGetConfigOption("OGR_ORGANIZE_POLYGONS", NULL); + if (pszMethodValueOption != NULL && pszMethodValueOption[0] != '\0') + pszMethodValue = pszMethodValueOption; + + if (pszMethodValue != NULL) + { + if (EQUAL(pszMethodValue, "SKIP")) + { + method = METHOD_SKIP; + bMixedUpGeometries = TRUE; + } + else if (EQUAL(pszMethodValue, "ONLY_CCW")) + { + method = METHOD_ONLY_CCW; + } + else if (EQUAL(pszMethodValue, "CCW_INNER_JUST_AFTER_CW_OUTER")) + { + method = METHOD_CCW_INNER_JUST_AFTER_CW_OUTER; + } + else if (!EQUAL(pszMethodValue, "DEFAULT")) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Unrecognized value for METHOD option : %s", pszMethodValue); + } + } + + int nCountCWPolygon = 0; + int indexOfCWPolygon = -1; + + for(i=0;igetEnvelope(&asPolyEx[i].sEnvelope); + + if( wkbFlatten(papoPolygons[i]->getGeometryType()) == wkbPolygon + && ((OGRPolygon *) papoPolygons[i])->getNumInteriorRings() == 0 + && ((OGRPolygon *)papoPolygons[i])->getExteriorRing()->getNumPoints() >= 4) + { + if( method != METHOD_CCW_INNER_JUST_AFTER_CW_OUTER ) + asPolyEx[i].dfArea = asPolyEx[i].poPolygon->get_Area(); + asPolyEx[i].poExteriorRing = asPolyEx[i].poPolygon->getExteriorRing(); + asPolyEx[i].poExteriorRing->getPoint(0, &asPolyEx[i].poAPoint); + asPolyEx[i].bIsCW = asPolyEx[i].poExteriorRing->isClockwise(); + if (asPolyEx[i].bIsCW) + { + indexOfCWPolygon = i; + nCountCWPolygon ++; + } + if (!bFoundCCW) + bFoundCCW = ! (asPolyEx[i].bIsCW); + } + else + { + if( !bMixedUpGeometries ) + { + CPLError( + CE_Warning, CPLE_AppDefined, + "organizePolygons() received an unexpected geometry.\n" + "Either a polygon with interior rings, or a polygon with less than 4 points,\n" + "or a non-Polygon geometry. Return arguments as a collection." ); + bMixedUpGeometries = TRUE; + } + if( wkbFlatten(papoPolygons[i]->getGeometryType()) != wkbPolygon ) + bNonPolygon = TRUE; + } + } + + /* If we are in ONLY_CCW mode and that we have found that there is only one outer ring, */ + /* then it is pretty easy : we can assume that all other rings are inside */ + if ((method == METHOD_ONLY_CCW || method == METHOD_CCW_INNER_JUST_AFTER_CW_OUTER) && + nCountCWPolygon == 1 && bUseFastVersion && !bNonPolygon ) + { + geom = asPolyEx[indexOfCWPolygon].poPolygon; + for(i=0; iaddRingDirectly(asPolyEx[i].poPolygon->stealExteriorRing()); + delete asPolyEx[i].poPolygon; + } + } + delete [] asPolyEx; + if (pbIsValidGeometry) + *pbIsValidGeometry = TRUE; + return geom; + } + + if( method == METHOD_CCW_INNER_JUST_AFTER_CW_OUTER && !bNonPolygon && asPolyEx[0].bIsCW ) + { + /* Inner rings are CCW oriented and follow immediately the outer */ + /* ring (that is CW oriented) in which they are included */ + OGRMultiPolygon* poMulti = NULL; + OGRPolygon* poCur = asPolyEx[0].poPolygon; + OGRGeometry* poRet = poCur; + /* We have already checked that the first ring is CW */ + OGREnvelope* psEnvelope = &(asPolyEx[0].sEnvelope); + for(i=1;iaddGeometryDirectly(poCur); + } + poCur = asPolyEx[i].poPolygon; + poMulti->addGeometryDirectly(poCur); + psEnvelope = &(asPolyEx[i].sEnvelope); + } + else + { + poCur->addRingDirectly(asPolyEx[i].poPolygon->stealExteriorRing()); + if(!(asPolyEx[i].poAPoint.getX() >= psEnvelope->MinX && + asPolyEx[i].poAPoint.getX() <= psEnvelope->MaxX && + asPolyEx[i].poAPoint.getY() >= psEnvelope->MinY && + asPolyEx[i].poAPoint.getY() <= psEnvelope->MaxY)) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Part %d does not respect CCW_INNER_JUST_AFTER_CW_OUTER rule", i); + } + delete asPolyEx[i].poPolygon; + } + } + delete [] asPolyEx; + if (pbIsValidGeometry) + *pbIsValidGeometry = TRUE; + return poRet; + } + else if( method == METHOD_CCW_INNER_JUST_AFTER_CW_OUTER && !bNonPolygon ) + { + method = METHOD_ONLY_CCW; + for(i=0;iget_Area(); + } + + /* Emits a warning if the number of parts is sufficiently big to anticipate for */ + /* very long computation time, and the user didn't specify an explicit method */ + if (nPolygonCount > N_CRITICAL_PART_NUMBER && method == METHOD_NORMAL && pszMethodValue == NULL) + { + static int firstTime = 1; + if (firstTime) + { + if (bFoundCCW) + { + CPLError( CE_Warning, CPLE_AppDefined, + "organizePolygons() received a polygon with more than %d parts. " + "The processing may be really slow.\n" + "You can skip the processing by setting METHOD=SKIP, " + "or only make it analyze counter-clock wise parts by setting " + "METHOD=ONLY_CCW if you can assume that the " + "outline of holes is counter-clock wise defined", N_CRITICAL_PART_NUMBER); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "organizePolygons() received a polygon with more than %d parts. " + "The processing may be really slow.\n" + "You can skip the processing by setting METHOD=SKIP.", + N_CRITICAL_PART_NUMBER); + } + firstTime = 0; + } + } + + + /* This a several steps algorithm : + 1) Sort polygons by descending areas + 2) For each polygon of rank i, find its smallest enclosing polygon + among the polygons of rank [i-1 ... 0]. If there are no such polygon, + this is a toplevel polygon. Otherwise, depending on if the enclosing + polygon is toplevel or not, we can decide if we are toplevel or not + 3) Re-sort the polygons to retrieve their inital order (nicer for some applications) + 4) For each non toplevel polygon (= inner ring), add it to its outer ring + 5) Add the toplevel polygons to the multipolygon + + Complexity : O(nPolygonCount^2) + */ + + /* Compute how each polygon relate to the other ones + To save a bit of computation we always begin the computation by a test + on the enveloppe. We also take into account the areas to avoid some + useless tests. (A contains B implies envelop(A) contains envelop(B) + and area(A) > area(B)) In practise, we can hope that few full geometry + intersection of inclusion test is done: + * if the polygons are well separated geographically (a set of islands + for example), no full geometry intersection or inclusion test is done. + (the envelopes don't intersect each other) + + * if the polygons are 'lake inside an island inside a lake inside an + area' and that each polygon is much smaller than its enclosing one, + their bounding boxes are stricly contained into each oter, and thus, + no full geometry intersection or inclusion test is done + */ + + if (!bMixedUpGeometries) + { + /* STEP 1 : Sort polygons by descending area */ + qsort(asPolyEx, nPolygonCount, sizeof(sPolyExtended), OGRGeometryFactoryCompareArea); + } + papoPolygons = NULL; /* just to use to avoid it afterwards */ + +/* -------------------------------------------------------------------- */ +/* Compute relationships, if things seem well structured. */ +/* -------------------------------------------------------------------- */ + + /* The first (largest) polygon is necessarily top-level */ + asPolyEx[0].bIsTopLevel = TRUE; + asPolyEx[0].poEnclosingPolygon = NULL; + + int nCountTopLevel = 1; + + /* STEP 2 */ + for(i=1; !bMixedUpGeometries && go_on && i=0;j--) + { + int b_i_inside_j = FALSE; + + if (method == METHOD_ONLY_CCW && asPolyEx[j].bIsCW == FALSE) + { + /* In that mode, i which is CCW if we reach here can only be */ + /* included in a CW polygon */ + continue; + } + + if (asPolyEx[j].sEnvelope.Contains(asPolyEx[i].sEnvelope)) + { + if (bUseFastVersion) + { + if( method == METHOD_ONLY_CCW && j == 0 ) + { + /* We are testing if a CCW ring is in the biggest CW ring */ + /* It *must* be inside as this is the last candidate, otherwise */ + /* the winding order rules is broken */ + b_i_inside_j = TRUE; + } + else if (asPolyEx[j].poExteriorRing->isPointOnRingBoundary(&asPolyEx[i].poAPoint, FALSE)) + { + /* If the point of i is on the boundary of j, we will iterate over the other points of i */ + int k, nPoints = asPolyEx[i].poExteriorRing->getNumPoints(); + for(k=1;kgetPoint(k, &point); + if (asPolyEx[j].poExteriorRing->isPointOnRingBoundary(&point, FALSE)) + { + /* If it is on the boundary of j, iterate again */ + } + else if (asPolyEx[j].poExteriorRing->isPointInRing(&point, FALSE)) + { + /* If then point is strictly included in j, then i is considered inside j */ + b_i_inside_j = TRUE; + break; + } + else + { + /* If it is outside, then i cannot be inside j */ + break; + } + } + if( !b_i_inside_j && k == nPoints && nPoints > 2 ) + { + /* all points of i are on the boundary of j ... */ + /* take a point in the middle of a segment of i and */ + /* test it against j */ + for(k=0;kgetPoint(k, &point1); + asPolyEx[i].poExteriorRing->getPoint(k+1, &point2); + pointMiddle.setX((point1.getX() + point2.getX()) / 2); + pointMiddle.setY((point1.getY() + point2.getY()) / 2); + if (asPolyEx[j].poExteriorRing->isPointOnRingBoundary(&pointMiddle, FALSE)) + { + /* If it is on the boundary of j, iterate again */ + } + else if (asPolyEx[j].poExteriorRing->isPointInRing(&pointMiddle, FALSE)) + { + /* If then point is strictly included in j, then i is considered inside j */ + b_i_inside_j = TRUE; + break; + } + else + { + /* If it is outside, then i cannot be inside j */ + break; + } + } + } + } + /* Note that isPointInRing only test strict inclusion in the ring */ + else if (asPolyEx[j].poExteriorRing->isPointInRing(&asPolyEx[i].poAPoint, FALSE)) + { + b_i_inside_j = TRUE; + } + } + else if (asPolyEx[j].poPolygon->Contains(asPolyEx[i].poPolygon)) + { + b_i_inside_j = TRUE; + } + } + + + if (b_i_inside_j) + { + if (asPolyEx[j].bIsTopLevel) + { + /* We are a lake */ + asPolyEx[i].bIsTopLevel = FALSE; + asPolyEx[i].poEnclosingPolygon = asPolyEx[j].poPolygon; + } + else + { + /* We are included in a something not toplevel (a lake), */ + /* so in OGCSF we are considered as toplevel too */ + nCountTopLevel ++; + asPolyEx[i].bIsTopLevel = TRUE; + asPolyEx[i].poEnclosingPolygon = NULL; + } + break; + } + /* We use Overlaps instead of Intersects to be more + tolerant about touching polygons */ + else if ( bUseFastVersion || !asPolyEx[i].sEnvelope.Intersects(asPolyEx[j].sEnvelope) + || !asPolyEx[i].poPolygon->Overlaps(asPolyEx[j].poPolygon) ) + { + + } + else + { + /* Bad... The polygons are intersecting but no one is + contained inside the other one. This is a really broken + case. We just make a multipolygon with the whole set of + polygons */ + go_on = FALSE; +#ifdef DEBUG + char* wkt1; + char* wkt2; + asPolyEx[i].poPolygon->exportToWkt(&wkt1); + asPolyEx[j].poPolygon->exportToWkt(&wkt2); + CPLDebug( "OGR", + "Bad intersection for polygons %d and %d\n" + "geom %d: %s\n" + "geom %d: %s", + i, j, i, wkt1, j, wkt2 ); + CPLFree(wkt1); + CPLFree(wkt2); +#endif + } + } + + if (j < 0) + { + /* We come here because we are not included in anything */ + /* We are toplevel */ + nCountTopLevel ++; + asPolyEx[i].bIsTopLevel = TRUE; + asPolyEx[i].poEnclosingPolygon = NULL; + } + } + + if (pbIsValidGeometry) + *pbIsValidGeometry = go_on && !bMixedUpGeometries; + +/* -------------------------------------------------------------------- */ +/* Things broke down - just turn everything into a multipolygon. */ +/* -------------------------------------------------------------------- */ + if ( !go_on || bMixedUpGeometries ) + { + if( bNonPolygon ) + geom = new OGRGeometryCollection(); + else + geom = new OGRMultiPolygon(); + + for( i=0; i < nPolygonCount; i++ ) + { + ((OGRGeometryCollection*)geom)-> + addGeometryDirectly( asPolyEx[i].poPolygon ); + } + } + +/* -------------------------------------------------------------------- */ +/* Try to turn into one or more polygons based on the ring */ +/* relationships. */ +/* -------------------------------------------------------------------- */ + else + { + /* STEP 3: Resort in initial order */ + qsort(asPolyEx, nPolygonCount, sizeof(sPolyExtended), OGRGeometryFactoryCompareByIndex); + + /* STEP 4: Add holes as rings of their enclosing polygon */ + for(i=0;iaddRingDirectly( + asPolyEx[i].poPolygon->stealExteriorRing()); + delete asPolyEx[i].poPolygon; + } + else if (nCountTopLevel == 1) + { + geom = asPolyEx[i].poPolygon; + } + } + + /* STEP 5: Add toplevel polygons */ + if (nCountTopLevel > 1) + { + for(i=0;iaddGeometryDirectly(asPolyEx[i].poPolygon); + } + else + { + ((OGRMultiPolygon*)geom)->addGeometryDirectly(asPolyEx[i].poPolygon); + } + } + } + } + } + + delete[] asPolyEx; + + return geom; +} + +/************************************************************************/ +/* createFromGML() */ +/************************************************************************/ + +/** + * \brief Create geometry from GML. + * + * This method translates a fragment of GML containing only the geometry + * portion into a corresponding OGRGeometry. There are many limitations + * on the forms of GML geometries supported by this parser, but they are + * too numerous to list here. + * + * The following GML2 elements are parsed : Point, LineString, Polygon, + * MultiPoint, MultiLineString, MultiPolygon, MultiGeometry. + * + * (OGR >= 1.8.0) The following GML3 elements are parsed : Surface, MultiSurface, + * PolygonPatch, Triangle, Rectangle, Curve, MultiCurve, LineStringSegment, Arc, + * Circle, CompositeSurface, OrientableSurface, Solid, Tin, TriangulatedSurface. + * + * Arc and Circle elements are stroked to linestring, by using a + * 4 degrees step, unless the user has overridden the value with the + * OGR_ARC_STEPSIZE configuration variable. + * + * The C function OGR_G_CreateFromGML() is the same as this method. + * + * @param pszData The GML fragment for the geometry. + * + * @return a geometry on succes, or NULL on error. + */ + +OGRGeometry *OGRGeometryFactory::createFromGML( const char *pszData ) + +{ + OGRGeometryH hGeom; + + hGeom = OGR_G_CreateFromGML( pszData ); + + return (OGRGeometry *) hGeom; +} + +/************************************************************************/ +/* createFromGEOS() */ +/************************************************************************/ + +OGRGeometry * +OGRGeometryFactory::createFromGEOS( GEOSContextHandle_t hGEOSCtxt, GEOSGeom geosGeom ) + +{ +#ifndef HAVE_GEOS + + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); + return NULL; + +#else + + size_t nSize = 0; + unsigned char *pabyBuf = NULL; + OGRGeometry *poGeometry = NULL; + + /* Special case as POINT EMPTY cannot be translated to WKB */ + if (GEOSGeomTypeId_r(hGEOSCtxt, geosGeom) == GEOS_POINT && + GEOSisEmpty_r(hGEOSCtxt, geosGeom)) + return new OGRPoint(); + +#if GEOS_VERSION_MAJOR > 3 || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 3) + /* GEOSGeom_getCoordinateDimension only available in GEOS 3.3.0 (unreleased at time of writing) */ + int nCoordDim = GEOSGeom_getCoordinateDimension_r(hGEOSCtxt, geosGeom); + GEOSWKBWriter* wkbwriter = GEOSWKBWriter_create_r(hGEOSCtxt); + GEOSWKBWriter_setOutputDimension_r(hGEOSCtxt, wkbwriter, nCoordDim); + pabyBuf = GEOSWKBWriter_write_r(hGEOSCtxt, wkbwriter, geosGeom, &nSize ); + GEOSWKBWriter_destroy_r(hGEOSCtxt, wkbwriter); +#else + pabyBuf = GEOSGeomToWKB_buf_r( hGEOSCtxt, geosGeom, &nSize ); +#endif + if( pabyBuf == NULL || nSize == 0 ) + { + return NULL; + } + + if( OGRGeometryFactory::createFromWkb( (unsigned char *) pabyBuf, + NULL, &poGeometry, (int) nSize ) + != OGRERR_NONE ) + { + poGeometry = NULL; + } + + if( pabyBuf != NULL ) + { + /* Since GEOS 3.1.1, so we test 3.2.0 */ +#if GEOS_CAPI_VERSION_MAJOR >= 2 || (GEOS_CAPI_VERSION_MAJOR == 1 && GEOS_CAPI_VERSION_MINOR >= 6) + GEOSFree_r( hGEOSCtxt, pabyBuf ); +#else + free( pabyBuf ); +#endif + } + + return poGeometry; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* haveGEOS() */ +/************************************************************************/ + +/** + * \brief Test if GEOS enabled. + * + * This static method returns TRUE if GEOS support is built into OGR, + * otherwise it returns FALSE. + * + * @return TRUE if available, otherwise FALSE. + */ + +int OGRGeometryFactory::haveGEOS() + +{ +#ifndef HAVE_GEOS + return FALSE; +#else + return TRUE; +#endif +} + +/************************************************************************/ +/* createFromFgf() */ +/************************************************************************/ + +/** + * \brief Create a geometry object of the appropriate type from it's FGF (FDO Geometry Format) binary representation. + * + * Also note that this is a static method, and that there + * is no need to instantiate an OGRGeometryFactory object. + * + * The C function OGR_G_CreateFromFgf() is the same as this method. + * + * @param pabyData pointer to the input BLOB data. + * @param poSR pointer to the spatial reference to be assigned to the + * created geometry object. This may be NULL. + * @param ppoReturn the newly created geometry object will be assigned to the + * indicated pointer on return. This will be NULL in case + * of failure. + * @param nBytes the number of bytes available in pabyData. + * @param pnBytesConsumed if not NULL, it will be set to the number of bytes + * consumed (at most nBytes). + * + * @return OGRERR_NONE if all goes well, otherwise any of + * OGRERR_NOT_ENOUGH_DATA, OGRERR_UNSUPPORTED_GEOMETRY_TYPE, or + * OGRERR_CORRUPT_DATA may be returned. + */ + +OGRErr OGRGeometryFactory::createFromFgf( unsigned char *pabyData, + OGRSpatialReference * poSR, + OGRGeometry **ppoReturn, + int nBytes, + int *pnBytesConsumed ) + +{ + return createFromFgfInternal(pabyData, poSR, ppoReturn, nBytes, + pnBytesConsumed, 0); +} + + +/************************************************************************/ +/* createFromFgfInternal() */ +/************************************************************************/ + +OGRErr OGRGeometryFactory::createFromFgfInternal( unsigned char *pabyData, + OGRSpatialReference * poSR, + OGRGeometry **ppoReturn, + int nBytes, + int *pnBytesConsumed, + int nRecLevel ) +{ + OGRErr eErr = OGRERR_NONE; + OGRGeometry *poGeom = NULL; + GInt32 nGType, nGDim; + int nTupleSize = 0; + int iOrdinal = 0; + + (void) iOrdinal; + + /* Arbitrary value, but certainly large enough for reasonable usages ! */ + if( nRecLevel == 32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many recursiong level (%d) while parsing FGF geometry.", + nRecLevel ); + return OGRERR_CORRUPT_DATA; + } + + *ppoReturn = NULL; + + if( nBytes < 4 ) + return OGRERR_NOT_ENOUGH_DATA; + +/* -------------------------------------------------------------------- */ +/* Decode the geometry type. */ +/* -------------------------------------------------------------------- */ + memcpy( &nGType, pabyData + 0, 4 ); + CPL_LSBPTR32( &nGType ); + + if( nGType < 0 || nGType > 13 ) + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + +/* -------------------------------------------------------------------- */ +/* Decode the dimentionality if appropriate. */ +/* -------------------------------------------------------------------- */ + switch( nGType ) + { + case 1: // Point + case 2: // LineString + case 3: // Polygon + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nGDim, pabyData + 4, 4 ); + CPL_LSBPTR32( &nGDim ); + + if( nGDim < 0 || nGDim > 3 ) + return OGRERR_CORRUPT_DATA; + + nTupleSize = 2; + if( nGDim & 0x01 ) // Z + nTupleSize++; + if( nGDim & 0x02 ) // M + nTupleSize++; + + break; + + default: + break; + } + +/* -------------------------------------------------------------------- */ +/* None */ +/* -------------------------------------------------------------------- */ + if( nGType == 0 ) + { + if( pnBytesConsumed ) + *pnBytesConsumed = 4; + } + +/* -------------------------------------------------------------------- */ +/* Point */ +/* -------------------------------------------------------------------- */ + else if( nGType == 1 ) + { + double adfTuple[4]; + + if( nBytes < nTupleSize * 8 + 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( adfTuple, pabyData + 8, nTupleSize*8 ); +#ifdef CPL_MSB + for( iOrdinal = 0; iOrdinal < nTupleSize; iOrdinal++ ) + CPL_SWAP64PTR( adfTuple + iOrdinal ); +#endif + if( nTupleSize > 2 ) + poGeom = new OGRPoint( adfTuple[0], adfTuple[1], adfTuple[2] ); + else + poGeom = new OGRPoint( adfTuple[0], adfTuple[1] ); + + if( pnBytesConsumed ) + *pnBytesConsumed = 8 + nTupleSize * 8; + } + +/* -------------------------------------------------------------------- */ +/* LineString */ +/* -------------------------------------------------------------------- */ + else if( nGType == 2 ) + { + double adfTuple[4]; + GInt32 nPointCount; + int iPoint; + OGRLineString *poLS; + + if( nBytes < 12 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nPointCount, pabyData + 8, 4 ); + CPL_LSBPTR32( &nPointCount ); + + if (nPointCount < 0 || nPointCount > INT_MAX / (nTupleSize * 8)) + return OGRERR_CORRUPT_DATA; + + if( nBytes - 12 < nTupleSize * 8 * nPointCount ) + return OGRERR_NOT_ENOUGH_DATA; + + poGeom = poLS = new OGRLineString(); + poLS->setNumPoints( nPointCount ); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + memcpy( adfTuple, pabyData + 12 + 8*nTupleSize*iPoint, + nTupleSize*8 ); +#ifdef CPL_MSB + for( iOrdinal = 0; iOrdinal < nTupleSize; iOrdinal++ ) + CPL_SWAP64PTR( adfTuple + iOrdinal ); +#endif + if( nTupleSize > 2 ) + poLS->setPoint( iPoint, adfTuple[0], adfTuple[1], adfTuple[2] ); + else + poLS->setPoint( iPoint, adfTuple[0], adfTuple[1] ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = 12 + nTupleSize * 8 * nPointCount; + } + +/* -------------------------------------------------------------------- */ +/* Polygon */ +/* -------------------------------------------------------------------- */ + else if( nGType == 3 ) + { + double adfTuple[4]; + GInt32 nPointCount; + GInt32 nRingCount; + int iPoint, iRing; + OGRLinearRing *poLR; + OGRPolygon *poPoly; + int nNextByte; + + if( nBytes < 12 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nRingCount, pabyData + 8, 4 ); + CPL_LSBPTR32( &nRingCount ); + + if (nRingCount < 0 || nRingCount > INT_MAX / 4) + return OGRERR_CORRUPT_DATA; + + /* Each ring takes at least 4 bytes */ + if (nBytes - 12 < nRingCount * 4) + return OGRERR_NOT_ENOUGH_DATA; + + nNextByte = 12; + + poGeom = poPoly = new OGRPolygon(); + + for( iRing = 0; iRing < nRingCount; iRing++ ) + { + if( nBytes - nNextByte < 4 ) + { + delete poGeom; + return OGRERR_NOT_ENOUGH_DATA; + } + + memcpy( &nPointCount, pabyData + nNextByte, 4 ); + CPL_LSBPTR32( &nPointCount ); + + if (nPointCount < 0 || nPointCount > INT_MAX / (nTupleSize * 8)) + { + delete poGeom; + return OGRERR_CORRUPT_DATA; + } + + nNextByte += 4; + + if( nBytes - nNextByte < nTupleSize * 8 * nPointCount ) + { + delete poGeom; + return OGRERR_NOT_ENOUGH_DATA; + } + + poLR = new OGRLinearRing(); + poLR->setNumPoints( nPointCount ); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + memcpy( adfTuple, pabyData + nNextByte, nTupleSize*8 ); + nNextByte += nTupleSize * 8; + +#ifdef CPL_MSB + for( iOrdinal = 0; iOrdinal < nTupleSize; iOrdinal++ ) + CPL_SWAP64PTR( adfTuple + iOrdinal ); +#endif + if( nTupleSize > 2 ) + poLR->setPoint( iPoint, adfTuple[0], adfTuple[1], adfTuple[2] ); + else + poLR->setPoint( iPoint, adfTuple[0], adfTuple[1] ); + } + + poPoly->addRingDirectly( poLR ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* GeometryCollections of various kinds. */ +/* -------------------------------------------------------------------- */ + else if( nGType == 4 // MultiPoint + || nGType == 5 // MultiLineString + || nGType == 6 // MultiPolygon + || nGType == 7 ) // MultiGeometry + { + OGRGeometryCollection *poGC = NULL; + GInt32 nGeomCount; + int iGeom, nBytesUsed; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nGeomCount, pabyData + 4, 4 ); + CPL_LSBPTR32( &nGeomCount ); + + if (nGeomCount < 0 || nGeomCount > INT_MAX / 4) + return OGRERR_CORRUPT_DATA; + + /* Each geometry takes at least 4 bytes */ + if (nBytes - 8 < 4 * nGeomCount) + return OGRERR_NOT_ENOUGH_DATA; + + nBytesUsed = 8; + + if( nGType == 4 ) + poGC = new OGRMultiPoint(); + else if( nGType == 5 ) + poGC = new OGRMultiLineString(); + else if( nGType == 6 ) + poGC = new OGRMultiPolygon(); + else if( nGType == 7 ) + poGC = new OGRGeometryCollection(); + + for( iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + int nThisGeomSize; + OGRGeometry *poThisGeom = NULL; + + eErr = createFromFgfInternal( pabyData + nBytesUsed, poSR, &poThisGeom, + nBytes - nBytesUsed, &nThisGeomSize, nRecLevel + 1); + if( eErr != OGRERR_NONE ) + { + delete poGC; + return eErr; + } + + nBytesUsed += nThisGeomSize; + eErr = poGC->addGeometryDirectly( poThisGeom ); + if( eErr != OGRERR_NONE ) + { + delete poGC; + delete poThisGeom; + return eErr; + } + } + + poGeom = poGC; + if( pnBytesConsumed ) + *pnBytesConsumed = nBytesUsed; + } + +/* -------------------------------------------------------------------- */ +/* Currently unsupported geometry. */ +/* */ +/* We need to add 10/11/12/13 curve types in some fashion. */ +/* -------------------------------------------------------------------- */ + else + { + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + +/* -------------------------------------------------------------------- */ +/* Assign spatial reference system. */ +/* -------------------------------------------------------------------- */ + if( eErr == OGRERR_NONE ) + { + if( poGeom != NULL && poSR ) + poGeom->assignSpatialReference( poSR ); + *ppoReturn = poGeom; + } + else + { + delete poGeom; + } + + return eErr; +} + +/************************************************************************/ +/* OGR_G_CreateFromFgf() */ +/************************************************************************/ + +OGRErr CPL_DLL OGR_G_CreateFromFgf( unsigned char *pabyData, + OGRSpatialReferenceH hSRS, + OGRGeometryH *phGeometry, + int nBytes, int *pnBytesConsumed ) + +{ + return OGRGeometryFactory::createFromFgf( pabyData, + (OGRSpatialReference *) hSRS, + (OGRGeometry **) phGeometry, + nBytes, pnBytesConsumed ); +} + +/************************************************************************/ +/* SplitLineStringAtDateline() */ +/************************************************************************/ + +#define SWAP_DBL(a,b) do { double tmp = a; a = b; b = tmp; } while(0) + +static void SplitLineStringAtDateline(OGRGeometryCollection* poMulti, + const OGRLineString* poLS, + double dfDateLineOffset) +{ + double dfLeftBorderX = 180 - dfDateLineOffset; + double dfRightBorderX = -180 + dfDateLineOffset; + double dfDiffSpace = 360 - dfDateLineOffset; + + int i; + int bIs3D = poLS->getCoordinateDimension() == 3; + OGRLineString* poNewLS = new OGRLineString(); + poMulti->addGeometryDirectly(poNewLS); + for(i=0;igetNumPoints();i++) + { + double dfX = poLS->getX(i); + if (i > 0 && fabs(dfX - poLS->getX(i-1)) > dfDiffSpace) + { + double dfX1 = poLS->getX(i-1); + double dfY1 = poLS->getY(i-1); + double dfZ1 = poLS->getY(i-1); + double dfX2 = poLS->getX(i); + double dfY2 = poLS->getY(i); + double dfZ2 = poLS->getY(i); + + if (dfX1 > -180 && dfX1 < dfRightBorderX && dfX2 == 180 && + i+1 < poLS->getNumPoints() && + poLS->getX(i+1) > -180 && poLS->getX(i+1) < dfRightBorderX) + { + if( bIs3D ) + poNewLS->addPoint(-180, poLS->getY(i), poLS->getZ(i)); + else + poNewLS->addPoint(-180, poLS->getY(i)); + + i++; + + if( bIs3D ) + poNewLS->addPoint(poLS->getX(i), poLS->getY(i), poLS->getZ(i)); + else + poNewLS->addPoint(poLS->getX(i), poLS->getY(i)); + continue; + } + else if (dfX1 > dfLeftBorderX && dfX1 < 180 && dfX2 == -180 && + i+1 < poLS->getNumPoints() && + poLS->getX(i+1) > dfLeftBorderX && poLS->getX(i+1) < 180) + { + if( bIs3D ) + poNewLS->addPoint(180, poLS->getY(i), poLS->getZ(i)); + else + poNewLS->addPoint(180, poLS->getY(i)); + + i++; + + if( bIs3D ) + poNewLS->addPoint(poLS->getX(i), poLS->getY(i), poLS->getZ(i)); + else + poNewLS->addPoint(poLS->getX(i), poLS->getY(i)); + continue; + } + + if (dfX1 < dfRightBorderX && dfX2 > dfLeftBorderX) + { + SWAP_DBL(dfX1, dfX2); + SWAP_DBL(dfY1, dfY2); + SWAP_DBL(dfZ1, dfZ2); + } + if (dfX1 > dfLeftBorderX && dfX2 < dfRightBorderX) + dfX2 += 360; + + if (dfX1 <= 180 && dfX2 >= 180 && dfX1 < dfX2) + { + double dfRatio = (180 - dfX1) / (dfX2 - dfX1); + double dfY = dfRatio * dfY2 + (1 - dfRatio) * dfY1; + double dfZ = dfRatio * dfZ2 + (1 - dfRatio) * dfZ1; + if( bIs3D ) + poNewLS->addPoint(poLS->getX(i-1) > dfLeftBorderX ? 180 : -180, dfY, dfZ); + else + poNewLS->addPoint(poLS->getX(i-1) > dfLeftBorderX ? 180 : -180, dfY); + poNewLS = new OGRLineString(); + if( bIs3D ) + poNewLS->addPoint(poLS->getX(i-1) > dfLeftBorderX ? -180 : 180, dfY, dfZ); + else + poNewLS->addPoint(poLS->getX(i-1) > dfLeftBorderX ? -180 : 180, dfY); + poMulti->addGeometryDirectly(poNewLS); + } + else + { + poNewLS = new OGRLineString(); + poMulti->addGeometryDirectly(poNewLS); + } + } + if( bIs3D ) + poNewLS->addPoint(dfX, poLS->getY(i), poLS->getZ(i)); + else + poNewLS->addPoint(dfX, poLS->getY(i)); + } +} + +/************************************************************************/ +/* FixPolygonCoordinatesAtDateLine() */ +/************************************************************************/ + +#ifdef HAVE_GEOS +static void FixPolygonCoordinatesAtDateLine(OGRPolygon* poPoly, double dfDateLineOffset) +{ + double dfLeftBorderX = 180 - dfDateLineOffset; + double dfRightBorderX = -180 + dfDateLineOffset; + double dfDiffSpace = 360 - dfDateLineOffset; + + int i, iPart; + for(iPart = 0; iPart < 1 + poPoly->getNumInteriorRings(); iPart++) + { + OGRLineString* poLS = (iPart == 0) ? poPoly->getExteriorRing() : + poPoly->getInteriorRing(iPart-1); + int bGoEast = FALSE; + int bIs3D = poLS->getCoordinateDimension() == 3; + for(i=1;igetNumPoints();i++) + { + double dfX = poLS->getX(i); + double dfPrevX = poLS->getX(i-1); + double dfDiffLong = fabs(dfX - dfPrevX); + if (dfDiffLong > dfDiffSpace) + { + if ((dfPrevX > dfLeftBorderX && dfX < dfRightBorderX) || (dfX < 0 && bGoEast)) + { + dfX += 360; + bGoEast = TRUE; + if( bIs3D ) + poLS->setPoint(i, dfX, poLS->getY(i), poLS->getZ(i)); + else + poLS->setPoint(i, dfX, poLS->getY(i)); + } + else if (dfPrevX < dfRightBorderX && dfX > dfLeftBorderX) + { + int j; + for(j=i-1;j>=0;j--) + { + dfX = poLS->getX(j); + if (dfX < 0) + { + if( bIs3D ) + poLS->setPoint(j, dfX + 360, poLS->getY(j), poLS->getZ(j)); + else + poLS->setPoint(j, dfX + 360, poLS->getY(j)); + } + } + bGoEast = FALSE; + } + else + { + bGoEast = FALSE; + } + } + } + } +} +#endif + +/************************************************************************/ +/* Sub360ToLon() */ +/************************************************************************/ + +static void Sub360ToLon( OGRGeometry* poGeom ) +{ + switch (wkbFlatten(poGeom->getGeometryType())) + { + case wkbPolygon: + case wkbMultiLineString: + case wkbMultiPolygon: + case wkbGeometryCollection: + { + int nSubGeomCount = OGR_G_GetGeometryCount((OGRGeometryH)poGeom); + for( int iGeom = 0; iGeom < nSubGeomCount; iGeom++ ) + { + Sub360ToLon((OGRGeometry*)OGR_G_GetGeometryRef((OGRGeometryH)poGeom, iGeom)); + } + + break; + } + + case wkbLineString: + { + OGRLineString* poLineString = (OGRLineString* )poGeom; + int nPointCount = poLineString->getNumPoints(); + int nCoordDim = poLineString->getCoordinateDimension(); + for( int iPoint = 0; iPoint < nPointCount; iPoint++) + { + if (nCoordDim == 2) + poLineString->setPoint(iPoint, + poLineString->getX(iPoint) - 360, + poLineString->getY(iPoint)); + else + poLineString->setPoint(iPoint, + poLineString->getX(iPoint) - 360, + poLineString->getY(iPoint), + poLineString->getZ(iPoint)); + } + break; + } + + default: + break; + } +} + +/************************************************************************/ +/* AddSimpleGeomToMulti() */ +/************************************************************************/ + +static void AddSimpleGeomToMulti(OGRGeometryCollection* poMulti, + const OGRGeometry* poGeom) +{ + switch (wkbFlatten(poGeom->getGeometryType())) + { + case wkbPolygon: + case wkbLineString: + poMulti->addGeometry(poGeom); + break; + + case wkbMultiLineString: + case wkbMultiPolygon: + case wkbGeometryCollection: + { + int nSubGeomCount = OGR_G_GetGeometryCount((OGRGeometryH)poGeom); + for( int iGeom = 0; iGeom < nSubGeomCount; iGeom++ ) + { + OGRGeometry* poSubGeom = + (OGRGeometry*)OGR_G_GetGeometryRef((OGRGeometryH)poGeom, iGeom); + AddSimpleGeomToMulti(poMulti, poSubGeom); + } + break; + } + + default: + break; + } +} + +/************************************************************************/ +/* CutGeometryOnDateLineAndAddToMulti() */ +/************************************************************************/ + +static void CutGeometryOnDateLineAndAddToMulti(OGRGeometryCollection* poMulti, + const OGRGeometry* poGeom, + double dfDateLineOffset) +{ + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + switch (eGeomType) + { + case wkbPolygon: + case wkbLineString: + { + int bWrapDateline = FALSE; + int bSplitLineStringAtDateline = FALSE; + OGREnvelope oEnvelope; + + poGeom->getEnvelope(&oEnvelope); + + /* Naive heuristics... Place to improvement... */ + OGRGeometry* poDupGeom = NULL; + + double dfLeftBorderX = 180 - dfDateLineOffset; + double dfRightBorderX = -180 + dfDateLineOffset; + double dfDiffSpace = 360 - dfDateLineOffset; + + if (oEnvelope.MinX > dfLeftBorderX && oEnvelope.MaxX > 180) + { +#ifndef HAVE_GEOS + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); +#else + bWrapDateline = TRUE; +#endif + } + else + { + OGRLineString* poLS; + if (eGeomType == wkbPolygon) + poLS = ((OGRPolygon*)poGeom)->getExteriorRing(); + else + poLS = (OGRLineString*)poGeom; + if (poLS) + { + int i; + double dfMaxSmallDiffLong = 0; + int bHasBigDiff = FALSE; + /* Detect big gaps in longitude */ + for(i=1;igetNumPoints();i++) + { + double dfPrevX = poLS->getX(i-1); + double dfX = poLS->getX(i); + double dfDiffLong = fabs(dfX - dfPrevX); + if (dfDiffLong > dfDiffSpace && + ((dfX > dfLeftBorderX && dfPrevX < dfRightBorderX) || (dfPrevX > dfLeftBorderX && dfX < dfRightBorderX))) + bHasBigDiff = TRUE; + else if (dfDiffLong > dfMaxSmallDiffLong) + dfMaxSmallDiffLong = dfDiffLong; + } + if (bHasBigDiff && dfMaxSmallDiffLong < dfDateLineOffset) + { + if (eGeomType == wkbLineString) + bSplitLineStringAtDateline = TRUE; + else + { +#ifndef HAVE_GEOS + CPLError( CE_Failure, CPLE_NotSupported, + "GEOS support not enabled." ); +#else + bWrapDateline = TRUE; + poDupGeom = poGeom->clone(); + FixPolygonCoordinatesAtDateLine((OGRPolygon*)poDupGeom, dfDateLineOffset); +#endif + } + } + } + } + + if (bSplitLineStringAtDateline) + { + SplitLineStringAtDateline(poMulti, (OGRLineString*)poGeom, dfDateLineOffset); + } + else if (bWrapDateline) + { + const OGRGeometry* poWorkGeom = (poDupGeom) ? poDupGeom : poGeom; + OGRGeometry* poRectangle1 = NULL; + OGRGeometry* poRectangle2 = NULL; + const char* pszWKT1 = "POLYGON((0 90,180 90,180 -90,0 -90,0 90))"; + const char* pszWKT2 = "POLYGON((180 90,360 90,360 -90,180 -90,180 90))"; + OGRGeometryFactory::createFromWkt((char**)&pszWKT1, NULL, &poRectangle1); + OGRGeometryFactory::createFromWkt((char**)&pszWKT2, NULL, &poRectangle2); + OGRGeometry* poGeom1 = poWorkGeom->Intersection(poRectangle1); + OGRGeometry* poGeom2 = poWorkGeom->Intersection(poRectangle2); + delete poRectangle1; + delete poRectangle2; + + if (poGeom1 != NULL && poGeom2 != NULL) + { + AddSimpleGeomToMulti(poMulti, poGeom1); + Sub360ToLon(poGeom2); + AddSimpleGeomToMulti(poMulti, poGeom2); + } + else + { + AddSimpleGeomToMulti(poMulti, poGeom); + } + + delete poGeom1; + delete poGeom2; + delete poDupGeom; + } + else + { + poMulti->addGeometry(poGeom); + } + break; + } + + case wkbMultiLineString: + case wkbMultiPolygon: + case wkbGeometryCollection: + { + int nSubGeomCount = OGR_G_GetGeometryCount((OGRGeometryH)poGeom); + for( int iGeom = 0; iGeom < nSubGeomCount; iGeom++ ) + { + OGRGeometry* poSubGeom = + (OGRGeometry*)OGR_G_GetGeometryRef((OGRGeometryH)poGeom, iGeom); + CutGeometryOnDateLineAndAddToMulti(poMulti, poSubGeom, dfDateLineOffset); + } + break; + } + + default: + break; + } +} + +/************************************************************************/ +/* transformWithOptions() */ +/************************************************************************/ + +OGRGeometry* OGRGeometryFactory::transformWithOptions( const OGRGeometry* poSrcGeom, + OGRCoordinateTransformation *poCT, + char** papszOptions ) +{ + OGRGeometry* poDstGeom = poSrcGeom->clone(); + if (poCT != NULL) + { + OGRErr eErr = poDstGeom->transform(poCT); + if (eErr != OGRERR_NONE) + { + delete poDstGeom; + return NULL; + } + } + + if (CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "WRAPDATELINE", "NO"))) + { + OGRwkbGeometryType eType = wkbFlatten(poSrcGeom->getGeometryType()); + OGRwkbGeometryType eNewType; + if (eType == wkbPolygon || eType == wkbMultiPolygon) + eNewType = wkbMultiPolygon; + else if (eType == wkbLineString || eType == wkbMultiLineString) + eNewType = wkbMultiLineString; + else + eNewType = wkbGeometryCollection; + + OGRGeometryCollection* poMulti = + (OGRGeometryCollection* )createGeometry(eNewType); + + double dfDateLineOffset = CPLAtofM(CSLFetchNameValueDef(papszOptions, "DATELINEOFFSET", "10")); + if(dfDateLineOffset <= 0 || dfDateLineOffset >= 360) + dfDateLineOffset = 10; + + CutGeometryOnDateLineAndAddToMulti(poMulti, poDstGeom, dfDateLineOffset); + + if (poMulti->getNumGeometries() == 0) + { + delete poMulti; + } + else if (poMulti->getNumGeometries() == 1) + { + delete poDstGeom; + poDstGeom = poMulti->getGeometryRef(0)->clone(); + delete poMulti; + } + else + { + delete poDstGeom; + poDstGeom = poMulti; + } + } + + return poDstGeom; +} + +/************************************************************************/ +/* OGRGF_GetDefaultStepSize() */ +/************************************************************************/ + +static double OGRGF_GetDefaultStepSize() +{ + return CPLAtofM(CPLGetConfigOption("OGR_ARC_STEPSIZE","4")); +} + +/************************************************************************/ +/* approximateArcAngles() */ +/************************************************************************/ + +/** + * Stroke arc to linestring. + * + * Stroke an arc of a circle to a linestring based on a center + * point, radius, start angle and end angle, all angles in degrees. + * + * If the dfMaxAngleStepSizeDegrees is zero, then a default value will be + * used. This is currently 4 degrees unless the user has overridden the + * value with the OGR_ARC_STEPSIZE configuration variable. + * + * @see CPLSetConfigOption() + * + * @param dfCenterX center X + * @param dfCenterY center Y + * @param dfZ center Z + * @param dfPrimaryRadius X radius of ellipse. + * @param dfSecondaryRadius Y radius of ellipse. + * @param dfRotation rotation of the ellipse clockwise. + * @param dfStartAngle angle to first point on arc (clockwise of X-positive) + * @param dfEndAngle angle to last point on arc (clockwise of X-positive) + * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the + * arc, zero to use the default setting. + * + * @return OGRLineString geometry representing an approximation of the arc. + * + * @since OGR 1.8.0 + */ + +OGRGeometry* OGRGeometryFactory::approximateArcAngles( + double dfCenterX, double dfCenterY, double dfZ, + double dfPrimaryRadius, double dfSecondaryRadius, double dfRotation, + double dfStartAngle, double dfEndAngle, + double dfMaxAngleStepSizeDegrees ) + +{ + double dfSlice; + int iPoint, nVertexCount; + OGRLineString *poLine = new OGRLineString(); + double dfRotationRadians = dfRotation * M_PI / 180.0; + + // support default arc step setting. + if( dfMaxAngleStepSizeDegrees < 1e-6 ) + { + dfMaxAngleStepSizeDegrees = OGRGF_GetDefaultStepSize(); + } + + // switch direction + dfStartAngle *= -1; + dfEndAngle *= -1; + + // Figure out the number of slices to make this into. + nVertexCount = (int) + ceil(fabs(dfEndAngle - dfStartAngle)/dfMaxAngleStepSizeDegrees) + 1; + nVertexCount = MAX(2,nVertexCount); + dfSlice = (dfEndAngle-dfStartAngle)/(nVertexCount-1); + +/* -------------------------------------------------------------------- */ +/* Compute the interpolated points. */ +/* -------------------------------------------------------------------- */ + for( iPoint=0; iPoint < nVertexCount; iPoint++ ) + { + double dfAngleOnEllipse; + double dfArcX, dfArcY; + double dfEllipseX, dfEllipseY; + + dfAngleOnEllipse = (dfStartAngle + iPoint * dfSlice) * M_PI / 180.0; + + // Compute position on the unrotated ellipse. + dfEllipseX = cos(dfAngleOnEllipse) * dfPrimaryRadius; + dfEllipseY = sin(dfAngleOnEllipse) * dfSecondaryRadius; + + // Rotate this position around the center of the ellipse. + dfArcX = dfCenterX + + dfEllipseX * cos(dfRotationRadians) + + dfEllipseY * sin(dfRotationRadians); + dfArcY = dfCenterY + - dfEllipseX * sin(dfRotationRadians) + + dfEllipseY * cos(dfRotationRadians); + + poLine->setPoint( iPoint, dfArcX, dfArcY, dfZ ); + } + + return poLine; +} + +/************************************************************************/ +/* OGR_G_ApproximateArcAngles() */ +/************************************************************************/ + +/** + * Stroke arc to linestring. + * + * Stroke an arc of a circle to a linestring based on a center + * point, radius, start angle and end angle, all angles in degrees. + * + * If the dfMaxAngleStepSizeDegrees is zero, then a default value will be + * used. This is currently 4 degrees unless the user has overridden the + * value with the OGR_ARC_STEPSIZE configuration variable. + * + * @see CPLSetConfigOption() + * + * @param dfCenterX center X + * @param dfCenterY center Y + * @param dfZ center Z + * @param dfPrimaryRadius X radius of ellipse. + * @param dfSecondaryRadius Y radius of ellipse. + * @param dfRotation rotation of the ellipse clockwise. + * @param dfStartAngle angle to first point on arc (clockwise of X-positive) + * @param dfEndAngle angle to last point on arc (clockwise of X-positive) + * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the + * arc, zero to use the default setting. + * + * @return OGRLineString geometry representing an approximation of the arc. + * + * @since OGR 1.8.0 + */ + +OGRGeometryH CPL_DLL +OGR_G_ApproximateArcAngles( + double dfCenterX, double dfCenterY, double dfZ, + double dfPrimaryRadius, double dfSecondaryRadius, double dfRotation, + double dfStartAngle, double dfEndAngle, + double dfMaxAngleStepSizeDegrees ) + +{ + return (OGRGeometryH) OGRGeometryFactory::approximateArcAngles( + dfCenterX, dfCenterY, dfZ, + dfPrimaryRadius, dfSecondaryRadius, dfRotation, + dfStartAngle, dfEndAngle, dfMaxAngleStepSizeDegrees ); +} + +/************************************************************************/ +/* forceToLineString() */ +/************************************************************************/ + +/** + * \brief Convert to line string. + * + * Tries to force the provided geometry to be a line string. This nominally + * effects a change on multilinestrings. + * In GDAL 2.0, for polygons or curvepolygons that have a single exterior ring, + * it will return the ring. For circular strings or compound curves, it will + * return an approximated line string. + * + * The passed in geometry is + * consumed and a new one returned (or potentially the same one). + * + * @param poGeom the input geometry - ownership is passed to the method. + * @param bOnlyInOrder flag that, if set to FALSE, indicate that the order of + * points in a linestring might be reversed if it enables + * to match the extremity of another linestring. If set + * to TRUE, the start of a linestring must match the end + * of another linestring. + * @return new geometry. + */ + +OGRGeometry *OGRGeometryFactory::forceToLineString( OGRGeometry *poGeom, bool bOnlyInOrder ) + +{ + if( poGeom == NULL ) + return NULL; + + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + +/* -------------------------------------------------------------------- */ +/* If this is already a LineString, nothing to do */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbLineString ) + { + /* except if it is a linearring */ + poGeom = OGRCurve::CastToLineString((OGRCurve*)poGeom); + + return poGeom; + } + +/* -------------------------------------------------------------------- */ +/* If it's a polygon with a single ring, return it */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbPolygon || eGeomType == wkbCurvePolygon ) + { + OGRCurvePolygon* poCP = (OGRCurvePolygon*)poGeom; + if( poCP->getNumInteriorRings() == 0 ) + { + OGRCurve* poRing = poCP->stealExteriorRingCurve(); + delete poCP; + return forceToLineString(poRing); + } + return poGeom; + } + +/* -------------------------------------------------------------------- */ +/* If it's a curve line, call CurveToLine() */ +/* -------------------------------------------------------------------- */ + if( eGeomType == wkbCircularString || + eGeomType == wkbCompoundCurve ) + { + OGRGeometry* poNewGeom = ((OGRCurve*)poGeom)->CurveToLine(); + delete poGeom; + return poNewGeom; + } + + + if( eGeomType != wkbGeometryCollection + && eGeomType != wkbMultiLineString + && eGeomType != wkbMultiCurve ) + return poGeom; + + // build an aggregated linestring from all the linestrings in the container. + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom; + if( poGeom->hasCurveGeometry() ) + { + OGRGeometryCollection *poNewGC = (OGRGeometryCollection *) poGC->getLinearGeometry(); + delete poGC; + poGeom = poGC = poNewGC; + } + + if( poGC->getNumGeometries() == 0 ) + { + poGeom = new OGRLineString(); + poGeom->assignSpatialReference(poGC->getSpatialReference()); + delete poGC; + return poGeom; + } + + int iGeom0 = 0; + while( iGeom0 < poGC->getNumGeometries() ) + { + if( wkbFlatten(poGC->getGeometryRef(iGeom0)->getGeometryType()) + != wkbLineString ) + { + iGeom0++; + continue; + } + + OGRLineString *poLineString0 = (OGRLineString *) poGC->getGeometryRef(iGeom0); + if( poLineString0->getNumPoints() < 2 ) + { + iGeom0++; + continue; + } + + OGRPoint pointStart0, pointEnd0; + poLineString0->StartPoint( &pointStart0 ); + poLineString0->EndPoint( &pointEnd0 ); + + int iGeom1; + for( iGeom1 = iGeom0 + 1; iGeom1 < poGC->getNumGeometries(); iGeom1++ ) + { + if( wkbFlatten(poGC->getGeometryRef(iGeom1)->getGeometryType()) + != wkbLineString ) + continue; + + OGRLineString *poLineString1 = (OGRLineString *) poGC->getGeometryRef(iGeom1); + if( poLineString1->getNumPoints() < 2 ) + continue; + + OGRPoint pointStart1, pointEnd1; + poLineString1->StartPoint( &pointStart1 ); + poLineString1->EndPoint( &pointEnd1 ); + + if ( !bOnlyInOrder && + ( pointEnd0.Equals( &pointEnd1 ) || pointStart0.Equals( &pointStart1 ) ) ) + { + poLineString1->reversePoints(); + poLineString1->StartPoint( &pointStart1 ); + poLineString1->EndPoint( &pointEnd1 ); + } + + if ( pointEnd0.Equals( &pointStart1 ) ) + { + poLineString0->addSubLineString( poLineString1, 1 ); + poGC->removeGeometry( iGeom1 ); + break; + } + + if( pointEnd1.Equals( &pointStart0 ) ) + { + poLineString1->addSubLineString( poLineString0, 1 ); + poGC->removeGeometry( iGeom0 ); + break; + } + } + + if ( iGeom1 == poGC->getNumGeometries() ) + { + iGeom0++; + } + } + + if ( poGC->getNumGeometries() == 1 ) + { + OGRLineString *poLineString = (OGRLineString *) poGC->getGeometryRef(0); + poGC->removeGeometry( 0, FALSE ); + delete poGC; + + return poLineString; + } + + return poGC; +} + +/************************************************************************/ +/* OGR_G_ForceToLineString() */ +/************************************************************************/ + +/** + * \brief Convert to line string. + * + * This function is the same as the C++ method + * OGRGeometryFactory::forceToLineString(). + * + * @param hGeom handle to the geometry to convert (ownership surrendered). + * @return the converted geometry (ownership to caller). + * + * @since GDAL/OGR 1.10.0 + */ + +OGRGeometryH OGR_G_ForceToLineString( OGRGeometryH hGeom ) + +{ + return (OGRGeometryH) + OGRGeometryFactory::forceToLineString( (OGRGeometry *) hGeom ); +} + + +/************************************************************************/ +/* forceTo() */ +/************************************************************************/ + +/** + * \brief Convert to another geometry type + * + * Tries to force the provided geometry to the specified geometry type. + * + * It can promote 'single' geometry type to their corresponding collection type + * (see OGR_GT_GetCollection()) or the reverse. non-linear geometry type to + * their corresponding linear geometry type (see OGR_GT_GetLinear()), by + * possibly approximating circular arcs they may contain. + * Regarding conversion from linear geometry types to curve geometry types, only + * "wraping" will be done. No attempt to retrieve potential circular arcs by + * de-approximating stroking will be done. For that, OGRGeometry::getCurveGeometry() + * can be used. + * + * The passed in geometry is consumed and a new one returned (or potentially the same one). + * + * @param poGeom the input geometry - ownership is passed to the method. + * @param eTargetType target output geometry type. + * @param papszOptions options as a null-terminated list of strings or NULL. + * @return new geometry. + * + * @since GDAL 2.0 + */ + +OGRGeometry * OGRGeometryFactory::forceTo( OGRGeometry* poGeom, + OGRwkbGeometryType eTargetType, + const char*const* papszOptions ) +{ + if( poGeom == NULL ) + return poGeom; + + eTargetType = wkbFlatten(eTargetType); + OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType()); + if( eType == eTargetType || eTargetType == wkbUnknown ) + return poGeom; + + if( poGeom->IsEmpty() ) + { + OGRGeometry* poRet = createGeometry(eTargetType); + if( poRet ) + poRet->assignSpatialReference(poGeom->getSpatialReference()); + delete poGeom; + return poRet; + } + + /* Promote single to multi */ + if( !OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) && + OGR_GT_IsSubClassOf(OGR_GT_GetCollection(eType), eTargetType) ) + { + OGRGeometryCollection* poRet = (OGRGeometryCollection*)createGeometry(eTargetType); + if( poRet ) + { + poRet->assignSpatialReference(poGeom->getSpatialReference()); + if( eType == wkbLineString ) + poGeom = OGRCurve::CastToLineString( (OGRCurve*)poGeom ); + poRet->addGeometryDirectly(poGeom); + } + else + delete poGeom; + return poRet; + } + + int bIsCurve = OGR_GT_IsCurve(eType); + if( bIsCurve && eTargetType == wkbCompoundCurve ) + { + return OGRCurve::CastToCompoundCurve((OGRCurve*)poGeom); + } + else if( bIsCurve && eTargetType == wkbCurvePolygon ) + { + OGRCurve* poCurve = (OGRCurve*)poGeom; + if( poCurve->getNumPoints() >= 3 && poCurve->get_IsClosed() ) + { + OGRCurvePolygon* poCP = new OGRCurvePolygon(); + if( poCP->addRingDirectly( poCurve ) == OGRERR_NONE ) + { + poCP->assignSpatialReference(poGeom->getSpatialReference()); + return poCP; + } + else + { + delete poCP; + } + } + } + else if( eType == wkbLineString && + OGR_GT_IsSubClassOf(eTargetType, wkbMultiSurface) ) + { + OGRGeometry* poTmp = forceTo(poGeom, wkbPolygon, papszOptions); + if( wkbFlatten(poTmp->getGeometryType()) != eType) + return forceTo(poTmp, eTargetType, papszOptions); + } + else if( bIsCurve && eTargetType == wkbMultiSurface ) + { + OGRGeometry* poTmp = forceTo(poGeom, wkbCurvePolygon, papszOptions); + if( wkbFlatten(poTmp->getGeometryType()) != eType) + return forceTo(poTmp, eTargetType, papszOptions); + } + else if( bIsCurve && eTargetType == wkbMultiPolygon ) + { + OGRGeometry* poTmp = forceTo(poGeom, wkbPolygon, papszOptions); + if( wkbFlatten(poTmp->getGeometryType()) != eType) + return forceTo(poTmp, eTargetType, papszOptions); + } + else if (eType == wkbPolygon && eTargetType == wkbCurvePolygon) + { + return OGRSurface::CastToCurvePolygon((OGRPolygon*)poGeom); + } + else if( OGR_GT_IsSubClassOf(eType, wkbCurvePolygon) && + eTargetType == wkbCompoundCurve ) + { + OGRCurvePolygon* poPoly = (OGRCurvePolygon*)poGeom; + if( poPoly->getNumInteriorRings() == 0 ) + { + OGRCurve* poRet = poPoly->stealExteriorRingCurve(); + if( poRet ) + poRet->assignSpatialReference(poGeom->getSpatialReference()); + delete poPoly; + return forceTo(poRet, eTargetType, papszOptions); + } + } + else if ( eType == wkbMultiPolygon && eTargetType == wkbMultiSurface ) + { + return OGRMultiPolygon::CastToMultiSurface((OGRMultiPolygon*)poGeom); + } + else if ( eType == wkbMultiLineString && eTargetType == wkbMultiCurve ) + { + return OGRMultiLineString::CastToMultiCurve((OGRMultiLineString*)poGeom); + } + else if ( OGR_GT_IsSubClassOf(eType, wkbGeometryCollection) ) + { + OGRGeometryCollection* poGC = (OGRGeometryCollection*)poGeom; + if( poGC->getNumGeometries() == 1 ) + { + OGRGeometry* poSubGeom = poGC->getGeometryRef(0); + if( poSubGeom ) + poSubGeom->assignSpatialReference(poGeom->getSpatialReference()); + poGC->removeGeometry(0, FALSE); + OGRGeometry* poRet = forceTo(poSubGeom, eTargetType, papszOptions); + if( OGR_GT_IsSubClassOf(wkbFlatten(poRet->getGeometryType()), eTargetType) ) + { + delete poGC; + return poRet; + } + poGC->addGeometryDirectly(poSubGeom); + } + } + else if( OGR_GT_IsSubClassOf(eType, wkbCurvePolygon) && + (OGR_GT_IsSubClassOf(eTargetType, wkbMultiSurface) || + OGR_GT_IsSubClassOf(eTargetType, wkbMultiCurve)) ) + { + OGRCurvePolygon* poCP = (OGRCurvePolygon*)poGeom; + if( poCP->getNumInteriorRings() == 0 ) + { + OGRCurve* poRing = poCP->getExteriorRingCurve(); + poRing->assignSpatialReference(poGeom->getSpatialReference()); + OGRwkbGeometryType eRingType = poRing->getGeometryType(); + OGRGeometry* poRingDup = poRing->clone(); + OGRGeometry* poRet = forceTo(poRingDup, eTargetType, papszOptions); + if( poRet->getGeometryType() != eRingType ) + { + delete poCP; + return poRet; + } + else + delete poRet; + } + } + + if( eTargetType == wkbLineString ) + { + poGeom = forceToLineString(poGeom); + } + else if( eTargetType == wkbPolygon ) + { + poGeom = forceToPolygon(poGeom); + } + else if( eTargetType == wkbMultiPolygon ) + { + poGeom = forceToMultiPolygon(poGeom); + } + else if( eTargetType == wkbMultiLineString ) + { + poGeom = forceToMultiLineString(poGeom); + } + else if( eTargetType == wkbMultiPoint ) + { + poGeom = forceToMultiPoint(poGeom); + } + + return poGeom; +} + + +/************************************************************************/ +/* OGR_G_ForceTo() */ +/************************************************************************/ + +/** + * \brief Convert to another geometry type + * + * This function is the same as the C++ method OGRGeometryFactory::forceTo(). + * + * @param hGeom the input geometry - ownership is passed to the method. + * @param eTargetType target output geometry type. + * @param papszOptions options as a null-terminated list of strings or NULL. + * @return new geometry. + * + * @since GDAL 2.0 + */ + +OGRGeometryH OGR_G_ForceTo( OGRGeometryH hGeom, + OGRwkbGeometryType eTargetType, + char** papszOptions ) + +{ + return (OGRGeometryH) + OGRGeometryFactory::forceTo( (OGRGeometry *) hGeom, eTargetType, + (const char* const*)papszOptions ); +} + + +/************************************************************************/ +/* GetCurveParmeters() */ +/************************************************************************/ + +/** + * \brief Returns the parameter of an arc circle. + * + * @param x0 x of first point + * @param y0 y of first point + * @param z0 z of first point + * @param x1 x of intermediate point + * @param y1 y of intermediate point + * @param z1 z of intermediate point + * @param x2 x of final point + * @param y2 y of final point + * @param z2 z of final point + * @param R radius (output) + * @param cx x of arc center (output) + * @param cx y of arc center (output) + * @param alpha0 angle between center and first point (output) + * @param alpha1 angle between center and intermediate point (output) + * @param alpha2 angle between center and final point (output) + * @return TRUE if the points are not aligned and define an arc circle. + * + * @since GDAL 2.0 + */ + +#define DISTANCE(x1,y1,x2,y2) sqrt(((x2)-(x1))*((x2)-(x1))+((y2)-(y1))*((y2)-(y1))) + +int OGRGeometryFactory::GetCurveParmeters( + double x0, double y0, double x1, double y1, double x2, double y2, + double& R, double& cx, double& cy, double& alpha0, double& alpha1, double& alpha2 ) +{ + /* Circle */ + if( x0 == x2 && y0 == y2 && (x0 != x1 || y0 != y1) ) + { + cx = (x0 + x1) / 2; + cy = (y0 + y1) / 2; + R = DISTANCE(cx,cy,x0,y0); + /* Arbitrarily pick counter-clock-wise order (like PostGIS does) */ + alpha0 = atan2(y0 - cy, x0 - cx); + alpha1 = alpha0 + M_PI; + alpha2 = alpha0 + 2 * M_PI; + return TRUE; + } + + double dx01 = x1 - x0; + double dy01 = y1 - y0; + double dx12 = x2 - x1; + double dy12 = y2 - y1; + + /* Normalize above values so as to make sure we don't end up with */ + /* computing a difference of too big values */ + double dfScale = fabs(dx01); + if( fabs(dy01) > dfScale ) dfScale = fabs(dy01); + if( fabs(dx12) > dfScale ) dfScale = fabs(dx12); + if( fabs(dy12) > dfScale ) dfScale = fabs(dy12); + double dfInvScale = 1.0 / dfScale; + dx01 *= dfInvScale; + dy01 *= dfInvScale; + dx12 *= dfInvScale; + dy12 *= dfInvScale; + + double det = dx01 * dy12 - dx12 * dy01; + if( fabs(det) < 1e-8 ) + { + return FALSE; + } + double x01_mid = (x0 + x1) * dfInvScale; + double x12_mid = (x1 + x2) * dfInvScale; + double y01_mid = (y0 + y1) * dfInvScale; + double y12_mid = (y1 + y2) * dfInvScale; + double c01 = dx01 * x01_mid + dy01 * y01_mid; + double c12 = dx12 * x12_mid + dy12 * y12_mid; + cx = 0.5 * dfScale * (c01 * dy12 - c12 * dy01) / det; + cy = 0.5 * dfScale * (- c01 * dx12 + c12 * dx01) / det; + + alpha0 = atan2((y0 - cy) * dfInvScale, (x0 - cx) * dfInvScale); + alpha1 = atan2((y1 - cy) * dfInvScale, (x1 - cx) * dfInvScale); + alpha2 = atan2((y2 - cy) * dfInvScale, (x2 - cx) * dfInvScale); + R = DISTANCE(cx,cy,x0,y0); + + /* if det is negative, the orientation if clockwise */ + if (det < 0) + { + if (alpha1 > alpha0) + alpha1 -= 2 * M_PI; + if (alpha2 > alpha1) + alpha2 -= 2 * M_PI; + } + else + { + if (alpha1 < alpha0) + alpha1 += 2 * M_PI; + if (alpha2 < alpha1) + alpha2 += 2 * M_PI; + } + + CPLAssert((alpha0 <= alpha1 && alpha1 <= alpha2) || + (alpha0 >= alpha1 && alpha1 >= alpha2)); + + return TRUE; +} + + +/************************************************************************/ +/* OGRGeometryFactoryStrokeArc() */ +/************************************************************************/ + +//#define ROUND_ANGLE_METHOD + +static void OGRGeometryFactoryStrokeArc(OGRLineString* poLine, + double cx, double cy, double R, + double z0, double z1, int bHasZ, + double alpha0, double alpha1, + double dfStep, + int bStealthConstraints) +{ + double alpha; + + int nSign = (dfStep > 0) ? 1 : -1; + +#ifdef ROUND_ANGLE_METHOD + /* Initial approach: no longer used */ + /* Discretize on angles that are multiple of dfStep so as to not */ + /* depend on winding order. */ + if (dfStep > 0 ) + { + alpha = floor(alpha0 / dfStep) * dfStep; + if( alpha <= alpha0 ) + alpha += dfStep; + } + else + { + alpha = ceil(alpha0 / dfStep) * dfStep; + if( alpha >= alpha0 ) + alpha += dfStep; + } +#else + /* Constant angle between all points, so as to not depend on winding order */ + int nSteps = (int)(fabs((alpha1 - alpha0) / dfStep)+0.5); + if( bStealthConstraints ) + { + /* We need at least 6 intermediate vertex, and if more additional */ + /* multiples of 2 */ + if( nSteps < 1+6 ) + nSteps = 1+6; + else + nSteps = 1+6 + 2 * ((nSteps - (1+6) + (2-1)) / 2); + } + else if( nSteps < 4 ) + nSteps = 4; + dfStep = nSign * fabs((alpha1 - alpha0) / nSteps); + alpha = alpha0 + dfStep; +#endif + + for(; (alpha - alpha1) * nSign < -1e-8; alpha += dfStep) + { + double dfX = cx + R * cos(alpha), dfY = cy + R * sin(alpha); + if( bHasZ ) + { + double z = z0 + (z1 - z0) * (alpha - alpha0) / (alpha1 - alpha0); + poLine->addPoint(dfX, dfY, z); + } + else + poLine->addPoint(dfX, dfY); + } +} + +/************************************************************************/ +/* OGRGF_SetHiddenValue() */ +/************************************************************************/ + +#define HIDDEN_ALPHA_WIDTH 32 +#define HIDDEN_ALPHA_SCALE (GUInt32)((((GUIntBig)1) << HIDDEN_ALPHA_WIDTH)-2) +#define HIDDEN_ALPHA_HALF_WIDTH (HIDDEN_ALPHA_WIDTH / 2) +#define HIDDEN_ALPHA_HALF_MASK ((1 << HIDDEN_ALPHA_HALF_WIDTH)-1) + +/* Encode 16-bit nValue in the 8-lsb of dfX and dfY */ + +#ifdef CPL_LSB +#define DOUBLE_LSB_OFFSET 0 +#else +#define DOUBLE_LSB_OFFSET 7 +#endif + +static void OGRGF_SetHiddenValue(GUInt16 nValue, double& dfX, double &dfY) +{ + GByte abyData[8]; + + memcpy(abyData, &dfX, sizeof(double)); + abyData[DOUBLE_LSB_OFFSET] = (GByte)(nValue & 0xFF); + memcpy(&dfX, abyData, sizeof(double)); + + memcpy(abyData, &dfY, sizeof(double)); + abyData[DOUBLE_LSB_OFFSET] = (GByte)(nValue >> 8); + memcpy(&dfY, abyData, sizeof(double)); +} + +/************************************************************************/ +/* OGRGF_GetHiddenValue() */ +/************************************************************************/ + +/* Decode 16-bit nValue from the 8-lsb of dfX and dfY */ +static GUInt16 OGRGF_GetHiddenValue(double dfX, double dfY) +{ + GUInt16 nValue; + + GByte abyData[8]; + memcpy(abyData, &dfX, sizeof(double)); + nValue = abyData[DOUBLE_LSB_OFFSET]; + memcpy(abyData, &dfY, sizeof(double)); + nValue |= (abyData[DOUBLE_LSB_OFFSET] << 8); + + return nValue; +} + +/************************************************************************/ +/* OGRGF_NeedSwithArcOrder() */ +/************************************************************************/ + +/* We need to define a full ordering between starting point and ending point */ +/* whatever it is */ +static int OGRGF_NeedSwithArcOrder(double x0, double y0, + double x2, double y2) +{ + return ( x0 < x2 || (x0 == x2 && y0 < y2) ); +} + +/************************************************************************/ +/* curveToLineString() */ +/************************************************************************/ + +/** + * \brief Converts an arc circle into an approximate line string + * + * The arc circle is defined by a first point, an intermediate point and a + * final point. + * + * The provided dfMaxAngleStepSizeDegrees is a hint. The discretization + * algorithm may pick a slightly different value. + * + * So as to avoid gaps when rendering curve polygons that share common arcs, + * this method is guaranteed to return a line with reversed vertex if called + * with inverted first and final point, and identical intermediate point. + * + * @param x0 x of first point + * @param y0 y of first point + * @param z0 z of first point + * @param x1 x of intermediate point + * @param y1 y of intermediate point + * @param z1 z of intermediate point + * @param x2 x of final point + * @param y2 y of final point + * @param z2 z of final point + * @param bHasZ TRUE if z must be taken into account + * @param dfMaxAngleStepSizeDegrees the largest step in degrees along the + * arc, zero to use the default setting. + * @param papszOptions options as a null-terminated list of strings or NULL. + * Recognized options: + *
      + *
    • ADD_INTERMEDIATE_POINT=STEALTH/YES/NO (Default to STEALTH). + * Determine if and how the intermediate point must be output in the linestring. + * If set to STEALTH, no explicit intermediate point is added but its + * properties are encoded in low significant bits of intermediate points + * and OGRGeometryFactory::curveFromLineString() can decode them. + * This is the best compromise for round-tripping in OGR and better results + * with PostGIS ST_LineToCurve() + * If set to YES, the intermediate point is explicitly added to the linestring. + * If set to NO, the intermediate point is not explicitly added. + *
    • + *
    + * + * @return the converted geometry (ownership to caller). + * + * @since GDAL 2.0 + */ + +OGRLineString* OGRGeometryFactory::curveToLineString( + double x0, double y0, double z0, + double x1, double y1, double z1, + double x2, double y2, double z2, + int bHasZ, + double dfMaxAngleStepSizeDegrees, + const char*const* papszOptions ) +{ + double R, cx, cy, alpha0, alpha1, alpha2; + + /* So as to make sure the same curve followed in both direction results */ + /* in perfectly(=binary identical) symetrical points */ + if( OGRGF_NeedSwithArcOrder(x0,y0,x2,y2) ) + { + OGRLineString* poLS = curveToLineString(x2,y2,z2,x1,y1,z1,x0,y0,z0, + bHasZ, dfMaxAngleStepSizeDegrees, + papszOptions); + poLS->reversePoints(); + return poLS; + } + + OGRLineString* poLine = new OGRLineString(); + int bIsArc = TRUE; + if( !GetCurveParmeters(x0, y0, x1, y1, x2, y2, + R, cx, cy, alpha0, alpha1, alpha2)) + { + bIsArc = FALSE; + cx = cy = R = alpha0 = alpha1 = alpha2 = 0.0; + } + + int nSign = (alpha1 >= alpha0) ? 1 : -1; + + // support default arc step setting. + if( dfMaxAngleStepSizeDegrees < 1e-6 ) + { + dfMaxAngleStepSizeDegrees = OGRGF_GetDefaultStepSize(); + } + + double dfStep = + dfMaxAngleStepSizeDegrees / 180 * M_PI; + if (dfStep <= 0.01 / 180 * M_PI) + { + CPLDebug("OGR", "Too small arc step size: limiting to 0.01 degree."); + dfStep = 0.01 / 180 * M_PI; + } + + dfStep *= nSign; + + if( bHasZ ) + poLine->addPoint(x0, y0, z0); + else + poLine->addPoint(x0, y0); + + int bAddIntermediatePoint = FALSE; + int bStealth = TRUE; + for(const char* const* papszIter = papszOptions; papszIter && *papszIter; papszIter++) + { + char* pszKey = NULL; + const char* pszValue = CPLParseNameValue(*papszIter, &pszKey); + if( pszKey != NULL && EQUAL(pszKey, "ADD_INTERMEDIATE_POINT") ) + { + if( EQUAL(pszValue, "YES") || EQUAL(pszValue, "TRUE") || EQUAL(pszValue, "ON") ) + { + bAddIntermediatePoint = TRUE; + bStealth = FALSE; + } + else if( EQUAL(pszValue, "NO") || EQUAL(pszValue, "FALSE") || EQUAL(pszValue, "OFF") ) + { + bAddIntermediatePoint = FALSE; + bStealth = FALSE; + } + else if( EQUAL(pszValue, "STEALTH") ) + { + /* default */ + } + } + else + { + CPLError(CE_Warning, CPLE_NotSupported, "Unsupported option: %s", + *papszIter); + } + CPLFree(pszKey); + } + + if( !bIsArc || bAddIntermediatePoint ) + { + OGRGeometryFactoryStrokeArc(poLine, cx, cy, R, + z0, z1, bHasZ, + alpha0, alpha1, dfStep, + FALSE); + + if( bHasZ ) + poLine->addPoint(x1, y1, z1); + else + poLine->addPoint(x1, y1); + + OGRGeometryFactoryStrokeArc(poLine, cx, cy, R, + z1, z2, bHasZ, + alpha1, alpha2, dfStep, + FALSE); + } + else + { + OGRGeometryFactoryStrokeArc(poLine, cx, cy, R, + z0, z2, bHasZ, + alpha0, alpha2, dfStep, + bStealth); + + if( bStealth ) + { + /* 'Hide' the angle of the intermediate point in the 8 low-significant */ + /* bits of the x,y of the first 2 computed points (so 32 bits), */ + /* then put 0xFF, and on the last couple points put again the */ + /* angle but in reverse order, so that overall the low-significant bits */ + /* of all the points are symetrical w.r.t the mid-point */ + double dfRatio = (alpha1 - alpha0) / (alpha2 - alpha0); + GUInt32 nAlphaRatio = (GUInt32)(0.5 + HIDDEN_ALPHA_SCALE * dfRatio); + GUInt16 nAlphaRatioLow = nAlphaRatio & HIDDEN_ALPHA_HALF_MASK; + GUInt16 nAlphaRatioHigh = nAlphaRatio >> HIDDEN_ALPHA_HALF_WIDTH; + /*printf("alpha0=%f, alpha1=%f, alpha2=%f, dfRatio=%f, nAlphaRatio = %u\n", + alpha0, alpha1, alpha2, dfRatio, nAlphaRatio);*/ + + CPLAssert( ((poLine->getNumPoints()-1 - 6) % 2) == 0 ); + + for(int i=1;i+1getNumPoints();i+=2) + { + double dfX, dfY; + GUInt16 nVal = 0xFFFF; + + dfX = poLine->getX(i); + dfY = poLine->getY(i); + if( i == 1 ) + nVal = nAlphaRatioLow; + else if( i == poLine->getNumPoints() - 2 ) + nVal = nAlphaRatioHigh; + OGRGF_SetHiddenValue(nVal, dfX, dfY); + poLine->setPoint(i, dfX, dfY); + + dfX = poLine->getX(i+1); + dfY = poLine->getY(i+1); + if( i == 1 ) + nVal = nAlphaRatioHigh; + else if( i == poLine->getNumPoints() - 2 ) + nVal = nAlphaRatioLow; + OGRGF_SetHiddenValue(nVal, dfX, dfY); + poLine->setPoint(i+1, dfX, dfY); + } + } + } + + if( bHasZ ) + poLine->addPoint(x2, y2, z2); + else + poLine->addPoint(x2, y2); + + return poLine; +} + +/************************************************************************/ +/* OGRGF_FixAngle() */ +/************************************************************************/ + +/* Fix dfAngle by offsets of 2 PI so that it lies between dfAngleStart and */ +/* dfAngleStop, whatever their respective order. */ +static double OGRGF_FixAngle(double dfAngleStart, double dfAngleStop, double dfAngle) +{ + if( dfAngleStart < dfAngleStop ) + { + while( dfAngle <= dfAngleStart + 1e-8 ) + dfAngle += 2 * M_PI; + } + else + { + while( dfAngle >= dfAngleStart - 1e-8 ) + dfAngle -= 2 * M_PI; + } + return dfAngle; +} + +/************************************************************************/ +/* OGRGF_DetectArc() */ +/************************************************************************/ + +//#define VERBOSE_DEBUG_CURVEFROMLINESTRING + +static int OGRGF_DetectArc(const OGRLineString* poLS, int i, + OGRCompoundCurve*& poCC, + OGRCircularString*& poCS, + OGRLineString*& poLSNew) +{ + OGRPoint p0, p1, p2, p3; + if( i + 3 >= poLS->getNumPoints() ) + return -1; + + poLS->getPoint(i, &p0); + poLS->getPoint(i+1, &p1); + poLS->getPoint(i+2, &p2); + double R_1, cx_1, cy_1, alpha0_1, alpha1_1, alpha2_1; + if( !(OGRGeometryFactory::GetCurveParmeters(p0.getX(), p0.getY(), + p1.getX(), p1.getY(), + p2.getX(), p2.getY(), + R_1, cx_1, cy_1, + alpha0_1, alpha1_1, alpha2_1) && + fabs(alpha2_1 - alpha0_1) < 2 * 20.0 / 180.0 * M_PI) ) + { + return -1; + } + + int j; + double dfDeltaAlpha10 = alpha1_1 - alpha0_1; + double dfDeltaAlpha21 = alpha2_1 - alpha1_1; + double dfMaxDeltaAlpha = MAX(fabs(dfDeltaAlpha10), + fabs(dfDeltaAlpha21)); + GUInt32 nAlphaRatioRef = + OGRGF_GetHiddenValue(p1.getX(), p1.getY()) | + (OGRGF_GetHiddenValue(p2.getX(), p2.getY()) << HIDDEN_ALPHA_HALF_WIDTH); + int bFoundFFFFFFFFPattern = FALSE; + int bFoundReversedAlphaRatioRef = FALSE; + int bValidAlphaRatio = (nAlphaRatioRef > 0 && nAlphaRatioRef < 0xFFFFFFFF); + int nCountValidAlphaRatio = 1; + + double dfScale = MAX(1, R_1); + dfScale = MAX(dfScale, fabs(cx_1)); + dfScale = MAX(dfScale, fabs(cy_1)); + dfScale = pow(10.0, ceil(log10(dfScale))); + double dfInvScale = 1.0 / dfScale ; + + int bInitialConstantStep = + (fabs(dfDeltaAlpha10 - dfDeltaAlpha21) / dfMaxDeltaAlpha) < 1e-4; + double dfDeltaEpsilon = ( bInitialConstantStep ) ? + dfMaxDeltaAlpha * 1e-4 : dfMaxDeltaAlpha/10; +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("----------------------------\n"); + printf("Curve beginning at offset i = %d\n", i); + printf("Initial alpha ratio = %u\n", nAlphaRatioRef); + printf("Initial R = %.16g, cx = %.16g, cy = %.16g\n", R_1, cx_1, cy_1); + printf("dfScale = %f\n", dfScale); + printf("bInitialConstantStep = %d, " + "fabs(dfDeltaAlpha10 - dfDeltaAlpha21)=%.8g, " + "dfMaxDeltaAlpha = %.8f, " + "dfDeltaEpsilon = %.8f (%.8f)\n", + bInitialConstantStep, + fabs(dfDeltaAlpha10 - dfDeltaAlpha21), + dfMaxDeltaAlpha, + dfDeltaEpsilon, 1.0 / 180.0 * M_PI); +#endif + int iMidPoint = -1; + double dfLastValidAlpha = alpha2_1; + + double dfLastLogRelDiff = 0; + + for(j = i + 1; j + 2 < poLS->getNumPoints(); j++ ) + { + poLS->getPoint(j, &p1); + poLS->getPoint(j+1, &p2); + poLS->getPoint(j+2, &p3); + double R_2, cx_2, cy_2, alpha0_2, alpha1_2, alpha2_2; + /* Check that the new candidate arc shares the same */ + /* radius, center and winding order */ + if( !(OGRGeometryFactory::GetCurveParmeters(p1.getX(), p1.getY(), + p2.getX(), p2.getY(), + p3.getX(), p3.getY(), + R_2, cx_2, cy_2, + alpha0_2, alpha1_2, alpha2_2)) ) + { +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("End of curve at j=%d\n : straight line", j); +#endif + break; + } + + double dfRelDiffR = fabs(R_1 - R_2) * dfInvScale; + double dfRelDiffCx = fabs(cx_1 - cx_2) * dfInvScale; + double dfRelDiffCy = fabs(cy_1 - cy_2) * dfInvScale; +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("j=%d: R = %.16g, cx = %.16g, cy = %.16g, " + "rel_diff_R=%.8g rel_diff_cx=%.8g rel_diff_cy=%.8g\n", + j, R_2, cx_2, cy_2, dfRelDiffR, dfRelDiffCx, dfRelDiffCy); +#endif + + if( //(dfRelDiffR > 1e-8 || dfRelDiffCx > 1e-8 || dfRelDiffCy > 1e-8) || + (dfRelDiffR > 1e-6 && dfRelDiffCx > 1e-6 && dfRelDiffCy > 1e-6) || + dfDeltaAlpha10 * (alpha1_2 - alpha0_2) < 0 ) + { +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("End of curve at j=%d\n", j); +#endif + break; + } + + if( dfRelDiffR > 0 && dfRelDiffCx > 0 && dfRelDiffCy > 0 ) + { + double dfLogRelDiff = MIN(MIN(fabs(log10(dfRelDiffR)), + fabs(log10(dfRelDiffCx))), + fabs(log10(dfRelDiffCy))); + /*printf("dfLogRelDiff = %f, dfLastLogRelDiff=%f, " + "dfLogRelDiff - dfLastLogRelDiff=%f\n", + dfLogRelDiff, dfLastLogRelDiff, + dfLogRelDiff - dfLastLogRelDiff);*/ + if( dfLogRelDiff > 0 && dfLastLogRelDiff > 0 && + dfLastLogRelDiff >= 8 && dfLogRelDiff <= 8 && + dfLogRelDiff < dfLastLogRelDiff - 2 ) + { +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("End of curve at j=%d. Significant different in " + "relative error w.r.t previous points\n", j); +#endif + break; + } + dfLastLogRelDiff = dfLogRelDiff; + } + + double dfStep10 = fabs(alpha1_2 - alpha0_2); + double dfStep21 = fabs(alpha2_2 - alpha1_2); + /* Check that the angle step is consistent with the original */ + /* step. */ + if( !(dfStep10 < 2 * dfMaxDeltaAlpha && dfStep21 < 2 * dfMaxDeltaAlpha) ) + { +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("End of curve at j=%d: dfStep10=%f, dfStep21=%f, 2*dfMaxDeltaAlpha=%f\n", + j, dfStep10, dfStep21, 2 * dfMaxDeltaAlpha); +#endif + break; + } + + if( bValidAlphaRatio && j > i + 1 && (i % 2) != (j % 2 ) ) + { + GUInt32 nAlphaRatioReversed = + (OGRGF_GetHiddenValue(p1.getX(), p1.getY()) << HIDDEN_ALPHA_HALF_WIDTH) | + (OGRGF_GetHiddenValue(p2.getX(), p2.getY())); +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("j=%d, nAlphaRatioReversed = %u\n", + j, nAlphaRatioReversed); +#endif + if( !bFoundFFFFFFFFPattern && nAlphaRatioReversed == 0xFFFFFFFF ) + { + bFoundFFFFFFFFPattern = TRUE; + nCountValidAlphaRatio ++; + } + else if( bFoundFFFFFFFFPattern && !bFoundReversedAlphaRatioRef && + nAlphaRatioReversed == 0xFFFFFFFF ) + { + nCountValidAlphaRatio ++; + } + else if( bFoundFFFFFFFFPattern && !bFoundReversedAlphaRatioRef && + nAlphaRatioReversed == nAlphaRatioRef ) + { + bFoundReversedAlphaRatioRef = TRUE; + nCountValidAlphaRatio ++; + } + else + { + if( bInitialConstantStep && + fabs(dfLastValidAlpha - alpha0_1) >= M_PI && + nCountValidAlphaRatio > 10 ) + { +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("End of curve at j=%d: " + "fabs(dfLastValidAlpha - alpha0_1)=%f, " + "nCountValidAlphaRatio=%d\n", + j, + fabs(dfLastValidAlpha - alpha0_1), + nCountValidAlphaRatio); +#endif + if( dfLastValidAlpha - alpha0_1 > 0 ) + { + while( dfLastValidAlpha - alpha0_1 - dfMaxDeltaAlpha - M_PI > -dfMaxDeltaAlpha/10 ) + { + dfLastValidAlpha -= dfMaxDeltaAlpha; + j --; +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("--> corrected as fabs(dfLastValidAlpha - alpha0_1)=%f, j=%d\n", + fabs(dfLastValidAlpha - alpha0_1), j); +#endif + } + } + else + { + while( dfLastValidAlpha - alpha0_1 + dfMaxDeltaAlpha + M_PI < dfMaxDeltaAlpha/10 ) + { + dfLastValidAlpha += dfMaxDeltaAlpha; + j --; +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("--> corrected as fabs(dfLastValidAlpha - alpha0_1)=%f, j=%d\n", + fabs(dfLastValidAlpha - alpha0_1), j); +#endif + } + } + poLS->getPoint(j+1, &p2); + break; + } + +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("j=%d, nAlphaRatioReversed = %u --> unconsistent values accross arc. Don't use it\n", + j, nAlphaRatioReversed); +#endif + bValidAlphaRatio = FALSE; + } + } + + /* Correct current end angle, consistently with start angle */ + dfLastValidAlpha = OGRGF_FixAngle(alpha0_1, alpha1_1, alpha2_2); + + /* Try to detect the precise intermediate point of the */ + /* arc circle by detecting irregular angle step */ + /* This is OK if we don't detect the right point or fail */ + /* to detect it */ +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("j=%d A(0,1)-maxDelta=%.8f A(1,2)-maxDelta=%.8f " + "x1=%.8f y1=%.8f x2=%.8f y2=%.8f x3=%.8f y3=%.8f\n", + j, fabs(dfStep10 - dfMaxDeltaAlpha), fabs(dfStep21 - dfMaxDeltaAlpha), + p1.getX(), p1.getY(), p2.getX(), p2.getY(), p3.getX(), p3.getY()); +#endif + if( j > i + 1 && iMidPoint < 0 && dfDeltaEpsilon < 1.0 / 180.0 * M_PI ) + { + if( fabs(dfStep10 - dfMaxDeltaAlpha) > dfDeltaEpsilon ) + iMidPoint = j + ((bInitialConstantStep) ? 0 : 1); + else if( fabs(dfStep21 - dfMaxDeltaAlpha) > dfDeltaEpsilon ) + iMidPoint = j + ((bInitialConstantStep) ? 1 : 2); +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + if( iMidPoint >= 0 ) + { + OGRPoint pMid; + poLS->getPoint(iMidPoint, &pMid); + printf("Midpoint detected at j = %d, iMidPoint = %d, x=%.8f y=%.8f\n", + j, iMidPoint, pMid.getX(), pMid.getY()); + } +#endif + } + } + + /* Take a minimum threshold of consecutive points */ + /* on the arc to avoid false positives */ + if( j < i + 3 ) + return -1; + + bValidAlphaRatio &= (bFoundFFFFFFFFPattern && bFoundReversedAlphaRatioRef ); + +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("bValidAlphaRatio=%d bFoundFFFFFFFFPattern=%d, bFoundReversedAlphaRatioRef=%d\n", + bValidAlphaRatio, bFoundFFFFFFFFPattern, bFoundReversedAlphaRatioRef); + printf("alpha0_1=%f dfLastValidAlpha=%f\n", + alpha0_1, dfLastValidAlpha); +#endif + + if( poLSNew != NULL ) + { + double dfScale2 = MAX(1, fabs(p0.getX())); + dfScale2 = MAX(dfScale, fabs(p0.getY())); + /* Not strictly necessary, but helps having 'clean' lines without duplicated points */ + if( fabs(poLSNew->getX(poLSNew->getNumPoints()-1) - p0.getX()) / dfScale2 > 1e-8 || + fabs(poLSNew->getY(poLSNew->getNumPoints()-1) - p0.getY()) / dfScale2 > 1e-8 ) + poLSNew->addPoint(&p0); + if( poLSNew->getNumPoints() >= 2 ) + { + if( poCC == NULL ) + poCC = new OGRCompoundCurve(); + poCC->addCurveDirectly(poLSNew); + } + else + delete poLSNew; + poLSNew = NULL; + } + + if( poCS == NULL ) + { + poCS = new OGRCircularString(); + poCS->addPoint(&p0); + } + + OGRPoint* poFinalPoint = + ( j + 2 >= poLS->getNumPoints() ) ? &p3 : &p2; + + double dfXMid = 0.0, dfYMid = 0.0, dfZMid = 0.0; + if( bValidAlphaRatio ) + { +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("Using alpha ratio...\n"); +#endif + double dfAlphaMid; + if( OGRGF_NeedSwithArcOrder(p0.getX(),p0.getY(), + poFinalPoint->getX(), + poFinalPoint->getY()) ) + { +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("Switching angles\n"); +#endif + dfAlphaMid = dfLastValidAlpha + nAlphaRatioRef * + (alpha0_1 - dfLastValidAlpha) / HIDDEN_ALPHA_SCALE; + dfAlphaMid = OGRGF_FixAngle(alpha0_1, dfLastValidAlpha, dfAlphaMid); + } + else + { + dfAlphaMid = alpha0_1 + nAlphaRatioRef * + (dfLastValidAlpha - alpha0_1) / HIDDEN_ALPHA_SCALE; + } + + dfXMid = cx_1 + R_1 * cos(dfAlphaMid); + dfYMid = cy_1 + R_1 * sin(dfAlphaMid); + +#define IS_ALMOST_INTEGER(x) ((fabs((x)-floor((x)+0.5)))<1e-8) + + if( poLS->getCoordinateDimension() == 3 ) + { + double dfLastAlpha = 0.0; + double dfLastZ = 0.0; + int k; + for( k = i; k < j+2; k++ ) + { + OGRPoint p; + poLS->getPoint(k, &p); + double dfAlpha = atan2(p.getY() - cy_1, p.getX() - cx_1); + dfAlpha = OGRGF_FixAngle(alpha0_1, dfLastValidAlpha, dfAlpha); + if( k > i && ((dfAlpha < dfLastValidAlpha && dfAlphaMid < dfAlpha) || + (dfAlpha > dfLastValidAlpha && dfAlphaMid > dfAlpha)) ) + { + double dfRatio = ( dfAlphaMid - dfLastAlpha ) / ( dfAlpha - dfLastAlpha ); + dfZMid = (1 - dfRatio) * dfLastZ + dfRatio * p.getZ(); + break; + } + dfLastAlpha = dfAlpha; + dfLastZ = p.getZ(); + } + if( k == j + 2 ) + dfZMid = dfLastZ; + if( IS_ALMOST_INTEGER(dfZMid) ) + dfZMid = (int)floor(dfZMid+0.5); + } + + /* A few rounding strategies in case the mid point was at "exact" coordinates */ + if( R_1 > 1e-5 ) + { + int bStartEndInteger = ( IS_ALMOST_INTEGER(p0.getX()) && + IS_ALMOST_INTEGER(p0.getY()) && + IS_ALMOST_INTEGER(poFinalPoint->getX()) && + IS_ALMOST_INTEGER(poFinalPoint->getY()) ); + if( bStartEndInteger && + fabs(dfXMid - floor(dfXMid+0.5)) / dfScale < 1e-4 && + fabs(dfYMid - floor(dfYMid+0.5)) / dfScale < 1e-4 ) + { + dfXMid = (int)floor(dfXMid+0.5); + dfYMid = (int)floor(dfYMid+0.5); + // Sometimes rounding to closest is not best approach + // Try neighbouring integers to look for the one that + // minimize the error w.r.t to the arc center + // But only do that if the radius is greater than + // the magnitude of the delta that we will try ! + double dfBestRError = fabs(R_1 - DISTANCE(dfXMid,dfYMid,cx_1,cy_1)); + int iBestX = 0, iBestY = 0; +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("initial_error=%f\n", dfBestRError); +#endif + if( dfBestRError > 0.001 && R_1 > 2 ) + { + int nSearchRadius = 1; + // Extend the search radius if the arc circle radius + // is much higher than the coordinate values + double dfMaxCoords = MAX(fabs(p0.getX()), fabs(p0.getY())); + dfMaxCoords = MAX(dfMaxCoords, poFinalPoint->getX()); + dfMaxCoords = MAX(dfMaxCoords, poFinalPoint->getY()); + dfMaxCoords = MAX(dfMaxCoords, dfXMid); + dfMaxCoords = MAX(dfMaxCoords, dfYMid); + if( R_1 > dfMaxCoords * 1000 ) + nSearchRadius = 100; + else if( R_1 > dfMaxCoords * 10 ) + nSearchRadius = 10; + for(int iY=-nSearchRadius;iY<=nSearchRadius;iY++) + { + for(int iX=-nSearchRadius;iX<=nSearchRadius;iX ++) + { + double dfCandidateX = dfXMid+iX; + double dfCandidateY = dfYMid+iY; + if( fabs(dfCandidateX - p0.getX()) < 1e-8 && + fabs(dfCandidateY - p0.getY()) < 1e-8 ) + continue; + if( fabs(dfCandidateX - poFinalPoint->getX()) < 1e-8 && + fabs(dfCandidateY - poFinalPoint->getY()) < 1e-8 ) + continue; + double dfRError = fabs(R_1 - DISTANCE(dfCandidateX,dfCandidateY,cx_1,cy_1)); +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("x=%d y=%d error=%f besterror=%f\n", + (int)(dfXMid+iX),(int)(dfYMid+iY),dfRError,dfBestRError); +#endif + if( dfRError < dfBestRError ) + { + iBestX = iX; + iBestY = iY; + dfBestRError = dfRError; + } + } + } + } + dfXMid += iBestX; + dfYMid += iBestY; + } + else + { + /* Limit the number of significant figures in decimal representation */ + if( fabs(dfXMid) < 100000000 ) + { + dfXMid = ((GIntBig)floor(dfXMid * 100000000+0.5)) / 100000000.0; + } + if( fabs(dfYMid) < 100000000 ) + { + dfYMid = ((GIntBig)floor(dfYMid * 100000000+0.5)) / 100000000.0; + } + } + } + +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("dfAlphaMid=%f, x_mid = %f, y_mid = %f\n", dfLastValidAlpha, dfXMid, dfYMid); +#endif + } + + /* If this is a full circle of a non-polygonal zone, we must */ + /* use a 5-point representation to keep the winding order */ + if( p0.Equals(poFinalPoint) && + !EQUAL(poLS->getGeometryName(), "LINEARRING") ) + { +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("Full circle of a non-polygonal zone\n"); +#endif + poLS->getPoint((i + j + 2) / 4, &p1); + poCS->addPoint(&p1); + if( bValidAlphaRatio ) + { + p1.setX( dfXMid ); + p1.setY( dfYMid ); + if( poLS->getCoordinateDimension() == 3 ) + p1.setZ( dfZMid ); + } + else + { + poLS->getPoint((i + j + 1) / 2, &p1); + } + poCS->addPoint(&p1); + poLS->getPoint(3 * (i + j + 2) / 4, &p1); + poCS->addPoint(&p1); + } + + else if( bValidAlphaRatio ) + { + p1.setX( dfXMid ); + p1.setY( dfYMid ); + if( poLS->getCoordinateDimension() == 3 ) + p1.setZ( dfZMid ); + poCS->addPoint(&p1); + } + + /* If we have found a candidate for a precise intermediate */ + /* point, use it */ + else if( iMidPoint >= 1 && iMidPoint < j ) + { + poLS->getPoint(iMidPoint, &p1); + poCS->addPoint(&p1); +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("Using detected midpoint...\n"); + printf("x_mid = %f, y_mid = %f\n", p1.getX(), p1.getY()); +#endif + } + /* Otherwise pick up the mid point between both extremities */ + else + { + poLS->getPoint((i + j + 1) / 2, &p1); + poCS->addPoint(&p1); +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("Pickup 'random' midpoint at index=%d...\n", (i + j + 1) / 2); + printf("x_mid = %f, y_mid = %f\n", p1.getX(), p1.getY()); +#endif + } + poCS->addPoint(poFinalPoint); + +#ifdef VERBOSE_DEBUG_CURVEFROMLINESTRING + printf("----------------------------\n"); +#endif + + if( j + 2 >= poLS->getNumPoints() ) + return -2; + return j + 1; +} + +/************************************************************************/ +/* curveFromLineString() */ +/************************************************************************/ + +/** + * \brief Try to convert a linestring approximating curves into a curve. + * + * This method can return a COMPOUNDCURVE, a CIRCULARSTRING or a LINESTRING. + * + * This method is the reverse of curveFromLineString(). + * + * @param poLS handle to the geometry to convert. + * @param papszOptions options as a null-terminated list of strings. + * Unused for now. Must be set to NULL. + * + * @return the converted geometry (ownership to caller). + * + * @since GDAL 2.0 + */ + +OGRCurve* OGRGeometryFactory::curveFromLineString(const OGRLineString* poLS, + CPL_UNUSED const char*const* papszOptions) +{ + OGRCompoundCurve* poCC = NULL; + OGRCircularString* poCS = NULL; + OGRLineString* poLSNew = NULL; + for(int i=0; i< poLS->getNumPoints(); /* nothing */) + { + int iNewI = OGRGF_DetectArc(poLS, i, poCC, poCS, poLSNew); + if( iNewI == -2 ) + break; + if( iNewI >= 0 ) + { + i = iNewI; + continue; + } + + if( poCS != NULL ) + { + if( poCC == NULL ) + poCC = new OGRCompoundCurve(); + poCC->addCurveDirectly(poCS); + poCS = NULL; + } + + OGRPoint p; + poLS->getPoint(i, &p); + if( poLSNew == NULL ) + { + poLSNew = new OGRLineString(); + poLSNew->addPoint(&p); + } + /* Not strictly necessary, but helps having 'clean' lines without duplicated points */ + else + { + double dfScale = MAX(1, fabs(p.getX())); + dfScale = MAX(dfScale, fabs(p.getY())); + if( fabs(poLSNew->getX(poLSNew->getNumPoints()-1) - p.getX()) / dfScale > 1e-8 || + fabs(poLSNew->getY(poLSNew->getNumPoints()-1) - p.getY()) / dfScale > 1e-8 ) + { + poLSNew->addPoint(&p); + } + } + + i++ ; + } + + OGRCurve* poRet; + + if( poLSNew != NULL && poLSNew->getNumPoints() < 2 ) + { + delete poLSNew; + poLSNew = NULL; + if( poCC != NULL ) + { + if( poCC->getNumCurves() == 1 ) + { + poRet = poCC->stealCurve(0); + delete poCC; + poCC = NULL; + } + else + poRet = poCC; + } + else + poRet = (OGRCurve*)poLS->clone(); + } + else if( poCC != NULL ) + { + poCC->addCurveDirectly(poLSNew ? (OGRCurve*)poLSNew : (OGRCurve*)poCS); + poRet = poCC; + } + else if( poLSNew != NULL ) + poRet = poLSNew; + else if( poCS != NULL ) + poRet = poCS; + else + poRet = (OGRCurve*)poLS->clone(); + + poRet->assignSpatialReference( poLS->getSpatialReference() ); + + return poRet; +} diff --git a/bazaar/plugin/gdal/ogr/ogrgeomfielddefn.cpp b/bazaar/plugin/gdal/ogr/ogrgeomfielddefn.cpp new file mode 100644 index 000000000..167f04a98 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrgeomfielddefn.cpp @@ -0,0 +1,619 @@ +/****************************************************************************** + * $Id: ogrgeomfielddefn.cpp 28806 2015-03-28 14:37:47Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRGeomFieldDefn class implementation. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_feature.h" +#include "ogr_api.h" +#include "ogr_p.h" +#include "ograpispy.h" + +CPL_CVSID("$Id: ogrgeomfielddefn.cpp 28806 2015-03-28 14:37:47Z rouault $"); + +/************************************************************************/ +/* OGRGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Constructor. + * + * @param pszNameIn the name of the new field. + * @param eGeomTypeIn the type of the new field. + * + * @since GDAL 1.11 + */ + +OGRGeomFieldDefn::OGRGeomFieldDefn( const char * pszNameIn, + OGRwkbGeometryType eGeomTypeIn ) + +{ + Initialize( pszNameIn, eGeomTypeIn ); +} + +/************************************************************************/ +/* OGRGeomFieldDefn() */ +/************************************************************************/ + +/** + * \brief Constructor. + * + * Create by cloning an existing geometry field definition. + * + * @param poPrototype the geometry field definition to clone. + * + * @since GDAL 1.11 + */ + +OGRGeomFieldDefn::OGRGeomFieldDefn( OGRGeomFieldDefn *poPrototype ) + +{ + Initialize( poPrototype->GetNameRef(), poPrototype->GetType() ); + SetSpatialRef( poPrototype->GetSpatialRef() ); + SetNullable( poPrototype->IsNullable() ); +} + +/************************************************************************/ +/* OGR_GFld_Create() */ +/************************************************************************/ +/** + * \brief Create a new field geometry definition. + * + * This function is the same as the CPP method OGRGeomFieldDefn::OGRGeomFieldDefn(). + * + * @param pszName the name of the new field definition. + * @param eType the type of the new field definition. + * @return handle to the new field definition. + * + * @since GDAL 1.11 + */ + +OGRGeomFieldDefnH OGR_GFld_Create( const char *pszName, + OGRwkbGeometryType eType ) + +{ + return (OGRGeomFieldDefnH) (new OGRGeomFieldDefn(pszName,eType)); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void OGRGeomFieldDefn::Initialize( const char * pszNameIn, + OGRwkbGeometryType eTypeIn ) + +{ + pszName = CPLStrdup( pszNameIn ); + eGeomType = eTypeIn; + poSRS = NULL; + bIgnore = FALSE; + bNullable = TRUE; +} + +/************************************************************************/ +/* ~OGRGeomFieldDefn() */ +/************************************************************************/ + +OGRGeomFieldDefn::~OGRGeomFieldDefn() + +{ + CPLFree( pszName ); + + if( NULL != poSRS ) + poSRS->Release(); +} + +/************************************************************************/ +/* OGR_GFld_Destroy() */ +/************************************************************************/ +/** + * \brief Destroy a geometry field definition. + * + * @param hDefn handle to the geometry field definition to destroy. + * + * @since GDAL 1.11 + */ + +void OGR_GFld_Destroy( OGRGeomFieldDefnH hDefn ) + +{ + VALIDATE_POINTER0( hDefn, "OGR_GFld_Destroy" ); + delete (OGRGeomFieldDefn *) hDefn; +} + +/************************************************************************/ +/* SetName() */ +/************************************************************************/ + +/** + * \brief Reset the name of this field. + * + * This method is the same as the C function OGR_GFld_SetName(). + * + * @param pszNameIn the new name to apply. + * + * @since GDAL 1.11 + */ + +void OGRGeomFieldDefn::SetName( const char * pszNameIn ) + +{ + CPLFree( pszName ); + pszName = CPLStrdup( pszNameIn ); +} + +/************************************************************************/ +/* OGR_GFld_SetName() */ +/************************************************************************/ +/** + * \brief Reset the name of this field. + * + * This function is the same as the CPP method OGRGeomFieldDefn::SetName(). + * + * @param hDefn handle to the geometry field definition to apply the new name to. + * @param pszName the new name to apply. + * + * @since GDAL 1.11 + */ + +void OGR_GFld_SetName( OGRGeomFieldDefnH hDefn, const char *pszName ) + +{ + VALIDATE_POINTER0( hDefn, "OGR_GFld_SetName" ); + ((OGRGeomFieldDefn *) hDefn)->SetName( pszName ); +} + +/************************************************************************/ +/* GetNameRef() */ +/************************************************************************/ + +/** + * \fn const char *OGRGeomFieldDefn::GetNameRef(); + * + * \brief Fetch name of this field. + * + * This method is the same as the C function OGR_GFld_GetNameRef(). + * + * @return pointer to an internal name string that should not be freed or + * modified. + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_GFld_GetNameRef() */ +/************************************************************************/ +/** + * \brief Fetch name of this field. + * + * This function is the same as the CPP method OGRGeomFieldDefn::GetNameRef(). + * + * @param hDefn handle to the geometry field definition. + * @return the name of the geometry field definition. + * + * @since GDAL 1.11 + */ + +const char *OGR_GFld_GetNameRef( OGRGeomFieldDefnH hDefn ) + +{ + VALIDATE_POINTER1( hDefn, "OGR_GFld_GetNameRef", "" ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_GFld_GetXXXX(hDefn, "GetNameRef"); +#endif + + return ((OGRGeomFieldDefn *) hDefn)->GetNameRef(); +} + +/************************************************************************/ +/* GetType() */ +/************************************************************************/ + +/** + * \fn OGRwkbGeometryType OGRGeomFieldDefn::GetType(); + * + * \brief Fetch geometry type of this field. + * + * This method is the same as the C function OGR_GFld_GetType(). + * + * @return field geometry type. + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_GFld_GetType() */ +/************************************************************************/ +/** + * \brief Fetch geometry type of this field. + * + * This function is the same as the CPP method OGRGeomFieldDefn::GetType(). + * + * @param hDefn handle to the geometry field definition to get type from. + * @return field geometry type. + * + * @since GDAL 1.11 + */ + +OGRwkbGeometryType OGR_GFld_GetType( OGRGeomFieldDefnH hDefn ) + +{ + VALIDATE_POINTER1( hDefn, "OGR_GFld_GetType", wkbUnknown ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_GFld_GetXXXX(hDefn, "GetType"); +#endif + + OGRwkbGeometryType eType = ((OGRGeomFieldDefn *) hDefn)->GetType(); + if( OGR_GT_IsNonLinear(eType) && !OGRGetNonLinearGeometriesEnabledFlag() ) + { + eType = OGR_GT_GetLinear(eType); + } + return eType; +} + +/************************************************************************/ +/* SetType() */ +/************************************************************************/ + +/** + * \fn void OGRGeomFieldDefn::SetType( OGRwkbGeometryType eType ); + * + * \brief Set the geometry type of this field. + * This should never be done to an OGRGeomFieldDefn + * that is already part of an OGRFeatureDefn. + * + * This method is the same as the C function OGR_GFld_SetType(). + * + * @param eType the new field geometry type. + * + * @since GDAL 1.11 + */ + +void OGRGeomFieldDefn::SetType( OGRwkbGeometryType eTypeIn ) + +{ + eGeomType = eTypeIn; +} + +/************************************************************************/ +/* OGR_GFld_SetType() */ +/************************************************************************/ +/** + * \brief Set the geometry type of this field. + * This should never be done to an OGRGeomFieldDefn + * that is already part of an OGRFeatureDefn. + * + * This function is the same as the CPP method OGRGeomFieldDefn::SetType(). + * + * @param hDefn handle to the geometry field definition to set type to. + * @param eType the new field geometry type. + * + * @since GDAL 1.11 + */ + +void OGR_GFld_SetType( OGRGeomFieldDefnH hDefn, OGRwkbGeometryType eType ) + +{ + VALIDATE_POINTER0( hDefn, "OGR_GFld_SetType" ); + ((OGRGeomFieldDefn *) hDefn)->SetType( eType ); +} + +/************************************************************************/ +/* IsIgnored() */ +/************************************************************************/ + +/** + * \fn int OGRGeomFieldDefn::IsIgnored(); + * + * \brief Return whether this field should be omitted when fetching features + * + * This method is the same as the C function OGR_GFld_IsIgnored(). + * + * @return ignore state + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_GFld_IsIgnored() */ +/************************************************************************/ + +/** + * \brief Return whether this field should be omitted when fetching features + * + * This method is the same as the C++ method OGRGeomFieldDefn::IsIgnored(). + * + * @param hDefn handle to the geometry field definition + * @return ignore state + * + * @since GDAL 1.11 + */ + +int OGR_GFld_IsIgnored( OGRGeomFieldDefnH hDefn ) +{ + VALIDATE_POINTER1( hDefn, "OGR_GFld_IsIgnored", FALSE ); + return ((OGRGeomFieldDefn *) hDefn)->IsIgnored(); +} + +/************************************************************************/ +/* SetIgnored() */ +/************************************************************************/ + +/** + * \fn void OGRGeomFieldDefn::SetIgnored( int ignore ); + * + * \brief Set whether this field should be omitted when fetching features + * + * This method is the same as the C function OGR_GFld_SetIgnored(). + * + * @param ignore ignore state + * + * @since GDAL 1.11 + */ + +/************************************************************************/ +/* OGR_GFld_SetIgnored() */ +/************************************************************************/ + +/** + * \brief Set whether this field should be omitted when fetching features + * + * This method is the same as the C++ method OGRGeomFieldDefn::SetIgnored(). + * + * @param hDefn handle to the geometry field definition + * @param ignore ignore state + * + * @since GDAL 1.11 + */ + +void OGR_GFld_SetIgnored( OGRGeomFieldDefnH hDefn, int ignore ) +{ + VALIDATE_POINTER0( hDefn, "OGR_GFld_SetIgnored" ); + ((OGRGeomFieldDefn *) hDefn)->SetIgnored( ignore ); +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ +/** + * \brief Fetch spatial reference system of this field. + * + * This method is the same as the C function OGR_GFld_GetSpatialRef(). + * + * @return field spatial reference system. + * + * @since GDAL 1.11 + */ + +OGRSpatialReference* OGRGeomFieldDefn::GetSpatialRef() +{ + return poSRS; +} + +/************************************************************************/ +/* OGR_GFld_GetSpatialRef() */ +/************************************************************************/ + +/** + * \brief Fetch spatial reference system of this field. + * + * This function is the same as the C++ method OGRGeomFieldDefn::GetSpatialRef(). + * + * @param hDefn handle to the geometry field definition + * + * @return field spatial reference system. + * + * @since GDAL 1.11 + */ + +OGRSpatialReferenceH OGR_GFld_GetSpatialRef( OGRGeomFieldDefnH hDefn ) +{ + VALIDATE_POINTER1( hDefn, "OGR_GFld_GetSpatialRef", NULL ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_GFld_GetXXXX(hDefn, "GetSpatialRef"); +#endif + + return (OGRSpatialReferenceH) ((OGRGeomFieldDefn *) hDefn)->GetSpatialRef(); +} + +/************************************************************************/ +/* SetSpatialRef() */ +/************************************************************************/ + +/** + * \brief Set the spatial reference of this field. + * + * This method is the same as the C function OGR_GFld_SetSpatialRef(). + * + * This method drops the reference of the previously set SRS object and + * acquires a new reference on the passed object (if non-NULL). + * + * @param poSRSIn the new SRS to apply. + * + * @since GDAL 1.11 + */ +void OGRGeomFieldDefn::SetSpatialRef(OGRSpatialReference* poSRSIn) +{ + if( poSRS != NULL ) + poSRS->Release(); + poSRS = poSRSIn; + if( poSRS != NULL ) + poSRS->Reference(); +} + +/************************************************************************/ +/* OGR_GFld_SetSpatialRef() */ +/************************************************************************/ + +/** + * \brief Set the spatial reference of this field. + * + * This function is the same as the C++ method OGRGeomFieldDefn::SetSpatialRef(). + * + * This function drops the reference of the previously set SRS object and + * acquires a new reference on the passed object (if non-NULL). + * + * @param hDefn handle to the geometry field definition + * @param hSRS the new SRS to apply. + * + * @since GDAL 1.11 + */ + +void OGR_GFld_SetSpatialRef( OGRGeomFieldDefnH hDefn, OGRSpatialReferenceH hSRS ) +{ + VALIDATE_POINTER0( hDefn, "OGR_GFld_SetSpatialRef" ); + ((OGRGeomFieldDefn *) hDefn)->SetSpatialRef( (OGRSpatialReference*) hSRS ); +} + +/************************************************************************/ +/* IsSame() */ +/************************************************************************/ + +/** + * \brief Test if the geometry field definition is identical to the other one. + * + * @param poOtherFieldDefn the other field definition to compare to. + * @return TRUE if the geometry field definition is identical to the other one. + * + * @since GDAL 1.11 + */ + +int OGRGeomFieldDefn::IsSame( OGRGeomFieldDefn * poOtherFieldDefn ) +{ + if( !(strcmp(GetNameRef(), poOtherFieldDefn->GetNameRef()) == 0 && + GetType() == poOtherFieldDefn->GetType() && + IsNullable() == poOtherFieldDefn->IsNullable()) ) + return FALSE; + OGRSpatialReference* poMySRS = GetSpatialRef(); + OGRSpatialReference* poOtherSRS = poOtherFieldDefn->GetSpatialRef(); + return ((poMySRS == poOtherSRS) || + (poMySRS != NULL && poOtherSRS != NULL && + poMySRS->IsSame(poOtherSRS))); +} + +/************************************************************************/ +/* IsNullable() */ +/************************************************************************/ + +/** + * \fn int OGRGeomFieldDefn::IsNullable() const + * + * \brief Return whether this geometry field can receive null values. + * + * By default, fields are nullable. + * + * Even if this method returns FALSE (i.e not-nullable field), it doesn't mean + * that OGRFeature::IsFieldSet() will necessary return TRUE, as fields can be + * temporary unset and null/not-null validation is usually done when + * OGRLayer::CreateFeature()/SetFeature() is called. + * + * Note that not-nullable geometry fields might also contain 'empty' geometries. + * + * This method is the same as the C function OGR_GFld_IsNullable(). + * + * @return TRUE if the field is authorized to be null. + * @since GDAL 2.0 + */ + +/************************************************************************/ +/* OGR_GFld_IsNullable() */ +/************************************************************************/ + +/** + * \brief Return whether this geometry field can receive null values. + * + * By default, fields are nullable. + * + * Even if this method returns FALSE (i.e not-nullable field), it doesn't mean + * that OGRFeature::IsFieldSet() will necessary return TRUE, as fields can be + * temporary unset and null/not-null validation is usually done when + * OGRLayer::CreateFeature()/SetFeature() is called. + * + * Note that not-nullable geometry fields might also contain 'empty' geometries. + * + * This method is the same as the C++ method OGRGeomFieldDefn::IsNullable(). + * + * @param hDefn handle to the field definition + * @return TRUE if the field is authorized to be null. + * @since GDAL 2.0 + */ + +int OGR_GFld_IsNullable( OGRGeomFieldDefnH hDefn ) +{ + return ((OGRGeomFieldDefn *) hDefn)->IsNullable(); +} + +/************************************************************************/ +/* SetNullable() */ +/************************************************************************/ + +/** + * \fn void OGRGeomFieldDefn::SetNullable( int bNullableIn ); + * + * \brief Set whether this geometry field can receive null values. + * + * By default, fields are nullable, so this method is generally called with FALSE + * to set a not-null constraint. + * + * Drivers that support writing not-null constraint will advertize the + * GDAL_DCAP_NOTNULL_GEOMFIELDS driver metadata item. + * + * This method is the same as the C function OGR_GFld_SetNullable(). + * + * @param bNullableIn FALSE if the field must have a not-null constraint. + * @since GDAL 2.0 + */ + +/************************************************************************/ +/* OGR_GFld_SetNullable() */ +/************************************************************************/ + +/** + * \brief Set whether this geometry field can receive null values. + * + * By default, fields are nullable, so this method is generally called with FALSE + * to set a not-null constraint. + * + * Drivers that support writing not-null constraint will advertize the + * GDAL_DCAP_NOTNULL_GEOMFIELDS driver metadata item. + * + * This method is the same as the C++ method OGRGeomFieldDefn::SetNullable(). + * + * @param hDefn handle to the field definition + * @param bNullableIn FALSE if the field must have a not-null constraint. + * @since GDAL 2.0 + */ + +void OGR_GFld_SetNullable( OGRGeomFieldDefnH hDefn, int bNullableIn ) +{ + ((OGRGeomFieldDefn *) hDefn)->SetNullable( bNullableIn ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrlinearring.cpp b/bazaar/plugin/gdal/ogr/ogrlinearring.cpp new file mode 100644 index 000000000..bf9ebdbaf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrlinearring.cpp @@ -0,0 +1,632 @@ +/****************************************************************************** + * $Id: ogrlinearring.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRLinearRing geometry class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrlinearring.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +/************************************************************************/ +/* OGRLinearRing() */ +/************************************************************************/ + +OGRLinearRing::OGRLinearRing() + +{ +} + +/************************************************************************/ +/* ~OGRLinearRing() */ +/************************************************************************/ + +OGRLinearRing::~OGRLinearRing() + +{ +} + +/************************************************************************/ +/* OGRLinearRing() */ +/************************************************************************/ + +OGRLinearRing::OGRLinearRing( OGRLinearRing * poSrcRing ) + +{ + if( poSrcRing == NULL ) + { + CPLDebug( "OGR", "OGRLinearRing::OGRLinearRing(OGRLinearRing*poSrcRing) - passed in ring is NULL!" ); + return; + } + + setNumPoints( poSrcRing->getNumPoints(), FALSE ); + + memcpy( paoPoints, poSrcRing->paoPoints, + sizeof(OGRRawPoint) * getNumPoints() ); + + if( poSrcRing->padfZ ) + { + Make3D(); + memcpy( padfZ, poSrcRing->padfZ, sizeof(double) * getNumPoints() ); + } +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRLinearRing::getGeometryName() const + +{ + return "LINEARRING"; +} + +/************************************************************************/ +/* WkbSize() */ +/* */ +/* Disable this method. */ +/************************************************************************/ + +int OGRLinearRing::WkbSize() const + +{ + return 0; +} + +/************************************************************************/ +/* importFromWkb() */ +/* */ +/* Disable method for this class. */ +/************************************************************************/ + +OGRErr OGRLinearRing::importFromWkb( CPL_UNUSED unsigned char *pabyData, + CPL_UNUSED int nSize, + CPL_UNUSED OGRwkbVariant eWkbVariant ) + +{ + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* exportToWkb() */ +/* */ +/* Disable method for this class. */ +/************************************************************************/ + +OGRErr OGRLinearRing::exportToWkb( CPL_UNUSED OGRwkbByteOrder eByteOrder, + CPL_UNUSED unsigned char * pabyData, + CPL_UNUSED OGRwkbVariant eWkbVariant ) const + +{ + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* _importFromWkb() */ +/* */ +/* Helper method for OGRPolygon. NOT A NORMAL importFromWkb() */ +/* method! */ +/************************************************************************/ + +OGRErr OGRLinearRing::_importFromWkb( OGRwkbByteOrder eByteOrder, int b3D, + unsigned char * pabyData, + int nBytesAvailable ) + +{ + if( nBytesAvailable < 4 && nBytesAvailable != -1 ) + return OGRERR_NOT_ENOUGH_DATA; + +/* -------------------------------------------------------------------- */ +/* Get the vertex count. */ +/* -------------------------------------------------------------------- */ + int nNewNumPoints; + + memcpy( &nNewNumPoints, pabyData, 4 ); + + if( OGR_SWAP( eByteOrder ) ) + nNewNumPoints = CPL_SWAP32(nNewNumPoints); + + /* Check if the wkb stream buffer is big enough to store + * fetched number of points. + * 16 or 24 - size of point structure + */ + int nPointSize = (b3D ? 24 : 16); + if (nNewNumPoints < 0 || nNewNumPoints > INT_MAX / nPointSize) + return OGRERR_CORRUPT_DATA; + int nBufferMinSize = nPointSize * nNewNumPoints; + + if( nBytesAvailable != -1 && nBufferMinSize > nBytesAvailable - 4 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Length of input WKB is too small" ); + return OGRERR_NOT_ENOUGH_DATA; + } + + /* (Re)Allocation of paoPoints buffer. */ + setNumPoints( nNewNumPoints, FALSE ); + + if( b3D ) + Make3D(); + else + Make2D(); + +/* -------------------------------------------------------------------- */ +/* Get the vertices */ +/* -------------------------------------------------------------------- */ + int i = 0; + + if( !b3D ) + { + memcpy( paoPoints, pabyData + 4, 16 * nPointCount ); + } + else + { + for( int i = 0; i < nPointCount; i++ ) + { + memcpy( &(paoPoints[i].x), pabyData + 4 + 24 * i, 8 ); + memcpy( &(paoPoints[i].y), pabyData + 4 + 24 * i + 8, 8 ); + memcpy( padfZ + i, pabyData + 4 + 24 * i + 16, 8 ); + } + } + +/* -------------------------------------------------------------------- */ +/* Byte swap if needed. */ +/* -------------------------------------------------------------------- */ + if( OGR_SWAP( eByteOrder ) ) + { + for( i = 0; i < nPointCount; i++ ) + { + CPL_SWAPDOUBLE( &(paoPoints[i].x) ); + CPL_SWAPDOUBLE( &(paoPoints[i].y) ); + + if( b3D ) + { + CPL_SWAPDOUBLE( padfZ + i ); + } + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* _exportToWkb() */ +/* */ +/* Helper method for OGRPolygon. THIS IS NOT THE NORMAL */ +/* exportToWkb() METHOD! */ +/************************************************************************/ + +OGRErr OGRLinearRing::_exportToWkb( OGRwkbByteOrder eByteOrder, int b3D, + unsigned char * pabyData ) const + +{ + int i, nWords; + +/* -------------------------------------------------------------------- */ +/* Copy in the raw data. */ +/* -------------------------------------------------------------------- */ + memcpy( pabyData, &nPointCount, 4 ); + +/* -------------------------------------------------------------------- */ +/* Copy in the raw data. */ +/* -------------------------------------------------------------------- */ + if( b3D ) + { + nWords = 3 * nPointCount; + for( i = 0; i < nPointCount; i++ ) + { + memcpy( pabyData+4+i*24, &(paoPoints[i].x), 8 ); + memcpy( pabyData+4+i*24+8, &(paoPoints[i].y), 8 ); + if( padfZ == NULL ) + memset( pabyData+4+i*24+16, 0, 8 ); + else + memcpy( pabyData+4+i*24+16, padfZ + i, 8 ); + } + } + else + { + nWords = 2 * nPointCount; + memcpy( pabyData+4, paoPoints, 16 * nPointCount ); + } + +/* -------------------------------------------------------------------- */ +/* Swap if needed. */ +/* -------------------------------------------------------------------- */ + if( OGR_SWAP( eByteOrder ) ) + { + int nCount; + + nCount = CPL_SWAP32( nPointCount ); + memcpy( pabyData, &nCount, 4 ); + + for( i = 0; i < nWords; i++ ) + { + CPL_SWAPDOUBLE( pabyData + 4 + 8 * i ); + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* _WkbSize() */ +/* */ +/* Helper method for OGRPolygon. NOT THE NORMAL WkbSize() METHOD! */ +/************************************************************************/ + +int OGRLinearRing::_WkbSize( int b3D ) const + +{ + if( b3D ) + return 4 + 24 * nPointCount; + else + return 4 + 16 * nPointCount; +} + +/************************************************************************/ +/* clone() */ +/* */ +/* We override the OGRCurve clone() to ensure that we get the */ +/* correct virtual table. */ +/************************************************************************/ + +OGRGeometry *OGRLinearRing::clone() const + +{ + OGRLinearRing *poNewLinearRing; + + poNewLinearRing = new OGRLinearRing(); + poNewLinearRing->assignSpatialReference( getSpatialReference() ); + + poNewLinearRing->setPoints( nPointCount, paoPoints, padfZ ); + + return poNewLinearRing; +} + + +/************************************************************************/ +/* epsilonEqual() */ +/************************************************************************/ + +static const double EPSILON = 1E-5; + +static inline bool epsilonEqual(double a, double b, double eps) +{ + return (::fabs(a - b) < eps); +} + +/************************************************************************/ +/* isClockwise() */ +/************************************************************************/ + +/** + * \brief Returns TRUE if the ring has clockwise winding (or less than 2 points) + * + * @return TRUE if clockwise otherwise FALSE. + */ + +int OGRLinearRing::isClockwise() const + +{ + int i, v, next; + double dx0, dy0, dx1, dy1, crossproduct; + int bUseFallback = FALSE; + + if( nPointCount < 2 ) + return TRUE; + + /* Find the lowest rightmost vertex */ + v = 0; + for ( i = 1; i < nPointCount - 1; i++ ) + { + /* => v < end */ + if ( paoPoints[i].y< paoPoints[v].y || + ( paoPoints[i].y== paoPoints[v].y && + paoPoints[i].x > paoPoints[v].x ) ) + { + v = i; + bUseFallback = FALSE; + } + else if ( paoPoints[i].y == paoPoints[v].y && + paoPoints[i].x == paoPoints[v].x ) + { + /* Two vertex with same coordinates are the lowest rightmost */ + /* vertex! We cannot use that point as the pivot (#5342) */ + bUseFallback = TRUE; + } + } + + /* previous */ + next = v - 1; + if ( next < 0 ) + { + next = nPointCount - 1 - 1; + } + + if( epsilonEqual(paoPoints[next].x, paoPoints[v].x, EPSILON) && + epsilonEqual(paoPoints[next].y, paoPoints[v].y, EPSILON) ) + { + /* Don't try to be too clever by retrying with a next point */ + /* This can lead to false results as in the case of #3356 */ + bUseFallback = TRUE; + } + + dx0 = paoPoints[next].x - paoPoints[v].x; + dy0 = paoPoints[next].y - paoPoints[v].y; + + + /* following */ + next = v + 1; + if ( next >= nPointCount - 1 ) + { + next = 0; + } + + if( epsilonEqual(paoPoints[next].x, paoPoints[v].x, EPSILON) && + epsilonEqual(paoPoints[next].y, paoPoints[v].y, EPSILON) ) + { + /* Don't try to be too clever by retrying with a next point */ + /* This can lead to false results as in the case of #3356 */ + bUseFallback = TRUE; + } + + dx1 = paoPoints[next].x - paoPoints[v].x; + dy1 = paoPoints[next].y - paoPoints[v].y; + + crossproduct = dx1 * dy0 - dx0 * dy1; + + if (!bUseFallback) + { + if ( crossproduct > 0 ) /* CCW */ + return FALSE; + else if ( crossproduct < 0 ) /* CW */ + return TRUE; + } + + /* ok, this is a degenerate case : the extent of the polygon is less than EPSILON */ + /* or 2 nearly identical points were found */ + /* Try with Green Formula as a fallback, but this is not a guarantee */ + /* as we'll probably be affected by numerical instabilities */ + + double dfSum = paoPoints[0].x * (paoPoints[1].y - paoPoints[nPointCount-1].y); + + for (i=1; igetX(); + const double dfTestY = poPoint->getY(); + + // Fast test if point is inside extent of the ring + if (bTestEnvelope) + { + OGREnvelope extent; + getEnvelope(&extent); + if ( !( dfTestX >= extent.MinX && dfTestX <= extent.MaxX + && dfTestY >= extent.MinY && dfTestY <= extent.MaxY ) ) + { + return 0; + } + } + + // For every point p in ring, + // test if ray starting from given point crosses segment (p - 1, p) + int iNumCrossings = 0; + + double prev_diff_x = getX(0) - dfTestX; + double prev_diff_y = getY(0) - dfTestY; + + for ( int iPoint = 1; iPoint < iNumPoints; iPoint++ ) + { + const double x1 = getX(iPoint) - dfTestX; + const double y1 = getY(iPoint) - dfTestY; + + const double x2 = prev_diff_x; + const double y2 = prev_diff_y; + + if( ( ( y1 > 0 ) && ( y2 <= 0 ) ) || ( ( y2 > 0 ) && ( y1 <= 0 ) ) ) + { + // Check if ray intersects with segment of the ring + const double dfIntersection = ( x1 * y2 - x2 * y1 ) / (y2 - y1); + if ( 0.0 < dfIntersection ) + { + // Count intersections + iNumCrossings++; + } + } + + prev_diff_x = x1; + prev_diff_y = y1; + } + + // If iNumCrossings number is even, given point is outside the ring, + // when the crossings number is odd, the point is inside the ring. + return ( ( iNumCrossings % 2 ) == 1 ? 1 : 0 ); +} + +/************************************************************************/ +/* isPointOnRingBoundary() */ +/************************************************************************/ + +OGRBoolean OGRLinearRing::isPointOnRingBoundary(const OGRPoint* poPoint, int bTestEnvelope) const +{ + if ( NULL == poPoint ) + { + CPLDebug( "OGR", "OGRLinearRing::isPointOnRingBoundary(const OGRPoint* poPoint) - passed point is NULL!" ); + return 0; + } + + const int iNumPoints = getNumPoints(); + + // Simple validation + if ( iNumPoints < 4 ) + return 0; + + const double dfTestX = poPoint->getX(); + const double dfTestY = poPoint->getY(); + + // Fast test if point is inside extent of the ring + if( bTestEnvelope ) + { + OGREnvelope extent; + getEnvelope(&extent); + if ( !( dfTestX >= extent.MinX && dfTestX <= extent.MaxX + && dfTestY >= extent.MinY && dfTestY <= extent.MaxY ) ) + { + return 0; + } + } + + double prev_diff_x = getX(0) - dfTestX; + double prev_diff_y = getY(0) - dfTestY; + + for ( int iPoint = 1; iPoint < iNumPoints; iPoint++ ) + { + const double x1 = getX(iPoint) - dfTestX; + const double y1 = getY(iPoint) - dfTestY; + + const double x2 = prev_diff_x; + const double y2 = prev_diff_y; + + /* If the point is on the segment, return immediatly */ + /* FIXME? If the test point is not exactly identical to one of */ + /* the vertices of the ring, but somewhere on a segment, there's */ + /* little chance that we get 0. So that should be tested against some epsilon */ + + if ( x1 * y2 - x2 * y1 == 0 ) + { + /* If iPoint and iPointPrev are the same, go on */ + if( !(x1 == x2 && y1 == y2) ) + { + return 1; + } + } + + prev_diff_x = x1; + prev_diff_y = y1; + } + + return 0; +} + +/************************************************************************/ +/* CastToLineString() */ +/************************************************************************/ + +/** + * \brief Cast to line string. + * + * The passed in geometry is consumed and a new one returned . + * + * @param poLR the input geometry - ownership is passed to the method. + * @return new geometry. + */ + +OGRLineString* OGRLinearRing::CastToLineString(OGRLinearRing* poLR) +{ + return TransferMembersAndDestroy(poLR, new OGRLineString()); +} + +/************************************************************************/ +/* GetCasterToLineString() */ +/************************************************************************/ + +OGRCurveCasterToLineString OGRLinearRing::GetCasterToLineString() const { + return (OGRCurveCasterToLineString) OGRLinearRing::CastToLineString; +} + +/************************************************************************/ +/* GetCasterToLinearRing() */ +/************************************************************************/ + +OGRCurveCasterToLinearRing OGRLinearRing::GetCasterToLinearRing() const { + return (OGRCurveCasterToLinearRing) OGRGeometry::CastToIdentity; +} diff --git a/bazaar/plugin/gdal/ogr/ogrlinestring.cpp b/bazaar/plugin/gdal/ogr/ogrlinestring.cpp new file mode 100644 index 000000000..1718db2e8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrlinestring.cpp @@ -0,0 +1,2020 @@ +/****************************************************************************** + * $Id: ogrlinestring.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRSimpleCurve and OGRLineString geometry classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" +#include +#include "ogr_geos.h" + +CPL_CVSID("$Id: ogrlinestring.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +/************************************************************************/ +/* OGRSimpleCurve() */ +/************************************************************************/ + +OGRSimpleCurve::OGRSimpleCurve() + +{ + nPointCount = 0; + paoPoints = NULL; + padfZ = NULL; +} + +/************************************************************************/ +/* ~OGRSimpleCurve() */ +/************************************************************************/ + +OGRSimpleCurve::~OGRSimpleCurve() + +{ + if( paoPoints != NULL ) + OGRFree( paoPoints ); + if( padfZ != NULL ) + OGRFree( padfZ ); +} + +/************************************************************************/ +/* flattenTo2D() */ +/************************************************************************/ + +void OGRSimpleCurve::flattenTo2D() + +{ + Make2D(); +} + +/************************************************************************/ +/* clone() */ +/************************************************************************/ + +OGRGeometry *OGRSimpleCurve::clone() const + +{ + OGRLineString *poNewLineString; + + poNewLineString = (OGRLineString*) + OGRGeometryFactory::createGeometry(getGeometryType()); + + poNewLineString->assignSpatialReference( getSpatialReference() ); + poNewLineString->setPoints( nPointCount, paoPoints, padfZ ); + poNewLineString->setCoordinateDimension( getCoordinateDimension() ); + + return poNewLineString; +} + +/************************************************************************/ +/* empty() */ +/************************************************************************/ + +void OGRSimpleCurve::empty() + +{ + setNumPoints( 0 ); +} + +/************************************************************************/ +/* setCoordinateDimension() */ +/************************************************************************/ + +void OGRSimpleCurve::setCoordinateDimension( int nNewDimension ) + +{ + nCoordDimension = nNewDimension; + if( nNewDimension == 2 ) + Make2D(); + else if( nNewDimension == 3 ) + Make3D(); +} + +/************************************************************************/ +/* WkbSize() */ +/* */ +/* Return the size of this object in well known binary */ +/* representation including the byte order, and type information. */ +/************************************************************************/ + +int OGRSimpleCurve::WkbSize() const + +{ + return 5 + 4 + 8 * nPointCount * getCoordinateDimension(); +} + +/************************************************************************/ +/* Make2D() */ +/************************************************************************/ + +void OGRSimpleCurve::Make2D() + +{ + if( padfZ != NULL ) + { + OGRFree( padfZ ); + padfZ = NULL; + } + nCoordDimension = 2; +} + +/************************************************************************/ +/* Make3D() */ +/************************************************************************/ + +void OGRSimpleCurve::Make3D() + +{ + if( padfZ == NULL ) + { + if( nPointCount == 0 ) + padfZ = (double *) OGRCalloc(sizeof(double),1); + else + padfZ = (double *) OGRCalloc(sizeof(double),nPointCount); + } + nCoordDimension = 3; +} + +/************************************************************************/ +/* getPoint() */ +/************************************************************************/ + +/** + * \brief Fetch a point in line string. + * + * This method relates to the SFCOM ILineString::get_Point() method. + * + * @param i the vertex to fetch, from 0 to getNumPoints()-1. + * @param poPoint a point to initialize with the fetched point. + */ + +void OGRSimpleCurve::getPoint( int i, OGRPoint * poPoint ) const + +{ + assert( i >= 0 ); + assert( i < nPointCount ); + assert( poPoint != NULL ); + + poPoint->setX( paoPoints[i].x ); + poPoint->setY( paoPoints[i].y ); + + if( getCoordinateDimension() == 3 && padfZ != NULL ) + poPoint->setZ( padfZ[i] ); +} + +/** + * \fn int OGRSimpleCurve::getNumPoints() const; + * + * \brief Fetch vertex count. + * + * Returns the number of vertices in the line string. + * + * @return vertex count. + */ + +/** + * \fn double OGRSimpleCurve::getX( int iVertex ) const; + * + * \brief Get X at vertex. + * + * Returns the X value at the indicated vertex. If iVertex is out of range a + * crash may occur, no internal range checking is performed. + * + * @param iVertex the vertex to return, between 0 and getNumPoints()-1. + * + * @return X value. + */ + +/** + * \fn double OGRSimpleCurve::getY( int iVertex ) const; + * + * \brief Get Y at vertex. + * + * Returns the Y value at the indicated vertex. If iVertex is out of range a + * crash may occur, no internal range checking is performed. + * + * @param iVertex the vertex to return, between 0 and getNumPoints()-1. + * + * @return X value. + */ + +/************************************************************************/ +/* getZ() */ +/************************************************************************/ + +/** + * \brief Get Z at vertex. + * + * Returns the Z (elevation) value at the indicated vertex. If no Z + * value is available, 0.0 is returned. If iVertex is out of range a + * crash may occur, no internal range checking is performed. + * + * @param iVertex the vertex to return, between 0 and getNumPoints()-1. + * + * @return Z value. + */ + +double OGRSimpleCurve::getZ( int iVertex ) const + +{ + if( padfZ != NULL && iVertex >= 0 && iVertex < nPointCount + && nCoordDimension >= 3 ) + return( padfZ[iVertex] ); + else + return 0.0; +} + +/************************************************************************/ +/* setNumPoints() */ +/************************************************************************/ + +/** + * \brief Set number of points in geometry. + * + * This method primary exists to preset the number of points in a linestring + * geometry before setPoint() is used to assign them to avoid reallocating + * the array larger with each call to addPoint(). + * + * This method has no SFCOM analog. + * + * @param nNewPointCount the new number of points for geometry. + */ + +void OGRSimpleCurve::setNumPoints( int nNewPointCount, int bZeroizeNewContent ) + +{ + if( nNewPointCount == 0 ) + { + OGRFree( paoPoints ); + paoPoints = NULL; + + OGRFree( padfZ ); + padfZ = NULL; + + nPointCount = 0; + return; + } + + if( nNewPointCount > nPointCount ) + { + OGRRawPoint* paoNewPoints = (OGRRawPoint *) + VSIRealloc(paoPoints, sizeof(OGRRawPoint) * nNewPointCount); + if (paoNewPoints == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Could not allocate array for %d points", nNewPointCount); + return; + } + paoPoints = paoNewPoints; + + if( bZeroizeNewContent ) + memset( paoPoints + nPointCount, + 0, sizeof(OGRRawPoint) * (nNewPointCount - nPointCount) ); + + if( getCoordinateDimension() == 3 ) + { + double* padfNewZ = (double *) + VSIRealloc( padfZ, sizeof(double)*nNewPointCount ); + if (padfNewZ == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Could not allocate array for %d points", nNewPointCount); + return; + } + padfZ = padfNewZ; + if( bZeroizeNewContent ) + memset( padfZ + nPointCount, 0, + sizeof(double) * (nNewPointCount - nPointCount) ); + } + } + + nPointCount = nNewPointCount; +} + +/************************************************************************/ +/* setPoint() */ +/************************************************************************/ + +/** + * \brief Set the location of a vertex in line string. + * + * If iPoint is larger than the number of necessary the number of existing + * points in the line string, the point count will be increased to + * accommodate the request. + * + * There is no SFCOM analog to this method. + * + * @param iPoint the index of the vertex to assign (zero based). + * @param poPoint the value to assign to the vertex. + */ + +void OGRSimpleCurve::setPoint( int iPoint, OGRPoint * poPoint ) + +{ + if (poPoint->getCoordinateDimension() < 3) + setPoint( iPoint, poPoint->getX(), poPoint->getY() ); + else + setPoint( iPoint, poPoint->getX(), poPoint->getY(), poPoint->getZ() ); +} + +/************************************************************************/ +/* setPoint() */ +/************************************************************************/ + +/** + * \brief Set the location of a vertex in line string. + * + * If iPoint is larger than the number of necessary the number of existing + * points in the line string, the point count will be increased to + * accommodate the request. + * + * There is no SFCOM analog to this method. + * + * @param iPoint the index of the vertex to assign (zero based). + * @param xIn input X coordinate to assign. + * @param yIn input Y coordinate to assign. + * @param zIn input Z coordinate to assign (defaults to zero). + */ + +void OGRSimpleCurve::setPoint( int iPoint, double xIn, double yIn, double zIn ) + +{ + if( getCoordinateDimension() == 2 ) + Make3D(); + + if( iPoint >= nPointCount ) + { + setNumPoints( iPoint+1 ); + if (nPointCount < iPoint + 1) + return; + } + + paoPoints[iPoint].x = xIn; + paoPoints[iPoint].y = yIn; + + if( zIn != 0.0 ) + { + Make3D(); + padfZ[iPoint] = zIn; + } + else if( getCoordinateDimension() == 3 ) + { + padfZ[iPoint] = 0.0; + } +} + +void OGRSimpleCurve::setPoint( int iPoint, double xIn, double yIn ) + +{ + if( iPoint >= nPointCount ) + { + setNumPoints( iPoint+1 ); + if (nPointCount < iPoint + 1) + return; + } + + paoPoints[iPoint].x = xIn; + paoPoints[iPoint].y = yIn; +} + +/************************************************************************/ +/* setZ() */ +/************************************************************************/ + +void OGRSimpleCurve::setZ( int iPoint, double zIn ) +{ + if( getCoordinateDimension() == 2 ) + Make3D(); + + if( iPoint >= nPointCount ) + { + setNumPoints( iPoint+1 ); + if (nPointCount < iPoint + 1) + return; + } + + if( padfZ ) + padfZ[iPoint] = zIn; +} + +/************************************************************************/ +/* addPoint() */ +/************************************************************************/ + +/** + * \brief Add a point to a line string. + * + * The vertex count of the line string is increased by one, and assigned from + * the passed location value. + * + * There is no SFCOM analog to this method. + * + * @param poPoint the point to assign to the new vertex. + */ + +void OGRSimpleCurve::addPoint( OGRPoint * poPoint ) + +{ + if ( poPoint->getCoordinateDimension() < 3 ) + setPoint( nPointCount, poPoint->getX(), poPoint->getY() ); + else + setPoint( nPointCount, poPoint->getX(), poPoint->getY(), poPoint->getZ() ); +} + +/************************************************************************/ +/* addPoint() */ +/************************************************************************/ + +/** + * \brief Add a point to a line string. + * + * The vertex count of the line string is increased by one, and assigned from + * the passed location value. + * + * There is no SFCOM analog to this method. + * + * @param x the X coordinate to assign to the new point. + * @param y the Y coordinate to assign to the new point. + * @param z the Z coordinate to assign to the new point (defaults to zero). + */ + +void OGRSimpleCurve::addPoint( double x, double y, double z ) + +{ + setPoint( nPointCount, x, y, z ); +} + +void OGRSimpleCurve::addPoint( double x, double y ) + +{ + setPoint( nPointCount, x, y ); +} + +/************************************************************************/ +/* setPoints() */ +/************************************************************************/ + +/** + * \brief Assign all points in a line string. + * + * This method clears any existing points assigned to this line string, + * and assigns a whole new set. It is the most efficient way of assigning + * the value of a line string. + * + * There is no SFCOM analog to this method. + * + * @param nPointsIn number of points being passed in paoPointsIn + * @param paoPointsIn list of points being assigned. + * @param padfZ the Z values that go with the points (optional, may be NULL). + */ + +void OGRSimpleCurve::setPoints( int nPointsIn, OGRRawPoint * paoPointsIn, + double * padfZ ) + +{ + setNumPoints( nPointsIn, FALSE ); + if (nPointCount < nPointsIn) + return; + + memcpy( paoPoints, paoPointsIn, sizeof(OGRRawPoint) * nPointsIn); + +/* -------------------------------------------------------------------- */ +/* Check 2D/3D. */ +/* -------------------------------------------------------------------- */ + if( padfZ == NULL && getCoordinateDimension() > 2 ) + { + Make2D(); + } + else if( padfZ ) + { + Make3D(); + memcpy( this->padfZ, padfZ, sizeof(double) * nPointsIn ); + } +} + +/************************************************************************/ +/* setPoints() */ +/************************************************************************/ + +/** + * \brief Assign all points in a line string. + * + * This method clear any existing points assigned to this line string, + * and assigns a whole new set. + * + * There is no SFCOM analog to this method. + * + * @param nPointsIn number of points being passed in padfX and padfY. + * @param padfX list of X coordinates of points being assigned. + * @param padfY list of Y coordinates of points being assigned. + * @param padfZ list of Z coordinates of points being assigned (defaults to + * NULL for 2D objects). + */ + +void OGRSimpleCurve::setPoints( int nPointsIn, double * padfX, double * padfY, + double * padfZ ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Check 2D/3D. */ +/* -------------------------------------------------------------------- */ + if( padfZ == NULL ) + Make2D(); + else + Make3D(); + +/* -------------------------------------------------------------------- */ +/* Assign values. */ +/* -------------------------------------------------------------------- */ + setNumPoints( nPointsIn, FALSE ); + if (nPointCount < nPointsIn) + return; + + for( i = 0; i < nPointsIn; i++ ) + { + paoPoints[i].x = padfX[i]; + paoPoints[i].y = padfY[i]; + } + + if( this->padfZ != NULL ) + memcpy( this->padfZ, padfZ, sizeof(double) * nPointsIn ); +} + +/************************************************************************/ +/* getPoints() */ +/************************************************************************/ + +/** + * \brief Returns all points of line string. + * + * This method copies all points into user list. This list must be at + * least sizeof(OGRRawPoint) * OGRGeometry::getNumPoints() byte in size. + * It also copies all Z coordinates. + * + * There is no SFCOM analog to this method. + * + * @param paoPointsOut a buffer into which the points is written. + * @param padfZ the Z values that go with the points (optional, may be NULL). + */ + +void OGRSimpleCurve::getPoints( OGRRawPoint * paoPointsOut, double * padfZ ) const +{ + if ( ! paoPointsOut ) + return; + + memcpy( paoPointsOut, paoPoints, sizeof(OGRRawPoint) * nPointCount ); + +/* -------------------------------------------------------------------- */ +/* Check 2D/3D. */ +/* -------------------------------------------------------------------- */ + if( padfZ ) + { + if ( this->padfZ ) + memcpy( padfZ, this->padfZ, sizeof(double) * nPointCount ); + else + memset( padfZ, 0, sizeof(double) * nPointCount ); + } +} + + +/************************************************************************/ +/* getPoints() */ +/************************************************************************/ + +/** + * \brief Returns all points of line string. + * + * This method copies all points into user arrays. The user provides the + * stride between 2 consecutives elements of the array. + * + * On some CPU architectures, care must be taken so that the arrays are properly aligned. + * + * There is no SFCOM analog to this method. + * + * @param pabyX a buffer of at least (sizeof(double) * nXStride * nPointCount) bytes, may be NULL. + * @param nXStride the number of bytes between 2 elements of pabyX. + * @param pabyY a buffer of at least (sizeof(double) * nYStride * nPointCount) bytes, may be NULL. + * @param nYStride the number of bytes between 2 elements of pabyY. + * @param pabyZ a buffer of at last size (sizeof(double) * nZStride * nPointCount) bytes, may be NULL. + * @param nZStride the number of bytes between 2 elements of pabyZ. + * + * @since OGR 1.9.0 + */ + +void OGRSimpleCurve::getPoints( void* pabyX, int nXStride, + void* pabyY, int nYStride, + void* pabyZ, int nZStride) const +{ + int i; + if (pabyX != NULL && nXStride == 0) + return; + if (pabyY != NULL && nYStride == 0) + return; + if (pabyZ != NULL && nZStride == 0) + return; + if (nXStride == 2 * sizeof(double) && + nYStride == 2 * sizeof(double) && + (char*)pabyY == (char*)pabyX + sizeof(double) && + (pabyZ == NULL || nZStride == sizeof(double))) + { + getPoints((OGRRawPoint *)pabyX, (double*)pabyZ); + return; + } + for(i=0;igetNumPoints(); + if (nOtherLineNumPoints == 0) + return; + +/* -------------------------------------------------------------------- */ +/* Do a bit of argument defaulting and validation. */ +/* -------------------------------------------------------------------- */ + if( nEndVertex == -1 ) + nEndVertex = nOtherLineNumPoints - 1; + + if( nStartVertex < 0 || nEndVertex < 0 + || nStartVertex >= nOtherLineNumPoints + || nEndVertex >= nOtherLineNumPoints ) + { + CPLAssert( FALSE ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Grow this linestring to hold the additional points. */ +/* -------------------------------------------------------------------- */ + int nOldPoints = nPointCount; + int nPointsToAdd = ABS(nEndVertex-nStartVertex) + 1; + + setNumPoints( nPointsToAdd + nOldPoints, FALSE ); + if (nPointCount < nPointsToAdd + nOldPoints) + return; + +/* -------------------------------------------------------------------- */ +/* Copy the x/y points - forward copies use memcpy. */ +/* -------------------------------------------------------------------- */ + if( nEndVertex >= nStartVertex ) + { + memcpy( paoPoints + nOldPoints, + poOtherLine->paoPoints + nStartVertex, + sizeof(OGRRawPoint) * nPointsToAdd ); + if( poOtherLine->padfZ != NULL ) + { + Make3D(); + memcpy( padfZ + nOldPoints, poOtherLine->padfZ + nStartVertex, + sizeof(double) * nPointsToAdd ); + } + } + +/* -------------------------------------------------------------------- */ +/* Copy the x/y points - reverse copies done double by double. */ +/* -------------------------------------------------------------------- */ + else + { + int i; + + for( i = 0; i < nPointsToAdd; i++ ) + { + paoPoints[i+nOldPoints].x = + poOtherLine->paoPoints[nStartVertex-i].x; + paoPoints[i+nOldPoints].y = + poOtherLine->paoPoints[nStartVertex-i].y; + } + + if( poOtherLine->padfZ != NULL ) + { + Make3D(); + + for( i = 0; i < nPointsToAdd; i++ ) + { + padfZ[i+nOldPoints] = poOtherLine->padfZ[nStartVertex-i]; + } + } + } +} + +/************************************************************************/ +/* importFromWkb() */ +/* */ +/* Initialize from serialized stream in well known binary */ +/* format. */ +/************************************************************************/ + +OGRErr OGRSimpleCurve::importFromWkb( unsigned char * pabyData, + int nSize, + OGRwkbVariant eWkbVariant ) + +{ + OGRwkbByteOrder eByteOrder; + int nDataOffset = 0; + int nNewNumPoints = 0; + + OGRErr eErr = importPreambuleOfCollectionFromWkb( pabyData, + nSize, + nDataOffset, + eByteOrder, + 16, + nNewNumPoints, + eWkbVariant ); + if( eErr >= 0 ) + return eErr; + + /* Check if the wkb stream buffer is big enough to store + * fetched number of points. + * 16 or 24 - size of point structure + */ + int nPointSize = (getCoordinateDimension() == 3 ? 24 : 16); + if (nNewNumPoints < 0 || nNewNumPoints > INT_MAX / nPointSize) + return OGRERR_CORRUPT_DATA; + int nBufferMinSize = nPointSize * nNewNumPoints; + + if( nSize != -1 && nBufferMinSize > nSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Length of input WKB is too small" ); + return OGRERR_NOT_ENOUGH_DATA; + } + + setNumPoints( nNewNumPoints, FALSE ); + if (nPointCount < nNewNumPoints) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Get the vertex. */ +/* -------------------------------------------------------------------- */ + int i = 0; + + if( getCoordinateDimension() == 3 ) + { + for( i = 0; i < nPointCount; i++ ) + { + memcpy( paoPoints + i, pabyData + 9 + i*24, 16 ); + memcpy( padfZ + i, pabyData + 9 + 16 + i*24, 8 ); + } + } + else + { + memcpy( paoPoints, pabyData + 9, 16 * nPointCount ); + } + +/* -------------------------------------------------------------------- */ +/* Byte swap if needed. */ +/* -------------------------------------------------------------------- */ + if( OGR_SWAP( eByteOrder ) ) + { + for( i = 0; i < nPointCount; i++ ) + { + CPL_SWAPDOUBLE( &(paoPoints[i].x) ); + CPL_SWAPDOUBLE( &(paoPoints[i].y) ); + } + + if( getCoordinateDimension() == 3 ) + { + for( i = 0; i < nPointCount; i++ ) + { + CPL_SWAPDOUBLE( padfZ + i ); + } + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* exportToWkb() */ +/* */ +/* Build a well known binary representation of this object. */ +/************************************************************************/ + +OGRErr OGRSimpleCurve::exportToWkb( OGRwkbByteOrder eByteOrder, + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const + +{ +/* -------------------------------------------------------------------- */ +/* Set the byte order. */ +/* -------------------------------------------------------------------- */ + pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER((unsigned char) eByteOrder); + +/* -------------------------------------------------------------------- */ +/* Set the geometry feature type. */ +/* -------------------------------------------------------------------- */ + GUInt32 nGType = getGeometryType(); + + if ( eWkbVariant == wkbVariantIso ) + nGType = getIsoGeometryType(); + else if( eWkbVariant == wkbVariantPostGIS1 && wkbHasZ((OGRwkbGeometryType)nGType) ) + nGType = (OGRwkbGeometryType)(wkbFlatten(nGType) | wkb25DBitInternalUse); /* yes we explicitly set wkb25DBit */ + + if( eByteOrder == wkbNDR ) + nGType = CPL_LSBWORD32( nGType ); + else + nGType = CPL_MSBWORD32( nGType ); + + memcpy( pabyData + 1, &nGType, 4 ); + +/* -------------------------------------------------------------------- */ +/* Copy in the data count. */ +/* -------------------------------------------------------------------- */ + memcpy( pabyData+5, &nPointCount, 4 ); + +/* -------------------------------------------------------------------- */ +/* Copy in the raw data. */ +/* -------------------------------------------------------------------- */ + int i; + + if( getCoordinateDimension() == 3 ) + { + for( i = 0; i < nPointCount; i++ ) + { + memcpy( pabyData + 9 + 24*i, paoPoints+i, 16 ); + memcpy( pabyData + 9 + 16 + 24*i, padfZ+i, 8 ); + } + } + else + memcpy( pabyData+9, paoPoints, 16 * nPointCount ); + +/* -------------------------------------------------------------------- */ +/* Swap if needed. */ +/* -------------------------------------------------------------------- */ + if( OGR_SWAP( eByteOrder ) ) + { + int nCount; + + nCount = CPL_SWAP32( nPointCount ); + memcpy( pabyData+5, &nCount, 4 ); + + for( i = getCoordinateDimension() * nPointCount - 1; i >= 0; i-- ) + { + CPL_SWAP64PTR( pabyData + 9 + 8 * i ); + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importFromWkt() */ +/* */ +/* Instantiate from well known text format. Currently this is */ +/* `LINESTRING ( x y, x y, ...)', */ +/************************************************************************/ + +OGRErr OGRSimpleCurve::importFromWkt( char ** ppszInput ) + +{ + int bHasZ = FALSE, bHasM = FALSE; + OGRErr eErr = importPreambuleFromWkt(ppszInput, &bHasZ, &bHasM); + if( eErr >= 0 ) + return eErr; + + const char *pszInput = *ppszInput; + +/* -------------------------------------------------------------------- */ +/* Read the point list. */ +/* -------------------------------------------------------------------- */ + nPointCount = 0; + + int nMaxPoints = 0; + pszInput = OGRWktReadPoints( pszInput, &paoPoints, &padfZ, &nMaxPoints, + &nPointCount ); + if( pszInput == NULL ) + return OGRERR_CORRUPT_DATA; + + *ppszInput = (char *) pszInput; + + if( padfZ == NULL ) + nCoordDimension = 2; + else + { + /* Ignore Z array when we have a LINESTRING M */ + if (bHasM && !bHasZ) + { + nCoordDimension = 2; + CPLFree(padfZ); + padfZ = NULL; + } + else + nCoordDimension = 3; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importFromWKTListOnly() */ +/* */ +/* Instantiate from "(x y, x y, ...)" */ +/************************************************************************/ + +OGRErr OGRSimpleCurve::importFromWKTListOnly( char ** ppszInput, int bHasZ, int bHasM, + OGRRawPoint*& paoPointsIn, int& nMaxPointsIn, + double*& padfZIn ) + +{ + const char *pszInput = *ppszInput; + +/* -------------------------------------------------------------------- */ +/* Read the point list. */ +/* -------------------------------------------------------------------- */ + int nPointCountRead = 0; + + pszInput = OGRWktReadPoints( pszInput, &paoPointsIn, &padfZIn, &nMaxPointsIn, + &nPointCountRead ); + + if( pszInput == NULL ) + return OGRERR_CORRUPT_DATA; + + *ppszInput = (char *) pszInput; + + /* Ignore Z array when we have a POLYGON M */ + if (bHasM && !bHasZ) + setPoints( nPointCountRead, paoPointsIn, NULL ); + else + setPoints( nPointCountRead, paoPointsIn, padfZIn ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* exportToWkt() */ +/* */ +/* Translate this structure into it's well known text format */ +/* equivelent. This could be made alot more CPU efficient! */ +/************************************************************************/ + +OGRErr OGRSimpleCurve::exportToWkt( char ** ppszDstText, + OGRwkbVariant eWkbVariant ) const + +{ + int nMaxString = nPointCount * 40 * 3 + 25; + int nRetLen = 0; + +/* -------------------------------------------------------------------- */ +/* Handle special empty case. */ +/* -------------------------------------------------------------------- */ + if( IsEmpty() ) + { + CPLString osEmpty; + if( getCoordinateDimension() == 3 && eWkbVariant == wkbVariantIso ) + osEmpty.Printf("%s Z EMPTY",getGeometryName()); + else + osEmpty.Printf("%s EMPTY",getGeometryName()); + *ppszDstText = CPLStrdup(osEmpty); + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* General case. */ +/* -------------------------------------------------------------------- */ + *ppszDstText = (char *) VSIMalloc( nMaxString ); + if( *ppszDstText == NULL ) + return OGRERR_NOT_ENOUGH_MEMORY; + + if( getCoordinateDimension() == 3 && eWkbVariant == wkbVariantIso ) + sprintf( *ppszDstText, "%s Z (", getGeometryName() ); + else + sprintf( *ppszDstText, "%s (", getGeometryName() ); + + for( int i = 0; i < nPointCount; i++ ) + { + if( nMaxString <= (int) strlen(*ppszDstText+nRetLen) + 32 + nRetLen ) + { + CPLDebug( "OGR", + "OGRSimpleCurve::exportToWkt() ... buffer overflow.\n" + "nMaxString=%d, strlen(*ppszDstText) = %d, i=%d\n" + "*ppszDstText = %s", + nMaxString, (int) strlen(*ppszDstText), i, *ppszDstText ); + + VSIFree( *ppszDstText ); + *ppszDstText = NULL; + return OGRERR_NOT_ENOUGH_MEMORY; + } + + if( i > 0 ) + strcat( *ppszDstText + nRetLen, "," ); + + nRetLen += strlen(*ppszDstText + nRetLen); + if( getCoordinateDimension() == 3 ) + OGRMakeWktCoordinate( *ppszDstText + nRetLen, + paoPoints[i].x, + paoPoints[i].y, + padfZ[i], + nCoordDimension ); + else + OGRMakeWktCoordinate( *ppszDstText + nRetLen, + paoPoints[i].x, + paoPoints[i].y, + 0.0, + nCoordDimension ); + + nRetLen += strlen(*ppszDstText + nRetLen); + } + + strcat( *ppszDstText+nRetLen, ")" ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* get_Length() */ +/* */ +/* For now we return a simple euclidian 2D distance. */ +/************************************************************************/ + +double OGRSimpleCurve::get_Length() const + +{ + double dfLength = 0; + int i; + + for( i = 0; i < nPointCount-1; i++ ) + { + double dfDeltaX, dfDeltaY; + + dfDeltaX = paoPoints[i+1].x - paoPoints[i].x; + dfDeltaY = paoPoints[i+1].y - paoPoints[i].y; + dfLength += sqrt(dfDeltaX*dfDeltaX + dfDeltaY*dfDeltaY); + } + + return dfLength; +} + +/************************************************************************/ +/* StartPoint() */ +/************************************************************************/ + +void OGRSimpleCurve::StartPoint( OGRPoint * poPoint ) const + +{ + getPoint( 0, poPoint ); +} + +/************************************************************************/ +/* EndPoint() */ +/************************************************************************/ + +void OGRSimpleCurve::EndPoint( OGRPoint * poPoint ) const + +{ + getPoint( nPointCount-1, poPoint ); +} + +/************************************************************************/ +/* Value() */ +/* */ +/* Get an interpolated point at some distance along the curve. */ +/************************************************************************/ + +void OGRSimpleCurve::Value( double dfDistance, OGRPoint * poPoint ) const + +{ + double dfLength = 0; + int i; + + if( dfDistance < 0 ) + { + StartPoint( poPoint ); + return; + } + + for( i = 0; i < nPointCount-1; i++ ) + { + double dfDeltaX, dfDeltaY, dfSegLength; + + dfDeltaX = paoPoints[i+1].x - paoPoints[i].x; + dfDeltaY = paoPoints[i+1].y - paoPoints[i].y; + dfSegLength = sqrt(dfDeltaX*dfDeltaX + dfDeltaY*dfDeltaY); + + if (dfSegLength > 0) + { + if( (dfLength <= dfDistance) && ((dfLength + dfSegLength) >= + dfDistance) ) + { + double dfRatio; + + dfRatio = (dfDistance - dfLength) / dfSegLength; + + poPoint->setX( paoPoints[i].x * (1 - dfRatio) + + paoPoints[i+1].x * dfRatio ); + poPoint->setY( paoPoints[i].y * (1 - dfRatio) + + paoPoints[i+1].y * dfRatio ); + + if( getCoordinateDimension() == 3 ) + poPoint->setZ( padfZ[i] * (1 - dfRatio) + + padfZ[i+1] * dfRatio ); + + return; + } + + dfLength += dfSegLength; + } + } + + EndPoint( poPoint ); +} + +/************************************************************************/ +/* Project() */ +/* */ +/* Return distance of point projected on line from origin of this line */ +/************************************************************************/ + +/** +* \brief Project point on linestring. +* +* The input point projeted on linestring. This is the shortest distance +* from point to the linestring. The distance from begin of linestring to +* the point projection returned. +* +* This method is built on the GEOS library (GEOS >= 3.2.0), check it for the +* definition of the geometry operation. +* If OGR is built without the GEOS library, this method will always return -1, +* issuing a CPLE_NotSupported error. +* +* @return a distance from the begin of the linestring to the projected point. +* +* @since OGR 1.11.0 +*/ + +/* GEOS >= 3.2.0 for project capabilty */ +#if defined(HAVE_GEOS) +#if GEOS_VERSION_MAJOR > 3 || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 2) +#define HAVE_GEOS_PROJECT +#endif +#endif + + +double OGRSimpleCurve::Project(const OGRPoint *poPoint) const + +{ + double dfResult = -1; +#ifndef HAVE_GEOS_PROJECT + + + CPLError(CE_Failure, CPLE_NotSupported, + "GEOS support not enabled."); + return dfResult; + +#else + GEOSGeom hThisGeosGeom = NULL; + GEOSGeom hPointGeosGeom = NULL; + + GEOSContextHandle_t hGEOSCtxt = createGEOSContext(); + hThisGeosGeom = exportToGEOS(hGEOSCtxt); + hPointGeosGeom = poPoint->exportToGEOS(hGEOSCtxt); + if (hThisGeosGeom != NULL && hPointGeosGeom != NULL) + { + dfResult = GEOSProject_r(hGEOSCtxt, hThisGeosGeom, hPointGeosGeom); + } + GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom); + GEOSGeom_destroy_r(hGEOSCtxt, hPointGeosGeom); + freeGEOSContext(hGEOSCtxt); + + return dfResult; + +#endif /* HAVE_GEOS */ +} + +/************************************************************************/ +/* getSubLine() */ +/* */ +/* Extracts a portion of this OGRLineString into a new OGRLineString */ +/************************************************************************/ + + +/** +* \brief Get the portion of linestring. +* +* The portion of the linestring extracted to new one. The input distances +* (maybe present as ratio of length of linestring) set begin and end of +* extracted portion. +* +* @param dfDistanceFrom The distance from the origin of linestring, where the subline should begins +* @param dfDistanceTo The distance from the origin of linestring, where the subline should ends +* @param bAsRatio The flag indicating that distances are the ratio of the linestring length. +* +* @return a newly allocated linestring now owned by the caller, or NULL on failure. +* +* @since OGR 1.11.0 +*/ + + +OGRLineString* OGRSimpleCurve::getSubLine(double dfDistanceFrom, double dfDistanceTo, int bAsRatio) const + +{ + OGRLineString *poNewLineString; + double dfLength = 0; + int i; + + poNewLineString = new OGRLineString(); + + poNewLineString->assignSpatialReference(getSpatialReference()); + //poNewLineString->setPoints(nPointCount, paoPoints, padfZ); + poNewLineString->setCoordinateDimension(getCoordinateDimension()); + + double dfLen = get_Length(); + if (bAsRatio == TRUE) + { + //convert to real distance + dfDistanceFrom *= dfLen; + dfDistanceTo *= dfLen; + } + + if (dfDistanceFrom < 0) + dfDistanceFrom = 0; + if (dfDistanceTo > dfLen) + dfDistanceTo = dfLen; + + if ( dfDistanceFrom > dfDistanceTo || dfDistanceFrom >= dfLen) + { + CPLError(CE_Failure, CPLE_IllegalArg, "Input distances are invalid."); + + return NULL; + } + + //get first point + + if (dfDistanceFrom == 0) + { + if (getCoordinateDimension() == 3) + poNewLineString->addPoint(paoPoints[0].x, paoPoints[0].y, padfZ[0]); + else + poNewLineString->addPoint(paoPoints[0].x, paoPoints[0].y); + + i = 0; + } + else + { + for (i = 0; i < nPointCount - 1; i++) + { + double dfDeltaX, dfDeltaY, dfSegLength; + + dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x; + dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y; + dfSegLength = sqrt(dfDeltaX*dfDeltaX + dfDeltaY*dfDeltaY); + + double dfX, dfY, dfRatio; + + if (dfSegLength > 0) + { + if ((dfLength <= dfDistanceFrom) && ((dfLength + dfSegLength) >= + dfDistanceFrom)) + { + dfRatio = (dfDistanceFrom - dfLength) / dfSegLength; + + dfX = paoPoints[i].x * (1 - dfRatio) + + paoPoints[i + 1].x * dfRatio; + dfY = paoPoints[i].y * (1 - dfRatio) + + paoPoints[i + 1].y * dfRatio; + + if (getCoordinateDimension() == 3) + { + poNewLineString->addPoint(dfX, dfY, padfZ[i] * (1 - dfRatio) + + padfZ[i+1] * dfRatio); + } + else + { + poNewLineString->addPoint(dfX, dfY); + } + + //check if dfDistanceTo is in same segment + if ((dfLength <= dfDistanceTo) && ((dfLength + dfSegLength) >= + dfDistanceTo)) + { + dfRatio = (dfDistanceTo - dfLength) / dfSegLength; + + dfX = paoPoints[i].x * (1 - dfRatio) + + paoPoints[i + 1].x * dfRatio; + dfY = paoPoints[i].y * (1 - dfRatio) + + paoPoints[i + 1].y * dfRatio; + + if (getCoordinateDimension() == 3) + { + poNewLineString->addPoint(dfX, dfY, padfZ[i] * (1 - dfRatio) + + padfZ[i + 1] * dfRatio); + } + else + { + poNewLineString->addPoint(dfX, dfY); + } + + if (poNewLineString->getNumPoints() < 2) + { + delete poNewLineString; + poNewLineString = NULL; + } + + return poNewLineString; + } + i++; + dfLength += dfSegLength; + break; + } + + dfLength += dfSegLength; + } + } + } + + //add points + for (; i < nPointCount - 1; i++) + { + double dfDeltaX, dfDeltaY, dfSegLength; + + if (getCoordinateDimension() == 3) + poNewLineString->addPoint(paoPoints[i].x, paoPoints[i].y, padfZ[i]); + else + poNewLineString->addPoint(paoPoints[i].x, paoPoints[i].y); + + dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x; + dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y; + dfSegLength = sqrt(dfDeltaX*dfDeltaX + dfDeltaY*dfDeltaY); + + double dfX, dfY, dfRatio; + + if (dfSegLength > 0) + { + if ((dfLength <= dfDistanceTo) && ((dfLength + dfSegLength) >= + dfDistanceTo)) + { + dfRatio = (dfDistanceTo - dfLength) / dfSegLength; + + dfX = paoPoints[i].x * (1 - dfRatio) + + paoPoints[i + 1].x * dfRatio; + dfY = paoPoints[i].y * (1 - dfRatio) + + paoPoints[i + 1].y * dfRatio; + + if (getCoordinateDimension() == 3) + poNewLineString->addPoint(dfX, dfY, padfZ[i] * (1 - dfRatio) + + padfZ[i + 1] * dfRatio); + else + poNewLineString->addPoint(dfX, dfY); + + return poNewLineString; + } + + dfLength += dfSegLength; + } + } + + + if (getCoordinateDimension() == 3) + poNewLineString->addPoint(paoPoints[nPointCount - 1].x, paoPoints[nPointCount - 1].y, padfZ[nPointCount - 1]); + else + poNewLineString->addPoint(paoPoints[nPointCount - 1].x, paoPoints[nPointCount - 1].y); + + if (poNewLineString->getNumPoints() < 2) + { + delete poNewLineString; + poNewLineString = NULL; + } + + return poNewLineString; +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRSimpleCurve::getEnvelope( OGREnvelope * psEnvelope ) const + +{ + double dfMinX, dfMinY, dfMaxX, dfMaxY; + + if( IsEmpty() ) + { + psEnvelope->MinX = 0; + psEnvelope->MaxX = 0; + psEnvelope->MinY = 0; + psEnvelope->MaxY = 0; + return; + } + + dfMinX = dfMaxX = paoPoints[0].x; + dfMinY = dfMaxY = paoPoints[0].y; + + for( int iPoint = 1; iPoint < nPointCount; iPoint++ ) + { + if( dfMaxX < paoPoints[iPoint].x ) + dfMaxX = paoPoints[iPoint].x; + if( dfMaxY < paoPoints[iPoint].y ) + dfMaxY = paoPoints[iPoint].y; + if( dfMinX > paoPoints[iPoint].x ) + dfMinX = paoPoints[iPoint].x; + if( dfMinY > paoPoints[iPoint].y ) + dfMinY = paoPoints[iPoint].y; + } + + psEnvelope->MinX = dfMinX; + psEnvelope->MaxX = dfMaxX; + psEnvelope->MinY = dfMinY; + psEnvelope->MaxY = dfMaxY; +} + + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRSimpleCurve::getEnvelope( OGREnvelope3D * psEnvelope ) const + +{ + getEnvelope((OGREnvelope*)psEnvelope); + + double dfMinZ, dfMaxZ; + + if( IsEmpty() || padfZ == NULL ) + { + psEnvelope->MinZ = 0; + psEnvelope->MaxZ = 0; + return; + } + + dfMinZ = dfMaxZ = padfZ[0]; + + for( int iPoint = 1; iPoint < nPointCount; iPoint++ ) + { + if( dfMinZ > padfZ[iPoint] ) + dfMinZ = padfZ[iPoint]; + if( dfMaxZ < padfZ[iPoint] ) + dfMaxZ = padfZ[iPoint]; + } + + psEnvelope->MinZ = dfMinZ; + psEnvelope->MaxZ = dfMaxZ; +} + +/************************************************************************/ +/* Equals() */ +/************************************************************************/ + +OGRBoolean OGRSimpleCurve::Equals( OGRGeometry * poOther ) const + +{ + OGRLineString *poOLine = (OGRLineString *) poOther; + + if( poOLine == this ) + return TRUE; + + if( poOther->getGeometryType() != getGeometryType() ) + return FALSE; + + if( IsEmpty() && poOther->IsEmpty() ) + return TRUE; + + // we should eventually test the SRS. + + if( getNumPoints() != poOLine->getNumPoints() ) + return FALSE; + + for( int iPoint = 0; iPoint < getNumPoints(); iPoint++ ) + { + if( getX(iPoint) != poOLine->getX(iPoint) + || getY(iPoint) != poOLine->getY(iPoint) + || getZ(iPoint) != poOLine->getZ(iPoint) ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* transform() */ +/************************************************************************/ + +OGRErr OGRSimpleCurve::transform( OGRCoordinateTransformation *poCT ) + +{ +#ifdef DISABLE_OGRGEOM_TRANSFORM + return OGRERR_FAILURE; +#else + double *xyz; + int *pabSuccess; + int i, j; + +/* -------------------------------------------------------------------- */ +/* Make a copy of the points to operate on, so as to be able to */ +/* keep only valid reprojected points if partial reprojection enabled */ +/* or keeping intact the original geometry if only full reprojection */ +/* allowed. */ +/* -------------------------------------------------------------------- */ + xyz = (double *) VSIMalloc(sizeof(double) * nPointCount * 3); + pabSuccess = (int *) VSICalloc(sizeof(int), nPointCount); + if( xyz == NULL || pabSuccess == NULL ) + { + VSIFree(xyz); + VSIFree(pabSuccess); + return OGRERR_NOT_ENOUGH_MEMORY; + } + + for( i = 0; i < nPointCount; i++ ) + { + xyz[i ] = paoPoints[i].x; + xyz[i+nPointCount] = paoPoints[i].y; + if( padfZ ) + xyz[i+nPointCount*2] = padfZ[i]; + else + xyz[i+nPointCount*2] = 0.0; + } + +/* -------------------------------------------------------------------- */ +/* Transform and reapply. */ +/* -------------------------------------------------------------------- */ + poCT->TransformEx( nPointCount, xyz, xyz + nPointCount, + xyz+nPointCount*2, pabSuccess ); + + const char* pszEnablePartialReprojection = NULL; + + for( i = 0, j = 0; i < nPointCount; i++ ) + { + if (pabSuccess[i]) + { + xyz[j] = xyz[i]; + xyz[j+nPointCount] = xyz[i+nPointCount]; + xyz[j+2*nPointCount] = xyz[i+2*nPointCount]; + j ++; + } + else + { + if (pszEnablePartialReprojection == NULL) + pszEnablePartialReprojection = + CPLGetConfigOption("OGR_ENABLE_PARTIAL_REPROJECTION", NULL); + if (pszEnablePartialReprojection == NULL) + { + static int bHasWarned = FALSE; + if (!bHasWarned) + { + /* Check that there is at least one valid reprojected point */ + /* and issue an error giving an hint to use OGR_ENABLE_PARTIAL_REPROJECTION */ + int bHasOneValidPoint = (j != 0); + for( ; i < nPointCount && !bHasOneValidPoint; i++ ) + { + if (pabSuccess[i]) + bHasOneValidPoint = TRUE; + } + if (bHasOneValidPoint) + { + bHasWarned = TRUE; + CPLError(CE_Failure, CPLE_AppDefined, + "Full reprojection failed, but partial is possible if you define " + "OGR_ENABLE_PARTIAL_REPROJECTION configuration option to TRUE"); + } + } + + CPLFree( xyz ); + CPLFree( pabSuccess ); + return OGRERR_FAILURE; + } + else if (!CSLTestBoolean(pszEnablePartialReprojection)) + { + CPLFree( xyz ); + CPLFree( pabSuccess ); + return OGRERR_FAILURE; + } + } + } + + if (j == 0 && nPointCount != 0) + { + CPLFree( xyz ); + CPLFree( pabSuccess ); + return OGRERR_FAILURE; + } + + setPoints( j, xyz, xyz+nPointCount, + ( padfZ ) ? xyz+nPointCount*2 : NULL); + CPLFree( xyz ); + CPLFree( pabSuccess ); + + assignSpatialReference( poCT->GetTargetCS() ); + + return OGRERR_NONE; +#endif +} + +/************************************************************************/ +/* IsEmpty() */ +/************************************************************************/ + +OGRBoolean OGRSimpleCurve::IsEmpty( ) const +{ + return (nPointCount == 0); +} + +/************************************************************************/ +/* OGRSimpleCurve::segmentize() */ +/************************************************************************/ + +void OGRSimpleCurve::segmentize( double dfMaxLength ) +{ + if (dfMaxLength <= 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "dfMaxLength must be strictly positive"); + return; + } + if( nPointCount < 2 ) + return; + + /* So as to make sure that the same line followed in both directions */ + /* result in the same segmentized line */ + if ( paoPoints[0].x < paoPoints[nPointCount - 1].x || + (paoPoints[0].x == paoPoints[nPointCount - 1].x && + paoPoints[0].y < paoPoints[nPointCount - 1].y) ) + { + reversePoints(); + segmentize(dfMaxLength); + reversePoints(); + } + + int i; + OGRRawPoint* paoNewPoints = NULL; + double* padfNewZ = NULL; + int nNewPointCount = 0; + double dfSquareMaxLength = dfMaxLength * dfMaxLength; + const int nCoordinateDimension = getCoordinateDimension(); + + for( i = 0; i < nPointCount; i++ ) + { + paoNewPoints = (OGRRawPoint *) + OGRRealloc(paoNewPoints, sizeof(OGRRawPoint) * (nNewPointCount + 1)); + paoNewPoints[nNewPointCount] = paoPoints[i]; + + if( nCoordinateDimension == 3 ) + { + padfNewZ = (double *) + OGRRealloc(padfNewZ, sizeof(double) * (nNewPointCount + 1)); + padfNewZ[nNewPointCount] = padfZ[i]; + } + + nNewPointCount++; + + if (i == nPointCount - 1) + break; + + double dfX = paoPoints[i+1].x - paoPoints[i].x; + double dfY = paoPoints[i+1].y - paoPoints[i].y; + double dfSquareDist = dfX * dfX + dfY * dfY; + if (dfSquareDist > dfSquareMaxLength) + { + int nIntermediatePoints = (int)floor(sqrt(dfSquareDist / dfSquareMaxLength)); + int j; + + paoNewPoints = (OGRRawPoint *) + OGRRealloc(paoNewPoints, sizeof(OGRRawPoint) * (nNewPointCount + nIntermediatePoints)); + if( nCoordinateDimension == 3 ) + { + padfNewZ = (double *) + OGRRealloc(padfNewZ, sizeof(double) * (nNewPointCount + nIntermediatePoints)); + } + + for(j=1;j<=nIntermediatePoints;j++) + { + paoNewPoints[nNewPointCount + j - 1].x = paoPoints[i].x + j * dfX / (nIntermediatePoints + 1); + paoNewPoints[nNewPointCount + j - 1].y = paoPoints[i].y + j * dfY / (nIntermediatePoints + 1); + if( nCoordinateDimension == 3 ) + { + /* No interpolation */ + padfNewZ[nNewPointCount + j - 1] = padfZ[i]; + } + } + + nNewPointCount += nIntermediatePoints; + } + } + + OGRFree(paoPoints); + paoPoints = paoNewPoints; + nPointCount = nNewPointCount; + + if( nCoordinateDimension == 3 ) + { + OGRFree(padfZ); + padfZ = padfNewZ; + } +} + +/************************************************************************/ +/* swapXY() */ +/************************************************************************/ + +void OGRSimpleCurve::swapXY() +{ + int i; + for( i = 0; i < nPointCount; i++ ) + { + double dfTemp = paoPoints[i].x; + paoPoints[i].x = paoPoints[i].y; + paoPoints[i].y = dfTemp; + } +} + +/************************************************************************/ +/* OGRSimpleCurvePointIterator */ +/************************************************************************/ + +class OGRSimpleCurvePointIterator: public OGRPointIterator +{ + const OGRSimpleCurve* poSC; + int iCurPoint; + + public: + OGRSimpleCurvePointIterator(const OGRSimpleCurve* poSC) : poSC(poSC), iCurPoint(0) {} + + virtual OGRBoolean getNextPoint(OGRPoint* p); +}; + +/************************************************************************/ +/* getNextPoint() */ +/************************************************************************/ + +OGRBoolean OGRSimpleCurvePointIterator::getNextPoint(OGRPoint* p) +{ + if( iCurPoint >= poSC->getNumPoints() ) + return FALSE; + poSC->getPoint(iCurPoint, p); + iCurPoint ++; + return TRUE; +} + +/************************************************************************/ +/* getPointIterator() */ +/************************************************************************/ + +OGRPointIterator* OGRSimpleCurve::getPointIterator() const +{ + return new OGRSimpleCurvePointIterator(this); +} + +/************************************************************************/ +/* OGRLineString() */ +/************************************************************************/ + +/** + * \brief Create an empty line string. + */ + +OGRLineString::OGRLineString() + +{ +} + +/************************************************************************/ +/* ~OGRLineString() */ +/************************************************************************/ + +OGRLineString::~OGRLineString() + +{ +} + +/************************************************************************/ +/* getGeometryType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRLineString::getGeometryType() const + +{ + if( nCoordDimension == 3 ) + return wkbLineString25D; + else + return wkbLineString; +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRLineString::getGeometryName() const + +{ + return "LINESTRING"; +} + +/************************************************************************/ +/* curveToLine() */ +/************************************************************************/ + +OGRLineString* OGRLineString::CurveToLine(CPL_UNUSED double dfMaxAngleStepSizeDegrees, + CPL_UNUSED const char* const* papszOptions) const +{ + return (OGRLineString*)clone(); +} + +/************************************************************************/ +/* get_LinearArea() */ +/************************************************************************/ + +/** + * \brief Compute area of ring / closed linestring. + * + * The area is computed according to Green's Theorem: + * + * Area is "Sum(x(i)*(y(i+1) - y(i-1)))/2" for i = 0 to pointCount-1, + * assuming the last point is a duplicate of the first. + * + * @return computed area. + */ + +double OGRSimpleCurve::get_LinearArea() const + +{ + double dfAreaSum; + int i; + + if( nPointCount < 2 ) + return 0; + + dfAreaSum = paoPoints[0].x * (paoPoints[1].y - paoPoints[nPointCount-1].y); + + for( i = 1; i < nPointCount-1; i++ ) + { + dfAreaSum += paoPoints[i].x * (paoPoints[i+1].y - paoPoints[i-1].y); + } + + dfAreaSum += paoPoints[nPointCount-1].x * (paoPoints[0].y - paoPoints[nPointCount-2].y); + + return 0.5 * fabs(dfAreaSum); +} + +/************************************************************************/ +/* getCurveGeometry() */ +/************************************************************************/ + +OGRGeometry* OGRLineString::getCurveGeometry(const char* const* papszOptions) const +{ + return OGRGeometryFactory::curveFromLineString(this, papszOptions); +} + +/************************************************************************/ +/* TransferMembersAndDestroy() */ +/************************************************************************/ + +OGRLineString* OGRLineString::TransferMembersAndDestroy( + OGRLineString* poSrc, + OGRLineString* poDst) +{ + poDst->setCoordinateDimension(poSrc->getCoordinateDimension()); + poDst->assignSpatialReference(poSrc->getSpatialReference()); + poDst->nPointCount = poSrc->nPointCount; + poDst->paoPoints = poSrc->paoPoints; + poDst->padfZ = poSrc->padfZ; + poSrc->nPointCount = 0; + poSrc->paoPoints = NULL; + poSrc->padfZ = NULL; + delete poSrc; + return poDst; +} + +/************************************************************************/ +/* CastToLinearRing() */ +/************************************************************************/ + +/** + * \brief Cast to linear ring. + * + * The passed in geometry is consumed and a new one returned (or NULL in case + * of failure) + * + * @param poLS the input geometry - ownership is passed to the method. + * @return new geometry. + */ + +OGRLinearRing* OGRLineString::CastToLinearRing(OGRLineString* poLS) +{ + if( poLS->nPointCount < 2 || !poLS->get_IsClosed() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot convert non-closed linestring to linearring"); + delete poLS; + return NULL; + } + return (OGRLinearRing*)TransferMembersAndDestroy(poLS, new OGRLinearRing()); +} + +/************************************************************************/ +/* GetCasterToLineString() */ +/************************************************************************/ + +OGRCurveCasterToLineString OGRLineString::GetCasterToLineString() const { + return (OGRCurveCasterToLineString) OGRGeometry::CastToIdentity; +} + +/************************************************************************/ +/* GetCasterToLinearRing() */ +/************************************************************************/ + +OGRCurveCasterToLinearRing OGRLineString::GetCasterToLinearRing() const { + return (OGRCurveCasterToLinearRing) OGRLineString::CastToLinearRing; +} + +/************************************************************************/ +/* get_Area() */ +/************************************************************************/ + +double OGRLineString::get_Area() const +{ + return get_LinearArea(); +} + +/************************************************************************/ +/* get_AreaOfCurveSegments() */ +/************************************************************************/ + +double OGRLineString::get_AreaOfCurveSegments() const +{ + return 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrmulticurve.cpp b/bazaar/plugin/gdal/ogr/ogrmulticurve.cpp new file mode 100644 index 000000000..274f3f150 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrmulticurve.cpp @@ -0,0 +1,178 @@ +/****************************************************************************** + * $Id: ogrmulticurve.cpp 27960 2014-11-14 18:31:32Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRMultiCurve class. + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" +#include "ogr_api.h" + +CPL_CVSID("$Id: ogrmulticurve.cpp 27960 2014-11-14 18:31:32Z rouault $"); + +/************************************************************************/ +/* OGRMultiCurve() */ +/************************************************************************/ + +/** + * \brief Create an empty multi curve collection. + */ + +OGRMultiCurve::OGRMultiCurve() +{ +} + +/************************************************************************/ +/* ~OGRMultiCurve() */ +/************************************************************************/ + +OGRMultiCurve::~OGRMultiCurve() +{ +} + +/************************************************************************/ +/* getGeometryType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRMultiCurve::getGeometryType() const + +{ + if( getCoordinateDimension() == 3 ) + return wkbMultiCurveZ; + else + return wkbMultiCurve; +} + +/************************************************************************/ +/* getDimension() */ +/************************************************************************/ + +int OGRMultiCurve::getDimension() const + +{ + return 1; +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRMultiCurve::getGeometryName() const + +{ + return "MULTICURVE"; +} + +/************************************************************************/ +/* isCompatibleSubType() */ +/************************************************************************/ + +OGRBoolean OGRMultiCurve::isCompatibleSubType( OGRwkbGeometryType eGeomType ) const +{ + return OGR_GT_IsCurve(eGeomType); +} + +/************************************************************************/ +/* addCurveDirectlyFromWkt() */ +/************************************************************************/ + +OGRErr OGRMultiCurve::addCurveDirectlyFromWkt( OGRGeometry* poSelf, OGRCurve* poCurve ) +{ + return ((OGRMultiCurve*)poSelf)->addGeometryDirectly(poCurve); +} + +/************************************************************************/ +/* importFromWkt() */ +/* */ +/* Instantiate from well known text format. */ +/************************************************************************/ + +OGRErr OGRMultiCurve::importFromWkt( char ** ppszInput ) + +{ + int bIsMultiCurve = (wkbFlatten(getGeometryType()) == wkbMultiCurve); + return importCurveCollectionFromWkt( ppszInput, + TRUE, /* bAllowEmptyComponent */ + bIsMultiCurve, /* bAllowLineString */ + bIsMultiCurve, /* bAllowCurve */ + bIsMultiCurve, /* bAllowCompoundCurve */ + addCurveDirectlyFromWkt ); +} + +/************************************************************************/ +/* exportToWkt() */ +/************************************************************************/ + +OGRErr OGRMultiCurve::exportToWkt( char ** ppszDstText, + CPL_UNUSED OGRwkbVariant eWkbVariant ) const + +{ + return exportToWktInternal( ppszDstText, wkbVariantIso, "LINESTRING" ); +} + +/************************************************************************/ +/* hasCurveGeometry() */ +/************************************************************************/ + +OGRBoolean OGRMultiCurve::hasCurveGeometry(int bLookForNonLinear) const +{ + if( bLookForNonLinear ) + return OGRGeometryCollection::hasCurveGeometry(TRUE); + return TRUE; +} + +/************************************************************************/ +/* CastToMultiLineString() */ +/************************************************************************/ + +/** + * \brief Cast to multi line string. + * + * This method should only be called if the multicurve actually only contains + * instances of OGRLineString. This can be verified if hasCurveGeometry(TRUE) + * returns FALSE. It is not intended to approximate circular curves. For that + * use getLinearGeometry(). + * + * The passed in geometry is consumed and a new one returned (or NULL in case + * of failure). + * + * @param poMS the input geometry - ownership is passed to the method. + * @return new geometry. + */ + +OGRMultiLineString* OGRMultiCurve::CastToMultiLineString(OGRMultiCurve* poMC) +{ + for(int i=0;inGeomCount;i++) + { + poMC->papoGeoms[i] = OGRCurve::CastToLineString( (OGRCurve*)poMC->papoGeoms[i] ); + if( poMC->papoGeoms[i] == NULL ) + { + delete poMC; + return NULL; + } + } + return (OGRMultiLineString*) TransferMembersAndDestroy(poMC, new OGRMultiLineString()); +} diff --git a/bazaar/plugin/gdal/ogr/ogrmultilinestring.cpp b/bazaar/plugin/gdal/ogr/ogrmultilinestring.cpp new file mode 100644 index 000000000..3273455a5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrmultilinestring.cpp @@ -0,0 +1,124 @@ +/****************************************************************************** + * $Id: ogrmultilinestring.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRMultiLineString class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrmultilinestring.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +/************************************************************************/ +/* OGRMultiLineString() */ +/************************************************************************/ + +/** + * \brief Create an empty multi line string collection. + */ + +OGRMultiLineString::OGRMultiLineString() +{ +} + +/************************************************************************/ +/* ~OGRMultiLineString() */ +/************************************************************************/ + +OGRMultiLineString::~OGRMultiLineString() +{ +} + +/************************************************************************/ +/* getGeometryType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRMultiLineString::getGeometryType() const + +{ + if( getCoordinateDimension() == 3 ) + return wkbMultiLineString25D; + else + return wkbMultiLineString; +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRMultiLineString::getGeometryName() const + +{ + return "MULTILINESTRING"; +} + +/************************************************************************/ +/* isCompatibleSubType() */ +/************************************************************************/ + +OGRBoolean OGRMultiLineString::isCompatibleSubType( OGRwkbGeometryType eGeomType ) const +{ + return wkbFlatten(eGeomType) == wkbLineString; +} + +/************************************************************************/ +/* exportToWkt() */ +/************************************************************************/ + +OGRErr OGRMultiLineString::exportToWkt( char ** ppszDstText, + OGRwkbVariant eWkbVariant ) const + +{ + return exportToWktInternal( ppszDstText, eWkbVariant, "LINESTRING" ); +} + +/************************************************************************/ +/* hasCurveGeometry() */ +/************************************************************************/ + +OGRBoolean OGRMultiLineString::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const +{ + return FALSE; +} + +/************************************************************************/ +/* CastToMultiCurve() */ +/************************************************************************/ + +/** + * \brief Cast to multicurve. + * + * The passed in geometry is consumed and a new one returned . + * + * @param poMLS the input geometry - ownership is passed to the method. + * @return new geometry. + */ + +OGRMultiCurve* OGRMultiLineString::CastToMultiCurve(OGRMultiLineString* poMLS) +{ + return (OGRMultiCurve*) TransferMembersAndDestroy(poMLS, new OGRMultiCurve()); +} diff --git a/bazaar/plugin/gdal/ogr/ogrmultipoint.cpp b/bazaar/plugin/gdal/ogr/ogrmultipoint.cpp new file mode 100644 index 000000000..88f1640fa --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrmultipoint.cpp @@ -0,0 +1,362 @@ +/****************************************************************************** + * $Id: ogrmultipoint.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRMultiPoint class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" +#include + +CPL_CVSID("$Id: ogrmultipoint.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +/************************************************************************/ +/* OGRMultiPoint() */ +/************************************************************************/ + +/** + * \brief Create an empty multi point collection. + */ + +OGRMultiPoint::OGRMultiPoint() +{ +} + +/************************************************************************/ +/* ~OGRMultiPoint() */ +/************************************************************************/ + +OGRMultiPoint::~OGRMultiPoint() +{ +} + +/************************************************************************/ +/* getGeometryType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRMultiPoint::getGeometryType() const + +{ + if( getCoordinateDimension() == 3 ) + return wkbMultiPoint25D; + else + return wkbMultiPoint; +} + +/************************************************************************/ +/* getDimension() */ +/************************************************************************/ + +int OGRMultiPoint::getDimension() const + +{ + return 0; +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRMultiPoint::getGeometryName() const + +{ + return "MULTIPOINT"; +} + +/************************************************************************/ +/* isCompatibleSubType() */ +/************************************************************************/ + +OGRBoolean OGRMultiPoint::isCompatibleSubType( OGRwkbGeometryType eGeomType ) const +{ + return wkbFlatten(eGeomType) == wkbPoint; +} + +/************************************************************************/ +/* exportToWkt() */ +/* */ +/* Translate this structure into it's well known text format */ +/* equivelent. This could be made alot more CPU efficient! */ +/************************************************************************/ + +OGRErr OGRMultiPoint::exportToWkt( char ** ppszDstText, + OGRwkbVariant eWkbVariant ) const + +{ + int nMaxString = getNumGeometries() * 22 + 128; + int nRetLen = 0; + +/* -------------------------------------------------------------------- */ +/* Return MULTIPOINT EMPTY if we get no valid points. */ +/* -------------------------------------------------------------------- */ + if( IsEmpty() ) + { + if( getCoordinateDimension() == 3 && eWkbVariant == wkbVariantIso ) + *ppszDstText = CPLStrdup("MULTIPOINT Z EMPTY"); + else + *ppszDstText = CPLStrdup("MULTIPOINT EMPTY"); + return OGRERR_NONE; + } + + *ppszDstText = (char *) VSIMalloc( nMaxString ); + if( *ppszDstText == NULL ) + return OGRERR_NOT_ENOUGH_MEMORY; + + if( getCoordinateDimension() == 3 && eWkbVariant == wkbVariantIso ) + sprintf( *ppszDstText, "%s Z (", getGeometryName() ); + else + sprintf( *ppszDstText, "%s (", getGeometryName() ); + + int bMustWriteComma = FALSE; + for( int i = 0; i < getNumGeometries(); i++ ) + { + OGRPoint *poPoint = (OGRPoint *) getGeometryRef( i ); + + if (poPoint->IsEmpty()) + { + CPLDebug( "OGR", "OGRMultiPoint::exportToWkt() - skipping POINT EMPTY."); + continue; + } + + if( bMustWriteComma ) + strcat( *ppszDstText + nRetLen, "," ); + bMustWriteComma = TRUE; + + nRetLen += strlen(*ppszDstText + nRetLen); + + if( nMaxString < nRetLen + 100 ) + { + nMaxString = nMaxString * 2; + *ppszDstText = (char *) CPLRealloc(*ppszDstText,nMaxString); + } + + if( eWkbVariant == wkbVariantIso ) + { + strcat( *ppszDstText + nRetLen, "(" ); + nRetLen ++; + } + + OGRMakeWktCoordinate( *ppszDstText + nRetLen, + poPoint->getX(), + poPoint->getY(), + poPoint->getZ(), + poPoint->getCoordinateDimension() ); + + if( eWkbVariant == wkbVariantIso ) + { + strcat( *ppszDstText + nRetLen, ")" ); + nRetLen ++; + } + } + + strcat( *ppszDstText+nRetLen, ")" ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importFromWkt() */ +/************************************************************************/ + +OGRErr OGRMultiPoint::importFromWkt( char ** ppszInput ) + +{ + const char *pszInputBefore = *ppszInput; + int bHasZ = FALSE, bHasM = FALSE; + OGRErr eErr = importPreambuleFromWkt(ppszInput, &bHasZ, &bHasM); + if( eErr >= 0 ) + return eErr; + + char szToken[OGR_WKT_TOKEN_MAX]; + const char *pszInput = *ppszInput; + int iGeom; + eErr = OGRERR_NONE; + + const char* pszPreScan = OGRWktReadToken( pszInput, szToken ); + OGRWktReadToken( pszPreScan, szToken ); + + // Do we have an inner bracket? + if (EQUAL(szToken,"(") || EQUAL(szToken, "EMPTY") ) + { + *ppszInput = (char*) pszInputBefore; + return importFromWkt_Bracketed( ppszInput, bHasM, bHasZ ); + } + + if (bHasZ || bHasM) + { + return OGRERR_CORRUPT_DATA; + } + +/* -------------------------------------------------------------------- */ +/* Read the point list which should consist of exactly one point. */ +/* -------------------------------------------------------------------- */ + int nMaxPoint = 0; + int nPointCount = 0; + OGRRawPoint *paoPoints = NULL; + double *padfZ = NULL; + + pszInput = OGRWktReadPoints( pszInput, &paoPoints, &padfZ, &nMaxPoint, + &nPointCount ); + if( pszInput == NULL ) + { + OGRFree( paoPoints ); + OGRFree( padfZ ); + return OGRERR_CORRUPT_DATA; + } + +/* -------------------------------------------------------------------- */ +/* Transform raw points into point objects. */ +/* -------------------------------------------------------------------- */ + for( iGeom = 0; iGeom < nPointCount && eErr == OGRERR_NONE; iGeom++ ) + { + OGRGeometry *poGeom; + if( padfZ ) + poGeom = new OGRPoint( paoPoints[iGeom].x, + paoPoints[iGeom].y, + padfZ[iGeom] ); + else + poGeom = new OGRPoint( paoPoints[iGeom].x, + paoPoints[iGeom].y ); + + eErr = addGeometryDirectly( poGeom ); + } + + OGRFree( paoPoints ); + if( padfZ ) + OGRFree( padfZ ); + + if( eErr != OGRERR_NONE ) + return eErr; + + *ppszInput = (char *) pszInput; + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* importFromWkt_Bracketed() */ +/* */ +/* This operates similar to importFromWkt(), but reads a format */ +/* with brackets around each point. This is the form defined */ +/* in the BNF of the SFSQL spec. It is called from */ +/* importFromWkt(). */ +/************************************************************************/ + +OGRErr OGRMultiPoint::importFromWkt_Bracketed( char ** ppszInput, int bHasM, int bHasZ ) + +{ + + char szToken[OGR_WKT_TOKEN_MAX]; + const char *pszInput = *ppszInput; + OGRErr eErr = OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* Skip MULTIPOINT keyword. */ +/* -------------------------------------------------------------------- */ + pszInput = OGRWktReadToken( pszInput, szToken ); + + if (bHasZ || bHasM) + { + /* Skip Z, M or ZM */ + pszInput = OGRWktReadToken( pszInput, szToken ); + } + +/* -------------------------------------------------------------------- */ +/* Read points till we get to the closing bracket. */ +/* -------------------------------------------------------------------- */ + int nMaxPoint = 0; + int nPointCount = 0; + OGRRawPoint *paoPoints = NULL; + double *padfZ = NULL; + + while( (pszInput = OGRWktReadToken( pszInput, szToken )) != NULL + && (EQUAL(szToken,"(") || EQUAL(szToken,",")) ) + { + OGRGeometry *poGeom; + + const char* pszNext = OGRWktReadToken( pszInput, szToken ); + if (EQUAL(szToken,"EMPTY")) + { + poGeom = new OGRPoint(0,0); + poGeom->empty(); + eErr = addGeometryDirectly( poGeom ); + if( eErr != OGRERR_NONE ) + return eErr; + + pszInput = pszNext; + + continue; + } + + pszInput = OGRWktReadPoints( pszInput, &paoPoints, &padfZ, &nMaxPoint, + &nPointCount ); + + if( pszInput == NULL || nPointCount != 1 ) + { + OGRFree( paoPoints ); + OGRFree( padfZ ); + return OGRERR_CORRUPT_DATA; + } + + /* Ignore Z array when we have a MULTIPOINT M */ + if( padfZ && !(bHasM && !bHasZ)) + poGeom = new OGRPoint( paoPoints[0].x, + paoPoints[0].y, + padfZ[0] ); + else + poGeom = new OGRPoint( paoPoints[0].x, + paoPoints[0].y ); + + eErr = addGeometryDirectly( poGeom ); + if( eErr != OGRERR_NONE ) + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + OGRFree( paoPoints ); + if( padfZ ) + OGRFree( padfZ ); + + if( !EQUAL(szToken,")") ) + return OGRERR_CORRUPT_DATA; + + *ppszInput = (char *) pszInput; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* hasCurveGeometry() */ +/************************************************************************/ + +OGRBoolean OGRMultiPoint::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const +{ + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrmultipolygon.cpp b/bazaar/plugin/gdal/ogr/ogrmultipolygon.cpp new file mode 100644 index 000000000..748ff3d3c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrmultipolygon.cpp @@ -0,0 +1,150 @@ +/****************************************************************************** + * $Id: ogrmultipolygon.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRMultiPolygon class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_api.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrmultipolygon.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +/************************************************************************/ +/* OGRMultiPolygon() */ +/************************************************************************/ + +/** + * \brief Create an empty multi polygon collection. + */ + +OGRMultiPolygon::OGRMultiPolygon() +{ +} + +/************************************************************************/ +/* ~OGRMultiPolygon() */ +/************************************************************************/ + +OGRMultiPolygon::~OGRMultiPolygon() +{ +} + +/************************************************************************/ +/* getGeometryType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRMultiPolygon::getGeometryType() const + +{ + if( getCoordinateDimension() == 3 ) + return wkbMultiPolygon25D; + else + return wkbMultiPolygon; +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRMultiPolygon::getGeometryName() const + +{ + return "MULTIPOLYGON"; +} + +/************************************************************************/ +/* isCompatibleSubType() */ +/************************************************************************/ + +OGRBoolean OGRMultiPolygon::isCompatibleSubType( OGRwkbGeometryType eGeomType ) const +{ + return wkbFlatten(eGeomType) == wkbPolygon; +} + +/************************************************************************/ +/* exportToWkt() */ +/************************************************************************/ + +OGRErr OGRMultiPolygon::exportToWkt( char ** ppszDstText, + OGRwkbVariant eWkbVariant ) const + +{ + return exportToWktInternal( ppszDstText, eWkbVariant, "POLYGON" ); +} + +/************************************************************************/ +/* hasCurveGeometry() */ +/************************************************************************/ + +OGRBoolean OGRMultiPolygon::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const +{ + return FALSE; +} + +/************************************************************************/ +/* PointOnSurface() */ +/************************************************************************/ + +OGRErr OGRMultiPolygon::PointOnSurface( OGRPoint * poPoint ) const +{ + if( poPoint == NULL || poPoint->IsEmpty() ) + return OGRERR_FAILURE; + + OGRGeometryH hInsidePoint = OGR_G_PointOnSurface( (OGRGeometryH) this ); + if( hInsidePoint == NULL ) + return OGRERR_FAILURE; + + OGRPoint *poInsidePoint = (OGRPoint *) hInsidePoint; + if( poInsidePoint->IsEmpty() ) + poPoint->empty(); + else + { + poPoint->setX( poInsidePoint->getX() ); + poPoint->setY( poInsidePoint->getY() ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CastToMultiSurface() */ +/************************************************************************/ + +/** + * \brief Cast to multisurface. + * + * The passed in geometry is consumed and a new one returned . + * + * @param poMP the input geometry - ownership is passed to the method. + * @return new geometry. + */ + +OGRMultiSurface* OGRMultiPolygon::CastToMultiSurface(OGRMultiPolygon* poMP) +{ + return (OGRMultiSurface*) TransferMembersAndDestroy(poMP, new OGRMultiSurface()); +} diff --git a/bazaar/plugin/gdal/ogr/ogrmultisurface.cpp b/bazaar/plugin/gdal/ogr/ogrmultisurface.cpp new file mode 100644 index 000000000..e1c6cbdc7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrmultisurface.cpp @@ -0,0 +1,284 @@ +/****************************************************************************** + * $Id: ogrmultisurface.cpp 27960 2014-11-14 18:31:32Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRMultiSurface class. + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" +#include "ogr_api.h" + +CPL_CVSID("$Id: ogrmultisurface.cpp 27960 2014-11-14 18:31:32Z rouault $"); + +/************************************************************************/ +/* OGRMultiSurface() */ +/************************************************************************/ + +/** + * \brief Create an empty multi surface collection. + */ + +OGRMultiSurface::OGRMultiSurface() +{ +} + +/************************************************************************/ +/* ~OGRMultiSurface() */ +/************************************************************************/ + +OGRMultiSurface::~OGRMultiSurface() +{ +} + +/************************************************************************/ +/* getGeometryType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRMultiSurface::getGeometryType() const + +{ + if( getCoordinateDimension() == 3 ) + return wkbMultiSurfaceZ; + else + return wkbMultiSurface; +} + +/************************************************************************/ +/* getDimension() */ +/************************************************************************/ + +int OGRMultiSurface::getDimension() const + +{ + return 2; +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRMultiSurface::getGeometryName() const + +{ + return "MULTISURFACE"; +} + +/************************************************************************/ +/* isCompatibleSubType() */ +/************************************************************************/ + +OGRBoolean OGRMultiSurface::isCompatibleSubType( OGRwkbGeometryType eGeomType ) const +{ + return OGR_GT_IsSurface(eGeomType); +} + +/************************************************************************/ +/* importFromWkt() */ +/* */ +/* Instantiate from well known text format. */ +/************************************************************************/ + +OGRErr OGRMultiSurface::importFromWkt( char ** ppszInput ) + +{ + int bHasZ = FALSE, bHasM = FALSE; + OGRErr eErr = importPreambuleFromWkt(ppszInput, &bHasZ, &bHasM); + if( eErr >= 0 ) + return eErr; + + if( bHasZ ) + setCoordinateDimension(3); + + int bIsMultiSurface = (wkbFlatten(getGeometryType()) == wkbMultiSurface); + + char szToken[OGR_WKT_TOKEN_MAX]; + const char *pszInput = *ppszInput; + eErr = OGRERR_NONE; + + /* Skip first '(' */ + pszInput = OGRWktReadToken( pszInput, szToken ); + +/* ==================================================================== */ +/* Read each surface in turn. Note that we try to reuse the same */ +/* point list buffer from ring to ring to cut down on */ +/* allocate/deallocate overhead. */ +/* ==================================================================== */ + OGRRawPoint *paoPoints = NULL; + int nMaxPoints = 0; + double *padfZ = NULL; + + do + { + + /* -------------------------------------------------------------------- */ + /* Get the first token, which should be the geometry type. */ + /* -------------------------------------------------------------------- */ + const char* pszInputBefore = pszInput; + pszInput = OGRWktReadToken( pszInput, szToken ); + + OGRSurface* poSurface; + + /* -------------------------------------------------------------------- */ + /* Do the import. */ + /* -------------------------------------------------------------------- */ + if (EQUAL(szToken,"(")) + { + OGRPolygon *poPolygon = new OGRPolygon(); + poSurface = poPolygon; + pszInput = pszInputBefore; + eErr = poPolygon->importFromWKTListOnly( (char**)&pszInput, bHasZ, bHasM, + paoPoints, nMaxPoints, padfZ ); + } + else if (EQUAL(szToken, "EMPTY") ) + { + poSurface = new OGRPolygon(); + } + /* We accept POLYGON() but this is an extension to the BNF, also */ + /* accepted by PostGIS */ + else if (bIsMultiSurface && + (EQUAL(szToken,"POLYGON") || + EQUAL(szToken,"CURVEPOLYGON"))) + { + OGRGeometry* poGeom = NULL; + pszInput = pszInputBefore; + eErr = OGRGeometryFactory::createFromWkt( (char **) &pszInput, + NULL, &poGeom ); + poSurface = (OGRSurface*) poGeom; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Unexpected token : %s", szToken); + eErr = OGRERR_CORRUPT_DATA; + break; + } + + if( eErr == OGRERR_NONE ) + eErr = addGeometryDirectly( poSurface ); + if( eErr != OGRERR_NONE ) + { + delete poSurface; + break; + } + +/* -------------------------------------------------------------------- */ +/* Read the delimeter following the surface. */ +/* -------------------------------------------------------------------- */ + pszInput = OGRWktReadToken( pszInput, szToken ); + + } while( szToken[0] == ',' && eErr == OGRERR_NONE ); + + CPLFree( paoPoints ); + CPLFree( padfZ ); + +/* -------------------------------------------------------------------- */ +/* freak if we don't get a closing bracket. */ +/* -------------------------------------------------------------------- */ + + if( eErr != OGRERR_NONE ) + return eErr; + + if( szToken[0] != ')' ) + return OGRERR_CORRUPT_DATA; + + *ppszInput = (char *) pszInput; + return OGRERR_NONE; +} + +/************************************************************************/ +/* exportToWkt() */ +/************************************************************************/ + +OGRErr OGRMultiSurface::exportToWkt( char ** ppszDstText, + CPL_UNUSED OGRwkbVariant eWkbVariant ) const + +{ + return exportToWktInternal( ppszDstText, wkbVariantIso, "POLYGON" ); +} + +/************************************************************************/ +/* hasCurveGeometry() */ +/************************************************************************/ + +OGRBoolean OGRMultiSurface::hasCurveGeometry(int bLookForNonLinear) const +{ + if( bLookForNonLinear ) + return OGRGeometryCollection::hasCurveGeometry(TRUE); + return TRUE; +} + +/************************************************************************/ +/* PointOnSurface() */ +/************************************************************************/ + +/** \brief This method relates to the SFCOM IMultiSurface::get_PointOnSurface() method. + * + * NOTE: Only implemented when GEOS included in build. + * + * @param poPoint point to be set with an internal point. + * + * @return OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise. + */ + +OGRErr OGRMultiSurface::PointOnSurface( OGRPoint * poPoint ) const +{ + OGRMultiPolygon* poMPoly = (OGRMultiPolygon*) getLinearGeometry(); + OGRErr ret = poMPoly->PointOnSurface(poPoint); + delete poMPoly; + return ret; +} + +/************************************************************************/ +/* CastToMultiPolygon() */ +/************************************************************************/ + +/** + * \brief Cast to multipolygon. + * + * This method should only be called if the multisurface actually only contains + * instances of OGRPolygon. This can be verified if hasCurveGeometry(TRUE) + * returns FALSE. It is not intended to approximate curve polygons. For that + * use getLinearGeometry(). + * + * The passed in geometry is consumed and a new one returned (or NULL in case + * of failure). + * + * @param poMS the input geometry - ownership is passed to the method. + * @return new geometry. + */ + +OGRMultiPolygon* OGRMultiSurface::CastToMultiPolygon(OGRMultiSurface* poMS) +{ + for(int i=0;inGeomCount;i++) + { + poMS->papoGeoms[i] = OGRSurface::CastToPolygon( (OGRSurface*)poMS->papoGeoms[i] ); + if( poMS->papoGeoms[i] == NULL ) + { + delete poMS; + return NULL; + } + } + return (OGRMultiPolygon*) TransferMembersAndDestroy(poMS, new OGRMultiPolygon()); +} diff --git a/bazaar/plugin/gdal/ogr/ogrpgeogeometry.cpp b/bazaar/plugin/gdal/ogr/ogrpgeogeometry.cpp new file mode 100644 index 000000000..e1722decb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrpgeogeometry.cpp @@ -0,0 +1,1631 @@ +/****************************************************************************** + * $Id: ogrpgeogeometry.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements decoder of shapebin geometry for PGeo + * Author: Frank Warmerdam, warmerdam@pobox.com + * Paul Ramsey, pramsey at cleverelephant.ca + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2011, Paul Ramsey + * Copyright (c) 2011-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrpgeogeometry.h" +#include "ogr_p.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrpgeogeometry.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +#define SHPP_TRISTRIP 0 +#define SHPP_TRIFAN 1 +#define SHPP_OUTERRING 2 +#define SHPP_INNERRING 3 +#define SHPP_FIRSTRING 4 +#define SHPP_RING 5 +#define SHPP_TRIANGLES 6 /* Multipatch 9.0 specific */ + + +/************************************************************************/ +/* OGRCreateFromMultiPatchPart() */ +/************************************************************************/ + +void OGRCreateFromMultiPatchPart(OGRMultiPolygon *poMP, + OGRPolygon*& poLastPoly, + int nPartType, + int nPartPoints, + double* padfX, + double* padfY, + double* padfZ) +{ + nPartType &= 0xf; + + if( nPartType == SHPP_TRISTRIP ) + { + int iBaseVert; + + if( poLastPoly != NULL ) + { + poMP->addGeometryDirectly( poLastPoly ); + poLastPoly = NULL; + } + + for( iBaseVert = 0; iBaseVert < nPartPoints-2; iBaseVert++ ) + { + OGRPolygon *poPoly = new OGRPolygon(); + OGRLinearRing *poRing = new OGRLinearRing(); + int iSrcVert = iBaseVert; + + poRing->setPoint( 0, + padfX[iSrcVert], + padfY[iSrcVert], + padfZ[iSrcVert] ); + poRing->setPoint( 1, + padfX[iSrcVert+1], + padfY[iSrcVert+1], + padfZ[iSrcVert+1] ); + + poRing->setPoint( 2, + padfX[iSrcVert+2], + padfY[iSrcVert+2], + padfZ[iSrcVert+2] ); + poRing->setPoint( 3, + padfX[iSrcVert], + padfY[iSrcVert], + padfZ[iSrcVert] ); + + poPoly->addRingDirectly( poRing ); + poMP->addGeometryDirectly( poPoly ); + } + } + else if( nPartType == SHPP_TRIFAN ) + { + int iBaseVert; + + if( poLastPoly != NULL ) + { + poMP->addGeometryDirectly( poLastPoly ); + poLastPoly = NULL; + } + + for( iBaseVert = 0; iBaseVert < nPartPoints-2; iBaseVert++ ) + { + OGRPolygon *poPoly = new OGRPolygon(); + OGRLinearRing *poRing = new OGRLinearRing(); + int iSrcVert = iBaseVert; + + poRing->setPoint( 0, + padfX[0], + padfY[0], + padfZ[0] ); + poRing->setPoint( 1, + padfX[iSrcVert+1], + padfY[iSrcVert+1], + padfZ[iSrcVert+1] ); + + poRing->setPoint( 2, + padfX[iSrcVert+2], + padfY[iSrcVert+2], + padfZ[iSrcVert+2] ); + poRing->setPoint( 3, + padfX[0], + padfY[0], + padfZ[0] ); + + poPoly->addRingDirectly( poRing ); + poMP->addGeometryDirectly( poPoly ); + } + } + else if( nPartType == SHPP_OUTERRING + || nPartType == SHPP_INNERRING + || nPartType == SHPP_FIRSTRING + || nPartType == SHPP_RING ) + { + if( poLastPoly != NULL + && (nPartType == SHPP_OUTERRING + || nPartType == SHPP_FIRSTRING) ) + { + poMP->addGeometryDirectly( poLastPoly ); + poLastPoly = NULL; + } + + if( poLastPoly == NULL ) + poLastPoly = new OGRPolygon(); + + OGRLinearRing *poRing = new OGRLinearRing; + + poRing->setPoints( nPartPoints, + padfX, + padfY, + padfZ ); + + poRing->closeRings(); + + poLastPoly->addRingDirectly( poRing ); + } + else if ( nPartType == SHPP_TRIANGLES ) + { + int iBaseVert; + + if( poLastPoly != NULL ) + { + poMP->addGeometryDirectly( poLastPoly ); + poLastPoly = NULL; + } + + for( iBaseVert = 0; iBaseVert < nPartPoints-2; iBaseVert+=3 ) + { + OGRPolygon *poPoly = new OGRPolygon(); + OGRLinearRing *poRing = new OGRLinearRing(); + int iSrcVert = iBaseVert; + + poRing->setPoint( 0, + padfX[iSrcVert], + padfY[iSrcVert], + padfZ[iSrcVert] ); + poRing->setPoint( 1, + padfX[iSrcVert+1], + padfY[iSrcVert+1], + padfZ[iSrcVert+1] ); + + poRing->setPoint( 2, + padfX[iSrcVert+2], + padfY[iSrcVert+2], + padfZ[iSrcVert+2] ); + poRing->setPoint( 3, + padfX[iSrcVert], + padfY[iSrcVert], + padfZ[iSrcVert] ); + + poPoly->addRingDirectly( poRing ); + poMP->addGeometryDirectly( poPoly ); + } + } + else + CPLDebug( "OGR", "Unrecognised parttype %d, ignored.", + nPartType ); +} + +/************************************************************************/ +/* OGRCreateFromMultiPatch() */ +/* */ +/* Translate a multipatch representation to an OGR geometry */ +/* Mostly copied from shape2ogr.cpp */ +/************************************************************************/ + +static OGRGeometry* OGRCreateFromMultiPatch(int nParts, + GInt32* panPartStart, + GInt32* panPartType, + int nPoints, + double* padfX, + double* padfY, + double* padfZ) +{ + OGRMultiPolygon *poMP = new OGRMultiPolygon(); + int iPart; + OGRPolygon *poLastPoly = NULL; + + for( iPart = 0; iPart < nParts; iPart++ ) + { + int nPartPoints, nPartStart; + + // Figure out details about this part's vertex list. + if( panPartStart == NULL ) + { + nPartPoints = nPoints; + nPartStart = 0; + } + else + { + + if( iPart == nParts - 1 ) + nPartPoints = + nPoints - panPartStart[iPart]; + else + nPartPoints = panPartStart[iPart+1] + - panPartStart[iPart]; + nPartStart = panPartStart[iPart]; + } + + OGRCreateFromMultiPatchPart(poMP, + poLastPoly, + panPartType[iPart], + nPartPoints, + padfX + nPartStart, + padfY + nPartStart, + padfZ + nPartStart); + } + + if( poLastPoly != NULL ) + { + poMP->addGeometryDirectly( poLastPoly ); + poLastPoly = NULL; + } + + return poMP; +} + + +/************************************************************************/ +/* OGRWriteToShapeBin() */ +/* */ +/* Translate OGR geometry to a shapefile binary representation */ +/************************************************************************/ + +OGRErr OGRWriteToShapeBin( OGRGeometry *poGeom, + GByte **ppabyShape, + int *pnBytes ) +{ + GUInt32 nGType = SHPT_NULL; + int nShpSize = 4; /* All types start with integer type number */ + int nShpZSize = 0; /* Z gets tacked onto the end */ + GUInt32 nPoints = 0; + GUInt32 nParts = 0; + +/* -------------------------------------------------------------------- */ +/* Null or Empty input maps to SHPT_NULL. */ +/* -------------------------------------------------------------------- */ + if ( ! poGeom || poGeom->IsEmpty() ) + { + *ppabyShape = (GByte*)VSIMalloc(nShpSize); + GUInt32 zero = SHPT_NULL; + memcpy(*ppabyShape, &zero, nShpSize); + *pnBytes = nShpSize; + return OGRERR_NONE; + } + + OGRwkbGeometryType nOGRType = wkbFlatten(poGeom->getGeometryType()); + int b3d = wkbHasZ(poGeom->getGeometryType()); + int nCoordDims = b3d ? 3 : 2; + +/* -------------------------------------------------------------------- */ +/* Calculate the shape buffer size */ +/* -------------------------------------------------------------------- */ + if ( nOGRType == wkbPoint ) + { + nShpSize += 8 * nCoordDims; + } + else if ( nOGRType == wkbLineString ) + { + OGRLineString *poLine = (OGRLineString*)poGeom; + nPoints = poLine->getNumPoints(); + nParts = 1; + nShpSize += 16 * nCoordDims; /* xy(z) box */ + nShpSize += 4; /* nparts */ + nShpSize += 4; /* npoints */ + nShpSize += 4; /* parts[1] */ + nShpSize += 8 * nCoordDims * nPoints; /* points */ + nShpZSize = 16 + 8 * nPoints; + } + else if ( nOGRType == wkbPolygon ) + { + poGeom->closeRings(); + OGRPolygon *poPoly = (OGRPolygon*)poGeom; + nParts = poPoly->getNumInteriorRings() + 1; + for ( GUInt32 i = 0; i < nParts; i++ ) + { + OGRLinearRing *poRing; + if ( i == 0 ) + poRing = poPoly->getExteriorRing(); + else + poRing = poPoly->getInteriorRing(i-1); + nPoints += poRing->getNumPoints(); + } + nShpSize += 16 * nCoordDims; /* xy(z) box */ + nShpSize += 4; /* nparts */ + nShpSize += 4; /* npoints */ + nShpSize += 4 * nParts; /* parts[nparts] */ + nShpSize += 8 * nCoordDims * nPoints; /* points */ + nShpZSize = 16 + 8 * nPoints; + } + else if ( nOGRType == wkbMultiPoint ) + { + OGRMultiPoint *poMPoint = (OGRMultiPoint*)poGeom; + for ( int i = 0; i < poMPoint->getNumGeometries(); i++ ) + { + OGRPoint *poPoint = (OGRPoint*)(poMPoint->getGeometryRef(i)); + if ( poPoint->IsEmpty() ) + continue; + nPoints++; + } + nShpSize += 16 * nCoordDims; /* xy(z) box */ + nShpSize += 4; /* npoints */ + nShpSize += 8 * nCoordDims * nPoints; /* points */ + nShpZSize = 16 + 8 * nPoints; + } + else if ( nOGRType == wkbMultiLineString ) + { + OGRMultiLineString *poMLine = (OGRMultiLineString*)poGeom; + for ( int i = 0; i < poMLine->getNumGeometries(); i++ ) + { + OGRLineString *poLine = (OGRLineString*)(poMLine->getGeometryRef(i)); + /* Skip empties */ + if ( poLine->IsEmpty() ) + continue; + nParts++; + nPoints += poLine->getNumPoints(); + } + nShpSize += 16 * nCoordDims; /* xy(z) box */ + nShpSize += 4; /* nparts */ + nShpSize += 4; /* npoints */ + nShpSize += 4 * nParts; /* parts[nparts] */ + nShpSize += 8 * nCoordDims * nPoints ; /* points */ + nShpZSize = 16 + 8 * nPoints; + } + else if ( nOGRType == wkbMultiPolygon ) + { + poGeom->closeRings(); + OGRMultiPolygon *poMPoly = (OGRMultiPolygon*)poGeom; + for( int j = 0; j < poMPoly->getNumGeometries(); j++ ) + { + OGRPolygon *poPoly = (OGRPolygon*)(poMPoly->getGeometryRef(j)); + int nRings = poPoly->getNumInteriorRings() + 1; + + /* Skip empties */ + if ( poPoly->IsEmpty() ) + continue; + + nParts += nRings; + for ( int i = 0; i < nRings; i++ ) + { + OGRLinearRing *poRing; + if ( i == 0 ) + poRing = poPoly->getExteriorRing(); + else + poRing = poPoly->getInteriorRing(i-1); + nPoints += poRing->getNumPoints(); + } + } + nShpSize += 16 * nCoordDims; /* xy(z) box */ + nShpSize += 4; /* nparts */ + nShpSize += 4; /* npoints */ + nShpSize += 4 * nParts; /* parts[nparts] */ + nShpSize += 8 * nCoordDims * nPoints ; /* points */ + nShpZSize = 16 + 8 * nPoints; + } + else + { + return OGRERR_UNSUPPORTED_OPERATION; + } + + /* Allocate our shape buffer */ + *ppabyShape = (GByte*)VSIMalloc(nShpSize); + if ( ! *ppabyShape ) + return OGRERR_FAILURE; + + /* Fill in the output size. */ + *pnBytes = nShpSize; + + /* Set up write pointers */ + unsigned char *pabyPtr = *ppabyShape; + unsigned char *pabyPtrZ = NULL; + if ( b3d ) + pabyPtrZ = pabyPtr + nShpSize - nShpZSize; + +/* -------------------------------------------------------------------- */ +/* Write in the Shape type number now */ +/* -------------------------------------------------------------------- */ + switch(nOGRType) + { + case wkbPoint: + { + nGType = b3d ? SHPT_POINTZ : SHPT_POINT; + break; + } + case wkbMultiPoint: + { + nGType = b3d ? SHPT_MULTIPOINTZ : SHPT_MULTIPOINT; + break; + } + case wkbLineString: + case wkbMultiLineString: + { + nGType = b3d ? SHPT_ARCZ : SHPT_ARC; + break; + } + case wkbPolygon: + case wkbMultiPolygon: + { + nGType = b3d ? SHPT_POLYGONZ : SHPT_POLYGON; + break; + } + default: + { + return OGRERR_UNSUPPORTED_OPERATION; + } + } + /* Write in the type number and advance the pointer */ + nGType = CPL_LSBWORD32( nGType ); + memcpy( pabyPtr, &nGType, 4 ); + pabyPtr += 4; + + +/* -------------------------------------------------------------------- */ +/* POINT and POINTZ */ +/* -------------------------------------------------------------------- */ + if ( nOGRType == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint*)poGeom; + double x = poPoint->getX(); + double y = poPoint->getY(); + + /* Copy in the raw data. */ + memcpy( pabyPtr, &x, 8 ); + memcpy( pabyPtr+8, &y, 8 ); + if( b3d ) + { + double z = poPoint->getZ(); + memcpy( pabyPtr+8+8, &z, 8 ); + } + + /* Swap if needed. Shape doubles always LSB */ + if( OGR_SWAP( wkbNDR ) ) + { + CPL_SWAPDOUBLE( pabyPtr ); + CPL_SWAPDOUBLE( pabyPtr+8 ); + if( b3d ) + CPL_SWAPDOUBLE( pabyPtr+8+8 ); + } + + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* All the non-POINT types require an envelope next */ +/* -------------------------------------------------------------------- */ + OGREnvelope3D envelope; + poGeom->getEnvelope(&envelope); + memcpy( pabyPtr, &(envelope.MinX), 8 ); + memcpy( pabyPtr+8, &(envelope.MinY), 8 ); + memcpy( pabyPtr+8+8, &(envelope.MaxX), 8 ); + memcpy( pabyPtr+8+8+8, &(envelope.MaxY), 8 ); + + /* Swap box if needed. Shape doubles are always LSB */ + if( OGR_SWAP( wkbNDR ) ) + { + for ( int i = 0; i < 4; i++ ) + CPL_SWAPDOUBLE( pabyPtr + 8*i ); + } + pabyPtr += 32; + + /* Write in the Z bounds at the end of the XY buffer */ + if ( b3d ) + { + memcpy( pabyPtrZ, &(envelope.MinZ), 8 ); + memcpy( pabyPtrZ+8, &(envelope.MaxZ), 8 ); + + /* Swap Z bounds if necessary */ + if( OGR_SWAP( wkbNDR ) ) + { + for ( int i = 0; i < 2; i++ ) + CPL_SWAPDOUBLE( pabyPtrZ + 8*i ); + } + pabyPtrZ += 16; + } + +/* -------------------------------------------------------------------- */ +/* LINESTRING and LINESTRINGZ */ +/* -------------------------------------------------------------------- */ + if ( nOGRType == wkbLineString ) + { + const OGRLineString *poLine = (OGRLineString*)poGeom; + + /* Write in the nparts (1) */ + GUInt32 nPartsLsb = CPL_LSBWORD32( nParts ); + memcpy( pabyPtr, &nPartsLsb, 4 ); + pabyPtr += 4; + + /* Write in the npoints */ + GUInt32 nPointsLsb = CPL_LSBWORD32( nPoints ); + memcpy( pabyPtr, &nPointsLsb, 4 ); + pabyPtr += 4; + + /* Write in the part index (0) */ + GUInt32 nPartIndex = 0; + memcpy( pabyPtr, &nPartIndex, 4 ); + pabyPtr += 4; + + /* Write in the point data */ + poLine->getPoints((OGRRawPoint*)pabyPtr, (double*)pabyPtrZ); + + /* Swap if necessary */ + if( OGR_SWAP( wkbNDR ) ) + { + for( GUInt32 k = 0; k < nPoints; k++ ) + { + CPL_SWAPDOUBLE( pabyPtr + 16*k ); + CPL_SWAPDOUBLE( pabyPtr + 16*k + 8 ); + if( b3d ) + CPL_SWAPDOUBLE( pabyPtrZ + 8*k ); + } + } + return OGRERR_NONE; + + } +/* -------------------------------------------------------------------- */ +/* POLYGON and POLYGONZ */ +/* -------------------------------------------------------------------- */ + else if ( nOGRType == wkbPolygon ) + { + OGRPolygon *poPoly = (OGRPolygon*)poGeom; + + /* Write in the part count */ + GUInt32 nPartsLsb = CPL_LSBWORD32( nParts ); + memcpy( pabyPtr, &nPartsLsb, 4 ); + pabyPtr += 4; + + /* Write in the total point count */ + GUInt32 nPointsLsb = CPL_LSBWORD32( nPoints ); + memcpy( pabyPtr, &nPointsLsb, 4 ); + pabyPtr += 4; + +/* -------------------------------------------------------------------- */ +/* Now we have to visit each ring and write an index number into */ +/* the parts list, and the coordinates into the points list. */ +/* to do it in one pass, we will use three write pointers. */ +/* pabyPtr writes the part indexes */ +/* pabyPoints writes the xy coordinates */ +/* pabyPtrZ writes the z coordinates */ +/* -------------------------------------------------------------------- */ + + /* Just past the partindex[nparts] array */ + unsigned char* pabyPoints = pabyPtr + 4*nParts; + + int nPointIndexCount = 0; + + for( GUInt32 i = 0; i < nParts; i++ ) + { + /* Check our Ring and condition it */ + OGRLinearRing *poRing; + if ( i == 0 ) + { + poRing = poPoly->getExteriorRing(); + /* Outer ring must be clockwise */ + if ( ! poRing->isClockwise() ) + poRing->reverseWindingOrder(); + } + else + { + poRing = poPoly->getInteriorRing(i-1); + /* Inner rings should be anti-clockwise */ + if ( poRing->isClockwise() ) + poRing->reverseWindingOrder(); + } + + int nRingNumPoints = poRing->getNumPoints(); + + /* Cannot write un-closed rings to shape */ + if( nRingNumPoints <= 2 || ! poRing->get_IsClosed() ) + return OGRERR_FAILURE; + + /* Write in the part index */ + GUInt32 nPartIndex = CPL_LSBWORD32( nPointIndexCount ); + memcpy( pabyPtr, &nPartIndex, 4 ); + + /* Write in the point data */ + poRing->getPoints((OGRRawPoint*)pabyPoints, (double*)pabyPtrZ); + + /* Swap if necessary */ + if( OGR_SWAP( wkbNDR ) ) + { + for( int k = 0; k < nRingNumPoints; k++ ) + { + CPL_SWAPDOUBLE( pabyPoints + 16*k ); + CPL_SWAPDOUBLE( pabyPoints + 16*k + 8 ); + if( b3d ) + CPL_SWAPDOUBLE( pabyPtrZ + 8*k ); + } + } + + nPointIndexCount += nRingNumPoints; + /* Advance the write pointers */ + pabyPtr += 4; + pabyPoints += 16 * nRingNumPoints; + if ( b3d ) + pabyPtrZ += 8 * nRingNumPoints; + } + + return OGRERR_NONE; + + } +/* -------------------------------------------------------------------- */ +/* MULTIPOINT and MULTIPOINTZ */ +/* -------------------------------------------------------------------- */ + else if ( nOGRType == wkbMultiPoint ) + { + OGRMultiPoint *poMPoint = (OGRMultiPoint*)poGeom; + + /* Write in the total point count */ + GUInt32 nPointsLsb = CPL_LSBWORD32( nPoints ); + memcpy( pabyPtr, &nPointsLsb, 4 ); + pabyPtr += 4; + +/* -------------------------------------------------------------------- */ +/* Now we have to visit each point write it into the points list */ +/* We will use two write pointers. */ +/* pabyPtr writes the xy coordinates */ +/* pabyPtrZ writes the z coordinates */ +/* -------------------------------------------------------------------- */ + + for( GUInt32 i = 0; i < nPoints; i++ ) + { + const OGRPoint *poPt = (OGRPoint*)(poMPoint->getGeometryRef(i)); + + /* Skip empties */ + if ( poPt->IsEmpty() ) + continue; + + /* Write the coordinates */ + double x = poPt->getX(); + double y = poPt->getY(); + memcpy(pabyPtr, &x, 8); + memcpy(pabyPtr+8, &y, 8); + if ( b3d ) + { + double z = poPt->getZ(); + memcpy(pabyPtrZ, &z, 8); + } + + /* Swap if necessary */ + if( OGR_SWAP( wkbNDR ) ) + { + CPL_SWAPDOUBLE( pabyPtr ); + CPL_SWAPDOUBLE( pabyPtr + 8 ); + if( b3d ) + CPL_SWAPDOUBLE( pabyPtrZ ); + } + + /* Advance the write pointers */ + pabyPtr += 16; + if ( b3d ) + pabyPtrZ += 8; + } + + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* MULTILINESTRING and MULTILINESTRINGZ */ +/* -------------------------------------------------------------------- */ + else if ( nOGRType == wkbMultiLineString ) + { + OGRMultiLineString *poMLine = (OGRMultiLineString*)poGeom; + + /* Write in the part count */ + GUInt32 nPartsLsb = CPL_LSBWORD32( nParts ); + memcpy( pabyPtr, &nPartsLsb, 4 ); + pabyPtr += 4; + + /* Write in the total point count */ + GUInt32 nPointsLsb = CPL_LSBWORD32( nPoints ); + memcpy( pabyPtr, &nPointsLsb, 4 ); + pabyPtr += 4; + + /* Just past the partindex[nparts] array */ + unsigned char* pabyPoints = pabyPtr + 4*nParts; + + int nPointIndexCount = 0; + + for( GUInt32 i = 0; i < nParts; i++ ) + { + const OGRLineString *poLine = (OGRLineString*)(poMLine->getGeometryRef(i)); + + /* Skip empties */ + if ( poLine->IsEmpty() ) + continue; + + int nLineNumPoints = poLine->getNumPoints(); + + /* Write in the part index */ + GUInt32 nPartIndex = CPL_LSBWORD32( nPointIndexCount ); + memcpy( pabyPtr, &nPartIndex, 4 ); + + /* Write in the point data */ + poLine->getPoints((OGRRawPoint*)pabyPoints, (double*)pabyPtrZ); + + /* Swap if necessary */ + if( OGR_SWAP( wkbNDR ) ) + { + for( int k = 0; k < nLineNumPoints; k++ ) + { + CPL_SWAPDOUBLE( pabyPoints + 16*k ); + CPL_SWAPDOUBLE( pabyPoints + 16*k + 8 ); + if( b3d ) + CPL_SWAPDOUBLE( pabyPtrZ + 8*k ); + } + } + + nPointIndexCount += nLineNumPoints; + + /* Advance the write pointers */ + pabyPtr += 4; + pabyPoints += 16 * nLineNumPoints; + if ( b3d ) + pabyPtrZ += 8 * nLineNumPoints; + } + + return OGRERR_NONE; + + } +/* -------------------------------------------------------------------- */ +/* MULTIPOLYGON and MULTIPOLYGONZ */ +/* -------------------------------------------------------------------- */ + else if ( nOGRType == wkbMultiPolygon ) + { + OGRMultiPolygon *poMPoly = (OGRMultiPolygon*)poGeom; + + /* Write in the part count */ + GUInt32 nPartsLsb = CPL_LSBWORD32( nParts ); + memcpy( pabyPtr, &nPartsLsb, 4 ); + pabyPtr += 4; + + /* Write in the total point count */ + GUInt32 nPointsLsb = CPL_LSBWORD32( nPoints ); + memcpy( pabyPtr, &nPointsLsb, 4 ); + pabyPtr += 4; + +/* -------------------------------------------------------------------- */ +/* Now we have to visit each ring and write an index number into */ +/* the parts list, and the coordinates into the points list. */ +/* to do it in one pass, we will use three write pointers. */ +/* pabyPtr writes the part indexes */ +/* pabyPoints writes the xy coordinates */ +/* pabyPtrZ writes the z coordinates */ +/* -------------------------------------------------------------------- */ + + /* Just past the partindex[nparts] array */ + unsigned char* pabyPoints = pabyPtr + 4*nParts; + + int nPointIndexCount = 0; + + for( int i = 0; i < poMPoly->getNumGeometries(); i++ ) + { + OGRPolygon *poPoly = (OGRPolygon*)(poMPoly->getGeometryRef(i)); + + /* Skip empties */ + if ( poPoly->IsEmpty() ) + continue; + + int nRings = 1 + poPoly->getNumInteriorRings(); + + for( int j = 0; j < nRings; j++ ) + { + /* Check our Ring and condition it */ + OGRLinearRing *poRing; + if ( j == 0 ) + { + poRing = poPoly->getExteriorRing(); + /* Outer ring must be clockwise */ + if ( ! poRing->isClockwise() ) + poRing->reverseWindingOrder(); + } + else + { + poRing = poPoly->getInteriorRing(j-1); + /* Inner rings should be anti-clockwise */ + if ( poRing->isClockwise() ) + poRing->reverseWindingOrder(); + } + + int nRingNumPoints = poRing->getNumPoints(); + + /* Cannot write closed rings to shape */ + if( nRingNumPoints <= 2 || ! poRing->get_IsClosed() ) + return OGRERR_FAILURE; + + /* Write in the part index */ + GUInt32 nPartIndex = CPL_LSBWORD32( nPointIndexCount ); + memcpy( pabyPtr, &nPartIndex, 4 ); + + /* Write in the point data */ + poRing->getPoints((OGRRawPoint*)pabyPoints, (double*)pabyPtrZ); + + /* Swap if necessary */ + if( OGR_SWAP( wkbNDR ) ) + { + for( int k = 0; k < nRingNumPoints; k++ ) + { + CPL_SWAPDOUBLE( pabyPoints + 16*k ); + CPL_SWAPDOUBLE( pabyPoints + 16*k + 8 ); + if( b3d ) + CPL_SWAPDOUBLE( pabyPtrZ + 8*k ); + } + } + + nPointIndexCount += nRingNumPoints; + /* Advance the write pointers */ + pabyPtr += 4; + pabyPoints += 16 * nRingNumPoints; + if ( b3d ) + pabyPtrZ += 8 * nRingNumPoints; + } + } + + return OGRERR_NONE; + + } + else + { + return OGRERR_UNSUPPORTED_OPERATION; + } + +} + + +/************************************************************************/ +/* OGRWriteMultiPatchToShapeBin() */ +/************************************************************************/ + +OGRErr OGRWriteMultiPatchToShapeBin( OGRGeometry *poGeom, + GByte **ppabyShape, + int *pnBytes ) +{ + if( wkbFlatten(poGeom->getGeometryType()) != wkbMultiPolygon ) + return OGRERR_UNSUPPORTED_OPERATION; + + poGeom->closeRings(); + OGRMultiPolygon *poMPoly = (OGRMultiPolygon*)poGeom; + int nParts = 0; + int* panPartStart = NULL; + int* panPartType = NULL; + int nPoints = 0; + OGRRawPoint* poPoints = NULL; + double* padfZ = NULL; + int nBeginLastPart = 0; + + for( int j = 0; j < poMPoly->getNumGeometries(); j++ ) + { + OGRPolygon *poPoly = (OGRPolygon*)(poMPoly->getGeometryRef(j)); + int nRings = poPoly->getNumInteriorRings() + 1; + + /* Skip empties */ + if ( poPoly->IsEmpty() ) + continue; + + OGRLinearRing *poRing = poPoly->getExteriorRing(); + if( nRings == 1 && poRing->getNumPoints() == 4 ) + { + if( nParts > 0 && + ((panPartType[nParts-1] == SHPP_TRIANGLES && nPoints - panPartStart[nParts-1] == 3) || + panPartType[nParts-1] == SHPP_TRIFAN) && + poRing->getX(0) == poPoints[nBeginLastPart].x && + poRing->getY(0) == poPoints[nBeginLastPart].y && + poRing->getZ(0) == padfZ[nBeginLastPart] && + poRing->getX(1) == poPoints[nPoints-1].x && + poRing->getY(1) == poPoints[nPoints-1].y && + poRing->getZ(1) == padfZ[nPoints-1] ) + { + panPartType[nParts-1] = SHPP_TRIFAN; + + poPoints = (OGRRawPoint*)CPLRealloc(poPoints, + (nPoints + 1) * sizeof(OGRRawPoint)); + padfZ = (double*)CPLRealloc(padfZ, (nPoints + 1) * sizeof(double)); + poPoints[nPoints].x = poRing->getX(2); + poPoints[nPoints].y = poRing->getY(2); + padfZ[nPoints] = poRing->getZ(2); + nPoints ++; + } + else if( nParts > 0 && + ((panPartType[nParts-1] == SHPP_TRIANGLES && nPoints - panPartStart[nParts-1] == 3) || + panPartType[nParts-1] == SHPP_TRISTRIP) && + poRing->getX(0) == poPoints[nPoints-2].x && + poRing->getY(0) == poPoints[nPoints-2].y && + poRing->getZ(0) == padfZ[nPoints-2] && + poRing->getX(1) == poPoints[nPoints-1].x && + poRing->getY(1) == poPoints[nPoints-1].y && + poRing->getZ(1) == padfZ[nPoints-1] ) + { + panPartType[nParts-1] = SHPP_TRISTRIP; + + poPoints = (OGRRawPoint*)CPLRealloc(poPoints, + (nPoints + 1) * sizeof(OGRRawPoint)); + padfZ = (double*)CPLRealloc(padfZ, (nPoints + 1) * sizeof(double)); + poPoints[nPoints].x = poRing->getX(2); + poPoints[nPoints].y = poRing->getY(2); + padfZ[nPoints] = poRing->getZ(2); + nPoints ++; + } + else + { + if( nParts == 0 || panPartType[nParts-1] != SHPP_TRIANGLES ) + { + nBeginLastPart = nPoints; + + panPartStart = (int*)CPLRealloc(panPartStart, (nParts + 1) * sizeof(int)); + panPartType = (int*)CPLRealloc(panPartType, (nParts + 1) * sizeof(int)); + panPartStart[nParts] = nPoints; + panPartType[nParts] = SHPP_TRIANGLES; + nParts ++; + } + + poPoints = (OGRRawPoint*)CPLRealloc(poPoints, + (nPoints + 3) * sizeof(OGRRawPoint)); + padfZ = (double*)CPLRealloc(padfZ, (nPoints + 3) * sizeof(double)); + for(int i=0;i<3;i++) + { + poPoints[nPoints+i].x = poRing->getX(i); + poPoints[nPoints+i].y = poRing->getY(i); + padfZ[nPoints+i] = poRing->getZ(i); + } + nPoints += 3; + } + } + else + { + panPartStart = (int*)CPLRealloc(panPartStart, (nParts + nRings) * sizeof(int)); + panPartType = (int*)CPLRealloc(panPartType, (nParts + nRings) * sizeof(int)); + + for ( int i = 0; i < nRings; i++ ) + { + panPartStart[nParts + i] = nPoints; + if ( i == 0 ) + { + poRing = poPoly->getExteriorRing(); + panPartType[nParts + i] = SHPP_OUTERRING; + } + else + { + poRing = poPoly->getInteriorRing(i-1); + panPartType[nParts + i] = SHPP_INNERRING; + } + poPoints = (OGRRawPoint*)CPLRealloc(poPoints, + (nPoints + poRing->getNumPoints()) * sizeof(OGRRawPoint)); + padfZ = (double*)CPLRealloc(padfZ, + (nPoints + poRing->getNumPoints()) * sizeof(double)); + for( int k = 0; k < poRing->getNumPoints(); k++ ) + { + poPoints[nPoints+k].x = poRing->getX(k); + poPoints[nPoints+k].y = poRing->getY(k); + padfZ[nPoints+k] = poRing->getZ(k); + } + nPoints += poRing->getNumPoints(); + } + + nParts += nRings; + } + } + + int nShpSize = 4; /* All types start with integer type number */ + nShpSize += 16 * 2; /* xy bbox */ + nShpSize += 4; /* nparts */ + nShpSize += 4; /* npoints */ + nShpSize += 4 * nParts; /* panPartStart[nparts] */ + nShpSize += 4 * nParts; /* panPartType[nparts] */ + nShpSize += 8 * 2 * nPoints; /* xy points */ + nShpSize += 16; /* z bbox */ + nShpSize += 8 * nPoints; /* z points */ + + *pnBytes = nShpSize; + *ppabyShape = (GByte*) CPLMalloc(nShpSize); + + GByte* pabyPtr = *ppabyShape; + + /* Write in the type number and advance the pointer */ + GUInt32 nGType = CPL_LSBWORD32( SHPT_MULTIPATCH ); + memcpy( pabyPtr, &nGType, 4 ); + pabyPtr += 4; + + OGREnvelope3D envelope; + poGeom->getEnvelope(&envelope); + memcpy( pabyPtr, &(envelope.MinX), 8 ); + memcpy( pabyPtr+8, &(envelope.MinY), 8 ); + memcpy( pabyPtr+8+8, &(envelope.MaxX), 8 ); + memcpy( pabyPtr+8+8+8, &(envelope.MaxY), 8 ); + + int i; + /* Swap box if needed. Shape doubles are always LSB */ + if( OGR_SWAP( wkbNDR ) ) + { + for ( i = 0; i < 4; i++ ) + CPL_SWAPDOUBLE( pabyPtr + 8*i ); + } + pabyPtr += 32; + + /* Write in the part count */ + GUInt32 nPartsLsb = CPL_LSBWORD32( nParts ); + memcpy( pabyPtr, &nPartsLsb, 4 ); + pabyPtr += 4; + + /* Write in the total point count */ + GUInt32 nPointsLsb = CPL_LSBWORD32( nPoints ); + memcpy( pabyPtr, &nPointsLsb, 4 ); + pabyPtr += 4; + + for( i = 0; i < nParts; i ++ ) + { + int nPartStart = CPL_LSBWORD32(panPartStart[i]); + memcpy( pabyPtr, &nPartStart, 4 ); + pabyPtr += 4; + } + for( i = 0; i < nParts; i ++ ) + { + int nPartType = CPL_LSBWORD32(panPartType[i]); + memcpy( pabyPtr, &nPartType, 4 ); + pabyPtr += 4; + } + + memcpy(pabyPtr, poPoints, 2 * 8 * nPoints); + /* Swap box if needed. Shape doubles are always LSB */ + if( OGR_SWAP( wkbNDR ) ) + { + for ( i = 0; i < 2 * nPoints; i++ ) + CPL_SWAPDOUBLE( pabyPtr + 8*i ); + } + pabyPtr += 2 * 8 * nPoints; + + memcpy( pabyPtr, &(envelope.MinZ), 8 ); + memcpy( pabyPtr+8, &(envelope.MaxZ), 8 ); + if( OGR_SWAP( wkbNDR ) ) + { + for ( i = 0; i < 2; i++ ) + CPL_SWAPDOUBLE( pabyPtr + 8*i ); + } + pabyPtr += 16; + + memcpy(pabyPtr, padfZ, 8 * nPoints); + /* Swap box if needed. Shape doubles are always LSB */ + if( OGR_SWAP( wkbNDR ) ) + { + for ( i = 0; i < nPoints; i++ ) + CPL_SWAPDOUBLE( pabyPtr + 8*i ); + } + pabyPtr += 8 * nPoints; + + CPLFree(panPartStart); + CPLFree(panPartType); + CPLFree(poPoints); + CPLFree(padfZ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGRCreateFromShapeBin() */ +/* */ +/* Translate shapefile binary representation to an OGR */ +/* geometry. */ +/************************************************************************/ + +OGRErr OGRCreateFromShapeBin( GByte *pabyShape, + OGRGeometry **ppoGeom, + int nBytes ) + +{ + *ppoGeom = NULL; + + if( nBytes < 4 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Shape buffer size (%d) too small", + nBytes); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Detect zlib compressed shapes and uncompress buffer if necessary */ +/* NOTE: this seems to be an undocumented feature, even in the */ +/* extended_shapefile_format.pdf found in the FileGDB API documentation*/ +/* -------------------------------------------------------------------- */ + if( nBytes >= 14 && + pabyShape[12] == 0x78 && pabyShape[13] == 0xDA /* zlib marker */) + { + GInt32 nUncompressedSize, nCompressedSize; + memcpy( &nUncompressedSize, pabyShape + 4, 4 ); + memcpy( &nCompressedSize, pabyShape + 8, 4 ); + CPL_LSBPTR32( &nUncompressedSize ); + CPL_LSBPTR32( &nCompressedSize ); + if (nCompressedSize + 12 == nBytes && + nUncompressedSize > 0) + { + GByte* pabyUncompressedBuffer = (GByte*)VSIMalloc(nUncompressedSize); + if (pabyUncompressedBuffer == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate %d bytes to uncompress zlib buffer", + nUncompressedSize); + return OGRERR_FAILURE; + } + + size_t nRealUncompressedSize = 0; + if( CPLZLibInflate( pabyShape + 12, nCompressedSize, + pabyUncompressedBuffer, nUncompressedSize, + &nRealUncompressedSize ) == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "CPLZLibInflate() failed"); + VSIFree(pabyUncompressedBuffer); + return OGRERR_FAILURE; + } + + OGRErr eErr = OGRCreateFromShapeBin(pabyUncompressedBuffer, + ppoGeom, + nRealUncompressedSize); + + VSIFree(pabyUncompressedBuffer); + + return eErr; + } + } + + int nSHPType = pabyShape[0]; + +/* -------------------------------------------------------------------- */ +/* Return a NULL geometry when SHPT_NULL is encountered. */ +/* Watch out, null return does not mean "bad data" it means */ +/* "no geometry here". Watch the OGRErr for the error status */ +/* -------------------------------------------------------------------- */ + if ( nSHPType == SHPT_NULL ) + { + *ppoGeom = NULL; + return OGRERR_NONE; + } + +// CPLDebug( "PGeo", +// "Shape type read from PGeo data is nSHPType = %d", +// nSHPType ); + +/* -------------------------------------------------------------------- */ +/* TODO: These types include additional attributes including */ +/* non-linear segments and such. They should be handled. */ +/* This is documented in the extended_shapefile_format.pdf */ +/* from the FileGDB API */ +/* -------------------------------------------------------------------- */ + switch( nSHPType ) + { + case SHPT_GENERALPOLYLINE: + nSHPType = SHPT_ARC; + break; + case SHPT_GENERALPOLYGON: + nSHPType = SHPT_POLYGON; + break; + case SHPT_GENERALPOINT: + nSHPType = SHPT_POINT; + break; + case SHPT_GENERALMULTIPOINT: + nSHPType = SHPT_MULTIPOINT; + break; + case SHPT_GENERALMULTIPATCH: + nSHPType = SHPT_MULTIPATCH; + } + +/* ==================================================================== */ +/* Extract vertices for a Polygon or Arc. */ +/* ==================================================================== */ + if( nSHPType == SHPT_ARC + || nSHPType == SHPT_ARCZ + || nSHPType == SHPT_ARCM + || nSHPType == SHPT_ARCZM + || nSHPType == SHPT_POLYGON + || nSHPType == SHPT_POLYGONZ + || nSHPType == SHPT_POLYGONM + || nSHPType == SHPT_POLYGONZM + || nSHPType == SHPT_MULTIPATCH + || nSHPType == SHPT_MULTIPATCHM) + { + GInt32 nPoints, nParts; + int i, nOffset; + GInt32 *panPartStart; + GInt32 *panPartType = NULL; + + if (nBytes < 44) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Corrupted Shape : nBytes=%d, nSHPType=%d", nBytes, nSHPType); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Extract part/point count, and build vertex and part arrays */ +/* to proper size. */ +/* -------------------------------------------------------------------- */ + memcpy( &nPoints, pabyShape + 40, 4 ); + memcpy( &nParts, pabyShape + 36, 4 ); + + CPL_LSBPTR32( &nPoints ); + CPL_LSBPTR32( &nParts ); + + if (nPoints < 0 || nParts < 0 || + nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000) + { + CPLError(CE_Failure, CPLE_AppDefined, "Corrupted Shape : nPoints=%d, nParts=%d.", + nPoints, nParts); + return OGRERR_FAILURE; + } + + int bHasZ = ( nSHPType == SHPT_POLYGONZ + || nSHPType == SHPT_POLYGONZM + || nSHPType == SHPT_ARCZ + || nSHPType == SHPT_ARCZM + || nSHPType == SHPT_MULTIPATCH + || nSHPType == SHPT_MULTIPATCHM ); + + int bIsMultiPatch = ( nSHPType == SHPT_MULTIPATCH || nSHPType == SHPT_MULTIPATCHM ); + + /* With the previous checks on nPoints and nParts, */ + /* we should not overflow here and after */ + /* since 50 M * (16 + 8 + 8) = 1 600 MB */ + int nRequiredSize = 44 + 4 * nParts + 16 * nPoints; + if ( bHasZ ) + { + nRequiredSize += 16 + 8 * nPoints; + } + if( bIsMultiPatch ) + { + nRequiredSize += 4 * nParts; + } + if (nRequiredSize > nBytes) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Corrupted Shape : nPoints=%d, nParts=%d, nBytes=%d, nSHPType=%d, nRequiredSize=%d", + nPoints, nParts, nBytes, nSHPType, nRequiredSize); + return OGRERR_FAILURE; + } + + panPartStart = (GInt32 *) VSICalloc(nParts,sizeof(GInt32)); + if (panPartStart == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Not enough memory for shape (nPoints=%d, nParts=%d)", nPoints, nParts); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Copy out the part array from the record. */ +/* -------------------------------------------------------------------- */ + memcpy( panPartStart, pabyShape + 44, 4 * nParts ); + for( i = 0; i < nParts; i++ ) + { + CPL_LSBPTR32( panPartStart + i ); + + /* We check that the offset is inside the vertex array */ + if (panPartStart[i] < 0 || + panPartStart[i] >= nPoints) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Corrupted Shape : panPartStart[%d] = %d, nPoints = %d", + i, panPartStart[i], nPoints); + CPLFree(panPartStart); + return OGRERR_FAILURE; + } + if (i > 0 && panPartStart[i] <= panPartStart[i-1]) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Corrupted Shape : panPartStart[%d] = %d, panPartStart[%d] = %d", + i, panPartStart[i], i - 1, panPartStart[i - 1]); + CPLFree(panPartStart); + return OGRERR_FAILURE; + } + } + + nOffset = 44 + 4*nParts; + +/* -------------------------------------------------------------------- */ +/* If this is a multipatch, we will also have parts types. */ +/* -------------------------------------------------------------------- */ + if( bIsMultiPatch ) + { + panPartType = (GInt32 *) VSICalloc(nParts,sizeof(GInt32)); + if (panPartType == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Not enough memory for panPartType for shape (nPoints=%d, nParts=%d)", nPoints, nParts); + CPLFree(panPartStart); + return OGRERR_FAILURE; + } + + memcpy( panPartType, pabyShape + nOffset, 4*nParts ); + for( i = 0; i < nParts; i++ ) + { + CPL_LSBPTR32( panPartType + i ); + } + nOffset += 4*nParts; + } + +/* -------------------------------------------------------------------- */ +/* Copy out the vertices from the record. */ +/* -------------------------------------------------------------------- */ + double *padfX = (double *) VSIMalloc(sizeof(double)*nPoints); + double *padfY = (double *) VSIMalloc(sizeof(double)*nPoints); + double *padfZ = (double *) VSICalloc(sizeof(double),nPoints); + if (padfX == NULL || padfY == NULL || padfZ == NULL) + { + CPLFree( panPartStart ); + CPLFree( panPartType ); + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + CPLError(CE_Failure, CPLE_OutOfMemory, + "Not enough memory for shape (nPoints=%d, nParts=%d)", nPoints, nParts); + return OGRERR_FAILURE; + } + + for( i = 0; i < nPoints; i++ ) + { + memcpy(padfX + i, pabyShape + nOffset + i * 16, 8 ); + memcpy(padfY + i, pabyShape + nOffset + i * 16 + 8, 8 ); + CPL_LSBPTR64( padfX + i ); + CPL_LSBPTR64( padfY + i ); + } + + nOffset += 16*nPoints; + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( bHasZ ) + { + for( i = 0; i < nPoints; i++ ) + { + memcpy( padfZ + i, pabyShape + nOffset + 16 + i*8, 8 ); + CPL_LSBPTR64( padfZ + i ); + } + + nOffset += 16 + 8*nPoints; + } + +/* -------------------------------------------------------------------- */ +/* Build corresponding OGR objects. */ +/* -------------------------------------------------------------------- */ + if( nSHPType == SHPT_ARC + || nSHPType == SHPT_ARCZ + || nSHPType == SHPT_ARCM + || nSHPType == SHPT_ARCZM ) + { +/* -------------------------------------------------------------------- */ +/* Arc - As LineString */ +/* -------------------------------------------------------------------- */ + if( nParts == 1 ) + { + OGRLineString *poLine = new OGRLineString(); + *ppoGeom = poLine; + + poLine->setPoints( nPoints, padfX, padfY, padfZ ); + } + +/* -------------------------------------------------------------------- */ +/* Arc - As MultiLineString */ +/* -------------------------------------------------------------------- */ + else + { + OGRMultiLineString *poMulti = new OGRMultiLineString; + *ppoGeom = poMulti; + + for( i = 0; i < nParts; i++ ) + { + OGRLineString *poLine = new OGRLineString; + int nVerticesInThisPart; + + if( i == nParts-1 ) + nVerticesInThisPart = nPoints - panPartStart[i]; + else + nVerticesInThisPart = + panPartStart[i+1] - panPartStart[i]; + + poLine->setPoints( nVerticesInThisPart, + padfX + panPartStart[i], + padfY + panPartStart[i], + padfZ + panPartStart[i] ); + + poMulti->addGeometryDirectly( poLine ); + } + } + } /* ARC */ + +/* -------------------------------------------------------------------- */ +/* Polygon */ +/* -------------------------------------------------------------------- */ + else if( nSHPType == SHPT_POLYGON + || nSHPType == SHPT_POLYGONZ + || nSHPType == SHPT_POLYGONM + || nSHPType == SHPT_POLYGONZM ) + { + if (nParts != 0) + { + if (nParts == 1) + { + OGRPolygon *poOGRPoly = new OGRPolygon; + *ppoGeom = poOGRPoly; + OGRLinearRing *poRing = new OGRLinearRing; + int nVerticesInThisPart = nPoints - panPartStart[0]; + + poRing->setPoints( nVerticesInThisPart, + padfX + panPartStart[0], + padfY + panPartStart[0], + padfZ + panPartStart[0] ); + + poOGRPoly->addRingDirectly( poRing ); + } + else + { + OGRGeometry *poOGR = NULL; + OGRPolygon** tabPolygons = new OGRPolygon*[nParts]; + + for( i = 0; i < nParts; i++ ) + { + tabPolygons[i] = new OGRPolygon(); + OGRLinearRing *poRing = new OGRLinearRing; + int nVerticesInThisPart; + + if( i == nParts-1 ) + nVerticesInThisPart = nPoints - panPartStart[i]; + else + nVerticesInThisPart = + panPartStart[i+1] - panPartStart[i]; + + poRing->setPoints( nVerticesInThisPart, + padfX + panPartStart[i], + padfY + panPartStart[i], + padfZ + panPartStart[i] ); + tabPolygons[i]->addRingDirectly(poRing); + } + + int isValidGeometry; + const char* papszOptions[] = { "METHOD=ONLY_CCW", NULL }; + poOGR = OGRGeometryFactory::organizePolygons( + (OGRGeometry**)tabPolygons, nParts, &isValidGeometry, papszOptions ); + + if (!isValidGeometry) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Geometry of polygon cannot be translated to Simple Geometry. " + "All polygons will be contained in a multipolygon.\n"); + } + + *ppoGeom = poOGR; + delete[] tabPolygons; + } + } + } /* polygon */ + +/* -------------------------------------------------------------------- */ +/* Multipatch */ +/* -------------------------------------------------------------------- */ + else if( bIsMultiPatch ) + { + *ppoGeom = OGRCreateFromMultiPatch( nParts, + panPartStart, + panPartType, + nPoints, + padfX, + padfY, + padfZ ); + } + + CPLFree( panPartStart ); + CPLFree( panPartType ); + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + + if (*ppoGeom != NULL) + (*ppoGeom)->setCoordinateDimension( bHasZ ? 3 : 2 ); + + return OGRERR_NONE; + } + +/* ==================================================================== */ +/* Extract vertices for a MultiPoint. */ +/* ==================================================================== */ + else if( nSHPType == SHPT_MULTIPOINT + || nSHPType == SHPT_MULTIPOINTM + || nSHPType == SHPT_MULTIPOINTZ + || nSHPType == SHPT_MULTIPOINTZM ) + { + GInt32 nPoints; + GInt32 nOffsetZ; + int i; + + int bHasZ = ( nSHPType == SHPT_MULTIPOINTZ + || nSHPType == SHPT_MULTIPOINTZM ); + + + memcpy( &nPoints, pabyShape + 36, 4 ); + CPL_LSBPTR32( &nPoints ); + + if (nPoints < 0 || nPoints > 50 * 1000 * 1000 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Corrupted Shape : nPoints=%d.", + nPoints); + return OGRERR_FAILURE; + } + + nOffsetZ = 40 + 2*8*nPoints + 2*8; + + OGRMultiPoint *poMultiPt = new OGRMultiPoint; + *ppoGeom = poMultiPt; + + for( i = 0; i < nPoints; i++ ) + { + double x, y, z; + OGRPoint *poPt = new OGRPoint; + + /* Copy X */ + memcpy(&x, pabyShape + 40 + i*16, 8); + CPL_LSBPTR64(&x); + poPt->setX(x); + + /* Copy Y */ + memcpy(&y, pabyShape + 40 + i*16 + 8, 8); + CPL_LSBPTR64(&y); + poPt->setY(y); + + /* Copy Z */ + if ( bHasZ ) + { + memcpy(&z, pabyShape + nOffsetZ + i*8, 8); + CPL_LSBPTR64(&z); + poPt->setZ(z); + } + + poMultiPt->addGeometryDirectly( poPt ); + } + + poMultiPt->setCoordinateDimension( bHasZ ? 3 : 2 ); + + return OGRERR_NONE; + } + +/* ==================================================================== */ +/* Extract vertices for a point. */ +/* ==================================================================== */ + else if( nSHPType == SHPT_POINT + || nSHPType == SHPT_POINTM + || nSHPType == SHPT_POINTZ + || nSHPType == SHPT_POINTZM ) + { + /* int nOffset; */ + double dfX, dfY, dfZ = 0; + + int bHasZ = (nSHPType == SHPT_POINTZ || nSHPType == SHPT_POINTZM); + + if (nBytes < 4 + 8 + 8 + ((bHasZ) ? 8 : 0)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Corrupted Shape : nBytes=%d, nSHPType=%d", nBytes, nSHPType); + return OGRERR_FAILURE; + } + + memcpy( &dfX, pabyShape + 4, 8 ); + memcpy( &dfY, pabyShape + 4 + 8, 8 ); + + CPL_LSBPTR64( &dfX ); + CPL_LSBPTR64( &dfY ); + /* nOffset = 20 + 8; */ + + if( bHasZ ) + { + memcpy( &dfZ, pabyShape + 4 + 16, 8 ); + CPL_LSBPTR64( &dfZ ); + } + + *ppoGeom = new OGRPoint( dfX, dfY, dfZ ); + (*ppoGeom)->setCoordinateDimension( bHasZ ? 3 : 2 ); + + return OGRERR_NONE; + } + + CPLError(CE_Failure, CPLE_AppDefined, + "Unsupported geometry type: %d", + nSHPType ); + + return OGRERR_FAILURE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrpgeogeometry.h b/bazaar/plugin/gdal/ogr/ogrpgeogeometry.h new file mode 100644 index 000000000..7b7b22bdb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrpgeogeometry.h @@ -0,0 +1,96 @@ +/****************************************************************************** + * $Id: ogrpgeogeometry.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements decoder of shapebin geometry for PGeo + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2011-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_PGEOGEOMETRY_H_INCLUDED +#define _OGR_PGEOGEOMETRY_H_INCLUDED + +#include "ogr_geometry.h" + +#define SHPT_NULL 0 + +#define SHPT_POINT 1 +#define SHPT_POINTM 21 +#define SHPT_POINTZM 11 +#define SHPT_POINTZ 9 + +#define SHPT_MULTIPOINT 8 +#define SHPT_MULTIPOINTM 28 +#define SHPT_MULTIPOINTZM 18 +#define SHPT_MULTIPOINTZ 20 + +#define SHPT_ARC 3 +#define SHPT_ARCM 23 +#define SHPT_ARCZM 13 +#define SHPT_ARCZ 10 + +#define SHPT_POLYGON 5 +#define SHPT_POLYGONM 25 +#define SHPT_POLYGONZM 15 +#define SHPT_POLYGONZ 19 + +#define SHPT_MULTIPATCHM 31 +#define SHPT_MULTIPATCH 32 + +#define SHPT_GENERALPOLYLINE 50 +#define SHPT_GENERALPOLYGON 51 +#define SHPT_GENERALPOINT 52 +#define SHPT_GENERALMULTIPOINT 53 +#define SHPT_GENERALMULTIPATCH 54 + +/* The following are layers geometry type */ +/* They are different from the above shape types */ +#define ESRI_LAYERGEOMTYPE_NULL 0 +#define ESRI_LAYERGEOMTYPE_POINT 1 +#define ESRI_LAYERGEOMTYPE_MULTIPOINT 2 +#define ESRI_LAYERGEOMTYPE_POLYLINE 3 +#define ESRI_LAYERGEOMTYPE_POLYGON 4 +#define ESRI_LAYERGEOMTYPE_MULTIPATCH 9 + +void OGRCreateFromMultiPatchPart(OGRMultiPolygon *poMP, + OGRPolygon*& poLastPoly, + int nPartType, + int nPartPoints, + double* padfX, + double* padfY, + double* padfZ); + +OGRErr CPL_DLL OGRCreateFromShapeBin( GByte *pabyShape, + OGRGeometry **ppoGeom, + int nBytes ); + +OGRErr CPL_DLL OGRWriteToShapeBin( OGRGeometry *poGeom, + GByte **ppabyShape, + int *pnBytes ); + +OGRErr CPL_DLL OGRWriteMultiPatchToShapeBin( OGRGeometry *poGeom, + GByte **ppabyShape, + int *pnBytes ); + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrpoint.cpp b/bazaar/plugin/gdal/ogr/ogrpoint.cpp new file mode 100644 index 000000000..d46f4f732 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrpoint.cpp @@ -0,0 +1,612 @@ +/****************************************************************************** + * $Id: ogrpoint.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The Point geometry class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" + +/* for std::numeric_limits */ +#include + +CPL_CVSID("$Id: ogrpoint.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +/* CAUTION: we use nCoordDimension == -2 to mean POINT EMPTY (2D) and + nCoordDimension == -3 to mean POINT Z EMPTY +*/ + +/************************************************************************/ +/* OGRPoint() */ +/************************************************************************/ + +/** + * \brief Create a (0,0) point. + */ + +OGRPoint::OGRPoint() + +{ + empty(); +} + +/************************************************************************/ +/* OGRPoint() */ +/* */ +/* Initialize point to value. */ +/************************************************************************/ + +OGRPoint::OGRPoint( double xIn, double yIn, double zIn ) + +{ + x = xIn; + y = yIn; + z = zIn; + nCoordDimension = 3; +} + +/************************************************************************/ +/* OGRPoint() */ +/* */ +/* Initialize point to value. */ +/************************************************************************/ + +OGRPoint::OGRPoint( double xIn, double yIn ) + +{ + x = xIn; + y = yIn; + z = 0.0; + nCoordDimension = 2; +} + +/************************************************************************/ +/* ~OGRPoint() */ +/************************************************************************/ + +OGRPoint::~OGRPoint() + +{ +} + +/************************************************************************/ +/* clone() */ +/* */ +/* Make a new object that is a copy of this object. */ +/************************************************************************/ + +OGRGeometry *OGRPoint::clone() const + +{ + OGRPoint *poNewPoint = new OGRPoint( x, y, z ); + + poNewPoint->assignSpatialReference( getSpatialReference() ); + poNewPoint->setCoordinateDimension( nCoordDimension ); + + return poNewPoint; +} + +/************************************************************************/ +/* empty() */ +/************************************************************************/ +void OGRPoint::empty() + +{ + x = y = z = 0.0; + nCoordDimension = -2; +} + +/************************************************************************/ +/* getDimension() */ +/************************************************************************/ + +int OGRPoint::getDimension() const + +{ + return 0; +} + +/************************************************************************/ +/* getGeometryType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRPoint::getGeometryType() const + +{ + if( getCoordinateDimension() == 3 ) + return wkbPoint25D; + else + return wkbPoint; +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRPoint::getGeometryName() const + +{ + return "POINT"; +} + +/************************************************************************/ +/* flattenTo2D() */ +/************************************************************************/ + +void OGRPoint::flattenTo2D() + +{ + z = 0; + if (nCoordDimension > 2) + nCoordDimension = 2; +} + +/************************************************************************/ +/* getCoordinateDimension() */ +/************************************************************************/ + +int OGRPoint::getCoordinateDimension() const + +{ + return nCoordDimension < 0 ? -nCoordDimension : nCoordDimension; +} + +/************************************************************************/ +/* setCoordinateDimension() */ +/************************************************************************/ + +void OGRPoint::setCoordinateDimension( int nNewDimension ) + +{ + nCoordDimension = nNewDimension; + + if( nCoordDimension == 2 ) + z = 0; +} + +/************************************************************************/ +/* WkbSize() */ +/* */ +/* Return the size of this object in well known binary */ +/* representation including the byte order, and type information. */ +/************************************************************************/ + +int OGRPoint::WkbSize() const + +{ + if( getCoordinateDimension() != 3 ) + return 21; + else + return 29; +} + +/************************************************************************/ +/* importFromWkb() */ +/* */ +/* Initialize from serialized stream in well known binary */ +/* format. */ +/************************************************************************/ + +OGRErr OGRPoint::importFromWkb( unsigned char * pabyData, + int nSize, + OGRwkbVariant eWkbVariant ) + +{ + OGRwkbByteOrder eByteOrder; + OGRBoolean bIs3D = FALSE; + + OGRErr eErr = importPreambuleFromWkb( pabyData, nSize, eByteOrder, bIs3D, eWkbVariant ); + if( eErr >= 0 ) + return eErr; + + if ( nSize < ((bIs3D) ? 29 : 21) && nSize != -1 ) + return OGRERR_NOT_ENOUGH_DATA; + +/* -------------------------------------------------------------------- */ +/* Get the vertex. */ +/* -------------------------------------------------------------------- */ + memcpy( &x, pabyData + 5, 8 ); + memcpy( &y, pabyData + 5 + 8, 8 ); + + if( OGR_SWAP( eByteOrder ) ) + { + CPL_SWAPDOUBLE( &x ); + CPL_SWAPDOUBLE( &y ); + } + + if( bIs3D ) + { + memcpy( &z, pabyData + 5 + 16, 8 ); + if( OGR_SWAP( eByteOrder ) ) + { + CPL_SWAPDOUBLE( &z ); + } + nCoordDimension = 3; + } + else + { + z = 0; + nCoordDimension = 2; + } + + /* Detect NaN coordinates --> EMPTY */ + if( x != x && y != y ) + nCoordDimension = -nCoordDimension; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* exportToWkb() */ +/* */ +/* Build a well known binary representation of this object. */ +/************************************************************************/ + +OGRErr OGRPoint::exportToWkb( OGRwkbByteOrder eByteOrder, + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const + +{ +/* -------------------------------------------------------------------- */ +/* Set the byte order. */ +/* -------------------------------------------------------------------- */ + pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER((unsigned char) eByteOrder); + +/* -------------------------------------------------------------------- */ +/* Set the geometry feature type. */ +/* -------------------------------------------------------------------- */ + GUInt32 nGType = getGeometryType(); + + if ( eWkbVariant == wkbVariantIso ) + nGType = getIsoGeometryType(); + + if( eByteOrder == wkbNDR ) + nGType = CPL_LSBWORD32( nGType ); + else + nGType = CPL_MSBWORD32( nGType ); + + memcpy( pabyData + 1, &nGType, 4 ); + +/* -------------------------------------------------------------------- */ +/* Copy in the raw data. */ +/* -------------------------------------------------------------------- */ + + if ( IsEmpty() && eWkbVariant == wkbVariantIso ) + { + double dNan = std::numeric_limits::quiet_NaN(); + memcpy( pabyData+5, &dNan, 8 ); + memcpy( pabyData+5+8, &dNan, 8 ); + if( getCoordinateDimension() == 3 ) + memcpy( pabyData+5+16, &dNan, 8 ); + } + else + { + memcpy( pabyData+5, &x, 16 ); + if( getCoordinateDimension() == 3 ) + { + memcpy( pabyData + 5 + 16, &z, 8 ); + } + } + +/* -------------------------------------------------------------------- */ +/* Swap if needed. */ +/* -------------------------------------------------------------------- */ + if( OGR_SWAP( eByteOrder ) ) + { + CPL_SWAPDOUBLE( pabyData + 5 ); + CPL_SWAPDOUBLE( pabyData + 5 + 8 ); + + if( getCoordinateDimension() == 3 ) + CPL_SWAPDOUBLE( pabyData + 5 + 16 ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importFromWkt() */ +/* */ +/* Instantiate point from well known text format ``POINT */ +/* (x,y)''. */ +/************************************************************************/ + +OGRErr OGRPoint::importFromWkt( char ** ppszInput ) + +{ + int bHasZ = FALSE, bHasM = FALSE; + OGRErr eErr = importPreambuleFromWkt(ppszInput, &bHasZ, &bHasM); + if( eErr >= 0 ) + { + if( eErr == OGRERR_NONE ) /* only the case for an EMPTY case */ + nCoordDimension = (bHasZ) ? -3 : -2; + + return eErr; + } + + const char *pszInput = *ppszInput; + +/* -------------------------------------------------------------------- */ +/* Read the point list which should consist of exactly one point. */ +/* -------------------------------------------------------------------- */ + OGRRawPoint *poPoints = NULL; + double *padfZ = NULL; + int nMaxPoint = 0, nPoints = 0; + + pszInput = OGRWktReadPoints( pszInput, &poPoints, &padfZ, + &nMaxPoint, &nPoints ); + if( pszInput == NULL || nPoints != 1 ) + { + CPLFree( poPoints ); + CPLFree( padfZ ); + return OGRERR_CORRUPT_DATA; + } + + x = poPoints[0].x; + y = poPoints[0].y; + + CPLFree( poPoints ); + + if( padfZ != NULL ) + { + /* If there's a 3rd value, and it is not a POINT M, */ + /* then assume it is the Z */ + if ((!(bHasM && !bHasZ))) + { + z = padfZ[0]; + nCoordDimension = 3; + } + else + nCoordDimension = 2; + CPLFree( padfZ ); + } + else if ( bHasZ ) + { + /* In theory we should have a z coordinate for POINT Z */ + /* oh well, let be tolerant */ + nCoordDimension = 3; + } + else + nCoordDimension = 2; + + *ppszInput = (char *) pszInput; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* exportToWkt() */ +/* */ +/* Translate this structure into it's well known text format */ +/* equivelent. */ +/************************************************************************/ + +OGRErr OGRPoint::exportToWkt( char ** ppszDstText, + OGRwkbVariant eWkbVariant ) const + +{ + char szTextEquiv[140]; + char szCoordinate[80]; + + if ( IsEmpty() ) + { + if( getCoordinateDimension() == 3 && eWkbVariant == wkbVariantIso ) + *ppszDstText = CPLStrdup("POINT Z EMPTY"); + else + *ppszDstText = CPLStrdup("POINT EMPTY"); + } + else + { + OGRMakeWktCoordinate(szCoordinate, x, y, z, getCoordinateDimension() ); + if( getCoordinateDimension() == 3 && eWkbVariant == wkbVariantIso ) + sprintf( szTextEquiv, "POINT Z (%s)", szCoordinate ); + else + sprintf( szTextEquiv, "POINT (%s)", szCoordinate ); + *ppszDstText = CPLStrdup( szTextEquiv ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRPoint::getEnvelope( OGREnvelope * psEnvelope ) const + +{ + psEnvelope->MinX = psEnvelope->MaxX = getX(); + psEnvelope->MinY = psEnvelope->MaxY = getY(); +} + +/************************************************************************/ +/* getEnvelope() */ +/************************************************************************/ + +void OGRPoint::getEnvelope( OGREnvelope3D * psEnvelope ) const + +{ + psEnvelope->MinX = psEnvelope->MaxX = getX(); + psEnvelope->MinY = psEnvelope->MaxY = getY(); + psEnvelope->MinZ = psEnvelope->MaxZ = getZ(); +} + + +/** + * \fn double OGRPoint::getX() const; + * + * \brief Fetch X coordinate. + * + * Relates to the SFCOM IPoint::get_X() method. + * + * @return the X coordinate of this point. + */ + +/** + * \fn double OGRPoint::getY() const; + * + * \brief Fetch Y coordinate. + * + * Relates to the SFCOM IPoint::get_Y() method. + * + * @return the Y coordinate of this point. + */ + +/** + * \fn double OGRPoint::getZ() const; + * + * \brief Fetch Z coordinate. + * + * Relates to the SFCOM IPoint::get_Z() method. + * + * @return the Z coordinate of this point, or zero if it is a 2D point. + */ + +/** + * \fn void OGRPoint::setX( double xIn ); + * + * \brief Assign point X coordinate. + * + * There is no corresponding SFCOM method. + */ + +/** + * \fn void OGRPoint::setY( double yIn ); + * + * \brief Assign point Y coordinate. + * + * There is no corresponding SFCOM method. + */ + +/** + * \fn void OGRPoint::setZ( double zIn ); + * + * \brief Assign point Z coordinate. + * Calling this method will force the geometry + * coordinate dimension to 3D (wkbPoint|wkbZ). + * + * There is no corresponding SFCOM method. + */ + +/************************************************************************/ +/* Equal() */ +/************************************************************************/ + +OGRBoolean OGRPoint::Equals( OGRGeometry * poOther ) const + +{ + OGRPoint *poOPoint = (OGRPoint *) poOther; + + if( poOPoint== this ) + return TRUE; + + if( poOther->getGeometryType() != getGeometryType() ) + return FALSE; + + if ( IsEmpty() && poOther->IsEmpty() ) + return TRUE; + + // we should eventually test the SRS. + + if( poOPoint->getX() != getX() + || poOPoint->getY() != getY() + || poOPoint->getZ() != getZ() ) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* transform() */ +/************************************************************************/ + +OGRErr OGRPoint::transform( OGRCoordinateTransformation *poCT ) + +{ +#ifdef DISABLE_OGRGEOM_TRANSFORM + return OGRERR_FAILURE; +#else + if( poCT->Transform( 1, &x, &y, &z ) ) + { + assignSpatialReference( poCT->GetTargetCS() ); + return OGRERR_NONE; + } + else + return OGRERR_FAILURE; +#endif +} + +/************************************************************************/ +/* IsEmpty() */ +/************************************************************************/ + +OGRBoolean OGRPoint::IsEmpty( ) const +{ + return nCoordDimension < 0; +} + +/************************************************************************/ +/* swapXY() */ +/************************************************************************/ + +void OGRPoint::swapXY() +{ + double dfTemp = x; + x = y; + y = dfTemp; +} + +/************************************************************************/ +/* Within() */ +/************************************************************************/ + +OGRBoolean OGRPoint::Within( const OGRGeometry *poOtherGeom ) const + +{ + if( !IsEmpty() && poOtherGeom != NULL && + wkbFlatten(poOtherGeom->getGeometryType()) == wkbCurvePolygon ) + { + return ((OGRCurvePolygon*)poOtherGeom)->Contains(this); + } + else + return OGRGeometry::Within(poOtherGeom); +} + +/************************************************************************/ +/* Intersects() */ +/************************************************************************/ + +OGRBoolean OGRPoint::Intersects( const OGRGeometry *poOtherGeom ) const + +{ + if( !IsEmpty() && poOtherGeom != NULL && + wkbFlatten(poOtherGeom->getGeometryType()) == wkbCurvePolygon ) + { + return ((OGRCurvePolygon*)poOtherGeom)->Intersects(this); + } + else + return OGRGeometry::Intersects(poOtherGeom); +} diff --git a/bazaar/plugin/gdal/ogr/ogrpolygon.cpp b/bazaar/plugin/gdal/ogr/ogrpolygon.cpp new file mode 100644 index 000000000..2ed14312f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrpolygon.cpp @@ -0,0 +1,754 @@ +/****************************************************************************** + * $Id: ogrpolygon.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRPolygon geometry class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" +#include "ogr_geos.h" +#include "ogr_api.h" + +CPL_CVSID("$Id: ogrpolygon.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +/************************************************************************/ +/* OGRPolygon() */ +/************************************************************************/ + +/** + * \brief Create an empty polygon. + */ + +OGRPolygon::OGRPolygon() + +{ +} + +/************************************************************************/ +/* ~OGRPolygon() */ +/************************************************************************/ + +OGRPolygon::~OGRPolygon() + +{ +} + +/************************************************************************/ +/* getGeometryType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRPolygon::getGeometryType() const + +{ + if( nCoordDimension == 3 ) + return wkbPolygon25D; + else + return wkbPolygon; +} + +/************************************************************************/ +/* getGeometryName() */ +/************************************************************************/ + +const char * OGRPolygon::getGeometryName() const + +{ + return "POLYGON"; +} + +/************************************************************************/ +/* getExteriorRing() */ +/************************************************************************/ + +/** + * \brief Fetch reference to external polygon ring. + * + * Note that the returned ring pointer is to an internal data object of + * the OGRPolygon. It should not be modified or deleted by the application, + * and the pointer is only valid till the polygon is next modified. Use + * the OGRGeometry::clone() method to make a separate copy within the + * application. + * + * Relates to the SFCOM IPolygon::get_ExteriorRing() method. + * + * @return pointer to external ring. May be NULL if the OGRPolygon is empty. + */ + +OGRLinearRing *OGRPolygon::getExteriorRing() + +{ + if( oCC.nCurveCount > 0 ) + return (OGRLinearRing*) oCC.papoCurves[0]; + else + return NULL; +} + +const OGRLinearRing *OGRPolygon::getExteriorRing() const + +{ + if( oCC.nCurveCount > 0 ) + return (OGRLinearRing*) oCC.papoCurves[0]; + else + return NULL; +} + +/************************************************************************/ +/* stealExteriorRing() */ +/************************************************************************/ + +/** + * \brief "Steal" reference to external polygon ring. + * + * After the call to that function, only call to stealInteriorRing() or + * destruction of the OGRPolygon is valid. Other operations may crash. + * + * @return pointer to external ring. May be NULL if the OGRPolygon is empty. + */ + +OGRLinearRing *OGRPolygon::stealExteriorRing() +{ + return (OGRLinearRing*)stealExteriorRingCurve(); +} + +/************************************************************************/ +/* getInteriorRing() */ +/************************************************************************/ + +/** + * \brief Fetch reference to indicated internal ring. + * + * Note that the returned ring pointer is to an internal data object of + * the OGRPolygon. It should not be modified or deleted by the application, + * and the pointer is only valid till the polygon is next modified. Use + * the OGRGeometry::clone() method to make a separate copy within the + * application. + * + * Relates to the SFCOM IPolygon::get_InternalRing() method. + * + * @param iRing internal ring index from 0 to getNumInternalRings() - 1. + * + * @return pointer to interior ring. May be NULL. + */ + +OGRLinearRing *OGRPolygon::getInteriorRing( int iRing ) + +{ + if( iRing < 0 || iRing >= oCC.nCurveCount-1 ) + return NULL; + else + return (OGRLinearRing*) oCC.papoCurves[iRing+1]; +} + +const OGRLinearRing *OGRPolygon::getInteriorRing( int iRing ) const + +{ + if( iRing < 0 || iRing >= oCC.nCurveCount-1 ) + return NULL; + else + return (OGRLinearRing*) oCC.papoCurves[iRing+1]; +} + +/************************************************************************/ +/* stealInteriorRing() */ +/************************************************************************/ + +/** + * \brief "Steal" reference to indicated interior ring. + * + * After the call to that function, only call to stealInteriorRing() or + * destruction of the OGRPolygon is valid. Other operations may crash. + * + * @param iRing internal ring index from 0 to getNumInternalRings() - 1. + * @return pointer to interior ring. May be NULL. + */ + +OGRLinearRing *OGRPolygon::stealInteriorRing(int iRing) +{ + if( iRing < 0 || iRing >= oCC.nCurveCount-1 ) + return NULL; + OGRLinearRing *poRet = (OGRLinearRing*) oCC.papoCurves[iRing+1]; + oCC.papoCurves[iRing+1] = NULL; + return poRet; +} + +/************************************************************************/ +/* checkRing() */ +/************************************************************************/ + +int OGRPolygon::checkRing( OGRCurve * poNewRing ) const +{ + if ( !(EQUAL(poNewRing->getGeometryName(), "LINEARRING")) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong curve type. Expected LINEARRING."); + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* WkbSize() */ +/* */ +/* Return the size of this object in well known binary */ +/* representation including the byte order, and type information. */ +/************************************************************************/ + +int OGRPolygon::WkbSize() const + +{ + int nSize = 9; + int b3D = getCoordinateDimension() == 3; + + for( int i = 0; i < oCC.nCurveCount; i++ ) + { + nSize += ((OGRLinearRing*)oCC.papoCurves[i])->_WkbSize( b3D ); + } + + return nSize; +} + +/************************************************************************/ +/* importFromWkb() */ +/* */ +/* Initialize from serialized stream in well known binary */ +/* format. */ +/************************************************************************/ + +OGRErr OGRPolygon::importFromWkb( unsigned char * pabyData, + int nSize, + OGRwkbVariant eWkbVariant ) + +{ + OGRwkbByteOrder eByteOrder; + int nDataOffset = 0; + OGRErr eErr = oCC.importPreambuleFromWkb(this, pabyData, nSize, nDataOffset, + eByteOrder, 4, eWkbVariant); + if( eErr >= 0 ) + return eErr; + + int b3D = (nCoordDimension == 3); +/* -------------------------------------------------------------------- */ +/* Get the rings. */ +/* -------------------------------------------------------------------- */ + for( int iRing = 0; iRing < oCC.nCurveCount; iRing++ ) + { + OGRErr eErr; + + OGRLinearRing* poLR = new OGRLinearRing(); + oCC.papoCurves[iRing] = poLR; + eErr = poLR->_importFromWkb( eByteOrder, b3D, + pabyData + nDataOffset, + nSize ); + if( eErr != OGRERR_NONE ) + { + delete oCC.papoCurves[iRing]; + oCC.nCurveCount = iRing; + return eErr; + } + + if( nSize != -1 ) + nSize -= poLR->_WkbSize( b3D ); + + nDataOffset += poLR->_WkbSize( b3D ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* exportToWkb() */ +/* */ +/* Build a well known binary representation of this object. */ +/************************************************************************/ + +OGRErr OGRPolygon::exportToWkb( OGRwkbByteOrder eByteOrder, + unsigned char * pabyData, + OGRwkbVariant eWkbVariant ) const + +{ + int nOffset; + int b3D = getCoordinateDimension() == 3; + +/* -------------------------------------------------------------------- */ +/* Set the byte order. */ +/* -------------------------------------------------------------------- */ + pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER((unsigned char) eByteOrder); + +/* -------------------------------------------------------------------- */ +/* Set the geometry feature type. */ +/* -------------------------------------------------------------------- */ + GUInt32 nGType = getGeometryType(); + + if ( eWkbVariant == wkbVariantIso ) + nGType = getIsoGeometryType(); + + if( eByteOrder == wkbNDR ) + nGType = CPL_LSBWORD32( nGType ); + else + nGType = CPL_MSBWORD32( nGType ); + + memcpy( pabyData + 1, &nGType, 4 ); + +/* -------------------------------------------------------------------- */ +/* Copy in the raw data. */ +/* -------------------------------------------------------------------- */ + if( OGR_SWAP( eByteOrder ) ) + { + int nCount; + + nCount = CPL_SWAP32( oCC.nCurveCount ); + memcpy( pabyData+5, &nCount, 4 ); + } + else + { + memcpy( pabyData+5, &oCC.nCurveCount, 4 ); + } + + nOffset = 9; + +/* ==================================================================== */ +/* Serialize each of the rings. */ +/* ==================================================================== */ + for( int iRing = 0; iRing < oCC.nCurveCount; iRing++ ) + { + OGRLinearRing* poLR = (OGRLinearRing*) oCC.papoCurves[iRing]; + poLR->_exportToWkb( eByteOrder, b3D, + pabyData + nOffset ); + + nOffset += poLR->_WkbSize(b3D); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* importFromWkt() */ +/* */ +/* Instantiate from well known text format. Currently this is */ +/* `POLYGON ((x y, x y, ...),(x y, ...),...)'. */ +/************************************************************************/ + +OGRErr OGRPolygon::importFromWkt( char ** ppszInput ) + +{ + int bHasZ = FALSE, bHasM = FALSE; + OGRErr eErr = importPreambuleFromWkt(ppszInput, &bHasZ, &bHasM); + if( eErr >= 0 ) + return eErr; + + OGRRawPoint *paoPoints = NULL; + int nMaxPoints = 0; + double *padfZ = NULL; + + eErr = importFromWKTListOnly( ppszInput, bHasZ, bHasM, + paoPoints, nMaxPoints, padfZ ); + + CPLFree( paoPoints ); + CPLFree( padfZ ); + + return eErr; +} + +/************************************************************************/ +/* importFromWKTListOnly() */ +/* */ +/* Instantiate from "((x y, x y, ...),(x y, ...),...)" */ +/************************************************************************/ + +OGRErr OGRPolygon::importFromWKTListOnly( char ** ppszInput, int bHasZ, int bHasM, + OGRRawPoint*& paoPoints, int& nMaxPoints, + double*& padfZ ) + +{ + char szToken[OGR_WKT_TOKEN_MAX]; + const char *pszInput = *ppszInput; + + /* Skip first '(' */ + pszInput = OGRWktReadToken( pszInput, szToken ); + if( EQUAL(szToken, "EMPTY") ) + { + *ppszInput = (char*) pszInput; + return OGRERR_NONE; + } + if( !EQUAL(szToken, "(") ) + return OGRERR_CORRUPT_DATA; + +/* ==================================================================== */ +/* Read each ring in turn. Note that we try to reuse the same */ +/* point list buffer from ring to ring to cut down on */ +/* allocate/deallocate overhead. */ +/* ==================================================================== */ + int nMaxRings = 0; + + nCoordDimension = 2; + + do + { + int nPoints = 0; + + const char* pszNext = OGRWktReadToken( pszInput, szToken ); + if (EQUAL(szToken,"EMPTY")) + { +/* -------------------------------------------------------------------- */ +/* Do we need to grow the ring array? */ +/* -------------------------------------------------------------------- */ + if( oCC.nCurveCount == nMaxRings ) + { + nMaxRings = nMaxRings * 2 + 1; + oCC.papoCurves = (OGRCurve **) + CPLRealloc(oCC.papoCurves, nMaxRings * sizeof(OGRLinearRing*)); + } + oCC.papoCurves[oCC.nCurveCount] = new OGRLinearRing(); + oCC.nCurveCount++; + + pszInput = OGRWktReadToken( pszNext, szToken ); + if ( !EQUAL(szToken, ",") ) + break; + + continue; + } + +/* -------------------------------------------------------------------- */ +/* Read points for one ring from input. */ +/* -------------------------------------------------------------------- */ + pszInput = OGRWktReadPoints( pszInput, &paoPoints, &padfZ, &nMaxPoints, + &nPoints ); + + if( pszInput == NULL || nPoints == 0 ) + { + return OGRERR_CORRUPT_DATA; + } + +/* -------------------------------------------------------------------- */ +/* Do we need to grow the ring array? */ +/* -------------------------------------------------------------------- */ + if( oCC.nCurveCount == nMaxRings ) + { + nMaxRings = nMaxRings * 2 + 1; + oCC.papoCurves = (OGRCurve **) + CPLRealloc(oCC.papoCurves, nMaxRings * sizeof(OGRLinearRing*)); + } + +/* -------------------------------------------------------------------- */ +/* Create the new ring, and assign to ring list. */ +/* -------------------------------------------------------------------- */ + OGRLinearRing* poLR = new OGRLinearRing(); + oCC.papoCurves[oCC.nCurveCount] = poLR; + /* Ignore Z array when we have a POLYGON M */ + if (bHasM && !bHasZ) + poLR->setPoints( nPoints, paoPoints, NULL ); + else + poLR->setPoints( nPoints, paoPoints, padfZ ); + + oCC.nCurveCount++; + + if( padfZ && !(bHasM && !bHasZ) ) + nCoordDimension = 3; + +/* -------------------------------------------------------------------- */ +/* Read the delimeter following the ring. */ +/* -------------------------------------------------------------------- */ + + pszInput = OGRWktReadToken( pszInput, szToken ); + } while( szToken[0] == ',' ); + +/* -------------------------------------------------------------------- */ +/* freak if we don't get a closing bracket. */ +/* -------------------------------------------------------------------- */ + + if( szToken[0] != ')' ) + return OGRERR_CORRUPT_DATA; + + *ppszInput = (char *) pszInput; + return OGRERR_NONE; +} + +/************************************************************************/ +/* exportToWkt() */ +/* */ +/* Translate this structure into it's well known text format */ +/* equivelent. This could be made alot more CPU efficient! */ +/************************************************************************/ + +OGRErr OGRPolygon::exportToWkt( char ** ppszDstText, + OGRwkbVariant eWkbVariant ) const + +{ + char **papszRings; + int iRing, nCumulativeLength = 0, nNonEmptyRings = 0; + OGRErr eErr; + int bMustWriteComma = FALSE; + +/* -------------------------------------------------------------------- */ +/* If we have no valid exterior ring, return POLYGON EMPTY. */ +/* -------------------------------------------------------------------- */ + if (getExteriorRing() == NULL || + getExteriorRing()->IsEmpty() ) + { + if( getCoordinateDimension() == 3 && eWkbVariant == wkbVariantIso ) + *ppszDstText = CPLStrdup("POLYGON Z EMPTY"); + else + *ppszDstText = CPLStrdup("POLYGON EMPTY"); + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Build a list of strings containing the stuff for each ring. */ +/* -------------------------------------------------------------------- */ + papszRings = (char **) CPLCalloc(sizeof(char *),oCC.nCurveCount); + + for( iRing = 0; iRing < oCC.nCurveCount; iRing++ ) + { + OGRLinearRing* poLR = (OGRLinearRing*) oCC.papoCurves[iRing]; + poLR->setCoordinateDimension( getCoordinateDimension() ); + if( poLR->getNumPoints() == 0 ) + { + papszRings[iRing] = NULL; + continue; + } + + eErr = poLR->exportToWkt( &(papszRings[iRing]) ); + if( eErr != OGRERR_NONE ) + goto error; + + CPLAssert( EQUALN(papszRings[iRing],"LINEARRING (", 12) ); + nCumulativeLength += strlen(papszRings[iRing] + 11); + + nNonEmptyRings++; + } + +/* -------------------------------------------------------------------- */ +/* Allocate exactly the right amount of space for the */ +/* aggregated string. */ +/* -------------------------------------------------------------------- */ + *ppszDstText = (char *) VSIMalloc(nCumulativeLength + nNonEmptyRings + 15); + + if( *ppszDstText == NULL ) + { + eErr = OGRERR_NOT_ENOUGH_MEMORY; + goto error; + } + +/* -------------------------------------------------------------------- */ +/* Build up the string, freeing temporary strings as we go. */ +/* -------------------------------------------------------------------- */ + if( getCoordinateDimension() == 3 && eWkbVariant == wkbVariantIso ) + strcpy( *ppszDstText, "POLYGON Z (" ); + else + strcpy( *ppszDstText, "POLYGON (" ); + nCumulativeLength = strlen(*ppszDstText); + + for( iRing = 0; iRing < oCC.nCurveCount; iRing++ ) + { + if( papszRings[iRing] == NULL ) + { + CPLDebug( "OGR", "OGRPolygon::exportToWkt() - skipping empty ring."); + continue; + } + + if( bMustWriteComma ) + (*ppszDstText)[nCumulativeLength++] = ','; + bMustWriteComma = TRUE; + + int nRingLen = strlen(papszRings[iRing] + 11); + memcpy( *ppszDstText + nCumulativeLength, papszRings[iRing] + 11, nRingLen ); + nCumulativeLength += nRingLen; + VSIFree( papszRings[iRing] ); + } + + (*ppszDstText)[nCumulativeLength++] = ')'; + (*ppszDstText)[nCumulativeLength] = '\0'; + + CPLFree( papszRings ); + + return OGRERR_NONE; + +error: + for( iRing = 0; iRing < oCC.nCurveCount; iRing++ ) + CPLFree(papszRings[iRing]); + CPLFree(papszRings); + return eErr; +} + +/************************************************************************/ +/* PointOnSurface() */ +/************************************************************************/ + +int OGRPolygon::PointOnSurface( OGRPoint *poPoint ) const + +{ + if( poPoint == NULL || poPoint->IsEmpty() ) + return OGRERR_FAILURE; + + OGRGeometryH hInsidePoint = OGR_G_PointOnSurface( (OGRGeometryH) this ); + if( hInsidePoint == NULL ) + return OGRERR_FAILURE; + + OGRPoint *poInsidePoint = (OGRPoint *) hInsidePoint; + if( poInsidePoint->IsEmpty() ) + poPoint->empty(); + else + { + poPoint->setX( poInsidePoint->getX() ); + poPoint->setY( poInsidePoint->getY() ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* IsPointOnSurface() */ +/************************************************************************/ + +OGRBoolean OGRPolygon::IsPointOnSurface( const OGRPoint * pt) const +{ + if ( NULL == pt) + return 0; + + for( int iRing = 0; iRing < oCC.nCurveCount; iRing++ ) + { + if ( ((OGRLinearRing*)oCC.papoCurves[iRing])->isPointInRing(pt) ) + { + return 1; + } + } + + return 0; +} + +/************************************************************************/ +/* closeRings() */ +/************************************************************************/ + +void OGRPolygon::closeRings() + +{ + for( int iRing = 0; iRing < oCC.nCurveCount; iRing++ ) + oCC.papoCurves[iRing]->closeRings(); +} + +/************************************************************************/ +/* CurvePolyToPoly() */ +/************************************************************************/ + +OGRPolygon* OGRPolygon::CurvePolyToPoly(CPL_UNUSED double dfMaxAngleStepSizeDegrees, + CPL_UNUSED const char* const* papszOptions) const +{ + return (OGRPolygon*) clone(); +} + +/************************************************************************/ +/* hasCurveGeometry() */ +/************************************************************************/ + +OGRBoolean OGRPolygon::hasCurveGeometry(CPL_UNUSED int bLookForNonLinear) const +{ + return FALSE; +} + +/************************************************************************/ +/* getLinearGeometry() */ +/************************************************************************/ + +OGRGeometry* OGRPolygon::getLinearGeometry(double dfMaxAngleStepSizeDegrees, + const char* const* papszOptions) const +{ + return OGRGeometry::getLinearGeometry(dfMaxAngleStepSizeDegrees, papszOptions); +} + +/************************************************************************/ +/* getCurveGeometry() */ +/************************************************************************/ + +OGRGeometry* OGRPolygon::getCurveGeometry(const char* const* papszOptions) const +{ + OGRCurvePolygon* poCC = new OGRCurvePolygon(); + poCC->assignSpatialReference( getSpatialReference() ); + int bHasCurveGeometry = FALSE; + for( int iRing = 0; iRing < oCC.nCurveCount; iRing++ ) + { + OGRCurve* poSubGeom = (OGRCurve* )oCC.papoCurves[iRing]->getCurveGeometry(papszOptions); + if( wkbFlatten(poSubGeom->getGeometryType()) != wkbLineString ) + bHasCurveGeometry = TRUE; + poCC->addRingDirectly( poSubGeom ); + } + if( !bHasCurveGeometry ) + { + delete poCC; + return clone(); + } + return poCC; +} + +/************************************************************************/ +/* CastToCurvePolygon() */ +/************************************************************************/ + +/** + * \brief Cast to curve polygon. + * + * The passed in geometry is consumed and a new one returned . + * + * @param poPoly the input geometry - ownership is passed to the method. + * @return new geometry. + */ + +OGRCurvePolygon* OGRPolygon::CastToCurvePolygon(OGRPolygon* poPoly) +{ + OGRCurvePolygon* poCP = new OGRCurvePolygon(); + poCP->setCoordinateDimension(poPoly->getCoordinateDimension()); + poCP->assignSpatialReference(poPoly->getSpatialReference()); + poCP->oCC.nCurveCount = poPoly->oCC.nCurveCount; + poCP->oCC.papoCurves = poPoly->oCC.papoCurves; + poPoly->oCC.nCurveCount = 0; + poPoly->oCC.papoCurves = NULL; + + for( int iRing = 0; iRing < poCP->oCC.nCurveCount; iRing++ ) + { + poCP->oCC.papoCurves[iRing] = OGRLinearRing::CastToLineString( + (OGRLinearRing*)poCP->oCC.papoCurves[iRing] ); + } + + delete poPoly; + return poCP; +} + +/************************************************************************/ +/* GetCasterToPolygon() */ +/************************************************************************/ + +OGRSurfaceCasterToPolygon OGRPolygon::GetCasterToPolygon() const { + return (OGRSurfaceCasterToPolygon) OGRGeometry::CastToIdentity; +} + +/************************************************************************/ +/* OGRSurfaceCasterToCurvePolygon() */ +/************************************************************************/ + +OGRSurfaceCasterToCurvePolygon OGRPolygon::GetCasterToCurvePolygon() const { + return (OGRSurfaceCasterToCurvePolygon) OGRPolygon::CastToCurvePolygon; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/GNUmakefile new file mode 100644 index 000000000..4d8b37112 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/GNUmakefile @@ -0,0 +1,52 @@ + +include ../../GDALmake.opt + +SUBDIRS-yes := \ + generic avc bna csv dgn geojson gml gmt mem kml \ + mitab ntf gpx rec s57 sdts shape tiger vrt \ + geoconcept xplane georss gtm dxf pgdump gpsbabel \ + sua openair pds htf aeronavfaa edigeo svg idrisi \ + arcgen segukooa segy sxf openfilegdb wasp selafin jml + +SUBDIRS-$(HAVE_DODS) += dods +SUBDIRS-$(HAVE_DWGDIRECT) += dxfdwg +SUBDIRS-$(HAVE_FME) += fme +SUBDIRS-$(HAVE_GRASS) += grass +SUBDIRS-$(HAVE_IDB) += idb +SUBDIRS-$(HAVE_XERCES) += ili +SUBDIRS-$(HAVE_NAS) += nas +SUBDIRS-$(HAVE_MYSQL) += mysql +SUBDIRS-$(ODBC_SETTING) += odbc pgeo mssqlspatial geomedia walk +SUBDIRS-$(HAVE_OGDI) += ogdi +SUBDIRS-$(HAVE_OCI) += oci +SUBDIRS-$(HAVE_OGR_PG) += pg +SUBDIRS-$(HAVE_SQLITE) += sqlite +SUBDIRS-$(HAVE_SDE) += sde +SUBDIRS-$(HAVE_FGDB) += filegdb +SUBDIRS-$(HAVE_ARCOBJECTS) += arcobjects +SUBDIRS-$(HAVE_INGRES) += ingres +SUBDIRS-$(HAVE_SQLITE) += vfk +SUBDIRS-$(HAVE_LIBKML) += libkml +SUBDIRS-$(CURL_SETTING) += wfs +SUBDIRS-$(MDB_ENABLED) += mdb +SUBDIRS-$(CURL_SETTING) += gft +SUBDIRS-$(CURL_SETTING) += gme +SUBDIRS-$(CURL_SETTING) += couchdb +SUBDIRS-$(CURL_SETTING) += cloudant +SUBDIRS-$(HAVE_FREEXL) += xls +SUBDIRS-$(HAVE_EXPAT) += ods +SUBDIRS-$(HAVE_EXPAT) += xlsx +SUBDIRS-$(CURL_SETTING) += elastic +SUBDIRS-$(HAVE_SQLITE) += gpkg +SUBDIRS-$(HAVE_SQLITE) += osm +SUBDIRS-$(HAVE_SOSI) += sosi +SUBDIRS-$(CURL_SETTING) += cartodb +SUBDIRS-$(CURL_SETTING) += plscenes +SUBDIRS-$(CURL_SETTING) += csw + +default: $(foreach d,$(SUBDIRS-yes),$(d)-target) + +clean: $(foreach d,$(SUBDIRS-yes),$(d)-clean) + rm -f o/*.o + $(RM) o/*.lo + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/GNUmakefile new file mode 100644 index 000000000..cdf7460ac --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ograeronavfaadriver.o ograeronavfaadatasource.o ograeronavfaalayer.o + +CPPFLAGS := -I.. -I../.. -I../xplane $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_aeronavfaa.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/drv_aeronavfaa.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/drv_aeronavfaa.html new file mode 100644 index 000000000..27a5568d3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/drv_aeronavfaa.html @@ -0,0 +1,23 @@ + + +Aeronav FAA + + + + +

    Aeronav FAA

    + +(GDAL/OGR >= 1.8.0)

    + +This driver reads text files describing aeronav information - obstacles, navaids and routes - as provided by the FAA.

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/makefile.vc new file mode 100644 index 000000000..89dde0613 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ograeronavfaadriver.obj ograeronavfaadatasource.obj ograeronavfaalayer.obj +EXTRAFLAGS = -I.. -I..\.. -I..\xplane + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/ogr_aeronavfaa.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/ogr_aeronavfaa.h new file mode 100644 index 000000000..0dbf3b697 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/ogr_aeronavfaa.h @@ -0,0 +1,188 @@ +/****************************************************************************** + * $Id: ogr_aeronavfaa.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: AeronavFAA Translator + * Purpose: Definition of classes for OGR AeronavFAA driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_AeronavFAA_H_INCLUDED +#define _OGR_AeronavFAA_H_INCLUDED + +#include "ogrsf_frmts.h" + + +typedef struct +{ + const char* pszFieldName; + int nStartCol; /* starting at 1 */ + int nLastCol; /* starting at 1 */ + OGRFieldType eType; +} RecordFieldDesc; + +typedef struct +{ + int nFields; + const RecordFieldDesc* pasFields; + int nLatStartCol; /* starting at 1 */ + int nLonStartCol; /* starting at 1 */ +} RecordDesc; + + + +/************************************************************************/ +/* OGRAeronavFAALayer */ +/************************************************************************/ + +class OGRAeronavFAALayer : public OGRLayer +{ +protected: + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference *poSRS; + + VSILFILE* fpAeronavFAA; + int bEOF; + + int nNextFID; + + const RecordDesc* psRecordDesc; + + virtual OGRFeature * GetNextRawFeature() = 0; + + public: + OGRAeronavFAALayer(VSILFILE* fp, const char* pszLayerName); + ~OGRAeronavFAALayer(); + + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRAeronavFAADOFLayer */ +/************************************************************************/ + +class OGRAeronavFAADOFLayer : public OGRAeronavFAALayer +{ + private: + int GetLatLon(const char* pszLat, const char* pszLon, double& dfLat, double& dfLon); + + protected: + virtual OGRFeature * GetNextRawFeature(); + + public: + OGRAeronavFAADOFLayer(VSILFILE* fp, const char* pszLayerName); +}; + +/************************************************************************/ +/* OGRAeronavFAANAVAIDLayer */ +/************************************************************************/ + +class OGRAeronavFAANAVAIDLayer : public OGRAeronavFAALayer +{ + private: + int GetLatLon(const char* pszLat, const char* pszLon, double& dfLat, double& dfLon); + + protected: + virtual OGRFeature * GetNextRawFeature(); + + public: + OGRAeronavFAANAVAIDLayer(VSILFILE* fp, const char* pszLayerName); +}; + +/************************************************************************/ +/* OGRAeronavFAARouteLayer */ +/************************************************************************/ + +class OGRAeronavFAARouteLayer : public OGRAeronavFAALayer +{ + private: + int bIsDPOrSTARS; + CPLString osLastReadLine; + CPLString osAPTName; + CPLString osStateName; + int GetLatLon(const char* pszLat, const char* pszLon, double& dfLat, double& dfLon); + + protected: + virtual OGRFeature * GetNextRawFeature(); + + public: + OGRAeronavFAARouteLayer(VSILFILE* fp, const char* pszLayerName, int bIsDPOrSTARS); + + virtual void ResetReading(); +}; + +/************************************************************************/ +/* OGRAeronavFAAIAPLayer */ +/************************************************************************/ + +class OGRAeronavFAAIAPLayer : public OGRAeronavFAALayer +{ + private: + CPLString osCityName; + CPLString osStateName; + CPLString osAPTName; + CPLString osAPTId; + int GetLatLon(const char* pszLat, const char* pszLon, double& dfLat, double& dfLon); + + protected: + virtual OGRFeature * GetNextRawFeature(); + + public: + OGRAeronavFAAIAPLayer(VSILFILE* fp, const char* pszLayerName); + + virtual void ResetReading(); +}; + +/************************************************************************/ +/* OGRAeronavFAADataSource */ +/************************************************************************/ + +class OGRAeronavFAADataSource : public OGRDataSource +{ + char* pszName; + + OGRLayer** papoLayers; + int nLayers; + + public: + OGRAeronavFAADataSource(); + ~OGRAeronavFAADataSource(); + + int Open( const char * pszFilename ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); +}; + + +#endif /* ndef _OGR_AeronavFAA_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/ograeronavfaadatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/ograeronavfaadatasource.cpp new file mode 100644 index 000000000..63329c7f7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/ograeronavfaadatasource.cpp @@ -0,0 +1,162 @@ +/****************************************************************************** + * $Id: ograeronavfaadatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: AeronavFAA Translator + * Purpose: Implements OGRAeronavFAADataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_aeronavfaa.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ograeronavfaadatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRAeronavFAADataSource() */ +/************************************************************************/ + +OGRAeronavFAADataSource::OGRAeronavFAADataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; +} + +/************************************************************************/ +/* ~OGRAeronavFAADataSource() */ +/************************************************************************/ + +OGRAeronavFAADataSource::~OGRAeronavFAADataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRAeronavFAADataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRAeronavFAADataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRAeronavFAADataSource::Open( const char * pszFilename ) + +{ + pszName = CPLStrdup( pszFilename ); + +// -------------------------------------------------------------------- +// Does this appear to be a .dat file? +// -------------------------------------------------------------------- + + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return FALSE; + + char szBuffer[10000]; + int nbRead = (int)VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fp); + szBuffer[nbRead] = '\0'; + + int bIsDOF = (szBuffer[128] == 13 && szBuffer[128+1] == 10 && + szBuffer[130+128] == 13 && szBuffer[130+129] == 10 && + szBuffer[2*130+128] == 13 && szBuffer[2*130+129] == 10 && + strncmp(szBuffer + 3 * 130, "------------------------------------------------------------------------------------------------------------------------- ", 122) == 0); + + int bIsNAVAID = (szBuffer[132] == 13 && szBuffer[132+1] == 10 && + strncmp(szBuffer + 20 - 1, "CREATION DATE", strlen("CREATION DATE")) == 0 && + szBuffer[134 + 132] == 13 && szBuffer[134 + 132+1] == 10); + + int bIsROUTE = strncmp(szBuffer, " UNITED STATES GOVERNMENT FLIGHT INFORMATION PUBLICATION 149343", 85) == 0 && + szBuffer[85] == 13 && szBuffer[85+1] == 10; + + int bIsIAP = strstr(szBuffer, "INSTRUMENT APPROACH PROCEDURE NAVAID & FIX DATA") != NULL && + szBuffer[85] == 13 && szBuffer[85+1] == 10; + if (bIsIAP) + bIsROUTE = FALSE; + + if (bIsDOF) + { + VSIFSeekL( fp, 0, SEEK_SET ); + nLayers = 1; + papoLayers = (OGRLayer**) CPLMalloc(sizeof(OGRLayer*)); + papoLayers[0] = new OGRAeronavFAADOFLayer(fp, CPLGetBasename(pszFilename)); + return TRUE; + } + else if (bIsNAVAID) + { + VSIFSeekL( fp, 0, SEEK_SET ); + nLayers = 1; + papoLayers = (OGRLayer**) CPLMalloc(sizeof(OGRLayer*)); + papoLayers[0] = new OGRAeronavFAANAVAIDLayer(fp, CPLGetBasename(pszFilename)); + return TRUE; + } + else if (bIsIAP) + { + VSIFSeekL( fp, 0, SEEK_SET ); + nLayers = 1; + papoLayers = (OGRLayer**) CPLMalloc(sizeof(OGRLayer*)); + papoLayers[0] = new OGRAeronavFAAIAPLayer(fp, CPLGetBasename(pszFilename)); + return TRUE; + } + else if (bIsROUTE) + { + int bIsDPOrSTARS = strstr(szBuffer, "DPs - DEPARTURE PROCEDURES") != NULL || + strstr(szBuffer, "STARS - STANDARD TERMINAL ARRIVALS") != NULL; + VSIFSeekL( fp, 0, SEEK_SET ); + nLayers = 1; + papoLayers = (OGRLayer**) CPLMalloc(sizeof(OGRLayer*)); + papoLayers[0] = new OGRAeronavFAARouteLayer(fp, CPLGetBasename(pszFilename), bIsDPOrSTARS); + return TRUE; + } + else + { + VSIFCloseL(fp); + return FALSE; + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/ograeronavfaadriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/ograeronavfaadriver.cpp new file mode 100644 index 000000000..8a6f9a1aa --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/ograeronavfaadriver.cpp @@ -0,0 +1,92 @@ +/****************************************************************************** + * $Id: ograeronavfaadriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: AeronavFAA Translator + * Purpose: Implements OGRAeronavFAADriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_aeronavfaa.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ograeronavfaadriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +// g++ ogr/ogrsf_frmts/aeronavfaa/*.cpp -Wall -g -fPIC -shared -o ogr_AeronavFAA.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts/aernovfaa -Iogr/ogrsf_frmts + +extern "C" void RegisterOGRAeronavFAA(); + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRAeronavFAADriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if (poOpenInfo->eAccess == GA_Update || + poOpenInfo->fpL == NULL || + !EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "dat") ) + { + return NULL; + } + + OGRAeronavFAADataSource *poDS = new OGRAeronavFAADataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRAeronavFAA() */ +/************************************************************************/ + +void RegisterOGRAeronavFAA() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "AeronavFAA" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "AeronavFAA" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Aeronav FAA" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_aeronavfaa.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRAeronavFAADriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/ograeronavfaalayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/ograeronavfaalayer.cpp new file mode 100644 index 000000000..7b1bfbac7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/aeronavfaa/ograeronavfaalayer.cpp @@ -0,0 +1,753 @@ +/****************************************************************************** + * $Id: ograeronavfaalayer.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: AeronavFAA Translator + * Purpose: Implements OGRAeronavFAALayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_aeronavfaa.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: ograeronavfaalayer.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* OGRAeronavFAALayer() */ +/************************************************************************/ + +OGRAeronavFAALayer::OGRAeronavFAALayer( VSILFILE* fp, const char* pszLayerName ) + +{ + fpAeronavFAA = fp; + nNextFID = 0; + bEOF = FALSE; + + psRecordDesc = NULL; + + poSRS = new OGRSpatialReference(SRS_WKT_WGS84); + + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + poFeatureDefn->Reference(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + SetDescription( poFeatureDefn->GetName() ); +} + +/************************************************************************/ +/* ~OGRAeronavFAALayer() */ +/************************************************************************/ + +OGRAeronavFAALayer::~OGRAeronavFAALayer() + +{ + if( poSRS != NULL ) + poSRS->Release(); + + poFeatureDefn->Release(); + + VSIFCloseL( fpAeronavFAA ); +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRAeronavFAALayer::ResetReading() + +{ + nNextFID = 0; + bEOF = FALSE; + VSIFSeekL( fpAeronavFAA, 0, SEEK_SET ); +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRAeronavFAALayer::GetNextFeature() +{ + OGRFeature *poFeature; + + while(TRUE) + { + if (bEOF) + return NULL; + + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRAeronavFAALayer::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + + + +static const RecordFieldDesc DOFFields [] = +{ + { "ORS_CODE", 1, 2, OFTString }, + { "NUMBER" , 4, 9, OFTInteger, }, + { "VERIF_STATUS", 11, 11, OFTString }, + { "COUNTRY", 13, 14, OFTString }, + { "STATE", 16, 17, OFTString }, + { "CITY", 19, 34, OFTString }, + { "TYPE", 63, 74, OFTString }, + { "QUANTITY", 76, 76, OFTInteger }, + { "AGL_HT", 78, 82, OFTInteger }, + { "AMSL_HT", 84, 88, OFTInteger }, + { "LIGHTING", 90, 90, OFTString }, + { "HOR_ACC", 92, 92, OFTString }, + { "VER_ACC", 94, 94, OFTString }, + { "MARK_INDIC", 96, 96, OFTString }, + { "FAA_STUDY_NUMBER", 98, 111, OFTString }, + { "ACTION", 113, 113, OFTString }, + { "DATE", 115, 121, OFTString } +}; + +static const RecordDesc DOF = { sizeof(DOFFields)/sizeof(DOFFields[0]), DOFFields, 36, 49 }; + + +/************************************************************************/ +/* OGRAeronavFAADOFLayer() */ +/************************************************************************/ + +OGRAeronavFAADOFLayer::OGRAeronavFAADOFLayer( VSILFILE* fp, const char* pszLayerName ) : + OGRAeronavFAALayer(fp, pszLayerName) +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + psRecordDesc = &DOF; + + int i; + for(i=0;inFields;i++) + { + OGRFieldDefn oField( psRecordDesc->pasFields[i].pszFieldName, psRecordDesc->pasFields[i].eType ); + oField.SetWidth(psRecordDesc->pasFields[i].nLastCol - psRecordDesc->pasFields[i].nStartCol + 1); + poFeatureDefn->AddFieldDefn( &oField ); + } +} + +/************************************************************************/ +/* GetLatLon() */ +/************************************************************************/ + +static int GetLatLon(const char* pszLat, + char chLatHemisphere, + const char* pszLon, + char chLonHemisphere, + int nSecLen, + double& dfLat, double& dfLon) +{ + char szDeg[4], szMin[3], szSec[10]; + szDeg[0] = pszLat[0]; + szDeg[1] = pszLat[1]; + szDeg[2] = 0; + szMin[0] = pszLat[3]; + szMin[1] = pszLat[4]; + szMin[2] = 0; + memcpy(szSec, pszLat + 6, MAX((int)sizeof(szSec) - 1, nSecLen)); + szSec[MAX((int)sizeof(szSec) - 1, nSecLen)] = 0; + + dfLat = atoi(szDeg) + atoi(szMin) / 60. + CPLAtof(szSec) / 3600.; + if (chLatHemisphere == 'S') + dfLat = -dfLat; + + szDeg[0] = pszLon[0]; + szDeg[1] = pszLon[1]; + szDeg[2] = pszLon[2]; + szDeg[3] = 0; + szMin[0] = pszLon[4]; + szMin[1] = pszLon[5]; + szMin[2] = 0; + memcpy(szSec, pszLon + 7, MAX((int)sizeof(szSec) - 1, nSecLen)); + szSec[MAX((int)sizeof(szSec) - 1, nSecLen)] = 0; + + dfLon = atoi(szDeg) + atoi(szMin) / 60. + CPLAtof(szSec) / 3600.; + if (chLonHemisphere == ' ' || chLonHemisphere == 'W') + dfLon = -dfLon; + + return TRUE; +} + + +/************************************************************************/ +/* GetLatLon() */ +/************************************************************************/ + +int OGRAeronavFAADOFLayer::GetLatLon(const char* pszLat, const char* pszLon, double& dfLat, double& dfLon) +{ + return ::GetLatLon(pszLat, pszLat[11], pszLon, pszLon[12], 5, dfLat, dfLon); +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRAeronavFAADOFLayer::GetNextRawFeature() +{ + const char* pszLine; + char szBuffer[130]; + + while(TRUE) + { + pszLine = CPLReadLine2L(fpAeronavFAA, 130, NULL); + if (pszLine == NULL) + { + bEOF = TRUE; + return NULL; + } + if (strlen(pszLine) != 128) + continue; + if ( ! (pszLine[psRecordDesc->nLatStartCol-1] >= '0' && + pszLine[psRecordDesc->nLatStartCol-1] <= '9') ) + continue; + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFID(nNextFID ++); + + int i; + for(i=0;inFields;i++) + { + int nWidth = psRecordDesc->pasFields[i].nLastCol - psRecordDesc->pasFields[i].nStartCol + 1; + strncpy(szBuffer, pszLine + psRecordDesc->pasFields[i].nStartCol - 1, nWidth); + szBuffer[nWidth] = 0; + while(nWidth > 0 && szBuffer[nWidth - 1] == ' ') + { + szBuffer[nWidth - 1] = 0; + nWidth --; + } + if (nWidth != 0) + poFeature->SetField(i, szBuffer); + } + + double dfLat, dfLon; + GetLatLon(pszLine + psRecordDesc->nLatStartCol - 1, + pszLine + psRecordDesc->nLonStartCol - 1, + dfLat, + dfLon); + + OGRGeometry* poGeom = new OGRPoint(dfLon, dfLat); + poGeom->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly( poGeom ); + return poFeature; + } +} + + + +static const RecordFieldDesc NAVAIDFields [] = +{ + { "ID", 2, 6, OFTString }, + { "NAVAID_TYPE", 8, 9, OFTString }, + { "STATUS" , 11, 11, OFTString }, + { "NAME" , 44, 68, OFTString }, + { "CAN_ARTCC" , 69, 69, OFTString }, + { "SERVICE" , 76, 76, OFTString }, + { "FREQUENCY" , 78, 84, OFTString }, + { "CHANNEL" , 86, 89, OFTString }, + { "ELEVATION" , 92, 96, OFTString }, + { "MAG_VAR" , 98, 100, OFTString }, + { "ARTCC" , 102, 104, OFTString }, + { "STATE" , 106, 107, OFTString }, +}; + +static const RecordDesc NAVAID = { sizeof(NAVAIDFields)/sizeof(NAVAIDFields[0]), NAVAIDFields, 17, 30 }; + + +/************************************************************************/ +/* OGRAeronavFAANAVAIDLayer() */ +/************************************************************************/ + +OGRAeronavFAANAVAIDLayer::OGRAeronavFAANAVAIDLayer( VSILFILE* fp, const char* pszLayerName ) : + OGRAeronavFAALayer(fp, pszLayerName) +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + psRecordDesc = &NAVAID; + + int i; + for(i=0;inFields;i++) + { + OGRFieldDefn oField( psRecordDesc->pasFields[i].pszFieldName, psRecordDesc->pasFields[i].eType ); + oField.SetWidth(psRecordDesc->pasFields[i].nLastCol - psRecordDesc->pasFields[i].nStartCol + 1); + poFeatureDefn->AddFieldDefn( &oField ); + } +} + + +/************************************************************************/ +/* GetLatLon() */ +/************************************************************************/ + +int OGRAeronavFAANAVAIDLayer::GetLatLon(const char* pszLat, const char* pszLon, double& dfLat, double& dfLon) +{ + return ::GetLatLon(pszLat + 2, pszLat[0], pszLon + 2, pszLon[0], 4, dfLat, dfLon); +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRAeronavFAANAVAIDLayer::GetNextRawFeature() +{ + const char* pszLine; + char szBuffer[134]; + + while(TRUE) + { + pszLine = CPLReadLine2L(fpAeronavFAA, 134, NULL); + if (pszLine == NULL) + { + bEOF = TRUE; + return NULL; + } + if (strlen(pszLine) != 132) + continue; + if ( !(pszLine[psRecordDesc->nLatStartCol-1] == 'N' || + pszLine[psRecordDesc->nLatStartCol-1] == 'S') ) + continue; + if ( !(pszLine[psRecordDesc->nLonStartCol-1] == 'E' || + pszLine[psRecordDesc->nLonStartCol-1] == 'W') ) + continue; + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFID(nNextFID ++); + + int i; + for(i=0;inFields;i++) + { + int nWidth = psRecordDesc->pasFields[i].nLastCol - psRecordDesc->pasFields[i].nStartCol + 1; + strncpy(szBuffer, pszLine + psRecordDesc->pasFields[i].nStartCol - 1, nWidth); + szBuffer[nWidth] = 0; + while(nWidth > 0 && szBuffer[nWidth - 1] == ' ') + { + szBuffer[nWidth - 1] = 0; + nWidth --; + } + if (nWidth != 0) + poFeature->SetField(i, szBuffer); + } + + double dfLat, dfLon; + GetLatLon(pszLine + psRecordDesc->nLatStartCol - 1, + pszLine + psRecordDesc->nLonStartCol - 1, + dfLat, + dfLon); + + OGRGeometry* poGeom = new OGRPoint(dfLon, dfLat); + poGeom->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly( poGeom ); + return poFeature; + } +} + + + +/************************************************************************/ +/* OGRAeronavFAARouteLayer() */ +/************************************************************************/ + +OGRAeronavFAARouteLayer::OGRAeronavFAARouteLayer( VSILFILE* fp, const char* pszLayerName, int bIsDPOrSTARS ) : + OGRAeronavFAALayer(fp, pszLayerName) +{ + this->bIsDPOrSTARS = bIsDPOrSTARS; + + poFeatureDefn->SetGeomType( wkbLineString ); + + if (bIsDPOrSTARS) + { + { + OGRFieldDefn oField( "APT_NAME", OFTString ); + poFeatureDefn->AddFieldDefn( &oField ); + } + + { + OGRFieldDefn oField( "STATE", OFTString ); + poFeatureDefn->AddFieldDefn( &oField ); + } + } + + { + OGRFieldDefn oField( "NAME", OFTString ); + poFeatureDefn->AddFieldDefn( &oField ); + } + +} + + +/************************************************************************/ +/* GetLatLon() */ +/************************************************************************/ + +int OGRAeronavFAARouteLayer::GetLatLon(const char* pszLat, const char* pszLon, double& dfLat, double& dfLon) +{ + return ::GetLatLon(pszLat, pszLat[10], pszLon, pszLon[11], 4, dfLat, dfLon); +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRAeronavFAARouteLayer::GetNextRawFeature() +{ + const char* pszLine; + OGRFeature* poFeature = NULL; + OGRLineString* poLS = NULL; + + while(TRUE) + { + if (osLastReadLine.size() != 0) + pszLine = osLastReadLine.c_str(); + else + pszLine = CPLReadLine2L(fpAeronavFAA, 87, NULL); + osLastReadLine = ""; + + if (pszLine == NULL) + { + bEOF = TRUE; + return poFeature; + } + if (strlen(pszLine) != 85) + continue; + + if (bIsDPOrSTARS && strncmp(pszLine, "===", 3) == 0 && pszLine[3] != '=') + { + osAPTName = pszLine + 3; + const char* pszComma = strchr(pszLine + 3, ','); + if (pszComma) + { + osAPTName.resize(pszComma - (pszLine + 3)); + osStateName = pszComma + 2; + const char* pszEqual = strchr(pszComma + 2, '='); + if (pszEqual) + osStateName.resize(pszEqual - (pszComma + 2)); + } + else + { + const char* pszEqual = strchr(pszLine + 3, '='); + if (pszEqual) + osAPTName.resize(pszEqual - (pszLine + 3)); + osStateName = ""; + } + } + + if (strncmp(pszLine + 2, "FACILITY OR", strlen("FACILITY OR")) == 0) + continue; + if (strncmp(pszLine + 2, "INTERSECTION", strlen("INTERSECTION")) == 0) + continue; + + if (strcmp(pszLine, "================================DELETIONS LIST=================================198326") == 0) + { + bEOF = TRUE; + return poFeature; + } + + if (poFeature == NULL) + { + if (pszLine[2] == ' ' || pszLine[2] == '-' ) + { + continue; + } + + if (strncmp(pszLine + 29, " ", 20) == 0 || + strchr(pszLine, '(') != NULL) + { + CPLString osName = pszLine + 2; + osName.resize(60); + while(osName.size() > 0 && osName[osName.size()-1] == ' ') + { + osName.resize(osName.size()-1); + } + + if (strcmp(osName.c_str(), "(DELETIONS LIST)") == 0) + { + bEOF = TRUE; + return NULL; + } + + poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFID(nNextFID ++); + if (bIsDPOrSTARS) + { + poFeature->SetField(0, osAPTName); + poFeature->SetField(1, osStateName); + poFeature->SetField(2, osName); + } + else + poFeature->SetField(0, osName); + poLS = new OGRLineString(); + poFeature->SetGeometryDirectly(poLS); + } + continue; + } + + if (strncmp(pszLine, " 0", 85) == 0) + { + if (poLS->getNumPoints() == 0) + continue; + else + return poFeature; + } + + if (pszLine[29 - 1] == ' ' && pszLine[42 - 1] == ' ') + continue; + if (strstr(pszLine, "RWY") || strchr(pszLine, '(')) + { + osLastReadLine = pszLine; + return poFeature; + } + + double dfLat, dfLon; + GetLatLon(pszLine + 29 - 1, + pszLine + 42 - 1, + dfLat, + dfLon); + poLS->addPoint(dfLon, dfLat); + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRAeronavFAARouteLayer::ResetReading() + +{ + OGRAeronavFAALayer::ResetReading(); + osLastReadLine = ""; + osAPTName = ""; + osStateName = ""; +} + + +static const RecordFieldDesc IAPFields [] = +{ + { "LOC_ID", 4, 8, OFTString }, + { "MAG_VAR" , 52, 54, OFTInteger, }, + { "ELEVATION", 62, 67, OFTInteger }, + { "TYPE", 71, 77, OFTString }, + +}; + +static const RecordDesc IAP = { sizeof(IAPFields)/sizeof(IAPFields[0]), IAPFields, -1, -1 }; + + +/************************************************************************/ +/* OGRAeronavFAAIAPLayer() */ +/************************************************************************/ + +OGRAeronavFAAIAPLayer::OGRAeronavFAAIAPLayer( VSILFILE* fp, const char* pszLayerName ) : + OGRAeronavFAALayer(fp, pszLayerName) +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + { + OGRFieldDefn oField( "CITY", OFTString ); + poFeatureDefn->AddFieldDefn( &oField ); + } + { + OGRFieldDefn oField( "STATE", OFTString ); + poFeatureDefn->AddFieldDefn( &oField ); + } + { + OGRFieldDefn oField( "APT_NAME", OFTString ); + poFeatureDefn->AddFieldDefn( &oField ); + } + { + OGRFieldDefn oField( "APT_CODE", OFTString ); + poFeatureDefn->AddFieldDefn( &oField ); + } + + + psRecordDesc = &IAP; + + int i; + for(i=0;inFields;i++) + { + OGRFieldDefn oField( psRecordDesc->pasFields[i].pszFieldName, psRecordDesc->pasFields[i].eType ); + oField.SetWidth(psRecordDesc->pasFields[i].nLastCol - psRecordDesc->pasFields[i].nStartCol + 1); + poFeatureDefn->AddFieldDefn( &oField ); + } + +} + + +/************************************************************************/ +/* GetLatLon() */ +/************************************************************************/ + +int OGRAeronavFAAIAPLayer::GetLatLon(const char* pszLat, const char* pszLon, double& dfLat, double& dfLon) +{ + return ::GetLatLon(pszLat, pszLat[11], pszLon, pszLon[12], 4, dfLat, dfLon); +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRAeronavFAAIAPLayer::GetNextRawFeature() +{ + const char* pszLine; + char szBuffer[87]; + int nCountUnderscoreLines = 0; + + while(TRUE) + { + pszLine = CPLReadLine2L(fpAeronavFAA, 87, NULL); + if (pszLine == NULL) + { + bEOF = TRUE; + return NULL; + } + if (strlen(pszLine) != 85) + continue; + + if (strncmp(pszLine, "DELETIONS", strlen("DELETIONS")) == 0) + { + bEOF = TRUE; + return NULL; + } + + if (nNextFID == 0 && nCountUnderscoreLines < 2) + { + if (strcmp(pszLine, "_____________________________________________________________________________ 285285") == 0) + nCountUnderscoreLines ++; + continue; + } + + if (pszLine[1] != ' ') + continue; + if (strncmp(pszLine, " ", 79) == 0) + continue; + if (strstr(pszLine, "NAVIGATIONAL AIDS") != NULL) + continue; + if (strstr(pszLine, "TERMINAL INSTRUMENT FIXES") != NULL) + continue; + + const char* pszComma = strchr(pszLine, ','); + if (pszComma) + { + const char* pszBegin = pszLine; + while( *pszBegin == ' ') + pszBegin ++; + osCityName = pszBegin; + osCityName.resize(pszComma - pszBegin); + osStateName = pszComma + 2; + osStateName.resize(78 - (pszComma + 2 - pszLine)); + while(osStateName.size() > 0 && osStateName[osStateName.size()-1] == ' ') + { + osStateName.resize(osStateName.size()-1); + } + osAPTName = ""; + osAPTId = ""; + continue; + } + + const char* pszLeftParenthesis = strstr(pszLine, " ("); + if (pszLeftParenthesis) + { + const char* pszRightParenthesis = strchr(pszLeftParenthesis, ')'); + if (pszRightParenthesis) + { + const char* pszBegin = pszLine; + while( *pszBegin == ' ') + pszBegin ++; + osAPTName = pszBegin; + osAPTName.resize(pszLeftParenthesis - pszBegin); + osAPTId = pszLeftParenthesis + 2; + osAPTId.resize(pszRightParenthesis - (pszLeftParenthesis + 2)); + } + continue; + } + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFID(nNextFID ++); + + poFeature->SetField(0, osCityName); + poFeature->SetField(1, osStateName); + poFeature->SetField(2, osAPTName); + poFeature->SetField(3, osAPTId); + + int i; + for(i=0;inFields;i++) + { + int nWidth = psRecordDesc->pasFields[i].nLastCol - psRecordDesc->pasFields[i].nStartCol + 1; + strncpy(szBuffer, pszLine + psRecordDesc->pasFields[i].nStartCol - 1, nWidth); + szBuffer[nWidth] = 0; + while(nWidth > 0 && szBuffer[nWidth - 1] == ' ') + { + szBuffer[nWidth - 1] = 0; + nWidth --; + } + if (nWidth != 0) + poFeature->SetField(i + 4, szBuffer); + } + + double dfLat, dfLon; + GetLatLon(pszLine + 16 - 1, + (pszLine[34 - 1] != ' ') ? pszLine + 34 - 1 : pszLine + 35 - 1, + dfLat, + dfLon); + + OGRGeometry* poGeom = new OGRPoint(dfLon, dfLat); + poGeom->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly( poGeom ); + return poFeature; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRAeronavFAAIAPLayer::ResetReading() + +{ + OGRAeronavFAALayer::ResetReading(); + osCityName = ""; + osStateName = ""; + osAPTName = ""; + osAPTId = ""; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/GNUmakefile new file mode 100644 index 000000000..20086a59b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ograrcgendriver.o ograrcgendatasource.o ograrcgenlayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_arcgen.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/drv_arcgen.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/drv_arcgen.html new file mode 100644 index 000000000..3f6bd9f97 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/drv_arcgen.html @@ -0,0 +1,23 @@ + + +ARCGEN - Arc/Info Generate + + + + +

    ARCGEN - Arc/Info Generate

    + +(GDAL/OGR >= 1.9.0)

    + +This driver reads files in Arc/Info Generate format. Those files are simple +ASCII files that contain points, lines or polygons (one type of geometry per +file).

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/makefile.vc new file mode 100644 index 000000000..be9cf5da2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ograrcgendriver.obj ograrcgendatasource.obj ograrcgenlayer.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/ogr_arcgen.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/ogr_arcgen.h new file mode 100644 index 000000000..263d6b745 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/ogr_arcgen.h @@ -0,0 +1,89 @@ +/****************************************************************************** + * $Id: ogr_arcgen.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: Arc/Info Generate Translator + * Purpose: Definition of classes for OGR .arcgen driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMARCGENS OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_ARCGEN_H_INCLUDED +#define _OGR_ARCGEN_H_INCLUDED + +#include "ogrsf_frmts.h" + +/************************************************************************/ +/* OGRARCGENLayer */ +/************************************************************************/ + +class OGRARCGENLayer : public OGRLayer +{ + OGRFeatureDefn* poFeatureDefn; + + VSILFILE* fp; + int bEOF; + + int nNextFID; + + OGRFeature * GetNextRawFeature(); + + public: + OGRARCGENLayer(const char* pszFilename, + VSILFILE* fp, OGRwkbGeometryType eType); + ~OGRARCGENLayer(); + + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRARCGENDataSource */ +/************************************************************************/ + +class OGRARCGENDataSource : public OGRDataSource +{ + char* pszName; + + OGRLayer** papoLayers; + int nLayers; + + public: + OGRARCGENDataSource(); + ~OGRARCGENDataSource(); + + int Open( const char * pszFilename ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_ARCGEN_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/ograrcgendatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/ograrcgendatasource.cpp new file mode 100644 index 000000000..eb7543b2f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/ograrcgendatasource.cpp @@ -0,0 +1,217 @@ +/****************************************************************************** + * $Id: ograrcgendatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: Arc/Info Generate Translator + * Purpose: Implements OGRARCGENDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMARCGENS OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_arcgen.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ograrcgendatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRARCGENDataSource() */ +/************************************************************************/ + +OGRARCGENDataSource::OGRARCGENDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; +} + +/************************************************************************/ +/* ~OGRARCGENDataSource() */ +/************************************************************************/ + +OGRARCGENDataSource::~OGRARCGENDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRARCGENDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRARCGENDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRARCGENDataSource::Open( const char * pszFilename ) + +{ + pszName = CPLStrdup( pszFilename ); + +// -------------------------------------------------------------------- +// Does this appear to be a Arc/Info generate file? +// -------------------------------------------------------------------- + + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return FALSE; + + /* Go to end of file, and count the number of END keywords */ + /* If there's 1, it's a point layer */ + /* If there's 2, it's a linestring or polygon layer */ + VSIFSeekL( fp, 0, SEEK_END ); + vsi_l_offset nSize = VSIFTellL(fp); + if (nSize < 10) + { + VSIFCloseL(fp); + return FALSE; + } + char szBuffer[10+1]; + VSIFSeekL( fp, nSize - 10, SEEK_SET ); + VSIFReadL( szBuffer, 1, 10, fp ); + szBuffer[10] = '\0'; + + VSIFSeekL( fp, 0, SEEK_SET ); + + OGRwkbGeometryType eType; + const char* szPtr = szBuffer; + const char* szEnd = strstr(szPtr, "END"); + if (szEnd == NULL) szEnd = strstr(szPtr, "end"); + if (szEnd == NULL) + { + VSIFCloseL(fp); + return FALSE; + } + szPtr = szEnd + 3; + szEnd = strstr(szPtr, "END"); + if (szEnd == NULL) szEnd = strstr(szPtr, "end"); + if (szEnd == NULL) + { + const char* pszLine = CPLReadLine2L(fp,256,NULL); + if (pszLine == NULL) + { + VSIFCloseL(fp); + return FALSE; + } + char** papszTokens = CSLTokenizeString2( pszLine, " ,", 0 ); + int nTokens = CSLCount(papszTokens); + CSLDestroy(papszTokens); + + if (nTokens == 3) + eType = wkbPoint; + else if (nTokens == 4) + eType = wkbPoint25D; + else + { + VSIFCloseL(fp); + return FALSE; + } + } + else + { + int nLineNumber = 0; + eType = wkbUnknown; + CPLString osFirstX, osFirstY; + CPLString osLastX, osLastY; + int bIs3D = FALSE; + const char* pszLine; + while((pszLine = CPLReadLine2L(fp,256,NULL)) != NULL) + { + nLineNumber ++; + if (nLineNumber == 2) + { + char** papszTokens = CSLTokenizeString2( pszLine, " ,", 0 ); + int nTokens = CSLCount(papszTokens); + if (nTokens == 2 || nTokens == 3) + { + if (nTokens == 3) + bIs3D = TRUE; + osFirstX = papszTokens[0]; + osFirstY = papszTokens[1]; + } + CSLDestroy(papszTokens); + if (nTokens != 2 && nTokens != 3) + break; + } + else if (nLineNumber > 2) + { + if (EQUAL(pszLine, "END")) + { + if (osFirstX.compare(osLastX) == 0 && + osFirstY.compare(osLastY) == 0) + eType = (bIs3D) ? wkbPolygon25D : wkbPolygon; + else + eType = (bIs3D) ? wkbLineString25D : wkbLineString; + break; + } + + char** papszTokens = CSLTokenizeString2( pszLine, " ,", 0 ); + int nTokens = CSLCount(papszTokens); + if (nTokens == 2 || nTokens == 3) + { + osLastX = papszTokens[0]; + osLastY = papszTokens[1]; + } + CSLDestroy(papszTokens); + if (nTokens != 2 && nTokens != 3) + break; + } + } + if (eType == wkbUnknown) + { + VSIFCloseL(fp); + return FALSE; + } + } + + VSIFSeekL( fp, 0, SEEK_SET ); + + nLayers = 1; + papoLayers = (OGRLayer**) CPLMalloc(sizeof(OGRLayer*)); + papoLayers[0] = new OGRARCGENLayer(pszName, fp, eType); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/ograrcgendriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/ograrcgendriver.cpp new file mode 100644 index 000000000..8a69e72d3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/ograrcgendriver.cpp @@ -0,0 +1,135 @@ +/****************************************************************************** + * $Id: ograrcgendriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: Arc/Info Generate Translator + * Purpose: Implements OGRARCGENDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMARCGENS OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_arcgen.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ograrcgendriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +extern "C" void RegisterOGRARCGEN(); + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRARCGENDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->eAccess == GA_Update || + poOpenInfo->fpL == NULL ) + { + return NULL; + } + + /* Check that the first line is compatible with a generate file */ + /* and in particular contain >= 32 && <= 127 bytes */ + int i; + int bFoundEOL = FALSE; + char* szFirstLine = CPLStrdup((const char*) poOpenInfo->pabyHeader); + for(i=0;szFirstLine[i] != '\0';i++) + { + if (szFirstLine[i] == '\n' || szFirstLine[i] == '\r') + { + bFoundEOL = TRUE; + szFirstLine[i] = '\0'; + break; + } + if (szFirstLine[i] < 32) + { + CPLFree(szFirstLine); + return NULL; + } + } + + if (!bFoundEOL) + { + CPLFree(szFirstLine); + return NULL; + } + + char** papszTokens = CSLTokenizeString2( szFirstLine, " ,", 0 ); + int nTokens = CSLCount(papszTokens); + if (nTokens != 1 && nTokens != 3 && nTokens != 4) + { + CSLDestroy(papszTokens); + CPLFree(szFirstLine); + return NULL; + } + for(int i=0;iOpen( poOpenInfo->pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRARCGEN() */ +/************************************************************************/ + +void RegisterOGRARCGEN() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ARCGEN" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ARCGEN" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Arc/Info Generate" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_arcgen.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRARCGENDriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/ograrcgenlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/ograrcgenlayer.cpp new file mode 100644 index 000000000..29bd01dcd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcgen/ograrcgenlayer.cpp @@ -0,0 +1,232 @@ +/****************************************************************************** + * $Id: ograrcgenlayer.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: Arc/Info Generate Translator + * Purpose: Implements OGRARCGENLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMARCGENS OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_arcgen.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: ograrcgenlayer.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* OGRARCGENLayer() */ +/************************************************************************/ + +OGRARCGENLayer::OGRARCGENLayer( const char* pszFilename, + VSILFILE* fp, OGRwkbGeometryType eType ) + +{ + this->fp = fp; + nNextFID = 0; + bEOF = FALSE; + + poFeatureDefn = new OGRFeatureDefn( CPLGetBasename(pszFilename) ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( eType ); + + OGRFieldDefn oField1( "ID", OFTInteger); + poFeatureDefn->AddFieldDefn( &oField1 ); + SetDescription( poFeatureDefn->GetName() ); +} + +/************************************************************************/ +/* ~OGRARCGENLayer() */ +/************************************************************************/ + +OGRARCGENLayer::~OGRARCGENLayer() + +{ + poFeatureDefn->Release(); + + VSIFCloseL( fp ); +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRARCGENLayer::ResetReading() + +{ + nNextFID = 0; + bEOF = FALSE; + VSIFSeekL( fp, 0, SEEK_SET ); +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRARCGENLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRARCGENLayer::GetNextRawFeature() +{ + if (bEOF) + return NULL; + + const char* pszLine; + OGRwkbGeometryType eType = poFeatureDefn->GetGeomType(); + + if (wkbFlatten(eType) == wkbPoint) + { + while(TRUE) + { + pszLine = CPLReadLine2L(fp,256,NULL); + if (pszLine == NULL || EQUAL(pszLine, "END")) + { + bEOF = TRUE; + return NULL; + } + char** papszTokens = CSLTokenizeString2( pszLine, " ,", 0 ); + int nTokens = CSLCount(papszTokens); + if (nTokens == 3 || nTokens == 4) + { + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFID(nNextFID ++); + poFeature->SetField(0, papszTokens[0]); + if (nTokens == 3) + poFeature->SetGeometryDirectly( + new OGRPoint(CPLAtof(papszTokens[1]), + CPLAtof(papszTokens[2]))); + else + poFeature->SetGeometryDirectly( + new OGRPoint(CPLAtof(papszTokens[1]), + CPLAtof(papszTokens[2]), + CPLAtof(papszTokens[3]))); + CSLDestroy(papszTokens); + return poFeature; + } + else + CSLDestroy(papszTokens); + } + } + + CPLString osID; + OGRLinearRing* poLR = + (wkbFlatten(eType) == wkbPolygon) ? new OGRLinearRing() : NULL; + OGRLineString* poLS = + (wkbFlatten(eType) == wkbLineString) ? new OGRLineString() : poLR; + while(TRUE) + { + pszLine = CPLReadLine2L(fp,256,NULL); + if (pszLine == NULL) + break; + + if (EQUAL(pszLine, "END")) + { + if (osID.size() == 0) + break; + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFID(nNextFID ++); + poFeature->SetField(0, osID.c_str()); + if (wkbFlatten(eType) == wkbPolygon) + { + OGRPolygon* poPoly = new OGRPolygon(); + poPoly->addRingDirectly(poLR); + poFeature->SetGeometryDirectly(poPoly); + } + else + poFeature->SetGeometryDirectly(poLS); + return poFeature; + } + + char** papszTokens = CSLTokenizeString2( pszLine, " ,", 0 ); + int nTokens = CSLCount(papszTokens); + if (osID.size() == 0) + { + if (nTokens >= 1) + osID = papszTokens[0]; + else + { + CSLDestroy(papszTokens); + break; + } + } + else + { + if (nTokens == 2) + { + poLS->addPoint(CPLAtof(papszTokens[0]), + CPLAtof(papszTokens[1])); + } + else if (nTokens == 3) + { + poLS->addPoint(CPLAtof(papszTokens[0]), + CPLAtof(papszTokens[1]), + CPLAtof(papszTokens[2])); + } + else + { + CSLDestroy(papszTokens); + break; + } + } + CSLDestroy(papszTokens); + } + + bEOF = TRUE; + delete poLS; + return NULL; +} +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRARCGENLayer::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/GNUmakefile new file mode 100644 index 000000000..83f963370 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/GNUmakefile @@ -0,0 +1,16 @@ + + +include ../../../GDALmake.opt + +OBJ = aodriver.o aoatasource.o aolayer.o + +CPPFLAGS := $(AO_INC) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_ao.h + +install-obj: $(O_OBJ:.o=.$(OBJ_EXT)) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aodatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aodatasource.cpp new file mode 100644 index 000000000..7e2c58de7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aodatasource.cpp @@ -0,0 +1,220 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements ArcObjects OGR Datasource. + * Author: Ragi Yaser Burhum, ragi@burhum.com + * + ****************************************************************************** + * Copyright (c) 2009, Ragi Yaser Burhum + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ao.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "gdal.h" +#include "aoutils.h" + + +/************************************************************************/ +/* AODataSource() */ +/************************************************************************/ + +AODataSource::AODataSource(): +OGRDataSource(), +m_pszName(0) +{ +} + +/************************************************************************/ +/* ~AODataSource() */ +/************************************************************************/ + +AODataSource::~AODataSource() +{ + CPLFree( m_pszName ); + + size_t count = m_layers.size(); + for(size_t i = 0; i < count; ++i ) + delete m_layers[i]; +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int AODataSource::Open(IWorkspace* pWorkspace, const char * pszNewName, int bUpdate ) +{ + CPLAssert( m_nLayers == 0 ); + + if (bUpdate) + { + // Start Editing? + } + + m_pszName = CPLStrdup( pszNewName ); + + m_ipWorkspace = pWorkspace; + + HRESULT hr; + + // Anything will be fetched + IEnumDatasetPtr ipEnumDataset; + + if (FAILED(hr = m_ipWorkspace->get_Datasets(esriDTAny, &ipEnumDataset))) + { + return AOErr(hr, "Failed Opening Workspace Layers"); + } + + + return LoadLayers(ipEnumDataset); +} + +/************************************************************************/ +/* LoadLayers() */ +/************************************************************************/ + +bool AODataSource::LoadLayers(IEnumDataset* pEnumDataset) +{ + HRESULT hr; + + pEnumDataset->Reset(); + + IDatasetPtr ipDataset; + + bool errEncountered = false; + + while ((S_OK == pEnumDataset->Next(&ipDataset)) && !(ipDataset == NULL)) + { + IFeatureDatasetPtr ipFD = ipDataset; + if (!(ipFD == NULL)) + { + //We are dealing with a FeatureDataset, need to get + IEnumDatasetPtr ipEnumDatasetSubset; + if (FAILED(hr = ipFD->get_Subsets(&ipEnumDatasetSubset))) + { + AOErr(hr, "Failed getting dataset subsets"); + errEncountered = true; + continue; //skipping + } + + if (LoadLayers(ipEnumDatasetSubset) == false) + errEncountered = true; + + continue; + } + + IFeatureClassPtr ipFC = ipDataset; + + if (ipFC == NULL) + continue; //skip + + AOLayer* pLayer = new AOLayer; + + ITablePtr ipTable = ipFC; + + if (!pLayer->Initialize(ipTable)) + { + errEncountered = true; + continue; + } + + m_layers.push_back(pLayer); + + } + + if (errEncountered && m_layers.size() == 0) + return false; //all of the ones we tried had errors + else + return true; //at least one worked +} + + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +OGRErr AODataSource::DeleteLayer( int iLayer ) +{ + if( iLayer < 0 || iLayer >= static_cast(m_layers.size()) ) + return OGRERR_FAILURE; + + // Fetch ArObject Table before deleting OGR layer object + + ITablePtr ipTable; + m_layers[iLayer]->GetTable(&ipTable); + + std::string name = m_layers[iLayer]->GetLayerDefn()->GetName(); + + // delete OGR layer + delete m_layers[iLayer]; + + m_layers.erase(m_layers.begin() + iLayer); + + IDatasetPtr ipDataset = ipTable; + + HRESULT hr; + + if (FAILED(hr = ipDataset->Delete())) + { + CPLError( CE_Warning, CPLE_AppDefined, "%s was not deleted however it has been closed", name.c_str()); + AOErr(hr, "Failed deleting dataset"); + + return OGRERR_FAILURE; + } + else + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int AODataSource::TestCapability( const char * pszCap ) +{ + /* + if( EQUAL(pszCap,ODsCCreateLayer) && bDSUpdate ) + return TRUE; + else if( EQUAL(pszCap,ODsCDeleteLayer) && bDSUpdate ) + return TRUE; + else + return FALSE; + */ + + if(EQUAL(pszCap,ODsCDeleteLayer)) + return TRUE; + else + return FALSE; +} +// +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *AODataSource::GetLayer( int iLayer ) +{ + int count = static_cast(m_layers.size()); + + if( iLayer < 0 || iLayer >= count ) + return NULL; + else + return m_layers[iLayer]; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aodriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aodriver.cpp new file mode 100644 index 000000000..cd326ce47 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aodriver.cpp @@ -0,0 +1,228 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements ArcObjects OGR driver. + * Author: Ragi Yaser Burhum, ragi@burhum.com + * + ****************************************************************************** + * Copyright (c) 2009, Ragi Yaser Burhum + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ao.h" +#include "cpl_conv.h" +#include "aoutils.h" + + +/************************************************************************/ +/* AODriver() */ +/************************************************************************/ +AODriver::AODriver(): +OGRSFDriver(), m_licensedCheckedOut(false), m_productCode(-1), m_initialized(false) +{ +} + +/************************************************************************/ +/* ~AODriver() */ +/************************************************************************/ +AODriver::~AODriver() + +{ + if (m_initialized) + { + if (m_licensedCheckedOut) + ShutdownDriver(); + + ::CoUninitialize(); + } +} + +bool AODriver::Init() +{ + if (m_initialized) + return true; + + ::CoInitialize(NULL); + m_initialized = true; //need to mark to un-init COM system on destruction + + + m_licensedCheckedOut = InitializeDriver(); + if (!m_licensedCheckedOut) + { + CPLError( CE_Failure, CPLE_AppDefined, "ArcGIS License checkout failed."); + + return false; + } + else + { + m_productCode = GetInitedProductCode(); + } + + return true; +} + + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *AODriver::GetName() + +{ + return "ArcObjects"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *AODriver::Open( const char* pszFilename, + int bUpdate ) + +{ + // First check if we have to do any work. + // In order to avoid all the COM overhead, we are going to check + // if we have an AO prefix + + if( !EQUALN(pszFilename,"AO:",3) ) + return NULL; + + //OK, it is our turn, let's pay the price + + if (!Init()) + { + return NULL; + } + + const char* pInitString = pszFilename + 3; //skip chars + + + IWorkspacePtr ipWorkspace = NULL; + OpenWorkspace(pInitString, &ipWorkspace); + + if (ipWorkspace == NULL) + return NULL; + + AODataSource* pDS; + + pDS = new AODataSource(); + + if(!pDS->Open( ipWorkspace, pszFilename, bUpdate ) ) + { + delete pDS; + return NULL; + } + else + return pDS; +} + +/************************************************************************ +* CreateDataSource() * +************************************************************************/ + +OGRDataSource* AODriver::CreateDataSource( const char * pszName, + char **papszOptions) +{ + return NULL; +} + + +void AODriver::OpenWorkspace(std::string conn, IWorkspace** ppWorkspace) +{ + //If there are any others we want to support in the future, we just need to add them here + // http://resources.esri.com/help/9.3/ArcGISDesktop/ArcObjects/esriGeoDatabase/IWorkspaceFactory.htm + // + //We can also add GUIDS based on licensing code if necessary + + const GUID* pFactories[]= + { + &CLSID_FileGDBWorkspaceFactory, + &CLSID_SdeWorkspaceFactory, + &CLSID_AccessWorkspaceFactory + }; + + CComBSTR connString(conn.c_str()); //not sure if GDAL is sending utf8 or native - check later + + HRESULT hr; + + // try to connect with every factory specified + // + + for (int i = 0; i < (sizeof(pFactories)/sizeof(pFactories[0])); ++i) + { + if (pFactories[i] == NULL) // in case we have conditional factories + continue; + + IWorkspaceFactoryPtr ipFactory(*pFactories[i]); + + VARIANT_BOOL isWorkspace = VARIANT_FALSE; + + if (FAILED(hr = ipFactory->IsWorkspace(connString, &isWorkspace)) || isWorkspace == VARIANT_FALSE) + continue; //try next factory + + IWorkspacePtr ipWorkspace = NULL; + + if (FAILED(hr = ipFactory->OpenFromFile(connString, 0, &ipWorkspace)) || ipWorkspace == NULL) + continue; + + *ppWorkspace = ipWorkspace.Detach(); + + return; + } + + + *ppWorkspace = NULL; +} + +/************************************************************************ +* TestCapability() * +************************************************************************/ + +int AODriver::TestCapability( const char * pszCap ) + +{ + + + /* + if (EQUAL(pszCap, ODsCCreateLayer) ) + return FALSE; + */ + if (EQUAL(pszCap, ODsCDeleteLayer) ) + return TRUE; + /* + if (EQUAL(pszCap, ODrCCreateDataSource) ) + return FALSE; + */ + + return FALSE; +} + +/************************************************************************ +* RegisterOGRao() * +************************************************************************/ + +void RegisterOGRao() + +{ + if (! GDAL_CHECK_VERSION("OGR AO")) + return; + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( new AODriver ); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aolayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aolayer.cpp new file mode 100644 index 000000000..d953e16cb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aolayer.cpp @@ -0,0 +1,608 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements ArcObjects OGR layer. + * Author: Ragi Yaser Burhum, ragi@burhum.com + * + ****************************************************************************** + * Copyright (c) 2009, Ragi Yaser Burhum + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ao.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "aoutils.h" +#include + +/************************************************************************/ +/* AOLayer() */ +/************************************************************************/ +AOLayer::AOLayer(): +OGRLayer(),m_pFeatureDefn(NULL),m_pSRS(NULL), m_ipQF(CLSID_QueryFilter), +m_pBuffer(NULL), m_bufferSize(0), m_suppressColumnMappingError(false),m_forceMulti(false) +{ +} + +/************************************************************************/ +/* ~AOLayer() */ +/************************************************************************/ + +AOLayer::~AOLayer() +{ + if (m_pFeatureDefn) + m_pFeatureDefn->Release(); + + if (m_pSRS) + m_pSRS->Release(); + + if (m_pBuffer) + delete [] m_pBuffer; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +bool AOLayer::Initialize(ITable* pTable) +{ + HRESULT hr; + + m_ipTable = pTable; + + CComBSTR temp; + + IDatasetPtr ipDataset = m_ipTable; + if (FAILED(hr = ipDataset->get_Name(&temp))) + return false; + + m_pFeatureDefn = new OGRFeatureDefn(CW2A(temp)); //Should I "new" an OGR smart pointer - sample says so, but it doesn't seem right + //as long as we use the same compiler & settings in both the ogr build and this + //driver, we should be OK + m_pFeatureDefn->Reference(); + + IFeatureClassPtr ipFC = m_ipTable; + + VARIANT_BOOL hasOID = VARIANT_FALSE; + ipFC->get_HasOID(&hasOID); + + if (hasOID == VARIANT_TRUE) + { + ipFC->get_OIDFieldName(&temp); + m_strOIDFieldName = CW2A(temp); + } + + if (FAILED(hr = ipFC->get_ShapeFieldName(&temp))) + return AOErr(hr, "No shape field found!"); + + m_strShapeFieldName = CW2A(temp); + + IFieldsPtr ipFields; + if (FAILED(hr = ipFC->get_Fields(&ipFields))) + return AOErr(hr, "Fields not found!"); + + long shapeIndex = -1; + if (FAILED(hr = ipFields->FindField(temp, &shapeIndex))) + return AOErr(hr, "Shape field not found!"); + + IFieldPtr ipShapeField; + if (FAILED(hr = ipFields->get_Field(shapeIndex, &ipShapeField))) + return false; + + // Use GeometryDef to set OGR shapetype and Spatial Reference information + // + + IGeometryDefPtr ipGeoDef; + if (FAILED(hr = ipShapeField->get_GeometryDef(&ipGeoDef))) + return false; + + OGRwkbGeometryType ogrGeoType; + if (!AOToOGRGeometry(ipGeoDef, &ogrGeoType)) + return false; + + m_pFeatureDefn->SetGeomType(ogrGeoType); + + if (wkbFlatten(ogrGeoType) == wkbMultiLineString || wkbFlatten(ogrGeoType) == wkbMultiPoint) + m_forceMulti = true; + + + // Mapping of Spatial Reference will be passive about errors + // (it is possible we won't be able to map some ESRI-specific projections) + + esriGeometry::ISpatialReferencePtr ipSR = NULL; + + if (FAILED(hr = ipGeoDef->get_SpatialReference(&ipSR))) + { + AOErr(hr, "Failed Fetching ESRI spatial reference"); + } + else + { + if (!AOToOGRSpatialReference(ipSR, &m_pSRS)) + { + //report error, but be passive about it + CPLError( CE_Warning, CPLE_AppDefined, "Failed Mapping ESRI Spatial Reference"); + } + } + + + // Map fields + // + return AOToOGRFields(ipFields, m_pFeatureDefn, m_OGRFieldToESRIField); +} + + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void AOLayer::ResetReading() +{ + HRESULT hr; + + if (FAILED(hr = m_ipTable->Search(m_ipQF, VARIANT_TRUE, &m_ipCursor))) + AOErr(hr, "Error Executing Query"); + +} + +/************************************************************************/ +/* GetTable() */ +/************************************************************************/ + +HRESULT AOLayer::GetTable(ITable** ppTable) +{ + return m_ipTable.QueryInterface(IID_ITable, ppTable); +} + + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void AOLayer::SetSpatialFilter( OGRGeometry* pOGRGeom ) +{ + if (pOGRGeom == NULL) + { + SwitchToAttributeOnlyFilter(); + return; + } + else + { + SwitchToSpatialFilter(); + } + + esriGeometry::IGeometryPtr ipGeometry = NULL; + + if ((!OGRGeometryToAOGeometry(pOGRGeom, &ipGeometry)) || ipGeometry == NULL) + { + // Crap! we failed and there is no return error mechanism. + // Report Error and dismiss ogr geometry to go back to at least a working + // state + CPLError( CE_Failure, CPLE_AppDefined, "Could not convert OGR spatial filter geometry to ArcObjecs one. Dismissing spatial filter!"); + + SwitchToAttributeOnlyFilter(); + return; + } + + // Set Spatial Reference on AO geometry + IGeoDatasetPtr ipGeoDataset = m_ipTable; + esriGeometry::ISpatialReferencePtr ipSR = NULL; + ipGeoDataset->get_SpatialReference(&ipSR); + ipGeometry->putref_SpatialReference(ipSR); + + ISpatialFilterPtr ipSF = m_ipQF; //QI should never fail because we called SwitchToSpatialFilter + + ipSF->putref_Geometry(ipGeometry); + + ResetReading(); +} + +/************************************************************************/ +/* SetSpatialFilterRect() */ +/************************************************************************/ + +void AOLayer::SetSpatialFilterRect (double dfMinX, double dfMinY, double dfMaxX, double dfMaxY) +{ + SwitchToSpatialFilter(); + + IGeoDatasetPtr ipGD = m_ipTable; + esriGeometry::IEnvelopePtr ipEnvelope(esriGeometry::CLSID_Envelope); + esriGeometry::ISpatialReferencePtr ipSR; + + ipGD->get_SpatialReference(&ipSR); + ipEnvelope->putref_SpatialReference(ipSR); + ipEnvelope->PutCoords(dfMinX, dfMinY, dfMaxX, dfMaxY); + + ISpatialFilterPtr ipSF(m_ipQF); + ipSF->putref_Geometry(ipEnvelope); + ipSF->put_SpatialRel(esriSpatialRelIntersects); + +} + + +/************************************************************************/ +/* SwitchToAttributeOnlyFilter() */ +/************************************************************************/ + +void AOLayer::SwitchToAttributeOnlyFilter() +{ + ISpatialFilterPtr ipSF = m_ipQF; + + if (ipSF == NULL) + return; //nothing to be done - it is an attribute filter + + //only need to preserve whereclause since it is the only thing we consume in this driver + CComBSTR strWhereClause; + m_ipQF->get_WhereClause(&strWhereClause); + + m_ipQF.CreateInstance(CLSID_QueryFilter); // will destroy old spatial filter + if (strWhereClause.Length() > 0) + { + m_ipQF->put_WhereClause(strWhereClause); + } +} + +/************************************************************************/ +/* SwitchToSpatialFilter() */ +/************************************************************************/ + +void AOLayer::SwitchToSpatialFilter() +{ + ISpatialFilterPtr ipSF = m_ipQF; + + if (!(ipSF == NULL)) + return; //nothing to be done it is a spatial filter already + + //only need to preserve whereclause since it is the only thing we consume in this driver + CComBSTR strWhereClause; + m_ipQF->get_WhereClause(&strWhereClause); + + m_ipQF.CreateInstance(CLSID_SpatialFilter); // will destroy old query filter + if (strWhereClause.Length() > 0) + { + m_ipQF->put_WhereClause(strWhereClause); + } + +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr AOLayer::SetAttributeFilter( const char* pszQuery ) +{ + + if( pszQuery == NULL ) + { + + CComBSTR whereClause(_T("")); + m_ipQF->put_WhereClause(whereClause); + + } + else + { + CComBSTR whereClause(pszQuery); + m_ipQF->put_WhereClause(whereClause); + } + + ResetReading(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGRFeatureFromAORow() */ +/************************************************************************/ + +bool AOLayer::OGRFeatureFromAORow(IRow* pRow, OGRFeature** ppFeature) +{ + HRESULT hr; + + OGRFeature* pOutFeature = new OGRFeature(m_pFeatureDefn); + + ///////////////////////////////////////////////////////// + // Translate OID + // + + long oid = -1; + if (FAILED(hr = pRow->get_OID(&oid))) + { + //this should never happen + delete pOutFeature; + return false; + } + pOutFeature->SetFID(oid); + + ///////////////////////////////////////////////////////// + // Translate Geometry + // + + IFeaturePtr ipFeature = pRow; + esriGeometry::IGeometryPtr ipGeometry = NULL; + + if (FAILED(hr = ipFeature->get_Shape(&ipGeometry)) || ipGeometry == NULL) + { + delete pOutFeature; + return AOErr(hr, "Failed retrieving shape from ArcObjects"); + } + + OGRGeometry* pOGRGeo = NULL; + + if ((!AOGeometryToOGRGeometry(m_forceMulti, ipGeometry, m_pSRS, m_pBuffer, m_bufferSize, &pOGRGeo)) || pOGRGeo == NULL) + { + delete pOutFeature; + return AOErr(hr, "Failed to translate ArcObjects Geometry to OGR Geometry"); + } + + pOutFeature->SetGeometryDirectly(pOGRGeo); + + + ////////////////////////////////////////////////////////// + // Map fields + // + + CComVariant val; + size_t mappedFieldCount = m_OGRFieldToESRIField.size(); + + bool foundBadColumn = false; + + for (size_t i = 0; i < mappedFieldCount; ++i) + { + long index = m_OGRFieldToESRIField[i]; + + if (FAILED(hr = pRow->get_Value(index, &val))) + { + // this should not happen + return AOErr(hr, "Failed retrieving row value"); + } + + if (val.vt == VT_NULL) + { + continue; //leave as unset + } + + // + // NOTE: This switch statement needs to be kept in sync with AOToOGRGeometry + // since we are only checking for types we mapped in that utility function + + switch (m_pFeatureDefn->GetFieldDefn(i)->GetType()) + { + + case OFTInteger: + { + val.ChangeType(VT_I4); + pOutFeature->SetField(i, val.intVal); + } + break; + + case OFTReal: + { + val.ChangeType(VT_R8); + pOutFeature->SetField(i, val.dblVal); + } + break; + case OFTString: + { + val.ChangeType(VT_BSTR); + pOutFeature->SetField(i, CW2A(val.bstrVal)); + } + break; + + /* TODO: Need to get test dataset to implement these leave it as NULL for now + case OFTBinary: + { + // Access as SafeArray with SafeArrayAccessData perhaps? + } + break; + case OFTDateTime: + { + // Examine test data to figure out how to extract that + } + break; + */ + default: + { + if (!m_suppressColumnMappingError) + { + foundBadColumn = true; + CPLError( CE_Warning, CPLE_AppDefined, "Row id: %d col:%d has unhandled col type (%d). Setting to NULL.", oid, i, m_pFeatureDefn->GetFieldDefn(i)->GetType()); + } + } + } + } + + if (foundBadColumn) + m_suppressColumnMappingError = true; + + + *ppFeature = pOutFeature; + + return true; +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature* AOLayer::GetNextFeature() +{ + while (1) //want to skip errors + { + if (m_ipCursor == NULL) + return NULL; + + HRESULT hr; + + IRowPtr ipRow; + + if (FAILED(hr = m_ipCursor->NextRow(&ipRow))) + { + AOErr(hr, "Failed fetching features"); + return NULL; + } + + if (hr == S_FALSE || ipRow == NULL) + { + // It's OK, we are done fetching + return NULL; + } + + OGRFeature* pOGRFeature = NULL; + + if (!OGRFeatureFromAORow(ipRow, &pOGRFeature)) + { + long oid = -1; + ipRow->get_OID(&oid); + + std::strstream msg; + msg << "Failed translating ArcObjects row [" << oid << "] to OGR Feature"; + + AOErr(hr, msg.str()); + + //return NULL; + continue; //skip feature + } + + return pOGRFeature; + } +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *AOLayer::GetFeature( GIntBig oid ) +{ + HRESULT hr; + + IRowPtr ipRow; + if (FAILED(hr = m_ipTable->GetRow(oid, &ipRow))) + { + AOErr(hr, "Failed fetching row"); + return NULL; + } + + OGRFeature* pOGRFeature = NULL; + + if (!OGRFeatureFromAORow(ipRow, &pOGRFeature)) + { + AOErr(hr, "Failed translating ArcObjects row to OGR Feature"); + return NULL; + } + + return pOGRFeature; +} + + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig AOLayer::GetFeatureCount( int bForce ) +{ + HRESULT hr; + + long rowCount = -1; + + if (FAILED(hr = m_ipTable->RowCount(m_ipQF, &rowCount))) + { + AOErr(hr, "Failed calculating row count"); + + return rowCount; + } + + return static_cast(rowCount); +} + + + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr AOLayer::GetExtent (OGREnvelope* psExtent, int bForce) +{ + + if (bForce) + { + return OGRLayer::GetExtent( psExtent, bForce ); + } + + HRESULT hr; + + IGeoDatasetPtr ipGeoDataset = m_ipTable; + + esriGeometry::IEnvelopePtr ipEnv = NULL; + if (FAILED(hr = ipGeoDataset->get_Extent(&ipEnv)) || ipEnv == NULL) + { + AOErr(hr, "Failed retrieving extent"); + + return OGRERR_FAILURE; + } + + double temp; + + ipEnv->get_XMin(&temp); + psExtent->MinX = temp; + + ipEnv->get_YMin(&temp); + psExtent->MinY = temp; + + ipEnv->get_XMax(&temp); + psExtent->MaxX = temp; + + ipEnv->get_YMax(&temp); + psExtent->MaxY = temp; + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int AOLayer::TestCapability( const char* pszCap ) +{ + if (EQUAL(pszCap,OLCRandomRead)) + return TRUE; + + else if (EQUAL(pszCap,OLCFastFeatureCount)) + return TRUE; + + else if (EQUAL(pszCap,OLCFastSpatialFilter)) + return TRUE; + + else if (EQUAL(pszCap,OLCFastGetExtent)) + return TRUE; + + // Have not implemented this yet + else if (EQUAL(pszCap,OLCCreateField)) + return FALSE; + + // Have not implemented this yet + else if (EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite)) + return FALSE; + + else + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aoutils.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aoutils.cpp new file mode 100644 index 000000000..41d3f863c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aoutils.cpp @@ -0,0 +1,463 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Different utility functions used in ArcObjects OGR driver. + * Author: Ragi Yaser Burhum, ragi@burhum.com + * + ****************************************************************************** + * Copyright (c) 2009, Ragi Yaser Burhum + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "aoutils.h" + + +bool AOErr(HRESULT hr, std::string desc) +{ + IErrorInfoPtr ipErrorInfo = NULL; + ::GetErrorInfo(NULL, &ipErrorInfo); + + if (ipErrorInfo) + { + CComBSTR comErrDesc; + ipErrorInfo->GetDescription(&comErrDesc); + + CW2A errMsg(comErrDesc); + + CPLError( CE_Failure, CPLE_AppDefined, "AO Error: %s HRESULT:%d COM_ERROR:%s", desc.c_str(), hr, errMsg ); + + ::SetErrorInfo(NULL, NULL); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, "AO Error: %s HRESULT:%d", desc.c_str(), hr); + } + + return false; +} + +bool AOToOGRGeometry(IGeometryDef* pGeoDef, OGRwkbGeometryType* pOut) +{ + esriGeometry::esriGeometryType geo; + VARIANT_BOOL hasZ; + + pGeoDef->get_GeometryType(&geo); + pGeoDef->get_HasZ(&hasZ); + + switch (geo) + { + case esriGeometry::esriGeometryPoint: *pOut = hasZ == VARIANT_TRUE? wkbPoint25D : wkbPoint; break; + case esriGeometry::esriGeometryMultipoint: *pOut = hasZ == VARIANT_TRUE? wkbMultiPoint25D : wkbMultiPoint; break; + case esriGeometry::esriGeometryLine: *pOut = hasZ == VARIANT_TRUE? wkbLineString25D : wkbLineString; break; + case esriGeometry::esriGeometryPolyline: *pOut = hasZ == VARIANT_TRUE? wkbMultiLineString25D : wkbMultiLineString; break; + case esriGeometry::esriGeometryPolygon: *pOut = hasZ == VARIANT_TRUE? wkbMultiPolygon25D : wkbMultiPolygon; break;// no mapping to single polygon + + default: + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot map esriGeometryType(%d) to OGRwkbGeometryType", geo); + return false; + } + } + + return true; +} + +bool AOToOGRFields(IFields* pFields, OGRFeatureDefn* pOGRFeatureDef, std::vector & ogrToESRIFieldMapping) +{ + HRESULT hr; + + long fieldCount; + if (FAILED(hr = pFields->get_FieldCount(&fieldCount))) + return false; + + ogrToESRIFieldMapping.clear(); + + for (long i = 0; i < fieldCount; ++i) + { + + IFieldPtr ipField; + if (FAILED(hr = pFields->get_Field(i, &ipField))) + return AOErr(hr, "Error getting field"); + + CComBSTR name; + if (FAILED(hr = ipField->get_Name(&name))) + return AOErr(hr, "Could not get field name"); + + esriFieldType fieldType; + if (FAILED(hr = ipField->get_Type(&fieldType))) + return AOErr(hr, "Error getting field type"); + + //skip these + if (fieldType == esriFieldTypeOID || fieldType == esriFieldTypeGeometry) + continue; + + OGRFieldType ogrType; + if (!AOToOGRFieldType(fieldType, &ogrType)) + { + // field cannot be mapped, skipping it + CPLError( CE_Warning, CPLE_AppDefined, "Skipping field %s", CW2A(name) ); + continue; + } + + OGRFieldDefn fieldTemplate( CW2A(name), ogrType); + pOGRFeatureDef->AddFieldDefn( &fieldTemplate ); + + ogrToESRIFieldMapping.push_back(i); + } + + CPLAssert(ogrToESRIFieldMapping.size() == pOGRFeatureDef->GetFieldCount()); + + return true; +} + +// We could make this function far more robust by doing automatic coertion of types, +// and/or skipping fields we do not know. But our purposes this works fine + +bool AOToOGRFieldType(esriFieldType aoType, OGRFieldType* pOut) +{ + /* + ESRI types + esriFieldTypeSmallInteger = 0, + esriFieldTypeInteger = 1, + esriFieldTypeSingle = 2, + esriFieldTypeDouble = 3, + esriFieldTypeString = 4, + esriFieldTypeDate = 5, + esriFieldTypeOID = 6, + esriFieldTypeGeometry = 7, + esriFieldTypeBlob = 8, + esriFieldTypeRaster = 9, + esriFieldTypeGUID = 10, + esriFieldTypeGlobalID = 11, + esriFieldTypeXML = 12 + */ + + //OGR Types + + // Desc Name AO->OGR Mapped By Us? + /** Simple 32bit integer */// OFTInteger = 0, YES + /** List of 32bit integers */// OFTIntegerList = 1, NO + /** Double Precision floating point */// OFTReal = 2, YES + /** List of doubles */// OFTRealList = 3, NO + /** String of ASCII chars */// OFTString = 4, YES + /** Array of strings */// OFTStringList = 5, NO + /** deprecated */// OFTWideString = 6, NO + /** deprecated */// OFTWideStringList = 7, NO + /** Raw Binary data */// OFTBinary = 8, YES + /** Date */// OFTDate = 9, NO + /** Time */// OFTTime = 10, NO + /** Date and Time */// OFTDateTime = 11 YES + + switch (aoType) + { + case esriFieldTypeSmallInteger: + case esriFieldTypeInteger: + { + *pOut = OFTInteger; + return true; + } + case esriFieldTypeSingle: + case esriFieldTypeDouble: + { + *pOut = OFTReal; + return true; + } + case esriFieldTypeGUID: + case esriFieldTypeGlobalID: + case esriFieldTypeXML: + case esriFieldTypeString: + { + *pOut = OFTString; + return true; + } + case esriFieldTypeDate: + { + *pOut = OFTDateTime; + return true; + } + case esriFieldTypeBlob: + { + *pOut = OFTBinary; + return true; + } + default: + { + /* Intentionally fail at these + esriFieldTypeOID + esriFieldTypeGeometry + esriFieldTypeRaster + */ + return false; + } + + } +} + + +bool AOGeometryToOGRGeometry(bool forceMulti, esriGeometry::IGeometry* pInAOGeo, OGRSpatialReference* pOGRSR, unsigned char* & pInOutWorkingBuffer, long & inOutBufferSize, OGRGeometry** ppOutGeometry) +{ + HRESULT hr; + + esriGeometry::IWkbPtr ipWkb = pInAOGeo; + + long reqSize = 0; + + if (FAILED(hr = ipWkb->get_WkbSize(&reqSize))) + { + AOErr(hr, "Error getting Wkb buffer size"); + return false; + } + + if (reqSize > inOutBufferSize) + { + // resize working buffer + delete [] pInOutWorkingBuffer; + pInOutWorkingBuffer = new unsigned char[reqSize]; + inOutBufferSize = reqSize; + } + + if (FAILED(hr = ipWkb->ExportToWkb(&reqSize, pInOutWorkingBuffer))) + { + AOErr(hr, "Error exporting to WKB buffer"); + return false; + } + + OGRGeometry* pOGRGeometry = NULL; + OGRErr eErr = OGRGeometryFactory::createFromWkb(pInOutWorkingBuffer, pOGRSR, &pOGRGeometry, reqSize); + if (eErr != OGRERR_NONE) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed attempting to import ArcGIS WKB Geometry. OGRGeometryFactory err:%d", eErr); + return false; + } + + // force geometries to multi if requested + + + // If it is a polygon, force to MultiPolygon since we always produce multipolygons + if (wkbFlatten(pOGRGeometry->getGeometryType()) == wkbPolygon) + { + pOGRGeometry = OGRGeometryFactory::forceToMultiPolygon(pOGRGeometry); + } + else if (forceMulti) + { + if (wkbFlatten(pOGRGeometry->getGeometryType()) == wkbLineString) + { + pOGRGeometry = OGRGeometryFactory::forceToMultiLineString(pOGRGeometry); + } + else if (wkbFlatten(pOGRGeometry->getGeometryType()) == wkbPoint) + { + pOGRGeometry = OGRGeometryFactory::forceToMultiPoint(pOGRGeometry); + } + } + + + *ppOutGeometry = pOGRGeometry; + + return true; +} + +bool AOToOGRSpatialReference(esriGeometry::ISpatialReference* pSR, OGRSpatialReference** ppSR) +{ + HRESULT hr; + + if (pSR == NULL) + { + CPLError( CE_Warning, CPLE_AppDefined, "ESRI Spatial Reference is NULL"); + return false; + } + + esriGeometry::IESRISpatialReferenceGEN2Ptr ipSRGen = pSR; + + if (ipSRGen == NULL) + { + CPLError( CE_Warning, CPLE_AppDefined, "ESRI Spatial Reference is Unknown"); + return false; + } + + long bufferSize = 0; + if (FAILED(hr = ipSRGen->get_ESRISpatialReferenceSize(&bufferSize)) || bufferSize == 0) + return false; //should never happen + + BSTR buffer = ::SysAllocStringLen(NULL,bufferSize); + + if (FAILED(hr = ipSRGen->ExportToESRISpatialReference2(&buffer, &bufferSize))) + { + ::SysFreeString(buffer); + + return AOErr(hr, "Failed to export ESRI string"); + } + + CW2A strESRIWKT(buffer); + + ::SysFreeString(buffer); + + if (strlen(strESRIWKT) <= 0) + { + CPLError( CE_Warning, CPLE_AppDefined, "ESRI Spatial Reference is NULL"); + return false; + } + + *ppSR = new OGRSpatialReference(strESRIWKT); + + OGRErr result = (*ppSR)->morphFromESRI(); + + if (result == OGRERR_NONE) + { + return true; + } + else + { + delete *ppSR; + *ppSR = NULL; + + CPLError( CE_Failure, CPLE_AppDefined, "Failed morhping from ESRI Geometry: %s", strESRIWKT); + + return false; + } +} + +bool OGRGeometryToAOGeometry(OGRGeometry* pOGRGeom, esriGeometry::IGeometry** ppGeometry) +{ + HRESULT hr; + + *ppGeometry = NULL; + + GByte* pWKB = NULL; + + long wkbSize = pOGRGeom->WkbSize(); + pWKB = (GByte *) CPLMalloc(wkbSize); + + if( pOGRGeom->exportToWkb( wkbNDR, pWKB ) != OGRERR_NONE ) + { + CPLFree (pWKB); + CPLError( CE_Failure, CPLE_AppDefined, "Could not export OGR geometry to WKB"); + return false; + } + + long bytesRead; + esriGeometry::IGeometryFactoryPtr ipGeomFact(esriGeometry::CLSID_GeometryEnvironment); + hr = ipGeomFact->CreateGeometryFromWkb(&bytesRead, pWKB, ppGeometry); + + CPLFree (pWKB); + + if (FAILED(hr)) + { + return AOErr(hr, "Failed translating OGR geometry to ESRI Geometry"); + } + + return true; +} + +// Attempt to checkout a license from the top down +bool InitializeDriver(esriLicenseExtensionCode license) +{ + IAoInitializePtr ipInit(CLSID_AoInitialize); + + if (license == 0) + { + // Try to init as engine, then engineGeoDB, then ArcView, + // then ArcEditor, then ArcInfo + if (!InitAttemptWithoutExtension(esriLicenseProductCodeEngine)) + if (!InitAttemptWithoutExtension(esriLicenseProductCodeArcView)) + if (!InitAttemptWithoutExtension(esriLicenseProductCodeArcEditor)) + if (!InitAttemptWithoutExtension(esriLicenseProductCodeArcInfo)) + { + // No appropriate license is available + + CPLError( CE_Failure, CPLE_AppDefined, "ArcGIS License checkout failed."); + return false; + } + + return true; + } + + // Try to init as engine, then engineGeoDB, then ArcView, + // then ArcEditor, then ArcInfo + if (!InitAttemptWithExtension(esriLicenseProductCodeEngine,license)) + if (!InitAttemptWithExtension(esriLicenseProductCodeArcView, license)) + if (!InitAttemptWithExtension(esriLicenseProductCodeArcEditor, license)) + if (!InitAttemptWithExtension(esriLicenseProductCodeArcInfo, license)) + { + // No appropriate license is available + CPLError( CE_Failure, CPLE_AppDefined, "ArcGIS License checkout failed."); + return false; + } + + return true; +} + +// Attempt to initialize without an extension +bool InitAttemptWithoutExtension(esriLicenseProductCode product) +{ + IAoInitializePtr ipInit(CLSID_AoInitialize); + + esriLicenseStatus status = esriLicenseFailure; + ipInit->Initialize(product, &status); + return (status == esriLicenseCheckedOut); +} + +// Attempt to initialize with an extension +bool InitAttemptWithExtension(esriLicenseProductCode product, + esriLicenseExtensionCode extension) +{ + IAoInitializePtr ipInit(CLSID_AoInitialize); + + esriLicenseStatus licenseStatus = esriLicenseFailure; + ipInit->IsExtensionCodeAvailable(product, extension, &licenseStatus); + if (licenseStatus == esriLicenseAvailable) + { + ipInit->Initialize(product, &licenseStatus); + if (licenseStatus == esriLicenseCheckedOut) + ipInit->CheckOutExtension(extension, &licenseStatus); + } + return (licenseStatus == esriLicenseCheckedOut); +} + +// Shutdown the driver and check-in the license if needed. +HRESULT ShutdownDriver(esriLicenseExtensionCode license) +{ + HRESULT hr; + + // Scope ipInit so released before AoUninitialize call + { + IAoInitializePtr ipInit(CLSID_AoInitialize); + esriLicenseStatus status; + if (license != NULL) + { + hr = ipInit->CheckInExtension(license, &status); + if (FAILED(hr) || status != esriLicenseCheckedIn) + CPLError( CE_Failure, CPLE_AppDefined, "License checkin failed."); + } + hr = ipInit->Shutdown(); + } + + return hr; +} + +int GetInitedProductCode() +{ + HRESULT hr; + IAoInitializePtr ipAO(CLSID_AoInitialize); + esriLicenseProductCode code; + if (FAILED(hr = ipAO->InitializedProduct(&code))) + return -1; + + return static_cast(code); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aoutils.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aoutils.h new file mode 100644 index 000000000..ec830efe8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/aoutils.h @@ -0,0 +1,39 @@ + +#ifndef _AO_UTILS_H_INCLUDED +#define _AO_UTILS_H_INCLUDED + +#include "ogr_ao.h" +#include + +// ArcObjects to OGR Geometry Mapping +bool AOToOGRGeometry(IGeometryDef* pGeoDef, OGRwkbGeometryType* outOGRType); +bool AOGeometryToOGRGeometry(bool forceMulti, esriGeometry::IGeometry* pInAOGeo, OGRSpatialReference* pOGRSR, unsigned char* & pInWorkingBuffer, long & inOutBufferSize, OGRGeometry** ppOutGeometry); //working buffer is an optimization to avoid reallocating mem +bool AOToOGRSpatialReference(esriGeometry::ISpatialReference* pSR, OGRSpatialReference** ppSR); +bool OGRGeometryToAOGeometry(OGRGeometry* pOGRGeom, esriGeometry::IGeometry** ppGeometry); + + +// ArcObjects to OGR Field Mapping +bool AOToOGRFields(IFields* pFields, OGRFeatureDefn* pOGRFeatureDef, std::vector & ogrToESRIFieldMapping); +bool AOToOGRFieldType(esriFieldType aoType, OGRFieldType* ogrType); + + +// COM error to OGR +bool AOErr(HRESULT hr, std::string desc); + +// Init driver and check out license +bool InitializeDriver(esriLicenseExtensionCode license = + (esriLicenseExtensionCode)0); + +// Exit app and check in license +HRESULT ShutdownDriver(esriLicenseExtensionCode license = + (esriLicenseExtensionCode)0); + + +// Helper functions to initialize +bool InitAttemptWithoutExtension(esriLicenseProductCode product); +bool InitAttemptWithExtension(esriLicenseProductCode product, + esriLicenseExtensionCode extension); +int GetInitedProductCode(); + + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/drv_ao.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/drv_ao.html new file mode 100644 index 000000000..3d988d720 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/drv_ao.html @@ -0,0 +1,100 @@ + + +ESRI ArcObjects + + + + +

    ESRI ArcObjects

    + +

    Overview

    + +

    The OGR ArcObjects driver provides read-only access to ArcObjects based datasources. Since it uses the ESRI SDK, it has the requirement +of needing an ESRI license to run. Nevertheless, this also means that the driver has full knowledge of ESRI abstractions. Among these, you have:

    + +
      +
    • GeoDatabases: + +
        +
      • Personal GeoDatabase (.mdb)
      • +
      • File GeoDatabase (.gdb)
      • +
      • Enterprise GeoDatabase (.sde).
      • +
      +
    • + +
    • ESRI Shapefiles
    • +
    + +

    Although it has not been extended to do this yet (there hasn't been a need), it can potentially also support the following GeoDatabase Abstractions

    + +
      +
    • Annotation and Dimension feature classes
    • +
    • Relationship Classes
    • +
    • Networks (GN and ND)
    • +
    • Topologies
    • +
    • Terrains
    • +
    • Representations
    • +
    • Parcel Fabrics
    • +
    + +

    You can try those above and they may work - but they have not been tested. Note the abstractions above cannot be supported with the Open FileGeoDatabase API.

    + + +

    Requirements

    + +

    +

      +
    • An ArcView license or ArcEngine license (or higher) - Required to run.
    • +
    • The ESRI libraries installed. This typically happens if you have ArcEngine or ArcGIS Desktop or Server installed - Required to compile. Note that this code should also compile + using the ArcEngine *nix SDKs, however I do not have access to these and thus I have not tried it myself
    • +
    +

    + + +

    Usage

    + +

    Prefix the Datasource with "AO:"

    + +
      +
    • Read from FileGDB and load into PostGIS:
    • +
      + ogr2ogr -overwrite -skipfailures -f "PostgreSQL" PG:"host=myhost user=myuser dbname=mydb password=mypass" AO:"C:\somefolder\BigFileGDB.gdb" "MyFeatureClass" +
      +
      + +
    • Get detailed info of Personal GeoDatabase:
    • +
      + ogrinfo -al AO:"C:\somefolder\PersonalGDB.mdb" +
      +
      + +
    • Get detailed info of Enterprise GeoDatabase (.sde contains target version to connect to):
    • +
      + ogrinfo -al AO:"C:\somefolder\MySDEConnection.sde" +
      +
      + +
    + + +

    Building Notes

    + +

    Read the GDAL Windows Building example for Plugins. You will find a similar section in nmake.opt for ArcObjects. +After you are done, go to the $gdal_source_root\ogr\ogrsf_frmts\arcobjects folder and execute:

    + +

    + + nmake /f makefile.vc plugin + nmake /f makefile.vc plugin-install + +

    + + +

    Known Issues

    + +

    + Date and blob fields have not been implemented. It is probably just a few lines of code, I just have not had time (or need) to do it. +

    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/makefile.vc new file mode 100644 index 000000000..7e6cdcd92 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/makefile.vc @@ -0,0 +1,38 @@ +OBJ = aodatasource.obj aodriver.obj aolayer.obj aoutils.obj +EXTRAFLAGS = -I.. -I..\.. -I$(AO_INC) + +GDAL_ROOT = ..\..\.. + +PLUGIN_DLL = ogr_ao.dll + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + $(INSTALL) *.obj ..\o + +all: default + +clean: + -del *.obj + -del *.dll + -del *.exp + -del *.lib + -del *.manifest + -del *.pdb + -del *.tlh + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(GDAL_ROOT)/gdal_i.lib $(SDE_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) + +#ogr_ao.dll: $(OBJ) +# link /dll $(LDEBUG) /out:ogr_ao.dll $(OBJ) \ +# $(GDALLIB) $(AO_LIB) +# copy ogr_ao.* c:\warmerda\bld\bin\gdalplugins\ \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/ogr_ao.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/ogr_ao.h new file mode 100644 index 000000000..d4849b067 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/arcobjects/ogr_ao.h @@ -0,0 +1,199 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Standard includes and class definitions ArcObjects OGR driver. + * Author: Ragi Yaser Burhum, ragi@burhum.com + * + ****************************************************************************** + * Copyright (c) 2009, Ragi Yaser Burhum + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_AO_H_INCLUDED +#define _OGR_AO_H_INCLUDED + +#include "ogrsf_frmts.h" + +#include +#include "cpl_string.h" + +//COM ATL Includes +#include +#include +#include +#include //CString + +using namespace ATL; + +// ArcGIS COM Includes +#import "C:\Program Files (x86)\ArcGIS\com\esriSystem.olb" raw_interfaces_only, raw_native_types, no_namespace, named_guids, exclude("OLE_COLOR", "OLE_HANDLE", "VARTYPE"), rename("min", "esrimin"), rename("max", "esrimax") +#import "C:\Program Files (x86)\ArcGIS\com\esriGeometry.olb" raw_interfaces_only, raw_native_types, named_guids, exclude("ISegment") +#import "C:\Program Files (x86)\ArcGIS\com\esriGeoDatabase.olb" raw_interfaces_only, raw_native_types, no_namespace, named_guids +#import "C:\Program Files (x86)\ArcGIS\com\esriDataSourcesGDB.olb" raw_interfaces_only, raw_native_types, no_namespace, named_guids + + + +/************************************************************************/ +/* AOLayer */ +/************************************************************************/ + +class AODataSource; + +class AOLayer : public OGRLayer +{ +public: + + AOLayer(); + virtual ~AOLayer(); + + bool Initialize(ITable* pTable); + + const char* GetFIDFieldName() const { return m_strOIDFieldName.c_str(); } + const char* GetShapeFieldName() const { return m_strShapeFieldName.c_str(); } + + virtual void ResetReading(); + virtual OGRFeature* GetNextFeature(); + virtual OGRFeature* GetFeature( GIntBig nFeatureId ); + + HRESULT GetTable(ITable** ppTable); + + + virtual OGRErr GetExtent( OGREnvelope *psExtent, int bForce ); + virtual GIntBig GetFeatureCount( int bForce ); + virtual OGRErr SetAttributeFilter( const char *pszQuery ); + virtual void SetSpatialFilterRect (double dfMinX, double dfMinY, double dfMaxX, double dfMaxY); + virtual void SetSpatialFilter( OGRGeometry * ); + +/* + virtual OGRErr CreateField( OGRFieldDefn *poFieldIn, + int bApproxOK ); + + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); +*/ + OGRFeatureDefn * GetLayerDefn() { return m_pFeatureDefn; } + + virtual OGRSpatialReference *GetSpatialRef() { return m_pSRS; } + + virtual int TestCapability( const char * ); + +protected: + bool OGRFeatureFromAORow(IRow* pRow, OGRFeature** ppFeature); + void SwitchToAttributeOnlyFilter(); + void SwitchToSpatialFilter(); + + ITablePtr m_ipTable; + OGRFeatureDefn* m_pFeatureDefn; + OGRSpatialReference* m_pSRS; + + std::string m_strOIDFieldName; + std::string m_strShapeFieldName; + + ICursorPtr m_ipCursor; + IQueryFilterPtr m_ipQF; + + std::vector m_OGRFieldToESRIField; //OGR Field Index to ESRI Field Index Mapping + + //buffers are used for avoiding constant reallocation of temp memory + unsigned char* m_pBuffer; + long m_bufferSize; //in bytes + bool m_suppressColumnMappingError; + bool m_forceMulti; +}; + +/************************************************************************/ +/* AODataSource */ +/************************************************************************/ +class AODataSource : public OGRDataSource +{ + +public: + AODataSource(); + virtual ~AODataSource(); + + + int Open(IWorkspace* pWorkspace, const char *, int ); + + const char* GetName() { return m_pszName; } + int GetLayerCount() { return static_cast(m_layers.size()); } + + OGRLayer* GetLayer( int ); + + + /* + virtual OGRLayer* ICreateLayer( const char *, + OGRSpatialReference* = NULL, + OGRwkbGeometryType = wkbUnknown, + char** = NULL ); + + */ + virtual OGRErr DeleteLayer( int ); + + int TestCapability( const char * ); + + /* +protected: + + void EnumerateSpatialTables(); + void OpenSpatialTable( const char* pszTableName ); +*/ +protected: + bool LoadLayers(IEnumDataset* pEnumDataset); + + char* m_pszName; + std::vector m_layers; + IWorkspacePtr m_ipWorkspace; + +}; + +/************************************************************************/ +/* AODriver */ +/************************************************************************/ + +class AODriver : public OGRSFDriver +{ + +public: + AODriver(); + virtual ~AODriver(); + + bool Init(); + + const char *GetName(); + virtual OGRDataSource *Open( const char *, int ); + int TestCapability( const char * ); + virtual OGRDataSource *CreateDataSource( const char *pszName, char ** = NULL); + + void OpenWorkspace(std::string, IWorkspace** ppWorkspace); + +private: + bool m_licensedCheckedOut; + int m_productCode; + bool m_initialized; +}; + +CPL_C_START +void CPL_DLL RegisterOGRao(); +CPL_C_END + +#endif /* ndef _OGR_PG_H_INCLUDED */ + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/GNUmakefile new file mode 100644 index 000000000..e690f7879 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/GNUmakefile @@ -0,0 +1,23 @@ + + +include ../../../GDALmake.opt + +OGR_OBJ = ogravcbindriver.o ogravcbindatasource.o ogravcbinlayer.o \ + ogravclayer.o ogravcdatasource.o \ + ogravce00layer.o ogravce00datasource.o ogravce00driver.o + +AVC_OBJ = avc_bin.o avc_binwr.o avc_e00gen.o avc_e00parse.o \ + avc_e00write.o avc_e00read.o avc_mbyte.o avc_misc.o \ + avc_rawbin.o + +OBJ = $(AVC_OBJ) $(OGR_OBJ) + +CPPFLAGS := -I../shape -I.. -I../.. $(CPPFLAGS) + + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_avc.h avc.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/HISTORY.TXT b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/HISTORY.TXT new file mode 100644 index 000000000..0522dfc3b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/HISTORY.TXT @@ -0,0 +1,302 @@ +AVCE00 library - Revision History +================================= + + +Current Version: +---------------- + + +Version 2.0.1 (CVS): +-------------------- + +- Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char + (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495) + +- Fixed VC++ WIN32 build problems in GDAL/OGR environment + (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2500) + +- Detect compressed E00 input files and refuse to open them instead of + crashing (bug 1928, GDAL/OGR ticket 2513) + +- Avoid scanning the whole E00 input file in AVCE00ReadOpenE00() if the file + does not start with an EXP line (GDAL/OGR ticket 1989) + + +Version 2.0.0 (2006-08-17): +--------------------------- + +- New functions to read E00 files directly as opposed to translating to + binary coverage. Used in the implementation of E00 read support in OGR. + Contributed by James E. Flemer (bug 1497) + + Adds the following new functions: + + AVCE00ReadE00Ptr AVCE00ReadOpenE00(const char *pszE00FileName); + void AVCE00ReadCloseE00(AVCE00ReadE00Ptr psRead); + int AVCE00ReadRewindE00(AVCE00ReadE00Ptr psRead); + int AVCE00ReadSeekE00(AVCE00ReadE00Ptr psRead, + int nOffset, int nWhence); + void *AVCE00ReadNextObjectE00(AVCE00ReadE00Ptr psRead, + int bContinue); + +- Support for a new AVCCoverPC2 coverage type that is a hybrid between + AVCCoverPC and AVCCoverV7 (bug 1491) + +- Accept empty subclass names for TX6/TX7 sections (bug 1261) + +- Applied patch to remove any embedded '\0' from data line in + AVCE00GenTableRec() + +- Support for reading standalone info tables (just tables, no coverage + data) by pointing AVCE00ReadOpen() to the info directory (bug 1549). + +- #include to solve warning on 64 bit platforms (bug 1461) + + +Version 1.3.0 (2005-06-02): +--------------------------- + +- Applied Carl Anderson's patch to reduce the amount of stating while trying + to discover filename "case" on Unix in AVCAdjustCaseSensitiveFilename. + http://bugzilla.remotesensing.org/show_bug.cgi?id=314 + +- Fixed leak in AVCE00ReadOpen() when trying to open something that's + not a coverage (bug513) + +- Added AVCBinReadObject() to randomly read an object using the index file. + Currently only works for Arcs, info records and polygons. NFW + +- Fixed up AVCBinReadObject() to work properly for PC Arc/Info style + coverages. Also fixed to use upper/lower case 'X' for index based on + base file type. NFW OGR Bug#493. + +- avc_e00write.c: modified to use VSIMkdir(). + +- Use record size while reading ARC, PAL, CNT to skip junk bytes. (bug940) + +- Fixed parsing of type 40 fields (modified for bug#599) to detect exponent + format with negative exponents (bug#1272) + +- Minor fixes in avc_e00gen.c to avoid compile warnings. (NFW) + +- Fixed pointer aliasing ("type punning") problem re: gcc 3.3.x. (NFW) + http://bugzilla.remotesensing.org/show_bug.cgi?id=592 + + +Version 1.2.1 (2000-11-25): +--------------------------- + +- Remove trailing '/' in info directory path when creating the info dir + since mkdir() on some UNIXes doesn't like it. + +- Fixed a problem writing arc.dir on NT4 networked drives in an empty dir. + (bugzilla bug#353) + +- Fixed E00 parsing to properly handle PAL entries with 0 arcs which happen + to contain a single "0 0 0" entry. + +- Fixed E00 generation to correctly format 0-arc PAL records so that + they have a single "0 0 0" (filler) arc record (bug#597) + +- Fixed reading of type 40 fields from E00: when a type 40 field is not + stored in exponent format then a decimal format may be used but the + decimal position is shifted to the right in the E00 value. So we have + to shift the decimal point to the left (i.e. divide by 10) as we + interpret the value (bug#599). Really odd! + +- Added a hack to remap type 40 fields bigger than 8 digits to double + precision binary floats while generating E00 output (bug#599). + In E00 format, type 40 fields bigger than 8 digits would lose some + digits of precision and double precision floats can carry up to 18 + digits of precision. + This hack is enabled using "-DAVC_MAP_TYPE40_TO_DOUBLE" in OPTFLAGS. + +- Fixed problem with info/arc####.dat files coming from Windows that + contained '\\' in the data file path. Remap '\\' in path to '/' on Unix. + This problem seems to be new with Arc8 on Windows. + +- Fixed args to fseek() call in _AVCBinReadNextTableRec()... how come + we never ran into this before??? + + +Version 1.2.0 (2000-10-17): +--------------------------- + +- Added Japanese (multibyte characters) support. + +- Made validation on new coverage name more flexible (used to accept only + isalnum() chars and '_') + +- Added a case to treat pal.adf files with nPrecision==1011 as double prec. + Actually, any file with nPrecision > 1000 is now treated as double prec. + +- Switch to MIT-style license + +- Added PC Coverage write. + +- Added optional -DAVC_IGNORE_RMDIR_ERROR to prevent generation of an error + when AVCDeleteCoverage() fails to delete the actual coverage directory. + +- Accept TXT files in AVCCoverWeird that use both PC or V7 TXT structure + + +Version 1.1.1 (2000-04-01): +--------------------------- + +- Support double-precision "weird" coverages... previous version assumed that + "weird" coverages were always single precision. + +- Fix problem with TX6/TXT object with strings > 80 chars. String has to + be split in 80 chars chunks in E00. + +- Made lib more robust to detect corrupted or invalid files in a coverage + directory by checking signature and skipping invalid files. + + +Version 1.1 (2000-01-10): +------------------------- + +- PC Arc/Info Coverage support + +- Support for reading "Weird" coverages (a kind of hybrid between PC and + Unix V7 Coverages). + + +Version 1.0 (1999-12-04): +------------------------- + +- Improved parsing of integer values in E00... big numbers in INFO table + headers were sometimes merged and this resulted in unpredictable behavior. + +Version 0.6 (1999-08-26): +------------------------- + +- Added AVCE00DeleteCoverage() + +- Fixed problem with handling of attribs of type 40 in E00 input/output + +- Fixed VC++ warnings + +- Fixed some memory leaks. + +- Fixed a Windows-specific bug: the arc.dir was sometimes overwritten when + several coverages were created by the same process... likely a buffering + issue. + +Version 0.5 (1999-06-10): +------------------------- + +- Overwrite existing arc.dir entries when necessary while creating INFO + tables. + The problem was that when same coverage name was used twice with the + same info directory... we ended up with 2 sets of tables with the + same names. + This happened if a user deleted a coverage directory using "rm". + In this case, IMPORT71 reuses and overwrites the old table entry with + the same name in the ARC.DIR... our lib now does the same. + +- Created NMAKE makefile.vc for Windows + +- Tested on Windows + + +Version 0.4 (1999-05-17): +------------------------- + +- Added TXT/TX6/TX7 write support. + +- Added RXP write support + +- Fixed problem with double precision RPL sections: the second PAL + termination line confused the parser. + +- The write library now detects the E00's precision and generate + a coverage using that precision by default when AVCE00WriteOpen() + is called using AVC_DEFAULT_PREC. (That's also the only valid + option.) + +- Added a check for maximum coverage name length (13 chars) when creating + a new one. Also force name to contain only alnum() and '_'. + +- INFO TABLES: the name of the system attributes (COVER#/COVER-ID) are + now changed to the new coverage name when creating a new + info table. + +- Fixed problem parsing an E00 INFO table that contains 0 records + (yes, they exist!) + + +Version 0.3 (1999-05-10): +------------------------- + +- Added support to create coverages from E00 (alpha version). Most file + types are supported, but only single precision has been tested on Linux. + There are still a number of known problems with the write support. + +- Added proper support and tested with tables containing 16 bits integers + (type 50, size=2) + +- Fixed problem with 8 bytes floats in single-prec. tables, and 4 bytes + floats in double-precision tables. + +- Fixed the following problem: + In some cases, the "number of records" field for a table in the + arc.dir does not correspond to the real number of records + in the data file. In this kind of situation, the number of + records returned by Arc/Info in an E00 file will be based + on the real data file size, and not on the value from the arc.dir. + +- Tested on a CPL_MSB system + + +Version 0.2 (1999-02-24): +------------------------- + +- Added support for Annotations: + - TXT + - TX6 + - TX7 (Handled as TX6) + +- Added support for coverages with regions (RXP/RPL sections) + +- Tested with routes coverage + +- Added support for "par.adf": Double precision coverages have their + tolerances stored in a file named "par.adf" which is different from + the "tol.adf" we find in single precision coverages... + +- PRJ section: remove '\r' at end of lines when coverage generated on + DOS systems is read on Unix. + +- Support centroids (CNT) with more than one label attached to them. + +- Do not skip INFO Tables with 0 records. Since Arc/Info exports them, + we should probably do it as well! + +- Changed AVCE00ReadOpen() so that the coverage name does not absolutely + have to be terminated by a "/". Now, you can either pass the name of + the coverage directory (with or without a '/' at the end), or the path + to one of the files in the coverage directory. + +- Added extra line after the end of PAL sections in double precision + coverages: + -1 0 0 0 0 0 0 + 0.00000000000000E+00 0.00000000000000E+00 + Even if I consider this second line to be a glitch, I guess I have to + mimic this behavior and add this extra line one as well! + +- Modified "avcconv" command-line program to accept an output_filename + as a its second argument. Until now its output was always sent to stdout. + +- Updated documentation, mainly the list of error codes. + + +Version 0.1 (1999-01-31): +------------------------- + +First version, supports the most common file types, still several +know problems. + + +--------- +$Id: HISTORY.TXT,v 1.32 2008/07/30 18:35:53 dmorissette Exp $ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc.h new file mode 100644 index 000000000..527d2fb63 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc.h @@ -0,0 +1,870 @@ +/********************************************************************** + * $Id: avc.h,v 1.25 2008/07/23 20:51:38 dmorissette Exp $ + * + * Name: avc.h + * Project: Arc/Info Vector coverage (AVC) BIN<->E00 conversion library + * Language: ANSI C + * Purpose: Header file containing all definitions for the library. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2001, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: avc.h,v $ + * Revision 1.25 2008/07/23 20:51:38 dmorissette + * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char + * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495) + * + * Revision 1.24 2006/08/17 20:09:45 dmorissette + * Update for 2.0.0 release + * + * Revision 1.23 2006/08/17 18:56:42 dmorissette + * Support for reading standalone info tables (just tables, no coverage + * data) by pointing AVCE00ReadOpen() to the info directory (bug 1549). + * + * Revision 1.22 2006/06/27 18:38:43 dmorissette + * Cleaned up E00 reading (bug 1497, patch from James F.) + * + * Revision 1.21 2006/06/16 11:48:11 daniel + * New functions to read E00 files directly as opposed to translating to + * binary coverage. Used in the implementation of E00 read support in OGR. + * Contributed by James E. Flemer. (bug 1497) + * + * Revision 1.20 2006/06/14 16:31:28 daniel + * Added support for AVCCoverPC2 type (bug 1491) + * + * Revision 1.19 2005/06/03 03:49:58 daniel + * Update email address, website url, and copyright dates + * + * Revision 1.18 2005/06/03 03:29:16 daniel + * Ready for 1.3.0 release + * + * Revision 1.17 2004/02/11 05:49:44 daniel + * Added support for deleted flag in arc.dir (bug 2332) + * + * Revision 1.16 2002/02/14 16:34:15 warmerda + * fixed prototype name for AVCBinReadNextPrj + * + * Revision 1.15 2002/02/13 20:35:24 warmerda + * added AVCBinReadObject + * + * Revision 1.14 2001/11/25 21:15:23 daniel + * Added hack (AVC_MAP_TYPE40_TO_DOUBLE) to map type 40 fields bigger than 8 + * digits to double precision as we generate E00 output (bug599) + * + * Revision 1.13 2001/02/20 15:24:11 daniel + * Updated AVC_VERSION="1.2.0 (2000-10-17)" + * + * Revision 1.12 2000/09/26 21:38:44 daniel + * Updated AVC_VERSION + * + * Revision 1.11 2000/09/26 20:21:04 daniel + * Added AVCCoverPC write + * + * Revision 1.10 2000/09/22 19:45:20 daniel + * Switch to MIT-style license + * + * Revision 1.9 2000/05/29 15:31:30 daniel + * Added Japanese DBCS support + * + * Revision 1.8 2000/01/10 02:56:01 daniel + * Added read support for "weird" coverages + * + * Revision 1.7 1999/12/24 07:18:34 daniel + * Added PC Arc/Info coverages support + * + * Revision 1.6 1999/08/23 18:15:56 daniel + * Added AVCE00DeleteCoverage() + * + * Revision 1.5 1999/06/08 22:07:28 daniel + * Added AVCReadWrite in AVCAccess type + * + * Revision 1.4 1999/05/17 16:16:41 daniel + * Added RXP + TXT/TX6/TX7 write support + * + * Revision 1.3 1999/05/11 02:15:04 daniel + * Added coverage write support + * + * Revision 1.2 1999/02/25 03:39:39 daniel + * Added TXT, TX6/TX7, RXP and RPL support + * + * Revision 1.1 1999/01/29 16:29:24 daniel + * Initial revision + * + **********************************************************************/ + +#ifndef _AVC_H_INCLUDED_ +#define _AVC_H_INCLUDED_ + +#include "cpl_conv.h" +#include "cpl_string.h" + +#include "dbfopen.h" +#include "avc_mbyte.h" + +CPL_C_START + +/*--------------------------------------------------------------------- + * Current version of the AVCE00 library... always useful! + *--------------------------------------------------------------------*/ +#define AVC_VERSION "2.0.0 (2006-08-17)" + + +/* Coverage precision + */ +#define AVC_DEFAULT_PREC 0 +#define AVC_SINGLE_PREC 1 +#define AVC_DOUBLE_PREC 2 + +/* AVC_FORMAT_DBF_FLOAT used as nPrecision value only for AVCPrintRealValue() + */ +#define AVC_FORMAT_DBF_FLOAT 42 + +/* Coverage file types + */ +typedef enum +{ + AVCFileUnknown = 0, + AVCFileARC, + AVCFilePAL, + AVCFileCNT, + AVCFileLAB, + AVCFilePRJ, + AVCFileTOL, + AVCFileLOG, + AVCFileTXT, /* TXT and TX6 share the same binary format */ + AVCFileTX6, + AVCFileRXP, + AVCFileRPL, /* RPL is a PAL for a region */ + AVCFileTABLE +}AVCFileType; + + +/* Read or Write access flag + */ +typedef enum +{ + AVCRead, + AVCWrite, + AVCReadWrite +} AVCAccess; + +/* Coverage type: PC Arc/Info or Unix Arc/Info v7 + */ +typedef enum +{ + AVCCoverTypeUnknown = 0, + AVCCoverV7, + AVCCoverPC, + AVCCoverPC2, /* Unknown version... hybrid between V7 and PC !!! */ + AVCCoverWeird, /* Unknown version... hybrid between V7 and PC !!! */ + AVCCoverV7Tables /* Standalone tables, only an info directory */ +} AVCCoverType; + +/* Enum for byte ordering + */ +typedef enum +{ + AVCBigEndian, /* CPL_MSB, Motorola ordering */ + AVCLittleEndian /* CPL_LSB, Intel ordering */ +} AVCByteOrder; + + +/* Macros to establish byte ordering for each coverage type + * The rule until now: all coverage types use big endian (Motorola ordering) + * except PC Arc/Info coverages variant 1 (AVCCoverPC). + */ +#define AVC_COVER_BYTE_ORDER(cover_type) \ + (((cover_type) == AVCCoverPC ) ? AVCLittleEndian : AVCBigEndian ) + +/*===================================================================== + Structures + =====================================================================*/ + +/*--------------------------------------------------------------------- + * Structures defining various Arc/Info objects types. + * These are shared by the Binary and the E00 functions. + *--------------------------------------------------------------------*/ + +typedef struct AVCVertex_t +{ + double x; /* Even for single precision, we always */ + double y; /* use doubles for the vertices in memory. */ +}AVCVertex; + +/*--------------------------------------------------------------------- + * AVCArc: Information about an ARC + *--------------------------------------------------------------------*/ +typedef struct AVCArc_t +{ + GInt32 nArcId; + GInt32 nUserId; + GInt32 nFNode; + GInt32 nTNode; + GInt32 nLPoly; + GInt32 nRPoly; + GInt32 numVertices; + AVCVertex *pasVertices; +}AVCArc; + +/*--------------------------------------------------------------------- + * AVCPal: A PAL (Polygon Arc List) references all the arcs that + * constitute a polygon. + *--------------------------------------------------------------------*/ +typedef struct AVCPalArc_t +{ + GInt32 nArcId; + GInt32 nFNode; + GInt32 nAdjPoly; +}AVCPalArc; + +typedef struct AVCPal_t +{ + GInt32 nPolyId; + AVCVertex sMin; + AVCVertex sMax; + GInt32 numArcs; + AVCPalArc *pasArcs; +}AVCPal; + +/*--------------------------------------------------------------------- + * AVCCnt: Information about a CNT (polygon centroid) + *--------------------------------------------------------------------*/ +typedef struct AVCCnt_t +{ + GInt32 nPolyId; + AVCVertex sCoord; + GInt32 numLabels; /* 0 or 1 */ + GInt32 *panLabelIds; +}AVCCnt; + + +/*--------------------------------------------------------------------- + * AVCLab: Information about a LAB (polygon Label) + *--------------------------------------------------------------------*/ +typedef struct AVCLab_t +{ + GInt32 nValue; + GInt32 nPolyId; + AVCVertex sCoord1; + AVCVertex sCoord2; + AVCVertex sCoord3; +}AVCLab; + +/*--------------------------------------------------------------------- + * AVCTol: Information about a TOL record (coverage tolerances) + *--------------------------------------------------------------------*/ +typedef struct AVCTol_t +{ + GInt32 nIndex; + GInt32 nFlag; + double dValue; +}AVCTol; + +/*--------------------------------------------------------------------- + * AVCTxt: Information about a TXT/TX6/TX7 record (annotations) + *--------------------------------------------------------------------*/ +typedef struct AVCTxt_t +{ + GInt32 nTxtId; + GInt32 nUserId; + GInt32 nLevel; + float f_1e2; /* Always (float)-1e+20, even for double precision! */ + GInt32 nSymbol; + GInt32 numVerticesLine; + GInt32 n28; /* Unknown value at byte 28 */ + GInt32 numChars; + GInt32 numVerticesArrow; + + GInt16 anJust1[20]; + GInt16 anJust2[20]; + + double dHeight; + double dV2; /* ??? */ + double dV3; /* ??? */ + + GByte *pszText; /* Needs to be unsigned char for DBCS */ + + AVCVertex *pasVertices; +}AVCTxt; + +/*--------------------------------------------------------------------- + * AVCRxp: Information about a RXP record (something related to regions...) + *--------------------------------------------------------------------*/ +typedef struct AVCRxp_t +{ + GInt32 n1; + GInt32 n2; +}AVCRxp; + + +/*--------------------------------------------------------------------- + * AVCTableDef: Definition of an INFO table's structure. + * This info is read from several files: + * info/arc.dir + * info/arc####.dat + * info/arc####.nit + * + * And the data for the table itself is stored in a binary + * file in the coverage directory. + *--------------------------------------------------------------------*/ + +typedef struct AVCFieldInfo_t +{ + char szName[17]; + GInt16 nSize; + GInt16 v2; + GInt16 nOffset; + GInt16 v4; + GInt16 v5; + GInt16 nFmtWidth; + GInt16 nFmtPrec; + GInt16 nType1; + GInt16 nType2; + GInt16 v10; + GInt16 v11; + GInt16 v12; + GInt16 v13; + char szAltName[17]; + GInt16 nIndex; /* >0 if valid, or -1 if field is deleted */ +}AVCFieldInfo; + +#define AVC_FT_DATE 10 +#define AVC_FT_CHAR 20 +#define AVC_FT_FIXINT 30 +#define AVC_FT_FIXNUM 40 +#define AVC_FT_BININT 50 +#define AVC_FT_BINFLOAT 60 + + +typedef struct AVCTableDef_t +{ + /* Stuff read from the arc.dir file + * (1 record, corresponding to this table, from the arc.dir file) + */ + char szTableName[33]; + char szInfoFile[9]; + GInt16 numFields; + GInt16 nRecSize; + GInt32 numRecords; + char szExternal[3]; /* "XX" or " " */ + GInt16 bDeletedFlag; /* 1 if deleted, 0 if table is active */ + + /* Data file path read from the arc####.dat file + */ + char szDataFile[81]; + + /* Field information read from the arc####.nit file + */ + AVCFieldInfo *pasFieldDef; + +}AVCTableDef; + +typedef struct AVCField_t +{ + GInt16 nInt16; + GInt32 nInt32; + float fFloat; + double dDouble; + GByte *pszStr; +}AVCField; + +/*--------------------------------------------------------------------- + * Stuff related to buffered reading of raw binary files + *--------------------------------------------------------------------*/ + +#define AVCRAWBIN_READBUFSIZE 1024 + +typedef struct AVCRawBinFile_t +{ + FILE *fp; + char *pszFname; + AVCAccess eAccess; + AVCByteOrder eByteOrder; + GByte abyBuf[AVCRAWBIN_READBUFSIZE]; + int nOffset; /* Location of current buffer in the file */ + int nCurSize; /* Nbr of bytes currently loaded */ + int nCurPos; /* Next byte to read from abyBuf[] */ + + int nFileDataSize; /* File Size as stated in the header */ + /* EOF=TRUE passed this point in file */ + /* Set to -1 if not specified. */ + + /* Handle on dataset's multibyte character encoding info. */ + AVCDBCSInfo *psDBCSInfo; + +}AVCRawBinFile; + + +/*--------------------------------------------------------------------- + * Stuff related to reading and writing binary coverage files + *--------------------------------------------------------------------*/ + +typedef struct AVCBinHeader_t +{ + GUInt32 nSignature; + GInt32 nPrecision; /* <0 for double prec., >0 for single prec. */ + GInt32 nRecordSize; /* nbr of 2 byte words, 0 for var. length */ + GInt32 nLength; /* Overall file length, in 2 byte words */ +}AVCBinHeader; + +typedef struct AVCBinFile_t +{ + AVCRawBinFile *psRawBinFile; + char *pszFilename; + AVCRawBinFile *psIndexFile; /* Index file, Write mode only */ + + DBFHandle hDBFFile; /* Used for AVCCoverPC/PC2 DBF TABLES only */ + int nCurDBFRecord; /* 0-based record index in DBF file */ + + AVCCoverType eCoverType; + AVCFileType eFileType; + int nPrecision; /* AVC_SINGLE/DOUBLE_PREC */ + + union + { + AVCTableDef *psTableDef; + }hdr; + + /* cur.* : temp. storage used to read one record (ARC, PAL, ... or + * Table record) from the file. + */ + union + { + AVCArc *psArc; + AVCPal *psPal; + AVCCnt *psCnt; + AVCLab *psLab; + AVCTol *psTol; + AVCTxt *psTxt; + AVCRxp *psRxp; + AVCField *pasFields; + char **papszPrj; + }cur; + +}AVCBinFile; + +/*--------------------------------------------------------------------- + * Stuff related to the generation of E00 + *--------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------- + * AVCE00GenInfo structure + * This structure is used by the E00 generator functions to store + * their buffer and their current state in case they need to be + * called more than once for a given object type (i.e. ARC, PAL and IFO). + *--------------------------------------------------------------------*/ + +typedef struct AVCE00GenInfo_t +{ + char *pszBuf; + int nBufSize; + + int nPrecision; /* AVC_SINGLE/DOUBLE_PREC */ + int iCurItem; + int numItems; +}AVCE00GenInfo; + +/*--------------------------------------------------------------------- + * Stuff related to the parsing of E00 + *--------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------- + * AVCE00ParseInfo structure + * This structure is used by the E00 parser functions to store + * their buffer and their current state while parsing an object. + *--------------------------------------------------------------------*/ + +typedef struct AVCE00ParseInfo_t +{ + AVCFileType eFileType; + int nPrecision; /* AVC_SINGLE/DOUBLE_PREC */ + int iCurItem; + int numItems; + int nStartLineNum; + int nCurLineNum; + + int nCurObjectId; + GBool bForceEndOfSection; /* For sections that don't have an */ + /* explicit end-of-section line. */ + AVCFileType eSuperSectionType;/* For sections containing several files*/ + char *pszSectionHdrLine; /* Used by supersection types */ + + union + { + AVCTableDef *psTableDef; + }hdr; + GBool bTableHdrComplete; /* FALSE until table header is */ + /* finished parsing */ + int nTableE00RecLength; + + /* cur.* : temp. storage used to store current object (ARC, PAL, ... or + * Table record) from the file. + */ + union + { + AVCArc *psArc; + AVCPal *psPal; + AVCCnt *psCnt; + AVCLab *psLab; + AVCTol *psTol; + AVCTxt *psTxt; + AVCRxp *psRxp; + AVCField *pasFields; + char **papszPrj; + }cur; + + char *pszBuf; /* Buffer used only for TABLEs */ + int nBufSize; +}AVCE00ParseInfo; + + +/*--------------------------------------------------------------------- + * Stuff related to the transparent binary -> E00 conversion + *--------------------------------------------------------------------*/ +typedef struct AVCE00Section_t +{ + AVCFileType eType; /* File Type */ + char *pszName; /* E00 section or Table Name */ + char *pszFilename; /* Binary/E00 file filename */ + int nLineNum; /* E00 line number */ + int nFeatureCount; +}AVCE00Section; + +typedef struct AVCE00ReadInfo_t +{ + char *pszCoverPath; + char *pszInfoPath; + char *pszCoverName; + AVCCoverType eCoverType; + + /* pasSections is built when the coverage is opened and describes + * the squeleton of the E00 file. + */ + AVCE00Section *pasSections; + int numSections; + + /* If bReadAllSections=TRUE then reading automatically continues to + * the next section when a section finishes. (This is the default) + * Otherwise, you can use AVCE00ReadGotoSection() to read one section + * at a time... this will set bReadAllSections=FALSE. + */ + GBool bReadAllSections; + + /* Info about the file (or E00 section) currently being processed + */ + int iCurSection; + AVCBinFile *hFile; + + int iCurStep; /* AVC_GEN_* values, see below */ + AVCE00GenInfo *hGenInfo; + + /* Info related to multibyte character encoding + */ + AVCDBCSInfo *psDBCSInfo; + +} *AVCE00ReadPtr; + +typedef struct AVCE00ReadInfoE00_t +{ + char *pszCoverPath; + char *pszCoverName; + + AVCE00ParseInfo *hParseInfo; + AVCFileType eCurFileType; + + /* pasSections is built when the coverage is opened and describes + * the sections in the E00 file. + */ + AVCE00Section *pasSections; + int numSections; + + /* If bReadAllSections=TRUE then reading automatically continues to + * the next section when a section finishes. (This is the default) + * Otherwise, you can use AVCE00ReadGotoSectionE00() to read one + * section at a time. + */ + GBool bReadAllSections; + + /* File handle of the E00 file currently being processed + */ + FILE *hFile; + +} *AVCE00ReadE00Ptr; + + +/* E00 generation steps... tells the AVCE00Read*() functions which + * parts of the given E00 file are currently being processed. + */ +#define AVC_GEN_NOTSTARTED 0 +#define AVC_GEN_DATA 1 +#define AVC_GEN_ENDSECTION 2 +#define AVC_GEN_TABLEHEADER 3 +#define AVC_GEN_TABLEDATA 4 + +/*--------------------------------------------------------------------- + * Stuff related to the transparent E00 -> binary conversion + *--------------------------------------------------------------------*/ +typedef struct AVCE00WriteInfo_t +{ + char *pszCoverPath; + char *pszInfoPath; + char *pszCoverName; + AVCCoverType eCoverType; + + /* Info about the file (or E00 section) currently being processed + */ + AVCFileType eCurFileType; + AVCBinFile *hFile; + + /* Requested precision for the new coverage... may differ from the + * precision of the E00 input lines. (AVC_SINGLE_PREC or AVC_DOUBLE_PREC) + */ + int nPrecision; + + AVCE00ParseInfo *hParseInfo; + + /* Info related to multibyte character encoding + */ + AVCDBCSInfo *psDBCSInfo; + +} *AVCE00WritePtr; + +/* Coverage generation steps... used to store the current state of the + * AVCE00WriteNextLine() function. + */ +#define AVC_WR_TOPLEVEL 0 +#define AVC_WR_READING_SECTION + +/*===================================================================== + Function prototypes (lower-level lib. functions) + =====================================================================*/ + +/*--------------------------------------------------------------------- + * Functions related to buffered reading of raw binary files (and writing) + *--------------------------------------------------------------------*/ +AVCRawBinFile *AVCRawBinOpen(const char *pszFname, const char *pszAccess, + AVCByteOrder eFileByteOrder, + AVCDBCSInfo *psDBCSInfo); +void AVCRawBinClose(AVCRawBinFile *psInfo); +void AVCRawBinFSeek(AVCRawBinFile *psInfo, int nOffset, int nFrom); +GBool AVCRawBinEOF(AVCRawBinFile *psInfo); +void AVCRawBinSetFileDataSize(AVCRawBinFile *psInfo, int nDataSize); + +void AVCRawBinReadBytes(AVCRawBinFile *psInfo, int nBytesToRead, + GByte *pBuf); +GInt16 AVCRawBinReadInt16(AVCRawBinFile *psInfo); +GInt32 AVCRawBinReadInt32(AVCRawBinFile *psInfo); +float AVCRawBinReadFloat(AVCRawBinFile *psInfo); +double AVCRawBinReadDouble(AVCRawBinFile *psInfo); +void AVCRawBinReadString(AVCRawBinFile *psFile, int nBytesToRead, + GByte *pBuf); + +void AVCRawBinWriteBytes(AVCRawBinFile *psFile, int nBytesToWrite, + const GByte *pBuf); +void AVCRawBinWriteInt16(AVCRawBinFile *psFile, GInt16 n16Value); +void AVCRawBinWriteInt32(AVCRawBinFile *psFile, GInt32 n32Value); +void AVCRawBinWriteFloat(AVCRawBinFile *psFile, float fValue); +void AVCRawBinWriteDouble(AVCRawBinFile *psFile, double dValue); +void AVCRawBinWriteZeros(AVCRawBinFile *psFile, int nBytesToWrite); +void AVCRawBinWritePaddedString(AVCRawBinFile *psFile, int nFieldSize, + const GByte *pszString); + +/*--------------------------------------------------------------------- + * Functions related to reading the binary coverage files + *--------------------------------------------------------------------*/ + +AVCBinFile *AVCBinReadOpen(const char *pszPath, const char *pszName, + AVCCoverType eCoverType, AVCFileType eType, + AVCDBCSInfo *psDBCSInfo); +void AVCBinReadClose(AVCBinFile *psFile); + +int AVCBinReadRewind(AVCBinFile *psFile); + +void *AVCBinReadObject(AVCBinFile *psFile, int iObjIndex ); +void *AVCBinReadNextObject(AVCBinFile *psFile); +AVCArc *AVCBinReadNextArc(AVCBinFile *psFile); +AVCPal *AVCBinReadNextPal(AVCBinFile *psFile); +AVCCnt *AVCBinReadNextCnt(AVCBinFile *psFile); +AVCLab *AVCBinReadNextLab(AVCBinFile *psFile); +AVCTol *AVCBinReadNextTol(AVCBinFile *psFile); +AVCTxt *AVCBinReadNextTxt(AVCBinFile *psFile); +AVCRxp *AVCBinReadNextRxp(AVCBinFile *psFile); +AVCField *AVCBinReadNextTableRec(AVCBinFile *psFile); +char **AVCBinReadNextPrj(AVCBinFile *psFile); + +char **AVCBinReadListTables(const char *pszInfoPath, + const char *pszCoverName, + char ***ppapszArcDatFiles, + AVCCoverType eCoverType, + AVCDBCSInfo *psDBCSInfo); + +/*--------------------------------------------------------------------- + * Functions related to writing the binary coverage files + *--------------------------------------------------------------------*/ +AVCBinFile *AVCBinWriteCreate(const char *pszPath, const char *pszName, + AVCCoverType eCoverType, + AVCFileType eType, int nPrecision, + AVCDBCSInfo *psDBCSInfo); +AVCBinFile *AVCBinWriteCreateTable(const char *pszInfoPath, + const char *pszCoverName, + AVCTableDef *psSrcTableDef, + AVCCoverType eCoverType, + int nPrecision, AVCDBCSInfo *psDBCSInfo); +void AVCBinWriteClose(AVCBinFile *psFile); + +int AVCBinWriteHeader(AVCBinFile *psFile); +int AVCBinWriteObject(AVCBinFile *psFile, void *psObj); +int AVCBinWriteArc(AVCBinFile *psFile, AVCArc *psArc); +int AVCBinWritePal(AVCBinFile *psFile, AVCPal *psPal); +int AVCBinWriteCnt(AVCBinFile *psFile, AVCCnt *psCnt); +int AVCBinWriteLab(AVCBinFile *psFile, AVCLab *psLab); +int AVCBinWriteTol(AVCBinFile *psFile, AVCTol *psTol); +int AVCBinWritePrj(AVCBinFile *psFile, char **papszPrj); +int AVCBinWriteTxt(AVCBinFile *psFile, AVCTxt *psTxt); +int AVCBinWriteRxp(AVCBinFile *psFile, AVCRxp *psRxp); +int AVCBinWriteTableRec(AVCBinFile *psFile, AVCField *pasFields); + +/*--------------------------------------------------------------------- + * Functions related to the generation of E00 + *--------------------------------------------------------------------*/ +AVCE00GenInfo *AVCE00GenInfoAlloc(int nCoverPrecision); +void AVCE00GenInfoFree(AVCE00GenInfo *psInfo); +void AVCE00GenReset(AVCE00GenInfo *psInfo); + +const char *AVCE00GenStartSection(AVCE00GenInfo *psInfo, AVCFileType eType, + const char *pszFilename); +const char *AVCE00GenEndSection(AVCE00GenInfo *psInfo, AVCFileType eType, + GBool bCont); + +const char *AVCE00GenObject(AVCE00GenInfo *psInfo, + AVCFileType eType, void *psObj, GBool bCont); +const char *AVCE00GenArc(AVCE00GenInfo *psInfo, AVCArc *psArc, GBool bCont); +const char *AVCE00GenPal(AVCE00GenInfo *psInfo, AVCPal *psPal, GBool bCont); +const char *AVCE00GenCnt(AVCE00GenInfo *psInfo, AVCCnt *psCnt, GBool bCont); +const char *AVCE00GenLab(AVCE00GenInfo *psInfo, AVCLab *psLab, GBool bCont); +const char *AVCE00GenTol(AVCE00GenInfo *psInfo, AVCTol *psTol, GBool bCont); +const char *AVCE00GenTxt(AVCE00GenInfo *psInfo, AVCTxt *psTxt, GBool bCont); +const char *AVCE00GenTx6(AVCE00GenInfo *psInfo, AVCTxt *psTxt, GBool bCont); +const char *AVCE00GenPrj(AVCE00GenInfo *psInfo, char **papszPrj, GBool bCont); +const char *AVCE00GenRxp(AVCE00GenInfo *psInfo, AVCRxp *psRxp, GBool bCont); + +const char *AVCE00GenTableHdr(AVCE00GenInfo *psInfo, AVCTableDef *psDef, + GBool bCont); +const char *AVCE00GenTableRec(AVCE00GenInfo *psInfo, int numFields, + AVCFieldInfo *pasDef, AVCField *pasFields, + GBool bCont); + +/*--------------------------------------------------------------------- + * Functions related to parsing E00 lines + *--------------------------------------------------------------------*/ +AVCE00ParseInfo *AVCE00ParseInfoAlloc(); +void AVCE00ParseInfoFree(AVCE00ParseInfo *psInfo); +void AVCE00ParseReset(AVCE00ParseInfo *psInfo); + +AVCFileType AVCE00ParseSectionHeader(AVCE00ParseInfo *psInfo, + const char *pszLine); +GBool AVCE00ParseSectionEnd(AVCE00ParseInfo *psInfo, const char *pszLine, + GBool bResetParseInfo); +AVCFileType AVCE00ParseSuperSectionHeader(AVCE00ParseInfo *psInfo, + const char *pszLine); +GBool AVCE00ParseSuperSectionEnd(AVCE00ParseInfo *psInfo, + const char *pszLine ); + +void *AVCE00ParseNextLine(AVCE00ParseInfo *psInfo, const char *pszLine); +AVCArc *AVCE00ParseNextArcLine(AVCE00ParseInfo *psInfo, const char *pszLine); +AVCPal *AVCE00ParseNextPalLine(AVCE00ParseInfo *psInfo, const char *pszLine); +AVCCnt *AVCE00ParseNextCntLine(AVCE00ParseInfo *psInfo, const char *pszLine); +AVCLab *AVCE00ParseNextLabLine(AVCE00ParseInfo *psInfo, const char *pszLine); +AVCTol *AVCE00ParseNextTolLine(AVCE00ParseInfo *psInfo, const char *pszLine); +AVCTxt *AVCE00ParseNextTxtLine(AVCE00ParseInfo *psInfo, const char *pszLine); +AVCTxt *AVCE00ParseNextTx6Line(AVCE00ParseInfo *psInfo, const char *pszLine); +char **AVCE00ParseNextPrjLine(AVCE00ParseInfo *psInfo, const char *pszLine); +AVCRxp *AVCE00ParseNextRxpLine(AVCE00ParseInfo *psInfo, const char *pszLine); +AVCTableDef *AVCE00ParseNextTableDefLine(AVCE00ParseInfo *psInfo, + const char *pszLine); +AVCField *AVCE00ParseNextTableRecLine(AVCE00ParseInfo *psInfo, + const char *pszLine); + + +/*--------------------------------------------------------------------- + * Misc. functions shared by several parts of the lib. + *--------------------------------------------------------------------*/ +int _AVCE00ComputeRecSize(int numFields, AVCFieldInfo *pasDef, + GBool bMapType40ToDouble); + +void _AVCDestroyTableFields(AVCTableDef *psTableDef, AVCField *pasFields); +void _AVCDestroyTableDef(AVCTableDef *psTableDef); +AVCTableDef *_AVCDupTableDef(AVCTableDef *psSrcDef); + +GBool AVCFileExists(const char *pszPath, const char *pszName); +char *AVCAdjustCaseSensitiveFilename(char *pszFname); +int AVCPrintRealValue(char *pszBuf, int nPrecision, AVCFileType eType, + double dValue); + +/*===================================================================== + Function prototypes (THE PUBLIC ONES) + =====================================================================*/ + +/*--------------------------------------------------------------------- + * Functions to read E00 + *--------------------------------------------------------------------*/ +AVCE00ReadE00Ptr AVCE00ReadOpenE00(const char *pszE00FileName); +void AVCE00ReadCloseE00(AVCE00ReadE00Ptr psRead); +int AVCE00ReadRewindE00(AVCE00ReadE00Ptr psRead); +void *AVCE00ReadNextObjectE00(AVCE00ReadE00Ptr psRead); + +AVCE00Section *AVCE00ReadSectionsListE00(AVCE00ReadE00Ptr psRead, int *numSect); +int AVCE00ReadGotoSectionE00(AVCE00ReadE00Ptr psRead, + AVCE00Section *psSect, + GBool bContinue); + +/*--------------------------------------------------------------------- + * Functions to make a binary coverage appear as E00 + *--------------------------------------------------------------------*/ + +AVCE00ReadPtr AVCE00ReadOpen(const char *pszCoverPath); +void AVCE00ReadClose(AVCE00ReadPtr psInfo); +const char *AVCE00ReadNextLine(AVCE00ReadPtr psInfo); +int AVCE00ReadRewind(AVCE00ReadPtr psInfo); + +AVCE00Section *AVCE00ReadSectionsList(AVCE00ReadPtr psInfo, int *numSect); +int AVCE00ReadGotoSection(AVCE00ReadPtr psInfo, + AVCE00Section *psSect, + GBool bContinue); + +/*--------------------------------------------------------------------- + * Functions to write E00 lines to a binary coverage + *--------------------------------------------------------------------*/ + +AVCE00WritePtr AVCE00WriteOpen(const char *pszCoverPath, + AVCCoverType eNewCoverType, int nPrecision); +void AVCE00WriteClose(AVCE00WritePtr psInfo); +int AVCE00WriteNextLine(AVCE00WritePtr psInfo, + const char *pszLine); +int AVCE00DeleteCoverage(const char *pszCoverPath); + +CPL_C_END + +#endif /* _AVC_H_INCLUDED_ */ + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_bin.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_bin.c new file mode 100644 index 000000000..2c93f294f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_bin.c @@ -0,0 +1,2598 @@ +/********************************************************************** + * $Id: avc_bin.c,v 1.30 2008/07/23 20:51:38 dmorissette Exp $ + * + * Name: avc_bin.c + * Project: Arc/Info vector coverage (AVC) BIN->E00 conversion library + * Language: ANSI C + * Purpose: Binary files access functions. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2005, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: avc_bin.c,v $ + * Revision 1.30 2008/07/23 20:51:38 dmorissette + * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char + * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495) + * + * Revision 1.29 2006/08/17 18:56:42 dmorissette + * Support for reading standalone info tables (just tables, no coverage + * data) by pointing AVCE00ReadOpen() to the info directory (bug 1549). + * + * Revision 1.28 2006/06/14 16:31:28 daniel + * Added support for AVCCoverPC2 type (bug 1491) + * + * Revision 1.27 2005/06/03 03:49:58 daniel + * Update email address, website url, and copyright dates + * + * Revision 1.26 2004/02/28 06:35:49 warmerda + * Fixed AVCBinReadObject() index support to use 'x' or 'X' for index + * depending on the case of the original name. + * Fixed so that PC Arc/Info coverages with the extra 256 byte header work + * properly when using indexes to read them. + * http://bugzilla.remotesensing.org/show_bug.cgi?id=493 + * + * Revision 1.25 2004/02/11 05:49:44 daniel + * Added support for deleted flag in arc.dir (bug 2332) + * + * Revision 1.24 2002/08/27 15:26:06 daniel + * Removed C++ style comments for IRIX compiler (GDAL bug 192) + * + * Revision 1.23 2002/04/16 20:04:24 daniel + * Use record size while reading ARC, PAL, CNT to skip junk bytes. (bug940) + * + * Revision 1.22 2002/03/18 19:03:37 daniel + * Fixed AVCBinReadObject() for PAL objects (bug 848) + * + * Revision 1.21 2002/02/14 22:54:13 warmerda + * added polygon and table support for random reading + * + * Revision 1.20 2002/02/13 20:35:24 warmerda + * added AVCBinReadObject + * + * Revision 1.19 2001/11/25 22:01:23 daniel + * Fixed order of args to AVCRawBinFSeek() in _AVCBinReadNextTableRec() + * + * Revision 1.18 2000/10/16 16:16:20 daniel + * Accept TXT files in AVCCoverWeird that use both PC or V7 TXT structure + * + * Revision 1.17 2000/09/26 20:21:04 daniel + * Added AVCCoverPC write + * + * Revision 1.16 2000/09/22 19:45:20 daniel + * Switch to MIT-style license + * + * Revision 1.15 2000/09/20 15:09:34 daniel + * Check for DAT/NIT fnames sometimes truncated to 8 chars in weird coverages + * + * Revision 1.14 2000/06/05 21:38:53 daniel + * Handle precision field > 1000 in cover file header as meaning double prec. + * + * Revision 1.13 2000/05/29 15:31:30 daniel + * Added Japanese DBCS support + * + * Revision 1.12 2000/02/14 17:22:36 daniel + * Check file signature (9993 or 9994) when reading header. + * + * Revision 1.11 2000/02/02 04:24:52 daniel + * Support double precision "weird" coverages + * + * Revision 1.10 2000/01/10 02:54:10 daniel + * Added read support for "weird" coverages + * + * Revision 1.9 2000/01/07 07:11:51 daniel + * Added support for reading PC Coverage TXT files + * + * Revision 1.8 1999/12/24 07:38:10 daniel + * Added missing DBFClose() + * + * Revision 1.7 1999/12/24 07:18:34 daniel + * Added PC Arc/Info coverages support + * + * Revision 1.6 1999/08/23 18:17:16 daniel + * Modified AVCBinReadListTables() to return INFO fnames for DeleteCoverage() + * + * Revision 1.5 1999/05/11 01:49:08 daniel + * Simple changes required by addition of coverage write support + * + * Revision 1.4 1999/03/03 18:42:53 daniel + * Fixed problem with INFO table headers (arc.dir) that sometimes contain an + * invalid number of records. + * + * Revision 1.3 1999/02/25 17:01:53 daniel + * Added support for 16 bit integers in INFO tables (type=50, size=2) + * + * Revision 1.2 1999/02/25 03:41:28 daniel + * Added TXT, TX6/TX7, RXP and RPL support + * + * Revision 1.1 1999/01/29 16:28:52 daniel + * Initial revision + * + **********************************************************************/ + +#include "avc.h" + +#include /* for isspace() */ + +/*===================================================================== + * Prototypes for some static functions + *====================================================================*/ + +static AVCBinFile *_AVCBinReadOpenTable(const char *pszInfoPath, + const char *pszTableName, + AVCCoverType eCoverType, + AVCDBCSInfo *psDBCSInfo); +static AVCBinFile *_AVCBinReadOpenDBFTable(const char *pszInfoPath, + const char *pszTableName); +static AVCBinFile *_AVCBinReadOpenPrj(const char *pszPath,const char *pszName); + +static int _AVCBinReadNextTableRec(AVCRawBinFile *psFile, int nFields, + AVCFieldInfo *pasDef, AVCField *pasFields, + int nRecordSize); +static int _AVCBinReadNextDBFTableRec(DBFHandle hDBFFile, int *piRecordIndex, + int nFields, AVCFieldInfo *pasDef, + AVCField *pasFields); + +/*===================================================================== + * Stuff related to reading the binary coverage files + *====================================================================*/ + +/********************************************************************** + * AVCBinReadOpen() + * + * Open a coverage file for reading, read the file header if applicable, + * and initialize a temp. storage structure to be ready to read objects + * from the file. + * + * pszPath is the coverage (or info directory) path, terminated by + * a '/' or a '\\' + * pszName is the name of the file to open relative to this directory. + * + * Note: For most file types except tables, passing pszPath="" and + * including the coverage path as part of pszName instead would work. + * + * Returns a valid AVCBinFile handle, or NULL if the file could + * not be opened. + * + * AVCBinClose() will eventually have to be called to release the + * resources used by the AVCBinFile structure. + **********************************************************************/ +AVCBinFile *AVCBinReadOpen(const char *pszPath, const char *pszName, + AVCCoverType eCoverType, AVCFileType eFileType, + AVCDBCSInfo *psDBCSInfo) +{ + AVCBinFile *psFile; + + /*----------------------------------------------------------------- + * The case of INFO tables is a bit more complicated... + * pass the control to a separate function. + *----------------------------------------------------------------*/ + if (eFileType == AVCFileTABLE) + { + if (eCoverType == AVCCoverPC || eCoverType == AVCCoverPC2) + return _AVCBinReadOpenDBFTable(pszPath, pszName); + else + return _AVCBinReadOpenTable(pszPath, pszName, + eCoverType, psDBCSInfo); + } + + /*----------------------------------------------------------------- + * PRJ files are text files... we won't use the AVCRawBin*() + * functions for them... + *----------------------------------------------------------------*/ + if (eFileType == AVCFilePRJ) + { + return _AVCBinReadOpenPrj(pszPath, pszName); + } + + /*----------------------------------------------------------------- + * All other file types share a very similar opening method. + *----------------------------------------------------------------*/ + psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile)); + + psFile->eFileType = eFileType; + psFile->eCoverType = eCoverType; + + psFile->pszFilename = (char*)CPLMalloc((strlen(pszPath)+strlen(pszName)+1)* + sizeof(char)); + sprintf(psFile->pszFilename, "%s%s", pszPath, pszName); + + AVCAdjustCaseSensitiveFilename(psFile->pszFilename); + + psFile->psRawBinFile = AVCRawBinOpen(psFile->pszFilename, "r", + AVC_COVER_BYTE_ORDER(eCoverType), + psDBCSInfo); + + if (psFile->psRawBinFile == NULL) + { + /* Failed to open file... just return NULL since an error message + * has already been issued by AVCRawBinOpen() + */ + CPLFree(psFile->pszFilename); + CPLFree(psFile); + return NULL; + } + + /*----------------------------------------------------------------- + * Read the header, and set the precision field if applicable + *----------------------------------------------------------------*/ + if (AVCBinReadRewind(psFile) != 0) + { + CPLFree(psFile->pszFilename); + CPLFree(psFile); + return NULL; + } + + /*----------------------------------------------------------------- + * Allocate a temp. structure to use to read objects from the file + * (Using Calloc() will automatically initialize the struct contents + * to NULL... this is very important for ARCs and PALs) + *----------------------------------------------------------------*/ + if (psFile->eFileType == AVCFileARC) + { + psFile->cur.psArc = (AVCArc*)CPLCalloc(1, sizeof(AVCArc)); + } + else if (psFile->eFileType == AVCFilePAL || + psFile->eFileType == AVCFileRPL ) + { + psFile->cur.psPal = (AVCPal*)CPLCalloc(1, sizeof(AVCPal)); + } + else if (psFile->eFileType == AVCFileCNT) + { + psFile->cur.psCnt = (AVCCnt*)CPLCalloc(1, sizeof(AVCCnt)); + } + else if (psFile->eFileType == AVCFileLAB) + { + psFile->cur.psLab = (AVCLab*)CPLCalloc(1, sizeof(AVCLab)); + } + else if (psFile->eFileType == AVCFileTOL) + { + psFile->cur.psTol = (AVCTol*)CPLCalloc(1, sizeof(AVCTol)); + } + else if (psFile->eFileType == AVCFileTXT || + psFile->eFileType == AVCFileTX6) + { + psFile->cur.psTxt = (AVCTxt*)CPLCalloc(1, sizeof(AVCTxt)); + } + else if (psFile->eFileType == AVCFileRXP) + { + psFile->cur.psRxp = (AVCRxp*)CPLCalloc(1, sizeof(AVCRxp)); + } + else + { + CPLError(CE_Failure, CPLE_IllegalArg, + "%s: Unsupported file type or corrupted file.", + psFile->pszFilename); + CPLFree(psFile->pszFilename); + CPLFree(psFile); + psFile = NULL; + } + + return psFile; +} + +/********************************************************************** + * AVCBinReadClose() + * + * Close a coverage file, and release all memory (object strcut., buffers, + * etc.) associated with this file. + **********************************************************************/ +void AVCBinReadClose(AVCBinFile *psFile) +{ + AVCRawBinClose(psFile->psRawBinFile); + psFile->psRawBinFile = NULL; + + CPLFree(psFile->pszFilename); + psFile->pszFilename = NULL; + + if (psFile->hDBFFile) + DBFClose(psFile->hDBFFile); + + if( psFile->psIndexFile != NULL ) + AVCRawBinClose( psFile->psIndexFile ); + + if (psFile->eFileType == AVCFileARC) + { + if (psFile->cur.psArc) + CPLFree(psFile->cur.psArc->pasVertices); + CPLFree(psFile->cur.psArc); + } + else if (psFile->eFileType == AVCFilePAL || + psFile->eFileType == AVCFileRPL ) + { + if (psFile->cur.psPal) + CPLFree(psFile->cur.psPal->pasArcs); + CPLFree(psFile->cur.psPal); + } + else if (psFile->eFileType == AVCFileCNT) + { + if (psFile->cur.psCnt) + CPLFree(psFile->cur.psCnt->panLabelIds); + CPLFree(psFile->cur.psCnt); + } + else if (psFile->eFileType == AVCFileLAB) + { + CPLFree(psFile->cur.psLab); + } + else if (psFile->eFileType == AVCFileTOL) + { + CPLFree(psFile->cur.psTol); + } + else if (psFile->eFileType == AVCFilePRJ) + { + CSLDestroy(psFile->cur.papszPrj); + } + else if (psFile->eFileType == AVCFileTXT || + psFile->eFileType == AVCFileTX6) + { + if (psFile->cur.psTxt) + { + CPLFree(psFile->cur.psTxt->pasVertices); + CPLFree(psFile->cur.psTxt->pszText); + } + CPLFree(psFile->cur.psTxt); + } + else if (psFile->eFileType == AVCFileRXP) + { + CPLFree(psFile->cur.psRxp); + } + else if (psFile->eFileType == AVCFileTABLE) + { + _AVCDestroyTableFields(psFile->hdr.psTableDef, psFile->cur.pasFields); + _AVCDestroyTableDef(psFile->hdr.psTableDef); + } + else + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Unsupported file type or invalid file handle!"); + } + + CPLFree(psFile); +} + +/********************************************************************** + * _AVCBinReadHeader() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadRewind() instead) + * + * Read the first 100 bytes header of the file and fill the AVCHeader + * structure. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadHeader(AVCRawBinFile *psFile, AVCBinHeader *psHeader, + AVCCoverType eCoverType) +{ + int nStatus = 0; + + /*----------------------------------------------------------------- + * For AVCCoverPC coverages (files without hte .adf extension), + * there is a first 256 bytes header that we just skip and that + * precedes the 100 bytes header block. + * + * In AVCCoverV7, we only have the 100 bytes header. + *----------------------------------------------------------------*/ + if (eCoverType == AVCCoverPC) + AVCRawBinFSeek(psFile, 256, SEEK_SET); + else + AVCRawBinFSeek(psFile, 0, SEEK_SET); + + psHeader->nSignature = AVCRawBinReadInt32(psFile); + + if (AVCRawBinEOF(psFile)) + nStatus = -1; + + psHeader->nPrecision = AVCRawBinReadInt32(psFile); + psHeader->nRecordSize= AVCRawBinReadInt32(psFile); + + /* Jump to 24th byte in header */ + AVCRawBinFSeek(psFile, 12, SEEK_CUR); + psHeader->nLength = AVCRawBinReadInt32(psFile); + + /*----------------------------------------------------------------- + * File length, in words (16 bits)... pass the info to the RawBinFile + * to prevent it from trying to read junk bytes at the end of files... + * this problem happens specially with PC Arc/Info files. + *----------------------------------------------------------------*/ + if (eCoverType == AVCCoverPC) + AVCRawBinSetFileDataSize(psFile, psHeader->nLength*2 + 256); + else + AVCRawBinSetFileDataSize(psFile, psHeader->nLength*2 ); + + /* Move the pointer at the end of the 100 bytes header + */ + AVCRawBinFSeek(psFile, 72, SEEK_CUR); + + return nStatus; +} + + +/********************************************************************** + * AVCBinReadRewind() + * + * Rewind the read pointer, and read/skip the header if necessary so + * that we are ready to read the data objects from the file after + * this call. + * + * Returns 0 on success, -1 on error, and -2 if file has an invalid + * signature and is possibly corrupted. + **********************************************************************/ +int AVCBinReadRewind(AVCBinFile *psFile) +{ + AVCBinHeader sHeader; + int nStatus=0; + + /*----------------------------------------------------------------- + * For AVCCoverPC coverages, there is a first 256 bytes header + * that we just skip and that precedes the 100 bytes header block. + * + * In AVCCoverV7, AVCCoverPC2 and AVCCoverWeird, we only find the + * 100 bytes header. + * + * Note: it is the call to _AVCBinReadHeader() that takes care + * of skipping the first 256 bytes header if necessary. + *----------------------------------------------------------------*/ + + AVCRawBinFSeek(psFile->psRawBinFile, 0, SEEK_SET); + + if ( psFile->eFileType == AVCFileARC || + psFile->eFileType == AVCFilePAL || + psFile->eFileType == AVCFileRPL || + psFile->eFileType == AVCFileCNT || + psFile->eFileType == AVCFileLAB || + psFile->eFileType == AVCFileTXT || + psFile->eFileType == AVCFileTX6 ) + { + nStatus = _AVCBinReadHeader(psFile->psRawBinFile, &sHeader, + psFile->eCoverType); + + /* Store the precision information inside the file handle. + * + * Of course, there had to be an exception... + * At least PAL and TXT files in PC Arc/Info coverages sometimes + * have a negative precision flag even if they contain single + * precision data... why is that???? A PC Arc bug? + * + * 2000-06-05: Found a double-precision PAL file with a signature + * of 1011 (should have been -11). So we'll assume + * that signature > 1000 also means double precision. + */ + if ((sHeader.nPrecision < 0 || sHeader.nPrecision > 1000) && + psFile->eCoverType != AVCCoverPC) + psFile->nPrecision = AVC_DOUBLE_PREC; + else + psFile->nPrecision = AVC_SINGLE_PREC; + + /* Validate the signature value... this will allow us to detect + * corrupted files or files that do not belong in the coverage. + */ + if (sHeader.nSignature != 9993 && sHeader.nSignature != 9994) + { + CPLError(CE_Warning, CPLE_AssertionFailed, + "%s appears to have an invalid file header.", + psFile->pszFilename); + return -2; + } + + /* In Weird coverages, TXT files can be stored in the PC or the V7 + * format. Look at the 'precision' field in the header to tell which + * type we have. + * Weird TXT in PC format: nPrecision = 16 + * Weird TXT in V7 format: nPrecision = +/-67 + * Use AVCFileTXT for PC type, and AVCFileTX6 for V7 type. + */ + if (psFile->eCoverType == AVCCoverWeird && + psFile->eFileType == AVCFileTXT && ABS(sHeader.nPrecision) == 67) + { + /* TXT file will be processed as V7 TXT/TX6/TX7 */ + psFile->eFileType = AVCFileTX6; + } + } + else if (psFile->eFileType == AVCFileTOL) + { + /*------------------------------------------------------------- + * For some reason, the tolerance files do not follow the + * general rules! + * Single precision "tol.adf" have no header + * Double precision "par.adf" have the usual 100 bytes header, + * but the 3rd field, which usually defines the precision has + * a positive value, even if the file is double precision! + * + * Also, we have a problem with PC Arc/Info TOL files since they + * do not contain the first 256 bytes header either... so we will + * just assume that double precision TOL files cannot exist in + * PC Arc/Info coverages... this should be OK. + *------------------------------------------------------------*/ + int nSignature = 0; + nSignature = AVCRawBinReadInt32(psFile->psRawBinFile); + + if (nSignature == 9993) + { + /* We have a double precision par.adf... read the 100 bytes + * header and set the precision information inside the file + * handle. + */ + nStatus = _AVCBinReadHeader(psFile->psRawBinFile, &sHeader, + psFile->eCoverType); + + psFile->nPrecision = AVC_DOUBLE_PREC; + } + else + { + /* It's a single precision tol.adf ... just set the + * precision field. + */ + AVCRawBinFSeek(psFile->psRawBinFile, 0, SEEK_SET); + psFile->nPrecision = AVC_SINGLE_PREC; + } + } + + return nStatus; +} + +/********************************************************************** + * AVCBinReadObject() + * + * Read the object with a particular index. For fixed length record + * files we seek directly to the object. For variable files we try to + * get the offset from the corresponding index file. + * + * NOTE: Currently only implemented for ARC, PAL and TABLE files. + * + * Returns the read object on success or NULL on error. + **********************************************************************/ +void *AVCBinReadObject(AVCBinFile *psFile, int iObjIndex ) +{ + int bIndexed = FALSE; + int nObjectOffset, nRecordSize=0, nRecordStart = 0, nLen; + char *pszExt = NULL; + + if( iObjIndex < 0 ) + return NULL; + + /*----------------------------------------------------------------- + * Determine some information from based on the coverage type. + *----------------------------------------------------------------*/ + nLen = strlen(psFile->pszFilename); + if( psFile->eFileType == AVCFileARC && + ((nLen>=3 && EQUALN((pszExt=psFile->pszFilename+nLen-3), "arc", 3)) || + (nLen>=7 && EQUALN((pszExt=psFile->pszFilename+nLen-7),"arc.adf",7)))) + { + bIndexed = TRUE; + } + else if( psFile->eFileType == AVCFilePAL && + ((nLen>=3 && EQUALN((pszExt=psFile->pszFilename+nLen-3), "pal", 3)) || + (nLen>=7 && EQUALN((pszExt=psFile->pszFilename+nLen-7),"pal.adf",7)))) + { + bIndexed = TRUE; + } + else if( psFile->eFileType == AVCFileTABLE ) + { + bIndexed = FALSE; + nRecordSize = psFile->hdr.psTableDef->nRecSize; + nRecordStart = 0; + } + else + return NULL; + + /*----------------------------------------------------------------- + * Ensure the index file is opened if an index file is required. + *----------------------------------------------------------------*/ + + if( bIndexed && psFile->psIndexFile == NULL ) + { + char chOrig; + + if( pszExt == NULL ) + return NULL; + + chOrig = pszExt[2]; + if( chOrig > 'A' && chOrig < 'Z' ) + pszExt[2] = 'X'; + else + pszExt[2] = 'x'; + + psFile->psIndexFile = + AVCRawBinOpen( psFile->pszFilename, "rb", + psFile->psRawBinFile->eByteOrder, + psFile->psRawBinFile->psDBCSInfo); + pszExt[2] = chOrig; + + if( psFile->psIndexFile == NULL ) + return NULL; + } + + /*----------------------------------------------------------------- + * Establish the offset to read the object from. + *----------------------------------------------------------------*/ + if( bIndexed ) + { + int nIndexOffset; + + if (psFile->eCoverType == AVCCoverPC) + nIndexOffset = 356 + (iObjIndex-1)*8; + else + nIndexOffset = 100 + (iObjIndex-1)*8; + + AVCRawBinFSeek( psFile->psIndexFile, nIndexOffset, SEEK_SET ); + if( AVCRawBinEOF( psFile->psIndexFile ) ) + return NULL; + + nObjectOffset = AVCRawBinReadInt32( psFile->psIndexFile ); + nObjectOffset *= 2; + + if (psFile->eCoverType == AVCCoverPC) + nObjectOffset += 256; + } + else + nObjectOffset = nRecordStart + nRecordSize * (iObjIndex-1); + + /*----------------------------------------------------------------- + * Seek to the start of the object in the data file. + *----------------------------------------------------------------*/ + AVCRawBinFSeek( psFile->psRawBinFile, nObjectOffset, SEEK_SET ); + if( AVCRawBinEOF( psFile->psRawBinFile ) ) + return NULL; + + /*----------------------------------------------------------------- + * Read and return the object. + *----------------------------------------------------------------*/ + return AVCBinReadNextObject( psFile ); +} + + +/********************************************************************** + * AVCBinReadNextObject() + * + * Read the next structure from the file. This function is just a generic + * cover on top of the AVCBinReadNextArc/Lab/Pal/Cnt() functions. + * + * Returns a (void*) to a static structure with the contents of the object + * that was read. The contents of the structure will be valid only until + * the next call. + * If you use the returned value, then make sure that you cast it to + * the right type for the current file! (AVCArc, AVCPal, AVCCnt, ...) + * + * Returns NULL if an error happened or if EOF was reached. + **********************************************************************/ +void *AVCBinReadNextObject(AVCBinFile *psFile) +{ + void *psObj = NULL; + + switch(psFile->eFileType) + { + case AVCFileARC: + psObj = (void*)AVCBinReadNextArc(psFile); + break; + case AVCFilePAL: + case AVCFileRPL: + psObj = (void*)AVCBinReadNextPal(psFile); + break; + case AVCFileCNT: + psObj = (void*)AVCBinReadNextCnt(psFile); + break; + case AVCFileLAB: + psObj = (void*)AVCBinReadNextLab(psFile); + break; + case AVCFileTOL: + psObj = (void*)AVCBinReadNextTol(psFile); + break; + case AVCFileTXT: + case AVCFileTX6: + psObj = (void*)AVCBinReadNextTxt(psFile); + break; + case AVCFileRXP: + psObj = (void*)AVCBinReadNextRxp(psFile); + break; + case AVCFileTABLE: + psObj = (void*)AVCBinReadNextTableRec(psFile); + break; + default: + CPLError(CE_Failure, CPLE_IllegalArg, + "AVCBinReadNextObject(): Unsupported file type!"); + } + + return psObj; +} + + + +/********************************************************************** + * AVCBinReadNextTableRec() + * + * Reads the next record from an attribute table. + * + * Returns a pointer to an array of static AVCField structure whose + * contents will be valid only until the next call, + * or NULL if an error happened or if EOF was reached. + **********************************************************************/ +AVCField *AVCBinReadNextTableRec(AVCBinFile *psFile) +{ + if (psFile->eCoverType != AVCCoverPC && + psFile->eCoverType != AVCCoverPC2 && + psFile->eFileType == AVCFileTABLE && + psFile->hdr.psTableDef->numRecords > 0 && + ! AVCRawBinEOF(psFile->psRawBinFile) && + _AVCBinReadNextTableRec(psFile->psRawBinFile, + psFile->hdr.psTableDef->numFields, + psFile->hdr.psTableDef->pasFieldDef, + psFile->cur.pasFields, + psFile->hdr.psTableDef->nRecSize) == 0 ) + { + return psFile->cur.pasFields; + } + else if ((psFile->eCoverType == AVCCoverPC || + psFile->eCoverType == AVCCoverPC2 ) && + psFile->eFileType == AVCFileTABLE && + psFile->hdr.psTableDef->numRecords > 0 && + _AVCBinReadNextDBFTableRec(psFile->hDBFFile, + &(psFile->nCurDBFRecord), + psFile->hdr.psTableDef->numFields, + psFile->hdr.psTableDef->pasFieldDef, + psFile->cur.pasFields) == 0) + { + return psFile->cur.pasFields; + } + + return NULL; +} + + + + +/*===================================================================== + * ARC + *====================================================================*/ + +/********************************************************************** + * _AVCBinReadNextArc() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadNextArc() instead) + * + * Read the next Arc structure from the file. + * + * The contents of the psArc structure is assumed to be valid, and the + * psArc->pasVertices buffer may be reallocated or free()'d if it is not + * NULL. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadNextArc(AVCRawBinFile *psFile, AVCArc *psArc, + int nPrecision) +{ + int i, numVertices; + int nRecordSize, nStartPos, nBytesRead; + + psArc->nArcId = AVCRawBinReadInt32(psFile); + if (AVCRawBinEOF(psFile)) + return -1; + + nRecordSize = AVCRawBinReadInt32(psFile) * 2; + nStartPos = psFile->nCurPos+psFile->nOffset; + psArc->nUserId = AVCRawBinReadInt32(psFile); + psArc->nFNode = AVCRawBinReadInt32(psFile); + psArc->nTNode = AVCRawBinReadInt32(psFile); + psArc->nLPoly = AVCRawBinReadInt32(psFile); + psArc->nRPoly = AVCRawBinReadInt32(psFile); + numVertices = AVCRawBinReadInt32(psFile); + + /* Realloc the vertices array only if it needs to grow... + * do not realloc to a smaller size. + * Note that for simplicity reasons, we always store the vertices as + * double values in memory, even for single precision coverages. + */ + if (psArc->pasVertices == NULL || numVertices > psArc->numVertices) + psArc->pasVertices = (AVCVertex*)CPLRealloc(psArc->pasVertices, + numVertices*sizeof(AVCVertex)); + + psArc->numVertices = numVertices; + + if (nPrecision == AVC_SINGLE_PREC) + { + for(i=0; ipasVertices[i].x = AVCRawBinReadFloat(psFile); + psArc->pasVertices[i].y = AVCRawBinReadFloat(psFile); + } + } + else + { + for(i=0; ipasVertices[i].x = AVCRawBinReadDouble(psFile); + psArc->pasVertices[i].y = AVCRawBinReadDouble(psFile); + } + + } + + /*----------------------------------------------------------------- + * Record size may be larger than number of vertices. Skip up to + * start of next object. + *----------------------------------------------------------------*/ + nBytesRead = (psFile->nCurPos + psFile->nOffset) - nStartPos; + if ( nBytesRead < nRecordSize) + AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR); + + return 0; +} + +/********************************************************************** + * AVCBinReadNextArc() + * + * Read the next Arc structure from the file. + * + * Returns a pointer to a static AVCArc structure whose contents will be + * valid only until the next call or NULL if an error happened or if EOF + * was reached. + **********************************************************************/ +AVCArc *AVCBinReadNextArc(AVCBinFile *psFile) +{ + if (psFile->eFileType != AVCFileARC || + AVCRawBinEOF(psFile->psRawBinFile) || + _AVCBinReadNextArc(psFile->psRawBinFile, psFile->cur.psArc, + psFile->nPrecision) !=0) + { + return NULL; + } + + return psFile->cur.psArc; +} + + +/*===================================================================== + * PAL + *====================================================================*/ + +/********************************************************************** + * _AVCBinReadNextPal() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadNextPal() instead) + * + * Read the next PAL (Polygon Arc List) structure from the file. + * + * The contents of the psPal structure is assumed to be valid, and the + * psPal->paVertices buffer may be reallocated or free()'d if it is not + * NULL. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadNextPal(AVCRawBinFile *psFile, AVCPal *psPal, + int nPrecision) +{ + int i, numArcs; + int nRecordSize, nStartPos, nBytesRead; + + psPal->nPolyId = AVCRawBinReadInt32(psFile); + nRecordSize = AVCRawBinReadInt32(psFile) * 2; + nStartPos = psFile->nCurPos+psFile->nOffset; + + if (AVCRawBinEOF(psFile)) + return -1; + + if (nPrecision == AVC_SINGLE_PREC) + { + psPal->sMin.x = AVCRawBinReadFloat(psFile); + psPal->sMin.y = AVCRawBinReadFloat(psFile); + psPal->sMax.x = AVCRawBinReadFloat(psFile); + psPal->sMax.y = AVCRawBinReadFloat(psFile); + } + else + { + psPal->sMin.x = AVCRawBinReadDouble(psFile); + psPal->sMin.y = AVCRawBinReadDouble(psFile); + psPal->sMax.x = AVCRawBinReadDouble(psFile); + psPal->sMax.y = AVCRawBinReadDouble(psFile); + } + + numArcs = AVCRawBinReadInt32(psFile); + + /* Realloc the arc list array only if it needs to grow... + * do not realloc to a smaller size. + */ + if (psPal->pasArcs == NULL || numArcs > psPal->numArcs) + psPal->pasArcs = (AVCPalArc*)CPLRealloc(psPal->pasArcs, + numArcs*sizeof(AVCPalArc)); + + psPal->numArcs = numArcs; + + for(i=0; ipasArcs[i].nArcId = AVCRawBinReadInt32(psFile); + psPal->pasArcs[i].nFNode = AVCRawBinReadInt32(psFile); + psPal->pasArcs[i].nAdjPoly = AVCRawBinReadInt32(psFile); + } + + /*----------------------------------------------------------------- + * Record size may be larger than number of vertices. Skip up to + * start of next object. + *----------------------------------------------------------------*/ + nBytesRead = (psFile->nCurPos + psFile->nOffset) - nStartPos; + if ( nBytesRead < nRecordSize) + AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR); + + return 0; +} + +/********************************************************************** + * AVCBinReadNextPal() + * + * Read the next PAL structure from the file. + * + * Returns a pointer to a static AVCPal structure whose contents will be + * valid only until the next call or NULL if an error happened or if EOF + * was reached. + **********************************************************************/ +AVCPal *AVCBinReadNextPal(AVCBinFile *psFile) +{ + if ((psFile->eFileType!=AVCFilePAL && psFile->eFileType!=AVCFileRPL) || + AVCRawBinEOF(psFile->psRawBinFile) || + _AVCBinReadNextPal(psFile->psRawBinFile, psFile->cur.psPal, + psFile->nPrecision) !=0) + { + return NULL; + } + + return psFile->cur.psPal; +} + + +/*===================================================================== + * CNT + *====================================================================*/ + +/********************************************************************** + * _AVCBinReadNextCnt() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadNextCnt() instead) + * + * Read the next CNT (Polygon Centroid) structure from the file. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadNextCnt(AVCRawBinFile *psFile, AVCCnt *psCnt, + int nPrecision) +{ + int i, numLabels; + int nRecordSize, nStartPos, nBytesRead; + + psCnt->nPolyId = AVCRawBinReadInt32(psFile); + nRecordSize = AVCRawBinReadInt32(psFile) * 2; + nStartPos = psFile->nCurPos+psFile->nOffset; + + if (AVCRawBinEOF(psFile)) + return -1; + + if (nPrecision == AVC_SINGLE_PREC) + { + psCnt->sCoord.x = AVCRawBinReadFloat(psFile); + psCnt->sCoord.y = AVCRawBinReadFloat(psFile); + } + else + { + psCnt->sCoord.x = AVCRawBinReadDouble(psFile); + psCnt->sCoord.y = AVCRawBinReadDouble(psFile); + } + + numLabels = AVCRawBinReadInt32(psFile); + + /* Realloc the LabelIds array only if it needs to grow... + * do not realloc to a smaller size. + */ + if (psCnt->panLabelIds == NULL || numLabels > psCnt->numLabels) + psCnt->panLabelIds = (GInt32 *)CPLRealloc(psCnt->panLabelIds, + numLabels*sizeof(GInt32)); + + psCnt->numLabels = numLabels; + + for(i=0; ipanLabelIds[i] = AVCRawBinReadInt32(psFile); + } + + /*----------------------------------------------------------------- + * Record size may be larger than number of vertices. Skip up to + * start of next object. + *----------------------------------------------------------------*/ + nBytesRead = (psFile->nCurPos + psFile->nOffset) - nStartPos; + if ( nBytesRead < nRecordSize) + AVCRawBinFSeek(psFile, nRecordSize - nBytesRead, SEEK_CUR); + + return 0; +} + +/********************************************************************** + * AVCBinReadNextCnt() + * + * Read the next CNT structure from the file. + * + * Returns a pointer to a static AVCCnt structure whose contents will be + * valid only until the next call or NULL if an error happened or if EOF + * was reached. + **********************************************************************/ +AVCCnt *AVCBinReadNextCnt(AVCBinFile *psFile) +{ + if (psFile->eFileType != AVCFileCNT || + AVCRawBinEOF(psFile->psRawBinFile) || + _AVCBinReadNextCnt(psFile->psRawBinFile, psFile->cur.psCnt, + psFile->nPrecision) !=0) + { + return NULL; + } + + return psFile->cur.psCnt; +} + + +/*===================================================================== + * LAB + *====================================================================*/ + +/********************************************************************** + * _AVCBinReadNextLab() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadNextLab() instead) + * + * Read the next LAB (Centroid Label) structure from the file. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadNextLab(AVCRawBinFile *psFile, AVCLab *psLab, + int nPrecision) +{ + + psLab->nValue = AVCRawBinReadInt32(psFile); + psLab->nPolyId = AVCRawBinReadInt32(psFile); + + if (AVCRawBinEOF(psFile)) + return -1; + + if (nPrecision == AVC_SINGLE_PREC) + { + psLab->sCoord1.x = AVCRawBinReadFloat(psFile); + psLab->sCoord1.y = AVCRawBinReadFloat(psFile); + psLab->sCoord2.x = AVCRawBinReadFloat(psFile); + psLab->sCoord2.y = AVCRawBinReadFloat(psFile); + psLab->sCoord3.x = AVCRawBinReadFloat(psFile); + psLab->sCoord3.y = AVCRawBinReadFloat(psFile); + } + else + { + psLab->sCoord1.x = AVCRawBinReadDouble(psFile); + psLab->sCoord1.y = AVCRawBinReadDouble(psFile); + psLab->sCoord2.x = AVCRawBinReadDouble(psFile); + psLab->sCoord2.y = AVCRawBinReadDouble(psFile); + psLab->sCoord3.x = AVCRawBinReadDouble(psFile); + psLab->sCoord3.y = AVCRawBinReadDouble(psFile); + } + + return 0; +} + +/********************************************************************** + * AVCBinReadNextLab() + * + * Read the next LAB structure from the file. + * + * Returns a pointer to a static AVCLab structure whose contents will be + * valid only until the next call or NULL if an error happened or if EOF + * was reached. + **********************************************************************/ +AVCLab *AVCBinReadNextLab(AVCBinFile *psFile) +{ + if (psFile->eFileType != AVCFileLAB || + AVCRawBinEOF(psFile->psRawBinFile) || + _AVCBinReadNextLab(psFile->psRawBinFile, psFile->cur.psLab, + psFile->nPrecision) !=0) + { + return NULL; + } + + return psFile->cur.psLab; +} + +/*===================================================================== + * TOL + *====================================================================*/ + +/********************************************************************** + * _AVCBinReadNextTol() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadNextTol() instead) + * + * Read the next TOL (tolerance) structure from the file. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadNextTol(AVCRawBinFile *psFile, AVCTol *psTol, + int nPrecision) +{ + + psTol->nIndex = AVCRawBinReadInt32(psFile); + psTol->nFlag = AVCRawBinReadInt32(psFile); + + if (AVCRawBinEOF(psFile)) + return -1; + + if (nPrecision == AVC_SINGLE_PREC) + { + psTol->dValue = AVCRawBinReadFloat(psFile); + } + else + { + psTol->dValue = AVCRawBinReadDouble(psFile); + } + + return 0; +} + +/********************************************************************** + * AVCBinReadNextTol() + * + * Read the next TOL structure from the file. + * + * Returns a pointer to a static AVCTol structure whose contents will be + * valid only until the next call or NULL if an error happened or if EOF + * was reached. + **********************************************************************/ +AVCTol *AVCBinReadNextTol(AVCBinFile *psFile) +{ + if (psFile->eFileType != AVCFileTOL || + AVCRawBinEOF(psFile->psRawBinFile) || + _AVCBinReadNextTol(psFile->psRawBinFile, psFile->cur.psTol, + psFile->nPrecision) !=0) + { + return NULL; + } + + return psFile->cur.psTol; +} + +/*===================================================================== + * PRJ + *====================================================================*/ + +/********************************************************************** + * _AVCBinReadOpenPrj() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadOpen() with type AVCFilePRJ instead) + * + * Open a PRJ file. + * + * This call will actually read the whole PRJ file in memory since PRJ + * files are small text files. + **********************************************************************/ +AVCBinFile *_AVCBinReadOpenPrj(const char *pszPath, const char *pszName) +{ + AVCBinFile *psFile; + char *pszFname, **papszPrj; + + /*----------------------------------------------------------------- + * Load the PRJ file contents into a stringlist. + *----------------------------------------------------------------*/ + pszFname = (char*)CPLMalloc((strlen(pszPath)+strlen(pszName)+1)* + sizeof(char)); + sprintf(pszFname, "%s%s", pszPath, pszName); + + papszPrj = CSLLoad(pszFname); + + CPLFree(pszFname); + + if (papszPrj == NULL) + { + /* Failed to open file... just return NULL since an error message + * has already been issued by CSLLoad() + */ + return NULL; + } + + /*----------------------------------------------------------------- + * Alloc and init the AVCBinFile handle. + *----------------------------------------------------------------*/ + psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile)); + + psFile->eFileType = AVCFilePRJ; + psFile->psRawBinFile = NULL; + psFile->cur.papszPrj = papszPrj; + psFile->pszFilename = NULL; + + + return psFile; +} + +/********************************************************************** + * AVCBinReadPrj() + * + * Return the contents of the previously opened PRJ (projection) file. + * + * PRJ files are simple text files with variable length lines, so we + * don't use the AVCRawBin*() functions for this case. + * + * Returns a reference to a static stringlist with the whole file + * contents, or NULL in case of error. + * + * The returned stringlist should NOT be freed by the caller. + **********************************************************************/ +char **AVCBinReadNextPrj(AVCBinFile *psFile) +{ + /*----------------------------------------------------------------- + * The file should have already been loaded by AVCBinFileOpen(), + * so there is not much to do here! + *----------------------------------------------------------------*/ + return psFile->cur.papszPrj; +} + +/*===================================================================== + * TXT/TX6/TX7 + *====================================================================*/ + +/********************************************************************** + * _AVCBinReadNextTxt() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadNextTxt() instead) + * + * Read the next TXT/TX6/TX7 (Annotation) structure from the file. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadNextTxt(AVCRawBinFile *psFile, AVCTxt *psTxt, + int nPrecision) +{ + int i, numVerticesBefore, numVertices, numCharsToRead, nRecordSize; + int numBytesRead; + + numVerticesBefore = ABS(psTxt->numVerticesLine) + + ABS(psTxt->numVerticesArrow); + + psTxt->nTxtId = AVCRawBinReadInt32(psFile); + if (AVCRawBinEOF(psFile)) + return -1; + + nRecordSize = 8 + 2*AVCRawBinReadInt32(psFile); + + psTxt->nUserId = AVCRawBinReadInt32(psFile); + psTxt->nLevel = AVCRawBinReadInt32(psFile); + + psTxt->f_1e2 = AVCRawBinReadFloat(psFile); + psTxt->nSymbol = AVCRawBinReadInt32(psFile); + psTxt->numVerticesLine = AVCRawBinReadInt32(psFile); + psTxt->n28 = AVCRawBinReadInt32(psFile); + psTxt->numChars = AVCRawBinReadInt32(psFile); + psTxt->numVerticesArrow = AVCRawBinReadInt32(psFile); + + for(i=0; i<20; i++) + { + psTxt->anJust1[i] = AVCRawBinReadInt16(psFile); + } + for(i=0; i<20; i++) + { + psTxt->anJust2[i] = AVCRawBinReadInt16(psFile); + } + + if (nPrecision == AVC_SINGLE_PREC) + { + psTxt->dHeight = AVCRawBinReadFloat(psFile); + psTxt->dV2 = AVCRawBinReadFloat(psFile); + psTxt->dV3 = AVCRawBinReadFloat(psFile); + } + else + { + psTxt->dHeight = AVCRawBinReadDouble(psFile); + psTxt->dV2 = AVCRawBinReadDouble(psFile); + psTxt->dV3 = AVCRawBinReadDouble(psFile); + } + + numCharsToRead = ((int)(psTxt->numChars + 3)/4)*4; + if (psTxt->pszText == NULL || + ((int)(strlen((char*)psTxt->pszText)+3)/4)*4 < numCharsToRead ) + { + psTxt->pszText = (GByte*)CPLRealloc(psTxt->pszText, + (numCharsToRead+1)*sizeof(char)); + } + + AVCRawBinReadString(psFile, numCharsToRead, psTxt->pszText); + psTxt->pszText[psTxt->numChars] = '\0'; + + /* Realloc the vertices array only if it needs to grow... + * do not realloc to a smaller size. + */ + numVertices = ABS(psTxt->numVerticesLine) + ABS(psTxt->numVerticesArrow); + + if (psTxt->pasVertices == NULL || numVertices > numVerticesBefore) + psTxt->pasVertices = (AVCVertex*)CPLRealloc(psTxt->pasVertices, + numVertices*sizeof(AVCVertex)); + + if (nPrecision == AVC_SINGLE_PREC) + { + for(i=0; ipasVertices[i].x = AVCRawBinReadFloat(psFile); + psTxt->pasVertices[i].y = AVCRawBinReadFloat(psFile); + } + } + else + { + for(i=0; ipasVertices[i].x = AVCRawBinReadDouble(psFile); + psTxt->pasVertices[i].y = AVCRawBinReadDouble(psFile); + } + } + + /* In V7 Coverages, we always have 8 bytes of junk at end of record. + * In Weird coverages, these 8 bytes are sometimes present, and + * sometimes not!!! (Probably another AI "random feature"! ;-) + * So we use the record size to establish if there is any junk to skip + */ + if (nPrecision == AVC_SINGLE_PREC) + numBytesRead = 132 + numCharsToRead + numVertices * 2 * 4; + else + numBytesRead = 144 + numCharsToRead + numVertices * 2 * 8; + + if (numBytesRead < nRecordSize) + AVCRawBinFSeek(psFile, nRecordSize - numBytesRead, SEEK_CUR); + + return 0; +} + +/********************************************************************** + * _AVCBinReadNextPCCoverageTxt() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadNextTxt() instead) + * + * Read the next TXT (Annotation) structure from a PC Coverage file. + * Note that it is assumed that PC Coverage files are always single + * precision. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadNextPCCoverageTxt(AVCRawBinFile *psFile, AVCTxt *psTxt, + int nPrecision) +{ + int i, numVerticesBefore, numVertices, numCharsToRead, nRecordSize; + + numVerticesBefore = ABS(psTxt->numVerticesLine) + + ABS(psTxt->numVerticesArrow); + + psTxt->nTxtId = AVCRawBinReadInt32(psFile); + if (AVCRawBinEOF(psFile)) + return -1; + + nRecordSize = 8 + 2*AVCRawBinReadInt32(psFile); + + psTxt->nUserId = 0; + psTxt->nLevel = AVCRawBinReadInt32(psFile); + + psTxt->numVerticesLine = AVCRawBinReadInt32(psFile); + /* We are not expecting more than 4 vertices */ + psTxt->numVerticesLine = MIN(psTxt->numVerticesLine, 4); + + psTxt->numVerticesArrow = 0; + + /* Realloc the vertices array only if it needs to grow... + * do not realloc to a smaller size. + * + * Note that because of the way V7 binary TXT files work, the rest of the + * lib expects to receive duplicate coords for the first vertex, so + * we have to include an additional vertex for that. + */ + psTxt->numVerticesLine += 1; + numVertices = ABS(psTxt->numVerticesLine) + ABS(psTxt->numVerticesArrow); + + if (psTxt->pasVertices == NULL || numVertices > numVerticesBefore) + psTxt->pasVertices = (AVCVertex*)CPLRealloc(psTxt->pasVertices, + numVertices*sizeof(AVCVertex)); + + for(i=1; ipasVertices[i].x = AVCRawBinReadFloat(psFile); + psTxt->pasVertices[i].y = AVCRawBinReadFloat(psFile); + } + else + { + psTxt->pasVertices[i].x = AVCRawBinReadDouble(psFile); + psTxt->pasVertices[i].y = AVCRawBinReadDouble(psFile); + } + } + /* Duplicate the first vertex because that's the way the other binary TXT + * files work and that's what the lib expects to generate the E00. + */ + psTxt->pasVertices[0].x = psTxt->pasVertices[1].x; + psTxt->pasVertices[0].y = psTxt->pasVertices[1].y; + + /* Skip the other floats (vertices) that are unused */ + if (nPrecision == AVC_SINGLE_PREC) + AVCRawBinFSeek(psFile, 4*(15-2*(numVertices-1)) , SEEK_CUR); + else + AVCRawBinFSeek(psFile, 8*(15-2*(numVertices-1)) , SEEK_CUR); + + if (nPrecision == AVC_SINGLE_PREC) + { + psTxt->dHeight = AVCRawBinReadFloat(psFile); + } + else + { + psTxt->dHeight = AVCRawBinReadDouble(psFile); + } + psTxt->f_1e2 = AVCRawBinReadFloat(psFile); + psTxt->nSymbol = AVCRawBinReadInt32(psFile); + psTxt->numChars = AVCRawBinReadInt32(psFile); + + /* In some cases, we may need to skip additional spaces after the + * text string... more than should be required to simply align with + * a 4 bytes boundary... include that in numCharsToRead + */ + if (nPrecision == AVC_SINGLE_PREC) + { + numCharsToRead = nRecordSize - (28 + 16*4); + } + else + { + numCharsToRead = nRecordSize - (28 + 16*8); + } + + /* Do a quick check in case file is corrupt! */ + psTxt->numChars = MIN(psTxt->numChars, numCharsToRead); + + if (psTxt->pszText == NULL || + ((int)(strlen((char*)psTxt->pszText)+3)/4)*4 < numCharsToRead ) + { + psTxt->pszText = (GByte*)CPLRealloc(psTxt->pszText, + (numCharsToRead+5)*sizeof(char)); + } + + + AVCRawBinReadString(psFile, numCharsToRead, psTxt->pszText); + psTxt->pszText[psTxt->numChars] = '\0'; + + /* Set unused members to default values... + */ + psTxt->dV2 = 0.0; + psTxt->dV3 = 0.0; + psTxt->n28 = 0; + for(i=0; i<20; i++) + { + psTxt->anJust1[i] = 0; + psTxt->anJust2[i] = 0; + } + + return 0; +} + +/********************************************************************** + * AVCBinReadNextTxt() + * + * Read the next TXT/TX6/TX7 structure from the file. + * + * Returns a pointer to a static AVCTxt structure whose contents will be + * valid only until the next call or NULL if an error happened or if EOF + * was reached. + **********************************************************************/ +AVCTxt *AVCBinReadNextTxt(AVCBinFile *psFile) +{ + int nStatus = 0; + + if ((psFile->eFileType != AVCFileTXT && psFile->eFileType != AVCFileTX6) || + AVCRawBinEOF(psFile->psRawBinFile) ) + { + return NULL; + } + + /* AVCCoverPC have a different TXT format than AVCCoverV7 + * + * Note: Some Weird coverages use the PC TXT structure, and some use the + * V7 structure. We distinguish them using the header's precision + * field in AVCBinReadRewind(). + */ + if (psFile->eFileType == AVCFileTXT && + (psFile->eCoverType == AVCCoverPC || + psFile->eCoverType == AVCCoverWeird) ) + { + /* TXT file in PC Coverages (and some Weird Coverages) + */ + nStatus = _AVCBinReadNextPCCoverageTxt(psFile->psRawBinFile, + psFile->cur.psTxt, + psFile->nPrecision); + } + else + { + /* TXT in V7 Coverages (and some Weird Coverages), and TX6/TX7 in + * all coverage types + */ + nStatus = _AVCBinReadNextTxt(psFile->psRawBinFile, psFile->cur.psTxt, + psFile->nPrecision); + } + + if (nStatus != 0) + { + return NULL; + } + + return psFile->cur.psTxt; +} + + +/*===================================================================== + * RXP + *====================================================================*/ + +/********************************************************************** + * _AVCBinReadNextRxp() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadNextRxp() instead) + * + * Read the next RXP (Region something...) structure from the file. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadNextRxp(AVCRawBinFile *psFile, + AVCRxp *psRxp, + CPL_UNUSED int nPrecision) +{ + + psRxp->n1 = AVCRawBinReadInt32(psFile); + if (AVCRawBinEOF(psFile)) + return -1; + psRxp->n2 = AVCRawBinReadInt32(psFile); + + return 0; +} + +/********************************************************************** + * AVCBinReadNextRxp() + * + * Read the next RXP structure from the file. + * + * Returns a pointer to a static AVCRxp structure whose contents will be + * valid only until the next call or NULL if an error happened or if EOF + * was reached. + **********************************************************************/ +AVCRxp *AVCBinReadNextRxp(AVCBinFile *psFile) +{ + if (psFile->eFileType != AVCFileRXP || + AVCRawBinEOF(psFile->psRawBinFile) || + _AVCBinReadNextRxp(psFile->psRawBinFile, psFile->cur.psRxp, + psFile->nPrecision) !=0) + { + return NULL; + } + + return psFile->cur.psRxp; +} + +/*===================================================================== + * NATIVE (V7.x) TABLEs + * + * Note: Also applies to AVCCoverWeird + *====================================================================*/ + +/********************************************************************** + * _AVCBinReadNextArcDir() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadOpen() with type AVCFileTABLE instead) + * + * Read the next record from an arc.dir (or "arcdr9") file. + * + * Note that arc.dir files have no header... they start with the + * first record immediately. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadNextArcDir(AVCRawBinFile *psFile, AVCTableDef *psArcDir) +{ + int i; + + /* Arc/Info Table name + */ + AVCRawBinReadString(psFile, 32, (GByte *)psArcDir->szTableName); + psArcDir->szTableName[32] = '\0'; + + if (AVCRawBinEOF(psFile)) + return -1; + + /* "ARC####" basename for .DAT and .NIT files + */ + AVCRawBinReadString(psFile, 8, (GByte *)psArcDir->szInfoFile); + psArcDir->szInfoFile[7] = '\0'; + for (i=6; i>0 && psArcDir->szInfoFile[i]==' '; i--) + psArcDir->szInfoFile[i] = '\0'; + + psArcDir->numFields = AVCRawBinReadInt16(psFile); + psArcDir->nRecSize = AVCRawBinReadInt16(psFile); + + AVCRawBinFSeek(psFile, 18, SEEK_CUR); /* Skip 18 bytes */ + + psArcDir->bDeletedFlag = AVCRawBinReadInt16(psFile); + psArcDir->numRecords = AVCRawBinReadInt32(psFile); + + AVCRawBinFSeek(psFile, 10, SEEK_CUR); /* Skip 10 bytes */ + + AVCRawBinReadBytes(psFile, 2, (GByte *)psArcDir->szExternal); + psArcDir->szExternal[2] = '\0'; + + AVCRawBinFSeek(psFile, 300, SEEK_CUR); /* Skip the remaining 300 bytes */ + + return 0; +} + +/********************************************************************** + * _AVCBinReadNextNit() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadOpen() with type AVCFileTABLE instead) + * + * Read the next record from an arc####.nit file. + * + * Note that arc####.nit files have no header... they start with the + * first record immediately. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadNextArcNit(AVCRawBinFile *psFile, AVCFieldInfo *psField) +{ + AVCRawBinReadString(psFile, 16, (GByte *)psField->szName); + psField->szName[16] = '\0'; + + if (AVCRawBinEOF(psFile)) + return -1; + + psField->nSize = AVCRawBinReadInt16(psFile); + psField->v2 = AVCRawBinReadInt16(psFile); /* Always -1 ? */ + psField->nOffset = AVCRawBinReadInt16(psFile); + psField->v4 = AVCRawBinReadInt16(psFile); /* Always 4 ? */ + psField->v5 = AVCRawBinReadInt16(psFile); /* Always -1 ? */ + psField->nFmtWidth = AVCRawBinReadInt16(psFile); + psField->nFmtPrec = AVCRawBinReadInt16(psFile); + psField->nType1 = AVCRawBinReadInt16(psFile); + psField->nType2 = AVCRawBinReadInt16(psFile); /* Always 0 ? */ + psField->v10 = AVCRawBinReadInt16(psFile); /* Always -1 ? */ + psField->v11 = AVCRawBinReadInt16(psFile); /* Always -1 ? */ + psField->v12 = AVCRawBinReadInt16(psFile); /* Always -1 ? */ + psField->v13 = AVCRawBinReadInt16(psFile); /* Always -1 ? */ + + AVCRawBinReadString(psFile, 16, (GByte *)psField->szAltName); /* Always Blank ? */ + psField->szAltName[16] = '\0'; + + AVCRawBinFSeek(psFile, 56, SEEK_CUR); /* Skip 56 bytes */ + + psField->nIndex = AVCRawBinReadInt16(psFile); + + AVCRawBinFSeek(psFile, 28, SEEK_CUR); /* Skip the remaining 28 bytes */ + + return 0; +} + +/********************************************************************** + * _AVCBinReadGetInfoFilename() + * + * Look for the DAT or NIT files for a given table... returns TRUE if + * they exist, or FALSE otherwise. + * + * If pszRetFnmae/pszRetNitFile != NULL then the filename with full path + * will be copied to the specified buffer. + **********************************************************************/ +GBool _AVCBinReadGetInfoFilename(const char *pszInfoPath, + const char *pszBasename, + const char *pszDatOrNit, + AVCCoverType eCoverType, + char *pszRetFname) +{ + GBool bFilesExist = FALSE; + char *pszBuf = NULL; + VSIStatBuf sStatBuf; + + if (pszRetFname) + pszBuf = pszRetFname; + else + pszBuf = (char*)CPLMalloc((strlen(pszInfoPath)+strlen(pszBasename)+10)* + sizeof(char)); + + if (eCoverType == AVCCoverWeird) + { + sprintf(pszBuf, "%s%s%s", pszInfoPath, pszBasename, pszDatOrNit); + } + else + { + sprintf(pszBuf, "%s%s.%s", pszInfoPath, pszBasename, pszDatOrNit); + } + + AVCAdjustCaseSensitiveFilename(pszBuf); + + if (VSIStat(pszBuf, &sStatBuf) == 0) + bFilesExist = TRUE; + + if (eCoverType == AVCCoverWeird && !bFilesExist) + { + /* In some cases, the filename can be truncated to 8 chars + * and we end up with "ARC000DA"... check that possibility. + */ + pszBuf[strlen(pszBuf)-1] = '\0'; + + AVCAdjustCaseSensitiveFilename(pszBuf); + + if (VSIStat(pszBuf, &sStatBuf) == 0) + bFilesExist = TRUE; + } + + if (pszRetFname == NULL) + CPLFree(pszBuf); + + return bFilesExist; + +} + +/********************************************************************** + * _AVCBinReadInfoFilesExist() + * + * Look for the DAT and NIT files for a given table... returns TRUE if + * they exist, or FALSE otherwise. + * + * If pszRetDatFile/pszRetNitFile != NULL then the .DAT and .NIT filename + * without the info path will be copied to the specified buffers. + **********************************************************************/ +GBool _AVCBinReadInfoFileExists(const char *pszInfoPath, + const char *pszBasename, + AVCCoverType eCoverType) +{ + + return (_AVCBinReadGetInfoFilename(pszInfoPath, pszBasename, + "dat", eCoverType, NULL) == TRUE && + _AVCBinReadGetInfoFilename(pszInfoPath, pszBasename, + "nit", eCoverType, NULL) == TRUE); + +} + +/********************************************************************** + * AVCBinReadListTables() + * + * Scan the arc.dir file and return stringlist with one entry for the + * Arc/Info name of each table that belongs to the specified coverage. + * Pass pszCoverName = NULL to get the list of all tables. + * + * ppapszArcDatFiles if not NULL will be set to point to a stringlist + * with the corresponding "ARC????" info file basenames corresponding + * to each table found. + * + * Note that arc.dir files have no header... they start with the + * first record immediately. + * + * In AVCCoverWeird, the file is called "arcdr9" + * + * Returns a stringlist that should be deallocated by the caller + * with CSLDestroy(), or NULL on error. + **********************************************************************/ +char **AVCBinReadListTables(const char *pszInfoPath, const char *pszCoverName, + char ***ppapszArcDatFiles, AVCCoverType eCoverType, + AVCDBCSInfo *psDBCSInfo) +{ + char **papszList = NULL; + char *pszFname; + char szNameToFind[33] = ""; + int nLen; + AVCRawBinFile *hFile; + AVCTableDef sEntry; + + if (ppapszArcDatFiles) + *ppapszArcDatFiles = NULL; + + /*----------------------------------------------------------------- + * For AVCCoverV7Tables type we do not look for tables for a specific + * coverage, we return all tables from the info dir. + *----------------------------------------------------------------*/ + if (eCoverType == AVCCoverV7Tables) + pszCoverName = NULL; + + /*----------------------------------------------------------------- + * All tables that belong to a given coverage have their name starting + * with the coverage name (in uppercase letters), followed by a 3 + * letters extension. + *----------------------------------------------------------------*/ + if (pszCoverName != NULL) + sprintf(szNameToFind, "%-.28s.", pszCoverName); + nLen = strlen(szNameToFind); + + /*----------------------------------------------------------------- + * Open the arc.dir and add all entries that match the criteria + * to our list. + * In AVCCoverWeird, the file is called "arcdr9" + *----------------------------------------------------------------*/ + pszFname = (char*)CPLMalloc((strlen(pszInfoPath)+9)*sizeof(char)); + if (eCoverType == AVCCoverWeird) + sprintf(pszFname, "%sarcdr9", pszInfoPath); + else + sprintf(pszFname, "%sarc.dir", pszInfoPath); + + AVCAdjustCaseSensitiveFilename(pszFname); + + hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType), + psDBCSInfo); + + if (hFile) + { + while (!AVCRawBinEOF(hFile) && + _AVCBinReadNextArcDir(hFile, &sEntry) == 0) + { + if (/* sEntry.numRecords > 0 && (DO NOT skip empty tables) */ + !sEntry.bDeletedFlag && + (pszCoverName == NULL || + EQUALN(szNameToFind, sEntry.szTableName, nLen)) && + _AVCBinReadInfoFileExists(pszInfoPath, + sEntry.szInfoFile, + eCoverType) ) + { + papszList = CSLAddString(papszList, sEntry.szTableName); + + if (ppapszArcDatFiles) + *ppapszArcDatFiles = CSLAddString(*ppapszArcDatFiles, + sEntry.szInfoFile); + } + } + AVCRawBinClose(hFile); + + } + + CPLFree(pszFname); + + return papszList; +} + +/********************************************************************** + * _AVCBinReadOpenTable() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadOpen() with type AVCFileTABLE instead) + * + * Open a INFO table, read the header file (.NIT), and finally open + * the associated data file to be ready to read records from it. + * + * Returns a valid AVCBinFile handle, or NULL if the file could + * not be opened. + * + * _AVCBinReadCloseTable() will eventually have to be called to release the + * resources used by the AVCBinFile structure. + **********************************************************************/ +AVCBinFile *_AVCBinReadOpenTable(const char *pszInfoPath, + const char *pszTableName, + AVCCoverType eCoverType, + AVCDBCSInfo *psDBCSInfo) +{ + AVCBinFile *psFile; + AVCRawBinFile *hFile; + AVCTableDef sTableDef; + AVCFieldInfo *pasFieldDef; + char *pszFname; + GBool bFound; + int i; + + /* Alloc a buffer big enough for the longest possible filename... + */ + pszFname = (char*)CPLMalloc((strlen(pszInfoPath)+81)*sizeof(char)); + + /*----------------------------------------------------------------- + * Fetch info about this table from the "arc.dir" + *----------------------------------------------------------------*/ + if (eCoverType == AVCCoverWeird) + sprintf(pszFname, "%sarcdr9", pszInfoPath); + else + sprintf(pszFname, "%sarc.dir", pszInfoPath); + + AVCAdjustCaseSensitiveFilename(pszFname); + + hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType), + psDBCSInfo); + bFound = FALSE; + + if (hFile) + { + while(!bFound && _AVCBinReadNextArcDir(hFile, &sTableDef) == 0) + { + if (!sTableDef.bDeletedFlag && + EQUALN(sTableDef.szTableName, pszTableName, + strlen(pszTableName)) && + _AVCBinReadInfoFileExists(pszInfoPath, + sTableDef.szInfoFile, + eCoverType)) + { + bFound = TRUE; + } + } + AVCRawBinClose(hFile); + } + + /* Hummm... quite likely that this table does not exist! + */ + if (!bFound) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Failed to open table %s", pszTableName); + CPLFree(pszFname); + return NULL; + } + + /*----------------------------------------------------------------- + * Establish the location of the data file... depends on the + * szExternal[] field. + *----------------------------------------------------------------*/ + if (EQUAL(sTableDef.szExternal, "XX")) + { + /*------------------------------------------------------------- + * The data file is located outside of the INFO directory. + * Read the path to the data file from the arc####.dat file + *------------------------------------------------------------*/ + _AVCBinReadGetInfoFilename(pszInfoPath, sTableDef.szInfoFile, + "dat", eCoverType, pszFname); + AVCAdjustCaseSensitiveFilename(pszFname); + + hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType), + psDBCSInfo); + + if (hFile) + { + /* Read the relative file path, and remove trailing spaces. + */ + AVCRawBinReadBytes(hFile, 80, (GByte *)sTableDef.szDataFile); + sTableDef.szDataFile[80] = '\0'; + + for(i = strlen(sTableDef.szDataFile)-1; + isspace((unsigned char)sTableDef.szDataFile[i]); + i--) + { + sTableDef.szDataFile[i] = '\0'; + } + + AVCRawBinClose(hFile); + } + else + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Failed to open file %s", pszFname); + CPLFree(pszFname); + return NULL; + } + + } + else + { + /*------------------------------------------------------------- + * The data file IS the arc####.dat file + * Note: sTableDef.szDataFile must be relative to info directory + *------------------------------------------------------------*/ + _AVCBinReadGetInfoFilename(pszInfoPath, sTableDef.szInfoFile, + "dat", eCoverType, pszFname); + strcpy(sTableDef.szDataFile, pszFname+strlen(pszInfoPath)); + } + + /*----------------------------------------------------------------- + * Read the table field definitions from the "arc####.nit" file. + *----------------------------------------------------------------*/ + _AVCBinReadGetInfoFilename(pszInfoPath, sTableDef.szInfoFile, + "nit", eCoverType, pszFname); + AVCAdjustCaseSensitiveFilename(pszFname); + + hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType), + psDBCSInfo); + + if (hFile) + { + int iField; + + pasFieldDef = (AVCFieldInfo*)CPLCalloc(sTableDef.numFields, + sizeof(AVCFieldInfo)); + + /*------------------------------------------------------------- + * There must be at least sTableDef.numFields valid entries + * in the .NIT file... + * + * Note that we ignore any deleted field entries (entries with + * index=-1)... I don't see any use for these deleted fields... + * and I don't understand why Arc/Info includes them in their + * E00 table headers... + *------------------------------------------------------------*/ + for(i=0, iField=0; iField 0) + iField++; + } + + AVCRawBinClose(hFile); + } + else + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Failed to open file %s", pszFname); + CPLFree(pszFname); + return NULL; + } + + + /*----------------------------------------------------------------- + * Open the data file... ready to read records from it. + * If the header says that table has 0 records, then we don't + * try to open the file... but we don't consider that as an error. + *----------------------------------------------------------------*/ + if (sTableDef.numRecords > 0 && + AVCFileExists(pszInfoPath, sTableDef.szDataFile)) + { + VSIStatBuf sStatBuf; + + sprintf(pszFname, "%s%s", pszInfoPath, sTableDef.szDataFile); + AVCAdjustCaseSensitiveFilename(pszFname); + + hFile = AVCRawBinOpen(pszFname, "r", AVC_COVER_BYTE_ORDER(eCoverType), + psDBCSInfo); + + /* OOPS... data file does not exist! + */ + if (hFile == NULL) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Failed to open file %s", pszFname); + CPLFree(pszFname); + return NULL; + } + + /*------------------------------------------------------------- + * In some cases, the number of records field for a table in the + * arc.dir does not correspond to the real number of records + * in the data file. In this kind of situation, the number of + * records returned by Arc/Info in an E00 file will be based + * on the real data file size, and not on the value from the arc.dir. + * + * Fetch the data file size, and correct the number of record + * field in the table header if necessary. + *------------------------------------------------------------*/ + if ( VSIStat(pszFname, &sStatBuf) != -1 && + sTableDef.nRecSize > 0 && + sStatBuf.st_size/sTableDef.nRecSize != sTableDef.numRecords) + { + sTableDef.numRecords = sStatBuf.st_size/sTableDef.nRecSize; + } + + } + else + { + hFile = NULL; + sTableDef.numRecords = 0; + } + + /*----------------------------------------------------------------- + * Alloc. and init. the AVCBinFile structure. + *----------------------------------------------------------------*/ + psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile)); + + psFile->psRawBinFile = hFile; + psFile->eCoverType = AVCCoverV7; + psFile->eFileType = AVCFileTABLE; + psFile->pszFilename = pszFname; + + psFile->hdr.psTableDef = (AVCTableDef*)CPLMalloc(sizeof(AVCTableDef)); + *(psFile->hdr.psTableDef) = sTableDef; + + psFile->hdr.psTableDef->pasFieldDef = pasFieldDef; + + /* We can't really tell the precision from a Table header... + * just set an arbitrary value... it probably won't be used anyways! + */ + psFile->nPrecision = AVC_SINGLE_PREC; + + /*----------------------------------------------------------------- + * Allocate temp. structures to use to read records from the file + * And allocate buffers for those fields that are stored as strings. + *----------------------------------------------------------------*/ + psFile->cur.pasFields = (AVCField*)CPLCalloc(sTableDef.numFields, + sizeof(AVCField)); + + for(i=0; icur.pasFields[i].pszStr = + (GByte*)CPLCalloc(pasFieldDef[i].nSize+1, sizeof(char)); + } + } + + return psFile; +} + + +/********************************************************************** + * _AVCBinReadNextTableRec() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadNextTableRec() instead) + * + * Reads the next record from an attribute table and fills the + * pasFields[] array. + * + * Note that it is assumed that the pasFields[] array has been properly + * initialized, re the allocation of buffers for fields strored as + * strings. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadNextTableRec(AVCRawBinFile *psFile, int nFields, + AVCFieldInfo *pasDef, AVCField *pasFields, + int nRecordSize) +{ + int i, nType, nBytesRead=0; + + if (psFile == NULL) + return -1; + + for(i=0; ihDBFFile = hDBFFile; + + psFile->eCoverType = AVCCoverPC; + psFile->eFileType = AVCFileTABLE; + psFile->pszFilename = CPLStrdup(pszDBFFilename); + + psFile->hdr.psTableDef = NULL; + + /* nCurDBFRecord is used to keep track of the 0-based index of the + * last record we read from the DBF file... this is to emulate + * sequential access which is assumed by the rest of the lib. + * Since the first record (record 0) has not been read yet, then + * we init the index at -1. + */ + psFile->nCurDBFRecord = -1; + + /* We can't really tell the precision from a Table header... + * just set an arbitrary value... it probably won't be used anyways! + */ + psFile->nPrecision = AVC_SINGLE_PREC; + + /*----------------------------------------------------------------- + * Build TableDef from the info in the DBF header + *----------------------------------------------------------------*/ + /* Use calloc() to init some unused struct members */ + psTableDef = (AVCTableDef*)CPLCalloc(1, sizeof(AVCTableDef)); + psFile->hdr.psTableDef = psTableDef; + + sprintf(psTableDef->szTableName, "%-32.32s", pszArcInfoTableName); + + psTableDef->numFields = DBFGetFieldCount(hDBFFile); + + /* We'll compute nRecSize value when we read fields info later */ + psTableDef->nRecSize = 0; + + psTableDef->numRecords = DBFGetRecordCount(hDBFFile); + + /* All DBF tables are considered External */ + strcpy(psTableDef->szExternal, "XX"); + + /*----------------------------------------------------------------- + * Build Field definitions + *----------------------------------------------------------------*/ + pasFieldDef = (AVCFieldInfo*)CPLCalloc(psTableDef->numFields, + sizeof(AVCFieldInfo)); + + psTableDef->pasFieldDef = pasFieldDef; + + for(iField=0; iField< psTableDef->numFields; iField++) + { + int nWidth, nDecimals; + /* DBFFieldType eDBFType; */ + char cNativeType; + + /*------------------------------------------------------------- + * Fetch DBF Field info and convert to Arc/Info type... + * Note that since DBF fields names are limited to 10 chars, + * we do not have to worry about field name length in the process. + *------------------------------------------------------------*/ + /* eDBFType = */ + DBFGetFieldInfo(hDBFFile, iField, + pasFieldDef[iField].szName, + &nWidth, &nDecimals); + cNativeType = DBFGetNativeFieldType(hDBFFile, iField); + + pasFieldDef[iField].nFmtWidth = (GInt16)nWidth; + pasFieldDef[iField].nFmtPrec = (GInt16)nDecimals; + + /* nIndex is the 1-based field index that we see in the E00 header */ + pasFieldDef[iField].nIndex = iField+1; + + if (cNativeType == 'F' || (cNativeType == 'N' && nDecimals > 0) ) + { + /*--------------------------------------------------------- + * BINARY FLOAT + *--------------------------------------------------------*/ + pasFieldDef[iField].nType1 = AVC_FT_BINFLOAT/10; + pasFieldDef[iField].nSize = 4; + pasFieldDef[iField].nFmtWidth = 12; /* PC Arc/Info ignores the */ + pasFieldDef[iField].nFmtPrec = 3; /* DBF width/precision */ + } + else if (cNativeType == 'N') + { + /*--------------------------------------------------------- + * BINARY INTEGER + *--------------------------------------------------------*/ + pasFieldDef[iField].nType1 = AVC_FT_BININT/10; + pasFieldDef[iField].nSize = 4; + pasFieldDef[iField].nFmtWidth = 5; /* PC Arc/Info ignores the */ + pasFieldDef[iField].nFmtPrec = -1; /* DBF width/precision */ + + /*--------------------------------------------------------- + * Some special integer fields need to have their names + * repaired because DBF does not support special characters. + *--------------------------------------------------------*/ + _AVCBinReadRepairDBFFieldName(pasFieldDef[iField].szName); + } + else if (cNativeType == 'D') + { + /*--------------------------------------------------------- + * DATE - Actually handled as a string internally + *--------------------------------------------------------*/ + pasFieldDef[iField].nType1 = AVC_FT_DATE/10; + pasFieldDef[iField].nSize = nWidth; + pasFieldDef[iField].nFmtPrec = -1; + + } + else /* (cNativeType == 'C' || cNativeType == 'L') */ + { + /*--------------------------------------------------------- + * CHAR STRINGS ... and all unknown types also handled as strings + *--------------------------------------------------------*/ + pasFieldDef[iField].nType1 = AVC_FT_CHAR/10; + pasFieldDef[iField].nSize = nWidth; + pasFieldDef[iField].nFmtPrec = -1; + + } + + /*--------------------------------------------------------- + * Keep track of position of field in record... first one always + * starts at offset=1 + *--------------------------------------------------------*/ + if (iField == 0) + pasFieldDef[iField].nOffset = 1; + else + pasFieldDef[iField].nOffset = (pasFieldDef[iField-1].nOffset + + pasFieldDef[iField-1].nSize ); + + /*--------------------------------------------------------- + * Set default values for all other unused members in the struct + *--------------------------------------------------------*/ + pasFieldDef[iField].v2 = -1; /* Always -1 ? */ + pasFieldDef[iField].v4 = 4; /* Always 4 ? */ + pasFieldDef[iField].v5 = -1; /* Always -1 ? */ + pasFieldDef[iField].nType2 = 0; /* Always 0 ? */ + pasFieldDef[iField].v10 = -1; /* Always -1 ? */ + pasFieldDef[iField].v11 = -1; /* Always -1 ? */ + pasFieldDef[iField].v12 = -1; /* Always -1 ? */ + pasFieldDef[iField].v13 = -1; /* Always -1 ? */ + + } + + /*----------------------------------------------------------------- + * Compute record size... + * Record size has to be rounded to a multiple of 2 bytes. + *----------------------------------------------------------------*/ + if (psTableDef->numFields > 0) + { + psTableDef->nRecSize = (pasFieldDef[psTableDef->numFields-1].nOffset-1+ + pasFieldDef[psTableDef->numFields-1].nSize); + psTableDef->nRecSize = ((psTableDef->nRecSize+1)/2)*2; + } + else + psTableDef->nRecSize = 0; + + /*----------------------------------------------------------------- + * Allocate temp. structures to use to read records from the file + * And allocate buffers for those fields that are stored as strings. + *----------------------------------------------------------------*/ + psFile->cur.pasFields = (AVCField*)CPLCalloc(psTableDef->numFields, + sizeof(AVCField)); + + for(iField=0; iFieldnumFields; iField++) + { + if (pasFieldDef[iField].nType1*10 == AVC_FT_DATE || + pasFieldDef[iField].nType1*10 == AVC_FT_CHAR || + pasFieldDef[iField].nType1*10 == AVC_FT_FIXINT || + pasFieldDef[iField].nType1*10 == AVC_FT_FIXNUM ) + { + psFile->cur.pasFields[iField].pszStr = + (GByte*)CPLCalloc(pasFieldDef[iField].nSize+1, sizeof(GByte)); + } + } + + return psFile; +} + + +/********************************************************************** + * _AVCBinReadNextDBFTableRec() + * + * (This function is for internal library use... external calls should + * go to AVCBinReadNextTableRec() instead) + * + * Reads the next record from a AVCCoverPC DBF attribute table and fills the + * pasFields[] array. + * + * Note that it is assumed that the pasFields[] array has been properly + * initialized, re the allocation of buffers for fields stored as + * strings. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinReadNextDBFTableRec(DBFHandle hDBFFile, int *piRecordIndex, + int nFields, AVCFieldInfo *pasDef, + AVCField *pasFields) +{ + int i, nType; + + /*----------------------------------------------------------------- + * Increment current record index. + * We use nCurDBFRecord to keep track of the 0-based index of the + * last record we read from the DBF file... this is to emulate + * sequential access which is assumed by the rest of the lib. + *----------------------------------------------------------------*/ + if (hDBFFile == NULL || piRecordIndex == NULL || + pasDef == NULL || pasFields == NULL) + return -1; + + (*piRecordIndex)++; + + if (*piRecordIndex >= DBFGetRecordCount(hDBFFile)) + return -1; /* Reached EOF */ + + /*----------------------------------------------------------------- + * Read/convert each field based on type + *----------------------------------------------------------------*/ + for(i=0; iBIN conversion library + * Language: ANSI C + * Purpose: Binary files access functions (write mode). + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2001, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: avc_binwr.c,v $ + * Revision 1.18 2008/07/23 20:51:38 dmorissette + * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char + * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495) + * + * Revision 1.17 2006/06/14 16:31:28 daniel + * Added support for AVCCoverPC2 type (bug 1491) + * + * Revision 1.16 2005/06/03 03:49:58 daniel + * Update email address, website url, and copyright dates + * + * Revision 1.15 2001/07/06 05:09:33 daniel + * Removed #ifdef around fseek to fix NT4 drive problem since ANSI-C requires + * an fseek() between read and write operations so this applies to Unix too. + * + * Revision 1.14 2001/07/06 04:25:00 daniel + * Fixed a problem writing arc.dir on NT4 networked drives in an empty dir. + * + * Revision 1.13 2000/10/16 16:13:29 daniel + * Fixed sHeader.nPrecision when writing PC TXT files + * + * Revision 1.12 2000/09/26 21:40:18 daniel + * Fixed writing of PC Coverage TXT... they're different from V7 TXT + * + * Revision 1.11 2000/09/26 20:21:04 daniel + * Added AVCCoverPC write + * + * Revision 1.10 2000/09/22 19:45:20 daniel + * Switch to MIT-style license + * + * Revision 1.9 2000/05/29 15:31:30 daniel + * Added Japanese DBCS support + * + * Revision 1.8 2000/01/10 02:55:12 daniel + * Added call to AVCAdjustCaseSensitiveFilename() when creating tables + * + * Revision 1.7 1999/12/24 07:18:34 daniel + * Added PC Arc/Info coverages support + * + * Revision 1.6 1999/08/26 17:26:09 daniel + * Removed printf() messages used in Windows tests + * + * Revision 1.5 1999/08/23 18:18:51 daniel + * Fixed memory leak and some VC++ warnings + * + * Revision 1.4 1999/06/08 22:08:14 daniel + * Modified CreateTable() to overwrite existing arc.dir entries if necessary + * + * Revision 1.3 1999/06/08 18:24:32 daniel + * Fixed some problems with '/' vs '\\' on Windows + * + * Revision 1.2 1999/05/17 16:17:36 daniel + * Added RXP + TXT/TX6/TX7 write support + * + * Revision 1.1 1999/05/11 02:34:46 daniel + * Initial revision + * + **********************************************************************/ + +#include "avc.h" + +#include /* tolower() */ + +/*===================================================================== + * Stuff related to writing the binary coverage files + *====================================================================*/ + +static void _AVCBinWriteCloseTable(AVCBinFile *psFile); + +static AVCBinFile *_AVCBinWriteCreateDBFTable(const char *pszPath, + const char *pszCoverName, + AVCTableDef *psSrcTableDef, + AVCCoverType eCoverType, + int nPrecision, + AVCDBCSInfo *psDBCSInfo); + +/********************************************************************** + * AVCBinWriteCreate() + * + * Open a coverage file for writing, write a header if applicable, and + * initialize the handle to be ready to write objects to the file. + * + * pszPath is the coverage (or info directory) path, terminated by + * a '/' or a '\\' + * pszName is the name of the file to create relative to this directory. + * + * Note: For most file types except tables, passing pszPath="" and + * including the coverage path as part of pszName instead would work. + * + * Returns a valid AVCBinFile handle, or NULL if the file could + * not be created. + * + * AVCBinClose() will eventually have to be called to release the + * resources used by the AVCBinFile structure. + **********************************************************************/ +AVCBinFile *AVCBinWriteCreate(const char *pszPath, const char *pszName, + AVCCoverType eCoverType, + AVCFileType eType, int nPrecision, + AVCDBCSInfo *psDBCSInfo) +{ + AVCBinFile *psFile; + char *pszFname = NULL, *pszExt; + GBool bCreateIndex = FALSE; + int nLen; + + /*----------------------------------------------------------------- + * Make sure precision value is valid (AVC_DEFAULT_PREC is NOT valid) + *----------------------------------------------------------------*/ + if (nPrecision!=AVC_SINGLE_PREC && nPrecision!=AVC_DOUBLE_PREC) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "AVCBinWriteCreate(): Invalid precision parameter " + "(value must be AVC_SINGLE_PREC or AVC_DOUBLE_PREC)"); + return NULL; + } + + /*----------------------------------------------------------------- + * The case of INFO tables is a bit different... + * tables have to be created through a separate function. + *----------------------------------------------------------------*/ + if (eType == AVCFileTABLE) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "AVCBinWriteCreate(): TABLEs must be created using " + "AVCBinWriteCreateTable()"); + return NULL; + } + + /*----------------------------------------------------------------- + * Alloc and init the AVCBinFile struct. + *----------------------------------------------------------------*/ + psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile)); + + psFile->eFileType = eType; + psFile->nPrecision = nPrecision; + + psFile->pszFilename = (char*)CPLMalloc((strlen(pszPath)+strlen(pszName)+1)* + sizeof(char)); + sprintf(psFile->pszFilename, "%s%s", pszPath, pszName); + + psFile->eCoverType = eCoverType; + + /*----------------------------------------------------------------- + * PRJ files are text files... we won't use the AVCRawBin*() + * functions for them... the file will be created and closed + * inside AVCBinWritePrj(). + *----------------------------------------------------------------*/ + if (eType == AVCFilePRJ) + { + return psFile; + } + + /*----------------------------------------------------------------- + * All other file types share a very similar creation method. + *----------------------------------------------------------------*/ + psFile->psRawBinFile = AVCRawBinOpen(psFile->pszFilename, "w", + AVC_COVER_BYTE_ORDER(psFile->eCoverType), + psDBCSInfo); + + if (psFile->psRawBinFile == NULL) + { + /* Failed to open file... just return NULL since an error message + * has already been issued by AVCRawBinOpen() + */ + CPLFree(psFile->pszFilename); + CPLFree(psFile); + return NULL; + } + + /*----------------------------------------------------------------- + * Create an Index file if applicable for current file type. + * Yep, we'll have a problem if filenames come in as uppercase, but + * this should not happen in a normal situation. + * For each type there is 3 possibilities, e.g. "pal", "pal.adf", "ttt.pal" + *----------------------------------------------------------------*/ + pszFname = CPLStrdup(psFile->pszFilename); + nLen = strlen(pszFname); + if (eType == AVCFileARC && + ( (nLen>=3 && EQUALN((pszExt=pszFname+nLen-3), "arc", 3)) || + (nLen>=7 && EQUALN((pszExt=pszFname+nLen-7), "arc.adf", 7)) ) ) + { + strncpy(pszExt, "arx", 3); + bCreateIndex = TRUE; + } + else if ((eType == AVCFilePAL || eType == AVCFileRPL) && + ( (nLen>=3 && EQUALN((pszExt=pszFname+nLen-3), "pal", 3)) || + (nLen>=7 && EQUALN((pszExt=pszFname+nLen-7), "pal.adf", 7)) ) ) + { + strncpy(pszExt, "pax", 3); + bCreateIndex = TRUE; + } + else if (eType == AVCFileCNT && + ( (nLen>=3 && EQUALN((pszExt=pszFname+nLen-3), "cnt", 3)) || + (nLen>=7 && EQUALN((pszExt=pszFname+nLen-7), "cnt.adf", 7)) ) ) + { + strncpy(pszExt, "cnx", 3); + bCreateIndex = TRUE; + } + else if ((eType == AVCFileTXT || eType == AVCFileTX6) && + ( (nLen>=3 && EQUALN((pszExt=pszFname+nLen-3), "txt", 3)) || + (nLen>=7 && EQUALN((pszExt=pszFname+nLen-7), "txt.adf", 7)) ) ) + { + strncpy(pszExt, "txx", 3); + bCreateIndex = TRUE; + } + + if (bCreateIndex) + { + psFile->psIndexFile = AVCRawBinOpen(pszFname, "w", + AVC_COVER_BYTE_ORDER(psFile->eCoverType), + psDBCSInfo); + } + + CPLFree(pszFname); + + /*----------------------------------------------------------------- + * Generate the appropriate headers for the main file and its index + * if one was created. + *----------------------------------------------------------------*/ + if (AVCBinWriteHeader(psFile) == -1) + { + /* Failed! Return NULL */ + AVCBinWriteClose(psFile); + psFile = NULL; + } + + return psFile; +} + + +/********************************************************************** + * _AVCBinWriteHeader() + * + * (This function is for internal library use... external calls should + * go to AVCBinWriteHeader() instead) + * + * Generate a 100 bytes header using the info in psHeader. + * + * Note: PC Coverage files have an initial 256 bytes header followed by the + * regular 100 bytes header. + * + * This function assumes that the file pointer is currently located at + * the beginning of the file. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWriteHeader(AVCRawBinFile *psFile, AVCBinHeader *psHeader, + AVCCoverType eCoverType) +{ + int nStatus = 0; + + if (eCoverType == AVCCoverPC) + { + /* PC Coverage header starts with an initial 256 bytes header + */ + AVCRawBinWriteInt16(psFile, 0x0400); /* Signature??? */ + AVCRawBinWriteInt32(psFile, psHeader->nLength); + + AVCRawBinWriteZeros(psFile, 250); + } + + AVCRawBinWriteInt32(psFile, psHeader->nSignature); + AVCRawBinWriteInt32(psFile, psHeader->nPrecision); + AVCRawBinWriteInt32(psFile, psHeader->nRecordSize); + AVCRawBinWriteZeros(psFile, 12); + AVCRawBinWriteInt32(psFile, psHeader->nLength); + + /* Pad the rest of the header with zeros + */ + AVCRawBinWriteZeros(psFile, 72); + + if (CPLGetLastErrorNo() != 0) + nStatus = -1; + + return nStatus; +} + + +/********************************************************************** + * AVCBinWriteHeader() + * + * Write a header to the specified file using the values that apply to + * this file's type. The function simply does nothing if it is called + * for a file type that requires no header. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int AVCBinWriteHeader(AVCBinFile *psFile) +{ + AVCBinHeader sHeader; + int nStatus=0; + GBool bHeader = TRUE; + + /*----------------------------------------------------------------- + * Set the appropriate header information for this file type. + *----------------------------------------------------------------*/ + sHeader.nPrecision = sHeader.nRecordSize = sHeader.nLength = 0; + sHeader.nSignature = 9994; + switch(psFile->eFileType) + { + case AVCFileARC: + sHeader.nPrecision = (psFile->nPrecision == AVC_DOUBLE_PREC)? -1 : 1; + break; + case AVCFilePAL: + case AVCFileRPL: + sHeader.nPrecision = (psFile->nPrecision == AVC_DOUBLE_PREC)? -11 : 11; + break; + case AVCFileLAB: + sHeader.nSignature = 9993; + sHeader.nPrecision = (psFile->nPrecision == AVC_DOUBLE_PREC)? -2 : 2; + sHeader.nRecordSize = (psFile->nPrecision == AVC_DOUBLE_PREC)? 28 : 16; + break; + case AVCFileCNT: + sHeader.nPrecision = (psFile->nPrecision == AVC_DOUBLE_PREC)? -14 : 14; + break; + case AVCFileTOL: + /* single prec: tol.adf has no header + * double prec: par.adf has a header + */ + if (psFile->nPrecision == AVC_DOUBLE_PREC) + { + sHeader.nSignature = 9993; + sHeader.nPrecision = 40; + sHeader.nRecordSize = 8; + } + else + { + bHeader = FALSE; + } + break; + case AVCFileTXT: + case AVCFileTX6: + if (psFile->eCoverType == AVCCoverPC) + sHeader.nPrecision = 1; + else + sHeader.nPrecision = (psFile->nPrecision==AVC_DOUBLE_PREC)? -67:67; + break; + default: + /* Other file types don't need a header */ + bHeader = FALSE; + } + + /*----------------------------------------------------------------- + * Write a header only if applicable. + *----------------------------------------------------------------*/ + if (bHeader) + nStatus = _AVCBinWriteHeader(psFile->psRawBinFile, &sHeader, + psFile->eCoverType); + + /*----------------------------------------------------------------- + * Write a header for the index file... it is identical to the main + * file's header. + *----------------------------------------------------------------*/ + if (nStatus == 0 && bHeader && psFile->psIndexFile) + nStatus = _AVCBinWriteHeader(psFile->psIndexFile, &sHeader, + psFile->eCoverType); + + return nStatus; +} + +/********************************************************************** + * AVCBinWriteClose() + * + * Close a coverage file opened for wirting, and release all memory + * (object strcut., buffers, etc.) associated with this file. + **********************************************************************/ + +void AVCBinWriteClose(AVCBinFile *psFile) +{ + if (psFile->eFileType == AVCFileTABLE) + { + _AVCBinWriteCloseTable(psFile); + return; + } + + /*----------------------------------------------------------------- + * Write the file size (nbr of 2 byte words) in the header at + * byte 24 in the 100 byte header (only if applicable) + * (And write the same value at byte 2-5 in the first header of PC Cover) + *----------------------------------------------------------------*/ + if (psFile->psRawBinFile && + (psFile->eFileType == AVCFileARC || + psFile->eFileType == AVCFilePAL || + psFile->eFileType == AVCFileRPL || + psFile->eFileType == AVCFileLAB || + psFile->eFileType == AVCFileCNT || + psFile->eFileType == AVCFileTXT || + psFile->eFileType == AVCFileTX6 || + (psFile->eFileType == AVCFileTOL && + psFile->nPrecision == AVC_DOUBLE_PREC) ) ) + { + GInt32 n32Size; + n32Size = psFile->psRawBinFile->nCurPos/2; + + if (psFile->eCoverType == AVCCoverPC) + { + /* PC Cover... Pad to multiple of 512 bytes and write 2 headers + * extra bytes at EOF are not included in the size written in + * header. + * The first 256 bytes header is not counted in the file size + * written in both headers + */ + n32Size -= 128; /* minus 256 bytes */ + + if (psFile->psRawBinFile->nCurPos%512 != 0) + AVCRawBinWriteZeros(psFile->psRawBinFile, + 512 - psFile->psRawBinFile->nCurPos%512); + + VSIFSeek(psFile->psRawBinFile->fp, 2, SEEK_SET); + AVCRawBinWriteInt32(psFile->psRawBinFile, n32Size); + + VSIFSeek(psFile->psRawBinFile->fp, 256+24, SEEK_SET); + AVCRawBinWriteInt32(psFile->psRawBinFile, n32Size); + } + else + { + /* V7 Cover ... only 1 header */ + VSIFSeek(psFile->psRawBinFile->fp, 24, SEEK_SET); + AVCRawBinWriteInt32(psFile->psRawBinFile, n32Size); + } + } + + AVCRawBinClose(psFile->psRawBinFile); + psFile->psRawBinFile = NULL; + + /*----------------------------------------------------------------- + * Same for the index file if it exists. + *----------------------------------------------------------------*/ + if (psFile->psIndexFile) + { + GInt32 n32Size; + n32Size = psFile->psIndexFile->nCurPos/2; + + if (psFile->eCoverType == AVCCoverPC) + { + /* PC Cover... Pad to multiple of 512 bytes and write 2 headers + * extra bytes at EOF are not included in the size written in + * header + */ + n32Size -= 128; /* minus 256 bytes */ + + if (psFile->psIndexFile->nCurPos%512 != 0) + AVCRawBinWriteZeros(psFile->psIndexFile, + 512 - psFile->psIndexFile->nCurPos%512); + + VSIFSeek(psFile->psIndexFile->fp, 2, SEEK_SET); + AVCRawBinWriteInt32(psFile->psIndexFile, n32Size); + + VSIFSeek(psFile->psIndexFile->fp, 256+24, SEEK_SET); + AVCRawBinWriteInt32(psFile->psIndexFile, n32Size); + } + else + { + /* V7 Cover ... only 1 header */ + VSIFSeek(psFile->psIndexFile->fp, 24, SEEK_SET); + AVCRawBinWriteInt32(psFile->psIndexFile, n32Size); + } + + AVCRawBinClose(psFile->psIndexFile); + psFile->psIndexFile = NULL; + } + + CPLFree(psFile->pszFilename); + + CPLFree(psFile); +} + + + +/********************************************************************** + * _AVCBinWriteIndexEntry() + * + * (This function is for internal library use... the index entries + * are automatically handled by the AVCBinWrite*() functions) + * + * Write an Index Entry at the current position in the file. + * + * Position is relative to the beginning of the file, including the header. + * Both position and size are specified in number of 2 byte words. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWriteIndexEntry(AVCRawBinFile *psFile, + int nPosition, int nSize) +{ + AVCRawBinWriteInt32(psFile, nPosition); + AVCRawBinWriteInt32(psFile, nSize); + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * AVCBinWriteObject() + * + * Write a CNT (Polygon Centroid) structure to the fin object to a + * coverage file. + * + * Simply redirects the call to the right function based on the value + * of psFile->eFileType. + * + * Returns 0 on success or -1 on error. + * + * If a problem happens, then CPLError() will be called by the lower-level + * functions and CPLGetLastErrorNo() can be used to find out what happened. + **********************************************************************/ +int AVCBinWriteObject(AVCBinFile *psFile, void *psObj) +{ + int nStatus = 0; + switch(psFile->eFileType) + { + case AVCFileARC: + nStatus = AVCBinWriteArc(psFile, (AVCArc *)psObj); + break; + case AVCFilePAL: + case AVCFileRPL: + nStatus = AVCBinWritePal(psFile, (AVCPal *)psObj); + break; + case AVCFileCNT: + nStatus = AVCBinWriteCnt(psFile, (AVCCnt *)psObj); + break; + case AVCFileLAB: + nStatus = AVCBinWriteLab(psFile, (AVCLab *)psObj); + break; + case AVCFileTOL: + nStatus = AVCBinWriteTol(psFile, (AVCTol *)psObj); + break; + case AVCFilePRJ: + nStatus = AVCBinWritePrj(psFile, (char **)psObj); + break; + case AVCFileTXT: + case AVCFileTX6: + nStatus = AVCBinWriteTxt(psFile, (AVCTxt *)psObj); + break; + case AVCFileRXP: + nStatus = AVCBinWriteRxp(psFile, (AVCRxp *)psObj); + break; + case AVCFileTABLE: + nStatus = AVCBinWriteTableRec(psFile, (AVCField *)psObj); + break; + default: + CPLError(CE_Failure, CPLE_IllegalArg, + "AVCBinWriteObject(): Unsupported file type!"); + nStatus = -1; + } + + return nStatus; +} + + +/*===================================================================== + * ARC + *====================================================================*/ + +/********************************************************************** + * _AVCBinWriteArc() + * + * (This function is for internal library use... external calls should + * go to AVCBinWriteNextArc() instead) + * + * Write an Arc structure to the file. + * + * The contents of the psArc structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWriteArc(AVCRawBinFile *psFile, AVCArc *psArc, + int nPrecision, AVCRawBinFile *psIndexFile) +{ + int i, nRecSize, nCurPos; + + nCurPos = psFile->nCurPos/2; /* Value in 2 byte words */ + + AVCRawBinWriteInt32(psFile, psArc->nArcId); + if (CPLGetLastErrorNo() != 0) + return -1; + + /*----------------------------------------------------------------- + * Record size is expressed in 2 byte words, and does not count the + * first 8 bytes of the ARC entry. + *----------------------------------------------------------------*/ + nRecSize = (6 * 4 + psArc->numVertices*2 * + ((nPrecision == AVC_SINGLE_PREC)? 4 : 8)) / 2; + AVCRawBinWriteInt32(psFile, nRecSize); + AVCRawBinWriteInt32(psFile, psArc->nUserId); + AVCRawBinWriteInt32(psFile, psArc->nFNode); + AVCRawBinWriteInt32(psFile, psArc->nTNode); + AVCRawBinWriteInt32(psFile, psArc->nLPoly); + AVCRawBinWriteInt32(psFile, psArc->nRPoly); + AVCRawBinWriteInt32(psFile, psArc->numVertices); + + if (nPrecision == AVC_SINGLE_PREC) + { + for(i=0; inumVertices; i++) + { + AVCRawBinWriteFloat(psFile, (float)psArc->pasVertices[i].x); + AVCRawBinWriteFloat(psFile, (float)psArc->pasVertices[i].y); + } + } + else + { + for(i=0; inumVertices; i++) + { + AVCRawBinWriteDouble(psFile, psArc->pasVertices[i].x); + AVCRawBinWriteDouble(psFile, psArc->pasVertices[i].y); + } + + } + + /*----------------------------------------------------------------- + * Write index entry (arx.adf) + *----------------------------------------------------------------*/ + if (psIndexFile) + { + _AVCBinWriteIndexEntry(psIndexFile, nCurPos, nRecSize); + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * AVCBinWriteArc() + * + * Write the next Arc structure to the file. + * + * The contents of the psArc structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + * + * If a problem happens, then CPLError() will be called by the lower-level + * functions and CPLGetLastErrorNo() can be used to find out what happened. + **********************************************************************/ +int AVCBinWriteArc(AVCBinFile *psFile, AVCArc *psArc) +{ + if (psFile->eFileType != AVCFileARC) + return -1; + + return _AVCBinWriteArc(psFile->psRawBinFile, psArc, + psFile->nPrecision, psFile->psIndexFile); +} + + +/*===================================================================== + * PAL + *====================================================================*/ + +/********************************************************************** + * _AVCBinWritePal() + * + * (This function is for internal library use... external calls should + * go to AVCBinWritePal() instead) + * + * Write a PAL (Polygon Arc List) structure to the file. + * + * The contents of the psPal structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWritePal(AVCRawBinFile *psFile, AVCPal *psPal, + int nPrecision, AVCRawBinFile *psIndexFile) +{ + int i, nRecSize, nCurPos; + + nCurPos = psFile->nCurPos/2; /* Value in 2 byte words */ + + AVCRawBinWriteInt32(psFile, psPal->nPolyId); + if (CPLGetLastErrorNo() != 0) + return -1; + + /*----------------------------------------------------------------- + * Record size is expressed in 2 byte words, and does not count the + * first 8 bytes of the PAL entry. + *----------------------------------------------------------------*/ + nRecSize = ( 4 + psPal->numArcs*3 * 4 + + 4 * ((nPrecision == AVC_SINGLE_PREC)? 4 : 8)) / 2; + + AVCRawBinWriteInt32(psFile, nRecSize); + + if (nPrecision == AVC_SINGLE_PREC) + { + AVCRawBinWriteFloat(psFile, (float)psPal->sMin.x); + AVCRawBinWriteFloat(psFile, (float)psPal->sMin.y); + AVCRawBinWriteFloat(psFile, (float)psPal->sMax.x); + AVCRawBinWriteFloat(psFile, (float)psPal->sMax.y); + } + else + { + AVCRawBinWriteDouble(psFile, psPal->sMin.x); + AVCRawBinWriteDouble(psFile, psPal->sMin.y); + AVCRawBinWriteDouble(psFile, psPal->sMax.x); + AVCRawBinWriteDouble(psFile, psPal->sMax.y); + } + + AVCRawBinWriteInt32(psFile, psPal->numArcs); + + for(i=0; inumArcs; i++) + { + AVCRawBinWriteInt32(psFile, psPal->pasArcs[i].nArcId); + AVCRawBinWriteInt32(psFile, psPal->pasArcs[i].nFNode); + AVCRawBinWriteInt32(psFile, psPal->pasArcs[i].nAdjPoly); + } + + /*----------------------------------------------------------------- + * Write index entry (pax.adf) + *----------------------------------------------------------------*/ + if (psIndexFile) + { + _AVCBinWriteIndexEntry(psIndexFile, nCurPos, nRecSize); + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * AVCBinWritePal() + * + * Write a PAL (Polygon Arc List) structure to the file. + * + * The contents of the psPal structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + * + * If a problem happens, then CPLError() will be called by the lower-level + * functions and CPLGetLastErrorNo() can be used to find out what happened. + **********************************************************************/ +int AVCBinWritePal(AVCBinFile *psFile, AVCPal *psPal) +{ + if (psFile->eFileType != AVCFilePAL && psFile->eFileType != AVCFileRPL) + return -1; + + return _AVCBinWritePal(psFile->psRawBinFile, psPal, + psFile->nPrecision, psFile->psIndexFile); +} + +/*===================================================================== + * CNT + *====================================================================*/ + +/********************************************************************** + * _AVCBinWriteCnt() + * + * (This function is for internal library use... external calls should + * go to AVCBinWriteCnt() instead) + * + * Write a CNT (Polygon Centroid) structure to the file. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWriteCnt(AVCRawBinFile *psFile, AVCCnt *psCnt, + int nPrecision, AVCRawBinFile *psIndexFile) +{ + int i, nRecSize, nCurPos; + + nCurPos = psFile->nCurPos/2; /* Value in 2 byte words */ + + AVCRawBinWriteInt32(psFile, psCnt->nPolyId); + if (CPLGetLastErrorNo() != 0) + return -1; + + /*----------------------------------------------------------------- + * Record size is expressed in 2 byte words, and does not count the + * first 8 bytes of the CNT entry. + *----------------------------------------------------------------*/ + nRecSize = ( 4 + psCnt->numLabels * 4 + + 2 * ((nPrecision == AVC_SINGLE_PREC)? 4 : 8)) / 2; + + AVCRawBinWriteInt32(psFile, nRecSize); + + if (nPrecision == AVC_SINGLE_PREC) + { + AVCRawBinWriteFloat(psFile, (float)psCnt->sCoord.x); + AVCRawBinWriteFloat(psFile, (float)psCnt->sCoord.y); + } + else + { + AVCRawBinWriteDouble(psFile, psCnt->sCoord.x); + AVCRawBinWriteDouble(psFile, psCnt->sCoord.y); + } + + AVCRawBinWriteInt32(psFile, psCnt->numLabels); + + for(i=0; inumLabels; i++) + { + AVCRawBinWriteInt32(psFile, psCnt->panLabelIds[i]); + } + + /*----------------------------------------------------------------- + * Write index entry (cnx.adf) + *----------------------------------------------------------------*/ + if (psIndexFile) + { + _AVCBinWriteIndexEntry(psIndexFile, nCurPos, nRecSize); + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * AVCBinWriteCnt() + * + * Write a CNT (Polygon Centroid) structure to the file. + * + * The contents of the psCnt structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + * + * If a problem happens, then CPLError() will be called by the lower-level + * functions and CPLGetLastErrorNo() can be used to find out what happened. + **********************************************************************/ +int AVCBinWriteCnt(AVCBinFile *psFile, AVCCnt *psCnt) +{ + if (psFile->eFileType != AVCFileCNT) + return -1; + + return _AVCBinWriteCnt(psFile->psRawBinFile, psCnt, + psFile->nPrecision, psFile->psIndexFile); +} + +/*===================================================================== + * LAB + *====================================================================*/ + +/********************************************************************** + * _AVCBinWriteLab() + * + * (This function is for internal library use... external calls should + * go to AVCBinWriteLab() instead) + * + * Write a LAB (Centroid Label) structure to the file. + * + * The contents of the psLab structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWriteLab(AVCRawBinFile *psFile, AVCLab *psLab, + int nPrecision) +{ + + AVCRawBinWriteInt32(psFile, psLab->nValue); + if (CPLGetLastErrorNo() != 0) + return -1; + + AVCRawBinWriteInt32(psFile, psLab->nPolyId); + + if (nPrecision == AVC_SINGLE_PREC) + { + AVCRawBinWriteFloat(psFile, (float)psLab->sCoord1.x); + AVCRawBinWriteFloat(psFile, (float)psLab->sCoord1.y); + AVCRawBinWriteFloat(psFile, (float)psLab->sCoord2.x); + AVCRawBinWriteFloat(psFile, (float)psLab->sCoord2.y); + AVCRawBinWriteFloat(psFile, (float)psLab->sCoord3.x); + AVCRawBinWriteFloat(psFile, (float)psLab->sCoord3.y); + } + else + { + AVCRawBinWriteDouble(psFile, psLab->sCoord1.x); + AVCRawBinWriteDouble(psFile, psLab->sCoord1.y); + AVCRawBinWriteDouble(psFile, psLab->sCoord2.x); + AVCRawBinWriteDouble(psFile, psLab->sCoord2.y); + AVCRawBinWriteDouble(psFile, psLab->sCoord3.x); + AVCRawBinWriteDouble(psFile, psLab->sCoord3.y); + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * AVCBinWriteLab() + * + * Write a LAB (Centroid Label) structure to the file. + * + * The contents of the psLab structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + * + * If a problem happens, then CPLError() will be called by the lower-level + * functions and CPLGetLastErrorNo() can be used to find out what happened. + **********************************************************************/ +int AVCBinWriteLab(AVCBinFile *psFile, AVCLab *psLab) +{ + if (psFile->eFileType != AVCFileLAB) + return -1; + + return _AVCBinWriteLab(psFile->psRawBinFile, psLab, + psFile->nPrecision); +} + +/*===================================================================== + * TOL + *====================================================================*/ + +/********************************************************************** + * _AVCBinWriteTol() + * + * (This function is for internal library use... external calls should + * go to AVCBinWriteTol() instead) + * + * Write a TOL (tolerance) structure to the file. + * + * The contents of the psTol structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWriteTol(AVCRawBinFile *psFile, AVCTol *psTol, + int nPrecision) +{ + + AVCRawBinWriteInt32(psFile, psTol->nIndex); + if (CPLGetLastErrorNo() != 0) + return -1; + + AVCRawBinWriteInt32(psFile, psTol->nFlag); + + if (nPrecision == AVC_SINGLE_PREC) + { + AVCRawBinWriteFloat(psFile, (float)psTol->dValue); + } + else + { + AVCRawBinWriteDouble(psFile, psTol->dValue); + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * AVCBinWriteTol() + * + * Write a TOL (tolerance) structure to the file. + * + * The contents of the psTol structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + * + * If a problem happens, then CPLError() will be called by the lower-level + * functions and CPLGetLastErrorNo() can be used to find out what happened. + **********************************************************************/ +int AVCBinWriteTol(AVCBinFile *psFile, AVCTol *psTol) +{ + if (psFile->eFileType != AVCFileTOL) + return -1; + + return _AVCBinWriteTol(psFile->psRawBinFile, psTol, + psFile->nPrecision); +} + +/*===================================================================== + * PRJ + *====================================================================*/ + +/********************************************************************** + * AVCBinWritePrj() + * + * Write a PRJ (Projection info) to the file. + * + * Since a PRJ file is a simple text file and there is only ONE projection + * info per prj.adf file, this function behaves differently from the + * other ones... all the job is done here, including creating and closing + * the output file. + * + * The contents of the papszPrj is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + * + * If a problem happens, then CPLError() will be called by the lower-level + * functions and CPLGetLastErrorNo() can be used to find out what happened. + **********************************************************************/ +int AVCBinWritePrj(AVCBinFile *psFile, char **papszPrj) +{ + if (psFile->eFileType != AVCFilePRJ) + return -1; + + CSLSave(papszPrj, psFile->pszFilename); + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/*===================================================================== + * TXT/TX6/TX7 + *====================================================================*/ + +/********************************************************************** + * _AVCBinWriteTxt() + * + * (This function is for internal library use... external calls should + * go to AVCBinWriteTxt() instead) + * + * Write a TXT/TX6/TX7 (Annotation) structure to the file. + * + * The contents of the psTxt structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWriteTxt(AVCRawBinFile *psFile, AVCTxt *psTxt, + int nPrecision, AVCRawBinFile *psIndexFile) +{ + int i, nRecSize, nCurPos, nStrLen, numVertices; + + nCurPos = psFile->nCurPos/2; /* Value in 2 byte words */ + + AVCRawBinWriteInt32(psFile, psTxt->nTxtId); + if (CPLGetLastErrorNo() != 0) + return -1; + + /*----------------------------------------------------------------- + * Record size is expressed in 2 byte words, and does not count the + * first 8 bytes of the TXT entry. + *----------------------------------------------------------------*/ + /* String uses a multiple of 4 bytes of storage */ + if (psTxt->pszText) + nStrLen = ((strlen((char*)psTxt->pszText) + 3)/4)*4; + else + nStrLen = 0; + + numVertices = ABS(psTxt->numVerticesLine) + ABS(psTxt->numVerticesArrow); + nRecSize = (112 + 8 + nStrLen + + (numVertices*2+3)* ((nPrecision == AVC_SINGLE_PREC)?4:8)) / 2; + + AVCRawBinWriteInt32(psFile, nRecSize); + + AVCRawBinWriteInt32(psFile, psTxt->nUserId ); + AVCRawBinWriteInt32(psFile, psTxt->nLevel ); + AVCRawBinWriteFloat(psFile, psTxt->f_1e2 ); + AVCRawBinWriteInt32(psFile, psTxt->nSymbol ); + AVCRawBinWriteInt32(psFile, psTxt->numVerticesLine ); + AVCRawBinWriteInt32(psFile, psTxt->n28 ); + AVCRawBinWriteInt32(psFile, psTxt->numChars ); + AVCRawBinWriteInt32(psFile, psTxt->numVerticesArrow ); + + for(i=0; i<20; i++) + { + AVCRawBinWriteInt16(psFile, psTxt->anJust1[i] ); + } + for(i=0; i<20; i++) + { + AVCRawBinWriteInt16(psFile, psTxt->anJust2[i] ); + } + + if (nPrecision == AVC_SINGLE_PREC) + { + AVCRawBinWriteFloat(psFile, (float)psTxt->dHeight); + AVCRawBinWriteFloat(psFile, (float)psTxt->dV2); + AVCRawBinWriteFloat(psFile, (float)psTxt->dV3); + } + else + { + AVCRawBinWriteDouble(psFile, psTxt->dHeight); + AVCRawBinWriteDouble(psFile, psTxt->dV2); + AVCRawBinWriteDouble(psFile, psTxt->dV3); + } + + if (nStrLen > 0) + AVCRawBinWritePaddedString(psFile, nStrLen, psTxt->pszText); + + if (nPrecision == AVC_SINGLE_PREC) + { + for(i=0; ipasVertices[i].x); + AVCRawBinWriteFloat(psFile, (float)psTxt->pasVertices[i].y); + } + } + else + { + for(i=0; ipasVertices[i].x); + AVCRawBinWriteDouble(psFile, psTxt->pasVertices[i].y); + } + } + + AVCRawBinWriteZeros(psFile, 8); + + /*----------------------------------------------------------------- + * Write index entry (cnx.adf) + *----------------------------------------------------------------*/ + if (psIndexFile) + { + _AVCBinWriteIndexEntry(psIndexFile, nCurPos, nRecSize); + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * _AVCBinWritePCCoverageTxt() + * + * (This function is for internal library use... external calls should + * go to AVCBinWriteTxt() instead) + * + * Write a TXT (Annotation) structure to a AVCCoverPC file. + * + * The contents of the psTxt structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * This function assumes that PC Coverages are always single precision. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWritePCCoverageTxt(AVCRawBinFile *psFile, AVCTxt *psTxt, + CPL_UNUSED int nPrecision, + AVCRawBinFile *psIndexFile) +{ + int i, nRecSize, nCurPos, nStrLen, numVertices; + + CPLAssert(nPrecision == AVC_SINGLE_PREC); + + nCurPos = psFile->nCurPos/2; /* Value in 2 byte words */ + + AVCRawBinWriteInt32(psFile, psTxt->nTxtId); + if (CPLGetLastErrorNo() != 0) + return -1; + + /*----------------------------------------------------------------- + * Record size is expressed in 2 byte words, and does not count the + * first 8 bytes of the TXT entry. + *----------------------------------------------------------------*/ + /* String uses a multiple of 4 bytes of storage, + * And if text is already a multiple of 4 bytes then we include 4 extra + * spaces anyways (was probably a bug in the software!). + */ + if (psTxt->pszText) + nStrLen = ((strlen((char*)psTxt->pszText) + 4)/4)*4; + else + nStrLen = 4; + + nRecSize = (92 - 8 + nStrLen) / 2; + + AVCRawBinWriteInt32(psFile, nRecSize); + AVCRawBinWriteInt32(psFile, psTxt->nLevel ); + + /*----------------------------------------------------------------- + * Number of vertices to write: + * Note that because of the way V7 binary TXT files work, the rest of the + * lib expects to receive duplicate coords for the first vertex, so + * we will also receive an additional vertex for that but we won't write + * it. We also ignore the arrow vertices if there is any. + *----------------------------------------------------------------*/ + numVertices = ABS(psTxt->numVerticesLine) -1; + numVertices = MIN(4, numVertices); /* Maximum of 4 points */ + + AVCRawBinWriteInt32(psFile, numVertices ); + + for(i=0; ipasVertices[i+1].x); + AVCRawBinWriteFloat(psFile, (float)psTxt->pasVertices[i+1].y); + } + + AVCRawBinWriteZeros(psFile, (4-numVertices)*4*2 + 28); + + AVCRawBinWriteFloat(psFile, (float)psTxt->dHeight); + AVCRawBinWriteFloat(psFile, psTxt->f_1e2 ); + AVCRawBinWriteInt32(psFile, psTxt->nSymbol ); + AVCRawBinWriteInt32(psFile, psTxt->numChars ); + + if (nStrLen > 0) + AVCRawBinWritePaddedString(psFile, nStrLen, psTxt->pszText); + + /*----------------------------------------------------------------- + * Write index entry (cnx.adf) + *----------------------------------------------------------------*/ + if (psIndexFile) + { + _AVCBinWriteIndexEntry(psIndexFile, nCurPos, nRecSize); + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * AVCBinWriteTxt() + * + * Write a TXT/TX6/TX7 (Annotation) structure to the file. + * + * The contents of the psTxt structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + * + * If a problem happens, then CPLError() will be called by the lower-level + * functions and CPLGetLastErrorNo() can be used to find out what happened. + **********************************************************************/ +int AVCBinWriteTxt(AVCBinFile *psFile, AVCTxt *psTxt) +{ + if (psFile->eFileType != AVCFileTXT && psFile->eFileType != AVCFileTX6) + return -1; + + /* AVCCoverPC and AVCCoverWeird have a different TXT format than AVCCoverV7 + */ + if (psFile->eCoverType == AVCCoverPC || + psFile->eCoverType == AVCCoverWeird) + { + return _AVCBinWritePCCoverageTxt(psFile->psRawBinFile, psTxt, + psFile->nPrecision, + psFile->psIndexFile); + } + else + { + return _AVCBinWriteTxt(psFile->psRawBinFile, psTxt, + psFile->nPrecision, psFile->psIndexFile); + } +} + +/*===================================================================== + * RXP + *====================================================================*/ + +/********************************************************************** + * _AVCBinWriteRxp() + * + * (This function is for internal library use... external calls should + * go to AVCBinWriteRxp() instead) + * + * Write a RXP (Region something...) structure to the file. + * + * The contents of the psRxp structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWriteRxp(AVCRawBinFile *psFile, + AVCRxp *psRxp, + CPL_UNUSED int nPrecision) +{ + + AVCRawBinWriteInt32(psFile, psRxp->n1); + if (CPLGetLastErrorNo() != 0) + return -1; + + AVCRawBinWriteInt32(psFile, psRxp->n2); + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * AVCBinWriteRxp() + * + * Write a RXP (Region something...) structure to the file. + * + * The contents of the psRxp structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + * + * If a problem happens, then CPLError() will be called by the lower-level + * functions and CPLGetLastErrorNo() can be used to find out what happened. + **********************************************************************/ +int AVCBinWriteRxp(AVCBinFile *psFile, AVCRxp *psRxp) +{ + if (psFile->eFileType != AVCFileRXP) + return -1; + + return _AVCBinWriteRxp(psFile->psRawBinFile, psRxp, + psFile->nPrecision); +} + + +/*===================================================================== + * TABLES + *====================================================================*/ + +/********************************************************************** + * _AVCBinWriteArcDir() + * + * (This function is for internal library use... external calls should + * go to AVCBinWriteCreateTable() instead) + * + * Write an ARC.DIR entry at the current position in file. + * + * The contents of the psTableDef structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWriteArcDir(AVCRawBinFile *psFile, AVCTableDef *psTableDef) +{ + /* STRING values MUST be padded with spaces. + */ + AVCRawBinWritePaddedString(psFile, 32, (GByte*)psTableDef->szTableName); + if (CPLGetLastErrorNo() != 0) + return -1; + + AVCRawBinWritePaddedString(psFile, 8, (GByte*)psTableDef->szInfoFile); + + AVCRawBinWriteInt16(psFile, psTableDef->numFields); + + /* Record size must be a multiple of 2 bytes */ + AVCRawBinWriteInt16(psFile, (GInt16)(((psTableDef->nRecSize+1)/2)*2)); + + /* ??? Unknown values ??? */ + AVCRawBinWritePaddedString(psFile, 16, (GByte*)" "); + AVCRawBinWriteInt16(psFile, 132); + AVCRawBinWriteInt16(psFile, 0); + + AVCRawBinWriteInt32(psFile, psTableDef->numRecords); + + AVCRawBinWriteZeros(psFile, 10); + + AVCRawBinWritePaddedString(psFile, 2, (GByte*)psTableDef->szExternal); + + AVCRawBinWriteZeros(psFile, 238); + AVCRawBinWritePaddedString(psFile, 8, (GByte*)" "); + AVCRawBinWriteZeros(psFile, 54); + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * _AVCBinWriteArcNit() + * + * (This function is for internal library use... external calls should + * go to AVCBinWriteCreateTable() instead) + * + * Write an ARC####.NIT entry at the current position in file. + * + * The contents of the psTableDef structure is assumed to be valid... this + * function performs no validation on the consistency of what it is + * given as input. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWriteArcNit(AVCRawBinFile *psFile, AVCFieldInfo *psField) +{ + /* STRING values MUST be padded with spaces. + */ + AVCRawBinWritePaddedString(psFile, 16, (GByte*)psField->szName); + if (CPLGetLastErrorNo() != 0) + return -1; + + AVCRawBinWriteInt16(psFile, psField->nSize); + AVCRawBinWriteInt16(psFile, psField->v2); + AVCRawBinWriteInt16(psFile, psField->nOffset); + AVCRawBinWriteInt16(psFile, psField->v4); + AVCRawBinWriteInt16(psFile, psField->v5); + AVCRawBinWriteInt16(psFile, psField->nFmtWidth); + AVCRawBinWriteInt16(psFile, psField->nFmtPrec); + AVCRawBinWriteInt16(psFile, psField->nType1); + AVCRawBinWriteInt16(psFile, psField->nType2); + AVCRawBinWriteInt16(psFile, psField->v10); + AVCRawBinWriteInt16(psFile, psField->v11); + AVCRawBinWriteInt16(psFile, psField->v12); + AVCRawBinWriteInt16(psFile, psField->v13); + + AVCRawBinWritePaddedString(psFile, 16, (GByte*)psField->szAltName); + + AVCRawBinWriteZeros(psFile, 56); + + AVCRawBinWriteInt16(psFile, psField->nIndex); + + AVCRawBinWriteZeros(psFile, 28); + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * _AVCBinWriteCreateArcDirEntry() + * + * Add an entry in the ARC.DIR for the table defined in psSrcTableDef. + * + * If an entry with the same table name already exists then this entry + * will be reused and overwritten. + * + * Note: there could be a problem if 2 processes try to add an entry + * at the exact same time... does Arc/Info do any locking on that file??? + * + * Returns an integer value corresponding to the new table index (ARC####) + * or -1 if something failed. + **********************************************************************/ + +/* Prototype for _AVCBinReadNextArcDir() from avc_bin.c + */ +int _AVCBinReadNextArcDir(AVCRawBinFile *psFile, AVCTableDef *psArcDir); + +int _AVCBinWriteCreateArcDirEntry(const char *pszArcDirFile, + AVCTableDef *psTableDef, + AVCDBCSInfo *psDBCSInfo) +{ + int iEntry, numDirEntries=0, nTableIndex = 0; + VSIStatBuf sStatBuf; + AVCRawBinFile *hRawBinFile; + GBool bFound; + AVCTableDef sEntry; + + /*----------------------------------------------------------------- + * Open and Scan the ARC.DIR to establish the table index (ARC####) + *----------------------------------------------------------------*/ +#ifdef _WIN32 + /*----------------------------------------------------------------- + * Note, DM, 20010507 - We used to use VSIFStat() to establish the + * size of arc.dir, but when working on a WinNT4 networked drive, the + * stat() information was not always right, and we sometimes ended + * up overwriting arc.dir entries. The solution: open and scan arc.dir + * until EOF to establish its size. + * That trick also seems to fix another network buffer problem: when + * writing a coverage in a new empty dir (with no info dir yet), we + * would get an error in fwrite() while writing the 3rd arc.dir + * entry of the coverage. That second problem could also have been + * fixed by forcing a VSIFSeek() before the first fwrite()... we've + * added it below. + *----------------------------------------------------------------*/ + FILE *fp; + if ((fp = VSIFOpen(pszArcDirFile, "r")) != NULL) + { + char buf[380]; + while (!VSIFEof(fp)) + { + if (VSIFRead(buf, 380, 1, fp) == 1) + numDirEntries++; + } + VSIFClose(fp); + hRawBinFile = AVCRawBinOpen(pszArcDirFile, "r+", + AVC_COVER_BYTE_ORDER(AVCCoverV7), + psDBCSInfo); + } + else +#endif + /* On Unix we can still use fstat() */ + if ( VSIStat(pszArcDirFile, &sStatBuf) != -1 ) + { + numDirEntries = sStatBuf.st_size/380; + hRawBinFile = AVCRawBinOpen(pszArcDirFile, "r+", + AVC_COVER_BYTE_ORDER(AVCCoverV7), + psDBCSInfo); + } + else + { + numDirEntries = 0; + hRawBinFile = AVCRawBinOpen(pszArcDirFile, "w", + AVC_COVER_BYTE_ORDER(AVCCoverV7), + psDBCSInfo); + } + + if (hRawBinFile == NULL) + { + /* Failed to open file... just return -1 since an error message + * has already been issued by AVCRawBinOpen() + */ + return -1; + } + + /* Init nTableIndex at -1 so that first table created should have + * index 0 + */ + nTableIndex = -1; + iEntry = 0; + bFound = FALSE; + while(!bFound && iEntryszTableName, sEntry.szTableName, + strlen(psTableDef->szTableName))) + { + bFound = TRUE; + break; + } + iEntry++; + } + + /*----------------------------------------------------------------- + * Reposition the file pointer and write the entry. + * + * We use VSIFSeek() directly since the AVCRawBin*() functions do + * not support random access yet... it is OK to do so here since the + * ARC.DIR does not have a header and we will close it right away. + *----------------------------------------------------------------*/ + if (bFound) + VSIFSeek(hRawBinFile->fp, iEntry*380, SEEK_SET); + else + { + /* Not found... Use the next logical table index */ + nTableIndex++; + + /* We're already at EOF so we shouldn't need to fseek here, but + * ANSI-C requires that a file positioning function be called + * between read and writes... this had never been a problem before + * on any system except with NT4 network drives. + */ + VSIFSeek(hRawBinFile->fp, numDirEntries*380, SEEK_SET); + } + + sprintf(psTableDef->szInfoFile, "ARC%4.4d", nTableIndex); + _AVCBinWriteArcDir(hRawBinFile, psTableDef); + + AVCRawBinClose(hRawBinFile); + + return nTableIndex; +} + + +/********************************************************************** + * AVCBinWriteCreateTable() + * + * Open an INFO table for writing: + * + * - Add an entry for the new table in the info/arc.dir + * - Write the attributes definitions to the info/arc####.nit + * - Create the data file, ready to write data records to it + * - If necessary, set the arc####.dat to point to the location of + * the data file. + * + * pszInfoPath is the info directory path, terminated by a '/' or a '\\' + * It is assumed that this 'info' directory already exists and is writable. + * + * psTableDef should contain a valid table definition for this coverage. + * This function will create and maintain its own copy of the structure. + * + * The name of the file to create and its location will be based on the + * table name and the external ("XX") flag values in the psTableDef + * structure, so you have to make sure that these values are valid. + * + * If a table with the same name is already present in the arc.dir, then + * the same arc.dir entry will be used and overwritten. This happens + * when a coverage directory is deleted by hand. The behavior implemented + * here correspond to Arc/Info's behavior. + * + * For internal tables, the data file goes directly in the info directory, so + * there is not much to worry about. + * + * For external tables, the table name is composed of 3 parts: + * + * . + * + * - : + * The first part of the table name (before the '.') is the + * name of the coverage to which the table belongs, and the data file + * will be created in this coverage's directory... so it is assumed that + * the directory "../" already exists and is writable. + * - : + * The coverage name is followed by a 3 chars extension that will be + * used to build the name of the external table to create. + * - : + * For some table types, the extension is followed by a subclass name. + * + * When is present, then the data file name will be: + * "..//." + * + * e.g. The table named "TEST.PATCOUNTY" would be stored in the file + * "../test/county.pat" (this path is realtive to the info directory) + * + * When the is not present, then the name of the data file + * will be the "..//.adf" + * + * e.g. The table named "TEST.PAT" would be stored in the file + * "../test/pat.adf" + * + * Of course, it would be too easy if there were no exceptions to these + * rules! Single precision ".TIC" and ".BND" follow the above rules and + * will be named "tic.adf" and "bnd.adf" but in double precision coverages, + * they will be named "dbltic.adf" and "dblbnd.adf". + * + * Returns a valid AVCBinFile handle, or NULL if the table could + * not be created. + * + * AVCBinClose() will eventually have to be called to release the + * resources used by the AVCBinFile structure. + **********************************************************************/ +AVCBinFile *AVCBinWriteCreateTable(const char *pszInfoPath, + const char *pszCoverName, + AVCTableDef *psSrcTableDef, + AVCCoverType eCoverType, + int nPrecision, AVCDBCSInfo *psDBCSInfo) +{ + AVCBinFile *psFile; + AVCRawBinFile *hRawBinFile; + AVCTableDef *psTableDef = NULL; + char *pszFname = NULL, szInfoFile[8]=""; + int i, nTableIndex = 0; + + if (eCoverType == AVCCoverPC || eCoverType == AVCCoverPC2) + return _AVCBinWriteCreateDBFTable(pszInfoPath, pszCoverName, + psSrcTableDef, eCoverType, + nPrecision, psDBCSInfo); + + /*----------------------------------------------------------------- + * Make sure precision value is valid (AVC_DEFAULT_PREC is NOT valid) + *----------------------------------------------------------------*/ + if (nPrecision!=AVC_SINGLE_PREC && nPrecision!=AVC_DOUBLE_PREC) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "AVCBinWriteCreateTable(): Invalid precision parameter " + "(value must be AVC_SINGLE_PREC or AVC_DOUBLE_PREC)"); + return NULL; + } + + /* Alloc a buffer big enough for the longest possible filename... + */ + pszFname = (char*)CPLMalloc((strlen(pszInfoPath)+81)*sizeof(char)); + + + /*----------------------------------------------------------------- + * Alloc and init the AVCBinFile struct. + *----------------------------------------------------------------*/ + psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile)); + + psFile->eFileType = AVCFileTABLE; + /* Precision is not important for tables */ + psFile->nPrecision = nPrecision; + psFile->eCoverType = eCoverType; + + psFile->hdr.psTableDef = psTableDef = _AVCDupTableDef(psSrcTableDef); + + /*----------------------------------------------------------------- + * Add a record for this table in the "arc.dir" + * Note: there could be a problem if 2 processes try to add an entry + * at the exact same time... does Arc/Info do any locking on that file??? + *----------------------------------------------------------------*/ + sprintf(pszFname, "%sarc.dir", pszInfoPath); + + nTableIndex = _AVCBinWriteCreateArcDirEntry(pszFname, psTableDef, + psDBCSInfo); + + if (nTableIndex < 0) + { + /* Failed to add arc.dir entry... just return NULL since an error + * message has already been issued by _AVCBinWriteCreateArcDirEntry() + */ + _AVCDestroyTableDef(psTableDef); + CPLFree(psFile); + CPLFree(pszFname); + return NULL; + } + + sprintf(szInfoFile, "arc%4.4d", nTableIndex); + + /*----------------------------------------------------------------- + * Create the "arc####.nit" with the attribute definitions. + *----------------------------------------------------------------*/ + sprintf(pszFname, "%s%s.nit", pszInfoPath, szInfoFile); + + hRawBinFile = AVCRawBinOpen(pszFname, "w", + AVC_COVER_BYTE_ORDER(AVCCoverV7), + psDBCSInfo); + + if (hRawBinFile == NULL) + { + /* Failed to open file... just return NULL since an error message + * has already been issued by AVCRawBinOpen() + */ + _AVCDestroyTableDef(psTableDef); + CPLFree(psFile); + CPLFree(pszFname); + return NULL; + } + + for(i=0; inumFields; i++) + { + _AVCBinWriteArcNit(hRawBinFile, &(psTableDef->pasFieldDef[i])); + } + + AVCRawBinClose(hRawBinFile); + hRawBinFile = NULL; + + /*----------------------------------------------------------------- + * The location of the data file depends on the external flag. + *----------------------------------------------------------------*/ + if (EQUAL(psTableDef->szExternal, " ")) + { + /*------------------------------------------------------------- + * Internal table: data goes directly in "arc####.dat" + *------------------------------------------------------------*/ + psTableDef->szDataFile[0] = '\0'; + sprintf(pszFname, "%s%s.dat", pszInfoPath, szInfoFile); + psFile->pszFilename = CPLStrdup(pszFname); + } + else + { + /*------------------------------------------------------------- + * External table: data stored in the coverage directory, and + * the path to the data file is written to "arc####.dat" + *... start by extracting the info to build the data file name... + *------------------------------------------------------------*/ + char szCoverName[40]="", szExt[4]="", szSubclass[40]="", *pszPtr; + int nLen; + FILE *fpOut; + + nLen = strlen(psTableDef->szTableName); + CPLAssert(nLen <= 32); + if (nLen > 32) return NULL; + pszPtr = psTableDef->szTableName; + + for(i=0; *pszPtr!='\0' && *pszPtr!='.' && *pszPtr!=' '; i++, pszPtr++) + { + szCoverName[i] = tolower(*pszPtr); + } + szCoverName[i] = '\0'; + + if (*pszPtr == '.') + pszPtr++; + + for(i=0; i<3 && *pszPtr!='\0' && *pszPtr!=' '; i++, pszPtr++) + { + szExt[i] = tolower(*pszPtr); + } + szExt[i] = '\0'; + + for(i=0; *pszPtr!='\0' && *pszPtr!=' '; i++, pszPtr++) + { + szSubclass[i] = tolower(*pszPtr); + } + szSubclass[i] = '\0'; + + /*------------------------------------------------------------- + * ... and build the data file name based on what we extracted + *------------------------------------------------------------*/ + if (strlen(szSubclass) == 0) + { + if (nPrecision == AVC_DOUBLE_PREC && + (EQUAL(szExt, "TIC") || EQUAL(szExt, "BND")) ) + { + /* "..//dbl.adf" */ + sprintf(psTableDef->szDataFile, + "../%s/dbl%s.adf", szCoverName, szExt); + } + else + { + /* "..//.adf" */ + sprintf(psTableDef->szDataFile, + "../%s/%s.adf", szCoverName, szExt); + } + } + else + { + /* "..//." */ + sprintf(psTableDef->szDataFile, + "../%s/%s.%s", szCoverName, szSubclass, szExt); + } + + /*------------------------------------------------------------- + * Write it to the arc####.dat + * Note that the path that is written in the arc####.dat contains + * '/' as a directory delimiter, even on Windows systems. + *------------------------------------------------------------*/ + sprintf(pszFname, "%s%s.dat", pszInfoPath, szInfoFile); + fpOut = VSIFOpen(pszFname, "wt"); + if (fpOut) + { + VSIFPrintf(fpOut, "%-80.80s", psTableDef->szDataFile); + VSIFClose(fpOut); + } + else + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Failed creating file %s.", pszFname); + CPLFree(pszFname); + _AVCDestroyTableDef(psTableDef); + CPLFree(psFile); + return NULL; + } + + sprintf(pszFname, "%s%s", + pszInfoPath, psTableDef->szDataFile); + psFile->pszFilename = CPLStrdup(pszFname); + +#ifdef WIN32 + /*------------------------------------------------------------- + * On a Windows system, we have to change the '/' to '\\' in the + * data file path. + *------------------------------------------------------------*/ + for(i=0; psFile->pszFilename[i] != '\0'; i++) + if (psFile->pszFilename[i] == '/') + psFile->pszFilename[i] = '\\'; +#endif /* WIN32 */ + + }/* if "XX" */ + + /*----------------------------------------------------------------- + * OK, now we're ready to create the actual data file. + *----------------------------------------------------------------*/ + AVCAdjustCaseSensitiveFilename(psFile->pszFilename); + psFile->psRawBinFile = AVCRawBinOpen(psFile->pszFilename, "w", + AVC_COVER_BYTE_ORDER(AVCCoverV7), + psDBCSInfo); + + if (psFile->psRawBinFile == NULL) + { + /* Failed to open file... just return NULL since an error message + * has already been issued by AVCRawBinOpen() + */ + CPLFree(pszFname); + CPLFree(psFile->pszFilename); + _AVCDestroyTableDef(psTableDef); + CPLFree(psFile); + return NULL; + } + + CPLFree(pszFname); + + return psFile; +} + + +/********************************************************************** + * _AVCBinWriteCreateDBFTable() + * + * Create a table (DBF file) in a PC Coverage and write the attribute defns to + * the file. The file will then be ready to write records to. + * + * In PC Coverages, only the following tables appear to be supported: + * - TEST.AAT -> AAT.DBF + * - TEST.PAT -> PAT.DBF + * - TEST.BND -> BND.DBF + * - TEST.TIC -> TIC.DBF + * + * However, this function will not fail if it is passed a table name not + * supported by PC Arc/Info. + * e.g. TEST.PATCOUNTY would be written as PATCOUNTY.DBF even if PC Arc/Info + * would probably not recognize that name. + * + * Returns a valid AVCBinFile handle, or NULL if the table could + * not be created. + * + * AVCBinClose() will eventually have to be called to release the + * resources used by the AVCBinFile structure. + **********************************************************************/ +AVCBinFile *_AVCBinWriteCreateDBFTable(const char *pszPath, + const char *pszCoverName, + AVCTableDef *psSrcTableDef, + AVCCoverType eCoverType, + int nPrecision, + CPL_UNUSED AVCDBCSInfo *psDBCSInfo) +{ + AVCBinFile *psFile; + AVCTableDef *psTableDef = NULL; + AVCFieldInfo *pasDef; + char *pszDBFBasename, szFieldName[12]; + int i, j, nType; + + /*----------------------------------------------------------------- + * Alloc and init the AVCBinFile struct. + *----------------------------------------------------------------*/ + psFile = (AVCBinFile*)CPLCalloc(1, sizeof(AVCBinFile)); + + psFile->eFileType = AVCFileTABLE; + /* Precision is not important for tables */ + psFile->nPrecision = nPrecision; + psFile->eCoverType = eCoverType; + + psFile->hdr.psTableDef = psTableDef = _AVCDupTableDef(psSrcTableDef); + + /* nCurDBFRecord is used to keep track of the 0-based index of the + * last record we read from the DBF file... this is to emulate + * sequential access which is assumed by the rest of the lib. + * Since the first record (record 0) has not been written yet, then + * we init the index at -1. + */ + psFile->nCurDBFRecord = -1; + + /*----------------------------------------------------------------- + * Establish name of file to create. + *----------------------------------------------------------------*/ + psFile->pszFilename = (char*)CPLCalloc(strlen(psSrcTableDef->szTableName)+ + strlen(pszPath)+10, sizeof(char)); + + if (EQUALN(psSrcTableDef->szTableName, pszCoverName, strlen(pszCoverName)) + && psSrcTableDef->szTableName[strlen(pszCoverName)] == '.') + { + pszDBFBasename = psSrcTableDef->szTableName + strlen(pszCoverName)+1; + } + else + { + pszDBFBasename = psSrcTableDef->szTableName; + } + + strcpy(psFile->pszFilename, pszPath); + + for(i=strlen(psFile->pszFilename); *pszDBFBasename; i++, pszDBFBasename++) + { + psFile->pszFilename[i] = tolower(*pszDBFBasename); + } + + strcat(psFile->pszFilename, ".dbf"); + + /*----------------------------------------------------------------- + * OK, let's try to create the DBF file. + *----------------------------------------------------------------*/ + AVCAdjustCaseSensitiveFilename(psFile->pszFilename); + psFile->hDBFFile = DBFCreate(psFile->pszFilename); + + if (psFile->hDBFFile == NULL) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Failed creating file %s.", psFile->pszFilename); + CPLFree(psFile->pszFilename); + _AVCDestroyTableDef(psTableDef); + CPLFree(psFile); + return NULL; + } + + /*----------------------------------------------------------------- + * Create fields. + *----------------------------------------------------------------*/ + pasDef = psTableDef->pasFieldDef; + for(i=0; inumFields; i++) + { + nType = pasDef[i].nType1*10; + + /*------------------------------------------------------------- + * Special characters '#' and '-' in field names have to be replaced + * with '_'. PC Field names are limited to 10 chars. + *------------------------------------------------------------*/ + strncpy(szFieldName, pasDef[i].szName, 10); + szFieldName[10] = '\0'; + for(j=0; szFieldName[j]; j++) + { + if (szFieldName[j] == '#' || szFieldName[j] == '-') + szFieldName[j] = '_'; + } + + if (nType == AVC_FT_DATE || nType == AVC_FT_CHAR) + { + /*--------------------------------------------------------- + * Values stored as strings + *--------------------------------------------------------*/ + DBFAddField(psFile->hDBFFile, szFieldName, FTString, + pasDef[i].nSize, 0); + } + else if (nType == AVC_FT_FIXINT || nType == AVC_FT_FIXNUM) + { + /*--------------------------------------------------------- + * Numerics (internally stored as strings) + *--------------------------------------------------------*/ + DBFAddField(psFile->hDBFFile, szFieldName, FTDouble, + pasDef[i].nSize, pasDef[i].nFmtPrec); + } + else if (nType == AVC_FT_BININT) + { + /*--------------------------------------------------------- + * Integers (16 and 32 bits) + *--------------------------------------------------------*/ + DBFAddField(psFile->hDBFFile, szFieldName, FTInteger, 11, 0); + } + else if (nType == AVC_FT_BINFLOAT) + { + /*--------------------------------------------------------- + * Single + Double precision floats + * Set them as width=13, prec=6 in the header like PC/Arc does + *--------------------------------------------------------*/ + DBFAddField(psFile->hDBFFile, szFieldName, FTDouble, + 13, 6); + } + else + { + /*--------------------------------------------------------- + * Hummm... unsupported field type... + *--------------------------------------------------------*/ + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported field type: (field=%s, type=%d, size=%d)", + szFieldName, nType, pasDef[i].nSize); + _AVCBinWriteCloseTable(psFile); + return NULL; + } + } + + return psFile; +} + + +/********************************************************************** + * _AVCBinWriteCloseTable() + * + * (This function is for internal library use... external calls should + * go to AVCBinWriteClose() instead) + * + * Close an info table opened for wirting, and release all memory + * (object struct., buffers, etc.) associated with this file. + **********************************************************************/ +static void _AVCBinWriteCloseTable(AVCBinFile *psFile) +{ + if (psFile->eFileType != AVCFileTABLE) + return; + + /*----------------------------------------------------------------- + * Close the data file + *----------------------------------------------------------------*/ + if (psFile->hDBFFile) + { + /*------------------------------------------------------------- + * The case of DBF files is simple! + *------------------------------------------------------------*/ + DBFClose(psFile->hDBFFile); + psFile->hDBFFile = NULL; + } + else if (psFile->psRawBinFile) + { + /*------------------------------------------------------------- + * __TODO__ make sure ARC.DIR entry contains accurate info about the + * number of records written, etc. + *------------------------------------------------------------*/ + AVCRawBinClose(psFile->psRawBinFile); + psFile->psRawBinFile = NULL; + } + + /*----------------------------------------------------------------- + * Release other memory + *----------------------------------------------------------------*/ + _AVCDestroyTableDef(psFile->hdr.psTableDef); + + CPLFree(psFile->pszFilename); + + CPLFree(psFile); +} + + +/********************************************************************** + * _AVCBinWriteTableRec() + * + * (This function is for internal library use... external calls should + * go to AVCBinWriteTableRec() instead) + * + * Write a table data record at the current position in file. + * + * The contents of the pasDef and pasFields structures is assumed to + * be valid... this function performs no validation on the consistency + * of what it is given as input. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int _AVCBinWriteTableRec(AVCRawBinFile *psFile, int nFields, + AVCFieldInfo *pasDef, AVCField *pasFields, + int nRecordSize, const char *pszFname) +{ + int i, nType, nBytesWritten=0; + + if (psFile == NULL) + return -1; + + for(i=0; ieFileType != AVCFileTABLE|| + psFile->hdr.psTableDef->numRecords == 0) + return -1; + + if (psFile->eCoverType == AVCCoverPC || psFile->eCoverType == AVCCoverPC2) + return _AVCBinWriteDBFTableRec(psFile->hDBFFile, + psFile->hdr.psTableDef->numFields, + psFile->hdr.psTableDef->pasFieldDef, + pasFields, + &(psFile->nCurDBFRecord), + psFile->pszFilename); + else + return _AVCBinWriteTableRec(psFile->psRawBinFile, + psFile->hdr.psTableDef->numFields, + psFile->hdr.psTableDef->pasFieldDef, + pasFields, + psFile->hdr.psTableDef->nRecSize, + psFile->pszFilename); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_e00gen.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_e00gen.c new file mode 100644 index 000000000..21b0d7764 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_e00gen.c @@ -0,0 +1,1448 @@ +/********************************************************************** + * $Id: avc_e00gen.c,v 1.18 2008/07/23 20:51:38 dmorissette Exp $ + * + * Name: avc_e00gen.c + * Project: Arc/Info vector coverage (AVC) BIN->E00 conversion library + * Language: ANSI C + * Purpose: Functions to generate ASCII E00 lines form binary structures. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2005, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: avc_e00gen.c,v $ + * Revision 1.18 2008/07/23 20:51:38 dmorissette + * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char + * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495) + * + * Revision 1.17 2006/06/14 15:01:33 daniel + * Remove any embeded '\0' from data line in AVCE00GenTableRec() + * + * Revision 1.16 2005/06/03 03:49:58 daniel + * Update email address, website url, and copyright dates + * + * Revision 1.15 2004/08/19 17:48:20 warmerda + * Avoid uninitialized variable warnings. + * + * Revision 1.14 2001/11/25 21:15:23 daniel + * Added hack (AVC_MAP_TYPE40_TO_DOUBLE) to map type 40 fields bigger than 8 + * digits to double precision as we generate E00 output (bug599) + * + * Revision 1.13 2001/11/19 20:39:48 daniel + * Change to correctly format 0-arc PAL records, so that they have a + * single "filler" arc record + * + * Revision 1.12 2000/09/26 20:21:04 daniel + * Added AVCCoverPC write + * + * Revision 1.11 2000/09/22 19:45:20 daniel + * Switch to MIT-style license + * + * Revision 1.10 2000/02/04 04:54:03 daniel + * Fixed warnings + * + * Revision 1.9 2000/02/03 07:21:02 daniel + * TXT/TX6 with string longer than 80 chars: split string in 80 chars chunks + * + * Revision 1.8 2000/02/02 04:28:00 daniel + * Fixed support of TX6/RXP/RPL coming from "weird" coverages + * + * Revision 1.7 1999/08/23 18:20:49 daniel + * Fixed support for attribute fields type 40 + * + * Revision 1.6 1999/05/17 16:19:39 daniel + * Made sure ACVE00GenTableRec() removes all spaces at the end of a + * table record line (it used to leave one space) + * + * Revision 1.5 1999/05/11 02:08:17 daniel + * Simple changes related to the addition of coverage write support. + * + * Revision 1.4 1999/03/03 02:06:38 daniel + * Properly handle 8 bytes floats inside single precision tables. + * + * Revision 1.3 1999/02/25 17:01:58 daniel + * Added support for 16 bit integers in INFO tables (type=50, size=2) + * + * Revision 1.2 1999/02/25 04:17:51 daniel + * Added TXT, TX6/TX7, RXP and RPL support + some minor changes + * + * Revision 1.1 1999/01/29 16:28:52 daniel + * Initial revision + * + **********************************************************************/ + +#include "avc.h" + +#include /* toupper() */ + +/********************************************************************** + * AVCE00GenInfoAlloc() + * + * Allocate and initialize a new AVCE00GenInfo structure. + * + * The structure will eventually have to be freed with AVCE00GenInfoFree(). + **********************************************************************/ +AVCE00GenInfo *AVCE00GenInfoAlloc(int nCoverPrecision) +{ + AVCE00GenInfo *psInfo; + + psInfo = (AVCE00GenInfo*)CPLCalloc(1,sizeof(AVCE00GenInfo)); + + /* Allocate output buffer. + * 2k should be enough... the biggest thing we'll need to store + * in it will be 1 complete INFO table record. + */ + psInfo->nBufSize = 2048; + psInfo->pszBuf = (char *)CPLMalloc(psInfo->nBufSize*sizeof(char)); + + psInfo->nPrecision = nCoverPrecision; + + return psInfo; +} + +/********************************************************************** + * AVCE00GenInfoFree() + * + * Free any memory associated with a AVCE00GenInfo structure. + **********************************************************************/ +void AVCE00GenInfoFree(AVCE00GenInfo *psInfo) +{ + if (psInfo) + CPLFree(psInfo->pszBuf); + CPLFree(psInfo); +} + + +/********************************************************************** + * AVCE00GenReset() + * + * Reset the fields in the AVCE00GenInfo structure so that further calls + * with bCont = TRUE (ex: AVCE00GenArc(psInfo, TRUE)) would return NULL. + **********************************************************************/ +void AVCE00GenReset(AVCE00GenInfo *psInfo) +{ + /* Reinitialize counters so that further calls with bCont = TRUE, + * like AVCE00GenArc(psInfo, TRUE) would return NULL. + */ + psInfo->iCurItem = psInfo->numItems = 0; +} + +/********************************************************************** + * AVCE00GenStartSection() + * + * Generate the first line of an E00 section. + * + * pszClassName applies only to JABBERWOCKY type of sections. + **********************************************************************/ +const char *AVCE00GenStartSection(AVCE00GenInfo *psInfo, AVCFileType eType, + const char *pszClassName) +{ + char *pszName = "UNK"; + + AVCE00GenReset(psInfo); + + if (eType == AVCFileTX6 || eType == AVCFileRXP || eType == AVCFileRPL) + { + /* TX6/RXP/RPL sections start with the class name (the basename + * of the file) in uppercase. + * ex: The section for "cities.txt" would start with "CITIES" + */ + int i; + for(i=0; pszClassName[i] != '\0'; i++) + { + psInfo->pszBuf[i] = toupper(pszClassName[i]); + } + psInfo->pszBuf[i] = '\0'; + } + else + { + /* In most cases, the section starts with a 3 letters code followed + * by the precision code (2 or 3) + */ + switch(eType) + { + case AVCFileARC: + pszName = "ARC"; + break; + case AVCFilePAL: + pszName = "PAL"; + break; + case AVCFileCNT: + pszName = "CNT"; + break; + case AVCFileLAB: + pszName = "LAB"; + break; + case AVCFileTOL: + pszName = "TOL"; + break; + case AVCFilePRJ: + pszName = "PRJ"; + break; + case AVCFileTXT: + pszName = "TXT"; + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported E00 section type!"); + } + + if (psInfo->nPrecision == AVC_DOUBLE_PREC) + sprintf(psInfo->pszBuf, "%s 3", pszName); + else + sprintf(psInfo->pszBuf, "%s 2", pszName); + } + + return psInfo->pszBuf; +} + +/********************************************************************** + * AVCE00GenEndSection() + * + * Generate the last line(s) of an E00 section. + * + * This function should be called once with bCont=FALSE to get the + * first "end of section" line for the current section, and then call + * with bCont=TRUE to get all the other lines. + * + * The function returns NULL when there are no more lines to generate + * for this "end of section". + **********************************************************************/ +const char *AVCE00GenEndSection(AVCE00GenInfo *psInfo, AVCFileType eType, + GBool bCont) +{ + if (bCont == FALSE) + { + /*------------------------------------------------------------- + * Most section types end with only 1 line. + *------------------------------------------------------------*/ + AVCE00GenReset(psInfo); + psInfo->iCurItem = 0; + + if (eType == AVCFileARC || + eType == AVCFilePAL || + eType == AVCFileRPL || + eType == AVCFileCNT || + eType == AVCFileTOL || + eType == AVCFileTXT || + eType == AVCFileTX6 ) + { + sprintf(psInfo->pszBuf, + " -1 0 0 0 0 0 0"); + } + else if (eType == AVCFileLAB) + { + if (psInfo->nPrecision == AVC_DOUBLE_PREC) + sprintf(psInfo->pszBuf, + " -1 0 0.00000000000000E+00 0.00000000000000E+00"); + else + sprintf(psInfo->pszBuf, + " -1 0 0.0000000E+00 0.0000000E+00"); + } + else if (eType == AVCFilePRJ) + { + sprintf(psInfo->pszBuf, "EOP"); + } + else if (eType == AVCFileRXP ) + { + sprintf(psInfo->pszBuf," -1 0"); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported E00 section type!"); + return NULL; + } + } + else if ( psInfo->iCurItem == 0 && + psInfo->nPrecision == AVC_DOUBLE_PREC && + (eType == AVCFilePAL || eType == AVCFileRPL) ) + { + /*--------------------------------------------------------- + * Return the 2nd line for the end of a PAL or RPL section. + *--------------------------------------------------------*/ + sprintf(psInfo->pszBuf, + " 0.00000000000000E+00 0.00000000000000E+00"); + + psInfo->iCurItem++; + } + else + { + /*----------------------------------------------------- + * All other section types end with only one line, and thus + * we return NULL when bCont==TRUE + *----------------------------------------------------*/ + return NULL; + } + + return psInfo->pszBuf; +} + + +/********************************************************************** + * AVCE00GenObject() + * + * Cover function on top of AVCE00GenArc/Pal/Cnt/Lab() that will + * call the right function according to argument eType. + * + * Since there is no compiler type checking on psObj, you have to + * be very careful to make sure you pass an object of the right type + * when you use this function! + * + * The function returns NULL when there are no more lines to generate + * for this ARC. + **********************************************************************/ +const char *AVCE00GenObject(AVCE00GenInfo *psInfo, + AVCFileType eType, void *psObj, GBool bCont) +{ + const char *pszLine = NULL; + + switch(eType) + { + case AVCFileARC: + pszLine = AVCE00GenArc(psInfo, (AVCArc*)psObj, bCont); + break; + case AVCFilePAL: + case AVCFileRPL: + pszLine = AVCE00GenPal(psInfo, (AVCPal*)psObj, bCont); + break; + case AVCFileCNT: + pszLine = AVCE00GenCnt(psInfo, (AVCCnt*)psObj, bCont); + break; + case AVCFileLAB: + pszLine = AVCE00GenLab(psInfo, (AVCLab*)psObj, bCont); + break; + case AVCFileTOL: + pszLine = AVCE00GenTol(psInfo, (AVCTol*)psObj, bCont); + break; + case AVCFileTXT: + pszLine = AVCE00GenTxt(psInfo, (AVCTxt*)psObj, bCont); + break; + case AVCFileTX6: + pszLine = AVCE00GenTx6(psInfo, (AVCTxt*)psObj, bCont); + break; + case AVCFilePRJ: + pszLine = AVCE00GenPrj(psInfo, (char**)psObj, bCont); + break; + case AVCFileRXP: + pszLine = AVCE00GenRxp(psInfo, (AVCRxp*)psObj, bCont); + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "AVCE00GenObject(): Unsupported file type!"); + } + + return pszLine; +} + + +/*===================================================================== + ARC stuff + =====================================================================*/ + +/********************************************************************** + * AVCE00GenArc() + * + * Generate the next line of an E00 ARC. + * + * This function should be called once with bCont=FALSE to get the + * first E00 line for the current ARC, and then call with bCont=TRUE + * to get all the other lines for this ARC. + * + * The function returns NULL when there are no more lines to generate + * for this ARC. + **********************************************************************/ +const char *AVCE00GenArc(AVCE00GenInfo *psInfo, AVCArc *psArc, GBool bCont) +{ + if (bCont == FALSE) + { + /* Initialize the psInfo structure with info about the + * current ARC. + */ + psInfo->iCurItem = 0; + if (psInfo->nPrecision == AVC_DOUBLE_PREC) + psInfo->numItems = psArc->numVertices; + else + psInfo->numItems = (psArc->numVertices+1)/2; + + /* And return the ARC header line + */ + sprintf(psInfo->pszBuf, "%10d%10d%10d%10d%10d%10d%10d", + psArc->nArcId, psArc->nUserId, + psArc->nFNode, psArc->nTNode, + psArc->nLPoly, psArc->nRPoly, + psArc->numVertices); + } + else if (psInfo->iCurItem < psInfo->numItems) + { + int iVertex; + + /* return the next set of vertices for the ARC. + */ + if (psInfo->nPrecision == AVC_DOUBLE_PREC) + { + iVertex = psInfo->iCurItem; + + psInfo->pszBuf[0] = '\0'; + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileARC, + psArc->pasVertices[iVertex].x); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileARC, + psArc->pasVertices[iVertex].y); + } + else + { + iVertex = psInfo->iCurItem*2; + + psInfo->pszBuf[0] = '\0'; + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileARC, + psArc->pasVertices[iVertex].x); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileARC, + psArc->pasVertices[iVertex].y); + + /* Check because if we have a odd number of vertices then + * the last line contains only one pair of vertices. + */ + if (iVertex+1 < psArc->numVertices) + { + AVCPrintRealValue(psInfo->pszBuf,psInfo->nPrecision,AVCFileARC, + psArc->pasVertices[iVertex+1].x); + AVCPrintRealValue(psInfo->pszBuf,psInfo->nPrecision,AVCFileARC, + psArc->pasVertices[iVertex+1].y); + } + } + psInfo->iCurItem++; + } + else + { + /* No more lines to generate for this ARC. + */ + return NULL; + } + + return psInfo->pszBuf; +} + +/*===================================================================== + PAL stuff + =====================================================================*/ + +/********************************************************************** + * AVCE00GenPal() + * + * Generate the next line of an E00 PAL (Polygon Arc List) entry. + * + * This function should be called once with bCont=FALSE to get the + * first E00 line for the current PAL, and then call with bCont=TRUE + * to get all the other lines for this PAL. + * + * The function returns NULL when there are no more lines to generate + * for this PAL entry. + **********************************************************************/ +const char *AVCE00GenPal(AVCE00GenInfo *psInfo, AVCPal *psPal, GBool bCont) +{ + if (bCont == FALSE) + { + /* Initialize the psInfo structure with info about the + * current PAL. (Number of lines excluding header) + */ + psInfo->numItems = (psPal->numArcs+1)/2; + + + /* And return the PAL header line. + */ + sprintf(psInfo->pszBuf, "%10d", psPal->numArcs); + + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFilePAL, + psPal->sMin.x); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFilePAL, + psPal->sMin.y); + + /* Double precision PAL entries have their header on 2 lines! + */ + if (psInfo->nPrecision == AVC_DOUBLE_PREC) + { + psInfo->iCurItem = -1; /* Means 1 line left in header */ + } + else + { + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFilePAL, + psPal->sMax.x); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFilePAL, + psPal->sMax.y); + psInfo->iCurItem = 0; /* Next thing = first Arc entry */ + } + + } + else if (psInfo->iCurItem == -1) + { + /* Second (and last) header line for double precision coverages + */ + psInfo->pszBuf[0] = '\0'; + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFilePAL, + psPal->sMax.x); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFilePAL, + psPal->sMax.y); + + if ( psInfo->numItems == 0 ) + { + psInfo->iCurItem = -2; /* We have a 0-arc polygon, which needs + an arc list with one "0 0 0" element */ + } + else + { + psInfo->iCurItem = 0; /* Next thing = first Arc entry */ + } + } + else if (psInfo->iCurItem == -2) + { + sprintf(psInfo->pszBuf, "%10d%10d%10d", 0, 0, 0); + psInfo->iCurItem = 0; /* Next thing = first Arc entry */ + } + else if (psInfo->iCurItem < psInfo->numItems) + { + /* Return PAL Arc entries... + */ + int iArc; + + iArc = psInfo->iCurItem*2; + + /* If we have a odd number of arcs then + * the last line contains only one arc entry. + */ + if (iArc+1 < psPal->numArcs) + { + sprintf(psInfo->pszBuf, "%10d%10d%10d%10d%10d%10d", + psPal->pasArcs[iArc].nArcId, + psPal->pasArcs[iArc].nFNode, + psPal->pasArcs[iArc].nAdjPoly, + psPal->pasArcs[iArc+1].nArcId, + psPal->pasArcs[iArc+1].nFNode, + psPal->pasArcs[iArc+1].nAdjPoly); + + } + else + { + sprintf(psInfo->pszBuf, "%10d%10d%10d", + psPal->pasArcs[iArc].nArcId, + psPal->pasArcs[iArc].nFNode, + psPal->pasArcs[iArc].nAdjPoly); + } + psInfo->iCurItem++; + } + else + { + /* No more lines to generate for this PAL. + */ + return NULL; + } + + return psInfo->pszBuf; +} + + +/*===================================================================== + CNT stuff + =====================================================================*/ + +/********************************************************************** + * AVCE00GenCnt() + * + * Generate the next line of an E00 CNT (Polygon Centroid) entry. + * + * This function should be called once with bCont=FALSE to get the + * first E00 line for the current CNT, and then call with bCont=TRUE + * to get all the other lines for this CNT. + * + * The function returns NULL when there are no more lines to generate + * for this CNT entry. + **********************************************************************/ +const char *AVCE00GenCnt(AVCE00GenInfo *psInfo, AVCCnt *psCnt, GBool bCont) +{ + if (bCont == FALSE) + { + /* Initialize the psInfo structure with info about the + * current CNT. + */ + psInfo->iCurItem = 0; + psInfo->numItems = (psCnt->numLabels+7)/8; + + /* And return the CNT header line. + */ + sprintf(psInfo->pszBuf, "%10d", psCnt->numLabels); + + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileCNT, + psCnt->sCoord.x); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileCNT, + psCnt->sCoord.y); + + } + else if (psInfo->iCurItem < psInfo->numItems) + { + /* Return CNT Label Ids, 8 label Ids per line... + */ + int i, nFirstLabel, numLabels; + + nFirstLabel = psInfo->iCurItem * 8; + numLabels = MIN(8, (psCnt->numLabels-nFirstLabel)); + + psInfo->pszBuf[0] = '\0'; + for(i=0; i < numLabels; i++) + { + sprintf(psInfo->pszBuf + strlen(psInfo->pszBuf), "%10d", + psCnt->panLabelIds[nFirstLabel+i] ); + } + + psInfo->iCurItem++; + } + else + { + /* No more lines to generate for this CNT. + */ + return NULL; + } + + return psInfo->pszBuf; +} + + +/*===================================================================== + LAB stuff + =====================================================================*/ + +/********************************************************************** + * AVCE00GenLab() + * + * Generate the next line of an E00 LAB (Label) entry. + * + * This function should be called once with bCont=FALSE to get the + * first E00 line for the current LAB, and then call with bCont=TRUE + * to get all the other lines for this LAB. + * + * The function returns NULL when there are no more lines to generate + * for this LAB entry. + **********************************************************************/ +const char *AVCE00GenLab(AVCE00GenInfo *psInfo, AVCLab *psLab, GBool bCont) +{ + if (bCont == FALSE) + { + /* Initialize the psInfo structure with info about the + * current LAB. (numItems = Number of lines excluding header) + */ + psInfo->iCurItem = 0; + if (psInfo->nPrecision == AVC_DOUBLE_PREC) + psInfo->numItems = 2; + else + psInfo->numItems = 1; + + /* And return the LAB header line. + */ + sprintf(psInfo->pszBuf, "%10d%10d", psLab->nValue, psLab->nPolyId); + + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileLAB, + psLab->sCoord1.x); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileLAB, + psLab->sCoord1.y); + + } + else if (psInfo->iCurItem < psInfo->numItems) + { + /* Return next Label coordinates... + */ + if (psInfo->nPrecision != AVC_DOUBLE_PREC) + { + /* Single precision, all on the same line + */ + psInfo->pszBuf[0] = '\0'; + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileLAB, + psLab->sCoord2.x); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileLAB, + psLab->sCoord2.y); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileLAB, + psLab->sCoord3.x); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileLAB, + psLab->sCoord3.y); + + } + else if (psInfo->iCurItem == 0) + { + /* 2nd line, in a double precision coverage + */ + psInfo->pszBuf[0] = '\0'; + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileLAB, + psLab->sCoord2.x); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileLAB, + psLab->sCoord2.y); + } + else + { + /* 3rd line, in a double precision coverage + */ + psInfo->pszBuf[0] = '\0'; + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileLAB, + psLab->sCoord3.x); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileLAB, + psLab->sCoord3.y); + } + + psInfo->iCurItem++; + } + else + { + /* No more lines to generate for this LAB. + */ + return NULL; + } + + return psInfo->pszBuf; +} + +/*===================================================================== + TOL stuff + =====================================================================*/ + +/********************************************************************** + * AVCE00GenTol() + * + * Generate the next line of an E00 TOL (Tolerance) entry. + * + * This function should be called once with bCont=FALSE to get the + * first E00 line for the current TOL, and then call with bCont=TRUE + * to get all the other lines for this TOL. + * + * The function returns NULL when there are no more lines to generate + * for this TOL entry. + **********************************************************************/ +const char *AVCE00GenTol(AVCE00GenInfo *psInfo, AVCTol *psTol, GBool bCont) +{ + if (bCont == TRUE) + { + /*--------------------------------------------------------- + * TOL entries are only 1 line, we support the bCont flag + * only for compatibility with the other AVCE00Gen*() functions. + *--------------------------------------------------------*/ + return NULL; + } + + sprintf(psInfo->pszBuf, "%10d%10d", psTol->nIndex, psTol->nFlag); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileTOL, + psTol->dValue); + + return psInfo->pszBuf; +} + +/*===================================================================== + PRJ stuff + =====================================================================*/ + +/********************************************************************** + * AVCE00GenPrj() + * + * Generate the next line of an E00 PRJ (Projection) section. + * + * This function should be called once with bCont=FALSE to get the + * first E00 line for the current PRJ, and then call with bCont=TRUE + * to get all the other lines for this PRJ. + * + * The function returns NULL when there are no more lines to generate + * for this PRJ entry. + **********************************************************************/ +const char *AVCE00GenPrj(AVCE00GenInfo *psInfo, char **papszPrj, GBool bCont) +{ + if (bCont == FALSE) + { + /*--------------------------------------------------------- + * Initialize the psInfo structure with info about the + * current PRJ. (numItems = Number of lines to output) + *--------------------------------------------------------*/ + psInfo->iCurItem = 0; + psInfo->numItems = CSLCount(papszPrj) * 2; + } + + if (psInfo->iCurItem < psInfo->numItems) + { + /*--------------------------------------------------------- + * Return the next PRJ section line. Note that every + * second line of the output is only a "~". + *--------------------------------------------------------*/ + + if (psInfo->iCurItem % 2 == 0) + { + /*----------------------------------------------------- + * In theory we should split lines longer than 80 chars on + * several lines, but I won't do it for now since I never + * saw any projection line longer than 80 chars. + *----------------------------------------------------*/ + sprintf(psInfo->pszBuf, "%s", papszPrj[psInfo->iCurItem/2]); + } + else + { + /*----------------------------------------------------- + * Every second line in a PRJ section contains only a "~", + * this is a way to tell that the previous line was complete. + *----------------------------------------------------*/ + sprintf(psInfo->pszBuf, "~"); + } + + psInfo->iCurItem++; + } + else + { + /* No more lines to generate for this PRJ. + */ + return NULL; + } + + return psInfo->pszBuf; +} + + +/*===================================================================== + TXT stuff + =====================================================================*/ + +/********************************************************************** + * AVCE00GenTxt() + * + * Generate the next line of an E00 TXT (Annotation) entry. + * + * This function should be called once with bCont=FALSE to get the + * first E00 line for the current TXT, and then call with bCont=TRUE + * to get all the other lines for this TXT. + * + * The function returns NULL when there are no more lines to generate + * for this TXT entry. + **********************************************************************/ +const char *AVCE00GenTxt(AVCE00GenInfo *psInfo, AVCTxt *psTxt, GBool bCont) +{ + int numFixedLines; + + /* numFixedLines is the number of lines to generate before the line(s) + * with the text string + */ + if (psInfo->nPrecision == AVC_SINGLE_PREC) + numFixedLines = 4; + else + numFixedLines = 6; + + if (bCont == FALSE) + { + /*------------------------------------------------------------- + * Initialize the psInfo structure with info about the + * current TXT. (numItems = Number of lines excluding header) + *------------------------------------------------------------*/ + psInfo->iCurItem = 0; + psInfo->numItems = numFixedLines + ((psTxt->numChars-1)/80 + 1); + + /* And return the TXT header line. + */ + sprintf(psInfo->pszBuf, "%10d%10d%10d%10d%10d", + psTxt->nLevel, psTxt->numVerticesLine - 1, + psTxt->numVerticesArrow, psTxt->nSymbol, psTxt->numChars); + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem < numFixedLines-1) + { + /*------------------------------------------------------------- + * Return next line of coordinates... start by placing the coord. + * values in the order that they should appear, and then generate the + * current line + * (This is a little bit less efficient, but will give much easier + * code to read ;-) + *------------------------------------------------------------*/ + double dXY[15]; + int i, nFirstValue, numValuesPerLine; + for(i=0; i<14; i++) + dXY[i] = 0.0; + + dXY[14] = psTxt->dHeight; + + /* note that the first vertex in the vertices list is never exported + */ + for(i=0; i < 4 && i< (psTxt->numVerticesLine-1); i++) + { + dXY[i] = psTxt->pasVertices[i+1].x; + dXY[i+4] = psTxt->pasVertices[i+1].y; + } + for(i=0; i < 3 && inumVerticesArrow); i++) + { + dXY[i+8] = psTxt->pasVertices[i+psTxt->numVerticesLine].x; + dXY[i+11] = psTxt->pasVertices[i+psTxt->numVerticesLine].y; + } + + /* OK, now that we prepared the coord. values, return the next line + * of coordinates. The only difference between double and single + * precision is the number of coordinates per line. + */ + if (psInfo->nPrecision != AVC_DOUBLE_PREC) + { + /* Single precision + */ + numValuesPerLine = 5; + } + else + { + /* Double precision + */ + numValuesPerLine = 3; + } + + nFirstValue = psInfo->iCurItem*numValuesPerLine; + psInfo->pszBuf[0] = '\0'; + for(i=0; ipszBuf, psInfo->nPrecision, AVCFileTXT, + dXY[nFirstValue+i] ); + } + + psInfo->iCurItem++; + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem == numFixedLines-1) + { + /*------------------------------------------------------------- + * Line with a -1.000E+02 value, ALWAYS SINGLE PRECISION !!! + *------------------------------------------------------------*/ + psInfo->pszBuf[0] = '\0'; + AVCPrintRealValue(psInfo->pszBuf, AVC_SINGLE_PREC, AVCFileTXT, + psTxt->f_1e2 ); + psInfo->iCurItem++; + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem >= numFixedLines ) + { + /*------------------------------------------------------------- + * Last line, contains the text string + * Strings longer than 80 chars have to be in 80 chars chunks + *------------------------------------------------------------*/ + int numLines, iLine; + numLines = (psTxt->numChars-1)/80 + 1; + iLine = numLines - (psInfo->numItems - psInfo->iCurItem); + + if ((int)strlen((char*)psTxt->pszText) > (iLine*80)) + sprintf(psInfo->pszBuf, "%-.80s", psTxt->pszText + (iLine*80) ); + else + psInfo->pszBuf[0] = '\0'; + + psInfo->iCurItem++; + } + else + { + /* No more lines to generate for this TXT. + */ + return NULL; + } + + return psInfo->pszBuf; +} + +/*===================================================================== + TX6 stuff + =====================================================================*/ + +/********************************************************************** + * AVCE00GenTx6() + * + * Generate the next line of an E00 TX6 (Annotation) entry. + * + * This function should be called once with bCont=FALSE to get the + * first E00 line for the current TX6, and then call with bCont=TRUE + * to get all the other lines for this TX6. + * + * Note that E00 files can also contain TX7 sections, they seem identical + * to TX6 sections, except for one value in each entry, and it was + * impossible to find where this value comes from... so we will always + * generate TX6 sections and not bother with TX7. + * + * The function returns NULL when there are no more lines to generate + * for this TX6 entry. + **********************************************************************/ +const char *AVCE00GenTx6(AVCE00GenInfo *psInfo, AVCTxt *psTxt, GBool bCont) +{ + if (bCont == FALSE) + { + /*------------------------------------------------------------- + * Initialize the psInfo structure with info about the + * current TX6. (numItems = Number of lines excluding header) + *------------------------------------------------------------*/ + psInfo->iCurItem = 0; + psInfo->numItems = 8 + psTxt->numVerticesLine + + ABS(psTxt->numVerticesArrow) + + ((psTxt->numChars-1)/80 + 1); + + /* And return the TX6 header line. + */ + sprintf(psInfo->pszBuf, "%10d%10d%10d%10d%10d%10d%10d", + psTxt->nUserId, psTxt->nLevel, psTxt->numVerticesLine, + psTxt->numVerticesArrow, psTxt->nSymbol, psTxt->n28, + psTxt->numChars); + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem < 6) + { + /*------------------------------------------------------------- + * Text Justification stuff... 2 sets of 20 int16 values. + *------------------------------------------------------------*/ + GInt16 *pValue; + + if (psInfo->iCurItem < 3) + pValue = psTxt->anJust2 + psInfo->iCurItem * 7; + else + pValue = psTxt->anJust1 + (psInfo->iCurItem-3) * 7; + + if (psInfo->iCurItem == 2 || psInfo->iCurItem == 5) + { + sprintf(psInfo->pszBuf, "%10d%10d%10d%10d%10d%10d", + pValue[0], pValue[1], pValue[2], + pValue[3], pValue[4], pValue[5]); + } + else + { + sprintf(psInfo->pszBuf, "%10d%10d%10d%10d%10d%10d%10d", + pValue[0], pValue[1], pValue[2], + pValue[3], pValue[4], pValue[5], pValue[6]); + } + + psInfo->iCurItem++; + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem == 6) + { + /*------------------------------------------------------------- + * Line with a -1.000E+02 value, ALWAYS SINGLE PRECISION !!! + *------------------------------------------------------------*/ + psInfo->pszBuf[0] = '\0'; + AVCPrintRealValue(psInfo->pszBuf, AVC_SINGLE_PREC, AVCFileTX6, + psTxt->f_1e2 ); + psInfo->iCurItem++; + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem == 7) + { + /*------------------------------------------------------------- + * Line with 3 values, 1st value is probably text height. + *------------------------------------------------------------*/ + psInfo->pszBuf[0] = '\0'; + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileTX6, + psTxt->dHeight ); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileTX6, + psTxt->dV2 ); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileTX6, + psTxt->dV3 ); + psInfo->iCurItem++; + } + else if (psInfo->iCurItem < psInfo->numItems-((psTxt->numChars-1)/80 + 1)) + { + /*------------------------------------------------------------- + * One line for each pair of X,Y coordinates + *------------------------------------------------------------*/ + psInfo->pszBuf[0] = '\0'; + + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileTX6, + psTxt->pasVertices[ psInfo->iCurItem-8 ].x ); + AVCPrintRealValue(psInfo->pszBuf, psInfo->nPrecision, AVCFileTX6, + psTxt->pasVertices[ psInfo->iCurItem-8 ].y ); + + psInfo->iCurItem++; + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem >= psInfo->numItems-((psTxt->numChars-1)/80 + 1)) + { + /*------------------------------------------------------------- + * Last line, contains the text string + * Strings longer than 80 chars have to be in 80 chars chunks + *------------------------------------------------------------*/ + int numLines, iLine; + numLines = (psTxt->numChars-1)/80 + 1; + iLine = numLines - (psInfo->numItems - psInfo->iCurItem); + + if ((int)strlen((char*)psTxt->pszText) > (iLine*80)) + sprintf(psInfo->pszBuf, "%-.80s", psTxt->pszText + (iLine*80) ); + else + psInfo->pszBuf[0] = '\0'; + + psInfo->iCurItem++; + } + else + { + /* No more lines to generate for this TX6. + */ + return NULL; + } + + return psInfo->pszBuf; +} + + +/*===================================================================== + RXP stuff + =====================================================================*/ + +/********************************************************************** + * AVCE00GenRxp() + * + * Generate the next line of an E00 RXP entry (RXPs are related to regions). + * + * This function should be called once with bCont=FALSE to get the + * first E00 line for the current RXP, and then call with bCont=TRUE + * to get all the other lines for this RXP. + * + * The function returns NULL when there are no more lines to generate + * for this RXP entry. + **********************************************************************/ +const char *AVCE00GenRxp(AVCE00GenInfo *psInfo, AVCRxp *psRxp, GBool bCont) +{ + if (bCont == TRUE) + { + /*--------------------------------------------------------- + * RXP entries are only 1 line, we support the bCont flag + * only for compatibility with the other AVCE00Gen*() functions. + *--------------------------------------------------------*/ + return NULL; + } + + sprintf(psInfo->pszBuf, "%10d%10d", psRxp->n1, psRxp->n2); + + return psInfo->pszBuf; +} + + + +/*===================================================================== + TABLE stuff + =====================================================================*/ + +/********************************************************************** + * AVCE00GenTableHdr() + * + * Generate the next line of an E00 Table header. + * + * This function should be called once with bCont=FALSE to get the + * first E00 line for the current table header, and then call with + * bCont=TRUE to get all the other lines. + * + * The function returns NULL when there are no more lines to generate. + **********************************************************************/ +const char *AVCE00GenTableHdr(AVCE00GenInfo *psInfo, AVCTableDef *psDef, + GBool bCont) +{ + if (bCont == FALSE) + { + int nRecSize; + /* Initialize the psInfo structure with info about the + * current Table Header + */ + psInfo->iCurItem = 0; + psInfo->numItems = psDef->numFields; + + nRecSize = psDef->nRecSize; +#ifdef AVC_MAP_TYPE40_TO_DOUBLE + { + /* Adjust Table record size if we're remapping type 40 fields */ + int i; + for(i=0; inumFields; i++) + { + if (psDef->pasFieldDef[i].nType1*10 == AVC_FT_FIXNUM && + psDef->pasFieldDef[i].nSize > 8) + { + nRecSize -= psDef->pasFieldDef[i].nSize; + nRecSize += 8; + } + } + nRecSize = ((nRecSize+1)/2)*2; + } +#endif + + /* And return the header's header line(!). + */ + sprintf(psInfo->pszBuf, "%-32.32s%s%4d%4d%4d%10d", + psDef->szTableName, + psDef->szExternal, + psDef->numFields, + psDef->numFields, + nRecSize, + psDef->numRecords); + } + else if (psInfo->iCurItem < psInfo->numItems) + { + int nSize, nType, nOffset; + + nSize = psDef->pasFieldDef[psInfo->iCurItem].nSize; + nType = psDef->pasFieldDef[psInfo->iCurItem].nType1 * 10; + nOffset = psDef->pasFieldDef[psInfo->iCurItem].nOffset; + +#ifdef AVC_MAP_TYPE40_TO_DOUBLE + /* Type 40 fields with more than 12 digits written to E00 by Arc/Info + * will lose some digits of precision (and we starts losing them at 8 + * with the way AVC lib writes type 40). This (optional) hack will + * remap type 40 fields with more than 8 digits to double precision + * floats which can carry up to 18 digits of precision. (bug 599) + */ + if (nType == AVC_FT_FIXNUM && nSize > 8) + { + /* Remap to double-precision float */ + nType = AVC_FT_BINFLOAT; + nSize = 8; + } + + /* Adjust field offset if this field is preceded by any type40 fields + * that were remapped. + */ + { + int i; + for(i=0; i < psInfo->iCurItem; i++) + { + if (psDef->pasFieldDef[i].nType1*10 == AVC_FT_FIXNUM && + psDef->pasFieldDef[i].nSize > 8) + { + nOffset -= psDef->pasFieldDef[i].nSize; + nOffset += 8; + } + } + } +#endif + /* Return next Field definition line + */ + sprintf(psInfo->pszBuf, + "%-16.16s%3d%2d%4d%1d%2d%4d%2d%3d%2d%4d%4d%2d%-16.16s%4d-", + psDef->pasFieldDef[psInfo->iCurItem].szName, + nSize, + psDef->pasFieldDef[psInfo->iCurItem].v2, + nOffset, + psDef->pasFieldDef[psInfo->iCurItem].v4, + psDef->pasFieldDef[psInfo->iCurItem].v5, + psDef->pasFieldDef[psInfo->iCurItem].nFmtWidth, + psDef->pasFieldDef[psInfo->iCurItem].nFmtPrec, + nType, + psDef->pasFieldDef[psInfo->iCurItem].v10, + psDef->pasFieldDef[psInfo->iCurItem].v11, + psDef->pasFieldDef[psInfo->iCurItem].v12, + psDef->pasFieldDef[psInfo->iCurItem].v13, + psDef->pasFieldDef[psInfo->iCurItem].szAltName, + psDef->pasFieldDef[psInfo->iCurItem].nIndex ); + + + psInfo->iCurItem++; + } + else + { + /* No more lines to generate. + */ + return NULL; + } + + return psInfo->pszBuf; +} + +/********************************************************************** + * AVCE00GenTableRec() + * + * Generate the next line of an E00 Table Data Record. + * + * This function should be called once with bCont=FALSE to get the + * first E00 line for the current table record, and then call with + * bCont=TRUE to get all the other lines. + * + * The function returns NULL when there are no more lines to generate. + **********************************************************************/ +const char *AVCE00GenTableRec(AVCE00GenInfo *psInfo, int numFields, + AVCFieldInfo *pasDef, AVCField *pasFields, + GBool bCont) +{ + int i, nSize, nType, nLen; + char *pszBuf2; + + if (bCont == FALSE) + { + /*------------------------------------------------------------- + * Initialize the psInfo structure to be ready to process this + * new Table Record + *------------------------------------------------------------*/ + psInfo->iCurItem = 0; +#ifdef AVC_MAP_TYPE40_TO_DOUBLE + psInfo->numItems = _AVCE00ComputeRecSize(numFields, pasDef, TRUE); +#else + psInfo->numItems = _AVCE00ComputeRecSize(numFields, pasDef, FALSE); +#endif + + /*------------------------------------------------------------- + * First, we need to make sure that the output buffer is big + * enough to hold the whole record, plus 81 chars to hold + * the line that we'll return to the caller. + *------------------------------------------------------------*/ + nSize = psInfo->numItems + 1 + 81; + + if (psInfo->nBufSize < nSize) + { + psInfo->pszBuf = (char*)CPLRealloc(psInfo->pszBuf, + nSize*sizeof(char)); + psInfo->nBufSize = nSize; + } + + /*------------------------------------------------------------- + * Generate the whole record now, and we'll return it to the + * caller by chunks of 80 chars. + * The first 80 chars of the buffer will be used to return + * one line at a time, and the rest of the buffer is used to + * hold the whole record. + *------------------------------------------------------------*/ + pszBuf2 = psInfo->pszBuf+81; + + for(i=0; i 8) + { + pszBuf2[0] = '\0'; + /* NOTE: The E00 representation for a binary float is + * defined by its binary size, not by the coverage's + * precision. + */ + nLen = AVCPrintRealValue(pszBuf2, AVC_DOUBLE_PREC, + AVCFileTABLE, + CPLAtof((char*)pasFields[i].pszStr)); + pszBuf2 += nLen; + } +#endif + else if (nType == AVC_FT_FIXNUM) + { + /* TYPE 40 attributes are stored with 1 byte per digit + * in binary format, and as single precision floats in + * E00 tables, even in double precision coverages. + */ + pszBuf2[0] = '\0'; + nLen = AVCPrintRealValue(pszBuf2, AVC_SINGLE_PREC, + AVCFileTABLE, + CPLAtof((char*)pasFields[i].pszStr)); + pszBuf2 += nLen; + } + else if (nType == AVC_FT_BININT && nSize == 4) + { + sprintf(pszBuf2, "%11d", pasFields[i].nInt32); + pszBuf2 += 11; + } + else if (nType == AVC_FT_BININT && nSize == 2) + { + sprintf(pszBuf2, "%6d", pasFields[i].nInt16); + pszBuf2 += 6; + } + else if (nType == AVC_FT_BINFLOAT && nSize == 4) + { + pszBuf2[0] = '\0'; + /* NOTE: The E00 representation for a binary float is + * defined by its binary size, not by the coverage's + * precision. + */ + nLen = AVCPrintRealValue(pszBuf2, AVC_SINGLE_PREC, + AVCFileTABLE, + pasFields[i].fFloat); + pszBuf2 += nLen; + } + else if (nType == AVC_FT_BINFLOAT && nSize == 8) + { + pszBuf2[0] = '\0'; + /* NOTE: The E00 representation for a binary float is + * defined by its binary size, not by the coverage's + * precision. + */ + nLen = AVCPrintRealValue(pszBuf2, AVC_DOUBLE_PREC, + AVCFileTABLE, + pasFields[i].dDouble); + pszBuf2 += nLen; + } + else + { + /*----------------------------------------------------- + * Hummm... unsupported field type... + *----------------------------------------------------*/ + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported field type: (type=%d, size=%d)", + nType, nSize); + return NULL; + } + } + + *pszBuf2 = '\0'; + + /* Make sure that we remove any embedded NUL characters from the + * data line before returning it, otherwise we may be accidentally + * truncating results. + */ + while ( --pszBuf2 >= psInfo->pszBuf+81 ) + { + if ( *pszBuf2 == '\0' ) + { + *pszBuf2 = ' '; + } + } + } + + if (psInfo->iCurItem < psInfo->numItems) + { + /*------------------------------------------------------------- + * Return the next 80 chars chunk. + * The first 80 chars of the buffer is used to return + * one line at a time, and the rest of the buffer (chars 81+) + * is used to hold the whole record. + *------------------------------------------------------------*/ + nLen = psInfo->numItems - psInfo->iCurItem; + + if (nLen > 80) + nLen = 80; + + strncpy(psInfo->pszBuf, psInfo->pszBuf+(81+psInfo->iCurItem), nLen); + psInfo->pszBuf[nLen] = '\0'; + + psInfo->iCurItem += nLen; + + /*------------------------------------------------------------- + * Arc/Info removes spaces at the end of the lines... let's + * remove them as well since it can reduce the E00 file size. + *------------------------------------------------------------*/ + nLen--; + while(nLen >= 0 && psInfo->pszBuf[nLen] == ' ') + { + psInfo->pszBuf[nLen] = '\0'; + nLen--; + } + } + else + { + /* No more lines to generate. + */ + return NULL; + } + + return psInfo->pszBuf; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_e00parse.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_e00parse.c new file mode 100644 index 000000000..4c47d5f0c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_e00parse.c @@ -0,0 +1,2237 @@ +/********************************************************************** + * $Id: avc_e00parse.c,v 1.19 2008/07/23 20:51:38 dmorissette Exp $ + * + * Name: avc_e00parse.c + * Project: Arc/Info vector coverage (AVC) E00->BIN conversion library + * Language: ANSI C + * Purpose: Functions to parse ASCII E00 lines and fill binary structures. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2005, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: avc_e00parse.c,v $ + * Revision 1.19 2008/07/23 20:51:38 dmorissette + * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char + * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495) + * + * Revision 1.18 2006/06/27 18:06:34 dmorissette + * Applied patch for EOP processing from James F. (bug 1497) + * + * Revision 1.17 2006/06/19 14:35:47 dmorissette + * New patch from James F. for E00 read support in OGR (bug 1497) + * + * Revision 1.16 2006/06/16 11:48:11 daniel + * New functions to read E00 files directly as opposed to translating to + * binary coverage. Used in the implementation of E00 read support in OGR. + * Contributed by James E. Flemer. (bug 1497) + * + * Revision 1.15 2006/03/02 22:46:26 daniel + * Accept empty subclass names for TX6/TX7 sections (bug 1261) + * + * Revision 1.14 2005/06/03 03:49:58 daniel + * Update email address, website url, and copyright dates + * + * Revision 1.13 2002/08/27 15:43:02 daniel + * Small typo in type 40 fix (forgot to commit to CVS on 2002-08-05) + * + * Revision 1.12 2002/08/05 20:20:17 daniel + * Fixed parsing type 40 fields to properly detect negative exp. (bug 1272) + * + * Revision 1.11 2001/11/25 21:15:23 daniel + * Added hack (AVC_MAP_TYPE40_TO_DOUBLE) to map type 40 fields bigger than 8 + * digits to double precision as we generate E00 output (bug599) + * + * Revision 1.10 2001/11/25 19:45:32 daniel + * Fixed reading of type 40 when not in exponent format (bug599) + * + * Revision 1.9 2001/07/12 20:59:34 daniel + * Properly handle PAL entries with 0 arcs + * + * Revision 1.8 2000/09/22 19:45:20 daniel + * Switch to MIT-style license + * + * Revision 1.7 2000/03/16 03:48:00 daniel + * Accept 0-length text strings in TX6/TX7 objects + * + * Revision 1.6 2000/02/03 07:21:40 daniel + * TXT/TX6 with string longer than 80 chars: split string in 80 chars chunks + * + * Revision 1.5 1999/12/05 03:40:13 daniel + * Fixed signed/unsigned mismatch compile warning + * + * Revision 1.4 1999/11/23 05:27:58 daniel + * Added AVCE00Str2Int() to extract integer values in E00 lines + * + * Revision 1.3 1999/08/23 18:20:49 daniel + * Fixed support for attribute fields type 40 + * + * Revision 1.2 1999/05/17 16:20:48 daniel + * Added RXP + TXT/TX6/TX7 write support + some simple problems fixed + * + * Revision 1.1 1999/05/11 02:34:46 daniel + * Initial revision + * + **********************************************************************/ + +#include "avc.h" + +#include /* toupper() */ + + +/********************************************************************** + * AVCE00Str2Int() + * + * Convert a portion of a string to an integer value. + * The difference between this function and atoi() is that this version + * takes only the specified number of characters... so it can handle the + * case of 2 numbers that are part of the same string but are not separated + * by a space. + **********************************************************************/ +int AVCE00Str2Int(const char *pszStr, int numChars) +{ + int nValue = 0; + + if (pszStr && numChars >= (int)strlen(pszStr)) + return atoi(pszStr); + else if (pszStr) + { + char cNextDigit; + char *pszTmp; + + /* Get rid of const */ + pszTmp = (char*)pszStr; + + cNextDigit = pszTmp[numChars]; + pszTmp[numChars] = '\0'; + nValue = atoi(pszTmp); + pszTmp[numChars] = cNextDigit; + } + + return nValue; +} + +/********************************************************************** + * AVCE00ParseInfoAlloc() + * + * Allocate and initialize a new AVCE00ParseInfo structure. + * + * AVCE00ParseStartSection() will have to be called at least once + * to specify the type of objects to parse. + * + * The structure will eventually have to be freed with AVCE00ParseInfoFree(). + **********************************************************************/ +AVCE00ParseInfo *AVCE00ParseInfoAlloc() +{ + AVCE00ParseInfo *psInfo; + + psInfo = (AVCE00ParseInfo*)CPLCalloc(1,sizeof(AVCE00ParseInfo)); + + psInfo->eFileType = AVCFileUnknown; + psInfo->eSuperSectionType = AVCFileUnknown; + + /* Allocate output buffer. + * 2k should be enough... the biggest thing we'll need to store + * in it will be 1 complete INFO table record. + */ + psInfo->nBufSize = 2048; + psInfo->pszBuf = (char *)CPLMalloc(psInfo->nBufSize*sizeof(char)); + + /* Set a default precision, but this value will be set on a section + * by section basis inside AVCE00ParseStartSection() + */ + psInfo->nPrecision = AVC_SINGLE_PREC; + + return psInfo; +} + +/********************************************************************** + * _AVCE00ParseDestroyCurObject() + * + * Release mem. associated with the psInfo->cur.* object we are + * currently using. + **********************************************************************/ +void _AVCE00ParseDestroyCurObject(AVCE00ParseInfo *psInfo) +{ + if (psInfo->eFileType == AVCFileUnknown) + return; + + if (psInfo->eFileType == AVCFileARC) + { + CPLFree(psInfo->cur.psArc->pasVertices); + CPLFree(psInfo->cur.psArc); + } + else if (psInfo->eFileType == AVCFilePAL || + psInfo->eFileType == AVCFileRPL ) + { + CPLFree(psInfo->cur.psPal->pasArcs); + CPLFree(psInfo->cur.psPal); + } + else if (psInfo->eFileType == AVCFileCNT) + { + CPLFree(psInfo->cur.psCnt->panLabelIds); + CPLFree(psInfo->cur.psCnt); + } + else if (psInfo->eFileType == AVCFileLAB) + { + CPLFree(psInfo->cur.psLab); + } + else if (psInfo->eFileType == AVCFileTOL) + { + CPLFree(psInfo->cur.psTol); + } + else if (psInfo->eFileType == AVCFilePRJ) + { + CSLDestroy(psInfo->cur.papszPrj); + } + else if (psInfo->eFileType == AVCFileTXT || + psInfo->eFileType == AVCFileTX6) + { + CPLFree(psInfo->cur.psTxt->pasVertices); + CPLFree(psInfo->cur.psTxt->pszText); + CPLFree(psInfo->cur.psTxt); + } + else if (psInfo->eFileType == AVCFileRXP) + { + CPLFree(psInfo->cur.psRxp); + } + else if (psInfo->eFileType == AVCFileTABLE) + { + _AVCDestroyTableFields(psInfo->hdr.psTableDef, psInfo->cur.pasFields); + _AVCDestroyTableDef(psInfo->hdr.psTableDef); + psInfo->bTableHdrComplete = FALSE; + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "_AVCE00ParseDestroyCurObject(): Unsupported file type!"); + } + + psInfo->eFileType = AVCFileUnknown; + psInfo->cur.psArc = NULL; +} + +/********************************************************************** + * AVCE00ParseInfoFree() + * + * Free any memory associated with a AVCE00ParseInfo structure. + **********************************************************************/ +void AVCE00ParseInfoFree(AVCE00ParseInfo *psInfo) +{ + if (psInfo) + { + CPLFree(psInfo->pszSectionHdrLine); + psInfo->pszSectionHdrLine = NULL; + CPLFree(psInfo->pszBuf); + _AVCE00ParseDestroyCurObject(psInfo); + } + + CPLFree(psInfo); +} + +/********************************************************************** + * AVCE00ParseReset() + * + * Reset the fields in a AVCE00ParseInfo structure so that further calls + * to the API will be ready to process a new object. + **********************************************************************/ +void AVCE00ParseReset(AVCE00ParseInfo *psInfo) +{ + psInfo->iCurItem = psInfo->numItems = 0; + psInfo->bForceEndOfSection = FALSE; +} + + +/********************************************************************** + * AVCE00ParseSuperSectionHeader() + * + * Check if pszLine is a valid "supersection" header line, if it is one + * then store the supersection type in the ParseInfo structure. + * + * What I call a "supersection" is a section that contains several + * files, such as the TX6/TX7, RPL, RXP, ... and also the IFO (TABLEs). + * + * The ParseInfo structure won't be ready to read objects until + * a call to AVCE00ParseSectionHeader() (see below) succesfully + * recognizes the beginning of a subsection of this type. + * + * Returns the new supersection type, or AVCFileUnknown if the line is + * not recognized. + **********************************************************************/ +AVCFileType AVCE00ParseSuperSectionHeader(AVCE00ParseInfo *psInfo, + const char *pszLine) +{ + /*----------------------------------------------------------------- + * If we're already inside a supersection or a section, then + * return AVCFileUnknown right away. + *----------------------------------------------------------------*/ + if (psInfo == NULL || + psInfo->eSuperSectionType != AVCFileUnknown || + psInfo->eFileType != AVCFileUnknown ) + { + return AVCFileUnknown; + } + + /*----------------------------------------------------------------- + * Check if pszLine is a valid supersection header line. + *----------------------------------------------------------------*/ + if (EQUALN(pszLine, "RPL ", 5)) + psInfo->eSuperSectionType = AVCFileRPL; + else if (EQUALN(pszLine, "TX6 ", 5) || EQUALN(pszLine, "TX7 ", 5)) + psInfo->eSuperSectionType = AVCFileTX6; + else if (EQUALN(pszLine, "RXP ", 5)) + psInfo->eSuperSectionType = AVCFileRXP; + else if (EQUALN(pszLine, "IFO ", 5)) + psInfo->eSuperSectionType = AVCFileTABLE; + else + return AVCFileUnknown; + + /*----------------------------------------------------------------- + * Record the start of the supersection (for faster seeking) + *----------------------------------------------------------------*/ + psInfo->nStartLineNum = psInfo->nCurLineNum; + + /*----------------------------------------------------------------- + * OK, we have a valid new section header. Set the precision and + * get ready to read objects from it. + *----------------------------------------------------------------*/ + if (atoi(pszLine+4) == 2) + psInfo->nPrecision = AVC_SINGLE_PREC; + else if (atoi(pszLine+4) == 3) + psInfo->nPrecision = AVC_DOUBLE_PREC; + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Parse Error: Invalid section header line (\"%s\")!", + pszLine); + psInfo->eSuperSectionType = AVCFileUnknown; + /* psInfo->nStartLineNum = -1; */ + } + + return psInfo->eSuperSectionType; +} + +/********************************************************************** + * AVCE00ParseSuperSectionEnd() + * + * Check if pszLine marks the end of a supersection, and if it is the + * case, then reset the supersection flag in the ParseInfo. + * + * Supersections always end with the line "JABBERWOCKY", except for + * the IFO section. + **********************************************************************/ +GBool AVCE00ParseSuperSectionEnd(AVCE00ParseInfo *psInfo, + const char *pszLine ) +{ + if (psInfo->eFileType == AVCFileUnknown && + psInfo->eSuperSectionType != AVCFileUnknown && + (EQUALN(pszLine, "JABBERWOCKY", 11) || + (psInfo->eSuperSectionType == AVCFileTABLE && + EQUALN(pszLine, "EOI", 3) ) ) ) + { + psInfo->eSuperSectionType = AVCFileUnknown; + /* psInfo->nStartLineNum = -1; */ + return TRUE; + } + + return FALSE; +} + + +/********************************************************************** + * AVCE00ParseSectionHeader() + * + * Check if pszLine is a valid section header line, then initialize the + * ParseInfo structure to be ready to parse of object from that section. + * + * Returns the new section type, or AVCFileUnknown if the line is + * not recognized as a valid section header. + * + * Note: by section header lines, we mean the "ARC 2", "PAL 2", etc. + **********************************************************************/ +AVCFileType AVCE00ParseSectionHeader(AVCE00ParseInfo *psInfo, + const char *pszLine) +{ + AVCFileType eNewType = AVCFileUnknown; + + if (psInfo == NULL || + psInfo->eFileType != AVCFileUnknown) + { + return AVCFileUnknown; + } + + /*----------------------------------------------------------------- + * Check if pszLine is a valid section header line. + *----------------------------------------------------------------*/ + if (psInfo->eSuperSectionType == AVCFileUnknown) + { + /*------------------------------------------------------------- + * We're looking for a top-level section... + *------------------------------------------------------------*/ + if (EQUALN(pszLine, "ARC ", 5)) + eNewType = AVCFileARC; + else if (EQUALN(pszLine, "PAL ", 5)) + eNewType = AVCFilePAL; + else if (EQUALN(pszLine, "CNT ", 5)) + eNewType = AVCFileCNT; + else if (EQUALN(pszLine, "LAB ", 5)) + eNewType = AVCFileLAB; + else if (EQUALN(pszLine, "TOL ", 5)) + eNewType = AVCFileTOL; + else if (EQUALN(pszLine, "PRJ ", 5)) + eNewType = AVCFilePRJ; + else if (EQUALN(pszLine, "TXT ", 5)) + eNewType = AVCFileTXT; + else + { + eNewType = AVCFileUnknown; + return AVCFileUnknown; + } + + /*------------------------------------------------------------- + * OK, we have a valid new section header. Set the precision and + * get ready to read objects from it. + *------------------------------------------------------------*/ + if (atoi(pszLine+4) == 2) + psInfo->nPrecision = AVC_SINGLE_PREC; + else if (atoi(pszLine+4) == 3) + psInfo->nPrecision = AVC_DOUBLE_PREC; + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Parse Error: Invalid section header line (\"%s\")!", + pszLine); + eNewType = AVCFileUnknown; + return AVCFileUnknown; + } + + } + else + { + /*------------------------------------------------------------- + * We're looking for a section inside a super-section... + * in this case, the header line contains the subclass name, + * so any non-empty line is acceptable! + * Note: the precision is already set from the previous call to + * AVCE00ParseSuperSectionHeader() + * Note2: Inside a double precision RPL supersection, the end of + * each sub-section is marked by 2 lines, just like what + * happens with double precision PALs... we have to make + * sure we don't catch that second line as the beginning + * of a new RPL sub-section. + *------------------------------------------------------------*/ + + if (psInfo->eSuperSectionType == AVCFileTX6 && strlen(pszLine)==0) + { + /* See bug 1261: It seems that empty subclass names are valid + * for TX7. We don't know if that's valid for other supersection + * types, so we'll handle this as a specific case just for TX7 + */ + eNewType = psInfo->eSuperSectionType; + } + else if (strlen(pszLine) > 0 && !isspace((unsigned char)pszLine[0]) && + !EQUALN(pszLine, "JABBERWOCKY", 11) && + !EQUALN(pszLine, "EOI", 3) && + ! ( psInfo->eSuperSectionType == AVCFileRPL && + EQUALN(pszLine, " 0.00000", 6) ) ) + { + eNewType = psInfo->eSuperSectionType; + } + else if (strlen(pszLine) == 0 && + psInfo->eSuperSectionType == AVCFileTX6) + { + eNewType = psInfo->eSuperSectionType; + } + else + { + eNewType = AVCFileUnknown; + return AVCFileUnknown; + } + } + + /*----------------------------------------------------------------- + * nCurObjectId is used to keep track of sequential ids that are + * not explicitly stored in E00. e.g. polygon Id in a PAL section. + *----------------------------------------------------------------*/ + psInfo->nCurObjectId = 0; + + /*----------------------------------------------------------------- + * Allocate a temp. structure to use to store the objects we read + * (Using Calloc() will automatically initialize the struct contents + * to NULL... this is very important for ARCs and PALs) + *----------------------------------------------------------------*/ + _AVCE00ParseDestroyCurObject(psInfo); + + if (eNewType == AVCFileARC) + { + psInfo->cur.psArc = (AVCArc*)CPLCalloc(1, sizeof(AVCArc)); + } + else if (eNewType == AVCFilePAL || + eNewType == AVCFileRPL ) + { + psInfo->cur.psPal = (AVCPal*)CPLCalloc(1, sizeof(AVCPal)); + } + else if (eNewType == AVCFileCNT) + { + psInfo->cur.psCnt = (AVCCnt*)CPLCalloc(1, sizeof(AVCCnt)); + } + else if (eNewType == AVCFileLAB) + { + psInfo->cur.psLab = (AVCLab*)CPLCalloc(1, sizeof(AVCLab)); + } + else if (eNewType == AVCFileTOL) + { + psInfo->cur.psTol = (AVCTol*)CPLCalloc(1, sizeof(AVCTol)); + } + else if (eNewType == AVCFilePRJ) + { + psInfo->cur.papszPrj = NULL; + } + else if (eNewType == AVCFileTXT || + eNewType == AVCFileTX6) + { + psInfo->cur.psTxt = (AVCTxt*)CPLCalloc(1, sizeof(AVCTxt)); + } + else if (eNewType == AVCFileRXP) + { + psInfo->cur.psRxp = (AVCRxp*)CPLCalloc(1, sizeof(AVCRxp)); + } + else if (eNewType == AVCFileTABLE) + { + psInfo->cur.pasFields = NULL; + psInfo->hdr.psTableDef = NULL; + psInfo->bTableHdrComplete = FALSE; + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "AVCE00ParseSectionHeader(): Unsupported file type!"); + eNewType = AVCFileUnknown; + } + + if (eNewType != AVCFileUnknown) + { + /*----------------------------------------------------------------- + * Record the start of the section (for faster seeking) + *----------------------------------------------------------------*/ + psInfo->nStartLineNum = psInfo->nCurLineNum; + + /*----------------------------------------------------------------- + * Keep track of section header line... this is used for some file + * types, specially the ones enclosed inside supersections. + *----------------------------------------------------------------*/ + CPLFree(psInfo->pszSectionHdrLine); + psInfo->pszSectionHdrLine = CPLStrdup(pszLine); + } + + psInfo->eFileType = eNewType; + + return psInfo->eFileType; +} + + +/********************************************************************** + * AVCE00ParseSectionEnd() + * + * Check if pszLine marks the end of the current section. + * + * Passing bResetParseInfo=TRUE will reset the parser struct if an end of + * section is found. Passing FALSE simply tests for the end of section + * without affecting the parse info struct. + * + * Return TRUE if this is the end of the section (and reset the + * ParseInfo structure) , or FALSE otherwise. + **********************************************************************/ +GBool AVCE00ParseSectionEnd(AVCE00ParseInfo *psInfo, const char *pszLine, + GBool bResetParseInfo) +{ + if ( psInfo->bForceEndOfSection || + ((psInfo->eFileType == AVCFileARC || + psInfo->eFileType == AVCFilePAL || + psInfo->eFileType == AVCFileLAB || + psInfo->eFileType == AVCFileRPL || + psInfo->eFileType == AVCFileCNT || + psInfo->eFileType == AVCFileTOL || + psInfo->eFileType == AVCFileTXT || + psInfo->eFileType == AVCFileTX6 || + psInfo->eFileType == AVCFileRXP ) && + EQUALN(pszLine, " -1 0", 20) ) ) + { + /* Reset ParseInfo only if explicitly requested. + */ + if (bResetParseInfo) + { + _AVCE00ParseDestroyCurObject(psInfo); + AVCE00ParseReset(psInfo); + psInfo->eFileType = AVCFileUnknown; + + CPLFree(psInfo->pszSectionHdrLine); + psInfo->pszSectionHdrLine = NULL; + + psInfo->bForceEndOfSection = FALSE; + } + + return TRUE; /* YES, we reached the end */ + } + + return FALSE; /* NO, it's not the end of section line */ +} + +/********************************************************************** + * AVCE00ParseNextLine() + * + * Take the next line of E00 input and parse it. + * + * Returns NULL if the current object is not complete yet (expecting + * more lines of input) or a reference to a complete object if it + * is complete. + * + * The returned object is a reference to an internal data structure. + * It should not be modified or freed by the caller. + * + * If the input is invalid or other problems happen, then a CPLError() + * will be generated. CPLGetLastErrorNo() should be called to check + * that the line was parsed succesfully. + * + * Note for TABLES: + * When parsing input from info tables, the first valid object that + * will be returned will be the AVCTableDef, and then the data records + * will follow. When all the records have been read, then the + * psInfo->bForceEndOfSection flag will be set to TRUE since there is + * no explicit "end of table" line in E00. + **********************************************************************/ +void *AVCE00ParseNextLine(AVCE00ParseInfo *psInfo, const char *pszLine) +{ + void *psObj = NULL; + + CPLAssert(psInfo); + switch(psInfo->eFileType) + { + case AVCFileARC: + psObj = (void*)AVCE00ParseNextArcLine(psInfo, pszLine); + break; + case AVCFilePAL: + case AVCFileRPL: + psObj = (void*)AVCE00ParseNextPalLine(psInfo, pszLine); + break; + case AVCFileCNT: + psObj = (void*)AVCE00ParseNextCntLine(psInfo, pszLine); + break; + case AVCFileLAB: + psObj = (void*)AVCE00ParseNextLabLine(psInfo, pszLine); + break; + case AVCFileTOL: + psObj = (void*)AVCE00ParseNextTolLine(psInfo, pszLine); + break; + case AVCFilePRJ: + psObj = (void*)AVCE00ParseNextPrjLine(psInfo, pszLine); + break; + case AVCFileTXT: + psObj = (void*)AVCE00ParseNextTxtLine(psInfo, pszLine); + break; + case AVCFileTX6: + psObj = (void*)AVCE00ParseNextTx6Line(psInfo, pszLine); + break; + case AVCFileRXP: + psObj = (void*)AVCE00ParseNextRxpLine(psInfo, pszLine); + break; + case AVCFileTABLE: + if ( ! psInfo->bTableHdrComplete ) + psObj = (void*)AVCE00ParseNextTableDefLine(psInfo, pszLine); + else + psObj = (void*)AVCE00ParseNextTableRecLine(psInfo, pszLine); + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "AVCE00ParseNextLine(): Unsupported file type!"); + } + + return psObj; +} + + +/********************************************************************** + * AVCE00ParseNextArcLine() + * + * Take the next line of E00 input for an ARC object and parse it. + * + * Returns NULL if the current object is not complete yet (expecting + * more lines of input) or a reference to a complete object if it + * is complete. + * + * The returned object is a reference to an internal data structure. + * It should not be modified or freed by the caller. + * + * If the input is invalid or other problems happen, then a CPLError() + * will be generated. CPLGetLastErrorNo() should be called to check + * that the line was parsed succesfully. + **********************************************************************/ +AVCArc *AVCE00ParseNextArcLine(AVCE00ParseInfo *psInfo, const char *pszLine) +{ + AVCArc *psArc; + int nLen; + + CPLAssert(psInfo->eFileType == AVCFileARC); + + psArc = psInfo->cur.psArc; + + nLen = strlen(pszLine); + + if (psInfo->numItems == 0) + { + /*------------------------------------------------------------- + * Begin processing a new object, read header line: + * ArcId, UserId, FNode, TNode, LPoly, RPoly, numVertices + *------------------------------------------------------------*/ + if (nLen < 70) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 ARC line: \"%s\"", pszLine); + return NULL; + } + else + { + psArc->nArcId = AVCE00Str2Int(pszLine, 10); + psArc->nUserId = AVCE00Str2Int(pszLine+10, 10); + psArc->nFNode = AVCE00Str2Int(pszLine+20, 10); + psArc->nTNode = AVCE00Str2Int(pszLine+30, 10); + psArc->nLPoly = AVCE00Str2Int(pszLine+40, 10); + psArc->nRPoly = AVCE00Str2Int(pszLine+50, 10); + psArc->numVertices = AVCE00Str2Int(pszLine+60, 10); + + /* Realloc the array of vertices + */ + psArc->pasVertices = (AVCVertex*)CPLRealloc(psArc->pasVertices, + psArc->numVertices* + sizeof(AVCVertex)); + + /* psInfo->iCurItem is the last vertex that was read. + * psInfo->numItems is the number of vertices to read. + */ + psInfo->iCurItem = 0; + psInfo->numItems = psArc->numVertices; + } + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->nPrecision == AVC_SINGLE_PREC && + ( (psInfo->iCurItem==psInfo->numItems-1 && nLen >= 28) || + nLen >= 56 ) ) + { + /*------------------------------------------------------------- + * Single precision ARCs: 2 pairs of X,Y values per line + * Except on the last line with an odd number of vertices) + *------------------------------------------------------------*/ + psArc->pasVertices[psInfo->iCurItem].x = CPLAtof(pszLine); + psArc->pasVertices[psInfo->iCurItem++].y = CPLAtof(pszLine+14); + if (psInfo->iCurItem < psInfo->numItems && nLen >= 56) + { + psArc->pasVertices[psInfo->iCurItem].x = CPLAtof(pszLine+28); + psArc->pasVertices[psInfo->iCurItem++].y = CPLAtof(pszLine+42); + } + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->nPrecision == AVC_DOUBLE_PREC && + nLen >= 42) + { + /*------------------------------------------------------------- + * Double precision ARCs: 1 pair of X,Y values per line + *------------------------------------------------------------*/ + psArc->pasVertices[psInfo->iCurItem].x = CPLAtof(pszLine); + psArc->pasVertices[psInfo->iCurItem++].y = CPLAtof(pszLine+21); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 ARC line: \"%s\"", pszLine); + psInfo->numItems = psInfo->iCurItem = 0; + return NULL; + } + + /*----------------------------------------------------------------- + * If we're done parsing this ARC, then reset the ParseInfo, + * and return a reference to the ARC structure + * Otherwise return NULL, which means that we are expecting more + * more lines of input. + *----------------------------------------------------------------*/ + if (psInfo->iCurItem >= psInfo->numItems) + { + psInfo->numItems = psInfo->iCurItem = 0; + return psArc; + } + + return NULL; +} + +/********************************************************************** + * AVCE00ParseNextPalLine() + * + * Take the next line of E00 input for an PAL object and parse it. + * + * Returns NULL if the current object is not complete yet (expecting + * more lines of input) or a reference to a complete object if it + * is complete. + * + * The returned object is a reference to an internal data structure. + * It should not be modified or freed by the caller. + * + * If the input is invalid or other problems happen, then a CPLError() + * will be generated. CPLGetLastErrorNo() should be called to check + * that the line was parsed succesfully. + **********************************************************************/ +AVCPal *AVCE00ParseNextPalLine(AVCE00ParseInfo *psInfo, const char *pszLine) +{ + AVCPal *psPal; + int nLen; + + CPLAssert(psInfo->eFileType == AVCFilePAL || + psInfo->eFileType == AVCFileRPL ); + + psPal = psInfo->cur.psPal; + + nLen = strlen(pszLine); + + if (psInfo->numItems == 0) + { + /*------------------------------------------------------------- + * Begin processing a new object, read header line: + * numArcs, MinX, MinY, MaxX, MaxY + * For Double precision, MaxX, MaxY are on a separate line. + *------------------------------------------------------------*/ + if (nLen < 52) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 PAL line: \"%s\"", pszLine); + return NULL; + } + else + { + /* Polygon Id is not stored in the E00 file. Polygons are + * stored in increasing order, starting at 1... so we just + * increment the previous value. + */ + psPal->nPolyId = ++psInfo->nCurObjectId; + + psPal->numArcs = AVCE00Str2Int(pszLine, 10); + + /* If a PAL record has 0 arcs, it really has a single "0 0 0" + * triplet as its data. + */ + if ( psPal->numArcs == 0 ) + { + psPal->numArcs = 1; + } + + /* Realloc the array of Arcs + */ + psPal->pasArcs = (AVCPalArc*)CPLRealloc(psPal->pasArcs, + psPal->numArcs* + sizeof(AVCPalArc)); + + /* psInfo->iCurItem is the index of the last arc that was read. + * psInfo->numItems is the number of arcs to read. + */ + psInfo->iCurItem = 0; + psInfo->numItems = psPal->numArcs; + + if (psInfo->nPrecision == AVC_SINGLE_PREC) + { + psPal->sMin.x = CPLAtof(pszLine + 10); + psPal->sMin.y = CPLAtof(pszLine + 24); + psPal->sMax.x = CPLAtof(pszLine + 38); + psPal->sMax.y = CPLAtof(pszLine + 52); + } + else + { + psPal->sMin.x = CPLAtof(pszLine + 10); + psPal->sMin.y = CPLAtof(pszLine + 31); + /* Set psInfo->iCurItem = -1 since we still have 2 values + * from the header to read on the next line. + */ + psInfo->iCurItem = -1; + } + + } + } + else if (psInfo->iCurItem == -1 && nLen >= 42) + { + psPal->sMax.x = CPLAtof(pszLine); + psPal->sMax.y = CPLAtof(pszLine + 21); + psInfo->iCurItem++; + } + else if (psInfo->iCurItem < psPal->numArcs && + (nLen >= 60 || + (psInfo->iCurItem == psPal->numArcs-1 && nLen >= 30)) ) + { + /*------------------------------------------------------------- + * 2 PAL entries (ArcId, FNode, AdjPoly) per line, + * (Except on the last line with an odd number of vertices) + *------------------------------------------------------------*/ + psPal->pasArcs[psInfo->iCurItem].nArcId = AVCE00Str2Int(pszLine, 10); + psPal->pasArcs[psInfo->iCurItem].nFNode = AVCE00Str2Int(pszLine+10,10); + psPal->pasArcs[psInfo->iCurItem++].nAdjPoly = AVCE00Str2Int(pszLine+20, + 10); + + if (psInfo->iCurItem < psInfo->numItems) + { + psPal->pasArcs[psInfo->iCurItem].nArcId = AVCE00Str2Int(pszLine+30, + 10); + psPal->pasArcs[psInfo->iCurItem].nFNode = AVCE00Str2Int(pszLine+40, + 10); + psPal->pasArcs[psInfo->iCurItem++].nAdjPoly = + AVCE00Str2Int(pszLine+50, 10); + } + + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 PAL line: \"%s\"", pszLine); + psInfo->numItems = psInfo->iCurItem = 0; + return NULL; + } + + /*----------------------------------------------------------------- + * If we're done parsing this PAL, then reset the ParseInfo, + * and return a reference to the PAL structure + * Otherwise return NULL, which means that we are expecting more + * more lines of input. + *----------------------------------------------------------------*/ + if (psInfo->iCurItem >= psInfo->numItems) + { + psInfo->numItems = psInfo->iCurItem = 0; + return psPal; + } + + return NULL; +} + + +/********************************************************************** + * AVCE00ParseNextCntLine() + * + * Take the next line of E00 input for an CNT object and parse it. + * + * Returns NULL if the current object is not complete yet (expecting + * more lines of input) or a reference to a complete object if it + * is complete. + * + * The returned object is a reference to an internal data structure. + * It should not be modified or freed by the caller. + * + * If the input is invalid or other problems happen, then a CPLError() + * will be generated. CPLGetLastErrorNo() should be called to check + * that the line was parsed succesfully. + **********************************************************************/ +AVCCnt *AVCE00ParseNextCntLine(AVCE00ParseInfo *psInfo, const char *pszLine) +{ + AVCCnt *psCnt; + int nLen; + + CPLAssert(psInfo->eFileType == AVCFileCNT); + + psCnt = psInfo->cur.psCnt; + + nLen = strlen(pszLine); + + if (psInfo->numItems == 0) + { + /*------------------------------------------------------------- + * Begin processing a new object, read header line: + * numLabels, X, Y + *------------------------------------------------------------*/ + if (nLen < 38) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 CNT line: \"%s\"", pszLine); + return NULL; + } + else + { + /* Polygon Id is not stored in the E00 file. Centroids are + * stored in increasing order of Polygon Id, starting at 1... + * so we just increment the previous value. + */ + psCnt->nPolyId = ++psInfo->nCurObjectId; + + psCnt->numLabels = AVCE00Str2Int(pszLine, 10); + + /* Realloc the array of Labels Ids + * Avoid allocating a 0-length segment since centroids can have + * 0 labels attached to them. + */ + if (psCnt->numLabels > 0) + psCnt->panLabelIds = (GInt32 *)CPLRealloc(psCnt->panLabelIds, + psCnt->numLabels* + sizeof(GInt32)); + + if (psInfo->nPrecision == AVC_SINGLE_PREC) + { + psCnt->sCoord.x = CPLAtof(pszLine + 10); + psCnt->sCoord.y = CPLAtof(pszLine + 24); + } + else + { + psCnt->sCoord.x = CPLAtof(pszLine + 10); + psCnt->sCoord.y = CPLAtof(pszLine + 31); + } + + /* psInfo->iCurItem is the index of the last label that was read. + * psInfo->numItems is the number of label ids to read. + */ + psInfo->iCurItem = 0; + psInfo->numItems = psCnt->numLabels; + + } + } + else if (psInfo->iCurItem < psInfo->numItems ) + { + /*------------------------------------------------------------- + * Each line can contain up to 8 label ids (10 chars each) + *------------------------------------------------------------*/ + int i=0; + while(psInfo->iCurItem < psInfo->numItems && nLen >= (i+1)*10) + { + psCnt->panLabelIds[psInfo->iCurItem++] = + AVCE00Str2Int(pszLine + i*10, 10); + i++; + } + + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 CNT line: \"%s\"", pszLine); + psInfo->numItems = psInfo->iCurItem = 0; + return NULL; + } + + /*----------------------------------------------------------------- + * If we're done parsing this CNT, then reset the ParseInfo, + * and return a reference to the CNT structure + * Otherwise return NULL, which means that we are expecting more + * more lines of input. + *----------------------------------------------------------------*/ + if (psInfo->iCurItem >= psInfo->numItems) + { + psInfo->numItems = psInfo->iCurItem = 0; + return psCnt; + } + + return NULL; +} + +/********************************************************************** + * AVCE00ParseNextLabLine() + * + * Take the next line of E00 input for an LAB object and parse it. + * + * Returns NULL if the current object is not complete yet (expecting + * more lines of input) or a reference to a complete object if it + * is complete. + * + * The returned object is a reference to an internal data structure. + * It should not be modified or freed by the caller. + * + * If the input is invalid or other problems happen, then a CPLError() + * will be generated. CPLGetLastErrorNo() should be called to check + * that the line was parsed succesfully. + **********************************************************************/ +AVCLab *AVCE00ParseNextLabLine(AVCE00ParseInfo *psInfo, const char *pszLine) +{ + AVCLab *psLab; + int nLen; + + CPLAssert(psInfo->eFileType == AVCFileLAB); + + psLab = psInfo->cur.psLab; + + nLen = strlen(pszLine); + + if (psInfo->numItems == 0) + { + /*------------------------------------------------------------- + * Begin processing a new object, read header line: + * LabelValue, PolyId, X1, Y1 + *------------------------------------------------------------*/ + if (nLen < 48) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 LAB line: \"%s\"", pszLine); + return NULL; + } + else + { + psLab->nValue = AVCE00Str2Int(pszLine, 10); + psLab->nPolyId = AVCE00Str2Int(pszLine+10, 10); + + if (psInfo->nPrecision == AVC_SINGLE_PREC) + { + psLab->sCoord1.x = CPLAtof(pszLine + 20); + psLab->sCoord1.y = CPLAtof(pszLine + 34); + } + else + { + psLab->sCoord1.x = CPLAtof(pszLine + 20); + psLab->sCoord1.y = CPLAtof(pszLine + 41); + } + + /* psInfo->iCurItem is the index of the last X,Y pair we read. + * psInfo->numItems is the number of X,Y pairs to read. + */ + psInfo->iCurItem = 1; + psInfo->numItems = 3; + + + } + } + else if (psInfo->iCurItem == 1 && psInfo->nPrecision == AVC_SINGLE_PREC && + nLen >= 56 ) + { + psLab->sCoord2.x = CPLAtof(pszLine); + psLab->sCoord2.y = CPLAtof(pszLine + 14); + psLab->sCoord3.x = CPLAtof(pszLine + 28); + psLab->sCoord3.y = CPLAtof(pszLine + 42); + psInfo->iCurItem += 2; + } + else if (psInfo->iCurItem == 1 && psInfo->nPrecision == AVC_DOUBLE_PREC && + nLen >= 42 ) + { + psLab->sCoord2.x = CPLAtof(pszLine); + psLab->sCoord2.y = CPLAtof(pszLine + 21); + psInfo->iCurItem++; + } + else if (psInfo->iCurItem == 2 && psInfo->nPrecision == AVC_DOUBLE_PREC && + nLen >= 42 ) + { + psLab->sCoord3.x = CPLAtof(pszLine); + psLab->sCoord3.y = CPLAtof(pszLine + 21); + psInfo->iCurItem++; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 LAB line: \"%s\"", pszLine); + psInfo->numItems = psInfo->iCurItem = 0; + return NULL; + } + + /*----------------------------------------------------------------- + * If we're done parsing this LAB, then reset the ParseInfo, + * and return a reference to the LAB structure + * Otherwise return NULL, which means that we are expecting more + * more lines of input. + *----------------------------------------------------------------*/ + if (psInfo->iCurItem >= psInfo->numItems) + { + psInfo->numItems = psInfo->iCurItem = 0; + return psLab; + } + + return NULL; +} + + + +/********************************************************************** + * AVCE00ParseNextTolLine() + * + * Take the next line of E00 input for an TOL object and parse it. + * + * Returns NULL if the current object is not complete yet (expecting + * more lines of input) or a reference to a complete object if it + * is complete. + * + * The returned object is a reference to an internal data structure. + * It should not be modified or freed by the caller. + * + * If the input is invalid or other problems happen, then a CPLError() + * will be generated. CPLGetLastErrorNo() should be called to check + * that the line was parsed succesfully. + **********************************************************************/ +AVCTol *AVCE00ParseNextTolLine(AVCE00ParseInfo *psInfo, const char *pszLine) +{ + AVCTol *psTol; + int nLen; + + CPLAssert(psInfo->eFileType == AVCFileTOL); + + psTol = psInfo->cur.psTol; + + nLen = strlen(pszLine); + + if (nLen >= 34) + { + /*------------------------------------------------------------- + * TOL Entries are only one line each: + * TolIndex, TolFlag, TolValue + *------------------------------------------------------------*/ + psTol->nIndex = AVCE00Str2Int(pszLine, 10); + psTol->nFlag = AVCE00Str2Int(pszLine + 10, 10); + + psTol->dValue = CPLAtof(pszLine + 20); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 TOL line: \"%s\"", pszLine); + psInfo->numItems = psInfo->iCurItem = 0; + return NULL; + } + + /*----------------------------------------------------------------- + * If we're done parsing this TOL, then reset the ParseInfo, + * and return a reference to the TOL structure + * Otherwise return NULL, which means that we are expecting more + * more lines of input. + *----------------------------------------------------------------*/ + if (psInfo->iCurItem >= psInfo->numItems) + { + psInfo->numItems = psInfo->iCurItem = 0; + return psTol; + } + + return NULL; +} + +/********************************************************************** + * AVCE00ParseNextPrjLine() + * + * Take the next line of E00 input for a PRJ object and parse it. + * + * Returns NULL if the current object is not complete yet (expecting + * more lines of input) or a reference to a complete object if it + * is complete. + * + * Since a PRJ section contains only ONE projection, the function will + * always return NULL, until it reaches the end-of-section (EOP) line. + * This is behavior is a bit different from the other section types that + * will usually return a valid object immediately before the last line + * of the section (the end-of-section line). + * + * The returned object is a reference to an internal data structure. + * It should not be modified or freed by the caller. + * + * If the input is invalid or other problems happen, then a CPLError() + * will be generated. CPLGetLastErrorNo() should be called to check + * that the line was parsed succesfully. + **********************************************************************/ +char **AVCE00ParseNextPrjLine(AVCE00ParseInfo *psInfo, const char *pszLine) +{ + CPLAssert(psInfo->eFileType == AVCFilePRJ); + + /*------------------------------------------------------------- + * Since a PRJ section contains only ONE projection, this function will + * always return NULL until it reaches the end-of-section (EOP) line. + * This is behavior is a bit different from the other section types that + * will usually return a valid object immediately before the last line + * of the section (the end-of-section line). + *------------------------------------------------------------*/ + + if (EQUALN(pszLine, "EOP", 3)) + { + /*------------------------------------------------------------- + * We reached end of section... return the PRJ. + *------------------------------------------------------------*/ + psInfo->bForceEndOfSection = TRUE; + return psInfo->cur.papszPrj; + } + + if ( pszLine[0] != '~' ) + { + /*------------------------------------------------------------- + * This is a new line... add it to the papszPrj stringlist. + *------------------------------------------------------------*/ + psInfo->cur.papszPrj = CSLAddString(psInfo->cur.papszPrj, pszLine); + } + else if ( strlen(pszLine) > 1 ) + { + /*------------------------------------------------------------- + * '~' is a line continuation char. Append what follows the '~' + * to the end of the previous line. + *------------------------------------------------------------*/ + int iLastLine, nNewLen; + + iLastLine = CSLCount(psInfo->cur.papszPrj) - 1; + nNewLen = strlen(psInfo->cur.papszPrj[iLastLine])+strlen(pszLine)-1+1; + if (iLastLine >= 0) + { + psInfo->cur.papszPrj[iLastLine] = + (char*)CPLRealloc(psInfo->cur.papszPrj[iLastLine], + nNewLen * sizeof(char)); + + strcat(psInfo->cur.papszPrj[iLastLine], pszLine+1); + } + } + + return NULL; +} + + +/********************************************************************** + * AVCE00ParseNextTxtLine() + * + * Take the next line of E00 input for an TXT object and parse it. + * + * Returns NULL if the current object is not complete yet (expecting + * more lines of input) or a reference to a complete object if it + * is complete. + * + * The returned object is a reference to an internal data structure. + * It should not be modified or freed by the caller. + * + * If the input is invalid or other problems happen, then a CPLError() + * will be generated. CPLGetLastErrorNo() should be called to check + * that the line was parsed succesfully. + **********************************************************************/ +AVCTxt *AVCE00ParseNextTxtLine(AVCE00ParseInfo *psInfo, const char *pszLine) +{ + AVCTxt *psTxt; + int i, nLen, numFixedLines; + + CPLAssert(psInfo->eFileType == AVCFileTXT); + + psTxt = psInfo->cur.psTxt; + + nLen = strlen(pszLine); + + /* numFixedLines is the number of lines to expect before the line(s) + * with the text string + */ + if (psInfo->nPrecision == AVC_SINGLE_PREC) + numFixedLines = 4; + else + numFixedLines = 6; + + if (psInfo->numItems == 0) + { + /*------------------------------------------------------------- + * Begin processing a new object, read header line: + *------------------------------------------------------------*/ + if (nLen < 50) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 TXT line: \"%s\"", pszLine); + return NULL; + } + else + { + int numVertices; + /*--------------------------------------------------------- + * With TXT, there are several unused fields that have to be + * set to default values... usually 0. + *--------------------------------------------------------*/ + psTxt->nUserId = 0; + psTxt->n28 = 0; + for(i=0; i<20; i++) + psTxt->anJust1[i] = psTxt->anJust2[i] = 0; + psTxt->dV2 = psTxt->dV3 = 0.0; + + /*--------------------------------------------------------- + * System Id is not stored in the E00 file. Annotations are + * stored in increasing order of System Id, starting at 1... + * so we just increment the previous value. + *--------------------------------------------------------*/ + psTxt->nTxtId = ++psInfo->nCurObjectId; + + psTxt->nLevel = AVCE00Str2Int(pszLine, 10); + + /* Add 1 to numVerticesLine because the first vertex is + * always duplicated in the TXT binary structure... + */ + psTxt->numVerticesLine = AVCE00Str2Int(pszLine+10, 10) + 1; + + psTxt->numVerticesArrow= AVCE00Str2Int(pszLine+20, 10); + psTxt->nSymbol = AVCE00Str2Int(pszLine+30, 10); + psTxt->numChars = AVCE00Str2Int(pszLine+40, 10); + + + /*--------------------------------------------------------- + * Realloc the string buffer and array of vertices + *--------------------------------------------------------*/ + psTxt->pszText = (GByte *)CPLRealloc(psTxt->pszText, + (psTxt->numChars+1)* + sizeof(GByte)); + numVertices = ABS(psTxt->numVerticesLine) + + ABS(psTxt->numVerticesArrow); + if (numVertices > 0) + psTxt->pasVertices = (AVCVertex*)CPLRealloc(psTxt->pasVertices, + numVertices*sizeof(AVCVertex)); + + /*--------------------------------------------------------- + * Fill the whole string buffer with spaces we'll just + * paste lines in it using strncpy() + *--------------------------------------------------------*/ + memset(psTxt->pszText, ' ', psTxt->numChars); + psTxt->pszText[psTxt->numChars] = '\0'; + + /*--------------------------------------------------------- + * psInfo->iCurItem is the index of the last line that was read. + * psInfo->numItems is the number of lines to read. + *--------------------------------------------------------*/ + psInfo->iCurItem = 0; + psInfo->numItems = numFixedLines + ((psTxt->numChars-1)/80 + 1); + + } + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem < numFixedLines-1 && nLen >=63) + { + /*------------------------------------------------------------- + * Then we have a set of 15 coordinate values... unused ones + * are present but are set to 0.00E+00 + * + * Vals 1 to 4 are X coords of line along which text is drawn + * Vals 5 to 8 are the corresponding Y coords + * Vals 9 to 11 are the X coords of the text arrow + * Vals 12 to 14 are the corresponding Y coords + * The 15th value is the height + * + * Note that the first vertex (values 1 and 5) is duplicated + * in the TXT structure... go wonder why??? + *------------------------------------------------------------*/ + int iCurCoord=0, numCoordPerLine, nItemSize, iVertex; + if (psInfo->nPrecision == AVC_SINGLE_PREC) + { + numCoordPerLine = 5; + nItemSize = 14; /* Num of chars for single precision float*/ + } + else + { + numCoordPerLine = 3; + nItemSize = 21; /* Num of chars for double precision float*/ + } + iCurCoord = psInfo->iCurItem * numCoordPerLine; + + for(i=0; inumVerticesLine-1) + { + psTxt->pasVertices[iVertex+1].x = CPLAtof(pszLine+i*nItemSize); + /* The first vertex is always duplicated */ + if (iVertex == 0) + psTxt->pasVertices[0].x = psTxt->pasVertices[1].x; + } + else if (iCurCoord >= 4 && iCurCoord < 8 && + (iVertex = iCurCoord % 4) < psTxt->numVerticesLine-1) + { + psTxt->pasVertices[iVertex+1].y = CPLAtof(pszLine+i*nItemSize); + /* The first vertex is always duplicated */ + if (iVertex == 0) + psTxt->pasVertices[0].y = psTxt->pasVertices[1].y; + } + else if (iCurCoord >= 8 && iCurCoord < 11 && + (iVertex = (iCurCoord-8) % 3) < psTxt->numVerticesArrow) + { + psTxt->pasVertices[iVertex+psTxt->numVerticesLine].x = + CPLAtof(pszLine+i*nItemSize); + } + else if (iCurCoord >= 11 && iCurCoord < 14 && + (iVertex = (iCurCoord-8) % 3) < psTxt->numVerticesArrow) + { + psTxt->pasVertices[iVertex+psTxt->numVerticesLine].y = + CPLAtof(pszLine+i*nItemSize); + } + else if (iCurCoord == 14) + { + psTxt->dHeight = CPLAtof(pszLine+i*nItemSize); + } + + } + + psInfo->iCurItem++; + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem == numFixedLines-1 && nLen >=14) + { + /*------------------------------------------------------------- + * Line with a -1.000E+02 value, ALWAYS SINGLE PRECISION !!! + *------------------------------------------------------------*/ + psTxt->f_1e2 = (float)CPLAtof(pszLine); + + psInfo->iCurItem++; + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem >= numFixedLines) + { + /*------------------------------------------------------------- + * Last line, contains the text string + * Note that text can be split in 80 chars chunk and that buffer + * has been previously initialized with spaces and '\0'-terminated + *------------------------------------------------------------*/ + int numLines, iLine; + numLines = (psTxt->numChars-1)/80 + 1; + iLine = numLines - (psInfo->numItems - psInfo->iCurItem); + + if (iLine == numLines-1) + { + strncpy((char*)psTxt->pszText+(iLine*80), pszLine, + MIN( nLen, (psTxt->numChars - (iLine*80)) ) ); + } + else + { + strncpy((char*)psTxt->pszText+(iLine*80), pszLine, MIN(nLen, 80)); + } + + psInfo->iCurItem++; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 TXT line: \"%s\"", pszLine); + psInfo->numItems = psInfo->iCurItem = 0; + return NULL; + } + + /*----------------------------------------------------------------- + * If we're done parsing this TXT, then reset the ParseInfo, + * and return a reference to the TXT structure + * Otherwise return NULL, which means that we are expecting more + * more lines of input. + *----------------------------------------------------------------*/ + if (psInfo->iCurItem >= psInfo->numItems) + { + psInfo->numItems = psInfo->iCurItem = 0; + return psTxt; + } + + return NULL; +} + + +/********************************************************************** + * AVCE00ParseNextTx6Line() + * + * Take the next line of E00 input for an TX6/TX7 object and parse it. + * + * Returns NULL if the current object is not complete yet (expecting + * more lines of input) or a reference to a complete object if it + * is complete. + * + * The returned object is a reference to an internal data structure. + * It should not be modified or freed by the caller. + * + * If the input is invalid or other problems happen, then a CPLError() + * will be generated. CPLGetLastErrorNo() should be called to check + * that the line was parsed succesfully. + **********************************************************************/ +AVCTxt *AVCE00ParseNextTx6Line(AVCE00ParseInfo *psInfo, const char *pszLine) +{ + AVCTxt *psTxt; + int i, nLen; + + CPLAssert(psInfo->eFileType == AVCFileTX6); + + psTxt = psInfo->cur.psTxt; + + nLen = strlen(pszLine); + + if (psInfo->numItems == 0) + { + /*------------------------------------------------------------- + * Begin processing a new object, read header line: + *------------------------------------------------------------*/ + if (nLen < 70) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 TX6/TX7 line: \"%s\"", pszLine); + return NULL; + } + else + { + int numVertices; + /*--------------------------------------------------------- + * System Id is not stored in the E00 file. Annotations are + * stored in increasing order of System Id, starting at 1... + * so we just increment the previous value. + *--------------------------------------------------------*/ + psTxt->nTxtId = ++psInfo->nCurObjectId; + + psTxt->nUserId = AVCE00Str2Int(pszLine, 10); + psTxt->nLevel = AVCE00Str2Int(pszLine+10, 10); + psTxt->numVerticesLine = AVCE00Str2Int(pszLine+20, 10); + psTxt->numVerticesArrow= AVCE00Str2Int(pszLine+30, 10); + psTxt->nSymbol = AVCE00Str2Int(pszLine+40, 10); + psTxt->n28 = AVCE00Str2Int(pszLine+50, 10); + psTxt->numChars = AVCE00Str2Int(pszLine+60, 10); + + /*--------------------------------------------------------- + * Realloc the string buffer and array of vertices + *--------------------------------------------------------*/ + psTxt->pszText = (GByte *)CPLRealloc(psTxt->pszText, + (psTxt->numChars+1)* + sizeof(GByte)); + + numVertices = ABS(psTxt->numVerticesLine) + + ABS(psTxt->numVerticesArrow); + if (numVertices > 0) + psTxt->pasVertices = (AVCVertex*)CPLRealloc(psTxt->pasVertices, + numVertices*sizeof(AVCVertex)); + + /*--------------------------------------------------------- + * Fill the whole string buffer with spaces we'll just + * paste lines in it using strncpy() + *--------------------------------------------------------*/ + memset(psTxt->pszText, ' ', psTxt->numChars); + psTxt->pszText[psTxt->numChars] = '\0'; + + /*--------------------------------------------------------- + * psInfo->iCurItem is the index of the last line that was read. + * psInfo->numItems is the number of lines to read. + *--------------------------------------------------------*/ + psInfo->iCurItem = 0; + psInfo->numItems = 8 + numVertices + ((psTxt->numChars-1)/80 + 1); + } + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem < 6 && nLen >=60) + { + /*------------------------------------------------------------- + * Text Justification stuff... 2 sets of 20 int16 values. + *------------------------------------------------------------*/ + GInt16 *pValue; + int numValPerLine=7; + + if (psInfo->iCurItem < 3) + pValue = psTxt->anJust2 + psInfo->iCurItem * 7; + else + pValue = psTxt->anJust1 + (psInfo->iCurItem-3) * 7; + + /* Last line of each set contains only 6 values instead of 7 */ + if (psInfo->iCurItem == 2 || psInfo->iCurItem == 5) + numValPerLine = 6; + + for(i=0; iiCurItem++; + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem == 6 && nLen >=14) + { + /*------------------------------------------------------------- + * Line with a -1.000E+02 value, ALWAYS SINGLE PRECISION !!! + *------------------------------------------------------------*/ + psTxt->f_1e2 = (float)CPLAtof(pszLine); + psInfo->iCurItem++; + } + else if (psInfo->iCurItem < psInfo->numItems && + psInfo->iCurItem == 7 && nLen >=42) + { + /*------------------------------------------------------------- + * Line with 3 values, 1st value is text height. + *------------------------------------------------------------*/ + psTxt->dHeight = CPLAtof(pszLine); + if (psInfo->nPrecision == AVC_SINGLE_PREC) + { + psTxt->dV2 = CPLAtof(pszLine+14); + psTxt->dV3 = CPLAtof(pszLine+28); + } + else + { + psTxt->dV2 = CPLAtof(pszLine+21); + psTxt->dV3 = CPLAtof(pszLine+42); + } + + psInfo->iCurItem++; + } + else if (psInfo->iCurItem < (8 + ABS(psTxt->numVerticesLine) + + ABS(psTxt->numVerticesArrow)) && nLen >= 28) + { + /*------------------------------------------------------------- + * One line for each pair of X,Y coordinates + * (Lines 8 to 8+numVertices-1) + *------------------------------------------------------------*/ + psTxt->pasVertices[ psInfo->iCurItem-8 ].x = CPLAtof(pszLine); + if (psInfo->nPrecision == AVC_SINGLE_PREC) + psTxt->pasVertices[ psInfo->iCurItem-8 ].y = CPLAtof(pszLine+14); + else + psTxt->pasVertices[ psInfo->iCurItem-8 ].y = CPLAtof(pszLine+21); + + psInfo->iCurItem++; + } + else if (psInfo->iCurItem < psInfo->numItems) + { + /*------------------------------------------------------------- + * Last line, contains the text string + * Note that text can be split in 80 chars chunk and that buffer + * has been previously initialized with spaces and '\0'-terminated + *------------------------------------------------------------*/ + int numLines, iLine; + numLines = (psTxt->numChars-1)/80 + 1; + iLine = numLines - (psInfo->numItems - psInfo->iCurItem); + + if (iLine == numLines-1) + { + strncpy((char*)psTxt->pszText+(iLine*80), pszLine, + MIN( nLen, (psTxt->numChars - (iLine*80)) ) ); + } + else + { + strncpy((char*)psTxt->pszText+(iLine*80), pszLine, MIN(nLen, 80)); + } + + psInfo->iCurItem++; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 TX6/TX7 line: \"%s\"", pszLine); + psInfo->numItems = psInfo->iCurItem = 0; + return NULL; + } + + /*----------------------------------------------------------------- + * If we're done parsing this TX6/TX7, then reset the ParseInfo, + * and return a reference to the TXT structure + * Otherwise return NULL, which means that we are expecting more + * more lines of input. + *----------------------------------------------------------------*/ + if (psInfo->iCurItem >= psInfo->numItems) + { + psInfo->numItems = psInfo->iCurItem = 0; + return psTxt; + } + + return NULL; +} + + + +/********************************************************************** + * AVCE00ParseNextRxpLine() + * + * Take the next line of E00 input for an RXP object and parse it. + * + * Returns NULL if the current object is not complete yet (expecting + * more lines of input) or a reference to a complete object if it + * is complete. + * + * The returned object is a reference to an internal data structure. + * It should not be modified or freed by the caller. + * + * If the input is invalid or other problems happen, then a CPLError() + * will be generated. CPLGetLastErrorNo() should be called to check + * that the line was parsed succesfully. + **********************************************************************/ +AVCRxp *AVCE00ParseNextRxpLine(AVCE00ParseInfo *psInfo, const char *pszLine) +{ + AVCRxp *psRxp; + int nLen; + + CPLAssert(psInfo->eFileType == AVCFileRXP); + + psRxp = psInfo->cur.psRxp; + + nLen = strlen(pszLine); + + if (nLen >= 20) + { + /*------------------------------------------------------------- + * RXP Entries are only one line each: + * Value1, Value2 (meaning of the value??? Don't know!!!) + *------------------------------------------------------------*/ + psRxp->n1 = AVCE00Str2Int(pszLine, 10); + psRxp->n2 = AVCE00Str2Int(pszLine + 10, 10); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 RXP line: \"%s\"", pszLine); + psInfo->numItems = psInfo->iCurItem = 0; + return NULL; + } + + /*----------------------------------------------------------------- + * If we're done parsing this RXP, then reset the ParseInfo, + * and return a reference to the RXP structure + * Otherwise return NULL, which means that we are expecting more + * more lines of input. + *----------------------------------------------------------------*/ + if (psInfo->iCurItem >= psInfo->numItems) + { + psInfo->numItems = psInfo->iCurItem = 0; + return psRxp; + } + + return NULL; +} + + +/*===================================================================== + TABLE stuff + =====================================================================*/ + +/********************************************************************** + * AVCE00ParseNextTableDefLine() + * + * Take the next line of E00 input for an TableDef object and parse it. + * + * Returns NULL if the current object is not complete yet (expecting + * more lines of input) or a reference to a complete object if it + * is complete. + * + * The returned object is a reference to an internal data structure. + * It should not be modified or freed by the caller. + * + * If the input is invalid or other problems happen, then a CPLError() + * will be generated. CPLGetLastErrorNo() should be called to check + * that the line was parsed succesfully. + **********************************************************************/ +AVCTableDef *AVCE00ParseNextTableDefLine(AVCE00ParseInfo *psInfo, + const char *pszLine) +{ + AVCTableDef *psTableDef; + int nLen; + + CPLAssert(psInfo->eFileType == AVCFileTABLE); + + psTableDef = psInfo->hdr.psTableDef; /* May be NULL on first call */ + + nLen = strlen(pszLine); + + if (psInfo->numItems == 0) + { + /*------------------------------------------------------------- + * Begin processing a new TableDef. Read header line: + * TableName, extFlag, numFields, RecSize, numRecords + *------------------------------------------------------------*/ + if (nLen < 56) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 Table Definition line: \"%s\"", + pszLine); + return NULL; + } + else + { + /*--------------------------------------------------------- + * Parse header line and alloc and init. a new psTableDef struct + *--------------------------------------------------------*/ + psTableDef = psInfo->hdr.psTableDef = + (AVCTableDef*)CPLCalloc(1, sizeof(AVCTableDef)); + psInfo->bTableHdrComplete = FALSE; + + strncpy(psTableDef->szTableName, pszLine, 32); + psTableDef->szTableName[32] = '\0'; + strncpy(psTableDef->szExternal, pszLine+32, 2); + psTableDef->szExternal[2] = '\0'; + + psTableDef->numFields = AVCE00Str2Int(pszLine+34, 4); + psTableDef->nRecSize = AVCE00Str2Int(pszLine+42, 4); + psTableDef->numRecords = AVCE00Str2Int(pszLine+46, 10); + + /*--------------------------------------------------------- + * Alloc array of fields defs, will be filled in further calls + *--------------------------------------------------------*/ + psTableDef->pasFieldDef = + (AVCFieldInfo*)CPLCalloc(psTableDef->numFields, + sizeof(AVCFieldInfo)); + + /*--------------------------------------------------------- + * psInfo->iCurItem is the index of the last field def we read. + * psInfo->numItems is the number of field defs to read, + * including deleted ones. + *--------------------------------------------------------*/ + psInfo->numItems = AVCE00Str2Int(pszLine+38, 4); + psInfo->iCurItem = 0; + psInfo->nCurObjectId = 0; /* We'll use it as a field index */ + } + } + else if (psInfo->iCurItem < psInfo->numItems && nLen >= 69 ) + { + /*------------------------------------------------------------- + * Read an attribute field definition + * If field index is -1, then we ignore this line... we do not + * even count it in psInfo->iCurItem. + *------------------------------------------------------------*/ + int nIndex; + + nIndex = AVCE00Str2Int(pszLine + 65, 4); + + if (nIndex > 0 && psInfo->nCurObjectId >= psTableDef->numFields) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 INFO Table Header: " + "number of fields is invalid " + "(expected %d, got at least %d)", + psTableDef->numFields, psInfo->nCurObjectId+1); + psInfo->numItems = psInfo->iCurItem = psInfo->nCurObjectId; + return NULL; + } + + if (nIndex > 0) + { + AVCFieldInfo *psDef; + psDef = &(psTableDef->pasFieldDef[psInfo->iCurItem]); + + psDef->nIndex = nIndex; + + strncpy(psDef->szName, pszLine, 16); + psDef->szName[16] = '\0'; + + psDef->nSize = AVCE00Str2Int(pszLine + 16, 3); + psDef->v2 = AVCE00Str2Int(pszLine + 19, 2); + + psDef->nOffset = AVCE00Str2Int(pszLine + 21, 4); + + psDef->v4 = AVCE00Str2Int(pszLine + 25, 1); + psDef->v5 = AVCE00Str2Int(pszLine + 26, 2); + psDef->nFmtWidth= AVCE00Str2Int(pszLine + 28, 4); + psDef->nFmtPrec = AVCE00Str2Int(pszLine + 32, 2); + psDef->nType1 = AVCE00Str2Int(pszLine + 34, 3)/10; + psDef->nType2 = AVCE00Str2Int(pszLine + 34, 3)%10; + psDef->v10 = AVCE00Str2Int(pszLine + 37, 2); + psDef->v11 = AVCE00Str2Int(pszLine + 39, 4); + psDef->v12 = AVCE00Str2Int(pszLine + 43, 4); + psDef->v13 = AVCE00Str2Int(pszLine + 47, 2); + + strncpy(psDef->szAltName, pszLine+49, 16); + psDef->szAltName[16] = '\0'; + + psInfo->nCurObjectId++; + } + psInfo->iCurItem++; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 Table Definition line: \"%s\"", pszLine); + psInfo->numItems = psInfo->iCurItem = 0; + return NULL; + } + + /*----------------------------------------------------------------- + * If we're done parsing this TableDef, then reset the ParseInfo, + * and return a reference to the TableDef structure. + * Next calls should go to AVCE00ParseNextTableRecLine() to + * read data records. + * Otherwise return NULL, which means that we are expecting more + * more lines of input. + *----------------------------------------------------------------*/ + if (psInfo->iCurItem >= psInfo->numItems) + { + psInfo->numItems = psInfo->iCurItem = 0; + psInfo->nCurObjectId = 0; + + psInfo->bTableHdrComplete = TRUE; + + /*--------------------------------------------------------- + * It is possible to have a table with 0 records... in this + * case we are already at the end of the section for that table. + *--------------------------------------------------------*/ + if (psTableDef->numRecords == 0) + psInfo->bForceEndOfSection = TRUE; + + return psTableDef; + } + + return NULL; +} + +/********************************************************************** + * _AVCE00ParseTableRecord() + * + * Parse the record data present inside psInfo->pszBuf and fill and + * return the psInfo->cur.pasFields[]. + * + * This function should not be called directly... it is used by + * AVCE00ParseNextTableRecLine(). + **********************************************************************/ +static AVCField *_AVCE00ParseTableRecord(AVCE00ParseInfo *psInfo) +{ + AVCField *pasFields; + AVCFieldInfo *pasDef; + AVCTableDef *psTableDef; + int i, nType, nSize; + char szFormat[10]; + char *pszBuf, szTmp[30]; + + pasFields = psInfo->cur.pasFields; + psTableDef = psInfo->hdr.psTableDef; + pasDef = psTableDef->pasFieldDef; + + pszBuf = psInfo->pszBuf; + + for(i=0; inumFields; i++) + { + nType = pasDef[i].nType1*10; + nSize = pasDef[i].nSize; + + if (nType == AVC_FT_DATE || nType == AVC_FT_CHAR || + nType == AVC_FT_FIXINT ) + { + strncpy((char*)pasFields[i].pszStr, pszBuf, nSize); + pasFields[i].pszStr[nSize] = '\0'; + pszBuf += nSize; + } + else if (nType == AVC_FT_FIXNUM) + { + /* TYPE 40 attributes are stored with 1 byte per digit + * in binary format, and as single precision floats in + * E00 tables, even in double precision coverages. + */ + const char *pszTmpStr; + strncpy(szTmp, pszBuf, 14); + szTmp[14] = '\0'; + pszBuf += 14; + + /* Compensate for a very odd behavior observed in some E00 files. + * A type 40 field can be written in decimal format instead of + * exponent format, but in this case the decimal point is shifted + * one position to the right, resulting in a value 10 times bigger + * than expected. So if the value is not in exponent format then + * we should shift the decimal point to the left before we + * interpret it. (bug 599) + */ + if (!strchr(szTmp, 'E') && !strchr(szTmp, 'e')) + { + char *pszTmp; + if ( (pszTmp=strchr(szTmp, '.')) != NULL && pszTmp != szTmp ) + { + *pszTmp = *(pszTmp-1); + *(pszTmp-1) = '.'; + } + } + + /* We use nSize and nFmtPrec for the format because nFmtWidth can + * be different from nSize, but nSize has priority since it + * is the actual size of the field in memory. + */ + sprintf(szFormat, "%%%d.%df", nSize, pasDef[i].nFmtPrec); + pszTmpStr = CPLSPrintf(szFormat, CPLAtof(szTmp)); + + /* If value is bigger than size, then it's too bad... we + * truncate it... but this should never happen in clean datasets. + */ + if ((int)strlen(pszTmpStr) > nSize) + pszTmpStr = pszTmpStr + strlen(pszTmpStr) - nSize; + strncpy((char*)pasFields[i].pszStr, pszTmpStr, nSize); + pasFields[i].pszStr[nSize] = '\0'; + } + else if (nType == AVC_FT_BININT && nSize == 4) + { + pasFields[i].nInt32 = AVCE00Str2Int(pszBuf, 11); + pszBuf += 11; + } + else if (nType == AVC_FT_BININT && nSize == 2) + { + pasFields[i].nInt16 = AVCE00Str2Int(pszBuf, 6); + pszBuf += 6; + } + else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 4) + { + /* NOTE: The E00 representation for a binary float is + * defined by its binary size, not by the coverage's + * precision. + */ + strncpy(szTmp, pszBuf, 14); + szTmp[14] = '\0'; + pasFields[i].fFloat = (float)CPLAtof(szTmp); + pszBuf += 14; + } + else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 8) + { + /* NOTE: The E00 representation for a binary float is + * defined by its binary size, not by the coverage's + * precision. + */ + strncpy(szTmp, pszBuf, 24); + szTmp[24] = '\0'; + pasFields[i].dDouble = CPLAtof(szTmp); + pszBuf += 24; + } + else + { + /*----------------------------------------------------- + * Hummm... unsupported field type... + *----------------------------------------------------*/ + CPLError(CE_Failure, CPLE_NotSupported, + "_AVCE00ParseTableRecord(): Unsupported field type " + "(type=%d, size=%d)", + nType, pasDef[i].nSize); + return NULL; + } + } + + CPLAssert(pszBuf == psInfo->pszBuf + psInfo->nTableE00RecLength); + + return pasFields; +} + +/********************************************************************** + * AVCE00ParseNextTableRecLine() + * + * Take the next line of E00 input for an Table data record and parse it. + * + * Returns NULL if the current record is not complete yet (expecting + * more lines of input) or a reference to a complete record if it + * is complete. + * + * The returned record is a reference to an internal data structure. + * It should not be modified or freed by the caller. + * + * If the input is invalid or other problems happen, then a CPLError() + * will be generated. CPLGetLastErrorNo() should be called to check + * that the line was parsed succesfully. + **********************************************************************/ +AVCField *AVCE00ParseNextTableRecLine(AVCE00ParseInfo *psInfo, + const char *pszLine) +{ + AVCField *pasFields = NULL; + AVCTableDef *psTableDef; + int i; + + CPLAssert(psInfo->eFileType == AVCFileTABLE); + + psTableDef = psInfo->hdr.psTableDef; + + if (psInfo->bForceEndOfSection || psTableDef->numFields == 0 || + psTableDef->numRecords == 0) + { + psInfo->bForceEndOfSection = TRUE; + return NULL; + } + + /*----------------------------------------------------------------- + * On the first call for a new table, we have some allocations to + * do: + * - make sure the psInfo->szBuf is big enough to hold one complete + * E00 data record. + * - Alloc the array of Field values (psInfo->cur.pasFields[]) + * for the number of fields in this table. + *----------------------------------------------------------------*/ + if (psInfo->numItems == 0 && psInfo->nCurObjectId == 0) + { + /*------------------------------------------------------------- + * Realloc E00 buffer + *------------------------------------------------------------*/ + psInfo->nTableE00RecLength = + _AVCE00ComputeRecSize(psTableDef->numFields, + psTableDef->pasFieldDef, FALSE); + + if (psInfo->nBufSize < psInfo->nTableE00RecLength + 1) + { + psInfo->nBufSize = psInfo->nTableE00RecLength + 1; + psInfo->pszBuf = (char*)CPLRealloc(psInfo->pszBuf, + psInfo->nBufSize); + } + + /*--------------------------------------------------------- + * Alloc psInfo->cur.pasFields[] + * Also alloc buffers for string attributes. + *--------------------------------------------------------*/ + psInfo->cur.pasFields = (AVCField*)CPLCalloc(psTableDef->numFields, + sizeof(AVCField)); + for(i=0; inumFields; i++) + { + if (psTableDef->pasFieldDef[i].nType1*10 == AVC_FT_DATE || + psTableDef->pasFieldDef[i].nType1*10 == AVC_FT_CHAR || + psTableDef->pasFieldDef[i].nType1*10 == AVC_FT_FIXINT || + psTableDef->pasFieldDef[i].nType1*10 == AVC_FT_FIXNUM ) + { + psInfo->cur.pasFields[i].pszStr = + (GByte*)CPLCalloc(psTableDef->pasFieldDef[i].nSize+1, + sizeof(GByte)); + } + } + + } + + if (psInfo->numItems == 0) + { + /*----------------------------------------------------------------- + * Begin processing a new record... we'll accumulate the 80 + * chars lines until we have the whole record in our buffer + * and parse it only at the end. + * Lines shorter than 80 chars are legal, and in this case + * they will be padded with spaces up to 80 chars. + *----------------------------------------------------------------*/ + + /*--------------------------------------------------------- + * First fill the whole record buffer with spaces we'll just + * paste lines in it using strncpy() + *--------------------------------------------------------*/ + memset(psInfo->pszBuf, ' ', psInfo->nTableE00RecLength); + psInfo->pszBuf[psInfo->nTableE00RecLength] = '\0'; + + + /*--------------------------------------------------------- + * psInfo->iCurItem is the number of chars buffered so far. + * psInfo->numItems is the number of chars to expect in one record. + *--------------------------------------------------------*/ + psInfo->numItems = psInfo->nTableE00RecLength; + psInfo->iCurItem = 0; + } + + + if (psInfo->iCurItem < psInfo->numItems) + { + /*------------------------------------------------------------- + * Continue to accumulate the 80 chars lines until we have + * the whole record in our buffer. We'll parse it only at the end. + * Lines shorter than 80 chars are legal, and in this case + * they padded with spaces up to 80 chars. + *------------------------------------------------------------*/ + int nSrcLen, nLenToCopy; + + nSrcLen = strlen(pszLine); + nLenToCopy = MIN(80, MIN(nSrcLen,(psInfo->numItems-psInfo->iCurItem))); + strncpy(psInfo->pszBuf+psInfo->iCurItem, pszLine, nLenToCopy); + + psInfo->iCurItem+=80; + } + + + if (psInfo->iCurItem >= psInfo->numItems) + { + /*------------------------------------------------------------- + * OK, we've got one full record in the buffer... parse it and + * return the pasFields[] + *------------------------------------------------------------*/ + pasFields = _AVCE00ParseTableRecord(psInfo); + + if (pasFields == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error parsing E00 Table Record: \"%s\"", + psInfo->pszBuf); + return NULL; + } + + psInfo->numItems = psInfo->iCurItem = 0; + psInfo->nCurObjectId++; + } + + /*----------------------------------------------------------------- + * Since there is no explicit "end of table" line, we set the + * bForceEndOfSection flag when the last record is read. + *----------------------------------------------------------------*/ + if (psInfo->nCurObjectId >= psTableDef->numRecords) + { + psInfo->bForceEndOfSection = TRUE; + } + + return pasFields; +} + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_e00read.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_e00read.c new file mode 100644 index 000000000..b7f5932ce --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_e00read.c @@ -0,0 +1,2181 @@ +/********************************************************************** + * $Id: avc_e00read.c,v 1.28 2008/07/30 19:22:18 dmorissette Exp $ + * + * Name: avc_e00read.c + * Project: Arc/Info vector coverage (AVC) BIN->E00 conversion library + * Language: ANSI C + * Purpose: Functions to open a binary coverage and read it as if it + * was an ASCII E00 file. This file is the main entry point + * for the library. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2005, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: avc_e00read.c,v $ + * Revision 1.28 2008/07/30 19:22:18 dmorissette + * Move detection of EXP header directly in AVCE00ReadOpenE00() and use + * VSIFGets() instead of CPLReadLine() to avoid problem with huge one line + * files (GDAL/OGR ticket #1989) + * + * Revision 1.27 2008/07/30 18:35:53 dmorissette + * Avoid scanning the whole E00 input file in AVCE00ReadOpenE00() if the + * file does not start with an EXP line (GDAL/OGR ticket 1989) + * + * Revision 1.26 2008/07/30 16:17:46 dmorissette + * Detect compressed E00 input files and refuse to open them instead of + * crashing (bug 1928, GDAL/OGR ticket 2513) + * + * Revision 1.25 2008/07/24 20:34:12 dmorissette + * Fixed VC++ WIN32 build problems in GDAL/OGR environment + * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2500) + * + * Revision 1.24 2008/07/24 13:49:20 dmorissette + * Fixed GCC compiler warning (GDAL ticket #2495) + * + * Revision 1.23 2006/08/17 19:51:01 dmorissette + * #include to solve warning on 64 bit platforms (bug 1461) + * + * Revision 1.22 2006/08/17 18:56:42 dmorissette + * Support for reading standalone info tables (just tables, no coverage + * data) by pointing AVCE00ReadOpen() to the info directory (bug 1549). + * + * Revision 1.21 2006/06/27 18:38:43 dmorissette + * Cleaned up E00 reading (bug 1497, patch from James F.) + * + * Revision 1.20 2006/06/27 18:06:34 dmorissette + * Applied patch for EOP processing from James F. (bug 1497) + * + * Revision 1.19 2006/06/16 11:48:11 daniel + * New functions to read E00 files directly as opposed to translating to + * binary coverage. Used in the implementation of E00 read support in OGR. + * Contributed by James E. Flemer. (bug 1497) + * + * Revision 1.18 2006/06/14 16:31:28 daniel + * Added support for AVCCoverPC2 type (bug 1491) + * + * Revision 1.17 2005/06/03 03:49:58 daniel + * Update email address, website url, and copyright dates + * + * Revision 1.16 2004/07/14 18:49:50 daniel + * Fixed leak when trying to open something that's not a coverage (bug513) + * + * Revision 1.15 2002/08/27 15:46:15 daniel + * Applied fix made in GDAL/OGR by 'aubin' (moved include ctype.h after avc.h) + * + * Revision 1.14 2000/09/22 19:45:21 daniel + * Switch to MIT-style license + * + * Revision 1.13 2000/05/29 15:31:31 daniel + * Added Japanese DBCS support + * + * Revision 1.12 2000/02/14 17:21:01 daniel + * Made more robust for corrupted or invalid files in cover directory + * + * Revision 1.11 2000/02/02 04:26:04 daniel + * Support reading TX6/TX7/RXP/RPL files in weird coverages + * + * Revision 1.10 2000/01/10 02:56:30 daniel + * Added read support for "weird" coverages + * + * Revision 1.9 2000/01/07 07:12:49 daniel + * Added support for reading PC Coverage TXT files + * + * Revision 1.8 1999/12/24 07:41:08 daniel + * Check fname length before testing for extension in AVCE00ReadFindCoverType() + * + * Revision 1.7 1999/12/24 07:18:34 daniel + * Added PC Arc/Info coverages support + * + * Revision 1.6 1999/08/26 17:22:18 daniel + * Use VSIFopen() instead of fopen() directly + * + * Revision 1.5 1999/08/23 18:21:41 daniel + * New syntax for AVCBinReadListTables() + * + * Revision 1.4 1999/05/11 02:10:01 daniel + * Free psInfo struct inside AVCE00ReadClose() + * + * Revision 1.3 1999/04/06 19:43:26 daniel + * Added E00 coverage path in EXP 0 header line + * + * Revision 1.2 1999/02/25 04:19:01 daniel + * Added TXT, TX6/TX7, RXP and RPL support + other minor changes + * + * Revision 1.1 1999/01/29 16:28:52 daniel + * Initial revision + * + **********************************************************************/ + +#include "avc.h" + +#ifdef WIN32 +# include /* getcwd() */ +#else +# include /* getcwd() */ +#endif + +#include /* toupper() */ + +static void _AVCE00ReadScanE00(AVCE00ReadE00Ptr psRead); +static int _AVCE00ReadBuildSqueleton(AVCE00ReadPtr psInfo, + char **papszCoverDir); +static AVCCoverType _AVCE00ReadFindCoverType(char **papszCoverDir); + + +/********************************************************************** + * AVCE00ReadOpen() + * + * Open a Arc/Info coverage to read it as if it was an E00 file. + * + * You can either pass the name of the coverage directory, or the path + * to one of the files in the coverage directory. The name of the + * coverage MUST be included in pszCoverPath... this means that + * passing "." is invalid. + * The following are all valid values for pszCoverPath: + * /home/data/country + * /home/data/country/ + * /home/data/country/arc.adf + * (Of course you should replace the '/' with '\\' on DOS systems!) + * + * Returns a new AVCE00ReadPtr handle or NULL if the coverage could + * not be opened or if it does not appear to be a valid Arc/Info coverage. + * + * The handle will eventually have to be released with AVCE00ReadClose(). + **********************************************************************/ +AVCE00ReadPtr AVCE00ReadOpen(const char *pszCoverPath) +{ + AVCE00ReadPtr psInfo; + int i, nLen, nCoverPrecision; + VSIStatBuf sStatBuf; + char **papszCoverDir = NULL; + + CPLErrorReset(); + + /*----------------------------------------------------------------- + * pszCoverPath must be either a valid directory name or a valid + * file name. + *----------------------------------------------------------------*/ + if (pszCoverPath == NULL || strlen(pszCoverPath) == 0 || + VSIStat(pszCoverPath, &sStatBuf) == -1) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Invalid coverage path: %s.", + pszCoverPath?pszCoverPath:"(NULL)"); + return NULL; + } + + /*----------------------------------------------------------------- + * Alloc the AVCE00ReadPtr handle + *----------------------------------------------------------------*/ + psInfo = (AVCE00ReadPtr)CPLCalloc(1, sizeof(struct AVCE00ReadInfo_t)); + + /*----------------------------------------------------------------- + * 2 possibilities about the value passed in pszCoverPath: + * - It can be the directory name of the coverage + * - or it can be the path to one of the files in the coverage + * + * If the name passed in pszCoverPath is not a directory, then we + * need to strip the last part of the filename to keep only the + * path, terminated by a '/' (or a '\\'). + *----------------------------------------------------------------*/ + if (VSI_ISDIR(sStatBuf.st_mode)) + { + /*------------------------------------------------------------- + * OK, we have a valid directory name... make sure it is + * terminated with a '/' (or '\\') + *------------------------------------------------------------*/ + nLen = strlen(pszCoverPath); + + if (pszCoverPath[nLen-1] == '/' || pszCoverPath[nLen-1] == '\\') + psInfo->pszCoverPath = CPLStrdup(pszCoverPath); + else + { +#ifdef WIN32 + psInfo->pszCoverPath = CPLStrdup(CPLSPrintf("%s\\",pszCoverPath)); +#else + psInfo->pszCoverPath = CPLStrdup(CPLSPrintf("%s/",pszCoverPath)); +#endif + } + } + else + { + /*------------------------------------------------------------- + * We are dealing with a filename. + * Extract the coverage path component and store it. + * The coverage path will remain terminated by a '/' or '\\' char. + *------------------------------------------------------------*/ + psInfo->pszCoverPath = CPLStrdup(pszCoverPath); + + for( i = strlen(psInfo->pszCoverPath)-1; + i > 0 && psInfo->pszCoverPath[i] != '/' && + psInfo->pszCoverPath[i] != '\\'; + i-- ) {} + + psInfo->pszCoverPath[i+1] = '\0'; + } + + + /*----------------------------------------------------------------- + * Extract the coverage name from the coverage path. Note that + * for this the coverage path must be in the form: + * "dir1/dir2/dir3/covername/" ... if it is not the case, then + * we would have to use getcwd() to find the current directory name... + * but for now we'll just produce an error if this happens. + *----------------------------------------------------------------*/ + nLen = 0; + for( i = strlen(psInfo->pszCoverPath)-1; + i > 0 && psInfo->pszCoverPath[i-1] != '/' && + psInfo->pszCoverPath[i-1] != '\\'&& + psInfo->pszCoverPath[i-1] != ':'; + i-- ) + { + nLen++; + } + + if (nLen > 0) + { + psInfo->pszCoverName = CPLStrdup(psInfo->pszCoverPath+i); + psInfo->pszCoverName[nLen] = '\0'; + } + else + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Invalid coverage path (%s): " + "coverage name must be included in path.", pszCoverPath); + + CPLFree(psInfo->pszCoverPath); + CPLFree(psInfo); + return NULL; + } + + /*----------------------------------------------------------------- + * Read the coverage directory listing and try to establish the cover type + *----------------------------------------------------------------*/ + papszCoverDir = CPLReadDir(psInfo->pszCoverPath); + + psInfo->eCoverType = _AVCE00ReadFindCoverType(papszCoverDir); + + if (psInfo->eCoverType == AVCCoverTypeUnknown ) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Invalid coverage (%s): directory does not appear to " + "contain any supported vector coverage file.", pszCoverPath); + CPLFree(psInfo->pszCoverName); + CPLFree(psInfo->pszCoverPath); + CPLFree(psInfo->pszInfoPath); + CPLFree(psInfo); + CSLDestroy(papszCoverDir); + return NULL; + } + + + /*----------------------------------------------------------------- + * INFO path: PC Coverages have all files in the same dir, and unix + * covers have the INFO files in ../info + *----------------------------------------------------------------*/ + if (psInfo->eCoverType == AVCCoverPC || psInfo->eCoverType == AVCCoverPC2) + { + psInfo->pszInfoPath = CPLStrdup(psInfo->pszCoverPath); + } + else + { + /*------------------------------------------------------------- + * Lazy way to build the INFO path: simply add "../info/"... + * this could probably be improved! + *------------------------------------------------------------*/ + psInfo->pszInfoPath =(char*)CPLMalloc((strlen(psInfo->pszCoverPath)+9)* + sizeof(char)); +#ifdef WIN32 +# define AVC_INFOPATH "..\\info\\" +#else +# define AVC_INFOPATH "../info/" +#endif + sprintf(psInfo->pszInfoPath, "%s%s", psInfo->pszCoverPath, + AVC_INFOPATH); + + AVCAdjustCaseSensitiveFilename(psInfo->pszInfoPath); + } + + /*----------------------------------------------------------------- + * For Unix coverages, check that the info directory exists and + * contains the "arc.dir". In AVCCoverWeird, the arc.dir is + * called "../INFO/ARCDR9". + * PC Coverages have their info tables in the same direcotry as + * the coverage files. + *----------------------------------------------------------------*/ + if (((psInfo->eCoverType == AVCCoverV7 || + psInfo->eCoverType == AVCCoverV7Tables) && + ! AVCFileExists(psInfo->pszInfoPath, "arc.dir") ) || + (psInfo->eCoverType == AVCCoverWeird && + ! AVCFileExists(psInfo->pszInfoPath, "arcdr9") ) ) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Invalid coverage (%s): 'info' directory not found or invalid.", + pszCoverPath); + CPLFree(psInfo->pszCoverName); + CPLFree(psInfo->pszCoverPath); + CPLFree(psInfo->pszInfoPath); + CPLFree(psInfo); + CSLDestroy(papszCoverDir); + return NULL; + } + + /*----------------------------------------------------------------- + * Make sure there was no error until now before we build squeleton. + *----------------------------------------------------------------*/ + if (CPLGetLastErrorNo() != 0) + { + CPLFree(psInfo->pszCoverName); + CPLFree(psInfo->pszCoverPath); + CPLFree(psInfo->pszInfoPath); + CPLFree(psInfo); + CSLDestroy(papszCoverDir); + return NULL; + } + + /*----------------------------------------------------------------- + * Build the E00 file squeleton and be ready to return a E00 header... + * We'll also read the coverage precision by the same way. + *----------------------------------------------------------------*/ + nCoverPrecision = _AVCE00ReadBuildSqueleton(psInfo, papszCoverDir); + + /* Ignore warnings produced while building squeleton */ + CPLErrorReset(); + + CSLDestroy(papszCoverDir); + papszCoverDir = NULL; + + psInfo->iCurSection = 0; + psInfo->iCurStep = AVC_GEN_NOTSTARTED; + psInfo->bReadAllSections = TRUE; + + /*----------------------------------------------------------------- + * Init the E00 generator. + *----------------------------------------------------------------*/ + psInfo->hGenInfo = AVCE00GenInfoAlloc(nCoverPrecision); + + /*----------------------------------------------------------------- + * Init multibyte encoding info + *----------------------------------------------------------------*/ + psInfo->psDBCSInfo = AVCAllocDBCSInfo(); + + /*----------------------------------------------------------------- + * If an error happened during the open call, cleanup and return NULL. + *----------------------------------------------------------------*/ + if (CPLGetLastErrorNo() != 0) + { + AVCE00ReadClose(psInfo); + psInfo = NULL; + } + + return psInfo; +} + +/********************************************************************** + * AVCE00ReadOpenE00() + * + * Open a E00 file for reading. + * + * Returns a new AVCE00ReadE00Ptr handle or NULL if the file could + * not be opened or if it does not appear to be a valid E00 file. + * + * The handle will eventually have to be released with + * AVCE00ReadCloseE00(). + **********************************************************************/ +AVCE00ReadE00Ptr AVCE00ReadOpenE00(const char *pszE00FileName) +{ + AVCE00ReadE00Ptr psRead; + VSIStatBuf sStatBuf; + FILE *fp; + char *p; + char szHeader[10]; + + CPLErrorReset(); + + /*----------------------------------------------------------------- + * pszE00FileName must be a valid file that can be opened for + * reading + *----------------------------------------------------------------*/ + if (pszE00FileName == NULL || strlen(pszE00FileName) == 0 || + VSIStat(pszE00FileName, &sStatBuf) == -1 || + VSI_ISDIR(sStatBuf.st_mode)) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Invalid E00 file path: %s.", + pszE00FileName?pszE00FileName:"(NULL)"); + return NULL; + } + + if (NULL == (fp = VSIFOpen(pszE00FileName, "r"))) + return NULL; + + /*----------------------------------------------------------------- + * Make sure the file starts with a "EXP 0" or "EXP 1" header + *----------------------------------------------------------------*/ + if (VSIFGets(szHeader, 5, fp) == NULL || !EQUALN("EXP ", szHeader, 4) ) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "This does not look like a E00 file: does not start with " + "a EXP header." ); + VSIFClose(fp); + return NULL; + } + VSIRewind(fp); + + /*----------------------------------------------------------------- + * Alloc the AVCE00ReadE00Ptr handle + *----------------------------------------------------------------*/ + psRead = (AVCE00ReadE00Ptr)CPLCalloc(1, + sizeof(struct AVCE00ReadInfoE00_t)); + + psRead->hFile = fp; + psRead->pszCoverPath = CPLStrdup(pszE00FileName); + psRead->eCurFileType = AVCFileUnknown; + + /*----------------------------------------------------------------- + * Extract the coverage name from the coverage path. + *----------------------------------------------------------------*/ + if (NULL != (p = strrchr(psRead->pszCoverPath, '/')) || + NULL != (p = strrchr(psRead->pszCoverPath, '\\')) || + NULL != (p = strrchr(psRead->pszCoverPath, ':'))) + { + psRead->pszCoverName = CPLStrdup(p + 1); + } + else + { + psRead->pszCoverName = CPLStrdup(psRead->pszCoverPath); + } + if (NULL != (p = strrchr(psRead->pszCoverName, '.'))) + { + *p = '\0'; + } + + /*----------------------------------------------------------------- + * Make sure there was no error until now before we scan file. + *----------------------------------------------------------------*/ + if (CPLGetLastErrorNo() != 0) + { + AVCE00ReadCloseE00(psRead); + return NULL; + } + + psRead->hParseInfo = AVCE00ParseInfoAlloc(); + + /*----------------------------------------------------------------- + * Scan the E00 file for sections + *----------------------------------------------------------------*/ + _AVCE00ReadScanE00(psRead); + if (CPLGetLastErrorNo() != 0) + { + AVCE00ReadCloseE00(psRead); + return NULL; + } + + AVCE00ReadRewindE00(psRead); + CPLErrorReset(); + + if (psRead->numSections < 1) + { + AVCE00ReadCloseE00(psRead); + return NULL; + } + + psRead->bReadAllSections = TRUE; + + /*----------------------------------------------------------------- + * If an error happened during the open call, cleanup and return NULL. + *----------------------------------------------------------------*/ + if (CPLGetLastErrorNo() != 0) + { + AVCE00ReadCloseE00(psRead); + psRead = NULL; + } + + return psRead; +} + +/********************************************************************** + * AVCE00ReadClose() + * + * Close a coverage and release all memory used by the AVCE00ReadPtr + * handle. + **********************************************************************/ +void AVCE00ReadClose(AVCE00ReadPtr psInfo) +{ + CPLErrorReset(); + + if (psInfo == NULL) + return; + + CPLFree(psInfo->pszCoverPath); + CPLFree(psInfo->pszInfoPath); + CPLFree(psInfo->pszCoverName); + + if (psInfo->hFile) + AVCBinReadClose(psInfo->hFile); + + if (psInfo->hGenInfo) + AVCE00GenInfoFree(psInfo->hGenInfo); + + if (psInfo->pasSections) + { + int i; + for(i=0; inumSections; i++) + { + CPLFree(psInfo->pasSections[i].pszName); + CPLFree(psInfo->pasSections[i].pszFilename); + } + CPLFree(psInfo->pasSections); + } + + AVCFreeDBCSInfo(psInfo->psDBCSInfo); + + CPLFree(psInfo); +} + +/********************************************************************** + * AVCE00ReadCloseE00() + * + * Close a coverage and release all memory used by the AVCE00ReadE00Ptr + * handle. + **********************************************************************/ +void AVCE00ReadCloseE00(AVCE00ReadE00Ptr psRead) +{ + if (psRead == NULL) + return; + + CPLFree(psRead->pszCoverPath); + CPLFree(psRead->pszCoverName); + + if (psRead->hFile) + { + VSIFClose(psRead->hFile); + psRead->hFile = 0; + } + + if (psRead->pasSections) + { + int i; + for(i=0; inumSections; i++) + { + CPLFree(psRead->pasSections[i].pszName); + CPLFree(psRead->pasSections[i].pszFilename); + } + CPLFree(psRead->pasSections); + } + + /* These Free calls handle NULL's */ + AVCE00ParseInfoFree(psRead->hParseInfo); + psRead->hParseInfo = NULL; + + CPLFree(psRead); +} + + +/********************************************************************** + * _AVCIncreaseSectionsArray() + * + * Add a number of structures to the Sections array and return the + * index of the first one that was added. Note that the address of the + * original array (*pasArray) is quite likely to change! + * + * The value of *pnumItems will be updated to reflect the new array size. + **********************************************************************/ +static int _AVCIncreaseSectionsArray(AVCE00Section **pasArray, int *pnumItems, + int numToAdd) +{ + int i; + + *pasArray = (AVCE00Section*)CPLRealloc(*pasArray, + (*pnumItems+numToAdd)* + sizeof(AVCE00Section)); + + for(i=0; i 4 && EQUAL(papszCoverDir[i]+nLen-4, ".adf") ) + { + bFoundAdfFile = TRUE; + } + else if (nLen > 4 && EQUAL(papszCoverDir[i]+nLen-4, ".dbf") ) + { + bFoundDbfFile = TRUE; + } + else if (EQUAL(papszCoverDir[i], "arc") || + EQUAL(papszCoverDir[i], "cnt") || + EQUAL(papszCoverDir[i], "pal") || + EQUAL(papszCoverDir[i], "lab") || + EQUAL(papszCoverDir[i], "prj") || + EQUAL(papszCoverDir[i], "tol") ) + { + bFoundArcFile = TRUE; + } + else if (EQUAL(papszCoverDir[i], "aat") || + EQUAL(papszCoverDir[i], "pat") || + EQUAL(papszCoverDir[i], "bnd") || + EQUAL(papszCoverDir[i], "tic") ) + { + bFoundTableFile = TRUE; + } + else if (EQUAL(papszCoverDir[i], "arc.dir") ) + { + bFoundArcDirFile = TRUE; + } + + } + + /*----------------------------------------------------------------- + * Check for PC Arc/Info coverage - variant 1. + * These PC coverages have files with no extension (e.g. "ARC","PAL",...) + * and their tables filenames are in the form "???.dbf" + *----------------------------------------------------------------*/ + if (bFoundArcFile && bFoundDbfFile) + return AVCCoverPC; + + /*----------------------------------------------------------------- + * Check for PC Arc/Info coverage - variant 2. + * looks like a hybrid between AVCCoverPC and AVCCoverV7 + * These PC coverages have files with .adf extension (e.g."ARC.ADF"), + * and their tables filenames are in the form "???.dbf" + *----------------------------------------------------------------*/ + if (bFoundAdfFile && bFoundDbfFile) + return AVCCoverPC2; + + /*----------------------------------------------------------------- + * Check for the weird coverages. + * Their coverage files have no extension just like PC Coverages, + * and their tables have 3 letters filenames with no extension + * either (e.g. "AAT", "PAT", etc.) + * They also have a ../info directory, but we don't really need + * to check that (not yet!). + *----------------------------------------------------------------*/ + if (bFoundArcFile && bFoundTableFile) + return AVCCoverWeird; + + /*----------------------------------------------------------------- + * V7 Coverages... they are the easiest to recognize + * because of the ".adf" file extension + *----------------------------------------------------------------*/ + if (bFoundAdfFile) + return AVCCoverV7; + + /*----------------------------------------------------------------- + * Standalone info tables. + * We were pointed at the "info" directory. We'll treat this as + * a coverage with just info tables. + *----------------------------------------------------------------*/ + if (bFoundArcDirFile) + return AVCCoverV7Tables; + + return AVCCoverTypeUnknown; +} + + +/********************************************************************** + * _AVCE00ReadAddJabberwockySection() + * + * Add to the squeleton a section that contains subsections + * for all the files with a given extension. + * + * Returns Updated Coverage precision + **********************************************************************/ +static int _AVCE00ReadAddJabberwockySection(AVCE00ReadPtr psInfo, + AVCFileType eFileType, + const char *pszSectionName, + int nCoverPrecision, + const char *pszFileExtension, + char **papszCoverDir ) +{ + int iSect, iDirEntry, nLen, nExtLen; + GBool bFoundFiles = FALSE; + AVCBinFile *psFile=NULL; + + nExtLen = strlen(pszFileExtension); + + /*----------------------------------------------------------------- + * Scan the directory for files with a ".txt" extension. + *----------------------------------------------------------------*/ + + for (iDirEntry=0; papszCoverDir && papszCoverDir[iDirEntry]; iDirEntry++) + { + nLen = strlen(papszCoverDir[iDirEntry]); + + if (nLen > nExtLen && EQUAL(papszCoverDir[iDirEntry] + nLen-nExtLen, + pszFileExtension) && + (psFile = AVCBinReadOpen(psInfo->pszCoverPath, + papszCoverDir[iDirEntry], + psInfo->eCoverType, eFileType, + psInfo->psDBCSInfo)) != NULL) + { + if (nCoverPrecision == AVC_DEFAULT_PREC) + nCoverPrecision = psFile->nPrecision; + AVCBinReadClose(psFile); + + if (bFoundFiles == FALSE) + { + /* Insert a "TX6 #" header before the first TX6 file + */ + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + psInfo->pasSections[iSect].eType = AVCFileUnknown; + + psInfo->pasSections[iSect].pszName = + CPLStrdup(CPLSPrintf("%s %c", pszSectionName, + (nCoverPrecision==AVC_DOUBLE_PREC)?'3':'2')); + + bFoundFiles = TRUE; + } + + /* Add this file to the squeleton + */ + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + + psInfo->pasSections[iSect].eType = eFileType; + psInfo->pasSections[iSect].pszFilename= + CPLStrdup(papszCoverDir[iDirEntry]); + + /* pszName will contain only the classname without the file + * extension */ + psInfo->pasSections[iSect].pszName = + CPLStrdup(papszCoverDir[iDirEntry]); + psInfo->pasSections[iSect].pszName[nLen-nExtLen] = '\0'; + } + } + + if (bFoundFiles) + { + /* Add a line to close the TX6 section. + */ + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + psInfo->pasSections[iSect].eType = AVCFileUnknown; + psInfo->pasSections[iSect].pszName = CPLStrdup("JABBERWOCKY"); + } + + return nCoverPrecision; +} + +/********************************************************************** + * _AVCE00ReadNextLineE00() + * + * Processes the next line of input from the E00 file. + * (See AVCE00WriteNextLine() for similar processing.) + * + * Returns the next object from the E00 file, or NULL. + **********************************************************************/ +static void *_AVCE00ReadNextLineE00(AVCE00ReadE00Ptr psRead, + const char *pszLine) +{ + /* int nStatus = 0; */ + void *psObj = 0; + + AVCE00ParseInfo *psInfo = psRead->hParseInfo; + + CPLErrorReset(); + + ++psInfo->nCurLineNum; + + if (psInfo->bForceEndOfSection) + { + /*------------------------------------------------------------- + * The last call encountered an implicit end of section, so + * we close the section now without waiting for an end-of-section + * line (there won't be any!)... and get ready to proceed with + * the next section. + * This is used for TABLEs. + *------------------------------------------------------------*/ + AVCE00ParseSectionEnd(psInfo, pszLine, TRUE); + psRead->eCurFileType = AVCFileUnknown; + } + + /*----------------------------------------------------------------- + * If we're at the top level inside a supersection... check if this + * supersection ends here. + *----------------------------------------------------------------*/ + if (AVCE00ParseSuperSectionEnd(psInfo, pszLine) == TRUE) + { + /* Nothing to do... it's all been done by the call to + * AVCE00ParseSuperSectionEnd() + */ + } + else if (psRead->eCurFileType == AVCFileUnknown) + { + /*------------------------------------------------------------- + * We're at the top level or inside a supersection... waiting + * to encounter a valid section or supersection header + * (i.e. "ARC 2", etc...) + *------------------------------------------------------------*/ + + /*------------------------------------------------------------- + * First check for a supersection header (TX6, RXP, IFO, ...) + *------------------------------------------------------------*/ + if ( AVCE00ParseSuperSectionHeader(psInfo, + pszLine) == AVCFileUnknown ) + { + /*--------------------------------------------------------- + * This was not a supersection header... check if it's a simple + * section header + *--------------------------------------------------------*/ + psRead->eCurFileType = AVCE00ParseSectionHeader(psInfo, + pszLine); + } + else + { + /* got supersection */ + } + + if (psRead->eCurFileType == AVCFileTABLE) + { + /*--------------------------------------------------------- + * send the first header line to the parser and wait until + * the whole header has been read. + *--------------------------------------------------------*/ + AVCE00ParseNextLine(psInfo, pszLine); + } + else if (psRead->eCurFileType != AVCFileUnknown) + { + /*--------------------------------------------------------- + * found a valid section header + *--------------------------------------------------------*/ + } + } + else if (psRead->eCurFileType == AVCFileTABLE && + ! psInfo->bTableHdrComplete ) + { + /*------------------------------------------------------------- + * We're reading a TABLE header... continue reading lines + * from the header + * + * Note: When parsing a TABLE, the first object returned will + * be the AVCTableDef, then data records will follow. + *------------------------------------------------------------*/ + psObj = AVCE00ParseNextLine(psInfo, pszLine); + if (psObj) + { + /* got table header */ + /* TODO: Enable return of table definition? */ + psObj = NULL; + } + } + else + { + /*------------------------------------------------------------- + * We're are in the middle of a section... first check if we + * have reached the end. + * + * note: The first call to AVCE00ParseSectionEnd() with FALSE will + * not reset the parser until we close the file... and then + * we call the function again to reset the parser. + *------------------------------------------------------------*/ + if (AVCE00ParseSectionEnd(psInfo, pszLine, FALSE)) + { + psRead->eCurFileType = AVCFileUnknown; + AVCE00ParseSectionEnd(psInfo, pszLine, TRUE); + } + else + /*------------------------------------------------------------- + * ... not at the end yet, so continue reading objects. + *------------------------------------------------------------*/ + { + psObj = AVCE00ParseNextLine(psInfo, pszLine); + + if (psObj) + { + /* got object */ + } + } + } + +#if 0 + if (CPLGetLastErrorNo() != 0) + nStatus = -1; +#endif + + return psObj; +} + +/********************************************************************** + * _AVCE00ReadBuildSqueleton() + * + * Build the squeleton of the E00 file corresponding to the specified + * coverage and set the appropriate fields in the AVCE00ReadPtr struct. + * + * Note that the order of the sections in the squeleton is important + * since some software may rely on this ordering when they read E00 files. + * + * The function returns the coverage precision that it will read from one + * of the file headers. + **********************************************************************/ +static int _AVCE00ReadBuildSqueleton(AVCE00ReadPtr psInfo, + char **papszCoverDir) +{ + int iSect, iTable, numTables, iFile, nLen; + char **papszTables, **papszFiles, szCWD[75]="", *pcTmp; + char *pszEXPPath=NULL; + int nCoverPrecision = AVC_DEFAULT_PREC; + char cPrecisionCode = '2'; + const char *szFname = NULL; + AVCBinFile *psFile=NULL; + + psInfo->numSections = 0; + psInfo->pasSections = NULL; + + /*----------------------------------------------------------------- + * Build the absolute coverage path to include in the EXP 0 line + * This line usually contains the full path of the E00 file that + * is being created, but since the lib does not write the output + * file directly, there is no simple way to get that value. Instead, + * we will use the absolute coverage path to which we add a .E00 + * extension. + * We need also make sure cover path is all in uppercase. + *----------------------------------------------------------------*/ +#ifdef WIN32 + if (psInfo->pszCoverPath[0] != '\\' && + !(isalpha(psInfo->pszCoverPath[0]) && psInfo->pszCoverPath[1] == ':')) +#else + if (psInfo->pszCoverPath[0] != '/') +#endif + { + if (getcwd(szCWD, 74) == NULL) + szCWD[0] = '\0'; /* Failed: buffer may be too small */ + + nLen = strlen(szCWD); + +#ifdef WIN32 + if (nLen > 0 && szCWD[nLen -1] != '\\') + strcat(szCWD, "\\"); +#else + if (nLen > 0 && szCWD[nLen -1] != '/') + strcat(szCWD, "/"); +#endif + } + + pszEXPPath = CPLStrdup(CPLSPrintf("EXP 0 %s%-.*s.E00", szCWD, + (int)strlen(psInfo->pszCoverPath)-1, + psInfo->pszCoverPath)); + pcTmp = pszEXPPath; + for( ; *pcTmp != '\0'; pcTmp++) + *pcTmp = toupper(*pcTmp); + + /*----------------------------------------------------------------- + * EXP Header + *----------------------------------------------------------------*/ + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + psInfo->pasSections[iSect].eType = AVCFileUnknown; + psInfo->pasSections[iSect].pszName = pszEXPPath; + + /*----------------------------------------------------------------- + * We have to try to open each file as we go for 2 reasons: + * - To validate the file's signature in order to detect cases like a user + * that places files such as "mystuff.txt" in the cover directory... + * this has already happened and obviously lead to problems!) + * - We also need to find the coverage's precision from the headers + *----------------------------------------------------------------*/ + + /*----------------------------------------------------------------- + * ARC section (arc.adf) + *----------------------------------------------------------------*/ + szFname = (psInfo->eCoverType==AVCCoverV7 || + psInfo->eCoverType==AVCCoverPC2 ) ? "arc.adf": "arc"; + if ( (iFile=CSLFindString(papszCoverDir, szFname)) != -1 && + (psFile = AVCBinReadOpen(psInfo->pszCoverPath, szFname, + psInfo->eCoverType, AVCFileARC, + psInfo->psDBCSInfo)) != NULL) + { + if (nCoverPrecision == AVC_DEFAULT_PREC) + nCoverPrecision = psFile->nPrecision; + AVCBinReadClose(psFile); + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + + psInfo->pasSections[iSect].eType = AVCFileARC; + psInfo->pasSections[iSect].pszName = CPLStrdup("ARC"); + psInfo->pasSections[iSect].pszFilename=CPLStrdup(papszCoverDir[iFile]); + } + + /*----------------------------------------------------------------- + * CNT section (cnt.adf) + *----------------------------------------------------------------*/ + szFname = (psInfo->eCoverType==AVCCoverV7 || + psInfo->eCoverType==AVCCoverPC2 ) ? "cnt.adf": "cnt"; + if ( (iFile=CSLFindString(papszCoverDir, szFname)) != -1 && + (psFile = AVCBinReadOpen(psInfo->pszCoverPath, szFname, + psInfo->eCoverType, AVCFileCNT, + psInfo->psDBCSInfo)) != NULL) + { + if (nCoverPrecision == AVC_DEFAULT_PREC) + nCoverPrecision = psFile->nPrecision; + AVCBinReadClose(psFile); + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + + psInfo->pasSections[iSect].eType = AVCFileCNT; + psInfo->pasSections[iSect].pszName = CPLStrdup("CNT"); + psInfo->pasSections[iSect].pszFilename=CPLStrdup(papszCoverDir[iFile]); + } + + /*----------------------------------------------------------------- + * LAB section (lab.adf) + *----------------------------------------------------------------*/ + szFname = (psInfo->eCoverType==AVCCoverV7 || + psInfo->eCoverType==AVCCoverPC2 ) ? "lab.adf": "lab"; + if ( (iFile=CSLFindString(papszCoverDir, szFname)) != -1 && + (psFile = AVCBinReadOpen(psInfo->pszCoverPath, szFname, + psInfo->eCoverType, AVCFileLAB, + psInfo->psDBCSInfo)) != NULL) + { + if (nCoverPrecision == AVC_DEFAULT_PREC) + nCoverPrecision = psFile->nPrecision; + AVCBinReadClose(psFile); + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + + psInfo->pasSections[iSect].eType = AVCFileLAB; + psInfo->pasSections[iSect].pszName = CPLStrdup("LAB"); + psInfo->pasSections[iSect].pszFilename=CPLStrdup(papszCoverDir[iFile]); + } + + /*----------------------------------------------------------------- + * PAL section (pal.adf) + *----------------------------------------------------------------*/ + szFname = (psInfo->eCoverType==AVCCoverV7 || + psInfo->eCoverType==AVCCoverPC2 ) ? "pal.adf": "pal"; + if ( (iFile=CSLFindString(papszCoverDir, szFname)) != -1 && + (psFile = AVCBinReadOpen(psInfo->pszCoverPath, szFname, + psInfo->eCoverType, AVCFilePAL, + psInfo->psDBCSInfo)) != NULL) + { + if (nCoverPrecision == AVC_DEFAULT_PREC) + nCoverPrecision = psFile->nPrecision; + AVCBinReadClose(psFile); + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + + psInfo->pasSections[iSect].eType = AVCFilePAL; + psInfo->pasSections[iSect].pszName = CPLStrdup("PAL"); + psInfo->pasSections[iSect].pszFilename=CPLStrdup(papszCoverDir[iFile]); + } + + /*----------------------------------------------------------------- + * TOL section (tol.adf for single precision, par.adf for double) + *----------------------------------------------------------------*/ + szFname = (psInfo->eCoverType==AVCCoverV7 || + psInfo->eCoverType==AVCCoverPC2 ) ? "tol.adf": "tol"; + if ( (iFile=CSLFindString(papszCoverDir, szFname)) != -1 && + (psFile = AVCBinReadOpen(psInfo->pszCoverPath, szFname, + psInfo->eCoverType, AVCFileTOL, + psInfo->psDBCSInfo)) != NULL) + { + if (nCoverPrecision == AVC_DEFAULT_PREC) + nCoverPrecision = psFile->nPrecision; + AVCBinReadClose(psFile); + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + + psInfo->pasSections[iSect].eType = AVCFileTOL; + psInfo->pasSections[iSect].pszName = CPLStrdup("TOL"); + psInfo->pasSections[iSect].pszFilename=CPLStrdup(papszCoverDir[iFile]); + } + + szFname = (psInfo->eCoverType==AVCCoverV7 || + psInfo->eCoverType==AVCCoverPC2 ) ? "par.adf": "par"; + if ( (iFile=CSLFindString(papszCoverDir, szFname)) != -1 && + (psFile = AVCBinReadOpen(psInfo->pszCoverPath, szFname, + psInfo->eCoverType, AVCFileTOL, + psInfo->psDBCSInfo)) != NULL) + { + if (nCoverPrecision == AVC_DEFAULT_PREC) + nCoverPrecision = psFile->nPrecision; + AVCBinReadClose(psFile); + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + + psInfo->pasSections[iSect].eType = AVCFileTOL; + psInfo->pasSections[iSect].pszName = CPLStrdup("TOL"); + psInfo->pasSections[iSect].pszFilename=CPLStrdup(papszCoverDir[iFile]); + } + + /*----------------------------------------------------------------- + * TXT section (txt.adf) + *----------------------------------------------------------------*/ + szFname = (psInfo->eCoverType==AVCCoverV7 || + psInfo->eCoverType==AVCCoverPC2 ) ? "txt.adf": "txt"; + if ( (iFile=CSLFindString(papszCoverDir, szFname)) != -1 && + (psFile = AVCBinReadOpen(psInfo->pszCoverPath, szFname, + psInfo->eCoverType, AVCFileTXT, + psInfo->psDBCSInfo)) != NULL) + { + if (nCoverPrecision == AVC_DEFAULT_PREC) + nCoverPrecision = psFile->nPrecision; + AVCBinReadClose(psFile); + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + + psInfo->pasSections[iSect].eType = AVCFileTXT; + psInfo->pasSections[iSect].pszName = CPLStrdup("TXT"); + psInfo->pasSections[iSect].pszFilename=CPLStrdup(papszCoverDir[iFile]); + } + + /*----------------------------------------------------------------- + * TX6 section (*.txt) + * Scan the directory for files with a ".txt" extension. + * Note: Never seen those in a PC Arc/Info coverage! + * In weird coverages, the filename ends with "txt" but there is no "." + *----------------------------------------------------------------*/ + if (psInfo->eCoverType == AVCCoverV7) + nCoverPrecision = _AVCE00ReadAddJabberwockySection(psInfo, AVCFileTX6, + "TX6", + nCoverPrecision, + ".txt", + papszCoverDir); + else if (psInfo->eCoverType == AVCCoverWeird) + nCoverPrecision = _AVCE00ReadAddJabberwockySection(psInfo, AVCFileTX6, + "TX6", + nCoverPrecision, + "txt", + papszCoverDir); + + /*----------------------------------------------------------------- + * At this point, we should have read the coverage precsion... and if + * we haven't yet then we'll just use single by default. + * We'll need cPrecisionCode for some of the sections that follow. + *----------------------------------------------------------------*/ + if (nCoverPrecision == AVC_DOUBLE_PREC) + cPrecisionCode = '3'; + else + cPrecisionCode = '2'; + + /*----------------------------------------------------------------- + * SIN 2/3 and EOX lines ... ??? + *----------------------------------------------------------------*/ + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 2); + psInfo->pasSections[iSect].eType = AVCFileUnknown; + psInfo->pasSections[iSect].pszName = CPLStrdup("SIN X"); + psInfo->pasSections[iSect].pszName[5] = cPrecisionCode; + iSect++; + psInfo->pasSections[iSect].eType = AVCFileUnknown; + psInfo->pasSections[iSect].pszName = CPLStrdup("EOX"); + iSect++; + + /*----------------------------------------------------------------- + * LOG section (log.adf) (ends with EOL) + *----------------------------------------------------------------*/ + + /*----------------------------------------------------------------- + * PRJ section (prj.adf) (ends with EOP) + *----------------------------------------------------------------*/ + szFname = (psInfo->eCoverType==AVCCoverV7 || + psInfo->eCoverType==AVCCoverPC2 ) ? "prj.adf": "prj"; + if ( (iFile=CSLFindString(papszCoverDir, szFname)) != -1 ) + { + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + + psInfo->pasSections[iSect].eType = AVCFilePRJ; + psInfo->pasSections[iSect].pszName = CPLStrdup("PRJ"); + psInfo->pasSections[iSect].pszFilename=CPLStrdup(papszCoverDir[iFile]); + } + + /*----------------------------------------------------------------- + * RXP section (*.rxp) + * Scan the directory for files with a ".rxp" extension. + *----------------------------------------------------------------*/ + if (psInfo->eCoverType == AVCCoverV7) + _AVCE00ReadAddJabberwockySection(psInfo, AVCFileRXP, "RXP", + nCoverPrecision,".rxp",papszCoverDir); + else if (psInfo->eCoverType == AVCCoverWeird) + _AVCE00ReadAddJabberwockySection(psInfo, AVCFileRXP, "RXP", + nCoverPrecision,"rxp",papszCoverDir); + + + /*----------------------------------------------------------------- + * RPL section (*.pal) + * Scan the directory for files with a ".rpl" extension. + *----------------------------------------------------------------*/ + if (psInfo->eCoverType == AVCCoverV7) + _AVCE00ReadAddJabberwockySection(psInfo, AVCFileRPL, "RPL", + nCoverPrecision,".pal",papszCoverDir); + else if (psInfo->eCoverType == AVCCoverWeird) + _AVCE00ReadAddJabberwockySection(psInfo, AVCFileRPL, "RPL", + nCoverPrecision,"rpl",papszCoverDir); + + /*----------------------------------------------------------------- + * IFO section (tables) + *----------------------------------------------------------------*/ + papszTables = papszFiles = NULL; + if (psInfo->eCoverType == AVCCoverV7 || + psInfo->eCoverType == AVCCoverV7Tables || + psInfo->eCoverType == AVCCoverWeird) + { + /*------------------------------------------------------------- + * Unix coverages: get tables from the ../info/arc.dir + * Weird coverages: the arc.dir is similar but called "arcdr9" + *------------------------------------------------------------*/ + papszTables = AVCBinReadListTables(psInfo->pszInfoPath, + psInfo->pszCoverName, + &papszFiles, psInfo->eCoverType, + psInfo->psDBCSInfo); + } + else if (psInfo->eCoverType == AVCCoverPC || + psInfo->eCoverType == AVCCoverPC2) + { + /*------------------------------------------------------------- + * PC coverages: look for "???.dbf" in the coverage directory + * and build the table name using the coverage name + * as the table basename, and the dbf file basename + * as the table extension. + *------------------------------------------------------------*/ + for(iFile=0; papszCoverDir && papszCoverDir[iFile]; iFile++) + { + if ((nLen = strlen(papszCoverDir[iFile])) == 7 && + EQUAL(papszCoverDir[iFile] + nLen -4, ".dbf")) + { + papszCoverDir[iFile][nLen - 4] = '\0'; + szFname = CPLSPrintf("%s.%s", psInfo->pszCoverName, + papszCoverDir[iFile]); + pcTmp = (char*)szFname; + for( ; *pcTmp != '\0'; pcTmp++) + *pcTmp = toupper(*pcTmp); + papszCoverDir[iFile][nLen - 4] = '.'; + + papszTables = CSLAddString(papszTables, szFname); + papszFiles = CSLAddString(papszFiles, papszCoverDir[iFile]); + } + } + } + + if ((numTables = CSLCount(papszTables)) > 0) + { + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), numTables+2); + + psInfo->pasSections[iSect].eType = AVCFileUnknown; + psInfo->pasSections[iSect].pszName = CPLStrdup("IFO X"); + psInfo->pasSections[iSect].pszName[5] = cPrecisionCode; + iSect++; + + for(iTable=0; iTablepasSections[iSect].eType = AVCFileTABLE; + psInfo->pasSections[iSect].pszName=CPLStrdup(papszTables[iTable]); + if (papszFiles) + { + psInfo->pasSections[iSect].pszFilename= + CPLStrdup(papszFiles[iTable]); + } + iSect++; + } + + psInfo->pasSections[iSect].eType = AVCFileUnknown; + psInfo->pasSections[iSect].pszName = CPLStrdup("EOI"); + iSect++; + + } + CSLDestroy(papszTables); + CSLDestroy(papszFiles); + + /*----------------------------------------------------------------- + * File ends with EOS + *----------------------------------------------------------------*/ + iSect = _AVCIncreaseSectionsArray(&(psInfo->pasSections), + &(psInfo->numSections), 1); + psInfo->pasSections[iSect].eType = AVCFileUnknown; + psInfo->pasSections[iSect].pszName = CPLStrdup("EOS"); + + + return nCoverPrecision; +} + + +/********************************************************************** + * _AVCE00ReadScanE00() + * + * Processes an entire E00 file to find all the interesting sections. + **********************************************************************/ +static void _AVCE00ReadScanE00(AVCE00ReadE00Ptr psRead) +{ + AVCE00ParseInfo *psInfo = psRead->hParseInfo; + + const char *pszLine; + const char *pszName = 0; + void *obj; + int iSect = 0; + GBool bFirstLine = TRUE; + + while (CPLGetLastErrorNo() == 0 && + (pszLine = CPLReadLine(psRead->hFile) ) != NULL ) + { + if (bFirstLine) + { + /* Look for the first non-empty line, after the EXP header, + * trying to detect compressed E00 files. If the file is + * compressed, the first line of data should be 79 or 80 chars + * long and contain several '~' characters. + */ + int nLen = strlen(pszLine); + if (nLen == 0 || EQUALN("EXP ", pszLine, 4)) + continue; /* Skip empty and EXP header lines */ + else if ( (nLen == 79 || nLen == 80) && + strchr(pszLine, '~') != NULL ) + { + /* Looks like a compressed file. Just log an error and return. + * The caller should reject the file because it contains 0 + * sections + */ + CPLError(CE_Failure, CPLE_OpenFailed, + "This looks like a compressed E00 file and cannot be " + "processed directly. You may need to uncompress it " + "first using the E00compr library or the e00conv " + "program." ); + return; + } + + /* All seems fine. Continue with normal processing */ + bFirstLine = FALSE; + } + + obj = _AVCE00ReadNextLineE00(psRead, pszLine); + + if (obj) + { + pszName = 0; + switch (psInfo->eFileType) + { + case AVCFileARC: + pszName = "ARC"; + break; + + case AVCFilePAL: + pszName = "PAL"; + break; + + case AVCFileCNT: + pszName = "CNT"; + break; + + case AVCFileLAB: + pszName = "LAB"; + break; + + case AVCFileRPL: + pszName = "RPL"; + break; + + case AVCFileTXT: + pszName = "TXT"; + break; + + case AVCFileTX6: + pszName = "TX6"; + break; + + case AVCFilePRJ: + pszName = "PRJ"; + break; + + case AVCFileTABLE: + pszName = psInfo->hdr.psTableDef->szTableName; + break; + + default: + break; + } + + if (pszName && (psRead->numSections == 0 || + psRead->pasSections[iSect].eType != psInfo->eFileType || + !EQUAL(pszName, psRead->pasSections[iSect].pszName))) + { + iSect = _AVCIncreaseSectionsArray(&(psRead->pasSections), + &(psRead->numSections), 1); + + psRead->pasSections[iSect].eType = psInfo->eFileType; + /* psRead->pasSections[iSect].pszName = CPLStrdup(psRead->pszCoverName); */ + psRead->pasSections[iSect].pszName = CPLStrdup(pszName); + psRead->pasSections[iSect].pszFilename = CPLStrdup(psRead->pszCoverPath); + psRead->pasSections[iSect].nLineNum = psInfo->nStartLineNum; + psRead->pasSections[iSect].nFeatureCount = 0; + } + + if (pszName && psRead->numSections) + { + /* increase feature count for current layer */ + ++psRead->pasSections[iSect].nFeatureCount; + } + } + } +} + +/********************************************************************** + * _AVCE00ReadNextTableLine() + * + * Return the next line of the E00 representation of a info table. + * + * This function is used by AVCE00ReadNextLine() to generate table + * output... it should never be called directly. + **********************************************************************/ +static const char *_AVCE00ReadNextTableLine(AVCE00ReadPtr psInfo) +{ + const char *pszLine = NULL; + AVCE00Section *psSect; + + psSect = &(psInfo->pasSections[psInfo->iCurSection]); + + CPLAssert(psSect->eType == AVCFileTABLE); + + if (psInfo->iCurStep == AVC_GEN_NOTSTARTED) + { + /*--------------------------------------------------------- + * Open table and start returning header + *--------------------------------------------------------*/ + if (psInfo->eCoverType == AVCCoverPC || + psInfo->eCoverType == AVCCoverPC2) + { + /*--------------------------------------------------------- + * PC Arc/Info: We pass the DBF table's full filename + the + * Arc/Info table name (for E00 header) + *--------------------------------------------------------*/ + char *pszFname; + pszFname = CPLStrdup(CPLSPrintf("%s%s", psInfo->pszInfoPath, + psSect->pszFilename )); + psInfo->hFile = AVCBinReadOpen(pszFname, psSect->pszName, + psInfo->eCoverType, psSect->eType, + psInfo->psDBCSInfo); + CPLFree(pszFname); + } + else + { + /*--------------------------------------------------------- + * AVCCoverV7 and AVCCoverWeird: + * We pass the INFO dir's path, and the Arc/Info table name + * will be searched in the arc.dir + *--------------------------------------------------------*/ + psInfo->hFile = AVCBinReadOpen(psInfo->pszInfoPath, + psSect->pszName, + psInfo->eCoverType, psSect->eType, + psInfo->psDBCSInfo); + } + + + /* For some reason the file could not be opened... abort now. + * An error message should have already been produced by + * AVCBinReadOpen() + */ + if (psInfo->hFile == NULL) + return NULL; + + psInfo->iCurStep = AVC_GEN_TABLEHEADER; + + pszLine = AVCE00GenTableHdr(psInfo->hGenInfo, + psInfo->hFile->hdr.psTableDef, + FALSE); + } + + if (pszLine == NULL && + psInfo->iCurStep == AVC_GEN_TABLEHEADER) + { + /*--------------------------------------------------------- + * Continue table header + *--------------------------------------------------------*/ + pszLine = AVCE00GenTableHdr(psInfo->hGenInfo, + psInfo->hFile->hdr.psTableDef, + TRUE); + + if (pszLine == NULL) + { + /* Finished with table header... time to proceed with the + * table data. + * Reset the AVCE00GenInfo struct. so that it returns NULL, + * which will force reading of the first record from the + * file on the next call to AVCE00ReadNextLine() + */ + AVCE00GenReset(psInfo->hGenInfo); + psInfo->iCurStep = AVC_GEN_TABLEDATA; + } + + } + + if (pszLine == NULL && + psInfo->iCurStep == AVC_GEN_TABLEDATA) + { + /*--------------------------------------------------------- + * Continue with records of data + *--------------------------------------------------------*/ + + pszLine = AVCE00GenTableRec(psInfo->hGenInfo, + psInfo->hFile->hdr.psTableDef->numFields, + psInfo->hFile->hdr.psTableDef->pasFieldDef, + psInfo->hFile->cur.pasFields, + TRUE); + + if (pszLine == NULL) + { + /* Current record is finished generating... we need to read + * a new one from the file. + */ + if (AVCBinReadNextObject(psInfo->hFile) != NULL) + { + pszLine = AVCE00GenTableRec(psInfo->hGenInfo, + psInfo->hFile->hdr.psTableDef->numFields, + psInfo->hFile->hdr.psTableDef->pasFieldDef, + psInfo->hFile->cur.pasFields, + FALSE); + } + + } + } + + if (pszLine == NULL) + { + /*--------------------------------------------------------- + * No more lines to output for this table ... Close it. + *--------------------------------------------------------*/ + AVCBinReadClose(psInfo->hFile); + psInfo->hFile = NULL; + + /*--------------------------------------------------------- + * And now proceed to the next section... + * OK, I don't really like recursivity either... but it was + * the simplest way to do this, and anyways we should never + * have more than one level of recursivity. + *--------------------------------------------------------*/ + if (psInfo->bReadAllSections) + psInfo->iCurSection++; + else + psInfo->iCurSection = psInfo->numSections; + psInfo->iCurStep = AVC_GEN_NOTSTARTED; + + pszLine = AVCE00ReadNextLine(psInfo); + } + + /*----------------------------------------------------------------- + * Check for errors... if any error happened, tehn return NULL + *----------------------------------------------------------------*/ + if (CPLGetLastErrorNo() != 0) + { + pszLine = NULL; + } + + return pszLine; +} + + +/********************************************************************** + * AVCE00ReadNextLine() + * + * Returns the next line of the E00 representation of the coverage + * or NULL when there are no more lines to generate, or if an error happened. + * The returned line is a null-terminated string, and it does not + * include a newline character. + * + * Call CPLGetLastErrorNo() after calling AVCE00ReadNextLine() to + * make sure that the line was generated succesfully. + * + * Note that AVCE00ReadNextLine() returns a reference to an + * internal buffer whose contents will + * be valid only until the next call to this function. The caller should + * not attempt to free() the returned pointer. + **********************************************************************/ +const char *AVCE00ReadNextLine(AVCE00ReadPtr psInfo) +{ + const char *pszLine = NULL; + AVCE00Section *psSect; + + CPLErrorReset(); + + /*----------------------------------------------------------------- + * Check if we have finished generating E00 output + *----------------------------------------------------------------*/ + if (psInfo->iCurSection >= psInfo->numSections) + return NULL; + + psSect = &(psInfo->pasSections[psInfo->iCurSection]); + + /*----------------------------------------------------------------- + * For simplicity, the generation of table output is in a separate + * function. + *----------------------------------------------------------------*/ + if (psSect->eType == AVCFileTABLE) + { + return _AVCE00ReadNextTableLine(psInfo); + } + + if (psSect->eType == AVCFileUnknown) + { + /*----------------------------------------------------------------- + * Section not attached to any file, used to hold header lines + * or section separators, etc... just return the line directly and + * move pointer to the next section. + *----------------------------------------------------------------*/ + pszLine = psSect->pszName; + if (psInfo->bReadAllSections) + psInfo->iCurSection++; + else + psInfo->iCurSection = psInfo->numSections; + psInfo->iCurStep = AVC_GEN_NOTSTARTED; + } + /*================================================================= + * ARC, PAL, CNT, LAB, TOL and TXT + *================================================================*/ + else if (psInfo->iCurStep == AVC_GEN_NOTSTARTED && + (psSect->eType == AVCFileARC || + psSect->eType == AVCFilePAL || + psSect->eType == AVCFileRPL || + psSect->eType == AVCFileCNT || + psSect->eType == AVCFileLAB || + psSect->eType == AVCFileTOL || + psSect->eType == AVCFileTXT || + psSect->eType == AVCFileTX6 || + psSect->eType == AVCFileRXP ) ) + { + /*----------------------------------------------------------------- + * Start processing of an ARC, PAL, CNT, LAB or TOL section: + * Open the file, get ready to read the first object from the + * file, and return the header line. + * If the file fails to open then we will return NULL. + *----------------------------------------------------------------*/ + psInfo->hFile = AVCBinReadOpen(psInfo->pszCoverPath, + psSect->pszFilename, + psInfo->eCoverType, psSect->eType, + psInfo->psDBCSInfo); + + /*------------------------------------------------------------- + * For some reason the file could not be opened... abort now. + * An error message should have already been produced by + * AVCBinReadOpen() + *------------------------------------------------------------*/ + if (psInfo->hFile == NULL) + return NULL; + + pszLine = AVCE00GenStartSection(psInfo->hGenInfo, + psSect->eType, psSect->pszName); + + /*------------------------------------------------------------- + * Reset the AVCE00GenInfo struct. so that it returns NULL, + * which will force reading of the first object from the + * file on the next call to AVCE00ReadNextLine() + *------------------------------------------------------------*/ + AVCE00GenReset(psInfo->hGenInfo); + psInfo->iCurStep = AVC_GEN_DATA; + } + else if (psInfo->iCurStep == AVC_GEN_DATA && + (psSect->eType == AVCFileARC || + psSect->eType == AVCFilePAL || + psSect->eType == AVCFileRPL || + psSect->eType == AVCFileCNT || + psSect->eType == AVCFileLAB || + psSect->eType == AVCFileTOL || + psSect->eType == AVCFileTXT || + psSect->eType == AVCFileTX6 || + psSect->eType == AVCFileRXP ) ) + { + /*----------------------------------------------------------------- + * Return the next line of an ARC/PAL/CNT/TOL/TXT object... + * if necessary, read the next object from the binary file. + *----------------------------------------------------------------*/ + pszLine = AVCE00GenObject(psInfo->hGenInfo, + psSect->eType, + (psSect->eType==AVCFileARC?(void*)(psInfo->hFile->cur.psArc): + psSect->eType==AVCFilePAL?(void*)(psInfo->hFile->cur.psPal): + psSect->eType==AVCFileRPL?(void*)(psInfo->hFile->cur.psPal): + psSect->eType==AVCFileCNT?(void*)(psInfo->hFile->cur.psCnt): + psSect->eType==AVCFileLAB?(void*)(psInfo->hFile->cur.psLab): + psSect->eType==AVCFileTOL?(void*)(psInfo->hFile->cur.psTol): + psSect->eType==AVCFileTXT?(void*)(psInfo->hFile->cur.psTxt): + psSect->eType==AVCFileTX6?(void*)(psInfo->hFile->cur.psTxt): + psSect->eType==AVCFileRXP?(void*)(psInfo->hFile->cur.psRxp): + NULL), + TRUE); + if (pszLine == NULL) + { + /*--------------------------------------------------------- + * Current object is finished generating... we need to read + * a new one from the file. + *--------------------------------------------------------*/ + if (AVCBinReadNextObject(psInfo->hFile) != NULL) + { + pszLine = AVCE00GenObject(psInfo->hGenInfo, + psSect->eType, + (psSect->eType==AVCFileARC?(void*)(psInfo->hFile->cur.psArc): + psSect->eType==AVCFilePAL?(void*)(psInfo->hFile->cur.psPal): + psSect->eType==AVCFileRPL?(void*)(psInfo->hFile->cur.psPal): + psSect->eType==AVCFileCNT?(void*)(psInfo->hFile->cur.psCnt): + psSect->eType==AVCFileLAB?(void*)(psInfo->hFile->cur.psLab): + psSect->eType==AVCFileTOL?(void*)(psInfo->hFile->cur.psTol): + psSect->eType==AVCFileTXT?(void*)(psInfo->hFile->cur.psTxt): + psSect->eType==AVCFileTX6?(void*)(psInfo->hFile->cur.psTxt): + psSect->eType==AVCFileRXP?(void*)(psInfo->hFile->cur.psRxp): + NULL), + FALSE); + } + } + if (pszLine == NULL) + { + /*--------------------------------------------------------- + * Still NULL ??? This means we finished reading this file... + * Start returning the "end of section" line(s)... + *--------------------------------------------------------*/ + AVCBinReadClose(psInfo->hFile); + psInfo->hFile = NULL; + psInfo->iCurStep = AVC_GEN_ENDSECTION; + pszLine = AVCE00GenEndSection(psInfo->hGenInfo, psSect->eType, + FALSE); + } + } + /*================================================================= + * PRJ + *================================================================*/ + else if (psInfo->iCurStep == AVC_GEN_NOTSTARTED && + psSect->eType == AVCFilePRJ ) + { + /*------------------------------------------------------------- + * Start processing of PRJ section... return first header line. + *------------------------------------------------------------*/ + pszLine = AVCE00GenStartSection(psInfo->hGenInfo, + psSect->eType, NULL); + + psInfo->hFile = NULL; + psInfo->iCurStep = AVC_GEN_DATA; + } + else if (psInfo->iCurStep == AVC_GEN_DATA && + psSect->eType == AVCFilePRJ ) + { + /*------------------------------------------------------------- + * Return the next line of a PRJ section + *------------------------------------------------------------*/ + if (psInfo->hFile == NULL) + { + /*--------------------------------------------------------- + * File has not been read yet... + * Read the PRJ file, and return the first PRJ line. + *--------------------------------------------------------*/ + psInfo->hFile = AVCBinReadOpen(psInfo->pszCoverPath, + psSect->pszFilename, + psInfo->eCoverType, psSect->eType, + psInfo->psDBCSInfo); + + /* For some reason the file could not be opened... abort now. + * An error message should have already been produced by + * AVCBinReadOpen() + */ + if (psInfo->hFile == NULL) + return NULL; + + pszLine = AVCE00GenPrj(psInfo->hGenInfo, + psInfo->hFile->cur.papszPrj, FALSE); + } + else + { + /*--------------------------------------------------------- + * Generate the next line of output. + *--------------------------------------------------------*/ + pszLine = AVCE00GenPrj(psInfo->hGenInfo, + psInfo->hFile->cur.papszPrj, TRUE); + } + + if (pszLine == NULL) + { + /*--------------------------------------------------------- + * Still NULL ??? This means we finished generating this PRJ + * section... + * Start returning the "end of section" line(s)... + *--------------------------------------------------------*/ + AVCBinReadClose(psInfo->hFile); + psInfo->hFile = NULL; + psInfo->iCurStep = AVC_GEN_ENDSECTION; + pszLine = AVCE00GenEndSection(psInfo->hGenInfo, psSect->eType, + FALSE); + } + } + else if (psInfo->iCurStep != AVC_GEN_ENDSECTION) + { + /* We should never get here! */ + CPLAssert(FALSE); + } + + + /*================================================================= + * End of section, for all files + *================================================================*/ + + /*----------------------------------------------------------------- + * Finished processing of an ARC, PAL, CNT, LAB, TOL, PRJ file ... + * continue returning the "end of section" line(s), and move the pointer + * to the next section once we're done. + *----------------------------------------------------------------*/ + if (psInfo->iCurStep == AVC_GEN_ENDSECTION && pszLine == NULL) + { + pszLine = AVCE00GenEndSection(psInfo->hGenInfo, psSect->eType, TRUE); + + if (pszLine == NULL) + { + /*--------------------------------------------------------- + * Finished returning the last lines of the section... + * proceed to the next section... + * OK, I don't really like recursivity either... but it was + * the simplest way to do this, and anyways we should never + * have more than one level of recursivity. + *--------------------------------------------------------*/ + if (psInfo->bReadAllSections) + psInfo->iCurSection++; + else + psInfo->iCurSection = psInfo->numSections; + psInfo->iCurStep = AVC_GEN_NOTSTARTED; + + pszLine = AVCE00ReadNextLine(psInfo); + } + } + + return pszLine; +} + + + +/********************************************************************** + * AVCE00ReadSectionsList() + * + * Returns an array of AVCE00Section structures that describe the + * squeleton of the whole coverage. The value of *numSect will be + * set to the number of sections in the array. + * + * You can scan the returned array, and use AVCE00ReadGotoSection() to move + * the read pointer directly to the beginning of a given section + * of the file. + * + * Sections of type AVCFileUnknown correspond to lines in the + * E00 output that are not directly linked to any coverage file, like + * the "EXP 0" line, the "IFO X", "SIN X", etc. + * + * THE RETURNED ARRAY IS AN INTERNAL STRUCTURE AND SHOULD NOT BE + * MODIFIED OR FREED BY THE CALLER... its contents will be valid + * for as long as the coverage will remain open. + **********************************************************************/ +AVCE00Section *AVCE00ReadSectionsList(AVCE00ReadPtr psInfo, int *numSect) +{ + CPLErrorReset(); + + *numSect = psInfo->numSections; + return psInfo->pasSections; +} + +/********************************************************************** + * AVCE00ReadGotoSection() + * + * Move the read pointer to the E00 section (coverage file) described in + * the psSect structure. Call AVCE00ReadSectionsList() to get the list of + * sections for the current coverage. + * + * if bContinue=TRUE, then reading will automatically continue with the + * next sections of the file once the requested section is finished. + * Otherwise, if bContinue=FALSE then reading will stop at the end + * of this section (i.e. AVCE00ReadNextLine() will return NULL when + * it reaches the end of this section) + * + * Sections of type AVCFileUnknown returned by AVCE00ReadSectionsList() + * correspond to lines in the E00 output that are not directly linked + * to any coverage file, like the "EXP 0" line, the "IFO X", "SIN X", etc. + * You can jump to these sections or any other one without problems. + * + * This function returns 0 on success or -1 on error. + **********************************************************************/ +int AVCE00ReadGotoSection(AVCE00ReadPtr psInfo, AVCE00Section *psSect, + GBool bContinue) +{ + int iSect; + GBool bFound = FALSE; + + CPLErrorReset(); + + /*----------------------------------------------------------------- + * Locate the requested section in the array. + *----------------------------------------------------------------*/ + for(iSect=0; iSectnumSections; iSect++) + { + if (psInfo->pasSections[iSect].eType == psSect->eType && + EQUAL(psInfo->pasSections[iSect].pszName, psSect->pszName)) + { + bFound = TRUE; + break; + } + } + + /*----------------------------------------------------------------- + * Not found ... generate an error... + *----------------------------------------------------------------*/ + if (!bFound) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Requested E00 section does not exist!"); + return -1; + } + + /*----------------------------------------------------------------- + * Found it ... close current section and get ready to read + * the new one. + *----------------------------------------------------------------*/ + if (psInfo->hFile) + { + AVCBinReadClose(psInfo->hFile); + psInfo->hFile = NULL; + } + + psInfo->bReadAllSections = bContinue; + psInfo->iCurSection = iSect; + psInfo->iCurStep = AVC_GEN_NOTSTARTED; + + return 0; +} + +/********************************************************************** + * AVCE00ReadRewind() + * + * Rewinds the AVCE00ReadPtr just like the stdio rewind() + * function would do if you were reading an ASCII E00 file. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int AVCE00ReadRewind(AVCE00ReadPtr psInfo) +{ + CPLErrorReset(); + + return AVCE00ReadGotoSection(psInfo, &(psInfo->pasSections[0]), TRUE); +} + +/********************************************************************** + * AVCE00ReadRewindE00() + * + * Rewinds the AVCE00ReadE00Ptr just like the stdio rewind() + * function would do if you were reading an ASCII E00 file. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int AVCE00ReadRewindE00(AVCE00ReadE00Ptr psRead) +{ + CPLErrorReset(); + + psRead->bReadAllSections = TRUE; + psRead->eCurFileType = AVCFileUnknown; + + psRead->hParseInfo->nCurLineNum = 0; + psRead->hParseInfo->nStartLineNum = 0; + psRead->hParseInfo->bForceEndOfSection = TRUE; + psRead->hParseInfo->eSuperSectionType = AVCFileUnknown; + AVCE00ParseSectionEnd(psRead->hParseInfo, NULL, 1); + + return fseek(psRead->hFile, 0, SEEK_SET); +} + +/********************************************************************** + * _AVCE00ReadSeekE00() + * + * Seeks to a new location in the E00 file, keeping parse state + * appropriately. + * + * NOTE: This is a pretty slow implementation. + * NOTE: The SEEK_END is not implemented. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +static int _AVCE00ReadSeekE00(AVCE00ReadE00Ptr psRead, int nOffset, + int nWhence) +{ + const char *pszLine; + /* void *obj; */ + + switch (nWhence) + { + case SEEK_CUR: + break; + + case SEEK_SET: + AVCE00ReadRewindE00(psRead); + break; + + default: + CPLAssert(nWhence == SEEK_CUR || nWhence == SEEK_SET); + break; + } + + while (nOffset-- && + CPLGetLastErrorNo() == 0 && + (pszLine = CPLReadLine(psRead->hFile) ) != NULL ) + { + /* obj = */ + _AVCE00ReadNextLineE00(psRead, pszLine); + } + + return nOffset ? -1 : 0; +} + +/********************************************************************** + * AVCE00ReadNextObjectE00() + * + * Returns the next object in an E00 file or NULL when there are no + * more objects, or if an error happened. The object type can be + * determined via the eCurFileType attribute of the + * AVCE00ReadE00Ptr object. + * + * Note that AVCE00ReadNextLine() returns a reference to an internal + * buffer whose contents will be valid only until the next call to + * this function. The caller should not attempt to free() the + * returned pointer. + **********************************************************************/ +void *AVCE00ReadNextObjectE00(AVCE00ReadE00Ptr psRead) +{ + const char *pszLine; + void *obj = NULL; + + do + { + pszLine = CPLReadLine(psRead->hFile); + if (pszLine == 0) + break; + obj = _AVCE00ReadNextLineE00(psRead, pszLine); + } + while (obj == NULL && + (psRead->bReadAllSections || + psRead->eCurFileType != AVCFileUnknown) && + CPLGetLastErrorNo() == 0); + return obj; +} + +/********************************************************************** + * AVCE00ReadSectionsListE00() + * + * Returns an array of AVCE00Section structures that describe the + * sections in the E00 file. The value of *numSect will be set to the + * number of sections in the array. + * + * You can scan the returned array, and use AVCE00ReadGotoSectionE00() + * to move the read pointer directly to the beginning of a given + * section of the file. + * + * THE RETURNED ARRAY IS AN INTERNAL STRUCTURE AND SHOULD NOT BE + * MODIFIED OR FREED BY THE CALLER... its contents will be valid + * for as long as the coverage will remain open. + **********************************************************************/ +AVCE00Section *AVCE00ReadSectionsListE00(AVCE00ReadE00Ptr psRead, + int *numSect) +{ + CPLErrorReset(); + + *numSect = psRead->numSections; + return psRead->pasSections; +} + +/********************************************************************** + * AVCE00ReadGotoSectionE00() + * + * Move the read pointer to the E00 section described in the psSect + * structure. Call AVCE00ReadSectionsListE00() to get the list of + * sections for the current coverage. + * + * If bContinue is TRUE, then reading will automatically continue with + * the next section of the file once the requested section is finished. + * Otherwise, if bContinue is FALSE then reading will stop at the end + * of this section (i.e. AVCE00ReadNextObjectE00() will return NULL + * when the end of this section is reached) + * + * This function returns 0 on success or -1 on error. + **********************************************************************/ +int AVCE00ReadGotoSectionE00(AVCE00ReadE00Ptr psRead, + AVCE00Section *psSect, GBool bContinue) +{ + int iSect; + GBool bFound = FALSE; + + CPLErrorReset(); + + /*----------------------------------------------------------------- + * Locate the requested section in the array. + *----------------------------------------------------------------*/ + for(iSect=0; iSectnumSections; iSect++) + { + if (psRead->pasSections[iSect].eType == psSect->eType && + EQUAL(psRead->pasSections[iSect].pszName, psSect->pszName)) + { + bFound = TRUE; + break; + } + } + + /*----------------------------------------------------------------- + * Not found ... generate an error... + *----------------------------------------------------------------*/ + if (!bFound) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Requested E00 section does not exist!"); + return -1; + } + + /*----------------------------------------------------------------- + * Found it ... advance parser to line number of start of section + *----------------------------------------------------------------*/ + _AVCE00ReadSeekE00(psRead, psRead->pasSections[iSect].nLineNum, SEEK_SET); + + psRead->bReadAllSections = bContinue; + + return 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_e00write.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_e00write.c new file mode 100644 index 000000000..f3fc8447c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_e00write.c @@ -0,0 +1,1013 @@ +/********************************************************************** + * $Id: avc_e00write.c,v 1.21 2008/07/23 20:51:38 dmorissette Exp $ + * + * Name: avc_e00write.c + * Project: Arc/Info vector coverage (AVC) E00->BIN conversion library + * Language: ANSI C + * Purpose: Functions to create a binary coverage from a stream of + * ASCII E00 lines. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2001, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: avc_e00write.c,v $ + * Revision 1.21 2008/07/23 20:51:38 dmorissette + * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char + * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495) + * + * Revision 1.20 2006/06/27 18:38:43 dmorissette + * Cleaned up E00 reading (bug 1497, patch from James F.) + * + * Revision 1.19 2006/06/14 16:31:28 daniel + * Added support for AVCCoverPC2 type (bug 1491) + * + * Revision 1.18 2006/03/02 22:46:26 daniel + * Accept empty subclass names for TX6/TX7 sections (bug 1261) + * + * Revision 1.17 2005/06/03 03:49:59 daniel + * Update email address, website url, and copyright dates + * + * Revision 1.16 2002/08/27 15:46:15 daniel + * Applied fix made in GDAL/OGR by 'aubin' (moved include ctype.h after avc.h) + * + * Revision 1.15 2002/04/16 21:19:10 daniel + * Use VSIRmdir() + * + * Revision 1.14 2002/03/18 19:00:44 daniel + * Use VSIMkdir() and not VSIMkDir() + * + * Revision 1.13 2002/02/18 21:16:33 warmerda + * modified to use VSIMkDir + * + * Revision 1.12 2001/05/23 15:23:17 daniel + * Remove trailing '/' in info directory path when creating the info dir. + * + * Revision 1.11 2000/09/26 20:21:04 daniel + * Added AVCCoverPC write + * + * Revision 1.10 2000/09/22 19:45:21 daniel + * Switch to MIT-style license + * + * Revision 1.9 2000/05/29 22:47:39 daniel + * Made validation on new coverage name more flexible. + * + * Revision 1.8 2000/05/29 15:31:31 daniel + * Added Japanese DBCS support + * + * Revision 1.7 2000/02/14 17:19:53 daniel + * Accept '-' cahracter in new coverage name + * + * Revision 1.6 2000/01/10 02:57:44 daniel + * Little changes to accommodate read support for "weird" coverages + * + * Revision 1.5 1999/12/24 07:18:34 daniel + * Added PC Arc/Info coverages support + * + * Revision 1.4 1999/08/26 17:36:36 daniel + * Avoid overwriting arc.dir on Windows... happened only when several + * coverages are created by the same process on Windows. + * + * Revision 1.3 1999/08/23 18:23:35 daniel + * Added AVCE00DeleteCoverage() + * + * Revision 1.2 1999/05/17 16:23:36 daniel + * Added AVC_DEFAULT_PREC + more cover name validation in AVCE00WriteOpen(). + * + * Revision 1.1 1999/05/11 02:34:46 daniel + * Initial revision + * + **********************************************************************/ + +#include "cpl_vsi.h" +#include "avc.h" +#include /* tolower() */ + +static GBool _IsStringAlnum(const char *pszFname); + +/********************************************************************** + * AVCE00WriteOpen() + * + * Open (create) an Arc/Info coverage, ready to be receive a stream + * of ASCII E00 lines and convert that to the binary coverage format. + * + * For now, writing to or overwriting existing coverages is not supported + * (and may quite well never be!)... you can only create new coverages. + * + * Important Note: The E00 source lines are assumed to be valid... the + * library performs no validation on the consistency of what it is + * given as input (i.e. topology, polygons consistency, etc.). + * So the coverage that will be created will be only as good as the + * E00 input that is used to generate it. + * + * pszCoverPath MUST be the name of the coverage directory, including + * the path to it. + * (contrary to AVCE00ReadOpen(), you cannot pass the name of one of + * the files in the coverage directory). + * The name of the coverage MUST be included in pszCoverPath... this + * means that passing "." is invalid. + * + * eNewCoverType is the type of coverage to create. + * Either AVCCoverV7 (Arc/Info V7 (Unix) coverage) + * or AVCCoverPC (PC Arc/Info coverage) + * + * nPrecision should always be AVC_DEFAULT_PREC to automagically detect the + * source coverage's precision and use that same precision + * for the new coverage. + * + * This parameter has been included to allow adding the + * possibility to eventually create coverages with a precision + * different from the source E00. + * Given the way the lib is built, it could be possible to + * also pass AVC_SINGLE_PREC or AVC_DOUBLE_PREC to explicitly + * request the creation of a coverage with that precision, + * but the library does not (not yet!) properly convert the + * TABLE attributes' precision, and the resulting coverage may + * be invalid in some cases. + * This improvement is on the ToDo list! + * + * Returns a new AVCE00WritePtr handle or NULL if the coverage could + * not be created or if a coverage with that name already exists. + * + * The handle will eventually have to be released with AVCE00ReadClose(). + **********************************************************************/ +AVCE00WritePtr AVCE00WriteOpen(const char *pszCoverPath, + AVCCoverType eNewCoverType, int nPrecision ) +{ + AVCE00WritePtr psInfo; + int i, nLen; + VSIStatBuf sStatBuf; + + CPLErrorReset(); + + /*----------------------------------------------------------------- + * Create pszCoverPath directory. + *----------------------------------------------------------------*/ + if (pszCoverPath == NULL || strlen(pszCoverPath) == 0) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Invalid (empty) coverage directory name."); + return NULL; + } + else if ( VSIStat(pszCoverPath, &sStatBuf) == 0 && + VSI_ISDIR(sStatBuf.st_mode) ) + { + /*------------------------------------------------------------- + * Directory already exists... make sure it is empty + * otherwise we can't use it as a coverage directory. + *------------------------------------------------------------*/ + char **papszFiles; + papszFiles = CPLReadDir(pszCoverPath); + for(i=0; papszFiles && papszFiles[i]; i++) + { + if (!EQUAL(".", papszFiles[i]) && + !EQUAL("..", papszFiles[i])) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Cannot create coverage %s: directory already exists " + "and is not empty.", pszCoverPath); + CSLDestroy(papszFiles); + papszFiles = NULL; + return NULL; + } + } + + CSLDestroy(papszFiles); + papszFiles = NULL; + } + else + { + /*------------------------------------------------------------- + * Create new pszCoverPath directory. + * This will fail if a file with the same name already exists. + *------------------------------------------------------------*/ + if( VSIMkdir(pszCoverPath, 0777) != 0 ) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Unable to create coverage directory: %s.", pszCoverPath); + return NULL; + } + } + + /*----------------------------------------------------------------- + * Alloc the AVCE00WritePtr handle + *----------------------------------------------------------------*/ + psInfo = (AVCE00WritePtr)CPLCalloc(1, sizeof(struct AVCE00WriteInfo_t)); + + /*----------------------------------------------------------------- + * Validate and store coverage type + *----------------------------------------------------------------*/ + if (eNewCoverType == AVCCoverV7 || eNewCoverType == AVCCoverPC) + psInfo->eCoverType = eNewCoverType; + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Requested coverage type cannot be created. Please use " + "the AVCCoverV7 or AVCCoverPC coverage type."); + CPLFree(psInfo); + return NULL; + } + + /*----------------------------------------------------------------- + * Requested precision for the new coverage... for now only + * AVC_DEFAULT_PREC is supported. When the first section is + * read, then this section's precision will be used for the whole + * coverage. (This is done inside AVCE00WriteNextLine()) + *----------------------------------------------------------------*/ + if (psInfo->eCoverType == AVCCoverPC) + psInfo->nPrecision = AVC_SINGLE_PREC; /* PC Cover always single prec.*/ + else if (nPrecision == AVC_DEFAULT_PREC) + psInfo->nPrecision = nPrecision; + else + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Coverages can only be created using AVC_DEFAULT_PREC. " + "Please see the documentation for AVCE00WriteOpen()."); + CPLFree(psInfo); + return NULL; + } + + /*----------------------------------------------------------------- + * Make sure coverage directory name is terminated with a '/' (or '\\') + *----------------------------------------------------------------*/ + nLen = strlen(pszCoverPath); + + if (pszCoverPath[nLen-1] == '/' || pszCoverPath[nLen-1] == '\\') + psInfo->pszCoverPath = CPLStrdup(pszCoverPath); + else + { +#ifdef WIN32 + psInfo->pszCoverPath = CPLStrdup(CPLSPrintf("%s\\",pszCoverPath)); +#else + psInfo->pszCoverPath = CPLStrdup(CPLSPrintf("%s/",pszCoverPath)); +#endif + } + + /*----------------------------------------------------------------- + * Extract the coverage name from the coverage path. Note that + * for this the coverage path must be in the form: + * "dir1/dir2/dir3/covername/" ... if it is not the case, then + * we would have to use getcwd() to find the current directory name... + * but for now we'll just produce an error if this happens. + *----------------------------------------------------------------*/ + nLen = 0; + for( i = strlen(psInfo->pszCoverPath)-1; + i > 0 && psInfo->pszCoverPath[i-1] != '/' && + psInfo->pszCoverPath[i-1] != '\\'&& + psInfo->pszCoverPath[i-1] != ':'; + i-- ) + { + nLen++; + } + + if (nLen > 0) + { + psInfo->pszCoverName = CPLStrdup(psInfo->pszCoverPath+i); + psInfo->pszCoverName[nLen] = '\0'; + } + else + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Invalid coverage path (%s): " + "coverage name must be included in path.", pszCoverPath); + + CPLFree(psInfo->pszCoverPath); + CPLFree(psInfo); + return NULL; + } + + if (strlen(psInfo->pszCoverName) > 13 || + !_IsStringAlnum(psInfo->pszCoverName) ) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Invalid coverage name (%s): " + "coverage name must be 13 chars or less and contain only " + "alphanumerical characters, '-' or '_'.", + psInfo->pszCoverName); + + CPLFree(psInfo->pszCoverPath); + CPLFree(psInfo->pszCoverName); + CPLFree(psInfo); + return NULL; + } + + if (psInfo->eCoverType == AVCCoverPC || psInfo->eCoverType == AVCCoverPC2) + { + /*------------------------------------------------------------- + * No 'info' directory is required for PC coverages + *------------------------------------------------------------*/ + psInfo->pszInfoPath = NULL; + } + else + { + /*------------------------------------------------------------- + * Lazy way to build the INFO path: simply add "../info/"... + * this could probably be improved! + *------------------------------------------------------------*/ + psInfo->pszInfoPath = (char*)CPLMalloc((strlen(psInfo->pszCoverPath)+9) + *sizeof(char)); +#ifdef WIN32 +# define AVC_INFOPATH "..\\info\\" +#else +# define AVC_INFOPATH "../info/" +#endif + sprintf(psInfo->pszInfoPath, "%s%s", psInfo->pszCoverPath, + AVC_INFOPATH); + + /*------------------------------------------------------------- + * Check if the info directory exists and contains the "arc.dir" + * if the info dir does not exist, then make sure we can create + * the arc.dir file (i.e. try to create an empty one) + * + * Note: On Windows, this VSIStat() call seems to sometimes fail even + * when the directory exists (buffering issue?), and the + * following if() block is sometimes executed even if it + * should not, but this should not cause problems since the + * arc.dir is opened with "a+b" access. + *------------------------------------------------------------*/ + if ( VSIStat(psInfo->pszInfoPath, &sStatBuf) == -1) + { + FILE *fp; + char *pszArcDir; + char *pszInfoDir; + + pszArcDir = CPLStrdup(CPLSPrintf("%s%s", + psInfo->pszInfoPath, "arc.dir")); + + /* Remove the trailing "/" from pszInfoPath. Most OSes are + * forgiving, and allow mkdir to include the trailing character, + * but some UNIXes are not. [GEH 2001/05/17] + */ + pszInfoDir = CPLStrdup(psInfo->pszInfoPath); + pszInfoDir[strlen(pszInfoDir)-1] = '\0'; + + VSIMkdir(pszInfoDir, 0777); + fp = VSIFOpen(pszArcDir, "a+b"); + + CPLFree(pszArcDir); + CPLFree(pszInfoDir); + if (fp) + { + VSIFClose(fp); + } + else + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Unable to create (or write to) 'info' directory %s", + psInfo->pszInfoPath); + CPLFree(psInfo->pszCoverPath); + CPLFree(psInfo->pszInfoPath); + CPLFree(psInfo); + return NULL; + } + } + } + + /*----------------------------------------------------------------- + * Init the E00 parser. + *----------------------------------------------------------------*/ + psInfo->hParseInfo = AVCE00ParseInfoAlloc(); + psInfo->eCurFileType = AVCFileUnknown; + + /*----------------------------------------------------------------- + * Init multibyte encoding info + *----------------------------------------------------------------*/ + psInfo->psDBCSInfo = AVCAllocDBCSInfo(); + + /*----------------------------------------------------------------- + * If an error happened during the open call, cleanup and return NULL. + *----------------------------------------------------------------*/ + if (CPLGetLastErrorNo() != 0) + { + AVCE00WriteClose(psInfo); + psInfo = NULL; + } + + return psInfo; +} + +/********************************************************************** + * AVCE00WriteClose() + * + * Close a coverage and release all memory used by the AVCE00WritePtr + * handle. + **********************************************************************/ +void AVCE00WriteClose(AVCE00WritePtr psInfo) +{ + CPLErrorReset(); + + if (psInfo == NULL) + return; + + CPLFree(psInfo->pszCoverPath); + CPLFree(psInfo->pszCoverName); + CPLFree(psInfo->pszInfoPath); + + if (psInfo->hFile) + AVCBinWriteClose(psInfo->hFile); + + if (psInfo->hParseInfo) + AVCE00ParseInfoFree(psInfo->hParseInfo); + + AVCFreeDBCSInfo(psInfo->psDBCSInfo); + + CPLFree(psInfo); +} + + +/********************************************************************** + * _IsStringAlnum() + * + * Scan a string, and return TRUE if it contains only valid characters, + * Return FALSE otherwise. + * + * We used to accept only isalnum() chars, but since extended chars with + * accents seem to be accepted, we will only check for chars that + * could confuse the lib. + **********************************************************************/ +static GBool _IsStringAlnum(const char *pszFname) +{ + GBool bOK = TRUE; + + while(bOK && *pszFname != '\0') + { + if (strchr(" \t.,/\\", (unsigned char)*pszFname) != NULL) + bOK = FALSE; + pszFname ++; + } + + return bOK; +} + +/********************************************************************** + * _AVCE00WriteRenameTable() + * + * Rename the table and the system fields in a tabledef that will + * be written to a new coverage. + **********************************************************************/ +static void _AVCE00WriteRenameTable(AVCTableDef *psTableDef, + const char *pszNewCoverName) +{ + char szOldName[40], szOldExt[40], szNewName[40], *pszTmp; + char szSysId[40], szUserId[40]; + int i; + + strcpy(szNewName, pszNewCoverName); + for(i=0; szNewName[i] != '\0'; i++) + szNewName[i] = toupper(szNewName[i]); + + /*----------------------------------------------------------------- + * Extract components from the current table name. + *----------------------------------------------------------------*/ + strcpy(szOldName, psTableDef->szTableName); + + if ( !EQUAL(psTableDef->szExternal, "XX") || + (pszTmp = strchr(szOldName, '.')) == NULL ) + return; /* We don't deal with that table */ + + *pszTmp = '\0'; + pszTmp++; + + strcpy(szOldExt, pszTmp); + if ( (pszTmp = strchr(szOldExt, ' ')) != NULL ) + *pszTmp = '\0'; + + if (strlen(szOldExt) < 3) + return; /* We don't deal with that table */ + + /*----------------------------------------------------------------- + * Look for system attributes with same name as table + * If the table name extension is followed by a subclass name + * (e.g. "TEST.PATCOUNTY") then this subclass is used to build + * the system attributes (COUNTY# and COUNTY-ID) and thus we do + * not need to rename them + * Otherwise (e.g. COUNTY.PAT) the coverage name is used and then + * we need to rename these attribs for the new coverage name. + *----------------------------------------------------------------*/ + if (strlen(szOldExt) == 3) + { + sprintf(szSysId, "%s#", szOldName); + sprintf(szUserId, "%s-ID", szOldName); + + for(i=0; inumFields; i++) + { + /* Remove trailing spaces */ + if ((pszTmp=strchr(psTableDef->pasFieldDef[i].szName,' '))!=NULL) + *pszTmp = '\0'; + + if (EQUAL(psTableDef->pasFieldDef[i].szName, szSysId)) + { + sprintf(psTableDef->pasFieldDef[i].szName, "%s#", szNewName); + } + else if (EQUAL(psTableDef->pasFieldDef[i].szName, szUserId)) + { + sprintf(psTableDef->pasFieldDef[i].szName, "%s-ID", szNewName); + } + } + } + + /*----------------------------------------------------------------- + * Build new table name + *----------------------------------------------------------------*/ + sprintf(psTableDef->szTableName, "%s.%s", szNewName, szOldExt); + +} + +/********************************************************************** + * _AVCE00WriteCreateCoverFile() + * + * Create a coverage file for the specified file type. + * + * The main part of the work is to find the right filename to use based on + * the file type, the coverage precision, etc... the rest of job is + * done by AVCBinWriteCreate(). + * + * Returns 0 on success, or -1 if an error happened. + * + * AVCWriteCloseCoverFile() will eventually have to be called to release the + * resources used by the AVCBinFile structure. + **********************************************************************/ +int _AVCE00WriteCreateCoverFile(AVCE00WritePtr psInfo, AVCFileType eType, + const char *pszLine, AVCTableDef *psTableDef) +{ + char *pszPath, szFname[50]=""; + int i, nStatus = 0; + + /* By now, new coverage precision should have been established */ + CPLAssert(psInfo->nPrecision != AVC_DEFAULT_PREC); + + /*----------------------------------------------------------------- + * Establish filename based on file type, precision, and possibly the + * contents of the header line. + *----------------------------------------------------------------*/ + pszPath = psInfo->pszCoverPath; + switch(eType) + { + case AVCFileARC: + strcpy(szFname, "arc"); + break; + case AVCFilePAL: + strcpy(szFname, "pal"); + break; + case AVCFileCNT: + strcpy(szFname, "cnt"); + break; + case AVCFileLAB: + strcpy(szFname, "lab"); + break; + case AVCFileTOL: + if (psInfo->nPrecision == AVC_SINGLE_PREC) + strcpy(szFname, "tol"); + else + strcpy(szFname, "par"); + break; + case AVCFilePRJ: + strcpy(szFname, "prj"); + break; + case AVCFileTXT: + strcpy(szFname, "txt"); + break; + case AVCFileTX6: + /* For TX6/TX7: the filename is subclass_name.txt + */ + + /* See bug 1261: It seems that empty subclass names are valid + * for TX7. In this case we'll default the filename to txt.txt + */ + if (pszLine[0] == '\0') + { + strcpy(szFname, "txt.txt"); + } + else if (strlen(pszLine) > 30 || strchr(pszLine, ' ') != NULL) + CPLError(CE_Failure, CPLE_IllegalArg, + "Invalid TX6/TX7 subclass name \"%s\"", pszLine); + else + sprintf(szFname, "%s.txt", pszLine); + break; + case AVCFileRPL: + /* For RPL and RXP: the filename is region_name.pal or region_name.rxp + */ + if (strlen(pszLine) > 30 || strchr(pszLine, ' ') != NULL) + CPLError(CE_Failure, CPLE_IllegalArg, + "Invalid RPL region name \"%s\"", pszLine); + else + sprintf(szFname, "%s.pal", pszLine); + break; + case AVCFileRXP: + if (strlen(pszLine) > 30 || strchr(pszLine, ' ') != NULL) + CPLError(CE_Failure, CPLE_IllegalArg, + "Invalid RXP name \"%s\"", pszLine); + else + sprintf(szFname, "%s.rxp", pszLine); + break; + case AVCFileTABLE: + /*------------------------------------------------------------- + * For tables, Filename will be based on info in the psTableDef + * but we need to rename the table and the system attributes + * based on the new coverage name. + *------------------------------------------------------------*/ + if (psInfo->eCoverType != AVCCoverPC && + psInfo->eCoverType != AVCCoverPC2) + pszPath = psInfo->pszInfoPath; + _AVCE00WriteRenameTable(psTableDef, psInfo->pszCoverName); + break; + default: + CPLError(CE_Failure, CPLE_IllegalArg, + "_AVCE00WriteCreateCoverFile(): Unsupported file type!"); + nStatus = -1; + break; + } + + /*----------------------------------------------------------------- + * V7 coverage filenames default to have a .adf extension + * but PC coverage filenames (except .dbf tables) have no extensions. + *----------------------------------------------------------------*/ + if (psInfo->eCoverType == AVCCoverV7 && strchr(szFname, '.') == NULL) + strcat(szFname, ".adf"); + + /*----------------------------------------------------------------- + * Make sure filename is all lowercase and attempt to create the file + *----------------------------------------------------------------*/ + for(i=0; szFname[i] != '\0'; i++) + szFname[i] = tolower(szFname[i]); + + if (nStatus == 0) + { + psInfo->eCurFileType = eType; + + if (eType == AVCFileTABLE) + psInfo->hFile = AVCBinWriteCreateTable(pszPath, + psInfo->pszCoverName, + psTableDef, + psInfo->eCoverType, + psInfo->nPrecision, + psInfo->psDBCSInfo); + else + + psInfo->hFile = AVCBinWriteCreate(pszPath, szFname, + psInfo->eCoverType, + eType, psInfo->nPrecision, + psInfo->psDBCSInfo); + + if (psInfo->hFile == NULL) + { + nStatus = -1; + psInfo->eCurFileType = AVCFileUnknown; + } + } + + return nStatus; +} + +/********************************************************************** + * _AVCE00WriteCloseCoverFile() + * + * Close current coverage file and reset the contents of psInfo. + * + * File should have been previously opened by _AVCE00WriteCreateCoverFile(). + * + **********************************************************************/ +void _AVCE00WriteCloseCoverFile(AVCE00WritePtr psInfo) +{ + /*----------------------------------------------------------------- + * PRJ sections behave differently... since there is only one "object" + * per section, they accumulate lines while we read them, and we + * write everything at once when we reach the end-of-section (EOP) line. + *----------------------------------------------------------------*/ + if (psInfo->eCurFileType == AVCFilePRJ) + { + AVCBinWriteObject(psInfo->hFile, psInfo->hParseInfo->cur.papszPrj); + } + + AVCBinWriteClose(psInfo->hFile); + psInfo->hFile = NULL; + psInfo->eCurFileType = AVCFileUnknown; +} + +/********************************************************************** + * AVCE00WriteNextLine() + * + * Take the next line of E00 input for this coverage, parse it and + * write the result to the coverage. + * + * Important Note: The E00 source lines are assumed to be valid... the + * library performs no validation on the consistency of what it is + * given as input (i.e. topology, polygons consistency, etc.). + * So the coverage that will be created will be only as good as the + * E00 input that is used to generate it. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int AVCE00WriteNextLine(AVCE00WritePtr psInfo, const char *pszLine) +{ + /*----------------------------------------------------------------- + * TODO: Update this call to use _AVCE00ReadNextLineE00(), if + * possible. + *----------------------------------------------------------------*/ + + int nStatus = 0; + + CPLErrorReset(); + + /*----------------------------------------------------------------- + * If we're at the top level inside a supersection... check if this + * supersection ends here. + *----------------------------------------------------------------*/ + if (AVCE00ParseSuperSectionEnd(psInfo->hParseInfo, pszLine) == TRUE) + { + /* Nothing to do... it's all been done by the call to + * AVCE00ParseSuperSectionEnd() + */ + } + else if (psInfo->eCurFileType == AVCFileUnknown) + { + /*------------------------------------------------------------- + * We're at the top level or inside a supersection... waiting + * to encounter a valid section or supersection header + * (i.e. "ARC 2", etc...) + *------------------------------------------------------------*/ + + /*------------------------------------------------------------- + * First check for a supersection header (TX6, RXP, IFO, ...) + *------------------------------------------------------------*/ + if ( AVCE00ParseSuperSectionHeader(psInfo->hParseInfo, + pszLine) == AVCFileUnknown ) + { + /*--------------------------------------------------------- + * This was not a supersection header... check if it's a simple + * section header + *--------------------------------------------------------*/ + psInfo->eCurFileType=AVCE00ParseSectionHeader(psInfo->hParseInfo, + pszLine); + } + + /*------------------------------------------------------------- + * If the coverage was created using AVC_DEFAULT_PREC and we are + * processing the first section header, then use this section's + * precision for the new coverage. + * (Note: this code segment will be executed only once per + * coverage and only if AVC_DEFAULT_PREC was selected) + *------------------------------------------------------------*/ + if (psInfo->nPrecision == AVC_DEFAULT_PREC && + psInfo->eCurFileType != AVCFileUnknown) + { + psInfo->nPrecision = psInfo->hParseInfo->nPrecision; + } + + if (psInfo->eCurFileType == AVCFileTABLE) + { + /*--------------------------------------------------------- + * We can't create the file for a TABLE until the + * whole header has been read... send the first header + * line to the parser and wait until the whole header has + * been read. + *--------------------------------------------------------*/ + AVCE00ParseNextLine(psInfo->hParseInfo, pszLine); + } + else if (psInfo->eCurFileType != AVCFileUnknown) + { + /*--------------------------------------------------------- + * OK, we've found a valid section header... create the + * corresponding file in the coverage. + * Note: supersection headers don't trigger the creation + * of any output file... they just alter the psInfo state. + *--------------------------------------------------------*/ + + nStatus = _AVCE00WriteCreateCoverFile(psInfo, + psInfo->eCurFileType, + psInfo->hParseInfo->pszSectionHdrLine, + NULL); + } + } + else if (psInfo->eCurFileType == AVCFileTABLE && + ! psInfo->hParseInfo->bTableHdrComplete ) + { + /*------------------------------------------------------------- + * We're reading a TABLE header... continue reading lines + * from the header, and create the output file only once + * the header will have been completely read. + * + * Note: When parsing a TABLE, the first object returned will + * be the AVCTableDef, then data records will follow. + *------------------------------------------------------------*/ + AVCTableDef *psTableDef; + psTableDef = (AVCTableDef*)AVCE00ParseNextLine(psInfo->hParseInfo, + pszLine); + if (psTableDef) + { + nStatus = _AVCE00WriteCreateCoverFile(psInfo, + psInfo->eCurFileType, + psInfo->hParseInfo->pszSectionHdrLine, + psTableDef); + } + } + else + { + /*------------------------------------------------------------- + * We're are in the middle of a section... first check if we + * have reached the end. + * + * note: The first call to AVCE00ParseSectionEnd() with FALSE will + * not reset the parser until we close the file... and then + * we call the function again to reset the parser. + *------------------------------------------------------------*/ + if (AVCE00ParseSectionEnd(psInfo->hParseInfo, pszLine, FALSE)) + { + _AVCE00WriteCloseCoverFile(psInfo); + AVCE00ParseSectionEnd(psInfo->hParseInfo, pszLine, TRUE); + } + else + /*------------------------------------------------------------- + * ... not at the end yet, so continue reading objects. + *------------------------------------------------------------*/ + { + void *psObj; + psObj = AVCE00ParseNextLine(psInfo->hParseInfo, pszLine); + + if (psObj) + AVCBinWriteObject(psInfo->hFile, psObj); + } + } + + + if (psInfo->hParseInfo->bForceEndOfSection) + { + /*------------------------------------------------------------- + * The last call encountered an implicit end of section, so + * we close the section now without waiting for an end-of-section + * line (there won't be any!)... and get ready to proceed with + * the next section. + * This is used for TABLEs. + *------------------------------------------------------------*/ + _AVCE00WriteCloseCoverFile(psInfo); + AVCE00ParseSectionEnd(psInfo->hParseInfo, pszLine, TRUE); + /* psInfo->hParseInfo->bForceEndOfSection = FALSE; */ + } + + if (CPLGetLastErrorNo() != 0) + nStatus = -1; + + return nStatus; +} + + +/********************************************************************** + * AVCE00DeleteCoverage() + * + * Delete a coverage directory, its contents, and the associated info + * tables. + * + * Note: + * When deleting tables, only the ../info/arc????.nit and arc????.dat + * need to be deleted; the arc.dir does not need to be updated. This + * is exactly what Arc/Info's KILL command does. + * + * Returns 0 on success or -1 on error. + **********************************************************************/ +int AVCE00DeleteCoverage(const char *pszCoverToDelete) +{ + int i, j, nStatus = 0; + char *pszInfoPath, *pszCoverPath, *pszCoverName; + const char *pszFname; + char **papszTables=NULL, **papszFiles=NULL; + AVCE00ReadPtr psInfo; + VSIStatBuf sStatBuf; + AVCCoverType eCoverType; + + CPLErrorReset(); + + /*----------------------------------------------------------------- + * Since we don't want to duplicate all the logic to figure coverage + * and info dir name, etc... we'll simply open the coverage and + * grab the info we need from the coverage handle. + * By the same way, this will verify that the coverage exists and is + * valid. + *----------------------------------------------------------------*/ + psInfo = AVCE00ReadOpen(pszCoverToDelete); + + if (psInfo == NULL) + { + CPLError(CE_Failure, CPLE_FileIO, + "Cannot delete coverage %s: it does not appear to be valid\n", + pszCoverToDelete); + return -1; + } + + pszCoverPath = CPLStrdup(psInfo->pszCoverPath); + pszInfoPath = CPLStrdup(psInfo->pszInfoPath); + pszCoverName = CPLStrdup(psInfo->pszCoverName); + eCoverType = psInfo->eCoverType; + + AVCE00ReadClose(psInfo); + + /*----------------------------------------------------------------- + * Delete files in cover directory. + *----------------------------------------------------------------*/ + papszFiles = CPLReadDir(pszCoverPath); + for(i=0; nStatus==0 && papszFiles && papszFiles[i]; i++) + { + if (!EQUAL(".", papszFiles[i]) && + !EQUAL("..", papszFiles[i])) + { + pszFname = CPLSPrintf("%s%s", pszCoverPath, papszFiles[i]); + if (unlink(pszFname) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed deleting %s%s", + pszCoverPath, papszFiles[i]); + nStatus = -1; + break; + } + } + } + + CSLDestroy(papszFiles); + papszFiles = NULL; + + /*----------------------------------------------------------------- + * Get the list of info files (ARC????) to delete and delete them + * (No 'info' directory for PC coverages) + *----------------------------------------------------------------*/ + if (nStatus == 0 && eCoverType != AVCCoverPC && eCoverType != AVCCoverPC2) + { + papszTables = AVCBinReadListTables(pszInfoPath, pszCoverName, + &papszFiles, eCoverType, + NULL /*DBCSInfo*/); + + for(i=0; nStatus==0 && papszFiles && papszFiles[i]; i++) + { + /* Convert table filename to lowercases */ + for(j=0; papszFiles[i] && papszFiles[i][j]!='\0'; j++) + papszFiles[i][j] = tolower(papszFiles[i][j]); + + /* Delete the .DAT file */ + pszFname = CPLSPrintf("%s%s.dat", pszInfoPath, papszFiles[i]); + if ( VSIStat(pszFname, &sStatBuf) != -1 && + unlink(pszFname) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed deleting %s%s", + pszInfoPath, papszFiles[i]); + nStatus = -1; + break; + } + + /* Delete the .DAT file */ + pszFname = CPLSPrintf("%s%s.nit", pszInfoPath, papszFiles[i]); + if ( VSIStat(pszFname, &sStatBuf) != -1 && + unlink(pszFname) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed deleting %s%s", + pszInfoPath, papszFiles[i]); + nStatus = -1; + break; + } + } + + CSLDestroy(papszTables); + CSLDestroy(papszFiles); + } + + /*----------------------------------------------------------------- + * Delete the coverage directory itself + * In some cases, the directory could be locked by another application + * on the same system or somewhere on the network. + * Define AVC_IGNORE_RMDIR_ERROR at compile time if you want this + * error to be ignored. + *----------------------------------------------------------------*/ + if (VSIRmdir(pszCoverPath) != 0) + { +#ifndef AVC_IGNORE_RMDIR_ERROR + CPLError(CE_Failure, CPLE_FileIO, + "Failed deleting directory %s", pszCoverPath); + nStatus = -1; +#endif + } + + CPLFree(pszCoverPath); + CPLFree(pszInfoPath); + CPLFree(pszCoverName); + + return nStatus; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_mbyte.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_mbyte.c new file mode 100644 index 000000000..022333d1d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_mbyte.c @@ -0,0 +1,539 @@ +/* $Id: avc_mbyte.c,v 1.4 2008/07/23 20:51:38 dmorissette Exp $ + * + * Name: avc_mbyte.c + * Project: Arc/Info vector coverage (AVC) E00->BIN conversion library + * Language: ANSI C + * Purpose: Functions to handle multibyte character conversions. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2005, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: avc_mbyte.c,v $ + * Revision 1.4 2008/07/23 20:51:38 dmorissette + * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char + * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495) + * + * Revision 1.3 2005/06/03 03:49:59 daniel + * Update email address, website url, and copyright dates + * + * Revision 1.2 2000/09/22 19:45:21 daniel + * Switch to MIT-style license + * + * Revision 1.1 2000/05/29 15:31:03 daniel + * Initial revision - Japanese support + * + **********************************************************************/ + +#include "avc.h" + +#ifdef _WIN32 +# include +#endif + +static int _AVCDetectJapaneseEncoding(const GByte *pszLine); +static const GByte *_AVCJapanese2ArcDBCS(AVCDBCSInfo *psDBCSInfo, + const GByte *pszLine, + int nMaxOutputLen); +static const GByte *_AVCArcDBCS2JapaneseShiftJIS(AVCDBCSInfo *psDBCSInfo, + const GByte *pszLine, + int nMaxOutputLen); + +/*===================================================================== + * Functions to handle multibyte char conversions + *====================================================================*/ + +#define IS_ASCII(c) ((c) < 0x80) + + +/********************************************************************** + * AVCAllocDBCSInfo() + * + * Alloc and init a new AVCDBCSInfo structure. + **********************************************************************/ +AVCDBCSInfo *AVCAllocDBCSInfo() +{ + AVCDBCSInfo *psInfo; + + psInfo = (AVCDBCSInfo*)CPLCalloc(1, sizeof(AVCDBCSInfo)); + + psInfo->nDBCSCodePage = AVCGetDBCSCodePage(); + psInfo->nDBCSEncoding = AVC_CODE_UNKNOWN; + psInfo->pszDBCSBuf = NULL; + psInfo->nDBCSBufSize = 0; + + return psInfo; +} + +/********************************************************************** + * AVCFreeDBCSInfo() + * + * Release all memory associated with a AVCDBCSInfo structure. + **********************************************************************/ +void AVCFreeDBCSInfo(AVCDBCSInfo *psInfo) +{ + if (psInfo) + { + CPLFree(psInfo->pszDBCSBuf); + CPLFree(psInfo); + } +} + +/********************************************************************** + * AVCGetDBCSCodePage() + * + * Fetch current multibyte codepage on the system. + * Returns a valid codepage number, or 0 if the codepage is single byte or + * unsupported. + **********************************************************************/ +int AVCGetDBCSCodePage() +{ +#ifdef _WIN32 + int nCP; + nCP = _getmbcp(); + + /* Check if that's a supported codepage */ + if (nCP == AVC_DBCS_JAPANESE) + return nCP; +#endif + + return 0; +} + + +/********************************************************************** + * AVCE00DetectEncoding() + * + * Try to detect the encoding used in the current file by examining lines + * of input. + * + * Returns TRUE once the encoding is established, or FALSE if more lines + * of input are required to establish the encoding. + **********************************************************************/ +GBool AVCE00DetectEncoding(AVCDBCSInfo *psDBCSInfo, const GByte *pszLine) +{ + if (psDBCSInfo == NULL || psDBCSInfo->nDBCSCodePage == 0 || + psDBCSInfo->nDBCSEncoding != AVC_CODE_UNKNOWN) + { + /* Either single byte codepage, or encoding has already been detected + */ + return TRUE; + } + + switch (psDBCSInfo->nDBCSCodePage) + { + case AVC_DBCS_JAPANESE: + psDBCSInfo->nDBCSEncoding = + _AVCDetectJapaneseEncoding(pszLine); + break; + default: + psDBCSInfo->nDBCSEncoding = AVC_CODE_UNKNOWN; + return TRUE; /* Codepage not supported... no need to scan more lines*/ + } + + if (psDBCSInfo->nDBCSEncoding != AVC_CODE_UNKNOWN) + return TRUE; /* We detected the encoding! */ + + return FALSE; +} + + +/********************************************************************** + * AVCE00Convert2ArcDBCS() + * + * If encoding is still unknown, try to detect the encoding used in the + * current file, and then convert the string to an encoding validfor output + * to a coverage. + * + * Returns a reference to a const buffer that should not be freed by the + * caller. It can be either the original string buffer or a ref. to an + * internal buffer. + **********************************************************************/ +const GByte *AVCE00Convert2ArcDBCS(AVCDBCSInfo *psDBCSInfo, + const GByte *pszLine, + int nMaxOutputLen) +{ + const GByte *pszOutBuf = NULL; + GByte *pszTmp = NULL; + GBool bAllAscii; + + if (psDBCSInfo == NULL || + psDBCSInfo->nDBCSCodePage == 0 || pszLine == NULL) + { + /* Single byte codepage... nothing to do + */ + return pszLine; + } + + /* If string is all ASCII then there is nothing to do... + */ + pszTmp = (GByte *)pszLine; + for(bAllAscii = TRUE ; bAllAscii && pszTmp && *pszTmp; pszTmp++) + { + if ( !IS_ASCII(*pszTmp) ) + bAllAscii = FALSE; + } + if (bAllAscii) + return pszLine; + + /* Make sure output buffer is large enough. + * We add 2 chars to buffer size to simplify processing... no need to + * check if second byte of a pair would overflow buffer. + */ + if (psDBCSInfo->pszDBCSBuf == NULL || + psDBCSInfo->nDBCSBufSize < nMaxOutputLen+2) + { + psDBCSInfo->nDBCSBufSize = nMaxOutputLen+2; + psDBCSInfo->pszDBCSBuf = + (GByte *)CPLRealloc(psDBCSInfo->pszDBCSBuf, + psDBCSInfo->nDBCSBufSize* + sizeof(GByte)); + } + + /* Do the conversion according to current code page + */ + switch (psDBCSInfo->nDBCSCodePage) + { + case AVC_DBCS_JAPANESE: + pszOutBuf = _AVCJapanese2ArcDBCS(psDBCSInfo, + pszLine, + nMaxOutputLen); + break; + default: + /* We should never get here anyways, but just in case return pszLine + */ + CPLAssert( !"SHOULD NEVER GET HERE" ); + pszOutBuf = pszLine; + } + + return pszOutBuf; +} + +/********************************************************************** + * AVCE00ConvertFromArcDBCS() + * + * Convert DBCS encoding in binary coverage files to E00 encoding. + * + * Returns a reference to a const buffer that should not be freed by the + * caller. It can be either the original string buffer or a ref. to an + * internal buffer. + **********************************************************************/ +const GByte *AVCE00ConvertFromArcDBCS(AVCDBCSInfo *psDBCSInfo, + const GByte *pszLine, + int nMaxOutputLen) +{ + const GByte *pszOutBuf = NULL; + GByte *pszTmp; + GBool bAllAscii; + + if (psDBCSInfo == NULL || + psDBCSInfo->nDBCSCodePage == 0 || pszLine == NULL) + { + /* Single byte codepage... nothing to do + */ + return pszLine; + } + + /* If string is all ASCII then there is nothing to do... + */ + pszTmp = (GByte *)pszLine; + for(bAllAscii = TRUE ; bAllAscii && pszTmp && *pszTmp; pszTmp++) + { + if ( !IS_ASCII(*pszTmp) ) + bAllAscii = FALSE; + } + if (bAllAscii) + return pszLine; + + /* Make sure output buffer is large enough. + * We add 2 chars to buffer size to simplify processing... no need to + * check if second byte of a pair would overflow buffer. + */ + if (psDBCSInfo->pszDBCSBuf == NULL || + psDBCSInfo->nDBCSBufSize < nMaxOutputLen+2) + { + psDBCSInfo->nDBCSBufSize = nMaxOutputLen+2; + psDBCSInfo->pszDBCSBuf = + (GByte *)CPLRealloc(psDBCSInfo->pszDBCSBuf, + psDBCSInfo->nDBCSBufSize* + sizeof(GByte)); + } + + /* Do the conversion according to current code page + */ + switch (psDBCSInfo->nDBCSCodePage) + { + case AVC_DBCS_JAPANESE: + pszOutBuf = _AVCArcDBCS2JapaneseShiftJIS(psDBCSInfo, + pszLine, + nMaxOutputLen); + break; + default: + /* We should never get here anyways, but just in case return pszLine + */ + pszOutBuf = pszLine; + } + + return pszOutBuf; +} + +/*===================================================================== + *===================================================================== + * Functions Specific to Japanese encoding (CodePage 932). + * + * For now we assume that we can receive only Katakana, Shift-JIS, or EUC + * encoding as input. Coverages use EUC encoding in most cases, except + * for Katakana characters that are prefixed with a 0x8e byte. + * + * Most of the Japanese conversion functions are based on information and + * algorithms found at: + * http://www.mars.dti.ne.jp/~torao/program/appendix/japanese-en.html + *===================================================================== + *====================================================================*/ + +/********************************************************************** + * _AVCDetectJapaneseEncoding() + * + * Scan a line of text to try to establish the type of japanese encoding + * + * Returns the encoding number (AVC_CODE_JAP_*), or AVC_CODE_UNKNOWN if no + * specific encoding was detected. + **********************************************************************/ + +#define IS_JAP_SHIFTJIS_1(c) ((c) >= 0x81 && (c) <= 0x9f) +#define IS_JAP_SHIFTJIS_2(c) (((c) >= 0x40 && (c) <= 0x7e) || \ + ((c) >= 0x80 && (c) <= 0xA0) ) +#define IS_JAP_EUC_1(c) ((c) >= 0xF0 && (c) <= 0xFE) +#define IS_JAP_EUC_2(c) ((c) >= 0xFD && (c) <= 0xFE) +#define IS_JAP_KANA(c) ((c) >= 0xA1 && (c) <= 0xDF) + +static int _AVCDetectJapaneseEncoding(const GByte *pszLine) +{ + int nEncoding = AVC_CODE_UNKNOWN; + + for( ; nEncoding == AVC_CODE_UNKNOWN && pszLine && *pszLine; pszLine++) + { + if (IS_ASCII(*pszLine)) + continue; + else if (IS_JAP_SHIFTJIS_1(*pszLine)) + { + nEncoding = AVC_CODE_JAP_SHIFTJIS; + break; + } + else if (IS_JAP_KANA(*pszLine) && *(pszLine+1) && + (IS_ASCII(*(pszLine+1)) || + (*(pszLine+1)>=0x80 && *(pszLine+1)<=0xA0) ) ) + { + nEncoding = AVC_CODE_JAP_SHIFTJIS; /* SHIFT-JIS + Kana */ + break; + } + else if (IS_JAP_EUC_1(*pszLine)) + { + nEncoding = AVC_CODE_JAP_EUC; + break; + } + + if (*(++pszLine) == '\0') + break; + + if (IS_JAP_SHIFTJIS_2(*pszLine)) + { + nEncoding = AVC_CODE_JAP_SHIFTJIS; + break; + } + else if (IS_JAP_EUC_2(*pszLine)) + { + nEncoding = AVC_CODE_JAP_EUC; + break; + } + } + + return nEncoding; +} + + +/********************************************************************** + * _AVCJapanese2ArcDBCS() + * + * Try to detect type of Japanese encoding if not done yet, and convert + * string from Japanese to proper coverage DBCS encoding. + **********************************************************************/ +static const GByte *_AVCJapanese2ArcDBCS(AVCDBCSInfo *psDBCSInfo, + const GByte *pszLine, + int nMaxOutputLen) +{ + GByte *pszOut; + int iDst; + + pszOut = psDBCSInfo->pszDBCSBuf; + + if (psDBCSInfo->nDBCSEncoding == AVC_CODE_UNKNOWN) + { + /* Type of encoding (Shift-JIS or EUC) not known yet... try to + * detect it now. + */ + psDBCSInfo->nDBCSEncoding = _AVCDetectJapaneseEncoding(pszLine); + +/* + if (psDBCSInfo->nDBCSEncoding == AVC_CODE_JAP_SHIFTJIS) + { + printf("Found Japanese Shift-JIS encoding\n"); + } + else if (psDBCSInfo->nDBCSEncoding == AVC_CODE_JAP_EUC) + { + printf("Found Japanese EUC encoding\n"); + } +*/ + } + + for(iDst=0; *pszLine && iDst < nMaxOutputLen; pszLine++) + { + if (IS_ASCII(*pszLine)) + { + /* No transformation required for ASCII */ + pszOut[iDst++] = *pszLine; + } + else if ( psDBCSInfo->nDBCSEncoding==AVC_CODE_JAP_EUC && *(pszLine+1) ) + { + /* This must be a pair of EUC chars and both should be in + * the range 0xA1-0xFE + */ + pszOut[iDst++] = *(pszLine++); + pszOut[iDst++] = *pszLine; + } + else if ( IS_JAP_KANA(*pszLine) ) + { + /* Katakana char. prefix it with 0x8e */ + pszOut[iDst++] = 0x8e; + pszOut[iDst++] = *pszLine; + } + else if ( *(pszLine+1) ) + { + /* This must be a pair of Shift-JIS chars... convert them to EUC + * + * If we haven't been able to establish the encoding for sure + * yet, then it is possible that a pair of EUC chars could be + * treated as shift-JIS here... but there is not much we can do + * about that unless we scan the whole E00 input before we + * start the conversion. + */ + unsigned char leader, trailer; + leader = *(pszLine++); + trailer = *pszLine; + + if(leader <= 0x9F) leader -= 0x71; + else leader -= 0xB1; + leader = (leader << 1) + 1; + + if(trailer > 0x7F) trailer --; + if(trailer >= 0x9E) + { + trailer -= 0x7D; + leader ++; + } + else + { + trailer -= 0x1F; + } + + pszOut[iDst++] = leader | 0x80; + pszOut[iDst++] = trailer | 0x80; + } + else + { + /* We should never get here unless a double-byte pair was + * truncated... but just in case... + */ + pszOut[iDst++] = *pszLine; + } + + } + + pszOut[iDst] = '\0'; + + return psDBCSInfo->pszDBCSBuf; +} + + +/********************************************************************** + * _AVCArcDBCS2JapaneseShiftJIS() + * + * Convert string from coverage DBCS (EUC) to Japanese Shift-JIS. + * + * We know that binary coverages use a custom EUC encoding for japanese + * which is EUC + all Katakana chars are prefixed with 0x8e. So this + * function just does a simple conversion. + **********************************************************************/ +static const GByte *_AVCArcDBCS2JapaneseShiftJIS(AVCDBCSInfo *psDBCSInfo, + const GByte *pszLine, + int nMaxOutputLen) +{ + GByte *pszOut; + int iDst; + + pszOut = psDBCSInfo->pszDBCSBuf; + + for(iDst=0; *pszLine && iDst < nMaxOutputLen; pszLine++) + { + if (IS_ASCII(*pszLine)) + { + /* No transformation required for ASCII */ + pszOut[iDst++] = *pszLine; + } + else if (*pszLine == 0x8e && *(pszLine+1)) + { + pszLine++; /* Flush the 0x8e */ + pszOut[iDst++] = *pszLine; + } + else if (*(pszLine+1)) + { + /* This is a pair of EUC chars... convert them to Shift-JIS + */ + unsigned char leader, trailer; + leader = *(pszLine++) & 0x7F; + trailer = *pszLine & 0x7F; + + if((leader & 0x01) != 0) trailer += 0x1F; + else trailer += 0x7D; + if(trailer >= 0x7F) trailer ++; + + leader = ((leader - 0x21) >> 1) + 0x81; + if(leader > 0x9F) leader += 0x40; + + pszOut[iDst++] = leader; + pszOut[iDst++] = trailer; + } + else + { + /* We should never get here unless a double-byte pair was + * truncated... but just in case... + */ + pszOut[iDst++] = *pszLine; + } + + } + + pszOut[iDst] = '\0'; + + return psDBCSInfo->pszDBCSBuf; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_mbyte.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_mbyte.h new file mode 100644 index 000000000..0f90a37db --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_mbyte.h @@ -0,0 +1,98 @@ +/********************************************************************** + * $Id: avc_mbyte.h,v 1.4 2008/07/23 20:51:38 dmorissette Exp $ + * + * Name: avc.h + * Project: Arc/Info Vector coverage (AVC) BIN<->E00 conversion library + * Language: ANSI C + * Purpose: Header file containing all definitions for the library. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2005, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: avc_mbyte.h,v $ + * Revision 1.4 2008/07/23 20:51:38 dmorissette + * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char + * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495) + * + * Revision 1.3 2005/06/03 03:49:59 daniel + * Update email address, website url, and copyright dates + * + * Revision 1.2 2000/09/22 19:45:21 daniel + * Switch to MIT-style license + * + * Revision 1.1 2000/05/29 15:31:03 daniel + * Initial revision - Japanese support + * + **********************************************************************/ + +#ifndef _AVC_MBYTE_H_INCLUDED_ +#define _AVC_MBYTE_H_INCLUDED_ + +CPL_C_START + +/*--------------------------------------------------------------------- + * Supported multibyte codepage numbers + *--------------------------------------------------------------------*/ +#define AVC_DBCS_JAPANESE 932 + +#define AVC_CODE_UNKNOWN 0 + +/*--------------------------------------------------------------------- + * Definitions for Japanese encodings (AVC_DBCS_JAPANESE) + *--------------------------------------------------------------------*/ +#define AVC_CODE_JAP_UNKNOWN 0 +#define AVC_CODE_JAP_SHIFTJIS 1 +#define AVC_CODE_JAP_EUC 2 + + +/*--------------------------------------------------------------------- + * We use the following structure to keep track of DBCS info. + *--------------------------------------------------------------------*/ +typedef struct AVCDBCSInfo_t +{ + int nDBCSCodePage; + int nDBCSEncoding; + unsigned char *pszDBCSBuf; + int nDBCSBufSize; +} AVCDBCSInfo; + +/*--------------------------------------------------------------------- + * Functions prototypes + *--------------------------------------------------------------------*/ + +AVCDBCSInfo *AVCAllocDBCSInfo(); +void AVCFreeDBCSInfo(AVCDBCSInfo *psInfo); +int AVCGetDBCSCodePage(); +GBool AVCE00DetectEncoding(AVCDBCSInfo *psDBCSInfo, const GByte *pszLine); +const GByte *AVCE00Convert2ArcDBCS(AVCDBCSInfo *psDBCSInfo, + const GByte *pszLine, + int nMaxOutputLen); +const GByte *AVCE00ConvertFromArcDBCS(AVCDBCSInfo *psDBCSInfo, + const GByte *pszLine, + int nMaxOutputLen); + +CPL_C_END + +#endif /* _AVC_MBYTE_H_INCLUDED_ */ + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_misc.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_misc.c new file mode 100644 index 000000000..7f1b200d8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_misc.c @@ -0,0 +1,504 @@ +/********************************************************************** + * $Id: avc_misc.c,v 1.9 2005/06/03 03:49:59 daniel Exp $ + * + * Name: avc_misc.c + * Project: Arc/Info vector coverage (AVC) BIN<->E00 conversion library + * Language: ANSI C + * Purpose: Misc. functions used by several parts of the library + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2005, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: avc_misc.c,v $ + * Revision 1.9 2005/06/03 03:49:59 daniel + * Update email address, website url, and copyright dates + * + * Revision 1.8 2004/08/31 21:00:20 warmerda + * Applied Carl Anderson's patch to reduce the amount of stating while + * trying to discover filename "case" on Unix in AVCAdjustCaseSensitiveFilename. + * http://bugzilla.remotesensing.org/show_bug.cgi?id=314 + * + * Revision 1.7 2001/11/25 21:38:01 daniel + * Remap '\\' to '/' in AVCAdjustCaseSensitiveFilename() on Unix. + * + * Revision 1.6 2001/11/25 21:15:23 daniel + * Added hack (AVC_MAP_TYPE40_TO_DOUBLE) to map type 40 fields bigger than 8 + * digits to double precision as we generate E00 output (bug599) + * + * Revision 1.5 2000/09/26 20:21:04 daniel + * Added AVCCoverPC write + * + * Revision 1.4 2000/09/22 19:45:21 daniel + * Switch to MIT-style license + * + * Revision 1.3 2000/01/10 02:53:21 daniel + * Added AVCAdjustCaseSensitiveFilename() and AVCFileExists() + * + * Revision 1.2 1999/08/23 18:24:27 daniel + * Fixed support for attribute fields of type 40 + * + * Revision 1.1 1999/05/11 02:34:46 daniel + * Initial revision + * + **********************************************************************/ + +#include "avc.h" + + +/********************************************************************** + * AVCE00ComputeRecSize() + * + * Computes the number of chars required to generate a E00 attribute + * table record. + * + * Returns -1 on error, i.e. if it encounters an unsupported field type. + **********************************************************************/ +int _AVCE00ComputeRecSize(int numFields, AVCFieldInfo *pasDef, + GBool bMapType40ToDouble) +{ + int i, nType, nBufSize=0; + + /*------------------------------------------------------------- + * Add up the nbr of chars used by each field + *------------------------------------------------------------*/ + for(i=0; i < numFields; i++) + { + nType = pasDef[i].nType1*10; + if (nType == AVC_FT_DATE || nType == AVC_FT_CHAR || + nType == AVC_FT_FIXINT ) + { + nBufSize += pasDef[i].nSize; + } + else if (nType == AVC_FT_BININT && pasDef[i].nSize == 4) + nBufSize += 11; + else if (nType == AVC_FT_BININT && pasDef[i].nSize == 2) + nBufSize += 6; + else if (bMapType40ToDouble && + nType == AVC_FT_FIXNUM && pasDef[i].nSize > 8) + { + /* See explanation in AVCE00GenTableHdr() about this hack to remap + * type 40 fields to double precision floats. + */ + nBufSize += 24; /* Remap to double float */ + } + else if ((nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 4) || + nType == AVC_FT_FIXNUM ) + nBufSize += 14; + else if (nType == AVC_FT_BINFLOAT && pasDef[i].nSize == 8) + nBufSize += 24; + else + { + /*----------------------------------------------------- + * Hummm... unsupported field type... + *----------------------------------------------------*/ + CPLError(CE_Failure, CPLE_NotSupported, + "_AVCE00ComputeRecSize(): Unsupported field type: " + "(type=%d, size=%d)", + nType, pasDef[i].nSize); + return -1; + } + } + + return nBufSize; +} + + + +/********************************************************************** + * _AVCDestroyTableFields() + * + * Release all memory associated with an array of AVCField structures. + **********************************************************************/ +void _AVCDestroyTableFields(AVCTableDef *psTableDef, AVCField *pasFields) +{ + int i, nFieldType; + + if (pasFields) + { + for(i=0; inumFields; i++) + { + nFieldType = psTableDef->pasFieldDef[i].nType1*10; + if (nFieldType == AVC_FT_DATE || + nFieldType == AVC_FT_CHAR || + nFieldType == AVC_FT_FIXINT || + nFieldType == AVC_FT_FIXNUM) + { + CPLFree(pasFields[i].pszStr); + } + } + CPLFree(pasFields); + } + +} + +/********************************************************************** + * _AVCDestroyTableDef() + * + * Release all memory associated with a AVCTableDef structure. + * + **********************************************************************/ +void _AVCDestroyTableDef(AVCTableDef *psTableDef) +{ + if (psTableDef) + { + CPLFree(psTableDef->pasFieldDef); + CPLFree(psTableDef); + } +} + + +/********************************************************************** + * _AVCDupTableDef() + * + * Create a new copy of a AVCTableDef structure. + **********************************************************************/ +AVCTableDef *_AVCDupTableDef(AVCTableDef *psSrcDef) +{ + AVCTableDef *psNewDef; + + if (psSrcDef == NULL) + return NULL; + + psNewDef = (AVCTableDef*)CPLMalloc(1*sizeof(AVCTableDef)); + + memcpy(psNewDef, psSrcDef, sizeof(AVCTableDef)); + + psNewDef->pasFieldDef = (AVCFieldInfo*)CPLMalloc(psSrcDef->numFields* + sizeof(AVCFieldInfo)); + + memcpy(psNewDef->pasFieldDef, psSrcDef->pasFieldDef, + psSrcDef->numFields*sizeof(AVCFieldInfo)); + + return psNewDef; +} + + +/********************************************************************** + * AVCFileExists() + * + * Returns TRUE if a file with the specified name exists in the + * specified directory. + * + * For now I simply try to fopen() the file ... would it be more + * efficient to use stat() ??? + **********************************************************************/ +GBool AVCFileExists(const char *pszPath, const char *pszName) +{ + char *pszBuf; + GBool bFileExists = FALSE; + FILE *fp; + + pszBuf = (char*)CPLMalloc((strlen(pszPath)+strlen(pszName)+1)* + sizeof(char)); + sprintf(pszBuf, "%s%s", pszPath, pszName); + + AVCAdjustCaseSensitiveFilename(pszBuf); + + if ((fp = VSIFOpen(pszBuf, "rb")) != NULL) + { + bFileExists = TRUE; + VSIFClose(fp); + } + + CPLFree(pszBuf); + + return bFileExists; +} + +/********************************************************************** + * AVCAdjustCaseSensitiveFilename() + * + * Scan a filename and its path, adjust uppercase/lowercases if + * necessary, and return a reference to that filename. + * + * This function works on the original buffer and returns a reference to it. + * It does nothing on Windows systems where filenames are not case sensitive. + * + * NFW: It seems like this could be made somewhat more efficient by + * getting a directory listing and doing a case insensitive search in + * that list rather than all this stating that can be very expensive + * in some circumstances. However, at least with Carl's fix this is + * somewhat faster. + * see: http://buzilla.remotesensing.org/show_bug.cgi?id=314 + **********************************************************************/ +char *AVCAdjustCaseSensitiveFilename(char *pszFname) +{ + +#ifdef _WIN32 + /*----------------------------------------------------------------- + * Nothing to do on Windows + *----------------------------------------------------------------*/ + return pszFname; + +#else + /*----------------------------------------------------------------- + * Unix case. + *----------------------------------------------------------------*/ + VSIStatBuf sStatBuf; + char *pszTmpPath = NULL; + int nTotalLen, iTmpPtr; + GBool bValidPath; + + /*----------------------------------------------------------------- + * Remap '\\' to '/' + *----------------------------------------------------------------*/ + for(pszTmpPath = pszFname; *pszTmpPath != '\0'; pszTmpPath++) + { + if (*pszTmpPath == '\\') + *pszTmpPath = '/'; + } + + /*----------------------------------------------------------------- + * First check if the filename is OK as is. + *----------------------------------------------------------------*/ + if (VSIStat(pszFname, &sStatBuf) == 0) + { + return pszFname; + } + + pszTmpPath = CPLStrdup(pszFname); + nTotalLen = strlen(pszTmpPath); + + /*----------------------------------------------------------------- + * Try all lower case, check if the filename is OK as that. + *----------------------------------------------------------------*/ + for (iTmpPtr=0; iTmpPtr< nTotalLen; iTmpPtr++) + { + if ( pszTmpPath[iTmpPtr] >= 0x41 && pszTmpPath[iTmpPtr] <= 0x5a ) + pszTmpPath[iTmpPtr] += 32; + } + + if (VSIStat(pszTmpPath, &sStatBuf) == 0) + { + strcpy(pszFname, pszTmpPath); + CPLFree(pszTmpPath); + return pszFname; + } + + /*----------------------------------------------------------------- + * Try all upper case, check if the filename is OK as that. + *----------------------------------------------------------------*/ + for (iTmpPtr=0; iTmpPtr< nTotalLen; iTmpPtr++) + { + if ( pszTmpPath[iTmpPtr] >= 0x61 && pszTmpPath[iTmpPtr] <= 0x7a ) + pszTmpPath[iTmpPtr] -= 32; + } + + if (VSIStat(pszTmpPath, &sStatBuf) == 0) + { + strcpy(pszFname, pszTmpPath); + CPLFree(pszTmpPath); + return pszFname; + } + + /*----------------------------------------------------------------- + * OK, file either does not exist or has the wrong cases... we'll + * go backwards until we find a portion of the path that is valid. + *----------------------------------------------------------------*/ + iTmpPtr = nTotalLen; + bValidPath = FALSE; + + while(iTmpPtr > 0 && !bValidPath) + { + /*------------------------------------------------------------- + * Move back to the previous '/' separator + *------------------------------------------------------------*/ + pszTmpPath[--iTmpPtr] = '\0'; + while( iTmpPtr > 0 && pszTmpPath[iTmpPtr-1] != '/' ) + { + pszTmpPath[--iTmpPtr] = '\0'; + } + + if (iTmpPtr > 0 && VSIStat(pszTmpPath, &sStatBuf) == 0) + bValidPath = TRUE; + } + + CPLAssert(iTmpPtr >= 0); + + /*----------------------------------------------------------------- + * Assume that CWD is valid... so an empty path is a valid path + *----------------------------------------------------------------*/ + if (iTmpPtr == 0) + bValidPath = TRUE; + + /*----------------------------------------------------------------- + * OK, now that we have a valid base, reconstruct the whole path + * by scanning all the sub-directories. + * If we get to a point where a path component does not exist then + * we simply return the rest of the path as is. + *----------------------------------------------------------------*/ + while(bValidPath && strlen(pszTmpPath) < (size_t)nTotalLen) + { + char **papszDir=NULL; + int iEntry, iLastPartStart; + + iLastPartStart = iTmpPtr; + papszDir = CPLReadDir(pszTmpPath); + + /*------------------------------------------------------------- + * Add one component to the current path + *------------------------------------------------------------*/ + pszTmpPath[iTmpPtr] = pszFname[iTmpPtr]; + iTmpPtr++; + for( ; pszFname[iTmpPtr] != '\0' && pszFname[iTmpPtr]!='/'; iTmpPtr++) + { + pszTmpPath[iTmpPtr] = pszFname[iTmpPtr]; + } + + while(iLastPartStart < iTmpPtr && pszTmpPath[iLastPartStart] == '/') + iLastPartStart++; + + /*------------------------------------------------------------- + * And do a case insensitive search in the current dir... + *------------------------------------------------------------*/ + for(iEntry=0; papszDir && papszDir[iEntry]; iEntry++) + { + if (EQUAL(pszTmpPath+iLastPartStart, papszDir[iEntry])) + { + /* Fount it! */ + strcpy(pszTmpPath+iLastPartStart, papszDir[iEntry]); + break; + } + } + + if (iTmpPtr > 0 && VSIStat(pszTmpPath, &sStatBuf) != 0) + bValidPath = FALSE; + + CSLDestroy(papszDir); + } + + /*----------------------------------------------------------------- + * We reached the last valid path component... just copy the rest + * of the path as is. + *----------------------------------------------------------------*/ + if (iTmpPtr < nTotalLen-1) + { + strncpy(pszTmpPath+iTmpPtr, pszFname+iTmpPtr, nTotalLen-iTmpPtr); + } + + /*----------------------------------------------------------------- + * Update the source buffer and return. + *----------------------------------------------------------------*/ + strcpy(pszFname, pszTmpPath); + CPLFree(pszTmpPath); + + return pszFname; + +#endif +} + + + +/********************************************************************** + * AVCPrintRealValue() + * + * Format a floating point value according to the specified coverage + * precision (AVC_SINGLE/DOUBLE_PREC), and append the formatted value + * to the end of the pszBuf buffer. + * + * The function returns the number of characters added to the buffer. + **********************************************************************/ +int AVCPrintRealValue(char *pszBuf, int nPrecision, AVCFileType eType, + double dValue) +{ + static int numExpDigits=-1; + int nLen = 0; + + /* WIN32 systems' printf for floating point output generates 3 + * digits exponents (ex: 1.23E+012), but E00 files must have 2 digits + * exponents (ex: 1.23E+12). + * Run a test (only once per prg execution) to establish the number + * of exponent digits on the current platform. + */ + if (numExpDigits == -1) + { + char szBuf[50]; + int i; + + sprintf(szBuf, "%10.7E", 123.45); + numExpDigits = 0; + for(i=strlen(szBuf)-1; i>0; i--) + { + if (szBuf[i] == '+' || szBuf[i] == '-') + break; + numExpDigits++; + } + } + + /* We will append the value at the end of the current buffer contents. + */ + pszBuf = pszBuf+strlen(pszBuf); + + if (dValue < 0.0) + { + *pszBuf = '-'; + dValue = -1.0*dValue; + } + else + *pszBuf = ' '; + + + /* Just to make things more complicated, double values are + * output in a different format in attribute tables than in + * the other files! + */ + if (nPrecision == AVC_FORMAT_DBF_FLOAT) + { + /* Float stored in DBF table in PC coverages */ + sprintf(pszBuf+1, "%9.6E", dValue); + nLen = 13; + } + else if (nPrecision == AVC_DOUBLE_PREC && eType == AVCFileTABLE) + { + sprintf(pszBuf+1, "%20.17E", dValue); + nLen = 24; + } + else if (nPrecision == AVC_DOUBLE_PREC) + { + sprintf(pszBuf+1, "%17.14E", dValue); + nLen = 21; + } + else + { + sprintf(pszBuf+1, "%10.7E", dValue); + nLen = 14; + } + + /* Adjust number of exponent digits if necessary + */ + if (numExpDigits > 2) + { + int n; + n = strlen(pszBuf); + + pszBuf[n - numExpDigits] = pszBuf[n-2]; + pszBuf[n - numExpDigits +1] = pszBuf[n-1]; + pszBuf[n - numExpDigits +2] = '\0'; + } + + /* Just make sure that the actual output length is what we expected. + */ + CPLAssert(strlen(pszBuf) == (size_t)nLen); + + return nLen; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_rawbin.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_rawbin.c new file mode 100644 index 000000000..1eb517e94 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/avc_rawbin.c @@ -0,0 +1,662 @@ +/********************************************************************** + * $Id: avc_rawbin.c,v 1.14 2008/07/23 20:51:38 dmorissette Exp $ + * + * Name: avc_rawbin.c + * Project: Arc/Info vector coverage (AVC) BIN->E00 conversion library + * Language: ANSI C + * Purpose: Raw Binary file access functions. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2005, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: avc_rawbin.c,v $ + * Revision 1.14 2008/07/23 20:51:38 dmorissette + * Fixed GCC 4.1.x compile warnings related to use of char vs unsigned char + * (GDAL/OGR ticket http://trac.osgeo.org/gdal/ticket/2495) + * + * Revision 1.13 2005/06/03 03:49:59 daniel + * Update email address, website url, and copyright dates + * + * Revision 1.12 2004/08/19 23:41:04 warmerda + * fixed pointer aliasing optimization bug + * + * Revision 1.11 2000/09/22 19:45:21 daniel + * Switch to MIT-style license + * + * Revision 1.10 2000/05/29 15:36:07 daniel + * Fixed compile warning + * + * Revision 1.9 2000/05/29 15:31:31 daniel + * Added Japanese DBCS support + * + * Revision 1.8 2000/01/10 02:59:11 daniel + * Fixed problem in AVCRawBinOpen() when file not found + * + * Revision 1.7 1999/12/24 07:18:34 daniel + * Added PC Arc/Info coverages support + * + * Revision 1.6 1999/08/29 15:05:43 daniel + * Added source filename in "Attempt to read past EOF" error message + * + * Revision 1.5 1999/06/08 22:09:03 daniel + * Allow opening file with "r+" (but no real random access support yet) + * + * Revision 1.4 1999/05/11 02:10:51 daniel + * Added write support + * + * Revision 1.3 1999/03/03 19:55:21 daniel + * Fixed syntax error in the CPL_MSB version of AVCRawBinReadInt32() + * + * Revision 1.2 1999/02/25 04:20:08 daniel + * Modified AVCRawBinEOF() to detect EOF even if AVCRawBinFSeek() was used. + * + * Revision 1.1 1999/01/29 16:28:52 daniel + * Initial revision + * + **********************************************************************/ + +#include "avc.h" +#include "avc_mbyte.h" + +/*--------------------------------------------------------------------- + * Define a static flag and set it with the byte ordering on this machine + * we will then compare with this value to decide if we nned to swap + * bytes or not. + * + * CPL_MSB or CPL_LSB should be set in the makefile... the default is + * CPL_LSB. + *--------------------------------------------------------------------*/ +#ifndef CPL_LSB +static AVCByteOrder geSystemByteOrder = AVCBigEndian; +#else +static AVCByteOrder geSystemByteOrder = AVCLittleEndian; +#endif + +/*===================================================================== + * Stuff related to buffered reading of raw binary files + *====================================================================*/ + +/********************************************************************** + * AVCRawBinOpen() + * + * Open a binary file for reading with buffering, or writing. + * + * Returns a valid AVCRawBinFile structure, or NULL if the file could + * not be opened or created. + * + * AVCRawBinClose() will eventually have to be called to release the + * resources used by the AVCRawBinFile structure. + **********************************************************************/ +AVCRawBinFile *AVCRawBinOpen(const char *pszFname, const char *pszAccess, + AVCByteOrder eFileByteOrder, + AVCDBCSInfo *psDBCSInfo) +{ + AVCRawBinFile *psFile; + + psFile = (AVCRawBinFile*)CPLCalloc(1, sizeof(AVCRawBinFile)); + + /*----------------------------------------------------------------- + * Validate access mode and open/create file. + * For now we support only: "r" for read-only or "w" for write-only + * or "a" for append. + * + * A case for "r+" is included here, but random access is not + * properly supported yet... so this option should be used with care. + *----------------------------------------------------------------*/ + if (EQUALN(pszAccess, "r+", 2)) + { + psFile->eAccess = AVCReadWrite; + psFile->fp = VSIFOpen(pszFname, "r+b"); + } + else if (EQUALN(pszAccess, "r", 1)) + { + psFile->eAccess = AVCRead; + psFile->fp = VSIFOpen(pszFname, "rb"); + } + else if (EQUALN(pszAccess, "w", 1)) + { + psFile->eAccess = AVCWrite; + psFile->fp = VSIFOpen(pszFname, "wb"); + } + else if (EQUALN(pszAccess, "a", 1)) + { + psFile->eAccess = AVCWrite; + psFile->fp = VSIFOpen(pszFname, "ab"); + } + else + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Acces mode \"%s\" not supported.", pszAccess); + CPLFree(psFile); + return NULL; + } + + /*----------------------------------------------------------------- + * Check that file was opened succesfully, and init struct. + *----------------------------------------------------------------*/ + if (psFile->fp == NULL) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Failed to open file %s", pszFname); + CPLFree(psFile); + return NULL; + } + + /*----------------------------------------------------------------- + * OK... Init psFile struct + *----------------------------------------------------------------*/ + psFile->pszFname = CPLStrdup(pszFname); + + psFile->eByteOrder = eFileByteOrder; + psFile->psDBCSInfo = psDBCSInfo; /* Handle on dataset DBCS info */ + + /*----------------------------------------------------------------- + * One can set nFileDataSize based on some header fields to force + * EOF beyond a given point in the file. Useful for cases like + * PC Arc/Info where the physical file size is always a multiple of + * 256 bytes padded with some junk at the end. + *----------------------------------------------------------------*/ + psFile->nFileDataSize = -1; + + return psFile; +} + +/********************************************************************** + * AVCRawBinClose() + * + * Close a binary file previously opened with AVCRawBinOpen() and release + * any memory used by the handle. + **********************************************************************/ +void AVCRawBinClose(AVCRawBinFile *psFile) +{ + if (psFile) + { + if (psFile->fp) + VSIFClose(psFile->fp); + CPLFree(psFile->pszFname); + CPLFree(psFile); + } +} + +/********************************************************************** + * AVCRawBinSetFileDataSize() + * + * One can set nFileDataSize based on some header fields to force + * EOF beyond a given point in the file. Useful for cases like + * PC Arc/Info where the physical file size is always a multiple of + * 256 bytes padded with some junk at the end. + * + * The default value is -1 which just looks for the real EOF. + **********************************************************************/ +void AVCRawBinSetFileDataSize(AVCRawBinFile *psFile, int nFileDataSize) +{ + if (psFile) + { + psFile->nFileDataSize = nFileDataSize; + } +} + +/********************************************************************** + * AVCRawBinReadBytes() + * + * Copy the number of bytes from the input file to the specified + * memory location. + **********************************************************************/ +static GBool bDisableReadBytesEOFError = FALSE; + +void AVCRawBinReadBytes(AVCRawBinFile *psFile, int nBytesToRead, GByte *pBuf) +{ + int nTotalBytesToRead = nBytesToRead; + + /* Make sure file is opened with Read access + */ + if (psFile == NULL || + (psFile->eAccess != AVCRead && psFile->eAccess != AVCReadWrite)) + { + CPLError(CE_Failure, CPLE_FileIO, + "AVCRawBinReadBytes(): call not compatible with access mode."); + return; + } + + /* Quick method: check to see if we can satisfy the request with a + * simple memcpy... most calls should take this path. + */ + if (psFile->nCurPos + nBytesToRead <= psFile->nCurSize) + { + memcpy(pBuf, psFile->abyBuf+psFile->nCurPos, nBytesToRead); + psFile->nCurPos += nBytesToRead; + return; + } + + /* This is the long method... it supports reading data that + * overlaps the input buffer boundaries. + */ + while(nBytesToRead > 0) + { + /* If we reached the end of our memory buffer then read another + * chunk from the file + */ + CPLAssert(psFile->nCurPos <= psFile->nCurSize); + if (psFile->nCurPos == psFile->nCurSize) + { + psFile->nOffset += psFile->nCurSize; + psFile->nCurSize = VSIFRead(psFile->abyBuf, sizeof(GByte), + AVCRAWBIN_READBUFSIZE, psFile->fp); + psFile->nCurPos = 0; + } + + if (psFile->nCurSize == 0) + { + /* Attempt to read past EOF... generate an error. + * + * Note: AVCRawBinEOF() can set bDisableReadBytesEOFError=TRUE + * to disable the error message whils it is testing + * for EOF. + * + * TODO: We are not resetting the buffer. Also, there is no easy + * way to recover from the situation. + */ + if (bDisableReadBytesEOFError == FALSE) + CPLError(CE_Failure, CPLE_FileIO, + "EOF encountered in %s after reading %d bytes while " + "trying to read %d bytes. File may be corrupt.", + psFile->pszFname, nTotalBytesToRead-nBytesToRead, + nTotalBytesToRead); + return; + } + + /* If the requested bytes are not all in the current buffer then + * just read the part that's in memory for now... the loop will + * take care of the rest. + */ + if (psFile->nCurPos + nBytesToRead > psFile->nCurSize) + { + int nBytes; + nBytes = psFile->nCurSize-psFile->nCurPos; + memcpy(pBuf, psFile->abyBuf+psFile->nCurPos, nBytes); + psFile->nCurPos += nBytes; + pBuf += nBytes; + nBytesToRead -= nBytes; + } + else + { + /* All the requested bytes are now in the buffer... + * simply copy them and return. + */ + memcpy(pBuf, psFile->abyBuf+psFile->nCurPos, nBytesToRead); + psFile->nCurPos += nBytesToRead; + + nBytesToRead = 0; /* Terminate the loop */ + } + } +} + +/********************************************************************** + * AVCRawBinReadString() + * + * Same as AVCRawBinReadBytes() except that the string is run through + * the DBCS conversion function. + * + * pBuf should be allocated with a size of at least nBytesToRead+1 bytes. + **********************************************************************/ +void AVCRawBinReadString(AVCRawBinFile *psFile, int nBytesToRead, GByte *pBuf) +{ + const GByte *pszConvBuf; + + AVCRawBinReadBytes(psFile, nBytesToRead, pBuf); + + pBuf[nBytesToRead] = '\0'; + + pszConvBuf = AVCE00ConvertFromArcDBCS(psFile->psDBCSInfo, + pBuf, + nBytesToRead); + + if (pszConvBuf != pBuf) + { + memcpy(pBuf, pszConvBuf, nBytesToRead); + } +} + +/********************************************************************** + * AVCRawBinFSeek() + * + * Move the read pointer to the specified location. + * + * As with fseek(), the specified position can be relative to the + * beginning of the file (SEEK_SET), or the current position (SEEK_CUR). + * SEEK_END is not supported. + **********************************************************************/ +void AVCRawBinFSeek(AVCRawBinFile *psFile, int nOffset, int nFrom) +{ + int nTarget = 0; + + CPLAssert(nFrom == SEEK_SET || nFrom == SEEK_CUR); + + /* Supported only with read access for now + */ + CPLAssert(psFile && psFile->eAccess != AVCWrite); + if (psFile == NULL || psFile->eAccess == AVCWrite) + return; + + /* Compute destination relative to current memory buffer + */ + if (nFrom == SEEK_SET) + nTarget = nOffset - psFile->nOffset; + else if (nFrom == SEEK_CUR) + nTarget = nOffset + psFile->nCurPos; + + /* Is the destination located inside the current buffer? + */ + if (nTarget > 0 && nTarget <= psFile->nCurSize) + { + /* Requested location is already in memory... just move the + * read pointer + */ + psFile->nCurPos = nTarget; + } + else + { + /* Requested location is not part of the memory buffer... + * move the FILE * to the right location and be ready to + * read from there. + */ + VSIFSeek(psFile->fp, psFile->nOffset+nTarget, SEEK_SET); + psFile->nCurPos = 0; + psFile->nCurSize = 0; + psFile->nOffset = psFile->nOffset+nTarget; + } + +} + +/********************************************************************** + * AVCRawBinEOF() + * + * Return TRUE if there is no more data to read from the file or + * FALSE otherwise. + **********************************************************************/ +GBool AVCRawBinEOF(AVCRawBinFile *psFile) +{ + if (psFile == NULL || psFile->fp == NULL) + return TRUE; + + /* In write access mode, always return TRUE, since we always write + * at EOF for now. + */ + if (psFile->eAccess != AVCRead && psFile->eAccess != AVCReadWrite) + return TRUE; + + /* If file data size was specified, then check that we have not + * passed that point yet... + */ + if (psFile->nFileDataSize > 0 && + (psFile->nOffset+psFile->nCurPos) >= psFile->nFileDataSize) + return TRUE; + + /* If the file pointer has been moved by AVCRawBinFSeek(), then + * we may be at a position past EOF, but VSIFeof() would still + * return FALSE. It also returns false if we have read just upto + * the end of the file. EOF marker would not have been set unless + * we try to read past that. + * + * To prevent this situation, if the memory buffer is empty, + * we will try to read 1 byte from the file to force the next + * chunk of data to be loaded (and we'll move the the read pointer + * back by 1 char after of course!). + * If we are at the end of the file, this will trigger the EOF flag. + */ + if ((psFile->nCurPos == 0 && psFile->nCurSize == 0) || + (psFile->nCurPos == AVCRAWBIN_READBUFSIZE && + psFile->nCurSize == AVCRAWBIN_READBUFSIZE)) + { + GByte c; + /* Set bDisableReadBytesEOFError=TRUE to temporarily disable + * the EOF error message from AVCRawBinReadBytes(). + */ + bDisableReadBytesEOFError = TRUE; + AVCRawBinReadBytes(psFile, 1, &c); + bDisableReadBytesEOFError = FALSE; + + if (psFile->nCurPos > 0) + AVCRawBinFSeek(psFile, -1, SEEK_CUR); + } + + return (psFile->nCurPos == psFile->nCurSize && + VSIFEof(psFile->fp)); +} + + +/********************************************************************** + * AVCRawBinRead() + * + * Arc/Info files are binary files with MSB first (Motorola) byte + * ordering. The following functions will read from the input file + * and return a value with the bytes ordered properly for the current + * platform. + **********************************************************************/ +GInt16 AVCRawBinReadInt16(AVCRawBinFile *psFile) +{ + GInt16 n16Value; + + AVCRawBinReadBytes(psFile, 2, (GByte*)(&n16Value)); + + if (psFile->eByteOrder != geSystemByteOrder) + { + return (GInt16)CPL_SWAP16(n16Value); + } + + return n16Value; +} + +GInt32 AVCRawBinReadInt32(AVCRawBinFile *psFile) +{ + GInt32 n32Value; + + AVCRawBinReadBytes(psFile, 4, (GByte*)(&n32Value)); + + if (psFile->eByteOrder != geSystemByteOrder) + { + return (GInt32)CPL_SWAP32(n32Value); + } + + return n32Value; +} + +float AVCRawBinReadFloat(AVCRawBinFile *psFile) +{ + float fValue; + + AVCRawBinReadBytes(psFile, 4, (GByte*)(&fValue)); + + if (psFile->eByteOrder != geSystemByteOrder) + { + CPL_SWAP32PTR( &fValue ); + } + + return fValue; +} + +double AVCRawBinReadDouble(AVCRawBinFile *psFile) +{ + double dValue; + + AVCRawBinReadBytes(psFile, 8, (GByte*)(&dValue)); + + if (psFile->eByteOrder != geSystemByteOrder) + { + CPL_SWAPDOUBLE(&dValue); + } + + return dValue; +} + + + +/********************************************************************** + * AVCRawBinWriteBytes() + * + * Write the number of bytes from the buffer to the file. + * + * If a problem happens, then CPLError() will be called and + * CPLGetLastErrNo() can be used to test if a write operation was + * succesful. + **********************************************************************/ +void AVCRawBinWriteBytes(AVCRawBinFile *psFile, int nBytesToWrite, + const GByte *pBuf) +{ + /*---------------------------------------------------------------- + * Make sure file is opened with Write access + *---------------------------------------------------------------*/ + if (psFile == NULL || + (psFile->eAccess != AVCWrite && psFile->eAccess != AVCReadWrite)) + { + CPLError(CE_Failure, CPLE_FileIO, + "AVCRawBinWriteBytes(): call not compatible with access mode."); + return; + } + + if (VSIFWrite((void*)pBuf, nBytesToWrite, 1, psFile->fp) != 1) + CPLError(CE_Failure, CPLE_FileIO, + "Writing to %s failed.", psFile->pszFname); + + /*---------------------------------------------------------------- + * In write mode, we keep track of current file position ( =nbr of + * bytes written) through psFile->nCurPos + *---------------------------------------------------------------*/ + psFile->nCurPos += nBytesToWrite; +} + + +/********************************************************************** + * AVCRawBinWrite() + * + * Arc/Info files are binary files with MSB first (Motorola) byte + * ordering. The following functions will reorder the byte for the + * value properly and write that to the output file. + * + * If a problem happens, then CPLError() will be called and + * CPLGetLastErrNo() can be used to test if a write operation was + * succesful. + **********************************************************************/ +void AVCRawBinWriteInt16(AVCRawBinFile *psFile, GInt16 n16Value) +{ + if (psFile->eByteOrder != geSystemByteOrder) + { + n16Value = (GInt16)CPL_SWAP16(n16Value); + } + + AVCRawBinWriteBytes(psFile, 2, (GByte*)&n16Value); +} + +void AVCRawBinWriteInt32(AVCRawBinFile *psFile, GInt32 n32Value) +{ + if (psFile->eByteOrder != geSystemByteOrder) + { + n32Value = (GInt32)CPL_SWAP32(n32Value); + } + + AVCRawBinWriteBytes(psFile, 4, (GByte*)&n32Value); +} + +void AVCRawBinWriteFloat(AVCRawBinFile *psFile, float fValue) +{ + if (psFile->eByteOrder != geSystemByteOrder) + { + CPL_SWAP32PTR( &fValue ); + } + + AVCRawBinWriteBytes(psFile, 4, (GByte*)&fValue); +} + +void AVCRawBinWriteDouble(AVCRawBinFile *psFile, double dValue) +{ + if (psFile->eByteOrder != geSystemByteOrder) + { + CPL_SWAPDOUBLE(&dValue); + } + + AVCRawBinWriteBytes(psFile, 8, (GByte*)&dValue); +} + + +/********************************************************************** + * AVCRawBinWriteZeros() + * + * Write a number of zeros (sepcified in bytes) at the current position + * in the file. + * + * If a problem happens, then CPLError() will be called and + * CPLGetLastErrNo() can be used to test if a write operation was + * succesful. + **********************************************************************/ +void AVCRawBinWriteZeros(AVCRawBinFile *psFile, int nBytesToWrite) +{ + char acZeros[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i; + + /* Write by 8 bytes chunks. The last chunk may be less than 8 bytes + */ + for(i=0; i< nBytesToWrite; i+=8) + { + AVCRawBinWriteBytes(psFile, MIN(8,(nBytesToWrite-i)), + (GByte*)acZeros); + } +} + +/********************************************************************** + * AVCRawBinWritePaddedString() + * + * Write a string and pad the end of the field (up to nFieldSize) with + * spaces number of spaces at the current position in the file. + * + * If a problem happens, then CPLError() will be called and + * CPLGetLastErrNo() can be used to test if a write operation was + * succesful. + **********************************************************************/ +void AVCRawBinWritePaddedString(AVCRawBinFile *psFile, int nFieldSize, + const GByte *pszString) +{ + char acSpaces[8] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}; + int i, nLen, numSpaces; + + /* If we're on a system with a multibyte codepage then we have to + * convert strings to the proper multibyte encoding. + */ + pszString = AVCE00Convert2ArcDBCS(psFile->psDBCSInfo, + pszString, nFieldSize); + + nLen = strlen((const char *)pszString); + nLen = MIN(nLen, nFieldSize); + numSpaces = nFieldSize - nLen; + + if (nLen > 0) + AVCRawBinWriteBytes(psFile, nLen, pszString); + + /* Write spaces by 8 bytes chunks. The last chunk may be less than 8 bytes + */ + for(i=0; i< numSpaces; i+=8) + { + AVCRawBinWriteBytes(psFile, MIN(8,(numSpaces-i)), + (GByte*)acSpaces); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/dbfopen.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/dbfopen.h new file mode 100644 index 000000000..7f7043c19 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/dbfopen.h @@ -0,0 +1,192 @@ +#ifndef _SHAPEFILE_H_INCLUDED +#define _SHAPEFILE_H_INCLUDED + +/* NOTE: This file (dbfopen.h) is a copy of shapefil.h (described below) + * in which only the DBF-related stuff was kept to include it in + * the AVCE00 library. + */ + +/****************************************************************************** + * $Id: dbfopen.h,v 1.2 2000/09/26 20:21:05 daniel Exp $ + * + * Project: Shapelib + * Purpose: Primary include file for Shapelib. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log: dbfopen.h,v $ + * Revision 1.2 2000/09/26 20:21:05 daniel + * Added AVCCoverPC write + * + * Revision 1.1 1999/12/24 07:16:41 daniel + * Checked into AVCE00 lib + Added DBFGetNativeFieldType() + * + * Revision 1.14 1999/11/05 14:12:05 warmerda + * updated license terms + * + * Revision 1.13 1999/06/02 18:24:21 warmerda + * added trimming code + * + * Revision 1.12 1999/06/02 17:56:12 warmerda + * added quad'' subnode support for trees + * + * Revision 1.11 1999/05/18 19:11:11 warmerda + * Added example searching capability + * + * Revision 1.10 1999/05/18 17:49:38 warmerda + * added initial quadtree support + * + * Revision 1.9 1999/05/11 03:19:28 warmerda + * added new Tuple api, and improved extension handling - add from candrsn + * + * Revision 1.8 1999/03/23 17:22:27 warmerda + * Added extern "C" protection for C++ users of shapefil.h. + * + * Revision 1.7 1998/12/31 15:31:07 warmerda + * Added the TRIM_DBF_WHITESPACE and DISABLE_MULTIPATCH_MEASURE options. + * + * Revision 1.6 1998/12/03 15:48:15 warmerda + * Added SHPCalculateExtents(). + * + * Revision 1.5 1998/11/09 20:57:16 warmerda + * Altered SHPGetInfo() call. + * + * Revision 1.4 1998/11/09 20:19:33 warmerda + * Added 3D support, and use of SHPObject. + * + * Revision 1.3 1995/08/23 02:24:05 warmerda + * Added support for reading bounds. + * + * Revision 1.2 1995/08/04 03:17:39 warmerda + * Added header. + * + */ + +#include + +#ifdef USE_DBMALLOC +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************/ +/* Configuration options. */ +/************************************************************************/ + +/* -------------------------------------------------------------------- */ +/* Should the DBFReadStringAttribute() strip leading and */ +/* trailing white space? */ +/* -------------------------------------------------------------------- */ + +/* Note: For AVCE00 purposes, we do not want the spaces to be stripped. + * + * #define TRIM_DBF_WHITESPACE + */ + +/************************************************************************/ +/* DBF Support. */ +/************************************************************************/ +typedef struct +{ + FILE *fp; + + int nRecords; + + int nRecordLength; + int nHeaderLength; + int nFields; + int *panFieldOffset; + int *panFieldSize; + int *panFieldDecimals; + char *pachFieldType; + + char *pszHeader; + + int nCurrentRecord; + int bCurrentRecordModified; + char *pszCurrentRecord; + + int bNoHeader; + int bUpdated; +} DBFInfo; + +typedef DBFInfo * DBFHandle; + +typedef enum { + FTString, + FTInteger, + FTDouble, + FTInvalid +} DBFFieldType; + +#define XBASE_FLDHDR_SZ 32 + +DBFHandle DBFOpen( const char * pszDBFFile, const char * pszAccess ); +DBFHandle DBFCreate( const char * pszDBFFile ); + +int DBFGetFieldCount( DBFHandle psDBF ); +int DBFGetRecordCount( DBFHandle psDBF ); +int DBFAddField( DBFHandle hDBF, const char * pszFieldName, + DBFFieldType eType, int nWidth, int nDecimals ); + +DBFFieldType DBFGetFieldInfo( DBFHandle psDBF, int iField, + char * pszFieldName, + int * pnWidth, int * pnDecimals ); + +char DBFGetNativeFieldType( DBFHandle psDBF, int iField ); + +int DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField ); +double DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField ); +const char *DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField ); + +int DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, + int nFieldValue ); +int DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField, + double dFieldValue ); +int DBFWriteStringAttribute( DBFHandle hDBF, int iShape, int iField, + const char * pszFieldValue ); +int DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, + void * pValue ); + +const char *DBFReadTuple(DBFHandle psDBF, int hEntity ); +int DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ); + +DBFHandle DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ); + +void DBFClose( DBFHandle hDBF ); + +#ifdef __cplusplus +} +#endif + +#endif /* ndef _SHAPEFILE_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/drv_avcbin.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/drv_avcbin.html new file mode 100644 index 000000000..e20e14da8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/drv_avcbin.html @@ -0,0 +1,61 @@ + + +Arc/Info Binary Coverage + + + + +

    Arc/Info Binary Coverage

    + +Arc/Info Binary Coverages (eg. Arc/Info V7 and earlier) are supported by +OGR for read access.

    + +The label, arc, polygon, centroid, region and text +sections of a coverage are all supported as layers. Attributes from INFO +are appended to labels, arcs, polygons or region where appropriate. When +available the projection information is read and translated. Polygon +geometries are collected for polygon and region layers from the composing arcs. +

    + +Text sections are represented as point layers. Display height is preserved +in the HEIGHT attribute field; however, other information about text +orientation is discarded.

    + +Info tables associated with a coverage, but not sepecifically named to be +attached to one of the existing geometric layers is currently not accessable +through OGR. Note that info tables are stored in an 'info' directory at +the same level as the coverage directory. If this is inaccessable or +corrupt no info attributes will be appended to coverage layers, but the +geometry should still be accessable.

    + +If the directory contains files with names like w001001.adf then the coverage is +a grid coverage +suitable to read with GDAL, not a vector coverage supported by OGR.

    + +The layers are named as follows: + +

      +
    1. A label layer (polygon labels, or free standing points) is named +LAB if present.

      +

    2. A centroid layer (polygon centroids) is named CNT if present.

      +

    3. An arc (line) layer is named ARC if present.

      +

    4. A polygon layer is named "PAL" if present.

      +

    5. A text section is named according to the section subclass.

      +

    6. A region subclass is named according to the subclass name.

      +

    + +The Arc/Info binary coverage driver attempts to optimize spatial queries +but due to the lack of a spatial index this is just accomplished by minimizing +processing for features not within the spatial window.

    + +Random (by FID) reads of arcs, and polygons is supported it may not be +supported for other feature types.

    + +

    See Also

    + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/drv_avce00.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/drv_avce00.html new file mode 100644 index 000000000..bdbacf5b1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/drv_avce00.html @@ -0,0 +1,54 @@ + + +Arc/Info E00 (ASCII) Coverage + + + + +

    Arc/Info E00 (ASCII) Coverage

    + +Arc/Info E00 Coverages (eg. Arc/Info V7 and earlier) are supported by +OGR for read access.

    + +The label, arc, polygon, centroid, region and text +sections of a coverage are all supported as layers. Attributes from INFO +are appended to labels, arcs, polygons or region where appropriate. When +available the projection information is read and translated. Polygon +geometries are collected for polygon and region layers from the composing arcs. +

    + +Text sections are represented as point layers. Display height is preserved +in the HEIGHT attribute field; however, other information about text +orientation is discarded.

    + +Info tables associated with a coverage, but not sepecifically named to be +attached to one of the existing geometric layers is currently not accessable +through OGR. Note that info tables are stored in an 'info' directory at +the same level as the coverage directory. If this is inaccessable or +corrupt no info attributes will be appended to coverage layers, but the +geometry should still be accessable.

    + +The layers are named as follows: + +

      +
    1. A label layer (polygon labels, or free standing points) is named +LAB if present.

      +

    2. A centroid layer (polygon centroids) is named CNT if present.

      +

    3. An arc (line) layer is named ARC if present.

      +

    4. A polygon layer is named "PAL" if present.

      +

    5. A text section is named according to the section subclass.

      +

    6. A region subclass is named according to the subclass name.

      +

    + +Random (by FID) reads of arcs, and polygons is supported it may not be +supported for other feature types. Random ascess to E00 files is generally +slow.

    + +

    See Also

    + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/makefile.vc new file mode 100644 index 000000000..5c58d0b44 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/makefile.vc @@ -0,0 +1,24 @@ + +LL_OBJ = avc_bin.obj avc_binwr.obj avc_e00gen.obj avc_e00parse.obj \ + avc_e00write.obj avc_e00read.obj avc_mbyte.obj avc_misc.obj \ + avc_rawbin.obj +OGR_OBJ = ogravcbindriver.obj ogravcbindatasource.obj ogravcbinlayer.obj\ + ogravclayer.obj ogravcdatasource.obj ogravce00layer.obj \ + ogravce00datasource.obj ogravce00driver.obj +OBJ = $(LL_OBJ) $(OGR_OBJ) + +EXTRAFLAGS = -I.. -I..\.. -I..\shape + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogr_avc.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogr_avc.h new file mode 100644 index 000000000..c66a93665 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogr_avc.h @@ -0,0 +1,234 @@ +/****************************************************************************** + * $Id: ogr_avc.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: Arc/Info Coverage (E00 & Binary) Reader + * Purpose: Declarations for OGR wrapper classes for coverage access. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_AVC_H_INCLUDED +#define _OGR_AVC_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "avc.h" + +class OGRAVCDataSource; + +/************************************************************************/ +/* OGRAVCLayer */ +/************************************************************************/ + +class OGRAVCLayer : public OGRLayer +{ + protected: + OGRFeatureDefn *poFeatureDefn; + + OGRAVCDataSource *poDS; + + AVCFileType eSectionType; + + int SetupFeatureDefinition( const char *pszName ); + int AppendTableDefinition( AVCTableDef *psTableDef ); + + int MatchesSpatialFilter( void * ); + OGRFeature *TranslateFeature( void * ); + + int TranslateTableFields( OGRFeature *poFeature, + int nFieldBase, + AVCTableDef *psTableDef, + AVCField *pasFields ); + + public: + OGRAVCLayer( AVCFileType eSectionType, + OGRAVCDataSource *poDS ); + ~OGRAVCLayer(); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRAVCDataSource */ +/************************************************************************/ + +class OGRAVCDataSource : public OGRDataSource +{ + protected: + OGRSpatialReference *poSRS; + char *pszCoverageName; + + public: + OGRAVCDataSource(); + ~OGRAVCDataSource(); + + virtual OGRSpatialReference *GetSpatialRef(); + + const char *GetCoverageName(); +}; + +/* ==================================================================== */ +/* Binary Coverage Classes */ +/* ==================================================================== */ + +class OGRAVCBinDataSource; + +/************************************************************************/ +/* OGRAVCBinLayer */ +/************************************************************************/ + +class OGRAVCBinLayer : public OGRAVCLayer +{ + AVCE00Section *psSection; + AVCBinFile *hFile; + + OGRAVCBinLayer *poArcLayer; + int bNeedReset; + + char szTableName[128]; + AVCBinFile *hTable; + int nTableBaseField; + int nTableAttrIndex; + + int nNextFID; + + int FormPolygonGeometry( OGRFeature *poFeature, + AVCPal *psPAL ); + + int CheckSetupTable(); + int AppendTableFields( OGRFeature *poFeature ); + + public: + OGRAVCBinLayer( OGRAVCBinDataSource *poDS, + AVCE00Section *psSectionIn ); + + ~OGRAVCBinLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + OGRFeature * GetFeature( GIntBig nFID ); + + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRAVCBinDataSource */ +/************************************************************************/ + +class OGRAVCBinDataSource : public OGRAVCDataSource +{ + OGRLayer **papoLayers; + int nLayers; + + char *pszName; + + AVCE00ReadPtr psAVC; + + public: + OGRAVCBinDataSource(); + ~OGRAVCBinDataSource(); + + int Open( const char *, int bTestOpen ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); + + AVCE00ReadPtr GetInfo() { return psAVC; } +}; + +/* ==================================================================== */ +/* E00 (ASCII) Coverage Classes */ +/* ==================================================================== */ + +/************************************************************************/ +/* OGRAVCE00Layer */ +/************************************************************************/ +class OGRAVCE00Layer : public OGRAVCLayer +{ + AVCE00Section *psSection; + AVCE00ReadE00Ptr psRead; + OGRAVCE00Layer *poArcLayer; + int nFeatureCount; + int bNeedReset; + int nNextFID; + + AVCE00Section *psTableSection; + AVCE00ReadE00Ptr psTableRead; + char *pszTableFilename; + int nTablePos; + int nTableBaseField; + int nTableAttrIndex; + + int FormPolygonGeometry( OGRFeature *poFeature, + AVCPal *psPAL ); + public: + OGRAVCE00Layer( OGRAVCDataSource *poDS, + AVCE00Section *psSectionIn ); + + ~OGRAVCE00Layer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + OGRFeature *GetFeature( GIntBig nFID ); + GIntBig GetFeatureCount(int bForce); + int CheckSetupTable(AVCE00Section *psTblSectionIn); + int AppendTableFields( OGRFeature *poFeature ); +}; + +/************************************************************************/ +/* OGRAVCE00DataSource */ +/************************************************************************/ + +class OGRAVCE00DataSource : public OGRAVCDataSource +{ + int nLayers; + char *pszName; + AVCE00ReadE00Ptr psE00; + OGRAVCE00Layer **papoLayers; + + protected: + int CheckAddTable(AVCE00Section *psTblSection); + + public: + OGRAVCE00DataSource(); + ~OGRAVCE00DataSource(); + + int Open(const char *, int bTestOpen); + + AVCE00ReadE00Ptr GetInfo() { return psE00; } + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + + OGRLayer *GetLayer( int ); + int TestCapability( const char * ); + virtual OGRSpatialReference *GetSpatialRef(); +}; + + +#endif /* _OGR_AVC_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravcbindatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravcbindatasource.cpp new file mode 100644 index 000000000..b827555f8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravcbindatasource.cpp @@ -0,0 +1,178 @@ +/****************************************************************************** + * $Id: ogravcbindatasource.cpp 28039 2014-11-30 18:24:59Z rouault $ + * + * Project: OGR + * Purpose: Implements OGRAVCBinDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_avc.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogravcbindatasource.cpp 28039 2014-11-30 18:24:59Z rouault $"); + +/************************************************************************/ +/* OGRAVCBinDataSource() */ +/************************************************************************/ + +OGRAVCBinDataSource::OGRAVCBinDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + poSRS = NULL; +} + +/************************************************************************/ +/* ~OGRAVCBinDataSource() */ +/************************************************************************/ + +OGRAVCBinDataSource::~OGRAVCBinDataSource() + +{ + if( psAVC ) + { + AVCE00ReadClose( psAVC ); + psAVC = NULL; + } + + CPLFree( pszName ); + + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRAVCBinDataSource::Open( const char * pszNewName, int bTestOpen ) + +{ +/* -------------------------------------------------------------------- */ +/* Open the source file. Suppress error reporting if we are in */ +/* TestOpen mode. */ +/* -------------------------------------------------------------------- */ + if( bTestOpen ) + CPLPushErrorHandler( CPLQuietErrorHandler ); + + psAVC = AVCE00ReadOpen( pszNewName ); + + if( bTestOpen ) + { + CPLPopErrorHandler(); + CPLErrorReset(); + } + + if( psAVC == NULL ) + return FALSE; + + pszName = CPLStrdup( pszNewName ); + pszCoverageName = CPLStrdup( psAVC->pszCoverName ); + +/* -------------------------------------------------------------------- */ +/* Create layers for the "interesting" sections of the coverage. */ +/* -------------------------------------------------------------------- */ + int iSection; + + papoLayers = (OGRLayer **) + CPLCalloc( sizeof(OGRLayer *), psAVC->numSections ); + nLayers = 0; + + for( iSection = 0; iSection < psAVC->numSections; iSection++ ) + { + AVCE00Section *psSec = psAVC->pasSections + iSection; + + switch( psSec->eType ) + { + case AVCFileARC: + case AVCFilePAL: + case AVCFileCNT: + case AVCFileLAB: + case AVCFileRPL: + case AVCFileTXT: + case AVCFileTX6: + papoLayers[nLayers++] = new OGRAVCBinLayer( this, psSec ); + break; + + case AVCFilePRJ: + { + char **papszPRJ; + AVCBinFile *hFile; + + hFile = AVCBinReadOpen(psAVC->pszCoverPath, + psSec->pszFilename, + psAVC->eCoverType, + psSec->eType, + psAVC->psDBCSInfo); + if( hFile && poSRS == NULL ) + { + papszPRJ = AVCBinReadNextPrj( hFile ); + + poSRS = new OGRSpatialReference(); + if( poSRS->importFromESRI( papszPRJ ) != OGRERR_NONE ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to parse PRJ section, ignoring." ); + delete poSRS; + poSRS = NULL; + } + AVCBinReadClose( hFile ); + } + } + break; + + default: + ; + } + } + + return nLayers > 0; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRAVCBinDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRAVCBinDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravcbindriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravcbindriver.cpp new file mode 100644 index 000000000..7a339f0e1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravcbindriver.cpp @@ -0,0 +1,120 @@ +/****************************************************************************** + * $Id: ogravcbindriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OGR + * Purpose: OGRAVCBinDriver implementation (Arc/Info Binary Coverages) + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_avc.h" + +CPL_CVSID("$Id: ogravcbindriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRAVCBinDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + OGRAVCBinDataSource *poDS; + OGRAVCE00DataSource *poDSE00; + + if( poOpenInfo->eAccess == GA_Update ) + return NULL; + if( !poOpenInfo->bStatOK ) + return NULL; + if( poOpenInfo->fpL != NULL ) + { + if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "E00") ) + { + /* ok */ + } + else + { + char** papszSiblingFiles = poOpenInfo->GetSiblingFiles(); + if( papszSiblingFiles != NULL ) + { + int i; + int bFoundCandidateFile = FALSE; + for( i = 0; papszSiblingFiles[i] != NULL; i++ ) + { + if( EQUAL(CPLGetExtension(papszSiblingFiles[i]), "ADF") ) + { + bFoundCandidateFile = TRUE; + break; + } + } + if( !bFoundCandidateFile ) + return NULL; + } + } + } + + poDS = new OGRAVCBinDataSource(); + + if( poDS->Open( poOpenInfo->pszFilename, TRUE ) + && poDS->GetLayerCount() > 0 ) + { + return poDS; + } + delete poDS; + + poDSE00 = new OGRAVCE00DataSource(); + + if( poDSE00->Open( poOpenInfo->pszFilename, TRUE ) + && poDSE00->GetLayerCount() > 0 ) + { + return poDSE00; + } + delete poDSE00; + + return NULL; +} + +/************************************************************************/ +/* RegisterOGRAVC() */ +/************************************************************************/ + +void RegisterOGRAVCBin() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "AVCBin" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "AVCBin" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Arc/Info Binary Coverage" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_avcbin.html" ); + + poDriver->pfnOpen = OGRAVCBinDriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravcbinlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravcbinlayer.cpp new file mode 100644 index 000000000..b330e3d4a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravcbinlayer.cpp @@ -0,0 +1,449 @@ +/****************************************************************************** + * $Id: ogravcbinlayer.cpp 28383 2015-01-30 15:47:59Z rouault $ + * + * Project: OGR + * Purpose: Implements OGRAVCBinLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_avc.h" +#include "ogr_api.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogravcbinlayer.cpp 28383 2015-01-30 15:47:59Z rouault $"); + +/************************************************************************/ +/* OGRAVCBinLayer() */ +/************************************************************************/ + +OGRAVCBinLayer::OGRAVCBinLayer( OGRAVCBinDataSource *poDSIn, + AVCE00Section *psSectionIn ) + : OGRAVCLayer( psSectionIn->eType, poDSIn ) + +{ + psSection = psSectionIn; + hFile = NULL; + poArcLayer = NULL; + bNeedReset = FALSE; + nNextFID = 1; + + hTable = NULL; + nTableBaseField = -1; + nTableAttrIndex = -1; + + SetupFeatureDefinition( psSection->pszName ); + + szTableName[0] = '\0'; + if( psSection->eType == AVCFilePAL ) + sprintf( szTableName, "%s.PAT", poDS->GetCoverageName() ); + else if( psSection->eType == AVCFileRPL ) + sprintf( szTableName, "%s.PAT%s", poDS->GetCoverageName(), + psSectionIn->pszName ); + else if( psSection->eType == AVCFileARC ) + sprintf( szTableName, "%s.AAT", poDS->GetCoverageName() ); + else if( psSection->eType == AVCFileLAB ) + { + AVCE00ReadPtr psInfo = ((OGRAVCBinDataSource *) poDS)->GetInfo(); + + sprintf( szTableName, "%s.PAT", poDS->GetCoverageName() ); + + for( int iSection = 0; iSection < psInfo->numSections; iSection++ ) + { + if( psInfo->pasSections[iSection].eType == AVCFilePAL ) + nTableAttrIndex = poFeatureDefn->GetFieldIndex( "PolyId" ); + } + } + + CheckSetupTable(); +} + +/************************************************************************/ +/* ~OGRAVCBinLayer() */ +/************************************************************************/ + +OGRAVCBinLayer::~OGRAVCBinLayer() + +{ + ResetReading(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRAVCBinLayer::ResetReading() + +{ + if( hFile != NULL ) + { + AVCBinReadClose( hFile ); + hFile = NULL; + } + + bNeedReset = FALSE; + nNextFID = 1; + + if( hTable != NULL ) + { + AVCBinReadClose( hTable ); + hTable = NULL; + } +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRAVCBinLayer::GetFeature( GIntBig nFID ) + +{ + if( (GIntBig)(int)nFID != nFID ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we haven't started yet, open the file now. */ +/* -------------------------------------------------------------------- */ + if( hFile == NULL ) + { + AVCE00ReadPtr psInfo = ((OGRAVCBinDataSource *) poDS)->GetInfo(); + + hFile = AVCBinReadOpen(psInfo->pszCoverPath, + psSection->pszFilename, + psInfo->eCoverType, + psSection->eType, + psInfo->psDBCSInfo); + } + +/* -------------------------------------------------------------------- */ +/* Read the raw feature - the -3 fid is a special flag */ +/* indicating serial access. */ +/* -------------------------------------------------------------------- */ + void *pFeature; + + if( nFID == -3 ) + { + while( (pFeature = AVCBinReadNextObject( hFile )) != NULL + && !MatchesSpatialFilter( pFeature ) ) + { + nNextFID++; + } + } + else + { + bNeedReset = TRUE; + pFeature = AVCBinReadObject( hFile, (int)nFID ); + } + + if( pFeature == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Translate the feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poFeature = TranslateFeature( pFeature ); + if( poFeature == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* LAB's we have to assign the FID to directly, since it */ +/* doesn't seem to be stored in the file structure. */ +/* -------------------------------------------------------------------- */ + if( psSection->eType == AVCFileLAB ) + { + if( nFID == -3 ) + poFeature->SetFID( nNextFID++ ); + else + poFeature->SetFID( nFID ); + } + +/* -------------------------------------------------------------------- */ +/* If this is a polygon layer, try to assemble the arcs to form */ +/* the whole polygon geometry. */ +/* -------------------------------------------------------------------- */ + if( psSection->eType == AVCFilePAL + || psSection->eType == AVCFileRPL ) + FormPolygonGeometry( poFeature, (AVCPal *) pFeature ); + +/* -------------------------------------------------------------------- */ +/* If we have an attribute table, append the attributes now. */ +/* -------------------------------------------------------------------- */ + AppendTableFields( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRAVCBinLayer::GetNextFeature() + +{ + if( bNeedReset ) + ResetReading(); + + OGRFeature *poFeature = GetFeature( -3 ); + + // Skip universe polygon. + if( poFeature != NULL && poFeature->GetFID() == 1 + && psSection->eType == AVCFilePAL ) + { + OGRFeature::DestroyFeature( poFeature ); + poFeature = GetFeature( -3 ); + } + + while( poFeature != NULL + && ((m_poAttrQuery != NULL + && !m_poAttrQuery->Evaluate( poFeature ) ) + || !FilterGeometry( poFeature->GetGeometryRef() ) ) ) + { + OGRFeature::DestroyFeature( poFeature ); + poFeature = GetFeature( -3 ); + } + + if( poFeature == NULL ) + ResetReading(); + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRAVCBinLayer::TestCapability( const char * pszCap ) + +{ + if( eSectionType == AVCFileARC && EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + else + return OGRAVCLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* FormPolygonGeometry() */ +/* */ +/* Collect all the arcs forming edges to this polygon and form */ +/* them into the appropriate OGR geometry on the target feature. */ +/************************************************************************/ + +int OGRAVCBinLayer::FormPolygonGeometry( OGRFeature *poFeature, + AVCPal *psPAL ) + +{ +/* -------------------------------------------------------------------- */ +/* Try to find the corresponding ARC layer if not already */ +/* recorded. */ +/* -------------------------------------------------------------------- */ + if( poArcLayer == NULL ) + { + int i; + + for( i = 0; i < poDS->GetLayerCount(); i++ ) + { + OGRAVCBinLayer *poLayer = (OGRAVCBinLayer *) poDS->GetLayer(i); + + if( poLayer->eSectionType == AVCFileARC ) + poArcLayer = poLayer; + } + + if( poArcLayer == NULL ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Read all the arcs related to this polygon, making a working */ +/* copy of them since the one returned by AVC is temporary. */ +/* -------------------------------------------------------------------- */ + OGRGeometryCollection oArcs; + int iArc; + + for( iArc = 0; iArc < psPAL->numArcs; iArc++ ) + { + OGRFeature *poArc; + + if( psPAL->pasArcs[iArc].nArcId == 0 ) + continue; + + // If the other side of the line is the same polygon then this + // arc is a "bridge" arc and can be discarded. If we don't discard + // it, then we should double it as bridge arcs seem to only appear + // once. But by discarding it we ensure a multi-ring polygon will be + // properly formed. + if( psPAL->pasArcs[iArc].nAdjPoly == psPAL->nPolyId ) + continue; + + poArc = poArcLayer->GetFeature( ABS(psPAL->pasArcs[iArc].nArcId) ); + + if( poArc == NULL ) + return FALSE; + + if( poArc->GetGeometryRef() == NULL ) + return FALSE; + + oArcs.addGeometry( poArc->GetGeometryRef() ); + OGRFeature::DestroyFeature( poArc ); + } + + OGRErr eErr; + OGRPolygon *poPolygon; + + poPolygon = (OGRPolygon *) + OGRBuildPolygonFromEdges( (OGRGeometryH) &oArcs, TRUE, FALSE, + 0.0, &eErr ); + if( poPolygon != NULL ) + poFeature->SetGeometryDirectly( poPolygon ); + + return eErr == OGRERR_NONE; +} + +/************************************************************************/ +/* CheckSetupTable() */ +/* */ +/* Check if the named table exists, and if so, setup access to */ +/* it (open it), and add it's fields to the feature class */ +/* definition. */ +/************************************************************************/ + +int OGRAVCBinLayer::CheckSetupTable() + +{ + if( szTableName[0] == '\0' ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Scan for the indicated section. */ +/* -------------------------------------------------------------------- */ + AVCE00ReadPtr psInfo = ((OGRAVCBinDataSource *) poDS)->GetInfo(); + int iSection; + AVCE00Section *psSection = NULL; + char szPaddedName[65]; + + sprintf( szPaddedName, "%s%32s", szTableName, " " ); + szPaddedName[32] = '\0'; + + for( iSection = 0; iSection < psInfo->numSections; iSection++ ) + { + if( EQUAL(szPaddedName,psInfo->pasSections[iSection].pszName) + && psInfo->pasSections[iSection].eType == AVCFileTABLE ) + psSection = psInfo->pasSections + iSection; + } + + if( psSection == NULL ) + { + szTableName[0] = '\0'; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try opening the table. */ +/* -------------------------------------------------------------------- */ + hTable = AVCBinReadOpen( psInfo->pszInfoPath, szTableName, + psInfo->eCoverType, AVCFileTABLE, + psInfo->psDBCSInfo); + + if( hTable == NULL ) + { + szTableName[0] = '\0'; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Setup attributes. */ +/* -------------------------------------------------------------------- */ + nTableBaseField = poFeatureDefn->GetFieldCount(); + + AppendTableDefinition( hTable->hdr.psTableDef ); + +/* -------------------------------------------------------------------- */ +/* Close table so we don't have to many files open at once. */ +/* -------------------------------------------------------------------- */ + AVCBinReadClose( hTable ); + + hTable = NULL; + + return TRUE; +} + +/************************************************************************/ +/* AppendTableFields() */ +/************************************************************************/ + +int OGRAVCBinLayer::AppendTableFields( OGRFeature *poFeature ) + +{ + AVCE00ReadPtr psInfo = ((OGRAVCBinDataSource *) poDS)->GetInfo(); + + if( szTableName[0] == '\0' ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Open the table if it is currently closed. */ +/* -------------------------------------------------------------------- */ + if( hTable == NULL ) + { + hTable = AVCBinReadOpen( psInfo->pszInfoPath, szTableName, + psInfo->eCoverType, AVCFileTABLE, + psInfo->psDBCSInfo); + } + + if( hTable == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read the info record. */ +/* */ +/* We usually assume the FID of the feature is the key but in a */ +/* polygon coverage we need to use the PolyId attribute of LAB */ +/* features to lookup the related attributes. In this case */ +/* nTableAttrIndex will already be setup to refer to the */ +/* PolyId field. */ +/* -------------------------------------------------------------------- */ + int nRecordId; + void *hRecord; + + if( nTableAttrIndex == -1 ) + nRecordId = (int) poFeature->GetFID(); + else + nRecordId = poFeature->GetFieldAsInteger( nTableAttrIndex ); + + hRecord = AVCBinReadObject( hTable, nRecordId ); + if( hRecord == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Translate it. */ +/* -------------------------------------------------------------------- */ + return TranslateTableFields( poFeature, nTableBaseField, + hTable->hdr.psTableDef, + (AVCField *) hRecord ); +} + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravcdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravcdatasource.cpp new file mode 100644 index 000000000..a4ffcb7b0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravcdatasource.cpp @@ -0,0 +1,78 @@ +/****************************************************************************** + * $Id: ogravcdatasource.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: OGR + * Purpose: Implements OGRAVCDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_avc.h" + +CPL_CVSID("$Id: ogravcdatasource.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* OGRAVCDataSource() */ +/************************************************************************/ + +OGRAVCDataSource::OGRAVCDataSource() + +{ + poSRS = NULL; + pszCoverageName = NULL; +} + +/************************************************************************/ +/* ~OGRAVCDataSource() */ +/************************************************************************/ + +OGRAVCDataSource::~OGRAVCDataSource() + +{ + if( poSRS ) + poSRS->Release(); + CPLFree( pszCoverageName ); +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRAVCDataSource::GetSpatialRef() + +{ + return poSRS; +} + +/************************************************************************/ +/* GetCoverageName() */ +/************************************************************************/ + +const char *OGRAVCDataSource::GetCoverageName() + +{ + if( pszCoverageName == NULL ) + return ""; + else + return pszCoverageName; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravce00datasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravce00datasource.cpp new file mode 100644 index 000000000..b759c5a57 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravce00datasource.cpp @@ -0,0 +1,249 @@ +/****************************************************************************** + * $Id: ogravce00datasource.cpp 28039 2014-11-30 18:24:59Z rouault $ + * + * Project: OGR + * Purpose: Implements OGRAVCE00DataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * James Flemer + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2006, James Flemer + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_avc.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogravce00datasource.cpp 28039 2014-11-30 18:24:59Z rouault $"); + +/************************************************************************/ +/* OGRAVCE00DataSource() */ +/************************************************************************/ + +OGRAVCE00DataSource::OGRAVCE00DataSource() + : nLayers(0), pszName(NULL), psE00(NULL), papoLayers(NULL) +{ +} + +/************************************************************************/ +/* ~OGRAVCE00DataSource() */ +/************************************************************************/ + +OGRAVCE00DataSource::~OGRAVCE00DataSource() + +{ + if( psE00 ) + { + AVCE00ReadCloseE00( psE00 ); + psE00 = NULL; + } + + CPLFree( pszName ); + + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRAVCE00DataSource::Open( const char * pszNewName, int bTestOpen ) + +{ +/* -------------------------------------------------------------------- */ +/* Open the source file. Suppress error reporting if we are in */ +/* TestOpen mode. */ +/* -------------------------------------------------------------------- */ + int bCompressed = FALSE; + + if( bTestOpen ) + CPLPushErrorHandler( CPLQuietErrorHandler ); + + psE00 = AVCE00ReadOpenE00(pszNewName); + + if( CPLGetLastErrorNo() == CPLE_OpenFailed + && strstr(CPLGetLastErrorMsg(),"compressed E00") != NULL ) + { + bCompressed = TRUE; + } + + if( bTestOpen ) + { + CPLPopErrorHandler(); + CPLErrorReset(); + } + + if( psE00 == NULL ) + { + if( bCompressed ) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "This looks like a compressed E00 file and cannot be " + "processed directly. You may need to uncompress it " + "first using the E00compr library or the e00conv " + "program." ); + } + return FALSE; + } + + pszName = CPLStrdup( pszNewName ); + /* pszCoverageName = CPLStrdup( psE00->pszCoverName ); */ + pszCoverageName = CPLStrdup( pszNewName ); + +/* -------------------------------------------------------------------- */ +/* Create layers for the "interesting" sections of the coverage. */ +/* -------------------------------------------------------------------- */ + int iSection; + + papoLayers = (OGRAVCE00Layer **) + CPLCalloc( sizeof(OGRAVCE00Layer *), psE00->numSections ); + nLayers = 0; + + for( iSection = 0; iSection < psE00->numSections; iSection++ ) + { + AVCE00Section *psSec = psE00->pasSections + iSection; + + switch( psSec->eType ) + { + case AVCFileARC: + case AVCFilePAL: + case AVCFileCNT: + case AVCFileLAB: + case AVCFileRPL: + case AVCFileTXT: + papoLayers[nLayers++] = new OGRAVCE00Layer( this, psSec ); + break; + + case AVCFileTX6: + break; + + case AVCFileTABLE: + CheckAddTable(psSec); + break; + + case AVCFilePRJ: + { +#if 0 + poSRS = new OGRSpatialReference(); + char **papszPRJ; + AVCE00File *hFile; + + hFile = AVCE00ReadOpen(psE00->pszCoverPath, + psSec->pszFilename, + psE00->eCoverType, + psSec->eType, + psE00->psDBCSInfo); + if( hFile && poSRS == NULL ) + { + papszPRJ = AVCE00ReadNextPrj( hFile ); + + poSRS = new OGRSpatialReference(); + if( poSRS->importFromESRI( papszPRJ ) != OGRERR_NONE ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to parse PRJ section, ignoring." ); + delete poSRS; + poSRS = NULL; + } + AVCE00ReadClose( hFile ); + } +#endif + } + break; + + default: + ; + } + } + + return nLayers > 0; +} + +int OGRAVCE00DataSource::CheckAddTable( AVCE00Section *psTblSection ) +{ + int i, nCount = 0; + for (i = 0; i < nLayers; ++i) + { + if (papoLayers[i]->CheckSetupTable(psTblSection)) + ++nCount; + } + return nCount; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRAVCE00DataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRAVCE00DataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ +OGRSpatialReference *OGRAVCE00DataSource::GetSpatialRef() +{ + if (NULL == poSRS && psE00 != NULL) + /* if (psE00 != NULL) */ + { + int iSection; + AVCE00Section *psSec; + char **pszPRJ; + + for( iSection = 0; iSection < psE00->numSections; iSection++ ) + { + psSec = psE00->pasSections + iSection; + if (psSec->eType == AVCFilePRJ) + { + AVCE00ReadGotoSectionE00(psE00, psSec, 0); + pszPRJ = (char **)AVCE00ReadNextObjectE00(psE00); + poSRS = new OGRSpatialReference(); + if( poSRS->importFromESRI( pszPRJ ) != OGRERR_NONE ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to parse PRJ section, ignoring." ); + delete poSRS; + poSRS = NULL; + } + break; + } + } + } + return poSRS; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravce00driver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravce00driver.cpp new file mode 100644 index 000000000..d5d8659f4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravce00driver.cpp @@ -0,0 +1,87 @@ +/****************************************************************************** + * $Id: ogravcbindriver.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: OGR + * Purpose: OGRAVCE00Driver implementation (Arc/Info E00ary Coverages) + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_avc.h" + +CPL_CVSID("$Id: ogravcbindriver.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRAVCE00DriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + OGRAVCE00DataSource *poDSE00; + + if( poOpenInfo->eAccess == GA_Update ) + return NULL; + if( !poOpenInfo->bStatOK ) + return NULL; + if( !EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "E00") ) + return NULL; + + poDSE00 = new OGRAVCE00DataSource(); + + if( poDSE00->Open( poOpenInfo->pszFilename, TRUE ) + && poDSE00->GetLayerCount() > 0 ) + { + return poDSE00; + } + delete poDSE00; + + return NULL; +} + +/************************************************************************/ +/* RegisterOGRAVC() */ +/************************************************************************/ + +void RegisterOGRAVCE00() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "AVCE00" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "AVCE00" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Arc/Info E00 (ASCII) Coverage" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "e00" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_avce00.html" ); + + poDriver->pfnOpen = OGRAVCE00DriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravce00layer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravce00layer.cpp new file mode 100644 index 000000000..6056e0986 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravce00layer.cpp @@ -0,0 +1,544 @@ +/****************************************************************************** + * $Id: ogravce00layer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: OGR + * Purpose: Implements OGRAVCE00Layer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * James Flemer + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2006, James Flemer + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_avc.h" +#include "ogr_api.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogravce00layer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGRAVCE00Layer() */ +/************************************************************************/ + +OGRAVCE00Layer::OGRAVCE00Layer( OGRAVCDataSource *poDSIn, + AVCE00Section *psSectionIn ) + : OGRAVCLayer( psSectionIn->eType, poDSIn ), + psSection(psSectionIn), + psRead(NULL), + poArcLayer(NULL), + nFeatureCount(-1), + bNeedReset(0), + nNextFID(1), + psTableSection(NULL), + psTableRead(NULL), + pszTableFilename(NULL), + nTablePos(0), + nTableBaseField(0), + nTableAttrIndex(-1) +{ + SetupFeatureDefinition( psSection->pszName ); + /* psRead = AVCE00ReadOpenE00(psSection->pszFilename); */ + +#if 0 + szTableName[0] = '\0'; + if( psSection->eType == AVCFilePAL ) + sprintf( szTableName, "%s.PAT", poDS->GetCoverageName() ); + else if( psSection->eType == AVCFileRPL ) + sprintf( szTableName, "%s.PAT%s", poDS->GetCoverageName(), + psSectionIn->pszName ); + else if( psSection->eType == AVCFileARC ) + sprintf( szTableName, "%s.AAT", poDS->GetCoverageName() ); + else if( psSection->eType == AVCFileLAB ) + { + AVCE00ReadPtr psInfo = ((OGRAVCE00DataSource *) poDS)->GetInfo(); + + sprintf( szTableName, "%s.PAT", poDS->GetCoverageName() ); + + for( int iSection = 0; iSection < psInfo->numSections; iSection++ ) + { + if( psInfo->pasSections[iSection].eType == AVCFilePAL ) + nTableAttrIndex = poFeatureDefn->GetFieldIndex( "PolyId" ); + } + } + +#endif +} + +/************************************************************************/ +/* ~OGRAVCE00Layer() */ +/************************************************************************/ + +OGRAVCE00Layer::~OGRAVCE00Layer() + +{ + if (psRead) + { + AVCE00ReadCloseE00(psRead); + psRead = NULL; + } + + if (psTableRead) + { + AVCE00ReadCloseE00(psTableRead); + psTableRead = NULL; + } + + if (pszTableFilename) + { + CPLFree(pszTableFilename); + pszTableFilename = NULL; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRAVCE00Layer::ResetReading() + +{ + if (psRead) + { + AVCE00ReadGotoSectionE00(psRead, psSection, 0); + } + + if (psTableRead) + { + AVCE00ReadGotoSectionE00(psTableRead, psTableSection, 0); + } + + bNeedReset = FALSE; + nNextFID = 1; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRAVCE00Layer::GetFeature( GIntBig nFID ) + +{ +/* -------------------------------------------------------------------- */ +/* If we haven't started yet, open the file now. */ +/* -------------------------------------------------------------------- */ + if( psRead == NULL ) + { + psRead = AVCE00ReadOpenE00(psSection->pszFilename); + if (psRead == NULL) + return NULL; + /* advance to the specified line number */ + if (AVCE00ReadGotoSectionE00(psRead, psSection, 0) != 0) + return NULL; + nNextFID = 1; + } + +/* -------------------------------------------------------------------- */ +/* Read the raw feature - the -3 fid is a special flag */ +/* indicating serial access. */ +/* -------------------------------------------------------------------- */ + void *pFeature; + + if( nFID == -3 ) + { + while( (pFeature = AVCE00ReadNextObjectE00(psRead)) != NULL + && psRead->hParseInfo->eFileType != AVCFileUnknown + && !MatchesSpatialFilter( pFeature ) ) + { + nNextFID++; + } + } + else + { + bNeedReset = TRUE; + + if (nNextFID > nFID) + { + /* advance to the specified line number */ + if (AVCE00ReadGotoSectionE00(psRead, psSection, 0) != 0) + return NULL; + } + + do + { + pFeature = AVCE00ReadNextObjectE00(psRead); + ++nNextFID; + } + while (NULL != pFeature && nNextFID <= nFID); + } + + if( pFeature == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Translate the feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poFeature = TranslateFeature( pFeature ); + if( poFeature == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* LAB's we have to assign the FID to directly, since it */ +/* doesn't seem to be stored in the file structure. */ +/* -------------------------------------------------------------------- */ + if( psSection->eType == AVCFileLAB ) + { + if( nFID == -3 ) + poFeature->SetFID( nNextFID++ ); + else + poFeature->SetFID( nFID ); + } + +/* -------------------------------------------------------------------- */ +/* If this is a polygon layer, try to assemble the arcs to form */ +/* the whole polygon geometry. */ +/* -------------------------------------------------------------------- */ + if( psSection->eType == AVCFilePAL + || psSection->eType == AVCFileRPL ) + { + FormPolygonGeometry( poFeature, (AVCPal *) pFeature ); + } + +/* -------------------------------------------------------------------- */ +/* If we have an attribute table, append the attributes now. */ +/* -------------------------------------------------------------------- */ + AppendTableFields( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRAVCE00Layer::GetNextFeature() + +{ + if( bNeedReset ) + ResetReading(); + + OGRFeature *poFeature = GetFeature( -3 ); + + // Skip universe polygon. + if( poFeature != NULL && poFeature->GetFID() == 1 + && psSection->eType == AVCFilePAL ) + { + OGRFeature::DestroyFeature( poFeature ); + poFeature = GetFeature( -3 ); + } + + while( poFeature != NULL + && ((m_poAttrQuery != NULL + && !m_poAttrQuery->Evaluate( poFeature ) ) + || !FilterGeometry( poFeature->GetGeometryRef() ) ) ) + { + OGRFeature::DestroyFeature( poFeature ); + poFeature = GetFeature( -3 ); + } + + if( poFeature == NULL ) + ResetReading(); + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +#if 0 +int OGRAVCE00Layer::TestCapability( const char * pszCap ) + +{ + if( eSectionType == AVCFileARC && EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + else + return OGRAVCLayer::TestCapability( pszCap ); +} +#endif + +/************************************************************************/ +/* FormPolygonGeometry() */ +/* */ +/* Collect all the arcs forming edges to this polygon and form */ +/* them into the appropriate OGR geometry on the target feature. */ +/************************************************************************/ + +int OGRAVCE00Layer::FormPolygonGeometry( OGRFeature *poFeature, + AVCPal *psPAL ) +{ +/* -------------------------------------------------------------------- */ +/* Try to find the corresponding ARC layer if not already */ +/* recorded. */ +/* -------------------------------------------------------------------- */ + if( poArcLayer == NULL ) + { + int i; + + for( i = 0; i < poDS->GetLayerCount(); i++ ) + { + OGRAVCE00Layer *poLayer = (OGRAVCE00Layer *) poDS->GetLayer(i); + + if( poLayer->eSectionType == AVCFileARC ) + poArcLayer = poLayer; + } + + if( poArcLayer == NULL ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Read all the arcs related to this polygon, making a working */ +/* copy of them since the one returned by AVC is temporary. */ +/* -------------------------------------------------------------------- */ + OGRGeometryCollection oArcs; + int iArc; + + for( iArc = 0; iArc < psPAL->numArcs; iArc++ ) + { + OGRFeature *poArc; + + if( psPAL->pasArcs[iArc].nArcId == 0 ) + continue; + + // If the other side of the line is the same polygon then this + // arc is a "bridge" arc and can be discarded. If we don't discard + // it, then we should double it as bridge arcs seem to only appear + // once. But by discarding it we ensure a multi-ring polygon will be + // properly formed. + if( psPAL->pasArcs[iArc].nAdjPoly == psPAL->nPolyId ) + continue; + + poArc = poArcLayer->GetFeature( ABS(psPAL->pasArcs[iArc].nArcId) ); + + if( poArc == NULL ) + return FALSE; + + if( poArc->GetGeometryRef() == NULL ) + return FALSE; + + oArcs.addGeometry( poArc->GetGeometryRef() ); + OGRFeature::DestroyFeature( poArc ); + } + + OGRErr eErr; + OGRPolygon *poPolygon; + + poPolygon = (OGRPolygon *) + OGRBuildPolygonFromEdges( (OGRGeometryH) &oArcs, TRUE, FALSE, + 0.0, &eErr ); + if( poPolygon != NULL ) + poFeature->SetGeometryDirectly( poPolygon ); + + return eErr == OGRERR_NONE; +} + +/************************************************************************/ +/* CheckSetupTable() */ +/* */ +/* Check if the named table exists, and if so, setup access to */ +/* it (open it), and add it's fields to the feature class */ +/* definition. */ +/************************************************************************/ + +int OGRAVCE00Layer::CheckSetupTable(AVCE00Section *psTblSectionIn) +{ + if (psTableRead) + return FALSE; + + const char *pszTableType = NULL; + switch (eSectionType) + { + case AVCFileARC: + pszTableType = ".AAT"; + break; + + case AVCFilePAL: + case AVCFileLAB: + pszTableType = ".PAT"; + break; + + default: + break; + } + +/* -------------------------------------------------------------------- */ +/* Is the table type found anywhere in the section pszName? Do */ +/* a case insensitive check. */ +/* -------------------------------------------------------------------- */ + if( pszTableType == NULL ) + return FALSE; + + int iCheckOff; + for( iCheckOff = 0; + psTblSectionIn->pszName[iCheckOff] != '\0'; + iCheckOff++ ) + { + if( EQUALN(psTblSectionIn->pszName + iCheckOff, + pszTableType, strlen(pszTableType) ) ) + break; + } + + if( psTblSectionIn->pszName[iCheckOff] == '\0' ) + return FALSE; + + psTableSection = psTblSectionIn; + +/* -------------------------------------------------------------------- */ +/* Try opening the table. */ +/* -------------------------------------------------------------------- */ + psTableRead = AVCE00ReadOpenE00(psTblSectionIn->pszFilename); + if (psTableRead == NULL) + return FALSE; + + /* advance to the specified line number */ + if (AVCE00ReadGotoSectionE00(psTableRead, psTableSection, 0) != 0) + { + AVCE00ReadCloseE00(psTableRead); + psTableRead = NULL; + return FALSE; + } + + AVCE00ReadNextObjectE00(psTableRead); + bNeedReset = 1; + + pszTableFilename = CPLStrdup(psTblSectionIn->pszFilename); + nTableBaseField = poFeatureDefn->GetFieldCount(); + + if (eSectionType == AVCFileLAB) + { + AVCE00ReadE00Ptr psInfo = ((OGRAVCE00DataSource *) poDS)->GetInfo(); + for( int iSection = 0; iSection < psInfo->numSections; iSection++ ) + { + if( psInfo->pasSections[iSection].eType == AVCFilePAL ) + nTableAttrIndex = poFeatureDefn->GetFieldIndex( "PolyId" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Setup attributes. */ +/* -------------------------------------------------------------------- */ + AppendTableDefinition( psTableRead->hParseInfo->hdr.psTableDef ); + +/* -------------------------------------------------------------------- */ +/* Close table so we don't have to many files open at once. */ +/* -------------------------------------------------------------------- */ + /* AVCE00ReadCloseE00( psTableRead ); */ + + return TRUE; +} + +/************************************************************************/ +/* AppendTableFields() */ +/************************************************************************/ + +int OGRAVCE00Layer::AppendTableFields( OGRFeature *poFeature ) + +{ + if (psTableRead == NULL) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Open the table if it is currently closed. */ +/* -------------------------------------------------------------------- */ + if (psTableRead == NULL) + { + psTableRead = AVCE00ReadOpenE00(pszTableFilename); + if (psTableRead == NULL) + return FALSE; + + /* advance to the specified line number */ + if (AVCE00ReadGotoSectionE00(psTableRead, psTableSection, 0) != 0) + { + AVCE00ReadCloseE00(psTableRead); + psTableRead = NULL; + return FALSE; + } + nTablePos = 0; + } + +/* -------------------------------------------------------------------- */ +/* Read the info record. */ +/* */ +/* We usually assume the FID of the feature is the key but in a */ +/* polygon coverage we need to use the PolyId attribute of LAB */ +/* features to lookup the related attributes. In this case */ +/* nTableAttrIndex will already be setup to refer to the */ +/* PolyId field. */ +/* -------------------------------------------------------------------- */ + int nRecordId; + void *hRecord; + + if( nTableAttrIndex == -1 ) + nRecordId = (int) poFeature->GetFID(); + else + nRecordId = poFeature->GetFieldAsInteger( nTableAttrIndex ); + + if (nRecordId <= nTablePos) + { + if (AVCE00ReadGotoSectionE00(psTableRead, psTableSection, 0) != 0) + return FALSE; + nTablePos = 0; + } + + do + { + hRecord = AVCE00ReadNextObjectE00(psTableRead); + ++nTablePos; + } + while (NULL != hRecord && nTablePos < nRecordId); + + if( hRecord == NULL ) + return FALSE; + + +/* -------------------------------------------------------------------- */ +/* Translate it. */ +/* -------------------------------------------------------------------- */ + return TranslateTableFields( poFeature, nTableBaseField, + psTableRead->hParseInfo->hdr.psTableDef, + (AVCField *) hRecord ); +} + + +GIntBig OGRAVCE00Layer::GetFeatureCount(int bForce) +{ + if (m_poAttrQuery != NULL || m_poFilterGeom != NULL) + return OGRAVCLayer::GetFeatureCount(bForce); + + if (bForce && nFeatureCount < 0) + { + if (psSection->nFeatureCount < 0) + { + nFeatureCount = (int) OGRLayer::GetFeatureCount(bForce); + } + else + { + nFeatureCount = psSection->nFeatureCount; + if (psSection->eType == AVCFilePAL) + --nFeatureCount; + } + } + return nFeatureCount; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravclayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravclayer.cpp new file mode 100644 index 000000000..9f79981fc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/avc/ogravclayer.cpp @@ -0,0 +1,594 @@ +/****************************************************************************** + * $Id: ogravclayer.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OGR + * Purpose: Implements OGRAVCLayer class. This is the base class for E00 + * and binary coverage layer implementations. It provides some base + * layer operations, and methods for transforming between OGR + * features, and the in memory structures of the AVC library. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_avc.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogravclayer.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRAVCLayer() */ +/************************************************************************/ + +OGRAVCLayer::OGRAVCLayer( AVCFileType eSectionTypeIn, + OGRAVCDataSource *poDSIn ) + +{ + eSectionType = eSectionTypeIn; + + poFeatureDefn = NULL; + poDS = poDSIn; +} + +/************************************************************************/ +/* ~OGRAVCLayer() */ +/************************************************************************/ + +OGRAVCLayer::~OGRAVCLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "AVC", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poFeatureDefn != NULL ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRAVCLayer::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRAVCLayer::GetSpatialRef() + +{ + return poDS->GetSpatialRef(); +} + +/************************************************************************/ +/* SetupFeatureDefinition() */ +/************************************************************************/ + +int OGRAVCLayer::SetupFeatureDefinition( const char *pszName ) + +{ + switch( eSectionType ) + { + case AVCFileARC: + { + poFeatureDefn = new OGRFeatureDefn( pszName ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbLineString ); + + OGRFieldDefn oUserId( "UserId", OFTInteger ); + OGRFieldDefn oFNode( "FNODE_", OFTInteger ); + OGRFieldDefn oTNode( "TNODE_", OFTInteger ); + OGRFieldDefn oLPoly( "LPOLY_", OFTInteger ); + OGRFieldDefn oRPoly( "RPOLY_", OFTInteger ); + + poFeatureDefn->AddFieldDefn( &oUserId ); + poFeatureDefn->AddFieldDefn( &oFNode ); + poFeatureDefn->AddFieldDefn( &oTNode ); + poFeatureDefn->AddFieldDefn( &oLPoly ); + poFeatureDefn->AddFieldDefn( &oRPoly ); + } + return TRUE; + + case AVCFilePAL: + case AVCFileRPL: + { + poFeatureDefn = new OGRFeatureDefn( pszName ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPolygon ); + + OGRFieldDefn oArcIds( "ArcIds", OFTIntegerList ); + poFeatureDefn->AddFieldDefn( &oArcIds ); + } + return TRUE; + + case AVCFileCNT: + { + poFeatureDefn = new OGRFeatureDefn( pszName ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oLabelIds( "LabelIds", OFTIntegerList ); + poFeatureDefn->AddFieldDefn( &oLabelIds ); + } + return TRUE; + + case AVCFileLAB: + { + poFeatureDefn = new OGRFeatureDefn( pszName ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oValueId( "ValueId", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oValueId ); + + OGRFieldDefn oPolyId( "PolyId", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oPolyId ); + } + return TRUE; + + case AVCFileTXT: + case AVCFileTX6: + { + poFeatureDefn = new OGRFeatureDefn( pszName ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oUserId( "UserId", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oUserId ); + + OGRFieldDefn oText( "Text", OFTString ); + poFeatureDefn->AddFieldDefn( &oText ); + + OGRFieldDefn oHeight( "Height", OFTReal ); + poFeatureDefn->AddFieldDefn( &oHeight ); + + OGRFieldDefn oLevel( "Level", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oLevel ); + } + return TRUE; + + default: + poFeatureDefn = NULL; + return FALSE; + } + + SetDescription( pszName ); +} + +/************************************************************************/ +/* TranslateFeature() */ +/* */ +/* Translate the AVC structure for a feature to the the */ +/* corresponding OGR definition. It is assumed that the passed */ +/* in feature is of a type matching the section type */ +/* established by SetupFeatureDefinition(). */ +/************************************************************************/ + +OGRFeature *OGRAVCLayer::TranslateFeature( void *pAVCFeature ) + +{ + m_nFeaturesRead++; + + switch( eSectionType ) + { +/* ==================================================================== */ +/* ARC */ +/* ==================================================================== */ + case AVCFileARC: + { + AVCArc *psArc = (AVCArc *) pAVCFeature; + +/* -------------------------------------------------------------------- */ +/* Create feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poOGRFeature = new OGRFeature( GetLayerDefn() ); + poOGRFeature->SetFID( psArc->nArcId ); + +/* -------------------------------------------------------------------- */ +/* Apply the line geometry. */ +/* -------------------------------------------------------------------- */ + OGRLineString *poLine = new OGRLineString(); + + poLine->setNumPoints( psArc->numVertices ); + for( int iVert = 0; iVert < psArc->numVertices; iVert++ ) + poLine->setPoint( iVert, + psArc->pasVertices[iVert].x, + psArc->pasVertices[iVert].y ); + + poOGRFeature->SetGeometryDirectly( poLine ); + +/* -------------------------------------------------------------------- */ +/* Apply attributes. */ +/* -------------------------------------------------------------------- */ + poOGRFeature->SetField( 0, psArc->nUserId ); + poOGRFeature->SetField( 1, psArc->nFNode ); + poOGRFeature->SetField( 2, psArc->nTNode ); + poOGRFeature->SetField( 3, psArc->nLPoly ); + poOGRFeature->SetField( 4, psArc->nRPoly ); + return poOGRFeature; + } + +/* ==================================================================== */ +/* PAL (Polygon) */ +/* RPL (Region) */ +/* ==================================================================== */ + case AVCFilePAL: + case AVCFileRPL: + { + AVCPal *psPAL = (AVCPal *) pAVCFeature; + +/* -------------------------------------------------------------------- */ +/* Create feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poOGRFeature = new OGRFeature( GetLayerDefn() ); + poOGRFeature->SetFID( psPAL->nPolyId ); + +/* -------------------------------------------------------------------- */ +/* Apply attributes. */ +/* -------------------------------------------------------------------- */ + // Setup ArcId list. + int *panArcs, i; + + panArcs = (int *) CPLMalloc(sizeof(int) * psPAL->numArcs ); + for( i = 0; i < psPAL->numArcs; i++ ) + panArcs[i] = psPAL->pasArcs[i].nArcId; + poOGRFeature->SetField( 0, psPAL->numArcs, panArcs ); + CPLFree( panArcs ); + + return poOGRFeature; + } + +/* ==================================================================== */ +/* CNT (Centroid) */ +/* ==================================================================== */ + case AVCFileCNT: + { + AVCCnt *psCNT = (AVCCnt *) pAVCFeature; + +/* -------------------------------------------------------------------- */ +/* Create feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poOGRFeature = new OGRFeature( GetLayerDefn() ); + poOGRFeature->SetFID( psCNT->nPolyId ); + +/* -------------------------------------------------------------------- */ +/* Apply Geometry */ +/* -------------------------------------------------------------------- */ + poOGRFeature->SetGeometryDirectly( + new OGRPoint( psCNT->sCoord.x, psCNT->sCoord.y ) ); + +/* -------------------------------------------------------------------- */ +/* Apply attributes. */ +/* -------------------------------------------------------------------- */ + poOGRFeature->SetField( 0, psCNT->numLabels, psCNT->panLabelIds ); + + return poOGRFeature; + } + +/* ==================================================================== */ +/* LAB (Label) */ +/* ==================================================================== */ + case AVCFileLAB: + { + AVCLab *psLAB = (AVCLab *) pAVCFeature; + +/* -------------------------------------------------------------------- */ +/* Create feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poOGRFeature = new OGRFeature( GetLayerDefn() ); + poOGRFeature->SetFID( psLAB->nValue ); + +/* -------------------------------------------------------------------- */ +/* Apply Geometry */ +/* -------------------------------------------------------------------- */ + poOGRFeature->SetGeometryDirectly( + new OGRPoint( psLAB->sCoord1.x, psLAB->sCoord1.y ) ); + +/* -------------------------------------------------------------------- */ +/* Apply attributes. */ +/* -------------------------------------------------------------------- */ + poOGRFeature->SetField( 0, psLAB->nValue ); + poOGRFeature->SetField( 1, psLAB->nPolyId ); + + return poOGRFeature; + } + +/* ==================================================================== */ +/* TXT/TX6 (Text) */ +/* ==================================================================== */ + case AVCFileTXT: + case AVCFileTX6: + { + AVCTxt *psTXT = (AVCTxt *) pAVCFeature; + +/* -------------------------------------------------------------------- */ +/* Create feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poOGRFeature = new OGRFeature( GetLayerDefn() ); + poOGRFeature->SetFID( psTXT->nTxtId ); + +/* -------------------------------------------------------------------- */ +/* Apply Geometry */ +/* -------------------------------------------------------------------- */ + if( psTXT->numVerticesLine > 0 ) + poOGRFeature->SetGeometryDirectly( + new OGRPoint( psTXT->pasVertices[0].x, + psTXT->pasVertices[0].y ) ); + +/* -------------------------------------------------------------------- */ +/* Apply attributes. */ +/* -------------------------------------------------------------------- */ + poOGRFeature->SetField( 0, psTXT->nUserId ); + poOGRFeature->SetField( 1, (const char *)psTXT->pszText ); + poOGRFeature->SetField( 2, psTXT->dHeight ); + poOGRFeature->SetField( 3, psTXT->nLevel ); + + return poOGRFeature; + } + + default: + return NULL; + } +} + +/************************************************************************/ +/* MatchesSpatialFilter() */ +/************************************************************************/ + +int OGRAVCLayer::MatchesSpatialFilter( void *pFeature ) + +{ + if( m_poFilterGeom == NULL ) + return TRUE; + + switch( eSectionType ) + { +/* ==================================================================== */ +/* ARC */ +/* */ +/* Check each line segment for possible intersection. */ +/* ==================================================================== */ + case AVCFileARC: + { + AVCArc *psArc = (AVCArc *) pFeature; + + for( int iVert = 0; iVert < psArc->numVertices-1; iVert++ ) + { + AVCVertex *psV1 = psArc->pasVertices + iVert; + AVCVertex *psV2 = psArc->pasVertices + iVert + 1; + + if( (psV1->x < m_sFilterEnvelope.MinX + && psV2->x < m_sFilterEnvelope.MinX) + || (psV1->x > m_sFilterEnvelope.MaxX + && psV2->x > m_sFilterEnvelope.MaxX) + || (psV1->y < m_sFilterEnvelope.MinY + && psV2->y < m_sFilterEnvelope.MinY) + || (psV1->y > m_sFilterEnvelope.MaxY + && psV2->y > m_sFilterEnvelope.MaxY) ) + /* This segment is completely outside extents */; + else + return TRUE; + } + + return FALSE; + } + +/* ==================================================================== */ +/* PAL (Polygon) */ +/* RPL (Region) */ +/* */ +/* Check against the polygon bounds stored in the PAL. */ +/* ==================================================================== */ + case AVCFilePAL: + case AVCFileRPL: + { + AVCPal *psPAL = (AVCPal *) pFeature; + + if( psPAL->sMin.x > m_sFilterEnvelope.MaxX + || psPAL->sMax.x < m_sFilterEnvelope.MinX + || psPAL->sMin.y > m_sFilterEnvelope.MaxY + || psPAL->sMax.y < m_sFilterEnvelope.MinY ) + return FALSE; + else + return TRUE; + } + +/* ==================================================================== */ +/* CNT (Centroid) */ +/* ==================================================================== */ + case AVCFileCNT: + { + AVCCnt *psCNT = (AVCCnt *) pFeature; + + if( psCNT->sCoord.x < m_sFilterEnvelope.MinX + || psCNT->sCoord.x > m_sFilterEnvelope.MaxX + || psCNT->sCoord.y < m_sFilterEnvelope.MinY + || psCNT->sCoord.y > m_sFilterEnvelope.MaxY ) + return FALSE; + else + return TRUE; + } + +/* ==================================================================== */ +/* LAB (Label) */ +/* ==================================================================== */ + case AVCFileLAB: + { + AVCLab *psLAB = (AVCLab *) pFeature; + + if( psLAB->sCoord1.x < m_sFilterEnvelope.MinX + || psLAB->sCoord1.x > m_sFilterEnvelope.MaxX + || psLAB->sCoord1.y < m_sFilterEnvelope.MinY + || psLAB->sCoord1.y > m_sFilterEnvelope.MaxY ) + return FALSE; + else + return TRUE; + } + +/* ==================================================================== */ +/* TXT/TX6 (Text) */ +/* ==================================================================== */ + case AVCFileTXT: + case AVCFileTX6: + { + AVCTxt *psTXT = (AVCTxt *) pFeature; + + if( psTXT->numVerticesLine == 0 ) + return TRUE; + + if( psTXT->pasVertices[0].x < m_sFilterEnvelope.MinX + || psTXT->pasVertices[0].x > m_sFilterEnvelope.MaxX + || psTXT->pasVertices[0].y < m_sFilterEnvelope.MinY + || psTXT->pasVertices[0].y > m_sFilterEnvelope.MaxY ) + return FALSE; + else + return TRUE; + } + + default: + return TRUE; + } +} + +/************************************************************************/ +/* AppendTableDefinition() */ +/* */ +/* Add fields to this layers feature definition based on the */ +/* definition from the coverage. */ +/************************************************************************/ + +int OGRAVCLayer::AppendTableDefinition( AVCTableDef *psTableDef ) + +{ + for( int iField = 0; iField < psTableDef->numFields; iField++ ) + { + AVCFieldInfo *psFInfo = psTableDef->pasFieldDef + iField; + char szFieldName[128]; + + /* Strip off white space */ + strcpy( szFieldName, psFInfo->szName ); + if( strstr(szFieldName," ") != NULL ) + *(strstr(szFieldName," ")) = '\0'; + + OGRFieldDefn oFDefn( szFieldName, OFTInteger ); + + if( psFInfo->nIndex < 0 ) + continue; + + // Skip FNODE#, TNODE#, LPOLY# and RPOLY# from AAT table. + if( eSectionType == AVCFileARC && iField < 4 ) + continue; + + oFDefn.SetWidth( psFInfo->nFmtWidth ); + + if( psFInfo->nType1 * 10 == AVC_FT_DATE + || psFInfo->nType1 * 10 == AVC_FT_CHAR ) + oFDefn.SetType( OFTString ); + + else if( psFInfo->nType1 * 10 == AVC_FT_FIXINT + || psFInfo->nType1 * 10 == AVC_FT_BININT ) + oFDefn.SetType( OFTInteger ); + + else if( psFInfo->nType1 * 10 == AVC_FT_FIXNUM + || psFInfo->nType1 * 10 == AVC_FT_BINFLOAT ) + { + oFDefn.SetType( OFTReal ); + if( psFInfo->nFmtPrec > 0 ) + oFDefn.SetPrecision( psFInfo->nFmtPrec ); + } + + poFeatureDefn->AddFieldDefn( &oFDefn ); + } + return TRUE; +} + +/************************************************************************/ +/* TranslateTableFields() */ +/************************************************************************/ + +int OGRAVCLayer::TranslateTableFields( OGRFeature *poFeature, + int nFieldBase, + AVCTableDef *psTableDef, + AVCField *pasFields ) + +{ + int iOutField = nFieldBase; + + for( int iField=0; iField < psTableDef->numFields; iField++ ) + { + AVCFieldInfo *psFInfo = psTableDef->pasFieldDef + iField; + int nType = psFInfo->nType1 * 10; + + if( psFInfo->nIndex < 0 ) + continue; + + // Skip FNODE#, TNODE#, LPOLY# and RPOLY# from AAT table. + if( eSectionType == AVCFileARC && iField < 4 ) + continue; + + if (nType == AVC_FT_DATE || nType == AVC_FT_CHAR || + nType == AVC_FT_FIXINT || nType == AVC_FT_FIXNUM) + { + if (nType == AVC_FT_CHAR) + { + /* Remove trailing spaces in char fields */ + int nLen = strlen((const char*)pasFields[iField].pszStr); + while (nLen > 0 && pasFields[iField].pszStr[nLen-1] == ' ') + nLen--; + pasFields[iField].pszStr[nLen] = '\0'; + } + poFeature->SetField( iOutField++, + (const char *)pasFields[iField].pszStr ); + } + else if (nType == AVC_FT_BININT && psFInfo->nSize == 4) + { + poFeature->SetField( iOutField++, pasFields[iField].nInt32 ); + } + else if (nType == AVC_FT_BININT && psFInfo->nSize == 2) + { + poFeature->SetField( iOutField++, pasFields[iField].nInt16 ); + } + else if (nType == AVC_FT_BINFLOAT && psFInfo->nSize == 4) + { + poFeature->SetField( iOutField++, pasFields[iField].fFloat ); + } + else if (nType == AVC_FT_BINFLOAT && psFInfo->nSize == 8) + { + poFeature->SetField( iOutField++, pasFields[iField].dDouble ); + } + else + { + CPLAssert( FALSE ); + return FALSE; + } + } + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/GNUmakefile new file mode 100644 index 000000000..3861a4006 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/GNUmakefile @@ -0,0 +1,15 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrbnadriver.o ogrbnadatasource.o ogrbnalayer.o ogrbnaparser.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_bna.h ogrbnaparser.h + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/drv_bna.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/drv_bna.html new file mode 100644 index 000000000..20b33d7c7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/drv_bna.html @@ -0,0 +1,105 @@ + + +BNA - Atlas BNA + + + + +

    BNA - Atlas BNA

    + +The BNA format is an ASCII exchange format for 2D vector data supported +by many software packages. +It only contains geometry and a few identifiers per record. Attributes must be stored into external files. +It does not support any coordinate system information.

    + +OGR has support for BNA reading and writing.

    + +The OGR driver supports reading and writing of all the BNA feature types : +

      +
    • points
    • +
    • polygons
    • +
    • lines
    • +
    • ellipses/circles
    • +
    + +As the BNA format is lacking from a formal specification, there can be various +forms of BNA data files. The OGR driver does it best to parse BNA datasets and supports +single line or multi line record formats, records with 2, 3 or 4 identifiers, +etc etc. If you have a BNA data file that cannot be parsed correctly by the BNA driver, +please report on the GDAL track system.

    + +To be recognized as BNA, the file extension must be ".bna". +When reading a BNA file, the driver will scan it entirely to find out which layers are +available. If the file name is foo.bna, the layers will be named foo_points, foo_polygons, foo_lines and foo_ellipses.

    + +The BNA driver support reading of polygons with holes or lakes. It determines what is a hole or a lake only from +geometrical analysis (inclusion, non-intersection tests) and ignores completely the notion of polygon winding (whether the +polygon edges are described clockwise or counter-clockwise). GDAL must be built with GEOS enabled to make geometry test work. Polygons are exposed as multipolygons in the OGR Simple Feature model.

    + +Ellipses and circles are discretized as polygons with 360 points.

    + +

    Creation Issues

    + +On export all layers are written to a single BNA file. Update of existing +files is not currently supported.

    +If the output file already exits, the writing will not occur. You have to delete the existing file first.

    + +The BNA writer supports the following creation options (dataset options): +

      +
    • LINEFORMAT: +By default when creating new .BNA files they are created with the line +termination conventions of the local platform (CR/LF on win32 or +LF on all other systems). This may be overridden through use of the +LINEFORMAT layer creation option which may have a value of CRLF +(DOS format) or LF (Unix format).

      +

    • MULTILINE: +By default, BNA files are created in the multi-line format (for each record, the first line contains the identifiers and the type/number of coordinates to follow. The following lines contains a pair of coordinates). This may be overridden through use of MULTILINE=NO.

      +

    • NB_IDS: +BNA records may contain from 2 to 4 identifiers per record. Some software packages only support a precise number of identifiers. You can override the default value (2) by a precise value : 2, 3 or 4, or NB_SOURCE_FIELDS. NB_SOURCE_FIELDS means +that the output file will contains the same number of identifiers as the features written in (clamped between 2 and 4).

      +

    • ELLIPSES_AS_ELLIPSES: +the BNA writer will try to recognize ellipses and circles when writing a polygon. This will only work if the feature has previously been read from a BNA file. As some software packages do not support ellipses/circles in BNA data file, it may be useful to tell the writer by specifying ELLIPSES_AS_ELLIPSES=NO not to export them as such, but keep them as polygons.

      +

    • NB_PAIRS_PER_LINE: +this option may be used to limit the number of coordinate pairs per line in multiline format.

      +

    • COORDINATE_PRECISION: +this option may be used to set the number of decimal for coordinates. Default value is 10.

      +

      + +

    + +

    VSI Virtual File System API support

    + +(Some features below might require OGR >= 1.9.0)

    + +The driver supports reading and writing to files managed by VSI Virtual File System API, which include +"regular" files, as well as files in the /vsizip/ (read-write) , /vsigzip/ (read-write) , /vsicurl/ (read-only) domains.

    + +Writing to /dev/stdout or /vsistdout/ is also supported.

    + +

    Example

    + +The ogrinfo utility can be used to dump the content of a BNA datafile : + +
    +ogrinfo -ro -al a_bna_file.bna
    +
    + +

    +The ogr2ogr utility can be used to do BNA to BNA translation : + +

    +ogr2ogr -f BNA -dsco "NB_IDS=2" -dsco "ELLIPSES_AS_ELLIPSES=NO" output.bna input.bna
    +
    +
    + + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/makefile.vc new file mode 100644 index 000000000..c7ae1c8a4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/makefile.vc @@ -0,0 +1,17 @@ + +OBJ = ogrbnadriver.obj ogrbnadatasource.obj ogrbnalayer.obj \ + ogrbnaparser.obj + +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogr_bna.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogr_bna.h new file mode 100644 index 000000000..0a778e11c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogr_bna.h @@ -0,0 +1,156 @@ +/****************************************************************************** + * $Id: ogr_bna.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: BNA Translator + * Purpose: Definition of classes for OGR .bna driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_BNA_H_INCLUDED +#define _OGR_BNA_H_INCLUDED + +#include "ogrsf_frmts.h" + +#include "ogrbnaparser.h" + +class OGRBNADataSource; + +/************************************************************************/ +/* OGRBNALayer */ +/************************************************************************/ + +typedef struct +{ + int offset; + int line; +} OffsetAndLine; + +class OGRBNALayer : public OGRLayer +{ + OGRFeatureDefn* poFeatureDefn; + + OGRBNADataSource* poDS; + int bWriter; + + int nIDs; + int eof; + int failed; + int curLine; + int nNextFID; + VSILFILE* fpBNA; + int nFeatures; + int partialIndexTable; + OffsetAndLine* offsetAndLineFeaturesTable; + + BNAFeatureType bnaFeatureType; + + OGRFeature * BuildFeatureFromBNARecord (BNARecord* record, long fid); + void FastParseUntil ( int interestFID); + void WriteFeatureAttributes(VSILFILE* fp, OGRFeature *poFeature); + void WriteCoord(VSILFILE* fp, double dfX, double dfY); + + public: + OGRBNALayer(const char *pszFilename, + const char* layerName, + BNAFeatureType bnaFeatureType, + OGRwkbGeometryType eLayerGeomType, + int bWriter, + OGRBNADataSource* poDS, + int nIDs = NB_MAX_BNA_IDS); + ~OGRBNALayer(); + + void SetFeatureIndexTable(int nFeatures, + OffsetAndLine* offsetAndLineFeaturesTable, + int partialIndexTable); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRErr ICreateFeature( OGRFeature *poFeature ); + OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + OGRFeature * GetFeature( GIntBig nFID ); + + int TestCapability( const char * ); + +}; + +/************************************************************************/ +/* OGRBNADataSource */ +/************************************************************************/ + +class OGRBNADataSource : public OGRDataSource +{ + char* pszName; + + OGRBNALayer** papoLayers; + int nLayers; + + int bUpdate; + + /* Export related */ + VSILFILE *fpOutput; /* Virtual file API */ + int bUseCRLF; + int bMultiLine; + int nbOutID; + int bEllipsesAsEllipses; + int nbPairPerLine; + int coordinatePrecision; + char* pszCoordinateSeparator; + + public: + OGRBNADataSource(); + ~OGRBNADataSource(); + + VSILFILE *GetOutputFP() { return fpOutput; } + int GetUseCRLF() { return bUseCRLF; } + int GetMultiLine() { return bMultiLine; } + int GetNbOutId() { return nbOutID; } + int GetEllipsesAsEllipses() { return bEllipsesAsEllipses; } + int GetNbPairPerLine() { return nbPairPerLine; } + int GetCoordinatePrecision() { return coordinatePrecision; } + const char* GetCoordinateSeparator() { return pszCoordinateSeparator; } + + int Open( const char * pszFilename, + int bUpdate ); + + int Create( const char *pszFilename, + char **papszOptions ); + + const char* GetName() { return pszName; } + + int GetLayerCount() { return nLayers; } + OGRLayer* GetLayer( int ); + + OGRLayer * ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ); + + int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_BNA_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnadatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnadatasource.cpp new file mode 100644 index 000000000..2d6130a81 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnadatasource.cpp @@ -0,0 +1,390 @@ +/****************************************************************************** + * $Id: ogrbnadatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: BNA Translator + * Purpose: Implements OGRBNADataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2007-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_bna.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" +#include "ogrbnaparser.h" + +/************************************************************************/ +/* OGRBNADataSource() */ +/************************************************************************/ + +OGRBNADataSource::OGRBNADataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + fpOutput = NULL; + + pszName = NULL; + + pszCoordinateSeparator = NULL; + + bUpdate = FALSE; +} + +/************************************************************************/ +/* ~OGRBNADataSource() */ +/************************************************************************/ + +OGRBNADataSource::~OGRBNADataSource() + +{ + if ( fpOutput != NULL ) + { + VSIFCloseL( fpOutput); + } + + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + CPLFree( pszCoordinateSeparator ); + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRBNADataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCDeleteLayer) ) + return FALSE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRBNADataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * OGRBNADataSource::ICreateLayer( const char * pszLayerName, + CPL_UNUSED OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + CPL_UNUSED char ** papszOptions ) +{ + BNAFeatureType bnaFeatureType; + + switch(eType) + { + case wkbPolygon: + case wkbPolygon25D: + case wkbMultiPolygon: + case wkbMultiPolygon25D: + bnaFeatureType = BNA_POLYGON; + break; + + case wkbPoint: + case wkbPoint25D: + bnaFeatureType = BNA_POINT; + break; + + case wkbLineString: + case wkbLineString25D: + bnaFeatureType = BNA_POLYLINE; + break; + + default: + CPLError( CE_Failure, CPLE_NotSupported, + "Geometry type of `%s' not supported in BNAs.\n", + OGRGeometryTypeToName(eType) ); + return NULL; + } + + nLayers++; + papoLayers = (OGRBNALayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRBNALayer*)); + papoLayers[nLayers-1] = new OGRBNALayer( pszName, pszLayerName, bnaFeatureType, eType, TRUE, this ); + + return papoLayers[nLayers-1]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRBNADataSource::Open( const char * pszFilename, int bUpdateIn) + +{ + int ok = FALSE; + + pszName = CPLStrdup( pszFilename ); + bUpdate = bUpdateIn; + + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp) + { + BNARecord* record; + int curLine = 0; + const char* layerRadixName[] = { "points", "polygons", "lines", "ellipses"}; + OGRwkbGeometryType wkbGeomTypes[] = { wkbPoint, wkbMultiPolygon, wkbLineString, wkbPolygon }; + int i; +#if defined(BNA_FAST_DS_OPEN) + record = BNA_GetNextRecord(fp, &ok, &curLine, FALSE, BNA_READ_NONE); + BNA_FreeRecord(record); + + if (ok) + { + nLayers = 4; + + papoLayers = (OGRBNALayer **) CPLMalloc(nLayers * sizeof(OGRBNALayer*)); + for(i=0;i<4;i++) + papoLayers[i] = new OGRBNALayer( pszFilename, + layerRadixName[i], + (BNAFeatureType)i, wkbGeomTypes[i], FALSE, this ); + } +#else + int nFeatures[4] = { 0, 0, 0, 0 }; + OffsetAndLine* offsetAndLineFeaturesTable[4] = { NULL, NULL, NULL, NULL }; + int nIDs[4] = {0, 0, 0, 0}; + int partialIndexTable = TRUE; + + while(1) + { + int offset = (int) VSIFTellL(fp); + int line = curLine; + record = BNA_GetNextRecord(fp, &ok, &curLine, FALSE, BNA_READ_NONE); + if (ok == FALSE) + { + BNA_FreeRecord(record); + if (line != 0) + ok = TRUE; + break; + } + if (record == NULL) + { + /* end of file */ + ok = TRUE; + + /* and we have finally build the whole index table */ + partialIndexTable = FALSE; + break; + } + + if (record->nIDs > nIDs[record->featureType]) + nIDs[record->featureType] = record->nIDs; + + nFeatures[record->featureType]++; + offsetAndLineFeaturesTable[record->featureType] = + (OffsetAndLine*)CPLRealloc(offsetAndLineFeaturesTable[record->featureType], + nFeatures[record->featureType] * sizeof(OffsetAndLine)); + offsetAndLineFeaturesTable[record->featureType][nFeatures[record->featureType]-1].offset = offset; + offsetAndLineFeaturesTable[record->featureType][nFeatures[record->featureType]-1].line = line; + + BNA_FreeRecord(record); + } + + nLayers = (nFeatures[0] != 0) + (nFeatures[1] != 0) + (nFeatures[2] != 0) + (nFeatures[3] != 0); + papoLayers = (OGRBNALayer **) CPLMalloc(nLayers * sizeof(OGRBNALayer*)); + int iLayer = 0; + for(i=0;i<4;i++) + { + if (nFeatures[i]) + { + papoLayers[iLayer] = new OGRBNALayer( pszFilename, + layerRadixName[i], + (BNAFeatureType)i, + wkbGeomTypes[i], + FALSE, + this, + nIDs[i]); + papoLayers[iLayer]->SetFeatureIndexTable(nFeatures[i], + offsetAndLineFeaturesTable[i], + partialIndexTable); + iLayer++; + } + } +#endif + VSIFCloseL(fp); + } + + return ok; +} + + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRBNADataSource::Create( const char *pszFilename, + char **papszOptions ) +{ + if( fpOutput != NULL) + { + CPLAssert( FALSE ); + return FALSE; + } + + if( strcmp(pszFilename,"/dev/stdout") == 0 ) + pszFilename = "/vsistdout/"; + +/* -------------------------------------------------------------------- */ +/* Do not override exiting file. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + + if( VSIStatL( pszFilename, &sStatBuf ) == 0 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Create the output file. */ +/* -------------------------------------------------------------------- */ + pszName = CPLStrdup( pszFilename ); + + fpOutput = VSIFOpenL( pszFilename, "wb" ); + if( fpOutput == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create BNA file %s.", + pszFilename ); + return FALSE; + } + + /* EOL token */ + const char *pszCRLFFormat = CSLFetchNameValue( papszOptions, "LINEFORMAT"); + + if( pszCRLFFormat == NULL ) + { +#ifdef WIN32 + bUseCRLF = TRUE; +#else + bUseCRLF = FALSE; +#endif + } + else if( EQUAL(pszCRLFFormat,"CRLF") ) + bUseCRLF = TRUE; + else if( EQUAL(pszCRLFFormat,"LF") ) + bUseCRLF = FALSE; + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "LINEFORMAT=%s not understood, use one of CRLF or LF.", + pszCRLFFormat ); +#ifdef WIN32 + bUseCRLF = TRUE; +#else + bUseCRLF = FALSE; +#endif + } + + /* Multi line or single line format ? */ + bMultiLine = CSLFetchBoolean( papszOptions, "MULTILINE", TRUE); + + /* Number of identifiers per record */ + const char* pszNbOutID = CSLFetchNameValue ( papszOptions, "NB_IDS"); + if (pszNbOutID == NULL) + { + nbOutID = NB_MIN_BNA_IDS; + } + else if (EQUAL(pszNbOutID, "NB_SOURCE_FIELDS")) + { + nbOutID = -1; + } + else + { + nbOutID = atoi(pszNbOutID); + if (nbOutID <= 0) + { + CPLError( CE_Warning, CPLE_AppDefined, + "NB_ID=%s not understood. Must be >=%d and <=%d or equal to NB_SOURCE_FIELDS", + pszNbOutID, NB_MIN_BNA_IDS, NB_MAX_BNA_IDS ); + nbOutID = NB_MIN_BNA_IDS; + } + if (nbOutID > NB_MAX_BNA_IDS) + { + CPLError( CE_Warning, CPLE_AppDefined, + "NB_ID=%s not understood. Must be >=%d and <=%d or equal to NB_SOURCE_FIELDS", + pszNbOutID, NB_MIN_BNA_IDS, NB_MAX_BNA_IDS ); + nbOutID = NB_MAX_BNA_IDS; + } + } + + /* Ellipses export as ellipses or polygons ? */ + bEllipsesAsEllipses = CSLFetchBoolean( papszOptions, "ELLIPSES_AS_ELLIPSES", TRUE); + + /* Number of coordinate pairs per line */ + const char* pszNbPairPerLine = CSLFetchNameValue( papszOptions, "NB_PAIRS_PER_LINE"); + if (pszNbPairPerLine) + { + nbPairPerLine = atoi(pszNbPairPerLine); + if (nbPairPerLine <= 0) + nbPairPerLine = (bMultiLine == FALSE) ? 1000000000 : 1; + if (bMultiLine == FALSE) + { + CPLError( CE_Warning, CPLE_AppDefined, "NB_PAIR_PER_LINE option is ignored when MULTILINE=NO"); + } + } + else + { + nbPairPerLine = (bMultiLine == FALSE) ? 1000000000 : 1; + } + + /* Coordinate precision */ + const char* pszCoordinatePrecision = CSLFetchNameValue( papszOptions, "COORDINATE_PRECISION"); + if (pszCoordinatePrecision) + { + coordinatePrecision = atoi(pszCoordinatePrecision); + if (coordinatePrecision <= 0) + coordinatePrecision = 0; + else if (coordinatePrecision >= 20) + coordinatePrecision = 20; + } + else + { + coordinatePrecision = 10; + } + + pszCoordinateSeparator = (char*)CSLFetchNameValue( papszOptions, "COORDINATE_SEPARATOR"); + if (pszCoordinateSeparator == NULL) + pszCoordinateSeparator = CPLStrdup(","); + else + pszCoordinateSeparator = CPLStrdup(pszCoordinateSeparator); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnadriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnadriver.cpp new file mode 100644 index 000000000..41dbb4b6b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnadriver.cpp @@ -0,0 +1,151 @@ +/****************************************************************************** + * $Id: ogrbnadriver.cpp + * + * Project: BNA Translator + * Purpose: Implements OGRBNADriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2007, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_bna.h" +#include "cpl_conv.h" + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRBNADriverOpen( GDALOpenInfo* poOpenInfo ) + +{ +// -------------------------------------------------------------------- +// Does this appear to be a .bna file? +// -------------------------------------------------------------------- + if( poOpenInfo->fpL == NULL || + !(EQUAL( CPLGetExtension(poOpenInfo->pszFilename), "bna" ) + || ((EQUALN( poOpenInfo->pszFilename, "/vsigzip/", 9) || + EQUALN( poOpenInfo->pszFilename, "/vsizip/", 8)) && + (strstr( poOpenInfo->pszFilename, ".bna") || + strstr( poOpenInfo->pszFilename, ".BNA")))) ) + { + return NULL; + } + + OGRBNADataSource *poDS = new OGRBNADataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, poOpenInfo->eAccess == GA_Update ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRBNADriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + OGRBNADataSource *poDS = new OGRBNADataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +static CPLErr OGRBNADriverDelete( const char *pszFilename ) + +{ + if( VSIUnlink( pszFilename ) == 0 ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* RegisterOGRBNA() */ +/************************************************************************/ + +void RegisterOGRBNA() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "BNA" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "BNA" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Atlas BNA" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "bna" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_bna.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +#ifdef WIN32 +" " +" " +" "); + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, ""); + + poDriver->pfnOpen = OGRBNADriverOpen; + poDriver->pfnCreate = OGRBNADriverCreate; + poDriver->pfnDelete = OGRBNADriverDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnalayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnalayer.cpp new file mode 100644 index 000000000..e1cdb33ea --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnalayer.cpp @@ -0,0 +1,874 @@ +/****************************************************************************** + * $Id: ogrbnalayer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: BNA Translator + * Purpose: Implements OGRBNALayer class. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_bna.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" +#include "ogr_p.h" + +#ifndef M_PI +# define M_PI 3.1415926535897932384626433832795 +#endif + +/************************************************************************/ +/* OGRBNALayer() */ +/* */ +/* Note that the OGRBNALayer assumes ownership of the passed */ +/* file pointer. */ +/************************************************************************/ + +OGRBNALayer::OGRBNALayer( const char *pszFilename, + const char* layerName, + BNAFeatureType bnaFeatureType, + OGRwkbGeometryType eLayerGeomType, + int bWriter, + OGRBNADataSource* poDS, + int nIDs) + +{ + eof = FALSE; + failed = FALSE; + curLine = 0; + nNextFID = 0; + + this->bWriter = bWriter; + this->poDS = poDS; + this->nIDs = nIDs; + + nFeatures = 0; + partialIndexTable = TRUE; + offsetAndLineFeaturesTable = NULL; + + const char* iKnowHowToCount[] = { "Primary", "Secondary", "Third", "Fourth", "Fifth" }; + char tmp[32]; + + poFeatureDefn = new OGRFeatureDefn( CPLSPrintf("%s_%s", + CPLGetBasename( pszFilename ) , + layerName )); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( eLayerGeomType ); + SetDescription( poFeatureDefn->GetName() ); + this->bnaFeatureType = bnaFeatureType; + + if (! bWriter ) + { + int i; + for(i=0;iAddFieldDefn( &oFieldID ); + } + else + { + sprintf(tmp, "%dth ID", i+1); + OGRFieldDefn oFieldID(tmp, OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldID ); + } + } + + if (bnaFeatureType == BNA_ELLIPSE) + { + OGRFieldDefn oFieldMajorRadius( "Major radius", OFTReal ); + poFeatureDefn->AddFieldDefn( &oFieldMajorRadius ); + + OGRFieldDefn oFieldMinorRadius( "Minor radius", OFTReal ); + poFeatureDefn->AddFieldDefn( &oFieldMinorRadius ); + } + + fpBNA = VSIFOpenL( pszFilename, "rb" ); + if( fpBNA == NULL ) + return; + } + else + { + fpBNA = NULL; + } +} + +/************************************************************************/ +/* ~OGRBNALayer() */ +/************************************************************************/ + +OGRBNALayer::~OGRBNALayer() + +{ + poFeatureDefn->Release(); + + CPLFree(offsetAndLineFeaturesTable); + + if (fpBNA) + VSIFCloseL( fpBNA ); +} + +/************************************************************************/ +/* SetFeatureIndexTable() */ +/************************************************************************/ +void OGRBNALayer::SetFeatureIndexTable(int nFeatures, OffsetAndLine* offsetAndLineFeaturesTable, int partialIndexTable) +{ + this->nFeatures = nFeatures; + this->offsetAndLineFeaturesTable = offsetAndLineFeaturesTable; + this->partialIndexTable = partialIndexTable; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRBNALayer::ResetReading() + +{ + if( fpBNA == NULL ) + return; + eof = FALSE; + failed = FALSE; + curLine = 0; + nNextFID = 0; + VSIFSeekL( fpBNA, 0, SEEK_SET ); +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRBNALayer::GetNextFeature() +{ + OGRFeature *poFeature; + BNARecord* record; + int offset, line; + + if (failed || eof || fpBNA == NULL) return NULL; + + while(1) + { + int ok = FALSE; + offset = (int) VSIFTellL(fpBNA); + line = curLine; + if (nNextFID < nFeatures) + { + VSIFSeekL( fpBNA, offsetAndLineFeaturesTable[nNextFID].offset, SEEK_SET ); + curLine = offsetAndLineFeaturesTable[nNextFID].line; + } + record = BNA_GetNextRecord(fpBNA, &ok, &curLine, TRUE, bnaFeatureType); + if (ok == FALSE) + { + BNA_FreeRecord(record); + failed = TRUE; + return NULL; + } + if (record == NULL) + { + /* end of file */ + eof = TRUE; + + /* and we have finally build the whole index table */ + partialIndexTable = FALSE; + return NULL; + } + + if (record->featureType == bnaFeatureType) + { + if (nNextFID >= nFeatures) + { + nFeatures++; + offsetAndLineFeaturesTable = + (OffsetAndLine*)CPLRealloc(offsetAndLineFeaturesTable, nFeatures * sizeof(OffsetAndLine)); + offsetAndLineFeaturesTable[nFeatures-1].offset = offset; + offsetAndLineFeaturesTable[nFeatures-1].line = line; + } + + poFeature = BuildFeatureFromBNARecord(record, nNextFID++); + + BNA_FreeRecord(record); + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + + delete poFeature; + } + else + { + BNA_FreeRecord(record); + } + } +} + + +/************************************************************************/ +/* WriteFeatureAttributes() */ +/************************************************************************/ + +void OGRBNALayer::WriteFeatureAttributes(VSILFILE* fp, OGRFeature *poFeature ) +{ + int i; + OGRFieldDefn *poFieldDefn; + int nbOutID = poDS->GetNbOutId(); + if (nbOutID < 0) + nbOutID = poFeatureDefn->GetFieldCount(); + for(i=0;iGetFieldCount()) + { + poFieldDefn = poFeatureDefn->GetFieldDefn( i ); + if( poFeature->IsFieldSet( i ) ) + { + if (poFieldDefn->GetType() == OFTReal) + { + char szBuffer[64]; + OGRFormatDouble(szBuffer, sizeof(szBuffer), + poFeature->GetFieldAsDouble(i), '.'); + VSIFPrintfL( fp, "\"%s\",", szBuffer); + } + else + { + const char *pszRaw = poFeature->GetFieldAsString( i ); + VSIFPrintfL( fp, "\"%s\",", pszRaw); + } + } + else + { + VSIFPrintfL( fp, "\"\","); + } + } + else + { + VSIFPrintfL( fp, "\"\","); + } + } +} + +/************************************************************************/ +/* WriteCoord() */ +/************************************************************************/ + +void OGRBNALayer::WriteCoord(VSILFILE* fp, double dfX, double dfY) +{ + char szBuffer[64]; + OGRFormatDouble(szBuffer, sizeof(szBuffer), dfX, '.', poDS->GetCoordinatePrecision()); + VSIFPrintfL( fp, "%s", szBuffer); + VSIFPrintfL( fp, "%s", poDS->GetCoordinateSeparator()); + OGRFormatDouble(szBuffer, sizeof(szBuffer), dfY, '.', poDS->GetCoordinatePrecision()); + VSIFPrintfL( fp, "%s", szBuffer); +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRBNALayer::ICreateFeature( OGRFeature *poFeature ) + +{ + int i,j,k,n; + OGRGeometry *poGeom = poFeature->GetGeometryRef(); + char eol[3]; + const char* partialEol = (poDS->GetMultiLine()) ? eol : poDS->GetCoordinateSeparator(); + + if (poGeom == NULL || poGeom->IsEmpty() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "OGR BNA driver cannot write features with empty geometries."); + return OGRERR_FAILURE; + } + + if (poDS->GetUseCRLF()) + { + eol[0] = 13; + eol[1] = 10; + eol[2] = 0; + } + else + { + eol[0] = 10; + eol[1] = 0; + } + + if ( ! bWriter ) + { + return OGRERR_FAILURE; + } + + if( poFeature->GetFID() == OGRNullFID ) + poFeature->SetFID( nFeatures++ ); + + VSILFILE* fp = poDS->GetOutputFP(); + int nbPairPerLine = poDS->GetNbPairPerLine(); + + switch( poGeom->getGeometryType() ) + { + case wkbPoint: + case wkbPoint25D: + { + OGRPoint* point = (OGRPoint*)poGeom; + WriteFeatureAttributes(fp, poFeature); + VSIFPrintfL( fp, "1"); + VSIFPrintfL( fp, "%s", partialEol); + WriteCoord(fp, point->getX(), point->getY()); + VSIFPrintfL( fp, "%s", eol); + break; + } + + case wkbPolygon: + case wkbPolygon25D: + { + OGRPolygon* polygon = (OGRPolygon*)poGeom; + OGRLinearRing* ring = polygon->getExteriorRing(); + if (ring == NULL) + { + return OGRERR_FAILURE; + } + + double firstX = ring->getX(0); + double firstY = ring->getY(0); + int nBNAPoints = ring->getNumPoints(); + int is_ellipse = FALSE; + + /* This code tries to detect an ellipse in a polygon geometry */ + /* This will only work presumably on ellipses already read from a BNA file */ + /* Mostly a BNA to BNA feature... */ + if (poDS->GetEllipsesAsEllipses() && + polygon->getNumInteriorRings() == 0 && + nBNAPoints == 361) + { + double oppositeX = ring->getX(180); + double oppositeY = ring->getY(180); + double quarterX = ring->getX(90); + double quarterY = ring->getY(90); + double antiquarterX = ring->getX(270); + double antiquarterY = ring->getY(270); + double center1X = 0.5*(firstX + oppositeX); + double center1Y = 0.5*(firstY + oppositeY); + double center2X = 0.5*(quarterX + antiquarterX); + double center2Y = 0.5*(quarterY + antiquarterY); + if (fabs(center1X - center2X) < 1e-5 && fabs(center1Y - center2Y) < 1e-5 && + fabs(oppositeY - firstY) < 1e-5 && + fabs(quarterX - antiquarterX) < 1e-5) + { + double major_radius = fabs(firstX - center1X); + double minor_radius = fabs(quarterY - center1Y); + is_ellipse = TRUE; + for(i=0;i<360;i++) + { + if (!(fabs(center1X + major_radius * cos(i * (M_PI / 180)) - ring->getX(i)) < 1e-5 && + fabs(center1Y + minor_radius * sin(i * (M_PI / 180)) - ring->getY(i)) < 1e-5)) + { + is_ellipse = FALSE; + break; + } + } + if ( is_ellipse == TRUE ) + { + WriteFeatureAttributes(fp, poFeature); + VSIFPrintfL( fp, "2"); + VSIFPrintfL( fp, "%s", partialEol); + WriteCoord(fp, center1X, center1Y); + VSIFPrintfL( fp, "%s", partialEol); + WriteCoord(fp, major_radius, minor_radius); + VSIFPrintfL( fp, "%s", eol); + } + } + } + + if ( is_ellipse == FALSE) + { + int nInteriorRings = polygon->getNumInteriorRings(); + for(i=0;igetInteriorRing(i)->getNumPoints() + 1; + } + if (nBNAPoints <= 3) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid geometry" ); + return OGRERR_FAILURE; + } + WriteFeatureAttributes(fp, poFeature); + VSIFPrintfL( fp, "%d", nBNAPoints); + n = ring->getNumPoints(); + int nbPair = 0; + for(i=0;igetX(i), ring->getY(i)); + nbPair++; + } + for(i=0;igetInteriorRing(i); + n = ring->getNumPoints(); + for(j=0;jgetX(j), ring->getY(j)); + nbPair++; + } + VSIFPrintfL( fp, "%s", ((nbPair % nbPairPerLine) == 0) ? partialEol : " "); + WriteCoord(fp, firstX, firstY); + nbPair++; + } + VSIFPrintfL( fp, "%s", eol); + } + break; + } + + case wkbMultiPolygon: + case wkbMultiPolygon25D: + { + OGRMultiPolygon* multipolygon = (OGRMultiPolygon*)poGeom; + int N = multipolygon->getNumGeometries(); + int nBNAPoints = 0; + double firstX = 0, firstY = 0; + for(i=0;igetGeometryRef(i); + OGRLinearRing* ring = polygon->getExteriorRing(); + if (ring == NULL) + continue; + + if (nBNAPoints) + nBNAPoints ++; + else + { + firstX = ring->getX(0); + firstY = ring->getY(0); + } + nBNAPoints += ring->getNumPoints(); + int nInteriorRings = polygon->getNumInteriorRings(); + for(j=0;jgetInteriorRing(j)->getNumPoints() + 1; + } + } + if (nBNAPoints <= 3) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid geometry" ); + return OGRERR_FAILURE; + } + WriteFeatureAttributes(fp, poFeature); + VSIFPrintfL( fp, "%d", nBNAPoints); + int nbPair = 0; + for(i=0;igetGeometryRef(i); + OGRLinearRing* ring = polygon->getExteriorRing(); + if (ring == NULL) + continue; + + n = ring->getNumPoints(); + int nInteriorRings = polygon->getNumInteriorRings(); + for(j=0;jgetX(j), ring->getY(j)); + nbPair++; + } + if (i != 0) + { + VSIFPrintfL( fp, "%s", ((nbPair % nbPairPerLine) == 0) ? partialEol : " "); + WriteCoord(fp, firstX, firstY); + nbPair++; + } + for(j=0;jgetInteriorRing(j); + n = ring->getNumPoints(); + for(k=0;kgetX(k), ring->getY(k)); + nbPair++; + } + VSIFPrintfL( fp, "%s", ((nbPair % nbPairPerLine) == 0) ? partialEol : " "); + WriteCoord(fp, firstX, firstY); + nbPair++; + } + } + VSIFPrintfL( fp, "%s", eol); + break; + } + + case wkbLineString: + case wkbLineString25D: + { + OGRLineString* line = (OGRLineString*)poGeom; + int n = line->getNumPoints(); + int i; + if (n < 2) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid geometry" ); + return OGRERR_FAILURE; + } + WriteFeatureAttributes(fp, poFeature); + VSIFPrintfL( fp, "-%d", n); + int nbPair = 0; + for(i=0;igetX(i), line->getY(i)); + nbPair++; + } + VSIFPrintfL( fp, "%s", eol); + break; + } + + default: + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported geometry type : %s.", + poGeom->getGeometryName() ); + + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + } + + return OGRERR_NONE; +} + + + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRBNALayer::CreateField( OGRFieldDefn *poField, + CPL_UNUSED int bApproxOK ) +{ + if( !bWriter || nFeatures != 0) + return OGRERR_FAILURE; + + poFeatureDefn->AddFieldDefn( poField ); + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* BuildFeatureFromBNARecord() */ +/************************************************************************/ +OGRFeature * OGRBNALayer::BuildFeatureFromBNARecord (BNARecord* record, long fid) +{ + OGRFeature *poFeature; + int i; + + poFeature = new OGRFeature( poFeatureDefn ); + for(i=0;iSetField( i, record->ids[i] ? record->ids[i] : ""); + } + poFeature->SetFID( fid ); + if (bnaFeatureType == BNA_POINT) + { + poFeature->SetGeometryDirectly( new OGRPoint( record->tabCoords[0][0], record->tabCoords[0][1] ) ); + } + else if (bnaFeatureType == BNA_POLYLINE) + { + OGRLineString* lineString = new OGRLineString (); + lineString->setCoordinateDimension(2); + lineString->setNumPoints(record->nCoords); + for(i=0;inCoords;i++) + { + lineString->setPoint(i, record->tabCoords[i][0], record->tabCoords[i][1] ); + } + poFeature->SetGeometryDirectly(lineString); + } + else if (bnaFeatureType == BNA_POLYGON) + { + double firstX = record->tabCoords[0][0]; + double firstY = record->tabCoords[0][1]; + int isFirstPolygon = 1; + double secondaryFirstX = 0, secondaryFirstY = 0; + + OGRLinearRing* ring = new OGRLinearRing (); + ring->setCoordinateDimension(2); + ring->addPoint(record->tabCoords[0][0], record->tabCoords[0][1] ); + + /* record->nCoords is really a safe upper bound */ + int nbPolygons = 0; + OGRPolygon** tabPolygons = + (OGRPolygon**)CPLMalloc(record->nCoords * sizeof(OGRPolygon*)); + + for(i=1;inCoords;i++) + { + ring->addPoint(record->tabCoords[i][0], record->tabCoords[i][1] ); + if (isFirstPolygon == 1 && + record->tabCoords[i][0] == firstX && + record->tabCoords[i][1] == firstY) + { + OGRPolygon* polygon = new OGRPolygon (); + polygon->addRingDirectly(ring); + tabPolygons[nbPolygons] = polygon; + nbPolygons++; + + if (i == record->nCoords - 1) + { + break; + } + + isFirstPolygon = 0; + + i ++; + secondaryFirstX = record->tabCoords[i][0]; + secondaryFirstY = record->tabCoords[i][1]; + ring = new OGRLinearRing (); + ring->setCoordinateDimension(2); + ring->addPoint(record->tabCoords[i][0], record->tabCoords[i][1] ); + } + else if (isFirstPolygon == 0 && + record->tabCoords[i][0] == secondaryFirstX && + record->tabCoords[i][1] == secondaryFirstY) + { + + OGRPolygon* polygon = new OGRPolygon (); + polygon->addRingDirectly(ring); + tabPolygons[nbPolygons] = polygon; + nbPolygons++; + + if (i < record->nCoords - 1) + { + /* After the closing of a subpolygon, the first coordinates of the first polygon */ + /* should be recalled... in theory */ + if (record->tabCoords[i+1][0] == firstX && record->tabCoords[i+1][1] == firstY) + { + if (i + 1 == record->nCoords - 1) + break; + i ++; + } + else + { +#if 0 + CPLError(CE_Warning, CPLE_AppDefined, + "Geometry of polygon of fid %d starting at line %d is not strictly conformant. " + "Trying to go on...\n", + fid, + offsetAndLineFeaturesTable[fid].line + 1); +#endif + } + + i ++; + secondaryFirstX = record->tabCoords[i][0]; + secondaryFirstY = record->tabCoords[i][1]; + ring = new OGRLinearRing (); + ring->setCoordinateDimension(2); + ring->addPoint(record->tabCoords[i][0], record->tabCoords[i][1] ); + } + else + { +#if 0 + CPLError(CE_Warning, CPLE_AppDefined, + "Geometry of polygon of fid %d starting at line %d is not strictly conformant. Trying to go on...\n", + fid, + offsetAndLineFeaturesTable[fid].line + 1); +#endif + } + } + } + if (i == record->nCoords) + { + /* Let's be a bit tolerant abount non closing polygons */ + if (isFirstPolygon) + { + ring->addPoint(record->tabCoords[0][0], record->tabCoords[0][1] ); + + OGRPolygon* polygon = new OGRPolygon (); + polygon->addRingDirectly(ring); + tabPolygons[nbPolygons] = polygon; + nbPolygons++; + } + } + + if (nbPolygons == 1) + { + /* Special optimization here : we directly put the polygon into the multipolygon. */ + /* This should save quite a few useless copies */ + OGRMultiPolygon* multipolygon = new OGRMultiPolygon(); + multipolygon->addGeometryDirectly(tabPolygons[0]); + poFeature->SetGeometryDirectly(multipolygon); + } + else + { + int isValidGeometry; + poFeature->SetGeometryDirectly( + OGRGeometryFactory::organizePolygons((OGRGeometry**)tabPolygons, nbPolygons, &isValidGeometry, NULL)); + + if (!isValidGeometry) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Geometry of polygon of fid %ld starting at line %d cannot be translated to Simple Geometry. " + "All polygons will be contained in a multipolygon.\n", + fid, + offsetAndLineFeaturesTable[fid].line + 1); + } + } + + CPLFree(tabPolygons); + } + else + { + /* Circle or ellipses are not part of the OGR Simple Geometry, so we discretize them + into polygons by 1 degree step */ + OGRPolygon* polygon = new OGRPolygon (); + OGRLinearRing* ring = new OGRLinearRing (); + ring->setCoordinateDimension(2); + double center_x = record->tabCoords[0][0]; + double center_y = record->tabCoords[0][1]; + double major_radius = record->tabCoords[1][0]; + double minor_radius = record->tabCoords[1][1]; + if (minor_radius == 0) + minor_radius = major_radius; + for(i=0;i<360;i++) + { + ring->addPoint(center_x + major_radius * cos(i * (M_PI / 180)), + center_y + minor_radius * sin(i * (M_PI / 180)) ); + } + ring->addPoint(center_x + major_radius, center_y); + polygon->addRingDirectly ( ring ); + poFeature->SetGeometryDirectly(polygon); + + poFeature->SetField( nIDs, major_radius); + poFeature->SetField( nIDs+1, minor_radius); + } + + return poFeature; +} + + +/************************************************************************/ +/* FastParseUntil() */ +/************************************************************************/ +void OGRBNALayer::FastParseUntil ( int interestFID) +{ + if (partialIndexTable) + { + ResetReading(); + + BNARecord* record; + + if (nFeatures > 0) + { + VSIFSeekL( fpBNA, offsetAndLineFeaturesTable[nFeatures-1].offset, SEEK_SET ); + curLine = offsetAndLineFeaturesTable[nFeatures-1].line; + + /* Just skip the last read one */ + int ok = FALSE; + record = BNA_GetNextRecord(fpBNA, &ok, &curLine, TRUE, BNA_READ_NONE); + BNA_FreeRecord(record); + } + + while(1) + { + int ok = FALSE; + int offset = (int) VSIFTellL(fpBNA); + int line = curLine; + record = BNA_GetNextRecord(fpBNA, &ok, &curLine, TRUE, BNA_READ_NONE); + if (ok == FALSE) + { + failed = TRUE; + return; + } + if (record == NULL) + { + /* end of file */ + eof = TRUE; + + /* and we have finally build the whole index table */ + partialIndexTable = FALSE; + return; + } + + if (record->featureType == bnaFeatureType) + { + nFeatures++; + offsetAndLineFeaturesTable = + (OffsetAndLine*)CPLRealloc(offsetAndLineFeaturesTable, nFeatures * sizeof(OffsetAndLine)); + offsetAndLineFeaturesTable[nFeatures-1].offset = offset; + offsetAndLineFeaturesTable[nFeatures-1].line = line; + + BNA_FreeRecord(record); + + if (nFeatures - 1 == interestFID) + return; + } + else + { + BNA_FreeRecord(record); + } + } + } +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature * OGRBNALayer::GetFeature( GIntBig nFID ) +{ + OGRFeature *poFeature; + BNARecord* record; + int ok; + + if (nFID < 0 || (GIntBig)(int)nFID != nFID) + return NULL; + + FastParseUntil((int)nFID); + + if (nFID >= nFeatures) + return NULL; + + VSIFSeekL( fpBNA, offsetAndLineFeaturesTable[nFID].offset, SEEK_SET ); + curLine = offsetAndLineFeaturesTable[nFID].line; + record = BNA_GetNextRecord(fpBNA, &ok, &curLine, TRUE, bnaFeatureType); + + poFeature = BuildFeatureFromBNARecord(record, (int)nFID); + + BNA_FreeRecord(record); + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRBNALayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCSequentialWrite) ) + return bWriter; + else if( EQUAL(pszCap,OLCCreateField) ) + return bWriter && nFeatures == 0; + else + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnaparser.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnaparser.cpp new file mode 100644 index 000000000..17b7cc001 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnaparser.cpp @@ -0,0 +1,644 @@ +/****************************************************************************** + * $Id: ogrbnaparser.cpp 27721 2014-09-22 12:42:28Z goatbar $ + * + * Project: BNA Parser + * Purpose: Parse a BNA record + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrbnaparser.h" + +#include "cpl_conv.h" +#include "cpl_string.h" + +void BNA_FreeRecord(BNARecord* record) +{ + if (record) + { + int i; + for(i=0;iids[i]) VSIFree(record->ids[i]); + record->ids[i] = NULL; + } + CPLFree(record->tabCoords); + record->tabCoords = NULL; + CPLFree(record); + } +} + +const char* BNA_FeatureTypeToStr(BNAFeatureType featureType) +{ + switch (featureType) + { + case BNA_POINT: + return "point"; + case BNA_POLYGON: + return "polygon"; + case BNA_POLYLINE: + return "polyline"; + case BNA_ELLIPSE: + return "ellipse"; + default: + return "unknown"; + } +} + +void BNA_Display(BNARecord* record) +{ + int i; + fprintf(stderr, "\"%s\", \"%s\", \"%s\", %s\n", + record->ids[0] ? record->ids[0] : "", + record->ids[1] ? record->ids[1] : "", + record->ids[2] ? record->ids[2] : "", + BNA_FeatureTypeToStr(record->featureType)); + for(i=0;inCoords;i++) + fprintf(stderr, "%f, %f\n", record->tabCoords[i][0], record->tabCoords[i][1]); +} + +/* +For a description of the format, see http://www.softwright.com/faq/support/boundary_file_bna_format.html +and http://64.145.236.125/forum/topic.asp?topic_id=1930&forum_id=1&Topic_Title=how+to+edit+*.bna+files%3F&forum_title=Surfer+Support&M=False +*/ + +/* The following lines are a valid BNA file (for this parser) : +"PID1","SID1",1 +-0,0 +"PID2","SID2",-2, -1e-5,-1e2 ,-2,-2 +"PID3""a","SID3",4 +5,5 +6,5 +6,6 +5,5 +"PID4","SID4",2 +10,10 +5,4 + +"PID4","SID4",2 + +10,10 +5,4 +*/ + +/* The following lines are a valid BNA file (for this parser) : + (small extract from ftp://ftp.ciesin.org/pub/census/usa/tiger/ct/bnablk/b09001.zip) +"090010000.00099A","099A","090010000.00099A","blk",21 +-73.049337, 41.125000 -73.093761, 41.157780 -73.107900, 41.168300 +-73.106878, 41.166459 -73.095800, 41.146500 -73.114553, 41.146331 +-73.138922, 41.147993 -73.166200, 41.154200 -73.198497, 41.085988 +-73.232241, 41.029986 -73.229548, 41.030507 -73.183922, 41.041955 +-73.178678, 41.043239 -73.177951, 41.043417 -73.147888, 41.050781 +-73.118658, 41.057942 -73.052399, 41.074174 -73.024976, 41.080892 +-73.000000, 41.087010 -73.035597, 41.114420 -73.049337, 41.125000 +*/ + +/* We are (and must be) a bit tolerant : BNA files format has several variations */ +/* and most don't follow strictly the 'specification' */ +/* Extra spaces, tabulations or line feed are accepted and ignored */ +/* We allow one line format and several line format in the same file */ +/* We allow from NB_MIN_BNA_IDS to NB_MAX_BNA_IDS ids */ +/* We allow that couples of coordinates on the same line may be separated only by spaces */ +/* (instead of being separated by a comma) */ + +#define STRING_NOT_TERMINATED "string not terminated when end of line occured" +#define MISSING_FIELDS "missing fields" +#define BAD_INTEGER_NUMBER_FORMAT "bad integer number format" +#define BAD_FLOAT_NUMBER_FORMAT "bad float number format" +#define PRIMARY_ID_MANDATORY "primary ID can't be empty or missing" +#define STRING_EXPECTED "string expected" +#define NUMBER_EXPECTED "number expected" +#define INTEGER_NUMBER_EXPECTED "integer number expected" +#define FLOAT_NUMBER_EXPECTED "float number expected" +#define INVALID_GEOMETRY_TYPE "invalid geometry type" +#define TOO_LONG_ID "too long id (> 256 characters)" +#define MAX_BNA_IDS_REACHED "maximum number of IDs reached" +#define NOT_ENOUGH_MEMORY "not enough memory for request number of coordinates" +#define LINE_TOO_LONG "line too long" + +#define TMP_BUFFER_SIZE 256 +#define LINE_BUFFER_SIZE 1024 + +enum +{ + BNA_LINE_OK, + BNA_LINE_EOF, + BNA_LINE_TOO_LONG +}; + +static int BNA_GetLine(char szLineBuffer[LINE_BUFFER_SIZE+1], VSILFILE* f) +{ + char* ptrCurLine = szLineBuffer; + int nRead = VSIFReadL(szLineBuffer, 1, LINE_BUFFER_SIZE, f); + szLineBuffer[nRead] = 0; + if (nRead == 0) + { + /* EOF */ + return BNA_LINE_EOF; + } + + int bFoundEOL = FALSE; + while (*ptrCurLine) + { + if (*ptrCurLine == 0x0d || *ptrCurLine == 0x0a) + { + bFoundEOL = TRUE; + break; + } + ptrCurLine ++; + } + if (!bFoundEOL) + { + if (nRead < LINE_BUFFER_SIZE) + return BNA_LINE_OK; + else + return BNA_LINE_TOO_LONG; + } + + if (*ptrCurLine == 0x0d) + { + if (ptrCurLine == szLineBuffer + LINE_BUFFER_SIZE - 1) + { + char c; + nRead = VSIFReadL(&c, 1, 1, f); + if (nRead == 1) + { + if (c == 0x0a) + { + /* Do nothing */ + } + else + { + VSIFSeekL(f, VSIFTellL(f) - 1, SEEK_SET); + } + } + } + else if (ptrCurLine[1] == 0x0a) + { + VSIFSeekL(f, VSIFTellL(f) + ptrCurLine + 2 - (szLineBuffer + nRead), SEEK_SET); + } + else + { + VSIFSeekL(f, VSIFTellL(f) + ptrCurLine + 1 - (szLineBuffer + nRead), SEEK_SET); + } + } + else /* *ptrCurLine == 0x0a */ + { + VSIFSeekL(f, VSIFTellL(f) + ptrCurLine + 1 - (szLineBuffer + nRead), SEEK_SET); + } + *ptrCurLine = 0; + + return BNA_LINE_OK; +} + + +BNARecord* BNA_GetNextRecord(VSILFILE* f, + int* ok, + int* curLine, + int verbose, + BNAFeatureType interestFeatureType) +{ + BNARecord* record; + char c; + int inQuotes = FALSE; + int numField = 0; + char* ptrBeginningOfNumber = NULL; + int exponentFound = 0; + int exponentSignFound = 0; + int dotFound = 0; + int numChar = 0; + const char* detailedErrorMsg = NULL; + BNAFeatureType currentFeatureType = BNA_UNKNOWN; + int nbExtraId = 0; + char tmpBuffer[NB_MAX_BNA_IDS][TMP_BUFFER_SIZE+1]; + int tmpBufferLength[NB_MAX_BNA_IDS] = {0, 0, 0}; + char szLineBuffer[LINE_BUFFER_SIZE + 1]; + + record = (BNARecord*)CPLMalloc(sizeof(BNARecord)); + memset(record, 0, sizeof(BNARecord)); + + while (TRUE) + { + numChar = 0; + (*curLine)++; + + int retGetLine = BNA_GetLine(szLineBuffer, f); + if (retGetLine == BNA_LINE_TOO_LONG) + { + detailedErrorMsg = LINE_TOO_LONG; + goto error; + } + else if (retGetLine == BNA_LINE_EOF) + { + break; + } + + char* ptrCurLine = szLineBuffer; + const char* ptrBeginLine = szLineBuffer; + + if (*ptrCurLine == 0) + continue; + + while(1) + { + numChar = ptrCurLine - ptrBeginLine; + c = *ptrCurLine; + if (c == 0) c = 10; + if (inQuotes) + { + if (c == 10) + { + detailedErrorMsg = STRING_NOT_TERMINATED; + goto error; + } + else if (c == '"' && ptrCurLine[1] == '"') + { + if (tmpBufferLength[numField] == TMP_BUFFER_SIZE) + { + detailedErrorMsg = TOO_LONG_ID; + goto error; + } + tmpBuffer[numField][tmpBufferLength[numField]++] = c; + + ptrCurLine++; + } + else if (c == '"') + { + inQuotes = FALSE; + } + else + { + if (tmpBufferLength[numField] == TMP_BUFFER_SIZE) + { + detailedErrorMsg = TOO_LONG_ID; + goto error; + } + tmpBuffer[numField][tmpBufferLength[numField]++] = c; + } + } + else if (c == ' ' || c == '\t') + { + if (numField > NB_MIN_BNA_IDS + nbExtraId && ptrBeginningOfNumber != NULL) + { + do + { + ptrCurLine++; + numChar = ptrCurLine - ptrBeginLine; + c = *ptrCurLine; + if (!(c == ' ' || c == '\t')) + break; + } while(c); + if (c == 0) c = 10; + + if (interestFeatureType == BNA_READ_ALL || + interestFeatureType == currentFeatureType) + { + char* pszComma = strchr(ptrBeginningOfNumber, ','); + if (pszComma) + *pszComma = '\0'; + record->tabCoords[(numField - nbExtraId - NB_MIN_BNA_IDS - 1) / 2] + [1 - ((numField - nbExtraId) % 2)] = + CPLAtof(ptrBeginningOfNumber); + if (pszComma) + *pszComma = ','; + } + if (numField == NB_MIN_BNA_IDS + 1 + nbExtraId + 2 * record->nCoords - 1) + { + if (c != 10) + { + if (verbose) + { + CPLError(CE_Warning, CPLE_AppDefined, + "At line %d, at char %d, extra data will be ignored!\n", + *curLine, numChar+1); + } + } + *ok = 1; + return record; + } + + ptrBeginningOfNumber = NULL; + exponentFound = 0; + exponentSignFound = 0; + dotFound = 0; + numField++; + + if (c == 10) + break; + + if (c != ',') + { + /* don't increment ptrCurLine */ + continue; + } + } + else + { + /* ignore */ + } + } + else if (c == 10 || c == ',') + { + /* Eat a comma placed at end of line */ + if (c == ',') + { + const char* ptr = ptrCurLine+1; + while(*ptr) + { + if (*ptr != ' ' && *ptr != '\t') + break; + ptr++; + } + if (*ptr == 0) + { + c = 10; + } + } + + if (numField == 0) + { + /* Maybe not so mandatory.. Atlas MapMaker(TM) exports BNA files with empty primaryID */ + /* + if (record->primaryID == NULL || *(record->primaryID) == 0) + { + detailedErrorMsg = PRIMARY_ID_MANDATORY; + goto error; + } + */ + } + else if (numField == NB_MIN_BNA_IDS + nbExtraId) + { + int nCoords; + if (ptrBeginningOfNumber == NULL) + { + detailedErrorMsg = INTEGER_NUMBER_EXPECTED; + goto error; + } + nCoords = atoi(ptrBeginningOfNumber); + if (nCoords == 0 || nCoords == -1) + { + detailedErrorMsg = INVALID_GEOMETRY_TYPE; + goto error; + } + else if (nCoords == 1) + { + currentFeatureType = record->featureType = BNA_POINT; + record->nCoords = 1; + } + else if (nCoords == 2) + { + currentFeatureType = record->featureType = BNA_ELLIPSE; + record->nCoords = 2; + } + else if (nCoords > 0) + { + currentFeatureType = record->featureType = BNA_POLYGON; + record->nCoords = nCoords; + } + else + { + currentFeatureType = record->featureType = BNA_POLYLINE; + record->nCoords = -nCoords; + } + + record->nIDs = NB_MIN_BNA_IDS + nbExtraId; + + if (interestFeatureType == BNA_READ_ALL || + interestFeatureType == currentFeatureType) + { + int i; + for(i=0;iids[i] = (char*)CPLMalloc(tmpBufferLength[i] + 1); + tmpBuffer[i][tmpBufferLength[i]] = 0; + memcpy(record->ids[i], tmpBuffer[i], tmpBufferLength[i] + 1); + } + } + + record->tabCoords = + (double(*)[2])VSIMalloc(record->nCoords * 2 * sizeof(double)); + if (record->tabCoords == NULL) + { + detailedErrorMsg = NOT_ENOUGH_MEMORY; + goto error; + } + } + } + else if (numField > NB_MIN_BNA_IDS + nbExtraId) + { + if (ptrBeginningOfNumber == NULL) + { + detailedErrorMsg = FLOAT_NUMBER_EXPECTED; + goto error; + } + if (interestFeatureType == BNA_READ_ALL || + interestFeatureType == currentFeatureType) + { + char* pszComma = strchr(ptrBeginningOfNumber, ','); + if (pszComma) + *pszComma = '\0'; + record->tabCoords[(numField - nbExtraId - NB_MIN_BNA_IDS - 1) / 2] + [1 - ((numField - nbExtraId) % 2)] = + CPLAtof(ptrBeginningOfNumber); + if (pszComma) + *pszComma = ','; + } + if (numField == NB_MIN_BNA_IDS + 1 + nbExtraId + 2 * record->nCoords - 1) + { + if (c != 10) + { + if (verbose) + { + CPLError(CE_Warning, CPLE_AppDefined, + "At line %d, at char %d, extra data will be ignored!\n", + *curLine, numChar+1); + } + } + *ok = 1; + return record; + } + } + + ptrBeginningOfNumber = NULL; + exponentFound = 0; + exponentSignFound = 0; + dotFound = 0; + numField++; + + if (c == 10) + break; + } + else if (c == '"') + { + if (numField < NB_MIN_BNA_IDS) + { + inQuotes = TRUE; + } + else if (numField >= NB_MIN_BNA_IDS && currentFeatureType == BNA_UNKNOWN) + { + if (ptrBeginningOfNumber == NULL) + { + if (nbExtraId == NB_MAX_BNA_IDS - NB_MIN_BNA_IDS) + { + detailedErrorMsg = MAX_BNA_IDS_REACHED; + goto error; + } + nbExtraId ++; + inQuotes = TRUE; + } + else + { + detailedErrorMsg = BAD_INTEGER_NUMBER_FORMAT; + goto error; + } + } + else + { + detailedErrorMsg = NUMBER_EXPECTED; + goto error; + } + } + else + { + if (numField < NB_MIN_BNA_IDS || (numField == NB_MIN_BNA_IDS + nbExtraId - 1)) + { + detailedErrorMsg = STRING_EXPECTED; + goto error; + } + else if (numField == NB_MIN_BNA_IDS + nbExtraId) + { + if (c >= '0' && c <= '9') + { + } + else if (c == '+' || c == '-') + { + if (ptrBeginningOfNumber != NULL) + { + detailedErrorMsg = BAD_INTEGER_NUMBER_FORMAT; + goto error; + } + } + else + { + detailedErrorMsg = BAD_INTEGER_NUMBER_FORMAT; + goto error; + } + if (ptrBeginningOfNumber == NULL) + ptrBeginningOfNumber = ptrCurLine; + } + else + { + if (c >= '0' && c <= '9') + { + } + else if (c == '.') + { + if (dotFound || exponentFound) + { + detailedErrorMsg = BAD_FLOAT_NUMBER_FORMAT; + goto error; + } + dotFound = 1; + } + else if (c == '+' || c == '-') + { + if (ptrBeginningOfNumber == NULL) + { + } + else if (exponentFound) + { + if (exponentSignFound == 0 && ptrCurLine > ptrBeginLine && + (ptrCurLine[-1] == 'e' || ptrCurLine[-1] == 'E' || + ptrCurLine[-1] == 'd' || ptrCurLine[-1] == 'D')) + { + exponentSignFound = 1; + } + else + { + detailedErrorMsg = BAD_FLOAT_NUMBER_FORMAT; + goto error; + } + } + else + { + detailedErrorMsg = BAD_FLOAT_NUMBER_FORMAT; + goto error; + } + } + else if (c == 'e' || c == 'E' || c == 'd' || c == 'D') + { + if (ptrBeginningOfNumber == NULL || + !(ptrCurLine[-1] >= '0' && ptrCurLine[-1] <= '9') || + exponentFound == 1) + { + detailedErrorMsg = BAD_FLOAT_NUMBER_FORMAT; + goto error; + } + exponentFound = 1; + } + else + { + detailedErrorMsg = BAD_FLOAT_NUMBER_FORMAT; + goto error; + } + if (ptrBeginningOfNumber == NULL) + ptrBeginningOfNumber = ptrCurLine; + } + } + ptrCurLine++; + } + } + + if (numField == 0) + { + /* End of file */ + *ok = 1; + BNA_FreeRecord(record); + return NULL; + } + else + { + detailedErrorMsg = MISSING_FIELDS; + goto error; + } +error: + if (verbose) + { + if (detailedErrorMsg) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Parsing failed at line %d, at char %d : %s!\n", + *curLine, numChar+1, detailedErrorMsg); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Parsing failed at line %d, at char %d!\n", + *curLine, numChar+1); + } + } + BNA_FreeRecord(record); + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnaparser.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnaparser.h new file mode 100644 index 000000000..b7c63ad76 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/bna/ogrbnaparser.h @@ -0,0 +1,93 @@ +/****************************************************************************** + * $Id: ogrbnaparser.h 27721 2014-09-22 12:42:28Z goatbar $ + * + * Project: BNA Parser header + * Purpose: Definition of structures, enums and functions of BNA parser + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2007, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + +#ifndef _OGR_BNA_PARSER_INCLUDED +#define _OGR_BNA_PARSER_INCLUDED + +#include "cpl_vsi.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef enum +{ + BNA_UNKNOWN = -1, + BNA_POINT, + BNA_POLYGON, + BNA_POLYLINE, + BNA_ELLIPSE, +/* The following values are not geometries, but flags for BNA_GetNextRecord */ + BNA_READ_ALL, + BNA_READ_NONE, +} BNAFeatureType; + +/* Standard BNA files should have 2 ids */ +/* But some BNA files have a third ID, for example those produced by Atlas GIS(TM) 4.0... */ +/* And BNA files found at ftp://ftp.ciesin.org/pub/census/usa/tiger/ have up to 4 ! */ +#define NB_MIN_BNA_IDS 2 +#define NB_MAX_BNA_IDS 4 + +typedef struct +{ + char* ids[NB_MAX_BNA_IDS]; + int nIDs; + BNAFeatureType featureType; + int nCoords; + double (*tabCoords)[2]; +} BNARecord; + +/** Get the next BNA record in the file + @param f open BNA files (VSI Large API handle) + @param ok (out) set to TRUE if reading was OK (or EOF detected) + @param curLine (in/out) incremenet number line + @param verbose if TRUE, errors will be reported + @param interestFeatureType if BNA_READ_ALL, any BNA feature will be parsed and read in details. + if BNA_READ_NONE, no BNA feature will be parsed and read in details. + Otherwise, if the read feature type doesn't match with the interestFeatureType, + the record will be parsed as fast as possible (tabCoords not allocated) + @return the parsed BNA record or NULL if EOF or error +*/ +BNARecord* BNA_GetNextRecord(VSILFILE* f, int* ok, int* curLine, int verbose, BNAFeatureType interestFeatureType); + +/** Free a record obtained by BNA_GetNextRecord */ +void BNA_FreeRecord(BNARecord* record); + +const char* BNA_FeatureTypeToStr(BNAFeatureType featureType); + +/** For debug purpose */ +void BNA_Display(BNARecord* record); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/GNUmakefile new file mode 100644 index 000000000..cd4507a0d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrcartodbdriver.o ogrcartodbdatasource.o ogrcartodblayer.o ogrcartodbtablelayer.o ogrcartodbresultlayer.o + +CPPFLAGS := $(JSON_INCLUDE) -I.. -I../.. -I../pgdump $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_cartodb.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/drv_cartodb.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/drv_cartodb.html new file mode 100644 index 000000000..b5014251a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/drv_cartodb.html @@ -0,0 +1,158 @@ + + +CartoDB + + + + +

    CartoDB

    + +(GDAL/OGR >= 1.11)

    + +This driver can connect to the services implementing the CartoDB API. +GDAL/OGR must be built with Curl support in order for the +CartoDB driver to be compiled.

    + +The driver supports read and write operations.

    + +

    Dataset name syntax

    + +The minimal syntax to open a CartoDB datasource is :
    CartoDB:[connection_name]

    + +For single-user accounts, connection name is the account name. +For multi-user accounts, connection_name must be the user name, not the account name. + +Additionnal optional parameters can be specified after the ':' sign. +Currently the following one is supported :

    + +

      +
    • tables=table_name1[,table_name2]*: A list of table names. +This is necessary when you need to access to public tables for example. +
    + +If several parameters are specified, they must be separated by a space.

    + +

    Configuration options

    + +The following configuration options are available : +
      +
    • CARTODB_API_URL: defaults to https://[account_name].cartodb.com/api/v2/sql. +Can be used to point to another server.
    • +
    • CARTODB_HTTPS: can be set to NO to use http:// protocol instead of +https:// (only if CARTODB_API_URL is not defined).
    • +
    • CARTODB_API_KEY: see following paragraph.
    • +
    + + +

    Authentication

    + +Most operations, in particular write operations, require an authenticated +access. The only exception is read-only access to public tables.

    + +Authenticated access is obtained by specifying the API key given in the +management interface of the CartoDB service. It is specified with the +CARTODB_API_KEY configuration option.

    + +

    Geometry

    + +The OGR driver will report as many geometry fields as available in the +layer (except the 'the_geom_webmercator' field), following RFC 41. + +

    Filtering

    + +The driver will forward any spatial filter set with SetSpatialFilter() to +the server. It also makes the same for attribute +filters set with SetAttributeFilter().

    + +

    Paging

    + +Features are retrieved from the server by chunks of 500 by default. +This number can be altered with the CARTODB_PAGE_SIZE +configuration option.

    + +

    Write support

    + +Table creation and deletion is possible.

    + +Write support is only enabled when the datasource is opened in update mode.

    + +The mapping between the operations of the CartoDB service and the OGR concepts is the following : +

      +
    • OGRFeature::CreateFeature() <==> INSERT operation
    • +
    • OGRFeature::SetFeature() <==> UPDATE operation
    • +
    • OGRFeature::DeleteFeature() <==> DELETE operation
    • +
    • OGRDataSource::CreateLayer() <==> CREATE TABLE operation
    • +
    • OGRDataSource::DeleteLayer() <==> DROP TABLE operation
    • +
    + +When inserting a new feature with CreateFeature(), and if the command is successful, OGR will fetch the +returned rowid and use it as the OGR FID.

    + +The above operations are by default issued to the server synchronously with the OGR API call. This however +can cause performance penalties when issuing a lot of commands due to many client/server exchanges.

    + +So, on a newly created layer, the INSERT of CreateFeature() operations are grouped together +in chunks until they reach 15 MB (can be changed with the CARTODB_MAX_CHUNK_SIZE +configuration option, with a value in MB), at which point they are transferred +to the server. By setting CARTODB_MAX_CHUNK_SIZE to 0, immediate transfer occurs.

    + +

    SQL

    + +SQL commands provided to the OGRDataSource::ExecuteSQL() call are executed on the server side, unless the OGRSQL +dialect is specified. You can use the full power of PostgreSQL + PostGIS SQL +capabilities.

    + +

    Open options

    + +

    Starting with GDAL 2.0, the following open options are available:

    +
      +
    • BATCH_INSERT=YES/NO: Whether to group feature insertions in a batch. +Defaults to YES. Only apply in creation or update mode.
    • +
    + +

    Layer creation options

    + +

    The following layer creation options are available:

    +
      +
    • OVERWRITE=YES/NO: Whether to overwrite an existing table with the +layer name to be created. Defaults to NO.
    • + +
    • GEOMETRY_NULLABLE=YES/NO: Whether the values of the geometry column +can be NULL. Defaults to YES.
    • + +
    • CARTODBFY=YES/NO: Whether the created layer should be "CartoDBifi'ed" + (i.e. registered in dashboard). Defaults to YES.
    • + +
    • LAUNDER=YES/NO: This may be "YES" to force new fields created on this +layer to have their field names "laundered" into a form more compatible with +PostgreSQL. This converts to lower case and converts some special characters +like "-" and "#" to "_". If "NO" exact names are preserved. +The default value is "YES". If enabled the table (layer) name will also be laundered.
    • + +
    + + +

    Examples

    + +
  • +Acceccing data from a public table: +
    +ogrinfo -ro "CartoDB:gdalautotest2 tables=tm_world_borders_simpl_0_3"
    +
    +

    + +

  • +Creating and populating a table from a shapefile: +
    +ogr2ogr --config CARTODB_API_KEY abcdefghijklmnopqrstuvw -f CartoDB "CartoDB:myaccount" myshapefile.shp
    +
    +

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/makefile.vc new file mode 100644 index 000000000..0a7c1cdc0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/makefile.vc @@ -0,0 +1,16 @@ + +OBJ = ogrcartodbdriver.obj ogrcartodbdatasource.obj ogrcartodblayer.obj ogrcartodbtablelayer.obj ogrcartodbresultlayer.obj + +EXTRAFLAGS = -I.. -I..\.. -I..\geojson\libjson -I..\pgdump + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogr_cartodb.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogr_cartodb.h new file mode 100644 index 000000000..1ca38b2c6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogr_cartodb.h @@ -0,0 +1,264 @@ +/****************************************************************************** + * $Id: ogr_cartodb.h 29003 2015-04-25 08:26:30Z rouault $ + * + * Project: CARTODB Translator + * Purpose: Definition of classes for OGR CartoDB driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_CARTODB_H_INCLUDED +#define _OGR_CARTODB_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_http.h" + +#include +#include + +json_object* OGRCARTODBGetSingleRow(json_object* poObj); +CPLString OGRCARTODBEscapeIdentifier(const char* pszStr); +CPLString OGRCARTODBEscapeLiteral(const char* pszStr); + +/************************************************************************/ +/* OGRCartoDBGeomFieldDefn */ +/************************************************************************/ + +class OGRCartoDBGeomFieldDefn: public OGRGeomFieldDefn +{ + public: + int nSRID; + + OGRCartoDBGeomFieldDefn(const char* pszName, OGRwkbGeometryType eType) : + OGRGeomFieldDefn(pszName, eType), nSRID(0) + { + } +}; + +/************************************************************************/ +/* OGRCARTODBLayer */ +/************************************************************************/ +class OGRCARTODBDataSource; + +class OGRCARTODBLayer : public OGRLayer +{ +protected: + OGRCARTODBDataSource* poDS; + + OGRFeatureDefn *poFeatureDefn; + OGRSpatialReference *poSRS; + CPLString osBaseSQL; + CPLString osFIDColName; + + int bEOF; + int nFetchedObjects; + int iNextInFetchedObjects; + GIntBig iNext; + json_object *poCachedObj; + + virtual OGRFeature *GetNextRawFeature(); + OGRFeature *BuildFeature(json_object* poRowObj); + + void EstablishLayerDefn(const char* pszLayerName, + json_object* poObjIn); + OGRSpatialReference *GetSRS(const char* pszGeomCol, int *pnSRID); + virtual CPLString GetSRS_SQL(const char* pszGeomCol) = 0; + + public: + OGRCARTODBLayer(OGRCARTODBDataSource* poDS); + ~OGRCARTODBLayer(); + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn(); + virtual OGRFeatureDefn * GetLayerDefnInternal(json_object* poObjIn) = 0; + virtual json_object* FetchNewFeatures(GIntBig iNext); + + virtual const char* GetFIDColumn() { return osFIDColName.c_str(); } + + virtual int TestCapability( const char * ); + + int GetFeaturesToFetch() { return atoi(CPLGetConfigOption("CARTODB_PAGE_SIZE", "500")); } +}; + + +/************************************************************************/ +/* OGRCARTODBTableLayer */ +/************************************************************************/ + +class OGRCARTODBTableLayer : public OGRCARTODBLayer +{ + CPLString osName; + CPLString osQuery; + CPLString osWHERE; + CPLString osSELECTWithoutWHERE; + + int bLaunderColumnNames; + + int bInDeferedInsert; + CPLString osDeferedInsertSQL; + GIntBig nNextFID; + + int bDeferedCreation; + int bCartoDBify; + int nMaxChunkSize; + + void BuildWhere(); + + virtual CPLString GetSRS_SQL(const char* pszGeomCol); + + public: + OGRCARTODBTableLayer(OGRCARTODBDataSource* poDS, const char* pszName); + ~OGRCARTODBTableLayer(); + + virtual const char* GetName() { return osName.c_str(); } + virtual OGRFeatureDefn * GetLayerDefnInternal(json_object* poObjIn); + virtual json_object* FetchNewFeatures(GIntBig iNext); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual int TestCapability( const char * ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + + virtual OGRFeature *GetNextRawFeature(); + + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + virtual void SetSpatialFilter( OGRGeometry *poGeom ) { SetSpatialFilter(0, poGeom); } + virtual void SetSpatialFilter( int iGeomField, OGRGeometry *poGeom ); + virtual OGRErr SetAttributeFilter( const char * ); + + virtual OGRErr GetExtent( OGREnvelope *psExtent, int bForce ) { return GetExtent(0, psExtent, bForce); } + virtual OGRErr GetExtent( int iGeomField, OGREnvelope *psExtent, int bForce ); + + void SetLaunderFlag( int bFlag ) + { bLaunderColumnNames = bFlag; } + void SetDeferedCreation( OGRwkbGeometryType eGType, + OGRSpatialReference* poSRS, + int bGeomNullable, + int bCartoDBify); + OGRErr RunDeferedCreationIfNecessary(); + int GetDeferedCreation() const { return bDeferedCreation; } + void CancelDeferedCreation() { bDeferedCreation = FALSE; } + + void FlushDeferedInsert(); +}; + +/************************************************************************/ +/* OGRCARTODBResultLayer */ +/************************************************************************/ + +class OGRCARTODBResultLayer : public OGRCARTODBLayer +{ + OGRFeature *poFirstFeature; + + virtual CPLString GetSRS_SQL(const char* pszGeomCol); + + public: + OGRCARTODBResultLayer( OGRCARTODBDataSource* poDS, + const char * pszRawStatement ); + virtual ~OGRCARTODBResultLayer(); + + virtual OGRFeatureDefn *GetLayerDefnInternal(json_object* poObjIn); + virtual OGRFeature *GetNextRawFeature(); + + int IsOK(); +}; + +/************************************************************************/ +/* OGRCARTODBDataSource */ +/************************************************************************/ + +class OGRCARTODBDataSource : public OGRDataSource +{ + char* pszName; + char* pszAccount; + + OGRCARTODBTableLayer** papoLayers; + int nLayers; + + int bReadWrite; + int bBatchInsert; + + int bUseHTTPS; + + CPLString osAPIKey; + + int bMustCleanPersistant; + + CPLString osCurrentSchema; + + int bHasOGRMetadataFunction; + + public: + OGRCARTODBDataSource(); + ~OGRCARTODBDataSource(); + + int Open( const char * pszFilename, + char** papszOpenOptions, + int bUpdate ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + virtual OGRLayer *GetLayerByName(const char *); + + virtual int TestCapability( const char * ); + + virtual OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + virtual OGRErr DeleteLayer(int); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + const char* GetAPIURL() const; + int IsReadWrite() const { return bReadWrite; } + int DoBatchInsert() const { return bBatchInsert; } + char** AddHTTPOptions(); + json_object* RunSQL(const char* pszUnescapedSQL); + const CPLString& GetCurrentSchema() { return osCurrentSchema; } + int FetchSRSId( OGRSpatialReference * poSRS ); + + int IsAuthenticatedConnection() { return osAPIKey.size() != 0; } + int HasOGRMetadataFunction() { return bHasOGRMetadataFunction; } + void SetOGRMetadataFunction(int bFlag) { bHasOGRMetadataFunction = bFlag; } + + OGRLayer * ExecuteSQLInternal( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter = NULL, + const char *pszDialect = NULL, + int bRunDeferedActions = FALSE ); +}; + +#endif /* ndef _OGR_CARTODB_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodbdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodbdatasource.cpp new file mode 100644 index 000000000..138aa5dab --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodbdatasource.cpp @@ -0,0 +1,724 @@ +/****************************************************************************** + * $Id: ogrcartodbdatasource.cpp 29019 2015-04-25 20:34:19Z rouault $ + * + * Project: CartoDB Translator + * Purpose: Implements OGRCARTODBDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_cartodb.h" +#include "ogr_pgdump.h" + +CPL_CVSID("$Id: ogrcartodbdatasource.cpp 29019 2015-04-25 20:34:19Z rouault $"); + +/************************************************************************/ +/* OGRCARTODBDataSource() */ +/************************************************************************/ + +OGRCARTODBDataSource::OGRCARTODBDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; + pszAccount = NULL; + + bReadWrite = FALSE; + bBatchInsert = TRUE; + bUseHTTPS = FALSE; + + bMustCleanPersistant = FALSE; + bHasOGRMetadataFunction = -1; +} + +/************************************************************************/ +/* ~OGRCARTODBDataSource() */ +/************************************************************************/ + +OGRCARTODBDataSource::~OGRCARTODBDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + if (bMustCleanPersistant) + { + char** papszOptions = NULL; + papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT", CPLSPrintf("CARTODB:%p", this)); + CPLHTTPFetch( GetAPIURL(), papszOptions); + CSLDestroy(papszOptions); + } + + CPLFree( pszName ); + CPLFree( pszAccount ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRCARTODBDataSource::TestCapability( const char * pszCap ) + +{ + if( bReadWrite && EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else if( bReadWrite && EQUAL(pszCap,ODsCDeleteLayer) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRCARTODBDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer *OGRCARTODBDataSource::GetLayerByName(const char * pszLayerName) +{ + OGRLayer* poLayer = OGRDataSource::GetLayerByName(pszLayerName); + return poLayer; +} + +/************************************************************************/ +/* OGRCARTODBGetOptionValue() */ +/************************************************************************/ + +CPLString OGRCARTODBGetOptionValue(const char* pszFilename, + const char* pszOptionName) +{ + CPLString osOptionName(pszOptionName); + osOptionName += "="; + const char* pszOptionValue = strstr(pszFilename, osOptionName); + if (!pszOptionValue) + return ""; + + CPLString osOptionValue(pszOptionValue + strlen(osOptionName)); + const char* pszSpace = strchr(osOptionValue.c_str(), ' '); + if (pszSpace) + osOptionValue.resize(pszSpace - osOptionValue.c_str()); + return osOptionValue; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRCARTODBDataSource::Open( const char * pszFilename, + char** papszOpenOptions, + int bUpdateIn ) + +{ + bReadWrite = bUpdateIn; + bBatchInsert = CSLTestBoolean(CSLFetchNameValueDef(papszOpenOptions, "BATCH_INSERT", "YES")); + + pszName = CPLStrdup( pszFilename ); + if( CSLFetchNameValue(papszOpenOptions, "ACCOUNT") ) + pszAccount = CPLStrdup(CSLFetchNameValue(papszOpenOptions, "ACCOUNT")); + else + { + pszAccount = CPLStrdup(pszFilename + strlen("CARTODB:")); + char* pchSpace = strchr(pszAccount, ' '); + if( pchSpace ) + *pchSpace = '\0'; + if( pszAccount[0] == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Missing account name"); + return FALSE; + } + } + + osAPIKey = CSLFetchNameValueDef(papszOpenOptions, "API_KEY", + CPLGetConfigOption("CARTODB_API_KEY", "")); + + CPLString osTables = OGRCARTODBGetOptionValue(pszFilename, "tables"); + + /*if( osTables.size() == 0 && osAPIKey.size() == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "When not specifying tables option, CARTODB_API_KEY must be defined"); + return FALSE; + }*/ + + bUseHTTPS = CSLTestBoolean(CPLGetConfigOption("CARTODB_HTTPS", "YES")); + + OGRLayer* poSchemaLayer = ExecuteSQLInternal("SELECT current_schema()"); + if( poSchemaLayer ) + { + OGRFeature* poFeat = poSchemaLayer->GetNextFeature(); + if( poFeat ) + { + if( poFeat->GetFieldCount() == 1 ) + { + osCurrentSchema = poFeat->GetFieldAsString(0); + } + delete poFeat; + } + ReleaseResultSet(poSchemaLayer); + } + if( osCurrentSchema.size() == 0 ) + return FALSE; + + if( osAPIKey.size() && bUpdateIn ) + { + ExecuteSQLInternal( + "DROP FUNCTION IF EXISTS ogr_table_metadata(TEXT,TEXT); " + "CREATE OR REPLACE FUNCTION ogr_table_metadata(schema_name TEXT, table_name TEXT) RETURNS TABLE " + "(attname TEXT, typname TEXT, attlen INT, format_type TEXT, " + "attnum INT, attnotnull BOOLEAN, indisprimary BOOLEAN, " + "defaultexpr TEXT, dim INT, srid INT, geomtyp TEXT, srtext TEXT) AS $$ " + "SELECT a.attname::text, t.typname::text, a.attlen::int, " + "format_type(a.atttypid,a.atttypmod)::text, " + "a.attnum::int, " + "a.attnotnull::boolean, " + "i.indisprimary::boolean, " + "pg_get_expr(def.adbin, c.oid)::text AS defaultexpr, " + "(CASE WHEN t.typname = 'geometry' THEN postgis_typmod_dims(a.atttypmod) ELSE NULL END)::int dim, " + "(CASE WHEN t.typname = 'geometry' THEN postgis_typmod_srid(a.atttypmod) ELSE NULL END)::int srid, " + "(CASE WHEN t.typname = 'geometry' THEN postgis_typmod_type(a.atttypmod) ELSE NULL END)::text geomtyp, " + "srtext " + "FROM pg_class c " + "JOIN pg_attribute a ON a.attnum > 0 AND " + "a.attrelid = c.oid AND c.relname = $2 " + "AND c.relname IN (SELECT CDB_UserTables())" + "JOIN pg_type t ON a.atttypid = t.oid " + "JOIN pg_namespace n ON c.relnamespace=n.oid AND n.nspname = $1 " + "LEFT JOIN pg_index i ON c.oid = i.indrelid AND " + "i.indisprimary = 't' AND a.attnum = ANY(i.indkey) " + "LEFT JOIN pg_attrdef def ON def.adrelid = c.oid AND " + "def.adnum = a.attnum " + "LEFT JOIN spatial_ref_sys srs ON srs.srid = postgis_typmod_srid(a.atttypmod) " + "ORDER BY a.attnum " + "$$ LANGUAGE SQL"); + } + + if (osTables.size() != 0) + { + char** papszTables = CSLTokenizeString2(osTables, ",", 0); + for(int i=0;papszTables && papszTables[i];i++) + { + papoLayers = (OGRCARTODBTableLayer**) CPLRealloc( + papoLayers, (nLayers + 1) * sizeof(OGRCARTODBTableLayer*)); + papoLayers[nLayers ++] = new OGRCARTODBTableLayer(this, papszTables[i]); + } + CSLDestroy(papszTables); + return TRUE; + } + + OGRLayer* poTableListLayer = ExecuteSQLInternal("SELECT CDB_UserTables()"); + if( poTableListLayer ) + { + OGRFeature* poFeat; + while( (poFeat = poTableListLayer->GetNextFeature()) != NULL ) + { + if( poFeat->GetFieldCount() == 1 ) + { + papoLayers = (OGRCARTODBTableLayer**) CPLRealloc( + papoLayers, (nLayers + 1) * sizeof(OGRCARTODBTableLayer*)); + papoLayers[nLayers ++] = new OGRCARTODBTableLayer( + this, poFeat->GetFieldAsString(0)); + } + delete poFeat; + } + ReleaseResultSet(poTableListLayer); + } + else if( osCurrentSchema == "public" ) + return FALSE; + + /* There's currently a bug with CDB_UserTables() on multi-user accounts */ + if( nLayers == 0 && osCurrentSchema != "public" ) + { + CPLString osSQL; + osSQL.Printf("SELECT c.relname FROM pg_class c, pg_namespace n " + "WHERE c.relkind in ('r', 'v') AND c.relname !~ '^pg_' AND c.relnamespace=n.oid AND n.nspname = '%s'", + OGRCARTODBEscapeLiteral(osCurrentSchema).c_str()); + poTableListLayer = ExecuteSQLInternal(osSQL); + if( poTableListLayer ) + { + OGRFeature* poFeat; + while( (poFeat = poTableListLayer->GetNextFeature()) != NULL ) + { + if( poFeat->GetFieldCount() == 1 ) + { + papoLayers = (OGRCARTODBTableLayer**) CPLRealloc( + papoLayers, (nLayers + 1) * sizeof(OGRCARTODBTableLayer*)); + papoLayers[nLayers ++] = new OGRCARTODBTableLayer( + this, poFeat->GetFieldAsString(0)); + } + delete poFeat; + } + ReleaseResultSet(poTableListLayer); + } + else + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* GetAPIURL() */ +/************************************************************************/ + +const char* OGRCARTODBDataSource::GetAPIURL() const +{ + const char* pszAPIURL = CPLGetConfigOption("CARTODB_API_URL", NULL); + if (pszAPIURL) + return pszAPIURL; + else if (bUseHTTPS) + return CPLSPrintf("https://%s.cartodb.com/api/v2/sql", pszAccount); + else + return CPLSPrintf("http://%s.cartodb.com/api/v2/sql", pszAccount); +} + +/************************************************************************/ +/* FetchSRSId() */ +/************************************************************************/ + +int OGRCARTODBDataSource::FetchSRSId( OGRSpatialReference * poSRS ) + +{ + const char* pszAuthorityName; + + if( poSRS == NULL ) + return 0; + + OGRSpatialReference oSRS(*poSRS); + poSRS = NULL; + + pszAuthorityName = oSRS.GetAuthorityName(NULL); + + if( pszAuthorityName == NULL || strlen(pszAuthorityName) == 0 ) + { +/* -------------------------------------------------------------------- */ +/* Try to identify an EPSG code */ +/* -------------------------------------------------------------------- */ + oSRS.AutoIdentifyEPSG(); + + pszAuthorityName = oSRS.GetAuthorityName(NULL); + if (pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG")) + { + const char* pszAuthorityCode = oSRS.GetAuthorityCode(NULL); + if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 ) + { + /* Import 'clean' SRS */ + oSRS.importFromEPSG( atoi(pszAuthorityCode) ); + + pszAuthorityName = oSRS.GetAuthorityName(NULL); + } + } + } +/* -------------------------------------------------------------------- */ +/* Check whether the EPSG authority code is already mapped to a */ +/* SRS ID. */ +/* -------------------------------------------------------------------- */ + if( pszAuthorityName != NULL && EQUAL( pszAuthorityName, "EPSG" ) ) + { + int nAuthorityCode; + + /* For the root authority name 'EPSG', the authority code + * should always be integral + */ + nAuthorityCode = atoi( oSRS.GetAuthorityCode(NULL) ); + + return nAuthorityCode; + } + + return 0; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer *OGRCARTODBDataSource::ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef, + OGRwkbGeometryType eGType, + char ** papszOptions ) +{ + if (!bReadWrite) + { + CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in read-only mode"); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we already have this layer? If so, should we blow it */ +/* away? */ +/* -------------------------------------------------------------------- */ + int iLayer; + + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszName,papoLayers[iLayer]->GetName()) ) + { + if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL + && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") ) + { + DeleteLayer( iLayer ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + pszName ); + return NULL; + } + } + } + + CPLString osName(pszName); + if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) ) + { + char* pszTmp = OGRPGCommonLaunderName(pszName); + osName = pszTmp; + CPLFree(pszTmp); + } + + OGRCARTODBTableLayer* poLayer = new OGRCARTODBTableLayer(this, osName); + int bGeomNullable = CSLFetchBoolean(papszOptions, "GEOMETRY_NULLABLE", TRUE); + int bCartoDBify = CSLFetchBoolean(papszOptions, "CARTODBFY", + CSLFetchBoolean(papszOptions, "CARTODBIFY", TRUE)); + poLayer->SetLaunderFlag( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) ); + poLayer->SetDeferedCreation(eGType, poSpatialRef, bGeomNullable, bCartoDBify); + papoLayers = (OGRCARTODBTableLayer**) CPLRealloc( + papoLayers, (nLayers + 1) * sizeof(OGRCARTODBTableLayer*)); + papoLayers[nLayers ++] = poLayer; + + return poLayer; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +OGRErr OGRCARTODBDataSource::DeleteLayer(int iLayer) +{ + if (!bReadWrite) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + if( iLayer < 0 || iLayer >= nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %d not in legal range of 0 to %d.", + iLayer, nLayers-1 ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Blow away our OGR structures related to the layer. This is */ +/* pretty dangerous if anything has a reference to this layer! */ +/* -------------------------------------------------------------------- */ + CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName(); + + CPLDebug( "CARTODB", "DeleteLayer(%s)", osLayerName.c_str() ); + + int bDeferedCreation = papoLayers[iLayer]->GetDeferedCreation(); + papoLayers[iLayer]->CancelDeferedCreation(); + delete papoLayers[iLayer]; + memmove( papoLayers + iLayer, papoLayers + iLayer + 1, + sizeof(void *) * (nLayers - iLayer - 1) ); + nLayers--; + + if (osLayerName.size() == 0) + return OGRERR_NONE; + + if( !bDeferedCreation ) + { + CPLString osSQL; + osSQL.Printf("DROP TABLE %s", + OGRCARTODBEscapeIdentifier(osLayerName).c_str()); + + json_object* poObj = RunSQL(osSQL); + if( poObj == NULL ) + return OGRERR_FAILURE; + json_object_put(poObj); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* AddHTTPOptions() */ +/************************************************************************/ + +char** OGRCARTODBDataSource::AddHTTPOptions() +{ + bMustCleanPersistant = TRUE; + + return CSLAddString(NULL, CPLSPrintf("PERSISTENT=CARTODB:%p", this)); +} + +/************************************************************************/ +/* RunSQL() */ +/************************************************************************/ + +json_object* OGRCARTODBDataSource::RunSQL(const char* pszUnescapedSQL) +{ + CPLString osSQL("POSTFIELDS=q="); + /* Do post escaping */ + for(int i=0;pszUnescapedSQL[i] != 0;i++) + { + const int ch = ((unsigned char*)pszUnescapedSQL)[i]; + if (ch != '&' && ch >= 32 && ch < 128) + osSQL += (char)ch; + else + osSQL += CPLSPrintf("%%%02X", ch); + } + +/* -------------------------------------------------------------------- */ +/* Provide the API Key */ +/* -------------------------------------------------------------------- */ + if( osAPIKey.size() ) + { + osSQL += "&api_key="; + osSQL += osAPIKey; + } + +/* -------------------------------------------------------------------- */ +/* Collection the header options and execute request. */ +/* -------------------------------------------------------------------- */ + const char* pszAPIURL = GetAPIURL(); + char** papszOptions = CSLAddString( + strncmp(pszAPIURL, "/vsimem/", strlen("/vsimem/")) != 0 ? AddHTTPOptions(): NULL, osSQL); + CPLHTTPResult * psResult = CPLHTTPFetch( GetAPIURL(), papszOptions); + CSLDestroy(papszOptions); + +/* -------------------------------------------------------------------- */ +/* Check for some error conditions and report. HTML Messages */ +/* are transformed info failure. */ +/* -------------------------------------------------------------------- */ + if (psResult && psResult->pszContentType && + strncmp(psResult->pszContentType, "text/html", 9) == 0) + { + CPLDebug( "CARTODB", "RunSQL HTML Response:%s", psResult->pabyData ); + CPLError(CE_Failure, CPLE_AppDefined, + "HTML error page returned by server"); + CPLHTTPDestroyResult(psResult); + return NULL; + } + if (psResult && psResult->pszErrBuf != NULL) + { + CPLDebug( "CARTODB", "RunSQL Error Message:%s", psResult->pszErrBuf ); + } + else if (psResult && psResult->nStatus != 0) + { + CPLDebug( "CARTODB", "RunSQL Error Status:%d", psResult->nStatus ); + } + + if( psResult->pabyData == NULL ) + { + CPLHTTPDestroyResult(psResult); + return NULL; + } + + if( strlen((const char*)psResult->pabyData) < 1000 ) + CPLDebug( "CARTODB", "RunSQL Response:%s", psResult->pabyData ); + + json_tokener* jstok = NULL; + json_object* poObj = NULL; + + jstok = json_tokener_new(); + poObj = json_tokener_parse_ex(jstok, (const char*) psResult->pabyData, -1); + if( jstok->err != json_tokener_success) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JSON parsing error: %s (at offset %d)", + json_tokener_error_desc(jstok->err), jstok->char_offset); + json_tokener_free(jstok); + CPLHTTPDestroyResult(psResult); + return NULL; + } + json_tokener_free(jstok); + + CPLHTTPDestroyResult(psResult); + + if( poObj != NULL ) + { + if( json_object_get_type(poObj) == json_type_object ) + { + json_object* poError = json_object_object_get(poObj, "error"); + if( poError != NULL && json_object_get_type(poError) == json_type_array && + json_object_array_length(poError) > 0 ) + { + poError = json_object_array_get_idx(poError, 0); + if( poError != NULL && json_object_get_type(poError) == json_type_string ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error returned by server : %s", json_object_get_string(poError)); + json_object_put(poObj); + return NULL; + } + } + } + else + { + json_object_put(poObj); + return NULL; + } + } + + return poObj; +} + +/************************************************************************/ +/* OGRCARTODBGetSingleRow() */ +/************************************************************************/ + +json_object* OGRCARTODBGetSingleRow(json_object* poObj) +{ + if( poObj == NULL ) + { + return NULL; + } + + json_object* poRows = json_object_object_get(poObj, "rows"); + if( poRows == NULL || + json_object_get_type(poRows) != json_type_array || + json_object_array_length(poRows) != 1 ) + { + return NULL; + } + + json_object* poRowObj = json_object_array_get_idx(poRows, 0); + if( poRowObj == NULL || json_object_get_type(poRowObj) != json_type_object ) + { + return NULL; + } + + return poRowObj; +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRCARTODBDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ + return ExecuteSQLInternal(pszSQLCommand, poSpatialFilter, pszDialect, + TRUE); +} + +OGRLayer * OGRCARTODBDataSource::ExecuteSQLInternal( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect, + int bRunDeferedActions ) + +{ + if( bRunDeferedActions ) + { + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + papoLayers[iLayer]->RunDeferedCreationIfNecessary(); + papoLayers[iLayer]->FlushDeferedInsert(); + } + } + + /* Skip leading spaces */ + while(*pszSQLCommand == ' ') + pszSQLCommand ++; + +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Special case DELLAYER: command. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand,"DELLAYER:",9) ) + { + const char *pszLayerName = pszSQLCommand + 9; + + while( *pszLayerName == ' ' ) + pszLayerName++; + + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(papoLayers[iLayer]->GetName(), + pszLayerName )) + { + DeleteLayer( iLayer ); + break; + } + } + return NULL; + } + + if( !EQUALN(pszSQLCommand, "SELECT", strlen("SELECT")) && + !EQUALN(pszSQLCommand, "EXPLAIN", strlen("EXPLAIN")) && + !EQUALN(pszSQLCommand, "WITH", strlen("WITH")) ) + { + RunSQL(pszSQLCommand); + return NULL; + } + + OGRCARTODBResultLayer* poLayer = new OGRCARTODBResultLayer( this, pszSQLCommand ); + + if( poSpatialFilter != NULL ) + poLayer->SetSpatialFilter( poSpatialFilter ); + + if( !poLayer->IsOK() ) + { + delete poLayer; + return NULL; + } + + return poLayer; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRCARTODBDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodbdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodbdriver.cpp new file mode 100644 index 000000000..b15d8eaf7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodbdriver.cpp @@ -0,0 +1,143 @@ +/****************************************************************************** + * $Id: ogrcartodbdriver.cpp 29019 2015-04-25 20:34:19Z rouault $ + * + * Project: CartoDB Translator + * Purpose: Implements OGRCARTODBDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_cartodb.h" + +// g++ -g -Wall -fPIC -shared -o ogr_CARTODB.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/cartodb ogr/ogrsf_frmts/cartodb/*.c* -L. -lgdal -Iogr/ogrsf_frmts/geojson/libjson + +CPL_CVSID("$Id: ogrcartodbdriver.cpp 29019 2015-04-25 20:34:19Z rouault $"); + +extern "C" void RegisterOGRCartoDB(); + +/************************************************************************/ +/* OGRCartoDBDriverIdentify() */ +/************************************************************************/ + +static int OGRCartoDBDriverIdentify( GDALOpenInfo* poOpenInfo ) +{ + return EQUALN(poOpenInfo->pszFilename, "CARTODB:", strlen("CARTODB:")); +} + +/************************************************************************/ +/* OGRCartoDBDriverOpen() */ +/************************************************************************/ + +static GDALDataset *OGRCartoDBDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( !OGRCartoDBDriverIdentify(poOpenInfo) ) + return NULL; + + OGRCARTODBDataSource *poDS = new OGRCARTODBDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, poOpenInfo->papszOpenOptions, + poOpenInfo->eAccess == GA_Update ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* OGRCartoDBDriverCreate() */ +/************************************************************************/ + +static GDALDataset *OGRCartoDBDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + CPL_UNUSED char **papszOptions ) + +{ + OGRCARTODBDataSource *poDS = new OGRCARTODBDataSource(); + + if( !poDS->Open( pszName, NULL, TRUE ) ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "CartoDB driver doesn't support database creation." ); + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRCARTODB() */ +/************************************************************************/ + +void RegisterOGRCartoDB() + +{ + if( GDALGetDriverByName( "CartoDB" ) == NULL ) + { + GDALDriver* poDriver = new GDALDriver(); + + poDriver->SetDescription( "CartoDB" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "CartoDB" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_cartodb.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CONNECTION_PREFIX, "CARTODB:" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, + "" + " "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, ""); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, + "" + " "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime Time" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_DEFAULT_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES" ); + + poDriver->pfnOpen = OGRCartoDBDriverOpen; + poDriver->pfnIdentify = OGRCartoDBDriverIdentify; + poDriver->pfnCreate = OGRCartoDBDriverCreate; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodblayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodblayer.cpp new file mode 100644 index 000000000..c72f1c4b5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodblayer.cpp @@ -0,0 +1,465 @@ +/****************************************************************************** + * $Id: ogrcartodblayer.cpp 29000 2015-04-24 21:46:17Z rouault $ + * + * Project: CartoDB Translator + * Purpose: Implements OGRCARTODBLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_cartodb.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrcartodblayer.cpp 29000 2015-04-24 21:46:17Z rouault $"); + +/************************************************************************/ +/* OGRCARTODBLayer() */ +/************************************************************************/ + +OGRCARTODBLayer::OGRCARTODBLayer(OGRCARTODBDataSource* poDS) + +{ + this->poDS = poDS; + + poSRS = NULL; + + poFeatureDefn = NULL; + + poCachedObj = NULL; + + ResetReading(); +} + +/************************************************************************/ +/* ~OGRCARTODBLayer() */ +/************************************************************************/ + +OGRCARTODBLayer::~OGRCARTODBLayer() + +{ + if( poCachedObj != NULL ) + json_object_put(poCachedObj); + + if( poSRS != NULL ) + poSRS->Release(); + + if( poFeatureDefn != NULL ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRCARTODBLayer::ResetReading() + +{ + if( poCachedObj != NULL ) + json_object_put(poCachedObj); + poCachedObj = NULL; + bEOF = FALSE; + nFetchedObjects = -1; + iNextInFetchedObjects = 0; + iNext = 0; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRCARTODBLayer::GetLayerDefn() +{ + return GetLayerDefnInternal(NULL); +} + +/************************************************************************/ +/* BuildFeature() */ +/************************************************************************/ + +OGRFeature *OGRCARTODBLayer::BuildFeature(json_object* poRowObj) +{ + OGRFeature* poFeature = NULL; + if( poRowObj != NULL && + json_object_get_type(poRowObj) == json_type_object ) + { + poFeature = new OGRFeature(poFeatureDefn); + + if( osFIDColName.size() ) + { + json_object* poVal = json_object_object_get(poRowObj, osFIDColName); + if( poVal != NULL && + json_object_get_type(poVal) == json_type_int ) + { + poFeature->SetFID(json_object_get_int64(poVal)); + } + } + else + { + poFeature->SetFID(iNext); + } + + for(int i=0;iGetFieldCount();i++) + { + json_object* poVal = json_object_object_get(poRowObj, + poFeatureDefn->GetFieldDefn(i)->GetNameRef()); + if( poVal != NULL && + json_object_get_type(poVal) == json_type_string ) + { + if( poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime ) + { + OGRField sField; + if( OGRParseXMLDateTime( json_object_get_string(poVal), + &sField) ) + { + poFeature->SetField(i, &sField); + } + } + else + { + poFeature->SetField(i, json_object_get_string(poVal)); + } + } + else if( poVal != NULL && + (json_object_get_type(poVal) == json_type_int || + json_object_get_type(poVal) == json_type_boolean) ) + { + poFeature->SetField(i, (GIntBig)json_object_get_int64(poVal)); + } + else if( poVal != NULL && + json_object_get_type(poVal) == json_type_double ) + { + poFeature->SetField(i, json_object_get_double(poVal)); + } + } + + for(int i=0;iGetGeomFieldCount();i++) + { + OGRGeomFieldDefn* poGeomFldDefn = poFeatureDefn->GetGeomFieldDefn(i); + json_object* poVal = json_object_object_get(poRowObj, + poGeomFldDefn->GetNameRef()); + if( poVal != NULL && + json_object_get_type(poVal) == json_type_string ) + { + OGRGeometry* poGeom = OGRGeometryFromHexEWKB( + json_object_get_string(poVal), NULL, FALSE); + if( poGeom != NULL ) + poGeom->assignSpatialReference(poGeomFldDefn->GetSpatialRef()); + poFeature->SetGeomFieldDirectly(i, poGeom); + } + } + } + return poFeature; +} + +/************************************************************************/ +/* FetchNewFeatures() */ +/************************************************************************/ + +json_object* OGRCARTODBLayer::FetchNewFeatures(GIntBig iNext) +{ + CPLString osSQL = osBaseSQL; + if( osSQL.ifind("SELECT") != std::string::npos && + osSQL.ifind(" LIMIT ") == std::string::npos ) + { + osSQL += " LIMIT "; + osSQL += CPLSPrintf("%d", GetFeaturesToFetch()); + osSQL += " OFFSET "; + osSQL += CPLSPrintf(CPL_FRMT_GIB, iNext); + } + return poDS->RunSQL(osSQL); +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRCARTODBLayer::GetNextRawFeature() +{ + if( bEOF ) + return NULL; + + if( iNextInFetchedObjects >= nFetchedObjects ) + { + if( nFetchedObjects > 0 && nFetchedObjects < GetFeaturesToFetch() ) + { + bEOF = TRUE; + return NULL; + } + + if( poFeatureDefn == NULL && osBaseSQL.size() == 0 ) + { + GetLayerDefn(); + } + + json_object* poObj = FetchNewFeatures(iNext); + if( poObj == NULL ) + { + bEOF = TRUE; + return NULL; + } + + if( poFeatureDefn == NULL ) + { + GetLayerDefnInternal(poObj); + } + + json_object* poRows = json_object_object_get(poObj, "rows"); + if( poRows == NULL || + json_object_get_type(poRows) != json_type_array || + json_object_array_length(poRows) == 0 ) + { + json_object_put(poObj); + bEOF = TRUE; + return NULL; + } + + if( poCachedObj != NULL ) + json_object_put(poCachedObj); + poCachedObj = poObj; + + nFetchedObjects = json_object_array_length(poRows); + iNextInFetchedObjects = 0; + } + + json_object* poRows = json_object_object_get(poCachedObj, "rows"); + json_object* poRowObj = json_object_array_get_idx(poRows, iNextInFetchedObjects); + + iNextInFetchedObjects ++; + + OGRFeature* poFeature = BuildFeature(poRowObj); + iNext = poFeature->GetFID() + 1; + + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRCARTODBLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRCARTODBLayer::TestCapability( const char * pszCap ) + +{ + if ( EQUAL(pszCap, OLCStringsAsUTF8) ) + return TRUE; + return FALSE; +} + +/************************************************************************/ +/* EstablishLayerDefn() */ +/************************************************************************/ + +void OGRCARTODBLayer::EstablishLayerDefn(const char* pszLayerName, + json_object* poObjIn) +{ + poFeatureDefn = new OGRFeatureDefn(pszLayerName); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType(wkbNone); + + CPLString osSQL; + size_t nPos = osBaseSQL.ifind(" LIMIT "); + if( nPos != std::string::npos ) + { + osSQL = osBaseSQL; + size_t nSize = osSQL.size(); + for(size_t i = nPos + strlen(" LIMIT "); i < nSize; i++) + { + if( osSQL[i] == ' ' ) + break; + osSQL[i] = '0'; + } + } + else + osSQL.Printf("%s LIMIT 0", osBaseSQL.c_str()); + json_object* poObj; + if( poObjIn != NULL ) + poObj = poObjIn; + else + { + poObj = poDS->RunSQL(osSQL); + if( poObj == NULL ) + { + return; + } + } + + json_object* poFields = json_object_object_get(poObj, "fields"); + if( poFields == NULL || json_object_get_type(poFields) != json_type_object) + { + if( poObjIn == NULL ) + json_object_put(poObj); + return; + } + + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poFields, it ) + { + const char* pszColName = it.key; + if( it.val != NULL && json_object_get_type(it.val) == json_type_object) + { + json_object* poType = json_object_object_get(it.val, "type"); + if( poType != NULL && json_object_get_type(poType) == json_type_string ) + { + const char* pszType = json_object_get_string(poType); + CPLDebug("CARTODB", "%s : %s", pszColName, pszType); + if( EQUAL(pszType, "string") || + EQUAL(pszType, "unknown(19)") /* name */ ) + { + OGRFieldDefn oFieldDefn(pszColName, OFTString); + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + else if( EQUAL(pszType, "number") ) + { + if( !EQUAL(pszColName, "cartodb_id") ) + { + OGRFieldDefn oFieldDefn(pszColName, OFTReal); + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + else + osFIDColName = pszColName; + } + else if( EQUAL(pszType, "date") ) + { + if( !EQUAL(pszColName, "created_at") && + !EQUAL(pszColName, "updated_at") ) + { + OGRFieldDefn oFieldDefn(pszColName, OFTDateTime); + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + } + else if( EQUAL(pszType, "geometry") ) + { + if( !EQUAL(pszColName, "the_geom_webmercator") ) + { + OGRCartoDBGeomFieldDefn *poFieldDefn = + new OGRCartoDBGeomFieldDefn(pszColName, wkbUnknown); + poFeatureDefn->AddGeomFieldDefn(poFieldDefn, FALSE); + OGRSpatialReference* poSRS = GetSRS(pszColName, &poFieldDefn->nSRID); + if( poSRS != NULL ) + { + poFeatureDefn->GetGeomFieldDefn( + poFeatureDefn->GetGeomFieldCount() - 1)->SetSpatialRef(poSRS); + poSRS->Release(); + } + } + } + else if( EQUAL(pszType, "boolean") ) + { + OGRFieldDefn oFieldDefn(pszColName, OFTInteger); + oFieldDefn.SetSubType(OFSTBoolean); + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + else + { + CPLDebug("CARTODB", "Unhandled type: %s. Defaulting to string", pszType); + OGRFieldDefn oFieldDefn(pszColName, OFTString); + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + } + else if( poType != NULL && json_object_get_type(poType) == json_type_int ) + { + /* FIXME? manual creations of geometry columns return integer types */ + OGRCartoDBGeomFieldDefn *poFieldDefn = + new OGRCartoDBGeomFieldDefn(pszColName, wkbUnknown); + poFeatureDefn->AddGeomFieldDefn(poFieldDefn, FALSE); + OGRSpatialReference* poSRS = GetSRS(pszColName, &poFieldDefn->nSRID); + if( poSRS != NULL ) + { + poFeatureDefn->GetGeomFieldDefn( + poFeatureDefn->GetGeomFieldCount() - 1)->SetSpatialRef(poSRS); + poSRS->Release(); + } + } + } + } + if( poObjIn == NULL ) + json_object_put(poObj); +} + +/************************************************************************/ +/* GetSRS() */ +/************************************************************************/ + +OGRSpatialReference* OGRCARTODBLayer::GetSRS(const char* pszGeomCol, + int *pnSRID) +{ + json_object* poObj = poDS->RunSQL(GetSRS_SQL(pszGeomCol)); + json_object* poRowObj = OGRCARTODBGetSingleRow(poObj); + if( poRowObj == NULL ) + { + if( poObj != NULL ) + json_object_put(poObj); + return NULL; + } + + json_object* poSRID = json_object_object_get(poRowObj, "srid"); + if( poSRID != NULL && json_object_get_type(poSRID) == json_type_int ) + { + *pnSRID = json_object_get_int(poSRID); + } + + json_object* poSRTEXT = json_object_object_get(poRowObj, "srtext"); + OGRSpatialReference* poSRS = NULL; + if( poSRTEXT != NULL && json_object_get_type(poSRTEXT) == json_type_string ) + { + const char* pszSRTEXT = json_object_get_string(poSRTEXT); + poSRS = new OGRSpatialReference(); + char* pszTmp = (char* )pszSRTEXT; + if( poSRS->importFromWkt(&pszTmp) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + json_object_put(poObj); + + return poSRS; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodbresultlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodbresultlayer.cpp new file mode 100644 index 000000000..8440fa0ef --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodbresultlayer.cpp @@ -0,0 +1,133 @@ +/****************************************************************************** + * $Id + * + * Project: CartoDB Translator + * Purpose: Implements OGRCARTODBResultLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_cartodb.h" + +CPL_CVSID("$Id"); + +/************************************************************************/ +/* OGRCARTODBResultLayer() */ +/************************************************************************/ + +OGRCARTODBResultLayer::OGRCARTODBResultLayer( OGRCARTODBDataSource* poDS, + const char * pszRawQueryIn ) : + OGRCARTODBLayer(poDS) +{ + osBaseSQL = pszRawQueryIn; + SetDescription( "result" ); + poFirstFeature = NULL; +} + +/************************************************************************/ +/* ~OGRCARTODBResultLayer() */ +/************************************************************************/ + +OGRCARTODBResultLayer::~OGRCARTODBResultLayer() + +{ + delete poFirstFeature; +} + +/************************************************************************/ +/* GetLayerDefnInternal() */ +/************************************************************************/ + +OGRFeatureDefn * OGRCARTODBResultLayer::GetLayerDefnInternal(json_object* poObjIn) +{ + if( poFeatureDefn != NULL ) + return poFeatureDefn; + + EstablishLayerDefn("result", poObjIn); + + return poFeatureDefn; +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRCARTODBResultLayer::GetNextRawFeature() +{ + if( poFirstFeature ) + { + OGRFeature* poRet = poFirstFeature; + poFirstFeature = NULL; + return poRet; + } + else + return OGRCARTODBLayer::GetNextRawFeature(); +} + +/************************************************************************/ +/* IsOK() */ +/************************************************************************/ + +int OGRCARTODBResultLayer::IsOK() +{ + CPLErrorReset(); + poFirstFeature = GetNextFeature(); + return CPLGetLastErrorType() == 0; +} + +/************************************************************************/ +/* GetSRS_SQL() */ +/************************************************************************/ + +CPLString OGRCARTODBResultLayer::GetSRS_SQL(const char* pszGeomCol) +{ + CPLString osSQL; + CPLString osLimitedSQL; + + size_t nPos = osBaseSQL.ifind(" LIMIT "); + if( nPos != std::string::npos ) + { + osLimitedSQL = osBaseSQL; + size_t nSize = osLimitedSQL.size(); + for(size_t i = nPos + strlen(" LIMIT "); i < nSize; i++) + { + if( osLimitedSQL[i] == ' ' && osLimitedSQL[i-1] == '0') + { + osLimitedSQL[i-1] = '1'; + break; + } + osLimitedSQL[i] = '0'; + } + } + else + osLimitedSQL.Printf("%s LIMIT 1", osBaseSQL.c_str()); + + /* Assuming that the SRID of the first non-NULL geometry applies */ + /* to geometries of all rows. */ + osSQL.Printf("SELECT srid, srtext FROM spatial_ref_sys WHERE srid IN " + "(SELECT ST_SRID(%s) FROM (%s) ogr_subselect)", + OGRCARTODBEscapeIdentifier(pszGeomCol).c_str(), + osLimitedSQL.c_str()); + + return osSQL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodbtablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodbtablelayer.cpp new file mode 100644 index 000000000..658858b83 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cartodb/ogrcartodbtablelayer.cpp @@ -0,0 +1,1343 @@ +/****************************************************************************** + * $Id: ogrcartodbtablelayer.cpp 29004 2015-04-25 08:38:29Z rouault $ + * + * Project: CartoDB Translator + * Purpose: Implements OGRCARTODBTableLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_cartodb.h" +#include "ogr_p.h" +#include "ogr_pgdump.h" + +CPL_CVSID("$Id: ogrcartodbtablelayer.cpp 29004 2015-04-25 08:38:29Z rouault $"); + +/************************************************************************/ +/* OGRCARTODBEscapeIdentifier( ) */ +/************************************************************************/ + +CPLString OGRCARTODBEscapeIdentifier(const char* pszStr) +{ + CPLString osStr; + + osStr += "\""; + + char ch; + for(int i=0; (ch = pszStr[i]) != '\0'; i++) + { + if (ch == '"') + osStr.append(1, ch); + osStr.append(1, ch); + } + + osStr += "\""; + + return osStr; +} + +/************************************************************************/ +/* OGRCARTODBEscapeLiteral( ) */ +/************************************************************************/ + +CPLString OGRCARTODBEscapeLiteral(const char* pszStr) +{ + CPLString osStr; + + char ch; + for(int i=0; (ch = pszStr[i]) != '\0'; i++) + { + if (ch == '\'') + osStr.append(1, ch); + osStr.append(1, ch); + } + + return osStr; +} + +/************************************************************************/ +/* OGRCARTODBTableLayer() */ +/************************************************************************/ + +OGRCARTODBTableLayer::OGRCARTODBTableLayer(OGRCARTODBDataSource* poDS, + const char* pszName) : + OGRCARTODBLayer(poDS) + +{ + osName = pszName; + SetDescription( osName ); + bLaunderColumnNames = TRUE; + bInDeferedInsert = poDS->DoBatchInsert(); + nNextFID = -1; + bDeferedCreation = FALSE; + bCartoDBify = FALSE; + nMaxChunkSize = atoi(CPLGetConfigOption("CARTODB_MAX_CHUNK_SIZE", "15")) * 1024 * 1024; +} + +/************************************************************************/ +/* ~OGRCARTODBTableLayer() */ +/************************************************************************/ + +OGRCARTODBTableLayer::~OGRCARTODBTableLayer() + +{ + if( bDeferedCreation ) RunDeferedCreationIfNecessary(); + FlushDeferedInsert(); +} + +/************************************************************************/ +/* GetLayerDefnInternal() */ +/************************************************************************/ + +OGRFeatureDefn * OGRCARTODBTableLayer::GetLayerDefnInternal(CPL_UNUSED json_object* poObjIn) +{ + if( poFeatureDefn != NULL ) + return poFeatureDefn; + + CPLString osCommand; + if( poDS->IsAuthenticatedConnection() ) + { + // Get everything ! + osCommand.Printf( + "SELECT a.attname, t.typname, a.attlen, " + "format_type(a.atttypid,a.atttypmod), " + "a.attnum, " + "a.attnotnull, " + "i.indisprimary, " + "pg_get_expr(def.adbin, c.oid) AS defaultexpr, " + "postgis_typmod_dims(a.atttypmod) dim, " + "postgis_typmod_srid(a.atttypmod) srid, " + "postgis_typmod_type(a.atttypmod)::text geomtyp, " + "srtext " + "FROM pg_class c " + "JOIN pg_attribute a ON a.attnum > 0 AND " + "a.attrelid = c.oid AND c.relname = '%s' " + "JOIN pg_type t ON a.atttypid = t.oid " + "JOIN pg_namespace n ON c.relnamespace=n.oid AND n.nspname= '%s' " + "LEFT JOIN pg_index i ON c.oid = i.indrelid AND " + "i.indisprimary = 't' AND a.attnum = ANY(i.indkey) " + "LEFT JOIN pg_attrdef def ON def.adrelid = c.oid AND " + "def.adnum = a.attnum " + "LEFT JOIN spatial_ref_sys srs ON srs.srid = postgis_typmod_srid(a.atttypmod) " + "ORDER BY a.attnum", + OGRCARTODBEscapeLiteral(osName).c_str(), + OGRCARTODBEscapeLiteral(poDS->GetCurrentSchema()).c_str()); + } + else if( poDS->HasOGRMetadataFunction() != FALSE ) + { + osCommand.Printf( "SELECT * FROM ogr_table_metadata('%s', '%s')", + OGRCARTODBEscapeLiteral(poDS->GetCurrentSchema()).c_str(), + OGRCARTODBEscapeLiteral(osName).c_str() ); + } + + if( osCommand.size() ) + { + if( !poDS->IsAuthenticatedConnection() && poDS->HasOGRMetadataFunction() < 0 ) + CPLPushErrorHandler(CPLQuietErrorHandler); + OGRLayer* poLyr = poDS->ExecuteSQLInternal(osCommand); + if( !poDS->IsAuthenticatedConnection() && poDS->HasOGRMetadataFunction() < 0 ) + { + CPLPopErrorHandler(); + if( poLyr == NULL ) + { + CPLDebug("CARTODB", "ogr_table_metadata(text, text) not available"); + CPLErrorReset(); + } + else if( poLyr->GetLayerDefn()->GetFieldCount() != 12 ) + { + CPLDebug("CARTODB", "ogr_table_metadata(text, text) has unexpected column count"); + poDS->ReleaseResultSet(poLyr); + poLyr = NULL; + } + poDS->SetOGRMetadataFunction(poLyr != NULL); + } + if( poLyr ) + { + poFeatureDefn = new OGRFeatureDefn(osName); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType(wkbNone); + + OGRFeature* poFeat; + while( (poFeat = poLyr->GetNextFeature()) != NULL ) + { + const char* pszAttname = poFeat->GetFieldAsString("attname"); + const char* pszType = poFeat->GetFieldAsString("typname"); + int nWidth = poFeat->GetFieldAsInteger("attlen"); + const char* pszFormatType = poFeat->GetFieldAsString("format_type"); + int bNotNull = poFeat->GetFieldAsInteger("attnotnull"); + int bIsPrimary = poFeat->GetFieldAsInteger("indisprimary"); + const char* pszDefault = (poFeat->IsFieldSet(poLyr->GetLayerDefn()->GetFieldIndex("defaultexpr"))) ? + poFeat->GetFieldAsString("defaultexpr") : NULL; + + if( bIsPrimary && + (EQUAL(pszType, "int2") || + EQUAL(pszType, "int4") || + EQUAL(pszType, "int8") || + EQUAL(pszType, "serial") || + EQUAL(pszType, "bigserial")) ) + { + osFIDColName = pszAttname; + } + else if( strcmp(pszAttname, "created_at") == 0 || + strcmp(pszAttname, "updated_at") == 0 || + strcmp(pszAttname, "the_geom_webmercator") == 0) + { + /* ignored */ + } + else + { + if( EQUAL(pszType,"geometry") ) + { + int nDim = poFeat->GetFieldAsInteger("dim"); + int nSRID = poFeat->GetFieldAsInteger("srid"); + const char* pszGeomType = poFeat->GetFieldAsString("geomtyp"); + const char* pszSRText = (poFeat->IsFieldSet( + poLyr->GetLayerDefn()->GetFieldIndex("srtext"))) ? + poFeat->GetFieldAsString("srtext") : NULL; + OGRwkbGeometryType eType = OGRFromOGCGeomType(pszGeomType); + if( nDim == 3 ) + eType = wkbSetZ(eType); + OGRCartoDBGeomFieldDefn *poFieldDefn = + new OGRCartoDBGeomFieldDefn(pszAttname, eType); + if( bNotNull ) + poFieldDefn->SetNullable(FALSE); + OGRSpatialReference* poSRS = NULL; + if( pszSRText != NULL ) + { + poSRS = new OGRSpatialReference(); + char* pszTmp = (char* )pszSRText; + if( poSRS->importFromWkt(&pszTmp) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + if( poSRS != NULL ) + { + poFieldDefn->SetSpatialRef(poSRS); + poSRS->Release(); + } + } + poFieldDefn->nSRID = nSRID; + poFeatureDefn->AddGeomFieldDefn(poFieldDefn, FALSE); + } + else + { + OGRFieldDefn oField(pszAttname, OFTString); + if( bNotNull ) + oField.SetNullable(FALSE); + OGRPGCommonLayerSetType(oField, pszType, pszFormatType, nWidth); + if( pszDefault ) + OGRPGCommonLayerNormalizeDefault(&oField, pszDefault); + + poFeatureDefn->AddFieldDefn( &oField ); + } + } + delete poFeat; + } + + poDS->ReleaseResultSet(poLyr); + } + } + + if( poFeatureDefn == NULL ) + { + osBaseSQL.Printf("SELECT * FROM %s", OGRCARTODBEscapeIdentifier(osName).c_str()); + EstablishLayerDefn(osName, NULL); + osBaseSQL = ""; + } + + if( osFIDColName.size() > 0 ) + { + osBaseSQL = "SELECT "; + osBaseSQL += OGRCARTODBEscapeIdentifier(osFIDColName); + } + for(int i=0; iGetGeomFieldCount(); i++) + { + if( osBaseSQL.size() == 0 ) + osBaseSQL = "SELECT "; + else + osBaseSQL += ", "; + osBaseSQL += OGRCARTODBEscapeIdentifier(poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef()); + } + for(int i=0; iGetFieldCount(); i++) + { + if( osBaseSQL.size() == 0 ) + osBaseSQL = "SELECT "; + else + osBaseSQL += ", "; + osBaseSQL += OGRCARTODBEscapeIdentifier(poFeatureDefn->GetFieldDefn(i)->GetNameRef()); + } + if( osBaseSQL.size() == 0 ) + osBaseSQL = "SELECT *"; + osBaseSQL += " FROM "; + osBaseSQL += OGRCARTODBEscapeIdentifier(osName); + + osSELECTWithoutWHERE = osBaseSQL; + + return poFeatureDefn; +} + +/************************************************************************/ +/* FetchNewFeatures() */ +/************************************************************************/ + +json_object* OGRCARTODBTableLayer::FetchNewFeatures(GIntBig iNext) +{ + if( osFIDColName.size() > 0 ) + { + CPLString osSQL; + osSQL.Printf("%s WHERE %s%s >= " CPL_FRMT_GIB " ORDER BY %s ASC LIMIT %d", + osSELECTWithoutWHERE.c_str(), + ( osWHERE.size() ) ? CPLSPrintf("%s AND ", osWHERE.c_str()) : "", + OGRCARTODBEscapeIdentifier(osFIDColName).c_str(), + iNext, + OGRCARTODBEscapeIdentifier(osFIDColName).c_str(), + GetFeaturesToFetch()); + return poDS->RunSQL(osSQL); + } + else + return OGRCARTODBLayer::FetchNewFeatures(iNext); +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRCARTODBTableLayer::GetNextRawFeature() +{ + if( bDeferedCreation && RunDeferedCreationIfNecessary() != OGRERR_NONE ) + return NULL; + FlushDeferedInsert(); + return OGRCARTODBLayer::GetNextRawFeature(); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRCARTODBTableLayer::SetAttributeFilter( const char *pszQuery ) + +{ + GetLayerDefn(); + + if( pszQuery == NULL ) + osQuery = ""; + else + { + osQuery = "("; + osQuery += pszQuery; + osQuery += ")"; + } + + BuildWhere(); + + ResetReading(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRCARTODBTableLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeomIn ) + +{ + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() || + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone ) + { + if( iGeomField != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } + return; + } + m_iGeomFieldFilter = iGeomField; + + if( InstallFilter( poGeomIn ) ) + { + BuildWhere(); + + ResetReading(); + } +} + +/************************************************************************/ +/* FlushDeferedInsert() */ +/************************************************************************/ + +void OGRCARTODBTableLayer::FlushDeferedInsert() + +{ + if( bInDeferedInsert && osDeferedInsertSQL.size() > 0 ) + { + osDeferedInsertSQL = "BEGIN;" + osDeferedInsertSQL + "COMMIT;"; + json_object* poObj = poDS->RunSQL(osDeferedInsertSQL); + if( poObj != NULL ) + { + json_object_put(poObj); + } + } + + bInDeferedInsert = FALSE; + osDeferedInsertSQL = ""; + nNextFID = -1; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRCARTODBTableLayer::CreateField( OGRFieldDefn *poFieldIn, + CPL_UNUSED int bApproxOK ) +{ + GetLayerDefn(); + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + OGRFieldDefn oField(poFieldIn); + if( bLaunderColumnNames ) + { + char* pszName = OGRPGCommonLaunderName(oField.GetNameRef()); + oField.SetName(pszName); + CPLFree(pszName); + } + +/* -------------------------------------------------------------------- */ +/* Create the new field. */ +/* -------------------------------------------------------------------- */ + + if( !bDeferedCreation ) + { + CPLString osSQL; + osSQL.Printf( "ALTER TABLE %s ADD COLUMN %s %s", + OGRCARTODBEscapeIdentifier(osName).c_str(), + OGRCARTODBEscapeIdentifier(oField.GetNameRef()).c_str(), + OGRPGCommonLayerGetType(oField, FALSE, TRUE).c_str() ); + if( !oField.IsNullable() ) + osSQL += " NOT NULL"; + if( oField.GetDefault() != NULL && !oField.IsDefaultDriverSpecific() ) + { + osSQL += " DEFAULT "; + osSQL += OGRPGCommonLayerGetPGDefault(&oField); + } + + json_object* poObj = poDS->RunSQL(osSQL); + if( poObj == NULL ) + return OGRERR_FAILURE; + json_object_put(poObj); + } + + poFeatureDefn->AddFieldDefn( &oField ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRCARTODBTableLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + int i; + + if( bDeferedCreation ) + { + if( RunDeferedCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + } + + GetLayerDefn(); + int bHasUserFieldMatchingFID = FALSE; + if( osFIDColName.size() ) + bHasUserFieldMatchingFID = poFeatureDefn->GetFieldIndex(osFIDColName) >= 0; + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + CPLString osSQL; + + int bHasJustGotNextFID = FALSE; + if( !bHasUserFieldMatchingFID && bInDeferedInsert && nNextFID < 0 && osFIDColName.size() ) + { + osSQL.Printf("SELECT nextval('%s') AS nextid", + OGRCARTODBEscapeLiteral(CPLSPrintf("%s_%s_seq", osName.c_str(), osFIDColName.c_str())).c_str()); + + json_object* poObj = poDS->RunSQL(osSQL); + json_object* poRowObj = OGRCARTODBGetSingleRow(poObj); + if( poRowObj != NULL ) + { + json_object* poID = json_object_object_get(poRowObj, "nextid"); + if( poID != NULL && json_object_get_type(poID) == json_type_int ) + { + nNextFID = json_object_get_int64(poID); + bHasJustGotNextFID = TRUE; + } + } + + if( poObj != NULL ) + json_object_put(poObj); + } + + osSQL.Printf("INSERT INTO %s ", OGRCARTODBEscapeIdentifier(osName).c_str()); + int bMustComma = FALSE; + for(i = 0; i < poFeatureDefn->GetFieldCount(); i++) + { + if( !poFeature->IsFieldSet(i) ) + continue; + + if( bMustComma ) + osSQL += ", "; + else + { + osSQL += "("; + bMustComma = TRUE; + } + + osSQL += OGRCARTODBEscapeIdentifier(poFeatureDefn->GetFieldDefn(i)->GetNameRef()); + } + + for(i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++) + { + if( poFeature->GetGeomFieldRef(i) == NULL ) + continue; + + if( bMustComma ) + osSQL += ", "; + else + { + osSQL += "("; + bMustComma = TRUE; + } + + osSQL += OGRCARTODBEscapeIdentifier(poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef()); + } + + if( !bHasUserFieldMatchingFID && + osFIDColName.size() && (poFeature->GetFID() != OGRNullFID || nNextFID >= 0) ) + { + if( bMustComma ) + osSQL += ", "; + else + { + osSQL += "("; + bMustComma = TRUE; + } + + osSQL += OGRCARTODBEscapeIdentifier(osFIDColName); + } + + if( !bMustComma ) + osSQL += " DEFAULT VALUES"; + else + { + osSQL += ") VALUES ("; + + bMustComma = FALSE; + for(i = 0; i < poFeatureDefn->GetFieldCount(); i++) + { + if( !poFeature->IsFieldSet(i) ) + continue; + + if( bMustComma ) + osSQL += ", "; + else + bMustComma = TRUE; + + OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType(); + if( eType == OFTString || eType == OFTDateTime || eType == OFTDate || eType == OFTTime ) + { + osSQL += "'"; + osSQL += OGRCARTODBEscapeLiteral(poFeature->GetFieldAsString(i)); + osSQL += "'"; + } + else if( (eType == OFTInteger || eType == OFTInteger64) && + poFeatureDefn->GetFieldDefn(i)->GetSubType() == OFSTBoolean ) + { + osSQL += poFeature->GetFieldAsInteger(i) ? "'t'" : "'f'"; + } + else + osSQL += poFeature->GetFieldAsString(i); + } + + for(i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++) + { + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(i); + if( poGeom == NULL ) + continue; + + if( bMustComma ) + osSQL += ", "; + else + bMustComma = TRUE; + + OGRCartoDBGeomFieldDefn* poGeomFieldDefn = + (OGRCartoDBGeomFieldDefn *)poFeatureDefn->GetGeomFieldDefn(i); + int nSRID = poGeomFieldDefn->nSRID; + if( nSRID == 0 ) + nSRID = 4326; + char* pszEWKB; + if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon && + wkbFlatten(GetGeomType()) == wkbMultiPolygon ) + { + OGRMultiPolygon* poNewGeom = new OGRMultiPolygon(); + poNewGeom->addGeometry(poGeom); + pszEWKB = OGRGeometryToHexEWKB(poNewGeom, nSRID, FALSE); + delete poNewGeom; + } + else + pszEWKB = OGRGeometryToHexEWKB(poGeom, nSRID, FALSE); + osSQL += "'"; + osSQL += pszEWKB; + osSQL += "'"; + CPLFree(pszEWKB); + } + + if( !bHasUserFieldMatchingFID ) + { + if( osFIDColName.size() && nNextFID >= 0 ) + { + if( bMustComma ) + osSQL += ", "; + else + bMustComma = TRUE; + + if( bHasJustGotNextFID ) + { + osSQL += CPLSPrintf(CPL_FRMT_GIB, nNextFID); + } + else + { + osSQL += CPLSPrintf("nextval('%s')", + OGRCARTODBEscapeLiteral(CPLSPrintf("%s_%s_seq", osName.c_str(), osFIDColName.c_str())).c_str()); + } + poFeature->SetFID(nNextFID); + nNextFID ++; + } + else if( osFIDColName.size() && poFeature->GetFID() != OGRNullFID ) + { + if( bMustComma ) + osSQL += ", "; + else + bMustComma = TRUE; + + osSQL += CPLSPrintf(CPL_FRMT_GIB, poFeature->GetFID()); + } + } + + osSQL += ")"; + } + + if( bInDeferedInsert ) + { + OGRErr eRet = OGRERR_NONE; + if( osDeferedInsertSQL.size() != 0 && + (int)osDeferedInsertSQL.size() + (int)osSQL.size() > nMaxChunkSize ) + { + osDeferedInsertSQL = "BEGIN;" + osDeferedInsertSQL + "COMMIT;"; + json_object* poObj = poDS->RunSQL(osDeferedInsertSQL); + if( poObj != NULL ) + json_object_put(poObj); + else + { + bInDeferedInsert = FALSE; + eRet = OGRERR_FAILURE; + } + osDeferedInsertSQL = ""; + } + + osDeferedInsertSQL += osSQL; + osDeferedInsertSQL += ";"; + + if( (int)osDeferedInsertSQL.size() > nMaxChunkSize ) + { + osDeferedInsertSQL = "BEGIN;" + osDeferedInsertSQL + "COMMIT;"; + json_object* poObj = poDS->RunSQL(osDeferedInsertSQL); + if( poObj != NULL ) + json_object_put(poObj); + else + { + bInDeferedInsert = FALSE; + eRet = OGRERR_FAILURE; + } + osDeferedInsertSQL = ""; + } + + return eRet; + } + + if( osFIDColName.size() ) + { + osSQL += " RETURNING "; + osSQL += OGRCARTODBEscapeIdentifier(osFIDColName); + + json_object* poObj = poDS->RunSQL(osSQL); + json_object* poRowObj = OGRCARTODBGetSingleRow(poObj); + if( poRowObj == NULL ) + { + if( poObj != NULL ) + json_object_put(poObj); + return OGRERR_FAILURE; + } + + json_object* poID = json_object_object_get(poRowObj, osFIDColName); + if( poID != NULL && json_object_get_type(poID) == json_type_int ) + { + poFeature->SetFID(json_object_get_int64(poID)); + } + + if( poObj != NULL ) + json_object_put(poObj); + + return OGRERR_NONE; + } + else + { + OGRErr eRet = OGRERR_FAILURE; + json_object* poObj = poDS->RunSQL(osSQL); + if( poObj != NULL ) + { + json_object* poTotalRows = json_object_object_get(poObj, "total_rows"); + if( poTotalRows != NULL && json_object_get_type(poTotalRows) == json_type_int ) + { + int nTotalRows = json_object_get_int(poTotalRows); + if( nTotalRows == 1 ) + { + eRet = OGRERR_NONE; + } + } + json_object_put(poObj); + } + + return eRet; + } +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRCARTODBTableLayer::ISetFeature( OGRFeature *poFeature ) + +{ + int i; + + if( bDeferedCreation && RunDeferedCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + FlushDeferedInsert(); + + GetLayerDefn(); + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + if (poFeature->GetFID() == OGRNullFID) + { + CPLError( CE_Failure, CPLE_AppDefined, + "FID required on features given to SetFeature()." ); + return OGRERR_FAILURE; + } + + CPLString osSQL; + osSQL.Printf("UPDATE %s SET ", OGRCARTODBEscapeIdentifier(osName).c_str()); + int bMustComma = FALSE; + for(i = 0; i < poFeatureDefn->GetFieldCount(); i++) + { + if( bMustComma ) + osSQL += ", "; + else + bMustComma = TRUE; + + osSQL += OGRCARTODBEscapeIdentifier(poFeatureDefn->GetFieldDefn(i)->GetNameRef()); + osSQL += " = "; + + if( !poFeature->IsFieldSet(i) ) + { + osSQL += "NULL"; + } + else + { + OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType(); + if( eType == OFTString || eType == OFTDateTime || eType == OFTDate || eType == OFTTime ) + { + osSQL += "'"; + osSQL += OGRCARTODBEscapeLiteral(poFeature->GetFieldAsString(i)); + osSQL += "'"; + } + else if( (eType == OFTInteger || eType == OFTInteger64) && + poFeatureDefn->GetFieldDefn(i)->GetSubType() == OFSTBoolean ) + { + osSQL += poFeature->GetFieldAsInteger(i) ? "'t'" : "'f'"; + } + else + osSQL += poFeature->GetFieldAsString(i); + } + } + + for(i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++) + { + if( bMustComma ) + osSQL += ", "; + else + bMustComma = TRUE; + + osSQL += OGRCARTODBEscapeIdentifier(poFeatureDefn->GetGeomFieldDefn(i)->GetNameRef()); + osSQL += " = "; + + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(i); + if( poGeom == NULL ) + { + osSQL += "NULL"; + } + else + { + OGRCartoDBGeomFieldDefn* poGeomFieldDefn = + (OGRCartoDBGeomFieldDefn *)poFeatureDefn->GetGeomFieldDefn(i); + int nSRID = poGeomFieldDefn->nSRID; + if( nSRID == 0 ) + nSRID = 4326; + char* pszEWKB = OGRGeometryToHexEWKB(poGeom, nSRID, FALSE); + osSQL += "'"; + osSQL += pszEWKB; + osSQL += "'"; + CPLFree(pszEWKB); + } + } + + osSQL += CPLSPrintf(" WHERE %s = " CPL_FRMT_GIB, + OGRCARTODBEscapeIdentifier(osFIDColName).c_str(), + poFeature->GetFID()); + + OGRErr eRet = OGRERR_FAILURE; + json_object* poObj = poDS->RunSQL(osSQL); + if( poObj != NULL ) + { + json_object* poTotalRows = json_object_object_get(poObj, "total_rows"); + if( poTotalRows != NULL && json_object_get_type(poTotalRows) == json_type_int ) + { + int nTotalRows = json_object_get_int(poTotalRows); + if( nTotalRows > 0 ) + { + eRet = OGRERR_NONE; + } + else + eRet = OGRERR_NON_EXISTING_FEATURE; + } + json_object_put(poObj); + } + + return eRet; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRCARTODBTableLayer::DeleteFeature( GIntBig nFID ) + +{ + + if( bDeferedCreation && RunDeferedCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + FlushDeferedInsert(); + + GetLayerDefn(); + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + if( osFIDColName.size() == 0 ) + return OGRERR_FAILURE; + + CPLString osSQL; + osSQL.Printf("DELETE FROM %s WHERE %s = " CPL_FRMT_GIB, + OGRCARTODBEscapeIdentifier(osName).c_str(), + OGRCARTODBEscapeIdentifier(osFIDColName).c_str(), + nFID); + + OGRErr eRet = OGRERR_FAILURE; + json_object* poObj = poDS->RunSQL(osSQL); + if( poObj != NULL ) + { + json_object* poTotalRows = json_object_object_get(poObj, "total_rows"); + if( poTotalRows != NULL && json_object_get_type(poTotalRows) == json_type_int ) + { + int nTotalRows = json_object_get_int(poTotalRows); + if( nTotalRows > 0 ) + { + eRet = OGRERR_NONE; + } + else + eRet = OGRERR_NON_EXISTING_FEATURE; + } + json_object_put(poObj); + } + + return eRet; +} + +/************************************************************************/ +/* GetSRS_SQL() */ +/************************************************************************/ + +CPLString OGRCARTODBTableLayer::GetSRS_SQL(const char* pszGeomCol) +{ + CPLString osSQL; + + osSQL.Printf("SELECT srid, srtext FROM spatial_ref_sys WHERE srid IN " + "(SELECT Find_SRID('%s', '%s', '%s'))", + OGRCARTODBEscapeLiteral(poDS->GetCurrentSchema()).c_str(), + OGRCARTODBEscapeLiteral(osName).c_str(), + OGRCARTODBEscapeLiteral(pszGeomCol).c_str()); + + return osSQL; +} + +/************************************************************************/ +/* BuildWhere() */ +/* */ +/* Build the WHERE statement appropriate to the current set of */ +/* criteria (spatial and attribute queries). */ +/************************************************************************/ + +void OGRCARTODBTableLayer::BuildWhere() + +{ + osWHERE = ""; + + if( m_poFilterGeom != NULL && + m_iGeomFieldFilter >= 0 && + m_iGeomFieldFilter < poFeatureDefn->GetGeomFieldCount() ) + { + OGREnvelope sEnvelope; + + m_poFilterGeom->getEnvelope( &sEnvelope ); + + CPLString osGeomColumn(poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)->GetNameRef()); + + char szBox3D_1[128]; + char szBox3D_2[128]; + char* pszComma; + + CPLsnprintf(szBox3D_1, sizeof(szBox3D_1), "%.18g %.18g", sEnvelope.MinX, sEnvelope.MinY); + while((pszComma = strchr(szBox3D_1, ',')) != NULL) + *pszComma = '.'; + CPLsnprintf(szBox3D_2, sizeof(szBox3D_2), "%.18g %.18g", sEnvelope.MaxX, sEnvelope.MaxY); + while((pszComma = strchr(szBox3D_2, ',')) != NULL) + *pszComma = '.'; + osWHERE.Printf("(%s && 'BOX3D(%s, %s)'::box3d)", + OGRCARTODBEscapeIdentifier(osGeomColumn).c_str(), + szBox3D_1, szBox3D_2 ); + } + + if( strlen(osQuery) > 0 ) + { + if( osWHERE.size() > 0 ) + osWHERE += " AND "; + osWHERE += osQuery; + } + + if( osFIDColName.size() == 0 ) + { + osBaseSQL = osSELECTWithoutWHERE; + if( osWHERE.size() ) + { + osBaseSQL += " WHERE "; + osBaseSQL += osWHERE; + } + } +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature* OGRCARTODBTableLayer::GetFeature( GIntBig nFeatureId ) +{ + + if( bDeferedCreation && RunDeferedCreationIfNecessary() != OGRERR_NONE ) + return NULL; + FlushDeferedInsert(); + + GetLayerDefn(); + + if( osFIDColName.size() == 0 ) + return OGRCARTODBLayer::GetFeature(nFeatureId); + + CPLString osSQL = osSELECTWithoutWHERE; + osSQL += " WHERE "; + osSQL += OGRCARTODBEscapeIdentifier(osFIDColName).c_str(); + osSQL += " = "; + osSQL += CPLSPrintf(CPL_FRMT_GIB, nFeatureId); + + json_object* poObj = poDS->RunSQL(osSQL); + json_object* poRowObj = OGRCARTODBGetSingleRow(poObj); + if( poRowObj == NULL ) + { + if( poObj != NULL ) + json_object_put(poObj); + return OGRCARTODBLayer::GetFeature(nFeatureId); + } + + OGRFeature* poFeature = BuildFeature(poRowObj); + json_object_put(poObj); + + return poFeature; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRCARTODBTableLayer::GetFeatureCount(int bForce) +{ + + if( bDeferedCreation && RunDeferedCreationIfNecessary() != OGRERR_NONE ) + return 0; + FlushDeferedInsert(); + + GetLayerDefn(); + + CPLString osSQL(CPLSPrintf("SELECT COUNT(*) FROM %s", + OGRCARTODBEscapeIdentifier(osName).c_str())); + if( osWHERE.size() ) + { + osSQL += " WHERE "; + osSQL += osWHERE; + } + + json_object* poObj = poDS->RunSQL(osSQL); + json_object* poRowObj = OGRCARTODBGetSingleRow(poObj); + if( poRowObj == NULL ) + { + if( poObj != NULL ) + json_object_put(poObj); + return OGRCARTODBLayer::GetFeatureCount(bForce); + } + + json_object* poCount = json_object_object_get(poRowObj, "count"); + if( poCount == NULL || json_object_get_type(poCount) != json_type_int ) + { + json_object_put(poObj); + return OGRCARTODBLayer::GetFeatureCount(bForce); + } + + GIntBig nRet = (GIntBig)json_object_get_int64(poCount); + + json_object_put(poObj); + + return nRet; +} + +/************************************************************************/ +/* GetExtent() */ +/* */ +/* For PostGIS use internal Extend(geometry) function */ +/* in other cases we use standard OGRLayer::GetExtent() */ +/************************************************************************/ + +OGRErr OGRCARTODBTableLayer::GetExtent( int iGeomField, OGREnvelope *psExtent, int bForce ) +{ + CPLString osSQL; + + if( bDeferedCreation && RunDeferedCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + FlushDeferedInsert(); + + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() || + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone ) + { + if( iGeomField != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } + return OGRERR_FAILURE; + } + + OGRGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->GetGeomFieldDefn(iGeomField); + + /* Do not take the spatial filter into account */ + osSQL.Printf( "SELECT ST_Extent(%s) FROM %s", + OGRCARTODBEscapeIdentifier(poGeomFieldDefn->GetNameRef()).c_str(), + OGRCARTODBEscapeIdentifier(osName).c_str()); + + json_object* poObj = poDS->RunSQL(osSQL); + json_object* poRowObj = OGRCARTODBGetSingleRow(poObj); + if( poRowObj != NULL ) + { + json_object* poExtent = json_object_object_get(poRowObj, "st_extent"); + if( poExtent != NULL && json_object_get_type(poExtent) == json_type_string ) + { + const char* pszBox = json_object_get_string(poExtent); + const char * ptr, *ptrEndParenthesis; + char szVals[64*6+6]; + + ptr = strchr(pszBox, '('); + if (ptr) + ptr ++; + if (ptr == NULL || + (ptrEndParenthesis = strchr(ptr, ')')) == NULL || + ptrEndParenthesis - ptr > (int)(sizeof(szVals) - 1)) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Bad extent representation: '%s'", pszBox); + + json_object_put(poObj); + return OGRERR_FAILURE; + } + + strncpy(szVals,ptr,ptrEndParenthesis - ptr); + szVals[ptrEndParenthesis - ptr] = '\0'; + + char ** papszTokens = CSLTokenizeString2(szVals," ,",CSLT_HONOURSTRINGS); + int nTokenCnt = 4; + + if ( CSLCount(papszTokens) != nTokenCnt ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Bad extent representation: '%s'", pszBox); + CSLDestroy(papszTokens); + + json_object_put(poObj); + return OGRERR_FAILURE; + } + + // Take X,Y coords + // For PostGis ver >= 1.0.0 -> Tokens: X1 Y1 X2 Y2 (nTokenCnt = 4) + // For PostGIS ver < 1.0.0 -> Tokens: X1 Y1 Z1 X2 Y2 Z2 (nTokenCnt = 6) + // => X2 index calculated as nTokenCnt/2 + // Y2 index caluclated as nTokenCnt/2+1 + + psExtent->MinX = CPLAtof( papszTokens[0] ); + psExtent->MinY = CPLAtof( papszTokens[1] ); + psExtent->MaxX = CPLAtof( papszTokens[nTokenCnt/2] ); + psExtent->MaxY = CPLAtof( papszTokens[nTokenCnt/2+1] ); + + CSLDestroy(papszTokens); + + json_object_put(poObj); + return OGRERR_NONE; + } + } + + if( poObj != NULL ) + json_object_put(poObj); + + if( iGeomField == 0 ) + return OGRLayer::GetExtent( psExtent, bForce ); + else + return OGRLayer::GetExtent( iGeomField, psExtent, bForce ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRCARTODBTableLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap, OLCFastFeatureCount) ) + return TRUE; + if( EQUAL(pszCap, OLCFastGetExtent) ) + return TRUE; + if( EQUAL(pszCap, OLCRandomRead) ) + { + GetLayerDefn(); + return osFIDColName.size() != 0; + } + + if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) + || EQUAL(pszCap,OLCDeleteFeature) + || EQUAL(pszCap,OLCCreateField) ) + { + return poDS->IsReadWrite(); + } + + return OGRCARTODBLayer::TestCapability(pszCap); +} + +/************************************************************************/ +/* SetDeferedCreation() */ +/************************************************************************/ + +void OGRCARTODBTableLayer::SetDeferedCreation (OGRwkbGeometryType eGType, + OGRSpatialReference* poSRS, + int bGeomNullable, + int bCartoDBify) +{ + bDeferedCreation = TRUE; + nNextFID = 1; + CPLAssert(poFeatureDefn == NULL); + this->bCartoDBify = bCartoDBify; + poFeatureDefn = new OGRFeatureDefn(osName); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType(wkbNone); + if( eGType == wkbPolygon ) + eGType = wkbMultiPolygon; + else if( eGType == wkbPolygon25D ) + eGType = wkbMultiPolygon25D; + if( eGType != wkbNone ) + { + OGRCartoDBGeomFieldDefn *poFieldDefn = + new OGRCartoDBGeomFieldDefn("the_geom", eGType); + poFieldDefn->SetNullable(bGeomNullable); + poFeatureDefn->AddGeomFieldDefn(poFieldDefn, FALSE); + if( poSRS != NULL ) + { + poFieldDefn->nSRID = poDS->FetchSRSId( poSRS ); + poFeatureDefn->GetGeomFieldDefn( + poFeatureDefn->GetGeomFieldCount() - 1)->SetSpatialRef(poSRS); + } + } + osFIDColName = "cartodb_id"; + osBaseSQL.Printf("SELECT * FROM %s", + OGRCARTODBEscapeIdentifier(osName).c_str()); +} + +/************************************************************************/ +/* RunDeferedCreationIfNecessary() */ +/************************************************************************/ + +OGRErr OGRCARTODBTableLayer::RunDeferedCreationIfNecessary() +{ + if( !bDeferedCreation ) + return OGRERR_NONE; + bDeferedCreation = FALSE; + + CPLString osSQL; + osSQL.Printf("CREATE TABLE %s ( %s SERIAL,", + OGRCARTODBEscapeIdentifier(osName).c_str(), + osFIDColName.c_str()); + + int nSRID = 0; + OGRwkbGeometryType eGType = GetGeomType(); + if( eGType != wkbNone ) + { + CPLString osGeomType = OGRToOGCGeomType(eGType); + if( wkbHasZ(eGType) ) + osGeomType += "Z"; + + OGRCartoDBGeomFieldDefn *poFieldDefn = + (OGRCartoDBGeomFieldDefn *)poFeatureDefn->GetGeomFieldDefn(0); + nSRID = poFieldDefn->nSRID; + + osSQL += CPLSPrintf("%s GEOMETRY(%s, %d)%s, %s GEOMETRY(%s, %d),", + "the_geom", + osGeomType.c_str(), + nSRID, + (!poFieldDefn->IsNullable()) ? " NOT NULL" : "", + "the_geom_webmercator", + osGeomType.c_str(), + 3857); + } + + for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(i); + if( strcmp(poFieldDefn->GetNameRef(), osFIDColName) != 0 ) + { + osSQL += OGRCARTODBEscapeIdentifier(poFieldDefn->GetNameRef()); + osSQL += " "; + osSQL += OGRPGCommonLayerGetType(*poFieldDefn, FALSE, TRUE); + if( !poFieldDefn->IsNullable() ) + osSQL += " NOT NULL"; + if( poFieldDefn->GetDefault() != NULL && !poFieldDefn->IsDefaultDriverSpecific() ) + { + osSQL += " DEFAULT "; + osSQL += poFieldDefn->GetDefault(); + } + osSQL += ","; + } + } + + osSQL += CPLSPrintf("PRIMARY KEY (%s) )", osFIDColName.c_str()); + + CPLString osSeqName(OGRCARTODBEscapeIdentifier(CPLSPrintf("%s_%s_seq", + osName.c_str(), osFIDColName.c_str()))); + + osSQL += ";"; + osSQL += CPLSPrintf("DROP SEQUENCE IF EXISTS %s CASCADE", osSeqName.c_str()); + osSQL += ";"; + osSQL += CPLSPrintf("CREATE SEQUENCE %s START 1", osSeqName.c_str()); + osSQL += ";"; + osSQL += CPLSPrintf("ALTER TABLE %s ALTER COLUMN %s SET DEFAULT nextval('%s')", + OGRCARTODBEscapeIdentifier(osName).c_str(), + osFIDColName.c_str(), osSeqName.c_str()); + + json_object* poObj = poDS->RunSQL(osSQL); + if( poObj == NULL ) + return OGRERR_FAILURE; + json_object_put(poObj); + + if( bCartoDBify ) + { + if( nSRID != 4326 ) + { + if( eGType != wkbNone ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot register table in dashboard with " + "cdb_cartodbfytable() since its SRS is not EPSG:4326"); + } + } + else + { + if( poDS->GetCurrentSchema() == "public" ) + osSQL.Printf("SELECT cdb_cartodbfytable('%s')", + OGRCARTODBEscapeLiteral(osName).c_str()); + else + osSQL.Printf("SELECT cdb_cartodbfytable('%s', '%s')", + OGRCARTODBEscapeLiteral(poDS->GetCurrentSchema()).c_str(), + OGRCARTODBEscapeLiteral(osName).c_str()); + + poObj = poDS->RunSQL(osSQL); + if( poObj == NULL ) + return OGRERR_FAILURE; + json_object_put(poObj); + } + } + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/GNUmakefile new file mode 100644 index 000000000..bb0ea7c09 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrcloudantdriver.o ogrcloudantdatasource.o ogrcloudanttablelayer.o + +CPPFLAGS := $(JSON_INCLUDE) -I.. -I../.. -I../geojson -I../couchdb $(GDAL_INCLUDE) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_cloudant.h ../../swq.h ../geojson/ogrgeojsonreader.h ../geojson/ogrgeojsonwriter.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/drv_cloudant.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/drv_cloudant.html new file mode 100644 index 000000000..172fa55d7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/drv_cloudant.html @@ -0,0 +1,113 @@ + + +CouchDB - Cloudant + + + + +

    CouchDB - Cloudant

    + +(GDAL/OGR >= 2.0.0)

    + +Cloudant and CouchDB are API compatible and based on the same core technology. The geospatial extension for Cloudant +is separate to GeoCouch. + +This driver can connect to the a Cloudant service, potentially enabled with the Cloudant spatial extension.

    +GDAL/OGR must be built with Curl support in order to the Cloudant driver to be compiled.

    +The driver supports read and write operations.

    + +

    Cloudant vs OGR concepts

    + +A Cloudant database is considered as a OGR layer. A Cloudant document is considered as a OGR feature.

    + +OGR preferably handles Cloudant documents following the GeoJSON specification.

    + +

    Dataset name syntax

    + +The syntax to open a Cloudant datasource is :
    cloudant:http://example.com[/layername]
    where +http://example.com points to the root of a CouchDB repository and, optionaly, layername is the name of a CouchDB database.

    + +It is also possible to directly open a view :

    cloudant:http://example.com/layername/_design/adesigndoc/_view/aview[?include_docs=true]
    +The include_docs=true might be needed depending on the value returned by the emit() call in the map() function.

    + +

    Authentication

    + +Some operations, in particular write operations, require authentication. The authentication can +be passed with the CLOUDANT_USERPWD environment variable set to user:password or directly in the URL.

    + +

    Filtering

    + +The driver will forward any spatial filter set with SetSpatialFilter() to the server when the Cloudant extension +is available.

    + +By default, the driver will try the following spatial filter function "_design/SpatialView/_geo/spatial", +which is the valid spatial filter function for layers created by OGR. If that filter function does not exist, +but another one exists, you can specify it with the CLOUDANT_SPATIAL_FILTER configuration option.

    +

    + +

    Paging

    + +Features are retrieved from the server by chunks of 200 by default. Cloudant uses bookmarks to page through the data. + +

    Write support

    + +Table creation and deletion is possible.

    + +Write support is only enabled when the datasource is opened in update mode.

    + +When inserting a new feature with CreateFeature(), and if the command is successful, OGR will fetch the +returned _id and _rev and use them.

    + +

    Write support and OGR transactions

    + +The CreateFeature()/SetFeature() operations are by default issued to the server synchronously with the OGR API call. This however +can cause performance penalties when issuing a lot of commands due to many client/server exchanges.

    + +It is possible to surround the CreateFeature()/SetFeature() operations between OGRLayer::StartTransaction() and OGRLayer::CommitTransaction(). +The operations will be stored into memory and only executed at the time CommitTransaction() is called.

    + +

    Layer creation options

    + +The following layer creation options are supported: +
      +
    • UPDATE_PERMISSIONS = LOGGED_USER|ALL|ADMIN|function(...)|DEFAULT : Update permissions for the new layer. +
        +
      • If set to LOGGED_USER (the default), only logged users will be able to make changes in the layer.
      • +
      • If set to ALL, all users will be able to make changes in the layer.
      • +
      • If set to ADMIN, only administrators will be able to make changes in the layer.
      • +
      • If beginning with "function(", the value of the creation option will be used as the content of the validate_doc_update function.
      • +
      • Otherwise, all users will be allowed to make changes in non-design documents.
      • +
      +
    • +
    • GEOJSON = YES|NO : Set to NO to avoid writting documents as GeoJSON documents. Default to YES.
    • +
    • COORDINATE_PRECISION = int_number : Maximum number of figures after decimal separator to write in coordinates. +Default to 15. "Smart" truncation will occur to remove trailing zeros. +Note: when opening a dataset in update mode, the OGR_CLOUDANT_COORDINATE_PRECISION configuration option can be set to have a similar role.
    • +
    + +

    Examples

    + +
  • +Listing the tables of a Cloudant repository: +
    +ogrinfo -ro "cloudant:http://some_account.some_cloudant_server.com"
    +
    +

    + +

  • +Creating and populating a table from a shapefile: +
    +ogr2ogr -f cloudant "cloudant:http://some_account.some_cloudant_server.com" shapefile.shp
    +
    +

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/makefile.vc new file mode 100644 index 000000000..2e089b2b8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrcloudantdriver.obj ogrcloudantdatasource.obj ogrcloudanttablelayer.obj +EXTRAFLAGS = -I.. -I..\.. -I..\geojson -I..\geojson\libjson -I..\couchdb + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/ogr_cloudant.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/ogr_cloudant.h new file mode 100644 index 000000000..45abe24f6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/ogr_cloudant.h @@ -0,0 +1,104 @@ +/****************************************************************************** + * $Id$ + * + * Project: Cloudant Translator + * Purpose: Definition of classes for OGR Cloudant driver. + * Author: Norman Barker, norman at cloudant com + * Based on the CouchDB driver + * + ****************************************************************************** + * Copyright (c) 2014, Norman Barker + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_CLOUDANT_H_INCLUDED +#define _OGR_CLOUDANT_H_INCLUDED + +#include "ogr_couchdb.h" + +typedef enum +{ + CLOUDANT_TABLE_LAYER +} CloudantLayerType; + +class OGRCloudantDataSource; + +/************************************************************************/ +/* OGRCloudantTableLayer */ +/************************************************************************/ + +class OGRCloudantTableLayer : public OGRCouchDBTableLayer +{ + int bHasStandardSpatial; + const char* pszSpatialView; + char* pszSpatialDDoc; + + protected: + virtual int GetFeaturesToFetch() { + return atoi(CPLGetConfigOption("CLOUDANT_PAGE_SIZE", "200")); + } + + virtual int RunSpatialFilterQueryIfNecessary(); + virtual void GetSpatialView(); + virtual void WriteMetadata(); + virtual void LoadMetadata(); + + public: + OGRCloudantTableLayer(OGRCloudantDataSource* poDS, + const char* pszName); + ~OGRCloudantTableLayer(); +}; + +/************************************************************************/ +/* OGRCloudantDataSource */ +/************************************************************************/ + +class OGRCloudantDataSource : public OGRCouchDBDataSource +{ + protected: + OGRLayer* OpenDatabase(const char* pszLayerName = NULL); + public: + OGRCloudantDataSource(); + ~OGRCloudantDataSource(); + virtual int Open( const char * pszFilename, int bUpdateIn); + virtual OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); +}; + +/************************************************************************/ +/* OGRCloudantDriver */ +/************************************************************************/ + +class OGRCloudantDriver : public OGRCouchDBDriver +{ + public: + ~OGRCloudantDriver(); + + virtual const char* GetName(); + virtual OGRDataSource* Open( const char *, int ); + virtual OGRDataSource* CreateDataSource( const char * pszName, + char **papszOptions ); + virtual int TestCapability( const char * ); + +}; + +#endif /* ndef _OGR_CLOUDANT_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/ogrcloudantdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/ogrcloudantdatasource.cpp new file mode 100644 index 000000000..833ebe7e0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/ogrcloudantdatasource.cpp @@ -0,0 +1,388 @@ +/****************************************************************************** + * $Id$ + * + * Project: Cloudant Translator + * Purpose: Definition of classes for OGR Cloudant driver. + * Author: Norman Barker, norman at cloudant com + * Based on the CouchDB driver + * + ****************************************************************************** + * Copyright (c) 2014, Norman Barker + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_cloudant.h" +#include "ogrgeojsonreader.h" +#include "ogrgeojsonwriter.h" +#include "swq.h" + +CPL_CVSID("$Id$"); + +/************************************************************************/ +/* OGRCloudantDataSource() */ +/************************************************************************/ + +OGRCloudantDataSource::OGRCloudantDataSource() + +{ + +} + +/************************************************************************/ +/* ~OGRCloudantDataSource() */ +/************************************************************************/ + +OGRCloudantDataSource::~OGRCloudantDataSource() + +{ + +} + +/************************************************************************/ +/* OpenDatabase() */ +/************************************************************************/ + +OGRLayer* OGRCloudantDataSource::OpenDatabase(const char* pszLayerName) +{ + CPLString osTableName; + CPLString osEscapedName; + + if (pszLayerName) + { + osTableName = pszLayerName; + char* pszEscapedName = CPLEscapeString(pszLayerName, -1, CPLES_URL); + osEscapedName = pszEscapedName; + CPLFree(pszEscapedName); + } + else + { + char* pszURL = CPLStrdup(osURL); + char* pszLastSlash = strrchr(pszURL, '/'); + if (pszLastSlash) + { + osEscapedName = pszLastSlash + 1; + char* pszName = CPLUnescapeString(osEscapedName, NULL, CPLES_URL); + osTableName = pszName; + CPLFree(pszName); + *pszLastSlash = 0; + } + osURL = pszURL; + CPLFree(pszURL); + pszURL = NULL; + + if (pszLastSlash == NULL) + return NULL; + } + + CPLString osURI("/"); + osURI += osEscapedName; + + json_object* poAnswerObj = GET(osURI); + if (poAnswerObj == NULL) + return NULL; + + if ( !json_object_is_type(poAnswerObj, json_type_object) || + json_object_object_get(poAnswerObj, "db_name") == NULL ) + { + IsError(poAnswerObj, "Database opening failed"); + + json_object_put(poAnswerObj); + return NULL; + } + + OGRCloudantTableLayer* poLayer = new OGRCloudantTableLayer(this, osTableName); + + if ( json_object_object_get(poAnswerObj, "update_seq") != NULL ) + { + int nUpdateSeq = json_object_get_int(json_object_object_get(poAnswerObj, "update_seq")); + poLayer->SetUpdateSeq(nUpdateSeq); + } + + json_object_put(poAnswerObj); + + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers ++] = poLayer; + + return poLayer; +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRCloudantDataSource::Open( const char * pszFilename, int bUpdateIn) + +{ + int bHTTP = FALSE; + if (strncmp(pszFilename, "http://", 7) == 0 || + strncmp(pszFilename, "https://", 8) == 0) + bHTTP = TRUE; + else if (!EQUALN(pszFilename, "cloudant:", 9)) + return FALSE; + + bReadWrite = bUpdateIn; + + pszName = CPLStrdup( pszFilename ); + + if (bHTTP) + osURL = pszFilename; + else + osURL = pszFilename + 9; + if (osURL.size() > 0 && osURL[osURL.size() - 1] == '/') + osURL.resize(osURL.size() - 1); + + const char* pszUserPwd = CPLGetConfigOption("CLOUDANT_USERPWD", NULL); + const char* pszSlash = "/"; + + if (pszUserPwd) + osUserPwd = pszUserPwd; + + if ((strstr(osURL, "/_design/") && strstr(osURL, "/_view/")) || + strstr(osURL, "/_all_docs")) + { + return OpenView() != NULL; + } + + /* If passed with https://useraccount.cloudant.com[:port]/database, do not */ + /* try to issue /all_dbs, but directly open the database */ + const char* pszKnowProvider = strstr(osURL, ".cloudant.com/"); + if (pszKnowProvider != NULL && + strchr(pszKnowProvider + strlen(".cloudant.com/"), '/' ) == NULL) + { + return OpenDatabase() != NULL; + } + + + pszKnowProvider = strstr(osURL, "localhost"); + if (pszKnowProvider != NULL && + strstr(pszKnowProvider + strlen("localhost"), pszSlash ) != NULL) + { + return OpenDatabase() != NULL; + } + + /* Get list of tables */ + json_object* poAnswerObj = GET("/_all_dbs"); + + if ( !json_object_is_type(poAnswerObj, json_type_array) ) + { + if ( json_object_is_type(poAnswerObj, json_type_object) ) + { + json_object* poError = json_object_object_get(poAnswerObj, "error"); + json_object* poReason = json_object_object_get(poAnswerObj, "reason"); + + const char* pszError = json_object_get_string(poError); + const char* pszReason = json_object_get_string(poReason); + + if (pszError && pszReason && strcmp(pszError, "not_found") == 0 && + strcmp(pszReason, "missing") == 0) + { + json_object_put(poAnswerObj); + poAnswerObj = NULL; + + CPLErrorReset(); + + return OpenDatabase() != NULL; + } + } + if (poAnswerObj == NULL) + { + IsError(poAnswerObj, "Database listing failed"); + + json_object_put(poAnswerObj); + return FALSE; + } + } + + int nTables = json_object_array_length(poAnswerObj); + + for(int i=0;iGetName()) ) + { + if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL + && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") ) + { + DeleteLayer( osLayerName ); + break; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + osLayerName.c_str()); + return NULL; + } + } + } + + char* pszEscapedName = CPLEscapeString(osLayerName, -1, CPLES_URL); + CPLString osEscapedName = pszEscapedName; + CPLFree(pszEscapedName); + +/* -------------------------------------------------------------------- */ +/* Create "database" */ +/* -------------------------------------------------------------------- */ + CPLString osURI; + osURI = "/"; + osURI += osEscapedName; + json_object* poAnswerObj = PUT(osURI, NULL); + + if (poAnswerObj == NULL) + return NULL; + + if (!IsOK(poAnswerObj, "Layer creation failed")) + { + json_object_put(poAnswerObj); + return NULL; + } + + json_object_put(poAnswerObj); + +/* -------------------------------------------------------------------- */ +/* Create "spatial index" */ +/* -------------------------------------------------------------------- */ + int nUpdateSeq = 0; + if (eGType != wkbNone) + { + char szSrid[100]; + bool bSrid = FALSE; + const char* designDoc = "_design/SpatialView"; + osURI = "/"; + osURI += osEscapedName; + osURI += "/"; + osURI += designDoc; + + if (poSpatialRef != NULL) + { + // epsg codes are supported in Cloudant + const char * pszEpsg = NULL; + const char * pszAuthName = NULL; + if (poSpatialRef->IsProjected()) + { + pszAuthName = poSpatialRef->GetAuthorityName("PROJCS"); + if ((pszAuthName != NULL) && (strncmp(pszAuthName, "EPSG", 4) == 0)) + pszEpsg = poSpatialRef->GetAuthorityCode("PROJCS"); + } + else + { + pszAuthName = poSpatialRef->GetAuthorityName("GEOGCS"); + if ((pszAuthName != NULL) && (strncmp(pszAuthName, "EPSG", 4) == 0)) + pszEpsg = poSpatialRef->GetAuthorityCode("GEOGCS"); + } + + if (pszEpsg != NULL) + { + const char * pszUrn = "urn:ogc:def:crs:epsg::"; + CPLStrlcpy(szSrid, pszUrn, sizeof(szSrid)); + if (CPLStrlcpy(szSrid + sizeof(pszUrn), pszEpsg, sizeof(szSrid)) >= sizeof(szSrid)) + { + CPLError(CE_Failure, CPLE_AppDefined, "Unable to parse SRID"); + return NULL; + } + else + bSrid = TRUE; + } + } + + // create a spatial design document and serialize it + json_object* poDoc = json_object_new_object(); + json_object* poStIndexes = json_object_new_object(); + json_object* poSpatial = json_object_new_object(); + + json_object_object_add(poDoc, "_id", + json_object_new_string(designDoc)); + json_object_object_add(poStIndexes, "spatial", poSpatial); + json_object_object_add(poSpatial, "index", + json_object_new_string("function(doc) {if (doc.geometry && doc.geometry.coordinates && doc.geometry.coordinates.length != 0){st_index(doc.geometry);}}")); + + if (bSrid) + json_object_object_add(poStIndexes, "srsid", json_object_new_string(szSrid)); + + json_object_object_add(poDoc, "st_indexes", poStIndexes); + + poAnswerObj = PUT(osURI, json_object_to_json_string(poDoc)); + + if (IsOK(poAnswerObj, "Cloudant spatial index creation failed")) + nUpdateSeq ++; + + json_object_put(poDoc); + json_object_put(poAnswerObj); + } + + int bGeoJSONDocument = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "GEOJSON", "TRUE")); + int nCoordPrecision = atoi(CSLFetchNameValueDef(papszOptions, "COORDINATE_PRECISION", "-1")); + + OGRCloudantTableLayer* poLayer = new OGRCloudantTableLayer(this, osLayerName); + if (nCoordPrecision != -1) + poLayer->SetCoordinatePrecision(nCoordPrecision); + poLayer->SetInfoAfterCreation(eGType, poSpatialRef, nUpdateSeq, bGeoJSONDocument); + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers ++] = poLayer; + return poLayer; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/ogrcloudantdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/ogrcloudantdriver.cpp new file mode 100644 index 000000000..0b68cd317 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/ogrcloudantdriver.cpp @@ -0,0 +1,118 @@ +/****************************************************************************** + * $Id$ + * + * Project: CouchDB Translator + * Purpose: Implements OGRCouchDBDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_cloudant.h" + +CPL_CVSID("$Id$"); + +extern "C" void RegisterOGRCloudant(); + +/************************************************************************/ +/* ~OGRCloudantDriver() */ +/************************************************************************/ + +OGRCloudantDriver::~OGRCloudantDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRCloudantDriver::GetName() + +{ + return "Cloudant"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRCloudantDriver::Open( const char * pszFilename, int bUpdate ) + +{ + if (!EQUALN(pszFilename, "cloudant:", 9)) + return NULL; + + OGRCloudantDataSource *poDS = new OGRCloudantDataSource(); + + if( !poDS->Open( pszFilename, bUpdate ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRCloudantDriver::CreateDataSource( const char * pszName, + CPL_UNUSED char **papszOptions ) +{ + OGRCloudantDataSource *poDS = new OGRCloudantDataSource(); + + if( !poDS->Open( pszName, TRUE ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRCloudantDriver::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap, ODrCCreateDataSource)) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRCloudant() */ +/************************************************************************/ + +void RegisterOGRCloudant() + +{ + OGRSFDriver* poDriver = new OGRCloudantDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "Cloudant / CouchDB" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( poDriver ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/ogrcloudanttablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/ogrcloudanttablelayer.cpp new file mode 100644 index 000000000..b9829e301 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/cloudant/ogrcloudanttablelayer.cpp @@ -0,0 +1,547 @@ +/****************************************************************************** + * $Id$ + * + * Project: Cloudant Translator + * Purpose: Definition of classes for OGR Cloudant driver. + * Author: Norman Barker, norman at cloudant com + * Based on CouchDB driver + * + ****************************************************************************** + * Copyright (c) 2014, Norman Barker + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_cloudant.h" +#include "ogrgeojsonreader.h" +#include "ogrgeojsonwriter.h" +#include "swq.h" + +#include + +CPL_CVSID("$Id$"); + +/************************************************************************/ +/* OGRCloudantTableLayer() */ +/************************************************************************/ + +OGRCloudantTableLayer::OGRCloudantTableLayer(OGRCloudantDataSource* poDS, + const char* pszName) : + OGRCouchDBTableLayer((OGRCouchDBDataSource*) poDS, pszName) + +{ + bHasStandardSpatial = -1; + pszSpatialView = NULL; + pszSpatialDDoc = NULL; +} + +/************************************************************************/ +/* ~OGRCouchDBTableLayer() */ +/************************************************************************/ + +OGRCloudantTableLayer::~OGRCloudantTableLayer() + +{ + if( bMustWriteMetadata ) + { + WriteMetadata(); + bMustWriteMetadata = FALSE; + } + + if (pszSpatialDDoc) + free((void*)pszSpatialDDoc); +} + +/************************************************************************/ +/* RunSpatialFilterQueryIfNecessary() */ +/************************************************************************/ + +int OGRCloudantTableLayer::RunSpatialFilterQueryIfNecessary() +{ + if (!bMustRunSpatialFilter) + return TRUE; + + bMustRunSpatialFilter = FALSE; + + CPLAssert(nOffset == 0); + + aosIdsToFetch.resize(0); + + if (pszSpatialView == NULL) + GetSpatialView(); + + OGREnvelope sEnvelope; + m_poFilterGeom->getEnvelope( &sEnvelope ); + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/"; + osURI += pszSpatialView; + osURI += "?bbox="; + osURI += CPLSPrintf("%.9f,%.9f,%.9f,%.9f", + sEnvelope.MinX, sEnvelope.MinY, + sEnvelope.MaxX, sEnvelope.MaxY); + + json_object* poAnswerObj = poDS->GET(osURI); + if (poAnswerObj == NULL) + { + CPLDebug("Cloudant", + "Cloudant geo not working --> client-side spatial filtering"); + bServerSideSpatialFilteringWorks = FALSE; + return FALSE; + } + + if ( !json_object_is_type(poAnswerObj, json_type_object) ) + { + CPLDebug("Cloudant", + "Cloudant geo not working --> client-side spatial filtering"); + bServerSideSpatialFilteringWorks = FALSE; + CPLError(CE_Failure, CPLE_AppDefined, + "FetchNextRowsSpatialFilter() failed"); + json_object_put(poAnswerObj); + return FALSE; + } + + /* Catch error for a non cloudant geo database */ + json_object* poError = json_object_object_get(poAnswerObj, "error"); + json_object* poReason = json_object_object_get(poAnswerObj, "reason"); + + const char* pszError = json_object_get_string(poError); + const char* pszReason = json_object_get_string(poReason); + + if (pszError && pszReason && strcmp(pszError, "not_found") == 0 && + strcmp(pszReason, "Document is missing attachment") == 0) + { + CPLDebug("Cloudant", + "Cloudant geo not working --> client-side spatial filtering"); + bServerSideSpatialFilteringWorks = FALSE; + json_object_put(poAnswerObj); + return FALSE; + } + + if (poDS->IsError(poAnswerObj, "FetchNextRowsSpatialFilter() failed")) + { + CPLDebug("Cloudant", + "Cloudant geo not working --> client-side spatial filtering"); + bServerSideSpatialFilteringWorks = FALSE; + json_object_put(poAnswerObj); + return FALSE; + } + + json_object* poRows = json_object_object_get(poAnswerObj, "rows"); + if (poRows == NULL || + !json_object_is_type(poRows, json_type_array)) + { + CPLDebug("Cloudant", + "Cloudant geo not working --> client-side spatial filtering"); + bServerSideSpatialFilteringWorks = FALSE; + CPLError(CE_Failure, CPLE_AppDefined, + "FetchNextRowsSpatialFilter() failed"); + json_object_put(poAnswerObj); + return FALSE; + } + + int nRows = json_object_array_length(poRows); + for(int i=0;iGET(osURI); + bHasStandardSpatial = (poAnswerObj != NULL && + json_object_is_type(poAnswerObj, json_type_object) && + json_object_object_get(poAnswerObj, "st_indexes") != NULL); + json_object_put(poAnswerObj); + } + + if (bHasStandardSpatial) + pszSpatialView = "_design/SpatialView/_geo/spatial"; + + papszTokens = + CSLTokenizeString2( pszSpatialView, "/", 0); + + if ((papszTokens[0] == NULL) || (papszTokens[1] == NULL)) + { + CPLError(CE_Failure, CPLE_AppDefined, "GetSpatialView() failed, invalid spatial design doc."); + return; + } + + pszSpatialDDoc = (char*) calloc(strlen(papszTokens[0]) + strlen(papszTokens[1]) + 2, 1); + + sprintf(pszSpatialDDoc, "%s/%s", papszTokens[0], papszTokens[1]); + + CSLDestroy(papszTokens); + + } +} + +/************************************************************************/ +/* WriteMetadata() */ +/************************************************************************/ + +void OGRCloudantTableLayer::WriteMetadata() +{ + GetLayerDefn(); + + if (pszSpatialDDoc == NULL) + GetSpatialView(); + if( pszSpatialDDoc == NULL ) + return; + + CPLString osURI; + osURI = "/"; + osURI += osEscapedName; + osURI += "/"; + osURI += pszSpatialDDoc; + + + json_object* poDDocObj = poDS->GET(osURI); + if (poDDocObj == NULL) + return; + + if ( !json_object_is_type(poDDocObj, json_type_object) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "WriteMetadata() failed"); + json_object_put(poDDocObj); + return; + } + + json_object* poError = json_object_object_get(poDDocObj, "error"); + const char* pszError = json_object_get_string(poError); + if (pszError && strcmp(pszError, "not_found") == 0) + { + json_object_put(poDDocObj); + return; + } + + if (poDS->IsError(poDDocObj, "WriteMetadata() failed")) + { + json_object_put(poDDocObj); + return; + } + + + if (poSRS) + { + // epsg codes are supported in Cloudant + const char * pszEpsg = NULL; + const char * pszAuthName = NULL; + char szSrid[100]; + + if (poSRS->IsProjected()) + { + pszAuthName = poSRS->GetAuthorityName("PROJCS"); + if ((pszAuthName != NULL) && (strncmp(pszAuthName, "EPSG", 4) == 0)) + pszEpsg = poSRS->GetAuthorityCode("PROJCS"); + } + else + { + pszAuthName = poSRS->GetAuthorityName("GEOGCS"); + if ((pszAuthName != NULL) && (strncmp(pszAuthName, "EPSG", 4) == 0)) + pszEpsg = poSRS->GetAuthorityCode("GEOGCS"); + } + + if (pszEpsg != NULL) + { + const char * pszUrn = "urn:ogc:def:crs:epsg::"; + CPLStrlcpy(szSrid, pszUrn, sizeof(szSrid)); + if (CPLStrlcpy(szSrid + sizeof(pszUrn), pszEpsg, sizeof(szSrid)) <= sizeof(szSrid)) + { + json_object_object_add(poDDocObj, "srsid", + json_object_new_string(pszUrn)); + + } + } + } + + if (eGeomType != wkbNone) + { + json_object_object_add(poDDocObj, "geomtype", + json_object_new_string(OGRToOGCGeomType(eGeomType))); + if (wkbHasZ(poFeatureDefn->GetGeomType())) + { + json_object_object_add(poDDocObj, "is_25D", + json_object_new_boolean(TRUE)); + } + } + else + { + json_object_object_add(poDDocObj, "geomtype", + json_object_new_string("NONE")); + } + + json_object_object_add(poDDocObj, "geojson_documents", + json_object_new_boolean(bGeoJSONDocument)); + + json_object* poFields = json_object_new_array(); + json_object_object_add(poDDocObj, "fields", poFields); + + for(int i=FIRST_FIELD;iGetFieldCount();i++) + { + json_object* poField = json_object_new_object(); + json_object_array_add(poFields, poField); + + json_object_object_add(poField, "name", + json_object_new_string(poFeatureDefn->GetFieldDefn(i)->GetNameRef())); + + const char* pszType = NULL; + switch (poFeatureDefn->GetFieldDefn(i)->GetType()) + { + case OFTInteger: pszType = "integer"; break; + case OFTReal: pszType = "real"; break; + case OFTString: pszType = "string"; break; + case OFTIntegerList: pszType = "integerlist"; break; + case OFTRealList: pszType = "reallist"; break; + case OFTStringList: pszType = "stringlist"; break; + default: pszType = "string"; break; + } + + json_object_object_add(poField, "type", + json_object_new_string(pszType)); + } + + json_object* poAnswerObj = poDS->PUT(osURI, + json_object_to_json_string(poDDocObj)); + + json_object_put(poDDocObj); + json_object_put(poAnswerObj); +} + +/************************************************************************/ +/* OGRCloudantIsNumericObject() */ +/************************************************************************/ + +static int OGRCloudantIsNumericObject(json_object* poObj) +{ + int iType = json_object_get_type(poObj); + return iType == json_type_int || iType == json_type_double; +} + +/************************************************************************/ +/* LoadMetadata() */ +/************************************************************************/ + +void OGRCloudantTableLayer::LoadMetadata() +{ + if (bHasLoadedMetadata) + return; + + bHasLoadedMetadata = TRUE; + + if (pszSpatialDDoc == NULL) + GetSpatialView(); + if( pszSpatialDDoc == NULL ) + return; + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/"; + osURI += pszSpatialDDoc; + + json_object* poAnswerObj = poDS->GET(osURI); + if (poAnswerObj == NULL) + return; + + if ( !json_object_is_type(poAnswerObj, json_type_object) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "LoadMetadata() failed"); + json_object_put(poAnswerObj); + return; + } + + json_object* poRev = json_object_object_get(poAnswerObj, "_rev"); + const char* pszRev = json_object_get_string(poRev); + if (pszRev) + osMetadataRev = pszRev; + + json_object* poError = json_object_object_get(poAnswerObj, "error"); + const char* pszError = json_object_get_string(poError); + if (pszError && strcmp(pszError, "not_found") == 0) + { + json_object_put(poAnswerObj); + return; + } + + if (poDS->IsError(poAnswerObj, "LoadMetadata() failed")) + { + json_object_put(poAnswerObj); + return; + } + + json_object* poJsonSRS = json_object_object_get(poAnswerObj, "srsid"); + const char* pszSRS = json_object_get_string(poJsonSRS); + if (pszSRS != NULL) + { + poSRS = new OGRSpatialReference(); + if (poSRS->importFromURN(pszSRS) != OGRERR_NONE) + { + delete poSRS; + poSRS = NULL; + } + } + + json_object* poGeomType = json_object_object_get(poAnswerObj, "geomtype"); + const char* pszGeomType = json_object_get_string(poGeomType); + + if (pszGeomType) + { + if (EQUAL(pszGeomType, "NONE")) + { + eGeomType = wkbNone; + bExtentValid = TRUE; + } + else + { + eGeomType = OGRFromOGCGeomType(pszGeomType); + + json_object* poIs25D = json_object_object_get(poAnswerObj, "is_25D"); + if (poIs25D && json_object_get_boolean(poIs25D)) + eGeomType = wkbSetZ(eGeomType); + + json_object* poExtent = json_object_object_get(poAnswerObj, "extent"); + if (poExtent && json_object_get_type(poExtent) == json_type_object) + { + json_object* poBbox = json_object_object_get(poExtent, "bbox"); + if (poBbox && + json_object_get_type(poBbox) == json_type_array && + json_object_array_length(poBbox) == 4 && + OGRCloudantIsNumericObject(json_object_array_get_idx(poBbox, 0)) && + OGRCloudantIsNumericObject(json_object_array_get_idx(poBbox, 1)) && + OGRCloudantIsNumericObject(json_object_array_get_idx(poBbox, 2)) && + OGRCloudantIsNumericObject(json_object_array_get_idx(poBbox, 3))) + { + dfMinX = json_object_get_double(json_object_array_get_idx(poBbox, 0)); + dfMinY = json_object_get_double(json_object_array_get_idx(poBbox, 1)); + dfMaxX = json_object_get_double(json_object_array_get_idx(poBbox, 2)); + dfMaxY = json_object_get_double(json_object_array_get_idx(poBbox, 3)); + bExtentValid = bExtentSet = TRUE; + } + } + } + } + + json_object* poGeoJSON = json_object_object_get(poAnswerObj, "geojson_documents"); + if (poGeoJSON && json_object_is_type(poGeoJSON, json_type_boolean)) + bGeoJSONDocument = json_object_get_boolean(poGeoJSON); + + json_object* poFields = json_object_object_get(poAnswerObj, "fields"); + if (poFields && json_object_is_type(poFields, json_type_array)) + { + poFeatureDefn = new OGRFeatureDefn( osName ); + poFeatureDefn->Reference(); + + poFeatureDefn->SetGeomType(eGeomType); + if( poFeatureDefn->GetGeomFieldCount() != 0 ) + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + OGRFieldDefn oFieldId("_id", OFTString); + poFeatureDefn->AddFieldDefn(&oFieldId); + + OGRFieldDefn oFieldRev("_rev", OFTString); + poFeatureDefn->AddFieldDefn(&oFieldRev); + + int nFields = json_object_array_length(poFields); + for(int i=0;iAddFieldDefn(&oField); + } + } + } + } + + std::sort(aosIdsToFetch.begin(), aosIdsToFetch.end()); + + json_object_put(poAnswerObj); + + return; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/GNUmakefile new file mode 100644 index 000000000..96aea419c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrcouchdbdriver.o ogrcouchdbdatasource.o ogrcouchdblayer.o ogrcouchdbtablelayer.o ogrcouchdbrowslayer.o + +CPPFLAGS := $(JSON_INCLUDE) -I.. -I../.. -I../geojson $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_couchdb.h ../../swq.h ../geojson/ogrgeojsonreader.h ../geojson/ogrgeojsonwriter.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/drv_couchdb.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/drv_couchdb.html new file mode 100644 index 000000000..a8ae4155a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/drv_couchdb.html @@ -0,0 +1,114 @@ + + +CouchDB - CouchDB/GeoCouch + + + + +

    CouchDB - CouchDB/GeoCouch

    + +(GDAL/OGR >= 1.9.0)

    + +This driver can connect to the a CouchDB service, potentially enabled with the GeoCouch spatial extension.

    +GDAL/OGR must be built with Curl support in order to the CouchDB driver to be compiled.

    +The driver supports read and write operations.

    + +

    CouchDB vs OGR concepts

    + +A CouchDB database is considered as a OGR layer. A CouchDB document is considered as a OGR feature.

    + +OGR handles preferably CouchDB documents following the GeoJSON specification.

    + +

    Dataset name syntax

    + +The syntax to open a CouchDB datasource is :
    couchdb:http://example.com[/layername]
    where +http://example.com points to the root of a CouchDB repository and, optionaly, layername is the name of a CouchDB database.

    + +It is also possible to directly open a view :

    couchdb:http://example.com/layername/_design/adesigndoc/_view/aview[?include_docs=true]
    +The include_docs=true might be needed depending on the value returned by the emit() call in the map() function.

    + +

    Authentication

    + +Some operations, in particular write operations, require authentication. The authentication can +be passed with the COUCHDB_USERPWD environment variable set to user:password or directly in the URL.

    + +

    Filtering

    + +The driver will forward any spatial filter set with SetSpatialFilter() to the server when GeoCouch extension +is available. It also makes the same for (very simple) attribute filters set with SetAttributeFilter(). When +server-side filtering fails, it will default back to client-side filtering.

    + +By default, the driver will try the following spatial filter function "_design/ogr_spatial/_spatial/spatial", +which is the valid spatial filter function for layers created by OGR. If that filter function does not exist, +but another one exists, you can specify it with the COUCHDB_SPATIAL_FILTER configuration option.

    + +Note that the first time an attribute request is issued, it might require write permissions in the database +to create a new index view.

    + +

    Paging

    + +Features are retrieved from the server by chunks of 500 by default. This number can be altered with the COUCHDB_PAGE_SIZE +configuration option.

    + +

    Write support

    + +Table creation and deletion is possible.

    + +Write support is only enabled when the datasource is opened in update mode.

    + +When inserting a new feature with CreateFeature(), and if the command is successful, OGR will fetch the +returned _id and _rev and use them.

    + +

    Write support and OGR transactions

    + +The CreateFeature()/SetFeature() operations are by default issued to the server synchronously with the OGR API call. This however +can cause performance penalties when issuing a lot of commands due to many client/server exchanges.

    + +It is possible to surround the CreateFeature()/SetFeature() operations between OGRLayer::StartTransaction() and OGRLayer::CommitTransaction(). +The operations will be stored into memory and only executed at the time CommitTransaction() is called.

    + +

    Layer creation options

    + +The following layer creation options are supported: +
      +
    • UPDATE_PERMISSIONS = LOGGED_USER|ALL|ADMIN|function(...)|DEFAULT : Update permissions for the new layer. +
        +
      • If set to LOGGED_USER (the default), only logged users will be able to make changes in the layer.
      • +
      • If set to ALL, all users will be able to make changes in the layer.
      • +
      • If set to ADMIN, only administrators will be able to make changes in the layer.
      • +
      • If beginning with "function(", the value of the creation option will be used as the content of the validate_doc_update function.
      • +
      • Otherwise, all users will be allowed to make changes in non-design documents.
      • +
      +
    • +
    • GEOJSON = YES|NO : Set to NO to avoid writting documents as GeoJSON documents. Default to YES.
    • +
    • COORDINATE_PRECISION = int_number : Maximum number of figures after decimal separator to write in coordinates. +Default to 15. "Smart" truncation will occur to remove trailing zeros. +Note: when opening a dataset in update mode, the OGR_COUCHDB_COORDINATE_PRECISION configuration option can be set to have a similar role.
    • +
    + +

    Examples

    + +
  • +Listing the tables of a CouchDB repository: +
    +ogrinfo -ro "couchdb:http://some_account.some_couchdb_server.com"
    +
    +

    + +

  • +Creating and populating a table from a shapefile: +
    +ogr2ogr -f couchdb "couchdb:http://some_account.some_couchdb_server.com" shapefile.shp
    +
    +

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/makefile.vc new file mode 100644 index 000000000..38224d2f3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrcouchdbdriver.obj ogrcouchdbdatasource.obj ogrcouchdblayer.obj ogrcouchdbtablelayer.obj ogrcouchdbrowslayer.obj +EXTRAFLAGS = -I.. -I..\.. -I..\geojson -I..\geojson\libjson + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogr_couchdb.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogr_couchdb.h new file mode 100644 index 000000000..f543034c3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogr_couchdb.h @@ -0,0 +1,318 @@ +/****************************************************************************** + * $Id: ogr_couchdb.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: CouchDB Translator + * Purpose: Definition of classes for OGR CouchDB / GeoCouch driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_COUCHDB_H_INCLUDED +#define _OGR_COUCHDB_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_http.h" +#include + +#include +#include + +#define _ID_FIELD 0 +#define _REV_FIELD 1 +#define FIRST_FIELD 2 + +typedef enum +{ + COUCHDB_TABLE_LAYER, + COUCHDB_ROWS_LAYER +} CouchDBLayerType; + +/************************************************************************/ +/* OGRCouchDBLayer */ +/************************************************************************/ +class OGRCouchDBDataSource; + +class OGRCouchDBLayer : public OGRLayer +{ +protected: + OGRCouchDBDataSource* poDS; + + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference* poSRS; + + int nNextInSeq; + int nOffset; + int bEOF; + + json_object* poFeatures; + std::vector aoFeatures; + + OGRFeature* GetNextRawFeature(); + OGRFeature* TranslateFeature( json_object* poObj ); + void ParseFieldValue(OGRFeature* poFeature, + const char* pszKey, + json_object* poValue); + + int FetchNextRowsAnalyseDocs(json_object* poAnswerObj); + virtual int FetchNextRows() = 0; + + int bGeoJSONDocument; + + void BuildFeatureDefnFromDoc(json_object* poDoc); + int BuildFeatureDefnFromRows(json_object* poAnswerObj); + + virtual int GetFeaturesToFetch() { return atoi(CPLGetConfigOption("COUCHDB_PAGE_SIZE", "500")); } + + public: + OGRCouchDBLayer(OGRCouchDBDataSource* poDS); + ~OGRCouchDBLayer(); + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn(); + + virtual int TestCapability( const char * ); + + virtual CouchDBLayerType GetLayerType() = 0; + + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + + virtual OGRSpatialReference * GetSpatialRef(); +}; + +/************************************************************************/ +/* OGRCouchDBTableLayer */ +/************************************************************************/ + +class OGRCouchDBTableLayer : public OGRCouchDBLayer +{ + int nNextFIDForCreate; + int bInTransaction; + std::vector aoTransactionFeatures; + + virtual int FetchNextRows(); + + int bHasOGRSpatial; + int bHasGeocouchUtilsMinimalSpatialView; + int bServerSideAttributeFilteringWorks; + int FetchNextRowsSpatialFilter(); + + int bHasInstalledAttributeFilter; + CPLString osURIAttributeFilter; + std::map oMapFilterFields; + CPLString BuildAttrQueryURI(int& bOutHasStrictComparisons); + int FetchNextRowsAttributeFilter(); + + int GetTotalFeatureCount(); + int GetMaximumId(); + + int nUpdateSeq; + int bAlwaysValid; + int FetchUpdateSeq(); + + int nCoordPrecision; + + OGRFeature* GetFeature( const char* pszId ); + OGRErr DeleteFeature( OGRFeature* poFeature ); + + protected: + + CPLString osName; + CPLString osEscapedName; + int bMustWriteMetadata; + int bMustRunSpatialFilter; + std::vector aosIdsToFetch; + int bServerSideSpatialFilteringWorks; + int bHasLoadedMetadata; + CPLString osMetadataRev; + int bExtentValid; + + int bExtentSet; + double dfMinX; + double dfMinY; + double dfMaxX; + double dfMaxY; + + OGRwkbGeometryType eGeomType; + + virtual void WriteMetadata(); + virtual void LoadMetadata(); + virtual int RunSpatialFilterQueryIfNecessary(); + + public: + OGRCouchDBTableLayer(OGRCouchDBDataSource* poDS, + const char* pszName); + ~OGRCouchDBTableLayer(); + + virtual void ResetReading(); + + virtual OGRFeatureDefn * GetLayerDefn(); + + virtual const char * GetName() { return osName.c_str(); } + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual OGRFeature * GetFeature( GIntBig nFID ); + + virtual void SetSpatialFilter( OGRGeometry * ); + virtual OGRErr SetAttributeFilter( const char * ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual int TestCapability( const char * ); + + void SetInfoAfterCreation(OGRwkbGeometryType eGType, + OGRSpatialReference* poSRSIn, + int nUpdateSeqIn, + int bGeoJSONDocumentIn); + + void SetUpdateSeq(int nUpdateSeqIn) { nUpdateSeq = nUpdateSeqIn; }; + + int HasFilterOnFieldOrCreateIfNecessary(const char* pszFieldName); + + void SetCoordinatePrecision(int nCoordPrecision) { this->nCoordPrecision = nCoordPrecision; } + + virtual CouchDBLayerType GetLayerType() { return COUCHDB_TABLE_LAYER; } + + OGRErr DeleteFeature( const char* pszId ); +}; + +/************************************************************************/ +/* OGRCouchDBRowsLayer */ +/************************************************************************/ + +class OGRCouchDBRowsLayer : public OGRCouchDBLayer +{ + int bAllInOne; + + virtual int FetchNextRows(); + + public: + OGRCouchDBRowsLayer(OGRCouchDBDataSource* poDS); + ~OGRCouchDBRowsLayer(); + + virtual void ResetReading(); + + int BuildFeatureDefn(); + + virtual CouchDBLayerType GetLayerType() { return COUCHDB_TABLE_LAYER; } +}; + +/************************************************************************/ +/* OGRCouchDBDataSource */ +/************************************************************************/ + +class OGRCouchDBDataSource : public OGRDataSource +{ + protected: + char* pszName; + + OGRLayer** papoLayers; + int nLayers; + + int bReadWrite; + + int bMustCleanPersistant; + + CPLString osURL; + CPLString osUserPwd; + + json_object* REQUEST(const char* pszVerb, + const char* pszURI, + const char* pszData); + + OGRLayer* OpenDatabase(const char* pszLayerName = NULL); + OGRLayer* OpenView(); + void DeleteLayer( const char *pszLayerName ); + + OGRLayer * ExecuteSQLStats( const char *pszSQLCommand ); + + public: + OGRCouchDBDataSource(); + ~OGRCouchDBDataSource(); + + int Open( const char * pszFilename, + int bUpdate ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + virtual OGRLayer *GetLayerByName(const char *); + + virtual int TestCapability( const char * ); + + virtual OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + virtual OGRErr DeleteLayer(int); + + virtual OGRLayer* ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + int IsReadWrite() const { return bReadWrite; } + + char* GetETag(const char* pszURI); + json_object* GET(const char* pszURI); + json_object* PUT(const char* pszURI, const char* pszData); + json_object* POST(const char* pszURI, const char* pszData); + json_object* DELETE(const char* pszURI); + + const CPLString& GetURL() const { return osURL; } + + static int IsError(json_object* poAnswerObj, + const char* pszErrorMsg); + static int IsOK (json_object* poAnswerObj, + const char* pszErrorMsg); +}; + +/************************************************************************/ +/* OGRCouchDBDriver */ +/************************************************************************/ + +class OGRCouchDBDriver : public OGRSFDriver +{ + public: + ~OGRCouchDBDriver(); + + virtual const char* GetName(); + virtual OGRDataSource* Open( const char *, int ); + virtual OGRDataSource* CreateDataSource( const char * pszName, + char **papszOptions ); + virtual int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_COUCHDB_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogrcouchdbdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogrcouchdbdatasource.cpp new file mode 100644 index 000000000..1891a3493 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogrcouchdbdatasource.cpp @@ -0,0 +1,1252 @@ +/****************************************************************************** + * $Id: ogrcouchdbdatasource.cpp 29101 2015-05-01 22:17:43Z rouault $ + * + * Project: CouchDB Translator + * Purpose: Implements OGRCouchDBDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_couchdb.h" +#include "swq.h" + +CPL_CVSID("$Id: ogrcouchdbdatasource.cpp 29101 2015-05-01 22:17:43Z rouault $"); + +/************************************************************************/ +/* OGRCouchDBDataSource() */ +/************************************************************************/ + +OGRCouchDBDataSource::OGRCouchDBDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; + + bReadWrite = FALSE; + + bMustCleanPersistant = FALSE; +} + +/************************************************************************/ +/* ~OGRCouchDBDataSource() */ +/************************************************************************/ + +OGRCouchDBDataSource::~OGRCouchDBDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + if (bMustCleanPersistant) + { + char** papszOptions = NULL; + papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT", CPLSPrintf("CouchDB:%p", this)); + CPLHTTPFetch( osURL, papszOptions); + CSLDestroy(papszOptions); + } + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRCouchDBDataSource::TestCapability( const char * pszCap ) + +{ + if( bReadWrite && EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else if( bReadWrite && EQUAL(pszCap,ODsCDeleteLayer) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRCouchDBDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer *OGRCouchDBDataSource::GetLayerByName(const char * pszLayerName) +{ + OGRLayer* poLayer = OGRDataSource::GetLayerByName(pszLayerName); + if (poLayer) + return poLayer; + + return OpenDatabase(pszLayerName); +} + +/************************************************************************/ +/* OpenDatabase() */ +/************************************************************************/ + +OGRLayer* OGRCouchDBDataSource::OpenDatabase(const char* pszLayerName) +{ + CPLString osTableName; + CPLString osEscapedName; + + if (pszLayerName) + { + osTableName = pszLayerName; + char* pszEscapedName = CPLEscapeString(pszLayerName, -1, CPLES_URL); + osEscapedName = pszEscapedName; + CPLFree(pszEscapedName); + } + else + { + char* pszURL = CPLStrdup(osURL); + char* pszLastSlash = strrchr(pszURL, '/'); + if (pszLastSlash) + { + osEscapedName = pszLastSlash + 1; + char* pszName = CPLUnescapeString(osEscapedName, NULL, CPLES_URL); + osTableName = pszName; + CPLFree(pszName); + *pszLastSlash = 0; + } + osURL = pszURL; + CPLFree(pszURL); + pszURL = NULL; + + if (pszLastSlash == NULL) + return NULL; + } + + CPLString osURI("/"); + osURI += osEscapedName; + + json_object* poAnswerObj = GET(osURI); + if (poAnswerObj == NULL) + return NULL; + + if ( !json_object_is_type(poAnswerObj, json_type_object) || + json_object_object_get(poAnswerObj, "db_name") == NULL ) + { + IsError(poAnswerObj, "Database opening failed"); + + json_object_put(poAnswerObj); + return NULL; + } + + OGRCouchDBTableLayer* poLayer = new OGRCouchDBTableLayer(this, osTableName); + + if ( json_object_object_get(poAnswerObj, "update_seq") != NULL ) + { + int nUpdateSeq = json_object_get_int(json_object_object_get(poAnswerObj, "update_seq")); + poLayer->SetUpdateSeq(nUpdateSeq); + } + + json_object_put(poAnswerObj); + + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers ++] = poLayer; + + return poLayer; +} + +/************************************************************************/ +/* OpenView() */ +/************************************************************************/ + +OGRLayer* OGRCouchDBDataSource::OpenView() +{ + OGRCouchDBRowsLayer* poLayer = new OGRCouchDBRowsLayer(this); + if (!poLayer->BuildFeatureDefn()) + { + delete poLayer; + return NULL; + } + + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers ++] = poLayer; + + return poLayer; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRCouchDBDataSource::Open( const char * pszFilename, int bUpdateIn) + +{ + int bHTTP = FALSE; + if (strncmp(pszFilename, "http://", 7) == 0 || + strncmp(pszFilename, "https://", 8) == 0) + bHTTP = TRUE; + else if (!EQUALN(pszFilename, "CouchDB:", 8)) + return FALSE; + + bReadWrite = bUpdateIn; + + pszName = CPLStrdup( pszFilename ); + + if (bHTTP) + osURL = pszFilename; + else + osURL = pszFilename + 8; + if (osURL.size() > 0 && osURL[osURL.size() - 1] == '/') + osURL.resize(osURL.size() - 1); + + const char* pszUserPwd = CPLGetConfigOption("COUCHDB_USERPWD", NULL); + if (pszUserPwd) + osUserPwd = pszUserPwd; + + + if ((strstr(osURL, "/_design/") && strstr(osURL, "/_view/")) || + strstr(osURL, "/_all_docs")) + { + return OpenView() != NULL; + } + + /* If passed with http://useraccount.knownprovider.com/database, do not */ + /* try to issue /all_dbs, but directly open the database */ + const char* pszKnowProvider = strstr(osURL, ".iriscouch.com/"); + if (pszKnowProvider != NULL && + strchr(pszKnowProvider + strlen(".iriscouch.com/"), '/' ) == NULL) + { + return OpenDatabase() != NULL; + } + pszKnowProvider = strstr(osURL, ".cloudant.com/"); + if (pszKnowProvider != NULL && + strchr(pszKnowProvider + strlen(".cloudant.com/"), '/' ) == NULL) + { + return OpenDatabase() != NULL; + } + + /* Get list of tables */ + json_object* poAnswerObj = GET("/_all_dbs"); + + if (poAnswerObj == NULL) + { + if (!EQUALN(pszFilename, "CouchDB:", 8)) + CPLErrorReset(); + return FALSE; + } + + if ( !json_object_is_type(poAnswerObj, json_type_array) ) + { + if ( json_object_is_type(poAnswerObj, json_type_object) ) + { + json_object* poError = json_object_object_get(poAnswerObj, "error"); + json_object* poReason = json_object_object_get(poAnswerObj, "reason"); + + const char* pszError = json_object_get_string(poError); + const char* pszReason = json_object_get_string(poReason); + + if (pszError && pszReason && strcmp(pszError, "not_found") == 0 && + strcmp(pszReason, "missing") == 0) + { + json_object_put(poAnswerObj); + poAnswerObj = NULL; + + CPLErrorReset(); + + return OpenDatabase() != NULL; + } + } + if (poAnswerObj) + { + IsError(poAnswerObj, "Database listing failed"); + + json_object_put(poAnswerObj); + return FALSE; + } + } + + int nTables = json_object_array_length(poAnswerObj); + for(int i=0;iGetName()) ) + { + if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL + && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") ) + { + DeleteLayer( pszName ); + break; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + pszName ); + return NULL; + } + } + } + + char* pszEscapedName = CPLEscapeString(pszName, -1, CPLES_URL); + CPLString osEscapedName = pszEscapedName; + CPLFree(pszEscapedName); + +/* -------------------------------------------------------------------- */ +/* Create "database" */ +/* -------------------------------------------------------------------- */ + CPLString osURI; + osURI = "/"; + osURI += osEscapedName; + json_object* poAnswerObj = PUT(osURI, NULL); + + if (poAnswerObj == NULL) + return NULL; + + if (!IsOK(poAnswerObj, "Layer creation failed")) + { + json_object_put(poAnswerObj); + return NULL; + } + + json_object_put(poAnswerObj); + +/* -------------------------------------------------------------------- */ +/* Create "spatial index" */ +/* -------------------------------------------------------------------- */ + int nUpdateSeq = 0; + if (eGType != wkbNone) + { + osURI = "/"; + osURI += osEscapedName; + osURI += "/_design/ogr_spatial"; + + CPLString osContent("{ \"spatial\": { \"spatial\" : \"function(doc) { if (doc.geometry && doc.geometry.coordinates && doc.geometry.coordinates.length != 0) { emit(doc.geometry, null); } } \" } }"); + + poAnswerObj = PUT(osURI, osContent); + + if (IsOK(poAnswerObj, "Spatial index creation failed")) + nUpdateSeq ++; + + json_object_put(poAnswerObj); + } + + +/* -------------------------------------------------------------------- */ +/* Create validation function */ +/* -------------------------------------------------------------------- */ + const char* pszUpdatePermissions = CSLFetchNameValueDef(papszOptions, "UPDATE_PERMISSIONS", "LOGGED_USER"); + CPLString osValidation; + if (EQUAL(pszUpdatePermissions, "LOGGED_USER")) + { + osValidation = "{\"validate_doc_update\": \"function(new_doc, old_doc, userCtx) { if(!userCtx.name) { throw({forbidden: \\\"Please log in first.\\\"}); } }\" }"; + } + else if (EQUAL(pszUpdatePermissions, "ALL")) + { + osValidation = "{\"validate_doc_update\": \"function(new_doc, old_doc, userCtx) { }\" }"; + } + else if (EQUAL(pszUpdatePermissions, "ADMIN")) + { + osValidation = "{\"validate_doc_update\": \"function(new_doc, old_doc, userCtx) {if (userCtx.roles.indexOf('_admin') === -1) { throw({forbidden: \\\"No changes allowed except by admin.\\\"}); } }\" }"; + } + else if (strncmp(pszUpdatePermissions, "function(", 9) == 0) + { + osValidation = "{\"validate_doc_update\": \""; + osValidation += pszUpdatePermissions; + osValidation += "\"}"; + } + + if (osValidation.size()) + { + osURI = "/"; + osURI += osEscapedName; + osURI += "/_design/ogr_validation"; + + poAnswerObj = PUT(osURI, osValidation); + + if (IsOK(poAnswerObj, "Validation function creation failed")) + nUpdateSeq ++; + + json_object_put(poAnswerObj); + } + + int bGeoJSONDocument = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "GEOJSON", "TRUE")); + int nCoordPrecision = atoi(CSLFetchNameValueDef(papszOptions, "COORDINATE_PRECISION", "-1")); + + OGRCouchDBTableLayer* poLayer = new OGRCouchDBTableLayer(this, pszName); + if (nCoordPrecision != -1) + poLayer->SetCoordinatePrecision(nCoordPrecision); + poLayer->SetInfoAfterCreation(eGType, poSpatialRef, nUpdateSeq, bGeoJSONDocument); + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers ++] = poLayer; + return poLayer; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +void OGRCouchDBDataSource::DeleteLayer( const char *pszLayerName ) + +{ + int iLayer; + +/* -------------------------------------------------------------------- */ +/* Try to find layer. */ +/* -------------------------------------------------------------------- */ + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) ) + break; + } + + if( iLayer == nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to delete layer '%s', but this layer is not known to OGR.", + pszLayerName ); + return; + } + + DeleteLayer(iLayer); +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +OGRErr OGRCouchDBDataSource::DeleteLayer(int iLayer) +{ + if (!bReadWrite) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + if( iLayer < 0 || iLayer >= nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %d not in legal range of 0 to %d.", + iLayer, nLayers-1 ); + return OGRERR_FAILURE; + } + + CPLString osLayerName = GetLayer(iLayer)->GetName(); + +/* -------------------------------------------------------------------- */ +/* Blow away our OGR structures related to the layer. This is */ +/* pretty dangerous if anything has a reference to this layer! */ +/* -------------------------------------------------------------------- */ + CPLDebug( "CouchDB", "DeleteLayer(%s)", osLayerName.c_str() ); + + delete papoLayers[iLayer]; + memmove( papoLayers + iLayer, papoLayers + iLayer + 1, + sizeof(void *) * (nLayers - iLayer - 1) ); + nLayers--; + +/* -------------------------------------------------------------------- */ +/* Remove from the database. */ +/* -------------------------------------------------------------------- */ + + char* pszEscapedName = CPLEscapeString(osLayerName, -1, CPLES_URL); + CPLString osEscapedName = pszEscapedName; + CPLFree(pszEscapedName); + + CPLString osURI; + osURI = "/"; + osURI += osEscapedName; + json_object* poAnswerObj = DELETE(osURI); + + if (poAnswerObj == NULL) + return OGRERR_FAILURE; + + if (!IsOK(poAnswerObj, "Layer deletion failed")) + { + json_object_put(poAnswerObj); + return OGRERR_FAILURE; + } + + json_object_put(poAnswerObj); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRCouchDBDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Special case DELLAYER: command. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand,"DELLAYER:",9) ) + { + const char *pszLayerName = pszSQLCommand + 9; + + while( *pszLayerName == ' ' ) + pszLayerName++; + + DeleteLayer( pszLayerName ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Special case 'COMPACT ON ' command. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand,"COMPACT ON ",11) ) + { + const char *pszLayerName = pszSQLCommand + 11; + + while( *pszLayerName == ' ' ) + pszLayerName++; + + CPLString osURI("/"); + osURI += pszLayerName; + osURI += "/_compact"; + + json_object* poAnswerObj = POST(osURI, NULL); + IsError(poAnswerObj, "Database compaction failed"); + json_object_put(poAnswerObj); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Special case 'VIEW CLEANUP ON ' command. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand,"VIEW CLEANUP ON ",16) ) + { + const char *pszLayerName = pszSQLCommand + 16; + + while( *pszLayerName == ' ' ) + pszLayerName++; + + CPLString osURI("/"); + osURI += pszLayerName; + osURI += "/_view_cleanup"; + + json_object* poAnswerObj = POST(osURI, NULL); + IsError(poAnswerObj, "View cleanup failed"); + json_object_put(poAnswerObj); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Deal with "DELETE FROM layer_name WHERE expression" statement */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand, "DELETE FROM ", 12) ) + { + const char* pszIter = pszSQLCommand + 12; + while(*pszIter && *pszIter != ' ') + pszIter ++; + if (*pszIter == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid statement"); + return NULL; + } + + CPLString osName = pszSQLCommand + 12; + osName.resize(pszIter - (pszSQLCommand + 12)); + OGRCouchDBLayer* poLayer = (OGRCouchDBLayer*)GetLayerByName(osName); + if (poLayer == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unknown layer : %s", osName.c_str()); + return NULL; + } + if (poLayer->GetLayerType() != COUCHDB_TABLE_LAYER) + return NULL; + OGRCouchDBTableLayer* poTableLayer = (OGRCouchDBTableLayer*)poLayer; + + while(*pszIter && *pszIter == ' ') + pszIter ++; + if (!EQUALN(pszIter, "WHERE ", 5)) + { + CPLError(CE_Failure, CPLE_AppDefined, "WHERE clause missing"); + return NULL; + } + pszIter += 5; + + const char* pszQuery = pszIter; + + /* Check with the generic SQL engine that this is a valid WHERE clause */ + OGRFeatureQuery oQuery; + OGRErr eErr = oQuery.Compile( poLayer->GetLayerDefn(), pszQuery ); + if( eErr != OGRERR_NONE ) + { + return NULL; + } + + swq_expr_node * pNode = (swq_expr_node *) oQuery.GetSWQExpr(); + if (pNode->eNodeType == SNT_OPERATION && + pNode->nOperation == SWQ_EQ && + pNode->nSubExprCount == 2 && + pNode->papoSubExpr[0]->eNodeType == SNT_COLUMN && + pNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT && + pNode->papoSubExpr[0]->field_index == _ID_FIELD && + pNode->papoSubExpr[1]->field_type == SWQ_STRING) + { + poTableLayer->DeleteFeature(pNode->papoSubExpr[1]->string_value); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid WHERE clause. Expecting '_id' = 'a_value'"); + return NULL; + } + + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try an optimized implementation when doing only stats */ +/* -------------------------------------------------------------------- */ + if (poSpatialFilter == NULL && EQUALN(pszSQLCommand, "SELECT", 6)) + { + OGRLayer* poRet = ExecuteSQLStats(pszSQLCommand); + if (poRet) + return poRet; + } + + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); +} + +/************************************************************************/ +/* ExecuteSQLStats() */ +/************************************************************************/ + +class PointerAutoFree +{ + void * m_p; + public: + PointerAutoFree(void* p) { m_p = p; } + ~PointerAutoFree() { CPLFree(m_p); } +}; + +class OGRCouchDBOneLineLayer : public OGRLayer +{ + public: + OGRFeature* poFeature; + OGRFeatureDefn* poFeatureDefn; + int bEnd; + + OGRCouchDBOneLineLayer() { poFeature = NULL; poFeatureDefn = NULL; bEnd = FALSE; } + ~OGRCouchDBOneLineLayer() + { + delete poFeature; + if( poFeatureDefn != NULL ) + poFeatureDefn->Release(); + } + + virtual void ResetReading() { bEnd = FALSE;} + virtual OGRFeature *GetNextFeature() + { + if (bEnd) return NULL; + bEnd = TRUE; + return poFeature->Clone(); + } + virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; } + virtual int TestCapability( const char * ) { return FALSE; } +}; + +OGRLayer * OGRCouchDBDataSource::ExecuteSQLStats( const char *pszSQLCommand ) +{ + swq_select sSelectInfo; + if( sSelectInfo.preparse( pszSQLCommand ) != CPLE_None ) + { + return NULL; + } + + if (sSelectInfo.table_count != 1) + { + return NULL; + } + + swq_table_def *psTableDef = &sSelectInfo.table_defs[0]; + if( psTableDef->data_source != NULL ) + { + return NULL; + } + + OGRCouchDBLayer* _poSrcLayer = + (OGRCouchDBLayer* )GetLayerByName( psTableDef->table_name ); + if (_poSrcLayer == NULL) + { + return NULL; + } + if (_poSrcLayer->GetLayerType() != COUCHDB_TABLE_LAYER) + return NULL; + + OGRCouchDBTableLayer* poSrcLayer = (OGRCouchDBTableLayer* ) _poSrcLayer; + + int nFieldCount = poSrcLayer->GetLayerDefn()->GetFieldCount(); + + swq_field_list sFieldList; + memset( &sFieldList, 0, sizeof(sFieldList) ); + sFieldList.table_count = sSelectInfo.table_count; + sFieldList.table_defs = sSelectInfo.table_defs; + + sFieldList.count = 0; + sFieldList.names = (char **) CPLMalloc( sizeof(char *) * nFieldCount ); + sFieldList.types = (swq_field_type *) + CPLMalloc( sizeof(swq_field_type) * nFieldCount ); + sFieldList.table_ids = (int *) + CPLMalloc( sizeof(int) * nFieldCount ); + sFieldList.ids = (int *) + CPLMalloc( sizeof(int) * nFieldCount ); + + PointerAutoFree oHolderNames(sFieldList.names); + PointerAutoFree oHolderTypes(sFieldList.types); + PointerAutoFree oHolderTableIds(sFieldList.table_ids); + PointerAutoFree oHolderIds(sFieldList.ids); + + int iField; + for( iField = 0; + iField < poSrcLayer->GetLayerDefn()->GetFieldCount(); + iField++ ) + { + OGRFieldDefn *poFDefn=poSrcLayer->GetLayerDefn()->GetFieldDefn(iField); + int iOutField = sFieldList.count++; + sFieldList.names[iOutField] = (char *) poFDefn->GetNameRef(); + if( poFDefn->GetType() == OFTInteger ) + sFieldList.types[iOutField] = SWQ_INTEGER; + else if( poFDefn->GetType() == OFTReal ) + sFieldList.types[iOutField] = SWQ_FLOAT; + else if( poFDefn->GetType() == OFTString ) + sFieldList.types[iOutField] = SWQ_STRING; + else + sFieldList.types[iOutField] = SWQ_OTHER; + + sFieldList.table_ids[iOutField] = 0; + sFieldList.ids[iOutField] = iField; + } + + CPLString osLastFieldName; + for( iField = 0; iField < sSelectInfo.result_columns; iField++ ) + { + swq_col_def *psColDef = sSelectInfo.column_defs + iField; + if (psColDef->field_name == NULL) + return NULL; + + if (strcmp(psColDef->field_name, "*") != 0) + { + if (osLastFieldName.size() == 0) + osLastFieldName = psColDef->field_name; + else if (strcmp(osLastFieldName, psColDef->field_name) != 0) + return NULL; + + if (poSrcLayer->GetLayerDefn()->GetFieldIndex(psColDef->field_name) == -1) + return NULL; + } + + if (!(psColDef->col_func == SWQCF_AVG || + psColDef->col_func == SWQCF_MIN || + psColDef->col_func == SWQCF_MAX || + psColDef->col_func == SWQCF_COUNT || + psColDef->col_func == SWQCF_SUM)) + return NULL; + + if (psColDef->distinct_flag) /* TODO: could perhaps be relaxed */ + return NULL; + } + + if (osLastFieldName.size() == 0) + return NULL; + + /* Normalize field name */ + int nIndex = poSrcLayer->GetLayerDefn()->GetFieldIndex(osLastFieldName); + osLastFieldName = poSrcLayer->GetLayerDefn()->GetFieldDefn(nIndex)->GetNameRef(); + +/* -------------------------------------------------------------------- */ +/* Finish the parse operation. */ +/* -------------------------------------------------------------------- */ + + if( sSelectInfo.parse( &sFieldList, 0 ) != CE_None ) + { + return NULL; + } + + if (sSelectInfo.join_defs != NULL || + sSelectInfo.where_expr != NULL || + sSelectInfo.order_defs != NULL || + sSelectInfo.query_mode != SWQM_SUMMARY_RECORD) + { + return NULL; + } + + for( iField = 0; iField < sSelectInfo.result_columns; iField++ ) + { + swq_col_def *psColDef = sSelectInfo.column_defs + iField; + if (psColDef->field_index == -1) + { + if (psColDef->col_func == SWQCF_COUNT) + continue; + + return NULL; + } + if (psColDef->field_type != SWQ_INTEGER && + psColDef->field_type != SWQ_FLOAT) + { + return NULL; + } + } + + int bFoundFilter = poSrcLayer->HasFilterOnFieldOrCreateIfNecessary(osLastFieldName); + if (!bFoundFilter) + return NULL; + + CPLString osURI = "/"; + osURI += poSrcLayer->GetName(); + osURI += "/_design/ogr_filter_"; + osURI += osLastFieldName; + osURI += "/_view/filter?reduce=true"; + + json_object* poAnswerObj = GET(osURI); + json_object* poRows = NULL; + if (!(poAnswerObj != NULL && + json_object_is_type(poAnswerObj, json_type_object) && + (poRows = json_object_object_get(poAnswerObj, "rows")) != NULL && + json_object_is_type(poRows, json_type_array))) + { + json_object_put(poAnswerObj); + return NULL; + } + + int nLength = json_object_array_length(poRows); + if (nLength != 1) + { + json_object_put(poAnswerObj); + return NULL; + } + + json_object* poRow = json_object_array_get_idx(poRows, 0); + if (!(poRow && json_object_is_type(poRow, json_type_object))) + { + json_object_put(poAnswerObj); + return NULL; + } + + json_object* poValue = json_object_object_get(poRow, "value"); + if (!(poValue != NULL && json_object_is_type(poValue, json_type_object))) + { + json_object_put(poAnswerObj); + return NULL; + } + + json_object* poSum = json_object_object_get(poValue, "sum"); + json_object* poCount = json_object_object_get(poValue, "count"); + json_object* poMin = json_object_object_get(poValue, "min"); + json_object* poMax = json_object_object_get(poValue, "max"); + if (poSum != NULL && (json_object_is_type(poSum, json_type_int) || + json_object_is_type(poSum, json_type_double)) && + poCount != NULL && (json_object_is_type(poCount, json_type_int) || + json_object_is_type(poCount, json_type_double)) && + poMin != NULL && (json_object_is_type(poMin, json_type_int) || + json_object_is_type(poMin, json_type_double)) && + poMax != NULL && (json_object_is_type(poMax, json_type_int) || + json_object_is_type(poMax, json_type_double)) ) + { + double dfSum = json_object_get_double(poSum); + int nCount = json_object_get_int(poCount); + double dfMin = json_object_get_double(poMin); + double dfMax = json_object_get_double(poMax); + json_object_put(poAnswerObj); + + //CPLDebug("CouchDB", "sum=%f, count=%d, min=%f, max=%f", + // dfSum, nCount, dfMin, dfMax); + + OGRFeatureDefn* poFeatureDefn = new OGRFeatureDefn(poSrcLayer->GetName()); + poFeatureDefn->Reference(); + + for( iField = 0; iField < sSelectInfo.result_columns; iField++ ) + { + swq_col_def *psColDef = sSelectInfo.column_defs + iField; + OGRFieldDefn oFDefn( "", OFTInteger ); + + if( psColDef->field_alias != NULL ) + { + oFDefn.SetName(psColDef->field_alias); + } + else + { + const swq_operation *op = swq_op_registrar::GetOperator( + (swq_op) psColDef->col_func ); + oFDefn.SetName( CPLSPrintf( "%s_%s", + op->pszName, + psColDef->field_name ) ); + } + + if( psColDef->col_func == SWQCF_COUNT ) + oFDefn.SetType( OFTInteger ); + else if (psColDef->field_type == SWQ_INTEGER) + oFDefn.SetType( OFTInteger ); + else if (psColDef->field_type == SWQ_FLOAT) + oFDefn.SetType( OFTReal ); + + poFeatureDefn->AddFieldDefn(&oFDefn); + } + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + for( iField = 0; iField < sSelectInfo.result_columns; iField++ ) + { + swq_col_def *psColDef = sSelectInfo.column_defs + iField; + switch(psColDef->col_func) + { + case SWQCF_AVG: + if (nCount) + poFeature->SetField(iField, dfSum / nCount); + break; + case SWQCF_MIN: + poFeature->SetField(iField, dfMin); + break; + case SWQCF_MAX: + poFeature->SetField(iField, dfMax); + break; + case SWQCF_COUNT: + poFeature->SetField(iField, nCount); + break; + case SWQCF_SUM: + poFeature->SetField(iField, dfSum); + break; + default: + break; + } + } + + poFeature->SetFID(0); + + OGRCouchDBOneLineLayer* poAnswerLayer = new OGRCouchDBOneLineLayer(); + poAnswerLayer->poFeatureDefn = poFeatureDefn; + poAnswerLayer->poFeature = poFeature; + return poAnswerLayer; + } + json_object_put(poAnswerObj); + + return NULL; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRCouchDBDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} + +/************************************************************************/ +/* GetETag() */ +/************************************************************************/ + +char* OGRCouchDBDataSource::GetETag(const char* pszURI) +{ + + // make a head request and only return the etag response header + char* pszEtag = NULL; + char **papszTokens; + char** papszOptions = NULL; + + bMustCleanPersistant = TRUE; + + papszOptions = CSLAddString(papszOptions, CPLSPrintf("PERSISTENT=CouchDB:%p", this)); + papszOptions = CSLAddString(papszOptions, "HEADERS=Content-Type: application/json"); + papszOptions = CSLAddString(papszOptions, "NO_BODY=1"); + + if (osUserPwd.size()) + { + CPLString osUserPwdOption("USERPWD="); + osUserPwdOption += osUserPwd; + papszOptions = CSLAddString(papszOptions, osUserPwdOption); + } + + CPLDebug("CouchDB", "HEAD %s", pszURI); + + CPLString osFullURL(osURL); + osFullURL += pszURI; + CPLPushErrorHandler(CPLQuietErrorHandler); + + CPLHTTPResult * psResult = CPLHTTPFetch( osFullURL, papszOptions); + CPLPopErrorHandler(); + CSLDestroy(papszOptions); + if (psResult == NULL) + return NULL; + + if (CSLFetchNameValue(psResult->papszHeaders, "Etag") != NULL) + { + papszTokens = + CSLTokenizeString2( CSLFetchNameValue(psResult->papszHeaders, "Etag"), "\"\r\n", 0 ); + + pszEtag = CPLStrdup(papszTokens[0]); + + CSLDestroy( papszTokens ); + } + + CPLHTTPDestroyResult(psResult); + return pszEtag; +} + +/************************************************************************/ +/* REQUEST() */ +/************************************************************************/ + +json_object* OGRCouchDBDataSource::REQUEST(const char* pszVerb, + const char* pszURI, + const char* pszData) +{ + bMustCleanPersistant = TRUE; + + char** papszOptions = NULL; + papszOptions = CSLAddString(papszOptions, CPLSPrintf("PERSISTENT=CouchDB:%p", this)); + + CPLString osCustomRequest("CUSTOMREQUEST="); + osCustomRequest += pszVerb; + papszOptions = CSLAddString(papszOptions, osCustomRequest); + + CPLString osPOSTFIELDS("POSTFIELDS="); + if (pszData) + osPOSTFIELDS += pszData; + papszOptions = CSLAddString(papszOptions, osPOSTFIELDS); + + papszOptions = CSLAddString(papszOptions, "HEADERS=Content-Type: application/json"); + + if (osUserPwd.size()) + { + CPLString osUserPwdOption("USERPWD="); + osUserPwdOption += osUserPwd; + papszOptions = CSLAddString(papszOptions, osUserPwdOption); + } + + CPLDebug("CouchDB", "%s %s", pszVerb, pszURI); + CPLString osFullURL(osURL); + osFullURL += pszURI; + CPLPushErrorHandler(CPLQuietErrorHandler); + + CPLHTTPResult * psResult = CPLHTTPFetch( osFullURL, papszOptions); + CPLPopErrorHandler(); + CSLDestroy(papszOptions); + if (psResult == NULL) + return NULL; + + const char* pszServer = CSLFetchNameValue(psResult->papszHeaders, "Server"); + if (pszServer == NULL || !EQUALN(pszServer, "CouchDB", 7)) + { + CPLHTTPDestroyResult(psResult); + return NULL; + } + + if (psResult->nDataLen == 0) + { + CPLHTTPDestroyResult(psResult); + return NULL; + } + + json_tokener* jstok = NULL; + json_object* jsobj = NULL; + + jstok = json_tokener_new(); + jsobj = json_tokener_parse_ex(jstok, (const char*)psResult->pabyData, -1); + if( jstok->err != json_tokener_success) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JSON parsing error: %s (at offset %d)", + json_tokener_error_desc(jstok->err), jstok->char_offset); + + json_tokener_free(jstok); + + CPLHTTPDestroyResult(psResult); + return NULL; + } + json_tokener_free(jstok); + + CPLHTTPDestroyResult(psResult); + return jsobj; +} + +/************************************************************************/ +/* GET() */ +/************************************************************************/ + +json_object* OGRCouchDBDataSource::GET(const char* pszURI) +{ + return REQUEST("GET", pszURI, NULL); +} + +/************************************************************************/ +/* PUT() */ +/************************************************************************/ + +json_object* OGRCouchDBDataSource::PUT(const char* pszURI, const char* pszData) +{ + return REQUEST("PUT", pszURI, pszData); +} + +/************************************************************************/ +/* POST() */ +/************************************************************************/ + +json_object* OGRCouchDBDataSource::POST(const char* pszURI, const char* pszData) +{ + return REQUEST("POST", pszURI, pszData); +} + +/************************************************************************/ +/* DELETE() */ +/************************************************************************/ + +json_object* OGRCouchDBDataSource::DELETE(const char* pszURI) +{ + return REQUEST("DELETE", pszURI, NULL); +} + +/************************************************************************/ +/* IsError() */ +/************************************************************************/ + +int OGRCouchDBDataSource::IsError(json_object* poAnswerObj, + const char* pszErrorMsg) +{ + if ( poAnswerObj == NULL || + !json_object_is_type(poAnswerObj, json_type_object) ) + { + return FALSE; + } + + json_object* poError = json_object_object_get(poAnswerObj, "error"); + json_object* poReason = json_object_object_get(poAnswerObj, "reason"); + + const char* pszError = json_object_get_string(poError); + const char* pszReason = json_object_get_string(poReason); + if (pszError != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "%s : %s, %s", + pszErrorMsg, + pszError ? pszError : "", + pszReason ? pszReason : ""); + + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* IsOK() */ +/************************************************************************/ + +int OGRCouchDBDataSource::IsOK(json_object* poAnswerObj, + const char* pszErrorMsg) +{ + if ( poAnswerObj == NULL || + !json_object_is_type(poAnswerObj, json_type_object) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s", + pszErrorMsg); + + return FALSE; + } + + json_object* poOK = json_object_object_get(poAnswerObj, "ok"); + if ( !poOK ) + { + IsError(poAnswerObj, pszErrorMsg); + + return FALSE; + } + + const char* pszOK = json_object_get_string(poOK); + if ( !pszOK || !CSLTestBoolean(pszOK) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMsg); + + return FALSE; + } + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogrcouchdbdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogrcouchdbdriver.cpp new file mode 100644 index 000000000..786337e5d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogrcouchdbdriver.cpp @@ -0,0 +1,125 @@ +/****************************************************************************** + * $Id: ogrcouchdbdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: CouchDB Translator + * Purpose: Implements OGRCouchDBDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_couchdb.h" + +// g++ -g -Wall -fPIC -shared -o ogr_CouchDB.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/couchdb ogr/ogrsf_frmts/couchdb/*.c* -L. -lgdal -Iogr/ogrsf_frmts/geojson/jsonc + +CPL_CVSID("$Id: ogrcouchdbdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +extern "C" void RegisterOGRCouchDB(); + +/************************************************************************/ +/* ~OGRCouchDBDriver() */ +/************************************************************************/ + +OGRCouchDBDriver::~OGRCouchDBDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRCouchDBDriver::GetName() + +{ + return "CouchDB"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRCouchDBDriver::Open( const char * pszFilename, int bUpdate ) + +{ + if (strncmp(pszFilename, "http://", 7) == 0 || + strncmp(pszFilename, "https://", 8) == 0) + { + /* ok */ + } + else if (!EQUALN(pszFilename, "CouchDB:", 8)) + return NULL; + + OGRCouchDBDataSource *poDS = new OGRCouchDBDataSource(); + + if( !poDS->Open( pszFilename, bUpdate ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRCouchDBDriver::CreateDataSource( const char * pszName, + CPL_UNUSED char **papszOptions ) +{ + OGRCouchDBDataSource *poDS = new OGRCouchDBDataSource(); + + if( !poDS->Open( pszName, TRUE ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRCouchDBDriver::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap, ODrCCreateDataSource)) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRCouchDB() */ +/************************************************************************/ + +void RegisterOGRCouchDB() + +{ + OGRSFDriver* poDriver = new OGRCouchDBDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "CouchDB / GeoCouch" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( poDriver ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogrcouchdblayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogrcouchdblayer.cpp new file mode 100644 index 000000000..a07036217 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogrcouchdblayer.cpp @@ -0,0 +1,550 @@ +/****************************************************************************** + * $Id: ogrcouchdblayer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: CouchDB Translator + * Purpose: Implements OGRCouchDBLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_couchdb.h" +#include "ogrgeojsonreader.h" +#include "ogrgeojsonutils.h" + +CPL_CVSID("$Id: ogrcouchdblayer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGRCouchDBLayer() */ +/************************************************************************/ + +OGRCouchDBLayer::OGRCouchDBLayer(OGRCouchDBDataSource* poDS) + +{ + this->poDS = poDS; + + nNextInSeq = 0; + + poSRS = NULL; + + poFeatureDefn = NULL; + + nOffset = 0; + bEOF = FALSE; + + poFeatures = NULL; + + bGeoJSONDocument = TRUE; +} + +/************************************************************************/ +/* ~OGRCouchDBLayer() */ +/************************************************************************/ + +OGRCouchDBLayer::~OGRCouchDBLayer() + +{ + if( poSRS != NULL ) + poSRS->Release(); + + if( poFeatureDefn != NULL ) + poFeatureDefn->Release(); + + json_object_put(poFeatures); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRCouchDBLayer::ResetReading() + +{ + nNextInSeq = 0; + nOffset = 0; + bEOF = FALSE; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRCouchDBLayer::GetLayerDefn() +{ + CPLAssert(poFeatureDefn); + return poFeatureDefn; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRCouchDBLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + GetLayerDefn(); + + while(TRUE) + { + if (nNextInSeq < nOffset || + nNextInSeq >= nOffset + (int)aoFeatures.size()) + { + if (bEOF) + return NULL; + + nOffset += aoFeatures.size(); + if (!FetchNextRows()) + return NULL; + } + + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRCouchDBLayer::GetNextRawFeature() +{ + if (nNextInSeq < nOffset || + nNextInSeq - nOffset >= (int)aoFeatures.size()) + return NULL; + + OGRFeature* poFeature = TranslateFeature(aoFeatures[nNextInSeq - nOffset]); + if (poFeature != NULL && poFeature->GetFID() == OGRNullFID) + poFeature->SetFID(nNextInSeq); + + nNextInSeq ++; + + return poFeature; +} + +/************************************************************************/ +/* SetNextByIndex() */ +/************************************************************************/ + +OGRErr OGRCouchDBLayer::SetNextByIndex( GIntBig nIndex ) +{ + if (nIndex < 0 || nIndex >= INT_MAX ) + return OGRERR_FAILURE; + bEOF = FALSE; + nNextInSeq = (int)nIndex; + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRCouchDBLayer::TestCapability( const char * pszCap ) + +{ + if ( EQUAL(pszCap, OLCStringsAsUTF8) ) + return TRUE; + else if ( EQUAL(pszCap, OLCFastSetNextByIndex) ) + return TRUE; + return FALSE; +} + +/************************************************************************/ +/* TranslateFeature() */ +/************************************************************************/ + +OGRFeature* OGRCouchDBLayer::TranslateFeature( json_object* poObj ) +{ + OGRFeature* poFeature = NULL; + poFeature = new OGRFeature( GetLayerDefn() ); + + json_object* poId = json_object_object_get(poObj, "_id"); + const char* pszId = json_object_get_string(poId); + if (pszId) + { + poFeature->SetField(_ID_FIELD, pszId); + + int nFID = atoi(pszId); + const char* pszFID = CPLSPrintf("%09d", nFID); + if (strcmp(pszId, pszFID) == 0) + poFeature->SetFID(nFID); + } + + json_object* poRev = json_object_object_get(poObj, "_rev"); + const char* pszRev = json_object_get_string(poRev); + if (pszRev) + poFeature->SetField(_REV_FIELD, pszRev); + +/* -------------------------------------------------------------------- */ +/* Translate GeoJSON "properties" object to feature attributes. */ +/* -------------------------------------------------------------------- */ + + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + if( bGeoJSONDocument ) + { + json_object* poObjProps = json_object_object_get( poObj, "properties" ); + if ( NULL != poObjProps && + json_object_get_type(poObjProps ) == json_type_object ) + { + json_object_object_foreachC( poObjProps, it ) + { + ParseFieldValue(poFeature, it.key, it.val); + } + } + } + else + { + json_object_object_foreachC( poObj, it ) + { + if( strcmp(it.key, "_id") != 0 && + strcmp(it.key, "_rev") != 0 && + strcmp(it.key, "geometry") != 0 ) + { + ParseFieldValue(poFeature, it.key, it.val); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Translate geometry sub-object of GeoJSON Feature. */ +/* -------------------------------------------------------------------- */ + + json_object* poObjGeom = json_object_object_get( poObj, "geometry" ); + if (poObjGeom != NULL) + { + OGRGeometry* poGeometry = OGRGeoJSONReadGeometry( poObjGeom ); + if( NULL != poGeometry ) + { + if (poSRS) + poGeometry->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly( poGeometry ); + } + } + + return poFeature; +} + +/************************************************************************/ +/* ParseFieldValue() */ +/************************************************************************/ + +void OGRCouchDBLayer::ParseFieldValue(OGRFeature* poFeature, + const char* pszKey, + json_object* poValue) +{ + int nField = poFeature->GetFieldIndex(pszKey); + if (nField < 0) + { + CPLDebug("CouchDB", + "Found field '%s' which is not in the layer definition. " + "Ignoring its value", + pszKey); + } + else if (poValue != NULL) + { + OGRFieldDefn* poFieldDefn = poFeature->GetFieldDefnRef(nField); + CPLAssert(poFieldDefn != NULL); + OGRFieldType eType = poFieldDefn->GetType(); + + if( OFTInteger == eType ) + { + poFeature->SetField( nField, json_object_get_int(poValue) ); + } + else if( OFTReal == eType ) + { + poFeature->SetField( nField, json_object_get_double(poValue) ); + } + else if( OFTIntegerList == eType ) + { + if ( json_object_get_type(poValue) == json_type_array ) + { + int nLength = json_object_array_length(poValue); + int* panVal = (int*)CPLMalloc(sizeof(int) * nLength); + for(int i=0;iSetField( nField, nLength, panVal ); + CPLFree(panVal); + } + } + else if( OFTRealList == eType ) + { + if ( json_object_get_type(poValue) == json_type_array ) + { + int nLength = json_object_array_length(poValue); + double* padfVal = (double*)CPLMalloc(sizeof(double) * nLength); + for(int i=0;iSetField( nField, nLength, padfVal ); + CPLFree(padfVal); + } + } + else if( OFTStringList == eType ) + { + if ( json_object_get_type(poValue) == json_type_array ) + { + int nLength = json_object_array_length(poValue); + char** papszVal = (char**)CPLMalloc(sizeof(char*) * (nLength+1)); + int i; + for(i=0;iSetField( nField, papszVal ); + CSLDestroy(papszVal); + } + } + else + { + poFeature->SetField( nField, json_object_get_string(poValue) ); + } + } +} + + +/************************************************************************/ +/* BuildFeatureDefnFromDoc() */ +/************************************************************************/ + +void OGRCouchDBLayer::BuildFeatureDefnFromDoc(json_object* poDoc) +{ +/* -------------------------------------------------------------------- */ +/* Read collection of properties. */ +/* -------------------------------------------------------------------- */ + json_object* poObjProps = json_object_object_get( poDoc, + "properties" ); + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + if( NULL != poObjProps && json_object_get_type(poObjProps) == json_type_object ) + { + json_object_object_foreachC( poObjProps, it ) + { + if( -1 == poFeatureDefn->GetFieldIndex( it.key ) ) + { + OGRFieldSubType eSubType; + OGRFieldDefn fldDefn( it.key, + GeoJSONPropertyToFieldType( it.val, eSubType ) ); + poFeatureDefn->AddFieldDefn( &fldDefn ); + } + } + } + else + { + bGeoJSONDocument = FALSE; + + json_object_object_foreachC( poDoc, it ) + { + if( strcmp(it.key, "_id") != 0 && + strcmp(it.key, "_rev") != 0 && + strcmp(it.key, "geometry") != 0 && + -1 == poFeatureDefn->GetFieldIndex( it.key ) ) + { + OGRFieldSubType eSubType; + OGRFieldDefn fldDefn( it.key, + GeoJSONPropertyToFieldType( it.val, eSubType ) ); + poFeatureDefn->AddFieldDefn( &fldDefn ); + } + } + } + + if( json_object_object_get( poDoc, "geometry" ) == NULL ) + { + poFeatureDefn->SetGeomType(wkbNone); + } +} + + +/************************************************************************/ +/* BuildFeatureDefnFromRows() */ +/************************************************************************/ + +int OGRCouchDBLayer::BuildFeatureDefnFromRows(json_object* poAnswerObj) +{ + if ( !json_object_is_type(poAnswerObj, json_type_object) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Layer definition creation failed"); + return FALSE; + } + + if (poDS->IsError(poAnswerObj, "Layer definition creation failed")) + { + return FALSE; + } + + json_object* poRows = json_object_object_get(poAnswerObj, "rows"); + if (poRows == NULL || + !json_object_is_type(poRows, json_type_array)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Layer definition creation failed"); + return FALSE; + } + + int nRows = json_object_array_length(poRows); + + json_object* poRow = NULL; + for(int i=0;iIsError(poAnswerObj, "FetchNextRowsAnalyseDocs() failed")) + { + json_object_put(poAnswerObj); + return FALSE; + } + + json_object* poRows = json_object_object_get(poAnswerObj, "rows"); + if (poRows == NULL || + !json_object_is_type(poRows, json_type_array)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "FetchNextRowsAnalyseDocs() failed"); + json_object_put(poAnswerObj); + return FALSE; + } + + int nRows = json_object_array_length(poRows); + for(int i=0;i + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_couchdb.h" + +CPL_CVSID("$Id: ogrcouchdbrowslayer.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* OGRCouchDBRowsLayer() */ +/************************************************************************/ + +OGRCouchDBRowsLayer::OGRCouchDBRowsLayer(OGRCouchDBDataSource* poDS) : + OGRCouchDBLayer(poDS) + +{ + poFeatureDefn = new OGRFeatureDefn( "rows" ); + poFeatureDefn->Reference(); + + OGRFieldDefn oFieldId("_id", OFTString); + poFeatureDefn->AddFieldDefn(&oFieldId); + + OGRFieldDefn oFieldRev("_rev", OFTString); + poFeatureDefn->AddFieldDefn(&oFieldRev); + + SetDescription( poFeatureDefn->GetName() ); + + bAllInOne = FALSE; +} + +/************************************************************************/ +/* ~OGRCouchDBRowsLayer() */ +/************************************************************************/ + +OGRCouchDBRowsLayer::~OGRCouchDBRowsLayer() + +{ +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRCouchDBRowsLayer::ResetReading() + +{ + OGRCouchDBLayer::ResetReading(); + + if (!bAllInOne) + { + json_object_put(poFeatures); + poFeatures = NULL; + aoFeatures.resize(0); + } +} + +/************************************************************************/ +/* FetchNextRows() */ +/************************************************************************/ + +int OGRCouchDBRowsLayer::FetchNextRows() +{ + if (bAllInOne) + return FALSE; + + json_object_put(poFeatures); + poFeatures = NULL; + aoFeatures.resize(0); + + int bHasEsperluet = (strstr(poDS->GetURL(), "?") != NULL); + + CPLString osURI; + if (strstr(poDS->GetURL(), "limit=") == NULL && + strstr(poDS->GetURL(), "skip=") == NULL) + { + if (!bHasEsperluet) + { + bHasEsperluet = TRUE; + osURI += "?"; + } + + osURI += CPLSPrintf("&limit=%d&skip=%d", + GetFeaturesToFetch(), nOffset); + } + if (strstr(poDS->GetURL(), "reduce=") == NULL) + { + if (!bHasEsperluet) + { + bHasEsperluet = TRUE; + osURI += "?"; + } + + osURI += "&reduce=false"; + } + json_object* poAnswerObj = poDS->GET(osURI); + return FetchNextRowsAnalyseDocs(poAnswerObj); +} + +/************************************************************************/ +/* BuildFeatureDefn() */ +/************************************************************************/ + +int OGRCouchDBRowsLayer::BuildFeatureDefn() +{ + int bRet = FetchNextRows(); + if (!bRet) + return FALSE; + + bRet = BuildFeatureDefnFromRows(poFeatures); + if (!bRet) + return FALSE; + + if ( bEOF ) + bAllInOne = TRUE; + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogrcouchdbtablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogrcouchdbtablelayer.cpp new file mode 100644 index 000000000..4fbbd159e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/couchdb/ogrcouchdbtablelayer.cpp @@ -0,0 +1,2108 @@ +/****************************************************************************** + * $Id: ogrcouchdbtablelayer.cpp 28928 2015-04-17 10:24:19Z rouault $ + * + * Project: CouchDB Translator + * Purpose: Implements OGRCouchDBTableLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_couchdb.h" +#include "ogrgeojsonreader.h" +#include "ogrgeojsonwriter.h" +#include "swq.h" + +#include + +CPL_CVSID("$Id: ogrcouchdbtablelayer.cpp 28928 2015-04-17 10:24:19Z rouault $"); + +/************************************************************************/ +/* OGRCouchDBTableLayer() */ +/************************************************************************/ + +OGRCouchDBTableLayer::OGRCouchDBTableLayer(OGRCouchDBDataSource* poDS, + const char* pszName) : + OGRCouchDBLayer(poDS) + +{ + osName = pszName; + char* pszEscapedName = CPLEscapeString(pszName, -1, CPLES_URL); + osEscapedName = pszEscapedName; + CPLFree(pszEscapedName); + + bInTransaction = FALSE; + + eGeomType = wkbUnknown; + + nNextFIDForCreate = -1; + bHasLoadedMetadata = FALSE; + bMustWriteMetadata = FALSE; + + bMustRunSpatialFilter = FALSE; + bServerSideSpatialFilteringWorks = TRUE; + bHasOGRSpatial = -1; + bHasGeocouchUtilsMinimalSpatialView = FALSE; + + bServerSideAttributeFilteringWorks = TRUE; + bHasInstalledAttributeFilter = FALSE; + + nUpdateSeq = -1; + bAlwaysValid = FALSE; + + bExtentValid = FALSE; + bExtentSet = FALSE; + dfMinX = 0; + dfMinY = 0; + dfMaxX = 0; + dfMaxY = 0; + + nCoordPrecision = atoi(CPLGetConfigOption("OGR_COUCHDB_COORDINATE_PRECISION", "-1")); + + SetDescription( osName ); +} + +/************************************************************************/ +/* ~OGRCouchDBTableLayer() */ +/************************************************************************/ + +OGRCouchDBTableLayer::~OGRCouchDBTableLayer() + +{ + if( bMustWriteMetadata ) + WriteMetadata(); + + for(int i=0;i<(int)aoTransactionFeatures.size();i++) + { + json_object_put(aoTransactionFeatures[i]); + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRCouchDBTableLayer::ResetReading() + +{ + OGRCouchDBLayer::ResetReading(); + + json_object_put(poFeatures); + poFeatures = NULL; + aoFeatures.resize(0); + + bMustRunSpatialFilter = m_poFilterGeom != NULL; + aosIdsToFetch.resize(0); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRCouchDBTableLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCFastFeatureCount) ) + return m_poFilterGeom == NULL && m_poAttrQuery == NULL; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return bExtentValid; + + else if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) + || EQUAL(pszCap,OLCDeleteFeature) ) + return poDS->IsReadWrite(); + + else if( EQUAL(pszCap,OLCCreateField) ) + return poDS->IsReadWrite(); + + else if( EQUAL(pszCap, OLCTransactions) ) + return poDS->IsReadWrite(); + + return OGRCouchDBLayer::TestCapability(pszCap); +} + +/************************************************************************/ +/* RunSpatialFilterQueryIfNecessary() */ +/************************************************************************/ + +int OGRCouchDBTableLayer::RunSpatialFilterQueryIfNecessary() +{ + if (!bMustRunSpatialFilter) + return TRUE; + + bMustRunSpatialFilter = FALSE; + + CPLAssert(nOffset == 0); + + aosIdsToFetch.resize(0); + + const char* pszSpatialFilter = NULL; + if (bHasOGRSpatial < 0 || bHasOGRSpatial == FALSE) + { + pszSpatialFilter = CPLGetConfigOption("COUCHDB_SPATIAL_FILTER" , NULL); + if (pszSpatialFilter) + bHasOGRSpatial = FALSE; + } + + if (bHasOGRSpatial < 0) + { + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/_design/ogr_spatial"; + + json_object* poAnswerObj = poDS->GET(osURI); + bHasOGRSpatial = (poAnswerObj != NULL && + json_object_is_type(poAnswerObj, json_type_object) && + json_object_object_get(poAnswerObj, "spatial") != NULL); + json_object_put(poAnswerObj); + + if (!bHasOGRSpatial) + { + /* Test if we have the 'minimal' spatial view provided by https://github.com/maxogden/geocouch-utils */ + osURI = "/"; + osURI += osEscapedName; + osURI += "/_design/geo"; + + json_object* poSpatialObj; + poAnswerObj = poDS->GET(osURI); + bHasGeocouchUtilsMinimalSpatialView = (poAnswerObj != NULL && + json_object_is_type(poAnswerObj, json_type_object) && + (poSpatialObj = json_object_object_get(poAnswerObj, "spatial")) != NULL && + json_object_is_type(poSpatialObj, json_type_object) && + json_object_object_get(poSpatialObj, "minimal") != NULL); + json_object_put(poAnswerObj); + + if (!bHasGeocouchUtilsMinimalSpatialView) + { + CPLDebug("CouchDB", + "Geocouch not working --> client-side spatial filtering"); + bServerSideSpatialFilteringWorks = FALSE; + return FALSE; + } + } + } + + OGREnvelope sEnvelope; + m_poFilterGeom->getEnvelope( &sEnvelope ); + + if (bHasOGRSpatial) + pszSpatialFilter = "_design/ogr_spatial/_spatial/spatial"; + else if (bHasGeocouchUtilsMinimalSpatialView) + pszSpatialFilter = "_design/geo/_spatial/minimal"; + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/"; + osURI += pszSpatialFilter; + osURI += "?bbox="; + osURI += CPLSPrintf("%.9f,%.9f,%.9f,%.9f", + sEnvelope.MinX, sEnvelope.MinY, + sEnvelope.MaxX, sEnvelope.MaxY); + + json_object* poAnswerObj = poDS->GET(osURI); + if (poAnswerObj == NULL) + { + CPLDebug("CouchDB", + "Geocouch not working --> client-side spatial filtering"); + bServerSideSpatialFilteringWorks = FALSE; + return FALSE; + } + + if ( !json_object_is_type(poAnswerObj, json_type_object) ) + { + CPLDebug("CouchDB", + "Geocouch not working --> client-side spatial filtering"); + bServerSideSpatialFilteringWorks = FALSE; + CPLError(CE_Failure, CPLE_AppDefined, + "FetchNextRowsSpatialFilter() failed"); + json_object_put(poAnswerObj); + return FALSE; + } + + /* Catch error for a non geocouch database */ + json_object* poError = json_object_object_get(poAnswerObj, "error"); + json_object* poReason = json_object_object_get(poAnswerObj, "reason"); + + const char* pszError = json_object_get_string(poError); + const char* pszReason = json_object_get_string(poReason); + + if (pszError && pszReason && strcmp(pszError, "not_found") == 0 && + strcmp(pszReason, "Document is missing attachment") == 0) + { + CPLDebug("CouchDB", + "Geocouch not working --> client-side spatial filtering"); + bServerSideSpatialFilteringWorks = FALSE; + json_object_put(poAnswerObj); + return FALSE; + } + + if (poDS->IsError(poAnswerObj, "FetchNextRowsSpatialFilter() failed")) + { + CPLDebug("CouchDB", + "Geocouch not working --> client-side spatial filtering"); + bServerSideSpatialFilteringWorks = FALSE; + json_object_put(poAnswerObj); + return FALSE; + } + + json_object* poRows = json_object_object_get(poAnswerObj, "rows"); + if (poRows == NULL || + !json_object_is_type(poRows, json_type_array)) + { + CPLDebug("CouchDB", + "Geocouch not working --> client-side spatial filtering"); + bServerSideSpatialFilteringWorks = FALSE; + CPLError(CE_Failure, CPLE_AppDefined, + "FetchNextRowsSpatialFilter() failed"); + json_object_put(poAnswerObj); + return FALSE; + } + + int nRows = json_object_array_length(poRows); + for(int i=0;i nOffset) + osContent += ","; + osContent += "\""; + osContent += aosIdsToFetch[i]; + osContent += "\""; + } + osContent += "]}"; + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/_all_docs?include_docs=true"; + json_object* poAnswerObj = poDS->POST(osURI, osContent); + return FetchNextRowsAnalyseDocs(poAnswerObj); +} + +/************************************************************************/ +/* HasFilterOnFieldOrCreateIfNecessary() */ +/************************************************************************/ + +int OGRCouchDBTableLayer::HasFilterOnFieldOrCreateIfNecessary(const char* pszFieldName) +{ + std::map::iterator oIter = oMapFilterFields.find(pszFieldName); + if (oIter != oMapFilterFields.end()) + return oIter->second; + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/_design/ogr_filter_"; + osURI += pszFieldName; + + int bFoundFilter = FALSE; + + json_object* poAnswerObj = poDS->GET(osURI); + if (poAnswerObj && + json_object_is_type(poAnswerObj, json_type_object) && + json_object_object_get(poAnswerObj, "views") != NULL) + { + bFoundFilter = TRUE; + } + json_object_put(poAnswerObj); + + if (!bFoundFilter) + { + json_object* poDoc = json_object_new_object(); + json_object* poViews = json_object_new_object(); + json_object* poFilter = json_object_new_object(); + + CPLString osMap; + + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn( + poFeatureDefn->GetFieldIndex(pszFieldName)); + CPLAssert(poFieldDefn); + int bIsNumeric = poFieldDefn->GetType() == OFTInteger || + poFieldDefn->GetType() == OFTReal; + + if (bGeoJSONDocument) + { + osMap = "function(doc) { if (doc.properties && doc.properties."; + osMap += pszFieldName; + if (bIsNumeric) + { + osMap += " && typeof doc.properties."; + osMap += pszFieldName; + osMap += " == \"number\""; + } + osMap += ") emit("; + osMap += "doc.properties."; + osMap += pszFieldName; + osMap +=", "; + if (bIsNumeric) + { + osMap += "doc.properties."; + osMap += pszFieldName; + } + else + osMap +="null"; + osMap += "); }"; + } + else + { + osMap = "function(doc) { if (doc."; + osMap += pszFieldName; + if (bIsNumeric) + { + osMap += " && typeof doc."; + osMap += pszFieldName; + osMap += " == \"number\""; + } + osMap += ") emit("; + osMap += "doc."; + osMap += pszFieldName; + osMap +=", "; + if (bIsNumeric) + { + osMap += "doc."; + osMap += pszFieldName; + } + else + osMap +="null"; + osMap += "); }"; + } + + json_object_object_add(poDoc, "views", poViews); + json_object_object_add(poViews, "filter", poFilter); + json_object_object_add(poFilter, "map", json_object_new_string(osMap)); + + if (bIsNumeric) + json_object_object_add(poFilter, "reduce", json_object_new_string("_stats")); + else + json_object_object_add(poFilter, "reduce", json_object_new_string("_count")); + + json_object* poAnswerObj = poDS->PUT(osURI, + json_object_to_json_string(poDoc)); + + json_object_put(poDoc); + + if (poDS->IsOK(poAnswerObj, "Filter creation failed")) + { + bFoundFilter = TRUE; + if (!bAlwaysValid) + bMustWriteMetadata = TRUE; + nUpdateSeq++; + } + + json_object_put(poAnswerObj); + } + + oMapFilterFields[pszFieldName] = bFoundFilter; + + return bFoundFilter; +} + +/************************************************************************/ +/* OGRCouchDBGetOpStr() */ +/************************************************************************/ + +static const char* OGRCouchDBGetOpStr(int nOperation, + int& bOutHasStrictComparisons) +{ + bOutHasStrictComparisons = FALSE; + + switch(nOperation) + { + case SWQ_EQ: return "="; + case SWQ_GE: return ">="; + case SWQ_LE: return "<="; + case SWQ_GT: bOutHasStrictComparisons = TRUE; return ">"; + case SWQ_LT: bOutHasStrictComparisons = TRUE; return "<"; + default: return "unknown op"; + } +} + +/************************************************************************/ +/* OGRCouchDBGetValue() */ +/************************************************************************/ + +static CPLString OGRCouchDBGetValue(swq_field_type eType, + swq_expr_node* poNode) +{ + if (eType == SWQ_STRING) + { + CPLString osVal("\""); + osVal += poNode->string_value; + osVal += "\""; + return osVal; + } + else if (eType == SWQ_INTEGER) + { + return CPLSPrintf("%d", (int)poNode->int_value); + } + else if (eType == SWQ_INTEGER64) + { + return CPLSPrintf(CPL_FRMT_GIB, poNode->int_value); + } + else if (eType == SWQ_FLOAT) + { + return CPLSPrintf("%.9f", poNode->float_value); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Handled case! File a bug!"); + return ""; + } +} + +/************************************************************************/ +/* OGRCouchDBGetKeyName() */ +/************************************************************************/ + +static const char* OGRCouchDBGetKeyName(int nOperation) +{ + if (nOperation == SWQ_EQ) + { + return "key"; + } + else if (nOperation == SWQ_GE || + nOperation == SWQ_GT) + { + return "startkey"; + } + else if (nOperation == SWQ_LE || + nOperation == SWQ_LT) + { + return "endkey"; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Handled case! File a bug!"); + return ""; + } +} +/************************************************************************/ +/* BuildAttrQueryURI() */ +/************************************************************************/ + +CPLString OGRCouchDBTableLayer::BuildAttrQueryURI(int& bOutHasStrictComparisons) +{ + CPLString osURI = ""; + + bOutHasStrictComparisons = FALSE; + + int bCanHandleFilter = FALSE; + + swq_expr_node * pNode = (swq_expr_node *) m_poAttrQuery->GetSWQExpr(); + if (pNode->eNodeType == SNT_OPERATION && + (pNode->nOperation == SWQ_EQ || + pNode->nOperation == SWQ_GE || + pNode->nOperation == SWQ_LE || + pNode->nOperation == SWQ_GT || + pNode->nOperation == SWQ_LT) && + pNode->nSubExprCount == 2 && + pNode->papoSubExpr[0]->eNodeType == SNT_COLUMN && + pNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT) + { + int nIndex = pNode->papoSubExpr[0]->field_index; + swq_field_type eType = pNode->papoSubExpr[1]->field_type; + const char* pszFieldName = poFeatureDefn->GetFieldDefn(nIndex)->GetNameRef(); + + if (pNode->nOperation == SWQ_EQ && + nIndex == _ID_FIELD && eType == SWQ_STRING) + { + bCanHandleFilter = TRUE; + + osURI = "/"; + osURI += osEscapedName; + osURI += "/_all_docs?"; + } + else if (nIndex >= FIRST_FIELD && + (eType == SWQ_STRING || eType == SWQ_INTEGER || + eType == SWQ_INTEGER64 || eType == SWQ_FLOAT)) + { + int bFoundFilter = HasFilterOnFieldOrCreateIfNecessary(pszFieldName); + if (bFoundFilter) + { + bCanHandleFilter = TRUE; + + osURI = "/"; + osURI += osEscapedName; + osURI += "/_design/ogr_filter_"; + osURI += pszFieldName; + osURI += "/_view/filter?"; + } + } + + if (bCanHandleFilter) + { + const char* pszOp = OGRCouchDBGetOpStr(pNode->nOperation, bOutHasStrictComparisons); + CPLString osVal = OGRCouchDBGetValue(eType, pNode->papoSubExpr[1]); + CPLDebug("CouchDB", "Evaluating %s %s %s", pszFieldName, pszOp, osVal.c_str()); + + osURI += OGRCouchDBGetKeyName(pNode->nOperation); + osURI += "="; + osURI += osVal; + } + } + else if (pNode->eNodeType == SNT_OPERATION && + pNode->nOperation == SWQ_AND && + pNode->nSubExprCount == 2 && + pNode->papoSubExpr[0]->eNodeType == SNT_OPERATION && + pNode->papoSubExpr[1]->eNodeType == SNT_OPERATION && + (((pNode->papoSubExpr[0]->nOperation == SWQ_GE || + pNode->papoSubExpr[0]->nOperation == SWQ_GT) && + (pNode->papoSubExpr[1]->nOperation == SWQ_LE || + pNode->papoSubExpr[1]->nOperation == SWQ_LT)) || + ((pNode->papoSubExpr[0]->nOperation == SWQ_LE || + pNode->papoSubExpr[0]->nOperation == SWQ_LT) && + (pNode->papoSubExpr[1]->nOperation == SWQ_GE || + pNode->papoSubExpr[1]->nOperation == SWQ_GT))) && + pNode->papoSubExpr[0]->nSubExprCount == 2 && + pNode->papoSubExpr[1]->nSubExprCount == 2 && + pNode->papoSubExpr[0]->papoSubExpr[0]->eNodeType == SNT_COLUMN && + pNode->papoSubExpr[0]->papoSubExpr[1]->eNodeType == SNT_CONSTANT && + pNode->papoSubExpr[1]->papoSubExpr[0]->eNodeType == SNT_COLUMN && + pNode->papoSubExpr[1]->papoSubExpr[1]->eNodeType == SNT_CONSTANT) + { + int nIndex0 = pNode->papoSubExpr[0]->papoSubExpr[0]->field_index; + swq_field_type eType0 = pNode->papoSubExpr[0]->papoSubExpr[1]->field_type; + int nIndex1 = pNode->papoSubExpr[1]->papoSubExpr[0]->field_index; + swq_field_type eType1 = pNode->papoSubExpr[1]->papoSubExpr[1]->field_type; + const char* pszFieldName = poFeatureDefn->GetFieldDefn(nIndex0)->GetNameRef(); + + if (nIndex0 == nIndex1 && eType0 == eType1 && + nIndex0 == _ID_FIELD && eType0 == SWQ_STRING) + { + bCanHandleFilter = TRUE; + + osURI = "/"; + osURI += osEscapedName; + osURI += "/_all_docs?"; + } + else if (nIndex0 == nIndex1 && eType0 == eType1 && + nIndex0 >= FIRST_FIELD && + (eType0 == SWQ_STRING || eType0 == SWQ_INTEGER || + eType0 == SWQ_INTEGER64 || eType0 == SWQ_FLOAT)) + { + int bFoundFilter = HasFilterOnFieldOrCreateIfNecessary(pszFieldName); + if (bFoundFilter) + { + bCanHandleFilter = TRUE; + + osURI = "/"; + osURI += osEscapedName; + osURI += "/_design/ogr_filter_"; + osURI += pszFieldName; + osURI += "/_view/filter?"; + } + } + + if (bCanHandleFilter) + { + swq_field_type eType = eType0; + CPLString osVal0 = OGRCouchDBGetValue(eType, pNode->papoSubExpr[0]->papoSubExpr[1]); + CPLString osVal1 = OGRCouchDBGetValue(eType, pNode->papoSubExpr[1]->papoSubExpr[1]); + + int nOperation0 = pNode->papoSubExpr[0]->nOperation; + int nOperation1 = pNode->papoSubExpr[1]->nOperation; + + const char* pszOp0 = OGRCouchDBGetOpStr(nOperation0, bOutHasStrictComparisons); + const char* pszOp1 = OGRCouchDBGetOpStr(nOperation1, bOutHasStrictComparisons); + + CPLDebug("CouchDB", "Evaluating %s %s %s AND %s %s %s", + pszFieldName, pszOp0, osVal0.c_str(), + pszFieldName, pszOp1, osVal1.c_str()); + + osURI += OGRCouchDBGetKeyName(nOperation0); + osURI += "="; + osURI += osVal0; + osURI += "&"; + osURI += OGRCouchDBGetKeyName(nOperation1); + osURI += "="; + osURI += osVal1; + } + } + else if (pNode->eNodeType == SNT_OPERATION && + pNode->nOperation == SWQ_BETWEEN && + pNode->nSubExprCount == 3 && + pNode->papoSubExpr[0]->eNodeType == SNT_COLUMN && + pNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT && + pNode->papoSubExpr[2]->eNodeType == SNT_CONSTANT) + { + int nIndex = pNode->papoSubExpr[0]->field_index; + swq_field_type eType = pNode->papoSubExpr[0]->field_type; + const char* pszFieldName = poFeatureDefn->GetFieldDefn(nIndex)->GetNameRef(); + + if (nIndex == _ID_FIELD && eType == SWQ_STRING) + { + bCanHandleFilter = TRUE; + + osURI = "/"; + osURI += osEscapedName; + osURI += "/_all_docs?"; + } + else if (nIndex >= FIRST_FIELD && + (eType == SWQ_STRING || eType == SWQ_INTEGER || + eType == SWQ_INTEGER64 || eType == SWQ_FLOAT)) + { + int bFoundFilter = HasFilterOnFieldOrCreateIfNecessary(pszFieldName); + if (bFoundFilter) + { + bCanHandleFilter = TRUE; + + osURI = "/"; + osURI += osEscapedName; + osURI += "/_design/ogr_filter_"; + osURI += pszFieldName; + osURI += "/_view/filter?"; + } + } + + if (bCanHandleFilter) + { + CPLString osVal0 = OGRCouchDBGetValue(eType, pNode->papoSubExpr[1]); + CPLString osVal1 = OGRCouchDBGetValue(eType, pNode->papoSubExpr[2]); + + CPLDebug("CouchDB", "Evaluating %s BETWEEN %s AND %s", + pszFieldName, osVal0.c_str(), osVal1.c_str()); + + osURI += OGRCouchDBGetKeyName(SWQ_GE); + osURI += "="; + osURI += osVal0; + osURI += "&"; + osURI += OGRCouchDBGetKeyName(SWQ_LE); + osURI += "="; + osURI += osVal1; + } + } + + return osURI; +} + +/************************************************************************/ +/* FetchNextRowsAttributeFilter() */ +/************************************************************************/ + +int OGRCouchDBTableLayer::FetchNextRowsAttributeFilter() +{ + if (bHasInstalledAttributeFilter) + { + bHasInstalledAttributeFilter = FALSE; + + CPLAssert(nOffset == 0); + + int bOutHasStrictComparisons = FALSE; + osURIAttributeFilter = BuildAttrQueryURI(bOutHasStrictComparisons); + + if (osURIAttributeFilter.size() == 0) + { + CPLDebug("CouchDB", + "Turning to client-side attribute filtering"); + bServerSideAttributeFilteringWorks = FALSE; + return FALSE; + } + } + + CPLString osURI(osURIAttributeFilter); + osURI += CPLSPrintf("&limit=%d&skip=%d&include_docs=true", + GetFeaturesToFetch(), nOffset); + if (strstr(osURI, "/_all_docs?") == NULL) + osURI += "&reduce=false"; + json_object* poAnswerObj = poDS->GET(osURI); + return FetchNextRowsAnalyseDocs(poAnswerObj); +} + +/************************************************************************/ +/* FetchNextRows() */ +/************************************************************************/ + +int OGRCouchDBTableLayer::FetchNextRows() +{ + json_object_put(poFeatures); + poFeatures = NULL; + aoFeatures.resize(0); + + if( m_poFilterGeom != NULL && bServerSideSpatialFilteringWorks ) + { + int bRet = FetchNextRowsSpatialFilter(); + if (bRet || bServerSideSpatialFilteringWorks) + return bRet; + } + + if( m_poAttrQuery != NULL && bServerSideAttributeFilteringWorks ) + { + int bRet = FetchNextRowsAttributeFilter(); + if (bRet || bServerSideAttributeFilteringWorks) + return bRet; + } + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += CPLSPrintf("/_all_docs?limit=%d&skip=%d&include_docs=true", + GetFeaturesToFetch(), nOffset); + json_object* poAnswerObj = poDS->GET(osURI); + return FetchNextRowsAnalyseDocs(poAnswerObj); +} + + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature * OGRCouchDBTableLayer::GetFeature( GIntBig nFID ) +{ + GetLayerDefn(); + + return GetFeature(CPLSPrintf("%09d", (int)nFID)); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature * OGRCouchDBTableLayer::GetFeature( const char* pszId ) +{ + GetLayerDefn(); + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/"; + osURI += pszId; + json_object* poAnswerObj = poDS->GET(osURI); + if (poAnswerObj == NULL) + return NULL; + + if ( !json_object_is_type(poAnswerObj, json_type_object) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "GetFeature(%s) failed", + pszId); + json_object_put(poAnswerObj); + return NULL; + } + + if ( poDS->IsError(poAnswerObj, CPLSPrintf("GetFeature(%s) failed", pszId)) ) + { + json_object_put(poAnswerObj); + return NULL; + } + + OGRFeature* poFeature = TranslateFeature( poAnswerObj ); + + json_object_put( poAnswerObj ); + + return poFeature; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRCouchDBTableLayer::GetLayerDefn() +{ + if (poFeatureDefn == NULL) + LoadMetadata(); + + if (poFeatureDefn == NULL) + { + poFeatureDefn = new OGRFeatureDefn( osName ); + poFeatureDefn->Reference(); + + poFeatureDefn->SetGeomType(eGeomType); + + OGRFieldDefn oFieldId("_id", OFTString); + poFeatureDefn->AddFieldDefn(&oFieldId); + + OGRFieldDefn oFieldRev("_rev", OFTString); + poFeatureDefn->AddFieldDefn(&oFieldRev); + + if (nNextFIDForCreate == 0) + { + return poFeatureDefn; + } + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/_all_docs?limit=10&include_docs=true"; + json_object* poAnswerObj = poDS->GET(osURI); + if (poAnswerObj == NULL) + return poFeatureDefn; + + BuildFeatureDefnFromRows(poAnswerObj); + + eGeomType = poFeatureDefn->GetGeomType(); + + json_object_put(poAnswerObj); + } + + return poFeatureDefn; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRCouchDBTableLayer::GetFeatureCount(int bForce) +{ + GetLayerDefn(); + + if (m_poFilterGeom == NULL && m_poAttrQuery != NULL) + { + int bOutHasStrictComparisons = FALSE; + CPLString osURI = BuildAttrQueryURI(bOutHasStrictComparisons); + if (!bOutHasStrictComparisons && osURI.size() != 0 && + strstr(osURI, "/_all_docs?") == NULL) + { + osURI += "&reduce=true"; + json_object* poAnswerObj = poDS->GET(osURI); + json_object* poRows = NULL; + if (poAnswerObj != NULL && + json_object_is_type(poAnswerObj, json_type_object) && + (poRows = json_object_object_get(poAnswerObj, "rows")) != NULL && + json_object_is_type(poRows, json_type_array)) + { + int nLength = json_object_array_length(poRows); + if (nLength == 0) + { + json_object_put(poAnswerObj); + return 0; + } + else if (nLength == 1) + { + json_object* poRow = json_object_array_get_idx(poRows, 0); + if (poRow && json_object_is_type(poRow, json_type_object)) + { + /* for string fields */ + json_object* poValue = json_object_object_get(poRow, "value"); + if (poValue != NULL && json_object_is_type(poValue, json_type_int)) + { + int nVal = json_object_get_int(poValue); + json_object_put(poAnswerObj); + return nVal; + } + else if (poValue != NULL && json_object_is_type(poValue, json_type_object)) + { + /* for numeric fields */ + json_object* poCount = json_object_object_get(poValue, "count"); + if (poCount != NULL && json_object_is_type(poCount, json_type_int)) + { + int nVal = json_object_get_int(poCount); + json_object_put(poAnswerObj); + return nVal; + } + } + } + } + } + json_object_put(poAnswerObj); + } + } + + if (m_poFilterGeom != NULL && m_poAttrQuery == NULL && + wkbFlatten(eGeomType) == wkbPoint) + { + /* Only optimize for wkbPoint case. Otherwise the result might be higher */ + /* than the real value since the intersection of the bounding box of the */ + /* geometry of a feature does not necessary mean the intersection of the */ + /* geometry itself */ + RunSpatialFilterQueryIfNecessary(); + if (bServerSideSpatialFilteringWorks) + { + return (int)aosIdsToFetch.size(); + } + } + + if (m_poFilterGeom != NULL || m_poAttrQuery != NULL) + return OGRCouchDBLayer::GetFeatureCount(bForce); + + return GetTotalFeatureCount(); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +int OGRCouchDBTableLayer::GetTotalFeatureCount() +{ + int nTotalRows = -1; + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/_all_docs?startkey_docid=_&endkey_docid=_zzzzzzzzzzzzzzz"; + json_object* poAnswerObj = poDS->GET(osURI); + if (poAnswerObj == NULL) + return nTotalRows; + + if ( !json_object_is_type(poAnswerObj, json_type_object) ) + { + json_object_put(poAnswerObj); + return nTotalRows; + } + + json_object* poTotalRows = json_object_object_get(poAnswerObj, + "total_rows"); + if (poTotalRows != NULL && + json_object_is_type(poTotalRows, json_type_int)) + { + nTotalRows = json_object_get_int(poTotalRows); + } + + json_object* poRows = json_object_object_get(poAnswerObj, "rows"); + if (poRows == NULL || + !json_object_is_type(poRows, json_type_array)) + { + json_object_put(poAnswerObj); + return nTotalRows; + } + + bHasOGRSpatial = FALSE; + + int nSpecialRows = json_object_array_length(poRows); + for(int i=0;i= nSpecialRows) + nTotalRows -= nSpecialRows; + + json_object_put(poAnswerObj); + + return nTotalRows; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRCouchDBTableLayer::CreateField( OGRFieldDefn *poField, + CPL_UNUSED int bApproxOK ) +{ + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + GetLayerDefn(); + + poFeatureDefn->AddFieldDefn(poField); + + bMustWriteMetadata = TRUE; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGRCouchDBWriteFeature */ +/************************************************************************/ + +static json_object* OGRCouchDBWriteFeature( OGRFeature* poFeature, + OGRwkbGeometryType eGeomType, + int bGeoJSONDocument, + int nCoordPrecision ) +{ + CPLAssert( NULL != poFeature ); + + json_object* poObj = json_object_new_object(); + CPLAssert( NULL != poObj ); + + if (poFeature->IsFieldSet(_ID_FIELD)) + { + const char* pszId = poFeature->GetFieldAsString(_ID_FIELD); + json_object_object_add( poObj, "_id", + json_object_new_string(pszId) ); + + if ( poFeature->GetFID() != OGRNullFID && + strcmp(CPLSPrintf("%09ld", (long)poFeature->GetFID()), pszId) != 0 ) + { + CPLDebug("CouchDB", + "_id field = %s, but FID = %09ld --> taking into account _id field only", + pszId, + (long)poFeature->GetFID()); + } + } + else if ( poFeature->GetFID() != OGRNullFID ) + { + json_object_object_add( poObj, "_id", + json_object_new_string(CPLSPrintf("%09ld", (long)poFeature->GetFID())) ); + } + + if (poFeature->IsFieldSet(_REV_FIELD)) + { + const char* pszRev = poFeature->GetFieldAsString(_REV_FIELD); + json_object_object_add( poObj, "_rev", + json_object_new_string(pszRev) ); + } + + if (bGeoJSONDocument) + { + json_object_object_add( poObj, "type", + json_object_new_string("Feature") ); + } + +/* -------------------------------------------------------------------- */ +/* Write feature attributes to GeoJSON "properties" object. */ +/* -------------------------------------------------------------------- */ + json_object* poObjProps = NULL; + + poObjProps = OGRGeoJSONWriteAttributes( poFeature ); + if (poObjProps) + { + json_object_object_del(poObjProps, "_id"); + json_object_object_del(poObjProps, "_rev"); + } + + if (bGeoJSONDocument) + { + json_object_object_add( poObj, "properties", poObjProps ); + } + else + { + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poObjProps, it ) + { + json_object_object_add( poObj, it.key, json_object_get(it.val) ); + } + json_object_put(poObjProps); + } + +/* -------------------------------------------------------------------- */ +/* Write feature geometry to GeoJSON "geometry" object. */ +/* Null geometries are allowed, according to the GeoJSON Spec. */ +/* -------------------------------------------------------------------- */ + if (eGeomType != wkbNone) + { + json_object* poObjGeom = NULL; + + OGRGeometry* poGeometry = poFeature->GetGeometryRef(); + if ( NULL != poGeometry ) + { + poObjGeom = OGRGeoJSONWriteGeometry( poGeometry, nCoordPrecision ); + if ( poObjGeom != NULL && + wkbFlatten(poGeometry->getGeometryType()) != wkbPoint && + !poGeometry->IsEmpty() ) + { + OGREnvelope sEnvelope; + poGeometry->getEnvelope(&sEnvelope); + + json_object* bbox = json_object_new_array(); + json_object_array_add(bbox, json_object_new_double_with_precision(sEnvelope.MinX, nCoordPrecision)); + json_object_array_add(bbox, json_object_new_double_with_precision(sEnvelope.MinY, nCoordPrecision)); + json_object_array_add(bbox, json_object_new_double_with_precision(sEnvelope.MaxX, nCoordPrecision)); + json_object_array_add(bbox, json_object_new_double_with_precision(sEnvelope.MaxY, nCoordPrecision)); + json_object_object_add( poObjGeom, "bbox", bbox ); + } + } + + json_object_object_add( poObj, "geometry", poObjGeom ); + } + + return poObj; +} + +/************************************************************************/ +/* GetMaximumId() */ +/************************************************************************/ + +int OGRCouchDBTableLayer::GetMaximumId() +{ + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/_all_docs?startkey_docid=999999999&endkey_docid=000000000&descending=true&limit=1"; + json_object* poAnswerObj = poDS->GET(osURI); + if (poAnswerObj == NULL) + return -1; + + if ( !json_object_is_type(poAnswerObj, json_type_object) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "GetMaximumId() failed"); + json_object_put(poAnswerObj); + return -1; + } + + if (poDS->IsError(poAnswerObj, "GetMaximumId() failed")) + { + json_object_put(poAnswerObj); + return -1; + } + + json_object* poRows = json_object_object_get(poAnswerObj, "rows"); + if (poRows == NULL || + !json_object_is_type(poRows, json_type_array)) + { + CPLError(CE_Failure, CPLE_AppDefined, "GetMaximumId() failed"); + json_object_put(poAnswerObj); + return -1; + } + + int nRows = json_object_array_length(poRows); + if (nRows != 1) + { + CPLError(CE_Failure, CPLE_AppDefined, "GetMaximumId() failed"); + json_object_put(poAnswerObj); + return -1; + } + + json_object* poRow = json_object_array_get_idx(poRows, 0); + if ( poRow == NULL || + !json_object_is_type(poRow, json_type_object) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "GetMaximumId() failed"); + json_object_put(poAnswerObj); + return -1; + } + + json_object* poId = json_object_object_get(poRow, "id"); + const char* pszId = json_object_get_string(poId); + if (pszId != NULL) + { + int nId = atoi(pszId); + json_object_put(poAnswerObj); + return nId; + } + + json_object_put(poAnswerObj); + return -1; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRCouchDBTableLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + GetLayerDefn(); + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + if (poFeature->IsFieldSet(_REV_FIELD)) + { + static int bOnce = FALSE; + if (!bOnce) + { + bOnce = TRUE; + CPLDebug("CouchDB", "CreateFeature() should be called with an unset _rev field. Ignoring it"); + } + poFeature->UnsetField(_REV_FIELD); + } + + if (nNextFIDForCreate < 0) + { + nNextFIDForCreate = GetMaximumId(); + if (nNextFIDForCreate >= 0) + nNextFIDForCreate ++; + else + nNextFIDForCreate = GetTotalFeatureCount(); + } + + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if (bExtentValid && poGeom != NULL && !poGeom->IsEmpty()) + { + OGREnvelope sEnvelope; + poGeom->getEnvelope(&sEnvelope); + if (!bExtentSet) + { + dfMinX = sEnvelope.MinX; + dfMinY = sEnvelope.MinY; + dfMaxX = sEnvelope.MaxX; + dfMaxY = sEnvelope.MaxY; + bExtentSet = TRUE; + } + if (sEnvelope.MinX < dfMinX) + dfMinX = sEnvelope.MinX; + if (sEnvelope.MinY < dfMinY) + dfMinY = sEnvelope.MinY; + if (sEnvelope.MaxX > dfMaxX) + dfMaxX = sEnvelope.MaxX; + if (sEnvelope.MaxY > dfMaxY) + dfMaxY = sEnvelope.MaxY; + } + + if (bExtentValid && eGeomType != wkbNone) + bMustWriteMetadata = TRUE; + + int nFID = nNextFIDForCreate ++; + CPLString osFID; + if (!poFeature->IsFieldSet(_ID_FIELD) || + !CSLTestBoolean(CPLGetConfigOption("COUCHDB_PRESERVE_ID_ON_INSERT", "FALSE"))) + { + if (poFeature->GetFID() != OGRNullFID) + { + nFID = (int)poFeature->GetFID(); + } + osFID = CPLSPrintf("%09d", nFID); + + poFeature->SetField(_ID_FIELD, osFID); + poFeature->SetFID(nFID); + } + else + { + const char* pszId = poFeature->GetFieldAsString(_ID_FIELD); + osFID = pszId; + } + + json_object* poObj = OGRCouchDBWriteFeature(poFeature, eGeomType, + bGeoJSONDocument, + nCoordPrecision); + + if (bInTransaction) + { + aoTransactionFeatures.push_back(poObj); + + return OGRERR_NONE; + } + + const char* pszJson = json_object_to_json_string( poObj ); + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/"; + osURI += osFID; + json_object* poAnswerObj = poDS->PUT(osURI, pszJson); + json_object_put( poObj ); + + if (poAnswerObj == NULL) + return OGRERR_FAILURE; + + if (!poDS->IsOK(poAnswerObj, "Feature creation failed")) + { + json_object_put(poAnswerObj); + return OGRERR_FAILURE; + } + + json_object* poId = json_object_object_get(poAnswerObj, "id"); + json_object* poRev = json_object_object_get(poAnswerObj, "rev"); + + const char* pszId = json_object_get_string(poId); + const char* pszRev = json_object_get_string(poRev); + + if (pszId) + { + poFeature->SetField(_ID_FIELD, pszId); + + int nFID = atoi(pszId); + const char* pszFID = CPLSPrintf("%09d", nFID); + if (strcmp(pszId, pszFID) == 0) + poFeature->SetFID(nFID); + else + poFeature->SetFID(-1); + } + if (pszRev) + { + poFeature->SetField(_REV_FIELD, pszRev); + } + + json_object_put(poAnswerObj); + + nUpdateSeq ++; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRCouchDBTableLayer::ISetFeature( OGRFeature *poFeature ) +{ + GetLayerDefn(); + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + if (!poFeature->IsFieldSet(_ID_FIELD)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "SetFeature() requires non null _id field"); + return OGRERR_FAILURE; + } + + json_object* poObj = OGRCouchDBWriteFeature(poFeature, eGeomType, + bGeoJSONDocument, + nCoordPrecision); + + const char* pszJson = json_object_to_json_string( poObj ); + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/"; + osURI += poFeature->GetFieldAsString(_ID_FIELD); + json_object* poAnswerObj = poDS->PUT(osURI, pszJson); + json_object_put( poObj ); + + if (poAnswerObj == NULL) + return OGRERR_FAILURE; + + if (!poDS->IsOK(poAnswerObj, "Feature update failed")) + { + json_object_put(poAnswerObj); + return OGRERR_FAILURE; + } + + json_object* poRev = json_object_object_get(poAnswerObj, "rev"); + const char* pszRev = json_object_get_string(poRev); + poFeature->SetField(_REV_FIELD, pszRev); + + json_object_put(poAnswerObj); + + if (bExtentValid && eGeomType != wkbNone) + { + bExtentValid = FALSE; + bMustWriteMetadata = TRUE; + } + nUpdateSeq ++; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRCouchDBTableLayer::DeleteFeature( GIntBig nFID ) +{ + GetLayerDefn(); + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + OGRFeature* poFeature = GetFeature(nFID); + if (poFeature == NULL) + return OGRERR_FAILURE; + + return DeleteFeature(poFeature); +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRCouchDBTableLayer::DeleteFeature( const char* pszId ) +{ + GetLayerDefn(); + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + OGRFeature* poFeature = GetFeature(pszId); + if (poFeature == NULL) + return OGRERR_FAILURE; + + return DeleteFeature(poFeature); +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRCouchDBTableLayer::DeleteFeature( OGRFeature* poFeature ) +{ + if (!poFeature->IsFieldSet(_ID_FIELD) || + !poFeature->IsFieldSet(_REV_FIELD)) + { + delete poFeature; + return OGRERR_FAILURE; + } + + const char* pszId = poFeature->GetFieldAsString(_ID_FIELD); + const char* pszRev = poFeature->GetFieldAsString(_REV_FIELD); + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/"; + osURI += CPLSPrintf("%s?rev=%s", pszId, pszRev); + + if (bExtentValid && eGeomType != wkbNone) + bMustWriteMetadata = TRUE; + + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if (bExtentValid && bExtentSet && poGeom != NULL && !poGeom->IsEmpty()) + { + OGREnvelope sEnvelope; + poGeom->getEnvelope(&sEnvelope); + if (dfMinX == sEnvelope.MinX || + dfMinY == sEnvelope.MinY || + dfMaxX == sEnvelope.MaxX || + dfMaxY == sEnvelope.MaxY) + { + bExtentValid = FALSE; + } + } + + delete poFeature; + + json_object* poAnswerObj = poDS->DELETE(osURI); + + if (poAnswerObj == NULL) + return OGRERR_FAILURE; + + if (!poDS->IsOK(poAnswerObj, "Feature deletion failed")) + { + json_object_put(poAnswerObj); + return OGRERR_FAILURE; + } + + nUpdateSeq ++; + + json_object_put(poAnswerObj); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +OGRErr OGRCouchDBTableLayer::StartTransaction() +{ + GetLayerDefn(); + + if (bInTransaction) + { + CPLError(CE_Failure, CPLE_AppDefined, "Already in transaction"); + return OGRERR_FAILURE; + } + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + bInTransaction = TRUE; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr OGRCouchDBTableLayer::CommitTransaction() +{ + GetLayerDefn(); + + if (!bInTransaction) + { + CPLError(CE_Failure, CPLE_AppDefined, "Should be in transaction"); + return OGRERR_FAILURE; + } + + bInTransaction = FALSE; + + if (aoTransactionFeatures.size() == 0) + return OGRERR_NONE; + + CPLString osPost("{ \"docs\": ["); + for(int i=0;i<(int)aoTransactionFeatures.size();i++) + { + if (i>0) osPost += ","; + const char* pszJson = json_object_to_json_string( aoTransactionFeatures[i] ); + osPost += pszJson; + json_object_put(aoTransactionFeatures[i]); + } + osPost += "] }"; + aoTransactionFeatures.resize(0); + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/_bulk_docs"; + json_object* poAnswerObj = poDS->POST(osURI, osPost); + + if (poAnswerObj == NULL) + return OGRERR_FAILURE; + + if ( json_object_is_type(poAnswerObj, json_type_object) ) + { + poDS->IsError(poAnswerObj, "Bulk feature creation failed"); + + json_object_put(poAnswerObj); + return OGRERR_FAILURE; + } + + if ( !json_object_is_type(poAnswerObj, json_type_array) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Bulk feature creation failed"); + json_object_put(poAnswerObj); + + return OGRERR_FAILURE; + } + + int nRows = json_object_array_length(poAnswerObj); + for(int i=0;iReference(); +} + +/************************************************************************/ +/* OGRCouchDBIsNumericObject() */ +/************************************************************************/ + +static int OGRCouchDBIsNumericObject(json_object* poObj) +{ + int iType = json_object_get_type(poObj); + return iType == json_type_int || iType == json_type_double; +} + +/************************************************************************/ +/* LoadMetadata() */ +/************************************************************************/ + +void OGRCouchDBTableLayer::LoadMetadata() +{ + if (bHasLoadedMetadata) + return; + + bHasLoadedMetadata = TRUE; + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/_design/ogr_metadata"; + json_object* poAnswerObj = poDS->GET(osURI); + if (poAnswerObj == NULL) + return; + + if ( !json_object_is_type(poAnswerObj, json_type_object) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "LoadMetadata() failed"); + json_object_put(poAnswerObj); + return; + } + + json_object* poRev = json_object_object_get(poAnswerObj, "_rev"); + const char* pszRev = json_object_get_string(poRev); + if (pszRev) + osMetadataRev = pszRev; + + json_object* poError = json_object_object_get(poAnswerObj, "error"); + const char* pszError = json_object_get_string(poError); + if (pszError && strcmp(pszError, "not_found") == 0) + { + json_object_put(poAnswerObj); + return; + } + + if (poDS->IsError(poAnswerObj, "LoadMetadata() failed")) + { + json_object_put(poAnswerObj); + return; + } + + json_object* poJsonSRS = json_object_object_get(poAnswerObj, "srs"); + const char* pszSRS = json_object_get_string(poJsonSRS); + if (pszSRS != NULL) + { + poSRS = new OGRSpatialReference(); + if (poSRS->importFromWkt((char**)&pszSRS) != OGRERR_NONE) + { + delete poSRS; + poSRS = NULL; + } + } + + json_object* poGeomType = json_object_object_get(poAnswerObj, "geomtype"); + const char* pszGeomType = json_object_get_string(poGeomType); + + if (pszGeomType) + { + if (EQUAL(pszGeomType, "NONE")) + { + eGeomType = wkbNone; + bExtentValid = TRUE; + } + else + { + eGeomType = OGRFromOGCGeomType(pszGeomType); + + json_object* poIs25D = json_object_object_get(poAnswerObj, "is_25D"); + if (poIs25D && json_object_get_boolean(poIs25D)) + eGeomType = wkbSetZ(eGeomType); + + json_object* poExtent = json_object_object_get(poAnswerObj, "extent"); + if (poExtent && json_object_get_type(poExtent) == json_type_object) + { + json_object* poUpdateSeq = + json_object_object_get(poExtent, "validity_update_seq"); + if (poUpdateSeq && json_object_get_type(poUpdateSeq) == json_type_int) + { + int nValidityUpdateSeq = json_object_get_int(poUpdateSeq); + if (nValidityUpdateSeq <= 0) + { + bAlwaysValid = TRUE; + } + else + { + if (nUpdateSeq < 0) + nUpdateSeq = FetchUpdateSeq(); + if (nUpdateSeq != nValidityUpdateSeq) + { + CPLDebug("CouchDB", + "_design/ogr_metadata.extent.validity_update_seq " + "doesn't match database update_seq --> ignoring stored extent"); + poUpdateSeq = NULL; + } + } + } + else + poUpdateSeq = NULL; + + json_object* poBbox = json_object_object_get(poExtent, "bbox"); + if (poUpdateSeq && poBbox && + json_object_get_type(poBbox) == json_type_array && + json_object_array_length(poBbox) == 4 && + OGRCouchDBIsNumericObject(json_object_array_get_idx(poBbox, 0)) && + OGRCouchDBIsNumericObject(json_object_array_get_idx(poBbox, 1)) && + OGRCouchDBIsNumericObject(json_object_array_get_idx(poBbox, 2)) && + OGRCouchDBIsNumericObject(json_object_array_get_idx(poBbox, 3))) + { + dfMinX = json_object_get_double(json_object_array_get_idx(poBbox, 0)); + dfMinY = json_object_get_double(json_object_array_get_idx(poBbox, 1)); + dfMaxX = json_object_get_double(json_object_array_get_idx(poBbox, 2)); + dfMaxY = json_object_get_double(json_object_array_get_idx(poBbox, 3)); + bExtentValid = bExtentSet = TRUE; + } + } + } + } + + json_object* poGeoJSON = json_object_object_get(poAnswerObj, "geojson_documents"); + if (poGeoJSON && json_object_is_type(poGeoJSON, json_type_boolean)) + bGeoJSONDocument = json_object_get_boolean(poGeoJSON); + + json_object* poFields = json_object_object_get(poAnswerObj, "fields"); + if (poFields && json_object_is_type(poFields, json_type_array)) + { + poFeatureDefn = new OGRFeatureDefn( osName ); + poFeatureDefn->Reference(); + + poFeatureDefn->SetGeomType(eGeomType); + if( poFeatureDefn->GetGeomFieldCount() != 0 ) + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + OGRFieldDefn oFieldId("_id", OFTString); + poFeatureDefn->AddFieldDefn(&oFieldId); + + OGRFieldDefn oFieldRev("_rev", OFTString); + poFeatureDefn->AddFieldDefn(&oFieldRev); + + int nFields = json_object_array_length(poFields); + for(int i=0;iAddFieldDefn(&oField); + } + } + } + } + + json_object_put(poAnswerObj); + + return; +} + +/************************************************************************/ +/* WriteMetadata() */ +/************************************************************************/ + +void OGRCouchDBTableLayer::WriteMetadata() +{ + GetLayerDefn(); + + CPLString osURI; + osURI = "/"; + osURI += osEscapedName; + osURI += "/_design/ogr_metadata"; + + json_object* poDoc = json_object_new_object(); + + if (osMetadataRev.size() > 0) + { + json_object_object_add(poDoc, "_rev", + json_object_new_string(osMetadataRev)); + } + + if (poSRS) + { + char* pszWKT = NULL; + poSRS->exportToWkt(&pszWKT); + if (pszWKT) + { + json_object_object_add(poDoc, "srs", + json_object_new_string(pszWKT)); + CPLFree(pszWKT); + } + } + + if (eGeomType != wkbNone) + { + json_object_object_add(poDoc, "geomtype", + json_object_new_string(OGRToOGCGeomType(eGeomType))); + if (wkbHasZ(poFeatureDefn->GetGeomType())) + { + json_object_object_add(poDoc, "is_25D", + json_object_new_boolean(TRUE)); + } + + if (bExtentValid && bExtentSet && nUpdateSeq >= 0) + { + json_object* poExtent = json_object_new_object(); + json_object_object_add(poDoc, "extent", poExtent); + + json_object_object_add(poExtent, "validity_update_seq", + json_object_new_int((bAlwaysValid) ? -1 : nUpdateSeq + 1)); + + json_object* poBbox = json_object_new_array(); + json_object_object_add(poExtent, "bbox", poBbox); + json_object_array_add(poBbox, json_object_new_double_with_precision(dfMinX, nCoordPrecision)); + json_object_array_add(poBbox, json_object_new_double_with_precision(dfMinY, nCoordPrecision)); + json_object_array_add(poBbox, json_object_new_double_with_precision(dfMaxX, nCoordPrecision)); + json_object_array_add(poBbox, json_object_new_double_with_precision(dfMaxY, nCoordPrecision)); + } + } + else + { + json_object_object_add(poDoc, "geomtype", + json_object_new_string("NONE")); + } + + json_object_object_add(poDoc, "geojson_documents", + json_object_new_boolean(bGeoJSONDocument)); + + json_object* poFields = json_object_new_array(); + json_object_object_add(poDoc, "fields", poFields); + + + for(int i=FIRST_FIELD;iGetFieldCount();i++) + { + json_object* poField = json_object_new_object(); + json_object_array_add(poFields, poField); + + json_object_object_add(poField, "name", + json_object_new_string(poFeatureDefn->GetFieldDefn(i)->GetNameRef())); + + const char* pszType = NULL; + switch (poFeatureDefn->GetFieldDefn(i)->GetType()) + { + case OFTInteger: pszType = "integer"; break; + case OFTReal: pszType = "real"; break; + case OFTString: pszType = "string"; break; + case OFTIntegerList: pszType = "integerlist"; break; + case OFTRealList: pszType = "reallist"; break; + case OFTStringList: pszType = "stringlist"; break; + default: pszType = "string"; break; + } + + json_object_object_add(poField, "type", + json_object_new_string(pszType)); + } + + json_object* poAnswerObj = poDS->PUT(osURI, + json_object_to_json_string(poDoc)); + + json_object_put(poDoc); + + if (poDS->IsOK(poAnswerObj, "Metadata creation failed")) + { + nUpdateSeq++; + + json_object* poRev = json_object_object_get(poAnswerObj, "_rev"); + const char* pszRev = json_object_get_string(poRev); + if (pszRev) + osMetadataRev = pszRev; + } + + json_object_put(poAnswerObj); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRCouchDBTableLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + LoadMetadata(); + + if (!bExtentValid) + return OGRCouchDBLayer::GetExtent(psExtent, bForce); + + psExtent->MinX = 0.0; + psExtent->MaxX = 0.0; + psExtent->MinY = 0.0; + psExtent->MaxY = 0.0; + + if (!bExtentSet) + return OGRERR_FAILURE; + + psExtent->MinX = dfMinX; + psExtent->MaxX = dfMaxX; + psExtent->MinY = dfMinY; + psExtent->MaxY = dfMaxY; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* FetchUpdateSeq() */ +/************************************************************************/ + +int OGRCouchDBTableLayer::FetchUpdateSeq() +{ + if (nUpdateSeq >= 0) + return nUpdateSeq; + + CPLString osURI("/"); + osURI += osEscapedName; + osURI += "/"; + + json_object* poAnswerObj = poDS->GET(osURI); + if (poAnswerObj != NULL && + json_object_is_type(poAnswerObj, json_type_object) && + json_object_object_get(poAnswerObj, "update_seq") != NULL) + { + nUpdateSeq = json_object_get_int(json_object_object_get(poAnswerObj, + "update_seq")); + } + else + { + poDS->IsError(poAnswerObj, "FetchUpdateSeq() failed"); + } + + json_object_put(poAnswerObj); + + return nUpdateSeq; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/GNUmakefile new file mode 100644 index 000000000..61a10d341 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/GNUmakefile @@ -0,0 +1,15 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrcsvdriver.o ogrcsvdatasource.o ogrcsvlayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_csv.h + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/drv_csv.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/drv_csv.html new file mode 100644 index 000000000..5f48439fb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/drv_csv.html @@ -0,0 +1,246 @@ + + +Comma Separated Value (.csv) + + + + +

    Comma Separated Value (.csv)

    + +

    OGR supports reading and writing primarily non-spatial tabular data stored in +text CSV files. CSV files are a common interchange format between +software packages supporting tabular data and are also easily produced +manually with a text editor or with end-user written scripts or programs.

    + +

    While in theory .csv files could have any extension, in order to +auto-recognise the format OGR only supports CSV files ending with the +extention ".csv". The datasource name may be either a single CSV file +or point to a directory. For a directory to be recognised as a .csv datasource +at least half the files in the directory need to have the extension .csv. +One layer (table) is produced from each .csv file accessed.

    + +

    Starting with GDAL 1.8.0, for files structured as CSV, but not ending with +.CSV extension, the 'CSV:' prefix can be added before the filename to force +loading by the CSV driver.

    + +

    The OGR CSV driver supports reading and writing. Because the CSV format +has variable length text lines, reading is done sequentially. Reading +features in random order will generally be very slow. OGR CSV layer never +have any coordinate system. When reading a field named "WKT" is assumed +to contain WKT geometry, but also is treated as a regular field. +The OGR CSV driver returns all attribute columns as string data types +if no field type information file (with .csvt extension) is +available.

    + +

    Limited type recognition can be done for Integer, Real, String, Date +(YYYY-MM-DD), Time (HH:MM:SS+nn), DateTime (YYYY-MM-DD HH:MM:SS+nn) columns +through a descriptive file with the same name as the CSV file, but a .csvt extension. +In a single line the types for each column have to be listed with double quotes and +be comma separated (e.g., "Integer","String"). It is also possible to specify +explicitly the width and precision of each column, e.g. "Integer(5)","Real(10.7)","String(15)". +The driver will then use these types as specified for the csv columns. +Starting with GDAL 2.0, subtypes can be passed between parenthesis, such as +"Integer(Boolean)", "Integer(Int16)" and "Real(Float32)"

    + +

    Starting with GDAL 2.0, automatic field type guessing can also be done if +specifying the open options described in the below "Open options" section.

    + +

    Format

    + +

    CSV files have one line for each feature (record) in the layer (table). +The attribute field values are separated by commas. At least two fields +per line must be present. Lines may be terminated by a DOS (CR/LF) or +Unix (LF) style line terminators. Each record should have the same number +of fields. The driver will also accept a semicolon, a tabulation or a space +(GDAL >= 2.0 for the later) character as field separator . This autodetection will work only +if there's no other potential separator on the first line of the CSV file. +Otherwise it will default to comma as separator.

    + +

    Complex attribute values (such as those containing commas, quotes or newlines) +may be placed in double quotes. Any occurances of double quotes within +the quoted string should be doubled up to "escape" them.

    + +

    The driver attempts to treat the first line of the file as a list of field +names for all the fields. However, if one or more of the names is all +numeric it is assumed that the first line is actually data values and +dummy field names are generated internally (field_1 through field_n) and +the first record is treated as a feature. Starting with GDAL 1.9.0 +numeric values are treated as field names if they are enclosed in double quotes. +

    + +

    All CSV files are treated as UTF-8 encoded. Starting with GDAL 1.9.0, a Byte +Order Mark (BOM) at the beginning of the file will be parsed correctly. From 1.9.2, +The option WRITE_BOM can be used to create a file with a Byte Order Mark, which can +improve compatibility with some software (particularly Excel). +

    + +Example (employee.csv): +
    +ID,Salary,Name,Comments
    +132,55000.0,John Walker,"The ""big"" cheese."
    +133,11000.0,Jane Lake,Cleaning Staff
    +
    + +

    Note that the Comments value for the first data record is placed in +double quotes because the value contains quotes, and those quotes have +to be doubled up so we know we haven't reached the end of the quoted string +yet.

    + +

    Many variations of textual input are sometimes called Comma Separated +Value files, including files without commas, but fixed column widths, +those using tabs as separators or those with other auxiliary data defining +field types or structure. This driver does not attempt to support all +such files, but instead to support simple .csv files that can be +auto-recognised. Scripts or other mechanisms can generally be used to convert +other variations into a form that is compatible with the OGR CSV driver.

    + +

    Reading CSV containing spatial information

    + +

    It is possible to extract spatial information (points) from a CSV file +which has columns for the X and Y coordinates, through the use of the +VRT driver.

    + +

    Consider the following CSV file (test.csv): +

    +Latitude,Longitude,Name
    +48.1,0.25,"First point"
    +49.2,1.1,"Second point"
    +47.5,0.75,"Third point"
    +
    + +You can write the associated VRT file (test.vrt): +
    +<OGRVRTDataSource>
    +    <OGRVRTLayer name="test">
    +        <SrcDataSource>test.csv</SrcDataSource>
    +        <GeometryType>wkbPoint</GeometryType>
    +        <LayerSRS>WGS84</LayerSRS>
    +        <GeometryField encoding="PointFromColumns" x="Longitude" y="Latitude"/>
    +    </OGRVRTLayer>
    +</OGRVRTDataSource>
    +
    +

    + +and ogrinfo -ro -al test.vrt will return : +
    +OGRFeature(test):1
    +  Latitude (String) = 48.1
    +  Longitude (String) = 0.25
    +  Name (String) = First point
    +  POINT (0.25 48.1 0)
    +
    +OGRFeature(test):2
    +  Latitude (String) = 49.2
    +  Longitude (String) = 1.1
    +  Name (String) = Second point
    +  POINT (1.1 49.200000000000003 0)
    +
    +OGRFeature(test):3
    +  Latitude (String) = 47.5
    +  Longitude (String) = 0.75
    +  Name (String) = Third point
    +  POINT (0.75 47.5 0)
    +
    + +

    Open options

    + +Starting with GDAL 2.0, the following open options can be specified +(typically with the -oo name=value parameters of ogrinfo or ogr2ogr): +
      +
    • MERGE_SEPARATOR=YES/NO (defaults to NO). Setting it to YES will +enable merging consecutive separators. Mostly useful when it is the space +character.
    • +
    • AUTODETECT_TYPE=YES/NO (defaults to NO). Setting it to YES will +enable auto-detection of field data types. If while reading the records +(beyond the records used for autodetection), a value is found to not correspond +to the autodetected data type, a warning will be emitted and the field will be +emptied.
    • +
    • KEEP_SOURCE_COLUMNS=YES/NO (default NO) keep a copy of the original +columns where the guessing is active, and the guessed type is different from +string. The name of the original columns will be suffixed with "_original". +This flag should be used only when AUTODETECT_TYPE=YES.
    • +
    • AUTODETECT_WIDTH=YES/NO/STRING_ONLY (defaults to NO). Setting it to +YES to detect the width of string and integer fields, and the width and precision +of real fields. Setting it to STRING_ONLY restricts to string fields. Setting it +to NO select default size and width. If while reading the records (beyond the +records used for autodetection), a value is found to not correspond to the +autodetected width/precision, a warning will be emitted and the field will be +emptied.
    • +
    • AUTODETECT_SIZE_LIMIT=size to specify the number of bytes to inspect +to determine the data type and width/precision. The default will be 100000. +Setting 0 means inspecting the whole file. Note : specifying a value over 1 MB +(or 0 if the file is larger than 1MB) will prevent reading from standard input.
    • +
    • QUOTED_FIELDS_AS_STRING=YES/NO (default NO). Only used if AUTODETECT_TYPE=YES. +Whether to enforce quoted fields as string fields when set to YES. Otherwise, +by default, the content of quoted fields will be tested for real, integer, etc... +data types.
    • +
    + +

    Creation Issues

    + +

    The driver supports creating new databases (as a directory +of .csv files), adding new .csv files to an existing directory or .csv +files or appending features to an existing .csv table. Deleting or +replacing existing features is not supported.

    + +

    Layer Creation options: + +

      +
    • LINEFORMAT: By default when creating new .csv files they are created with the line +termination conventions of the local platform (CR/LF on win32 or +LF on all other systems). This may be overridden through use of the +LINEFORMAT layer creation option which may have a value of CRLF +(DOS format) or LF (Unix format).
    • +

    • GEOMETRY (Starting with GDAL 1.6.0): By default, the geometry of a feature written to a .csv +file is discarded. It is possible to export the geometry in its WKT representation by specifying +GEOMETRY=AS_WKT. It is also possible to export point geometries into their X,Y,Z components (different +columns in the csv file) by specifying GEOMETRY=AS_XYZ, GEOMETRY=AS_XY or GEOMETRY=AS_YX. +The geometry column(s) will be prepended to the columns with the attributes values.
    • +

    • CREATE_CSVT=YES/NO (Starting with GDAL 1.7.0): Create the associated .csvt file (see above paragraph) +to describe the type of each column of the layer and its optional width and precision. Default value : NO
    • +

    • SEPARATOR=COMMA/SEMICOLON/TAB/SPACE (Starting with GDAL 1.7.0): Field separator character. Default value : COMMA
    • +

    • WRITE_BOM=YES/NO (Starting with GDAL >1.9.2): Write a UTF-8 Byte Order Mark (BOM) at the start of the file. Default value : NO
    • +

    +

    + +

    VSI Virtual File System API support

    + +(Some features below might require OGR >= 1.9.0)

    + +The driver supports reading and writing to files managed by VSI Virtual File System API, which include +"regular" files, as well as files in the /vsizip/ (read-write) , /vsigzip/ (read-only) , /vsicurl/ (read-only) domains.

    + +Writing to /dev/stdout or /vsistdout/ is also supported.

    + +

    Examples

    + +
      +
    • This example shows using ogr2ogr to transform a shapefile with point geometry into a .csv file with the X,Y,Z coordinates of the points as first columns in the .csv file +
      ogr2ogr -f CSV output.csv input.shp -lco GEOMETRY=AS_XYZ
      +
    • +
    + +

    Particular datasources

    + +The CSV driver can also read files whose structure is close to CSV files : +
      +
    • Airport data files NfdcFacilities.xls, NfdcRunways.xls, NfdcRemarks.xls and NfdcSchedules.xls + found on tha FAA website (OGR >= 1.8.0)
    • +
    • Files from the USGS GNIS (Geographic Names Information System) (OGR >= 1.9.0)
    • +
    • The allCountries file from GeoNames (OGR >= 1.9.0 for direct import)
    • +
    • Eurostat .TSV files (OGR >= 1.10.0)
    • +
    + +

    Other Notes

    + +

    +

    +

    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/makefile.vc new file mode 100644 index 000000000..b8b9c052e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrcsvdriver.obj ogrcsvdatasource.obj ogrcsvlayer.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/ogr_csv.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/ogr_csv.h new file mode 100644 index 000000000..8e73dd3dc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/ogr_csv.h @@ -0,0 +1,191 @@ +/****************************************************************************** + * $Id: ogr_csv.h 29237 2015-05-24 08:38:20Z rouault $ + * + * Project: CSV Translator + * Purpose: Definition of classes for OGR .csv driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_CSV_H_INCLUDED +#define _OGR_CSV_H_INCLUDED + +#include "ogrsf_frmts.h" + +typedef enum +{ + OGR_CSV_GEOM_NONE, + OGR_CSV_GEOM_AS_WKT, + OGR_CSV_GEOM_AS_XYZ, + OGR_CSV_GEOM_AS_XY, + OGR_CSV_GEOM_AS_YX, +} OGRCSVGeometryFormat; + +class OGRCSVDataSource; + +char **OGRCSVReadParseLineL( VSILFILE * fp, char chDelimiter, + int bDontHonourStrings = FALSE, + int bKeepLeadingAndClosingQuotes = FALSE, + int bMergeDelimiter = FALSE); + +/************************************************************************/ +/* OGRCSVLayer */ +/************************************************************************/ + +class OGRCSVLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + + VSILFILE *fpCSV; + + int nNextFID; + + int bHasFieldNames; + + OGRFeature * GetNextUnfilteredFeature(); + + int bNew; + int bInWriteMode; + int bUseCRLF; + int bNeedRewindBeforeRead; + OGRCSVGeometryFormat eGeometryFormat; + + char* pszFilename; + int bCreateCSVT; + int bWriteBOM; + char chDelimiter; + + int nCSVFieldCount; + int* panGeomFieldIndex; + int bFirstFeatureAppendedDuringSession; + int bHiddenWKTColumn; + + /*http://www.faa.gov/airports/airport_safety/airportdata_5010/menu/index.cfm specific */ + int iNfdcLatitudeS, iNfdcLongitudeS; + int bDontHonourStrings; + + /* GNIS specific */ + int iLongitudeField, iLatitudeField; + + int bIsEurostatTSV; + int nEurostatDims; + + GIntBig nTotalFeatures; + + char **AutodetectFieldTypes(char** papszOpenOptions, int nFieldCount); + + int bWarningBadTypeOrWidth; + int bKeepSourceColumns; + + int bMergeDelimiter; + + char **GetNextLineTokens(); + + public: + OGRCSVLayer( const char *pszName, VSILFILE *fp, const char *pszFilename, + int bNew, int bInWriteMode, char chDelimiter ); + ~OGRCSVLayer(); + + void BuildFeatureDefn( const char* pszNfdcGeomField = NULL, + const char* pszGeonamesGeomFieldPrefix = NULL, + char** papszOpenOptions = NULL ); + + void ResetReading(); + OGRFeature * GetNextFeature(); + virtual OGRFeature* GetFeature( GIntBig nFID ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr CreateGeomField( OGRGeomFieldDefn *poGeomField, + int bApproxOK = TRUE ); + + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + + void SetCRLF(int); + void SetWriteGeometry(OGRwkbGeometryType eGType, + OGRCSVGeometryFormat eGeometryFormat); + void SetCreateCSVT(int bCreateCSVT); + void SetWriteBOM(int bWriteBOM); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + + OGRErr WriteHeader(); +}; + +/************************************************************************/ +/* OGRCSVDataSource */ +/************************************************************************/ + +class OGRCSVDataSource : public OGRDataSource +{ + char *pszName; + + OGRCSVLayer **papoLayers; + int nLayers; + + int bUpdate; + + CPLString osDefaultCSVName; + + int bEnableGeometryFields; + + public: + OGRCSVDataSource(); + ~OGRCSVDataSource(); + + int Open( const char * pszFilename, + int bUpdate, int bForceAccept, + char** papszOpenOptions = NULL ); + int OpenTable( const char * pszFilename, + char** papszOpenOptions, + const char* pszNfdcRunwaysGeomField = NULL, + const char* pszGeonamesGeomFieldPrefix = NULL); + + const char *GetName() { return pszName; } + + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + virtual OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + + virtual OGRErr DeleteLayer(int); + + int TestCapability( const char * ); + + void SetDefaultCSVName( const char *pszName ) + { osDefaultCSVName = pszName; } + + void EnableGeometryFields() { bEnableGeometryFields = TRUE; } + + static CPLString GetRealExtension(CPLString osFilename); +}; + +#endif /* ndef _OGR_CSV_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/ogrcsvdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/ogrcsvdatasource.cpp new file mode 100644 index 000000000..db318ee33 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/ogrcsvdatasource.cpp @@ -0,0 +1,733 @@ +/****************************************************************************** + * $Id: ogrcsvdatasource.cpp 29237 2015-05-24 08:38:20Z rouault $ + * + * Project: CSV Translator + * Purpose: Implements OGRCSVDataSource class + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_csv.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" +#include "cpl_vsi_virtual.h" + +CPL_CVSID("$Id: ogrcsvdatasource.cpp 29237 2015-05-24 08:38:20Z rouault $"); + +/************************************************************************/ +/* OGRCSVDataSource() */ +/************************************************************************/ + +OGRCSVDataSource::OGRCSVDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; + + bUpdate = FALSE; + bEnableGeometryFields = FALSE; +} + +/************************************************************************/ +/* ~OGRCSVDataSource() */ +/************************************************************************/ + +OGRCSVDataSource::~OGRCSVDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRCSVDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return bUpdate; + else if( EQUAL(pszCap,ODsCDeleteLayer) ) + return bUpdate; + else if( EQUAL(pszCap,ODsCCreateGeomFieldAfterCreateLayer) ) + return bUpdate && bEnableGeometryFields; + else if( EQUAL(pszCap,ODsCCurveGeometries) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRCSVDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetRealExtension() */ +/************************************************************************/ + +CPLString OGRCSVDataSource::GetRealExtension(CPLString osFilename) +{ + CPLString osExt = CPLGetExtension(osFilename); + if( strncmp(osFilename, "/vsigzip/", 9) == 0 && EQUAL(osExt, "gz") ) + { + if( strlen(osFilename) > 7 && EQUAL(osFilename + strlen(osFilename) - 7, ".csv.gz") ) + osExt = "csv"; + else if( strlen(osFilename) > 7 && EQUAL(osFilename + strlen(osFilename) - 7, ".tsv.gz") ) + osExt = "tsv"; + } + return osExt; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRCSVDataSource::Open( const char * pszFilename, int bUpdateIn, + int bForceOpen, char** papszOpenOptions ) + +{ + pszName = CPLStrdup( pszFilename ); + bUpdate = bUpdateIn; + + if (bUpdateIn && bForceOpen && EQUAL(pszFilename, "/vsistdout/")) + return TRUE; + + /* For writable /vsizip/, do nothing more */ + if (bUpdateIn && bForceOpen && strncmp(pszFilename, "/vsizip/", 8) == 0) + return TRUE; + + CPLString osFilename(pszFilename); + CPLString osBaseFilename = CPLGetFilename(pszFilename); + CPLString osExt = GetRealExtension(osFilename); + pszFilename = NULL; + + int bIgnoreExtension = EQUALN(osFilename, "CSV:", 4); + int bUSGeonamesFile = FALSE; + /* int bGeonamesOrgFile = FALSE; */ + if (bIgnoreExtension) + { + osFilename = osFilename + 4; + } + + /* Those are *not* real .XLS files, but text file with tab as column separator */ + if (EQUAL(osBaseFilename, "NfdcFacilities.xls") || + EQUAL(osBaseFilename, "NfdcRunways.xls") || + EQUAL(osBaseFilename, "NfdcRemarks.xls") || + EQUAL(osBaseFilename, "NfdcSchedules.xls")) + { + if (bUpdateIn) + return FALSE; + bIgnoreExtension = TRUE; + } + else if ((EQUALN(osBaseFilename, "NationalFile_", 13) || + EQUALN(osBaseFilename, "POP_PLACES_", 11) || + EQUALN(osBaseFilename, "HIST_FEATURES_", 14) || + EQUALN(osBaseFilename, "US_CONCISE_", 11) || + EQUALN(osBaseFilename, "AllNames_", 9) || + EQUALN(osBaseFilename, "Feature_Description_History_", 28) || + EQUALN(osBaseFilename, "ANTARCTICA_", 11) || + EQUALN(osBaseFilename, "GOVT_UNITS_", 11) || + EQUALN(osBaseFilename, "NationalFedCodes_", 17) || + EQUALN(osBaseFilename, "AllStates_", 10) || + EQUALN(osBaseFilename, "AllStatesFedCodes_", 18) || + (strlen(osBaseFilename) > 2 && EQUALN(osBaseFilename+2, "_Features_", 10)) || + (strlen(osBaseFilename) > 2 && EQUALN(osBaseFilename+2, "_FedCodes_", 10))) && + (EQUAL(osExt, "txt") || EQUAL(osExt, "zip")) ) + { + if (bUpdateIn) + return FALSE; + bIgnoreExtension = TRUE; + bUSGeonamesFile = TRUE; + + if (EQUAL(osExt, "zip") && + strstr(osFilename, "/vsizip/") == NULL ) + { + osFilename = "/vsizip/" + osFilename; + } + } + else if (EQUAL(osBaseFilename, "allCountries.txt") || + EQUAL(osBaseFilename, "allCountries.zip")) + { + if (bUpdateIn) + return FALSE; + bIgnoreExtension = TRUE; + /* bGeonamesOrgFile = TRUE; */ + + if (EQUAL(osExt, "zip") && + strstr(osFilename, "/vsizip/") == NULL ) + { + osFilename = "/vsizip/" + osFilename; + } + } + +/* -------------------------------------------------------------------- */ +/* Determine what sort of object this is. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + + if( VSIStatExL( osFilename, &sStatBuf, VSI_STAT_NATURE_FLAG ) != 0 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Is this a single CSV file? */ +/* -------------------------------------------------------------------- */ + if( VSI_ISREG(sStatBuf.st_mode) + && (bIgnoreExtension || EQUAL(osExt,"csv") || EQUAL(osExt,"tsv")) ) + { + if (EQUAL(CPLGetFilename(osFilename), "NfdcFacilities.xls")) + { + return OpenTable( osFilename, papszOpenOptions, "ARP"); + } + else if (EQUAL(CPLGetFilename(osFilename), "NfdcRunways.xls")) + { + OpenTable( osFilename, papszOpenOptions, "BaseEndPhysical"); + OpenTable( osFilename, papszOpenOptions, "BaseEndDisplaced"); + OpenTable( osFilename, papszOpenOptions, "ReciprocalEndPhysical"); + OpenTable( osFilename, papszOpenOptions, "ReciprocalEndDisplaced"); + return nLayers != 0; + } + else if (bUSGeonamesFile) + { + /* GNIS specific */ + if (EQUALN(osBaseFilename, "NationalFedCodes_", 17) || + EQUALN(osBaseFilename, "AllStatesFedCodes_", 18) || + EQUALN(osBaseFilename, "ANTARCTICA_", 11) || + (strlen(osBaseFilename) > 2 && EQUALN(osBaseFilename+2, "_FedCodes_", 10))) + { + OpenTable( osFilename, papszOpenOptions, NULL, "PRIMARY"); + } + else if (EQUALN(osBaseFilename, "GOVT_UNITS_", 11) || + EQUALN(osBaseFilename, "Feature_Description_History_", 28)) + { + OpenTable( osFilename, papszOpenOptions, NULL, ""); + } + else + { + OpenTable( osFilename, papszOpenOptions, NULL, "PRIM"); + OpenTable( osFilename, papszOpenOptions, NULL, "SOURCE"); + } + return nLayers != 0; + } + + return OpenTable( osFilename, papszOpenOptions ); + } + +/* -------------------------------------------------------------------- */ +/* Is this a single a ZIP file with only a CSV file inside ? */ +/* -------------------------------------------------------------------- */ + if( strncmp(osFilename, "/vsizip/", 8) == 0 && + EQUAL(osExt, "zip") && + VSI_ISREG(sStatBuf.st_mode) ) + { + char** papszFiles = VSIReadDir(osFilename); + if (CSLCount(papszFiles) != 1 || + !EQUAL(CPLGetExtension(papszFiles[0]), "CSV")) + { + CSLDestroy(papszFiles); + return FALSE; + } + osFilename = CPLFormFilename(osFilename, papszFiles[0], NULL); + CSLDestroy(papszFiles); + return OpenTable( osFilename, papszOpenOptions ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise it has to be a directory. */ +/* -------------------------------------------------------------------- */ + if( !VSI_ISDIR(sStatBuf.st_mode) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Scan through for entries ending in .csv. */ +/* -------------------------------------------------------------------- */ + int nNotCSVCount = 0, i; + char **papszNames = CPLReadDir( osFilename ); + + for( i = 0; papszNames != NULL && papszNames[i] != NULL; i++ ) + { + CPLString oSubFilename = + CPLFormFilename( osFilename, papszNames[i], NULL ); + + if( EQUAL(papszNames[i],".") || EQUAL(papszNames[i],"..") ) + continue; + + if (EQUAL(CPLGetExtension(oSubFilename),"csvt")) + continue; + + if( VSIStatL( oSubFilename, &sStatBuf ) != 0 + || !VSI_ISREG(sStatBuf.st_mode) ) + { + nNotCSVCount++; + continue; + } + + if (EQUAL(CPLGetExtension(oSubFilename),"csv")) + { + if( !OpenTable( oSubFilename, papszOpenOptions ) ) + { + CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str()); + nNotCSVCount++; + continue; + } + } + + /* GNIS specific */ + else if ( strlen(papszNames[i]) > 2 && + EQUALN(papszNames[i]+2, "_Features_", 10) && + EQUAL(CPLGetExtension(papszNames[i]), "txt") ) + { + int bRet = OpenTable( oSubFilename, papszOpenOptions, NULL, "PRIM"); + bRet |= OpenTable( oSubFilename, papszOpenOptions, NULL, "SOURCE"); + if ( !bRet ) + { + CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str()); + nNotCSVCount++; + continue; + } + } + /* GNIS specific */ + else if ( strlen(papszNames[i]) > 2 && + EQUALN(papszNames[i]+2, "_FedCodes_", 10) && + EQUAL(CPLGetExtension(papszNames[i]), "txt") ) + { + if ( !OpenTable( oSubFilename, papszOpenOptions, NULL, "PRIMARY") ) + { + CPLDebug("CSV", "Cannot open %s", oSubFilename.c_str()); + nNotCSVCount++; + continue; + } + } + else + { + nNotCSVCount++; + continue; + } + } + + CSLDestroy( papszNames ); + +/* -------------------------------------------------------------------- */ +/* We presume that this is indeed intended to be a CSV */ +/* datasource if over half the files were .csv files. */ +/* -------------------------------------------------------------------- */ + return bForceOpen || nNotCSVCount < nLayers; +} + +/************************************************************************/ +/* OpenTable() */ +/************************************************************************/ + +int OGRCSVDataSource::OpenTable( const char * pszFilename, + char** papszOpenOptions, + const char* pszNfdcRunwaysGeomField, + const char* pszGeonamesGeomFieldPrefix) + +{ +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE * fp; + + if( bUpdate ) + fp = VSIFOpenL( pszFilename, "rb+" ); + else + fp = VSIFOpenL( pszFilename, "rb" ); + if( fp == NULL ) + { + CPLError( CE_Warning, CPLE_OpenFailed, + "Failed to open %s, %s.", + pszFilename, VSIStrerror( errno ) ); + return FALSE; + } + + if( !bUpdate && strstr(pszFilename, "/vsigzip/") == NULL && + strstr(pszFilename, "/vsizip/") == NULL ) + fp = (VSILFILE*) VSICreateBufferedReaderHandle((VSIVirtualHandle*)fp); + + CPLString osLayerName = CPLGetBasename(pszFilename); + CPLString osExt = CPLGetExtension(pszFilename); + if( strncmp(pszFilename, "/vsigzip/", 9) == 0 && EQUAL(osExt, "gz") ) + { + if( strlen(pszFilename) > 7 && EQUAL(pszFilename + strlen(pszFilename) - 7, ".csv.gz") ) + { + osLayerName = osLayerName.substr(0, osLayerName.size() - 4); + osExt = "csv"; + } + else if( strlen(pszFilename) > 7 && EQUAL(pszFilename + strlen(pszFilename) - 7, ".tsv.gz") ) + { + osLayerName = osLayerName.substr(0, osLayerName.size() - 4); + osExt = "tsv"; + } + } + +/* -------------------------------------------------------------------- */ +/* Read and parse a line. Did we get multiple fields? */ +/* -------------------------------------------------------------------- */ + + const char* pszLine = CPLReadLineL( fp ); + if (pszLine == NULL) + { + VSIFCloseL( fp ); + return FALSE; + } + char chDelimiter = CSVDetectSeperator(pszLine); +#if 0 + const char *pszDelimiter = CSLFetchNameValueDef( papszOpenOptions, "SEPARATOR", "AUTO"); + if( !EQUAL(pszDelimiter, "AUTO") ) + { + if (EQUAL(pszDelimiter, "COMMA")) + chDelimiter = ','; + else if (EQUAL(pszDelimiter, "SEMICOLON")) + chDelimiter = ';'; + else if (EQUAL(pszDelimiter, "TAB")) + chDelimiter = '\t'; + else if (EQUAL(pszDelimiter, "SPACE")) + chDelimiter = ' '; + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "SEPARATOR=%s not understood, use one of COMMA, SEMICOLON, SPACE or TAB.", + pszDelimiter ); + } + } +#endif + + /* Force the delimiter to be TAB for a .tsv file that has a tabulation */ + /* in its first line */ + if( EQUAL(osExt, "tsv") && chDelimiter != '\t' && + strchr(pszLine, '\t') != NULL ) + { + chDelimiter = '\t'; + } + + VSIRewindL( fp ); + + /* GNIS specific */ + if (pszGeonamesGeomFieldPrefix != NULL && + strchr(pszLine, '|') != NULL) + chDelimiter = '|'; + + char **papszFields = OGRCSVReadParseLineL( fp, chDelimiter, FALSE ); + + if( CSLCount(papszFields) < 2 ) + { + VSIFCloseL( fp ); + CSLDestroy( papszFields ); + return FALSE; + } + + VSIRewindL( fp ); + CSLDestroy( papszFields ); + +/* -------------------------------------------------------------------- */ +/* Create a layer. */ +/* -------------------------------------------------------------------- */ + nLayers++; + papoLayers = (OGRCSVLayer **) CPLRealloc(papoLayers, + sizeof(void*) * nLayers); + + if (pszNfdcRunwaysGeomField != NULL) + { + osLayerName += "_"; + osLayerName += pszNfdcRunwaysGeomField; + } + else if (pszGeonamesGeomFieldPrefix != NULL && + !EQUAL(pszGeonamesGeomFieldPrefix, "")) + { + osLayerName += "_"; + osLayerName += pszGeonamesGeomFieldPrefix; + } + if (EQUAL(pszFilename, "/vsistdin/")) + osLayerName = "layer"; + papoLayers[nLayers-1] = + new OGRCSVLayer( osLayerName, fp, pszFilename, FALSE, bUpdate, + chDelimiter ); + papoLayers[nLayers-1]->BuildFeatureDefn( pszNfdcRunwaysGeomField, + pszGeonamesGeomFieldPrefix, + papszOpenOptions ); + return TRUE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRCSVDataSource::ICreateLayer( const char *pszLayerName, + CPL_UNUSED OGRSpatialReference *poSpatialRef, + OGRwkbGeometryType eGType, + char ** papszOptions ) +{ +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if (!bUpdate) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened read-only.\n" + "New layer %s cannot be created.\n", + pszName, pszLayerName ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Verify that the datasource is a directory. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + + if( strncmp(pszName, "/vsizip/", 8) == 0) + { + /* Do nothing */ + } + else if( !EQUAL(pszName, "/vsistdout/") && + (VSIStatL( pszName, &sStatBuf ) != 0 + || !VSI_ISDIR( sStatBuf.st_mode )) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create csv layer (file) against a non-directory datasource." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* What filename would we use? */ +/* -------------------------------------------------------------------- */ + CPLString osFilename; + + if( osDefaultCSVName != "" ) + { + osFilename = CPLFormFilename( pszName, osDefaultCSVName, NULL ); + osDefaultCSVName = ""; + } + else + { + osFilename = CPLFormFilename( pszName, pszLayerName, "csv" ); + } + +/* -------------------------------------------------------------------- */ +/* Does this directory/file already exist? */ +/* -------------------------------------------------------------------- */ + if( VSIStatL( osFilename, &sStatBuf ) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create layer %s, but %s already exists.", + pszLayerName, osFilename.c_str() ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the empty file. */ +/* -------------------------------------------------------------------- */ + + const char *pszDelimiter = CSLFetchNameValue( papszOptions, "SEPARATOR"); + char chDelimiter = ','; + if (pszDelimiter != NULL) + { + if (EQUAL(pszDelimiter, "COMMA")) + chDelimiter = ','; + else if (EQUAL(pszDelimiter, "SEMICOLON")) + chDelimiter = ';'; + else if (EQUAL(pszDelimiter, "TAB")) + chDelimiter = '\t'; + else if (EQUAL(pszDelimiter, "SPACE")) + chDelimiter = ' '; + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "SEPARATOR=%s not understood, use one of COMMA, SEMICOLON, SPACE or TAB.", + pszDelimiter ); + } + } + +/* -------------------------------------------------------------------- */ +/* Create a layer. */ +/* -------------------------------------------------------------------- */ + nLayers++; + papoLayers = (OGRCSVLayer **) CPLRealloc(papoLayers, + sizeof(void*) * nLayers); + + papoLayers[nLayers-1] = new OGRCSVLayer( pszLayerName, NULL, osFilename, + TRUE, TRUE, chDelimiter ); + papoLayers[nLayers-1]->BuildFeatureDefn(); + +/* -------------------------------------------------------------------- */ +/* Was a partiuclar CRLF order requested? */ +/* -------------------------------------------------------------------- */ + const char *pszCRLFFormat = CSLFetchNameValue( papszOptions, "LINEFORMAT"); + int bUseCRLF; + + if( pszCRLFFormat == NULL ) + { +#ifdef WIN32 + bUseCRLF = TRUE; +#else + bUseCRLF = FALSE; +#endif + } + else if( EQUAL(pszCRLFFormat,"CRLF") ) + bUseCRLF = TRUE; + else if( EQUAL(pszCRLFFormat,"LF") ) + bUseCRLF = FALSE; + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "LINEFORMAT=%s not understood, use one of CRLF or LF.", + pszCRLFFormat ); +#ifdef WIN32 + bUseCRLF = TRUE; +#else + bUseCRLF = FALSE; +#endif + } + + papoLayers[nLayers-1]->SetCRLF( bUseCRLF ); + +/* -------------------------------------------------------------------- */ +/* Should we write the geometry ? */ +/* -------------------------------------------------------------------- */ + const char *pszGeometry = CSLFetchNameValue( papszOptions, "GEOMETRY"); + if( bEnableGeometryFields ) + { + papoLayers[nLayers-1]->SetWriteGeometry(eGType, OGR_CSV_GEOM_AS_WKT); + } + else if (pszGeometry != NULL) + { + if (EQUAL(pszGeometry, "AS_WKT")) + { + papoLayers[nLayers-1]->SetWriteGeometry(eGType, OGR_CSV_GEOM_AS_WKT); + } + else if (EQUAL(pszGeometry, "AS_XYZ") || + EQUAL(pszGeometry, "AS_XY") || + EQUAL(pszGeometry, "AS_YX")) + { + if (eGType == wkbUnknown || wkbFlatten(eGType) == wkbPoint) + { + papoLayers[nLayers-1]->SetWriteGeometry(eGType, + EQUAL(pszGeometry, "AS_XYZ") ? OGR_CSV_GEOM_AS_XYZ : + EQUAL(pszGeometry, "AS_XY") ? OGR_CSV_GEOM_AS_XY : + OGR_CSV_GEOM_AS_YX); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Geometry type %s is not compatible with GEOMETRY=AS_XYZ.", + OGRGeometryTypeToName(eGType) ); + } + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unsupported value %s for creation option GEOMETRY", + pszGeometry ); + } + } + +/* -------------------------------------------------------------------- */ +/* Should we create a CSVT file ? */ +/* -------------------------------------------------------------------- */ + + const char *pszCreateCSVT = CSLFetchNameValue( papszOptions, "CREATE_CSVT"); + if (pszCreateCSVT) + papoLayers[nLayers-1]->SetCreateCSVT(CSLTestBoolean(pszCreateCSVT)); + +/* -------------------------------------------------------------------- */ +/* Should we write a UTF8 BOM ? */ +/* -------------------------------------------------------------------- */ + + const char *pszWriteBOM = CSLFetchNameValue( papszOptions, "WRITE_BOM"); + if (pszWriteBOM) + papoLayers[nLayers-1]->SetWriteBOM(CSLTestBoolean(pszWriteBOM)); + + return papoLayers[nLayers-1]; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +OGRErr OGRCSVDataSource::DeleteLayer( int iLayer ) + +{ + char *pszFilename; + char *pszFilenameCSVT; + +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( !bUpdate ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened read-only.\n" + "Layer %d cannot be deleted.\n", + pszName, iLayer ); + + return OGRERR_FAILURE; + } + + if( iLayer < 0 || iLayer >= nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %d not in legal range of 0 to %d.", + iLayer, nLayers-1 ); + return OGRERR_FAILURE; + } + + pszFilename = + CPLStrdup(CPLFormFilename(pszName,papoLayers[iLayer]->GetLayerDefn()->GetName(),"csv")); + pszFilenameCSVT = + CPLStrdup(CPLFormFilename(pszName,papoLayers[iLayer]->GetLayerDefn()->GetName(),"csvt")); + + delete papoLayers[iLayer]; + + while( iLayer < nLayers - 1 ) + { + papoLayers[iLayer] = papoLayers[iLayer+1]; + iLayer++; + } + + nLayers--; + + VSIUnlink( pszFilename ); + CPLFree( pszFilename ); + VSIUnlink( pszFilenameCSVT ); + CPLFree( pszFilenameCSVT ); + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/ogrcsvdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/ogrcsvdriver.cpp new file mode 100644 index 000000000..9044ee409 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/ogrcsvdriver.cpp @@ -0,0 +1,309 @@ +/****************************************************************************** + * $Id: ogrcsvdriver.cpp 29237 2015-05-24 08:38:20Z rouault $ + * + * Project: CSV Translator + * Purpose: Implements OGRCSVDriver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_csv.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrcsvdriver.cpp 29237 2015-05-24 08:38:20Z rouault $"); + +/************************************************************************/ +/* OGRCSVDriverIdentify() */ +/************************************************************************/ + +static int OGRCSVDriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->fpL != NULL ) + { + CPLString osBaseFilename = CPLGetFilename(poOpenInfo->pszFilename); + CPLString osExt = OGRCSVDataSource::GetRealExtension(poOpenInfo->pszFilename); + + if (EQUAL(osBaseFilename, "NfdcFacilities.xls") || + EQUAL(osBaseFilename, "NfdcRunways.xls") || + EQUAL(osBaseFilename, "NfdcRemarks.xls") || + EQUAL(osBaseFilename, "NfdcSchedules.xls")) + { + return TRUE; + } + else if ((EQUALN(osBaseFilename, "NationalFile_", 13) || + EQUALN(osBaseFilename, "POP_PLACES_", 11) || + EQUALN(osBaseFilename, "HIST_FEATURES_", 14) || + EQUALN(osBaseFilename, "US_CONCISE_", 11) || + EQUALN(osBaseFilename, "AllNames_", 9) || + EQUALN(osBaseFilename, "Feature_Description_History_", 28) || + EQUALN(osBaseFilename, "ANTARCTICA_", 11) || + EQUALN(osBaseFilename, "GOVT_UNITS_", 11) || + EQUALN(osBaseFilename, "NationalFedCodes_", 17) || + EQUALN(osBaseFilename, "AllStates_", 10) || + EQUALN(osBaseFilename, "AllStatesFedCodes_", 18) || + (strlen(osBaseFilename) > 2 && EQUALN(osBaseFilename+2, "_Features_", 10)) || + (strlen(osBaseFilename) > 2 && EQUALN(osBaseFilename+2, "_FedCodes_", 10))) && + (EQUAL(osExt, "txt") || EQUAL(osExt, "zip")) ) + { + return TRUE; + } + else if (EQUAL(osBaseFilename, "allCountries.txt") || + EQUAL(osBaseFilename, "allCountries.zip")) + { + return TRUE; + } + else if (EQUAL(osExt,"csv") || EQUAL(osExt,"tsv")) + { + return TRUE; + } + else if (strncmp(poOpenInfo->pszFilename, "/vsizip/", 8) == 0 && + EQUAL(osExt,"zip")) + { + return -1; /* unsure */ + } + else + { + return FALSE; + } + } + else if( EQUALN(poOpenInfo->pszFilename, "CSV:", 4) ) + { + return TRUE; + } + else if ( poOpenInfo->bIsDirectory ) + { + return -1; /* unsure */ + } + else + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRCSVDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( OGRCSVDriverIdentify(poOpenInfo) == FALSE ) + return NULL; + + OGRCSVDataSource *poDS = new OGRCSVDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, poOpenInfo->eAccess == GA_Update, FALSE, + poOpenInfo->papszOpenOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRCSVDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ +/* -------------------------------------------------------------------- */ +/* First, ensure there isn't any such file yet. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + + if (strcmp(pszName, "/dev/stdout") == 0) + pszName = "/vsistdout/"; + + if( VSIStatL( pszName, &sStatBuf ) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "It seems a file system object called '%s' already exists.", + pszName ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* If the target is not a simple .csv then create it as a */ +/* directory. */ +/* -------------------------------------------------------------------- */ + CPLString osDirName; + + if( EQUAL(CPLGetExtension(pszName),"csv") ) + { + osDirName = CPLGetPath(pszName); + if( osDirName == "" ) + osDirName = "."; + + /* HACK: CPLGetPath("/vsimem/foo.csv") = "/vsimem", but this is not */ + /* recognized afterwards as a valid directory name */ + if( osDirName == "/vsimem" ) + osDirName = "/vsimem/"; + } + else + { + if( strncmp(pszName, "/vsizip/", 8) == 0) + { + /* do nothing */ + } + else if( !EQUAL(pszName, "/vsistdout/") && + VSIMkdir( pszName, 0755 ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to create directory %s:\n%s", + pszName, VSIStrerror( errno ) ); + return NULL; + } + osDirName = pszName; + } + +/* -------------------------------------------------------------------- */ +/* Force it to open as a datasource. */ +/* -------------------------------------------------------------------- */ + OGRCSVDataSource *poDS = new OGRCSVDataSource(); + + if( !poDS->Open( osDirName, TRUE, TRUE ) ) + { + delete poDS; + return NULL; + } + + if( osDirName != pszName ) + poDS->SetDefaultCSVName( CPLGetFilename(pszName) ); + + const char *pszGeometry = CSLFetchNameValue( papszOptions, "GEOMETRY"); + if (pszGeometry != NULL && EQUAL(pszGeometry, "AS_WKT")) + poDS->EnableGeometryFields(); + + return poDS; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +static CPLErr OGRCSVDriverDelete( const char *pszFilename ) + +{ + if( CPLUnlinkTree( pszFilename ) == 0 ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* RegisterOGRCSV() */ +/************************************************************************/ + +void RegisterOGRCSV() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "CSV" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "CSV" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Comma Separated Value (.csv)" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "csv" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_csv.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +""); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" " +#ifdef WIN32 +" " +" " +" "); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +#if 0 +" " +#endif +" " +" "); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime Time" ); + + poDriver->pfnOpen = OGRCSVDriverOpen; + poDriver->pfnIdentify = OGRCSVDriverIdentify; + poDriver->pfnCreate = OGRCSVDriverCreate; + poDriver->pfnDelete = OGRCSVDriverDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/ogrcsvlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/ogrcsvlayer.cpp new file mode 100644 index 000000000..74ffde150 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csv/ogrcsvlayer.cpp @@ -0,0 +1,2016 @@ +/****************************************************************************** + * $Id: ogrcsvlayer.cpp 29237 2015-05-24 08:38:20Z rouault $ + * + * Project: CSV Translator + * Purpose: Implements OGRCSVLayer class. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_csv.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrcsvlayer.cpp 29237 2015-05-24 08:38:20Z rouault $"); + + + +/************************************************************************/ +/* CSVSplitLine() */ +/* */ +/* Tokenize a CSV line into fields in the form of a string */ +/* list. This is used instead of the CPLTokenizeString() */ +/* because it provides correct CSV escaping and quoting */ +/* semantics. */ +/************************************************************************/ + +static char **CSVSplitLine( const char *pszString, char chDelimiter, + int bKeepLeadingAndClosingQuotes, + int bMergeDelimiter ) + +{ + char **papszRetList = NULL; + char *pszToken; + int nTokenMax, nTokenLen; + + pszToken = (char *) CPLCalloc(10,1); + nTokenMax = 10; + + while( pszString != NULL && *pszString != '\0' ) + { + int bInString = FALSE; + + nTokenLen = 0; + + /* Try to find the next delimeter, marking end of token */ + for( ; *pszString != '\0'; pszString++ ) + { + + /* End if this is a delimeter skip it and break. */ + if( !bInString && *pszString == chDelimiter ) + { + pszString++; + if( bMergeDelimiter ) + { + while( *pszString == chDelimiter ) + pszString ++; + } + break; + } + + if( *pszString == '"' ) + { + if( !bInString || pszString[1] != '"' ) + { + bInString = !bInString; + if( !bKeepLeadingAndClosingQuotes ) + continue; + } + else /* doubled quotes in string resolve to one quote */ + { + pszString++; + } + } + + if( nTokenLen >= nTokenMax-2 ) + { + nTokenMax = nTokenMax * 2 + 10; + pszToken = (char *) CPLRealloc( pszToken, nTokenMax ); + } + + pszToken[nTokenLen] = *pszString; + nTokenLen++; + } + + pszToken[nTokenLen] = '\0'; + papszRetList = CSLAddString( papszRetList, pszToken ); + + /* If the last token is an empty token, then we have to catch + * it now, otherwise we won't reenter the loop and it will be lost. + */ + if ( *pszString == '\0' && *(pszString-1) == chDelimiter ) + { + papszRetList = CSLAddString( papszRetList, "" ); + } + } + + if( papszRetList == NULL ) + papszRetList = (char **) CPLCalloc(sizeof(char *),1); + + CPLFree( pszToken ); + + return papszRetList; +} + +/************************************************************************/ +/* OGRCSVReadParseLineL() */ +/* */ +/* Read one line, and return split into fields. The return */ +/* result is a stringlist, in the sense of the CSL functions. */ +/************************************************************************/ + +char **OGRCSVReadParseLineL( VSILFILE * fp, char chDelimiter, + int bDontHonourStrings, + int bKeepLeadingAndClosingQuotes, + int bMergeDelimiter ) + +{ + const char *pszLine; + char *pszWorkLine; + char **papszReturn; + + pszLine = CPLReadLineL( fp ); + if( pszLine == NULL ) + return( NULL ); + + /* Skip BOM */ + GByte* pabyData = (GByte*) pszLine; + if (pabyData[0] == 0xEF && pabyData[1] == 0xBB && pabyData[2] == 0xBF) + pszLine += 3; + + /* Special fix to read NdfcFacilities.xls that has non-balanced double quotes */ + if (chDelimiter == '\t' && bDontHonourStrings) + { + return CSLTokenizeStringComplex(pszLine, "\t", FALSE, TRUE); + } + +/* -------------------------------------------------------------------- */ +/* If there are no quotes, then this is the simple case. */ +/* Parse, and return tokens. */ +/* -------------------------------------------------------------------- */ + if( strchr(pszLine,'\"') == NULL ) + return CSVSplitLine( pszLine, chDelimiter, bKeepLeadingAndClosingQuotes, + bMergeDelimiter ); + +/* -------------------------------------------------------------------- */ +/* We must now count the quotes in our working string, and as */ +/* long as it is odd, keep adding new lines. */ +/* -------------------------------------------------------------------- */ + pszWorkLine = CPLStrdup( pszLine ); + + int i = 0, nCount = 0; + int nWorkLineLength = strlen(pszWorkLine); + + while( TRUE ) + { + for( ; pszWorkLine[i] != '\0'; i++ ) + { + if( pszWorkLine[i] == '\"' ) + nCount++; + } + + if( nCount % 2 == 0 ) + break; + + pszLine = CPLReadLineL( fp ); + if( pszLine == NULL ) + break; + + int nLineLen = strlen(pszLine); + + char* pszWorkLineTmp = (char *) + VSIRealloc(pszWorkLine, + nWorkLineLength + nLineLen + 2); + if (pszWorkLineTmp == NULL) + break; + pszWorkLine = pszWorkLineTmp; + strcat( pszWorkLine + nWorkLineLength, "\n" ); // This gets lost in CPLReadLine(). + strcat( pszWorkLine + nWorkLineLength, pszLine ); + + nWorkLineLength += nLineLen + 1; + } + + papszReturn = CSVSplitLine( pszWorkLine, chDelimiter, + bKeepLeadingAndClosingQuotes, bMergeDelimiter ); + + CPLFree( pszWorkLine ); + + return papszReturn; +} + +/************************************************************************/ +/* OGRCSVLayer() */ +/* */ +/* Note that the OGRCSVLayer assumes ownership of the passed */ +/* file pointer. */ +/************************************************************************/ + +OGRCSVLayer::OGRCSVLayer( const char *pszLayerNameIn, + VSILFILE * fp, const char *pszFilename, + int bNew, int bInWriteMode, + char chDelimiter ) + +{ + fpCSV = fp; + + nCSVFieldCount = 0; + panGeomFieldIndex = NULL; + iNfdcLatitudeS = iNfdcLongitudeS = -1; + iLatitudeField = iLongitudeField = -1; + bHasFieldNames = FALSE; + this->bInWriteMode = bInWriteMode; + this->bNew = bNew; + this->pszFilename = CPLStrdup(pszFilename); + this->chDelimiter = chDelimiter; + + bFirstFeatureAppendedDuringSession = TRUE; + bHiddenWKTColumn = FALSE; + bUseCRLF = FALSE; + bNeedRewindBeforeRead = FALSE; + eGeometryFormat = OGR_CSV_GEOM_NONE; + + nNextFID = 1; + + poFeatureDefn = new OGRFeatureDefn( pszLayerNameIn ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + bCreateCSVT = FALSE; + bDontHonourStrings = FALSE; + bWriteBOM = FALSE; + + bIsEurostatTSV = FALSE; + nEurostatDims = 0; + + nTotalFeatures = -1; + bWarningBadTypeOrWidth = FALSE; + bKeepSourceColumns = FALSE; + + bMergeDelimiter = FALSE; +} + +/************************************************************************/ +/* BuildFeatureDefn() */ +/************************************************************************/ + +void OGRCSVLayer::BuildFeatureDefn( const char* pszNfdcGeomField, + const char* pszGeonamesGeomFieldPrefix, + char** papszOpenOptions ) +{ + + bMergeDelimiter = CSLFetchBoolean(papszOpenOptions, "MERGE_SEPARATOR", FALSE); + +/* -------------------------------------------------------------------- */ +/* If this is not a new file, read ahead to establish if it is */ +/* already in CRLF (DOS) mode, or just a normal unix CR mode. */ +/* -------------------------------------------------------------------- */ + if( !bNew && bInWriteMode ) + { + int nBytesRead = 0; + char chNewByte; + + while( nBytesRead < 10000 && VSIFReadL( &chNewByte, 1, 1, fpCSV ) == 1 ) + { + if( chNewByte == 13 ) + { + bUseCRLF = TRUE; + break; + } + nBytesRead ++; + } + VSIRewindL( fpCSV ); + } + +/* -------------------------------------------------------------------- */ +/* Check if the first record seems to be field definitions or */ +/* not. We assume it is field definitions if none of the */ +/* values are strictly numeric. */ +/* -------------------------------------------------------------------- */ + char **papszTokens = NULL; + int nFieldCount=0, iField; + + if( !bNew ) + { + const char *pszLine = NULL; + char szDelimiter[2]; + szDelimiter[0] = chDelimiter; szDelimiter[1] = '\0'; + + pszLine = CPLReadLineL( fpCSV ); + if ( pszLine != NULL ) + { + /* Detect and remove UTF-8 BOM marker if found (#4623) */ + if (pszLine[0] == (char)0xEF && + pszLine[1] == (char)0xBB && + pszLine[2] == (char)0xBF) + { + pszLine += 3; + } + + /* tokenize the strings and preserve quotes, so we can separate string from numeric */ + /* this is only used in the test for bHasFieldNames (bug #4361) */ + papszTokens = CSLTokenizeString2( pszLine, szDelimiter, + (CSLT_HONOURSTRINGS | + CSLT_ALLOWEMPTYTOKENS | + CSLT_PRESERVEQUOTES) ); + nFieldCount = CSLCount( papszTokens ); + bHasFieldNames = TRUE; + + for( iField = 0; iField < nFieldCount && bHasFieldNames; iField++ ) + { + CPLValueType eType = CPLGetValueType(papszTokens[iField]); + if ( (eType == CPL_VALUE_INTEGER || + eType == CPL_VALUE_REAL) ) { + /* we have a numeric field, therefore do not consider the first line as field names */ + bHasFieldNames = FALSE; + } + } + + CPLString osExt = OGRCSVDataSource::GetRealExtension(pszFilename); + + /* Eurostat .tsv files */ + if( EQUAL(osExt, "tsv") && nFieldCount > 1 && + strchr(papszTokens[0], ',') != NULL && strchr(papszTokens[0], '\\') != NULL ) + { + bHasFieldNames = TRUE; + bIsEurostatTSV = TRUE; + } + + /* tokenize without quotes to get the actual values */ + CSLDestroy( papszTokens ); + // papszTokens = OGRCSVReadParseLineL( fpCSV, chDelimiter, FALSE ); + int nFlags = CSLT_HONOURSTRINGS; + if( !bMergeDelimiter ) + nFlags |= CSLT_ALLOWEMPTYTOKENS; + papszTokens = CSLTokenizeString2( pszLine, szDelimiter, nFlags ); + nFieldCount = CSLCount( papszTokens ); + } + } + else + bHasFieldNames = FALSE; + + if( !bNew ) + ResetReading(); + + nCSVFieldCount = nFieldCount; + + panGeomFieldIndex = (int*) CPLCalloc(nFieldCount, sizeof(int)); + for( iField = 0; iField < nFieldCount; iField++ ) + { + panGeomFieldIndex[iField] = -1; + } + +/* -------------------------------------------------------------------- */ +/* Check for geonames.org tables */ +/* -------------------------------------------------------------------- */ + if( !bHasFieldNames && nFieldCount == 19 ) + { + if (CPLGetValueType(papszTokens[0]) == CPL_VALUE_INTEGER && + CPLGetValueType(papszTokens[4]) == CPL_VALUE_REAL && + CPLGetValueType(papszTokens[5]) == CPL_VALUE_REAL && + CPLAtof(papszTokens[4]) >= -90 && CPLAtof(papszTokens[4]) <= 90 && + CPLAtof(papszTokens[5]) >= -180 && CPLAtof(papszTokens[4]) <= 180) + { + bHasFieldNames = TRUE; + CSLDestroy(papszTokens); + papszTokens = NULL; + + static const struct { + const char* pszName; + OGRFieldType eType; + } + asGeonamesFieldDesc[] = + { + { "GEONAMEID", OFTString }, + { "NAME", OFTString }, + { "ASCIINAME", OFTString }, + { "ALTNAMES", OFTString }, + { "LATITUDE", OFTReal }, + { "LONGITUDE", OFTReal }, + { "FEATCLASS", OFTString }, + { "FEATCODE", OFTString }, + { "COUNTRY", OFTString }, + { "CC2", OFTString }, + { "ADMIN1", OFTString }, + { "ADMIN2", OFTString }, + { "ADMIN3", OFTString }, + { "ADMIN4", OFTString }, + { "POPULATION", OFTReal }, + { "ELEVATION", OFTInteger }, + { "GTOPO30", OFTInteger }, + { "TIMEZONE", OFTString }, + { "MODDATE", OFTString } + }; + for(iField = 0; iField < nFieldCount; iField++) + { + OGRFieldDefn oFieldDefn(asGeonamesFieldDesc[iField].pszName, + asGeonamesFieldDesc[iField].eType); + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + + iLatitudeField = 4; + iLongitudeField = 5; + + nFieldCount = 0; + } + } + + +/* -------------------------------------------------------------------- */ +/* Search a csvt file for types */ +/* -------------------------------------------------------------------- */ + char** papszFieldTypes = NULL; + if (!bNew) { + char* dname = CPLStrdup(CPLGetDirname(pszFilename)); + char* fname = CPLStrdup(CPLGetBasename(pszFilename)); + VSILFILE* fpCSVT = VSIFOpenL(CPLFormFilename(dname, fname, ".csvt"), "r"); + CPLFree(dname); + CPLFree(fname); + if (fpCSVT!=NULL) { + VSIRewindL(fpCSVT); + papszFieldTypes = OGRCSVReadParseLineL(fpCSVT, ',', FALSE,FALSE); + VSIFCloseL(fpCSVT); + } + } + +/* -------------------------------------------------------------------- */ +/* Optionaly auto-detect types */ +/* -------------------------------------------------------------------- */ + if( !bNew && papszFieldTypes == NULL && + CSLTestBoolean(CSLFetchNameValueDef(papszOpenOptions, + "AUTODETECT_TYPE", "NO")) ) + { + papszFieldTypes = AutodetectFieldTypes(papszOpenOptions, nFieldCount); + if( papszFieldTypes != NULL ) + { + bKeepSourceColumns = CSLTestBoolean(CSLFetchNameValueDef(papszOpenOptions, + "KEEP_SOURCE_COLUMNS", "NO")); + } + } + +/* -------------------------------------------------------------------- */ +/* Build field definitions. */ +/* -------------------------------------------------------------------- */ + for( iField = 0; !bIsEurostatTSV && iField < nFieldCount; iField++ ) + { + char *pszFieldName = NULL; + char szFieldNameBuffer[100]; + + if( bHasFieldNames ) + { + pszFieldName = papszTokens[iField]; + + // trim white space. + while( *pszFieldName == ' ' ) + pszFieldName++; + + while( pszFieldName[0] != '\0' + && pszFieldName[strlen(pszFieldName)-1] == ' ' ) + pszFieldName[strlen(pszFieldName)-1] = '\0'; + + if (*pszFieldName == '\0') + pszFieldName = NULL; + } + + if (pszFieldName == NULL) + { + /* Re-read single column CSV files that have a trailing comma */ + /* in the header line */ + if( iField == 1 && nFieldCount == 2 && papszTokens[1][0] == '\0' ) + { + nCSVFieldCount = nFieldCount = 1; + break; + } + pszFieldName = szFieldNameBuffer; + sprintf( szFieldNameBuffer, "field_%d", iField+1 ); + } + + OGRFieldDefn oField(pszFieldName, OFTString); + if (papszFieldTypes!=NULL && iField= '0' && pszLeftParenthesis[1] <= '9') + { + int nWidth = 0; + int nPrecision = 0; + + char* pszDot = strchr(pszLeftParenthesis, '.'); + if (pszDot) *pszDot = 0; + *pszLeftParenthesis = 0; + + if (pszLeftParenthesis[-1] == ' ') + pszLeftParenthesis[-1] = 0; + + nWidth = atoi(pszLeftParenthesis+1); + if (pszDot) + nPrecision = atoi(pszDot+1); + + oField.SetWidth(nWidth); + oField.SetPrecision(nPrecision); + } + + if (EQUAL(papszFieldTypes[iField], "Integer")) + oField.SetType(OFTInteger); + else if (EQUAL(papszFieldTypes[iField], "Integer64")) + oField.SetType(OFTInteger64); + else if (EQUAL(papszFieldTypes[iField], "Real")) + oField.SetType(OFTReal); + else if (EQUAL(papszFieldTypes[iField], "String")) + oField.SetType(OFTString); + else if (EQUAL(papszFieldTypes[iField], "Date")) + oField.SetType(OFTDate); + else if (EQUAL(papszFieldTypes[iField], "Time")) + oField.SetType(OFTTime); + else if (EQUAL(papszFieldTypes[iField], "DateTime")) + oField.SetType(OFTDateTime); + else + CPLError(CE_Warning, CPLE_NotSupported, "Unknown type : %s", papszFieldTypes[iField]); + } + } + + if( (EQUAL(oField.GetNameRef(),"WKT") || + EQUALN(oField.GetNameRef(),"_WKT", 4) ) + && oField.GetType() == OFTString ) + { + eGeometryFormat = OGR_CSV_GEOM_AS_WKT; + + const char* pszFieldName = oField.GetNameRef(); + panGeomFieldIndex[iField] = poFeatureDefn->GetGeomFieldCount(); + OGRGeomFieldDefn oGeomFieldDefn( + EQUAL(pszFieldName,"WKT") ? "" : CPLSPrintf("geom_%s", pszFieldName), + wkbUnknown ); + + /* Useful hack for RFC 41 testing */ + const char* pszEPSG = strstr(pszFieldName, "_EPSG_"); + if( pszEPSG != NULL ) + { + int nEPSGCode = atoi(pszEPSG + strlen("_EPSG_")); + OGRSpatialReference* poSRS = new OGRSpatialReference(); + poSRS->importFromEPSG(nEPSGCode); + oGeomFieldDefn.SetSpatialRef(poSRS); + poSRS->Release(); + } + + if( strstr(pszFieldName, "_POINT") ) + oGeomFieldDefn.SetType(wkbPoint); + else if( strstr(pszFieldName, "_LINESTRING") ) + oGeomFieldDefn.SetType(wkbLineString); + else if( strstr(pszFieldName, "_POLYGON") ) + oGeomFieldDefn.SetType(wkbPolygon); + else if( strstr(pszFieldName, "_MULTIPOINT") ) + oGeomFieldDefn.SetType(wkbMultiPoint); + else if( strstr(pszFieldName, "_MULTILINESTRING") ) + oGeomFieldDefn.SetType(wkbMultiLineString); + else if( strstr(pszFieldName, "_MULTIPOLYGON") ) + oGeomFieldDefn.SetType(wkbMultiPolygon); + + poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn); + } + + /*http://www.faa.gov/airports/airport_safety/airportdata_5010/menu/index.cfm specific */ + if ( pszNfdcGeomField != NULL && + EQUALN(oField.GetNameRef(), pszNfdcGeomField, strlen(pszNfdcGeomField)) && + EQUAL(oField.GetNameRef() + strlen(pszNfdcGeomField), "LatitudeS") ) + iNfdcLatitudeS = iField; + else if ( pszNfdcGeomField != NULL && + EQUALN(oField.GetNameRef(), pszNfdcGeomField, strlen(pszNfdcGeomField)) && + EQUAL(oField.GetNameRef() + strlen(pszNfdcGeomField), "LongitudeS") ) + iNfdcLongitudeS = iField; + + /* GNIS specific */ + else if ( pszGeonamesGeomFieldPrefix != NULL && + EQUALN(oField.GetNameRef(), pszGeonamesGeomFieldPrefix, strlen(pszGeonamesGeomFieldPrefix)) && + (EQUAL(oField.GetNameRef() + strlen(pszGeonamesGeomFieldPrefix), "_LAT_DEC") || + EQUAL(oField.GetNameRef() + strlen(pszGeonamesGeomFieldPrefix), "_LATITUDE_DEC") || + EQUAL(oField.GetNameRef() + strlen(pszGeonamesGeomFieldPrefix), "_LATITUDE")) ) + { + oField.SetType(OFTReal); + iLatitudeField = iField; + } + else if ( pszGeonamesGeomFieldPrefix != NULL && + EQUALN(oField.GetNameRef(), pszGeonamesGeomFieldPrefix, strlen(pszGeonamesGeomFieldPrefix)) && + (EQUAL(oField.GetNameRef() + strlen(pszGeonamesGeomFieldPrefix), "_LONG_DEC") || + EQUAL(oField.GetNameRef() + strlen(pszGeonamesGeomFieldPrefix), "_LONGITUDE_DEC") || + EQUAL(oField.GetNameRef() + strlen(pszGeonamesGeomFieldPrefix), "_LONGITUDE")) ) + { + oField.SetType(OFTReal); + iLongitudeField = iField; + } + + poFeatureDefn->AddFieldDefn( &oField ); + + if( bKeepSourceColumns && oField.GetType() != OFTString ) + { + OGRFieldDefn oFieldOriginal( CPLSPrintf("%s_original", oField.GetNameRef()), OFTString); + poFeatureDefn->AddFieldDefn( &oFieldOriginal ); + } + } + + if ( iNfdcLatitudeS != -1 && iNfdcLongitudeS != -1 ) + { + bDontHonourStrings = TRUE; + poFeatureDefn->SetGeomType( wkbPoint ); + } + else if ( iLatitudeField != -1 && iLongitudeField != -1 ) + { + poFeatureDefn->SetGeomType( wkbPoint ); + } + +/* -------------------------------------------------------------------- */ +/* Build field definitions for Eurostat TSV files. */ +/* -------------------------------------------------------------------- */ + + CPLString osSeqDim; + for( iField = 0; bIsEurostatTSV && iField < nFieldCount; iField++ ) + { + if( iField == 0 ) + { + char** papszDims = CSLTokenizeString2( papszTokens[0], ",\\", 0 ); + nEurostatDims = CSLCount(papszDims) - 1; + for(int iSubField = 0; iSubField < nEurostatDims; iSubField++) + { + OGRFieldDefn oField(papszDims[iSubField], OFTString); + poFeatureDefn->AddFieldDefn( &oField ); + } + + osSeqDim = papszDims[nEurostatDims]; + CSLDestroy(papszDims); + } + else + { + if( papszTokens[iField][0] != '\0' + && papszTokens[iField][strlen(papszTokens[iField])-1] == ' ' ) + papszTokens[iField][strlen(papszTokens[iField])-1] = '\0'; + + OGRFieldDefn oField(CPLSPrintf("%s_%s", osSeqDim.c_str(), papszTokens[iField]), OFTReal); + poFeatureDefn->AddFieldDefn( &oField ); + + OGRFieldDefn oField2(CPLSPrintf("%s_%s_flag", osSeqDim.c_str(), papszTokens[iField]), OFTString); + poFeatureDefn->AddFieldDefn( &oField2 ); + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + + CSLDestroy( papszTokens ); + CSLDestroy( papszFieldTypes ); +} + +/************************************************************************/ +/* OGRCSVIsTrue() */ +/************************************************************************/ + +static int OGRCSVIsTrue(const char* pszStr) +{ + return EQUAL(pszStr, "t") || EQUAL(pszStr, "true") || EQUAL(pszStr, "y") || + EQUAL(pszStr, "yes") || EQUAL(pszStr, "on"); +} + +/************************************************************************/ +/* OGRCSVIsFalse() */ +/************************************************************************/ + +static int OGRCSVIsFalse(const char* pszStr) +{ + return EQUAL(pszStr, "f") || EQUAL(pszStr, "false") || EQUAL(pszStr, "n") || + EQUAL(pszStr, "no") || EQUAL(pszStr, "off"); +} + +/************************************************************************/ +/* AutodetectFieldTypes() */ +/************************************************************************/ + +char** OGRCSVLayer::AutodetectFieldTypes(char** papszOpenOptions, int nFieldCount) +{ + int iField; + char** papszFieldTypes = NULL; + + /* Use 1000000 as default maximum distance to be compatible with /vsistdin/ */ + /* caching */ + int nBytes = atoi(CSLFetchNameValueDef(papszOpenOptions, + "AUTODETECT_SIZE_LIMIT", "1000000")); + if( nBytes == 0 ) + { + vsi_l_offset nCurPos = VSIFTellL(fpCSV); + VSIFSeekL(fpCSV, 0, SEEK_END); + vsi_l_offset nFileSize = VSIFTellL(fpCSV); + VSIFSeekL(fpCSV, nCurPos, SEEK_SET); + if( nFileSize < INT_MAX ) + nBytes = (int)nFileSize; + else + nBytes = INT_MAX; + } + else if( nBytes < 0 || (vsi_l_offset)nBytes < VSIFTellL(fpCSV) ) + nBytes = 1000000; + + const char* pszAutodetectWidth = CSLFetchNameValueDef(papszOpenOptions, + "AUTODETECT_WIDTH", "NO"); + int bAutodetectWidth = FALSE; + int bAutodetectWidthForIntOrReal = FALSE; + if( EQUAL(pszAutodetectWidth, "YES") ) + bAutodetectWidth = bAutodetectWidthForIntOrReal = TRUE; + else if( EQUAL(pszAutodetectWidth, "STRING_ONLY") ) + bAutodetectWidth = TRUE; + + int bQuotedFieldAsString = CSLTestBoolean(CSLFetchNameValueDef(papszOpenOptions, + "QUOTED_FIELDS_AS_STRING", "NO")); + + char* pszData = (char*) VSIMalloc( nBytes ); + if( pszData != NULL && (vsi_l_offset)nBytes > VSIFTellL(fpCSV) ) + { + int nRequested = nBytes - 1 - (int)VSIFTellL(fpCSV); + int nRead = VSIFReadL(pszData, 1, nRequested, fpCSV); + pszData[nRead] = 0; + + CPLString osTmpMemFile(CPLSPrintf("/vsimem/tmp%p", this)); + VSILFILE* fpMem = VSIFileFromMemBuffer( osTmpMemFile, + (GByte*)pszData, + nRead, + FALSE ); + + std::vector aeFieldType; + std::vector abFieldBoolean; + std::vector abFieldSet; + std::vector anFieldWidth; + std::vector anFieldPrecision; + aeFieldType.resize(nFieldCount); + abFieldBoolean.resize(nFieldCount); + abFieldSet.resize(nFieldCount); + anFieldWidth.resize(nFieldCount); + anFieldPrecision.resize(nFieldCount); + int nStringFieldCount = 0; + + while( !VSIFEofL(fpMem) ) + { + char** papszTokens = OGRCSVReadParseLineL( fpMem, chDelimiter, FALSE, + bQuotedFieldAsString, + bMergeDelimiter ); + /* Can happen if we just reach EOF while trying to read new bytes */ + if( papszTokens == NULL ) + break; + + /* Ignore last line if it is truncated */ + if( VSIFEofL(fpMem) && nRead == nRequested && + pszData[nRead-1] != 13 && pszData[nRead-1] != 10 ) + { + CSLDestroy(papszTokens); + break; + } + + for( iField = 0; papszTokens[iField] != NULL && + iField < nFieldCount; iField++ ) + { + int nFieldWidth = 0, nFieldPrecision = 0; + + if( papszTokens[iField][0] == 0 ) + continue; + if (chDelimiter == ';') + { + char* chComma = strchr(papszTokens[iField], ','); + if (chComma) + *chComma = '.'; + } + CPLValueType eType = CPLGetValueType(papszTokens[iField]); + + if( bAutodetectWidth ) + { + nFieldWidth = strlen(papszTokens[iField]); + if( papszTokens[iField][0] == '"' && + papszTokens[iField][nFieldWidth-1] == '"' ) + { + nFieldWidth -= 2; + } + if( eType == CPL_VALUE_REAL && bAutodetectWidthForIntOrReal ) + { + const char* pszDot = strchr(papszTokens[iField], '.'); + if( pszDot != NULL ) + nFieldPrecision = strlen(pszDot + 1); + } + } + + OGRFieldType eOGRFieldType; + int bIsBoolean = FALSE; + if( eType == CPL_VALUE_INTEGER ) + { + GIntBig nVal = CPLAtoGIntBig(papszTokens[iField]); + if( (GIntBig)(int)nVal != nVal ) + eOGRFieldType = OFTInteger64; + else + eOGRFieldType = OFTInteger; + } + else if( eType == CPL_VALUE_REAL ) + { + eOGRFieldType = OFTReal; + } + else if( abFieldSet[iField] && aeFieldType[iField] == OFTString ) + { + eOGRFieldType = OFTString; + if( abFieldBoolean[iField] ) + { + abFieldBoolean[iField] = OGRCSVIsTrue(papszTokens[iField]) || + OGRCSVIsFalse(papszTokens[iField]); + } + } + else + { + OGRField sWrkField; + CPLPushErrorHandler(CPLQuietErrorHandler); + int bSuccess = OGRParseDate( papszTokens[iField], &sWrkField, 0 ); + CPLPopErrorHandler(); + CPLErrorReset(); + if( bSuccess ) + { + int bHasDate = strchr( papszTokens[iField], '/' ) != NULL || + strchr( papszTokens[iField], '-' ) != NULL; + int bHasTime = strchr( papszTokens[iField], ':' ) != NULL; + if( bHasDate && bHasTime ) + eOGRFieldType = OFTDateTime; + else if( bHasDate ) + eOGRFieldType = OFTDate; + else + eOGRFieldType = OFTTime; + } + else + { + eOGRFieldType = OFTString; + bIsBoolean = OGRCSVIsTrue(papszTokens[iField]) || + OGRCSVIsFalse(papszTokens[iField]); + } + } + + if( !abFieldSet[iField] ) + { + aeFieldType[iField] = eOGRFieldType; + abFieldSet[iField] = TRUE; + abFieldBoolean[iField] = bIsBoolean; + if( eOGRFieldType == OFTString && !bIsBoolean ) + nStringFieldCount ++; + } + else if( aeFieldType[iField] != eOGRFieldType ) + { + /* Promotion rules */ + if( aeFieldType[iField] == OFTInteger ) + { + if( eOGRFieldType == OFTInteger64 || + eOGRFieldType == OFTReal ) + aeFieldType[iField] = eOGRFieldType; + else + { + aeFieldType[iField] = OFTString; + nStringFieldCount ++; + } + } + else if( aeFieldType[iField] == OFTInteger64 ) + { + if( eOGRFieldType == OFTReal ) + aeFieldType[iField] = eOGRFieldType; + else if( eOGRFieldType != OFTInteger ) + { + aeFieldType[iField] = OFTString; + nStringFieldCount ++; + } + } + else if ( aeFieldType[iField] == OFTReal ) + { + if( eOGRFieldType != OFTInteger && + eOGRFieldType != OFTReal ) + { + aeFieldType[iField] = OFTString; + nStringFieldCount ++; + } + } + else if( aeFieldType[iField] == OFTDate ) + { + if( eOGRFieldType == OFTDateTime ) + aeFieldType[iField] = OFTDateTime; + else + { + aeFieldType[iField] = OFTString; + nStringFieldCount ++; + } + } + else if( aeFieldType[iField] == OFTDateTime ) + { + if( eOGRFieldType != OFTDate && + eOGRFieldType != OFTDateTime ) + { + aeFieldType[iField] = OFTString; + nStringFieldCount ++; + } + } + else if( aeFieldType[iField] == OFTTime ) + { + aeFieldType[iField] = OFTString; + nStringFieldCount ++; + } + } + + if( nFieldWidth > anFieldWidth[iField] ) + anFieldWidth[iField] = nFieldWidth; + if( nFieldPrecision > anFieldPrecision[iField] ) + anFieldPrecision[iField] = nFieldPrecision; + } + + CSLDestroy(papszTokens); + + /* If all fields are String and we don't need to compute width, */ + /* just stop auto-detection now */ + if( nStringFieldCount == nFieldCount && bAutodetectWidth ) + break; + } + + papszFieldTypes = (char**) CPLCalloc( nFieldCount + 1, sizeof(char*) ); + for(iField = 0; iField < nFieldCount; iField ++ ) + { + CPLString osFieldType; + if( !abFieldSet[iField] ) + osFieldType = "String"; + else if( aeFieldType[iField] == OFTInteger ) + osFieldType = "Integer"; + else if( aeFieldType[iField] == OFTInteger64 ) + osFieldType = "Integer64"; + else if( aeFieldType[iField] == OFTReal ) + osFieldType = "Real"; + else if( aeFieldType[iField] == OFTDateTime ) + osFieldType = "DateTime"; + else if( aeFieldType[iField] == OFTDate ) + osFieldType = "Date"; + else if( aeFieldType[iField] == OFTTime ) + osFieldType = "Time"; + else if( abFieldBoolean[iField] ) + osFieldType = "Integer(Boolean)"; + else + osFieldType = "String"; + + if( !abFieldBoolean[iField] ) + { + if( anFieldWidth[iField] > 0 && + (aeFieldType[iField] == OFTString || + (bAutodetectWidthForIntOrReal && + (aeFieldType[iField] == OFTInteger || + aeFieldType[iField] == OFTInteger64))) ) + { + osFieldType += CPLSPrintf(" (%d)", anFieldWidth[iField]); + } + else if ( anFieldWidth[iField] > 0 && + bAutodetectWidthForIntOrReal && + aeFieldType[iField] == OFTReal ) + { + osFieldType += CPLSPrintf(" (%d.%d)", anFieldWidth[iField], + anFieldPrecision[iField]); + } + } + + papszFieldTypes[iField] = CPLStrdup(osFieldType); + } + + VSIFCloseL(fpMem); + VSIUnlink(osTmpMemFile); + + } + VSIFree(pszData); + + ResetReading(); + + return papszFieldTypes; +} + + +/************************************************************************/ +/* ~OGRCSVLayer() */ +/************************************************************************/ + +OGRCSVLayer::~OGRCSVLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "CSV", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + // Make sure the header file is written even if no features are written. + if (bNew && bInWriteMode) + WriteHeader(); + + CPLFree( panGeomFieldIndex ); + + poFeatureDefn->Release(); + CPLFree(pszFilename); + + if (fpCSV) + VSIFCloseL( fpCSV ); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRCSVLayer::ResetReading() + +{ + if (fpCSV) + VSIRewindL( fpCSV ); + + if( bHasFieldNames ) + CSLDestroy( OGRCSVReadParseLineL( fpCSV, chDelimiter, bDontHonourStrings ) ); + + bNeedRewindBeforeRead = FALSE; + + nNextFID = 1; +} + +/************************************************************************/ +/* GetNextLineTokens() */ +/************************************************************************/ + +char** OGRCSVLayer::GetNextLineTokens() +{ +/* -------------------------------------------------------------------- */ +/* Read the CSV record. */ +/* -------------------------------------------------------------------- */ + char **papszTokens; + + while(TRUE) + { + papszTokens = OGRCSVReadParseLineL( fpCSV, chDelimiter, bDontHonourStrings, + FALSE, bMergeDelimiter ); + if( papszTokens == NULL ) + return NULL; + + if( papszTokens[0] != NULL ) + break; + + CSLDestroy(papszTokens); + } + return papszTokens; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature* OGRCSVLayer::GetFeature(GIntBig nFID) +{ + if( nFID < 1 || fpCSV == NULL ) + return NULL; + if( nFID < nNextFID || bNeedRewindBeforeRead ) + ResetReading(); + while( nNextFID < nFID ) + { + char **papszTokens = GetNextLineTokens(); + if( papszTokens == NULL ) + return NULL; + CSLDestroy(papszTokens); + nNextFID ++; + } + return GetNextUnfilteredFeature(); +} + +/************************************************************************/ +/* GetNextUnfilteredFeature() */ +/************************************************************************/ + +OGRFeature * OGRCSVLayer::GetNextUnfilteredFeature() + +{ + if (fpCSV == NULL) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Read the CSV record. */ +/* -------------------------------------------------------------------- */ + char **papszTokens = GetNextLineTokens(); + if( papszTokens == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the OGR feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poFeature = new OGRFeature( poFeatureDefn ); + +/* -------------------------------------------------------------------- */ +/* Set attributes for any indicated attribute records. */ +/* -------------------------------------------------------------------- */ + int iAttr; + int iOGRField = 0; + int nAttrCount = MIN(CSLCount(papszTokens), nCSVFieldCount ); + CPLValueType eType; + + for( iAttr = 0; !bIsEurostatTSV && iAttr < nAttrCount; iAttr++, iOGRField++) + { + int iGeom = panGeomFieldIndex[iAttr]; + if( iGeom >= 0 && papszTokens[iAttr][0] != '\0'&& + !(poFeatureDefn->GetGeomFieldDefn(iGeom)->IsIgnored()) ) + { + char *pszWKT = papszTokens[iAttr]; + OGRGeometry *poGeom = NULL; + + if( OGRGeometryFactory::createFromWkt( &pszWKT, NULL, &poGeom ) + == OGRERR_NONE ) + { + poGeom->assignSpatialReference( + poFeatureDefn->GetGeomFieldDefn(iGeom)->GetSpatialRef()); + poFeature->SetGeomFieldDirectly( iGeom, poGeom ); + } + } + + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(iOGRField); + OGRFieldType eFieldType = poFieldDefn->GetType(); + OGRFieldSubType eFieldSubType = poFieldDefn->GetSubType(); + if( eFieldType == OFTInteger && eFieldSubType == OFSTBoolean ) + { + if (papszTokens[iAttr][0] != '\0' && !poFieldDefn->IsIgnored() ) + { + if( OGRCSVIsTrue(papszTokens[iAttr]) || + strcmp(papszTokens[iAttr], "1") == 0 ) + { + poFeature->SetField( iOGRField, 1 ); + } + else if( OGRCSVIsFalse(papszTokens[iAttr]) || + strcmp(papszTokens[iAttr], "0") == 0 ) + { + poFeature->SetField( iOGRField, 0 ); + } + else if( !bWarningBadTypeOrWidth ) + { + bWarningBadTypeOrWidth = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid value type found in record %d for field %s. " + "This warning will no longer be emitted", + nNextFID, poFieldDefn->GetNameRef()); + } + } + } + else if( eFieldType == OFTReal || eFieldType == OFTInteger || + eFieldType == OFTInteger64 ) + { + if (papszTokens[iAttr][0] != '\0' && !poFieldDefn->IsIgnored() ) + { + if (chDelimiter == ';' && eFieldType == OFTReal) + { + char* chComma = strchr(papszTokens[iAttr], ','); + if (chComma) + *chComma = '.'; + } + eType = CPLGetValueType(papszTokens[iAttr]); + if ( eType == CPL_VALUE_INTEGER || eType == CPL_VALUE_REAL ) + { + poFeature->SetField( iOGRField, papszTokens[iAttr] ); + if( !bWarningBadTypeOrWidth && + (eFieldType == OFTInteger || eFieldType == OFTInteger64) && eType == CPL_VALUE_REAL ) + { + bWarningBadTypeOrWidth = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid value type found in record %d for field %s. " + "This warning will no longer be emitted", + nNextFID, poFieldDefn->GetNameRef()); + } + else if( !bWarningBadTypeOrWidth && poFieldDefn->GetWidth() > 0 && + (int)strlen(papszTokens[iAttr]) > poFieldDefn->GetWidth() ) + { + bWarningBadTypeOrWidth = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "Value with a width greater than field width found in record %d for field %s. " + "This warning will no longer be emitted", + nNextFID, poFieldDefn->GetNameRef()); + } + else if( !bWarningBadTypeOrWidth && eType == CPL_VALUE_REAL && + poFieldDefn->GetWidth() > 0) + { + const char* pszDot = strchr(papszTokens[iAttr], '.'); + int nPrecision = 0; + if( pszDot != NULL ) + nPrecision = strlen(pszDot + 1); + if( nPrecision > poFieldDefn->GetPrecision() ) + { + bWarningBadTypeOrWidth = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "Value with a precision greater than field precision found in record %d for field %s. " + "This warning will no longer be emitted", + nNextFID, poFieldDefn->GetNameRef()); + } + } + } + else + { + if( !bWarningBadTypeOrWidth ) + { + bWarningBadTypeOrWidth = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid value type found in record %d for field %s. " + "This warning will no longer be emitted", + nNextFID, poFieldDefn->GetNameRef()); + } + } + } + } + else if (eFieldType != OFTString) + { + if (papszTokens[iAttr][0] != '\0' && !poFieldDefn->IsIgnored()) + { + poFeature->SetField( iOGRField, papszTokens[iAttr] ); + if( !bWarningBadTypeOrWidth && !poFeature->IsFieldSet(iOGRField) ) + { + bWarningBadTypeOrWidth = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid value type found in record %d for field %s. " + "This warning will no longer be emitted", + nNextFID, poFieldDefn->GetNameRef()); + } + } + } + else + { + if( !poFieldDefn->IsIgnored() ) + { + poFeature->SetField( iOGRField, papszTokens[iAttr] ); + if( !bWarningBadTypeOrWidth && poFieldDefn->GetWidth() > 0 && + (int)strlen(papszTokens[iAttr]) > poFieldDefn->GetWidth() ) + { + bWarningBadTypeOrWidth = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "Value with a width greater than field width found in record %d for field %s. " + "This warning will no longer be emitted", + nNextFID, poFieldDefn->GetNameRef()); + } + } + } + + if( bKeepSourceColumns && eFieldType != OFTString ) + { + iOGRField ++; + if( papszTokens[iAttr][0] != '\0' && + !poFeatureDefn->GetFieldDefn(iOGRField)->IsIgnored() ) + { + poFeature->SetField( iOGRField, papszTokens[iAttr] ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Eurostat TSV files. */ +/* -------------------------------------------------------------------- */ + + for( iAttr = 0; bIsEurostatTSV && iAttr < nAttrCount; iAttr++) + { + if( iAttr == 0 ) + { + char** papszDims = CSLTokenizeString2( papszTokens[0], ",", 0 ); + if( CSLCount(papszDims) != nEurostatDims ) + { + CSLDestroy(papszDims); + break; + } + for( int iSubAttr = 0; iSubAttr < nEurostatDims; iSubAttr ++ ) + { + if( !poFeatureDefn->GetFieldDefn(iSubAttr)->IsIgnored() ) + poFeature->SetField( iSubAttr, papszDims[iSubAttr] ); + } + CSLDestroy(papszDims); + } + else + { + char** papszVals = CSLTokenizeString2( papszTokens[iAttr], " ", 0 ); + eType = CPLGetValueType(papszVals[0]); + if ( (papszVals[0][0] != '\0') && + ( eType == CPL_VALUE_INTEGER || + eType == CPL_VALUE_REAL ) ) + { + if( !poFeatureDefn->GetFieldDefn(nEurostatDims + 2 * (iAttr - 1))->IsIgnored() ) + poFeature->SetField( nEurostatDims + 2 * (iAttr - 1), papszVals[0] ); + } + if( CSLCount(papszVals) == 2 ) + { + if( !poFeatureDefn->GetFieldDefn(nEurostatDims + 2 * (iAttr - 1) + 1)->IsIgnored() ) + poFeature->SetField( nEurostatDims + 2 * (iAttr - 1) + 1, papszVals[1] ); + } + CSLDestroy(papszVals); + } + } + +/* -------------------------------------------------------------------- */ +/*http://www.faa.gov/airports/airport_safety/airportdata_5010/menu/index.cfm specific */ +/* -------------------------------------------------------------------- */ + + if ( iNfdcLatitudeS != -1 && + iNfdcLongitudeS != -1 && + nAttrCount > iNfdcLatitudeS && + nAttrCount > iNfdcLongitudeS && + papszTokens[iNfdcLongitudeS][0] != 0 && + papszTokens[iNfdcLatitudeS][0] != 0) + { + double dfLon = CPLAtof(papszTokens[iNfdcLongitudeS]) / 3600; + if (strchr(papszTokens[iNfdcLongitudeS], 'W')) + dfLon *= -1; + double dfLat = CPLAtof(papszTokens[iNfdcLatitudeS]) / 3600; + if (strchr(papszTokens[iNfdcLatitudeS], 'S')) + dfLat *= -1; + if( !(poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored()) ) + poFeature->SetGeometryDirectly( new OGRPoint(dfLon, dfLat) ); + } + +/* -------------------------------------------------------------------- */ +/* GNIS specific */ +/* -------------------------------------------------------------------- */ + else if ( iLatitudeField != -1 && + iLongitudeField != -1 && + nAttrCount > iLatitudeField && + nAttrCount > iLongitudeField && + papszTokens[iLongitudeField][0] != 0 && + papszTokens[iLatitudeField][0] != 0) + { + /* Some records have dummy 0,0 value */ + if (papszTokens[iLongitudeField][0] != '0' || + papszTokens[iLongitudeField][1] != '\0' || + papszTokens[iLatitudeField][0] != '0' || + papszTokens[iLatitudeField][1] != '\0') + { + double dfLon = CPLAtof(papszTokens[iLongitudeField]); + double dfLat = CPLAtof(papszTokens[iLatitudeField]); + if( !(poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored()) ) + poFeature->SetGeometryDirectly( new OGRPoint(dfLon, dfLat) ); + } + } + + CSLDestroy( papszTokens ); + +/* -------------------------------------------------------------------- */ +/* Translate the record id. */ +/* -------------------------------------------------------------------- */ + poFeature->SetFID( nNextFID++ ); + + m_nFeaturesRead++; + + return poFeature; +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRCSVLayer::GetNextFeature() + +{ + OGRFeature *poFeature = NULL; + + if( bNeedRewindBeforeRead ) + ResetReading(); + +/* -------------------------------------------------------------------- */ +/* Read features till we find one that satisfies our current */ +/* spatial criteria. */ +/* -------------------------------------------------------------------- */ + while( TRUE ) + { + poFeature = GetNextUnfilteredFeature(); + if( poFeature == NULL ) + break; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeomFieldRef(m_iGeomFieldFilter) ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + break; + + delete poFeature; + } + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRCSVLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCSequentialWrite) ) + return bInWriteMode && !bKeepSourceColumns; + else if( EQUAL(pszCap,OLCCreateField) ) + return bNew && !bHasFieldNames; + else if( EQUAL(pszCap,OLCCreateGeomField) ) + return bNew && !bHasFieldNames && eGeometryFormat == OGR_CSV_GEOM_AS_WKT; + else if( EQUAL(pszCap,OLCIgnoreFields) ) + return TRUE; + else if( EQUAL(pszCap,OLCCurveGeometries) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRCSVLayer::CreateField( OGRFieldDefn *poNewField, int bApproxOK ) + +{ +/* -------------------------------------------------------------------- */ +/* If we have already written our field names, then we are not */ +/* allowed to add new fields. */ +/* -------------------------------------------------------------------- */ + if( !TestCapability(OLCCreateField) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create new fields after first feature written."); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Does this duplicate an existing field? */ +/* -------------------------------------------------------------------- */ + if( poFeatureDefn->GetFieldIndex( poNewField->GetNameRef() ) != -1 ) + { + if( poFeatureDefn->GetGeomFieldIndex( poNewField->GetNameRef() ) != -1 ) + return OGRERR_NONE; + if( poFeatureDefn->GetGeomFieldIndex( CPLSPrintf("geom_%s", poNewField->GetNameRef()) ) != -1 ) + return OGRERR_NONE; + + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create field %s, but a field with this name already exists.", + poNewField->GetNameRef() ); + + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Is this a legal field type for CSV? For now we only allow */ +/* simple integer, real and string fields. */ +/* -------------------------------------------------------------------- */ + switch( poNewField->GetType() ) + { + case OFTInteger: + case OFTInteger64: + case OFTReal: + case OFTString: + // these types are OK. + break; + + default: + if( bApproxOK ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Attempt to create field of type %s, but this is not supported\n" + "for .csv files. Just treating as a plain string.", + poNewField->GetFieldTypeName( poNewField->GetType() ) ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create field of type %s, but this is not supported\n" + "for .csv files.", + poNewField->GetFieldTypeName( poNewField->GetType() ) ); + return OGRERR_FAILURE; + } + } + +/* -------------------------------------------------------------------- */ +/* Seems ok, add to field list. */ +/* -------------------------------------------------------------------- */ + poFeatureDefn->AddFieldDefn( poNewField ); + nCSVFieldCount ++; + + panGeomFieldIndex = (int*) CPLRealloc(panGeomFieldIndex, + sizeof(int) * poFeatureDefn->GetFieldCount()); + panGeomFieldIndex[poFeatureDefn->GetFieldCount() - 1] = -1; + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* CreateGeomField() */ +/************************************************************************/ + +OGRErr OGRCSVLayer::CreateGeomField( OGRGeomFieldDefn *poGeomField, + CPL_UNUSED int bApproxOK ) +{ + if( !TestCapability(OLCCreateGeomField) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create new fields after first feature written."); + return OGRERR_FAILURE; + } + + poFeatureDefn->AddGeomFieldDefn( poGeomField ); + + const char* pszName = poGeomField->GetNameRef(); + if( EQUALN(pszName, "geom_", strlen("geom_")) ) + pszName += strlen("geom_"); + if( !EQUAL(pszName, "WKT") && !EQUALN(pszName, "_WKT", 4) ) + pszName = CPLSPrintf("_WKT%s", pszName); + + OGRFieldDefn oRegularFieldDefn( pszName, OFTString ); + poFeatureDefn->AddFieldDefn( &oRegularFieldDefn ); + nCSVFieldCount ++; + + panGeomFieldIndex = (int*) CPLRealloc(panGeomFieldIndex, + sizeof(int) * poFeatureDefn->GetFieldCount()); + panGeomFieldIndex[poFeatureDefn->GetFieldCount() - 1] = + poFeatureDefn->GetGeomFieldCount() - 1; + + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* WriteHeader() */ +/* */ +/* Write the header, and possibly the .csvt file if they */ +/* haven't already been written. */ +/************************************************************************/ + +OGRErr OGRCSVLayer::WriteHeader() +{ + if( !bNew ) + return OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* Write field names if we haven't written them yet. */ +/* Write .csvt file if needed */ +/* -------------------------------------------------------------------- */ + bNew = FALSE; + bHasFieldNames = TRUE; + + for(int iFile=0;iFile<((bCreateCSVT) ? 2 : 1);iFile++) + { + VSILFILE* fpCSVT = NULL; + if (bCreateCSVT && iFile == 0) + { + char* pszDirName = CPLStrdup(CPLGetDirname(pszFilename)); + char* pszBaseName = CPLStrdup(CPLGetBasename(pszFilename)); + fpCSVT = VSIFOpenL(CPLFormFilename(pszDirName, pszBaseName, ".csvt"), "wb"); + CPLFree(pszDirName); + CPLFree(pszBaseName); + } + else + { + if( strncmp(pszFilename, "/vsistdout/", 11) == 0 || + strncmp(pszFilename, "/vsizip/", 8) == 0 ) + fpCSV = VSIFOpenL( pszFilename, "wb" ); + else + fpCSV = VSIFOpenL( pszFilename, "w+b" ); + + if( fpCSV == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create %s:\n%s", + pszFilename, VSIStrerror( errno ) ); + return OGRERR_FAILURE; + } + } + + if (bWriteBOM && fpCSV) + { + VSIFWriteL("\xEF\xBB\xBF", 1, 3, fpCSV); + } + + if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ) + { + if (fpCSV) VSIFPrintfL( fpCSV, "X%cY%cZ", chDelimiter, chDelimiter); + if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", "Real,Real,Real"); + if (poFeatureDefn->GetFieldCount() > 0) + { + if (fpCSV) VSIFPrintfL( fpCSV, "%c", chDelimiter ); + if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", ","); + } + } + else if (eGeometryFormat == OGR_CSV_GEOM_AS_XY) + { + if (fpCSV) VSIFPrintfL( fpCSV, "X%cY", chDelimiter); + if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", "Real,Real"); + if (poFeatureDefn->GetFieldCount() > 0) + { + if (fpCSV) VSIFPrintfL( fpCSV, "%c", chDelimiter ); + if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", ","); + } + } + else if (eGeometryFormat == OGR_CSV_GEOM_AS_YX) + { + if (fpCSV) VSIFPrintfL( fpCSV, "Y%cX", chDelimiter); + if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", "Real,Real"); + if (poFeatureDefn->GetFieldCount() > 0) + { + if (fpCSV) VSIFPrintfL( fpCSV, "%c", chDelimiter ); + if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", ","); + } + } + + if( bHiddenWKTColumn ) + { + if (fpCSV) VSIFPrintfL( fpCSV, "%s", "WKT" ); + if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", "String"); + } + + for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + char *pszEscaped; + + if( iField > 0 || bHiddenWKTColumn ) + { + if (fpCSV) VSIFPrintfL( fpCSV, "%c", chDelimiter ); + if (fpCSVT) VSIFPrintfL( fpCSVT, "%s", ","); + } + + pszEscaped = + CPLEscapeString( poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), + -1, CPLES_CSV ); + + if (fpCSV) + { + int bAddDoubleQuote = FALSE; + if( chDelimiter == ' ' && pszEscaped[0] != '"' && strchr(pszEscaped, ' ') != NULL ) + bAddDoubleQuote = TRUE; + if( bAddDoubleQuote ) + VSIFWriteL( "\"", 1, 1, fpCSV ); + VSIFPrintfL( fpCSV, "%s", pszEscaped ); + if( bAddDoubleQuote ) + VSIFWriteL( "\"", 1, 1, fpCSV ); + } + CPLFree( pszEscaped ); + + if (fpCSVT) + { + int nWidth = poFeatureDefn->GetFieldDefn(iField)->GetWidth(); + int nPrecision = poFeatureDefn->GetFieldDefn(iField)->GetPrecision(); + + switch( poFeatureDefn->GetFieldDefn(iField)->GetType() ) + { + case OFTInteger: + { + if( poFeatureDefn->GetFieldDefn(iField)->GetSubType() == OFSTBoolean ) + { + nWidth = 0; + VSIFPrintfL( fpCSVT, "%s", "Integer(Boolean)"); + } + else if( poFeatureDefn->GetFieldDefn(iField)->GetSubType() == OFSTInt16 ) + { + nWidth = 0; + VSIFPrintfL( fpCSVT, "%s", "Integer(Int16)"); + } + else + VSIFPrintfL( fpCSVT, "%s", "Integer"); + break; + } + case OFTInteger64: + VSIFPrintfL( fpCSVT, "%s", "Integer64"); + break; + case OFTReal: + { + if( poFeatureDefn->GetFieldDefn(iField)->GetSubType() == OFSTFloat32 ) + { + nWidth = 0; + VSIFPrintfL( fpCSVT, "%s", "Real(Float32)"); + } + else + VSIFPrintfL( fpCSVT, "%s", "Real"); + break; + } + case OFTDate: VSIFPrintfL( fpCSVT, "%s", "Date"); break; + case OFTTime: VSIFPrintfL( fpCSVT, "%s", "Time"); break; + case OFTDateTime: VSIFPrintfL( fpCSVT, "%s", "DateTime"); break; + default: VSIFPrintfL( fpCSVT, "%s", "String"); break; + } + + if (nWidth != 0) + { + if (nPrecision != 0) + VSIFPrintfL( fpCSVT, "(%d.%d)", nWidth, nPrecision); + else + VSIFPrintfL( fpCSVT, "(%d)", nWidth); + } + } + } + + /* The CSV driver will not recognize single column tables, so add */ + /* a fake second blank field */ + if( poFeatureDefn->GetFieldCount() == 1 || + (poFeatureDefn->GetFieldCount() == 0 && bHiddenWKTColumn) ) + { + if (fpCSV) VSIFPrintfL( fpCSV, "%c", chDelimiter ); + } + + if( bUseCRLF ) + { + if (fpCSV) VSIFPutcL( 13, fpCSV ); + if (fpCSVT) VSIFPutcL( 13, fpCSVT ); + } + if (fpCSV) VSIFPutcL( '\n', fpCSV ); + if (fpCSVT) VSIFPutcL( '\n', fpCSVT ); + if (fpCSVT) VSIFCloseL(fpCSVT); + } + + if (fpCSV == NULL) + return OGRERR_FAILURE; + else + return OGRERR_NONE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRCSVLayer::ICreateFeature( OGRFeature *poNewFeature ) + +{ + int iField; + + if( !bInWriteMode ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The CreateFeature() operation is not permitted on a read-only CSV." ); + return OGRERR_FAILURE; + } + + /* If we need rewind, it means that we have just written a feature before */ + /* so there's no point seeking to the end of the file, as we're already */ + /* at the end */ + int bNeedSeekEnd = !bNeedRewindBeforeRead; + + bNeedRewindBeforeRead = TRUE; + +/* -------------------------------------------------------------------- */ +/* Write field names if we haven't written them yet. */ +/* Write .csvt file if needed */ +/* -------------------------------------------------------------------- */ + if( bNew ) + { + OGRErr eErr = WriteHeader(); + if (eErr != OGRERR_NONE) + return eErr; + bNeedSeekEnd = FALSE; + } + + if (fpCSV == NULL) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Make sure we are at the end of the file. */ +/* -------------------------------------------------------------------- */ + if (bNeedSeekEnd) + { + if (bFirstFeatureAppendedDuringSession) + { + /* Add a newline character to the end of the file if necessary */ + bFirstFeatureAppendedDuringSession = FALSE; + VSIFSeekL( fpCSV, 0, SEEK_END ); + VSIFSeekL( fpCSV, VSIFTellL(fpCSV) - 1, SEEK_SET); + char chLast; + VSIFReadL( &chLast, 1, 1, fpCSV ); + VSIFSeekL( fpCSV, 0, SEEK_END ); + if (chLast != '\n') + { + if( bUseCRLF ) + VSIFPutcL( 13, fpCSV ); + VSIFPutcL( '\n', fpCSV ); + } + } + else + { + VSIFSeekL( fpCSV, 0, SEEK_END ); + } + } + +/* -------------------------------------------------------------------- */ +/* Write out the geometry */ +/* -------------------------------------------------------------------- */ + if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ || + eGeometryFormat == OGR_CSV_GEOM_AS_XY || + eGeometryFormat == OGR_CSV_GEOM_AS_YX) + { + OGRGeometry *poGeom = poNewFeature->GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + { + OGRPoint* poPoint = (OGRPoint*) poGeom; + char szBuffer[75]; + if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ ) + OGRMakeWktCoordinate(szBuffer, poPoint->getX(), poPoint->getY(), poPoint->getZ(), 3); + else if (eGeometryFormat == OGR_CSV_GEOM_AS_XY ) + OGRMakeWktCoordinate(szBuffer, poPoint->getX(), poPoint->getY(), 0, 2); + else + OGRMakeWktCoordinate(szBuffer, poPoint->getY(), poPoint->getX(), 0, 2); + char* pc = szBuffer; + while(*pc != '\0') + { + if (*pc == ' ') + *pc = chDelimiter; + pc ++; + } + VSIFPrintfL( fpCSV, "%s", szBuffer ); + } + else + { + VSIFPrintfL( fpCSV, "%c", chDelimiter ); + if (eGeometryFormat == OGR_CSV_GEOM_AS_XYZ) + VSIFPrintfL( fpCSV, "%c", chDelimiter ); + } + if (poFeatureDefn->GetFieldCount() > 0) + VSIFPrintfL( fpCSV, "%c", chDelimiter ); + } + +/* -------------------------------------------------------------------- */ +/* Special case to deal with hidden "WKT" geometry column */ +/* -------------------------------------------------------------------- */ + int bNonEmptyLine = FALSE; + + if( bHiddenWKTColumn ) + { + char *pszWKT = NULL; + OGRGeometry *poGeom = poNewFeature->GetGeomFieldRef(0); + if (poGeom && poGeom->exportToWkt(&pszWKT) == OGRERR_NONE) + { + bNonEmptyLine = TRUE; + VSIFWriteL( "\"", 1, 1, fpCSV ); + VSIFWriteL( pszWKT, 1, strlen(pszWKT), fpCSV ); + VSIFWriteL( "\"", 1, 1, fpCSV ); + } + CPLFree(pszWKT); + } + +/* -------------------------------------------------------------------- */ +/* Write out all the field values. */ +/* -------------------------------------------------------------------- */ + + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + char *pszEscaped; + + if( iField > 0 || bHiddenWKTColumn ) + VSIFPrintfL( fpCSV, "%c", chDelimiter ); + + if (eGeometryFormat == OGR_CSV_GEOM_AS_WKT && + panGeomFieldIndex[iField] >= 0 ) + { + int iGeom = panGeomFieldIndex[iField]; + OGRGeometry *poGeom = poNewFeature->GetGeomFieldRef(iGeom); + if (poGeom && poGeom->exportToWkt(&pszEscaped) == OGRERR_NONE) + { + int nLenWKT = (int)strlen(pszEscaped); + char* pszNew = (char*) CPLMalloc(1 + nLenWKT + 1 + 1); + pszNew[0] = '"'; + memcpy(pszNew + 1, pszEscaped, nLenWKT); + pszNew[1 + nLenWKT] = '"'; + pszNew[1 + nLenWKT + 1] = '\0'; + CPLFree(pszEscaped); + pszEscaped = pszNew; + } + else + pszEscaped = CPLStrdup(""); + } + else if (poFeatureDefn->GetFieldDefn(iField)->GetType() == OFTReal) + { + pszEscaped = CPLStrdup(poNewFeature->GetFieldAsString(iField)); + } + else + { + pszEscaped = + CPLEscapeString( poNewFeature->GetFieldAsString(iField), + -1, CPLES_CSV ); + } + + int nLen = (int)strlen(pszEscaped); + bNonEmptyLine |= (nLen != 0); + int bAddDoubleQuote = FALSE; + if( chDelimiter == ' ' && pszEscaped[0] != '"' && strchr(pszEscaped, ' ') != NULL ) + bAddDoubleQuote = TRUE; + if( bAddDoubleQuote ) + VSIFWriteL( "\"", 1, 1, fpCSV ); + VSIFWriteL( pszEscaped, 1, nLen, fpCSV ); + if( bAddDoubleQuote ) + VSIFWriteL( "\"", 1, 1, fpCSV ); + CPLFree( pszEscaped ); + } + + if( (poFeatureDefn->GetFieldCount() == 1 || + (poFeatureDefn->GetFieldCount() == 0 && bHiddenWKTColumn)) && !bNonEmptyLine ) + VSIFPrintfL( fpCSV, "%c", chDelimiter ); + + if( bUseCRLF ) + VSIFPutcL( 13, fpCSV ); + VSIFPutcL( '\n', fpCSV ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetCRLF() */ +/************************************************************************/ + +void OGRCSVLayer::SetCRLF( int bNewValue ) + +{ + bUseCRLF = bNewValue; +} + +/************************************************************************/ +/* SetWriteGeometry() */ +/************************************************************************/ + +void OGRCSVLayer::SetWriteGeometry(OGRwkbGeometryType eGType, + OGRCSVGeometryFormat eGeometryFormat) +{ + this->eGeometryFormat = eGeometryFormat; + if (eGeometryFormat == OGR_CSV_GEOM_AS_WKT && eGType != wkbNone ) + { + OGRGeomFieldDefn oGFld("WKT", eGType); + bHiddenWKTColumn = TRUE; + /* We don't use CreateGeomField() since we don't want to generate */ + /* a geometry field in first position, as it confuses applications */ + /* (such as MapServer <= 6.4) that assume that the first regular field */ + /* they add will be at index 0 */ + poFeatureDefn->AddGeomFieldDefn( &oGFld ); + } + else + poFeatureDefn->SetGeomType( eGType ); +} + +/************************************************************************/ +/* SetCreateCSVT() */ +/************************************************************************/ + +void OGRCSVLayer::SetCreateCSVT(int bCreateCSVT) +{ + this->bCreateCSVT = bCreateCSVT; +} + +/************************************************************************/ +/* SetWriteBOM() */ +/************************************************************************/ + +void OGRCSVLayer::SetWriteBOM(int bWriteBOM) +{ + this->bWriteBOM = bWriteBOM; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRCSVLayer::GetFeatureCount( int bForce ) +{ + if (bInWriteMode || m_poFilterGeom != NULL || m_poAttrQuery != NULL) + return OGRLayer::GetFeatureCount(bForce); + + if (nTotalFeatures >= 0) + return nTotalFeatures; + + if (fpCSV == NULL) + return 0; + + ResetReading(); + + char **papszTokens; + nTotalFeatures = 0; + while(TRUE) + { + papszTokens = GetNextLineTokens(); + if( papszTokens == NULL ) + break; + + nTotalFeatures ++; + + CSLDestroy(papszTokens); + } + + ResetReading(); + + return nTotalFeatures; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/csw/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csw/GNUmakefile new file mode 100644 index 000000000..ce881bf87 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csw/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrcswdataset.o + +CPPFLAGS := -I.. -I../.. -I../gml -I../wfs $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ../wfs/ogr_wfs.h ../../swq.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/csw/drv_csw.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csw/drv_csw.html new file mode 100644 index 000000000..776db96ed --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csw/drv_csw.html @@ -0,0 +1,93 @@ + + +CSW - OGC CSW (Catalog Service for the Web) + + + + +

    CSW - OGC CSW (Catalog Service for the Web)

    + +(GDAL/OGR >= 2.0)

    + +This driver can connect to a OGC CSW service. It supports CSW 2.0.2 protocol. +GDAL/OGR must be built with Curl support in order to the +CSW driver to be compiled. And the GML driver should be set-up for read support +(thus requiring GDAL/OGR to be built with Xerces or Expat support).

    + +It retrieves records with Dublin Core metadata.

    + +

    Dataset name syntax

    + +The minimal syntax to open a CSW datasource is : CSW: and the URL open option, +or CSW:http://path/to/CSW/endpoint

    + +The following open options are available: +

      +
    • URL: URL to the CSW server endpoint (if not specified in the connection string already)
    • +
    • ELEMENTSETNAME=brief/summary/full: Level of details of properties. Defaults to full.
    • +
    • FULL_EXTENT_RECORDS_AS_NON_SPATIAL=YES/NO: Whether records with +(-180,-90,180,90) extent should be considered non-spatial. Defaults to NO.
    • +
    • OUTPUT_SCHEMA=URL : Value of outputSchema parameter, in the restricted +set supported by the serve. Special value gmd can be used as a shortcut for +http://www.isotc211.org/2005/gmd, csw for http://www.opengis.net/cat/csw/2.0.2. +When this open option is set, a raw_xml field will be filled with the +XML content of each record. Other metadata fields will remain empty.
    • +
    • MAX_RECORDS=value : Maximum number of records to retrieve in a single time. +Defaults to 500. Servers might have a lower accepted value.
    • +
    + +

    Filtering

    + +The driver will forward any spatial filter set with SetSpatialFilter() to +the server. It also makes its best effort to do the same for attribute +filters set with SetAttributeFilter() when possible +(turning OGR SQL language into OGC filter description).

    + +The anytext field can be queried to do a search in any text field. Note that +we always return it as null content however in OGR side, to avoid duplicating information.

    + +

    Issues

    + +Some servers do not respect EPSG axis order, in particular latitude, longitude +order for WGS 84 geodetic coordinates, so it might be needed to specify +the GML_INVERT_AXIS_ORDER_IF_LAT_LONG=NO configuration option in those cases.

    + +

    Examples

    + +
  • +Listing all the records of a CSW server: +
    +ogrinfo -ro -al -noextent CSW:http://catalog.data.gov/csw
    +
    +

    + +

  • +Listing all the records of a CSW server with spatial and an attribute filter on a give field: +
    +ogrinfo -ro -al -noextent CSW:http://catalog.data.gov/csw -spat 2 49 2 49 -where "subject LIKE '%mineralogy%'"
    +
    +

    + +

  • +Listing all the records of a CSW server that matches a text on any text field: +
    +ogrinfo -ro -al -q CSW:http://catalog.data.gov/csw -spat 2 49 2 49 -where "anytext LIKE '%France%'"
    +
    +

    + +

  • +Listing all the records of a CSW server as ISO 19115/19119: +
    +ogrinfo -ro -al -q CSW:http://catalog.data.gov/csw -oo OUTPUT_SCHEMA=gmd
    +
    +

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/csw/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csw/makefile.vc new file mode 100644 index 000000000..35fdb70ec --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csw/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrcswdataset.obj +EXTRAFLAGS = -I.. -I..\.. -I..\gml -I..\wfs + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/csw/ogrcswdataset.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csw/ogrcswdataset.cpp new file mode 100644 index 000000000..0d5bbe3d7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/csw/ogrcswdataset.cpp @@ -0,0 +1,1085 @@ +/****************************************************************************** + * $Id: ogrcswdataset.cpp 29034 2015-04-27 11:53:19Z rouault $ + * + * Project: CSW Translator + * Purpose: Implements OGRCSWDriver. + * Author: Even Rouault, Even Rouault + * + ****************************************************************************** + * Copyright (c) 2015, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrsf_frmts.h" +#include "cpl_conv.h" +#include "cpl_http.h" +#include "ogr_wfs.h" +#include "ogr_p.h" +#include "gmlutils.h" + +CPL_CVSID("$Id: ogrcswdataset.cpp 29034 2015-04-27 11:53:19Z rouault $"); + +extern "C" void RegisterOGRCSW(); + +/************************************************************************/ +/* OGRCSWLayer */ +/************************************************************************/ + +class OGRCSWDataSource; + +class OGRCSWLayer : public OGRLayer +{ + OGRCSWDataSource* poDS; + OGRFeatureDefn* poFeatureDefn; + + GDALDataset *poBaseDS; + OGRLayer *poBaseLayer; + + int nPagingStartIndex; + int nFeatureRead; + int nFeaturesInCurrentPage; + + CPLString osQuery, osCSWWhere; + + GDALDataset* FetchGetRecords(); + GIntBig GetFeatureCountWithHits(); + void BuildQuery(); + + public: + OGRCSWLayer(OGRCSWDataSource* poDS); + ~OGRCSWLayer(); + + virtual void ResetReading(); + virtual OGRFeature* GetNextFeature(); + virtual GIntBig GetFeatureCount(int bForce = FALSE); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ) { return FALSE; } + + virtual void SetSpatialFilter( OGRGeometry * ); + virtual OGRErr SetAttributeFilter( const char * ); +}; + +/************************************************************************/ +/* OGRCSWDataSource */ +/************************************************************************/ + +class OGRCSWDataSource : public OGRDataSource +{ + char* pszName; + CPLString osBaseURL; + CPLString osVersion; + CPLString osElementSetName; + CPLString osOutputSchema; + int nMaxRecords; + + OGRCSWLayer* poLayer; + int bFullExtentRecordsAsNonSpatial; + + CPLHTTPResult* SendGetCapabilities(); + + public: + OGRCSWDataSource(); + ~OGRCSWDataSource(); + + int Open( const char * pszFilename, + char** papszOpenOptions ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return poLayer != NULL; } + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ) { return FALSE; } + + CPLHTTPResult* HTTPFetch( const char* pszURL, const char* pszPost ); + + const CPLString& GetBaseURL() { return osBaseURL; } + const CPLString& GetVersion() { return osVersion; } + const CPLString& GetElementSetName() { return osElementSetName; } + const CPLString& GetOutputSchema() { return osOutputSchema; } + int FullExtentRecordsAsNonSpatial() { return bFullExtentRecordsAsNonSpatial; } + int GetMaxRecords() { return nMaxRecords; } +}; + +/************************************************************************/ +/* OGRCSWLayer() */ +/************************************************************************/ + +OGRCSWLayer::OGRCSWLayer(OGRCSWDataSource* poDS) +{ + this->poDS = poDS; + poFeatureDefn = new OGRFeatureDefn("records"); + SetDescription(poFeatureDefn->GetName()); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType(wkbPolygon); + OGRSpatialReference* poSRS = new OGRSpatialReference(SRS_WKT_WGS84); + poFeatureDefn->GetGeomFieldDefn(0)->SetName("boundingbox"); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + { + OGRFieldDefn oField("identifier", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("other_identifiers", OFTStringList); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("type", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("subject", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("other_subjects", OFTStringList); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("references", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("other_references", OFTStringList); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("modified", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("abstract", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("date", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("language", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("rights", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("format", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("other_formats", OFTStringList); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("creator", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("source", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + { + OGRFieldDefn oField("anytext", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + if( poDS->GetOutputSchema().size() ) + { + OGRFieldDefn oField("raw_xml", OFTString); + poFeatureDefn->AddFieldDefn(&oField); + } + + poBaseDS = NULL; + poBaseLayer = NULL; + + nPagingStartIndex = 0; + nFeatureRead = 0; + nFeaturesInCurrentPage = 0; + + poSRS->Release(); +} + +/************************************************************************/ +/* ~OGRCSWLayer() */ +/************************************************************************/ + +OGRCSWLayer::~OGRCSWLayer() +{ + poFeatureDefn->Release(); + GDALClose(poBaseDS); + CPLString osTmpDirName = CPLSPrintf("/vsimem/tempcsw_%p", this); + OGRWFSRecursiveUnlink(osTmpDirName); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRCSWLayer::ResetReading() +{ + nPagingStartIndex = 0; + nFeatureRead = 0; + nFeaturesInCurrentPage = 0; + GDALClose(poBaseDS); + poBaseDS = NULL; + poBaseLayer = NULL; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature* OGRCSWLayer::GetNextFeature() +{ + while(TRUE) + { + if (nFeatureRead == nPagingStartIndex + nFeaturesInCurrentPage) + { + nPagingStartIndex = nFeatureRead; + + GDALClose(poBaseDS); + poBaseLayer = NULL; + + poBaseDS = FetchGetRecords(); + if (poBaseDS) + { + poBaseLayer = poBaseDS->GetLayer(0); + poBaseLayer->ResetReading(); + nFeaturesInCurrentPage = (int)poBaseLayer->GetFeatureCount(); + } + } + if (!poBaseLayer) + return NULL; + + OGRFeature* poSrcFeature = poBaseLayer->GetNextFeature(); + if (poSrcFeature == NULL) + return NULL; + nFeatureRead ++; + + OGRFeature* poNewFeature = new OGRFeature(poFeatureDefn); + + for(int i=0;iGetFieldCount();i++) + { + const char* pszFieldname = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + int iSrcField = poSrcFeature->GetFieldIndex(pszFieldname); + /* http://www.paikkatietohakemisto.fi/geonetwork/srv/en/csw returns URI ... */ + if( iSrcField < 0 && strcmp(pszFieldname, "references") == 0 ) + iSrcField = poSrcFeature->GetFieldIndex("URI"); + if( iSrcField >= 0 && poSrcFeature->IsFieldSet(iSrcField) ) + { + OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType(); + OGRFieldType eSrcType = poSrcFeature->GetFieldDefnRef(iSrcField)->GetType(); + if( eType == eSrcType ) + { + poNewFeature->SetField(i, poSrcFeature->GetRawFieldRef(iSrcField)); + } + else + { + if( eType == OFTString && eSrcType == OFTStringList && + strcmp(pszFieldname, "identifier") == 0 ) + { + char** papszValues = poSrcFeature->GetFieldAsStringList(iSrcField); + poNewFeature->SetField("identifier", *papszValues); + if( papszValues[1] ) + poNewFeature->SetField("other_identifiers", papszValues + 1); + } + else if( eType == OFTString && eSrcType == OFTStringList && + strcmp(pszFieldname, "subject") == 0 ) + { + char** papszValues = poSrcFeature->GetFieldAsStringList(iSrcField); + poNewFeature->SetField("subject", *papszValues); + if( papszValues[1] ) + poNewFeature->SetField("other_subjects", papszValues + 1); + } + else if( eType == OFTString && eSrcType == OFTStringList && + strcmp(pszFieldname, "references") == 0 ) + { + char** papszValues = poSrcFeature->GetFieldAsStringList(iSrcField); + poNewFeature->SetField("references", *papszValues); + if( papszValues[1] ) + poNewFeature->SetField("other_references", papszValues + 1); + } + else if( eType == OFTString && eSrcType == OFTStringList && + strcmp(pszFieldname, "format") == 0 ) + { + char** papszValues = poSrcFeature->GetFieldAsStringList(iSrcField); + poNewFeature->SetField("format", *papszValues); + if( papszValues[1] ) + poNewFeature->SetField("other_formats", papszValues + 1); + } + else + poNewFeature->SetField(i, poSrcFeature->GetFieldAsString(iSrcField)); + } + } + } + + OGRGeometry* poGeom = poSrcFeature->StealGeometry(); + if( poGeom ) + { + if( poDS->FullExtentRecordsAsNonSpatial() ) + { + OGREnvelope sEnvelope; + poGeom->getEnvelope(&sEnvelope); + if( sEnvelope.MinX == -180 && sEnvelope.MinY == -90 && + sEnvelope.MaxX == 180 && sEnvelope.MaxY == 90 ) + { + delete poGeom; + poGeom = NULL; + } + } + if( poGeom ) + { + poGeom->assignSpatialReference(poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef()); + poNewFeature->SetGeometryDirectly(poGeom); + } + } + + poNewFeature->SetFID(nFeatureRead); + delete poSrcFeature; + + if( osCSWWhere.size() == 0 && + m_poAttrQuery != NULL && + !m_poAttrQuery->Evaluate( poNewFeature ) ) + { + delete poNewFeature; + } + else + { + return poNewFeature; + } + } +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRCSWLayer::GetFeatureCount(int bForce) +{ + GIntBig nFeatures = GetFeatureCountWithHits(); + if( nFeatures >= 0 ) + return nFeatures; + return OGRLayer::GetFeatureCount(bForce); +} + +/************************************************************************/ +/* GetFeatureCountWithHits() */ +/************************************************************************/ + +GIntBig OGRCSWLayer::GetFeatureCountWithHits() +{ + CPLString osPost = CPLSPrintf( +"" +"" +"" +"%s" +"%s" +"" +"", + poDS->GetVersion().c_str(), + poDS->GetElementSetName().c_str(), + osQuery.c_str()); + + CPLHTTPResult* psResult = poDS->HTTPFetch( poDS->GetBaseURL(), osPost); + if (psResult == NULL) + { + return -1; + } + + CPLXMLNode* psXML = CPLParseXMLString( (const char*) psResult->pabyData ); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + return -1; + } + CPLStripXMLNamespace( psXML, NULL, TRUE ); + CPLHTTPDestroyResult(psResult); + psResult = NULL; + + GIntBig nFeatures = CPLAtoGIntBig(CPLGetXMLValue(psXML, + "=GetRecordsResponse.SearchResults.numberOfRecordsMatched", "-1")); + + CPLDestroyXMLNode(psXML); + return nFeatures; +} + +/************************************************************************/ +/* FetchGetRecords() */ +/************************************************************************/ + +GDALDataset* OGRCSWLayer::FetchGetRecords() +{ + CPLHTTPResult* psResult = NULL; + + CPLString osOutputSchema = poDS->GetOutputSchema(); + if( osOutputSchema.size() ) + osOutputSchema = " outputSchema=\"" + osOutputSchema + "\""; + + CPLString osPost = CPLSPrintf( +"" +"" +"" +"%s" +"%s" +"" +"", + poDS->GetVersion().c_str(), + osOutputSchema.c_str(), + nPagingStartIndex + 1, + poDS->GetMaxRecords(), + poDS->GetElementSetName().c_str(), + osQuery.c_str()); + + psResult = poDS->HTTPFetch( poDS->GetBaseURL(), osPost); + if (psResult == NULL) + { + return NULL; + } + + CPLString osTmpDirName = CPLSPrintf("/vsimem/tempcsw_%p", this); + VSIMkdir(osTmpDirName, 0); + + GByte *pabyData = psResult->pabyData; + int nDataLen = psResult->nDataLen; + + if (strstr((const char*)pabyData, "pabyData = NULL; + + CPLHTTPDestroyResult(psResult); + + GDALDataset* poBaseDS = NULL; + + if( poDS->GetOutputSchema().size() ) + { + GDALDriver* poDrv = (GDALDriver*)GDALGetDriverByName("Memory"); + if( poDrv == NULL ) + return NULL; + CPLXMLNode* psRoot = CPLParseXMLFile(osTmpFileName); + if( psRoot == NULL ) + { + if( strstr((const char*)pabyData, " 1000) + pabyData[1000] = 0; + CPLError(CE_Failure, CPLE_AppDefined, + "Error: cannot parse %s", pabyData); + } + return NULL; + } + CPLXMLNode* psSearchResults = CPLGetXMLNode(psRoot, "=csw:GetRecordsResponse.csw:SearchResults"); + if( psSearchResults == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GetRecordsResponse.SearchResults"); + CPLDestroyXMLNode(psRoot); + return NULL; + } + + poBaseDS = poDrv->Create("", 0, 0, 0, GDT_Unknown, NULL); + OGRLayer* poLyr = poBaseDS->CreateLayer("records"); + OGRFieldDefn oField("raw_xml", OFTString); + poLyr->CreateField(&oField); + for( CPLXMLNode* psIter = psSearchResults->psChild; psIter; psIter = psIter->psNext ) + { + if( psIter->eType == CXT_Element ) + { + OGRFeature* poFeature = new OGRFeature(poLyr->GetLayerDefn()); + + CPLXMLNode* psNext = psIter->psNext; + psIter->psNext = NULL; + char* pszXML = CPLSerializeXMLTree(psIter); + + const char* pszWest = NULL; + const char* pszEast = NULL; + const char* pszSouth = NULL; + const char* pszNorth = NULL; + CPLXMLNode* psBBox = CPLSearchXMLNode( psIter, "gmd:EX_GeographicBoundingBox"); + if( psBBox ) + { + /* ISO 19115/19119: http://www.isotc211.org/2005/gmd */ + pszWest = CPLGetXMLValue(psBBox, "gmd:westBoundLongitude.gco:Decimal", NULL); + pszEast = CPLGetXMLValue(psBBox, "gmd:eastBoundLongitude.gco:Decimal", NULL); + pszSouth = CPLGetXMLValue(psBBox, "gmd:southBoundLatitude.gco:Decimal", NULL); + pszNorth = CPLGetXMLValue(psBBox, "gmd:northBoundLatitude.gco:Decimal", NULL); + } + else if( (psBBox = CPLSearchXMLNode( psIter, "spdom") ) != NULL ) + { + /* FGDC: http://www.opengis.net/cat/csw/csdgm */ + pszWest = CPLGetXMLValue(psBBox, "bounding.westbc", NULL); + pszEast = CPLGetXMLValue(psBBox, "bounding.eastbc", NULL); + pszSouth = CPLGetXMLValue(psBBox, "bounding.southbc", NULL); + pszNorth = CPLGetXMLValue(psBBox, "bounding.northbc", NULL); + } + if( pszWest && pszEast && pszSouth && pszNorth ) + { + double dfMinX = CPLAtof(pszWest); + double dfMaxX = CPLAtof(pszEast); + double dfMinY = CPLAtof(pszSouth); + double dfMaxY = CPLAtof(pszNorth); + OGRLinearRing* poLR = new OGRLinearRing(); + poLR->addPoint(dfMinX, dfMinY); + poLR->addPoint(dfMinX, dfMaxY); + poLR->addPoint(dfMaxX, dfMaxY); + poLR->addPoint(dfMaxX, dfMinY); + poLR->addPoint(dfMinX, dfMinY); + OGRPolygon* poPoly = new OGRPolygon(); + poPoly->addRingDirectly(poLR); + poFeature->SetGeometryDirectly(poPoly); + } + else if( (psBBox = CPLSearchXMLNode( psIter, "ows:BoundingBox") ) != NULL ) + { + CPLFree(psBBox->pszValue); + psBBox->pszValue = CPLStrdup("gml:Envelope"); + CPLString osSRS = CPLGetXMLValue(psBBox, "crs", ""); + OGRGeometry* poGeom = GML2OGRGeometry_XMLNode( psBBox, + FALSE, + 0, 0, FALSE, TRUE, + FALSE ); + int bLatLongOrder = TRUE; + if( osSRS.size() ) + bLatLongOrder = GML_IsSRSLatLongOrder(osSRS); + if( bLatLongOrder && CSLTestBoolean( + CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES")) ) + poGeom->swapXY(); + poFeature->SetGeometryDirectly(poGeom); + } + + psIter->psNext = psNext; + + poFeature->SetField(0, pszXML); + poLyr->CreateFeature(poFeature); + CPLFree(pszXML); + delete poFeature; + } + } + CPLDestroyXMLNode(psRoot); + } + else + { + poBaseDS = (GDALDataset*) OGROpen(osTmpFileName, FALSE, NULL); + if (poBaseDS == NULL) + { + if( strstr((const char*)pabyData, " 1000) + pabyData[1000] = 0; + CPLError(CE_Failure, CPLE_AppDefined, + "Error: cannot parse %s", pabyData); + } + return NULL; + } + } + + OGRLayer* poLayer = poBaseDS->GetLayer(0); + if (poLayer == NULL) + { + GDALClose(poBaseDS); + return NULL; + } + + return poBaseDS; +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRCSWLayer::SetSpatialFilter( OGRGeometry * poGeom ) +{ + OGRLayer::SetSpatialFilter(poGeom); + ResetReading(); + BuildQuery(); +} + +/************************************************************************/ +/* OGRCSWAddRightPrefixes() */ +/************************************************************************/ + +static void OGRCSWAddRightPrefixes(swq_expr_node* poNode) +{ + if( poNode->eNodeType == SNT_COLUMN ) + { + if( EQUAL(poNode->string_value, "identifier") || + EQUAL(poNode->string_value, "title") || + EQUAL(poNode->string_value, "type") || + EQUAL(poNode->string_value, "subject") || + EQUAL(poNode->string_value, "date") || + EQUAL(poNode->string_value, "language") || + EQUAL(poNode->string_value, "rights") || + EQUAL(poNode->string_value, "format") || + EQUAL(poNode->string_value, "creator") || + EQUAL(poNode->string_value, "source") ) + { + char* pszNewVal = CPLStrdup(CPLSPrintf("dc:%s", poNode->string_value)); + CPLFree(poNode->string_value); + poNode->string_value = pszNewVal; + } + else if( EQUAL(poNode->string_value, "references") || + EQUAL(poNode->string_value, "modified") || + EQUAL(poNode->string_value, "abstract") ) + { + char* pszNewVal = CPLStrdup(CPLSPrintf("dct:%s", poNode->string_value)); + CPLFree(poNode->string_value); + poNode->string_value = pszNewVal; + } + else if( EQUAL(poNode->string_value, "other_identifiers") ) + { + CPLFree(poNode->string_value); + poNode->string_value = CPLStrdup("dc:identifier"); + } + else if( EQUAL(poNode->string_value, "other_subjects") ) + { + CPLFree(poNode->string_value); + poNode->string_value = CPLStrdup("dc:subject"); + } + else if( EQUAL(poNode->string_value, "other_references") ) + { + CPLFree(poNode->string_value); + poNode->string_value = CPLStrdup("dct:references"); + } + else if( EQUAL(poNode->string_value, "other_formats") ) + { + CPLFree(poNode->string_value); + poNode->string_value = CPLStrdup("dc:format"); + } + else if( EQUAL(poNode->string_value, "AnyText") ) + { + CPLFree(poNode->string_value); + poNode->string_value = CPLStrdup("csw:AnyText"); + } + else if( EQUAL(poNode->string_value, "boundingbox") ) + { + CPLFree(poNode->string_value); + poNode->string_value = CPLStrdup("ows:BoundingBox"); + } + } + else if( poNode->eNodeType == SNT_OPERATION ) + { + for(int i=0;i < poNode->nSubExprCount;i++) + OGRCSWAddRightPrefixes(poNode->papoSubExpr[i]); + } +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRCSWLayer::SetAttributeFilter( const char * pszFilter ) +{ + if (pszFilter != NULL && pszFilter[0] == 0) + pszFilter = NULL; + + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszFilter) ? CPLStrdup(pszFilter) : NULL; + + delete m_poAttrQuery; + m_poAttrQuery = NULL; + + if( pszFilter != NULL ) + { + m_poAttrQuery = new OGRFeatureQuery(); + + OGRErr eErr = m_poAttrQuery->Compile( GetLayerDefn(), pszFilter, TRUE, + WFSGetCustomFuncRegistrar() ); + if( eErr != OGRERR_NONE ) + { + delete m_poAttrQuery; + m_poAttrQuery = NULL; + return eErr; + } + } + + if (m_poAttrQuery != NULL ) + { + swq_expr_node* poNode = (swq_expr_node*) m_poAttrQuery->GetSWQExpr(); + swq_expr_node* poNodeClone = poNode->Clone(); + poNodeClone->ReplaceBetweenByGEAndLERecurse(); + OGRCSWAddRightPrefixes(poNodeClone); + + int bNeedsNullCheck = FALSE; + if( poNode->field_type != SWQ_BOOLEAN ) + osCSWWhere = ""; + else + osCSWWhere = WFS_TurnSQLFilterToOGCFilter(poNodeClone, + NULL, + NULL, + 110, + FALSE, + FALSE, + FALSE, + "ogc:", + &bNeedsNullCheck); + delete poNodeClone; + } + else + osCSWWhere = ""; + + if (m_poAttrQuery != NULL && osCSWWhere.size() == 0) + { + CPLDebug("CSW", "Using client-side only mode for filter \"%s\"", pszFilter); + OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter); + if (eErr != OGRERR_NONE) + return eErr; + } + + ResetReading(); + BuildQuery(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* BuildQuery() */ +/************************************************************************/ + +void OGRCSWLayer::BuildQuery() +{ + if( m_poFilterGeom != NULL || osCSWWhere.size() != 0 ) + { + osQuery = ""; + osQuery += ""; + if( m_poFilterGeom != NULL && osCSWWhere.size() != 0 ) + osQuery += ""; + if( m_poFilterGeom != NULL ) + { + osQuery += ""; + osQuery += "ows:BoundingBox"; + osQuery += ""; + OGREnvelope sEnvelope; + m_poFilterGeom->getEnvelope(&sEnvelope); + if( CSLTestBoolean( + CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES")) ) + { + osQuery += CPLSPrintf("%.16g %.16g", sEnvelope.MinY, sEnvelope.MinX); + osQuery += CPLSPrintf("%.16g %.16g", sEnvelope.MaxY, sEnvelope.MaxX); + } + else + { + osQuery += CPLSPrintf("%.16g %.16g", sEnvelope.MinX, sEnvelope.MinY); + osQuery += CPLSPrintf("%.16g %.16g", sEnvelope.MaxX, sEnvelope.MaxY); + } + osQuery += ""; + osQuery += ""; + } + osQuery += osCSWWhere; + if( m_poFilterGeom != NULL && osCSWWhere.size() != 0 ) + osQuery += ""; + osQuery += ""; + osQuery += ""; + } + else + osQuery = ""; +} + +/************************************************************************/ +/* OGRCSWDataSource() */ +/************************************************************************/ + +OGRCSWDataSource::OGRCSWDataSource() +{ + pszName = NULL; + poLayer = NULL; + bFullExtentRecordsAsNonSpatial = FALSE; + nMaxRecords = 500; +} + +/************************************************************************/ +/* ~OGRCSWDataSource() */ +/************************************************************************/ + +OGRCSWDataSource::~OGRCSWDataSource() +{ + delete poLayer; + CPLFree( pszName ); +} + +/************************************************************************/ +/* SendGetCapabilities() */ +/************************************************************************/ + +CPLHTTPResult* OGRCSWDataSource::SendGetCapabilities() +{ + CPLString osURL(osBaseURL); + + osURL = CPLURLAddKVP(osURL, "SERVICE", "CSW"); + osURL = CPLURLAddKVP(osURL, "REQUEST", "GetCapabilities"); + + CPLHTTPResult* psResult; + + CPLDebug("CSW", "%s", osURL.c_str()); + + psResult = HTTPFetch( osURL, NULL); + if (psResult == NULL) + { + return NULL; + } + + if (strstr((const char*)psResult->pabyData, + "pabyData, + "pabyData, + "pabyData); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + return psResult; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRCSWDataSource::Open( const char * pszFilename, + char** papszOpenOptions ) +{ + const char* pszBaseURL = CSLFetchNameValue(papszOpenOptions, "URL"); + if( pszBaseURL == NULL ) + { + pszBaseURL = pszFilename; + if (EQUALN(pszFilename, "CSW:", 4)) + pszBaseURL += 4; + if( pszBaseURL[0] == '\0' ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Missing URL open option"); + return FALSE; + } + } + osBaseURL = pszBaseURL; + osElementSetName = CSLFetchNameValueDef(papszOpenOptions, "ELEMENTSETNAME", + "full"); + bFullExtentRecordsAsNonSpatial = CSLFetchBoolean(papszOpenOptions, + "FULL_EXTENT_RECORDS_AS_NON_SPATIAL", + FALSE); + osOutputSchema = CSLFetchNameValueDef(papszOpenOptions, "OUTPUT_SCHEMA", ""); + if( EQUAL(osOutputSchema, "gmd") ) + osOutputSchema = "http://www.isotc211.org/2005/gmd"; + else if( EQUAL(osOutputSchema, "csw") ) + osOutputSchema = "http://www.opengis.net/cat/csw/2.0.2"; + nMaxRecords = atoi(CSLFetchNameValueDef(papszOpenOptions, "MAX_RECORDS", "500")); + + if (strncmp(osBaseURL, "http://", 7) != 0 && + strncmp(osBaseURL, "https://", 8) != 0 && + strncmp(osBaseURL, "/vsimem/", strlen("/vsimem/")) != 0) + return FALSE; + + CPLHTTPResult* psResult = SendGetCapabilities(); + if( psResult == NULL ) + return FALSE; + + CPLXMLNode* psXML = CPLParseXMLString( (const char*) psResult->pabyData ); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + return FALSE; + } + CPLStripXMLNamespace( psXML, NULL, TRUE ); + CPLHTTPDestroyResult(psResult); + psResult = NULL; + + const char* pszVersion = CPLGetXMLValue( psXML, "=Capabilities.version", NULL); + if( pszVersion == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find Capabilities.version"); + CPLDestroyXMLNode(psXML); + return FALSE; + } + if( !EQUAL(pszVersion, "2.0.2") ) + CPLDebug("CSW", "Presumably only work properly with 2.0.2. Reported version is %s", pszVersion); + osVersion = pszVersion; + CPLDestroyXMLNode(psXML); + + poLayer = new OGRCSWLayer(this); + + return TRUE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRCSWDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= ((poLayer != NULL) ? 1 : 0) ) + return NULL; + else + return poLayer; +} + +/************************************************************************/ +/* HTTPFetch() */ +/************************************************************************/ + +CPLHTTPResult* OGRCSWDataSource::HTTPFetch( const char* pszURL, const char* pszPost ) +{ + char** papszOptions = NULL; + if( pszPost ) + { + papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", pszPost); + papszOptions = CSLAddNameValue(papszOptions, "HEADERS", + "Content-Type: application/xml; charset=UTF-8"); + } + CPLHTTPResult* psResult = CPLHTTPFetch( pszURL, papszOptions ); + CSLDestroy(papszOptions); + + if (psResult == NULL) + { + return NULL; + } + if (psResult->nStatus != 0 || psResult->pszErrBuf != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s (%d)", + (psResult->pszErrBuf) ? psResult->pszErrBuf : "unknown", psResult->nStatus); + CPLHTTPDestroyResult(psResult); + return NULL; + } + if (psResult->pabyData == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Empty content returned by server"); + CPLHTTPDestroyResult(psResult); + return NULL; + } + return psResult; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +static int OGRCSWDriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + return EQUALN(poOpenInfo->pszFilename, "CSW:", 4); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRCSWDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( !OGRCSWDriverIdentify(poOpenInfo) || poOpenInfo->eAccess == GA_Update ) + return NULL; + + OGRCSWDataSource *poDS = new OGRCSWDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, + poOpenInfo->papszOpenOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRCSW() */ +/************************************************************************/ + +void RegisterOGRCSW() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "CSW" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "CSW" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "OGC CSW (Catalog Service for the Web)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_csw.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CONNECTION_PREFIX, "CSW:" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " +" " ); + + poDriver->pfnIdentify = OGRCSWDriverIdentify; + poDriver->pfnOpen = OGRCSWDriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/Doxyfile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/Doxyfile new file mode 100644 index 000000000..b80aca49d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/Doxyfile @@ -0,0 +1,260 @@ +# This file describes the settings to be used by doxygen for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of word surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = dgnlib + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +#HTML_HEADER = html/gdal_header.html + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +#HTML_FOOTER = html/gdal_footer.html + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each page. A value of NO (the default) enables the index and the +# value YES disables it. + +DISABLE_INDEX = NO + +# If the EXTRACT_ALL tag is set to YES all classes and functions will be +# included in the documentation, even if no documentation was available. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members inside documented classes or files. + +HIDE_UNDOC_MEMBERS = NO + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output + +GENERATE_HTML = YES + +# Disable RTF + +GENERATE_RTF = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the FULL_PATH_NAMES tag is set to YES Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used + +FULL_PATH_NAMES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = dgnlib.h dgnopen.cpp dgnread.cpp dgnstroke.cpp dgnhelp.cpp dgnwrite.cpp + + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +FILE_PATTERNS = *.h *.cpp *.c *.dox + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = . + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. + +MACRO_EXPANSION = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). In the former case 1 is used as the +# definition. + +PREDEFINED = + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED tag. + +EXPAND_ONLY_PREDEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = /usr/local/bin/ + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/GNUmakefile new file mode 100644 index 000000000..732799b1b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/GNUmakefile @@ -0,0 +1,60 @@ + + +include ../../../GDALmake.opt + +MIN_OBJ = dgnopen.o dgnread.o dgnfloat.o dgnhelp.o dgnwrite.o dgnstroke.o +OBJ = ogrdgndriver.o ogrdgndatasource.o ogrdgnlayer.o $(MIN_OBJ) + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +CPL_DIR = ../../../port + +WEB_DIR = $(HOME)/dgnlib-web +DIST_REV = 1.11 +DIST_DIR = dgnlib-$(DIST_REV) +DIST_FILE = dgnlib-$(DIST_REV).zip +DIST_FILES = dgnread.cpp dgnopen.cpp dgnlib.h dgnlibp.h dgnfloat.cpp \ + dgnstroke.cpp dgnwrite.cpp dgnhelp.cpp \ + dgnwritetest.c dgndump.c \ + \ + dist/Makefile dist/Makefile.vc dist/cpl_config.h \ + dist/README \ + \ + $(CPL_DIR)/cpl_port.h \ + $(CPL_DIR)/cpl_vsi.h $(CPL_DIR)/cpl_vsisimple.cpp \ + $(CPL_DIR)/cpl_conv.h $(CPL_DIR)/cpl_conv.cpp \ + $(CPL_DIR)/cpl_error.h $(CPL_DIR)/cpl_error.cpp \ + $(CPL_DIR)/cpl_string.h $(CPL_DIR)/cpl_string.cpp \ + $(CPL_DIR)/cpl_config.h.vc $(CPL_DIR)/cpl_vsil_simple.cpp \ + $(CPL_DIR)/cpl_path.cpp $(CPL_DIR)/cpl_dir.cpp \ + $(CPL_DIR)/cpl_multiproc.h $(CPL_DIR)/cpl_multiproc.cpp + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -rf *.o $(DEST_DIR) dgnlib-$(DIST_REV).zip html man $(O_OBJ) + rm -f dgndump$(EXE) dgnwritetest$(EXE) + +dgndump$(EXE): dgndump.$(OBJ_EXT) + $(LD) $(LDFLAGS) dgndump.$(OBJ_EXT) $(CONFIG_LIBS) -o dgndump$(EXE) + +dgnwritetest$(EXE): dgnwritetest.$(OBJ_EXT) + $(LD) $(LDFLAGS) dgnwritetest.$(OBJ_EXT) $(CONFIG_LIBS) -o dgnwritetest$(EXE) + +docs: + doxygen + +zip: + rm -rf $(DIST_DIR) + mkdir $(DIST_DIR) + cp $(DIST_FILES) $(DIST_DIR) + zip -r $(DIST_FILE) $(DIST_DIR) + +web-docs-update: docs + cp html/* $(WEB_DIR)/libhtml + +web-update: web-docs-update dist + cp $(DIST_FILE) $(WEB_DIR)/dl + +pge_test: pge_test.cpp dgn_pge.cpp $(OBJ) + gcc -Wall -g pge_test.cpp dgn_pge.cpp $(OBJ) $(GDAL_LIB) -lm -o pge_test diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgndump.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgndump.c new file mode 100644 index 000000000..b0e4c78ed --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgndump.c @@ -0,0 +1,253 @@ +/****************************************************************************** + * $Id: dgndump.c 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: Microstation DGN Access Library + * Purpose: Temporary low level DGN dumper application. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Avenza Systems Inc, http://www.avenza.com/ + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "dgnlibp.h" + +CPL_CVSID("$Id: dgndump.c 27942 2014-11-11 00:57:41Z rouault $"); + +static void DGNDumpRawElement( DGNHandle hDGN, DGNElemCore *psCore, + FILE *fpOut ); + +/************************************************************************/ +/* Usage() */ +/************************************************************************/ + +static void Usage() + +{ + printf( "Usage: dgndump [-e xmin ymin xmax ymax] [-s] [-r n] filename.dgn\n" ); + printf( "\n" ); + printf( " -e xmin ymin xmax ymax: only get elements within extents.\n" ); + printf( " -s: produce summary report of element types and levels.\n"); + printf( " -r n: report raw binary contents of elements of type n.\n"); + + exit( 1 ); +} + +/************************************************************************/ +/* main() */ +/************************************************************************/ + +int main( int argc, char ** argv ) + +{ + DGNHandle hDGN; + DGNElemCore *psElement; + const char *pszFilename = NULL; + int bSummary = FALSE, iArg, bRaw = FALSE, bReportExtents = FALSE; + char achRaw[128]; + double dfSFXMin=0.0, dfSFXMax=0.0, dfSFYMin=0.0, dfSFYMax=0.0; + + memset( achRaw, 0, 128 ); + + for( iArg = 1; iArg < argc; iArg++ ) + { + if( strcmp(argv[iArg],"-s") == 0 ) + { + bSummary = TRUE; + } + else if( strcmp(argv[iArg],"-e") == 0 && iArg < argc-4 ) + { + dfSFXMin = CPLAtof(argv[iArg+1]); + dfSFYMin = CPLAtof(argv[iArg+2]); + dfSFXMax = CPLAtof(argv[iArg+3]); + dfSFYMax = CPLAtof(argv[iArg+4]); + iArg += 4; + } + else if( strcmp(argv[iArg],"-r") == 0 && iArg < argc-1 ) + { + achRaw[MAX(0,MIN(127,atoi(argv[iArg+1])))] = 1; + bRaw = TRUE; + iArg++; + } + else if( strcmp(argv[iArg],"-extents") == 0 ) + { + bReportExtents = TRUE; + } + else if( argv[iArg][0] == '-' || pszFilename != NULL ) + Usage(); + else + pszFilename = argv[iArg]; + } + + if( pszFilename == NULL ) + Usage(); + + hDGN = DGNOpen( pszFilename, FALSE ); + if( hDGN == NULL ) + exit( 1 ); + + if( bRaw ) + DGNSetOptions( hDGN, DGNO_CAPTURE_RAW_DATA ); + + DGNSetSpatialFilter( hDGN, dfSFXMin, dfSFYMin, dfSFXMax, dfSFYMax ); + + if( !bSummary ) + { + while( (psElement=DGNReadElement(hDGN)) != NULL ) + { + DGNDumpElement( hDGN, psElement, stdout ); + + CPLAssert( psElement->type >= 0 && psElement->type < 128 ); + + if( achRaw[psElement->type] != 0 ) + DGNDumpRawElement( hDGN, psElement, stdout ); + + if( bReportExtents ) + { + DGNPoint sMin, sMax; + if( DGNGetElementExtents( hDGN, psElement, &sMin, &sMax ) ) + printf( " Extents: (%.6f,%.6f,%.6f)\n" + " to (%.6f,%.6f,%.6f)\n", + sMin.x, sMin.y, sMin.z, + sMax.x, sMax.y, sMax.z ); + } + + DGNFreeElement( hDGN, psElement ); + } + } + else + { + const DGNElementInfo *pasEI; + int nCount, i, nLevel, nType; + int anLevelTypeCount[128*64]; + int anLevelCount[64]; + int anTypeCount[128]; + double adfExtents[6]; + + DGNGetExtents( hDGN, adfExtents ); + printf( "X Range: %.2f to %.2f\n", + adfExtents[0], adfExtents[3] ); + printf( "Y Range: %.2f to %.2f\n", + adfExtents[1], adfExtents[4] ); + printf( "Z Range: %.2f to %.2f\n", + adfExtents[2], adfExtents[5] ); + + pasEI = DGNGetElementIndex( hDGN, &nCount ); + + printf( "Total Elements: %d\n", nCount ); + + memset( anLevelTypeCount, 0, 128*64*sizeof(int) ); + memset( anLevelCount, 0, 64*sizeof(int) ); + memset( anTypeCount, 0, 128*sizeof(int) ); + + for( i = 0; i < nCount; i++ ) + { + anLevelTypeCount[pasEI[i].level * 128 + pasEI[i].type]++; + anLevelCount[pasEI[i].level]++; + anTypeCount[pasEI[i].type]++; + } + + printf( "\n" ); + printf( "Per Type Report\n" ); + printf( "===============\n" ); + + for( nType = 0; nType < 128; nType++ ) + { + if( anTypeCount[nType] != 0 ) + { + printf( "Type %s: %d\n", + DGNTypeToName( nType ), + anTypeCount[nType] ); + } + } + + printf( "\n" ); + printf( "Per Level Report\n" ); + printf( "================\n" ); + + for( nLevel = 0; nLevel < 64; nLevel++ ) + { + if( anLevelCount[nLevel] == 0 ) + continue; + + printf( "Level %d, %d elements:\n", + nLevel, + anLevelCount[nLevel] ); + + for( nType = 0; nType < 128; nType++ ) + { + if( anLevelTypeCount[nLevel * 128 + nType] != 0 ) + { + printf( " Type %s: %d\n", + DGNTypeToName( nType ), + anLevelTypeCount[nLevel*128 + nType] ); + } + } + + printf( "\n" ); + } + } + + DGNClose( hDGN ); + + return 0; +} + +/************************************************************************/ +/* DGNDumpRawElement() */ +/************************************************************************/ + +static void DGNDumpRawElement( DGNHandle hDGN, DGNElemCore *psCore, + FILE *fpOut ) + +{ + int i, iChar = 0; + char szLine[80]; + + fprintf( fpOut, " Raw Data (%d bytes):\n", psCore->raw_bytes ); + for( i = 0; i < psCore->raw_bytes; i++ ) + { + char szHex[3]; + + if( (i % 16) == 0 ) + { + sprintf( szLine, "%6d: %71s", i, " " ); + iChar = 0; + } + + sprintf( szHex, "%02x", psCore->raw_data[i] ); + strncpy( szLine+8+iChar*2, szHex, 2 ); + + if( psCore->raw_data[i] < 32 || psCore->raw_data[i] > 127 ) + szLine[42+iChar] = '.'; + else + szLine[42+iChar] = psCore->raw_data[i]; + + if( i == psCore->raw_bytes - 1 || (i+1) % 16 == 0 ) + { + fprintf( fpOut, "%s\n", szLine ); + } + + iChar++; + } +} + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnfloat.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnfloat.cpp new file mode 100644 index 000000000..20f19baf5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnfloat.cpp @@ -0,0 +1,251 @@ +/****************************************************************************** + * $Id: dgnfloat.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: Microstation DGN Access Library + * Purpose: Functions for translating DGN floats into IEEE floats. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Avenza Systems Inc, http://www.avenza.com/ + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "dgnlibp.h" + +CPL_CVSID("$Id: dgnfloat.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +typedef struct dbl { + GUInt32 hi; + GUInt32 lo; +} double64_t; + +/************************************************************************/ +/* DGN2IEEEDouble() */ +/************************************************************************/ + +void DGN2IEEEDouble(void * dbl) + +{ + double64_t dt; + GUInt32 sign; + GUInt32 exponent; + GUInt32 rndbits; + unsigned char *src; + unsigned char *dest; + +/* -------------------------------------------------------------------- */ +/* Arrange the VAX double so that it may be accessed by a */ +/* double64_t structure, (two GUInt32s). */ +/* -------------------------------------------------------------------- */ + src = (unsigned char *) dbl; + dest = (unsigned char *) &dt; +#ifdef CPL_LSB + dest[2] = src[0]; + dest[3] = src[1]; + dest[0] = src[2]; + dest[1] = src[3]; + dest[6] = src[4]; + dest[7] = src[5]; + dest[4] = src[6]; + dest[5] = src[7]; +#else + dest[1] = src[0]; + dest[0] = src[1]; + dest[3] = src[2]; + dest[2] = src[3]; + dest[5] = src[4]; + dest[4] = src[5]; + dest[7] = src[6]; + dest[6] = src[7]; +#endif + +/* -------------------------------------------------------------------- */ +/* Save the sign of the double */ +/* -------------------------------------------------------------------- */ + sign = dt.hi & 0x80000000; + +/* -------------------------------------------------------------------- */ +/* Adjust the exponent so that we may work with it */ +/* -------------------------------------------------------------------- */ + exponent = dt.hi >> 23; + exponent = exponent & 0x000000ff; + + if (exponent) + exponent = exponent -129 + 1023; + +/* -------------------------------------------------------------------- */ +/* Save the bits that we are discarding so we can round properly */ +/* -------------------------------------------------------------------- */ + rndbits = dt.lo & 0x00000007; + + dt.lo = dt.lo >> 3; + dt.lo = (dt.lo & 0x1fffffff) | (dt.hi << 29); + + if (rndbits) + dt.lo = dt.lo | 0x00000001; + +/* -------------------------------------------------------------------- */ +/* Shift the hi-order int over 3 and insert the exponent and sign */ +/* -------------------------------------------------------------------- */ + dt.hi = dt.hi >> 3; + dt.hi = dt.hi & 0x000fffff; + dt.hi = dt.hi | (exponent << 20) | sign; + + + +#ifdef CPL_LSB +/* -------------------------------------------------------------------- */ +/* Change the number to a byte swapped format */ +/* -------------------------------------------------------------------- */ + src = (unsigned char *) &dt; + dest = (unsigned char *) dbl; + + dest[0] = src[4]; + dest[1] = src[5]; + dest[2] = src[6]; + dest[3] = src[7]; + dest[4] = src[0]; + dest[5] = src[1]; + dest[6] = src[2]; + dest[7] = src[3]; +#else + memcpy( dbl, &dt, 8 ); +#endif +} + +/************************************************************************/ +/* IEEE2DGNDouble() */ +/************************************************************************/ + +void IEEE2DGNDouble(void * dbl) + +{ + double64_t dt; + GInt32 exponent; + GInt32 sign; + GByte *src,*dest; + +#ifdef CPL_LSB + src = (GByte *) dbl; + dest = (GByte *) &dt; + + dest[0] = src[4]; + dest[1] = src[5]; + dest[2] = src[6]; + dest[3] = src[7]; + dest[4] = src[0]; + dest[5] = src[1]; + dest[6] = src[2]; + dest[7] = src[3]; +#else + memcpy( &dt, dbl, 8 ); +#endif + + sign = dt.hi & 0x80000000; + exponent = dt.hi >> 20; + exponent = exponent & 0x000007ff; + +/* -------------------------------------------------------------------- */ +/* An exponent of zero means a zero value. */ +/* -------------------------------------------------------------------- */ + if (exponent) + exponent = exponent -1023+129; + +/* -------------------------------------------------------------------- */ +/* In the case of overflow, return the largest number we can */ +/* -------------------------------------------------------------------- */ + if (exponent > 255) + { + dest = (GByte *) dbl; + + if (sign) + dest[1] = 0xff; + else + dest[1] = 0x7f; + + dest[0] = 0xff; + dest[2] = 0xff; + dest[3] = 0xff; + dest[4] = 0xff; + dest[5] = 0xff; + dest[6] = 0xff; + dest[7] = 0xff; + + return; + } + +/* -------------------------------------------------------------------- */ +/* In the case of of underflow return zero */ +/* -------------------------------------------------------------------- */ + else if ((exponent < 0 ) || + (exponent == 0 && sign == 0)) + { + dest = (GByte *) dbl; + + dest[0] = 0x00; + dest[1] = 0x00; + dest[2] = 0x00; + dest[3] = 0x00; + dest[4] = 0x00; + dest[5] = 0x00; + dest[6] = 0x00; + dest[7] = 0x00; + + return; + } + else + { +/* -------------------------------------------------------------------- */ +/* Shift the fraction 3 bits left and set the exponent and sign*/ +/* -------------------------------------------------------------------- */ + dt.hi = dt.hi << 3; + dt.hi = dt.hi | (dt.lo >> 29); + dt.hi = dt.hi & 0x007fffff; + dt.hi = dt.hi | (exponent << 23) | sign; + + dt.lo = dt.lo << 3; + } + +/* -------------------------------------------------------------------- */ +/* Convert the double back to VAX format */ +/* -------------------------------------------------------------------- */ + src = (GByte *) &dt; + dest = (GByte *) dbl; + +#ifdef CPL_LSB + dest[2] = src[0]; + dest[3] = src[1]; + dest[0] = src[2]; + dest[1] = src[3]; + dest[6] = src[4]; + dest[7] = src[5]; + dest[4] = src[6]; + dest[5] = src[7]; +#else + dest[1] = src[0]; + dest[0] = src[1]; + dest[3] = src[2]; + dest[2] = src[3]; + dest[5] = src[4]; + dest[4] = src[5]; + dest[7] = src[6]; + dest[6] = src[7]; +#endif +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnhelp.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnhelp.cpp new file mode 100644 index 000000000..0a34a6d69 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnhelp.cpp @@ -0,0 +1,1450 @@ +/****************************************************************************** + * $Id: dgnhelp.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: Microstation DGN Access Library + * Purpose: Application visible helper functions for parsing DGN information. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Avenza Systems Inc, http://www.avenza.com/ + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "dgnlibp.h" + +CPL_CVSID("$Id: dgnhelp.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +static unsigned char abyDefaultPCT[256][3] = +{ + {255,255,255}, + {0,0,255}, + {0,255,0}, + {255,0,0}, + {255,255,0}, + {255,0,255}, + {255,127,0}, + {0,255,255}, + {64,64,64}, + {192,192,192}, + {254,0,96}, + {160,224,0}, + {0,254,160}, + {128,0,160}, + {176,176,176}, + {0,240,240}, + {240,240,240}, + {0,0,240}, + {0,240,0}, + {240,0,0}, + {240,240,0}, + {240,0,240}, + {240,122,0}, + {0,240,240}, + {240,240,240}, + {0,0,240}, + {0,240,0}, + {240,0,0}, + {240,240,0}, + {240,0,240}, + {240,122,0}, + {0,225,225}, + {225,225,225}, + {0,0,225}, + {0,225,0}, + {225,0,0}, + {225,225,0}, + {225,0,225}, + {225,117,0}, + {0,225,225}, + {225,225,225}, + {0,0,225}, + {0,225,0}, + {225,0,0}, + {225,225,0}, + {225,0,225}, + {225,117,0}, + {0,210,210}, + {210,210,210}, + {0,0,210}, + {0,210,0}, + {210,0,0}, + {210,210,0}, + {210,0,210}, + {210,112,0}, + {0,210,210}, + {210,210,210}, + {0,0,210}, + {0,210,0}, + {210,0,0}, + {210,210,0}, + {210,0,210}, + {210,112,0}, + {0,195,195}, + {195,195,195}, + {0,0,195}, + {0,195,0}, + {195,0,0}, + {195,195,0}, + {195,0,195}, + {195,107,0}, + {0,195,195}, + {195,195,195}, + {0,0,195}, + {0,195,0}, + {195,0,0}, + {195,195,0}, + {195,0,195}, + {195,107,0}, + {0,180,180}, + {180,180,180}, + {0,0,180}, + {0,180,0}, + {180,0,0}, + {180,180,0}, + {180,0,180}, + {180,102,0}, + {0,180,180}, + {180,180,180}, + {0,0,180}, + {0,180,0}, + {180,0,0}, + {180,180,0}, + {180,0,180}, + {180,102,0}, + {0,165,165}, + {165,165,165}, + {0,0,165}, + {0,165,0}, + {165,0,0}, + {165,165,0}, + {165,0,165}, + {165,97,0}, + {0,165,165}, + {165,165,165}, + {0,0,165}, + {0,165,0}, + {165,0,0}, + {165,165,0}, + {165,0,165}, + {165,97,0}, + {0,150,150}, + {150,150,150}, + {0,0,150}, + {0,150,0}, + {150,0,0}, + {150,150,0}, + {150,0,150}, + {150,92,0}, + {0,150,150}, + {150,150,150}, + {0,0,150}, + {0,150,0}, + {150,0,0}, + {150,150,0}, + {150,0,150}, + {150,92,0}, + {0,135,135}, + {135,135,135}, + {0,0,135}, + {0,135,0}, + {135,0,0}, + {135,135,0}, + {135,0,135}, + {135,87,0}, + {0,135,135}, + {135,135,135}, + {0,0,135}, + {0,135,0}, + {135,0,0}, + {135,135,0}, + {135,0,135}, + {135,87,0}, + {0,120,120}, + {120,120,120}, + {0,0,120}, + {0,120,0}, + {120,0,0}, + {120,120,0}, + {120,0,120}, + {120,82,0}, + {0,120,120}, + {120,120,120}, + {0,0,120}, + {0,120,0}, + {120,0,0}, + {120,120,0}, + {120,0,120}, + {120,82,0}, + {0,105,105}, + {105,105,105}, + {0,0,105}, + {0,105,0}, + {105,0,0}, + {105,105,0}, + {105,0,105}, + {105,77,0}, + {0,105,105}, + {105,105,105}, + {0,0,105}, + {0,105,0}, + {105,0,0}, + {105,105,0}, + {105,0,105}, + {105,77,0}, + {0,90,90}, + {90,90,90}, + {0,0,90}, + {0,90,0}, + {90,0,0}, + {90,90,0}, + {90,0,90}, + {90,72,0}, + {0,90,90}, + {90,90,90}, + {0,0,90}, + {0,90,0}, + {90,0,0}, + {90,90,0}, + {90,0,90}, + {90,72,0}, + {0,75,75}, + {75,75,75}, + {0,0,75}, + {0,75,0}, + {75,0,0}, + {75,75,0}, + {75,0,75}, + {75,67,0}, + {0,75,75}, + {75,75,75}, + {0,0,75}, + {0,75,0}, + {75,0,0}, + {75,75,0}, + {75,0,75}, + {75,67,0}, + {0,60,60}, + {60,60,60}, + {0,0,60}, + {0,60,0}, + {60,0,0}, + {60,60,0}, + {60,0,60}, + {60,62,0}, + {0,60,60}, + {60,60,60}, + {0,0,60}, + {0,60,0}, + {60,0,0}, + {60,60,0}, + {60,0,60}, + {60,62,0}, + {0,45,45}, + {45,45,45}, + {0,0,45}, + {0,45,0}, + {45,0,0}, + {45,45,0}, + {45,0,45}, + {45,57,0}, + {0,45,45}, + {45,45,45}, + {0,0,45}, + {0,45,0}, + {45,0,0}, + {45,45,0}, + {45,0,45}, + {45,57,0}, + {0,30,30}, + {30,30,30}, + {0,0,30}, + {0,30,0}, + {30,0,0}, + {30,30,0}, + {30,0,30}, + {30,52,0}, + {0,30,30}, + {30,30,30}, + {0,0,30}, + {0,30,0}, + {30,0,0}, + {30,30,0}, + {30,0,30}, + {192,192,192}, + {28,0,100} +}; + + +/************************************************************************/ +/* DGNLookupColor() */ +/************************************************************************/ + +/** + * Translate color index into RGB values. + * + * If no color table has yet been encountered in the file a hard-coded + * "default" color table will be used. This seems to be what Microstation + * uses as a color table when there isn't one in a DGN file but I am not + * absolutely convinced it is appropriate. + * + * @param hDGN the file. + * @param color_index the color index to lookup. + * @param red location to put red component. + * @param green location to put green component. + * @param blue location to put blue component. + * + * @return TRUE on success or FALSE on failure. May fail if color_index is + * out of range. + */ + +int DGNLookupColor( DGNHandle hDGN, int color_index, + int * red, int * green, int * blue ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + + if( color_index < 0 || color_index > 255 ) + return FALSE; + + if( !psDGN->got_color_table ) + { + *red = abyDefaultPCT[color_index][0]; + *green = abyDefaultPCT[color_index][1]; + *blue = abyDefaultPCT[color_index][2]; + } + else + { + *red = psDGN->color_table[color_index][0]; + *green = psDGN->color_table[color_index][1]; + *blue = psDGN->color_table[color_index][2]; + } + + return TRUE; +} + +/************************************************************************/ +/* DGNGetShapeFillInfo() */ +/************************************************************************/ + +/** + * Fetch fill color for a shape. + * + * This method will check for a 0x0041 user attribute linkaged with fill + * color information for the element. If found the function returns TRUE, + * and places the fill color in *pnColor, otherwise FALSE is returned and + * *pnColor is not updated. + * + * @param hDGN the file. + * @param psElem the element. + * @param pnColor the location to return the fill color. + * + * @return TRUE on success or FALSE on failure. + */ + +int DGNGetShapeFillInfo( DGNHandle hDGN, DGNElemCore *psElem, int *pnColor ) + +{ + int iLink; + + for( iLink = 0; TRUE; iLink++ ) + { + int nLinkType, nLinkSize; + unsigned char *pabyData; + + pabyData = DGNGetLinkage( hDGN, psElem, iLink, &nLinkType, + NULL, NULL, &nLinkSize ); + if( pabyData == NULL ) + return FALSE; + + if( nLinkType == DGNLT_SHAPE_FILL && nLinkSize >= 7 ) + { + *pnColor = pabyData[8]; + return TRUE; + } + } +} + +/************************************************************************/ +/* DGNGetAssocID() */ +/************************************************************************/ + +/** + * Fetch association id for an element. + * + * This method will check if an element has an association id, and if so + * returns it, otherwise returning -1. Association ids are kept as a + * user attribute linkage where present. + * + * @param hDGN the file. + * @param psElem the element. + * + * @return The id or -1 on failure. + */ + +int DGNGetAssocID( DGNHandle hDGN, DGNElemCore *psElem ) + +{ + int iLink; + + for( iLink = 0; TRUE; iLink++ ) + { + int nLinkType, nLinkSize; + unsigned char *pabyData; + + pabyData = DGNGetLinkage( hDGN, psElem, iLink, &nLinkType, + NULL, NULL, &nLinkSize ); + if( pabyData == NULL ) + return -1; + + if( nLinkType == DGNLT_ASSOC_ID && nLinkSize >= 8 ) + { + return pabyData[4] + + pabyData[5] * 256 + + pabyData[6]*256*256 + + pabyData[7] * 256*256*256; + } + } +} + +/************************************************************************/ +/* DGNRad50ToAscii() */ +/* */ +/* Convert one 16-bits Radix-50 to ASCII (3 chars). */ +/************************************************************************/ + +void DGNRad50ToAscii(unsigned short sRad50, char *str ) +{ + unsigned short sValue; + char ch = '\0'; + unsigned short saQuots[3] = {1600,40,1}; + int i; + + for ( i=0; i<3; i++) + { + sValue = sRad50; + sValue /= saQuots[i]; + /* Map 0..39 to ASCII */ + if (sValue==0) + ch = ' '; /* space */ + else if (sValue >= 1 && sValue <= 26) + ch = (char) (sValue-1+'A');/* printable alpha A..Z */ + else if (sValue == 27) + ch = '$'; /* dollar */ + else if (sValue == 28) + ch = '.'; /* period */ + else if (sValue == 29) + ch = ' '; /* unused char, emit a space instead */ + else if (sValue >= 30 && sValue <= 39) + ch = (char) (sValue-30+'0'); /* digit 0..9 */ + *str = ch; + str++; + + sRad50-=(sValue*saQuots[i]); + } + + /* Do zero-terminate */ + *str = '\0'; +} + +/************************************************************************/ +/* DGNAsciiToRad50() */ +/************************************************************************/ + +void DGNAsciiToRad50( const char *str, unsigned short *pRad50 ) + +{ + unsigned short rad50 = 0; + int i; + + for( i = 0; i < 3; i++ ) + { + unsigned short value; + + if( i >= (int) strlen(str) ) + { + rad50 = rad50 * 40; + continue; + } + + if( str[i] == '$' ) + value = 27; + else if( str[i] == '.' ) + value = 28; + else if( str[i] == ' ' ) + value = 29; + else if( str[i] >= '0' && str[i] <= '9' ) + value = str[i] - '0' + 30; + else if( str[i] >= 'a' && str[i] <= 'z' ) + value = str[i] - 'a' + 1; + else if( str[i] >= 'A' && str[i] <= 'Z' ) + value = str[i] - 'A' + 1; + else + value = 0; + + rad50 = rad50 * 40 + value; + } + + *pRad50 = rad50; +} + + +/************************************************************************/ +/* DGNGetLineStyleName() */ +/* */ +/* Read the line style name from symbol table. */ +/* The got name is stored in psLine. */ +/************************************************************************/ + +int DGNGetLineStyleName(CPL_UNUSED DGNInfo *psDGN, + DGNElemMultiPoint *psLine, + char szLineStyle[65] ) +{ + if (psLine->core.attr_bytes > 0 && + psLine->core.attr_data[1] == 0x10 && + psLine->core.attr_data[2] == 0xf9 && + psLine->core.attr_data[3] == 0x79) + { +#ifdef notdef + for (int i=0;ibuffer + 0x21e5 + i) == psLine->core.attr_data[4] && + *((unsigned char*)psDGN->buffer + 0x21e6 + i) == psLine->core.attr_data[5] && + *((unsigned char*)psDGN->buffer + 0x21e7 + i) == psLine->core.attr_data[6] && + *((unsigned char*)psDGN->buffer + 0x21e8 + i) == psLine->core.attr_data[7]) + { + memcpy( szLineStyle, + (unsigned char*)psDGN->buffer + 0x21e9 + i, 64 ); + szLineStyle[64] = '\0'; + return TRUE; + } + } +#endif + return FALSE; + } + else + { + szLineStyle[0] = '\0'; + return FALSE; + } +} + +/************************************************************************/ +/* DGNDumpElement() */ +/************************************************************************/ + +/** + * Emit textual report of an element. + * + * This function exists primarily for debugging, and will produce a textual + * report about any element type to the designated file. + * + * @param hDGN the file from which the element originated. + * @param psElement the element to report on. + * @param fp the file (such as stdout) to report the element information to. + */ + +void DGNDumpElement( DGNHandle hDGN, DGNElemCore *psElement, FILE *fp ) + +{ + DGNInfo *psInfo = (DGNInfo *) hDGN; + + fprintf( fp, "\n" ); + fprintf( fp, "Element:%-12s Level:%2d id:%-6d ", + DGNTypeToName( psElement->type ), + psElement->level, + psElement->element_id ); + + if( psElement->complex ) + fprintf( fp, "(Complex) " ); + + if( psElement->deleted ) + fprintf( fp, "(DELETED) " ); + + fprintf( fp, "\n" ); + + fprintf( fp, " offset=%d size=%d bytes\n", + psElement->offset, psElement->size ); + + fprintf( fp, + " graphic_group:%-3d color:%d weight:%d style:%d\n", + psElement->graphic_group, + psElement->color, + psElement->weight, + psElement->style ); + + if( psElement->properties != 0 ) + { + int nClass; + + fprintf( fp, " properties=%d", psElement->properties ); + if( psElement->properties & DGNPF_HOLE ) + fprintf( fp, ",HOLE" ); + if( psElement->properties & DGNPF_SNAPPABLE ) + fprintf( fp, ",SNAPPABLE" ); + if( psElement->properties & DGNPF_PLANAR ) + fprintf( fp, ",PLANAR" ); + if( psElement->properties & DGNPF_ORIENTATION ) + fprintf( fp, ",ORIENTATION" ); + if( psElement->properties & DGNPF_ATTRIBUTES ) + fprintf( fp, ",ATTRIBUTES" ); + if( psElement->properties & DGNPF_MODIFIED ) + fprintf( fp, ",MODIFIED" ); + if( psElement->properties & DGNPF_NEW ) + fprintf( fp, ",NEW" ); + if( psElement->properties & DGNPF_LOCKED ) + fprintf( fp, ",LOCKED" ); + + nClass = psElement->properties & DGNPF_CLASS; + if( nClass == DGNC_PATTERN_COMPONENT ) + fprintf( fp, ",PATTERN_COMPONENT" ); + else if( nClass == DGNC_CONSTRUCTION_ELEMENT ) + fprintf( fp, ",CONSTRUCTION ELEMENT" ); + else if( nClass == DGNC_DIMENSION_ELEMENT ) + fprintf( fp, ",DIMENSION ELEMENT" ); + else if( nClass == DGNC_PRIMARY_RULE_ELEMENT ) + fprintf( fp, ",PRIMARY RULE ELEMENT" ); + else if( nClass == DGNC_LINEAR_PATTERNED_ELEMENT ) + fprintf( fp, ",LINEAR PATTERNED ELEMENT" ); + else if( nClass == DGNC_CONSTRUCTION_RULE_ELEMENT ) + fprintf( fp, ",CONSTRUCTION_RULE_ELEMENT" ); + + fprintf( fp, "\n" ); + } + + switch( psElement->stype ) + { + case DGNST_MULTIPOINT: + { + DGNElemMultiPoint *psLine = (DGNElemMultiPoint *) psElement; + int i; + + for( i=0; i < psLine->num_vertices; i++ ) + fprintf( fp, " (%.6f,%.6f,%.6f)\n", + psLine->vertices[i].x, + psLine->vertices[i].y, + psLine->vertices[i].z ); + } + break; + + case DGNST_CELL_HEADER: + { + DGNElemCellHeader *psCell = (DGNElemCellHeader*) psElement; + + fprintf( fp, " totlength=%d, name=%s, class=%x, levels=%02x%02x%02x%02x\n", + psCell->totlength, psCell->name, psCell->cclass, + psCell->levels[0], psCell->levels[1], psCell->levels[2], + psCell->levels[3] ); + fprintf( fp, " rnglow=(%.5f,%.5f,%.5f)\n" + " rnghigh=(%.5f,%.5f,%.5f)\n", + psCell->rnglow.x, psCell->rnglow.y, psCell->rnglow.z, + psCell->rnghigh.x, psCell->rnghigh.y, psCell->rnghigh.z ); + fprintf( fp, " origin=(%.5f,%.5f,%.5f)\n", + psCell->origin.x, psCell->origin.y, psCell->origin.z); + + if( psInfo->dimension == 2 ) + fprintf( fp, " xscale=%g, yscale=%g, rotation=%g\n", + psCell->xscale, psCell->yscale, psCell->rotation ); + else + fprintf( fp, " trans=%g,%g,%g,%g,%g,%g,%g,%g,%g\n", + psCell->trans[0], + psCell->trans[1], + psCell->trans[2], + psCell->trans[3], + psCell->trans[4], + psCell->trans[5], + psCell->trans[6], + psCell->trans[7], + psCell->trans[8] ); + } + break; + + case DGNST_CELL_LIBRARY: + { + DGNElemCellLibrary *psCell = (DGNElemCellLibrary*) psElement; + + fprintf( fp, + " name=%s, class=%x, levels=%02x%02x%02x%02x, numwords=%d\n", + psCell->name, psCell->cclass, + psCell->levels[0], psCell->levels[1], psCell->levels[2], + psCell->levels[3], psCell->numwords ); + fprintf( fp, " dispsymb=%d, description=%s\n", + psCell->dispsymb, psCell->description ); + } + break; + + case DGNST_SHARED_CELL_DEFN: + { + DGNElemSharedCellDefn *psShared = (DGNElemSharedCellDefn *) psElement; + + fprintf( fp, " totlength=%d\n", psShared->totlength); + } + break; + + case DGNST_ARC: + { + DGNElemArc *psArc = (DGNElemArc *) psElement; + + if( psInfo->dimension == 2 ) + fprintf( fp, " origin=(%.5f,%.5f), rotation=%f\n", + psArc->origin.x, + psArc->origin.y, + psArc->rotation ); + else + fprintf( fp, " origin=(%.5f,%.5f,%.5f), quat=%d,%d,%d,%d\n", + psArc->origin.x, + psArc->origin.y, + psArc->origin.z, + psArc->quat[0], + psArc->quat[1], + psArc->quat[2], + psArc->quat[3] ); + fprintf( fp, " axes=(%.5f,%.5f), start angle=%f, sweep=%f\n", + psArc->primary_axis, + psArc->secondary_axis, + psArc->startang, + psArc->sweepang ); + } + break; + + case DGNST_TEXT: + { + DGNElemText *psText = (DGNElemText *) psElement; + + fprintf( fp, + " origin=(%.5f,%.5f), rotation=%f\n" + " font=%d, just=%d, length_mult=%g, height_mult=%g\n" + " string = \"%s\"\n", + psText->origin.x, + psText->origin.y, + psText->rotation, + psText->font_id, + psText->justification, + psText->length_mult, + psText->height_mult, + psText->string ); + } + break; + + case DGNST_TEXT_NODE: + { + DGNElemTextNode *psNode = (DGNElemTextNode *) psElement; + + fprintf( fp, + " totlength=%d, num_texts=%d\n", + psNode->totlength, + psNode->numelems ); + fprintf( fp, + " origin=(%.5f,%.5f), rotation=%f\n" + " font=%d, just=%d, length_mult=%g, height_mult=%g\n", + psNode->origin.x, + psNode->origin.y, + psNode->rotation, + psNode->font_id, + psNode->justification, + psNode->length_mult, + psNode->height_mult ); + fprintf( fp, + " max_length=%d, used=%d,", + psNode->max_length, + psNode->max_used ); + fprintf( fp, + " node_number=%d\n", + psNode->node_number ); + } + break; + + case DGNST_COMPLEX_HEADER: + { + DGNElemComplexHeader *psHdr = (DGNElemComplexHeader *) psElement; + + fprintf( fp, + " totlength=%d, numelems=%d\n", + psHdr->totlength, + psHdr->numelems ); + if (psElement->type == DGNT_3DSOLID_HEADER || + psElement->type == DGNT_3DSURFACE_HEADER) { + fprintf( fp, + " surftype=%d, boundelms=%d\n", + psHdr->surftype, psHdr->boundelms ); + } + } + break; + + case DGNST_COLORTABLE: + { + DGNElemColorTable *psCT = (DGNElemColorTable *) psElement; + int i; + + fprintf( fp, " screen_flag: %d\n", psCT->screen_flag ); + for( i = 0; i < 256; i++ ) + { + fprintf( fp, " %3d: (%3d,%3d,%3d)\n", + i, + psCT->color_info[i][0], + psCT->color_info[i][1], + psCT->color_info[i][2] ); + } + } + break; + + case DGNST_TCB: + { + DGNElemTCB *psTCB = (DGNElemTCB *) psElement; + int iView; + + fprintf( fp, " dimension = %d\n", psTCB->dimension ); + fprintf( fp, " uor_per_subunit = %ld, subunits = `%s'\n", + psTCB->uor_per_subunit, psTCB->sub_units ); + fprintf( fp, " subunits_per_master = %ld, master units = `%s'\n", + psTCB->subunits_per_master, psTCB->master_units ); + fprintf( fp, " origin = (%.5f,%.5f,%.5f)\n", + psTCB->origin_x, + psTCB->origin_y, + psTCB->origin_z ); + + for( iView = 0; iView < 8; iView++ ) + { + DGNViewInfo *psView = psTCB->views + iView; + + fprintf(fp, + " View%d: flags=%04X, levels=%02X%02X%02X%02X%02X%02X%02X%02X\n", + iView, + psView->flags, + psView->levels[0], + psView->levels[1], + psView->levels[2], + psView->levels[3], + psView->levels[4], + psView->levels[5], + psView->levels[6], + psView->levels[7] ); + fprintf(fp, + " origin=(%g,%g,%g)\n delta=(%g,%g,%g)\n", + psView->origin.x, psView->origin.y, psView->origin.z, + psView->delta.x, psView->delta.y, psView->delta.z ); + fprintf(fp, + " trans=(%g,%g,%g,%g,%g,%g,%g,%g,%g)\n", + psView->transmatrx[0], + psView->transmatrx[1], + psView->transmatrx[2], + psView->transmatrx[3], + psView->transmatrx[4], + psView->transmatrx[5], + psView->transmatrx[6], + psView->transmatrx[7], + psView->transmatrx[8] ); + } + } + break; + + case DGNST_TAG_SET: + { + DGNElemTagSet *psTagSet = (DGNElemTagSet*) psElement; + int iTag; + + fprintf( fp, " tagSetName=%s, tagSet=%d, tagCount=%d, flags=%d\n", + psTagSet->tagSetName, psTagSet->tagSet, + psTagSet->tagCount, psTagSet->flags ); + for( iTag = 0; iTag < psTagSet->tagCount; iTag++ ) + { + DGNTagDef *psTagDef = psTagSet->tagList + iTag; + + fprintf( fp, " %d: name=%s, type=%d, prompt=%s", + psTagDef->id, psTagDef->name, psTagDef->type, + psTagDef->prompt ); + if( psTagDef->type == 1 ) + fprintf( fp, ", default=%s\n", + psTagDef->defaultValue.string ); + else if( psTagDef->type == 3 || psTagDef->type == 5 ) + fprintf( fp, ", default=%d\n", + psTagDef->defaultValue.integer ); + else if( psTagDef->type == 4 ) + fprintf( fp, ", default=%g\n", + psTagDef->defaultValue.real ); + else + fprintf( fp, ", default=\n" ); + } + } + break; + + case DGNST_TAG_VALUE: + { + DGNElemTagValue *psTag = (DGNElemTagValue*) psElement; + + fprintf( fp, " tagType=%d, tagSet=%d, tagIndex=%d, tagLength=%d\n", + psTag->tagType, psTag->tagSet, psTag->tagIndex, + psTag->tagLength ); + if( psTag->tagType == 1 ) + fprintf( fp, " value=%s\n", psTag->tagValue.string ); + else if( psTag->tagType == 3 ) + fprintf( fp, " value=%d\n", psTag->tagValue.integer ); + else if( psTag->tagType == 4 ) + fprintf( fp, " value=%g\n", psTag->tagValue.real ); + } + break; + + case DGNST_CONE: + { + DGNElemCone *psCone = (DGNElemCone *) psElement; + + fprintf( fp, + " center_1=(%g,%g,%g) radius=%g\n" + " center_2=(%g,%g,%g) radius=%g\n" + " quat=%d,%d,%d,%d unknown=%d\n", + psCone->center_1.x, psCone->center_1.y, psCone->center_1.z, + psCone->radius_1, + psCone->center_2.x, psCone->center_2.y, psCone->center_2.z, + psCone->radius_2, + psCone->quat[0], psCone->quat[1], + psCone->quat[2], psCone->quat[3], + psCone->unknown ); + } + break; + + case DGNST_BSPLINE_SURFACE_HEADER: + { + DGNElemBSplineSurfaceHeader *psSpline = + (DGNElemBSplineSurfaceHeader *) psElement; + + fprintf( fp, " desc_words=%ld, curve type=%d\n", + psSpline->desc_words, psSpline->curve_type); + + fprintf( fp, " U: properties=%02x", + psSpline->u_properties); + if (psSpline->u_properties != 0) { + if (psSpline->u_properties & DGNBSC_CURVE_DISPLAY) { + fprintf(fp, ",CURVE_DISPLAY"); + } + if (psSpline->u_properties & DGNBSC_POLY_DISPLAY) { + fprintf(fp, ",POLY_DISPLAY"); + } + if (psSpline->u_properties & DGNBSC_RATIONAL) { + fprintf(fp, ",RATIONAL"); + } + if (psSpline->u_properties & DGNBSC_CLOSED) { + fprintf(fp, ",CLOSED"); + } + } + fprintf(fp, "\n"); + fprintf( fp, " order=%d\n %d poles, %d knots, %d rule lines\n", + psSpline->u_order, psSpline->num_poles_u, + psSpline->num_knots_u, psSpline->rule_lines_u); + + fprintf( fp, " V: properties=%02x", + psSpline->v_properties); + if (psSpline->v_properties != 0) { + if (psSpline->v_properties & DGNBSS_ARC_SPACING) { + fprintf(fp, ",ARC_SPACING"); + } + if (psSpline->v_properties & DGNBSS_CLOSED) { + fprintf(fp, ",CLOSED"); + } + } + fprintf(fp, "\n"); + fprintf( fp, " order=%d\n %d poles, %d knots, %d rule lines\n", + psSpline->v_order, psSpline->num_poles_v, + psSpline->num_knots_v, psSpline->rule_lines_v); + } + break; + + case DGNST_BSPLINE_CURVE_HEADER: + { + DGNElemBSplineCurveHeader *psSpline = + (DGNElemBSplineCurveHeader *) psElement; + + fprintf( fp, + " desc_words=%ld, curve type=%d\n" + " properties=%02x", + psSpline->desc_words, psSpline->curve_type, + psSpline->properties); + if (psSpline->properties != 0) { + if (psSpline->properties & DGNBSC_CURVE_DISPLAY) { + fprintf(fp, ",CURVE_DISPLAY"); + } + if (psSpline->properties & DGNBSC_POLY_DISPLAY) { + fprintf(fp, ",POLY_DISPLAY"); + } + if (psSpline->properties & DGNBSC_RATIONAL) { + fprintf(fp, ",RATIONAL"); + } + if (psSpline->properties & DGNBSC_CLOSED) { + fprintf(fp, ",CLOSED"); + } + } + fprintf(fp, "\n"); + fprintf( fp, " order=%d\n %d poles, %d knots\n", + psSpline->order, psSpline->num_poles, psSpline->num_knots); + } + break; + + case DGNST_BSPLINE_SURFACE_BOUNDARY: + { + DGNElemBSplineSurfaceBoundary *psBounds = + (DGNElemBSplineSurfaceBoundary *) psElement; + + fprintf( fp, " boundary number=%d, # vertices=%d\n", + psBounds->number, psBounds->numverts); + for (int i=0;inumverts;i++) { + fprintf( fp, " (%.6f,%.6f)\n", + psBounds->vertices[i].x, + psBounds->vertices[i].y); + } + } + break; + + case DGNST_KNOT_WEIGHT: + { + DGNElemKnotWeight *psArray = (DGNElemKnotWeight *) psElement; + int numelems = (psArray->core.size-36)/4; + for (int i=0;iarray[i]); + } + } + break; + + default: + break; + } + + if( psElement->attr_bytes > 0 ) + { + int iLink; + + fprintf( fp, "Attributes (%d bytes):\n", psElement->attr_bytes ); + + for( iLink = 0; TRUE; iLink++ ) + + { + int nLinkType, nEntityNum=0, nMSLink=0, nLinkSize, i; + unsigned char *pabyData; + + pabyData = DGNGetLinkage( hDGN, psElement, iLink, &nLinkType, + &nEntityNum, &nMSLink, &nLinkSize ); + if( pabyData == NULL ) + break; + + fprintf( fp, "Type=0x%04x", nLinkType ); + if( nMSLink != 0 || nEntityNum != 0 ) + fprintf( fp, ", EntityNum=%d, MSLink=%d", + nEntityNum, nMSLink ); + + int nBytes = psElement->attr_data + psElement->attr_bytes - pabyData; + if( nBytes < nLinkSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Corrupt linkage, element id:%d, link:%d", + psElement->element_id, iLink); + fprintf(fp, " (Corrupt, declared size: %d, assuming size: %d)", + nLinkSize, nBytes); + nLinkSize = nBytes; + } + fprintf( fp, "\n 0x" ); + + for( i = 0; i < nLinkSize; i++ ) + fprintf( fp, "%02x", pabyData[i] ); + fprintf( fp, "\n" ); + + } + } +} + + +/************************************************************************/ +/* DGNTypeToName() */ +/************************************************************************/ + +/** + * Convert type to name. + * + * Returns a human readable name for an element type such as DGNT_LINE. + * + * @param nType the DGNT_* type code to translate. + * + * @return a pointer to an internal string with the translation. This string + * should not be modified or freed. + */ + +const char *DGNTypeToName( int nType ) + +{ + static char szNumericResult[16]; + + switch( nType ) + { + case DGNT_CELL_LIBRARY: + return "Cell Library"; + + case DGNT_CELL_HEADER: + return "Cell Header"; + + case DGNT_LINE: + return "Line"; + + case DGNT_LINE_STRING: + return "Line String"; + + case DGNT_POINT_STRING: + return "Point String"; + + case DGNT_GROUP_DATA: + return "Group Data"; + + case DGNT_SHAPE: + return "Shape"; + + case DGNT_TEXT_NODE: + return "Text Node"; + + case DGNT_DIGITIZER_SETUP: + return "Digitizer Setup"; + + case DGNT_TCB: + return "TCB"; + + case DGNT_LEVEL_SYMBOLOGY: + return "Level Symbology"; + + case DGNT_CURVE: + return "Curve"; + + case DGNT_COMPLEX_CHAIN_HEADER: + return "Complex Chain Header"; + + case DGNT_COMPLEX_SHAPE_HEADER: + return "Complex Shape Header"; + + case DGNT_ELLIPSE: + return "Ellipse"; + + case DGNT_ARC: + return "Arc"; + + case DGNT_TEXT: + return "Text"; + + case DGNT_BSPLINE_POLE: + return "B-Spline Pole"; + + case DGNT_BSPLINE_SURFACE_HEADER: + return "B-Spline Surface Header"; + + case DGNT_BSPLINE_SURFACE_BOUNDARY: + return "B-Spline Surface Boundary"; + + case DGNT_BSPLINE_KNOT: + return "B-Spline Knot"; + + case DGNT_BSPLINE_CURVE_HEADER: + return "B-Spline Curve Header"; + + case DGNT_BSPLINE_WEIGHT_FACTOR: + return "B-Spline Weight Factor"; + + case DGNT_APPLICATION_ELEM: + return "Application Element"; + + case DGNT_SHARED_CELL_DEFN: + return "Shared Cell Definition"; + + case DGNT_SHARED_CELL_ELEM: + return "Shared Cell Element"; + + case DGNT_TAG_VALUE: + return "Tag Value"; + + case DGNT_CONE: + return "Cone"; + + case DGNT_3DSURFACE_HEADER: + return "3D Surface Header"; + + case DGNT_3DSOLID_HEADER: + return "3D Solid Header"; + + default: + sprintf( szNumericResult, "%d", nType ); + return szNumericResult; + } +} + +/************************************************************************/ +/* DGNGetAttrLinkSize() */ +/************************************************************************/ + +/** + * Get attribute linkage size. + * + * Returns the size, in bytes, of the attribute linkage starting at byte + * offset nOffset. On failure a value of 0 is returned. + * + * @param hDGN the file from which the element originated. + * @param psElement the element to report on. + * @param nOffset byte offset within attribute data of linkage to check. + * + * @return size of linkage in bytes, or zero. + */ + +int DGNGetAttrLinkSize( CPL_UNUSED DGNHandle hDGN, + DGNElemCore *psElement, + int nOffset ) +{ + if( psElement->attr_bytes < nOffset + 4 ) + return 0; + + /* DMRS Linkage */ + if( (psElement->attr_data[nOffset+0] == 0 + && psElement->attr_data[nOffset+1] == 0) + || (psElement->attr_data[nOffset+0] == 0 + && psElement->attr_data[nOffset+1] == 0x80) ) + return 8; + + /* If low order bit of second byte is set, first byte is length */ + if( psElement->attr_data[nOffset+1] & 0x10 ) + return psElement->attr_data[nOffset+0] * 2 + 2; + + /* unknown */ + return 0; +} + +/************************************************************************/ +/* DGNGetLinkage() */ +/************************************************************************/ + +/** + * Returns requested linkage raw data. + * + * A pointer to the raw data for the requested attribute linkage is returned + * as well as (potentially) various information about the linkage including + * the linkage type, database entity number and MSLink value, and the length + * of the raw linkage data in bytes. + * + * If the requested linkage (iIndex) does not exist a value of zero is + * returned. + * + * The entity number is (loosely speaking) the index of the table within + * the current database to which the MSLINK value will refer. The entity + * number should be used to lookup the table name in the MSCATALOG table. + * The MSLINK value is the key value for the record in the target table. + * + * @param hDGN the file from which the element originated. + * @param psElement the element to report on. + * @param iIndex the zero based index of the linkage to fetch. + * @param pnLinkageType variable to return linkage type. This may be one of + * the predefined DGNLT_ values or a different value. This pointer may be NULL. + * @param pnEntityNum variable to return the entity number in or NULL if not + * required. + * @param pnMSLink variable to return the MSLINK value in, or NULL if not + * required. + * @param pnLength variable to returned the linkage size in bytes or NULL. + * + * @return pointer to raw internal linkage data. This data should not be + * altered or freed. NULL returned on failure. + */ + +unsigned char *DGNGetLinkage( DGNHandle hDGN, DGNElemCore *psElement, + int iIndex, int *pnLinkageType, + int *pnEntityNum, int *pnMSLink, int *pnLength ) + +{ + int nAttrOffset; + int iLinkage, nLinkSize; + + for( iLinkage=0, nAttrOffset=0; + (nLinkSize = DGNGetAttrLinkSize( hDGN, psElement, nAttrOffset)) != 0; + iLinkage++, nAttrOffset += nLinkSize ) + { + if( iLinkage == iIndex ) + { + int nLinkageType=0, nEntityNum=0, nMSLink = 0; + CPLAssert( nLinkSize > 4 ); + + if( psElement->attr_data[nAttrOffset+0] == 0x00 + && (psElement->attr_data[nAttrOffset+1] == 0x00 + || psElement->attr_data[nAttrOffset+1] == 0x80) ) + { + nLinkageType = DGNLT_DMRS; + nEntityNum = psElement->attr_data[nAttrOffset+2] + + psElement->attr_data[nAttrOffset+3] * 256; + nMSLink = psElement->attr_data[nAttrOffset+4] + + psElement->attr_data[nAttrOffset+5] * 256 + + psElement->attr_data[nAttrOffset+6] * 65536; + } + else + nLinkageType = psElement->attr_data[nAttrOffset+2] + + psElement->attr_data[nAttrOffset+3] * 256; + + // Possibly an external database linkage? + if( nLinkSize == 16 && nLinkageType != DGNLT_SHAPE_FILL ) + { + nEntityNum = psElement->attr_data[nAttrOffset+6] + + psElement->attr_data[nAttrOffset+7] * 256; + nMSLink = psElement->attr_data[nAttrOffset+8] + + psElement->attr_data[nAttrOffset+9] * 256 + + psElement->attr_data[nAttrOffset+10] * 65536 + + psElement->attr_data[nAttrOffset+11] * 65536 * 256; + + } + + if( pnLinkageType != NULL ) + *pnLinkageType = nLinkageType; + if( pnEntityNum != NULL ) + *pnEntityNum = nEntityNum; + if( pnMSLink != NULL ) + *pnMSLink = nMSLink; + if( pnLength != NULL ) + *pnLength = nLinkSize; + + return psElement->attr_data + nAttrOffset; + } + } + + return NULL; +} + +/************************************************************************/ +/* DGNRotationToQuat() */ +/* */ +/* Compute a quaternion for a given Z rotation. */ +/************************************************************************/ + +void DGNRotationToQuaternion( double dfRotation, int *panQuaternion ) + +{ + double dfRadianRot = (dfRotation / 180.0) * PI; + + panQuaternion[0] = (int) (cos(-dfRadianRot/2.0) * 2147483647); + panQuaternion[1] = 0; + panQuaternion[2] = 0; + panQuaternion[3] = (int) (sin(-dfRadianRot/2.0) * 2147483647); +} + +/************************************************************************/ +/* DGNQuaternionToMatrix() */ +/* */ +/* Compute a rotation matrix for a given quaternion */ +/* FIXME: Write documentation on how to use this matrix */ +/* (i.e. things like row/column major, OpenGL style or not) */ +/* kintel 20030819 */ +/************************************************************************/ + +void DGNQuaternionToMatrix( int *quat, float *mat ) +{ + double q[4]; + + q[0] = 1.0 * quat[1] / (1<<31); + q[1] = 1.0 * quat[2] / (1<<31); + q[2] = 1.0 * quat[3] / (1<<31); + q[3] = 1.0 * quat[0] / (1<<31); + + mat[0*3+0] = (float) (q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3]); + mat[0*3+1] = (float) (2 * (q[2]*q[3] + q[0]*q[1])); + mat[0*3+2] = (float) (2 * (q[0]*q[2] - q[1]*q[3])); + mat[1*3+0] = (float) (2 * (q[0]*q[1] - q[2]*q[3])); + mat[1*3+1] = (float) (-q[0]*q[0] + q[1]*q[1] - q[2]*q[2] + q[3]*q[3]); + mat[1*3+2] = (float) (2 * (q[0]*q[3] + q[1]*q[2])); + mat[2*3+0] = (float) (2 * (q[0]*q[2] + q[1]*q[3])); + mat[2*3+1] = (float) (2 * (q[1]*q[2] - q[0]*q[3])); + mat[2*3+2] = (float) (-q[0]*q[0] - q[1]*q[1] + q[2]*q[2] + q[3]*q[3]); +} + +/************************************************************************/ +/* DGNTransformPointWithQuaternion() */ +/************************************************************************/ + +void DGNTransformPointWithQuaternionVertex( CPL_UNUSED int *quat, + CPL_UNUSED DGNPoint *v1, + CPL_UNUSED DGNPoint *v2 ) +{ +/* ==================================================================== */ +/* Original code provided by kintel 20030819, but assumed to be */ +/* incomplete. */ +/* ==================================================================== */ + +#ifdef notdef + See below for sketched implementation. kintel 20030819. + float x,y,z,w; + // FIXME: Convert quat to x,y,z,w + v2.x = w*w*v1.x + 2*y*w*v1.z - 2*z*w*v1.y + x*x*v1.x + 2*y*x*v1.y + 2*z*x*v1.z - z*z*v1.x - y*y*v1.x; + v2.y = 2*x*y*v1.x + y*y*v1.y + 2*z*y*v1.z + 2*w*z*v1.x - z*z*v1.y + w*w*v1.y - 2*x*w*v1.z - x*x*v1.y; + v2.z = 2*x*z*v1.x + 2*y*z*v1.y + z*z*v1.z - 2*w*y*v1.x - y*y*v1.z + 2*w*x*v1.y - x*x*v1.z + w*w*v1.z; +#endif + +/* ==================================================================== */ +/* Impelementation provided by Peggy Jung - 2004/03/05. */ +/* peggy.jung at moskito-gis dot de. I haven't tested it. */ +/* ==================================================================== */ + +/* Version: 0.1 Datum: 26.01.2004 + +IN: +x,y,z // DGNPoint &v1 +quat[] // + +OUT: +newX, newY, newZ // DGNPoint &v2 + +A u t o r : Peggy Jung +*/ +/* + double ROT[12]; //rotation matrix for a given quaternion + double xx, xy, xz, xw, yy, yz, yw, zz, zw; + double a, b, c, d, n, x, y, z; + + x = v1->x; + y = v1->y; + z = v1->z; + + n = sqrt((double)PDP2PC_long(quat[0])*(double)PDP2PC_long(quat[0])+(double)PDP2PC_long(quat[1])*(double)PDP2PC_long(quat[1])+ + (double)PDP2PC_long(quat[2])*(double)PDP2PC_long(quat[2])+(double)PDP2PC_long(quat[3])*(double)PDP2PC_long(quat[3])); + + a = (double)PDP2PC_long(quat[0])/n; //w + b = (double)PDP2PC_long(quat[1])/n; //x + c = (double)PDP2PC_long(quat[2])/n; //y + d = (double)PDP2PC_long(quat[3])/n; //z + + xx = b*b; + xy = b*c; + xz = b*d; + xw = b*a; + + yy = c*c; + yz = c*d; + yw = c*a; + + zz = d*d; + zw = d+a; + + ROT[0] = 1 - 2 * yy - 2 * zz ; + ROT[1] = 2 * xy - 2 * zw ; + ROT[2] = 2 * xz + 2 * yw ; + + ROT[4] = 2 * xy + 2 * zw ; + ROT[5] = 1 - 2 * xx - 2 * zz ; + ROT[6] = 2 * yz - 2 * xw ; + + ROT[8] = 2 * xz - 2 * yw ; + ROT[9] = 2 * yz + 2 * xw ; + ROT[10] = 1 - 2 * xx - 2 * yy ; + + v2->x = ROT[0]*x + ROT[1]*y + ROT[2]*z; + v2->y = ROT[4]*x + ROT[5]*y + ROT[6]*z; + v2->z = ROT[8]*x + ROT[9]*y + ROT[10]*z; +*/ +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnlib.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnlib.h new file mode 100644 index 000000000..1265839e4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnlib.h @@ -0,0 +1,884 @@ +/****************************************************************************** + * $Id: dgnlib.h 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: Microstation DGN Access Library + * Purpose: Definitions of public structures and API of DGN Library. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Avenza Systems Inc, http://www.avenza.com/ + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _DGNLIB_H_INCLUDED +#define _DGNLIB_H_INCLUDED + +#include "cpl_conv.h" + +CPL_C_START + +#define CPLE_DGN_ERROR_BASE +#define CPLE_ElementTooBig CPLE_DGN_ERROR_BASE+1 + +/** + * \file dgnlib.h + * + * Definitions of public structures and API of DGN Library. + */ + +/** + * DGN Point structure. + * + * Note that the DGNReadElement() function transforms points into "master" + * coordinate system space when they are in the file in UOR (units of + * resolution) coordinates. + */ + +typedef struct { + double x; /*!< x (normally eastwards) coordinate. */ + double y; /*!< y (normally northwards) coordinate. */ + double z; /*!< z, up coordinate. Zero for 2D objects. */ +} DGNPoint; + +/** + * Element summary information. + * + * Minimal information kept about each element if an element summary + * index is built for a file by DGNGetElementIndex(). + */ +typedef struct { + unsigned char level; /*!< Element Level: 0-63 */ + unsigned char type; /*!< Element type (DGNT_*) */ + unsigned char stype; /*!< Structure type (DGNST_*) */ + unsigned char flags; /*!< Other flags */ + long offset; /*!< Offset within file (private) */ +} DGNElementInfo; + +/** + * Core element structure. + * + * Core information kept about each element that can be read from a DGN + * file. This structure is the first component of each specific element + * structure (like DGNElemMultiPoint). Normally the DGNElemCore.stype + * field would be used to decide what specific structure type to case the + * DGNElemCore pointer to. + * + */ + +typedef struct { + int offset; + int size; + + int element_id; /*!< Element number (zero based) */ + int stype; /*!< Structure type: (DGNST_*) */ + int level; /*!< Element Level: 0-63 */ + int type; /*!< Element type (DGNT_) */ + int complex; /*!< Is element complex? */ + int deleted; /*!< Is element deleted? */ + + int graphic_group; /*!< Graphic group number */ + int properties; /*!< Properties: ORing of DGNPF_ flags */ + int color; /*!< Color index (0-255) */ + int weight; /*!< Line Weight (0-31) */ + int style; /*!< Line Style: One of DGNS_* values */ + + int attr_bytes; /*!< Bytes of attribute data, usually zero. */ + unsigned char *attr_data; /*!< Raw attribute data */ + + int raw_bytes; /*!< Bytes of raw data, usually zero. */ + unsigned char *raw_data; /*!< All raw element data including header. */ +} DGNElemCore; + +/** + * Multipoint element + * + * The core.stype code is DGNST_MULTIPOINT. + * + * Used for: DGNT_LINE(3), DGNT_LINE_STRING(4), DGNT_SHAPE(6), DGNT_CURVE(11), + * DGNT_BSPLINE_POLE(21) + */ + +typedef struct { + DGNElemCore core; + + int num_vertices; /*!< Number of vertices in "vertices" */ + DGNPoint vertices[2]; /*!< Array of two or more vertices */ + +} DGNElemMultiPoint; + +/** + * Ellipse element + * + * The core.stype code is DGNST_ARC. + * + * Used for: DGNT_ELLIPSE(15), DGNT_ARC(16) + */ + +typedef struct { + DGNElemCore core; + + DGNPoint origin; /*!< Origin of ellipse */ + + double primary_axis; /*!< Primary axis length */ + double secondary_axis; /*!< Secondary axis length */ + + double rotation; /*!< Counterclockwise rotation in degrees */ + int quat[4]; + + double startang; /*!< Start angle (degrees counterclockwise of primary axis) */ + double sweepang; /*!< Sweep angle (degrees) */ + +} DGNElemArc; + +/** + * Text element + * + * The core.stype code is DGNST_TEXT. + * + * NOTE: Currently we are not capturing the "editable fields" information. + * + * Used for: DGNT_TEXT(17). + */ + +typedef struct { + DGNElemCore core; + + int font_id; /*!< Microstation font id, no list available*/ + int justification; /*!< Justification, see DGNJ_* */ + double length_mult; /*!< Char width in master (if square) */ + double height_mult; /*!< Char height in master units */ + double rotation; /*!< Counterclockwise rotation in degrees */ + DGNPoint origin; /*!< Bottom left corner of text. */ + char string[1]; /*!< Actual text (length varies, \0 terminated*/ +} DGNElemText; + +/** + * Complex header element + * + * The core.stype code is DGNST_COMPLEX_HEADER. + * + * Used for: DGNT_COMPLEX_CHAIN_HEADER(12), DGNT_COMPLEX_SHAPE_HEADER(14), + * DGNT_3DSURFACE_HEADER(18) and DGNT_3DSOLID_HEADER(19). + * + * Compatible with DGNT_TEXT_NODE (7), see DGNAddRawAttrLink() + */ + +typedef struct { + DGNElemCore core; + + int totlength; /*!< Total length of surface in words, + excluding the first 19 words + (header + totlength field) */ + int numelems; /*!< # of elements in surface */ + int surftype; /*!< surface/solid type + (only used for 3D surface/solid). + One of DGNSUT_* or DGNSOT_*. */ + int boundelms; /*!< # of elements in each boundary + (only used for 3D surface/solid). */ +} DGNElemComplexHeader; + +/** + * Color table. + * + * The core.stype code is DGNST_COLORTABLE. + * + * Returned for DGNT_GROUP_DATA(5) elements, with a level number of + * DGN_GDL_COLOR_TABLE(1). + */ + +typedef struct { + DGNElemCore core; + + int screen_flag; + GByte color_info[256][3]; /*!< Color table, 256 colors by red (0), green(1) and blue(2) component. */ +} DGNElemColorTable; + +typedef struct { + int flags; + unsigned char levels[8]; + DGNPoint origin; + DGNPoint delta; + double transmatrx[9]; + double conversion; + unsigned long activez; +} DGNViewInfo; + +/** + * Terminal Control Block (header). + * + * The core.stype code is DGNST_TCB. + * + * Returned for DGNT_TCB(9). + * + * The first TCB in the file is used to determine the dimension (2D vs. 3D), + * and transformation from UOR (units of resolution) to subunits, and subunits + * to master units. This is handled transparently within DGNReadElement(), so + * it is not normally necessary to handle this element type at the application + * level, though it can be useful to get the sub_units, and master_units names. + */ + +typedef struct { + DGNElemCore core; + + int dimension; /*!< Dimension (2 or 3) */ + + double origin_x; /*!< X origin of UOR space in master units(?)*/ + double origin_y; /*!< Y origin of UOR space in master units(?)*/ + double origin_z; /*!< Z origin of UOR space in master units(?)*/ + + long uor_per_subunit; /*!< UOR per subunit. */ + char sub_units[3]; /*!< User name for subunits (2 chars)*/ + long subunits_per_master; /*!< Subunits per master unit. */ + char master_units[3]; /*!< User name for master units (2 chars)*/ + + DGNViewInfo views[8]; + +} DGNElemTCB; + +/** + * Cell Header. + * + * The core.stype code is DGNST_CELL_HEADER. + * + * Returned for DGNT_CELL_HEADER(2). + */ + +typedef struct { + DGNElemCore core; + + int totlength; /*!< Total length of cell in words, + excluding the first 19 words + (header + totlength field) */ + char name[7]; /*!< Cell name */ + unsigned short cclass; /*!< Class bitmap */ + unsigned short levels[4]; /*!< Levels used in cell */ + + DGNPoint rnglow; /*!< X/Y/Z minimums for cell */ + DGNPoint rnghigh; /*!< X/Y/Z maximums for cell */ + + double trans[9]; /*!< 2D/3D Transformation Matrix */ + DGNPoint origin; /*!< Cell Origin */ + + double xscale; + double yscale; + double rotation; + +} DGNElemCellHeader; + +/** + * Cell Library. + * + * The core.stype code is DGNST_CELL_LIBRARY. + * + * Returned for DGNT_CELL_LIBRARY(1). + */ + +typedef struct { + DGNElemCore core; + + short celltype; /*!< Cell type. */ + short attindx; /*!< Attribute linkage. */ + char name[7]; /*!< Cell name */ + + int numwords; /*!< Number of words in cell definition */ + + short dispsymb; /*!< Display symbol */ + unsigned short cclass; /*!< Class bitmap */ + unsigned short levels[4]; /*!< Levels used in cell */ + + char description[28]; /*!< Description */ + +} DGNElemCellLibrary; + +/** + * Shared Cell Definition. + * + * The core.stype code is DGNST_SHARED_CELL_DEFN. + * + * Returned for DGNT_SHARED_CELL_DEFN(2). + */ + +typedef struct { + DGNElemCore core; + + int totlength; /*!< Total length of cell in words, + excluding the first 19 words + (header + totlength field) */ + // FIXME: Most of the contents of this element type is currently + // unknown. Please get in touch with the authors if you have + // information about how to decode this element. +} DGNElemSharedCellDefn; + +typedef union { char *string; GInt32 integer; double real; } tagValueUnion; + +/** + * Tag Value. + * + * The core.stype code is DGNST_TAG_VALUE. + * + * Returned for DGNT_TAG_VALUE(37). + */ + +typedef struct { + DGNElemCore core; + + int tagType; /*!< Tag type indicator, DGNTT_* */ + int tagSet; /*!< Which tag set does this relate to? */ + int tagIndex; /*!< Tag index within tag set. */ + int tagLength; /*!< Length of tag information (text) */ + tagValueUnion tagValue; /*!< Textual value of tag */ + +} DGNElemTagValue; + +/** + * Tag definition. + * + * Structure holding definition of one tag within a DGNTagSet. + */ +typedef struct _DGNTagDef { + char *name; /*!< Name of this tag. */ + int id; /*!< Tag index/identifier. */ + char *prompt; /*!< User prompt when requesting value. */ + int type; /*!< Tag type (one of DGNTT_STRING(1), DGNTT_INTEGER(3) or DGNTT_FLOAT(4). */ + tagValueUnion defaultValue; /*!< Default tag value */ +} DGNTagDef; + +#define DGNTT_STRING 1 +#define DGNTT_INTEGER 3 +#define DGNTT_FLOAT 4 + +/** + * Tag Set. + * + * The core.stype code is DGNST_TAG_SET. + * + * Returned for DGNT_APPLICATION_ELEM(66), Level: 24. + */ + +typedef struct { + DGNElemCore core; + + int tagCount; /*!< Number of tags in tagList. */ + int tagSet; /*!< Tag set index. */ + int flags; /*!< Tag flags - not too much known. */ + char *tagSetName; /*!< Tag set name. */ + + DGNTagDef *tagList; /*!< List of tag definitions in this set. */ + +} DGNElemTagSet; + +/** + * Cone element + * + * The core.stype code is DGNST_CONE. + * + * Used for: DGNT_CONE(23) + */ +typedef struct { + DGNElemCore core; + + short unknown; /*!< Unknown data */ + int quat[4]; /*!< Orientation quaternion */ + DGNPoint center_1; /*!< center of first circle */ + double radius_1; /*!< radius of first circle */ + DGNPoint center_2; /*!< center of second circle */ + double radius_2; /*!< radius of second circle */ + +} DGNElemCone; + + +/** + * Text Node Header. + * + * The core.stype code is DGNST_TEXT_NODE. + * + * Used for DGNT_TEXT_NODE (7). + * First fields (up to numelems) are compatible with DGNT_COMPLEX_HEADER (7), + * \sa DGNAddRawAttrLink() + */ + +typedef struct { + DGNElemCore core; + + int totlength; /*!< Total length of the node + (bytes = totlength * 2 + 38) */ + int numelems; /*!< Number of text strings */ + int node_number; /*!< text node number */ + short max_length; /*!< maximum length allowed, characters */ + short max_used; /*!< maximum length used */ + short font_id; /*!< text font used */ + short justification; /*!< justification type, see DGNJ_ */ + long line_spacing; /*!< spacing between text strings */ + double length_mult; /*!< length multiplier */ + double height_mult; /*!< height multiplier */ + double rotation; /*!< rotation angle (2d)*/ + DGNPoint origin; /*!< Snap origin (as defined by user) */ + +} DGNElemTextNode; + + +/** + * B-Spline Surface Header element + * + * The core.stype code is DGNST_BSPLINE_SURFACE_HEADER. + * + * Used for: DGNT_BSPLINE_SURFACE_HEADER(24) + */ +typedef struct { + DGNElemCore core; + + long desc_words; /*!< Total length of B-Spline surface in + words, excluding the first 20 words + (header + desc_words field) */ + unsigned char curve_type; /*!< curve type */ + unsigned char u_order; /*!< B-spline U order: 2-15 */ + unsigned short u_properties; /*!< surface U properties: + ORing of DGNBSC_ flags */ + short num_poles_u; /*!< number of poles */ + short num_knots_u; /*!< number of knots */ + short rule_lines_u; /*!< number of rule lines */ + + unsigned char v_order; /*!< B-spline V order: 2-15 */ + unsigned short v_properties; /*!< surface V properties: + Oring of DGNBSS_ flags */ + short num_poles_v; /*!< number of poles */ + short num_knots_v; /*!< number of knots */ + short rule_lines_v; /*!< number of rule lines */ + + short num_bounds; /*!< number of boundaries */ +} DGNElemBSplineSurfaceHeader; + +/** + * B-Spline Curve Header element + * + * The core.stype code is DGNST_BSPLINE_CURVE_HEADER. + * + * Used for: DGNT_BSPLINE_CURVE_HEADER(27) + */ +typedef struct { + DGNElemCore core; + + long desc_words; /*!< Total length of B-Spline curve in words, + excluding the first 20 words + (header + desc_words field) */ + unsigned char order; /*!< B-spline order: 2-15 */ + unsigned char properties; /*!< Properties: ORing of DGNBSC_ flags */ + unsigned char curve_type; /*!< curve type */ + short num_poles; /*!< number of poles, max. 101 */ + short num_knots; /*!< number of knots */ +} DGNElemBSplineCurveHeader; + +/** + * B-Spline Surface Boundary element + * + * The core.stype code is DGNST_BSPLINE_SURFACE_BOUNDARY + * + * Used for: DGNT_BSPLINE_SURFACE_BOUNDARY(25) + */ +typedef struct { + DGNElemCore core; + + short number; /*!< boundary number */ + short numverts; /*!< number of boundary vertices */ + DGNPoint vertices[1]; /*!< Array of 1 or more 2D boundary vertices + (in UV space) */ +} DGNElemBSplineSurfaceBoundary; + +/** + * B-Spline Knot/Weight element + * + * The core.stype code is DGNST_KNOT_WEIGHT + * + * Used for: DGNT_BSPLINE_KNOT(26), DGNT_BSPLINE_WEIGHT_FACTOR(28) + */ +typedef struct { + DGNElemCore core; + + float array[1]; /*!< array (variable length). Length is + given in the corresponding B-Spline + header. */ +} DGNElemKnotWeight; + + +/* -------------------------------------------------------------------- */ +/* Structure types */ +/* -------------------------------------------------------------------- */ + +/** DGNElemCore style: Element uses DGNElemCore structure */ +#define DGNST_CORE 1 + +/** DGNElemCore style: Element uses DGNElemMultiPoint structure */ +#define DGNST_MULTIPOINT 2 + +/** DGNElemCore style: Element uses DGNElemColorTable structure */ +#define DGNST_COLORTABLE 3 + +/** DGNElemCore style: Element uses DGNElemTCB structure */ +#define DGNST_TCB 4 + +/** DGNElemCore style: Element uses DGNElemArc structure */ +#define DGNST_ARC 5 + +/** DGNElemCore style: Element uses DGNElemText structure */ +#define DGNST_TEXT 6 + +/** DGNElemCore style: Element uses DGNElemComplexHeader structure */ +#define DGNST_COMPLEX_HEADER 7 + +/** DGNElemCore style: Element uses DGNElemCellHeader structure */ +#define DGNST_CELL_HEADER 8 + +/** DGNElemCore style: Element uses DGNElemTagValue structure */ +#define DGNST_TAG_VALUE 9 + +/** DGNElemCore style: Element uses DGNElemTagSet structure */ +#define DGNST_TAG_SET 10 + +/** DGNElemCore style: Element uses DGNElemCellLibrary structure */ +#define DGNST_CELL_LIBRARY 11 + +/** DGNElemCore style: Element uses DGNElemCone structure */ +#define DGNST_CONE 12 + +/** DGNElemCore style: Element uses DGNElemTextNode structure */ +#define DGNST_TEXT_NODE 13 + +/** DGNElemCore style: Element uses DGNElemBSplineSurfaceHeader structure */ +#define DGNST_BSPLINE_SURFACE_HEADER 14 + +/** DGNElemCore style: Element uses DGNElemBSplineCurveHeader structure */ +#define DGNST_BSPLINE_CURVE_HEADER 15 + +/** DGNElemCore style: Element uses DGNElemBSplineSurfaceBoundary structure */ +#define DGNST_BSPLINE_SURFACE_BOUNDARY 16 + +/** DGNElemCore style: Element uses DGNElemKnotWeight structure */ +#define DGNST_KNOT_WEIGHT 17 + +/** DGNElemCore style: Element uses DGNElemSharedCellDefn structure */ +#define DGNST_SHARED_CELL_DEFN 18 + + +/* -------------------------------------------------------------------- */ +/* Element types */ +/* -------------------------------------------------------------------- */ +#define DGNT_CELL_LIBRARY 1 +#define DGNT_CELL_HEADER 2 +#define DGNT_LINE 3 +#define DGNT_LINE_STRING 4 +#define DGNT_GROUP_DATA 5 +#define DGNT_SHAPE 6 +#define DGNT_TEXT_NODE 7 +#define DGNT_DIGITIZER_SETUP 8 +#define DGNT_TCB 9 +#define DGNT_LEVEL_SYMBOLOGY 10 +#define DGNT_CURVE 11 +#define DGNT_COMPLEX_CHAIN_HEADER 12 +#define DGNT_COMPLEX_SHAPE_HEADER 14 +#define DGNT_ELLIPSE 15 +#define DGNT_ARC 16 +#define DGNT_TEXT 17 +#define DGNT_3DSURFACE_HEADER 18 +#define DGNT_3DSOLID_HEADER 19 +#define DGNT_BSPLINE_POLE 21 +#define DGNT_POINT_STRING 22 +#define DGNT_BSPLINE_SURFACE_HEADER 24 +#define DGNT_BSPLINE_SURFACE_BOUNDARY 25 +#define DGNT_BSPLINE_KNOT 26 +#define DGNT_BSPLINE_CURVE_HEADER 27 +#define DGNT_BSPLINE_WEIGHT_FACTOR 28 +#define DGNT_CONE 23 +#define DGNT_SHARED_CELL_DEFN 34 +#define DGNT_SHARED_CELL_ELEM 35 +#define DGNT_TAG_VALUE 37 +#define DGNT_APPLICATION_ELEM 66 + +/* -------------------------------------------------------------------- */ +/* Line Styles */ +/* -------------------------------------------------------------------- */ +#define DGNS_SOLID 0 +#define DGNS_DOTTED 1 +#define DGNS_MEDIUM_DASH 2 +#define DGNS_LONG_DASH 3 +#define DGNS_DOT_DASH 4 +#define DGNS_SHORT_DASH 5 +#define DGNS_DASH_DOUBLE_DOT 6 +#define DGNS_LONG_DASH_SHORT_DASH 7 + +/* -------------------------------------------------------------------- */ +/* 3D Surface Types */ +/* -------------------------------------------------------------------- */ +#define DGNSUT_SURFACE_OF_PROJECTION 0 +#define DGNSUT_BOUNDED_PLANE 1 +#define DGNSUT_BOUNDED_PLANE2 2 +#define DGNSUT_RIGHT_CIRCULAR_CYLINDER 3 +#define DGNSUT_RIGHT_CIRCULAR_CONE 4 +#define DGNSUT_TABULATED_CYLINDER 5 +#define DGNSUT_TABULATED_CONE 6 +#define DGNSUT_CONVOLUTE 7 +#define DGNSUT_SURFACE_OF_REVOLUTION 8 +#define DGNSUT_WARPED_SURFACE 9 + +/* -------------------------------------------------------------------- */ +/* 3D Solid Types */ +/* -------------------------------------------------------------------- */ +#define DGNSOT_VOLUME_OF_PROJECTION 0 +#define DGNSOT_VOLUME_OF_REVOLUTION 1 +#define DGNSOT_BOUNDED_VOLUME 2 + + +/* -------------------------------------------------------------------- */ +/* Class */ +/* -------------------------------------------------------------------- */ +#define DGNC_PRIMARY 0 +#define DGNC_PATTERN_COMPONENT 1 +#define DGNC_CONSTRUCTION_ELEMENT 2 +#define DGNC_DIMENSION_ELEMENT 3 +#define DGNC_PRIMARY_RULE_ELEMENT 4 +#define DGNC_LINEAR_PATTERNED_ELEMENT 5 +#define DGNC_CONSTRUCTION_RULE_ELEMENT 6 + +/* -------------------------------------------------------------------- */ +/* Group Data level numbers. */ +/* */ +/* These are symbolic values for the typ 5 (DGNT_GROUP_DATA) */ +/* level values that have special meanings. */ +/* -------------------------------------------------------------------- */ +#define DGN_GDL_COLOR_TABLE 1 +#define DGN_GDL_NAMED_VIEW 3 +#define DGN_GDL_REF_FILE 9 + +/* -------------------------------------------------------------------- */ +/* Word 17 property flags. */ +/* -------------------------------------------------------------------- */ +#define DGNPF_HOLE 0x8000 +#define DGNPF_SNAPPABLE 0x4000 +#define DGNPF_PLANAR 0x2000 +#define DGNPF_ORIENTATION 0x1000 +#define DGNPF_ATTRIBUTES 0x0800 +#define DGNPF_MODIFIED 0x0400 +#define DGNPF_NEW 0x0200 +#define DGNPF_LOCKED 0x0100 +#define DGNPF_CLASS 0x000f + +/* -------------------------------------------------------------------- */ +/* DGNElementInfo flag values. */ +/* -------------------------------------------------------------------- */ +#define DGNEIF_DELETED 0x01 +#define DGNEIF_COMPLEX 0x02 + +/* -------------------------------------------------------------------- */ +/* Justifications */ +/* -------------------------------------------------------------------- */ +#define DGNJ_LEFT_TOP 0 +#define DGNJ_LEFT_CENTER 1 +#define DGNJ_LEFT_BOTTOM 2 +#define DGNJ_LEFTMARGIN_TOP 3 /* text node header only */ +#define DGNJ_LEFTMARGIN_CENTER 4 /* text node header only */ +#define DGNJ_LEFTMARGIN_BOTTOM 5 /* text node header only */ +#define DGNJ_CENTER_TOP 6 +#define DGNJ_CENTER_CENTER 7 +#define DGNJ_CENTER_BOTTOM 8 +#define DGNJ_RIGHTMARGIN_TOP 9 /* text node header only */ +#define DGNJ_RIGHTMARGIN_CENTER 10 /* text node header only */ +#define DGNJ_RIGHTMARGIN_BOTTOM 11 /* text node header only */ +#define DGNJ_RIGHT_TOP 12 +#define DGNJ_RIGHT_CENTER 13 +#define DGNJ_RIGHT_BOTTOM 14 + +/* -------------------------------------------------------------------- */ +/* DGN file reading options. */ +/* -------------------------------------------------------------------- */ +#define DGNO_CAPTURE_RAW_DATA 0x01 + +/* -------------------------------------------------------------------- */ +/* Known attribute linkage types, including my synthetic ones. */ +/* -------------------------------------------------------------------- */ +#define DGNLT_DMRS 0x0000 +#define DGNLT_INFORMIX 0x3848 +#define DGNLT_ODBC 0x5e62 +#define DGNLT_ORACLE 0x6091 +#define DGNLT_RIS 0x71FB +#define DGNLT_SYBASE 0x4f58 +#define DGNLT_XBASE 0x1971 +#define DGNLT_SHAPE_FILL 0x0041 +#define DGNLT_ASSOC_ID 0x7D2F + +/* -------------------------------------------------------------------- */ +/* File creation options. */ +/* -------------------------------------------------------------------- */ + +#define DGNCF_USE_SEED_UNITS 0x01 +#define DGNCF_USE_SEED_ORIGIN 0x02 +#define DGNCF_COPY_SEED_FILE_COLOR_TABLE 0x04 +#define DGNCF_COPY_WHOLE_SEED_FILE 0x08 + +/* -------------------------------------------------------------------- */ +/* B-Spline Curve flags. Also used for U-direction of surfaces */ +/* -------------------------------------------------------------------- */ +#define DGNBSC_CURVE_DISPLAY 0x10 +#define DGNBSC_POLY_DISPLAY 0x20 +#define DGNBSC_RATIONAL 0x40 +#define DGNBSC_CLOSED 0x80 + +/* -------------------------------------------------------------------- */ +/* B-Spline Curve flags for V-direction of surfaces. */ +/* -------------------------------------------------------------------- */ +#define DGNBSS_ARC_SPACING 0x40 +#define DGNBSS_CLOSED 0x80 + +/* -------------------------------------------------------------------- */ +/* API */ +/* -------------------------------------------------------------------- */ +/** Opaque handle representing DGN file, used with DGN API. */ +typedef void *DGNHandle; + +DGNHandle CPL_DLL DGNOpen( const char *, int ); +void CPL_DLL DGNSetOptions( DGNHandle, int ); +int CPL_DLL DGNTestOpen( GByte *, int ); +const DGNElementInfo CPL_DLL *DGNGetElementIndex( DGNHandle, int * ); +int CPL_DLL DGNGetExtents( DGNHandle, double * ); +int CPL_DLL DGNGetDimension( DGNHandle ); +DGNElemCore CPL_DLL *DGNReadElement( DGNHandle ); +void CPL_DLL DGNFreeElement( DGNHandle, DGNElemCore * ); +void CPL_DLL DGNRewind( DGNHandle ); +int CPL_DLL DGNGotoElement( DGNHandle, int ); +void CPL_DLL DGNClose( DGNHandle ); +int CPL_DLL DGNLoadTCB( DGNHandle ); +int CPL_DLL DGNLookupColor( DGNHandle, int, int *, int *, int * ); +int CPL_DLL DGNGetShapeFillInfo( DGNHandle, DGNElemCore *, int * ); +int CPL_DLL DGNGetAssocID( DGNHandle, DGNElemCore * ); +int CPL_DLL DGNGetElementExtents( DGNHandle, DGNElemCore *, + DGNPoint *, DGNPoint * ); + +void CPL_DLL DGNDumpElement( DGNHandle, DGNElemCore *, FILE * ); +const char CPL_DLL *DGNTypeToName( int ); + +void CPL_DLL DGNRotationToQuaternion( double, int * ); +void CPL_DLL DGNQuaternionToMatrix( int *, float * ); +int CPL_DLL DGNStrokeArc( DGNHandle, DGNElemArc *, int, DGNPoint * ); +int CPL_DLL DGNStrokeCurve( DGNHandle, DGNElemMultiPoint*, int, DGNPoint * ); +void CPL_DLL DGNSetSpatialFilter( DGNHandle hDGN, + double dfXMin, double dfYMin, + double dfXMax, double dfYMax ); +int CPL_DLL DGNGetAttrLinkSize( DGNHandle, DGNElemCore *, int ); +unsigned char CPL_DLL * + DGNGetLinkage( DGNHandle hDGN, DGNElemCore *psElement, + int iIndex, int *pnLinkageType, + int *pnEntityNum, int *pnMSLink, int *pnLinkSize); + +/* Write API */ + +int CPL_DLL DGNWriteElement( DGNHandle, DGNElemCore * ); +int CPL_DLL DGNResizeElement( DGNHandle, DGNElemCore *, int ); +DGNHandle CPL_DLL + DGNCreate( const char *pszNewFilename, const char *pszSeedFile, + int nCreationFlags, + double dfOriginX, double dfOriginY, double dfOriginZ, + int nMasterUnitPerSubUnit, int nUORPerSubUnit, + const char *pszMasterUnits, const char *pszSubUnits ); +DGNElemCore CPL_DLL *DGNCloneElement( DGNHandle hDGNSrc, DGNHandle hDGNDst, + DGNElemCore *psSrcElement ); +int CPL_DLL DGNUpdateElemCore( DGNHandle hDGN, DGNElemCore *psElement, + int nLevel, int nGraphicGroup, int nColor, + int nWeight, int nStyle ); +int CPL_DLL DGNUpdateElemCoreExtended( DGNHandle hDGN, + DGNElemCore *psElement ); + +DGNElemCore CPL_DLL * + DGNCreateMultiPointElem( DGNHandle hDGN, int nType, + int nPointCount, DGNPoint*pasVertices ); +DGNElemCore CPL_DLL * + DGNCreateArcElem2D( DGNHandle hDGN, int nType, + double dfOriginX, double dfOriginY, + double dfPrimaryAxis, double dfSecondaryAxis, + double dfRotation, + double dfStartAngle, double dfSweepAngle ); + +DGNElemCore CPL_DLL * + DGNCreateArcElem( DGNHandle hDGN, int nType, + double dfOriginX, double dfOriginY, + double dfOriginZ, + double dfPrimaryAxis, double dfSecondaryAxis, + double dfStartAngle, double dfSweepAngle, + double dfRotation, int *panQuaternion ); + +DGNElemCore CPL_DLL * + DGNCreateConeElem( DGNHandle hDGN, + double center_1X, double center_1Y, + double center_1Z, double radius_1, + double center_2X, double center_2Y, + double center_2Z, double radius_2, + int *panQuaternion ); + +DGNElemCore CPL_DLL * + DGNCreateTextElem( DGNHandle hDGN, const char *pszText, + int nFontId, int nJustification, + double dfLengthMult, double dfHeightMult, + double dfRotation, int *panQuaternion, + double dfOriginX, double dfOriginY, double dfOriginZ ); + +DGNElemCore CPL_DLL * + DGNCreateColorTableElem( DGNHandle hDGN, int nScreenFlag, + GByte abyColorInfo[256][3] ); +DGNElemCore CPL_DLL * +DGNCreateComplexHeaderElem( DGNHandle hDGN, int nType, + int nTotLength, int nNumElems ); +DGNElemCore CPL_DLL * +DGNCreateComplexHeaderFromGroup( DGNHandle hDGN, int nType, + int nNumElems, DGNElemCore **papsElems ); + +DGNElemCore CPL_DLL * +DGNCreateSolidHeaderElem( DGNHandle hDGN, int nType, int nSurfType, + int nBoundElems, int nTotLength, int nNumElems ); +DGNElemCore CPL_DLL * +DGNCreateSolidHeaderFromGroup( DGNHandle hDGN, int nType, int nSurfType, + int nBoundElems, int nNumElems, + DGNElemCore **papsElems ); + +DGNElemCore CPL_DLL * +DGNCreateCellHeaderElem( DGNHandle hDGN, int nTotLength, const char *pszName, + short nClass, short *panLevels, + DGNPoint *psRangeLow, DGNPoint *psRangeHigh, + DGNPoint *psOrigin, double dfXScale, double dfYScale, + double dfRotation ); + +DGNElemCore CPL_DLL * +DGNCreateCellHeaderFromGroup( DGNHandle hDGN, const char *pszName, + short nClass, short *panLevels, + int nNumElems, DGNElemCore **papsElems, + DGNPoint *psOrigin, + double dfXScale, double dfYScale, + double dfRotation ); + +int CPL_DLL DGNAddMSLink( DGNHandle hDGN, DGNElemCore *psElement, + int nLinkageType, int nEntityNum, int nMSLink ); + +int CPL_DLL DGNAddRawAttrLink( DGNHandle hDGN, DGNElemCore *psElement, + int nLinkSize, unsigned char *pabyRawLinkData ); + +int CPL_DLL DGNAddShapeFillInfo( DGNHandle hDGN, DGNElemCore *psElement, + int nColor ); + +int CPL_DLL DGNElemTypeHasDispHdr( int nElemType ); + +CPL_C_END + +#endif /* ndef _DGNLIB_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnlibp.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnlibp.h new file mode 100644 index 000000000..189e151cd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnlibp.h @@ -0,0 +1,111 @@ +/****************************************************************************** + * $Id: dgnlibp.h 22381 2011-05-16 21:14:22Z rouault $ + * + * Project: Microstation DGN Access Library + * Purpose: Internal (privatE) datastructures, and prototypes for DGN Access + * Library. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Avenza Systems Inc, http://www.avenza.com/ + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _DGNLIBP_H_INCLUDED +#define _DGNLIBP_H_INCLUDED + +#include "dgnlib.h" + + +#ifndef PI +#define PI 3.1415926535897932384626433832795 +#endif + +typedef struct { + FILE *fp; + int next_element_id; + + int nElemBytes; + GByte abyElem[131076]; + + int got_tcb; + int dimension; + int options; + double scale; + double origin_x; + double origin_y; + double origin_z; + + int index_built; + int element_count; + int max_element_count; + DGNElementInfo *element_index; + + int got_color_table; + GByte color_table[256][3]; + + int got_bounds; + GUInt32 min_x; + GUInt32 min_y; + GUInt32 min_z; + GUInt32 max_x; + GUInt32 max_y; + GUInt32 max_z; + + int has_spatial_filter; + int sf_converted_to_uor; + + int select_complex_group; + int in_complex_group; + + GUInt32 sf_min_x; + GUInt32 sf_min_y; + GUInt32 sf_max_x; + GUInt32 sf_max_y; + + double sf_min_x_geo; + double sf_min_y_geo; + double sf_max_x_geo; + double sf_max_y_geo; +} DGNInfo; + +#define DGN_INT32( p ) ((p)[2] \ + + ((p)[3] << 8) \ + + ((p)[1] << 24) \ + + ((p)[0] << 16)) +#define DGN_WRITE_INT32( n, p ) { GInt32 nMacroWork = (n); \ + ((unsigned char *)p)[0] = (unsigned char)((nMacroWork & 0x00ff0000) >> 16); \ + ((unsigned char *)p)[1] = (unsigned char)((nMacroWork & 0xff000000) >> 24); \ + ((unsigned char *)p)[2] = (unsigned char)((nMacroWork & 0x000000ff) >> 0); \ + ((unsigned char *)p)[3] = (unsigned char)((nMacroWork & 0x0000ff00) >> 8); } + +int DGNParseCore( DGNInfo *, DGNElemCore * ); +void DGNTransformPoint( DGNInfo *, DGNPoint * ); +void DGNInverseTransformPoint( DGNInfo *, DGNPoint * ); +void DGNInverseTransformPointToInt( DGNInfo *, DGNPoint *, unsigned char * ); +void DGN2IEEEDouble( void * ); +void IEEE2DGNDouble( void * ); +void DGNBuildIndex( DGNInfo * ); +void DGNRad50ToAscii( unsigned short rad50, char *str ); +void DGNAsciiToRad50( const char *str, unsigned short *rad50 ); +void DGNSpatialFilterToUOR( DGNInfo *); +int DGNLoadRawElement( DGNInfo *psDGN, int *pnType, int *pnLevel ); + +#endif /* ndef _DGNLIBP_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnopen.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnopen.cpp new file mode 100644 index 000000000..8dd456cb2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnopen.cpp @@ -0,0 +1,316 @@ +/****************************************************************************** + * $Id: dgnopen.cpp 27272 2014-05-01 23:14:58Z rouault $ + * + * Project: Microstation DGN Access Library + * Purpose: DGN Access Library file open code. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Avenza Systems Inc, http://www.avenza.com/ + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "dgnlibp.h" + +CPL_CVSID("$Id: dgnopen.cpp 27272 2014-05-01 23:14:58Z rouault $"); + +/************************************************************************/ +/* DGNTestOpen() */ +/************************************************************************/ + +/** + * Test if header is DGN. + * + * @param pabyHeader block of header data from beginning of file. + * @param nByteCount number of bytes in pabyHeader. + * + * @return TRUE if the header appears to be from a DGN file, otherwise FALSE. + */ + +int DGNTestOpen( GByte *pabyHeader, int nByteCount ) + +{ + if( nByteCount < 4 ) + return FALSE; + + // Is it a cell library? + if( pabyHeader[0] == 0x08 + && pabyHeader[1] == 0x05 + && pabyHeader[2] == 0x17 + && pabyHeader[3] == 0x00 ) + return TRUE; + + // Is it not a regular 2D or 3D file? + if( (pabyHeader[0] != 0x08 && pabyHeader[0] != 0xC8) + || pabyHeader[1] != 0x09 + || pabyHeader[2] != 0xFE || pabyHeader[3] != 0x02 ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* DGNOpen() */ +/************************************************************************/ + +/** + * Open a DGN file. + * + * The file is opened, and minimally verified to ensure it is a DGN (ISFF) + * file. If the file cannot be opened for read access an error with code + * CPLE_OpenFailed with be reported via CPLError() and NULL returned. + * If the file header does + * not appear to be a DGN file, an error with code CPLE_AppDefined will be + * reported via CPLError(), and NULL returned. + * + * If successful a handle for further access is returned. This should be + * closed with DGNClose() when no longer needed. + * + * DGNOpen() does not scan the file on open, and should be very fast even for + * large files. + * + * @param pszFilename name of file to try opening. + * @param bUpdate should the file be opened with read+update (r+) mode? + * + * @return handle to use for further access to file using DGN API, or NULL + * if open fails. + */ + +DGNHandle DGNOpen( const char * pszFilename, int bUpdate ) + +{ + DGNInfo *psDGN; + FILE *fp; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + if( bUpdate ) + fp = VSIFOpen( pszFilename, "rb+" ); + else + fp = VSIFOpen( pszFilename, "rb" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open `%s' for read access.\n", + pszFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Verify the format ... add later. */ +/* -------------------------------------------------------------------- */ + GByte abyHeader[512]; + + VSIFRead( abyHeader, 1, sizeof(abyHeader), fp ); + if( !DGNTestOpen( abyHeader, sizeof(abyHeader) ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "File `%s' does not have expected DGN header.\n", + pszFilename ); + VSIFClose( fp ); + return NULL; + } + + VSIRewind( fp ); + +/* -------------------------------------------------------------------- */ +/* Create the info structure. */ +/* -------------------------------------------------------------------- */ + psDGN = (DGNInfo *) CPLCalloc(sizeof(DGNInfo),1); + psDGN->fp = fp; + psDGN->next_element_id = 0; + + psDGN->got_tcb = FALSE; + psDGN->scale = 1.0; + psDGN->origin_x = 0.0; + psDGN->origin_y = 0.0; + psDGN->origin_z = 0.0; + + psDGN->index_built = FALSE; + psDGN->element_count = 0; + psDGN->element_index = NULL; + + psDGN->got_bounds = FALSE; + + if( abyHeader[0] == 0xC8 ) + psDGN->dimension = 3; + else + psDGN->dimension = 2; + + psDGN->has_spatial_filter = FALSE; + psDGN->sf_converted_to_uor = FALSE; + psDGN->select_complex_group = FALSE; + psDGN->in_complex_group = FALSE; + + return (DGNHandle) psDGN; +} + +/************************************************************************/ +/* DGNSetOptions() */ +/************************************************************************/ + +/** + * Set file access options. + * + * Sets a flag affecting how the file is accessed. Currently + * there is only one support flag: + * + * DGNO_CAPTURE_RAW_DATA: If this is enabled (it is off by default), + * then the raw binary data associated with elements will be kept in + * the raw_data field within the DGNElemCore when they are read. This + * is required if the application needs to interprete the raw data itself. + * It is also necessary if the element is to be written back to this file, + * or another file using DGNWriteElement(). Off by default (to conserve + * memory). + * + * @param hDGN handle to file returned by DGNOpen(). + * @param nOptions ORed option flags. + */ + +void DGNSetOptions( DGNHandle hDGN, int nOptions ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + + psDGN->options = nOptions; +} + +/************************************************************************/ +/* DGNSetSpatialFilter() */ +/************************************************************************/ + +/** + * Set rectangle for which features are desired. + * + * If a spatial filter is set with this function, DGNReadElement() will + * only return spatial elements (elements with a known bounding box) and + * only those elements for which this bounding box overlaps the requested + * region. + * + * If all four values (dfXMin, dfXMax, dfYMin and dfYMax) are zero, the + * spatial filter is disabled. Note that installing a spatial filter + * won't reduce the amount of data read from disk. All elements are still + * scanned, but the amount of processing work for elements outside the + * spatial filter is minimized. + * + * @param hDGN Handle from DGNOpen() for file to update. + * @param dfXMin minimum x coordinate for extents (georeferenced coordinates). + * @param dfYMin minimum y coordinate for extents (georeferenced coordinates). + * @param dfXMax maximum x coordinate for extents (georeferenced coordinates). + * @param dfYMax maximum y coordinate for extents (georeferenced coordinates). + */ + +void DGNSetSpatialFilter( DGNHandle hDGN, + double dfXMin, double dfYMin, + double dfXMax, double dfYMax ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + + if( dfXMin == 0.0 && dfXMax == 0.0 + && dfYMin == 0.0 && dfYMax == 0.0 ) + { + psDGN->has_spatial_filter = FALSE; + return; + } + + psDGN->has_spatial_filter = TRUE; + psDGN->sf_converted_to_uor = FALSE; + + psDGN->sf_min_x_geo = dfXMin; + psDGN->sf_min_y_geo = dfYMin; + psDGN->sf_max_x_geo = dfXMax; + psDGN->sf_max_y_geo = dfYMax; + + DGNSpatialFilterToUOR( psDGN ); + +} + +/************************************************************************/ +/* DGNSpatialFilterToUOR() */ +/************************************************************************/ + +void DGNSpatialFilterToUOR( DGNInfo *psDGN ) + +{ + DGNPoint sMin, sMax; + + if( psDGN->sf_converted_to_uor + || !psDGN->has_spatial_filter + || !psDGN->got_tcb ) + return; + + sMin.x = psDGN->sf_min_x_geo; + sMin.y = psDGN->sf_min_y_geo; + sMin.z = 0; + + sMax.x = psDGN->sf_max_x_geo; + sMax.y = psDGN->sf_max_y_geo; + sMax.z = 0; + + DGNInverseTransformPoint( psDGN, &sMin ); + DGNInverseTransformPoint( psDGN, &sMax ); + + psDGN->sf_min_x = (GUInt32) (sMin.x + 2147483648.0); + psDGN->sf_min_y = (GUInt32) (sMin.y + 2147483648.0); + psDGN->sf_max_x = (GUInt32) (sMax.x + 2147483648.0); + psDGN->sf_max_y = (GUInt32) (sMax.y + 2147483648.0); + + psDGN->sf_converted_to_uor = TRUE; +} + +/************************************************************************/ +/* DGNClose() */ +/************************************************************************/ + +/** + * Close DGN file. + * + * @param hDGN Handle from DGNOpen() for file to close. + */ + +void DGNClose( DGNHandle hDGN ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + + VSIFClose( psDGN->fp ); + CPLFree( psDGN->element_index ); + CPLFree( psDGN ); +} + +/************************************************************************/ +/* DGNGetDimension() */ +/************************************************************************/ + +/** + * Return 2D/3D dimension of file. + * + * Return 2 or 3 depending on the dimension value of the provided file. + */ + +int DGNGetDimension( DGNHandle hDGN ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + + return psDGN->dimension; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnread.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnread.cpp new file mode 100644 index 000000000..aa31a4f3f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnread.cpp @@ -0,0 +1,1852 @@ +/****************************************************************************** + * $Id: dgnread.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: Microstation DGN Access Library + * Purpose: DGN Access Library element reading code. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Avenza Systems Inc, http://www.avenza.com/ + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "dgnlibp.h" + +CPL_CVSID("$Id: dgnread.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +static DGNElemCore *DGNParseTCB( DGNInfo * ); +static DGNElemCore *DGNParseColorTable( DGNInfo * ); +static DGNElemCore *DGNParseTagSet( DGNInfo * ); + + +/************************************************************************/ +/* DGNGotoElement() */ +/************************************************************************/ + +/** + * Seek to indicated element. + * + * Changes what element will be read on the next call to DGNReadElement(). + * Note that this function requires and index, and one will be built if + * not already available. + * + * @param hDGN the file to affect. + * @param element_id the element to seek to. These values are sequentially + * ordered starting at zero for the first element. + * + * @return returns TRUE on success or FALSE on failure. + */ + +int DGNGotoElement( DGNHandle hDGN, int element_id ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + + DGNBuildIndex( psDGN ); + + if( element_id < 0 || element_id >= psDGN->element_count ) + return FALSE; + + if( VSIFSeek( psDGN->fp, psDGN->element_index[element_id].offset, + SEEK_SET ) != 0 ) + return FALSE; + + psDGN->next_element_id = element_id; + psDGN->in_complex_group = FALSE; + + return TRUE; +} + +/************************************************************************/ +/* DGNLoadRawElement() */ +/************************************************************************/ + +int DGNLoadRawElement( DGNInfo *psDGN, int *pnType, int *pnLevel ) + +{ +/* -------------------------------------------------------------------- */ +/* Read the first four bytes to get the level, type, and word */ +/* count. */ +/* -------------------------------------------------------------------- */ + int nType, nWords, nLevel; + + if( VSIFRead( psDGN->abyElem, 1, 4, psDGN->fp ) != 4 ) + return FALSE; + + /* Is this an 0xFFFF endof file marker? */ + if( psDGN->abyElem[0] == 0xff && psDGN->abyElem[1] == 0xff ) + return FALSE; + + nWords = psDGN->abyElem[2] + psDGN->abyElem[3]*256; + nType = psDGN->abyElem[1] & 0x7f; + nLevel = psDGN->abyElem[0] & 0x3f; + +/* -------------------------------------------------------------------- */ +/* Read the rest of the element data into the working buffer. */ +/* -------------------------------------------------------------------- */ + CPLAssert( nWords * 2 + 4 <= (int) sizeof(psDGN->abyElem) ); + + if( (int) VSIFRead( psDGN->abyElem + 4, 2, nWords, psDGN->fp ) != nWords ) + return FALSE; + + psDGN->nElemBytes = nWords * 2 + 4; + + psDGN->next_element_id++; + +/* -------------------------------------------------------------------- */ +/* Return requested info. */ +/* -------------------------------------------------------------------- */ + if( pnType != NULL ) + *pnType = nType; + + if( pnLevel != NULL ) + *pnLevel = nLevel; + + return TRUE; +} + + +/************************************************************************/ +/* DGNGetRawExtents() */ +/* */ +/* Returns FALSE if the element type does not have reconisable */ +/* element extents, other TRUE and the extents will be updated. */ +/* */ +/* It is assumed the raw element data has been loaded into the */ +/* working area by DGNLoadRawElement(). */ +/************************************************************************/ + +static int +DGNGetRawExtents( DGNInfo *psDGN, int nType, unsigned char *pabyRawData, + GUInt32 *pnXMin, GUInt32 *pnYMin, GUInt32 *pnZMin, + GUInt32 *pnXMax, GUInt32 *pnYMax, GUInt32 *pnZMax ) + +{ + if( pabyRawData == NULL ) + pabyRawData = psDGN->abyElem + 0; + + switch( nType ) + { + case DGNT_LINE: + case DGNT_LINE_STRING: + case DGNT_SHAPE: + case DGNT_CURVE: + case DGNT_BSPLINE_POLE: + case DGNT_BSPLINE_SURFACE_HEADER: + case DGNT_BSPLINE_CURVE_HEADER: + case DGNT_ELLIPSE: + case DGNT_ARC: + case DGNT_TEXT: + case DGNT_TEXT_NODE: + case DGNT_COMPLEX_CHAIN_HEADER: + case DGNT_COMPLEX_SHAPE_HEADER: + case DGNT_CONE: + case DGNT_3DSURFACE_HEADER: + case DGNT_3DSOLID_HEADER: + *pnXMin = DGN_INT32( pabyRawData + 4 ); + *pnYMin = DGN_INT32( pabyRawData + 8 ); + if( pnZMin != NULL ) + *pnZMin = DGN_INT32( pabyRawData + 12 ); + + *pnXMax = DGN_INT32( pabyRawData + 16 ); + *pnYMax = DGN_INT32( pabyRawData + 20 ); + if( pnZMax != NULL ) + *pnZMax = DGN_INT32( pabyRawData + 24 ); + return TRUE; + + default: + return FALSE; + } +} + + +/************************************************************************/ +/* DGNGetElementExtents() */ +/************************************************************************/ + +/** + * Fetch extents of an element. + * + * This function will return the extents of the passed element if possible. + * The extents are extracted from the element header if it contains them, + * and transformed into master georeferenced format. Some element types + * do not have extents at all and will fail. + * + * This call will also fail if the extents raw data for the element is not + * available. This will occur if it was not the most recently read element, + * and if the raw_data field is not loaded. + * + * @param hDGN the handle of the file to read from. + * + * @param psElement the element to extract extents from. + * + * @param psMin structure loaded with X, Y and Z minimum values for the + * extent. + * + * @param psMax structure loaded with X, Y and Z maximum values for the + * extent. + * + * @return TRUE on success of FALSE if extracting extents fails. + */ + +int DGNGetElementExtents( DGNHandle hDGN, DGNElemCore *psElement, + DGNPoint *psMin, DGNPoint *psMax ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + GUInt32 anMin[3], anMax[3]; + int bResult; + +/* -------------------------------------------------------------------- */ +/* Get the extents if we have raw data in the element, or */ +/* loaded in the file buffer. */ +/* -------------------------------------------------------------------- */ + if( psElement->raw_data != NULL ) + bResult = DGNGetRawExtents( psDGN, psElement->type, + psElement->raw_data, + anMin + 0, anMin + 1, anMin + 2, + anMax + 0, anMax + 1, anMax + 2 ); + else if( psElement->element_id == psDGN->next_element_id - 1 ) + bResult = DGNGetRawExtents( psDGN, psElement->type, + psDGN->abyElem + 0, + anMin + 0, anMin + 1, anMin + 2, + anMax + 0, anMax + 1, anMax + 2 ); + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "DGNGetElementExtents() fails because the requested element\n" + " does not have raw data available." ); + return FALSE; + } + + if( !bResult ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Transform to user coordinate system and return. The offset */ +/* is to convert from "binary offset" form to twos complement. */ +/* -------------------------------------------------------------------- */ + psMin->x = anMin[0] - 2147483648.0; + psMin->y = anMin[1] - 2147483648.0; + psMin->z = anMin[2] - 2147483648.0; + + psMax->x = anMax[0] - 2147483648.0; + psMax->y = anMax[1] - 2147483648.0; + psMax->z = anMax[2] - 2147483648.0; + + DGNTransformPoint( psDGN, psMin ); + DGNTransformPoint( psDGN, psMax ); + + return TRUE; +} + +/************************************************************************/ +/* DGNProcessElement() */ +/* */ +/* Assumes the raw element data has already been loaded, and */ +/* tries to convert it into an element structure. */ +/************************************************************************/ + +static DGNElemCore *DGNProcessElement( DGNInfo *psDGN, int nType, int nLevel ) + +{ + DGNElemCore *psElement = NULL; + +/* -------------------------------------------------------------------- */ +/* Handle based on element type. */ +/* -------------------------------------------------------------------- */ + switch( nType ) + { + case DGNT_CELL_HEADER: + { + DGNElemCellHeader *psCell; + + psCell = (DGNElemCellHeader *) + CPLCalloc(sizeof(DGNElemCellHeader),1); + psElement = (DGNElemCore *) psCell; + psElement->stype = DGNST_CELL_HEADER; + DGNParseCore( psDGN, psElement ); + + psCell->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; + + DGNRad50ToAscii( psDGN->abyElem[38] + psDGN->abyElem[39] * 256, + psCell->name + 0 ); + DGNRad50ToAscii( psDGN->abyElem[40] + psDGN->abyElem[41] * 256, + psCell->name + 3 ); + + psCell->cclass = psDGN->abyElem[42] + psDGN->abyElem[43] * 256; + psCell->levels[0] = psDGN->abyElem[44] + psDGN->abyElem[45] * 256; + psCell->levels[1] = psDGN->abyElem[46] + psDGN->abyElem[47] * 256; + psCell->levels[2] = psDGN->abyElem[48] + psDGN->abyElem[49] * 256; + psCell->levels[3] = psDGN->abyElem[50] + psDGN->abyElem[51] * 256; + + if( psDGN->dimension == 2 ) + { + psCell->rnglow.x = DGN_INT32( psDGN->abyElem + 52 ); + psCell->rnglow.y = DGN_INT32( psDGN->abyElem + 56 ); + psCell->rnghigh.x = DGN_INT32( psDGN->abyElem + 60 ); + psCell->rnghigh.y = DGN_INT32( psDGN->abyElem + 64 ); + + psCell->trans[0] = + 1.0 * DGN_INT32( psDGN->abyElem + 68 ) / (1<<31); + psCell->trans[1] = + 1.0 * DGN_INT32( psDGN->abyElem + 72 ) / (1<<31); + psCell->trans[2] = + 1.0 * DGN_INT32( psDGN->abyElem + 76 ) / (1<<31); + psCell->trans[3] = + 1.0 * DGN_INT32( psDGN->abyElem + 80 ) / (1<<31); + + psCell->origin.x = DGN_INT32( psDGN->abyElem + 84 ); + psCell->origin.y = DGN_INT32( psDGN->abyElem + 88 ); + + { + double a, b, c, d, a2, c2; + a = DGN_INT32( psDGN->abyElem + 68 ); + b = DGN_INT32( psDGN->abyElem + 72 ); + c = DGN_INT32( psDGN->abyElem + 76 ); + d = DGN_INT32( psDGN->abyElem + 80 ); + a2 = a * a; + c2 = c * c; + + psCell->xscale = sqrt(a2 + c2) / 214748; + psCell->yscale = sqrt(b*b + d*d) / 214748; + if( (a2 + c2) <= 0.0 ) + psCell->rotation = 0.0; + else + psCell->rotation = acos(a / sqrt(a2 + c2)); + + if (b <= 0) + psCell->rotation = psCell->rotation * 180 / PI; + else + psCell->rotation = 360 - psCell->rotation * 180 / PI; + } + } + else + { + psCell->rnglow.x = DGN_INT32( psDGN->abyElem + 52 ); + psCell->rnglow.y = DGN_INT32( psDGN->abyElem + 56 ); + psCell->rnglow.z = DGN_INT32( psDGN->abyElem + 60 ); + psCell->rnghigh.x = DGN_INT32( psDGN->abyElem + 64 ); + psCell->rnghigh.y = DGN_INT32( psDGN->abyElem + 68 ); + psCell->rnghigh.z = DGN_INT32( psDGN->abyElem + 72 ); + + psCell->trans[0] = + 1.0 * DGN_INT32( psDGN->abyElem + 76 ) / (1<<31); + psCell->trans[1] = + 1.0 * DGN_INT32( psDGN->abyElem + 80 ) / (1<<31); + psCell->trans[2] = + 1.0 * DGN_INT32( psDGN->abyElem + 84 ) / (1<<31); + psCell->trans[3] = + 1.0 * DGN_INT32( psDGN->abyElem + 88 ) / (1<<31); + psCell->trans[4] = + 1.0 * DGN_INT32( psDGN->abyElem + 92 ) / (1<<31); + psCell->trans[5] = + 1.0 * DGN_INT32( psDGN->abyElem + 96 ) / (1<<31); + psCell->trans[6] = + 1.0 * DGN_INT32( psDGN->abyElem + 100 ) / (1<<31); + psCell->trans[7] = + 1.0 * DGN_INT32( psDGN->abyElem + 104 ) / (1<<31); + psCell->trans[8] = + 1.0 * DGN_INT32( psDGN->abyElem + 108 ) / (1<<31); + + psCell->origin.x = DGN_INT32( psDGN->abyElem + 112 ); + psCell->origin.y = DGN_INT32( psDGN->abyElem + 116 ); + psCell->origin.z = DGN_INT32( psDGN->abyElem + 120 ); + } + + DGNTransformPoint( psDGN, &(psCell->rnglow) ); + DGNTransformPoint( psDGN, &(psCell->rnghigh) ); + DGNTransformPoint( psDGN, &(psCell->origin) ); + } + break; + + case DGNT_CELL_LIBRARY: + { + DGNElemCellLibrary *psCell; + int iWord; + + psCell = (DGNElemCellLibrary *) + CPLCalloc(sizeof(DGNElemCellLibrary),1); + psElement = (DGNElemCore *) psCell; + psElement->stype = DGNST_CELL_LIBRARY; + DGNParseCore( psDGN, psElement ); + + DGNRad50ToAscii( psDGN->abyElem[32] + psDGN->abyElem[33] * 256, + psCell->name + 0 ); + DGNRad50ToAscii( psDGN->abyElem[34] + psDGN->abyElem[35] * 256, + psCell->name + 3 ); + + psElement->properties = psDGN->abyElem[38] + + psDGN->abyElem[39] * 256; + + psCell->dispsymb = psDGN->abyElem[40] + psDGN->abyElem[41] * 256; + + psCell->cclass = psDGN->abyElem[42] + psDGN->abyElem[43] * 256; + psCell->levels[0] = psDGN->abyElem[44] + psDGN->abyElem[45] * 256; + psCell->levels[1] = psDGN->abyElem[46] + psDGN->abyElem[47] * 256; + psCell->levels[2] = psDGN->abyElem[48] + psDGN->abyElem[49] * 256; + psCell->levels[3] = psDGN->abyElem[50] + psDGN->abyElem[51] * 256; + + psCell->numwords = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; + + memset( psCell->description, 0, sizeof(psCell->description) ); + + for( iWord = 0; iWord < 9; iWord++ ) + { + int iOffset = 52 + iWord * 2; + + DGNRad50ToAscii( psDGN->abyElem[iOffset] + + psDGN->abyElem[iOffset+1] * 256, + psCell->description + iWord * 3 ); + } + } + break; + + case DGNT_LINE: + { + DGNElemMultiPoint *psLine; + + psLine = (DGNElemMultiPoint *) + CPLCalloc(sizeof(DGNElemMultiPoint),1); + psElement = (DGNElemCore *) psLine; + psElement->stype = DGNST_MULTIPOINT; + DGNParseCore( psDGN, psElement ); + + psLine->num_vertices = 2; + if( psDGN->dimension == 2 ) + { + psLine->vertices[0].x = DGN_INT32( psDGN->abyElem + 36 ); + psLine->vertices[0].y = DGN_INT32( psDGN->abyElem + 40 ); + psLine->vertices[1].x = DGN_INT32( psDGN->abyElem + 44 ); + psLine->vertices[1].y = DGN_INT32( psDGN->abyElem + 48 ); + } + else + { + psLine->vertices[0].x = DGN_INT32( psDGN->abyElem + 36 ); + psLine->vertices[0].y = DGN_INT32( psDGN->abyElem + 40 ); + psLine->vertices[0].z = DGN_INT32( psDGN->abyElem + 44 ); + psLine->vertices[1].x = DGN_INT32( psDGN->abyElem + 48 ); + psLine->vertices[1].y = DGN_INT32( psDGN->abyElem + 52 ); + psLine->vertices[1].z = DGN_INT32( psDGN->abyElem + 56 ); + } + DGNTransformPoint( psDGN, psLine->vertices + 0 ); + DGNTransformPoint( psDGN, psLine->vertices + 1 ); + } + break; + + case DGNT_LINE_STRING: + case DGNT_SHAPE: + case DGNT_CURVE: + case DGNT_BSPLINE_POLE: + { + DGNElemMultiPoint *psLine; + int i, count; + int pntsize = psDGN->dimension * 4; + + count = psDGN->abyElem[36] + psDGN->abyElem[37]*256; + psLine = (DGNElemMultiPoint *) + CPLCalloc(sizeof(DGNElemMultiPoint)+(count-2)*sizeof(DGNPoint),1); + psElement = (DGNElemCore *) psLine; + psElement->stype = DGNST_MULTIPOINT; + DGNParseCore( psDGN, psElement ); + + if( psDGN->nElemBytes < 38 + count * pntsize ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Trimming multipoint vertices to %d from %d because\n" + "element is short.\n", + (psDGN->nElemBytes - 38) / pntsize, + count ); + count = (psDGN->nElemBytes - 38) / pntsize; + } + psLine->num_vertices = count; + for( i = 0; i < psLine->num_vertices; i++ ) + { + psLine->vertices[i].x = + DGN_INT32( psDGN->abyElem + 38 + i*pntsize ); + psLine->vertices[i].y = + DGN_INT32( psDGN->abyElem + 42 + i*pntsize ); + if( psDGN->dimension == 3 ) + psLine->vertices[i].z = + DGN_INT32( psDGN->abyElem + 46 + i*pntsize ); + + DGNTransformPoint( psDGN, psLine->vertices + i ); + } + } + break; + + case DGNT_TEXT_NODE: + { + DGNElemTextNode *psNode; + + psNode = (DGNElemTextNode *) CPLCalloc(sizeof(DGNElemTextNode),1); + psElement = (DGNElemCore *) psNode; + psElement->stype = DGNST_TEXT_NODE; + DGNParseCore( psDGN, psElement ); + + psNode->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; + psNode->numelems = psDGN->abyElem[38] + psDGN->abyElem[39] * 256; + + psNode->node_number = psDGN->abyElem[40] + psDGN->abyElem[41] * 256; + psNode->max_length = psDGN->abyElem[42]; + psNode->max_used = psDGN->abyElem[43]; + psNode->font_id = psDGN->abyElem[44]; + psNode->justification = psDGN->abyElem[45]; + psNode->length_mult = (DGN_INT32( psDGN->abyElem + 50 )) + * psDGN->scale * 6.0 / 1000.0; + psNode->height_mult = (DGN_INT32( psDGN->abyElem + 54 )) + * psDGN->scale * 6.0 / 1000.0; + + if( psDGN->dimension == 2 ) + { + psNode->rotation = DGN_INT32( psDGN->abyElem + 58 ) / 360000.0; + + psNode->origin.x = DGN_INT32( psDGN->abyElem + 62 ); + psNode->origin.y = DGN_INT32( psDGN->abyElem + 66 ); + } + else + { + /* leave quaternion for later */ + + psNode->origin.x = DGN_INT32( psDGN->abyElem + 74 ); + psNode->origin.y = DGN_INT32( psDGN->abyElem + 78 ); + psNode->origin.z = DGN_INT32( psDGN->abyElem + 82 ); + } + DGNTransformPoint( psDGN, &(psNode->origin) ); + + } + break; + + case DGNT_GROUP_DATA: + if( nLevel == DGN_GDL_COLOR_TABLE ) + { + psElement = DGNParseColorTable( psDGN ); + } + else + { + psElement = (DGNElemCore *) CPLCalloc(sizeof(DGNElemCore),1); + psElement->stype = DGNST_CORE; + DGNParseCore( psDGN, psElement ); + } + break; + + case DGNT_ELLIPSE: + { + DGNElemArc *psEllipse; + + psEllipse = (DGNElemArc *) CPLCalloc(sizeof(DGNElemArc),1); + psElement = (DGNElemCore *) psEllipse; + psElement->stype = DGNST_ARC; + DGNParseCore( psDGN, psElement ); + + memcpy( &(psEllipse->primary_axis), psDGN->abyElem + 36, 8 ); + DGN2IEEEDouble( &(psEllipse->primary_axis) ); + psEllipse->primary_axis *= psDGN->scale; + + memcpy( &(psEllipse->secondary_axis), psDGN->abyElem + 44, 8 ); + DGN2IEEEDouble( &(psEllipse->secondary_axis) ); + psEllipse->secondary_axis *= psDGN->scale; + + if( psDGN->dimension == 2 ) + { + psEllipse->rotation = DGN_INT32( psDGN->abyElem + 52 ); + psEllipse->rotation = psEllipse->rotation / 360000.0; + + memcpy( &(psEllipse->origin.x), psDGN->abyElem + 56, 8 ); + DGN2IEEEDouble( &(psEllipse->origin.x) ); + + memcpy( &(psEllipse->origin.y), psDGN->abyElem + 64, 8 ); + DGN2IEEEDouble( &(psEllipse->origin.y) ); + } + else + { + /* leave quaternion for later */ + + memcpy( &(psEllipse->origin.x), psDGN->abyElem + 68, 8 ); + DGN2IEEEDouble( &(psEllipse->origin.x) ); + + memcpy( &(psEllipse->origin.y), psDGN->abyElem + 76, 8 ); + DGN2IEEEDouble( &(psEllipse->origin.y) ); + + memcpy( &(psEllipse->origin.z), psDGN->abyElem + 84, 8 ); + DGN2IEEEDouble( &(psEllipse->origin.z) ); + + psEllipse->quat[0] = DGN_INT32( psDGN->abyElem + 52 ); + psEllipse->quat[1] = DGN_INT32( psDGN->abyElem + 56 ); + psEllipse->quat[2] = DGN_INT32( psDGN->abyElem + 60 ); + psEllipse->quat[3] = DGN_INT32( psDGN->abyElem + 64 ); + } + + DGNTransformPoint( psDGN, &(psEllipse->origin) ); + + psEllipse->startang = 0.0; + psEllipse->sweepang = 360.0; + } + break; + + case DGNT_ARC: + { + DGNElemArc *psEllipse; + GInt32 nSweepVal; + + psEllipse = (DGNElemArc *) CPLCalloc(sizeof(DGNElemArc),1); + psElement = (DGNElemCore *) psEllipse; + psElement->stype = DGNST_ARC; + DGNParseCore( psDGN, psElement ); + + psEllipse->startang = DGN_INT32( psDGN->abyElem + 36 ); + psEllipse->startang = psEllipse->startang / 360000.0; + if( psDGN->abyElem[41] & 0x80 ) + { + psDGN->abyElem[41] &= 0x7f; + nSweepVal = -1 * DGN_INT32( psDGN->abyElem + 40 ); + } + else + nSweepVal = DGN_INT32( psDGN->abyElem + 40 ); + + if( nSweepVal == 0 ) + psEllipse->sweepang = 360.0; + else + psEllipse->sweepang = nSweepVal / 360000.0; + + memcpy( &(psEllipse->primary_axis), psDGN->abyElem + 44, 8 ); + DGN2IEEEDouble( &(psEllipse->primary_axis) ); + psEllipse->primary_axis *= psDGN->scale; + + memcpy( &(psEllipse->secondary_axis), psDGN->abyElem + 52, 8 ); + DGN2IEEEDouble( &(psEllipse->secondary_axis) ); + psEllipse->secondary_axis *= psDGN->scale; + + if( psDGN->dimension == 2 ) + { + psEllipse->rotation = DGN_INT32( psDGN->abyElem + 60 ); + psEllipse->rotation = psEllipse->rotation / 360000.0; + + memcpy( &(psEllipse->origin.x), psDGN->abyElem + 64, 8 ); + DGN2IEEEDouble( &(psEllipse->origin.x) ); + + memcpy( &(psEllipse->origin.y), psDGN->abyElem + 72, 8 ); + DGN2IEEEDouble( &(psEllipse->origin.y) ); + } + else + { + /* for now we don't try to handle quaternion */ + psEllipse->rotation = 0; + + memcpy( &(psEllipse->origin.x), psDGN->abyElem + 76, 8 ); + DGN2IEEEDouble( &(psEllipse->origin.x) ); + + memcpy( &(psEllipse->origin.y), psDGN->abyElem + 84, 8 ); + DGN2IEEEDouble( &(psEllipse->origin.y) ); + + memcpy( &(psEllipse->origin.z), psDGN->abyElem + 92, 8 ); + DGN2IEEEDouble( &(psEllipse->origin.z) ); + + psEllipse->quat[0] = DGN_INT32( psDGN->abyElem + 60 ); + psEllipse->quat[1] = DGN_INT32( psDGN->abyElem + 64 ); + psEllipse->quat[2] = DGN_INT32( psDGN->abyElem + 68 ); + psEllipse->quat[3] = DGN_INT32( psDGN->abyElem + 72 ); + } + + DGNTransformPoint( psDGN, &(psEllipse->origin) ); + } + break; + + case DGNT_TEXT: + { + DGNElemText *psText; + int num_chars, text_off; + + if( psDGN->dimension == 2 ) + num_chars = psDGN->abyElem[58]; + else + num_chars = psDGN->abyElem[74]; + + psText = (DGNElemText *) CPLCalloc(sizeof(DGNElemText)+num_chars,1); + psElement = (DGNElemCore *) psText; + psElement->stype = DGNST_TEXT; + DGNParseCore( psDGN, psElement ); + + psText->font_id = psDGN->abyElem[36]; + psText->justification = psDGN->abyElem[37]; + psText->length_mult = (DGN_INT32( psDGN->abyElem + 38 )) + * psDGN->scale * 6.0 / 1000.0; + psText->height_mult = (DGN_INT32( psDGN->abyElem + 42 )) + * psDGN->scale * 6.0 / 1000.0; + + if( psDGN->dimension == 2 ) + { + psText->rotation = DGN_INT32( psDGN->abyElem + 46 ); + psText->rotation = psText->rotation / 360000.0; + + psText->origin.x = DGN_INT32( psDGN->abyElem + 50 ); + psText->origin.y = DGN_INT32( psDGN->abyElem + 54 ); + text_off = 60; + } + else + { + /* leave quaternion for later */ + + psText->origin.x = DGN_INT32( psDGN->abyElem + 62 ); + psText->origin.y = DGN_INT32( psDGN->abyElem + 66 ); + psText->origin.z = DGN_INT32( psDGN->abyElem + 70 ); + text_off = 76; + } + + DGNTransformPoint( psDGN, &(psText->origin) ); + + /* experimental multibyte support from Ason Kang (hiska@netian.com)*/ + if (*(psDGN->abyElem + text_off) == 0xFF + && *(psDGN->abyElem + text_off + 1) == 0xFD) + { + int n=0; + for (int i=0;iabyElem + text_off + 2 + i*2 ,2); + w = CPL_LSBWORD16(w); + if (w<256) { // if alpa-numeric code area : Normal character + *(psText->string + n) = (char) (w & 0xFF); + n++; // skip 1 byte; + } + else { // if extend code area : 2 byte Korean character + *(psText->string + n) = (char) (w >> 8); // hi + *(psText->string + n + 1) = (char) (w & 0xFF); // lo + n+=2; // 2 byte + } + } + psText->string[n] = '\0'; // terminate C string + } + else + { + memcpy( psText->string, psDGN->abyElem + text_off, num_chars ); + psText->string[num_chars] = '\0'; + } + } + break; + + case DGNT_TCB: + psElement = DGNParseTCB( psDGN ); + break; + + case DGNT_COMPLEX_CHAIN_HEADER: + case DGNT_COMPLEX_SHAPE_HEADER: + { + DGNElemComplexHeader *psHdr; + + psHdr = (DGNElemComplexHeader *) + CPLCalloc(sizeof(DGNElemComplexHeader),1); + psElement = (DGNElemCore *) psHdr; + psElement->stype = DGNST_COMPLEX_HEADER; + DGNParseCore( psDGN, psElement ); + + psHdr->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; + psHdr->numelems = psDGN->abyElem[38] + psDGN->abyElem[39] * 256; + } + break; + + case DGNT_TAG_VALUE: + { + DGNElemTagValue *psTag; + + psTag = (DGNElemTagValue *) + CPLCalloc(sizeof(DGNElemTagValue),1); + psElement = (DGNElemCore *) psTag; + psElement->stype = DGNST_TAG_VALUE; + DGNParseCore( psDGN, psElement ); + + psTag->tagType = psDGN->abyElem[74] + psDGN->abyElem[75] * 256; + memcpy( &(psTag->tagSet), psDGN->abyElem + 68, 4 ); + psTag->tagSet = CPL_LSBWORD32(psTag->tagSet); + psTag->tagIndex = psDGN->abyElem[72] + psDGN->abyElem[73] * 256; + psTag->tagLength = psDGN->abyElem[150] + psDGN->abyElem[151] * 256; + + if( psTag->tagType == 1 ) + { + psTag->tagValue.string = + CPLStrdup( (char *) psDGN->abyElem + 154 ); + } + else if( psTag->tagType == 3 ) + { + memcpy( &(psTag->tagValue.integer), + psDGN->abyElem + 154, 4 ); + psTag->tagValue.integer = + CPL_LSBWORD32( psTag->tagValue.integer ); + } + else if( psTag->tagType == 4 ) + { + memcpy( &(psTag->tagValue.real), + psDGN->abyElem + 154, 8 ); + DGN2IEEEDouble( &(psTag->tagValue.real) ); + } + } + break; + + case DGNT_APPLICATION_ELEM: + if( nLevel == 24 ) + { + psElement = DGNParseTagSet( psDGN ); + } + else + { + psElement = (DGNElemCore *) CPLCalloc(sizeof(DGNElemCore),1); + psElement->stype = DGNST_CORE; + DGNParseCore( psDGN, psElement ); + } + break; + + case DGNT_CONE: + { + DGNElemCone *psCone; + + psCone = (DGNElemCone *) CPLCalloc(sizeof(DGNElemCone),1); + psElement = (DGNElemCore *) psCone; + psElement->stype = DGNST_CONE; + DGNParseCore( psDGN, psElement ); + + CPLAssert( psDGN->dimension == 3 ); + psCone->unknown = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; + psCone->quat[0] = DGN_INT32( psDGN->abyElem + 38 ); + psCone->quat[1] = DGN_INT32( psDGN->abyElem + 42 ); + psCone->quat[2] = DGN_INT32( psDGN->abyElem + 46 ); + psCone->quat[3] = DGN_INT32( psDGN->abyElem + 50 ); + + memcpy( &(psCone->center_1.x), psDGN->abyElem + 54, 8 ); + DGN2IEEEDouble( &(psCone->center_1.x) ); + memcpy( &(psCone->center_1.y), psDGN->abyElem + 62, 8 ); + DGN2IEEEDouble( &(psCone->center_1.y) ); + memcpy( &(psCone->center_1.z), psDGN->abyElem + 70, 8 ); + DGN2IEEEDouble( &(psCone->center_1.z) ); + memcpy( &(psCone->radius_1), psDGN->abyElem + 78, 8 ); + DGN2IEEEDouble( &(psCone->radius_1) ); + + memcpy( &(psCone->center_2.x), psDGN->abyElem + 86, 8 ); + DGN2IEEEDouble( &(psCone->center_2.x) ); + memcpy( &(psCone->center_2.y), psDGN->abyElem + 94, 8 ); + DGN2IEEEDouble( &(psCone->center_2.y) ); + memcpy( &(psCone->center_2.z), psDGN->abyElem + 102, 8 ); + DGN2IEEEDouble( &(psCone->center_2.z) ); + memcpy( &(psCone->radius_2), psDGN->abyElem + 110, 8 ); + DGN2IEEEDouble( &(psCone->radius_2) ); + + psCone->radius_1 *= psDGN->scale; + psCone->radius_2 *= psDGN->scale; + DGNTransformPoint( psDGN, &psCone->center_1 ); + DGNTransformPoint( psDGN, &psCone->center_2 ); + } + break; + + case DGNT_3DSURFACE_HEADER: + case DGNT_3DSOLID_HEADER: + { + DGNElemComplexHeader *psShape; + + psShape = + (DGNElemComplexHeader *) CPLCalloc(sizeof(DGNElemComplexHeader),1); + psElement = (DGNElemCore *) psShape; + psElement->stype = DGNST_COMPLEX_HEADER; + DGNParseCore( psDGN, psElement ); + + // Read complex header + psShape->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; + psShape->numelems = psDGN->abyElem[38] + psDGN->abyElem[39] * 256; + psShape->surftype = psDGN->abyElem[40]; + psShape->boundelms = psDGN->abyElem[41] + 1; + } + break; + case DGNT_BSPLINE_SURFACE_HEADER: + { + DGNElemBSplineSurfaceHeader *psSpline; + + psSpline = (DGNElemBSplineSurfaceHeader *) + CPLCalloc(sizeof(DGNElemBSplineSurfaceHeader), 1); + psElement = (DGNElemCore *) psSpline; + psElement->stype = DGNST_BSPLINE_SURFACE_HEADER; + DGNParseCore( psDGN, psElement ); + + // Read B-Spline surface header + psSpline->desc_words = DGN_INT32(psDGN->abyElem + 36); + psSpline->curve_type = psDGN->abyElem[41]; + + // U + psSpline->u_order = (psDGN->abyElem[40] & 0x0f) + 2; + psSpline->u_properties = psDGN->abyElem[40] & 0xf0; + psSpline->num_poles_u = psDGN->abyElem[42] + psDGN->abyElem[43]*256; + psSpline->num_knots_u = psDGN->abyElem[44] + psDGN->abyElem[45]*256; + psSpline->rule_lines_u = psDGN->abyElem[46] + psDGN->abyElem[47]*256; + + // V + psSpline->v_order = (psDGN->abyElem[48] & 0x0f) + 2; + psSpline->v_properties = psDGN->abyElem[48] & 0xf0; + psSpline->num_poles_v = psDGN->abyElem[50] + psDGN->abyElem[51]*256; + psSpline->num_knots_v = psDGN->abyElem[52] + psDGN->abyElem[53]*256; + psSpline->rule_lines_v = psDGN->abyElem[54] + psDGN->abyElem[55]*256; + + psSpline->num_bounds = psDGN->abyElem[56] + psDGN->abyElem[57]*556; + } + break; + case DGNT_BSPLINE_CURVE_HEADER: + { + DGNElemBSplineCurveHeader *psSpline; + + psSpline = (DGNElemBSplineCurveHeader *) + CPLCalloc(sizeof(DGNElemBSplineCurveHeader), 1); + psElement = (DGNElemCore *) psSpline; + psElement->stype = DGNST_BSPLINE_CURVE_HEADER; + DGNParseCore( psDGN, psElement ); + + // Read B-Spline curve header + psSpline->desc_words = DGN_INT32(psDGN->abyElem + 36); + + // flags + psSpline->order = (psDGN->abyElem[40] & 0x0f) + 2; + psSpline->properties = psDGN->abyElem[40] & 0xf0; + psSpline->curve_type = psDGN->abyElem[41]; + + psSpline->num_poles = psDGN->abyElem[42] + psDGN->abyElem[43]*256; + psSpline->num_knots = psDGN->abyElem[44] + psDGN->abyElem[45]*256; + } + break; + case DGNT_BSPLINE_SURFACE_BOUNDARY: + { + DGNElemBSplineSurfaceBoundary *psBounds; + short numverts = psDGN->abyElem[38] + psDGN->abyElem[39]*256; + + psBounds = (DGNElemBSplineSurfaceBoundary *) + CPLCalloc(sizeof(DGNElemBSplineSurfaceBoundary)+ + (numverts-1)*sizeof(DGNPoint), 1); + psElement = (DGNElemCore *) psBounds; + psElement->stype = DGNST_BSPLINE_SURFACE_BOUNDARY; + DGNParseCore( psDGN, psElement ); + + // Read B-Spline surface boundary + psBounds->number = psDGN->abyElem[36] + psDGN->abyElem[37]*256; + psBounds->numverts = numverts; + + for (int i=0;inumverts;i++) { + psBounds->vertices[i].x = DGN_INT32( psDGN->abyElem + 40 + i*8 ); + psBounds->vertices[i].y = DGN_INT32( psDGN->abyElem + 44 + i*8 ); + psBounds->vertices[i].z = 0; + } + } + break; + case DGNT_BSPLINE_KNOT: + case DGNT_BSPLINE_WEIGHT_FACTOR: + { + DGNElemKnotWeight *psArray; + // FIXME: Is it OK to assume that the # of elements corresponds + // directly to the element size? kintel 20051215. + int attr_bytes = psDGN->nElemBytes - + (psDGN->abyElem[30] + psDGN->abyElem[31]*256)*2 - 32; + int numelems = (psDGN->nElemBytes - 36 - attr_bytes)/4; + + psArray = (DGNElemKnotWeight *) + CPLCalloc(sizeof(DGNElemKnotWeight) + (numelems-1)*sizeof(float), 1); + + psElement = (DGNElemCore *) psArray; + psElement->stype = DGNST_KNOT_WEIGHT; + DGNParseCore( psDGN, psElement ); + + // Read array + for (int i=0;iarray[i] = + 1.0f * DGN_INT32(psDGN->abyElem + 36 + i*4) / ((1UL << 31) - 1); + } + } + break; + case DGNT_SHARED_CELL_DEFN: + { + DGNElemSharedCellDefn *psShared; + + psShared = (DGNElemSharedCellDefn *) + CPLCalloc(sizeof(DGNElemSharedCellDefn),1); + psElement = (DGNElemCore *) psShared; + psElement->stype = DGNST_SHARED_CELL_DEFN; + DGNParseCore( psDGN, psElement ); + + psShared->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; + } + break; + default: + { + psElement = (DGNElemCore *) CPLCalloc(sizeof(DGNElemCore),1); + psElement->stype = DGNST_CORE; + DGNParseCore( psDGN, psElement ); + } + break; + } + +/* -------------------------------------------------------------------- */ +/* If the element structure type is "core" or if we are running */ +/* in "capture all" mode, record the complete binary image of */ +/* the element. */ +/* -------------------------------------------------------------------- */ + if( psElement->stype == DGNST_CORE + || (psDGN->options & DGNO_CAPTURE_RAW_DATA) ) + { + psElement->raw_bytes = psDGN->nElemBytes; + psElement->raw_data = (unsigned char *)CPLMalloc(psElement->raw_bytes); + + memcpy( psElement->raw_data, psDGN->abyElem, psElement->raw_bytes ); + } + +/* -------------------------------------------------------------------- */ +/* Collect some additional generic information. */ +/* -------------------------------------------------------------------- */ + psElement->element_id = psDGN->next_element_id - 1; + + psElement->offset = VSIFTell( psDGN->fp ) - psDGN->nElemBytes; + psElement->size = psDGN->nElemBytes; + + return psElement; +} + +/************************************************************************/ +/* DGNReadElement() */ +/************************************************************************/ + +/** + * Read a DGN element. + * + * This function will return the next element in the file, starting with the + * first. It is affected by DGNGotoElement() calls. + * + * The element is read into a structure which includes the DGNElemCore + * structure. It is expected that applications will inspect the stype + * field of the returned DGNElemCore and use it to cast the pointer to the + * appropriate element structure type such as DGNElemMultiPoint. + * + * @param hDGN the handle of the file to read from. + * + * @return pointer to element structure, or NULL on EOF or processing error. + * The structure should be freed with DGNFreeElement() when no longer needed. + */ + +DGNElemCore *DGNReadElement( DGNHandle hDGN ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + DGNElemCore *psElement = NULL; + int nType, nLevel; + int bInsideFilter; + +/* -------------------------------------------------------------------- */ +/* Load the element data into the current buffer. If a spatial */ +/* filter is in effect, loop until we get something within our */ +/* spatial constraints. */ +/* -------------------------------------------------------------------- */ + do { + bInsideFilter = TRUE; + + if( !DGNLoadRawElement( psDGN, &nType, &nLevel ) ) + return NULL; + + if( psDGN->has_spatial_filter ) + { + GUInt32 nXMin, nXMax, nYMin, nYMax; + + if( !psDGN->sf_converted_to_uor ) + DGNSpatialFilterToUOR( psDGN ); + + if( !DGNGetRawExtents( psDGN, nType, NULL, + &nXMin, &nYMin, NULL, + &nXMax, &nYMax, NULL ) ) + { + /* If we don't have spatial characterists for the element + we will pass it through. */ + bInsideFilter = TRUE; + } + else if( nXMin > psDGN->sf_max_x + || nYMin > psDGN->sf_max_y + || nXMax < psDGN->sf_min_x + || nYMax < psDGN->sf_min_y ) + bInsideFilter = FALSE; + + /* + ** We want to select complex elements based on the extents of + ** the header, not the individual elements. + */ + if( nType == DGNT_COMPLEX_CHAIN_HEADER + || nType == DGNT_COMPLEX_SHAPE_HEADER ) + { + psDGN->in_complex_group = TRUE; + psDGN->select_complex_group = bInsideFilter; + } + else if( psDGN->abyElem[0] & 0x80 /* complex flag set */ ) + { + if( psDGN->in_complex_group ) + bInsideFilter = psDGN->select_complex_group; + } + else + psDGN->in_complex_group = FALSE; + } + } while( !bInsideFilter ); + +/* -------------------------------------------------------------------- */ +/* Convert into an element structure. */ +/* -------------------------------------------------------------------- */ + psElement = DGNProcessElement( psDGN, nType, nLevel ); + + return psElement; +} + +/************************************************************************/ +/* DGNElemTypeHasDispHdr() */ +/************************************************************************/ + +/** + * Does element type have display header. + * + * @param nElemType element type (0-63) to test. + * + * @return TRUE if elements of passed in type have a display header after the + * core element header, or FALSE otherwise. + */ + +int DGNElemTypeHasDispHdr( int nElemType ) + +{ + switch( nElemType ) + { + case 0: + case DGNT_TCB: + case DGNT_CELL_LIBRARY: + case DGNT_LEVEL_SYMBOLOGY: + case 32: + case 44: + case 48: + case 49: + case 50: + case 51: + case 57: + case 60: + case 61: + case 62: + case 63: + return FALSE; + + default: + return TRUE; + } +} + + +/************************************************************************/ +/* DGNParseCore() */ +/************************************************************************/ + +int DGNParseCore( DGNInfo *psDGN, DGNElemCore *psElement ) + +{ + GByte *psData = psDGN->abyElem+0; + + psElement->level = psData[0] & 0x3f; + psElement->complex = psData[0] & 0x80; + psElement->deleted = psData[1] & 0x80; + psElement->type = psData[1] & 0x7f; + + if( psDGN->nElemBytes >= 36 && DGNElemTypeHasDispHdr( psElement->type ) ) + { + psElement->graphic_group = psData[28] + psData[29] * 256; + psElement->properties = psData[32] + psData[33] * 256; + psElement->style = psData[34] & 0x7; + psElement->weight = (psData[34] & 0xf8) >> 3; + psElement->color = psData[35]; + } + else + { + psElement->graphic_group = 0; + psElement->properties = 0; + psElement->style = 0; + psElement->weight = 0; + psElement->color = 0; + } + + if( psElement->properties & DGNPF_ATTRIBUTES ) + { + int nAttIndex; + + nAttIndex = psData[30] + psData[31] * 256; + + psElement->attr_bytes = psDGN->nElemBytes - nAttIndex*2 - 32; + if( psElement->attr_bytes > 0 ) + { + psElement->attr_data = (unsigned char *) + CPLMalloc(psElement->attr_bytes); + memcpy( psElement->attr_data, psData + nAttIndex * 2 + 32, + psElement->attr_bytes ); + } + else + { + CPLError( + CE_Warning, CPLE_AppDefined, + "Computed %d bytes for attribute info on element,\n" + "perhaps this element type doesn't really have a disphdr?", + psElement->attr_bytes ); + psElement->attr_bytes = 0; + } + } + + return TRUE; +} + +/************************************************************************/ +/* DGNParseColorTable() */ +/************************************************************************/ + +static DGNElemCore *DGNParseColorTable( DGNInfo * psDGN ) + +{ + DGNElemCore *psElement; + DGNElemColorTable *psColorTable; + + psColorTable = (DGNElemColorTable *) + CPLCalloc(sizeof(DGNElemColorTable),1); + psElement = (DGNElemCore *) psColorTable; + psElement->stype = DGNST_COLORTABLE; + + DGNParseCore( psDGN, psElement ); + + psColorTable->screen_flag = + psDGN->abyElem[36] + psDGN->abyElem[37] * 256; + + memcpy( psColorTable->color_info[255], psDGN->abyElem+38, 3 ); + memcpy( psColorTable->color_info, psDGN->abyElem+41, 765 ); + + // We used to only install a color table as the default color + // table if it was the first in the file. But apparently we should + // really be using the last one. This doesn't necessarily accomplish + // that either if the elements are being read out of order but it will + // usually do better at least. + memcpy( psDGN->color_table, psColorTable->color_info, 768 ); + psDGN->got_color_table = 1; + + return psElement; +} + +/************************************************************************/ +/* DGNParseTagSet() */ +/************************************************************************/ + +static DGNElemCore *DGNParseTagSet( DGNInfo * psDGN ) + +{ + DGNElemCore *psElement; + DGNElemTagSet *psTagSet; + int nDataOffset, iTag; + + psTagSet = (DGNElemTagSet *) CPLCalloc(sizeof(DGNElemTagSet),1); + psElement = (DGNElemCore *) psTagSet; + psElement->stype = DGNST_TAG_SET; + + DGNParseCore( psDGN, psElement ); + +/* -------------------------------------------------------------------- */ +/* Parse the overall information. */ +/* -------------------------------------------------------------------- */ + psTagSet->tagCount = + psDGN->abyElem[44] + psDGN->abyElem[45] * 256; + psTagSet->flags = + psDGN->abyElem[46] + psDGN->abyElem[47] * 256; + psTagSet->tagSetName = CPLStrdup( (const char *) (psDGN->abyElem + 48) ); + +/* -------------------------------------------------------------------- */ +/* Get the tag set number out of the attributes, if available. */ +/* -------------------------------------------------------------------- */ + psTagSet->tagSet = -1; + + if( psElement->attr_bytes >= 8 + && psElement->attr_data[0] == 0x03 + && psElement->attr_data[1] == 0x10 + && psElement->attr_data[2] == 0x2f + && psElement->attr_data[3] == 0x7d ) + psTagSet->tagSet = psElement->attr_data[4] + + psElement->attr_data[5] * 256; + +/* -------------------------------------------------------------------- */ +/* Parse each of the tag definitions. */ +/* -------------------------------------------------------------------- */ + psTagSet->tagList = (DGNTagDef *) + CPLMalloc(sizeof(DGNTagDef) * psTagSet->tagCount); + + nDataOffset = 48 + strlen(psTagSet->tagSetName) + 1 + 1; + + for( iTag = 0; iTag < psTagSet->tagCount; iTag++ ) + { + DGNTagDef *tagDef = psTagSet->tagList + iTag; + + CPLAssert( nDataOffset < psDGN->nElemBytes ); + + /* collect tag name. */ + tagDef->name = CPLStrdup( (char *) psDGN->abyElem + nDataOffset ); + nDataOffset += strlen(tagDef->name)+1; + + /* Get tag id */ + tagDef->id = psDGN->abyElem[nDataOffset] + + psDGN->abyElem[nDataOffset+1] * 256; + nDataOffset += 2; + + /* Get User Prompt */ + tagDef->prompt = CPLStrdup( (char *) psDGN->abyElem + nDataOffset ); + nDataOffset += strlen(tagDef->prompt)+1; + + + /* Get type */ + tagDef->type = psDGN->abyElem[nDataOffset] + + psDGN->abyElem[nDataOffset+1] * 256; + nDataOffset += 2; + + /* skip five zeros */ + nDataOffset += 5; + + /* Get the default */ + if( tagDef->type == 1 ) + { + tagDef->defaultValue.string = + CPLStrdup( (char *) psDGN->abyElem + nDataOffset ); + nDataOffset += strlen(tagDef->defaultValue.string)+1; + } + else if( tagDef->type == 3 || tagDef->type == 5 ) + { + memcpy( &(tagDef->defaultValue.integer), + psDGN->abyElem + nDataOffset, 4 ); + tagDef->defaultValue.integer = + CPL_LSBWORD32( tagDef->defaultValue.integer ); + nDataOffset += 4; + } + else if( tagDef->type == 4 ) + { + memcpy( &(tagDef->defaultValue.real), + psDGN->abyElem + nDataOffset, 8 ); + DGN2IEEEDouble( &(tagDef->defaultValue.real) ); + nDataOffset += 8; + } + else + nDataOffset += 4; + } + return psElement; +} + +/************************************************************************/ +/* DGNParseTCB() */ +/************************************************************************/ + +static DGNElemCore *DGNParseTCB( DGNInfo * psDGN ) + +{ + DGNElemTCB *psTCB; + DGNElemCore *psElement; + int iView; + + psTCB = (DGNElemTCB *) CPLCalloc(sizeof(DGNElemTCB),1); + psElement = (DGNElemCore *) psTCB; + psElement->stype = DGNST_TCB; + DGNParseCore( psDGN, psElement ); + + if( psDGN->abyElem[1214] & 0x40 ) + psTCB->dimension = 3; + else + psTCB->dimension = 2; + + psTCB->subunits_per_master = DGN_INT32( psDGN->abyElem + 1112 ); + + psTCB->master_units[0] = (char) psDGN->abyElem[1120]; + psTCB->master_units[1] = (char) psDGN->abyElem[1121]; + psTCB->master_units[2] = '\0'; + + psTCB->uor_per_subunit = DGN_INT32( psDGN->abyElem + 1116 ); + + psTCB->sub_units[0] = (char) psDGN->abyElem[1122]; + psTCB->sub_units[1] = (char) psDGN->abyElem[1123]; + psTCB->sub_units[2] = '\0'; + + /* Get global origin */ + memcpy( &(psTCB->origin_x), psDGN->abyElem+1240, 8 ); + memcpy( &(psTCB->origin_y), psDGN->abyElem+1248, 8 ); + memcpy( &(psTCB->origin_z), psDGN->abyElem+1256, 8 ); + + /* Transform to IEEE */ + DGN2IEEEDouble( &(psTCB->origin_x) ); + DGN2IEEEDouble( &(psTCB->origin_y) ); + DGN2IEEEDouble( &(psTCB->origin_z) ); + + /* Convert from UORs to master units. */ + if( psTCB->uor_per_subunit != 0 + && psTCB->subunits_per_master != 0 ) + { + psTCB->origin_x = psTCB->origin_x / + (psTCB->uor_per_subunit * psTCB->subunits_per_master); + psTCB->origin_y = psTCB->origin_y / + (psTCB->uor_per_subunit * psTCB->subunits_per_master); + psTCB->origin_z = psTCB->origin_z / + (psTCB->uor_per_subunit * psTCB->subunits_per_master); + } + + if( !psDGN->got_tcb ) + { + psDGN->got_tcb = TRUE; + psDGN->dimension = psTCB->dimension; + psDGN->origin_x = psTCB->origin_x; + psDGN->origin_y = psTCB->origin_y; + psDGN->origin_z = psTCB->origin_z; + + if( psTCB->uor_per_subunit != 0 + && psTCB->subunits_per_master != 0 ) + psDGN->scale = 1.0 + / (psTCB->uor_per_subunit * psTCB->subunits_per_master); + } + + /* Collect views */ + for( iView = 0; iView < 8; iView++ ) + { + unsigned char *pabyRawView = psDGN->abyElem + 46 + iView*118; + DGNViewInfo *psView = psTCB->views + iView; + int i; + + psView->flags = pabyRawView[0] + pabyRawView[1] * 256; + memcpy( psView->levels, pabyRawView + 2, 8 ); + + psView->origin.x = DGN_INT32( pabyRawView + 10 ); + psView->origin.y = DGN_INT32( pabyRawView + 14 ); + psView->origin.z = DGN_INT32( pabyRawView + 18 ); + + DGNTransformPoint( psDGN, &(psView->origin) ); + + psView->delta.x = DGN_INT32( pabyRawView + 22 ); + psView->delta.y = DGN_INT32( pabyRawView + 26 ); + psView->delta.z = DGN_INT32( pabyRawView + 30 ); + + psView->delta.x *= psDGN->scale; + psView->delta.y *= psDGN->scale; + psView->delta.z *= psDGN->scale; + + memcpy( psView->transmatrx, pabyRawView + 34, sizeof(double) * 9 ); + for( i = 0; i < 9; i++ ) + DGN2IEEEDouble( psView->transmatrx + i ); + + memcpy( &(psView->conversion), pabyRawView + 106, sizeof(double) ); + DGN2IEEEDouble( &(psView->conversion) ); + + psView->activez = DGN_INT32( pabyRawView + 114 ); + } + + return psElement; +} + +/************************************************************************/ +/* DGNFreeElement() */ +/************************************************************************/ + +/** + * Free an element structure. + * + * This function will deallocate all resources associated with any element + * structure returned by DGNReadElement(). + * + * @param hDGN handle to file from which the element was read. + * @param psElement the element structure returned by DGNReadElement(). + */ + +void DGNFreeElement( CPL_UNUSED DGNHandle hDGN, DGNElemCore *psElement ) +{ + if( psElement->attr_data != NULL ) + VSIFree( psElement->attr_data ); + + if( psElement->raw_data != NULL ) + VSIFree( psElement->raw_data ); + + if( psElement->stype == DGNST_TAG_SET ) + { + int iTag; + + DGNElemTagSet *psTagSet = (DGNElemTagSet *) psElement; + CPLFree( psTagSet->tagSetName ); + + for( iTag = 0; iTag < psTagSet->tagCount; iTag++ ) + { + CPLFree( psTagSet->tagList[iTag].name ); + CPLFree( psTagSet->tagList[iTag].prompt ); + + if( psTagSet->tagList[iTag].type == 1 ) + CPLFree( psTagSet->tagList[iTag].defaultValue.string ); + } + CPLFree( psTagSet->tagList ); + } + else if( psElement->stype == DGNST_TAG_VALUE ) + { + if( ((DGNElemTagValue *) psElement)->tagType == 1 ) + CPLFree( ((DGNElemTagValue *) psElement)->tagValue.string ); + } + + CPLFree( psElement ); +} + +/************************************************************************/ +/* DGNRewind() */ +/************************************************************************/ + +/** + * Rewind element reading. + * + * Rewind the indicated DGN file, so the next element read with + * DGNReadElement() will be the first. Does not require indexing like + * the more general DGNReadElement() function. + * + * @param hDGN handle to file. + */ + +void DGNRewind( DGNHandle hDGN ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + + VSIRewind( psDGN->fp ); + + psDGN->next_element_id = 0; + psDGN->in_complex_group = FALSE; +} + +/************************************************************************/ +/* DGNTransformPoint() */ +/************************************************************************/ + +void DGNTransformPoint( DGNInfo *psDGN, DGNPoint *psPoint ) + +{ + psPoint->x = psPoint->x * psDGN->scale - psDGN->origin_x; + psPoint->y = psPoint->y * psDGN->scale - psDGN->origin_y; + psPoint->z = psPoint->z * psDGN->scale - psDGN->origin_z; +} + +/************************************************************************/ +/* DGNInverseTransformPoint() */ +/************************************************************************/ + +void DGNInverseTransformPoint( DGNInfo *psDGN, DGNPoint *psPoint ) + +{ + psPoint->x = (psPoint->x + psDGN->origin_x) / psDGN->scale; + psPoint->y = (psPoint->y + psDGN->origin_y) / psDGN->scale; + psPoint->z = (psPoint->z + psDGN->origin_z) / psDGN->scale; + + psPoint->x = MAX(-2147483647,MIN(2147483647,psPoint->x)); + psPoint->y = MAX(-2147483647,MIN(2147483647,psPoint->y)); + psPoint->z = MAX(-2147483647,MIN(2147483647,psPoint->z)); +} + +/************************************************************************/ +/* DGNInverseTransformPointToInt() */ +/************************************************************************/ + +void DGNInverseTransformPointToInt( DGNInfo *psDGN, DGNPoint *psPoint, + unsigned char *pabyTarget ) + +{ + double adfCT[3]; + int i; + + adfCT[0] = (psPoint->x + psDGN->origin_x) / psDGN->scale; + adfCT[1] = (psPoint->y + psDGN->origin_y) / psDGN->scale; + adfCT[2] = (psPoint->z + psDGN->origin_z) / psDGN->scale; + + for( i = 0; i < psDGN->dimension; i++ ) + { + GInt32 nCTI; + unsigned char *pabyCTI = (unsigned char *) &nCTI; + + nCTI = (GInt32) MAX(-2147483647,MIN(2147483647,adfCT[i])); + +#ifdef WORDS_BIGENDIAN + pabyTarget[i*4+0] = pabyCTI[1]; + pabyTarget[i*4+1] = pabyCTI[0]; + pabyTarget[i*4+2] = pabyCTI[3]; + pabyTarget[i*4+3] = pabyCTI[2]; +#else + pabyTarget[i*4+3] = pabyCTI[1]; + pabyTarget[i*4+2] = pabyCTI[0]; + pabyTarget[i*4+1] = pabyCTI[3]; + pabyTarget[i*4+0] = pabyCTI[2]; +#endif + } +} + +/************************************************************************/ +/* DGNLoadTCB() */ +/************************************************************************/ + +/** + * Load TCB if not already loaded. + * + * This function will load the TCB element if it is not already loaded. + * It is used primarily to ensure the TCB is loaded before doing any operations + * that require TCB values (like creating new elements). + * + * @return FALSE on failure or TRUE on success. + */ + +int DGNLoadTCB( DGNHandle hDGN ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + + if( psDGN->got_tcb ) + return TRUE; + + while( !psDGN->got_tcb ) + { + DGNElemCore *psElem = DGNReadElement( hDGN ); + if( psElem == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DGNLoadTCB() - unable to find TCB in file." ); + return FALSE; + } + DGNFreeElement( hDGN, psElem ); + } + + return TRUE; +} + +/************************************************************************/ +/* DGNGetElementIndex() */ +/************************************************************************/ + +/** + * Fetch element index. + * + * This function will return an array with brief information about every + * element in a DGN file. It requires one pass through the entire file to + * generate (this is not repeated on subsequent calls). + * + * The returned array of DGNElementInfo structures contain the level, type, + * stype, and other flags for each element in the file. This can facilitate + * application level code representing the number of elements of various types + * effeciently. + * + * Note that while building the index requires one pass through the whole file, + * it does not generally request much processing for each element. + * + * @param hDGN the file to get an index for. + * @param pnElementCount the integer to put the total element count into. + * + * @return a pointer to an internal array of DGNElementInfo structures (there + * will be *pnElementCount entries in the array), or NULL on failure. The + * returned array should not be modified or freed, and will last only as long + * as the DGN file remains open. + */ + +const DGNElementInfo *DGNGetElementIndex( DGNHandle hDGN, int *pnElementCount ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + + DGNBuildIndex( psDGN ); + + if( pnElementCount != NULL ) + *pnElementCount = psDGN->element_count; + + return psDGN->element_index; +} + +/************************************************************************/ +/* DGNGetExtents() */ +/************************************************************************/ + +/** + * Fetch overall file extents. + * + * The extents are collected for each element while building an index, so + * if an index has not already been built, it will be built when + * DGNGetExtents() is called. + * + * The Z min/max values are generally meaningless (0 and 0xffffffff in uor + * space). + * + * @param hDGN the file to get extents for. + * @param padfExtents pointer to an array of six doubles into which are loaded + * the values xmin, ymin, zmin, xmax, ymax, and zmax. + * + * @return TRUE on success or FALSE on failure. + */ + +int DGNGetExtents( DGNHandle hDGN, double * padfExtents ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + DGNPoint sMin, sMax; + + DGNBuildIndex( psDGN ); + + if( !psDGN->got_bounds ) + return FALSE; + + sMin.x = psDGN->min_x - 2147483648.0; + sMin.y = psDGN->min_y - 2147483648.0; + sMin.z = psDGN->min_z - 2147483648.0; + + DGNTransformPoint( psDGN, &sMin ); + + padfExtents[0] = sMin.x; + padfExtents[1] = sMin.y; + padfExtents[2] = sMin.z; + + sMax.x = psDGN->max_x - 2147483648.0; + sMax.y = psDGN->max_y - 2147483648.0; + sMax.z = psDGN->max_z - 2147483648.0; + + DGNTransformPoint( psDGN, &sMax ); + + padfExtents[3] = sMax.x; + padfExtents[4] = sMax.y; + padfExtents[5] = sMax.z; + + return TRUE; +} + +/************************************************************************/ +/* DGNBuildIndex() */ +/************************************************************************/ + +void DGNBuildIndex( DGNInfo *psDGN ) + +{ + int nMaxElements, nType, nLevel; + long nLastOffset; + GUInt32 anRegion[6]; + + if( psDGN->index_built ) + return; + + psDGN->index_built = TRUE; + + DGNRewind( psDGN ); + + nMaxElements = 0; + + nLastOffset = VSIFTell( psDGN->fp ); + while( DGNLoadRawElement( psDGN, &nType, &nLevel ) ) + { + DGNElementInfo *psEI; + + if( psDGN->element_count == nMaxElements ) + { + nMaxElements = (int) (nMaxElements * 1.5) + 500; + + psDGN->element_index = (DGNElementInfo *) + CPLRealloc( psDGN->element_index, + nMaxElements * sizeof(DGNElementInfo) ); + } + + psEI = psDGN->element_index + psDGN->element_count; + psEI->level = (unsigned char) nLevel; + psEI->type = (unsigned char) nType; + psEI->flags = 0; + psEI->offset = (long) nLastOffset; + + if( psDGN->abyElem[0] & 0x80 ) + psEI->flags |= DGNEIF_COMPLEX; + + if( psDGN->abyElem[1] & 0x80 ) + psEI->flags |= DGNEIF_DELETED; + + if( nType == DGNT_LINE || nType == DGNT_LINE_STRING + || nType == DGNT_SHAPE || nType == DGNT_CURVE + || nType == DGNT_BSPLINE_POLE ) + psEI->stype = DGNST_MULTIPOINT; + + else if( nType == DGNT_GROUP_DATA && nLevel == DGN_GDL_COLOR_TABLE ) + { + DGNElemCore *psCT = DGNParseColorTable( psDGN ); + DGNFreeElement( (DGNHandle) psDGN, psCT ); + psEI->stype = DGNST_COLORTABLE; + } + else if( nType == DGNT_ELLIPSE || nType == DGNT_ARC ) + psEI->stype = DGNST_ARC; + + else if( nType == DGNT_COMPLEX_SHAPE_HEADER + || nType == DGNT_COMPLEX_CHAIN_HEADER + || nType == DGNT_3DSURFACE_HEADER + || nType == DGNT_3DSOLID_HEADER) + psEI->stype = DGNST_COMPLEX_HEADER; + + else if( nType == DGNT_TEXT ) + psEI->stype = DGNST_TEXT; + + else if( nType == DGNT_TAG_VALUE ) + psEI->stype = DGNST_TAG_VALUE; + + else if( nType == DGNT_APPLICATION_ELEM ) + { + if( nLevel == 24 ) + psEI->stype = DGNST_TAG_SET; + else + psEI->stype = DGNST_CORE; + } + else if( nType == DGNT_TCB ) + { + DGNElemCore *psTCB = DGNParseTCB( psDGN ); + DGNFreeElement( (DGNHandle) psDGN, psTCB ); + psEI->stype = DGNST_TCB; + } + else if( nType == DGNT_CONE ) + psEI->stype = DGNST_CONE; + else + psEI->stype = DGNST_CORE; + + if( !(psEI->flags & DGNEIF_DELETED) + && !(psEI->flags & DGNEIF_COMPLEX) + && DGNGetRawExtents( psDGN, nType, NULL, + anRegion+0, anRegion+1, anRegion+2, + anRegion+3, anRegion+4, anRegion+5 ) ) + { +#ifdef notdef + printf( "panRegion[%d]=%.1f,%.1f,%.1f,%.1f,%.1f,%.1f\n", + psDGN->element_count, + anRegion[0] - 2147483648.0, + anRegion[1] - 2147483648.0, + anRegion[2] - 2147483648.0, + anRegion[3] - 2147483648.0, + anRegion[4] - 2147483648.0, + anRegion[5] - 2147483648.0 ); +#endif + if( psDGN->got_bounds ) + { + psDGN->min_x = MIN(psDGN->min_x, anRegion[0]); + psDGN->min_y = MIN(psDGN->min_y, anRegion[1]); + psDGN->min_z = MIN(psDGN->min_z, anRegion[2]); + psDGN->max_x = MAX(psDGN->max_x, anRegion[3]); + psDGN->max_y = MAX(psDGN->max_y, anRegion[4]); + psDGN->max_z = MAX(psDGN->max_z, anRegion[5]); + } + else + { + memcpy( &(psDGN->min_x), anRegion, sizeof(GInt32) * 6 ); + psDGN->got_bounds = TRUE; + } + } + + psDGN->element_count++; + + nLastOffset = VSIFTell( psDGN->fp ); + } + + DGNRewind( psDGN ); + + psDGN->max_element_count = nMaxElements; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnstroke.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnstroke.cpp new file mode 100644 index 000000000..a7386de19 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnstroke.cpp @@ -0,0 +1,328 @@ +/****************************************************************************** + * $Id: dgnstroke.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: Microstation DGN Access Library + * Purpose: Code to stroke Arcs/Ellipses into polylines. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Avenza Systems Inc, http://www.avenza.com/ + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "dgnlibp.h" +#include + +CPL_CVSID("$Id: dgnstroke.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +#define DEG_TO_RAD (PI/180.0) + +/************************************************************************/ +/* ComputePointOnArc() */ +/************************************************************************/ + +static void ComputePointOnArc2D( double dfPrimary, double dfSecondary, + double dfAxisRotation, double dfAngle, + double *pdfX, double *pdfY ) + +{ + //dfAxisRotation and dfAngle are suposed to be in Radians + double dfCosRotation = cos(dfAxisRotation); + double dfSinRotation = sin(dfAxisRotation); + double dfEllipseX = dfPrimary * cos(dfAngle); + double dfEllipseY = dfSecondary * sin(dfAngle); + + *pdfX = dfEllipseX * dfCosRotation - dfEllipseY * dfSinRotation; + *pdfY = dfEllipseX * dfSinRotation + dfEllipseY * dfCosRotation; +} + +/************************************************************************/ +/* DGNStrokeArc() */ +/************************************************************************/ + +/** + * Generate a polyline approximation of an arc. + * + * Produce a series of equidistant (actually equi-angle) points along + * an arc. Currently this only works for 2D arcs (and ellipses). + * + * @param hFile the DGN file to which the arc belongs (currently not used). + * @param psArc the arc to be approximated. + * @param nPoints the number of points to use to approximate the arc. + * @param pasPoints the array of points into which to put the results. + * There must be room for at least nPoints points. + * + * @return TRUE on success or FALSE on failure. + */ + +int DGNStrokeArc( CPL_UNUSED DGNHandle hFile, + DGNElemArc *psArc, + int nPoints, DGNPoint * pasPoints ) +{ + double dfAngleStep, dfAngle; + int i; + + if( nPoints < 2 ) + return FALSE; + + if( psArc->primary_axis == 0.0 || psArc->secondary_axis == 0.0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Zero primary or secondary axis in DGNStrokeArc()." ); + return FALSE; + } + + dfAngleStep = psArc->sweepang / (nPoints - 1); + for( i = 0; i < nPoints; i++ ) + { + dfAngle = (psArc->startang + dfAngleStep * i) * DEG_TO_RAD; + + ComputePointOnArc2D( psArc->primary_axis, + psArc->secondary_axis, + psArc->rotation * DEG_TO_RAD, + dfAngle, + &(pasPoints[i].x), + &(pasPoints[i].y) ); + pasPoints[i].x += psArc->origin.x; + pasPoints[i].y += psArc->origin.y; + pasPoints[i].z = psArc->origin.z; + } + + return TRUE; +} + +/************************************************************************/ +/* DGNStrokeCurve() */ +/************************************************************************/ + +/** + * Generate a polyline approximation of an curve. + * + * Produce a series of equidistant points along a microstation curve element. + * Currently this only works for 2D. + * + * @param hFile the DGN file to which the arc belongs (currently not used). + * @param psCurve the curve to be approximated. + * @param nPoints the number of points to use to approximate the curve. + * @param pasPoints the array of points into which to put the results. + * There must be room for at least nPoints points. + * + * @return TRUE on success or FALSE on failure. + */ + +int DGNStrokeCurve( CPL_UNUSED DGNHandle hFile, + DGNElemMultiPoint *psCurve, + int nPoints, DGNPoint * pasPoints ) +{ + int k, nDGNPoints, iOutPoint; + double *padfMx, *padfMy, *padfD, dfTotalD = 0, dfStepSize, dfD; + double *padfTx, *padfTy; + DGNPoint *pasDGNPoints = psCurve->vertices; + + nDGNPoints = psCurve->num_vertices; + + if( nDGNPoints < 6 ) + return FALSE; + + if( nPoints < nDGNPoints - 4 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Compute the Compute the slopes/distances of the segments. */ +/* -------------------------------------------------------------------- */ + padfMx = (double *) CPLMalloc(sizeof(double) * nDGNPoints); + padfMy = (double *) CPLMalloc(sizeof(double) * nDGNPoints); + padfD = (double *) CPLMalloc(sizeof(double) * nDGNPoints); + padfTx = (double *) CPLMalloc(sizeof(double) * nDGNPoints); + padfTy = (double *) CPLMalloc(sizeof(double) * nDGNPoints); + + for( k = 0; k < nDGNPoints-1; k++ ) + { + padfD[k] = sqrt( (pasDGNPoints[k+1].x-pasDGNPoints[k].x) + * (pasDGNPoints[k+1].x-pasDGNPoints[k].x) + + (pasDGNPoints[k+1].y-pasDGNPoints[k].y) + * (pasDGNPoints[k+1].y-pasDGNPoints[k].y) ); + if( padfD[k] == 0.0 ) + { + padfD[k] = 0.0001; + padfMx[k] = 0.0; + padfMy[k] = 0.0; + } + else + { + padfMx[k] = (pasDGNPoints[k+1].x - pasDGNPoints[k].x) / padfD[k]; + padfMy[k] = (pasDGNPoints[k+1].y - pasDGNPoints[k].y) / padfD[k]; + } + + if( k > 1 && k < nDGNPoints - 3 ) + dfTotalD += padfD[k]; + } + +/* -------------------------------------------------------------------- */ +/* Compute the Tx, and Ty coefficients for each segment. */ +/* -------------------------------------------------------------------- */ + for( k = 2; k < nDGNPoints - 2; k++ ) + { + if( fabs(padfMx[k+1] - padfMx[k]) == 0.0 + && fabs(padfMx[k-1] - padfMx[k-2]) == 0.0 ) + { + padfTx[k] = (padfMx[k] + padfMx[k-1]) / 2; + } + else + { + padfTx[k] = (padfMx[k-1] * fabs( padfMx[k+1] - padfMx[k]) + + padfMx[k] * fabs( padfMx[k-1] - padfMx[k-2] )) + / (ABS(padfMx[k+1] - padfMx[k]) + ABS(padfMx[k-1] - padfMx[k-2])); + } + + if( fabs(padfMy[k+1] - padfMy[k]) == 0.0 + && fabs(padfMy[k-1] - padfMy[k-2]) == 0.0 ) + { + padfTy[k] = (padfMy[k] + padfMy[k-1]) / 2; + } + else + { + padfTy[k] = (padfMy[k-1] * fabs( padfMy[k+1] - padfMy[k]) + + padfMy[k] * fabs( padfMy[k-1] - padfMy[k-2] )) + / (ABS(padfMy[k+1] - padfMy[k]) + ABS(padfMy[k-1] - padfMy[k-2])); + } + } + +/* -------------------------------------------------------------------- */ +/* Determine a step size in D. We scale things so that we have */ +/* roughly equidistant steps in D, but assume we also want to */ +/* include every node along the way. */ +/* -------------------------------------------------------------------- */ + dfStepSize = dfTotalD / (nPoints - (nDGNPoints - 4) - 1); + +/* ==================================================================== */ +/* Process each of the segments. */ +/* ==================================================================== */ + dfD = dfStepSize; + iOutPoint = 0; + + for( k = 2; k < nDGNPoints - 3; k++ ) + { + double dfAx, dfAy, dfBx, dfBy, dfCx, dfCy; + +/* -------------------------------------------------------------------- */ +/* Compute the "x" coefficients for this segment. */ +/* -------------------------------------------------------------------- */ + dfCx = padfTx[k]; + dfBx = (3.0 * (pasDGNPoints[k+1].x - pasDGNPoints[k].x) / padfD[k] + - 2.0 * padfTx[k] - padfTx[k+1]) / padfD[k]; + dfAx = (padfTx[k] + padfTx[k+1] + - 2 * (pasDGNPoints[k+1].x - pasDGNPoints[k].x) / padfD[k]) + / (padfD[k] * padfD[k]); + +/* -------------------------------------------------------------------- */ +/* Compute the Y coefficients for this segment. */ +/* -------------------------------------------------------------------- */ + dfCy = padfTy[k]; + dfBy = (3.0 * (pasDGNPoints[k+1].y - pasDGNPoints[k].y) / padfD[k] + - 2.0 * padfTy[k] - padfTy[k+1]) / padfD[k]; + dfAy = (padfTy[k] + padfTy[k+1] + - 2 * (pasDGNPoints[k+1].y - pasDGNPoints[k].y) / padfD[k]) + / (padfD[k] * padfD[k]); + +/* -------------------------------------------------------------------- */ +/* Add the start point for this segment. */ +/* -------------------------------------------------------------------- */ + pasPoints[iOutPoint].x = pasDGNPoints[k].x; + pasPoints[iOutPoint].y = pasDGNPoints[k].y; + pasPoints[iOutPoint].z = 0.0; + iOutPoint++; + +/* -------------------------------------------------------------------- */ +/* Step along, adding intermediate points. */ +/* -------------------------------------------------------------------- */ + while( dfD < padfD[k] && iOutPoint < nPoints - (nDGNPoints-k-1) ) + { + pasPoints[iOutPoint].x = dfAx * dfD * dfD * dfD + + dfBx * dfD * dfD + + dfCx * dfD + + pasDGNPoints[k].x; + pasPoints[iOutPoint].y = dfAy * dfD * dfD * dfD + + dfBy * dfD * dfD + + dfCy * dfD + + pasDGNPoints[k].y; + pasPoints[iOutPoint].z = 0.0; + iOutPoint++; + + dfD += dfStepSize; + } + + dfD -= padfD[k]; + } + +/* -------------------------------------------------------------------- */ +/* Add the start point for this segment. */ +/* -------------------------------------------------------------------- */ + while( iOutPoint < nPoints ) + { + pasPoints[iOutPoint].x = pasDGNPoints[nDGNPoints-3].x; + pasPoints[iOutPoint].y = pasDGNPoints[nDGNPoints-3].y; + pasPoints[iOutPoint].z = 0.0; + iOutPoint++; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + CPLFree( padfMx ); + CPLFree( padfMy ); + CPLFree( padfD ); + CPLFree( padfTx ); + CPLFree( padfTy ); + + return TRUE; +} + +/************************************************************************/ +/* main() */ +/* */ +/* test mainline */ +/************************************************************************/ +#ifdef notdef +int main( int argc, char ** argv ) + +{ + if( argc != 5 ) + { + printf( "Usage: stroke primary_axis secondary_axis axis_rotation angle\n" ); + exit( 1 ); + } + + double dfX, dfY, dfPrimary, dfSecondary, dfAxisRotation, dfAngle; + + dfPrimary = CPLAtof(argv[1]); + dfSecondary = CPLAtof(argv[2]); + dfAxisRotation = CPLAtof(argv[3]) / 180 * PI; + dfAngle = CPLAtof(argv[4]) / 180 * PI; + + ComputePointOnArc2D( dfPrimary, dfSecondary, dfAxisRotation, dfAngle, + &dfX, &dfY ); + + printf( "X=%.2f, Y=%.2f\n", dfX, dfY ); + + exit( 0 ); +} + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnwrite.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnwrite.cpp new file mode 100644 index 000000000..4258a606d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnwrite.cpp @@ -0,0 +1,2488 @@ +/****************************************************************************** + * $Id: dgnwrite.cpp 28435 2015-02-07 14:35:34Z rouault $ + * + * Project: Microstation DGN Access Library + * Purpose: DGN Access functions related to writing DGN elements. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "dgnlibp.h" + +CPL_CVSID("$Id: dgnwrite.cpp 28435 2015-02-07 14:35:34Z rouault $"); + +static void DGNPointToInt( DGNInfo *psDGN, DGNPoint *psPoint, + unsigned char *pabyTarget ); + +/************************************************************************/ +/* DGNResizeElement() */ +/************************************************************************/ + +/** + * Resize an existing element. + * + * If the new size is the same as the old nothing happens. + * + * Otherwise, the old element in the file is marked as deleted, and the + * DGNElemCore.offset and element_id are set to -1 indicating that the + * element should be written to the end of file when next written by + * DGNWriteElement(). The internal raw data buffer is updated to the new + * size. + * + * Only elements with "raw_data" loaded may be moved. + * + * In normal use the DGNResizeElement() call would be called on a previously + * loaded element, and afterwards the raw_data would be updated before calling + * DGNWriteElement(). If DGNWriteElement() isn't called after + * DGNResizeElement() then the element will be lost having been marked as + * deleted in it's old position but never written at the new location. + * + * @param hDGN the DGN file on which the element lives. + * @param psElement the element to alter. + * @param nNewSize the desired new size of the element in bytes. Must be + * a multiple of 2. + * + * @return TRUE on success, or FALSE on error. + */ + +int DGNResizeElement( DGNHandle hDGN, DGNElemCore *psElement, int nNewSize ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + +/* -------------------------------------------------------------------- */ +/* Check various conditions. */ +/* -------------------------------------------------------------------- */ + if( psElement->raw_bytes == 0 + || psElement->raw_bytes != psElement->size ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Raw bytes not loaded, or not matching element size." ); + return FALSE; + } + + if( nNewSize % 2 == 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DGNResizeElement(%d): " + "can't change to odd (not divisible by two) size.", + nNewSize ); + return FALSE; + } + + if( nNewSize == psElement->raw_bytes ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Mark the existing element as deleted if the element has to */ +/* move to the end of the file. */ +/* -------------------------------------------------------------------- */ + + if( psElement->offset != -1 ) + { + int nOldFLoc = VSIFTell( psDGN->fp ); + unsigned char abyLeader[2]; + + if( VSIFSeek( psDGN->fp, psElement->offset, SEEK_SET ) != 0 + || VSIFRead( abyLeader, sizeof(abyLeader), 1, psDGN->fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed seek or read when trying to mark existing\n" + "element as deleted in DGNResizeElement()\n" ); + return FALSE; + } + + abyLeader[1] |= 0x80; + + if( VSIFSeek( psDGN->fp, psElement->offset, SEEK_SET ) != 0 + || VSIFWrite( abyLeader, sizeof(abyLeader), 1, psDGN->fp ) != 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed seek or write when trying to mark existing\n" + "element as deleted in DGNResizeElement()\n" ); + return FALSE; + } + + VSIFSeek( psDGN->fp, SEEK_SET, nOldFLoc ); + + if( psElement->element_id != -1 && psDGN->index_built ) + psDGN->element_index[psElement->element_id].flags + |= DGNEIF_DELETED; + } + + psElement->offset = -1; /* move to end of file. */ + psElement->element_id = -1; + +/* -------------------------------------------------------------------- */ +/* Set the new size information, and realloc the raw data buffer. */ +/* -------------------------------------------------------------------- */ + psElement->size = nNewSize; + psElement->raw_data = (unsigned char *) + CPLRealloc( psElement->raw_data, nNewSize ); + psElement->raw_bytes = nNewSize; + +/* -------------------------------------------------------------------- */ +/* Update the size information within the raw buffer. */ +/* -------------------------------------------------------------------- */ + int nWords = (nNewSize / 2) - 2; + + psElement->raw_data[2] = (unsigned char) (nWords % 256); + psElement->raw_data[3] = (unsigned char) (nWords / 256); + + return TRUE; +} + +/************************************************************************/ +/* DGNWriteElement() */ +/************************************************************************/ + +/** + * Write element to file. + * + * Only elements with "raw_data" loaded may be written. This should + * include elements created with the various DGNCreate*() functions, and + * those read from the file with the DGNO_CAPTURE_RAW_DATA flag turned on + * with DGNSetOptions(). + * + * The passed element is written to the indicated file. If the + * DGNElemCore.offset field is -1 then the element is written at the end of + * the file (and offset/element are reset properly) otherwise the element + * is written back to the location indicated by DGNElemCore.offset. + * + * If the element is added at the end of the file, and if an element index + * has already been built, it will be updated to reference the new element. + * + * This function takes care of ensuring that the end-of-file marker is + * maintained after the last element. + * + * @param hDGN the file to write the element to. + * @param psElement the element to write. + * + * @return TRUE on success or FALSE in case of failure. + */ + +int DGNWriteElement( DGNHandle hDGN, DGNElemCore *psElement ) + +{ + DGNInfo *psDGN = (DGNInfo *) hDGN; + +/* ==================================================================== */ +/* If this element hasn't been positioned yet, place it at the */ +/* end of the file. */ +/* ==================================================================== */ + if( psElement->offset == -1 ) + { + int nJunk; + + // We must have an index, in order to properly assign the + // element id of the newly written element. Ensure it is built. + if( !psDGN->index_built ) + DGNBuildIndex( psDGN ); + + // Read the current "last" element. + if( !DGNGotoElement( hDGN, psDGN->element_count-1 ) ) + return FALSE; + + if( !DGNLoadRawElement( psDGN, &nJunk, &nJunk ) ) + return FALSE; + + // Establish the position of the new element. + psElement->offset = VSIFTell( psDGN->fp ); + psElement->element_id = psDGN->element_count; + + // Grow element buffer if needed. + if( psDGN->element_count == psDGN->max_element_count ) + { + psDGN->max_element_count += 500; + + psDGN->element_index = (DGNElementInfo *) + CPLRealloc( psDGN->element_index, + psDGN->max_element_count * sizeof(DGNElementInfo)); + } + + // Set up the element info + DGNElementInfo *psInfo; + + psInfo = psDGN->element_index + psDGN->element_count; + psInfo->level = (unsigned char) psElement->level; + psInfo->type = (unsigned char) psElement->type; + psInfo->stype = (unsigned char) psElement->stype; + psInfo->offset = psElement->offset; + if( psElement->complex ) + psInfo->flags = DGNEIF_COMPLEX; + else + psInfo->flags = 0; + + psDGN->element_count++; + } + +/* -------------------------------------------------------------------- */ +/* Write out the element. */ +/* -------------------------------------------------------------------- */ + if( VSIFSeek( psDGN->fp, psElement->offset, SEEK_SET ) != 0 + || VSIFWrite( psElement->raw_data, psElement->raw_bytes, + 1, psDGN->fp) != 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error seeking or writing new element of %d bytes at %d.", + psElement->offset, + psElement->raw_bytes ); + return FALSE; + } + + psDGN->next_element_id = psElement->element_id + 1; + +/* -------------------------------------------------------------------- */ +/* Write out the end of file 0xffff marker (if we were */ +/* extending the file), but push the file pointer back before */ +/* this EOF when done. */ +/* -------------------------------------------------------------------- */ + if( psDGN->next_element_id == psDGN->element_count ) + { + unsigned char abyEOF[2]; + + abyEOF[0] = 0xff; + abyEOF[1] = 0xff; + + VSIFWrite( abyEOF, 2, 1, psDGN->fp ); + VSIFSeek( psDGN->fp, -2, SEEK_CUR ); + } + + return TRUE; +} + +/************************************************************************/ +/* DGNCreate() */ +/************************************************************************/ + +/** + * Create new DGN file. + * + * This function will create a new DGN file based on the provided seed + * file, and return a handle on which elements may be read and written. + * + * The following creation flags may be passed: + *
      + *
    • DGNCF_USE_SEED_UNITS: The master and subunit resolutions and names + * from the seed file will be used in the new file. The nMasterUnitPerSubUnit, + * nUORPerSubUnit, pszMasterUnits, and pszSubUnits arguments will be ignored. + *
    • DGNCF_USE_SEED_ORIGIN: The origin from the seed file will be used + * and the X, Y and Z origin passed into the call will be ignored. + *
    • DGNCF_COPY_SEED_FILE_COLOR_TABLE: Should the first color table occuring + * in the seed file also be copied? + *
    • DGNCF_COPY_WHOLE_SEED_FILE: By default only the first three elements + * (TCB, Digitizer Setup and Level Symbology) are copied from the seed file. + * If this flag is provided the entire seed file is copied verbatim (with the + * TCB origin and units possibly updated). + *
    + * + * @param pszNewFilename the filename to create. If it already exists + * it will be overwritten. + * @param pszSeedFile the seed file to copy header from. + * @param nCreationFlags An ORing of DGNCF_* flags that are to take effect. + * @param dfOriginX the X origin for the file. + * @param dfOriginY the Y origin for the file. + * @param dfOriginZ the Z origin for the file. + * @param nSubUnitsPerMasterUnit the number of subunits in one master unit. + * @param nUORPerSubUnit the number of UOR (units of resolution) per subunit. + * @param pszMasterUnits the name of the master units (2 characters). + * @param pszSubUnits the name of the subunits (2 characters). + */ + +DGNHandle + DGNCreate( const char *pszNewFilename, const char *pszSeedFile, + int nCreationFlags, + double dfOriginX, double dfOriginY, double dfOriginZ, + int nSubUnitsPerMasterUnit, int nUORPerSubUnit, + const char *pszMasterUnits, const char *pszSubUnits ) + +{ + DGNInfo *psSeed, *psDGN; + FILE *fpNew; + DGNElemCore *psSrcTCB; + +/* -------------------------------------------------------------------- */ +/* Open seed file, and read TCB element. */ +/* -------------------------------------------------------------------- */ + psSeed = (DGNInfo *) DGNOpen( pszSeedFile, FALSE ); + if( psSeed == NULL ) + return NULL; + + DGNSetOptions( psSeed, DGNO_CAPTURE_RAW_DATA ); + + psSrcTCB = DGNReadElement( psSeed ); + + CPLAssert( psSrcTCB->raw_bytes >= 1536 ); + +/* -------------------------------------------------------------------- */ +/* Open output file. */ +/* -------------------------------------------------------------------- */ + fpNew = VSIFOpen( pszNewFilename, "wb" ); + if( fpNew == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open output file: %s", pszNewFilename ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Modify TCB appropriately for the output file. */ +/* -------------------------------------------------------------------- */ + GByte *pabyRawTCB = (GByte *) CPLMalloc(psSrcTCB->raw_bytes); + + memcpy( pabyRawTCB, psSrcTCB->raw_data, psSrcTCB->raw_bytes ); + + if( !(nCreationFlags & DGNCF_USE_SEED_UNITS) ) + { + memcpy( pabyRawTCB+1120, pszMasterUnits, 2 ); + memcpy( pabyRawTCB+1122, pszSubUnits, 2 ); + + DGN_WRITE_INT32( nUORPerSubUnit, pabyRawTCB+1116 ); + DGN_WRITE_INT32( nSubUnitsPerMasterUnit,pabyRawTCB+1112); + } + else + { + nUORPerSubUnit = DGN_INT32( pabyRawTCB+1116 ); + nSubUnitsPerMasterUnit = DGN_INT32( pabyRawTCB+1112 ); + } + + if( !(nCreationFlags & DGNCF_USE_SEED_ORIGIN) ) + { + dfOriginX *= (nUORPerSubUnit * nSubUnitsPerMasterUnit); + dfOriginY *= (nUORPerSubUnit * nSubUnitsPerMasterUnit); + dfOriginZ *= (nUORPerSubUnit * nSubUnitsPerMasterUnit); + + memcpy( pabyRawTCB+1240, &dfOriginX, 8 ); + memcpy( pabyRawTCB+1248, &dfOriginY, 8 ); + memcpy( pabyRawTCB+1256, &dfOriginZ, 8 ); + + IEEE2DGNDouble( pabyRawTCB+1240 ); + IEEE2DGNDouble( pabyRawTCB+1248 ); + IEEE2DGNDouble( pabyRawTCB+1256 ); + } + +/* -------------------------------------------------------------------- */ +/* Write TCB and EOF to new file. */ +/* -------------------------------------------------------------------- */ + unsigned char abyEOF[2]; + + VSIFWrite( pabyRawTCB, psSrcTCB->raw_bytes, 1, fpNew ); + CPLFree( pabyRawTCB ); + + abyEOF[0] = 0xff; + abyEOF[1] = 0xff; + + VSIFWrite( abyEOF, 2, 1, fpNew ); + + DGNFreeElement( psSeed, psSrcTCB ); + +/* -------------------------------------------------------------------- */ +/* Close and re-open using DGN API. */ +/* -------------------------------------------------------------------- */ + VSIFClose( fpNew ); + + psDGN = (DGNInfo *) DGNOpen( pszNewFilename, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Now copy over elements according to options in effect. */ +/* -------------------------------------------------------------------- */ + DGNElemCore *psSrcElement, *psDstElement; + + while( (psSrcElement = DGNReadElement( psSeed )) != NULL ) + { + if( (nCreationFlags & DGNCF_COPY_WHOLE_SEED_FILE) + || (psSrcElement->stype == DGNST_COLORTABLE + && nCreationFlags & DGNCF_COPY_SEED_FILE_COLOR_TABLE) + || psSrcElement->element_id <= 2 ) + { + psDstElement = DGNCloneElement( psSeed, psDGN, psSrcElement ); + DGNWriteElement( psDGN, psDstElement ); + DGNFreeElement( psDGN, psDstElement ); + } + + DGNFreeElement( psSeed, psSrcElement ); + } + + DGNClose( psSeed ); + + return psDGN; +} + +/************************************************************************/ +/* DGNCloneElement() */ +/************************************************************************/ + +/** + * Clone a retargetted element. + * + * Creates a copy of an element in a suitable form to write to a + * different file than that it was read from. + * + * NOTE: At this time the clone operation will fail if the source + * and destination file have a different origin or master/sub units. + * + * @param hDGNSrc the source file (from which psSrcElement was read). + * @param hDGNDst the destination file (to which the returned element may be + * written). + * @param psSrcElement the element to be cloned (from hDGNSrc). + * + * @return NULL on failure, or an appropriately modified copy of + * the source element suitable to write to hDGNDst. + */ + +DGNElemCore *DGNCloneElement( CPL_UNUSED DGNHandle hDGNSrc, + DGNHandle hDGNDst, + DGNElemCore *psSrcElement ) + +{ + DGNElemCore *psClone = NULL; + + DGNLoadTCB( hDGNDst ); + +/* -------------------------------------------------------------------- */ +/* Per structure specific copying. The core is fixed up later. */ +/* -------------------------------------------------------------------- */ + if( psSrcElement->stype == DGNST_CORE ) + { + psClone = (DGNElemCore *) CPLMalloc(sizeof(DGNElemCore)); + memcpy( psClone, psSrcElement, sizeof(DGNElemCore) ); + } + else if( psSrcElement->stype == DGNST_MULTIPOINT ) + { + DGNElemMultiPoint *psMP, *psSrcMP; + int nSize; + + psSrcMP = (DGNElemMultiPoint *) psSrcElement; + + nSize = sizeof(DGNElemMultiPoint) + + sizeof(DGNPoint) * (psSrcMP->num_vertices-2); + + psMP = (DGNElemMultiPoint *) CPLMalloc( nSize ); + memcpy( psMP, psSrcElement, nSize ); + + psClone = (DGNElemCore *) psMP; + } + else if( psSrcElement->stype == DGNST_ARC ) + { + DGNElemArc *psArc; + + psArc = (DGNElemArc *) CPLMalloc(sizeof(DGNElemArc)); + memcpy( psArc, psSrcElement, sizeof(DGNElemArc) ); + + psClone = (DGNElemCore *) psArc; + } + else if( psSrcElement->stype == DGNST_TEXT ) + { + DGNElemText *psText, *psSrcText; + int nSize; + + psSrcText = (DGNElemText *) psSrcElement; + nSize = sizeof(DGNElemText) + strlen(psSrcText->string); + + psText = (DGNElemText *) CPLMalloc( nSize ); + memcpy( psText, psSrcElement, nSize ); + + psClone = (DGNElemCore *) psText; + } + else if( psSrcElement->stype == DGNST_TEXT_NODE ) + { + DGNElemTextNode *psNode; + + psNode = (DGNElemTextNode *) + CPLMalloc(sizeof(DGNElemTextNode)); + memcpy( psNode, psSrcElement, sizeof(DGNElemTextNode) ); + + psClone = (DGNElemCore *) psNode; + } + else if( psSrcElement->stype == DGNST_COMPLEX_HEADER ) + { + DGNElemComplexHeader *psCH; + + psCH = (DGNElemComplexHeader *) + CPLMalloc(sizeof(DGNElemComplexHeader)); + memcpy( psCH, psSrcElement, sizeof(DGNElemComplexHeader) ); + + psClone = (DGNElemCore *) psCH; + } + else if( psSrcElement->stype == DGNST_COLORTABLE ) + { + DGNElemColorTable *psCT; + + psCT = (DGNElemColorTable *) CPLMalloc(sizeof(DGNElemColorTable)); + memcpy( psCT, psSrcElement, sizeof(DGNElemColorTable) ); + + psClone = (DGNElemCore *) psCT; + } + else if( psSrcElement->stype == DGNST_TCB ) + { + DGNElemTCB *psTCB; + + psTCB = (DGNElemTCB *) CPLMalloc(sizeof(DGNElemTCB)); + memcpy( psTCB, psSrcElement, sizeof(DGNElemTCB) ); + + psClone = (DGNElemCore *) psTCB; + } + else if( psSrcElement->stype == DGNST_CELL_HEADER ) + { + DGNElemCellHeader *psCH; + + psCH = (DGNElemCellHeader *) CPLMalloc(sizeof(DGNElemCellHeader)); + memcpy( psCH, psSrcElement, sizeof(DGNElemCellHeader) ); + + psClone = (DGNElemCore *) psCH; + } + else if( psSrcElement->stype == DGNST_CELL_LIBRARY ) + { + DGNElemCellLibrary *psCL; + + psCL = (DGNElemCellLibrary *) CPLMalloc(sizeof(DGNElemCellLibrary)); + memcpy( psCL, psSrcElement, sizeof(DGNElemCellLibrary) ); + + psClone = (DGNElemCore *) psCL; + } + else if( psSrcElement->stype == DGNST_TAG_VALUE ) + { + DGNElemTagValue *psTV; + + psTV = (DGNElemTagValue *) CPLMalloc(sizeof(DGNElemTagValue)); + memcpy( psTV, psSrcElement, sizeof(DGNElemTagValue) ); + + if( psTV->tagType == 1 ) + psTV->tagValue.string = CPLStrdup( psTV->tagValue.string ); + + psClone = (DGNElemCore *) psTV; + } + else if( psSrcElement->stype == DGNST_TAG_SET ) + { + DGNElemTagSet *psTS; + int iTag; + DGNTagDef *pasTagList; + + psTS = (DGNElemTagSet *) CPLMalloc(sizeof(DGNElemTagSet)); + memcpy( psTS, psSrcElement, sizeof(DGNElemTagSet) ); + + psTS->tagSetName = CPLStrdup( psTS->tagSetName ); + + pasTagList = (DGNTagDef *) + CPLMalloc( sizeof(DGNTagDef) * psTS->tagCount ); + memcpy( pasTagList, psTS->tagList, + sizeof(DGNTagDef) * psTS->tagCount ); + + for( iTag = 0; iTag < psTS->tagCount; iTag++ ) + { + pasTagList[iTag].name = CPLStrdup( pasTagList[iTag].name ); + pasTagList[iTag].prompt = CPLStrdup( pasTagList[iTag].prompt ); + if( pasTagList[iTag].type == 1 ) + pasTagList[iTag].defaultValue.string = + CPLStrdup( pasTagList[iTag].defaultValue.string); + } + + psTS->tagList = pasTagList; + psClone = (DGNElemCore *) psTS; + } + else if( psSrcElement->stype == DGNST_CONE ) + { + DGNElemCone *psCone; + + psCone = (DGNElemCone *) CPLMalloc(sizeof(DGNElemCone)); + memcpy( psCone, psSrcElement, sizeof(DGNElemCone) ); + + psClone = (DGNElemCore *) psCone; + } + else if( psSrcElement->stype == DGNST_BSPLINE_SURFACE_HEADER ) + { + DGNElemBSplineSurfaceHeader *psSurface; + + psSurface = (DGNElemBSplineSurfaceHeader *) + CPLMalloc(sizeof(DGNElemBSplineSurfaceHeader)); + memcpy( psSurface, psSrcElement, sizeof(DGNElemBSplineSurfaceHeader) ); + + psClone = (DGNElemCore *) psSurface; + } + else if( psSrcElement->stype == DGNST_BSPLINE_CURVE_HEADER ) + { + DGNElemBSplineCurveHeader *psCurve; + + psCurve = (DGNElemBSplineCurveHeader *) + CPLMalloc(sizeof(DGNElemBSplineCurveHeader)); + memcpy( psCurve, psSrcElement, sizeof(DGNElemBSplineCurveHeader) ); + + psClone = (DGNElemCore *) psCurve; + } + else if( psSrcElement->stype == DGNST_BSPLINE_SURFACE_BOUNDARY ) + { + DGNElemBSplineSurfaceBoundary *psBSB, *psSrcBSB; + int nSize; + + psSrcBSB = (DGNElemBSplineSurfaceBoundary *) psSrcElement; + + nSize = sizeof(DGNElemBSplineSurfaceBoundary) + + sizeof(DGNPoint) * (psSrcBSB->numverts-1); + + psBSB = (DGNElemBSplineSurfaceBoundary *) CPLMalloc( nSize ); + memcpy( psBSB, psSrcElement, nSize ); + + psClone = (DGNElemCore *) psBSB; + } + else if( psSrcElement->stype == DGNST_KNOT_WEIGHT ) + { + DGNElemKnotWeight *psArray /* , *psSrcArray*/; + int nSize, numelems; + + // FIXME: Is it OK to assume that the # of elements corresponds + // directly to the element size? kintel 20051218. + numelems = (psSrcElement->size - 36 - psSrcElement->attr_bytes)/4; + + /* psSrcArray = (DGNElemKnotWeight *) psSrcElement; */ + + nSize = sizeof(DGNElemKnotWeight) + sizeof(long) * (numelems-1); + + psArray = (DGNElemKnotWeight *) CPLMalloc( nSize ); + memcpy( psArray, psSrcElement, nSize ); + + psClone = (DGNElemCore *) psArray; + } + else if( psSrcElement->stype == DGNST_SHARED_CELL_DEFN ) + { + DGNElemSharedCellDefn *psCH; + + psCH = (DGNElemSharedCellDefn *)CPLMalloc(sizeof(DGNElemSharedCellDefn)); + memcpy( psCH, psSrcElement, sizeof(DGNElemSharedCellDefn) ); + + psClone = (DGNElemCore *) psCH; + } + else + { + CPLAssert( FALSE ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Copy core raw data, and attributes. */ +/* -------------------------------------------------------------------- */ + if( psClone->raw_bytes != 0 ) + { + psClone->raw_data = (unsigned char *) CPLMalloc(psClone->raw_bytes); + memcpy( psClone->raw_data, psSrcElement->raw_data, + psClone->raw_bytes ); + } + + if( psClone->attr_bytes != 0 ) + { + psClone->attr_data = (unsigned char *) CPLMalloc(psClone->attr_bytes); + memcpy( psClone->attr_data, psSrcElement->attr_data, + psClone->attr_bytes ); + } + +/* -------------------------------------------------------------------- */ +/* Clear location and id information. */ +/* -------------------------------------------------------------------- */ + psClone->offset = -1; + psClone->element_id = -1; + + return psClone; +} + +/************************************************************************/ +/* DGNUpdateElemCore() */ +/************************************************************************/ + +/** + * Change element core values. + * + * The indicated values in the element are updated in the structure, as well + * as in the raw data. The updated element is not written to disk. That + * must be done with DGNWriteElement(). The element must have raw_data + * loaded. + * + * @param hDGN the file on which the element belongs. + * @param psElement the element to modify. + * @param nLevel the new level value. + * @param nGraphicGroup the new graphic group value. + * @param nColor the new color index. + * @param nWeight the new element weight. + * @param nStyle the new style value for the element. + * + * @return Returns TRUE on success or FALSE on failure. + */ + +int DGNUpdateElemCore( DGNHandle hDGN, DGNElemCore *psElement, + int nLevel, int nGraphicGroup, int nColor, + int nWeight, int nStyle ) + +{ + psElement->level = nLevel; + psElement->graphic_group = nGraphicGroup; + psElement->color = nColor; + psElement->weight = nWeight; + psElement->style = nStyle; + + return DGNUpdateElemCoreExtended( hDGN, psElement ); +} + +/************************************************************************/ +/* DGNUpdateElemCoreExtended() */ +/************************************************************************/ + +/** + * Update internal raw data representation. + * + * The raw_data representation of the passed element is updated to reflect + * the various core fields. The DGNElemCore level, type, complex, deleted, + * graphic_group, properties, color, weight and style values are all + * applied to the raw_data representation. Spatial bounds, element type + * specific information and attributes are not updated in the raw data. + * + * @param hDGN the file to which the element belongs. + * @param psElement the element to be updated. + * + * @return TRUE on success, or FALSE on failure. + */ + + +int DGNUpdateElemCoreExtended( CPL_UNUSED DGNHandle hDGN, + DGNElemCore *psElement ) +{ + GByte *rd = psElement->raw_data; + int nWords = (psElement->raw_bytes / 2) - 2; + + if( psElement->raw_data == NULL + || psElement->raw_bytes < 36 ) + { + CPLAssert( FALSE ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Setup first four bytes. */ +/* -------------------------------------------------------------------- */ + rd[0] = (GByte) psElement->level; + if( psElement->complex ) + rd[0] |= 0x80; + + rd[1] = (GByte) psElement->type; + if( psElement->deleted ) + rd[1] |= 0x80; + + rd[2] = (GByte) (nWords % 256); + rd[3] = (GByte) (nWords / 256); + +/* -------------------------------------------------------------------- */ +/* If the attribute offset hasn't been set, set it now under */ +/* the assumption it should point to the end of the element. */ +/* -------------------------------------------------------------------- */ + if( psElement->raw_data[30] == 0 && psElement->raw_data[31] == 0 ) + { + int nAttIndex = (psElement->raw_bytes - 32) / 2; + + psElement->raw_data[30] = (GByte) (nAttIndex % 256); + psElement->raw_data[31] = (GByte) (nAttIndex / 256); + } +/* -------------------------------------------------------------------- */ +/* Handle the graphic properties. */ +/* -------------------------------------------------------------------- */ + if( psElement->raw_bytes > 36 && DGNElemTypeHasDispHdr( psElement->type ) ) + { + rd[28] = (GByte) (psElement->graphic_group % 256); + rd[29] = (GByte) (psElement->graphic_group / 256); + rd[32] = (GByte) (psElement->properties % 256); + rd[33] = (GByte) (psElement->properties / 256); + rd[34] = (GByte) (psElement->style | (psElement->weight << 3)); + rd[35] = (GByte) psElement->color; + } + + return TRUE; +} + +/************************************************************************/ +/* DGNInitializeElemCore() */ +/************************************************************************/ + +static void DGNInitializeElemCore( CPL_UNUSED DGNHandle hDGN, + DGNElemCore *psElement ) +{ + memset( psElement, 0, sizeof(DGNElemCore) ); + + psElement->offset = -1; + psElement->element_id = -1; +} + +/************************************************************************/ +/* DGNWriteBounds() */ +/* */ +/* Write bounds to element raw data. */ +/************************************************************************/ + +static void DGNWriteBounds( DGNInfo *psInfo, DGNElemCore *psElement, + DGNPoint *psMin, DGNPoint *psMax ) + +{ + CPLAssert( psElement->raw_bytes >= 28 ); + + DGNInverseTransformPointToInt( psInfo, psMin, psElement->raw_data + 4 ); + DGNInverseTransformPointToInt( psInfo, psMax, psElement->raw_data + 16 ); + + /* convert from twos complement to "binary offset" format. */ + + psElement->raw_data[5] ^= 0x80; + psElement->raw_data[9] ^= 0x80; + psElement->raw_data[13] ^= 0x80; + psElement->raw_data[17] ^= 0x80; + psElement->raw_data[21] ^= 0x80; + psElement->raw_data[25] ^= 0x80; +} + +/************************************************************************/ +/* DGNCreateMultiPointElem() */ +/************************************************************************/ + +/** + * Create new multi-point element. + * + * The newly created element will still need to be written to file using + * DGNWriteElement(). Also the level and other core values will be defaulted. + * Use DGNUpdateElemCore() on the element before writing to set these values. + * + * NOTE: There are restrictions on the nPointCount for some elements. For + * instance, DGNT_LINE can only have 2 points. Maximum element size + * precludes very large numbers of points. + * + * @param hDGN the file on which the element will eventually be written. + * @param nType the type of the element to be created. It must be one of + * DGNT_LINE, DGNT_LINE_STRING, DGNT_SHAPE, DGNT_CURVE or DGNT_BSPLINE_POLE. + * @param nPointCount the number of points in the pasVertices list. + * @param pasVertices the list of points to be written. + * + * @return the new element (a DGNElemMultiPoint structure) or NULL on failure. + */ + +DGNElemCore *DGNCreateMultiPointElem( DGNHandle hDGN, int nType, + int nPointCount, DGNPoint *pasVertices ) + +{ + DGNElemMultiPoint *psMP; + DGNElemCore *psCore; + DGNInfo *psDGN = (DGNInfo *) hDGN; + int i; + DGNPoint sMin, sMax; + + CPLAssert( nType == DGNT_LINE + || nType == DGNT_LINE_STRING + || nType == DGNT_SHAPE + || nType == DGNT_CURVE + || nType == DGNT_BSPLINE_POLE ); + + DGNLoadTCB( hDGN ); + +/* -------------------------------------------------------------------- */ +/* Is this too many vertices to write to a single element? */ +/* -------------------------------------------------------------------- */ + if( nPointCount > 101 ) + { + CPLError( CE_Failure, CPLE_ElementTooBig, + "Attempt to create %s element with %d points failed.\n" + "Element would be too large.", + DGNTypeToName( nType ), nPointCount ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Allocate element. */ +/* -------------------------------------------------------------------- */ + psMP = (DGNElemMultiPoint *) + CPLCalloc( sizeof(DGNElemMultiPoint) + + sizeof(DGNPoint) * (nPointCount-2), 1 ); + psCore = &(psMP->core); + + DGNInitializeElemCore( hDGN, psCore ); + psCore->stype = DGNST_MULTIPOINT; + psCore->type = nType; + +/* -------------------------------------------------------------------- */ +/* Set multipoint specific information in the structure. */ +/* -------------------------------------------------------------------- */ + psMP->num_vertices = nPointCount; + memcpy( psMP->vertices + 0, pasVertices, sizeof(DGNPoint) * nPointCount ); + +/* -------------------------------------------------------------------- */ +/* Setup Raw data for the multipoint section. */ +/* -------------------------------------------------------------------- */ + if( nType == DGNT_LINE ) + { + CPLAssert( nPointCount == 2 ); + + psCore->raw_bytes = 36 + psDGN->dimension* 4 * nPointCount; + + psCore->raw_data = (unsigned char*) CPLCalloc(psCore->raw_bytes,1); + + DGNInverseTransformPointToInt( psDGN, pasVertices + 0, + psCore->raw_data + 36 ); + DGNInverseTransformPointToInt( psDGN, pasVertices + 1, + psCore->raw_data + 36 + + psDGN->dimension * 4 ); + } + else + { + CPLAssert( nPointCount >= 2 ); + + psCore->raw_bytes = 38 + psDGN->dimension * 4 * nPointCount; + psCore->raw_data = (unsigned char*) CPLCalloc(psCore->raw_bytes,1); + + psCore->raw_data[36] = (unsigned char) (nPointCount % 256); + psCore->raw_data[37] = (unsigned char) (nPointCount/256); + + for( i = 0; i < nPointCount; i++ ) + DGNInverseTransformPointToInt( psDGN, pasVertices + i, + psCore->raw_data + 38 + + psDGN->dimension * i * 4 ); + } + +/* -------------------------------------------------------------------- */ +/* Set the core raw data, including the bounds. */ +/* -------------------------------------------------------------------- */ + DGNUpdateElemCoreExtended( hDGN, psCore ); + + sMin = sMax = pasVertices[0]; + for( i = 1; i < nPointCount; i++ ) + { + sMin.x = MIN(pasVertices[i].x,sMin.x); + sMin.y = MIN(pasVertices[i].y,sMin.y); + sMin.z = MIN(pasVertices[i].z,sMin.z); + sMax.x = MAX(pasVertices[i].x,sMax.x); + sMax.y = MAX(pasVertices[i].y,sMax.y); + sMax.z = MAX(pasVertices[i].z,sMax.z); + } + + DGNWriteBounds( psDGN, psCore, &sMin, &sMax ); + + return psCore; +} + +/************************************************************************/ +/* DGNCreateArcElem2D() */ +/************************************************************************/ + +DGNElemCore * +DGNCreateArcElem2D( DGNHandle hDGN, int nType, + double dfOriginX, double dfOriginY, + double dfPrimaryAxis, double dfSecondaryAxis, + double dfRotation, + double dfStartAngle, double dfSweepAngle ) + +{ + return DGNCreateArcElem( hDGN, nType, dfOriginX, dfOriginY, 0.0, + dfPrimaryAxis, dfSecondaryAxis, + dfStartAngle, dfSweepAngle, + dfRotation, NULL ); +} + +/************************************************************************/ +/* DGNCreateArcElem() */ +/************************************************************************/ + +/** + * Create Arc or Ellipse element. + * + * Create a new 2D or 3D arc or ellipse element. The start angle, and sweep + * angle are ignored for DGNT_ELLIPSE but used for DGNT_ARC. + * + * The newly created element will still need to be written to file using + * DGNWriteElement(). Also the level and other core values will be defaulted. + * Use DGNUpdateElemCore() on the element before writing to set these values. + * + * @param hDGN the DGN file on which the element will eventually be written. + * @param nType either DGNT_ELLIPSE or DGNT_ARC to select element type. + * @param dfOriginX the origin (center of rotation) of the arc (X). + * @param dfOriginY the origin (center of rotation) of the arc (Y). + * @param dfOriginZ the origin (center of rotation) of the arc (Y). + * @param dfPrimaryAxis the length of the primary axis. + * @param dfSecondaryAxis the length of the secondary axis. + * @param dfStartAngle start angle, degrees counterclockwise of primary axis. + * @param dfSweepAngle sweep angle, degrees + * @param dfRotation Counterclockwise rotation in degrees. + * @param panQuaternion 3D orientation quaternion (NULL to use rotation). + * + * @return the new element (DGNElemArc) or NULL on failure. + */ + +DGNElemCore * +DGNCreateArcElem( DGNHandle hDGN, int nType, + double dfOriginX, double dfOriginY, double dfOriginZ, + double dfPrimaryAxis, double dfSecondaryAxis, + double dfStartAngle, double dfSweepAngle, + double dfRotation, int *panQuaternion ) + +{ + DGNElemArc *psArc; + DGNElemCore *psCore; + DGNInfo *psDGN = (DGNInfo *) hDGN; + DGNPoint sMin, sMax, sOrigin; + GInt32 nAngle; + + CPLAssert( nType == DGNT_ARC || nType == DGNT_ELLIPSE ); + + DGNLoadTCB( hDGN ); + +/* -------------------------------------------------------------------- */ +/* Allocate element. */ +/* -------------------------------------------------------------------- */ + psArc = (DGNElemArc *) CPLCalloc( sizeof(DGNElemArc), 1 ); + psCore = &(psArc->core); + + DGNInitializeElemCore( hDGN, psCore ); + psCore->stype = DGNST_ARC; + psCore->type = nType; + +/* -------------------------------------------------------------------- */ +/* Set arc specific information in the structure. */ +/* -------------------------------------------------------------------- */ + sOrigin.x = dfOriginX; + sOrigin.y = dfOriginY; + sOrigin.z = dfOriginZ; + + psArc->origin = sOrigin; + psArc->primary_axis = dfPrimaryAxis; + psArc->secondary_axis = dfSecondaryAxis; + memset( psArc->quat, 0, sizeof(int) * 4 ); + psArc->startang = dfStartAngle; + psArc->sweepang = dfSweepAngle; + + psArc->rotation = dfRotation; + if( panQuaternion == NULL ) + { + DGNRotationToQuaternion( dfRotation, psArc->quat ); + } + else + { + memcpy( psArc->quat, panQuaternion, sizeof(int)*4 ); + } + +/* -------------------------------------------------------------------- */ +/* Setup Raw data for the arc section. */ +/* -------------------------------------------------------------------- */ + if( nType == DGNT_ARC ) + { + double dfScaledAxis; + + if( psDGN->dimension == 3 ) + psCore->raw_bytes = 100; + else + psCore->raw_bytes = 80; + psCore->raw_data = (unsigned char*) CPLCalloc(psCore->raw_bytes,1); + + /* start angle */ + nAngle = (int) (dfStartAngle * 360000.0); + DGN_WRITE_INT32( nAngle, psCore->raw_data + 36 ); + + /* sweep angle */ + if( dfSweepAngle < 0.0 ) + { + nAngle = (int) (ABS(dfSweepAngle) * 360000.0); + nAngle |= 0x80000000; + } + else if( dfSweepAngle > 364.9999 ) + { + nAngle = 0; + } + else + { + nAngle = (int) (dfSweepAngle * 360000.0); + } + DGN_WRITE_INT32( nAngle, psCore->raw_data + 40 ); + + /* axes */ + dfScaledAxis = dfPrimaryAxis / psDGN->scale; + memcpy( psCore->raw_data + 44, &dfScaledAxis, 8 ); + IEEE2DGNDouble( psCore->raw_data + 44 ); + + dfScaledAxis = dfSecondaryAxis / psDGN->scale; + memcpy( psCore->raw_data + 52, &dfScaledAxis, 8 ); + IEEE2DGNDouble( psCore->raw_data + 52 ); + + if( psDGN->dimension == 3 ) + { + /* quaternion */ + DGN_WRITE_INT32( psArc->quat[0], psCore->raw_data + 60 ); + DGN_WRITE_INT32( psArc->quat[1], psCore->raw_data + 64 ); + DGN_WRITE_INT32( psArc->quat[2], psCore->raw_data + 68 ); + DGN_WRITE_INT32( psArc->quat[3], psCore->raw_data + 72 ); + + /* origin */ + DGNInverseTransformPoint( psDGN, &sOrigin ); + memcpy( psCore->raw_data + 76, &(sOrigin.x), 8 ); + memcpy( psCore->raw_data + 84, &(sOrigin.y), 8 ); + memcpy( psCore->raw_data + 92, &(sOrigin.z), 8 ); + IEEE2DGNDouble( psCore->raw_data + 76 ); + IEEE2DGNDouble( psCore->raw_data + 84 ); + IEEE2DGNDouble( psCore->raw_data + 92 ); + } + else + { + /* rotation */ + nAngle = (int) (dfRotation * 360000.0); + DGN_WRITE_INT32( nAngle, psCore->raw_data + 60 ); + + /* origin */ + DGNInverseTransformPoint( psDGN, &sOrigin ); + memcpy( psCore->raw_data + 64, &(sOrigin.x), 8 ); + memcpy( psCore->raw_data + 72, &(sOrigin.y), 8 ); + IEEE2DGNDouble( psCore->raw_data + 64 ); + IEEE2DGNDouble( psCore->raw_data + 72 ); + } + } + +/* -------------------------------------------------------------------- */ +/* Setup Raw data for the ellipse section. */ +/* -------------------------------------------------------------------- */ + else + { + double dfScaledAxis; + + if( psDGN->dimension == 3 ) + psCore->raw_bytes = 92; + else + psCore->raw_bytes = 72; + psCore->raw_data = (unsigned char*) CPLCalloc(psCore->raw_bytes,1); + + /* axes */ + dfScaledAxis = dfPrimaryAxis / psDGN->scale; + memcpy( psCore->raw_data + 36, &dfScaledAxis, 8 ); + IEEE2DGNDouble( psCore->raw_data + 36 ); + + dfScaledAxis = dfSecondaryAxis / psDGN->scale; + memcpy( psCore->raw_data + 44, &dfScaledAxis, 8 ); + IEEE2DGNDouble( psCore->raw_data + 44 ); + + if( psDGN->dimension == 3 ) + { + /* quaternion */ + DGN_WRITE_INT32( psArc->quat[0], psCore->raw_data + 52 ); + DGN_WRITE_INT32( psArc->quat[1], psCore->raw_data + 56 ); + DGN_WRITE_INT32( psArc->quat[2], psCore->raw_data + 60 ); + DGN_WRITE_INT32( psArc->quat[3], psCore->raw_data + 64 ); + + /* origin */ + DGNInverseTransformPoint( psDGN, &sOrigin ); + memcpy( psCore->raw_data + 68, &(sOrigin.x), 8 ); + memcpy( psCore->raw_data + 76, &(sOrigin.y), 8 ); + memcpy( psCore->raw_data + 84, &(sOrigin.z), 8 ); + IEEE2DGNDouble( psCore->raw_data + 68 ); + IEEE2DGNDouble( psCore->raw_data + 76 ); + IEEE2DGNDouble( psCore->raw_data + 84 ); + } + else + { + /* rotation */ + nAngle = (int) (dfRotation * 360000.0); + DGN_WRITE_INT32( nAngle, psCore->raw_data + 52 ); + + /* origin */ + DGNInverseTransformPoint( psDGN, &sOrigin ); + memcpy( psCore->raw_data + 56, &(sOrigin.x), 8 ); + memcpy( psCore->raw_data + 64, &(sOrigin.y), 8 ); + IEEE2DGNDouble( psCore->raw_data + 56 ); + IEEE2DGNDouble( psCore->raw_data + 64 ); + } + + psArc->startang = 0.0; + psArc->sweepang = 360.0; + } + +/* -------------------------------------------------------------------- */ +/* Set the core raw data, including the bounds. */ +/* -------------------------------------------------------------------- */ + DGNUpdateElemCoreExtended( hDGN, psCore ); + + sMin.x = dfOriginX - MAX(dfPrimaryAxis,dfSecondaryAxis); + sMin.y = dfOriginY - MAX(dfPrimaryAxis,dfSecondaryAxis); + sMin.z = dfOriginZ - MAX(dfPrimaryAxis,dfSecondaryAxis); + sMax.x = dfOriginX + MAX(dfPrimaryAxis,dfSecondaryAxis); + sMax.y = dfOriginY + MAX(dfPrimaryAxis,dfSecondaryAxis); + sMax.z = dfOriginZ + MAX(dfPrimaryAxis,dfSecondaryAxis); + + DGNWriteBounds( psDGN, psCore, &sMin, &sMax ); + + return psCore; +} + +/************************************************************************/ +/* DGNCreateConeElem() */ +/************************************************************************/ + +/** + * Create Cone element. + * + * Create a new 3D cone element. + * + * The newly created element will still need to be written to file using + * DGNWriteElement(). Also the level and other core values will be defaulted. + * Use DGNUpdateElemCore() on the element before writing to set these values. + * + * @param hDGN the DGN file on which the element will eventually be written. + * @param dfCenter_1X the center of the first bounding circle (X). + * @param dfCenter_1Y the center of the first bounding circle (Y). + * @param dfCenter_1Z the center of the first bounding circle (Z). + * @param dfRadius_1 the radius of the first bounding circle. + * @param dfCenter_2X the center of the second bounding circle (X). + * @param dfCenter_2Y the center of the second bounding circle (Y). + * @param dfCenter_2Z the center of the second bounding circle (Z). + * @param dfRadius_2 the radius of the second bounding circle. + * @param panQuaternion 3D orientation quaternion (NULL for default orientation - circles parallel to the X-Y plane). + * + * @return the new element (DGNElemCone) or NULL on failure. + */ + +DGNElemCore * +DGNCreateConeElem( DGNHandle hDGN, + double dfCenter_1X, double dfCenter_1Y, + double dfCenter_1Z, double dfRadius_1, + double dfCenter_2X, double dfCenter_2Y, + double dfCenter_2Z, double dfRadius_2, + int *panQuaternion ) +{ + DGNElemCone *psCone; + DGNElemCore *psCore; + DGNInfo *psDGN = (DGNInfo *) hDGN; + DGNPoint sMin, sMax, sCenter_1, sCenter_2; + double dfScaledRadius; + + DGNLoadTCB( hDGN ); + +/* -------------------------------------------------------------------- */ +/* Allocate element. */ +/* -------------------------------------------------------------------- */ + psCone = (DGNElemCone *) CPLCalloc( sizeof(DGNElemCone), 1 ); + psCore = &(psCone->core); + + DGNInitializeElemCore( hDGN, psCore ); + psCore->stype = DGNST_CONE; + psCore->type = DGNT_CONE; + +/* -------------------------------------------------------------------- */ +/* Set cone specific information in the structure. */ +/* -------------------------------------------------------------------- */ + sCenter_1.x = dfCenter_1X; + sCenter_1.y = dfCenter_1Y; + sCenter_1.z = dfCenter_1Z; + sCenter_2.x = dfCenter_2X; + sCenter_2.y = dfCenter_2Y; + sCenter_2.z = dfCenter_2Z; + psCone->center_1 = sCenter_1; + psCone->center_2 = sCenter_2; + psCone->radius_1 = dfRadius_1; + psCone->radius_2 = dfRadius_2; + + memset( psCone->quat, 0, sizeof(int) * 4 ); + if( panQuaternion != NULL ) + { + memcpy( psCone->quat, panQuaternion, sizeof(int)*4 ); + } + else { + psCone->quat[0] = 1 << 31; + psCone->quat[1] = 0; + psCone->quat[2] = 0; + psCone->quat[3] = 0; + } + +/* -------------------------------------------------------------------- */ +/* Setup Raw data for the cone. */ +/* -------------------------------------------------------------------- */ + psCore->raw_bytes = 118; + psCore->raw_data = (unsigned char*) CPLCalloc(psCore->raw_bytes,1); + + /* unknown data */ + psCore->raw_data[36] = 0; + psCore->raw_data[37] = 0; + + /* quaternion */ + DGN_WRITE_INT32( psCone->quat[0], psCore->raw_data + 38 ); + DGN_WRITE_INT32( psCone->quat[1], psCore->raw_data + 42 ); + DGN_WRITE_INT32( psCone->quat[2], psCore->raw_data + 46 ); + DGN_WRITE_INT32( psCone->quat[3], psCore->raw_data + 50 ); + + /* center_1 */ + DGNInverseTransformPoint( psDGN, &sCenter_1 ); + memcpy( psCore->raw_data + 54, &sCenter_1.x, 8 ); + memcpy( psCore->raw_data + 62, &sCenter_1.y, 8 ); + memcpy( psCore->raw_data + 70, &sCenter_1.z, 8 ); + IEEE2DGNDouble( psCore->raw_data + 54 ); + IEEE2DGNDouble( psCore->raw_data + 62 ); + IEEE2DGNDouble( psCore->raw_data + 70 ); + + /* radius_1 */ + dfScaledRadius = psCone->radius_1 / psDGN->scale; + memcpy( psCore->raw_data + 78, &dfScaledRadius, 8 ); + IEEE2DGNDouble( psCore->raw_data + 78 ); + + /* center_2 */ + DGNInverseTransformPoint( psDGN, &sCenter_2 ); + memcpy( psCore->raw_data + 86, &sCenter_2.x, 8 ); + memcpy( psCore->raw_data + 94, &sCenter_2.y, 8 ); + memcpy( psCore->raw_data + 102, &sCenter_2.z, 8 ); + IEEE2DGNDouble( psCore->raw_data + 86 ); + IEEE2DGNDouble( psCore->raw_data + 94 ); + IEEE2DGNDouble( psCore->raw_data + 102 ); + + /* radius_2 */ + dfScaledRadius = psCone->radius_2 / psDGN->scale; + memcpy( psCore->raw_data + 110, &dfScaledRadius, 8 ); + IEEE2DGNDouble( psCore->raw_data + 110 ); + +/* -------------------------------------------------------------------- */ +/* Set the core raw data, including the bounds. */ +/* -------------------------------------------------------------------- */ + DGNUpdateElemCoreExtended( hDGN, psCore ); + + //FIXME: Calculate bounds. Do we need to take the quaternion into account? + // kintel 20030819 + + // Old implementation attempt: + // What if center_1.z > center_2.z ? +// double largestRadius = +// psCone->radius_1>psCone->radius_2?psCone->radius_1:psCone->radius_2; +// sMin.x = psCone->center_1.x-largestRadius; +// sMin.y = psCone->center_1.y-largestRadius; +// sMin.z = psCone->center_1.z; +// sMax.x = psCone->center_2.x+largestRadius; +// sMax.y = psCone->center_2.y+largestRadius; +// sMax.z = psCone->center_2.z; + + DGNWriteBounds( psDGN, psCore, &sMin, &sMax ); + + return psCore; +} + +/************************************************************************/ +/* DGNCreateTextElem() */ +/************************************************************************/ + +/** + * Create text element. + * + * The newly created element will still need to be written to file using + * DGNWriteElement(). Also the level and other core values will be defaulted. + * Use DGNUpdateElemCore() on the element before writing to set these values. + * + * @param hDGN the file on which the element will eventually be written. + * @param pszText the string of text. + * @param nFontId microstation font id for the text. 1 may be used as default. + * @param nJustification text justification. One of DGNJ_LEFT_TOP, + * DGNJ_LEFT_CENTER, DGNJ_LEFT_BOTTOM, DGNJ_CENTER_TOP, DGNJ_CENTER_CENTER, + * DGNJ_CENTER_BOTTOM, DGNJ_RIGHT_TOP, DGNJ_RIGHT_CENTER, DGNJ_RIGHT_BOTTOM. + * @param dfLengthMult character width in master units. + * @param dfHeightMult character height in master units. + * @param dfRotation Counterclockwise text rotation in degrees. + * @param panQuaternion 3D orientation quaternion (NULL to use rotation). + * @param dfOriginX Text origin (X). + * @param dfOriginY Text origin (Y). + * @param dfOriginZ Text origin (Z). + * + * @return the new element (DGNElemText) or NULL on failure. + */ + +DGNElemCore * +DGNCreateTextElem( DGNHandle hDGN, const char *pszText, + int nFontId, int nJustification, + double dfLengthMult, double dfHeightMult, + double dfRotation, int *panQuaternion, + double dfOriginX, double dfOriginY, double dfOriginZ ) + +{ + DGNElemText *psText; + DGNElemCore *psCore; + DGNInfo *psDGN = (DGNInfo *) hDGN; + DGNPoint sMin, sMax, sLowLeft, sLowRight, sUpLeft, sUpRight; + GInt32 nIntValue, nBase; + double length, height, diagonal; + + DGNLoadTCB( hDGN ); + +/* -------------------------------------------------------------------- */ +/* Allocate element. */ +/* -------------------------------------------------------------------- */ + psText = (DGNElemText *) + CPLCalloc( sizeof(DGNElemText)+strlen(pszText), 1 ); + psCore = &(psText->core); + + DGNInitializeElemCore( hDGN, psCore ); + psCore->stype = DGNST_TEXT; + psCore->type = DGNT_TEXT; + +/* -------------------------------------------------------------------- */ +/* Set arc specific information in the structure. */ +/* -------------------------------------------------------------------- */ + psText->font_id = nFontId; + psText->justification = nJustification; + psText->length_mult = dfLengthMult; + psText->height_mult = dfHeightMult; + psText->rotation = dfRotation; + psText->origin.x = dfOriginX; + psText->origin.y = dfOriginY; + psText->origin.z = dfOriginZ; + strcpy( psText->string, pszText ); + +/* -------------------------------------------------------------------- */ +/* Setup Raw data for the text specific portion. */ +/* -------------------------------------------------------------------- */ + if( psDGN->dimension == 2 ) + psCore->raw_bytes = 60 + strlen(pszText); + else + psCore->raw_bytes = 76 + strlen(pszText); + + psCore->raw_bytes += (psCore->raw_bytes % 2); + psCore->raw_data = (unsigned char*) CPLCalloc(psCore->raw_bytes,1); + + psCore->raw_data[36] = (unsigned char) nFontId; + psCore->raw_data[37] = (unsigned char) nJustification; + + nIntValue = (int) (dfLengthMult * 1000.0 / (psDGN->scale * 6.0) + 0.5); + DGN_WRITE_INT32( nIntValue, psCore->raw_data + 38 ); + + nIntValue = (int) (dfHeightMult * 1000.0 / (psDGN->scale * 6.0) + 0.5); + DGN_WRITE_INT32( nIntValue, psCore->raw_data + 42 ); + + if( psDGN->dimension == 2 ) + { + nIntValue = (int) (dfRotation * 360000.0); + DGN_WRITE_INT32( nIntValue, psCore->raw_data + 46 ); + + DGNInverseTransformPointToInt( psDGN, &(psText->origin), + psCore->raw_data + 50 ); + + nBase = 58; + } + else + { + int anQuaternion[4]; + + if( panQuaternion == NULL ) + DGNRotationToQuaternion( dfRotation, anQuaternion ); + else + memcpy( anQuaternion, panQuaternion, sizeof(int) * 4 ); + + DGN_WRITE_INT32( anQuaternion[0], psCore->raw_data + 46 ); + DGN_WRITE_INT32( anQuaternion[1], psCore->raw_data + 50 ); + DGN_WRITE_INT32( anQuaternion[2], psCore->raw_data + 54 ); + DGN_WRITE_INT32( anQuaternion[3], psCore->raw_data + 58 ); + + DGNInverseTransformPointToInt( psDGN, &(psText->origin), + psCore->raw_data + 62 ); + nBase = 74; + } + + psCore->raw_data[nBase] = (unsigned char) strlen(pszText); + psCore->raw_data[nBase+1] = 0; /* edflds? */ + memcpy( psCore->raw_data + nBase+2, pszText, strlen(pszText) ); + +/* -------------------------------------------------------------------- */ +/* Set the core raw data, including the bounds. */ +/* */ +/* Code contributed by Mart Kelder. */ +/* -------------------------------------------------------------------- */ + DGNUpdateElemCoreExtended( hDGN, psCore ); + + //calculate bounds if rotation is 0 + sMin.x = dfOriginX; + sMin.y = dfOriginY; + sMin.z = 0.0; + sMax.x = dfOriginX + dfLengthMult * strlen(pszText); + sMax.y = dfOriginY + dfHeightMult; + sMax.z = 0.0; + + //calculate rotated bounding box coordinates + length = sMax.x-sMin.x; + height = sMax.y-sMin.y; + diagonal=sqrt(length*length+height*height); + sLowLeft.x=sMin.x; + sLowLeft.y=sMin.y; + sLowRight.x=sMin.x+cos(psText->rotation*PI/180.0)*length; + sLowRight.y=sMin.y+sin(psText->rotation*PI/180.0)*length; + sUpRight.x=sMin.x+cos((psText->rotation*PI/180.0)+atan(height/length))*diagonal; + sUpRight.y=sMin.y+sin((psText->rotation*PI/180.0)+atan(height/length))*diagonal; + sUpLeft.x=sMin.x+cos((psText->rotation+90.0)*PI/180.0)*height; + sUpLeft.y=sMin.y+sin((psText->rotation+90.0)*PI/180.0)*height; + + //calculate new values for bounding box + sMin.x=MIN(sLowLeft.x,MIN(sLowRight.x,MIN(sUpLeft.x,sUpRight.x))); + sMin.y=MIN(sLowLeft.y,MIN(sLowRight.y,MIN(sUpLeft.y,sUpRight.y))); + sMax.x=MAX(sLowLeft.x,MAX(sLowRight.x,MAX(sUpLeft.x,sUpRight.x))); + sMax.y=MAX(sLowLeft.y,MAX(sLowRight.y,MAX(sUpLeft.y,sUpRight.y))); + sMin.x = dfOriginX - dfLengthMult * strlen(pszText); + sMin.y = dfOriginY - dfHeightMult; + sMin.z = 0.0; + sMax.x = dfOriginX + dfLengthMult * strlen(pszText); + sMax.y = dfOriginY + dfHeightMult; + sMax.z = 0.0; + + DGNWriteBounds( psDGN, psCore, &sMin, &sMax ); + + return psCore; +} + +/************************************************************************/ +/* DGNCreateColorTableElem() */ +/************************************************************************/ + +/** + * Create color table element. + * + * Creates a color table element with the indicated color table. + * + * Note that color table elements are actally of type DGNT_GROUP_DATA(5) + * and always on level 1. Do not alter the level with DGNUpdateElemCore() + * or the element will essentially be corrupt. + * + * The newly created element will still need to be written to file using + * DGNWriteElement(). Also the level and other core values will be defaulted. + * Use DGNUpdateElemCore() on the element before writing to set these values. + * + * @param hDGN the file to which the element will eventually be written. + * @param nScreenFlag the screen to which the color table applies + * (0 = left, 1 = right). + * @param abyColorInfo array of 256 color entries. The first is + * the background color. + * + * @return the new element (DGNElemColorTable) or NULL on failure. + */ + + +DGNElemCore * +DGNCreateColorTableElem( DGNHandle hDGN, int nScreenFlag, + GByte abyColorInfo[256][3] ) + +{ + DGNElemColorTable *psCT; + DGNElemCore *psCore; + +/* -------------------------------------------------------------------- */ +/* Allocate element. */ +/* -------------------------------------------------------------------- */ + psCT = (DGNElemColorTable *) CPLCalloc( sizeof(DGNElemColorTable), 1 ); + psCore = &(psCT->core); + + DGNInitializeElemCore( hDGN, psCore ); + psCore->stype = DGNST_COLORTABLE; + psCore->type = DGNT_GROUP_DATA; + psCore->level = DGN_GDL_COLOR_TABLE; + +/* -------------------------------------------------------------------- */ +/* Set colortable specific information in the structure. */ +/* -------------------------------------------------------------------- */ + psCT->screen_flag = nScreenFlag; + memcpy( psCT->color_info, abyColorInfo, 768 ); + +/* -------------------------------------------------------------------- */ +/* Setup Raw data for the color table specific portion. */ +/* -------------------------------------------------------------------- */ + psCore->raw_bytes = 806; /* FIXME: this is invalid : 806 < 41 + 783 (see below lines) */ + psCore->raw_data = (unsigned char*) CPLCalloc(psCore->raw_bytes,1); + + psCore->raw_data[36] = (unsigned char) (nScreenFlag % 256); + psCore->raw_data[37] = (unsigned char) (nScreenFlag / 256); + + memcpy( psCore->raw_data + 38, abyColorInfo[255], 3 ); + memcpy( psCore->raw_data + 41, abyColorInfo, 783 ); + +/* -------------------------------------------------------------------- */ +/* Set the core raw data. */ +/* -------------------------------------------------------------------- */ + DGNUpdateElemCoreExtended( hDGN, psCore ); + + return psCore; +} + +/************************************************************************/ +/* DGNCreateComplexHeaderElem() */ +/************************************************************************/ + +/** + * Create complex chain/shape header. + * + * The newly created element will still need to be written to file using + * DGNWriteElement(). Also the level and other core values will be defaulted. + * Use DGNUpdateElemCore() on the element before writing to set these values. + * + * The nTotLength is the sum of the size of all elements in the complex + * group plus 5. The DGNCreateComplexHeaderFromGroup() can be used to build + * a complex element from the members more conveniently. + * + * @param hDGN the file on which the element will be written. + * @param nType DGNT_COMPLEX_CHAIN_HEADER or DGNT_COMPLEX_SHAPE_HEADER. + * depending on whether the list is open or closed (last point equal to last) + * or if the object represents a surface or a solid. + * @param nTotLength the value of the totlength field in the element. + * @param nNumElems the number of elements in the complex group not including + * the header element. + * + * @return the new element (DGNElemComplexHeader) or NULL on failure. + */ +DGNElemCore * +DGNCreateComplexHeaderElem( DGNHandle hDGN, int nType, + int nTotLength, int nNumElems ) +{ + DGNElemComplexHeader *psCH; + DGNElemCore *psCore; + unsigned char abyRawZeroLinkage[8] = {0,0,0,0,0,0,0,0}; + + CPLAssert( nType == DGNT_COMPLEX_CHAIN_HEADER + || nType == DGNT_COMPLEX_SHAPE_HEADER ); + + DGNLoadTCB( hDGN ); + +/* -------------------------------------------------------------------- */ +/* Allocate element. */ +/* -------------------------------------------------------------------- */ + psCH = (DGNElemComplexHeader *) + CPLCalloc( sizeof(DGNElemComplexHeader), 1 ); + psCore = &(psCH->core); + + DGNInitializeElemCore( hDGN, psCore ); + psCore->complex = TRUE; + psCore->stype = DGNST_COMPLEX_HEADER; + psCore->type = nType; + +/* -------------------------------------------------------------------- */ +/* Set complex header specific information in the structure. */ +/* -------------------------------------------------------------------- */ + psCH->totlength = nTotLength - 4; + psCH->numelems = nNumElems; + psCH->surftype = 0; + psCH->boundelms = 0; + +/* -------------------------------------------------------------------- */ +/* Setup Raw data for the complex specific portion. */ +/* -------------------------------------------------------------------- */ + psCore->raw_bytes = 40; + psCore->raw_data = (unsigned char*) CPLCalloc(psCore->raw_bytes,1); + + psCore->raw_data[36] = (unsigned char) ((nTotLength-4) % 256); + psCore->raw_data[37] = (unsigned char) ((nTotLength-4) / 256); + psCore->raw_data[38] = (unsigned char) (nNumElems % 256); + psCore->raw_data[39] = (unsigned char) (nNumElems / 256); + +/* -------------------------------------------------------------------- */ +/* Set the core raw data. */ +/* -------------------------------------------------------------------- */ + DGNUpdateElemCoreExtended( hDGN, psCore ); + +/* -------------------------------------------------------------------- */ +/* Elements have to be at least 48 bytes long, so we have to */ +/* add a dummy bit of attribute data to fill out the length. */ +/* -------------------------------------------------------------------- */ + DGNAddRawAttrLink( hDGN, psCore, 8, abyRawZeroLinkage ); + + return psCore; +} + +/************************************************************************/ +/* DGNCreateComplexHeaderFromGroup() */ +/************************************************************************/ + +/** + * Create complex chain/shape header. + * + * This function is similar to DGNCreateComplexHeaderElem(), but it takes + * care of computing the total size of the set of elements being written, + * and collecting the bounding extents. It also takes care of some other + * convenience issues, like marking all the member elements as complex, and + * setting the level based on the level of the member elements. + * + * @param hDGN the file on which the element will be written. + * @param nType DGNT_COMPLEX_CHAIN_HEADER or DGNT_COMPLEX_SHAPE_HEADER. + * depending on whether the list is open or closed (last point equal to last) + * or if the object represents a surface or a solid. + * @param nNumElems the number of elements in the complex group not including + * the header element. + * @param papsElems array of pointers to nNumElems elements in the complex + * group. Some updates may be made to these elements. + * + * @return the new element (DGNElemComplexHeader) or NULL on failure. + */ + +DGNElemCore * +DGNCreateComplexHeaderFromGroup( DGNHandle hDGN, int nType, + int nNumElems, DGNElemCore **papsElems ) + +{ + int nTotalLength = 5; + int i, nLevel; + DGNElemCore *psCH; + DGNPoint sMin = {0.0,0.0,0.0}, sMax = {0.0,0.0,0.0}; + + DGNLoadTCB( hDGN ); + + if( nNumElems < 1 || papsElems == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Need at least one element to form a complex group." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Collect the total size, and bounds. */ +/* -------------------------------------------------------------------- */ + nLevel = papsElems[0]->level; + + for( i = 0; i < nNumElems; i++ ) + { + DGNPoint sThisMin, sThisMax; + + nTotalLength += papsElems[i]->raw_bytes / 2; + + papsElems[i]->complex = TRUE; + papsElems[i]->raw_data[0] |= 0x80; + + if( papsElems[i]->level != nLevel ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Not all level values matching in a complex set group!"); + } + + DGNGetElementExtents( hDGN, papsElems[i], &sThisMin, &sThisMax ); + if( i == 0 ) + { + sMin = sThisMin; + sMax = sThisMax; + } + else + { + sMin.x = MIN(sMin.x,sThisMin.x); + sMin.y = MIN(sMin.y,sThisMin.y); + sMin.z = MIN(sMin.z,sThisMin.z); + sMax.x = MAX(sMax.x,sThisMax.x); + sMax.y = MAX(sMax.y,sThisMax.y); + sMax.z = MAX(sMax.z,sThisMax.z); + } + } + +/* -------------------------------------------------------------------- */ +/* Create the corresponding complex header. */ +/* -------------------------------------------------------------------- */ + psCH = DGNCreateComplexHeaderElem( hDGN, nType, nTotalLength, nNumElems ); + DGNUpdateElemCore( hDGN, psCH, papsElems[0]->level, psCH->graphic_group, + psCH->color, psCH->weight, psCH->style ); + + DGNWriteBounds( (DGNInfo *) hDGN, psCH, &sMin, &sMax ); + + return psCH; +} + +/************************************************************************/ +/* DGNCreateSolidHeaderElem() */ +/************************************************************************/ + +/** + * Create 3D solid/surface. + * + * The newly created element will still need to be written to file using + * DGNWriteElement(). Also the level and other core values will be defaulted. + * Use DGNUpdateElemCore() on the element before writing to set these values. + * + * The nTotLength is the sum of the size of all elements in the solid + * group plus 6. The DGNCreateSolidHeaderFromGroup() can be used to build + * a solid element from the members more conveniently. + * + * @param hDGN the file on which the element will be written. + * @param nType DGNT_3DSURFACE_HEADER or DGNT_3DSOLID_HEADER. + * @param nSurfType the surface/solid type, one of DGNSUT_* or DGNSOT_*. + * @param nBoundElems the number of elements in each boundary. + * @param nTotLength the value of the totlength field in the element. + * @param nNumElems the number of elements in the solid not including + * the header element. + * + * @return the new element (DGNElemComplexHeader) or NULL on failure. + */ +DGNElemCore * +DGNCreateSolidHeaderElem( DGNHandle hDGN, int nType, int nSurfType, + int nBoundElems, int nTotLength, int nNumElems ) +{ + DGNElemComplexHeader *psCH; + DGNElemCore *psCore; + unsigned char abyRawZeroLinkage[8] = {0,0,0,0,0,0,0,0}; + + CPLAssert( nType == DGNT_3DSURFACE_HEADER + || nType == DGNT_3DSOLID_HEADER ); + + DGNLoadTCB( hDGN ); + +/* -------------------------------------------------------------------- */ +/* Allocate element. */ +/* -------------------------------------------------------------------- */ + psCH = (DGNElemComplexHeader *) + CPLCalloc( sizeof(DGNElemComplexHeader), 1 ); + psCore = &(psCH->core); + + DGNInitializeElemCore( hDGN, psCore ); + psCore->complex = TRUE; + psCore->stype = DGNST_COMPLEX_HEADER; + psCore->type = nType; + +/* -------------------------------------------------------------------- */ +/* Set solid header specific information in the structure. */ +/* -------------------------------------------------------------------- */ + psCH->totlength = nTotLength - 4; + psCH->numelems = nNumElems; + psCH->surftype = nSurfType; + psCH->boundelms = nBoundElems; + +/* -------------------------------------------------------------------- */ +/* Setup Raw data for the solid specific portion. */ +/* -------------------------------------------------------------------- */ + psCore->raw_bytes = 42; + + psCore->raw_data = (unsigned char*) CPLCalloc(psCore->raw_bytes,1); + + psCore->raw_data[36] = (unsigned char) ((nTotLength-4) % 256); + psCore->raw_data[37] = (unsigned char) ((nTotLength-4) / 256); + psCore->raw_data[38] = (unsigned char) (nNumElems % 256); + psCore->raw_data[39] = (unsigned char) (nNumElems / 256); + psCore->raw_data[40] = (unsigned char) psCH->surftype; + psCore->raw_data[41] = (unsigned char) psCH->boundelms - 1; + +/* -------------------------------------------------------------------- */ +/* Set the core raw data. */ +/* -------------------------------------------------------------------- */ + DGNUpdateElemCoreExtended( hDGN, psCore ); + +/* -------------------------------------------------------------------- */ +/* Elements have to be at least 48 bytes long, so we have to */ +/* add a dummy bit of attribute data to fill out the length. */ +/* -------------------------------------------------------------------- */ + DGNAddRawAttrLink( hDGN, psCore, 8, abyRawZeroLinkage ); + + return psCore; +} + +/************************************************************************/ +/* DGNCreateSolidHeaderFromGroup() */ +/************************************************************************/ + +/** + * Create 3D solid/surface header. + * + * This function is similar to DGNCreateSolidHeaderElem(), but it takes + * care of computing the total size of the set of elements being written, + * and collecting the bounding extents. It also takes care of some other + * convenience issues, like marking all the member elements as complex, and + * setting the level based on the level of the member elements. + * + * @param hDGN the file on which the element will be written. + * @param nType DGNT_3DSURFACE_HEADER or DGNT_3DSOLID_HEADER. + * @param nSurfType the surface/solid type, one of DGNSUT_* or DGNSOT_*. + * @param nBoundElems the number of boundary elements. + * @param nNumElems the number of elements in the solid not including + * the header element. + * @param papsElems array of pointers to nNumElems elements in the solid. + * Some updates may be made to these elements. + * + * @return the new element (DGNElemComplexHeader) or NULL on failure. + */ + +DGNElemCore * +DGNCreateSolidHeaderFromGroup( DGNHandle hDGN, int nType, int nSurfType, + int nBoundElems, int nNumElems, + DGNElemCore **papsElems ) + +{ + int nTotalLength = 6; + int i, nLevel; + DGNElemCore *psCH; + DGNPoint sMin = {0.0,0.0,0.0}, sMax = {0.0,0.0,0.0}; + + DGNLoadTCB( hDGN ); + + if( nNumElems < 1 || papsElems == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Need at least one element to form a solid." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Collect the total size, and bounds. */ +/* -------------------------------------------------------------------- */ + nLevel = papsElems[0]->level; + + for( i = 0; i < nNumElems; i++ ) + { + DGNPoint sThisMin, sThisMax; + + nTotalLength += papsElems[i]->raw_bytes / 2; + + papsElems[i]->complex = TRUE; + papsElems[i]->raw_data[0] |= 0x80; + + if( papsElems[i]->level != nLevel ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Not all level values matching in a complex set group!"); + } + + DGNGetElementExtents( hDGN, papsElems[i], &sThisMin, &sThisMax ); + if( i == 0 ) + { + sMin = sThisMin; + sMax = sThisMax; + } + else + { + sMin.x = MIN(sMin.x,sThisMin.x); + sMin.y = MIN(sMin.y,sThisMin.y); + sMin.z = MIN(sMin.z,sThisMin.z); + sMax.x = MAX(sMax.x,sThisMax.x); + sMax.y = MAX(sMax.y,sThisMax.y); + sMax.z = MAX(sMax.z,sThisMax.z); + } + } + +/* -------------------------------------------------------------------- */ +/* Create the corresponding solid header. */ +/* -------------------------------------------------------------------- */ + psCH = DGNCreateSolidHeaderElem( hDGN, nType, nSurfType, nBoundElems, + nTotalLength, nNumElems ); + DGNUpdateElemCore( hDGN, psCH, papsElems[0]->level, psCH->graphic_group, + psCH->color, psCH->weight, psCH->style ); + + DGNWriteBounds( (DGNInfo *) hDGN, psCH, &sMin, &sMax ); + + return psCH; +} + +/************************************************************************/ +/* DGNCreateCellHeaderElem() */ +/************************************************************************/ + +DGNElemCore CPL_DLL * +DGNCreateCellHeaderElem( DGNHandle hDGN, int nTotLength, const char *pszName, + short nClass, short *panLevels, + DGNPoint *psRangeLow, DGNPoint *psRangeHigh, + DGNPoint *psOrigin, double dfXScale, double dfYScale, + double dfRotation ) + +/** + * Create cell header. + * + * The newly created element will still need to be written to file using + * DGNWriteElement(). Also the level and other core values will be defaulted. + * Use DGNUpdateElemCore() on the element before writing to set these values. + * + * Generally speaking the function DGNCreateCellHeaderFromGroup() should + * be used instead of this function. + * + * @param hDGN the file handle on which the element is to be written. + * @param nTotLength total length of cell in words not including the 38 bytes + * of the cell header that occur before the totlength indicator. + * @param nClass the class value for the cell. + * @param panLevels an array of shorts holding the bit mask of levels in + * effect for this cell. This array should contain 4 shorts (64 bits). + * @param psRangeLow the cell diagonal origin in original cell file + * coordinates. + * @param psRangeHigh the cell diagonal top left corner in original cell file + * coordinates. + * @param psOrigin the origin of the cell in output file coordinates. + * @param dfXScale the amount of scaling applied in the X dimension in + * mapping from cell file coordinates to output file coordinates. + * @param dfYScale the amount of scaling applied in the Y dimension in + * mapping from cell file coordinates to output file coordinates. + * @param dfRotation the amount of rotation (degrees counterclockwise) in + * mapping from cell coordinates to output file coordinates. + * + * @return the new element (DGNElemCellHeader) or NULL on failure. + */ + +{ + DGNElemCellHeader *psCH; + DGNElemCore *psCore; + DGNInfo *psInfo = (DGNInfo *) hDGN; + + DGNLoadTCB( hDGN ); + +/* -------------------------------------------------------------------- */ +/* Allocate element. */ +/* -------------------------------------------------------------------- */ + psCH = (DGNElemCellHeader *) CPLCalloc( sizeof(DGNElemCellHeader), 1 ); + psCore = &(psCH->core); + + DGNInitializeElemCore( hDGN, psCore ); + psCore->stype = DGNST_CELL_HEADER; + psCore->type = DGNT_CELL_HEADER; + +/* -------------------------------------------------------------------- */ +/* Set complex header specific information in the structure. */ +/* -------------------------------------------------------------------- */ + psCH->totlength = nTotLength; + +/* -------------------------------------------------------------------- */ +/* Setup Raw data for the cell header specific portion. */ +/* -------------------------------------------------------------------- */ + if( psInfo->dimension == 2 ) + psCore->raw_bytes = 92; + else + psCore->raw_bytes = 124; + psCore->raw_data = (unsigned char*) CPLCalloc(psCore->raw_bytes,1); + + psCore->raw_data[36] = (unsigned char) (nTotLength % 256); + psCore->raw_data[37] = (unsigned char) (nTotLength / 256); + + DGNAsciiToRad50( pszName, (unsigned short *) (psCore->raw_data + 38) ); + if( strlen(pszName) > 3 ) + DGNAsciiToRad50( pszName+3, (unsigned short *) (psCore->raw_data+40) ); + + psCore->raw_data[42] = (unsigned char) (nClass % 256); + psCore->raw_data[43] = (unsigned char) (nClass / 256); + + memcpy( psCore->raw_data + 44, panLevels, 8 ); + + if( psInfo->dimension == 2 ) + { + DGNPointToInt( psInfo, psRangeLow, psCore->raw_data + 52 ); + DGNPointToInt( psInfo, psRangeHigh, psCore->raw_data+ 60 ); + + DGNInverseTransformPointToInt( psInfo, psOrigin, + psCore->raw_data + 84 ); + } + else + { + DGNPointToInt( psInfo, psRangeLow, psCore->raw_data + 52 ); + DGNPointToInt( psInfo, psRangeHigh, psCore->raw_data+ 64 ); + + DGNInverseTransformPointToInt( psInfo, psOrigin, + psCore->raw_data + 112 ); + } + +/* -------------------------------------------------------------------- */ +/* Produce a transformation matrix that approximates the */ +/* requested scaling and rotation. */ +/* -------------------------------------------------------------------- */ + if( psInfo->dimension == 2 ) + { + long anTrans[4]; + double cos_a = cos(-dfRotation * PI / 180.0); + double sin_a = sin(-dfRotation * PI / 180.0); + + anTrans[0] = (long) (cos_a * dfXScale * 214748); + anTrans[1] = (long) (sin_a * dfYScale * 214748); + anTrans[2] = (long)(-sin_a * dfXScale * 214748); + anTrans[3] = (long) (cos_a * dfYScale * 214748); + + DGN_WRITE_INT32( anTrans[0], psCore->raw_data + 68 ); + DGN_WRITE_INT32( anTrans[1], psCore->raw_data + 72 ); + DGN_WRITE_INT32( anTrans[2], psCore->raw_data + 76 ); + DGN_WRITE_INT32( anTrans[3], psCore->raw_data + 80 ); + } + else + { + } + +/* -------------------------------------------------------------------- */ +/* Set the core raw data. */ +/* -------------------------------------------------------------------- */ + DGNUpdateElemCoreExtended( hDGN, psCore ); + + return psCore; +} + +/************************************************************************/ +/* DGNPointToInt() */ +/* */ +/* Convert a point directly to integer coordinates and write to */ +/* the indicate memory location. Intended to be used for the */ +/* range section of the CELL HEADER. */ +/************************************************************************/ + +static void DGNPointToInt( DGNInfo *psDGN, DGNPoint *psPoint, + unsigned char *pabyTarget ) + +{ + double adfCT[3]; + int i; + + adfCT[0] = psPoint->x; + adfCT[1] = psPoint->y; + adfCT[2] = psPoint->z; + + for( i = 0; i < psDGN->dimension; i++ ) + { + GInt32 nCTI; + unsigned char *pabyCTI = (unsigned char *) &nCTI; + + nCTI = (GInt32) MAX(-2147483647,MIN(2147483647,adfCT[i])); + +#ifdef WORDS_BIGENDIAN + pabyTarget[i*4+0] = pabyCTI[1]; + pabyTarget[i*4+1] = pabyCTI[0]; + pabyTarget[i*4+2] = pabyCTI[3]; + pabyTarget[i*4+3] = pabyCTI[2]; +#else + pabyTarget[i*4+3] = pabyCTI[1]; + pabyTarget[i*4+2] = pabyCTI[0]; + pabyTarget[i*4+1] = pabyCTI[3]; + pabyTarget[i*4+0] = pabyCTI[2]; +#endif + } +} + +/************************************************************************/ +/* DGNCreateCellHeaderFromGroup() */ +/************************************************************************/ + +/** + * Create cell header from a group of elements. + * + * The newly created element will still need to be written to file using + * DGNWriteElement(). Also the level and other core values will be defaulted. + * Use DGNUpdateElemCore() on the element before writing to set these values. + * + * This function will compute the total length, bounding box, and diagonal + * range values from the set of provided elements. Note that the proper + * diagonal range values will only be written if 1.0 is used for the x and y + * scale values, and 0.0 for the rotation. Use of other values will result + * in incorrect scaling handles being presented to the user in Microstation + * when they select the element. + * + * @param hDGN the file handle on which the element is to be written. + * @param nClass the class value for the cell. + * @param panLevels an array of shorts holding the bit mask of levels in + * effect for this cell. This array should contain 4 shorts (64 bits). + * This array would normally be passed in as NULL, and the function will + * build a mask from the passed list of elements. + * @param psOrigin the origin of the cell in output file coordinates. + * @param dfXScale the amount of scaling applied in the X dimension in + * mapping from cell file coordinates to output file coordinates. + * @param dfYScale the amount of scaling applied in the Y dimension in + * mapping from cell file coordinates to output file coordinates. + * @param dfRotation the amount of rotation (degrees counterclockwise) in + * mapping from cell coordinates to output file coordinates. + * + * @return the new element (DGNElemCellHeader) or NULL on failure. + */ + +DGNElemCore * +DGNCreateCellHeaderFromGroup( DGNHandle hDGN, const char *pszName, + short nClass, short *panLevels, + int nNumElems, DGNElemCore **papsElems, + DGNPoint *psOrigin, + double dfXScale, double dfYScale, + double dfRotation ) + +{ + int nTotalLength; + int i /* , nLevel */; + DGNElemCore *psCH; + DGNPoint sMin={0.0,0.0,0.0}, sMax={0.0,0.0,0.0}; + unsigned char abyLevelsOccuring[8] = {0,0,0,0,0,0,0,0}; + DGNInfo *psInfo = (DGNInfo *) hDGN; + + DGNLoadTCB( hDGN ); + + if( nNumElems < 1 || papsElems == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Need at least one element to form a cell." ); + return NULL; + } + + if( psInfo->dimension == 2 ) + nTotalLength = 27; + else + nTotalLength = 43; + +/* -------------------------------------------------------------------- */ +/* Collect the total size, and bounds. */ +/* -------------------------------------------------------------------- */ + /* nLevel = papsElems[0]->level; */ + + for( i = 0; i < nNumElems; i++ ) + { + DGNPoint sThisMin, sThisMax; + int nLevel; + + nTotalLength += papsElems[i]->raw_bytes / 2; + + /* mark as complex */ + papsElems[i]->complex = TRUE; + papsElems[i]->raw_data[0] |= 0x80; + + /* establish level */ + nLevel = papsElems[i]->level; + nLevel = MAX(1,MIN(nLevel,64)); + abyLevelsOccuring[(nLevel-1) >> 3] |= (0x1 << ((nLevel-1)&0x7)); + + DGNGetElementExtents( hDGN, papsElems[i], &sThisMin, &sThisMax ); + if( i == 0 ) + { + sMin = sThisMin; + sMax = sThisMax; + } + else + { + sMin.x = MIN(sMin.x,sThisMin.x); + sMin.y = MIN(sMin.y,sThisMin.y); + sMin.z = MIN(sMin.z,sThisMin.z); + sMax.x = MAX(sMax.x,sThisMax.x); + sMax.y = MAX(sMax.y,sThisMax.y); + sMax.z = MAX(sMax.z,sThisMax.z); + } + } + +/* -------------------------------------------------------------------- */ +/* It seems that the range needs to be adjusted according to */ +/* the rotation and scaling. */ +/* */ +/* NOTE: Omitting code ... this is already done in */ +/* DGNInverseTransformPoint() called from DGNWriteBounds(). */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + sMin.x -= psOrigin->x; + sMin.y -= psOrigin->y; + sMin.z -= psOrigin->z; + sMax.x -= psOrigin->x; + sMax.y -= psOrigin->y; + sMax.z -= psOrigin->z; + + sMin.x /= ((DGNInfo *) hDGN)->scale; + sMin.y /= ((DGNInfo *) hDGN)->scale; + sMin.z /= ((DGNInfo *) hDGN)->scale; + sMax.x /= ((DGNInfo *) hDGN)->scale; + sMax.y /= ((DGNInfo *) hDGN)->scale; + sMax.z /= ((DGNInfo *) hDGN)->scale; +#endif + +/* -------------------------------------------------------------------- */ +/* Create the corresponding cell header. */ +/* -------------------------------------------------------------------- */ + if( panLevels == NULL ) + panLevels = (short *) abyLevelsOccuring + 0; + + psCH = DGNCreateCellHeaderElem( hDGN, nTotalLength, pszName, + nClass, panLevels, + &sMin, &sMax, psOrigin, + dfXScale, dfYScale, dfRotation ); + DGNWriteBounds( (DGNInfo *) hDGN, psCH, &sMin, &sMax ); + + return psCH; + +} + +/************************************************************************/ +/* DGNAddMSLink() */ +/************************************************************************/ + +/** + * Add a database link to element. + * + * The target element must already have raw_data loaded, and it will be + * resized (see DGNResizeElement()) as needed for the new attribute data. + * Note that the element is not written to disk immediate. Use + * DGNWriteElement() for that. + * + * @param hDGN the file to which the element corresponds. + * @param psElement the element being updated. + * @param nLinkageType link type (DGNLT_*). Usually one of DGNLT_DMRS, + * DGNLT_INFORMIX, DGNLT_ODBC, DGNLT_ORACLE, DGNLT_RIS, DGNLT_SYBASE, + * or DGNLT_XBASE. + * @param nEntityNum indicator of the table referenced on target database. + * @param nMSLink indicator of the record referenced on target table. + * + * @return -1 on failure, or the link index. + */ + +int DGNAddMSLink( DGNHandle hDGN, DGNElemCore *psElement, + int nLinkageType, int nEntityNum, int nMSLink ) + +{ + unsigned char abyLinkage[32]; + int nLinkageSize; + + if( nLinkageType == DGNLT_DMRS ) + { + nLinkageSize = 8; + abyLinkage[0] = 0x00; + abyLinkage[1] = 0x00; + abyLinkage[2] = (GByte) (nEntityNum % 256); + abyLinkage[3] = (GByte) (nEntityNum / 256); + abyLinkage[4] = (GByte) (nMSLink % 256); + abyLinkage[5] = (GByte) ((nMSLink / 256) % 256); + abyLinkage[6] = (GByte) (nMSLink / 65536); + abyLinkage[7] = 0x01; + } + else + { + nLinkageSize = 16; + abyLinkage[0] = 0x07; + abyLinkage[1] = 0x10; + abyLinkage[2] = (GByte) (nLinkageType % 256); + abyLinkage[3] = (GByte) (nLinkageType / 256); + abyLinkage[4] = (GByte) (0x81); + abyLinkage[5] = (GByte) (0x0F); + abyLinkage[6] = (GByte) (nEntityNum % 256); + abyLinkage[7] = (GByte) (nEntityNum / 256); + abyLinkage[8] = (GByte) (nMSLink % 256); + abyLinkage[9] = (GByte) ((nMSLink / 256) % 256); + abyLinkage[10] = (GByte) ((nMSLink / 65536) % 256); + abyLinkage[11] = (GByte) (nMSLink / 16777216); + abyLinkage[12] = 0x00; + abyLinkage[13] = 0x00; + abyLinkage[14] = 0x00; + abyLinkage[15] = 0x00; + } + + return DGNAddRawAttrLink( hDGN, psElement, nLinkageSize, abyLinkage ); +} + +/************************************************************************/ +/* DGNAddRawAttrLink() */ +/************************************************************************/ + +/** + * Add a raw attribute linkage to element. + * + * Given a raw data buffer, append it to this element as an attribute linkage + * without trying to interprete the linkage data. + * + * The target element must already have raw_data loaded, and it will be + * resized (see DGNResizeElement()) as needed for the new attribute data. + * Note that the element is not written to disk immediate. Use + * DGNWriteElement() for that. + * + * This function will take care of updating the "totlength" field of + * complex chain or shape headers to account for the extra attribute space + * consumed in the header element. + * + * @param hDGN the file to which the element corresponds. + * @param psElement the element being updated. + * @param nLinkSize the size of the linkage in bytes. + * @param pabyRawLinkData the raw linkage data (nLinkSize bytes worth). + * + * @return -1 on failure, or the link index. + */ + +int DGNAddRawAttrLink( DGNHandle hDGN, DGNElemCore *psElement, + int nLinkSize, unsigned char *pabyRawLinkData ) + +{ + int iLinkage; + + if( nLinkSize % 2 == 1 ) + nLinkSize++; + + if( psElement->size + nLinkSize > 768 ) + { + CPLError( CE_Failure, CPLE_ElementTooBig, + "Attempt to add %d byte linkage to element exceeds maximum" + " element size.", + nLinkSize ); + return -1; + } + +/* -------------------------------------------------------------------- */ +/* Ensure the attribute linkage bit is set. */ +/* -------------------------------------------------------------------- */ + psElement->properties |= DGNPF_ATTRIBUTES; + +/* -------------------------------------------------------------------- */ +/* Append the attribute linkage to the linkage area. */ +/* -------------------------------------------------------------------- */ + psElement->attr_bytes += nLinkSize; + psElement->attr_data = (unsigned char *) + CPLRealloc( psElement->attr_data, psElement->attr_bytes ); + + memcpy( psElement->attr_data + (psElement->attr_bytes-nLinkSize), + pabyRawLinkData, nLinkSize ); + +/* -------------------------------------------------------------------- */ +/* Grow the raw data, if we have rawdata. */ +/* -------------------------------------------------------------------- */ + psElement->raw_bytes += nLinkSize; + psElement->raw_data = (unsigned char *) + CPLRealloc( psElement->raw_data, psElement->raw_bytes ); + + memcpy( psElement->raw_data + (psElement->raw_bytes-nLinkSize), + pabyRawLinkData, nLinkSize ); + +/* -------------------------------------------------------------------- */ +/* If the element is a shape or chain complex header, then we */ +/* need to increase the total complex group size appropriately. */ +/* -------------------------------------------------------------------- */ + if( psElement->stype == DGNST_COMPLEX_HEADER || + psElement->stype == DGNST_TEXT_NODE ) // compatible structures + { + DGNElemComplexHeader *psCT = (DGNElemComplexHeader *) psElement; + + psCT->totlength += (nLinkSize / 2); + + psElement->raw_data[36] = (unsigned char) (psCT->totlength % 256); + psElement->raw_data[37] = (unsigned char) (psCT->totlength / 256); + } + +/* -------------------------------------------------------------------- */ +/* Ensure everything is updated properly, including element */ +/* length and properties. */ +/* -------------------------------------------------------------------- */ + DGNUpdateElemCoreExtended( hDGN, psElement ); + +/* -------------------------------------------------------------------- */ +/* Figure out what the linkage index is. */ +/* -------------------------------------------------------------------- */ + for( iLinkage = 0; ; iLinkage++ ) + { + if( DGNGetLinkage( hDGN, psElement, iLinkage, NULL, NULL, NULL, NULL ) + == NULL ) + break; + } + + return iLinkage-1; +} + +/************************************************************************/ +/* DGNAddShapeFileInfo() */ +/************************************************************************/ + +/** + * Add a shape fill attribute linkage. + * + * The target element must already have raw_data loaded, and it will be + * resized (see DGNResizeElement()) as needed for the new attribute data. + * Note that the element is not written to disk immediate. Use + * DGNWriteElement() for that. + * + * @param hDGN the file to which the element corresponds. + * @param psElement the element being updated. + * @param nColor fill color (color index from palette). + * + * @return -1 on failure, or the link index. + */ + +int DGNAddShapeFillInfo( DGNHandle hDGN, DGNElemCore *psElement, + int nColor ) + +{ + unsigned char abyFillInfo[16] = + { 0x07, 0x10, 0x41, 0x00, 0x02, 0x08, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + abyFillInfo[8] = (unsigned char) nColor; + + return DGNAddRawAttrLink( hDGN, psElement, 16, abyFillInfo ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnwritetest.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnwritetest.c new file mode 100644 index 000000000..1d9e86cc2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dgnwritetest.c @@ -0,0 +1,232 @@ +/****************************************************************************** + * $Id: dgnwritetest.c 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: Microstation DGN Access Library + * Purpose: Test program for use of write api. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "dgnlib.h" + +CPL_CVSID("$Id: dgnwritetest.c 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* main() */ +/************************************************************************/ + +int main( int argc, char ** argv ) + +{ + DGNHandle hNewDGN; + DGNElemCore *psMembers[2]; + DGNPoint asPoints[10]; + DGNElemCore *psLine; + +/* -------------------------------------------------------------------- */ +/* Create new DGN file. */ +/* -------------------------------------------------------------------- */ + hNewDGN = DGNCreate( "out.dgn", "seed.dgn", + DGNCF_USE_SEED_UNITS + | DGNCF_USE_SEED_ORIGIN, + 0.0, 0.0, 0.0, 0, 0, "", "" ); + + if( hNewDGN == NULL ) + { + printf( "DGNCreate failed.\n" ); + exit( 10 ); + } + +/* -------------------------------------------------------------------- */ +/* Write one line segment to it. */ +/* -------------------------------------------------------------------- */ + memset( &asPoints, 0, sizeof(asPoints) ); + + asPoints[0].x = 0; + asPoints[0].y = 0; + asPoints[0].z = 100; + asPoints[1].x = 10000; + asPoints[1].y = 4000; + asPoints[1].z = 110; + + psLine = DGNCreateMultiPointElem( hNewDGN, DGNT_LINE, 2, asPoints ); + DGNUpdateElemCore( hNewDGN, psLine, 15, 0, 3, 1, 0 ); + DGNWriteElement( hNewDGN, psLine ); + DGNFreeElement( hNewDGN, psLine ); + +/* -------------------------------------------------------------------- */ +/* Write a line string. */ +/* -------------------------------------------------------------------- */ + asPoints[0].x = 0; + asPoints[0].y = 1000; + asPoints[1].x = 6000; + asPoints[1].y = 5000; + asPoints[2].x = 12000; + asPoints[2].y = 6000; + + psLine = DGNCreateMultiPointElem( hNewDGN, DGNT_LINE_STRING, 3, asPoints ); + DGNUpdateElemCore( hNewDGN, psLine, 15, 0, 3, 1, 0 ); + DGNWriteElement( hNewDGN, psLine ); + DGNFreeElement( hNewDGN, psLine ); + +/* -------------------------------------------------------------------- */ +/* Write an Arc. */ +/* -------------------------------------------------------------------- */ + psLine = DGNCreateArcElem( hNewDGN, DGNT_ARC, + 2000.0, 3000.0, 500.0, 2000.0, 1000.0, + 0.0, 270.0, 0.0, NULL ); + + DGNUpdateElemCore( hNewDGN, psLine, 15, 0, 3, 1, 0 ); + DGNWriteElement( hNewDGN, psLine ); + DGNFreeElement( hNewDGN, psLine ); + +/* -------------------------------------------------------------------- */ +/* Write an Ellipse with fill info. */ +/* -------------------------------------------------------------------- */ + psLine = DGNCreateArcElem( hNewDGN, DGNT_ELLIPSE, + 200.0, 30.0, 5.0, 10.0, 10.0, + 0.0, 360.0, 0.0, NULL ); + + DGNUpdateElemCore( hNewDGN, psLine, 15, 0, 3, 1, 0 ); + DGNWriteElement( hNewDGN, psLine ); + DGNFreeElement( hNewDGN, psLine ); + +/* -------------------------------------------------------------------- */ +/* Write some text. */ +/* -------------------------------------------------------------------- */ + psLine = DGNCreateTextElem( hNewDGN, "This is a test string", + 0, DGNJ_CENTER_TOP, 200.0, 200.0, 0.0, NULL, + 2000.0, 3000.0, 0.0 ); + + DGNAddMSLink( hNewDGN, psLine, DGNLT_XBASE, 7, 101 ); + DGNAddMSLink( hNewDGN, psLine, DGNLT_DMRS, 7, 101 ); + DGNUpdateElemCore( hNewDGN, psLine, 15, 0, 3, 1, 0 ); + DGNWriteElement( hNewDGN, psLine ); + DGNFreeElement( hNewDGN, psLine ); + + psLine = DGNCreateTextElem( hNewDGN, "------- 30 degrees", + 0, DGNJ_CENTER_TOP, 200.0, 200.0, 30.0, NULL, + 2000.0, 3000.0, 0.0 ); + + DGNUpdateElemCore( hNewDGN, psLine, 15, 0, 3, 1, 0 ); + DGNWriteElement( hNewDGN, psLine ); + DGNFreeElement( hNewDGN, psLine ); + + psLine = DGNCreateTextElem( hNewDGN, "------- 90 degrees", + 0, DGNJ_CENTER_TOP, 200.0, 200.0, 90.0, NULL, + 2000.0, 3000.0, 0.0 ); + + DGNUpdateElemCore( hNewDGN, psLine, 15, 0, 3, 1, 0 ); + DGNWriteElement( hNewDGN, psLine ); + DGNFreeElement( hNewDGN, psLine ); + +/* -------------------------------------------------------------------- */ +/* Write a complex shape consisting of two line strings. */ +/* -------------------------------------------------------------------- */ + asPoints[0].x = 8000; + asPoints[0].y = 8000; + asPoints[1].x = 6000; + asPoints[1].y = 8000; + asPoints[2].x = 6000; + asPoints[2].y = 6000; + + psMembers[0] = DGNCreateMultiPointElem( hNewDGN, DGNT_LINE_STRING, 3, + asPoints ); + DGNUpdateElemCore( hNewDGN, psMembers[0], 9, 0, 3, 1, 0 ); + + asPoints[0].x = 6000; + asPoints[0].y = 6000; + asPoints[1].x = 8000; + asPoints[1].y = 6000; + asPoints[2].x = 8000; + asPoints[2].y = 8000; + + psMembers[1] = DGNCreateMultiPointElem( hNewDGN, DGNT_LINE_STRING, 3, + asPoints ); + DGNUpdateElemCore( hNewDGN, psMembers[1], 9, 0, 3, 1, 0 ); + + psLine = DGNCreateComplexHeaderFromGroup( hNewDGN, + DGNT_COMPLEX_SHAPE_HEADER, + 2, psMembers ); + + DGNUpdateElemCore( hNewDGN, psLine, 9, 0, 3, 1, 0 ); + DGNAddShapeFillInfo( hNewDGN, psLine, 7 ); + + DGNWriteElement( hNewDGN, psLine ); + DGNWriteElement( hNewDGN, psMembers[0] ); + DGNWriteElement( hNewDGN, psMembers[1] ); + + DGNFreeElement( hNewDGN, psLine ); + DGNFreeElement( hNewDGN, psMembers[0] ); + DGNFreeElement( hNewDGN, psMembers[1] ); + +/* -------------------------------------------------------------------- */ +/* Write a cell with two lines. */ +/* -------------------------------------------------------------------- */ + asPoints[0].x = 7000; + asPoints[0].y = 7000; + asPoints[1].x = 5000; + asPoints[1].y = 7000; + asPoints[2].x = 5000; + asPoints[2].y = 5000; + + psMembers[0] = DGNCreateMultiPointElem( hNewDGN, DGNT_LINE_STRING, 3, + asPoints ); + DGNUpdateElemCore( hNewDGN, psMembers[0], 10, 0, 3, 1, 0 ); + + asPoints[0].x = 5000; + asPoints[0].y = 5000; + asPoints[1].x = 8000; + asPoints[1].y = 5000; + asPoints[2].x = 7000; + asPoints[2].y = 7000; + + psMembers[1] = DGNCreateMultiPointElem( hNewDGN, DGNT_LINE_STRING, 3, + asPoints ); + DGNUpdateElemCore( hNewDGN, psMembers[1], 9, 0, 3, 1, 0 ); + + asPoints[0].x = 5000; + asPoints[0].y = 5000; + + psLine = DGNCreateCellHeaderFromGroup( hNewDGN, "BE70", 1, NULL, + 2, psMembers, asPoints + 0, + 1.0, 1.0, 0.0 ); + + DGNWriteElement( hNewDGN, psLine ); + DGNWriteElement( hNewDGN, psMembers[0] ); + DGNWriteElement( hNewDGN, psMembers[1] ); + + DGNFreeElement( hNewDGN, psLine ); + DGNFreeElement( hNewDGN, psMembers[0] ); + DGNFreeElement( hNewDGN, psMembers[1] ); + +/* -------------------------------------------------------------------- */ +/* Close it. */ +/* -------------------------------------------------------------------- */ + DGNClose( hNewDGN ); + + return 0; +} + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dist/Makefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dist/Makefile new file mode 100644 index 000000000..efca3c3c6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dist/Makefile @@ -0,0 +1,18 @@ + +DGNLIB_OBJ = dgnfloat.o dgnhelp.o dgnread.o dgnwrite.o \ + dgnopen.o dgnstroke.o +CPLLIB_OBJ = cpl_conv.o cpl_dir.o cpl_error.o cpl_multiproc.o \ + cpl_path.o cpl_string.o cpl_vsil_simple.o cpl_vsisimple.o + +OBJ = $(CPLLIB_OBJ) $(DGNLIB_OBJ) + +default: dgndump dgnwritetest + +dgndump: dgndump.c $(OBJ) + $(CXX) dgndump.c $(OBJ) -o dgndump + +dgnwritetest: dgnwritetest.c $(OBJ) + $(CXX) dgndump.c $(OBJ) -o dgnwritetest + +clean: + rm -f *.o dgndump dgnwritetest diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dist/Makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dist/Makefile.vc new file mode 100644 index 000000000..f355cc236 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dist/Makefile.vc @@ -0,0 +1,27 @@ + +DGNLIB_OBJ = dgnfloat.obj dgnhelp.obj dgnread.obj dgnwrite.obj \ + dgnopen.obj dgnstroke.obj +CPLLIB_OBJ = cpl_conv.obj cpl_dir.obj cpl_error.obj cpl_multiproc.obj \ + cpl_path.obj cpl_string.obj cpl_vsil_simple.obj \ + cpl_vsisimple.obj + +OBJ = $(CPLLIB_OBJ) $(DGNLIB_OBJ) + +CPPFLAGS = /EHsc + +default: wininclude dgndump.exe dgnwritetest.exe dgnlib.dll + +dgndump.exe: dgndump.c $(OBJ) + $(CXX) dgndump.c $(OBJ) + +dgnwritetest.exe: dgnwritetest.c $(OBJ) + $(CXX) dgnwritetest.c $(OBJ) + +dgnlib.dll: $(OBJ) + link /dll /debug $(OBJ) /out:dgnlib.dll /implib:dgnlib_i.lib + +wininclude: + copy cpl_config.h.vc cpl_config.h + +clean: + del *.lib *.dll *.exe *.obj diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dist/README b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dist/README new file mode 100644 index 000000000..d3b54511d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dist/README @@ -0,0 +1,209 @@ + + DGNLIB Source Distribution + ========================== + +Current information on DGNLIB, including detailed API documentation +can be found at: + + http://dgnlib.maptools.org/ + + +Building +-------- + +This is a preliminary source distribution, and I have not gone to any +pains to make it easy to configure and build. To build please do the +following: + +== Unix: + +1. Update cpl_config.h to represent your platform accurately. Generally all +that matters is to #define WORDS_BIGENDIAN on big endian platforms. The +rest can likely be ignored.

    + +2. Build the programs. + + % make + +== Windows: + +1. Ensure you have Microsoft Visual C++ 6.x or later installed. + +2. Ensure the commandline VC++ environment is setup properly. You may need + to run the VCVARS32.BAT script from within the VC++ tree. + +3. Compile the code, and build statically linked executables for the sample + programs and DLL using the following command. + + nmake /f makefile.vc + + + +Release Notes +============= + +Release 1.11 +------------ + + o B-Spline support: + - The DGNT_BSPLINE define was bogus, replaced with DGNT_BSPLINE_POLE. + - Added support for the following B-Spline related elements: + o DGNElemBSplineSurfaceHeader (24) + o DGNElemBSplineCurveHeader (27) + o DGNElemBSplineSurfaceBoundary (25) + o DGNElemKnotWeight (26/28) + + o Added initial support for Shared Cell Definition elements + + o 3D solid/surface fixes: + - Fixed incorrect reading and writing of surftype field. + - Added boundelms field to DGNElemComplexHeader + - Added DGNCreateSolidHeaderElem() and DGNCreateSolidHeaderElemFromGroup() + - Changed DGNCreateComplexHeaderElem() and + DGNCreateComplexHeaderElemFromGroup() to only support complex chains/shapes + - Fixed bug creating solids/surfaces: totlength was 1 word too short. + - Renamed bogus define DGNSUT_SOLID to DGNSUT_SURFACE_OF_PROJECTION + + o Added support for cloning Cone elements + + o Fixes for uor handling in DGNCreate(). + + o Treat the most recently read color table as the global color table, + instead of just the first one encountered. + + o Added Text Node implementation from Ilya Beylin. + + +Release 1.10 +------------ + + o Added DGNElemTypeHasDispHdr(), and use it in DGNParseCore() to avoid trying + to read disp hdr, or attributes from inappropriate elements. + + o Check if a negative amount of attribute information is being read in + DGNParseCore(), and if so just issue a warning, and skip attempt. + + o Fixed DGNCreateCellHeaderFromGroup() to not pre-transform the bounds. + + o Fixed missing handling of min/max Z in DGNCreateMultiPointElem(). (Marius) + + o Added surface type to DGNCreateComplexHeaderElem(). (Marius) + + o DGNRad50ToAscii() reimplemented to correct a few problems. New version + provided by Armin Berg (iez.com). + + o Fixes to complex header and cell header creation to set the total group + size properly, and to set up complex header pad attributes properly. Now + passed EDG validation. + + +Release 1.9 +----------- + + o Added DGNLoadTCB(), and fixed problem when writing new elements to an + existing file without having already read the TCB. + + o Fixed serious bug in setting the index "offset" location. Critical for + writing applications. + + +Release 1.8 +----------- + + o Added support for reading 3D cone (23), 3D surface (18) and 3D solid (19) + elements based on contributions from Marius Kintel. + + o Main site moved to http://dgnlib.maptools.org/ + + +Release 1.7 +----------- + + o Improved calcuation of bounding box in DGLCreateTextElem() based on code + from Matt Kelder. + + o Added preliminary 3D write support. + + o Modified DGNCreateTextElem() arguments, and deprecated DGNCreateArcElem2D() + in favour of DGNCreateArcElem(). + + o Report transformation matrix for ViewInfo when dumping. + + +Release 1.6 +----------- + + o Bug fix in DGNGetLinkage() for DMRS recognition from Henk Jan Priester. + + o Added DGNCreateCellHeaderFromGroup() and DGNCreateCellHeaderElem() + functions in the write API. + + o Added a Microsoft VC++ makefile (Makefile.vc). + + +Release 1.5 +----------- + + o Added 2D write support! + + o Fixed collection of background color in color table (color 255) as per + bug report by Henk Jan Priester (Justcroft Technical Systems). + + o Added support for association ids (DGNLT_ASSOC_ID, DGNGetAssocID()). + + o Fixed problem with updating the DGNInfo.color_table field when a color + table is read. + + o Fixed problem with spatial queries either including all of a complex + element (and it's members) or none. + + o Added support for reading the eight view descriptions in the TCB element. + + o Added a Makefile in the source distribution. + + o Fixed error with tags in DGNFreeElement() as reported by Henk Jan Priester. + + + +Release 1.4 +----------- + + o Added "fast" spatial searching via the DGNSetSpatialFilter() function. + This basically just reads the extents information at beginning of + geometric features to see if they should be returned. + + o Actually extract the global origin from the TCB, and use it when + transforming points. Only a few files (ie. Brazos County roads.rds) use + a non-zero origin. + + o Added experimental multi byte text support from Ason Kang + (hiska@netian.com). + + o Add experimental support for 3D files. Somes 3d elements not supported. + 3d quaterion for ellipse and arc elements not utilized. + + o Added preliminary support for reading cell header structures based + on input from Mike Cane. + + o Moved a bunch of stuff into dgnread.cpp. + + o Build default colortable into dgnhelp.cpp for use of DGNLookupColor(). + + o Added DGNGetShapeFillInfo(). + + o DGNGetShapeFillInfo() now tries to handle multi-part attribute linkages. + + +Release 1.3 +----------- + + o Added raw_data/raw_bytes members of DGNElemCore, and DGNSetOptions() + to allow control of when raw element image is retained. + + o Added -r flag to dgndump to dump raw contents of desired elements. + + o Add CPL_CVSID() to simplify tracking of versions. + + o Fixed bug with freeing element index in DGNClose(). + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dist/cpl_config.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dist/cpl_config.h new file mode 100644 index 000000000..7ca7d3957 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/dist/cpl_config.h @@ -0,0 +1,57 @@ +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ + +#define CPL_MULTIPROC_STUB 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FLOAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_VALUES_H 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* The size of a `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of a `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/drv_dgn.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/drv_dgn.html new file mode 100644 index 000000000..8708e509a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/drv_dgn.html @@ -0,0 +1,147 @@ + + +Microstation DGN + + + + +

    Microstation DGN

    + +Microstation DGN files from Microstation versions predating version 8.0 +are supported for reading. The entire file is represented as one layer +(named "elements").

    + +DGN files are considered to have no georeferencing information through OGR. +Features will all have the following generic attributes: + +

      +
    • Type: The integer type code as listed below in supported elements. +
    • Level: The DGN level number (0-63). +
    • GraphicGroup: The graphic group number. +
    • ColorIndex: The color index from the dgn palette. +
    • Weight: The drawing weight (thickness) for the element. +
    • Style: The style value for the element. +
    + +DGN files do not contain spatial indexes; however, the DGN driver does take +advantage of the extents information at the beginning of each element to +minimize processing of elements outside the current spatial filter window +when in effect.

    + +

    Supported Elements

    + +The following element types are supported:

    + +

      +
    • Line (3): Line geometry. +
    • Line String (4): Multi segment line geometry. +
    • Shape (6): Polygon geometry. +
    • Curve (11): Approximated as a line geometry. +
    • B-Spline (21): Treated (inaccurately) as a line geometry. +
    • Arc (16): Approximated as a line geometry. +
    • Ellipse (15): Approximated as a line geometry. +
    • Text (17): Treated as a point geometry. +
    + +Generally speaking any concept of complex objects, and cells as associated +components is lost. Each component of a complex object or cell is treated +as a independent feature.

    + +

    Styling Information

    + +Some drawing information about features can be extracted from the ColorIndex, +Weight and Style generic attributes; however, for all features an OGR +style string has been prepared with the values encoded in ready-to-use form +for applications supporting OGR style strings.

    + +The various kinds of linear geomtries will carry style information indicating +the color, thickness and line style (ie. dotted, solid, etc).

    + +Polygons (Shape elements) will carry styling information for the edge as well +as a fill color if provided. Fill patterns are not supported.

    + +Text elements will contain the text, angle, color and size information +(expressed in ground units) in the style string.

    + +

    Creation Issues

    + +2D DGN files may be written with OGR with significant limitations:

    + +

      +
    • Output features have the usual fixed DGN attributes. Attempts to create +any other fields will fail.

      + +

    • Virtual no effort is currently made to translate OGR feature style +strings back into DGN representation information.

      + +

    • POINT geometries that are not text (Text is NULL, and the feature +style string is not a LABEL) will be translated as a degenerate (0 length) +line element.

      + +

    • Polygon, and multipolygon objects will be translated to simple polygons +with all rings other than the first discarded.

      + +

    • Polygons and line strings with too many vertices will be split into +a group of elmements prefixed with a Complex Shape Header or Complex Chain +Header element as appropriate.

      + +

    • A seed file must be provided (or if not provided, +$PREFIX/share/gdal/seed_2d.dgn will be used). Many aspects of the resulting +DGN file are determined by the seed file, and cannot be affected via OGR, +such as initial view window.

      + +

    • The various collection geometries other than MultiPolygon are completely +discarded at this time.

      + +

    • Geometries which fall outside the "design plane" of the seed file will +be discarded, or corrupted in unpredictable ways.

      + +

    • DGN files can only have one layer. Attempts +to create more than one layer in a DGN file will fail.

      + +

    + +The dataset creation supports the following options:

    + +

      + +
    • 3D=YES or NO: Determine whether 2D (seed_2d.dgn) +or 3D (seed_3d.dgn) seed file should be used. This option is ignored if the +SEED option is provided.

      + +

    • SEED=filename: Override the seed file to use.

      + +

    • COPY_WHOLE_SEED_FILE=YES/NO: Indicate whether the whole +seed file should be copied. If not, only the first three elements (and +potentially the color table) will be copied. Default is NO.

      + +

    • COPY_SEED_FILE_COLOR_TABLE=YES/NO: Indicates whether the +color table should be copied from the seed file. By default this is NO.

      + +

    • MASTER_UNIT_NAME=name: Override the master unit name from +the seed file with the provided one or two character unit name.

      + +

    • SUB_UNIT_NAME=name: Override the sub unit name from +the seed file with the provided one or two character unit name.

      + +

    • SUB_UNITS_PER_MASTER_UNIT=count: Override the number of +subunits per master unit. By default the seed file value is used.

      + +

    • UOR_PER_SUB_UNIT=count: Override the number of +UORs (Units of Resolution) per sub unit. By default the seed file value is +used.

      + +

    • ORIGIN=x,y,z: Override the origin of the design plane. By +default the origin from the seed file is used.

      + +

    + +
    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/makefile.vc new file mode 100644 index 000000000..516b8904d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/makefile.vc @@ -0,0 +1,23 @@ + +LL_OBJ = dgnopen.obj dgnread.obj dgnfloat.obj dgnstroke.obj dgnhelp.obj\ + dgnwrite.obj +OGR_OBJ = ogrdgndriver.obj ogrdgndatasource.obj ogrdgnlayer.obj +OBJ = $(LL_OBJ) $(OGR_OBJ) + +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + +dgndump.exe: $(LL_OBJ) dgndump.obj + cl /Zi dgndump.obj $(LL_OBJ) $(GDAL_ROOT)/port/cpl.lib $(LIBS) + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/ogr_dgn.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/ogr_dgn.h new file mode 100644 index 000000000..654d64a7e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/ogr_dgn.h @@ -0,0 +1,119 @@ +/****************************************************************************** + * $Id: ogr_dgn.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: OGR Driver for DGN Reader. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam (warmerdam@pobox.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_DGN_H_INCLUDED +#define _OGR_DGN_H_INCLUDED + +#include "dgnlib.h" +#include "ogrsf_frmts.h" + +/************************************************************************/ +/* OGRDGNLayer */ +/************************************************************************/ + +class OGRDGNLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + + int iNextShapeId; + + DGNHandle hDGN; + int bUpdate; + + char *pszLinkFormat; + + OGRFeature *ElementToFeature( DGNElemCore * ); + + void ConsiderBrush( DGNElemCore *, const char *pszPen, + OGRFeature *poFeature ); + + DGNElemCore **LineStringToElementGroup( OGRLineString *, int ); + DGNElemCore **TranslateLabel( OGRFeature * ); + + int bHaveSimpleQuery; + OGRFeature *poEvalFeature; + + OGRErr CreateFeatureWithGeom( OGRFeature *, OGRGeometry * ); + + public: + OGRDGNLayer( const char * pszName, DGNHandle hDGN, + int bUpdate ); + ~OGRDGNLayer(); + + void SetSpatialFilter( OGRGeometry * ); + + void ResetReading(); + OGRFeature * GetNextFeature(); + OGRFeature * GetFeature( GIntBig nFeatureId ); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ); + + OGRErr ICreateFeature( OGRFeature *poFeature ); + +}; + +/************************************************************************/ +/* OGRDGNDataSource */ +/************************************************************************/ + +class OGRDGNDataSource : public OGRDataSource +{ + OGRDGNLayer **papoLayers; + int nLayers; + + char *pszName; + DGNHandle hDGN; + + char **papszOptions; + + public: + OGRDGNDataSource(); + ~OGRDGNDataSource(); + + int Open( const char *, int bTestOpen, int bUpdate ); + int PreCreate( const char *, char ** ); + + OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_DGN_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/ogrdgndatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/ogrdgndatasource.cpp new file mode 100644 index 000000000..bdd8db0b8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/ogrdgndatasource.cpp @@ -0,0 +1,335 @@ +/****************************************************************************** + * $Id: ogrdgndatasource.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam (warmerdam@pobox.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dgn.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrdgndatasource.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +/************************************************************************/ +/* OGRDGNDataSource() */ +/************************************************************************/ + +OGRDGNDataSource::OGRDGNDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + hDGN = NULL; + pszName = NULL; + papszOptions = NULL; +} + +/************************************************************************/ +/* ~OGRDGNDataSource() */ +/************************************************************************/ + +OGRDGNDataSource::~OGRDGNDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + CPLFree( pszName ); + CSLDestroy( papszOptions ); + + if( hDGN != NULL ) + DGNClose( hDGN ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRDGNDataSource::Open( const char * pszNewName, + int bTestOpen, + int bUpdate ) + +{ + CPLAssert( nLayers == 0 ); + +/* -------------------------------------------------------------------- */ +/* For now we require files to have the .dgn or .DGN */ +/* extension. Eventually we will implement a more */ +/* sophisticated test to see if it is a dgn file. */ +/* -------------------------------------------------------------------- */ + if( bTestOpen ) + { + FILE *fp; + GByte abyHeader[512]; + int nHeaderBytes = 0; + + fp = VSIFOpen( pszNewName, "rb" ); + if( fp == NULL ) + return FALSE; + + nHeaderBytes = (int) VSIFRead( abyHeader, 1, sizeof(abyHeader), fp ); + + VSIFClose( fp ); + + if( nHeaderBytes < 512 ) + return FALSE; + + if( !DGNTestOpen( abyHeader, nHeaderBytes ) ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try to open the file as a DGN file. */ +/* -------------------------------------------------------------------- */ + hDGN = DGNOpen( pszNewName, bUpdate ); + if( hDGN == NULL ) + { + if( !bTestOpen ) + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open %s as a Microstation .dgn file.\n", + pszNewName ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRDGNLayer *poLayer; + + poLayer = new OGRDGNLayer( "elements", hDGN, bUpdate ); + pszName = CPLStrdup( pszNewName ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRDGNLayer **) + CPLRealloc( papoLayers, sizeof(OGRDGNLayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + + return TRUE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDGNDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRDGNDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + + +/************************************************************************/ +/* PreCreate() */ +/* */ +/* Called by OGRDGNDriver::Create() method to setup a stub */ +/* OGRDataSource object without the associated file created */ +/* yet. It will be created by theICreateLayer() call. */ +/************************************************************************/ + +int OGRDGNDataSource::PreCreate( const char *pszFilename, + char **papszOptions ) + +{ + this->papszOptions = CSLDuplicate( papszOptions ); + pszName = CPLStrdup( pszFilename ); + + return TRUE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer *OGRDGNDataSource::ICreateLayer( const char *pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eGeomType, + char **papszExtraOptions ) + +{ + const char *pszSeed, *pszMasterUnit = "m", *pszSubUnit = "cm"; + const char *pszValue; + int nUORPerSU=1, nSUPerMU=100; + int nCreationFlags = 0, b3DRequested; + double dfOriginX = -21474836.0, /* default origin centered on zero */ + dfOriginY = -21474836.0, /* with two decimals of precision */ + dfOriginZ = -21474836.0; + +/* -------------------------------------------------------------------- */ +/* Ensure only one layer gets created. */ +/* -------------------------------------------------------------------- */ + if( nLayers > 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DGN driver only supports one layer will all the elements in it." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* If the coordinate system is geographic, we should use a */ +/* localized default origin and resolution. */ +/* -------------------------------------------------------------------- */ + if( poSRS != NULL && poSRS->IsGeographic() ) + { + dfOriginX = -200.0; + dfOriginY = -200.0; + + pszMasterUnit = "d"; + pszSubUnit = "s"; + nSUPerMU = 3600; + nUORPerSU = 1000; + } + +/* -------------------------------------------------------------------- */ +/* Parse out various creation options. */ +/* -------------------------------------------------------------------- */ + papszOptions = CSLInsertStrings( papszOptions, 0, papszExtraOptions ); + + b3DRequested = CSLFetchBoolean( papszOptions, "3D", + wkbHasZ(eGeomType) ); + + pszSeed = CSLFetchNameValue( papszOptions, "SEED" ); + if( pszSeed ) + nCreationFlags |= DGNCF_USE_SEED_ORIGIN | DGNCF_USE_SEED_UNITS; + else if( b3DRequested ) + pszSeed = CPLFindFile( "gdal", "seed_3d.dgn" ); + else + pszSeed = CPLFindFile( "gdal", "seed_2d.dgn" ); + + if( pszSeed == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No seed file provided, and unable to find seed_2d.dgn." ); + return NULL; + } + + if( CSLFetchBoolean( papszOptions, "COPY_WHOLE_SEED_FILE", TRUE ) ) + nCreationFlags |= DGNCF_COPY_WHOLE_SEED_FILE; + if( CSLFetchBoolean( papszOptions, "COPY_SEED_FILE_COLOR_TABLE", TRUE ) ) + nCreationFlags |= DGNCF_COPY_SEED_FILE_COLOR_TABLE; + + pszValue = CSLFetchNameValue( papszOptions, "MASTER_UNIT_NAME" ); + if( pszValue != NULL ) + { + nCreationFlags &= ~DGNCF_USE_SEED_UNITS; + pszMasterUnit = pszValue; + } + + pszValue = CSLFetchNameValue( papszOptions, "SUB_UNIT_NAME" ); + if( pszValue != NULL ) + { + nCreationFlags &= ~DGNCF_USE_SEED_UNITS; + pszSubUnit = pszValue; + } + + + pszValue = CSLFetchNameValue( papszOptions, "SUB_UNITS_PER_MASTER_UNIT" ); + if( pszValue != NULL ) + { + nCreationFlags &= ~DGNCF_USE_SEED_UNITS; + nSUPerMU = atoi(pszValue); + } + + pszValue = CSLFetchNameValue( papszOptions, "UOR_PER_SUB_UNIT" ); + if( pszValue != NULL ) + { + nCreationFlags &= ~DGNCF_USE_SEED_UNITS; + nUORPerSU = atoi(pszValue); + } + + pszValue = CSLFetchNameValue( papszOptions, "ORIGIN" ); + if( pszValue != NULL ) + { + char **papszTuple = CSLTokenizeStringComplex( pszValue, " ,", + FALSE, FALSE ); + + nCreationFlags &= ~DGNCF_USE_SEED_ORIGIN; + if( CSLCount(papszTuple) == 3 ) + { + dfOriginX = CPLAtof(papszTuple[0]); + dfOriginY = CPLAtof(papszTuple[1]); + dfOriginZ = CPLAtof(papszTuple[2]); + } + else if( CSLCount(papszTuple) == 2 ) + { + dfOriginX = CPLAtof(papszTuple[0]); + dfOriginY = CPLAtof(papszTuple[1]); + dfOriginZ = 0.0; + } + else + { + CSLDestroy(papszTuple); + CPLError( CE_Failure, CPLE_AppDefined, + "ORIGIN is not a valid 2d or 3d tuple.\n" + "Separate tuple values with comma." ); + return FALSE; + } + CSLDestroy(papszTuple); + } + +/* -------------------------------------------------------------------- */ +/* Try creating the base file. */ +/* -------------------------------------------------------------------- */ + hDGN = DGNCreate( pszName, pszSeed, nCreationFlags, + dfOriginX, dfOriginY, dfOriginZ, + nSUPerMU, nUORPerSU, pszMasterUnit, pszSubUnit ); + if( hDGN == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRDGNLayer *poLayer; + + poLayer = new OGRDGNLayer( pszLayerName, hDGN, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRDGNLayer **) + CPLRealloc( papoLayers, sizeof(OGRDGNLayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + + return poLayer; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/ogrdgndriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/ogrdgndriver.cpp new file mode 100644 index 000000000..95d309207 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/ogrdgndriver.cpp @@ -0,0 +1,142 @@ +/****************************************************************************** + * $Id: ogrdgndriver.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRDGNDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam (warmerdam@pobox.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dgn.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrdgndriver.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static int OGRDGNDriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + return poOpenInfo->fpL != NULL && + poOpenInfo->nHeaderBytes >= 512 && + DGNTestOpen(poOpenInfo->pabyHeader, poOpenInfo->nHeaderBytes); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRDGNDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + OGRDGNDataSource *poDS; + + if( !OGRDGNDriverIdentify(poOpenInfo) ) + return NULL; + + poDS = new OGRDGNDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, TRUE, (poOpenInfo->eAccess == GA_Update) ) + || poDS->GetLayerCount() == 0 ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRDGNDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ +/* -------------------------------------------------------------------- */ +/* Return a new OGRDataSource() */ +/* -------------------------------------------------------------------- */ + OGRDGNDataSource *poDS = NULL; + + poDS = new OGRDGNDataSource(); + + if( !poDS->PreCreate( pszName, papszOptions ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* RegisterOGRDGN() */ +/************************************************************************/ + +void RegisterOGRDGN() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "DGN" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "DGN" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Microstation DGN" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "dgn" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_dgn.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, "" ); + + poDriver->pfnOpen = OGRDGNDriverOpen; + poDriver->pfnIdentify = OGRDGNDriverIdentify; + poDriver->pfnCreate = OGRDGNDriverCreate; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/ogrdgnlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/ogrdgnlayer.cpp new file mode 100644 index 000000000..aecf85fa2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/ogrdgnlayer.cpp @@ -0,0 +1,1167 @@ +/****************************************************************************** + * $Id: ogrdgnlayer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRDGNLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam (warmerdam@pobox.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dgn.h" +#include "cpl_conv.h" +#include "ogr_featurestyle.h" +#include "ogr_api.h" +#include + +CPL_CVSID("$Id: ogrdgnlayer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGRDGNLayer() */ +/************************************************************************/ + +OGRDGNLayer::OGRDGNLayer( const char * pszName, DGNHandle hDGN, + int bUpdate ) + +{ + this->hDGN = hDGN; + this->bUpdate = bUpdate; + +/* -------------------------------------------------------------------- */ +/* Work out what link format we are using. */ +/* -------------------------------------------------------------------- */ + OGRFieldType eLinkFieldType; + + pszLinkFormat = (char *) CPLGetConfigOption( "DGN_LINK_FORMAT", "FIRST" ); + if( EQUAL(pszLinkFormat,"FIRST") ) + eLinkFieldType = OFTInteger; + else if( EQUAL(pszLinkFormat,"LIST") ) + eLinkFieldType = OFTIntegerList; + else if( EQUAL(pszLinkFormat,"STRING") ) + eLinkFieldType = OFTString; + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "DGN_LINK_FORMAT=%s, but only FIRST, LIST or STRING supported.", + pszLinkFormat ); + pszLinkFormat = (char *) "FIRST"; + eLinkFieldType = OFTInteger; + } + pszLinkFormat = CPLStrdup(pszLinkFormat); + +/* -------------------------------------------------------------------- */ +/* Create the feature definition. */ +/* -------------------------------------------------------------------- */ + poFeatureDefn = new OGRFeatureDefn( pszName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + OGRFieldDefn oField( "", OFTInteger ); + +/* -------------------------------------------------------------------- */ +/* Element type */ +/* -------------------------------------------------------------------- */ + oField.SetName( "Type" ); + oField.SetType( OFTInteger ); + oField.SetWidth( 2 ); + oField.SetPrecision( 0 ); + poFeatureDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* Level number. */ +/* -------------------------------------------------------------------- */ + oField.SetName( "Level" ); + oField.SetType( OFTInteger ); + oField.SetWidth( 2 ); + oField.SetPrecision( 0 ); + poFeatureDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* graphic group */ +/* -------------------------------------------------------------------- */ + oField.SetName( "GraphicGroup" ); + oField.SetType( OFTInteger ); + oField.SetWidth( 4 ); + oField.SetPrecision( 0 ); + poFeatureDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* ColorIndex */ +/* -------------------------------------------------------------------- */ + oField.SetName( "ColorIndex" ); + oField.SetType( OFTInteger ); + oField.SetWidth( 3 ); + oField.SetPrecision( 0 ); + poFeatureDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* Weight */ +/* -------------------------------------------------------------------- */ + oField.SetName( "Weight" ); + oField.SetType( OFTInteger ); + oField.SetWidth( 2 ); + oField.SetPrecision( 0 ); + poFeatureDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* Style */ +/* -------------------------------------------------------------------- */ + oField.SetName( "Style" ); + oField.SetType( OFTInteger ); + oField.SetWidth( 1 ); + oField.SetPrecision( 0 ); + poFeatureDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* EntityNum */ +/* -------------------------------------------------------------------- */ + oField.SetName( "EntityNum" ); + oField.SetType( eLinkFieldType ); + oField.SetWidth( 0 ); + oField.SetPrecision( 0 ); + poFeatureDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* MSLink */ +/* -------------------------------------------------------------------- */ + oField.SetName( "MSLink" ); + oField.SetType( eLinkFieldType ); + oField.SetWidth( 0 ); + oField.SetPrecision( 0 ); + poFeatureDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* Text */ +/* -------------------------------------------------------------------- */ + oField.SetName( "Text" ); + oField.SetType( OFTString ); + oField.SetWidth( 0 ); + oField.SetPrecision( 0 ); + poFeatureDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* Create template feature for evaluating simple expressions. */ +/* -------------------------------------------------------------------- */ + bHaveSimpleQuery = FALSE; + poEvalFeature = new OGRFeature( poFeatureDefn ); + + /* TODO: I am intending to keep track of simple attribute queries (ones + using only FID, Type and Level and short circuiting their operation + based on the index. However, there are some complexities with + complex elements, and spatial queries that have caused me to put it + off for now. + */ +} + +/************************************************************************/ +/* ~OGRDGNLayer() */ +/************************************************************************/ + +OGRDGNLayer::~OGRDGNLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "Mem", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + delete poEvalFeature; + + poFeatureDefn->Release(); + + CPLFree( pszLinkFormat ); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRDGNLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) + +{ + if( !InstallFilter(poGeomIn) ) + return; + + if( m_poFilterGeom != NULL ) + { + DGNSetSpatialFilter( hDGN, + m_sFilterEnvelope.MinX, + m_sFilterEnvelope.MinY, + m_sFilterEnvelope.MaxX, + m_sFilterEnvelope.MaxY ); + } + else + { + DGNSetSpatialFilter( hDGN, 0.0, 0.0, 0.0, 0.0 ); + } + + ResetReading(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRDGNLayer::ResetReading() + +{ + iNextShapeId = 0; + DGNRewind( hDGN ); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRDGNLayer::GetFeature( GIntBig nFeatureId ) + +{ + OGRFeature *poFeature; + DGNElemCore *psElement; + + if( nFeatureId > INT_MAX || !DGNGotoElement( hDGN, (int)nFeatureId ) ) + return NULL; + + // We should likely clear the spatial search region as it affects + // DGNReadElement() but I will defer that for now. + + psElement = DGNReadElement( hDGN ); + poFeature = ElementToFeature( psElement ); + DGNFreeElement( hDGN, psElement ); + + if( poFeature == NULL ) + return NULL; + + if( poFeature->GetFID() != nFeatureId ) + { + delete poFeature; + return NULL; + } + + return poFeature; +} + +/************************************************************************/ +/* ConsiderBrush() */ +/* */ +/* Method to set the style for a polygon, including a brush if */ +/* appropriate. */ +/************************************************************************/ + +void OGRDGNLayer::ConsiderBrush( DGNElemCore *psElement, const char *pszPen, + OGRFeature *poFeature ) + +{ + int gv_red, gv_green, gv_blue; + char szFullStyle[256]; + int nFillColor; + + if( DGNGetShapeFillInfo( hDGN, psElement, &nFillColor ) + && DGNLookupColor( hDGN, nFillColor, + &gv_red, &gv_green, &gv_blue ) ) + { + sprintf( szFullStyle, + "BRUSH(fc:#%02x%02x%02x,id:\"ogr-brush-0\")", + gv_red, gv_green, gv_blue ); + + if( nFillColor != psElement->color ) + { + strcat( szFullStyle, ";" ); + strcat( szFullStyle, pszPen ); + } + poFeature->SetStyleString( szFullStyle ); + } + else + poFeature->SetStyleString( pszPen ); +} + +/************************************************************************/ +/* ElementToFeature() */ +/************************************************************************/ + +OGRFeature *OGRDGNLayer::ElementToFeature( DGNElemCore *psElement ) + +{ + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + poFeature->SetFID( psElement->element_id ); + poFeature->SetField( "Type", psElement->type ); + poFeature->SetField( "Level", psElement->level ); + poFeature->SetField( "GraphicGroup", psElement->graphic_group ); + poFeature->SetField( "ColorIndex", psElement->color ); + poFeature->SetField( "Weight", psElement->weight ); + poFeature->SetField( "Style", psElement->style ); + + + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Collect linkage information */ +/* -------------------------------------------------------------------- */ +#define MAX_LINK 100 + int anEntityNum[MAX_LINK], anMSLink[MAX_LINK]; + unsigned char *pabyData; + int iLink=0, nLinkCount=0; + + anEntityNum[0] = 0; + anMSLink[0] = 0; + + pabyData = DGNGetLinkage( hDGN, psElement, iLink, NULL, + anEntityNum+iLink, anMSLink+iLink, NULL ); + while( pabyData && nLinkCount < MAX_LINK ) + { + iLink++; + + if( anEntityNum[nLinkCount] != 0 || anMSLink[nLinkCount] != 0 ) + nLinkCount++; + + anEntityNum[nLinkCount] = 0; + anMSLink[nLinkCount] = 0; + + pabyData = DGNGetLinkage( hDGN, psElement, iLink, NULL, + anEntityNum+nLinkCount, anMSLink+nLinkCount, + NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Apply attribute linkage to feature. */ +/* -------------------------------------------------------------------- */ + if( nLinkCount > 0 ) + { + if( EQUAL(pszLinkFormat,"FIRST") ) + { + poFeature->SetField( "EntityNum", anEntityNum[0] ); + poFeature->SetField( "MSLink", anMSLink[0] ); + } + else if( EQUAL(pszLinkFormat,"LIST") ) + { + poFeature->SetField( "EntityNum", nLinkCount, anEntityNum ); + poFeature->SetField( "MSLink", nLinkCount, anMSLink ); + } + else if( EQUAL(pszLinkFormat,"STRING") ) + { + char szEntityList[MAX_LINK*9], szMSLinkList[MAX_LINK*9]; + int nEntityLen = 0, nMSLinkLen = 0; + + for( iLink = 0; iLink < nLinkCount; iLink++ ) + { + if( iLink != 0 ) + { + szEntityList[nEntityLen++] = ','; + szMSLinkList[nMSLinkLen++] = ','; + } + + sprintf( szEntityList + nEntityLen, "%d", anEntityNum[iLink]); + sprintf( szMSLinkList + nMSLinkLen, "%d", anMSLink[iLink] ); + + nEntityLen += strlen(szEntityList + nEntityLen ); + nMSLinkLen += strlen(szMSLinkList + nMSLinkLen ); + } + + poFeature->SetField( "EntityNum", szEntityList ); + poFeature->SetField( "MSLink", szMSLinkList ); + } + } + +/* -------------------------------------------------------------------- */ +/* Lookup color. */ +/* -------------------------------------------------------------------- */ + char gv_color[128]; + int gv_red, gv_green, gv_blue; + char szFSColor[128], szPen[256]; + + szFSColor[0] = '\0'; + if( DGNLookupColor( hDGN, psElement->color, + &gv_red, &gv_green, &gv_blue ) ) + { + sprintf( gv_color, "%f %f %f 1.0", + gv_red / 255.0, gv_green / 255.0, gv_blue / 255.0 ); + + sprintf( szFSColor, "c:#%02x%02x%02x", + gv_red, gv_green, gv_blue ); + } + +/* -------------------------------------------------------------------- */ +/* Generate corresponding PEN style. */ +/* -------------------------------------------------------------------- */ + if( psElement->style == DGNS_SOLID ) + sprintf( szPen, "PEN(id:\"ogr-pen-0\"" ); + else if( psElement->style == DGNS_DOTTED ) + sprintf( szPen, "PEN(id:\"ogr-pen-5\"" ); + else if( psElement->style == DGNS_MEDIUM_DASH ) + sprintf( szPen, "PEN(id:\"ogr-pen-2\"" ); + else if( psElement->style == DGNS_LONG_DASH ) + sprintf( szPen, "PEN(id:\"ogr-pen-4\"" ); + else if( psElement->style == DGNS_DOT_DASH ) + sprintf( szPen, "PEN(id:\"ogr-pen-6\"" ); + else if( psElement->style == DGNS_SHORT_DASH ) + sprintf( szPen, "PEN(id:\"ogr-pen-3\"" ); + else if( psElement->style == DGNS_DASH_DOUBLE_DOT ) + sprintf( szPen, "PEN(id:\"ogr-pen-7\"" ); + else if( psElement->style == DGNS_LONG_DASH_SHORT_DASH ) + sprintf( szPen, "PEN(p:\"10px 5px 4px 5px\"" ); + else + sprintf( szPen, "PEN(id:\"ogr-pen-0\"" ); + + if( strlen(szFSColor) > 0 ) + sprintf( szPen+strlen(szPen), ",%s", szFSColor ); + + if( psElement->weight > 1 ) + sprintf( szPen+strlen(szPen), ",w:%dpx", psElement->weight ); + + strcat( szPen, ")" ); + + switch( psElement->stype ) + { + case DGNST_MULTIPOINT: + if( psElement->type == DGNT_SHAPE ) + { + OGRLinearRing *poLine = new OGRLinearRing(); + OGRPolygon *poPolygon = new OGRPolygon(); + DGNElemMultiPoint *psEMP = (DGNElemMultiPoint *) psElement; + + poLine->setNumPoints( psEMP->num_vertices ); + for( int i = 0; i < psEMP->num_vertices; i++ ) + { + poLine->setPoint( i, + psEMP->vertices[i].x, + psEMP->vertices[i].y, + psEMP->vertices[i].z ); + } + + poPolygon->addRingDirectly( poLine ); + + poFeature->SetGeometryDirectly( poPolygon ); + + ConsiderBrush( psElement, szPen, poFeature ); + } + else if( psElement->type == DGNT_CURVE ) + { + DGNElemMultiPoint *psEMP = (DGNElemMultiPoint *) psElement; + OGRLineString *poLine = new OGRLineString(); + DGNPoint *pasPoints; + int nPoints; + + nPoints = 5 * psEMP->num_vertices; + pasPoints = (DGNPoint *) CPLMalloc(sizeof(DGNPoint) * nPoints); + + DGNStrokeCurve( hDGN, psEMP, nPoints, pasPoints ); + + poLine->setNumPoints( nPoints ); + for( int i = 0; i < nPoints; i++ ) + { + poLine->setPoint( i, + pasPoints[i].x, + pasPoints[i].y, + pasPoints[i].z ); + } + + poFeature->SetGeometryDirectly( poLine ); + CPLFree( pasPoints ); + + poFeature->SetStyleString( szPen ); + } + else + { + OGRLineString *poLine = new OGRLineString(); + DGNElemMultiPoint *psEMP = (DGNElemMultiPoint *) psElement; + + if( psEMP->num_vertices > 0 ) + { + poLine->setNumPoints( psEMP->num_vertices ); + for( int i = 0; i < psEMP->num_vertices; i++ ) + { + poLine->setPoint( i, + psEMP->vertices[i].x, + psEMP->vertices[i].y, + psEMP->vertices[i].z ); + } + + poFeature->SetGeometryDirectly( poLine ); + } + + poFeature->SetStyleString( szPen ); + } + break; + + case DGNST_ARC: + { + OGRLineString *poLine = new OGRLineString(); + DGNElemArc *psArc = (DGNElemArc *) psElement; + DGNPoint asPoints[90]; + int nPoints; + + nPoints = (int) (MAX(1,ABS(psArc->sweepang) / 5) + 1); + DGNStrokeArc( hDGN, psArc, nPoints, asPoints ); + + poLine->setNumPoints( nPoints ); + for( int i = 0; i < nPoints; i++ ) + { + poLine->setPoint( i, + asPoints[i].x, + asPoints[i].y, + asPoints[i].z ); + } + + poFeature->SetGeometryDirectly( poLine ); + poFeature->SetStyleString( szPen ); + } + break; + + case DGNST_TEXT: + { + OGRPoint *poPoint = new OGRPoint(); + DGNElemText *psText = (DGNElemText *) psElement; + char *pszOgrFS; + + poPoint->setX( psText->origin.x ); + poPoint->setY( psText->origin.y ); + poPoint->setZ( psText->origin.z ); + + poFeature->SetGeometryDirectly( poPoint ); + + pszOgrFS = (char *) CPLMalloc(strlen(psText->string) + 150); + + // setup the basic label. + sprintf( pszOgrFS, "LABEL(t:\"%s\"", psText->string ); + + // set the color if we have it. + if( strlen(szFSColor) > 0 ) + sprintf( pszOgrFS+strlen(pszOgrFS), ",%s", szFSColor ); + + // Add the size info in ground units. + if( ABS(psText->height_mult) >= 6.0 ) + CPLsprintf( pszOgrFS+strlen(pszOgrFS), ",s:%dg", + (int) psText->height_mult ); + else if( ABS(psText->height_mult) > 0.1 ) + CPLsprintf( pszOgrFS+strlen(pszOgrFS), ",s:%.3fg", + psText->height_mult ); + else + CPLsprintf( pszOgrFS+strlen(pszOgrFS), ",s:%.12fg", + psText->height_mult ); + + // Add the font name. Name it MstnFont if not available + // in the font list. #3392 + static const char *papszFontList[] = + { "STANDARD", "WORKING", "FANCY", "ENGINEERING", "NEWZERO", "STENCEL", //0-5 + "USTN_FANCY", "COMPRESSED", "STENCEQ", NULL, "hand", "ARCH", //6-11 + "ARCHB", NULL, NULL, "IGES1001", "IGES1002", "IGES1003", //12-17 + "CENTB", "MICROS", NULL, NULL, "ISOFRACTIONS", "ITALICS", //18-23 + "ISO30", NULL, "GREEK", "ISOREC", "Isoeq", NULL, //24-29 + "ISO_FONTLEFT", "ISO_FONTRIGHT", "INTL_ENGINEERING", "INTL_WORKING", "ISOITEQ", NULL, //30-35 + "USTN FONT 26", NULL, NULL, NULL, NULL, "ARCHITECTURAL", //36-41 + "BLOCK_OUTLINE", "LOW_RES_FILLED", NULL, NULL, NULL, NULL, //42-47 + NULL, NULL, "UPPERCASE", NULL, NULL, NULL, //48-53 + NULL, NULL, NULL, NULL, NULL, NULL, //54-49 + "FONT060", "din", "dinit", "helvl", "HELVLIT", "helv", //60-65 + "HELVIT", "cent", "CENTIT", "SCRIPT", NULL, NULL, //66-71 + NULL, NULL, NULL, NULL, "MICROQ", "dotfont", //72-77 + "DOTIT", NULL, NULL, NULL, NULL, NULL, //78-83 + NULL, NULL, NULL, NULL, NULL, NULL, //84-89 + NULL, NULL, "FONT092", NULL, "FONT094", NULL, //90-95 + NULL, NULL, NULL, NULL, "ANSI_SYMBOLS", "FEATURE_CONTROL_SYSMBOLS", //96-101 + "SYMB_FAST", NULL, NULL, "INTL_ISO", "INTL_ISO_EQUAL", "INTL_ISO_ITALIC", //102-107 + "INTL_ISO_ITALIC_EQUAL" }; //108 + + if(psText->font_id <= 108 && papszFontList[psText->font_id] != NULL ) + { + sprintf( pszOgrFS+strlen(pszOgrFS), ",f:%s", + papszFontList[psText->font_id] ); + } + else + { + sprintf( pszOgrFS+strlen(pszOgrFS), ",f:MstnFont%d", + psText->font_id ); + } + + // Add the angle, if not horizontal + if( psText->rotation != 0.0 ) + sprintf( pszOgrFS+strlen(pszOgrFS), ",a:%d", + (int) (psText->rotation+0.5) ); + + strcat( pszOgrFS, ")" ); + + poFeature->SetStyleString( pszOgrFS ); + CPLFree( pszOgrFS ); + + poFeature->SetField( "Text", psText->string ); + } + break; + + case DGNST_COMPLEX_HEADER: + { + DGNElemComplexHeader *psHdr = (DGNElemComplexHeader *) psElement; + int iChild; + OGRMultiLineString oChildren; + + /* collect subsequent child geometries. */ + // we should disable the spatial filter ... add later. + for( iChild = 0; iChild < psHdr->numelems; iChild++ ) + { + OGRFeature *poChildFeature = NULL; + DGNElemCore *psChildElement; + + psChildElement = DGNReadElement( hDGN ); + // should verify complex bit set, not another header. + + if( psChildElement != NULL ) + { + poChildFeature = ElementToFeature( psChildElement ); + DGNFreeElement( hDGN, psChildElement ); + } + + if( poChildFeature != NULL + && poChildFeature->GetGeometryRef() != NULL ) + { + OGRGeometry *poGeom; + + poGeom = poChildFeature->GetGeometryRef(); + if( wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) + oChildren.addGeometry( poGeom ); + } + + if( poChildFeature != NULL ) + delete poChildFeature; + } + + // Try to assemble into polygon geometry. + OGRGeometry *poGeom; + + if( psElement->type == DGNT_COMPLEX_SHAPE_HEADER ) + poGeom = (OGRPolygon *) + OGRBuildPolygonFromEdges( (OGRGeometryH) &oChildren, + TRUE, TRUE, 100000, NULL ); + else + poGeom = oChildren.clone(); + + if( poGeom != NULL ) + poFeature->SetGeometryDirectly( poGeom ); + + ConsiderBrush( psElement, szPen, poFeature ); + } + break; + + default: + break; + } + +/* -------------------------------------------------------------------- */ +/* Fixup geometry dimension. */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetGeometryRef() != NULL ) + poFeature->GetGeometryRef()->setCoordinateDimension( + DGNGetDimension( hDGN ) ); + + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRDGNLayer::GetNextFeature() + +{ + DGNElemCore *psElement; + + DGNGetElementIndex( hDGN, NULL ); + + while( (psElement = DGNReadElement( hDGN )) != NULL ) + { + OGRFeature *poFeature; + + if( psElement->deleted ) + { + DGNFreeElement( hDGN, psElement ); + continue; + } + + poFeature = ElementToFeature( psElement ); + DGNFreeElement( hDGN, psElement ); + + if( poFeature == NULL ) + continue; + + if( poFeature->GetGeometryRef() == NULL ) + { + delete poFeature; + continue; + } + + if( (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) + && FilterGeometry( poFeature->GetGeometryRef() ) ) + return poFeature; + + delete poFeature; + } + + return NULL; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDGNLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) ) + return bUpdate; + else if( EQUAL(pszCap,OLCRandomWrite) ) + return FALSE; /* maybe later? */ + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return m_poFilterGeom == NULL || m_poAttrQuery == NULL; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRDGNLayer::GetFeatureCount( int bForce ) + +{ +/* -------------------------------------------------------------------- */ +/* If any odd conditions are in effect collect the information */ +/* normally. */ +/* -------------------------------------------------------------------- */ + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL ) + return OGRLayer::GetFeatureCount( bForce ); + +/* -------------------------------------------------------------------- */ +/* Otherwise scan the index. */ +/* -------------------------------------------------------------------- */ + int nElementCount, i, nFeatureCount = 0; + int bInComplexShape = FALSE; + const DGNElementInfo *pasIndex = DGNGetElementIndex(hDGN,&nElementCount); + + for( i = 0; i < nElementCount; i++ ) + { + if( pasIndex[i].flags & DGNEIF_DELETED ) + continue; + + switch( pasIndex[i].stype ) + { + case DGNST_MULTIPOINT: + case DGNST_ARC: + case DGNST_TEXT: + if( !(pasIndex[i].flags & DGNEIF_COMPLEX) || !bInComplexShape ) + { + nFeatureCount++; + bInComplexShape = FALSE; + } + break; + + case DGNST_COMPLEX_HEADER: + nFeatureCount++; + bInComplexShape = TRUE; + break; + + default: + break; + } + } + + return nFeatureCount; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRDGNLayer::GetExtent( OGREnvelope *psExtent, CPL_UNUSED int bForce ) +{ + double adfExtents[6]; + + if( !DGNGetExtents( hDGN, adfExtents ) ) + return OGRERR_FAILURE; + + psExtent->MinX = adfExtents[0]; + psExtent->MinY = adfExtents[1]; + psExtent->MaxX = adfExtents[3]; + psExtent->MaxY = adfExtents[4]; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* LineStringToElementGroup() */ +/* */ +/* Convert an OGR line string to one or more DGN elements. If */ +/* the input is too long for a single element (more than 38 */ +/* points) we split it into multiple LINE_STRING elements, and */ +/* prefix with a complex group header element. */ +/* */ +/* This method can create handle creating shapes, or line */ +/* strings for the aggregate object, but the components of a */ +/* complex shape group are always line strings. */ +/************************************************************************/ + +#define MAX_ELEM_POINTS 38 + +DGNElemCore **OGRDGNLayer::LineStringToElementGroup( OGRLineString *poLS, + int nGroupType ) + +{ + int nTotalPoints = poLS->getNumPoints(); + int iNextPoint = 0, iGeom = 0; + DGNElemCore **papsGroup; + + papsGroup = (DGNElemCore **) + CPLCalloc( sizeof(void*), (nTotalPoints/(MAX_ELEM_POINTS-1))+3 ); + + for( iNextPoint = 0; iNextPoint < nTotalPoints; ) + { + DGNPoint asPoints[38]; + int nThisCount = 0; + + // we need to repeat end points of elements. + if( iNextPoint != 0 ) + iNextPoint--; + + for( ; iNextPoint < nTotalPoints && nThisCount < MAX_ELEM_POINTS; + iNextPoint++, nThisCount++ ) + { + asPoints[nThisCount].x = poLS->getX( iNextPoint ); + asPoints[nThisCount].y = poLS->getY( iNextPoint ); + asPoints[nThisCount].z = poLS->getZ( iNextPoint ); + } + + if( nTotalPoints <= MAX_ELEM_POINTS ) + papsGroup[0] = DGNCreateMultiPointElem( hDGN, nGroupType, + nThisCount, asPoints); + else + papsGroup[++iGeom] = + DGNCreateMultiPointElem( hDGN, DGNT_LINE_STRING, + nThisCount, asPoints); + } + +/* -------------------------------------------------------------------- */ +/* We needed to make into a group. Create the complex header */ +/* from the rest of the group. */ +/* -------------------------------------------------------------------- */ + if( papsGroup[0] == NULL ) + { + if( nGroupType == DGNT_SHAPE ) + nGroupType = DGNT_COMPLEX_SHAPE_HEADER; + else + nGroupType = DGNT_COMPLEX_CHAIN_HEADER; + + papsGroup[0] = + DGNCreateComplexHeaderFromGroup( hDGN, nGroupType, + iGeom, papsGroup + 1 ); + } + + return papsGroup; +} + +/************************************************************************/ +/* TranslateLabel() */ +/* */ +/* Translate LABEL feature. */ +/************************************************************************/ + +DGNElemCore **OGRDGNLayer::TranslateLabel( OGRFeature *poFeature ) + +{ + DGNElemCore **papsGroup; + OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef(); + OGRStyleMgr oMgr; + OGRStyleLabel *poLabel; + const char *pszText = poFeature->GetFieldAsString( "Text" ); + double dfRotation = 0.0; + double dfCharHeight = 100.0; + const char *pszFontName; + int nFontID = 1; // 1 is the default font for DGN. Not 0. + + oMgr.InitFromFeature( poFeature ); + poLabel = (OGRStyleLabel *) oMgr.GetPart( 0 ); + if( poLabel != NULL && poLabel->GetType() != OGRSTCLabel ) + { + delete poLabel; + poLabel = NULL; + } + + if( poLabel != NULL ) + { + GBool bDefault; + + if( poLabel->TextString(bDefault) != NULL && !bDefault ) + pszText = poLabel->TextString(bDefault); + dfRotation = poLabel->Angle(bDefault); + + poLabel->Size( bDefault ); + if( !bDefault && poLabel->GetUnit() == OGRSTUGround ) + dfCharHeight = poLabel->Size(bDefault); + // this part is really kind of bogus. + if( !bDefault && poLabel->GetUnit() == OGRSTUMM ) + dfCharHeight = poLabel->Size(bDefault)/1000.0; + + /* get font id */ + static const char *papszFontNumbers[] = + { "STANDARD=0", "WORKING=1", "FANCY=2", "ENGINEERING=3", "NEWZERO=4", + "STENCEL=5", "USTN_FANCY=7", "COMPRESSED=8", "STENCEQ=9", "hand=10", + "ARCH=11", "ARCHB=12", "IGES1001=15", "IGES1002=16", "IGES1003=17", + "CENTB=18", "MICROS=19", "ISOFRACTIONS=22", "ITALICS=23", + "ISO30=24", "GREEK=25", "ISOREC=26", "Isoeq=27", "ISO_FONTLEFT=30", + "ISO_FONTRIGHT=31", "INTL_ENGINEERING=32", "INTL_WORKING=33", + "ISOITEQ=34", "USTN FONT 26=36", "ARCHITECTURAL=41", + "BLOCK_OUTLINE=42", "LOW_RES_FILLED=43", "UPPERCASE50", + "FONT060=60", "din=61", "dinit=62", "helvl=63", "HELVLIT=64", + "helv=65", "HELVIT=66", "cent=67", "CENTIT=68", "SCRIPT=69", + "MICROQ=76", "dotfont=77", "DOTIT=78", "FONT092=92", "FONT094=94", + "ANSI_SYMBOLS=100", "FEATURE_CONTROL_SYSMBOLS=101", "SYMB_FAST=102", + "INTL_ISO=105", "INTL_ISO_EQUAL=106", "INTL_ISO_ITALIC=107", + "INTL_ISO_ITALIC_EQUAL=108", NULL }; + + pszFontName = poLabel->FontName( bDefault ); + if( !bDefault && pszFontName != NULL ) + { + const char *pszFontNumber = + CSLFetchNameValue((char**)papszFontNumbers, pszFontName); + + if( pszFontNumber != NULL ) + { + nFontID = atoi( pszFontNumber ); + } + } + } + + papsGroup = (DGNElemCore **) CPLCalloc(sizeof(void*),2); + papsGroup[0] = + DGNCreateTextElem( hDGN, pszText, nFontID, DGNJ_LEFT_BOTTOM, + dfCharHeight, dfCharHeight, dfRotation, NULL, + poPoint->getX(), + poPoint->getY(), + poPoint->getZ() ); + + if( poLabel ) + delete poLabel; + + return papsGroup; +} + +/************************************************************************/ +/* ICreateFeature() */ +/* */ +/* Create a new feature and write to file. */ +/************************************************************************/ + +OGRErr OGRDGNLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + if( !bUpdate ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to create feature on read-only DGN file." ); + return OGRERR_FAILURE; + } + + if( poFeature->GetGeometryRef() == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Features with empty, geometry collection geometries not\n" + "supported in DGN format." ); + return OGRERR_FAILURE; + } + + return CreateFeatureWithGeom( poFeature, poFeature->GetGeometryRef() ); +} + +/************************************************************************/ +/* CreateFeatureWithGeom() */ +/* */ +/* Create an element or element group from a given geometry and */ +/* the given feature. This method recurses to handle */ +/* collections as essentially independent features. */ +/************************************************************************/ + +OGRErr OGRDGNLayer::CreateFeatureWithGeom( OGRFeature *poFeature, + OGRGeometry *poGeom) + +{ +/* -------------------------------------------------------------------- */ +/* Translate the geometry. */ +/* -------------------------------------------------------------------- */ + DGNElemCore **papsGroup = NULL; + int i; + const char *pszStyle = poFeature->GetStyleString(); + + if( wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poGeom; + const char *pszText = poFeature->GetFieldAsString("Text"); + + if( (pszText == NULL || strlen(pszText) == 0) + && (pszStyle == NULL || strstr(pszStyle,"LABEL") == NULL) ) + { + DGNPoint asPoints[2]; + + papsGroup = (DGNElemCore **) CPLCalloc(sizeof(void*),2); + + // Treat a non text point as a degenerate line. + asPoints[0].x = poPoint->getX(); + asPoints[0].y = poPoint->getY(); + asPoints[0].z = poPoint->getZ(); + asPoints[1] = asPoints[0]; + + papsGroup[0] = DGNCreateMultiPointElem( hDGN, DGNT_LINE, + 2, asPoints ); + } + else + { + papsGroup = TranslateLabel( poFeature ); + } + } + else if( wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) + { + papsGroup = LineStringToElementGroup( (OGRLineString *) poGeom, + DGNT_LINE_STRING ); + } + else if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) + { + OGRPolygon *poPoly = ((OGRPolygon *) poGeom); + + DGNElemCore **papsGroupExt = LineStringToElementGroup( + poPoly->getExteriorRing(), DGNT_SHAPE); + + int innerRingsCnt = poPoly->getNumInteriorRings(); + + if (innerRingsCnt > 0) { + CPLDebug("InnerRings", "there are %d inner rings", innerRingsCnt); + std::list dgnElements; + + for (i = 0; papsGroupExt[i] != NULL; i++) { + dgnElements.push_back(papsGroupExt[i]); + } + CPLFree(papsGroupExt); + + // get all interior rings and create complex group shape + for (int iRing = 0; iRing < innerRingsCnt; iRing++) { + DGNElemCore **papsGroupInner = LineStringToElementGroup( + poPoly->getInteriorRing(iRing), DGNT_SHAPE); + papsGroupInner[0]->properties |= DGNPF_HOLE; + DGNUpdateElemCoreExtended(hDGN, papsGroupInner[0]); + for (i = 0; papsGroupInner[i] != NULL; i++) { + dgnElements.push_back(papsGroupInner[i]); + } + CPLFree(papsGroupInner); + } + int index = 1; + papsGroup = (DGNElemCore **) CPLCalloc(sizeof(void*), + dgnElements.size() + 2); + for (std::list::iterator list_iter = + dgnElements.begin(); list_iter != dgnElements.end(); + list_iter++) { + papsGroup[index++] = *list_iter; + } + + //papsGroup[0] = DGNCreateComplexHeaderFromGroup( hDGN, DGNT_COMPLEX_SHAPE_HEADER, dgnElements.size(), papsGroup+1); + DGNPoint asPoints[1]; + asPoints[0].x = 0; + asPoints[0].y = 0; + asPoints[0].z = 0; + papsGroup[0] = DGNCreateCellHeaderFromGroup(hDGN, "", 1, NULL, + dgnElements.size(), papsGroup + 1, asPoints + 0, 1.0, 1.0, + 0.0); + DGNAddShapeFillInfo(hDGN, papsGroup[0], 6); + } else { + papsGroup = papsGroupExt; + } + } + else if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon + || wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint + || wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString + || wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection) + { + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom; + int iGeom; + + for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ ) + { + OGRErr eErr = CreateFeatureWithGeom( poFeature, + poGC->getGeometryRef(iGeom) ); + if( eErr != OGRERR_NONE ) + return eErr; + } + + return OGRERR_NONE; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported geometry type (%s) for DGN.", + OGRGeometryTypeToName( poGeom->getGeometryType() ) ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Add other attributes. */ +/* -------------------------------------------------------------------- */ + int nLevel = poFeature->GetFieldAsInteger( "Level" ); + int nGraphicGroup = poFeature->GetFieldAsInteger( "GraphicGroup" ); + int nColor = poFeature->GetFieldAsInteger( "ColorIndex" ); + int nWeight = poFeature->GetFieldAsInteger( "Weight" ); + int nStyle = poFeature->GetFieldAsInteger( "Style" ); + int nMSLink = poFeature->GetFieldAsInteger( "MSLink" ); + + nLevel = MAX(0,MIN(63,nLevel)); + nColor = MAX(0,MIN(255,nColor)); + nWeight = MAX(0,MIN(31,nWeight)); + nStyle = MAX(0,MIN(7,nStyle)); + nMSLink = MAX(0,nMSLink); + + DGNUpdateElemCore( hDGN, papsGroup[0], nLevel, nGraphicGroup, nColor, + nWeight, nStyle ); + DGNAddMSLink( hDGN, papsGroup[0], DGNLT_ODBC, 0, nMSLink ); +/* -------------------------------------------------------------------- */ +/* Write to file. */ +/* -------------------------------------------------------------------- */ + for( i = 0; papsGroup[i] != NULL; i++ ) + { + DGNWriteElement( hDGN, papsGroup[i] ); + + if( i == 0 ) + poFeature->SetFID( papsGroup[i]->element_id ); + + DGNFreeElement( hDGN, papsGroup[i] ); + } + + CPLFree( papsGroup ); + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/web/index.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/web/index.html new file mode 100644 index 000000000..33423ddd9 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/web/index.html @@ -0,0 +1,229 @@ + + +DGNLib: Microstation DGN (ISFF) Reader + + + + +

    DGNLib: a Microstation DGN (ISFF) Reader

    + +I (Frank Warmerdam) +have implemented a Microstation DGN reading library on contract to +Avenza Systems Inc., makers of +MAPublisher. The library will be OpenSource (under my usual X/MIT license), +portable and callable from C/C++. While it has been incorporated into my +OGR Simple Features library, it is +also be usable standalone with minimal overhead. The dgnlib library, and this +page of DGN related information are primarily hosted at +http://dgnlib.maptools.org.

    + +

    DGNLib

    + +DGNLib is a small C/C++ library for reading and writing DGN files. + +
      + +
    • Where can I get the source code?

      + +dgnlib-1.11.zip: Current standalone source +with dgndump example mainline.

      + +

    • Does DGNLib support all DGN elements?

      + +No, but it does support most 2D and 3D elements. Some of the more esoteric +elements are read in "raw data mode", but not interpreted. It does support +lines, line strings, curves, bsplines, ellipses, +arcs, and text elements, as well as extracting color tables, and master +coordinate information.

      + +

    • Does DGNLib support Microstation V8 DGN Files?

      + +No, they are a substantially different format, and are not recognised at +all. The DGNdirect library from the OpenDWG +Alliance does support DGN v8.

      + +

    • Is it portable?

      + +Yes, fairly, though as distributed it may be necessary to tweak the +cpl_port.h and cpl_config.h files for different platforms. It was developed +on Linux for a client deploying on MacOS, and Windows.

      + +

    • Does it require alot of memory?

      + +Generally speaking no. Only one element at a time is kept in memory. If +the file is "indexed" an additional 12 bytes per element is kept in memory +after a pre-scan, but this isn't required.

      + +

    • Does DGNLib include code to draw DGN elements?

      + +No, but there is a document that tries to +indicate how to properly draw DGN elements given the structures returned by +DGNLib. The OGR layer re-interprets the drawing information into +OGR Feature Style +format which is understood by some applications such as +UMN MapServer. +

      + +

    • What is the license?

      + +DGNLib is under my usual MIT/X style open source license. Thus is can be +easily used in commercial, and free products with no concern about licensing +issues. See the header of any source file for the full license text.

      + +

    • Is there support for writing DGN files?

      + +Yes, there is preliminary support for writing 2D and 3D DGN files in +recent releases.

      + +

    • Is there documentation?

      + +There is a detailed DGN Library API Reference + available. There is currently no tutorial style information on how to +use the library, but the dgndump.c utility can serve as a limited example.

      + +

    • Is further work going on to improve the library?

      + +The initial project is complete, but I am interested in fixing +bugs and adding minor features. Improvements are welcome.

      + +

    + +

    DGN Related Information

    + + + +

    Other DGN Viewers/Libraries

    + +
      + +
    • DGNdirect is a library from the +Open Design Alliance that supports reading (and to some extent) writing DGN +v8 files. It also supports reading DGN v7 files. The Open Design Alliance +offers it under quite reasonable (but not Open Source) licensing terms.

      + +

    • Bentley itself offers DGN libraries, and tools as part of their +OpenDGN program.

      + +

    • GraphStore has a product called +eCADLite that can be used in demo mode as a DGN viewer.

      + +

    • Columbus is a free +document viewer which includes DGN support.

      + +

    • AutoVue from Cimmetry.

      + +

    • Safe Software's FME product has +excellent DGN read and write support, and can be used as a standalone product, +or FMEObjects acts as a library.

      + +

    • My own OpenEV uses dgnlib for DGN +support.

      + +

    + +

    Sample Datasets

    + + + +

    Credits

    + +I would like to thank:

    + +

      +
    • Dennis Christopher (and Avenza Systems +Inc) for funding this development, and allowing me to release it as +open source.

      + +

    • Rodney Jenson who gave me some hints, and moral support.

      + +

    • Patrick van Dijk, for information and code related to Quaternion +handling.

      + +

    • Cybertal Components, whose's OpenDGN based dumping program was helpful to +me in testing. Unfortunately, Cybertal does not appear to still be in +business.

      + +

    • Ason Kang who provided a sample multi-byte text file and code to +handle it properly.

      + +

    • The various US DOTs and other government organizations who are progressive +enough to provide mapping information directly in DGN format on the web. It's +a great store of sample data.

      + +

    • Mike Cane for held with Radix50 names, and support for cell headers.

      + +

    • Jeff Smith (Geographic Software Specialists) who funded DGN write +support.

      + +

    • Jamie Cecchetto (Century +Systems) for funding 3D write support.

      + +

    • DM Solutions Group for +providing the maptools.org site to +host this on, and for funding (also thanks to Tyler Mitchell of +Lignum) integration into +OGR.

      + +

    • Marius Kintel (Systems In Motion) for +adding support for reading 3D cone (23), 3D surface (18) and 3D solid (19) +elements.

      +

    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/web/representation.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/web/representation.html new file mode 100644 index 000000000..ea59054c8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dgn/web/representation.html @@ -0,0 +1,234 @@ + + +Microstation DGN Feature Representation + + + +

    Microstation DGN Feature Representation

    + +This document addresses how DGN features should be drawn to match the +behaviour of Microstation as closely as possible. It is written for +users of the dgnlib reader code, but the information +should be applicable to anyone working with DGN.

    + +The information is based on the ISFF.TXT document, supposition, and +review of display effects in various viewers. It is incomplete, and at times +likely incorrect. I would appreciate feedback at +warmerdam@pobox.com.

    + +

    General Symbology Information

    + +All graphical elements contain a set of common information, represented by +the following fields in DGNElemCore.

    + +

      +
    1. int color +
    2. int weight +
    3. int style +
    4. int properties +
    + +

    Color

    + +The color field of the DGNElemCore is a color index number (0-255), +referencing a color from the appropriate color table. This is normally +translated into an +RGB color for use using DGNLookupColor(). The returned color should be +considered the foreground color for drawing the element.

    + +NOTE: There can be more than one color table in a DGN file, and +currently on the first is utilized. In theory the screen or view value from +the colormap should be used to establish which is to be used but this doesn't +seem to be an issue in any sample data I have encountered.

    + +

    Weight

    + +This is a value between 0 and 31 which is supposed to establish the weight, or +thickness of lines for this element. It is unclear how this should be +utilized. Geographic Explorer treats it as a line width in pixels.

    + +

    Style

    + +The style field has one of the following values, with 0 (Solid) being +the default. Presumably this only affects drawing of line segments. There +is no information on appropriate sizing information for dashes or dots, or +relative guidelines implicit in the names.

    + +

      +
    • DGNS_SOLID (0) +
    • DGNS_DOTTED (1) +
    • DGNS_MEDIUM_DASH (2) +
    • DGNS_LONG_DASH (3) +
    • DGNS_DOT_DASH (4) +
    • DGNS_SHORT_DASH (5) +
    • DGNS_DASH_DOUBLE_DOT (6) +
    • DGNS_LONG_DASH_SHORT_DASH (7) +
    + +

    Properties

    + +The properties bitfield in DGNElemCore contains various possible values, but +only a few affect drawing.

    + +The DGNPF_ORIENTATION flag is set an element is to be oriented relative +to the screen, instead of relative to the design plane. It isn't clear what +all properties this affects, perhaps the orientation of text in a viewer +that can rotate the view of the file as a whole. No examples of this being +used have been found.

    + +If used with a line element, the DBNPF_HOLE flag indicates that +the line should be treated as infinite, while it is otherwise just a segment +between the two provided points. If used with a closed element (shape, +complex shape, ellipse, ...) it indicates that shape is a hole (unfilled) while +by default such elements are filled.

    + +

    Element Types

    + +

    DGNT_LINE

    + +Single line segment. Use color, weight and line +style.

    + +

    DGNT_LINESTRING

    + +Polyline. Use color, weight, and line style.

    + +

    DGNT_SHAPE

    + +Polygon. Use color for edge color. If the polygon is supposed to be filled +the 0x0041 fill information attribute +linkage will also be included. The fill color may differ from the element +color in which case the element should be filled with the fill color, and +then the boundary drawn in the element (outline) color.

    + +If the DGNPF_HOLE flag is set the shape is really a hole within another +element and should be "cleared". The exact semantics are unclear. The +first and last vertices of a shape are always the same.

    + +

    DGNT_CURVE

    + +The curve (type 11) element is a 2D or 3D parametric spline curve completely defined by a set of n points. The first two and last two points define +endpoint derivatives and do not display. The interpolated curve passes through all other points.

    + +A curve with n points defines n-1 line segments; interpolation occurs over the middle n-5 segments. Each segment has its own parametric cubic +interpolation polynomial for the x and y (and z in 3D) dimensions. The parameter for each of these polynomials is the length along the line segment. +Thus, for a segment k, the interpolated points P are expressed as a function of the distance d along the segment as follows:

    +

    +       Pk(d) = {Fk,x(d), Fk,y(d), Fk,z(d)} with 0 <= d <= Dk
    +
    + +Fk,x, Fk,y, and Fk,z are cubic polynomials and Dk is the length of segment k. +In addition, the polynomial coefficients are functions of the segment +length and the endpoint derivatives of Fk,x, Fk,y, and Fk,z. The subscript k +is merely a reminder that these functions depend on the segment.

    + +The cubic polynomials are defined as follows:

    +

    +    Fk,x = axd3 + bxd2 + cxd + Xk
    +    cx = tk
    +    bx = [3(Xk+1-Xk)/Dk - 2tk,x - tk+1,x] / Dk
    +    ax = [tk,x + tk+1,x - 2(xk+1-xk)/Dk] / Dk2
    +
    +The m variable is analogous to the slope of the segment.

    + +

    + If (|mk+1,x-mk,x| + |mk-1,x-mk-2,x|) <> 0, then:
    +    tk,x = (mk-1,x|mk+1,x-mk,x| + mk,x|mk-1,x-mk-2,x|)/(|mk+1,x-mk,x| + 
    +    |mk-1,x-mk-2,x|)
    + else:
    +    tk,x = (mk+1,x+mk,x) / 2
    +    mk,x = (Xk+1 - Xk) / Dk
    +
    +Fk,y(d) and Fk,z(d) are defined analogously.

    + +

    DGNT_BSPLINE

    + +Fill in later. + +

    DGNT_ELLIPSE

    + +Ellipse. See DGNT_ARC for details of geometry. The DGNPF_HOLE and +fill color information are applied to ellipses in the same manner as +they are applied to the Shape.

    + +

    DGNT_ARC

    + +Arc (portion of ellipse). Draw line with color, line style and weight.

    + +The DGNElemArc (also used for ellipses) looks like this: +

    +typedef struct {
    +  DGNElemCore 	core;
    +  DGNPoint	origin;
    +  double	primary_axis;
    +  double        secondary_axis;
    +  double	rotation;
    +  long          quat[4];
    +  double	startang;
    +  double	sweepang;
    +} DGNElemArc;
    +
    + +The origin is the center of rotation for the ellipse or arc.

    + +The primary_axis and secondary_axis define the distance from the origin to +the ellipse edge on the primary (horizontal) and secondary (vertical) axis.

    + +The rotation indicates the counterclockwise (or clockwise if negative) +rotation in degrees of the defined ellipse around the origin.

    + +The startang indicates the start angle (degrees, counterclockwise) of the +arc. For ellipses this is always 0.

    + +The sweepang indicates the angle through which the arc sweeps (counterclockwise +in degrees, clockwise if negative). For ellipses this is always 360.

    + +The DGNStrokeArc() function can be used to approximate arcs and ellipses as +a polyline.

    + +

    DGNT_TEXT

    + +Text. Should be drawn using color. The text structure looks like:

    + +

    +typedef struct {
    +    DGNElemCore core;
    +    
    +    int		font_id;
    +    int		justification;
    +    long        length_mult;
    +    long        height_mult;
    +    double	rotation;
    +    DGNPoint	origin;
    +    char	string[1];
    +} DGNElemText;
    +
    + +The font_id is a value from 0-255 for the to use. It is unclear to me at +this time how this can be related to a non-microstation specific font.

    + +The justification is generally ignored, since the provided origin is always +at the bottom left of the text to be placed. The justification relates to +how the user originally placed the text.

    + +The rotation indicates the rotation in degrees counterclockwise of the +text (zero is horizontal, left to right).

    + +The origin is position where the bottom left corner of the text is placed.

    + +The height_mult field gives the height of the text in master coordinates. +This appears to be the height of a line, measuring distance from the baseline +of one line to the next, and is usually a bit more than the height of the text +itself.

    + +If length_mult is different than height_mult, it means the aspect ratio of +the text has been adjusted from the default.

    + +

    Level Symbology

    + +Fill in later.

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/C0101.met.raw.nc.das b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/C0101.met.raw.nc.das new file mode 100644 index 000000000..16b5c57bc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/C0101.met.raw.nc.das @@ -0,0 +1,141 @@ +Attributes { + ogr_layer_info_1 { + string layer_name air; + string spatial_ref WGS84; + string target_container air_temperature; + layer_extents { + Float64 x_min -180; + Float64 y_min -90; + Float64 x_max 180; + Float64 y_max 90; + } + extra_containers { + string c0 air_temperature_qc; + } + x_field { + string name lon; + string scope dds; + } + y_field { + string name lat; + string scope dds; + } + } + ogr_layer_info_2 { + string layer_name wind; + string spatial_ref WGS84; + string target_container wind_min; + layer_extents { + Float64 x_min -180; + Float64 y_min -90; + Float64 x_max 180; + Float64 y_max 90; + } + extra_containers { + string c0 win_min_qc; + string c1 wind_gust; + string c2 wind_gust_qc; + string c3 wind_speed; + string c4 wind_speed_qc; + string c5 wind_speed_sc; + string c6 wind_speed_sc_qc; + string c7 wind_speed_ve; + string c8 wind_speed_ve_qc; + } + x_field { + string name lon; + string scope dds; + } + y_field { + string name lat; + string scope dds; + } + } + ogr_layer_info_3 { + string layer_name wind_direction; + string spatial_ref WGS84; + string target_container wind_direction; + layer_extents { + Float64 x_min -180; + Float64 y_min -90; + Float64 x_max 180; + Float64 y_max 90; + } + extra_containers { + string c0 wind_direction_qc; + string c1 wind_direction_ve; + string c2 wind_direction_ve_qc; + string c3 wind_direction_stddev; + string c4 wind_direction_stddev_qc; + string c5 wind_direction_ve_stddev; + string c6 wind_direction_ve_stddev_qc; + string c7 wind_direction_uv_stddev; + string c8 wind_direction_uv_stddev_qc; + } + x_field { + string name lon; + string scope dds; + } + y_field { + string name lat; + string scope dds; + } + } + ogr_layer_info_4 { + string layer_name compass; + string spatial_ref WGS84; + string target_container compass; + layer_extents { + Float64 x_min -180; + Float64 y_min -90; + Float64 x_max 180; + Float64 y_max 90; + } + extra_containers { + string c0 compass_qc; + string c1 compass_stddev; + string c2 compass_stddev_qc; + } + x_field { + string name lon; + string scope dds; + } + y_field { + string name lat; + string scope dds; + } + } + ogr_layer_info_5 { + string layer_name visibility; + string spatial_ref WGS84; + string target_container visibility_counts; + layer_extents { + Float64 x_min -180; + Float64 y_min -90; + Float64 x_max 180; + Float64 y_max 90; + } + extra_containers { + string c0 visibility_counts_qc; + string c1 visibility_counts_qc; + string c2 visibility; + string c3 visibility_qc; + string c4 min_visibility_counts; + string c5 min_visibility_counts_qc; + string c6 min_visibility; + string c7 min_visibility_qc; + string c8 max_visibility_counts; + string c9 max_visibility_counts_qc; + string c10 max_visibility; + string c11 max_visibility_qc; + } + x_field { + string name lon; + string scope dds; + } + y_field { + string name lat; + string scope dds; + } + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/GNUmakefile new file mode 100644 index 000000000..4aedbcafd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/GNUmakefile @@ -0,0 +1,18 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrdodsdriver.o ogrdodsdatasource.o ogrdodslayer.o \ + ogrdodssequencelayer.o ogrdodsfielddefn.o ogrdodsgrid.o + +CPPFLAGS := -I.. $(CPPFLAGS) $(DODS_INC) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + + +dodstest: dodstest.cpp + g++ -g -I$(DODS_INC) dodstest.cpp -o dodstest -L/u/pkg/DODS/lib -ldap++ -lpthread -lrx -L/u/pkg/dods/DODS/lib -lcurl -lssl -lcrypto -L/u/pkg/dods/DODS/lib -lxml2 -L/u/pkg/dods/DODS/lib -ldl -lz + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/bbhenv.dat.das b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/bbhenv.dat.das new file mode 100644 index 000000000..cad978848 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/bbhenv.dat.das @@ -0,0 +1,21 @@ +Attributes { + ogr_layer_info { + string layer_name WaterQuality; + string spatial_ref WGS84; + string target_container Water_Quality; + layer_extents { + Float64 x_min -180; + Float64 y_min -90; + Float64 x_max 180; + Float64 y_max 90; + } + x_field { + string name YR; + string scope dds; + } + y_field { + string name JD; + string scope dds; + } + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/drv_dods.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/drv_dods.html new file mode 100644 index 000000000..db9cc847a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/drv_dods.html @@ -0,0 +1,77 @@ + + +DODS/OPeNDAP + + + + +

    DODS/OPeNDAP

    + +This driver implements read-only support for reading feature data from +OPeNDAP (DODS) servers. It is optionally included in OGR if built +with OPeNDAP support libraries.

    + + +When opening a database, it's name should be specified in the form +"DODS:url". The URL may include a constraint expression a shown here. +Note that it may be necessary to quote or otherwise protect DODS URLs on +the commandline if they include question mark or ampersand characters +as these often have special meaning to command shells.

    + +

    +DODS:http://dods.gso.uri.edu/dods-3.4/nph-dods/broad1999?&press=148
    +
    + +By default top level Sequence, Grid and Array objects will be translated +into corresponding layers. Sequences are (by default) treated as point +layers with the point geometries picked up from lat and lon variables if +available. To provide more sophisticated translation of sequence, grid or +array items into features it is necessary to provide additional information +to OGR as DAS (dataset auxiliary informaton) either from the remote server, +or locally via the AIS mechanism.

    + +A DAS definition for an OGR layer might look something like: + +

    +Attributes {
    +    ogr_layer_info {
    +	string layer_name WaterQuality;
    +	string spatial_ref WGS84;
    +	string target_container Water_Quality;
    +       	layer_extents {
    +	    Float64 x_min -180;
    +	    Float64 y_min -90;
    +	    Float64 x_max 180;
    +	    Float64 y_max 90;
    +        }
    +        x_field {
    +            string name YR;
    +	    string scope dds;
    +        }
    +        y_field {
    +            string name JD;
    +	    string scope dds;
    +        }
    +    }
    +}
    +
    + +

    Caveats

    + +
      + +
    • No field widths are captured for attribute fields from DODS.

      + +

    • Performance for repeated requests is dramatically improved by enabling +DODS caching. Try setting USE_CACHE=1 in your ~/.dodsrc. + +
    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/makefile.vc new file mode 100644 index 000000000..258f57d06 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/makefile.vc @@ -0,0 +1,14 @@ + +OBJ = ogrdodsdriver.obj ogrdodsdatasource.obj ogrdodslayer.obj ogrdodssequencelayer.obj ogrdodsfielddefn.obj ogrdodsgrid.obj + +GDAL_ROOT = ..\..\.. + +EXTRAFLAGS = -I.. -I$(DODS_DIR) -I$(DODS_DIR)\include -I$(DODS_DIR)\include\gnu -I$(DODS_DIR)\include\pthreads -I$(DODS_DIR)\include\xdr /DWIN32 /DWIN32_LEAN_AND_MEAN -DFRMT_dods $(DODS_FLAGS) /GR + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/natl_prof_bot.cdp.das b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/natl_prof_bot.cdp.das new file mode 100644 index 000000000..c22f28ca9 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/natl_prof_bot.cdp.das @@ -0,0 +1,41 @@ +Attributes { + ogr_layer_info_1 { + string layer_name normalized; + string spatial_ref WGS84; + string target_container location.profile; + x_field { + string name location.lon; + string scope dds; + } + y_field { + string name location.lat; + string scope dds; + } + } + ogr_layer_info_2 { + string layer_name profiles; + string spatial_ref WGS84; + string target_container location; + x_field { + string name location.lon; + string scope dds; + } + y_field { + string name location.lat; + string scope dds; + } + } + ogr_layer_info_3 { + string layer_name lines; + string spatial_ref WGS84; + string target_container location; + x_field { + string name location.profile.depth; + string scope dds; + } + y_field { + string name location.profile.T_20; + string scope dds; + } + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogr_ais_eg.xml b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogr_ais_eg.xml new file mode 100644 index 000000000..ed0ccb365 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogr_ais_eg.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogr_dods.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogr_dods.h new file mode 100644 index 000000000..1a1070778 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogr_dods.h @@ -0,0 +1,355 @@ +/****************************************************************************** + * $Id: ogr_dods.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for OGR/DODS driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_DODS_H_INCLUDED +#define _OGR_DODS_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_error.h" +#include "cpl_conv.h" + +// Lots of DODS related definitions + +#include +#include +#include +#include + +#define DEFAULT_BASETYPE_FACTORY + +// #define DODS_DEBUG 1 +#include + +#include // DODS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef LIBDAP_310 +/* AISConnect.h/AISConnect class was renamed to Connect.h/Connect in libdap 3.10 */ +#include +#define AISConnect Connect +#else +#include +#endif + +#include +#include +#include +#include +#include + +using namespace libdap; + +/************************************************************************/ +/* OGRDODSFieldDefn */ +/************************************************************************/ +class OGRDODSFieldDefn { +public: + OGRDODSFieldDefn(); + ~OGRDODSFieldDefn(); + + int Initialize( AttrTable *, + BaseType *poTarget = NULL, BaseType *poSuperSeq = NULL ); + int Initialize( const char *, const char * = "das", + BaseType *poTarget = NULL, BaseType *poSuperSeq = NULL ); + + int bValid; + char *pszFieldName; + char *pszFieldScope; + int iFieldIndex; + char *pszFieldValue; + char *pszPathToSequence; + + int bRelativeToSuperSequence; + int bRelativeToSequence; +}; + +/************************************************************************/ +/* OGRDODSLayer */ +/************************************************************************/ + +class OGRDODSDataSource; + +class OGRDODSLayer : public OGRLayer +{ + protected: + OGRFeatureDefn *poFeatureDefn; + + OGRSpatialReference *poSRS; + + int iNextShapeId; + + OGRDODSDataSource *poDS; + + char *pszQuery; + + char *pszFIDColumn; + + char *pszTarget; + + OGRDODSFieldDefn **papoFields; + + virtual int ProvideDataDDS(); + int bDataLoaded; + + AISConnect *poConnection; + DataDDS *poDataDDS; + + BaseType *poTargetVar; + + AttrTable *poOGRLayerInfo; + + int bKnowExtent; + OGREnvelope sExtent; + + public: + OGRDODSLayer( OGRDODSDataSource *poDS, + const char *pszTarget, + AttrTable *poAttrInfo ); + virtual ~OGRDODSLayer(); + + virtual void ResetReading(); + + virtual OGRFeature *GetNextFeature(); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int TestCapability( const char * ); + + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); +}; + +/************************************************************************/ +/* OGRDODSSequenceLayer */ +/************************************************************************/ + +class OGRDODSSequenceLayer : public OGRDODSLayer +{ +private: + OGRDODSFieldDefn oXField; + OGRDODSFieldDefn oYField; + OGRDODSFieldDefn oZField; + + const char *pszSubSeqPath; + + Sequence *poSuperSeq; + + int iLastSuperSeq; + + int nRecordCount; /* -1 if not yet known */ + int nSuperSeqCount; + int *panSubSeqSize; + + double GetFieldValueAsDouble( OGRDODSFieldDefn *, int ); + BaseType *GetFieldValue( OGRDODSFieldDefn *, int, + Sequence * ); + + double BaseTypeToDouble( BaseType * ); + + int BuildFields( BaseType *, const char *, + const char * ); + + Sequence *FindSuperSequence( BaseType * ); + +protected: + virtual int ProvideDataDDS(); + +public: + OGRDODSSequenceLayer( OGRDODSDataSource *poDS, + const char *pszTarget, + AttrTable *poAttrInfo ); + virtual ~OGRDODSSequenceLayer(); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual GIntBig GetFeatureCount( int ); +}; + +/************************************************************************/ +/* OGRDODSGridLayer */ +/************************************************************************/ + +class OGRDODSDim +{ +public: + OGRDODSDim() { + pszDimName = NULL; + nDimStart = 0; + nDimEnd = 0; + nDimStride = 0; + nDimEntries = 0; + poMap = NULL; + pRawData = NULL; + iLastValue = 0; + } + ~OGRDODSDim() { + CPLFree( pszDimName ); + CPLFree( pRawData ); + } + + char *pszDimName; + int nDimStart; + int nDimEnd; + int nDimStride; + int nDimEntries; + Array *poMap; + void *pRawData; + int iLastValue; +}; + +class OGRDODSArrayRef +{ +public: + OGRDODSArrayRef() { + pszName = NULL; + iFieldIndex = -1; + poArray = NULL; + pRawData = NULL; + } + ~OGRDODSArrayRef() { + CPLFree( pszName ); + CPLFree( pRawData ); + } + + char *pszName; + int iFieldIndex; + Array *poArray; + void *pRawData; +}; + +class OGRDODSGridLayer : public OGRDODSLayer +{ + Grid *poTargetGrid; // NULL if simple array used. + Array *poTargetArray; + + int nArrayRefCount; + OGRDODSArrayRef *paoArrayRefs; // includes poTargetArray. + + OGRDODSFieldDefn oXField; + OGRDODSFieldDefn oYField; + OGRDODSFieldDefn oZField; + + int nDimCount; + OGRDODSDim *paoDimensions; + int nMaxRawIndex; + + void *pRawData; + + int ArrayEntryToField( Array *poArray, void *pRawData, + int iArrayIndex, + OGRFeature *poFeature, int iField); + +protected: + virtual int ProvideDataDDS(); + +public: + OGRDODSGridLayer( OGRDODSDataSource *poDS, + const char *pszTarget, + AttrTable *poAttrInfo ); + virtual ~OGRDODSGridLayer(); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual GIntBig GetFeatureCount( int ); + +}; + +/************************************************************************/ +/* OGRDODSDataSource */ +/************************************************************************/ + +class OGRDODSDataSource : public OGRDataSource +{ + OGRDODSLayer **papoLayers; + int nLayers; + + char *pszName; + + void AddLayer( OGRDODSLayer * ); + + public: // Just intended for read access by layer classes. + AISConnect *poConnection; + + DAS oDAS; + DDS *poDDS; + BaseTypeFactory *poBTF; + + string oBaseURL; + string oProjection; + string oConstraints; + + public: + + OGRDODSDataSource(); + ~OGRDODSDataSource(); + + int Open( const char * ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRDODSDriver */ +/************************************************************************/ + +class OGRDODSDriver : public OGRSFDriver +{ + public: + ~OGRDODSDriver(); + const char *GetName(); + OGRDataSource *Open( const char *, int ); + int TestCapability( const char * ); +}; + +string OGRDODSGetVarPath( BaseType * ); +int OGRDODSGetVarIndex( Sequence *poParent, string oVarName ); + +int OGRDODSIsFloatInvalid( const float * ); +int OGRDODSIsDoubleInvalid( const double * ); + +#endif /* ndef _OGR_DODS_H_INCLUDED */ + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodsdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodsdatasource.cpp new file mode 100644 index 000000000..1ab4590cb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodsdatasource.cpp @@ -0,0 +1,299 @@ +/****************************************************************************** + * $Id: ogrdodsdatasource.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OGR/DODS Interface + * Purpose: Implements OGRDODSDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dods.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrdodsdatasource.cpp 27044 2014-03-16 23:41:27Z rouault $"); +/************************************************************************/ +/* OGRDODSDataSource() */ +/************************************************************************/ + +OGRDODSDataSource::OGRDODSDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + poConnection = NULL; + + poBTF = new BaseTypeFactory(); + poDDS = new DDS( poBTF ); +} + +/************************************************************************/ +/* ~OGRDODSDataSource() */ +/************************************************************************/ + +OGRDODSDataSource::~OGRDODSDataSource() + +{ + int i; + + CPLFree( pszName ); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + if( poConnection != NULL ) + delete poConnection; + + delete poDDS; + delete poBTF; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRDODSDataSource::Open( const char * pszNewName ) + +{ + CPLAssert( nLayers == 0 ); + + pszName = CPLStrdup( pszNewName ); + +/* -------------------------------------------------------------------- */ +/* Parse the URL into a base url, projection and constraint */ +/* expression. */ +/* -------------------------------------------------------------------- */ + char *pszWrkURL = CPLStrdup( pszNewName + 5 ); + char *pszFound; + + pszFound = strstr(pszWrkURL,"&"); + if( pszFound ) + { + oConstraints = pszFound; + *pszFound = '\0'; + } + + pszFound = strstr(pszWrkURL,"?"); + if( pszFound ) + { + oProjection = pszFound+1; + *pszFound = '\0'; + } + + // Trim common requests. + int nLen = strlen(pszWrkURL); + if( strcmp(pszWrkURL+nLen-4,".das") == 0 ) + pszWrkURL[nLen-4] = '\0'; + else if( strcmp(pszWrkURL+nLen-4,".dds") == 0 ) + pszWrkURL[nLen-4] = '\0'; + else if( strcmp(pszWrkURL+nLen-4,".asc") == 0 ) + pszWrkURL[nLen-4] = '\0'; + else if( strcmp(pszWrkURL+nLen-5,".dods") == 0 ) + pszWrkURL[nLen-5] = '\0'; + else if( strcmp(pszWrkURL+nLen-5,".html") == 0 ) + pszWrkURL[nLen-5] = '\0'; + + oBaseURL = pszWrkURL; + CPLFree( pszWrkURL ); + +/* -------------------------------------------------------------------- */ +/* Do we want to override the .dodsrc file setting? Only do */ +/* the putenv() if there isn't already a DODS_CONF in the */ +/* environment. */ +/* -------------------------------------------------------------------- */ + if( CPLGetConfigOption( "DODS_CONF", NULL ) != NULL + && getenv("DODS_CONF") == NULL ) + { + static char szDODS_CONF[1000]; + + sprintf( szDODS_CONF, "DODS_CONF=%.980s", + CPLGetConfigOption( "DODS_CONF", "" ) ); + putenv( szDODS_CONF ); + } + +/* -------------------------------------------------------------------- */ +/* If we have a overridding AIS file location, apply it now. */ +/* -------------------------------------------------------------------- */ + if( CPLGetConfigOption( "DODS_AIS_FILE", NULL ) != NULL ) + { + string oAISFile = CPLGetConfigOption( "DODS_AIS_FILE", "" ); + RCReader::instance()->set_ais_database( oAISFile ); + } + +/* -------------------------------------------------------------------- */ +/* Connect to the server. */ +/* -------------------------------------------------------------------- */ + string version; + + try + { + poConnection = new AISConnect( oBaseURL ); + version = poConnection->request_version(); + } + catch (Error &e) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "%s", e.get_error_message().c_str() ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* We presume we only work with version 3 servers. */ +/* -------------------------------------------------------------------- */ + + if (version.empty() || version.find("/3.") == string::npos) + { + CPLError( CE_Warning, CPLE_AppDefined, + "I connected to the URL but could not get a DAP 3.x version string\n" + "from the server. I will continue to connect but access may fail."); + } + +/* -------------------------------------------------------------------- */ +/* Fetch the DAS and DDS info about the server. */ +/* -------------------------------------------------------------------- */ + try + { + poConnection->request_das( oDAS ); + poConnection->request_dds( *poDDS, oProjection + oConstraints ); + } + catch (Error &e) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error fetching DAS or DDS:\n%s", + e.get_error_message().c_str() ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Do we have any ogr_layer_info attributes in the DAS? If so, */ +/* use them to define the layers. */ +/* -------------------------------------------------------------------- */ + AttrTable::Attr_iter dv_i; + +#ifdef LIBDAP_39 + AttrTable* poTable = oDAS.container(); + if (poTable == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot get container"); + return FALSE; + } +#else + AttrTable* poTable = &oDAS; +#endif + + for( dv_i = poTable->attr_begin(); dv_i != poTable->attr_end(); dv_i++ ) + { + if( EQUALN(poTable->get_name(dv_i).c_str(),"ogr_layer_info",14) + && poTable->is_container( dv_i ) ) + { + AttrTable *poAttr = poTable->get_attr_table( dv_i ); + string target_container = poAttr->get_attr( "target_container" ); + BaseType *poVar = poDDS->var( target_container.c_str() ); + + if( poVar == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to find variable '%s' named in\n" + "ogr_layer_info.target_container, skipping.", + target_container.c_str() ); + continue; + } + + if( poVar->type() == dods_sequence_c ) + AddLayer( + new OGRDODSSequenceLayer(this, + target_container.c_str(), + poAttr) ); + else if( poVar->type() == dods_grid_c + || poVar->type() == dods_array_c ) + AddLayer( new OGRDODSGridLayer(this,target_container.c_str(), + poAttr) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Walk through the DODS variables looking for easily targetted */ +/* ones. Eventually this will need to be driven by the AIS info. */ +/* -------------------------------------------------------------------- */ + if( nLayers == 0 ) + { + DDS::Vars_iter v_i; + + for( v_i = poDDS->var_begin(); v_i != poDDS->var_end(); v_i++ ) + { + BaseType *poVar = *v_i; + + if( poVar->type() == dods_sequence_c ) + AddLayer( new OGRDODSSequenceLayer(this,poVar->name().c_str(), + NULL) ); + else if( poVar->type() == dods_grid_c + || poVar->type() == dods_array_c ) + AddLayer( new OGRDODSGridLayer(this,poVar->name().c_str(), + NULL) ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* AddLayer() */ +/************************************************************************/ + +void OGRDODSDataSource::AddLayer( OGRDODSLayer *poLayer ) + +{ + papoLayers = (OGRDODSLayer **) + CPLRealloc( papoLayers, sizeof(OGRDODSLayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDODSDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRDODSDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodsdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodsdriver.cpp new file mode 100644 index 000000000..9f2c5cd19 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodsdriver.cpp @@ -0,0 +1,99 @@ +/****************************************************************************** + * $Id: ogrdodsdriver.cpp 27597 2014-08-22 17:15:49Z rouault $ + * + * Project: OGR/DODS Interface + * Purpose: Implements OGRDODSDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dods.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrdodsdriver.cpp 27597 2014-08-22 17:15:49Z rouault $"); + +/************************************************************************/ +/* ~OGRDODSDriver() */ +/************************************************************************/ + +OGRDODSDriver::~OGRDODSDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRDODSDriver::GetName() + +{ + return "OGR_DODS"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRDODSDriver::Open( const char * pszFilename, + int bUpdate ) + +{ + OGRDODSDataSource *poDS; + + if( !EQUALN(pszFilename,"DODS:http:",10) ) + return NULL; + + poDS = new OGRDODSDataSource(); + + if( !poDS->Open( pszFilename ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDODSDriver::TestCapability( const char * pszCap ) + +{ + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRDODS() */ +/************************************************************************/ + +void RegisterOGRDODS() + +{ + if (! GDAL_CHECK_VERSION("OGR/DODS driver")) + return; + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( new OGRDODSDriver ); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodsfielddefn.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodsfielddefn.cpp new file mode 100644 index 000000000..1801c1054 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodsfielddefn.cpp @@ -0,0 +1,179 @@ +/****************************************************************************** + * $Id: ogrdodsfielddefn.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: OGR/DODS Interface + * Purpose: Implements OGRDODSFieldDefn class. This is a small class used + * to encapsulate information about a referenced field. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dods.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrdodsfielddefn.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* OGRDODSFieldDefn() */ +/************************************************************************/ + +OGRDODSFieldDefn::OGRDODSFieldDefn() + +{ + pszFieldName = NULL; + pszFieldScope = NULL; + iFieldIndex = -1; + pszFieldValue = NULL; + bValid = FALSE; + pszPathToSequence = NULL; + bRelativeToSuperSequence = FALSE; + bRelativeToSequence = FALSE; +} + +/************************************************************************/ +/* ~OGRDODSFieldDefn() */ +/************************************************************************/ + +OGRDODSFieldDefn::~OGRDODSFieldDefn() + +{ + CPLFree( pszFieldName ); + CPLFree( pszFieldScope ); + CPLFree( pszFieldValue ); + CPLFree( pszPathToSequence ); +} + +/************************************************************************/ +/* Initialize() */ +/* */ +/* Build field reference from a DAS entry. The AttrTable */ +/* passed should be the container of the field defn. For */ +/* instance, the "x_field" node with a name and scope sub */ +/* entry. */ +/************************************************************************/ + +int OGRDODSFieldDefn::Initialize( AttrTable *poEntry, + BaseType *poTarget, + BaseType *poSuperSeq ) + +{ + const char *pszFieldScope = poEntry->get_attr("scope").c_str(); + if( pszFieldScope == NULL ) + pszFieldScope = "dds"; + + return Initialize( poEntry->get_attr("name").c_str(), pszFieldScope, + poTarget, poSuperSeq ); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +int OGRDODSFieldDefn::Initialize( const char *pszFieldNameIn, + const char *pszFieldScopeIn, + BaseType *poTarget, + BaseType *poSuperSeq ) + +{ + pszFieldScope = CPLStrdup( pszFieldScopeIn ); + pszFieldName = CPLStrdup( pszFieldNameIn ); + + if( poTarget != NULL && EQUAL(pszFieldScope,"dds") ) + { + string oTargPath = OGRDODSGetVarPath( poTarget ); + int nTargPathLen = strlen(oTargPath.c_str()); + + if( EQUALN(oTargPath.c_str(),pszFieldNameIn,nTargPathLen) + && pszFieldNameIn[nTargPathLen] == '.' ) + { + CPLFree( pszFieldName ); + pszFieldName = CPLStrdup( pszFieldNameIn + nTargPathLen + 1 ); + + bRelativeToSequence = TRUE; + iFieldIndex = OGRDODSGetVarIndex( + dynamic_cast( poTarget ), pszFieldName ); + } + else if( poSuperSeq != NULL ) + { + string oTargPath = OGRDODSGetVarPath( poSuperSeq ); + int nTargPathLen = strlen(oTargPath.c_str()); + + if( EQUALN(oTargPath.c_str(),pszFieldNameIn,nTargPathLen) + && pszFieldNameIn[nTargPathLen] == '.' ) + { + CPLFree( pszFieldName ); + pszFieldName = CPLStrdup( pszFieldNameIn + nTargPathLen + 1 ); + + bRelativeToSuperSequence = TRUE; + iFieldIndex = OGRDODSGetVarIndex( + dynamic_cast( poSuperSeq ), pszFieldName ); + } + } + } + + bValid = TRUE; + + return TRUE; +} + +/************************************************************************/ +/* OGRDODSGetVarPath() */ +/* */ +/* Return the full path to a variable. */ +/************************************************************************/ + +string OGRDODSGetVarPath( BaseType *poTarget ) + +{ + string oFullName; + + oFullName = poTarget->name(); + + while( (poTarget = poTarget->get_parent()) != NULL ) + { + oFullName = poTarget->name() + "." + oFullName; + } + + return oFullName; +} + +/************************************************************************/ +/* OGRDODSGetVarIndex() */ +/************************************************************************/ + +int OGRDODSGetVarIndex( Sequence *poParent, string oVarName ) + +{ + Sequence::Vars_iter v_i; + int i; + + for( v_i = poParent->var_begin(), i=0; + v_i != poParent->var_end(); + v_i++, i++ ) + { + if( EQUAL((*v_i)->name().c_str(),oVarName.c_str()) ) + return i; + } + + return -1; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodsgrid.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodsgrid.cpp new file mode 100644 index 000000000..aa31e97e8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodsgrid.cpp @@ -0,0 +1,599 @@ +/****************************************************************************** + * $Id: ogrdodsgrid.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OGR/DODS Interface + * Purpose: Implements OGRDODSGridLayer class, which implements the + * "Grid/Array" access strategy. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_dods.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrdodsgrid.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRDODSGridLayer() */ +/************************************************************************/ + +OGRDODSGridLayer::OGRDODSGridLayer( OGRDODSDataSource *poDSIn, + const char *pszTargetIn, + AttrTable *poOGRLayerInfoIn ) + + : OGRDODSLayer( poDSIn, pszTargetIn, poOGRLayerInfoIn ) + +{ + pRawData = NULL; + +/* -------------------------------------------------------------------- */ +/* What is the layer name? */ +/* -------------------------------------------------------------------- */ + string oLayerName; + const char *pszLayerName = pszTargetIn; + + if( poOGRLayerInfo != NULL ) + { + oLayerName = poOGRLayerInfo->get_attr( "layer_name" ); + if( strlen(oLayerName.c_str()) > 0 ) + pszLayerName = oLayerName.c_str(); + } + + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + poFeatureDefn->Reference(); + +/* -------------------------------------------------------------------- */ +/* Fetch the target variable. */ +/* -------------------------------------------------------------------- */ + BaseType *poTargVar = poDS->poDDS->var( pszTargetIn ); + + if( poTargVar->type() == dods_grid_c ) + { + poTargetGrid = dynamic_cast( poTargVar ); + poTargetArray = dynamic_cast(poTargetGrid->array_var()); + } + else if( poTargVar->type() == dods_array_c ) + { + poTargetGrid = NULL; + poTargetArray = dynamic_cast( poTargVar ); + } + else + { + CPLAssert( FALSE ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Count arrays in use. */ +/* -------------------------------------------------------------------- */ + AttrTable *poExtraContainers = NULL; + nArrayRefCount = 1; // primary target. + + if( poOGRLayerInfo != NULL ) + poExtraContainers = poOGRLayerInfo->find_container("extra_containers"); + + if( poExtraContainers != NULL ) + { + AttrTable::Attr_iter dv_i; + + for( dv_i = poExtraContainers->attr_begin(); + dv_i != poExtraContainers->attr_end(); dv_i++ ) + { + nArrayRefCount++; + } + } + +/* -------------------------------------------------------------------- */ +/* Collect extra_containers. */ +/* -------------------------------------------------------------------- */ + paoArrayRefs = new OGRDODSArrayRef[nArrayRefCount]; + paoArrayRefs[0].pszName = CPLStrdup( pszTargetIn ); + paoArrayRefs[0].poArray = poTargetArray; + + nArrayRefCount = 1; + + if( poExtraContainers != NULL ) + { + AttrTable::Attr_iter dv_i; + + for( dv_i = poExtraContainers->attr_begin(); + dv_i != poExtraContainers->attr_end(); dv_i++ ) + { + const char *pszTargetName=poExtraContainers->get_attr(dv_i).c_str(); + BaseType *poExtraTarget = poDS->poDDS->var( pszTargetName ); + + if( poExtraTarget == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to find extra_container '%s', skipping.", + pszTargetName ); + continue; + } + + if( poExtraTarget->type() == dods_array_c ) + paoArrayRefs[nArrayRefCount].poArray = + dynamic_cast( poExtraTarget ); + else if( poExtraTarget->type() == dods_grid_c ) + { + Grid *poGrid = dynamic_cast( poExtraTarget ); + paoArrayRefs[nArrayRefCount].poArray = + dynamic_cast( poGrid->array_var() ); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Target container '%s' is not grid or array, skipping.", + pszTargetName ); + continue; + } + + paoArrayRefs[nArrayRefCount++].pszName = CPLStrdup(pszTargetName); + } + } + +/* -------------------------------------------------------------------- */ +/* Collect dimension information. */ +/* -------------------------------------------------------------------- */ + int iDim; + Array::Dim_iter iterDim; + + nDimCount = poTargetArray->dimensions(); + paoDimensions = new OGRDODSDim[nDimCount]; + nMaxRawIndex = 1; + + for( iterDim = poTargetArray->dim_begin(), iDim = 0; + iterDim != poTargetArray->dim_end(); + iterDim++, iDim++ ) + { + paoDimensions[iDim].pszDimName = + CPLStrdup(poTargetArray->dimension_name(iterDim).c_str()); + paoDimensions[iDim].nDimStart = + poTargetArray->dimension_start(iterDim); + paoDimensions[iDim].nDimEnd = + poTargetArray->dimension_stop(iterDim); + paoDimensions[iDim].nDimStride = + poTargetArray->dimension_stride(iterDim); + paoDimensions[iDim].poMap = NULL; + + paoDimensions[iDim].nDimEntries = + (paoDimensions[iDim].nDimEnd + 1 - paoDimensions[iDim].nDimStart + + paoDimensions[iDim].nDimStride - 1) + / paoDimensions[iDim].nDimStride; + + nMaxRawIndex *= paoDimensions[iDim].nDimEntries; + } + +/* -------------------------------------------------------------------- */ +/* If we are working with a grid, collect the maps. */ +/* -------------------------------------------------------------------- */ + if( poTargetGrid != NULL ) + { + int iMap; + Grid::Map_iter iterMap; + + for( iterMap = poTargetGrid->map_begin(), iMap = 0; + iterMap != poTargetGrid->map_end(); + iterMap++, iMap++ ) + { + paoDimensions[iMap].poMap = dynamic_cast(*iterMap); + } + + CPLAssert( iMap == nDimCount ); + } + +/* -------------------------------------------------------------------- */ +/* Setup field definitions. The first nDimCount will be the */ +/* dimension attributes, and after that comes the actual target */ +/* array. */ +/* -------------------------------------------------------------------- */ + for( iDim = 0; iDim < nDimCount; iDim++ ) + { + OGRFieldDefn oField( paoDimensions[iDim].pszDimName, OFTInteger ); + + if( EQUAL(oField.GetNameRef(), poTargetArray->name().c_str()) ) + oField.SetName(CPLSPrintf("%s_i",paoDimensions[iDim].pszDimName)); + + if( paoDimensions[iDim].poMap != NULL ) + { + switch( paoDimensions[iDim].poMap->var()->type() ) + { + case dods_byte_c: + case dods_int16_c: + case dods_uint16_c: + case dods_int32_c: + case dods_uint32_c: + oField.SetType( OFTInteger ); + break; + + case dods_float32_c: + case dods_float64_c: + oField.SetType( OFTReal ); + break; + + case dods_str_c: + case dods_url_c: + oField.SetType( OFTString ); + break; + + default: + // Ignore + break; + } + } + + poFeatureDefn->AddFieldDefn( &oField ); + } + +/* -------------------------------------------------------------------- */ +/* Setup the array attributes themselves. */ +/* -------------------------------------------------------------------- */ + int iArray; + for( iArray=0; iArray < nArrayRefCount; iArray++ ) + { + OGRDODSArrayRef *poRef = paoArrayRefs + iArray; + OGRFieldDefn oArrayField( poRef->poArray->name().c_str(), OFTInteger ); + + switch( poRef->poArray->var()->type() ) + { + case dods_byte_c: + case dods_int16_c: + case dods_uint16_c: + case dods_int32_c: + case dods_uint32_c: + oArrayField.SetType( OFTInteger ); + break; + + case dods_float32_c: + case dods_float64_c: + oArrayField.SetType( OFTReal ); + break; + + case dods_str_c: + case dods_url_c: + oArrayField.SetType( OFTString ); + break; + + default: + // Ignore + break; + } + + poFeatureDefn->AddFieldDefn( &oArrayField ); + poRef->iFieldIndex = poFeatureDefn->GetFieldCount() - 1; + } + +/* -------------------------------------------------------------------- */ +/* X/Y/Z fields. */ +/* -------------------------------------------------------------------- */ + if( poOGRLayerInfo != NULL ) + { + AttrTable *poField = poOGRLayerInfo->find_container("x_field"); + if( poField != NULL ) + { + oXField.Initialize( poField ); + oXField.iFieldIndex = + poFeatureDefn->GetFieldIndex( oXField.pszFieldName ); + } + + poField = poOGRLayerInfo->find_container("y_field"); + if( poField != NULL ) + { + oYField.Initialize( poField ); + oYField.iFieldIndex = + poFeatureDefn->GetFieldIndex( oYField.pszFieldName ); + } + + poField = poOGRLayerInfo->find_container("z_field"); + if( poField != NULL ) + { + oZField.Initialize( poField ); + oZField.iFieldIndex = + poFeatureDefn->GetFieldIndex( oZField.pszFieldName ); + } + + } + +/* -------------------------------------------------------------------- */ +/* If we have no layerinfo, then check if there are obvious x/y */ +/* fields. */ +/* -------------------------------------------------------------------- */ + else + { + if( poFeatureDefn->GetFieldIndex( "lat" ) != -1 + && poFeatureDefn->GetFieldIndex( "lon" ) != -1 ) + { + oXField.Initialize( "lon", "dds" ); + oXField.iFieldIndex = poFeatureDefn->GetFieldIndex( "lon" ); + oYField.Initialize( "lat", "dds" ); + oYField.iFieldIndex = poFeatureDefn->GetFieldIndex( "lat" ); + } + else if( poFeatureDefn->GetFieldIndex( "latitude" ) != -1 + && poFeatureDefn->GetFieldIndex( "longitude" ) != -1 ) + { + oXField.Initialize( "longitude", "dds" ); + oXField.iFieldIndex = poFeatureDefn->GetFieldIndex( "longitude" ); + oYField.Initialize( "latitude", "dds" ); + oYField.iFieldIndex = poFeatureDefn->GetFieldIndex( "latitude" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Set the layer geometry type if we have point inputs. */ +/* -------------------------------------------------------------------- */ + if( oZField.iFieldIndex >= 0 ) + poFeatureDefn->SetGeomType( wkbPoint25D ); + else if( oXField.iFieldIndex >= 0 && oYField.iFieldIndex >= 0 ) + poFeatureDefn->SetGeomType( wkbPoint ); + else + poFeatureDefn->SetGeomType( wkbNone ); +} + +/************************************************************************/ +/* ~OGRDODSGridLayer() */ +/************************************************************************/ + +OGRDODSGridLayer::~OGRDODSGridLayer() + +{ + delete[] paoArrayRefs; + delete[] paoDimensions; +} + +/************************************************************************/ +/* ArrayEntryToField() */ +/************************************************************************/ + +int OGRDODSGridLayer::ArrayEntryToField( Array *poArray, void *pRawData, + int iArrayIndex, + OGRFeature *poFeature, int iField) + +{ + switch( poArray->var()->type() ) + { + case dods_byte_c: + { + GByte *pabyRawData = (GByte *) pRawData; + poFeature->SetField( iField, pabyRawData[iArrayIndex] ); + } + break; + + case dods_int16_c: + { + GInt16 *panRawData = (GInt16 *) pRawData; + poFeature->SetField( iField, panRawData[iArrayIndex] ); + } + break; + + case dods_uint16_c: + { + GUInt16 *panRawData = (GUInt16 *) pRawData; + poFeature->SetField( iField, panRawData[iArrayIndex] ); + } + break; + + case dods_int32_c: + { + GInt32 *panRawData = (GInt32 *) pRawData; + poFeature->SetField( iField, panRawData[iArrayIndex] ); + } + break; + + case dods_uint32_c: + { + GUInt32 *panRawData = (GUInt32 *) pRawData; + poFeature->SetField( iField, (int) panRawData[iArrayIndex] ); + } + break; + + case dods_float32_c: + { + float * pafRawData = (float *) pRawData; + poFeature->SetField( iField, pafRawData[iArrayIndex] ); + } + break; + + case dods_float64_c: + { + double * padfRawData = (double *) pRawData; + poFeature->SetField( iField, padfRawData[iArrayIndex] ); + } + break; + + default: + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRDODSGridLayer::GetFeature( GIntBig nFeatureId ) + +{ + if( nFeatureId < 0 || nFeatureId >= nMaxRawIndex ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Ensure we have the dataset. */ +/* -------------------------------------------------------------------- */ + if( !ProvideDataDDS() ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the feature being read. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poFeature = new OGRFeature( poFeatureDefn ); + poFeature->SetFID( nFeatureId ); + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Establish the values for the various dimension indices. */ +/* -------------------------------------------------------------------- */ + int iDim; + int nRemainder = nFeatureId; + + for( iDim = nDimCount-1; iDim >= 0; iDim-- ) + { + paoDimensions[iDim].iLastValue = + (nRemainder % paoDimensions[iDim].nDimEntries) + * paoDimensions[iDim].nDimStride + + paoDimensions[iDim].nDimStart; + nRemainder = nRemainder / paoDimensions[iDim].nDimEntries; + + if( poTargetGrid == NULL ) + poFeature->SetField( iDim, paoDimensions[iDim].iLastValue ); + } + CPLAssert( nRemainder == 0 ); + +/* -------------------------------------------------------------------- */ +/* For grids, we need to apply the values of the dimensions */ +/* looked up in the corresponding map. */ +/* -------------------------------------------------------------------- */ + if( poTargetGrid != NULL ) + { + for( iDim = 0; iDim < nDimCount; iDim++ ) + { + ArrayEntryToField( paoDimensions[iDim].poMap, + paoDimensions[iDim].pRawData, + paoDimensions[iDim].iLastValue, + poFeature, iDim ); + } + } + +/* -------------------------------------------------------------------- */ +/* Process all the regular data fields. */ +/* -------------------------------------------------------------------- */ + int iArray; + for( iArray = 0; iArray < nArrayRefCount; iArray++ ) + { + OGRDODSArrayRef *poRef = paoArrayRefs + iArray; + + ArrayEntryToField( poRef->poArray, poRef->pRawData, nFeatureId, + poFeature, poRef->iFieldIndex ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have geometry information? */ +/* -------------------------------------------------------------------- */ + if( oXField.iFieldIndex >= 0 && oYField.iFieldIndex >= 0 ) + { + OGRPoint *poPoint = new OGRPoint(); + + poPoint->setX( poFeature->GetFieldAsDouble( oXField.iFieldIndex ) ); + poPoint->setY( poFeature->GetFieldAsDouble( oYField.iFieldIndex ) ); + + if( oZField.iFieldIndex >= 0 ) + poPoint->setZ( poFeature->GetFieldAsDouble(oZField.iFieldIndex) ); + + poFeature->SetGeometryDirectly( poPoint ); + } + + return poFeature; +} + +/************************************************************************/ +/* ProvideDataDDS() */ +/************************************************************************/ + +int OGRDODSGridLayer::ProvideDataDDS() + +{ + if( bDataLoaded ) + return poTargetVar != NULL; + + int bResult = OGRDODSLayer::ProvideDataDDS(); + + if( !bResult ) + return bResult; + + int iArray; + for( iArray=0; iArray < nArrayRefCount; iArray++ ) + { + OGRDODSArrayRef *poRef = paoArrayRefs + iArray; + BaseType *poTarget = poDataDDS->var( poRef->pszName ); + + // Reset ref array pointer to point in DataDDS result. + if( poTarget->type() == dods_grid_c ) + { + Grid *poGrid = dynamic_cast( poTarget ); + poRef->poArray = dynamic_cast(poGrid->array_var()); + + if( iArray == 0 ) + poTargetGrid = poGrid; + } + else if( poTarget->type() == dods_array_c ) + { + poRef->poArray = dynamic_cast( poTarget ); + } + else + { + CPLAssert( FALSE ); + return FALSE; + } + + if( iArray == 0 ) + poTargetArray = poRef->poArray; + + // Allocate appropriate raw data array, and pull out data into it. + poRef->pRawData = CPLMalloc( poRef->poArray->width() ); + poRef->poArray->buf2val( &(poRef->pRawData) ); + } + + // Setup pointers to each of the map objects. + if( poTargetGrid != NULL ) + { + int iMap; + Grid::Map_iter iterMap; + + for( iterMap = poTargetGrid->map_begin(), iMap = 0; + iterMap != poTargetGrid->map_end(); + iterMap++, iMap++ ) + { + paoDimensions[iMap].poMap = dynamic_cast(*iterMap); + paoDimensions[iMap].pRawData = + CPLMalloc( paoDimensions[iMap].poMap->width() ); + paoDimensions[iMap].poMap->buf2val( &(paoDimensions[iMap].pRawData) ); + } + } + + return bResult; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRDODSGridLayer::GetFeatureCount( int bForce ) + +{ + if( m_poFilterGeom == NULL && m_poAttrQuery == NULL ) + return nMaxRawIndex; + else + return OGRDODSLayer::GetFeatureCount( bForce ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodslayer.cpp new file mode 100644 index 000000000..9056a784c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodslayer.cpp @@ -0,0 +1,264 @@ +/****************************************************************************** + * $Id: ogrdodslayer.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: OGR/DODS Interface + * Purpose: Implements OGRDODSLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dods.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrdodslayer.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* OGRDODSLayer() */ +/************************************************************************/ + +OGRDODSLayer::OGRDODSLayer( OGRDODSDataSource *poDSIn, + const char *pszTargetIn, + AttrTable *poOGRLayerInfoIn ) + +{ + poDS = poDSIn; + poFeatureDefn = NULL; + pszQuery = NULL; + pszFIDColumn = NULL; + poSRS = NULL; + iNextShapeId = 0; + pszTarget = CPLStrdup( pszTargetIn ); + papoFields = NULL; + + bDataLoaded = FALSE; + poConnection = NULL; + poTargetVar = NULL; + poOGRLayerInfo = poOGRLayerInfoIn; + bKnowExtent = FALSE; + + poDataDDS = new DataDDS( poDSIn->poBTF ); + +/* ==================================================================== */ +/* Harvest some metadata if available. */ +/* ==================================================================== */ + if( poOGRLayerInfo != NULL ) + { + string oMValue; + +/* -------------------------------------------------------------------- */ +/* spatial_ref */ +/* -------------------------------------------------------------------- */ + oMValue = poOGRLayerInfo->get_attr( "spatial_ref" ); + if( oMValue.length() > 0 ) + { + poSRS = new OGRSpatialReference(); + if( poSRS->SetFromUserInput( oMValue.c_str() ) != OGRERR_NONE ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Ignoring unreconised SRS '%s'", + oMValue.c_str() ); + delete poSRS; + poSRS = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Layer extents. */ +/* -------------------------------------------------------------------- */ + AttrTable *poLayerExt=poOGRLayerInfo->find_container("layer_extents"); + if( poLayerExt != NULL ) + { + bKnowExtent = TRUE; + sExtent.MinX = CPLAtof(poLayerExt->get_attr("x_min").c_str()); + sExtent.MaxX = CPLAtof(poLayerExt->get_attr("x_max").c_str()); + sExtent.MinY = CPLAtof(poLayerExt->get_attr("y_min").c_str()); + sExtent.MaxY = CPLAtof(poLayerExt->get_attr("y_max").c_str()); + } + + } + +/* ==================================================================== */ +/* Are we actually referencing a nested subsequence? If so, we */ +/* need to find the super sequence so we can do the layered */ +/* stepping. */ +/* ==================================================================== */ +} + +/************************************************************************/ +/* ~OGRDODSLayer() */ +/************************************************************************/ + +OGRDODSLayer::~OGRDODSLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "DODS", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( papoFields != NULL ) + { + int iField; + + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + delete papoFields[iField]; + + CPLFree( papoFields ); + } + + if( poSRS != NULL ) + poSRS->Release(); + + if( poFeatureDefn != NULL ) + poFeatureDefn->Release(); + + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + + CPLFree( pszTarget ); + pszTarget = NULL; + + if( poConnection != NULL ) + delete poConnection; + + delete poDataDDS; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRDODSLayer::ResetReading() + +{ + iNextShapeId = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRDODSLayer::GetNextFeature() + +{ + OGRFeature *poFeature; + + for( poFeature = GetFeature( iNextShapeId++ ); + poFeature != NULL; + poFeature = GetFeature( iNextShapeId++ ) ) + { + if( FilterGeometry( poFeature->GetGeometryRef() ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } + + return NULL; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDODSLayer::TestCapability( const char * pszCap ) + +{ + return FALSE; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRDODSLayer::GetSpatialRef() + +{ + return poSRS; +} + +/************************************************************************/ +/* ProvideDataDDS() */ +/************************************************************************/ + +int OGRDODSLayer::ProvideDataDDS() + +{ + if( bDataLoaded ) + return poTargetVar != NULL; + + bDataLoaded = TRUE; + try + { + poConnection = new AISConnect( poDS->oBaseURL ); + CPLDebug( "DODS", "request_data(%s,%s)", + poDS->oBaseURL.c_str(), + (poDS->oProjection + poDS->oConstraints).c_str() ); + + // We may need to use custom constraints here. + poConnection->request_data( *poDataDDS, + poDS->oProjection + poDS->oConstraints ); + } + catch (Error &e) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DataDDS request failed:\n%s", + e.get_error_message().c_str() ); + return FALSE; + } + + poTargetVar = poDataDDS->var( pszTarget ); + + return poTargetVar != NULL; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRDODSLayer::GetExtent(OGREnvelope *psExtent, int bForce) + +{ + OGRErr eErr; + + if( bKnowExtent ) + { + *psExtent = this->sExtent; + return OGRERR_NONE; + } + + if( !bForce ) + return OGRERR_FAILURE; + + eErr = OGRLayer::GetExtent( &sExtent, bForce ); + if( eErr == OGRERR_NONE ) + { + bKnowExtent = TRUE; + *psExtent = sExtent; + } + + return eErr; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodssequencelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodssequencelayer.cpp new file mode 100644 index 000000000..e8dcd538f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dods/ogrdodssequencelayer.cpp @@ -0,0 +1,1029 @@ +/****************************************************************************** + * $Id: ogrdodssequencelayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OGR/DODS Interface + * Purpose: Implements OGRDODSSequenceLayer class, which implements the + * "Simple Sequence" access strategy. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_dods.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrdodssequencelayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRDODSSequenceLayer() */ +/************************************************************************/ + +OGRDODSSequenceLayer::OGRDODSSequenceLayer( OGRDODSDataSource *poDSIn, + const char *pszTargetIn, + AttrTable *poOGRLayerInfoIn ) + + : OGRDODSLayer( poDSIn, pszTargetIn, poOGRLayerInfoIn ) + +{ + pszSubSeqPath = "profile"; // hardcode for now. + panSubSeqSize = NULL; + iLastSuperSeq = -1; + +/* -------------------------------------------------------------------- */ +/* What is the layer name? */ +/* -------------------------------------------------------------------- */ + string oLayerName; + const char *pszLayerName = pszTargetIn; + + if( poOGRLayerInfo != NULL ) + { + oLayerName = poOGRLayerInfo->get_attr( "layer_name" ); + if( strlen(oLayerName.c_str()) > 0 ) + pszLayerName = oLayerName.c_str(); + } + + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + poFeatureDefn->Reference(); + +/* -------------------------------------------------------------------- */ +/* Fetch the target variable. */ +/* -------------------------------------------------------------------- */ + Sequence *seq = dynamic_cast(poDS->poDDS->var( pszTargetIn )); + + poTargetVar = seq; + poSuperSeq = FindSuperSequence( seq ); + +/* -------------------------------------------------------------------- */ +/* X/Y/Z fields. */ +/* -------------------------------------------------------------------- */ + if( poOGRLayerInfo != NULL ) + { + AttrTable *poField = poOGRLayerInfo->find_container("x_field"); + if( poField != NULL ) + oXField.Initialize( poField, poTargetVar, poSuperSeq ); + + poField = poOGRLayerInfo->find_container("y_field"); + if( poField != NULL ) + oYField.Initialize( poField, poTargetVar, poSuperSeq ); + + poField = poOGRLayerInfo->find_container("z_field"); + if( poField != NULL ) + oZField.Initialize( poField, poTargetVar, poSuperSeq ); + } + +/* -------------------------------------------------------------------- */ +/* If we have no layerinfo, then check if there are obvious x/y */ +/* fields. */ +/* -------------------------------------------------------------------- */ + else + { + string oTargName = pszTargetIn; + string oSSTargName; + string x, y; + + if( poSuperSeq != NULL ) + oSSTargName = OGRDODSGetVarPath( poSuperSeq ); + else + oSSTargName = "impossiblexxx"; + + if( poDS->poDDS->var( oTargName + ".lon" ) != NULL + && poDS->poDDS->var( oTargName + ".lat" ) != NULL ) + { + oXField.Initialize( (oTargName + ".lon").c_str(), "dds", + poTargetVar, poSuperSeq ); + oYField.Initialize( (oTargName + ".lat").c_str(), "dds", + poTargetVar, poSuperSeq ); + } + else if( poDS->poDDS->var( oSSTargName + ".lon" ) != NULL + && poDS->poDDS->var( oSSTargName + ".lat" ) != NULL ) + { + oXField.Initialize( (oSSTargName + ".lon").c_str(), "dds", + poTargetVar, poSuperSeq ); + oYField.Initialize( (oSSTargName + ".lat").c_str(), "dds", + poTargetVar, poSuperSeq ); + } + } + +/* -------------------------------------------------------------------- */ +/* Add fields for the contents of the sequence. */ +/* -------------------------------------------------------------------- */ + Sequence::Vars_iter v_i; + + for( v_i = seq->var_begin(); v_i != seq->var_end(); v_i++ ) + BuildFields( *v_i, NULL, NULL ); + +/* -------------------------------------------------------------------- */ +/* Add fields for the contents of the super-sequence if we have */ +/* one. */ +/* -------------------------------------------------------------------- */ + if( poSuperSeq != NULL ) + { + for( v_i = poSuperSeq->var_begin(); + v_i != poSuperSeq->var_end(); + v_i++ ) + BuildFields( *v_i, NULL, NULL ); + } +} + +/************************************************************************/ +/* ~OGRDODSSequenceLayer() */ +/************************************************************************/ + +OGRDODSSequenceLayer::~OGRDODSSequenceLayer() + +{ +} + +/************************************************************************/ +/* FindSuperSequence() */ +/* */ +/* Are we a subsequence of a sequence? */ +/************************************************************************/ + +Sequence *OGRDODSSequenceLayer::FindSuperSequence( BaseType *poChild ) + +{ + BaseType *poParent; + + for( poParent = poChild->get_parent(); + poParent != NULL; + poParent = poParent->get_parent() ) + { + if( poParent->type() == dods_sequence_c ) + { + return dynamic_cast( poParent ); + } + } + + return NULL; +} + +/************************************************************************/ +/* BuildFields() */ +/* */ +/* Build the field definition or definitions corresponding to */ +/* the passed variable and it's children (if it has them). */ +/************************************************************************/ + +int OGRDODSSequenceLayer::BuildFields( BaseType *poFieldVar, + const char *pszPathToVar, + const char *pszPathToSequence ) + +{ + OGRFieldDefn oField( "", OFTInteger ); + +/* -------------------------------------------------------------------- */ +/* Setup field name, including path if non-local. */ +/* -------------------------------------------------------------------- */ + if( pszPathToVar == NULL ) + oField.SetName( poFieldVar->name().c_str() ); + else + oField.SetName( CPLSPrintf( "%s.%s", pszPathToVar, + poFieldVar->name().c_str() ) ); + +/* -------------------------------------------------------------------- */ +/* Capture this field definition. */ +/* -------------------------------------------------------------------- */ + switch( poFieldVar->type() ) + { + case dods_byte_c: + case dods_int16_c: + case dods_uint16_c: + case dods_int32_c: + case dods_uint32_c: + if( pszPathToSequence ) + oField.SetType( OFTIntegerList ); + else + oField.SetType( OFTInteger ); + break; + + case dods_float32_c: + case dods_float64_c: + if( pszPathToSequence ) + oField.SetType( OFTRealList ); + else + oField.SetType( OFTReal ); + break; + + case dods_str_c: + case dods_url_c: + if( pszPathToSequence ) + oField.SetType( OFTStringList ); + else + oField.SetType( OFTString ); + break; + + case dods_sequence_c: + { + Sequence *seq = dynamic_cast( poFieldVar ); + Sequence::Vars_iter v_i; + + // We don't support a 3rd level of sequence nesting. + if( pszPathToSequence != NULL ) + return FALSE; + + // We don't explore down into the target sequence if we + // are recursing from a supersequence. + if( poFieldVar == this->poTargetVar ) + return FALSE; + + for( v_i = seq->var_begin(); v_i != seq->var_end(); v_i++ ) + { + BuildFields( *v_i, oField.GetNameRef(), oField.GetNameRef() ); + } + } + return FALSE; + + default: + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Add field to feature defn, and capture mapping. */ +/* -------------------------------------------------------------------- */ + poFeatureDefn->AddFieldDefn( &oField ); + + papoFields = (OGRDODSFieldDefn **) + CPLRealloc( papoFields, sizeof(void*) * poFeatureDefn->GetFieldCount()); + + papoFields[poFeatureDefn->GetFieldCount()-1] = + new OGRDODSFieldDefn(); + + papoFields[poFeatureDefn->GetFieldCount()-1]->Initialize( + OGRDODSGetVarPath(poFieldVar).c_str(), "dds", + poTargetVar, poSuperSeq ); + + + if( pszPathToSequence ) + papoFields[poFeatureDefn->GetFieldCount()-1]->pszPathToSequence + = CPLStrdup( pszPathToSequence ); + + return TRUE; +} + +/************************************************************************/ +/* GetFieldValue() */ +/************************************************************************/ + +BaseType *OGRDODSSequenceLayer::GetFieldValue( OGRDODSFieldDefn *poFDefn, + int nFeatureId, + Sequence *seq ) + +{ + if( seq == NULL ) + seq = dynamic_cast(poTargetVar); + + if( !poFDefn->bValid ) + return NULL; + +/* ==================================================================== */ +/* Fetch the actual value. */ +/* ==================================================================== */ + +/* -------------------------------------------------------------------- */ +/* Simple case of a direct field within the sequence object. */ +/* -------------------------------------------------------------------- */ + if( poFDefn->iFieldIndex >= 0 && poFDefn->bRelativeToSequence ) + { + return seq->var_value( nFeatureId, poFDefn->iFieldIndex ); + } + else if( poFDefn->iFieldIndex >= 0 && poFDefn->bRelativeToSuperSequence ) + { + return poSuperSeq->var_value( iLastSuperSeq, poFDefn->iFieldIndex ); + } + +/* -------------------------------------------------------------------- */ +/* More complex case where we need to drill down by name. */ +/* -------------------------------------------------------------------- */ + if( poFDefn->bRelativeToSequence ) + return seq->var_value( nFeatureId, poFDefn->pszFieldName ); + else if( poSuperSeq != NULL && poFDefn->bRelativeToSuperSequence ) + return poSuperSeq->var_value( iLastSuperSeq, poFDefn->pszFieldName ); + else + return poDataDDS->var( poFDefn->pszFieldName ); +} + +/************************************************************************/ +/* BaseTypeToDouble() */ +/************************************************************************/ + +double OGRDODSSequenceLayer::BaseTypeToDouble( BaseType *poBT ) + +{ + switch( poBT->type() ) + { + case dods_byte_c: + { + signed char byVal; + void *pValPtr = &byVal; + + poBT->buf2val( &pValPtr ); + return (double) byVal; + } + break; + + case dods_int16_c: + { + GInt16 nIntVal; + void *pValPtr = &nIntVal; + + poBT->buf2val( &pValPtr ); + return (double) nIntVal; + } + break; + + case dods_uint16_c: + { + GUInt16 nIntVal; + void *pValPtr = &nIntVal; + + poBT->buf2val( &pValPtr ); + return (double) nIntVal; + } + break; + + case dods_int32_c: + { + GInt32 nIntVal; + void *pValPtr = &nIntVal; + + poBT->buf2val( &pValPtr ); + return (double) nIntVal; + } + break; + + case dods_uint32_c: + { + GUInt32 nIntVal; + void *pValPtr = &nIntVal; + + poBT->buf2val( &pValPtr ); + return (double) nIntVal; + } + break; + + case dods_float32_c: + return dynamic_cast(poBT)->value(); + + case dods_float64_c: + return dynamic_cast(poBT)->value(); + + case dods_str_c: + case dods_url_c: + { + string *poStrVal = NULL; + double dfResult; + + poBT->buf2val( (void **) &poStrVal ); + dfResult = CPLAtof(poStrVal->c_str()); + delete poStrVal; + return dfResult; + } + break; + + default: + CPLAssert( FALSE ); + break; + } + + return 0.0; +} + +/************************************************************************/ +/* GetFieldValueAsDouble() */ +/************************************************************************/ + +double OGRDODSSequenceLayer::GetFieldValueAsDouble( OGRDODSFieldDefn *poFDefn, + int nFeatureId ) + +{ + BaseType *poBT; + + poBT = GetFieldValue( poFDefn, nFeatureId, NULL ); + if( poBT == NULL ) + return 0.0; + + return BaseTypeToDouble( poBT ); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRDODSSequenceLayer::GetFeature( GIntBig nFeatureId ) + +{ +/* -------------------------------------------------------------------- */ +/* Ensure we have the dataset. */ +/* -------------------------------------------------------------------- */ + if( !ProvideDataDDS() ) + return NULL; + + Sequence *seq = dynamic_cast(poTargetVar); + +/* -------------------------------------------------------------------- */ +/* Figure out what the super and subsequence number this */ +/* feature will be, and validate it. If there is not super */ +/* sequence the feature id is the subsequence number. */ +/* -------------------------------------------------------------------- */ + int iSubSeq = -1; + + if( nFeatureId < 0 || nFeatureId >= nRecordCount ) + return NULL; + + if( poSuperSeq == NULL ) + iSubSeq = nFeatureId; + else + { + int nSeqOffset = 0, iSuperSeq; + + // for now we just scan through till find find out what + // super sequence this in. In the long term we need a better (cached) + // approach that doesn't involve this quadratic cost. + for( iSuperSeq = 0; + iSuperSeq < nSuperSeqCount; + iSuperSeq++ ) + { + if( nSeqOffset + panSubSeqSize[iSuperSeq] > nFeatureId ) + { + iSubSeq = nFeatureId - nSeqOffset; + break; + } + nSeqOffset += panSubSeqSize[iSuperSeq]; + } + + CPLAssert( iSubSeq != -1 ); + + // Make sure we have the right target var ... the one + // corresponding to our current super sequence. + if( iSuperSeq != iLastSuperSeq ) + { + iLastSuperSeq = iSuperSeq; + poTargetVar = poSuperSeq->var_value( iSuperSeq, pszSubSeqPath ); + seq = dynamic_cast(poTargetVar); + } + } + +/* -------------------------------------------------------------------- */ +/* Create the feature being read. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poFeature = new OGRFeature( poFeatureDefn ); + poFeature->SetFID( nFeatureId ); + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Process all the regular data fields. */ +/* -------------------------------------------------------------------- */ + int iField; + + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + if( papoFields[iField]->pszPathToSequence ) + continue; + + BaseType *poFieldVar = GetFieldValue( papoFields[iField], iSubSeq, + NULL ); + + if( poFieldVar == NULL ) + continue; + + switch( poFieldVar->type() ) + { + case dods_byte_c: + { + signed char byVal; + void *pValPtr = &byVal; + + poFieldVar->buf2val( &pValPtr ); + poFeature->SetField( iField, byVal ); + } + break; + + case dods_int16_c: + { + GInt16 nIntVal; + void *pValPtr = &nIntVal; + + poFieldVar->buf2val( &pValPtr ); + poFeature->SetField( iField, nIntVal ); + } + break; + + case dods_uint16_c: + { + GUInt16 nIntVal; + void *pValPtr = &nIntVal; + + poFieldVar->buf2val( &pValPtr ); + poFeature->SetField( iField, nIntVal ); + } + break; + + case dods_int32_c: + { + GInt32 nIntVal; + void *pValPtr = &nIntVal; + + poFieldVar->buf2val( &pValPtr ); + poFeature->SetField( iField, nIntVal ); + } + break; + + case dods_uint32_c: + { + GUInt32 nIntVal; + void *pValPtr = &nIntVal; + + poFieldVar->buf2val( &pValPtr ); + poFeature->SetField( iField, (int) nIntVal ); + } + break; + + case dods_float32_c: + poFeature->SetField( iField, + dynamic_cast(poFieldVar)->value()); + break; + + case dods_float64_c: + poFeature->SetField( iField, + dynamic_cast(poFieldVar)->value()); + break; + + case dods_str_c: + case dods_url_c: + { + string *poStrVal = NULL; + poFieldVar->buf2val( (void **) &poStrVal ); + poFeature->SetField( iField, poStrVal->c_str() ); + delete poStrVal; + } + break; + + default: + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Handle data nested in sequences. */ +/* -------------------------------------------------------------------- */ + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + OGRDODSFieldDefn *poFD = papoFields[iField]; + const char *pszPathFromSubSeq; + + if( poFD->pszPathToSequence == NULL ) + continue; + + CPLAssert( strlen(poFD->pszPathToSequence) + < strlen(poFD->pszFieldName)-1 ); + + if( strstr(poFD->pszFieldName,poFD->pszPathToSequence) != NULL ) + pszPathFromSubSeq = + strstr(poFD->pszFieldName,poFD->pszPathToSequence) + + strlen(poFD->pszPathToSequence) + 1; + else + continue; + +/* -------------------------------------------------------------------- */ +/* Get the sequence out of which this variable will be collected. */ +/* -------------------------------------------------------------------- */ + BaseType *poFieldVar = seq->var_value( iSubSeq, + poFD->pszPathToSequence ); + Sequence *poSubSeq; + int nSubSeqCount; + + if( poFieldVar == NULL ) + continue; + + poSubSeq = dynamic_cast( poFieldVar ); + if( poSubSeq == NULL ) + continue; + + nSubSeqCount = poSubSeq->number_of_rows(); + +/* -------------------------------------------------------------------- */ +/* Allocate array to put values into. */ +/* -------------------------------------------------------------------- */ + OGRFieldDefn *poOFD = poFeature->GetFieldDefnRef( iField ); + int *panIntList = NULL; + double *padfDblList = NULL; + char **papszStrList = NULL; + + if( poOFD->GetType() == OFTIntegerList ) + { + panIntList = (int *) CPLCalloc(sizeof(int),nSubSeqCount); + } + else if( poOFD->GetType() == OFTRealList ) + { + padfDblList = (double *) CPLCalloc(sizeof(double),nSubSeqCount); + } + else if( poOFD->GetType() == OFTStringList ) + { + papszStrList = (char **) CPLCalloc(sizeof(char*),nSubSeqCount+1); + } + else + continue; + +/* -------------------------------------------------------------------- */ +/* Loop, fetching subsequence values. */ +/* -------------------------------------------------------------------- */ + int iSubIndex; + for( iSubIndex = 0; iSubIndex < nSubSeqCount; iSubIndex++ ) + { + poFieldVar = poSubSeq->var_value( iSubIndex, pszPathFromSubSeq ); + + if( poFieldVar == NULL ) + continue; + + switch( poFieldVar->type() ) + { + case dods_byte_c: + { + signed char byVal; + void *pValPtr = &byVal; + + poFieldVar->buf2val( &pValPtr ); + panIntList[iSubIndex] = byVal; + } + break; + + case dods_int16_c: + { + GInt16 nIntVal; + void *pValPtr = &nIntVal; + + poFieldVar->buf2val( &pValPtr ); + panIntList[iSubIndex] = nIntVal; + } + break; + + case dods_uint16_c: + { + GUInt16 nIntVal; + void *pValPtr = &nIntVal; + + poFieldVar->buf2val( &pValPtr ); + panIntList[iSubIndex] = nIntVal; + } + break; + + case dods_int32_c: + { + GInt32 nIntVal; + void *pValPtr = &nIntVal; + + poFieldVar->buf2val( &pValPtr ); + panIntList[iSubIndex] = nIntVal; + } + break; + + case dods_uint32_c: + { + GUInt32 nIntVal; + void *pValPtr = &nIntVal; + + poFieldVar->buf2val( &pValPtr ); + panIntList[iSubIndex] = nIntVal; + } + break; + + case dods_float32_c: + padfDblList[iSubIndex] = + dynamic_cast(poFieldVar)->value(); + break; + + case dods_float64_c: + padfDblList[iSubIndex] = + dynamic_cast(poFieldVar)->value(); + break; + + case dods_str_c: + case dods_url_c: + { + string *poStrVal = NULL; + poFieldVar->buf2val( (void **) &poStrVal ); + papszStrList[iSubIndex] = CPLStrdup( poStrVal->c_str() ); + delete poStrVal; + } + break; + + default: + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Apply back to feature. */ +/* -------------------------------------------------------------------- */ + if( poOFD->GetType() == OFTIntegerList ) + { + poFeature->SetField( iField, nSubSeqCount, panIntList ); + CPLFree(panIntList); + } + else if( poOFD->GetType() == OFTRealList ) + { + poFeature->SetField( iField, nSubSeqCount, padfDblList ); + CPLFree(padfDblList); + } + else if( poOFD->GetType() == OFTStringList ) + { + poFeature->SetField( iField, papszStrList ); + CSLDestroy( papszStrList ); + } + } + +/* ==================================================================== */ +/* Fetch the geometry. */ +/* ==================================================================== */ + if( oXField.bValid && oYField.bValid ) + { + int iXField = poFeature->GetFieldIndex( oXField.pszFieldName ); + int iYField = poFeature->GetFieldIndex( oYField.pszFieldName ); + int iZField = -1; + + if( oZField.bValid ) + iZField = poFeature->GetFieldIndex(oZField.pszFieldName); + +/* -------------------------------------------------------------------- */ +/* If we can't find the values in attributes then use the more */ +/* general mechanism to fetch the value. */ +/* -------------------------------------------------------------------- */ + + if( iXField == -1 || iYField == -1 + || (oZField.bValid && iZField == -1) ) + { + poFeature->SetGeometryDirectly( + new OGRPoint( GetFieldValueAsDouble( &oXField, iSubSeq ), + GetFieldValueAsDouble( &oYField, iSubSeq ), + GetFieldValueAsDouble( &oZField, iSubSeq ) ) ); + } +/* -------------------------------------------------------------------- */ +/* If the fields are list values, then build a linestring. */ +/* -------------------------------------------------------------------- */ + else if( poFeature->GetFieldDefnRef(iXField)->GetType() == OFTRealList + && poFeature->GetFieldDefnRef(iYField)->GetType() == OFTRealList ) + { + const double *padfX, *padfY, *padfZ = NULL; + int nPointCount, i; + OGRLineString *poLS = new OGRLineString(); + + padfX = poFeature->GetFieldAsDoubleList( iXField, &nPointCount ); + padfY = poFeature->GetFieldAsDoubleList( iYField, &nPointCount ); + if( iZField != -1 ) + padfZ = poFeature->GetFieldAsDoubleList(iZField,&nPointCount); + + poLS->setPoints( nPointCount, (double *) padfX, (double *) padfY, + (double *) padfZ ); + + // Make a pass clearing out NaN or Inf values. + for( i = 0; i < nPointCount; i++ ) + { + double dfX = poLS->getX(i); + double dfY = poLS->getY(i); + double dfZ = poLS->getZ(i); + int bReset = FALSE; + + if( OGRDODSIsDoubleInvalid( &dfX ) ) + { + dfX = 0.0; + bReset = TRUE; + } + if( OGRDODSIsDoubleInvalid( &dfY ) ) + { + dfY = 0.0; + bReset = TRUE; + } + if( OGRDODSIsDoubleInvalid( &dfZ ) ) + { + dfZ = 0.0; + bReset = TRUE; + } + + if( bReset ) + poLS->setPoint( i, dfX, dfY, dfZ ); + } + + poFeature->SetGeometryDirectly( poLS ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise build a point. */ +/* -------------------------------------------------------------------- */ + else + { + poFeature->SetGeometryDirectly( + new OGRPoint( + poFeature->GetFieldAsDouble( iXField ), + poFeature->GetFieldAsDouble( iYField ), + poFeature->GetFieldAsDouble( iZField ) ) ); + } + } + + return poFeature; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRDODSSequenceLayer::GetFeatureCount( int bForce ) + +{ + if( !bDataLoaded && !bForce ) + return -1; + + ProvideDataDDS(); + + return nRecordCount; +} + +/************************************************************************/ +/* ProvideDataDDS() */ +/************************************************************************/ + +int OGRDODSSequenceLayer::ProvideDataDDS() + +{ + if( bDataLoaded ) + return poTargetVar != NULL; + + int bResult = OGRDODSLayer::ProvideDataDDS(); + + if( !bResult ) + return bResult; + + // If we are in nested sequence mode, we now need to properly set + // the poTargetVar based on the current step in the supersequence. + poSuperSeq = FindSuperSequence( poTargetVar ); + +/* ==================================================================== */ +/* Figure out the record count. */ +/* ==================================================================== */ +/* -------------------------------------------------------------------- */ +/* For simple sequences without a supersequence just return the */ +/* count of elements. */ +/* -------------------------------------------------------------------- */ + if( poSuperSeq == NULL ) + nRecordCount = dynamic_cast(poTargetVar)->number_of_rows(); + +/* -------------------------------------------------------------------- */ +/* Otherwise we have to count up all the target sequence */ +/* instances for each of the super sequence items. */ +/* -------------------------------------------------------------------- */ + else + { + int iSuper; + + nSuperSeqCount = poSuperSeq->number_of_rows(); + panSubSeqSize = (int *) calloc(sizeof(int),nSuperSeqCount); + nRecordCount = 0; + for( iSuper = 0; iSuper < nSuperSeqCount; iSuper++ ) + { + Sequence *poSubSeq = dynamic_cast( + poSuperSeq->var_value( iSuper, pszSubSeqPath ) ); + + panSubSeqSize[iSuper] = poSubSeq->number_of_rows(); + nRecordCount += poSubSeq->number_of_rows(); + } + } + + return poTargetVar != NULL; +} + +/* IEEE Constants: + + http://www.psc.edu/general/software/packages/ieee/ieee.html + +Single Precision: + + S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF + 0 1 8 9 31 + +The value V represented by the word may be determined as follows: + + * If E=255 and F is nonzero, then V=NaN ("Not a number") + * If E=255 and F is zero and S is 1, then V=-Infinity + * If E=255 and F is zero and S is 0, then V=Infinity + * If 0 + +AutoCAD DWG + + + + +

    AutoCAD DWG

    + +OGR supports reading most versions of AutoCAD DWG when built with the Open +Design Alliance Teiga library. DWG is an binary working format +used for AutoCAD drawings. A reasonable effort has been made to make the +OGR DWG driver work similarly to the OGR DXF driver which shares a common +data model. The entire contents of the .dwg file is +represented as a single layer named "entities".

    + +DWG files are considered to have no georeferencing information through OGR. +Features will all have the following generic attributes: + +

      +
    • Layer: The name of the DXF layer. The default layer is "0". +
    • SubClasses: Where available, a list of classes to which an element belongs. +
    • ExtendedEntity: Where available, extended entity attributes all appended to form a single text attribute. +
    • Linetype: Where available, the line type used for this entity. +
    • EntityHandle: The hexadecimal entity handle. A sort of feature id. +
    • Text: The text of labels. +
    + +A reasonable attempt is made to preserve line color, line width, text size +and orientation via OGR feature styling information when translating elements. +Currently no effort is made to preserve fill styles or complex line style +attributes.

    + +The approximation of arcs, ellipses, circles and rounded polylines as +linestrings is done by splitting the arcs into subarcs of no more than a +threshhold angle. This angle is the OGR_ARC_STEPSIZE. This defaults to +four degrees, but may be overridden by setting the configuration variable +OGR_ARC_STEPSIZE.

    + +

    DWG_INLINE_BLOCKS

    + +The default behavior is for block references to be expanded with the +geometry of the block they reference. However, if the DWG_INLINE_BLOCKS +configuration option is set to the value FALSE, then the behavior is different +as described here. + +
      +
    • A new layer will be available called blocks. It will contain one or +more features for each block defined in the file. In addition to the usual +attributes, they will also have a BlockName attribute indicate what block +they are part of. +
    • The entities layer will have new attributes BlockName, BlockScale, +and BlockAngle. +
    • block referencesd will populate these new fields with the corresponding +information (they are null for all other entities). +
    • block references will not have block geometry inlined - instead they will +have a point geometry for the insertion point. +
    + +The intention is that with DWG_INLINE_BLOCKS disabled, the block references +will remain as references and the original block definitions will be +available via the blocks layer.

    + +

    Building

    + +Currently DWG building is somewhat adhoc. On linux the normal practice is +to hand edit gdal/ogr/ogrsf_frmts/dwg/GNUmakefile, update paths, and then +build the driver as a plugin using the "make plugin" target.

    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/makefile.vc new file mode 100644 index 000000000..b1fba6107 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/makefile.vc @@ -0,0 +1,65 @@ + +OBJ = \ + ogrdwgdriver.obj \ + ogrdwgdatasource.obj \ + ogrdwglayer.obj \ + ogrdwgblockslayer.obj \ + ogrdwg_blockmap.obj \ + ogrdwg_dimension.obj \ + ogrdwg_hatch.obj + +DXF_OBJ = ..\dxf\intronurbs.obj ..\dxf\ogrdxf_polyline_smooth.obj + +PLUGIN_DLL = ogr_DWG.dll + +GDAL_ROOT = ..\..\.. + +TD_LIBS = \ + $(TD_LIBDIR)/TD_ExamplesCommon.lib \ + $(TD_LIBDIR)/TD_Key.lib \ + $(TD_LIBDIR)/ModelerGeometry.lib \ + $(TD_LIBDIR)/TD_BrepRenderer.lib \ + $(TD_LIBDIR)/TD_Br.lib \ + $(TD_LIBDIR)/TD_AcisBuilder.lib \ + $(TD_LIBDIR)/TD_DynBlocks.lib \ + $(TD_LIBDIR)/TD_Db.lib \ + $(TD_LIBDIR)/TD_DbRoot.lib \ + $(TD_LIBDIR)/TD_Gs.lib \ + $(TD_LIBDIR)/TD_SpatialIndex.lib \ + $(TD_LIBDIR)/TD_Ave.lib \ + $(TD_LIBDIR)/TD_Root.lib \ + $(TD_LIBDIR)/TD_Gi.lib \ + $(TD_LIBDIR)/TD_Ge.lib \ + $(TD_LIBDIR)/TD_FT.lib \ + $(TD_LIBDIR)/TD_Alloc.lib \ + $(TD_LIBDIR)/RxRasterServices.lib \ + $(TD_LIBDIR)/Jpeg.lib \ + $(TD_LIBDIR)/RecomputeDimBlock.lib \ + $(TD_LIBDIR)/ExFieldEvaluator.lib \ + $(TD_LIBDIR)/OdBagFiler.lib \ + $(TD_LIBDIR)/RasterProcessor.lib \ + advapi32.lib + +!INCLUDE $(GDAL_ROOT)\nmake.opt + + +EXTRAFLAGS = -I.. -I..\.. $(TD_FLAGS) $(TD_INCLUDE) -I..\dxf + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + -del *.dll + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(DXF_OBJ) \ + $(GDAL_ROOT)/gdal_i.lib $(TD_LIBS) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest \ + -outputresource:$(PLUGIN_DLL);2 + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogr_dwg.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogr_dwg.h new file mode 100644 index 000000000..934db8873 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogr_dwg.h @@ -0,0 +1,279 @@ +/****************************************************************************** + * $Id: ogr_dxf.h 22008 2011-03-22 19:45:20Z warmerdam $ + * + * Project: DWG Translator + * Purpose: Definition of classes for OGR .dwg driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_DWG_H_INCLUDED +#define _OGR_DWG_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_conv.h" +#include +#include +#include +#include + +#include "ogr_autocad_services.h" + +#include "OdaCommon.h" +#include "diagnostics.h" +#include "DbDatabase.h" +#include "DbEntity.h" +#include "DbDimAssoc.h" +#include "DbObjectIterator.h" +#include "DbBlockTable.h" +#include "DbBlockTableRecord.h" +#include "DbSymbolTable.h" + +#include "OdCharMapper.h" +#include "RxObjectImpl.h" + +#include "ExSystemServices.h" +#include "ExHostAppServices.h" +#include "OdFileBuf.h" +#include "RxDynamicModule.h" +#include "FdField.h" + +class OGRDWGDataSource; +class OGRDWGServices; + +/************************************************************************/ +/* DWGBlockDefinition */ +/* */ +/* Container for info about a block. */ +/************************************************************************/ + +class DWGBlockDefinition +{ +public: + DWGBlockDefinition() : poGeometry(NULL) {} + ~DWGBlockDefinition(); + + OGRGeometry *poGeometry; + std::vector apoFeatures; +}; + +/************************************************************************/ +/* OGRDWGBlocksLayer() */ +/************************************************************************/ + +class OGRDWGBlocksLayer : public OGRLayer +{ + OGRDWGDataSource *poDS; + + OGRFeatureDefn *poFeatureDefn; + + int iNextFID; + unsigned int iNextSubFeature; + + std::map::iterator oIt; + + public: + OGRDWGBlocksLayer( OGRDWGDataSource *poDS ); + ~OGRDWGBlocksLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ); + + OGRFeature * GetNextUnfilteredFeature(); +}; + +/************************************************************************/ +/* OGRDWGLayer */ +/************************************************************************/ +class OGRDWGLayer : public OGRLayer +{ + OGRDWGDataSource *poDS; + + OGRFeatureDefn *poFeatureDefn; + int iNextFID; + + std::set oIgnoredEntities; + + std::queue apoPendingFeatures; + void ClearPendingFeatures(); + + std::map oStyleProperties; + + void TranslateGenericProperties( OGRFeature *poFeature, + OdDbEntityPtr poEntity ); + void PrepareLineStyle( OGRFeature *poFeature ); +// void ApplyOCSTransformer( OGRGeometry * ); + + OGRFeature * TranslatePOINT( OdDbEntityPtr poEntity ); + OGRFeature * TranslateLINE( OdDbEntityPtr poEntity ); + OGRFeature * TranslateLWPOLYLINE( OdDbEntityPtr poEntity ); + OGRFeature * Translate2DPOLYLINE( OdDbEntityPtr poEntity ); + OGRFeature * Translate3DPOLYLINE( OdDbEntityPtr poEntity ); + OGRFeature * TranslateELLIPSE( OdDbEntityPtr poEntity ); + OGRFeature * TranslateARC( OdDbEntityPtr poEntity ); + OGRFeature * TranslateMTEXT( OdDbEntityPtr poEntity ); + OGRFeature * TranslateDIMENSION( OdDbEntityPtr poEntity ); + OGRFeature * TranslateCIRCLE( OdDbEntityPtr poEntity ); + OGRFeature * TranslateSPLINE( OdDbEntityPtr poEntity ); + OGRFeature * TranslateHATCH( OdDbEntityPtr poEntity ); + OGRFeature * TranslateTEXT( OdDbEntityPtr poEntity ); + OGRFeature * TranslateINSERT( OdDbEntityPtr poEntity ); + + void FormatDimension( CPLString &osText, double dfValue ); + + CPLString TextUnescape( OdString oString); + + OdDbBlockTableRecordPtr poBlock; + OdDbObjectIteratorPtr poEntIter; + + public: + OGRDWGLayer( OGRDWGDataSource *poDS ); + ~OGRDWGLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ); + + OGRFeature * GetNextUnfilteredFeature(); + + // internal + void SetBlockTable( OdDbBlockTableRecordPtr ); + static double AngleCorrect( double dfAngle, double dfRatio ); +}; + +/************************************************************************/ +/* OGRDWGDataSource */ +/************************************************************************/ + +class OGRDWGDataSource : public OGRDataSource +{ + VSILFILE *fp; + + CPLString osName; + std::vector apoLayers; + + int iEntitiesSectionOffset; + + std::map oBlockMap; + std::map oHeaderVariables; + + CPLString osEncoding; + + // indexed by layer name, then by property name. + std::map< CPLString, std::map > + oLayerTable; + + std::map oLineTypeTable; + + int bInlineBlocks; + + OGRDWGServices *poServices; + OdDbDatabasePtr poDb; + + public: + OGRDWGDataSource(); + ~OGRDWGDataSource(); + + OdDbDatabasePtr GetDB() { return poDb; } + + int Open( OGRDWGServices *poServices, + const char * pszFilename, int bHeaderOnly=FALSE ); + + const char *GetName() { return osName; } + + int GetLayerCount() { return apoLayers.size(); } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); + + // The following is only used by OGRDWGLayer + + int InlineBlocks() { return bInlineBlocks; } + void AddStandardFields( OGRFeatureDefn *poDef ); + + // Implemented in ogrdxf_blockmap.cpp + void ReadBlocksSection(); + OGRGeometry *SimplifyBlockGeometry( OGRGeometryCollection * ); + DWGBlockDefinition *LookupBlock( const char *pszName ); + std::map &GetBlockMap() { return oBlockMap; } + + // Layer and other Table Handling (ogrdatasource.cpp) + void ReadLayerDefinitions(); + void ReadLineTypeDefinitions(); + const char *LookupLayerProperty( const char *pszLayer, + const char *pszProperty ); + const char *LookupLineType( const char *pszName ); + + // Header variables. + void ReadHeaderSection(); + const char *GetVariable(const char *pszName, + const char *pszDefault=NULL ); + + const char *GetEncoding() { return osEncoding; } +}; + +/************************************************************************/ +/* OGRDWGServices */ +/* */ +/* Services implementation for OGR. Eventually we should */ +/* override the ExSystemServices IO to use VSI*L. */ +/************************************************************************/ +class OGRDWGServices : public ExSystemServices, public ExHostAppServices +{ +protected: + ODRX_USING_HEAP_OPERATORS(ExSystemServices); +}; + +/************************************************************************/ +/* OGRDWGDriver */ +/************************************************************************/ + +class OGRDWGDriver : public OGRSFDriver +{ + int bInitialized; + void Initialize(); + + OdStaticRxObject oServices; + + static void ErrorHandler( OdResult oRes ); + + public: + OGRDWGDriver(); + ~OGRDWGDriver(); + + OGRDWGServices *GetServices() { return &oServices; } + + const char *GetName(); + OGRDataSource *Open( const char *, int ); + int TestCapability( const char * ); +}; + + +#endif /* ndef _OGR_DWG_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwg_blockmap.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwg_blockmap.cpp new file mode 100644 index 000000000..0cebe2343 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwg_blockmap.cpp @@ -0,0 +1,173 @@ +/****************************************************************************** + * $Id: ogrdwg_blockmap.cpp 22011 2011-03-22 20:13:38Z warmerdam $ + * + * Project: DWG Translator + * Purpose: Implements BlockMap reading and management portion of + * OGRDWGDataSource class + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dwg.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" + +CPL_CVSID("$Id: ogrdwg_blockmap.cpp 22011 2011-03-22 20:13:38Z warmerdam $"); + +/************************************************************************/ +/* ReadBlockSection() */ +/************************************************************************/ + +void OGRDWGDataSource::ReadBlocksSection() + +{ + OGRDWGLayer *poReaderLayer = (OGRDWGLayer *) GetLayerByName( "Entities" ); + int bMergeBlockGeometries = CSLTestBoolean( + CPLGetConfigOption( "DWG_MERGE_BLOCK_GEOMETRIES", "TRUE" ) ); + +/* -------------------------------------------------------------------- */ +/* Loop over all the block tables, skipping *Model_Space which */ +/* we assume is primary entities. */ +/* -------------------------------------------------------------------- */ + OdDbBlockTableRecordPtr poModelSpace, poBlock; + OdDbBlockTablePtr pTable = GetDB()->getBlockTableId().safeOpenObject(); + OdDbSymbolTableIteratorPtr pBlkIter = pTable->newIterator(); + + for (pBlkIter->start(); ! pBlkIter->done(); pBlkIter->step()) + { + poBlock = pBlkIter->getRecordId().safeOpenObject(); + CPLString osBlockName = (const char *) poBlock->getName(); + + if( EQUAL(osBlockName,"*Model_Space") ) + { + poModelSpace = poBlock; + continue; + } + + poReaderLayer->SetBlockTable( poBlock ); + + // Now we will process entities till we run out. + // We aggregate the geometries of the features into a multi-geometry, + // but throw away other stuff attached to the features. + + OGRFeature *poFeature; + OGRGeometryCollection *poColl = new OGRGeometryCollection(); + std::vector apoFeatures; + + while( (poFeature = poReaderLayer->GetNextUnfilteredFeature()) != NULL ) + { + if( (poFeature->GetStyleString() != NULL + && strstr(poFeature->GetStyleString(),"LABEL") != NULL) + || !bMergeBlockGeometries ) + { + apoFeatures.push_back( poFeature ); + } + else + { + poColl->addGeometryDirectly( poFeature->StealGeometry() ); + delete poFeature; + } + } + + if( poColl->getNumGeometries() == 0 ) + delete poColl; + else + oBlockMap[osBlockName].poGeometry = SimplifyBlockGeometry(poColl); + + if( apoFeatures.size() > 0 ) + oBlockMap[osBlockName].apoFeatures = apoFeatures; + } + + CPLDebug( "DWG", "Read %d blocks with meaningful geometry.", + (int) oBlockMap.size() ); + + poReaderLayer->SetBlockTable( poModelSpace ); +} + +/************************************************************************/ +/* SimplifyBlockGeometry() */ +/************************************************************************/ + +OGRGeometry *OGRDWGDataSource::SimplifyBlockGeometry( + OGRGeometryCollection *poCollection ) + +{ +/* -------------------------------------------------------------------- */ +/* If there is only one geometry in the collection, just return */ +/* it. */ +/* -------------------------------------------------------------------- */ + if( poCollection->getNumGeometries() == 1 ) + { + OGRGeometry *poReturn = poCollection->getGeometryRef(0); + poCollection->removeGeometry(0,FALSE); + delete poCollection; + return poReturn; + } + +/* -------------------------------------------------------------------- */ +/* Eventually we likely ought to have logic to convert to */ +/* polygon, multipolygon, multilinestring or multipoint but */ +/* I'll put that off till it would be meaningful. */ +/* -------------------------------------------------------------------- */ + + return poCollection; +} + +/************************************************************************/ +/* LookupBlock() */ +/* */ +/* Find the geometry collection corresponding to a name if it */ +/* exists. Note that the returned geometry pointer is to a */ +/* geometry that continues to be owned by the datasource. It */ +/* should be cloned for use. */ +/************************************************************************/ + +DWGBlockDefinition *OGRDWGDataSource::LookupBlock( const char *pszName ) + +{ + CPLString osName = pszName; + + if( oBlockMap.count( osName ) == 0 ) + return NULL; + else + return &(oBlockMap[osName]); +} + +/************************************************************************/ +/* ~DWGBlockDefinition() */ +/* */ +/* Safe cleanup of a block definition. */ +/************************************************************************/ + +DWGBlockDefinition::~DWGBlockDefinition() + +{ + delete poGeometry; + + while( !apoFeatures.empty() ) + { + delete apoFeatures.back(); + apoFeatures.pop_back(); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwg_dimension.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwg_dimension.cpp new file mode 100644 index 000000000..eec257646 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwg_dimension.cpp @@ -0,0 +1,383 @@ +/****************************************************************************** + * $Id: ogrdxf_dimension.cpp 19643 2010-05-08 21:56:18Z rouault $ + * + * Project: DWG Translator + * Purpose: Implements translation support for DIMENSION elements as a part + * of the OGRDWGLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dwg.h" +#include "cpl_conv.h" + +#include "DbDimension.h" +#include "DbRotatedDimension.h" +#include "DbAlignedDimension.h" + +CPL_CVSID("$Id: ogrdxf_dimension.cpp 19643 2010-05-08 21:56:18Z rouault $"); + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* TranslateDIMENSION() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::TranslateDIMENSION( OdDbEntityPtr poEntity ) + +{ + OdDbDimensionPtr poDim = OdDbDimension::cast( poEntity ); + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + double dfHeight = CPLAtof(poDS->GetVariable("$DIMTXT", "2.5")); + OdGePoint3d oTextPos, oTarget1, oTarget2, oArrow1; + CPLString osText; + + TranslateGenericProperties( poFeature, poEntity ); + +/* -------------------------------------------------------------------- */ +/* Generic Dimension stuff. */ +/* -------------------------------------------------------------------- */ + osText = (const char *) poDim->dimensionText(); + + oTextPos = poDim->textPosition(); + +/* -------------------------------------------------------------------- */ +/* Specific based on the subtype. */ +/* -------------------------------------------------------------------- */ + OdRxClass *poClass = poEntity->isA(); + const OdString osName = poClass->name(); + const char *pszEntityClassName = (const char *) osName; + + if( EQUAL(pszEntityClassName,"AcDbRotatedDimension") ) + { + OdDbRotatedDimensionPtr poRDim = OdDbDimension::cast( poEntity ); + + oTarget2 = poRDim->xLine1Point(); + oTarget1 = poRDim->xLine2Point(); + oArrow1 = poRDim->dimLinePoint(); + } + + else if( EQUAL(pszEntityClassName,"AcDbAlignedDimension") ) + { + OdDbAlignedDimensionPtr poADim = OdDbDimension::cast( poEntity ); + + oTarget2 = poADim->xLine1Point(); + oTarget1 = poADim->xLine2Point(); + oArrow1 = poADim->dimLinePoint(); + } + +/************************************************************************* + + DIMENSION geometry layout + + (11,21)(text center point) + | DimText | +(10,20) X<--------------------------------->X (Arrow2 - computed) +(Arrow1)| | + | | + | X (13,23) (Target2) + | + X (14,24) (Target1) + + +Given: + Locations Arrow1, Target1, and Target2 we need to compute Arrow2. + +Steps: + 1) Compute direction vector from Target1 to Arrow1 (Vec1). + 2) Compute direction vector for arrow as perpendicular to Vec1 (call Vec2). + 3) Compute Arrow2 location as intersection between line defined by + Vec2 and Arrow1 and line defined by Target2 and direction Vec1 (call Arrow2) + +Then we can draw lines for the various components. + +Note that Vec1 and Vec2 may be horizontal, vertical or on an angle but +the approach is as above in all these cases. + +*************************************************************************/ + +/* -------------------------------------------------------------------- */ +/* Step 1, compute direction vector between Target1 and Arrow1. */ +/* -------------------------------------------------------------------- */ + double dfVec1X, dfVec1Y; + + dfVec1X = (oArrow1.x - oTarget1.x); + dfVec1Y = (oArrow1.y - oTarget1.y); + +/* -------------------------------------------------------------------- */ +/* Step 2, compute the direction vector from Arrow1 to Arrow2 */ +/* as a perpendicluar to Vec1. */ +/* -------------------------------------------------------------------- */ + double dfVec2X, dfVec2Y; + + dfVec2X = dfVec1Y; + dfVec2Y = -dfVec1X; + +/* -------------------------------------------------------------------- */ +/* Step 3, compute intersection of line from target2 along */ +/* direction vector 1, with the line through Arrow1 and */ +/* direction vector 2. */ +/* -------------------------------------------------------------------- */ + double dfL1M, dfL1B, dfL2M, dfL2B; + double dfArrowX2, dfArrowY2; + + // special case if vec1 is vertical. + if( dfVec1X == 0.0 ) + { + dfArrowX2 = oTarget2.x; + dfArrowY2 = oArrow1.y; + } + + // special case if vec2 is horizontal. + else if( dfVec1Y == 0.0 ) + { + dfArrowX2 = oArrow1.x; + dfArrowY2 = oTarget2.y; + } + + else // General case for diagonal vectors. + { + // first convert vec1 + target2 into y = mx + b format: call this L1 + + dfL1M = dfVec1Y / dfVec1X; + dfL1B = oTarget2.y - dfL1M * oTarget2.x; + + // convert vec2 + Arrow1 into y = mx + b format, call this L2 + + dfL2M = dfVec2Y / dfVec2X; + dfL2B = oArrow1.y - dfL2M * oArrow1.x; + + // Compute intersection x = (b2-b1) / (m1-m2) + + dfArrowX2 = (dfL2B - dfL1B) / (dfL1M-dfL2M); + dfArrowY2 = dfL2M * dfArrowX2 + dfL2B; + } + +/* -------------------------------------------------------------------- */ +/* Compute the text angle. */ +/* -------------------------------------------------------------------- */ + double dfAngle = 0.0; + + dfAngle = atan2(dfVec2Y,dfVec2X) * 180.0 / PI; + +/* -------------------------------------------------------------------- */ +/* Rescale the direction vectors so we can use them in */ +/* constructing arrowheads. We want them to be about 3% of the */ +/* length of line on which the arrows will be drawn. */ +/* -------------------------------------------------------------------- */ +#define VECTOR_LEN(x,y) sqrt( (x)*(x) + (y)*(y) ) +#define POINT_DIST(x1,y1,x2,y2) VECTOR_LEN((x2-x1),(y2-y1)) + + double dfBaselineLength = POINT_DIST(oArrow1.x,oArrow1.y, + dfArrowX2,dfArrowY2); + double dfTargetLength = dfBaselineLength * 0.03; + double dfScaleFactor; + + // recompute vector 2 to ensure the direction is regular + dfVec2X = (dfArrowX2 - oArrow1.x); + dfVec2Y = (dfArrowY2 - oArrow1.y); + + // vector 1 + dfScaleFactor = dfTargetLength / VECTOR_LEN(dfVec1X,dfVec1Y); + dfVec1X *= dfScaleFactor; + dfVec1Y *= dfScaleFactor; + + // vector 2 + dfScaleFactor = dfTargetLength / VECTOR_LEN(dfVec2X,dfVec2Y); + dfVec2X *= dfScaleFactor; + dfVec2Y *= dfScaleFactor; + +/* -------------------------------------------------------------------- */ +/* Create geometries for the different components of the */ +/* dimension object. */ +/* -------------------------------------------------------------------- */ + OGRMultiLineString *poMLS = new OGRMultiLineString(); + OGRLineString oLine; + + // main arrow line between Arrow1 and Arrow2 + oLine.setPoint( 0, oArrow1.x, oArrow1.y ); + oLine.setPoint( 1, dfArrowX2, dfArrowY2 ); + poMLS->addGeometry( &oLine ); + + // dimension line from Target1 to Arrow1 with a small extension. + oLine.setPoint( 0, oTarget1.x, oTarget1.y ); + oLine.setPoint( 1, oArrow1.x + dfVec1X, oArrow1.y + dfVec1Y ); + poMLS->addGeometry( &oLine ); + + // dimension line from Target2 to Arrow2 with a small extension. + oLine.setPoint( 0, oTarget2.x, oTarget2.y ); + oLine.setPoint( 1, dfArrowX2 + dfVec1X, dfArrowY2 + dfVec1Y ); + poMLS->addGeometry( &oLine ); + + // add arrow1 arrow head. + + oLine.setPoint( 0, oArrow1.x, oArrow1.y ); + oLine.setPoint( 1, + oArrow1.x + dfVec2X*3 + dfVec1X, + oArrow1.y + dfVec2Y*3 + dfVec1Y ); + poMLS->addGeometry( &oLine ); + + oLine.setPoint( 0, oArrow1.x, oArrow1.y ); + oLine.setPoint( 1, + oArrow1.x + dfVec2X*3 - dfVec1X, + oArrow1.y + dfVec2Y*3 - dfVec1Y ); + poMLS->addGeometry( &oLine ); + + // add arrow2 arrow head. + + oLine.setPoint( 0, dfArrowX2, dfArrowY2 ); + oLine.setPoint( 1, + dfArrowX2 - dfVec2X*3 + dfVec1X, + dfArrowY2 - dfVec2Y*3 + dfVec1Y ); + poMLS->addGeometry( &oLine ); + + oLine.setPoint( 0, dfArrowX2, dfArrowY2 ); + oLine.setPoint( 1, + dfArrowX2 - dfVec2X*3 - dfVec1X, + dfArrowY2 - dfVec2Y*3 - dfVec1Y ); + poMLS->addGeometry( &oLine ); + + poFeature->SetGeometryDirectly( poMLS ); + + PrepareLineStyle( poFeature ); + +/* -------------------------------------------------------------------- */ +/* Is the layer disabled/hidden/frozen/off? */ +/* -------------------------------------------------------------------- */ + CPLString osLayer = poFeature->GetFieldAsString("Layer"); + + int bHidden = + EQUAL(poDS->LookupLayerProperty( osLayer, "Hidden" ), "1"); + +/* -------------------------------------------------------------------- */ +/* Work out the color for this feature. */ +/* -------------------------------------------------------------------- */ + int nColor = 256; + + if( oStyleProperties.count("Color") > 0 ) + nColor = atoi(oStyleProperties["Color"]); + + // Use layer color? + if( nColor < 1 || nColor > 255 ) + { + const char *pszValue = poDS->LookupLayerProperty( osLayer, "Color" ); + if( pszValue != NULL ) + nColor = atoi(pszValue); + } + + if( nColor < 1 || nColor > 255 ) + nColor = 8; + +/* -------------------------------------------------------------------- */ +/* Prepare a new feature to serve as the dimension text label */ +/* feature. We will push it onto the layer as a pending */ +/* feature for the next feature read. */ +/* -------------------------------------------------------------------- */ + + // a single space suppresses labelling. + if( osText == " " ) + return poFeature; + + OGRFeature *poLabelFeature = poFeature->Clone(); + + poLabelFeature->SetGeometryDirectly( new OGRPoint( oTextPos.x, oTextPos.y ) ); + + // Do we need to compute the dimension value? + if( osText.size() == 0 ) + { + FormatDimension( osText, POINT_DIST( oArrow1.x, oArrow1.y, + dfArrowX2, dfArrowY2 ) ); + } + + CPLString osStyle; + char szBuffer[64]; + char* pszComma; + + osStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",p:5",osText.c_str()); + + if( dfAngle != 0.0 ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle); + pszComma = strchr(szBuffer, ','); + if (pszComma) + *pszComma = '.'; + osStyle += CPLString().Printf(",a:%s", szBuffer); + } + + if( dfHeight != 0.0 ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight); + pszComma = strchr(szBuffer, ','); + if (pszComma) + *pszComma = '.'; + osStyle += CPLString().Printf(",s:%sg", szBuffer); + } + + const unsigned char *pabyDWGColors = ACGetColorTable(); + + snprintf( szBuffer, sizeof(szBuffer), ",c:#%02x%02x%02x", + pabyDWGColors[nColor*3+0], + pabyDWGColors[nColor*3+1], + pabyDWGColors[nColor*3+2] ); + osStyle += szBuffer; + + if( bHidden ) + osStyle += "00"; + + osStyle += ")"; + + poLabelFeature->SetStyleString( osStyle ); + + apoPendingFeatures.push( poLabelFeature ); + + return poFeature; +} + +/************************************************************************/ +/* FormatDimension() */ +/* */ +/* Format a dimension number according to the current files */ +/* formatting conventions. */ +/************************************************************************/ + +void OGRDWGLayer::FormatDimension( CPLString &osText, double dfValue ) + +{ + int nPrecision = atoi(poDS->GetVariable("$LUPREC","4")); + char szFormat[32]; + char szBuffer[64]; + + // we could do a significantly more precise formatting if we want + // to spend the effort. See QCAD's rs_dimlinear.cpp and related files + // for example. + + sprintf(szFormat, "%%.%df", nPrecision ); + CPLsnprintf(szBuffer, sizeof(szBuffer), szFormat, dfValue); + char* pszComma = strchr(szBuffer, ','); + if (pszComma) + *pszComma = '.'; + osText = szBuffer; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwg_hatch.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwg_hatch.cpp new file mode 100644 index 000000000..5ca985364 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwg_hatch.cpp @@ -0,0 +1,257 @@ +/****************************************************************************** + * $Id: ogrdwg_hatch.cpp 26672 2013-11-28 13:10:36Z rouault $ + * + * Project: DWG Translator + * Purpose: Implements translation support for HATCH elements as part + * of the OGRDWGLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dwg.h" +#include "cpl_conv.h" +#include "ogr_api.h" + +#include "DbHatch.h" + +#include "ogrdxf_polyline_smooth.h" + +#include "Ge/GePoint2dArray.h" +#include "Ge/GeCurve2d.h" +#include "Ge/GeCircArc2d.h" +#include "Ge/GeEllipArc2d.h" + +CPL_CVSID("$Id: ogrdwg_hatch.cpp 26672 2013-11-28 13:10:36Z rouault $"); + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +static OGRErr DWGCollectBoundaryLoop( OdDbHatchPtr poHatch, int iLoop, + OGRGeometryCollection *poGC ); + +/************************************************************************/ +/* TranslateHATCH() */ +/* */ +/* We mostly just try to convert hatch objects as polygons or */ +/* multipolygons representing the hatched area. It is hard to */ +/* preserve the actual details of the hatching. */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::TranslateHATCH( OdDbEntityPtr poEntity ) + +{ + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + OdDbHatchPtr poHatch = OdDbHatch::cast( poEntity ); + OGRGeometryCollection oGC; + + TranslateGenericProperties( poFeature, poEntity ); + + poFeature->SetField( "Text", + (const char *) poHatch->patternName() ); + +/* -------------------------------------------------------------------- */ +/* Collect the loops. */ +/* -------------------------------------------------------------------- */ + for( int i = 0; i < poHatch->numLoops(); i++ ) + { + DWGCollectBoundaryLoop( poHatch, i, &oGC ); + } + +/* -------------------------------------------------------------------- */ +/* Try to turn the set of lines into something useful. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr; + + OGRGeometryH hFinalGeom = + OGRBuildPolygonFromEdges( (OGRGeometryH) &oGC, + TRUE, TRUE, 0.0000001, &eErr ); + + poFeature->SetGeometryDirectly( (OGRGeometry *) hFinalGeom ); + +/* -------------------------------------------------------------------- */ +/* Work out the color for this feature. For now we just assume */ +/* solid fill. We cannot trivially translate the various sorts */ +/* of hatching. */ +/* -------------------------------------------------------------------- */ + CPLString osLayer = poFeature->GetFieldAsString("Layer"); + + int nColor = 256; + + if( oStyleProperties.count("Color") > 0 ) + nColor = atoi(oStyleProperties["Color"]); + + // Use layer color? + if( nColor < 1 || nColor > 255 ) + { + const char *pszValue = poDS->LookupLayerProperty( osLayer, "Color" ); + if( pszValue != NULL ) + nColor = atoi(pszValue); + } + +/* -------------------------------------------------------------------- */ +/* Setup the style string. */ +/* -------------------------------------------------------------------- */ + if( nColor >= 1 && nColor <= 255 ) + { + CPLString osStyle; + const unsigned char *pabyDWGColors = ACGetColorTable(); + + osStyle.Printf( "BRUSH(fc:#%02x%02x%02x)", + pabyDWGColors[nColor*3+0], + pabyDWGColors[nColor*3+1], + pabyDWGColors[nColor*3+2] ); + + poFeature->SetStyleString( osStyle ); + } + + return poFeature; +} + +/************************************************************************/ +/* CollectBoundaryLoop() */ +/************************************************************************/ + +static OGRErr DWGCollectBoundaryLoop( OdDbHatchPtr poHatch, int iLoop, + OGRGeometryCollection *poGC ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Handle simple polyline loops. */ +/* -------------------------------------------------------------------- */ + if( poHatch->loopTypeAt( iLoop ) & OdDbHatch::kPolyline ) + { + DXFSmoothPolyline oSmoothPolyline; + OdGePoint2dArray vertices; + OdGeDoubleArray bulges; + + poHatch->getLoopAt (iLoop, vertices, bulges); + + for (i = 0; i < (int) vertices.size(); i++) + { + if( i >= (int) bulges.size() ) + oSmoothPolyline.AddPoint( vertices[i].x, vertices[i].y, 0.0, + 0.0 ); + else + oSmoothPolyline.AddPoint( vertices[i].x, vertices[i].y, 0.0, + bulges[i] ); + } + + oSmoothPolyline.Close(); + + OGRLineString *poLS = (OGRLineString *) oSmoothPolyline.Tesselate(); + poGC->addGeometryDirectly( poLS ); + + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Handle an edges array. */ +/* -------------------------------------------------------------------- */ + EdgeArray oEdges; + poHatch->getLoopAt( iLoop, oEdges ); + + for( i = 0; i < (int) oEdges.size(); i++ ) + { + OdGeCurve2d* poEdge = oEdges[i]; + + if( poEdge->type() == OdGe::kLineSeg2d ) + { + OGRLineString *poLS = new OGRLineString(); + OdGePoint2d oStart = poEdge->evalPoint(0.0); + OdGePoint2d oEnd = poEdge->evalPoint(1.0); + + poLS->addPoint( oStart.x, oStart.y ); + poLS->addPoint( oEnd.x, oEnd.y ); + poGC->addGeometryDirectly( poLS ); + } + else if( poEdge->type() == OdGe::kCircArc2d ) + { + OdGeCircArc2d* poCircArc = (OdGeCircArc2d*) poEdge; + OdGePoint2d oCenter = poCircArc->center(); + double dfStartAngle = poCircArc->startAng() * 180 / PI; + double dfEndAngle = poCircArc->endAng() * 180 / PI; + + if( !poCircArc->isClockWise() ) + { + dfStartAngle *= -1; + dfEndAngle *= -1; + } + else if( dfStartAngle > dfEndAngle ) + { + dfEndAngle += 360.0; + } + + OGRLineString *poLS = (OGRLineString *) + OGRGeometryFactory::approximateArcAngles( + oCenter.x, oCenter.y, 0.0, + poCircArc->radius(), poCircArc->radius(), 0.0, + dfStartAngle, dfEndAngle, 0.0 ); + + poGC->addGeometryDirectly( poLS ); + } + else if( poEdge->type() == OdGe::kEllipArc2d ) + { + OdGeEllipArc2d* poArc = (OdGeEllipArc2d*) poEdge; + OdGePoint2d oCenter = poArc->center(); + double dfRatio = poArc->minorRadius() / poArc->majorRadius(); + OdGeVector2d oMajorAxis = poArc->majorAxis(); + double dfRotation; + double dfStartAng, dfEndAng; + + dfRotation = -1 * atan2( oMajorAxis.y, oMajorAxis.x ) * 180 / PI; + + dfStartAng = poArc->startAng()*180/PI; + dfEndAng = poArc->endAng()*180/PI; + + if( !poArc->isClockWise() ) + { + dfStartAng *= -1; + dfEndAng *= -1; + } + else if( dfStartAng > dfEndAng ) + { + dfEndAng += 360.0; + } + + OGRLineString *poLS = (OGRLineString *) + OGRGeometryFactory::approximateArcAngles( + oCenter.x, oCenter.y, 0.0, + poArc->majorRadius(), poArc->minorRadius(), dfRotation, + OGRDWGLayer::AngleCorrect(dfStartAng,dfRatio), + OGRDWGLayer::AngleCorrect(dfEndAng,dfRatio), + 0.0 ); + poGC->addGeometryDirectly( poLS ); + } + else + CPLDebug( "DWG", "Unsupported edge type (%d) in hatch loop.", + (int) poEdge->type() ); + + //case OdGe::kNurbCurve2d : dumpNurbCurveEdge(indent + 1, pEdge); + } + + return OGRERR_NONE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwgblockslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwgblockslayer.cpp new file mode 100644 index 000000000..89117320c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwgblockslayer.cpp @@ -0,0 +1,189 @@ +/****************************************************************************** + * $Id: ogrdwglayer.cpp 19643 2010-05-08 21:56:18Z rouault $ + * + * Project: DWG Translator + * Purpose: Implements OGRDWGBlocksLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dwg.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrdwglayer.cpp 19643 2010-05-08 21:56:18Z rouault $"); + +/************************************************************************/ +/* OGRDWGBlocksLayer() */ +/************************************************************************/ + +OGRDWGBlocksLayer::OGRDWGBlocksLayer( OGRDWGDataSource *poDS ) + +{ + this->poDS = poDS; + + ResetReading(); + + poFeatureDefn = new OGRFeatureDefn( "blocks" ); + poFeatureDefn->Reference(); + + poDS->AddStandardFields( poFeatureDefn ); +} + +/************************************************************************/ +/* ~OGRDWGBlocksLayer() */ +/************************************************************************/ + +OGRDWGBlocksLayer::~OGRDWGBlocksLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "DWG", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poFeatureDefn ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRDWGBlocksLayer::ResetReading() + +{ + iNextFID = 0; + iNextSubFeature = 0; + oIt = poDS->GetBlockMap().begin(); +} + +/************************************************************************/ +/* GetNextUnfilteredFeature() */ +/************************************************************************/ + +OGRFeature *OGRDWGBlocksLayer::GetNextUnfilteredFeature() + +{ + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* Are we out of features? */ +/* -------------------------------------------------------------------- */ + if( oIt == poDS->GetBlockMap().end() ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Are we done reading the current blocks features? */ +/* -------------------------------------------------------------------- */ + DWGBlockDefinition *psBlock = &(oIt->second); + unsigned int nSubFeatureCount = psBlock->apoFeatures.size(); + + if( psBlock->poGeometry != NULL ) + nSubFeatureCount++; + + if( iNextSubFeature >= nSubFeatureCount ) + { + oIt++; + + iNextSubFeature = 0; + + if( oIt == poDS->GetBlockMap().end() ) + return NULL; + + psBlock = &(oIt->second); + } + +/* -------------------------------------------------------------------- */ +/* Is this a geometry based block? */ +/* -------------------------------------------------------------------- */ + if( psBlock->poGeometry != NULL + && iNextSubFeature == psBlock->apoFeatures.size() ) + { + poFeature = new OGRFeature( poFeatureDefn ); + poFeature->SetGeometry( psBlock->poGeometry ); + iNextSubFeature++; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise duplicate the next sub-feature. */ +/* -------------------------------------------------------------------- */ + else + { + poFeature = new OGRFeature( poFeatureDefn ); + poFeature->SetFrom( psBlock->apoFeatures[iNextSubFeature] ); + iNextSubFeature++; + } + +/* -------------------------------------------------------------------- */ +/* Set FID and block name. */ +/* -------------------------------------------------------------------- */ + poFeature->SetFID( iNextFID++ ); + + poFeature->SetField( "BlockName", oIt->first.c_str() ); + + m_nFeaturesRead++; + + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRDWGBlocksLayer::GetNextFeature() + +{ + while( TRUE ) + { + OGRFeature *poFeature = GetNextUnfilteredFeature(); + + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature ) ) ) + { + return poFeature; + } + + delete poFeature; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDWGBlocksLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + else + return FALSE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwgdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwgdatasource.cpp new file mode 100644 index 000000000..5ef139e8c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwgdatasource.cpp @@ -0,0 +1,369 @@ +/****************************************************************************** + * $Id: ogrdxfdatasource.cpp 22009 2011-03-22 20:01:34Z warmerdam $ + * + * Project: DWG Translator + * Purpose: Implements OGRDWGDataSource class + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dwg.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +#include "DbSymbolTable.h" +#include "DbLayerTable.h" +#include "DbLayerTableRecord.h" +#include "DbLinetypeTable.h" +#include "DbLinetypeTableRecord.h" + +CPL_CVSID("$Id: ogrdxfdatasource.cpp 22009 2011-03-22 20:01:34Z warmerdam $"); + +/************************************************************************/ +/* OGRDWGDataSource() */ +/************************************************************************/ + +OGRDWGDataSource::OGRDWGDataSource() + +{ + poDb = NULL; +} + +/************************************************************************/ +/* ~OGRDWGDataSource() */ +/************************************************************************/ + +OGRDWGDataSource::~OGRDWGDataSource() + +{ +/* -------------------------------------------------------------------- */ +/* Destroy layers. */ +/* -------------------------------------------------------------------- */ + while( apoLayers.size() > 0 ) + { + delete apoLayers.back(); + apoLayers.pop_back(); + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDWGDataSource::TestCapability( const char * pszCap ) + +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + + +OGRLayer *OGRDWGDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= (int) apoLayers.size() ) + return NULL; + else + return apoLayers[iLayer]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRDWGDataSource::Open( OGRDWGServices *poServices, + const char * pszFilename, int bHeaderOnly ) + +{ + if( !EQUAL(CPLGetExtension(pszFilename),"dwg") ) + return FALSE; + + this->poServices = poServices; + + osEncoding = CPL_ENC_ISO8859_1; + + osName = pszFilename; + + bInlineBlocks = CSLTestBoolean( + CPLGetConfigOption( "DWG_INLINE_BLOCKS", "TRUE" ) ); + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + try + { + OdString f(pszFilename); + poDb = poServices->readFile(f.c_str(), true, false, Oda::kShareDenyNo); + } + catch( OdError& e ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", + (const char *) poServices->getErrorDescription(e.code()) ); + } + catch( ... ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DWG readFile(%s) failed with generic exception.", + pszFilename ); + } + + if( poDb.isNull() ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Process the header, picking up a few useful pieces of */ +/* information. */ +/* -------------------------------------------------------------------- */ + ReadHeaderSection(); + ReadLineTypeDefinitions(); + ReadLayerDefinitions(); + +/* -------------------------------------------------------------------- */ +/* Create a blocks layer if we are not in inlining mode. */ +/* -------------------------------------------------------------------- */ + if( !bInlineBlocks ) + apoLayers.push_back( new OGRDWGBlocksLayer( this ) ); + +/* -------------------------------------------------------------------- */ +/* Create out layer object - we will need it when interpreting */ +/* blocks. */ +/* -------------------------------------------------------------------- */ + apoLayers.push_back( new OGRDWGLayer( this ) ); + + ReadBlocksSection(); + + return TRUE; +} + +/************************************************************************/ +/* ReadLayerDefinitions() */ +/************************************************************************/ + +void OGRDWGDataSource::ReadLayerDefinitions() + +{ + OdDbLayerTablePtr poLT = poDb->getLayerTableId().safeOpenObject(); + OdDbSymbolTableIteratorPtr poIter = poLT->newIterator(); + + for (poIter->start(); !poIter->done(); poIter->step()) + { + CPLString osValue; + OdDbLayerTableRecordPtr poLD = poIter->getRecordId().safeOpenObject(); + std::map oLayerProperties; + + CPLString osLayerName = ACTextUnescape(poLD->getName(),GetEncoding()); + + oLayerProperties["Exists"] = "1"; + + OdDbLinetypeTableRecordPtr poLT = poLD->linetypeObjectId().safeOpenObject(); + oLayerProperties["Linetype"] = + ACTextUnescape(poLT->getName(),GetEncoding()); + + osValue.Printf( "%d", poLD->colorIndex() ); + oLayerProperties["Color"] = osValue; + + osValue.Printf( "%d", (int) poLD->lineWeight() ); + oLayerProperties["LineWeight"] = osValue; + + if( poLD->isFrozen() || poLD->isHidden() || poLD->isOff() ) + oLayerProperties["Hidden"] = "1"; + else + oLayerProperties["Hidden"] = "0"; + + oLayerTable[osLayerName] = oLayerProperties; + } + + CPLDebug( "DWG", "Read %d layer definitions.", + (int) oLayerTable.size() ); +} + +/************************************************************************/ +/* LookupLayerProperty() */ +/************************************************************************/ + +const char *OGRDWGDataSource::LookupLayerProperty( const char *pszLayer, + const char *pszProperty ) + +{ + if( pszLayer == NULL ) + return NULL; + + try { + return (oLayerTable[pszLayer])[pszProperty]; + } catch( ... ) { + return NULL; + } +} + +/************************************************************************/ +/* ReadLineTypeDefinitions() */ +/************************************************************************/ + +void OGRDWGDataSource::ReadLineTypeDefinitions() + +{ + OdDbLinetypeTablePtr poTable = poDb->getLinetypeTableId().safeOpenObject(); + OdDbSymbolTableIteratorPtr poIter = poTable->newIterator(); + + for (poIter->start(); !poIter->done(); poIter->step()) + { + CPLString osLineTypeName; + CPLString osLineTypeDef; + OdDbLinetypeTableRecordPtr poLT = poIter->getRecordId().safeOpenObject(); + + osLineTypeName = ACTextUnescape(poLT->getName(),GetEncoding()); + + if (poLT->numDashes()) + { + for (int i=0; i < poLT->numDashes(); i++) + { + if( i > 0 ) + osLineTypeDef += " "; + + CPLString osValue; + osValue.Printf( "%g", fabs(poLT->dashLengthAt(i)) ); + osLineTypeDef += osValue; + + osLineTypeDef += "g"; + } + + oLineTypeTable[osLineTypeName] = osLineTypeDef; + CPLDebug( "DWG", "LineType '%s' = '%s'", + osLineTypeName.c_str(), + osLineTypeDef.c_str() ); + } + } +} + +/************************************************************************/ +/* LookupLineType() */ +/************************************************************************/ + +const char *OGRDWGDataSource::LookupLineType( const char *pszName ) + +{ + if( oLineTypeTable.count(pszName) > 0 ) + return oLineTypeTable[pszName]; + else + return NULL; +} + +/************************************************************************/ +/* ReadHeaderSection() */ +/************************************************************************/ + +void OGRDWGDataSource::ReadHeaderSection() + +{ + // using: DWGCODEPAGE, DIMTXT, LUPREC + + CPLString osValue; + + osValue.Printf( "%d", poDb->getLUPREC() ); + oHeaderVariables["$LUPREC"] = osValue; + + osValue.Printf( "%g", poDb->dimtxt() ); + oHeaderVariables["$DIMTXT"] = osValue; + + CPLDebug( "DWG", "Read %d header variables.", + (int) oHeaderVariables.size() ); + +/* -------------------------------------------------------------------- */ +/* Decide on what CPLRecode() name to use for the files */ +/* encoding or allow the encoding to be overridden. */ +/* -------------------------------------------------------------------- */ + CPLString osCodepage = GetVariable( "$DWGCODEPAGE", "ANSI_1252" ); + + // not strictly accurate but works even without iconv. + if( osCodepage == "ANSI_1252" ) + osEncoding = CPL_ENC_ISO8859_1; + else if( EQUALN(osCodepage,"ANSI_",5) ) + { + osEncoding = "CP"; + osEncoding += osCodepage + 5; + } + else + { + // fallback to the default + osEncoding = CPL_ENC_ISO8859_1; + } + + if( CPLGetConfigOption( "DWG_ENCODING", NULL ) != NULL ) + osEncoding = CPLGetConfigOption( "DWG_ENCODING", NULL ); + + if( osEncoding != CPL_ENC_ISO8859_1 ) + CPLDebug( "DWG", "Treating DWG as encoding '%s', $DWGCODEPAGE='%s'", + osEncoding.c_str(), osCodepage.c_str() ); +} + +/************************************************************************/ +/* GetVariable() */ +/* */ +/* Fetch a variable that came from the HEADER section. */ +/************************************************************************/ + +const char *OGRDWGDataSource::GetVariable( const char *pszName, + const char *pszDefault ) + +{ + if( oHeaderVariables.count(pszName) == 0 ) + return pszDefault; + else + return oHeaderVariables[pszName]; +} + +/************************************************************************/ +/* AddStandardFields() */ +/************************************************************************/ + +void OGRDWGDataSource::AddStandardFields( OGRFeatureDefn *poFeatureDefn ) + +{ + OGRFieldDefn oLayerField( "Layer", OFTString ); + poFeatureDefn->AddFieldDefn( &oLayerField ); + + OGRFieldDefn oClassField( "SubClasses", OFTString ); + poFeatureDefn->AddFieldDefn( &oClassField ); + + OGRFieldDefn oExtendedField( "ExtendedEntity", OFTString ); + poFeatureDefn->AddFieldDefn( &oExtendedField ); + + OGRFieldDefn oLinetypeField( "Linetype", OFTString ); + poFeatureDefn->AddFieldDefn( &oLinetypeField ); + + OGRFieldDefn oEntityHandleField( "EntityHandle", OFTString ); + poFeatureDefn->AddFieldDefn( &oEntityHandleField ); + + OGRFieldDefn oTextField( "Text", OFTString ); + poFeatureDefn->AddFieldDefn( &oTextField ); + + if( !bInlineBlocks ) + { + OGRFieldDefn oTextField( "BlockName", OFTString ); + poFeatureDefn->AddFieldDefn( &oTextField ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwgdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwgdriver.cpp new file mode 100644 index 000000000..8650d8027 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwgdriver.cpp @@ -0,0 +1,157 @@ +/****************************************************************************** + * $Id: ogrdxfdriver.cpp 18535 2010-01-12 01:40:32Z warmerdam $ + * + * Project: DWG Translator + * Purpose: Implements OGRDWGDriver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dwg.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrdxfdriver.cpp 18535 2010-01-12 01:40:32Z warmerdam $"); + +CPL_C_START +void CPL_DLL RegisterOGRDWG(); +CPL_C_END + +/************************************************************************/ +/* OGRDWGDriver() */ +/************************************************************************/ + +OGRDWGDriver::OGRDWGDriver() + +{ + bInitialized = FALSE; +} + +/************************************************************************/ +/* ~OGRDWGDriver() */ +/************************************************************************/ + +OGRDWGDriver::~OGRDWGDriver() + +{ + if( bInitialized && !CSLTestBoolean( + CPLGetConfigOption("IN_GDAL_GLOBAL_DESTRUCTOR", "NO")) ) + { + bInitialized = FALSE; + odUninitialize(); + } +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void OGRDWGDriver::Initialize() + +{ + if( bInitialized ) + return; + + bInitialized = TRUE; + + OdGeContext::gErrorFunc = ErrorHandler; + + odInitialize(&oServices); + oServices.disableOutput( true ); + + /********************************************************************/ + /* Find the data file and and initialize the character mapper */ + /********************************************************************/ + OdString iniFile = oServices.findFile(OD_T("adinit.dat")); + if (!iniFile.isEmpty()) + OdCharMapper::initialize(iniFile); + +} + +/************************************************************************/ +/* ErrorHandler() */ +/************************************************************************/ + +void OGRDWGDriver::ErrorHandler( OdResult oResult ) + +{ + CPLError( CE_Failure, CPLE_AppDefined, + "GeError:%s", + (const char *) OdError(oResult).description().c_str() ); +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRDWGDriver::GetName() + +{ + return "DWG"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRDWGDriver::Open( const char * pszFilename, int bUpdate ) + +{ + Initialize(); + + OGRDWGDataSource *poDS = new OGRDWGDataSource(); + + if( !poDS->Open( &oServices, pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDWGDriver::TestCapability( const char * pszCap ) + +{ + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRDWG() */ +/************************************************************************/ + +void RegisterOGRDWG() + +{ + OGRSFDriver* poDriver = new OGRDWGDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "AutoCAD DWG" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "dwg" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_dwg.html" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( poDriver ); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwglayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwglayer.cpp new file mode 100644 index 000000000..00c374160 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dwg/ogrdwglayer.cpp @@ -0,0 +1,1462 @@ +/****************************************************************************** + * $Id: ogrdwglayer.cpp 22008 2011-03-22 19:45:20Z warmerdam $ + * + * Project: DWG Translator + * Purpose: Implements OGRDWGLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dwg.h" +#include "cpl_conv.h" + +#include "ogrdxf_polyline_smooth.h" + +#include "DbPolyline.h" +#include "Db2dPolyline.h" +#include "Db3dPolyline.h" +#include "Db3dPolylineVertex.h" +#include "DbLine.h" +#include "DbPoint.h" +#include "DbEllipse.h" +#include "DbArc.h" +#include "DbMText.h" +#include "DbText.h" +#include "DbCircle.h" +#include "DbSpline.h" +#include "DbBlockReference.h" +#include "DbAttribute.h" +#include "DbFiler.h" +#include "Ge/GeScale3d.h" + +CPL_CVSID("$Id: ogrdwglayer.cpp 22008 2011-03-22 19:45:20Z warmerdam $"); + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* OGRDWGLayer() */ +/************************************************************************/ + +OGRDWGLayer::OGRDWGLayer( OGRDWGDataSource *poDS ) + +{ + this->poDS = poDS; + + iNextFID = 0; + + poFeatureDefn = new OGRFeatureDefn( "entities" ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + poDS->AddStandardFields( poFeatureDefn ); + + if( !poDS->InlineBlocks() ) + { + OGRFieldDefn oScaleField( "BlockScale", OFTRealList ); + poFeatureDefn->AddFieldDefn( &oScaleField ); + + OGRFieldDefn oBlockAngleField( "BlockAngle", OFTReal ); + poFeatureDefn->AddFieldDefn( &oBlockAngleField ); + } + +/* -------------------------------------------------------------------- */ +/* Find the *Paper_Space block, which seems to contain all the */ +/* regular entities. */ +/* -------------------------------------------------------------------- */ + OdDbBlockTablePtr pTable = poDS->GetDB()->getBlockTableId().safeOpenObject(); + OdDbSymbolTableIteratorPtr pBlkIter = pTable->newIterator(); + + for (pBlkIter->start(); ! pBlkIter->done(); pBlkIter->step()) + { + poBlock = pBlkIter->getRecordId().safeOpenObject(); + + if( EQUAL(poBlock->getName(),"*Model_Space") ) + break; + else + poBlock = NULL; + } + + ResetReading(); +} + +/************************************************************************/ +/* ~OGRDWGLayer() */ +/************************************************************************/ + +OGRDWGLayer::~OGRDWGLayer() + +{ + ClearPendingFeatures(); + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "DWG", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poFeatureDefn ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* TextUnescape() */ +/************************************************************************/ + +CPLString OGRDWGLayer::TextUnescape( OdString oString ) + +{ + return ACTextUnescape( (const char *) oString, poDS->GetEncoding() ); +} + +/************************************************************************/ +/* SetBlockTable() */ +/* */ +/* Set what block table to read features from. This layer */ +/* object is used to read blocks features as well as generic */ +/* entities. */ +/************************************************************************/ + +void OGRDWGLayer::SetBlockTable( OdDbBlockTableRecordPtr poNewBlock ) + +{ + poBlock = poNewBlock; + + ResetReading(); +} + + +/************************************************************************/ +/* ClearPendingFeatures() */ +/************************************************************************/ + +void OGRDWGLayer::ClearPendingFeatures() + +{ + while( !apoPendingFeatures.empty() ) + { + delete apoPendingFeatures.front(); + apoPendingFeatures.pop(); + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRDWGLayer::ResetReading() + +{ + iNextFID = 0; + ClearPendingFeatures(); + + if( !poBlock.isNull() ) + poEntIter = poBlock->newIterator(); +} + +/************************************************************************/ +/* TranslateGenericProperties() */ +/* */ +/* Try and convert entity properties handled similarly for most */ +/* or all entity types */ +/************************************************************************/ + +void OGRDWGLayer::TranslateGenericProperties( OGRFeature *poFeature, + OdDbEntityPtr poEntity ) + +{ + poFeature->SetField( "Layer", TextUnescape(poEntity->layer()) ); + poFeature->SetField( "Linetype", TextUnescape(poEntity->layer()) ); + + CPLString osValue; + osValue.Printf( "%d", (int) poEntity->lineWeight() ); + oStyleProperties["LineWeight"] = osValue; + + OdDbHandle oHandle = poEntity->getDbHandle(); + poFeature->SetField( "EntityHandle", (const char *) oHandle.ascii() ); + + + if( poEntity->colorIndex() != 256 ) + { + osValue.Printf( "%d", poEntity->colorIndex() ); + oStyleProperties["Color"] = osValue.c_str(); + } + +/* -------------------------------------------------------------------- */ +/* Collect the subclasses. */ +/* -------------------------------------------------------------------- */ + CPLString osSubClasses; + OdRxClass *poClass = poEntity->isA(); + + while( poClass != NULL ) + { + if( osSubClasses.size() > 0 ) + osSubClasses = ":" + osSubClasses; + + osSubClasses = ((const char *) poClass->name()) + osSubClasses; + if( EQUAL(poClass->name(),"AcDbEntity") ) + break; + + poClass = poClass->myParent(); + } + + poFeature->SetField( "SubClasses", osSubClasses.c_str() ); + +/* -------------------------------------------------------------------- */ +/* Collect Xdata. */ +/* -------------------------------------------------------------------- */ + OdResBufPtr poResBufBase = poEntity->xData(); + OdResBuf *poResBuf = poResBufBase; + CPLString osFullXData; + + for ( ; poResBuf != NULL; poResBuf = poResBuf->next() ) + { + CPLString osXDataItem; + + switch (OdDxfCode::_getType(poResBuf->restype())) + { + case OdDxfCode::Name: + case OdDxfCode::String: + case OdDxfCode::LayerName: + osXDataItem = (const char *) poResBuf->getString(); + break; + + case OdDxfCode::Bool: + if( poResBuf->getBool() ) + osXDataItem = "true"; + else + osXDataItem = "false"; + break; + + case OdDxfCode::Integer8: + osXDataItem.Printf( "%d", (int) poResBuf->getInt8() ); + break; + + case OdDxfCode::Integer16: + osXDataItem.Printf( "%d", (int) poResBuf->getInt16() ); + break; + + case OdDxfCode::Integer32: + osXDataItem.Printf( "%d", (int) poResBuf->getInt32() ); + break; + + case OdDxfCode::Double: + case OdDxfCode::Angle: + osXDataItem.Printf( "%g", poResBuf->getDouble() ); + break; + + case OdDxfCode::Point: + { + OdGePoint3d oPoint = poResBuf->getPoint3d(); + osXDataItem.Printf( "(%g,%g,%g)", oPoint.x, oPoint.y, oPoint.z ); + } + break; + + case OdDxfCode::BinaryChunk: + { + OdBinaryData oBinData = poResBuf->getBinaryChunk(); + char *pszAsHex = CPLBinaryToHex( oBinData.size(), + (GByte*) oBinData.asArrayPtr() ); + osXDataItem = pszAsHex; + CPLFree( pszAsHex ); + } + break; + + case OdDxfCode::ObjectId: + case OdDxfCode::SoftPointerId: + case OdDxfCode::HardPointerId: + case OdDxfCode::SoftOwnershipId: + case OdDxfCode::HardOwnershipId: + case OdDxfCode::Handle: + osXDataItem = (const char *) poResBuf->getHandle().ascii(); + break; + + default: + break; + } + + if( osFullXData.size() > 0 ) + osFullXData += " "; + osFullXData += (const char *) osXDataItem; + } + + poFeature->SetField( "ExtendedEntity", osFullXData ); + + +#ifdef notdef + // OCS vector. + case 210: + oStyleProperties["210_N.dX"] = pszValue; + break; + + case 220: + oStyleProperties["220_N.dY"] = pszValue; + break; + + case 230: + oStyleProperties["230_N.dZ"] = pszValue; + break; + + + default: + break; + } +#endif +} + +/************************************************************************/ +/* PrepareLineStyle() */ +/************************************************************************/ + +void OGRDWGLayer::PrepareLineStyle( OGRFeature *poFeature ) + +{ + CPLString osLayer = poFeature->GetFieldAsString("Layer"); + +/* -------------------------------------------------------------------- */ +/* Is the layer disabled/hidden/frozen/off? */ +/* -------------------------------------------------------------------- */ + int bHidden = + EQUAL(poDS->LookupLayerProperty( osLayer, "Hidden" ), "1"); + +/* -------------------------------------------------------------------- */ +/* Work out the color for this feature. */ +/* -------------------------------------------------------------------- */ + int nColor = 256; + + if( oStyleProperties.count("Color") > 0 ) + nColor = atoi(oStyleProperties["Color"]); + + // Use layer color? + if( nColor < 1 || nColor > 255 ) + { + const char *pszValue = poDS->LookupLayerProperty( osLayer, "Color" ); + if( pszValue != NULL ) + nColor = atoi(pszValue); + } + + if( nColor < 1 || nColor > 255 ) + return; + +/* -------------------------------------------------------------------- */ +/* Get line weight if available. */ +/* -------------------------------------------------------------------- */ + double dfWeight = 0.0; + + if( oStyleProperties.count("LineWeight") > 0 ) + { + CPLString osWeight = oStyleProperties["LineWeight"]; + + if( osWeight == "-1" ) + osWeight = poDS->LookupLayerProperty(osLayer,"LineWeight"); + + dfWeight = CPLAtof(osWeight) / 100.0; + } + +/* -------------------------------------------------------------------- */ +/* Do we have a dash/dot line style? */ +/* -------------------------------------------------------------------- */ + const char *pszPattern = poDS->LookupLineType( + poFeature->GetFieldAsString("Linetype") ); + +/* -------------------------------------------------------------------- */ +/* Format the style string. */ +/* -------------------------------------------------------------------- */ + CPLString osStyle; + const unsigned char *pabyDWGColors = ACGetColorTable(); + + osStyle.Printf( "PEN(c:#%02x%02x%02x", + pabyDWGColors[nColor*3+0], + pabyDWGColors[nColor*3+1], + pabyDWGColors[nColor*3+2] ); + + if( bHidden ) + osStyle += "00"; + + if( dfWeight > 0.0 ) + { + char szBuffer[64]; + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.2g", dfWeight); + char* pszComma = strchr(szBuffer, ','); + if (pszComma) + *pszComma = '.'; + osStyle += CPLString().Printf( ",w:%sg", szBuffer ); + } + + if( pszPattern ) + { + osStyle += ",p:\""; + osStyle += pszPattern; + osStyle += "\""; + } + + osStyle += ")"; + + poFeature->SetStyleString( osStyle ); +} + +/************************************************************************/ +/* TranslateMTEXT() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::TranslateMTEXT( OdDbEntityPtr poEntity ) + +{ + OdDbMTextPtr poMTE = OdDbMText::cast( poEntity ); + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + TranslateGenericProperties( poFeature, poEntity ); + +/* -------------------------------------------------------------------- */ +/* Set the location. */ +/* -------------------------------------------------------------------- */ + OdGePoint3d oLocation = poMTE->location(); + + poFeature->SetGeometryDirectly( + new OGRPoint( oLocation.x, oLocation.y, oLocation.z ) ); + +/* -------------------------------------------------------------------- */ +/* Apply text after stripping off any extra terminating newline. */ +/* -------------------------------------------------------------------- */ + CPLString osText = TextUnescape( poMTE->contents() ); + + if( osText != "" && osText[osText.size()-1] == '\n' ) + osText.resize( osText.size() - 1 ); + + poFeature->SetField( "Text", osText ); + +/* -------------------------------------------------------------------- */ +/* We need to escape double quotes with backslashes before they */ +/* can be inserted in the style string. */ +/* -------------------------------------------------------------------- */ + if( strchr( osText, '"') != NULL ) + { + CPLString osEscaped; + size_t iC; + + for( iC = 0; iC < osText.size(); iC++ ) + { + if( osText[iC] == '"' ) + osEscaped += "\\\""; + else + osEscaped += osText[iC]; + } + osText = osEscaped; + } + +/* -------------------------------------------------------------------- */ +/* Work out the color for this feature. */ +/* -------------------------------------------------------------------- */ + int nColor = 256; + + if( oStyleProperties.count("Color") > 0 ) + nColor = atoi(oStyleProperties["Color"]); + + // Use layer color? + if( nColor < 1 || nColor > 255 ) + { + CPLString osLayer = poFeature->GetFieldAsString("Layer"); + const char *pszValue = poDS->LookupLayerProperty( osLayer, "Color" ); + if( pszValue != NULL ) + nColor = atoi(pszValue); + } + +/* -------------------------------------------------------------------- */ +/* Prepare style string. */ +/* -------------------------------------------------------------------- */ + double dfAngle = poMTE->rotation() * 180 / PI; + double dfHeight = poMTE->textHeight(); + int nAttachmentPoint = (int) poMTE->attachment(); + + CPLString osStyle; + char szBuffer[64]; + char* pszComma; + + osStyle.Printf("LABEL(f:\"Arial\",t:\"%s\"",osText.c_str()); + + if( dfAngle != 0.0 ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle); + pszComma = strchr(szBuffer, ','); + if (pszComma) + *pszComma = '.'; + osStyle += CPLString().Printf(",a:%s", szBuffer); + } + + if( dfHeight != 0.0 ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight); + pszComma = strchr(szBuffer, ','); + if (pszComma) + *pszComma = '.'; + osStyle += CPLString().Printf(",s:%sg", szBuffer); + } + + if( nAttachmentPoint >= 0 && nAttachmentPoint <= 9 ) + { + const static int anAttachmentMap[10] = + { -1, 7, 8, 9, 4, 5, 6, 1, 2, 3 }; + + osStyle += + CPLString().Printf(",p:%d", anAttachmentMap[nAttachmentPoint]); + } + + if( nColor > 0 && nColor < 256 ) + { + const unsigned char *pabyDWGColors = ACGetColorTable(); + osStyle += + CPLString().Printf( ",c:#%02x%02x%02x", + pabyDWGColors[nColor*3+0], + pabyDWGColors[nColor*3+1], + pabyDWGColors[nColor*3+2] ); + } + + osStyle += ")"; + + poFeature->SetStyleString( osStyle ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateTEXT() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::TranslateTEXT( OdDbEntityPtr poEntity ) + +{ + OdDbTextPtr poText = OdDbText::cast( poEntity ); + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + TranslateGenericProperties( poFeature, poEntity ); + +/* -------------------------------------------------------------------- */ +/* Set the location. */ +/* -------------------------------------------------------------------- */ + OdGePoint3d oLocation = poText->position(); + + poFeature->SetGeometryDirectly( + new OGRPoint( oLocation.x, oLocation.y, oLocation.z ) ); + +/* -------------------------------------------------------------------- */ +/* Apply text after stripping off any extra terminating newline. */ +/* -------------------------------------------------------------------- */ + CPLString osText = TextUnescape( poText->textString() ); + + if( osText != "" && osText[osText.size()-1] == '\n' ) + osText.resize( osText.size() - 1 ); + + poFeature->SetField( "Text", osText ); + +/* -------------------------------------------------------------------- */ +/* We need to escape double quotes with backslashes before they */ +/* can be inserted in the style string. */ +/* -------------------------------------------------------------------- */ + if( strchr( osText, '"') != NULL ) + { + CPLString osEscaped; + size_t iC; + + for( iC = 0; iC < osText.size(); iC++ ) + { + if( osText[iC] == '"' ) + osEscaped += "\\\""; + else + osEscaped += osText[iC]; + } + osText = osEscaped; + } + +/* -------------------------------------------------------------------- */ +/* Is the layer disabled/hidden/frozen/off? */ +/* -------------------------------------------------------------------- */ + CPLString osLayer = poFeature->GetFieldAsString("Layer"); + + int bHidden = + EQUAL(poDS->LookupLayerProperty( osLayer, "Hidden" ), "1"); + +/* -------------------------------------------------------------------- */ +/* Work out the color for this feature. */ +/* -------------------------------------------------------------------- */ + int nColor = 256; + + if( oStyleProperties.count("Color") > 0 ) + nColor = atoi(oStyleProperties["Color"]); + + // Use layer color? + if( nColor < 1 || nColor > 255 ) + { + const char *pszValue = poDS->LookupLayerProperty( osLayer, "Color" ); + if( pszValue != NULL ) + nColor = atoi(pszValue); + } + + if( nColor < 1 || nColor > 255 ) + nColor = 8; + +/* -------------------------------------------------------------------- */ +/* Prepare style string. */ +/* -------------------------------------------------------------------- */ + double dfAngle = poText->rotation() * 180 / PI; + double dfHeight = poText->height(); + + CPLString osStyle; + char szBuffer[64]; + char* pszComma; + + osStyle.Printf("LABEL(f:\"Arial\",t:\"%s\"",osText.c_str()); + + if( dfAngle != 0.0 ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle); + pszComma = strchr(szBuffer, ','); + if (pszComma) + *pszComma = '.'; + osStyle += CPLString().Printf(",a:%s", szBuffer); + } + + if( dfHeight != 0.0 ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight); + pszComma = strchr(szBuffer, ','); + if (pszComma) + *pszComma = '.'; + osStyle += CPLString().Printf(",s:%sg", szBuffer); + } + + const unsigned char *pabyDWGColors = ACGetColorTable(); + + snprintf( szBuffer, sizeof(szBuffer), ",c:#%02x%02x%02x", + pabyDWGColors[nColor*3+0], + pabyDWGColors[nColor*3+1], + pabyDWGColors[nColor*3+2] ); + osStyle += szBuffer; + + if( bHidden ) + osStyle += "00"; + + osStyle += ")"; + + poFeature->SetStyleString( osStyle ); + + return poFeature; +} + +/************************************************************************/ +/* TranslatePOINT() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::TranslatePOINT( OdDbEntityPtr poEntity ) + +{ + OdDbPointPtr poPE = OdDbPoint::cast( poEntity ); + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + TranslateGenericProperties( poFeature, poEntity ); + + OdGePoint3d oPoint = poPE->position(); + + poFeature->SetGeometryDirectly( new OGRPoint( oPoint.x, oPoint.y, oPoint.z ) ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateLWPOLYLINE() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::TranslateLWPOLYLINE( OdDbEntityPtr poEntity ) + +{ + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + OdDbPolylinePtr poPL = OdDbPolyline::cast( poEntity ); + + TranslateGenericProperties( poFeature, poEntity ); + +/* -------------------------------------------------------------------- */ +/* Collect polyline details. */ +/* -------------------------------------------------------------------- */ + DXFSmoothPolyline oSmoothPolyline; + + for (unsigned int i = 0; i < poPL->numVerts(); i++) + { + OdGePoint3d oPoint; + poPL->getPointAt( i, oPoint ); + + oSmoothPolyline.AddPoint( oPoint.x, oPoint.y, 0.0, + poPL->getBulgeAt( i ) ); + } + + if(oSmoothPolyline.IsEmpty()) + { + delete poFeature; + return NULL; + } + + if( poPL->isClosed() ) + oSmoothPolyline.Close(); + + poFeature->SetGeometryDirectly( + oSmoothPolyline.Tesselate() ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* Translate2DPOLYLINE() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::Translate2DPOLYLINE( OdDbEntityPtr poEntity ) + +{ + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + OdDb2dPolylinePtr poPL = OdDb2dPolyline::cast( poEntity ); + + TranslateGenericProperties( poFeature, poEntity ); + +/* -------------------------------------------------------------------- */ +/* Create a polyline geometry from the vertices. */ +/* -------------------------------------------------------------------- */ + OGRLineString *poLS = new OGRLineString(); + OdDbObjectIteratorPtr poIter = poPL->vertexIterator(); + + while( !poIter->done() ) + { + OdDb2dVertexPtr poVertex = poIter->entity(); + OdGePoint3d oPoint = poPL->vertexPosition( *poVertex ); + poLS->addPoint( oPoint.x, oPoint.y, oPoint.z ); + poIter->step(); + } + + poFeature->SetGeometryDirectly( poLS ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* Translate3DPOLYLINE() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::Translate3DPOLYLINE( OdDbEntityPtr poEntity ) + +{ + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + OdDb3dPolylinePtr poPL = OdDb3dPolyline::cast( poEntity ); + + TranslateGenericProperties( poFeature, poEntity ); + +/* -------------------------------------------------------------------- */ +/* Create a polyline geometry from the vertices. */ +/* -------------------------------------------------------------------- */ + OGRLineString *poLS = new OGRLineString(); + OdDbObjectIteratorPtr poIter = poPL->vertexIterator(); + + while( !poIter->done() ) + { + OdDb3dPolylineVertexPtr poVertex = poIter->entity(); + OdGePoint3d oPoint = poVertex->position(); + poLS->addPoint( oPoint.x, oPoint.y, oPoint.z ); + poIter->step(); + } + + poFeature->SetGeometryDirectly( poLS ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateLINE() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::TranslateLINE( OdDbEntityPtr poEntity ) + +{ + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + OdDbLinePtr poPL = OdDbLine::cast( poEntity ); + + TranslateGenericProperties( poFeature, poEntity ); + +/* -------------------------------------------------------------------- */ +/* Create a polyline geometry from the vertices. */ +/* -------------------------------------------------------------------- */ + OGRLineString *poLS = new OGRLineString(); + OdGePoint3d oPoint; + + poPL->getStartPoint( oPoint ); + poLS->addPoint( oPoint.x, oPoint.y, oPoint.z ); + + poPL->getEndPoint( oPoint ); + poLS->addPoint( oPoint.x, oPoint.y, oPoint.z ); + + poFeature->SetGeometryDirectly( poLS ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateCIRCLE() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::TranslateCIRCLE( OdDbEntityPtr poEntity ) + +{ + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + OdDbCirclePtr poC = OdDbCircle::cast( poEntity ); + + TranslateGenericProperties( poFeature, poEntity ); + +/* -------------------------------------------------------------------- */ +/* Get geometry information. */ +/* -------------------------------------------------------------------- */ + OdGePoint3d oCenter = poC->center(); + double dfRadius = poC->radius(); + +/* -------------------------------------------------------------------- */ +/* Create geometry */ +/* -------------------------------------------------------------------- */ + OGRGeometry *poCircle = + OGRGeometryFactory::approximateArcAngles( + oCenter.x, oCenter.y, oCenter.z, + dfRadius, dfRadius, 0.0, 0.0, 360.0, 0.0 ); + + poFeature->SetGeometryDirectly( poCircle ); + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* AngleCorrect() */ +/* */ +/* Convert from a "true" angle on the ellipse as returned by */ +/* the DWG API to an angle of rotation on the ellipse as if the */ +/* ellipse were actually circular. */ +/************************************************************************/ + +double OGRDWGLayer::AngleCorrect( double dfTrueAngle, double dfRatio ) + +{ + double dfRotAngle; + double dfDeltaX, dfDeltaY; + + dfTrueAngle *= (PI / 180); // convert to radians. + + dfDeltaX = cos(dfTrueAngle); + dfDeltaY = sin(dfTrueAngle); + + dfRotAngle = atan2( dfDeltaY, dfDeltaX * dfRatio); + + dfRotAngle *= (180 / PI); // convert to degrees. + + if( dfTrueAngle < 0 && dfRotAngle > 0 ) + dfRotAngle -= 360.0; + + if( dfTrueAngle > 360 && dfRotAngle < 360 ) + dfRotAngle += 360.0; + + return dfRotAngle; +} + +/************************************************************************/ +/* TranslateELLIPSE() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::TranslateELLIPSE( OdDbEntityPtr poEntity ) + +{ + OdDbEllipsePtr poEE = OdDbEllipse::cast( poEntity ); + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + TranslateGenericProperties( poFeature, poEntity ); + +/* -------------------------------------------------------------------- */ +/* Get some details. */ +/* -------------------------------------------------------------------- */ + double dfStartAngle, dfEndAngle, dfRatio; + OdGePoint3d oCenter; + OdGeVector3d oMajorAxis, oUnitNormal; + + // note we reverse start and end angles to account for ogr orientation. + poEE->get( oCenter, oUnitNormal, oMajorAxis, + dfRatio, dfEndAngle, dfStartAngle ); + + dfStartAngle = -1 * dfStartAngle * 180 / PI; + dfEndAngle = -1 * dfEndAngle * 180 / PI; + +/* -------------------------------------------------------------------- */ +/* The DWG SDK expresses the angles as the angle to a real */ +/* point on the ellipse while DXF and the OGR "arc angles" API */ +/* work in terms of an angle of rotation on the ellipse as if */ +/* the ellipse were actually circular. So we need to "correct" */ +/* for the ratio. */ +/* -------------------------------------------------------------------- */ + dfStartAngle = AngleCorrect( dfStartAngle, dfRatio ); + dfEndAngle = AngleCorrect( dfEndAngle, dfRatio ); + + if( dfStartAngle > dfEndAngle ) + dfEndAngle += 360.0; + +/* -------------------------------------------------------------------- */ +/* Compute primary and secondary axis lengths, and the angle of */ +/* rotation for the ellipse. */ +/* -------------------------------------------------------------------- */ + double dfPrimaryRadius, dfSecondaryRadius; + double dfRotation; + + dfPrimaryRadius = sqrt( oMajorAxis.x * oMajorAxis.x + + oMajorAxis.y * oMajorAxis.y + + oMajorAxis.z * oMajorAxis.z ); + + dfSecondaryRadius = dfRatio * dfPrimaryRadius; + + dfRotation = -1 * atan2( oMajorAxis.y, oMajorAxis.x ) * 180 / PI; + +/* -------------------------------------------------------------------- */ +/* Create geometry */ +/* -------------------------------------------------------------------- */ + OGRGeometry *poEllipse = + OGRGeometryFactory::approximateArcAngles( + oCenter.x, oCenter.y, oCenter.z, + dfPrimaryRadius, dfSecondaryRadius, dfRotation, + dfStartAngle, dfEndAngle, 0.0 ); + + poFeature->SetGeometryDirectly( poEllipse ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateARC() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::TranslateARC( OdDbEntityPtr poEntity ) + +{ + OdDbArcPtr poAE = OdDbArc::cast( poEntity ); + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + double dfRadius = 0.0; + double dfStartAngle = 0.0, dfEndAngle = 360.0; + OdGePoint3d oCenter; + + TranslateGenericProperties( poFeature, poEntity ); + +/* -------------------------------------------------------------------- */ +/* Collect parameters. */ +/* -------------------------------------------------------------------- */ + dfEndAngle = -1 * poAE->startAngle() * 180 / PI; + dfStartAngle = -1 * poAE->endAngle() * 180 / PI; + dfRadius = poAE->radius(); + oCenter = poAE->center(); + +/* -------------------------------------------------------------------- */ +/* Create geometry */ +/* -------------------------------------------------------------------- */ + if( dfStartAngle > dfEndAngle ) + dfEndAngle += 360.0; + + OGRGeometry *poArc = + OGRGeometryFactory::approximateArcAngles( + oCenter.x, oCenter.y, oCenter.z, + dfRadius, dfRadius, 0.0, dfStartAngle, dfEndAngle, 0.0 ); + + poFeature->SetGeometryDirectly( poArc ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateSPLINE() */ +/************************************************************************/ + +void rbspline(int npts,int k,int p1,double b[],double h[], double p[]); +void rbsplinu(int npts,int k,int p1,double b[],double h[], double p[]); + +OGRFeature *OGRDWGLayer::TranslateSPLINE( OdDbEntityPtr poEntity ) + +{ + OdDbSplinePtr poSpline = OdDbSpline::cast( poEntity ); + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + std::vector adfControlPoints; + int nDegree, i; + + TranslateGenericProperties( poFeature, poEntity ); + + nDegree = poSpline->degree(); + +/* -------------------------------------------------------------------- */ +/* Collect the control points in our vector. */ +/* -------------------------------------------------------------------- */ + int nControlPoints = poSpline->numControlPoints(); + + adfControlPoints.push_back( 0.0 ); // some sort of control info. + + for( i = 0; i < nControlPoints; i++ ) + { + OdGePoint3d oCP; + + poSpline->getControlPointAt( i, oCP ); + + adfControlPoints.push_back( oCP.x ); + adfControlPoints.push_back( oCP.y ); + adfControlPoints.push_back( 0.0 ); + } + +/* -------------------------------------------------------------------- */ +/* Process values. */ +/* -------------------------------------------------------------------- */ + if( poSpline->isClosed() ) + { + for( i = 0; i < nDegree; i++ ) + { + adfControlPoints.push_back( adfControlPoints[i*3+1] ); + adfControlPoints.push_back( adfControlPoints[i*3+2] ); + adfControlPoints.push_back( adfControlPoints[i*3+3] ); + } + } + +/* -------------------------------------------------------------------- */ +/* Interpolate spline */ +/* -------------------------------------------------------------------- */ + std::vector h, p; + + h.push_back(1.0); + for( i = 0; i < nControlPoints; i++ ) + h.push_back( 1.0 ); + + // resolution: + //int p1 = getGraphicVariableInt("$SPLINESEGS", 8) * npts; + int p1 = nControlPoints * 8; + + p.push_back( 0.0 ); + for( i = 0; i < 3*p1; i++ ) + p.push_back( 0.0 ); + + if( poSpline->isClosed() ) + rbsplinu( nControlPoints, nDegree+1, p1, &(adfControlPoints[0]), + &(h[0]), &(p[0]) ); + else + rbspline( nControlPoints, nDegree+1, p1, &(adfControlPoints[0]), + &(h[0]), &(p[0]) ); + +/* -------------------------------------------------------------------- */ +/* Turn into OGR geometry. */ +/* -------------------------------------------------------------------- */ + OGRLineString *poLS = new OGRLineString(); + + poLS->setNumPoints( p1 ); + for( i = 0; i < p1; i++ ) + poLS->setPoint( i, p[i*3+1], p[i*3+2] ); + + poFeature->SetGeometryDirectly( poLS ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* GeometryInsertTransformer */ +/************************************************************************/ + + +class GeometryInsertTransformer : public OGRCoordinateTransformation +{ +public: + GeometryInsertTransformer() : + dfXOffset(0),dfYOffset(0),dfZOffset(0), + dfXScale(1.0),dfYScale(1.0),dfZScale(1.0), + dfAngle(0.0) {} + + double dfXOffset; + double dfYOffset; + double dfZOffset; + double dfXScale; + double dfYScale; + double dfZScale; + double dfAngle; + + OGRSpatialReference *GetSourceCS() { return NULL; } + OGRSpatialReference *GetTargetCS() { return NULL; } + int Transform( int nCount, + double *x, double *y, double *z ) + { return TransformEx( nCount, x, y, z, NULL ); } + + int TransformEx( int nCount, + double *x, double *y, double *z = NULL, + int *pabSuccess = NULL ) + { + int i; + for( i = 0; i < nCount; i++ ) + { + double dfXNew, dfYNew; + + x[i] *= dfXScale; + y[i] *= dfYScale; + z[i] *= dfZScale; + + dfXNew = x[i] * cos(dfAngle) - y[i] * sin(dfAngle); + dfYNew = x[i] * sin(dfAngle) + y[i] * cos(dfAngle); + + x[i] = dfXNew; + y[i] = dfYNew; + + x[i] += dfXOffset; + y[i] += dfYOffset; + z[i] += dfZOffset; + + if( pabSuccess ) + pabSuccess[i] = TRUE; + } + return TRUE; + } +}; + +/************************************************************************/ +/* TranslateINSERT() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::TranslateINSERT( OdDbEntityPtr poEntity ) + +{ + OdDbBlockReferencePtr poRef = OdDbBlockReference::cast( poEntity ); + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + TranslateGenericProperties( poFeature, poEntity ); + +/* -------------------------------------------------------------------- */ +/* Collect parameters from the object. */ +/* -------------------------------------------------------------------- */ + GeometryInsertTransformer oTransformer; + CPLString osBlockName; + double dfAngle = poRef->rotation() * 180 / PI; + OdGePoint3d oPosition = poRef->position(); + OdGeScale3d oScale = poRef->scaleFactors(); + + oTransformer.dfXOffset = oPosition.x; + oTransformer.dfYOffset = oPosition.y; + oTransformer.dfZOffset = oPosition.z; + + oTransformer.dfXScale = oScale.sx; + oTransformer.dfYScale = oScale.sy; + oTransformer.dfZScale = oScale.sz; + + oTransformer.dfAngle = poRef->rotation(); + + OdDbBlockTableRecordPtr poBlockRec = poRef->blockTableRecord().openObject(); + if (poBlockRec.get()) + osBlockName = (const char *) poBlockRec->getName(); + +/* -------------------------------------------------------------------- */ +/* In the case where we do not inlined blocks we just capture */ +/* info on a point feature. */ +/* -------------------------------------------------------------------- */ + if( !poDS->InlineBlocks() ) + { + poFeature->SetGeometryDirectly( + new OGRPoint( oTransformer.dfXOffset, + oTransformer.dfYOffset, + oTransformer.dfZOffset ) ); + + poFeature->SetField( "BlockName", osBlockName ); + + poFeature->SetField( "BlockAngle", dfAngle ); + poFeature->SetField( "BlockScale", 3, &(oTransformer.dfXScale) ); + + return poFeature; + } + +/* -------------------------------------------------------------------- */ +/* Lookup the block. */ +/* -------------------------------------------------------------------- */ + DWGBlockDefinition *poBlock = poDS->LookupBlock( osBlockName ); + + if( poBlock == NULL ) + { + delete poFeature; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Transform the geometry. */ +/* -------------------------------------------------------------------- */ + if( poBlock->poGeometry != NULL ) + { + OGRGeometry *poGeometry = poBlock->poGeometry->clone(); + + poGeometry->transform( &oTransformer ); + + poFeature->SetGeometryDirectly( poGeometry ); + } + +/* -------------------------------------------------------------------- */ +/* If we have complete features associated with the block, push */ +/* them on the pending feature stack copying over key override */ +/* information. */ +/* */ +/* Note that while we transform the geometry of the features we */ +/* don't adjust subtle things like text angle. */ +/* -------------------------------------------------------------------- */ + unsigned int iSubFeat; + + for( iSubFeat = 0; iSubFeat < poBlock->apoFeatures.size(); iSubFeat++ ) + { + OGRFeature *poSubFeature = poBlock->apoFeatures[iSubFeat]->Clone(); + CPLString osCompEntityId; + + if( poSubFeature->GetGeometryRef() != NULL ) + poSubFeature->GetGeometryRef()->transform( &oTransformer ); + + ACAdjustText( dfAngle, oScale.sx, poSubFeature ); + +#ifdef notdef + osCompEntityId = poSubFeature->GetFieldAsString( "EntityHandle" ); + osCompEntityId += ":"; +#endif + osCompEntityId += poFeature->GetFieldAsString( "EntityHandle" ); + + poSubFeature->SetField( "EntityHandle", osCompEntityId ); + + apoPendingFeatures.push( poSubFeature ); + } + +/* -------------------------------------------------------------------- */ +/* If we have attributes, insert them on the stack at this */ +/* point too. */ +/* -------------------------------------------------------------------- */ + OdDbObjectIteratorPtr pIter = poRef->attributeIterator(); + for (; !pIter->done(); pIter->step()) + { + OdDbAttributePtr pAttr = pIter->entity(); + if (!pAttr.isNull()) + { + oStyleProperties.clear(); + + OGRFeature *poAttrFeat = TranslateTEXT( pAttr ); + + if( poAttrFeat ) + apoPendingFeatures.push( poAttrFeat ); + } + } + +/* -------------------------------------------------------------------- */ +/* Return the working feature if we had geometry, otherwise */ +/* return NULL and let the machinery find the rest of the */ +/* features in the pending feature stack. */ +/* -------------------------------------------------------------------- */ + if( poBlock->poGeometry == NULL ) + { + delete poFeature; + return NULL; + } + else + { + return poFeature; + } +} + +/************************************************************************/ +/* GetNextUnfilteredFeature() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::GetNextUnfilteredFeature() + +{ + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* If we have pending features, return one of them. */ +/* -------------------------------------------------------------------- */ + if( !apoPendingFeatures.empty() ) + { + poFeature = apoPendingFeatures.front(); + apoPendingFeatures.pop(); + + poFeature->SetFID( iNextFID++ ); + return poFeature; + } + +/* -------------------------------------------------------------------- */ +/* Fetch the next entity. */ +/* -------------------------------------------------------------------- */ + while( poFeature == NULL && !poEntIter->done() ) + { + + OdDbObjectId oId = poEntIter->objectId(); + OdDbEntityPtr poEntity = OdDbEntity::cast( oId.openObject() ); + + if (poEntity.isNull()) + return NULL; + +/* -------------------------------------------------------------------- */ +/* What is the class name for this entity? */ +/* -------------------------------------------------------------------- */ + OdRxClass *poClass = poEntity->isA(); + const OdString osName = poClass->name(); + const char *pszEntityClassName = (const char *) osName; + +/* -------------------------------------------------------------------- */ +/* Handle the entity. */ +/* -------------------------------------------------------------------- */ + oStyleProperties.clear(); + + if( EQUAL(pszEntityClassName,"AcDbPoint") ) + { + poFeature = TranslatePOINT( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDbLine") ) + { + poFeature = TranslateLINE( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDbPolyline") ) + { + poFeature = TranslateLWPOLYLINE( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDb2dPolyline") ) + { + poFeature = Translate2DPOLYLINE( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDb3dPolyline") ) + { + poFeature = Translate3DPOLYLINE( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDbEllipse") ) + { + poFeature = TranslateELLIPSE( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDbArc") ) + { + poFeature = TranslateARC( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDbMText") ) + { + poFeature = TranslateMTEXT( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDbText") + || EQUAL(pszEntityClassName,"AcDbAttributeDefinition") ) + { + poFeature = TranslateTEXT( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDbAlignedDimension") + || EQUAL(pszEntityClassName,"AcDbRotatedDimension") ) + { + poFeature = TranslateDIMENSION( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDbCircle") ) + { + poFeature = TranslateCIRCLE( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDbSpline") ) + { + poFeature = TranslateSPLINE( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDbHatch") ) + { + poFeature = TranslateHATCH( poEntity ); + } + else if( EQUAL(pszEntityClassName,"AcDbBlockReference") ) + { + poFeature = TranslateINSERT( poEntity ); + if( poFeature == NULL && !apoPendingFeatures.empty() ) + { + poFeature = apoPendingFeatures.front(); + apoPendingFeatures.pop(); + } + + } + else + { + if( oIgnoredEntities.count(pszEntityClassName) == 0 ) + { + oIgnoredEntities.insert( pszEntityClassName ); + CPLDebug( "DWG", "Ignoring one or more of entity '%s'.", + pszEntityClassName ); + } + } + + poEntIter->step(); + } + +/* -------------------------------------------------------------------- */ +/* Set FID. */ +/* -------------------------------------------------------------------- */ + if( poFeature != NULL ) + { + poFeature->SetFID( iNextFID++ ); + m_nFeaturesRead++; + } + + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRDWGLayer::GetNextFeature() + +{ + while( TRUE ) + { + OGRFeature *poFeature = GetNextUnfilteredFeature(); + + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature ) ) ) + { + return poFeature; + } + + delete poFeature; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDWGLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + else + return FALSE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/GNUmakefile new file mode 100644 index 000000000..ff505e8c2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/GNUmakefile @@ -0,0 +1,19 @@ + +include ../../../GDALmake.opt + +OBJ = ogrdxfdriver.o ogrdxfdatasource.o ogrdxflayer.o \ + ogrdxfreader.o ogrdxf_blockmap.o ogrdxf_dimension.o \ + ogrdxfwriterds.o ogrdxfwriterlayer.o intronurbs.o \ + ogrdxf_polyline_smooth.o ogrdxfblockslayer.o \ + ogrdxfblockswriterlayer.o ogrdxf_hatch.o \ + ogr_autocad_services.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_dxf.h + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/drv_dxf.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/drv_dxf.html new file mode 100644 index 000000000..e4752ea04 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/drv_dxf.html @@ -0,0 +1,207 @@ + + +AutoCAD DXF + + + + +

    AutoCAD DXF

    + +OGR supports reading most versions of AutoCAD DXF and writing AutoCAD 2000 +version files. DXF is an ASCII format used for interchanging AutoCAD drawings +between different software packages. The entire contents of the file is +represented as a single layer named "entities".

    + + +DXF files are considered to have no georeferencing information through OGR. +Features will all have the following generic attributes: + +

      +
    • Layer: The name of the DXF layer. The default layer is "0". +
    • SubClasses: Where available, a list of classes to which an element belongs. +
    • ExtendedEntity: Where available, extended entity attributes all appended to form a single text attribute. +
    • Linetype: Where available, the line type used for this entity. +
    • EntityHandle: The hexadecimal entity handle. A sort of feature id. +
    • Text: The text of labels. +
    + +

    Supported Elements

    + +The following element types are supported:

    + +

      +
    • POINT: Produces a simple point geometry feature. +
    • MTEXT, TEXT: Produces a point feature with LABEL style information. +
    • LINE, POLYLINE, LWPOLYLINE: translated as a LINESTRING. Rounded +polylines (those with their vertices' budge attributes set) will be tesselated. +Single-vertex polylines are translated to POINT. +
    • CIRCLE, ELLIPSE, ARC: Translated as a LINESTRING, tesselating the +arc into line segments. +
    • INSERT: An attempt is made to insert the block definition as defined +in the insert. Linework blocks are aggregated into a single feature with +a geometry collection for the geometry. Text blocks are returned as one or +more text features. To avoid merging blocks into a geometry collection the +DXF_MERGE_BLOCK_GEOMETRIES config option may be set to FALSE. +
    • DIMENSION: This element is exploded into a feature with arrows and +leaders, and a feature with the dimension label. +
    • HATCH: Line and arc boundaries are collected as a polygon geometry, but +no effort is currently made to represent the fill style of HATCH entities. +
    • 3DFACE, SOLID: Translated as POLYGON. + +
    + +A reasonable attempt is made to preserve line color, line width, text size +and orientation via OGR feature styling information when translating elements. +Currently no effort is made to preserve fill styles or complex line style +attributes.

    + +The approximation of arcs, ellipses, circles and rounded polylines as +linestrings is done by splitting the arcs into subarcs of no more than a +threshhold angle. This angle is the OGR_ARC_STEPSIZE. This defaults to +four degrees, but may be overridden by setting the configuration variable +OGR_ARC_STEPSIZE.

    + +

    DXF_INLINE_BLOCKS

    + +The default behavior is for INSERT entities to be expanded with the +geometry of the BLOCK they reference. However, if the DXF_INLINE_BLOCKS +configuration option is set to the value FALSE, then the behavior is different +as described here. + +
      +
    • A new layer will be available called blocks. It will contain one or +more features for each BLOCK defined in the file. In addition to the usual +attributes, they will also have a BlockName attribute indicate what block +they are part of. +
    • The entities layer will have new attributes BlockName, BlockScale, +and BlockAngle. +
    • INSERT entities will populate these new fields with the corresponding +information (they are null for all other entities). +
    • INSERT entities will not have block geometry inlined - instead they will +have a point geometry for the insertion point. +
    + +The intention is that with DXF_INLINE_BLOCKS disabled, the block references +will remain as references and the original block definitions will be +available via the blocks layer. On export this configuration will result in the +creation of similar blocks.

    + +

    Character Encodings

    + +Normally DXF files are in the ANSI_1252 / Win1252 encoding. GDAL/OGR attempts +to translate this to UTF-8 when reading and back into ANSI_1252 when writing. +DXF files can also have a header field ($DWGCODEPAGE) indicating the +encoding of the file. In GDAL 1.8.x and earlier this was ignored but from +GDAL 1.9.0 and later an attempt is made to use this to recode other +code pages to UTF-8. Whether this works will depend on the code page naming +and whether GDAL/OGR is built against the iconv library for character recoding. +

    + +In some cases the $DWGCODEPAGE setting in a DXF file will be wrong, or +unrecognised by OGR. It could be edited manually, or the DXF_ENCODING +configuration variable can be used to override what id will be used by +OGR in transcoding. The value of DXF_ENCODING should be an encoding name +supported by CPLRecode() (ie. an iconv name), not a DXF $DWGCODEPAGE name. +Using a DXF_ENCODING name of "UTF-8" will avoid any attempt to recode the +text as it is read.

    + +


    +

    Creation Issues

    + +DXF files are written in AutoCAD 2000 format. A standard header (everything +up to the ENTITIES keyword) is written from the $GDAL_DATA/header.dxf file, +and the $GDAL_DATA/trailer.dxf file is added after the entities. Only one +layer can be created on the output file.

    + +Point features with LABEL styling are written as MTEXT entities based +on the styling information.

    + +Point features without LABEL styling are written as POINT entities.

    + +LineString and MultiLineString features are written as one or more +LWPOLYLINE entities, closed in the case of polygon rings. An effort is +made to preserve line width and color.

    + +Polygon and MultiPolygon features are written as HATCH entities. + +The dataset creation supports the following dataset creation options:

    + +

      + +
    • HEADER=filename: Override the header file used - in place +of header.dxf located in the GDAL_DATA directory. + +
    • TRAILER=filename: Override the trailer file used - in place +of trailer.dxf located in the GDAL_DATA directory.

      + +

    + +Note that in GDAL 1.8 and later, the header and trailer templates can +be complete DXF files. The driver will scan them and only extract the needed +portions (portion before or after the ENTITIES section).

    + +

    Block References

    + +It is possible to export a "blocks" layer to DXF in addition to the +"entities" layer in order to produce actual DXF BLOCKs definitions +in the output file. It is also possible to write INSERT entities if +a block name is provided for an entity. To make this work the follow +conditions apply. + +
      +
    • A "blocks" layer may be created, and it must be created before the +entities layer. +
    • The entities in the blocks layer should have the BlockName field +populated. +
    • Objects to be written as INSERTs in the entities layer should have a +POINT geometry, and the BlockName field set. +
    • If a block (name) is already defined in the template header, that will be +used regardless of whether a new definition was provided in the blocks layer. +
    + +The intention is that a simple translation from DXF to with DXF_INLINE_BLOCKS +set to FALSE will approximately reproduce the original blocks and keep +INSERT entities as INSERT entities rather than exploding them.

    + +

    Layer Definitions

    + +When writing entities, if populated the Layer field is used to set the +written entities layer. If the layer is not already defined in the template +header then a new layer definition will be introduced, copied from the +definition of the default layer ("0"). + +

    Line Type Definitions

    + +When writing LWPOLYLINE entities the following rules apply with regard to +Linetype definitions.

    + +

      +
    • If the Linetype field is set on the written features, and that Linetype +is already defined in the template header then it will be referenced from +the entities regardless of whether an OGR style string existed. +
    • If the Linetype is set, but the Linetype is not predefined in the +header template, then a definition will be added if the feature has an +OGR style string with a PEN tool and a "p" pattern setting. +
    • If the feature has no Linetype field set, but it does have an OGR +style string with a PEN tool with a "p" pattern set then an automatically +named Linetype will be created in the output file. +
    • It is assumed that patterns are using "g" (georeferenced) units for +defining the line pattern. If not the scaling of the DXF patterns is likely to be wrong - potentially very wrong. +
    + +The intention is that "dot dash" style patterns will be preserved when written +to DXF and that specific linetypes can be predefined in the header template, +and referenced using the Linetype field if desired.

    + +

    Units

    + +At the moment GDAL writes DXF to report the the measurement units as "English - Inches". +For changing the units edit $MEASUREMENT and $INSUNITS variables in the header template. + +

    See also

    + +AutoCAD 2000 DXF Reference

    + +DXF header reference + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/intronurbs.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/intronurbs.cpp new file mode 100644 index 000000000..b32294b9a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/intronurbs.cpp @@ -0,0 +1,425 @@ +/****************************************************************************** + * $Id$ + * + * Project: DXF Translator + * Purpose: Low level spline interpolation. + * Author: David F. Rogers + * + ****************************************************************************** + +This code is derived from the code associated with the book "An Introduction +to NURBS" by David F. Rogers. More information on the book and the code is +available at: + + http://www.nar-associates.com/nurbs/ + + +Copyright (c) 2009, David F. Rogers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of David F. Rogers nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +/************************************************************************/ +/* knotu() */ +/************************************************************************/ + +/* Subroutine to generate a B-spline uniform (periodic) knot vector. + + c = order of the basis function + n = the number of defining polygon vertices + nplus2 = index of x() for the first occurence of the maximum knot vector value + nplusc = maximum value of the knot vector -- $n + c$ + x[] = array containing the knot vector +*/ + +static void knotu(int n,int c,int x[]) + +{ + int nplusc, /* nplus2, */i; + + nplusc = n + c; + /* nplus2 = n + 2; */ + + x[1] = 0; + for (i = 2; i <= nplusc; i++){ + x[i] = i-1; + } +} + +/************************************************************************/ +/* knot() */ +/************************************************************************/ + +/* + Subroutine to generate a B-spline open knot vector with multiplicity + equal to the order at the ends. + + c = order of the basis function + n = the number of defining polygon vertices + nplus2 = index of x() for the first occurence of the maximum knot vector value + nplusc = maximum value of the knot vector -- $n + c$ + x() = array containing the knot vector +*/ + +static void knot(int n,int c,int x[]) + +{ + int nplusc,nplus2,i; + + nplusc = n + c; + nplus2 = n + 2; + + x[1] = 0; + for (i = 2; i <= nplusc; i++){ + if ( (i > c) && (i < nplus2) ) + x[i] = x[i-1] + 1; + else + x[i] = x[i-1]; + } +} + +/************************************************************************/ +/* rbasis() */ +/************************************************************************/ + +/* Subroutine to generate rational B-spline basis functions--open knot vector + + C code for An Introduction to NURBS + by David F. Rogers. Copyright (C) 2000 David F. Rogers, + All rights reserved. + + Name: rbais + Language: C + Subroutines called: none + Book reference: Chapter 4, Sec. 4. , p 296 + + c = order of the B-spline basis function + d = first term of the basis function recursion relation + e = second term of the basis function recursion relation + h[] = array containing the homogeneous weights + npts = number of defining polygon vertices + nplusc = constant -- npts + c -- maximum number of knot values + r[] = array containing the rationalbasis functions + r[1] contains the basis function associated with B1 etc. + t = parameter value + temp[] = temporary array + x[] = knot vector +*/ + +static void rbasis(int c,double t,int npts, int x[], double h[], double r[]) + +{ + int nplusc; + int i,k; + double d,e; + double sum; + std::vector temp; + + nplusc = npts + c; + + temp.resize( nplusc+1 ); + +/* calculate the first order nonrational basis functions n[i] */ + + for (i = 1; i<= nplusc-1; i++){ + if (( t >= x[i]) && (t < x[i+1])) + temp[i] = 1; + else + temp[i] = 0; + } + +/* calculate the higher order nonrational basis functions */ + + for (k = 2; k <= c; k++){ + for (i = 1; i <= nplusc-k; i++){ + if (temp[i] != 0) /* if the lower order basis function is zero skip the calculation */ + d = ((t-x[i])*temp[i])/(x[i+k-1]-x[i]); + else + d = 0; + + if (temp[i+1] != 0) /* if the lower order basis function is zero skip the calculation */ + e = ((x[i+k]-t)*temp[i+1])/(x[i+k]-x[i+1]); + else + e = 0; + + temp[i] = d + e; + } + } + + if (t == (double)x[nplusc]){ /* pick up last point */ + temp[npts] = 1; + } + +/* calculate sum for denominator of rational basis functions */ + + sum = 0.; + for (i = 1; i <= npts; i++){ + sum = sum + temp[i]*h[i]; + } + +/* form rational basis functions and put in r vector */ + + for (i = 1; i <= npts; i++){ + if (sum != 0){ + r[i] = (temp[i]*h[i])/(sum);} + else + r[i] = 0; + } +} + +/************************************************************************/ +/* rbspline() */ +/************************************************************************/ + +/* Subroutine to generate a rational B-spline curve using an uniform open knot vector + + C code for An Introduction to NURBS + by David F. Rogers. Copyright (C) 2000 David F. Rogers, + All rights reserved. + + Name: rbspline.c + Language: C + Subroutines called: knot.c, rbasis.c, fmtmul.c + Book reference: Chapter 4, Alg. p. 297 + + b[] = array containing the defining polygon vertices + b[1] contains the x-component of the vertex + b[2] contains the y-component of the vertex + b[3] contains the z-component of the vertex + h[] = array containing the homogeneous weighting factors + k = order of the B-spline basis function + nbasis = array containing the basis functions for a single value of t + nplusc = number of knot values + npts = number of defining polygon vertices + p[,] = array containing the curve points + p[1] contains the x-component of the point + p[2] contains the y-component of the point + p[3] contains the z-component of the point + p1 = number of points to be calculated on the curve + t = parameter value 0 <= t <= npts - k + 1 + x[] = array containing the knot vector +*/ + +void rbspline(int npts,int k,int p1,double b[],double h[], double p[]) + +{ + int i,j,icount,jcount; + int i1; + int nplusc; + + double step; + double t; + double temp; + std::vector nbasis; + std::vector x; + + nplusc = npts + k; + + x.resize( nplusc+1 ); + nbasis.resize( npts+1 ); + +/* zero and redimension the knot vector and the basis array */ + + for(i = 0; i <= npts; i++){ + nbasis[i] = 0.; + } + + for(i = 0; i <= nplusc; i++){ + x[i] = 0; + } + +/* generate the uniform open knot vector */ + + knot(npts,k,&(x[0])); + +/* + printf("The knot vector is "); + for (i = 1; i <= nplusc; i++){ + printf(" %d ", x[i]); + } + printf("\n"); +*/ + + icount = 0; + +/* calculate the points on the rational B-spline curve */ + + t = 0; + step = ((double)x[nplusc])/((double)(p1-1)); + + for (i1 = 1; i1<= p1; i1++){ + + if ((double)x[nplusc] - t < 5e-6){ + t = (double)x[nplusc]; + } + + /* generate the basis function for this value of t */ + rbasis(k,t,npts,&(x[0]),h,&(nbasis[0])); + for (j = 1; j <= 3; j++){ /* generate a point on the curve */ + jcount = j; + p[icount+j] = 0.; + + for (i = 1; i <= npts; i++){ /* Do local matrix multiplication */ + temp = nbasis[i]*b[jcount]; + p[icount + j] = p[icount + j] + temp; +/* + printf("jcount,nbasis,b,nbasis*b,p = %d %f %f %f %f\n",jcount,nbasis[i],b[jcount],temp,p[icount+j]); +*/ + jcount = jcount + 3; + } + } +/* + printf("icount, p %d %f %f %f \n",icount,p[icount+1],p[icount+2],p[icount+3]); +*/ + icount = icount + 3; + t = t + step; + } +} + +/************************************************************************/ +/* rbsplinu() */ +/************************************************************************/ + +/* Subroutine to generate a rational B-spline curve using an uniform periodic knot vector + + C code for An Introduction to NURBS + by David F. Rogers. Copyright (C) 2000 David F. Rogers, + All rights reserved. + + Name: rbsplinu.c + Language: C + Subroutines called: knotu.c, rbasis.c, fmtmul.c + Book reference: Chapter 4, Alg. p. 298 + + b[] = array containing the defining polygon vertices + b[1] contains the x-component of the vertex + b[2] contains the y-component of the vertex + b[3] contains the z-component of the vertex + h[] = array containing the homogeneous weighting factors + k = order of the B-spline basis function + nbasis = array containing the basis functions for a single value of t + nplusc = number of knot values + npts = number of defining polygon vertices + p[,] = array containing the curve points + p[1] contains the x-component of the point + p[2] contains the y-component of the point + p[3] contains the z-component of the point + p1 = number of points to be calculated on the curve + t = parameter value 0 <= t <= npts - k + 1 + x[] = array containing the knot vector +*/ + +void rbsplinu(int npts,int k,int p1,double b[],double h[], double p[]) + +{ + int i,j,icount,jcount; + int i1; + int nplusc; + + double step; + double t; + double temp; + std::vector nbasis; + std::vector x; + + nplusc = npts + k; + + x.resize( nplusc+1); + nbasis.resize(npts+1); + +/* zero and redimension the knot vector and the basis array */ + + for(i = 0; i <= npts; i++){ + nbasis[i] = 0.; + } + + for(i = 0; i <= nplusc; i++){ + x[i] = 0; + } + +/* generate the uniform periodic knot vector */ + + knotu(npts,k,&(x[0])); + +/* + printf("The knot vector is "); + for (i = 1; i <= nplusc; i++){ + printf(" %d ", x[i]); + } + printf("\n"); + + printf("The usable parameter range is "); + for (i = k; i <= npts+1; i++){ + printf(" %d ", x[i]); + } + printf("\n"); +*/ + + icount = 0; + +/* calculate the points on the rational B-spline curve */ + + t = k-1; + step = ((double)((npts)-(k-1)))/((double)(p1-1)); + + for (i1 = 1; i1<= p1; i1++){ + + if ((double)x[nplusc] - t < 5e-6){ + t = (double)x[nplusc]; + } + + rbasis(k,t,npts,&(x[0]),h,&(nbasis[0])); /* generate the basis function for this value of t */ +/* + printf("t = %f \n",t); + printf("nbasis = "); + for (i = 1; i <= npts; i++){ + printf("%f ",nbasis[i]); + } + printf("\n"); +*/ + for (j = 1; j <= 3; j++){ /* generate a point on the curve */ + jcount = j; + p[icount+j] = 0.; + + for (i = 1; i <= npts; i++){ /* Do local matrix multiplication */ + temp = nbasis[i]*b[jcount]; + p[icount + j] = p[icount + j] + temp; +/* + printf("jcount,nbasis,b,nbasis*b,p = %d %f %f %f %f\n",jcount,nbasis[i],b[jcount],temp,p[icount+j]); +*/ + jcount = jcount + 3; + } + } +/* + printf("icount, p %d %f %f %f \n",icount,p[icount+1],p[icount+2],p[icount+3]); +*/ + icount = icount + 3; + t = t + step; + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/makefile.vc new file mode 100644 index 000000000..4ea75c6cd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/makefile.vc @@ -0,0 +1,17 @@ +OBJ = ogrdxfdriver.obj ogrdxfdatasource.obj ogrdxflayer.obj \ + ogrdxfreader.obj ogrdxf_blockmap.obj ogrdxf_dimension.obj \ + ogrdxfwriterds.obj ogrdxfwriterlayer.obj intronurbs.obj \ + ogrdxf_polyline_smooth.obj ogrdxfblockslayer.obj \ + ogrdxfblockswriterlayer.obj ogrdxf_hatch.obj \ + ogr_autocad_services.obj + +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogr_autocad_services.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogr_autocad_services.cpp new file mode 100644 index 000000000..ec1e0137d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogr_autocad_services.cpp @@ -0,0 +1,519 @@ +/****************************************************************************** + * $Id$ + * + * Project: DXF and DWG Translators + * Purpose: Implements various generic services shared between autocad related + * drivers. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_autocad_services.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrdwglayer.cpp 22008 2011-03-22 19:45:20Z warmerdam $"); + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* ACTextUnescape() */ +/* */ +/* Unexcape DXF/DWG style escape sequences such as \P for newline */ +/* and \~ for space, and do the recoding to UTF8. */ +/************************************************************************/ + +CPLString ACTextUnescape( const char *pszRawInput, const char *pszEncoding ) + +{ + CPLString osResult; + CPLString osInput = pszRawInput; + +/* -------------------------------------------------------------------- */ +/* Translate text from Win-1252 to UTF8. We approximate this */ +/* by treating Win-1252 as Latin-1. Note that we likely ought */ +/* to be consulting the $DWGCODEPAGE header variable which */ +/* defaults to ANSI_1252 if not set. */ +/* -------------------------------------------------------------------- */ + osInput.Recode( pszEncoding, CPL_ENC_UTF8 ); + + const char *pszInput = osInput.c_str(); + +/* -------------------------------------------------------------------- */ +/* Now translate escape sequences. They are all plain ascii */ +/* characters and won't have been affected by the UTF8 */ +/* recoding. */ +/* -------------------------------------------------------------------- */ + while( *pszInput != '\0' ) + { + if( pszInput[0] == '\\' && pszInput[1] == 'P' ) + { + osResult += '\n'; + pszInput++; + } + else if( pszInput[0] == '\\' && pszInput[1] == '~' ) + { + osResult += ' '; + pszInput++; + } + else if( pszInput[0] == '\\' && pszInput[1] == 'U' + && pszInput[2] == '+' ) + { + CPLString osHex; + int iChar; + + osHex.assign( pszInput+3, 4 ); + sscanf( osHex.c_str(), "%x", &iChar ); + + wchar_t anWCharString[2]; + anWCharString[0] = (wchar_t) iChar; + anWCharString[1] = 0; + + char *pszUTF8Char = CPLRecodeFromWChar( anWCharString, + CPL_ENC_UCS2, + CPL_ENC_UTF8 ); + + osResult += pszUTF8Char; + CPLFree( pszUTF8Char ); + + pszInput += 6; + } + else if( pszInput[0] == '\\' + && (pszInput[1] == 'W' + || pszInput[1] == 'T' + || pszInput[1] == 'A' ) ) + { + // eg. \W1.073172x;\T1.099;Bonneuil de Verrines + // See data/dwg/EP/42002.dwg + // Not sure what \W and \T do, but we skip them. + // According to qcad rs_text.cpp, \A values are vertical + // alignment, 0=bottom, 1=mid, 2=top but we ignore for now. + + while( *pszInput != ';' && *pszInput != '\0' ) + pszInput++; + } + else if( pszInput[0] == '\\' && pszInput[1] == '\\' ) + { + osResult += '\\'; + pszInput++; + } + else if( EQUALN(pszInput,"%%c",3) + || EQUALN(pszInput,"%%d",3) + || EQUALN(pszInput,"%%p",3) ) + { + wchar_t anWCharString[2]; + + anWCharString[1] = 0; + + // These are especial symbol representations for autocad. + if( EQUALN(pszInput,"%%c",3) ) + anWCharString[0] = 0x2300; // diameter (0x00F8 is a good approx) + else if( EQUALN(pszInput,"%%d",3) ) + anWCharString[0] = 0x00B0; // degree + else if( EQUALN(pszInput,"%%p",3) ) + anWCharString[0] = 0x00B1; // plus/minus + + char *pszUTF8Char = CPLRecodeFromWChar( anWCharString, + CPL_ENC_UCS2, + CPL_ENC_UTF8 ); + + osResult += pszUTF8Char; + CPLFree( pszUTF8Char ); + + pszInput += 2; + } + else + osResult += *pszInput; + + pszInput++; + } + + return osResult; +} + +/************************************************************************/ +/* ACGetColorTable() */ +/************************************************************************/ + +const unsigned char *ACGetColorTable() + +{ + static const unsigned char abyDXFColors[768] = { + 0, 0, 0, // 0 + 255, 0, 0, // 1 + 255,255, 0, // 2 + 0,255, 0, // 3 + 0,255,255, // 4 + 0, 0,255, // 5 + 255, 0,255, // 6 + 0, 0, 0, // 7 - it should be white, but that plots poorly + 127,127,127, // 8 + 191,191,191, // 9 + 255, 0, 0, // 10 + 255,127,127, // 11 + 165, 0, 0, // 12 + 165, 82, 82, // 13 + 127, 0, 0, // 14 + 127, 63, 63, // 15 + 76, 0, 0, // 16 + 76, 38, 38, // 17 + 38, 0, 0, // 18 + 38, 19, 19, // 19 + 255, 63, 0, // 20 + 255,159,127, // 21 + 165, 41, 0, // 22 + 165,103, 82, // 23 + 127, 31, 0, // 24 + 127, 79, 63, // 25 + 76, 19, 0, // 26 + 76, 47, 38, // 27 + 38, 9, 0, // 28 + 38, 23, 19, // 29 + 255,127, 0, // 30 + 255,191,127, // 31 + 165, 82, 0, // 32 + 165,124, 82, // 33 + 127, 63, 0, // 34 + 127, 95, 63, // 35 + 76, 38, 0, // 36 + 76, 57, 38, // 37 + 38, 19, 0, // 38 + 38, 28, 19, // 39 + 255,191, 0, // 40 + 255,223,127, // 41 + 165,124, 0, // 42 + 165,145, 82, // 43 + 127, 95, 0, // 44 + 127,111, 63, // 45 + 76, 57, 0, // 46 + 76, 66, 38, // 47 + 38, 28, 0, // 48 + 38, 33, 19, // 49 + 255,255, 0, // 50 + 255,255,127, // 51 + 165,165, 0, // 52 + 165,165, 82, // 53 + 127,127, 0, // 54 + 127,127, 63, // 55 + 76, 76, 0, // 56 + 76, 76, 38, // 57 + 38, 38, 0, // 58 + 38, 38, 19, // 59 + 191,255, 0, // 60 + 223,255,127, // 61 + 124,165, 0, // 62 + 145,165, 82, // 63 + 95,127, 0, // 64 + 111,127, 63, // 65 + 57, 76, 0, // 66 + 66, 76, 38, // 67 + 28, 38, 0, // 68 + 33, 38, 19, // 69 + 127,255, 0, // 70 + 191,255,127, // 71 + 82,165, 0, // 72 + 124,165, 82, // 73 + 63,127, 0, // 74 + 95,127, 63, // 75 + 38, 76, 0, // 76 + 57, 76, 38, // 77 + 19, 38, 0, // 78 + 28, 38, 19, // 79 + 63,255, 0, // 80 + 159,255,127, // 81 + 41,165, 0, // 82 + 103,165, 82, // 83 + 31,127, 0, // 84 + 79,127, 63, // 85 + 19, 76, 0, // 86 + 47, 76, 38, // 87 + 9, 38, 0, // 88 + 23, 38, 19, // 89 + 0,255, 0, // 90 + 127,255,127, // 91 + 0,165, 0, // 92 + 82,165, 82, // 93 + 0,127, 0, // 94 + 63,127, 63, // 95 + 0, 76, 0, // 96 + 38, 76, 38, // 97 + 0, 38, 0, // 98 + 19, 38, 19, // 99 + 0,255, 63, // 100 + 127,255,159, // 101 + 0,165, 41, // 102 + 82,165,103, // 103 + 0,127, 31, // 104 + 63,127, 79, // 105 + 0, 76, 19, // 106 + 38, 76, 47, // 107 + 0, 38, 9, // 108 + 19, 38, 23, // 109 + 0,255,127, // 110 + 127,255,191, // 111 + 0,165, 82, // 112 + 82,165,124, // 113 + 0,127, 63, // 114 + 63,127, 95, // 115 + 0, 76, 38, // 116 + 38, 76, 57, // 117 + 0, 38, 19, // 118 + 19, 38, 28, // 119 + 0,255,191, // 120 + 127,255,223, // 121 + 0,165,124, // 122 + 82,165,145, // 123 + 0,127, 95, // 124 + 63,127,111, // 125 + 0, 76, 57, // 126 + 38, 76, 66, // 127 + 0, 38, 28, // 128 + 19, 38, 33, // 129 + 0,255,255, // 130 + 127,255,255, // 131 + 0,165,165, // 132 + 82,165,165, // 133 + 0,127,127, // 134 + 63,127,127, // 135 + 0, 76, 76, // 136 + 38, 76, 76, // 137 + 0, 38, 38, // 138 + 19, 38, 38, // 139 + 0,191,255, // 140 + 127,223,255, // 141 + 0,124,165, // 142 + 82,145,165, // 143 + 0, 95,127, // 144 + 63,111,127, // 145 + 0, 57, 76, // 146 + 38, 66, 76, // 147 + 0, 28, 38, // 148 + 19, 33, 38, // 149 + 0,127,255, // 150 + 127,191,255, // 151 + 0, 82,165, // 152 + 82,124,165, // 153 + 0, 63,127, // 154 + 63, 95,127, // 155 + 0, 38, 76, // 156 + 38, 57, 76, // 157 + 0, 19, 38, // 158 + 19, 28, 38, // 159 + 0, 63,255, // 160 + 127,159,255, // 161 + 0, 41,165, // 162 + 82,103,165, // 163 + 0, 31,127, // 164 + 63, 79,127, // 165 + 0, 19, 76, // 166 + 38, 47, 76, // 167 + 0, 9, 38, // 168 + 19, 23, 38, // 169 + 0, 0,255, // 170 + 127,127,255, // 171 + 0, 0,165, // 172 + 82, 82,165, // 173 + 0, 0,127, // 174 + 63, 63,127, // 175 + 0, 0, 76, // 176 + 38, 38, 76, // 177 + 0, 0, 38, // 178 + 19, 19, 38, // 179 + 63, 0,255, // 180 + 159,127,255, // 181 + 41, 0,165, // 182 + 103, 82,165, // 183 + 31, 0,127, // 184 + 79, 63,127, // 185 + 19, 0, 76, // 186 + 47, 38, 76, // 187 + 9, 0, 38, // 188 + 23, 19, 38, // 189 + 127, 0,255, // 190 + 191,127,255, // 191 + 82, 0,165, // 192 + 124, 82,165, // 193 + 63, 0,127, // 194 + 95, 63,127, // 195 + 38, 0, 76, // 196 + 57, 38, 76, // 197 + 19, 0, 38, // 198 + 28, 19, 38, // 199 + 191, 0,255, // 200 + 223,127,255, // 201 + 124, 0,165, // 202 + 145, 82,165, // 203 + 95, 0,127, // 204 + 111, 63,127, // 205 + 57, 0, 76, // 206 + 66, 38, 76, // 207 + 28, 0, 38, // 208 + 33, 19, 38, // 209 + 255, 0,255, // 210 + 255,127,255, // 211 + 165, 0,165, // 212 + 165, 82,165, // 213 + 127, 0,127, // 214 + 127, 63,127, // 215 + 76, 0, 76, // 216 + 76, 38, 76, // 217 + 38, 0, 38, // 218 + 38, 19, 38, // 219 + 255, 0,191, // 220 + 255,127,223, // 221 + 165, 0,124, // 222 + 165, 82,145, // 223 + 127, 0, 95, // 224 + 127, 63,111, // 225 + 76, 0, 57, // 226 + 76, 38, 66, // 227 + 38, 0, 28, // 228 + 38, 19, 33, // 229 + 255, 0,127, // 230 + 255,127,191, // 231 + 165, 0, 82, // 232 + 165, 82,124, // 233 + 127, 0, 63, // 234 + 127, 63, 95, // 235 + 76, 0, 38, // 236 + 76, 38, 57, // 237 + 38, 0, 19, // 238 + 38, 19, 28, // 239 + 255, 0, 63, // 240 + 255,127,159, // 241 + 165, 0, 41, // 242 + 165, 82,103, // 243 + 127, 0, 31, // 244 + 127, 63, 79, // 245 + 76, 0, 19, // 246 + 76, 38, 47, // 247 + 38, 0, 9, // 248 + 38, 19, 23, // 249 + 84, 84, 84, // 250 + 118,118,118, // 251 + 152,152,152, // 252 + 186,186,186, // 253 + 220,220,220, // 254 + 255,255,255 // 255 + }; + + return abyDXFColors; +} + +/************************************************************************/ +/* ACAdjustText() */ +/* */ +/* Rotate and scale text features by the designated amount by */ +/* adjusting the style string. */ +/************************************************************************/ + +void ACAdjustText( double dfAngle, double dfScale, OGRFeature *poFeature ) + +{ +/* -------------------------------------------------------------------- */ +/* We only try to alter text elements (LABEL styles). */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetStyleString() == NULL ) + return; + + CPLString osOldStyle = poFeature->GetStyleString(); + + if( strstr(osOldStyle,"LABEL") == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Is there existing angle text? */ +/* -------------------------------------------------------------------- */ + double dfOldAngle = 0.0; + CPLString osPreAngle, osPostAngle; + size_t nAngleOff = osOldStyle.find( ",a:" ); + + if( nAngleOff != std::string::npos ) + { + size_t nEndOfAngleOff = osOldStyle.find( ",", nAngleOff + 1 ); + + if( nEndOfAngleOff == std::string::npos ) + nEndOfAngleOff = osOldStyle.find( ")", nAngleOff + 1 ); + + osPreAngle.assign( osOldStyle, nAngleOff ); + osPostAngle.assign( osOldStyle.c_str() + nEndOfAngleOff ); + + dfOldAngle = CPLAtof( osOldStyle.c_str() + nAngleOff + 3 ); + } + else + { + CPLAssert( osOldStyle[osOldStyle.size()-1] == ')' ); + osPreAngle.assign( osOldStyle, osOldStyle.size() - 1 ); + osPostAngle = ")"; + } + +/* -------------------------------------------------------------------- */ +/* Format with the new angle. */ +/* -------------------------------------------------------------------- */ + CPLString osNewStyle; + + osNewStyle.Printf( "%s,a:%g%s", + osPreAngle.c_str(), + dfOldAngle + dfAngle, + osPostAngle.c_str() ); + + osOldStyle = osNewStyle; + +/* -------------------------------------------------------------------- */ +/* Is there existing scale text? */ +/* -------------------------------------------------------------------- */ + double dfOldScale = 1.0; + CPLString osPreScale, osPostScale; + size_t nScaleOff = osOldStyle.find( ",s:" ); + + if( nScaleOff != std::string::npos ) + { + size_t nEndOfScaleOff = osOldStyle.find( ",", nScaleOff + 1 ); + + if( nEndOfScaleOff == std::string::npos ) + nEndOfScaleOff = osOldStyle.find( ")", nScaleOff + 1 ); + + osPreScale.assign( osOldStyle, nScaleOff ); + osPostScale.assign( osOldStyle.c_str() + nEndOfScaleOff ); + + dfOldScale = CPLAtof( osOldStyle.c_str() + nScaleOff + 3 ); + } + else + { + CPLAssert( osOldStyle[osOldStyle.size()-1] == ')' ); + osPreScale.assign( osOldStyle, osOldStyle.size() - 1 ); + osPostScale = ")"; + } + +/* -------------------------------------------------------------------- */ +/* Format with the new scale. */ +/* -------------------------------------------------------------------- */ + osNewStyle.Printf( "%s,s:%gg%s", + osPreScale.c_str(), + dfOldScale * dfScale, + osPostScale.c_str() ); + + poFeature->SetStyleString( osNewStyle ); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogr_autocad_services.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogr_autocad_services.h new file mode 100644 index 000000000..a557a6511 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogr_autocad_services.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * $Id$ + * + * Project: DXF and DWG Translators + * Purpose: Declarations for shared AutoCAD format services. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_AUTOCAD_SERVICES_H_INCLUDED +#define _OGR_AUTOCAD_SERVICES_H_INCLUDED + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_geometry.h" +#include "ogr_feature.h" + +/* -------------------------------------------------------------------- */ +/* Various Functions. */ +/* -------------------------------------------------------------------- */ +CPLString ACTextUnescape( const char *pszInput, const char *pszEncoding ); + +const unsigned char *ACGetColorTable( void ); + +void ACAdjustText( double dfAngle, double dfScale, OGRFeature *poFeature ); + +#endif /* ndef _OGR_AUTOCAD_SERVICES_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogr_dxf.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogr_dxf.h new file mode 100644 index 000000000..187fc542b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogr_dxf.h @@ -0,0 +1,402 @@ +/****************************************************************************** + * $Id: ogr_dxf.h 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: DXF Translator + * Purpose: Definition of classes for OGR .dxf driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_DXF_H_INCLUDED +#define _OGR_DXF_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "ogr_autocad_services.h" +#include "cpl_conv.h" +#include +#include +#include +#include + +class OGRDXFDataSource; + +/************************************************************************/ +/* DXFBlockDefinition */ +/* */ +/* Container for info about a block. */ +/************************************************************************/ + +class DXFBlockDefinition +{ +public: + DXFBlockDefinition() : poGeometry(NULL) {} + ~DXFBlockDefinition(); + + OGRGeometry *poGeometry; + std::vector apoFeatures; +}; + +/************************************************************************/ +/* OGRDXFBlocksLayer() */ +/************************************************************************/ + +class OGRDXFBlocksLayer : public OGRLayer +{ + OGRDXFDataSource *poDS; + + OGRFeatureDefn *poFeatureDefn; + + int iNextFID; + unsigned int iNextSubFeature; + + std::map::iterator oIt; + + public: + OGRDXFBlocksLayer( OGRDXFDataSource *poDS ); + ~OGRDXFBlocksLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ); + + OGRFeature * GetNextUnfilteredFeature(); +}; + +/************************************************************************/ +/* OGRDXFLayer */ +/************************************************************************/ +class OGRDXFLayer : public OGRLayer +{ + OGRDXFDataSource *poDS; + + OGRFeatureDefn *poFeatureDefn; + int iNextFID; + + std::set oIgnoredEntities; + + std::queue apoPendingFeatures; + void ClearPendingFeatures(); + + std::map oStyleProperties; + + void TranslateGenericProperty( OGRFeature *poFeature, + int nCode, char *pszValue ); + void PrepareLineStyle( OGRFeature *poFeature ); + void ApplyOCSTransformer( OGRGeometry * ); + + OGRFeature * TranslatePOINT(); + OGRFeature * TranslateLINE(); + OGRFeature * TranslatePOLYLINE(); + OGRFeature * TranslateLWPOLYLINE(); + OGRFeature * TranslateCIRCLE(); + OGRFeature * TranslateELLIPSE(); + OGRFeature * TranslateARC(); + OGRFeature * TranslateSPLINE(); + OGRFeature * Translate3DFACE(); + OGRFeature * TranslateINSERT(); + OGRFeature * TranslateMTEXT(); + OGRFeature * TranslateTEXT(); + OGRFeature * TranslateDIMENSION(); + OGRFeature * TranslateHATCH(); + OGRFeature * TranslateSOLID(); + + void FormatDimension( CPLString &osText, double dfValue ); + OGRErr CollectBoundaryPath( OGRGeometryCollection * ); + OGRErr CollectPolylinePath( OGRGeometryCollection * ); + + CPLString TextUnescape( const char * ); + + public: + OGRDXFLayer( OGRDXFDataSource *poDS ); + ~OGRDXFLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ); + + OGRFeature * GetNextUnfilteredFeature(); +}; + +/************************************************************************/ +/* OGRDXFReader */ +/* */ +/* A class for very low level DXF reading without interpretation. */ +/************************************************************************/ + +class OGRDXFReader +{ +public: + OGRDXFReader(); + ~OGRDXFReader(); + + void Initialize( VSILFILE * fp ); + + VSILFILE *fp; + + int iSrcBufferOffset; + int nSrcBufferBytes; + int iSrcBufferFileOffset; + char achSrcBuffer[1025]; + + int nLastValueSize; + int nLineNumber; + + int ReadValue( char *pszValueBuffer, + int nValueBufferSize = 81 ); + void UnreadValue(); + void LoadDiskChunk(); + void ResetReadPointer( int iNewOffset ); +}; + + +/************************************************************************/ +/* OGRDXFDataSource */ +/************************************************************************/ + +class OGRDXFDataSource : public OGRDataSource +{ + VSILFILE *fp; + + CPLString osName; + std::vector apoLayers; + + int iEntitiesSectionOffset; + + std::map oBlockMap; + std::map oHeaderVariables; + + CPLString osEncoding; + + // indexed by layer name, then by property name. + std::map< CPLString, std::map > + oLayerTable; + + std::map oLineTypeTable; + + int bInlineBlocks; + + OGRDXFReader oReader; + + public: + OGRDXFDataSource(); + ~OGRDXFDataSource(); + + int Open( const char * pszFilename, int bHeaderOnly=FALSE ); + + const char *GetName() { return osName; } + + int GetLayerCount() { return apoLayers.size(); } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); + + // The following is only used by OGRDXFLayer + + int InlineBlocks() { return bInlineBlocks; } + void AddStandardFields( OGRFeatureDefn *poDef ); + + // Implemented in ogrdxf_blockmap.cpp + void ReadBlocksSection(); + OGRGeometry *SimplifyBlockGeometry( OGRGeometryCollection * ); + DXFBlockDefinition *LookupBlock( const char *pszName ); + std::map &GetBlockMap() { return oBlockMap; } + + // Layer and other Table Handling (ogrdatasource.cpp) + void ReadTablesSection(); + void ReadLayerDefinition(); + void ReadLineTypeDefinition(); + const char *LookupLayerProperty( const char *pszLayer, + const char *pszProperty ); + const char *LookupLineType( const char *pszName ); + + // Header variables. + void ReadHeaderSection(); + const char *GetVariable(const char *pszName, + const char *pszDefault=NULL ); + + const char *GetEncoding() { return osEncoding; } + + // reader related. + int ReadValue( char *pszValueBuffer, int nValueBufferSize = 81 ) + { return oReader.ReadValue( pszValueBuffer, nValueBufferSize ); } + void RestartEntities() + { oReader.ResetReadPointer(iEntitiesSectionOffset); } + void UnreadValue() + { oReader.UnreadValue(); } + void ResetReadPointer( int iNewOffset ) + { oReader.ResetReadPointer( iNewOffset ); } +}; + +/************************************************************************/ +/* OGRDXFWriterLayer */ +/************************************************************************/ + +class OGRDXFWriterDS; + +class OGRDXFWriterLayer : public OGRLayer +{ + VSILFILE *fp; + OGRFeatureDefn *poFeatureDefn; + + OGRDXFWriterDS *poDS; + + int WriteValue( int nCode, const char *pszValue ); + int WriteValue( int nCode, int nValue ); + int WriteValue( int nCode, double dfValue ); + + OGRErr WriteCore( OGRFeature* ); + OGRErr WritePOINT( OGRFeature* ); + OGRErr WriteTEXT( OGRFeature* ); + OGRErr WritePOLYLINE( OGRFeature*, OGRGeometry* = NULL ); + OGRErr WriteHATCH( OGRFeature*, OGRGeometry* = NULL ); + OGRErr WriteINSERT( OGRFeature* ); + + static CPLString TextEscape( const char * ); + int ColorStringToDXFColor( const char * ); + CPLString PrepareLineTypeDefinition( OGRFeature*, OGRStyleTool* ); + + std::map oNewLineTypes; + int nNextAutoID; + int bWriteHatch; + + public: + OGRDXFWriterLayer( OGRDXFWriterDS *poDS, VSILFILE *fp ); + ~OGRDXFWriterLayer(); + + void ResetReading() {} + OGRFeature *GetNextFeature() { return NULL; } + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ); + OGRErr ICreateFeature( OGRFeature *poFeature ); + OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + + void ResetFP( VSILFILE * ); + + std::map& GetNewLineTypeMap() { return oNewLineTypes;} +}; + +/************************************************************************/ +/* OGRDXFBlocksWriterLayer */ +/************************************************************************/ + +class OGRDXFBlocksWriterLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + + public: + OGRDXFBlocksWriterLayer( OGRDXFWriterDS *poDS ); + ~OGRDXFBlocksWriterLayer(); + + void ResetReading() {} + OGRFeature *GetNextFeature() { return NULL; } + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ); + OGRErr ICreateFeature( OGRFeature *poFeature ); + OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + + std::vector apoBlocks; + OGRFeature *FindBlock( const char * ); +}; + +/************************************************************************/ +/* OGRDXFWriterDS */ +/************************************************************************/ + +class OGRDXFWriterDS : public OGRDataSource +{ + friend class OGRDXFWriterLayer; + + int nNextFID; + + CPLString osName; + OGRDXFWriterLayer *poLayer; + OGRDXFBlocksWriterLayer *poBlocksLayer; + VSILFILE *fp; + CPLString osTrailerFile; + + CPLString osTempFilename; + VSILFILE *fpTemp; + + CPLString osHeaderFile; + OGRDXFDataSource oHeaderDS; + char **papszLayersToCreate; + + vsi_l_offset nHANDSEEDOffset; + + std::vector anDefaultLayerCode; + std::vector aosDefaultLayerText; + + std::set aosUsedEntities; + void ScanForEntities( const char *pszFilename, + const char *pszTarget ); + + int WriteNewLineTypeRecords( VSILFILE *fp ); + int WriteNewBlockRecords( VSILFILE * ); + int WriteNewBlockDefinitions( VSILFILE * ); + int WriteNewLayerDefinitions( VSILFILE * ); + int TransferUpdateHeader( VSILFILE * ); + int TransferUpdateTrailer( VSILFILE * ); + int FixupHANDSEED( VSILFILE * ); + + OGREnvelope oGlobalEnvelope; + + public: + OGRDXFWriterDS(); + ~OGRDXFWriterDS(); + + int Open( const char * pszFilename, + char **papszOptions ); + + const char *GetName() { return osName; } + + int GetLayerCount(); + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); + + OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + + int CheckEntityID( const char *pszEntityID ); + long WriteEntityID( VSILFILE * fp, + long nPreferredFID = OGRNullFID ); + + void UpdateExtent( OGREnvelope* psEnvelope ); +}; + +#endif /* ndef _OGR_DXF_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_blockmap.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_blockmap.cpp new file mode 100644 index 000000000..84b8cd80e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_blockmap.cpp @@ -0,0 +1,179 @@ +/****************************************************************************** + * $Id: ogrdxf_blockmap.cpp 23668 2011-12-30 21:44:47Z rouault $ + * + * Project: DXF Translator + * Purpose: Implements BlockMap reading and management portion of + * OGRDXFDataSource class + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dxf.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" + +CPL_CVSID("$Id: ogrdxf_blockmap.cpp 23668 2011-12-30 21:44:47Z rouault $"); + +/************************************************************************/ +/* ReadBlockSection() */ +/************************************************************************/ + +void OGRDXFDataSource::ReadBlocksSection() + +{ + char szLineBuf[257]; + int nCode; + OGRDXFLayer *poReaderLayer = (OGRDXFLayer *) GetLayerByName( "Entities" ); + int bMergeBlockGeometries = CSLTestBoolean( + CPLGetConfigOption( "DXF_MERGE_BLOCK_GEOMETRIES", "TRUE" ) ); + + iEntitiesSectionOffset = oReader.iSrcBufferFileOffset + oReader.iSrcBufferOffset; + + while( (nCode = ReadValue( szLineBuf, sizeof(szLineBuf) )) > -1 + && !EQUAL(szLineBuf,"ENDSEC") ) + { + // We are only interested in extracting blocks. + if( nCode != 0 || !EQUAL(szLineBuf,"BLOCK") ) + continue; + + // Process contents of BLOCK definition till we find the + // first entity. + CPLString osBlockName; + + while( (nCode = ReadValue( szLineBuf,sizeof(szLineBuf) )) > 0 ) + { + if( nCode == 2 ) + osBlockName = szLineBuf; + + // anything else we want? + } + + if( EQUAL(szLineBuf,"ENDBLK") ) + continue; + + if (nCode >= 0) + UnreadValue(); + + // Now we will process entities till we run out at the ENDBLK code. + // we aggregate the geometries of the features into a multi-geometry, + // but throw away other stuff attached to the features. + + OGRFeature *poFeature; + OGRGeometryCollection *poColl = new OGRGeometryCollection(); + std::vector apoFeatures; + + while( (poFeature = poReaderLayer->GetNextUnfilteredFeature()) != NULL ) + { + if( (poFeature->GetStyleString() != NULL + && strstr(poFeature->GetStyleString(),"LABEL") != NULL) + || !bMergeBlockGeometries ) + { + apoFeatures.push_back( poFeature ); + } + else + { + poColl->addGeometryDirectly( poFeature->StealGeometry() ); + delete poFeature; + } + } + + if( poColl->getNumGeometries() == 0 ) + delete poColl; + else + oBlockMap[osBlockName].poGeometry = SimplifyBlockGeometry(poColl); + + if( apoFeatures.size() > 0 ) + oBlockMap[osBlockName].apoFeatures = apoFeatures; + } + + CPLDebug( "DXF", "Read %d blocks with meaningful geometry.", + (int) oBlockMap.size() ); +} + +/************************************************************************/ +/* SimplifyBlockGeometry() */ +/************************************************************************/ + +OGRGeometry *OGRDXFDataSource::SimplifyBlockGeometry( + OGRGeometryCollection *poCollection ) + +{ +/* -------------------------------------------------------------------- */ +/* If there is only one geometry in the collection, just return */ +/* it. */ +/* -------------------------------------------------------------------- */ + if( poCollection->getNumGeometries() == 1 ) + { + OGRGeometry *poReturn = poCollection->getGeometryRef(0); + poCollection->removeGeometry(0,FALSE); + delete poCollection; + return poReturn; + } + +/* -------------------------------------------------------------------- */ +/* Eventually we likely ought to have logic to convert to */ +/* polygon, multipolygon, multilinestring or multipoint but */ +/* I'll put that off till it would be meaningful. */ +/* -------------------------------------------------------------------- */ + + return poCollection; +} + +/************************************************************************/ +/* LookupBlock() */ +/* */ +/* Find the geometry collection corresponding to a name if it */ +/* exists. Note that the returned geometry pointer is to a */ +/* geometry that continues to be owned by the datasource. It */ +/* should be cloned for use. */ +/************************************************************************/ + +DXFBlockDefinition *OGRDXFDataSource::LookupBlock( const char *pszName ) + +{ + CPLString osName = pszName; + + if( oBlockMap.count( osName ) == 0 ) + return NULL; + else + return &(oBlockMap[osName]); +} + +/************************************************************************/ +/* ~DXFBlockDefinition() */ +/* */ +/* Safe cleanup of a block definition. */ +/************************************************************************/ + +DXFBlockDefinition::~DXFBlockDefinition() + +{ + delete poGeometry; + + while( !apoFeatures.empty() ) + { + delete apoFeatures.back(); + apoFeatures.pop_back(); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_dimension.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_dimension.cpp new file mode 100644 index 000000000..717bd5d9a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_dimension.cpp @@ -0,0 +1,374 @@ +/****************************************************************************** + * $Id: ogrdxf_dimension.cpp 28039 2014-11-30 18:24:59Z rouault $ + * + * Project: DXF Translator + * Purpose: Implements translation support for DIMENSION elements as a part + * of the OGRDXFLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dxf.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrdxf_dimension.cpp 28039 2014-11-30 18:24:59Z rouault $"); + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* TranslateDIMENSION() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::TranslateDIMENSION() + +{ + char szLineBuf[257]; + int nCode /*, nDimType = 0 */; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + double dfArrowX1 = 0.0, dfArrowY1 = 0.0 /*, dfArrowZ1 = 0.0 */; + double dfTargetX1 = 0.0, dfTargetY1 = 0.0 /* , dfTargetZ1 = 0.0 */; + double dfTargetX2 = 0.0, dfTargetY2 = 0.0 /* , dfTargetZ2 = 0.0 */; + double dfTextX = 0.0, dfTextY = 0.0 /* , dfTextZ = 0.0 */; + double dfAngle = 0.0; + double dfHeight = CPLAtof(poDS->GetVariable("$DIMTXT", "2.5")); + + CPLString osText; + + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 10: + dfArrowX1 = CPLAtof(szLineBuf); + break; + + case 20: + dfArrowY1 = CPLAtof(szLineBuf); + break; + + case 30: + /* dfArrowZ1 = CPLAtof(szLineBuf); */ + break; + + case 11: + dfTextX = CPLAtof(szLineBuf); + break; + + case 21: + dfTextY = CPLAtof(szLineBuf); + break; + + case 31: + /* dfTextZ = CPLAtof(szLineBuf); */ + break; + + case 13: + dfTargetX2 = CPLAtof(szLineBuf); + break; + + case 23: + dfTargetY2 = CPLAtof(szLineBuf); + break; + + case 33: + /* dfTargetZ2 = CPLAtof(szLineBuf); */ + break; + + case 14: + dfTargetX1 = CPLAtof(szLineBuf); + break; + + case 24: + dfTargetY1 = CPLAtof(szLineBuf); + break; + + case 34: + /* dfTargetZ1 = CPLAtof(szLineBuf); */ + break; + + case 70: + /* nDimType = atoi(szLineBuf); */ + break; + + case 1: + osText = szLineBuf; + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + if( nCode == 0 ) + poDS->UnreadValue(); + +/************************************************************************* + + DIMENSION geometry layout + + (11,21)(text center point) + | DimText | +(10,20) X<--------------------------------->X (Arrow2 - computed) +(Arrow1)| | + | | + | X (13,23) (Target2) + | + X (14,24) (Target1) + + +Given: + Locations Arrow1, Target1, and Target2 we need to compute Arrow2. + +Steps: + 1) Compute direction vector from Target1 to Arrow1 (Vec1). + 2) Compute direction vector for arrow as perpendicular to Vec1 (call Vec2). + 3) Compute Arrow2 location as intersection between line defined by + Vec2 and Arrow1 and line defined by Target2 and direction Vec1 (call Arrow2) + +Then we can draw lines for the various components. + +Note that Vec1 and Vec2 may be horizontal, vertical or on an angle but +the approach is as above in all these cases. + +*************************************************************************/ + + ; + +/* -------------------------------------------------------------------- */ +/* Step 1, compute direction vector between Target1 and Arrow1. */ +/* -------------------------------------------------------------------- */ + double dfVec1X, dfVec1Y; + + dfVec1X = (dfArrowX1 - dfTargetX1); + dfVec1Y = (dfArrowY1 - dfTargetY1); + +/* -------------------------------------------------------------------- */ +/* Step 2, compute the direction vector from Arrow1 to Arrow2 */ +/* as a perpendicluar to Vec1. */ +/* -------------------------------------------------------------------- */ + double dfVec2X, dfVec2Y; + + dfVec2X = dfVec1Y; + dfVec2Y = -dfVec1X; + +/* -------------------------------------------------------------------- */ +/* Step 3, compute intersection of line from target2 along */ +/* direction vector 1, with the line through Arrow1 and */ +/* direction vector 2. */ +/* -------------------------------------------------------------------- */ + double dfL1M, dfL1B, dfL2M, dfL2B; + double dfArrowX2, dfArrowY2; + + // special case if vec1 is vertical. + if( dfVec1X == 0.0 ) + { + dfArrowX2 = dfTargetX2; + dfArrowY2 = dfArrowY1; + } + + // special case if vec2 is horizontal. + else if( dfVec1Y == 0.0 ) + { + dfArrowX2 = dfArrowX1; + dfArrowY2 = dfTargetY2; + } + + else // General case for diagonal vectors. + { + // first convert vec1 + target2 into y = mx + b format: call this L1 + + dfL1M = dfVec1Y / dfVec1X; + dfL1B = dfTargetY2 - dfL1M * dfTargetX2; + + // convert vec2 + Arrow1 into y = mx + b format, call this L2 + + dfL2M = dfVec2Y / dfVec2X; + dfL2B = dfArrowY1 - dfL2M * dfArrowX1; + + // Compute intersection x = (b2-b1) / (m1-m2) + + dfArrowX2 = (dfL2B - dfL1B) / (dfL1M-dfL2M); + dfArrowY2 = dfL2M * dfArrowX2 + dfL2B; + } + +/* -------------------------------------------------------------------- */ +/* Compute the text angle. */ +/* -------------------------------------------------------------------- */ + dfAngle = atan2(dfVec2Y,dfVec2X) * 180.0 / PI; + +/* -------------------------------------------------------------------- */ +/* Rescale the direction vectors so we can use them in */ +/* constructing arrowheads. We want them to be about 3% of the */ +/* length of line on which the arrows will be drawn. */ +/* -------------------------------------------------------------------- */ +#define VECTOR_LEN(x,y) sqrt( (x)*(x) + (y)*(y) ) +#define POINT_DIST(x1,y1,x2,y2) VECTOR_LEN((x2-x1),(y2-y1)) + + double dfBaselineLength = POINT_DIST(dfArrowX1,dfArrowY1, + dfArrowX2,dfArrowY2); + double dfTargetLength = dfBaselineLength * 0.03; + double dfScaleFactor; + + // recompute vector 2 to ensure the direction is regular + dfVec2X = (dfArrowX2 - dfArrowX1); + dfVec2Y = (dfArrowY2 - dfArrowY1); + + // vector 1 + dfScaleFactor = dfTargetLength / VECTOR_LEN(dfVec1X,dfVec1Y); + dfVec1X *= dfScaleFactor; + dfVec1Y *= dfScaleFactor; + + // vector 2 + dfScaleFactor = dfTargetLength / VECTOR_LEN(dfVec2X,dfVec2Y); + dfVec2X *= dfScaleFactor; + dfVec2Y *= dfScaleFactor; + +/* -------------------------------------------------------------------- */ +/* Create geometries for the different components of the */ +/* dimension object. */ +/* -------------------------------------------------------------------- */ + OGRMultiLineString *poMLS = new OGRMultiLineString(); + OGRLineString oLine; + + // main arrow line between Arrow1 and Arrow2 + oLine.setPoint( 0, dfArrowX1, dfArrowY1 ); + oLine.setPoint( 1, dfArrowX2, dfArrowY2 ); + poMLS->addGeometry( &oLine ); + + // dimension line from Target1 to Arrow1 with a small extension. + oLine.setPoint( 0, dfTargetX1, dfTargetY1 ); + oLine.setPoint( 1, dfArrowX1 + dfVec1X, dfArrowY1 + dfVec1Y ); + poMLS->addGeometry( &oLine ); + + // dimension line from Target2 to Arrow2 with a small extension. + oLine.setPoint( 0, dfTargetX2, dfTargetY2 ); + oLine.setPoint( 1, dfArrowX2 + dfVec1X, dfArrowY2 + dfVec1Y ); + poMLS->addGeometry( &oLine ); + + // add arrow1 arrow head. + + oLine.setPoint( 0, dfArrowX1, dfArrowY1 ); + oLine.setPoint( 1, + dfArrowX1 + dfVec2X*3 + dfVec1X, + dfArrowY1 + dfVec2Y*3 + dfVec1Y ); + poMLS->addGeometry( &oLine ); + + oLine.setPoint( 0, dfArrowX1, dfArrowY1 ); + oLine.setPoint( 1, + dfArrowX1 + dfVec2X*3 - dfVec1X, + dfArrowY1 + dfVec2Y*3 - dfVec1Y ); + poMLS->addGeometry( &oLine ); + + // add arrow2 arrow head. + + oLine.setPoint( 0, dfArrowX2, dfArrowY2 ); + oLine.setPoint( 1, + dfArrowX2 - dfVec2X*3 + dfVec1X, + dfArrowY2 - dfVec2Y*3 + dfVec1Y ); + poMLS->addGeometry( &oLine ); + + oLine.setPoint( 0, dfArrowX2, dfArrowY2 ); + oLine.setPoint( 1, + dfArrowX2 - dfVec2X*3 - dfVec1X, + dfArrowY2 - dfVec2Y*3 - dfVec1Y ); + poMLS->addGeometry( &oLine ); + + poFeature->SetGeometryDirectly( poMLS ); + + PrepareLineStyle( poFeature ); + +/* -------------------------------------------------------------------- */ +/* Prepare a new feature to serve as the dimension text label */ +/* feature. We will push it onto the layer as a pending */ +/* feature for the next feature read. */ +/* -------------------------------------------------------------------- */ + + // a single space suppresses labelling. + if( osText == " " ) + return poFeature; + + OGRFeature *poLabelFeature = poFeature->Clone(); + + poLabelFeature->SetGeometryDirectly( new OGRPoint( dfTextX, dfTextY ) ); + + // Do we need to compute the dimension value? + if( osText.size() == 0 ) + { + FormatDimension( osText, POINT_DIST( dfArrowX1, dfArrowY1, + dfArrowX2, dfArrowY2 ) ); + } + + CPLString osStyle; + char szBuffer[64]; + + osStyle.Printf("LABEL(f:\"Arial\",t:\"%s\",p:5",osText.c_str()); + + if( dfAngle != 0.0 ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle); + osStyle += CPLString().Printf(",a:%s", szBuffer); + } + + if( dfHeight != 0.0 ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight); + osStyle += CPLString().Printf(",s:%sg", szBuffer); + } + + // add color! + + osStyle += ")"; + + poLabelFeature->SetStyleString( osStyle ); + + apoPendingFeatures.push( poLabelFeature ); + + return poFeature; +} + +/************************************************************************/ +/* FormatDimension() */ +/* */ +/* Format a dimension number according to the current files */ +/* formatting conventions. */ +/************************************************************************/ + +void OGRDXFLayer::FormatDimension( CPLString &osText, double dfValue ) + +{ + int nPrecision = atoi(poDS->GetVariable("$LUPREC","4")); + char szFormat[32]; + char szBuffer[64]; + + // we could do a significantly more precise formatting if we want + // to spend the effort. See QCAD's rs_dimlinear.cpp and related files + // for example. + + sprintf(szFormat, "%%.%df", nPrecision ); + CPLsnprintf(szBuffer, sizeof(szBuffer), szFormat, dfValue); + osText = szBuffer; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_hatch.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_hatch.cpp new file mode 100644 index 000000000..ab841ac4b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_hatch.cpp @@ -0,0 +1,538 @@ +/****************************************************************************** + * $Id: ogrdxf_hatch.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: DXF Translator + * Purpose: Implements translation support for HATCH elements as part + * of the OGRDXFLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dxf.h" +#include "cpl_conv.h" +#include "ogr_api.h" + +#include "ogrdxf_polyline_smooth.h" + +CPL_CVSID("$Id: ogrdxf_hatch.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* TranslateHATCH() */ +/* */ +/* We mostly just try to convert hatch objects as polygons or */ +/* multipolygons representing the hatched area. It is hard to */ +/* preserve the actual details of the hatching. */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::TranslateHATCH() + +{ + char szLineBuf[257]; + int nCode; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + CPLString osHatchPattern; + /* int nFillFlag = 0; */ + OGRGeometryCollection oGC; + + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 70: + /* nFillFlag = atoi(szLineBuf); */ + break; + + case 2: + osHatchPattern = szLineBuf; + poFeature->SetField( "Text", osHatchPattern.c_str() ); + break; + + case 91: + { + int nBoundaryPathCount = atoi(szLineBuf); + int iBoundary; + + for( iBoundary = 0; iBoundary < nBoundaryPathCount; iBoundary++ ) + { + if (CollectBoundaryPath( &oGC ) != OGRERR_NONE) + break; + } + } + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + if( nCode == 0 ) + poDS->UnreadValue(); + +/* -------------------------------------------------------------------- */ +/* Try to turn the set of lines into something useful. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr; + + OGRGeometry* poFinalGeom = (OGRGeometry *) + OGRBuildPolygonFromEdges( (OGRGeometryH) &oGC, + TRUE, TRUE, 0.0000001, &eErr ); + if( eErr != OGRERR_NONE ) + { + delete poFinalGeom; + OGRMultiLineString* poMLS = new OGRMultiLineString(); + for(int i=0;iaddGeometry(oGC.getGeometryRef(i)); + poFinalGeom = poMLS; + } + + ApplyOCSTransformer( poFinalGeom ); + poFeature->SetGeometryDirectly( poFinalGeom ); + +/* -------------------------------------------------------------------- */ +/* Work out the color for this feature. For now we just assume */ +/* solid fill. We cannot trivially translate the various sorts */ +/* of hatching. */ +/* -------------------------------------------------------------------- */ + CPLString osLayer = poFeature->GetFieldAsString("Layer"); + + int nColor = 256; + + if( oStyleProperties.count("Color") > 0 ) + nColor = atoi(oStyleProperties["Color"]); + + // Use layer color? + if( nColor < 1 || nColor > 255 ) + { + const char *pszValue = poDS->LookupLayerProperty( osLayer, "Color" ); + if( pszValue != NULL ) + nColor = atoi(pszValue); + } + +/* -------------------------------------------------------------------- */ +/* Setup the style string. */ +/* -------------------------------------------------------------------- */ + if( nColor >= 1 && nColor <= 255 ) + { + CPLString osStyle; + const unsigned char *pabyDXFColors = ACGetColorTable(); + + osStyle.Printf( "BRUSH(fc:#%02x%02x%02x)", + pabyDXFColors[nColor*3+0], + pabyDXFColors[nColor*3+1], + pabyDXFColors[nColor*3+2] ); + + poFeature->SetStyleString( osStyle ); + } + + return poFeature; +} + +/************************************************************************/ +/* CollectBoundaryPath() */ +/************************************************************************/ + +OGRErr OGRDXFLayer::CollectBoundaryPath( OGRGeometryCollection *poGC ) + +{ + int nCode; + char szLineBuf[257]; + +/* -------------------------------------------------------------------- */ +/* Read the boundary path type. */ +/* -------------------------------------------------------------------- */ + nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf)); + if( nCode != 92 ) + return OGRERR_FAILURE; + + int nBoundaryPathType = atoi(szLineBuf); + +/* ==================================================================== */ +/* Handle polyline loops. */ +/* ==================================================================== */ + if( nBoundaryPathType & 0x02 ) + return CollectPolylinePath( poGC ); + +/* ==================================================================== */ +/* Handle non-polyline loops. */ +/* ==================================================================== */ + +/* -------------------------------------------------------------------- */ +/* Read number of edges. */ +/* -------------------------------------------------------------------- */ + nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf)); + if( nCode != 93 ) + return OGRERR_FAILURE; + + int nEdgeCount = atoi(szLineBuf); + +/* -------------------------------------------------------------------- */ +/* Loop reading edges. */ +/* -------------------------------------------------------------------- */ + int iEdge; + + for( iEdge = 0; iEdge < nEdgeCount; iEdge++ ) + { +/* -------------------------------------------------------------------- */ +/* Read the edge type. */ +/* -------------------------------------------------------------------- */ +#define ET_LINE 1 +#define ET_CIRCULAR_ARC 2 +#define ET_ELLIPTIC_ARC 3 +#define ET_SPLINE 4 + + nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf)); + if( nCode != 72 ) + return OGRERR_FAILURE; + + int nEdgeType = atoi(szLineBuf); + +/* -------------------------------------------------------------------- */ +/* Process a line edge. */ +/* -------------------------------------------------------------------- */ + if( nEdgeType == ET_LINE ) + { + double dfStartX; + double dfStartY; + double dfEndX; + double dfEndY; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 10 ) + dfStartX = CPLAtof(szLineBuf); + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 20 ) + dfStartY = CPLAtof(szLineBuf); + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 11 ) + dfEndX = CPLAtof(szLineBuf); + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 21 ) + dfEndY = CPLAtof(szLineBuf); + else + break; + + OGRLineString *poLS = new OGRLineString(); + + poLS->addPoint( dfStartX, dfStartY ); + poLS->addPoint( dfEndX, dfEndY ); + + poGC->addGeometryDirectly( poLS ); + } +/* -------------------------------------------------------------------- */ +/* Process a circular arc. */ +/* -------------------------------------------------------------------- */ + else if( nEdgeType == ET_CIRCULAR_ARC ) + { + double dfCenterX; + double dfCenterY; + double dfRadius; + double dfStartAngle; + double dfEndAngle; + int bCounterClockwise = FALSE; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 10 ) + dfCenterX = CPLAtof(szLineBuf); + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 20 ) + dfCenterY = CPLAtof(szLineBuf); + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 40 ) + dfRadius = CPLAtof(szLineBuf); + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 50 ) + dfStartAngle = CPLAtof(szLineBuf); + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 51 ) + dfEndAngle = CPLAtof(szLineBuf); + else + break; + + if( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) == 73 ) + bCounterClockwise = atoi(szLineBuf); + else if (nCode >= 0) + poDS->UnreadValue(); + + if( dfStartAngle > dfEndAngle ) + dfEndAngle += 360.0; + if( bCounterClockwise ) + { + dfStartAngle *= -1; + dfEndAngle *= -1; + } + + OGRGeometry *poArc = OGRGeometryFactory::approximateArcAngles( + dfCenterX, dfCenterY, 0.0, + dfRadius, dfRadius, 0.0, + dfStartAngle, dfEndAngle, 0.0 ); + + poArc->flattenTo2D(); + + poGC->addGeometryDirectly( poArc ); + } + +/* -------------------------------------------------------------------- */ +/* Process an elliptical arc. */ +/* -------------------------------------------------------------------- */ + else if( nEdgeType == ET_ELLIPTIC_ARC ) + { + double dfCenterX; + double dfCenterY; + double dfMajorRadius, dfMinorRadius; + double dfMajorX, dfMajorY; + double dfStartAngle; + double dfEndAngle; + double dfRotation; + double dfRatio; + int bCounterClockwise = FALSE; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 10 ) + dfCenterX = CPLAtof(szLineBuf); + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 20 ) + dfCenterY = CPLAtof(szLineBuf); + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 11 ) + dfMajorX = CPLAtof(szLineBuf); + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 21 ) + dfMajorY = CPLAtof(szLineBuf); + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 40 ) + dfRatio = CPLAtof(szLineBuf) / 100.0; + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 50 ) + dfStartAngle = CPLAtof(szLineBuf); + else + break; + + if( poDS->ReadValue(szLineBuf,sizeof(szLineBuf)) == 51 ) + dfEndAngle = CPLAtof(szLineBuf); + else + break; + + if( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) == 73 ) + bCounterClockwise = atoi(szLineBuf); + else if (nCode >= 0) + poDS->UnreadValue(); + + if( dfStartAngle > dfEndAngle ) + dfEndAngle += 360.0; + if( bCounterClockwise ) + { + dfStartAngle *= -1; + dfEndAngle *= -1; + } + + dfMajorRadius = sqrt( dfMajorX * dfMajorX + dfMajorY * dfMajorY ); + dfMinorRadius = dfMajorRadius * dfRatio; + + dfRotation = -1 * atan2( dfMajorY, dfMajorX ) * 180 / PI; + + OGRGeometry *poArc = OGRGeometryFactory::approximateArcAngles( + dfCenterX, dfCenterY, 0.0, + dfMajorRadius, dfMinorRadius, dfRotation, + dfStartAngle, dfEndAngle, 0.0 ); + + poArc->flattenTo2D(); + + poGC->addGeometryDirectly( poArc ); + } + else + { + CPLDebug( "DXF", "Unsupported HATCH boundary line type:%d", + nEdgeType ); + return OGRERR_UNSUPPORTED_OPERATION; + } + } + +/* -------------------------------------------------------------------- */ +/* Skip through source boundary objects if present. */ +/* -------------------------------------------------------------------- */ + nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf)); + if( nCode != 97 ) + { + if (nCode < 0) + return OGRERR_FAILURE; + poDS->UnreadValue(); + } + else + { + int iObj, nObjCount = atoi(szLineBuf); + + for( iObj = 0; iObj < nObjCount; iObj++ ) + { + if (poDS->ReadValue( szLineBuf, sizeof(szLineBuf) ) < 0) + return OGRERR_FAILURE; + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CollectPolylinePath() */ +/************************************************************************/ + +OGRErr OGRDXFLayer::CollectPolylinePath( OGRGeometryCollection *poGC ) + +{ + int nCode; + char szLineBuf[257]; + DXFSmoothPolyline oSmoothPolyline; + double dfBulge = 0.0; + double dfX = 0.0, dfY = 0.0; + int bHaveX = FALSE, bHaveY = FALSE; + int bIsClosed = FALSE; + int nVertexCount = -1; + int bHaveBulges = FALSE; + +/* -------------------------------------------------------------------- */ +/* Read the boundary path type. */ +/* -------------------------------------------------------------------- */ + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + if( nVertexCount > 0 && (int) oSmoothPolyline.size() == nVertexCount ) + break; + + switch( nCode ) + { + case 93: + nVertexCount = atoi(szLineBuf); + break; + + case 72: + bHaveBulges = atoi(szLineBuf); + break; + + case 73: + bIsClosed = atoi(szLineBuf); + break; + + case 10: + if( bHaveX && bHaveY ) + { + oSmoothPolyline.AddPoint(dfX, dfY, 0.0, dfBulge); + dfBulge = 0.0; + bHaveY = FALSE; + } + dfX = CPLAtof(szLineBuf); + bHaveX = TRUE; + break; + + case 20: + if( bHaveX && bHaveY ) + { + oSmoothPolyline.AddPoint( dfX, dfY, 0.0, dfBulge ); + dfBulge = 0.0; + bHaveX = bHaveY = FALSE; + } + dfY = CPLAtof(szLineBuf); + bHaveY = TRUE; + if( bHaveX && bHaveY && !bHaveBulges ) + { + oSmoothPolyline.AddPoint( dfX, dfY, 0.0, dfBulge ); + dfBulge = 0.0; + bHaveX = bHaveY = FALSE; + } + break; + + case 42: + dfBulge = CPLAtof(szLineBuf); + if( bHaveX && bHaveY ) + { + oSmoothPolyline.AddPoint( dfX, dfY, 0.0, dfBulge ); + dfBulge = 0.0; + bHaveX = bHaveY = FALSE; + } + break; + + default: + break; + } + } + + if( nCode != 10 && nCode != 20 && nCode != 42 && nCode >= 0) + poDS->UnreadValue(); + + if( bHaveX && bHaveY ) + oSmoothPolyline.AddPoint(dfX, dfY, 0.0, dfBulge); + + if( bIsClosed ) + oSmoothPolyline.Close(); + + poGC->addGeometryDirectly( oSmoothPolyline.Tesselate() ); + +/* -------------------------------------------------------------------- */ +/* Skip through source boundary objects if present. */ +/* -------------------------------------------------------------------- */ + nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf)); + if( nCode != 97 ) + { + if (nCode < 0) + return OGRERR_FAILURE; + poDS->UnreadValue(); + } + else + { + int iObj, nObjCount = atoi(szLineBuf); + + for( iObj = 0; iObj < nObjCount; iObj++ ) + { + if (poDS->ReadValue( szLineBuf, sizeof(szLineBuf) ) < 0) + return OGRERR_FAILURE; + } + } + return OGRERR_NONE; +} + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_polyline_smooth.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_polyline_smooth.cpp new file mode 100644 index 000000000..f950d3b0f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_polyline_smooth.cpp @@ -0,0 +1,370 @@ +/****************************************************************************** + * File: ogrdxf_polyline_smooth.cpp + * + * Project: Interpolation support for smooth POLYLINE and LWPOLYLINE entities. + * Purpose: Implementation of classes for OGR .dxf driver. + * Author: TJ Snider, timsn@thtree.com + * Ray Gardener, Daylon Graphics Ltd. + * + ****************************************************************************** + * Copyright (c) 2010 Daylon Graphics Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "stdlib.h" +#include "math.h" +#include "ogrdxf_polyline_smooth.h" + + +/************************************************************************/ +/* Local helper functions */ +/************************************************************************/ + +static double GetRadius(double bulge, double length) +{ + const double h = (bulge * length) / 2; + return (h / 2) + (length * length / (8 * h)); +} + + +static double GetLength +( + const DXFSmoothPolylineVertex& start, + const DXFSmoothPolylineVertex& end +) +{ + return sqrt(pow(end.x - start.x, 2) + pow(end.y - start.y, 2)); +} + + +static double GetAngle +( + const DXFSmoothPolylineVertex& start, + const DXFSmoothPolylineVertex& end +) +{ + return atan2((start.y - end.y), (start.x - end.x)) * 180.0 / M_PI; +} + + +static double GetOGRangle(double angle) +{ + return angle > 0.0 + ? -(angle - 180.0) + : -(angle + 180.0); +} + + +/************************************************************************/ +/* DXFSmoothPolyline::Tesselate() */ +/************************************************************************/ + +OGRGeometry* DXFSmoothPolyline::Tesselate() const +{ + assert(!m_vertices.empty()); + + +/* -------------------------------------------------------------------- */ +/* If polyline is one vertex, convert it to a point */ +/* -------------------------------------------------------------------- */ + + if(m_vertices.size() == 1) + { + OGRPoint* poPt = new OGRPoint(m_vertices[0].x, m_vertices[0].y, m_vertices[0].z); + if(m_vertices[0].z == 0 || m_dim == 2) + poPt->flattenTo2D(); + return poPt; + } + + +/* -------------------------------------------------------------------- */ +/* Otherwise, presume a line string */ +/* -------------------------------------------------------------------- */ + + OGRLineString* poLS = new OGRLineString; + + m_blinestringstarted = false; + + std::vector::const_iterator iter = m_vertices.begin(); + std::vector::const_iterator eiter = m_vertices.end(); + + eiter--; + + DXFSmoothPolylineVertex begin = *iter; + + double dfZ = 0.0; + const bool bConstantZ = this->HasConstantZ(dfZ); + + while(iter != eiter) + { + iter++; + DXFSmoothPolylineVertex end = *iter; + + const double len = GetLength(begin,end); + + if((len == 0) || (begin.bulge == 0)) + { + this->EmitLine(begin, end, poLS, bConstantZ, dfZ); + } + else + { + const double radius = GetRadius(begin.bulge,len); + this->EmitArc(begin, end, radius, len, begin.bulge, poLS, dfZ); + } + + // Move to next vertex + begin = end; + } + + +/* -------------------------------------------------------------------- */ +/* Flatten to 2D if necessary */ +/* -------------------------------------------------------------------- */ + + if(bConstantZ && dfZ == 0.0 && m_dim == 2) + poLS->flattenTo2D(); + + +/* -------------------------------------------------------------------- */ +/* If polyline is closed, convert linestring to a linear ring */ +/* */ +/* Actually, on review I'm not convinced this is a good idea. */ +/* Note that most (all) "filled polygons" are expressed with */ +/* hatches which are now handled fairly well and they tend to */ +/* echo linear polylines. */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + if(m_bClosed) + { + OGRLinearRing *poLR = new OGRLinearRing(); + poLR->addSubLineString( poLS, 0 ); + delete poLS; + + // Wrap as polygon. + OGRPolygon *poPoly = new OGRPolygon(); + poPoly->addRingDirectly( poLR ); + + return poPoly; + } +#endif + + return poLS; +} + + +/************************************************************************/ +/* DXFSmoothPolyline::EmitArc() */ +/************************************************************************/ + +void DXFSmoothPolyline::EmitArc +( + const DXFSmoothPolylineVertex& start, + const DXFSmoothPolylineVertex& end, + double radius, double len, double bulge, + OGRLineString* poLS, + double dfZ +) const +{ + assert(poLS); + + double ogrArcRotation = 0.0, + ogrArcRadius = fabs(radius); + + +/* -------------------------------------------------------------------- */ +/* Set arc's direction and keep bulge positive */ +/* -------------------------------------------------------------------- */ + + const bool bClockwise = (bulge < 0); + + if(bClockwise) + bulge *= -1; + + +/* -------------------------------------------------------------------- */ +/* Get arc's center point */ +/* -------------------------------------------------------------------- */ + + const double saggita = fabs(bulge * (len / 2.0)); + const double apo = bClockwise + ? -(ogrArcRadius - saggita) + : -(saggita - ogrArcRadius); + + DXFSmoothPolylineVertex v; + v.x = start.x - end.x; + v.y = start.y - end.y; + +#ifdef notdef + const bool bMathissue = (v.x == 0.0 || v.y == 0.0); +#endif + + DXFSmoothPolylineVertex midpoint; + midpoint.x = end.x + 0.5 * v.x; + midpoint.y = end.y + 0.5 * v.y; + + DXFSmoothPolylineVertex pperp; + pperp.x = v.y; + pperp.y = -v.x; + pperp.normalize(); + + DXFSmoothPolylineVertex ogrArcCenter; + ogrArcCenter.x = midpoint.x + (pperp.x * apo); + ogrArcCenter.y = midpoint.y + (pperp.y * apo); + + +/* -------------------------------------------------------------------- */ +/* Get the line's general vertical direction (-1 = down, +1 = up) */ +/* -------------------------------------------------------------------- */ + + const double linedir = end.y > start.y ? 1.0 : -1.0; + + +/* -------------------------------------------------------------------- */ +/* Get arc's starting angle. */ +/* -------------------------------------------------------------------- */ + + double a = GetAngle(ogrArcCenter, start); + + if(bClockwise && (linedir == 1.0)) + a += (linedir * 180.0); + + double ogrArcStartAngle = GetOGRangle(a); + + +/* -------------------------------------------------------------------- */ +/* Get arc's ending angle. */ +/* -------------------------------------------------------------------- */ + + a = GetAngle(ogrArcCenter, end); + + if(bClockwise && (linedir == 1.0)) + a += (linedir * 180.0); + + double ogrArcEndAngle = GetOGRangle(a); + + if(!bClockwise && (ogrArcStartAngle < ogrArcEndAngle)) + ogrArcEndAngle = -180.0 + (linedir * a); + + if(bClockwise && (ogrArcStartAngle > ogrArcEndAngle)) + ogrArcEndAngle += 360.0; + +/* -------------------------------------------------------------------- */ +/* Flip arc's rotation if necessary. */ +/* -------------------------------------------------------------------- */ + + if(bClockwise && (linedir == 1.0)) + ogrArcRotation = linedir * 180.0; + + +/* -------------------------------------------------------------------- */ +/* Tesselate the arc segment and append to the linestring. */ +/* -------------------------------------------------------------------- */ + + OGRLineString* poArcpoLS = + (OGRLineString*)OGRGeometryFactory::approximateArcAngles( + ogrArcCenter.x, ogrArcCenter.y, dfZ, + ogrArcRadius, ogrArcRadius, ogrArcRotation, + ogrArcStartAngle, ogrArcEndAngle, + 0.0); + + poLS->addSubLineString(poArcpoLS); + + delete poArcpoLS; +} + + + +/************************************************************************/ +/* DXFSmoothPolyline::EmitLine() */ +/************************************************************************/ + +void DXFSmoothPolyline::EmitLine +( + const DXFSmoothPolylineVertex& start, + const DXFSmoothPolylineVertex& end, + OGRLineString* poLS, + bool bConstantZ, + double dfZ +) const +{ + assert(poLS); + + if(!m_blinestringstarted) + { + poLS->addPoint(start.x, start.y, + bConstantZ ? dfZ : start.z); + m_blinestringstarted = true; + } + + poLS->addPoint(end.x, end.y, + bConstantZ ? dfZ : end.z); +} + + +/************************************************************************/ +/* DXFSmoothPolyline::Close() */ +/************************************************************************/ + +void DXFSmoothPolyline::Close() +{ + assert(!m_bClosed); + + if(m_vertices.size() >= 2) + { + const bool bVisuallyClosed = + (m_vertices[m_vertices.size() - 1].shares_2D_pos(m_vertices[0])); + + if(!bVisuallyClosed) + { + m_vertices.push_back(m_vertices[0]); + } + m_bClosed = true; + } +} + + +/************************************************************************/ +/* DXFSmoothPolyline::HasConstantZ() */ +/************************************************************************/ + +bool DXFSmoothPolyline::HasConstantZ(double& dfZ) const +{ + // Treat the polyline as having constant Z if all Z members + // are equal or if any bulge attribute exists. In the latter case, + // set dfZ to zero. Leave dfZ unassigned if false is returned. + + assert(!m_vertices.empty()); + + const double d = m_vertices[0].z; + + for(size_t i = 1; i < m_vertices.size(); i++) + { + if(m_vertices[i].bulge != 0.0) + { + dfZ = 0.0; + return true; + } + if(m_vertices[i].z != d) + return false; + } + dfZ = d; + return true; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_polyline_smooth.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_polyline_smooth.h new file mode 100644 index 000000000..a785d8369 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxf_polyline_smooth.h @@ -0,0 +1,156 @@ +/****************************************************************************** + * File: ogrdxf_polyline_smooth.h + * + * Project: Interpolation support for smooth POLYLINE and LWPOLYLINE entities. + * Purpose: Definition of classes for OGR .dxf driver. + * Author: TJ Snider, timsn@thtree.com + * Ray Gardener, Daylon Graphics Ltd. + * + ****************************************************************************** + * Copyright (c) 2010 Daylon Graphics Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + +#ifndef __OGRDXF_SMOOTH_POLYLINE_H__ +#define __OGRDXF_SMOOTH_POLYLINE_H__ + +#include "ogrsf_frmts.h" +#include "cpl_conv.h" +#include +#include "assert.h" + +#ifndef M_PI + #define M_PI 3.14159265358979323846 /* pi */ +#endif + + +class DXFSmoothPolylineVertex +{ +public: + double x, y, z, bulge; + + + DXFSmoothPolylineVertex() + { + x = y = z = bulge = 0.0; + } + + + DXFSmoothPolylineVertex(double dfX, double dfY, double dfZ, double dfBulge) + { + this->set(dfX, dfY, dfZ, dfBulge); + } + + + void set(double dfX, double dfY, double dfZ, double dfBulge) + { + x = dfX; + y = dfY; + z = dfZ; + bulge = dfBulge; + } + + + void scale(double s) + { + x *= s; + y *= s; + } + + + double length() const + { + return (sqrt(x*x + y*y)); + } + + + void normalize() + { + const double len = this->length(); + assert(len != 0.0); + + x /= len; + y /= len; + } + + + bool shares_2D_pos(const DXFSmoothPolylineVertex& v) const + { + return (x == v.x && y == v.y); + } + +}; + + +class DXFSmoothPolyline +{ + // A DXF polyline that includes vertex bulge information. + // Call Tesselate() to convert to an OGRGeometry. + // We treat Z as constant over the entire string; this may + // change in the future. + +private: + + std::vector m_vertices; + mutable bool m_blinestringstarted; + bool m_bClosed; + int m_dim; + + +public: + DXFSmoothPolyline() + { + m_bClosed = false; + m_dim = 2; + } + + OGRGeometry* Tesselate() const; + + size_t size() { return m_vertices.size(); } + + void SetSize(int n) { m_vertices.reserve(n); } + + void AddPoint(double dfX, double dfY, double dfZ, double dfBulge) + { + m_vertices.push_back(DXFSmoothPolylineVertex(dfX, dfY, dfZ, dfBulge)); + } + + void Close(); + + bool IsEmpty() const { return m_vertices.empty(); } + + bool HasConstantZ(double&) const; + + void setCoordinateDimension(int n) { m_dim = n; } + + + +private: + void EmitArc(const DXFSmoothPolylineVertex&, const DXFSmoothPolylineVertex&, + double radius, double len, double saggita, + OGRLineString*, double dfZ = 0.0) const; + + void EmitLine(const DXFSmoothPolylineVertex&, const DXFSmoothPolylineVertex&, + OGRLineString*, bool bConstantZ, double dfZ) const; +}; + +#endif /* __OGRDXF_SMOOTH_POLYLINE_H__ */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfblockslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfblockslayer.cpp new file mode 100644 index 000000000..2b495357f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfblockslayer.cpp @@ -0,0 +1,189 @@ +/****************************************************************************** + * $Id: ogrdxflayer.cpp 19643 2010-05-08 21:56:18Z rouault $ + * + * Project: DXF Translator + * Purpose: Implements OGRDXFBlocksLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dxf.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrdxflayer.cpp 19643 2010-05-08 21:56:18Z rouault $"); + +/************************************************************************/ +/* OGRDXFBlocksLayer() */ +/************************************************************************/ + +OGRDXFBlocksLayer::OGRDXFBlocksLayer( OGRDXFDataSource *poDS ) + +{ + this->poDS = poDS; + + ResetReading(); + + poFeatureDefn = new OGRFeatureDefn( "blocks" ); + poFeatureDefn->Reference(); + + poDS->AddStandardFields( poFeatureDefn ); +} + +/************************************************************************/ +/* ~OGRDXFBlocksLayer() */ +/************************************************************************/ + +OGRDXFBlocksLayer::~OGRDXFBlocksLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "DXF", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poFeatureDefn ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRDXFBlocksLayer::ResetReading() + +{ + iNextFID = 0; + iNextSubFeature = 0; + oIt = poDS->GetBlockMap().begin(); +} + +/************************************************************************/ +/* GetNextUnfilteredFeature() */ +/************************************************************************/ + +OGRFeature *OGRDXFBlocksLayer::GetNextUnfilteredFeature() + +{ + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* Are we out of features? */ +/* -------------------------------------------------------------------- */ + if( oIt == poDS->GetBlockMap().end() ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Are we done reading the current blocks features? */ +/* -------------------------------------------------------------------- */ + DXFBlockDefinition *psBlock = &(oIt->second); + unsigned int nSubFeatureCount = psBlock->apoFeatures.size(); + + if( psBlock->poGeometry != NULL ) + nSubFeatureCount++; + + if( iNextSubFeature >= nSubFeatureCount ) + { + oIt++; + + iNextSubFeature = 0; + + if( oIt == poDS->GetBlockMap().end() ) + return NULL; + + psBlock = &(oIt->second); + } + +/* -------------------------------------------------------------------- */ +/* Is this a geometry based block? */ +/* -------------------------------------------------------------------- */ + if( psBlock->poGeometry != NULL + && iNextSubFeature == psBlock->apoFeatures.size() ) + { + poFeature = new OGRFeature( poFeatureDefn ); + poFeature->SetGeometry( psBlock->poGeometry ); + iNextSubFeature++; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise duplicate the next sub-feature. */ +/* -------------------------------------------------------------------- */ + else + { + poFeature = new OGRFeature( poFeatureDefn ); + poFeature->SetFrom( psBlock->apoFeatures[iNextSubFeature] ); + iNextSubFeature++; + } + +/* -------------------------------------------------------------------- */ +/* Set FID and block name. */ +/* -------------------------------------------------------------------- */ + poFeature->SetFID( iNextFID++ ); + + poFeature->SetField( "BlockName", oIt->first.c_str() ); + + m_nFeaturesRead++; + + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRDXFBlocksLayer::GetNextFeature() + +{ + while( TRUE ) + { + OGRFeature *poFeature = GetNextUnfilteredFeature(); + + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature ) ) ) + { + return poFeature; + } + + delete poFeature; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDXFBlocksLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + else + return FALSE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfblockswriterlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfblockswriterlayer.cpp new file mode 100644 index 000000000..3239b9d37 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfblockswriterlayer.cpp @@ -0,0 +1,151 @@ +/****************************************************************************** + * $Id: ogrdxfwriterlayer.cpp 20670 2010-09-22 00:21:17Z warmerdam $ + * + * Project: DXF Translator + * Purpose: Implements OGRDXFBlocksWriterLayer used for capturing block + * definitions for writing to a DXF file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dxf.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_featurestyle.h" + +CPL_CVSID("$Id: ogrdxfwriterlayer.cpp 20670 2010-09-22 00:21:17Z warmerdam $"); + +/************************************************************************/ +/* OGRDXFBlocksWriterLayer() */ +/************************************************************************/ + +OGRDXFBlocksWriterLayer::OGRDXFBlocksWriterLayer( OGRDXFWriterDS *poDS ) + +{ + (void) poDS; + + poFeatureDefn = new OGRFeatureDefn( "blocks" ); + poFeatureDefn->Reference(); + + OGRFieldDefn oLayerField( "Layer", OFTString ); + poFeatureDefn->AddFieldDefn( &oLayerField ); + + OGRFieldDefn oClassField( "SubClasses", OFTString ); + poFeatureDefn->AddFieldDefn( &oClassField ); + + OGRFieldDefn oExtendedField( "ExtendedEntity", OFTString ); + poFeatureDefn->AddFieldDefn( &oExtendedField ); + + OGRFieldDefn oLinetypeField( "Linetype", OFTString ); + poFeatureDefn->AddFieldDefn( &oLinetypeField ); + + OGRFieldDefn oEntityHandleField( "EntityHandle", OFTString ); + poFeatureDefn->AddFieldDefn( &oEntityHandleField ); + + OGRFieldDefn oTextField( "Text", OFTString ); + poFeatureDefn->AddFieldDefn( &oTextField ); + + OGRFieldDefn oBlockField( "BlockName", OFTString ); + poFeatureDefn->AddFieldDefn( &oBlockField ); +} + +/************************************************************************/ +/* ~OGRDXFBlocksWriterLayer() */ +/************************************************************************/ + +OGRDXFBlocksWriterLayer::~OGRDXFBlocksWriterLayer() + +{ + for( size_t i=0; i < apoBlocks.size(); i++ ) + delete apoBlocks[i]; + + if( poFeatureDefn ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDXFBlocksWriterLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCSequentialWrite) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/* */ +/* This is really a dummy as our fields are precreated. */ +/************************************************************************/ + +OGRErr OGRDXFBlocksWriterLayer::CreateField( OGRFieldDefn *poField, + int bApproxOK ) + +{ + if( poFeatureDefn->GetFieldIndex(poField->GetNameRef()) >= 0 + && bApproxOK ) + return OGRERR_NONE; + + CPLError( CE_Failure, CPLE_AppDefined, + "DXF layer does not support arbitrary field creation, field '%s' not created.", + poField->GetNameRef() ); + + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/* */ +/* We just stash a copy of the features for later writing to */ +/* the blocks section of the header. */ +/************************************************************************/ + +OGRErr OGRDXFBlocksWriterLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + apoBlocks.push_back( poFeature->Clone() ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* FindBlock() */ +/************************************************************************/ + +OGRFeature *OGRDXFBlocksWriterLayer::FindBlock( const char *pszBlockName ) + +{ + for( size_t i=0; i < apoBlocks.size(); i++ ) + { + const char *pszThisName = apoBlocks[i]->GetFieldAsString("BlockName"); + + if( pszThisName != NULL && strcmp(pszBlockName,pszThisName) == 0 ) + return apoBlocks[i]; + } + + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfdatasource.cpp new file mode 100644 index 000000000..ece823d2a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfdatasource.cpp @@ -0,0 +1,543 @@ +/****************************************************************************** + * $Id: ogrdxfdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: DXF Translator + * Purpose: Implements OGRDXFDataSource class + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dxf.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrdxfdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRDXFDataSource() */ +/************************************************************************/ + +OGRDXFDataSource::OGRDXFDataSource() + +{ + fp = NULL; +} + +/************************************************************************/ +/* ~OGRDXFDataSource() */ +/************************************************************************/ + +OGRDXFDataSource::~OGRDXFDataSource() + +{ +/* -------------------------------------------------------------------- */ +/* Destroy layers. */ +/* -------------------------------------------------------------------- */ + while( apoLayers.size() > 0 ) + { + delete apoLayers.back(); + apoLayers.pop_back(); + } + +/* -------------------------------------------------------------------- */ +/* Close file. */ +/* -------------------------------------------------------------------- */ + if( fp != NULL ) + { + VSIFCloseL( fp ); + fp = NULL; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDXFDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + + +OGRLayer *OGRDXFDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= (int) apoLayers.size() ) + return NULL; + else + return apoLayers[iLayer]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRDXFDataSource::Open( const char * pszFilename, int bHeaderOnly ) + +{ + osEncoding = CPL_ENC_ISO8859_1; + + osName = pszFilename; + + bInlineBlocks = CSLTestBoolean( + CPLGetConfigOption( "DXF_INLINE_BLOCKS", "TRUE" ) ); + + if( CSLTestBoolean( + CPLGetConfigOption( "DXF_HEADER_ONLY", "FALSE" ) ) ) + bHeaderOnly = TRUE; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszFilename, "r" ); + if( fp == NULL ) + return FALSE; + + oReader.Initialize( fp ); + +/* -------------------------------------------------------------------- */ +/* Confirm we have a header section. */ +/* -------------------------------------------------------------------- */ + char szLineBuf[257]; + int nCode; + int bEntitiesOnly = FALSE; + + if( ReadValue( szLineBuf ) != 0 || !EQUAL(szLineBuf,"SECTION") ) + return FALSE; + + if( ReadValue( szLineBuf ) != 2 + || (!EQUAL(szLineBuf,"HEADER") && !EQUAL(szLineBuf,"ENTITIES") && !EQUAL(szLineBuf,"TABLES")) ) + return FALSE; + + if( EQUAL(szLineBuf,"ENTITIES") ) + bEntitiesOnly = TRUE; + + /* Some files might have no header but begin directly with a TABLES section */ + else if( EQUAL(szLineBuf,"TABLES") ) + { + if( CPLGetConfigOption( "DXF_ENCODING", NULL ) != NULL ) + osEncoding = CPLGetConfigOption( "DXF_ENCODING", NULL ); + + ReadTablesSection(); + ReadValue(szLineBuf); + } + +/* -------------------------------------------------------------------- */ +/* Process the header, picking up a few useful pieces of */ +/* information. */ +/* -------------------------------------------------------------------- */ + else /* if( EQUAL(szLineBuf,"HEADER") ) */ + { + ReadHeaderSection(); + ReadValue(szLineBuf); + +/* -------------------------------------------------------------------- */ +/* Process the CLASSES section, if present. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(szLineBuf,"ENDSEC") ) + ReadValue(szLineBuf); + + if( EQUAL(szLineBuf,"SECTION") ) + ReadValue(szLineBuf); + + if( EQUAL(szLineBuf,"CLASSES") ) + { + while( (nCode = ReadValue( szLineBuf,sizeof(szLineBuf) )) > -1 + && !EQUAL(szLineBuf,"ENDSEC") ) + { + //printf("C:%d/%s\n", nCode, szLineBuf ); + } + } + +/* -------------------------------------------------------------------- */ +/* Process the TABLES section, if present. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(szLineBuf,"ENDSEC") ) + ReadValue(szLineBuf); + + if( EQUAL(szLineBuf,"SECTION") ) + ReadValue(szLineBuf); + + if( EQUAL(szLineBuf,"TABLES") ) + { + ReadTablesSection(); + ReadValue(szLineBuf); + } + } + +/* -------------------------------------------------------------------- */ +/* Create a blocks layer if we are not in inlining mode. */ +/* -------------------------------------------------------------------- */ + if( !bInlineBlocks ) + apoLayers.push_back( new OGRDXFBlocksLayer( this ) ); + +/* -------------------------------------------------------------------- */ +/* Create out layer object - we will need it when interpreting */ +/* blocks. */ +/* -------------------------------------------------------------------- */ + apoLayers.push_back( new OGRDXFLayer( this ) ); + +/* -------------------------------------------------------------------- */ +/* Process the BLOCKS section if present. */ +/* -------------------------------------------------------------------- */ + if( !bEntitiesOnly ) + { + if( EQUAL(szLineBuf,"ENDSEC") ) + ReadValue(szLineBuf); + + if( EQUAL(szLineBuf,"SECTION") ) + ReadValue(szLineBuf); + + if( EQUAL(szLineBuf,"BLOCKS") ) + { + ReadBlocksSection(); + ReadValue(szLineBuf); + } + } + + if( bHeaderOnly ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Now we are at the entities section, hopefully. Confirm. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(szLineBuf,"SECTION") ) + ReadValue(szLineBuf); + + if( !EQUAL(szLineBuf,"ENTITIES") ) + return FALSE; + + iEntitiesSectionOffset = oReader.iSrcBufferFileOffset + oReader.iSrcBufferOffset; + apoLayers[0]->ResetReading(); + + return TRUE; +} + +/************************************************************************/ +/* ReadTablesSection() */ +/************************************************************************/ + +void OGRDXFDataSource::ReadTablesSection() + +{ + char szLineBuf[257]; + int nCode; + + while( (nCode = ReadValue( szLineBuf, sizeof(szLineBuf) )) > -1 + && !EQUAL(szLineBuf,"ENDSEC") ) + { + // We are only interested in extracting tables. + if( nCode != 0 || !EQUAL(szLineBuf,"TABLE") ) + continue; + + // Currently we are only interested in the LAYER table. + nCode = ReadValue( szLineBuf, sizeof(szLineBuf) ); + + if( nCode != 2 ) + continue; + + //CPLDebug( "DXF", "Found table %s.", szLineBuf ); + + while( (nCode = ReadValue( szLineBuf, sizeof(szLineBuf) )) > -1 + && !EQUAL(szLineBuf,"ENDTAB") ) + { + if( nCode == 0 && EQUAL(szLineBuf,"LAYER") ) + ReadLayerDefinition(); + if( nCode == 0 && EQUAL(szLineBuf,"LTYPE") ) + ReadLineTypeDefinition(); + } + } + + CPLDebug( "DXF", "Read %d layer definitions.", (int) oLayerTable.size() ); +} + +/************************************************************************/ +/* ReadLayerDefinition() */ +/************************************************************************/ + +void OGRDXFDataSource::ReadLayerDefinition() + +{ + char szLineBuf[257]; + int nCode; + std::map oLayerProperties; + CPLString osLayerName = ""; + + oLayerProperties["Hidden"] = "0"; + + while( (nCode = ReadValue( szLineBuf, sizeof(szLineBuf) )) > 0 ) + { + switch( nCode ) + { + case 2: + osLayerName = ACTextUnescape(szLineBuf,GetEncoding()); + oLayerProperties["Exists"] = "1"; + break; + + case 6: + oLayerProperties["Linetype"] = ACTextUnescape(szLineBuf, + GetEncoding()); + break; + + case 62: + oLayerProperties["Color"] = szLineBuf; + + if( atoi(szLineBuf) < 0 ) // Is layer off? + oLayerProperties["Hidden"] = "1"; + break; + + case 70: + oLayerProperties["Flags"] = szLineBuf; + if( atoi(szLineBuf) & 0x01 ) // Is layer frozen? + oLayerProperties["Hidden"] = "1"; + break; + + case 370: + case 39: + oLayerProperties["LineWeight"] = szLineBuf; + break; + + default: + break; + } + } + + if( oLayerProperties.size() > 0 ) + oLayerTable[osLayerName] = oLayerProperties; + + if( nCode == 0 ) + UnreadValue(); +} + +/************************************************************************/ +/* LookupLayerProperty() */ +/************************************************************************/ + +const char *OGRDXFDataSource::LookupLayerProperty( const char *pszLayer, + const char *pszProperty ) + +{ + if( pszLayer == NULL ) + return NULL; + + try { + return (oLayerTable[pszLayer])[pszProperty]; + } catch( ... ) { + return NULL; + } +} + +/************************************************************************/ +/* ReadLineTypeDefinition() */ +/************************************************************************/ + +void OGRDXFDataSource::ReadLineTypeDefinition() + +{ + char szLineBuf[257]; + int nCode; + CPLString osLineTypeName; + CPLString osLineTypeDef; + + while( (nCode = ReadValue( szLineBuf, sizeof(szLineBuf) )) > 0 ) + { + switch( nCode ) + { + case 2: + osLineTypeName = ACTextUnescape(szLineBuf,GetEncoding()); + break; + + case 49: + { + if( osLineTypeDef != "" ) + osLineTypeDef += " "; + + if( szLineBuf[0] == '-' ) + osLineTypeDef += szLineBuf+1; + else + osLineTypeDef += szLineBuf; + + osLineTypeDef += "g"; + } + break; + + default: + break; + } + } + + if( osLineTypeDef != "" ) + oLineTypeTable[osLineTypeName] = osLineTypeDef; + + if( nCode == 0 ) + UnreadValue(); +} + +/************************************************************************/ +/* LookupLineType() */ +/************************************************************************/ + +const char *OGRDXFDataSource::LookupLineType( const char *pszName ) + +{ + if( oLineTypeTable.count(pszName) > 0 ) + return oLineTypeTable[pszName]; + else + return NULL; +} + +/************************************************************************/ +/* ReadHeaderSection() */ +/************************************************************************/ + +void OGRDXFDataSource::ReadHeaderSection() + +{ + char szLineBuf[257]; + int nCode; + + while( (nCode = ReadValue( szLineBuf, sizeof(szLineBuf) )) > -1 + && !EQUAL(szLineBuf,"ENDSEC") ) + { + if( nCode != 9 ) + continue; + + CPLString osName = szLineBuf; + + ReadValue( szLineBuf, sizeof(szLineBuf) ); + + CPLString osValue = szLineBuf; + + oHeaderVariables[osName] = osValue; + } + + if (nCode != -1) + { + nCode = ReadValue( szLineBuf, sizeof(szLineBuf) ); + UnreadValue(); + } + + /* Unusual DXF files produced by dxflib */ + /* such as http://www.ribbonsoft.com/library/architecture/plants/decd5.dxf */ + /* where there is a spurious ENDSEC in the middle of the header variables */ + if (nCode == 9 && EQUALN(szLineBuf,"$", 1) ) + { + while( (nCode = ReadValue( szLineBuf, sizeof(szLineBuf) )) > -1 + && !EQUAL(szLineBuf,"ENDSEC") ) + { + if( nCode != 9 ) + continue; + + CPLString osName = szLineBuf; + + ReadValue( szLineBuf, sizeof(szLineBuf) ); + + CPLString osValue = szLineBuf; + + oHeaderVariables[osName] = osValue; + } + } + + CPLDebug( "DXF", "Read %d header variables.", + (int) oHeaderVariables.size() ); + +/* -------------------------------------------------------------------- */ +/* Decide on what CPLRecode() name to use for the files */ +/* encoding or allow the encoding to be overridden. */ +/* -------------------------------------------------------------------- */ + CPLString osCodepage = GetVariable( "$DWGCODEPAGE", "ANSI_1252" ); + + // not strictly accurate but works even without iconv. + if( osCodepage == "ANSI_1252" ) + osEncoding = CPL_ENC_ISO8859_1; + else if( EQUALN(osCodepage,"ANSI_",5) ) + { + osEncoding = "CP"; + osEncoding += osCodepage + 5; + } + else + { + // fallback to the default + osEncoding = CPL_ENC_ISO8859_1; + } + + if( CPLGetConfigOption( "DXF_ENCODING", NULL ) != NULL ) + osEncoding = CPLGetConfigOption( "DXF_ENCODING", NULL ); + + if( osEncoding != CPL_ENC_ISO8859_1 ) + CPLDebug( "DXF", "Treating DXF as encoding '%s', $DWGCODEPAGE='%s'", + osEncoding.c_str(), osCodepage.c_str() ); +} + +/************************************************************************/ +/* GetVariable() */ +/* */ +/* Fetch a variable that came from the HEADER section. */ +/************************************************************************/ + +const char *OGRDXFDataSource::GetVariable( const char *pszName, + const char *pszDefault ) + +{ + if( oHeaderVariables.count(pszName) == 0 ) + return pszDefault; + else + return oHeaderVariables[pszName]; +} + +/************************************************************************/ +/* AddStandardFields() */ +/************************************************************************/ + +void OGRDXFDataSource::AddStandardFields( OGRFeatureDefn *poFeatureDefn ) + +{ + OGRFieldDefn oLayerField( "Layer", OFTString ); + poFeatureDefn->AddFieldDefn( &oLayerField ); + + OGRFieldDefn oClassField( "SubClasses", OFTString ); + poFeatureDefn->AddFieldDefn( &oClassField ); + + OGRFieldDefn oExtendedField( "ExtendedEntity", OFTString ); + poFeatureDefn->AddFieldDefn( &oExtendedField ); + + OGRFieldDefn oLinetypeField( "Linetype", OFTString ); + poFeatureDefn->AddFieldDefn( &oLinetypeField ); + + OGRFieldDefn oEntityHandleField( "EntityHandle", OFTString ); + poFeatureDefn->AddFieldDefn( &oEntityHandleField ); + + OGRFieldDefn oTextField( "Text", OFTString ); + poFeatureDefn->AddFieldDefn( &oTextField ); + + if( !bInlineBlocks ) + { + OGRFieldDefn oTextField( "BlockName", OFTString ); + poFeatureDefn->AddFieldDefn( &oTextField ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfdriver.cpp new file mode 100644 index 000000000..de4394faf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfdriver.cpp @@ -0,0 +1,128 @@ +/****************************************************************************** + * $Id: ogrdxfdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: DXF Translator + * Purpose: Implements OGRDXFDriver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dxf.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrdxfdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRDXFDriverIdentify() */ +/************************************************************************/ + +static int OGRDXFDriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + return poOpenInfo->fpL != NULL && + EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"dxf"); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRDXFDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( !OGRDXFDriverIdentify(poOpenInfo) ) + return NULL; + + OGRDXFDataSource *poDS = new OGRDXFDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRDXFDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + OGRDXFWriterDS *poDS = new OGRDXFWriterDS(); + + if( poDS->Open( pszName, papszOptions ) ) + return poDS; + else + { + delete poDS; + return NULL; + } +} + +/************************************************************************/ +/* RegisterOGRDXF() */ +/************************************************************************/ + +void RegisterOGRDXF() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "DXF" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "DXF" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "AutoCAD DXF" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "dxf" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_dxf.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, + "" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRDXFDriverOpen; + poDriver->pfnIdentify = OGRDXFDriverIdentify; + poDriver->pfnCreate = OGRDXFDriverCreate; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxflayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxflayer.cpp new file mode 100644 index 000000000..eea57e1f6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxflayer.cpp @@ -0,0 +1,2093 @@ +/****************************************************************************** + * $Id: ogrdxflayer.cpp 27945 2014-11-11 01:33:15Z rouault $ + * + * Project: DXF Translator + * Purpose: Implements OGRDXFLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dxf.h" +#include "cpl_conv.h" +#include "ogrdxf_polyline_smooth.h" +#include "ogr_api.h" + +CPL_CVSID("$Id: ogrdxflayer.cpp 27945 2014-11-11 01:33:15Z rouault $"); + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* OGRDXFLayer() */ +/************************************************************************/ + +OGRDXFLayer::OGRDXFLayer( OGRDXFDataSource *poDS ) + +{ + this->poDS = poDS; + + iNextFID = 0; + + poFeatureDefn = new OGRFeatureDefn( "entities" ); + poFeatureDefn->Reference(); + + poDS->AddStandardFields( poFeatureDefn ); + + if( !poDS->InlineBlocks() ) + { + OGRFieldDefn oScaleField( "BlockScale", OFTRealList ); + poFeatureDefn->AddFieldDefn( &oScaleField ); + + OGRFieldDefn oBlockAngleField( "BlockAngle", OFTReal ); + poFeatureDefn->AddFieldDefn( &oBlockAngleField ); + } + + SetDescription( poFeatureDefn->GetName() ); +} + +/************************************************************************/ +/* ~OGRDXFLayer() */ +/************************************************************************/ + +OGRDXFLayer::~OGRDXFLayer() + +{ + ClearPendingFeatures(); + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "DXF", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poFeatureDefn ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ClearPendingFeatures() */ +/************************************************************************/ + +void OGRDXFLayer::ClearPendingFeatures() + +{ + while( !apoPendingFeatures.empty() ) + { + delete apoPendingFeatures.front(); + apoPendingFeatures.pop(); + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRDXFLayer::ResetReading() + +{ + iNextFID = 0; + ClearPendingFeatures(); + poDS->RestartEntities(); +} + +/************************************************************************/ +/* TranslateGenericProperty() */ +/* */ +/* Try and convert entity properties handled similarly for most */ +/* or all entity types. */ +/************************************************************************/ + +void OGRDXFLayer::TranslateGenericProperty( OGRFeature *poFeature, + int nCode, char *pszValue ) + +{ + switch( nCode ) + { + case 8: + poFeature->SetField( "Layer", TextUnescape(pszValue) ); + break; + + case 100: + { + CPLString osSubClass = poFeature->GetFieldAsString("SubClasses"); + if( osSubClass.size() > 0 ) + osSubClass += ":"; + osSubClass += pszValue; + poFeature->SetField( "SubClasses", osSubClass.c_str() ); + } + break; + + case 62: + oStyleProperties["Color"] = pszValue; + break; + + case 6: + poFeature->SetField( "Linetype", TextUnescape(pszValue) ); + break; + + case 370: + case 39: + oStyleProperties["LineWeight"] = pszValue; + break; + + case 5: + poFeature->SetField( "EntityHandle", pszValue ); + break; + + // Extended entity data + case 1000: + case 1002: + case 1004: + case 1005: + case 1040: + case 1041: + case 1070: + case 1071: + { + CPLString osAggregate = poFeature->GetFieldAsString("ExtendedEntity"); + + if( osAggregate.size() > 0 ) + osAggregate += " "; + osAggregate += pszValue; + + poFeature->SetField( "ExtendedEntity", osAggregate ); + } + break; + + // OCS vector. + case 210: + oStyleProperties["210_N.dX"] = pszValue; + break; + + case 220: + oStyleProperties["220_N.dY"] = pszValue; + break; + + case 230: + oStyleProperties["230_N.dZ"] = pszValue; + break; + + + default: + break; + } +} + +/************************************************************************/ +/* PrepareLineStyle() */ +/************************************************************************/ + +void OGRDXFLayer::PrepareLineStyle( OGRFeature *poFeature ) + +{ + CPLString osLayer = poFeature->GetFieldAsString("Layer"); + +/* -------------------------------------------------------------------- */ +/* Is the layer disabled/hidden/frozen/off? */ +/* -------------------------------------------------------------------- */ + int bHidden = + EQUAL(poDS->LookupLayerProperty( osLayer, "Hidden" ), "1"); + +/* -------------------------------------------------------------------- */ +/* Work out the color for this feature. */ +/* -------------------------------------------------------------------- */ + int nColor = 256; + + if( oStyleProperties.count("Color") > 0 ) + nColor = atoi(oStyleProperties["Color"]); + + // Use layer color? + if( nColor < 1 || nColor > 255 ) + { + const char *pszValue = poDS->LookupLayerProperty( osLayer, "Color" ); + if( pszValue != NULL ) + nColor = atoi(pszValue); + } + + if( nColor < 1 || nColor > 255 ) + return; + +/* -------------------------------------------------------------------- */ +/* Get line weight if available. */ +/* -------------------------------------------------------------------- */ + double dfWeight = 0.0; + + if( oStyleProperties.count("LineWeight") > 0 ) + { + CPLString osWeight = oStyleProperties["LineWeight"]; + + if( osWeight == "-1" ) + osWeight = poDS->LookupLayerProperty(osLayer,"LineWeight"); + + dfWeight = CPLAtof(osWeight) / 100.0; + } + +/* -------------------------------------------------------------------- */ +/* Do we have a dash/dot line style? */ +/* -------------------------------------------------------------------- */ + const char *pszPattern = poDS->LookupLineType( + poFeature->GetFieldAsString("Linetype") ); + +/* -------------------------------------------------------------------- */ +/* Format the style string. */ +/* -------------------------------------------------------------------- */ + CPLString osStyle; + const unsigned char *pabyDXFColors = ACGetColorTable(); + + osStyle.Printf( "PEN(c:#%02x%02x%02x", + pabyDXFColors[nColor*3+0], + pabyDXFColors[nColor*3+1], + pabyDXFColors[nColor*3+2] ); + + if( bHidden ) + osStyle += "00"; + + if( dfWeight > 0.0 ) + { + char szBuffer[64]; + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.2g", dfWeight); + osStyle += CPLString().Printf( ",w:%sg", szBuffer ); + } + + if( pszPattern ) + { + osStyle += ",p:\""; + osStyle += pszPattern; + osStyle += "\""; + } + + osStyle += ")"; + + poFeature->SetStyleString( osStyle ); +} + +/************************************************************************/ +/* OCSTransformer */ +/************************************************************************/ + +class OCSTransformer : public OGRCoordinateTransformation +{ +private: + double adfN[3]; + double adfAX[3]; + double adfAY[3]; + +public: + OCSTransformer( double adfN[3] ) { + static const double dSmall = 1.0 / 64.0; + static const double adfWZ[3] = {0, 0, 1}; + static const double adfWY[3] = {0, 1, 0}; + + memcpy( this->adfN, adfN, sizeof(double)*3 ); + + if ((ABS(adfN[0]) < dSmall) && (ABS(adfN[1]) < dSmall)) + CrossProduct(adfWY, adfN, adfAX); + else + CrossProduct(adfWZ, adfN, adfAX); + + Scale2Unit( adfAX ); + CrossProduct(adfN, adfAX, adfAY); + Scale2Unit( adfAY ); + } + + void CrossProduct(const double *a, const double *b, double *vResult) { + vResult[0] = a[1] * b[2] - a[2] * b[1]; + vResult[1] = a[2] * b[0] - a[0] * b[2]; + vResult[2] = a[0] * b[1] - a[1] * b[0]; + } + + void Scale2Unit(double* adfV) { + double dfLen=sqrt(adfV[0]*adfV[0] + adfV[1]*adfV[1] + adfV[2]*adfV[2]); + if (dfLen != 0) + { + adfV[0] /= dfLen; + adfV[1] /= dfLen; + adfV[2] /= dfLen; + } + } + OGRSpatialReference *GetSourceCS() { return NULL; } + OGRSpatialReference *GetTargetCS() { return NULL; } + int Transform( int nCount, + double *x, double *y, double *z ) + { return TransformEx( nCount, x, y, z, NULL ); } + + int TransformEx( int nCount, + double *adfX, double *adfY, double *adfZ = NULL, + int *pabSuccess = NULL ) + { + int i; + for( i = 0; i < nCount; i++ ) + { + double x = adfX[i], y = adfY[i], z = adfZ[i]; + + adfX[i] = x * adfAX[0] + y * adfAY[0] + z * adfN[0]; + adfY[i] = x * adfAX[1] + y * adfAY[1] + z * adfN[1]; + adfZ[i] = x * adfAX[2] + y * adfAY[2] + z * adfN[2]; + + if( pabSuccess ) + pabSuccess[i] = TRUE; + } + return TRUE; + } +}; + +/************************************************************************/ +/* ApplyOCSTransformer() */ +/* */ +/* Apply a transformation from OCS to world coordinates if an */ +/* OCS vector was found in the object. */ +/************************************************************************/ + +void OGRDXFLayer::ApplyOCSTransformer( OGRGeometry *poGeometry ) + +{ + if( oStyleProperties.count("210_N.dX") == 0 + || oStyleProperties.count("220_N.dY") == 0 + || oStyleProperties.count("230_N.dZ") == 0 ) + return; + + if( poGeometry == NULL ) + return; + + double adfN[3]; + + adfN[0] = CPLAtof(oStyleProperties["210_N.dX"]); + adfN[1] = CPLAtof(oStyleProperties["220_N.dY"]); + adfN[2] = CPLAtof(oStyleProperties["230_N.dZ"]); + + OCSTransformer oTransformer( adfN ); + + poGeometry->transform( &oTransformer ); +} + +/************************************************************************/ +/* TextUnescape() */ +/* */ +/* Unexcape DXF style escape sequences such as \P for newline */ +/* and \~ for space, and do the recoding to UTF8. */ +/************************************************************************/ + +CPLString OGRDXFLayer::TextUnescape( const char *pszInput ) + +{ + return ACTextUnescape( pszInput, poDS->GetEncoding() ); +} + +/************************************************************************/ +/* TranslateMTEXT() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::TranslateMTEXT() + +{ + char szLineBuf[257]; + int nCode; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + double dfX = 0.0, dfY = 0.0, dfZ = 0.0; + double dfAngle = 0.0; + double dfHeight = 0.0; + double dfXDirection = 0.0, dfYDirection = 0.0; + int bHaveZ = FALSE; + int nAttachmentPoint = -1; + CPLString osText; + + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 10: + dfX = CPLAtof(szLineBuf); + break; + + case 20: + dfY = CPLAtof(szLineBuf); + break; + + case 30: + dfZ = CPLAtof(szLineBuf); + bHaveZ = TRUE; + break; + + case 40: + dfHeight = CPLAtof(szLineBuf); + break; + + case 71: + nAttachmentPoint = atoi(szLineBuf); + break; + + case 11: + dfXDirection = CPLAtof(szLineBuf); + break; + + case 21: + dfYDirection = CPLAtof(szLineBuf); + dfAngle = atan2( dfYDirection, dfXDirection ) * 180.0 / PI; + break; + + case 1: + case 3: + if( osText != "" ) + osText += "\n"; + osText += TextUnescape(szLineBuf); + break; + + case 50: + dfAngle = CPLAtof(szLineBuf); + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + if( nCode == 0 ) + poDS->UnreadValue(); + + OGRPoint* poGeom; + if( bHaveZ ) + poGeom = new OGRPoint( dfX, dfY, dfZ ); + else + poGeom = new OGRPoint( dfX, dfY ); + ApplyOCSTransformer( poGeom ); + poFeature->SetGeometryDirectly( poGeom ); + +/* -------------------------------------------------------------------- */ +/* Apply text after stripping off any extra terminating newline. */ +/* -------------------------------------------------------------------- */ + if( osText != "" && osText[osText.size()-1] == '\n' ) + osText.resize( osText.size() - 1 ); + + poFeature->SetField( "Text", osText ); + + +/* -------------------------------------------------------------------- */ +/* We need to escape double quotes with backslashes before they */ +/* can be inserted in the style string. */ +/* -------------------------------------------------------------------- */ + if( strchr( osText, '"') != NULL ) + { + CPLString osEscaped; + size_t iC; + + for( iC = 0; iC < osText.size(); iC++ ) + { + if( osText[iC] == '"' ) + osEscaped += "\\\""; + else + osEscaped += osText[iC]; + } + osText = osEscaped; + } + +/* -------------------------------------------------------------------- */ +/* Work out the color for this feature. */ +/* -------------------------------------------------------------------- */ + int nColor = 256; + + if( oStyleProperties.count("Color") > 0 ) + nColor = atoi(oStyleProperties["Color"]); + + // Use layer color? + if( nColor < 1 || nColor > 255 ) + { + CPLString osLayer = poFeature->GetFieldAsString("Layer"); + const char *pszValue = poDS->LookupLayerProperty( osLayer, "Color" ); + if( pszValue != NULL ) + nColor = atoi(pszValue); + } + +/* -------------------------------------------------------------------- */ +/* Prepare style string. */ +/* -------------------------------------------------------------------- */ + CPLString osStyle; + char szBuffer[64]; + + osStyle.Printf("LABEL(f:\"Arial\",t:\"%s\"",osText.c_str()); + + if( dfAngle != 0.0 ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle); + osStyle += CPLString().Printf(",a:%s", szBuffer); + } + + if( dfHeight != 0.0 ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight); + osStyle += CPLString().Printf(",s:%sg", szBuffer); + } + + if( nAttachmentPoint >= 0 && nAttachmentPoint <= 9 ) + { + const static int anAttachmentMap[10] = + { -1, 7, 8, 9, 4, 5, 6, 1, 2, 3 }; + + osStyle += + CPLString().Printf(",p:%d", anAttachmentMap[nAttachmentPoint]); + } + + if( nColor > 0 && nColor < 256 ) + { + const unsigned char *pabyDXFColors = ACGetColorTable(); + osStyle += + CPLString().Printf( ",c:#%02x%02x%02x", + pabyDXFColors[nColor*3+0], + pabyDXFColors[nColor*3+1], + pabyDXFColors[nColor*3+2] ); + } + + osStyle += ")"; + + poFeature->SetStyleString( osStyle ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateTEXT() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::TranslateTEXT() + +{ + char szLineBuf[257]; + int nCode; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + double dfX = 0.0, dfY = 0.0, dfZ = 0.0; + double dfAngle = 0.0; + double dfHeight = 0.0; + CPLString osText; + int bHaveZ = FALSE; + + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 10: + dfX = CPLAtof(szLineBuf); + break; + + case 20: + dfY = CPLAtof(szLineBuf); + break; + + case 30: + dfZ = CPLAtof(szLineBuf); + bHaveZ = TRUE; + break; + + case 40: + dfHeight = CPLAtof(szLineBuf); + break; + + case 1: + // case 3: // we used to capture prompt, but it should not be displayed as text. + osText += szLineBuf; + break; + + case 50: + dfAngle = CPLAtof(szLineBuf); + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + if( nCode == 0 ) + poDS->UnreadValue(); + + OGRPoint* poGeom; + if( bHaveZ ) + poGeom = new OGRPoint( dfX, dfY, dfZ ); + else + poGeom = new OGRPoint( dfX, dfY ); + ApplyOCSTransformer( poGeom ); + poFeature->SetGeometryDirectly( poGeom ); + +/* -------------------------------------------------------------------- */ +/* Translate text from Win-1252 to UTF8. We approximate this */ +/* by treating Win-1252 as Latin-1. */ +/* -------------------------------------------------------------------- */ + osText.Recode( poDS->GetEncoding(), CPL_ENC_UTF8 ); + + poFeature->SetField( "Text", osText ); + +/* -------------------------------------------------------------------- */ +/* We need to escape double quotes with backslashes before they */ +/* can be inserted in the style string. */ +/* -------------------------------------------------------------------- */ + if( strchr( osText, '"') != NULL ) + { + CPLString osEscaped; + size_t iC; + + for( iC = 0; iC < osText.size(); iC++ ) + { + if( osText[iC] == '"' ) + osEscaped += "\\\""; + else + osEscaped += osText[iC]; + } + osText = osEscaped; + } + +/* -------------------------------------------------------------------- */ +/* Is the layer disabled/hidden/frozen/off? */ +/* -------------------------------------------------------------------- */ + CPLString osLayer = poFeature->GetFieldAsString("Layer"); + + int bHidden = + EQUAL(poDS->LookupLayerProperty( osLayer, "Hidden" ), "1"); + +/* -------------------------------------------------------------------- */ +/* Work out the color for this feature. */ +/* -------------------------------------------------------------------- */ + int nColor = 256; + + if( oStyleProperties.count("Color") > 0 ) + nColor = atoi(oStyleProperties["Color"]); + + // Use layer color? + if( nColor < 1 || nColor > 255 ) + { + const char *pszValue = poDS->LookupLayerProperty( osLayer, "Color" ); + if( pszValue != NULL ) + nColor = atoi(pszValue); + } + + if( nColor < 1 || nColor > 255 ) + nColor = 8; + +/* -------------------------------------------------------------------- */ +/* Prepare style string. */ +/* -------------------------------------------------------------------- */ + CPLString osStyle; + char szBuffer[64]; + + osStyle.Printf("LABEL(f:\"Arial\",t:\"%s\"",osText.c_str()); + + if( dfAngle != 0.0 ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfAngle); + osStyle += CPLString().Printf(",a:%s", szBuffer); + } + + if( dfHeight != 0.0 ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.3g", dfHeight); + osStyle += CPLString().Printf(",s:%sg", szBuffer); + } + + const unsigned char *pabyDWGColors = ACGetColorTable(); + + snprintf( szBuffer, sizeof(szBuffer), ",c:#%02x%02x%02x", + pabyDWGColors[nColor*3+0], + pabyDWGColors[nColor*3+1], + pabyDWGColors[nColor*3+2] ); + osStyle += szBuffer; + + if( bHidden ) + osStyle += "00"; + + osStyle += ")"; + + poFeature->SetStyleString( osStyle ); + + return poFeature; +} + +/************************************************************************/ +/* TranslatePOINT() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::TranslatePOINT() + +{ + char szLineBuf[257]; + int nCode; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + double dfX = 0.0, dfY = 0.0, dfZ = 0.0; + int bHaveZ = FALSE; + + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 10: + dfX = CPLAtof(szLineBuf); + break; + + case 20: + dfY = CPLAtof(szLineBuf); + break; + + case 30: + dfZ = CPLAtof(szLineBuf); + bHaveZ = TRUE; + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + OGRPoint* poGeom; + if( bHaveZ ) + poGeom = new OGRPoint( dfX, dfY, dfZ ); + else + poGeom = new OGRPoint( dfX, dfY ); + ApplyOCSTransformer( poGeom ); + poFeature->SetGeometryDirectly( poGeom ); + + if( nCode == 0 ) + poDS->UnreadValue(); + + // Set style pen color + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateLINE() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::TranslateLINE() + +{ + char szLineBuf[257]; + int nCode; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + double dfX1 = 0.0, dfY1 = 0.0, dfZ1 = 0.0; + double dfX2 = 0.0, dfY2 = 0.0, dfZ2 = 0.0; + int bHaveZ = FALSE; + +/* -------------------------------------------------------------------- */ +/* Process values. */ +/* -------------------------------------------------------------------- */ + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 10: + dfX1 = CPLAtof(szLineBuf); + break; + + case 11: + dfX2 = CPLAtof(szLineBuf); + break; + + case 20: + dfY1 = CPLAtof(szLineBuf); + break; + + case 21: + dfY2 = CPLAtof(szLineBuf); + break; + + case 30: + dfZ1 = CPLAtof(szLineBuf); + bHaveZ = TRUE; + break; + + case 31: + dfZ2 = CPLAtof(szLineBuf); + bHaveZ = TRUE; + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + if( nCode == 0 ) + poDS->UnreadValue(); + +/* -------------------------------------------------------------------- */ +/* Create geometry */ +/* -------------------------------------------------------------------- */ + OGRLineString *poLS = new OGRLineString(); + if( bHaveZ ) + { + poLS->addPoint( dfX1, dfY1, dfZ1 ); + poLS->addPoint( dfX2, dfY2, dfZ2 ); + } + else + { + poLS->addPoint( dfX1, dfY1 ); + poLS->addPoint( dfX2, dfY2 ); + } + + ApplyOCSTransformer( poLS ); + poFeature->SetGeometryDirectly( poLS ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateLWPOLYLINE() */ +/************************************************************************/ +OGRFeature *OGRDXFLayer::TranslateLWPOLYLINE() + +{ + // Collect vertices and attributes into a smooth polyline. + // If there are no bulges, then we are a straight-line polyline. + // Single-vertex polylines become points. + // Group code 30 (vertex Z) is not part of this entity. + + char szLineBuf[257]; + int nCode; + int nPolylineFlag = 0; + + + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + double dfX = 0.0, dfY = 0.0, dfZ = 0.0; + int bHaveX = FALSE; + int bHaveY = FALSE; + + int nNumVertices = 1; // use 1 based index + int npolyarcVertexCount = 1; + double dfBulge = 0.0; + DXFSmoothPolyline smoothPolyline; + + smoothPolyline.setCoordinateDimension(2); + +/* -------------------------------------------------------------------- */ +/* Collect information from the LWPOLYLINE object itself. */ +/* -------------------------------------------------------------------- */ + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + if(npolyarcVertexCount > nNumVertices) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many vertices found in LWPOLYLINE." ); + delete poFeature; + return NULL; + } + + switch( nCode ) + { + case 38: + // Constant elevation. + dfZ = CPLAtof(szLineBuf); + smoothPolyline.setCoordinateDimension(3); + break; + + case 90: + nNumVertices = atoi(szLineBuf); + break; + + case 70: + nPolylineFlag = atoi(szLineBuf); + break; + + case 10: + if( bHaveX && bHaveY ) + { + smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge); + npolyarcVertexCount++; + dfBulge = 0.0; + bHaveY = FALSE; + } + dfX = CPLAtof(szLineBuf); + bHaveX = TRUE; + break; + + case 20: + if( bHaveX && bHaveY ) + { + smoothPolyline.AddPoint( dfX, dfY, dfZ, dfBulge ); + npolyarcVertexCount++; + dfBulge = 0.0; + bHaveX = FALSE; + } + dfY = CPLAtof(szLineBuf); + bHaveY = TRUE; + break; + + case 42: + dfBulge = CPLAtof(szLineBuf); + break; + + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + if( nCode == 0 ) + poDS->UnreadValue(); + + if( bHaveX && bHaveY ) + smoothPolyline.AddPoint(dfX, dfY, dfZ, dfBulge); + + + if(smoothPolyline.IsEmpty()) + { + delete poFeature; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Close polyline if necessary. */ +/* -------------------------------------------------------------------- */ + if(nPolylineFlag & 0x01) + smoothPolyline.Close(); + + OGRGeometry* poGeom = smoothPolyline.Tesselate(); + ApplyOCSTransformer( poGeom ); + poFeature->SetGeometryDirectly( poGeom ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + + +/************************************************************************/ +/* TranslatePOLYLINE() */ +/* */ +/* We also capture the following VERTEXes. */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::TranslatePOLYLINE() + +{ + char szLineBuf[257]; + int nCode; + int nPolylineFlag = 0; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + +/* -------------------------------------------------------------------- */ +/* Collect information from the POLYLINE object itself. */ +/* -------------------------------------------------------------------- */ + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 70: + nPolylineFlag = atoi(szLineBuf); + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Collect VERTEXes as a smooth polyline. */ +/* -------------------------------------------------------------------- */ + double dfX = 0.0, dfY = 0.0, dfZ = 0.0; + double dfBulge = 0.0; + DXFSmoothPolyline smoothPolyline; + int nVertexFlag = 0; + + smoothPolyline.setCoordinateDimension(2); + + while( nCode == 0 && !EQUAL(szLineBuf,"SEQEND") ) + { + // Eat non-vertex objects. + if( !EQUAL(szLineBuf,"VERTEX") ) + { + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf)))>0 ) {} + continue; + } + + // process a Vertex + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 10: + dfX = CPLAtof(szLineBuf); + break; + + case 20: + dfY = CPLAtof(szLineBuf); + break; + + case 30: + dfZ = CPLAtof(szLineBuf); + smoothPolyline.setCoordinateDimension(3); + break; + + case 42: + dfBulge = CPLAtof(szLineBuf); + break; + + case 70: + nVertexFlag = atoi(szLineBuf); + break; + + default: + break; + } + } + + // Ignore Spline frame control points ( see #4683 ) + if ((nVertexFlag & 16) == 0) + smoothPolyline.AddPoint( dfX, dfY, dfZ, dfBulge ); + dfBulge = 0.0; + } + + if(smoothPolyline.IsEmpty()) + { + delete poFeature; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Close polyline if necessary. */ +/* -------------------------------------------------------------------- */ + if(nPolylineFlag & 0x01) + smoothPolyline.Close(); + + OGRGeometry* poGeom = smoothPolyline.Tesselate(); + ApplyOCSTransformer( poGeom ); + poFeature->SetGeometryDirectly( poGeom ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateCIRCLE() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::TranslateCIRCLE() + +{ + char szLineBuf[257]; + int nCode; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + double dfX1 = 0.0, dfY1 = 0.0, dfZ1 = 0.0, dfRadius = 0.0; + int bHaveZ = FALSE; + +/* -------------------------------------------------------------------- */ +/* Process values. */ +/* -------------------------------------------------------------------- */ + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 10: + dfX1 = CPLAtof(szLineBuf); + break; + + case 20: + dfY1 = CPLAtof(szLineBuf); + break; + + case 30: + dfZ1 = CPLAtof(szLineBuf); + bHaveZ = TRUE; + break; + + case 40: + dfRadius = CPLAtof(szLineBuf); + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + if( nCode == 0 ) + poDS->UnreadValue(); + +/* -------------------------------------------------------------------- */ +/* Create geometry */ +/* -------------------------------------------------------------------- */ + OGRGeometry *poCircle = + OGRGeometryFactory::approximateArcAngles( dfX1, dfY1, dfZ1, + dfRadius, dfRadius, 0.0, + 0.0, 360.0, + 0.0 ); + + if( !bHaveZ ) + poCircle->flattenTo2D(); + + ApplyOCSTransformer( poCircle ); + poFeature->SetGeometryDirectly( poCircle ); + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateELLIPSE() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::TranslateELLIPSE() + +{ + char szLineBuf[257]; + int nCode; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + double dfX1 = 0.0, dfY1 = 0.0, dfZ1 = 0.0, dfRatio = 0.0; + double dfStartAngle = 0.0, dfEndAngle = 360.0; + double dfAxisX=0.0, dfAxisY=0.0, dfAxisZ=0.0; + int bHaveZ = FALSE; + +/* -------------------------------------------------------------------- */ +/* Process values. */ +/* -------------------------------------------------------------------- */ + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 10: + dfX1 = CPLAtof(szLineBuf); + break; + + case 20: + dfY1 = CPLAtof(szLineBuf); + break; + + case 30: + dfZ1 = CPLAtof(szLineBuf); + bHaveZ = TRUE; + break; + + case 11: + dfAxisX = CPLAtof(szLineBuf); + break; + + case 21: + dfAxisY = CPLAtof(szLineBuf); + break; + + case 31: + dfAxisZ = CPLAtof(szLineBuf); + break; + + case 40: + dfRatio = CPLAtof(szLineBuf); + break; + + case 41: + // These *seem* to always be in radians regardless of $AUNITS + dfEndAngle = -1 * CPLAtof(szLineBuf) * 180.0 / PI; + break; + + case 42: + // These *seem* to always be in radians regardless of $AUNITS + dfStartAngle = -1 * CPLAtof(szLineBuf) * 180.0 / PI; + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + if( nCode == 0 ) + poDS->UnreadValue(); + +/* -------------------------------------------------------------------- */ +/* Compute primary and secondary axis lengths, and the angle of */ +/* rotation for the ellipse. */ +/* -------------------------------------------------------------------- */ + double dfPrimaryRadius, dfSecondaryRadius; + double dfRotation; + + if( dfStartAngle > dfEndAngle ) + dfEndAngle += 360.0; + + dfPrimaryRadius = sqrt( dfAxisX * dfAxisX + + dfAxisY * dfAxisY + + dfAxisZ * dfAxisZ ); + + dfSecondaryRadius = dfRatio * dfPrimaryRadius; + + dfRotation = -1 * atan2( dfAxisY, dfAxisX ) * 180 / PI; + +/* -------------------------------------------------------------------- */ +/* Create geometry */ +/* -------------------------------------------------------------------- */ + OGRGeometry *poEllipse = + OGRGeometryFactory::approximateArcAngles( dfX1, dfY1, dfZ1, + dfPrimaryRadius, + dfSecondaryRadius, + dfRotation, + dfStartAngle, dfEndAngle, + 0.0 ); + + if( !bHaveZ ) + poEllipse->flattenTo2D(); + + ApplyOCSTransformer( poEllipse ); + poFeature->SetGeometryDirectly( poEllipse ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateARC() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::TranslateARC() + +{ + char szLineBuf[257]; + int nCode; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + double dfX1 = 0.0, dfY1 = 0.0, dfZ1 = 0.0, dfRadius = 0.0; + double dfStartAngle = 0.0, dfEndAngle = 360.0; + int bHaveZ = FALSE; + +/* -------------------------------------------------------------------- */ +/* Process values. */ +/* -------------------------------------------------------------------- */ + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 10: + dfX1 = CPLAtof(szLineBuf); + break; + + case 20: + dfY1 = CPLAtof(szLineBuf); + break; + + case 30: + dfZ1 = CPLAtof(szLineBuf); + bHaveZ = TRUE; + break; + + case 40: + dfRadius = CPLAtof(szLineBuf); + break; + + case 50: + // This is apparently always degrees regardless of AUNITS + dfEndAngle = -1 * CPLAtof(szLineBuf); + break; + + case 51: + // This is apparently always degrees regardless of AUNITS + dfStartAngle = -1 * CPLAtof(szLineBuf); + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + if( nCode == 0 ) + poDS->UnreadValue(); + +/* -------------------------------------------------------------------- */ +/* Create geometry */ +/* -------------------------------------------------------------------- */ + if( dfStartAngle > dfEndAngle ) + dfEndAngle += 360.0; + + OGRGeometry *poArc = + OGRGeometryFactory::approximateArcAngles( dfX1, dfY1, dfZ1, + dfRadius, dfRadius, 0.0, + dfStartAngle, dfEndAngle, + 0.0 ); + if( !bHaveZ ) + poArc->flattenTo2D(); + + ApplyOCSTransformer( poArc ); + poFeature->SetGeometryDirectly( poArc ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateSPLINE() */ +/************************************************************************/ + +void rbspline(int npts,int k,int p1,double b[],double h[], double p[]); +void rbsplinu(int npts,int k,int p1,double b[],double h[], double p[]); + +OGRFeature *OGRDXFLayer::TranslateSPLINE() + +{ + char szLineBuf[257]; + int nCode, nDegree = -1, nFlags = -1, bClosed = FALSE, i; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + std::vector adfControlPoints; + + adfControlPoints.push_back(0.0); + +/* -------------------------------------------------------------------- */ +/* Process values. */ +/* -------------------------------------------------------------------- */ + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 10: + adfControlPoints.push_back( CPLAtof(szLineBuf) ); + break; + + case 20: + adfControlPoints.push_back( CPLAtof(szLineBuf) ); + adfControlPoints.push_back( 0.0 ); + break; + + case 70: + nFlags = atoi(szLineBuf); + if( nFlags & 1 ) + bClosed = TRUE; + break; + + case 71: + nDegree = atoi(szLineBuf); + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + if( nCode == 0 ) + poDS->UnreadValue(); + + if( bClosed ) + { + for( i = 0; i < nDegree; i++ ) + { + adfControlPoints.push_back( adfControlPoints[i*3+1] ); + adfControlPoints.push_back( adfControlPoints[i*3+2] ); + adfControlPoints.push_back( adfControlPoints[i*3+3] ); + } + } + +/* -------------------------------------------------------------------- */ +/* Interpolate spline */ +/* -------------------------------------------------------------------- */ + int nControlPoints = adfControlPoints.size() / 3; + std::vector h, p; + + h.push_back(1.0); + for( i = 0; i < nControlPoints; i++ ) + h.push_back( 1.0 ); + + // resolution: + //int p1 = getGraphicVariableInt("$SPLINESEGS", 8) * npts; + int p1 = nControlPoints * 8; + + p.push_back( 0.0 ); + for( i = 0; i < 3*p1; i++ ) + p.push_back( 0.0 ); + + if( bClosed ) + rbsplinu( nControlPoints, nDegree+1, p1, &(adfControlPoints[0]), + &(h[0]), &(p[0]) ); + else + rbspline( nControlPoints, nDegree+1, p1, &(adfControlPoints[0]), + &(h[0]), &(p[0]) ); + +/* -------------------------------------------------------------------- */ +/* Turn into OGR geometry. */ +/* -------------------------------------------------------------------- */ + OGRLineString *poLS = new OGRLineString(); + + poLS->setNumPoints( p1 ); + for( i = 0; i < p1; i++ ) + poLS->setPoint( i, p[i*3+1], p[i*3+2] ); + + ApplyOCSTransformer( poLS ); + poFeature->SetGeometryDirectly( poLS ); + + PrepareLineStyle( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* Translate3DFACE() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::Translate3DFACE() + +{ + char szLineBuf[257]; + int nCode; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + double dfX1 = 0.0, dfY1 = 0.0, dfZ1 = 0.0; + double dfX2 = 0.0, dfY2 = 0.0, dfZ2 = 0.0; + double dfX3 = 0.0, dfY3 = 0.0, dfZ3 = 0.0; + double dfX4 = 0.0, dfY4 = 0.0, dfZ4 = 0.0; + +/* -------------------------------------------------------------------- */ +/* Process values. */ +/* -------------------------------------------------------------------- */ + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 10: + dfX1 = CPLAtof(szLineBuf); + break; + + case 11: + dfX2 = CPLAtof(szLineBuf); + break; + + case 12: + dfX3 = CPLAtof(szLineBuf); + break; + + case 13: + dfX4 = CPLAtof(szLineBuf); + break; + + case 20: + dfY1 = CPLAtof(szLineBuf); + break; + + case 21: + dfY2 = CPLAtof(szLineBuf); + break; + + case 22: + dfY3 = CPLAtof(szLineBuf); + break; + + case 23: + dfY4 = CPLAtof(szLineBuf); + break; + + case 30: + dfZ1 = CPLAtof(szLineBuf); + break; + + case 31: + dfZ2 = CPLAtof(szLineBuf); + break; + + case 32: + dfZ3 = CPLAtof(szLineBuf); + break; + + case 33: + dfZ4 = CPLAtof(szLineBuf); + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + if( nCode == 0 ) + poDS->UnreadValue(); + +/* -------------------------------------------------------------------- */ +/* Create geometry */ +/* -------------------------------------------------------------------- */ + OGRPolygon *poPoly = new OGRPolygon(); + OGRLinearRing* poLR = new OGRLinearRing(); + poLR->addPoint( dfX1, dfY1, dfZ1 ); + poLR->addPoint( dfX2, dfY2, dfZ2 ); + poLR->addPoint( dfX3, dfY3, dfZ3 ); + if( dfX4 != dfX3 || dfY4 != dfY3 || dfZ4 != dfZ3 ) + poLR->addPoint( dfX4, dfY4, dfZ4 ); + poPoly->addRingDirectly(poLR); + poPoly->closeRings(); + + ApplyOCSTransformer( poLR ); + poFeature->SetGeometryDirectly( poPoly ); + + /* PrepareLineStyle( poFeature ); */ + + return poFeature; +} + +/* -------------------------------------------------------------------- */ +/* Distance */ +/* */ +/* Calculate distance between two points */ +/* -------------------------------------------------------------------- */ + +static double Distance(double dX0, double dY0, double dX1, double dY1) { + return sqrt((dX1 - dX0) * (dX1 - dX0) + (dY1 - dY0) * (dY1 - dY0)); +} + +/* -------------------------------------------------------------------- */ +/* AddEdgesByNearest */ +/* */ +/* Order and add SOLID edges to geometry collection */ +/* -------------------------------------------------------------------- */ + +static void AddEdgesByNearest(OGRGeometryCollection* poCollection, OGRLineString *poLS, + OGRLineString *poLS4, double dfX2, double dfY2, double dfX3, + double dfY3, double dfX4, double dfY4) { + OGRLineString *poLS2 = new OGRLineString(); + OGRLineString *poLS3 = new OGRLineString(); + poLS->addPoint(dfX2, dfY2); + poCollection->addGeometryDirectly(poLS); + poLS2->addPoint(dfX2, dfY2); + double dTo3 = Distance(dfX2, dfY2, dfX3, dfY3); + double dTo4 = Distance(dfX2, dfY2, dfX4, dfY4); + if (dTo3 <= dTo4) { + poLS2->addPoint(dfX3, dfY3); + poCollection->addGeometryDirectly(poLS2); + poLS3->addPoint(dfX3, dfY3); + poLS3->addPoint(dfX4, dfY4); + poCollection->addGeometryDirectly(poLS3); + poLS4->addPoint(dfX4, dfY4); + } else { + poLS2->addPoint(dfX4, dfY4); + poCollection->addGeometryDirectly(poLS2); + poLS3->addPoint(dfX4, dfY4); + poLS3->addPoint(dfX3, dfY3); + poCollection->addGeometryDirectly(poLS3); + poLS4->addPoint(dfX3, dfY3); + } +} + +/************************************************************************/ +/* TranslateSOLID() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::TranslateSOLID() + +{ + CPLDebug("SOLID", "translating solid"); + char szLineBuf[257]; + int nCode; + OGRFeature *poFeature = new OGRFeature(poFeatureDefn); + double dfX1 = 0.0, dfY1 = 0.0; + double dfX2 = 0.0, dfY2 = 0.0; + double dfX3 = 0.0, dfY3 = 0.0; + double dfX4 = 0.0, dfY4 = 0.0; + + while ((nCode = poDS->ReadValue(szLineBuf, sizeof(szLineBuf))) > 0) { + switch (nCode) { + case 10: + dfX1 = CPLAtof(szLineBuf); + break; + + case 20: + dfY1 = CPLAtof(szLineBuf); + break; + + case 30: + break; + + case 11: + dfX2 = CPLAtof(szLineBuf); + break; + + case 21: + dfY2 = CPLAtof(szLineBuf); + break; + + case 31: + break; + + case 12: + dfX3 = CPLAtof(szLineBuf); + break; + + case 22: + dfY3 = CPLAtof(szLineBuf); + break; + + case 32: + break; + + case 13: + dfX4 = CPLAtof(szLineBuf); + break; + + case 23: + dfY4 = CPLAtof(szLineBuf); + break; + + case 33: + break; + + default: + TranslateGenericProperty(poFeature, nCode, szLineBuf); + break; + } + } + + CPLDebug("Corner coordinates are", "%f,%f,%f,%f,%f,%f,%f,%f", dfX1, dfY1, + dfX2, dfY2, dfX3, dfY3, dfX4, dfY4); + + OGRGeometryCollection* poCollection = NULL; + poCollection = new OGRGeometryCollection(); + + OGRLineString *poLS = new OGRLineString(); + poLS->addPoint(dfX1, dfY1); + + // corners in SOLID can be in any order, so we need to order them for + // creating edges for polygon + + double dTo2 = Distance(dfX1, dfY1, dfX2, dfY2); + double dTo3 = Distance(dfX1, dfY1, dfX3, dfY3); + double dTo4 = Distance(dfX1, dfY1, dfX4, dfY4); + + OGRLineString *poLS4 = new OGRLineString(); + + if (dTo2 <= dTo3 && dTo2 <= dTo4) { + AddEdgesByNearest(poCollection, poLS, poLS4, dfX2, dfY2, dfX3, dfY3, + dfX4, dfY4); + } else if (dTo3 <= dTo2 && dTo3 <= dTo4) { + AddEdgesByNearest(poCollection, poLS, poLS4, dfX3, dfY3, dfX2, dfY2, + dfX4, dfY4); + } else /* if (dTo4 <= dTo2 && dTo4 <= dTo3) */ { + AddEdgesByNearest(poCollection, poLS, poLS4, dfX4, dfY4, dfX3, dfY3, + dfX2, dfY2); + } + poLS4->addPoint(dfX1, dfY1); + poCollection->addGeometryDirectly(poLS4); + OGRErr eErr; + + OGRGeometry* poFinalGeom = (OGRGeometry *) OGRBuildPolygonFromEdges( + (OGRGeometryH) poCollection, TRUE, TRUE, 0, &eErr); + + delete poCollection; + + ApplyOCSTransformer(poFinalGeom); + + poFeature->SetGeometryDirectly(poFinalGeom); + + if (nCode == 0) + poDS->UnreadValue(); + + // Set style pen color + PrepareLineStyle(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* GeometryInsertTransformer */ +/************************************************************************/ + +class GeometryInsertTransformer : public OGRCoordinateTransformation +{ +public: + GeometryInsertTransformer() : + dfXOffset(0),dfYOffset(0),dfZOffset(0), + dfXScale(1.0),dfYScale(1.0),dfZScale(1.0), + dfAngle(0.0) {} + + double dfXOffset; + double dfYOffset; + double dfZOffset; + double dfXScale; + double dfYScale; + double dfZScale; + double dfAngle; + + OGRSpatialReference *GetSourceCS() { return NULL; } + OGRSpatialReference *GetTargetCS() { return NULL; } + int Transform( int nCount, + double *x, double *y, double *z ) + { return TransformEx( nCount, x, y, z, NULL ); } + + int TransformEx( int nCount, + double *x, double *y, double *z = NULL, + int *pabSuccess = NULL ) + { + int i; + for( i = 0; i < nCount; i++ ) + { + double dfXNew, dfYNew; + + x[i] *= dfXScale; + y[i] *= dfYScale; + z[i] *= dfZScale; + + dfXNew = x[i] * cos(dfAngle) - y[i] * sin(dfAngle); + dfYNew = x[i] * sin(dfAngle) + y[i] * cos(dfAngle); + + x[i] = dfXNew; + y[i] = dfYNew; + + x[i] += dfXOffset; + y[i] += dfYOffset; + z[i] += dfZOffset; + + if( pabSuccess ) + pabSuccess[i] = TRUE; + } + return TRUE; + } +}; + +/************************************************************************/ +/* TranslateINSERT() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::TranslateINSERT() + +{ + char szLineBuf[257]; + int nCode; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + GeometryInsertTransformer oTransformer; + CPLString osBlockName; + double dfAngle = 0.0; + +/* -------------------------------------------------------------------- */ +/* Process values. */ +/* -------------------------------------------------------------------- */ + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) + { + switch( nCode ) + { + case 10: + oTransformer.dfXOffset = CPLAtof(szLineBuf); + break; + + case 20: + oTransformer.dfYOffset = CPLAtof(szLineBuf); + break; + + case 30: + oTransformer.dfZOffset = CPLAtof(szLineBuf); + break; + + case 41: + oTransformer.dfXScale = CPLAtof(szLineBuf); + break; + + case 42: + oTransformer.dfYScale = CPLAtof(szLineBuf); + break; + + case 43: + oTransformer.dfZScale = CPLAtof(szLineBuf); + break; + + case 50: + dfAngle = CPLAtof(szLineBuf); + // We want to transform this to radians. + // It is apparently always in degrees regardless of $AUNITS + oTransformer.dfAngle = dfAngle * PI / 180.0; + break; + + case 2: + osBlockName = szLineBuf; + break; + + default: + TranslateGenericProperty( poFeature, nCode, szLineBuf ); + break; + } + } + + if( nCode == 0 ) + poDS->UnreadValue(); + +/* -------------------------------------------------------------------- */ +/* In the case where we do not inlined blocks we just capture */ +/* info on a point feature. */ +/* -------------------------------------------------------------------- */ + if( !poDS->InlineBlocks() ) + { + // ApplyOCSTransformer( poGeom ); ? + poFeature->SetGeometryDirectly( + new OGRPoint( oTransformer.dfXOffset, + oTransformer.dfYOffset, + oTransformer.dfZOffset ) ); + + poFeature->SetField( "BlockName", osBlockName ); + + poFeature->SetField( "BlockAngle", dfAngle ); + poFeature->SetField( "BlockScale", 3, &(oTransformer.dfXScale) ); + + return poFeature; + } + +/* -------------------------------------------------------------------- */ +/* Lookup the block. */ +/* -------------------------------------------------------------------- */ + DXFBlockDefinition *poBlock = poDS->LookupBlock( osBlockName ); + + if( poBlock == NULL ) + { + delete poFeature; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Transform the geometry. */ +/* -------------------------------------------------------------------- */ + if( poBlock->poGeometry != NULL ) + { + OGRGeometry *poGeometry = poBlock->poGeometry->clone(); + + poGeometry->transform( &oTransformer ); + + poFeature->SetGeometryDirectly( poGeometry ); + } + +/* -------------------------------------------------------------------- */ +/* If we have complete features associated with the block, push */ +/* them on the pending feature stack copying over key override */ +/* information. */ +/* */ +/* Note that while we transform the geometry of the features we */ +/* don't adjust subtle things like text angle. */ +/* -------------------------------------------------------------------- */ + unsigned int iSubFeat; + + for( iSubFeat = 0; iSubFeat < poBlock->apoFeatures.size(); iSubFeat++ ) + { + OGRFeature *poSubFeature = poBlock->apoFeatures[iSubFeat]->Clone(); + CPLString osCompEntityId; + + if( poSubFeature->GetGeometryRef() != NULL ) + poSubFeature->GetGeometryRef()->transform( &oTransformer ); + + ACAdjustText( dfAngle, oTransformer.dfXScale, poSubFeature ); + +#ifdef notdef + osCompEntityId = poSubFeature->GetFieldAsString( "EntityHandle" ); + osCompEntityId += ":"; +#endif + osCompEntityId += poFeature->GetFieldAsString( "EntityHandle" ); + + poSubFeature->SetField( "EntityHandle", osCompEntityId ); + + apoPendingFeatures.push( poSubFeature ); + } + +/* -------------------------------------------------------------------- */ +/* Return the working feature if we had geometry, otherwise */ +/* return NULL and let the machinery find the rest of the */ +/* features in the pending feature stack. */ +/* -------------------------------------------------------------------- */ + if( poBlock->poGeometry == NULL ) + { + delete poFeature; + return NULL; + } + else + { + // Set style pen color + PrepareLineStyle( poFeature ); + return poFeature; + } +} + +/************************************************************************/ +/* GetNextUnfilteredFeature() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::GetNextUnfilteredFeature() + +{ + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* If we have pending features, return one of them. */ +/* -------------------------------------------------------------------- */ + if( !apoPendingFeatures.empty() ) + { + poFeature = apoPendingFeatures.front(); + apoPendingFeatures.pop(); + + poFeature->SetFID( iNextFID++ ); + return poFeature; + } + +/* -------------------------------------------------------------------- */ +/* Read the entity type. */ +/* -------------------------------------------------------------------- */ + char szLineBuf[257]; + int nCode; + + while( poFeature == NULL ) + { + // read ahead to an entity. + while( (nCode = poDS->ReadValue(szLineBuf,sizeof(szLineBuf))) > 0 ) {} + + if( nCode == -1 ) + { + CPLDebug( "DXF", "Unexpected end of data without ENDSEC." ); + return NULL; + } + + if( EQUAL(szLineBuf,"ENDSEC") ) + { + //CPLDebug( "DXF", "Clean end of features at ENDSEC." ); + poDS->UnreadValue(); + return NULL; + } + + if( EQUAL(szLineBuf,"ENDBLK") ) + { + //CPLDebug( "DXF", "Clean end of block at ENDBLK." ); + poDS->UnreadValue(); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Handle the entity. */ +/* -------------------------------------------------------------------- */ + oStyleProperties.clear(); + + if( EQUAL(szLineBuf,"POINT") ) + { + poFeature = TranslatePOINT(); + } + else if( EQUAL(szLineBuf,"MTEXT") ) + { + poFeature = TranslateMTEXT(); + } + else if( EQUAL(szLineBuf,"TEXT") + || EQUAL(szLineBuf,"ATTDEF") ) + { + poFeature = TranslateTEXT(); + } + else if( EQUAL(szLineBuf,"LINE") ) + { + poFeature = TranslateLINE(); + } + else if( EQUAL(szLineBuf,"POLYLINE") ) + { + poFeature = TranslatePOLYLINE(); + } + else if( EQUAL(szLineBuf,"LWPOLYLINE") ) + { + poFeature = TranslateLWPOLYLINE(); + } + else if( EQUAL(szLineBuf,"CIRCLE") ) + { + poFeature = TranslateCIRCLE(); + } + else if( EQUAL(szLineBuf,"ELLIPSE") ) + { + poFeature = TranslateELLIPSE(); + } + else if( EQUAL(szLineBuf,"ARC") ) + { + poFeature = TranslateARC(); + } + else if( EQUAL(szLineBuf,"SPLINE") ) + { + poFeature = TranslateSPLINE(); + } + else if( EQUAL(szLineBuf,"3DFACE") ) + { + poFeature = Translate3DFACE(); + } + else if( EQUAL(szLineBuf,"INSERT") ) + { + poFeature = TranslateINSERT(); + } + else if( EQUAL(szLineBuf,"DIMENSION") ) + { + poFeature = TranslateDIMENSION(); + } + else if( EQUAL(szLineBuf,"HATCH") ) + { + poFeature = TranslateHATCH(); + } + else if( EQUAL(szLineBuf,"SOLID") ) + { + poFeature = TranslateSOLID(); + } + else + { + if( oIgnoredEntities.count(szLineBuf) == 0 ) + { + oIgnoredEntities.insert( szLineBuf ); + CPLDebug( "DWG", "Ignoring one or more of entity '%s'.", + szLineBuf ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Set FID. */ +/* -------------------------------------------------------------------- */ + poFeature->SetFID( iNextFID++ ); + m_nFeaturesRead++; + + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRDXFLayer::GetNextFeature() + +{ + while( TRUE ) + { + OGRFeature *poFeature = GetNextUnfilteredFeature(); + + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature ) ) ) + { + return poFeature; + } + + delete poFeature; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDXFLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + else + return FALSE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfreader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfreader.cpp new file mode 100644 index 000000000..79ddd90e0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfreader.cpp @@ -0,0 +1,247 @@ +/****************************************************************************** + * $Id: ogrdxf_diskio.cpp 20278 2010-08-14 15:11:01Z warmerdam $ + * + * Project: DXF Translator + * Purpose: Implements low level DXF reading with caching and parsing of + * of the code/value pairs. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dxf.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" + +CPL_CVSID("$Id: ogrdxf_diskio.cpp 20278 2010-08-14 15:11:01Z warmerdam $"); + +/************************************************************************/ +/* OGRDXFReader() */ +/************************************************************************/ + +OGRDXFReader::OGRDXFReader() + +{ + fp = NULL; + + iSrcBufferOffset = 0; + nSrcBufferBytes = 0; + iSrcBufferFileOffset = 0; + + nLastValueSize = 0; + nLineNumber = 0; +} + +/************************************************************************/ +/* ~OGRDXFReader() */ +/************************************************************************/ + +OGRDXFReader::~OGRDXFReader() + +{ +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void OGRDXFReader::Initialize( VSILFILE *fp ) + +{ + this->fp = fp; +} + +/************************************************************************/ +/* ResetReadPointer() */ +/************************************************************************/ + +void OGRDXFReader::ResetReadPointer( int iNewOffset ) + +{ + nSrcBufferBytes = 0; + iSrcBufferOffset = 0; + iSrcBufferFileOffset = iNewOffset; + nLastValueSize = 0; + nLineNumber = 0; + + VSIFSeekL( fp, iNewOffset, SEEK_SET ); +} + +/************************************************************************/ +/* LoadDiskChunk() */ +/* */ +/* Load another block (512 bytes) of input from the source */ +/* file. */ +/************************************************************************/ + +void OGRDXFReader::LoadDiskChunk() + +{ + CPLAssert( iSrcBufferOffset >= 0 ); + + if( nSrcBufferBytes - iSrcBufferOffset > 511 ) + return; + + if( iSrcBufferOffset > 0 ) + { + CPLAssert( nSrcBufferBytes <= 1024 ); + CPLAssert( iSrcBufferOffset <= nSrcBufferBytes ); + + memmove( achSrcBuffer, achSrcBuffer + iSrcBufferOffset, + nSrcBufferBytes - iSrcBufferOffset ); + iSrcBufferFileOffset += iSrcBufferOffset; + nSrcBufferBytes -= iSrcBufferOffset; + iSrcBufferOffset = 0; + } + + nSrcBufferBytes += VSIFReadL( achSrcBuffer + nSrcBufferBytes, + 1, 512, fp ); + achSrcBuffer[nSrcBufferBytes] = '\0'; + + CPLAssert( nSrcBufferBytes <= 1024 ); + CPLAssert( iSrcBufferOffset <= nSrcBufferBytes ); +} + +/************************************************************************/ +/* ReadValue() */ +/* */ +/* Read one type code and value line pair from the DXF file. */ +/************************************************************************/ + +int OGRDXFReader::ReadValue( char *pszValueBuf, int nValueBufSize ) + +{ +/* -------------------------------------------------------------------- */ +/* Make sure we have lots of data in our buffer for one value. */ +/* -------------------------------------------------------------------- */ + if( nSrcBufferBytes - iSrcBufferOffset < 512 ) + LoadDiskChunk(); + + if( nValueBufSize > 512 ) + nValueBufSize = 512; + +/* -------------------------------------------------------------------- */ +/* Capture the value code, and skip past it. */ +/* -------------------------------------------------------------------- */ + int iStartSrcBufferOffset = iSrcBufferOffset; + int nValueCode = atoi(achSrcBuffer + iSrcBufferOffset); + + nLineNumber ++; + + // proceed to newline. + while( achSrcBuffer[iSrcBufferOffset] != '\n' + && achSrcBuffer[iSrcBufferOffset] != '\r' + && achSrcBuffer[iSrcBufferOffset] != '\0' ) + iSrcBufferOffset++; + + if( achSrcBuffer[iSrcBufferOffset] == '\0' ) + return -1; + + // skip past newline. CR, CRLF, or LFCR + if( (achSrcBuffer[iSrcBufferOffset] == '\r' + && achSrcBuffer[iSrcBufferOffset+1] == '\n' ) + || (achSrcBuffer[iSrcBufferOffset] == '\n' + && achSrcBuffer[iSrcBufferOffset+1] == '\r' ) ) + iSrcBufferOffset += 2; + else + iSrcBufferOffset += 1; + + if( achSrcBuffer[iSrcBufferOffset] == '\0' ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Capture the value string. */ +/* -------------------------------------------------------------------- */ + int iEOL = iSrcBufferOffset; + + nLineNumber ++; + + // proceed to newline. + while( achSrcBuffer[iEOL] != '\n' + && achSrcBuffer[iEOL] != '\r' + && achSrcBuffer[iEOL] != '\0' ) + iEOL++; + + if( achSrcBuffer[iEOL] == '\0' ) + return -1; + + if( (iEOL - iSrcBufferOffset) > nValueBufSize-1 ) + { + strncpy( pszValueBuf, achSrcBuffer + iSrcBufferOffset, + nValueBufSize-1 ); + pszValueBuf[nValueBufSize-1] = '\0'; + + CPLDebug( "DXF", "Long line truncated to %d characters.\n%s...", + nValueBufSize-1, + pszValueBuf ); + } + else + { + strncpy( pszValueBuf, achSrcBuffer + iSrcBufferOffset, + iEOL - iSrcBufferOffset ); + pszValueBuf[iEOL - iSrcBufferOffset] = '\0'; + } + + iSrcBufferOffset = iEOL; + + // skip past newline. CR, CRLF, or LFCR + if( (achSrcBuffer[iSrcBufferOffset] == '\r' + && achSrcBuffer[iSrcBufferOffset+1] == '\n' ) + || (achSrcBuffer[iSrcBufferOffset] == '\n' + && achSrcBuffer[iSrcBufferOffset+1] == '\r' ) ) + iSrcBufferOffset += 2; + else + iSrcBufferOffset += 1; + +/* -------------------------------------------------------------------- */ +/* Record how big this value was, so it can be unread safely. */ +/* -------------------------------------------------------------------- */ + nLastValueSize = iSrcBufferOffset - iStartSrcBufferOffset; + +/* -------------------------------------------------------------------- */ +/* Is this a comment? If so, tail recurse to get another line. */ +/* -------------------------------------------------------------------- */ + if( nValueCode == 999 ) + return ReadValue(pszValueBuf,nValueBufSize); + else + return nValueCode; +} + +/************************************************************************/ +/* UnreadValue() */ +/* */ +/* Unread the last value read, accomplished by resetting the */ +/* read pointer. */ +/************************************************************************/ + +void OGRDXFReader::UnreadValue() + +{ + CPLAssert( iSrcBufferOffset >= nLastValueSize ); + CPLAssert( nLastValueSize > 0 ); + + iSrcBufferOffset -= nLastValueSize; + + nLastValueSize = 0; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfwriterds.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfwriterds.cpp new file mode 100644 index 000000000..9d61abbe3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfwriterds.cpp @@ -0,0 +1,951 @@ +/****************************************************************************** + * $Id: ogrdxfwriterds.cpp 27945 2014-11-11 01:33:15Z rouault $ + * + * Project: DXF Translator + * Purpose: Implements OGRDXFWriterDS - the OGRDataSource class used for + * writing a DXF file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dxf.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrdxfwriterds.cpp 27945 2014-11-11 01:33:15Z rouault $"); + +/************************************************************************/ +/* OGRDXFWriterDS() */ +/************************************************************************/ + +OGRDXFWriterDS::OGRDXFWriterDS() + +{ + fp = NULL; + fpTemp = NULL; + poLayer = NULL; + poBlocksLayer = NULL; + papszLayersToCreate = NULL; + nNextFID = 80; + nHANDSEEDOffset = 0; +} + +/************************************************************************/ +/* ~OGRDXFWriterDS() */ +/************************************************************************/ + +OGRDXFWriterDS::~OGRDXFWriterDS() + +{ + if (fp != NULL) + { +/* -------------------------------------------------------------------- */ +/* Transfer over the header into the destination file with any */ +/* adjustments or insertions needed. */ +/* -------------------------------------------------------------------- */ + CPLDebug( "DXF", "Compose final DXF file from components." ); + + TransferUpdateHeader( fp ); + + if (fpTemp != NULL) + { +/* -------------------------------------------------------------------- */ +/* Copy in the temporary file contents. */ +/* -------------------------------------------------------------------- */ + const char *pszLine; + + VSIFCloseL(fpTemp); + fpTemp = VSIFOpenL( osTempFilename, "r" ); + + while( (pszLine = CPLReadLineL(fpTemp)) != NULL ) + { + VSIFWriteL( pszLine, 1, strlen(pszLine), fp ); + VSIFWriteL( "\n", 1, 1, fp ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup temporary file. */ +/* -------------------------------------------------------------------- */ + VSIFCloseL( fpTemp ); + VSIUnlink( osTempFilename ); + } + +/* -------------------------------------------------------------------- */ +/* Write trailer. */ +/* -------------------------------------------------------------------- */ + if( osTrailerFile != "" ) + TransferUpdateTrailer( fp ); + +/* -------------------------------------------------------------------- */ +/* Fixup the HANDSEED value now that we know all the entity ids */ +/* created. */ +/* -------------------------------------------------------------------- */ + FixupHANDSEED( fp ); + +/* -------------------------------------------------------------------- */ +/* Close file. */ +/* -------------------------------------------------------------------- */ + + VSIFCloseL( fp ); + fp = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Destroy layers. */ +/* -------------------------------------------------------------------- */ + delete poLayer; + delete poBlocksLayer; + + CSLDestroy(papszLayersToCreate); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDXFWriterDS::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + // Unable to have more than one OGR entities layer in a DXF file, with one options blocks layer. + return poBlocksLayer == NULL || poLayer == NULL; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + + +OGRLayer *OGRDXFWriterDS::GetLayer( int iLayer ) + +{ + if( iLayer == 0 ) + return poLayer; + else + return NULL; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int OGRDXFWriterDS::GetLayerCount() + +{ + if( poLayer ) + return 1; + else + return 0; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRDXFWriterDS::Open( const char * pszFilename, char **papszOptions ) + +{ +/* -------------------------------------------------------------------- */ +/* Open the standard header, or a user provided header. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue(papszOptions,"HEADER") != NULL ) + osHeaderFile = CSLFetchNameValue(papszOptions,"HEADER"); + else + { + const char *pszValue = CPLFindFile( "gdal", "header.dxf" ); + if( pszValue == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to find template header file header.dxf for reading,\nis GDAL_DATA set properly?" ); + return FALSE; + } + osHeaderFile = pszValue; + } + +/* -------------------------------------------------------------------- */ +/* Establish the name for our trailer file. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue(papszOptions,"TRAILER") != NULL ) + osTrailerFile = CSLFetchNameValue(papszOptions,"TRAILER"); + else + { + const char *pszValue = CPLFindFile( "gdal", "trailer.dxf" ); + if( pszValue != NULL ) + osTrailerFile = pszValue; + } + +/* -------------------------------------------------------------------- */ +/* What entity id do we want to start with when writing? Small */ +/* values run a risk of overlapping some undetected entity in */ +/* the header or trailer despite the prescanning we do. */ +/* -------------------------------------------------------------------- */ +#ifdef DEBUG + nNextFID = 80; +#else + nNextFID = 131072; +#endif + + if( CSLFetchNameValue( papszOptions, "FIRST_ENTITY" ) != NULL ) + nNextFID = atoi(CSLFetchNameValue( papszOptions, "FIRST_ENTITY" )); + +/* -------------------------------------------------------------------- */ +/* Prescan the header and trailer for entity codes. */ +/* -------------------------------------------------------------------- */ + ScanForEntities( osHeaderFile, "HEADER" ); + ScanForEntities( osTrailerFile, "TRAILER" ); + +/* -------------------------------------------------------------------- */ +/* Attempt to read the template header file so we have a list */ +/* of layers, linestyles and blocks. */ +/* -------------------------------------------------------------------- */ + if( !oHeaderDS.Open( osHeaderFile, TRUE ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Create the output file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszFilename, "w+" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open '%s' for writing.", + pszFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Establish the temporary file. */ +/* -------------------------------------------------------------------- */ + osTempFilename = pszFilename; + osTempFilename += ".tmp"; + + fpTemp = VSIFOpenL( osTempFilename, "w" ); + if( fpTemp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open '%s' for writing.", + osTempFilename.c_str() ); + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer *OGRDXFWriterDS::ICreateLayer( const char *pszName, + OGRSpatialReference *, + OGRwkbGeometryType, + char ** ) + +{ + if( EQUAL(pszName,"blocks") && poBlocksLayer == NULL ) + { + poBlocksLayer = new OGRDXFBlocksWriterLayer( this ); + return poBlocksLayer; + } + else if( poLayer == NULL ) + { + poLayer = new OGRDXFWriterLayer( this, fpTemp ); + return poLayer; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to have more than one OGR entities layer in a DXF file, with one options blocks layer." ); + return NULL; + } +} + +/************************************************************************/ +/* WriteValue() */ +/************************************************************************/ + +static int WriteValue( VSILFILE *fp, int nCode, const char *pszLine ) + +{ + char szLinePair[300]; + + snprintf(szLinePair, sizeof(szLinePair), "%3d\n%s\n", nCode, pszLine ); + size_t nLen = strlen(szLinePair); + if( VSIFWriteL( szLinePair, 1, nLen, fp ) != nLen ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Attempt to write line to DXF file failed, disk full?." ); + return FALSE; + } + else + return TRUE; +} + +/************************************************************************/ +/* WriteValue() */ +/************************************************************************/ + +static int WriteValue( VSILFILE *fp, int nCode, double dfValue ) + +{ + char szLinePair[64]; + + CPLsnprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue ); + size_t nLen = strlen(szLinePair); + if( VSIFWriteL( szLinePair, 1, nLen, fp ) != nLen ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Attempt to write line to DXF file failed, disk full?." ); + return FALSE; + } + else + return TRUE; +} +/************************************************************************/ +/* TransferUpdateHeader() */ +/************************************************************************/ + +int OGRDXFWriterDS::TransferUpdateHeader( VSILFILE *fpOut ) + +{ + oHeaderDS.ResetReadPointer( 0 ); + +/* -------------------------------------------------------------------- */ +/* Copy header, inserting in new objects as needed. */ +/* -------------------------------------------------------------------- */ + char szLineBuf[257]; + int nCode; + CPLString osSection, osTable, osEntity; + + while( (nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) )) != -1 + && osSection != "ENTITIES" ) + { + if( nCode == 0 && EQUAL(szLineBuf,"ENDTAB") ) + { + // If we are at the end of the LAYER TABLE consider inserting + // missing definitions. + if( osTable == "LAYER" ) + { + if( !WriteNewLayerDefinitions( fp ) ) + return FALSE; + } + + // If at the end of the BLOCK_RECORD TABLE consider inserting + // missing definitions. + if( osTable == "BLOCK_RECORD" && poBlocksLayer ) + { + if( !WriteNewBlockRecords( fp ) ) + return FALSE; + } + + // If at the end of the LTYPE TABLE consider inserting + // missing layer type definitions. + if( osTable == "LTYPE" ) + { + if( !WriteNewLineTypeRecords( fp ) ) + return FALSE; + } + + osTable = ""; + } + + // If we are at the end of the BLOCKS section, consider inserting + // suplementary blocks. + if( nCode == 0 && osSection == "BLOCKS" && EQUAL(szLineBuf,"ENDSEC") + && poBlocksLayer != NULL ) + { + if( !WriteNewBlockDefinitions( fp ) ) + return FALSE; + } + + // We need to keep track of where $HANDSEED is so that we can + // come back and fix it up when we have generated all entity ids. + if( nCode == 9 && EQUAL(szLineBuf,"$HANDSEED") ) + { + if( !WriteValue( fpOut, nCode, szLineBuf ) ) + return FALSE; + + nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) ); + + // ensure we have room to overwrite with a longer value. + while( strlen(szLineBuf) < 8 ) + { + memmove( szLineBuf+1, szLineBuf, strlen(szLineBuf)+1 ); + szLineBuf[0] = '0'; + } + + nHANDSEEDOffset = VSIFTellL( fpOut ); + } + + // Patch EXTMIN with minx and miny + if( nCode == 9 && EQUAL(szLineBuf,"$EXTMIN") ) + { + if( !WriteValue( fpOut, nCode, szLineBuf ) ) + return FALSE; + + nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) ); + if (nCode == 10) + { + if( !WriteValue( fpOut, nCode, oGlobalEnvelope.MinX ) ) + return FALSE; + + nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) ); + if (nCode == 20) + { + if( !WriteValue( fpOut, nCode, oGlobalEnvelope.MinY ) ) + return FALSE; + + continue; + } + } + } + + // Patch EXTMAX with maxx and maxy + if( nCode == 9 && EQUAL(szLineBuf,"$EXTMAX") ) + { + if( !WriteValue( fpOut, nCode, szLineBuf ) ) + return FALSE; + + nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) ); + if (nCode == 10) + { + if( !WriteValue( fpOut, nCode, oGlobalEnvelope.MaxX ) ) + return FALSE; + + nCode = oHeaderDS.ReadValue( szLineBuf, sizeof(szLineBuf) ); + if (nCode == 20) + { + if( !WriteValue( fpOut, nCode, oGlobalEnvelope.MaxY ) ) + return FALSE; + + continue; + } + } + } + + // Copy over the source line. + if( !WriteValue( fpOut, nCode, szLineBuf ) ) + return FALSE; + + // Track what entity we are in - that is the last "code 0" object. + if( nCode == 0 ) + osEntity = szLineBuf; + + // Track what section we are in. + if( nCode == 0 && EQUAL(szLineBuf,"SECTION") ) + { + nCode = oHeaderDS.ReadValue( szLineBuf ); + if( nCode == -1 ) + break; + + if( !WriteValue( fpOut, nCode, szLineBuf ) ) + return FALSE; + + osSection = szLineBuf; + } + + // Track what TABLE we are in. + if( nCode == 0 && EQUAL(szLineBuf,"TABLE") ) + { + nCode = oHeaderDS.ReadValue( szLineBuf ); + if( !WriteValue( fpOut, nCode, szLineBuf ) ) + return FALSE; + + osTable = szLineBuf; + } + + // If we are starting the first layer, then capture + // the layer contents while copying so we can duplicate + // it for any new layer definitions. + if( nCode == 0 && EQUAL(szLineBuf,"LAYER") + && osTable == "LAYER" && aosDefaultLayerText.size() == 0 ) + { + do { + anDefaultLayerCode.push_back( nCode ); + aosDefaultLayerText.push_back( szLineBuf ); + + if( nCode != 0 && !WriteValue( fpOut, nCode, szLineBuf ) ) + return FALSE; + + nCode = oHeaderDS.ReadValue( szLineBuf ); + + if( nCode == 2 && !EQUAL(szLineBuf,"0") ) + { + anDefaultLayerCode.resize(0); + aosDefaultLayerText.resize(0); + break; + } + } while( nCode != 0 ); + + oHeaderDS.UnreadValue(); + } + } + + return TRUE; +} + +/************************************************************************/ +/* TransferUpdateTrailer() */ +/************************************************************************/ + +int OGRDXFWriterDS::TransferUpdateTrailer( VSILFILE *fpOut ) +{ + OGRDXFReader oReader; + VSILFILE *fp; + +/* -------------------------------------------------------------------- */ +/* Open the file and setup a reader. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( osTrailerFile, "r" ); + + if( fp == NULL ) + return FALSE; + + oReader.Initialize( fp ); + +/* -------------------------------------------------------------------- */ +/* Scan ahead to find the OBJECTS section. */ +/* -------------------------------------------------------------------- */ + char szLineBuf[257]; + int nCode; + + while( (nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) )) != -1 ) + { + if( nCode == 0 && EQUAL(szLineBuf,"SECTION") ) + { + nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) ); + if( nCode == 2 && EQUAL(szLineBuf,"OBJECTS") ) + break; + } + } + + if( nCode == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find OBJECTS section in trailer file '%s'.", + osTrailerFile.c_str() ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Insert the "end of section" for ENTITIES, and the start of */ +/* the OBJECTS section. */ +/* -------------------------------------------------------------------- */ + WriteValue( fpOut, 0, "ENDSEC" ); + WriteValue( fpOut, 0, "SECTION" ); + WriteValue( fpOut, 2, "OBJECTS" ); + +/* -------------------------------------------------------------------- */ +/* Copy the remainder of the file. */ +/* -------------------------------------------------------------------- */ + while( (nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) )) != -1 ) + { + if( !WriteValue( fpOut, nCode, szLineBuf ) ) + { + VSIFCloseL( fp ); + return FALSE; + } + } + + VSIFCloseL( fp ); + + return TRUE; +} + +/************************************************************************/ +/* FixupHANDSEED() */ +/* */ +/* Fixup the next entity id information in the $HANDSEED header */ +/* variable. */ +/************************************************************************/ + +int OGRDXFWriterDS::FixupHANDSEED( VSILFILE *fp ) + +{ +/* -------------------------------------------------------------------- */ +/* What is a good next handle seed? Scan existing values. */ +/* -------------------------------------------------------------------- */ + unsigned int nHighestHandle = 0; + std::set::iterator it; + + for( it = aosUsedEntities.begin(); it != aosUsedEntities.end(); it++ ) + { + unsigned int nHandle; + if( sscanf( (*it).c_str(), "%x", &nHandle ) == 1 ) + { + if( nHandle > nHighestHandle ) + nHighestHandle = nHandle; + } + } + +/* -------------------------------------------------------------------- */ +/* Read the existing handseed value, replace it, and write back. */ +/* -------------------------------------------------------------------- */ + char szWorkBuf[30]; + int i = 0; + + if( nHANDSEEDOffset == 0 ) + return FALSE; + + VSIFSeekL( fp, nHANDSEEDOffset, SEEK_SET ); + VSIFReadL( szWorkBuf, 1, sizeof(szWorkBuf), fp ); + + while( szWorkBuf[i] != '\n' ) + i++; + + i++; + if( szWorkBuf[i] == '\r' ) + i++; + + CPLString osNewValue; + + osNewValue.Printf( "%08X", nHighestHandle + 1 ); + strncpy( szWorkBuf + i, osNewValue.c_str(), osNewValue.size() ); + + VSIFSeekL( fp, nHANDSEEDOffset, SEEK_SET ); + VSIFWriteL( szWorkBuf, 1, sizeof(szWorkBuf), fp ); + + return TRUE; +} + +/************************************************************************/ +/* WriteNewLayerDefinitions() */ +/************************************************************************/ + +int OGRDXFWriterDS::WriteNewLayerDefinitions( VSILFILE * fpOut ) + +{ + int iLayer, nNewLayers = CSLCount(papszLayersToCreate); + + for( iLayer = 0; iLayer < nNewLayers; iLayer++ ) + { + for( unsigned i = 0; i < aosDefaultLayerText.size(); i++ ) + { + if( anDefaultLayerCode[i] == 2 ) + { + if( !WriteValue( fpOut, 2, papszLayersToCreate[iLayer] ) ) + return FALSE; + } + else if( anDefaultLayerCode[i] == 5 ) + { + WriteEntityID( fpOut ); + } + else + { + if( !WriteValue( fpOut, + anDefaultLayerCode[i], + aosDefaultLayerText[i] ) ) + return FALSE; + } + } + } + + return TRUE; +} + +/************************************************************************/ +/* WriteNewLineTypeRecords() */ +/************************************************************************/ + +int OGRDXFWriterDS::WriteNewLineTypeRecords( VSILFILE *fp ) + +{ + if( poLayer == NULL ) + return TRUE; + + std::map::iterator oIt; + std::map& oNewLineTypes = + poLayer->GetNewLineTypeMap(); + + for( oIt = oNewLineTypes.begin(); + oIt != oNewLineTypes.end(); oIt++ ) + { + WriteValue( fp, 0, "LTYPE" ); + WriteEntityID( fp ); + WriteValue( fp, 100, "AcDbSymbolTableRecord" ); + WriteValue( fp, 100, "AcDbLinetypeTableRecord" ); + WriteValue( fp, 2, (*oIt).first ); + WriteValue( fp, 70, "0" ); + WriteValue( fp, 3, "" ); + WriteValue( fp, 72, "65" ); + VSIFWriteL( (*oIt).second.c_str(), 1, (*oIt).second.size(), fp ); + + CPLDebug( "DXF", "Define Line type '%s'.", + (*oIt).first.c_str() ); + } + + return TRUE; +} + +/************************************************************************/ +/* WriteNewBlockRecords() */ +/************************************************************************/ + +int OGRDXFWriterDS::WriteNewBlockRecords( VSILFILE * fp ) + +{ + std::set aosAlreadyHandled; + +/* ==================================================================== */ +/* Loop over all block objects written via the blocks layer. */ +/* ==================================================================== */ + for( size_t iBlock=0; iBlock < poBlocksLayer->apoBlocks.size(); iBlock++ ) + { + OGRFeature* poThisBlockFeat = poBlocksLayer->apoBlocks[iBlock]; + +/* -------------------------------------------------------------------- */ +/* Is this block already defined in the template header? */ +/* -------------------------------------------------------------------- */ + CPLString osBlockName = poThisBlockFeat->GetFieldAsString("BlockName"); + + if( oHeaderDS.LookupBlock( osBlockName ) != NULL ) + continue; + +/* -------------------------------------------------------------------- */ +/* Have we already written a BLOCK_RECORD for this block? */ +/* -------------------------------------------------------------------- */ + if( aosAlreadyHandled.find(osBlockName) != aosAlreadyHandled.end() ) + continue; + + aosAlreadyHandled.insert( osBlockName ); + +/* -------------------------------------------------------------------- */ +/* Write the block record. */ +/* -------------------------------------------------------------------- */ + WriteValue( fp, 0, "BLOCK_RECORD" ); + WriteEntityID( fp ); + WriteValue( fp, 100, "AcDbSymbolTableRecord" ); + WriteValue( fp, 100, "AcDbBlockTableRecord" ); + WriteValue( fp, 2, poThisBlockFeat->GetFieldAsString("BlockName") ); + if( !WriteValue( fp, 340, "0" ) ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* WriteNewBlockDefinitions() */ +/************************************************************************/ + +int OGRDXFWriterDS::WriteNewBlockDefinitions( VSILFILE * fp ) + +{ + poLayer->ResetFP( fp ); + +/* ==================================================================== */ +/* Loop over all block objects written via the blocks layer. */ +/* ==================================================================== */ + for( size_t iBlock=0; iBlock < poBlocksLayer->apoBlocks.size(); iBlock++ ) + { + OGRFeature* poThisBlockFeat = poBlocksLayer->apoBlocks[iBlock]; + +/* -------------------------------------------------------------------- */ +/* Is this block already defined in the template header? */ +/* -------------------------------------------------------------------- */ + CPLString osBlockName = poThisBlockFeat->GetFieldAsString("BlockName"); + + if( oHeaderDS.LookupBlock( osBlockName ) != NULL ) + continue; + +/* -------------------------------------------------------------------- */ +/* Write the block definition preamble. */ +/* -------------------------------------------------------------------- */ + CPLDebug( "DXF", "Writing BLOCK definition for '%s'.", + poThisBlockFeat->GetFieldAsString("BlockName") ); + + WriteValue( fp, 0, "BLOCK" ); + WriteEntityID( fp ); + WriteValue( fp, 100, "AcDbEntity" ); + if( strlen(poThisBlockFeat->GetFieldAsString("Layer")) > 0 ) + WriteValue( fp, 8, poThisBlockFeat->GetFieldAsString("Layer") ); + else + WriteValue( fp, 8, "0" ); + WriteValue( fp, 100, "AcDbBlockBegin" ); + WriteValue( fp, 2, poThisBlockFeat->GetFieldAsString("BlockName") ); + WriteValue( fp, 70, "0" ); + + // Origin + WriteValue( fp, 10, "0.0" ); + WriteValue( fp, 20, "0.0" ); + WriteValue( fp, 30, "0.0" ); + + WriteValue( fp, 3, poThisBlockFeat->GetFieldAsString("BlockName") ); + WriteValue( fp, 1, "" ); + +/* -------------------------------------------------------------------- */ +/* Write out the feature entities. */ +/* -------------------------------------------------------------------- */ + if( poLayer->CreateFeature( poThisBlockFeat ) != OGRERR_NONE ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Write out following features if they are the same block. */ +/* -------------------------------------------------------------------- */ + while( iBlock < poBlocksLayer->apoBlocks.size()-1 + && EQUAL(poBlocksLayer->apoBlocks[iBlock+1]->GetFieldAsString("BlockName"), + osBlockName) ) + { + iBlock++; + + if( poLayer->CreateFeature( poBlocksLayer->apoBlocks[iBlock] ) + != OGRERR_NONE ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Write out the block definition postamble. */ +/* -------------------------------------------------------------------- */ + WriteValue( fp, 0, "ENDBLK" ); + WriteEntityID( fp ); + WriteValue( fp, 100, "AcDbEntity" ); + if( strlen(poThisBlockFeat->GetFieldAsString("Layer")) > 0 ) + WriteValue( fp, 8, poThisBlockFeat->GetFieldAsString("Layer") ); + else + WriteValue( fp, 8, "0" ); + WriteValue( fp, 100, "AcDbBlockEnd" ); + } + + return TRUE; +} + +/************************************************************************/ +/* ScanForEntities() */ +/* */ +/* Scan the indicated file for entity ids ("5" records) and */ +/* build them up as a set so we can be careful to avoid */ +/* creating new entities with conflicting ids. */ +/************************************************************************/ + +void OGRDXFWriterDS::ScanForEntities( const char *pszFilename, + const char *pszTarget ) + +{ + OGRDXFReader oReader; + VSILFILE *fp; + +/* -------------------------------------------------------------------- */ +/* Open the file and setup a reader. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszFilename, "r" ); + + if( fp == NULL ) + return; + + oReader.Initialize( fp ); + +/* -------------------------------------------------------------------- */ +/* Add every code "5" line to our entities list. */ +/* -------------------------------------------------------------------- */ + char szLineBuf[257]; + int nCode; + const char *pszPortion = "HEADER"; + + while( (nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) )) != -1 ) + { + if( (nCode == 5 || nCode == 105) && EQUAL(pszTarget,pszPortion) ) + { + CPLString osEntity( szLineBuf ); + + if( CheckEntityID( osEntity ) ) + CPLDebug( "DXF", "Encounted entity '%s' multiple times.", + osEntity.c_str() ); + else + aosUsedEntities.insert( osEntity ); + } + + if( nCode == 0 && EQUAL(szLineBuf,"SECTION") ) + { + nCode = oReader.ReadValue( szLineBuf, sizeof(szLineBuf) ); + if( nCode == 2 && EQUAL(szLineBuf,"ENTITIES") ) + pszPortion = "BODY"; + if( nCode == 2 && EQUAL(szLineBuf,"OBJECTS") ) + pszPortion = "TRAILER"; + } + } + + VSIFCloseL( fp ); +} + +/************************************************************************/ +/* CheckEntityID() */ +/* */ +/* Does the mentioned entity already exist? */ +/************************************************************************/ + +int OGRDXFWriterDS::CheckEntityID( const char *pszEntityID ) + +{ + std::set::iterator it; + + it = aosUsedEntities.find( pszEntityID ); + if( it != aosUsedEntities.end() ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* WriteEntityID() */ +/************************************************************************/ + +long OGRDXFWriterDS::WriteEntityID( VSILFILE *fp, long nPreferredFID ) + +{ + CPLString osEntityID; + + if( nPreferredFID != OGRNullFID ) + { + + osEntityID.Printf( "%X", (unsigned int) nPreferredFID ); + if( !CheckEntityID( osEntityID ) ) + { + aosUsedEntities.insert( osEntityID ); + WriteValue( fp, 5, osEntityID ); + return nPreferredFID; + } + } + + do + { + osEntityID.Printf( "%X", nNextFID++ ); + } + while( CheckEntityID( osEntityID ) ); + + aosUsedEntities.insert( osEntityID ); + WriteValue( fp, 5, osEntityID ); + + return nNextFID - 1; +} + +/************************************************************************/ +/* UpdateExtent() */ +/************************************************************************/ + +void OGRDXFWriterDS::UpdateExtent( OGREnvelope* psEnvelope ) +{ + oGlobalEnvelope.Merge(*psEnvelope); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfwriterlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfwriterlayer.cpp new file mode 100644 index 000000000..de8c83283 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/dxf/ogrdxfwriterlayer.cpp @@ -0,0 +1,1227 @@ +/****************************************************************************** + * $Id: ogrdxfwriterlayer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: DXF Translator + * Purpose: Implements OGRDXFWriterLayer - the OGRLayer class used for + * writing a DXF file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_dxf.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_featurestyle.h" + +CPL_CVSID("$Id: ogrdxfwriterlayer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* OGRDXFWriterLayer() */ +/************************************************************************/ + +OGRDXFWriterLayer::OGRDXFWriterLayer( OGRDXFWriterDS *poDS, VSILFILE *fp ) + +{ + this->fp = fp; + this->poDS = poDS; + + nNextAutoID = 1; + bWriteHatch = CSLTestBoolean(CPLGetConfigOption("DXF_WRITE_HATCH", "YES")); + + poFeatureDefn = new OGRFeatureDefn( "entities" ); + poFeatureDefn->Reference(); + + OGRFieldDefn oLayerField( "Layer", OFTString ); + poFeatureDefn->AddFieldDefn( &oLayerField ); + + OGRFieldDefn oClassField( "SubClasses", OFTString ); + poFeatureDefn->AddFieldDefn( &oClassField ); + + OGRFieldDefn oExtendedField( "ExtendedEntity", OFTString ); + poFeatureDefn->AddFieldDefn( &oExtendedField ); + + OGRFieldDefn oLinetypeField( "Linetype", OFTString ); + poFeatureDefn->AddFieldDefn( &oLinetypeField ); + + OGRFieldDefn oEntityHandleField( "EntityHandle", OFTString ); + poFeatureDefn->AddFieldDefn( &oEntityHandleField ); + + OGRFieldDefn oTextField( "Text", OFTString ); + poFeatureDefn->AddFieldDefn( &oTextField ); + + OGRFieldDefn oBlockField( "BlockName", OFTString ); + poFeatureDefn->AddFieldDefn( &oBlockField ); + + OGRFieldDefn oScaleField( "BlockScale", OFTRealList ); + poFeatureDefn->AddFieldDefn( &oScaleField ); + + OGRFieldDefn oBlockAngleField( "BlockAngle", OFTReal ); + poFeatureDefn->AddFieldDefn( &oBlockAngleField ); +} + +/************************************************************************/ +/* ~OGRDXFWriterLayer() */ +/************************************************************************/ + +OGRDXFWriterLayer::~OGRDXFWriterLayer() + +{ + if( poFeatureDefn ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetFP() */ +/* */ +/* Redirect output. Mostly used for writing block definitions. */ +/************************************************************************/ + +void OGRDXFWriterLayer::ResetFP( VSILFILE *fpNew ) + +{ + fp = fpNew; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRDXFWriterLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + else if( EQUAL(pszCap,OLCSequentialWrite) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/* */ +/* This is really a dummy as our fields are precreated. */ +/************************************************************************/ + +OGRErr OGRDXFWriterLayer::CreateField( OGRFieldDefn *poField, + int bApproxOK ) + +{ + if( poFeatureDefn->GetFieldIndex(poField->GetNameRef()) >= 0 + && bApproxOK ) + return OGRERR_NONE; + + CPLError( CE_Failure, CPLE_AppDefined, + "DXF layer does not support arbitrary field creation, field '%s' not created.", + poField->GetNameRef() ); + + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* WriteValue() */ +/************************************************************************/ + +int OGRDXFWriterLayer::WriteValue( int nCode, const char *pszValue ) + +{ + CPLString osLinePair; + + osLinePair.Printf( "%3d\n", nCode ); + + if( strlen(pszValue) < 255 ) + osLinePair += pszValue; + else + osLinePair.append( pszValue, 255 ); + + osLinePair += "\n"; + + return VSIFWriteL( osLinePair.c_str(), + 1, osLinePair.size(), fp ) == osLinePair.size(); +} + +/************************************************************************/ +/* WriteValue() */ +/************************************************************************/ + +int OGRDXFWriterLayer::WriteValue( int nCode, int nValue ) + +{ + CPLString osLinePair; + + osLinePair.Printf( "%3d\n%d\n", nCode, nValue ); + + return VSIFWriteL( osLinePair.c_str(), + 1, osLinePair.size(), fp ) == osLinePair.size(); +} + +/************************************************************************/ +/* WriteValue() */ +/************************************************************************/ + +int OGRDXFWriterLayer::WriteValue( int nCode, double dfValue ) + +{ + char szLinePair[64]; + + CPLsnprintf(szLinePair, sizeof(szLinePair), "%3d\n%.15g\n", nCode, dfValue ); + size_t nLen = strlen(szLinePair); + + return VSIFWriteL( szLinePair, + 1, nLen, fp ) == nLen; +} + +/************************************************************************/ +/* WriteCore() */ +/* */ +/* Write core fields common to all sorts of elements. */ +/************************************************************************/ + +OGRErr OGRDXFWriterLayer::WriteCore( OGRFeature *poFeature ) + +{ +/* -------------------------------------------------------------------- */ +/* Write out an entity id. I'm not sure why this is critical, */ +/* but it seems that VoloView will just quietly fail to open */ +/* dxf files without entity ids set on most/all entities. */ +/* Also, for reasons I don't understand these ids seem to have */ +/* to start somewhere around 0x50 hex (80 decimal). */ +/* -------------------------------------------------------------------- */ + poFeature->SetFID( poDS->WriteEntityID(fp,(int)poFeature->GetFID()) ); + +/* -------------------------------------------------------------------- */ +/* For now we assign everything to the default layer - layer */ +/* "0" - if there is no layer property on the source features. */ +/* -------------------------------------------------------------------- */ + const char *pszLayer = poFeature->GetFieldAsString( "Layer" ); + if( pszLayer == NULL || strlen(pszLayer) == 0 ) + { + WriteValue( 8, "0" ); + } + else + { + const char *pszExists = + poDS->oHeaderDS.LookupLayerProperty( pszLayer, "Exists" ); + if( (pszExists == NULL || strlen(pszExists) == 0) + && CSLFindString( poDS->papszLayersToCreate, pszLayer ) == -1 ) + { + poDS->papszLayersToCreate = + CSLAddString( poDS->papszLayersToCreate, pszLayer ); + } + + WriteValue( 8, pszLayer ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* WriteINSERT() */ +/************************************************************************/ + +OGRErr OGRDXFWriterLayer::WriteINSERT( OGRFeature *poFeature ) + +{ + WriteValue( 0, "INSERT" ); + WriteCore( poFeature ); + WriteValue( 100, "AcDbEntity" ); + WriteValue( 100, "AcDbBlockReference" ); + WriteValue( 2, poFeature->GetFieldAsString("BlockName") ); + + // Write style symbol color + OGRStyleTool *poTool = NULL; + OGRStyleMgr oSM; + if( poFeature->GetStyleString() != NULL ) + { + oSM.InitFromFeature( poFeature ); + + if( oSM.GetPartCount() > 0 ) + poTool = oSM.GetPart(0); + } + if( poTool && poTool->GetType() == OGRSTCSymbol ) + { + OGRStyleSymbol *poSymbol = (OGRStyleSymbol *) poTool; + GBool bDefault; + + if( poSymbol->Color(bDefault) != NULL && !bDefault ) + WriteValue( 62, ColorStringToDXFColor( poSymbol->Color(bDefault) ) ); + } + delete poTool; + +/* -------------------------------------------------------------------- */ +/* Write location. */ +/* -------------------------------------------------------------------- */ + OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef(); + + WriteValue( 10, poPoint->getX() ); + if( !WriteValue( 20, poPoint->getY() ) ) + return OGRERR_FAILURE; + + if( poPoint->getGeometryType() == wkbPoint25D ) + { + if( !WriteValue( 30, poPoint->getZ() ) ) + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Write scaling. */ +/* -------------------------------------------------------------------- */ + int nScaleCount; + const double *padfScale = + poFeature->GetFieldAsDoubleList( "BlockScale", &nScaleCount ); + + if( nScaleCount == 3 ) + { + WriteValue( 41, padfScale[0] ); + WriteValue( 42, padfScale[1] ); + WriteValue( 43, padfScale[2] ); + } + +/* -------------------------------------------------------------------- */ +/* Write rotation. */ +/* -------------------------------------------------------------------- */ + double dfAngle = poFeature->GetFieldAsDouble( "BlockAngle" ); + + if( dfAngle != 0.0 ) + { + WriteValue( 50, dfAngle ); // degrees + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* WritePOINT() */ +/************************************************************************/ + +OGRErr OGRDXFWriterLayer::WritePOINT( OGRFeature *poFeature ) + +{ + WriteValue( 0, "POINT" ); + WriteCore( poFeature ); + WriteValue( 100, "AcDbEntity" ); + WriteValue( 100, "AcDbPoint" ); + + // Write style pen color + OGRStyleTool *poTool = NULL; + OGRStyleMgr oSM; + if( poFeature->GetStyleString() != NULL ) + { + oSM.InitFromFeature( poFeature ); + + if( oSM.GetPartCount() > 0 ) + poTool = oSM.GetPart(0); + } + if( poTool && poTool->GetType() == OGRSTCPen ) + { + OGRStylePen *poPen = (OGRStylePen *) poTool; + GBool bDefault; + + if( poPen->Color(bDefault) != NULL && !bDefault ) + WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) ); + } + delete poTool; + + OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef(); + + WriteValue( 10, poPoint->getX() ); + if( !WriteValue( 20, poPoint->getY() ) ) + return OGRERR_FAILURE; + + if( poPoint->getGeometryType() == wkbPoint25D ) + { + if( !WriteValue( 30, poPoint->getZ() ) ) + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TextEscape() */ +/* */ +/* Translate UTF8 to Win1252 and escape special characters like */ +/* newline and space with DXF style escapes. Note that */ +/* non-win1252 unicode characters are translated using the */ +/* unicode escape sequence. */ +/************************************************************************/ + +CPLString OGRDXFWriterLayer::TextEscape( const char *pszInput ) + +{ + CPLString osResult; + wchar_t *panInput = CPLRecodeToWChar( pszInput, + CPL_ENC_UTF8, + CPL_ENC_UCS2 ); + int i; + + + for( i = 0; panInput[i] != 0; i++ ) + { + if( panInput[i] == '\n' ) + osResult += "\\P"; + else if( panInput[i] == ' ' ) + osResult += "\\~"; + else if( panInput[i] == '\\' ) + osResult += "\\\\"; + else if( panInput[i] > 255 ) + { + CPLString osUnicode; + osUnicode.Printf( "\\U+%04x", (int) panInput[i] ); + osResult += osUnicode; + } + else + osResult += (char) panInput[i]; + } + + CPLFree(panInput); + + return osResult; +} + +/************************************************************************/ +/* WriteTEXT() */ +/************************************************************************/ + +OGRErr OGRDXFWriterLayer::WriteTEXT( OGRFeature *poFeature ) + +{ + WriteValue( 0, "MTEXT" ); + WriteCore( poFeature ); + WriteValue( 100, "AcDbEntity" ); + WriteValue( 100, "AcDbMText" ); + +/* -------------------------------------------------------------------- */ +/* Do we have styling information? */ +/* -------------------------------------------------------------------- */ + OGRStyleTool *poTool = NULL; + OGRStyleMgr oSM; + + if( poFeature->GetStyleString() != NULL ) + { + oSM.InitFromFeature( poFeature ); + + if( oSM.GetPartCount() > 0 ) + poTool = oSM.GetPart(0); + } + +/* ==================================================================== */ +/* Process the LABEL tool. */ +/* ==================================================================== */ + if( poTool && poTool->GetType() == OGRSTCLabel ) + { + OGRStyleLabel *poLabel = (OGRStyleLabel *) poTool; + GBool bDefault; + +/* -------------------------------------------------------------------- */ +/* Color */ +/* -------------------------------------------------------------------- */ + if( poLabel->ForeColor(bDefault) != NULL && !bDefault ) + WriteValue( 62, ColorStringToDXFColor( + poLabel->ForeColor(bDefault) ) ); + +/* -------------------------------------------------------------------- */ +/* Angle */ +/* -------------------------------------------------------------------- */ + double dfAngle = poLabel->Angle(bDefault); + + // The DXF2000 reference says this is in radians, but in files + // I see it seems to be in degrees. Perhaps this is version dependent? + if( !bDefault ) + WriteValue( 50, dfAngle ); + +/* -------------------------------------------------------------------- */ +/* Height - We need to fetch this in georeferenced units - I'm */ +/* doubt the default translation mechanism will be much good. */ +/* -------------------------------------------------------------------- */ + poTool->SetUnit( OGRSTUGround ); + double dfHeight = poLabel->Size(bDefault); + + if( !bDefault ) + WriteValue( 40, dfHeight ); + +/* -------------------------------------------------------------------- */ +/* Anchor / Attachment Point */ +/* -------------------------------------------------------------------- */ + int nAnchor = poLabel->Anchor(bDefault); + + if( !bDefault ) + { + const static int anAnchorMap[] = + { -1, 7, 8, 9, 4, 5, 6, 1, 2, 3, 7, 8, 9 }; + + if( nAnchor > 0 && nAnchor < 13 ) + WriteValue( 71, anAnchorMap[nAnchor] ); + } + +/* -------------------------------------------------------------------- */ +/* Escape the text, and convert to ISO8859. */ +/* -------------------------------------------------------------------- */ + const char *pszText = poLabel->TextString( bDefault ); + + if( pszText != NULL && !bDefault ) + { + CPLString osEscaped = TextEscape( pszText ); + WriteValue( 1, osEscaped ); + } + } + + delete poTool; + +/* -------------------------------------------------------------------- */ +/* Write the location. */ +/* -------------------------------------------------------------------- */ + OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef(); + + WriteValue( 10, poPoint->getX() ); + if( !WriteValue( 20, poPoint->getY() ) ) + return OGRERR_FAILURE; + + if( poPoint->getGeometryType() == wkbPoint25D ) + { + if( !WriteValue( 30, poPoint->getZ() ) ) + return OGRERR_FAILURE; + } + + return OGRERR_NONE; + +} + +/************************************************************************/ +/* PrepareLineTypeDefinition() */ +/************************************************************************/ +CPLString +OGRDXFWriterLayer::PrepareLineTypeDefinition( CPL_UNUSED OGRFeature *poFeature, + OGRStyleTool *poTool ) +{ + CPLString osDef; + OGRStylePen *poPen = (OGRStylePen *) poTool; + GBool bDefault; + const char *pszPattern; + +/* -------------------------------------------------------------------- */ +/* Fetch pattern. */ +/* -------------------------------------------------------------------- */ + pszPattern = poPen->Pattern( bDefault ); + if( bDefault || strlen(pszPattern) == 0 ) + return ""; + +/* -------------------------------------------------------------------- */ +/* Split into pen up / pen down bits. */ +/* -------------------------------------------------------------------- */ + char **papszTokens = CSLTokenizeString(pszPattern); + int i; + double dfTotalLength = 0; + + for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; i++ ) + { + const char *pszToken = papszTokens[i]; + const char *pszUnit; + CPLString osAmount; + CPLString osDXFEntry; + + // Split amount and unit. + for( pszUnit = pszToken; + strchr( "0123456789.", *pszUnit) != NULL; + pszUnit++ ) {} + + osAmount.assign(pszToken,(int) (pszUnit-pszToken)); + + // If the unit is other than 'g' we really should be trying to + // do some type of transformation - but what to do? Pretty hard. + + // + + // Even entries are "pen down" represented as negative in DXF. + if( i%2 == 0 ) + osDXFEntry.Printf( " 49\n-%s\n 74\n0\n", osAmount.c_str() ); + else + osDXFEntry.Printf( " 49\n%s\n 74\n0\n", osAmount.c_str() ); + + osDef += osDXFEntry; + + dfTotalLength += CPLAtof(osAmount); + } + +/* -------------------------------------------------------------------- */ +/* Prefix 73 and 40 items to the definition. */ +/* -------------------------------------------------------------------- */ + CPLString osPrefix; + + osPrefix.Printf( " 73\n%d\n 40\n%.6g\n", + CSLCount(papszTokens), + dfTotalLength ); + osDef = osPrefix + osDef; + + CSLDestroy( papszTokens ); + + return osDef; +} + +/************************************************************************/ +/* WritePOLYLINE() */ +/************************************************************************/ + +OGRErr OGRDXFWriterLayer::WritePOLYLINE( OGRFeature *poFeature, + OGRGeometry *poGeom ) + +{ +/* -------------------------------------------------------------------- */ +/* For now we handle multilinestrings by writing a series of */ +/* entities. */ +/* -------------------------------------------------------------------- */ + if( poGeom == NULL ) + poGeom = poFeature->GetGeometryRef(); + + if ( poGeom->IsEmpty() ) + { + return OGRERR_NONE; + } + + if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon + || wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ) + { + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom; + int iGeom; + OGRErr eErr = OGRERR_NONE; + + for( iGeom = 0; + eErr == OGRERR_NONE && iGeom < poGC->getNumGeometries(); + iGeom++ ) + { + eErr = WritePOLYLINE( poFeature, poGC->getGeometryRef( iGeom ) ); + } + + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Polygons are written with on entity per ring. */ +/* -------------------------------------------------------------------- */ + if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) + { + OGRPolygon *poPoly = (OGRPolygon *) poGeom; + int iGeom; + OGRErr eErr; + + eErr = WritePOLYLINE( poFeature, poPoly->getExteriorRing() ); + for( iGeom = 0; + eErr == OGRERR_NONE && iGeom < poPoly->getNumInteriorRings(); + iGeom++ ) + { + eErr = WritePOLYLINE( poFeature, poPoly->getInteriorRing(iGeom) ); + } + + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Do we now have a geometry we can work with? */ +/* -------------------------------------------------------------------- */ + if( wkbFlatten(poGeom->getGeometryType()) != wkbLineString ) + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + + OGRLineString *poLS = (OGRLineString *) poGeom; + +/* -------------------------------------------------------------------- */ +/* Write as a lightweight polygon, */ +/* or as POLYLINE if the line contains different heights */ +/* -------------------------------------------------------------------- */ + int bHasDifferentZ = FALSE; + if( poLS->getGeometryType() == wkbLineString25D ) + { + double z0 = poLS->getZ(0); + for( int iVert = 0; iVert < poLS->getNumPoints(); iVert++ ) + { + if (z0 != poLS->getZ(iVert)) + { + bHasDifferentZ = TRUE; + break; + } + } + } + + WriteValue( 0, bHasDifferentZ ? "POLYLINE" : "LWPOLYLINE" ); + WriteCore( poFeature ); + WriteValue( 100, "AcDbEntity" ); + if( bHasDifferentZ ) + { + WriteValue( 100, "AcDb3dPolyline" ); + WriteValue( 10, 0.0 ); + WriteValue( 20, 0.0 ); + WriteValue( 30, 0.0 ); + } + else + WriteValue( 100, "AcDbPolyline" ); + if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) ) + WriteValue( 70, 1 + (bHasDifferentZ ? 8 : 0) ); + else + WriteValue( 70, 0 + (bHasDifferentZ ? 8 : 0) ); + if( !bHasDifferentZ ) + WriteValue( 90, poLS->getNumPoints() ); + else + WriteValue( 66, "1" ); // Vertex Flag + +/* -------------------------------------------------------------------- */ +/* Do we have styling information? */ +/* -------------------------------------------------------------------- */ + OGRStyleTool *poTool = NULL; + OGRStyleMgr oSM; + + if( poFeature->GetStyleString() != NULL ) + { + oSM.InitFromFeature( poFeature ); + + if( oSM.GetPartCount() > 0 ) + poTool = oSM.GetPart(0); + } + +/* -------------------------------------------------------------------- */ +/* Handle a PEN tool to control drawing color and width. */ +/* Perhaps one day also dottedness, etc. */ +/* -------------------------------------------------------------------- */ + if( poTool && poTool->GetType() == OGRSTCPen ) + { + OGRStylePen *poPen = (OGRStylePen *) poTool; + GBool bDefault; + + if( poPen->Color(bDefault) != NULL && !bDefault ) + WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) ); + + // we want to fetch the width in ground units. + poPen->SetUnit( OGRSTUGround, 1.0 ); + double dfWidth = poPen->Width(bDefault); + + if( !bDefault ) + WriteValue( 370, (int) floor(dfWidth * 100 + 0.5) ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a Linetype for the feature? */ +/* -------------------------------------------------------------------- */ + CPLString osLineType = poFeature->GetFieldAsString( "Linetype" ); + + if( osLineType.size() > 0 + && (poDS->oHeaderDS.LookupLineType( osLineType ) != NULL + || oNewLineTypes.count(osLineType) > 0 ) ) + { + // Already define -> just reference it. + WriteValue( 6, osLineType ); + } + else if( poTool != NULL && poTool->GetType() == OGRSTCPen ) + { + CPLString osDefinition = PrepareLineTypeDefinition( poFeature, + poTool ); + + if( osDefinition != "" && osLineType == "" ) + { + // Is this definition already created and named? + std::map::iterator it; + + for( it = oNewLineTypes.begin(); + it != oNewLineTypes.end(); + it++ ) + { + if( (*it).second == osDefinition ) + { + osLineType = (*it).first; + break; + } + } + + // create an automatic name for it. + if( osLineType == "" ) + { + do + { + osLineType.Printf( "AutoLineType-%d", nNextAutoID++ ); + } + while( poDS->oHeaderDS.LookupLineType(osLineType) != NULL ); + } + } + + // If it isn't already defined, add it now. + if( osDefinition != "" && oNewLineTypes.count(osLineType) == 0 ) + { + oNewLineTypes[osLineType] = osDefinition; + WriteValue( 6, osLineType ); + } + } + +/* -------------------------------------------------------------------- */ +/* Write the vertices */ +/* -------------------------------------------------------------------- */ + + if( !bHasDifferentZ && poLS->getGeometryType() == wkbLineString25D ) + { + // if LWPOLYLINE with Z write it only once + if( !WriteValue( 38, poLS->getZ(0) ) ) + return OGRERR_FAILURE; + } + + int iVert; + + for( iVert = 0; iVert < poLS->getNumPoints(); iVert++ ) + { + if( bHasDifferentZ ) + { + WriteValue( 0, "VERTEX" ); + WriteValue( 100, "AcDbEntity" ); + WriteValue( 100, "AcDbVertex" ); + WriteValue( 100, "AcDb3dPolylineVertex" ); + WriteCore( poFeature ); + } + WriteValue( 10, poLS->getX(iVert) ); + if( !WriteValue( 20, poLS->getY(iVert) ) ) + return OGRERR_FAILURE; + + if( bHasDifferentZ ) + { + if( !WriteValue( 30 , poLS->getZ(iVert) ) ) + return OGRERR_FAILURE; + WriteValue( 70, 32 ); + } + } + + if( bHasDifferentZ ) + { + WriteValue( 0, "SEQEND" ); + WriteCore( poFeature ); + WriteValue( 100, "AcDbEntity" ); + } + + delete poTool; + + return OGRERR_NONE; + +#ifdef notdef +/* -------------------------------------------------------------------- */ +/* Alternate unmaintained implementation as a polyline entity. */ +/* -------------------------------------------------------------------- */ + WriteValue( 0, "POLYLINE" ); + WriteCore( poFeature ); + WriteValue( 100, "AcDbEntity" ); + WriteValue( 100, "AcDbPolyline" ); + if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) ) + WriteValue( 70, 1 ); + else + WriteValue( 70, 0 ); + WriteValue( 66, "1" ); + + int iVert; + + for( iVert = 0; iVert < poLS->getNumPoints(); iVert++ ) + { + WriteValue( 0, "VERTEX" ); + WriteValue( 8, "0" ); + WriteValue( 10, poLS->getX(iVert) ); + if( !WriteValue( 20, poLS->getY(iVert) ) ) + return OGRERR_FAILURE; + + if( poLS->getGeometryType() == wkbLineString25D ) + { + if( !WriteValue( 30, poLS->getZ(iVert) ) ) + return OGRERR_FAILURE; + } + } + + WriteValue( 0, "SEQEND" ); + WriteValue( 8, "0" ); + + return OGRERR_NONE; +#endif +} + +/************************************************************************/ +/* WriteHATCH() */ +/************************************************************************/ + +OGRErr OGRDXFWriterLayer::WriteHATCH( OGRFeature *poFeature, + OGRGeometry *poGeom ) + +{ +/* -------------------------------------------------------------------- */ +/* For now we handle multipolygons by writing a series of */ +/* entities. */ +/* -------------------------------------------------------------------- */ + if( poGeom == NULL ) + poGeom = poFeature->GetGeometryRef(); + + if ( poGeom->IsEmpty() ) + { + return OGRERR_NONE; + } + + if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ) + { + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom; + int iGeom; + OGRErr eErr = OGRERR_NONE; + + for( iGeom = 0; + eErr == OGRERR_NONE && iGeom < poGC->getNumGeometries(); + iGeom++ ) + { + eErr = WriteHATCH( poFeature, poGC->getGeometryRef( iGeom ) ); + } + + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Do we now have a geometry we can work with? */ +/* -------------------------------------------------------------------- */ + if( wkbFlatten(poGeom->getGeometryType()) != wkbPolygon ) + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + +/* -------------------------------------------------------------------- */ +/* Write as a hatch. */ +/* -------------------------------------------------------------------- */ + WriteValue( 0, "HATCH" ); + WriteCore( poFeature ); + WriteValue( 100, "AcDbEntity" ); + WriteValue( 100, "AcDbHatch" ); + + WriteValue( 10, 0 ); // elevation point X. 0 for DXF + WriteValue( 20, 0 ); // elevation point Y + WriteValue( 30, 0 ); // elevation point Z + WriteValue(210, 0 ); // extrusion direction X + WriteValue(220, 0 ); // extrusion direction Y + WriteValue(230,1.0); // extrusion direction Z + + WriteValue( 2, "SOLID" ); // fill pattern + WriteValue( 70, 1 ); // solid fill + WriteValue( 71, 0 ); // associativity + +/* -------------------------------------------------------------------- */ +/* Do we have styling information? */ +/* -------------------------------------------------------------------- */ + OGRStyleTool *poTool = NULL; + OGRStyleMgr oSM; + + if( poFeature->GetStyleString() != NULL ) + { + oSM.InitFromFeature( poFeature ); + + if( oSM.GetPartCount() > 0 ) + poTool = oSM.GetPart(0); + } + // Write style brush fore color + if( poTool && poTool->GetType() == OGRSTCBrush ) + { + OGRStyleBrush *poBrush = (OGRStyleBrush *) poTool; + GBool bDefault; + + if( poBrush->ForeColor(bDefault) != NULL && !bDefault ) + WriteValue( 62, ColorStringToDXFColor( poBrush->ForeColor(bDefault) ) ); + } + delete poTool; + +/* -------------------------------------------------------------------- */ +/* Handle a PEN tool to control drawing color and width. */ +/* Perhaps one day also dottedness, etc. */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + if( poTool && poTool->GetType() == OGRSTCPen ) + { + OGRStylePen *poPen = (OGRStylePen *) poTool; + GBool bDefault; + + if( poPen->Color(bDefault) != NULL && !bDefault ) + WriteValue( 62, ColorStringToDXFColor( poPen->Color(bDefault) ) ); + + double dfWidthInMM = poPen->Width(bDefault); + + if( !bDefault ) + WriteValue( 370, (int) floor(dfWidthInMM * 100 + 0.5) ); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a Linetype for the feature? */ +/* -------------------------------------------------------------------- */ + CPLString osLineType = poFeature->GetFieldAsString( "Linetype" ); + + if( osLineType.size() > 0 + && (poDS->oHeaderDS.LookupLineType( osLineType ) != NULL + || oNewLineTypes.count(osLineType) > 0 ) ) + { + // Already define -> just reference it. + WriteValue( 6, osLineType ); + } + else if( poTool != NULL && poTool->GetType() == OGRSTCPen ) + { + CPLString osDefinition = PrepareLineTypeDefinition( poFeature, + poTool ); + + if( osDefinition != "" && osLineType == "" ) + { + // Is this definition already created and named? + std::map::iterator it; + + for( it = oNewLineTypes.begin(); + it != oNewLineTypes.end(); + it++ ) + { + if( (*it).second == osDefinition ) + { + osLineType = (*it).first; + break; + } + } + + // create an automatic name for it. + if( osLineType == "" ) + { + do + { + osLineType.Printf( "AutoLineType-%d", nNextAutoID++ ); + } + while( poDS->oHeaderDS.LookupLineType(osLineType) != NULL ); + } + } + + // If it isn't already defined, add it now. + if( osDefinition != "" && oNewLineTypes.count(osLineType) == 0 ) + { + oNewLineTypes[osLineType] = osDefinition; + WriteValue( 6, osLineType ); + } + } + delete poTool; +#endif + +/* -------------------------------------------------------------------- */ +/* Process the loops (rings). */ +/* -------------------------------------------------------------------- */ + OGRPolygon *poPoly = (OGRPolygon *) poGeom; + + WriteValue( 91, poPoly->getNumInteriorRings() + 1 ); + + for( int iRing = -1; iRing < poPoly->getNumInteriorRings(); iRing++ ) + { + OGRLinearRing *poLR; + + if( iRing == -1 ) + poLR = poPoly->getExteriorRing(); + else + poLR = poPoly->getInteriorRing( iRing ); + + WriteValue( 92, 2 ); // Polyline + WriteValue( 72, 0 ); // has bulge + WriteValue( 73, 1 ); // is closed + WriteValue( 93, poLR->getNumPoints() ); + + for( int iVert = 0; iVert < poLR->getNumPoints(); iVert++ ) + { + WriteValue( 10, poLR->getX(iVert) ); + WriteValue( 20, poLR->getY(iVert) ); + } + + WriteValue( 97, 0 ); // 0 source boundary objects + } + + WriteValue( 75, 0 ); // hatch style = Hatch "odd parity" area (Normal style) + WriteValue( 76, 1 ); // hatch pattern type = predefined + WriteValue( 98, 0 ); // 0 seed points + + return OGRERR_NONE; + +#ifdef notdef +/* -------------------------------------------------------------------- */ +/* Alternate unmaintained implementation as a polyline entity. */ +/* -------------------------------------------------------------------- */ + WriteValue( 0, "POLYLINE" ); + WriteCore( poFeature ); + WriteValue( 100, "AcDbEntity" ); + WriteValue( 100, "AcDbPolyline" ); + if( EQUAL( poGeom->getGeometryName(), "LINEARRING" ) ) + WriteValue( 70, 1 ); + else + WriteValue( 70, 0 ); + WriteValue( 66, "1" ); + + int iVert; + + for( iVert = 0; iVert < poLS->getNumPoints(); iVert++ ) + { + WriteValue( 0, "VERTEX" ); + WriteValue( 8, "0" ); + WriteValue( 10, poLS->getX(iVert) ); + if( !WriteValue( 20, poLS->getY(iVert) ) ) + return OGRERR_FAILURE; + + if( poLS->getGeometryType() == wkbLineString25D ) + { + if( !WriteValue( 30, poLS->getZ(iVert) ) ) + return OGRERR_FAILURE; + } + } + + WriteValue( 0, "SEQEND" ); + WriteValue( 8, "0" ); + + return OGRERR_NONE; +#endif +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRDXFWriterLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + OGRGeometry *poGeom = poFeature->GetGeometryRef(); + OGRwkbGeometryType eGType = wkbNone; + + if( poGeom != NULL ) + { + if( !poGeom->IsEmpty() ) + { + OGREnvelope sEnvelope; + poGeom->getEnvelope(&sEnvelope); + poDS->UpdateExtent(&sEnvelope); + } + eGType = wkbFlatten(poGeom->getGeometryType()); + } + + if( eGType == wkbPoint ) + { + const char *pszBlockName = poFeature->GetFieldAsString("BlockName"); + + // we don't want to treat as a block ref if we are writing blocks layer + if( pszBlockName != NULL + && poDS->poBlocksLayer != NULL + && poFeature->GetDefnRef() == poDS->poBlocksLayer->GetLayerDefn()) + pszBlockName = NULL; + + // We don't want to treat as a blocks ref if the block is not defined + if( pszBlockName + && poDS->oHeaderDS.LookupBlock(pszBlockName) == NULL ) + { + if( poDS->poBlocksLayer == NULL + || poDS->poBlocksLayer->FindBlock(pszBlockName) == NULL ) + pszBlockName = NULL; + } + + if( pszBlockName != NULL ) + return WriteINSERT( poFeature ); + + else if( poFeature->GetStyleString() != NULL + && EQUALN(poFeature->GetStyleString(),"LABEL",5) ) + return WriteTEXT( poFeature ); + else + return WritePOINT( poFeature ); + } + else if( eGType == wkbLineString + || eGType == wkbMultiLineString ) + return WritePOLYLINE( poFeature ); + + else if( eGType == wkbPolygon + || eGType == wkbMultiPolygon ) + { + if( bWriteHatch ) + return WriteHATCH( poFeature ); + else + return WritePOLYLINE( poFeature ); + } + + // Explode geometry collections into multiple entities. + else if( eGType == wkbGeometryCollection ) + { + OGRGeometryCollection *poGC = (OGRGeometryCollection *) + poFeature->StealGeometry(); + int iGeom; + + for( iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++ ) + { + poFeature->SetGeometry( poGC->getGeometryRef(iGeom) ); + + OGRErr eErr = CreateFeature( poFeature ); + + if( eErr != OGRERR_NONE ) + return eErr; + + } + + poFeature->SetGeometryDirectly( poGC ); + return OGRERR_NONE; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "No known way to write feature with geometry '%s'.", + OGRGeometryTypeToName(eGType) ); + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ColorStringToDXFColor() */ +/************************************************************************/ + +int OGRDXFWriterLayer::ColorStringToDXFColor( const char *pszRGB ) + +{ +/* -------------------------------------------------------------------- */ +/* Parse the RGB string. */ +/* -------------------------------------------------------------------- */ + if( pszRGB == NULL ) + return -1; + + int nRed, nGreen, nBlue, nTransparency = 255; + + int nCount = sscanf(pszRGB,"#%2x%2x%2x%2x",&nRed,&nGreen,&nBlue, + &nTransparency); + + if (nCount < 3 ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Find near color in DXF palette. */ +/* -------------------------------------------------------------------- */ + const unsigned char *pabyDXFColors = ACGetColorTable(); + int i; + int nMinDist = 768; + int nBestColor = -1; + + for( i = 1; i < 256; i++ ) + { + int nDist = ABS(nRed - pabyDXFColors[i*3+0]) + + ABS(nGreen - pabyDXFColors[i*3+1]) + + ABS(nBlue - pabyDXFColors[i*3+2]); + + if( nDist < nMinDist ) + { + nBestColor = i; + nMinDist = nDist; + } + } + + return nBestColor; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/GNUmakefile new file mode 100644 index 000000000..7d13656f8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogredigeodriver.o ogredigeodatasource.o ogredigeolayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_edigeo.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/drv_edigeo.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/drv_edigeo.html new file mode 100644 index 000000000..3d2ca6b9f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/drv_edigeo.html @@ -0,0 +1,66 @@ + + +EDIGEO + + + + +

    EDIGEO

    + +(GDAL/OGR >= 1.9.0)

    + +This driver reads files encoded in the French EDIGEO exchange format, +a text based file format aimed at exchanging geographical information between GIS, with +powerful description capabilities, topology modelling, etc.

    + +The driver has been developed to read files of the French PCI +(Plan Cadastral Informatisé - Digital Cadastral Plan) as produced by the +DGI (Direction Générale des Impots - General Tax Office). The driver +should also be able to open other EDIGEO based products.

    + +The driver must be provided with the .THF file describing the EDIGEO exchange and it will +read the associated .DIC, .GEO, .SCD, .QAL and .VEC files.

    + +In order the SRS of the layers to be correctly built, the IGNF file that contains +the defintion of IGN SRS must be placed in the directory of PROJ.4 resource files.

    + +The whole set of files will be parsed into memory. This may be a limitation if dealing with +big EDIGEO exchanges.

    + + + +

    Labels

    + +For EDIGEO PCI files, the labels are contained in the ID_S_OBJ_Z_1_2_2 layer. OGR will export +styling following the OGR Feature Style specification.

    + +It will also add the following fields : +

      +
    • OGR_OBJ_LNK: the id of the object that is linked to this label +
    • OBJ_OBJ_LNK_LAYER: the name of the layer of the linked object +
    • OGR_ATR_VAL: the value of the attribute to display (found in the ATR attribute of the OGR_OBJ_LNK object) +
    • OGR_ANGLE: the rotation angle in degrees (0 = horizontal, counter-clock-wise oriented) +
    • OGR_FONT_SIZE: the value of the HEI attribute multiplied by the value of the configuration option OGR_EDIGEO_FONT_SIZE_FACTOR that defaults to 2. +
    + +Combined with the FON (font family) attributes, they can be used to define the styling in QGIS for example.

    + +By default, OGR will create specific layers (xxx_LABEL) to dispatch into the various labels of the ID_S_OBJ_Z_1_2_2 layer +according to the value of xxx=OBJ_OBJ_LNK_LAYER. This can be disabled by setting OGR_EDIGEO_CREATE_LABEL_LAYERS to NO.

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/makefile.vc new file mode 100644 index 000000000..9921bb7cf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogredigeodriver.obj ogredigeodatasource.obj ogredigeolayer.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/ogr_edigeo.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/ogr_edigeo.h new file mode 100644 index 000000000..1a11f9c9e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/ogr_edigeo.h @@ -0,0 +1,244 @@ +/****************************************************************************** + * $Id: ogr_edigeo.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: EDIGEO Translator + * Purpose: Definition of classes for OGR .edigeo driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_EDIGEO_H_INCLUDED +#define _OGR_EDIGEO_H_INCLUDED + +#include "ogrsf_frmts.h" +#include +#include +#include + +/************************************************************************/ +/* OGREDIGEOLayer */ +/************************************************************************/ + +class OGREDIGEODataSource; + +class OGREDIGEOLayer : public OGRLayer +{ + OGREDIGEODataSource* poDS; + + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference *poSRS; + + int nNextFID; + + OGRFeature * GetNextRawFeature(); + + std::vector aosFeatures; + + /* Map attribute RID ('TEX2_id') to its index in the OGRFeatureDefn */ + std::map mapAttributeToIndex; + + public: + OGREDIGEOLayer(OGREDIGEODataSource* poDS, + const char* pszName, OGRwkbGeometryType eType, + OGRSpatialReference* poSRS); + ~OGREDIGEOLayer(); + + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + virtual OGRFeature * GetFeature(GIntBig nFID); + virtual GIntBig GetFeatureCount( int bForce ); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); + + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce); + + + void AddFeature(OGRFeature* poFeature); + + int GetAttributeIndex(const CPLString& osRID); + void AddFieldDefn(const CPLString& osName, OGRFieldType eType, + const CPLString& osRID); +}; + +/************************************************************************/ +/* OGREDIGEODataSource */ +/************************************************************************/ + +typedef std::pair intintType; +typedef std::pair xyPairType; +typedef std::vector< xyPairType > xyPairListType; +typedef std::pair strstrType; +typedef std::vector strListType; + +/* From the .DIC file */ +class OGREDIGEOAttributeDef +{ + public: + OGREDIGEOAttributeDef() {} + + CPLString osLAB; /* e.g. TEX2 */ + CPLString osTYP; /* e.g. T */ +}; + +/* From the .SCD file */ +class OGREDIGEOObjectDescriptor +{ + public: + OGREDIGEOObjectDescriptor() {} + + CPLString osRID; /* e.g. BATIMENT_id */ + CPLString osNameRID; /* e.g. ID_N_OBJ_E_2_1_0 */ + CPLString osKND; /* e.g. ARE */ + strListType aosAttrRID; /* e.g. DUR_id, TEX_id */ +}; + +/* From the .SCD file */ +class OGREDIGEOAttributeDescriptor +{ + public: + OGREDIGEOAttributeDescriptor() {} + + CPLString osRID; /* e.g. TEX2_id */ + CPLString osNameRID; /* e.g. ID_N_ATT_TEX2 */ + int nWidth; /* e.g. 80 */ +}; + +/* From the .VEC files */ +class OGREDIGEOFEADesc +{ + public: + OGREDIGEOFEADesc() {} + + std::vector< strstrType > aosAttIdVal; /* e.g. (TEX2_id,BECHEREL),(IDU_id,022) */ + CPLString osSCP; /* e.g. COMMUNE_id */ + CPLString osQUP_RID; /* e.g. Actualite_Objet_X */ +}; + +class OGREDIGEODataSource : public OGRDataSource +{ + friend class OGREDIGEOLayer; + + char* pszName; + VSILFILE* fpTHF; + + OGRLayer** papoLayers; + int nLayers; + + VSILFILE* OpenFile(const char *pszType, + const CPLString& osExt); + + CPLString osLON; /* Nom du lot */ + CPLString osGNN; /* Nom du sous-ensemble de données générales */ + CPLString osGON; /* Nom du sous-ensemble de la référence de coordonnées */ + CPLString osQAN; /* Nom du sous-ensemble de qualité */ + CPLString osDIN; /* Nom du sous-ensemble de définition de la nomenclature */ + CPLString osSCN; /* Nom du sous-ensemble de définition du SCD */ + strListType aosGDN; /* Nom du sous-ensemble de données géographiques */ + int ReadTHF(VSILFILE* fp); + + CPLString osREL; + OGRSpatialReference* poSRS; + int ReadGEO(); + + /* Map from ID_N_OBJ_E_2_1_0 to OBJ_E_2_1_0 */ + std::map mapObjects; + + /* Map from ID_N_ATT_TEX2 to (osLAB=TEX2, osTYP=T) */ + std::map mapAttributes; + int ReadDIC(); + + std::vector aoObjList; + /* Map from TEX2_id to (osNameRID=ID_N_ATT_TEX2, nWidth=80) */ + std::map mapAttributesSCD; + int ReadSCD(); + + int bExtentValid; + double dfMinX; + double dfMinY; + double dfMaxX; + double dfMaxY; + int ReadGEN(); + + /* Map from Actualite_Objet_X to (creationData, updateData) */ + std::map mapQAL; + int ReadQAL(); + + std::map mapLayer; + + int CreateLayerFromObjectDesc(const OGREDIGEOObjectDescriptor& objDesc); + + std::map< CPLString, xyPairType > mapPNO; /* Map Noeud_X to (x,y) */ + std::map< CPLString, xyPairListType > mapPAR; /* Map Arc_X to ((x1,y1),...(xn,yn)) */ + std::map< CPLString, OGREDIGEOFEADesc > mapFEA; /* Map Object_X to FEADesc */ + std::map< CPLString, strListType > mapPFE_PAR; /* Map Face_X to (Arc_X1,..Arc_Xn) */ + std::vector< strstrType > listFEA_PFE; /* List of (Object_X,Face_Y) */ + std::vector< std::pair > listFEA_PAR; /* List of (Object_X,(Arc_Y1,..Arc_Yn))) */ + std::vector< strstrType > listFEA_PNO; /* List of (Object_X,Noeud_Y) */ + std::map< CPLString, CPLString> mapFEA_FEA; /* Map Attribut_TEX{X}_id_Objet_{Y} to Objet_Y */ + + int bRecodeToUTF8; + int bHasUTF8ContentOnly; + + int ReadVEC(const char* pszVECName); + + OGRFeature* CreateFeature(const CPLString& osFEA); + int BuildPoints(); + int BuildLineStrings(); + int BuildPolygon(const CPLString& osFEA, + const CPLString& osPFE); + int BuildPolygons(); + + int iATR, iDI3, iDI4, iHEI, iFON; + int iATR_VAL, iANGLE, iSIZE, iOBJ_LNK, iOBJ_LNK_LAYER; + double dfSizeFactor; + int bIncludeFontFamily; + int SetStyle(const CPLString& osFEA, + OGRFeature* poFeature); + + std::set< CPLString > setLayersWithLabels; + void CreateLabelLayers(); + + int bHasReadEDIGEO; + void ReadEDIGEO(); + + public: + OGREDIGEODataSource(); + ~OGREDIGEODataSource(); + + int Open( const char * pszFilename ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount(); + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); + + int HasUTF8ContentOnly() { return bHasUTF8ContentOnly; } +}; + + +#endif /* ndef _OGR_EDIGEO_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/ogredigeodatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/ogredigeodatasource.cpp new file mode 100644 index 000000000..0577b3b96 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/ogredigeodatasource.cpp @@ -0,0 +1,1559 @@ +/****************************************************************************** + * $Id: ogredigeodatasource.cpp 28039 2014-11-30 18:24:59Z rouault $ + * + * Project: EDIGEO Translator + * Purpose: Implements OGREDIGEODataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_edigeo.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogredigeodatasource.cpp 28039 2014-11-30 18:24:59Z rouault $"); + +#ifndef M_PI +# define M_PI 3.1415926535897932384626433832795 +#endif + +/************************************************************************/ +/* OGREDIGEODataSource() */ +/************************************************************************/ + +OGREDIGEODataSource::OGREDIGEODataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; + poSRS = NULL; + + bExtentValid = FALSE; + dfMinX = dfMinY = dfMaxX = dfMaxY = 0; + + fpTHF = NULL; + bHasReadEDIGEO = FALSE; + + bIncludeFontFamily = CSLTestBoolean(CPLGetConfigOption( + "OGR_EDIGEO_INCLUDE_FONT_FAMILY", "YES")); + + iATR = iDI3 = iDI4 = iHEI = iFON = -1; + iATR_VAL = iANGLE = iSIZE = iOBJ_LNK = iOBJ_LNK_LAYER = -1; + dfSizeFactor = CPLAtof(CPLGetConfigOption("OGR_EDIGEO_FONT_SIZE_FACTOR", "2")); + if (dfSizeFactor <= 0 || dfSizeFactor >= 100) + dfSizeFactor = 2; + + bRecodeToUTF8 = CSLTestBoolean(CPLGetConfigOption( + "OGR_EDIGEO_RECODE_TO_UTF8", "YES")); + bHasUTF8ContentOnly = TRUE; +} + +/************************************************************************/ +/* ~OGREDIGEODataSource() */ +/************************************************************************/ + +OGREDIGEODataSource::~OGREDIGEODataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + CPLFree( pszName ); + + if (fpTHF) + VSIFCloseL(fpTHF); + + if (poSRS) + poSRS->Release(); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGREDIGEODataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGREDIGEODataSource::GetLayer( int iLayer ) + +{ + ReadEDIGEO(); + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int OGREDIGEODataSource::GetLayerCount() +{ + ReadEDIGEO(); + return nLayers; +} + +/************************************************************************/ +/* ReadTHF() */ +/************************************************************************/ + +int OGREDIGEODataSource::ReadTHF(VSILFILE* fp) +{ + const char* pszLine; + while((pszLine = CPLReadLine2L(fp, 81, NULL)) != NULL) + { + if (strlen(pszLine) < 8 || pszLine[7] != ':') + continue; + + /* Cf Z 52000 tableau 56 for field list*/ + + if (strncmp(pszLine, "LONSA", 5) == 0) + { + if (osLON.size() != 0) + { + CPLDebug("EDIGEO", "We only handle one lot per THF file"); + break; + } + osLON = pszLine + 8; + } + else if (strncmp(pszLine, "GNNSA", 5) == 0) + osGNN = pszLine + 8; + else if (strncmp(pszLine, "GONSA", 5) == 0) + osGON = pszLine + 8; + else if (strncmp(pszLine, "QANSA", 5) == 0) + osQAN = pszLine + 8; + else if (strncmp(pszLine, "DINSA", 5) == 0) + osDIN = pszLine + 8; + else if (strncmp(pszLine, "SCNSA", 5) == 0) + osSCN = pszLine + 8; + else if (strncmp(pszLine, "GDNSA", 5) == 0) + aosGDN.push_back(pszLine + 8); + } + if (osLON.size() == 0) + { + CPLDebug("EDIGEO", "LON field missing"); + return 0; + } + if (osGON.size() == 0) + { + CPLDebug("EDIGEO", "GON field missing"); + return 0; + } + if (osDIN.size() == 0) + { + CPLDebug("EDIGEO", "DIN field missing"); + return 0; + } + if (osSCN.size() == 0) + { + CPLDebug("EDIGEO", "SCN field missing"); + return FALSE; + } + + CPLDebug("EDIGEO", "LON = %s", osLON.c_str()); + CPLDebug("EDIGEO", "GNN = %s", osGNN.c_str()); + CPLDebug("EDIGEO", "GON = %s", osGON.c_str()); + CPLDebug("EDIGEO", "QAN = %s", osQAN.c_str()); + CPLDebug("EDIGEO", "DIN = %s", osDIN.c_str()); + CPLDebug("EDIGEO", "SCN = %s", osSCN.c_str()); + for(int i=0;i<(int)aosGDN.size();i++) + CPLDebug("EDIGEO", "GDN[%d] = %s", i, aosGDN[i].c_str()); + + return TRUE; +} + + +/************************************************************************/ +/* OpenFile() */ +/************************************************************************/ + +VSILFILE* OGREDIGEODataSource::OpenFile(const char *pszType, + const CPLString& osExt) +{ + CPLString osTmp = osLON + pszType; + CPLString osFilename = CPLFormCIFilename(CPLGetPath(pszName), + osTmp.c_str(), osExt.c_str()); + VSILFILE* fp = VSIFOpenL(osFilename, "rb"); + if (fp == NULL) + { + CPLString osExtLower = osExt; + for(int i=0;i<(int)osExt.size();i++) + osExtLower[i] = (char)tolower(osExt[i]); + CPLString osFilename2 = CPLFormCIFilename(CPLGetPath(pszName), + osTmp.c_str(), osExtLower.c_str()); + fp = VSIFOpenL(osFilename2, "rb"); + if (fp == NULL) + { + CPLDebug("EDIGEO", "Cannot open %s", osFilename.c_str()); + } + } + return fp; +} + +/************************************************************************/ +/* ReadGEO() */ +/************************************************************************/ + +int OGREDIGEODataSource::ReadGEO() +{ + VSILFILE* fp = OpenFile(osGON, "GEO"); + if (fp == NULL) + return FALSE; + + const char* pszLine; + while((pszLine = CPLReadLine2L(fp, 81, NULL)) != NULL) + { + if (strlen(pszLine) < 8 || pszLine[7] != ':') + continue; + + if (strncmp(pszLine, "RELSA", 5) == 0) + { + osREL = pszLine + 8; + CPLDebug("EDIGEO", "REL = %s", osREL.c_str()); + break; + } + } + + VSIFCloseL(fp); + + if (osREL.size() == 0) + { + CPLDebug("EDIGEO", "REL field missing"); + return FALSE; + } + + /* All the SRS names mentionned in B.8.2.3 and B.8.3.1 are in the IGN file */ + poSRS = new OGRSpatialReference(); + CPLString osProj4Str = "+init=IGNF:" + osREL; + if (poSRS->SetFromUserInput(osProj4Str.c_str()) != OGRERR_NONE) + { + /* Hard code a few common cases */ + if (osREL == "LAMB1") + poSRS->importFromProj4("+proj=lcc +lat_1=49.5 +lat_0=49.5 +lon_0=0 +k_0=0.99987734 +x_0=600000 +y_0=200000 +a=6378249.2 +b=6356514.999978254 +nadgrids=ntf_r93.gsb,null +pm=paris +units=m +no_defs"); + else if (osREL == "LAMB2") + poSRS->importFromProj4("+proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=0 +k_0=0.99987742 +x_0=600000 +y_0=200000 +a=6378249.2 +b=6356514.999978254 +nadgrids=ntf_r93.gsb,null +pm=paris +units=m +no_defs"); + else if (osREL == "LAMB3") + poSRS->importFromProj4("+proj=lcc +lat_1=44.1 +lat_0=44.1 +lon_0=0 +k_0=0.9998775 +x_0=600000 +y_0=200000 +a=6378249.2 +b=6356514.999978254 +nadgrids=ntf_r93.gsb,null +pm=paris +units=m +no_defs"); + else if (osREL == "LAMB4") + poSRS->importFromProj4("+proj=lcc +lat_1=42.165 +lat_0=42.165 +lon_0=0 +k_0=0.99994471 +x_0=234.358 +y_0=185861.369 +a=6378249.2 +b=6356514.999978254 +nadgrids=ntf_r93.gsb,null +pm=paris +units=m +no_defs"); + else if (osREL == "LAMB93") + poSRS->importFromProj4("+proj=lcc +lat_1=44 +lat_2=49 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS81 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"); + else + { + CPLDebug("EDIGEO", "Cannot resolve %s SRS. Check that the IGNF file is in the directory of PROJ.4 resource files", osREL.c_str()); + delete poSRS; + poSRS = NULL; + } + } + + return TRUE; +} + +/************************************************************************/ +/* ReadGEN() */ +/************************************************************************/ + +int OGREDIGEODataSource::ReadGEN() +{ + VSILFILE* fp = OpenFile(osGNN, "GEN"); + if (fp == NULL) + return FALSE; + + const char* pszLine; + CPLString osCM1, osCM2; + while((pszLine = CPLReadLine2L(fp, 81, NULL)) != NULL) + { + if (strlen(pszLine) < 8 || pszLine[7] != ':') + continue; + + if (strncmp(pszLine, "CM1CC", 5) == 0) + { + osCM1 = pszLine + 8; + } + else if (strncmp(pszLine, "CM2CC", 5) == 0) + { + osCM2 = pszLine + 8; + } + } + + VSIFCloseL(fp); + + if (osCM1.size() == 0 || osCM2.size() == 0) + return FALSE; + + char** papszTokens1 = CSLTokenizeString2(osCM1.c_str(), ";", 0); + char** papszTokens2 = CSLTokenizeString2(osCM2.c_str(), ";", 0); + if (CSLCount(papszTokens1) == 2 && CSLCount(papszTokens2) == 2) + { + bExtentValid = TRUE; + dfMinX = CPLAtof(papszTokens1[0]); + dfMinY = CPLAtof(papszTokens1[1]); + dfMaxX = CPLAtof(papszTokens2[0]); + dfMaxY = CPLAtof(papszTokens2[1]); + } + CSLDestroy(papszTokens1); + CSLDestroy(papszTokens2); + + return bExtentValid; +} + +/************************************************************************/ +/* ReadDIC() */ +/************************************************************************/ + +int OGREDIGEODataSource::ReadDIC() +{ + VSILFILE* fp = OpenFile(osDIN, "DIC"); + if (fp == NULL) + return FALSE; + + const char* pszLine; + CPLString osRTY, osRID, osLAB, osTYP; + while(TRUE) + { + pszLine = CPLReadLine2L(fp, 81, NULL); + if (pszLine != NULL) + { + if (strlen(pszLine) < 8 || pszLine[7] != ':') + continue; + } + + if (pszLine == NULL || strncmp(pszLine, "RTYSA", 5) == 0) + { + if (osRTY == "DID") + { + //CPLDebug("EDIGEO", "Object %s = %s", + // osRID.c_str(), osLAB.c_str()); + mapObjects[osRID] = osLAB; + } + else if (osRTY == "DIA") + { + //CPLDebug("EDIGEO", "Attribute %s = %s, %s", + // osRID.c_str(), osLAB.c_str(), osTYP.c_str()); + OGREDIGEOAttributeDef sAttributeDef; + sAttributeDef.osLAB = osLAB; + sAttributeDef.osTYP = osTYP; + mapAttributes[osRID] = sAttributeDef; + } + if (pszLine == NULL) + break; + osRTY = pszLine + 8; + osRID = ""; + osLAB = ""; + osTYP = ""; + } + if (strncmp(pszLine, "RIDSA", 5) == 0) + osRID = pszLine + 8; + else if (strncmp(pszLine, "LABSA", 5) == 0) + osLAB = pszLine + 8; + else if (strncmp(pszLine, "TYPSA", 5) == 0) + osTYP = pszLine + 8; + } + + VSIFCloseL(fp); + + + return TRUE; +} + +/************************************************************************/ +/* ReadSCD() */ +/************************************************************************/ + +int OGREDIGEODataSource::ReadSCD() +{ + VSILFILE* fp = OpenFile(osSCN, "SCD"); + if (fp == NULL) + return FALSE; + + const char* pszLine; + CPLString osRTY, osRID, osNameRID, osKND; + strListType aosAttrRID; + int nWidth = 0; + while(TRUE) + { + pszLine = CPLReadLine2L(fp, 81, NULL); + if (pszLine != NULL) + { + if (strlen(pszLine) < 8 || pszLine[7] != ':') + continue; + } + + if (pszLine == NULL || strncmp(pszLine, "RTYSA", 5) == 0) + { + if (osRTY == "OBJ") + { + if (mapObjects.find(osNameRID) == mapObjects.end()) + { + CPLDebug("EDIGEO", "Cannot find object %s", + osNameRID.c_str()); + } + else + { + OGREDIGEOObjectDescriptor objDesc; + objDesc.osRID = osRID; + objDesc.osNameRID = osNameRID; + objDesc.osKND = osKND; + objDesc.aosAttrRID = aosAttrRID; + /*CPLDebug("EDIGEO", "Object %s = %s, %s, %d attributes", + osRID.c_str(), osNameRID.c_str(), osKND.c_str(), + (int)aosAttrRID.size());*/ + + aoObjList.push_back(objDesc); + } + } + else if (osRTY == "ATT") + { + if (mapAttributes.find(osNameRID) == mapAttributes.end()) + { + CPLDebug("EDIGEO", "Cannot find attribute %s", + osNameRID.c_str()); + } + else + { + OGREDIGEOAttributeDescriptor attDesc; + attDesc.osRID = osRID; + attDesc.osNameRID = osNameRID; + attDesc.nWidth = nWidth; + /*CPLDebug("EDIGEO", "Attribute %s = %s, %d", + osRID.c_str(), osNameRID.c_str(), nWidth);*/ + + mapAttributesSCD[osRID] = attDesc; + } + } + if (pszLine == NULL) + break; + osRTY = pszLine + 8; + osRID = ""; + osNameRID = ""; + osKND = ""; + aosAttrRID.resize(0); + nWidth = 0; + } + if (strncmp(pszLine, "RIDSA", 5) == 0) + osRID = pszLine + 8; + else if (strncmp(pszLine, "DIPCP", 5) == 0) + { + const char* pszDIP = pszLine + 8; + char** papszTokens = CSLTokenizeString2(pszDIP, ";", 0); + if (CSLCount(papszTokens) == 4) + { + osNameRID = papszTokens[3]; + } + CSLDestroy(papszTokens); + } + else if (strncmp(pszLine, "KNDSA", 5) == 0) + osKND = pszLine + 8; + else if (strncmp(pszLine, "AAPCP", 5) == 0) + { + const char* pszAAP = pszLine + 8; + char** papszTokens = CSLTokenizeString2(pszAAP, ";", 0); + if (CSLCount(papszTokens) == 4) + { + const char* pszAttRID = papszTokens[3]; + aosAttrRID.push_back(pszAttRID); + } + CSLDestroy(papszTokens); + } + else if (strncmp(pszLine, "CANSN", 5) == 0) + nWidth = atoi(pszLine + 8); + } + + VSIFCloseL(fp); + + + return TRUE; +} + +/************************************************************************/ +/* ReadQAL() */ +/************************************************************************/ + +int OGREDIGEODataSource::ReadQAL() +{ + VSILFILE* fp = OpenFile(osQAN, "QAL"); + if (fp == NULL) + return FALSE; + + const char* pszLine; + CPLString osRTY, osRID; + int nODA = 0, nUDA = 0; + while(TRUE) + { + pszLine = CPLReadLine2L(fp, 81, NULL); + if (pszLine != NULL) + { + if (strlen(pszLine) < 8 || pszLine[7] != ':') + continue; + } + + if (pszLine == NULL || strncmp(pszLine, "RTYSA", 5) == 0) + { + if (osRTY == "QUP") + { + mapQAL[osRID] = intintType(nODA, nUDA); + } + if (pszLine == NULL) + break; + osRTY = pszLine + 8; + osRID = ""; + nODA = 0; + nUDA = 0; + } + else if (strncmp(pszLine, "RIDSA", 5) == 0) + osRID = pszLine + 8; + else if (strncmp(pszLine, "ODASD", 5) == 0) + nODA = atoi(pszLine + 8); + else if (strncmp(pszLine, "UDASD", 5) == 0) + nUDA = atoi(pszLine + 8); + } + + VSIFCloseL(fp); + + return TRUE; +} + +/************************************************************************/ +/* CreateLayerFromObjectDesc() */ +/************************************************************************/ + +int OGREDIGEODataSource::CreateLayerFromObjectDesc(const OGREDIGEOObjectDescriptor& objDesc) +{ + OGRwkbGeometryType eType = wkbUnknown; + if (objDesc.osKND == "ARE") + eType = wkbPolygon; + else if (objDesc.osKND == "LIN") + eType = wkbLineString; + else if (objDesc.osKND == "PCT") + eType = wkbPoint; + else + { + CPLDebug("EDIGEO", "Unknown KND : %s", objDesc.osKND.c_str()); + return FALSE; + } + + const char* pszLayerName = objDesc.osRID.c_str(); + //mapObjects.find(objDesc.osNameRID)->second.c_str(); + OGREDIGEOLayer* poLayer = new OGREDIGEOLayer(this, pszLayerName, + eType, poSRS); + + poLayer->AddFieldDefn("OBJECT_RID", OFTString, ""); + + for(int j=0;j<(int)objDesc.aosAttrRID.size();j++) + { + std::map::iterator it = + mapAttributesSCD.find(objDesc.aosAttrRID[j]); + if (it != mapAttributesSCD.end()) + { + const OGREDIGEOAttributeDescriptor& attrDesc = it->second; + const OGREDIGEOAttributeDef& attrDef = + mapAttributes[attrDesc.osNameRID]; + OGRFieldType eType = OFTString; + if (attrDef.osTYP == "R" || attrDef.osTYP == "E") + eType = OFTReal; + else if (attrDef.osTYP == "I" || attrDef.osTYP == "N") + eType = OFTInteger; + + poLayer->AddFieldDefn(attrDef.osLAB, eType, objDesc.aosAttrRID[j]); + } + } + + + if (strcmp(poLayer->GetName(), "ID_S_OBJ_Z_1_2_2") == 0) + { + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + + iATR = poFDefn->GetFieldIndex("ATR"); + iDI3 = poFDefn->GetFieldIndex("DI3"); + iDI4 = poFDefn->GetFieldIndex("DI4"); + iHEI = poFDefn->GetFieldIndex("HEI"); + iFON = poFDefn->GetFieldIndex("FON"); + + poLayer->AddFieldDefn("OGR_OBJ_LNK", OFTString, ""); + iOBJ_LNK = poFDefn->GetFieldIndex("OGR_OBJ_LNK"); + + poLayer->AddFieldDefn("OGR_OBJ_LNK_LAYER", OFTString, ""); + iOBJ_LNK_LAYER = poFDefn->GetFieldIndex("OGR_OBJ_LNK_LAYER"); + + poLayer->AddFieldDefn("OGR_ATR_VAL", OFTString, ""); + iATR_VAL = poFDefn->GetFieldIndex("OGR_ATR_VAL"); + + poLayer->AddFieldDefn("OGR_ANGLE", OFTReal, ""); + iANGLE = poFDefn->GetFieldIndex("OGR_ANGLE"); + + poLayer->AddFieldDefn("OGR_FONT_SIZE", OFTReal, ""); + iSIZE = poFDefn->GetFieldIndex("OGR_FONT_SIZE"); + } + else if (mapQAL.size() != 0) + { + poLayer->AddFieldDefn("CREAT_DATE", OFTInteger, ""); + poLayer->AddFieldDefn("UPDATE_DATE", OFTInteger, ""); + } + + mapLayer[objDesc.osRID] = poLayer; + + papoLayers = (OGRLayer**) + CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers] = poLayer; + nLayers ++; + + return TRUE; +} + +/************************************************************************/ +/* ReadVEC() */ +/************************************************************************/ + +int OGREDIGEODataSource::ReadVEC(const char* pszVECName) +{ + VSILFILE* fp = OpenFile(pszVECName, "VEC"); + if (fp == NULL) + return FALSE; + + const char* pszLine; + CPLString osRTY, osRID; + xyPairListType aXY; + CPLString osLnkStartType, osLnkStartName, osLnkEndType, osLnkEndName; + strListType osLnkEndNameList; + CPLString osAttId; + std::vector< strstrType > aosAttIdVal; + CPLString osSCP; + CPLString osQUP_RID; + int bIso8859_1 = FALSE; + + while(TRUE) + { + pszLine = CPLReadLine2L(fp, 81, NULL); +skip_read_next_line: + if (pszLine != NULL) + { + if (strlen(pszLine) < 8 || pszLine[7] != ':') + continue; + } + + if (pszLine == NULL || strncmp(pszLine, "RTYSA", 5) == 0) + { + if (osRTY == "PAR") + { + if (aXY.size() < 2) + CPLDebug("EDIGEO", "Error: ARC %s has not enough points", + osRID.c_str()); + else + mapPAR[osRID] = aXY; + } + else if (osRTY == "LNK") + { + if (osLnkStartType == "PAR" && osLnkEndType == "PFE") + { + /*CPLDebug("EDIGEO", "PFE[%s] -> PAR[%s]", + osLnkEndName.c_str(), osLnkStartName.c_str());*/ + if (mapPFE_PAR.find(osLnkEndName) == mapPFE_PAR.end()) + mapPFE_PAR[osLnkEndName].push_back(osLnkStartName); + else + { + int bAlreadyExists = FALSE; + strListType& osPARList = mapPFE_PAR[osLnkEndName]; + for(int j=0;j<(int)osPARList.size();j++) + { + if (osPARList[j] == osLnkStartName) + bAlreadyExists = TRUE; + } + if (!bAlreadyExists) + osPARList.push_back(osLnkStartName); + } + } + else if (osLnkStartType == "FEA" && osLnkEndType == "PFE") + { + /*CPLDebug("EDIGEO", "FEA[%s] -> PFE[%s]", + osLnkStartName.c_str(), osLnkEndName.c_str());*/ + listFEA_PFE.push_back(strstrType + (osLnkStartName, osLnkEndName)); + } + else if (osLnkStartType == "FEA" && osLnkEndType == "PAR") + { + /*CPLDebug("EDIGEO", "FEA[%s] -> PAR[%s]", + osLnkStartName.c_str(), osLnkEndName.c_str());*/ + listFEA_PAR.push_back(std::pair + (osLnkStartName, osLnkEndNameList)); + } + else if (osLnkStartType == "FEA" && osLnkEndType == "PNO") + { + /*CPLDebug("EDIGEO", "FEA[%s] -> PNO[%s]", + osLnkStartName.c_str(), osLnkEndName.c_str());*/ + listFEA_PNO.push_back(strstrType + (osLnkStartName, osLnkEndName)); + } + else if (osLnkStartType == "FEA" && osLnkEndType == "FEA") + { + /*CPLDebug("EDIGEO", "FEA[%s] -> FEA[%s]", + osLnkStartName.c_str(), osLnkEndName.c_str());*/ + if (osSCP == "IS_S_REL_IWW") + mapFEA_FEA[osLnkStartName] = osLnkEndName; + } + else if (osLnkStartType == "PAR" && osLnkEndType == "PNO") + { + } + else + { + CPLDebug("EDIGEO", "Unhandled LNK(%s) %s=%s --> %s=%s", + osRID.c_str(), + osLnkStartType.c_str(), osLnkStartName.c_str(), + osLnkEndType.c_str(), osLnkEndName.c_str()); + } + } + else if (osRTY == "FEA") + { + OGREDIGEOFEADesc feaDesc; + feaDesc.aosAttIdVal = aosAttIdVal; + feaDesc.osSCP = osSCP; + feaDesc.osQUP_RID = osQUP_RID; + mapFEA[osRID] = feaDesc; + } + else if (osRTY == "PNO") + { + if (aXY.size() == 1) + { + /*CPLDebug("EDIGEO", "PNO[%s] = %f, %f", + osRID.c_str(), aXY[0].first, aXY[0].second);*/ + mapPNO[osRID] = aXY[0]; + } + } + if (pszLine == NULL) + break; + osRTY = pszLine + 8; + osRID = ""; + aXY.resize(0); + osLnkStartType = ""; + osLnkStartName = ""; + osLnkEndType = ""; + osLnkEndName = ""; + osAttId = ""; + aosAttIdVal.resize(0); + osLnkEndNameList.resize(0); + osSCP = ""; + osQUP_RID = ""; + bIso8859_1 = FALSE; + } + else if (strncmp(pszLine, "RIDSA", 5) == 0) + osRID = pszLine + 8; + else if (strncmp(pszLine, "CORCC", 5) == 0) + { + const char* pszY = strchr(pszLine+8, ';'); + if (pszY) + { + double dfX = CPLAtof(pszLine + 8); + double dfY = CPLAtof(pszY + 1); + aXY.push_back(xyPairType (dfX, dfY)); + } + } + else if (strncmp(pszLine, "FTPCP", 5) == 0) + { + char** papszTokens = CSLTokenizeString2(pszLine + 8, ";", 0); + if (CSLCount(papszTokens) == 4) + { + if (osLnkStartType.size() == 0) + { + osLnkStartType = papszTokens[2]; + osLnkStartName = papszTokens[3]; + } + else + { + osLnkEndType = papszTokens[2]; + osLnkEndName = papszTokens[3]; + osLnkEndNameList.push_back(osLnkEndName); + } + } + CSLDestroy(papszTokens); + } + else if (strncmp(pszLine, "SCPCP", 5) == 0) + { + char** papszTokens = CSLTokenizeString2(pszLine + 8, ";", 0); + if (CSLCount(papszTokens) == 4) + { + if (osRTY == "LNK") + { + if (strcmp(papszTokens[2], "ASS") == 0) + osSCP = papszTokens[3]; + } + else if (strcmp(papszTokens[2], "OBJ") == 0) + osSCP = papszTokens[3]; + } + CSLDestroy(papszTokens); + } + else if (strncmp(pszLine, "ATPCP", 5) == 0) + { + char** papszTokens = CSLTokenizeString2(pszLine + 8, ";", 0); + if (CSLCount(papszTokens) == 4) + { + if (strcmp(papszTokens[2], "ATT") == 0) + osAttId = papszTokens[3]; + } + CSLDestroy(papszTokens); + } + else if (strcmp(pszLine, "TEXT 06:8859-1") == 0) + { + bIso8859_1 = TRUE; + } + else if (strncmp(pszLine, "ATVS", 4) == 0) + { + CPLString osAttVal = pszLine + 8; + int bSkipReadNextLine = FALSE; + while(TRUE) + { + pszLine = CPLReadLine2L(fp, 81, NULL); + if (pszLine != NULL && + strlen(pszLine) >= 8 && + pszLine[7] == ':' && + strncmp(pszLine, "NEXT ", 5) == 0) + { + osAttVal += pszLine + 8; + } + else + { + bSkipReadNextLine = TRUE; + break; + } + } + if (bIso8859_1 && bRecodeToUTF8) + { + char* pszNewVal = CPLRecode(osAttVal.c_str(), + CPL_ENC_ISO8859_1, CPL_ENC_UTF8); + osAttVal = pszNewVal; + CPLFree(pszNewVal); + } + else if (bHasUTF8ContentOnly) + { + bHasUTF8ContentOnly = CPLIsUTF8(osAttVal.c_str(), -1); + } + if (osAttId.size() != 0) + aosAttIdVal.push_back( strstrType (osAttId, osAttVal) ); + osAttId = ""; + bIso8859_1 = FALSE; + if (bSkipReadNextLine) + goto skip_read_next_line; + } + else if (strncmp(pszLine, "ATVCP", 5) == 0) + { + char** papszTokens = CSLTokenizeString2(pszLine + 8, ";", 0); + if (CSLCount(papszTokens) == 4) + { + if (strcmp(papszTokens[2], "ATT") == 0) + { + CPLString osAttVal = papszTokens[3]; + if (osAttId.size() != 0) + aosAttIdVal.push_back( strstrType (osAttId, osAttVal) ); + osAttId = ""; + } + } + CSLDestroy(papszTokens); + } + else if (strncmp(pszLine, "QAPCP", 5) == 0) + { + char** papszTokens = CSLTokenizeString2(pszLine + 8, ";", 0); + if (CSLCount(papszTokens) == 4) + { + if (strcmp(papszTokens[2], "QUP") == 0) + { + osQUP_RID = papszTokens[3]; + } + } + CSLDestroy(papszTokens); + } + } + + VSIFCloseL(fp); + + return TRUE; +} + +/************************************************************************/ +/* CreateFeature() */ +/************************************************************************/ + +OGRFeature* OGREDIGEODataSource::CreateFeature(const CPLString& osFEA) +{ + const std::map< CPLString, OGREDIGEOFEADesc >::iterator itFEA = + mapFEA.find(osFEA); + if (itFEA == mapFEA.end()) + { + CPLDebug("EDIGEO", "ERROR: Cannot find FEA %s", osFEA.c_str()); + return NULL; + } + + const OGREDIGEOFEADesc& fea = itFEA->second; + const std::map::iterator itLyr = + mapLayer.find(fea.osSCP); + if (itLyr != mapLayer.end()) + { + OGREDIGEOLayer* poLayer = itLyr->second; + + OGRFeature* poFeature = new OGRFeature(poLayer->GetLayerDefn()); + poFeature->SetField(0, itFEA->first.c_str()); + for(int i=0;i<(int)fea.aosAttIdVal.size();i++) + { + const CPLString& id = fea.aosAttIdVal[i].first; + const CPLString& val = fea.aosAttIdVal[i].second; + int iIndex = poLayer->GetAttributeIndex(id); + if (iIndex != -1) + poFeature->SetField(iIndex, val.c_str()); + else + CPLDebug("EDIGEO", + "ERROR: Cannot find attribute %s", id.c_str()); + } + + if (strcmp(poLayer->GetName(), "ID_S_OBJ_Z_1_2_2") != 0 && + mapQAL.size() != 0 && fea.osQUP_RID.size() != 0) + { + const std::map::iterator itQAL = + mapQAL.find(fea.osQUP_RID); + if (itQAL != mapQAL.end()) + { + const intintType& creationUpdateDate = itQAL->second; + if (creationUpdateDate.first != 0) + poFeature->SetField("CREAT_DATE", creationUpdateDate.first); + if (creationUpdateDate.second != 0) + poFeature->SetField("UPDATE_DATE", creationUpdateDate.second); + } + } + + poLayer->AddFeature(poFeature); + + return poFeature; + } + else + { + CPLDebug("EDIGEO", "ERROR: Cannot find layer %s", fea.osSCP.c_str()); + return NULL; + } +} + +/************************************************************************/ +/* SetStyle() */ +/************************************************************************/ + +int OGREDIGEODataSource::SetStyle(const CPLString& osFEA, + OGRFeature* poFeature) +{ + /* EDIGEO PCI specific */ + /* See EDIGeO_PCI.pdf, chapter 3 "Principes généraux de */ + /* positionnement de la toponymie. */ + const char* pszATR = NULL; + if (strcmp(poFeature->GetDefnRef()->GetName(), "ID_S_OBJ_Z_1_2_2") == 0 && + iATR != -1 && (pszATR = poFeature->GetFieldAsString(iATR)) != NULL) + { + const CPLString osATR = pszATR; + std::map< CPLString, CPLString>::iterator itFEA_FEA = + mapFEA_FEA.find(osFEA); + if (itFEA_FEA != mapFEA_FEA.end()) + { + const CPLString& osOBJ_LNK = itFEA_FEA->second; + std::map< CPLString, OGREDIGEOFEADesc >::iterator itFEA_LNK = + mapFEA.find(osOBJ_LNK); + if (itFEA_LNK != mapFEA.end()) + { + const OGREDIGEOFEADesc& fea_lnk = itFEA_LNK->second; + for(int j=0;j<(int)fea_lnk.aosAttIdVal.size();j++) + { + if (fea_lnk.aosAttIdVal[j].first == osATR) + { + double dfAngle = 0; + if (iDI3 != -1 && iDI4 != -1) + { + double dfBaseVectorX = + poFeature->GetFieldAsDouble(iDI3); + double dfBaseVectorY = + poFeature->GetFieldAsDouble(iDI4); + dfAngle = atan2(dfBaseVectorY, dfBaseVectorX) + / M_PI * 180; + if (dfAngle < 0) + dfAngle += 360; + } + double dfSize = 1; + if (iHEI != -1) + dfSize = poFeature->GetFieldAsDouble(iHEI); + if (dfSize <= 0 || dfSize >= 100) + dfSize = 1; + const char* pszFontFamily = NULL; + if (iFON != -1) + pszFontFamily = poFeature->GetFieldAsString(iFON); + + CPLString osStyle("LABEL(t:\""); + osStyle += fea_lnk.aosAttIdVal[j].second; + osStyle += "\""; + if (dfAngle != 0) + { + osStyle += ",a:"; + osStyle += CPLString().Printf("%.1f", dfAngle); + } + if (pszFontFamily != NULL && bIncludeFontFamily) + { + osStyle += ",f:\""; + osStyle += pszFontFamily; + osStyle += "\""; + } + osStyle += ",s:"; + osStyle += CPLString().Printf("%.1f", dfSize); + osStyle += ",c:#000000)"; + poFeature->SetStyleString(osStyle); + + poFeature->SetField(iATR_VAL, + fea_lnk.aosAttIdVal[j].second); + poFeature->SetField(iANGLE, dfAngle); + poFeature->SetField(iSIZE, dfSize * dfSizeFactor); + poFeature->SetField(iOBJ_LNK, osOBJ_LNK); + poFeature->SetField(iOBJ_LNK_LAYER, fea_lnk.osSCP); + + setLayersWithLabels.insert(fea_lnk.osSCP); + + break; + } + } + } + } + } + + return TRUE; +} + +/************************************************************************/ +/* BuildPoints() */ +/************************************************************************/ + +int OGREDIGEODataSource::BuildPoints() +{ + for(int i=0;i<(int)listFEA_PNO.size();i++) + { + const CPLString& osFEA = listFEA_PNO[i].first; + const CPLString& osPNO = listFEA_PNO[i].second; + const std::map< CPLString, xyPairType >::iterator itPNO = + mapPNO.find(osPNO); + if (itPNO == mapPNO.end()) + { + CPLDebug("EDIGEO", "Cannot find PNO %s", osPNO.c_str()); + } + else + { + OGRFeature* poFeature = CreateFeature(osFEA); + if (poFeature) + { + const xyPairType& pno = itPNO->second; + OGRPoint* poPoint = new OGRPoint(pno.first, pno.second); + if (poSRS) + poPoint->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poPoint); + + SetStyle(osFEA, poFeature); + } + } + } + + return TRUE; +} + +/************************************************************************/ +/* BuildLineStrings() */ +/************************************************************************/ + +int OGREDIGEODataSource::BuildLineStrings() +{ + int i, iter; + + for(iter=0;iter<(int)listFEA_PAR.size();iter++) + { + const CPLString& osFEA = listFEA_PAR[iter].first; + const strListType & aosPAR = listFEA_PAR[iter].second; + OGRFeature* poFeature = CreateFeature(osFEA); + if (poFeature) + { + OGRMultiLineString* poMulti = NULL; + for(int k=0;k<(int)aosPAR.size();k++) + { + const std::map< CPLString, xyPairListType >::iterator itPAR = + mapPAR.find(aosPAR[k]); + if (itPAR != mapPAR.end()) + { + const xyPairListType& arc = itPAR->second; + + OGRLineString* poLS = new OGRLineString(); + poLS->setNumPoints((int)arc.size()); + for(i=0;i<(int)arc.size();i++) + { + poLS->setPoint(i, arc[i].first, arc[i].second); + } + + if (poFeature->GetGeometryRef() != NULL) + { + if (poMulti == NULL) + { + OGRLineString* poPrevLS = + (OGRLineString*) poFeature->StealGeometry(); + poMulti = new OGRMultiLineString(); + poMulti->addGeometryDirectly(poPrevLS); + poFeature->SetGeometryDirectly(poMulti); + } + poMulti->addGeometryDirectly(poLS); + } + else + poFeature->SetGeometryDirectly(poLS); + } + else + CPLDebug("EDIGEO", + "ERROR: Cannot find ARC %s", aosPAR[k].c_str()); + } + if (poFeature->GetGeometryRef()) + poFeature->GetGeometryRef()->assignSpatialReference(poSRS); + } + } + + return TRUE; +} + +/************************************************************************/ +/* BuildPolygon() */ +/************************************************************************/ + +int OGREDIGEODataSource::BuildPolygon(const CPLString& osFEA, + const CPLString& osPFE) +{ + int i; + + const std::map< CPLString, strListType >::iterator itPFE_PAR = + mapPFE_PAR.find(osPFE); + if (itPFE_PAR == mapPFE_PAR.end()) + { + CPLDebug("EDIGEO", "ERROR: Cannot find PFE %s", osPFE.c_str()); + return FALSE; + } + + const strListType & aosPARList = itPFE_PAR->second; + +/* -------------------------------------------------------------------- */ +/* Resolve arc ids to arc coordinate lists. */ +/* -------------------------------------------------------------------- */ + std::vector< const xyPairListType *> aoPARPtrList; + for(i=0;i<(int)aosPARList.size();i++) + { + const std::map< CPLString, xyPairListType >::iterator itPAR = + mapPAR.find(aosPARList[i]); + if (itPAR != mapPAR.end()) + aoPARPtrList.push_back(&(itPAR->second)); + else + CPLDebug("EDIGEO", + "ERROR: Cannot find ARC %s", aosPARList[i].c_str()); + } + + if (aoPARPtrList.size() == 0) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Now try to chain all arcs together. */ +/* -------------------------------------------------------------------- */ + std::vector aoXYList; + + int j; + for(j=0;j<(int)aoPARPtrList.size();j++) + { + if (aoPARPtrList[j] == NULL) + continue; + const xyPairListType& sFirstRing = *(aoPARPtrList[j]); + const xyPairType* psNext = &(sFirstRing[sFirstRing.size()-1]); + + xyPairListType aoXY; + for(i=0;i<(int)sFirstRing.size();i++) + aoXY.push_back(sFirstRing[i]); + aoPARPtrList[j] = NULL; + + int nIter = 1; + while(aoXY[aoXY.size()-1] != aoXY[0] && nIter < (int)aoPARPtrList.size()) + { + int bFound = FALSE; + int bReverseSecond = FALSE; + for(i=0;i<(int)aoPARPtrList.size();i++) + { + if (aoPARPtrList[i] != NULL) + { + const xyPairListType& sSecondRing = *(aoPARPtrList[i]); + if (*psNext == sSecondRing[0]) + { + bFound = TRUE; + bReverseSecond = FALSE; + break; + } + else if (*psNext == sSecondRing[sSecondRing.size()-1]) + { + bFound = TRUE; + bReverseSecond = TRUE; + break; + } + } + } + + if (!bFound) + { + CPLDebug("EDIGEO", "Cannot find ring for FEA %s / PFE %s", + osFEA.c_str(), osPFE.c_str()); + break; + } + else + { + const xyPairListType& secondRing = *(aoPARPtrList[i]); + aoPARPtrList[i] = NULL; + if (!bReverseSecond) + { + for(i=1;i<(int)secondRing.size();i++) + aoXY.push_back(secondRing[i]); + psNext = &secondRing[secondRing.size()-1]; + } + else + { + for(i=1;i<(int)secondRing.size();i++) + aoXY.push_back(secondRing[secondRing.size()-1-i]); + psNext = &secondRing[0]; + } + } + + nIter ++; + } + + aoXYList.push_back(aoXY); + } + +/* -------------------------------------------------------------------- */ +/* Create feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature* poFeature = CreateFeature(osFEA); + if (poFeature) + { + std::vector aosPolygons; + for(j=0;j<(int)aoXYList.size();j++) + { + const xyPairListType& aoXY = aoXYList[j]; + OGRLinearRing* poLS = new OGRLinearRing(); + poLS->setNumPoints((int)aoXY.size()); + for(i=0;i<(int)aoXY.size();i++) + poLS->setPoint(i, aoXY[i].first, aoXY[i].second); + poLS->closeRings(); + OGRPolygon* poPolygon = new OGRPolygon(); + poPolygon->addRingDirectly(poLS); + aosPolygons.push_back(poPolygon); + } + + int bIsValidGeometry; + OGRGeometry* poGeom = OGRGeometryFactory::organizePolygons( + &aosPolygons[0], (int)aosPolygons.size(), + &bIsValidGeometry, NULL); + if (poGeom) + { + if (poSRS) + poGeom->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poGeom); + } + } + + return TRUE; +} + +/************************************************************************/ +/* BuildPolygons() */ +/************************************************************************/ + +int OGREDIGEODataSource::BuildPolygons() +{ + int iter; + for(iter=0;iter<(int)listFEA_PFE.size();iter++) + { + const CPLString& osFEA = listFEA_PFE[iter].first; + const CPLString& osPFE = listFEA_PFE[iter].second; + BuildPolygon(osFEA, osPFE); + } + + return TRUE; +} + +/************************************************************************/ +/* OGREDIGEOSortForQGIS() */ +/************************************************************************/ + +static int OGREDIGEOSortForQGIS(const void* a, const void* b) +{ + OGREDIGEOLayer* poLayerA = *((OGREDIGEOLayer**) a); + OGREDIGEOLayer* poLayerB = *((OGREDIGEOLayer**) b); + int nTypeA, nTypeB; + switch (poLayerA->GetLayerDefn()->GetGeomType()) + { + case wkbPoint: nTypeA = 1; break; + case wkbLineString: nTypeA = 2; break; + case wkbPolygon: nTypeA = 3; break; + default: nTypeA = 4; break; + } + switch (poLayerB->GetLayerDefn()->GetGeomType()) + { + case wkbPoint: nTypeB = 1; break; + case wkbLineString: nTypeB = 2; break; + case wkbPolygon: nTypeB = 3; break; + default: nTypeB = 4; break; + } + if (nTypeA == nTypeB) + { + int nCmp = strcmp(poLayerA->GetName(), poLayerB->GetName()); + if (nCmp == 0) + return 0; + + static const char* apszPolyOrder[] = + { "COMMUNE_id", "LIEUDIT_id", "SECTION_id", "SUBDSECT_id", + "SUBDFISC_id", "PARCELLE_id", "BATIMENT_id" }; + for(int i=0;i<(int)(sizeof(apszPolyOrder)/sizeof(char*));i++) + { + if (strcmp(poLayerA->GetName(), apszPolyOrder[i]) == 0) + return -1; + if (strcmp(poLayerB->GetName(), apszPolyOrder[i]) == 0) + return 1; + } + return nCmp; + } + else + return nTypeB - nTypeA; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGREDIGEODataSource::Open( const char * pszFilename ) + +{ + pszName = CPLStrdup( pszFilename ); + + fpTHF = VSIFOpenL(pszFilename, "rb"); + if (fpTHF == NULL) + return FALSE; + + const char* pszLine; + int i = 0; + int bIsEDIGEO = FALSE; + while(i < 100 && (pszLine = CPLReadLine2L(fpTHF, 81, NULL)) != NULL) + { + if (strcmp(pszLine, "RTYSA03:GTS") == 0) + { + bIsEDIGEO = TRUE; + break; + } + i++; + } + + if (!bIsEDIGEO) + { + VSIFCloseL(fpTHF); + fpTHF = NULL; + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* ReadEDIGEO() */ +/************************************************************************/ + +void OGREDIGEODataSource::ReadEDIGEO() +{ + if (bHasReadEDIGEO) + return; + + bHasReadEDIGEO = TRUE; + +/* -------------------------------------------------------------------- */ +/* Read .THF file */ +/* -------------------------------------------------------------------- */ + VSIFSeekL(fpTHF, 0, SEEK_SET); + if (!ReadTHF(fpTHF)) + { + VSIFCloseL(fpTHF); + fpTHF = NULL; + return; + } + VSIFCloseL(fpTHF); + fpTHF = NULL; + +/* -------------------------------------------------------------------- */ +/* Read .GEO file */ +/* -------------------------------------------------------------------- */ + if (!ReadGEO()) + return; + +/* -------------------------------------------------------------------- */ +/* Read .GEN file */ +/* -------------------------------------------------------------------- */ + if (osGNN.size() != 0) + ReadGEN(); + +/* -------------------------------------------------------------------- */ +/* Read .DIC file */ +/* -------------------------------------------------------------------- */ + if (!ReadDIC()) + return; + +/* -------------------------------------------------------------------- */ +/* Read .SCD file */ +/* -------------------------------------------------------------------- */ + if (!ReadSCD()) + return; + +/* -------------------------------------------------------------------- */ +/* Read .QAL file */ +/* -------------------------------------------------------------------- */ + if (osQAN.size() != 0) + ReadQAL(); + +/* -------------------------------------------------------------------- */ +/* Create layers from SCD definitions */ +/* -------------------------------------------------------------------- */ + int i; + for(i=0;i<(int)aoObjList.size();i++) + { + CreateLayerFromObjectDesc(aoObjList[i]); + } + +/* -------------------------------------------------------------------- */ +/* Read .VEC files and create features */ +/* -------------------------------------------------------------------- */ + for(i=0;i<(int)aosGDN.size();i++) + { + ReadVEC(aosGDN[i]); + + BuildPoints(); + BuildLineStrings(); + BuildPolygons(); + + mapPNO.clear(); + mapPAR.clear(); + mapFEA.clear(); + mapPFE_PAR.clear(); + listFEA_PFE.clear(); + listFEA_PAR.clear(); + listFEA_PNO.clear(); + mapFEA_FEA.clear(); + } + + mapObjects.clear(); + mapAttributes.clear(); + mapAttributesSCD.clear(); + mapQAL.clear(); + +/* -------------------------------------------------------------------- */ +/* Delete empty layers */ +/* -------------------------------------------------------------------- */ + for(i=0;iGetFeatureCount(TRUE) == 0) + { + delete papoLayers[i]; + if (i < nLayers - 1) + memmove(papoLayers + i, papoLayers + i + 1, + (nLayers - i - 1) * sizeof(OGREDIGEOLayer*)); + nLayers --; + } + else + i++; + } + +/* -------------------------------------------------------------------- */ +/* When added from QGIS, the layers must be ordered from */ +/* bottom (Polygon) to top (Point) to get nice visual effect */ +/* -------------------------------------------------------------------- */ + if (CSLTestBoolean(CPLGetConfigOption("OGR_EDIGEO_SORT_FOR_QGIS", "YES"))) + qsort(papoLayers, nLayers, sizeof(OGREDIGEOLayer*), OGREDIGEOSortForQGIS); + +/* -------------------------------------------------------------------- */ +/* Create a label layer for each feature layer */ +/* -------------------------------------------------------------------- */ + if (CSLTestBoolean(CPLGetConfigOption("OGR_EDIGEO_CREATE_LABEL_LAYERS", "YES"))) + CreateLabelLayers(); + + return; +} + +/************************************************************************/ +/* CreateLabelLayers() */ +/************************************************************************/ + +void OGREDIGEODataSource::CreateLabelLayers() +{ + OGRLayer* poLayer = GetLayerByName("ID_S_OBJ_Z_1_2_2"); + if (poLayer == NULL) + return; + + std::map mapLayerNameToLayer; + + OGRFeature* poFeature; + OGRFeatureDefn* poFeatureDefn = poLayer->GetLayerDefn(); + while((poFeature = poLayer->GetNextFeature()) != NULL) + { + const char* pszBelongingLayerName = + poFeature->GetFieldAsString(iOBJ_LNK_LAYER); + if (pszBelongingLayerName) + { + CPLString osBelongingLayerName = pszBelongingLayerName; + std::map::iterator it = + mapLayerNameToLayer.find(osBelongingLayerName); + OGREDIGEOLayer* poLabelLayer; + + if (it == mapLayerNameToLayer.end()) + { + /* Create label layer if it does not already exist */ + CPLString osLayerLabelName = osBelongingLayerName + "_LABEL"; + poLabelLayer = new OGREDIGEOLayer(this, osLayerLabelName.c_str(), + wkbPoint, poSRS); + int i; + OGRFeatureDefn* poLabelFeatureDefn = poLabelLayer->GetLayerDefn(); + for(i=0;iGetFieldCount();i++) + poLabelFeatureDefn->AddFieldDefn(poFeatureDefn->GetFieldDefn(i)); + mapLayerNameToLayer[osBelongingLayerName] = poLabelLayer; + + papoLayers = (OGRLayer**) + CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers] = poLabelLayer; + nLayers ++; + } + else + poLabelLayer = mapLayerNameToLayer[osBelongingLayerName]; + + OGRFeature* poNewFeature = new OGRFeature(poLabelLayer->GetLayerDefn()); + poNewFeature->SetFrom(poFeature); + poLabelLayer->AddFeature(poNewFeature); + } + delete poFeature; + } + + poLayer->ResetReading(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/ogredigeodriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/ogredigeodriver.cpp new file mode 100644 index 000000000..91caf420b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/ogredigeodriver.cpp @@ -0,0 +1,101 @@ +/****************************************************************************** + * $Id: ogredigeodriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: EDIGEO Translator + * Purpose: Implements OGREDIGEODriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_edigeo.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogredigeodriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +extern "C" void RegisterOGREDIGEO(); + +// g++ -fPIC -g -Wall ogr/ogrsf_frmts/edigeo/*.cpp -shared -o ogr_EDIGEO.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/generic -Iogr/ogrsf_frmts/edigeo -L. -lgdal + +/************************************************************************/ +/* OGREDIGEODriverIdentify() */ +/************************************************************************/ + +static int OGREDIGEODriverIdentify( GDALOpenInfo * poOpenInfo ) + +{ + return poOpenInfo->fpL != NULL && + EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "thf"); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGREDIGEODriverOpen( GDALOpenInfo * poOpenInfo ) + +{ + if( poOpenInfo->eAccess == GA_Update || + !OGREDIGEODriverIdentify(poOpenInfo) ) + return NULL; + + OGREDIGEODataSource *poDS = new OGREDIGEODataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGREDIGEO() */ +/************************************************************************/ + +void RegisterOGREDIGEO() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "EDIGEO" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "EDIGEO" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "French EDIGEO exchange format" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "thf" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_edigeo.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGREDIGEODriverOpen; + poDriver->pfnIdentify = OGREDIGEODriverIdentify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/ogredigeolayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/ogredigeolayer.cpp new file mode 100644 index 000000000..5d4eec7f1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/edigeo/ogredigeolayer.cpp @@ -0,0 +1,230 @@ +/****************************************************************************** + * $Id: ogredigeolayer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: EDIGEO Translator + * Purpose: Implements OGREDIGEOLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_edigeo.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: ogredigeolayer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGREDIGEOLayer() */ +/************************************************************************/ + +OGREDIGEOLayer::OGREDIGEOLayer( OGREDIGEODataSource* poDS, + const char* pszName, OGRwkbGeometryType eType, + OGRSpatialReference* poSRS ) + +{ + this->poDS = poDS; + nNextFID = 0; + + this->poSRS = poSRS; + if (poSRS) + poSRS->Reference(); + + poFeatureDefn = new OGRFeatureDefn( pszName ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( eType ); + if( poFeatureDefn->GetGeomFieldCount() != 0 ) + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + SetDescription( poFeatureDefn->GetName() ); +} + +/************************************************************************/ +/* ~OGREDIGEOLayer() */ +/************************************************************************/ + +OGREDIGEOLayer::~OGREDIGEOLayer() + +{ + for(int i=0;i<(int)aosFeatures.size();i++) + delete aosFeatures[i]; + + poFeatureDefn->Release(); + + if (poSRS) + poSRS->Release(); +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGREDIGEOLayer::ResetReading() + +{ + nNextFID = 0; +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGREDIGEOLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGREDIGEOLayer::GetNextRawFeature() +{ + if (nNextFID < (int)aosFeatures.size()) + { + OGRFeature* poFeature = aosFeatures[nNextFID]->Clone(); + nNextFID++; + return poFeature; + } + else + return NULL; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature * OGREDIGEOLayer::GetFeature(GIntBig nFID) +{ + if (nFID >= 0 && nFID < (int)aosFeatures.size()) + return aosFeatures[(int)nFID]->Clone(); + else + return NULL; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGREDIGEOLayer::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap, OLCFastFeatureCount)) + return m_poFilterGeom == NULL && m_poAttrQuery == NULL; + + else if (EQUAL(pszCap, OLCRandomRead)) + return TRUE; + + else if (EQUAL(pszCap, OLCStringsAsUTF8)) + return poDS->HasUTF8ContentOnly(); + + return FALSE; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGREDIGEOLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + /*if (poDS->bExtentValid) + { + psExtent->MinX = poDS->dfMinX; + psExtent->MinY = poDS->dfMinY; + psExtent->MaxX = poDS->dfMaxX; + psExtent->MaxY = poDS->dfMaxY; + return OGRERR_NONE; + }*/ + + return OGRLayer::GetExtent(psExtent, bForce); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGREDIGEOLayer::GetFeatureCount( int bForce ) +{ + if (m_poFilterGeom != NULL || m_poAttrQuery != NULL) + return OGRLayer::GetFeatureCount(bForce); + + return (int)aosFeatures.size(); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +void OGREDIGEOLayer::AddFeature(OGRFeature* poFeature) +{ + poFeature->SetFID(aosFeatures.size()); + aosFeatures.push_back(poFeature); +} + +/************************************************************************/ +/* GetAttributeIndex() */ +/************************************************************************/ + +int OGREDIGEOLayer::GetAttributeIndex(const CPLString& osRID) +{ + std::map::iterator itAttrIndex = + mapAttributeToIndex.find(osRID); + if (itAttrIndex != mapAttributeToIndex.end()) + return itAttrIndex->second; + else + return -1; +} + +/************************************************************************/ +/* AddFieldDefn() */ +/************************************************************************/ + +void OGREDIGEOLayer::AddFieldDefn(const CPLString& osName, + OGRFieldType eType, + const CPLString& osRID) +{ + if (osRID.size() != 0) + mapAttributeToIndex[osRID] = poFeatureDefn->GetFieldCount(); + + OGRFieldDefn oFieldDefn(osName, eType); + poFeatureDefn->AddFieldDefn(&oFieldDefn); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/GNUmakefile new file mode 100644 index 000000000..fe0d840ac --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrelasticdriver.o ogrelasticdatasource.o ogrelasticlayer.o + +CPPFLAGS := $(JSON_INCLUDE) -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_elastic.h ../geojson/ogrgeojsonreader.h ../geojson/ogrgeojsonwriter.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/drv_elasticsearch.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/drv_elasticsearch.html new file mode 100644 index 000000000..299125cd8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/drv_elasticsearch.html @@ -0,0 +1,97 @@ + + +OGR ElasticSearch Driver + + + + +

    ElasticSearch: Geographically Encoded Objects for ElasticSearch

    + +(Driver available in GDAL 1.10 or later)
    + +Driver is WRITE Only +

    + +ElasticSearch is an Enterprise-level search engine for a variety of data sources. It supports full-text indexing and geospatial querying of those data in a fast and efficient manor using a predefined REST API. This driver serializes all of the supported OGR file formats in to an ElasticSearch index. +
    However, the driver is limited in the geometry it handles: even if polygons are provided as input, they are stored as geo point and the "center" of the polygon is used as value of the point. +

    Field definitions

    + +Fields are dynamically mapped from the input OGR data source. However, the driver will take advantage of advanced options within ElasticSearch as defined in a field mapping file. + +

    +The mapping file allows you to modify the mapping according to the ElasticSearch field-specific types. There are many options to choose from, however, most of the functionality is based on all the different things you are able to do with text fields within ElasticSearch. +

    +ogr2ogr -progress --config ES_WRITEMAP /path/to/file/map.txt -f "ElasticSearch" http://localhost:9200 my_shapefile.shp
    +
    +

    + +The ElasticSearch writer supports the following Configuration Options: +

      +
    • ES_WRITEMAP=/path/to/mapfile.txt Creates a mapping file that can be modified by the user prior to insert in to the index.
      +
    • ES_META=/path/to/mapfile.txt Tells the driver to the user-defined field mappings.
      +
    • ES_BULK=10000 Identifies the number of records to be inserted at a time. Lower record counts help with memory consumption within ElasticSearch but take longer to insert.
      +
    • ES_OVERWRITE=1 Overwrites the current index by deleting an existing one.
      + +
    +

    + +

    +It is possible to apply several options at a time. The following use case takes advantage of a predefined mapping file as well as a limited BULK insert count. +

    +ogr2ogr -progress --config ES_OVERWRITE 1 --config ES_BULK 10000 --config ES_META /path/to/file/map.txt -f "ElasticSearch" http://localhost:9200 PG:"host=localhost user=postgres dbname=my_db password=password" "my_table" -nln thetable
    +
    +
  • + +

    Examples

    + +Basic Transform:
    +
    +ogr2ogr -progress -f "ElasticSearch" http://localhost:9200 my_shapefile.shp
    +
    + +Create a Mapping File:
    +The mapping file allows you to modify the mapping according to the ElasticSearch field-specific types. There are many options to choose from, however, most of the functionality is based on all the different things you are able to do with text fields. +
    +
    +ogr2ogr -progress --config ES_WRITEMAP /path/to/file/map.txt -f "ElasticSearch" http://localhost:9200 my_shapefile.shp
    +
    + +Read the Mapping File:
    +Reads the mapping file during the transformation
    +
    +ogr2ogr -progress --config ES_META /path/to/file/map.txt -f "ElasticSearch" http://localhost:9200 my_shapefile.shp
    +
    + +Bulk Uploading (for larger datasets):
    +Bulk loading helps when uploading a lot of data. The integer value is the number of bytes that are collection before being inserted. +
    +
    +ogr2ogr -progress --config ES_BULK 10000 -f "ElasticSearch" http://localhost:9200 PG:"host=localhost user=postgres dbname=my_db password=password" "my_table" -nln thetable
    +
    + +Overwrite the current Index:
    +If specified, this will overwrite the current index. Otherwise, the data will be appended. +
    +
    +ogr2ogr -progress --config ES_OVERWRITE 1 -f "ElasticSearch" http://localhost:9200 PG:"host=localhost user=postgres dbname=my_db password=password" "my_table" -nln thetable
    +
    + +Specify several at a time:
    +Several flags can be set at the same time.
    +
    +ogr2ogr -progress --config ES_OVERWRITE 1 --config ES_BULK 10000 --config ES_META /path/to/file/map.txt -f "ElasticSearch" http://localhost:9200 PG:"host=localhost user=postgres dbname=my_db password=password" "my_table" -nln thetable
    +
    +

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/makefile.vc new file mode 100644 index 000000000..0b84247d0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrelasticdriver.obj ogrelasticdatasource.obj ogrelasticlayer.obj +EXTRAFLAGS = -DHAVE_CURL $(CURL_CFLAGS) $(CURL_INC) -I.. -I..\.. -I..\geojson -I..\geojson\libjson + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/ogr_elastic.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/ogr_elastic.h new file mode 100644 index 000000000..0722dd1c1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/ogr_elastic.h @@ -0,0 +1,121 @@ +/****************************************************************************** + * $Id: ogr_elastic.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: ElasticSearch Translator + * Purpose: + * Author: + * + ****************************************************************************** + * Copyright (c) 2011, Adam Estrada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_ELASTIC_H_INCLUDED +#define _OGR_ELASTIC_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "ogr_p.h" +#include "cpl_hash_set.h" + +class OGRElasticDataSource; + +/************************************************************************/ +/* OGRElasticLayer */ +/************************************************************************/ + +class OGRElasticLayer : public OGRLayer { + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference* poSRS; + OGRElasticDataSource* poDS; + CPLString sIndex; + void* pAttributes; + char* pszLayerName; + +public: + OGRElasticLayer(const char *pszFilename, + const char* layerName, + OGRElasticDataSource* poDS, + OGRSpatialReference *poSRSIn, + int bWriteMode = FALSE); + ~OGRElasticLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRErr ICreateFeature(OGRFeature *poFeature); + OGRErr CreateField(OGRFieldDefn *poField, int bApproxOK); + + OGRFeatureDefn * GetLayerDefn(); + + int TestCapability(const char *); + + GIntBig GetFeatureCount(int bForce); + + void PushIndex(); + CPLString BuildMap(); +}; + +/************************************************************************/ +/* OGRElasticDataSource */ +/************************************************************************/ + +class OGRElasticDataSource : public OGRDataSource { + char* pszName; + + OGRElasticLayer** papoLayers; + int nLayers; + +public: + OGRElasticDataSource(); + ~OGRElasticDataSource(); + + int Open(const char * pszFilename, + int bUpdate); + + int Create(const char *pszFilename, + char **papszOptions); + + const char* GetName() { + return pszName; + } + + int GetLayerCount() { + return nLayers; + } + OGRLayer* GetLayer(int); + + OGRLayer * ICreateLayer(const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions); + + int TestCapability(const char *); + + void UploadFile(const CPLString &url, const CPLString &data); + void DeleteIndex(const CPLString &url); + + int bOverwrite; + int nBulkUpload; + char* pszWriteMap; + char* pszMapping; +}; + + +#endif /* ndef _OGR_Elastic_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/ogrelasticdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/ogrelasticdatasource.cpp new file mode 100644 index 000000000..2ff5210cd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/ogrelasticdatasource.cpp @@ -0,0 +1,201 @@ +/****************************************************************************** + * $Id: ogrelasticdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: ElasticSearch Translator + * Purpose: + * Author: + * + ****************************************************************************** + * Copyright (c) 2011, Adam Estrada + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +// What was this supposed to do? +// #pragma warning( disable : 4251 ) + +#include "ogr_elastic.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" +#include "cpl_http.h" + +CPL_CVSID("$Id: ogrelasticdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRElasticDataSource() */ +/************************************************************************/ + +OGRElasticDataSource::OGRElasticDataSource() { + papoLayers = NULL; + nLayers = 0; + pszName = NULL; + pszMapping = NULL; + pszWriteMap = NULL; +} + +/************************************************************************/ +/* ~OGRElasticDataSource() */ +/************************************************************************/ + +OGRElasticDataSource::~OGRElasticDataSource() { + for (int i = 0; i < nLayers; i++) + delete papoLayers[i]; + CPLFree(papoLayers); + CPLFree(pszName); + CPLFree(pszMapping); + CPLFree(pszWriteMap); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRElasticDataSource::TestCapability(const char * pszCap) { + if (EQUAL(pszCap, ODsCCreateLayer)) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRElasticDataSource::GetLayer(int iLayer) { + if (iLayer < 0 || iLayer >= nLayers) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * OGRElasticDataSource::ICreateLayer(const char * pszLayerName, + OGRSpatialReference *poSRS, + CPL_UNUSED OGRwkbGeometryType eType, + CPL_UNUSED char ** papszOptions) { + nLayers++; + papoLayers = (OGRElasticLayer **) CPLRealloc(papoLayers, nLayers * sizeof (OGRElasticLayer*)); + papoLayers[nLayers - 1] = new OGRElasticLayer(pszName, pszLayerName, this, poSRS, TRUE); + + return papoLayers[nLayers - 1]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRElasticDataSource::Open(CPL_UNUSED const char * pszFilename, + CPL_UNUSED int bUpdateIn) { + CPLError(CE_Failure, CPLE_NotSupported, + "OGR/Elastic driver does not support opening a file"); + return FALSE; +} + + +/************************************************************************/ +/* DeleteIndex() */ +/************************************************************************/ + +void OGRElasticDataSource::DeleteIndex(const CPLString &url) { + char** papszOptions = NULL; + papszOptions = CSLAddNameValue(papszOptions, "CUSTOMREQUEST", "DELETE"); + CPLHTTPResult* psResult = CPLHTTPFetch(url, papszOptions); + CSLDestroy(papszOptions); + if (psResult) { + CPLHTTPDestroyResult(psResult); + } +} + +/************************************************************************/ +/* UploadFile() */ +/************************************************************************/ + +void OGRElasticDataSource::UploadFile(const CPLString &url, const CPLString &data) { + char** papszOptions = NULL; + papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", data.c_str()); + papszOptions = CSLAddNameValue(papszOptions, "HEADERS", + "Content-Type: application/x-javascript; charset=UTF-8"); + + CPLHTTPResult* psResult = CPLHTTPFetch(url, papszOptions); + CSLDestroy(papszOptions); + if (psResult) { + CPLHTTPDestroyResult(psResult); + } +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRElasticDataSource::Create(const char *pszFilename, + CPL_UNUSED char **papszOptions) { + this->pszName = CPLStrdup(pszFilename); + + const char* pszMetaFile = CPLGetConfigOption("ES_META", NULL); + const char* pszWriteMap = CPLGetConfigOption("ES_WRITEMAP", NULL);; + this->bOverwrite = CSLTestBoolean(CPLGetConfigOption("ES_OVERWRITE", "0")); + this->nBulkUpload = (int) CPLAtof(CPLGetConfigOption("ES_BULK", "0")); + + if (pszWriteMap != NULL) { + this->pszWriteMap = CPLStrdup(pszWriteMap); + } + + // Read in the meta file from disk + if (pszMetaFile != NULL) + { + int fsize; + char *fdata; + FILE *fp; + + fp = fopen(pszMetaFile, "rb"); + if (fp != NULL) { + fseek(fp, 0, SEEK_END); + fsize = (int) ftell(fp); + + fdata = (char *) malloc(fsize + 1); + + fseek(fp, 0, SEEK_SET); + if (0 == fread(fdata, fsize, 1, fp)) { + CPLError(CE_Failure, CPLE_FileIO, + "OGRElasticDataSource::Create read failed."); + } + fdata[fsize] = 0; + this->pszMapping = fdata; + fclose(fp); + } + } + + // Do a status check to ensure that the server is valid + CPLHTTPResult* psResult = CPLHTTPFetch(CPLSPrintf("%s/_status", pszFilename), NULL); + int bOK = (psResult != NULL && psResult->pszErrBuf == NULL); + if (!bOK) + { + CPLError(CE_Failure, CPLE_NoWriteAccess, + "Could not connect to server"); + } + + CPLHTTPDestroyResult(psResult); + + return bOK; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/ogrelasticdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/ogrelasticdriver.cpp new file mode 100644 index 000000000..3b2f616b9 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/ogrelasticdriver.cpp @@ -0,0 +1,85 @@ +/****************************************************************************** + * $Id: ogrelasticdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: ElasticSearch Translator + * Purpose: + * Author: + * + ****************************************************************************** + * Copyright (c) 2011, Adam Estrada + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_elastic.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrelasticdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRElasticSearchDriverCreate() */ +/************************************************************************/ +static GDALDataset* OGRElasticSearchDriverCreate( const char * pszName, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED int nBands, + CPL_UNUSED GDALDataType eDT, + char ** papszOptions ) +{ + OGRElasticDataSource *poDS = new OGRElasticDataSource(); + + if (!poDS->Create(pszName, papszOptions)) { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRElastic() */ +/************************************************************************/ + +void RegisterOGRElastic() { + if (!GDAL_CHECK_VERSION("OGR/Elastic Search driver")) + return; + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ElasticSearch" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ElasticSearch" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Elastic Search" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_elasticsearch.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, + ""); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, + ""); + + poDriver->pfnCreate = OGRElasticSearchDriverCreate; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/ogrelasticlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/ogrelasticlayer.cpp new file mode 100644 index 000000000..266530ab3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/elastic/ogrelasticlayer.cpp @@ -0,0 +1,356 @@ +/****************************************************************************** + * $Id: ogrelasticlayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: ElasticSearch Translator + * Purpose: + * Author: + * + ****************************************************************************** + * Copyright (c) 2011, Adam Estrada + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_elastic.h" +#include "cpl_conv.h" +#include "cpl_minixml.h" +#include "ogr_api.h" +#include "ogr_p.h" +#include // JSON-C + +CPL_CVSID("$Id: ogrelasticlayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRElasticLayer() */ +/************************************************************************/ + +OGRElasticLayer::OGRElasticLayer(CPL_UNUSED const char* pszFilename, + const char* pszLayerName, + OGRElasticDataSource* poDS, + OGRSpatialReference *poSRSIn, + CPL_UNUSED int bWriteMode) { + this->pszLayerName = CPLStrdup(pszLayerName); + this->poDS = poDS; + this->pAttributes = NULL; + + // If we are overwriting, then delete the current index if it exists + if (poDS->bOverwrite) { + poDS->DeleteIndex(CPLSPrintf("%s/%s", poDS->GetName(), pszLayerName)); + } + + // Create the index + poDS->UploadFile(CPLSPrintf("%s/%s", poDS->GetName(), pszLayerName), ""); + + // If we have a user specified mapping, then go ahead and update it now + if (poDS->pszMapping != NULL) { + poDS->UploadFile(CPLSPrintf("%s/%s/FeatureCollection/_mapping", poDS->GetName(), pszLayerName), + poDS->pszMapping); + } + + poFeatureDefn = new OGRFeatureDefn(pszLayerName); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + poSRS = poSRSIn; + if (poSRS) + poSRS->Reference(); + + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + ResetReading(); + return; +} + +/************************************************************************/ +/* ~OGRElasticLayer() */ +/************************************************************************/ + +OGRElasticLayer::~OGRElasticLayer() { + PushIndex(); + + CPLFree(pszLayerName); + + poFeatureDefn->Release(); + + if (poSRS != NULL) + poSRS->Release(); +} + + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRElasticLayer::GetLayerDefn() { + return poFeatureDefn; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRElasticLayer::ResetReading() { + return; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRElasticLayer::GetNextFeature() { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot read features when writing a Elastic file"); + return NULL; +} + +/************************************************************************/ +/* AppendGroup() */ +/************************************************************************/ + +json_object *AppendGroup(json_object *parent, const CPLString &name) { + json_object *obj = json_object_new_object(); + json_object *properties = json_object_new_object(); + json_object_object_add(parent, name, obj); + json_object_object_add(obj, "properties", properties); + return properties; +} + +/************************************************************************/ +/* AddPropertyMap() */ +/************************************************************************/ + +json_object *AddPropertyMap(const CPLString &type, const CPLString &format = "") { + json_object *obj = json_object_new_object(); + json_object_object_add(obj, "store", json_object_new_string("yes")); + json_object_object_add(obj, "type", json_object_new_string(type.c_str())); + if (!format.empty()) { + json_object_object_add(obj, "format", json_object_new_string(format.c_str())); + } + return obj; +} + +/************************************************************************/ +/* BuildMap() */ +/************************************************************************/ + +CPLString OGRElasticLayer::BuildMap() { + json_object *map = json_object_new_object(); + json_object *properties = json_object_new_object(); + + json_object *Feature = AppendGroup(map, "FeatureCollection"); + json_object_object_add(Feature, "type", AddPropertyMap("string")); + json_object_object_add(Feature, "properties", properties); + if (pAttributes) json_object_object_add(properties, "properties", (json_object *) pAttributes); + json_object *geometry = AppendGroup(Feature, "geometry"); + json_object_object_add(geometry, "type", AddPropertyMap("string")); + json_object_object_add(geometry, "coordinates", AddPropertyMap("geo_point")); + + CPLString jsonMap(json_object_to_json_string(map)); + json_object_put(map); + + // The attribute's were freed from the deletion of the map object + // because we added it as a child of one of the map object attributes + if (pAttributes) { + pAttributes = NULL; + } + + return jsonMap; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRElasticLayer::ICreateFeature(OGRFeature *poFeature) { + + // Check to see if the user has elected to only write out the mapping file + // This method will only write out one layer from the vector file in cases where there are multiple layers + if (poDS->pszWriteMap != NULL) { + if (pAttributes) { + CPLString map = BuildMap(); + + // Write the map to a file + FILE *f = fopen(poDS->pszWriteMap, "wb"); + if (f) { + fwrite(map.c_str(), 1, map.length(), f); + fclose(f); + } + } + return OGRERR_NONE; + } + + // Check to see if we have any fields to upload to this index + if (poDS->pszMapping == NULL && pAttributes) { + poDS->UploadFile(CPLSPrintf("%s/%s/FeatureCollection/_mapping", poDS->GetName(), pszLayerName), BuildMap()); + } + + // Get the center point of the geometry + OGREnvelope env; + if (!poFeature->GetGeometryRef()) { + return OGRERR_FAILURE; + } + poFeature->GetGeometryRef()->getEnvelope(&env); + + json_object *fieldObject = json_object_new_object(); + json_object *geometry = json_object_new_object(); + json_object *coordinates = json_object_new_array(); + json_object *properties = json_object_new_object(); + + json_object_object_add(fieldObject, "geometry", geometry); + json_object_object_add(geometry, "type", json_object_new_string("POINT")); + json_object_object_add(geometry, "coordinates", coordinates); + json_object_array_add(coordinates, json_object_new_double((env.MaxX + env.MinX)*0.5)); + json_object_array_add(coordinates, json_object_new_double((env.MaxY + env.MinY)*0.5)); + json_object_object_add(fieldObject, "type", json_object_new_string("Feature")); + json_object_object_add(fieldObject, "properties", properties); + + // For every field that + int fieldCount = poFeatureDefn->GetFieldCount(); + for (int i = 0; i < fieldCount; i++) { + if(!poFeature->IsFieldSet( i ) ) { + continue; + } + switch (poFeatureDefn->GetFieldDefn(i)->GetType()) { + case OFTInteger: + json_object_object_add(properties, + poFeatureDefn->GetFieldDefn(i)->GetNameRef(), + json_object_new_int(poFeature->GetFieldAsInteger(i))); + break; + case OFTReal: + json_object_object_add(properties, + poFeatureDefn->GetFieldDefn(i)->GetNameRef(), + json_object_new_double(poFeature->GetFieldAsDouble(i))); + break; + default: + { + CPLString tmp = poFeature->GetFieldAsString(i); + json_object_object_add(properties, + poFeatureDefn->GetFieldDefn(i)->GetNameRef(), + json_object_new_string(tmp)); + } + } + } + + // Build the field string + CPLString fields(json_object_to_json_string(fieldObject)); + json_object_put(fieldObject); + + // Check to see if we're using bulk uploading + if (poDS->nBulkUpload > 0) { + sIndex += CPLSPrintf("{\"index\" :{\"_index\":\"%s\", \"_type\":\"FeatureCollection\"}}\n", pszLayerName) + + fields + "\n\n"; + + // Only push the data if we are over our bulk upload limit + if ((int) sIndex.length() > poDS->nBulkUpload) { + PushIndex(); + } + + } else { // Fall back to using single item upload for every feature + poDS->UploadFile(CPLSPrintf("%s/%s/FeatureCollection/", poDS->GetName(), pszLayerName), fields); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* PushIndex() */ +/************************************************************************/ + +void OGRElasticLayer::PushIndex() { + if (sIndex.empty()) { + return; + } + + poDS->UploadFile(CPLSPrintf("%s/_bulk", poDS->GetName()), sIndex); + sIndex.clear(); +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRElasticLayer::CreateField(OGRFieldDefn *poFieldDefn, + CPL_UNUSED int bApproxOK) { + if (!pAttributes) { + pAttributes = json_object_new_object(); + } + + switch (poFieldDefn->GetType()) { + case OFTInteger: + json_object_object_add((json_object *) pAttributes, poFieldDefn->GetNameRef(), AddPropertyMap("integer")); + break; + case OFTReal: + json_object_object_add((json_object *) pAttributes, poFieldDefn->GetNameRef(), AddPropertyMap("float")); + break; + case OFTString: + json_object_object_add((json_object *) pAttributes, poFieldDefn->GetNameRef(), AddPropertyMap("string")); + break; + case OFTDateTime: + case OFTDate: + json_object_object_add((json_object *) pAttributes, poFieldDefn->GetNameRef(), AddPropertyMap("date", "yyyy/MM/dd HH:mm:ss||yyyy/MM/dd")); + break; + default: + + // These types are mapped as strings and may not be correct + /* + OFTTime: + OFTIntegerList = 1, + OFTRealList = 3, + OFTStringList = 5, + OFTWideString = 6, + OFTWideStringList = 7, + OFTBinary = 8, + OFTMaxType = 11 + */ + json_object_object_add((json_object *) pAttributes, poFieldDefn->GetNameRef(), AddPropertyMap("string")); + } + + poFeatureDefn->AddFieldDefn(poFieldDefn); + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRElasticLayer::TestCapability(const char * pszCap) { + if (EQUAL(pszCap, OLCFastFeatureCount)) + return FALSE; + + else if (EQUAL(pszCap, OLCStringsAsUTF8)) + return TRUE; + + else if (EQUAL(pszCap, OLCSequentialWrite)) + return TRUE; + else if (EQUAL(pszCap, OLCCreateField)) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRElasticLayer::GetFeatureCount(CPL_UNUSED int bForce) { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot read features when writing a Elastic file"); + return 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbDatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbDatasource.cpp new file mode 100644 index 000000000..806f0dbaa --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbDatasource.cpp @@ -0,0 +1,540 @@ +/****************************************************************************** + * $Id: FGdbDatasource.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements FileGDB OGR Datasource. + * Author: Ragi Yaser Burhum, ragi@burhum.com + * Paul Ramsey, pramsey at cleverelephant.ca + * + ****************************************************************************** + * Copyright (c) 2010, Ragi Yaser Burhum + * Copyright (c) 2011, Paul Ramsey + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_fgdb.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "gdal.h" +#include "FGdbUtils.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: FGdbDatasource.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +using std::vector; +using std::wstring; + +/************************************************************************/ +/* FGdbDataSource() */ +/************************************************************************/ + +FGdbDataSource::FGdbDataSource(FGdbDriver* poDriver, + FGdbDatabaseConnection* pConnection): +OGRDataSource(), +m_poDriver(poDriver), m_pConnection(pConnection), m_pszName(0), m_pGeodatabase(NULL), m_bUpdate(false) +{ +} + +/************************************************************************/ +/* ~FGdbDataSource() */ +/************************************************************************/ + +FGdbDataSource::~FGdbDataSource() +{ + CPLMutexHolderOptionalLockD(m_poDriver->GetMutex()); + + if( m_pConnection->IsLocked() ) + CommitTransaction(); + + size_t count = m_layers.size(); + for(size_t i = 0; i < count; ++i ) + delete m_layers[i]; + + m_poDriver->Release( m_pszName ); + CPLFree( m_pszName ); +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int FGdbDataSource::Open(const char * pszNewName, int bUpdate ) +{ + m_pszName = CPLStrdup( pszNewName ); + m_pGeodatabase = m_pConnection->GetGDB(); + m_bUpdate = bUpdate; + + std::vector typesRequested; + + // We're only interested in Tables, Feature Datasets and Feature Classes + typesRequested.push_back(L"Feature Class"); + typesRequested.push_back(L"Table"); + typesRequested.push_back(L"Feature Dataset"); + + bool rv = LoadLayers(L"\\"); + + return rv; +} + +/************************************************************************/ +/* OpenFGDBTables() */ +/************************************************************************/ + +bool FGdbDataSource::OpenFGDBTables(const std::wstring &type, + const std::vector &layers) +{ + fgdbError hr; + for ( unsigned int i = 0; i < layers.size(); i++ ) + { + Table* pTable = new Table; + //CPLDebug("FGDB", "Opening %s", WStringToString(layers[i]).c_str()); + if (FAILED(hr = m_pGeodatabase->OpenTable(layers[i], *pTable))) + { + delete pTable; + GDBErr(hr, "Error opening " + WStringToString(layers[i]), + CE_Warning, + ". Skipping it. " + "Might be due to unsupported spatial reference system. Using OpenFileGDB driver should solve it"); + continue; + } + FGdbLayer* pLayer = new FGdbLayer(); + if (!pLayer->Initialize(this, pTable, layers[i], type)) + { + delete pLayer; + return GDBErr(hr, "Error initializing OGRLayer for " + WStringToString(layers[i])); + } + + m_layers.push_back(pLayer); + } + return true; +} + +/************************************************************************/ +/* LoadLayers() */ +/************************************************************************/ + +bool FGdbDataSource::LoadLayers(const std::wstring &root) +{ + std::vector tables; + std::vector featureclasses; + std::vector featuredatasets; + fgdbError hr; + + /* Find all the Tables in the root */ + if ( FAILED(hr = m_pGeodatabase->GetChildDatasets(root, L"Table", tables)) ) + { + return GDBErr(hr, "Error reading Tables in " + WStringToString(root)); + } + /* Open the tables we found */ + if ( tables.size() > 0 && ! OpenFGDBTables(L"Table", tables) ) + return false; + + /* Find all the Feature Classes in the root */ + if ( FAILED(hr = m_pGeodatabase->GetChildDatasets(root, L"Feature Class", featureclasses)) ) + { + return GDBErr(hr, "Error reading Feature Classes in " + WStringToString(root)); + } + /* Open the tables we found */ + if ( featureclasses.size() > 0 && ! OpenFGDBTables(L"Feature Class", featureclasses) ) + return false; + + /* Find all the Feature Datasets in the root */ + if ( FAILED(hr = m_pGeodatabase->GetChildDatasets(root, L"Feature Dataset", featuredatasets)) ) + { + return GDBErr(hr, "Error reading Feature Datasets in " + WStringToString(root)); + } + /* Look for Feature Classes inside the Feature Dataset */ + for ( unsigned int i = 0; i < featuredatasets.size(); i++ ) + { + if ( FAILED(hr = m_pGeodatabase->GetChildDatasets(featuredatasets[i], L"Feature Class", featureclasses)) ) + { + return GDBErr(hr, "Error reading Feature Classes in " + WStringToString(featuredatasets[i])); + } + if ( featureclasses.size() > 0 && ! OpenFGDBTables(L"Feature Class", featureclasses) ) + return false; + } + return true; +} + + +#if 0 +/************************************************************************/ +/* LoadLayersOld() */ +/************************************************************************/ + +/* Old recursive LoadLayers. Removed in favor of simple one that only + looks at FeatureClasses and Tables. */ + +// Flattens out hierarchichal GDB structure +bool FGdbDataSource::LoadLayersOld(const std::vector & datasetTypes, + const wstring & parent) +{ + long hr = S_OK; + + // I didn't find an API to give me the type of the dataset based on name - I am *not* + // parsing XML for something like this - in the meantime I can use this hack to see + // if the dataset had any children whatsoever - if so, then I won't attempt to open it + // otherwise, do attempt to do that + + bool childrenFound = false; + bool errorsEncountered = false; + + for (size_t dsTypeIndex = 0; dsTypeIndex < datasetTypes.size(); dsTypeIndex++) + { + std::vector childDatasets; + m_pGeodatabase->GetChildDatasets( parent, datasetTypes[dsTypeIndex], childDatasets); + + if (childDatasets.size() > 0) + { + //it is a container of other datasets + + for (size_t childDatasetIndex = 0; + childDatasetIndex < childDatasets.size(); + childDatasetIndex++) + { + childrenFound = true; + + // do something with it + // For now, we just ignore dataset containers and only open the children + //std::wcout << datasetTypes[dsTypeIndex] << L" " << childDatasets[childDatasetIndex] << std::endl; + + if (!LoadLayersOld(datasetTypes, childDatasets[childDatasetIndex])) + errorsEncountered = true; + } + } + } + + //it is a full fledged dataset itself without children - open it (except the root) + + if ((!childrenFound) && parent != L"\\") + { + //wcout << "Opening " << parent << "..."; + Table* pTable = new Table; + if (FAILED(hr = m_pGeodatabase->OpenTable(parent,*pTable))) + { + delete pTable; + return GDBErr(hr, "Error opening " + WStringToString(parent)); + } + + FGdbLayer* pLayer = new FGdbLayer; + + //pLayer has ownership of the table pointer as soon Initialize is called + if (!pLayer->Initialize(this, pTable, parent)) + { + delete pLayer; + + return GDBErr(hr, "Error initializing OGRLayer for " + + WStringToString(parent)); + } + + m_layers.push_back(pLayer); + } + + return !errorsEncountered; +} +#endif + + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +OGRErr FGdbDataSource::DeleteLayer( int iLayer ) +{ + if( !m_bUpdate ) + return OGRERR_FAILURE; + + if( iLayer < 0 || iLayer >= static_cast(m_layers.size()) ) + return OGRERR_FAILURE; + + FGdbLayer* poBaseLayer = m_layers[iLayer]; + + // Fetch FGDBAPI Table before deleting OGR layer object + + //Table* pTable = poBaseLayer->GetTable(); + + std::string name = poBaseLayer->GetLayerDefn()->GetName(); + std::wstring strPath = poBaseLayer->GetTablePath(); + std::wstring strType = poBaseLayer->GetType(); + + // delete OGR layer + delete m_layers[iLayer]; + + //pTable = NULL; // OGR Layer had ownership of FGDB Table + + m_layers.erase(m_layers.begin() + iLayer); + + long hr; + + if (FAILED(hr = m_pGeodatabase->Delete(strPath, strType))) + { + CPLError( CE_Warning, CPLE_AppDefined, + "%s was not deleted however it has been closed", name.c_str()); + GDBErr(hr, "Failed deleting dataset"); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int FGdbDataSource::TestCapability( const char * pszCap ) +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return m_bUpdate; + + else if( EQUAL(pszCap,ODsCDeleteLayer) ) + return m_bUpdate; + + return FALSE; +} + + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *FGdbDataSource::GetLayer( int iLayer ) +{ + int count = static_cast(m_layers.size()); + + if( iLayer < 0 || iLayer >= count ) + return NULL; + else + return m_layers[iLayer]; +} + +/************************************************************************/ +/* ICreateLayer() */ +/* */ +/* See FGdbLayer::Create for creation options */ +/************************************************************************/ + +OGRLayer * +FGdbDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ) +{ + if( !m_bUpdate ) + return NULL; + + FGdbLayer* pLayer = new FGdbLayer(); + if (!pLayer->Create(this, pszLayerName, poSRS, eType, papszOptions)) + { + delete pLayer; + return NULL; + } + + m_layers.push_back(pLayer); + + return pLayer; +} + + +/************************************************************************/ +/* OGRFGdbSingleFeatureLayer */ +/************************************************************************/ + +class OGRFGdbSingleFeatureLayer : public OGRLayer +{ + private: + char *pszVal; + OGRFeatureDefn *poFeatureDefn; + int iNextShapeId; + + public: + OGRFGdbSingleFeatureLayer( const char* pszLayerName, + const char *pszVal ); + ~OGRFGdbSingleFeatureLayer(); + + virtual void ResetReading() { iNextShapeId = 0; } + virtual OGRFeature *GetNextFeature(); + virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; } + virtual int TestCapability( const char * ) { return FALSE; } +}; + +/************************************************************************/ +/* OGRFGdbSingleFeatureLayer() */ +/************************************************************************/ + +OGRFGdbSingleFeatureLayer::OGRFGdbSingleFeatureLayer(const char* pszLayerName, + const char *pszVal ) +{ + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + OGRFieldDefn oField( "FIELD_1", OFTString ); + poFeatureDefn->AddFieldDefn( &oField ); + + iNextShapeId = 0; + this->pszVal = pszVal ? CPLStrdup(pszVal) : NULL; +} + +/************************************************************************/ +/* ~OGRFGdbSingleFeatureLayer() */ +/************************************************************************/ + +OGRFGdbSingleFeatureLayer::~OGRFGdbSingleFeatureLayer() +{ + if( poFeatureDefn != NULL ) + poFeatureDefn->Release(); + CPLFree(pszVal); +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature * OGRFGdbSingleFeatureLayer::GetNextFeature() +{ + if (iNextShapeId != 0) + return NULL; + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + if (pszVal) + poFeature->SetField(0, pszVal); + poFeature->SetFID(iNextShapeId ++); + return poFeature; +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * FGdbDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ + size_t count = m_layers.size(); + for(size_t i = 0; i < count; ++i ) + { + m_layers[i]->EndBulkLoad(); + } + +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Special case GetLayerDefinition */ +/* -------------------------------------------------------------------- */ + if (EQUALN(pszSQLCommand, "GetLayerDefinition ", strlen("GetLayerDefinition "))) + { + FGdbLayer* poLayer = (FGdbLayer*) GetLayerByName(pszSQLCommand + strlen("GetLayerDefinition ")); + if (poLayer) + { + char* pszVal = NULL; + poLayer->GetLayerXML(&pszVal); + OGRLayer* poRet = new OGRFGdbSingleFeatureLayer( "LayerDefinition", pszVal ); + CPLFree(pszVal); + return poRet; + } + else + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Special case GetLayerMetadata */ +/* -------------------------------------------------------------------- */ + if (EQUALN(pszSQLCommand, "GetLayerMetadata ", strlen("GetLayerMetadata "))) + { + FGdbLayer* poLayer = (FGdbLayer*) GetLayerByName(pszSQLCommand + strlen("GetLayerMetadata ")); + if (poLayer) + { + char* pszVal = NULL; + poLayer->GetLayerMetadataXML(&pszVal); + OGRLayer* poRet = new OGRFGdbSingleFeatureLayer( "LayerMetadata", pszVal ); + CPLFree(pszVal); + return poRet; + } + else + return NULL; + } + + /* TODO: remove that workaround when the SDK has finally a decent */ + /* SQL support ! */ + if( EQUALN(pszSQLCommand, "SELECT ", 7) && pszDialect == NULL ) + { + CPLDebug("FGDB", "Support for SELECT is known to be partially " + "non-compliant with FileGDB SDK API v1.2.\n" + "So for now, we use default OGR SQL engine. " + "Explicitly specify -dialect FileGDB\n" + "to use the SQL engine from the FileGDB SDK API"); + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + } + +/* -------------------------------------------------------------------- */ +/* Run the SQL */ +/* -------------------------------------------------------------------- */ + EnumRows* pEnumRows = new EnumRows; + long hr; + try + { + hr = m_pGeodatabase->ExecuteSQL( + StringToWString(pszSQLCommand), true, *pEnumRows); + } + catch(...) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Exception occured at executing '%s'. Application may become unstable", pszSQLCommand); + delete pEnumRows; + return NULL; + } + + if (FAILED(hr)) + { + GDBErr(hr, CPLSPrintf("Failed at executing '%s'", pszSQLCommand)); + delete pEnumRows; + return NULL; + } + + if( EQUALN(pszSQLCommand, "SELECT ", 7) ) + { + return new FGdbResultLayer(this, pszSQLCommand, pEnumRows); + } + else + { + delete pEnumRows; + return NULL; + } +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void FGdbDataSource::ReleaseResultSet( OGRLayer * poResultsSet ) +{ + delete poResultsSet; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbDriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbDriver.cpp new file mode 100644 index 000000000..0c63ebc10 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbDriver.cpp @@ -0,0 +1,608 @@ +/****************************************************************************** + * $Id: FGdbDriver.cpp 29180 2015-05-10 15:07:48Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements FileGDB OGR driver. + * Author: Ragi Yaser Burhum, ragi@burhum.com + * Paul Ramsey, pramsey at cleverelephant.ca + * + ****************************************************************************** + * Copyright (c) 2010, Ragi Yaser Burhum + * Copyright (c) 2011, Paul Ramsey + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_fgdb.h" +#include "cpl_conv.h" +#include "FGdbUtils.h" +#include "cpl_multiproc.h" +#include "ogrmutexeddatasource.h" + +CPL_CVSID("$Id: FGdbDriver.cpp 29180 2015-05-10 15:07:48Z rouault $"); + +extern "C" void RegisterOGRFileGDB(); + +/************************************************************************/ +/* FGdbDriver() */ +/************************************************************************/ +FGdbDriver::FGdbDriver(): OGRSFDriver(), hMutex(NULL) +{ +} + +/************************************************************************/ +/* ~FGdbDriver() */ +/************************************************************************/ +FGdbDriver::~FGdbDriver() + +{ + if( oMapConnections.size() != 0 ) + CPLDebug("FileGDB", "Remaining %d connections. Bug?", + (int)oMapConnections.size()); + if( hMutex != NULL ) + CPLDestroyMutex(hMutex); + hMutex = NULL; +} + + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *FGdbDriver::GetName() + +{ + return "FileGDB"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *FGdbDriver::Open( const char* pszFilename, int bUpdate ) + +{ + // First check if we have to do any work. + int nLen = strlen(pszFilename); + if(! ((nLen >= 4 && EQUAL(pszFilename + nLen - 4, ".gdb")) || + (nLen >= 5 && EQUAL(pszFilename + nLen - 5, ".gdb/"))) ) + return NULL; + + long hr; + + /* Check that the filename is really a directory, to avoid confusion with */ + /* Garmin MapSource - gdb format which can be a problem when the FileGDB */ + /* driver is loaded as a plugin, and loaded before the GPSBabel driver */ + /* (http://trac.osgeo.org/osgeo4w/ticket/245) */ + VSIStatBuf stat; + if( CPLStat( pszFilename, &stat ) != 0 || !VSI_ISDIR(stat.st_mode) ) + { + return NULL; + } + + CPLMutexHolderD(&hMutex); + Geodatabase* pGeoDatabase = NULL; + + FGdbDatabaseConnection* pConnection = oMapConnections[pszFilename]; + if( pConnection != NULL ) + { + pGeoDatabase = pConnection->m_pGeodatabase; + pConnection->m_nRefCount ++; + CPLDebug("FileGDB", "ref_count of %s = %d now", pszFilename, + pConnection->m_nRefCount); + } + else + { + pGeoDatabase = new Geodatabase; + hr = ::OpenGeodatabase(StringToWString(pszFilename), *pGeoDatabase); + + if (FAILED(hr) || pGeoDatabase == NULL) + { + delete pGeoDatabase; + + if( OGRGetDriverByName("OpenFileGDB") != NULL && bUpdate == FALSE ) + { + std::wstring fgdb_error_desc_w; + std::string fgdb_error_desc("Unknown error"); + fgdbError er; + er = FileGDBAPI::ErrorInfo::GetErrorDescription(hr, fgdb_error_desc_w); + if ( er == S_OK ) + { + fgdb_error_desc = WStringToString(fgdb_error_desc_w); + } + CPLDebug("FileGDB", "Cannot open %s with FileGDB driver: %s. Failing silently so OpenFileGDB can be tried", + pszFilename, + fgdb_error_desc.c_str()); + } + else + { + GDBErr(hr, "Failed to open Geodatabase"); + } + oMapConnections.erase(pszFilename); + return NULL; + } + + CPLDebug("FileGDB", "Really opening %s", pszFilename); + pConnection = new FGdbDatabaseConnection(pGeoDatabase); + oMapConnections[pszFilename] = pConnection; + } + + FGdbDataSource* pDS; + + pDS = new FGdbDataSource(this, pConnection); + + if(!pDS->Open( pszFilename, bUpdate ) ) + { + delete pDS; + return NULL; + } + else + { + OGRMutexedDataSource* poMutexedDS = + new OGRMutexedDataSource(pDS, TRUE, hMutex, TRUE); + if( bUpdate ) + return OGRCreateEmulatedTransactionDataSourceWrapper(poMutexedDS, this, TRUE, FALSE); + else + return poMutexedDS; + } +} + +/***********************************************************************/ +/* CreateDataSource() */ +/***********************************************************************/ + +OGRDataSource* FGdbDriver::CreateDataSource( const char * conn, + char **papszOptions) +{ + long hr; + Geodatabase *pGeodatabase; + std::wstring wconn = StringToWString(conn); + int bUpdate = TRUE; // If we're creating, we must be writing. + VSIStatBuf stat; + + CPLMutexHolderD(&hMutex); + + /* We don't support options yet, so warn if they send us some */ + if ( papszOptions ) + { + /* TODO: warning, ignoring options */ + } + + /* Only accept names of form "filename.gdb" and */ + /* also .gdb.zip to be able to return FGDB with MapServer OGR output (#4199) */ + const char* pszExt = CPLGetExtension(conn); + if ( !(EQUAL(pszExt,"gdb") || EQUAL(pszExt, "zip")) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "FGDB data source name must use 'gdb' extension.\n" ); + return NULL; + } + + /* Don't try to create on top of something already there */ + if( CPLStat( conn, &stat ) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s already exists.\n", conn ); + return NULL; + } + + /* Try to create the geodatabase */ + pGeodatabase = new Geodatabase; // Create on heap so we can store it in the Datasource + hr = CreateGeodatabase(wconn, *pGeodatabase); + + /* Handle creation errors */ + if ( S_OK != hr ) + { + const char *errstr = "Error creating geodatabase (%s).\n"; + if ( hr == -2147220653 ) + errstr = "File already exists (%s).\n"; + delete pGeodatabase; + CPLError( CE_Failure, CPLE_AppDefined, errstr, conn ); + return NULL; + } + + FGdbDatabaseConnection* pConnection = new FGdbDatabaseConnection(pGeodatabase); + oMapConnections[conn] = pConnection; + + /* Ready to embed the Geodatabase in an OGR Datasource */ + FGdbDataSource* pDS = new FGdbDataSource(this, pConnection); + if ( ! pDS->Open(conn, bUpdate) ) + { + delete pDS; + return NULL; + } + else + return OGRCreateEmulatedTransactionDataSourceWrapper( + new OGRMutexedDataSource(pDS, TRUE, hMutex, TRUE), this, + TRUE, FALSE); +} + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +OGRErr FGdbDriver::StartTransaction(OGRDataSource*& poDSInOut, int& bOutHasReopenedDS) +{ + CPLMutexHolderOptionalLockD(hMutex); + + bOutHasReopenedDS = FALSE; + + OGRMutexedDataSource* poMutexedDS = (OGRMutexedDataSource*)poDSInOut; + FGdbDataSource* poDS = (FGdbDataSource* )poMutexedDS->GetBaseDataSource(); + if( !poDS->GetUpdate() ) + return OGRERR_FAILURE; + FGdbDatabaseConnection* pConnection = poDS->GetConnection(); + if( pConnection->GetRefCount() != 1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot start transaction as database is opened in another connection"); + return OGRERR_FAILURE; + } + if( pConnection->IsLocked() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Transaction is already in progress"); + return OGRERR_FAILURE; + } + + bOutHasReopenedDS = TRUE; + + CPLString osName(poMutexedDS->GetName()); + if( osName[osName.size()-1] == '/' || osName[osName.size()-1] == '\\' ) + osName.resize(osName.size()-1); + + pConnection->m_nRefCount ++; + delete poDSInOut; + poDSInOut = NULL; + poMutexedDS = NULL; + poDS = NULL; + + ::CloseGeodatabase(*(pConnection->m_pGeodatabase)); + delete pConnection->m_pGeodatabase; + pConnection->m_pGeodatabase = NULL; + + CPLString osEditedName(osName); + osEditedName += ".ogredited"; + + CPLPushErrorHandler(CPLQuietErrorHandler); + CPLUnlinkTree(osEditedName); + CPLPopErrorHandler(); + + OGRErr eErr = OGRERR_NONE; + CPLString osDatabaseToReopen; + if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE1") || + CPLCopyTree( osEditedName, osName ) != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot backup geodatabase"); + eErr = OGRERR_FAILURE; + osDatabaseToReopen = osName; + } + else + osDatabaseToReopen = osEditedName; + + pConnection->m_pGeodatabase = new Geodatabase; + long hr = ::OpenGeodatabase(StringToWString(osDatabaseToReopen), *(pConnection->m_pGeodatabase)); + if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE2") || FAILED(hr)) + { + delete pConnection->m_pGeodatabase; + pConnection->m_pGeodatabase = NULL; + Release(osName); + GDBErr(hr, CPLSPrintf("Failed to open %s. Dataset should be closed", + osDatabaseToReopen.c_str())); + + return OGRERR_FAILURE; + } + + FGdbDataSource* pDS = new FGdbDataSource(this, pConnection); + pDS->Open(osName, TRUE); + poDSInOut = new OGRMutexedDataSource(pDS, TRUE, hMutex, TRUE); + + if( eErr == OGRERR_NONE ) + pConnection->SetLocked(TRUE); + return eErr; +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr FGdbDriver::CommitTransaction(OGRDataSource*& poDSInOut, int& bOutHasReopenedDS) +{ + CPLMutexHolderOptionalLockD(hMutex); + + bOutHasReopenedDS = FALSE; + + + OGRMutexedDataSource* poMutexedDS = (OGRMutexedDataSource*)poDSInOut; + FGdbDataSource* poDS = (FGdbDataSource* )poMutexedDS->GetBaseDataSource(); + FGdbDatabaseConnection* pConnection = poDS->GetConnection(); + if( !pConnection->IsLocked() ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "No transaction in progress"); + return OGRERR_FAILURE; + } + + bOutHasReopenedDS = TRUE; + + CPLString osName(poMutexedDS->GetName()); + if( osName[osName.size()-1] == '/' || osName[osName.size()-1] == '\\' ) + osName.resize(osName.size()-1); + + pConnection->m_nRefCount ++; + delete poDSInOut; + poDSInOut = NULL; + poMutexedDS = NULL; + poDS = NULL; + + ::CloseGeodatabase(*(pConnection->m_pGeodatabase)); + delete pConnection->m_pGeodatabase; + pConnection->m_pGeodatabase = NULL; + + CPLString osEditedName(osName); + osEditedName += ".ogredited"; + CPLString osTmpName(osName); + osTmpName += ".ogrtmp"; + + /* Install the backup copy as the main database in 3 steps : */ + /* first rename the main directory in .tmp */ + /* then rename the edited copy under regular name */ + /* and finally dispose the .tmp directory */ + /* That way there's no risk definitely losing data */ + if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE1") || + VSIRename(osName, osTmpName) != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot rename %s to %s. Edited database during transaction is in %s" + "Dataset should be closed", + osName.c_str(), osTmpName.c_str(), osEditedName.c_str()); + pConnection->SetLocked(FALSE); + Release(osName); + return OGRERR_FAILURE; + } + + if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE2") || + VSIRename(osEditedName, osName) != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot rename %s to %s. The original geodatabase is in '%s'. " + "Dataset should be closed", + osEditedName.c_str(), osName.c_str(), osTmpName.c_str()); + pConnection->SetLocked(FALSE); + Release(osName); + return OGRERR_FAILURE; + } + + if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE3") || + CPLUnlinkTree(osTmpName) != 0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot remove %s. Manual cleanup required", osTmpName.c_str()); + } + + pConnection->m_pGeodatabase = new Geodatabase; + long hr = ::OpenGeodatabase(StringToWString(osName), *(pConnection->m_pGeodatabase)); + if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE4") || FAILED(hr)) + { + delete pConnection->m_pGeodatabase; + pConnection->m_pGeodatabase = NULL; + pConnection->SetLocked(FALSE); + Release(osName); + GDBErr(hr, "Failed to re-open Geodatabase. Dataset should be closed"); + return OGRERR_FAILURE; + } + + FGdbDataSource* pDS = new FGdbDataSource(this, pConnection); + pDS->Open(osName, TRUE); + poDSInOut = new OGRMutexedDataSource(pDS, TRUE, hMutex, TRUE); + + pConnection->SetLocked(FALSE); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +OGRErr FGdbDriver::RollbackTransaction(OGRDataSource*& poDSInOut, int& bOutHasReopenedDS) +{ + CPLMutexHolderOptionalLockD(hMutex); + + bOutHasReopenedDS = FALSE; + + OGRMutexedDataSource* poMutexedDS = (OGRMutexedDataSource*)poDSInOut; + FGdbDataSource* poDS = (FGdbDataSource* )poMutexedDS->GetBaseDataSource(); + FGdbDatabaseConnection* pConnection = poDS->GetConnection(); + if( !pConnection->IsLocked() ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "No transaction in progress"); + return OGRERR_FAILURE; + } + + bOutHasReopenedDS = TRUE; + + CPLString osName(poMutexedDS->GetName()); + if( osName[osName.size()-1] == '/' || osName[osName.size()-1] == '\\' ) + osName.resize(osName.size()-1); + + pConnection->m_nRefCount ++; + delete poDSInOut; + poDSInOut = NULL; + poMutexedDS = NULL; + poDS = NULL; + + ::CloseGeodatabase(*(pConnection->m_pGeodatabase)); + delete pConnection->m_pGeodatabase; + pConnection->m_pGeodatabase = NULL; + + CPLString osEditedName(osName); + osEditedName += ".ogredited"; + + OGRErr eErr = OGRERR_NONE; + if( EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE1") || + CPLUnlinkTree(osEditedName) != 0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot remove %s. Manual cleanup required", osEditedName.c_str()); + eErr = OGRERR_FAILURE; + } + + pConnection->m_pGeodatabase = new Geodatabase; + long hr = ::OpenGeodatabase(StringToWString(osName), *(pConnection->m_pGeodatabase)); + if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE2") || + FAILED(hr)) + { + delete pConnection->m_pGeodatabase; + pConnection->m_pGeodatabase = NULL; + pConnection->SetLocked(FALSE); + Release(osName); + GDBErr(hr, "Failed to re-open Geodatabase. Dataset should be closed"); + return OGRERR_FAILURE; + } + + FGdbDataSource* pDS = new FGdbDataSource(this, pConnection); + pDS->Open(osName, TRUE); + poDSInOut = new OGRMutexedDataSource(pDS, TRUE, hMutex, TRUE); + + pConnection->SetLocked(FALSE); + + return eErr; +} + +/***********************************************************************/ +/* Release() */ +/***********************************************************************/ + +void FGdbDriver::Release(const char* pszName) +{ + CPLMutexHolderOptionalLockD(hMutex); + + FGdbDatabaseConnection* pConnection = oMapConnections[pszName]; + if( pConnection != NULL ) + { + pConnection->m_nRefCount --; + CPLDebug("FileGDB", "ref_count of %s = %d now", pszName, + pConnection->m_nRefCount); + if( pConnection->m_nRefCount == 0 ) + { + if( pConnection->m_pGeodatabase != NULL ) + { + CPLDebug("FileGDB", "Really closing %s now", pszName); + ::CloseGeodatabase(*(pConnection->m_pGeodatabase)); + delete pConnection->m_pGeodatabase; + pConnection->m_pGeodatabase = NULL; + } + delete pConnection; + oMapConnections.erase(pszName); + } + } +} + +/***********************************************************************/ +/* TestCapability() */ +/***********************************************************************/ + +int FGdbDriver::TestCapability( const char * pszCap ) +{ + if (EQUAL(pszCap, ODrCCreateDataSource) ) + return TRUE; + + else if (EQUAL(pszCap, ODrCDeleteDataSource) ) + return TRUE; + + return FALSE; +} +/************************************************************************/ +/* DeleteDataSource() */ +/************************************************************************/ + +OGRErr FGdbDriver::DeleteDataSource( const char *pszDataSource ) +{ + CPLMutexHolderD(&hMutex); + + std::wstring wstr = StringToWString(pszDataSource); + + long hr; + + if (S_OK != (hr = ::DeleteGeodatabase(wstr))) + { + GDBErr(hr, "Failed to delete Geodatabase"); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/***********************************************************************/ +/* RegisterOGRFileGDB() */ +/***********************************************************************/ + +void RegisterOGRFileGDB() + +{ + if (! GDAL_CHECK_VERSION("OGR FGDB")) + return; + OGRSFDriver* poDriver = new FGdbDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ESRI FileGDB" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gdb" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_filegdb.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, "" ); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" " +""); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Real String Date DateTime Binary" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_DEFAULT_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES" ); + + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbLayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbLayer.cpp new file mode 100644 index 000000000..e68936b38 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbLayer.cpp @@ -0,0 +1,2869 @@ +/****************************************************************************** +* $Id: FGdbLayer.cpp 29330 2015-06-14 12:11:11Z rouault $ +* +* Project: OpenGIS Simple Features Reference Implementation +* Purpose: Implements FileGDB OGR layer. +* Author: Ragi Yaser Burhum, ragi@burhum.com +* Paul Ramsey, pramsey at cleverelephant.ca +* +****************************************************************************** +* Copyright (c) 2010, Ragi Yaser Burhum +* Copyright (c) 2011, Paul Ramsey + * Copyright (c) 2011-2014, Even Rouault +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +****************************************************************************/ + +#include "ogr_fgdb.h" +#include "ogrpgeogeometry.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "FGdbUtils.h" +#include "cpl_minixml.h" // the only way right now to extract schema information + +CPL_CVSID("$Id: FGdbLayer.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +using std::string; +using std::wstring; + +/************************************************************************/ +/* FGdbBaseLayer() */ +/************************************************************************/ +FGdbBaseLayer::FGdbBaseLayer() : + m_pFeatureDefn(NULL), m_pSRS(NULL), m_pEnumRows(NULL), + m_suppressColumnMappingError(false), m_forceMulti(false) +{ +} + +/************************************************************************/ +/* ~FGdbBaseLayer() */ +/************************************************************************/ +FGdbBaseLayer::~FGdbBaseLayer() +{ + if (m_pFeatureDefn) + { + m_pFeatureDefn->Release(); + m_pFeatureDefn = NULL; + } + + if (m_pEnumRows) + { + delete m_pEnumRows; + m_pEnumRows = NULL; + } + + if (m_pSRS) + { + m_pSRS->Release(); + m_pSRS = NULL; + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature* FGdbBaseLayer::GetNextFeature() +{ + while (1) //want to skip errors + { + if (m_pEnumRows == NULL) + return NULL; + + long hr; + + Row row; + + if (FAILED(hr = m_pEnumRows->Next(row))) + { + GDBErr(hr, "Failed fetching features"); + return NULL; + } + + if (hr != S_OK) + { + // It's OK, we are done fetching - failure is catched by FAILED macro + return NULL; + } + + OGRFeature* pOGRFeature = NULL; + + if (!OGRFeatureFromGdbRow(&row, &pOGRFeature)) + { + int32 oid = -1; + row.GetOID(oid); + + GDBErr(hr, CPLSPrintf("Failed translating FGDB row [%d] to OGR Feature", oid)); + + //return NULL; + continue; //skip feature + } + + return pOGRFeature; + } +} + +/************************************************************************/ +/* FGdbLayer() */ +/************************************************************************/ +FGdbLayer::FGdbLayer(): + m_pDS(NULL), m_pTable(NULL), m_wstrSubfields(L"*"), m_pOGRFilterGeometry(NULL), + m_bFilterDirty(true), + m_bLaunderReservedKeywords(true) +{ + m_bBulkLoadAllowed = -1; /* uninitialized */ + m_bBulkLoadInProgress = FALSE; + m_pEnumRows = new EnumRows; + +#ifdef EXTENT_WORKAROUND + m_bLayerEnvelopeValid = false; + m_bLayerJustCreated = false; +#endif + m_papszOptions = NULL; + m_bCreateMultipatch = FALSE; +} + +/************************************************************************/ +/* ~FGdbLayer() */ +/************************************************************************/ + +FGdbLayer::~FGdbLayer() +{ + EndBulkLoad(); + +#ifdef EXTENT_WORKAROUND + WorkAroundExtentProblem(); +#endif + + // NOTE: never delete m_pDS - the memory doesn't belong to us + // TODO: check if we need to close the table or if the destructor + // takes care of closing as it should + if (m_pTable) + { + delete m_pTable; + m_pTable = NULL; + } + + if (m_pOGRFilterGeometry) + { + OGRGeometryFactory::destroyGeometry(m_pOGRFilterGeometry); + m_pOGRFilterGeometry = NULL; + } + + for(size_t i = 0; i < m_apoByteArrays.size(); i++ ) + delete m_apoByteArrays[i]; + + CSLDestroy(m_papszOptions); +} + +#ifdef EXTENT_WORKAROUND + +/************************************************************************/ +/* UpdateRowWithGeometry() */ +/************************************************************************/ + +bool FGdbLayer::UpdateRowWithGeometry(Row& row, OGRGeometry* poGeom) +{ + ShapeBuffer shape; + long hr; + + /* Write geometry to a buffer */ + GByte *pabyShape = NULL; + int nShapeSize = 0; + if ( OGRWriteToShapeBin( poGeom, &pabyShape, &nShapeSize ) != OGRERR_NONE ) + return false; + + /* Copy it into a ShapeBuffer */ + if ( nShapeSize > 0 ) + { + shape.Allocate(nShapeSize); + memcpy(shape.shapeBuffer, pabyShape, nShapeSize); + shape.inUseLength = nShapeSize; + } + + /* Free the shape buffer */ + CPLFree(pabyShape); + + /* Write ShapeBuffer into the Row */ + hr = row.SetGeometry(shape); + if (FAILED(hr)) + { + return false; + } + + /* Update row */ + hr = m_pTable->Update(row); + if (FAILED(hr)) + { + return false; + } + + return true; +} + +/************************************************************************/ +/* WorkAroundExtentProblem() */ +/* */ +/* Work-around problem with FileGDB API 1.1 on Linux 64bit. See #4455 */ +/************************************************************************/ + +void FGdbLayer::WorkAroundExtentProblem() +{ + if (!m_bLayerJustCreated || !m_bLayerEnvelopeValid) + return; + + OGREnvelope sEnvelope; + if (GetExtent(&sEnvelope, TRUE) != OGRERR_NONE) + return; + + /* The characteristic of the bug is that the reported extent */ + /* is the real extent truncated incorrectly to integer values */ + /* We work around that by temporary updating one feature with a geometry */ + /* whose coordinates are integer values but ceil'ed and floor'ed */ + /* such that they include the real layer extent. */ + if (((double)(int)sEnvelope.MinX == sEnvelope.MinX && + (double)(int)sEnvelope.MinY == sEnvelope.MinY && + (double)(int)sEnvelope.MaxX == sEnvelope.MaxX && + (double)(int)sEnvelope.MaxY == sEnvelope.MaxY) && + (fabs(sEnvelope.MinX - sLayerEnvelope.MinX) > 1e-5 || + fabs(sEnvelope.MinY - sLayerEnvelope.MinY) > 1e-5 || + fabs(sEnvelope.MaxX - sLayerEnvelope.MaxX) > 1e-5 || + fabs(sEnvelope.MaxY - sLayerEnvelope.MaxY) > 1e-5)) + { + long hr; + Row row; + EnumRows enumRows; + + if (FAILED(hr = m_pTable->Search(StringToWString("*"), StringToWString(""), true, enumRows))) + return; + + if (FAILED(hr = enumRows.Next(row))) + return; + + if (hr != S_OK) + return; + + /* Backup original shape buffer */ + ShapeBuffer originalGdbGeometry; + if (FAILED(hr = row.GetGeometry(originalGdbGeometry))) + return; + + OGRGeometry* pOGRGeo = NULL; + if ((!GDBGeometryToOGRGeometry(m_forceMulti, &originalGdbGeometry, m_pSRS, &pOGRGeo)) || pOGRGeo == NULL) + { + delete pOGRGeo; + return; + } + + OGRwkbGeometryType eType = wkbFlatten(pOGRGeo->getGeometryType()); + + delete pOGRGeo; + pOGRGeo = NULL; + + OGRPoint oP1(floor(sLayerEnvelope.MinX), floor(sLayerEnvelope.MinY)); + OGRPoint oP2(ceil(sLayerEnvelope.MaxX), ceil(sLayerEnvelope.MaxY)); + + OGRLinearRing oLR; + oLR.addPoint(&oP1); + oLR.addPoint(&oP2); + oLR.addPoint(&oP1); + + if ( eType == wkbPoint ) + { + UpdateRowWithGeometry(row, &oP1); + UpdateRowWithGeometry(row, &oP2); + } + else if ( eType == wkbLineString ) + { + UpdateRowWithGeometry(row, &oLR); + } + else if ( eType == wkbPolygon ) + { + OGRPolygon oPoly; + oPoly.addRing(&oLR); + + UpdateRowWithGeometry(row, &oPoly); + } + else if ( eType == wkbMultiPoint ) + { + OGRMultiPoint oColl; + oColl.addGeometry(&oP1); + oColl.addGeometry(&oP2); + + UpdateRowWithGeometry(row, &oColl); + } + else if ( eType == wkbMultiLineString ) + { + OGRMultiLineString oColl; + oColl.addGeometry(&oLR); + + UpdateRowWithGeometry(row, &oColl); + } + else if ( eType == wkbMultiPolygon ) + { + OGRMultiPolygon oColl; + OGRPolygon oPoly; + oPoly.addRing(&oLR); + oColl.addGeometry(&oPoly); + + UpdateRowWithGeometry(row, &oColl); + } + else + return; + + /* Restore original ShapeBuffer */ + hr = row.SetGeometry(originalGdbGeometry); + if (FAILED(hr)) + return; + + /* Update Row */ + hr = m_pTable->Update(row); + if (FAILED(hr)) + return; + + CPLDebug("FGDB", "Workaround extent problem with Linux 64bit FGDB SDK 1.1"); + } +} +#endif // EXTENT_WORKAROUND + +/************************************************************************/ +/* ICreateFeature() */ +/* Create an FGDB Row and populate it from an OGRFeature. */ +/* */ +/************************************************************************/ + +OGRErr FGdbLayer::ICreateFeature( OGRFeature *poFeature ) +{ + Table *fgdb_table = m_pTable; + Row fgdb_row; + fgdbError hr; + + if( !m_pDS->GetUpdate() ) + return OGRERR_FAILURE; + + if (m_bBulkLoadAllowed < 0) + m_bBulkLoadAllowed = CSLTestBoolean(CPLGetConfigOption("FGDB_BULK_LOAD", "NO")); + + if (m_bBulkLoadAllowed && !m_bBulkLoadInProgress) + StartBulkLoad(); + + hr = fgdb_table->CreateRowObject(fgdb_row); + + /* Check the status of the Row create */ + if (FAILED(hr)) + { + GDBErr(hr, "Failed at creating Row in CreateFeature."); + return OGRERR_FAILURE; + } + + /* As we have issues with fixed values for dates, or CURRENT_xxxx isn't */ + /* handled anyway, let's fill ourselves all unset fields with their default */ + poFeature->FillUnsetWithDefault(FALSE, NULL); + + /* Populate the row with the feature content */ + if (PopulateRowWithFeature(fgdb_row, poFeature) != OGRERR_NONE) + return OGRERR_FAILURE; + + /* Cannot write to FID field - it is managed by GDB*/ + //std::wstring wfield_name = StringToWString(m_strOIDFieldName); + //hr = fgdb_row.SetInteger(wfield_name, poFeature->GetFID()); + + /* Write the row to the table */ + hr = fgdb_table->Insert(fgdb_row); + if (FAILED(hr)) + { + GDBErr(hr, "Failed at writing Row to Table in CreateFeature."); + return OGRERR_FAILURE; + } + + int32 oid = -1; + if (!FAILED(hr = fgdb_row.GetOID(oid))) + { + poFeature->SetFID(oid); + } + +#ifdef EXTENT_WORKAROUND + /* For WorkAroundExtentProblem() needs */ + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if ( m_bLayerJustCreated && poGeom != NULL && !poGeom->IsEmpty() ) + { + OGREnvelope sFeatureGeomEnvelope; + poGeom->getEnvelope(&sFeatureGeomEnvelope); + if (!m_bLayerEnvelopeValid) + { + memcpy(&sLayerEnvelope, &sFeatureGeomEnvelope, sizeof(sLayerEnvelope)); + m_bLayerEnvelopeValid = true; + } + else + { + sLayerEnvelope.Merge(sFeatureGeomEnvelope); + } + } +#endif + + return OGRERR_NONE; +} + +/************************************************************************/ +/* PopulateRowWithFeature() */ +/* */ +/************************************************************************/ + +OGRErr FGdbLayer::PopulateRowWithFeature( Row& fgdb_row, OGRFeature *poFeature ) +{ + ShapeBuffer shape; + fgdbError hr; + + OGRFeatureDefn* poFeatureDefn = m_pFeatureDefn; + int nFieldCount = poFeatureDefn->GetFieldCount(); + + /* Copy the OGR visible fields (everything except geometry and FID) */ + int nCountBinaryField = 0; + for( int i = 0; i < nFieldCount; i++ ) + { + std::string field_name = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + std::wstring wfield_name = StringToWString(field_name); + const std::string & strFieldType = m_vOGRFieldToESRIFieldType[i]; + + /* Set empty fields to NULL */ + if( !poFeature->IsFieldSet( i ) ) + { + if( strFieldType == "esriFieldTypeGlobalID" ) + continue; + + if (FAILED(hr = fgdb_row.SetNull(wfield_name))) + { + GDBErr(hr, "Failed setting field to NULL."); + return OGRERR_FAILURE; + } + continue; + } + + /* Set the information using the appropriate FGDB function */ + int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType(); + + if ( nOGRFieldType == OFTInteger ) + { + int fldvalue = poFeature->GetFieldAsInteger(i); + if( strFieldType == "esriFieldTypeInteger" ) + hr = fgdb_row.SetInteger(wfield_name, fldvalue); + else + { + if( fldvalue < -32768 || fldvalue > 32767 ) + { + static int bHasWarned = FALSE; + if( !bHasWarned ) + { + bHasWarned = TRUE; + CPLError(CE_Warning, CPLE_NotSupported, + "Value %d for field %s does not fit into a short and will be clamped. " + "This warning will not be emitted any more", + fldvalue, field_name.c_str()); + } + if( fldvalue < -32768 ) + fldvalue = -32768; + else + fldvalue = 32767; + } + hr = fgdb_row.SetShort(wfield_name, (short) fldvalue); + } + } + else if ( nOGRFieldType == OFTReal || nOGRFieldType == OFTInteger64 ) + { + /* Doubles (we don't handle FGDB Floats) */ + double fldvalue = poFeature->GetFieldAsDouble(i); + if( strFieldType == "esriFieldTypeDouble" ) + hr = fgdb_row.SetDouble(wfield_name, fldvalue); + else + hr = fgdb_row.SetFloat(wfield_name, (float) fldvalue); + } + else if ( nOGRFieldType == OFTString ) + { + /* Strings we convert to wstring */ + std::string fldvalue = poFeature->GetFieldAsString(i); + if( strFieldType == "esriFieldTypeString" ) + { + std::wstring wfldvalue = StringToWString(fldvalue); + hr = fgdb_row.SetString(wfield_name, wfldvalue); + } + // Apparently, esriFieldTypeGlobalID can not be set, but is + // initialized by the FileGDB SDK itself. + else if( strFieldType == "esriFieldTypeGUID" /*|| + strFieldType == "esriFieldTypeGlobalID" */ ) + { + Guid guid; + std::wstring wfldvalue = StringToWString(fldvalue); + if( FAILED(hr = guid.FromString(wfldvalue)) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot parse GUID value %s for field %s.", + fldvalue.c_str(), field_name.c_str() ); + } + else + { + hr = fgdb_row.SetGUID(wfield_name, guid); + } + } + else if( strFieldType == "esriFieldTypeXML" ) + { + hr = fgdb_row.SetXML(wfield_name, fldvalue); + } + else + hr = 0; + } + else if ( nOGRFieldType == OFTDateTime || nOGRFieldType == OFTDate ) + { + /* Dates we need to coerce a little */ + struct tm val; + poFeature->GetFieldAsDateTime(i, &(val.tm_year), &(val.tm_mon), &(val.tm_mday), + &(val.tm_hour), &(val.tm_min), &(val.tm_sec), NULL); + val.tm_year -= 1900; + val.tm_mon = val.tm_mon - 1; /* OGR months go 1-12, FGDB go 0-11 */ + hr = fgdb_row.SetDate(wfield_name, val); + } + else if ( nOGRFieldType == OFTBinary ) + { + /* Binary data */ + int bytesize; + GByte *bytes = poFeature->GetFieldAsBinary(i, &bytesize); + if ( bytesize ) + { + /* This is annoying but SetBinary() doesn't keep the binary */ + /* content. The ByteArray object must still be alive at */ + /* the time Insert() is called */ + m_apoByteArrays[nCountBinaryField]->Allocate(bytesize); + memcpy(m_apoByteArrays[nCountBinaryField]->byteArray, bytes, bytesize); + m_apoByteArrays[nCountBinaryField]->inUseLength = bytesize; + hr = fgdb_row.SetBinary(wfield_name, *(m_apoByteArrays[nCountBinaryField])); + } + else + { + hr = fgdb_row.SetNull(wfield_name); + } + nCountBinaryField ++; + } + else + { + /* We can't handle this type */ + CPLError( CE_Failure, CPLE_AppDefined, + "FGDB driver does not support OGR type." ); + return OGRERR_FAILURE; + } + + if (FAILED(hr)) + { + CPLError(CE_Warning, CPLE_AppDefined, "Cannot set value for field %s", + field_name.c_str()); + } + } + + if ( m_pFeatureDefn->GetGeomType() != wkbNone ) + { + /* Done with attribute fields, now do geometry */ + OGRGeometry *poGeom = poFeature->GetGeometryRef(); + + if (poGeom == NULL || poGeom->IsEmpty()) + { + /* EMPTY geometries should be treated as NULL, see #4832 */ + hr = fgdb_row.SetNull(StringToWString(m_strShapeFieldName)); + if (FAILED(hr)) + { + GDBErr(hr, "Failed at writing EMPTY Geometry to Row in CreateFeature."); + return OGRERR_FAILURE; + } + } + else + { + /* Write geometry to a buffer */ + GByte *pabyShape = NULL; + int nShapeSize = 0; + OGRErr err; + + if( m_bCreateMultipatch && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ) + { + err = OGRWriteMultiPatchToShapeBin( poGeom, &pabyShape, &nShapeSize ); + } + else + { + err = OGRWriteToShapeBin( poGeom, &pabyShape, &nShapeSize ); + } + if ( err != OGRERR_NONE ) + return err; + + /* Copy it into a ShapeBuffer */ + if ( nShapeSize > 0 ) + { + shape.Allocate(nShapeSize); + memcpy(shape.shapeBuffer, pabyShape, nShapeSize); + shape.inUseLength = nShapeSize; + } + + /* Free the shape buffer */ + CPLFree(pabyShape); + + /* Write ShapeBuffer into the Row */ + hr = fgdb_row.SetGeometry(shape); + if (FAILED(hr)) + { + GDBErr(hr, "Failed at writing Geometry to Row in CreateFeature."); + return OGRERR_FAILURE; + } + } + } + + return OGRERR_NONE; + +} + +/************************************************************************/ +/* GetRow() */ +/************************************************************************/ + +OGRErr FGdbLayer::GetRow( EnumRows& enumRows, Row& row, GIntBig nFID ) +{ + long hr; + CPLString osQuery; + + /* Querying a 64bit FID causes a runtime exception in FileGDB... */ + if( (GIntBig)(int)nFID != nFID ) + { + return OGRERR_FAILURE; + } + + osQuery.Printf("%s = " CPL_FRMT_GIB, m_strOIDFieldName.c_str(), nFID); + + if (FAILED(hr = m_pTable->Search(m_wstrSubfields, StringToWString(osQuery.c_str()), true, enumRows))) + { + GDBErr(hr, "Failed fetching row "); + return OGRERR_FAILURE; + } + + if (FAILED(hr = enumRows.Next(row))) + { + GDBErr(hr, "Failed fetching row "); + return OGRERR_FAILURE; + } + + if (hr != S_OK) + return OGRERR_NON_EXISTING_FEATURE; //none found - but no failure + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr FGdbLayer::DeleteFeature( GIntBig nFID ) + +{ + long hr; + EnumRows enumRows; + Row row; + + if( !m_pDS->GetUpdate() ) + return OGRERR_FAILURE; + + EndBulkLoad(); + + OGRErr eErr = GetRow(enumRows, row, nFID); + if( eErr != OGRERR_NONE) + return eErr; + + if (FAILED(hr = m_pTable->Delete(row))) + { + GDBErr(hr, "Failed deleting row "); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr FGdbLayer::ISetFeature( OGRFeature* poFeature ) + +{ + long hr; + EnumRows enumRows; + Row row; + + if( !m_pDS->GetUpdate() ) + return OGRERR_FAILURE; + + if( poFeature->GetFID() == OGRNullFID ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SetFeature() with unset FID fails." ); + return OGRERR_FAILURE; + } + + EndBulkLoad(); + + OGRErr eErr = GetRow(enumRows, row, poFeature->GetFID()); + if( eErr != OGRERR_NONE) + return eErr; + + /* Populate the row with the feature content */ + if (PopulateRowWithFeature(row, poFeature) != OGRERR_NONE) + return OGRERR_FAILURE; + + if (FAILED(hr = m_pTable->Update(row))) + { + GDBErr(hr, "Failed updating row "); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateFieldDefn() */ +/************************************************************************/ + +char* FGdbLayer::CreateFieldDefn(OGRFieldDefn& oField, + int bApproxOK, + std::string& fieldname_clean, + std::string& gdbFieldType) +{ + std::string fieldname = oField.GetNameRef(); + std::string fidname = std::string(GetFIDColumn()); + std::string nullable = (oField.IsNullable()) ? "true" : "false"; + + /* Try to map the OGR type to an ESRI type */ + OGRFieldType fldtype = oField.GetType(); + if ( ! OGRToGDBFieldType(fldtype, oField.GetSubType(), &gdbFieldType) ) + { + GDBErr(-1, "Failed converting field type."); + return NULL; + } + + if( oField.GetType() == OFTInteger64 && !bApproxOK ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Integer64 not supported in FileGDB"); + return NULL; + } + + const char* pszColumnTypes = CSLFetchNameValue(m_papszOptions, "COLUMN_TYPES"); + if( pszColumnTypes != NULL ) + { + char** papszTokens = CSLTokenizeString2(pszColumnTypes, ",", 0); + const char* pszFieldType = CSLFetchNameValue(papszTokens, fieldname.c_str()); + if( pszFieldType != NULL ) + { + OGRFieldType fldtypeCheck; + OGRFieldSubType eSubType; + if( GDBToOGRFieldType(pszFieldType, &fldtypeCheck, &eSubType) ) + { + if( fldtypeCheck != fldtype ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Ignoring COLUMN_TYPES=%s=%s : %s not consistent with OGR data type", + fieldname.c_str(), pszFieldType, pszFieldType); + } + else + gdbFieldType = pszFieldType; + } + else + CPLError(CE_Warning, CPLE_AppDefined, "Ignoring COLUMN_TYPES=%s=%s : %s not recognized", + fieldname.c_str(), pszFieldType, pszFieldType); + } + CSLDestroy(papszTokens); + } + + if (fieldname_clean.size() != 0) + { + oField.SetName(fieldname_clean.c_str()); + } + else + { + /* Clean field names */ + fieldname_clean = FGDBLaunderName(fieldname); + + if (m_bLaunderReservedKeywords) + fieldname_clean = FGDBEscapeReservedKeywords(fieldname_clean); + + /* Truncate to 64 characters */ + if (fieldname_clean.size() > 64) + fieldname_clean.resize(64); + + std::string temp_fieldname = fieldname_clean; + + /* Ensures uniqueness of field name */ + int numRenames = 1; + while ((m_pFeatureDefn->GetFieldIndex(temp_fieldname.c_str()) >= 0) && (numRenames < 10)) + { + temp_fieldname = CPLSPrintf("%s_%d", fieldname_clean.substr(0, 62).c_str(), numRenames); + numRenames ++; + } + while ((m_pFeatureDefn->GetFieldIndex(temp_fieldname.c_str()) >= 0) && (numRenames < 100)) + { + temp_fieldname = CPLSPrintf("%s_%d", fieldname_clean.substr(0, 61).c_str(), numRenames); + numRenames ++; + } + + if (temp_fieldname != fieldname) + { + if( !bApproxOK || (m_pFeatureDefn->GetFieldIndex(temp_fieldname.c_str()) >= 0) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Failed to add field named '%s'", + fieldname.c_str() ); + return NULL; + } + CPLError(CE_Warning, CPLE_NotSupported, + "Normalized/laundered field name: '%s' to '%s'", + fieldname.c_str(), temp_fieldname.c_str()); + + fieldname_clean = temp_fieldname; + oField.SetName(fieldname_clean.c_str()); + } + } + + /* Then the Field definition */ + CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:Field"); + + /* Add the XML attributes to the Field node */ + FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema"); + FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1"); + FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:Field"); + + /* Basic field information */ + CPLCreateXMLElementAndValue(defn_xml, "Name", fieldname_clean.c_str()); + CPLCreateXMLElementAndValue(defn_xml, "Type", gdbFieldType.c_str()); + CPLCreateXMLElementAndValue(defn_xml, "IsNullable", nullable.c_str()); + + /* Get the Width and Precision if we know them */ + int width = oField.GetWidth(); + int precision = oField.GetPrecision(); + if ( width <= 0 ) + GDBFieldTypeToWidthPrecision(gdbFieldType, &width, &precision); + + /* Write out the Width and Precision */ + char buf[100]; + snprintf(buf, 100, "%d", width); + CPLCreateXMLElementAndValue(defn_xml,"Length", buf); + snprintf(buf, 100, "%d", precision); + CPLCreateXMLElementAndValue(defn_xml,"Precision", buf); + + /* We know nothing about Scale, so zero it out */ + CPLCreateXMLElementAndValue(defn_xml,"Scale", "0"); + + /* Attempt to preserve the original fieldname */ + if (fieldname != fieldname_clean) + { + CPLCreateXMLElementAndValue(defn_xml, "AliasName", fieldname.c_str()); + } + + if( oField.GetDefault() != NULL ) + { + const char* pszDefault = oField.GetDefault(); + /*int nYear, nMonth, nDay, nHour, nMinute; + float fSecond;*/ + if( oField.GetType() == OFTString ) + { + CPLString osVal = pszDefault; + if( osVal[0] == '\'' && osVal[osVal.size()-1] == '\'' ) + { + osVal = osVal.substr(1); + osVal.resize(osVal.size()-1); + char* pszTmp = CPLUnescapeString(osVal, NULL, CPLES_SQL); + osVal = pszTmp; + CPLFree(pszTmp); + } + CPLXMLNode* psDefaultValue = + CPLCreateXMLElementAndValue(defn_xml, "DefaultValue", osVal); + FGDB_CPLAddXMLAttribute(psDefaultValue, "xsi:type", "xs:string"); + } + else if( oField.GetType() == OFTInteger && + !EQUAL(gdbFieldType.c_str(), "esriFieldTypeSmallInteger") && + CPLGetValueType(pszDefault) == CPL_VALUE_INTEGER ) + { + CPLXMLNode* psDefaultValue = + CPLCreateXMLElementAndValue(defn_xml, "DefaultValue", pszDefault); + FGDB_CPLAddXMLAttribute(psDefaultValue, "xsi:type", "xs:int"); + } + else if( oField.GetType() == OFTReal && + !EQUAL(gdbFieldType.c_str(), "esriFieldTypeSingle") && + CPLGetValueType(pszDefault) != CPL_VALUE_STRING ) + { + CPLXMLNode* psDefaultValue = + CPLCreateXMLElementAndValue(defn_xml, "DefaultValue", pszDefault); + FGDB_CPLAddXMLAttribute(psDefaultValue, "xsi:type", "xs:double"); + } + /*else if( oField.GetType() == OFTDateTime && + sscanf(pszDefault, "'%d/%d/%d %d:%d:%f'", &nYear, &nMonth, &nDay, + &nHour, &nMinute, &fSecond) == 6 ) + { + CPLXMLNode* psDefaultValue = + CPLCreateXMLElementAndValue(defn_xml, "DefaultValue", + CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d", + nYear, nMonth, nDay, nHour, nMinute, (int)(fSecond + 0.5))); + FGDB_CPLAddXMLAttribute(psDefaultValue, "xsi:type", "xs:dateTime"); + }*/ + } + /* afternoon */ + + /* Convert our XML tree into a string for FGDB */ + char *defn_str = CPLSerializeXMLTree(defn_xml); + CPLDebug("FGDB", "CreateField() generated XML for FGDB\n%s", defn_str); + + /* Free the XML */ + CPLDestroyXMLNode(defn_xml); + + return defn_str; +} + +/************************************************************************/ +/* CreateField() */ +/* Build up an FGDB XML field definition and use it to create a Field */ +/* Update the OGRFeatureDefn to reflect the new field. */ +/* */ +/************************************************************************/ + +OGRErr FGdbLayer::CreateField(OGRFieldDefn* poField, int bApproxOK) +{ + OGRFieldDefn oField(poField); + std::string fieldname_clean; + std::string gdbFieldType; + + if( !m_pDS->GetUpdate() ) + return OGRERR_FAILURE; + + char* defn_str = CreateFieldDefn(oField, bApproxOK, + fieldname_clean, gdbFieldType); + if (defn_str == NULL) + return OGRERR_FAILURE; + + /* Add the FGDB Field to the FGDB Table. */ + fgdbError hr = m_pTable->AddField(defn_str); + + CPLFree(defn_str); + + /* Check the status of the Field add */ + if (FAILED(hr)) + { + GDBErr(hr, "Failed at creating Field for " + std::string(oField.GetNameRef())); + return OGRERR_FAILURE; + } + + /* Now add the OGRFieldDefn to the OGRFeatureDefn */ + m_pFeatureDefn->AddFieldDefn(&oField); + + m_vOGRFieldToESRIField.push_back(StringToWString(fieldname_clean)); + m_vOGRFieldToESRIFieldType.push_back( gdbFieldType ); + + if( oField.GetType() == OFTBinary ) + m_apoByteArrays.push_back(new ByteArray()); + + /* All done and happy */ + return OGRERR_NONE; + +} + +/************************************************************************/ +/* DeleteField() */ +/************************************************************************/ + +OGRErr FGdbLayer::DeleteField( int iFieldToDelete ) +{ + + if( !m_pDS->GetUpdate() ) + return OGRERR_FAILURE; + + if (iFieldToDelete < 0 || iFieldToDelete >= m_pFeatureDefn->GetFieldCount()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + + ResetReading(); + + const char* pszFieldName = m_pFeatureDefn->GetFieldDefn(iFieldToDelete)->GetNameRef(); + + fgdbError hr; + if (FAILED(hr = m_pTable->DeleteField(StringToWString(pszFieldName)))) + { + GDBErr(hr, "Failed deleting field " + std::string(pszFieldName)); + return OGRERR_FAILURE; + } + + m_vOGRFieldToESRIField.erase (m_vOGRFieldToESRIField.begin() + iFieldToDelete); + m_vOGRFieldToESRIFieldType.erase( m_vOGRFieldToESRIFieldType.begin() + iFieldToDelete ); + + + return m_pFeatureDefn->DeleteFieldDefn( iFieldToDelete ); +} + +#ifdef AlterFieldDefn_implemented_but_not_working + +/************************************************************************/ +/* AlterFieldDefn() */ +/************************************************************************/ + +OGRErr FGdbLayer::AlterFieldDefn( int iFieldToAlter, OGRFieldDefn* poNewFieldDefn, int nFlags ) +{ + + if( !m_pDS->GetUpdate() ) + return OGRERR_FAILURE; + + if (iFieldToAlter < 0 || iFieldToAlter >= m_pFeatureDefn->GetFieldCount()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + + OGRFieldDefn* poFieldDefn = m_pFeatureDefn->GetFieldDefn(iFieldToAlter); + OGRFieldDefn oField(poFieldDefn); + + if (nFlags & ALTER_TYPE_FLAG) + oField.SetType(poNewFieldDefn->GetType()); + if (nFlags & ALTER_NAME_FLAG) + { + if (strcmp(poNewFieldDefn->GetNameRef(), oField.GetNameRef()) != 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Altering field name is not supported" ); + return OGRERR_FAILURE; + } + oField.SetName(poNewFieldDefn->GetNameRef()); + } + if (nFlags & ALTER_WIDTH_PRECISION_FLAG) + { + oField.SetWidth(poNewFieldDefn->GetWidth()); + oField.SetPrecision(poNewFieldDefn->GetPrecision()); + } + + std::string fieldname_clean = WStringToString(m_vOGRFieldToESRIField[iFieldToAlter]); + std::string gdbFieldType; + + char* defn_str = CreateFieldDefn(oField, TRUE, + fieldname_clean, gdbFieldType); + if (defn_str == NULL) + return OGRERR_FAILURE; + + ResetReading(); + + /* Add the FGDB Field to the FGDB Table. */ + fgdbError hr = m_pTable->AlterField(defn_str); + + CPLFree(defn_str); + + /* Check the status of the AlterField */ + if (FAILED(hr)) + { + GDBErr(hr, "Failed at altering field " + std::string(oField.GetNameRef())); + return OGRERR_FAILURE; + } + + m_vOGRFieldToESRIFieldType[iFieldToAlter] = gdbFieldType; + + poFieldDefn->SetType(oField.GetType()); + poFieldDefn->SetWidth(oField.GetWidth()); + poFieldDefn->SetPrecision(oField.GetPrecision()); + + return OGRERR_NONE; +} +#endif // AlterFieldDefn_implemented_but_not_working + +/************************************************************************/ +/* XMLSpatialReference() */ +/* Build up an XML representation of an OGRSpatialReference. */ +/* Used in layer creation. */ +/* */ +/************************************************************************/ + +CPLXMLNode* XMLSpatialReference(OGRSpatialReference* poSRS, char** papszOptions) +{ + /* We always need a SpatialReference */ + CPLXMLNode *srs_xml = CPLCreateXMLNode(NULL, CXT_Element, "SpatialReference"); + + /* Extract the WKID before morphing */ + int nSRID = 0; + if ( poSRS && poSRS->GetAuthorityCode(NULL) ) + { + nSRID = atoi(poSRS->GetAuthorityCode(NULL)); + } + + /* NULL poSRS => UnknownCoordinateSystem */ + if ( ! poSRS ) + { + FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:UnknownCoordinateSystem"); + } + else + { + /* Set the SpatialReference type attribute correctly for GEOGCS/PROJCS */ + if ( poSRS->IsProjected() ) + FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:ProjectedCoordinateSystem"); + else + FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:GeographicCoordinateSystem"); + + /* Add the WKT to the XML */ + SpatialReferenceInfo oESRI_SRS; + + /* Do we have a known SRID ? If so, directly query the ESRI SRS DB */ + if( nSRID && SpatialReferences::FindSpatialReferenceBySRID(nSRID, oESRI_SRS) ) + { + CPLDebug("FGDB", + "Layer SRS has a SRID (%d). Using WKT from ESRI SRS DBFound perfect match. ", + nSRID); + CPLCreateXMLElementAndValue(srs_xml,"WKT", WStringToString(oESRI_SRS.srtext).c_str()); + } + else + { + /* Make a clone so we can morph it without morphing the original */ + OGRSpatialReference* poSRSClone = poSRS->Clone(); + + /* Flip the WKT to ESRI form, return UnknownCoordinateSystem if we can't */ + if ( poSRSClone->morphToESRI() != OGRERR_NONE ) + { + delete poSRSClone; + FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type", "esri:UnknownCoordinateSystem"); + return srs_xml; + } + + char *wkt = NULL; + poSRSClone->exportToWkt(&wkt); + if (wkt) + { + EnumSpatialReferenceInfo oEnumESRI_SRS; + std::vector oaiCandidateSRS; + nSRID = 0; + + /* Enumerate SRS from ESRI DB and find a match */ + while(TRUE) + { + if ( poSRS->IsProjected() ) + { + if( !oEnumESRI_SRS.NextProjectedSpatialReference(oESRI_SRS) ) + break; + } + else + { + if( !oEnumESRI_SRS.NextGeographicSpatialReference(oESRI_SRS) ) + break; + } + + std::string osESRI_WKT = WStringToString(oESRI_SRS.srtext); + const char* pszESRI_WKT = osESRI_WKT.c_str(); + if( strcmp(pszESRI_WKT, wkt) == 0 ) + { + /* Exact match found (not sure this case happens) */ + nSRID = oESRI_SRS.auth_srid; + break; + } + OGRSpatialReference oSRS_FromESRI; + if( oSRS_FromESRI.SetFromUserInput(pszESRI_WKT) == OGRERR_NONE && + poSRSClone->IsSame(&oSRS_FromESRI) ) + { + /* Potential match found */ + oaiCandidateSRS.push_back(oESRI_SRS.auth_srid); + } + } + + if( nSRID != 0 ) + { + CPLDebug("FGDB", + "Found perfect match in ESRI SRS DB " + "for layer SRS. SRID is %d", nSRID); + } + else if( oaiCandidateSRS.size() == 0 ) + { + CPLDebug("FGDB", + "Did not found a match in ESRI SRS DB for layer SRS. " + "Using morphed SRS WKT. Failure is to be expected"); + } + else if( oaiCandidateSRS.size() == 1 ) + { + nSRID = oaiCandidateSRS[0]; + if( SpatialReferences::FindSpatialReferenceBySRID( + nSRID, oESRI_SRS) ) + { + CPLDebug("FGDB", + "Found a single match in ESRI SRS DB " + "for layer SRS. SRID is %d", + nSRID); + nSRID = oESRI_SRS.auth_srid; + OGRFree(wkt); + wkt = CPLStrdup(WStringToString(oESRI_SRS.srtext).c_str()); + } + } + else + { + /* Not sure this case can happen */ + + CPLString osCandidateSRS; + for(int i=0; i<(int)oaiCandidateSRS.size() && i < 10; i++) + { + if( osCandidateSRS.size() ) + osCandidateSRS += ", "; + osCandidateSRS += CPLSPrintf("%d", oaiCandidateSRS[i]); + } + if(oaiCandidateSRS.size() > 10) + osCandidateSRS += "..."; + + CPLDebug("FGDB", + "As several candidates (%s) have been found in " + "ESRI SRS DB for layer SRS, none has been selected. " + "Using morphed SRS WKT. Failure is to be expected", + osCandidateSRS.c_str()); + } + + CPLCreateXMLElementAndValue(srs_xml,"WKT", wkt); + OGRFree(wkt); + } + + /* Dispose of our close */ + delete poSRSClone; + } + } + + /* Handle Origin/Scale/Tolerance */ + const char* grid[7] = { + "XOrigin", "YOrigin", "XYScale", + "ZOrigin", "ZScale", + "XYTolerance", "ZTolerance" }; + const char* gridvalues[7]; + + /* + Need different default parameters for geographic and projected coordinate systems. + Try and use ArcGIS 10 default values. + */ + // default tolerance is 1mm in the units of the coordinate system + double ztol = 0.001 * (poSRS ? poSRS->GetTargetLinearUnits("VERT_CS") : 1.0); + // default scale is 10x the tolerance + long zscale = 1 / ztol * 10; + + char s_xyscale[50], s_xytol[50], s_zscale[50], s_ztol[50]; + CPLsnprintf(s_ztol, 50, "%f", ztol); + snprintf(s_zscale, 50, "%ld", zscale); + + if ( poSRS == NULL || poSRS->IsProjected() ) + { + // default tolerance is 1mm in the units of the coordinate system + double xytol = 0.001 * (poSRS ? poSRS->GetTargetLinearUnits("PROJCS") : 1.0); + // default scale is 10x the tolerance + long xyscale = 1 / xytol * 10; + + CPLsnprintf(s_xytol, 50, "%f", xytol); + snprintf(s_xyscale, 50, "%ld", xyscale); + + // Ideally we would use the same X/Y origins as ArcGIS, but we need the algorithm they use. + gridvalues[0] = "-2147483647"; + gridvalues[1] = "-2147483647"; + gridvalues[2] = s_xyscale; + gridvalues[3] = "-100000"; + gridvalues[4] = s_zscale; + gridvalues[5] = s_xytol; + gridvalues[6] = s_ztol; + } + else + { + gridvalues[0] = "-400"; + gridvalues[1] = "-400"; + gridvalues[2] = "1000000000"; + gridvalues[3] = "-100000"; + gridvalues[4] = s_zscale; + gridvalues[5] = "0.000000008983153"; + gridvalues[6] = s_ztol; + } + + /* Convert any layer creation options available, use defaults otherwise */ + for( int i = 0; i < 7; i++ ) + { + if ( CSLFetchNameValue( papszOptions, grid[i] ) != NULL ) + gridvalues[i] = CSLFetchNameValue( papszOptions, grid[i] ); + + CPLCreateXMLElementAndValue(srs_xml, grid[i], gridvalues[i]); + } + + /* FGDB is always High Precision */ + CPLCreateXMLElementAndValue(srs_xml, "HighPrecision", "true"); + + /* Add the WKID to the XML */ + if ( nSRID ) + { + CPLCreateXMLElementAndValue(srs_xml, "WKID", CPLSPrintf("%d", nSRID)); + } + + return srs_xml; +} + +/************************************************************************/ +/* CreateFeatureDataset() */ +/************************************************************************/ + +bool FGdbLayer::CreateFeatureDataset(FGdbDataSource* pParentDataSource, + std::string feature_dataset_name, + OGRSpatialReference* poSRS, + char** papszOptions ) +{ + /* XML node */ + CPLXMLNode *xml_xml = CPLCreateXMLNode(NULL, CXT_Element, "?xml"); + FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0"); + FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8"); + + /* First build up a bare-bones feature definition */ + CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:DataElement"); + CPLAddXMLSibling(xml_xml, defn_xml); + + /* Add the attributes to the DataElement */ + FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema"); + FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1"); + + /* Need to set this to esri:DEFeatureDataset or esri:DETable */ + FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:DEFeatureDataset"); + + /* Add in more children */ + std::string catalog_page = "\\" + feature_dataset_name; + CPLCreateXMLElementAndValue(defn_xml,"CatalogPath", catalog_page.c_str()); + CPLCreateXMLElementAndValue(defn_xml,"Name", feature_dataset_name.c_str()); + CPLCreateXMLElementAndValue(defn_xml,"ChildrenExpanded", "false"); + CPLCreateXMLElementAndValue(defn_xml,"DatasetType", "esriDTFeatureDataset"); + CPLCreateXMLElementAndValue(defn_xml,"Versioned", "false"); + CPLCreateXMLElementAndValue(defn_xml,"CanVersion", "false"); + + /* Add in empty extent */ + CPLXMLNode *extent_xml = CPLCreateXMLNode(NULL, CXT_Element, "Extent"); + FGDB_CPLAddXMLAttribute(extent_xml, "xsi:nil", "true"); + CPLAddXMLChild(defn_xml, extent_xml); + + /* Add the SRS */ + if( TRUE ) // TODO: conditional on existence of SRS + { + CPLXMLNode *srs_xml = XMLSpatialReference(poSRS, papszOptions); + if ( srs_xml ) + CPLAddXMLChild(defn_xml, srs_xml); + } + + /* Convert our XML tree into a string for FGDB */ + char *defn_str = CPLSerializeXMLTree(xml_xml); + CPLDestroyXMLNode(xml_xml); + + /* TODO, tie this to debugging levels */ + CPLDebug("FGDB", "%s", defn_str); + + /* Create the FeatureDataset. */ + Geodatabase *gdb = pParentDataSource->GetGDB(); + fgdbError hr = gdb->CreateFeatureDataset(defn_str); + + /* Free the XML */ + CPLFree(defn_str); + + /* Check table create status */ + if (FAILED(hr)) + { + return GDBErr(hr, "Failed at creating FeatureDataset " + feature_dataset_name); + } + + return true; +} + +/************************************************************************/ +/* Create() */ +/* Build up an FGDB XML layer definition and use it to create a Table */ +/* or Feature Class to work from. */ +/* */ +/* Layer creation options: */ +/* FEATURE_DATASET, nest layer inside a FeatureDataset folder */ +/* GEOMETRY_NAME, user-selected name for the geometry column */ +/* FID/OID_NAME, user-selected name for the FID column */ +/* XORIGIN, YORIGIN, ZORIGIN, origin of the snapping grid */ +/* XYSCALE, ZSCALE, inverse resolution of the snapping grid */ +/* XYTOLERANCE, ZTOLERANCE, snapping tolerance for topology/networks */ +/* */ +/************************************************************************/ + +bool FGdbLayer::Create(FGdbDataSource* pParentDataSource, + const char* pszLayerNameIn, + OGRSpatialReference* poSRS, + OGRwkbGeometryType eType, + char** papszOptions) +{ + std::string parent_path = ""; + std::wstring wtable_path, wparent_path; + std::string geometry_name = FGDB_GEOMETRY_NAME; + std::string fid_name = FGDB_OID_NAME; + std::string esri_type; + bool has_z = false; + +#ifdef EXTENT_WORKAROUND + m_bLayerJustCreated = true; +#endif + + /* Launder the Layer name */ + std::string layerName = pszLayerNameIn; + + layerName = FGDBLaunderName(pszLayerNameIn); + layerName = FGDBEscapeReservedKeywords(layerName); + layerName = FGDBEscapeUnsupportedPrefixes(layerName); + + if (layerName.size() > 160) + layerName.resize(160); + + /* Ensures uniqueness of layer name */ + int numRenames = 1; + while ((pParentDataSource->GetLayerByName(layerName.c_str()) != NULL) && (numRenames < 10)) + { + layerName = CPLSPrintf("%s_%d", layerName.substr(0, 158).c_str(), numRenames); + numRenames ++; + } + while ((pParentDataSource->GetLayerByName(layerName.c_str()) != NULL) && (numRenames < 100)) + { + layerName = CPLSPrintf("%s_%d", layerName.substr(0, 157).c_str(), numRenames); + numRenames ++; + } + + if (layerName != pszLayerNameIn) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Normalized/laundered layer name: '%s' to '%s'", + pszLayerNameIn, layerName.c_str()); + } + + std::string table_path = "\\" + std::string(layerName); + + /* Handle the FEATURE_DATASET case */ + if ( CSLFetchNameValue( papszOptions, "FEATURE_DATASET") != NULL ) + { + std::string feature_dataset = CSLFetchNameValue( papszOptions, "FEATURE_DATASET"); + + /* Check if FEATURE_DATASET exists. Otherwise create it */ + std::vector featuredatasets; + Geodatabase *gdb = pParentDataSource->GetGDB(); + int bFeatureDataSetExists = FALSE; + fgdbError hr; + if ( !FAILED(hr = gdb->GetChildDatasets(L"\\", L"Feature Dataset", featuredatasets)) ) + { + std::wstring feature_dataset_with_slash = L"\\" + StringToWString(feature_dataset); + for ( unsigned int i = 0; i < featuredatasets.size(); i++ ) + { + if (featuredatasets[i] == feature_dataset_with_slash) + bFeatureDataSetExists = TRUE; + } + } + + if (!bFeatureDataSetExists) + { + bool rv = CreateFeatureDataset(pParentDataSource, feature_dataset, poSRS, papszOptions); + if ( ! rv ) + return rv; + } + + table_path = "\\" + feature_dataset + table_path; + parent_path = "\\" + feature_dataset; + } + + /* Convert table_path into wstring */ + wtable_path = StringToWString(table_path); + wparent_path = StringToWString(parent_path); + + /* Over-ride the geometry name if necessary */ + if ( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL ) + geometry_name = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME"); + + /* Over-ride the OID name if necessary */ + if ( CSLFetchNameValue( papszOptions, "FID") != NULL ) + fid_name = CSLFetchNameValue( papszOptions, "FID"); + else if ( CSLFetchNameValue( papszOptions, "OID_NAME") != NULL ) + fid_name = CSLFetchNameValue( papszOptions, "OID_NAME"); + + /* Figure out our geometry type */ + if ( eType != wkbNone ) + { + if ( wkbFlatten(eType) == wkbUnknown ) + { + return GDBErr(-1, "FGDB layers cannot be created with a wkbUnknown layer geometry type."); + } + if ( ! OGRGeometryToGDB(eType, &esri_type, &has_z) ) + return GDBErr(-1, "Unable to map OGR type to ESRI type"); + + if( wkbFlatten(eType) == wkbMultiPolygon && + CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "CREATE_MULTIPATCH", "NO")) ) + { + esri_type = "esriGeometryMultiPatch"; + has_z = true; + } + } + + m_bLaunderReservedKeywords = CSLFetchBoolean( papszOptions, "LAUNDER_RESERVED_KEYWORDS", TRUE) == TRUE; + + /* XML node */ + CPLXMLNode *xml_xml = CPLCreateXMLNode(NULL, CXT_Element, "?xml"); + FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0"); + FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8"); + + /* First build up a bare-bones feature definition */ + CPLXMLNode *defn_xml = CPLCreateXMLNode(NULL, CXT_Element, "esri:DataElement"); + CPLAddXMLSibling(xml_xml, defn_xml); + + /* Add the attributes to the DataElement */ + FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs", "http://www.w3.org/2001/XMLSchema"); + FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri", "http://www.esri.com/schemas/ArcGIS/10.1"); + + /* Need to set this to esri:DEFeatureDataset or esri:DETable */ + FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", (eType == wkbNone ? "esri:DETable" : "esri:DEFeatureClass")); + + /* Add in more children */ + CPLCreateXMLElementAndValue(defn_xml,"CatalogPath",table_path.c_str()); + CPLCreateXMLElementAndValue(defn_xml,"Name", layerName.c_str()); + CPLCreateXMLElementAndValue(defn_xml,"ChildrenExpanded", "false"); + + /* WKB type of none implies this is a 'Table' otherwise it's a 'Feature Class' */ + std::string datasettype = (eType == wkbNone ? "esriDTTable" : "esriDTFeatureClass"); + CPLCreateXMLElementAndValue(defn_xml,"DatasetType", datasettype.c_str() ); + CPLCreateXMLElementAndValue(defn_xml,"Versioned", "false"); + CPLCreateXMLElementAndValue(defn_xml,"CanVersion", "false"); + + if ( CSLFetchNameValue( papszOptions, "CONFIGURATION_KEYWORD") != NULL ) + CPLCreateXMLElementAndValue(defn_xml,"ConfigurationKeyword", + CSLFetchNameValue( papszOptions, "CONFIGURATION_KEYWORD")); + + /* We might need to make OID optional later, but OGR likes to have a FID */ + CPLCreateXMLElementAndValue(defn_xml,"HasOID", "true"); + CPLCreateXMLElementAndValue(defn_xml,"OIDFieldName", fid_name.c_str()); + + /* Add in empty Fields */ + CPLXMLNode *fields_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Fields"); + FGDB_CPLAddXMLAttribute(fields_xml, "xsi:type", "esri:Fields"); + CPLXMLNode *fieldarray_xml = CPLCreateXMLNode(fields_xml, CXT_Element, "FieldArray"); + FGDB_CPLAddXMLAttribute(fieldarray_xml, "xsi:type", "esri:ArrayOfField"); + + /* Feature Classes have an implicit geometry column, so we'll add it at creation time */ + CPLXMLNode *srs_xml = NULL; + if ( eType != wkbNone ) + { + CPLXMLNode *shape_xml = CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field"); + FGDB_CPLAddXMLAttribute(shape_xml, "xsi:type", "esri:Field"); + CPLCreateXMLElementAndValue(shape_xml, "Name", geometry_name.c_str()); + CPLCreateXMLElementAndValue(shape_xml, "Type", "esriFieldTypeGeometry"); + if( CSLFetchBoolean( papszOptions, "GEOMETRY_NULLABLE", TRUE) ) + CPLCreateXMLElementAndValue(shape_xml, "IsNullable", "true"); + else + CPLCreateXMLElementAndValue(shape_xml, "IsNullable", "false"); + CPLCreateXMLElementAndValue(shape_xml, "Length", "0"); + CPLCreateXMLElementAndValue(shape_xml, "Precision", "0"); + CPLCreateXMLElementAndValue(shape_xml, "Scale", "0"); + CPLCreateXMLElementAndValue(shape_xml, "Required", "true"); + CPLXMLNode *geom_xml = CPLCreateXMLNode(shape_xml, CXT_Element, "GeometryDef"); + FGDB_CPLAddXMLAttribute(geom_xml, "xsi:type", "esri:GeometryDef"); + CPLCreateXMLElementAndValue(geom_xml, "AvgNumPoints", "0"); + CPLCreateXMLElementAndValue(geom_xml, "GeometryType", esri_type.c_str()); + CPLCreateXMLElementAndValue(geom_xml,"HasM", "false"); + CPLCreateXMLElementAndValue(geom_xml,"HasZ", (has_z ? "true" : "false")); + + /* Add the SRS if we have one */ + srs_xml = XMLSpatialReference(poSRS, papszOptions); + if ( srs_xml ) + CPLAddXMLChild(geom_xml, srs_xml); + } + + /* All (?) Tables and Feature Classes will have an ObjectID */ + CPLXMLNode *oid_xml = CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field"); + FGDB_CPLAddXMLAttribute(oid_xml, "xsi:type", "esri:Field"); + CPLCreateXMLElementAndValue(oid_xml, "Name", fid_name.c_str()); + CPLCreateXMLElementAndValue(oid_xml, "Type", "esriFieldTypeOID"); + CPLCreateXMLElementAndValue(oid_xml, "IsNullable", "false"); + CPLCreateXMLElementAndValue(oid_xml, "Length", "12"); + CPLCreateXMLElementAndValue(oid_xml, "Precision", "0"); + CPLCreateXMLElementAndValue(oid_xml, "Scale", "0"); + CPLCreateXMLElementAndValue(oid_xml, "Required", "true"); + + /* Add in empty Indexes */ + CPLXMLNode *indexes_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Indexes"); + FGDB_CPLAddXMLAttribute(indexes_xml, "xsi:type", "esri:Indexes"); + CPLXMLNode *indexarray_xml = CPLCreateXMLNode(indexes_xml, CXT_Element, "IndexArray"); + FGDB_CPLAddXMLAttribute(indexarray_xml, "xsi:type", "esri:ArrayOfIndex"); + + /* CLSID http://forums.arcgis.com/threads/34536?p=118484#post118484 */ + if ( eType == wkbNone ) + { + CPLCreateXMLElementAndValue(defn_xml, "CLSID", "{7A566981-C114-11D2-8A28-006097AFF44E}"); + CPLCreateXMLElementAndValue(defn_xml, "EXTCLSID", ""); + } + else + { + CPLCreateXMLElementAndValue(defn_xml, "CLSID", "{52353152-891A-11D0-BEC6-00805F7C4268}"); + CPLCreateXMLElementAndValue(defn_xml, "EXTCLSID", ""); + } + + /* Set the alias for the Feature Class */ + if (pszLayerNameIn != layerName) + { + CPLCreateXMLElementAndValue(defn_xml, "AliasName", pszLayerNameIn); + } + + /* Map from OGR WKB type to ESRI type */ + if ( eType != wkbNone ) + { + /* Declare our feature type */ + CPLCreateXMLElementAndValue(defn_xml,"FeatureType", "esriFTSimple"); + CPLCreateXMLElementAndValue(defn_xml,"ShapeType", esri_type.c_str()); + CPLCreateXMLElementAndValue(defn_xml,"ShapeFieldName", geometry_name.c_str()); + + /* Dimensionality */ + CPLCreateXMLElementAndValue(defn_xml,"HasM", "false"); + CPLCreateXMLElementAndValue(defn_xml,"HasZ", (has_z ? "true" : "false")); + + /* TODO: Handle spatial indexes (layer creation option?) */ + CPLCreateXMLElementAndValue(defn_xml,"HasSpatialIndex", "false"); + + /* These field are required for Arcmap to display aliases correctly */ + CPLCreateXMLNode(defn_xml, CXT_Element, "AreaFieldName"); + CPLCreateXMLNode(defn_xml, CXT_Element, "LengthFieldName"); + + /* We can't know the extent at this point */ + CPLXMLNode *extn_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Extent"); + FGDB_CPLAddXMLAttribute(extn_xml, "xsi:nil", "true"); + } + + /* Feature Class with known SRS gets an SRS entry */ + if( eType != wkbNone && srs_xml != NULL) + { + CPLAddXMLChild(defn_xml, CPLCloneXMLTree(srs_xml)); + } + + /* Convert our XML tree into a string for FGDB */ + char *defn_str; + + if( CSLFetchNameValue( papszOptions, "XML_DEFINITION") != NULL ) + defn_str = CPLStrdup(CSLFetchNameValue( papszOptions, "XML_DEFINITION")); + else + defn_str = CPLSerializeXMLTree(xml_xml); + CPLDestroyXMLNode(xml_xml); + + /* TODO, tie this to debugging levels */ + CPLDebug("FGDB", "%s", defn_str); + //std::cout << defn_str << std::endl; + + /* Create the table. */ + Table *table = new Table; + Geodatabase *gdb = pParentDataSource->GetGDB(); + fgdbError hr = gdb->CreateTable(defn_str, wparent_path, *table); + + /* Free the XML */ + CPLFree(defn_str); + + /* Check table create status */ + if (FAILED(hr)) + { + delete table; + return GDBErr(hr, "Failed at creating table for " + table_path); + } + + m_papszOptions = CSLDuplicate(papszOptions); + m_bCreateMultipatch = CSLTestBoolean(CSLFetchNameValueDef(m_papszOptions, "CREATE_MULTIPATCH", "NO")); + + // Default to YES here assuming ogr2ogr scenario + m_bBulkLoadAllowed = CSLTestBoolean(CPLGetConfigOption("FGDB_BULK_LOAD", "YES")); + + /* Store the new FGDB Table pointer and set up the OGRFeatureDefn */ + return FGdbLayer::Initialize(pParentDataSource, table, wtable_path, L"Table"); +} + +/*************************************************************************/ +/* Initialize() */ +/* Has ownership of the table as soon as it is called. */ +/************************************************************************/ + +bool FGdbLayer::Initialize(FGdbDataSource* pParentDataSource, Table* pTable, + std::wstring wstrTablePath, std::wstring wstrType) +{ + long hr; + + m_pDS = pParentDataSource; // we never assume ownership of the parent - so our destructor should not delete + + m_pTable = pTable; + + m_wstrTablePath = wstrTablePath; + m_wstrType = wstrType; + + wstring wstrQueryName; + if (FAILED(hr = pParentDataSource->GetGDB()->GetQueryName(wstrTablePath, wstrQueryName))) + return GDBErr(hr, "Failed at getting underlying table name for " + + WStringToString(wstrTablePath)); + + m_strName = WStringToString(wstrQueryName); + + m_pFeatureDefn = new OGRFeatureDefn(m_strName.c_str()); //TODO: Should I "new" an OGR smart pointer - sample says so, but it doesn't seem right + SetDescription( m_pFeatureDefn->GetName() ); + //as long as we use the same compiler & settings in both the ogr build and this + //driver, we should be OK + m_pFeatureDefn->Reference(); + + string tableDef; + if (FAILED(hr = m_pTable->GetDefinition(tableDef))) + return GDBErr(hr, "Failed at getting table definition for " + + WStringToString(wstrTablePath)); + + //xxx printf("Table definition = %s", tableDef.c_str() ); + + bool abort = false; + + // extract schema information from table + CPLXMLNode *psRoot = CPLParseXMLString( tableDef.c_str() ); + + if (psRoot == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "%s", + ("Failed parsing GDB Table Schema XML for " + m_strName).c_str()); + return false; + } + + CPLXMLNode *pDataElementNode = psRoot->psNext; // Move to next field which should be DataElement + + if( pDataElementNode != NULL + && pDataElementNode->psChild != NULL + && pDataElementNode->eType == CXT_Element + && EQUAL(pDataElementNode->pszValue,"esri:DataElement") ) + { + CPLXMLNode *psNode; + + for( psNode = pDataElementNode->psChild; + psNode != NULL; + psNode = psNode->psNext ) + { + if( psNode->eType == CXT_Element && psNode->psChild != NULL ) + { + if (EQUAL(psNode->pszValue,"OIDFieldName") ) + { + char* pszUnescaped = CPLUnescapeString( + psNode->psChild->pszValue, NULL, CPLES_XML); + m_strOIDFieldName = pszUnescaped; + CPLFree(pszUnescaped); + } + else if (EQUAL(psNode->pszValue,"ShapeFieldName") ) + { + char* pszUnescaped = CPLUnescapeString( + psNode->psChild->pszValue, NULL, CPLES_XML); + m_strShapeFieldName = pszUnescaped; + CPLFree(pszUnescaped); + } + else if (EQUAL(psNode->pszValue,"Fields") ) + { + if (!GDBToOGRFields(psNode)) + { + abort = true; + break; + } + } + } + } + + if (m_strShapeFieldName.size() == 0) + m_pFeatureDefn->SetGeomType(wkbNone); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, "%s", + ("Failed parsing GDB Table Schema XML (DataElement) for " + m_strName).c_str()); + return false; + } + CPLDestroyXMLNode( psRoot ); + + if( m_pFeatureDefn->GetGeomFieldCount() != 0 ) + { + m_pFeatureDefn->GetGeomFieldDefn(0)->SetName(m_strShapeFieldName.c_str()); + m_pFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_pSRS); + } + + if (abort) + return false; + + return true; //AOToOGRFields(ipFields, m_pFeatureDefn, m_vOGRFieldToESRIField); +} + +/************************************************************************/ +/* ParseGeometryDef() */ +/************************************************************************/ + +bool FGdbLayer::ParseGeometryDef(CPLXMLNode* psRoot) +{ + CPLXMLNode *psGeometryDefItem; + + string geometryType; + bool hasZ = false; + string wkt, wkid, latestwkid; + + for (psGeometryDefItem = psRoot->psChild; + psGeometryDefItem != NULL; + psGeometryDefItem = psGeometryDefItem->psNext ) + { + //loop through all "GeometryDef" elements + // + + if (psGeometryDefItem->eType == CXT_Element && + psGeometryDefItem->psChild != NULL) + { + if (EQUAL(psGeometryDefItem->pszValue,"GeometryType")) + { + char* pszUnescaped = CPLUnescapeString( + psGeometryDefItem->psChild->pszValue, NULL, CPLES_XML); + + geometryType = pszUnescaped; + + CPLFree(pszUnescaped); + } + else if (EQUAL(psGeometryDefItem->pszValue,"SpatialReference")) + { + ParseSpatialReference(psGeometryDefItem, &wkt, &wkid, &latestwkid); // we don't check for success because it + // may not be there + } + /* No M support in OGR yet + else if (EQUAL(psFieldNode->pszValue,"HasM") + { + char* pszUnescaped = CPLUnescapeString(psNode->psChild->pszValue, NULL, CPLES_XML); + + if (!strcmp(szUnescaped, "true")) + hasM = true; + + CPLFree(pszUnescaped); + } + */ + else if (EQUAL(psGeometryDefItem->pszValue,"HasZ")) + { + char* pszUnescaped = CPLUnescapeString( + psGeometryDefItem->psChild->pszValue, NULL, CPLES_XML); + + if (!strcmp(pszUnescaped, "true")) + hasZ = true; + + CPLFree(pszUnescaped); + } + } + + } + + OGRwkbGeometryType ogrGeoType; + if (!GDBToOGRGeometry(geometryType, hasZ, &ogrGeoType)) + return false; + + m_pFeatureDefn->SetGeomType(ogrGeoType); + + if (wkbFlatten(ogrGeoType) == wkbMultiLineString || + wkbFlatten(ogrGeoType) == wkbMultiPoint) + m_forceMulti = true; + + if (latestwkid.length() > 0 || wkid.length() > 0) + { + int bSuccess = FALSE; + m_pSRS = new OGRSpatialReference(); + CPLPushErrorHandler(CPLQuietErrorHandler); + if( latestwkid.length() > 0 ) + { + if( m_pSRS->importFromEPSG(atoi(latestwkid.c_str())) == OGRERR_NONE ) + { + bSuccess = TRUE; + } + else + { + CPLDebug("FGDB", "Cannot import SRID %s", latestwkid.c_str()); + } + } + if( !bSuccess && wkid.length() > 0 ) + { + if( m_pSRS->importFromEPSG(atoi(wkid.c_str())) == OGRERR_NONE ) + { + bSuccess = TRUE; + } + else + { + CPLDebug("FGDB", "Cannot import SRID %s", wkid.c_str()); + } + } + CPLPopErrorHandler(); + CPLErrorReset(); + if( !bSuccess ) + { + delete m_pSRS; + m_pSRS = NULL; + } + else + return true; + } + + if (wkt.length() > 0) + { + if (!GDBToOGRSpatialReference(wkt, &m_pSRS)) + { + //report error, but be passive about it + CPLError( CE_Warning, CPLE_AppDefined, + "Failed Mapping ESRI Spatial Reference"); + } + } + else + { + //report error, but be passive about it + CPLError( CE_Warning, CPLE_AppDefined, "Empty Spatial Reference"); + } + + return true; +} + +/************************************************************************/ +/* ParseSpatialReference() */ +/************************************************************************/ + +bool FGdbLayer::ParseSpatialReference(CPLXMLNode* psSpatialRefNode, + string* pOutWkt, string* pOutWKID, + string* pOutLatestWKID) +{ + *pOutWkt = ""; + *pOutWKID = ""; + *pOutLatestWKID = ""; + + CPLXMLNode* psSRItemNode; + + /* Loop through all the SRS elements we want to store */ + for( psSRItemNode = psSpatialRefNode->psChild; + psSRItemNode != NULL; + psSRItemNode = psSRItemNode->psNext ) + { + /* The WKID maps (mostly) to an EPSG code */ + if( psSRItemNode->eType == CXT_Element && + psSRItemNode->psChild != NULL && + EQUAL(psSRItemNode->pszValue,"WKID") ) + { + char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, NULL, CPLES_XML); + *pOutWKID = pszUnescaped; + CPLFree(pszUnescaped); + + // Needed with FileGDB v1.4 with layers with empty SRS + if( *pOutWKID == "0" ) + *pOutWKID = ""; + } + /* The concept of LatestWKID is explained in http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r3000000n1000000 */ + else if( psSRItemNode->eType == CXT_Element && + psSRItemNode->psChild != NULL && + EQUAL(psSRItemNode->pszValue,"LatestWKID") ) + { + char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, NULL, CPLES_XML); + *pOutLatestWKID = pszUnescaped; + CPLFree(pszUnescaped); + } + /* The WKT well-known text can be converted by OGR */ + else if( psSRItemNode->eType == CXT_Element && + psSRItemNode->psChild != NULL && + EQUAL(psSRItemNode->pszValue,"WKT") ) + { + char* pszUnescaped = CPLUnescapeString(psSRItemNode->psChild->pszValue, NULL, CPLES_XML); + *pOutWkt = pszUnescaped; + CPLFree(pszUnescaped); + } + + } + return (*pOutWkt != "" || *pOutWKID != ""); +} + +/************************************************************************/ +/* GDBToOGRFields() */ +/************************************************************************/ + +bool FGdbLayer::GDBToOGRFields(CPLXMLNode* psRoot) +{ + m_vOGRFieldToESRIField.clear(); + + if (psRoot->psChild == NULL || psRoot->psChild->psNext == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "Unrecognized GDB XML Schema"); + + return false; + } + + psRoot = psRoot->psChild->psNext; //change root to "FieldArray" + + //CPLAssert(ogrToESRIFieldMapping.size() == pOGRFeatureDef->GetFieldCount()); + + CPLXMLNode* psFieldNode; + int bShouldQueryOpenFileGDB = FALSE; + + for( psFieldNode = psRoot->psChild; + psFieldNode != NULL; + psFieldNode = psFieldNode->psNext ) + { + //loop through all "Field" elements + // + + if( psFieldNode->eType == CXT_Element && psFieldNode->psChild != NULL && + EQUAL(psFieldNode->pszValue,"Field")) + { + + CPLXMLNode* psFieldItemNode; + std::string fieldName; + std::string fieldType; + int nLength = 0; + int nPrecision = 0; + int bNullable = TRUE; + std::string osDefault; + + // loop through all items in Field element + // + + for( psFieldItemNode = psFieldNode->psChild; + psFieldItemNode != NULL; + psFieldItemNode = psFieldItemNode->psNext ) + { + if (psFieldItemNode->eType == CXT_Element) + { + if (EQUAL(psFieldItemNode->pszValue,"Name")) + { + char* pszUnescaped = CPLUnescapeString( + psFieldItemNode->psChild->pszValue, NULL, CPLES_XML); + fieldName = pszUnescaped; + CPLFree(pszUnescaped); + } + else if (EQUAL(psFieldItemNode->pszValue,"Type") ) + { + char* pszUnescaped = CPLUnescapeString( + psFieldItemNode->psChild->pszValue, NULL, CPLES_XML); + fieldType = pszUnescaped; + CPLFree(pszUnescaped); + } + else if (EQUAL(psFieldItemNode->pszValue,"GeometryDef") ) + { + if (!ParseGeometryDef(psFieldItemNode)) + return false; // if we failed parsing the GeometryDef, we are done! + } + else if (EQUAL(psFieldItemNode->pszValue,"Length") ) + { + nLength = atoi(psFieldItemNode->psChild->pszValue); + } + else if (EQUAL(psFieldItemNode->pszValue,"Precision") ) + { + nPrecision = atoi(psFieldItemNode->psChild->pszValue); + } + else if (EQUAL(psFieldItemNode->pszValue,"IsNullable") ) + { + bNullable = EQUAL(psFieldItemNode->psChild->pszValue, "true"); + } + else if (EQUAL(psFieldItemNode->pszValue,"DefaultValue")) + { + osDefault = CPLGetXMLValue(psFieldItemNode, NULL, ""); + } + } + } + + + /////////////////////////////////////////////////////////////////// + // At this point we have parsed everything about the current field + + + if (fieldType == "esriFieldTypeGeometry") + { + m_strShapeFieldName = fieldName; + m_pFeatureDefn->GetGeomFieldDefn(0)->SetNullable(bNullable); + + continue; // finish here for special field - don't add as OGR fielddef + } + else if (fieldType == "esriFieldTypeOID") + { + //m_strOIDFieldName = fieldName; // already set by this point + + continue; // finish here for special field - don't add as OGR fielddef + } + + OGRFieldType ogrType; + OGRFieldSubType eSubType; + //CPLDebug("FGDB", "name = %s, type = %s", fieldName.c_str(), fieldType.c_str() ); + if (!GDBToOGRFieldType(fieldType, &ogrType, &eSubType)) + { + // field cannot be mapped, skipping further processing + CPLError( CE_Warning, CPLE_AppDefined, "Skipping field: [%s] type: [%s] ", + fieldName.c_str(), fieldType.c_str() ); + continue; + } + + + //TODO: Optimization - modify m_wstrSubFields so it only fetches fields that are mapped + + OGRFieldDefn fieldTemplate( fieldName.c_str(), ogrType); + fieldTemplate.SetSubType(eSubType); + /* On creation (GDBFieldTypeToWidthPrecision) if string width is 0, we pick up */ + /* 65535 by default to mean unlimited string length, but we don't want */ + /* to advertize such a big number */ + if( ogrType == OFTString && nLength < 65535 ) + fieldTemplate.SetWidth(nLength); + //fieldTemplate.SetPrecision(nPrecision); + fieldTemplate.SetNullable(bNullable); + if( osDefault.size() ) + { + if( ogrType == OFTString ) + { + char* pszTmp = CPLEscapeString(osDefault.c_str(), -1, CPLES_SQL); + osDefault = "'"; + osDefault += pszTmp; + CPLFree(pszTmp); + osDefault += "'"; + fieldTemplate.SetDefault(osDefault.c_str()); + } + else if( ogrType == OFTInteger || + ogrType == OFTReal ) + { +#ifdef unreliable + /* Disabling this as GDBs and the FileGDB SDK aren't reliable for numeric values */ + /* It often occurs that the XML definition in a00000004.gdbtable doesn't */ + /* match the default values (in binary) found in the field definition */ + /* section of the .gdbtable of the layers themselves */ + /* The Table::GetDefinition() API of FileGDB doesn't seem to use the */ + /* XML definition, but rather the values found in the field definition */ + /* section of the .gdbtable of the layers themselves */ + /* It seems that the XML definition in a00000004.gdbtable is authoritative */ + /* in ArcGIS, so we're screwed... */ + + fieldTemplate.SetDefault(osDefault.c_str()); +#endif + bShouldQueryOpenFileGDB = TRUE; + } + else if( ogrType == OFTDateTime ) + { + int nYear, nMonth, nDay, nHour, nMinute; + float fSecond; + if( sscanf(osDefault.c_str(), "%d-%d-%dT%d:%d:%fZ", &nYear, &nMonth, &nDay, + &nHour, &nMinute, &fSecond) == 6 || + sscanf(osDefault.c_str(), "'%d-%d-%d %d:%d:%fZ'", &nYear, &nMonth, &nDay, + &nHour, &nMinute, &fSecond) == 6 ) + { + fieldTemplate.SetDefault(CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%02d'", + nYear, nMonth, nDay, nHour, nMinute, (int)(fSecond + 0.5))); + } + } + } + + m_pFeatureDefn->AddFieldDefn( &fieldTemplate ); + + m_vOGRFieldToESRIField.push_back(StringToWString(fieldName)); + m_vOGRFieldToESRIFieldType.push_back( fieldType ); + if( ogrType == OFTBinary ) + m_apoByteArrays.push_back(new ByteArray()); + + } + } + + /* Using OpenFileGDB to get reliable default values for integer/real fields */ + if( bShouldQueryOpenFileGDB ) + { + const char* apszDrivers[] = { "OpenFileGDB", NULL }; + GDALDataset* poDS = (GDALDataset*) GDALOpenEx(m_pDS->GetName(), + GDAL_OF_VECTOR, (char**)apszDrivers, NULL, NULL); + if( poDS != NULL ) + { + OGRLayer* poLyr = poDS->GetLayerByName(GetName()); + if( poLyr ) + { + for(int i=0;iGetLayerDefn()->GetFieldCount();i++) + { + OGRFieldDefn* poSrcDefn = poLyr->GetLayerDefn()->GetFieldDefn(i); + if( (poSrcDefn->GetType() == OFTInteger || poSrcDefn->GetType() == OFTReal) && + poSrcDefn->GetDefault() != NULL ) + { + int nIdxDst = m_pFeatureDefn->GetFieldIndex(poSrcDefn->GetNameRef()); + if( nIdxDst >= 0 ) + m_pFeatureDefn->GetFieldDefn(nIdxDst)->SetDefault(poSrcDefn->GetDefault()); + } + } + } + GDALClose( poDS ); + } + } + + return true; +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void FGdbLayer::ResetReading() +{ + long hr; + + EndBulkLoad(); + + if (m_pOGRFilterGeometry && !m_pOGRFilterGeometry->IsEmpty()) + { + // Search spatial + // As of beta1, FileGDB only supports bbox searched, if we have GEOS installed, + // we can do the rest ourselves. + + OGREnvelope ogrEnv; + + m_pOGRFilterGeometry->getEnvelope(&ogrEnv); + + //spatial query + FileGDBAPI::Envelope env(ogrEnv.MinX, ogrEnv.MaxX, ogrEnv.MinY, ogrEnv.MaxY); + + if FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause, env, true, *m_pEnumRows)) + GDBErr(hr, "Failed Searching"); + + m_bFilterDirty = false; + + return; + } + + // Search non-spatial + + if FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause, true, *m_pEnumRows)) + GDBErr(hr, "Failed Searching"); + + m_bFilterDirty = false; + +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void FGdbLayer::SetSpatialFilter( OGRGeometry* pOGRGeom ) +{ + if( !InstallFilter(pOGRGeom) ) + return; + + if (m_pOGRFilterGeometry) + { + OGRGeometryFactory::destroyGeometry(m_pOGRFilterGeometry); + m_pOGRFilterGeometry = NULL; + } + + if (pOGRGeom == NULL || pOGRGeom->IsEmpty()) + { + m_bFilterDirty = true; + + return; + } + + m_pOGRFilterGeometry = pOGRGeom->clone(); + + m_pOGRFilterGeometry->transformTo(m_pSRS); + + m_bFilterDirty = true; +} + +/************************************************************************/ +/* SetSpatialFilterRect() */ +/************************************************************************/ + +void FGdbLayer::SetSpatialFilterRect (double dfMinX, double dfMinY, double dfMaxX, double dfMaxY) +{ + + //TODO: can optimize this by changing how the filter gets generated - + //this will work for now + + OGRGeometry* pTemp = OGRGeometryFactory::createGeometry(wkbPolygon); + + pTemp->assignSpatialReference(m_pSRS); + + OGRLinearRing ring; + + ring.addPoint( dfMinX, dfMinY ); + ring.addPoint( dfMinX, dfMaxY ); + ring.addPoint( dfMaxX, dfMaxY ); + ring.addPoint( dfMaxX, dfMinY ); + ring.addPoint( dfMinX, dfMinY ); + ((OGRPolygon *) pTemp)->addRing( &ring ); + + SetSpatialFilter(pTemp); + + OGRGeometryFactory::destroyGeometry(pTemp); +} + + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr FGdbLayer::SetAttributeFilter( const char* pszQuery ) +{ + m_wstrWhereClause = StringToWString( (pszQuery != NULL) ? pszQuery : "" ); + + m_bFilterDirty = true; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGRFeatureFromGdbRow() */ +/************************************************************************/ + +bool FGdbBaseLayer::OGRFeatureFromGdbRow(Row* pRow, OGRFeature** ppFeature) +{ + long hr; + + OGRFeature* pOutFeature = new OGRFeature(m_pFeatureDefn); + + ///////////////////////////////////////////////////////// + // Translate OID + // + + int32 oid = -1; + if (FAILED(hr = pRow->GetOID(oid))) + { + //this should never happen + delete pOutFeature; + return false; + } + pOutFeature->SetFID(oid); + + + ///////////////////////////////////////////////////////// + // Translate Geometry + // + + ShapeBuffer gdbGeometry; + // Row::GetGeometry() will fail with -2147467259 for NULL geometries + // Row::GetGeometry() will fail with -2147219885 for tables without a geometry field + if (!m_pFeatureDefn->IsGeometryIgnored() && + !FAILED(hr = pRow->GetGeometry(gdbGeometry))) + { + OGRGeometry* pOGRGeo = NULL; + + if ((!GDBGeometryToOGRGeometry(m_forceMulti, &gdbGeometry, m_pSRS, &pOGRGeo))) + { + delete pOutFeature; + return GDBErr(hr, "Failed to translate FileGDB Geometry to OGR Geometry for row " + string(CPLSPrintf("%d", (int)oid))); + } + + pOutFeature->SetGeometryDirectly(pOGRGeo); + } + + + ////////////////////////////////////////////////////////// + // Map fields + // + + + size_t mappedFieldCount = m_vOGRFieldToESRIField.size(); + + bool foundBadColumn = false; + + for (size_t i = 0; i < mappedFieldCount; ++i) + { + OGRFieldDefn* poFieldDefn = m_pFeatureDefn->GetFieldDefn(i); + // The IsNull() and GetXXX() API are very slow when there are a + // big number of fields, for example with Tiger database. + if (poFieldDefn->IsIgnored() ) + continue; + + const wstring & wstrFieldName = m_vOGRFieldToESRIField[i]; + const std::string & strFieldType = m_vOGRFieldToESRIFieldType[i]; + + bool isNull = false; + + if (FAILED(hr = pRow->IsNull(wstrFieldName, isNull))) + { + GDBErr(hr, "Failed to determine NULL status from column " + + WStringToString(wstrFieldName)); + foundBadColumn = true; + continue; + } + + if (isNull) + { + continue; //leave as unset + } + + // + // NOTE: This switch statement needs to be kept in sync with GDBToOGRFieldType utility function + // since we are only checking for types we mapped in that utility function + + switch (poFieldDefn->GetType()) + { + + case OFTInteger: + { + int32 val; + + if (FAILED(hr = pRow->GetInteger(wstrFieldName, val))) + { + int16 shortval; + if (FAILED(hr = pRow->GetShort(wstrFieldName, shortval))) + { + GDBErr(hr, "Failed to determine integer value for column " + + WStringToString(wstrFieldName)); + foundBadColumn = true; + continue; + } + val = shortval; + } + + pOutFeature->SetField(i, (int)val); + } + break; + + case OFTReal: + { + if (strFieldType == "esriFieldTypeSingle") + { + float val; + + if (FAILED(hr = pRow->GetFloat(wstrFieldName, val))) + { + GDBErr(hr, "Failed to determine float value for column " + + WStringToString(wstrFieldName)); + foundBadColumn = true; + continue; + } + + pOutFeature->SetField(i, val); + } + else + { + double val; + + if (FAILED(hr = pRow->GetDouble(wstrFieldName, val))) + { + GDBErr(hr, "Failed to determine real value for column " + + WStringToString(wstrFieldName)); + foundBadColumn = true; + continue; + } + + pOutFeature->SetField(i, val); + } + } + break; + case OFTString: + { + wstring val; + std::string strValue; + + if( strFieldType == "esriFieldTypeGlobalID" ) + { + Guid guid; + if( FAILED(hr = pRow->GetGlobalID(guid)) || + FAILED(hr = guid.ToString(val)) ) + { + GDBErr(hr, "Failed to determine string value for column " + + WStringToString(wstrFieldName)); + foundBadColumn = true; + continue; + } + strValue = WStringToString(val); + } + else if( strFieldType == "esriFieldTypeGUID" ) + { + Guid guid; + if( FAILED(hr = pRow->GetGUID(wstrFieldName, guid)) || + FAILED(hr = guid.ToString(val)) ) + { + GDBErr(hr, "Failed to determine string value for column " + + WStringToString(wstrFieldName)); + foundBadColumn = true; + continue; + } + strValue = WStringToString(val); + } + else if( strFieldType == "esriFieldTypeXML" ) + { + if (FAILED(hr = pRow->GetXML(wstrFieldName, strValue))) + { + GDBErr(hr, "Failed to determine XML value for column " + + WStringToString(wstrFieldName)); + foundBadColumn = true; + continue; + } + } + else + { + if (FAILED(hr = pRow->GetString(wstrFieldName, val))) + { + GDBErr(hr, "Failed to determine string value for column " + + WStringToString(wstrFieldName)); + foundBadColumn = true; + continue; + } + strValue = WStringToString(val); + } + + pOutFeature->SetField(i, strValue.c_str()); + } + break; + + case OFTBinary: + { + ByteArray binaryBuf; + + if (FAILED(hr = pRow->GetBinary(wstrFieldName, binaryBuf))) + { + GDBErr(hr, "Failed to determine binary value for column " + WStringToString(wstrFieldName)); + foundBadColumn = true; + continue; + } + + pOutFeature->SetField(i, (int)binaryBuf.inUseLength, (GByte*)binaryBuf.byteArray); + } + break; + + case OFTDateTime: + { + struct tm val; + + if (FAILED(hr = pRow->GetDate(wstrFieldName, val))) + { + GDBErr(hr, "Failed to determine date value for column " + + WStringToString(wstrFieldName)); + foundBadColumn = true; + continue; + } + + pOutFeature->SetField(i, val.tm_year + 1900, val.tm_mon + 1, + val.tm_mday, val.tm_hour, val.tm_min, val.tm_sec); + // Examine test data to figure out how to extract that + } + break; + + default: + { + if (!m_suppressColumnMappingError) + { + foundBadColumn = true; + CPLError( CE_Warning, CPLE_AppDefined, + "Row id: %d col:%d has unhandled col type (%d). Setting to NULL.", + (int)oid, (int)i, m_pFeatureDefn->GetFieldDefn(i)->GetType()); + } + } + } + } + + if (foundBadColumn) + m_suppressColumnMappingError = true; + + + *ppFeature = pOutFeature; + + return true; +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature* FGdbLayer::GetNextFeature() +{ + if (m_bFilterDirty) + ResetReading(); + + EndBulkLoad(); + + return FGdbBaseLayer::GetNextFeature(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *FGdbLayer::GetFeature( GIntBig oid ) +{ + // do query to fetch individual row + EnumRows enumRows; + Row row; + + EndBulkLoad(); + + if (GetRow(enumRows, row, oid) != OGRERR_NONE) + return NULL; + + OGRFeature* pOGRFeature = NULL; + + if (!OGRFeatureFromGdbRow(&row, &pOGRFeature)) + { + return NULL; + } + + return pOGRFeature; +} + + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig FGdbLayer::GetFeatureCount( CPL_UNUSED int bForce ) +{ + long hr; + int32 rowCount = 0; + + EndBulkLoad(); + + if (m_pOGRFilterGeometry != NULL || m_wstrWhereClause.size() != 0) + { + ResetReading(); + if (m_pEnumRows == NULL) + return 0; + + int nFeatures = 0; + while( TRUE ) + { + long hr; + + Row row; + + if (FAILED(hr = m_pEnumRows->Next(row))) + { + GDBErr(hr, "Failed fetching features"); + return 0; + } + + if (hr != S_OK) + { + break; + } + nFeatures ++; + } + ResetReading(); + return nFeatures; + } + + if (FAILED(hr = m_pTable->GetRowCount(rowCount))) + { + GDBErr(hr, "Failed counting rows"); + return 0; + } + + return static_cast(rowCount); +} + + + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr FGdbLayer::GetExtent (OGREnvelope* psExtent, int bForce) +{ + if (m_pOGRFilterGeometry != NULL || m_wstrWhereClause.size() != 0 || + m_strShapeFieldName.size() == 0) + { + int* pabSaveFieldIgnored = new int[m_pFeatureDefn->GetFieldCount()]; + for(int i=0;iGetFieldCount();i++) + { + pabSaveFieldIgnored[i] = m_pFeatureDefn->GetFieldDefn(i)->IsIgnored(); + m_pFeatureDefn->GetFieldDefn(i)->SetIgnored(TRUE); + } + OGRErr eErr = OGRLayer::GetExtent(psExtent, bForce); + for(int i=0;iGetFieldCount();i++) + { + m_pFeatureDefn->GetFieldDefn(i)->SetIgnored(pabSaveFieldIgnored[i]); + } + delete[] pabSaveFieldIgnored; + return eErr; + } + + long hr; + Envelope envelope; + if (FAILED(hr = m_pTable->GetExtent(envelope))) + { + GDBErr(hr, "Failed fetching extent"); + return OGRERR_FAILURE; + } + + psExtent->MinX = envelope.xMin; + psExtent->MinY = envelope.yMin; + psExtent->MaxX = envelope.xMax; + psExtent->MaxY = envelope.yMax; + + if (CPLIsNan(psExtent->MinX) || + CPLIsNan(psExtent->MinY) || + CPLIsNan(psExtent->MaxX) || + CPLIsNan(psExtent->MaxY)) + return OGRERR_FAILURE; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* StartBulkLoad() */ +/************************************************************************/ + +void FGdbLayer::StartBulkLoad () +{ + if ( ! m_pTable ) + return; + + if ( m_bBulkLoadInProgress ) + return; + + m_bBulkLoadInProgress = TRUE; + m_pTable->LoadOnlyMode(true); + m_pTable->SetWriteLock(); +} + +/************************************************************************/ +/* EndBulkLoad() */ +/************************************************************************/ + +void FGdbLayer::EndBulkLoad () +{ + if ( ! m_pTable ) + return; + + if ( ! m_bBulkLoadInProgress ) + return; + + m_bBulkLoadInProgress = FALSE; + m_bBulkLoadAllowed = -1; /* so that the configuration option is read the first time we CreateFeature() again */ + m_pTable->LoadOnlyMode(false); + m_pTable->FreeWriteLock(); +} + +/* OGRErr FGdbLayer::StartTransaction () +{ + if ( ! m_pTable ) + return OGRERR_FAILURE; + + m_pTable->LoadOnlyMode(true); + m_pTable->SetWriteLock(); + return OGRERR_NONE; + +} */ + + +/* OGRErr FGdbLayer::CommitTransaction () +{ + if ( ! m_pTable ) + return OGRERR_FAILURE; + + m_pTable->LoadOnlyMode(false); + m_pTable->FreeWriteLock(); + return OGRERR_NONE; +} */ + +/* OGRErr FGdbLayer::RollbackTransaction () +{ + if ( ! m_pTable ) + return OGRERR_FAILURE; + + m_pTable->LoadOnlyMode(false); + m_pTable->FreeWriteLock(); + return OGRERR_NONE; +} */ + + +/************************************************************************/ +/* GetLayerXML() */ +/* Return XML definition of the Layer as provided by FGDB. Caller must */ +/* free result. */ +/* Not currently used by the driver, but can be used by external code */ +/* for specific purposes. */ +/************************************************************************/ + +OGRErr FGdbLayer::GetLayerXML (char **ppXml) +{ + long hr; + std::string xml; + + if ( FAILED(hr = m_pTable->GetDefinition(xml)) ) + { + GDBErr(hr, "Failed fetching XML table definition"); + return OGRERR_FAILURE; + } + + *ppXml = CPLStrdup(xml.c_str()); + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetLayerMetadataXML() */ +/* Return XML metadata for the Layer as provided by FGDB. Caller must */ +/* free result. */ +/* Not currently used by the driver, but can be used by external code */ +/* for specific purposes. */ +/************************************************************************/ + +OGRErr FGdbLayer::GetLayerMetadataXML (char **ppXml) +{ + long hr; + std::string xml; + + if ( FAILED(hr = m_pTable->GetDocumentation(xml)) ) + { + GDBErr(hr, "Failed fetching XML table metadata"); + return OGRERR_FAILURE; + } + + *ppXml = CPLStrdup(xml.c_str()); + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int FGdbLayer::TestCapability( const char* pszCap ) +{ + + if (EQUAL(pszCap,OLCRandomRead)) + return TRUE; + + else if (EQUAL(pszCap,OLCFastFeatureCount)) + return m_pOGRFilterGeometry == NULL && m_wstrWhereClause.size() == 0; + + else if (EQUAL(pszCap,OLCFastSpatialFilter)) + return TRUE; + + else if (EQUAL(pszCap,OLCFastGetExtent)) + return m_pOGRFilterGeometry == NULL && m_wstrWhereClause.size() == 0; + + else if (EQUAL(pszCap,OLCCreateField)) /* CreateField() */ + return m_pDS->GetUpdate(); + + else if (EQUAL(pszCap,OLCSequentialWrite)) /* ICreateFeature() */ + return m_pDS->GetUpdate(); + + else if (EQUAL(pszCap,OLCStringsAsUTF8)) /* Native UTF16, converted to UTF8 */ + return TRUE; + + else if (EQUAL(pszCap,OLCReorderFields)) /* TBD ReorderFields() */ + return m_pDS->GetUpdate(); + + else if (EQUAL(pszCap,OLCDeleteFeature)) /* DeleteFeature() */ + return m_pDS->GetUpdate(); + + else if (EQUAL(pszCap,OLCRandomWrite)) /* ISetFeature() */ + return m_pDS->GetUpdate(); + + else if (EQUAL(pszCap,OLCDeleteField)) /* DeleteField() */ + return m_pDS->GetUpdate(); + +#ifdef AlterFieldDefn_implemented_but_not_working + else if (EQUAL(pszCap,OLCAlterFieldDefn)) /* AlterFieldDefn() */ + return m_pDS->GetUpdate(); +#endif + + else if (EQUAL(pszCap,OLCFastSetNextByIndex)) /* TBD FastSetNextByIndex() */ + return FALSE; + + else if (EQUAL(pszCap,OLCTransactions)) /* TBD Start/End Transactions() */ + return FALSE; + + else if( EQUAL(pszCap,OLCIgnoreFields) ) + return TRUE; + + else + return FALSE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbResultLayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbResultLayer.cpp new file mode 100644 index 000000000..cd26dcbd5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbResultLayer.cpp @@ -0,0 +1,164 @@ +/****************************************************************************** +* +* Project: OpenGIS Simple Features Reference Implementation +* Purpose: Implements FileGDB OGR result layer. +* Author: Even Rouault, +* +****************************************************************************** + * Copyright (c) 2012, Even Rouault +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +****************************************************************************/ + +#include "ogr_fgdb.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "FGdbUtils.h" + +using std::string; +using std::wstring; + +/************************************************************************/ +/* FGdbResultLayer() */ +/************************************************************************/ +FGdbResultLayer::FGdbResultLayer(FGdbDataSource* pParentDataSource, + const char* pszSQL, + EnumRows* pEnumRows) +{ + m_pFeatureDefn = new OGRFeatureDefn("result"); + SetDescription( m_pFeatureDefn->GetName() ); + m_pFeatureDefn->Reference(); + m_pEnumRows = pEnumRows; + m_pDS = pParentDataSource; + osSQL = pszSQL; + + m_suppressColumnMappingError = false; + + FieldInfo fieldInfo; + m_pEnumRows->GetFieldInformation(fieldInfo); + + int fieldCount; + fieldInfo.GetFieldCount(fieldCount); + for (int i = 0; i < fieldCount; i++) + { + FieldType fieldType; + string strFieldType; + wstring fieldName; + fieldInfo.GetFieldType(i, fieldType); + fieldInfo.GetFieldName(i, fieldName); + + OGRFieldType eType = OFTString; + int bSkip = FALSE; + + switch(fieldType) + { + case fieldTypeSmallInteger: + case fieldTypeInteger: + eType = OFTInteger; + break; + + case fieldTypeSingle: + eType = OFTReal; + strFieldType = "esriFieldTypeSingle"; + break; + + case fieldTypeDouble: + eType = OFTReal; + break; + + case fieldTypeString: + eType = OFTString; + break; + + case fieldTypeDate: + eType = OFTDateTime; + break; + + case fieldTypeOID: + bSkip = TRUE; + break; + + case fieldTypeGeometry: + bSkip = TRUE; + break; + + case fieldTypeBlob: + eType = OFTBinary; + break; + + case fieldTypeRaster: + bSkip = TRUE; + break; + + case fieldTypeGUID: + break; + + case fieldTypeGlobalID: + break; + + case fieldTypeXML: + break; + + default: + CPLAssert(FALSE); + break; + } + + if (!bSkip) + { + OGRFieldDefn oFieldDefn(WStringToString(fieldName).c_str(), eType); + m_pFeatureDefn->AddFieldDefn(&oFieldDefn); + + m_vOGRFieldToESRIField.push_back(fieldName); + m_vOGRFieldToESRIFieldType.push_back( strFieldType ); + } + } +} + +/************************************************************************/ +/* ~FGdbResultLayer() */ +/************************************************************************/ + +FGdbResultLayer::~FGdbResultLayer() +{ +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void FGdbResultLayer::ResetReading() +{ + m_pEnumRows->Close(); + long hr; + if (FAILED(hr = m_pDS->GetGDB()->ExecuteSQL( + StringToWString(osSQL), true, *m_pEnumRows))) + { + GDBErr(hr, CPLSPrintf("Failed at executing '%s'", osSQL.c_str())); + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int FGdbResultLayer::TestCapability( const char * ) +{ + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbUtils.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbUtils.cpp new file mode 100644 index 000000000..98c38f0be --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbUtils.cpp @@ -0,0 +1,623 @@ +/****************************************************************************** +* $Id: FGdbUtils.cpp 28573 2015-02-27 18:13:19Z rouault $ +* +* Project: OpenGIS Simple Features Reference Implementation +* Purpose: Different utility functions used in FileGDB OGR driver. +* Author: Ragi Yaser Burhum, ragi@burhum.com +* Paul Ramsey, pramsey at cleverelephant.ca +* +****************************************************************************** +* Copyright (c) 2010, Ragi Yaser Burhum +* Copyright (c) 2011, Paul Ramsey + * Copyright (c) 2011-2014, Even Rouault +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +****************************************************************************/ + +#include "FGdbUtils.h" +#include + +#include "ogr_api.h" +#include "ogrpgeogeometry.h" + +CPL_CVSID("$Id: FGdbUtils.cpp 28573 2015-02-27 18:13:19Z rouault $"); + +using std::string; + +/*************************************************************************/ +/* StringToWString() */ +/*************************************************************************/ + +std::wstring StringToWString(const std::string& utf8string) +{ + wchar_t* pszUTF16 = CPLRecodeToWChar( utf8string.c_str(), CPL_ENC_UTF8, CPL_ENC_UCS2); + std::wstring utf16string = pszUTF16; + CPLFree(pszUTF16); + return utf16string; +} + +/*************************************************************************/ +/* WStringToString() */ +/*************************************************************************/ + +std::string WStringToString(const std::wstring& utf16string) +{ + char* pszUTF8 = CPLRecodeFromWChar( utf16string.c_str(), CPL_ENC_UCS2, CPL_ENC_UTF8 ); + std::string utf8string = pszUTF8; + CPLFree(pszUTF8); + return utf8string; +} + +/*************************************************************************/ +/* GDBErr() */ +/*************************************************************************/ + +bool GDBErr(long int hr, std::string desc, CPLErr errType, const char* pszAddMsg) +{ + std::wstring fgdb_error_desc_w; + fgdbError er; + er = FileGDBAPI::ErrorInfo::GetErrorDescription(hr, fgdb_error_desc_w); + if ( er == S_OK ) + { + std::string fgdb_error_desc = WStringToString(fgdb_error_desc_w); + CPLError( errType, CPLE_AppDefined, + "%s (%s)%s", desc.c_str(), fgdb_error_desc.c_str(), pszAddMsg); + } + else + { + CPLError( errType, CPLE_AppDefined, + "Error (%ld): %s%s", hr, desc.c_str(), pszAddMsg); + } + // FIXME? EvenR: not sure if ClearErrors() is really necessary, but as it, it causes crashes in case of + // repeated errors + //FileGDBAPI::ErrorInfo::ClearErrors(); + + return false; +} + +/*************************************************************************/ +/* GDBDebug() */ +/*************************************************************************/ + +bool GDBDebug(long int hr, std::string desc) +{ + std::wstring fgdb_error_desc_w; + fgdbError er; + er = FileGDBAPI::ErrorInfo::GetErrorDescription(hr, fgdb_error_desc_w); + if ( er == S_OK ) + { + std::string fgdb_error_desc = WStringToString(fgdb_error_desc_w); + CPLDebug("FGDB", "%s (%s)", desc.c_str(), fgdb_error_desc.c_str()); + } + else + { + CPLDebug("FGDB", "%s", desc.c_str()); + } + // FIXME? EvenR: not sure if ClearErrors() is really necessary, but as it, it causes crashes in case of + // repeated errors + //FileGDBAPI::ErrorInfo::ClearErrors(); + + return false; +} + +/*************************************************************************/ +/* GDBToOGRGeometry() */ +/*************************************************************************/ + +bool GDBToOGRGeometry(string geoType, bool hasZ, OGRwkbGeometryType* pOut) +{ + if (geoType == "esriGeometryPoint") + { + *pOut = hasZ? wkbPoint25D : wkbPoint; + } + else if (geoType == "esriGeometryMultipoint") + { + *pOut = hasZ? wkbMultiPoint25D : wkbMultiPoint; + } + else if (geoType == "esriGeometryLine") + { + *pOut = hasZ? wkbLineString25D : wkbLineString; + } + else if (geoType == "esriGeometryPolyline") + { + *pOut = hasZ? wkbMultiLineString25D : wkbMultiLineString; + } + else if (geoType == "esriGeometryPolygon" || + geoType == "esriGeometryMultiPatch") + { + *pOut = hasZ? wkbMultiPolygon25D : wkbMultiPolygon; // no mapping to single polygon + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot map esriGeometryType(%s) to OGRwkbGeometryType", geoType.c_str()); + return false; + } + + return true; +} + +/*************************************************************************/ +/* OGRGeometryToGDB() */ +/*************************************************************************/ + +bool OGRGeometryToGDB(OGRwkbGeometryType ogrType, std::string *gdbType, bool *hasZ) +{ + switch (ogrType) + { + /* 3D forms */ + case wkbPoint25D: + { + *gdbType = "esriGeometryPoint"; + *hasZ = true; + break; + } + + case wkbMultiPoint25D: + { + *gdbType = "esriGeometryMultipoint"; + *hasZ = true; + break; + } + + case wkbLineString25D: + case wkbMultiLineString25D: + { + *gdbType = "esriGeometryPolyline"; + *hasZ = true; + break; + } + + case wkbPolygon25D: + case wkbMultiPolygon25D: + { + *gdbType = "esriGeometryPolygon"; + *hasZ = true; + break; + } + + /* 2D forms */ + case wkbPoint: + { + *gdbType = "esriGeometryPoint"; + *hasZ = false; + break; + } + + case wkbMultiPoint: + { + *gdbType = "esriGeometryMultipoint"; + *hasZ = false; + break; + } + + case wkbLineString: + case wkbMultiLineString: + { + *gdbType = "esriGeometryPolyline"; + *hasZ = false; + break; + } + + case wkbPolygon: + case wkbMultiPolygon: + { + *gdbType = "esriGeometryPolygon"; + *hasZ = false; + break; + } + + default: + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot map OGRwkbGeometryType (%s) to ESRI type", + OGRGeometryTypeToName(ogrType)); + return false; + } + } + return true; +} + +/*************************************************************************/ +/* GDBToOGRFieldType() */ +/*************************************************************************/ + +// We could make this function far more robust by doing automatic coertion of types, +// and/or skipping fields we do not know. But our purposes this works fine +bool GDBToOGRFieldType(std::string gdbType, OGRFieldType* pOut, OGRFieldSubType* pSubType) +{ + /* + ESRI types + esriFieldTypeSmallInteger = 0, + esriFieldTypeInteger = 1, + esriFieldTypeSingle = 2, + esriFieldTypeDouble = 3, + esriFieldTypeString = 4, + esriFieldTypeDate = 5, + esriFieldTypeOID = 6, + esriFieldTypeGeometry = 7, + esriFieldTypeBlob = 8, + esriFieldTypeRaster = 9, + esriFieldTypeGUID = 10, + esriFieldTypeGlobalID = 11, + esriFieldTypeXML = 12 + */ + + //OGR Types + + // Desc Name GDB->OGR Mapped By Us? + /** Simple 32bit integer */// OFTInteger = 0, YES + /** List of 32bit integers */// OFTIntegerList = 1, NO + /** Double Precision floating point */// OFTReal = 2, YES + /** List of doubles */// OFTRealList = 3, NO + /** String of ASCII chars */// OFTString = 4, YES + /** Array of strings */// OFTStringList = 5, NO + /** deprecated */// OFTWideString = 6, NO + /** deprecated */// OFTWideStringList = 7, NO + /** Raw Binary data */// OFTBinary = 8, YES + /** Date */// OFTDate = 9, NO + /** Time */// OFTTime = 10, NO + /** Date and Time */// OFTDateTime = 11 YES + + *pSubType = OFSTNone; + if (gdbType == "esriFieldTypeSmallInteger" ) + { + *pSubType = OFSTInt16; + *pOut = OFTInteger; + return true; + } + else if (gdbType == "esriFieldTypeInteger") + { + *pOut = OFTInteger; + return true; + } + else if (gdbType == "esriFieldTypeSingle" ) + { + *pSubType = OFSTFloat32; + *pOut = OFTReal; + return true; + } + else if (gdbType == "esriFieldTypeDouble") + { + *pOut = OFTReal; + return true; + } + else if (gdbType == "esriFieldTypeGUID" || + gdbType == "esriFieldTypeGlobalID" || + gdbType == "esriFieldTypeXML" || + gdbType == "esriFieldTypeString") + { + *pOut = OFTString; + return true; + } + else if (gdbType == "esriFieldTypeDate") + { + *pOut = OFTDateTime; + return true; + } + else if (gdbType == "esriFieldTypeBlob") + { + *pOut = OFTBinary; + return true; + } + else + { + /* Intentionally fail at these + esriFieldTypeOID + esriFieldTypeGeometry + esriFieldTypeRaster + */ + CPLError( CE_Warning, CPLE_AppDefined, "%s", ("Cannot map field " + gdbType).c_str()); + + return false; + } +} + +/*************************************************************************/ +/* OGRToGDBFieldType() */ +/*************************************************************************/ + +bool OGRToGDBFieldType(OGRFieldType ogrType, OGRFieldSubType eSubType, std::string* gdbType) +{ + switch(ogrType) + { + case OFTInteger: + { + if( eSubType == OFSTInt16 ) + *gdbType = "esriFieldTypeSmallInteger"; + else + *gdbType = "esriFieldTypeInteger"; + break; + } + case OFTReal: + case OFTInteger64: + { + if( eSubType == OFSTFloat32 ) + *gdbType = "esriFieldTypeSingle"; + else + *gdbType = "esriFieldTypeDouble"; + break; + } + case OFTString: + { + *gdbType = "esriFieldTypeString"; + break; + } + case OFTBinary: + { + *gdbType = "esriFieldTypeBlob"; + break; + } + case OFTDate: + case OFTDateTime: + { + *gdbType = "esriFieldTypeDate"; + break; + } + default: + { + CPLError( CE_Warning, CPLE_AppDefined, + "Cannot map OGR field type (%s)", + OGR_GetFieldTypeName(ogrType) ); + return false; + } + } + + return true; +} + +/*************************************************************************/ +/* GDBFieldTypeToWidthPrecision() */ +/*************************************************************************/ + +bool GDBFieldTypeToWidthPrecision(std::string &gdbType, int *width, int *precision) +{ + *precision = 0; + + /* Width (Length in FileGDB terms) based on FileGDB_API/samples/XMLsamples/OneOfEachFieldType.xml */ + /* Length is in bytes per doc of FileGDB_API/xmlResources/FileGDBAPI.xsd */ + if(gdbType == "esriFieldTypeSmallInteger" ) + { + *width = 2; + } + else if(gdbType == "esriFieldTypeInteger" ) + { + *width = 4; + } + else if(gdbType == "esriFieldTypeSingle" ) + { + *width = 4; + *precision = 5; // FIXME ? + } + else if(gdbType == "esriFieldTypeDouble" ) + { + *width = 8; + *precision = 15; // FIXME ? + } + else if(gdbType == "esriFieldTypeString" || + gdbType == "esriFieldTypeXML") + { + *width = atoi(CPLGetConfigOption("FGDB_STRING_WIDTH", "65536")); + } + else if(gdbType == "esriFieldTypeDate" ) + { + *width = 8; + } + else if(gdbType == "esriFieldTypeOID" ) + { + *width = 4; + } + else if(gdbType == "esriFieldTypeGUID" ) + { + *width = 16; + } + else if(gdbType == "esriFieldTypeBlob" ) + { + *width = 0; + } + else if(gdbType == "esriFieldTypeGlobalID" ) + { + *width = 38; + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Cannot map ESRI field type (%s)", gdbType.c_str()); + return false; + } + + return true; +} + +/*************************************************************************/ +/* GDBFieldTypeToWidthPrecision() */ +/*************************************************************************/ + +bool GDBGeometryToOGRGeometry(bool forceMulti, FileGDBAPI::ShapeBuffer* pGdbGeometry, + OGRSpatialReference* pOGRSR, OGRGeometry** ppOutGeometry) +{ + + OGRGeometry* pOGRGeometry = NULL; + + OGRErr eErr = OGRCreateFromShapeBin( pGdbGeometry->shapeBuffer, + &pOGRGeometry, + pGdbGeometry->inUseLength); + + //OGRErr eErr = OGRGeometryFactory::createFromWkb(pGdbGeometry->shapeBuffer, pOGRSR, &pOGRGeometry, pGdbGeometry->inUseLength ); + + if (eErr != OGRERR_NONE) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed attempting to import GDB WKB Geometry. OGRGeometryFactory err:%d", eErr); + return false; + } + + if( pOGRGeometry != NULL ) + { + // force geometries to multi if requested + + // If it is a polygon, force to MultiPolygon since we always produce multipolygons + if (wkbFlatten(pOGRGeometry->getGeometryType()) == wkbPolygon) + { + pOGRGeometry = OGRGeometryFactory::forceToMultiPolygon(pOGRGeometry); + } + else if (forceMulti) + { + if (wkbFlatten(pOGRGeometry->getGeometryType()) == wkbLineString) + { + pOGRGeometry = OGRGeometryFactory::forceToMultiLineString(pOGRGeometry); + } + else if (wkbFlatten(pOGRGeometry->getGeometryType()) == wkbPoint) + { + pOGRGeometry = OGRGeometryFactory::forceToMultiPoint(pOGRGeometry); + } + } + + if (pOGRGeometry) + pOGRGeometry->assignSpatialReference( pOGRSR ); + } + + + *ppOutGeometry = pOGRGeometry; + + return true; +} + +/*************************************************************************/ +/* GDBToOGRSpatialReference() */ +/*************************************************************************/ + +bool GDBToOGRSpatialReference(const string & wkt, OGRSpatialReference** ppSR) +{ + if (wkt.size() <= 0) + { + CPLError( CE_Warning, CPLE_AppDefined, "ESRI Spatial Reference is NULL"); + return false; + } + + *ppSR = new OGRSpatialReference(wkt.c_str()); + + OGRErr result = (*ppSR)->morphFromESRI(); + + if (result == OGRERR_NONE) + { + return true; + } + else + { + delete *ppSR; + *ppSR = NULL; + + CPLError( CE_Failure, CPLE_AppDefined, + "Failed morhping from ESRI Geometry: %s", wkt.c_str()); + + return false; + } +} + +/*************************************************************************/ +/* FGDB_CPLAddXMLAttribute() */ +/*************************************************************************/ + +/* Utility method for attributing nodes */ +void FGDB_CPLAddXMLAttribute(CPLXMLNode* node, const char* attrname, const char* attrvalue) +{ + if ( !node ) return; + CPLCreateXMLNode( CPLCreateXMLNode( node, CXT_Attribute, attrname ), CXT_Text, attrvalue ); +} + +/*************************************************************************/ +/* FGDBLaunderName() */ +/*************************************************************************/ + +std::string FGDBLaunderName(const std::string name) +{ + std::string newName = name; + + if ( newName[0]>='0' && newName[0]<='9' ) + { + newName = "_" + newName; + } + + for(size_t i=0; i < newName.size(); i++) + { + if ( !( newName[i] == '_' || + ( newName[i]>='0' && newName[i]<='9') || + ( newName[i]>='a' && newName[i]<='z') || + ( newName[i]>='A' && newName[i]<='Z') )) + { + newName[i] = '_'; + } + } + + return newName; +} + +/*************************************************************************/ +/* FGDBEscapeUnsupportedPrefixes() */ +/*************************************************************************/ + +std::string FGDBEscapeUnsupportedPrefixes(const std::string className) +{ + std::string newName = className; + // From ESRI docs + // Feature classes starting with these strings are unsupported. + static const char* UNSUPPORTED_PREFIXES[] = {"sde_", "gdb_", "delta_", NULL}; + + for (int i = 0; UNSUPPORTED_PREFIXES[i] != NULL; i++) + { + if (newName.find(UNSUPPORTED_PREFIXES[i]) == 0) + { + newName = "_" + newName; + break; + } + } + + return newName; +} + +/*************************************************************************/ +/* FGDBEscapeReservedKeywords() */ +/*************************************************************************/ + +std::string FGDBEscapeReservedKeywords(const std::string name) +{ + std::string newName = name; + std::string upperName = name; + std::transform(upperName.begin(), upperName.end(), upperName.begin(), ::toupper); + + // From ESRI docs + static const char* RESERVED_WORDS[] = {FGDB_OID_NAME, "ADD", "ALTER", "AND", "AS", "ASC", "BETWEEN", + "BY", "COLUMN", "CREATE", "DATE", "DELETE", "DESC", + "DROP", "EXISTS", "FOR", "FROM", "IN", "INSERT", "INTO", + "IS", "LIKE", "NOT", "NULL", "OR", "ORDER", "SELECT", + "SET", "TABLE", "UPDATE", "VALUES", "WHERE", NULL}; + + // Append an underscore to any FGDB reserved words used as field names + // This is the same behaviour ArcCatalog follows. + for (int i = 0; RESERVED_WORDS[i] != NULL; i++) + { + const char* w = RESERVED_WORDS[i]; + if (upperName == w) + { + newName += '_'; + break; + } + } + + return newName; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbUtils.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbUtils.h new file mode 100644 index 000000000..215eb9d53 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/FGdbUtils.h @@ -0,0 +1,89 @@ +/****************************************************************************** + * $Id: FGdbUtils.h 28573 2015-02-27 18:13:19Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Various FileGDB OGR Datasource utility functions + * Author: Ragi Yaser Burhum, ragi@burhum.com + * + ****************************************************************************** + * Copyright (c) 2010, Ragi Yaser Burhum + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + +#ifndef _FGDB_UTILS_H_INCLUDED +#define _FGDB_UTILS_H_INCLUDED + +#include "ogr_fgdb.h" +#include +#include +#include "cpl_minixml.h" + +std::wstring StringToWString(const std::string& s); +std::string WStringToString(const std::wstring& s); + +// +// GDB API to OGR Geometry Mapping +// + +// Type mapping +bool GDBToOGRGeometry(std::string geoType, bool hasZ, OGRwkbGeometryType* pOut); +bool OGRGeometryToGDB(OGRwkbGeometryType ogrType, std::string *gdbType, bool *hasZ); + + +bool GDBToOGRSpatialReference(const std::string & wkt, OGRSpatialReference** ppSR); + +// Feature mapping +bool GDBGeometryToOGRGeometry(bool forceMulti, FileGDBAPI::ShapeBuffer* pGdbGeometry, + OGRSpatialReference* pOGRSR, OGRGeometry** ppOutGeometry); + +//temporary version - until we can parse the full binary format +bool GhettoGDBGeometryToOGRGeometry(bool forceMulti, FileGDBAPI::ShapeBuffer* pGdbGeometry, + OGRSpatialReference* pOGRSR, OGRGeometry** ppOutGeometry); +// +// GDB API to OGR Field Mapping +// +bool GDBToOGRFieldType(std::string gdbType, OGRFieldType* ogrType, OGRFieldSubType* pSubType); +bool OGRToGDBFieldType(OGRFieldType ogrType, OGRFieldSubType eSubType, std::string* gdbType); + +// +// GDB Field Width defaults +// +bool GDBFieldTypeToWidthPrecision(std::string &gdbType, int *width, int *precision); + +// +// GDBAPI error to OGR +// +bool GDBErr(long hr, std::string desc, CPLErr errType = CE_Failure, const char* pszAddMsg = ""); +bool GDBDebug(long hr, std::string desc); + +// +// Utility for adding attributes to CPL nodes +// +void FGDB_CPLAddXMLAttribute(CPLXMLNode* node, const char* attrname, const char* attrvalue); + +// +// Utility for escaping reserved words and cleaning field names +// +std::string FGDBLaunderName(const std::string name); +std::string FGDBEscapeUnsupportedPrefixes(const std::string className); +std::string FGDBEscapeReservedKeywords(const std::string name); + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/GNUmakefile new file mode 100644 index 000000000..d1c0a7449 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = FGdbDatasource.o FGdbDriver.o FGdbLayer.o FGdbUtils.o FGdbResultLayer.o + +CPPFLAGS := -I../generic $(FGDB_INC) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_fgdb.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/drv_filegdb.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/drv_filegdb.html new file mode 100644 index 000000000..17a13a835 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/drv_filegdb.html @@ -0,0 +1,124 @@ + + +ESRI File Geodatabase (FileGDB) + + + + +

    ESRI File Geodatabase (FileGDB)

    + +

    The FileGDB driver provides read and write access to File Geodatabases (.gdb directories) created by ArcGIS 10 and above.

    + +

    Note : starting with OGR 1.11, the OpenFileGDB driver driver exists as an alternative built-in +i.e. not depending on a third-party library) read-only driver.

    + +

    Requirements

    + +

    +

    +

    + +

    Bulk feature loading (OGR >= 1.9.2)

    + +The FGDB_BULK_LOAD configuration option can be set to YES to speed-up feature insertion (or sometimes solve problems +when inserting a lot of features (see http://trac.osgeo.org/gdal/ticket/4420). The effect of this configuration option +is to cause a write lock to be taken and a temporary disabling of the indexes. Those are restored when the datasource is +closed or when a read operation is done.

    + +Starting with GDAL 2.0, bulk load is enabled by default for newly created layers (unless otherwise specified). + +

    SQL support (OGR >= 1.10)

    + +Starting with OGR 1.10, SQL statements are run through the SQL engine of the FileGDB SDK API. This holds for non-SELECT +statements. However, due to partial/inaccurate support for SELECT statements in current FileGDB SDK API versions (v1.2), +SELECT statements will be run by default by the OGR SQL engine. This can be changed by specifying the +-dialect FileGDB option to ogrinfo or ogr2ogr. + +

    Special SQL requests

    + +"GetLayerDefinition a_layer_name" and "GetLayerMetadata a_layer_name" can be used as special SQL requests to get +respectively the definition and metadata of a FileGDB table as XML content. + +

    Transaction support (OGR >= 2.0)

    + +The FileGDB driver implements transactions at the database level, through an +emulation (as per RFC 54), +since the FileGDB SDK itself does not offer it. This works by backing up the +current state of a geodatabase when StartTransaction(force=TRUE) is called. +If the transaction is committed, the backup copy is destroyed. If the transaction +is rolled back, the backup copy is restored. So this might be costly when operating +on huge geodatabases.

    + +Note that this emulation has an unspecified behaviour in case of concurrent updates +(with different connections in the same or another process).

    + +

    Dataset Creation Options

    + +

    None.

    + +

    Layer Creation Options

    + +
      +
    • FEATURE_DATASET: When this option is set, the new layer will be created inside the named FeatureDataset folder. If the folder does not already exist, it will be created.
    • +
    • GEOMETRY_NAME: Set name of geometry column in new layer. Defaults to "SHAPE".
    • +
    • GEOMETRY_NULLABLE: (GDAL >=2.0) Whether the values of the geometry column can be NULL. Can be set to NO so that geometry is required. Default to "YES"
    • +
    • FID: Name of the OID column to create. Defaults to "OBJECTID". Note: option was called OID_NAME in releases before GDAL 2
    • +
    • XYTOLERANCE, ZTOLERANCE: These parameters control the snapping tolerance used for advanced ArcGIS features like network and topology rules. They won't effect any OGR operations, but they will by used by ArcGIS. The units of the parameters are the units of the coordinate reference system. +

      ArcMap 10.0 and OGR defaults for XYTOLERANCE are 0.001m (or equivalent) for projected coordinate systems, and 0.000000008983153° for geographic coordinate systems.

    • +
    • XORIGIN, YORIGIN, ZORIGIN, XYSCALE, ZSCALE: These parameters control the coordinate precision grid inside the file geodatabase. The dimensions of the grid are determined by the origin, and the scale. The origin defines the location of a reference grid point in space. The scale is the reciprocal of the resolution. So, to get a grid with an origin at 0 and a resolution of 0.001 on all axes, you would set all the origins to 0 and all the scales to 1000. +

      Important: The domain specified by (xmin=XORIGIN, ymin=YORIGIN, xmax=(XORIGIN + 9E+15 / XYSCALE), ymax=(YORIGIN + 9E+15 / XYSCALE)) needs to encompass every possible coordinate value for the feature class. If features are added with coordinates that fall outside the domain, errors will occur in ArcGIS with spatial indexing, feature selection, and exporting data.

      +

      ArcMap 10.0 and OGR defaults:

        +
      • For geographic coordinate systems: XORIGIN=-400, YORIGIN=-400, XYSCALE=1000000000
      • +
      • For projected coordinate systems: XYSCALE=10000 for the default XYTOLERANCE of 0.001m. XORIGIN and YORIGIN change based on the coordinate system, but the OGR default of -2147483647 is suitable with the default XYSCALE for all coordinate systems.

    • +
    • XML_DEFINITION : (GDAL >= 1.10) When this option is set, its value will be used as the XML definition to create the new table. The root node of such a XML definition must be a <esri:DataElement> element conformant to FileGDBAPI.xsd
    • +
    • CREATE_MULTIPATCH=YES : (GDAL >= 1.11) When this option is set, geometries of layers of type MultiPolygon will be written as MultiPatch
    • +
    • CONFIGURATION_KEYWORD=DEFAULTS/TEXT_UTF16/MAX_FILE_SIZE_4GB/MAX_FILE_SIZE_256TB/GEOMETRY_OUTOFLINE/BLOB_OUTOFLINE/GEOMETRY_AND_BLOB_OUTOFLINE : (GDAL >= 2.0) Customize how data is stored. By default text in UTF-8 and data up to 1TB
    • +
    + +

    Examples

    + +
      +
    • Read layer from FileGDB and load into PostGIS:
    • + ogr2ogr -overwrite -skipfailures -f "PostgreSQL" PG:"host=myhost user=myuser dbname=mydb password=mypass" "C:\somefolder\BigFileGDB.gdb" "MyFeatureClass" + +
    • Get detailed info for FileGDB:
    • + ogrinfo -al "C:\somefolder\MyGDB.gdb" + +
    + +

    Building Notes

    + +

    Read the GDAL Windows Building example for Plugins. You will find a similar section in nmake.opt for FileGDB. After you are done, go to the $gdal_source_root\ogr\ogrsf_frmts\filegdb folder and execute:

    + +

    + + nmake /f makefile.vc plugin + nmake /f makefile.vc plugin-install + +

    + + +

    Known Issues

    + +
      +
    • The SDK is known to be unable to open layers with particular spatialy reference systems. + This might be the case if messages "FGDB: Error opening XXXXXXX. Skipping it (Invalid function arguments.)" + when running "ogrinfo --debug on the.gdb" (reported as warning in GDAL 2.0). + Using the OpenFileGDB driver will generally solve that issue.
    • +
    • Blob fields have not been implemented.
    • +
    • FGDB coordinate snapping will cause geometries to be altered during writing. Use the origin and scale layer creation options to control the snapping behavior.
    • +
    • Driver can't read data from compressed feature classes (SDC, Smart Data Compression) because operation is not supported by the ESRI SDK.
    • +
    + +

    Links

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/makefile.vc new file mode 100644 index 000000000..aa3332a1a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/makefile.vc @@ -0,0 +1,33 @@ +OBJ = FGdbDriver.obj FGdbDatasource.obj FGdbLayer.obj FGdbUtils.obj FGdbResultLayer.obj +EXTRAFLAGS = -I.. -I..\generic -I..\.. -I$(FGDB_INC) + +GDAL_ROOT = ..\..\.. + +PLUGIN_DLL = ogr_FileGDB.dll + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + $(INSTALL) *.obj ..\o + +all: default + +clean: + -del *.obj + -del *.dll + -del *.exp + -del *.lib + -del *.manifest + -del *.pdb + -del *.tlh + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) $(GDAL_ROOT)/gdal_i.lib $(FGDB_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/ogr_fgdb.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/ogr_fgdb.h new file mode 100644 index 000000000..039085b0b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/filegdb/ogr_fgdb.h @@ -0,0 +1,340 @@ +/****************************************************************************** +* $Id: ogr_fgdb.h 29330 2015-06-14 12:11:11Z rouault $ +* +* Project: OpenGIS Simple Features Reference Implementation +* Purpose: Standard includes and class definitions ArcObjects OGR driver. +* Author: Ragi Yaser Burhum, ragi@burhum.com +* +****************************************************************************** +* Copyright (c) 2009, Ragi Yaser Burhum + * Copyright (c) 2011-2014, Even Rouault +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +****************************************************************************/ + +#ifndef _OGR_FGDB_H_INCLUDED +#define _OGR_FGDB_H_INCLUDED + +#include +#include "ogrsf_frmts.h" +#include "ogremulatedtransaction.h" + +/* GDAL string utilities */ +#include "cpl_string.h" + +/* GDAL XML handler */ +#include "cpl_minixml.h" + +/* FGDB API headers */ +#include "FileGDBAPI.h" + +/* Workaround needed for Linux, at least for FileGDB API 1.1 (#4455) */ +#if defined(__linux__) +#define EXTENT_WORKAROUND +#endif + +/************************************************************************ +* Default layer creation options +*/ + +#define FGDB_FEATURE_DATASET ""; +#define FGDB_GEOMETRY_NAME "SHAPE" +#define FGDB_OID_NAME "OBJECTID" + + +/* The ESRI FGDB API namespace */ +using namespace FileGDBAPI; + +class FGdbDriver; + +/************************************************************************/ +/* FGdbBaseLayer */ +/************************************************************************/ + +class FGdbBaseLayer : public OGRLayer +{ +protected: + + FGdbBaseLayer(); + virtual ~FGdbBaseLayer(); + + OGRFeatureDefn* m_pFeatureDefn; + OGRSpatialReference* m_pSRS; + + EnumRows* m_pEnumRows; + + std::vector m_vOGRFieldToESRIField; //OGR Field Index to ESRI Field Name Mapping + std::vector m_vOGRFieldToESRIFieldType; //OGR Field Index to ESRI Field Type Mapping + + bool m_suppressColumnMappingError; + bool m_forceMulti; + + bool OGRFeatureFromGdbRow(Row* pRow, OGRFeature** ppFeature); + +public: + virtual OGRFeature* GetNextFeature(); +}; + +/************************************************************************/ +/* FGdbLayer */ +/************************************************************************/ + +class FGdbDataSource; + +class FGdbLayer : public FGdbBaseLayer +{ + friend class FGdbDataSource; + + int m_bBulkLoadAllowed; + int m_bBulkLoadInProgress; + + void StartBulkLoad(); + void EndBulkLoad(); + +#ifdef EXTENT_WORKAROUND + bool m_bLayerJustCreated; + OGREnvelope sLayerEnvelope; + bool m_bLayerEnvelopeValid; + void WorkAroundExtentProblem(); + bool UpdateRowWithGeometry(Row& row, OGRGeometry* poGeom); +#endif + + std::vector m_apoByteArrays; + OGRErr PopulateRowWithFeature( Row& row, OGRFeature *poFeature ); + OGRErr GetRow( EnumRows& enumRows, Row& row, GIntBig nFID ); + + char **m_papszOptions; + + int m_bCreateMultipatch; + + char* CreateFieldDefn(OGRFieldDefn& oField, + int bApproxOK, + std::string& fieldname_clean, + std::string& gdbFieldType); + +public: + + FGdbLayer(); + virtual ~FGdbLayer(); + + // Internal used by FGDB driver */ + bool Initialize(FGdbDataSource* pParentDataSource, Table* pTable, std::wstring wstrTablePath, std::wstring wstrType); + bool Create(FGdbDataSource* pParentDataSource, const char * pszLayerName, OGRSpatialReference *poSRS, OGRwkbGeometryType eType, char ** papszOptions); + bool CreateFeatureDataset(FGdbDataSource* pParentDataSource, std::string feature_dataset_name, OGRSpatialReference* poSRS, char** papszOptions ); + + // virtual const char *GetName(); + virtual const char* GetFIDColumn() { return m_strOIDFieldName.c_str(); } + + virtual void ResetReading(); + virtual OGRFeature* GetNextFeature(); + virtual OGRFeature* GetFeature( GIntBig nFeatureId ); + + Table* GetTable() { return m_pTable; } + + std::wstring GetTablePath() const { return m_wstrTablePath; } + std::wstring GetType() const { return m_wstrType; } + + virtual OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK ); + virtual OGRErr DeleteField( int iFieldToDelete ); +#ifdef AlterFieldDefn_implemented_but_not_working + virtual OGRErr AlterFieldDefn( int iFieldToAlter, OGRFieldDefn* poNewFieldDefn, int nFlags ); +#endif + + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + virtual OGRErr GetExtent( OGREnvelope *psExtent, int bForce ); + virtual GIntBig GetFeatureCount( int bForce ); + virtual OGRErr SetAttributeFilter( const char *pszQuery ); + virtual void SetSpatialFilterRect (double dfMinX, double dfMinY, double dfMaxX, double dfMaxY); + virtual void SetSpatialFilter( OGRGeometry * ); + +// virtual OGRErr StartTransaction( ); +// virtual OGRErr CommitTransaction( ); +// virtual OGRErr RollbackTransaction( ); + + OGRFeatureDefn * GetLayerDefn() { return m_pFeatureDefn; } + + virtual int TestCapability( const char * ); + + // Access the XML directly. The 2 following methods are not currently used by the driver, but + // can be used by external code for specific purposes. + OGRErr GetLayerXML ( char **poXml ); + OGRErr GetLayerMetadataXML ( char **poXmlMeta ); + +protected: + + bool GDBToOGRFields(CPLXMLNode* psFields); + bool ParseGeometryDef(CPLXMLNode* psGeometryDef); + bool ParseSpatialReference(CPLXMLNode* psSpatialRefNode, std::string* pOutWkt, + std::string* pOutWKID, std::string* pOutLatestWKID); + + FGdbDataSource* m_pDS; + Table* m_pTable; + + std::string m_strName; //contains underlying FGDB table name (not catalog name) + + std::string m_strOIDFieldName; + std::string m_strShapeFieldName; + + std::wstring m_wstrTablePath; + std::wstring m_wstrType; // the type: "Table" or "Feature Class" + + std::wstring m_wstrSubfields; + std::wstring m_wstrWhereClause; + OGRGeometry* m_pOGRFilterGeometry; + + bool m_bFilterDirty; //optimization to avoid multiple calls to search until necessary + + bool m_bLaunderReservedKeywords; + +}; + +/************************************************************************/ +/* FGdbResultLayer */ +/************************************************************************/ + +class FGdbResultLayer : public FGdbBaseLayer +{ +public: + + FGdbResultLayer(FGdbDataSource* pParentDataSource, const char* pszStatement, EnumRows* pEnumRows); + virtual ~FGdbResultLayer(); + + virtual void ResetReading(); + + OGRFeatureDefn * GetLayerDefn() { return m_pFeatureDefn; } + + virtual int TestCapability( const char * ); + +protected: + + FGdbDataSource* m_pDS; + CPLString osSQL; +}; + +/************************************************************************/ +/* FGdbDataSource */ +/************************************************************************/ + +class FGdbDatabaseConnection; + +class FGdbDataSource : public OGRDataSource +{ + +public: + FGdbDataSource(FGdbDriver* poDriver, FGdbDatabaseConnection* pConnection); + virtual ~FGdbDataSource(); + + int Open(const char *, int ); + + const char* GetName() { return m_pszName; } + int GetLayerCount() { return static_cast(m_layers.size()); } + + OGRLayer* GetLayer( int ); + + virtual OGRLayer* ICreateLayer( const char *, OGRSpatialReference* = NULL, OGRwkbGeometryType = wkbUnknown, char** = NULL ); + + virtual OGRErr DeleteLayer( int ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poResultsSet ); + + int TestCapability( const char * ); + + Geodatabase* GetGDB() { return m_pGeodatabase; } + bool GetUpdate() { return m_bUpdate; } + FGdbDatabaseConnection* GetConnection() { return m_pConnection; } + + /* + protected: + + void EnumerateSpatialTables(); + void OpenSpatialTable( const char* pszTableName ); + */ +protected: + bool LoadLayers(const std::wstring & parent); + bool OpenFGDBTables(const std::wstring &type, + const std::vector &layers); + + FGdbDriver* m_poDriver; + FGdbDatabaseConnection* m_pConnection; + char* m_pszName; + std::vector m_layers; + Geodatabase* m_pGeodatabase; + bool m_bUpdate; +}; + +/************************************************************************/ +/* FGdbDriver */ +/************************************************************************/ + +class FGdbDatabaseConnection +{ +public: + FGdbDatabaseConnection(Geodatabase* pGeodatabase) : + m_pGeodatabase(pGeodatabase), m_nRefCount(1), m_bLocked(FALSE) {} + + Geodatabase* m_pGeodatabase; + int m_nRefCount; + int m_bLocked; + + Geodatabase* GetGDB() { return m_pGeodatabase; } + void SetLocked(int bLockedIn) { m_bLocked = bLockedIn; } + int GetRefCount() const { return m_nRefCount; } + int IsLocked() const { return m_bLocked; } +}; + +class FGdbDriver : public OGRSFDriver, public IOGRTransactionBehaviour +{ + std::map oMapConnections; + CPLMutex* hMutex; + +public: + FGdbDriver(); + virtual ~FGdbDriver(); + + virtual const char *GetName(); + virtual OGRDataSource *Open( const char *, int ); + virtual int TestCapability( const char * ); + virtual OGRDataSource *CreateDataSource( const char *pszName, char ** = NULL); + virtual OGRErr DeleteDataSource( const char *pszDataSource ); + + /* From IOGRTransactionBehaviour */ + virtual OGRErr StartTransaction(OGRDataSource*& poDSInOut, int& bOutHasReopenedDS); + virtual OGRErr CommitTransaction(OGRDataSource*& poDSInOut, int& bOutHasReopenedDS); + virtual OGRErr RollbackTransaction(OGRDataSource*& poDSInOut, int& bOutHasReopenedDS); + + void Release(const char* pszName); + CPLMutex* GetMutex() { return hMutex; } + +private: + +}; + +CPL_C_START +void CPL_DLL RegisterOGRFileGDB(); +CPL_C_END + +#endif /* ndef _OGR_PG_H_INCLUDED */ + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/GNUmakefile new file mode 100644 index 000000000..8e3a8cf69 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/GNUmakefile @@ -0,0 +1,15 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrfmecacheindex.o ogrfmedatasource.o ogrfmedriver.o \ + ogrfmelayer.o ogrfmelayercached.o ogrfmelayerdb.o \ + fme2ogr_utils.o + +CPPFLAGS += -I.. -I../.. $(FME_INCLUDE) \ + -DSUPPORT_INDIRECT_FMEDLL -DSUPPORT_PERSISTENT_CACHE + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/drv_fme.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/drv_fme.html new file mode 100644 index 000000000..b40667915 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/drv_fme.html @@ -0,0 +1,96 @@ + + +FMEObjects Gateway + + + + +

    FMEObjects Gateway

    + +Feature sources supported by FMEObjects are supported for reading by OGR +if the FMEObjects gateway is configured, and if a licensed copy of +FMEObjects is installed and accessable.

    + +To using the FMEObjects based readers the data source name passed should +be the name of the FME reader to use, a colon and then the actual data +source name (ie. the filename). For instance, "NTF:F:\DATA\NTF\2144.NTF" +would indicate the NTF reader should be used to read the file + +There are a number of special cases:

    + +

      +
    • A data source ending in .fdd will be assumed to be an "FME Datasource + Definition" file which will contain the reader name, the data source name, + and then a set of name/value pairs of lines for the macros suitable to pass + to the createReader() call.

      + +

    • A datasource named PROMPT will result in prompting the user for + information using the regular FME dialogs. This only works on Windows.

      + +

    • A datasource named "PROMPT:filename" will result in prompting, and then + having the resulting definition saved to the indicate files in .fdd format. + The .fdd extension will be forced on the filename. This only works on + Windows. +
    + +Each FME feature type will be treated as a layer through OGR, named by the +feature type. With some limitations FME coordinate systems are supported. +All FME geometry types should be properly supported. FME graphical attributes +(color, line width, etc) are not converted into OGR Feature Style information. +

    + +

    Caching

    + +In order to enable fast access to large datasets without having to +retranslate them each time they are accessed, the FMEObjects gateway +supports a mechanism to cache features read from FME readers in +"Fast Feature Stores", a native vector format for FME with a spatial +index for fast spatial searches. These cached files are kept in the +directory indicated by the OGRFME_TMPDIR environment variable (or +TMPDIR or /tmp or C:\ if that is not available).

    + +The cached featur files will have the prefix FME_OLEDB_ and a master index +is kept in the file ogrfmeds.ind. To clear away the index delete all these +files. Do not just delete some.

    + +By default features in the cache are re-read after 3600s (60 minutes). +Cache rentention times can be altered at compile time by altering +the fme2ogr.h include file.

    + +Input from the SDE and ORACLE readers are not cached. These sources are +treated specially in a number of other ways as well.

    + +

    Caveats

    + +
      + +
    1. Establishing an FME session is quite an expensive operation, on a +350Mhz Linux system this can be in exceess of 10s.

      + +

    2. Old files in the feature cache are cleaned up, but only on subsequent +visits to the FMEObjects gateway code in OGR. This means that if unused +the FMEObjects gateway will leave old cached features around indefinately.

      + +

    + +

    Build/Configuration

    + +To include the FMEObjects gateway in an OGR build it is necessary to have +FME loaded on the system. The --with-fme=$FME_HOME configuration +switch should be supplied to configure. The FMEObjects gateway is not +explicitly linked against (it is loaded later when it may be needed) so it +is practical to distribute an OGR binary build with FMEObjects support without +distributing FMEObjects. It will just "work" for people who have FMEObjects +in the path.

    + +The FMEObjects gateway has been tested on Linux and Windows.

    + +


    + +More information on the FME product line, and how to purchase a license for +the FME software (enabling FMEObjects support) can be found on the +Safe Software web site at www.safe.com. +Development of this driver was financially supported by Safe Software.

    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/fme2ogr.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/fme2ogr.h new file mode 100644 index 000000000..d62915cd5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/fme2ogr.h @@ -0,0 +1,294 @@ +/****************************************************************************** + * $Id: fme2ogr.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: FMEObjects Translator + * Purpose: Declarations for translating IFMEFeatures to OGRFeatures. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001 Safe Software Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _FME2OGR_H_INCLUDED +#define _FME2OGR_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_minixml.h" + +#include +#include +#include +#include +#include +#include + +class OGRFMEDataSource; + +void CPLFMEError( IFMESession *, const char *, ... ); + +/************************************************************************/ +/* OGRFMELayer */ +/************************************************************************/ + +class OGRFMELayer : public OGRLayer +{ + protected: + OGRFeatureDefn *poFeatureDefn; + OGRSpatialReference *poSpatialRef; + + OGRFMEDataSource *poDS; + + char *pszAttributeFilter; + + IFMEFeature *poFMEFeature; + + public: + OGRFMELayer( OGRFMEDataSource * ); + virtual ~OGRFMELayer(); + + virtual int Initialize( IFMEFeature *, + OGRSpatialReference * ); + + virtual OGRErr SetAttributeFilter( const char * ); + + OGRSpatialReference *GetSpatialRef(); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } +}; + +/************************************************************************/ +/* OGRFMELayerCached */ +/************************************************************************/ + +class OGRFMELayerCached : public OGRFMELayer +{ + private: + int nPreviousFeature; + + char *pszIndexBase; + IFMESpatialIndex *poIndex; + + OGRFeature * ReadNextIndexFeature(); + + OGREnvelope sExtents; + + int bQueryActive; + + public: + OGRFMELayerCached( OGRFMEDataSource * ); + virtual ~OGRFMELayerCached(); + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + virtual GIntBig GetFeatureCount( int bForce ); + + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); + + int AssignIndex( const char *pszBase, const OGREnvelope*, + OGRSpatialReference *poSRS ); + CPLXMLNode *SerializeToXML(); + int InitializeFromXML( CPLXMLNode * ); +}; + +/************************************************************************/ +/* OGRFMELayerDB */ +/************************************************************************/ + +class OGRFMELayerDB : public OGRFMELayer +{ + private: + int nPreviousFeature; + + IFMEUniversalReader *poReader; + + char *pszReaderName; + char *pszDataset; + IFMEStringArray *poUserDirectives; + + int CreateReader(); + + public: + OGRFMELayerDB( OGRFMEDataSource * poDSIn, + const char *pszReaderName, + const char *pszDataset, + IFMEStringArray *poUserDirectives ); + virtual ~OGRFMELayerDB(); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + virtual GIntBig GetFeatureCount( int bForce ); + + virtual int TestCapability( const char * ); + + void AssignIndex( const char *pszBase ); +}; + +/************************************************************************/ +/* OGRFMEDataSource */ +/************************************************************************/ + +class OGRFMEDataSource : public OGRDataSource +{ + char *pszName; // full name, ie. "SHAPE:D:\DATA" + char *pszReaderName; // reader/driver name, ie. "SHAPE" + char *pszDataset; // FME dataset name, ie. "D:\DATA" + + IFMEStringArray *poUserDirectives; + + IFMESession *poSession; + IFMEUniversalReader *poReader; + + int nLayers; + OGRFMELayer **papoLayers; + + IFMEFeature *poFMEFeature; + + int ReadFMEFeature(); + + IFMEString *poFMEString; + + char *PromptForSource(); + char *ReadFileSource(const char *); + OGRSpatialReference *ExtractSRS(); + int IsPartOfConnectionCache( IFMEUniversalReader * ); + void OfferForConnectionCaching( IFMEUniversalReader *, + const char *, + const char *); + + int bUseCaching; + int bCoordSysOverride; + + void ClarifyGeometryClass( IFMEFeature *poFeature, + OGRwkbGeometryType &eBestGeomType ); + + + public: + OGRFMEDataSource(); + ~OGRFMEDataSource(); + + IFMESession * AcquireSession(); + void ReleaseSession(); + + int TestCapability( const char * ); + + OGRGeometry *ProcessGeometry( OGRFMELayer *, IFMEFeature *, + OGRwkbGeometryType ); + OGRFeature *ProcessFeature( OGRFMELayer *, IFMEFeature * ); + void ResetReading(); + + int Open( const char * ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + IFMESession *GetFMESession() { return poSession; } + IFMEUniversalReader *GetFMEReader() { return poReader; } + + void BuildSpatialIndexes(); + + OGRSpatialReference *FME2OGRSpatialRef( const char *pszFMECoordsys ); + + // Stuff related to persistent feature caches. + CPLXMLNode *SerializeToXML(); + int InitializeFromXML( CPLXMLNode * ); +}; + +/************************************************************************/ +/* OGRFMEDriver */ +/************************************************************************/ + +class OGRFMEDriver : public OGRSFDriver +{ + public: + ~OGRFMEDriver(); + + int TestCapability( const char * ); + + const char *GetName(); + OGRDataSource *Open( const char *, int ); +}; + +/************************************************************************/ +/* OGRFMECacheIndex */ +/************************************************************************/ + +class OGRFMECacheIndex +{ + CPLXMLNode *psTree; // Implicitly locked if this is non-NULL. + + char *pszPath; + void *hLock; + + public: + OGRFMECacheIndex( const char *pszPath ); + ~OGRFMECacheIndex(); + + const char *GetPath() { return pszPath; } + + int Load(); + int Save(); + + CPLXMLNode *FindMatch( const char *pszDriver, + const char *pszDataset, + IFMEStringArray &oUserDirectives ); + + int Lock(); + int Unlock(); + + void Touch( CPLXMLNode * ); + void Add( CPLXMLNode * ); + void Reference( CPLXMLNode * ); + void Dereference( CPLXMLNode * ); + + int ExpireOldCaches( IFMESession * ); +}; + +// The number of seconds an unreferenced spatial cache should be retained in +// the cache index before cleaning up if unused. +// Default: 15 minutes +#ifndef FMECACHE_RETENTION +# define FMECACHE_RETENTION 900 +#endif + +// The number of seconds before a "referenced" data source in the cache +// index is considered to be orphaned due to a process dying or something. +#ifndef FMECACHE_REF_TIMEOUT +# define FMECACHE_REF_TIMEOUT FMECACHE_RETENTION*3 +#endif + +// The number of seconds from creation a spatial cache should be retained in +// the cache index before cleaning it up. +// Default: 1hour +#ifndef FMECACHE_MAX_RETENTION +# define FMECACHE_MAX_RETENTION 3600 +#endif + + +CPL_C_START +void RegisterOGRFME(); +CPL_C_END + +#endif /* ndef _FME2OGR_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/fme2ogr_utils.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/fme2ogr_utils.cpp new file mode 100644 index 000000000..7c71d1146 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/fme2ogr_utils.cpp @@ -0,0 +1,75 @@ +/****************************************************************************** + * $Id: fme2ogr_utils.cpp 12123 2007-09-11 23:57:40Z warmerdam $ + * + * Project: FMEObjects Translator + * Purpose: Various FME related support functions. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001 Safe Software Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "fme2ogr.h" +#include "cpl_conv.h" +#include + +/************************************************************************/ +/* CPLFMEError() */ +/* */ +/* This function takes care of reporting errors through */ +/* CPLError(), but appending the last FME error message. */ +/************************************************************************/ + +void CPLFMEError( IFMESession * poSession, const char *pszFormat, ... ) + +{ + va_list hVaArgs; + char *pszErrorBuf = (char *) CPLMalloc(10000); + +/* -------------------------------------------------------------------- */ +/* Format the error message into a buffer. */ +/* -------------------------------------------------------------------- */ + va_start( hVaArgs, pszFormat ); + vsprintf( pszErrorBuf, pszFormat, hVaArgs ); + va_end( hVaArgs ); + +/* -------------------------------------------------------------------- */ +/* Get the last error message from FME. */ +/* -------------------------------------------------------------------- */ + const char *pszFMEErrorString = poSession->getLastErrorMsg(); + + if( pszFMEErrorString == NULL ) + { + pszFMEErrorString = "FME reports no error message."; + } + +/* -------------------------------------------------------------------- */ +/* Send composite error through CPL, and cleanup. */ +/* -------------------------------------------------------------------- */ + CPLError( CE_Failure, CPLE_AppDefined, + "%s\n%s", + pszErrorBuf, pszFMEErrorString ); + + CPLFree( pszErrorBuf ); +} + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/makefile.vc new file mode 100644 index 000000000..44bf403d4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/makefile.vc @@ -0,0 +1,25 @@ + +OBJ = ogrfmedatasource.obj ogrfmelayer.obj ogrfmelayerdb.obj \ + ogrfmelayercached.obj ogrfmecacheindex.obj ogrfmedriver.obj \ + fme2ogr_utils.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. -I$(FME_DIR) -I$(FME_DIR)\fmeobjects\cpp \ + -DSUPPORT_INDIRECT_FMEDLL \ + /DSUPPORT_PERSISTENT_CACHE \ + /DSUPPORT_CLEANUP_SESSION + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + +gmlview.exe: $(LL_OBJ) gmlview.obj + cl /Zi gmlview.obj $(LL_OBJ) \ + ../../ogr.lib ../ogrsf_frmts.lib ../ogrsf_frmts_sup.lib \ + $(GDAL_ROOT)/port/cpl.lib $(XERCES_LIB) $(LIBS) + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmecacheindex.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmecacheindex.cpp new file mode 100644 index 000000000..3452060c7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmecacheindex.cpp @@ -0,0 +1,480 @@ +/****************************************************************************** + * $Id: ogrfmecacheindex.cpp 12123 2007-09-11 23:57:40Z warmerdam $ + * + * Project: FMEObjects Translator + * Purpose: Implement the OGRFMECacheIndex class, a mechanism to manage a + * persisent index list of cached datasets. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002 Safe Software Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "fme2ogr.h" +#include "cpl_multiproc.h" +#include "cpl_conv.h" +#include "cpl_minixml.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: ogrfmecacheindex.cpp 12123 2007-09-11 23:57:40Z warmerdam $"); + +/************************************************************************/ +/* OGRFMECacheIndex() */ +/************************************************************************/ + +OGRFMECacheIndex::OGRFMECacheIndex( const char * pszPathIn ) + +{ + psTree = NULL; + pszPath = CPLStrdup( pszPathIn ); + hLock = NULL; +} + +/************************************************************************/ +/* ~OGRFMECacheIndex() */ +/************************************************************************/ + +OGRFMECacheIndex::~OGRFMECacheIndex() + +{ + if( psTree != NULL ) + { + Unlock(); + CPLDestroyXMLNode( psTree ); + psTree = NULL; + } + CPLFree( pszPath ); +} + +/************************************************************************/ +/* Lock() */ +/************************************************************************/ + +int OGRFMECacheIndex::Lock() + +{ + if( pszPath == NULL ) + return FALSE; + + hLock = CPLLockFile( pszPath, 5.0 ); + + return hLock != NULL; +} + +/************************************************************************/ +/* Unlock() */ +/************************************************************************/ + +int OGRFMECacheIndex::Unlock() + +{ + if( pszPath == NULL || hLock == NULL ) + return FALSE; + + CPLUnlockFile( hLock ); + hLock = NULL; + + return TRUE; +} + +/************************************************************************/ +/* Load() */ +/************************************************************************/ + +int OGRFMECacheIndex::Load() + +{ +/* -------------------------------------------------------------------- */ +/* Lock the cache index file if not already locked. */ +/* -------------------------------------------------------------------- */ + if( hLock == NULL && !Lock() ) + return FALSE; + + if( psTree != NULL ) + { + CPLDestroyXMLNode( psTree ); + psTree = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Open the index file. If we don't get it, we assume it is */ +/* because it doesn't exist, and we create a "stub" tree in */ +/* memory. */ +/* -------------------------------------------------------------------- */ + FILE *fpIndex; + int nLength; + char *pszIndexBuffer; + + fpIndex = VSIFOpen( GetPath(), "rb" ); + if( fpIndex == NULL ) + { + psTree = CPLCreateXMLNode( NULL, CXT_Element, "OGRFMECacheIndex" ); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Load the data from the file. */ +/* -------------------------------------------------------------------- */ + VSIFSeek( fpIndex, 0, SEEK_END ); + nLength = VSIFTell( fpIndex ); + VSIFSeek( fpIndex, 0, SEEK_SET ); + + pszIndexBuffer = (char *) CPLMalloc(nLength+1); + if( (int) VSIFRead( pszIndexBuffer, 1, nLength, fpIndex ) != nLength ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Read of %d byte index file failed.", nLength ); + return FALSE; + } + VSIFClose( fpIndex ); + +/* -------------------------------------------------------------------- */ +/* Parse the result into an inmemory XML tree. */ +/* -------------------------------------------------------------------- */ + pszIndexBuffer[nLength] = '\0'; + psTree = CPLParseXMLString( pszIndexBuffer ); + + CPLFree( pszIndexBuffer ); + + return psTree != NULL; +} + +/************************************************************************/ +/* Save() */ +/************************************************************************/ + +int OGRFMECacheIndex::Save() + +{ + if( hLock == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Convert the XML tree into one big character buffer, and */ +/* write it out. */ +/* -------------------------------------------------------------------- */ + char *pszIndexBuffer = CPLSerializeXMLTree( psTree ); + + if( pszIndexBuffer == NULL ) + return FALSE; + + FILE *fpIndex = VSIFOpen( GetPath(), "wb" ); + if( fpIndex == NULL ) + return FALSE; + + VSIFWrite( pszIndexBuffer, 1, strlen(pszIndexBuffer), fpIndex ); + CPLFree( pszIndexBuffer ); + + VSIFClose( fpIndex ); + + Unlock(); + + return TRUE; +} + +/************************************************************************/ +/* FindMatch() */ +/* */ +/* Find a DataSource subtree that matches the passed in */ +/* component values. */ +/************************************************************************/ + +CPLXMLNode *OGRFMECacheIndex::FindMatch( const char *pszDriver, + const char *pszDataset, + IFMEStringArray &oUserDirectives ) + +{ + CPLXMLNode *psCDS; + + if( psTree == NULL ) + return NULL; + + for( psCDS = psTree->psChild; psCDS != NULL; psCDS = psCDS->psNext ) + { + if( !EQUAL(pszDriver,CPLGetXMLValue(psCDS,"Driver","")) ) + continue; + + if( !EQUAL(pszDataset,CPLGetXMLValue(psCDS,"DSName","")) ) + continue; + + CPLXMLNode *psDirective; + int bMatch = TRUE; + int iDir; + + psDirective = CPLGetXMLNode( psCDS, "UserDirectives.Directive" ); + for( iDir = 0; + iDir < (int)oUserDirectives.entries() && bMatch; + iDir++ ) + { + if( psDirective == NULL || psDirective->psChild == NULL ) + bMatch = FALSE; + else if( !EQUAL(psDirective->psChild->pszValue, + oUserDirectives(iDir)) ) + bMatch = FALSE; + else + psDirective = psDirective->psNext; + } + + if( iDir < (int) oUserDirectives.entries() || !bMatch + || (psDirective != NULL && psDirective->psNext != NULL) ) + continue; + + return psCDS; + } + + return NULL; +} + +/************************************************************************/ +/* Touch() */ +/* */ +/* Update the LastUseTime on the passed datasource. */ +/************************************************************************/ + +void OGRFMECacheIndex::Touch( CPLXMLNode *psDSNode ) + +{ + if( psDSNode == NULL || !EQUAL(psDSNode->pszValue,"DataSource") ) + return; + +/* -------------------------------------------------------------------- */ +/* Prepare the new time value to use. */ +/* -------------------------------------------------------------------- */ + char szNewTime[32]; + + sprintf( szNewTime, "%lu", (unsigned long) time(NULL) ); + +/* -------------------------------------------------------------------- */ +/* Set or insert LastUseTime into dataset. */ +/* -------------------------------------------------------------------- */ + CPLSetXMLValue( psDSNode, "LastUseTime", szNewTime ); +} + +/************************************************************************/ +/* Reference() */ +/************************************************************************/ + +void OGRFMECacheIndex::Reference( CPLXMLNode *psDSNode ) + +{ + if( psDSNode == NULL || !EQUAL(psDSNode->pszValue,"DataSource") ) + return; + + char szNewRefCount[32]; + + sprintf( szNewRefCount, "%d", + atoi(CPLGetXMLValue(psDSNode, "RefCount", "0")) + 1 ); + + CPLSetXMLValue( psDSNode, "RefCount", szNewRefCount ); + + Touch( psDSNode ); +} + +/************************************************************************/ +/* Dereference() */ +/************************************************************************/ + +void OGRFMECacheIndex::Dereference( CPLXMLNode *psDSNode ) + +{ + if( psDSNode == NULL + || !EQUAL(psDSNode->pszValue,"DataSource") + || CPLGetXMLNode(psDSNode,"RefCount") == NULL ) + return; + + char szNewRefCount[32]; + int nRefCount = atoi(CPLGetXMLValue(psDSNode, "RefCount", "1")); + if( nRefCount < 1 ) + nRefCount = 1; + + sprintf( szNewRefCount, "%d", nRefCount-1 ); + + CPLSetXMLValue( psDSNode, "RefCount", szNewRefCount ); + + Touch( psDSNode ); +} + +/************************************************************************/ +/* Add() */ +/* */ +/* Note that Add() takes over ownership of the passed tree. */ +/************************************************************************/ + +void OGRFMECacheIndex::Add( CPLXMLNode *psDSNode ) + +{ + CPLAssert( psTree != NULL ); + + psDSNode->psNext = psTree->psChild; + psTree->psChild = psDSNode; + + if( psDSNode == NULL || !EQUAL(psDSNode->pszValue,"DataSource") ) + return; + +/* -------------------------------------------------------------------- */ +/* Prepare the creation time value to use. */ +/* -------------------------------------------------------------------- */ + char szNewTime[32]; + + sprintf( szNewTime, "%lu", (unsigned long) time(NULL) ); + +/* -------------------------------------------------------------------- */ +/* Set or insert CreationTime into dataset. */ +/* -------------------------------------------------------------------- */ + CPLSetXMLValue( psDSNode, "CreationTime", szNewTime ); +} + +/************************************************************************/ +/* ExpireOldCaches() */ +/* */ +/* Make a pass over all the cache index entries. Remove (and */ +/* free the associated FME spatial caches) for any entries that */ +/* haven't been touched for a long time. Note that two */ +/* different timeouts apply. One is for layers with a RefCount */ +/* of 0 and the other (longer time) is for those with a */ +/* non-zero refcount. Even if the RefCount is non-zero we */ +/* assume this may because a program crashed during it's run. */ +/************************************************************************/ + +int OGRFMECacheIndex::ExpireOldCaches( IFMESession *poSession ) + +{ + CPLXMLNode *psDSNode, *psLastDSNode = NULL; + unsigned long nCurTime = time(NULL); + int bChangeMade = FALSE; + + if( psTree == NULL ) + return FALSE; + + for( psLastDSNode = NULL; TRUE; psLastDSNode = psDSNode ) + { + if( psLastDSNode != NULL ) + psDSNode = psLastDSNode->psNext; + else + psDSNode = psTree->psChild; + if( psDSNode == NULL ) + break; + + if( !EQUAL(psDSNode->pszValue,"DataSource") ) + continue; + +/* -------------------------------------------------------------------- */ +/* When was this datasource last accessed? */ +/* -------------------------------------------------------------------- */ + unsigned long nLastUseTime = 0; + + sscanf( CPLGetXMLValue( psDSNode, "LastUseTime", "0" ), + "%lu", &nLastUseTime ); + +/* -------------------------------------------------------------------- */ +/* When was this datasource created. */ +/* -------------------------------------------------------------------- */ + unsigned long nCreationTime = 0; + + sscanf( CPLGetXMLValue( psDSNode, "CreationTime", "0" ), + "%lu", &nCreationTime ); + +/* -------------------------------------------------------------------- */ +/* Do we want to delete this datasource according to our */ +/* retention and ref timeout rules? */ +/* -------------------------------------------------------------------- */ + int bCleanup = FALSE; + + // Do we want to cleanup this node? + if( atoi(CPLGetXMLValue( psDSNode, "RefCount", "0" )) > 0 + && nLastUseTime + FMECACHE_REF_TIMEOUT < nCurTime ) + bCleanup = TRUE; + + if( atoi(CPLGetXMLValue( psDSNode, "RefCount", "0" )) < 1 + && nLastUseTime + FMECACHE_RETENTION < nCurTime ) + bCleanup = TRUE; + + if( atoi(CPLGetXMLValue( psDSNode, "RefCount", "0" )) < 1 + && nCreationTime + FMECACHE_MAX_RETENTION < nCurTime ) + bCleanup = TRUE; + + if( !bCleanup ) + continue; + + bChangeMade = TRUE; + + CPLDebug( "OGRFMECacheIndex", + "ExpireOldCaches() cleaning up data source %s - %ds since last use, %ds old.", + CPLGetXMLValue( psDSNode, "DSName", "" ), + nCurTime - nLastUseTime, + nCurTime - nCreationTime ); + +/* -------------------------------------------------------------------- */ +/* Loop over all the layers, to delete the spatial caches on */ +/* disk. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psLayerN; + + for( psLayerN = psDSNode->psChild; + psLayerN != NULL; + psLayerN = psLayerN->psNext ) + { + IFMESpatialIndex *poIndex; + + if( !EQUAL(psLayerN->pszValue,"OGRLayer") ) + continue; + + const char *pszBase; + + pszBase = CPLGetXMLValue( psLayerN, "SpatialCacheName", "" ); + if( EQUAL(pszBase,"") ) + continue; + + // open, and then delete the index on close. + poIndex = poSession->createSpatialIndex( pszBase, "READ", NULL ); + + if( poIndex == NULL ) + continue; + + if( poIndex->open() != 0 ) + { + CPLDebug( "OGRFMECacheIndex", "Failed to open FME index %s.", + pszBase ); + + poSession->destroySpatialIndex( poIndex ); + continue; + } + + poIndex->close( FME_TRUE ); + poSession->destroySpatialIndex( poIndex ); + } + +/* -------------------------------------------------------------------- */ +/* Remove the datasource from the tree. */ +/* -------------------------------------------------------------------- */ + if( psLastDSNode == NULL ) + psTree->psChild = psDSNode->psNext; + else + psLastDSNode->psNext = psDSNode->psNext; + + psDSNode->psNext = NULL; + CPLDestroyXMLNode( psDSNode ); + psDSNode = psLastDSNode; + } + + return bChangeMade; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmedatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmedatasource.cpp new file mode 100644 index 000000000..a8557470d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmedatasource.cpp @@ -0,0 +1,1720 @@ +/****************************************************************************** + * $Id: ogrfmedatasource.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: FMEObjects Translator + * Purpose: Implementations of the OGRFMEDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001, 2002 Safe Software Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "fme2ogr.h" +//#include "ogr2fme.h" +#include "cpl_conv.h" +#include "cpl_string.h" +//#include "ogrfme_cdsys.h" +#include "cpl_multiproc.h" +#include +#include +#include + +const char* kPROVIDERNAME = "FME_OLEDB"; + +CPL_CVSID("$Id: ogrfmedatasource.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +#ifdef WIN32 +#define FMEDLL_NAME "fme.dll" +#define PATH_CHAR '\\' +#else +#define FMEDLL_NAME "libfmeobj.so" +#define PATH_CHAR '/' +#endif + +static IFMESession *poSharedSession = NULL; +static int nSharedSessionRefCount = 0; +static void *hSessionMutex = NULL; + +typedef struct { + IFMEUniversalReader *poReader; + char *pszReaderType; + char *pszDefinition; +} CachedConnection; + +static int nCachedConnectionCount = 0; +static CachedConnection *pasCachedConnections = NULL; + +typedef struct { + OGREnvelope sExtent; + char *pszIndFile; + char *pszCoordSys; + IFMESpatialIndex *poIndex; + OGRwkbGeometryType eBestGeomType; +} CacheLayerInfo; + +/************************************************************************/ +/* FME_Logger() */ +/* */ +/* Output that would normally go to the FME log file will */ +/* instead be redirected through this function. */ +/************************************************************************/ + +void FME_Logger( FME_MsgLevel severity, const char *message ) + +{ + char *pszMessageCopy = CPLStrdup(message); + + if( pszMessageCopy[strlen(pszMessageCopy)-1] == '\n' ) + pszMessageCopy[strlen(pszMessageCopy)-1] = '\0'; + + CPLDebug( "FME_LOG", "%d:%s", severity, pszMessageCopy ); + + CPLFree( pszMessageCopy ); +} + +/************************************************************************/ +/* GetTmpDir() */ +/************************************************************************/ + +static const char *GetTmpDir() + +{ + const char *pszTmpDir; + + pszTmpDir = getenv("OGRFME_TMPDIR"); + if( pszTmpDir == NULL ) + pszTmpDir = getenv("TMPDIR"); + if( pszTmpDir == NULL ) + pszTmpDir = getenv("TEMPDIR"); + if( pszTmpDir == NULL ) //20020419 - ryan + pszTmpDir = getenv("TMP"); + if( pszTmpDir == NULL ) + pszTmpDir = getenv("TEMP"); + if( pszTmpDir == NULL ) + { +#ifdef WIN32 + pszTmpDir = "C:\\"; +#else + pszTmpDir = "/tmp"; +#endif + } + + return pszTmpDir; +} + +/************************************************************************/ +/* BuildTmpNam() */ +/* */ +/* Create a basename for the temporary file for a given layer */ +/* on this dataset. */ +/************************************************************************/ + +static char *BuildTmpNam( const char *pszLayerName ) + +{ + int i; + char szFilename[2048]; + VSIStatBuf sStat; + const char *pszTmpDir = GetTmpDir(); + +/* -------------------------------------------------------------------- */ +/* Look for an unused name. */ +/* -------------------------------------------------------------------- */ + for( i = -1; TRUE; i++ ) + { + if( i == -1 ) + sprintf( szFilename, "%s%c%s_%s", + pszTmpDir, PATH_CHAR, kPROVIDERNAME, pszLayerName ); + else + sprintf( szFilename, "%s%c%s_%s_%d", + pszTmpDir, PATH_CHAR, kPROVIDERNAME, pszLayerName, i ); + + if( VSIStat( szFilename, &sStat ) != 0 ) + break; + } + + return CPLStrdup( szFilename ); +} + +/************************************************************************/ +/* OGRFMEDataSource() */ +/************************************************************************/ + +OGRFMEDataSource::OGRFMEDataSource() + +{ + pszName = NULL; + pszDataset = NULL; + pszReaderName = NULL; + poSession = NULL; + poReader = NULL; + poFMEFeature = NULL; + + nLayers = 0; + papoLayers = NULL; + + poUserDirectives = NULL; + + bUseCaching = FALSE; +} + +/************************************************************************/ +/* ~OGRFMEDataSource() */ +/************************************************************************/ + +OGRFMEDataSource::~OGRFMEDataSource() + +{ + CPLDebug( kPROVIDERNAME, "~OGRFMEDataSource(): %p", this ); + + if( poSharedSession == NULL ) + return; + + AcquireSession(); + +/* -------------------------------------------------------------------- */ +/* Destroy the layers, so we know we don't still have the */ +/* caches open when we dereference them. */ +/* -------------------------------------------------------------------- */ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + +/* -------------------------------------------------------------------- */ +/* If we have a cached instances, decrement the reference count. */ +/* -------------------------------------------------------------------- */ +#ifdef SUPPORT_PERSISTENT_CACHE + { + OGRFMECacheIndex oCacheIndex( + CPLFormFilename(GetTmpDir(), "ogrfmeds", "ind" ) ); + + if( pszReaderName != NULL && nLayers > 0 + && bUseCaching && oCacheIndex.Lock() && oCacheIndex.Load() ) + { + CPLXMLNode *psMatchDS = NULL; + + psMatchDS = oCacheIndex.FindMatch( pszReaderName, pszDataset, + *poUserDirectives ); + + if( psMatchDS != NULL ) + oCacheIndex.Dereference( psMatchDS ); + + if( oCacheIndex.ExpireOldCaches( poSession ) || psMatchDS != NULL ) + oCacheIndex.Save(); + + oCacheIndex.Unlock(); + } + } +#endif /* def SUPPORT_PERSISTENT_CACHE */ + +/* -------------------------------------------------------------------- */ +/* Cleanup up various resources. */ +/* -------------------------------------------------------------------- */ + if( poFMEFeature != NULL ) + poSession->destroyFeature( poFMEFeature ); + + if( poUserDirectives != NULL ) + poSession->destroyStringArray( poUserDirectives ); + + if( poReader != NULL ) + { + if( !IsPartOfConnectionCache( poReader ) ) + poSession->destroyReader( poReader ); + else + CPLDebug( kPROVIDERNAME, "Preserving cached reader on destructor"); + } + + if( poSession != NULL ) + { + if( --nSharedSessionRefCount == 0 ) + { +#ifdef SUPPORT_CLEANUP_SESSION +#ifdef SUPPORT_INDIRECT_FMEDLL + int (*pfnFME_destroySession)(void *); + + pfnFME_destroySession = (int (*)(void*)) + CPLGetSymbol(FMEDLL_NAME, "FME_DestroySession" ); + if( pfnFME_destroySession == NULL ) + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to fetch FME_DestroySession entry point." ); + else + pfnFME_destroySession( (void *) (&poSession) ); +#else + FME_destroySession( poSession ); +#endif // def SUPPORT_INDIRECT_FMEDLL + poSharedSession = NULL; +#else // ndef SUPPORT_CLEANUP_SESSION + CPLDebug( kPROVIDERNAME, "no active datasources left, but preserving session." ); +#endif + } + } + + CPLFree( pszName ); + CPLFree( pszDataset ); + CPLFree( pszReaderName ); + + ReleaseSession(); +} + +/************************************************************************/ +/* PromptForSource() */ +/************************************************************************/ + +char *OGRFMEDataSource::PromptForSource() + +{ + IFMEDialog *poDialog = NULL; + IFMEString *poSourceFormat, *poSourceDSName; + char *pszResult = NULL; + + poSourceFormat = poSession->createString(); + poSourceDSName = poSession->createString(); + + if( poSession->createDialog( poDialog ) != 0 ) + return NULL; + + poUserDirectives->append( "SPATIAL_SETTINGS" ); + poUserDirectives->append( "no" ); + if( poDialog->sourcePrompt( NULL, NULL, *poSourceFormat, *poSourceDSName, + *poUserDirectives ) ) + { + pszResult = CPLStrdup(CPLSPrintf("%s:%s", + poSourceFormat->data(), + poSourceDSName->data())); + } + + poSession->destroyString( poSourceFormat ); + poSession->destroyString( poSourceDSName ); + + return pszResult; +} + +/************************************************************************/ +/* ReadFileSource() */ +/************************************************************************/ + +char *OGRFMEDataSource::ReadFileSource( const char *pszFilename ) + +{ + FILE *fp; + char **papszLines = NULL; + const char *pszLine; + +/* -------------------------------------------------------------------- */ +/* Read the definition file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpen( pszFilename, "rt" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to open file %s.", + pszFilename ); + return NULL; + } + + while( (pszLine = CPLReadLine(fp)) != NULL ) + { + if( *pszLine != '#' ) + papszLines = CSLAddString( papszLines, pszLine ); + } + + VSIFClose( fp ); + +/* -------------------------------------------------------------------- */ +/* verify minimal requirements. */ +/* -------------------------------------------------------------------- */ + if( CSLCount(papszLines) < 2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Insufficient lines in FME Data Definition file." + "At least a readername and data source name is required." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Apply extra values to user directives. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 2; papszLines[i] != NULL; i++ ) + poUserDirectives->append( papszLines[i] ); + +/* -------------------------------------------------------------------- */ +/* Prepare reader:dataset response string. */ +/* -------------------------------------------------------------------- */ + char *pszReturn; + + pszReturn = CPLStrdup(CPLSPrintf("%s:%s", papszLines[0], papszLines[1] )); + + CSLDestroy( papszLines ); + + return pszReturn; +} + +/************************************************************************/ +/* SaveDefinitionFile() */ +/************************************************************************/ + +static void SaveDefinitionFile( const char *pszFilename, + const char *pszReader, + const char *pszDatasource, + IFMEStringArray &oUserDirectives ) + +{ + FILE *fp; + int i; + + fp = VSIFOpen( CPLResetExtension( pszFilename, "fdd" ), "wt" ); + if( fp == NULL ) + return; + + fprintf( fp, "%s\n", pszReader ); + fprintf( fp, "%s\n", pszDatasource ); + + for( i = 0; i < (int) oUserDirectives.entries(); i++ ) + { + fprintf( fp, "%s\n", oUserDirectives(i) ); + } + + VSIFClose( fp ); +} + +/************************************************************************/ +/* ExtractSRS() */ +/************************************************************************/ + +OGRSpatialReference * +OGRFMEDataSource::ExtractSRS() + +{ +/* -------------------------------------------------------------------- */ +/* Try to find the COORDSYS in the user directives. */ +/* -------------------------------------------------------------------- */ + const char *pszCoordSys = NULL; + for( int i = 0; i < (int) poUserDirectives->entries(); i += 2 ) + { + if( EQUAL((*poUserDirectives)(i),"COORDSYS") ) + pszCoordSys = (*poUserDirectives)(i+1); + } + + if( pszCoordSys == NULL || strlen(pszCoordSys) == 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Translate FME name to an OGRSpatialReference. */ +/* -------------------------------------------------------------------- */ + return FME2OGRSpatialRef( pszCoordSys ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRFMEDataSource::Open( const char * pszCompositeName ) + +{ + FME_MsgNum err; + + CPLAssert( poSession == NULL ); // only open once + +/* -------------------------------------------------------------------- */ +/* Do some initial validation. Does this even look like it */ +/* could plausibly be an FME suitable name? We accept PROMPT:, */ +/* : or anything ending in .fdd as a reasonable candidate. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; pszCompositeName[i] != ':' && pszCompositeName[i] != '\0'; i++) + { + if( pszCompositeName[i] == '/' || pszCompositeName[i] == '\\' + || pszCompositeName[i] == '.' ) + break; + } + + if( (i < 2 || pszCompositeName[i] != ':' + || EQUALN(pszCompositeName,"OCI:",4) + || EQUALN(pszCompositeName,"gltp:",5) + || EQUALN(pszCompositeName,"http",4) + || EQUALN(pszCompositeName,"DODS:",5) + || EQUALN(pszCompositeName,"ODBC:",5) + || EQUALN(pszCompositeName,"MYSQL:",5)) + && !EQUAL(CPLGetExtension( pszCompositeName ), "fdd") + && !EQUALN(pszCompositeName,"PROMPT",6) ) + { + CPLDebug( kPROVIDERNAME, + "OGRFMEDataSource::Open(%s) don't try to open via FME.", + pszCompositeName ); + return FALSE; + } + + CPLDebug( kPROVIDERNAME, "OGRFMEDataSource::Open(%s):%p/%ld", + pszCompositeName, this, (long) CPLGetPID() ); + +/* -------------------------------------------------------------------- */ +/* Create an FME Session. */ +/* -------------------------------------------------------------------- */ + poSession = AcquireSession(); + if( poSession == NULL ) + return FALSE; + + nSharedSessionRefCount++; + + CPLDebug( kPROVIDERNAME, "%p:acquired session", this ); + + poUserDirectives = poSession->createStringArray(); + +/* -------------------------------------------------------------------- */ +/* Redirect FME log messages through CPLDebug(). */ +/* -------------------------------------------------------------------- */ + IFMELogFile *poLogFile = poSession->logFile(); + + poLogFile->setFileName( NULL, FME_FALSE ); + poLogFile->setCallBack( FME_Logger ); + + CPLDebug( kPROVIDERNAME, "%p:reset logfile", this ); + +/* -------------------------------------------------------------------- */ +/* Prompt for a source, if none is provided. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszCompositeName,"") || EQUALN(pszCompositeName,"PROMPT",6) ) + { + pszName = PromptForSource(); + if( pszName == NULL ) + { + ReleaseSession(); + return FALSE; + } + } + else if( CPLGetExtension( pszCompositeName ) != NULL + && EQUAL(CPLGetExtension( pszCompositeName ),"fdd") ) + { + pszName = ReadFileSource(pszCompositeName); + if( pszName == NULL ) + { + ReleaseSession(); + return FALSE; + } + } + else + { + pszName = CPLStrdup( pszCompositeName ); + } + +/* -------------------------------------------------------------------- */ +/* Extract the reader name and password compontents. The */ +/* reader name will be followed by a single colon and then the */ +/* FME DATASET name. */ +/* -------------------------------------------------------------------- */ + for( i = 0; pszName[i] != '\0' && pszName[i] != ':'; i++ ) {} + + if( pszName[i] == '\0' || i < 2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to parse reader and data source from:\n%s", + pszName ); + ReleaseSession(); + return FALSE; + } + + pszReaderName = CPLStrdup( pszName ); + pszReaderName[i] = '\0'; + + pszDataset = CPLStrdup(pszName + i + 1); + + CPLDebug( kPROVIDERNAME, "%s:parsed out dataset", pszDataset ); + +/* -------------------------------------------------------------------- */ +/* If we prompted for a defintion that includes a file to save */ +/* it to, do the save now. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszCompositeName,"PROMPT:",7) + && strlen(pszCompositeName) > 7 ) + { + SaveDefinitionFile( pszCompositeName+7, + pszReaderName, pszDataset, + *poUserDirectives ); + } + +/* -------------------------------------------------------------------- */ +/* Is there a Coordsys statement in the user directives? */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference *poSRS = ExtractSRS(); + + CPLDebug( kPROVIDERNAME, "got the SRS parsed"); + + bCoordSysOverride = poSRS != NULL; + +/* -------------------------------------------------------------------- */ +/* Allocate an FME string, and feature for use here and */ +/* elsewhere. */ +/* -------------------------------------------------------------------- */ + poFMEFeature = poSession->createFeature(); + poFMEString = poSession->createString(); + +/* -------------------------------------------------------------------- */ +/* Are we going to use the direct access DB mechanism, or the */ +/* spatiallly cached (dumb reader) mechanism. */ +/* -------------------------------------------------------------------- */ + bUseCaching = !EQUALN(pszReaderName,"SDE",3) + && !EQUALN(pszReaderName,"ORACLE",6); + +/* -------------------------------------------------------------------- */ +/* Is there already a cache for this dataset? If so, we will */ +/* use it. */ +/* -------------------------------------------------------------------- */ +#ifdef SUPPORT_PERSISTENT_CACHE + OGRFMECacheIndex oCacheIndex( + CPLFormFilename(GetTmpDir(), "ogrfmeds", "ind" ) ); + CPLXMLNode *psMatchDS = NULL; + + if( bUseCaching && oCacheIndex.Lock() && oCacheIndex.Load() ) + { + int bNeedSave = oCacheIndex.ExpireOldCaches( poSession ); + + psMatchDS = oCacheIndex.FindMatch( pszReaderName, pszDataset, + *poUserDirectives ); + + if( psMatchDS != NULL ) + { + oCacheIndex.Reference( psMatchDS ); + oCacheIndex.Save(); + + psMatchDS = CPLCloneXMLTree( psMatchDS ); + oCacheIndex.Unlock(); + + } + else + { + if( bNeedSave ) + oCacheIndex.Save(); + + oCacheIndex.Unlock(); + } + + if( psMatchDS != NULL ) + { + if( InitializeFromXML( psMatchDS ) ) + { + CPLDestroyXMLNode( psMatchDS ); + ReleaseSession(); + + return TRUE; + } + + CPLDestroyXMLNode( psMatchDS ); + } + } +#endif /* def SUPPORT_PERSISTENT_CACHE */ + +/* -------------------------------------------------------------------- */ +/* Create a reader. */ +/* -------------------------------------------------------------------- */ + IFMEStringArray *poParms = poSession->createStringArray(); + + for( i = 0; i < (int) poUserDirectives->entries(); i++ ) + CPLDebug( kPROVIDERNAME, "oUserDirectives(%d) = '%s'", + i, (*poUserDirectives)(i) ); + + poReader = poSession->createReader(pszReaderName, FME_FALSE, + poUserDirectives); + if( poReader == NULL ) + { + CPLFMEError( poSession, + "Failed to create reader of type `%s'.\n", + pszReaderName ); + ReleaseSession(); + return FALSE; + } + + CPLDebug( kPROVIDERNAME, "%p:reader created.", this ); + +/* -------------------------------------------------------------------- */ +/* Now try to open the dataset. */ +/* -------------------------------------------------------------------- */ + err = poReader->open( pszDataset, *poParms ); + if( err ) + { + CPLFMEError( poSession, + "Failed to open dataset `%s' with reader of type `%s'.\n", + pszDataset, pszReaderName ); + ReleaseSession(); + return FALSE; + } + + CPLDebug( kPROVIDERNAME, "%p:reader opened.", this ); + +/* -------------------------------------------------------------------- */ +/* There are some circumstances where we want to keep a */ +/* "connection" open for a data source. Offer this reader for */ +/* connection caching. */ +/* -------------------------------------------------------------------- */ + OfferForConnectionCaching( poReader, pszReaderName, + pszDataset ); + +/* -------------------------------------------------------------------- */ +/* Create a layer for each schema feature. */ +/* -------------------------------------------------------------------- */ + FME_Boolean eEndOfSchema; + + while( TRUE ) + { + err = poReader->readSchema( *poFMEFeature, eEndOfSchema ); + if( err ) + { + CPLFMEError( poSession, "IFMEReader::readSchema() failed." ); + ReleaseSession(); + return FALSE; + } + + if( eEndOfSchema == FME_TRUE ) + break; + + CPLDebug( kPROVIDERNAME, "%p:readSchema() got %s.", + this, poFMEFeature->getFeatureType() ); + + OGRFMELayer *poNewLayer = NULL; + + if( bUseCaching ) + poNewLayer = new OGRFMELayerCached( this ); + else + poNewLayer = new OGRFMELayerDB( this, pszReaderName, pszDataset, + poUserDirectives ); + + if( !poNewLayer->Initialize( poFMEFeature, poSRS ) ) + { + CPLDebug( kPROVIDERNAME, "%p:Initialize() failed.", this ); + delete poNewLayer; + ReleaseSession(); + return FALSE; + } + + papoLayers = (OGRFMELayer **) + CPLRealloc(papoLayers, sizeof(void*) * ++nLayers ); + papoLayers[nLayers-1] = poNewLayer; + } + + poSession->destroyStringArray( poParms ); + + if( poSRS != NULL ) + poSRS->Release(); + + CPLDebug( kPROVIDERNAME, "%p:schema read.", this ); + +/* -------------------------------------------------------------------- */ +/* Do we want to build our own index/caches for each layer? */ +/* -------------------------------------------------------------------- */ + if( bUseCaching ) + BuildSpatialIndexes(); + + CPLDebug( kPROVIDERNAME, "%p:Open() successful.", this ); + + ReleaseSession(); + +/* -------------------------------------------------------------------- */ +/* If we are caching, add this cache to the cache index. */ +/* -------------------------------------------------------------------- */ +#ifdef SUPPORT_PERSISTENT_CACHE + if( bUseCaching && oCacheIndex.Lock() && oCacheIndex.Load() ) + { + CPLXMLNode *psXML = SerializeToXML(); + + oCacheIndex.Add( psXML ); // cache index takes ownership of tree + oCacheIndex.Reference( psXML ); + oCacheIndex.Save(); + oCacheIndex.Unlock(); + } +#endif + + return TRUE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRFMEDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* BuildSpatialIndexes() */ +/* */ +/* Import all the features, building per-layer spatial */ +/* caches with indexing. */ +/************************************************************************/ + +void OGRFMEDataSource::BuildSpatialIndexes() + +{ + CacheLayerInfo *pasCLI; + int iLayer; + + pasCLI = (CacheLayerInfo *) CPLCalloc(sizeof(CacheLayerInfo),nLayers); + +/* -------------------------------------------------------------------- */ +/* Create index files with "temp file" names. */ +/* -------------------------------------------------------------------- */ + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + CacheLayerInfo *psCLI = pasCLI + iLayer; + + psCLI->pszCoordSys = NULL; + + psCLI->pszIndFile = + BuildTmpNam( papoLayers[iLayer]->GetLayerDefn()->GetName() ); + + psCLI->poIndex = + poSession->createSpatialIndex( psCLI->pszIndFile, "WRITE", NULL ); + + if( psCLI->poIndex == NULL || psCLI->poIndex->open() != 0 ) + { + CPLDebug( kPROVIDERNAME, + "Serious error creating or opening spatial index ... bailing." ); + return; + } + + // our special marker meaning unset. + psCLI->eBestGeomType = (OGRwkbGeometryType) 500; + } + +/* -------------------------------------------------------------------- */ +/* Read all features, and store them into appropriate spatial */ +/* indexes. */ +/* -------------------------------------------------------------------- */ + while( ReadFMEFeature() ) + { + CacheLayerInfo *psCLI = NULL; + + poFMEFeature->getFeatureType( *poFMEString ); + + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(papoLayers[iLayer]->GetLayerDefn()->GetName(), + poFMEString->data()) ) + { + psCLI = pasCLI + iLayer; + break; + } + } + + if( psCLI == NULL ) + { + CPLDebug( "FME_LOG", + "Skipping %s feature, doesn't match a layer.", + poFMEString->data() ); + continue; + } + + psCLI->poIndex->store( *poFMEFeature ); + + // Aggregate to extents. + FME_Real64 dfMinX, dfMaxX, dfMinY, dfMaxY; + + poFMEFeature->boundingBox( dfMinX, dfMaxX, dfMinY, dfMaxY ); + + if( psCLI->poIndex->entries() == 1 ) + { + psCLI->sExtent.MinX = dfMinX; + psCLI->sExtent.MaxX = dfMaxX; + psCLI->sExtent.MinY = dfMinY; + psCLI->sExtent.MaxY = dfMaxY; + } + else + { + psCLI->sExtent.MinX = MIN(psCLI->sExtent.MinX,dfMinX); + psCLI->sExtent.MaxX = MAX(psCLI->sExtent.MaxX,dfMaxX); + psCLI->sExtent.MinY = MIN(psCLI->sExtent.MinY,dfMinY); + psCLI->sExtent.MaxY = MAX(psCLI->sExtent.MaxY,dfMaxY); + } + + // Update best geometry type to use based on this geometry. + ClarifyGeometryClass( poFMEFeature, psCLI->eBestGeomType ); + + // Check on coordsys. + if( poFMEFeature->getCoordSys() != NULL + && strlen(poFMEFeature->getCoordSys()) > 0 ) + { + if( psCLI->pszCoordSys == NULL ) + psCLI->pszCoordSys = CPLStrdup(poFMEFeature->getCoordSys()); + else + { + if( !EQUAL(psCLI->pszCoordSys,poFMEFeature->getCoordSys()) ) + CPLDebug( "FME_OLEDB", + "Conflicting coordsys %s (vs. %s) on layer %s.", + poFMEFeature->getCoordSys(), + psCLI->pszCoordSys, + papoLayers[iLayer]->GetLayerDefn()->GetName() ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Close indexes and assign to layers. */ +/* -------------------------------------------------------------------- */ + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + OGRFMELayerCached * poLayer = (OGRFMELayerCached *) papoLayers[iLayer]; + CacheLayerInfo *psCLI = pasCLI + iLayer; + + // If there are no features, we destroy the layer. + if( psCLI->poIndex->entries() == 0 ) + { + CPLDebug( "FME_LOG", "Drop layer %s, there are not features.", + poLayer->GetLayerDefn()->GetName() ); + psCLI->poIndex->close(FME_TRUE); + + delete poLayer; + papoLayers[iLayer] = NULL; + } + else + { + OGRSpatialReference *poSpatialRef = NULL; + + psCLI->poIndex->close(FME_FALSE); + poSession->destroySpatialIndex( psCLI->poIndex ); + + if( psCLI->pszCoordSys != NULL && !bCoordSysOverride ) + { + CPLDebug("FME_OLEDB", + "Applying COORDSYS=%s to layer %s from feature scan.", + psCLI->pszCoordSys, + papoLayers[iLayer]->GetLayerDefn()->GetName() ); + + poSpatialRef = FME2OGRSpatialRef( psCLI->pszCoordSys ); + } + + poLayer->AssignIndex( psCLI->pszIndFile, &(psCLI->sExtent), + poSpatialRef ); + if( psCLI->eBestGeomType != 500 + && psCLI->eBestGeomType + != poLayer->GetLayerDefn()->GetGeomType() ) + { + CPLDebug( "FME_LOG", "Setting geom type from %d to %d", + poLayer->GetLayerDefn()->GetGeomType(), + psCLI->eBestGeomType ); + + poLayer->GetLayerDefn()->SetGeomType( psCLI->eBestGeomType ); + } + } + + CPLFree( psCLI->pszIndFile ); + CPLFree( psCLI->pszCoordSys ); + } + + CPLFree( pasCLI ); + +/* -------------------------------------------------------------------- */ +/* Compress missing layers from the layer list. */ +/* -------------------------------------------------------------------- */ + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( papoLayers[iLayer] == NULL ) + { + papoLayers[iLayer] = papoLayers[nLayers-1]; + nLayers--; + iLayer--; + } + } +} + +/************************************************************************/ +/* ClarifyGeometryClass() */ +/* */ +/* Examine an FME features geometry, and ensure the wkb */ +/* geometry type we are using will include it. That is, we */ +/* generally make the geometry type sufficiently general to */ +/* include the type of geometry of this feature. */ +/* */ +/* Exceptions are when the existing type is unknown, in which */ +/* case we assign it the type of the geometry we find, and if */ +/* it is a mixture of polygon and multipolygon in which case we */ +/* use multipolygon with the understanding that we will force */ +/* the polygons to multipolygon. */ +/************************************************************************/ + +void OGRFMEDataSource::ClarifyGeometryClass( + IFMEFeature *poFeature, + OGRwkbGeometryType &eBestGeomType ) + +{ + FME_GeometryType eFMEType = poFeature->getGeometryType(); + OGRwkbGeometryType eThisType; + +/* -------------------------------------------------------------------- */ +/* Classify this FME geometry. The hard case is aggregate. */ +/* -------------------------------------------------------------------- */ + if( eFMEType == FME_GEOM_POINT ) + eThisType = wkbPoint; + else if( eFMEType == FME_GEOM_LINE ) + eThisType = wkbLineString; + else if( eFMEType == FME_GEOM_POLYGON || eFMEType == FME_GEOM_DONUT ) + eThisType = wkbPolygon; + else if( eFMEType == FME_GEOM_AGGREGATE ) + { + // This is the hard case! Split the aggregate to see if we can + // categorize it more specifically. + OGRwkbGeometryType eComponentType = (OGRwkbGeometryType) 500; + IFMEFeatureVector *poFeatVector; + + poFeatVector = poSession->createFeatureVector(); + + poFeature->splitAggregate( *poFeatVector ); + + for( int iPart = 0; iPart < (int)poFeatVector->entries(); iPart++ ) + { + IFMEFeature *poFMEPart = (*poFeatVector)(iPart); + + if( poFMEPart != NULL ) + ClarifyGeometryClass( poFMEPart, eComponentType ); + } + + poSession->destroyFeatureVector( poFeatVector ); + + if( wkbFlatten(eComponentType) == wkbPolygon ) + eThisType = wkbMultiPolygon; + else if( wkbFlatten(eComponentType) == wkbPoint ) + eThisType = wkbMultiPoint; + else if( wkbFlatten(eComponentType) == wkbLineString ) + eThisType = wkbMultiLineString; + else + eThisType = wkbGeometryCollection; + } + else + eThisType = wkbUnknown; + + // Is this 3D? + if( poFeature->getDimension() == FME_THREE_D ) + eThisType = wkbSetZ(eThisType); + +/* -------------------------------------------------------------------- */ +/* Now adjust the working type. */ +/* -------------------------------------------------------------------- */ + OGRwkbGeometryType eNewBestGeomType = eBestGeomType; + + if( eBestGeomType == 500 ) + eNewBestGeomType = eThisType; + else if( eThisType == wkbNone ) + /* do nothing */; + else if( wkbFlatten(eThisType) == wkbFlatten(eBestGeomType) ) + /* no change */; + else if( wkbFlatten(eThisType) == wkbPolygon + && wkbFlatten(eBestGeomType) == wkbMultiPolygon ) + /* do nothing */; + else if( wkbFlatten(eThisType) == wkbMultiPolygon + && wkbFlatten(eBestGeomType) == wkbPolygon ) + eNewBestGeomType = wkbMultiPolygon; + else if( wkbFlatten(eThisType) >= 4 && wkbFlatten(eThisType) <= 7 + && wkbFlatten(eBestGeomType) >= 4 && wkbFlatten(eBestGeomType) <= 7 ) + /* they are both collections, but not the same ... go to generic coll*/ + eNewBestGeomType = wkbGeometryCollection; + else + eNewBestGeomType = wkbUnknown; + + if( (wkbHasZ(eBestGeomType) || wkbHasZ(eThisType)) + && (int) eNewBestGeomType != 500 ) + { + eNewBestGeomType = wkbSetZ(eNewBestGeomType); + } + + eBestGeomType = eNewBestGeomType; +} + +/************************************************************************/ +/* ReadFMEFeature() */ +/* */ +/* Internal working function to read an FME feature into the */ +/* poFMEFeature object, and increment the nPreviousFeature */ +/* counter. Returns FALSE on end of input, or on error. */ +/************************************************************************/ + +int OGRFMEDataSource::ReadFMEFeature() + +{ + FME_Boolean eEndOfSchema; + FME_MsgNum err; + + poFMEFeature->resetFeature(); + err = poReader->read( *poFMEFeature, eEndOfSchema ); + + if( err ) + { + CPLFMEError( poSession, "Error while reading feature." ); + return FALSE; + } + + if( eEndOfSchema == FME_TRUE ) + { + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* ProcessGeometry() */ +/* */ +/* Translate an FME geometry into an OGR geometry. */ +/************************************************************************/ + +OGRGeometry * +OGRFMEDataSource::ProcessGeometry( OGRFMELayer * poLayer, + IFMEFeature * poGeomFeat, + OGRwkbGeometryType eDesiredType ) +{ + + FME_GeometryType eGeomType = poGeomFeat->getGeometryType(); + int bForceToMulti = FALSE; + + if( wkbFlatten(eDesiredType) == wkbGeometryCollection + || wkbFlatten(eDesiredType) == wkbMultiPolygon ) + bForceToMulti = TRUE; + +/* -------------------------------------------------------------------- */ +/* Point */ +/* -------------------------------------------------------------------- */ + if( eGeomType == FME_GEOM_POINT ) + { + return + new OGRPoint(poGeomFeat->getXCoordinate(0), + poGeomFeat->getYCoordinate(0), + poGeomFeat->getZCoordinate(0)); + } + +/* -------------------------------------------------------------------- */ +/* Line */ +/* -------------------------------------------------------------------- */ + else if( eGeomType == FME_GEOM_LINE ) + { + OGRLineString *poLine = new OGRLineString(); + + poLine->setNumPoints( poGeomFeat->numCoords() ); + + for( int iPoint = 0; iPoint < (int) poGeomFeat->numCoords(); iPoint++ ) + { + poLine->setPoint( iPoint, + poGeomFeat->getXCoordinate(iPoint), + poGeomFeat->getYCoordinate(iPoint), + poGeomFeat->getZCoordinate(iPoint) ); + } + + return poLine; + } + +/* -------------------------------------------------------------------- */ +/* Polygon */ +/* -------------------------------------------------------------------- */ + else if( eGeomType == FME_GEOM_POLYGON ) + { + OGRLinearRing *poLine = new OGRLinearRing(); + OGRPolygon *poPolygon = new OGRPolygon(); + + poLine->setNumPoints( poGeomFeat->numCoords() ); + + for( int iPoint = 0; iPoint < (int)poGeomFeat->numCoords(); iPoint++ ) + { + poLine->setPoint( iPoint, + poGeomFeat->getXCoordinate(iPoint), + poGeomFeat->getYCoordinate(iPoint), + poGeomFeat->getZCoordinate(iPoint) ); + } + poPolygon->addRingDirectly( poLine ); + + if( !bForceToMulti ) + return poPolygon; + + OGRMultiPolygon *poMP = new OGRMultiPolygon(); + + poMP->addGeometryDirectly( poPolygon ); + + return poMP; + } + +/* -------------------------------------------------------------------- */ +/* Donut */ +/* -------------------------------------------------------------------- */ + else if( eGeomType == FME_GEOM_DONUT ) + { + OGRPolygon *poPolygon = new OGRPolygon(); + IFMEFeatureVector *poFeatVector; + IFMEFeature *poFMERing = NULL; + + poFeatVector = poSession->createFeatureVector(); + + poGeomFeat->getDonutParts( *poFeatVector ); + + for( int iPart = 0; iPart < (int)poFeatVector->entries(); iPart++ ) + { + OGRLinearRing *poRing; + + poFMERing = (*poFeatVector)(iPart); + if( poFMERing == NULL ) + continue; + + poRing = new OGRLinearRing(); + + poRing->setNumPoints( poFMERing->numCoords() ); + + for( int iPoint=0; iPoint < (int)poFMERing->numCoords(); iPoint++ ) + { + poRing->setPoint( iPoint, + poFMERing->getXCoordinate(iPoint), + poFMERing->getYCoordinate(iPoint), + poFMERing->getZCoordinate(iPoint) ); + } + + poPolygon->addRingDirectly( poRing ); + } + + poFeatVector->clearAndDestroy(); + poSession->destroyFeatureVector( poFeatVector ); + + if( !bForceToMulti ) + return poPolygon; + + OGRMultiPolygon *poMP = new OGRMultiPolygon(); + + poMP->addGeometryDirectly( poPolygon ); + + return poMP; + } + +/* -------------------------------------------------------------------- */ +/* Aggregate */ +/* -------------------------------------------------------------------- */ + else if( eGeomType == FME_GEOM_AGGREGATE ) + { + OGRGeometryCollection *poCollection; + IFMEFeatureVector *poFeatVector; + OGRwkbGeometryType eSubType = wkbUnknown; + + if( bForceToMulti && eDesiredType == wkbMultiPolygon ) + { + poCollection = new OGRMultiPolygon(); + eSubType = wkbPolygon; + } + else + poCollection = new OGRGeometryCollection(); + + poFeatVector = poSession->createFeatureVector(); + + poGeomFeat->splitAggregate( *poFeatVector ); + + for( int iPart = 0; iPart < (int)poFeatVector->entries(); iPart++ ) + { + OGRGeometry *poOGRPart; + IFMEFeature *poFMEPart; + + poFMEPart = (*poFeatVector)(iPart); + if( poFMEPart == NULL ) + continue; + + poOGRPart = ProcessGeometry( poLayer, poFMEPart, eSubType ); + if( poOGRPart == NULL ) + continue; + + poCollection->addGeometryDirectly( poOGRPart ); + } + + poSession->destroyFeatureVector( poFeatVector ); + + return poCollection; + } + + else if( eGeomType == FME_GEOM_UNDEFINED ) + { + return NULL; + } + else + { + CPLDebug( kPROVIDERNAME, + "unable to translate unsupported geometry type: %d\n", + eGeomType ); + + return NULL; + } +} + +/************************************************************************/ +/* ProcessFeature() */ +/* */ +/* Process the current fme feature into an OGR feature of the */ +/* passed layer types. */ +/************************************************************************/ + +OGRFeature *OGRFMEDataSource::ProcessFeature( OGRFMELayer *poLayer, + IFMEFeature *poSrcFeature ) + +{ + OGRFeatureDefn *poDefn = poLayer->GetLayerDefn(); + OGRFeature *poFeature = new OGRFeature( poDefn ); + int iAttr; + +/* -------------------------------------------------------------------- */ +/* Transfer attributes ... for numeric values assume the string */ +/* representation is appropriate, and automatically */ +/* translatable. Eventually we will need special handling for */ +/* array style fields. */ +/* -------------------------------------------------------------------- */ + for( iAttr = 0; iAttr < poDefn->GetFieldCount(); iAttr++ ) + { + OGRFieldDefn *poField = poDefn->GetFieldDefn(iAttr); + + if( poSrcFeature->getAttribute( poField->GetNameRef(), + *poFMEString ) == FME_TRUE ) + { + poFeature->SetField( iAttr, poFMEString->data() ); + } + } + +/* -------------------------------------------------------------------- */ +/* Translate the geometry. */ +/* -------------------------------------------------------------------- */ + OGRGeometry *poOGRGeom = NULL; + + poOGRGeom = ProcessGeometry( poLayer, poSrcFeature, + poLayer->GetLayerDefn()->GetGeomType() ); + if( poOGRGeom != NULL ) + poFeature->SetGeometryDirectly( poOGRGeom ); + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRFMEDataSource::TestCapability( const char * ) + +{ + return FALSE; +} + +/************************************************************************/ +/* OfferForConnectionCaching() */ +/* */ +/* Sometimes we want to keep a prototype reader open to */ +/* maintain a connection, for instance to SDE where creating */ +/* the connection is pretty expensive. */ +/************************************************************************/ + +void OGRFMEDataSource::OfferForConnectionCaching(IFMEUniversalReader *poReader, + const char *pszReaderType, + const char *pszDataset) + +{ +/* -------------------------------------------------------------------- */ +/* For now we only cache SDE readers. */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(pszReaderType,"SDE",3) + && !EQUALN(pszReaderType,"ORACLE",6) ) + return; + +/* -------------------------------------------------------------------- */ +/* We want to build a definition of this connection that */ +/* indicates a unique connection. For now we base it on the */ +/* Server, UserName, Password, and Instance values. We will */ +/* pick these all out of the RUNTIME_MACROS if present. */ +/* */ +/* First find the runtime macros. */ +/* -------------------------------------------------------------------- */ + const char *pszRuntimeMacros = NULL; + int i; + + for( i = 0; i < (int) poUserDirectives->entries()-1; i += 2 ) + { + if( EQUALN((const char *) (*poUserDirectives)(i),"RUNTIME_MACROS",14) ) + pszRuntimeMacros = (*poUserDirectives)(i+1); + } + +/* -------------------------------------------------------------------- */ +/* Break into name/value pairs. */ +/* -------------------------------------------------------------------- */ + char **papszTokens = NULL; + + if( pszRuntimeMacros != NULL ) + papszTokens = CSLTokenizeStringComplex( pszRuntimeMacros, ",", + TRUE, TRUE); + +/* -------------------------------------------------------------------- */ +/* Look for Name values we want, and append them to the */ +/* definition string. */ +/* -------------------------------------------------------------------- */ + char szDefinition[5000]; + + sprintf( szDefinition, "%s::", pszDataset ); + + for( i = 0; i < CSLCount(papszTokens)-1; i += 2 ) + { + if( strstr(papszTokens[i],"Server") != NULL + || strstr(papszTokens[i],"Service") != NULL + || strstr(papszTokens[i],"UserName") != NULL + || strstr(papszTokens[i],"Password") != NULL + || strstr(papszTokens[i],"Instance") != NULL ) + { + if( strlen(papszTokens[i+1]) + strlen(papszTokens[i]) + 20 + < sizeof(szDefinition) - strlen(szDefinition) ) + { + sprintf( szDefinition + strlen(szDefinition), "%s=%s;", + papszTokens[i], papszTokens[i+1] ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Do we already have a reader cached for this definition? */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nCachedConnectionCount; i++ ) + { + if( strcmp(szDefinition, pasCachedConnections[i].pszDefinition) == 0 ) + return; + } + +/* -------------------------------------------------------------------- */ +/* Added this reader to the cache. */ +/* -------------------------------------------------------------------- */ + CPLDebug( kPROVIDERNAME, + "Caching IFMEUniversalReader to maintain connection.\n" + "ReaderType=%s, Definition=%s", + pszReaderType, szDefinition ); + + nCachedConnectionCount++; + pasCachedConnections = (CachedConnection *) + CPLRealloc(pasCachedConnections, + sizeof(CachedConnection) * nCachedConnectionCount); + + pasCachedConnections[nCachedConnectionCount-1].poReader = poReader; + pasCachedConnections[nCachedConnectionCount-1].pszReaderType = + CPLStrdup(pszReaderType); + pasCachedConnections[nCachedConnectionCount-1].pszDefinition = + CPLStrdup(szDefinition); +} + +/************************************************************************/ +/* IsPartOfConnectionCache() */ +/* */ +/* I this reader being used to maintain a connection cache? */ +/************************************************************************/ +int OGRFMEDataSource::IsPartOfConnectionCache( IFMEUniversalReader *poReader ) + +{ + int i; + + for( i = 0; i < nCachedConnectionCount; i++ ) + if( poReader == pasCachedConnections[i].poReader ) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* AcquireSession() */ +/* */ +/* Get unique ownership of the FME session for this thread. */ +/************************************************************************/ + +IFMESession *OGRFMEDataSource::AcquireSession() + +{ + FME_MsgNum err; + +/* -------------------------------------------------------------------- */ +/* Create session mutex if we don't already have one. */ +/* -------------------------------------------------------------------- */ + if( hSessionMutex == NULL ) + { + hSessionMutex = CPLCreateMutex(); + + CPLDebug( kPROVIDERNAME, "%p:Creating FME session, mutex=%d.", + this, hSessionMutex ); + } + +/* -------------------------------------------------------------------- */ +/* Try to acquire ownership of the session, even if the session */ +/* doesn't yet exist. */ +/* -------------------------------------------------------------------- */ + else + { +#ifdef DEBUG_MUTEX + CPLDebug( kPROVIDERNAME, "%p:Wait for session mutex.", this ); +#endif + + if( !CPLAcquireMutex( hSessionMutex, 5.0 ) ) + { + CPLDebug( kPROVIDERNAME, "%p:Failed to acquire session mutex in 5s.", + this ); + } + +#ifdef DEBUG_MUTEX + else + CPLDebug( kPROVIDERNAME, "%p:Got session mutex.", this ); +#endif + } + +/* -------------------------------------------------------------------- */ +/* If the session doesn't exist, create it now. */ +/* -------------------------------------------------------------------- */ + if( poSharedSession == NULL ) + { +#ifdef SUPPORT_INDIRECT_FMEDLL + FME_MsgNum (*pfnFME_CreateSession)( void * ); + pfnFME_CreateSession = (FME_MsgNum (*)(void*)) + CPLGetSymbol( FMEDLL_NAME, "FME_CreateSession" ); + if( pfnFME_CreateSession == NULL ) + { + CPLReleaseMutex( hSessionMutex ); + CPLDebug( kPROVIDERNAME, "Unable to load FME_CreateSession from %s, skipping FME Driver.", FMEDLL_NAME ); + return NULL; + } + + err = pfnFME_CreateSession( (void *) (&poSharedSession) ); +#else + err = FME_createSession(poSharedSession); +#endif + if( err ) + { + poSharedSession = NULL; + CPLReleaseMutex( hSessionMutex ); + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to create FMESession." ); + return NULL; + } + + // Dale Nov 26 '01 -- Set up to log "badnews" from FME + // to help track down problems + + IFMEStringArray *poSessionDirectives = + poSharedSession->createStringArray(); + + if( poSessionDirectives == NULL ) + { + err = 1; + CPLError( CE_Warning, CPLE_AppDefined, + "Something has gone wonky with createStringArray() on the IFMESession.\n" + "Is it possible you built with gcc 3.2 on Linux? This seems problematic." ); + + } + else + { + poSessionDirectives->append("FME_DEBUG"); + poSessionDirectives->append("BADNEWS"); + + err = poSharedSession->init( poSessionDirectives ); + + poSharedSession->destroyStringArray( poSessionDirectives ); + + if( err ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to initialize FMESession.\n%s", + poSharedSession->getLastErrorMsg()); + } + } + + if( err ) + { +#ifdef SUPPORT_INDIRECT_FMEDLL + int (*pfnFME_destroySession)(void *); + + pfnFME_destroySession = (int (*)(void*)) + CPLGetSymbol(FMEDLL_NAME, "FME_DestroySession" ); + if( pfnFME_destroySession != NULL ) + pfnFME_destroySession( (void *) (&poSharedSession) ); +#else + FME_destroySession( poSharedSession ); +#endif // def SUPPORT_INDIRECT_FMEDLL + + poSharedSession = NULL; + CPLReleaseMutex( hSessionMutex ); + return NULL; + } + } + + return poSharedSession; +} + +/************************************************************************/ +/* ReleaseSession() */ +/* */ +/* Release the lock on the FME session. */ +/************************************************************************/ + +void OGRFMEDataSource::ReleaseSession() + +{ +#ifdef DEBUG_MUTEX + CPLDebug( kPROVIDERNAME, "%p:Release session mutex.", this ); +#endif + + CPLReleaseMutex( hSessionMutex ); +} + +/************************************************************************/ +/* SerializeToXML() */ +/* */ +/* Convert the information about this datasource, and it's */ +/* layers into an XML format that can be stored in the */ +/* persistent feature cache index. */ +/************************************************************************/ + +CPLXMLNode *OGRFMEDataSource::SerializeToXML() + +{ + CPLXMLNode *psDS; + + CPLAssert( bUseCaching ); + +/* -------------------------------------------------------------------- */ +/* Setup data source information. */ +/* -------------------------------------------------------------------- */ + psDS = CPLCreateXMLNode( NULL, CXT_Element, "DataSource" ); + + CPLCreateXMLElementAndValue( psDS, "Driver", pszReaderName ); + CPLCreateXMLElementAndValue( psDS, "DSName", pszDataset ); + CPLCreateXMLElementAndValue( psDS, "RefCount", "0" ); + CPLCreateXMLElementAndValue( psDS, "CreationTime", "0" ); + CPLCreateXMLElementAndValue( psDS, "LastUseTime", "0" ); + +/* -------------------------------------------------------------------- */ +/* Append all the FME user directives in force. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psUD; + + psUD = CPLCreateXMLNode( psDS, CXT_Element, "UserDirectives" ); + for( int i = 0; i < (int) poUserDirectives->entries(); i++ ) + CPLCreateXMLElementAndValue( psUD, "Directive", + (*poUserDirectives)(i) ); + +/* -------------------------------------------------------------------- */ +/* Now append all the layer information. */ +/* -------------------------------------------------------------------- */ + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + OGRFMELayerCached * poLayer = (OGRFMELayerCached *) papoLayers[iLayer]; + CPLXMLNode *psLayer; + + psLayer = poLayer->SerializeToXML(); + CPLAddXMLChild( psDS, psLayer ); + } + + return psDS; +} + +/************************************************************************/ +/* InitializeFromXML() */ +/************************************************************************/ + +int OGRFMEDataSource::InitializeFromXML( CPLXMLNode *psDS ) + +{ + CPLAssert( bUseCaching ); + +/* -------------------------------------------------------------------- */ +/* Loop over layers, instantiating from the cached data. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psLayerN; + + for( psLayerN = psDS->psChild; psLayerN != NULL; + psLayerN = psLayerN->psNext ) + { + OGRFMELayerCached *poNewLayer; + + if( !EQUAL(psLayerN->pszValue,"OGRLayer") ) + continue; + + poNewLayer = new OGRFMELayerCached( this ); + +/* -------------------------------------------------------------------- */ +/* Initialize the layer from the XML. */ +/* -------------------------------------------------------------------- */ + if( !poNewLayer->InitializeFromXML( psLayerN ) ) + { + // this is *not* proper cleanup + CPLAssert( FALSE ); + nLayers = 0; + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Assign the spatial index. We should really change this to */ +/* check if it succeeds! */ +/* -------------------------------------------------------------------- */ + poNewLayer->AssignIndex( + CPLGetXMLValue( psLayerN, "SpatialCacheName", + "" ), + NULL, NULL ); + +/* -------------------------------------------------------------------- */ +/* Add the layer to the layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRFMELayer **) + CPLRealloc(papoLayers, sizeof(void*) * ++nLayers ); + papoLayers[nLayers-1] = poNewLayer; + } + + return TRUE; +} + +/************************************************************************/ +/* FME2OGRSpatialRef() */ +/* */ +/* Translate an FME coordinate system into an */ +/* OGRSpatialReference using the coordinate system manager */ +/* getCoordSysAsOGCDef() method. We assume the session has */ +/* already been acquired. */ +/************************************************************************/ + +OGRSpatialReference * +OGRFMEDataSource::FME2OGRSpatialRef( const char *pszCoordsys ) + +{ + IFMEString *poOGCDef; + + poOGCDef = poSession->createString(); + + poSession->coordSysManager()->getCoordSysAsOGCDef( + pszCoordsys, *poOGCDef ); + + char *pszWKT = (char *) poOGCDef->data(); + OGRSpatialReference oSRS; + + if( oSRS.importFromWkt( &pszWKT ) == OGRERR_NONE ) + { + poSession->destroyString( poOGCDef ); + return oSRS.Clone(); + } + else + { + poSession->destroyString( poOGCDef ); + return NULL; + } +} + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmedriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmedriver.cpp new file mode 100644 index 000000000..902a86355 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmedriver.cpp @@ -0,0 +1,107 @@ +/****************************************************************************** + * $Id: ogrfmedriver.cpp 12396 2007-10-13 10:02:17Z rouault $ + * + * Project: FMEObjects Translator + * Purpose: Implementations of the OGRFMEDriver class. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001 Safe Software Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "fme2ogr.h" +#include "cpl_error.h" + +CPL_CVSID("$Id: ogrfmedriver.cpp 12396 2007-10-13 10:02:17Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* OGRFMEDriver */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* ~OGRFMEDriver() */ +/************************************************************************/ + +OGRFMEDriver::~OGRFMEDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRFMEDriver::GetName() + +{ + return "FMEObjects Gateway"; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRFMEDriver::TestCapability( const char * ) + +{ + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRFMEDriver::Open( const char * pszFilename, int bUpdate ) + +{ + OGRFMEDataSource *poDS = new OGRFMEDataSource; + + if( !poDS->Open( pszFilename ) ) + { + delete poDS; + return NULL; + } + + if( bUpdate ) + { + delete poDS; + + CPLError( CE_Failure, CPLE_OpenFailed, + "FMEObjects Driver doesn't support update." ); + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRFME() */ +/************************************************************************/ + +void RegisterOGRFME() + +{ + if (! GDAL_CHECK_VERSION("FME driver")) + return; + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( new OGRFMEDriver ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmelayer.cpp new file mode 100644 index 000000000..f45a45b6a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmelayer.cpp @@ -0,0 +1,364 @@ +/****************************************************************************** + * $Id: ogrfmelayer.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: FMEObjects Translator + * Purpose: Implementation of the OGRFMELayer base class. The class + * implements behaviour shared between database and spatial cached + * layer types. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001 Safe Software Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "fme2ogr.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrfmelayer.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +/************************************************************************/ +/* OGRFMELayer() */ +/************************************************************************/ + +OGRFMELayer::OGRFMELayer( OGRFMEDataSource *poDSIn ) + +{ + poDS = poDSIn; + + poFeatureDefn = NULL; + poSpatialRef = NULL; + pszAttributeFilter = NULL; + + poFMEFeature = NULL; +} + +/************************************************************************/ +/* ~OGRFMELayer() */ +/************************************************************************/ + +OGRFMELayer::~OGRFMELayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "FME", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + CPLFree( pszAttributeFilter ); + + if( poFMEFeature != NULL ) + poDS->GetFMESession()->destroyFeature( poFMEFeature ); + + if( poFeatureDefn != NULL ) + { + poFeatureDefn->Release(); + } + + if( poSpatialRef != NULL ) + poSpatialRef->Release(); +} + +/************************************************************************/ +/* Initialize() */ +/* */ +/* Build an OGRFeatureDefn for this layer from the passed */ +/* schema IFMEFeature. */ +/************************************************************************/ + +int OGRFMELayer::Initialize( IFMEFeature * poSchemaFeature, + OGRSpatialReference *poSRS ) + +{ + IFMEString *poFMEString = NULL; + + poFMEString = poDS->GetFMESession()->createString(); + poFMEFeature = poDS->GetFMESession()->createFeature(); + + if( poSRS != NULL ) + poSpatialRef = poSRS->Clone(); + +/* -------------------------------------------------------------------- */ +/* Create the definition with the definition name being the */ +/* same as the FME feature type. */ +/* -------------------------------------------------------------------- */ + poSchemaFeature->getFeatureType( *poFMEString ); + + poFeatureDefn = new OGRFeatureDefn( poFMEString->data() ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + poDS->GetFMESession()->destroyString( poFMEString ); + +/* -------------------------------------------------------------------- */ +/* Get the list of attribute names. */ +/* -------------------------------------------------------------------- */ + IFMEStringArray *poAttrNames; + + poAttrNames = poDS->GetFMESession()->createStringArray(); + poSchemaFeature->getAllAttributeNames( *poAttrNames ); + +/* ==================================================================== */ +/* Loop over attributes, adding them to our feature defn. */ +/* ==================================================================== */ + OGRwkbGeometryType eGeomType = wkbNone; + IFMEString *poAttrValue; + poAttrValue = poDS->GetFMESession()->createString(); + + for( int iAttr = 0; iAttr < (int)poAttrNames->entries(); iAttr++ ) + { + const char *pszAttrName = (*poAttrNames)(iAttr); + +/* -------------------------------------------------------------------- */ +/* Get the attribute value. */ +/* -------------------------------------------------------------------- */ + if( !poSchemaFeature->getAttribute(pszAttrName,*poAttrValue) ) + continue; + +/* -------------------------------------------------------------------- */ +/* Handle geometry attributes. Use them to try and establish */ +/* the geometry type of this layer. If we get conflicting */ +/* geometries just fall back to the generic geometry type. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszAttrName,"fme_geometry",12) ) + { + OGRwkbGeometryType eAttrGeomType = wkbNone; + + if( EQUAL(poAttrValue->data(),"fme_point") ) + eAttrGeomType = wkbPoint; + else if( EQUAL(poAttrValue->data(),"fme_text") ) + eAttrGeomType = wkbPoint; + else if( EQUAL(poAttrValue->data(),"fme_area") ) + eAttrGeomType = wkbPolygon; + else if( EQUAL(poAttrValue->data(),"fme_polygon") ) + eAttrGeomType = wkbPolygon; + else if( EQUAL(poAttrValue->data(),"fme_rectangle") ) + eAttrGeomType = wkbPolygon; + else if( EQUAL(poAttrValue->data(),"fme_rounded_rectangle") ) + eAttrGeomType = wkbPolygon; + else if( EQUAL(poAttrValue->data(),"fme_line") ) + eAttrGeomType = wkbLineString; + else if( EQUAL(poAttrValue->data(),"fme_arc") ) + eAttrGeomType = wkbLineString; + else if( EQUAL(poAttrValue->data(),"fme_aggregate") ) + eAttrGeomType = wkbGeometryCollection; + else if( EQUAL(poAttrValue->data(),"fme_no_geom") ) + eAttrGeomType = wkbNone; + else + { + CPLDebug( "FME_OLEDB", + "geometry field %s has unknown value %s, ignored.", + pszAttrName, + poAttrValue->data() ); + continue; + } + + if( eGeomType == wkbNone ) + eGeomType = eAttrGeomType; + else if( eGeomType != eAttrGeomType ) + eGeomType = wkbUnknown; + } + +/* -------------------------------------------------------------------- */ +/* Skip '*' attributes which appear to be the raw attribute */ +/* names from the source reader. The versions that don't start */ +/* with * appear to be massaged suitably for use, with fme */ +/* standard data types. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszAttrName,"fme_geometry",12) + || pszAttrName[0] == '*' + || EQUALN(pszAttrName,"fme_geomattr",12) ) + { + continue; + } + +/* -------------------------------------------------------------------- */ +/* Parse the type into tokens for easier use. */ +/* -------------------------------------------------------------------- */ + char **papszTokens; + + papszTokens = CSLTokenizeStringComplex( poAttrValue->data(), + "(,", FALSE, FALSE ); + +/* -------------------------------------------------------------------- */ +/* Establish new fields. */ +/* -------------------------------------------------------------------- */ + OGRFieldType eType; + int nWidth, nPrecision = 0; + + if( CSLCount(papszTokens) == 2 && EQUAL(papszTokens[0],"fme_char") ) + { + eType = OFTString; + nWidth = atoi(papszTokens[1]); + } + else if( CSLCount(papszTokens) == 3 + && EQUAL(papszTokens[0],"fme_decimal") ) + { + nWidth = atoi(papszTokens[1]); + nPrecision = atoi(papszTokens[2]); + if( nPrecision == 0 ) + eType = OFTInteger; + else + eType = OFTReal; + } + else if( CSLCount(papszTokens) == 1 + && EQUAL(papszTokens[0],"fme_int16") ) + { + nWidth = 6; + nPrecision = 0; + eType = OFTInteger; + } + else if( CSLCount(papszTokens) == 1 + && EQUAL(papszTokens[0],"fme_int32") ) + { + nWidth = 0; + nPrecision = 0; + eType = OFTInteger; + } + else if( CSLCount(papszTokens) == 1 + && (EQUAL(papszTokens[0],"fme_real32") + || EQUAL(papszTokens[0],"fme_real64")) ) + { + nWidth = 0; + nPrecision = 0; + eType = OFTReal; + } + else if( CSLCount(papszTokens) == 1 + && EQUAL(papszTokens[0],"fme_boolean") ) + { + nWidth = 1; + nPrecision = 0; + eType = OFTInteger; + } + else + { + printf( "Not able to translate field type: %s\n", + poAttrValue->data() ); + CSLDestroy( papszTokens ); + continue; + } + +/* -------------------------------------------------------------------- */ +/* Add the field to the feature definition. */ +/* -------------------------------------------------------------------- */ + OGRFieldDefn oFieldDefn( pszAttrName, eType ); + + oFieldDefn.SetWidth( nWidth ); + oFieldDefn.SetPrecision( nPrecision ); + + poFeatureDefn->AddFieldDefn( &oFieldDefn ); + + CSLDestroy( papszTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Assign the geometry type ... try to apply 3D-ness as well. */ +/* -------------------------------------------------------------------- */ + if( poSchemaFeature->getDimension() == FME_THREE_D ) + eGeomType = wkbSetZ(eGeomType); + + poFeatureDefn->SetGeomType( eGeomType ); + +/* -------------------------------------------------------------------- */ +/* Translate the spatial reference system. */ +/* -------------------------------------------------------------------- */ + if( poSchemaFeature->getCoordSys() != NULL + && strlen(poSchemaFeature->getCoordSys()) > 0 + && poSpatialRef == NULL ) + { + CPLDebug( "FME_OLEDB", "Layer %s has COORDSYS=%s on schema feature.", + poFeatureDefn->GetName(), + poSchemaFeature->getCoordSys() ); + poSpatialRef = poDS->FME2OGRSpatialRef(poSchemaFeature->getCoordSys()); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + poDS->GetFMESession()->destroyString( poAttrValue ); + poDS->GetFMESession()->destroyStringArray( poAttrNames ); + + return TRUE; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRFMELayer::SetAttributeFilter( const char *pszNewFilter ) + +{ + OGRErr eErr; + + CPLFree( pszAttributeFilter ); + pszAttributeFilter = NULL; + +/* -------------------------------------------------------------------- */ +/* Allow clearing of attribute query. */ +/* -------------------------------------------------------------------- */ + if( pszNewFilter == NULL || strlen(pszNewFilter) == 0 ) + { + if( m_poAttrQuery != NULL ) + { + delete m_poAttrQuery; + m_poAttrQuery = NULL; + } + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Compile new query. Note that we currently will only accept */ +/* queries we recognise as valid. It would be better to pass */ +/* directly through to Oracle or other databases where we will */ +/* use the setConstraints method and let them decide on the */ +/* validity of the query. However, it will be difficult to */ +/* return the error at this point if we defer setting the */ +/* constraint. */ +/* -------------------------------------------------------------------- */ + if( !m_poAttrQuery ) + m_poAttrQuery = new OGRFeatureQuery(); + + eErr = m_poAttrQuery->Compile( GetLayerDefn(), pszNewFilter ); + if( eErr != OGRERR_NONE ) + { + delete m_poAttrQuery; + m_poAttrQuery = NULL; + } + else + { + pszAttributeFilter = CPLStrdup( pszNewFilter ); + } + + ResetReading(); + return eErr; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRFMELayer::GetSpatialRef() + +{ + return poSpatialRef; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmelayercached.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmelayercached.cpp new file mode 100644 index 000000000..0bd17b618 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmelayercached.cpp @@ -0,0 +1,484 @@ +/****************************************************************************** + * $Id: ogrfmelayercached.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: FMEObjects Translator + * Purpose: Implementation of the OGRFMELayerCached class. This is the + * class implementing behaviour for layers that are built into a + * temporary spatial cache (as opposed to live read database). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001 Safe Software Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "fme2ogr.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrfmelayercached.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRFMELayerCached() */ +/************************************************************************/ + +OGRFMELayerCached::OGRFMELayerCached( OGRFMEDataSource *poDSIn ) + : OGRFMELayer( poDSIn ) + +{ + nPreviousFeature = -1; + + pszIndexBase = NULL; + poIndex = NULL; + + memset( &sExtents, 0, sizeof(sExtents) ); + + bQueryActive = FALSE; +} + +/************************************************************************/ +/* ~OGRFMELayerCached() */ +/************************************************************************/ + +OGRFMELayerCached::~OGRFMELayerCached() + +{ + CPLFree( pszIndexBase ); + if( poIndex != NULL ) + { +#ifdef SUPPORT_PERSISTENT_CACHE + poIndex->close( FME_FALSE ); +#else + poIndex->close( FME_TRUE ); +#endif + poDS->GetFMESession()->destroySpatialIndex( poIndex ); + } +} + +/************************************************************************/ +/* AssignIndex() */ +/* */ +/* Assign spatial index .. spatial index is opened for read */ +/* access. */ +/************************************************************************/ + +int OGRFMELayerCached::AssignIndex( const char *pszBase, + const OGREnvelope *psExtents, + OGRSpatialReference *poSRS ) + +{ + CPLAssert( poIndex == NULL ); + + pszIndexBase = CPLStrdup( pszBase ); + poIndex = + poDS->GetFMESession()->createSpatialIndex( pszBase, "READ", NULL ); + if( poIndex == NULL ) + return FALSE; + if( poIndex->open() != 0 ) + { + poDS->GetFMESession()->destroySpatialIndex( poIndex ); + poIndex = NULL; + return FALSE; + } + + if( psExtents != NULL ) + sExtents = *psExtents; + + if( poSRS != NULL ) + { + if( poSpatialRef != NULL ) + delete poSpatialRef; + poSpatialRef = poSRS; + } + + return TRUE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRFMELayerCached::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return FALSE; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* ReadNextIndexFeature() */ +/************************************************************************/ + +OGRFeature *OGRFMELayerCached::ReadNextIndexFeature() + +{ + OGRFeature *poOGRFeature = NULL; + FME_Boolean endOfQuery; + + if( poIndex == NULL ) + return NULL; + + if( !bQueryActive ) + ResetReading(); + + poDS->AcquireSession(); + + if( poIndex->fetch( *poFMEFeature, endOfQuery ) == 0 + && !endOfQuery ) + { + poOGRFeature = poDS->ProcessFeature( this, poFMEFeature ); + + if( poOGRFeature != NULL ) + { + poOGRFeature->SetFID( ++nPreviousFeature ); + m_nFeaturesRead++; + } + } + + poDS->ReleaseSession(); + + return poOGRFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRFMELayerCached::GetNextFeature() + +{ + OGRFeature *poFeature; + + while( TRUE ) + { + poFeature = ReadNextIndexFeature(); + + if( poFeature != NULL ) + nPreviousFeature = poFeature->GetFID(); + else + break; + + if( m_poAttrQuery == NULL + || poIndex == NULL + || m_poAttrQuery->Evaluate( poFeature ) ) + break; + + delete poFeature; + } + + return poFeature; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRFMELayerCached::ResetReading() + +{ + nPreviousFeature = -1; + + if( poIndex == NULL ) + { + CPLAssert( FALSE ); + return; + } + + poDS->AcquireSession(); + + if( m_poFilterGeom == NULL ) + { + poIndex->queryAll(); + } + else + { + OGREnvelope oEnvelope; + + m_poFilterGeom->getEnvelope( &oEnvelope ); + + poFMEFeature->resetFeature(); + + poFMEFeature->setDimension( FME_TWO_D ); + poFMEFeature->setGeometryType( FME_GEOM_LINE ); + poFMEFeature->addCoordinate( oEnvelope.MinX, oEnvelope.MinY ); + poFMEFeature->addCoordinate( oEnvelope.MaxX, oEnvelope.MaxY ); + + poIndex->queryEnvelope( *poFMEFeature ); + } + + bQueryActive = TRUE; + + poDS->ReleaseSession(); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRFMELayerCached::GetFeatureCount( int bForce ) + +{ + int nResult; + + poDS->AcquireSession(); + + if( m_poAttrQuery != NULL || poIndex == NULL ) + { + nResult = OGRLayer::GetFeatureCount( bForce ); + } + else + { + ResetReading(); + nResult = poIndex->entries(); + } + + poDS->ReleaseSession(); + + return nResult; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRFMELayerCached::GetExtent( OGREnvelope *psExtent, int /* bForce */ ) + +{ + if( sExtents.MinX == 0.0 && sExtents.MaxX == 0.0 + && sExtents.MinY == 0.0 && sExtents.MaxY == 0.0 ) + return OGRERR_FAILURE; + + *psExtent = sExtents; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *OGRFMELayerCached::SerializeToXML() + +{ + CPLXMLNode *psLayer; + char szGeomType[64]; + + psLayer = CPLCreateXMLNode( NULL, CXT_Element, "OGRLayer" ); + +/* -------------------------------------------------------------------- */ +/* Handle various layer values. */ +/* -------------------------------------------------------------------- */ + CPLCreateXMLElementAndValue( psLayer, "Name", poFeatureDefn->GetName()); + sprintf( szGeomType, "%d", (int) poFeatureDefn->GetGeomType() ); + CPLCreateXMLElementAndValue( psLayer, "GeomType", szGeomType ); + + CPLCreateXMLElementAndValue( psLayer, "SpatialCacheName", + pszIndexBase ); + +/* -------------------------------------------------------------------- */ +/* Handle spatial reference if available. */ +/* -------------------------------------------------------------------- */ + if( GetSpatialRef() != NULL ) + { + char *pszWKT = NULL; + OGRSpatialReference *poSRS = GetSpatialRef(); + + poSRS->exportToWkt( &pszWKT ); + + if( pszWKT != NULL ) + { + CPLCreateXMLElementAndValue( psLayer, "SRS", pszWKT ); + CPLFree( pszWKT ); + } + } + +/* -------------------------------------------------------------------- */ +/* Handle extents if available. */ +/* -------------------------------------------------------------------- */ + OGREnvelope sEnvelope; + if( GetExtent( &sEnvelope, FALSE ) == OGRERR_NONE ) + { + char szExtent[512]; + + sprintf( szExtent, "%24.15E,%24.15E,%24.15E,%24.15E", + sEnvelope.MinX, sEnvelope.MinY, + sEnvelope.MaxX, sEnvelope.MaxY ); + CPLCreateXMLElementAndValue( psLayer, "Extent", szExtent ); + } + +/* -------------------------------------------------------------------- */ +/* Emit the field schemas. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psSchema = CPLCreateXMLNode( psLayer, CXT_Element, "Schema" ); + + for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFieldDef = poFeatureDefn->GetFieldDefn( iField ); + const char *pszType; + char szWidth[32], szPrecision[32]; + CPLXMLNode *psXMLFD; + + sprintf( szWidth, "%d", poFieldDef->GetWidth() ); + sprintf( szPrecision, "%d", poFieldDef->GetPrecision() ); + + if( poFieldDef->GetType() == OFTInteger ) + pszType = "Integer"; + else if( poFieldDef->GetType() == OFTIntegerList ) + pszType = "IntegerList"; + else if( poFieldDef->GetType() == OFTReal ) + pszType = "Real"; + else if( poFieldDef->GetType() == OFTRealList ) + pszType = "RealList"; + else if( poFieldDef->GetType() == OFTString ) + pszType = "String"; + else if( poFieldDef->GetType() == OFTStringList ) + pszType = "StringList"; + else if( poFieldDef->GetType() == OFTBinary ) + pszType = "Binary"; + else + pszType = "Unsupported"; + + psXMLFD = CPLCreateXMLNode( psSchema, CXT_Element, "OGRFieldDefn" ); + CPLCreateXMLElementAndValue( psXMLFD, "Name",poFieldDef->GetNameRef()); + CPLCreateXMLElementAndValue( psXMLFD, "Type", pszType ); + CPLCreateXMLElementAndValue( psXMLFD, "Width", szWidth ); + CPLCreateXMLElementAndValue( psXMLFD, "Precision", szPrecision ); + } + + return psLayer; +} + +/************************************************************************/ +/* InitializeFromXML() */ +/************************************************************************/ + +int OGRFMELayerCached::InitializeFromXML( CPLXMLNode *psLayer ) + +{ +/* -------------------------------------------------------------------- */ +/* Create the feature definition. */ +/* -------------------------------------------------------------------- */ + poFeatureDefn = new OGRFeatureDefn( CPLGetXMLValue(psLayer,"Name","X") ); + poFeatureDefn->Reference(); + +/* -------------------------------------------------------------------- */ +/* Set the geometry type, if available. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psLayer, "GeomType" ) != NULL ) + poFeatureDefn->SetGeomType( (OGRwkbGeometryType) + atoi(CPLGetXMLValue( psLayer, "GeomType", "0" )) ); + +/* -------------------------------------------------------------------- */ +/* If we can extract extents, apply them to the layer. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psLayer, "Extent" ) != NULL ) + { + if( sscanf( CPLGetXMLValue( psLayer, "Extent", "" ), + "%lf,%lf,%lf,%lf", + &sExtents.MinX, + &sExtents.MaxX, + &sExtents.MinY, + &sExtents.MaxY ) != 4 ) + { + memset( &sExtents, 0, sizeof(sExtents) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Apply a SRS if found in the XML. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLNode( psLayer, "SRS" ) != NULL ) + { + char *pszSRS = (char *) CPLGetXMLValue( psLayer, "SRS", "" ); + OGRSpatialReference oSRS; + + if( oSRS.importFromWkt( &pszSRS ) == OGRERR_NONE ) + { + if( poSpatialRef != NULL ) + delete poSpatialRef; + + poSpatialRef = oSRS.Clone(); + } + } + +/* -------------------------------------------------------------------- */ +/* Apply the Schema. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psFieldDef; + + for( psFieldDef = CPLGetXMLNode( psLayer, "Schema.OGRFieldDefn" ); + psFieldDef != NULL; psFieldDef = psFieldDef->psNext ) + { + const char *pszType; + OGRFieldType eType; + + if( !EQUAL(psFieldDef->pszValue,"OGRFieldDefn") ) + continue; + + pszType = CPLGetXMLValue( psFieldDef, "type", "String" ); + if( EQUAL(pszType,"String") ) + eType = OFTString; + else if( EQUAL(pszType,"StringList") ) + eType = OFTStringList; + else if( EQUAL(pszType,"Integer") ) + eType = OFTInteger; + else if( EQUAL(pszType,"IntegerList") ) + eType = OFTIntegerList; + else if( EQUAL(pszType,"Real") ) + eType = OFTReal; + else if( EQUAL(pszType,"RealList") ) + eType = OFTRealList; + else if( EQUAL(pszType,"Binary") ) + eType = OFTBinary; + else + eType = OFTString; + + OGRFieldDefn oField( CPLGetXMLValue( psFieldDef, "name", "default" ), + eType ); + + oField.SetWidth( atoi(CPLGetXMLValue(psFieldDef,"width","0")) ); + oField.SetPrecision( atoi(CPLGetXMLValue(psFieldDef,"precision","0"))); + + poFeatureDefn->AddFieldDefn( &oField ); + } + +/* -------------------------------------------------------------------- */ +/* Ensure we have a working string and feature object */ +/* -------------------------------------------------------------------- */ + poFMEFeature = poDS->GetFMESession()->createFeature(); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmelayerdb.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmelayerdb.cpp new file mode 100644 index 000000000..0b61a5476 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/fme/ogrfmelayerdb.cpp @@ -0,0 +1,443 @@ +/****************************************************************************** + * $Id: ogrfmelayerdb.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: FMEObjects Translator + * Purpose: Implementation of the OGRFMELayerDB class. This is the + * class implementing behaviour for layers that are built on + * smart readers representing databases with spatial constraints, + * and where clause support. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001 Safe Software Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "fme2ogr.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrfmelayerdb.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRFMELayerDB() */ +/************************************************************************/ + +OGRFMELayerDB::OGRFMELayerDB( OGRFMEDataSource *poDSIn, + const char *pszReaderNameIn, + const char *pszDatasetIn, + IFMEStringArray *poUserDirectivesIn ) + : OGRFMELayer( poDSIn ) + +{ + nPreviousFeature = -1; + + poReader = NULL; + + pszReaderName = CPLStrdup( pszReaderNameIn ); + pszDataset = CPLStrdup( pszDatasetIn ); + + poUserDirectives = poDS->GetFMESession()->createStringArray(); + + for( FME_UInt32 i = 0; i < poUserDirectivesIn->entries(); i++ ) + { + CPLDebug( "FMEOLEDB", "userDirective[%d] = %s\n", + i, (const char *) (*poUserDirectivesIn)(i) ); + + poUserDirectives->append( (*poUserDirectivesIn)(i) ); + } +} + +/************************************************************************/ +/* ~OGRFMELayerDB() */ +/************************************************************************/ + +OGRFMELayerDB::~OGRFMELayerDB() + +{ + if( poReader != NULL ) + poDS->GetFMESession()->destroyReader( poReader ); + + CPLFree( pszReaderName ); + CPLFree( pszDataset ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRFMELayerDB::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return FALSE; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRFMELayerDB::GetNextFeature() + +{ + OGRFeature *poFeature; + FME_Boolean eEndOfSchema; + FME_MsgNum err; + + poDS->AcquireSession(); + + if( poReader == NULL ) + { + if( !CreateReader() ) + { + return NULL; + poDS->ReleaseSession(); + } + } + + err = poReader->read( *poFMEFeature, eEndOfSchema ); + + if( err ) + { + CPLFMEError( poDS->GetFMESession(), "Error while reading feature." ); + poDS->ReleaseSession(); + return NULL; + } + + if( eEndOfSchema == FME_TRUE ) + { + poDS->ReleaseSession(); + return NULL; + } + + poFeature = poDS->ProcessFeature( this, poFMEFeature ); + + if( nPreviousFeature == -1 ) + CPLDebug( "FMEOLEDB", "Fetching first feature from layer `%s'.", + GetLayerDefn()->GetName() ); + + poFeature->SetFID( ++nPreviousFeature ); + m_nFeaturesRead++; + + poDS->ReleaseSession(); + + return poFeature; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRFMELayerDB::ResetReading() + +{ + nPreviousFeature = -1; + + poDS->AcquireSession(); + +/* -------------------------------------------------------------------- */ +/* Blow away existing reader, if we have one. */ +/* -------------------------------------------------------------------- */ + if( poReader != NULL ) + poDS->GetFMESession()->destroyReader( poReader ); + poReader = NULL; + + poDS->ReleaseSession(); +} + +/************************************************************************/ +/* SetMacro() */ +/* */ +/* Set the value of one macro within a set of macros stored in */ +/* comma delimeted name value pairs (as per RUNTIME_MACROS in */ +/* user directives). */ +/************************************************************************/ + +static void SetMacro( IFMEString *poMacros, const char *pszTarget, + const char *pszNewValue ) + +{ + char *pszWorking, *pszValStart; + int nOldValLength; + + pszWorking = (char *) CPLMalloc(strlen(poMacros->data()) + + strlen(pszNewValue) + + strlen(pszTarget) + 20 ); + strcpy( pszWorking, poMacros->data() ); + + pszValStart = strstr( pszWorking, pszTarget ); + if( pszValStart == NULL + || pszValStart[strlen(pszTarget)] != ',' ) + { + if( strlen(pszWorking) > 0 ) + strcat( pszWorking, "," ); + + sprintf( pszWorking + strlen(pszWorking), "%s,%s", + pszTarget, pszNewValue ); + *poMacros = pszWorking; + CPLFree( pszWorking ); + return; + } + + pszValStart += strlen(pszTarget) + 1; + + for( nOldValLength = 0; + pszValStart[nOldValLength] != ',' + && pszValStart[nOldValLength] != '\0'; + nOldValLength++ ) {} + + memmove( pszValStart + strlen(pszNewValue), + pszValStart + nOldValLength, + strlen(pszValStart + nOldValLength)+1 ); + + memcpy( pszValStart, pszNewValue, strlen( pszNewValue ) ); + + *poMacros = pszWorking; + CPLFree( pszWorking ); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRFMELayerDB::SetAttributeFilter( const char *pszNewFilter ) + +{ + CPLFree( pszAttributeFilter ); + pszAttributeFilter = NULL; + + if( pszNewFilter != NULL ) + pszAttributeFilter = CPLStrdup( pszNewFilter ); + + ResetReading(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateReader() */ +/************************************************************************/ + +int OGRFMELayerDB::CreateReader() + +{ + FME_MsgNum err; + IFMESession *poSession = poDS->GetFMESession(); + FME_UInt32 i; + + CPLAssert( poReader == NULL && nPreviousFeature == -1 ); + +/* -------------------------------------------------------------------- */ +/* Make a copy of the user directives, so we won't be altering */ +/* the originals. */ +/* -------------------------------------------------------------------- */ + IFMEStringArray *poUDC = poSession->createStringArray(); + + for( i = 0; i < poUserDirectives->entries(); i++ ) + poUDC->append( (*poUserDirectives)(i) ); + +/* -------------------------------------------------------------------- */ +/* Update the IDLIST to just select the desired table. */ +/* -------------------------------------------------------------------- */ + + for( i = 0; i < poUDC->entries(); i++ ) + { + if( EQUAL((const char *) (*poUDC)(i),"IDLIST") ) + { + IFMEString *poIDList = poSession->createString(); + *poIDList = GetLayerDefn()->GetName(); + poUDC->setElement( i+1, *poIDList ); + poSession->destroyString( poIDList ); + break; + } + } + + if( i == poUDC->entries() ) + { + poUDC->append( "IDLIST" ); + poUDC->append( GetLayerDefn()->GetName() ); + } +/* -------------------------------------------------------------------- */ +/* Update the macros for source information, if needed. */ +/* -------------------------------------------------------------------- */ + if( m_poFilterGeom != NULL ) + { + const char *pszDirective = "RUNTIME_MACROS"; + + if( !poUDC->contains(pszDirective) ) + { + poUDC->append(pszDirective); + poUDC->append(""); + } + for( i = 0; i < poUDC->entries(); i++ ) + { + if( EQUAL((const char *) (*poUDC)(i),pszDirective) ) + { + IFMEString *poMacroValue = poSession->createString(); + char szSEARCH_ENVELOPE[1024]; + OGREnvelope oEnvelope; + + poUDC->getElement( i+1, *poMacroValue ); + + m_poFilterGeom->getEnvelope( &oEnvelope ); + + if( EQUALN(pszReaderName,"SDE",3) ) + { + sprintf( szSEARCH_ENVELOPE, "%.16f", oEnvelope.MinX ); + SetMacro( poMacroValue, "_SDE3MINX", szSEARCH_ENVELOPE ); + + sprintf( szSEARCH_ENVELOPE, "%.16f", oEnvelope.MinY ); + SetMacro( poMacroValue, "_SDE3MINY", szSEARCH_ENVELOPE ); + + sprintf( szSEARCH_ENVELOPE, "%.16f", oEnvelope.MaxX ); + SetMacro( poMacroValue, "_SDE3MAXX", szSEARCH_ENVELOPE ); + + sprintf( szSEARCH_ENVELOPE, "%.16f", oEnvelope.MaxY ); + SetMacro( poMacroValue, "_SDE3MAXY", szSEARCH_ENVELOPE ); + } + else + { + sprintf( szSEARCH_ENVELOPE, "%.16f", oEnvelope.MinX ); + SetMacro( poMacroValue, "_ORACLE_MINX", szSEARCH_ENVELOPE); + + sprintf( szSEARCH_ENVELOPE, "%.16f", oEnvelope.MinY ); + SetMacro( poMacroValue, "_ORACLE_MINY", szSEARCH_ENVELOPE); + + sprintf( szSEARCH_ENVELOPE, "%.16f", oEnvelope.MaxX ); + SetMacro( poMacroValue, "_ORACLE_MAXX", szSEARCH_ENVELOPE); + + sprintf( szSEARCH_ENVELOPE, "%.16f", oEnvelope.MaxY ); + SetMacro( poMacroValue, "_ORACLE_MAXY", szSEARCH_ENVELOPE); + } + + poUDC->setElement( i+1, *poMacroValue ); + + CPLDebug( "FMEOLEDB", "Update %s to:\n%s", + pszDirective, poMacroValue->data() ); + + poSession->destroyString( poMacroValue ); + break; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create new reader with desired constraints. */ +/* -------------------------------------------------------------------- */ + poReader = poSession->createReader(pszReaderName, FME_FALSE, poUDC); + poSession->destroyStringArray( poUDC ); + if( poReader == NULL ) + { + CPLFMEError( poSession, + "Failed to create reader of type `%s'.\n", + pszReaderName ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Setup constraints applied in open(). */ +/* -------------------------------------------------------------------- */ + IFMEStringArray *poParms = poSession->createStringArray(); + + if( pszAttributeFilter != NULL && strlen(pszAttributeFilter) > 0 ) + { + if( EQUALN(pszReaderName,"SDE",3) ) + poParms->append( "WHERE" ); + else + poParms->append( "WHERE_CLAUSE" ); + + poParms->append( pszAttributeFilter ); + } +#ifdef notdef + if( m_poFilterGeom != NULL ) + { + char szSEARCH_ENVELOPE[1024]; + OGREnvelope oEnvelope; + + m_poFilterGeom->getEnvelope( &oEnvelope ); + + sprintf( szSEARCH_ENVELOPE, "%.16f", oEnvelope.MinX ); + poParms->append( "SEARCH_ENVELOPE" ); + poParms->append( szSEARCH_ENVELOPE ); + + sprintf( szSEARCH_ENVELOPE, "%.16f", oEnvelope.MinY ); + poParms->append( "SEARCH_ENVELOPE" ); + poParms->append( szSEARCH_ENVELOPE ); + + sprintf( szSEARCH_ENVELOPE, "%.16f", oEnvelope.MaxX ); + poParms->append( "SEARCH_ENVELOPE" ); + poParms->append( szSEARCH_ENVELOPE ); + + sprintf( szSEARCH_ENVELOPE, "%.16f", oEnvelope.MaxY ); + poParms->append( "SEARCH_ENVELOPE" ); + poParms->append( szSEARCH_ENVELOPE ); + } +#endif + + for( i = 0; i < poParms->entries(); i++ ) + { + CPLDebug( "FMEOLEDB", "openParms[%d] = %s", + i, (const char *) (*poParms)(i) ); + } + +/* -------------------------------------------------------------------- */ +/* Now try to open the dataset. */ +/* -------------------------------------------------------------------- */ + err = poReader->open( pszDataset, *poParms ); + if( err ) + { + CPLFMEError( poSession, + "Failed to open dataset `%s' with reader of type `%s'.\n", + pszDataset, pszReaderName ); + return FALSE; + } + + poSession->destroyStringArray( poParms ); + + return TRUE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRFMELayerDB::GetFeatureCount( int bForce ) + +{ + /* + ** This could be improved by just reading through the FME Features + ** without having to convert to OGRFeatures. Optimization deferred. + */ + + return OGRLayer::GetFeatureCount( bForce ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/GNUmakefile new file mode 100644 index 000000000..70a2e70b6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/GNUmakefile @@ -0,0 +1,228 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrsfdriverregistrar.o ogrlayer.o ogrdatasource.o \ + ogrsfdriver.o ogrregisterall.o ogr_gensql.o \ + ogr_attrind.o ogr_miattrind.o ogrlayerdecorator.o \ + ogrwarpedlayer.o ogrunionlayer.o ogrlayerpool.o \ + ogrmutexedlayer.o ogrmutexeddatasource.o \ + ogremulatedtransaction.o + +CXXFLAGS := $(CXXFLAGS) -DINST_DATA=\"$(INST_DATA)\" + +ifeq ($(OGR_ENABLED),yes) + +BASEFORMATS = \ + -DAVCBIN_ENABLED \ + -DBNA_ENABLED \ + -DCSV_ENABLED \ + -DDGN_ENABLED \ + -DGML_ENABLED \ + -DGMT_ENABLED \ + -DGPX_ENABLED \ + -DMEM_ENABLED \ + -DNTF_ENABLED \ + -DREC_ENABLED \ + -DS57_ENABLED \ + -DSDTS_ENABLED \ + -DSHAPE_ENABLED \ + -DTAB_ENABLED \ + -DTIGER_ENABLED \ + -DVRT_ENABLED \ + -DKML_ENABLED \ + -DGEOJSON_ENABLED \ + -DGEOCONCEPT_ENABLED \ + -DXPLANE_ENABLED \ + -DGEORSS_ENABLED \ + -DGTM_ENABLED \ + -DDXF_ENABLED \ + -DPGDUMP_ENABLED \ + -DGPSBABEL_ENABLED \ + -DSUA_ENABLED \ + -DOPENAIR_ENABLED \ + -DPDS_ENABLED \ + -DHTF_ENABLED \ + -DAERONAVFAA_ENABLED \ + -DEDIGEO_ENABLED \ + -DSVG_ENABLED \ + -DIDRISI_ENABLED \ + -DARCGEN_ENABLED \ + -DSEGUKOOA_ENABLED \ + -DSEGY_ENABLED \ + -DSXF_ENABLED \ + -DOPENFILEGDB_ENABLED \ + -DWASP_ENABLED \ + -DSELAFIN_ENABLED \ + -DJML_ENABLED + +CXXFLAGS := $(CXXFLAGS) $(BASEFORMATS) + +ifeq ($(HAVE_OGDI),yes) +CXXFLAGS := $(CXXFLAGS) -DOGDI_ENABLED +endif + +ifeq ($(HAVE_OCI),yes) +CXXFLAGS := $(CXXFLAGS) -DOCI_ENABLED +endif + +ifeq ($(HAVE_SDE),yes) +CXXFLAGS := $(CXXFLAGS) -DSDE_ENABLED +endif + +ifeq ($(HAVE_FGDB),yes) +CXXFLAGS := $(CXXFLAGS) -DFGDB_ENABLED +endif + +ifeq ($(HAVE_OGR_PG),yes) +CXXFLAGS := $(CXXFLAGS) -DPG_ENABLED +endif + +ifeq ($(HAVE_MYSQL),yes) +CXXFLAGS := $(CXXFLAGS) -DMYSQL_ENABLED +endif + +ifeq ($(HAVE_INGRES),yes) +CXXFLAGS := $(CXXFLAGS) -DINGRES_ENABLED +endif + +ifeq ($(PCIDSK_SETTING),internal) +CXXFLAGS := $(CXXFLAGS) -DPCIDSK_ENABLED +endif + +ifeq ($(PCIDSK_SETTING),external) +CXXFLAGS := $(CXXFLAGS) -DPCIDSK_ENABLED +endif + +ifeq ($(HAVE_FME),yes) +CXXFLAGS := $(CXXFLAGS) -DFME_ENABLED +endif + +ifeq ($(ODBC_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DODBC_ENABLED +endif + +ifeq ($(PGEO_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DPGEO_ENABLED +endif + +ifeq ($(MSSQLSPATIAL_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DMSSQLSPATIAL_ENABLED +endif + +ifeq ($(HAVE_DODS),yes) +CXXFLAGS := $(CXXFLAGS) -DDODS_ENABLED +endif + +ifeq ($(HAVE_SQLITE),yes) +CXXFLAGS := $(CXXFLAGS) -DSQLITE_ENABLED +endif + +ifeq ($(HAVE_GRASS),yes) +CXXFLAGS := $(CXXFLAGS) -DGRASS_ENABLED +endif + +ifeq ($(HAVE_XERCES),yes) +CXXFLAGS := $(CXXFLAGS) -DILI_ENABLED +endif + +ifeq ($(HAVE_NAS),yes) +CXXFLAGS := $(CXXFLAGS) -DNAS_ENABLED +endif + +ifeq ($(HAVE_LIBKML),yes) +CXXFLAGS := $(CXXFLAGS) -DLIBKML_ENABLED +endif + +ifeq ($(HAVE_DWGDIRECT),yes) +CXXFLAGS := $(CXXFLAGS) -DDWGDIRECT_ENABLED +endif + +ifeq ($(HAVE_IDB),yes) +CXXFLAGS := $(CXXFLAGS) -DIDB_ENABLED +endif + +ifeq ($(HAVE_PANORAMA),yes) +CXXFLAGS := $(CXXFLAGS) -DPANORAMA_ENABLED +endif + +ifeq ($(HAVE_SOSI),yes) +CXXFLAGS := $(CXXFLAGS) -DSOSI_ENABLED +endif + +ifeq ($(HAVE_SQLITE),yes) +CXXFLAGS := $(CXXFLAGS) -DVFK_ENABLED +endif + +ifeq ($(CURL_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DWFS_ENABLED +CXXFLAGS := $(CXXFLAGS) -DCSW_ENABLED +endif + +ifeq ($(GEOMEDIA_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DGEOMEDIA_ENABLED +endif + +ifeq ($(MDB_ENABLED),yes) +CXXFLAGS := $(CXXFLAGS) -DMDB_ENABLED +endif + +ifeq ($(CURL_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DGFT_ENABLED +endif + +ifeq ($(CURL_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DGME_ENABLED +endif + +ifeq ($(CURL_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DCOUCHDB_ENABLED +endif + +ifeq ($(CURL_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DCLOUDANT_ENABLED +endif + +ifeq ($(HAVE_FREEXL),yes) +CXXFLAGS := $(CXXFLAGS) -DFREEXL_ENABLED +endif + +ifeq ($(HAVE_EXPAT),yes) +CXXFLAGS := $(CXXFLAGS) -DODS_ENABLED +endif + +ifeq ($(HAVE_EXPAT),yes) +CXXFLAGS := $(CXXFLAGS) -DXLSX_ENABLED +endif + +ifeq ($(CURL_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DELASTIC_ENABLED +endif + +ifeq ($(HAVE_SQLITE),yes) +CXXFLAGS := $(CXXFLAGS) -DOSM_ENABLED +endif + +ifeq ($(ODBC_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DWALK_ENABLED +endif + +ifeq ($(CURL_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DCARTODB_ENABLED +endif + +ifeq ($(CURL_SETTING),yes) +CXXFLAGS := $(CXXFLAGS) -DPLSCENES_ENABLED +endif + +endif + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ../../../GDALmake.opt ../../swq.h + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/makefile.vc new file mode 100644 index 000000000..00de6bac0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/makefile.vc @@ -0,0 +1,169 @@ + +OBJ = ogrsfdriverregistrar.obj ogrlayer.obj ogr_gensql.obj \ + ogrdatasource.obj ogrsfdriver.obj ogrregisterall.obj \ + ogr_attrind.obj ogr_miattrind.obj ogrlayerdecorator.obj \ + ogrwarpedlayer.obj ogrunionlayer.obj ogrlayerpool.obj \ + ogrmutexedlayer.obj ogrmutexeddatasource.obj \ + ogremulatedtransaction.obj + + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF INCLUDE_OGR_FRMTS + +BASEFORMATS = -DSHAPE_ENABLED -DTAB_ENABLED -DNTF_ENABLED -DSDTS_ENABLED -DTIGER_ENABLED -DS57_ENABLED -DDGN_ENABLED -DVRT_ENABLED -DAVCBIN_ENABLED -DREC_ENABLED -DMEM_ENABLED -DCSV_ENABLED -DGML_ENABLED -DGMT_ENABLED -DBNA_ENABLED -DKML_ENABLED -DGEOJSON_ENABLED -DGPX_ENABLED -DGEOCONCEPT_ENABLED -DXPLANE_ENABLED -DGEORSS_ENABLED -DGTM_ENABLED -DDXF_ENABLED -DPGDUMP_ENABLED -DGPSBABEL_ENABLED -DSUA_ENABLED -DOPENAIR_ENABLED -DPDS_ENABLED -DHTF_ENABLED -DAERONAVFAA_ENABLED -DEDIGEO_ENABLED -DSVG_ENABLED -DIDRISI_ENABLED -DARCGEN_ENABLED -DSEGUKOOA_ENABLED -DSEGY_ENABLED -DSXF_ENABLED -DOPENFILEGDB_ENABLED -DWASP_ENABLED -DSELAFIN_ENABLED -DJML_ENABLED + +EXTRAFLAGS = -I.. -I..\.. $(OGDIDEF) $(FMEDEF) $(OCIDEF) $(PGDEF) \ + $(ODBCDEF) $(SQLITEDEF) $(MYSQLDEF) $(ILIDEF) $(DWGDEF) \ + $(SDEDEF) $(BASEFORMATS) $(IDBDEF) $(NASDEF) $(DODSDEF) \ + $(LIBKMLDEF) $(WFSDEF) $(SOSIDEF) $(GFTDEF) \ + $(COUCHDBDEF) $(CLOUDANTDEF) $(FGDBDEF) $(XLSDEF) $(ODSDEF) $(XLSXDEF) $(INGRESDEF) \ + $(ELASTICDEF) $(OSMDEF) $(VFKDEF) $(CARTODBDEF) $(GMEDEF) $(PLSCENESDEF) $(CSWDEF) + +!IFDEF OGDIDIR +OGDIDEF = -DOGDI_ENABLED +!ENDIF + +!IFDEF ODBC_SUPPORTED +ODBCDEF = -DODBC_ENABLED -DPGEO_ENABLED -DMSSQLSPATIAL_ENABLED -DGEOMEDIA_ENABLED -DWALK_ENABLED +!ENDIF + +!IFDEF PG_LIB +!IFNDEF PG_PLUGIN +PGDEF = -DPG_ENABLED +!ENDIF +!ENDIF + +!IFDEF MYSQL_LIB +MYSQLDEF = -DMYSQL_ENABLED +!ENDIF + +!IFDEF SQLITE_LIB +SQLITEDEF = -DSQLITE_ENABLED +!ENDIF + +!IFDEF INGRES_HOME +!IFNDEF INGRES_PLUGIN +INGRESDEF = -DINGRES_ENABLED +!ENDIF +!ENDIF + +!IFDEF OCI_LIB +!IFNDEF OCI_PLUGIN +OCIDEF = -DOCI_ENABLED +!ENDIF +!ENDIF + +!IFDEF FME_DIR +FMEDEF = -DFME_ENABLED +!ENDIF + +!IFDEF ILI_ENABLED +ILIDEF = -DILI_ENABLED +!ENDIF + +!IFDEF DWGDIRECT +!IF "$(DWG_PLUGIN)" != "YES" +ILIDEF = -DDWGDIRECT_ENABLED +!ENDIF +!ENDIF + +!IFDEF SDE_ENABLED +!IF "$(SDE_PLUGIN)" != "YES" +SDEDEF = -DSDE_ENABLED +!ENDIF +!ENDIF + +!IFDEF INFORMIXDIR +IDBDEF = -DIDB_ENABLED +!ENDIF + +!IFDEF NAS_ENABLED +NASDEF = -DNAS_ENABLED +!ENDIF + +!IFDEF DODS_DIR +DODSDEF = -DDODS_ENABLED +!ENDIF + +!IFDEF LIBKML_DIR +!IFNDEF LIBKML_PLUGIN +LIBKMLDEF = -DLIBKML_ENABLED +!ENDIF +!ENDIF + +!IFDEF CURL_LIB +WFSDEF = -DWFS_ENABLED +CSWDEF = -DCSW_ENABLED +!ENDIF + +!IFDEF SOSI_ENABLED +SOSIDEF = -DSOSI_ENABLED +!ENDIF + +!IFDEF CURL_LIB +GFTDEF = -DGFT_ENABLED +!ENDIF + +!IFDEF CURL_LIB +COUCHDBDEF = -DCOUCHDB_ENABLED +!ENDIF + +!IFDEF CURL_LIB +CLOUDANTDEF = -DCLOUDANT_ENABLED +!ENDIF + +!IFDEF FGDB_LIB +!IF "$(FGDB_PLUGIN)" != "YES" +FGDBDEF = -DFGDB_ENABLED +!ENDIF +!ENDIF + +!IFDEF FREEXL_LIBS +XLSDEF = -DFREEXL_ENABLED +!ENDIF + +!IFDEF EXPAT_INCLUDE +ODSDEF = -DODS_ENABLED +!ENDIF + +!IFDEF EXPAT_INCLUDE +XLSXDEF = -DXLSX_ENABLED +!ENDIF + +!IFDEF CURL_LIB +ELASTICDEF = -DELASTIC_ENABLED +!ENDIF + +!IFDEF SQLITE_LIB +OSMDEF = -DOSM_ENABLED +VFKDEF = -DVFK_ENABLED +!ENDIF + +!IFDEF CURL_LIB +CARTODBDEF = -DCARTODB_ENABLED +!ENDIF + +!IFDEF CURL_LIB +GMEDEF = -DGME_ENABLED +!ENDIF + +!IFDEF CURL_LIB +PLSCENESDEF = -DPLSCENES_ENABLED +!ENDIF + +!ELSE + +EXTRAFLAGS = -I.. -I..\.. + +!ENDIF + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogr_attrind.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogr_attrind.cpp new file mode 100644 index 000000000..3354f3a42 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogr_attrind.cpp @@ -0,0 +1,84 @@ +/****************************************************************************** + * $Id: ogr_attrind.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implementation of OGRLayerAttrIndex and OGRAttrIndex base classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_attrind.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogr_attrind.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* ==================================================================== */ +/* OGRLayerAttrIndex */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* OGRLayerAttrIndex() */ +/************************************************************************/ + +OGRLayerAttrIndex::OGRLayerAttrIndex() + +{ + poLayer = NULL; + pszIndexPath = NULL; +} + +/************************************************************************/ +/* ~OGRLayerAttrIndex() */ +/************************************************************************/ + +OGRLayerAttrIndex::~OGRLayerAttrIndex() + +{ + CPLFree( pszIndexPath ); + pszIndexPath = NULL; +} + +/************************************************************************/ +/* ==================================================================== */ +/* OGRAttrIndex */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* OGRAttrIndex() */ +/************************************************************************/ + +OGRAttrIndex::OGRAttrIndex() + +{ +} + +/************************************************************************/ +/* ~OGRAttrIndex() */ +/************************************************************************/ + +OGRAttrIndex::~OGRAttrIndex() +{ +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogr_gensql.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogr_gensql.cpp new file mode 100644 index 000000000..f0dc8880c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogr_gensql.cpp @@ -0,0 +1,2280 @@ +/****************************************************************************** + * $Id: ogr_gensql.cpp 28927 2015-04-17 08:42:26Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGenSQLResultsLayer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "swq.h" +#include "ogr_p.h" +#include "ogr_gensql.h" +#include "cpl_string.h" +#include "ogr_api.h" +#include "cpl_time.h" +#include + +CPL_CVSID("$Id: ogr_gensql.cpp 28927 2015-04-17 08:42:26Z rouault $"); + + +class OGRGenSQLGeomFieldDefn: public OGRGeomFieldDefn +{ + public: + OGRGenSQLGeomFieldDefn(OGRGeomFieldDefn* poGeomFieldDefn) : + OGRGeomFieldDefn(poGeomFieldDefn->GetNameRef(), + poGeomFieldDefn->GetType()), bForceGeomType(FALSE) + { + SetSpatialRef(poGeomFieldDefn->GetSpatialRef()); + } + + int bForceGeomType; +}; + +/************************************************************************/ +/* OGRGenSQLResultsLayerHasSpecialField() */ +/************************************************************************/ + +static +int OGRGenSQLResultsLayerHasSpecialField(swq_expr_node* expr, + int nMinIndexForSpecialField) +{ + if (expr->eNodeType == SNT_COLUMN) + { + if (expr->table_index == 0) + { + return expr->field_index >= nMinIndexForSpecialField && + expr->field_index < nMinIndexForSpecialField + SPECIAL_FIELD_COUNT; + } + } + else if (expr->eNodeType == SNT_OPERATION) + { + for( int i = 0; i < expr->nSubExprCount; i++ ) + { + if (OGRGenSQLResultsLayerHasSpecialField(expr->papoSubExpr[i], + nMinIndexForSpecialField)) + return TRUE; + } + } + return FALSE; +} + +/************************************************************************/ +/* OGRGenSQLResultsLayer() */ +/************************************************************************/ + +OGRGenSQLResultsLayer::OGRGenSQLResultsLayer( GDALDataset *poSrcDS, + void *pSelectInfo, + OGRGeometry *poSpatFilter, + const char *pszWHERE, + const char *pszDialect ) + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + + this->poSrcDS = poSrcDS; + this->pSelectInfo = pSelectInfo; + poDefn = NULL; + poSummaryFeature = NULL; + panFIDIndex = NULL; + bOrderByValid = FALSE; + nIndexSize = 0; + nNextIndexFID = 0; + nExtraDSCount = 0; + papoExtraDS = NULL; + panGeomFieldToSrcGeomField = NULL; + +/* -------------------------------------------------------------------- */ +/* Identify all the layers involved in the SELECT. */ +/* -------------------------------------------------------------------- */ + int iTable; + + papoTableLayers = (OGRLayer **) + CPLCalloc( sizeof(OGRLayer *), psSelectInfo->table_count ); + + for( iTable = 0; iTable < psSelectInfo->table_count; iTable++ ) + { + swq_table_def *psTableDef = psSelectInfo->table_defs + iTable; + GDALDataset *poTableDS = poSrcDS; + + if( psTableDef->data_source != NULL ) + { + poTableDS = (GDALDataset*) GDALOpenEx( psTableDef->data_source, + GDAL_OF_VECTOR | GDAL_OF_SHARED, NULL, NULL, NULL ); + if( poTableDS == NULL ) + { + if( strlen(CPLGetLastErrorMsg()) == 0 ) + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open secondary datasource\n" + "`%s' required by JOIN.", + psTableDef->data_source ); + return; + } + + papoExtraDS = (GDALDataset **) + CPLRealloc( papoExtraDS, sizeof(void*) * ++nExtraDSCount ); + + papoExtraDS[nExtraDSCount-1] = poTableDS; + } + + papoTableLayers[iTable] = + poTableDS->GetLayerByName( psTableDef->table_name ); + + CPLAssert( papoTableLayers[iTable] != NULL ); + + if( papoTableLayers[iTable] == NULL ) + return; + } + + poSrcLayer = papoTableLayers[0]; + +/* -------------------------------------------------------------------- */ +/* If the user has explicitly requested a OGRSQL dialect, then */ +/* we should avoid to forward the where clause to the source layer */ +/* when there is a risk it cannot understand it (#4022) */ +/* -------------------------------------------------------------------- */ + int bForwardWhereToSourceLayer = TRUE; + if( pszWHERE ) + { + if( psSelectInfo->where_expr && pszDialect != NULL && + EQUAL(pszDialect, "OGRSQL") ) + { + int nMinIndexForSpecialField = poSrcLayer->GetLayerDefn()->GetFieldCount(); + bForwardWhereToSourceLayer = !OGRGenSQLResultsLayerHasSpecialField + (psSelectInfo->where_expr, nMinIndexForSpecialField); + } + if (bForwardWhereToSourceLayer) + this->pszWHERE = CPLStrdup(pszWHERE); + else + this->pszWHERE = NULL; + } + else + this->pszWHERE = NULL; + +/* -------------------------------------------------------------------- */ +/* Prepare a feature definition based on the query. */ +/* -------------------------------------------------------------------- */ + OGRFeatureDefn *poSrcDefn = poSrcLayer->GetLayerDefn(); + + poDefn = new OGRFeatureDefn( psSelectInfo->table_defs[0].table_alias ); + SetDescription( poDefn->GetName() ); + poDefn->SetGeomType(wkbNone); + poDefn->Reference(); + + iFIDFieldIndex = poSrcDefn->GetFieldCount(); + + /* + 1 since we can add an implicit geometry field */ + panGeomFieldToSrcGeomField = (int*) CPLMalloc(sizeof(int) * (1 + psSelectInfo->result_columns)); + + for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + OGRFieldDefn oFDefn( "", OFTInteger ); + OGRGeomFieldDefn oGFDefn( "", wkbUnknown ); + OGRFieldDefn *poSrcFDefn = NULL; + OGRGeomFieldDefn *poSrcGFDefn = NULL; + int bIsGeometry = FALSE; + OGRFeatureDefn *poLayerDefn = NULL; + int iSrcGeomField = -1; + + if( psColDef->table_index != -1 ) + poLayerDefn = + papoTableLayers[psColDef->table_index]->GetLayerDefn(); + + if( psColDef->field_index > -1 + && poLayerDefn != NULL + && psColDef->field_index < poLayerDefn->GetFieldCount() ) + { + poSrcFDefn = poLayerDefn->GetFieldDefn(psColDef->field_index); + } + + if( poLayerDefn != NULL && + IS_GEOM_FIELD_INDEX(poLayerDefn, psColDef->field_index) ) + { + bIsGeometry = TRUE; + iSrcGeomField = + ALL_FIELD_INDEX_TO_GEOM_FIELD_INDEX(poLayerDefn, psColDef->field_index); + poSrcGFDefn = poLayerDefn->GetGeomFieldDefn(iSrcGeomField); + } + + if( psColDef->target_type == SWQ_GEOMETRY ) + bIsGeometry = TRUE; + + if( psColDef->col_func == SWQCF_COUNT ) + bIsGeometry = FALSE; + + if( strlen(psColDef->field_name) == 0 && !bIsGeometry ) + { + CPLFree( psColDef->field_name ); + psColDef->field_name = (char *) CPLMalloc(40); + sprintf( psColDef->field_name, "FIELD_%d", poDefn->GetFieldCount()+1 ); + } + + if( psColDef->field_alias != NULL ) + { + if( bIsGeometry ) + oGFDefn.SetName(psColDef->field_alias); + else + oFDefn.SetName(psColDef->field_alias); + } + else if( psColDef->col_func != SWQCF_NONE ) + { + const swq_operation *op = swq_op_registrar::GetOperator( + (swq_op) psColDef->col_func ); + + oFDefn.SetName( CPLSPrintf( "%s_%s", + op->pszName, + psColDef->field_name ) ); + } + else + { + CPLString osName; + if( psColDef->table_name[0] ) + { + osName = psColDef->table_name; + osName += "."; + } + osName += psColDef->field_name; + + if( bIsGeometry ) + oGFDefn.SetName(osName); + else + oFDefn.SetName(osName); + } + + if( psColDef->col_func == SWQCF_COUNT ) + oFDefn.SetType( OFTInteger64 ); + else if( poSrcFDefn != NULL ) + { + if( psColDef->col_func != SWQCF_AVG || + psColDef->field_type == SWQ_DATE || + psColDef->field_type == SWQ_TIME || + psColDef->field_type == SWQ_TIMESTAMP ) + { + oFDefn.SetType( poSrcFDefn->GetType() ); + if( psColDef->col_func == SWQCF_NONE || + psColDef->col_func == SWQCF_MIN || + psColDef->col_func == SWQCF_MAX ) + { + oFDefn.SetSubType( poSrcFDefn->GetSubType() ); + } + } + else + oFDefn.SetType( OFTReal ); + if( psColDef->col_func != SWQCF_AVG && + psColDef->col_func != SWQCF_SUM ) + { + oFDefn.SetWidth( poSrcFDefn->GetWidth() ); + oFDefn.SetPrecision( poSrcFDefn->GetPrecision() ); + } + } + else if( poSrcGFDefn != NULL ) + { + oGFDefn.SetType( poSrcGFDefn->GetType() ); + oGFDefn.SetSpatialRef( poSrcGFDefn->GetSpatialRef() ); + } + else if ( psColDef->field_index >= iFIDFieldIndex ) + { + switch ( SpecialFieldTypes[psColDef->field_index-iFIDFieldIndex] ) + { + case SWQ_INTEGER: + oFDefn.SetType( OFTInteger ); + break; + case SWQ_INTEGER64: + oFDefn.SetType( OFTInteger64 ); + break; + case SWQ_FLOAT: + oFDefn.SetType( OFTReal ); + break; + default: + oFDefn.SetType( OFTString ); + break; + } + } + else + { + switch( psColDef->field_type ) + { + case SWQ_INTEGER: + oFDefn.SetType( OFTInteger ); + break; + + case SWQ_INTEGER64: + oFDefn.SetType( OFTInteger64 ); + break; + + case SWQ_BOOLEAN: + oFDefn.SetType( OFTInteger ); + oFDefn.SetSubType( OFSTBoolean ); + break; + + case SWQ_FLOAT: + oFDefn.SetType( OFTReal ); + break; + + default: + oFDefn.SetType( OFTString ); + break; + } + } + + /* setting up the target_type */ + switch (psColDef->target_type) + { + case SWQ_OTHER: + break; + case SWQ_INTEGER: + oFDefn.SetType( OFTInteger ); + break; + case SWQ_INTEGER64: + oFDefn.SetType( OFTInteger64 ); + break; + case SWQ_BOOLEAN: + oFDefn.SetType( OFTInteger ); + oFDefn.SetSubType( OFSTBoolean ); + break; + case SWQ_FLOAT: + oFDefn.SetType( OFTReal ); + break; + case SWQ_STRING: + oFDefn.SetType( OFTString ); + break; + case SWQ_TIMESTAMP: + oFDefn.SetType( OFTDateTime ); + break; + case SWQ_DATE: + oFDefn.SetType( OFTDate ); + break; + case SWQ_TIME: + oFDefn.SetType( OFTTime ); + break; + case SWQ_GEOMETRY: + break; + + default: + CPLAssert( FALSE ); + oFDefn.SetType( OFTString ); + break; + } + if( psColDef->target_subtype != OFSTNone ) + oFDefn.SetSubType( psColDef->target_subtype ); + + if (psColDef->field_length > 0) + { + oFDefn.SetWidth( psColDef->field_length ); + } + + if (psColDef->field_precision >= 0) + { + oFDefn.SetPrecision( psColDef->field_precision ); + } + + if( bIsGeometry ) + { + panGeomFieldToSrcGeomField[poDefn->GetGeomFieldCount()] = iSrcGeomField; + /* Hack while drivers haven't been updated so that */ + /* poSrcDefn->GetGeomFieldDefn(0)->GetSpatialRef() == poSrcLayer->GetSpatialRef() */ + if( iSrcGeomField == 0 && + poSrcDefn->GetGeomFieldCount() == 1 && + oGFDefn.GetSpatialRef() == NULL ) + { + oGFDefn.SetSpatialRef(poSrcLayer->GetSpatialRef()); + } + int bForceGeomType = FALSE; + if( psColDef->eGeomType != wkbUnknown ) + { + oGFDefn.SetType( psColDef->eGeomType ); + bForceGeomType = TRUE; + } + if( psColDef->nSRID > 0 ) + { + OGRSpatialReference* poSRS = new OGRSpatialReference(); + if( poSRS->importFromEPSG( psColDef->nSRID ) == OGRERR_NONE ) + { + oGFDefn.SetSpatialRef( poSRS ); + } + poSRS->Release(); + } + + OGRGenSQLGeomFieldDefn* poMyGeomFieldDefn = + new OGRGenSQLGeomFieldDefn(&oGFDefn); + poMyGeomFieldDefn->bForceGeomType = bForceGeomType; + poDefn->AddGeomFieldDefn( poMyGeomFieldDefn, FALSE ); + } + else + poDefn->AddFieldDefn( &oFDefn ); + } + +/* -------------------------------------------------------------------- */ +/* Add implicit geometry field. */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->query_mode == SWQM_RECORDSET && + poDefn->GetGeomFieldCount() == 0 && + poSrcDefn->GetGeomFieldCount() == 1 ) + { + psSelectInfo->result_columns++; + + psSelectInfo->column_defs = (swq_col_def *) + CPLRealloc( psSelectInfo->column_defs, sizeof(swq_col_def) * psSelectInfo->result_columns ); + + swq_col_def *col_def = psSelectInfo->column_defs + psSelectInfo->result_columns - 1; + + memset( col_def, 0, sizeof(swq_col_def) ); + const char* pszName = poSrcDefn->GetGeomFieldDefn(0)->GetNameRef(); + if( *pszName != '\0' ) + col_def->field_name = CPLStrdup( pszName ); + else + col_def->field_name = CPLStrdup( OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME ); + col_def->field_alias = NULL; + col_def->table_index = 0; + col_def->field_index = GEOM_FIELD_INDEX_TO_ALL_FIELD_INDEX(poSrcDefn, 0); + col_def->field_type = SWQ_GEOMETRY; + col_def->target_type = SWQ_GEOMETRY; + + panGeomFieldToSrcGeomField[poDefn->GetGeomFieldCount()] = 0; + + OGRGenSQLGeomFieldDefn* poMyGeomFieldDefn = + new OGRGenSQLGeomFieldDefn(poSrcDefn->GetGeomFieldDefn(0)); + poDefn->AddGeomFieldDefn( poMyGeomFieldDefn, FALSE ); + + /* Hack while drivers haven't been updated so that */ + /* poSrcDefn->GetGeomFieldDefn(0)->GetSpatialRef() == poSrcLayer->GetSpatialRef() */ + if( poSrcDefn->GetGeomFieldDefn(0)->GetSpatialRef() == NULL ) + { + poDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSrcLayer->GetSpatialRef()); + } + } + +/* -------------------------------------------------------------------- */ +/* Now that we have poSrcLayer, we can install a spatial filter */ +/* if there is one. */ +/* -------------------------------------------------------------------- */ + if( poSpatFilter != NULL ) + SetSpatialFilter( 0, poSpatFilter ); + + ResetReading(); + + FindAndSetIgnoredFields(); + + if( !bForwardWhereToSourceLayer ) + SetAttributeFilter( pszWHERE ); +} + +/************************************************************************/ +/* ~OGRGenSQLResultsLayer() */ +/************************************************************************/ + +OGRGenSQLResultsLayer::~OGRGenSQLResultsLayer() + +{ + if( m_nFeaturesRead > 0 && poDefn != NULL ) + { + CPLDebug( "GenSQL", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poDefn->GetName() ); + } + + ClearFilters(); + +/* -------------------------------------------------------------------- */ +/* Free various datastructures. */ +/* -------------------------------------------------------------------- */ + CPLFree( papoTableLayers ); + papoTableLayers = NULL; + + CPLFree( panFIDIndex ); + CPLFree( panGeomFieldToSrcGeomField ); + + delete poSummaryFeature; + delete (swq_select *) pSelectInfo; + + if( poDefn != NULL ) + { + poDefn->Release(); + } + +/* -------------------------------------------------------------------- */ +/* Release any additional datasources being used in joins. */ +/* -------------------------------------------------------------------- */ + for( int iEDS = 0; iEDS < nExtraDSCount; iEDS++ ) + GDALClose( (GDALDatasetH)papoExtraDS[iEDS] ); + + CPLFree( papoExtraDS ); + CPLFree( pszWHERE ); +} + +/************************************************************************/ +/* ClearFilters() */ +/* */ +/* Clear up all filters currently in place on the target layer, */ +/* and joined layers. We try not to leave them installed */ +/* except when actively fetching features. */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::ClearFilters() + +{ +/* -------------------------------------------------------------------- */ +/* Clear any filters installed on the target layer. */ +/* -------------------------------------------------------------------- */ + if( poSrcLayer != NULL ) + { + poSrcLayer->SetAttributeFilter( "" ); + poSrcLayer->SetSpatialFilter( NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Clear any attribute filter installed on the joined layers. */ +/* -------------------------------------------------------------------- */ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + int iJoin; + + if( psSelectInfo != NULL ) + { + for( iJoin = 0; iJoin < psSelectInfo->join_count; iJoin++ ) + { + swq_join_def *psJoinInfo = psSelectInfo->join_defs + iJoin; + OGRLayer *poJoinLayer = + papoTableLayers[psJoinInfo->secondary_table]; + + poJoinLayer->SetAttributeFilter( "" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Clear any ignored field lists installed on source layers */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo != NULL ) + { + for( int iTable = 0; iTable < psSelectInfo->table_count; iTable++ ) + { + OGRLayer* poLayer = papoTableLayers[iTable]; + poLayer->SetIgnoredFields(NULL); + } + } +} + +/************************************************************************/ +/* MustEvaluateSpatialFilterOnGenSQL() */ +/************************************************************************/ + +int OGRGenSQLResultsLayer::MustEvaluateSpatialFilterOnGenSQL() +{ + int bEvaluateSpatialFilter = FALSE; + if( m_poFilterGeom != NULL && + m_iGeomFieldFilter >= 0 && + m_iGeomFieldFilter < GetLayerDefn()->GetGeomFieldCount() ) + { + int iSrcGeomField = panGeomFieldToSrcGeomField[m_iGeomFieldFilter]; + if( iSrcGeomField < 0 ) + bEvaluateSpatialFilter = TRUE; + } + return bEvaluateSpatialFilter; +} + +/************************************************************************/ +/* ApplyFiltersToSource() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::ApplyFiltersToSource() +{ + poSrcLayer->SetAttributeFilter( pszWHERE ); + if( m_iGeomFieldFilter >= 0 && + m_iGeomFieldFilter < GetLayerDefn()->GetGeomFieldCount() ) + { + int iSrcGeomField = panGeomFieldToSrcGeomField[m_iGeomFieldFilter]; + if( iSrcGeomField >= 0 ) + poSrcLayer->SetSpatialFilter( iSrcGeomField, m_poFilterGeom ); + } + + poSrcLayer->ResetReading(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::ResetReading() + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + + if( psSelectInfo->query_mode == SWQM_RECORDSET ) + { + ApplyFiltersToSource(); + } + + nNextIndexFID = 0; +} + +/************************************************************************/ +/* SetNextByIndex() */ +/* */ +/* If we already have an FID list, we can easily resposition */ +/* ourselves in it. */ +/************************************************************************/ + +OGRErr OGRGenSQLResultsLayer::SetNextByIndex( GIntBig nIndex ) + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + + CreateOrderByIndex(); + + if( psSelectInfo->query_mode == SWQM_SUMMARY_RECORD + || psSelectInfo->query_mode == SWQM_DISTINCT_LIST + || panFIDIndex != NULL ) + { + nNextIndexFID = nIndex; + return OGRERR_NONE; + } + else + { + return poSrcLayer->SetNextByIndex( nIndex ); + } +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRGenSQLResultsLayer::GetExtent( int iGeomField, + OGREnvelope *psExtent, + int bForce ) + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() || + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone ) + { + if( iGeomField != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } + return OGRERR_FAILURE; + } + + if( psSelectInfo->query_mode == SWQM_RECORDSET ) + { + int iSrcGeomField = panGeomFieldToSrcGeomField[iGeomField]; + if( iSrcGeomField >= 0 ) + return poSrcLayer->GetExtent( iSrcGeomField, psExtent, bForce ); + else if( iGeomField == 0 ) + return OGRLayer::GetExtent( psExtent, bForce ); + else + return OGRLayer::GetExtent( iGeomField, psExtent, bForce ); + } + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRGenSQLResultsLayer::GetFeatureCount( int bForce ) + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + + CreateOrderByIndex(); + + if( psSelectInfo->query_mode == SWQM_DISTINCT_LIST ) + { + if( !PrepareSummary() ) + return 0; + + swq_summary *psSummary = psSelectInfo->column_summary + 0; + + if( psSummary == NULL ) + return 0; + + return psSummary->count; + } + else if( psSelectInfo->query_mode != SWQM_RECORDSET ) + return 1; + else if( m_poAttrQuery == NULL && !MustEvaluateSpatialFilterOnGenSQL() ) + return poSrcLayer->GetFeatureCount( bForce ); + else + return OGRLayer::GetFeatureCount( bForce ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGenSQLResultsLayer::TestCapability( const char *pszCap ) + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + + if( EQUAL(pszCap,OLCFastSetNextByIndex) ) + { + if( psSelectInfo->query_mode == SWQM_SUMMARY_RECORD + || psSelectInfo->query_mode == SWQM_DISTINCT_LIST + || panFIDIndex != NULL ) + return TRUE; + else + return poSrcLayer->TestCapability( pszCap ); + } + + if( psSelectInfo->query_mode == SWQM_RECORDSET + && (EQUAL(pszCap,OLCFastFeatureCount) + || EQUAL(pszCap,OLCRandomRead) + || EQUAL(pszCap,OLCFastGetExtent)) ) + return poSrcLayer->TestCapability( pszCap ); + + else if( psSelectInfo->query_mode != SWQM_RECORDSET ) + { + if( EQUAL(pszCap,OLCFastFeatureCount) ) + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* ContainGeomSpecialField() */ +/************************************************************************/ + +int OGRGenSQLResultsLayer::ContainGeomSpecialField(swq_expr_node* expr) +{ + if (expr->eNodeType == SNT_COLUMN) + { + if( expr->table_index == 0 && expr->field_index != -1 ) + { + OGRLayer* poLayer = papoTableLayers[expr->table_index]; + int nSpecialFieldIdx = expr->field_index - + poLayer->GetLayerDefn()->GetFieldCount(); + if( nSpecialFieldIdx == SPF_OGR_GEOMETRY || + nSpecialFieldIdx == SPF_OGR_GEOM_WKT || + nSpecialFieldIdx == SPF_OGR_GEOM_AREA ) + return TRUE; + if( expr->field_index == + GEOM_FIELD_INDEX_TO_ALL_FIELD_INDEX(poLayer->GetLayerDefn(), 0) ) + return TRUE; + return FALSE; + } + } + else if (expr->eNodeType == SNT_OPERATION) + { + for( int i = 0; i < expr->nSubExprCount; i++ ) + { + if (ContainGeomSpecialField(expr->papoSubExpr[i])) + return TRUE; + } + } + return FALSE; +} + +/************************************************************************/ +/* PrepareSummary() */ +/************************************************************************/ + +int OGRGenSQLResultsLayer::PrepareSummary() + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + + if( poSummaryFeature != NULL ) + return TRUE; + + poSummaryFeature = new OGRFeature( poDefn ); + poSummaryFeature->SetFID( 0 ); + +/* -------------------------------------------------------------------- */ +/* Ensure our query parameters are in place on the source */ +/* layer. And initialize reading. */ +/* -------------------------------------------------------------------- */ + ApplyFiltersToSource(); + +/* -------------------------------------------------------------------- */ +/* Ignore geometry reading if no spatial filter in place and that */ +/* the where clause and no column references OGR_GEOMETRY, */ +/* OGR_GEOM_WKT or OGR_GEOM_AREA special fields. */ +/* -------------------------------------------------------------------- */ + int bSaveIsGeomIgnored = poSrcLayer->GetLayerDefn()->IsGeometryIgnored(); + if ( m_poFilterGeom == NULL && ( psSelectInfo->where_expr == NULL || + !ContainGeomSpecialField(psSelectInfo->where_expr) ) ) + { + int bFoundGeomExpr = FALSE; + for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + if (psColDef->table_index == 0 && psColDef->field_index != -1) + { + OGRLayer* poLayer = papoTableLayers[psColDef->table_index]; + int nSpecialFieldIdx = psColDef->field_index - + poLayer->GetLayerDefn()->GetFieldCount(); + if (nSpecialFieldIdx == SPF_OGR_GEOMETRY || + nSpecialFieldIdx == SPF_OGR_GEOM_WKT || + nSpecialFieldIdx == SPF_OGR_GEOM_AREA) + { + bFoundGeomExpr = TRUE; + break; + } + if( psColDef->field_index == + GEOM_FIELD_INDEX_TO_ALL_FIELD_INDEX(poLayer->GetLayerDefn(), 0) ) + { + bFoundGeomExpr = TRUE; + break; + } + } + if (psColDef->expr != NULL && ContainGeomSpecialField(psColDef->expr)) + { + bFoundGeomExpr = TRUE; + break; + } + } + if (!bFoundGeomExpr) + poSrcLayer->GetLayerDefn()->SetGeometryIgnored(TRUE); + } + +/* -------------------------------------------------------------------- */ +/* We treat COUNT(*) as a special case, and fill with */ +/* GetFeatureCount(). */ +/* -------------------------------------------------------------------- */ + + if( psSelectInfo->result_columns == 1 + && psSelectInfo->column_defs[0].col_func == SWQCF_COUNT + && psSelectInfo->column_defs[0].field_index < 0 ) + { + GIntBig nRes = poSrcLayer->GetFeatureCount( TRUE ); + poSummaryFeature->SetField( 0, nRes ); + + if( (GIntBig)(int)nRes == nRes ) + { + poDefn->GetFieldDefn(0)->SetType(OFTInteger); + delete poSummaryFeature; + poSummaryFeature = new OGRFeature( poDefn ); + poSummaryFeature->SetFID( 0 ); + poSummaryFeature->SetField( 0, (int)nRes ); + } + + poSrcLayer->GetLayerDefn()->SetGeometryIgnored(bSaveIsGeomIgnored); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise, process all source feature through the summary */ +/* building facilities of SWQ. */ +/* -------------------------------------------------------------------- */ + const char *pszError; + OGRFeature *poSrcFeature; + int iField; + + while( (poSrcFeature = poSrcLayer->GetNextFeature()) != NULL ) + { + for( iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + + if (psColDef->col_func == SWQCF_COUNT) + { + /* psColDef->field_index can be -1 in the case of a COUNT(*) */ + if (psColDef->field_index < 0) + pszError = swq_select_summarize( psSelectInfo, iField, "" ); + else if (IS_GEOM_FIELD_INDEX(poSrcLayer->GetLayerDefn(), psColDef->field_index) ) + { + int iSrcGeomField = ALL_FIELD_INDEX_TO_GEOM_FIELD_INDEX( + poSrcLayer->GetLayerDefn(), psColDef->field_index); + OGRGeometry* poGeom = poSrcFeature->GetGeomFieldRef(iSrcGeomField); + if( poGeom != NULL ) + pszError = swq_select_summarize( psSelectInfo, iField, "" ); + else + pszError = NULL; + } + else if (poSrcFeature->IsFieldSet(psColDef->field_index)) + pszError = swq_select_summarize( psSelectInfo, iField, poSrcFeature->GetFieldAsString( + psColDef->field_index ) ); + else + pszError = NULL; + } + else + { + const char* pszVal = NULL; + if (poSrcFeature->IsFieldSet(psColDef->field_index)) + pszVal = poSrcFeature->GetFieldAsString( + psColDef->field_index ); + pszError = swq_select_summarize( psSelectInfo, iField, pszVal ); + } + + if( pszError != NULL ) + { + delete poSrcFeature; + delete poSummaryFeature; + poSummaryFeature = NULL; + + poSrcLayer->GetLayerDefn()->SetGeometryIgnored(bSaveIsGeomIgnored); + + CPLError( CE_Failure, CPLE_AppDefined, "%s", pszError ); + return FALSE; + } + } + + delete poSrcFeature; + } + + poSrcLayer->GetLayerDefn()->SetGeometryIgnored(bSaveIsGeomIgnored); + + pszError = swq_select_finish_summarize( psSelectInfo ); + if( pszError != NULL ) + { + delete poSummaryFeature; + poSummaryFeature = NULL; + + CPLError( CE_Failure, CPLE_AppDefined, "%s", pszError ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If we have run out of features on the source layer, clear */ +/* away the filters we have installed till a next run through */ +/* the features. */ +/* -------------------------------------------------------------------- */ + if( poSrcFeature == NULL ) + ClearFilters(); + +/* -------------------------------------------------------------------- */ +/* Now apply the values to the summary feature. If we are in */ +/* DISTINCT_LIST mode we don't do this step. */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->query_mode == SWQM_SUMMARY_RECORD ) + { + for( iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + if (psSelectInfo->column_summary != NULL) + { + swq_summary *psSummary = psSelectInfo->column_summary + iField; + if( psColDef->col_func == SWQCF_COUNT ) + { + if( (GIntBig)(int)psSummary->count == psSummary->count ) + { + delete poSummaryFeature; + poSummaryFeature = NULL; + poDefn->GetFieldDefn(iField)->SetType(OFTInteger); + } + } + } + } + + if( poSummaryFeature == NULL ) + { + poSummaryFeature = new OGRFeature( poDefn ); + poSummaryFeature->SetFID( 0 ); + } + + for( iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + if (psSelectInfo->column_summary != NULL) + { + swq_summary *psSummary = psSelectInfo->column_summary + iField; + + if( psColDef->col_func == SWQCF_AVG ) + { + if( psColDef->field_type == SWQ_DATE || + psColDef->field_type == SWQ_TIME || + psColDef->field_type == SWQ_TIMESTAMP) + { + struct tm brokendowntime; + double dfAvg = psSummary->sum / psSummary->count; + CPLUnixTimeToYMDHMS((GIntBig)dfAvg, &brokendowntime); + poSummaryFeature->SetField( iField, + brokendowntime.tm_year + 1900, + brokendowntime.tm_mon + 1, + brokendowntime.tm_mday, + brokendowntime.tm_hour, + brokendowntime.tm_min, + brokendowntime.tm_sec + fmod(dfAvg, 1), 0); + } + else + poSummaryFeature->SetField( iField, + psSummary->sum / psSummary->count ); + } + else if( psColDef->col_func == SWQCF_MIN ) + { + if( psColDef->field_type == SWQ_DATE || + psColDef->field_type == SWQ_TIME || + psColDef->field_type == SWQ_TIMESTAMP) + poSummaryFeature->SetField( iField, psSummary->szMin ); + else + poSummaryFeature->SetField( iField, psSummary->min ); + } + else if( psColDef->col_func == SWQCF_MAX ) + { + if( psColDef->field_type == SWQ_DATE || + psColDef->field_type == SWQ_TIME || + psColDef->field_type == SWQ_TIMESTAMP) + poSummaryFeature->SetField( iField, psSummary->szMax ); + else + poSummaryFeature->SetField( iField, psSummary->max ); + } + else if( psColDef->col_func == SWQCF_COUNT ) + poSummaryFeature->SetField( iField, psSummary->count ); + else if( psColDef->col_func == SWQCF_SUM ) + poSummaryFeature->SetField( iField, psSummary->sum ); + } + else if ( psColDef->col_func == SWQCF_COUNT ) + poSummaryFeature->SetField( iField, 0 ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* OGRMultiFeatureFetcher() */ +/************************************************************************/ + +static swq_expr_node *OGRMultiFeatureFetcher( swq_expr_node *op, + void *pFeatureList ) + +{ + std::vector *papoFeatures = + (std::vector *) pFeatureList; + OGRFeature *poFeature; + swq_expr_node *poRetNode = NULL; + + CPLAssert( op->eNodeType == SNT_COLUMN ); + +/* -------------------------------------------------------------------- */ +/* What feature are we using? The primary or one of the joined ones?*/ +/* -------------------------------------------------------------------- */ + if( op->table_index < 0 || op->table_index >= (int)papoFeatures->size() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Request for unexpected table_index (%d) in field fetcher.", + op->table_index ); + return NULL; + } + + poFeature = (*papoFeatures)[op->table_index]; + +/* -------------------------------------------------------------------- */ +/* Fetch the value. */ +/* -------------------------------------------------------------------- */ + switch( op->field_type ) + { + case SWQ_INTEGER: + case SWQ_BOOLEAN: + if( poFeature == NULL + || !poFeature->IsFieldSet(op->field_index) ) + { + poRetNode = new swq_expr_node(0); + poRetNode->is_null = TRUE; + } + else + poRetNode = new swq_expr_node( + poFeature->GetFieldAsInteger(op->field_index) ); + break; + + case SWQ_INTEGER64: + if( poFeature == NULL + || !poFeature->IsFieldSet(op->field_index) ) + { + poRetNode = new swq_expr_node((GIntBig)0); + poRetNode->is_null = TRUE; + } + else + poRetNode = new swq_expr_node( + poFeature->GetFieldAsInteger64(op->field_index) ); + break; + + case SWQ_FLOAT: + if( poFeature == NULL + || !poFeature->IsFieldSet(op->field_index) ) + { + poRetNode = new swq_expr_node( 0.0 ); + poRetNode->is_null = TRUE; + } + else + poRetNode = new swq_expr_node( + poFeature->GetFieldAsDouble(op->field_index) ); + break; + + case SWQ_GEOMETRY: + if( poFeature == NULL ) + { + poRetNode = new swq_expr_node( (OGRGeometry*) NULL ); + } + else + { + int iSrcGeomField = ALL_FIELD_INDEX_TO_GEOM_FIELD_INDEX( + poFeature->GetDefnRef(), op->field_index); + poRetNode = new swq_expr_node( + poFeature->GetGeomFieldRef(iSrcGeomField) ); + } + break; + + default: + if( poFeature == NULL + || !poFeature->IsFieldSet(op->field_index) ) + { + poRetNode = new swq_expr_node(""); + poRetNode->is_null = TRUE; + } + else + poRetNode = new swq_expr_node( + poFeature->GetFieldAsString(op->field_index) ); + break; + } + + return poRetNode; +} + +/************************************************************************/ +/* GetFilterForJoin() */ +/************************************************************************/ + +static CPLString GetFilterForJoin(swq_expr_node* poExpr, OGRFeature* poSrcFeat, + OGRLayer* poJoinLayer, int secondary_table) +{ + if( poExpr->eNodeType == SNT_CONSTANT ) + { + char* pszRes = poExpr->Unparse(NULL, '"'); + CPLString osRes = pszRes; + CPLFree(pszRes); + return osRes; + } + + if( poExpr->eNodeType == SNT_COLUMN ) + { + CPLAssert( poExpr->field_index != -1 ); + CPLAssert( poExpr->table_index == 0 || poExpr->table_index == secondary_table ); + + if( poExpr->table_index == 0 ) + { + // if source key is null, we can't do join. + if( !poSrcFeat->IsFieldSet( poExpr->field_index ) ) + { + return ""; + } + OGRFieldType ePrimaryFieldType = + poSrcFeat->GetFieldDefnRef(poExpr->field_index)->GetType(); + OGRField *psSrcField = + poSrcFeat->GetRawFieldRef(poExpr->field_index); + + switch( ePrimaryFieldType ) + { + case OFTInteger: + return CPLString().Printf("%d", psSrcField->Integer ); + break; + + case OFTInteger64: + return CPLString().Printf(CPL_FRMT_GIB, psSrcField->Integer64 ); + break; + + case OFTReal: + return CPLString().Printf("%.16g", psSrcField->Real ); + break; + + case OFTString: + { + char *pszEscaped = CPLEscapeString( psSrcField->String, + strlen(psSrcField->String), + CPLES_SQL ); + CPLString osRes = "'"; + osRes += pszEscaped; + osRes += "'"; + CPLFree( pszEscaped ); + return osRes; + } + break; + + default: + CPLAssert( FALSE ); + return ""; + } + } + + if( poExpr->table_index == secondary_table ) + { + OGRFieldDefn* poSecondaryFieldDefn = + poJoinLayer->GetLayerDefn()->GetFieldDefn(poExpr->field_index); + return CPLSPrintf("\"%s\"", poSecondaryFieldDefn->GetNameRef()); + } + + CPLAssert(FALSE); + return ""; + } + + if( poExpr->eNodeType == SNT_OPERATION ) + { + /* -------------------------------------------------------------------- */ + /* Operation - start by unparsing all the subexpressions. */ + /* -------------------------------------------------------------------- */ + std::vector apszSubExpr; + int i; + + for( i = 0; i < poExpr->nSubExprCount; i++ ) + { + CPLString osSubExpr = GetFilterForJoin(poExpr->papoSubExpr[i], poSrcFeat, + poJoinLayer, secondary_table); + if( osSubExpr.size() == 0 ) + { + for( --i; i >=0; i-- ) + CPLFree( apszSubExpr[i] ); + return ""; + } + apszSubExpr.push_back( CPLStrdup(osSubExpr) ); + } + + CPLString osExpr = poExpr->UnparseOperationFromUnparsedSubExpr(&apszSubExpr[0]); + + /* -------------------------------------------------------------------- */ + /* cleanup subexpressions. */ + /* -------------------------------------------------------------------- */ + for( i = 0; i < poExpr->nSubExprCount; i++ ) + CPLFree( apszSubExpr[i] ); + + return osExpr; + } + + return ""; +} + +/************************************************************************/ +/* TranslateFeature() */ +/************************************************************************/ + +OGRFeature *OGRGenSQLResultsLayer::TranslateFeature( OGRFeature *poSrcFeat ) + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + OGRFeature *poDstFeat; + std::vector apoFeatures; + + if( poSrcFeat == NULL ) + return NULL; + + m_nFeaturesRead++; + + apoFeatures.push_back( poSrcFeat ); + +/* -------------------------------------------------------------------- */ +/* Fetch the corresponding features from any jointed tables. */ +/* -------------------------------------------------------------------- */ + int iJoin; + + for( iJoin = 0; iJoin < psSelectInfo->join_count; iJoin++ ) + { + CPLString osFilter; + + swq_join_def *psJoinInfo = psSelectInfo->join_defs + iJoin; + + /* OGRMultiFeatureFetcher assumes that the features are pushed in */ + /* apoFeatures with increasing secondary_table, so make sure */ + /* we have taken care of this */ + CPLAssert(psJoinInfo->secondary_table == iJoin + 1); + + OGRLayer *poJoinLayer = papoTableLayers[psJoinInfo->secondary_table]; + + osFilter = GetFilterForJoin(psJoinInfo->poExpr, poSrcFeat, poJoinLayer, + psJoinInfo->secondary_table); + //CPLDebug("OGR", "Filter = %s\n", osFilter.c_str()); + + // if source key is null, we can't do join. + if( osFilter.size() == 0 ) + { + apoFeatures.push_back( NULL ); + continue; + } + + OGRFeature *poJoinFeature = NULL; + + poJoinLayer->ResetReading(); + if( poJoinLayer->SetAttributeFilter( osFilter.c_str() ) == OGRERR_NONE ) + poJoinFeature = poJoinLayer->GetNextFeature(); + + apoFeatures.push_back( poJoinFeature ); + } + +/* -------------------------------------------------------------------- */ +/* Create destination feature. */ +/* -------------------------------------------------------------------- */ + poDstFeat = new OGRFeature( poDefn ); + + poDstFeat->SetFID( poSrcFeat->GetFID() ); + + poDstFeat->SetStyleString( poSrcFeat->GetStyleString() ); + +/* -------------------------------------------------------------------- */ +/* Evaluate fields that are complex expressions. */ +/* -------------------------------------------------------------------- */ + int iRegularField = 0; + int iGeomField = 0; + for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + swq_expr_node *poResult; + + if( psColDef->field_index != -1 ) + { + if( psColDef->field_type == SWQ_GEOMETRY || + psColDef->target_type == SWQ_GEOMETRY ) + iGeomField++; + else + iRegularField++; + continue; + } + + poResult = psColDef->expr->Evaluate( OGRMultiFeatureFetcher, + (void *) &apoFeatures ); + + if( poResult == NULL ) + { + delete poDstFeat; + return NULL; + } + + if( poResult->is_null ) + { + if( poResult->field_type == SWQ_GEOMETRY ) + iGeomField++; + else + iRegularField++; + delete poResult; + continue; + } + + switch( poResult->field_type ) + { + case SWQ_BOOLEAN: + case SWQ_INTEGER: + poDstFeat->SetField( iRegularField++, (int)poResult->int_value ); + break; + + case SWQ_INTEGER64: + poDstFeat->SetField( iRegularField++, poResult->int_value ); + break; + + case SWQ_FLOAT: + poDstFeat->SetField( iRegularField++, poResult->float_value ); + break; + + case SWQ_GEOMETRY: + { + OGRGenSQLGeomFieldDefn* poGeomFieldDefn = + (OGRGenSQLGeomFieldDefn*) poDstFeat->GetGeomFieldDefnRef(iGeomField); + if( poGeomFieldDefn->bForceGeomType && + poResult->geometry_value != NULL ) + { + OGRwkbGeometryType eCurType = + wkbFlatten(poResult->geometry_value->getGeometryType()); + OGRwkbGeometryType eReqType = + wkbFlatten(poGeomFieldDefn->GetType()); + if( eCurType == wkbPolygon && eReqType == wkbMultiPolygon ) + { + poResult->geometry_value = (OGRGeometry*) + OGR_G_ForceToMultiPolygon( (OGRGeometryH) poResult->geometry_value ); + } + else if( (eCurType == wkbMultiPolygon || eCurType == wkbGeometryCollection) && + eReqType == wkbPolygon ) + { + poResult->geometry_value = (OGRGeometry*) + OGR_G_ForceToPolygon( (OGRGeometryH) poResult->geometry_value ); + } + else if( eCurType == wkbLineString && eReqType == wkbMultiLineString ) + { + poResult->geometry_value = (OGRGeometry*) + OGR_G_ForceToMultiLineString( (OGRGeometryH) poResult->geometry_value ); + } + else if( (eCurType == wkbMultiLineString || eCurType == wkbGeometryCollection) && + eReqType == wkbLineString ) + { + poResult->geometry_value = (OGRGeometry*) + OGR_G_ForceToLineString( (OGRGeometryH) poResult->geometry_value ); + } + } + poDstFeat->SetGeomField( iGeomField++, poResult->geometry_value ); + break; + } + + default: + poDstFeat->SetField( iRegularField++, poResult->string_value ); + break; + } + + delete poResult; + } + +/* -------------------------------------------------------------------- */ +/* Copy fields from primary record to the destination feature. */ +/* -------------------------------------------------------------------- */ + iRegularField = 0; + iGeomField = 0; + for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + + if( psColDef->table_index != 0 ) + { + if( psColDef->field_type == SWQ_GEOMETRY || + psColDef->target_type == SWQ_GEOMETRY ) + iGeomField++; + else + iRegularField++; + continue; + } + + if( IS_GEOM_FIELD_INDEX(poSrcFeat->GetDefnRef(), psColDef->field_index) ) + { + int iSrcGeomField = ALL_FIELD_INDEX_TO_GEOM_FIELD_INDEX( + poSrcFeat->GetDefnRef(), psColDef->field_index); + poDstFeat->SetGeomField( iGeomField ++, + poSrcFeat->GetGeomFieldRef(iSrcGeomField) ); + } + else if( psColDef->field_index >= iFIDFieldIndex && + psColDef->field_index < iFIDFieldIndex + SPECIAL_FIELD_COUNT ) + { + switch (SpecialFieldTypes[psColDef->field_index - iFIDFieldIndex]) + { + case SWQ_INTEGER: + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsInteger(psColDef->field_index) ); + break; + case SWQ_INTEGER64: + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsInteger64(psColDef->field_index) ); + break; + case SWQ_FLOAT: + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsDouble(psColDef->field_index) ); + break; + default: + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsString(psColDef->field_index) ); + } + iRegularField ++; + } + else + { + switch (psColDef->target_type) + { + case SWQ_INTEGER: + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsInteger(psColDef->field_index) ); + break; + + case SWQ_INTEGER64: + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsInteger64(psColDef->field_index) ); + break; + + case SWQ_FLOAT: + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsDouble(psColDef->field_index) ); + break; + + case SWQ_STRING: + case SWQ_TIMESTAMP: + case SWQ_DATE: + case SWQ_TIME: + poDstFeat->SetField( iRegularField, poSrcFeat->GetFieldAsString(psColDef->field_index) ); + break; + + case SWQ_GEOMETRY: + CPLAssert(0); + break; + + default: + poDstFeat->SetField( iRegularField, + poSrcFeat->GetRawFieldRef( psColDef->field_index ) ); + } + iRegularField ++; + } + } + +/* -------------------------------------------------------------------- */ +/* Copy values from any joined tables. */ +/* -------------------------------------------------------------------- */ + for( iJoin = 0; iJoin < psSelectInfo->join_count; iJoin++ ) + { + CPLString osFilter; + + swq_join_def *psJoinInfo = psSelectInfo->join_defs + iJoin; + OGRFeature *poJoinFeature = apoFeatures[iJoin+1]; + + if( poJoinFeature == NULL ) + continue; + + // Copy over selected field values. + iRegularField = 0; + for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + + if( psColDef->field_type == SWQ_GEOMETRY || + psColDef->target_type == SWQ_GEOMETRY ) + continue; + + if( psColDef->table_index == psJoinInfo->secondary_table ) + poDstFeat->SetField( iRegularField, + poJoinFeature->GetRawFieldRef( + psColDef->field_index ) ); + + iRegularField ++; + } + + delete poJoinFeature; + } + + return poDstFeat; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRGenSQLResultsLayer::GetNextFeature() + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + + CreateOrderByIndex(); + +/* -------------------------------------------------------------------- */ +/* Handle summary sets. */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->query_mode == SWQM_SUMMARY_RECORD + || psSelectInfo->query_mode == SWQM_DISTINCT_LIST ) + return GetFeature( nNextIndexFID++ ); + + int bEvaluateSpatialFilter = MustEvaluateSpatialFilterOnGenSQL(); + +/* -------------------------------------------------------------------- */ +/* Handle ordered sets. */ +/* -------------------------------------------------------------------- */ + while( TRUE ) + { + OGRFeature *poFeature; + + if( panFIDIndex != NULL ) + poFeature = GetFeature( nNextIndexFID++ ); + else + { + OGRFeature *poSrcFeat = poSrcLayer->GetNextFeature(); + + if( poSrcFeat == NULL ) + return NULL; + + poFeature = TranslateFeature( poSrcFeat ); + delete poSrcFeat; + } + + if( poFeature == NULL ) + return NULL; + + if( (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) && + (!bEvaluateSpatialFilter || + FilterGeometry( poFeature->GetGeomFieldRef(m_iGeomFieldFilter) )) ) + return poFeature; + + delete poFeature; + } + + return NULL; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRGenSQLResultsLayer::GetFeature( GIntBig nFID ) + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + + CreateOrderByIndex(); + +/* -------------------------------------------------------------------- */ +/* Handle request for summary record. */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->query_mode == SWQM_SUMMARY_RECORD ) + { + if( !PrepareSummary() || nFID != 0 || poSummaryFeature == NULL ) + return NULL; + else + return poSummaryFeature->Clone(); + } + +/* -------------------------------------------------------------------- */ +/* Handle request for distinct list record. */ +/* -------------------------------------------------------------------- */ + if( psSelectInfo->query_mode == SWQM_DISTINCT_LIST ) + { + if( !PrepareSummary() ) + return NULL; + + swq_summary *psSummary = psSelectInfo->column_summary + 0; + + if( psSummary == NULL ) + return NULL; + + if( nFID < 0 || nFID >= psSummary->count ) + return NULL; + + if( psSummary->distinct_list[nFID] != NULL ) + poSummaryFeature->SetField( 0, psSummary->distinct_list[nFID] ); + else + poSummaryFeature->UnsetField( 0 ); + poSummaryFeature->SetFID( nFID ); + + return poSummaryFeature->Clone(); + } + +/* -------------------------------------------------------------------- */ +/* Are we running in sorted mode? If so, run the fid through */ +/* the index. */ +/* -------------------------------------------------------------------- */ + if( panFIDIndex != NULL ) + { + if( nFID < 0 || nFID >= nIndexSize ) + return NULL; + else + nFID = panFIDIndex[nFID]; + } + +/* -------------------------------------------------------------------- */ +/* Handle request for random record. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poSrcFeature = poSrcLayer->GetFeature( nFID ); + OGRFeature *poResult; + + if( poSrcFeature == NULL ) + return NULL; + + poResult = TranslateFeature( poSrcFeature ); + poResult->SetFID( nFID ); + + delete poSrcFeature; + + return poResult; +} + +/************************************************************************/ +/* GetSpatialFilter() */ +/************************************************************************/ + +OGRGeometry *OGRGenSQLResultsLayer::GetSpatialFilter() + +{ + return NULL; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn *OGRGenSQLResultsLayer::GetLayerDefn() + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + if( psSelectInfo->query_mode == SWQM_SUMMARY_RECORD && + poSummaryFeature == NULL ) + { + // Run PrepareSummary() is we have a COUNT column so as to be + // able to downcast it from OFTInteger64 to OFTInteger + for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + if( psColDef->col_func == SWQCF_COUNT ) + { + PrepareSummary(); + break; + } + } + } + + return poDefn; +} + +/************************************************************************/ +/* CreateOrderByIndex() */ +/* */ +/* This method is responsible for creating an index providing */ +/* ordered access to the features according to the supplied */ +/* ORDER BY clauses. */ +/* */ +/* This is accomplished by making one pass through all the */ +/* eligible source features, and capturing the order by fields */ +/* of all records in memory. A quick sort is then applied to */ +/* this in memory copy of the order-by fields to create the */ +/* required index. */ +/* */ +/* Keeping all the key values in memory will *not* scale up to */ +/* very large input datasets. */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::CreateOrderByIndex() + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + OGRField *pasIndexFields; + GIntBig i; + int nOrderItems = psSelectInfo->order_specs; + GIntBig *panFIDList; + + if( ! (psSelectInfo->order_specs > 0 + && psSelectInfo->query_mode == SWQM_RECORDSET + && nOrderItems != 0 ) ) + return; + + if( bOrderByValid ) + return; + + bOrderByValid = TRUE; + + ResetReading(); + +/* -------------------------------------------------------------------- */ +/* Allocate set of key values, and the output index. */ +/* -------------------------------------------------------------------- */ + size_t nFeaturesAlloc = 100; + + panFIDIndex = NULL; + pasIndexFields = (OGRField *) + CPLCalloc(sizeof(OGRField), nOrderItems * nFeaturesAlloc); + panFIDList = (GIntBig *) CPLMalloc(sizeof(GIntBig) * nFeaturesAlloc); + +/* -------------------------------------------------------------------- */ +/* Read in all the key values. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poSrcFeat; + nIndexSize = 0; + + while( (poSrcFeat = poSrcLayer->GetNextFeature()) != NULL ) + { + int iKey; + + if ((size_t)nIndexSize == nFeaturesAlloc) + { + GIntBig nNewFeaturesAlloc = (nFeaturesAlloc * 4) / 3; + if( (GIntBig)(size_t)(sizeof(OGRField) * nOrderItems * nNewFeaturesAlloc) != + (GIntBig)sizeof(OGRField) * nOrderItems * nNewFeaturesAlloc ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocate pasIndexFields"); + VSIFree(pasIndexFields); + VSIFree(panFIDList); + nIndexSize = 0; + return; + } + OGRField* pasNewIndexFields = (OGRField *) + VSIRealloc(pasIndexFields, + sizeof(OGRField) * nOrderItems * (size_t)nNewFeaturesAlloc); + if (pasNewIndexFields == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocate pasIndexFields"); + VSIFree(pasIndexFields); + VSIFree(panFIDList); + nIndexSize = 0; + return; + } + pasIndexFields = pasNewIndexFields; + + GIntBig* panNewFIDList = (GIntBig *) + VSIRealloc(panFIDList, sizeof(GIntBig) * (size_t)nNewFeaturesAlloc); + if (panNewFIDList == NULL) + { + VSIFree(pasIndexFields); + VSIFree(panFIDList); + nIndexSize = 0; + return; + } + panFIDList = panNewFIDList; + + memset(pasIndexFields + nFeaturesAlloc, 0, + sizeof(OGRField) * nOrderItems * (size_t)(nNewFeaturesAlloc - nFeaturesAlloc)); + + nFeaturesAlloc = (size_t)nNewFeaturesAlloc; + } + + for( iKey = 0; iKey < nOrderItems; iKey++ ) + { + swq_order_def *psKeyDef = psSelectInfo->order_defs + iKey; + OGRFieldDefn *poFDefn; + OGRField *psSrcField, *psDstField; + + psDstField = pasIndexFields + nIndexSize * nOrderItems + iKey; + + if ( psKeyDef->field_index >= iFIDFieldIndex) + { + if ( psKeyDef->field_index < iFIDFieldIndex + SPECIAL_FIELD_COUNT ) + { + switch (SpecialFieldTypes[psKeyDef->field_index - iFIDFieldIndex]) + { + case SWQ_INTEGER: + psDstField->Integer = poSrcFeat->GetFieldAsInteger(psKeyDef->field_index); + break; + + case SWQ_INTEGER64: + psDstField->Integer64 = poSrcFeat->GetFieldAsInteger64(psKeyDef->field_index); + break; + + case SWQ_FLOAT: + psDstField->Real = poSrcFeat->GetFieldAsDouble(psKeyDef->field_index); + break; + + default: + psDstField->String = CPLStrdup( poSrcFeat->GetFieldAsString(psKeyDef->field_index) ); + break; + } + } + continue; + } + + poFDefn = poSrcLayer->GetLayerDefn()->GetFieldDefn( + psKeyDef->field_index ); + + psSrcField = poSrcFeat->GetRawFieldRef( psKeyDef->field_index ); + + if( poFDefn->GetType() == OFTInteger + || poFDefn->GetType() == OFTInteger64 + || poFDefn->GetType() == OFTReal + || poFDefn->GetType() == OFTDate + || poFDefn->GetType() == OFTTime + || poFDefn->GetType() == OFTDateTime) + memcpy( psDstField, psSrcField, sizeof(OGRField) ); + else if( poFDefn->GetType() == OFTString ) + { + if( poSrcFeat->IsFieldSet( psKeyDef->field_index ) ) + psDstField->String = CPLStrdup( psSrcField->String ); + else + memcpy( psDstField, psSrcField, sizeof(OGRField) ); + } + } + + panFIDList[nIndexSize] = poSrcFeat->GetFID(); + delete poSrcFeat; + + nIndexSize++; + } + + //CPLDebug("GenSQL", "CreateOrderByIndex() = %d features", nIndexSize); + +/* -------------------------------------------------------------------- */ +/* Initialize panFIDIndex */ +/* -------------------------------------------------------------------- */ + panFIDIndex = (GIntBig *) VSIMalloc(sizeof(GIntBig) * (size_t)nIndexSize); + if( panFIDIndex == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocate panFIDIndex"); + VSIFree(pasIndexFields); + VSIFree(panFIDList); + nIndexSize = 0; + return; + } + for( i = 0; i < nIndexSize; i++ ) + panFIDIndex[i] = i; + +/* -------------------------------------------------------------------- */ +/* Quick sort the records. */ +/* -------------------------------------------------------------------- */ + if( !SortIndexSection( pasIndexFields, 0, nIndexSize ) ) + { + VSIFree(pasIndexFields); + VSIFree(panFIDList); + nIndexSize = 0; + VSIFree(panFIDIndex); + panFIDIndex = NULL; + return; + } + +/* -------------------------------------------------------------------- */ +/* Rework the FID map to map to real FIDs. */ +/* -------------------------------------------------------------------- */ + int bAlreadySorted = TRUE; + for( i = 0; i < nIndexSize; i++ ) + { + if (panFIDIndex[i] != i) + bAlreadySorted = FALSE; + panFIDIndex[i] = panFIDList[panFIDIndex[i]]; + } + + CPLFree( panFIDList ); + +/* -------------------------------------------------------------------- */ +/* Free the key field values. */ +/* -------------------------------------------------------------------- */ + for( int iKey = 0; iKey < nOrderItems; iKey++ ) + { + swq_order_def *psKeyDef = psSelectInfo->order_defs + iKey; + OGRFieldDefn *poFDefn; + + if ( psKeyDef->field_index >= iFIDFieldIndex && + psKeyDef->field_index < iFIDFieldIndex + SPECIAL_FIELD_COUNT ) + { + /* warning: only special fields of type string should be deallocated */ + if (SpecialFieldTypes[psKeyDef->field_index - iFIDFieldIndex] == SWQ_STRING) + { + for( i = 0; i < nIndexSize; i++ ) + { + OGRField *psField = pasIndexFields + iKey + i * nOrderItems; + CPLFree( psField->String ); + } + } + continue; + } + + poFDefn = poSrcLayer->GetLayerDefn()->GetFieldDefn( + psKeyDef->field_index ); + + if( poFDefn->GetType() == OFTString ) + { + for( i = 0; i < nIndexSize; i++ ) + { + OGRField *psField = pasIndexFields + iKey + i * nOrderItems; + + if( psField->Set.nMarker1 != OGRUnsetMarker + || psField->Set.nMarker2 != OGRUnsetMarker ) + CPLFree( psField->String ); + } + } + } + + CPLFree( pasIndexFields ); + + /* If it is already sorted, then free than panFIDIndex array */ + /* so that GetNextFeature() can call a sequential GetNextFeature() */ + /* on the source array. Very useful for layers where random access */ + /* is slow. */ + /* Use case: the GML result of a WFS GetFeature with a SORTBY */ + if (bAlreadySorted) + { + CPLFree( panFIDIndex ); + panFIDIndex = NULL; + + nIndexSize = 0; + } + + ResetReading(); +} + +/************************************************************************/ +/* SortIndexSection() */ +/* */ +/* Sort the records in a section of the index. */ +/************************************************************************/ + +int OGRGenSQLResultsLayer::SortIndexSection( OGRField *pasIndexFields, + GIntBig nStart, GIntBig nEntries ) + +{ + if( nEntries < 2 ) + return TRUE; + + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + int nOrderItems = psSelectInfo->order_specs; + + GIntBig nFirstGroup = nEntries / 2; + GIntBig nFirstStart = nStart; + GIntBig nSecondGroup = nEntries - nFirstGroup; + GIntBig nSecondStart = nStart + nFirstGroup; + GIntBig iMerge = 0; + GIntBig *panMerged; + + if( !SortIndexSection( pasIndexFields, nFirstStart, nFirstGroup ) || + !SortIndexSection( pasIndexFields, nSecondStart, nSecondGroup ) ) + return FALSE; + + panMerged = (GIntBig *) VSIMalloc( sizeof(GIntBig) * (size_t)nEntries ); + if( panMerged == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocated panMerged"); + return FALSE; + } + + while( iMerge < nEntries ) + { + int nResult; + + if( nFirstGroup == 0 ) + nResult = -1; + else if( nSecondGroup == 0 ) + nResult = 1; + else + nResult = Compare( pasIndexFields + + panFIDIndex[nFirstStart] * nOrderItems, + pasIndexFields + + panFIDIndex[nSecondStart] * nOrderItems ); + + if( nResult < 0 ) + { + panMerged[iMerge++] = panFIDIndex[nSecondStart++]; + nSecondGroup--; + } + else + { + panMerged[iMerge++] = panFIDIndex[nFirstStart++]; + nFirstGroup--; + } + } + + /* Copy the merge list back into the main index */ + + memcpy( panFIDIndex + nStart, panMerged, sizeof(GIntBig) * (size_t)nEntries ); + CPLFree( panMerged ); + + return TRUE; +} + +/************************************************************************/ +/* Compare() */ +/************************************************************************/ + +int OGRGenSQLResultsLayer::Compare( OGRField *pasFirstTuple, + OGRField *pasSecondTuple ) + +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + int nResult = 0, iKey; + + for( iKey = 0; nResult == 0 && iKey < psSelectInfo->order_specs; iKey++ ) + { + swq_order_def *psKeyDef = psSelectInfo->order_defs + iKey; + OGRFieldDefn *poFDefn; + + if( psKeyDef->field_index >= iFIDFieldIndex + SPECIAL_FIELD_COUNT ) + { + CPLAssert( FALSE ); + return 0; + } + else if( psKeyDef->field_index >= iFIDFieldIndex ) + poFDefn = NULL; + else + poFDefn = poSrcLayer->GetLayerDefn()->GetFieldDefn( + psKeyDef->field_index ); + + if( (pasFirstTuple[iKey].Set.nMarker1 == OGRUnsetMarker + && pasFirstTuple[iKey].Set.nMarker2 == OGRUnsetMarker) + || (pasSecondTuple[iKey].Set.nMarker1 == OGRUnsetMarker + && pasSecondTuple[iKey].Set.nMarker2 == OGRUnsetMarker) ) + nResult = 0; + else if ( poFDefn == NULL ) + { + switch (SpecialFieldTypes[psKeyDef->field_index - iFIDFieldIndex]) + { + case SWQ_INTEGER: + if( pasFirstTuple[iKey].Integer < pasSecondTuple[iKey].Integer ) + nResult = -1; + else if( pasFirstTuple[iKey].Integer > pasSecondTuple[iKey].Integer ) + nResult = 1; + break; + case SWQ_INTEGER64: + if( pasFirstTuple[iKey].Integer64 < pasSecondTuple[iKey].Integer64 ) + nResult = -1; + else if( pasFirstTuple[iKey].Integer64 > pasSecondTuple[iKey].Integer64 ) + nResult = 1; + break; + case SWQ_FLOAT: + if( pasFirstTuple[iKey].Real < pasSecondTuple[iKey].Real ) + nResult = -1; + else if( pasFirstTuple[iKey].Real > pasSecondTuple[iKey].Real ) + nResult = 1; + break; + case SWQ_STRING: + nResult = strcmp(pasFirstTuple[iKey].String, + pasSecondTuple[iKey].String); + break; + + default: + CPLAssert( FALSE ); + nResult = 0; + } + } + else if( poFDefn->GetType() == OFTInteger ) + { + if( pasFirstTuple[iKey].Integer < pasSecondTuple[iKey].Integer ) + nResult = -1; + else if( pasFirstTuple[iKey].Integer + > pasSecondTuple[iKey].Integer ) + nResult = 1; + } + else if( poFDefn->GetType() == OFTInteger64 ) + { + if( pasFirstTuple[iKey].Integer64 < pasSecondTuple[iKey].Integer64 ) + nResult = -1; + else if( pasFirstTuple[iKey].Integer64 + > pasSecondTuple[iKey].Integer64 ) + nResult = 1; + } + else if( poFDefn->GetType() == OFTString ) + nResult = strcmp(pasFirstTuple[iKey].String, + pasSecondTuple[iKey].String); + else if( poFDefn->GetType() == OFTReal ) + { + if( pasFirstTuple[iKey].Real < pasSecondTuple[iKey].Real ) + nResult = -1; + else if( pasFirstTuple[iKey].Real > pasSecondTuple[iKey].Real ) + nResult = 1; + } + else if( poFDefn->GetType() == OFTDate || + poFDefn->GetType() == OFTTime || + poFDefn->GetType() == OFTDateTime) + { + nResult = OGRCompareDate(&pasFirstTuple[iKey], + &pasSecondTuple[iKey]); + } + + if( psKeyDef->ascending_flag ) + nResult *= -1; + } + + return nResult; +} + + +/************************************************************************/ +/* AddFieldDefnToSet() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::AddFieldDefnToSet(int iTable, int iColumn, + CPLHashSet* hSet) +{ + if (iTable != -1 && iColumn != -1) + { + OGRLayer* poLayer = papoTableLayers[iTable]; + if (iColumn < poLayer->GetLayerDefn()->GetFieldCount()) + { + OGRFieldDefn* poFDefn = + poLayer->GetLayerDefn()->GetFieldDefn(iColumn); + CPLHashSetInsert(hSet, poFDefn); + } + } +} + +/************************************************************************/ +/* ExploreExprForIgnoredFields() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::ExploreExprForIgnoredFields(swq_expr_node* expr, + CPLHashSet* hSet) +{ + if (expr->eNodeType == SNT_COLUMN) + { + AddFieldDefnToSet(expr->table_index, expr->field_index, hSet); + } + else if (expr->eNodeType == SNT_OPERATION) + { + for( int i = 0; i < expr->nSubExprCount; i++ ) + ExploreExprForIgnoredFields(expr->papoSubExpr[i], hSet); + } +} + +/************************************************************************/ +/* FindAndSetIgnoredFields() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::FindAndSetIgnoredFields() +{ + swq_select *psSelectInfo = (swq_select *) pSelectInfo; + CPLHashSet* hSet = CPLHashSetNew(CPLHashSetHashPointer, + CPLHashSetEqualPointer, + NULL); + +/* -------------------------------------------------------------------- */ +/* 1st phase : explore the whole select infos to determine which */ +/* source fields are used */ +/* -------------------------------------------------------------------- */ + for( int iField = 0; iField < psSelectInfo->result_columns; iField++ ) + { + swq_col_def *psColDef = psSelectInfo->column_defs + iField; + AddFieldDefnToSet(psColDef->table_index, psColDef->field_index, hSet); + if (psColDef->expr) + ExploreExprForIgnoredFields(psColDef->expr, hSet); + } + + if (psSelectInfo->where_expr) + ExploreExprForIgnoredFields(psSelectInfo->where_expr, hSet); + + for( int iJoin = 0; iJoin < psSelectInfo->join_count; iJoin++ ) + { + swq_join_def *psJoinDef = psSelectInfo->join_defs + iJoin; + ExploreExprForIgnoredFields(psJoinDef->poExpr, hSet); + } + + for( int iOrder = 0; iOrder < psSelectInfo->order_specs; iOrder++ ) + { + swq_order_def *psOrderDef = psSelectInfo->order_defs + iOrder; + AddFieldDefnToSet(psOrderDef->table_index, psOrderDef->field_index, hSet); + } + +/* -------------------------------------------------------------------- */ +/* 2nd phase : now, we can exclude the unused fields */ +/* -------------------------------------------------------------------- */ + for( int iTable = 0; iTable < psSelectInfo->table_count; iTable++ ) + { + OGRLayer* poLayer = papoTableLayers[iTable]; + OGRFeatureDefn *poSrcFDefn = poLayer->GetLayerDefn(); + int iSrcField; + char** papszIgnoredFields = NULL; + for(iSrcField=0;iSrcFieldGetFieldCount();iSrcField++) + { + OGRFieldDefn* poFDefn = poSrcFDefn->GetFieldDefn(iSrcField); + if (CPLHashSetLookup(hSet,poFDefn) == NULL) + { + papszIgnoredFields = CSLAddString(papszIgnoredFields, poFDefn->GetNameRef()); + //CPLDebug("OGR", "Adding %s to the list of ignored fields of layer %s", + // poFDefn->GetNameRef(), poLayer->GetName()); + } + } + poLayer->SetIgnoredFields((const char**)papszIgnoredFields); + CSLDestroy(papszIgnoredFields); + } + + CPLHashSetDestroy(hSet); +} + +/************************************************************************/ +/* InvalidateOrderByIndex() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::InvalidateOrderByIndex() +{ + CPLFree( panFIDIndex ); + panFIDIndex = NULL; + + nIndexSize = 0; + bOrderByValid = FALSE; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRGenSQLResultsLayer::SetAttributeFilter( const char* pszAttributeFilter ) +{ + InvalidateOrderByIndex(); + return OGRLayer::SetAttributeFilter(pszAttributeFilter); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRGenSQLResultsLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeom ) +{ + InvalidateOrderByIndex(); + if( iGeomField == 0 ) + OGRLayer::SetSpatialFilter(poGeom); + else + OGRLayer::SetSpatialFilter(iGeomField, poGeom); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogr_gensql.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogr_gensql.h new file mode 100644 index 000000000..fe5fe64ef --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogr_gensql.h @@ -0,0 +1,129 @@ +/****************************************************************************** + * $Id: ogr_gensql.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Classes related to generic implementation of ExecuteSQL(). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GENSQL_H_INCLUDED +#define _OGR_GENSQL_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "swq.h" +#include "cpl_hash_set.h" + +#define GEOM_FIELD_INDEX_TO_ALL_FIELD_INDEX(poFDefn, iGeom) \ + ((poFDefn)->GetFieldCount() + SPECIAL_FIELD_COUNT + (iGeom)) + +#define IS_GEOM_FIELD_INDEX(poFDefn, idx) \ + (((idx) >= (poFDefn)->GetFieldCount() + SPECIAL_FIELD_COUNT) && \ + ((idx) < (poFDefn)->GetFieldCount() + SPECIAL_FIELD_COUNT + (poFDefn)->GetGeomFieldCount())) + +#define ALL_FIELD_INDEX_TO_GEOM_FIELD_INDEX(poFDefn, idx) \ + ((idx) - ((poFDefn)->GetFieldCount() + SPECIAL_FIELD_COUNT)) + +/************************************************************************/ +/* OGRGenSQLResultsLayer */ +/************************************************************************/ + +class CPL_DLL OGRGenSQLResultsLayer : public OGRLayer +{ + private: + GDALDataset *poSrcDS; + OGRLayer *poSrcLayer; + void *pSelectInfo; + + char *pszWHERE; + + OGRLayer **papoTableLayers; + + OGRFeatureDefn *poDefn; + + int PrepareSummary(); + + int *panGeomFieldToSrcGeomField; + + GIntBig nIndexSize; + GIntBig *panFIDIndex; + int bOrderByValid; + + GIntBig nNextIndexFID; + OGRFeature *poSummaryFeature; + + int iFIDFieldIndex; + + int nExtraDSCount; + GDALDataset **papoExtraDS; + + OGRFeature *TranslateFeature( OGRFeature * ); + void CreateOrderByIndex(); + int SortIndexSection( OGRField *pasIndexFields, + GIntBig nStart, GIntBig nEntries ); + int Compare( OGRField *pasFirst, OGRField *pasSecond ); + + void ClearFilters(); + void ApplyFiltersToSource(); + + void FindAndSetIgnoredFields(); + void ExploreExprForIgnoredFields(swq_expr_node* expr, CPLHashSet* hSet); + void AddFieldDefnToSet(int iTable, int iColumn, CPLHashSet* hSet); + + int ContainGeomSpecialField(swq_expr_node* expr); + + void InvalidateOrderByIndex(); + + int MustEvaluateSpatialFilterOnGenSQL(); + + public: + OGRGenSQLResultsLayer( GDALDataset *poSrcDS, + void *pSelectInfo, + OGRGeometry *poSpatFilter, + const char *pszWHERE, + const char *pszDialect ); + virtual ~OGRGenSQLResultsLayer(); + + virtual OGRGeometry *GetSpatialFilter(); + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + virtual OGRFeature *GetFeature( GIntBig nFID ); + + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE) { return GetExtent(0, psExtent, bForce); } + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); + + virtual void SetSpatialFilter( OGRGeometry * poGeom ) { SetSpatialFilter(0, poGeom); } + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + virtual OGRErr SetAttributeFilter( const char * ); +}; + +#endif /* ndef _OGR_GENSQL_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogr_miattrind.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogr_miattrind.cpp new file mode 100644 index 000000000..40bdaf2f1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogr_miattrind.cpp @@ -0,0 +1,837 @@ +/****************************************************************************** + * $Id: ogr_miattrind.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements interface to MapInfo .ID files used as attribute + * indexes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_attrind.h" +#include "mitab/mitab_priv.h" +#include "cpl_minixml.h" + +CPL_CVSID("$Id: ogr_miattrind.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGRMIAttrIndex */ +/* */ +/* MapInfo .ID implementation of access to one fields */ +/* indexing. */ +/************************************************************************/ + +class OGRMILayerAttrIndex; + +class OGRMIAttrIndex : public OGRAttrIndex +{ +public: + int iIndex; + TABINDFile *poINDFile; + OGRMILayerAttrIndex *poLIndex; + OGRFieldDefn *poFldDefn; + + int iField; + + OGRMIAttrIndex( OGRMILayerAttrIndex *, int iIndex, int iField); + ~OGRMIAttrIndex(); + + GByte *BuildKey( OGRField *psKey ); + GIntBig GetFirstMatch( OGRField *psKey ); + GIntBig *GetAllMatches( OGRField *psKey ); + GIntBig *GetAllMatches( OGRField *psKey, GIntBig* panFIDList, int* nFIDCount, int* nLength ); + + OGRErr AddEntry( OGRField *psKey, GIntBig nFID ); + OGRErr RemoveEntry( OGRField *psKey, GIntBig nFID ); + + OGRErr Clear(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* OGRMILayerAttrIndex */ +/* */ +/* MapInfo .ID specific implementation of a layer attribute */ +/* index. */ +/* ==================================================================== */ +/************************************************************************/ + +class OGRMILayerAttrIndex : public OGRLayerAttrIndex +{ +public: + TABINDFile *poINDFile; + + int nIndexCount; + OGRMIAttrIndex **papoIndexList; + + char *pszMetadataFilename; + char *pszMIINDFilename; + + int bINDAsReadOnly; + int bUnlinkINDFile; + + OGRMILayerAttrIndex(); + virtual ~OGRMILayerAttrIndex(); + + /* base class virtual methods */ + OGRErr Initialize( const char *pszIndexPath, OGRLayer * ); + OGRErr CreateIndex( int iField ); + OGRErr DropIndex( int iField ); + OGRErr IndexAllFeatures( int iField = -1 ); + + OGRErr AddToIndex( OGRFeature *poFeature, int iField = -1 ); + OGRErr RemoveFromIndex( OGRFeature *poFeature ); + + OGRAttrIndex *GetFieldIndex( int iField ); + + /* custom to OGRMILayerAttrIndex */ + OGRErr SaveConfigToXML(); + OGRErr LoadConfigFromXML(); + OGRErr LoadConfigFromXML(const char* pszRawXML); + void AddAttrInd( int iField, int iINDIndex ); + + OGRLayer *GetLayer() { return poLayer; } +}; + +/************************************************************************/ +/* OGRMILayerAttrIndex() */ +/************************************************************************/ + +OGRMILayerAttrIndex::OGRMILayerAttrIndex() + +{ + poINDFile = NULL; + nIndexCount = 0; + papoIndexList = NULL; + bUnlinkINDFile = FALSE; + bINDAsReadOnly = TRUE; + pszMIINDFilename = NULL; + pszMetadataFilename = NULL; +} + +/************************************************************************/ +/* ~OGRMILayerAttrIndex() */ +/************************************************************************/ + +OGRMILayerAttrIndex::~OGRMILayerAttrIndex() + +{ + if( poINDFile != NULL ) + { + poINDFile->Close(); + delete poINDFile; + poINDFile = NULL; + } + + if (bUnlinkINDFile) + VSIUnlink( pszMIINDFilename ); + + for( int i = 0; i < nIndexCount; i++ ) + delete papoIndexList[i]; + CPLFree( papoIndexList ); + + CPLFree( pszMIINDFilename ); + CPLFree( pszMetadataFilename ); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +OGRErr OGRMILayerAttrIndex::Initialize( const char *pszIndexPathIn, + OGRLayer *poLayerIn ) + +{ + if( poLayerIn == poLayer ) + return OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* Capture input information and form static pathnames. */ +/* -------------------------------------------------------------------- */ + poLayer = poLayerIn; + + pszIndexPath = CPLStrdup( pszIndexPathIn ); + + /* try to process the XML string directly */ + if (EQUALN(pszIndexPathIn, "", 21)) + return LoadConfigFromXML(pszIndexPathIn); + + pszMetadataFilename = CPLStrdup( + CPLResetExtension( pszIndexPathIn, "idm" ) ); + + pszMIINDFilename = CPLStrdup(CPLResetExtension( pszIndexPathIn, "ind" )); + +/* -------------------------------------------------------------------- */ +/* If a metadata file already exists, load it. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr; + VSIStatBuf sStat; + + if( VSIStat( pszMetadataFilename, &sStat ) == 0 ) + { + eErr = LoadConfigFromXML(); + if( eErr != OGRERR_NONE ) + return eErr; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* LoadConfigFromXML() */ +/************************************************************************/ + +OGRErr OGRMILayerAttrIndex::LoadConfigFromXML(const char* pszRawXML) + +{ +/* -------------------------------------------------------------------- */ +/* Parse the XML. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRoot = CPLParseXMLString( pszRawXML ); + + if( psRoot == NULL ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Open the index file. */ +/* -------------------------------------------------------------------- */ + poINDFile = new TABINDFile(); + + if (pszMIINDFilename == NULL) + pszMIINDFilename = CPLStrdup(CPLGetXMLValue(psRoot,"MIIDFilename","")); + + if( pszMIINDFilename == NULL ) + return OGRERR_FAILURE; + + /* NOTE: Replaced r+ with r according to explanation in Ticket #1620. + * This change has to be observed if it doesn't cause any + * problems in future. (mloskot) + */ + if( poINDFile->Open( pszMIINDFilename, "r" ) != 0 ) + { + CPLDestroyXMLNode( psRoot ); + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open index file %s.", + pszMIINDFilename ); + return OGRERR_FAILURE; + } +/* -------------------------------------------------------------------- */ +/* Process each attrindex. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psAttrIndex; + + for( psAttrIndex = psRoot->psChild; + psAttrIndex != NULL; + psAttrIndex = psAttrIndex->psNext ) + { + int iField, iIndexIndex; + + if( psAttrIndex->eType != CXT_Element + || !EQUAL(psAttrIndex->pszValue,"OGRMIAttrIndex") ) + continue; + + iField = atoi(CPLGetXMLValue(psAttrIndex,"FieldIndex","-1")); + iIndexIndex = atoi(CPLGetXMLValue(psAttrIndex,"IndexIndex","-1")); + + if( iField == -1 || iIndexIndex == -1 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Skipping corrupt OGRMIAttrIndex entry." ); + continue; + } + + AddAttrInd( iField, iIndexIndex ); + } + + CPLDestroyXMLNode( psRoot ); + + CPLDebug( "OGR", "Restored %d field indexes for layer %s from %s on %s.", + nIndexCount, poLayer->GetLayerDefn()->GetName(), + pszMetadataFilename ? pszMetadataFilename : "--unknown--", + pszMIINDFilename ); + + return OGRERR_NONE; +} + +OGRErr OGRMILayerAttrIndex::LoadConfigFromXML() +{ + FILE *fp; + int nXMLSize; + char *pszRawXML; + + CPLAssert( poINDFile == NULL ); + +/* -------------------------------------------------------------------- */ +/* Read the XML file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpen( pszMetadataFilename, "rb" ); + if( fp == NULL ) + return OGRERR_NONE; + + VSIFSeek( fp, 0, SEEK_END ); + nXMLSize = VSIFTell( fp ); + VSIFSeek( fp, 0, SEEK_SET ); + + pszRawXML = (char *) CPLMalloc(nXMLSize+1); + pszRawXML[nXMLSize] = '\0'; + VSIFRead( pszRawXML, nXMLSize, 1, fp ); + + VSIFClose( fp ); + + OGRErr eErr = LoadConfigFromXML(pszRawXML); + CPLFree(pszRawXML); + + return eErr; +} + +/************************************************************************/ +/* SaveConfigToXML() */ +/************************************************************************/ + +OGRErr OGRMILayerAttrIndex::SaveConfigToXML() + +{ + if( nIndexCount == 0 ) + return OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* Create the XML tree corresponding to this layer. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRoot; + + psRoot = CPLCreateXMLNode( NULL, CXT_Element, "OGRMILayerAttrIndex" ); + + CPLCreateXMLElementAndValue( psRoot, "MIIDFilename", + CPLGetFilename( pszMIINDFilename ) ); + + for( int i = 0; i < nIndexCount; i++ ) + { + OGRMIAttrIndex *poAI = papoIndexList[i]; + CPLXMLNode *psIndex; + + psIndex = CPLCreateXMLNode( psRoot, CXT_Element, "OGRMIAttrIndex" ); + + CPLCreateXMLElementAndValue( psIndex, "FieldIndex", + CPLSPrintf( "%d", poAI->iField ) ); + + CPLCreateXMLElementAndValue( psIndex, "FieldName", + poLayer->GetLayerDefn()->GetFieldDefn(poAI->iField)->GetNameRef() ); + + + CPLCreateXMLElementAndValue( psIndex, "IndexIndex", + CPLSPrintf( "%d", poAI->iIndex ) ); + } + +/* -------------------------------------------------------------------- */ +/* Save it. */ +/* -------------------------------------------------------------------- */ + char *pszRawXML = CPLSerializeXMLTree( psRoot ); + FILE *fp; + + CPLDestroyXMLNode( psRoot ); + + fp = VSIFOpen( pszMetadataFilename, "wb" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to pen `%s' for write.", + pszMetadataFilename ); + CPLFree( pszRawXML ); + return OGRERR_FAILURE; + } + + VSIFWrite( pszRawXML, 1, strlen(pszRawXML), fp ); + VSIFClose( fp ); + + CPLFree( pszRawXML ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* IndexAllFeatures() */ +/************************************************************************/ + +OGRErr OGRMILayerAttrIndex::IndexAllFeatures( int iField ) + +{ + OGRFeature *poFeature; + + poLayer->ResetReading(); + + while( (poFeature = poLayer->GetNextFeature()) != NULL ) + { + OGRErr eErr = AddToIndex( poFeature, iField ); + + delete poFeature; + + if( eErr != CE_None ) + return eErr; + } + + poLayer->ResetReading(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateIndex() */ +/* */ +/* Create an index corresponding to the indicated field, but do */ +/* not populate it. Use IndexAllFeatures() for that. */ +/************************************************************************/ + +OGRErr OGRMILayerAttrIndex::CreateIndex( int iField ) + +{ +/* -------------------------------------------------------------------- */ +/* Do we have an open .ID file yet? If not, create it now. */ +/* -------------------------------------------------------------------- */ + if( poINDFile == NULL ) + { + poINDFile = new TABINDFile(); + if( poINDFile->Open( pszMIINDFilename, "w+" ) != 0 ) + { + delete poINDFile; + poINDFile = NULL; + + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create %s.", + pszMIINDFilename ); + return OGRERR_FAILURE; + } + } + else if (bINDAsReadOnly) + { + poINDFile->Close(); + if( poINDFile->Open( pszMIINDFilename, "r+" ) != 0 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s as write-only.", + pszMIINDFilename ); + + if( poINDFile->Open( pszMIINDFilename, "r" ) != 0 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Cannot re-open %s as read-only.", + pszMIINDFilename ); + delete poINDFile; + poINDFile = NULL; + } + + return OGRERR_FAILURE; + } + else + { + bINDAsReadOnly = FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have this field indexed already? */ +/* -------------------------------------------------------------------- */ + int i; + OGRFieldDefn *poFldDefn=poLayer->GetLayerDefn()->GetFieldDefn(iField); + + for( i = 0; i < nIndexCount; i++ ) + { + if( papoIndexList[i]->iField == iField ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "It seems we already have an index for field %d/%s\n" + "of layer %s.", + iField, poFldDefn->GetNameRef(), + poLayer->GetLayerDefn()->GetName() ); + return OGRERR_FAILURE; + } + } + +/* -------------------------------------------------------------------- */ +/* What is the corresponding field type in TAB? Note that we */ +/* don't allow indexing of any of the list types. */ +/* -------------------------------------------------------------------- */ + TABFieldType eTABFT; + int nFieldWidth = 0; + + switch( poFldDefn->GetType() ) + { + case OFTInteger: + eTABFT = TABFInteger; + break; + + case OFTReal: + eTABFT = TABFFloat; + break; + + case OFTString: + eTABFT = TABFChar; + if( poFldDefn->GetWidth() > 0 ) + nFieldWidth = poFldDefn->GetWidth(); + else + nFieldWidth = 64; + break; + + default: + CPLError( CE_Failure, CPLE_AppDefined, + "Indexing not support for the field type of field %s.", + poFldDefn->GetNameRef() ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Create the index. */ +/* -------------------------------------------------------------------- */ + int iINDIndex; + + iINDIndex = poINDFile->CreateIndex( eTABFT, nFieldWidth ); + + // CreateIndex() reports it's own errors. + if( iINDIndex < 0 ) + return OGRERR_FAILURE; + + AddAttrInd( iField, iINDIndex ); + + bUnlinkINDFile = FALSE; + +/* -------------------------------------------------------------------- */ +/* Save the new configuration. */ +/* -------------------------------------------------------------------- */ + return SaveConfigToXML(); +} + +/************************************************************************/ +/* DropIndex() */ +/* */ +/* For now we don't have any capability to remove index data */ +/* from the MapInfo index file, so we just limit ourselves to */ +/* ignoring it from now on. */ +/************************************************************************/ + +OGRErr OGRMILayerAttrIndex::DropIndex( int iField ) + +{ +/* -------------------------------------------------------------------- */ +/* Do we have this field indexed already? */ +/* -------------------------------------------------------------------- */ + int i; + OGRFieldDefn *poFldDefn=poLayer->GetLayerDefn()->GetFieldDefn(iField); + + for( i = 0; i < nIndexCount; i++ ) + { + if( papoIndexList[i]->iField == iField ) + break; + + } + + if( i == nIndexCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DROP INDEX on field (%s) that doesn't have an index.", + poFldDefn->GetNameRef() ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Remove from the list. */ +/* -------------------------------------------------------------------- */ + OGRMIAttrIndex *poAI = papoIndexList[i]; + + memmove( papoIndexList + i, papoIndexList + i + 1, + sizeof(void*) * (nIndexCount - i - 1) ); + + delete poAI; + + nIndexCount--; + +/* -------------------------------------------------------------------- */ +/* Save the new configuration, or if there is nothing left try */ +/* to clean up the index files. */ +/* -------------------------------------------------------------------- */ + + if( nIndexCount > 0 ) + return SaveConfigToXML(); + else + { + bUnlinkINDFile = TRUE; + VSIUnlink( pszMetadataFilename ); + + return OGRERR_NONE; + } +} + +/************************************************************************/ +/* AddAttrInd() */ +/************************************************************************/ + +void OGRMILayerAttrIndex::AddAttrInd( int iField, int iINDIndex ) + +{ + OGRMIAttrIndex *poAttrInd = new OGRMIAttrIndex( this, iINDIndex, iField); + + nIndexCount++; + papoIndexList = (OGRMIAttrIndex **) + CPLRealloc(papoIndexList, sizeof(void*) * nIndexCount); + + papoIndexList[nIndexCount-1] = poAttrInd; +} + +/************************************************************************/ +/* GetFieldAttrIndex() */ +/************************************************************************/ + +OGRAttrIndex *OGRMILayerAttrIndex::GetFieldIndex( int iField ) + +{ + for( int i = 0; i < nIndexCount; i++ ) + { + if( papoIndexList[i]->iField == iField ) + return papoIndexList[i]; + } + + return NULL; +} + +/************************************************************************/ +/* AddToIndex() */ +/************************************************************************/ + +OGRErr OGRMILayerAttrIndex::AddToIndex( OGRFeature *poFeature, + int iTargetField ) + +{ + OGRErr eErr = OGRERR_NONE; + + if( poFeature->GetFID() == OGRNullFID ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to index feature with no FID." ); + return OGRERR_FAILURE; + } + + for( int i = 0; i < nIndexCount && eErr == OGRERR_NONE; i++ ) + { + int iField = papoIndexList[i]->iField; + + if( iTargetField != -1 && iTargetField != iField ) + continue; + + if( !poFeature->IsFieldSet( iField ) ) + continue; + + eErr = + papoIndexList[i]->AddEntry( poFeature->GetRawFieldRef( iField ), + poFeature->GetFID() ); + } + + return eErr; +} + +/************************************************************************/ +/* RemoveFromIndex() */ +/************************************************************************/ + +OGRErr OGRMILayerAttrIndex::RemoveFromIndex( OGRFeature * /*poFeature*/ ) + +{ + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGRCreateDefaultLayerIndex() */ +/************************************************************************/ + +OGRLayerAttrIndex *OGRCreateDefaultLayerIndex() + +{ + return new OGRMILayerAttrIndex(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* OGRMIAttrIndex */ +/* ==================================================================== */ +/************************************************************************/ + +/* class declared at top of file */ + +/************************************************************************/ +/* OGRMIAttrIndex() */ +/************************************************************************/ + +OGRMIAttrIndex::OGRMIAttrIndex( OGRMILayerAttrIndex *poLayerIndex, + int iIndexIn, int iFieldIn ) + +{ + iIndex = iIndexIn; + iField = iFieldIn; + poLIndex = poLayerIndex; + poINDFile = poLayerIndex->poINDFile; + + poFldDefn = poLayerIndex->GetLayer()->GetLayerDefn()->GetFieldDefn(iField); +} + +/************************************************************************/ +/* ~OGRMIAttrIndex() */ +/************************************************************************/ + +OGRMIAttrIndex::~OGRMIAttrIndex() +{ +} + +/************************************************************************/ +/* AddEntry() */ +/************************************************************************/ + +OGRErr OGRMIAttrIndex::AddEntry( OGRField *psKey, GIntBig nFID ) + +{ + GByte *pabyKey = BuildKey( psKey ); + + if( psKey == NULL ) + return OGRERR_FAILURE; + + if( nFID >= INT_MAX ) + return OGRERR_FAILURE; + + if( poINDFile->AddEntry( iIndex, pabyKey, (int)nFID+1 ) != 0 ) + return OGRERR_FAILURE; + else + return OGRERR_NONE; +} + +/************************************************************************/ +/* RemoveEntry() */ +/************************************************************************/ + +OGRErr OGRMIAttrIndex::RemoveEntry( OGRField * /*psKey*/, GIntBig /*nFID*/ ) + +{ + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* BuildKey() */ +/************************************************************************/ + +GByte *OGRMIAttrIndex::BuildKey( OGRField *psKey ) + +{ + switch( poFldDefn->GetType() ) + { + case OFTInteger: + return poINDFile->BuildKey( iIndex, psKey->Integer ); + break; + + case OFTInteger64: + { + if( (GIntBig)(int)psKey->Integer64 != psKey->Integer64 ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "64bit integer value passed to OGRMIAttrIndex::BuildKey()"); + } + return poINDFile->BuildKey( iIndex, (int)psKey->Integer64 ); + break; + } + + case OFTReal: + return poINDFile->BuildKey( iIndex, psKey->Real ); + break; + + case OFTString: + return poINDFile->BuildKey( iIndex, psKey->String ); + break; + + default: + CPLAssert( FALSE ); + + return NULL; + } +} + +/************************************************************************/ +/* GetFirstMatch() */ +/************************************************************************/ + +GIntBig OGRMIAttrIndex::GetFirstMatch( OGRField *psKey ) + +{ + GByte *pabyKey = BuildKey( psKey ); + GIntBig nFID; + + nFID = poINDFile->FindFirst( iIndex, pabyKey ); + if( nFID < 1 ) + return OGRNullFID; + else + return nFID - 1; +} + +/************************************************************************/ +/* GetAllMatches() */ +/************************************************************************/ + +GIntBig *OGRMIAttrIndex::GetAllMatches( OGRField *psKey, GIntBig* panFIDList, int* nFIDCount, int* nLength ) +{ + GByte *pabyKey = BuildKey( psKey ); + GIntBig nFID; + + if (panFIDList == NULL) + { + panFIDList = (GIntBig *) CPLMalloc(sizeof(GIntBig) * 2); + *nFIDCount = 0; + *nLength = 2; + } + + nFID = poINDFile->FindFirst( iIndex, pabyKey ); + while( nFID > 0 ) + { + if( *nFIDCount >= *nLength-1 ) + { + *nLength = (*nLength) * 2 + 10; + panFIDList = (GIntBig *) CPLRealloc(panFIDList, sizeof(GIntBig)* (*nLength)); + } + panFIDList[(*nFIDCount)++] = nFID - 1; + + nFID = poINDFile->FindNext( iIndex, pabyKey ); + } + + panFIDList[*nFIDCount] = OGRNullFID; + + return panFIDList; +} + +GIntBig *OGRMIAttrIndex::GetAllMatches( OGRField *psKey ) +{ + int nFIDCount, nLength; + return GetAllMatches( psKey, NULL, &nFIDCount, &nLength ); +} + +/************************************************************************/ +/* Clear() */ +/************************************************************************/ + +OGRErr OGRMIAttrIndex::Clear() + +{ + return OGRERR_UNSUPPORTED_OPERATION; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrdatasource.cpp new file mode 100644 index 000000000..0204b121d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrdatasource.cpp @@ -0,0 +1,377 @@ +/****************************************************************************** + * $Id: ogrdatasource.cpp 28806 2015-03-28 14:37:47Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The generic portions of the GDALDataset class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrsf_frmts.h" +#include "ogr_api.h" +#include "ograpispy.h" + +CPL_CVSID("$Id: ogrdatasource.cpp 28806 2015-03-28 14:37:47Z rouault $"); + +/************************************************************************/ +/* ~OGRDataSource() */ +/************************************************************************/ + +OGRDataSource::OGRDataSource() + +{ +} + +/************************************************************************/ +/* DestroyDataSource() */ +/************************************************************************/ + +void OGRDataSource::DestroyDataSource( OGRDataSource *poDS ) + +{ + delete poDS; +} + +/************************************************************************/ +/* OGR_DS_Destroy() */ +/************************************************************************/ + +void OGR_DS_Destroy( OGRDataSourceH hDS ) + +{ + if( hDS == NULL ) + return; +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpyPreClose(hDS); +#endif + delete (GDALDataset *) hDS; + //VALIDATE_POINTER0( hDS, "OGR_DS_Destroy" ); +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpyPostClose(hDS); +#endif +} + +/************************************************************************/ +/* OGR_DS_Reference() */ +/************************************************************************/ + +int OGR_DS_Reference( OGRDataSourceH hDataSource ) + +{ + VALIDATE_POINTER1( hDataSource, "OGR_DS_Reference", 0 ); + + return ((GDALDataset *) hDataSource)->Reference(); +} + +/************************************************************************/ +/* OGR_DS_Dereference() */ +/************************************************************************/ + +int OGR_DS_Dereference( OGRDataSourceH hDataSource ) + +{ + VALIDATE_POINTER1( hDataSource, "OGR_DS_Dereference", 0 ); + + return ((GDALDataset *) hDataSource)->Dereference(); +} + +/************************************************************************/ +/* OGR_DS_GetRefCount() */ +/************************************************************************/ + +int OGR_DS_GetRefCount( OGRDataSourceH hDataSource ) + +{ + VALIDATE_POINTER1( hDataSource, "OGR_DS_GetRefCount", 0 ); + + return ((GDALDataset *) hDataSource)->GetRefCount(); +} + +/************************************************************************/ +/* OGR_DS_GetSummaryRefCount() */ +/************************************************************************/ + +int OGR_DS_GetSummaryRefCount( OGRDataSourceH hDataSource ) + +{ + VALIDATE_POINTER1( hDataSource, "OGR_DS_GetSummaryRefCount", 0 ); + + return ((GDALDataset *) hDataSource)->GetSummaryRefCount(); +} + +/************************************************************************/ +/* OGR_DS_CreateLayer() */ +/************************************************************************/ + +OGRLayerH OGR_DS_CreateLayer( OGRDataSourceH hDS, + const char * pszName, + OGRSpatialReferenceH hSpatialRef, + OGRwkbGeometryType eType, + char ** papszOptions ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_CreateLayer", NULL ); + + if (pszName == NULL) + { + CPLError ( CE_Failure, CPLE_ObjectNull, "Name was NULL in OGR_DS_CreateLayer"); + return 0; + } + OGRLayerH hLayer = (OGRLayerH) ((GDALDataset *)hDS)->CreateLayer( + pszName, (OGRSpatialReference *) hSpatialRef, eType, papszOptions ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_DS_CreateLayer(hDS, pszName, hSpatialRef, eType, papszOptions, hLayer); +#endif + + return hLayer; +} + +/************************************************************************/ +/* OGR_DS_CopyLayer() */ +/************************************************************************/ + +OGRLayerH OGR_DS_CopyLayer( OGRDataSourceH hDS, + OGRLayerH hSrcLayer, const char *pszNewName, + char **papszOptions ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_CopyLayer", NULL ); + VALIDATE_POINTER1( hSrcLayer, "OGR_DS_CopyLayer", NULL ); + VALIDATE_POINTER1( pszNewName, "OGR_DS_CopyLayer", NULL ); + + return (OGRLayerH) + ((GDALDataset *) hDS)->CopyLayer( (OGRLayer *) hSrcLayer, + pszNewName, papszOptions ); +} + +/************************************************************************/ +/* OGR_DS_DeleteLayer() */ +/************************************************************************/ + +OGRErr OGR_DS_DeleteLayer( OGRDataSourceH hDS, int iLayer ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_DeleteLayer", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_DS_DeleteLayer(hDS, iLayer); +#endif + + OGRErr eErr = ((GDALDataset *) hDS)->DeleteLayer( iLayer ); + + return eErr; +} + +/************************************************************************/ +/* OGR_DS_GetLayerByName() */ +/************************************************************************/ + +OGRLayerH OGR_DS_GetLayerByName( OGRDataSourceH hDS, const char *pszName ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_GetLayerByName", NULL ); + + OGRLayerH hLayer = (OGRLayerH) ((GDALDataset *) hDS)->GetLayerByName( pszName ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_DS_GetLayerByName(hDS, pszName, hLayer); +#endif + + return hLayer; +} + +/************************************************************************/ +/* OGR_DS_ExecuteSQL() */ +/************************************************************************/ + +OGRLayerH OGR_DS_ExecuteSQL( OGRDataSourceH hDS, + const char *pszStatement, + OGRGeometryH hSpatialFilter, + const char *pszDialect ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_ExecuteSQL", NULL ); + + OGRLayerH hLayer = (OGRLayerH) + ((GDALDataset *)hDS)->ExecuteSQL( pszStatement, + (OGRGeometry *) hSpatialFilter, + pszDialect ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_DS_ExecuteSQL(hDS, pszStatement, hSpatialFilter, pszDialect, hLayer); +#endif + + return hLayer; +} + +/************************************************************************/ +/* OGR_DS_ReleaseResultSet() */ +/************************************************************************/ + +void OGR_DS_ReleaseResultSet( OGRDataSourceH hDS, OGRLayerH hLayer ) + +{ + VALIDATE_POINTER0( hDS, "OGR_DS_ReleaseResultSet" ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_DS_ReleaseResultSet(hDS, hLayer); +#endif + + ((GDALDataset *) hDS)->ReleaseResultSet( (OGRLayer *) hLayer ); +} + +/************************************************************************/ +/* OGR_DS_TestCapability() */ +/************************************************************************/ + +int OGR_DS_TestCapability( OGRDataSourceH hDS, const char *pszCap ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_TestCapability", 0 ); + VALIDATE_POINTER1( pszCap, "OGR_DS_TestCapability", 0 ); + + return ((GDALDataset *) hDS)->TestCapability( pszCap ); +} + +/************************************************************************/ +/* OGR_DS_GetLayerCount() */ +/************************************************************************/ + +int OGR_DS_GetLayerCount( OGRDataSourceH hDS ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_GetLayerCount", 0 ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_DS_GetLayerCount(hDS); +#endif + + return ((GDALDataset *)hDS)->GetLayerCount(); +} + +/************************************************************************/ +/* OGR_DS_GetLayer() */ +/************************************************************************/ + +OGRLayerH OGR_DS_GetLayer( OGRDataSourceH hDS, int iLayer ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_GetLayer", NULL ); + + OGRLayerH hLayer = (OGRLayerH) ((GDALDataset*)hDS)->GetLayer( iLayer ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_DS_GetLayer(hDS, iLayer, hLayer); +#endif + + return hLayer; +} + +/************************************************************************/ +/* OGR_DS_GetName() */ +/************************************************************************/ + +const char *OGR_DS_GetName( OGRDataSourceH hDS ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_GetName", NULL ); + + return ((GDALDataset*)hDS)->GetDescription(); +} + +/************************************************************************/ +/* OGR_DS_SyncToDisk() */ +/************************************************************************/ + +OGRErr OGR_DS_SyncToDisk( OGRDataSourceH hDS ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_SyncToDisk", OGRERR_INVALID_HANDLE ); + + ((GDALDataset *) hDS)->FlushCache(); + if( CPLGetLastErrorType() != 0 ) + return OGRERR_FAILURE; + else + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_DS_GetDriver() */ +/************************************************************************/ + +OGRSFDriverH OGR_DS_GetDriver( OGRDataSourceH hDS ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_GetDriver", NULL ); + + return (OGRSFDriverH) ((OGRDataSource *) hDS)->GetDriver(); +} + +/************************************************************************/ +/* OGR_DS_GetStyleTable() */ +/************************************************************************/ + +OGRStyleTableH OGR_DS_GetStyleTable( OGRDataSourceH hDS ) + +{ + VALIDATE_POINTER1( hDS, "OGR_DS_GetStyleTable", NULL ); + + return (OGRStyleTableH) ((GDALDataset *) hDS)->GetStyleTable( ); +} + +/************************************************************************/ +/* OGR_DS_SetStyleTableDirectly() */ +/************************************************************************/ + +void OGR_DS_SetStyleTableDirectly( OGRDataSourceH hDS, + OGRStyleTableH hStyleTable ) + +{ + VALIDATE_POINTER0( hDS, "OGR_DS_SetStyleTableDirectly" ); + + ((GDALDataset *) hDS)->SetStyleTableDirectly( (OGRStyleTable *) hStyleTable); +} + +/************************************************************************/ +/* OGR_DS_SetStyleTable() */ +/************************************************************************/ + +void OGR_DS_SetStyleTable( OGRDataSourceH hDS, OGRStyleTableH hStyleTable ) + +{ + VALIDATE_POINTER0( hDS, "OGR_DS_SetStyleTable" ); + VALIDATE_POINTER0( hStyleTable, "OGR_DS_SetStyleTable" ); + + ((GDALDataset *) hDS)->SetStyleTable( (OGRStyleTable *) hStyleTable); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogremulatedtransaction.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogremulatedtransaction.cpp new file mode 100644 index 000000000..e60fc7a2e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogremulatedtransaction.cpp @@ -0,0 +1,611 @@ +/****************************************************************************** + * $Id$ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implement OGRDataSourceWithTransaction class + * Author: Even Rouault, even dot rouault at spatialys dot com + * + ****************************************************************************** + * Copyright (c) 2015, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogremulatedtransaction.h" +#include "ogrlayerdecorator.h" +#include +#include + +CPL_CVSID("$Id$"); + +class OGRDataSourceWithTransaction; + +class OGRLayerWithTransaction: public OGRLayerDecorator +{ + protected: + friend class OGRDataSourceWithTransaction; + + OGRDataSourceWithTransaction* m_poDS; + OGRFeatureDefn* m_poFeatureDefn; + + public: + + OGRLayerWithTransaction(OGRDataSourceWithTransaction* poDS, + OGRLayer* poBaseLayer); + ~OGRLayerWithTransaction(); + + virtual const char *GetName() { return GetDescription(); } + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr DeleteField( int iField ); + virtual OGRErr ReorderFields( int* panMap ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + + virtual OGRErr CreateGeomField( OGRGeomFieldDefn *poField, + int bApproxOK = TRUE ); + + virtual OGRFeature *GetNextFeature(); + virtual OGRFeature *GetFeature( GIntBig nFID ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + +}; + + +class OGRDataSourceWithTransaction : public OGRDataSource +{ + protected: + OGRDataSource *m_poBaseDataSource; + IOGRTransactionBehaviour* m_poTransactionBehaviour; + int m_bHasOwnershipDataSource; + int m_bHasOwnershipTransactionBehaviour; + int m_bInTransaction; + + std::map m_oMapLayers; + std::set m_oSetLayers; + std::set m_oSetExecuteSQLLayers; + + OGRLayer* WrapLayer(OGRLayer* poLayer); + void RemapLayers(); + + public: + + OGRDataSourceWithTransaction(OGRDataSource* poBaseDataSource, + IOGRTransactionBehaviour* poTransactionBehaviour, + int bTakeOwnershipDataSource, + int bTakeOwnershipTransactionBehaviour); + + virtual ~OGRDataSourceWithTransaction(); + + int IsInTransaction() const { return m_bInTransaction; } + + virtual const char *GetName(); + + virtual int GetLayerCount() ; + virtual OGRLayer *GetLayer(int); + virtual OGRLayer *GetLayerByName(const char *); + virtual OGRErr DeleteLayer(int); + + virtual int TestCapability( const char * ); + + virtual OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + virtual OGRLayer *CopyLayer( OGRLayer *poSrcLayer, + const char *pszNewName, + char **papszOptions = NULL ); + + virtual OGRStyleTable *GetStyleTable(); + virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable ); + + virtual void SetStyleTable(OGRStyleTable *poStyleTable); + + virtual OGRLayer * ExecuteSQL( const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poResultsSet ); + + virtual void FlushCache(); + + virtual OGRErr StartTransaction(int bForce=FALSE); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); +}; + +/************************************************************************/ +/* ~IOGRTransactionBehaviour */ +/************************************************************************/ + +IOGRTransactionBehaviour::~IOGRTransactionBehaviour() +{ +} + +/************************************************************************/ +/* OGRCreateEmulatedTransactionDataSourceWrapper() */ +/************************************************************************/ + +OGRDataSource* OGRCreateEmulatedTransactionDataSourceWrapper( + OGRDataSource* poBaseDataSource, + IOGRTransactionBehaviour* poTransactionBehaviour, + int bTakeOwnershipDataSource, + int bTakeOwnershipTransactionBehaviour) +{ + return new OGRDataSourceWithTransaction(poBaseDataSource, + poTransactionBehaviour, + bTakeOwnershipDataSource, + bTakeOwnershipTransactionBehaviour); +} + + +/************************************************************************/ +/* OGRDataSourceWithTransaction */ +/************************************************************************/ + +OGRDataSourceWithTransaction::OGRDataSourceWithTransaction( + OGRDataSource* poBaseDataSource, + IOGRTransactionBehaviour* poTransactionBehaviour, + int bTakeOwnershipDataSource, + int bTakeOwnershipTransactionBehaviour) : + m_poBaseDataSource(poBaseDataSource), + m_poTransactionBehaviour(poTransactionBehaviour), + m_bHasOwnershipDataSource(bTakeOwnershipDataSource), + m_bHasOwnershipTransactionBehaviour(bTakeOwnershipTransactionBehaviour), + m_bInTransaction(FALSE) +{ +} + +OGRDataSourceWithTransaction::~OGRDataSourceWithTransaction() +{ + std::set::iterator oIter = m_oSetLayers.begin(); + for(; oIter != m_oSetLayers.end(); ++oIter ) + delete *oIter; + + if( m_bHasOwnershipDataSource ) + delete m_poBaseDataSource; + if( m_bHasOwnershipTransactionBehaviour ) + delete m_poTransactionBehaviour; +} + + +OGRLayer* OGRDataSourceWithTransaction::WrapLayer(OGRLayer* poLayer) +{ + if( poLayer ) + { + OGRLayer* poWrappedLayer = m_oMapLayers[poLayer->GetName()]; + if( poWrappedLayer ) + poLayer = poWrappedLayer; + else + { + OGRLayerWithTransaction* poWrappedLayer = new OGRLayerWithTransaction(this,poLayer); + m_oMapLayers[poLayer->GetName()] = poWrappedLayer; + m_oSetLayers.insert(poWrappedLayer); + poLayer = poWrappedLayer; + } + } + return poLayer; +} + +void OGRDataSourceWithTransaction::RemapLayers() +{ + std::set::iterator oIter = m_oSetLayers.begin(); + for(; oIter != m_oSetLayers.end(); ++oIter ) + { + OGRLayerWithTransaction* poWrappedLayer = *oIter; + if( m_poBaseDataSource == NULL ) + poWrappedLayer->m_poDecoratedLayer = NULL; + else + { + poWrappedLayer->m_poDecoratedLayer = + m_poBaseDataSource->GetLayerByName(poWrappedLayer->GetName()); + } + } + m_oMapLayers.clear(); +} + +const char *OGRDataSourceWithTransaction::GetName() +{ + if( !m_poBaseDataSource ) return ""; + return m_poBaseDataSource->GetName(); +} + +int OGRDataSourceWithTransaction::GetLayerCount() +{ + if( !m_poBaseDataSource ) return 0; + return m_poBaseDataSource->GetLayerCount(); +} + +OGRLayer *OGRDataSourceWithTransaction::GetLayer(int iIndex) +{ + if( !m_poBaseDataSource ) return NULL; + return WrapLayer(m_poBaseDataSource->GetLayer(iIndex)); + +} + +OGRLayer *OGRDataSourceWithTransaction::GetLayerByName(const char *pszName) +{ + if( !m_poBaseDataSource ) return NULL; + return WrapLayer(m_poBaseDataSource->GetLayerByName(pszName)); +} + +OGRErr OGRDataSourceWithTransaction::DeleteLayer(int iIndex) +{ + if( !m_poBaseDataSource ) return OGRERR_FAILURE; + OGRLayer* poLayer = GetLayer(iIndex); + CPLString osName; + if( poLayer ) + osName = poLayer->GetName(); + OGRErr eErr = m_poBaseDataSource->DeleteLayer(iIndex); + if( eErr == OGRERR_NONE && osName.size()) + { + std::map::iterator oIter = m_oMapLayers.find(osName); + if(oIter != m_oMapLayers.end()) + { + delete oIter->second; + m_oSetLayers.erase(oIter->second); + m_oMapLayers.erase(oIter); + } + } + return eErr; +} + +int OGRDataSourceWithTransaction::TestCapability( const char * pszCap ) +{ + if( !m_poBaseDataSource ) return FALSE; + + if( EQUAL(pszCap,ODsCEmulatedTransactions) ) + return TRUE; + + return m_poBaseDataSource->TestCapability(pszCap); +} + +OGRLayer *OGRDataSourceWithTransaction::ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef, + OGRwkbGeometryType eGType, + char ** papszOptions) +{ + if( !m_poBaseDataSource ) return NULL; + return WrapLayer(m_poBaseDataSource->CreateLayer(pszName, poSpatialRef, eGType, papszOptions)); +} + +OGRLayer *OGRDataSourceWithTransaction::CopyLayer( OGRLayer *poSrcLayer, + const char *pszNewName, + char **papszOptions ) +{ + if( !m_poBaseDataSource ) return NULL; + return WrapLayer(m_poBaseDataSource->CopyLayer(poSrcLayer, pszNewName, papszOptions )); +} + +OGRStyleTable *OGRDataSourceWithTransaction::GetStyleTable() +{ + if( !m_poBaseDataSource ) return NULL; + return m_poBaseDataSource->GetStyleTable(); +} + +void OGRDataSourceWithTransaction::SetStyleTableDirectly( OGRStyleTable *poStyleTable ) +{ + if( !m_poBaseDataSource ) return; + m_poBaseDataSource->SetStyleTableDirectly(poStyleTable); +} + +void OGRDataSourceWithTransaction::SetStyleTable(OGRStyleTable *poStyleTable) +{ + if( !m_poBaseDataSource ) return; + m_poBaseDataSource->SetStyleTable(poStyleTable); +} + +OGRLayer * OGRDataSourceWithTransaction::ExecuteSQL( const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) +{ + if( !m_poBaseDataSource ) return NULL; + OGRLayer* poLayer = m_poBaseDataSource->ExecuteSQL(pszStatement, poSpatialFilter, + pszDialect); + if( poLayer != NULL ) + m_oSetExecuteSQLLayers.insert(poLayer); + return poLayer; +} + +void OGRDataSourceWithTransaction::ReleaseResultSet( OGRLayer * poResultsSet ) +{ + if( !m_poBaseDataSource ) return; + m_oSetExecuteSQLLayers.erase(poResultsSet); + m_poBaseDataSource->ReleaseResultSet(poResultsSet); +} + +void OGRDataSourceWithTransaction::FlushCache() +{ + if( !m_poBaseDataSource ) return; + return m_poBaseDataSource->FlushCache(); +} + +OGRErr OGRDataSourceWithTransaction::StartTransaction(int bForce) +{ + if( !m_poBaseDataSource ) return OGRERR_FAILURE; + if( !bForce ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Transactions only supported in forced mode"); + return OGRERR_UNSUPPORTED_OPERATION; + } + if( m_oSetExecuteSQLLayers.size() != 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot start transaction while a layer returned by " + "ExecuteSQL() hasn't been released."); + return OGRERR_FAILURE; + } + if( m_bInTransaction ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Transaction is already in progress"); + return OGRERR_FAILURE; + } + int bHasReopenedDS = FALSE; + OGRErr eErr = + m_poTransactionBehaviour->StartTransaction(m_poBaseDataSource, bHasReopenedDS); + if( bHasReopenedDS ) + RemapLayers(); + if( eErr == OGRERR_NONE ) + m_bInTransaction = TRUE; + return eErr; +} + +OGRErr OGRDataSourceWithTransaction::CommitTransaction() +{ + if( !m_poBaseDataSource ) return OGRERR_FAILURE; + if( !m_bInTransaction ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "No transaction in progress"); + return OGRERR_FAILURE; + } + if( m_oSetExecuteSQLLayers.size() != 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot interrupt transaction while a layer returned by " + "ExecuteSQL() hasn't been released."); + return OGRERR_FAILURE; + } + m_bInTransaction = FALSE; + int bHasReopenedDS = FALSE; + OGRErr eErr = + m_poTransactionBehaviour->CommitTransaction(m_poBaseDataSource, bHasReopenedDS); + if( bHasReopenedDS ) + RemapLayers(); + return eErr; +} + +OGRErr OGRDataSourceWithTransaction::RollbackTransaction() +{ + if( !m_poBaseDataSource ) return OGRERR_FAILURE; + if( !m_bInTransaction ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "No transaction in progress"); + return OGRERR_FAILURE; + } + if( m_oSetExecuteSQLLayers.size() != 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot interrupt transaction while a layer returned by " + "ExecuteSQL() hasn't been released."); + return OGRERR_FAILURE; + } + m_bInTransaction = FALSE; + int bHasReopenedDS = FALSE; + OGRErr eErr = + m_poTransactionBehaviour->RollbackTransaction(m_poBaseDataSource, bHasReopenedDS); + if( bHasReopenedDS ) + RemapLayers(); + return eErr; +} + +char **OGRDataSourceWithTransaction::GetMetadata( const char * pszDomain ) +{ + if( !m_poBaseDataSource ) return NULL; + return m_poBaseDataSource->GetMetadata(pszDomain); +} + +CPLErr OGRDataSourceWithTransaction::SetMetadata( char ** papszMetadata, + const char * pszDomain ) +{ + if( !m_poBaseDataSource ) return CE_Failure; + return m_poBaseDataSource->SetMetadata(papszMetadata, pszDomain); +} + +const char *OGRDataSourceWithTransaction::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if( !m_poBaseDataSource ) return NULL; + return m_poBaseDataSource->GetMetadataItem(pszName, pszDomain); +} + +CPLErr OGRDataSourceWithTransaction::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) +{ + if( !m_poBaseDataSource ) return CE_Failure; + return m_poBaseDataSource->SetMetadataItem(pszName, pszValue, pszDomain); +} + + +/************************************************************************/ +/* OGRLayerWithTransaction */ +/************************************************************************/ + +OGRLayerWithTransaction::OGRLayerWithTransaction( + OGRDataSourceWithTransaction* poDS, OGRLayer* poBaseLayer): + OGRLayerDecorator(poBaseLayer, FALSE), + m_poDS(poDS), + m_poFeatureDefn(NULL) +{ +} + +OGRLayerWithTransaction::~OGRLayerWithTransaction() +{ + if( m_poFeatureDefn ) + m_poFeatureDefn->Release(); +} + +OGRFeatureDefn *OGRLayerWithTransaction::GetLayerDefn() +{ + if( !m_poDecoratedLayer ) + { + if( m_poFeatureDefn == NULL ) + { + m_poFeatureDefn = new OGRFeatureDefn(GetDescription()); + m_poFeatureDefn->Reference(); + } + return m_poFeatureDefn; + } + else if( m_poFeatureDefn == NULL ) + { + OGRFeatureDefn* poSrcFeatureDefn = m_poDecoratedLayer->GetLayerDefn(); + m_poFeatureDefn = poSrcFeatureDefn->Clone(); + m_poFeatureDefn->Reference(); + } + return m_poFeatureDefn; +} + +OGRErr OGRLayerWithTransaction::CreateField( OGRFieldDefn *poField, + int bApproxOK ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + int nFields = m_poDecoratedLayer->GetLayerDefn()->GetFieldCount(); + OGRErr eErr = m_poDecoratedLayer->CreateField(poField, bApproxOK); + if( m_poFeatureDefn && eErr == OGRERR_NONE && m_poDecoratedLayer->GetLayerDefn()->GetFieldCount() == nFields + 1 ) + { + m_poFeatureDefn->AddFieldDefn( m_poDecoratedLayer->GetLayerDefn()->GetFieldDefn(nFields) ); + } + return eErr; +} + +OGRErr OGRLayerWithTransaction::CreateGeomField( OGRGeomFieldDefn *poField, + int bApproxOK ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + int nFields = m_poDecoratedLayer->GetLayerDefn()->GetGeomFieldCount(); + OGRErr eErr = m_poDecoratedLayer->CreateGeomField(poField, bApproxOK); + if( m_poFeatureDefn && eErr == OGRERR_NONE && m_poDecoratedLayer->GetLayerDefn()->GetGeomFieldCount() == nFields + 1 ) + { + m_poFeatureDefn->AddGeomFieldDefn( m_poDecoratedLayer->GetLayerDefn()->GetGeomFieldDefn(nFields) ); + } + return eErr; +} + +OGRErr OGRLayerWithTransaction::DeleteField( int iField ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + OGRErr eErr = m_poDecoratedLayer->DeleteField(iField); + if( m_poFeatureDefn && eErr == OGRERR_NONE ) + m_poFeatureDefn->DeleteFieldDefn(iField); + return eErr; +} + +OGRErr OGRLayerWithTransaction::ReorderFields( int* panMap ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + OGRErr eErr = m_poDecoratedLayer->ReorderFields(panMap); + if( m_poFeatureDefn && eErr == OGRERR_NONE ) + m_poFeatureDefn->ReorderFieldDefns(panMap); + return eErr; +} + +OGRErr OGRLayerWithTransaction::AlterFieldDefn( int iField, + OGRFieldDefn* poNewFieldDefn, + int nFlags ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + OGRErr eErr = m_poDecoratedLayer->AlterFieldDefn(iField, poNewFieldDefn, nFlags); + if( m_poFeatureDefn && eErr == OGRERR_NONE ) + { + OGRFieldDefn* poSrcFieldDefn = m_poDecoratedLayer->GetLayerDefn()->GetFieldDefn(iField); + OGRFieldDefn* poDstFieldDefn = m_poFeatureDefn->GetFieldDefn(iField); + poDstFieldDefn->SetName(poSrcFieldDefn->GetNameRef()); + poDstFieldDefn->SetType(poSrcFieldDefn->GetType()); + poDstFieldDefn->SetSubType(poSrcFieldDefn->GetSubType()); + poDstFieldDefn->SetWidth(poSrcFieldDefn->GetWidth()); + poDstFieldDefn->SetPrecision(poSrcFieldDefn->GetPrecision()); + poDstFieldDefn->SetDefault(poSrcFieldDefn->GetDefault()); + poDstFieldDefn->SetNullable(poSrcFieldDefn->IsNullable()); + } + return eErr; +} + +OGRFeature * OGRLayerWithTransaction::GetNextFeature() +{ + if( !m_poDecoratedLayer ) return NULL; + OGRFeature* poSrcFeature = m_poDecoratedLayer->GetNextFeature(); + if( !poSrcFeature ) + return NULL; + OGRFeature* poFeature = new OGRFeature(GetLayerDefn()); + poFeature->SetFrom(poSrcFeature); + poFeature->SetFID(poSrcFeature->GetFID()); + delete poSrcFeature; + return poFeature; +} + +OGRFeature * OGRLayerWithTransaction::GetFeature( GIntBig nFID ) +{ + if( !m_poDecoratedLayer ) return NULL; + OGRFeature* poSrcFeature = m_poDecoratedLayer->GetFeature(nFID); + if( !poSrcFeature ) + return NULL; + OGRFeature* poFeature = new OGRFeature(GetLayerDefn()); + poFeature->SetFrom(poSrcFeature); + poFeature->SetFID(poSrcFeature->GetFID()); + delete poSrcFeature; + return poFeature; +} + +OGRErr OGRLayerWithTransaction::ISetFeature( OGRFeature *poFeature ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + OGRFeature* poSrcFeature = new OGRFeature(m_poDecoratedLayer->GetLayerDefn()); + poSrcFeature->SetFrom(poFeature); + poSrcFeature->SetFID(poFeature->GetFID()); + OGRErr eErr = m_poDecoratedLayer->SetFeature(poSrcFeature); + delete poSrcFeature; + return eErr; +} + +OGRErr OGRLayerWithTransaction::ICreateFeature( OGRFeature *poFeature ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + OGRFeature* poSrcFeature = new OGRFeature(m_poDecoratedLayer->GetLayerDefn()); + poSrcFeature->SetFrom(poFeature); + poSrcFeature->SetFID(poFeature->GetFID()); + OGRErr eErr = m_poDecoratedLayer->CreateFeature(poSrcFeature); + poFeature->SetFID(poSrcFeature->GetFID()); + delete poSrcFeature; + return eErr; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogremulatedtransaction.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogremulatedtransaction.h new file mode 100644 index 000000000..d0477c170 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogremulatedtransaction.h @@ -0,0 +1,128 @@ +/****************************************************************************** + * $Id$ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRDataSourceWithTransaction class + * Author: Even Rouault, even dot rouault at spatialys dot com + * + ****************************************************************************** + * Copyright (c) 2015, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGREMULATEDTRANSACTION_H_INCLUDED +#define _OGREMULATEDTRANSACTION_H_INCLUDED + +#include "ogrsf_frmts.h" + +/** IOGRTransactionBehaviour is an interface that a driver must implement + * to provide emulation of transactions. + * + * @since GDAL 2.0 + */ +class CPL_DLL IOGRTransactionBehaviour +{ + public: + virtual ~IOGRTransactionBehaviour(); + + /** Start a transaction. + * + * The implementation may update the poDSInOut reference by closing + * and reopening the datasource (or assigning it to NULL in case of error). + * In which case bOutHasReopenedDS must be set to TRUE. + * + * The implementation can for example backup the existing files/directories + * that compose the current datasource. + * + * @param poDSInOut datasource handle that may be modified + * @param bOutHasReopenedDS output boolean to indicate if datasource has been closed + * @return OGRERR_NONE in case of success + */ + virtual OGRErr StartTransaction(OGRDataSource*& poDSInOut, + int& bOutHasReopenedDS) = 0; + + /** Commit a transaction. + * + * The implementation may update the poDSInOut reference by closing + * and reopening the datasource (or assigning it to NULL in case of error). + * In which case bOutHasReopenedDS must be set to TRUE. + * + * The implementation can for example remove the backup it may have done + * at StartTransaction() time. + * + * @param poDSInOut datasource handle that may be modified + * @param bOutHasReopenedDS output boolean to indicate if datasource has been closed + * @return OGRERR_NONE in case of success + */ + virtual OGRErr CommitTransaction(OGRDataSource*& poDSInOut, + int& bOutHasReopenedDS) = 0; + + /** Rollback a transaction. + * + * The implementation may update the poDSInOut reference by closing + * and reopening the datasource (or assigning it to NULL in case of error). + * In which case bOutHasReopenedDS must be set to TRUE. + * + * The implementation can for example restore the backup it may have done + * at StartTransaction() time. + * + * @param poDSInOut datasource handle that may be modified + * @param bOutHasReopenedDS output boolean to indicate if datasource has been closed + * @return OGRERR_NONE in case of success + */ + virtual OGRErr RollbackTransaction(OGRDataSource*& poDSInOut, + int& bOutHasReopenedDS) = 0; +}; + + +/** Returns a new datasource object that adds transactional behaviour to an existing datasource. + * + * The provided poTransactionBehaviour object should implement driver-specific + * behaviour for transactions. + * + * The generic mechanisms offered by the wrapper class do not cover concurrent + * updates (though different datasource connections) to the same datasource files. + * + * There are restrictions on what can be accomplished. For example it is not + * allowed to have a unreleased layer returned by ExecuteSQL() before calling + * StartTransaction(), CommitTransaction() or RollbackTransaction(). + * + * Layer structural changes are not allowed after StartTransaction() if the + * layer definition object has been returned previously with GetLayerDefn(). + * + * @param poBaseDataSource the datasource to which to add transactional behaviour. + * @param poTransactionBehaviour an implementation of the IOGRTransactionBehaviour interface. + * @param bTakeOwnershipDataSource whether the returned object should own the + * passed poBaseDataSource (and thus destroy it + * when it is destroyed itself). + * @param bTakeOwnershipTransactionBehaviour whether the returned object should own + * the passed poTransactionBehaviour + * (and thus destroy it when + * it is destroyed itself). + * @return a new datasource handle + * @since GDAL 2.0 + */ +OGRDataSource CPL_DLL* OGRCreateEmulatedTransactionDataSourceWrapper( + OGRDataSource* poBaseDataSource, + IOGRTransactionBehaviour* poTransactionBehaviour, + int bTakeOwnershipDataSource, + int bTakeOwnershipTransactionBehaviour); + +#endif // _OGREMULATEDTRANSACTION_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayer.cpp new file mode 100644 index 000000000..dee21a0b5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayer.cpp @@ -0,0 +1,3904 @@ +/****************************************************************************** + * $Id: ogrlayer.cpp 28928 2015-04-17 10:24:19Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The generic portions of the OGRSFLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrsf_frmts.h" +#include "ogr_api.h" +#include "ogr_p.h" +#include "ogr_attrind.h" +#include "swq.h" +#include "ograpispy.h" + +CPL_CVSID("$Id: ogrlayer.cpp 28928 2015-04-17 10:24:19Z rouault $"); + +/************************************************************************/ +/* OGRLayer() */ +/************************************************************************/ + +OGRLayer::OGRLayer() + +{ + m_poStyleTable = NULL; + m_poAttrQuery = NULL; + m_pszAttrQueryString = NULL; + m_poAttrIndex = NULL; + m_nRefCount = 0; + + m_nFeaturesRead = 0; + + m_poFilterGeom = NULL; + m_bFilterIsEnvelope = FALSE; + m_pPreparedFilterGeom = NULL; + m_iGeomFieldFilter = 0; +} + +/************************************************************************/ +/* ~OGRLayer() */ +/************************************************************************/ + +OGRLayer::~OGRLayer() + +{ + if ( m_poStyleTable ) + { + delete m_poStyleTable; + m_poStyleTable = NULL; + } + + if( m_poAttrIndex != NULL ) + { + delete m_poAttrIndex; + m_poAttrIndex = NULL; + } + + if( m_poAttrQuery != NULL ) + { + delete m_poAttrQuery; + m_poAttrQuery = NULL; + } + + CPLFree( m_pszAttrQueryString ); + + if( m_poFilterGeom ) + { + delete m_poFilterGeom; + m_poFilterGeom = NULL; + } + + if( m_pPreparedFilterGeom != NULL ) + { + OGRDestroyPreparedGeometry(m_pPreparedFilterGeom); + m_pPreparedFilterGeom = NULL; + } +} + +/************************************************************************/ +/* Reference() */ +/************************************************************************/ + +int OGRLayer::Reference() + +{ + return ++m_nRefCount; +} + +/************************************************************************/ +/* OGR_L_Reference() */ +/************************************************************************/ + +int OGR_L_Reference( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_Reference", 0 ); + + return ((OGRLayer *) hLayer)->Reference(); +} + +/************************************************************************/ +/* Dereference() */ +/************************************************************************/ + +int OGRLayer::Dereference() + +{ + return --m_nRefCount; +} + +/************************************************************************/ +/* OGR_L_Dereference() */ +/************************************************************************/ + +int OGR_L_Dereference( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_Dereference", 0 ); + + return ((OGRLayer *) hLayer)->Dereference(); +} + +/************************************************************************/ +/* GetRefCount() */ +/************************************************************************/ + +int OGRLayer::GetRefCount() const + +{ + return m_nRefCount; +} + +/************************************************************************/ +/* OGR_L_GetRefCount() */ +/************************************************************************/ + +int OGR_L_GetRefCount( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetRefCount", 0 ); + + return ((OGRLayer *) hLayer)->GetRefCount(); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRLayer::GetFeatureCount( int bForce ) + +{ + OGRFeature *poFeature; + GIntBig nFeatureCount = 0; + + if( !bForce ) + return -1; + + ResetReading(); + while( (poFeature = GetNextFeature()) != NULL ) + { + nFeatureCount++; + delete poFeature; + } + ResetReading(); + + return nFeatureCount; +} + +/************************************************************************/ +/* OGR_L_GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGR_L_GetFeatureCount( OGRLayerH hLayer, int bForce ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetFeatureCount", 0 ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_GetFeatureCount(hLayer, bForce); +#endif + + return ((OGRLayer *) hLayer)->GetFeatureCount(bForce); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRLayer::GetExtent(OGREnvelope *psExtent, int bForce ) + +{ + return GetExtentInternal(0, psExtent, bForce); +} + +OGRErr OGRLayer::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce ) + +{ + if( iGeomField == 0 ) + return GetExtent(psExtent, bForce); + else + return GetExtentInternal(iGeomField, psExtent, bForce); +} + +OGRErr OGRLayer::GetExtentInternal(int iGeomField, OGREnvelope *psExtent, int bForce ) + +{ + OGRFeature *poFeature; + OGREnvelope oEnv; + GBool bExtentSet = FALSE; + + psExtent->MinX = 0.0; + psExtent->MaxX = 0.0; + psExtent->MinY = 0.0; + psExtent->MaxY = 0.0; + +/* -------------------------------------------------------------------- */ +/* If this layer has a none geometry type, then we can */ +/* reasonably assume there are not extents available. */ +/* -------------------------------------------------------------------- */ + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() || + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone ) + { + if( iGeomField != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* If not forced, we should avoid having to scan all the */ +/* features and just return a failure. */ +/* -------------------------------------------------------------------- */ + if( !bForce ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* OK, we hate to do this, but go ahead and read through all */ +/* the features to collect geometries and build extents. */ +/* -------------------------------------------------------------------- */ + ResetReading(); + while( (poFeature = GetNextFeature()) != NULL ) + { + OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iGeomField); + if (poGeom == NULL || poGeom->IsEmpty()) + { + /* Do nothing */ + } + else if (!bExtentSet) + { + poGeom->getEnvelope(psExtent); + if( !(CPLIsNan(psExtent->MinX) || CPLIsNan(psExtent->MinY) || + CPLIsNan(psExtent->MaxX) || CPLIsNan(psExtent->MaxY)) ) + { + bExtentSet = TRUE; + } + } + else + { + poGeom->getEnvelope(&oEnv); + if (oEnv.MinX < psExtent->MinX) + psExtent->MinX = oEnv.MinX; + if (oEnv.MinY < psExtent->MinY) + psExtent->MinY = oEnv.MinY; + if (oEnv.MaxX > psExtent->MaxX) + psExtent->MaxX = oEnv.MaxX; + if (oEnv.MaxY > psExtent->MaxY) + psExtent->MaxY = oEnv.MaxY; + } + delete poFeature; + } + ResetReading(); + + return (bExtentSet ? OGRERR_NONE : OGRERR_FAILURE); +} + +/************************************************************************/ +/* OGR_L_GetExtent() */ +/************************************************************************/ + +OGRErr OGR_L_GetExtent( OGRLayerH hLayer, OGREnvelope *psExtent, int bForce ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetExtent", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_GetExtent(hLayer, bForce); +#endif + + return ((OGRLayer *) hLayer)->GetExtent( psExtent, bForce ); +} + +/************************************************************************/ +/* OGR_L_GetExtentEx() */ +/************************************************************************/ + +OGRErr OGR_L_GetExtentEx( OGRLayerH hLayer, int iGeomField, + OGREnvelope *psExtent, int bForce ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetExtentEx", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_GetExtentEx(hLayer, iGeomField, bForce); +#endif + + return ((OGRLayer *) hLayer)->GetExtent( iGeomField, psExtent, bForce ); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRLayer::SetAttributeFilter( const char *pszQuery ) + +{ + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + +/* -------------------------------------------------------------------- */ +/* Are we just clearing any existing query? */ +/* -------------------------------------------------------------------- */ + if( pszQuery == NULL || strlen(pszQuery) == 0 ) + { + if( m_poAttrQuery ) + { + delete m_poAttrQuery; + m_poAttrQuery = NULL; + ResetReading(); + } + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Or are we installing a new query? */ +/* -------------------------------------------------------------------- */ + OGRErr eErr; + + if( !m_poAttrQuery ) + m_poAttrQuery = new OGRFeatureQuery(); + + eErr = m_poAttrQuery->Compile( GetLayerDefn(), pszQuery ); + if( eErr != OGRERR_NONE ) + { + delete m_poAttrQuery; + m_poAttrQuery = NULL; + } + + ResetReading(); + + return eErr; +} + +/************************************************************************/ +/* ContainGeomSpecialField() */ +/************************************************************************/ + +static int ContainGeomSpecialField(swq_expr_node* expr, + int nLayerFieldCount) +{ + if (expr->eNodeType == SNT_COLUMN) + { + if( expr->table_index == 0 && expr->field_index != -1 ) + { + int nSpecialFieldIdx = expr->field_index - + nLayerFieldCount; + return nSpecialFieldIdx == SPF_OGR_GEOMETRY || + nSpecialFieldIdx == SPF_OGR_GEOM_WKT || + nSpecialFieldIdx == SPF_OGR_GEOM_AREA; + } + } + else if (expr->eNodeType == SNT_OPERATION) + { + for( int i = 0; i < expr->nSubExprCount; i++ ) + { + if (ContainGeomSpecialField(expr->papoSubExpr[i], + nLayerFieldCount)) + return TRUE; + } + } + return FALSE; +} + +/************************************************************************/ +/* AttributeFilterEvaluationNeedsGeometry() */ +/************************************************************************/ + +int OGRLayer::AttributeFilterEvaluationNeedsGeometry() +{ + if( !m_poAttrQuery ) + return FALSE; + + swq_expr_node* expr = (swq_expr_node *) m_poAttrQuery->GetSWQExpr(); + int nLayerFieldCount = GetLayerDefn()->GetFieldCount(); + + return ContainGeomSpecialField(expr, nLayerFieldCount); +} + +/************************************************************************/ +/* OGR_L_SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGR_L_SetAttributeFilter( OGRLayerH hLayer, const char *pszQuery ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_SetAttributeFilter", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_SetAttributeFilter(hLayer, pszQuery); +#endif + + return ((OGRLayer *) hLayer)->SetAttributeFilter( pszQuery ); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRLayer::GetFeature( GIntBig nFID ) + +{ + OGRFeature *poFeature; + + /* Save old attribute and spatial filters */ + char* pszOldFilter = m_pszAttrQueryString ? CPLStrdup(m_pszAttrQueryString) : NULL; + OGRGeometry* poOldFilterGeom = ( m_poFilterGeom != NULL ) ? m_poFilterGeom->clone() : NULL; + int iOldGeomFieldFilter = m_iGeomFieldFilter; + /* Unset filters */ + SetAttributeFilter(NULL); + SetSpatialFilter(0, NULL); + + ResetReading(); + while( (poFeature = GetNextFeature()) != NULL ) + { + if( poFeature->GetFID() == nFID ) + break; + else + delete poFeature; + } + + /* Restore filters */ + SetAttributeFilter(pszOldFilter); + CPLFree(pszOldFilter); + SetSpatialFilter(iOldGeomFieldFilter, poOldFilterGeom); + delete poOldFilterGeom; + + return poFeature; +} + +/************************************************************************/ +/* OGR_L_GetFeature() */ +/************************************************************************/ + +OGRFeatureH OGR_L_GetFeature( OGRLayerH hLayer, GIntBig nFeatureId ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetFeature", NULL ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_GetFeature(hLayer, nFeatureId); +#endif + + return (OGRFeatureH) ((OGRLayer *)hLayer)->GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* SetNextByIndex() */ +/************************************************************************/ + +OGRErr OGRLayer::SetNextByIndex( GIntBig nIndex ) + +{ + OGRFeature *poFeature; + + if( nIndex < 0 ) + return OGRERR_FAILURE; + + ResetReading(); + while( nIndex-- > 0 ) + { + poFeature = GetNextFeature(); + if( poFeature == NULL ) + return OGRERR_FAILURE; + + delete poFeature; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_L_SetNextByIndex() */ +/************************************************************************/ + +OGRErr OGR_L_SetNextByIndex( OGRLayerH hLayer, GIntBig nIndex ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_SetNextByIndex", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_SetNextByIndex(hLayer, nIndex); +#endif + + return ((OGRLayer *)hLayer)->SetNextByIndex( nIndex ); +} + +/************************************************************************/ +/* OGR_L_GetNextFeature() */ +/************************************************************************/ + +OGRFeatureH OGR_L_GetNextFeature( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetNextFeature", NULL ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_GetNextFeature(hLayer); +#endif + + return (OGRFeatureH) ((OGRLayer *)hLayer)->GetNextFeature(); +} + +/************************************************************************/ +/* ConvertNonLinearGeomsIfNecessary() */ +/************************************************************************/ + +void OGRLayer::ConvertNonLinearGeomsIfNecessary( OGRFeature *poFeature ) +{ + if( !TestCapability(OLCCurveGeometries) ) + { + int nGeomFieldCount = GetLayerDefn()->GetGeomFieldCount(); + for(int i=0;iGetGeomFieldRef(i); + if( poGeom != NULL && OGR_GT_IsNonLinear(poGeom->getGeometryType()) ) + { + OGRwkbGeometryType eTargetType = OGR_GT_GetLinear(poGeom->getGeometryType()); + poFeature->SetGeomFieldDirectly(i, + OGRGeometryFactory::forceTo(poFeature->StealGeometry(i), eTargetType)); + } + } + } +} + +/************************************************************************/ +/* SetFeature() */ +/************************************************************************/ + +OGRErr OGRLayer::SetFeature( OGRFeature *poFeature ) + +{ + ConvertNonLinearGeomsIfNecessary(poFeature); + return ISetFeature(poFeature); +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRLayer::ISetFeature( OGRFeature * ) + +{ + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_SetFeature() */ +/************************************************************************/ + +OGRErr OGR_L_SetFeature( OGRLayerH hLayer, OGRFeatureH hFeat ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_SetFeature", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( hFeat, "OGR_L_SetFeature", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_SetFeature(hLayer, hFeat); +#endif + + return ((OGRLayer *)hLayer)->SetFeature( (OGRFeature *) hFeat ); +} + +/************************************************************************/ +/* CreateFeature() */ +/************************************************************************/ + +OGRErr OGRLayer::CreateFeature( OGRFeature *poFeature ) + +{ + ConvertNonLinearGeomsIfNecessary(poFeature); + return ICreateFeature(poFeature); +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRLayer::ICreateFeature( OGRFeature * ) + +{ + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_CreateFeature() */ +/************************************************************************/ + +OGRErr OGR_L_CreateFeature( OGRLayerH hLayer, OGRFeatureH hFeat ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_CreateFeature", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( hFeat, "OGR_L_CreateFeature", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_CreateFeature(hLayer, hFeat); +#endif + + return ((OGRLayer *) hLayer)->CreateFeature( (OGRFeature *) hFeat ); +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRLayer::CreateField( OGRFieldDefn * poField, int bApproxOK ) + +{ + (void) poField; + (void) bApproxOK; + + CPLError( CE_Failure, CPLE_NotSupported, + "CreateField() not supported by this layer.\n" ); + + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_CreateField() */ +/************************************************************************/ + +OGRErr OGR_L_CreateField( OGRLayerH hLayer, OGRFieldDefnH hField, + int bApproxOK ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_CreateField", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( hField, "OGR_L_CreateField", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_CreateField(hLayer, hField, bApproxOK); +#endif + + return ((OGRLayer *) hLayer)->CreateField( (OGRFieldDefn *) hField, + bApproxOK ); +} + +/************************************************************************/ +/* DeleteField() */ +/************************************************************************/ + +OGRErr OGRLayer::DeleteField( int iField ) + +{ + (void) iField; + + CPLError( CE_Failure, CPLE_NotSupported, + "DeleteField() not supported by this layer.\n" ); + + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_DeleteField() */ +/************************************************************************/ + +OGRErr OGR_L_DeleteField( OGRLayerH hLayer, int iField ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_DeleteField", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_DeleteField(hLayer, iField); +#endif + + return ((OGRLayer *) hLayer)->DeleteField( iField ); +} + +/************************************************************************/ +/* ReorderFields() */ +/************************************************************************/ + +OGRErr OGRLayer::ReorderFields( int* panMap ) + +{ + (void) panMap; + + CPLError( CE_Failure, CPLE_NotSupported, + "ReorderFields() not supported by this layer.\n" ); + + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_ReorderFields() */ +/************************************************************************/ + +OGRErr OGR_L_ReorderFields( OGRLayerH hLayer, int* panMap ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_ReorderFields", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_ReorderFields(hLayer, panMap); +#endif + + return ((OGRLayer *) hLayer)->ReorderFields( panMap ); +} + +/************************************************************************/ +/* ReorderField() */ +/************************************************************************/ + +OGRErr OGRLayer::ReorderField( int iOldFieldPos, int iNewFieldPos ) + +{ + OGRErr eErr; + + int nFieldCount = GetLayerDefn()->GetFieldCount(); + + if (iOldFieldPos < 0 || iOldFieldPos >= nFieldCount) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + if (iNewFieldPos < 0 || iNewFieldPos >= nFieldCount) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + if (iNewFieldPos == iOldFieldPos) + return OGRERR_NONE; + + int* panMap = (int*) CPLMalloc(sizeof(int) * nFieldCount); + int i; + if (iOldFieldPos < iNewFieldPos) + { + /* "0","1","2","3","4" (1,3) -> "0","2","3","1","4" */ + for(i=0;i "0","3","1","2","4" */ + for(i=0;iReorderField( iOldFieldPos, iNewFieldPos ); +} + +/************************************************************************/ +/* AlterFieldDefn() */ +/************************************************************************/ + +OGRErr OGRLayer::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, + int nFlags ) + +{ + (void) iField; + (void) poNewFieldDefn; + (void) nFlags; + + CPLError( CE_Failure, CPLE_NotSupported, + "AlterFieldDefn() not supported by this layer.\n" ); + + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_AlterFieldDefn() */ +/************************************************************************/ + +OGRErr OGR_L_AlterFieldDefn( OGRLayerH hLayer, int iField, OGRFieldDefnH hNewFieldDefn, + int nFlags ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_AlterFieldDefn", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( hNewFieldDefn, "OGR_L_AlterFieldDefn", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_AlterFieldDefn(hLayer, iField, hNewFieldDefn, nFlags); +#endif + + return ((OGRLayer *) hLayer)->AlterFieldDefn( iField, (OGRFieldDefn*) hNewFieldDefn, nFlags ); +} + +/************************************************************************/ +/* CreateGeomField() */ +/************************************************************************/ + +OGRErr OGRLayer::CreateGeomField( OGRGeomFieldDefn * poField, int bApproxOK ) + +{ + (void) poField; + (void) bApproxOK; + + CPLError( CE_Failure, CPLE_NotSupported, + "CreateGeomField() not supported by this layer.\n" ); + + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_CreateGeomField() */ +/************************************************************************/ + +OGRErr OGR_L_CreateGeomField( OGRLayerH hLayer, OGRGeomFieldDefnH hField, + int bApproxOK ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_CreateGeomField", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( hField, "OGR_L_CreateGeomField", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_CreateGeomField(hLayer, hField, bApproxOK); +#endif + + return ((OGRLayer *) hLayer)->CreateGeomField( (OGRGeomFieldDefn *) hField, + bApproxOK ); +} + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +OGRErr OGRLayer::StartTransaction() + +{ + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_L_StartTransaction() */ +/************************************************************************/ + +OGRErr OGR_L_StartTransaction( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_StartTransaction", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_StartTransaction(hLayer); +#endif + + return ((OGRLayer *)hLayer)->StartTransaction(); +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr OGRLayer::CommitTransaction() + +{ + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_L_CommitTransaction() */ +/************************************************************************/ + +OGRErr OGR_L_CommitTransaction( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_CommitTransaction", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_CommitTransaction(hLayer); +#endif + + return ((OGRLayer *)hLayer)->CommitTransaction(); +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +OGRErr OGRLayer::RollbackTransaction() + +{ + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_RollbackTransaction() */ +/************************************************************************/ + +OGRErr OGR_L_RollbackTransaction( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_RollbackTransaction", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_RollbackTransaction(hLayer); +#endif + + return ((OGRLayer *)hLayer)->RollbackTransaction(); +} + +/************************************************************************/ +/* OGR_L_GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefnH OGR_L_GetLayerDefn( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetLayerDefn", NULL ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_GetLayerDefn(hLayer); +#endif + + return (OGRFeatureDefnH) ((OGRLayer *)hLayer)->GetLayerDefn(); +} + +/************************************************************************/ +/* OGR_L_FindFieldIndex() */ +/************************************************************************/ + +int OGR_L_FindFieldIndex( OGRLayerH hLayer, const char *pszFieldName, int bExactMatch ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_FindFieldIndex", -1 ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_FindFieldIndex(hLayer, pszFieldName, bExactMatch); +#endif + + return ((OGRLayer *)hLayer)->FindFieldIndex( pszFieldName, bExactMatch ); +} + +/************************************************************************/ +/* FindFieldIndex() */ +/************************************************************************/ + +int OGRLayer::FindFieldIndex( const char *pszFieldName, CPL_UNUSED int bExactMatch ) +{ + return GetLayerDefn()->GetFieldIndex( pszFieldName ); +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRLayer::GetSpatialRef() +{ + if( GetLayerDefn()->GetGeomFieldCount() > 0 ) + return GetLayerDefn()->GetGeomFieldDefn(0)->GetSpatialRef(); + else + return NULL; +} + +/************************************************************************/ +/* OGR_L_GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReferenceH OGR_L_GetSpatialRef( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetSpatialRef", NULL ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_GetSpatialRef(hLayer); +#endif + + return (OGRSpatialReferenceH) ((OGRLayer *) hLayer)->GetSpatialRef(); +} + +/************************************************************************/ +/* OGR_L_TestCapability() */ +/************************************************************************/ + +int OGR_L_TestCapability( OGRLayerH hLayer, const char *pszCap ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_TestCapability", 0 ); + VALIDATE_POINTER1( pszCap, "OGR_L_TestCapability", 0 ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_TestCapability(hLayer, pszCap); +#endif + + return ((OGRLayer *) hLayer)->TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetSpatialFilter() */ +/************************************************************************/ + +OGRGeometry *OGRLayer::GetSpatialFilter() + +{ + return m_poFilterGeom; +} + +/************************************************************************/ +/* OGR_L_GetSpatialFilter() */ +/************************************************************************/ + +OGRGeometryH OGR_L_GetSpatialFilter( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetSpatialFilter", NULL ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_GetSpatialFilter(hLayer); +#endif + + return (OGRGeometryH) ((OGRLayer *) hLayer)->GetSpatialFilter(); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) + +{ + m_iGeomFieldFilter = 0; + if( InstallFilter( poGeomIn ) ) + ResetReading(); +} + + +void OGRLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeomIn ) + +{ + if( iGeomField == 0 ) + { + m_iGeomFieldFilter = iGeomField; + SetSpatialFilter( poGeomIn ); + } + else + { + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + return; + } + + m_iGeomFieldFilter = iGeomField; + if( InstallFilter( poGeomIn ) ) + ResetReading(); + } +} + +/************************************************************************/ +/* OGR_L_SetSpatialFilter() */ +/************************************************************************/ + +void OGR_L_SetSpatialFilter( OGRLayerH hLayer, OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER0( hLayer, "OGR_L_SetSpatialFilter" ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_SetSpatialFilter(hLayer, hGeom); +#endif + + ((OGRLayer *) hLayer)->SetSpatialFilter( (OGRGeometry *) hGeom ); +} + +/************************************************************************/ +/* OGR_L_SetSpatialFilterEx() */ +/************************************************************************/ + +void OGR_L_SetSpatialFilterEx( OGRLayerH hLayer, int iGeomField, + OGRGeometryH hGeom ) + +{ + VALIDATE_POINTER0( hLayer, "OGR_L_SetSpatialFilterEx" ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_SetSpatialFilterEx(hLayer, iGeomField, hGeom); +#endif + + ((OGRLayer *) hLayer)->SetSpatialFilter( iGeomField, (OGRGeometry *) hGeom ); +} +/************************************************************************/ +/* SetSpatialFilterRect() */ +/************************************************************************/ + +void OGRLayer::SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) + +{ + SetSpatialFilterRect( 0, dfMinX, dfMinY, dfMaxX, dfMaxY ); +} + + +void OGRLayer::SetSpatialFilterRect( int iGeomField, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) + +{ + OGRLinearRing oRing; + OGRPolygon oPoly; + + oRing.addPoint( dfMinX, dfMinY ); + oRing.addPoint( dfMinX, dfMaxY ); + oRing.addPoint( dfMaxX, dfMaxY ); + oRing.addPoint( dfMaxX, dfMinY ); + oRing.addPoint( dfMinX, dfMinY ); + + oPoly.addRing( &oRing ); + + if( iGeomField == 0 ) + /* for drivers that only overload SetSpatialFilter(OGRGeometry*) */ + SetSpatialFilter( &oPoly ); + else + SetSpatialFilter( iGeomField, &oPoly ); +} + +/************************************************************************/ +/* OGR_L_SetSpatialFilterRect() */ +/************************************************************************/ + +void OGR_L_SetSpatialFilterRect( OGRLayerH hLayer, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) + +{ + VALIDATE_POINTER0( hLayer, "OGR_L_SetSpatialFilterRect" ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_SetSpatialFilterRect(hLayer, dfMinX, dfMinY, dfMaxX, dfMaxY); +#endif + + ((OGRLayer *) hLayer)->SetSpatialFilterRect( dfMinX, dfMinY, + dfMaxX, dfMaxY ); +} + +/************************************************************************/ +/* OGR_L_SetSpatialFilterRectEx() */ +/************************************************************************/ + +void OGR_L_SetSpatialFilterRectEx( OGRLayerH hLayer, + int iGeomField, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) + +{ + VALIDATE_POINTER0( hLayer, "OGR_L_SetSpatialFilterRectEx" ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_SetSpatialFilterRectEx(hLayer, iGeomField, dfMinX, dfMinY, dfMaxX, dfMaxY); +#endif + + ((OGRLayer *) hLayer)->SetSpatialFilterRect( iGeomField, + dfMinX, dfMinY, + dfMaxX, dfMaxY ); +} + +/************************************************************************/ +/* InstallFilter() */ +/* */ +/* This method is only intended to be used from within */ +/* drivers, normally from the SetSpatialFilter() method. */ +/* It installs a filter, and also tests it to see if it is */ +/* rectangular. If so, it this is kept track of alongside the */ +/* filter geometry itself so we can do cheaper comparisons in */ +/* the FilterGeometry() call. */ +/* */ +/* Returns TRUE if the newly installed filter differs in some */ +/* way from the current one. */ +/************************************************************************/ + +int OGRLayer::InstallFilter( OGRGeometry * poFilter ) + +{ + if( m_poFilterGeom == NULL && poFilter == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Replace the existing filter. */ +/* -------------------------------------------------------------------- */ + if( m_poFilterGeom != NULL ) + { + delete m_poFilterGeom; + m_poFilterGeom = NULL; + } + + if( m_pPreparedFilterGeom != NULL ) + { + OGRDestroyPreparedGeometry(m_pPreparedFilterGeom); + m_pPreparedFilterGeom = NULL; + } + + if( poFilter != NULL ) + m_poFilterGeom = poFilter->clone(); + + m_bFilterIsEnvelope = FALSE; + + if( m_poFilterGeom == NULL ) + return TRUE; + + if( m_poFilterGeom != NULL ) + m_poFilterGeom->getEnvelope( &m_sFilterEnvelope ); + + /* Compile geometry filter as a prepared geometry */ + m_pPreparedFilterGeom = OGRCreatePreparedGeometry(m_poFilterGeom); + +/* -------------------------------------------------------------------- */ +/* Now try to determine if the filter is really a rectangle. */ +/* -------------------------------------------------------------------- */ + if( wkbFlatten(m_poFilterGeom->getGeometryType()) != wkbPolygon ) + return TRUE; + + OGRPolygon *poPoly = (OGRPolygon *) m_poFilterGeom; + + if( poPoly->getNumInteriorRings() != 0 ) + return TRUE; + + OGRLinearRing *poRing = poPoly->getExteriorRing(); + if (poRing == NULL) + return TRUE; + + if( poRing->getNumPoints() > 5 || poRing->getNumPoints() < 4 ) + return TRUE; + + // If the ring has 5 points, the last should be the first. + if( poRing->getNumPoints() == 5 + && ( poRing->getX(0) != poRing->getX(4) + || poRing->getY(0) != poRing->getY(4) ) ) + return TRUE; + + // Polygon with first segment in "y" direction. + if( poRing->getX(0) == poRing->getX(1) + && poRing->getY(1) == poRing->getY(2) + && poRing->getX(2) == poRing->getX(3) + && poRing->getY(3) == poRing->getY(0) ) + m_bFilterIsEnvelope = TRUE; + + // Polygon with first segment in "x" direction. + if( poRing->getY(0) == poRing->getY(1) + && poRing->getX(1) == poRing->getX(2) + && poRing->getY(2) == poRing->getY(3) + && poRing->getX(3) == poRing->getX(0) ) + m_bFilterIsEnvelope = TRUE; + + return TRUE; +} + +/************************************************************************/ +/* FilterGeometry() */ +/* */ +/* Compare the passed in geometry to the currently installed */ +/* filter. Optimize for case where filter is just an */ +/* envelope. */ +/************************************************************************/ + +int OGRLayer::FilterGeometry( OGRGeometry *poGeometry ) + +{ +/* -------------------------------------------------------------------- */ +/* In trivial cases of new filter or target geometry, we accept */ +/* an intersection. No geometry is taken to mean "the whole */ +/* world". */ +/* -------------------------------------------------------------------- */ + if( m_poFilterGeom == NULL ) + return TRUE; + + if( poGeometry == NULL ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Compute the target geometry envelope, and if there is no */ +/* intersection between the envelopes we are sure not to have */ +/* any intersection. */ +/* -------------------------------------------------------------------- */ + OGREnvelope sGeomEnv; + + poGeometry->getEnvelope( &sGeomEnv ); + + if( sGeomEnv.MaxX < m_sFilterEnvelope.MinX + || sGeomEnv.MaxY < m_sFilterEnvelope.MinY + || m_sFilterEnvelope.MaxX < sGeomEnv.MinX + || m_sFilterEnvelope.MaxY < sGeomEnv.MinY ) + return FALSE; + + +/* -------------------------------------------------------------------- */ +/* If the filter geometry is its own envelope and if the */ +/* envelope of the geometry is inside the filter geometry, */ +/* the geometry itself is inside the filter geometry */ +/* -------------------------------------------------------------------- */ + if( m_bFilterIsEnvelope && + sGeomEnv.MinX >= m_sFilterEnvelope.MinX && + sGeomEnv.MinY >= m_sFilterEnvelope.MinY && + sGeomEnv.MaxX <= m_sFilterEnvelope.MaxX && + sGeomEnv.MaxY <= m_sFilterEnvelope.MaxY) + { + return TRUE; + } + else + { +/* -------------------------------------------------------------------- */ +/* If the filter geometry is its own envelope and if the */ +/* the geometry (line, or polygon without hole) h has at least one */ +/* point inside the filter geometry, the geometry itself is inside */ +/* the filter geometry. */ +/* -------------------------------------------------------------------- */ + if( m_bFilterIsEnvelope ) + { + OGRLineString* poLS = NULL; + + switch( wkbFlatten(poGeometry->getGeometryType()) ) + { + case wkbPolygon: + { + OGRPolygon* poPoly = (OGRPolygon* )poGeometry; + OGRLinearRing* poRing = poPoly->getExteriorRing(); + if (poRing != NULL && poPoly->getNumInteriorRings() == 0) + { + poLS = poRing; + } + break; + } + + case wkbLineString: + poLS = (OGRLineString* )poGeometry; + break; + + default: + break; + } + + if( poLS != NULL ) + { + int nNumPoints = poLS->getNumPoints(); + for(int i = 0; i < nNumPoints; i++) + { + double x = poLS->getX(i); + double y = poLS->getY(i); + if (x >= m_sFilterEnvelope.MinX && + y >= m_sFilterEnvelope.MinY && + x <= m_sFilterEnvelope.MaxX && + y <= m_sFilterEnvelope.MaxY) + { + return TRUE; + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Fallback to full intersect test (using GEOS) if we still */ +/* don't know for sure. */ +/* -------------------------------------------------------------------- */ + if( OGRGeometryFactory::haveGEOS() ) + { + //CPLDebug("OGRLayer", "GEOS intersection"); + if( m_pPreparedFilterGeom != NULL ) + return OGRPreparedGeometryIntersects(m_pPreparedFilterGeom, + poGeometry); + else + return m_poFilterGeom->Intersects( poGeometry ); + } + else + return TRUE; + } +} + +/************************************************************************/ +/* OGR_L_ResetReading() */ +/************************************************************************/ + +void OGR_L_ResetReading( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER0( hLayer, "OGR_L_ResetReading" ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_ResetReading(hLayer); +#endif + + ((OGRLayer *) hLayer)->ResetReading(); +} + +/************************************************************************/ +/* InitializeIndexSupport() */ +/* */ +/* This is only intended to be called by driver layer */ +/* implementations but we don't make it protected so that the */ +/* datasources can do it too if that is more appropriate. */ +/************************************************************************/ + +OGRErr OGRLayer::InitializeIndexSupport( const char *pszFilename ) + +{ + OGRErr eErr; + + if (m_poAttrIndex != NULL) + return OGRERR_NONE; + + m_poAttrIndex = OGRCreateDefaultLayerIndex(); + + eErr = m_poAttrIndex->Initialize( pszFilename, this ); + if( eErr != OGRERR_NONE ) + { + delete m_poAttrIndex; + m_poAttrIndex = NULL; + } + + return eErr; +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr OGRLayer::SyncToDisk() + +{ + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_L_SyncToDisk() */ +/************************************************************************/ + +OGRErr OGR_L_SyncToDisk( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_SyncToDisk", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_SyncToDisk(hLayer); +#endif + + return ((OGRLayer *) hLayer)->SyncToDisk(); +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRLayer::DeleteFeature( CPL_UNUSED GIntBig nFID ) +{ + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_L_DeleteFeature() */ +/************************************************************************/ + +OGRErr OGR_L_DeleteFeature( OGRLayerH hLayer, GIntBig nFID ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_DeleteFeature", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_DeleteFeature(hLayer, nFID); +#endif + + return ((OGRLayer *) hLayer)->DeleteFeature( nFID ); +} + +/************************************************************************/ +/* GetFeaturesRead() */ +/************************************************************************/ + +GIntBig OGRLayer::GetFeaturesRead() + +{ + return m_nFeaturesRead; +} + +/************************************************************************/ +/* OGR_L_GetFeaturesRead() */ +/************************************************************************/ + +GIntBig OGR_L_GetFeaturesRead( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetFeaturesRead", 0 ); + + return ((OGRLayer *) hLayer)->GetFeaturesRead(); +} + +/************************************************************************/ +/* GetFIDColumn */ +/************************************************************************/ + +const char *OGRLayer::GetFIDColumn() + +{ + return ""; +} + +/************************************************************************/ +/* OGR_L_GetFIDColumn() */ +/************************************************************************/ + +const char *OGR_L_GetFIDColumn( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetFIDColumn", NULL ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_GetFIDColumn(hLayer); +#endif + + return ((OGRLayer *) hLayer)->GetFIDColumn(); +} + +/************************************************************************/ +/* GetGeometryColumn() */ +/************************************************************************/ + +const char *OGRLayer::GetGeometryColumn() + +{ + if( GetLayerDefn()->GetGeomFieldCount() > 0 ) + return GetLayerDefn()->GetGeomFieldDefn(0)->GetNameRef(); + else + return ""; +} + +/************************************************************************/ +/* OGR_L_GetGeometryColumn() */ +/************************************************************************/ + +const char *OGR_L_GetGeometryColumn( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetGeometryColumn", NULL ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_GetGeometryColumn(hLayer); +#endif + + return ((OGRLayer *) hLayer)->GetGeometryColumn(); +} + +/************************************************************************/ +/* GetStyleTable() */ +/************************************************************************/ + +OGRStyleTable *OGRLayer::GetStyleTable() +{ + return m_poStyleTable; +} + +/************************************************************************/ +/* SetStyleTableDirectly() */ +/************************************************************************/ + +void OGRLayer::SetStyleTableDirectly( OGRStyleTable *poStyleTable ) +{ + if ( m_poStyleTable ) + delete m_poStyleTable; + m_poStyleTable = poStyleTable; +} + +/************************************************************************/ +/* SetStyleTable() */ +/************************************************************************/ + +void OGRLayer::SetStyleTable(OGRStyleTable *poStyleTable) +{ + if ( m_poStyleTable ) + delete m_poStyleTable; + if ( poStyleTable ) + m_poStyleTable = poStyleTable->Clone(); +} + +/************************************************************************/ +/* OGR_L_GetStyleTable() */ +/************************************************************************/ + +OGRStyleTableH OGR_L_GetStyleTable( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetStyleTable", NULL ); + + return (OGRStyleTableH) ((OGRLayer *) hLayer)->GetStyleTable( ); +} + +/************************************************************************/ +/* OGR_L_SetStyleTableDirectly() */ +/************************************************************************/ + +void OGR_L_SetStyleTableDirectly( OGRLayerH hLayer, + OGRStyleTableH hStyleTable ) + +{ + VALIDATE_POINTER0( hLayer, "OGR_L_SetStyleTableDirectly" ); + + ((OGRLayer *) hLayer)->SetStyleTableDirectly( (OGRStyleTable *) hStyleTable); +} + +/************************************************************************/ +/* OGR_L_SetStyleTable() */ +/************************************************************************/ + +void OGR_L_SetStyleTable( OGRLayerH hLayer, + OGRStyleTableH hStyleTable ) + +{ + VALIDATE_POINTER0( hLayer, "OGR_L_SetStyleTable" ); + VALIDATE_POINTER0( hStyleTable, "OGR_L_SetStyleTable" ); + + ((OGRLayer *) hLayer)->SetStyleTable( (OGRStyleTable *) hStyleTable); +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRLayer::GetName() + +{ + return GetLayerDefn()->GetName(); +} + +/************************************************************************/ +/* OGR_L_GetName() */ +/************************************************************************/ + +const char* OGR_L_GetName( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetName", "" ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_GetName(hLayer); +#endif + + return ((OGRLayer *) hLayer)->GetName(); +} + +/************************************************************************/ +/* GetGeomType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRLayer::GetGeomType() +{ + OGRFeatureDefn* poLayerDefn = GetLayerDefn(); + if( poLayerDefn == NULL ) + { + CPLDebug("OGR", "GetLayerType() returns NULL !"); + return wkbUnknown; + } + return poLayerDefn->GetGeomType(); +} + +/************************************************************************/ +/* OGR_L_GetGeomType() */ +/************************************************************************/ + +OGRwkbGeometryType OGR_L_GetGeomType( OGRLayerH hLayer ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_GetGeomType", wkbUnknown ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_GetGeomType(hLayer); +#endif + + OGRwkbGeometryType eType = ((OGRLayer *) hLayer)->GetGeomType(); + if( OGR_GT_IsNonLinear(eType) && !OGRGetNonLinearGeometriesEnabledFlag() ) + { + eType = OGR_GT_GetLinear(eType); + } + return eType; +} + +/************************************************************************/ +/* SetIgnoredFields() */ +/************************************************************************/ + +OGRErr OGRLayer::SetIgnoredFields( const char **papszFields ) +{ + OGRFeatureDefn *poDefn = GetLayerDefn(); + + // first set everything as *not* ignored + for( int iField = 0; iField < poDefn->GetFieldCount(); iField++ ) + { + poDefn->GetFieldDefn(iField)->SetIgnored( FALSE ); + } + poDefn->SetGeometryIgnored( FALSE ); + poDefn->SetStyleIgnored( FALSE ); + + if ( papszFields == NULL ) + return OGRERR_NONE; + + // ignore some fields + while ( *papszFields ) + { + const char* pszFieldName = *papszFields; + // check special fields + if ( EQUAL(pszFieldName, "OGR_GEOMETRY") ) + poDefn->SetGeometryIgnored( TRUE ); + else if ( EQUAL(pszFieldName, "OGR_STYLE") ) + poDefn->SetStyleIgnored( TRUE ); + else + { + // check ordinary fields + int iField = poDefn->GetFieldIndex(pszFieldName); + if ( iField == -1 ) + { + // check geometry field + iField = poDefn->GetGeomFieldIndex(pszFieldName); + if ( iField == -1 ) + { + return OGRERR_FAILURE; + } + else + poDefn->GetGeomFieldDefn(iField)->SetIgnored( TRUE ); + } + else + poDefn->GetFieldDefn(iField)->SetIgnored( TRUE ); + } + papszFields++; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGR_L_SetIgnoredFields() */ +/************************************************************************/ + +OGRErr OGR_L_SetIgnoredFields( OGRLayerH hLayer, const char **papszFields ) + +{ + VALIDATE_POINTER1( hLayer, "OGR_L_SetIgnoredFields", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpy_L_SetIgnoredFields(hLayer, papszFields); +#endif + + return ((OGRLayer *) hLayer)->SetIgnoredFields( papszFields ); +} + +/************************************************************************/ +/* helper functions for layer overlay methods */ +/************************************************************************/ + +static +OGRErr clone_spatial_filter(OGRLayer *pLayer, OGRGeometry **ppGeometry) +{ + OGRErr ret = OGRERR_NONE; + OGRGeometry *g = pLayer->GetSpatialFilter(); + *ppGeometry = g ? g->clone() : NULL; + return ret; +} + +static +OGRErr create_field_map(OGRFeatureDefn *poDefn, int **map) +{ + OGRErr ret = OGRERR_NONE; + int n = poDefn->GetFieldCount(); + if (n > 0) { + *map = (int*)VSIMalloc(sizeof(int) * n); + if (!(*map)) return OGRERR_NOT_ENOUGH_MEMORY; + for(int i=0;iGetLayerDefn(); + const char* pszInputPrefix = CSLFetchNameValue(papszOptions, "INPUT_PREFIX"); + const char* pszMethodPrefix = CSLFetchNameValue(papszOptions, "METHOD_PREFIX"); + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + if (poDefnResult->GetFieldCount() > 0) { + // the user has defined the schema of the output layer + for( int iField = 0; iField < poDefnInput->GetFieldCount(); iField++ ) { + CPLString osName(poDefnInput->GetFieldDefn(iField)->GetNameRef()); + if( pszInputPrefix != NULL ) + osName = pszInputPrefix + osName; + mapInput[iField] = poDefnResult->GetFieldIndex(osName); + } + if (!mapMethod) return ret; + for( int iField = 0; iField < poDefnMethod->GetFieldCount(); iField++ ) { + CPLString osName(poDefnMethod->GetFieldDefn(iField)->GetNameRef()); + if( pszMethodPrefix != NULL ) + osName = pszMethodPrefix + osName; + mapMethod[iField] = poDefnResult->GetFieldIndex(osName); + } + } else { + // use schema from the input layer or from input and method layers + int nFieldsInput = poDefnInput->GetFieldCount(); + for( int iField = 0; iField < nFieldsInput; iField++ ) { + OGRFieldDefn oFieldDefn(poDefnInput->GetFieldDefn(iField)); + if( pszInputPrefix != NULL ) + oFieldDefn.SetName(CPLSPrintf("%s%s", pszInputPrefix, oFieldDefn.GetNameRef())); + ret = pLayerResult->CreateField(&oFieldDefn); + if (ret != OGRERR_NONE) { + if (!bSkipFailures) + return ret; + else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + mapInput[iField] = iField; + } + if (!combined) return ret; + if (!mapMethod) return ret; + for( int iField = 0; iField < poDefnMethod->GetFieldCount(); iField++ ) { + OGRFieldDefn oFieldDefn(poDefnMethod->GetFieldDefn(iField)); + if( pszMethodPrefix != NULL ) + oFieldDefn.SetName(CPLSPrintf("%s%s", pszMethodPrefix, oFieldDefn.GetNameRef())); + ret = pLayerResult->CreateField(&oFieldDefn); + if (ret != OGRERR_NONE) { + if (!bSkipFailures) + return ret; + else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + mapMethod[iField] = nFieldsInput+iField; + } + } + return ret; +} + +static +OGRGeometry *set_filter_from(OGRLayer *pLayer, OGRGeometry *pGeometryExistingFilter, OGRFeature *pFeature) +{ + OGRGeometry *geom = pFeature->GetGeometryRef(); + if (!geom) return NULL; + if (pGeometryExistingFilter) { + if (!geom->Intersects(pGeometryExistingFilter)) return NULL; + OGRGeometry *intersection = geom->Intersection(pGeometryExistingFilter); + pLayer->SetSpatialFilter(intersection); + if (intersection) delete intersection; + } else { + pLayer->SetSpatialFilter(geom); + } + return geom; +} + +static OGRGeometry* promote_to_multi(OGRGeometry* poGeom) +{ + OGRwkbGeometryType eType = wkbFlatten(poGeom->getGeometryType()); + if( eType == wkbPolygon ) + return OGRGeometryFactory::forceToMultiPolygon(poGeom); + else if( eType == wkbLineString ) + return OGRGeometryFactory::forceToMultiLineString(poGeom); + else + return poGeom; +} + +/************************************************************************/ +/* Intersection() */ +/************************************************************************/ +/** + * \brief Intersection of two layers. + * + * The result layer contains features whose geometries represent areas + * that are common between features in the input layer and in the + * method layer. The features in the result layer have attributes from + * both input and method layers. The schema of the result layer can be + * set by the user or, if it is empty, is initialized to contain all + * fields in the input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *

      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_Intersection(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::Intersection( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnMethod = pLayerMethod->GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + int *mapInput = NULL; + int *mapMethod = NULL; + OGREnvelope sEnvelopeMethod; + GBool bEnvelopeSet; + double progress_max = (double) GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + // get resources + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnMethod, &mapMethod); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, poDefnMethod, mapInput, mapMethod, 1, papszOptions); + if (ret != OGRERR_NONE) goto done; + poDefnResult = pLayerResult->GetLayerDefn(); + bEnvelopeSet = pLayerMethod->GetExtent(&sEnvelopeMethod, 1) == OGRERR_NONE; + + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // is it worth to proceed? + if (bEnvelopeSet) { + OGRGeometry *x_geom = x->GetGeometryRef(); + if (x_geom) { + OGREnvelope x_env; + x_geom->getEnvelope(&x_env); + if (x_env.MaxX < sEnvelopeMethod.MinX + || x_env.MaxY < sEnvelopeMethod.MinY + || sEnvelopeMethod.MaxX < x_env.MinX + || sEnvelopeMethod.MaxY < x_env.MinY) { + delete x; + continue; + } + } else { + delete x; + continue; + } + } + + // set up the filter for method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + pLayerMethod->ResetReading(); + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry* poIntersection = x_geom->Intersection(y_geom); + if( poIntersection == NULL || poIntersection->IsEmpty() || + (x_geom->getDimension() == 2 && + y_geom->getDimension() == 2 && + poIntersection->getDimension() < 2) ) + { + delete poIntersection; + delete y; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + z->SetFieldsFrom(y, mapMethod); + if( bPromoteToMulti ) + poIntersection = promote_to_multi(poIntersection); + z->SetGeometryDirectly(poIntersection); + delete y; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + delete x; + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + + delete x; + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (mapInput) VSIFree(mapInput); + if (mapMethod) VSIFree(mapMethod); + return ret; +} + +/************************************************************************/ +/* OGR_L_Intersection() */ +/************************************************************************/ +/** + * \brief Intersection of two layers. + * + * The result layer contains features whose geometries represent areas + * that are common between features in the input layer and in the + * method layer. The features in the result layer have attributes from + * both input and method layers. The schema of the result layer can be + * set by the user or, if it is empty, is initialized to contain all + * fields in the input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::Intersection(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_Intersection( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_Intersection", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_Intersection", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_Intersection", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->Intersection( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} + +/************************************************************************/ +/* Union() */ +/************************************************************************/ + +/** + * \brief Union of two layers. + * + * The result layer contains features whose geometries represent areas + * that are in either in the input layer or in the method layer. The + * features in the result layer have attributes from both input and + * method layers. For features which represent areas that are only in + * the input or in the method layer the respective attributes have + * undefined values. The schema of the result layer can be set by the + * user or, if it is empty, is initialized to contain all fields in + * the input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer (even if it is undefined). + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_Union(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::Union( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnMethod = pLayerMethod->GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + OGRGeometry *pGeometryInputFilter = NULL; + int *mapInput = NULL; + int *mapMethod = NULL; + double progress_max = (double) GetFeatureCount(0) + (double) pLayerMethod->GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + // get resources + ret = clone_spatial_filter(this, &pGeometryInputFilter); + if (ret != OGRERR_NONE) goto done; + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnMethod, &mapMethod); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, poDefnMethod, mapInput, mapMethod, 1, papszOptions); + if (ret != OGRERR_NONE) goto done; + poDefnResult = pLayerResult->GetLayerDefn(); + + // add features based on input layer + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *x_geom_diff = x_geom->clone(); // this will be the geometry of the result feature + pLayerMethod->ResetReading(); + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry *poIntersection = x_geom->Intersection(y_geom); + if( poIntersection == NULL || poIntersection->IsEmpty() || + (x_geom->getDimension() == 2 && + y_geom->getDimension() == 2 && + poIntersection->getDimension() < 2) ) + { + delete poIntersection; + delete y; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + z->SetFieldsFrom(y, mapMethod); + if( bPromoteToMulti ) + poIntersection = promote_to_multi(poIntersection); + z->SetGeometryDirectly(poIntersection); + OGRGeometry *x_geom_diff_new = x_geom_diff ? x_geom_diff->Difference(y_geom) : NULL; + if (x_geom_diff) delete x_geom_diff; + x_geom_diff = x_geom_diff_new; + delete y; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + delete x; + if (x_geom_diff) + delete x_geom_diff; + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + + if( x_geom_diff == NULL || x_geom_diff->IsEmpty() ) + { + delete x_geom_diff; + delete x; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + if( bPromoteToMulti ) + x_geom_diff = promote_to_multi(x_geom_diff); + z->SetGeometryDirectly(x_geom_diff); + delete x; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + + // restore filter on method layer and add features based on it + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + pLayerMethod->ResetReading(); + while (OGRFeature *x = pLayerMethod->GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on input layer + OGRGeometry *x_geom = set_filter_from(this, pGeometryInputFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *x_geom_diff = x_geom->clone(); // this will be the geometry of the result feature + ResetReading(); + while (OGRFeature *y = GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry *x_geom_diff_new = x_geom_diff ? x_geom_diff->Difference(y_geom) : NULL; + if (x_geom_diff) delete x_geom_diff; + x_geom_diff = x_geom_diff_new; + delete y; + } + + if( x_geom_diff == NULL || x_geom_diff->IsEmpty() ) + { + delete x_geom_diff; + delete x; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapMethod); + if( bPromoteToMulti ) + x_geom_diff = promote_to_multi(x_geom_diff); + z->SetGeometryDirectly(x_geom_diff); + delete x; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + SetSpatialFilter(pGeometryInputFilter); + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (pGeometryInputFilter) delete pGeometryInputFilter; + if (mapInput) VSIFree(mapInput); + if (mapMethod) VSIFree(mapMethod); + return ret; +} + +/************************************************************************/ +/* OGR_L_Union() */ +/************************************************************************/ + +/** + * \brief Union of two layers. + * + * The result layer contains features whose geometries represent areas + * that are in either in the input layer or in the method layer. The + * features in the result layer have attributes from both input and + * method layers. For features which represent areas that are only in + * the input or in the method layer the respective attributes have + * undefined values. The schema of the result layer can be set by the + * user or, if it is empty, is initialized to contain all fields in + * the input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer (even if it is undefined). + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::Union(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_Union( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_Union", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_Union", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_Union", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->Union( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} + +/************************************************************************/ +/* SymDifference() */ +/************************************************************************/ + +/** + * \brief Symmetrical difference of two layers. + * + * The result layer contains features whose geometries represent areas + * that are in either in the input layer or in the method layer but + * not in both. The features in the result layer have attributes from + * both input and method layers. For features which represent areas + * that are only in the input or in the method layer the respective + * attributes have undefined values. The schema of the result layer + * can be set by the user or, if it is empty, is initialized to + * contain all fields in the input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer (even if it is undefined). + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_SymDifference(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::SymDifference( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnMethod = pLayerMethod->GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + OGRGeometry *pGeometryInputFilter = NULL; + int *mapInput = NULL; + int *mapMethod = NULL; + double progress_max = (double) GetFeatureCount(0) + (double) pLayerMethod->GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + // get resources + ret = clone_spatial_filter(this, &pGeometryInputFilter); + if (ret != OGRERR_NONE) goto done; + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnMethod, &mapMethod); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, poDefnMethod, mapInput, mapMethod, 1, papszOptions); + if (ret != OGRERR_NONE) goto done; + poDefnResult = pLayerResult->GetLayerDefn(); + + // add features based on input layer + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *geom = x_geom->clone(); // this will be the geometry of the result feature + pLayerMethod->ResetReading(); + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry *geom_new = geom ? geom->Difference(y_geom) : NULL; + if (geom) delete geom; + geom = geom_new; + delete y; + if (geom && geom->IsEmpty()) break; + } + + OGRFeature *z = NULL; + if (geom && !geom->IsEmpty()) { + z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + if( bPromoteToMulti ) + geom = promote_to_multi(geom); + z->SetGeometryDirectly(geom); + } else { + if (geom) delete geom; + } + delete x; + if (z) { + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + + // restore filter on method layer and add features based on it + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + pLayerMethod->ResetReading(); + while (OGRFeature *x = pLayerMethod->GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on input layer + OGRGeometry *x_geom = set_filter_from(this, pGeometryInputFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *geom = x_geom->clone(); // this will be the geometry of the result feature + ResetReading(); + while (OGRFeature *y = GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry *geom_new = geom ? geom->Difference(y_geom) : NULL; + if (geom) delete geom; + geom = geom_new; + delete y; + if (geom == NULL || geom->IsEmpty()) break; + } + + OGRFeature *z = NULL; + if (geom && !geom->IsEmpty()) { + z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapMethod); + if( bPromoteToMulti ) + geom = promote_to_multi(geom); + z->SetGeometryDirectly(geom); + } else { + if (geom) delete geom; + } + delete x; + if (z) { + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + SetSpatialFilter(pGeometryInputFilter); + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (pGeometryInputFilter) delete pGeometryInputFilter; + if (mapInput) VSIFree(mapInput); + if (mapMethod) VSIFree(mapMethod); + return ret; +} + +/************************************************************************/ +/* OGR_L_SymDifference() */ +/************************************************************************/ + +/** + * \brief Symmetrical difference of two layers. + * + * The result layer contains features whose geometries represent areas + * that are in either in the input layer or in the method layer but + * not in both. The features in the result layer have attributes from + * both input and method layers. For features which represent areas + * that are only in the input or in the method layer the respective + * attributes have undefined values. The schema of the result layer + * can be set by the user or, if it is empty, is initialized to + * contain all fields in the input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer (even if it is undefined). + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::SymDifference(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_SymDifference( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_SymDifference", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_SymDifference", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_SymDifference", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->SymDifference( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} + +/************************************************************************/ +/* Identity() */ +/************************************************************************/ + +/** + * \brief Identify the features of this layer with the ones from the + * identity layer. + * + * The result layer contains features whose geometries represent areas + * that are in the input layer. The features in the result layer have + * attributes from both input and method layers. The schema of the + * result layer can be set by the user or, if it is empty, is + * initialized to contain all fields in input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer (even if it is undefined). + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_Identity(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::Identity( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnMethod = pLayerMethod->GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + int *mapInput = NULL; + int *mapMethod = NULL; + double progress_max = (double) GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + // get resources + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnMethod, &mapMethod); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, poDefnMethod, mapInput, mapMethod, 1, papszOptions); + if (ret != OGRERR_NONE) goto done; + poDefnResult = pLayerResult->GetLayerDefn(); + + // split the features in input layer to the result layer + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *x_geom_diff = x_geom->clone(); // this will be the geometry of the result feature + pLayerMethod->ResetReading(); + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry* poIntersection = x_geom->Intersection(y_geom); + if( poIntersection == NULL || poIntersection->IsEmpty() || + (x_geom->getDimension() == 2 && + y_geom->getDimension() == 2 && + poIntersection->getDimension() < 2) ) + { + delete poIntersection; + delete y; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + z->SetFieldsFrom(y, mapMethod); + if( bPromoteToMulti ) + poIntersection = promote_to_multi(poIntersection); + z->SetGeometryDirectly(poIntersection); + OGRGeometry *x_geom_diff_new = x_geom_diff ? x_geom_diff->Difference(y_geom) : NULL; + if (x_geom_diff) delete x_geom_diff; + x_geom_diff = x_geom_diff_new; + delete y; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + delete x; + delete x_geom_diff; + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + + if( x_geom_diff == NULL || x_geom_diff->IsEmpty() ) + { + delete x_geom_diff; + delete x; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + if( bPromoteToMulti ) + x_geom_diff = promote_to_multi(x_geom_diff); + z->SetGeometryDirectly(x_geom_diff); + delete x; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (mapInput) VSIFree(mapInput); + if (mapMethod) VSIFree(mapMethod); + return ret; +} + +/************************************************************************/ +/* OGR_L_Identity() */ +/************************************************************************/ + +/** + * \brief Identify the features of this layer with the ones from the + * identity layer. + * + * The result layer contains features whose geometries represent areas + * that are in the input layer. The features in the result layer have + * attributes from both input and method layers. The schema of the + * result layer can be set by the user or, if it is empty, is + * initialized to contain all fields in input and method layers. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in input and in method + * layer, then the attribute in the result feature will get the value + * from the feature of the method layer (even if it is undefined). + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::Identity(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_Identity( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_Identity", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_Identity", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_Identity", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->Identity( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} + +/************************************************************************/ +/* Update() */ +/************************************************************************/ + +/** + * \brief Update this layer with features from the update layer. + * + * The result layer contains features whose geometries represent areas + * that are either in the input layer or in the method layer. The + * features in the result layer have areas of the features of the + * method layer or those ares of the features of the input layer that + * are not covered by the method layer. The features of the result + * layer get their attributes from the input layer. The schema of the + * result layer can be set by the user or, if it is empty, is + * initialized to contain all fields in the input layer. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in the method layer, then + * the attribute in the result feature the originates from the method + * layer will get the value from the feature of the method layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_Update(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::Update( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnMethod = pLayerMethod->GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + int *mapInput = NULL; + int *mapMethod = NULL; + double progress_max = (double) GetFeatureCount(0) + (double) pLayerMethod->GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + // get resources + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnMethod, &mapMethod); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, poDefnMethod, mapInput, mapMethod, 0, papszOptions); + if (ret != OGRERR_NONE) goto done; + poDefnResult = pLayerResult->GetLayerDefn(); + + // add clipped features from the input layer + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *x_geom_diff = x_geom->clone(); //this will be the geometry of a result feature + pLayerMethod->ResetReading(); + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRGeometry *x_geom_diff_new = x_geom_diff ? x_geom_diff->Difference(y_geom) : NULL; + if (x_geom_diff) delete x_geom_diff; + x_geom_diff = x_geom_diff_new; + delete y; + } + + if( x_geom_diff == NULL || x_geom_diff->IsEmpty() ) + { + delete x_geom_diff; + delete x; + } + else + { + OGRFeature *z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + if( bPromoteToMulti ) + x_geom_diff = promote_to_multi(x_geom_diff); + z->SetGeometryDirectly(x_geom_diff); + delete x; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + + // restore the original filter and add features from the update layer + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + pLayerMethod->ResetReading(); + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete y; + goto done; + } + } + progress_counter += 1.0; + } + + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + OGRFeature *z = new OGRFeature(poDefnResult); + if (mapMethod) z->SetFieldsFrom(y, mapMethod); + z->SetGeometry(y_geom); + delete y; + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (mapInput) VSIFree(mapInput); + if (mapMethod) VSIFree(mapMethod); + return ret; +} + +/************************************************************************/ +/* OGR_L_Update() */ +/************************************************************************/ + +/** + * \brief Update this layer with features from the update layer. + * + * The result layer contains features whose geometries represent areas + * that are either in the input layer or in the method layer. The + * features in the result layer have areas of the features of the + * method layer or those ares of the features of the input layer that + * are not covered by the method layer. The features of the result + * layer get their attributes from the input layer. The schema of the + * result layer can be set by the user or, if it is empty, is + * initialized to contain all fields in the input layer. + * + * \note If the schema of the result is set by user and contains + * fields that have the same name as a field in the method layer, then + * the attribute in the result feature the originates from the method + * layer will get the value from the feature of the method layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::Update(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_Update( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_Update", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_Update", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_Update", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->Update( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} + +/************************************************************************/ +/* Clip() */ +/************************************************************************/ + +/** + * \brief Clip off areas that are not covered by the method layer. + * + * The result layer contains features whose geometries represent areas + * that are in the input layer and in the method layer. The features + * in the result layer have the (possibly clipped) areas of features + * in the input layer and the attributes from the same features. The + * schema of the result layer can be set by the user or, if it is + * empty, is initialized to contain all fields in the input layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_Clip(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::Clip( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + int *mapInput = NULL; + double progress_max = (double) GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, NULL, mapInput, NULL, 0, papszOptions); + if (ret != OGRERR_NONE) goto done; + + poDefnResult = pLayerResult->GetLayerDefn(); + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *geom = NULL; // this will be the geometry of the result feature + pLayerMethod->ResetReading(); + // incrementally add area from y to geom + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + if (!geom) { + geom = y_geom->clone(); + } else { + OGRGeometry *geom_new = geom->Union(y_geom); + delete geom; + geom = geom_new; + } + delete y; + } + + // possibly add a new feature with area x intersection sum of y + OGRFeature *z = NULL; + if (geom) { + OGRGeometry* poIntersection = x_geom->Intersection(geom); + if( poIntersection != NULL && !poIntersection->IsEmpty() ) + { + z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + if( bPromoteToMulti ) + poIntersection = promote_to_multi(poIntersection); + z->SetGeometryDirectly(poIntersection); + } + else + delete poIntersection; + delete geom; + } + delete x; + if (z) { + if (z->GetGeometryRef() != NULL && !z->GetGeometryRef()->IsEmpty()) + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (mapInput) VSIFree(mapInput); + return ret; +} + +/************************************************************************/ +/* OGR_L_Clip() */ +/************************************************************************/ + +/** + * \brief Clip off areas that are not covered by the method layer. + * + * The result layer contains features whose geometries represent areas + * that are in the input layer and in the method layer. The features + * in the result layer have the (possibly clipped) areas of features + * in the input layer and the attributes from the same features. The + * schema of the result layer can be set by the user or, if it is + * empty, is initialized to contain all fields in the input layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::Clip(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_Clip( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_Clip", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_Clip", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_Clip", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->Clip( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} + +/************************************************************************/ +/* Erase() */ +/************************************************************************/ + +/** + * \brief Remove areas that are covered by the method layer. + * + * The result layer contains features whose geometries represent areas + * that are in the input layer but not in the method layer. The + * features in the result layer have attributes from the input + * layer. The schema of the result layer can be set by the user or, if + * it is empty, is initialized to contain all fields in the input + * layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This method is the same as the C function OGR_L_Erase(). + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGRLayer::Erase( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) +{ + OGRErr ret = OGRERR_NONE; + OGRFeatureDefn *poDefnInput = GetLayerDefn(); + OGRFeatureDefn *poDefnResult = NULL; + OGRGeometry *pGeometryMethodFilter = NULL; + int *mapInput = NULL; + double progress_max = (double) GetFeatureCount(0); + double progress_counter = 0; + double progress_ticker = 0; + int bSkipFailures = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "SKIP_FAILURES", "NO")); + int bPromoteToMulti = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "PROMOTE_TO_MULTI", "NO")); + + // check for GEOS + if (!OGRGeometryFactory::haveGEOS()) { + return OGRERR_UNSUPPORTED_OPERATION; + } + + // get resources + ret = clone_spatial_filter(pLayerMethod, &pGeometryMethodFilter); + if (ret != OGRERR_NONE) goto done; + ret = create_field_map(poDefnInput, &mapInput); + if (ret != OGRERR_NONE) goto done; + ret = set_result_schema(pLayerResult, poDefnInput, NULL, mapInput, NULL, 0, papszOptions); + if (ret != OGRERR_NONE) goto done; + poDefnResult = pLayerResult->GetLayerDefn(); + + ResetReading(); + while (OGRFeature *x = GetNextFeature()) { + + if (pfnProgress) { + double p = progress_counter/progress_max; + if (p > progress_ticker) { + if (!pfnProgress(p, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + delete x; + goto done; + } + } + progress_counter += 1.0; + } + + // set up the filter on the method layer + OGRGeometry *x_geom = set_filter_from(pLayerMethod, pGeometryMethodFilter, x); + if (!x_geom) { + delete x; + continue; + } + + OGRGeometry *geom = NULL; // this will be the geometry of the result feature + pLayerMethod->ResetReading(); + // incrementally add area from y to geom + while (OGRFeature *y = pLayerMethod->GetNextFeature()) { + OGRGeometry *y_geom = y->GetGeometryRef(); + if (!y_geom) {delete y; continue;} + if (!geom) { + geom = y_geom->clone(); + } else { + OGRGeometry *geom_new = geom->Union(y_geom); + delete geom; + geom = geom_new; + } + delete y; + } + + // possibly add a new feature with area x minus sum of y + OGRFeature *z = NULL; + if (geom) { + OGRGeometry* x_geom_diff = x_geom->Difference(geom); + if( x_geom_diff != NULL && !x_geom_diff->IsEmpty() ) + { + z = new OGRFeature(poDefnResult); + z->SetFieldsFrom(x, mapInput); + if( bPromoteToMulti ) + x_geom_diff = promote_to_multi(x_geom_diff); + z->SetGeometryDirectly(x_geom_diff); + } + else + delete x_geom_diff; + delete geom; + } + delete x; + if (z) { + if (z->GetGeometryRef() != NULL && !z->GetGeometryRef()->IsEmpty()) + ret = pLayerResult->CreateFeature(z); + delete z; + if (ret != OGRERR_NONE) { + if (!bSkipFailures) { + goto done; + } else { + CPLErrorReset(); + ret = OGRERR_NONE; + } + } + } + } + if (pfnProgress && !pfnProgress(1.0, "", pProgressArg)) { + CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated"); + ret = OGRERR_FAILURE; + goto done; + } +done: + // release resources + pLayerMethod->SetSpatialFilter(pGeometryMethodFilter); + if (pGeometryMethodFilter) delete pGeometryMethodFilter; + if (mapInput) VSIFree(mapInput); + return ret; +} + +/************************************************************************/ +/* OGR_L_Erase() */ +/************************************************************************/ + +/** + * \brief Remove areas that are covered by the method layer. + * + * The result layer contains features whose geometries represent areas + * that are in the input layer but not in the method layer. The + * features in the result layer have attributes from the input + * layer. The schema of the result layer can be set by the user or, if + * it is empty, is initialized to contain all fields in the input + * layer. + * + * \note For best performance use the minimum amount of features in + * the method layer and copy it into a memory layer. + * + * \note This method relies on GEOS support. Do not use unless the + * GEOS support is compiled in. + * + * The recognized list of options is : + *
      + *
    • SKIP_FAILURES=YES/NO. Set it to YES to go on, even when a + * feature could not be inserted. + *
    • PROMOTE_TO_MULTI=YES/NO. Set it to YES to convert Polygons + * into MultiPolygons, or LineStrings to MultiLineStrings. + *
    • INPUT_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the input layer. + *
    • METHOD_PREFIX=string. Set a prefix for the field names that + * will be created from the fields of the method layer. + *
    + * + * This function is the same as the C++ method OGRLayer::Erase(). + * + * @param pLayerInput the input layer. Should not be NULL. + * + * @param pLayerMethod the method layer. Should not be NULL. + * + * @param pLayerResult the layer where the features resulting from the + * operation are inserted. Should not be NULL. See above the note + * about the schema. + * + * @param papszOptions NULL terminated list of options (may be NULL). + * + * @param pfnProgress a GDALProgressFunc() compatible callback function for + * reporting progress or NULL. + * + * @param pProgressArg argument to be passed to pfnProgress. May be NULL. + * + * @return an error code if there was an error or the execution was + * interrupted, OGRERR_NONE otherwise. + * + * @since OGR 1.10 + */ + +OGRErr OGR_L_Erase( OGRLayerH pLayerInput, + OGRLayerH pLayerMethod, + OGRLayerH pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ) + +{ + VALIDATE_POINTER1( pLayerInput, "OGR_L_Erase", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerMethod, "OGR_L_Erase", OGRERR_INVALID_HANDLE ); + VALIDATE_POINTER1( pLayerResult, "OGR_L_Erase", OGRERR_INVALID_HANDLE ); + + return ((OGRLayer *)pLayerInput)->Erase( (OGRLayer *)pLayerMethod, (OGRLayer *)pLayerResult, papszOptions, pfnProgress, pProgressArg ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayerdecorator.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayerdecorator.cpp new file mode 100644 index 000000000..b697b3205 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayerdecorator.cpp @@ -0,0 +1,289 @@ +/****************************************************************************** + * $Id: ogrlayerdecorator.cpp 28601 2015-03-03 11:06:40Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRLayerDecorator class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrlayerdecorator.h" + +CPL_CVSID("$Id: ogrlayerdecorator.cpp 28601 2015-03-03 11:06:40Z rouault $"); + +OGRLayerDecorator::OGRLayerDecorator(OGRLayer* poDecoratedLayer, + int bTakeOwnership) : + m_poDecoratedLayer(poDecoratedLayer), + m_bHasOwnership(bTakeOwnership) +{ + SetDescription( poDecoratedLayer->GetDescription() ); + CPLAssert(poDecoratedLayer != NULL); +} + +OGRLayerDecorator::~OGRLayerDecorator() +{ + if( m_bHasOwnership ) + delete m_poDecoratedLayer; +} + + +OGRGeometry *OGRLayerDecorator::GetSpatialFilter() +{ + if( !m_poDecoratedLayer ) return NULL; + return m_poDecoratedLayer->GetSpatialFilter(); +} + +void OGRLayerDecorator::SetSpatialFilter( OGRGeometry * poGeom ) +{ + if( !m_poDecoratedLayer ) return; + m_poDecoratedLayer->SetSpatialFilter(poGeom); +} + +void OGRLayerDecorator::SetSpatialFilter( int iGeomField, OGRGeometry * poGeom ) +{ + if( !m_poDecoratedLayer ) return; + m_poDecoratedLayer->SetSpatialFilter(iGeomField, poGeom); +} + +void OGRLayerDecorator::SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) +{ + if( !m_poDecoratedLayer ) return; + m_poDecoratedLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY); +} + +void OGRLayerDecorator::SetSpatialFilterRect( int iGeomField, double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) +{ + if( !m_poDecoratedLayer ) return; + m_poDecoratedLayer->SetSpatialFilterRect(iGeomField, dfMinX, dfMinY, dfMaxX, dfMaxY); +} + +OGRErr OGRLayerDecorator::SetAttributeFilter( const char * poAttrFilter ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->SetAttributeFilter(poAttrFilter); +} + +void OGRLayerDecorator::ResetReading() +{ + if( !m_poDecoratedLayer ) return; + m_poDecoratedLayer->ResetReading(); +} + +OGRFeature *OGRLayerDecorator::GetNextFeature() +{ + if( !m_poDecoratedLayer ) return NULL; + return m_poDecoratedLayer->GetNextFeature(); +} + +OGRErr OGRLayerDecorator::SetNextByIndex( GIntBig nIndex ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->SetNextByIndex(nIndex); +} + +OGRFeature *OGRLayerDecorator::GetFeature( GIntBig nFID ) +{ + if( !m_poDecoratedLayer ) return NULL; + return m_poDecoratedLayer->GetFeature(nFID); +} + +OGRErr OGRLayerDecorator::ISetFeature( OGRFeature *poFeature ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->SetFeature(poFeature); +} + +OGRErr OGRLayerDecorator::ICreateFeature( OGRFeature *poFeature ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->CreateFeature(poFeature); +} + +OGRErr OGRLayerDecorator::DeleteFeature( GIntBig nFID ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->DeleteFeature(nFID); +} + +const char* OGRLayerDecorator::GetName() +{ + if( !m_poDecoratedLayer ) return GetDescription(); + return m_poDecoratedLayer->GetName(); +} + +OGRwkbGeometryType OGRLayerDecorator::GetGeomType() +{ + if( !m_poDecoratedLayer ) return wkbNone; + return m_poDecoratedLayer->GetGeomType(); +} + +OGRFeatureDefn *OGRLayerDecorator::GetLayerDefn() +{ + if( !m_poDecoratedLayer ) return NULL; + return m_poDecoratedLayer->GetLayerDefn(); +} + +OGRSpatialReference *OGRLayerDecorator::GetSpatialRef() +{ + if( !m_poDecoratedLayer ) return NULL; + return m_poDecoratedLayer->GetSpatialRef(); +} + +GIntBig OGRLayerDecorator::GetFeatureCount( int bForce ) +{ + if( !m_poDecoratedLayer ) return 0; + return m_poDecoratedLayer->GetFeatureCount(bForce); +} + +OGRErr OGRLayerDecorator::GetExtent(OGREnvelope *psExtent, int bForce) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->GetExtent(psExtent, bForce); +} + +OGRErr OGRLayerDecorator::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->GetExtent(iGeomField, psExtent, bForce); +} + +int OGRLayerDecorator::TestCapability( const char * pszCapability ) +{ + if( !m_poDecoratedLayer ) return FALSE; + return m_poDecoratedLayer->TestCapability(pszCapability); +} + +OGRErr OGRLayerDecorator::CreateField( OGRFieldDefn *poField, + int bApproxOK ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->CreateField(poField, bApproxOK); +} + +OGRErr OGRLayerDecorator::DeleteField( int iField ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->DeleteField(iField); +} + +OGRErr OGRLayerDecorator::ReorderFields( int* panMap ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->ReorderFields(panMap); +} + +OGRErr OGRLayerDecorator::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->AlterFieldDefn(iField, poNewFieldDefn, nFlags); +} + +OGRErr OGRLayerDecorator::SyncToDisk() +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->SyncToDisk(); +} + +OGRStyleTable *OGRLayerDecorator::GetStyleTable() +{ + if( !m_poDecoratedLayer ) return NULL; + return m_poDecoratedLayer->GetStyleTable(); +} + +void OGRLayerDecorator::SetStyleTableDirectly( OGRStyleTable *poStyleTable ) +{ + if( !m_poDecoratedLayer ) return; + m_poDecoratedLayer->SetStyleTableDirectly(poStyleTable); +} + +void OGRLayerDecorator::SetStyleTable(OGRStyleTable *poStyleTable) +{ + if( !m_poDecoratedLayer ) return; + m_poDecoratedLayer->SetStyleTable(poStyleTable); +} + +OGRErr OGRLayerDecorator::StartTransaction() +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->StartTransaction(); +} + +OGRErr OGRLayerDecorator::CommitTransaction() +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->CommitTransaction(); +} + +OGRErr OGRLayerDecorator::RollbackTransaction() +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->RollbackTransaction(); +} + +const char *OGRLayerDecorator::GetFIDColumn() +{ + if( !m_poDecoratedLayer ) return ""; + return m_poDecoratedLayer->GetFIDColumn(); +} + +const char *OGRLayerDecorator::GetGeometryColumn() +{ + if( !m_poDecoratedLayer ) return ""; + return m_poDecoratedLayer->GetGeometryColumn(); +} + +OGRErr OGRLayerDecorator::SetIgnoredFields( const char **papszFields ) +{ + if( !m_poDecoratedLayer ) return OGRERR_FAILURE; + return m_poDecoratedLayer->SetIgnoredFields(papszFields); +} + +char **OGRLayerDecorator::GetMetadata( const char * pszDomain ) +{ + if( !m_poDecoratedLayer ) return NULL; + return m_poDecoratedLayer->GetMetadata(pszDomain); +} + +CPLErr OGRLayerDecorator::SetMetadata( char ** papszMetadata, + const char * pszDomain ) +{ + if( !m_poDecoratedLayer ) return CE_Failure; + return m_poDecoratedLayer->SetMetadata(papszMetadata, pszDomain); +} + +const char *OGRLayerDecorator::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + if( !m_poDecoratedLayer ) return NULL; + return m_poDecoratedLayer->GetMetadataItem(pszName, pszDomain); +} + +CPLErr OGRLayerDecorator::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) +{ + if( !m_poDecoratedLayer ) return CE_Failure; + return m_poDecoratedLayer->SetMetadataItem(pszName, pszValue, pszDomain); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayerdecorator.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayerdecorator.h new file mode 100644 index 000000000..535bfcd6b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayerdecorator.h @@ -0,0 +1,111 @@ +/****************************************************************************** + * $Id: ogrlayerdecorator.h 28601 2015-03-03 11:06:40Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRLayerDecorator class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRLAYERDECORATOR_H_INCLUDED +#define _OGRLAYERDECORATOR_H_INCLUDED + +#include "ogrsf_frmts.h" + +class CPL_DLL OGRLayerDecorator : public OGRLayer +{ + protected: + OGRLayer *m_poDecoratedLayer; + int m_bHasOwnership; + + public: + + OGRLayerDecorator(OGRLayer* poDecoratedLayer, + int bTakeOwnership); + virtual ~OGRLayerDecorator(); + + virtual OGRGeometry *GetSpatialFilter(); + virtual void SetSpatialFilter( OGRGeometry * ); + virtual void SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + virtual void SetSpatialFilterRect( int iGeomField, double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + virtual OGRFeature *GetFeature( GIntBig nFID ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + virtual const char *GetName(); + virtual OGRwkbGeometryType GetGeomType(); + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr DeleteField( int iField ); + virtual OGRErr ReorderFields( int* panMap ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + + virtual OGRErr SyncToDisk(); + + virtual OGRStyleTable *GetStyleTable(); + virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable ); + + virtual void SetStyleTable(OGRStyleTable *poStyleTable); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual const char *GetFIDColumn(); + virtual const char *GetGeometryColumn(); + + virtual OGRErr SetIgnoredFields( const char **papszFields ); + + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + OGRLayer* GetBaseLayer() { return m_poDecoratedLayer; } +}; + +#endif // _OGRLAYERDECORATOR_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayerpool.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayerpool.cpp new file mode 100644 index 000000000..8dd789b2a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayerpool.cpp @@ -0,0 +1,580 @@ +/****************************************************************************** + * $Id: ogrlayerpool.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRLayerPool and OGRProxiedLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrlayerpool.h" + +CPL_CVSID("$Id: ogrlayerpool.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRAbstractProxiedLayer() */ +/************************************************************************/ + +OGRAbstractProxiedLayer::OGRAbstractProxiedLayer(OGRLayerPool* poPool) +{ + CPLAssert(poPool != NULL); + this->poPool = poPool; + poPrevLayer = NULL; + poNextLayer = NULL; +} + +/************************************************************************/ +/* ~OGRAbstractProxiedLayer() */ +/************************************************************************/ + +OGRAbstractProxiedLayer::~OGRAbstractProxiedLayer() +{ + /* Remove us from the list of LRU layers if necessary */ + poPool->UnchainLayer(this); +} + + + +/************************************************************************/ +/* OGRLayerPool() */ +/************************************************************************/ + +OGRLayerPool::OGRLayerPool(int nMaxSimultaneouslyOpened) +{ + poMRULayer = NULL; + poLRULayer = NULL; + nMRUListSize = 0; + this->nMaxSimultaneouslyOpened = nMaxSimultaneouslyOpened; +} + +/************************************************************************/ +/* ~OGRLayerPool() */ +/************************************************************************/ + +OGRLayerPool::~OGRLayerPool() +{ + CPLAssert( poMRULayer == NULL ); + CPLAssert( poLRULayer == NULL ); + CPLAssert( nMRUListSize == 0 ); +} + +/************************************************************************/ +/* SetLastUsedLayer() */ +/************************************************************************/ + +void OGRLayerPool::SetLastUsedLayer(OGRAbstractProxiedLayer* poLayer) +{ + /* If we are already the MRU layer, nothing to do */ + if (poLayer == poMRULayer) + return; + + //CPLDebug("OGR", "SetLastUsedLayer(%s)", poLayer->GetName()); + + if (poLayer->poPrevLayer != NULL || poLayer->poNextLayer != NULL) + { + /* Remove current layer from its current place in the list */ + UnchainLayer(poLayer); + } + else if (nMRUListSize == nMaxSimultaneouslyOpened) + { + /* If we have reached the maximum allowed number of layers */ + /* simultaneously opened, then close the LRU one that */ + /* was still active until now */ + CPLAssert(poLRULayer != NULL); + + poLRULayer->CloseUnderlyingLayer(); + UnchainLayer(poLRULayer); + } + + /* Put current layer on top of MRU list */ + CPLAssert(poLayer->poPrevLayer == NULL); + CPLAssert(poLayer->poNextLayer == NULL); + poLayer->poNextLayer = poMRULayer; + if (poMRULayer != NULL) + { + CPLAssert(poMRULayer->poPrevLayer == NULL); + poMRULayer->poPrevLayer = poLayer; + } + poMRULayer = poLayer; + if (poLRULayer == NULL) + poLRULayer = poLayer; + nMRUListSize ++; +} + +/************************************************************************/ +/* UnchainLayer() */ +/************************************************************************/ + +void OGRLayerPool::UnchainLayer(OGRAbstractProxiedLayer* poLayer) +{ + OGRAbstractProxiedLayer* poPrevLayer = poLayer->poPrevLayer; + OGRAbstractProxiedLayer* poNextLayer = poLayer->poNextLayer; + + CPLAssert(poPrevLayer == NULL || poPrevLayer->poNextLayer == poLayer); + CPLAssert(poNextLayer == NULL || poNextLayer->poPrevLayer == poLayer); + + if (poPrevLayer != NULL || poNextLayer != NULL || poLayer == poMRULayer) + nMRUListSize --; + + if (poLayer == poMRULayer) + poMRULayer = poNextLayer; + if (poLayer == poLRULayer) + poLRULayer = poPrevLayer; + if (poPrevLayer != NULL) + poPrevLayer->poNextLayer = poNextLayer; + if (poNextLayer != NULL) + poNextLayer->poPrevLayer = poPrevLayer; + poLayer->poPrevLayer = NULL; + poLayer->poNextLayer = NULL; +} + + + +/************************************************************************/ +/* OGRProxiedLayer() */ +/************************************************************************/ + +OGRProxiedLayer::OGRProxiedLayer(OGRLayerPool* poPool, + OpenLayerFunc pfnOpenLayer, + FreeUserDataFunc pfnFreeUserData, + void* pUserData) : OGRAbstractProxiedLayer(poPool) +{ + CPLAssert(pfnOpenLayer != NULL); + + this->pfnOpenLayer = pfnOpenLayer; + this->pfnFreeUserData = pfnFreeUserData; + this->pUserData = pUserData; + poUnderlyingLayer = NULL; + poFeatureDefn = NULL; + poSRS = NULL; +} + +/************************************************************************/ +/* ~OGRProxiedLayer() */ +/************************************************************************/ + +OGRProxiedLayer::~OGRProxiedLayer() +{ + delete poUnderlyingLayer; + + if( poSRS ) + poSRS->Release(); + + if( poFeatureDefn ) + poFeatureDefn->Release(); + + if( pfnFreeUserData != NULL ) + pfnFreeUserData(pUserData); +} + +/************************************************************************/ +/* OpenUnderlyingLayer() */ +/************************************************************************/ + +int OGRProxiedLayer::OpenUnderlyingLayer() +{ + CPLDebug("OGR", "OpenUnderlyingLayer(%p)", this); + CPLAssert(poUnderlyingLayer == NULL); + poPool->SetLastUsedLayer(this); + poUnderlyingLayer = pfnOpenLayer(pUserData); + if( poUnderlyingLayer == NULL ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Cannot open underlying layer"); + } + return poUnderlyingLayer != NULL; +} + +/************************************************************************/ +/* CloseUnderlyingLayer() */ +/************************************************************************/ + +void OGRProxiedLayer::CloseUnderlyingLayer() +{ + CPLDebug("OGR", "CloseUnderlyingLayer(%p)", this); + delete poUnderlyingLayer; + poUnderlyingLayer = NULL; +} + +/************************************************************************/ +/* GetUnderlyingLayer() */ +/************************************************************************/ + +OGRLayer* OGRProxiedLayer::GetUnderlyingLayer() +{ + if( poUnderlyingLayer == NULL ) + OpenUnderlyingLayer(); + return poUnderlyingLayer; +} + +/************************************************************************/ +/* GetSpatialFilter() */ +/************************************************************************/ + +OGRGeometry *OGRProxiedLayer::GetSpatialFilter() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return NULL; + return poUnderlyingLayer->GetSpatialFilter(); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRProxiedLayer::SetSpatialFilter( OGRGeometry * poGeom ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return; + poUnderlyingLayer->SetSpatialFilter(poGeom); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRProxiedLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeom ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return; + poUnderlyingLayer->SetSpatialFilter(iGeomField, poGeom); +} +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::SetAttributeFilter( const char * poAttrFilter ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->SetAttributeFilter(poAttrFilter); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRProxiedLayer::ResetReading() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return; + poUnderlyingLayer->ResetReading(); +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRProxiedLayer::GetNextFeature() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return NULL; + return poUnderlyingLayer->GetNextFeature(); +} + +/************************************************************************/ +/* SetNextByIndex() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::SetNextByIndex( GIntBig nIndex ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->SetNextByIndex(nIndex); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRProxiedLayer::GetFeature( GIntBig nFID ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return NULL; + return poUnderlyingLayer->GetFeature(nFID); +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::ISetFeature( OGRFeature *poFeature ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->SetFeature(poFeature); +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::ICreateFeature( OGRFeature *poFeature ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->CreateFeature(poFeature); +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::DeleteFeature( GIntBig nFID ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->DeleteFeature(nFID); +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRProxiedLayer::GetName() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return ""; + return poUnderlyingLayer->GetName(); +} + +/************************************************************************/ +/* GetGeomType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRProxiedLayer::GetGeomType() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return wkbUnknown; + return poUnderlyingLayer->GetGeomType(); +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn *OGRProxiedLayer::GetLayerDefn() +{ + if( poFeatureDefn != NULL ) + return poFeatureDefn; + + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) + { + poFeatureDefn = new OGRFeatureDefn(""); + } + else + { + poFeatureDefn = poUnderlyingLayer->GetLayerDefn(); + } + + poFeatureDefn->Reference(); + + return poFeatureDefn; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRProxiedLayer::GetSpatialRef() +{ + if( poSRS != NULL ) + return poSRS; + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return NULL; + OGRSpatialReference* poRet = poUnderlyingLayer->GetSpatialRef(); + if( poRet != NULL ) + { + poSRS = poRet; + poSRS->Reference(); + } + return poRet; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRProxiedLayer::GetFeatureCount( int bForce ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return 0; + return poUnderlyingLayer->GetFeatureCount(bForce); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->GetExtent(iGeomField, psExtent, bForce); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->GetExtent(psExtent, bForce); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRProxiedLayer::TestCapability( const char * pszCapability ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return FALSE; + return poUnderlyingLayer->TestCapability(pszCapability); +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::CreateField( OGRFieldDefn *poField, + int bApproxOK ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->CreateField(poField, bApproxOK); +} + +/************************************************************************/ +/* DeleteField() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::DeleteField( int iField ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->DeleteField(iField); +} + +/************************************************************************/ +/* ReorderFields() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::ReorderFields( int* panMap ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->ReorderFields(panMap); +} + +/************************************************************************/ +/* AlterFieldDefn() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->AlterFieldDefn(iField, poNewFieldDefn, nFlags); +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::SyncToDisk() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->SyncToDisk(); +} + +/************************************************************************/ +/* GetStyleTable() */ +/************************************************************************/ + +OGRStyleTable *OGRProxiedLayer::GetStyleTable() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return NULL; + return poUnderlyingLayer->GetStyleTable(); +} + +/************************************************************************/ +/* SetStyleTableDirectly() */ +/************************************************************************/ + +void OGRProxiedLayer::SetStyleTableDirectly( OGRStyleTable *poStyleTable ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return; + return poUnderlyingLayer->SetStyleTableDirectly(poStyleTable); +} + +/************************************************************************/ +/* SetStyleTable() */ +/************************************************************************/ + +void OGRProxiedLayer::SetStyleTable(OGRStyleTable *poStyleTable) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return; + return poUnderlyingLayer->SetStyleTable(poStyleTable); +} + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::StartTransaction() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->StartTransaction(); +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::CommitTransaction() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->CommitTransaction(); +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::RollbackTransaction() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->RollbackTransaction(); +} + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char *OGRProxiedLayer::GetFIDColumn() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return ""; + return poUnderlyingLayer->GetFIDColumn(); +} + +/************************************************************************/ +/* GetGeometryColumn() */ +/************************************************************************/ + +const char *OGRProxiedLayer::GetGeometryColumn() +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return ""; + return poUnderlyingLayer->GetGeometryColumn(); +} + +/************************************************************************/ +/* SetIgnoredFields() */ +/************************************************************************/ + +OGRErr OGRProxiedLayer::SetIgnoredFields( const char **papszFields ) +{ + if( poUnderlyingLayer == NULL && !OpenUnderlyingLayer() ) return OGRERR_FAILURE; + return poUnderlyingLayer->SetIgnoredFields(papszFields); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayerpool.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayerpool.h new file mode 100644 index 000000000..c6e013f0a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrlayerpool.h @@ -0,0 +1,162 @@ +/****************************************************************************** + * $Id: ogrlayerpool.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRLayerPool and OGRProxiedLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRLAYERPOOL_H_INCLUDED +#define _OGRLAYERPOOL_H_INCLUDED + +#include "ogrsf_frmts.h" + +typedef OGRLayer* (*OpenLayerFunc)(void* user_data); +typedef void (*FreeUserDataFunc)(void* user_data); + +class OGRLayerPool; + +/************************************************************************/ +/* OGRAbstractProxiedLayer */ +/************************************************************************/ + +class OGRAbstractProxiedLayer : public OGRLayer +{ + friend class OGRLayerPool; + + OGRAbstractProxiedLayer *poPrevLayer; /* Chain to a layer that was used more recently */ + OGRAbstractProxiedLayer *poNextLayer; /* Chain to a layer that was used less recently */ + + protected: + OGRLayerPool *poPool; + + virtual void CloseUnderlyingLayer() = 0; + + public: + OGRAbstractProxiedLayer(OGRLayerPool* poPool); + virtual ~OGRAbstractProxiedLayer(); +}; + +/************************************************************************/ +/* OGRLayerPool */ +/************************************************************************/ + +class OGRLayerPool +{ + protected: + OGRAbstractProxiedLayer *poMRULayer; /* the most recently used layer */ + OGRAbstractProxiedLayer *poLRULayer; /* the least recently used layer (still opened) */ + int nMRUListSize; /* the size of the list */ + int nMaxSimultaneouslyOpened; + + public: + OGRLayerPool(int nMaxSimultaneouslyOpened = 100); + ~OGRLayerPool(); + + void SetLastUsedLayer(OGRAbstractProxiedLayer* poProxiedLayer); + void UnchainLayer(OGRAbstractProxiedLayer* poProxiedLayer); + + int GetMaxSimultaneouslyOpened() const { return nMaxSimultaneouslyOpened; } + int GetSize() const { return nMRUListSize; } +}; + +/************************************************************************/ +/* OGRProxiedLayer */ +/************************************************************************/ + +class OGRProxiedLayer : public OGRAbstractProxiedLayer +{ + OpenLayerFunc pfnOpenLayer; + FreeUserDataFunc pfnFreeUserData; + void *pUserData; + OGRLayer *poUnderlyingLayer; + OGRFeatureDefn *poFeatureDefn; + OGRSpatialReference *poSRS; + + int OpenUnderlyingLayer(); + + protected: + + virtual void CloseUnderlyingLayer(); + + public: + + OGRProxiedLayer(OGRLayerPool* poPool, + OpenLayerFunc pfnOpenLayer, + FreeUserDataFunc pfnFreeUserData, + void* pUserData); + virtual ~OGRProxiedLayer(); + + OGRLayer *GetUnderlyingLayer(); + + virtual OGRGeometry *GetSpatialFilter(); + virtual void SetSpatialFilter( OGRGeometry * ); + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + virtual OGRFeature *GetFeature( GIntBig nFID ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + virtual const char *GetName(); + virtual OGRwkbGeometryType GetGeomType(); + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr DeleteField( int iField ); + virtual OGRErr ReorderFields( int* panMap ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + + virtual OGRErr SyncToDisk(); + + virtual OGRStyleTable *GetStyleTable(); + virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable ); + + virtual void SetStyleTable(OGRStyleTable *poStyleTable); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual const char *GetFIDColumn(); + virtual const char *GetGeometryColumn(); + + virtual OGRErr SetIgnoredFields( const char **papszFields ); +}; + +#endif // _OGRLAYERPOOL_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrmutexeddatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrmutexeddatasource.cpp new file mode 100644 index 000000000..f186424bd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrmutexeddatasource.cpp @@ -0,0 +1,242 @@ +/****************************************************************************** + * $Id: ogrmutexeddatasource.cpp 28602 2015-03-03 11:16:35Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRMutexedDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrmutexeddatasource.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: ogrmutexeddatasource.cpp 28602 2015-03-03 11:16:35Z rouault $"); + +OGRMutexedDataSource::OGRMutexedDataSource(OGRDataSource* poBaseDataSource, + int bTakeOwnership, + CPLMutex* hMutexIn, + int bWrapLayersInMutexedLayer) : + m_poBaseDataSource(poBaseDataSource), + m_bHasOwnership(bTakeOwnership), + m_hGlobalMutex(hMutexIn), + m_bWrapLayersInMutexedLayer(bWrapLayersInMutexedLayer) +{ +} + +OGRMutexedDataSource::~OGRMutexedDataSource() +{ + std::map::iterator oIter = m_oMapLayers.begin(); + for(; oIter != m_oMapLayers.end(); ++oIter ) + delete oIter->second; + + if( m_bHasOwnership ) + delete m_poBaseDataSource; +} + +const char *OGRMutexedDataSource::GetName() +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return m_poBaseDataSource->GetName(); +} + +int OGRMutexedDataSource::GetLayerCount() +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return m_poBaseDataSource->GetLayerCount(); +} + +OGRLayer* OGRMutexedDataSource::WrapLayerIfNecessary(OGRLayer* poLayer) +{ + if( poLayer && m_bWrapLayersInMutexedLayer ) + { + OGRLayer* poWrappedLayer = m_oMapLayers[poLayer]; + if( poWrappedLayer ) + poLayer = poWrappedLayer; + else + { + OGRMutexedLayer* poWrappedLayer = new OGRMutexedLayer(poLayer, FALSE, m_hGlobalMutex); + m_oMapLayers[poLayer] = poWrappedLayer; + m_oReverseMapLayers[poWrappedLayer] = poLayer; + poLayer = poWrappedLayer; + } + } + return poLayer; +} + +OGRLayer *OGRMutexedDataSource::GetLayer(int iIndex) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return WrapLayerIfNecessary(m_poBaseDataSource->GetLayer(iIndex)); +} + +OGRLayer *OGRMutexedDataSource::GetLayerByName(const char *pszName) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return WrapLayerIfNecessary(m_poBaseDataSource->GetLayerByName(pszName)); +} + +OGRErr OGRMutexedDataSource::DeleteLayer(int iIndex) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + OGRLayer* poLayer = m_bWrapLayersInMutexedLayer ? GetLayer(iIndex) : NULL; + OGRErr eErr = m_poBaseDataSource->DeleteLayer(iIndex); + if( eErr == OGRERR_NONE && poLayer) + { + std::map::iterator oIter = m_oMapLayers.find(poLayer); + if(oIter != m_oMapLayers.end()) + { + delete oIter->second; + m_oReverseMapLayers.erase(oIter->second); + m_oMapLayers.erase(oIter); + } + } + return eErr; +} + +int OGRMutexedDataSource::TestCapability( const char * pszCap ) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return m_poBaseDataSource->TestCapability(pszCap); +} + +OGRLayer *OGRMutexedDataSource::ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef, + OGRwkbGeometryType eGType, + char ** papszOptions) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return WrapLayerIfNecessary(m_poBaseDataSource->CreateLayer(pszName, poSpatialRef, eGType, papszOptions)); +} + +OGRLayer *OGRMutexedDataSource::CopyLayer( OGRLayer *poSrcLayer, + const char *pszNewName, + char **papszOptions ) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return WrapLayerIfNecessary(m_poBaseDataSource->CopyLayer(poSrcLayer, pszNewName, papszOptions )); +} + +OGRStyleTable *OGRMutexedDataSource::GetStyleTable() +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return m_poBaseDataSource->GetStyleTable(); +} + +void OGRMutexedDataSource::SetStyleTableDirectly( OGRStyleTable *poStyleTable ) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + m_poBaseDataSource->SetStyleTableDirectly(poStyleTable); +} + +void OGRMutexedDataSource::SetStyleTable(OGRStyleTable *poStyleTable) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + m_poBaseDataSource->SetStyleTable(poStyleTable); +} + +OGRLayer * OGRMutexedDataSource::ExecuteSQL( const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return WrapLayerIfNecessary(m_poBaseDataSource->ExecuteSQL(pszStatement, poSpatialFilter, + pszDialect)); +} + +void OGRMutexedDataSource::ReleaseResultSet( OGRLayer * poResultsSet ) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + if( poResultsSet && m_bWrapLayersInMutexedLayer ) + { + std::map::iterator oIter = m_oReverseMapLayers.find((OGRMutexedLayer*)poResultsSet); + CPLAssert(oIter != m_oReverseMapLayers.end()); + delete poResultsSet; + poResultsSet = oIter->second; + m_oMapLayers.erase(poResultsSet); + m_oReverseMapLayers.erase(oIter); + } + + m_poBaseDataSource->ReleaseResultSet(poResultsSet); +} + +void OGRMutexedDataSource::FlushCache() +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return m_poBaseDataSource->FlushCache(); +} + +OGRErr OGRMutexedDataSource::StartTransaction(int bForce) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return m_poBaseDataSource->StartTransaction(bForce); +} + +OGRErr OGRMutexedDataSource::CommitTransaction() +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return m_poBaseDataSource->CommitTransaction(); +} + +OGRErr OGRMutexedDataSource::RollbackTransaction() +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return m_poBaseDataSource->RollbackTransaction(); +} + +char **OGRMutexedDataSource::GetMetadata( const char * pszDomain ) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return m_poBaseDataSource->GetMetadata(pszDomain); +} + +CPLErr OGRMutexedDataSource::SetMetadata( char ** papszMetadata, + const char * pszDomain ) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return m_poBaseDataSource->SetMetadata(papszMetadata, pszDomain); +} + +const char *OGRMutexedDataSource::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return m_poBaseDataSource->GetMetadataItem(pszName, pszDomain); +} + +CPLErr OGRMutexedDataSource::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) +{ + CPLMutexHolderOptionalLockD(m_hGlobalMutex); + return m_poBaseDataSource->SetMetadataItem(pszName, pszValue, pszDomain); +} + +#if defined(WIN32) && defined(_MSC_VER) +// Horrible hack: for some reason MSVC doesn't export the class +// if it is not referenced from the DLL itself +void OGRRegisterMutexedDataSource(); +void OGRRegisterMutexedDataSource() +{ + delete new OGRMutexedDataSource(NULL, FALSE, NULL, FALSE); +} +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrmutexeddatasource.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrmutexeddatasource.h new file mode 100644 index 000000000..e3ab8e065 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrmutexeddatasource.h @@ -0,0 +1,113 @@ +/****************************************************************************** + * $Id: ogrmutexeddatasource.h 28601 2015-03-03 11:06:40Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRLMutexedDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRMUTEXEDDATASOURCELAYER_H_INCLUDED +#define _OGRMUTEXEDDATASOURCELAYER_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_multiproc.h" +#include "ogrmutexedlayer.h" +#include + +/** OGRMutexedDataSource class protects all virtual methods of OGRDataSource + * with a mutex. + * If the passed mutex is NULL, then no locking will be done. + * + * Note that the constructors and destructors are not explictely protected + * by the mutex* + */ +class CPL_DLL OGRMutexedDataSource : public OGRDataSource +{ + protected: + OGRDataSource *m_poBaseDataSource; + int m_bHasOwnership; + CPLMutex *m_hGlobalMutex; + int m_bWrapLayersInMutexedLayer; + std::map m_oMapLayers; + std::map m_oReverseMapLayers; + + OGRLayer* WrapLayerIfNecessary(OGRLayer* poLayer); + + public: + + /* The construction of the object isn't protected by the mutex */ + OGRMutexedDataSource(OGRDataSource* poBaseDataSource, + int bTakeOwnership, + CPLMutex* hMutexIn, + int bWrapLayersInMutexedLayer); + + /* The destruction of the object isn't protected by the mutex */ + virtual ~OGRMutexedDataSource(); + + OGRDataSource* GetBaseDataSource() { return m_poBaseDataSource; } + + virtual const char *GetName(); + + virtual int GetLayerCount() ; + virtual OGRLayer *GetLayer(int); + virtual OGRLayer *GetLayerByName(const char *); + virtual OGRErr DeleteLayer(int); + + virtual int TestCapability( const char * ); + + virtual OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + virtual OGRLayer *CopyLayer( OGRLayer *poSrcLayer, + const char *pszNewName, + char **papszOptions = NULL ); + + virtual OGRStyleTable *GetStyleTable(); + virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable ); + + virtual void SetStyleTable(OGRStyleTable *poStyleTable); + + virtual OGRLayer * ExecuteSQL( const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poResultsSet ); + + virtual void FlushCache(); + + virtual OGRErr StartTransaction(int bForce=FALSE); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); +}; + +#endif // _OGRMUTEXEDDATASOURCELAYER_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrmutexedlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrmutexedlayer.cpp new file mode 100644 index 000000000..a127ce2c6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrmutexedlayer.cpp @@ -0,0 +1,298 @@ +/****************************************************************************** + * $Id: ogrmutexedlayer.cpp 28601 2015-03-03 11:06:40Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRMutexedLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrmutexedlayer.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: ogrmutexedlayer.cpp 28601 2015-03-03 11:06:40Z rouault $"); + +OGRMutexedLayer::OGRMutexedLayer(OGRLayer* poDecoratedLayer, + int bTakeOwnership, + CPLMutex* hMutex) : + OGRLayerDecorator(poDecoratedLayer, bTakeOwnership), m_hMutex(hMutex) +{ + SetDescription( poDecoratedLayer->GetDescription() ); +} + +OGRMutexedLayer::~OGRMutexedLayer() +{ +} + + +OGRGeometry *OGRMutexedLayer::GetSpatialFilter() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetSpatialFilter(); +} + +void OGRMutexedLayer::SetSpatialFilter( OGRGeometry * poGeom ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + OGRLayerDecorator::SetSpatialFilter(poGeom); +} + +void OGRMutexedLayer::SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + OGRLayerDecorator::SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY); +} + +void OGRMutexedLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeom ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + OGRLayerDecorator::SetSpatialFilter(iGeomField, poGeom); +} + +void OGRMutexedLayer::SetSpatialFilterRect( int iGeomField, double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + OGRLayerDecorator::SetSpatialFilterRect(iGeomField, dfMinX, dfMinY, dfMaxX, dfMaxY); +} + +OGRErr OGRMutexedLayer::SetAttributeFilter( const char * poAttrFilter ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::SetAttributeFilter(poAttrFilter); +} + +void OGRMutexedLayer::ResetReading() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + OGRLayerDecorator::ResetReading(); +} + +OGRFeature *OGRMutexedLayer::GetNextFeature() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetNextFeature(); +} + +OGRErr OGRMutexedLayer::SetNextByIndex( GIntBig nIndex ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::SetNextByIndex(nIndex); +} + +OGRFeature *OGRMutexedLayer::GetFeature( GIntBig nFID ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetFeature(nFID); +} + +OGRErr OGRMutexedLayer::ISetFeature( OGRFeature *poFeature ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::ISetFeature(poFeature); +} + +OGRErr OGRMutexedLayer::ICreateFeature( OGRFeature *poFeature ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::ICreateFeature(poFeature); +} + +OGRErr OGRMutexedLayer::DeleteFeature( GIntBig nFID ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::DeleteFeature(nFID); +} + +const char *OGRMutexedLayer::GetName() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetName(); +} + +OGRwkbGeometryType OGRMutexedLayer::GetGeomType() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetGeomType(); +} + +OGRFeatureDefn *OGRMutexedLayer::GetLayerDefn() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetLayerDefn(); +} + +OGRSpatialReference *OGRMutexedLayer::GetSpatialRef() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetSpatialRef(); +} + +GIntBig OGRMutexedLayer::GetFeatureCount( int bForce ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetFeatureCount(bForce); +} + +OGRErr OGRMutexedLayer::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetExtent(iGeomField, psExtent, bForce); +} + +OGRErr OGRMutexedLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetExtent(psExtent, bForce); +} + +int OGRMutexedLayer::TestCapability( const char * pszCapability ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::TestCapability(pszCapability); +} + +OGRErr OGRMutexedLayer::CreateField( OGRFieldDefn *poField, + int bApproxOK ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::CreateField(poField, bApproxOK); +} + +OGRErr OGRMutexedLayer::DeleteField( int iField ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::DeleteField(iField); +} + +OGRErr OGRMutexedLayer::ReorderFields( int* panMap ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::ReorderFields(panMap); +} + +OGRErr OGRMutexedLayer::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::AlterFieldDefn(iField, poNewFieldDefn, nFlags); +} + +OGRErr OGRMutexedLayer::SyncToDisk() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::SyncToDisk(); +} + +OGRStyleTable *OGRMutexedLayer::GetStyleTable() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetStyleTable(); +} + +void OGRMutexedLayer::SetStyleTableDirectly( OGRStyleTable *poStyleTable ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::SetStyleTableDirectly(poStyleTable); +} + +void OGRMutexedLayer::SetStyleTable(OGRStyleTable *poStyleTable) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::SetStyleTable(poStyleTable); +} + +OGRErr OGRMutexedLayer::StartTransaction() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::StartTransaction(); +} + +OGRErr OGRMutexedLayer::CommitTransaction() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::CommitTransaction(); +} + +OGRErr OGRMutexedLayer::RollbackTransaction() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::RollbackTransaction(); +} + +const char *OGRMutexedLayer::GetFIDColumn() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetFIDColumn(); +} + +const char *OGRMutexedLayer::GetGeometryColumn() +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetGeometryColumn(); +} + +OGRErr OGRMutexedLayer::SetIgnoredFields( const char **papszFields ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::SetIgnoredFields(papszFields); +} + +char **OGRMutexedLayer::GetMetadata( const char * pszDomain ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetMetadata(pszDomain); +} + +CPLErr OGRMutexedLayer::SetMetadata( char ** papszMetadata, + const char * pszDomain ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::SetMetadata(papszMetadata, pszDomain); +} + +const char *OGRMutexedLayer::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::GetMetadataItem(pszName, pszDomain); +} + +CPLErr OGRMutexedLayer::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) +{ + CPLMutexHolderOptionalLockD(m_hMutex); + return OGRLayerDecorator::SetMetadataItem(pszName, pszValue, pszDomain); +} + +#if defined(WIN32) && defined(_MSC_VER) +// Horrible hack: for some reason MSVC doesn't export the class +// if it is not referenced from the DLL itself +void OGRRegisterMutexedLayer(); +void OGRRegisterMutexedLayer() +{ + CPLAssert(FALSE); // Never call this function: it will segfault + delete new OGRMutexedLayer(NULL, FALSE, NULL); +} +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrmutexedlayer.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrmutexedlayer.h new file mode 100644 index 000000000..451e8178a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrmutexedlayer.h @@ -0,0 +1,120 @@ +/****************************************************************************** + * $Id: ogrmutexedlayer.h 28601 2015-03-03 11:06:40Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRLMutexedLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRMUTEXEDLAYER_H_INCLUDED +#define _OGRMUTEXEDLAYER_H_INCLUDED + +#include "ogrlayerdecorator.h" +#include "cpl_multiproc.h" + +/** OGRMutexedLayer class protects all virtual methods of OGRLayer with a mutex. + * + * If the passed mutex is NULL, then no locking will be done. + * + * Note that the constructors and destructors are not explictely protected + * by the mutex. + */ +class CPL_DLL OGRMutexedLayer : public OGRLayerDecorator +{ + protected: + CPLMutex *m_hMutex; + + public: + + /* The construction of the object isn't protected by the mutex */ + OGRMutexedLayer(OGRLayer* poDecoratedLayer, + int bTakeOwnership, + CPLMutex* hMutex); + + /* The destruction of the object isn't protected by the mutex */ + virtual ~OGRMutexedLayer(); + + virtual OGRGeometry *GetSpatialFilter(); + virtual void SetSpatialFilter( OGRGeometry * ); + virtual void SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + virtual void SetSpatialFilterRect( int iGeomField, double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + virtual OGRFeature *GetFeature( GIntBig nFID ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + virtual const char *GetName(); + virtual OGRwkbGeometryType GetGeomType(); + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr DeleteField( int iField ); + virtual OGRErr ReorderFields( int* panMap ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + + virtual OGRErr SyncToDisk(); + + virtual OGRStyleTable *GetStyleTable(); + virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable ); + + virtual void SetStyleTable(OGRStyleTable *poStyleTable); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual const char *GetFIDColumn(); + virtual const char *GetGeometryColumn(); + + virtual OGRErr SetIgnoredFields( const char **papszFields ); + + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); +}; + +#endif // _OGRMUTEXEDLAYER_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrregisterall.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrregisterall.cpp new file mode 100644 index 000000000..e61cd1b21 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrregisterall.cpp @@ -0,0 +1,293 @@ +/****************************************************************************** + * $Id: ogrregisterall.cpp 29028 2015-04-26 21:19:29Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Function to register all known OGR drivers. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrsf_frmts.h" + +CPL_CVSID("$Id: ogrregisterall.cpp 29028 2015-04-26 21:19:29Z rouault $"); + +/************************************************************************/ +/* OGRRegisterAll() */ +/************************************************************************/ + +void OGRRegisterAll() +{ + GDALAllRegister(); +} + +void OGRRegisterAllInternal() +{ + +#ifdef SHAPE_ENABLED + RegisterOGRShape(); +#endif +#ifdef TAB_ENABLED + RegisterOGRTAB(); +#endif +#ifdef NTF_ENABLED + RegisterOGRNTF(); +#endif +#ifdef SDTS_ENABLED + RegisterOGRSDTS(); +#endif +#ifdef S57_ENABLED + RegisterOGRS57(); +#endif +#ifdef DGN_ENABLED + RegisterOGRDGN(); +#endif +#ifdef VRT_ENABLED + RegisterOGRVRT(); +#endif +#ifdef REC_ENABLED + RegisterOGRREC(); +#endif +#ifdef MEM_ENABLED + RegisterOGRMEM(); +#endif +#ifdef BNA_ENABLED + RegisterOGRBNA(); +#endif +#ifdef CSV_ENABLED + RegisterOGRCSV(); +#endif +#ifdef NAS_ENABLED + RegisterOGRNAS(); +#endif +#ifdef GML_ENABLED + RegisterOGRGML(); +#endif +#ifdef GPX_ENABLED + RegisterOGRGPX(); +#endif +#ifdef LIBKML_ENABLED + RegisterOGRLIBKML(); +#endif +#ifdef KML_ENABLED + RegisterOGRKML(); +#endif +#ifdef GEOJSON_ENABLED + RegisterOGRGeoJSON(); +#endif +#ifdef ILI_ENABLED + RegisterOGRILI1(); + RegisterOGRILI2(); +#endif +#ifdef GMT_ENABLED + RegisterOGRGMT(); +#endif +#ifdef SQLITE_ENABLED + RegisterOGRGeoPackage(); + RegisterOGRSQLite(); +#endif +#ifdef DODS_ENABLED + RegisterOGRDODS(); +#endif +#ifdef ODBC_ENABLED + RegisterOGRODBC(); +#endif +#ifdef WASP_ENABLED + RegisterOGRWAsP(); +#endif + +/* Register before PGeo and Geomedia drivers */ +/* that don't work well on Linux */ +#ifdef MDB_ENABLED + RegisterOGRMDB(); +#endif + +#ifdef PGEO_ENABLED + RegisterOGRPGeo(); +#endif +#ifdef MSSQLSPATIAL_ENABLED + RegisterOGRMSSQLSpatial(); +#endif +#ifdef OGDI_ENABLED + RegisterOGROGDI(); +#endif +#ifdef PG_ENABLED + RegisterOGRPG(); +#endif +#ifdef MYSQL_ENABLED + RegisterOGRMySQL(); +#endif +#ifdef OCI_ENABLED + RegisterOGROCI(); +#endif +#ifdef INGRES_ENABLED + RegisterOGRIngres(); +#endif +#ifdef SDE_ENABLED + RegisterOGRSDE(); +#endif +/* Register OpenFileGDB before FGDB as it is more capable for read-only */ +#ifdef OPENFILEGDB_ENABLED + RegisterOGROpenFileGDB(); +#endif +#ifdef FGDB_ENABLED + RegisterOGRFileGDB(); +#endif +#ifdef XPLANE_ENABLED + RegisterOGRXPlane(); +#endif +#ifdef DWGDIRECT_ENABLED + RegisterOGRDXFDWG(); +#endif +#ifdef DXF_ENABLED + RegisterOGRDXF(); +#endif +#ifdef GRASS_ENABLED + RegisterOGRGRASS(); +#endif +#ifdef FME_ENABLED + RegisterOGRFME(); +#endif +#ifdef IDB_ENABLED + RegisterOGRIDB(); +#endif +#ifdef GEOCONCEPT_ENABLED + RegisterOGRGeoconcept(); +#endif +#ifdef GEORSS_ENABLED + RegisterOGRGeoRSS(); +#endif +#ifdef GTM_ENABLED + RegisterOGRGTM(); +#endif +#ifdef VFK_ENABLED + RegisterOGRVFK(); +#endif +#ifdef PGDUMP_ENABLED + RegisterOGRPGDump(); +#endif +#ifdef OSM_ENABLED + /* Register before GPSBabel, that could recognize .osm file too */ + RegisterOGROSM(); +#endif +#ifdef GPSBABEL_ENABLED + RegisterOGRGPSBabel(); +#endif +#ifdef SUA_ENABLED + RegisterOGRSUA(); +#endif +#ifdef OPENAIR_ENABLED + RegisterOGROpenAir(); +#endif +#ifdef PDS_ENABLED + RegisterOGRPDS(); +#endif +#ifdef WFS_ENABLED + RegisterOGRWFS(); +#endif +#ifdef SOSI_ENABLED + RegisterOGRSOSI(); +#endif +#ifdef HTF_ENABLED + RegisterOGRHTF(); +#endif +#ifdef AERONAVFAA_ENABLED + RegisterOGRAeronavFAA(); +#endif +#ifdef GEOMEDIA_ENABLED + RegisterOGRGeomedia(); +#endif +#ifdef EDIGEO_ENABLED + RegisterOGREDIGEO(); +#endif +#ifdef GFT_ENABLED + RegisterOGRGFT(); +#endif +#ifdef GME_ENABLED + RegisterOGRGME(); +#endif +#ifdef SVG_ENABLED + RegisterOGRSVG(); +#endif +#ifdef COUCHDB_ENABLED + RegisterOGRCouchDB(); +#endif +#ifdef CLOUDANT_ENABLED + RegisterOGRCloudant(); +#endif +#ifdef IDRISI_ENABLED + RegisterOGRIdrisi(); +#endif +#ifdef ARCGEN_ENABLED + RegisterOGRARCGEN(); +#endif +#ifdef SEGUKOOA_ENABLED + RegisterOGRSEGUKOOA(); +#endif +#ifdef SEGY_ENABLED + RegisterOGRSEGY(); +#endif +#ifdef FREEXL_ENABLED + RegisterOGRXLS(); +#endif +#ifdef ODS_ENABLED + RegisterOGRODS(); +#endif +#ifdef XLSX_ENABLED + RegisterOGRXLSX(); +#endif +#ifdef ELASTIC_ENABLED + RegisterOGRElastic(); +#endif +#ifdef WALK_ENABLED + RegisterOGRWalk(); +#endif +#ifdef CARTODB_ENABLED + RegisterOGRCartoDB(); +#endif +#ifdef SXF_ENABLED + RegisterOGRSXF(); +#endif +#ifdef SELAFIN_ENABLED + RegisterOGRSelafin(); +#endif +#ifdef JML_ENABLED + RegisterOGRJML(); +#endif +#ifdef PLSCENES_ENABLED + RegisterOGRPLSCENES(); +#endif +#ifdef CSW_ENABLED + RegisterOGRCSW(); +#endif + +/* Put TIGER and AVCBIN at end since they need poOpenInfo->GetSiblingFiles() */ +#ifdef TIGER_ENABLED + RegisterOGRTiger(); +#endif +#ifdef AVCBIN_ENABLED + RegisterOGRAVCBin(); + RegisterOGRAVCE00(); +#endif + +} /* OGRRegisterAll */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrsfdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrsfdriver.cpp new file mode 100644 index 000000000..5d3275f30 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrsfdriver.cpp @@ -0,0 +1,230 @@ +/****************************************************************************** + * $Id: ogrsfdriver.cpp 27698 2014-09-20 11:59:07Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The generic portions of the OGRSFDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrsf_frmts.h" +#include "ogr_api.h" +#include "ogr_p.h" +#include "ograpispy.h" + +CPL_CVSID("$Id: ogrsfdriver.cpp 27698 2014-09-20 11:59:07Z rouault $"); + +/************************************************************************/ +/* ~OGRSFDriver() */ +/************************************************************************/ + +OGRSFDriver::~OGRSFDriver() + +{ +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRSFDriver::CreateDataSource( const char *, char ** ) + +{ + CPLError( CE_Failure, CPLE_NotSupported, + "CreateDataSource() not supported by this driver.\n" ); + + return NULL; +} + +/************************************************************************/ +/* OGR_Dr_CreateDataSource() */ +/************************************************************************/ + +OGRDataSourceH OGR_Dr_CreateDataSource( OGRSFDriverH hDriver, + const char *pszName, + char ** papszOptions ) + +{ + VALIDATE_POINTER1( hDriver, "OGR_Dr_CreateDataSource", NULL ); + + GDALDriver* poDriver = (GDALDriver*)hDriver; + + /* MapServer had the bad habit of calling with NULL name for a memory datasource */ + if( pszName == NULL ) + pszName = ""; + + OGRDataSourceH hDS = (OGRDataSourceH) poDriver->Create( pszName, 0, 0, 0, GDT_Unknown, papszOptions ); + +#ifdef OGRAPISPY_ENABLED + OGRAPISpyCreateDataSource(hDriver, pszName, papszOptions, (OGRDataSourceH) hDS); +#endif + + return hDS; +} + +/************************************************************************/ +/* DeleteDataSource() */ +/************************************************************************/ + +OGRErr OGRSFDriver::DeleteDataSource( const char *pszDataSource ) + +{ + (void) pszDataSource; + CPLError( CE_Failure, CPLE_NotSupported, + "DeleteDataSource() not supported by this driver." ); + + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* OGR_Dr_DeleteDataSource() */ +/************************************************************************/ + +OGRErr OGR_Dr_DeleteDataSource( OGRSFDriverH hDriver, + const char *pszDataSource ) + +{ + VALIDATE_POINTER1( hDriver, "OGR_Dr_DeleteDataSource", + OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + OGRAPISpyDeleteDataSource(hDriver, pszDataSource); +#endif + + return ((GDALDriver *) hDriver)->Delete( pszDataSource ); +} + +/************************************************************************/ +/* OGR_Dr_GetName() */ +/************************************************************************/ + +const char *OGR_Dr_GetName( OGRSFDriverH hDriver ) + +{ + VALIDATE_POINTER1( hDriver, "OGR_Dr_GetName", NULL ); + + return ((GDALDriver*)hDriver)->GetDescription(); +} + +/************************************************************************/ +/* OGR_Dr_Open() */ +/************************************************************************/ + +OGRDataSourceH OGR_Dr_Open( OGRSFDriverH hDriver, const char *pszName, + int bUpdate ) + +{ + VALIDATE_POINTER1( hDriver, "OGR_Dr_Open", NULL ); + + const char* const apszDrivers[] = { ((GDALDriver*)hDriver)->GetDescription(), + NULL }; + +#ifdef OGRAPISPY_ENABLED + int iSnapshot = OGRAPISpyOpenTakeSnapshot(pszName, bUpdate); +#endif + + GDALDatasetH hDS = GDALOpenEx(pszName, + GDAL_OF_VECTOR | + ((bUpdate) ? GDAL_OF_UPDATE: 0), + apszDrivers, NULL, NULL); + +#ifdef OGRAPISPY_ENABLED + OGRAPISpyOpen(pszName, bUpdate, iSnapshot, &hDS); +#endif + + return (OGRDataSourceH) hDS; +} + +/************************************************************************/ +/* OGR_Dr_TestCapability() */ +/************************************************************************/ + +int OGR_Dr_TestCapability( OGRSFDriverH hDriver, const char *pszCap ) + +{ + VALIDATE_POINTER1( hDriver, "OGR_Dr_TestCapability", 0 ); + VALIDATE_POINTER1( pszCap, "OGR_Dr_TestCapability", 0 ); + + GDALDriver* poDriver = (GDALDriver *) hDriver; + if( EQUAL(pszCap, ODrCCreateDataSource) ) + { + return poDriver->pfnCreate != NULL || + poDriver->pfnCreateVectorOnly != NULL; + } + else if( EQUAL(pszCap, ODrCDeleteDataSource) ) + { + return poDriver->pfnDelete != NULL || + poDriver->pfnDeleteDataSource != NULL; + } + else + return FALSE; +} + +/************************************************************************/ +/* OGR_Dr_CopyDataSource() */ +/************************************************************************/ + +OGRDataSourceH OGR_Dr_CopyDataSource( OGRSFDriverH hDriver, + OGRDataSourceH hSrcDS, + const char *pszNewName, + char **papszOptions ) + +{ + VALIDATE_POINTER1( hDriver, "OGR_Dr_CopyDataSource", NULL ); + VALIDATE_POINTER1( hSrcDS, "OGR_Dr_CopyDataSource", NULL ); + VALIDATE_POINTER1( pszNewName, "OGR_Dr_CopyDataSource", NULL ); + + GDALDriver* poDriver = (GDALDriver*)hDriver; + if( !poDriver->GetMetadataItem( GDAL_DCAP_CREATE ) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "%s driver does not support data source creation.", + poDriver->GetDescription() ); + return NULL; + } + + GDALDataset *poSrcDS = (GDALDataset*) hSrcDS; + GDALDataset *poODS; + + poODS = poDriver->Create( pszNewName, 0, 0, 0, GDT_Unknown, papszOptions ); + if( poODS == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Process each data source layer. */ +/* -------------------------------------------------------------------- */ + for( int iLayer = 0; iLayer < poSrcDS->GetLayerCount(); iLayer++ ) + { + OGRLayer *poLayer = poSrcDS->GetLayer(iLayer); + + if( poLayer == NULL ) + continue; + + poODS->CopyLayer( poLayer, poLayer->GetLayerDefn()->GetName(), + papszOptions ); + } + + return (OGRDataSourceH)poODS; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrsfdriverregistrar.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrsfdriverregistrar.cpp new file mode 100644 index 000000000..7173a2461 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrsfdriverregistrar.cpp @@ -0,0 +1,416 @@ +/****************************************************************************** + * $Id: ogrsfdriverregistrar.cpp 28806 2015-03-28 14:37:47Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRSFDriverRegistrar class implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrsf_frmts.h" +#include "ogr_api.h" +#include "ograpispy.h" + +CPL_CVSID("$Id: ogrsfdriverregistrar.cpp 28806 2015-03-28 14:37:47Z rouault $"); + +/************************************************************************/ +/* OGRSFDriverRegistrar */ +/************************************************************************/ + +/** + * \brief Constructor + * + * Normally the driver registrar is constucted by the + * OGRSFDriverRegistrar::GetRegistrar() accessor which ensures singleton + * status. + */ + +OGRSFDriverRegistrar::OGRSFDriverRegistrar() + +{ +} + +/************************************************************************/ +/* ~OGRSFDriverRegistrar() */ +/************************************************************************/ + +OGRSFDriverRegistrar::~OGRSFDriverRegistrar() + +{ +} + +/************************************************************************/ +/* GetRegistrar() */ +/************************************************************************/ + + +OGRSFDriverRegistrar *OGRSFDriverRegistrar::GetRegistrar() +{ + static OGRSFDriverRegistrar oSingleton; + return &oSingleton; +} + +/************************************************************************/ +/* OGRCleanupAll() */ +/************************************************************************/ + +#if defined(WIN32) && defined(_MSC_VER) +#include "ogremulatedtransaction.h" +void OGRRegisterMutexedDataSource(); +void OGRRegisterMutexedLayer(); +int OGRwillNeverBeTrue = FALSE; +#endif + +/** + * \brief Cleanup all OGR related resources. + * + * FIXME + */ +void OGRCleanupAll() + +{ + GDALDestroyDriverManager(); +#if defined(WIN32) && defined(_MSC_VER) +// Horrible hack: for some reason MSVC doesn't export those classes&symbols +// if they are not referenced from the DLL itself + if(OGRwillNeverBeTrue) + { + OGRRegisterMutexedDataSource(); + OGRRegisterMutexedLayer(); + OGRCreateEmulatedTransactionDataSourceWrapper(NULL,NULL,FALSE,FALSE); + } +#endif +} + +/************************************************************************/ +/* OGROpen() */ +/************************************************************************/ + +OGRDataSourceH OGROpen( const char *pszName, int bUpdate, + OGRSFDriverH *pahDriverList ) + +{ + VALIDATE_POINTER1( pszName, "OGROpen", NULL ); + +#ifdef OGRAPISPY_ENABLED + int iSnapshot = OGRAPISpyOpenTakeSnapshot(pszName, bUpdate); +#endif + + GDALDatasetH hDS = GDALOpenEx(pszName, GDAL_OF_VECTOR | + ((bUpdate) ? GDAL_OF_UPDATE: 0), NULL, NULL, NULL); + if( hDS != NULL && pahDriverList != NULL ) + *pahDriverList = (OGRSFDriverH) GDALGetDatasetDriver(hDS); + +#ifdef OGRAPISPY_ENABLED + OGRAPISpyOpen(pszName, bUpdate, iSnapshot, &hDS); +#endif + + return (OGRDataSourceH) hDS; +} + +/************************************************************************/ +/* OGROpenShared() */ +/************************************************************************/ + +OGRDataSourceH OGROpenShared( const char *pszName, int bUpdate, + OGRSFDriverH *pahDriverList ) + +{ + VALIDATE_POINTER1( pszName, "OGROpenShared", NULL ); + + GDALDatasetH hDS = GDALOpenEx(pszName, GDAL_OF_VECTOR | + ((bUpdate) ? GDAL_OF_UPDATE: 0) | GDAL_OF_SHARED, NULL, NULL, NULL); + if( hDS != NULL && pahDriverList != NULL ) + *pahDriverList = (OGRSFDriverH) GDALGetDatasetDriver(hDS); + return (OGRDataSourceH) hDS; +} + +/************************************************************************/ +/* OGRReleaseDataSource() */ +/************************************************************************/ + +OGRErr OGRReleaseDataSource( OGRDataSourceH hDS ) + +{ + VALIDATE_POINTER1( hDS, "OGRReleaseDataSource", OGRERR_INVALID_HANDLE ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpyPreClose(hDS); +#endif + GDALClose( (GDALDatasetH) hDS ); + +#ifdef OGRAPISPY_ENABLED + if( bOGRAPISpyEnabled ) + OGRAPISpyPostClose(hDS); +#endif + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetOpenDSCount() */ +/************************************************************************/ + +int OGRSFDriverRegistrar::GetOpenDSCount() +{ + CPLError(CE_Failure, CPLE_AppDefined, "Stub implementation in GDAL 2.0"); + return 0; +} + + +/************************************************************************/ +/* OGRGetOpenDSCount() */ +/************************************************************************/ + +int OGRGetOpenDSCount() + +{ + return OGRSFDriverRegistrar::GetRegistrar()->GetOpenDSCount(); +} + +/************************************************************************/ +/* GetOpenDS() */ +/************************************************************************/ + +OGRDataSource *OGRSFDriverRegistrar::GetOpenDS( CPL_UNUSED int iDS ) +{ + CPLError(CE_Failure, CPLE_AppDefined, "Stub implementation in GDAL 2.0"); + return NULL; +} + +/************************************************************************/ +/* OGRGetOpenDS() */ +/************************************************************************/ + +OGRDataSourceH OGRGetOpenDS( int iDS ) + +{ + return (OGRDataSourceH) OGRSFDriverRegistrar::GetRegistrar()->GetOpenDS( iDS ); +} + +/************************************************************************/ +/* OpenWithDriverArg() */ +/************************************************************************/ + +GDALDataset* OGRSFDriverRegistrar::OpenWithDriverArg(GDALDriver* poDriver, + GDALOpenInfo* poOpenInfo) +{ + OGRDataSource* poDS = (OGRDataSource*) + ((OGRSFDriver*)poDriver)->Open(poOpenInfo->pszFilename, + poOpenInfo->eAccess == GA_Update); + if( poDS != NULL ) + poDS->SetDescription( poDS->GetName() ); + return poDS; +} + +/************************************************************************/ +/* CreateVectorOnly() */ +/************************************************************************/ + +GDALDataset* OGRSFDriverRegistrar::CreateVectorOnly( GDALDriver* poDriver, + const char * pszName, + char ** papszOptions ) +{ + OGRDataSource* poDS = (OGRDataSource*) + ((OGRSFDriver*)poDriver)->CreateDataSource(pszName, papszOptions); + if( poDS != NULL && poDS->GetName() != NULL ) + poDS->SetDescription( poDS->GetName() ); + return poDS; +} + +/************************************************************************/ +/* DeleteDataSource() */ +/************************************************************************/ + +CPLErr OGRSFDriverRegistrar::DeleteDataSource( GDALDriver* poDriver, + const char * pszName ) +{ + if( ((OGRSFDriver*)poDriver)->DeleteDataSource(pszName) == OGRERR_NONE ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* RegisterDriver() */ +/************************************************************************/ + +void OGRSFDriverRegistrar::RegisterDriver( OGRSFDriver * poDriver ) + +{ + GDALDriver* poGDALDriver = (GDALDriver*) GDALGetDriverByName( poDriver->GetName() ) ; + if( poGDALDriver == NULL) + { + poDriver->SetDescription( poDriver->GetName() ); + poDriver->SetMetadataItem("OGR_DRIVER", "YES"); + + if( poDriver->GetMetadataItem(GDAL_DMD_LONGNAME) == NULL ) + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, poDriver->GetName() ); + + poDriver->pfnOpenWithDriverArg = OpenWithDriverArg; + + if( poDriver->TestCapability(ODrCCreateDataSource) ) + { + poDriver->SetMetadataItem( GDAL_DCAP_CREATE, "YES" ); + poDriver->pfnCreateVectorOnly = CreateVectorOnly; + } + if( poDriver->TestCapability(ODrCDeleteDataSource) ) + { + poDriver->pfnDeleteDataSource = DeleteDataSource; + } + + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } + else + { + if( poGDALDriver->GetMetadataItem("OGR_DRIVER") == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "A non OGR driver is registered with the same name: %s", poDriver->GetName()); + } + delete poDriver; + } +} + +/************************************************************************/ +/* OGRRegisterDriver() */ +/************************************************************************/ + +void OGRRegisterDriver( OGRSFDriverH hDriver ) + +{ + VALIDATE_POINTER0( hDriver, "OGRRegisterDriver" ); + + GetGDALDriverManager()->RegisterDriver( (GDALDriver*)hDriver ); +} + +/************************************************************************/ +/* OGRDeregisterDriver() */ +/************************************************************************/ + +void OGRDeregisterDriver( OGRSFDriverH hDriver ) + +{ + VALIDATE_POINTER0( hDriver, "OGRDeregisterDriver" ); + + GetGDALDriverManager()->DeregisterDriver( (GDALDriver*)hDriver ); +} + +/************************************************************************/ +/* GetDriverCount() */ +/************************************************************************/ + +int OGRSFDriverRegistrar::GetDriverCount() + +{ + /* We must be careful only to return drivers that are actual OGRSFDriver* */ + GDALDriverManager* poDriverManager = GetGDALDriverManager(); + int nTotal = poDriverManager->GetDriverCount(); + int nOGRDriverCount = 0; + for(int i=0;iGetDriver(i); + if( poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) != NULL ) + nOGRDriverCount ++; + } + return nOGRDriverCount; +} + +/************************************************************************/ +/* OGRGetDriverCount() */ +/************************************************************************/ + +int OGRGetDriverCount() + +{ + return OGRSFDriverRegistrar::GetRegistrar()->GetDriverCount(); +} + +/************************************************************************/ +/* GetDriver() */ +/************************************************************************/ + +GDALDriver *OGRSFDriverRegistrar::GetDriver( int iDriver ) + +{ + /* We must be careful only to return drivers that are actual OGRSFDriver* */ + GDALDriverManager* poDriverManager = GetGDALDriverManager(); + int nTotal = poDriverManager->GetDriverCount(); + int nOGRDriverCount = 0; + for(int i=0;iGetDriver(i); + if( poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) != NULL ) + { + if( nOGRDriverCount == iDriver ) + return poDriver; + nOGRDriverCount ++; + } + } + return NULL; +} + +/************************************************************************/ +/* OGRGetDriver() */ +/************************************************************************/ + +OGRSFDriverH OGRGetDriver( int iDriver ) + +{ + return (OGRSFDriverH) OGRSFDriverRegistrar::GetRegistrar()->GetDriver( iDriver ); +} + +/************************************************************************/ +/* GetDriverByName() */ +/************************************************************************/ + +GDALDriver *OGRSFDriverRegistrar::GetDriverByName( const char * pszName ) + +{ + GDALDriverManager* poDriverManager = GetGDALDriverManager(); + GDALDriver* poGDALDriver = + poDriverManager->GetDriverByName(CPLSPrintf("OGR_%s", pszName)); + if( poGDALDriver == NULL ) + poGDALDriver = poDriverManager->GetDriverByName(pszName); + if( poGDALDriver == NULL || + poGDALDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == NULL ) + return NULL; + return poGDALDriver; +} + +/************************************************************************/ +/* OGRGetDriverByName() */ +/************************************************************************/ + +OGRSFDriverH OGRGetDriverByName( const char *pszName ) + +{ + VALIDATE_POINTER1( pszName, "OGRGetDriverByName", NULL ); + + return (OGRSFDriverH) + OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName( pszName ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrunionlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrunionlayer.cpp new file mode 100644 index 000000000..5f1b1991d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrunionlayer.cpp @@ -0,0 +1,1286 @@ +/****************************************************************************** + * $Id: ogrunionlayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRUnionLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrunionlayer.h" +#include "ogrwarpedlayer.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrunionlayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRUnionLayerGeomFieldDefn() */ +/************************************************************************/ + +OGRUnionLayerGeomFieldDefn::OGRUnionLayerGeomFieldDefn(const char* pszName, + OGRwkbGeometryType eType) : + OGRGeomFieldDefn(pszName, eType), bGeomTypeSet(FALSE), bSRSSet(FALSE) +{ +} + +/************************************************************************/ +/* OGRUnionLayerGeomFieldDefn() */ +/************************************************************************/ + +OGRUnionLayerGeomFieldDefn::OGRUnionLayerGeomFieldDefn( + OGRGeomFieldDefn* poSrc) : + OGRGeomFieldDefn(poSrc->GetNameRef(), poSrc->GetType()), + bGeomTypeSet(FALSE), bSRSSet(FALSE) +{ + SetSpatialRef(poSrc->GetSpatialRef()); +} + +/************************************************************************/ +/* OGRUnionLayerGeomFieldDefn() */ +/************************************************************************/ + +OGRUnionLayerGeomFieldDefn::OGRUnionLayerGeomFieldDefn( + OGRUnionLayerGeomFieldDefn* poSrc) : + OGRGeomFieldDefn(poSrc->GetNameRef(), poSrc->GetType()), + bGeomTypeSet(poSrc->bGeomTypeSet), bSRSSet(poSrc->bSRSSet) +{ + SetSpatialRef(poSrc->GetSpatialRef()); + sStaticEnvelope = poSrc->sStaticEnvelope; +} + +/************************************************************************/ +/* ~OGRUnionLayerGeomFieldDefn() */ +/************************************************************************/ + +OGRUnionLayerGeomFieldDefn::~OGRUnionLayerGeomFieldDefn() +{ +} + +/************************************************************************/ +/* OGRUnionLayer() */ +/************************************************************************/ + +OGRUnionLayer::OGRUnionLayer( const char* pszName, + int nSrcLayersIn, + OGRLayer** papoSrcLayersIn, + int bTakeLayerOwnership ) +{ + CPLAssert(nSrcLayersIn > 0); + + SetDescription( pszName ); + + osName = pszName; + nSrcLayers = nSrcLayersIn; + papoSrcLayers = papoSrcLayersIn; + bHasLayerOwnership = bTakeLayerOwnership; + + poFeatureDefn = NULL; + nFields = 0; + papoFields = NULL; + nGeomFields = 0; + papoGeomFields = NULL; + eFieldStrategy = FIELD_UNION_ALL_LAYERS; + + bPreserveSrcFID = FALSE; + + nFeatureCount = -1; + + iCurLayer = -1; + pszAttributeFilter = NULL; + nNextFID = 0; + panMap = NULL; + papszIgnoredFields = NULL; + bAttrFilterPassThroughValue = -1; + poGlobalSRS = NULL; + + pabModifiedLayers = (int*)CPLCalloc(sizeof(int), nSrcLayers); + pabCheckIfAutoWrap = (int*)CPLCalloc(sizeof(int), nSrcLayers); +} + +/************************************************************************/ +/* ~OGRUnionLayer() */ +/************************************************************************/ + +OGRUnionLayer::~OGRUnionLayer() +{ + int i; + + if( bHasLayerOwnership ) + { + for(i = 0; i < nSrcLayers; i++) + delete papoSrcLayers[i]; + } + CPLFree(papoSrcLayers); + + for(i = 0; i < nFields; i++) + delete papoFields[i]; + CPLFree(papoFields); + for(i = 0; i < nGeomFields; i++) + delete papoGeomFields[i]; + CPLFree(papoGeomFields); + + CPLFree(pszAttributeFilter); + CPLFree(panMap); + CSLDestroy(papszIgnoredFields); + CPLFree(pabModifiedLayers); + CPLFree(pabCheckIfAutoWrap); + + if( poFeatureDefn ) + poFeatureDefn->Release(); + if( poGlobalSRS != NULL ) + poGlobalSRS->Release(); +} + +/************************************************************************/ +/* SetFields() */ +/************************************************************************/ + +void OGRUnionLayer::SetFields(FieldUnionStrategy eFieldStrategyIn, + int nFieldsIn, + OGRFieldDefn** papoFieldsIn, + int nGeomFieldsIn, + OGRUnionLayerGeomFieldDefn** papoGeomFieldsIn) +{ + CPLAssert(nFields == 0); + CPLAssert(poFeatureDefn == NULL); + + eFieldStrategy = eFieldStrategyIn; + if( nFieldsIn ) + { + nFields = nFieldsIn; + papoFields = (OGRFieldDefn** )CPLMalloc(nFields * sizeof(OGRFieldDefn*)); + for(int i=0;i 0 ) + { + papoGeomFields = (OGRUnionLayerGeomFieldDefn** )CPLMalloc( + nGeomFields * sizeof(OGRUnionLayerGeomFieldDefn*)); + for(int i=0;iGetType() != poSrcFieldDefn->GetType() ) + { + if( poSrcFieldDefn->GetType() == OFTReal && + (poFieldDefn->GetType() == OFTInteger || + poFieldDefn->GetType() == OFTInteger64) ) + poFieldDefn->SetType(OFTReal); + if( poFieldDefn->GetType() == OFTReal && + (poSrcFieldDefn->GetType() == OFTInteger || + poSrcFieldDefn->GetType() == OFTInteger64) ) + poFieldDefn->SetType(OFTReal); + else if( poSrcFieldDefn->GetType() == OFTInteger64 && + poFieldDefn->GetType() == OFTInteger) + poFieldDefn->SetType(OFTInteger64); + else if( poFieldDefn->GetType() == OFTInteger64 && + poSrcFieldDefn->GetType() == OFTInteger) + poFieldDefn->SetType(OFTInteger64); + else + poFieldDefn->SetType(OFTString); + } + + if( poFieldDefn->GetWidth() != poSrcFieldDefn->GetWidth() || + poFieldDefn->GetPrecision() != poSrcFieldDefn->GetPrecision() ) + { + poFieldDefn->SetWidth(0); + poFieldDefn->SetPrecision(0); + } +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn *OGRUnionLayer::GetLayerDefn() +{ + if( poFeatureDefn != NULL ) + return poFeatureDefn; + + poFeatureDefn = new OGRFeatureDefn( osName ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType(wkbNone); + + int iCompareFirstIndex = 0; + if( osSourceLayerFieldName.size() ) + { + OGRFieldDefn oField(osSourceLayerFieldName, OFTString); + poFeatureDefn->AddFieldDefn(&oField); + iCompareFirstIndex = 1; + } + + if( eFieldStrategy == FIELD_SPECIFIED ) + { + int i; + for(i = 0; i < nFields; i++) + poFeatureDefn->AddFieldDefn(papoFields[i]); + for(i = 0; i < nGeomFields; i++) + { + poFeatureDefn->AddGeomFieldDefn(new OGRUnionLayerGeomFieldDefn(papoGeomFields[i]), FALSE); + OGRUnionLayerGeomFieldDefn* poGeomFieldDefn = + (OGRUnionLayerGeomFieldDefn* ) poFeatureDefn->GetGeomFieldDefn(i); + + if( poGeomFieldDefn->bGeomTypeSet == FALSE || + poGeomFieldDefn->bSRSSet == FALSE ) + { + for(int iLayer = 0; iLayer < nSrcLayers; iLayer++) + { + OGRFeatureDefn* poSrcFeatureDefn = + papoSrcLayers[iLayer]->GetLayerDefn(); + int nIndex = + poSrcFeatureDefn->GetGeomFieldIndex(poGeomFieldDefn->GetNameRef()); + if( nIndex >= 0 ) + { + OGRGeomFieldDefn* poSrcGeomFieldDefn = + poSrcFeatureDefn->GetGeomFieldDefn(nIndex); + if( poGeomFieldDefn->bGeomTypeSet == FALSE ) + { + poGeomFieldDefn->bGeomTypeSet = TRUE; + poGeomFieldDefn->SetType(poSrcGeomFieldDefn->GetType()); + } + if( poGeomFieldDefn->bSRSSet == FALSE ) + { + poGeomFieldDefn->bSRSSet = TRUE; + poGeomFieldDefn->SetSpatialRef(poSrcGeomFieldDefn->GetSpatialRef()); + if( i == 0 && poGlobalSRS == NULL ) + { + poGlobalSRS = poSrcGeomFieldDefn->GetSpatialRef(); + if( poGlobalSRS != NULL ) + poGlobalSRS->Reference(); + } + } + break; + } + } + } + } + } + else if( eFieldStrategy == FIELD_FROM_FIRST_LAYER ) + { + OGRFeatureDefn* poSrcFeatureDefn = papoSrcLayers[0]->GetLayerDefn(); + int i; + for(i = 0; i < poSrcFeatureDefn->GetFieldCount(); i++) + poFeatureDefn->AddFieldDefn(poSrcFeatureDefn->GetFieldDefn(i)); + for(i = 0; nGeomFields != - 1 && i < poSrcFeatureDefn->GetGeomFieldCount(); i++) + { + OGRGeomFieldDefn* poFldDefn = poSrcFeatureDefn->GetGeomFieldDefn(i); + poFeatureDefn->AddGeomFieldDefn( + new OGRUnionLayerGeomFieldDefn(poFldDefn), FALSE); + } + } + else if (eFieldStrategy == FIELD_UNION_ALL_LAYERS ) + { + if( nGeomFields == 1 ) + { + poFeatureDefn->AddGeomFieldDefn( + new OGRUnionLayerGeomFieldDefn(papoGeomFields[0]), FALSE); + } + + for(int iLayer = 0; iLayer < nSrcLayers; iLayer++) + { + OGRFeatureDefn* poSrcFeatureDefn = + papoSrcLayers[iLayer]->GetLayerDefn(); + + /* Add any field that is found in the source layers */ + int i; + for(i = 0; i < poSrcFeatureDefn->GetFieldCount(); i++) + { + OGRFieldDefn* poSrcFieldDefn = poSrcFeatureDefn->GetFieldDefn(i); + int nIndex = + poFeatureDefn->GetFieldIndex(poSrcFieldDefn->GetNameRef()); + if( nIndex < 0 ) + poFeatureDefn->AddFieldDefn(poSrcFieldDefn); + else + { + OGRFieldDefn* poFieldDefn = + poFeatureDefn->GetFieldDefn(nIndex); + MergeFieldDefn(poFieldDefn, poSrcFieldDefn); + } + } + + for(i = 0; nGeomFields != - 1 && i < poSrcFeatureDefn->GetGeomFieldCount(); i++) + { + OGRGeomFieldDefn* poSrcFieldDefn = poSrcFeatureDefn->GetGeomFieldDefn(i); + int nIndex = + poFeatureDefn->GetGeomFieldIndex(poSrcFieldDefn->GetNameRef()); + if( nIndex < 0 ) + { + poFeatureDefn->AddGeomFieldDefn( + new OGRUnionLayerGeomFieldDefn(poSrcFieldDefn), FALSE); + if( poFeatureDefn->GetGeomFieldCount() == 1 && nGeomFields == 0 && + GetSpatialRef() != NULL ) + { + OGRUnionLayerGeomFieldDefn* poGeomFieldDefn = + (OGRUnionLayerGeomFieldDefn* ) poFeatureDefn->GetGeomFieldDefn(0); + poGeomFieldDefn->bSRSSet = TRUE; + poGeomFieldDefn->SetSpatialRef(GetSpatialRef()); + } + } + else + { + if( nIndex == 0 && nGeomFields == 1 ) + { + OGRUnionLayerGeomFieldDefn* poGeomFieldDefn = + (OGRUnionLayerGeomFieldDefn* ) poFeatureDefn->GetGeomFieldDefn(0); + if( poGeomFieldDefn->bGeomTypeSet == FALSE ) + { + poGeomFieldDefn->bGeomTypeSet = TRUE; + poGeomFieldDefn->SetType(poSrcFieldDefn->GetType()); + } + if( poGeomFieldDefn->bSRSSet == FALSE ) + { + poGeomFieldDefn->bSRSSet = TRUE; + poGeomFieldDefn->SetSpatialRef(poSrcFieldDefn->GetSpatialRef()); + } + } + /* TODO: merge type, SRS, extent ? */ + } + } + } + } + else if (eFieldStrategy == FIELD_INTERSECTION_ALL_LAYERS ) + { + OGRFeatureDefn* poSrcFeatureDefn = papoSrcLayers[0]->GetLayerDefn(); + int i; + for(i = 0; i < poSrcFeatureDefn->GetFieldCount(); i++) + poFeatureDefn->AddFieldDefn(poSrcFeatureDefn->GetFieldDefn(i)); + for(i = 0; i < poSrcFeatureDefn->GetGeomFieldCount(); i++) + { + OGRGeomFieldDefn* poFldDefn = poSrcFeatureDefn->GetGeomFieldDefn(i); + poFeatureDefn->AddGeomFieldDefn( + new OGRUnionLayerGeomFieldDefn(poFldDefn), FALSE); + } + + /* Remove any field that is not found in the source layers */ + for(int iLayer = 1; iLayer < nSrcLayers; iLayer++) + { + OGRFeatureDefn* poSrcFeatureDefn = + papoSrcLayers[iLayer]->GetLayerDefn(); + for(i = iCompareFirstIndex; i < poFeatureDefn->GetFieldCount();) + { + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(i); + int nSrcIndex = poSrcFeatureDefn->GetFieldIndex( + poFieldDefn->GetNameRef()); + if( nSrcIndex < 0 ) + { + poFeatureDefn->DeleteFieldDefn(i); + } + else + { + OGRFieldDefn* poSrcFieldDefn = + poSrcFeatureDefn->GetFieldDefn(nSrcIndex); + MergeFieldDefn(poFieldDefn, poSrcFieldDefn); + + i ++; + } + } + for(i = 0; i < poFeatureDefn->GetGeomFieldCount();) + { + OGRGeomFieldDefn* poFieldDefn = poFeatureDefn->GetGeomFieldDefn(i); + int nSrcIndex = poSrcFeatureDefn->GetGeomFieldIndex( + poFieldDefn->GetNameRef()); + if( nSrcIndex < 0 ) + { + poFeatureDefn->DeleteGeomFieldDefn(i); + } + else + { + /* TODO: merge type, SRS, extent ? */ + + i ++; + } + } + } + } + + return poFeatureDefn; +} + +/************************************************************************/ +/* GetGeomType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRUnionLayer::GetGeomType() +{ + if( nGeomFields < 0 ) + return wkbNone; + if( nGeomFields >= 1 && papoGeomFields[0]->bGeomTypeSet ) + { + return papoGeomFields[0]->GetType(); + } + + return OGRLayer::GetGeomType(); +} + +/************************************************************************/ +/* SetSpatialFilterToSourceLayer() */ +/************************************************************************/ + +void OGRUnionLayer::SetSpatialFilterToSourceLayer(OGRLayer* poSrcLayer) +{ + if( m_iGeomFieldFilter >= 0 && + m_iGeomFieldFilter < GetLayerDefn()->GetGeomFieldCount() ) + { + int iSrcGeomField = poSrcLayer->GetLayerDefn()->GetGeomFieldIndex( + GetLayerDefn()->GetGeomFieldDefn(m_iGeomFieldFilter)->GetNameRef()); + if( iSrcGeomField >= 0 ) + { + poSrcLayer->SetSpatialFilter(iSrcGeomField, m_poFilterGeom); + } + else + { + poSrcLayer->SetSpatialFilter(NULL); + } + } + else + { + poSrcLayer->SetSpatialFilter(NULL); + } +} + +/************************************************************************/ +/* ConfigureActiveLayer() */ +/************************************************************************/ + +void OGRUnionLayer::ConfigureActiveLayer() +{ + AutoWarpLayerIfNecessary(iCurLayer); + ApplyAttributeFilterToSrcLayer(iCurLayer); + SetSpatialFilterToSourceLayer(papoSrcLayers[iCurLayer]); + papoSrcLayers[iCurLayer]->ResetReading(); + + /* Establish map */ + OGRFeatureDefn* poFeatureDefn = GetLayerDefn(); + OGRFeatureDefn* poSrcFeatureDefn = papoSrcLayers[iCurLayer]->GetLayerDefn(); + CPLFree(panMap); + panMap = (int*) CPLMalloc(poSrcFeatureDefn->GetFieldCount() * sizeof(int)); + for(int i=0; i < poSrcFeatureDefn->GetFieldCount(); i++) + { + OGRFieldDefn* poSrcFieldDefn = poSrcFeatureDefn->GetFieldDefn(i); + if( CSLFindString(papszIgnoredFields, + poSrcFieldDefn->GetNameRef() ) == -1 ) + { + panMap[i] = + poFeatureDefn->GetFieldIndex(poSrcFieldDefn->GetNameRef()); + } + else + { + panMap[i] = -1; + } + } + + if( papoSrcLayers[iCurLayer]->TestCapability(OLCIgnoreFields) ) + { + char** papszIter = papszIgnoredFields; + char** papszFieldsSrc = NULL; + while ( papszIter != NULL && *papszIter != NULL ) + { + const char* pszFieldName = *papszIter; + if ( EQUAL(pszFieldName, "OGR_GEOMETRY") || + EQUAL(pszFieldName, "OGR_STYLE") || + poSrcFeatureDefn->GetFieldIndex(pszFieldName) >= 0 || + poSrcFeatureDefn->GetGeomFieldIndex(pszFieldName) >= 0 ) + { + papszFieldsSrc = CSLAddString(papszFieldsSrc, pszFieldName); + } + papszIter++; + } + + /* Attribute fields */ + int* panSrcFieldsUsed = (int*) CPLCalloc(sizeof(int), + poSrcFeatureDefn->GetFieldCount()); + for(int iField = 0; + iField < poFeatureDefn->GetFieldCount(); iField++) + { + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(iField); + int iSrcField = + poSrcFeatureDefn->GetFieldIndex(poFieldDefn->GetNameRef()); + if (iSrcField >= 0) + panSrcFieldsUsed[iSrcField] = TRUE; + } + for(int iSrcField = 0; + iSrcField < poSrcFeatureDefn->GetFieldCount(); iSrcField ++) + { + if( !panSrcFieldsUsed[iSrcField] ) + { + OGRFieldDefn *poSrcDefn = + poSrcFeatureDefn->GetFieldDefn( iSrcField ); + papszFieldsSrc = + CSLAddString(papszFieldsSrc, poSrcDefn->GetNameRef()); + } + } + CPLFree(panSrcFieldsUsed); + + /* geometry fields now */ + panSrcFieldsUsed = (int*) CPLCalloc(sizeof(int), + poSrcFeatureDefn->GetGeomFieldCount()); + for(int iField = 0; + iField < poFeatureDefn->GetGeomFieldCount(); iField++) + { + OGRGeomFieldDefn* poFieldDefn = poFeatureDefn->GetGeomFieldDefn(iField); + int iSrcField = + poSrcFeatureDefn->GetGeomFieldIndex(poFieldDefn->GetNameRef()); + if (iSrcField >= 0) + panSrcFieldsUsed[iSrcField] = TRUE; + } + for(int iSrcField = 0; + iSrcField < poSrcFeatureDefn->GetGeomFieldCount(); iSrcField ++) + { + if( !panSrcFieldsUsed[iSrcField] ) + { + OGRGeomFieldDefn *poSrcDefn = + poSrcFeatureDefn->GetGeomFieldDefn( iSrcField ); + papszFieldsSrc = + CSLAddString(papszFieldsSrc, poSrcDefn->GetNameRef()); + } + } + CPLFree(panSrcFieldsUsed); + + papoSrcLayers[iCurLayer]->SetIgnoredFields((const char**)papszFieldsSrc); + + CSLDestroy(papszFieldsSrc); + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRUnionLayer::ResetReading() +{ + iCurLayer = 0; + ConfigureActiveLayer(); + nNextFID = 0; +} + +/************************************************************************/ +/* AutoWarpLayerIfNecessary() */ +/************************************************************************/ + +void OGRUnionLayer::AutoWarpLayerIfNecessary(int iLayer) +{ + if( !pabCheckIfAutoWrap[iLayer] ) + { + pabCheckIfAutoWrap[iLayer] = TRUE; + + for(int i=0; iGetGeomFieldCount();i++) + { + OGRSpatialReference* poSRS = GetLayerDefn()->GetGeomFieldDefn(i)->GetSpatialRef(); + if( poSRS != NULL ) + poSRS->Reference(); + + OGRFeatureDefn* poSrcFeatureDefn = papoSrcLayers[iLayer]->GetLayerDefn(); + int iSrcGeomField = poSrcFeatureDefn->GetGeomFieldIndex( + GetLayerDefn()->GetGeomFieldDefn(i)->GetNameRef()); + if( iSrcGeomField >= 0 ) + { + OGRSpatialReference* poSRS2 = + poSrcFeatureDefn->GetGeomFieldDefn(iSrcGeomField)->GetSpatialRef(); + + if( (poSRS == NULL && poSRS2 != NULL) || + (poSRS != NULL && poSRS2 == NULL) ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "SRS of geometry field '%s' layer %s not consistent with UnionLayer SRS", + GetLayerDefn()->GetGeomFieldDefn(i)->GetNameRef(), + papoSrcLayers[iLayer]->GetName()); + } + else if (poSRS != NULL && poSRS2 != NULL && + poSRS != poSRS2 && !poSRS->IsSame(poSRS2)) + { + CPLDebug("VRT", "SRS of geometry field '%s' layer %s not consistent with UnionLayer SRS. " + "Trying auto warping", + GetLayerDefn()->GetGeomFieldDefn(i)->GetNameRef(), + papoSrcLayers[iLayer]->GetName()); + OGRCoordinateTransformation* poCT = + OGRCreateCoordinateTransformation( poSRS2, poSRS ); + OGRCoordinateTransformation* poReversedCT = (poCT != NULL) ? + OGRCreateCoordinateTransformation( poSRS, poSRS2 ) : NULL; + if( poCT != NULL && poReversedCT != NULL ) + papoSrcLayers[iLayer] = new OGRWarpedLayer( + papoSrcLayers[iLayer], iSrcGeomField, TRUE, poCT, poReversedCT); + } + } + + if( poSRS != NULL ) + poSRS->Release(); + } + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRUnionLayer::GetNextFeature() +{ + if( poFeatureDefn == NULL ) GetLayerDefn(); + if( iCurLayer < 0 ) + ResetReading(); + + if( iCurLayer == nSrcLayers ) + return NULL; + + while(TRUE) + { + OGRFeature* poSrcFeature = papoSrcLayers[iCurLayer]->GetNextFeature(); + if( poSrcFeature == NULL ) + { + iCurLayer ++; + if( iCurLayer < nSrcLayers ) + { + ConfigureActiveLayer(); + continue; + } + else + break; + } + + OGRFeature* poFeature = TranslateFromSrcLayer(poSrcFeature); + delete poSrcFeature; + + if( (m_poFilterGeom == NULL || + FilterGeometry( poFeature->GetGeomFieldRef(m_iGeomFieldFilter) ) ) && + (m_poAttrQuery == NULL || + m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + + delete poFeature; + } + return NULL; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRUnionLayer::GetFeature( GIntBig nFeatureId ) +{ + OGRFeature* poFeature = NULL; + + if( !bPreserveSrcFID ) + { + poFeature = OGRLayer::GetFeature(nFeatureId); + } + else + { + int iGeomFieldFilterSave = m_iGeomFieldFilter; + OGRGeometry* poGeomSave = m_poFilterGeom; + m_poFilterGeom = NULL; + SetSpatialFilter(NULL); + + for(int i=0;iGetFeature(nFeatureId); + if( poSrcFeature != NULL ) + { + poFeature = TranslateFromSrcLayer(poSrcFeature); + delete poSrcFeature; + + break; + } + } + + SetSpatialFilter(iGeomFieldFilterSave, poGeomSave); + delete poGeomSave; + + ResetReading(); + } + + return poFeature; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::ICreateFeature( OGRFeature* poFeature ) +{ + if( osSourceLayerFieldName.size() == 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "CreateFeature() not supported when SourceLayerFieldName is not set"); + return OGRERR_FAILURE; + } + + if( poFeature->GetFID() != OGRNullFID ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "CreateFeature() not supported when FID is set"); + return OGRERR_FAILURE; + } + + if( !poFeature->IsFieldSet(0) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "CreateFeature() not supported when '%s' field is not set", + osSourceLayerFieldName.c_str()); + return OGRERR_FAILURE; + } + + const char* pszSrcLayerName = poFeature->GetFieldAsString(0); + for(int i=0;iGetName()) == 0) + { + pabModifiedLayers[i] = TRUE; + + OGRFeature* poSrcFeature = + new OGRFeature(papoSrcLayers[i]->GetLayerDefn()); + poSrcFeature->SetFrom(poFeature, TRUE); + OGRErr eErr = papoSrcLayers[i]->CreateFeature(poSrcFeature); + if( eErr == OGRERR_NONE ) + poFeature->SetFID(poSrcFeature->GetFID()); + delete poSrcFeature; + return eErr; + } + } + + CPLError(CE_Failure, CPLE_NotSupported, + "CreateFeature() not supported : '%s' source layer does not exist", + pszSrcLayerName); + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::ISetFeature( OGRFeature* poFeature ) +{ + if( !bPreserveSrcFID ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() not supported when PreserveSrcFID is OFF"); + return OGRERR_FAILURE; + } + + if( osSourceLayerFieldName.size() == 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() not supported when SourceLayerFieldName is not set"); + return OGRERR_FAILURE; + } + + if( poFeature->GetFID() == OGRNullFID ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() not supported when FID is not set"); + return OGRERR_FAILURE; + } + + if( !poFeature->IsFieldSet(0) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() not supported when '%s' field is not set", + osSourceLayerFieldName.c_str()); + return OGRERR_FAILURE; + } + + const char* pszSrcLayerName = poFeature->GetFieldAsString(0); + for(int i=0;iGetName()) == 0) + { + pabModifiedLayers[i] = TRUE; + + OGRFeature* poSrcFeature = + new OGRFeature(papoSrcLayers[i]->GetLayerDefn()); + poSrcFeature->SetFrom(poFeature, TRUE); + poSrcFeature->SetFID(poFeature->GetFID()); + OGRErr eErr = papoSrcLayers[i]->SetFeature(poSrcFeature); + delete poSrcFeature; + return eErr; + } + } + + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() not supported : '%s' source layer does not exist", + pszSrcLayerName); + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRUnionLayer::GetSpatialRef() +{ + if( nGeomFields < 0 ) + return NULL; + if( nGeomFields >= 1 && + papoGeomFields[0]->bSRSSet ) + return papoGeomFields[0]->GetSpatialRef(); + + if( poGlobalSRS == NULL ) + { + poGlobalSRS = papoSrcLayers[0]->GetSpatialRef(); + if( poGlobalSRS != NULL ) + poGlobalSRS->Reference(); + } + return poGlobalSRS; +} + +/************************************************************************/ +/* GetAttrFilterPassThroughValue() */ +/************************************************************************/ + +int OGRUnionLayer::GetAttrFilterPassThroughValue() +{ + if( m_poAttrQuery == NULL ) + return TRUE; + + if( bAttrFilterPassThroughValue >= 0) + return bAttrFilterPassThroughValue; + + char** papszUsedFields = m_poAttrQuery->GetUsedFields(); + int bRet = TRUE; + + for(int iLayer = 0; iLayer < nSrcLayers; iLayer++) + { + OGRFeatureDefn* poSrcFeatureDefn = + papoSrcLayers[iLayer]->GetLayerDefn(); + char** papszIter = papszUsedFields; + while( papszIter != NULL && *papszIter != NULL ) + { + int bIsSpecial = FALSE; + for(int i = 0; i < SPECIAL_FIELD_COUNT; i++) + { + if( EQUAL(*papszIter, SpecialFieldNames[i]) ) + { + bIsSpecial = TRUE; + break; + } + } + if( !bIsSpecial && + poSrcFeatureDefn->GetFieldIndex(*papszIter) < 0 ) + { + bRet = FALSE; + break; + } + papszIter ++; + } + } + + CSLDestroy(papszUsedFields); + + bAttrFilterPassThroughValue = bRet; + + return bRet; +} + +/************************************************************************/ +/* ApplyAttributeFilterToSrcLayer() */ +/************************************************************************/ + +void OGRUnionLayer::ApplyAttributeFilterToSrcLayer(int iSubLayer) +{ + CPLAssert(iSubLayer >= 0 && iSubLayer < nSrcLayers); + + if( GetAttrFilterPassThroughValue() ) + papoSrcLayers[iSubLayer]->SetAttributeFilter(pszAttributeFilter); + else + papoSrcLayers[iSubLayer]->SetAttributeFilter(NULL); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRUnionLayer::GetFeatureCount( int bForce ) +{ + if (nFeatureCount >= 0 && + m_poFilterGeom == NULL && m_poAttrQuery == NULL) + { + return nFeatureCount; + } + + if( !GetAttrFilterPassThroughValue() ) + return OGRLayer::GetFeatureCount(bForce); + + GIntBig nRet = 0; + for(int i = 0; i < nSrcLayers; i++) + { + AutoWarpLayerIfNecessary(i); + ApplyAttributeFilterToSrcLayer(i); + SetSpatialFilterToSourceLayer(papoSrcLayers[i]); + nRet += papoSrcLayers[i]->GetFeatureCount(bForce); + } + ResetReading(); + return nRet; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::SetAttributeFilter( const char * pszAttributeFilterIn ) +{ + if( pszAttributeFilterIn == NULL && pszAttributeFilter == NULL) + return OGRERR_NONE; + if( pszAttributeFilterIn != NULL && pszAttributeFilter != NULL && + strcmp(pszAttributeFilterIn, pszAttributeFilter) == 0) + return OGRERR_NONE; + + if( poFeatureDefn == NULL ) GetLayerDefn(); + + bAttrFilterPassThroughValue = -1; + + OGRErr eErr = OGRLayer::SetAttributeFilter(pszAttributeFilterIn); + if( eErr != OGRERR_NONE ) + return eErr; + + CPLFree(pszAttributeFilter); + pszAttributeFilter = pszAttributeFilterIn ? + CPLStrdup(pszAttributeFilterIn) : NULL; + + if( iCurLayer >= 0 && iCurLayer < nSrcLayers) + ApplyAttributeFilterToSrcLayer(iCurLayer); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRUnionLayer::TestCapability( const char * pszCap ) +{ + if( EQUAL(pszCap, OLCFastFeatureCount ) ) + { + if( nFeatureCount >= 0 && + m_poFilterGeom == NULL && m_poAttrQuery == NULL ) + return TRUE; + + if( !GetAttrFilterPassThroughValue() ) + return FALSE; + + for(int i = 0; i < nSrcLayers; i++) + { + AutoWarpLayerIfNecessary(i); + ApplyAttributeFilterToSrcLayer(i); + SetSpatialFilterToSourceLayer(papoSrcLayers[i]); + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCFastGetExtent ) ) + { + if( nGeomFields >= 1 && + papoGeomFields[0]->sStaticEnvelope.IsInit() ) + return TRUE; + + for(int i = 0; i < nSrcLayers; i++) + { + AutoWarpLayerIfNecessary(i); + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCFastSpatialFilter) ) + { + for(int i = 0; i < nSrcLayers; i++) + { + AutoWarpLayerIfNecessary(i); + ApplyAttributeFilterToSrcLayer(i); + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCStringsAsUTF8) ) + { + for(int i = 0; i < nSrcLayers; i++) + { + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCRandomRead ) ) + { + if( !bPreserveSrcFID ) + return FALSE; + + for(int i = 0; i < nSrcLayers; i++) + { + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCRandomWrite ) ) + { + if( !bPreserveSrcFID || osSourceLayerFieldName.size() == 0) + return FALSE; + + for(int i = 0; i < nSrcLayers; i++) + { + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCSequentialWrite ) ) + { + if( osSourceLayerFieldName.size() == 0) + return FALSE; + + for(int i = 0; i < nSrcLayers; i++) + { + if( !papoSrcLayers[i]->TestCapability(pszCap) ) + return FALSE; + } + return TRUE; + } + + if( EQUAL(pszCap, OLCIgnoreFields) ) + return TRUE; + + if( EQUAL(pszCap,OLCCurveGeometries) ) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) +{ + if( iGeomField >= 0 && iGeomField < nGeomFields && + papoGeomFields[iGeomField]->sStaticEnvelope.IsInit() ) + { + memcpy(psExtent, &papoGeomFields[iGeomField]->sStaticEnvelope, sizeof(OGREnvelope)); + return OGRERR_NONE; + } + + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + return OGRERR_FAILURE; + } + + int bInit = FALSE; + for(int i = 0; i < nSrcLayers; i++) + { + AutoWarpLayerIfNecessary(i); + int iSrcGeomField = papoSrcLayers[i]->GetLayerDefn()->GetGeomFieldIndex( + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetNameRef()); + if( iSrcGeomField >= 0 ) + { + if( !bInit ) + { + if( papoSrcLayers[i]->GetExtent(iSrcGeomField, psExtent, bForce) == OGRERR_NONE ) + bInit = TRUE; + } + else + { + OGREnvelope sExtent; + if( papoSrcLayers[i]->GetExtent(iSrcGeomField, &sExtent, bForce) == OGRERR_NONE ) + { + psExtent->Merge(sExtent); + } + } + } + } + return (bInit) ? OGRERR_NONE : OGRERR_FAILURE; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::GetExtent( OGREnvelope *psExtent, int bForce ) +{ + return GetExtent(0, psExtent, bForce); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRUnionLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) +{ + SetSpatialFilter(0, poGeomIn); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRUnionLayer::SetSpatialFilter( int iGeomField, OGRGeometry *poGeom ) +{ + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ) + { + if( poGeom != NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + return; + } + } + + m_iGeomFieldFilter = iGeomField; + if( InstallFilter( poGeom ) ) + ResetReading(); + + if( iCurLayer >= 0 && iCurLayer < nSrcLayers) + { + SetSpatialFilterToSourceLayer(papoSrcLayers[iCurLayer]); + } +} + + +/************************************************************************/ +/* TranslateFromSrcLayer() */ +/************************************************************************/ + +OGRFeature* OGRUnionLayer::TranslateFromSrcLayer(OGRFeature* poSrcFeature) +{ + CPLAssert(panMap != NULL); + CPLAssert(iCurLayer >= 0 && iCurLayer < nSrcLayers); + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFrom(poSrcFeature, panMap, TRUE); + + if( osSourceLayerFieldName.size() && + !poFeatureDefn->GetFieldDefn(0)->IsIgnored() ) + { + poFeature->SetField(0, papoSrcLayers[iCurLayer]->GetName()); + } + + for(int i=0;iGetGeomFieldCount();i++) + { + if( poFeatureDefn->GetGeomFieldDefn(i)->IsIgnored() ) + poFeature->SetGeomFieldDirectly(i, NULL); + else + { + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(i); + if( poGeom != NULL ) + { + poGeom->assignSpatialReference( + poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef()); + } + } + } + + if( bPreserveSrcFID ) + poFeature->SetFID(poSrcFeature->GetFID()); + else + poFeature->SetFID(nNextFID ++); + return poFeature; +} + +/************************************************************************/ +/* SetIgnoredFields() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::SetIgnoredFields( const char **papszFields ) +{ + OGRErr eErr = OGRLayer::SetIgnoredFields(papszFields); + if( eErr != OGRERR_NONE ) + return eErr; + + CSLDestroy(papszIgnoredFields); + papszIgnoredFields = papszFields ? CSLDuplicate((char**)papszFields) : NULL; + + return eErr; +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr OGRUnionLayer::SyncToDisk() +{ + for(int i = 0; i < nSrcLayers; i++) + { + if (pabModifiedLayers[i]) + { + papoSrcLayers[i]->SyncToDisk(); + pabModifiedLayers[i] = FALSE; + } + } + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrunionlayer.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrunionlayer.h new file mode 100644 index 000000000..8d641b639 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrunionlayer.h @@ -0,0 +1,153 @@ +/****************************************************************************** + * $Id: ogrunionlayer.h 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRUnionLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRUNIONLAYER_H_INCLUDED +#define _OGRUNIONLAYER_H_INCLUDED + +#include "ogrsf_frmts.h" + +/************************************************************************/ +/* OGRUnionLayerGeomFieldDefn */ +/************************************************************************/ + +class OGRUnionLayerGeomFieldDefn: public OGRGeomFieldDefn +{ + public: + + int bGeomTypeSet; + int bSRSSet; + OGREnvelope sStaticEnvelope; + + OGRUnionLayerGeomFieldDefn(const char* pszName, OGRwkbGeometryType eType); + OGRUnionLayerGeomFieldDefn(OGRGeomFieldDefn* poSrc); + OGRUnionLayerGeomFieldDefn(OGRUnionLayerGeomFieldDefn* poSrc); + ~OGRUnionLayerGeomFieldDefn(); +}; + +/************************************************************************/ +/* OGRUnionLayer */ +/************************************************************************/ + +typedef enum +{ + FIELD_FROM_FIRST_LAYER, + FIELD_UNION_ALL_LAYERS, + FIELD_INTERSECTION_ALL_LAYERS, + FIELD_SPECIFIED, +} FieldUnionStrategy; + +class OGRUnionLayer : public OGRLayer +{ + protected: + CPLString osName; + int nSrcLayers; + OGRLayer **papoSrcLayers; + int bHasLayerOwnership; + + OGRFeatureDefn *poFeatureDefn; + int nFields; + OGRFieldDefn **papoFields; + int nGeomFields; + OGRUnionLayerGeomFieldDefn **papoGeomFields; + FieldUnionStrategy eFieldStrategy; + CPLString osSourceLayerFieldName; + + int bPreserveSrcFID; + + GIntBig nFeatureCount; + + int iCurLayer; + char *pszAttributeFilter; + int nNextFID; + int *panMap; + char **papszIgnoredFields; + int bAttrFilterPassThroughValue; + int *pabModifiedLayers; + int *pabCheckIfAutoWrap; + OGRSpatialReference *poGlobalSRS; + + void AutoWarpLayerIfNecessary(int iSubLayer); + OGRFeature *TranslateFromSrcLayer(OGRFeature* poSrcFeature); + void ApplyAttributeFilterToSrcLayer(int iSubLayer); + int GetAttrFilterPassThroughValue(); + void ConfigureActiveLayer(); + void SetSpatialFilterToSourceLayer(OGRLayer* poSrcLayer); + + public: + OGRUnionLayer( const char* pszName, + int nSrcLayers, /* must be >= 1 */ + OGRLayer** papoSrcLayers, /* array itself ownership always transferred, layer ownership depending on bTakeLayerOwnership */ + int bTakeLayerOwnership); + + virtual ~OGRUnionLayer(); + + /* All the following non virtual methods must be called just after the constructor */ + /* and before any virtual method */ + void SetFields(FieldUnionStrategy eFieldStrategy, + int nFields, + OGRFieldDefn** papoFields, /* duplicated by the method */ + int nGeomFields, /* maybe -1 to explicitly disable geometry fields */ + OGRUnionLayerGeomFieldDefn** papoGeomFields /* duplicated by the method */); + void SetSourceLayerFieldName(const char* pszSourceLayerFieldName); + void SetPreserveSrcFID(int bPreserveSrcFID); + void SetFeatureCount(int nFeatureCount); + virtual const char *GetName() { return osName.c_str(); } + virtual OGRwkbGeometryType GetGeomType(); + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual OGRErr ICreateFeature( OGRFeature* poFeature ); + + virtual OGRErr ISetFeature( OGRFeature* poFeature ); + + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual GIntBig GetFeatureCount( int ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual int TestCapability( const char * ); + + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent( OGREnvelope *psExtent, int bForce ); + + virtual void SetSpatialFilter( OGRGeometry * poGeomIn ); + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + + virtual OGRErr SetIgnoredFields( const char **papszFields ); + + virtual OGRErr SyncToDisk(); +}; + +#endif // _OGRUNIONLAYER_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrwarpedlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrwarpedlayer.cpp new file mode 100644 index 000000000..a3f0e6be6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrwarpedlayer.cpp @@ -0,0 +1,572 @@ +/****************************************************************************** + * $Id: ogrwarpedlayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRWarpedLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrwarpedlayer.h" + +CPL_CVSID("$Id: ogrwarpedlayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRWarpedLayer() */ +/************************************************************************/ + +OGRWarpedLayer::OGRWarpedLayer( OGRLayer* poDecoratedLayer, + int iGeomField, + int bTakeOwnership, + OGRCoordinateTransformation* poCT, + OGRCoordinateTransformation* poReversedCT ) : + OGRLayerDecorator(poDecoratedLayer, + bTakeOwnership), + m_iGeomField(iGeomField), + m_poCT(poCT), + m_poReversedCT(poReversedCT) +{ + CPLAssert(poCT != NULL); + SetDescription( poDecoratedLayer->GetDescription() ); + + m_poFeatureDefn = NULL; + + if( m_poCT->GetTargetCS() != NULL ) + { + m_poSRS = m_poCT->GetTargetCS(); + m_poSRS->Reference(); + } + else + m_poSRS = NULL; +} + +/************************************************************************/ +/* ~OGRWarpedLayer() */ +/************************************************************************/ + +OGRWarpedLayer::~OGRWarpedLayer() +{ + if( m_poFeatureDefn != NULL ) + m_poFeatureDefn->Release(); + if( m_poSRS != NULL ) + m_poSRS->Release(); + delete m_poCT; + delete m_poReversedCT; +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRWarpedLayer::SetSpatialFilter( OGRGeometry * poGeom ) +{ + SetSpatialFilter( 0, poGeom ); +} + +/************************************************************************/ +/* SetSpatialFilterRect() */ +/************************************************************************/ + +void OGRWarpedLayer::SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) +{ + OGRLayer::SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY); +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRWarpedLayer::SetSpatialFilter( int iGeomField, OGRGeometry *poGeom ) +{ + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + return; + } + + m_iGeomFieldFilter = iGeomField; + if( InstallFilter( poGeom ) ) + ResetReading(); + + if( m_iGeomFieldFilter == m_iGeomField ) + { + if( poGeom == NULL || m_poReversedCT == NULL ) + { + m_poDecoratedLayer->SetSpatialFilter(m_iGeomFieldFilter, + NULL); + } + else + { + OGREnvelope sEnvelope; + poGeom->getEnvelope(&sEnvelope); + if( CPLIsInf(sEnvelope.MinX) && CPLIsInf(sEnvelope.MinY) && + CPLIsInf(sEnvelope.MaxX) && CPLIsInf(sEnvelope.MaxY) ) + { + m_poDecoratedLayer->SetSpatialFilterRect(m_iGeomFieldFilter, + sEnvelope.MinX, + sEnvelope.MinY, + sEnvelope.MaxX, + sEnvelope.MaxY); + } + else if( ReprojectEnvelope(&sEnvelope, m_poReversedCT) ) + { + m_poDecoratedLayer->SetSpatialFilterRect(m_iGeomFieldFilter, + sEnvelope.MinX, + sEnvelope.MinY, + sEnvelope.MaxX, + sEnvelope.MaxY); + } + else + { + m_poDecoratedLayer->SetSpatialFilter(m_iGeomFieldFilter, + NULL); + } + } + } + else + { + m_poDecoratedLayer->SetSpatialFilter(m_iGeomFieldFilter, + poGeom); + } +} + +/************************************************************************/ +/* SetSpatialFilterRect() */ +/************************************************************************/ + +void OGRWarpedLayer::SetSpatialFilterRect( int iGeomField, double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ) +{ + OGRLayer::SetSpatialFilterRect(iGeomField, dfMinX, dfMinY, dfMaxX, dfMaxY); +} + + +/************************************************************************/ +/* SrcFeatureToWarpedFeature() */ +/************************************************************************/ + +OGRFeature *OGRWarpedLayer::SrcFeatureToWarpedFeature(OGRFeature* poSrcFeature) +{ + OGRFeature* poFeature = new OGRFeature(GetLayerDefn()); + poFeature->SetFrom(poSrcFeature); + poFeature->SetFID(poSrcFeature->GetFID()); + + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(m_iGeomField); + if( poGeom == NULL ) + return poFeature; + + if( poGeom->transform(m_poCT) != OGRERR_NONE ) + { + delete poFeature->StealGeometry(m_iGeomField); + } + + return poFeature; +} + + +/************************************************************************/ +/* WarpedFeatureToSrcFeature() */ +/************************************************************************/ + +OGRFeature *OGRWarpedLayer::WarpedFeatureToSrcFeature(OGRFeature* poFeature) +{ + OGRFeature* poSrcFeature = new OGRFeature(m_poDecoratedLayer->GetLayerDefn()); + poSrcFeature->SetFrom(poFeature); + poSrcFeature->SetFID(poFeature->GetFID()); + + OGRGeometry* poGeom = poSrcFeature->GetGeomFieldRef(m_iGeomField); + if( poGeom != NULL ) + { + if( m_poReversedCT == NULL ) + { + delete poSrcFeature; + return NULL; + } + + if( poGeom->transform(m_poReversedCT) != OGRERR_NONE ) + { + delete poSrcFeature; + return NULL; + } + } + + return poSrcFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRWarpedLayer::GetNextFeature() +{ + while(TRUE) + { + OGRFeature* poFeature = m_poDecoratedLayer->GetNextFeature(); + if( poFeature == NULL ) + return NULL; + + OGRFeature* poFeatureNew = SrcFeatureToWarpedFeature(poFeature); + delete poFeature; + + OGRGeometry* poGeom = poFeatureNew->GetGeomFieldRef(m_iGeomField); + if( m_poFilterGeom != NULL && !FilterGeometry( poGeom ) ) + { + delete poFeatureNew; + continue; + } + + return poFeatureNew; + } +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRWarpedLayer::GetFeature( GIntBig nFID ) +{ + OGRFeature* poFeature = m_poDecoratedLayer->GetFeature(nFID); + if( poFeature != NULL ) + { + OGRFeature* poFeatureNew = SrcFeatureToWarpedFeature(poFeature); + delete poFeature; + poFeature = poFeatureNew; + } + return poFeature; +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRWarpedLayer::ISetFeature( OGRFeature *poFeature ) +{ + OGRErr eErr; + + OGRFeature* poFeatureNew = WarpedFeatureToSrcFeature(poFeature); + if( poFeatureNew == NULL ) + return OGRERR_FAILURE; + + eErr = m_poDecoratedLayer->SetFeature(poFeatureNew); + + delete poFeatureNew; + + return eErr; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRWarpedLayer::ICreateFeature( OGRFeature *poFeature ) +{ + OGRErr eErr; + + OGRFeature* poFeatureNew = WarpedFeatureToSrcFeature(poFeature); + if( poFeatureNew == NULL ) + return OGRERR_FAILURE; + + eErr = m_poDecoratedLayer->CreateFeature(poFeatureNew); + + delete poFeatureNew; + + return eErr; +} + + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn *OGRWarpedLayer::GetLayerDefn() +{ + if( m_poFeatureDefn != NULL ) + return m_poFeatureDefn; + + m_poFeatureDefn = m_poDecoratedLayer->GetLayerDefn()->Clone(); + m_poFeatureDefn->Reference(); + if( m_poFeatureDefn->GetGeomFieldCount() > 0 ) + m_poFeatureDefn->GetGeomFieldDefn(m_iGeomField)->SetSpatialRef(m_poSRS); + + return m_poFeatureDefn; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRWarpedLayer::GetSpatialRef() +{ + if( m_iGeomField == 0 ) + return m_poSRS; + else + return OGRLayer::GetSpatialRef(); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRWarpedLayer::GetFeatureCount( int bForce ) +{ + if( m_poFilterGeom == NULL ) + return m_poDecoratedLayer->GetFeatureCount(bForce); + + return OGRLayer::GetFeatureCount(bForce); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRWarpedLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + return GetExtent(0, psExtent, bForce); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRWarpedLayer::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) +{ + if( iGeomField == m_iGeomField ) + { + if( sStaticEnvelope.IsInit() ) + { + memcpy(psExtent, &sStaticEnvelope, sizeof(OGREnvelope)); + return OGRERR_NONE; + } + + OGREnvelope sExtent; + OGRErr eErr = m_poDecoratedLayer->GetExtent(m_iGeomField, &sExtent, bForce); + if( eErr != OGRERR_NONE ) + return eErr; + + if( ReprojectEnvelope(&sExtent, m_poCT) ) + { + memcpy(psExtent, &sExtent, sizeof(OGREnvelope)); + return OGRERR_NONE; + } + else + return OGRERR_FAILURE; + } + else + return m_poDecoratedLayer->GetExtent(iGeomField, psExtent, bForce); +} + +/************************************************************************/ +/* TransformAndUpdateBBAndReturnX() */ +/************************************************************************/ + +static double TransformAndUpdateBBAndReturnX( + OGRCoordinateTransformation* poCT, + double dfX, double dfY, + double& dfMinX, double& dfMinY, double& dfMaxX, double& dfMaxY) +{ + int bSuccess; + poCT->TransformEx( 1, &dfX, &dfY, NULL, &bSuccess ); + if( bSuccess ) + { + if( dfX < dfMinX ) dfMinX = dfX; + if( dfY < dfMinY ) dfMinY = dfY; + if( dfX > dfMaxX ) dfMaxX = dfX; + if( dfY > dfMaxY ) dfMaxY = dfY; + return dfX; + } + else + return 0.0; +} + +/************************************************************************/ +/* FindXDiscontinuity() */ +/************************************************************************/ + +static void FindXDiscontinuity(OGRCoordinateTransformation* poCT, + double dfX1, double dfX2, double dfY, + double& dfMinX, double& dfMinY, double& dfMaxX, double& dfMaxY, + int nRecLevel = 0) +{ + double dfXMid = (dfX1 + dfX2) / 2; + + double dfWrkX1 = TransformAndUpdateBBAndReturnX(poCT, dfX1, dfY, dfMinX, dfMinY, dfMaxX, dfMaxY); + double dfWrkXMid = TransformAndUpdateBBAndReturnX(poCT, dfXMid, dfY, dfMinX, dfMinY, dfMaxX, dfMaxY); + double dfWrkX2 = TransformAndUpdateBBAndReturnX(poCT, dfX2, dfY, dfMinX, dfMinY, dfMaxX, dfMaxY); + + double dfDX1 = dfWrkXMid - dfWrkX1; + double dfDX2 = dfWrkX2 - dfWrkXMid; + + if( dfDX1 * dfDX2 < 0 && nRecLevel < 30) + { + FindXDiscontinuity(poCT, dfX1, dfXMid, dfY, dfMinX, dfMinY, dfMaxX, dfMaxY, nRecLevel + 1); + FindXDiscontinuity(poCT, dfXMid, dfX2, dfY, dfMinX, dfMinY, dfMaxX, dfMaxY, nRecLevel + 1); + } +} + +/************************************************************************/ +/* ReprojectEnvelope() */ +/************************************************************************/ + +int OGRWarpedLayer::ReprojectEnvelope( OGREnvelope* psEnvelope, + OGRCoordinateTransformation* poCT ) +{ +#define NSTEP 20 + double dfXStep = (psEnvelope->MaxX - psEnvelope->MinX) / NSTEP; + double dfYStep = (psEnvelope->MaxY - psEnvelope->MinY) / NSTEP; + int i, j; + double *padfX, *padfY; + int* pabSuccess; + + padfX = (double*) VSIMalloc((NSTEP + 1) * (NSTEP + 1) * sizeof(double)); + padfY = (double*) VSIMalloc((NSTEP + 1) * (NSTEP + 1) * sizeof(double)); + pabSuccess = (int*) VSIMalloc((NSTEP + 1) * (NSTEP + 1) * sizeof(int)); + if( padfX == NULL || padfY == NULL || pabSuccess == NULL) + { + VSIFree(padfX); + VSIFree(padfY); + VSIFree(pabSuccess); + return FALSE; + } + + for(j = 0; j <= NSTEP; j++) + { + for(i = 0; i <= NSTEP; i++) + { + padfX[j * (NSTEP + 1) + i] = psEnvelope->MinX + i * dfXStep; + padfY[j * (NSTEP + 1) + i] = psEnvelope->MinY + j * dfYStep; + } + } + + int bRet = FALSE; + + if( poCT->TransformEx( (NSTEP + 1) * (NSTEP + 1), padfX, padfY, NULL, + pabSuccess ) ) + { + double dfMinX = 0.0, dfMinY = 0.0, dfMaxX = 0.0, dfMaxY = 0.0; + int bSet = FALSE; + for(j = 0; j <= NSTEP; j++) + { + double dfXOld = 0.0; + double dfDXOld = 0.0; + int iOld = -1, iOldOld = -1; + for(i = 0; i <= NSTEP; i++) + { + if( pabSuccess[j * (NSTEP + 1) + i] ) + { + double dfX = padfX[j * (NSTEP + 1) + i]; + double dfY = padfY[j * (NSTEP + 1) + i]; + + if( !bSet ) + { + dfMinX = dfMaxX = dfX; + dfMinY = dfMaxY = dfY; + bSet = TRUE; + } + else + { + if( dfX < dfMinX ) dfMinX = dfX; + if( dfY < dfMinY ) dfMinY = dfY; + if( dfX > dfMaxX ) dfMaxX = dfX; + if( dfY > dfMaxY ) dfMaxY = dfY; + } + + if( iOld >= 0 ) + { + double dfDXNew = dfX - dfXOld; + if( iOldOld >= 0 && dfDXNew * dfDXOld < 0 ) + { + FindXDiscontinuity(poCT, + psEnvelope->MinX + iOldOld * dfXStep, + psEnvelope->MinX + i * dfXStep, + psEnvelope->MinY + j * dfYStep, + dfMinX, dfMinY, dfMaxX, dfMaxY); + } + dfDXOld = dfDXNew; + } + + dfXOld = dfX; + iOldOld = iOld; + iOld = i; + + } + } + } + if( bSet ) + { + psEnvelope->MinX = dfMinX; + psEnvelope->MinY = dfMinY; + psEnvelope->MaxX = dfMaxX; + psEnvelope->MaxY = dfMaxY; + bRet = TRUE; + } + } + + VSIFree(padfX); + VSIFree(padfY); + VSIFree(pabSuccess); + + return bRet; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRWarpedLayer::TestCapability( const char * pszCapability ) +{ + if( EQUAL(pszCapability, OLCFastGetExtent) && + sStaticEnvelope.IsInit() ) + return TRUE; + + int bVal = m_poDecoratedLayer->TestCapability(pszCapability); + + if( EQUAL(pszCapability, OLCFastSpatialFilter) || + EQUAL(pszCapability, OLCRandomWrite) || + EQUAL(pszCapability, OLCSequentialWrite) ) + { + if( bVal ) + bVal = m_poReversedCT != NULL; + } + else if( EQUAL(pszCapability, OLCFastFeatureCount) ) + { + if( bVal ) + bVal = m_poFilterGeom == NULL; + } + + return bVal; +} + +/************************************************************************/ +/* SetExtent() */ +/************************************************************************/ + +void OGRWarpedLayer::SetExtent( double dfXMin, double dfYMin, + double dfXMax, double dfYMax ) +{ + sStaticEnvelope.MinX = dfXMin; + sStaticEnvelope.MinY = dfYMin; + sStaticEnvelope.MaxX = dfXMax; + sStaticEnvelope.MaxY = dfYMax; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrwarpedlayer.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrwarpedlayer.h new file mode 100644 index 000000000..761998177 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/generic/ogrwarpedlayer.h @@ -0,0 +1,91 @@ +/****************************************************************************** + * $Id: ogrwarpedlayer.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines OGRWarpedLayer class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRWARPEDLAYER_H_INCLUDED +#define _OGRWARPEDLAYER_H_INCLUDED + +#include "ogrlayerdecorator.h" + +/************************************************************************/ +/* OGRWarpedLayer */ +/************************************************************************/ + +class OGRWarpedLayer : public OGRLayerDecorator +{ + protected: + OGRFeatureDefn *m_poFeatureDefn; + int m_iGeomField; + + OGRCoordinateTransformation *m_poCT; + OGRCoordinateTransformation *m_poReversedCT; /* may be NULL */ + OGRSpatialReference *m_poSRS; + + OGREnvelope sStaticEnvelope; + + static int ReprojectEnvelope( OGREnvelope* psEnvelope, + OGRCoordinateTransformation* poCT ); + + OGRFeature * SrcFeatureToWarpedFeature(OGRFeature* poFeature); + OGRFeature * WarpedFeatureToSrcFeature(OGRFeature* poFeature); + + public: + + OGRWarpedLayer(OGRLayer* poDecoratedLayer, + int iGeomField, + int bTakeOwnership, + OGRCoordinateTransformation* poCT, /* must NOT be NULL, ownership acquired by OGRWarpedLayer */ + OGRCoordinateTransformation* poReversedCT /* may be NULL, ownership acquired by OGRWarpedLayer */); + virtual ~OGRWarpedLayer(); + + void SetExtent(double dfXMin, double dfYMin, double dfXMax, double dfYMax); + + virtual void SetSpatialFilter( OGRGeometry * ); + virtual void SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + virtual void SetSpatialFilterRect( int iGeomField, double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + + virtual OGRFeature *GetNextFeature(); + virtual OGRFeature *GetFeature( GIntBig nFID ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); +}; + +#endif // _OGRWARPEDLAYER_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/GNUmakefile new file mode 100644 index 000000000..42d3bdc6d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/GNUmakefile @@ -0,0 +1,11 @@ +include ../../../GDALmake.opt + +OBJ = geoconcept.o geoconcept_syscoord.o \ + ogrgeoconceptdriver.o ogrgeoconceptdatasource.o ogrgeoconceptlayer.o + +CPPFLAGS := -DUSE_CPL -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/drv_geoconcept.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/drv_geoconcept.html new file mode 100644 index 000000000..2715419f8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/drv_geoconcept.html @@ -0,0 +1,275 @@ + + +GeoConcept Export (available from GDAL 1.6.0) + + + + +

    GeoConcept text export

    + +GeoConcept text export files should be available for writing and reading.

    + +The OGR GeoConcept driver treats a single GeoConcept file within +a directory as a dataset comprising layers. +GeoConcept files extensions are .txt or .gxt.

    + +Currently the GeoConcept driver only supports multi-polygons, lines and +points.

    + +

    GeoConcept Text File Format (gxt)

    + +GeoConcept is a GIS developped by the Company GeoConcept SA.

    + +It's an object oriented GIS, where the features are named « objects », +and feature types are named « type/subtype » (class allowing inheritance).

    + +Among its import/export formats, it proposes a simple text format +named gxt. A gxt file may contain objects from several type/subtype.

    + +GeoConcept text export files should be available for writing and reading.

    + +The OGR GeoConcept driver treats a single GeoConcept file within a +directory as a dataset comprising layers. GeoConcept files extensions +are .txt or .gxt.

    + +Currently the GeoConcept driver only supports multi-polygons, +lines and points.

    + +

    Creation Issues

    + +The GeoConcept driver treats a GeoConcept file (.txt +or .gxt) as a dataset.

    + +GeoConcept files can store multiple kinds of geometry (one by layer), +even if a GeoConcept layer can only have one kind of geometry.

    + +Note this makes it very difficult to translate a mixed geometry layer +from another format into GeoConcept format using ogr2ogr, since ogr2ogr has +no support for separating out geometries from a source layer.

    + +GeoConcept sub-type is treated as OGR feature. The name of a layer is therefore +the concatenation of the GeoConcept type name, '.' and GeoConcept +sub-type name.

    + +GeoConcept type definition (.gct files) are used for creation only.

    + +GeoConcept feature fields definition are stored in an associated .gct +file, and so fields suffer a number of limitations (FIXME) :

    + +

      +
    • Attribute names are not limited in length.
    • + +
    • Only Integer, Real and String field types are supported. The various +list, and other field types cannot be created for the moment (they exist in +the GeoConcept model, but are not yet supported by the GeoConcept +driver).
    • +
    + +The OGR GeoConcept driver does not support deleting features.

    + +

    Dataset Creation Options

    + +EXTENSION=TXT|GXT : indicates the GeoConcept export file extension. TXT +was used by earlier releases of GeoConcept. GXT is currently used.

    + +CONFIG=path to the GCT : the GCT file describe the GeoConcept types +definitions : In this file, every line must start with //# followed by +a keyword. Lines starting with // are comments.

    + +It is important to note that a GeoConcept export file can hold different types and +associated sub-types.

    + +

      +
    • configuration section : the GCT file starts with //#SECTION CONFIG +and ends with //#ENDSECTION CONFIG. All the configuration is enclosed within +these marks.
    • + +
    • map section : purely for documentation at the time of writing this document. +This section starts with //#SECTION MAP and ends with //#ENDSECTION MAP.
    • + +
    • type section : this section defines a class of features. A type has a name + (keyword Name) and an ID (keyword ID). A type holds sub-types and +fields. +This section starts with //#SECTION TYPE and ends with //#ENDSECTION TYPE. + +
        +
      • sub-type section : this sub-section defines a kind og features within a class. +A sub-type has a name (keyword Name), an ID (keyword ID), +a type of geometry (keyword Kind) and a dimension. The following types of + geometry are supported : POINT, LINE, POLYGON. The current release of this driver does not +support the TEXT geometry. The dimension can be 2D, 3DM or 3D. A sub-type holds fields. +This section starts with //#SECTION SUBTYPE and ends with //#ENDSECTION SUBTYPE. + +
          +
        • fields section : defines user fields. A field has a name (keyword Name), +an ID (keyword ID), a type (keyword Kind). The following types +of fields are supported : INT, REAL, MEMO, CHOICE, DATE, TIME, LENGTH, AREA. +This section starts with //#SECTION FIELD and ends with //#ENDSECTION FIELD.
        • + +
        +
      • + +
      • field section : defines type fields. See above.
      • + +
      +
    • + +
    • field section : defines general fields. Out of these, the following rules apply : + +
        + +
      • private field names start with a '@' : the private fields are Identifier, +Class, Subclass, Name, NbFields, X, +Y, XP, YP, Graphics, Angle.
      • + +
      • some private field are mandatory (they must appear in the configuration) : +Identifier, Class, Subclass, +Name, X, Y.
      • + +
      • If the sub-type is linear (LINE), then the following fields must be declared +XP, YP.
      • + +
      • If the sub-type is linear or polygonal (LINE, POLY), then Graphics +must be declared.
      • + +
      • If the sub-type is ponctual or textual (POINT, TEXT), the Angle +may be declared.
      • + +
      + +When this option is not used, the driver manage types and sub-types name based on either +the layer name or on the use of -nln option. +
    • + +
    + +

    Layer Creation Options

    + +FEATURETYPE=TYPE.SUBTYPE : defines the feature to be created. The TYPE +corresponds to one of the Name found in the GCT file for a type section. +The SUBTYPE corresponds to one of the Name found in the GCT file for a +sub-type section within the previous type section.

    + +At the present moment, coordinates are written with 2 decimales for cartesian +spatial reference systems (including height) or with 9 decimales for +geographical spatial reference systems.

    + +

    Examples

    + +

    Example of a .gct file :

    + +
    +//#SECTION CONFIG
    +//#SECTION MAP
    +//# Name=SCAN1000-TILES-LAMB93
    +//# Unit=m
    +//# Precision=1000
    +//#ENDSECTION MAP
    +//#SECTION TYPE
    +//# Name=TILE
    +//# ID=10
    +//#SECTION SUBTYPE
    +//# Name=TILE
    +//# ID=100
    +//# Kind=POLYGON
    +//# 3D=2D
    +//#SECTION FIELD
    +//# Name=IDSEL
    +//# ID=101
    +//# Kind=TEXT
    +//#ENDSECTION FIELD
    +//#SECTION FIELD
    +//# Name=NOM
    +//# ID=102
    +//# Kind=TEXT
    +//#ENDSECTION FIELD
    +//#SECTION FIELD
    +//# Name=WITHDATA
    +//# ID=103
    +//# Kind=INT
    +//#ENDSECTION FIELD
    +//#ENDSECTION SUBTYPE
    +//#ENDSECTION TYPE
    +//#SECTION FIELD
    +//# Name=@Identifier
    +//# ID=-1
    +//# Kind=INT
    +//#ENDSECTION FIELD
    +//#SECTION FIELD
    +//# Name=@Class
    +//# ID=-2
    +//# Kind=CHOICE
    +//#ENDSECTION FIELD
    +//#SECTION FIELD
    +//# Name=@Subclass
    +//# ID=-3
    +//# Kind=CHOICE
    +//#ENDSECTION FIELD
    +//#SECTION FIELD
    +//# Name=@Name
    +//# ID=-4
    +//# Kind=TEXT
    +//#ENDSECTION FIELD
    +//#SECTION FIELD
    +//# Name=@X
    +//# ID=-5
    +//# Kind=REAL
    +//#ENDSECTION FIELD
    +//#SECTION FIELD
    +//# Name=@Y
    +//# ID=-6
    +//# Kind=REAL
    +//#ENDSECTION FIELD
    +//#SECTION FIELD
    +//# Name=@Graphics
    +//# ID=-7
    +//# Kind=REAL
    +//#ENDSECTION FIELD
    +//#ENDSECTION CONFIG
    +
    + +

    Example of a GeoConcept text export :

    + +
    +//$DELIMITER "	"
    +//$QUOTED-TEXT "no"
    +//$CHARSET ANSI
    +//$UNIT Distance=m
    +//$FORMAT 2
    +//$SYSCOORD {Type: 2001}
    +//$FIELDS Class=TILE;Subclass=TILE;Kind=4;Fields=Private#Identifier	Private#Class	Private#Subclass	Private#Name	Private#NbFields	IDSEL	NOM	WITHDATA	Private#X	Private#Y	Private#Graphics
    +-1	TILE	TILE	TILE	3	000-2007-0050-7130-LAMB93		0	50000.00	7130000.00	4	600000.00	7130000.00	600000.00	6580000.00	50000.00	6580000.00	50000.00	7130000.00
    +-1	TILE	TILE	TILE	3	000-2007-0595-7130-LAMB93		0	595000.00	7130000.00	4	1145000.00	7130000.00	1145000.00	6580000.00	595000.00	6580000.00	595000.00	7130000.00
    +-1	TILE	TILE	TILE	3	000-2007-0595-6585-LAMB93		0	595000.00	6585000.00	4	1145000.00	6585000.00	1145000.00	6035000.00	595000.00	6035000.00	595000.00	6585000.00
    +-1	TILE	TILE	TILE	3	000-2007-1145-6250-LAMB93		0	1145000.00	6250000.00	4	1265000.00	6250000.00	1265000.00	6030000.00	1145000.00	6030000.00	1145000.00	6250000.00
    +-1	TILE	TILE	TILE	3	000-2007-0050-6585-LAMB93		0	50000.00	6585000.00	4	600000.00	6585000.00	600000.00	6035000.00	50000.00	6035000.00	50000.00	6585000.00
    +
    + +

    Example of use :

    + +Creating a GeoConcept export file :
    + +
    +ogr2ogr -f "Geoconcept" -a_srs "+init=IGNF:LAMB93" -dsco EXTENSION=txt -dsco CONFIG=tile_schema.gct tile.gxt tile.shp -lco FEATURETYPE=TILE.TILE
    +
    + +Appending new features to an existing GeoConcept export file :
    + +
    +ogr2ogr -f "Geoconcept" -update -append tile.gxt tile.shp -nln TILE.TILE
    +
    + +Translating a GeoConcept export file layer into MapInfo file :
    + +
    +ogr2ogr -f "MapInfo File" -dsco FORMAT=MIF tile.mif tile.gxt TILE.TILE
    +
    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/geoconcept.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/geoconcept.c new file mode 100644 index 000000000..0273865cf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/geoconcept.c @@ -0,0 +1,5386 @@ +/********************************************************************** + * $Id: geoconcept.c + * + * Name: geoconcept.c + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements Physical Access class. + * Language: C + * + ********************************************************************** + * Copyright (c) 2007, Geoconcept and IGN + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + **********************************************************************/ + +#include +#include "geoconcept.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_core.h" + +CPL_CVSID("$Id: geoconcept.c,v 1.0.0 2007-11-03 20:58:19 drichard Exp $") + +#define kItemSize_GCIO 256 +#define kExtraSize_GCIO 4096 +#define kIdSize_GCIO 12 +#define UNDEFINEDID_GCIO 199901L + +static char* gkGCCharset[]= +{ +/* 0 */ "", +/* 1 */ "ANSI", +/* 2 */ "DOS", +/* 3 */ "MAC" +}; + +static char* gkGCAccess[]= +{ +/* 0 */ "", +/* 1 */ "NO", +/* 2 */ "READ", +/* 3 */ "UPDATE", +/* 4 */ "WRITE" +}; + +static char* gkGCStatus[]= +{ +/* 0 */ "NONE", +/* 1 */ "MEMO", +/* 2 */ "EOF" +}; + +static char* gk3D[]= +{ +/* 0 */ "", +/* 1 */ "2D", +/* 2 */ "3DM", +/* 3 */ "3D" +}; + +static char* gkGCTypeKind[]= +{ +/* 0 */ "", +/* 1 */ "POINT", +/* 2 */ "LINE", +/* 3 */ "TEXT", +/* 4 */ "POLYGON", +/* 5 */ "MEMO", +/* 6 */ "INT", +/* 7 */ "REAL", +/* 8 */ "LENGTH", +/* 9 */ "AREA", +/*10 */ "POSITION", +/*11 */ "DATE", +/*12 */ "TIME", +/*13 */ "CHOICE", +/*14 */ "MEMO" +}; + +/* -------------------------------------------------------------------- */ +/* GCIO API Prototypes */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +static char GCIOAPI_CALL1(*) _getHeaderValue_GCIO ( const char *s ) +{ + char *b, *e; + + if( (b= strchr(s,'='))==NULL ) return NULL; + b++; + while (isspace((unsigned char)*b)) b++; + e= b; + while (*e!='\0' && !isspace((unsigned char)*e)) e++; + *e= '\0'; + return b; +}/* _getHeaderValue_GCIO */ + +/* -------------------------------------------------------------------- */ +const char GCIOAPI_CALL1(*) GCCharset2str_GCIO ( GCCharset cs ) +{ + switch (cs) { + case vANSI_GCIO : + case vDOS_GCIO : + case vMAC_GCIO : + return gkGCCharset[cs]; + default : + return gkGCCharset[vUnknownCharset_GCIO]; + } +}/* GCCharset2str_GCIO */ + +/* -------------------------------------------------------------------- */ +GCCharset GCIOAPI_CALL str2GCCharset_GCIO ( const char* s) +{ + if (strcmp(s,gkGCCharset[vANSI_GCIO])==0) return vANSI_GCIO; + if (strcmp(s,gkGCCharset[vDOS_GCIO])==0) return vDOS_GCIO; + if (strcmp(s,gkGCCharset[vMAC_GCIO])==0) return vMAC_GCIO; + return vUnknownCharset_GCIO; +}/* str2GCCharset_GCIO */ + +/* -------------------------------------------------------------------- */ +const char GCIOAPI_CALL1(*) GCAccessMode2str_GCIO ( GCAccessMode mode ) +{ + switch (mode) { + case vNoAccess_GCIO : + case vReadAccess_GCIO : + case vUpdateAccess_GCIO : + case vWriteAccess_GCIO : + return gkGCAccess[mode]; + default : + return gkGCAccess[vUnknownAccessMode_GCIO]; + } +}/* GCAccessMode2str_GCIO */ + +/* -------------------------------------------------------------------- */ +GCAccessMode GCIOAPI_CALL str2GCAccessMode_GCIO ( const char* s) +{ + if (strcmp(s,gkGCAccess[vNoAccess_GCIO])==0) return vNoAccess_GCIO; + if (strcmp(s,gkGCAccess[vReadAccess_GCIO])==0) return vReadAccess_GCIO; + if (strcmp(s,gkGCAccess[vUpdateAccess_GCIO])==0) return vUpdateAccess_GCIO; + if (strcmp(s,gkGCAccess[vWriteAccess_GCIO])==0) return vWriteAccess_GCIO; + return vUnknownAccessMode_GCIO; +}/* str2GCAccessMode_GCIO */ + +/* -------------------------------------------------------------------- */ +const char GCIOAPI_CALL1(*) GCAccessStatus2str_GCIO ( GCAccessStatus stts ) +{ + switch (stts) { + case vMemoStatus_GCIO : + case vEof_GCIO : + return gkGCStatus[stts]; + default : + return gkGCStatus[vNoStatus_GCIO]; + } +}/* GCAccessStatus2str_GCIO */ + +/* -------------------------------------------------------------------- */ +GCAccessStatus GCIOAPI_CALL str2GCAccessStatus_GCIO ( const char* s) +{ + if (strcmp(s,gkGCStatus[vMemoStatus_GCIO])==0) return vMemoStatus_GCIO; + if (strcmp(s,gkGCStatus[vEof_GCIO])==0) return vEof_GCIO; + return vNoStatus_GCIO; +}/* str2GCAccessStatus_GCIO */ + +/* -------------------------------------------------------------------- */ +const char GCIOAPI_CALL1(*) GCDim2str_GCIO ( GCDim sys ) +{ + switch (sys) { + case v2D_GCIO : + case v3D_GCIO : + case v3DM_GCIO : + return gk3D[sys]; + default : + return gk3D[vUnknown3D_GCIO]; + } +}/* GCDim2str_GCIO */ + +/* -------------------------------------------------------------------- */ +GCDim GCIOAPI_CALL str2GCDim ( const char* s ) +{ + if (strcmp(s,gk3D[v2D_GCIO])==0) return v2D_GCIO; + if (strcmp(s,gk3D[v3D_GCIO])==0) return v3D_GCIO; + if (strcmp(s,gk3D[v3DM_GCIO])==0) return v3DM_GCIO; + return vUnknown3D_GCIO; +}/* str2GCDim */ + +/* -------------------------------------------------------------------- */ +const char GCIOAPI_CALL1(*) GCTypeKind2str_GCIO ( GCTypeKind item ) +{ + switch (item) { + case vPoint_GCIO : + case vLine_GCIO : + case vText_GCIO : + case vPoly_GCIO : + case vMemoFld_GCIO : + case vIntFld_GCIO : + case vRealFld_GCIO : + case vLengthFld_GCIO : + case vAreaFld_GCIO : + case vPositionFld_GCIO : + case vDateFld_GCIO : + case vTimeFld_GCIO : + case vChoiceFld_GCIO : + case vInterFld_GCIO : + return gkGCTypeKind[item]; + default : + return gkGCTypeKind[vUnknownItemType_GCIO]; + } +}/* GCTypeKind2str_GCIO */ + +/* -------------------------------------------------------------------- */ +GCTypeKind GCIOAPI_CALL str2GCTypeKind_GCIO ( const char *s ) +{ + if (strcmp(s,gkGCTypeKind[vPoint_GCIO])==0) return vPoint_GCIO; + if (strcmp(s,gkGCTypeKind[vLine_GCIO])==0) return vLine_GCIO; + if (strcmp(s,gkGCTypeKind[vText_GCIO])==0) return vText_GCIO; + if (strcmp(s,gkGCTypeKind[vPoly_GCIO])==0) return vPoly_GCIO; + if (strcmp(s,gkGCTypeKind[vMemoFld_GCIO])==0) return vMemoFld_GCIO; + if (strcmp(s,gkGCTypeKind[vIntFld_GCIO])==0) return vIntFld_GCIO; + if (strcmp(s,gkGCTypeKind[vRealFld_GCIO])==0) return vRealFld_GCIO; + if (strcmp(s,gkGCTypeKind[vLengthFld_GCIO])==0) return vLengthFld_GCIO; + if (strcmp(s,gkGCTypeKind[vAreaFld_GCIO])==0) return vAreaFld_GCIO; + if (strcmp(s,gkGCTypeKind[vPositionFld_GCIO])==0) return vPositionFld_GCIO; + if (strcmp(s,gkGCTypeKind[vDateFld_GCIO])==0) return vDateFld_GCIO; + if (strcmp(s,gkGCTypeKind[vTimeFld_GCIO])==0) return vTimeFld_GCIO; + if (strcmp(s,gkGCTypeKind[vChoiceFld_GCIO])==0) return vChoiceFld_GCIO; + if (strcmp(s,gkGCTypeKind[vInterFld_GCIO])==0) return vInterFld_GCIO; + return vUnknownItemType_GCIO; +}/* str2GCTypeKind_GCIO */ + +/* -------------------------------------------------------------------- */ +static const char GCIOAPI_CALL1(*) _metaDelimiter2str_GCIO ( char delim ) +{ + switch( delim ) { + case '\t' : + return "tab"; + default : + return "\t"; + } +}/* _metaDelimiter2str_GCIO */ + +/* -------------------------------------------------------------------- */ +static long GCIOAPI_CALL _read_GCIO ( + GCExportFileH* hGXT + ) +{ + FILE* h; + long nread; + int c; + char *result; + + h= GetGCHandle_GCIO(hGXT); + nread= 0L; + result= GetGCCache_GCIO(hGXT); + SetGCCurrentOffset_GCIO(hGXT, VSIFTell(h));/* keep offset of beginning of lines */ + while ((c= VSIFGetc(h))!=EOF) + { + c= (0x00FF & (unsigned char)(c)); + switch (c) { + case 0X1A : continue ; /* PC end-of-file */ + case '\r' : /* PC '\r\n' line, MAC '\r' */ + if ((c= VSIFGetc(h))!='\n') + { + VSIUngetc(c,h); + c= '\n'; + } + case '\n' : + SetGCCurrentLinenum_GCIO(hGXT,GetGCCurrentLinenum_GCIO(hGXT)+1L); + if (nread==0L) continue; + *result= '\0'; + return nread; + default : + *result= (char)c; + result++; + nread++; + if (nread == kCacheSize_GCIO) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Too many characters at line %lu.\n", GetGCCurrentLinenum_GCIO(hGXT)); + return EOF; + } + }/* switch */ + }/* while */ + *result= '\0'; + if (c==EOF) + { + SetGCStatus_GCIO(hGXT, vEof_GCIO); + if (nread==0L) + { + return EOF; + } + } + + return nread; +}/* _read_GCIO */ + +/* -------------------------------------------------------------------- */ +static long GCIOAPI_CALL _get_GCIO ( + GCExportFileH* hGXT + ) +{ + if (GetGCStatus_GCIO(hGXT)==vEof_GCIO) + { + SetGCCache_GCIO(hGXT,""); + SetGCWhatIs_GCIO(hGXT, vUnknownIO_ItemType_GCIO); + return EOF; + } + if (GetGCStatus_GCIO(hGXT)==vMemoStatus_GCIO) + { + SetGCStatus_GCIO(hGXT, vNoStatus_GCIO); + return GetGCCurrentOffset_GCIO(hGXT); + } + if (_read_GCIO(hGXT)==EOF) + { + SetGCWhatIs_GCIO(hGXT, vUnknownIO_ItemType_GCIO); + return EOF; + } + SetGCWhatIs_GCIO(hGXT, vStdCol_GCIO); + if (strstr(GetGCCache_GCIO(hGXT),kCom_GCIO)==GetGCCache_GCIO(hGXT)) + { /* // */ + SetGCWhatIs_GCIO(hGXT, vComType_GCIO); + if (strstr(GetGCCache_GCIO(hGXT),kHeader_GCIO)==GetGCCache_GCIO(hGXT)) + { /* //# */ + SetGCWhatIs_GCIO(hGXT, vHeader_GCIO); + } + else + { + if (strstr(GetGCCache_GCIO(hGXT),kPragma_GCIO)==GetGCCache_GCIO(hGXT)) + { /* //$ */ + SetGCWhatIs_GCIO(hGXT, vPragma_GCIO); + } + } + } + return GetGCCurrentOffset_GCIO(hGXT); +}/* _get_GCIO */ + +/* -------------------------------------------------------------------- */ +static void GCIOAPI_CALL _InitExtent_GCIO ( + GCExtent* theExtent + ) +{ + theExtent->XUL= HUGE_VAL; + theExtent->YUL= -HUGE_VAL; + theExtent->XLR= -HUGE_VAL; + theExtent->YLR= HUGE_VAL; +}/* _InitExtent_GCIO */ + +/* -------------------------------------------------------------------- */ +GCExtent GCIOAPI_CALL1(*) CreateExtent_GCIO ( + double Xmin, + double Ymin, + double Xmax, + double Ymax + ) +{ + GCExtent* theExtent; + + if( !(theExtent= CPLMalloc(sizeof(GCExtent))) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to create a Geoconcept extent for '[%g %g,%g %g]'.\n", + Xmin, Ymin,Xmax, Ymax); + return NULL; + } + _InitExtent_GCIO(theExtent); + theExtent->XUL= Xmin; + theExtent->YUL= Ymax; + theExtent->XLR= Xmax; + theExtent->YLR= Ymin; + + return theExtent; +}/* CreateExtent_GCIO */ + +/* -------------------------------------------------------------------- */ +static void GCIOAPI_CALL _ReInitExtent_GCIO ( + GCExtent* theExtent + ) +{ + _InitExtent_GCIO(theExtent); +}/* _ReInitExtent_GCIO */ + +/* -------------------------------------------------------------------- */ +void GCIOAPI_CALL DestroyExtent_GCIO ( + GCExtent** theExtent + ) +{ + _ReInitExtent_GCIO(*theExtent); + CPLFree(*theExtent); + *theExtent= NULL; +}/* DestroyExtent_GCIO */ + +/* -------------------------------------------------------------------- */ +static void GCIOAPI_CALL _InitField_GCIO ( + GCField* theField + ) +{ + SetFieldName_GCIO(theField, NULL); + SetFieldID_GCIO(theField, UNDEFINEDID_GCIO); + SetFieldKind_GCIO(theField, vUnknownItemType_GCIO); + SetFieldExtra_GCIO(theField, NULL); + SetFieldList_GCIO(theField, NULL); +}/* _InitField_GCIO */ + +/* -------------------------------------------------------------------- */ +static const char GCIOAPI_CALL1(*) _NormalizeFieldName_GCIO ( + const char* name + ) +{ + if( name[0]=='@' ) + { + if( EQUAL(name, "@Identificateur") || EQUAL(name, kIdentifier_GCIO) ) + { + return kIdentifier_GCIO; + } + else if( EQUAL(name, "@Type") || EQUAL(name, kClass_GCIO) ) + { + return kClass_GCIO; + } + else if( EQUAL(name, "@Sous-type") || EQUAL(name, kSubclass_GCIO) ) + { + return kSubclass_GCIO; + } + else if( EQUAL(name, "@Nom") || EQUAL(name, kName_GCIO) ) + { + return kName_GCIO; + } + else if( EQUAL(name, kNbFields_GCIO) ) + { + return kNbFields_GCIO; + } + else if( EQUAL(name, kX_GCIO) ) + { + return kX_GCIO; + } + else if( EQUAL(name, kY_GCIO) ) + { + return kY_GCIO; + } + else if( EQUAL(name, "@X'") || EQUAL(name, kXP_GCIO) ) + { + return kXP_GCIO; + } + else if( EQUAL(name, "@Y'") || EQUAL(name, kYP_GCIO) ) + { + return kYP_GCIO; + } + else if( EQUAL(name, kGraphics_GCIO) ) + { + return kGraphics_GCIO; + } + else if( EQUAL(name, kAngle_GCIO) ) + { + return kAngle_GCIO; + } + else + { + return name; + } + } + else + { + return name; + } +}/* _NormalizeFieldName_GCIO */ + +/* -------------------------------------------------------------------- */ +static GCField GCIOAPI_CALL1(*) _CreateField_GCIO ( + const char* name, + long id, + GCTypeKind knd, + const char* extra, + const char* enums + ) +{ + GCField* theField; + + if( !(theField= CPLMalloc(sizeof(GCField))) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to create a Geoconcept field for '%s'.\n", + name); + return NULL; + } + _InitField_GCIO(theField); + SetFieldName_GCIO(theField, CPLStrdup(name)); + SetFieldID_GCIO(theField, id); + SetFieldKind_GCIO(theField, knd); + if( extra && extra[0]!='\0' ) SetFieldExtra_GCIO(theField, CPLStrdup(extra)); + if( enums && enums[0]!='\0' ) SetFieldList_GCIO(theField, CSLTokenizeString2(enums,";",0)); + + return theField; +}/* _CreateField_GCIO */ + +/* -------------------------------------------------------------------- */ +static void GCIOAPI_CALL _ReInitField_GCIO ( + GCField* theField + ) +{ + if( GetFieldName_GCIO(theField) ) + { + CPLFree(GetFieldName_GCIO(theField)); + } + if( GetFieldExtra_GCIO(theField) ) + { + CPLFree( GetFieldExtra_GCIO(theField) ); + } + if( GetFieldList_GCIO(theField) ) + { + CSLDestroy( GetFieldList_GCIO(theField) ); + } + _InitField_GCIO(theField); +}/* _ReInitField_GCIO */ + +/* -------------------------------------------------------------------- */ +static void GCIOAPI_CALL _DestroyField_GCIO ( + GCField** theField + ) +{ + _ReInitField_GCIO(*theField); + CPLFree(*theField); + *theField= NULL; +}/* _DestroyField_GCIO */ + +/* -------------------------------------------------------------------- */ +static int GCIOAPI_CALL _findFieldByName_GCIO ( + CPLList* fields, + const char* name + ) +{ + GCField* theField; + + if( fields ) + { + CPLList* e; + int n, i; + if( (n= CPLListCount(fields))>0 ) + { + for( i= 0; i0 ) + { + for (i= 0; i0 ) + { + if( *subtypName=='*' ) return 0; + for( i = 0; i < n; i++) + { + if( (e= CPLListGet(GetTypeSubtypes_GCIO(theClass),i)) ) + { + if( (theSubType= (GCSubType*)CPLListGetData(e)) ) + { + if( EQUAL(GetSubTypeName_GCIO(theSubType),subtypName) ) + { + return i; + } + } + } + } + } + } + return -1; +}/* _findSubTypeByName_GCIO */ + +/* -------------------------------------------------------------------- */ +static GCSubType GCIOAPI_CALL1(*) _getSubType_GCIO ( + GCType* theClass, + int where + ) +{ + CPLList* e; + + if( (e= CPLListGet(GetTypeSubtypes_GCIO(theClass),where)) ) + return (GCSubType*)CPLListGetData(e); + return NULL; +}/* _getSubType_GCIO */ + +/* -------------------------------------------------------------------- */ +static void GCIOAPI_CALL _InitType_GCIO ( + GCType* theClass + ) +{ + SetTypeName_GCIO(theClass, NULL); + SetTypeSubtypes_GCIO(theClass, NULL);/* GCSubType */ + SetTypeFields_GCIO(theClass, NULL); /* GCField */ + SetTypeID_GCIO(theClass, UNDEFINEDID_GCIO); +}/* _InitType_GCIO */ + +/* -------------------------------------------------------------------- */ +static GCType GCIOAPI_CALL1(*) _CreateType_GCIO ( + const char* typName, + long id + ) +{ + GCType* theClass; + + if( !(theClass= CPLMalloc(sizeof(GCType))) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to create a Geoconcept type for '%s#%ld'.\n", + typName, id); + return NULL; + } + _InitType_GCIO(theClass); + SetTypeName_GCIO(theClass, CPLStrdup(typName)); + SetTypeID_GCIO(theClass, id); + + return theClass; +}/* _CreateType_GCIO */ + +/* -------------------------------------------------------------------- */ +static void GCIOAPI_CALL _ReInitType_GCIO ( + GCType* theClass + ) +{ + if( GetTypeSubtypes_GCIO(theClass) ) + { + CPLList* e; + GCSubType* theSubType; + int i, n; + if( (n= CPLListCount(GetTypeSubtypes_GCIO(theClass)))>0 ) + { + for (i= 0; i0 ) + { + for (i= 0; i0 ) + { + if( *typName=='*' ) return 0; + for( i = 0; i < n; i++) + { + if( (e= CPLListGet(GetMetaTypes_GCIO(header),i)) ) + { + if( (theClass= (GCType*)CPLListGetData(e)) ) + { + if( EQUAL(GetTypeName_GCIO(theClass),typName) ) + { + return i; + } + } + } + } + } + } + return -1; +}/* _findTypeByName_GCIO */ + +/* -------------------------------------------------------------------- */ +static GCType GCIOAPI_CALL1(*) _getType_GCIO ( + GCExportFileH* hGXT, + int where + ) +{ + CPLList* e; + + if( (e= CPLListGet(GetMetaTypes_GCIO(GetGCMeta_GCIO(hGXT)),where)) ) + return (GCType*)CPLListGetData(e); + return NULL; +}/* _getType_GCIO */ + +/* -------------------------------------------------------------------- */ +static void GCIOAPI_CALL _InitHeader_GCIO ( + GCExportFileMetadata* header + ) +{ + SetMetaVersion_GCIO(header, NULL); + SetMetaDelimiter_GCIO(header, kTAB_GCIO[0]); + SetMetaQuotedText_GCIO(header, FALSE); + SetMetaCharset_GCIO(header, vANSI_GCIO); + SetMetaUnit_GCIO(header, "m"); + SetMetaFormat_GCIO(header, 2); + SetMetaSysCoord_GCIO(header, NULL); /* GCSysCoord */ + SetMetaPlanarFormat_GCIO(header, 0); + SetMetaHeightFormat_GCIO(header, 0); + SetMetaSRS_GCIO(header, NULL); + SetMetaTypes_GCIO(header, NULL); /* GCType */ + SetMetaFields_GCIO(header, NULL); /* GCField */ + SetMetaResolution_GCIO(header, 0.1); + SetMetaExtent_GCIO(header, NULL); +}/* _InitHeader_GCIO */ + +/* -------------------------------------------------------------------- */ +GCExportFileMetadata GCIOAPI_CALL1(*) CreateHeader_GCIO ( ) +{ + GCExportFileMetadata* m; + + if( !(m= CPLMalloc(sizeof(GCExportFileMetadata)) ) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to create Geoconcept metadata.\n"); + return NULL; + } + _InitHeader_GCIO(m); + + return m; +}/* CreateHeader_GCIO */ + +/* -------------------------------------------------------------------- */ +static void GCIOAPI_CALL _ReInitHeader_GCIO ( + GCExportFileMetadata* header + ) +{ + if( GetMetaVersion_GCIO(header) ) + { + CPLFree( GetMetaVersion_GCIO(header) ); + } + if( GetMetaExtent_GCIO(header) ) + { + DestroyExtent_GCIO(&(GetMetaExtent_GCIO(header))); + } + if( GetMetaTypes_GCIO(header) ) + { + CPLList* e; + GCType* theClass; + int i, n; + if( (n= CPLListCount(GetMetaTypes_GCIO(header)))>0 ) + { + for (i= 0; i0 ) + { + for (i= 0; i4 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Geometry type.\n" + "Geoconcept export syntax error at line %ld.\n", + GetGCCurrentLinenum_GCIO(hGXT) ); + CPLFree(nm); + CSLDestroy(vl); + CSLDestroy(kv); + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGXT))); + return NULL; + } + CSLDestroy(vl); + if( !(theSubType= AddSubType_GCIO(hGXT,GetTypeName_GCIO(theClass), + nm, + -1, + (GCTypeKind)v, + vUnknown3D_GCIO)) ) + { + CPLFree(nm); + CSLDestroy(kv); + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGXT))); + CPLError( CE_Failure, CPLE_AppDefined, + "Geoconcept export syntax error at line %ld.\n", + GetGCCurrentLinenum_GCIO(hGXT) ); + return NULL; + } + CPLFree(nm); + /* Fields=(Private#)?char*(\s((Private#)?char*))* */ + vl= CSLTokenizeString2(kv[3],"=",mask); + if( !vl || CSLCount(vl)!=2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Expected: Fields=(Private#)?char*(\\t((Private#)?char*))*\n" + "Found: [%s]\n" + "Geoconcept export syntax error at line %ld.\n", + kv[3], + GetGCCurrentLinenum_GCIO(hGXT) ); + CSLDestroy(vl); + CSLDestroy(kv); + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGXT))); + return NULL; + } + for (i=0; i<2; i++) CPLDebug("GEOCONCEPT", "%d vl[%d]=[%s]\n", __LINE__, i, vl[i]); + CSLDestroy(kv); + p= vl[0]; + if( !EQUAL(p, "Fields") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Expected: 'Fields'\n" + "Found: [%s]\n" + "Geoconcept export syntax error at line %ld.\n", + p, + GetGCCurrentLinenum_GCIO(hGXT) ); + CSLDestroy(vl); + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGXT))); + return NULL; + } + fl= CSLTokenizeString2(vl[1],"\t",mask); + if( !fl || (n= CSLCount(fl))==0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Expected: (Private#)?char*(\\t((Private#)?char*))*\n" + "Found: [%s]\n" + "Geoconcept export syntax error at line %ld.\n", + vl[1], + GetGCCurrentLinenum_GCIO(hGXT) ); + CSLDestroy(fl); + CSLDestroy(vl); + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGXT))); + return NULL; + } + CSLDestroy(vl); + for (i= 0; iY[<>Z]{<>More Graphics} + */ + + if( gt==wkbPoint ) + { + /* + * More Graphics : + * Angle + * Angle in tenth of degrees (counterclockwise) of the symbol + * displayed to represent the ponctual entity or angle of the text entity + * NOT IMPLEMENTED + */ + x= CPLAtof(pszFields[i]), i++; + y= CPLAtof(pszFields[i]), i++; + if( d==v3D_GCIO||d==v3DM_GCIO ) + { + z= CPLAtof(pszFields[i]), i++; + } + if( buildGeom ) + { + if( OGR_G_GetCoordinateDimension(g)==3 ) + OGR_G_AddPoint(g,x,y,z); + else + OGR_G_AddPoint_2D(g,x,y); + } + else + { + MergeOGREnvelope_GCIO(bbox,x,y); + } + return g; + } + + if( gt==wkbLineString ) + { + /* + * More Graphics : + * XP<>YP[<>ZP]Nr points=k[<>X<>Y[<>Z]]k... + */ + x= CPLAtof(pszFields[i]), i++; + y= CPLAtof(pszFields[i]), i++; + if( d==v3D_GCIO||d==v3DM_GCIO ) + { + z= CPLAtof(pszFields[i]), i++; + } + if( buildGeom ) + { + if( OGR_G_GetCoordinateDimension(g)==3 ) + OGR_G_AddPoint(g,x,y,z); + else + OGR_G_AddPoint_2D(g,x,y); + } + else + { + MergeOGREnvelope_GCIO(bbox,x,y); + } + /* skip XP<>YP[<>ZP] : the last point is in k[<>X<>Y[<>Z]]k */ + i++; + i++; + if( d==v3D_GCIO||d==v3DM_GCIO ) + { + i++; + } + np= atoi(pszFields[i]), i++; + for( ip= 1; ip<=np; ip++ ) + { + x= CPLAtof(pszFields[i]), i++; + y= CPLAtof(pszFields[i]), i++; + if( d==v3D_GCIO || d==v3DM_GCIO ) + { + z= CPLAtof(pszFields[i]), i++; + } + if( buildGeom ) + { + if( OGR_G_GetCoordinateDimension(g)==3 ) + OGR_G_AddPoint(g,x,y,z); + else + OGR_G_AddPoint_2D(g,x,y); + } + else + { + MergeOGREnvelope_GCIO(bbox,x,y); + } + } + return g; + } + + if( gt==wkbMultiPolygon ) + { + /* + * More Graphics : + * {Single Polygon{<>NrPolys=j[<>X<>Y[<>Z]<>Single Polygon]j}} + * with Single Polygon : + * Nr points=k[<>X<>Y[<>Z]]k... + */ + CPLList* Lpo, *e; + OGRGeometryH outer, ring; + int npo, ipo, ilpo; + + + Lpo= e= NULL; + outer= ring= NULL; + if( buildGeom ) + { + if( !(outer= OGR_G_CreateGeometry(wkbPolygon)) ) + { + goto onError; + } + OGR_G_SetCoordinateDimension(outer,OGR_G_GetCoordinateDimension(g)); + if( GetMetaSRS_GCIO(Meta) ) + { + OGR_G_AssignSpatialReference(outer,GetMetaSRS_GCIO(Meta)); + } + if( !(ring= OGR_G_CreateGeometry(wkbLinearRing)) ) + { + OGR_G_DestroyGeometry(outer); + goto onError; + } + OGR_G_SetCoordinateDimension(ring,OGR_G_GetCoordinateDimension(g)); + if( GetMetaSRS_GCIO(Meta) ) + { + OGR_G_AssignSpatialReference(ring,GetMetaSRS_GCIO(Meta)); + } + } + x= CPLAtof(pszFields[i]), i++; + y= CPLAtof(pszFields[i]), i++; + if( d==v3D_GCIO||d==v3DM_GCIO ) + { + z= CPLAtof(pszFields[i]), i++; + } + if( buildGeom ) + { + if( OGR_G_GetCoordinateDimension(g)==3 ) + OGR_G_AddPoint(ring,x,y,z); + else + OGR_G_AddPoint_2D(ring,x,y); + } + else + { + MergeOGREnvelope_GCIO(bbox,x,y); + } + np= atoi(pszFields[i]), i++; + for( ip= 1; ip<=np; ip++ ) + { + x= CPLAtof(pszFields[i]), i++; + y= CPLAtof(pszFields[i]), i++; + if( d==v3D_GCIO||d==v3DM_GCIO ) + { + z= CPLAtof(pszFields[i]), i++; + } + if( buildGeom ) + { + if( OGR_G_GetCoordinateDimension(g)==3 ) + OGR_G_AddPoint(ring,x,y,z); + else + OGR_G_AddPoint_2D(ring,x,y); + } + else + { + MergeOGREnvelope_GCIO(bbox,x,y); + } + } + if( buildGeom ) + { + OGR_G_AddGeometryDirectly(outer,ring); + if( (Lpo= CPLListAppend(Lpo,outer))==NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to add a polygon to subtype '%s.%s'.\n", + GetTypeName_GCIO(GetSubTypeType_GCIO(theSubType)), + GetSubTypeName_GCIO(theSubType) ); + OGR_G_DestroyGeometry(outer); + goto onError; + } + } + /* additionnal ring : either holes, or islands */ + if( i < nbtp-1 ) + { + npo= atoi(pszFields[i]), i++; + for( ipo= 1; ipo<=npo; ipo++ ) + { + if( buildGeom ) + { + if( !(ring= OGR_G_CreateGeometry(wkbLinearRing)) ) + { + goto onError; + } + OGR_G_SetCoordinateDimension(ring,OGR_G_GetCoordinateDimension(g)); + if( GetMetaSRS_GCIO(Meta) ) + { + OGR_G_AssignSpatialReference(ring,GetMetaSRS_GCIO(Meta)); + } + } + x= CPLAtof(pszFields[i]), i++; + y= CPLAtof(pszFields[i]), i++; + if( d==v3D_GCIO||d==v3DM_GCIO ) + { + z= CPLAtof(pszFields[i]), i++; + } + if( buildGeom ) + { + if( OGR_G_GetCoordinateDimension(g)==3 ) + OGR_G_AddPoint(ring,x,y,z); + else + OGR_G_AddPoint_2D(ring,x,y); + } + else + { + MergeOGREnvelope_GCIO(bbox,x,y); + } + np= atoi(pszFields[i]), i++; + for( ip= 1; ip<=np; ip++ ) + { + x= CPLAtof(pszFields[i]), i++; + y= CPLAtof(pszFields[i]), i++; + if( d==v3D_GCIO||d==v3DM_GCIO ) + { + z= CPLAtof(pszFields[i]), i++; + } + if( buildGeom ) + { + if( OGR_G_GetCoordinateDimension(g)==3 ) + OGR_G_AddPoint(ring,x,y,z); + else + OGR_G_AddPoint_2D(ring,x,y); + } + else + { + MergeOGREnvelope_GCIO(bbox,x,y); + } + } + if( buildGeom ) + { + /* is the ring of hole or another polygon ? */ + for( ilpo= 0; ilpo0 ) + { + for (ipo= 0; ipo0 ) + { + for (ipo= 0; ipo */ + /* Class */ + /* Subclass */ + /* Name */ + /* NbFields */ + /* User's field [0..N] */ + /* Graphics */ + /* Graphics depends on the Kind of the */ + /* B.- Algorithm : */ + /* 1.- Get Class */ + /* 2.- Get Subclass */ + /* 3.- Find feature in schema */ + /* 4.- Get Kind */ + /* 5.- Get NbFields */ + /* 6.- Get Geometry as 5+NbFields field */ + /* 7.- Parse Geometry and build OGRGeometryH */ + /* 8.- Compute extent and update file extent */ + /* 9.- increment number of features */ + /* FIXME : add index when reading feature to */ + /* allow direct access ! */ + if( GetMetaQuotedText_GCIO(Meta) ) + { + bTokenBehaviour|= CSLT_HONOURSTRINGS; + } + CPLDebug("GEOCONCEPT","Cache=[%s] delim=[%s]", GetGCCache_GCIO(H), delim); + if( !(pszFields= CSLTokenizeString2(GetGCCache_GCIO(H), + delim, + bTokenBehaviour)) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %ld, Geoconcept line syntax is wrong.\n", + GetGCCurrentLinenum_GCIO(H) ); + return NULL; + } + if( (nbtf= CSLCount(pszFields)) <= 5 ) + { + CSLDestroy(pszFields); + CPLError( CE_Failure, CPLE_AppDefined, + "Line %ld, Missing fields (at least 5 are expected, %d found).\n", + GetGCCurrentLinenum_GCIO(H), nbtf ); + return NULL; + } + /* Class */ + if( (whereClass = _findTypeByName_GCIO(H,pszFields[1]))==-1 ) + { + if( CPLListCount(GetMetaTypes_GCIO(Meta))==0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %ld, %s%s pragma expected fro type definition before objects dump.", + GetGCCurrentLinenum_GCIO(H), kPragma_GCIO, kMetadataFIELDS_GCIO ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %ld, Unknown type '%s'.\n", + GetGCCurrentLinenum_GCIO(H), pszFields[1] ); + } + CSLDestroy(pszFields); + return NULL; + } + theClass= _getType_GCIO(H,whereClass); + if( *theSubType ) + { + /* reading ... */ + if( !EQUAL(GetTypeName_GCIO(GetSubTypeType_GCIO(*theSubType)),GetTypeName_GCIO(theClass)) ) + { + CSLDestroy(pszFields); + return NULL; + } + } + /* Subclass */ + if( (whereSubType= _findSubTypeByName_GCIO(theClass,pszFields[2]))==-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %ld, Unknown subtype found '%s' for type '%s'.\n", + GetGCCurrentLinenum_GCIO(H), pszFields[2], pszFields[1] ); + CSLDestroy(pszFields); + return NULL; + } + if( *theSubType ) + { + if( !EQUAL(GetSubTypeName_GCIO(_getSubType_GCIO(theClass,whereSubType)),GetSubTypeName_GCIO(*theSubType)) ) + { + CSLDestroy(pszFields); + return NULL; + } + } + else + { + *theSubType= _getSubType_GCIO(theClass,whereSubType); + } + snprintf(tdst, kItemSize_GCIO-1, "%s.%s", GetTypeName_GCIO(theClass), GetSubTypeName_GCIO(*theSubType)); + tdst[kItemSize_GCIO-1]= '\0'; + /* Name */ + if( _findFieldByName_GCIO(GetSubTypeFields_GCIO(*theSubType),kName_GCIO)==-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %ld, missing mandatory field %s for type '%s'.\n", + GetGCCurrentLinenum_GCIO(H), kName_GCIO, tdst ); + CSLDestroy(pszFields); + return NULL; + } + nbf= 4; + /* NbFields */ + nbstf= GetSubTypeNbFields_GCIO(*theSubType); + if( nbstf==-1 ) + { + /* figure out how many user's attributes we've got : */ + i= 1 + nbf, nbstf= 0; + while( (theField= GetSubTypeField_GCIO(*theSubType,i)) ) + { + if( IsPrivateField_GCIO(theField) ) { break; };//FIXME: could count geometry private fields ... + nbstf++; + SetSubTypeNbFields_GCIO(*theSubType, nbstf); + i++; + } + } + if( nbtf < 1 + nbf + nbstf + 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %ld, Total number of fields differs with type definition '%s' (%d found, at least %d expected).\n", + GetGCCurrentLinenum_GCIO(H), tdst, nbtf, 1+nbf+nbstf+1 ); + CSLDestroy(pszFields); + return NULL; + } + i= atoi(pszFields[nbf]); + if( i!=nbstf ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %ld, Number of user's fields differs with type definition '%s' (%d found, %d expected).\n", + GetGCCurrentLinenum_GCIO(H), tdst, i, nbstf ); + CSLDestroy(pszFields); + return NULL; + } + /* + * the Subclass has no definition : let's build one + */ + if( !(fd= GetSubTypeFeatureDefn_GCIO(*theSubType)) ) + { + if( !(fd= OGR_FD_Create(tdst)) ) + { + CSLDestroy(pszFields); + return NULL; + } + + switch( GetSubTypeKind_GCIO(*theSubType) ) + { + case vPoint_GCIO : + case vText_GCIO :/* FIXME : treat as point ? */ + switch( d ) + { + case v3D_GCIO : + case v3DM_GCIO : + OGR_FD_SetGeomType(fd,wkbPoint25D); + break; + default : + OGR_FD_SetGeomType(fd,wkbPoint); + break; + } + break; + case vLine_GCIO : + switch( d ) + { + case v3D_GCIO : + case v3DM_GCIO : + OGR_FD_SetGeomType(fd,wkbLineString25D); + break; + default : + OGR_FD_SetGeomType(fd,wkbLineString); + break; + } + break; + case vPoly_GCIO : + switch( d ) + { + case v3D_GCIO : + case v3DM_GCIO : + OGR_FD_SetGeomType(fd,wkbMultiPolygon25D); + break; + default : + OGR_FD_SetGeomType(fd,wkbMultiPolygon); + break; + } + break; + default : + CSLDestroy(pszFields); + OGR_FD_Destroy(fd); + CPLError( CE_Failure, CPLE_NotSupported, + "Unknown Geoconcept type for '%s'.\n", + tdst ); + return NULL; + } + for( i= 1 + nbf; i<1 + nbf + nbstf; i++ ) + { + theField= GetSubTypeField_GCIO(*theSubType,i); + if( !(fld= OGR_Fld_Create(GetFieldName_GCIO(theField),OFTString)) ) /* FIXME */ + { + CSLDestroy(pszFields); + OGR_FD_Destroy(fd); + return NULL; + } + OGR_FD_AddFieldDefn(fd,fld); + OGR_Fld_Destroy(fld); + fld= NULL; + } + } + + /* + * the Subclass is just under parsing via _parseObject_GCIO + */ + if( buildFeature ) + { + if( !(f= OGR_F_Create(fd)) ) + { + if( !GetSubTypeFeatureDefn_GCIO(*theSubType) ) + OGR_FD_Destroy(fd); + CSLDestroy(pszFields); + return NULL; + } + OGR_F_SetFID(f, atol(pszFields[0])); /* FID */ + if( OGR_F_GetFID(f)==OGRNullFID ) + { + OGR_F_SetFID(f, GetGCCurrentLinenum_GCIO(H)); + } + for( i= 1 + nbf, j= 0; i<1 + nbf + nbstf; i++, j++ ) + { + theField= GetSubTypeField_GCIO(*theSubType,i); + if( pszFields[i][0]=='\0' ) + OGR_F_UnsetField(f,j); + else + OGR_F_SetFieldString(f,j,pszFields[i]); + } + } + else + { + i= 1 + nbf + nbstf; + } + CPLDebug("GEOCONCEPT", "%d %d/%d/%d/%d\n", __LINE__, i, nbf, nbstf, nbtf); + if( !(g= _buildOGRGeometry_GCIO(Meta,*theSubType,i,(const char **)pszFields,nbtf,d,bbox)) ) + { + /* + * the Subclass is under reading via ReadNextFeature_GCIO + */ + if( buildFeature ) + { + CSLDestroy(pszFields); + if( f ) OGR_F_Destroy(f); + return NULL; + } + } + if( buildFeature ) + { + if( OGR_F_SetGeometryDirectly(f,g)!=OGRERR_NONE ) + { + CSLDestroy(pszFields); + if( f ) OGR_F_Destroy(f); + return NULL; + } + } + CSLDestroy(pszFields); + + /* Assign definition : */ + if( !GetSubTypeFeatureDefn_GCIO(*theSubType) ) + { + SetSubTypeFeatureDefn_GCIO(*theSubType, fd); + OGR_FD_Reference(fd); + } + + /* + * returns either the built object for ReadNextFeature_GCIO or + * the feature definition for _parseObject_GCIO : + */ + return buildFeature? f:(OGRFeatureH)fd; +}/* _buildOGRFeature_GCIO */ + +/* -------------------------------------------------------------------- */ +static GCExportFileMetadata GCIOAPI_CALL1(*) _parseObject_GCIO ( + GCExportFileH* H + ) +{ + GCExportFileMetadata* Meta; + GCSubType* theSubType; + GCDim d; + long coff; + OGREnvelope bbox, *pszBbox= &bbox; + + Meta= GetGCMeta_GCIO(H); + + InitOGREnvelope_GCIO(pszBbox); + + d= vUnknown3D_GCIO; + theSubType= NULL; + coff= -1L; +reloop: + /* TODO: Switch to C++ casts below. */ + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(H) == vComType_GCIO ) + { + if( _get_GCIO(H)==-1 ) + return Meta; + goto reloop; + } + /* analyze the line according to schema : */ + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(H) == vPragma_GCIO ) + { + if( strstr(GetGCCache_GCIO(H),k3DOBJECTMONO_GCIO) ) + { + d= v3DM_GCIO; + coff= GetGCCurrentOffset_GCIO(H); + } + else if( strstr(GetGCCache_GCIO(H),k3DOBJECT_GCIO) ) + { + d= v3D_GCIO; + coff= GetGCCurrentOffset_GCIO(H); + } + else if( strstr(GetGCCache_GCIO(H),k2DOBJECT_GCIO) ) + { + d= v2D_GCIO; + coff= GetGCCurrentOffset_GCIO(H); + } + else + { + /* not an object pragma ... */ + SetGCStatus_GCIO(H,vMemoStatus_GCIO); + return Meta; + } + if( _get_GCIO(H)==-1 ) + return Meta; + goto reloop; + } + if( coff==-1L) coff= GetGCCurrentOffset_GCIO(H); + if( !_buildOGRFeature_GCIO(H,&theSubType,d,pszBbox) ) + { + return NULL; + } + if( GetSubTypeBOF_GCIO(theSubType)==-1L ) + { + SetSubTypeBOF_GCIO(theSubType,coff);/* Begin Of Features for the Class.Subclass */ + SetSubTypeBOFLinenum_GCIO(theSubType, GetGCCurrentLinenum_GCIO(H)); + CPLDebug("GEOCONCEPT","Feature Type [%s] starts at #%ld, line %ld\n", + GetSubTypeName_GCIO(theSubType), + GetSubTypeBOF_GCIO(theSubType), + GetSubTypeBOFLinenum_GCIO(theSubType)); + } + SetSubTypeNbFeatures_GCIO(theSubType, GetSubTypeNbFeatures_GCIO(theSubType)+1L); + SetGCNbObjects_GCIO(H,GetGCNbObjects_GCIO(H)+1L); + /* update bbox of both feature and file */ + SetExtentULAbscissa_GCIO(GetMetaExtent_GCIO(Meta),pszBbox->MinX); + SetExtentULOrdinate_GCIO(GetMetaExtent_GCIO(Meta),pszBbox->MaxY); + SetExtentLRAbscissa_GCIO(GetMetaExtent_GCIO(Meta),pszBbox->MaxX); + SetExtentLROrdinate_GCIO(GetMetaExtent_GCIO(Meta),pszBbox->MinY); + if( !GetSubTypeExtent_GCIO(theSubType) ) + { + SetSubTypeExtent_GCIO(theSubType, CreateExtent_GCIO(HUGE_VAL,HUGE_VAL,-HUGE_VAL,-HUGE_VAL)); + } + SetExtentULAbscissa_GCIO(GetSubTypeExtent_GCIO(theSubType),pszBbox->MinX); + SetExtentULOrdinate_GCIO(GetSubTypeExtent_GCIO(theSubType),pszBbox->MaxY); + SetExtentLRAbscissa_GCIO(GetSubTypeExtent_GCIO(theSubType),pszBbox->MaxX); + SetExtentLROrdinate_GCIO(GetSubTypeExtent_GCIO(theSubType),pszBbox->MinY); + if( d==vUnknown3D_GCIO && GetSubTypeDim_GCIO(theSubType)==vUnknown3D_GCIO ) + { + switch( d ) + { + case v3DM_GCIO : + case v3D_GCIO : + SetSubTypeDim_GCIO(theSubType,v3D_GCIO); + break; + default : + SetSubTypeDim_GCIO(theSubType,v2D_GCIO); + break; + } + } + d= vUnknown3D_GCIO; + theSubType= NULL; + + return Meta; +}/* _parseObject_GCIO */ + +/* -------------------------------------------------------------------- */ +GCExportFileH GCIOAPI_CALL1(*) Open_GCIO ( + const char* pszGeoconceptFile, + const char* ext, /* gxt if NULL */ + const char* mode, + const char* gctPath /* if !NULL, mode=w */ + ) +{ + GCExportFileH* hGXT; + + CPLDebug( "GEOCONCEPT", "filename '%s' - '%s' - mode '%s' - config path '%s'", + pszGeoconceptFile? pszGeoconceptFile:"???", + ext? ext:"gxt", + mode? mode:"???", + gctPath? gctPath:"???" ); + + if( !(hGXT= _Create_GCIO(pszGeoconceptFile,ext,mode)) ) + { + return NULL; + } + + if( GetGCMode_GCIO(hGXT)==vUpdateAccess_GCIO ) + { + FILE* h; + + /* file must exists ... */ + if( !(h= VSIFOpen(CPLFormFilename(GetGCPath_GCIO(hGXT),GetGCBasename_GCIO(hGXT),GetGCExtension_GCIO(hGXT)), "rt")) ) + { + _Destroy_GCIO(&hGXT,FALSE); + return NULL; + } + } + + SetGCHandle_GCIO(hGXT, VSIFOpen(CPLFormFilename(GetGCPath_GCIO(hGXT),GetGCBasename_GCIO(hGXT),GetGCExtension_GCIO(hGXT)), mode)); + if( !GetGCHandle_GCIO(hGXT) ) + { + _Destroy_GCIO(&hGXT,FALSE); + return NULL; + } + + if( GetGCMode_GCIO(hGXT)==vWriteAccess_GCIO ) + { + if( gctPath!=NULL ) + { + /* load Metadata */ + GCExportFileH* hGCT; + + hGCT= _Create_GCIO(gctPath,"gct","-"); + SetGCHandle_GCIO(hGCT, VSIFOpen(CPLFormFilename(GetGCPath_GCIO(hGCT),GetGCBasename_GCIO(hGCT),GetGCExtension_GCIO(hGCT)), "r")); + if( !GetGCHandle_GCIO(hGCT) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "opening a Geoconcept config file '%s' failed.\n", + gctPath); + _Destroy_GCIO(&hGCT,FALSE); + _Destroy_GCIO(&hGXT,TRUE); + return NULL; + } + if( ReadConfig_GCIO(hGCT)==NULL ) + { + _Destroy_GCIO(&hGCT,FALSE); + _Destroy_GCIO(&hGXT,TRUE); + return NULL; + } + SetGCMeta_GCIO(hGXT, GetGCMeta_GCIO(hGCT)); + SetGCMeta_GCIO(hGCT, NULL); + _Destroy_GCIO(&hGCT,FALSE); + SetMetaExtent_GCIO(GetGCMeta_GCIO(hGXT), CreateExtent_GCIO(HUGE_VAL,HUGE_VAL,-HUGE_VAL,-HUGE_VAL)); + } + } + else + { + /* read basic Metadata from export */ + /* read and parse the export file ... */ + if( ReadHeader_GCIO(hGXT)==NULL ) + { + _Destroy_GCIO(&hGXT,FALSE); + return NULL; + } + } + /* check schema */ + if( !_checkSchema_GCIO(hGXT) ) + { + _Destroy_GCIO(&hGXT,GetGCMode_GCIO(hGXT)==vWriteAccess_GCIO? TRUE:FALSE); + return NULL; + } + + CPLDebug( "GEOCONCEPT", + "Export =(\n" + " Path : %s\n" + " Basename : %s\n" + " Extension : %s\n" + " Mode : %s\n" + " Status : %s\n" + ")", + GetGCPath_GCIO(hGXT), + GetGCBasename_GCIO(hGXT), + GetGCExtension_GCIO(hGXT), + GCAccessMode2str_GCIO(GetGCMode_GCIO(hGXT)), + GCAccessStatus2str_GCIO(GetGCStatus_GCIO(hGXT)) + ); + + return hGXT; +}/* Open_GCIO */ + +/* -------------------------------------------------------------------- */ +void GCIOAPI_CALL Close_GCIO ( + GCExportFileH** hGXT + ) +{ + _Destroy_GCIO(hGXT,FALSE); +}/* Close_GCIO */ + +/* -------------------------------------------------------------------- */ +GCExportFileH GCIOAPI_CALL1(*) Rewind_GCIO ( + GCExportFileH* hGXT, + GCSubType* theSubType + ) +{ + if( hGXT ) + { + if( GetGCHandle_GCIO(hGXT) ) + { + if( !theSubType ) + { + VSIRewind(GetGCHandle_GCIO(hGXT)); + SetGCCurrentLinenum_GCIO(hGXT, 0L); + } + else + { + VSIFSeek(GetGCHandle_GCIO(hGXT), GetSubTypeBOF_GCIO(theSubType), SEEK_SET); + SetGCCurrentLinenum_GCIO(hGXT, GetSubTypeBOFLinenum_GCIO(theSubType)); + } + SetGCStatus_GCIO(hGXT,vNoStatus_GCIO); + } + } + return hGXT; +}/* Rewind_GCIO */ + +/* -------------------------------------------------------------------- */ +GCExportFileH GCIOAPI_CALL1(*) FFlush_GCIO ( + GCExportFileH* hGXT + ) +{ + if( hGXT ) + { + if( GetGCHandle_GCIO(hGXT) ) + { + VSIFFlush(GetGCHandle_GCIO(hGXT)); + } + } + return hGXT; +}/* FFlush_GCIO */ + +/* -------------------------------------------------------------------- */ +GCAccessMode GCIOAPI_CALL GetMode_GCIO ( + GCExportFileH* hGXT + ) +{ + return hGXT? GetGCMode_GCIO(hGXT):vUnknownAccessMode_GCIO; +}/* GetMode_GCIO */ + +/* -------------------------------------------------------------------- */ +GCSubType GCIOAPI_CALL1(*) AddSubType_GCIO ( + GCExportFileH* H, + const char* typName, + const char* subtypName, + long id, + GCTypeKind knd, + GCDim sys + ) +{ + int whereClass; + GCType* theClass; + GCSubType* theSubType; + CPLList* L; + + if( (whereClass = _findTypeByName_GCIO(H,typName))==-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "failed to find a Geoconcept type for '%s.%s#%ld'.\n", + typName, subtypName, id); + return NULL; + } + + theClass= _getType_GCIO(H,whereClass); + if( GetTypeSubtypes_GCIO(theClass) ) + { + if( _findSubTypeByName_GCIO(theClass,subtypName)!=-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Geoconcept subtype '%s.%s#%ld' already exists.\n", + typName, subtypName, id); + return NULL; + } + } + + if( !(theSubType= _CreateSubType_GCIO(subtypName,id,knd,sys)) ) + { + return NULL; + } + if( (L= CPLListAppend(GetTypeSubtypes_GCIO(theClass),theSubType))==NULL ) + { + _DestroySubType_GCIO(&theSubType); + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to add a Geoconcept subtype for '%s.%s#%ld'.\n", + typName, subtypName, id); + return NULL; + } + SetTypeSubtypes_GCIO(theClass, L); + SetSubTypeType_GCIO(theSubType, theClass); + + CPLDebug("GEOCONCEPT", "SubType '%s.%s#%ld' added.", typName, subtypName, id); + + return theSubType; +}/* AddSubType_GCIO */ + +/* -------------------------------------------------------------------- */ +static void GCIOAPI_CALL _dropSubType_GCIO ( + GCSubType** theSubType + ) +{ + GCType* theClass; + int where; + + if( !theSubType || !(*theSubType) ) return; + if( !(theClass= GetSubTypeType_GCIO(*theSubType)) ) return; + if( (where= _findSubTypeByName_GCIO(theClass,GetSubTypeName_GCIO(*theSubType)))==-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "subtype %s does not exist.\n", + GetSubTypeName_GCIO(*theSubType)? GetSubTypeName_GCIO(*theSubType):"''"); + return; + } + CPLListRemove(GetTypeSubtypes_GCIO(theClass),where); + _DestroySubType_GCIO(theSubType); + + return; +}/* _dropSubType_GCIO */ + +/* -------------------------------------------------------------------- */ +GCType GCIOAPI_CALL1(*) AddType_GCIO ( + GCExportFileH* H, + const char* typName, + long id + ) +{ + GCType* theClass; + CPLList* L; + + if( _findTypeByName_GCIO(H,typName)!=-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "type %s already exists.\n", + typName); + return NULL; + } + + if( !(theClass= _CreateType_GCIO(typName,id)) ) + { + return NULL; + } + if( (L= CPLListAppend(GetMetaTypes_GCIO(GetGCMeta_GCIO(H)),theClass))==NULL ) + { + _DestroyType_GCIO(&theClass); + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to add a Geoconcept type for '%s#%ld'.\n", + typName, id); + return NULL; + } + SetMetaTypes_GCIO(GetGCMeta_GCIO(H), L); + + CPLDebug("GEOCONCEPT", "Type '%s#%ld' added.", typName, id); + + return theClass; +}/* AddType_GCIO */ + +/* -------------------------------------------------------------------- */ +static void GCIOAPI_CALL _dropType_GCIO ( + GCExportFileH* H, + GCType **theClass + ) +{ + int where; + + if( !theClass || !(*theClass) ) return; + if( (where= _findTypeByName_GCIO(H,GetTypeName_GCIO(*theClass)))==-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "type %s does not exist.\n", + GetTypeName_GCIO(*theClass)? GetTypeName_GCIO(*theClass):"''"); + return; + } + CPLListRemove(GetMetaTypes_GCIO(GetGCMeta_GCIO(H)),where); + _DestroyType_GCIO(theClass); + + return; +}/* _dropType_GCIO */ + +/* -------------------------------------------------------------------- */ +GCField GCIOAPI_CALL1(*) AddTypeField_GCIO ( + GCExportFileH* H, + const char* typName, + int where, /* -1 : in the end */ + const char* name, + long id, + GCTypeKind knd, + const char* extra, + const char* enums + ) +{ + int whereClass; + GCType* theClass; + GCField* theField; + CPLList* L; + const char* normName; + + if( (whereClass = _findTypeByName_GCIO(H,typName))==-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "failed to find a Geoconcept type for '%s@%s#%ld'.\n", + typName, name, id); + return NULL; + } + theClass= _getType_GCIO(H,whereClass); + + normName= _NormalizeFieldName_GCIO(name); + if( _findFieldByName_GCIO(GetTypeFields_GCIO(theClass),normName)!=-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "field '%s@%s#%ld' already exists.\n", + typName, name, id); + return NULL; + } + + if( !(theField= _CreateField_GCIO(normName,id,knd,extra,enums)) ) + { + return NULL; + } + if ( + where==-1 || + (where==0 && CPLListCount(GetTypeFields_GCIO(theClass))==0) + ) + { + L= CPLListAppend(GetTypeFields_GCIO(theClass),theField); + } + else + { + L= CPLListInsert(GetTypeFields_GCIO(theClass),theField,where); + } + if ( !L ) + { + _DestroyField_GCIO(&theField); + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to add a Geoconcept field for '%s@%s#%ld'.\n", + typName, name, id); + return NULL; + } + SetTypeFields_GCIO(theClass, L); + + CPLDebug("GEOCONCEPT", "Field '%s@%s#%ld' added.", typName, name, id); + + return theField; +}/* AddTypeField_GCIO */ + +/* -------------------------------------------------------------------- */ +GCField GCIOAPI_CALL1(*) AddSubTypeField_GCIO ( + GCExportFileH* H, + const char* typName, + const char* subtypName, + int where, /* -1 : in the end */ + const char* name, + long id, + GCTypeKind knd, + const char* extra, + const char* enums + ) +{ + int whereClass, whereSubType; + GCType* theClass; + GCSubType* theSubType; + GCField* theField; + CPLList* L; + const char* normName; + + if( (whereClass= _findTypeByName_GCIO(H,typName))==-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "failed to find a Geoconcept type for '%s.%s@%s#%ld'.\n", + typName, subtypName, name, id); + return NULL; + } + theClass= _getType_GCIO(H,whereClass); + + if( (whereSubType= _findSubTypeByName_GCIO(theClass,subtypName))==-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "failed to find a Geoconcept subtype for '%s.%s@%s#%ld'.\n", + typName, subtypName, name, id); + return NULL; + } + theSubType= _getSubType_GCIO(theClass,whereSubType); + + normName= _NormalizeFieldName_GCIO(name); + if( _findFieldByName_GCIO(GetSubTypeFields_GCIO(theSubType),normName)!=-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "field '%s.%s@%s#%ld' already exists.\n", + typName, subtypName, name, id); + return NULL; + } + + if( !(theField= _CreateField_GCIO(normName,id,knd,extra,enums)) ) + { + return NULL; + } + if( + where==-1 || + (where==0 && CPLListCount(GetSubTypeFields_GCIO(theSubType))==0) + ) + { + L= CPLListAppend(GetSubTypeFields_GCIO(theSubType),theField); + } + else + { + L= CPLListInsert(GetSubTypeFields_GCIO(theSubType),theField,where); + } + if( !L ) + { + _DestroyField_GCIO(&theField); + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to add a Geoconcept field for '%s.%s@%s#%ld'.\n", + typName, subtypName, name, id); + return NULL; + } + SetSubTypeFields_GCIO(theSubType, L); + + CPLDebug("GEOCONCEPT", "Field '%s.%s@%s#%ld' added.", typName, subtypName, name, id); + + return theField; +}/* AddSubTypeField_GCIO */ + +/* -------------------------------------------------------------------- */ +static OGRErr GCIOAPI_CALL _readConfigField_GCIO ( + GCExportFileH* hGCT + ) +{ + int eof, res; + char *k, n[kItemSize_GCIO], x[kExtraSize_GCIO], e[kExtraSize_GCIO]; + const char* normName; + long id; + GCTypeKind knd; + CPLList* L; + GCField* theField; + + eof= 0; + n[0]= '\0'; + x[0]= '\0'; + e[0]= '\0'; + id= UNDEFINEDID_GCIO; + knd= vUnknownItemType_GCIO; + theField= NULL; + while( _get_GCIO(hGCT)!=EOF ) + { + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vComType_GCIO ) + { + continue; + } + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vHeader_GCIO ) + { + if( strstr(GetGCCache_GCIO(hGCT),kConfigEndField_GCIO)!=NULL) + { + eof= 1; + if( n[0]=='\0' || id==UNDEFINEDID_GCIO || knd==vUnknownItemType_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing %s.\n", + n[0]=='\0'? "Name": id==UNDEFINEDID_GCIO? "ID": "Kind"); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + normName= _NormalizeFieldName_GCIO(n); + if( _findFieldByName_GCIO(GetMetaFields_GCIO(GetGCMeta_GCIO(hGCT)),normName)!=-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "field '@%s#%ld' already exists.\n", + n, id); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( !(theField= _CreateField_GCIO(normName,id,knd,x,e)) ) + { + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (L= CPLListAppend(GetMetaFields_GCIO(GetGCMeta_GCIO(hGCT)),theField))==NULL ) + { + _DestroyField_GCIO(&theField); + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to add a Geoconcept field for '@%s#%ld'.\n", + n, id); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + SetMetaFields_GCIO(GetGCMeta_GCIO(hGCT), L); + break; + } + res= OGRERR_NONE; + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigName_GCIO))!=NULL ) + { + if( n[0]!='\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Name found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Name found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + strncpy(n,k,kItemSize_GCIO-1), n[kItemSize_GCIO-1]= '\0'; + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigID_GCIO))!=NULL ) + { + if( id!=UNDEFINEDID_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( sscanf(k,"%ld", &id)!=1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigKind_GCIO))!=NULL ) + { + if( knd!=vUnknownItemType_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Kind found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Kind found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (knd= str2GCTypeKind_GCIO(k))==vUnknownItemType_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Not supported Kind found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigExtra_GCIO))!=NULL || + (k= strstr(GetGCCache_GCIO(hGCT),kConfigExtraText_GCIO))!=NULL ) + { + if( x[0]!='\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Extra information found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Extra information found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + strncpy(x,k,kExtraSize_GCIO-1), x[kExtraSize_GCIO-1]= '\0'; + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigList_GCIO))!=NULL ) + { + if( e[0]!='\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate List found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid List found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + strncpy(e,k,kExtraSize_GCIO-1), e[kExtraSize_GCIO-1]= '\0'; + } + else + { /* Skipping ... */ + res= OGRERR_NONE; + } + if( res != OGRERR_NONE ) + { + goto onError; + } + continue; + } +onError: + return OGRERR_CORRUPT_DATA; + } + if (eof!=1) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Geoconcept config field end block %s not found.\n", + kConfigEndField_GCIO); + return OGRERR_CORRUPT_DATA; + } + + return OGRERR_NONE; +}/* _readConfigField_GCIO */ + +/* -------------------------------------------------------------------- */ +static OGRErr GCIOAPI_CALL _readConfigFieldType_GCIO ( + GCExportFileH* hGCT, + GCType* theClass + ) +{ + int eof, res; + char *k, n[kItemSize_GCIO], x[kExtraSize_GCIO], e[kExtraSize_GCIO]; + long id; + GCTypeKind knd; + GCField* theField; + + eof= 0; + n[0]= '\0'; + x[0]= '\0'; + e[0]= '\0'; + id= UNDEFINEDID_GCIO; + knd= vUnknownItemType_GCIO; + theField= NULL; + while( _get_GCIO(hGCT)!=EOF ) { + /* TODO: Switch to C++ casts below. */ + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vComType_GCIO ) + { + continue; + } + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vHeader_GCIO ) + { + if( strstr(GetGCCache_GCIO(hGCT),kConfigEndField_GCIO)!=NULL) + { + eof= 1; + if( n[0]=='\0' || id==UNDEFINEDID_GCIO || knd==vUnknownItemType_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing %s.\n", + n[0]=='\0'? "Name": id==UNDEFINEDID_GCIO? "ID": "Kind"); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (theField= AddTypeField_GCIO(hGCT,GetTypeName_GCIO(theClass),-1,n,id,knd,x,e))==NULL ) + { + res= OGRERR_CORRUPT_DATA; + goto onError; + } + break; + } + res= OGRERR_NONE; + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigName_GCIO))!=NULL ) + { + if( n[0]!='\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Name found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Name found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + strncpy(n,k,kItemSize_GCIO-1), n[kItemSize_GCIO-1]= '\0'; + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigID_GCIO))!=NULL ) + { + if( id!=UNDEFINEDID_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( sscanf(k,"%ld", &id)!=1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigKind_GCIO))!=NULL ) + { + if( knd!=vUnknownItemType_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Kind found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Kind found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (knd= str2GCTypeKind_GCIO(k))==vUnknownItemType_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Not supported Kind found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigExtra_GCIO))!=NULL || + (k= strstr(GetGCCache_GCIO(hGCT),kConfigExtraText_GCIO))!=NULL ) + { + if( x[0]!='\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Extra information found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid extra information found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + strncpy(x,k,kExtraSize_GCIO-1), x[kExtraSize_GCIO-1]= '\0'; + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigList_GCIO))!=NULL ) + { + if( e[0]!='\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate List found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid List found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + strncpy(e,k,kExtraSize_GCIO-1), e[kExtraSize_GCIO-1]= '\0'; + } + else + { /* Skipping ... */ + res= OGRERR_NONE; + } + if( res != OGRERR_NONE ) + { + goto onError; + } + continue; + } +onError: + return OGRERR_CORRUPT_DATA; + } + if (eof!=1) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Geoconcept config field end block %s not found.\n", + kConfigEndField_GCIO); + return OGRERR_CORRUPT_DATA; + } + + return OGRERR_NONE; +}/* _readConfigFieldType_GCIO */ + +/* -------------------------------------------------------------------- */ +static OGRErr GCIOAPI_CALL _readConfigFieldSubType_GCIO ( + GCExportFileH* hGCT, + GCType* theClass, + GCSubType* theSubType + ) +{ + int eof, res; + char *k, n[kItemSize_GCIO], x[kExtraSize_GCIO], e[kExtraSize_GCIO]; + long id; + GCTypeKind knd; + GCField* theField; + + eof= 0; + n[0]= '\0'; + x[0]= '\0'; + e[0]= '\0'; + id= UNDEFINEDID_GCIO; + knd= vUnknownItemType_GCIO; + theField= NULL; + while( _get_GCIO(hGCT)!=EOF ) { + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vComType_GCIO ) + { + continue; + } + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vHeader_GCIO ) + { + if( strstr(GetGCCache_GCIO(hGCT),kConfigEndField_GCIO)!=NULL) + { + eof= 1; + if( n[0]=='\0' || id==UNDEFINEDID_GCIO || knd==vUnknownItemType_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing %s.\n", + n[0]=='\0'? "Name": id==UNDEFINEDID_GCIO? "ID": "Kind"); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (theField= AddSubTypeField_GCIO(hGCT,GetTypeName_GCIO(theClass),GetSubTypeName_GCIO(theSubType),-1,n,id,knd,x,e))==NULL ) + { + res= OGRERR_CORRUPT_DATA; + goto onError; + } + break; + } + res= OGRERR_NONE; + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigName_GCIO))!=NULL ) + { + if( n[0]!='\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Name found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Name found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + strncpy(n,k,kItemSize_GCIO-1), n[kItemSize_GCIO-1]= '\0'; + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigID_GCIO))!=NULL ) + { + if( id!=UNDEFINEDID_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( sscanf(k,"%ld", &id)!=1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigKind_GCIO))!=NULL ) + { + if( knd!=vUnknownItemType_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Kind found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Kind found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (knd= str2GCTypeKind_GCIO(k))==vUnknownItemType_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Not supported Kind found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigExtra_GCIO))!=NULL || + (k= strstr(GetGCCache_GCIO(hGCT),kConfigExtraText_GCIO))!=NULL ) + { + if( x[0]!='\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Extra information found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid extra information found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + strncpy(x,k,kExtraSize_GCIO-1), x[kExtraSize_GCIO-1]= '\0'; + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigList_GCIO))!=NULL ) + { + if( e[0]!='\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate List found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid List found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + strncpy(e,k,kExtraSize_GCIO-1), e[kExtraSize_GCIO-1]= '\0'; + } + else + { /* Skipping ... */ + res= OGRERR_NONE; + } + if( res != OGRERR_NONE ) + { + goto onError; + } + continue; + } +onError: + return OGRERR_CORRUPT_DATA; + } + if (eof!=1) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Geoconcept config field end block %s not found.\n", + kConfigEndField_GCIO); + return OGRERR_CORRUPT_DATA; + } + + return OGRERR_NONE; +}/* _readConfigFieldSubType_GCIO */ + +/* -------------------------------------------------------------------- */ +static OGRErr GCIOAPI_CALL _readConfigSubTypeType_GCIO ( + GCExportFileH* hGCT, + GCType* theClass + ) +{ + int eost, res; + char *k, n[kItemSize_GCIO]; + long id; + GCTypeKind knd; + GCDim sys; + GCSubType* theSubType; + + eost= 0; + n[0]= '\0'; + id= UNDEFINEDID_GCIO; + knd= vUnknownItemType_GCIO; + sys= v2D_GCIO; + theSubType= NULL; + while( _get_GCIO(hGCT)!=EOF ) { + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vComType_GCIO ) + { + continue; + } + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vHeader_GCIO ) + { + if( strstr(GetGCCache_GCIO(hGCT),kConfigEndSubType_GCIO)!=NULL) + { + eost= 1; + break; + } + res= OGRERR_NONE; + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigName_GCIO))!=NULL ) + { + if( n[0]!='\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Name found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Name found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + strncpy(n,k,kItemSize_GCIO-1), n[kItemSize_GCIO-1]= '\0'; + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigID_GCIO))!=NULL ) + { + if( id!=UNDEFINEDID_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( sscanf(k,"%ld", &id)!=1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigKind_GCIO))!=NULL ) + { + if( knd!=vUnknownItemType_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Kind found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Kind found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (knd= str2GCTypeKind_GCIO(k))==vUnknownItemType_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Not supported Kind found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfig3D_GCIO))!=NULL ) + { + if( sys!=vUnknown3D_GCIO && sys!=v2D_GCIO) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Dimension found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Dimension found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (sys= str2GCDim(k))==vUnknown3D_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Not supported Dimension found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + else + if( strstr(GetGCCache_GCIO(hGCT),kConfigBeginField_GCIO)!=NULL ) + { + if( theSubType==NULL ) + { + if( n[0]=='\0' || id==UNDEFINEDID_GCIO || knd==vUnknownItemType_GCIO || sys==vUnknown3D_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing %s.\n", + n[0]=='\0'? "Name": id==UNDEFINEDID_GCIO? "ID": knd==vUnknownItemType_GCIO? "Kind": "3D"); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (theSubType= AddSubType_GCIO(hGCT,GetTypeName_GCIO(theClass),n,id,knd,sys))==NULL ) + { + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + res= _readConfigFieldSubType_GCIO(hGCT,theClass,theSubType); + } + else + { /* Skipping ... */ + res= OGRERR_NONE; + } + if( res != OGRERR_NONE ) + { + goto onError; + } + continue; + } +onError: + if( theSubType ) + { + _dropSubType_GCIO(&theSubType); + } + return OGRERR_CORRUPT_DATA; + } + if (eost!=1) + { + if( theSubType ) + { + _dropSubType_GCIO(&theSubType); + } + CPLError( CE_Failure, CPLE_AppDefined, + "Geoconcept config subtype end block %s not found.\n", + kConfigEndSubType_GCIO); + return OGRERR_CORRUPT_DATA; + } + + return OGRERR_NONE; +}/* _readConfigSubTypeType_GCIO */ + +/* -------------------------------------------------------------------- */ +static OGRErr GCIOAPI_CALL _readConfigType_GCIO ( + GCExportFileH* hGCT + ) +{ + int eot, res; + char *k, n[kItemSize_GCIO]; + long id; + GCType *theClass; + + eot= 0; + n[0]= '\0'; + id= UNDEFINEDID_GCIO; + theClass= NULL; + while( _get_GCIO(hGCT)!=EOF ) { + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vComType_GCIO ) + { + continue; + } + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vHeader_GCIO ) + { + if( strstr(GetGCCache_GCIO(hGCT),kConfigEndType_GCIO)!=NULL ) + { + eot= 1; + break; + } + res= OGRERR_NONE; + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigName_GCIO))!=NULL ) + { + if( n[0]!='\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate Name found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Name found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + strncpy(n,k,kItemSize_GCIO-1), n[kItemSize_GCIO-1]= '\0'; + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigID_GCIO))!=NULL ) + { + if( id!=UNDEFINEDID_GCIO ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Duplicate ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( sscanf(k,"%ld", &id)!=1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Not supported ID found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + else + if( strstr(GetGCCache_GCIO(hGCT),kConfigBeginSubType_GCIO)!=NULL ) + { + if( theClass==NULL ) + { + if( n[0]=='\0' || id==UNDEFINEDID_GCIO || (theClass= AddType_GCIO(hGCT,n,id))==NULL ) + { + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + res= _readConfigSubTypeType_GCIO(hGCT,theClass); + } + else + if( strstr(GetGCCache_GCIO(hGCT),kConfigBeginField_GCIO)!=NULL) + { + if( theClass==NULL ) + { + if( n[0]=='\0' || id==UNDEFINEDID_GCIO || (theClass= AddType_GCIO(hGCT,n,id))==NULL ) + { + res= OGRERR_CORRUPT_DATA; + goto onError; + } + } + res= _readConfigFieldType_GCIO(hGCT,theClass); + } + else + { /* Skipping ... */ + res= OGRERR_NONE; + } + if( res != OGRERR_NONE ) + { + goto onError; + } + continue; + } +onError: + if( theClass ) + { + _dropType_GCIO(hGCT,&theClass); + } + return OGRERR_CORRUPT_DATA; + } + if (eot!=1) + { + if( theClass ) + { + _dropType_GCIO(hGCT,&theClass); + } + CPLError( CE_Failure, CPLE_AppDefined, + "Geoconcept config type end block %s not found.\n", + kConfigEndType_GCIO); + return OGRERR_CORRUPT_DATA; + } + + return OGRERR_NONE; +}/* _readConfigType_GCIO */ + +/* -------------------------------------------------------------------- */ +static OGRErr GCIOAPI_CALL _readConfigMap_GCIO ( + GCExportFileH* hGCT + ) +{ + int eom, res; + char* k; + + eom= 0; + while( _get_GCIO(hGCT)!=EOF ) { + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vComType_GCIO ) + { + continue; + } + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vHeader_GCIO ) + { + if( strstr(GetGCCache_GCIO(hGCT),kConfigEndMap_GCIO)!=NULL ) + { + eom= 1; + break; + } + res= OGRERR_NONE; + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigUnit_GCIO))!=NULL && + strstr(GetGCCache_GCIO(hGCT),kConfigZUnit_GCIO)==NULL ) + { + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Unit found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + SetMetaUnit_GCIO(GetGCMeta_GCIO(hGCT),k); + } + else + if( (k= strstr(GetGCCache_GCIO(hGCT),kConfigPrecision_GCIO))!=NULL && + strstr(GetGCCache_GCIO(hGCT),kConfigZPrecision_GCIO)==NULL ) + { + double r; + if( (k= _getHeaderValue_GCIO(k))==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Precision found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + if( CPLsscanf(k,"%lf", &r)!=1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Precision found : '%s'.\n", + GetGCCache_GCIO(hGCT)); + res= OGRERR_CORRUPT_DATA; + goto onError; + } + SetMetaResolution_GCIO(GetGCMeta_GCIO(hGCT),r); + } + else + { /* Skipping ... */ + res= OGRERR_NONE; + } + if( res != OGRERR_NONE ) + { + goto onError; + } + continue; + } +onError: + return OGRERR_CORRUPT_DATA; + } + if (eom!=1) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Geoconcept config map end block %s not found.\n", + kConfigEndMap_GCIO); + return OGRERR_CORRUPT_DATA; + } + + return OGRERR_NONE; +}/* _readConfigMap_GCIO */ + +/* -------------------------------------------------------------------- */ +GCExportFileMetadata GCIOAPI_CALL1(*) ReadConfig_GCIO ( + GCExportFileH* hGCT + ) +{ + int eoc, res, it, nt; + int i, n, il, nl, ll; + int is, ns; + char l[kExtraSize_GCIO]; + const char *v; + GCField* theField; + GCSubType* theSubType; + GCType* theClass; + CPLList *e, *es, *et; + GCExportFileMetadata* Meta; + + eoc= 0; + if( _get_GCIO(hGCT)==EOF ) + { + return NULL; + } + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) != vHeader_GCIO && + strstr(GetGCCache_GCIO(hGCT),kConfigBeginConfig_GCIO)==NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Geoconcept config begin block %s not found.\n", + kConfigBeginConfig_GCIO); + return NULL; + } + SetGCMeta_GCIO(hGCT, CreateHeader_GCIO()); + if( (Meta= GetGCMeta_GCIO(hGCT))==NULL ) + { + return NULL; + } + while( _get_GCIO(hGCT)!=EOF ) + { + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vComType_GCIO ) + { + continue; + } + if( (enum _tIO_MetadataType_GCIO)GetGCWhatIs_GCIO(hGCT) == vHeader_GCIO ) + { + if( strstr(GetGCCache_GCIO(hGCT),kConfigEndConfig_GCIO)!=NULL ) + { + eoc= 1; + break; + } + res= OGRERR_NONE; + if( strstr(GetGCCache_GCIO(hGCT),kConfigBeginMap_GCIO)!=NULL ) + { + res= _readConfigMap_GCIO(hGCT); + } + else + if( strstr(GetGCCache_GCIO(hGCT),kConfigBeginType_GCIO)!=NULL ) + { + res= _readConfigType_GCIO(hGCT); + } + else + if( strstr(GetGCCache_GCIO(hGCT),kConfigBeginField_GCIO)!=NULL ) + { + res= _readConfigField_GCIO(hGCT); + } + else + { /* Skipping : Version, Origin, ... */ + res= OGRERR_NONE; + } + if( res != OGRERR_NONE ) + { + goto onError; + } + continue; + } +onError: + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGCT))); + CPLError( CE_Failure, CPLE_AppDefined, + "Geoconcept config syntax error at line %ld.\n", + GetGCCurrentLinenum_GCIO(hGCT) ); + return NULL; + } + if (eoc!=1) + { + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGCT))); + CPLError( CE_Failure, CPLE_AppDefined, + "Geoconcept config end block %s not found.\n", + kConfigEndConfig_GCIO); + return NULL; + } + + if( (nt= CPLListCount(GetMetaTypes_GCIO(Meta)))==0 ) + { + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGCT))); + CPLError( CE_Failure, CPLE_AppDefined, + "No types found.\n"); + return NULL; + } + /* for each general fields add it on top of types' fields list */ + if( GetMetaFields_GCIO(Meta) ) + { + if( (n= CPLListCount(GetMetaFields_GCIO(Meta)))>0 ) + { + for (i= n-1; i>=0; i--) + { + if( (e= CPLListGet(GetMetaFields_GCIO(Meta),i)) ) + { + if( (theField= (GCField*)CPLListGetData(e)) ) + { + l[0]= '\0'; + ll= 0; + if( (nl= CSLCount(GetFieldList_GCIO(theField)))>0 ) + { + for (il= 0; il=0; i--) + { + if( (e= CPLListGet(GetMetaFields_GCIO(Meta),i)) ) + { + if( (theField= (GCField*)CPLListGetData(e)) ) + { + _DestroyField_GCIO(&theField); + } + } + } + } + CPLListDestroy(GetMetaFields_GCIO(Meta)); + SetMetaFields_GCIO(Meta, NULL); + } + + /* for each field of types add it on top of types' subtypes field list */ + for (it= 0; it0 ) + { + for (i= n-1; i>=0; i--) + { + if( (e= CPLListGet(GetTypeFields_GCIO(theClass),i)) ) + { + if( (theField= (GCField*)CPLListGetData(e)) ) + { + l[0]= '\0'; + ll= 0; + if( (nl= CSLCount(GetFieldList_GCIO(theField)))>0 ) + { + for (il= 0; il0 ) + { + for (i= n-1; i>=0; i--) + { + if( (e= CPLListGet(GetTypeFields_GCIO(theClass),i)) ) + { + if( (theField= (GCField*)CPLListGetData(e)) ) + { + _DestroyField_GCIO(&theField); + } + } + } + } + CPLListDestroy(GetTypeFields_GCIO(theClass)); + SetTypeFields_GCIO(theClass, NULL); + } + } + } + + /* let's reorder sub-types fields : */ + for (it= 0; it0 ) + { + if( (i= _findFieldByName_GCIO(GetSubTypeFields_GCIO(theSubType),kIdentifier_GCIO))!=-1 ) + { + e= CPLListGet(GetSubTypeFields_GCIO(theSubType),i); + if( !(orderedFields= CPLListAppend(orderedFields,(GCField*)CPLListGetData(e))) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to arrange Geoconcept subtype '%s.%s' fields list.\n", + GetTypeName_GCIO(theClass), GetSubTypeName_GCIO(theSubType)); + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGCT))); + return NULL; + } + } + if( (i= _findFieldByName_GCIO(GetSubTypeFields_GCIO(theSubType),kClass_GCIO))!=-1 ) + { + e= CPLListGet(GetSubTypeFields_GCIO(theSubType),i); + if( !(orderedFields= CPLListAppend(orderedFields,(GCField*)CPLListGetData(e))) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to arrange Geoconcept subtype '%s.%s' fields list.\n", + GetTypeName_GCIO(theClass), GetSubTypeName_GCIO(theSubType)); + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGCT))); + return NULL; + } + } + if( (i= _findFieldByName_GCIO(GetSubTypeFields_GCIO(theSubType),kSubclass_GCIO))!=-1 ) + { + e= CPLListGet(GetSubTypeFields_GCIO(theSubType),i); + if( !(orderedFields= CPLListAppend(orderedFields,(GCField*)CPLListGetData(e))) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to arrange Geoconcept subtype '%s.%s' fields list.\n", + GetTypeName_GCIO(theClass), GetSubTypeName_GCIO(theSubType)); + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGCT))); + return NULL; + } + } + if( (i= _findFieldByName_GCIO(GetSubTypeFields_GCIO(theSubType),kName_GCIO))!=-1 ) + { + e= CPLListGet(GetSubTypeFields_GCIO(theSubType),i); + if( !(orderedFields= CPLListAppend(orderedFields,(GCField*)CPLListGetData(e))) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to arrange Geoconcept subtype '%s.%s' fields list.\n", + GetTypeName_GCIO(theClass), GetSubTypeName_GCIO(theSubType)); + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGCT))); + return NULL; + } + } + if( (i= _findFieldByName_GCIO(GetSubTypeFields_GCIO(theSubType),kNbFields_GCIO))!=-1 ) + { + e= CPLListGet(GetSubTypeFields_GCIO(theSubType),i); + if( !(orderedFields= CPLListAppend(orderedFields,(GCField*)CPLListGetData(e))) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to arrange Geoconcept subtype '%s.%s' fields list.\n", + GetTypeName_GCIO(theClass), GetSubTypeName_GCIO(theSubType)); + DestroyHeader_GCIO(&(GetGCMeta_GCIO(hGCT))); + return NULL; + } + } + for( i= 0; i0 ) + { + for (iF= 0; iF0) fprintf(gc,"%c", delim); + if( IsPrivateField_GCIO(theField) ) + { + fprintf(gc,"%s%s", kPrivate_GCIO, GetFieldName_GCIO(theField)+1); + } + else + { + fprintf(gc,"%s%s", kPublic_GCIO, GetFieldName_GCIO(theField)); + } + } + } + } + } + fprintf(gc,"\n"); + SetSubTypeHeaderWritten_GCIO(theSubType,TRUE); + + return gc; +}/* _writeFieldsPragma_GCIO */ + +/* -------------------------------------------------------------------- */ +GCExportFileH GCIOAPI_CALL1(*) WriteHeader_GCIO ( + GCExportFileH* H + ) +{ + GCExportFileMetadata* Meta; + int nT, iT, nS, iS; + GCSubType* theSubType; + GCType* theClass; + CPLList* e; + FILE* gc; + + /* FIXME : howto change default values ? */ + /* there seems to be no ways in Geoconcept to change them ... */ + Meta= GetGCMeta_GCIO(H); + gc= GetGCHandle_GCIO(H); + if( GetMetaVersion_GCIO(Meta) ) + { + fprintf(gc,"%s%s %s\n", kPragma_GCIO, kMetadataVERSION_GCIO, GetMetaVersion_GCIO(Meta)); + } + fprintf(gc,"%s%s \"%s\"\n", kPragma_GCIO, kMetadataDELIMITER_GCIO, _metaDelimiter2str_GCIO(GetMetaDelimiter_GCIO(Meta))); + fprintf(gc,"%s%s \"%s\"\n", kPragma_GCIO, kMetadataQUOTEDTEXT_GCIO, GetMetaQuotedText_GCIO(Meta)? "yes":"no"); + fprintf(gc,"%s%s %s\n", kPragma_GCIO, kMetadataCHARSET_GCIO, GCCharset2str_GCIO(GetMetaCharset_GCIO(Meta))); + if( strcmp(GetMetaUnit_GCIO(Meta),"deg")==0 || + strcmp(GetMetaUnit_GCIO(Meta),"deg.min")==0 || + strcmp(GetMetaUnit_GCIO(Meta),"rad")==0 || + strcmp(GetMetaUnit_GCIO(Meta),"gr")==0 ) + { + fprintf(gc,"%s%s Angle:%s\n", kPragma_GCIO, kMetadataUNIT_GCIO, GetMetaUnit_GCIO(Meta)); + } + else + { + fprintf(gc,"%s%s Distance:%s\n", kPragma_GCIO, kMetadataUNIT_GCIO, GetMetaUnit_GCIO(Meta)); + } + fprintf(gc,"%s%s %d\n", kPragma_GCIO, kMetadataFORMAT_GCIO, GetMetaFormat_GCIO(Meta)); + if( GetMetaSysCoord_GCIO(Meta) ) + { + fprintf(gc,"%s%s {Type: %d}", kPragma_GCIO, + kMetadataSYSCOORD_GCIO, + GetSysCoordSystemID_GCSRS(GetMetaSysCoord_GCIO(Meta))); + if( GetSysCoordTimeZone_GCSRS(GetMetaSysCoord_GCIO(Meta))!=-1 ) + { + fprintf(gc,";{TimeZone: %d}", GetSysCoordTimeZone_GCSRS(GetMetaSysCoord_GCIO(Meta))); + } + } + else + { + fprintf(gc,"%s%s {Type: -1}", kPragma_GCIO, kMetadataSYSCOORD_GCIO); + } + fprintf(gc,"\n"); + + if( (nT= CPLListCount(GetMetaTypes_GCIO(Meta)))>0 ) + { + for (iT= 0; iT0 ) + { + for (iS= 0; iSY[<>Z]{Single Polygon{<>NrPolys=j[<>X<>Y[<>Z]<>Single Polygon]j}} + * with : + * Single Polygon = Nr points=k[<>PointX<>PointY[<>Z]]k... + */ + if( (nR= OGR_G_GetGeometryCount(poPoly))==0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Ignore POLYGON EMPTY in Geoconcept writer.\n" ); + return TRUE; + } + poRing= OGR_G_GetGeometryRef(poPoly,0); + if( !_writeLine_GCIO(h,quotes,delim,poRing,vPoly_GCIO,dim,fmt,e,pCS,hCS) ) + { + return FALSE; + } + /* number of interior rings : */ + if( nR>1 ) + { + if( VSIFPrintf(h,"%c%d%c", delim, nR-1, delim)<=0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Write failed.\n"); + return FALSE; + } + for( iR= 1; iR + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + **********************************************************************/ +#ifndef _GEOCONCEPT_H_INCLUDED +#define _GEOCONCEPT_H_INCLUDED + +#include "cpl_port.h" +#include "cpl_list.h" +#include "cpl_vsi.h" +#include "ogr_api.h" + +#include "geoconcept_syscoord.h" + +/* From shapefil.h : */ +/* -------------------------------------------------------------------- */ +/* GCIOAPI_CALL */ +/* */ +/* The following two macros are present to allow forcing */ +/* various calling conventions on the Geoconcept API. */ +/* */ +/* To force __stdcall conventions (needed to call GCIOLib */ +/* from Visual Basic and/or Dephi I believe) the makefile could */ +/* be modified to define: */ +/* */ +/* /DGCIOAPI_CALL=__stdcall */ +/* */ +/* If it is desired to force export of the GCIOLib API without */ +/* using the geoconcept.def file, use the following definition. */ +/* */ +/* /DGCIO_DLLEXPORT */ +/* */ +/* To get both at once it will be necessary to hack this */ +/* include file to define: */ +/* */ +/* #define GCIOAPI_CALL __declspec(dllexport) __stdcall */ +/* #define GCIOAPI_CALL1 __declspec(dllexport) * __stdcall */ +/* */ +/* The complexity of the situation is partly caused by the */ +/* peculiar requirement of Visual C++ that __stdcall appear */ +/* after any "*"'s in the return value of a function while the */ +/* __declspec(dllexport) must appear before them. */ +/* -------------------------------------------------------------------- */ + +#ifdef GCIO_DLLEXPORT +# define GCIOAPI_CALL __declspec(dllexport) +# define GCIOAPI_CALL1(x) __declspec(dllexport) x +#endif + +#ifndef GCIOAPI_CALL +# define GCIOAPI_CALL +#endif + +#ifndef GCIOAPI_CALL1 +# define GCIOAPI_CALL1(x) x GCIOAPI_CALL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define kCacheSize_GCIO 65535 +#define kCharsetMAX_GCIO 15 +#define kUnitMAX_GCIO 7 +#define kTAB_GCIO "\t" +#define kCom_GCIO "//" +#define kHeader_GCIO "//#" +#define kPragma_GCIO "//$" +#define kCartesianPlanarRadix 2 +#define kGeographicPlanarRadix 9 +#define kElevationRadix 2 + +#define kConfigBeginConfig_GCIO "SECTION CONFIG" +#define kConfigEndConfig_GCIO "ENDSECTION CONFIG" +#define kConfigBeginMap_GCIO "SECTION MAP" +#define kConfigEndMap_GCIO "ENDSECTION MAP" +#define kConfigBeginType_GCIO "SECTION TYPE" +#define kConfigBeginSubType_GCIO "SECTION SUBTYPE" +#define kConfigBeginField_GCIO "SECTION FIELD" +#define kConfigEndField_GCIO "ENDSECTION FIELD" +#define kConfigEndSubType_GCIO "ENDSECTION SUBTYPE" +#define kConfigEndType_GCIO "ENDSECTION TYPE" + +#define kConfigUnit_GCIO "Unit" +#define kConfigPrecision_GCIO "Precision" +#define kConfigName_GCIO "Name" +#define kConfigID_GCIO "ID" +#define kConfigKind_GCIO "Kind" +#define kConfig3D_GCIO "3D" +#define kConfigExtra_GCIO "Extra" +#define kConfigExtraText_GCIO "ExtraText" +#define kConfigList_GCIO "List" +/* unused config keywords : */ +#define kConfigZUnit_GCIO "ZUnit" +#define kConfigZPrecision_GCIO "ZPrecision" + +#define kMetadataVERSION_GCIO "VERSION" +#define kMetadataDELIMITER_GCIO "DELIMITER" +#define kMetadataQUOTEDTEXT_GCIO "QUOTED-TEXT" +#define kMetadataCHARSET_GCIO "CHARSET" +#define kMetadataUNIT_GCIO "UNIT" +#define kMetadataFORMAT_GCIO "FORMAT" +#define kMetadataSYSCOORD_GCIO "SYSCOORD" +#define kMetadataFIELDS_GCIO "FIELDS" +#define kPrivate_GCIO "Private#" +#define kPublic_GCIO "" +#define k3DOBJECTMONO_GCIO "3DOBJECTMONO" +#define k3DOBJECT_GCIO "3DOBJECT" +#define k2DOBJECT_GCIO "2DOBJECT" + +#define kIdentifier_GCIO "@Identifier" +#define kClass_GCIO "@Class" +#define kSubclass_GCIO "@Subclass" +#define kName_GCIO "@Name" +#define kNbFields_GCIO "@NbFields" +#define kX_GCIO "@X" +#define kY_GCIO "@Y" +#define kXP_GCIO "@XP" +#define kYP_GCIO "@YP" +#define kGraphics_GCIO "@Graphics" +#define kAngle_GCIO "@Angle" + +#define WRITEERROR_GCIO -1 +#define GEOMETRYEXPECTED_GCIO -2 +#define WRITECOMPLETED_GCIO -3 + +/* -------------------------------------------------------------------- */ +/* GCIO API Enumerations */ +/* -------------------------------------------------------------------- */ + +typedef enum _tCharset_GCIO { + vUnknownCharset_GCIO = 0, + vANSI_GCIO, /* Windows */ + vDOS_GCIO, + vMAC_GCIO +} GCCharset; + +typedef enum _tAccessMode_GCIO { + vUnknownAccessMode_GCIO = 0, + vNoAccess_GCIO, + vReadAccess_GCIO, + vUpdateAccess_GCIO, + vWriteAccess_GCIO +} GCAccessMode; + +typedef enum _tAccessStatus_GCIO { + vNoStatus_GCIO, + vMemoStatus_GCIO, + vEof_GCIO +} GCAccessStatus; + +typedef enum _t3D_GCIO { + vUnknown3D_GCIO = 0, + v2D_GCIO, + v3D_GCIO, + v3DM_GCIO +} GCDim; + +typedef enum _tItemType_GCIO { + vUnknownItemType_GCIO = 0, + vPoint_GCIO, + vLine_GCIO, + vText_GCIO, + vPoly_GCIO, + vMemoFld_GCIO, + vIntFld_GCIO, + vRealFld_GCIO, + vLengthFld_GCIO, + vAreaFld_GCIO, + vPositionFld_GCIO, + vDateFld_GCIO, + vTimeFld_GCIO, + vChoiceFld_GCIO, + vInterFld_GCIO +} GCTypeKind; + +typedef enum _tIO_MetadataType_GCIO { + vUnknownIO_ItemType_GCIO = 0, + vComType_GCIO, + vStdCol_GCIO, + vEndCol_GCIO, + vHeader_GCIO, + vPragma_GCIO, + vConfigBeginConfig_GCIO, + vConfigEndConfig_GCIO, + vConfigBeginMap_GCIO, + vConfigEndMap_GCIO, + vConfigBeginType_GCIO, + vConfigBeginSubType_GCIO, + vConfigBeginField_GCIO, + vConfigEndField_GCIO, + vConfigEndSubType_GCIO, + vConfigEndType_GCIO, + vMetadataDELIMITER_GCIO, + vMetadataQUOTEDTEXT_GCIO, + vMetadataCHARSET_GCIO, + vMetadataUNIT_GCIO, + vMetadataFORMAT_GCIO, + vMetadataSYSCOORD_GCIO, + vMetadataFIELDS_GCIO +} MetadataType; + +typedef enum _boolean_GCIO {vFALSE=0,vTRUE} tBOOLEAN_GCIO ; + +/* -------------------------------------------------------------------- */ +/* GCIO API Types */ +/* -------------------------------------------------------------------- */ +typedef struct _tDocFrame_GCIO GCExtent; +typedef struct _tField_GCIO GCField; +typedef struct _tSubType_GCIO GCSubType; +typedef struct _tType_GCIO GCType; +typedef struct _tExportHeader_GCIO GCExportFileMetadata; +typedef struct _GCExportFileH GCExportFileH; + +struct _tDocFrame_GCIO { + double XUL; + double YUL; + double XLR; + double YLR; +}; + +struct _tField_GCIO { + char* name; /* @name => private */ + char* extra; + char** enums; + long id; + GCTypeKind knd; +}; + +struct _tSubType_GCIO { + GCExportFileH* _h; + GCType* _type; /* parent's type */ + char* name; + CPLList* fields; /* GCField */ + GCExtent* frame; + OGRFeatureDefnH _poFeaDefn; + long id; + long _foff; /* offset 1st feature */ + unsigned long _flin; /* 1st ligne 1st feature */ + unsigned long _nFeatures; + GCTypeKind knd; + GCDim sys; + int _nbf; /* number of user's fields */ + int _hdrW; /* pragma field written */ +}; + +struct _tType_GCIO { + char* name; + CPLList* subtypes; /* GCSubType */ + CPLList* fields; /* GCField */ + long id; +}; + +struct _tExportHeader_GCIO { + CPLList* types; /* GCType */ + CPLList* fields; /* GCField */ + OGRSpatialReferenceH srs; + GCExtent* frame; + char* version; + char unit[kUnitMAX_GCIO+1]; + double resolution; + GCCharset charset; + int quotedtext; + int format; + GCSysCoord* sysCoord; + int pCS; + int hCS; + char delimiter; +}; + +struct _GCExportFileH { + char cache[kCacheSize_GCIO+1]; + char* path; + char* bn; + char* ext; + FILE* H; + GCExportFileMetadata* header; + long coff; + unsigned long clin; + unsigned long nbObjects; + GCAccessMode mode; + GCAccessStatus status; + GCTypeKind whatIs; +}; + +/* -------------------------------------------------------------------- */ +/* GCIO API Prototypes */ +/* -------------------------------------------------------------------- */ + +const char GCIOAPI_CALL1(*) GCCharset2str_GCIO ( GCCharset cs ); +GCCharset GCIOAPI_CALL str2GCCharset_GCIO ( const char* s); +const char GCIOAPI_CALL1(*) GCAccessMode2str_GCIO ( GCAccessMode mode ); +GCAccessMode str2GCAccessMode_GCIO ( const char* s); +const char GCIOAPI_CALL1(*) GCAccessStatus2str_GCIO ( GCAccessStatus stts ); +GCAccessStatus str2GCAccessStatus_GCIO ( const char* s); +const char GCIOAPI_CALL1(*) GCDim2str_GCIO ( GCDim sys ); +GCDim str2GCDim ( const char* s ); +const char GCIOAPI_CALL1(*) GCTypeKind2str_GCIO ( GCTypeKind item ); +GCTypeKind str2GCTypeKind_GCIO ( const char *s ); + +GCExportFileH GCIOAPI_CALL1(*) Open_GCIO ( const char* pszGeoconceptFile, const char* ext, const char* mode, const char* gctPath ); +void GCIOAPI_CALL Close_GCIO ( GCExportFileH** hGCT ); +GCExportFileH GCIOAPI_CALL1(*) Rewind_GCIO ( GCExportFileH* H, GCSubType* theSubType ); +GCExportFileH GCIOAPI_CALL1(*) FFlush_GCIO ( GCExportFileH* H ); + +GCType GCIOAPI_CALL1(*) AddType_GCIO ( GCExportFileH* H, const char* typName, long id ); +GCSubType GCIOAPI_CALL1(*) AddSubType_GCIO ( GCExportFileH* H, const char* typName, const char* subtypName, long id, GCTypeKind knd, GCDim sys ); +GCField GCIOAPI_CALL1(*) AddTypeField_GCIO ( GCExportFileH* H, const char* typName, int where, const char* name, long id, GCTypeKind knd, const char* extra, const char* enums ); +GCField GCIOAPI_CALL1(*) AddSubTypeField_GCIO ( GCExportFileH* H, const char* typName, const char* subtypName, int where, const char* name, long id, GCTypeKind knd, const char* extra, const char* enums ); +GCExportFileMetadata GCIOAPI_CALL1(*) ReadConfig_GCIO ( GCExportFileH* H ); +GCExportFileH GCIOAPI_CALL1(*) WriteHeader_GCIO ( GCExportFileH* H ); +GCExportFileMetadata GCIOAPI_CALL1(*) CreateHeader_GCIO ( ); +void GCIOAPI_CALL DestroyHeader_GCIO ( GCExportFileMetadata** m ); +GCExtent GCIOAPI_CALL1(*) CreateExtent_GCIO ( double Xmin, double Ymin, double Xmax, double Ymax ); +void GCIOAPI_CALL DestroyExtent_GCIO ( GCExtent** theExtent ); +GCExportFileMetadata GCIOAPI_CALL1(*) ReadHeader_GCIO ( GCExportFileH* H ); +GCSubType GCIOAPI_CALL1(*) FindFeature_GCIO ( GCExportFileH* H, const char* typDOTsubtypName ); +int GCIOAPI_CALL FindFeatureFieldIndex_GCIO ( GCSubType* theSubType, const char *fieldName ); +GCField GCIOAPI_CALL1(*) FindFeatureField_GCIO ( GCSubType* theSubType, const char *fieldName ); +int GCIOAPI_CALL StartWritingFeature_GCIO ( GCSubType* theSubType, long id ); +int GCIOAPI_CALL WriteFeatureFieldAsString_GCIO ( GCSubType* theSubType, int iField, const char* theValue ); +int GCIOAPI_CALL WriteFeatureGeometry_GCIO ( GCSubType* theSubType, OGRGeometryH poGeom ); +void GCIOAPI_CALL StopWritingFeature_GCIO ( GCSubType* theSubType ); +OGRFeatureH GCIOAPI_CALL ReadNextFeature_GCIO ( GCSubType* theSubType ); + +#define GetGCCache_GCIO(gc) (gc)->cache +#define SetGCCache_GCIO(gc,v) strncpy((gc)->cache, (v), kCacheSize_GCIO), (gc)->cache[kCacheSize_GCIO]= '\0'; +#define GetGCPath_GCIO(gc) (gc)->path +#define SetGCPath_GCIO(gc,v) (gc)->path= (v) +#define GetGCBasename_GCIO(gc) (gc)->bn +#define SetGCBasename_GCIO(gc,v) (gc)->bn= (v) +#define GetGCExtension_GCIO(gc) (gc)->ext +#define SetGCExtension_GCIO(gc,v) (gc)->ext= (v) +#define GetGCHandle_GCIO(gc) (gc)->H +#define SetGCHandle_GCIO(gc,v) (gc)->H= (v) +#define GetGCMeta_GCIO(gc) (gc)->header +#define SetGCMeta_GCIO(gc,v) (gc)->header= (v) +#define GetGCCurrentOffset_GCIO(gc) (gc)->coff +#define SetGCCurrentOffset_GCIO(gc,v) (gc)->coff= (v) +#define GetGCCurrentLinenum_GCIO(gc) (gc)->clin +#define SetGCCurrentLinenum_GCIO(gc,v) (gc)->clin= (v) +#define GetGCNbObjects_GCIO(gc) (gc)->nbObjects +#define SetGCNbObjects_GCIO(gc,v) (gc)->nbObjects= (v) +#define GetGCMode_GCIO(gc) (gc)->mode +#define SetGCMode_GCIO(gc,v) (gc)->mode= (v) +#define GetGCStatus_GCIO(gc) (gc)->status +#define SetGCStatus_GCIO(gc,v) (gc)->status= (v) +#define GetGCWhatIs_GCIO(gc) (gc)->whatIs +#define SetGCWhatIs_GCIO(gc,v) (gc)->whatIs= (v) + +#define GetMetaTypes_GCIO(header) (header)->types +#define SetMetaTypes_GCIO(header,v) (header)->types= (v) +#define CountMetaTypes_GCIO(header) CPLListCount((header)->types) +#define GetMetaType_GCIO(header,rank) (GCType*)CPLListGetData(CPLListGet((header)->types,(rank))) +#define GetMetaFields_GCIO(header) (header)->fields +#define SetMetaFields_GCIO(header,v) (header)->fields= (v) +#define GetMetaCharset_GCIO(header) (header)->charset +#define SetMetaCharset_GCIO(header,v) (header)->charset= (v) +#define GetMetaDelimiter_GCIO(header) (header)->delimiter +#define SetMetaDelimiter_GCIO(header,v) (header)->delimiter= (v) +#define GetMetaUnit_GCIO(header) (header)->unit +#define SetMetaUnit_GCIO(header,v) strncpy((header)->unit, (v), kUnitMAX_GCIO), (header)->unit[kUnitMAX_GCIO]= '\0'; +#define GetMetaZUnit_GCIO(header) (header)->zUnit +#define SetMetaZUnit_GCIO(header,v) strncpy((header)->zUnit, (v), kUnitMAX_GCIO), (header)->zUnit[kUnitMAX_GCIO]= '\0'; +#define GetMetaResolution_GCIO(header) (header)->resolution +#define SetMetaResolution_GCIO(header,v) (header)->resolution= (v) +#define GetMetaZResolution_GCIO(header) (header)->zResolution +#define SetMetaZResolution_GCIO(header,v) (header)->zResolution= (v) +#define GetMetaQuotedText_GCIO(header) (header)->quotedtext +#define SetMetaQuotedText_GCIO(header,v) (header)->quotedtext= (v) +#define GetMetaFormat_GCIO(header) (header)->format +#define SetMetaFormat_GCIO(header,v) (header)->format= (v) +#define GetMetaSysCoord_GCIO(header) (header)->sysCoord +#define SetMetaSysCoord_GCIO(header,v) (header)->sysCoord= (v) +#define GetMetaPlanarFormat_GCIO(header) (header)->pCS +#define SetMetaPlanarFormat_GCIO(header, v) (header)->pCS= (v) +#define GetMetaHeightFormat_GCIO(header) (header)->hCS +#define SetMetaHeightFormat_GCIO(header, v) (header)->hCS= (v) + +#define GetMetaExtent_GCIO(header) (header)->frame +#define SetMetaExtent_GCIO(header,v) (header)->frame= (v) +#define GetMetaSRS_GCIO(header) (header)->srs +#define SetMetaSRS_GCIO(header,v) (header)->srs= (v) +#define GetMetaVersion_GCIO(header) (header)->version +#define SetMetaVersion_GCIO(header,v) (header)->version= (v) + +#define GetTypeName_GCIO(theClass) (theClass)->name +#define SetTypeName_GCIO(theClass,v) (theClass)->name= (v) +#define GetTypeID_GCIO(theClass) (theClass)->id +#define SetTypeID_GCIO(theClass,v) (theClass)->id= (v) +#define GetTypeSubtypes_GCIO(theClass) (theClass)->subtypes +#define SetTypeSubtypes_GCIO(theClass,v) (theClass)->subtypes= (v) +#define GetTypeFields_GCIO(theClass) (theClass)->fields +#define SetTypeFields_GCIO(theClass,v) (theClass)->fields= (v) +#define CountTypeSubtypes_GCIO(theClass) CPLListCount((theClass)->subtypes) +#define GetTypeSubtype_GCIO(theClass,rank) (GCSubType*)CPLListGetData(CPLListGet((theClass)->subtypes,(rank))) +#define CountTypeFields_GCIO(theClass) CPLListCount((theClass)->fields) +#define GetTypeField_GCIO(theClass,rank) (GCField*)CPLListGetData(CPLListGet((theClass)->fields,(rank))) + +#define GetSubTypeType_GCIO(theSubType) (theSubType)->_type +#define SetSubTypeType_GCIO(theSubType,v) (theSubType)->_type= (v) +#define GetSubTypeName_GCIO(theSubType) (theSubType)->name +#define SetSubTypeName_GCIO(theSubType,v) (theSubType)->name= (v) +#define GetSubTypeID_GCIO(theSubType) (theSubType)->id +#define SetSubTypeID_GCIO(theSubType,v) (theSubType)->id= (v) +#define GetSubTypeKind_GCIO(theSubType) (theSubType)->knd +#define SetSubTypeKind_GCIO(theSubType,v) (theSubType)->knd= (v) +#define GetSubTypeDim_GCIO(theSubType) (theSubType)->sys +#define SetSubTypeDim_GCIO(theSubType,v) (theSubType)->sys= (v) +#define GetSubTypeFields_GCIO(theSubType) (theSubType)->fields +#define SetSubTypeFields_GCIO(theSubType,v) (theSubType)->fields= (v) +#define CountSubTypeFields_GCIO(theSubType) CPLListCount((theSubType)->fields) +#define GetSubTypeField_GCIO(theSubType,rank) (GCField*)CPLListGetData(CPLListGet((theSubType)->fields,(rank))) +#define GetSubTypeNbFields_GCIO(theSubType) (theSubType)->_nbf +#define SetSubTypeNbFields_GCIO(theSubType,v) (theSubType)->_nbf= (v) +#define GetSubTypeNbFeatures_GCIO(theSubType) (theSubType)->_nFeatures +#define SetSubTypeNbFeatures_GCIO(theSubType,v) (theSubType)->_nFeatures= (v) +#define GetSubTypeBOF_GCIO(theSubType) (theSubType)->_foff +#define SetSubTypeBOF_GCIO(theSubType,v) (theSubType)->_foff= (v) +#define GetSubTypeBOFLinenum_GCIO(theSubType) (theSubType)->_flin +#define SetSubTypeBOFLinenum_GCIO(theSubType,v) (theSubType)->_flin= (v) +#define GetSubTypeExtent_GCIO(theSubType) (theSubType)->frame +#define SetSubTypeExtent_GCIO(theSubType,v) (theSubType)->frame= (v) +#define GetSubTypeGCHandle_GCIO(theSubType) (theSubType)->_h +#define SetSubTypeGCHandle_GCIO(theSubType,v) (theSubType)->_h= (v) +#define GetSubTypeFeatureDefn_GCIO(theSubType) (theSubType)->_poFeaDefn +#define SetSubTypeFeatureDefn_GCIO(theSubType,v) (theSubType)->_poFeaDefn= (v) +#define IsSubTypeHeaderWritten_GCIO(theSubType) (theSubType)->_hdrW +#define SetSubTypeHeaderWritten_GCIO(theSubType,v) (theSubType)->_hdrW= (v) + +#define GetFieldName_GCIO(theField) (theField)->name +#define SetFieldName_GCIO(theField,v) (theField)->name= (v) +#define GetFieldID_GCIO(theField) (theField)->id +#define SetFieldID_GCIO(theField,v) (theField)->id= (v) +#define GetFieldKind_GCIO(theField) (theField)->knd +#define SetFieldKind_GCIO(theField,v) (theField)->knd= (v) +#define GetFieldExtra_GCIO(theField) (theField)->extra +#define SetFieldExtra_GCIO(theField,v) (theField)->extra= (v) +#define GetFieldList_GCIO(theField) (theField)->enums +#define SetFieldList_GCIO(theField,v) (theField)->enums= (v) +#define IsPrivateField_GCIO(theField) (*(GetFieldName_GCIO(theField))=='@') + +#define GetExtentULAbscissa_GCIO(theExtent) (theExtent)->XUL +#define GetExtentULOrdinate_GCIO(theExtent) (theExtent)->YUL +#define SetExtentULAbscissa_GCIO(theExtent,v) (theExtent)->XUL= (v) < (theExtent)->XUL? (v): (theExtent)->XUL +#define SetExtentULOrdinate_GCIO(theExtent,v) (theExtent)->YUL= (v) > (theExtent)->YUL? (v): (theExtent)->YUL +#define GetExtentLRAbscissa_GCIO(theExtent) (theExtent)->XLR +#define GetExtentLROrdinate_GCIO(theExtent) (theExtent)->YLR +#define SetExtentLRAbscissa_GCIO(theExtent,v) (theExtent)->XLR= (v) > (theExtent)->XLR? (v): (theExtent)->XLR +#define SetExtentLROrdinate_GCIO(theExtent,v) (theExtent)->YLR= (v) < (theExtent)->YLR? (v): (theExtent)->YLR + +/* OGREnvelope C API : */ +#define InitOGREnvelope_GCIO(poEvlp) \ + if( poEvlp!=NULL ) \ + {\ + (poEvlp)->MinX= (poEvlp)->MinY= HUGE_VAL;\ + (poEvlp)->MaxX= (poEvlp)->MaxY= -HUGE_VAL;\ + } + +#define MergeOGREnvelope_GCIO(poEvlp,x,y) \ + if( poEvlp!=NULL ) \ + {\ + if( (x) < (poEvlp)->MinX ) (poEvlp)->MinX= (x);\ + if( (x) > (poEvlp)->MaxX ) (poEvlp)->MaxX= (x);\ + if( (y) < (poEvlp)->MinY ) (poEvlp)->MinY= (y);\ + if( (y) > (poEvlp)->MaxY ) (poEvlp)->MaxY= (y);\ + } + +#ifdef __cplusplus +} +#endif + + +#endif /* ndef _GEOCONCEPT_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/geoconcept_syscoord.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/geoconcept_syscoord.c new file mode 100644 index 000000000..c749ac9da --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/geoconcept_syscoord.c @@ -0,0 +1,1024 @@ +/********************************************************************** + * $Id: geoconcept_syscoord.c + * + * Name: geoconcept_syscoord.c + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements translation between Geoconcept SysCoord + * and OGRSpatialRef format + * Language: C + * + ********************************************************************** + * Copyright (c) 2007, Geoconcept and IGN + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + **********************************************************************/ + +#include "geoconcept_syscoord.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: geoconcept_syscoord.c,v 1.0.0 2007-12-24 15:40:28 drichard Exp $") + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + + +/* -------------------------------------------------------------------- */ +/* GCSRS globals */ +/* -------------------------------------------------------------------- */ + +/* + * The following information came from GEO CONCEPT PROJECTION files, + * aka GCP files. + * A lot of information has been added to these GCP. There are mostly + * noticed as FIXME in the source. + */ + +static GCSysCoord gk_asSysCoordList[]= +/* + * pszSysCoordName, pszUnit, dfPM, dfLambda0, dfPhi0, dfk0, dfX0, dfY0, dfPhi1, dfPhi2, nDatumID, nProjID, coordSystemID, timeZoneValue + * + * #12, #14, #15, #17 : parameters listed below are "generic" ... + * + * Geoconcept uses cos(lat_ts) as scale factor, but cos(lat_ts)==cos(-lat_ts) : I then set dfPhi1 with lat_ts + */ +{ +{"Lambert 2 extended", NULL, 2.337229166667, 0.000000000, 46.80000000,0.99987742000, 600000.000, 2200000.000, 0.0, 0.0, 13, 2, 1,-1}, +{"Lambert 1", NULL, 2.337229166667, 0.000000000, 49.50000000,0.99987734000, 600000.000, 200000.000, 0.0, 0.0, 13, 2, 2,-1}, +{"Lambert 2", NULL, 2.337229166667, 0.000000000, 46.80000000,0.99987742000, 600000.000, 200000.000, 0.0, 0.0, 13, 2, 3,-1}, +{"Lambert 3", NULL, 2.337229166667, 0.000000000, 44.10000000,0.99987750000, 600000.000, 200000.000, 0.0, 0.0, 13, 2, 4,-1}, +{"Lambert 4", NULL, 2.337229166667, 0.000000000, 42.16500000,0.99994471000, 234.358, 185861.369, 0.0, 0.0, 13, 2, 5,-1}, +{"Bonne NTF", NULL, 2.337222222222, 0.000000000, 48.86000000,1.00000000000, 0.000, 0.000, 0.0, 0.0, 1, 3, 11,-1}, +{"UTM Nord - ED50", NULL, 0.000000000000, 0.000000000, 0.00000000,0.99960000000, 500000.000, 0.000, 0.0, 0.0, 14, 1, 12, 0}, +{"Plate carrée", NULL, 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, 11, 4, 13,-1}, +{"MGRS (Military UTM)", NULL, 0.000000000000, 0.000000000, 0.00000000,0.99960000000, 0.000, 0.000, 0.0, 0.0, 4, 11, 14,-1}, +{"UTM Sud - WGS84", NULL, 0.000000000000, 0.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0, 4, 1, 15, 0}, +{"National GB projection", NULL, 0.000000000000, -2.000000000, 49.00000000,0.99960127170, 400000.000, -100000.000, 0.0, 0.0, 12, 12, 16,-1}, +{"UTM Nord - WGS84", NULL, 0.000000000000, 0.000000000, 0.00000000,0.99960000000, 500000.000, 0.000, 0.0, 0.0, 4, 1, 17, 0}, +{"UTM Nord - WGS84", NULL, 0.000000000000, 0.000000000, 0.00000000,0.99960000000, 500000.000, 0.000, 0.0, 0.0,9990, 1, 17, 0}, +{"Lambert 2 étendu - sans grille", NULL, 2.337229166667, 0.000000000, 46.80000000,0.99987742000, 600000.000, 2200000.000, 0.0, 0.0, 1, 2, 91,-1}, +{"Lambert 1 - sans grille", NULL, 2.337229166667, 0.000000000, 49.50000000,0.99987734000, 600000.000, 200000.000, 0.0, 0.0, 1, 2, 92,-1}, +{"Lambert 2 - sans grille", NULL, 2.337229166667, 0.000000000, 46.80000000,0.99987742000, 600000.000, 200000.000, 0.0, 0.0, 1, 2, 93,-1}, +{"Lambert 3 - sans grille", NULL, 2.337229166667, 0.000000000, 44.10000000,0.99987750000, 600000.000, 200000.000, 0.0, 0.0, 1, 2, 94,-1}, +{"Lambert 4 - sans grille", NULL, 2.337229166667, 0.000000000, 42.16500000,0.99994471000, 234.358, 185861.369, 0.0, 0.0, 1, 2, 95,-1}, +{"(Long/Lat) NTF", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, 1, 0, 100,-1}, +{"(Long/Lat) WGS84", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, 4, 0, 101,-1}, +{"(Long/Lat) ED50", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, 14, 0, 102,-1}, +{"(Long/Lat) Australian 1984", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, 7, 0, 103,-1}, +{"(Long/Lat) Airy", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, 12, 0, 104,-1}, +{"(Long/Lat) NTF Paris (gr)", "gr", 2.337229166667, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, 1, 0, 105,-1}, +{"(Long/Lat) WGS 72", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, 3, 0, 107,-1}, +{"Geoportail MILLER", NULL, 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, 4, 24, 222,-1}, +{"IGN-RRAFGUADU20", NULL, 0.000000000000, -63.000000000, 0.00000000,0.99960000000, 500000.000, 0.000, 0.0, 0.0,9984, 1, 501,-1},/* FIXME does not exist in IGNF, use IGN-UTM20W84GUAD instead */ +{"IGN-RRAFMARTU20", NULL, 0.000000000000, -63.000000000, 0.00000000,0.99960000000, 500000.000, 0.000, 0.0, 0.0,9984, 1, 502,-1},/* FIXME does not exist in IGNF, use IGN-UTM20W84MART instead, never reached cause identical to 501:-1 */ +{"IGN-RGM04UTM38S", NULL, 0.000000000000, 45.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0,9984, 1, 503,-1},/* FIXME 5030 datum changed into 9984 */ +{"IGN-RGR92UTM40S", NULL, 0.000000000000, 57.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0,9984, 1, 504,-1}, +{"IGN-UTM22RGFG95", NULL, 0.000000000000, -51.000000000, 0.00000000,0.99960000000, 500000.000, 0.000, 0.0, 0.0,9984, 1, 505,-1}, +{"IGN-UTM01SWG84", NULL, 0.000000000000,-177.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0,9984, 1, 506,-1},/* never reached cause identical to 15:1 */ +{"IGN-RGSPM06U21", NULL, 0.000000000000, -57.000000000, 0.00000000,0.99960000000, 500000.000, 0.000, 0.0, 0.0,9984, 1, 507,-1}, +{"IGN-RGPFUTM5S", NULL, 0.000000000000,-153.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0,9984, 1, 508,-1}, +{"IGN-RGPFUTM6S", NULL, 0.000000000000,-147.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0,9984, 1, 509,-1}, +{"IGN-RGPFUTM7S", NULL, 0.000000000000,-141.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0,9984, 1, 510,-1}, +{"IGN-CROZ63UTM39S", NULL, 0.000000000000, 51.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0,9983, 1, 511,-1}, +{"IGN-WGS84UTM1S", NULL, 0.000000000000,-177.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0, 4, 1, 512,-1}, +{"IGN-RGNCUTM57S", NULL, 0.000000000000, 159.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0,9984, 1, 513,-1}, +{"IGN-RGNCUTM58S", NULL, 0.000000000000, 165.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0,9984, 1, 514,-1}, +{"IGN-RGNCUTM59S", NULL, 0.000000000000, 171.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0,9984, 1, 515,-1}, +{"IGN-KERG62UTM42S", NULL, 0.000000000000, 69.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0,9988, 1, 516,-1}, +{"IGN-REUN47GAUSSL", NULL, 0.000000000000, 55.533333333,-21.11666667,1.00000000000, 160000.000, 50000.000, 0.0, 0.0, 2, 19, 520,-1}, +{"Lambert 1 Carto", NULL, 2.337229166667, 0.000000000, 49.50000000,0.99987734000, 600000.000, 1200000.000, 0.0, 0.0, 13, 2, 1002,-1}, +{"Lambert 2 Carto", NULL, 2.337229166667, 0.000000000, 46.80000000,0.99987742000, 600000.000, 2200000.000, 0.0, 0.0, 13, 2, 1003,-1},/* never reached cause identical to 1:-1 */ +{"Lambert 3 Carto", NULL, 2.337229166667, 0.000000000, 44.10000000,0.99987750000, 600000.000, 3200000.000, 0.0, 0.0, 13, 2, 1004,-1}, +{"Lambert 4 Carto", NULL, 2.337229166667, 0.000000000, 42.16500000,0.99994471000, 234.358, 4185861.369, 0.0, 0.0, 13, 2, 1005,-1}, +{"Lambert 93", NULL, 0.000000000000, 3.000000000, 46.50000000,0.00000000000, 700000.000, 6600000.000, 44.0, 49.0,9984, 18, 1006,-1}, +{"IGN-RGNCLAM", NULL, 0.000000000000, 166.000000000,-21.30000000,0.00000000000, 400000.000, 300000.000,-20.4,-22.2,9984, 18, 1007,-1},/* Added in GCP */ +{"Lambert 1 Carto - sans grille", NULL, 2.337229166667, 0.000000000, 49.50000000,0.99987734000, 600000.000, 1200000.000, 0.0, 0.0, 1, 2, 1092,-1}, +{"Lambert 2 Carto - sans grille", NULL, 2.337229166667, 0.000000000, 46.80000000,0.99987742000, 600000.000, 2200000.000, 0.0, 0.0, 1, 2, 1093,-1}, +{"Lambert 3 Carto - sans grille", NULL, 2.337229166667, 0.000000000, 44.10000000,0.99987750000, 600000.000, 3200000.000, 0.0, 0.0, 1, 2, 1094,-1}, +{"Lambert 4 Carto - sans grille", NULL, 2.337229166667, 0.000000000, 42.16500000,0.99994471000, 234.358, 185861.369, 0.0, 0.0, 1, 2, 1095,-1}, +{"Suisse", NULL, 0.000000000000, 7.439583333, 46.95240556,1.00000000000, 600000.000, 200000.000, 0.0, 0.0, 2, 25, 1556,-1}, +{"Geoportail France", NULL, 0.000000000000, 0.000000000, 0.00000000,0.68835457569, 0.000, 0.000, 46.5, 0.0,9984, 26, 2012,-1}, +{"Geoportail Antilles", NULL, 0.000000000000, 0.000000000, 0.00000000,0.96592582629, 0.000, 0.000, 15.0, 0.0,9984, 26, 2016,-1}, +{"Geoportail Guyane", NULL, 0.000000000000, 0.000000000, 0.00000000,0.99756405026, 0.000, 0.000, 4.0, 0.0,9984, 26, 2017,-1}, +{"Geoportail Reunion", NULL, 0.000000000000, 0.000000000, 0.00000000,0.93358042649, 0.000, 0.000,-21.0, 0.0,9984, 26, 2018,-1}, +{"Geoportail Mayotte", NULL, 0.000000000000, 0.000000000, 0.00000000,0.97814760073, 0.000, 0.000,-12.0, 0.0,9984, 26, 2019,-1}, +{"Geoportail ST Pierre et Miquelon",NULL, 0.000000000000, 0.000000000, 0.00000000,0.68199836006, 0.000, 0.000, 47.0, 0.0,9984, 26, 2020,-1}, +{"Geoportail Nouvelle Caledonie", NULL, 0.000000000000, 0.000000000, 0.00000000,0.92718385456, 0.000, 0.000,-22.0, 0.0,9984, 26, 2021,-1}, +{"Geoportail Wallis", NULL, 0.000000000000, 0.000000000, 0.00000000,0.97029572627, 0.000, 0.000,-14.0, 0.0,9984, 26, 2022,-1}, +{"Geoportail Polynesie", NULL, 0.000000000000, 0.000000000, 0.00000000,0.96592582628, 0.000, 0.000,-15.0, 0.0,9984, 26, 2023,-1}, +{"Mercator sur sphère WGS84", NULL, 0.000000000000, 0.000000000, 0.00000000,1.00000000000, 0.000, 0.000, 0.0, 0.0,2015, 21, 2027,-1}, +{"(Long/Lat) RGF 93", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, 13, 0, 2028,-1}, +{"(Long/Lat) ITRS-89", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0,9984, 0, 2028,-1}, +{"Geoportail Crozet", NULL, 0.000000000000, 0.000000000, 0.00000000,0.69465837046, 0.000, 0.000,-46.0, 0.0,9984, 26, 2040,-1},/* FIXME : wrong scale factor was 0.69088241108 */ +{"Geoportail Kerguelen", NULL, 0.000000000000, 0.000000000, 0.00000000,0.64944804833, 0.000, 0.000,-49.5, 0.0,9984, 26, 2042,-1},/* FIXME : wrong scale factor was 0.67815966987 */ +{"Lambert CC 42", NULL, 0.000000000000, 3.000000000, 42.00000000,0.00000000000,1700000.000, 1200000.000, 41.2, 42.8,9984, 18, 2501,-1}, +{"Lambert CC 43", NULL, 0.000000000000, 3.000000000, 43.00000000,0.00000000000,1700000.000, 2200000.000, 42.2, 43.8,9984, 18, 2502,-1}, +{"Lambert CC 44", NULL, 0.000000000000, 3.000000000, 44.00000000,0.00000000000,1700000.000, 3200000.000, 43.2, 44.8,9984, 18, 2503,-1}, +{"Lambert CC 45", NULL, 0.000000000000, 3.000000000, 45.00000000,0.00000000000,1700000.000, 4200000.000, 44.2, 45.8,9984, 18, 2504,-1}, +{"Lambert CC 46", NULL, 0.000000000000, 3.000000000, 46.00000000,0.00000000000,1700000.000, 5200000.000, 45.2, 46.8,9984, 18, 2505,-1}, +{"Lambert CC 47", NULL, 0.000000000000, 3.000000000, 47.00000000,0.00000000000,1700000.000, 6200000.000, 46.2, 47.8,9984, 18, 2506,-1}, +{"Lambert CC 48", NULL, 0.000000000000, 3.000000000, 48.00000000,0.00000000000,1700000.000, 7200000.000, 47.2, 48.8,9984, 18, 2507,-1}, +{"Lambert CC 49", NULL, 0.000000000000, 3.000000000, 49.00000000,0.00000000000,1700000.000, 8200000.000, 48.2, 49.8,9984, 18, 2508,-1}, +{"Lambert CC 50", NULL, 0.000000000000, 3.000000000, 50.00000000,0.00000000000,1700000.000, 9200000.000, 49.2, 50.8,9984, 18, 2509,-1}, +{"(Long/Lat) IGN-RGM04GEO", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0,9984, 0,10001,-1}, +{"(Long/Lat) IGN-RGFG95GEO", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0,9984, 0,10002,-1},/* never reached, identical to 10001:-1 */ +{"(Long/Lat) IGN-WGS84RRAFGEO", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0,9984, 0,10003,-1},/* never reached, identical to 10001:-1 */ +{"(Long/Lat) IGN-RGR92GEO", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0,9984, 0,10004,-1},/* never reached, identical to 10001:-1 */ +{"(Long/Lat) IGN-WGS84G", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, 4, 0,10005,-1}, +{"(Long/Lat) CROZ63GEO", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, 4, 0,10006,-1}, +{"(Long/Lat) RGSPM06GEO", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0,9984, 0,10007,-1},/* never reached, identical to 10001:-1 */ +{"(Long/Lat) RGPFGEO", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0,9984, 0,10008,-1},/* never reached, identical to 10001:-1 */ +{"(Long/Lat) RGNCGEO", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0,9984, 0,10009,-1},/* never reached, identical to 10001:-1 */ +{"(Long/Lat) KER62GEO", "d", 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0,9988, 0,10010,-1}, +{"UTM Sud - ED50", NULL, 0.000000000000, 0.000000000, 0.00000000,0.99960000000, 500000.000,10000000.000, 0.0, 0.0, 14, 1,99912, 0},/* FIXME allow retrieving 12:0 - See _findSysCoord_GCSRS() */ +{NULL, NULL, 0.000000000000, 0.000000000, 0.00000000,0.00000000000, 0.000, 0.000, 0.0, 0.0, -1, -1, -1,-1} +}; + +static GCProjectionInfo gk_asProjList[]= +/* + * pszProjName, nSphere, nProjID + */ +{ +{"Geographic shift", 0, 0}, +{"UTM", 0, 1}, +{"Lambert Conform Conic", 0, 2}, +{"Bonne", 0, 3}, +{"Plate carrée", 0, 4}, +{"MGRS (Military UTM)", 0, 11}, +{"Transversal Mercator", 0, 12}, +{"Lambert secant", 0, 18}, +{"Gauss Laborde", 1, 19}, +{"Polyconic", 0, 20}, +{"Direct Mercator", 0, 21}, +{"Stereographic oblic", 1, 22}, +{"Miller", 0, 24}, +{"Mercator oblic", 1, 25}, +{"Equi rectangular", 1, 26}, + +{NULL, 0, -1} +}; + +static GCDatumInfo gk_asDatumList[]= + /* + * pszDatumName, dfShiftX, dfShiftY, dfShiftZ, dfRotX, dfRotY, dfRotZ, dfScaleFactor, dfFA, dfFlattening, nEllipsoidID, nDatumID + */ + /* + * Wrong dx, dy, dz : + * IGN-RGM04GEO, was -217, -216, 67 + * IGN-RGFG95GEO, was -2, -2, 2 + * IGN-RGSPM06GEO, was -125.593, 143.763, -194.558 + * + * #1 and #13 are identical + * #8, #11, #2015 are spherical views of #4 + * #5030, #5031 and #5032 are identical + * FIXME : #5030, #5031, #5032 are ITRS89 compliant, so "compatible" with #4, better use #9999 as ellipsoid + * FIXME : #9999 to #9986 added + */ +{ +{"NTF (Clarke 1880)", -168.0000, -60.0000, 320.0000, 0.00000, 0.00000, 0.00000, 0.0, -112.200,-54.7388e-6, 3, 1}, +{"ED50 France (International 1909)", -84.0000, -97.0000,-117.0000, 0.00000, 0.00000, 0.00000, 0.0, -251.000,-14.1927e-6, 5, 2}, +{"WGS 72", 0.0000, 12.0000, 6.0000, 0.00000, 0.00000, 0.00000, 0.0, 2.000, 0.0312e-6, 6, 3}, +{"WGS_1984", 0.0000, 0.0000, 0.0000, 0.00000, 0.00000, 0.00000, 0.0, 0.000, 0.0, 9999, 4}, +{"ED 79", -83.0000, -95.0000,-116.0000, 0.00000, 0.00000, 0.00000, 0.0, -251.000,-14.1927e-6, 5, 5}, +{"Australian Geodetic 1966", -133.0000, -48.0000, 148.0000, 0.00000, 0.00000, 0.00000, 0.0, -23.000, -0.0081e-6, 7, 6}, +{"Australian Geodetic 1984", -134.0000, -48.0000, 149.0000, 0.00000, 0.00000, 0.00000, 0.0, -23.000, -0.0081e-6, 7, 7}, +{"Sphere", 0.0000, 0.0000, 0.0000, 0.00000, 0.00000, 0.00000, 0.0, 0.000, 0.0, 1, 8}, +{"Sphere DCW", 0.0000, 0.0000, 0.0000, 0.00000, 0.00000, 0.00000, 0.0, 0.000, 0.0, 1, 11}, +{"Airy", 375.0000,-111.0000, 431.0000, 0.00000, 0.00000, 0.00000, 0.0, 573.604, 11.96002325e-6, 8, 12}, +{"NTF-Grille", -168.0000, -60.0000, 320.0000, 0.00000, 0.00000, 0.00000, 0.0, -112.200,-54.7388e-6, 3, 13}, +{"ED50 (International 1909)", -87.0000, -98.0000,-121.0000, 0.00000, 0.00000, 0.00000, 0.0, -251.000,-14.1927e-6, 5, 14}, +{"WGS 84 sur sphere", 0.0000, 0.0000, 0.0000, 0.00000, 0.00000, 0.00000, 0.0, 0.000, 0.0, 1,2015}, +{"IGN-RGM04GEO", 0.0000, 0.0000, 0.0000, 0.00000, 0.00000, 0.00000, 0.0, 0.000, 0.0, 4,5030}, +{"IGN-RGFG95GEO", 0.0000, 0.0000, 0.0000, 0.00000, 0.00000, 0.00000, 0.0, 0.000, 0.0, 4,5031}, +{"IGN-RGSPM06GEO", 0.0000, 0.0000, 0.0000, 0.00000, 0.00000, 0.00000, 0.0, 0.000, 0.0, 4,5032}, +{"IGN-WALL78", 253.0000,-133.0000,-127.0000, 0.00000, 0.00000, 0.00000, 0.0, -251.000,-14.1927e-6, 5,9999},/* FIXME */ +{"IGN-TAHA", 72.4380, 345.9180, 79.4860,-1.60450,-0.88230, -0.55650, 1.3746e-6, -251.000,-14.1927e-6, 5,9998},/* FIXME */ +{"IGN-MOOREA87", 215.9820, 149.5930, 176.2290, 3.26240, 1.69200, 1.15710, 10.47730e-6,-251.000,-14.1927e-6, 5,9997},/* FIXME */ +{"IGN-TAHI51", 162.0000, 117.0000, 154.0000, 0.00000, 0.00000, 0.00000, 0.0, -251.000,-14.1927e-6, 5,9996},/* FIXME */ +{"IGN-NUKU72", 165.7320, 216.7200, 180.5050,-0.64340,-0.45120, -0.07910, 7.42040e-6,-251.000,-14.1927e-6, 5,9995},/* FIXME */ +{"IGN-IGN63", 410.7210, 55.0490, 80.7460,-2.57790,-2.35140, -0.66640, 17.33110e-6,-251.000,-14.1927e-6, 5,9994},/* FIXME */ +{"IGN-MART38", 126.9260, 547.9390, 130.4090,-2.78670, 5.16124, -0.85844, 13.82265e-6,-251.000,-14.1927e-6, 5,9993},/* FIXME */ +{"IGN-GUAD48", -472.2900, -5.6300,-304.1200, 0.43620,-0.83740, 0.25630, 1.89840e-6,-251.000,-14.1927e-6, 5,9992},/* FIXME */ +{"IGN-GUADFM49", 136.5960, 248.1480,-429.7890, 0.00000, 0.00000, 0.00000, 0.0, -251.000,-14.1927e-6, 5,9991},/* FIXME */ +{"IGN-STPM50", -95.5930, 573.7630, 173.4420,-0.96020, 1.25100, -1.39180, 42.62650e-6, -69.400,-37.2957e-6, 2,9990},/* FIXME */ +{"IGN-CSG67", -193.0660, 236.9930, 105.4470, 0.48140,-0.80740, 0.12760, 1.56490e-6,-251.000,-14.1927e-6, 5,9989},/* FIXME */ +{"IGN-KERG62", 145.0000,-187.0000, 103.0000, 0.00000, 0.00000, 0.00000, 0.0, -251.000,-14.1927e-6, 5,9988},/* FIXME */ +{"IGN-REUN47", 789.5240,-626.4860, -89.9040, 0.60060,76.79460,-10.57880,-32.32410e-6,-251.000,-14.1927e-6, 5,9987},/* FIXME */ +{"IGN-MAYO50", -599.9280,-275.5520,-195.6650, 0.08350, 0.47150, -0.06020,-49.28140e-6,-251.000,-14.1927e-6, 5,9986},/* FIXME */ +{"IGN-TAHI79", 221.5250, 152.9480, 176.7680, 2.38470, 1.38960, 0.87700, 11.47410e-6,-251.000,-14.1927e-6, 5,9985},/* FIXME */ +{"ITRS-89", 0.0000, 0.0000, 0.0000, 0.00000, 0.00000, 0.00000, 0.0, 0.000, 0.0, 4,9984}, +{"IGN-CROZ63", 0.0000, 0.0000, 0.0000, 0.00000, 0.00000, 0.00000, 0.0, -251.000,-14.1927e-6, 5,9983},/* FIXME added cause the Bursa-Wolf parameters are not known */ + +{NULL, 0.0000, 0.0000, 0.0000, 0.00000, 0.00000, 0.00000, 0.0, 0.000, 0.0, -1, -1} +}; + +static GCSpheroidInfo gk_asSpheroidList[]= + /* + * pszSpheroidName, dfA, dfE, nEllipsoidID + * + * cause Geoconcept assimilates WGS84 and GRS80, WGS84 is added to the list + */ +{ +{"Sphere", 6378137.0000, 0.00000000000000, 1}, +{"Clarke 1866", 6378206.4000, 0.08227185423947, 2},/* Wrong, semi-major was 6378249.4000 */ +{"Clarke 1880", 6378249.2000, 0.08248325676300, 3},/* Wrong, excentricity was 0.082483256945 */ +{"GRS 80", 6378137.0000, 0.08181919104300, 4},/* Wrong, excentricity was 0.081819191060 */ +{"International 1909", 6378388.0000, 0.08199188997900, 5}, +{"WGS 72", 6378135.0000, 0.08181881201777, 6}, +{"Australian National", 6378160.0000, 0.08182017998700, 7}, +{"Airy", 6377563.3960, 0.08167337387420, 8}, +{"WGS 84", 6378137.0000, 0.08181919084262,9999}, + +{NULL, 0, 0, -1} +}; + +/* -------------------------------------------------------------------- */ +/* GCSRS API Prototypes */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +static int GCSRSAPI_CALL _areCompatibleSpheroids_GCSRS ( int id1, int id2 ) +{ + if( id1==id2 ) return TRUE; + + switch( id1 ) + { + case 4 : + case 9999 : + switch( id2 ) + { + case 4 : + case 9999 : + return TRUE; + default : + break; + } + break; + default : + break; + } + + return FALSE; +}/* _areCompatibleSpheroids_GCSRS */ + +/* -------------------------------------------------------------------- */ +static int GCSRSAPI_CALL _areCompatibleDatums_GCSRS ( int id1, int id2 ) +{ + if( id1==id2 ) return TRUE; + + switch( id1 ) + { + case 1 : /* NTF */ + case 13 : + switch( id2 ) + { + case 1 : + case 13 : + return TRUE; + default : + break; + } + break; + case 2 : /* ED50 */ + case 14 : + case 9983 : + case 9985 : + case 9986 : + case 9987 : + case 9989 : + case 9991 : + case 9992 : + case 9993 : + case 9994 : + case 9995 : + case 9997 : + case 9998 : + case 9999 : + switch( id2 ) + { + case 2 : + case 14 : + case 9983 : + case 9985 : + case 9986 : + case 9987 : + case 9989 : + case 9991 : + case 9992 : + case 9993 : + case 9994 : + case 9995 : + case 9997 : + case 9998 : + case 9999 : + return TRUE; + default : + break; + } + break; + case 4 : /* WGS84 - ITRS89 */ + case 8 : + case 11 : + case 2015 : + case 5030 : + case 5031 : + case 5032 : + case 9984 : + switch( id2 ) + { + case 4 : + case 8 : + case 11 : + case 2015 : + case 5030 : + case 5031 : + case 5032 : + case 9984 : + return TRUE; + default : + break; + } + break; + default : + break; + } + + return FALSE; +}/* _areCompatibleDatums_GCSRS */ + +#define _CPLDebugSpheroid_GCSRS(e) \ +CPLDebug( "GEOCONCEPT", "SemiMajor:%.4f;Excentricity:%.10f;",\ + GetInfoSpheroidSemiMajor_GCSRS(e),\ + GetInfoSpheroidExcentricity_GCSRS(e)\ +); + +/* -------------------------------------------------------------------- */ +static GCSpheroidInfo GCSRSAPI_CALL1(*) _findSpheroid_GCSRS ( double a, double rf ) +{ + int iSpheroid, iResol= 0, nResol= 2; + GCSpheroidInfo* ell; + double e, p[]= {1e-10, 1e-8}; + + /* f = 1 - sqrt(1 - e^2) */ + e= 1.0/rf; + e= sqrt(e*(2.0-e)); +ell_relax: + for( iSpheroid= 0, ell= &(gk_asSpheroidList[0]); + GetInfoSpheroidID_GCSRS(ell)!=-1; + iSpheroid++, ell= &(gk_asSpheroidList[iSpheroid]) ) + { + if( fabs(GetInfoSpheroidSemiMajor_GCSRS(ell) - a) > 1e-4 ) continue; + if( fabs(GetInfoSpheroidExcentricity_GCSRS(ell) - e) > p[iResol] ) continue; + break; + } + if( GetInfoSpheroidID_GCSRS(ell)==-1 && iResol!=nResol-1 ) + { + iResol++; + goto ell_relax; + } + + return ell; +}/* _findSpheroid_GCSRS */ + +#define _CPLDebugDatum_GCSRS(d) \ +CPLDebug( "GEOCONCEPT", "ID:%d;ShiftX:%.4f;ShiftY:%.4f;ShiftZ:%.4f;DiffA:%.4f;DiffFlattening:%.7f;",\ + GetInfoDatumID_GCSRS((d)),\ + GetInfoDatumShiftX_GCSRS((d)),\ + GetInfoDatumShiftY_GCSRS((d)),\ + GetInfoDatumShiftZ_GCSRS((d)),\ + GetInfoDatumDiffA_GCSRS((d)),\ + GetInfoDatumDiffFlattening_GCSRS((d))\ +); + +/* -------------------------------------------------------------------- */ +static GCDatumInfo GCSRSAPI_CALL1(*) _findDatum_GCSRS ( double dx, + double dy, + double dz, + double a, + double f ) +{ + int iDatum, bRelax= FALSE; + GCDatumInfo* datum; + +datum_relax: + for( iDatum= 0, datum= &(gk_asDatumList[0]); + GetInfoDatumID_GCSRS(datum)!=-1; + iDatum++, datum= &(gk_asDatumList[iDatum]) ) + { + if( !bRelax ) + { + if( fabs(GetInfoDatumShiftX_GCSRS(datum) - dx) > 1e-4 ) continue; + if( fabs(GetInfoDatumShiftY_GCSRS(datum) - dy) > 1e-4 ) continue; + if( fabs(GetInfoDatumShiftZ_GCSRS(datum) - dz) > 1e-4 ) continue; + } + if( fabs(GetInfoDatumDiffA_GCSRS(datum) - (6378137.0000-a)) > 1e-4 ) continue; + if( fabs(GetInfoDatumDiffFlattening_GCSRS(datum) - (0.003352779565406696648-f)) > 1e-7 ) continue; + break; + } + if( GetInfoDatumID_GCSRS(datum)==-1 && !bRelax ) + { + /* + * FIXME : when both nadgrids and towgs84 are defined, bursa-wolf parameters are lost ! + * if the projection and the ellipsoid are known, one can retrieve the datum + * Try relaxed search ... + */ + bRelax= TRUE; + goto datum_relax; + } + + return datum; +}/* _findDatum_GCSRS */ + +/* -------------------------------------------------------------------- */ +static GCProjectionInfo GCSRSAPI_CALL1(*) _findProjection_GCSRS ( const char* p, double lat_ts ) +{ + int iProj; + GCProjectionInfo* proj; + + for( iProj= 0, proj= &(gk_asProjList[0]); + GetInfoProjID_GCSRS(proj)!=-1; + iProj++, proj= &(gk_asProjList[iProj]) ) + { + if( iProj==0 && p==NULL) + break; + if( iProj==1 && + ( EQUAL(p,SRS_PT_TRANSVERSE_MERCATOR) || + EQUAL(p,SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED) ) ) + break; + if( iProj==2 && + EQUAL(p,SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) ) + break; + if( iProj==3 && + EQUAL(p,SRS_PT_BONNE) ) + break; + if( iProj==4 && + EQUAL(p,SRS_PT_EQUIRECTANGULAR) && + lat_ts==0.0 ) + break; + /* FIXME : iProj==6 ? */ + if( iProj==7 && + ( EQUAL(p,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) || + EQUAL(p,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM) ) ) + break; + if( iProj==8 && + EQUAL(p,SRS_PT_GAUSSSCHREIBERTMERCATOR) ) + break; + if( iProj==9 && + EQUAL(p,SRS_PT_POLYCONIC) ) + break; + /* FIXME + if( iProj==10 && + ( EQUAL(p,SRS_PT_MERCATOR_1SP) || + EQUAL(p,SRS_PT_MERCATOR_2SP) ) ) + break; + */ + if( iProj==11 && + ( EQUAL(p,SRS_PT_OBLIQUE_STEREOGRAPHIC) || + EQUAL(p,SRS_PT_POLAR_STEREOGRAPHIC) ) ) + break; + if( iProj==12 && + EQUAL(p,SRS_PT_MILLER_CYLINDRICAL) ) + break; + /* FIXME + if( iProj==13 && + ( EQUAL(p,SRS_PT_HOTINE_OBLIQUE_MERCATOR) || + EQUAL(p,SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN) || + EQUAL(p,SRS_PT_LABORDE_OBLIQUE_MERCATOR) ) ) + break; + */ + if( iProj==14 && + EQUAL(p,SRS_PT_EQUIRECTANGULAR) && + lat_ts!=0.0 ) + break; + } + + return proj; +}/* _findProjection_GCSRS */ + +#define _CPLDebugSysCoord_GCSRS(m,s) \ +CPLDebug( "GEOCONCEPT", "[%s]ID=%d;Zone=%d;DatumID=%d;ProjID=%d;PrimeMeridian=%.10f;CentralMeridian=%.10f;LatitudeOfOrigin=%.10f;StandardParallel1=%.10f;StandardParallel2=%.10f;ScaleFactor=%.10f;FalseEasting=%.10f;FalseNorthing=%.10f;",\ + (m)? (m):"",\ + GetSysCoordSystemID_GCSRS((s)),\ + GetSysCoordTimeZone_GCSRS((s)),\ + GetSysCoordDatumID_GCSRS((s)),\ + GetSysCoordProjID_GCSRS((s)),\ + GetSysCoordPrimeMeridian_GCSRS((s)),\ + GetSysCoordCentralMeridian_GCSRS((s)),\ + GetSysCoordLatitudeOfOrigin_GCSRS((s)),\ + GetSysCoordStandardParallel1_GCSRS((s)),\ + GetSysCoordStandardParallel2_GCSRS((s)),\ + GetSysCoordScaleFactor_GCSRS((s)),\ + GetSysCoordFalseEasting_GCSRS((s)),\ + GetSysCoordFalseNorthing_GCSRS((s))\ +); + +/* -------------------------------------------------------------------- */ +static GCSysCoord GCSRSAPI_CALL1(*) _findSysCoord_GCSRS ( GCSysCoord* theSysCoord ) +{ + int iSysCoord, bestSysCoord= -1; + GCSysCoord* gcsc; + + if( !theSysCoord) return NULL; + + SetSysCoordSystemID_GCSRS(theSysCoord, -1); + SetSysCoordTimeZone_GCSRS(theSysCoord, -1); + _CPLDebugSysCoord_GCSRS(NULL,theSysCoord); + for( iSysCoord= 0, gcsc= &(gk_asSysCoordList[0]); + GetSysCoordSystemID_GCSRS(gcsc)!=-1; + iSysCoord++, gcsc= &(gk_asSysCoordList[iSysCoord]) ) + { + if( !_areCompatibleDatums_GCSRS(GetSysCoordDatumID_GCSRS(gcsc), GetSysCoordDatumID_GCSRS(theSysCoord)) ) continue; + + if( GetSysCoordProjID_GCSRS(gcsc) != GetSysCoordProjID_GCSRS(theSysCoord) ) continue; + + if( fabs(GetSysCoordPrimeMeridian_GCSRS(gcsc) - GetSysCoordPrimeMeridian_GCSRS(theSysCoord) ) > 1e-8 ) continue; + + if( fabs(GetSysCoordCentralMeridian_GCSRS(gcsc) - GetSysCoordCentralMeridian_GCSRS(theSysCoord) ) > 1e-8 ) + { + switch( GetSysCoordProjID_GCSRS(gcsc) ) + { + case 1 :/* UTM familly : central meridian is the 6* zone - 183 (in degrees) */ + if( GetSysCoordCentralMeridian_GCSRS(gcsc)==0.0 ) /* generic UTM definition */ + { + break; + } + default : + continue; + } + } + if( fabs(GetSysCoordLatitudeOfOrigin_GCSRS(gcsc) - GetSysCoordLatitudeOfOrigin_GCSRS(theSysCoord) ) > 1e-8 ) continue; + + if( fabs(GetSysCoordStandardParallel1_GCSRS(gcsc) - GetSysCoordStandardParallel1_GCSRS(theSysCoord) ) > 1e-8 ) continue; + if( fabs(GetSysCoordStandardParallel2_GCSRS(gcsc) - GetSysCoordStandardParallel2_GCSRS(theSysCoord) ) > 1e-8 ) continue; + + if( fabs(GetSysCoordScaleFactor_GCSRS(gcsc) - GetSysCoordScaleFactor_GCSRS(theSysCoord) ) > 1e-8 ) continue; + + if( fabs(GetSysCoordFalseEasting_GCSRS(gcsc) - GetSysCoordFalseEasting_GCSRS(theSysCoord) ) > 1e-4 ) continue; + if( fabs(GetSysCoordFalseNorthing_GCSRS(gcsc) - GetSysCoordFalseNorthing_GCSRS(theSysCoord) ) > 1e-4 ) continue; + + /* Found a candidate : */ + if( bestSysCoord==-1) + { + bestSysCoord= iSysCoord; + } + else + { + switch( GetSysCoordProjID_GCSRS(gcsc) ) + { + case 0:/* long/lat */ + if( GetSysCoordDatumID_GCSRS(gcsc)==GetSysCoordDatumID_GCSRS(theSysCoord) && + GetSysCoordDatumID_GCSRS(&(gk_asSysCoordList[bestSysCoord]))!=GetSysCoordDatumID_GCSRS(theSysCoord)) /* exact match */ + { + bestSysCoord= iSysCoord; + } + break; + case 1:/* UTM familly : central meridian is the 6* zone - 183 (in degrees) */ + if( GetSysCoordCentralMeridian_GCSRS(gcsc)!=0.0 && + GetSysCoordDatumID_GCSRS(gcsc)==GetSysCoordDatumID_GCSRS(theSysCoord) && + GetSysCoordDatumID_GCSRS(&(gk_asSysCoordList[bestSysCoord]))!=GetSysCoordDatumID_GCSRS(theSysCoord)) /* exact match */ + { + bestSysCoord= iSysCoord; + } + break; + default : + break; + } + } + } + /* Seems to be the right Geoconcept system: */ + if( bestSysCoord>=0 ) + { + gcsc= &(gk_asSysCoordList[bestSysCoord]); + switch( GetSysCoordSystemID_GCSRS(gcsc) ) + { + case 99912 : /* hack */ + SetSysCoordSystemID_GCSRS(theSysCoord, 12); + break; + default : + SetSysCoordSystemID_GCSRS(theSysCoord, GetSysCoordSystemID_GCSRS(gcsc)); + break; + } + SetSysCoordTimeZone_GCSRS(theSysCoord, GetSysCoordTimeZone_GCSRS(gcsc)); + if( GetSysCoordName_GCSRS(gcsc) ) + SetSysCoordName_GCSRS(theSysCoord, CPLStrdup(GetSysCoordName_GCSRS(gcsc))); + if( GetSysCoordUnit_GCSRS(gcsc) ) + SetSysCoordUnit_GCSRS(theSysCoord, CPLStrdup(GetSysCoordUnit_GCSRS(gcsc))); + } + + return theSysCoord; +}/* _findSysCoord_GCSRS */ + +/* -------------------------------------------------------------------- */ +static void GCSRSAPI_CALL _InitSysCoord_GCSRS ( + GCSysCoord* theSysCoord + ) +{ + SetSysCoordSystemID_GCSRS(theSysCoord, -1); + SetSysCoordTimeZone_GCSRS(theSysCoord, -1); + SetSysCoordName_GCSRS(theSysCoord, NULL); + SetSysCoordUnit_GCSRS(theSysCoord, NULL); + SetSysCoordCentralMeridian_GCSRS(theSysCoord, 0.0); + SetSysCoordLatitudeOfOrigin_GCSRS(theSysCoord, 0.0); + SetSysCoordStandardParallel1_GCSRS(theSysCoord, 0.0); + SetSysCoordStandardParallel2_GCSRS(theSysCoord, 0.0); + SetSysCoordScaleFactor_GCSRS(theSysCoord, 0.0); + SetSysCoordFalseEasting_GCSRS(theSysCoord, 0.0); + SetSysCoordFalseNorthing_GCSRS(theSysCoord, 0.0); + SetSysCoordDatumID_GCSRS(theSysCoord, -1); + SetSysCoordProjID_GCSRS(theSysCoord, -1); + SetSysCoordPrimeMeridian_GCSRS(theSysCoord, 0); +}/* _InitSysCoord_GCSRS */ + +/* -------------------------------------------------------------------- */ +GCSysCoord GCSRSAPI_CALL1(*) CreateSysCoord_GCSRS ( + int srsid, + int timezone + ) +{ + int iSysCoord; + GCSysCoord* theSysCoord, *gcsc; + + if( !(theSysCoord= CPLMalloc(sizeof(GCSysCoord))) ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "failed to create a Geoconcept coordinate system.\n" + ); + return NULL; + } + _InitSysCoord_GCSRS(theSysCoord); + if( srsid>=0) + { + for( iSysCoord= 0, gcsc= &(gk_asSysCoordList[0]); + GetSysCoordSystemID_GCSRS(gcsc)!=-1; + iSysCoord++, gcsc= &(gk_asSysCoordList[iSysCoord]) ) + { + if( srsid==GetSysCoordSystemID_GCSRS(gcsc) ) + { + SetSysCoordSystemID_GCSRS(theSysCoord, srsid); + SetSysCoordTimeZone_GCSRS(theSysCoord, timezone); + if( GetSysCoordName_GCSRS(gcsc) ) + SetSysCoordName_GCSRS(theSysCoord, CPLStrdup(GetSysCoordName_GCSRS(gcsc))); + if( GetSysCoordUnit_GCSRS(gcsc) ) + SetSysCoordUnit_GCSRS(theSysCoord, CPLStrdup(GetSysCoordUnit_GCSRS(gcsc))); + SetSysCoordCentralMeridian_GCSRS(theSysCoord, GetSysCoordCentralMeridian_GCSRS(gcsc)); + SetSysCoordLatitudeOfOrigin_GCSRS(theSysCoord, GetSysCoordLatitudeOfOrigin_GCSRS(gcsc)); + SetSysCoordStandardParallel1_GCSRS(theSysCoord, GetSysCoordStandardParallel1_GCSRS(gcsc)); + SetSysCoordStandardParallel2_GCSRS(theSysCoord, GetSysCoordStandardParallel2_GCSRS(gcsc)); + SetSysCoordScaleFactor_GCSRS(theSysCoord, GetSysCoordScaleFactor_GCSRS(gcsc)); + SetSysCoordFalseEasting_GCSRS(theSysCoord, GetSysCoordFalseEasting_GCSRS(gcsc)); + SetSysCoordFalseNorthing_GCSRS(theSysCoord, GetSysCoordFalseNorthing_GCSRS(gcsc)); + SetSysCoordDatumID_GCSRS(theSysCoord, GetSysCoordDatumID_GCSRS(gcsc)); + SetSysCoordProjID_GCSRS(theSysCoord, GetSysCoordProjID_GCSRS(gcsc)); + break; + } + } + } + + return theSysCoord; +}/* CreateSysCoord_GCSRS */ + +/* -------------------------------------------------------------------- */ +static void GCSRSAPI_CALL _ReInitSysCoord_GCSRS ( + GCSysCoord* theSysCoord + ) +{ + if( GetSysCoordName_GCSRS(theSysCoord) ) + { + CPLFree(GetSysCoordName_GCSRS(theSysCoord)); + } + if( GetSysCoordUnit_GCSRS(theSysCoord) ) + { + CPLFree(GetSysCoordUnit_GCSRS(theSysCoord)); + } + _InitSysCoord_GCSRS(theSysCoord); +}/* _ReInitSysCoord_GCSRS */ + +/* -------------------------------------------------------------------- */ +void GCSRSAPI_CALL DestroySysCoord_GCSRS ( + GCSysCoord** theSysCoord + ) +{ + _ReInitSysCoord_GCSRS(*theSysCoord); + CPLFree(*theSysCoord); + *theSysCoord= NULL; +}/* DestroySysCoord_GCSRS */ + +/* -------------------------------------------------------------------- */ +GCSysCoord GCSRSAPI_CALL1(*) OGRSpatialReference2SysCoord_GCSRS ( OGRSpatialReferenceH poSR ) +{ + char* pszProj4= NULL; + GCSpheroidInfo* ell= NULL; + GCDatumInfo* datum= NULL; + GCProjectionInfo* gcproj= NULL; + double a, rf, f, p[7]; + GCSysCoord* syscoord= NULL; + + if( !poSR ) return NULL; + + pszProj4= NULL; + OSRExportToProj4(poSR, &pszProj4); + if( !pszProj4 ) pszProj4= CPLStrdup(""); + + CPLDebug("GEOCONCEPT", "SRS : %s", pszProj4); + + if( !(syscoord= CreateSysCoord_GCSRS(-1,-1)) ) + { + goto onError; + } + SetSysCoordPrimeMeridian_GCSRS(syscoord, OSRGetPrimeMeridian(poSR,NULL)); + + a= OSRGetSemiMajor(poSR,NULL); + rf= OSRGetInvFlattening(poSR,NULL); + ell= _findSpheroid_GCSRS(a, rf); + if( GetInfoSpheroidID_GCSRS(ell)==-1 ) + { + CPLDebug("GEOCONCEPT", "Unsupported ellipsoid : %.4f %.10f", a, rf); + goto onError; + } + CPLDebug("GEOCONCEPT", "ellipsoid found : %s", + GetInfoSpheroidName_GCSRS(ell)); + + OSRGetTOWGS84(poSR,p,7); + f= 1.0 - sqrt(1.0 - GetInfoSpheroidExcentricity_GCSRS(ell)*GetInfoSpheroidExcentricity_GCSRS(ell)); + datum= _findDatum_GCSRS(p[0], p[1], p[2], GetInfoSpheroidSemiMajor_GCSRS(ell), f); + if( GetInfoDatumID_GCSRS(datum)==-1 ) + { + CPLDebug("GEOCONCEPT", "Unsupported datum : %.4f %.4f; %.4f %.4f %.10f", + p[0], p[1], p[2], a, 1.0/rf); + goto onError; + } + /* FIXME : WGS 84 and GRS 80 assimilation by Geoconcept : */ + if( GetInfoSpheroidID_GCSRS(ell)==4 ) /* GRS 80 */ + { + datum= &(gk_asDatumList[31]); + } + else if( GetInfoSpheroidID_GCSRS(ell)==9999 ) /* WGS 84 */ + { + datum= &(gk_asDatumList[3]); + } + CPLDebug("GEOCONCEPT", "datum found : %s", GetInfoDatumName_GCSRS(datum)); + SetSysCoordDatumID_GCSRS(syscoord, GetInfoDatumID_GCSRS(datum)); + + gcproj= _findProjection_GCSRS(OSRIsGeographic(poSR)? + NULL + : + OSRGetAttrValue(poSR, "PROJECTION", 0), + OSRGetProjParm(poSR,SRS_PP_PSEUDO_STD_PARALLEL_1,0.0,NULL)); + if( GetInfoProjID_GCSRS(gcproj)==-1 ) + { + CPLDebug("GEOCONCEPT", "Unsupported projection : %s", + OSRIsGeographic(poSR)? "GEOCS":OSRGetAttrValue(poSR, "PROJECTION", 0)); + goto onError; + } + CPLDebug("GEOCONCEPT", "projection : %s", GetInfoProjName_GCSRS(gcproj)); + SetSysCoordProjID_GCSRS(syscoord, GetInfoProjID_GCSRS(gcproj)); + + /* then overwrite them with projection specific parameters ... */ + if( OSRIsProjected(poSR) ) + { + double v; + + SetSysCoordPrimeMeridian_GCSRS(syscoord, OSRGetPrimeMeridian(poSR,NULL)); + SetSysCoordCentralMeridian_GCSRS(syscoord, OSRGetProjParm(poSR,SRS_PP_CENTRAL_MERIDIAN,0.0,NULL)); + SetSysCoordLatitudeOfOrigin_GCSRS(syscoord, OSRGetProjParm(poSR,SRS_PP_LATITUDE_OF_ORIGIN,0.0,NULL)); + SetSysCoordStandardParallel1_GCSRS(syscoord, OSRGetProjParm(poSR,SRS_PP_STANDARD_PARALLEL_1,0.0,NULL)); + SetSysCoordStandardParallel2_GCSRS(syscoord, OSRGetProjParm(poSR,SRS_PP_STANDARD_PARALLEL_2,0.0,NULL)); + SetSysCoordFalseEasting_GCSRS(syscoord, OSRGetProjParm(poSR,SRS_PP_FALSE_EASTING,0.0,NULL)); + SetSysCoordFalseNorthing_GCSRS(syscoord, OSRGetProjParm(poSR,SRS_PP_FALSE_NORTHING,0.0,NULL)); + if( (v= OSRGetProjParm(poSR,SRS_PP_SCALE_FACTOR,0.0,NULL))!= 0.0 ) + { + SetSysCoordScaleFactor_GCSRS(syscoord, v); + } + if( (v= OSRGetProjParm(poSR,SRS_PP_PSEUDO_STD_PARALLEL_1,0.0,NULL))!= 0.0 ) + { + /* should be SRS_PT_EQUIRECTANGULAR : */ + SetSysCoordScaleFactor_GCSRS(syscoord, cos(v*PI/180.0)); + SetSysCoordStandardParallel1_GCSRS(syscoord, v);/* allow keeping lat_ts sign */ + } + } + + /* Retrieve the syscoord : */ + if( !_findSysCoord_GCSRS(syscoord) ) + { + CPLDebug("GEOCONCEPT", "invalid syscoord ?!"); + goto onError; + } + if( GetSysCoordSystemID_GCSRS(syscoord)==-1 ) + { + CPLDebug("GEOCONCEPT", "Cannot find syscoord"); + goto onError; + } + /* when SRS_PT_TRANSVERSE_MERCATOR, get zone : */ + if( GetSysCoordTimeZone_GCSRS(syscoord)==0 ) + { + int pbNorth= 1; + SetSysCoordTimeZone_GCSRS(syscoord, OSRGetUTMZone(poSR,&pbNorth)); + } + + if( pszProj4 ) + { + CPLFree(pszProj4); + } + CPLDebug( "GEOCONCEPT", "SysCoord value: %d:%d", + GetSysCoordSystemID_GCSRS(syscoord), + GetSysCoordTimeZone_GCSRS(syscoord) ); + + return syscoord; + +onError: + if( pszProj4 ) + { + CPLDebug( "GEOCONCEPT", + "Unhandled spatial reference system '%s'.", + pszProj4); + CPLFree(pszProj4); + } + if( syscoord ) + { + DestroySysCoord_GCSRS(&syscoord); + } + return NULL; +}/* OGRSpatialReference2SysCoord_GCSRS */ + +/* -------------------------------------------------------------------- */ +OGRSpatialReferenceH GCSRSAPI_CALL SysCoord2OGRSpatialReference_GCSRS ( GCSysCoord* syscoord ) +{ + OGRSpatialReferenceH poSR; + GCDatumInfo* datum= NULL; + GCSpheroidInfo* ell= NULL; + int i; + double f; + + poSR= OSRNewSpatialReference(NULL); + + if( syscoord && GetSysCoordSystemID_GCSRS(syscoord)!=-1 ) + { + switch( GetSysCoordProjID_GCSRS(syscoord) ) + { + case 0 : /* long/lat */ + break; + case 1: /* UTM */ + case 11: /* MGRS */ + case 12: /* TM */ + OSRSetTM(poSR, GetSysCoordLatitudeOfOrigin_GCSRS(syscoord), + GetSysCoordCentralMeridian_GCSRS(syscoord), + GetSysCoordScaleFactor_GCSRS(syscoord), + GetSysCoordFalseEasting_GCSRS(syscoord), + GetSysCoordFalseNorthing_GCSRS(syscoord)); + break; + case 2: /* LCC 1SP */ + OSRSetLCC1SP(poSR, GetSysCoordLatitudeOfOrigin_GCSRS(syscoord), + GetSysCoordCentralMeridian_GCSRS(syscoord), + GetSysCoordScaleFactor_GCSRS(syscoord), + GetSysCoordFalseEasting_GCSRS(syscoord), + GetSysCoordFalseNorthing_GCSRS(syscoord)); + break; + case 3: /* Bonne */ + OSRSetBonne(poSR, GetSysCoordLatitudeOfOrigin_GCSRS(syscoord), + GetSysCoordCentralMeridian_GCSRS(syscoord), + GetSysCoordFalseEasting_GCSRS(syscoord), + GetSysCoordFalseNorthing_GCSRS(syscoord)); + break; + case 4: /* Plate Caree */ + OSRSetEquirectangular(poSR, GetSysCoordLatitudeOfOrigin_GCSRS(syscoord), + GetSysCoordCentralMeridian_GCSRS(syscoord), + GetSysCoordFalseEasting_GCSRS(syscoord), + GetSysCoordFalseNorthing_GCSRS(syscoord)); + break; + case 18: /* LCC 2SP */ + OSRSetLCC(poSR, GetSysCoordStandardParallel1_GCSRS(syscoord), + GetSysCoordStandardParallel2_GCSRS(syscoord), + GetSysCoordLatitudeOfOrigin_GCSRS(syscoord), + GetSysCoordCentralMeridian_GCSRS(syscoord), + GetSysCoordFalseEasting_GCSRS(syscoord), + GetSysCoordFalseNorthing_GCSRS(syscoord)); + break; + case 19: /* Gauss Schreiber : Reunion */ + OSRSetGaussSchreiberTMercator(poSR, GetSysCoordLatitudeOfOrigin_GCSRS(syscoord), + GetSysCoordCentralMeridian_GCSRS(syscoord), + GetSysCoordScaleFactor_GCSRS(syscoord), + GetSysCoordFalseEasting_GCSRS(syscoord), + GetSysCoordFalseNorthing_GCSRS(syscoord)); + break; + case 20: /* Polyconic */ + OSRSetPolyconic(poSR, GetSysCoordLatitudeOfOrigin_GCSRS(syscoord), + GetSysCoordCentralMeridian_GCSRS(syscoord), + GetSysCoordFalseEasting_GCSRS(syscoord), + GetSysCoordFalseNorthing_GCSRS(syscoord)); + break; + case 21: /* Direct Mercator */ + OSRSetMercator(poSR, GetSysCoordLatitudeOfOrigin_GCSRS(syscoord), + GetSysCoordCentralMeridian_GCSRS(syscoord), + GetSysCoordScaleFactor_GCSRS(syscoord), + GetSysCoordFalseEasting_GCSRS(syscoord), + GetSysCoordFalseNorthing_GCSRS(syscoord)); + break; + case 22: /* Stereographic oblic */ + OSRSetOS(poSR, GetSysCoordLatitudeOfOrigin_GCSRS(syscoord), + GetSysCoordCentralMeridian_GCSRS(syscoord), + GetSysCoordScaleFactor_GCSRS(syscoord), + GetSysCoordFalseEasting_GCSRS(syscoord), + GetSysCoordFalseNorthing_GCSRS(syscoord)); + break; + case 24: /* Miller */ + OSRSetMC(poSR, GetSysCoordLatitudeOfOrigin_GCSRS(syscoord), + GetSysCoordCentralMeridian_GCSRS(syscoord), + GetSysCoordFalseEasting_GCSRS(syscoord), + GetSysCoordFalseNorthing_GCSRS(syscoord)); + break; + case 26: /* Equi rectangular */ + OSRSetEquirectangular2(poSR, GetSysCoordLatitudeOfOrigin_GCSRS(syscoord), + GetSysCoordCentralMeridian_GCSRS(syscoord), + GetSysCoordStandardParallel1_GCSRS(syscoord), + GetSysCoordFalseEasting_GCSRS(syscoord), + GetSysCoordFalseNorthing_GCSRS(syscoord)); + break; + default : + break; + } + if( GetSysCoordProjID_GCSRS(syscoord)>0 ) + OSRSetProjCS(poSR, GetSysCoordName_GCSRS(syscoord)); + + for( i= 0, datum= &(gk_asDatumList[0]); + GetInfoDatumID_GCSRS(datum)!=-1; + i++, datum= &(gk_asDatumList[i]) ) + { + if( GetInfoDatumID_GCSRS(datum)==GetSysCoordDatumID_GCSRS(syscoord) ) break; + } + for( i= 0, ell= &(gk_asSpheroidList[0]); + GetInfoSpheroidID_GCSRS(ell)!=-1; + i++, ell= &(gk_asSpheroidList[i]) ) + { + if( _areCompatibleSpheroids_GCSRS(GetInfoSpheroidID_GCSRS(ell), + GetInfoDatumSpheroidID_GCSRS(datum)) ) break; + } + /* FIXME : WGS 84 and GRS 80 assimilation by Geoconcept : */ + if( GetInfoDatumID_GCSRS(datum)==4 ) /* WGS 84 */ + { + ell= &(gk_asSpheroidList[8]); + } + else if( GetInfoDatumID_GCSRS(datum)==9984 ) /* GRS 80 */ + { + ell= &(gk_asSpheroidList[3]); + } + f= 1.0 - sqrt(1.0 - GetInfoSpheroidExcentricity_GCSRS(ell)*GetInfoSpheroidExcentricity_GCSRS(ell)); + OSRSetGeogCS(poSR, GetSysCoordProjID_GCSRS(syscoord)!=0 || !GetSysCoordName_GCSRS(syscoord)? + "unnamed":GetSysCoordName_GCSRS(syscoord), + GetInfoDatumID_GCSRS(datum)>=0? GetInfoDatumName_GCSRS(datum):"unknown", + GetInfoSpheroidID_GCSRS(ell)>=0? GetInfoSpheroidName_GCSRS(ell):"unknown", + GetInfoSpheroidID_GCSRS(ell)>=0? GetInfoSpheroidSemiMajor_GCSRS(ell):6378137.0, + GetInfoSpheroidID_GCSRS(ell)>=0? (f==0? 0:1/f):298.257223563, + "Greenwich", + GetSysCoordPrimeMeridian_GCSRS(syscoord), + SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV)); + /* As Geoconcept uses Molodensky, we've got only 3 out of 7 params for Bursa-Wolf : */ + /* the 4 missing Bursa-Wolf parameters have been added to the gk_asDatumList ! */ + if( GetInfoProjID_GCSRS(syscoord)>0 && GetInfoDatumID_GCSRS(datum)!=-1 ) + { + OSRSetTOWGS84(poSR, GetInfoDatumShiftX_GCSRS(datum), + GetInfoDatumShiftY_GCSRS(datum), + GetInfoDatumShiftZ_GCSRS(datum), + GetInfoDatumRotationX_GCSRS(datum), + GetInfoDatumRotationY_GCSRS(datum), + GetInfoDatumRotationZ_GCSRS(datum), + 1e6*GetInfoDatumScaleFactor_GCSRS(datum)); + } + } + +/* -------------------------------------------------------------------- */ +/* Report on translation. */ +/* -------------------------------------------------------------------- */ + { + char* pszWKT; + + OSRExportToWkt(poSR,&pszWKT); + if( pszWKT!=NULL ) + { + CPLDebug( "GEOCONCEPT", + "This SysCoord value: %d:%d was translated to : %s", + GetSysCoordSystemID_GCSRS(syscoord), + GetSysCoordTimeZone_GCSRS(syscoord), + pszWKT ); + CPLFree( pszWKT ); + } + } + + return poSR; +}/* SysCoord2OGRSpatialReference_GCSRS */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/geoconcept_syscoord.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/geoconcept_syscoord.h new file mode 100644 index 000000000..a4600c57c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/geoconcept_syscoord.h @@ -0,0 +1,188 @@ +/********************************************************************** + * $Id: geoconcept_syscoord.h + * + * Name: geoconcept_syscoord.h + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements translation between Geoconcept SysCoord + * and OGRSpatialRef format + * Language: C + * + ********************************************************************** + * Copyright (c) 2007, Geoconcept and IGN + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + **********************************************************************/ +#ifndef _GEOCONCEPT_SYSCOORD_H_INCLUDED +#define _GEOCONCEPT_SYSCOORD_H_INCLUDED + +#include "ogr_srs_api.h" + +#ifdef GCSRS_DLLEXPORT +# define GCSRSAPI_CALL __declspec(dllexport) +# define GCSRSAPI_CALL1(x) __declspec(dllexport) x +#endif + +#ifndef GCSRSAPI_CALL +# define GCSRSAPI_CALL +#endif + +#ifndef GCSRSAPI_CALL1 +# define GCSRSAPI_CALL1(x) x GCSRSAPI_CALL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* -------------------------------------------------------------------- */ +/* GCSRS API Types */ +/* -------------------------------------------------------------------- */ +typedef struct _tSpheroidInfo_GCSRS GCSpheroidInfo; +typedef struct _tDatumInfo_GCSRS GCDatumInfo; +typedef struct _tProjectionInfo_GCSRS GCProjectionInfo; +typedef struct _tSysCoord_GCSRS GCSysCoord; + +struct _tSpheroidInfo_GCSRS { + const char *pszSpheroidName; + double dfA; /* semi major axis in meters */ + double dfE; /* excentricity */ + int nEllipsoidID; +}; + +struct _tDatumInfo_GCSRS { + const char *pszDatumName; + double dfShiftX; + double dfShiftY; + double dfShiftZ; + double dfRotX; + double dfRotY; + double dfRotZ; + double dfScaleFactor; + double dfDiffA; /* + * semi-major difference to-datum minus from-datum : + * http://home.hiwaay.net/~taylorc/bookshelf/math-science/geodesy/datum/transform/molodensky/ + */ + double dfDiffFlattening; /* + * Change in flattening : "to" minus "from" + */ + int nEllipsoidID; + int nDatumID; +}; + +struct _tProjectionInfo_GCSRS { + const char *pszProjName; + int nSphere;/* + * 1 = sphere de courbure + * 2 = sphere equatoriale + * 3 = sphere bitangeante + * 4 = sphere polaire nord + * 5 = sphere polaire sud + * 6 = Hotine + */ + int nProjID; +}; + +struct _tSysCoord_GCSRS { + char *pszSysCoordName; + char *pszUnit; + + double dfPM; + /* inherited : */ + double dfLambda0; + double dfPhi0; + double dfk0; + double dfX0; + double dfY0; + double dfPhi1; + double dfPhi2; + + int nDatumID; + int nProjID; + int coordSystemID; + int timeZoneValue;/* when 0, replace by zone */ +}; + +/* -------------------------------------------------------------------- */ +/* GCSRS API Prototypes */ +/* -------------------------------------------------------------------- */ + +#define GetInfoSpheroidID_GCSRS(theSpheroid) (theSpheroid)->nEllipsoidID +#define GetInfoSpheroidName_GCSRS(theSpheroid) (theSpheroid)->pszSpheroidName +#define GetInfoSpheroidSemiMajor_GCSRS(theSpheroid) (theSpheroid)->dfA +#define GetInfoSpheroidExcentricity_GCSRS(theSpheroid) (theSpheroid)->dfE + +#define GetInfoDatumID_GCSRS(theDatum) (theDatum)->nDatumID +#define GetInfoDatumName_GCSRS(theDatum) (theDatum)->pszDatumName +#define GetInfoDatumShiftX_GCSRS(theDatum) (theDatum)->dfShiftX +#define GetInfoDatumShiftY_GCSRS(theDatum) (theDatum)->dfShiftY +#define GetInfoDatumShiftZ_GCSRS(theDatum) (theDatum)->dfShiftZ +#define GetInfoDatumDiffA_GCSRS(theDatum) (theDatum)->dfDiffA +#define GetInfoDatumRotationX_GCSRS(theDatum) (theDatum)->dfRotX +#define GetInfoDatumRotationY_GCSRS(theDatum) (theDatum)->dfRotY +#define GetInfoDatumRotationZ_GCSRS(theDatum) (theDatum)->dfRotZ +#define GetInfoDatumScaleFactor_GCSRS(theDatum) (theDatum)->dfScaleFactor +#define GetInfoDatumDiffFlattening_GCSRS(theDatum) (theDatum)->dfDiffFlattening +#define GetInfoDatumSpheroidID_GCSRS(theDatum) (theDatum)->nEllipsoidID + +#define GetInfoProjID_GCSRS(theProj) (theProj)->nProjID +#define GetInfoProjName_GCSRS(theProj) (theProj)->pszProjName +#define GetInfoProjSphereType_GCSRS(theProj) (theProj)->nSphere +#define GetInfoProjSpheroidID_GCSRS(theProj) (theProj)->nEllipsoidID + +GCSysCoord GCSRSAPI_CALL1(*) CreateSysCoord_GCSRS ( int srsid, int timezone ); +void GCSRSAPI_CALL DestroySysCoord_GCSRS ( GCSysCoord** theSysCoord ); +#define GetSysCoordSystemID_GCSRS(theSysCoord) (theSysCoord)->coordSystemID +#define SetSysCoordSystemID_GCSRS(theSysCoord,v) (theSysCoord)->coordSystemID= (v) +#define GetSysCoordTimeZone_GCSRS(theSysCoord) (theSysCoord)->timeZoneValue +#define SetSysCoordTimeZone_GCSRS(theSysCoord,v) (theSysCoord)->timeZoneValue= (v) +#define GetSysCoordName_GCSRS(theSysCoord) (theSysCoord)->pszSysCoordName +#define SetSysCoordName_GCSRS(theSysCoord,v) (theSysCoord)->pszSysCoordName= (v) +#define GetSysCoordUnit_GCSRS(theSysCoord) (theSysCoord)->pszUnit +#define SetSysCoordUnit_GCSRS(theSysCoord,v) (theSysCoord)->pszUnit= (v) +#define GetSysCoordPrimeMeridian_GCSRS(theSysCoord) (theSysCoord)->dfPM +#define SetSysCoordPrimeMeridian_GCSRS(theSysCoord,v) (theSysCoord)->dfPM= (v) +#define GetSysCoordCentralMeridian_GCSRS(theSysCoord) (theSysCoord)->dfLambda0 +#define SetSysCoordCentralMeridian_GCSRS(theSysCoord,v) (theSysCoord)->dfLambda0= (v) +#define GetSysCoordLatitudeOfOrigin_GCSRS(theSysCoord) (theSysCoord)->dfPhi0 +#define SetSysCoordLatitudeOfOrigin_GCSRS(theSysCoord,v) (theSysCoord)->dfPhi0= (v) +#define GetSysCoordStandardParallel1_GCSRS(theSysCoord) (theSysCoord)->dfPhi1 +#define SetSysCoordStandardParallel1_GCSRS(theSysCoord,v) (theSysCoord)->dfPhi1= (v) +#define GetSysCoordStandardParallel2_GCSRS(theSysCoord) (theSysCoord)->dfPhi2 +#define SetSysCoordStandardParallel2_GCSRS(theSysCoord,v) (theSysCoord)->dfPhi2= (v) +#define GetSysCoordScaleFactor_GCSRS(theSysCoord) (theSysCoord)->dfk0 +#define SetSysCoordScaleFactor_GCSRS(theSysCoord,v) (theSysCoord)->dfk0= (v) +#define GetSysCoordFalseEasting_GCSRS(theSysCoord) (theSysCoord)->dfX0 +#define SetSysCoordFalseEasting_GCSRS(theSysCoord,v) (theSysCoord)->dfX0= (v) +#define GetSysCoordFalseNorthing_GCSRS(theSysCoord) (theSysCoord)->dfY0 +#define SetSysCoordFalseNorthing_GCSRS(theSysCoord,v) (theSysCoord)->dfY0= (v) +#define GetSysCoordDatumID_GCSRS(theSysCoord) (theSysCoord)->nDatumID +#define SetSysCoordDatumID_GCSRS(theSysCoord,v) (theSysCoord)->nDatumID= (v) +#define GetSysCoordProjID_GCSRS(theSysCoord) (theSysCoord)->nProjID +#define SetSysCoordProjID_GCSRS(theSysCoord,v) (theSysCoord)->nProjID= (v) + +GCSysCoord GCSRSAPI_CALL1(*) OGRSpatialReference2SysCoord_GCSRS ( OGRSpatialReferenceH poSR ); +OGRSpatialReferenceH GCSRSAPI_CALL SysCoord2OGRSpatialReference_GCSRS ( GCSysCoord* syscoord ); + +#ifdef __cplusplus +} +#endif + + +#endif /* ndef _GEOCONCEPT_SYSCOORD_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/makefile.vc new file mode 100644 index 000000000..9e7fa65e4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/makefile.vc @@ -0,0 +1,13 @@ +OBJ = geoconcept.obj geoconcept_syscoord.obj \ + ogrgeoconceptdriver.obj \ + ogrgeoconceptdatasource.obj ogrgeoconceptlayer.obj +EXTRAFLAGS = -I.. -I..\.. -DUSE_CPL /wd4706 + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptdatasource.cpp new file mode 100644 index 000000000..98cb67fb1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptdatasource.cpp @@ -0,0 +1,572 @@ +/****************************************************************************** + * $Id: ogrgeoconceptdatasource.cpp + * + * Name: ogrgeoconceptdatasource.h + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGeoconceptDataSource class. + * Language: C++ + * + ****************************************************************************** + * Copyright (c) 2007, Geoconcept and IGN + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrgeoconceptlayer.h" +#include "ogrgeoconceptdatasource.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrgeoconceptdatasource.cpp 00000 2007-11-03 11:49:22Z drichard $"); + +/************************************************************************/ +/* OGRGeoconceptDataSource() */ +/************************************************************************/ + +OGRGeoconceptDataSource::OGRGeoconceptDataSource() + +{ + _papoLayers = NULL; + _nLayers = 0; + + _pszGCT = NULL; + _pszName = NULL; + _pszDirectory = NULL; + _pszExt = NULL; + _papszOptions = NULL; + + _bSingleNewFile = FALSE; + _bUpdate = FALSE; + _hGXT = NULL; +} + +/************************************************************************/ +/* ~OGRGeoconceptDataSource() */ +/************************************************************************/ + +OGRGeoconceptDataSource::~OGRGeoconceptDataSource() + +{ + if ( _pszGCT ) + { + CPLFree( _pszGCT ); + } + if ( _pszName ) + { + CPLFree( _pszName ); + } + if ( _pszDirectory ) + { + CPLFree( _pszDirectory ); + } + if ( _pszExt ) + { + CPLFree( _pszExt ); + } + + if ( _papoLayers ) + { + for( int i = 0; i < _nLayers; i++ ) + { + delete _papoLayers[i]; + } + + CPLFree( _papoLayers ); + } + + if( _hGXT ) + { + Close_GCIO(&_hGXT); + } + + if ( _papszOptions ) + { + CSLDestroy( _papszOptions ); + } +} + +/************************************************************************/ +/* Open() */ +/* */ +/* Open an existing file. */ +/************************************************************************/ + +int OGRGeoconceptDataSource::Open( const char* pszName, int bTestOpen, int bUpdate ) + +{ +/* -------------------------------------------------------------------- */ +/* Is the given path a directory or a regular file? */ +/* -------------------------------------------------------------------- */ + VSIStatBuf stat; + + if( CPLStat( pszName, &stat ) != 0 + || (!VSI_ISDIR(stat.st_mode) && !VSI_ISREG(stat.st_mode)) ) + { + if( !bTestOpen ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s is neither a file or directory, Geoconcept access failed.", + pszName ); + } + + return FALSE; + } + + if( VSI_ISDIR(stat.st_mode) ) + { + CPLDebug( "GEOCONCEPT", + "%s is a directory, Geoconcept access is not yet supported.", + pszName ); + + return FALSE; + } + + if( VSI_ISREG(stat.st_mode) ) + { + _bSingleNewFile= FALSE; + _bUpdate= bUpdate; + _pszName= CPLStrdup( pszName ); + if( !LoadFile( _bUpdate? "a+t":"rt" ) ) + { + CPLDebug( "GEOCONCEPT", + "Failed to open Geoconcept %s." + " It may be corrupt.", + pszName ); + + return FALSE; + } + + return TRUE; + } + + return _nLayers > 0; +} + +/************************************************************************/ +/* LoadFile() */ +/************************************************************************/ + +int OGRGeoconceptDataSource::LoadFile( const char *pszMode ) + +{ + OGRGeoconceptLayer *poFile; + + if( _pszExt == NULL) + { + const char* pszExtension = CPLGetExtension(_pszName); + _pszExt = CPLStrdup(pszExtension); + } + CPLStrlwr( _pszExt ); + + if( !_pszDirectory ) + _pszDirectory = CPLStrdup( CPLGetPath(_pszName) ); + + if( (_hGXT= Open_GCIO(_pszName,_pszExt,pszMode,_pszGCT))==NULL ) + { + return FALSE; + } + + /* Collect layers : */ + GCExportFileMetadata* Meta= GetGCMeta_GCIO(_hGXT); + if( Meta ) + { + int nC, iC, nS, iS; + + if( (nC= CountMetaTypes_GCIO(Meta))>0 ) + { + GCType* aClass; + GCSubType* aSubclass; + + for( iC= 0; iCOpen(aSubclass) != OGRERR_NONE ) + { + delete poFile; + return FALSE; + } + + /* Add layer to data source layers list */ + _papoLayers = (OGRGeoconceptLayer **) + CPLRealloc( _papoLayers, sizeof(OGRGeoconceptLayer *) * (_nLayers+1) ); + _papoLayers[_nLayers++] = poFile; + + CPLDebug("GEOCONCEPT", + "nLayers=%d - last=[%s]", + _nLayers, poFile->GetLayerDefn()->GetName()); + } + } + } + } + } + } + } + + return TRUE; +} + +/************************************************************************/ +/* Create() */ +/* */ +/* Create a new dataset. */ +/* */ +/* Options (-dsco) : */ +/* EXTENSION : gxt|txt */ +/* CONFIG : path to GCT file */ +/************************************************************************/ + +int OGRGeoconceptDataSource::Create( const char *pszName, char** papszOptions ) + +{ + const char *pszConf; + const char *pszExtension; + + if( _pszName ) CPLFree(_pszName); + _papszOptions = CSLDuplicate( papszOptions ); + + pszConf= CSLFetchNameValue(papszOptions,"CONFIG"); + if( pszConf != NULL ) + { + _pszGCT = CPLStrdup(pszConf); + } + + _pszExt = (char *)CSLFetchNameValue(papszOptions,"EXTENSION"); + pszExtension = CSLFetchNameValue(papszOptions,"EXTENSION"); + if( pszExtension == NULL ) + { + _pszExt = CPLStrdup(CPLGetExtension(pszName)); + } + else + { + _pszExt = CPLStrdup(pszExtension); + } + + if( strlen(_pszExt) == 0 ) + { + if( VSIMkdir( pszName, 0755 ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Directory %s already exists" + " as geoconcept datastore or" + " is made up of a non existing list of directories.", + pszName ); + + return FALSE; + } + _pszDirectory = CPLStrdup( pszName ); + CPLFree(_pszExt); + _pszExt = CPLStrdup("gxt"); + char *pszbName = CPLStrdup(CPLGetBasename( pszName )); + if (strlen(pszbName)==0) {/* pszName ends with '/' */ + CPLFree(pszbName); + char *pszNameDup= CPLStrdup(pszName); + pszNameDup[strlen(pszName)-2] = '\0'; + pszbName = CPLStrdup(CPLGetBasename( pszNameDup )); + CPLFree(pszNameDup); + } + _pszName = CPLStrdup((char *)CPLFormFilename( _pszDirectory, pszbName, NULL )); + CPLFree(pszbName); + } + else + { + _pszDirectory = CPLStrdup( CPLGetPath(pszName) ); + _pszName = CPLStrdup( pszName ); + } + +/* -------------------------------------------------------------------- */ +/* Create a new single file. */ +/* OGRGeoconceptDriver::ICreateLayer() will do the job. */ +/* -------------------------------------------------------------------- */ + _bSingleNewFile = TRUE; + + if( !LoadFile( "wt" ) ) + { + CPLDebug( "GEOCONCEPT", + "Failed to create Geoconcept %s.", + pszName ); + + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/* */ +/* Options (-lco) : */ +/* FEATURETYPE : TYPE.SUBTYPE */ +/************************************************************************/ + +OGRLayer *OGRGeoconceptDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS /* = NULL */, + OGRwkbGeometryType eType /* = wkbUnknown */, + char ** papszOptions /* = NULL */ ) + +{ + GCTypeKind gcioFeaType; + GCDim gcioDim; + OGRGeoconceptLayer *poFile= NULL; + const char *pszFeatureType; + char **ft; + int iLayer; + char pszln[512]; + + if( _hGXT == NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Internal Error : null datasource handler." + ); + return NULL; + } + + if( poSRS == NULL && !_bUpdate) + { + CPLError( CE_Failure, CPLE_NotSupported, + "SRS is mandatory of creating a Geoconcept Layer." + ); + return NULL; + } + + /* + * pszLayerName Class.Subclass if -nln option used, otherwise file name + */ + if( !(pszFeatureType = CSLFetchNameValue(papszOptions,"FEATURETYPE")) ) + { + if( !pszLayerName || !strchr(pszLayerName,'.') ) + { + snprintf(pszln,511,"%s.%s", pszLayerName? pszLayerName:"ANONCLASS", + pszLayerName? pszLayerName:"ANONSUBCLASS"); + pszln[511]= '\0'; + pszFeatureType= pszln; + } + else + pszFeatureType= pszLayerName; + } + + if( !(ft= CSLTokenizeString2(pszFeatureType,".",0)) || + CSLCount(ft)!=2 ) + { + CSLDestroy(ft); + CPLError( CE_Failure, CPLE_AppDefined, + "Feature type name '%s' is incorrect." + "Correct syntax is : Class.Subclass.", + pszFeatureType ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Figure out what type of layer we need. */ +/* -------------------------------------------------------------------- */ + gcioDim= v2D_GCIO; + if( eType == wkbUnknown ) + gcioFeaType = vUnknownItemType_GCIO; + else if( eType == wkbPoint ) + gcioFeaType = vPoint_GCIO; + else if( eType == wkbLineString ) + gcioFeaType = vLine_GCIO; + else if( eType == wkbPolygon ) + gcioFeaType = vPoly_GCIO; + else if( eType == wkbMultiPoint ) + gcioFeaType = vPoint_GCIO; + else if( eType == wkbMultiLineString ) + gcioFeaType = vLine_GCIO; + else if( eType == wkbMultiPolygon ) + gcioFeaType = vPoly_GCIO; + else if( eType == wkbPoint25D ) + { + gcioFeaType = vPoint_GCIO; + gcioDim= v3DM_GCIO; + } + else if( eType == wkbLineString25D ) + { + gcioFeaType = vLine_GCIO; + gcioDim= v3DM_GCIO; + } + else if( eType == wkbPolygon25D ) + { + gcioFeaType = vPoly_GCIO; + gcioDim= v3DM_GCIO; + } + else if( eType == wkbMultiPoint25D ) + { + gcioFeaType = vPoint_GCIO; + gcioDim= v3DM_GCIO; + } + else if( eType == wkbMultiLineString25D ) + { + gcioFeaType = vLine_GCIO; + gcioDim= v3DM_GCIO; + } + else if( eType == wkbMultiPolygon25D ) + { + gcioFeaType = vPoly_GCIO; + gcioDim= v3DM_GCIO; + } + else + { + CSLDestroy(ft); + CPLError( CE_Failure, CPLE_NotSupported, + "Geometry type of '%s' not supported in Geoconcept files.", + OGRGeometryTypeToName(eType) ); + return NULL; + } + + /* + * As long as we use the CONFIG, creating a layer implies the + * layer name to exist in the CONFIG as "Class.Subclass". + * Removing the CONFIG, implies on-the-fly-creation of layers... + */ + if( _nLayers > 0 ) + for( iLayer= 0; iLayer<_nLayers; iLayer++) + { + poFile= (OGRGeoconceptLayer*)GetLayer(iLayer); + if( EQUAL(poFile->GetLayerDefn()->GetName(),pszFeatureType) ) + { + break; + } + poFile= NULL; + } + if( !poFile ) + { + GCSubType* aSubclass= NULL; + GCExportFileMetadata* m; + + if( !(m= GetGCMeta_GCIO(_hGXT)) ) + { + if( !(m= CreateHeader_GCIO()) ) + { + CSLDestroy(ft); + return NULL; + } + SetMetaExtent_GCIO(m, CreateExtent_GCIO(HUGE_VAL,HUGE_VAL,-HUGE_VAL,-HUGE_VAL)); + SetGCMeta_GCIO(_hGXT, m); + } + if( FindFeature_GCIO(_hGXT, pszFeatureType) ) + { + CSLDestroy(ft); + CPLError( CE_Failure, CPLE_AppDefined, + "Layer '%s' already exists.", + pszFeatureType ); + return NULL; + } + if( !AddType_GCIO(_hGXT, ft[0], -1L) ) + { + CSLDestroy(ft); + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to add layer '%s'.", + pszFeatureType ); + return NULL; + } + if( !(aSubclass= AddSubType_GCIO(_hGXT, ft[0], ft[1], -1L, gcioFeaType, gcioDim)) ) + { + CSLDestroy(ft); + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to add layer '%s'.", + pszFeatureType ); + return NULL; + } + /* complete feature type with private fields : */ + AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kIdentifier_GCIO, -100, vIntFld_GCIO, NULL, NULL); + AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kClass_GCIO, -101, vMemoFld_GCIO, NULL, NULL); + AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kSubclass_GCIO, -102, vMemoFld_GCIO, NULL, NULL); + AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kName_GCIO, -103, vMemoFld_GCIO, NULL, NULL); + AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kNbFields_GCIO, -104, vIntFld_GCIO, NULL, NULL); + AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kX_GCIO, -105, vRealFld_GCIO, NULL, NULL); + AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kY_GCIO, -106, vRealFld_GCIO, NULL, NULL); + /* user's fields will be added with Layer->CreateField() method ... */ + switch( gcioFeaType ) + { + case vPoint_GCIO : + break; + case vLine_GCIO : + AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kXP_GCIO, -107, vRealFld_GCIO, NULL, NULL); + AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kYP_GCIO, -108, vRealFld_GCIO, NULL, NULL); + AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kGraphics_GCIO, -109, vUnknownItemType_GCIO, NULL, NULL); + break; + default : + AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kGraphics_GCIO, -109, vUnknownItemType_GCIO, NULL, NULL); + break; + } + SetSubTypeGCHandle_GCIO(aSubclass,_hGXT); + + /* Add layer to data source layers list */ + poFile = new OGRGeoconceptLayer; + if( poFile->Open(aSubclass) != OGRERR_NONE ) + { + CSLDestroy(ft); + delete poFile; + return NULL; + } + + _papoLayers = (OGRGeoconceptLayer **) + CPLRealloc( _papoLayers, sizeof(OGRGeoconceptLayer *) * (_nLayers+1) ); + _papoLayers[_nLayers++] = poFile; + + CPLDebug("GEOCONCEPT", + "nLayers=%d - last=[%s]", + _nLayers, poFile->GetLayerDefn()->GetName()); + } + CSLDestroy(ft); + +/* -------------------------------------------------------------------- */ +/* Assign the coordinate system (if provided) */ +/* -------------------------------------------------------------------- */ + if( poSRS != NULL ) + poFile->SetSpatialRef( poSRS ); + + return poFile; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGeoconceptDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRGeoconceptDataSource::GetLayer( int iLayer ) + +{ + OGRLayer *poFile; + if( iLayer < 0 || iLayer >= GetLayerCount() ) + poFile= NULL; + else + poFile= _papoLayers[iLayer]; + return poFile; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptdatasource.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptdatasource.h new file mode 100644 index 000000000..bb166bf6c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptdatasource.h @@ -0,0 +1,76 @@ +/********************************************************************** + * $Id: ogrgeoconceptdatasource.h + * + * Name: ogrgeoconceptdatasource.h + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGeoconceptDataSource class. + * Language: C++ + * + ********************************************************************** + * Copyright (c) 2007, Geoconcept and IGN + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + **********************************************************************/ + +#include "ogrsf_frmts.h" +#include "ogrgeoconceptlayer.h" + +#ifndef _GEOCONCEPT_OGR_DATASOURCE_H_INCLUDED_ +#define _GEOCONCEPT_OGR_DATASOURCE_H_INCLUDED_ + +/**********************************************************************/ +/* OGCGeoconceptDataSource Class */ +/**********************************************************************/ +class OGRGeoconceptDataSource : public OGRDataSource +{ + private: + OGRGeoconceptLayer **_papoLayers; + int _nLayers; + + char *_pszGCT; + char *_pszName; + char *_pszDirectory; + char *_pszExt; + char **_papszOptions; + int _bSingleNewFile; + int _bUpdate; + GCExportFileH *_hGXT; + + public: + OGRGeoconceptDataSource(); + ~OGRGeoconceptDataSource(); + + int Open( const char* pszName, int bTestOpen, int bUpdate ); + int Create( const char* pszName, char** papszOptions ); + + const char* GetName() { return _pszName; } + int GetLayerCount() { return _nLayers; } + OGRLayer* GetLayer( int iLayer ); +// OGRErr DeleteLayer( int iLayer ); + int TestCapability( const char* pszCap ); + + OGRLayer* ICreateLayer( const char* pszName, + OGRSpatialReference* poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char** papszOptions = NULL ); + private: + int LoadFile( const char * ); +}; + +#endif /* _GEOCONCEPT_OGR_DATASOURCE_H_INCLUDED_ */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptdriver.cpp new file mode 100644 index 000000000..32c5f7a5c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptdriver.cpp @@ -0,0 +1,266 @@ +/****************************************************************************** + * $Id: ogrgeoconceptdriver.cpp + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGeoconceptDriver class. + * Author: Didier Richard, didier.richard@ign.fr + * Language: C++ + * + ****************************************************************************** + * Copyright (c) 2007, Geoconcept and IGN + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrgeoconceptdatasource.h" +#include "ogrgeoconceptdriver.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrgeoconceptdriver.cpp 00000 2007-11-03 10:42:48Z drichard $"); + +/************************************************************************/ +/* ~OGRGeoconceptDriver() */ +/************************************************************************/ + +OGRGeoconceptDriver::~OGRGeoconceptDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRGeoconceptDriver::GetName() + +{ + return "Geoconcept"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRGeoconceptDriver::Open( const char* pszFilename, + int bUpdate ) + +{ + OGRGeoconceptDataSource *poDS; + +/* -------------------------------------------------------------------- */ +/* We will only consider .gxt and .txt files. */ +/* -------------------------------------------------------------------- */ + const char* pszExtension = CPLGetExtension(pszFilename); + if( !EQUAL(pszExtension,"gxt") && !EQUAL(pszExtension,"txt") ) + { + return NULL; + } + + poDS = new OGRGeoconceptDataSource(); + + if( !poDS->Open( pszFilename, TRUE, bUpdate ) ) + { + delete poDS; + return NULL; + } + return poDS; +} + +/************************************************************************/ +/* CreateDataSource() */ +/* */ +/* Options (-dsco) : */ +/* EXTENSION=GXT|TXT (default GXT) */ +/************************************************************************/ + +OGRDataSource *OGRGeoconceptDriver::CreateDataSource( const char* pszName, + char** papszOptions ) + +{ + VSIStatBuf stat; + /* int bSingleNewFile = FALSE; */ + + if( pszName==NULL || strlen(pszName)==0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid datasource name (null or empty)"); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Is the target a valid existing directory? */ +/* -------------------------------------------------------------------- */ + if( CPLStat( pszName, &stat ) == 0 ) + { + if( !VSI_ISDIR(stat.st_mode) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s is not a valid existing directory.", + pszName ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Does it end with the extension .gxt indicating the user likely */ +/* wants to create a single file set? */ +/* -------------------------------------------------------------------- */ + else if( + EQUAL(CPLGetExtension(pszName),"gxt") || + EQUAL(CPLGetExtension(pszName),"txt") + ) + { + /* bSingleNewFile = TRUE; */ + } + +/* -------------------------------------------------------------------- */ +/* Otherwise try to create a new directory. */ +/* -------------------------------------------------------------------- */ + else + { + VSIStatBuf sStat; + + if( VSIStat( pszName, &sStat ) == 0 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create datasource named %s, " + "but that is an existing directory.", + pszName ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Return a new OGRDataSource() */ +/* -------------------------------------------------------------------- */ + OGRGeoconceptDataSource *poDS = NULL; + + poDS = new OGRGeoconceptDataSource(); + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + return NULL; + } + return poDS; +} + +/************************************************************************/ +/* DeleteDataSource() */ +/************************************************************************/ + +OGRErr OGRGeoconceptDriver::DeleteDataSource( const char *pszDataSource ) + +{ + int iExt; + VSIStatBuf sStatBuf; + static const char *apszExtensions[] = + { "gxt", "txt", "gct", "gcm", "gcr", NULL }; + + if( VSIStat( pszDataSource, &sStatBuf ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s does not appear to be a file or directory.", + pszDataSource ); + + return OGRERR_FAILURE; + } + + if( VSI_ISREG(sStatBuf.st_mode) + && ( + EQUAL(CPLGetExtension(pszDataSource),"gxt") || + EQUAL(CPLGetExtension(pszDataSource),"txt") + ) ) + { + for( iExt=0; apszExtensions[iExt] != NULL; iExt++ ) + { + const char *pszFile = CPLResetExtension(pszDataSource, + apszExtensions[iExt] ); + if( VSIStat( pszFile, &sStatBuf ) == 0 ) + VSIUnlink( pszFile ); + } + } + else if( VSI_ISDIR(sStatBuf.st_mode) ) + { + char **papszDirEntries = CPLReadDir( pszDataSource ); + int iFile; + + for( iFile = 0; + papszDirEntries != NULL && papszDirEntries[iFile] != NULL; + iFile++ ) + { + if( CSLFindString( (char **) apszExtensions, + CPLGetExtension(papszDirEntries[iFile])) != -1) + { + VSIUnlink( CPLFormFilename( pszDataSource, + papszDirEntries[iFile], + NULL ) ); + } + } + + CSLDestroy( papszDirEntries ); + + VSIRmdir( pszDataSource ); + } + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGeoconceptDriver::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODrCCreateDataSource) ) + return TRUE; + else if( EQUAL(pszCap,ODrCDeleteDataSource) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRGeoconcept() */ +/************************************************************************/ + +void RegisterOGRGeoconcept() + +{ + OGRSFDriver* poDriver = new OGRGeoconceptDriver; + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "gxt txt" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" "); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" "); + + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( poDriver ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptdriver.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptdriver.h new file mode 100644 index 000000000..8bd21b5fe --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptdriver.h @@ -0,0 +1,52 @@ +/********************************************************************** + * $Id: ogrgeoconceptdriver.h + * + * Name: ogrgeoconceptdriver.h + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGeoconceptDriver class. + * Language: C++ + * + ********************************************************************** + * Copyright (c) 2007, Geoconcept and IGN + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + **********************************************************************/ + +#include "ogrsf_frmts.h" + +#ifndef _GEOCONCEPT_OGR_DRIVER_H_INCLUDED_ +#define _GEOCONCEPT_OGR_DRIVER_H_INCLUDED_ + +/************************************************************************/ +/* OGRGeoconceptDriver */ +/************************************************************************/ + +class OGRGeoconceptDriver : public OGRSFDriver +{ +public: + ~OGRGeoconceptDriver(); + + const char* GetName( ); + OGRDataSource* Open( const char* pszName, int bUpdate = FALSE ); + int TestCapability( const char* pszCap ); + OGRDataSource* CreateDataSource( const char* pszName, char** papszOptions = NULL ); + OGRErr DeleteDataSource( const char* pszName ); +}; + +#endif /* _GEOCONCEPT_OGR_DRIVER_H_INCLUDED_ */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptlayer.cpp new file mode 100644 index 000000000..ed22dc2e2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptlayer.cpp @@ -0,0 +1,668 @@ +/****************************************************************************** + * $Id: ogrgeoconceptlayer.cpp + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGeoconceptLayer class. + * Author: Didier Richard, didier.richard@ign.fr + * Language: C++ + * + ****************************************************************************** + * Copyright (c) 2007, Geoconcept and IGN + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrgeoconceptlayer.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrgeoconceptlayer.cpp 00000 2007-11-03 16:08:14Z drichard $"); + +/************************************************************************/ +/* OGRGeoconceptLayer() */ +/************************************************************************/ + +OGRGeoconceptLayer::OGRGeoconceptLayer() +{ + _poFeatureDefn = NULL; + _gcFeature = NULL; +} + +/************************************************************************/ +/* ~OGRGeoconceptLayer() */ +/************************************************************************/ + +OGRGeoconceptLayer::~OGRGeoconceptLayer() + +{ + CPLDebug( "GEOCONCEPT", + "%ld features on layer %s.", + GetSubTypeNbFeatures_GCIO(_gcFeature), + _poFeatureDefn->GetName()); + + if( _poFeatureDefn ) + { + _poFeatureDefn->Release(); + } + + _gcFeature= NULL; /* deleted when OGCGeoconceptDatasource destroyed */ +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRErr OGRGeoconceptLayer::Open( GCSubType* Subclass ) + +{ + _gcFeature= Subclass; + if( GetSubTypeFeatureDefn_GCIO(_gcFeature) ) + { + _poFeatureDefn = (OGRFeatureDefn *)GetSubTypeFeatureDefn_GCIO(_gcFeature); + _poFeatureDefn->Reference(); + } + else + { + char pszln[512]; + int n, i; + + snprintf(pszln, 511, "%s.%s", GetSubTypeName_GCIO(_gcFeature), + GetTypeName_GCIO(GetSubTypeType_GCIO(_gcFeature))); + pszln[511]='\0'; + + _poFeatureDefn = new OGRFeatureDefn(pszln); + SetDescription( _poFeatureDefn->GetName() ); + _poFeatureDefn->Reference(); + _poFeatureDefn->SetGeomType(wkbUnknown); + + if( (n= CountSubTypeFields_GCIO(_gcFeature))>0 ) + { + GCField* aField= NULL; + OGRFieldType oft; + for( i= 0; iAddFieldDefn(&ofd); + } + } + } + SetSubTypeFeatureDefn_GCIO(_gcFeature, (OGRFeatureDefnH) _poFeatureDefn); + _poFeatureDefn->Reference(); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGeoconceptLayer::ResetReading() + +{ + Rewind_GCIO(GetSubTypeGCHandle_GCIO(_gcFeature),_gcFeature); +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRGeoconceptLayer::GetNextFeature() + +{ + OGRFeature* poFeature = NULL; + + for( ;; ) + { + if( !(poFeature= (OGRFeature*)ReadNextFeature_GCIO(_gcFeature)) ) + { + /* + * As several features are embed in the Geoconcept file, + * when reaching the end of the feature type, resetting + * the reader would allow reading other features : + * ogrinfo -ro export.gxt FT1 FT2 ... + * will be all features for all features types ! + */ + Rewind_GCIO(GetSubTypeGCHandle_GCIO(_gcFeature),NULL); + break; + } + if( (m_poFilterGeom == NULL || FilterGeometry( poFeature->GetGeometryRef() ) ) + && + (m_poAttrQuery == NULL || m_poAttrQuery->Evaluate( poFeature )) ) + { + break; + } + delete poFeature; + } + + CPLDebug( "GEOCONCEPT", + "FID : " CPL_FRMT_GIB "\n" + "%s : %s", + poFeature? poFeature->GetFID():-1L, + poFeature && poFeature->GetFieldCount()>0? poFeature->GetFieldDefnRef(0)->GetNameRef():"-", + poFeature && poFeature->GetFieldCount()>0? poFeature->GetFieldAsString(0):""); + + return poFeature; +} + +/************************************************************************/ +/* OGRGeoconceptLayer_GetCompatibleFieldName() */ +/************************************************************************/ + +static char* OGRGeoconceptLayer_GetCompatibleFieldName(const char* pszName) +{ + char* pszCompatibleName = CPLStrdup(pszName); + int i; + for(i=0;pszCompatibleName[i] != 0;i++) + { + if (pszCompatibleName[i] == ' ') + pszCompatibleName[i] = '_'; + } + return pszCompatibleName; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRGeoconceptLayer::ICreateFeature( OGRFeature* poFeature ) + +{ + OGRwkbGeometryType eGt; + OGRGeometry* poGeom; + int nextField, iGeom, nbGeom, isSingle; + + poGeom= poFeature->GetGeometryRef(); + + if (poGeom == NULL) + { + CPLError( CE_Warning, CPLE_NotSupported, + "NULL geometry not supported in Geoconcept, feature skipped.\n"); + return OGRERR_NONE; + } + + eGt= poGeom->getGeometryType(); + switch( eGt ) { + case wkbPoint : + case wkbPoint25D : + case wkbMultiPoint : + case wkbMultiPoint25D : + if( GetSubTypeKind_GCIO(_gcFeature)==vUnknownItemType_GCIO ) + { + SetSubTypeKind_GCIO(_gcFeature,vPoint_GCIO); + } + else if( GetSubTypeKind_GCIO(_gcFeature)!=vPoint_GCIO ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can't write non ponctual feature in a ponctual Geoconcept layer %s.\n", + _poFeatureDefn->GetName()); + return OGRERR_FAILURE; + } + break; + case wkbLineString : + case wkbLineString25D : + case wkbMultiLineString : + case wkbMultiLineString25D : + if( GetSubTypeKind_GCIO(_gcFeature)==vUnknownItemType_GCIO ) + { + SetSubTypeKind_GCIO(_gcFeature,vLine_GCIO); + } + else if( GetSubTypeKind_GCIO(_gcFeature)!=vLine_GCIO ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can't write non linear feature in a linear Geoconcept layer %s.\n", + _poFeatureDefn->GetName()); + return OGRERR_FAILURE; + } + break; + case wkbPolygon : + case wkbPolygon25D : + case wkbMultiPolygon : + case wkbMultiPolygon25D : + if( GetSubTypeKind_GCIO(_gcFeature)==vUnknownItemType_GCIO ) + { + SetSubTypeKind_GCIO(_gcFeature,vPoly_GCIO); + } + else if( GetSubTypeKind_GCIO(_gcFeature)!=vPoly_GCIO ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can't write non polygonal feature in a polygonal Geoconcept layer %s.\n", + _poFeatureDefn->GetName()); + return OGRERR_FAILURE; + } + break; + case wkbUnknown : + case wkbGeometryCollection : + case wkbGeometryCollection25D : + case wkbNone : + case wkbLinearRing : + default : + CPLError( CE_Warning, CPLE_AppDefined, + "Geometry type %s not supported in Geoconcept, feature skipped.\n", + OGRGeometryTypeToName(eGt) ); + return OGRERR_NONE; + } + if( GetSubTypeDim_GCIO(_gcFeature)==vUnknown3D_GCIO ) + { + if( poGeom->getCoordinateDimension()==3 ) + { + SetSubTypeDim_GCIO(_gcFeature,v3D_GCIO); + } + else + { + SetSubTypeDim_GCIO(_gcFeature,v2D_GCIO); + } + } + + switch( eGt ) { + case wkbPoint : + case wkbPoint25D : + nbGeom= 1; + isSingle= TRUE; + break; + case wkbMultiPoint : + case wkbMultiPoint25D : + nbGeom= ((OGRGeometryCollection*)poGeom)->getNumGeometries(); + isSingle= FALSE; + break; + case wkbLineString : + case wkbLineString25D : + nbGeom= 1; + isSingle= TRUE; + break; + case wkbMultiLineString : + case wkbMultiLineString25D : + nbGeom= ((OGRGeometryCollection*)poGeom)->getNumGeometries(); + isSingle= FALSE; + break; + case wkbPolygon : + case wkbPolygon25D : + nbGeom= 1; + isSingle= TRUE; + break; + case wkbMultiPolygon : + case wkbMultiPolygon25D : + nbGeom= ((OGRGeometryCollection*)poGeom)->getNumGeometries(); + isSingle= FALSE; + break; + case wkbUnknown : + case wkbGeometryCollection : + case wkbGeometryCollection25D : + case wkbNone : + case wkbLinearRing : + default : + nbGeom= 0; + isSingle= FALSE; + break; + } + + /* 1st feature, let's write header : */ + if( GetGCMode_GCIO(GetSubTypeGCHandle_GCIO(_gcFeature)) == vWriteAccess_GCIO && + GetFeatureCount(TRUE) == 0 ) + if( WriteHeader_GCIO(GetSubTypeGCHandle_GCIO(_gcFeature))==NULL ) + { + return OGRERR_FAILURE; + } + + if( nbGeom>0 ) + { + for( iGeom= 0; iGeomGetFID():OGRNullFID); + while (nextField!=WRITECOMPLETED_GCIO) + { + if( nextField==WRITEERROR_GCIO ) + { + return OGRERR_FAILURE; + } + if( nextField==GEOMETRYEXPECTED_GCIO ) + { + OGRGeometry* poGeomPart= + isSingle? poGeom:((OGRGeometryCollection*)poGeom)->getGeometryRef(iGeom); + nextField= WriteFeatureGeometry_GCIO(_gcFeature, + (OGRGeometryH)poGeomPart); + } + else + { + int iF, nF; + OGRFieldDefn *poField; + GCField* theField= GetSubTypeField_GCIO(_gcFeature,nextField); + /* for each field, find out its mapping ... */ + if( (nF= poFeature->GetFieldCount())>0 ) + { + for( iF= 0; iFGetFieldDefnRef(iF); + char* pszName = OGRGeoconceptLayer_GetCompatibleFieldName(poField->GetNameRef()); + if( EQUAL(pszName, GetFieldName_GCIO(theField)) ) + { + CPLFree(pszName); + nextField= WriteFeatureFieldAsString_GCIO(_gcFeature, + nextField, + poFeature->IsFieldSet(iF)? + poFeature->GetFieldAsString(iF) + : + NULL); + break; + } + CPLFree(pszName); + } + if( iF==nF ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find a field attached to %s on Geoconcept layer %s.\n", + GetFieldName_GCIO(theField), _poFeatureDefn->GetName()); + return OGRERR_FAILURE; + } + } + else + { + nextField= WRITECOMPLETED_GCIO; + } + } + } + StopWritingFeature_GCIO(_gcFeature); + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRGeoconceptLayer::GetSpatialRef() + +{ + GCExportFileH* hGXT= GetSubTypeGCHandle_GCIO(_gcFeature); + if( !hGXT ) return NULL; + GCExportFileMetadata* Meta= GetGCMeta_GCIO(hGXT); + if( !Meta ) return NULL; + return (OGRSpatialReference*)GetMetaSRS_GCIO(Meta); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/************************************************************************/ + +GIntBig OGRGeoconceptLayer::GetFeatureCount( int bForce ) + +{ + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL ) + return OGRLayer::GetFeatureCount( bForce ); + else + return GetSubTypeNbFeatures_GCIO(_gcFeature); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRGeoconceptLayer::GetExtent( OGREnvelope* psExtent, + CPL_UNUSED int bForce ) +{ + GCExtent* theExtent; + + theExtent= GetSubTypeExtent_GCIO( _gcFeature ); + psExtent->MinX= GetExtentULAbscissa_GCIO(theExtent); + psExtent->MinY= GetExtentLROrdinate_GCIO(theExtent); + psExtent->MaxX= GetExtentLRAbscissa_GCIO(theExtent); + psExtent->MaxY= GetExtentULOrdinate_GCIO(theExtent); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGeoconceptLayer::TestCapability( const char* pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return FALSE; // the GetFeature() method does not work for this layer. TODO + + else if( EQUAL(pszCap,OLCSequentialWrite) ) + return TRUE; // the CreateFeature() method works for this layer. + + else if( EQUAL(pszCap,OLCRandomWrite) ) + return FALSE; // the SetFeature() method is not operational on this layer. + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; // this layer does not implement spatial filtering efficiently. + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return FALSE; // this layer can not return a feature count efficiently. FIXME + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return FALSE; // this layer can not return its data extent efficiently. FIXME + + else if( EQUAL(pszCap,OLCFastSetNextByIndex) ) + return FALSE; // this layer can not perform the SetNextByIndex() call efficiently. + + else if( EQUAL(pszCap,OLCDeleteFeature) ) + return FALSE; + + else if( EQUAL(pszCap,OLCCreateField) ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRGeoconceptLayer::CreateField( OGRFieldDefn *poField, + CPL_UNUSED int bApproxOK ) +{ + if( GetGCMode_GCIO(GetSubTypeGCHandle_GCIO(_gcFeature))==vReadAccess_GCIO ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can't create fields on a read-only Geoconcept layer.\n"); + return OGRERR_FAILURE; + + } + +/* -------------------------------------------------------------------- */ +/* Add field to layer */ +/* -------------------------------------------------------------------- */ + + { + /* check whether field exists ... */ + GCField* theField; + char* pszName = OGRGeoconceptLayer_GetCompatibleFieldName(poField->GetNameRef()); + + if( !(theField= FindFeatureField_GCIO(_gcFeature,pszName)) ) + { + if( GetFeatureCount(TRUE) > 0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can't create field '%s' on existing Geoconcept layer '%s.%s'.\n", + pszName, + GetSubTypeName_GCIO(_gcFeature), + GetTypeName_GCIO(GetSubTypeType_GCIO(_gcFeature)) ); + CPLFree(pszName); + return OGRERR_FAILURE; + } + if( GetSubTypeNbFields_GCIO(_gcFeature)==-1) + SetSubTypeNbFields_GCIO(_gcFeature, 0L); + if( !(theField= AddSubTypeField_GCIO(GetSubTypeGCHandle_GCIO(_gcFeature), + GetTypeName_GCIO(GetSubTypeType_GCIO(_gcFeature)), + GetSubTypeName_GCIO(_gcFeature), + FindFeatureFieldIndex_GCIO(_gcFeature,kNbFields_GCIO) + +GetSubTypeNbFields_GCIO(_gcFeature)+1L, + pszName, + GetSubTypeNbFields_GCIO(_gcFeature)-999L, + vUnknownItemType_GCIO, NULL, NULL)) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Field '%s' could not be created for Feature %s.%s.\n", + pszName, + GetSubTypeName_GCIO(_gcFeature), + GetTypeName_GCIO(GetSubTypeType_GCIO(_gcFeature)) + ); + CPLFree(pszName); + return OGRERR_FAILURE; + } + SetSubTypeNbFields_GCIO(_gcFeature, GetSubTypeNbFields_GCIO(_gcFeature)+1L); + _poFeatureDefn->AddFieldDefn(poField); + } + else + { + if( _poFeatureDefn->GetFieldIndex(GetFieldName_GCIO(theField))==-1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Field %s not found for Feature %s.%s.\n", + GetFieldName_GCIO(theField), + GetSubTypeName_GCIO(_gcFeature), + GetTypeName_GCIO(GetSubTypeType_GCIO(_gcFeature)) + ); + CPLFree(pszName); + return OGRERR_FAILURE; + } + } + + CPLFree(pszName); + pszName = NULL; + + /* check/update type ? */ + if( GetFieldKind_GCIO(theField)==vUnknownItemType_GCIO ) + { + switch(poField->GetType()) { + case OFTInteger : + SetFieldKind_GCIO(theField,vIntFld_GCIO); + break; + case OFTReal : + SetFieldKind_GCIO(theField,vRealFld_GCIO); + break; + case OFTDate : + SetFieldKind_GCIO(theField,vDateFld_GCIO); + break; + case OFTTime : + case OFTDateTime : + SetFieldKind_GCIO(theField,vTimeFld_GCIO); + break; + case OFTString : + SetFieldKind_GCIO(theField,vMemoFld_GCIO); + break; + case OFTIntegerList : + case OFTRealList : + case OFTStringList : + case OFTBinary : + default : + CPLError( CE_Failure, CPLE_NotSupported, + "Can't create fields of type %s on Geoconcept feature %s.\n", + OGRFieldDefn::GetFieldTypeName(poField->GetType()), + _poFeatureDefn->GetName() ); + return OGRERR_FAILURE; + } + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr OGRGeoconceptLayer::SyncToDisk() + +{ + FFlush_GCIO(GetSubTypeGCHandle_GCIO(_gcFeature)); + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetSpatialRef() */ +/************************************************************************/ + +void OGRGeoconceptLayer::SetSpatialRef( OGRSpatialReference *poSpatialRef ) + +{ + GCSysCoord* os, *ns; + OGRSpatialReference* poSRS= GetSpatialRef(); + GCExportFileH* hGXT; + GCExportFileMetadata* Meta; + /*----------------------------------------------------------------- + * Keep a copy of the OGRSpatialReference... + * Note: we have to take the reference count into account... + *----------------------------------------------------------------*/ + if( poSRS && poSRS->Dereference() == 0) delete poSRS; + + if( !poSpatialRef ) return; + + poSRS= poSpatialRef->Clone(); + if( !(hGXT= GetSubTypeGCHandle_GCIO(_gcFeature)) ) return; + if( !(Meta= GetGCMeta_GCIO(hGXT)) ) return; + os= GetMetaSysCoord_GCIO(Meta); + ns= OGRSpatialReference2SysCoord_GCSRS((OGRSpatialReferenceH)poSRS); + + if( os && ns && + GetSysCoordSystemID_GCSRS(os)!=-1 && + ( + GetSysCoordSystemID_GCSRS(os)!=GetSysCoordSystemID_GCSRS(ns) || + GetSysCoordTimeZone_GCSRS(os)!=GetSysCoordTimeZone_GCSRS(ns) + ) + ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Can't change SRS on Geoconcept layers.\n" ); + return; + } + + if( os ) DestroySysCoord_GCSRS(&os); + SetMetaSysCoord_GCIO(Meta, ns); + SetMetaSRS_GCIO(Meta, (OGRSpatialReferenceH)poSRS); + return; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptlayer.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptlayer.h new file mode 100644 index 000000000..72b2f2ea0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geoconcept/ogrgeoconceptlayer.h @@ -0,0 +1,82 @@ +/********************************************************************** + * $Id: ogrgeoconceptlayer.h + * + * Name: ogrgeoconceptlayer.h + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGeoconceptLayer class. + * Language: C++ + * + ********************************************************************** + * Copyright (c) 2007, Geoconcept and IGN + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + **********************************************************************/ + +#include "ogrsf_frmts.h" +#include "geoconcept.h" + +#ifndef _GEOCONCEPT_OGR_LAYER_H_INCLUDED_ +#define _GEOCONCEPT_OGR_LAYER_H_INCLUDED_ + +/**********************************************************************/ +/* OGCGeoconceptLayer Class */ +/**********************************************************************/ +class OGRGeoconceptLayer : public OGRLayer +{ + private: + OGRFeatureDefn *_poFeatureDefn; + + GCSubType *_gcFeature; + + public: + OGRGeoconceptLayer(); + ~OGRGeoconceptLayer(); + + OGRErr Open( GCSubType* Subclass ); + +// OGRGeometry* GetSpatialFilter( ); +// void SetSpatialFilter( OGRGeometry* poGeomIn ); +// void SetSpatialFilterRect( double dfMinX, double dfMinY, double dfMaxX, double dfMaxY ); +// OGRErr SetAttributeFilter( const char* pszQuery ); + void ResetReading(); + OGRFeature* GetNextFeature(); +// OGRErr SetNextByIndex( GIntBig nIndex ); + +// OGRFeature* GetFeature( GIntBig nFID ); +// OGRErr ISetFeature( OGRFeature* poFeature ); +// OGRErr DeleteFeature( GIntBig nFID ); + OGRErr ICreateFeature( OGRFeature* poFeature ); + OGRFeatureDefn* GetLayerDefn( ) { return _poFeatureDefn; } // FIXME + OGRSpatialReference* GetSpatialRef( ); + GIntBig GetFeatureCount( int bForce = TRUE ); + OGRErr GetExtent( OGREnvelope *psExtent, int bForce = TRUE ); + int TestCapability( const char* pszCap ); +// const char* GetInfo( const char* pszTag ); + OGRErr CreateField( OGRFieldDefn* poField, int bApproxOK = TRUE ); + OGRErr SyncToDisk( ); +// OGRStyleTable* GetStyleTable( ); +// void SetStyleTableDirectly( OGRStyleTable* poStyleTable ); +// void SetStyleTable( OGRStyleTable* poStyleTable ); +// const char* GetFIDColumn( ); +// const char* GetGeometryColumn( ); + + void SetSpatialRef( OGRSpatialReference *poSpatialRef ); +}; + +#endif /* _GEOCONCEPT_OGR_LAYER_H_INCLUDED_ */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/GNUmakefile new file mode 100644 index 000000000..9e0cd5dfb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/GNUmakefile @@ -0,0 +1,30 @@ +# $Id$ +# +# Makefile to build OGR GeoJSON driver +# +include ../../../GDALmake.opt + +ifeq ($(LIBJSONC_SETTING),internal) +SUBDIRS-yes := libjson +endif + +OBJ = \ + ogrgeojsondriver.o \ + ogrgeojsondatasource.o \ + ogrgeojsonlayer.o \ + ogrgeojsonwritelayer.o \ + ogrgeojsonutils.o \ + ogrgeojsonreader.o \ + ogrgeojsonwriter.o \ + ogresrijsonreader.o \ + ogrtopojsonreader.o + +CPPFLAGS := $(JSON_INCLUDE) -I. -I.. -I../.. $(CPPFLAGS) + +default: $(foreach d,$(SUBDIRS-yes),$(d)-target) $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: $(foreach d,$(SUBDIRS-yes),$(d)-clean) + rm -f *.o $(O_OBJ) + rm -f *~ + +$(O_OBJ): ogr_geojson.h ogrgeojsonreader.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/drv_geojson.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/drv_geojson.html new file mode 100644 index 000000000..e7fbb8f20 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/drv_geojson.html @@ -0,0 +1,207 @@ + + +GeoJSON + + + + +

    GeoJSON

    + +

    This driver implements read/write support for access to features encoded in +GeoJSON format. The GeoJSON is a dialect based on the +JavaScript Object Notation (JSON). The JSON is a lightweight +plain text format for data interchange and GeoJSON is nothing other than its specialization for geographic content.

    + +

    GeoJSON is supported as output format of a number of services : +FeatureServer, +GeoServer , +CartoWeb, etc...

    + +

    The OGR GeoJSON driver translates a GeoJSON encoded data to objects of OGR Simple Features model: +Datasource, Layer, Feature, Geometry. +The implementation is based on GeoJSON Specification, v1.0.

    + +

    Starting with OGR 1.8.0, the GeoJSON driver can read the JSON output of Feature Service request following the +GeoServices REST Specification, like +implemented by ArcGIS Server REST API. +And starting with OGR 2.0, the GeoJSON driver can scroll through such result sets that +are spread over multiple pages (for ArcGIS servers >= 10.3). This is automatically enabled +if URL does not contain an explicit resultOffset parameter. If it contains +this parameter and scrolling is still desired, the FEATURE_SERVER_PAGING open option must be set to YES. +The page size can be explicitly set with the resultRecordCount parameter (but +is subject to a server limit). If it is not set, OGR will set it to the maximum +value allowed by the server.

    + +

    Starting with OGR 1.11, the GeoJSON driver can read the TopoJSON format

    + +

    Datasource

    + +

    The OGR GeoJSON driver accepts three types of sources of data: +

      +
    • Uniform Resource Locator (URL) - a Web address to + perform HTTP request
    • +
    • Plain text file with GeoJSON data - identified from the file extension .geojson or .json
    • +
    • Text passed directly and encoded in GeoJSON
    • +
    +

    Layer

    + +

    A GeoJSON datasource is translated to single OGRLayer object with pre-defined name OGRGeoJSON: +

    +ogrinfo -ro http://featureserver/data/.geojson OGRGeoJSON
    +
    +It's also valid to assume that OGRDataSource::GetLayerCount() for GeoJSON datasource always returns 1. +

    + +

    Accessing Web Service as a datasource (ie. FeatureServer), each request will produce new layer. +This behavior conforms to stateless nature of HTTP transaction and is similar to how Web browsers operate: +single request == single page.

    + +

    If a top-level member of GeoJSON data is of any other type than FeatureCollection, the driver will +produce a layer with only one feature. Otherwise, a layer will consists of a set of features.

    + +

    Feature

    + +

    The OGR GeoJSON driver maps each object of following types to new OGRFeature object: +Point, LineString, Polygon, GeometryCollection, Feature.

    + +

    According to the GeoJSON Specification, only the Feature object must have a member with +name properties. Each and every member of properties is translated to OGR object of type of +OGRField and added to corresponding OGRFeature object.

    + +

    The GeoJSON Specification does not require all Feature objects in a collection must +have the same schema of properties. If Feature objects in a set defined by FeatureCollection +object have different schema of properties, then resulting schema of fields in OGRFeatureDefn is generated as +union of all Feature properties.

    + +

    Schema detection will recognized fields of type String, Integer, Real, StringList, IntegerList and RealList. +Starting with GDAL 2.0, Integer(Boolean), Date, Time and DateTime fields are also recognized.

    + +

    It is possible to tell the driver to not to process attributes by setting environment variable +ATTRIBUTES_SKIP=YES. Default behavior is to preserve all attributes (as an union, see previous paragraph), +what is equal to setting ATTRIBUTES_SKIP=NO.

    + +

    Geometry

    + +

    Similarly to the issue with mixed-properties features, the GeoJSON Specification draft does not require +all Feature objects in a collection must have geometry of the same type. Fortunately, OGR objects model does +allow to have geometries of different types in single layer - a heterogeneous layer. By default, the GeoJSON driver +preserves type of geometries.

    + +

    However, sometimes there is a need to generate homogeneous layer from a set of heterogeneous features. +For this purpose, it's possible to tell the driver to wrap all geometries with OGRGeometryCollection type as a common denominator. +This behavior may be controlled by setting environment variable GEOMETRY_AS_COLLECTION=YES (default is NO).

    + +

    Environment variables

    + +
      +
    • GEOMETRY_AS_COLLECTION - used to control translation of geometries: YES - wrap geometries with OGRGeometryCollection type
    • +
    • ATTRIBUTES_SKIP - controls translation of attributes: YES - skip all attributes
    • +
    + +

    Open options

    + +

    (GDAL >= 2.0)

    + +
      +
    • FLATTEN_NESTED_ATTRIBUTES = YES/NO : Whether to +recursively explore nested objects and produce flatten OGR attributes. Defaults to NO.
    • +
    • NESTED_ATTRIBUTE_SEPARATOR = character : Separator between components +of nested attributes. Defaults to '_'
    • +
    • FEATURE_SERVER_PAGING = YES/NO: Whether to automatically scroll through +results with a ArcGIS Feature Service endpoint.
    • +
    + +

    To explain FLATTEN_NESTED_ATTRIBUTES, consider the following GeoJSON fragment :

    + +
    +{
    +  "type": "FeatureCollection",
    +  "features" :
    +  [
    +    {
    +      "type": "Feature",
    +      "geometry": {
    +        "type": "Point",
    +        "coordinates": [ 2, 49 ]
    +      }, 
    +      "properties": {
    +        "a_property": "foo", 
    +        "some_object": {
    +          "a_property": 1, 
    +          "another_property": 2 
    +        }
    +      }
    +    }
    +  ]
    +}
    +
    + +

    "ogrinfo test.json -al -oo FLATTEN_NESTED_ATTRIBUTES=yes" reports :

    + +
    +OGRFeature(OGRGeoJSON):0
    +  a_property (String) = foo
    +  some_object_a_property (Integer) = 1
    +  some_object_another_property (Integer) = 2
    +  POINT (2 49)
    +
    + +

    Layer creation option

    + +
      +
    • WRITE_BBOX = YES/NO : (OGR >= 1.9.0) Set to YES to write a bbox property with the bounding box of the geometries at the feature and feature +collection level. Defaults to NO.
    • +
    • COORDINATE_PRECISION = int_number : (OGR >= 1.9.0) Maximum number of figures after decimal separator to write in coordinates. +Default to 15. "Smart" truncation will occur to remove trailing zeros.
    • +
    + +

    VSI Virtual File System API support

    + +(Some features below might require OGR >= 1.9.0)

    + +The driver supports reading and writing to files managed by VSI Virtual File System API, which include +"regular" files, as well as files in the /vsizip/ (read-write) , /vsigzip/ (read-write) , /vsicurl/ (read-only) domains.

    + +Writing to /dev/stdout or /vsistdout/ is also supported.

    + +

    Example

    + +

    How to dump content of .geojson file: +

    +ogrinfo -ro point.geojson
    +
    +

    + +

    How to query features from remote service with filtering by attribute: +

    +ogrinfo -ro http://featureserver/cities/.geojson OGRGeoJSON -where "name=Warsaw"
    +
    +

    + +

    How to translate number of features queried from FeatureServer to ESRI Shapefile: +

    +ogr2ogr -f "ESRI Shapefile" cities.shp http://featureserver/cities/.geojson OGRGeoJSON
    +
    +

    + +

    Read the result of a FeatureService request against a GeoServices REST server: +

    +ogrinfo -ro -al "http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Hydrography/Watershed173811/FeatureServer/0/query?where=objectid+%3D+objectid&outfields=*&f=json"
    +
    +

    + +

    See Also

    + +

    +

    +

    + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/AUTHORS b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/AUTHORS new file mode 100644 index 000000000..b389989c4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/AUTHORS @@ -0,0 +1,5 @@ +Michael Clark +Jehiah Czebotar +Eric Haszlakiewicz +C. Watford (christopher.watford@gmail.com) + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/COPYING b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/COPYING new file mode 100644 index 000000000..740d1258d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/COPYING @@ -0,0 +1,42 @@ + +Copyright (c) 2009-2012 Eric Haszlakiewicz + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +---------------------------------------------------------------- + +Copyright (c) 2004, 2005 Metaparadigm Pte Ltd + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/ChangeLog b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/ChangeLog new file mode 100644 index 000000000..4e74c907b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/ChangeLog @@ -0,0 +1,175 @@ + +0.11 + + * IMPORTANT: the name of the library has changed to libjson-c.so and + the header files are now in include/json-c. + The pkgconfig name has also changed from json to json-c. + You should change your build to use appropriate -I and -l options. + A compatibility shim is in place so builds using the old name will + continue to work, but that will be removed in the next release. + * Maximum recursion depth is now a runtime option. + json_tokener_new() is provided for compatibility. + json_tokener_new_ex(depth) + * Include json_object_iterator.h in the installed headers. + * Add support for building on Android. + * Rewrite json_object_object_add to replace just the value if the key already exists so keys remain valid. + * Make it safe to delete keys while iterating with the json_object_object_foreach macro. + * Add a json_set_serializer() function to allow the string output of a json_object to be customized. + * Make float parsing locale independent. + * Add a json_tokener_set_flags() function and a JSON_TOKENER_STRICT flag. + * Enable -Werror when building. + * speed improvements to parsing 64-bit integers on systems with working sscanf + * Add a json_object_object_length function. + * Fix a bug (buffer overrun) when expanding arrays to more than 64 entries. + +0.10 + + * Add a json_object_to_json_string_ext() function to allow output to be + formatted in a more human readable form. + * Add json_object_object_get_ex(), a NULL-safe get object method, to be able + to distinguish between a key not present and the value being NULL. + * Add an alternative iterator implementation, see json_object_iterator.h + * Make json_object_iter public to enable external use of the + json_object_object_foreachC macro. + * Add a printbuf_memset() function to provide an effecient way to set and + append things like whitespace indentation. + * Adjust json_object_is_type and json_object_get_type so they return + json_type_null for NULL objects and handle NULL passed to + json_objct_object_get(). + * Rename boolean type to json_bool. + * Fix various compile issues for Visual Studio and MinGW. + * Allow json_tokener_parse_ex() to be re-used to parse multiple object. + Also, fix some parsing issues with capitalized hexadecimal numbers and + number in E notation. + * Add json_tokener_get_error() and json_tokener_error_desc() to better + encapsulate the process of retrieving errors while parsing. + * Various improvements to the documentation of many functions. + * Add new json_object_array_sort() function. + * Fix a bug in json_object_get_int(), which would incorrectly return 0 + when called on a string type object. + Eric Haszlakiewicz + * Add a json_type_to_name() function. + Eric Haszlakiewicz + * Add a json_tokener_parse_verbose() function. + Jehiah Czebotar + * Improve support for null bytes within JSON strings. + Jehiah Czebotar + * Fix file descriptor leak if memory allocation fails in json_util + Zachary Blair, zack_blair at hotmail dot com + * Add int64 support. Two new functions json_object_net_int64 and + json_object_get_int64. Binary compatibility preserved. + Eric Haszlakiewicz, EHASZLA at transunion com + Rui Miguel Silva Seabra, rms at 1407 dot org + * Fix subtle bug in linkhash where lookup could hang after all slots + were filled then successively freed. + Spotted by Jean-Marc Naud, j dash m at newtraxtech dot com + * Make json_object_from_file take const char *filename + Spotted by Vikram Raj V, vsagar at attinteractive dot com + * Add handling of surrogate pairs (json_tokener.c, test4.c, Makefile.am) + Brent Miller, bdmiller at yahoo dash inc dot com + * Correction to comment describing printbuf_memappend in printbuf.h + Brent Miller, bdmiller at yahoo dash inc dot com + +0.9 + * Add README.html README-WIN32.html config.h.win32 to Makefile.am + Michael Clark, + * Add const qualifier to the json_tokener_parse functions + Eric Haszlakiewicz, EHASZLA at transunion dot com + * Rename min and max so we can never clash with C or C++ std library + Ian Atha, thatha at yahoo dash inc dot com + * Fix any noticeable spelling or grammar errors. + * Make sure every va_start has a va_end. + * Check all pointers for validity. + Erik Hovland, erik at hovland dot org + * Fix json_object_get_boolean to return false for empty string + Spotted by Vitaly Kruglikov, Vitaly dot Kruglikov at palm dot com + * optimizations to json_tokener_parse_ex(), printbuf_memappend() + Brent Miller, bdmiller at yahoo dash inc dot com + * Disable REFCOUNT_DEBUG by default in json_object.c + * Don't use this as a variable, so we can compile with a C++ compiler + * Add casts from void* to type of assignment when using malloc + * Add #ifdef __cplusplus guards to all of the headers + * Add typedefs for json_object, json_tokener, array_list, printbuf, lh_table + Michael Clark, + * Null pointer dereference fix. Fix json_object_get_boolean strlen test + to not return TRUE for zero length string. Remove redundant includes. + Erik Hovland, erik at hovland dot org + * Fixed warning reported by adding -Wstrict-prototypes + -Wold-style-definition to the compilatin flags. + Dotan Barak, dotanba at gmail dot com + * Add const correctness to public interfaces + Gerard Krol, g dot c dot krol at student dot tudelft dot nl + +0.8 + * Add va_end for every va_start + Dotan Barak, dotanba at gmail dot com + * Add macros to enable compiling out debug code + Geoffrey Young, geoff at modperlcookbook dot org + * Fix bug with use of capital E in numbers with exponents + Mateusz Loskot, mateusz at loskot dot net + * Add stddef.h include + * Patch allows for json-c compile with -Werror and not fail due to + -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations + Geoffrey Young, geoff at modperlcookbook dot org + +0.7 + * Add escaping of backslash to json output + * Add escaping of foward slash on tokenizing and output + * Changes to internal tokenizer from using recursion to + using a depth state structure to allow incremental parsing + +0.6 + * Fix bug in escaping of control characters + Johan Björklund, johbjo09 at kth dot se + * Remove include "config.h" from headers (should only + be included from .c files) + Michael Clark + +0.5 + * Make headers C++ compatible by change *this to *obj + * Add ifdef C++ extern "C" to headers + * Use simpler definition of min and max in bits.h + Larry Lansing, llansing at fuzzynerd dot com + + * Remove automake 1.6 requirement + * Move autogen commands into autogen.sh. Update README + * Remove error pointer special case for Windows + * Change license from LGPL to MIT + Michael Clark + +0.4 + * Fix additional error case in object parsing + * Add back sign reversal in nested object parse as error pointer + value is negative, while error value is positive. + Michael Clark + +0.3 + * fix pointer arithmetic bug for error pointer check in is_error() macro + * fix type passed to printbuf_memappend in json_tokener + * update autotools bootstrap instructions in README + Michael Clark + +0.2 + * printbuf.c - C. Watford (christopher.watford@gmail.com) + Added a Win32/Win64 compliant implementation of vasprintf + * debug.c - C. Watford (christopher.watford@gmail.com) + Removed usage of vsyslog on Win32/Win64 systems, needs to be handled + by a configure script + * json_object.c - C. Watford (christopher.watford@gmail.com) + Added scope operator to wrap usage of json_object_object_foreach, this + needs to be rethought to be more ANSI C friendly + * json_object.h - C. Watford (christopher.watford@gmail.com) + Added Microsoft C friendly version of json_object_object_foreach + * json_tokener.c - C. Watford (christopher.watford@gmail.com) + Added a Win32/Win64 compliant implementation of strndup + * json_util.c - C. Watford (christopher.watford@gmail.com) + Added cast and mask to suffice size_t v. unsigned int conversion + correctness + * json_tokener.c - sign reversal issue on error info for nested object parse + spotted by Johan Björklund (johbjo09 at kth.se) + * json_object.c - escape " in json_escape_str + * Change to automake and libtool to build shared and static library + Michael Clark + +0.1 + * initial release diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/GNUmakefile new file mode 100644 index 000000000..8106b61cd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/GNUmakefile @@ -0,0 +1,29 @@ +# $Id$ +# +# Makefile building jcon-c library (http://oss.metaparadigm.com/json-c/) +# +include ../../../../GDALmake.opt + +OBJ = \ + arraylist.o \ + debug.o \ + json_object.o \ + json_tokener.o \ + json_util.o \ + linkhash.o \ + printbuf.o \ + json_object_iterator.o \ + json_c_version.o + +O_OBJ = $(foreach file,$(OBJ),../../o/$(file)) + +CPPFLAGS := $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +../../o/%.$(OBJ_EXT): %.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< + +clean: + rm -f *.o $(O_OBJ) + rm -f *~ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/README b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/README new file mode 100644 index 000000000..beeba53d5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/README @@ -0,0 +1,20 @@ +Building on Unix with gcc and autotools + +If checking out from CVS: + + sh autogen.sh + +Then configure, make, make install + + +Test programs + +To build the test programs run 'make check' + + +Linking to libjson + +If your system has pkgconfig then you can just add this to your makefile + +CFLAGS += $(shell pkg-config --cflags json) +LDFLAGS += $(shell pkg-config --libs json) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/README.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/README.html new file mode 100644 index 000000000..7c2f9f469 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/README.html @@ -0,0 +1,34 @@ + + + + JSON-C - A JSON implementation in C + + + +

    JSON-C - A JSON implementation in C

    + +

    Overview

    +

    JSON-C implements a reference counting object model that allows you to easily + construct JSON objects in C, output them as JSON formatted strings and parse + JSON formatted strings back into the C representation of JSON objects.

    + +

    Building

    +

    To setup JSON-C to build on your system please run configure and make.

    +

    If you are on Win32 and are not using the VS project file, be sure + to rename config.h.win32 to config.h before building.

    + +

    Documentation

    +

    Doxygen generated documentation exists here + and Win32 specific notes can be found here.

    + +

    GIT Reposository

    +

    git clone https://github.com/json-c/json-c.git

    + +

    Mailing List

    + Send email to json-c <at> googlegroups <dot> com

    + +

    License

    +

    This program is free software; you can redistribute it and/or modify it under the terms of the MIT License..

    +
    + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/arraylist.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/arraylist.c new file mode 100644 index 000000000..e7236ab12 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/arraylist.c @@ -0,0 +1,97 @@ +/* + * $Id: arraylist.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#ifdef STDC_HEADERS +# include +# include +#endif /* STDC_HEADERS */ + +#include "bits.h" +#include "arraylist.h" + +struct array_list* +array_list_new(array_list_free_fn *free_fn) +{ + struct array_list *arr; + + arr = (struct array_list*)calloc(1, sizeof(struct array_list)); + if(!arr) return NULL; + arr->size = ARRAY_LIST_DEFAULT_SIZE; + arr->length = 0; + arr->free_fn = free_fn; + if(!(arr->array = (void**)calloc(sizeof(void*), arr->size))) { + free(arr); + return NULL; + } + return arr; +} + +extern void +array_list_free(struct array_list *arr) +{ + int i; + for(i = 0; i < arr->length; i++) + if(arr->array[i]) arr->free_fn(arr->array[i]); + free(arr->array); + free(arr); +} + +void* +array_list_get_idx(struct array_list *arr, int i) +{ + if(i >= arr->length) return NULL; + return arr->array[i]; +} + +static int array_list_expand_internal(struct array_list *arr, int max) +{ + void *t; + int new_size; + + if(max < arr->size) return 0; + new_size = json_max(arr->size << 1, max); + if(!(t = realloc(arr->array, new_size*sizeof(void*)))) return -1; + arr->array = (void**)t; + (void)memset(arr->array + arr->size, 0, (new_size-arr->size)*sizeof(void*)); + arr->size = new_size; + return 0; +} + +int +array_list_put_idx(struct array_list *arr, int idx, void *data) +{ + if(array_list_expand_internal(arr, idx+1)) return -1; + if(arr->array[idx]) arr->free_fn(arr->array[idx]); + arr->array[idx] = data; + if(arr->length <= idx) arr->length = idx + 1; + return 0; +} + +int +array_list_add(struct array_list *arr, void *data) +{ + return array_list_put_idx(arr, arr->length, data); +} + +void +array_list_sort(struct array_list *arr, int(*sort_fn)(const void *, const void *)) +{ + qsort(arr->array, arr->length, sizeof(arr->array[0]), + (int (*)(const void *, const void *))sort_fn); +} + +int +array_list_length(struct array_list *arr) +{ + return arr->length; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/arraylist.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/arraylist.h new file mode 100644 index 000000000..4f3113c09 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/arraylist.h @@ -0,0 +1,56 @@ +/* + * $Id: arraylist.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _arraylist_h_ +#define _arraylist_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARRAY_LIST_DEFAULT_SIZE 32 + +typedef void (array_list_free_fn) (void *data); + +struct array_list +{ + void **array; + int length; + int size; + array_list_free_fn *free_fn; +}; + +extern struct array_list* +array_list_new(array_list_free_fn *free_fn); + +extern void +array_list_free(struct array_list *al); + +extern void* +array_list_get_idx(struct array_list *al, int i); + +extern int +array_list_put_idx(struct array_list *al, int i, void *data); + +extern int +array_list_add(struct array_list *al, void *data); + +extern int +array_list_length(struct array_list *al); + +extern void +array_list_sort(struct array_list *arr, int(*compar)(const void *, const void *)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/bits.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/bits.h new file mode 100644 index 000000000..c8cbbc820 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/bits.h @@ -0,0 +1,28 @@ +/* + * $Id: bits.h,v 1.10 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _bits_h_ +#define _bits_h_ + +#ifndef json_min +#define json_min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef json_max +#define json_max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#define hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) +#define error_ptr(error) ((void*)error) +#define error_description(error) (json_tokener_errors[error]) +#define is_error(ptr) (ptr == NULL) + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/config.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/config.h new file mode 100644 index 000000000..190931bab --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/config.h @@ -0,0 +1,126 @@ +/* + * $Id: config.h.win32,v 1.2 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/* config.h.win32 Generated by configure. */ + +#define PACKAGE_STRING "JSON C Library 0.2" +#define PACKAGE_BUGREPORT "michael@metaparadigm.com" +#define PACKAGE_NAME "JSON C Library" +#define PACKAGE_TARNAME "json-c" +#define PACKAGE_VERSION "0.2" + +#include "symbol_renames.h" + +/* config.h.in. Generated from configure.ac by autoheader. */ + +#ifndef __GNUC__ +#define __attribute__(x) /* DO NOTHING */ +#endif + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `open' function. */ +#undef HAVE_OPEN + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#define HAVE_REALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#ifndef HAVE_STRERROR +#define HAVE_STRERROR 1 +#endif + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#ifdef _WIN32_WCE +#undef HAVE_SYS_STAT_H +#else +#define HAVE_SYS_STAT_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#ifdef _WIN32_WCE +#undef HAVE_SYS_TYPES_H +#else +#define HAVE_SYS_TYPES_H 1 +#endif + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vprintf' function. */ +#ifdef _MSC_VER +#undef HAVE_VPRINTF +#endif + +/* Define to 1 if you have the `vasprintf' function. */ +#define HAVE_VASPRINTF 1 +#ifdef _MSC_VER +#undef HAVE_VASPRINTF +#endif + +/* Define to 1 if you have the `vsyslog' function. */ +#undef HAVE_VSYSLOG + +/* Define to 1 if you have the `strncasecmp' function. */ +#ifndef HAVE_STRNCASECMP +#define HAVE_STRNCASECMP 1 +#endif +#if defined(_MSC_VER) && !defined(strncasecmp) + /* MSC has the version as _strnicmp */ +#define strncasecmp _strnicmp +#endif + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +#include diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/debug.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/debug.c new file mode 100644 index 000000000..7199b486f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/debug.c @@ -0,0 +1,94 @@ +/* + * $Id: debug.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#if HAVE_SYSLOG_H +# include +#endif /* HAVE_SYSLOG_H */ + +#if HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#include "debug.h" + +static int _syslog = 0; +static int _debug = 0; + +void mc_set_debug(int debug) { _debug = debug; } +int mc_get_debug(void) { return _debug; } + +extern void mc_set_syslog(int syslog) +{ + _syslog = syslog; +} + +void mc_abort(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_ERR, msg, ap); + } else +#endif + vprintf(msg, ap); + va_end(ap); + exit(1); +} + + +void mc_debug(const char *msg, ...) +{ + va_list ap; + if(_debug) { + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_DEBUG, msg, ap); + } else +#endif + vprintf(msg, ap); + va_end(ap); + } +} + +void mc_error(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_ERR, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} + +void mc_info(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_INFO, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/debug.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/debug.h new file mode 100644 index 000000000..9a5957418 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/debug.h @@ -0,0 +1,84 @@ +/* + * $Id: debug.h,v 1.5 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "symbol_renames.h" + +extern void mc_set_debug(int debug); +extern int mc_get_debug(void); + +extern void mc_set_syslog(int syslog); +extern void mc_abort(const char *msg, ...); +extern void mc_debug(const char *msg, ...); +extern void mc_error(const char *msg, ...); +extern void mc_info(const char *msg, ...); + +#ifndef __STRING +#define __STRING(x) #x +#endif + +#ifndef PARSER_BROKEN_FIXED + +#define JASSERT(cond) do {} while(0) + +#else + +#define JASSERT(cond) do { \ + if (!(cond)) { \ + mc_error("cjson assert failure %s:%d : cond \"" __STRING(cond) "failed\n", __FILE__, __LINE__); \ + *(int *)0 = 1;\ + abort(); \ + }\ + } while(0) + +#endif + +#ifdef MC_MAINTAINER_MODE +#define MC_SET_DEBUG(x) mc_set_debug(x) +#define MC_GET_DEBUG() mc_get_debug() +#define MC_SET_SYSLOG(x) mc_set_syslog(x) +#define MC_ABORT(x, ...) mc_abort(x, ##__VA_ARGS__) +#define MC_DEBUG(x, ...) mc_debug(x, ##__VA_ARGS__) +#define MC_ERROR(x, ...) mc_error(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) mc_info(x, ##__VA_ARGS__) +#else +#define MC_SET_DEBUG(x) if (0) mc_set_debug(x) +#define MC_GET_DEBUG() (0) +#define MC_SET_SYSLOG(x) if (0) mc_set_syslog(x) +#if defined(_MSC_VER) && _MSC_VER <= 1310 +/* VC++ 7.1 and earlier don't like macros with ... */ +#define MC_ABORT +#define MC_DEBUG +#define MC_ERROR +#define MC_INFO +#else +#define MC_ABORT(x, ...) if (0) mc_abort(x, ##__VA_ARGS__) +#define MC_DEBUG(x, ...) if (0) mc_debug(x, ##__VA_ARGS__) +#define MC_ERROR(x, ...) if (0) mc_error(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) if (0) mc_info(x, ##__VA_ARGS__) +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/dump_symbols.sh b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/dump_symbols.sh new file mode 100644 index 000000000..a39a0d4e1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/dump_symbols.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +symbol_list="" +for filename in arraylist.o debug.o json_c_version.o json_object.o json_tokener.o json_object_iterator.o json_util.o linkhash.o printbuf.o ; do +symbol_list="$symbol_list $(objdump -t ../../o/$filename | grep text | awk '{print $6}' | grep -v .text)" +symbol_list="$symbol_list $(objdump -t ../../o/$filename | grep .data.rel.local | awk '{print $6}' | grep -v .data.rel.local)" +done + +echo "/* This is a generated file by dump_symbols.h. *DO NOT EDIT MANUALLY !* */" +echo "#ifndef symbol_renames" +echo "#define symbol_renames" +echo "" +for symbol in $symbol_list +do + echo "#define $symbol gdal_$symbol" +done +echo "" +echo "#endif /* symbol_renames */" + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json.h new file mode 100644 index 000000000..4339b20e9 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json.h @@ -0,0 +1,34 @@ +/* + * $Id: json.h,v 1.6 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_h_ +#define _json_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bits.h" +#include "debug.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_util.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_object_iterator.h" +#include "json_c_version.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_c_version.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_c_version.c new file mode 100644 index 000000000..13eb18855 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_c_version.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ +#include "config.h" + +#include "json_c_version.h" + +const char *json_c_version(void) +{ + return JSON_C_VERSION; +} + +int json_c_version_num(void) +{ + return JSON_C_VERSION_NUM; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_c_version.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_c_version.h new file mode 100644 index 000000000..ff20f950f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_c_version.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2012 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ + +#ifndef _json_c_version_h_ +#define _json_c_version_h_ + +#define JSON_C_MAJOR_VERSION 0 +#define JSON_C_MINOR_VERSION 11 +#define JSON_C_MICRO_VERSION 0 +#define JSON_C_VERSION_NUM ((JSON_C_MAJOR_VERSION << 16) | \ + (JSON_C_MINOR_VERSION << 8) | \ + JSON_C_MICRO_VERSION) +#define JSON_C_VERSION "0.11" + +const char *json_c_version(void); /* Returns JSON_C_VERSION */ +int json_c_version_num(void); /* Returns JSON_C_VERSION_NUM */ + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_config.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_config.h new file mode 100644 index 000000000..965ff1c37 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_config.h @@ -0,0 +1,4 @@ +/* json_config.h. Generated from json_config.h.in by configure. */ + +/* Define to 1 if you have the header file. */ +#define JSON_C_HAVE_INTTYPES_H 1 diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_inttypes.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_inttypes.h new file mode 100644 index 000000000..9de8d246d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_inttypes.h @@ -0,0 +1,28 @@ + +#ifndef _json_inttypes_h_ +#define _json_inttypes_h_ + +#include "json_config.h" + +#if defined(_MSC_VER) && _MSC_VER <= 1700 + +/* Anything less than Visual Studio C++ 10 is missing stdint.h and inttypes.h */ +typedef __int32 int32_t; +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX ((int32_t)_I32_MAX) +typedef __int64 int64_t; +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX ((int64_t)_I64_MAX) +#define PRId64 "I64d" +#define SCNd64 "I64d" + +#else + +#ifdef JSON_C_HAVE_INTTYPES_H +#include +#endif +/* inttypes.h includes stdint.h */ + +#endif + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object.c new file mode 100644 index 000000000..867754eba --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object.c @@ -0,0 +1,777 @@ +/* + * $Id: json_object.c,v 1.17 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "cpl_conv.h" + +#include "config.h" + +#include +#include +#include +#include + +#include "debug.h" +#include "printbuf.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_object_private.h" +#include "json_util.h" + +#if !defined(HAVE_STRDUP) && defined(_MSC_VER) + /* MSC has the version as _strdup */ +# define strdup _strdup +#elif !defined(HAVE_STRDUP) +# error You do not have strdup on your system. +#endif /* HAVE_STRDUP */ + +// Don't define this. It's not thread-safe. +/* #define REFCOUNT_DEBUG 1 */ + +const char *json_number_chars = "0123456789.+-eE"; +const char *json_hex_chars = "0123456789abcdefABCDEF"; + +static void json_object_generic_delete(struct json_object* jso); +static struct json_object* json_object_new(enum json_type o_type); + +static json_object_to_json_string_fn json_object_object_to_json_string; +static json_object_to_json_string_fn json_object_boolean_to_json_string; +static json_object_to_json_string_fn json_object_int_to_json_string; +static json_object_to_json_string_fn json_object_double_to_json_string; +static json_object_to_json_string_fn json_object_string_to_json_string; +static json_object_to_json_string_fn json_object_array_to_json_string; + + +/* ref count debugging */ + +#ifdef REFCOUNT_DEBUG + +static struct lh_table *json_object_table; + +static void json_object_init(void) __attribute__ ((constructor)); +static void json_object_init(void) { + MC_DEBUG("json_object_init: creating object table\n"); + json_object_table = lh_kptr_table_new(128, "json_object_table", NULL); +} + +static void json_object_fini(void) __attribute__ ((destructor)); +static void json_object_fini(void) { + struct lh_entry *ent; + if(MC_GET_DEBUG()) { + if (json_object_table->count) { + MC_DEBUG("json_object_fini: %d referenced objects at exit\n", + json_object_table->count); + lh_foreach(json_object_table, ent) { + struct json_object* obj = (struct json_object*)ent->v; + MC_DEBUG("\t%s:%p\n", json_type_to_name(obj->o_type), obj); + } + } + } + MC_DEBUG("json_object_fini: freeing object table\n"); + lh_table_free(json_object_table); +} +#endif /* REFCOUNT_DEBUG */ + + +/* string escaping */ + +static int json_escape_str(struct printbuf *pb, char *str, int len) +{ + int pos = 0, start_offset = 0; + unsigned char c; + while (len--) { + c = str[pos]; + switch(c) { + case '\b': + case '\n': + case '\r': + case '\t': + case '\f': + case '"': + case '\\': + case '/': + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + if(c == '\b') printbuf_memappend(pb, "\\b", 2); + else if(c == '\n') printbuf_memappend(pb, "\\n", 2); + else if(c == '\r') printbuf_memappend(pb, "\\r", 2); + else if(c == '\t') printbuf_memappend(pb, "\\t", 2); + else if(c == '\f') printbuf_memappend(pb, "\\f", 2); + else if(c == '"') printbuf_memappend(pb, "\\\"", 2); + else if(c == '\\') printbuf_memappend(pb, "\\\\", 2); + else if(c == '/') printbuf_memappend(pb, "\\/", 2); + start_offset = ++pos; + break; + default: + if(c < ' ') { + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + sprintbuf(pb, "\\u00%c%c", + json_hex_chars[c >> 4], + json_hex_chars[c & 0xf]); + start_offset = ++pos; + } else pos++; + } + } + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + return 0; +} + + +/* reference counting */ + +extern struct json_object* json_object_get(struct json_object *jso) +{ + if(jso) { + jso->_ref_count++; + } + return jso; +} + +int json_object_put(struct json_object *jso) +{ + if(jso) + { + jso->_ref_count--; + if(!jso->_ref_count) + { + if (jso->_user_delete) + jso->_user_delete(jso, jso->_userdata); + jso->_delete(jso); + return 1; + } + } + return 0; +} + + +/* generic object construction and destruction parts */ + +static void json_object_generic_delete(struct json_object* jso) +{ +#ifdef REFCOUNT_DEBUG + MC_DEBUG("json_object_delete_%s: %p\n", + json_type_to_name(jso->o_type), jso); + lh_table_delete(json_object_table, jso); +#endif /* REFCOUNT_DEBUG */ + printbuf_free(jso->_pb); + free(jso); +} + +static struct json_object* json_object_new(enum json_type o_type) +{ + struct json_object *jso; + + jso = (struct json_object*)calloc(sizeof(struct json_object), 1); + if(!jso) return NULL; + jso->o_type = o_type; + jso->_ref_count = 1; + jso->_delete = &json_object_generic_delete; +#ifdef REFCOUNT_DEBUG + lh_table_insert(json_object_table, jso, jso); + MC_DEBUG("json_object_new_%s: %p\n", json_type_to_name(jso->o_type), jso); +#endif /* REFCOUNT_DEBUG */ + return jso; +} + + +/* type checking functions */ + +int json_object_is_type(struct json_object *jso, enum json_type type) +{ + if (!jso) + return (type == json_type_null); + return (jso->o_type == type); +} + +enum json_type json_object_get_type(struct json_object *jso) +{ + if (!jso) + return json_type_null; + return jso->o_type; +} + +/* set a custom conversion to string */ + +void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn to_string_func, + void *userdata, + json_object_delete_fn *user_delete) +{ + // First, clean up any previously existing user info + if (jso->_user_delete) + { + jso->_user_delete(jso, jso->_userdata); + } + jso->_userdata = NULL; + jso->_user_delete = NULL; + + if (to_string_func == NULL) + { + // Reset to the standard serialization function + switch(jso->o_type) + { + case json_type_null: + jso->_to_json_string = NULL; + break; + case json_type_boolean: + jso->_to_json_string = &json_object_boolean_to_json_string; + break; + case json_type_double: + jso->_to_json_string = &json_object_double_to_json_string; + break; + case json_type_int: + jso->_to_json_string = &json_object_int_to_json_string; + break; + case json_type_object: + jso->_to_json_string = &json_object_object_to_json_string; + break; + case json_type_array: + jso->_to_json_string = &json_object_array_to_json_string; + break; + case json_type_string: + jso->_to_json_string = &json_object_string_to_json_string; + break; + } + return; + } + + jso->_to_json_string = to_string_func; + jso->_userdata = userdata; + jso->_user_delete = user_delete; +} + + +/* extended conversion to string */ + +const char* json_object_to_json_string_ext(struct json_object *jso, int flags) +{ + if (!jso) + return "null"; + + if ((!jso->_pb) && !(jso->_pb = printbuf_new())) + return NULL; + + printbuf_reset(jso->_pb); + + if(jso->_to_json_string(jso, jso->_pb, 0, flags) < 0) + return NULL; + + return jso->_pb->buf; +} + +/* backwards-compatible conversion to string */ + +const char* json_object_to_json_string(struct json_object *jso) +{ + return json_object_to_json_string_ext(jso, JSON_C_TO_STRING_SPACED); +} + +static void indent(struct printbuf *pb, int level, int flags) +{ + if (flags & JSON_C_TO_STRING_PRETTY) + { + printbuf_memset(pb, -1, ' ', level * 2); + } +} + +/* json_object_object */ + +static int json_object_object_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + int had_children = 0; + struct json_object_iter iter; + + sprintbuf(pb, "{" /*}*/); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + json_object_object_foreachC(jso, iter) + { + if (had_children) + { + sprintbuf(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED) + sprintbuf(pb, " "); + indent(pb, level+1, flags); + sprintbuf(pb, "\""); + json_escape_str(pb, iter.key, strlen(iter.key)); + if (flags & JSON_C_TO_STRING_SPACED) + sprintbuf(pb, "\": "); + else + sprintbuf(pb, "\":"); + if(iter.val == NULL) + sprintbuf(pb, "null"); + else + iter.val->_to_json_string(iter.val, pb, level+1,flags); + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + sprintbuf(pb, "\n"); + indent(pb,level,flags); + } + if (flags & JSON_C_TO_STRING_SPACED) + return sprintbuf(pb, /*{*/ " }"); + else + return sprintbuf(pb, /*{*/ "}"); +} + + +static void json_object_lh_entry_free(struct lh_entry *ent) +{ + free(ent->k); + json_object_put((struct json_object*)ent->v); +} + +static void json_object_object_delete(struct json_object* jso) +{ + lh_table_free(jso->o.c_object); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_object(void) +{ + struct json_object *jso = json_object_new(json_type_object); + if(!jso) return NULL; + jso->_delete = &json_object_object_delete; + jso->_to_json_string = &json_object_object_to_json_string; + jso->o.c_object = lh_kchar_table_new(JSON_OBJECT_DEF_HASH_ENTRIES, + NULL, &json_object_lh_entry_free); + return jso; +} + +struct lh_table* json_object_get_object(struct json_object *jso) +{ + if(!jso) return NULL; + switch(jso->o_type) { + case json_type_object: + return jso->o.c_object; + default: + return NULL; + } +} + +void json_object_object_add(struct json_object* jso, const char *key, + struct json_object *val) +{ + // We lookup the entry and replace the value, rather than just deleting + // and re-adding it, so the existing key remains valid. + json_object *existing_value = NULL; + struct lh_entry *existing_entry; + existing_entry = lh_table_lookup_entry(jso->o.c_object, (void*)key); + if (!existing_entry) + { + lh_table_insert(jso->o.c_object, strdup(key), val); + return; + } + existing_value = (void *)existing_entry->v; + if (existing_value) + json_object_put(existing_value); + existing_entry->v = val; +} + +int json_object_object_length(struct json_object *jso) +{ + return lh_table_length(jso->o.c_object); +} + +struct json_object* json_object_object_get(struct json_object* jso, const char *key) +{ + struct json_object *result = NULL; + json_object_object_get_ex(jso, key, &result); + return result; +} + +json_bool json_object_object_get_ex(struct json_object* jso, const char *key, struct json_object **value) +{ + if (value != NULL) + *value = NULL; + + if (NULL == jso) + return FALSE; + + switch(jso->o_type) + { + case json_type_object: + return lh_table_lookup_ex(jso->o.c_object, (void*)key, (void**)value); + default: + if (value != NULL) + *value = NULL; + return FALSE; + } +} + +void json_object_object_del(struct json_object* jso, const char *key) +{ + lh_table_delete(jso->o.c_object, key); +} + + +/* json_object_boolean */ + +static int json_object_boolean_to_json_string(struct json_object* jso, + struct printbuf *pb, + CPL_UNUSED int level, + CPL_UNUSED int flags) +{ + if(jso->o.c_boolean) return sprintbuf(pb, "true"); + else return sprintbuf(pb, "false"); +} + +struct json_object* json_object_new_boolean(json_bool b) +{ + struct json_object *jso = json_object_new(json_type_boolean); + if(!jso) return NULL; + jso->_to_json_string = &json_object_boolean_to_json_string; + jso->o.c_boolean = b; + return jso; +} + +json_bool json_object_get_boolean(struct json_object *jso) +{ + if(!jso) return FALSE; + switch(jso->o_type) { + case json_type_boolean: + return jso->o.c_boolean; + case json_type_int: + return (jso->o.c_int64 != 0); + case json_type_double: + return (jso->o.c_double != 0); + case json_type_string: + return (jso->o.c_string.len != 0); + default: + return FALSE; + } +} + + +/* json_object_int */ + +static int json_object_int_to_json_string(struct json_object* jso, + struct printbuf *pb, + CPL_UNUSED int level, + CPL_UNUSED int flags) +{ + return sprintbuf(pb, "%"PRId64, jso->o.c_int64); +} + +struct json_object* json_object_new_int(int32_t i) +{ + struct json_object *jso = json_object_new(json_type_int); + if(!jso) return NULL; + jso->_to_json_string = &json_object_int_to_json_string; + jso->o.c_int64 = i; + return jso; +} + +int32_t json_object_get_int(struct json_object *jso) +{ + int64_t cint64; + enum json_type o_type; + + if(!jso) return 0; + + o_type = jso->o_type; + cint64 = jso->o.c_int64; + + if (o_type == json_type_string) + { + /* + * Parse strings into 64-bit numbers, then use the + * 64-to-32-bit number handling below. + */ + if (json_parse_int64(jso->o.c_string.str, &cint64) != 0) + return 0; /* whoops, it didn't work. */ + o_type = json_type_int; + } + + switch(o_type) { + case json_type_int: + /* Make sure we return the correct values for out of range numbers. */ + if (cint64 <= INT32_MIN) + return INT32_MIN; + else if (cint64 >= INT32_MAX) + return INT32_MAX; + else + return (int32_t)cint64; + case json_type_double: + return (int32_t)jso->o.c_double; + case json_type_boolean: + return jso->o.c_boolean; + default: + return 0; + } +} + +struct json_object* json_object_new_int64(int64_t i) +{ + struct json_object *jso = json_object_new(json_type_int); + if(!jso) return NULL; + jso->_to_json_string = &json_object_int_to_json_string; + jso->o.c_int64 = i; + return jso; +} + +int64_t json_object_get_int64(struct json_object *jso) +{ + int64_t cint; + + if(!jso) return 0; + switch(jso->o_type) { + case json_type_int: + return jso->o.c_int64; + case json_type_double: + return (int64_t)jso->o.c_double; + case json_type_boolean: + return jso->o.c_boolean; + case json_type_string: + if (json_parse_int64(jso->o.c_string.str, &cint) == 0) return cint; + default: + return 0; + } +} + + +/* json_object_double */ + +static int json_object_double_to_json_string(struct json_object* jso, + struct printbuf *pb, + CPL_UNUSED int level, + int flags) +{ + char buf[128], *p, *q; + int size; + + size = snprintf(buf, 128, "%f", jso->o.c_double); + p = strchr(buf, ','); + if (p) { + *p = '.'; + } else { + p = strchr(buf, '.'); + } + if (p && (flags & JSON_C_TO_STRING_NOZERO)) { + /* last useful digit, always keep 1 zero */ + p++; + for (q=p ; *q ; q++) { + if (*q!='0') p=q; + } + /* drop trailing zeroes */ + *(++p) = 0; + size = p-buf; + } + printbuf_memappend(pb, buf, size); + return size; +} + +struct json_object* json_object_new_double(double d) +{ + struct json_object *jso = json_object_new(json_type_double); + if(!jso) return NULL; + jso->_to_json_string = &json_object_double_to_json_string; + jso->o.c_double = d; + return jso; +} + +double json_object_get_double(struct json_object *jso) +{ + if(!jso) return 0.0; + switch(jso->o_type) { + case json_type_double: + return jso->o.c_double; + case json_type_int: + return jso->o.c_int64; + case json_type_boolean: + return jso->o.c_boolean; + case json_type_string: + return CPLAtofM(jso->o.c_string.str); + default: + return 0.0; + } +} + + +/* json_object_string */ + +static int json_object_string_to_json_string(struct json_object* jso, + struct printbuf *pb, + CPL_UNUSED int level, + CPL_UNUSED int flags) +{ + sprintbuf(pb, "\""); + json_escape_str(pb, jso->o.c_string.str, jso->o.c_string.len); + sprintbuf(pb, "\""); + return 0; +} + +static void json_object_string_delete(struct json_object* jso) +{ + free(jso->o.c_string.str); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_string(const char *s) +{ + struct json_object *jso = json_object_new(json_type_string); + if(!jso) return NULL; + jso->_delete = &json_object_string_delete; + jso->_to_json_string = &json_object_string_to_json_string; + jso->o.c_string.str = strdup(s); + jso->o.c_string.len = strlen(s); + return jso; +} + +struct json_object* json_object_new_string_len(const char *s, int len) +{ + struct json_object *jso = json_object_new(json_type_string); + if(!jso) return NULL; + jso->_delete = &json_object_string_delete; + jso->_to_json_string = &json_object_string_to_json_string; + jso->o.c_string.str = (char*)malloc(len + 1); + memcpy(jso->o.c_string.str, (void *)s, len); + jso->o.c_string.str[len] = '\0'; + jso->o.c_string.len = len; + return jso; +} + +const char* json_object_get_string(struct json_object *jso) +{ + if(!jso) return NULL; + switch(jso->o_type) { + case json_type_string: + return jso->o.c_string.str; + default: + return json_object_to_json_string(jso); + } +} + +int json_object_get_string_len(struct json_object *jso) { + if(!jso) return 0; + switch(jso->o_type) { + case json_type_string: + return jso->o.c_string.len; + default: + return 0; + } +} + + +/* json_object_array */ + +static int json_object_array_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + int had_children = 0; + int ii; + sprintbuf(pb, "["); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + for(ii=0; ii < json_object_array_length(jso); ii++) + { + struct json_object *val; + if (had_children) + { + sprintbuf(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED) + sprintbuf(pb, " "); + indent(pb, level + 1, flags); + val = json_object_array_get_idx(jso, ii); + if(val == NULL) + sprintbuf(pb, "null"); + else + val->_to_json_string(val, pb, level+1, flags); + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + sprintbuf(pb, "\n"); + indent(pb,level,flags); + } + + if (flags & JSON_C_TO_STRING_SPACED) + return sprintbuf(pb, " ]"); + else + return sprintbuf(pb, "]"); +} + +static void json_object_array_entry_free(void *data) +{ + json_object_put((struct json_object*)data); +} + +static void json_object_array_delete(struct json_object* jso) +{ + array_list_free(jso->o.c_array); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_array(void) +{ + struct json_object *jso = json_object_new(json_type_array); + if(!jso) return NULL; + jso->_delete = &json_object_array_delete; + jso->_to_json_string = &json_object_array_to_json_string; + jso->o.c_array = array_list_new(&json_object_array_entry_free); + return jso; +} + +struct array_list* json_object_get_array(struct json_object *jso) +{ + if(!jso) return NULL; + switch(jso->o_type) { + case json_type_array: + return jso->o.c_array; + default: + return NULL; + } +} + +void json_object_array_sort(struct json_object *jso, int(*sort_fn)(const void *, const void *)) +{ + array_list_sort(jso->o.c_array, sort_fn); +} + +int json_object_array_length(struct json_object *jso) +{ + return array_list_length(jso->o.c_array); +} + +int json_object_array_add(struct json_object *jso,struct json_object *val) +{ + return array_list_add(jso->o.c_array, val); +} + +int json_object_array_put_idx(struct json_object *jso, int idx, + struct json_object *val) +{ + return array_list_put_idx(jso->o.c_array, idx, val); +} + +struct json_object* json_object_array_get_idx(struct json_object *jso, + int idx) +{ + return (struct json_object*)array_list_get_idx(jso->o.c_array, idx); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object.h new file mode 100644 index 000000000..387580572 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object.h @@ -0,0 +1,564 @@ +/* + * $Id: json_object.h,v 1.12 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_object_h_ +#define _json_object_h_ + +#include "symbol_renames.h" +#include "cpl_port.h" + +#include "json_inttypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_OBJECT_DEF_HASH_ENTRIES 16 + +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output + * to have no extra whitespace or formatting applied. + */ +#define JSON_C_TO_STRING_PLAIN 0 +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output to have + * minimal whitespace inserted to make things slightly more readable. + */ +#define JSON_C_TO_STRING_SPACED (1<<0) +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes + * the output to be formatted. + * + * See the "Two Space Tab" option at http://jsonformatter.curiousconcept.com/ + * for an example of the format. + */ +#define JSON_C_TO_STRING_PRETTY (1<<1) +/** + * A flag to drop trailing zero for float values + */ +#define JSON_C_TO_STRING_NOZERO (1<<2) + +#undef FALSE +#define FALSE ((json_bool)0) + +#undef TRUE +#define TRUE ((json_bool)1) + +extern const char *json_number_chars; +extern const char *json_hex_chars; + +/* CAW: added for ANSI C iteration correctness */ +struct json_object_iter +{ + char *key; + struct json_object *val; + struct lh_entry *entry; +}; + +/* forward structure definitions */ + +typedef int json_bool; +typedef struct printbuf printbuf; +typedef struct lh_table lh_table; +typedef struct array_list array_list; +typedef struct json_object json_object; +typedef struct json_object_iter json_object_iter; +typedef struct json_tokener json_tokener; + +/** + * Type of custom user delete functions. See json_object_set_serializer. + */ +typedef void (json_object_delete_fn)(struct json_object *jso, void *userdata); + +/** + * Type of a custom serialization function. See json_object_set_serializer. + */ +typedef int (json_object_to_json_string_fn)(struct json_object *jso, + struct printbuf *pb, + int level, + int flags); + +/* supported object types */ + +typedef enum json_type { + /* If you change this, be sure to update json_type_to_name() too */ + json_type_null, + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, +} json_type; + +/* reference counting functions */ + +/** + * Increment the reference count of json_object, thereby grabbing shared + * ownership of obj. + * + * @param obj the json_object instance + */ +extern struct json_object* json_object_get(struct json_object *obj); + +/** + * Decrement the reference count of json_object and free if it reaches zero. + * You must have ownership of obj prior to doing this or you will cause an + * imbalance in the reference count. + * + * @param obj the json_object instance + * @returns 1 if the object was freed. + */ +int CPL_DLL json_object_put(struct json_object *obj); + +/** + * Check if the json_object is of a given type + * @param obj the json_object instance + * @param type one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, + */ +extern int json_object_is_type(struct json_object *obj, enum json_type type); + +/** + * Get the type of the json_object. See also json_type_to_name() to turn this + * into a string suitable, for instance, for logging. + * + * @param obj the json_object instance + * @returns type being one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, + */ +extern enum json_type json_object_get_type(struct json_object *obj); + + +/** Stringify object to json format. + * Equivalent to json_object_to_json_string_ext(obj, JSON_C_TO_STRING_SPACED) + * @param obj the json_object instance + * @returns a string in JSON format + */ +extern const char* json_object_to_json_string(struct json_object *obj); + +/** Stringify object to json format + * @param obj the json_object instance + * @param flags formatting options, see JSON_C_TO_STRING_PRETTY and other constants + * @returns a string in JSON format + */ +extern const char CPL_DLL* json_object_to_json_string_ext(struct json_object *obj, int +flags); + +/** + * Set a custom serialization function to be used when this particular object + * is converted to a string by json_object_to_json_string. + * + * If a custom serializer is already set on this object, any existing + * user_delete function is called before the new one is set. + * + * If to_string_func is NULL, the other parameters are ignored + * and the default behaviour is reset. + * + * The userdata parameter is optional and may be passed as NULL. If provided, + * it is passed to to_string_func as-is. This parameter may be NULL even + * if user_delete is non-NULL. + * + * The user_delete parameter is optional and may be passed as NULL, even if + * the userdata parameter is non-NULL. It will be called just before the + * json_object is deleted, after it's reference count goes to zero + * (see json_object_put()). + * If this is not provided, it is up to the caller to free the userdata at + * an appropriate time. (i.e. after the json_object is deleted) + * + * @param jso the object to customize + * @param to_string_func the custom serialization function + * @param userdata an optional opaque cookie + * @param user_delete an optional function from freeing userdata + */ +/* GDAL added */ extern void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn to_string_func, + void *userdata, + json_object_delete_fn *user_delete); + + + +/* object type methods */ + +/** Create a new empty object with a reference count of 1. The caller of + * this object initially has sole ownership. Remember, when using + * json_object_object_add or json_object_array_put_idx, ownership will + * transfer to the object/array. Call json_object_get if you want to maintain + * shared ownership or also add this object as a child of multiple objects or + * arrays. Any ownerships you acquired but did not transfer must be released + * through json_object_put. + * + * @returns a json_object of type json_type_object + */ +extern struct json_object CPL_DLL* json_object_new_object(void); + +/** Get the hashtable of a json_object of type json_type_object + * @param obj the json_object instance + * @returns a linkhash + */ +extern struct lh_table* json_object_get_object(struct json_object *obj); + +/** Get the size of an object in terms of the number of fields it has. + * @param obj the json_object whose length to return + */ +extern int json_object_object_length(struct json_object* obj); + +/** Add an object field to a json_object of type json_type_object + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object, independent of the lifetime of obj, you must wrap the + * passed object with json_object_get. + * + * Upon calling this, the ownership of val transfers to obj. Thus you must + * make sure that you do in fact have ownership over this object. For instance, + * json_object_new_object will give you ownership until you transfer it, + * whereas json_object_object_get does not. + * + * @param obj the json_object instance + * @param key the object field name (a private copy will be duplicated) + * @param val a json_object or NULL member to associate with the given field + */ +extern void CPL_DLL json_object_object_add(struct json_object* obj, const char *key, + struct json_object *val); + +/** Get the json_object associate with a given object field + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of the returned value is retained + * by obj (do not do json_object_put unless you have done a json_object_get). + * If you delete the value from obj (json_object_object_del) and wish to access + * the returned reference afterwards, make sure you have first gotten shared + * ownership through json_object_get (& don't forget to do a json_object_put + * or transfer ownership to prevent a memory leak). + * + * @param obj the json_object instance + * @param key the object field name + * @returns the json_object associated with the given field name + */ +extern struct json_object* json_object_object_get(struct json_object* obj, + const char *key); + +/** Get the json_object associated with a given object field. + * + * This returns true if the key is found, false in all other cases (including + * if obj isn't a json_type_object). + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of value is retained by obj. + * + * @param obj the json_object instance + * @param key the object field name + * @param value a pointer where to store a reference to the json_object + * associated with the given field name. + * + * It is safe to pass a NULL value. + * @returns whether or not the key exists + */ +extern json_bool json_object_object_get_ex(struct json_object* obj, + const char *key, + struct json_object **value); + +/** Delete the given json_object field + * + * The reference count will be decremented for the deleted object. If there + * are no more owners of the value represented by this key, then the value is + * freed. Otherwise, the reference to the value will remain in memory. + * + * @param obj the json_object instance + * @param key the object field name + */ +extern void json_object_object_del(struct json_object* obj, const char *key); + +/** + * Iterate through all keys and values of an object. + * + * Adding keys to the object while iterating is NOT allowed. + * + * Deleting an existing key, or replacing an existing key with a + * new value IS allowed. + * + * @param obj the json_object instance + * @param key the local name for the char* key variable defined in the body + * @param val the local name for the json_object* object variable defined in + * the body + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L + +# define json_object_object_foreach(obj,key,val) \ + char *key; \ + struct json_object *val __attribute__((__unused__)); \ + for(struct lh_entry *entry ## key = json_object_get_object(obj)->head, *entry_next ## key = NULL; \ + ({ if(entry ## key) { \ + key = (char*)entry ## key->k; \ + val = (struct json_object*)entry ## key->v; \ + entry_next ## key = entry ## key->next; \ + } ; entry ## key; }); \ + entry ## key = entry_next ## key ) + +#else /* ANSI C or MSC */ + +# define json_object_object_foreach(obj,key,val) \ + char *key;\ + struct json_object *val; \ + struct lh_entry *entry ## key; \ + struct lh_entry *entry_next ## key = NULL; \ + for(entry ## key = json_object_get_object(obj)->head; \ + (entry ## key ? ( \ + key = (char*)entry ## key->k, \ + val = (struct json_object*)entry ## key->v, \ + entry_next ## key = entry ## key->next, \ + entry ## key) : 0); \ + entry ## key = entry_next ## key) + +#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L */ + +/** Iterate through all keys and values of an object (ANSI C Safe) + * @param obj the json_object instance + * @param iter the object iterator + */ +#define json_object_object_foreachC(obj,iter) \ + for(iter.entry = json_object_get_object(obj)->head; (iter.entry ? (iter.key = (char*)iter.entry->k, iter.val = (struct json_object*)iter.entry->v, iter.entry) : 0); iter.entry = iter.entry->next) + +/* Array type methods */ + +/** Create a new empty json_object of type json_type_array + * @returns a json_object of type json_type_array + */ +extern struct json_object CPL_DLL * json_object_new_array(void); + +/** Get the arraylist of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an arraylist + */ +extern struct array_list* json_object_get_array(struct json_object *obj); + +/** Get the length of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an int + */ +extern int json_object_array_length(struct json_object *obj); + +/** Sorts the elements of jso of type json_type_array +* +* Pointers to the json_object pointers will be passed as the two arguments +* to @sort_fn +* +* @param obj the json_object instance +* @param sort_fn a sorting function +*/ +extern void json_object_array_sort(struct json_object *jso, int(*sort_fn)(const void *, const void *)); + +/** Add an element to the end of a json_object of type json_type_array + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * @param obj the json_object instance + * @param val the json_object to be added + */ +extern int CPL_DLL json_object_array_add(struct json_object *obj, + struct json_object *val); + +/** Insert or replace an element at a specified index in an array (a json_object of type json_type_array) + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * The reference count of a replaced object will be decremented. + * + * The array size will be automatically be expanded to the size of the + * index if the index is larger than the current size. + * + * @param obj the json_object instance + * @param idx the index to insert the element at + * @param val the json_object to be added + */ +extern int json_object_array_put_idx(struct json_object *obj, int idx, + struct json_object *val); + +/** Get the element at specificed index of the array (a json_object of type json_type_array) + * @param obj the json_object instance + * @param idx the index to get the element at + * @returns the json_object at the specified index (or NULL) + */ +extern struct json_object* json_object_array_get_idx(struct json_object *obj, + int idx); + +/* json_bool type methods */ + +/** Create a new empty json_object of type json_type_boolean + * @param b a json_bool TRUE or FALSE (0 or 1) + * @returns a json_object of type json_type_boolean + */ +extern struct json_object* json_object_new_boolean(json_bool b); + +/** Get the json_bool value of a json_object + * + * The type is coerced to a json_bool if the passed object is not a json_bool. + * integer and double objects will return FALSE if there value is zero + * or TRUE otherwise. If the passed object is a string it will return + * TRUE if it has a non zero length. If any other object type is passed + * TRUE will be returned if the object is not NULL. + * + * @param obj the json_object instance + * @returns a json_bool + */ +extern json_bool json_object_get_boolean(struct json_object *obj); + + +/* int type methods */ + +/** Create a new empty json_object of type json_type_int + * Note that values are stored as 64-bit values internally. + * To ensure the full range is maintained, use json_object_new_int64 instead. + * @param i the integer + * @returns a json_object of type json_type_int + */ +extern struct json_object CPL_DLL* json_object_new_int(int32_t i); + + +/** Create a new empty json_object of type json_type_int + * @param i the integer + * @returns a json_object of type json_type_int + */ +extern struct json_object CPL_DLL* json_object_new_int64(int64_t i); + + +/** Get the int value of a json_object + * + * The type is coerced to a int if the passed object is not a int. + * double objects will return their integer conversion. Strings will be + * parsed as an integer. If no conversion exists then 0 is returned + * and errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * Note that integers are stored internally as 64-bit values. + * If the value of too big or too small to fit into 32-bit, INT32_MAX or + * INT32_MIN are returned, respectively. + * + * @param obj the json_object instance + * @returns an int + */ +extern int32_t json_object_get_int(struct json_object *obj); + +/** Get the int value of a json_object + * + * The type is coerced to a int64 if the passed object is not a int64. + * double objects will return their int64 conversion. Strings will be + * parsed as an int64. If no conversion exists then 0 is returned. + * + * NOTE: Set errno to 0 directly before a call to this function to determine + * whether or not conversion was successful (it does not clear the value for + * you). + * + * @param obj the json_object instance + * @returns an int64 + */ +extern int64_t json_object_get_int64(struct json_object *obj); + + +/* double type methods */ + +/** Create a new empty json_object of type json_type_double + * @param d the double + * @returns a json_object of type json_type_double + */ +extern struct json_object CPL_DLL* json_object_new_double(double d); + +/** Get the double floating point value of a json_object + * + * The type is coerced to a double if the passed object is not a double. + * integer objects will return their double conversion. Strings will be + * parsed as a double. If no conversion exists then 0.0 is returned and + * errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * If the value is too big to fit in a double, then the value is set to + * the closest infinity with errno set to ERANGE. If strings cannot be + * converted to their double value, then EINVAL is set & NaN is returned. + * + * Arrays of length 0 are interpreted as 0 (with no error flags set). + * Arrays of length 1 are effectively cast to the equivalent object and + * converted using the above rules. All other arrays set the error to + * EINVAL & return NaN. + * + * NOTE: Set errno to 0 directly before a call to this function to + * determine whether or not conversion was successful (it does not clear + * the value for you). + * + * @param obj the json_object instance + * @returns a double floating point number + */ +extern double json_object_get_double(struct json_object *obj); + + +/* string type methods */ + +/** Create a new empty json_object of type json_type_string + * + * A copy of the string is made and the memory is managed by the json_object + * + * @param s the string + * @returns a json_object of type json_type_string + */ +extern struct json_object CPL_DLL* json_object_new_string(const char *s); + +extern struct json_object* json_object_new_string_len(const char *s, int len); + +/** Get the string value of a json_object + * + * If the passed object is not of type json_type_string then the JSON + * representation of the object is returned. + * + * The returned string memory is managed by the json_object and will + * be freed when the reference count of the json_object drops to zero. + * + * @param obj the json_object instance + * @returns a string + */ +extern const char* json_object_get_string(struct json_object *obj); + +/** Get the string length of a json_object + * + * If the passed object is not of type json_type_string then zero + * will be returned. + * + * @param obj the json_object instance + * @returns int + */ +extern int json_object_get_string_len(struct json_object *obj); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object_iterator.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object_iterator.c new file mode 100644 index 000000000..36bc28518 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object_iterator.c @@ -0,0 +1,170 @@ +/** +******************************************************************************* +* @file json_object_iterator.c +* +* Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. +* +* This library is free software; you can redistribute it and/or modify +* it under the terms of the MIT license. See COPYING for details. +* +* @brief json-c forces clients to use its private data +* structures for JSON Object iteration. This API +* implementation corrects that by abstracting the +* private json-c details. +* +******************************************************************************* +*/ + +#include + +#include "json.h" +#include "json_object_private.h" + +#include "json_object_iterator.h" + +#include "cpl_port.h" + +/** + * How It Works + * + * For each JSON Object, json-c maintains a linked list of zero + * or more lh_entry (link-hash entry) structures inside the + * Object's link-hash table (lh_table). + * + * Each lh_entry structure on the JSON Object's linked list + * represents a single name/value pair. The "next" field of the + * last lh_entry in the list is set to NULL, which terminates + * the list. + * + * We represent a valid iterator that refers to an actual + * name/value pair via a pointer to the pair's lh_entry + * structure set as the iterator's opaque_ field. + * + * We follow json-c's current pair list representation by + * representing a valid "end" iterator (one that refers past the + * last pair) with a NULL value in the iterator's opaque_ field. + * + * A JSON Object without any pairs in it will have the "head" + * field of its lh_table structure set to NULL. For such an + * object, json_object_iter_begin will return an iterator with + * the opaque_ field set to NULL, which is equivalent to the + * "end" iterator. + * + * When iterating, we simply update the iterator's opaque_ field + * to point to the next lh_entry structure in the linked list. + * opaque_ will become NULL once we iterate past the last pair + * in the list, which makes the iterator equivalent to the "end" + * iterator. + */ + +/// Our current representation of the "end" iterator; +/// +/// @note May not always be NULL +static const void* kObjectEndIterValue = NULL; + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj) +{ + struct json_object_iterator iter; + struct lh_table* pTable; + + /// @note json_object_get_object will return NULL if passed NULL + /// or a non-json_type_object instance + pTable = json_object_get_object(obj); + JASSERT(NULL != pTable); + + /// @note For a pair-less Object, head is NULL, which matches our + /// definition of the "end" iterator + iter.opaque_ = pTable->head; + return iter; +} + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_end( CPL_UNUSED const struct json_object* obj ) +{ + struct json_object_iterator iter; + + JASSERT(NULL != obj); + JASSERT(json_object_is_type(obj, json_type_object)); + + iter.opaque_ = kObjectEndIterValue; + + return iter; +} + +/** + * **************************************************************************** + */ +void +json_object_iter_next(struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + iter->opaque_ = ((struct lh_entry *)iter->opaque_)->next; +} + + +/** + * **************************************************************************** + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (const char*)(((struct lh_entry *)iter->opaque_)->k); +} + + +/** + * **************************************************************************** + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (struct json_object*)(((struct lh_entry *)iter->opaque_)->v); +} + + +/** + * **************************************************************************** + */ +json_bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2) +{ + JASSERT(NULL != iter1); + JASSERT(NULL != iter2); + + return (iter1->opaque_ == iter2->opaque_); +} + + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_init_default(void) +{ + struct json_object_iterator iter; + + /** + * @note Make this a negative, invalid value, such that + * accidental access to it would likely be trapped by the + * hardware as an invalid address. + */ + iter.opaque_ = NULL; + + return iter; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object_iterator.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object_iterator.h new file mode 100644 index 000000000..f6e7ca62f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object_iterator.h @@ -0,0 +1,239 @@ +/** +******************************************************************************* +* @file json_object_iterator.h +* +* Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. +* +* This library is free software; you can redistribute it and/or modify +* it under the terms of the MIT license. See COPYING for details. +* +* @brief json-c forces clients to use its private data +* structures for JSON Object iteration. This API +* corrects that by abstracting the private json-c +* details. +* +* API attributes:
    +* * Thread-safe: NO
    +* * Reentrant: NO +* +******************************************************************************* +*/ + + +#ifndef JSON_OBJECT_ITERATOR_H +#define JSON_OBJECT_ITERATOR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Forward declaration for the opaque iterator information. + */ +struct json_object_iter_info_; + +/** + * The opaque iterator that references a name/value pair within + * a JSON Object instance or the "end" iterator value. + */ +struct json_object_iterator { + const void* opaque_; +}; + + +/** + * forward declaration of json-c's JSON value instance structure + */ +struct json_object; + + +/** + * Initializes an iterator structure to a "default" value that + * is convenient for initializing an iterator variable to a + * default state (e.g., initialization list in a class' + * constructor). + * + * @code + * struct json_object_iterator iter = json_object_iter_init_default(); + * MyClass() : iter_(json_object_iter_init_default()) + * @endcode + * + * @note The initialized value doesn't reference any specific + * pair, is considered an invalid iterator, and MUST NOT + * be passed to any json-c API that expects a valid + * iterator. + * + * @note User and internal code MUST NOT make any assumptions + * about and dependencies on the value of the "default" + * iterator value. + * + * @return json_object_iterator + */ +struct json_object_iterator +json_object_iter_init_default(void); + +/** Retrieves an iterator to the first pair of the JSON Object. + * + * @warning Any modification of the underlying pair invalidates all + * iterators to that pair. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator If the JSON Object has at + * least one pair, on return, the iterator refers + * to the first pair. If the JSON Object doesn't + * have any pairs, the returned iterator is + * equivalent to the "end" iterator for the same + * JSON Object instance. + * + * @code + * struct json_object_iterator it; + * struct json_object_iterator itEnd; + * struct json_object* obj; + * + * obj = json_tokener_parse("{'first':'george', 'age':100}"); + * it = json_object_iter_begin(obj); + * itEnd = json_object_iter_end(obj); + * + * while (!json_object_iter_equal(&it, &itEnd)) { + * printf("%s\n", + * json_object_iter_peek_name(&it)); + * json_object_iter_next(&it); + * } + * + * @endcode + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj); + +/** Retrieves the iterator that represents the position beyond the + * last pair of the given JSON Object instance. + * + * @warning Do NOT write code that assumes that the "end" + * iterator value is NULL, even if it is so in a + * particular instance of the implementation. + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The "end" iterator and the + * equality test method, on the other hand, permit us to + * cleanly abstract pretty much any reasonable underlying + * representation without burdening the iterator + * structure with unnecessary data. + * + * @note For performance reasons, memorize the "end" iterator prior + * to any loop. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator On return, the iterator refers + * to the "end" of the Object instance's pairs + * (i.e., NOT the last pair, but "beyond the last + * pair" value) + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj); + +/** Returns an iterator to the next pair, if any + * + * @warning Any modification of the underlying pair + * invalidates all iterators to that pair. + * + * @param iter [IN/OUT] Pointer to iterator that references a + * name/value pair; MUST be a valid, non-end iterator. + * WARNING: bad things will happen if invalid or "end" + * iterator is passed. Upon return will contain the + * reference to the next pair if there is one; if there + * are no more pairs, will contain the "end" iterator + * value, which may be compared against the return value + * of json_object_iter_end() for the same JSON Object + * instance. + */ +void +json_object_iter_next(struct json_object_iterator* iter); + + +/** Returns a const pointer to the name of the pair referenced + * by the given iterator. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if an invalid or + * "end" iterator is passed. + * + * @return const char* Pointer to the name of the referenced + * name/value pair. The name memory belongs to the + * name/value pair, will be freed when the pair is + * deleted or modified, and MUST NOT be modified or + * freed by the user. + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter); + + +/** Returns a pointer to the json-c instance representing the + * value of the referenced name/value pair, without altering + * the instance's reference count. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if invalid or + * "end" iterator is passed. + * + * @return struct json_object* Pointer to the json-c value + * instance of the referenced name/value pair; the + * value's reference count is not changed by this + * function: if you plan to hold on to this json-c node, + * take a look at json_object_get() and + * json_object_put(). IMPORTANT: json-c API represents + * the JSON Null value as a NULL json_object instance + * pointer. + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter); + + +/** Tests two iterators for equality. Typically used to test + * for end of iteration by comparing an iterator to the + * corresponding "end" iterator (that was derived from the same + * JSON Object instance). + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The equality test method, on + * the other hand, permits us to cleanly abstract pretty + * much any reasonable underlying representation. + * + * @param iter1 Pointer to first valid, non-NULL iterator + * @param iter2 POinter to second valid, non-NULL iterator + * + * @warning if a NULL iterator pointer or an uninitialized + * or invalid iterator, or iterators derived from + * different JSON Object instances are passed, bad things + * will happen! + * + * @return json_bool non-zero if iterators are equal (i.e., both + * reference the same name/value pair or are both at + * "end"); zero if they are not equal. + */ +json_bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2); + + +#ifdef __cplusplus +} +#endif + + +#endif // JSON_OBJECT_ITERATOR_H diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object_private.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object_private.h new file mode 100644 index 000000000..5ed791b58 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_object_private.h @@ -0,0 +1,47 @@ +/* + * $Id: json_object_private.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_object_private_h_ +#define _json_object_private_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (json_object_private_delete_fn)(struct json_object *o); + +struct json_object +{ + enum json_type o_type; + json_object_private_delete_fn *_delete; + json_object_to_json_string_fn *_to_json_string; + int _ref_count; + struct printbuf *_pb; + union data { + json_bool c_boolean; + double c_double; + int64_t c_int64; + struct lh_table *c_object; + struct array_list *c_array; + struct { + char *str; + int len; + } c_string; + } o; + json_object_delete_fn *_user_delete; + void *_userdata; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_tokener.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_tokener.c new file mode 100644 index 000000000..1e282576a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_tokener.c @@ -0,0 +1,771 @@ +/* + * $Id: json_tokener.c,v 1.20 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#include "config.h" + +#include "cpl_conv.h" + +#include +#include +#include +#include +#include +#include + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" +#include "arraylist.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +#ifdef HAVE_LOCALE_H +#include +#endif /* HAVE_LOCALE_H */ + +#if !HAVE_STRDUP && defined(_MSC_VER) + /* MSC has the version as _strdup */ +# define strdup _strdup +#elif !HAVE_STRDUP +# error You do not have strdup on your system. +#endif /* HAVE_STRDUP */ + +#if !HAVE_STRNCASECMP && defined(_MSC_VER) + /* MSC has the version as _strnicmp */ +# define strncasecmp _strnicmp +#elif !HAVE_STRNCASECMP +# error You do not have strncasecmp on your system. +#endif /* HAVE_STRNCASECMP */ + +static const char* json_null_str = "null"; +static const char* json_true_str = "true"; +static const char* json_false_str = "false"; + +// XXX after v0.10 this array will become static: +const char* json_tokener_errors[] = { + "success", + "continue", + "nesting too deep", + "unexpected end of data", + "unexpected character", + "null expected", + "boolean expected", + "number expected", + "array value separator ',' expected", + "quoted object property name expected", + "object property name separator ':' expected", + "object value separator ',' expected", + "invalid string sequence", + "expected comment", +}; + +const char *json_tokener_error_desc(enum json_tokener_error jerr) +{ + int jerr_int = (int)jerr; + if (jerr_int < 0 || jerr_int > (int)sizeof(json_tokener_errors)) + return "Unknown error, invalid json_tokener_error value passed to json_tokener_error_desc()"; + return json_tokener_errors[jerr]; +} + +enum json_tokener_error json_tokener_get_error(json_tokener *tok) +{ + return tok->err; +} + +/* Stuff for decoding unicode sequences */ +#define IS_HIGH_SURROGATE(uc) (((uc) & 0xFC00) == 0xD800) +#define IS_LOW_SURROGATE(uc) (((uc) & 0xFC00) == 0xDC00) +#define DECODE_SURROGATE_PAIR(hi,lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000) +static unsigned char utf8_replacement_char[3] = { 0xEF, 0xBF, 0xBD }; + +struct json_tokener* json_tokener_new_ex(int depth) +{ + struct json_tokener *tok; + + tok = (struct json_tokener*)calloc(1, sizeof(struct json_tokener)); + if (!tok) return NULL; + tok->stack = (struct json_tokener_srec *)calloc(depth, sizeof(struct json_tokener_srec)); + if (!tok->stack) { + free(tok); + return NULL; + } + tok->pb = printbuf_new(); + tok->max_depth = depth; + json_tokener_reset(tok); + return tok; +} + +struct json_tokener* json_tokener_new(void) +{ + return json_tokener_new_ex(JSON_TOKENER_DEFAULT_DEPTH); +} + +void json_tokener_free(struct json_tokener *tok) +{ + json_tokener_reset(tok); + if (tok->pb) printbuf_free(tok->pb); + if (tok->stack) free(tok->stack); + free(tok); +} + +static void json_tokener_reset_level(struct json_tokener *tok, int depth) +{ + tok->stack[depth].state = json_tokener_state_eatws; + tok->stack[depth].saved_state = json_tokener_state_start; + json_object_put(tok->stack[depth].current); + tok->stack[depth].current = NULL; + free(tok->stack[depth].obj_field_name); + tok->stack[depth].obj_field_name = NULL; +} + +void json_tokener_reset(struct json_tokener *tok) +{ + int i; + if (!tok) + return; + + for(i = tok->depth; i >= 0; i--) + json_tokener_reset_level(tok, i); + tok->depth = 0; + tok->err = json_tokener_success; +} + +struct json_object* json_tokener_parse(const char *str) +{ + enum json_tokener_error jerr_ignored; + struct json_object* obj; + obj = json_tokener_parse_verbose(str, &jerr_ignored); + return obj; +} + +struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error) +{ + struct json_tokener* tok; + struct json_object* obj; + + tok = json_tokener_new(); + if (!tok) + return NULL; + obj = json_tokener_parse_ex(tok, str, -1); + *error = tok->err; + if(tok->err != json_tokener_success) { + if (obj != NULL) + json_object_put(obj); + obj = NULL; + } + + json_tokener_free(tok); + return obj; +} + +#define state tok->stack[tok->depth].state +#define saved_state tok->stack[tok->depth].saved_state +#define current tok->stack[tok->depth].current +#define obj_field_name tok->stack[tok->depth].obj_field_name + +/* Optimization: + * json_tokener_parse_ex() consumed a lot of CPU in its main loop, + * iterating character-by character. A large performance boost is + * achieved by using tighter loops to locally handle units such as + * comments and strings. Loops that handle an entire token within + * their scope also gather entire strings and pass them to + * printbuf_memappend() in a single call, rather than calling + * printbuf_memappend() one char at a time. + * + * PEEK_CHAR() and ADVANCE_CHAR() macros are used for code that is + * common to both the main loop and the tighter loops. + */ + +/* PEEK_CHAR(dest, tok) macro: + * Peeks at the current char and stores it in dest. + * Returns 1 on success, sets tok->err and returns 0 if no more chars. + * Implicit inputs: str, len vars + */ +#define PEEK_CHAR(dest, tok) \ + (((tok)->char_offset == len) ? \ + (((tok)->depth == 0 && state == json_tokener_state_eatws && saved_state == json_tokener_state_finish) ? \ + (((tok)->err = json_tokener_success), 0) \ + : \ + (((tok)->err = json_tokener_continue), 0) \ + ) : \ + (((dest) = *str), 1) \ + ) + +/* ADVANCE_CHAR() macro: + * Incrementes str & tok->char_offset. + * For convenience of existing conditionals, returns the old value of c (0 on eof) + * Implicit inputs: c var + */ +#define ADVANCE_CHAR(str, tok) \ + ( ++(str), ((tok)->char_offset)++, c) + + +/* End optimization macro defs */ + + +struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + const char *str, int len) +{ + struct json_object *obj = NULL; + char c = '\1'; +#ifdef HAVE_SETLOCALE + char *oldlocale=NULL, *tmplocale; + + tmplocale = setlocale(LC_NUMERIC, NULL); + if (tmplocale) oldlocale = strdup(tmplocale); + setlocale(LC_NUMERIC, "C"); +#endif + + tok->char_offset = 0; + tok->err = json_tokener_success; + + while (PEEK_CHAR(c, tok)) { + + redo_char: + switch(state) { + + case json_tokener_state_eatws: + /* Advance until we change state */ + while (isspace((int)c)) { + if ((!ADVANCE_CHAR(str, tok)) || (!PEEK_CHAR(c, tok))) + goto out; + } + if(c == '/') { + printbuf_reset(tok->pb); + printbuf_memappend_fast(tok->pb, &c, 1); + state = json_tokener_state_comment_start; + } else { + state = saved_state; + goto redo_char; + } + break; + + case json_tokener_state_start: + switch(c) { + case '{': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_object_field_start; + current = json_object_new_object(); + break; + case '[': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_array; + current = json_object_new_array(); + break; + case 'N': + case 'n': + state = json_tokener_state_null; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case '"': + case '\'': + state = json_tokener_state_string; + printbuf_reset(tok->pb); + tok->quote_char = c; + break; + case 'T': + case 't': + case 'F': + case 'f': + state = json_tokener_state_boolean; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; +#if defined(__GNUC__) + case '0' ... '9': +#else + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': +#endif + case '-': + state = json_tokener_state_number; + printbuf_reset(tok->pb); + tok->is_double = 0; + goto redo_char; + default: + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + break; + + case json_tokener_state_finish: + if(tok->depth == 0) goto out; + obj = json_object_get(current); + json_tokener_reset_level(tok, tok->depth); + tok->depth--; + goto redo_char; + + case json_tokener_state_null: + printbuf_memappend_fast(tok->pb, &c, 1); + if(strncasecmp(json_null_str, tok->pb->buf, + json_min(tok->st_pos+1, (int)strlen(json_null_str))) == 0) { + if(tok->st_pos == (int)strlen(json_null_str)) { + current = NULL; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_null; + goto out; + } + tok->st_pos++; + break; + + case json_tokener_state_comment_start: + if(c == '*') { + state = json_tokener_state_comment; + } else if(c == '/') { + state = json_tokener_state_comment_eol; + } else { + tok->err = json_tokener_error_parse_comment; + goto out; + } + printbuf_memappend_fast(tok->pb, &c, 1); + break; + + case json_tokener_state_comment: + { + /* Advance until we change state */ + const char *case_start = str; + while(c != '*') { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, 1+str-case_start); + state = json_tokener_state_comment_end; + } + break; + + case json_tokener_state_comment_eol: + { + /* Advance until we change state */ + const char *case_start = str; + while(c != '\n') { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } + break; + + case json_tokener_state_comment_end: + printbuf_memappend_fast(tok->pb, &c, 1); + if(c == '/') { + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } else { + state = json_tokener_state_comment; + } + break; + + case json_tokener_state_string: + { + /* Advance until we change state */ + const char *case_start = str; + while(1) { + if(c == tok->quote_char) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + current = json_object_new_string_len(tok->pb->buf, tok->pb->bpos); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + break; + } else if(c == '\\') { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + saved_state = json_tokener_state_string; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + } + break; + + case json_tokener_state_string_escape: + switch(c) { + case '"': + case '\\': + case '/': + printbuf_memappend_fast(tok->pb, &c, 1); + state = saved_state; + break; + case 'b': + case 'n': + case 'r': + case 't': + case 'f': + if(c == 'b') printbuf_memappend_fast(tok->pb, "\b", 1); + else if(c == 'n') printbuf_memappend_fast(tok->pb, "\n", 1); + else if(c == 'r') printbuf_memappend_fast(tok->pb, "\r", 1); + else if(c == 't') printbuf_memappend_fast(tok->pb, "\t", 1); + else if(c == 'f') printbuf_memappend_fast(tok->pb, "\f", 1); + state = saved_state; + break; + case 'u': + tok->ucs_char = 0; + tok->st_pos = 0; + state = json_tokener_state_escape_unicode; + break; + default: + tok->err = json_tokener_error_parse_string; + goto out; + } + break; + + case json_tokener_state_escape_unicode: + { + unsigned int got_hi_surrogate = 0; + + /* Handle a 4-byte sequence, or two sequences if a surrogate pair */ + while(1) { + if(strchr(json_hex_chars, c)) { + tok->ucs_char += ((unsigned int)hexdigit(c) << ((3-tok->st_pos++)*4)); + if(tok->st_pos == 4) { + unsigned char unescaped_utf[4]; + + if (got_hi_surrogate) { + if (IS_LOW_SURROGATE(tok->ucs_char)) { + /* Recalculate the ucs_char, then fall thru to process normally */ + tok->ucs_char = DECODE_SURROGATE_PAIR(got_hi_surrogate, tok->ucs_char); + } else { + /* Hi surrogate was not followed by a low surrogate */ + /* Replace the hi and process the rest normally */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + got_hi_surrogate = 0; + } + + if (tok->ucs_char < 0x80) { + unescaped_utf[0] = tok->ucs_char; + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 1); + } else if (tok->ucs_char < 0x800) { + unescaped_utf[0] = 0xc0 | (tok->ucs_char >> 6); + unescaped_utf[1] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 2); + } else if (IS_HIGH_SURROGATE(tok->ucs_char)) { + /* Got a high surrogate. Remember it and look for the + * the beginning of another sequence, which should be the + * low surrogate. + */ + got_hi_surrogate = tok->ucs_char; + /* Not at end, and the next two chars should be "\u" */ + if ((tok->char_offset+1 != len) && + (tok->char_offset+2 != len) && + (str[1] == '\\') && + (str[2] == 'u')) + { + /* Advance through the 16 bit surrogate, and move on to the + * next sequence. The next step is to process the following + * characters. + */ + if( !ADVANCE_CHAR(str, tok) || !ADVANCE_CHAR(str, tok) ) { + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + /* Advance to the first char of the next sequence and + * continue processing with the next sequence. + */ + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + goto out; + } + tok->ucs_char = 0; + tok->st_pos = 0; + continue; /* other json_tokener_state_escape_unicode */ + } else { + /* Got a high surrogate without another sequence following + * it. Put a replacement char in for the hi surrogate + * and pretend we finished. + */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + } else if (IS_LOW_SURROGATE(tok->ucs_char)) { + /* Got a low surrogate not preceded by a high */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } else if (tok->ucs_char < 0x10000) { + unescaped_utf[0] = 0xe0 | (tok->ucs_char >> 12); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[2] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 3); + } else if (tok->ucs_char < 0x110000) { + unescaped_utf[0] = 0xf0 | ((tok->ucs_char >> 18) & 0x07); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 12) & 0x3f); + unescaped_utf[2] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[3] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 4); + } else { + /* Don't know what we got--insert the replacement char */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + state = saved_state; + break; + } + } else { + tok->err = json_tokener_error_parse_string; + goto out; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + if (got_hi_surrogate) /* Clean up any pending chars */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + goto out; + } + } + } + break; + + case json_tokener_state_boolean: + printbuf_memappend_fast(tok->pb, &c, 1); + if(strncasecmp(json_true_str, tok->pb->buf, + json_min(tok->st_pos+1, (int)strlen(json_true_str))) == 0) { + if(tok->st_pos == (int)strlen(json_true_str)) { + current = json_object_new_boolean(1); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else if(strncasecmp(json_false_str, tok->pb->buf, + json_min(tok->st_pos+1, (int)strlen(json_false_str))) == 0) { + if(tok->st_pos == (int)strlen(json_false_str)) { + current = json_object_new_boolean(0); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_boolean; + goto out; + } + tok->st_pos++; + break; + + case json_tokener_state_number: + { + /* Advance until we change state */ + const char *case_start = str; + int case_len=0; + while(c && strchr(json_number_chars, c)) { + ++case_len; + if(c == '.' || c == 'e' || c == 'E') + tok->is_double = 1; + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, case_len); + goto out; + } + } + if (case_len>0) + printbuf_memappend_fast(tok->pb, case_start, case_len); + } + { + int64_t num64; + double numd; + if (!tok->is_double && json_parse_int64(tok->pb->buf, &num64) == 0) { + current = json_object_new_int64(num64); + } else if(tok->is_double && json_parse_double(tok->pb->buf, &numd) == 0) { + current = json_object_new_double(numd); + } else { + tok->err = json_tokener_error_parse_number; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + break; + + case json_tokener_state_array_after_sep: + case json_tokener_state_array: + if(c == ']') { + if (state == json_tokener_state_array_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else { + if(tok->depth >= tok->max_depth-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_array_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + } + break; + + case json_tokener_state_array_add: + json_object_array_add(current, obj); + saved_state = json_tokener_state_array_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_array_sep: + if(c == ']') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_array_after_sep; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_array; + goto out; + } + break; + + case json_tokener_state_object_field_start: + case json_tokener_state_object_field_start_after_sep: + if(c == '}') { + if (state == json_tokener_state_object_field_start_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if (c == '"' || c == '\'') { + tok->quote_char = c; + printbuf_reset(tok->pb); + state = json_tokener_state_object_field; + } else { + tok->err = json_tokener_error_parse_object_key_name; + goto out; + } + break; + + case json_tokener_state_object_field: + { + /* Advance until we change state */ + const char *case_start = str; + while(1) { + if(c == tok->quote_char) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + obj_field_name = strdup(tok->pb->buf); + saved_state = json_tokener_state_object_field_end; + state = json_tokener_state_eatws; + break; + } else if(c == '\\') { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + saved_state = json_tokener_state_object_field; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + } + break; + + case json_tokener_state_object_field_end: + if(c == ':') { + saved_state = json_tokener_state_object_value; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_key_sep; + goto out; + } + break; + + case json_tokener_state_object_value: + if(tok->depth >= tok->max_depth-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_object_value_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + + case json_tokener_state_object_value_add: + json_object_object_add(current, obj_field_name, obj); + free(obj_field_name); + obj_field_name = NULL; + saved_state = json_tokener_state_object_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_object_sep: + if(c == '}') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_object_field_start_after_sep; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_value_sep; + goto out; + } + break; + + } + if (!ADVANCE_CHAR(str, tok)) + goto out; + } /* while(POP_CHAR) */ + + out: + if (!c) { /* We hit an eof char (0) */ + if(state != json_tokener_state_finish && + saved_state != json_tokener_state_finish) + tok->err = json_tokener_error_parse_eof; + } + +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, oldlocale); + if (oldlocale) free(oldlocale); +#endif + + if (tok->err == json_tokener_success) + { + json_object *ret = json_object_get(current); + int ii; + + /* Partially reset, so we parse additional objects on subsequent calls. */ + for(ii = tok->depth; ii >= 0; ii--) + json_tokener_reset_level(tok, ii); + return ret; + } + + MC_DEBUG("json_tokener_parse_ex: error %s at offset %d\n", + json_tokener_errors[tok->err], tok->char_offset); + return NULL; +} + +void json_tokener_set_flags(struct json_tokener *tok, int flags) +{ + tok->flags = flags; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_tokener.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_tokener.h new file mode 100644 index 000000000..08e5ff7fc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_tokener.h @@ -0,0 +1,209 @@ +/* + * $Id: json_tokener.h,v 1.10 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_tokener_h_ +#define _json_tokener_h_ + +#include +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum json_tokener_error { + json_tokener_success, + json_tokener_continue, + json_tokener_error_depth, + json_tokener_error_parse_eof, + json_tokener_error_parse_unexpected, + json_tokener_error_parse_null, + json_tokener_error_parse_boolean, + json_tokener_error_parse_number, + json_tokener_error_parse_array, + json_tokener_error_parse_object_key_name, + json_tokener_error_parse_object_key_sep, + json_tokener_error_parse_object_value_sep, + json_tokener_error_parse_string, + json_tokener_error_parse_comment +}; + +enum json_tokener_state { + json_tokener_state_eatws, + json_tokener_state_start, + json_tokener_state_finish, + json_tokener_state_null, + json_tokener_state_comment_start, + json_tokener_state_comment, + json_tokener_state_comment_eol, + json_tokener_state_comment_end, + json_tokener_state_string, + json_tokener_state_string_escape, + json_tokener_state_escape_unicode, + json_tokener_state_boolean, + json_tokener_state_number, + json_tokener_state_array, + json_tokener_state_array_add, + json_tokener_state_array_sep, + json_tokener_state_object_field_start, + json_tokener_state_object_field, + json_tokener_state_object_field_end, + json_tokener_state_object_value, + json_tokener_state_object_value_add, + json_tokener_state_object_sep, + json_tokener_state_array_after_sep, + json_tokener_state_object_field_start_after_sep +}; + +struct json_tokener_srec +{ + enum json_tokener_state state, saved_state; + struct json_object *obj; + struct json_object *current; + char *obj_field_name; +}; + +#define JSON_TOKENER_DEFAULT_DEPTH 32 + +struct json_tokener +{ + char *str; + struct printbuf *pb; + int max_depth, depth, is_double, st_pos, char_offset; + enum json_tokener_error err; + unsigned int ucs_char; + char quote_char; + struct json_tokener_srec *stack; + int flags; +}; + +/** + * Be strict when parsing JSON input. Use caution with + * this flag as what is considered valid may become more + * restrictive from one release to the next, causing your + * code to fail on previously working input. + * + * This flag is not set by default. + * + * @see json_tokener_set_flags() + */ +#define JSON_TOKENER_STRICT 0x01 + +/** + * Given an error previously returned by json_tokener_get_error(), + * return a human readable description of the error. + * + * @return a generic error message is returned if an invalid error value is provided. + */ +const char *json_tokener_error_desc(enum json_tokener_error jerr); + +/** + * @b XXX do not use json_tokener_errors directly. + * After v0.10 this will be removed. + * + * See json_tokener_error_desc() instead. + */ +extern const char* json_tokener_errors[]; + +/** + * Retrieve the error caused by the last call to json_tokener_parse_ex(), + * or json_tokener_success if there is no error. + * + * When parsing a JSON string in pieces, if the tokener is in the middle + * of parsing this will return json_tokener_continue. + * + * See also json_tokener_error_desc(). + */ +enum json_tokener_error json_tokener_get_error(struct json_tokener *tok); + +extern struct json_tokener* json_tokener_new(void); +extern struct json_tokener* json_tokener_new_ex(int depth); +extern void json_tokener_free(struct json_tokener *tok); +extern void json_tokener_reset(struct json_tokener *tok); +extern struct json_object* json_tokener_parse(const char *str); +extern struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error); + +/** + * Set flags that control how parsing will be done. + */ +extern void json_tokener_set_flags(struct json_tokener *tok, int flags); + +/** + * Parse a string and return a non-NULL json_object if a valid JSON value + * is found. The string does not need to be a JSON object or array; + * it can also be a string, number or boolean value. + * + * A partial JSON string can be parsed. If the parsing is incomplete, + * NULL will be returned and json_tokener_get_error() will be return + * json_tokener_continue. + * json_tokener_parse_ex() can then be called with additional bytes in str + * to continue the parsing. + * + * If json_tokener_parse_ex() returns NULL and the error anything other than + * json_tokener_continue, a fatal error has occurred and parsing must be + * halted. Then tok object must not be re-used until json_tokener_reset() is + * called. + * + * When a valid JSON value is parsed, a non-NULL json_object will be + * returned. Also, json_tokener_get_error() will return json_tokener_success. + * Be sure to check the type with json_object_is_type() or + * json_object_get_type() before using the object. + * + * @b XXX this shouldn't use internal fields: + * Trailing characters after the parsed value do not automatically cause an + * error. It is up to the caller to decide whether to treat this as an + * error or to handle the additional characters, perhaps by parsing another + * json value starting from that point. + * + * Extra characters can be detected by comparing the tok->char_offset against + * the length of the last len parameter passed in. + * + * The tokener does \b not maintain an internal buffer so the caller is + * responsible for calling json_tokener_parse_ex with an appropriate str + * parameter starting with the extra characters. + * + * Example: + * @code +json_object *jobj = NULL; +const char *mystring = NULL; +int stringlen = 0; +enum json_tokener_error jerr; +do { + mystring = ... // get JSON string, e.g. read from file, etc... + stringlen = strlen(mystring); + jobj = json_tokener_parse_ex(tok, mystring, stringlen); +} while ((jerr = json_tokener_get_error(tok)) == json_tokener_continue); +if (jerr != json_tokener_success) +{ + fprintf(stderr, "Error: %s\n", json_tokener_error_desc(jerr)); + // Handle errors, as appropriate for your application. +} +if (tok->char_offset < stringlen) // XXX shouldn't access internal fields +{ + // Handle extra characters after parsed object as desired. + // e.g. issue an error, parse another object from that point, etc... +} +// Success, use jobj here. + +@endcode + * + * @param tok a json_tokener previously allocated with json_tokener_new() + * @param str an string with any valid JSON expression, or portion of. This does not need to be null terminated. + * @param len the length of str + */ +extern struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + const char *str, int len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_util.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_util.c new file mode 100644 index 000000000..75611554f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_util.c @@ -0,0 +1,301 @@ +/* + * $Id: json_util.c,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" +#undef realloc + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef HAVE_SYS_STAT_H +#include +#endif /* HAVE_SYS_STAT_H */ + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + +#ifdef WIN32 +# define WIN32_LEAN_AND_MEAN +# include +# include +#endif /* defined(WIN32) */ + +#if !defined(HAVE_OPEN) && defined(WIN32) +# define open _open +#endif + +#if !defined(HAVE_SNPRINTF) && defined(_MSC_VER) + /* MSC has the version as _snprintf */ +# define snprintf _snprintf +#elif !defined(HAVE_SNPRINTF) +# error You do not have snprintf on your system. +#endif /* HAVE_SNPRINTF */ + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +#include "cpl_conv.h" +static int sscanf_is_broken = 0; +static int sscanf_is_broken_testdone = 0; +static void sscanf_is_broken_test(void); + +struct json_object* json_object_from_file(const char *filename) +{ + struct printbuf *pb; + struct json_object *obj; + char buf[JSON_FILE_BUF_SIZE]; + int fd, ret; + + if((fd = open(filename, O_RDONLY)) < 0) { + MC_ERROR("json_object_from_file: error reading file %s: %s\n", + filename, strerror(errno)); + return NULL; + } + if(!(pb = printbuf_new())) { + close(fd); + MC_ERROR("json_object_from_file: printbuf_new failed\n"); + return NULL; + } + while((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) { + printbuf_memappend(pb, buf, ret); + } + close(fd); + if(ret < 0) { + MC_ABORT("json_object_from_file: error reading file %s: %s\n", + filename, strerror(errno)); + printbuf_free(pb); + return NULL; + } + obj = json_tokener_parse(pb->buf); + printbuf_free(pb); + return obj; +} + +/* extended "format and write to file" function */ + +int json_object_to_file_ext(char *filename, struct json_object *obj, int flags) +{ + const char *json_str; + int fd, ret; + unsigned int wpos, wsize; + + if(!obj) { + MC_ERROR("json_object_to_file: object is null\n"); + return -1; + } + + if((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) { + MC_ERROR("json_object_to_file: error opening file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + if(!(json_str = json_object_to_json_string_ext(obj,flags))) { + close(fd); + return -1; + } + + wsize = (unsigned int)(strlen(json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ + wpos = 0; + while(wpos < wsize) { + if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0) { + close(fd); + MC_ERROR("json_object_to_file: error writing file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + /* because of the above check for ret < 0, we can safely cast and add */ + wpos += (unsigned int)ret; + } + + close(fd); + return 0; +} + +// backwards compatible "format and write to file" function + +int json_object_to_file(char *filename, struct json_object *obj) +{ + return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); +} + +int json_parse_double(const char *buf, double *retval) +{ + *retval = CPLStrtod(buf, 0); + return 0; +} + +/* + * Not all implementations of sscanf actually work properly. + * Check whether the one we're currently using does, and if + * it's broken, enable the workaround code. + */ +static void sscanf_is_broken_test() +{ + int64_t num64; + int ret_errno; + int is_int64_min; + int ret_errno2; + int is_int64_max; + + (void)sscanf(" -01234567890123456789012345", "%" SCNd64, &num64); + ret_errno = errno; + is_int64_min = (num64 == INT64_MIN); + + (void)sscanf(" 01234567890123456789012345", "%" SCNd64, &num64); + ret_errno2 = errno; + is_int64_max = (num64 == INT64_MAX); + + if (ret_errno != ERANGE || !is_int64_min || + ret_errno2 != ERANGE || !is_int64_max) + { + MC_DEBUG("sscanf_is_broken_test failed, enabling workaround code\n"); + sscanf_is_broken = 1; + } +} + +int json_parse_int64(const char *buf, int64_t *retval) +{ + int64_t num64; + const char *buf_sig_digits; + int orig_has_neg; + int saved_errno; + + if (!sscanf_is_broken_testdone) + { + sscanf_is_broken_test(); + sscanf_is_broken_testdone = 1; + } + + // Skip leading spaces + while (isspace((int)*buf) && *buf) + buf++; + + errno = 0; // sscanf won't always set errno, so initialize + if (sscanf(buf, "%" SCNd64, &num64) != 1) + { + MC_DEBUG("Failed to parse, sscanf != 1\n"); + return 1; + } + + saved_errno = errno; + buf_sig_digits = buf; + orig_has_neg = 0; + if (*buf_sig_digits == '-') + { + buf_sig_digits++; + orig_has_neg = 1; + } + + // Not all sscanf implementations actually work + if (sscanf_is_broken && saved_errno != ERANGE) + { + char buf_cmp[100]; + char *buf_cmp_start = buf_cmp; + int recheck_has_neg = 0; + int buf_cmp_len; + + // Skip leading zeros, but keep at least one digit + while (buf_sig_digits[0] == '0' && buf_sig_digits[1] != '\0') + buf_sig_digits++; + if (num64 == 0) // assume all sscanf impl's will parse -0 to 0 + orig_has_neg = 0; // "-0" is the same as just plain "0" + + snprintf(buf_cmp_start, sizeof(buf_cmp), "%" PRId64, num64); + if (*buf_cmp_start == '-') + { + recheck_has_neg = 1; + buf_cmp_start++; + } + // No need to skip leading spaces or zeros here. + + buf_cmp_len = strlen(buf_cmp_start); + /** + * If the sign is different, or + * some of the digits are different, or + * there is another digit present in the original string + * then we have NOT successfully parsed the value. + */ + if (orig_has_neg != recheck_has_neg || + strncmp(buf_sig_digits, buf_cmp_start, strlen(buf_cmp_start)) != 0 || + ((int)strlen(buf_sig_digits) != buf_cmp_len && + isdigit((int)buf_sig_digits[buf_cmp_len]) + ) + ) + { + saved_errno = ERANGE; + } + } + + // Not all sscanf impl's set the value properly when out of range. + // Always do this, even for properly functioning implementations, + // since it shouldn't slow things down much. + if (saved_errno == ERANGE) + { + if (orig_has_neg) + num64 = INT64_MIN; + else + num64 = INT64_MAX; + } + *retval = num64; + return 0; +} + +#ifndef HAVE_REALLOC +void* rpl_realloc(void* p, size_t n) +{ + if (n == 0) + n = 1; + if (p == 0) + return malloc(n); + return realloc(p, n); +} +#endif + +#define NELEM(a) (sizeof(a) / sizeof(a[0])) +static const char* json_type_name[] = { + /* If you change this, be sure to update the enum json_type definition too */ + "null", + "boolean", + "double", + "int", + "object", + "array", + "string", +}; + +const char *json_type_to_name(enum json_type o_type) +{ + int o_type_int = (int)o_type; + if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name)) + { + MC_ERROR("json_type_to_name: type %d is out of range [0,%d]\n", o_type, NELEM(json_type_name)); + return NULL; + } + return json_type_name[o_type]; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_util.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_util.h new file mode 100644 index 000000000..b9a69c8b6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/json_util.h @@ -0,0 +1,41 @@ +/* + * $Id: json_util.h,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_util_h_ +#define _json_util_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_FILE_BUF_SIZE 4096 + +/* utility functions */ +extern struct json_object* json_object_from_file(const char *filename); +extern int json_object_to_file(char *filename, struct json_object *obj); +extern int json_object_to_file_ext(char *filename, struct json_object *obj, int flags); +extern int json_parse_int64(const char *buf, int64_t *retval); +extern int json_parse_double(const char *buf, double *retval); + + +/** + * Return a string describing the type of the object. + * e.g. "int", or "object", etc... + */ +extern const char *json_type_to_name(enum json_type o_type); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/linkhash.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/linkhash.c new file mode 100644 index 000000000..50431485e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/linkhash.c @@ -0,0 +1,233 @@ +/* + * $Id: linkhash.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "linkhash.h" + +void lh_abort(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + va_end(ap); + exit(1); +} + +unsigned long lh_ptr_hash(const void *k) +{ + /* CAW: refactored to be 64bit nice */ + return (unsigned long)((((ptrdiff_t)k * LH_PRIME) >> 4) & ULONG_MAX); +} + +int lh_ptr_equal(const void *k1, const void *k2) +{ + return (k1 == k2); +} + +unsigned long lh_char_hash(const void *k) +{ + unsigned int h = 0; + const char* data = (const char*)k; + + while( *data!=0 ) h = h*129 + (unsigned int)(*data++) + LH_PRIME; + + return h; +} + +int lh_char_equal(const void *k1, const void *k2) +{ + return (strcmp((const char*)k1, (const char*)k2) == 0); +} + +struct lh_table* lh_table_new(int size, const char *name, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn) +{ + int i; + struct lh_table *t; + + t = (struct lh_table*)calloc(1, sizeof(struct lh_table)); + if(!t) lh_abort("lh_table_new: calloc failed\n"); + t->count = 0; + t->size = size; + t->name = name; + t->table = (struct lh_entry*)calloc(size, sizeof(struct lh_entry)); + if(!t->table) lh_abort("lh_table_new: calloc failed\n"); + t->free_fn = free_fn; + t->hash_fn = hash_fn; + t->equal_fn = equal_fn; + for(i = 0; i < size; i++) t->table[i].k = LH_EMPTY; + return t; +} + +struct lh_table* lh_kchar_table_new(int size, const char *name, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, name, free_fn, lh_char_hash, lh_char_equal); +} + +struct lh_table* lh_kptr_table_new(int size, const char *name, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, name, free_fn, lh_ptr_hash, lh_ptr_equal); +} + +void lh_table_resize(struct lh_table *t, int new_size) +{ + struct lh_table *new_t; + struct lh_entry *ent; + + new_t = lh_table_new(new_size, t->name, NULL, t->hash_fn, t->equal_fn); + ent = t->head; + while(ent) { + lh_table_insert(new_t, ent->k, ent->v); + ent = ent->next; + } + free(t->table); + t->table = new_t->table; + t->size = new_size; + t->head = new_t->head; + t->tail = new_t->tail; + t->resizes++; + free(new_t); +} + +void lh_table_free(struct lh_table *t) +{ + struct lh_entry *c; + for(c = t->head; c != NULL; c = c->next) { + if(t->free_fn) { + t->free_fn(c); + } + } + free(t->table); + free(t); +} + + +int lh_table_insert(struct lh_table *t, void *k, const void *v) +{ + unsigned long h, n; + + t->inserts++; + if(t->count >= t->size * LH_LOAD_FACTOR) lh_table_resize(t, t->size * 2); + + h = t->hash_fn(k); + n = h % t->size; + + while( 1 ) { + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) break; + t->collisions++; + if ((int)++n == t->size) n = 0; + } + + t->table[n].k = k; + t->table[n].v = v; + t->count++; + + if(t->head == NULL) { + t->head = t->tail = &t->table[n]; + t->table[n].next = t->table[n].prev = NULL; + } else { + t->tail->next = &t->table[n]; + t->table[n].prev = t->tail; + t->table[n].next = NULL; + t->tail = &t->table[n]; + } + + return 0; +} + + +struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) +{ + unsigned long h = t->hash_fn(k); + unsigned long n = h % t->size; + int count = 0; + + t->lookups++; + while( count < t->size ) { + if(t->table[n].k == LH_EMPTY) return NULL; + if(t->table[n].k != LH_FREED && + t->equal_fn(t->table[n].k, k)) return &t->table[n]; + if ((int)++n == t->size) n = 0; + count++; + } + return NULL; +} + + +const void* lh_table_lookup(struct lh_table *t, const void *k) +{ + void *result; + lh_table_lookup_ex(t, k, &result); + return result; +} + +json_bool lh_table_lookup_ex(struct lh_table* t, const void* k, void **v) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if (e != NULL) { + if (v != NULL) *v = (void *)e->v; + return TRUE; /* key found */ + } + if (v != NULL) *v = NULL; + return FALSE; /* key not found */ +} + +int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e) +{ + ptrdiff_t n = (ptrdiff_t)(e - t->table); /* CAW: fixed to be 64bit nice, still need the crazy negative case... */ + + /* CAW: this is bad, really bad, maybe stack goes other direction on this machine... */ + if(n < 0) { return -2; } + + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) return -1; + t->count--; + if(t->free_fn) t->free_fn(e); + t->table[n].v = NULL; + t->table[n].k = LH_FREED; + if(t->tail == &t->table[n] && t->head == &t->table[n]) { + t->head = t->tail = NULL; + } else if (t->head == &t->table[n]) { + t->head->next->prev = NULL; + t->head = t->head->next; + } else if (t->tail == &t->table[n]) { + t->tail->prev->next = NULL; + t->tail = t->tail->prev; + } else { + t->table[n].prev->next = t->table[n].next; + t->table[n].next->prev = t->table[n].prev; + } + t->table[n].next = t->table[n].prev = NULL; + return 0; +} + + +int lh_table_delete(struct lh_table *t, const void *k) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if(!e) return -1; + return lh_table_delete_entry(t, e); +} + +int lh_table_length(struct lh_table *t) +{ + return t->count; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/linkhash.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/linkhash.h new file mode 100644 index 000000000..378de0b76 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/linkhash.h @@ -0,0 +1,292 @@ +/* + * $Id: linkhash.h,v 1.6 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _linkhash_h_ +#define _linkhash_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * golden prime used in hash functions + */ +#define LH_PRIME 0x9e370001UL + +/** + * The fraction of filled hash buckets until an insert will cause the table + * to be resized. + * This can range from just above 0 up to 1.0. + */ +#define LH_LOAD_FACTOR 0.66 + +/** + * sentinel pointer value for empty slots + */ +#define LH_EMPTY (void*)-1 + +/** + * sentinel pointer value for freed slots + */ +#define LH_FREED (void*)-2 + +struct lh_entry; + +/** + * callback function prototypes + */ +typedef void (lh_entry_free_fn) (struct lh_entry *e); +/** + * callback function prototypes + */ +typedef unsigned long (lh_hash_fn) (const void *k); +/** + * callback function prototypes + */ +typedef int (lh_equal_fn) (const void *k1, const void *k2); + +/** + * An entry in the hash table + */ +struct lh_entry { + /** + * The key. + */ + void *k; + /** + * The value. + */ + const void *v; + /** + * The next entry + */ + struct lh_entry *next; + /** + * The previous entry. + */ + struct lh_entry *prev; +}; + + +/** + * The hash table structure. + */ +struct lh_table { + /** + * Size of our hash. + */ + int size; + /** + * Numbers of entries. + */ + int count; + + /** + * Number of collisions. + */ + int collisions; + + /** + * Number of resizes. + */ + int resizes; + + /** + * Number of lookups. + */ + int lookups; + + /** + * Number of inserts. + */ + int inserts; + + /** + * Number of deletes. + */ + int deletes; + + /** + * Name of the hash table. + */ + const char *name; + + /** + * The first entry. + */ + struct lh_entry *head; + + /** + * The last entry. + */ + struct lh_entry *tail; + + struct lh_entry *table; + + /** + * A pointer onto the function responsible for freeing an entry. + */ + lh_entry_free_fn *free_fn; + lh_hash_fn *hash_fn; + lh_equal_fn *equal_fn; +}; + + +/** + * Pre-defined hash and equality functions + */ +extern unsigned long lh_ptr_hash(const void *k); +extern int lh_ptr_equal(const void *k1, const void *k2); + +extern unsigned long lh_char_hash(const void *k); +extern int lh_char_equal(const void *k1, const void *k2); + + +/** + * Convenience list iterator. + */ +#define lh_foreach(table, entry) \ +for(entry = table->head; entry; entry = entry->next) + +/** + * lh_foreach_safe allows calling of deletion routine while iterating. + */ +#define lh_foreach_safe(table, entry, tmp) \ +for(entry = table->head; entry && ((tmp = entry->next) || 1); entry = tmp) + + + +/** + * Create a new linkhash table. + * @param size initial table size. The table is automatically resized + * although this incurs a performance penalty. + * @param name the table name. + * @param free_fn callback function used to free memory for entries + * when lh_table_free or lh_table_delete is called. + * If NULL is provided, then memory for keys and values + * must be freed by the caller. + * @param hash_fn function used to hash keys. 2 standard ones are defined: + * lh_ptr_hash and lh_char_hash for hashing pointer values + * and C strings respectively. + * @param equal_fn comparison function to compare keys. 2 standard ones defined: + * lh_ptr_hash and lh_char_hash for comparing pointer values + * and C strings respectively. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_table_new(int size, const char *name, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn); + +/** + * Convenience function to create a new linkhash + * table with char keys. + * @param size initial table size. + * @param name table name. + * @param free_fn callback function used to free memory for entries. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_kchar_table_new(int size, const char *name, + lh_entry_free_fn *free_fn); + + +/** + * Convenience function to create a new linkhash + * table with ptr keys. + * @param size initial table size. + * @param name table name. + * @param free_fn callback function used to free memory for entries. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_kptr_table_new(int size, const char *name, + lh_entry_free_fn *free_fn); + + +/** + * Free a linkhash table. + * If a callback free function is provided then it is called for all + * entries in the table. + * @param t table to free. + */ +extern void lh_table_free(struct lh_table *t); + + +/** + * Insert a record into the table. + * @param t the table to insert into. + * @param k a pointer to the key to insert. + * @param v a pointer to the value to insert. + */ +extern int lh_table_insert(struct lh_table *t, void *k, const void *v); + + +/** + * Lookup a record into the table. + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the record structure of the value or NULL if it does not exist. + */ +extern struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k); + +/** + * Lookup a record into the table + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the found value or NULL if it does not exist. + * @deprecated Use lh_table_lookup_ex instead. + */ +extern const void* lh_table_lookup(struct lh_table *t, const void *k); + +/** + * Lookup a record in the table + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @param v a pointer to a where to store the found value (set to NULL if it doesn't exist). + * @return whether or not the key was found + */ +extern json_bool lh_table_lookup_ex(struct lh_table *t, const void *k, void **v); + +/** + * Delete a record from the table. + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param e a pointer to the entry to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e); + + +/** + * Delete a record from the table. + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param k a pointer to the key to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete(struct lh_table *t, const void *k); + +extern int lh_table_length(struct lh_table *t); + +void lh_abort(const char *msg, ...); +void lh_table_resize(struct lh_table *t, int new_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/makefile.vc new file mode 100644 index 000000000..17ae0247f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/makefile.vc @@ -0,0 +1,27 @@ +# $Id$ +# +# Makefile building jcon-c library (http://oss.metaparadigm.com/json-c/) +# + +OBJ = \ + arraylist.obj \ + debug.obj \ + json_object.obj \ + json_tokener.obj \ + json_util.obj \ + linkhash.obj \ + printbuf.obj \ + json_object_iterator.obj \ + json_c_version.obj + +GDAL_ROOT = ..\..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. -I..\..\.. $(SOFTWARNFLAGS) + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/printbuf.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/printbuf.c new file mode 100644 index 000000000..6ec9533e5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/printbuf.c @@ -0,0 +1,148 @@ +/* + * $Id: printbuf.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#include "config.h" + +#include +#include +#include + +#include "cpl_string.h" + +#if HAVE_STDARG_H +# include +#else /* !HAVE_STDARG_H */ +# error Not enough var arg support! +#endif /* HAVE_STDARG_H */ + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" + +static int printbuf_extend(struct printbuf *p, int min_size); + +struct printbuf* printbuf_new(void) +{ + struct printbuf *p; + + p = (struct printbuf*)calloc(1, sizeof(struct printbuf)); + if(!p) return NULL; + p->size = 32; + p->bpos = 0; + if(!(p->buf = (char*)malloc(p->size))) { + free(p); + return NULL; + } + return p; +} + + +/** + * Extend the buffer p so it has a size of at least min_size. + * + * If the current size is large enough, nothing is changed. + * + * Note: this does not check the available space! The caller + * is responsible for performing those calculations. + */ +static int printbuf_extend(struct printbuf *p, int min_size) +{ + char *t; + int new_size; + + if (p->size >= min_size) + return 0; + + new_size = json_max(p->size * 2, min_size + 8); +#ifdef PRINTBUF_DEBUG + MC_DEBUG("printbuf_memappend: realloc " + "bpos=%d min_size=%d old_size=%d new_size=%d\n", + p->bpos, min_size, p->size, new_size); +#endif /* PRINTBUF_DEBUG */ + if(!(t = (char*)realloc(p->buf, new_size))) + return -1; + p->size = new_size; + p->buf = t; + return 0; +} + +int printbuf_memappend(struct printbuf *p, const char *buf, int size) +{ + if (p->size <= p->bpos + size + 1) { + if (printbuf_extend(p, p->bpos + size + 1) < 0) + return -1; + } + memcpy(p->buf + p->bpos, buf, size); + p->bpos += size; + p->buf[p->bpos]= '\0'; + return size; +} + +int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len) +{ + int size_needed; + + if (offset == -1) + offset = pb->bpos; + size_needed = offset + len; + if (pb->size < size_needed) + { + if (printbuf_extend(pb, size_needed) < 0) + return -1; + } + + memset(pb->buf + offset, charvalue, len); + if (pb->bpos < size_needed) + pb->bpos = size_needed; + + return 0; +} +/* Use CPLVASPrintf for portability issues */ +int sprintbuf(struct printbuf *p, const char *msg, ...) +{ + va_list ap; + char *t; + int size, ret; + + /* user stack buffer first */ + va_start(ap, msg); + if((size = CPLVASPrintf(&t, msg, ap)) == -1) return -1; + va_end(ap); + + if (strcmp(msg, "%f") == 0) + { + char* pszComma = strchr(t, ','); + if (pszComma) + *pszComma = '.'; + } + + ret = printbuf_memappend(p, t, size); + CPLFree(t); + return ret; +} + +void printbuf_reset(struct printbuf *p) +{ + p->buf[0] = '\0'; + p->bpos = 0; +} + +void printbuf_free(struct printbuf *p) +{ + if(p) { + free(p->buf); + free(p); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/printbuf.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/printbuf.h new file mode 100644 index 000000000..b1bde7f9f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/printbuf.h @@ -0,0 +1,77 @@ +/* + * $Id: printbuf.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#ifndef _printbuf_h_ +#define _printbuf_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct printbuf { + char *buf; + int bpos; + int size; +}; + +extern struct printbuf* +printbuf_new(void); + +/* As an optimization, printbuf_memappend_fast is defined as a macro + * that handles copying data if the buffer is large enough; otherwise + * it invokes printbuf_memappend_real() which performs the heavy + * lifting of realloc()ing the buffer and copying data. + * Your code should not use printbuf_memappend directly--use + * printbuf_memappend_fast instead. + */ +extern int +printbuf_memappend(struct printbuf *p, const char *buf, int size); + +#define printbuf_memappend_fast(p, bufptr, bufsize) \ +do { \ + if ((p->size - p->bpos) > bufsize) { \ + memcpy(p->buf + p->bpos, (bufptr), bufsize); \ + p->bpos += bufsize; \ + p->buf[p->bpos]= '\0'; \ + } else { printbuf_memappend(p, (bufptr), bufsize); } \ +} while (0) + +#define printbuf_length(p) ((p)->bpos) + +/** + * Set len bytes of the buffer to charvalue, starting at offset offset. + * Similar to calling memset(x, charvalue, len); + * + * The memory allocated for the buffer is extended as necessary. + * + * If offset is -1, this starts at the end of the current data in the buffer. + */ +extern int +printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len); + +extern int +sprintbuf(struct printbuf *p, const char *msg, ...); + +extern void +printbuf_reset(struct printbuf *p); + +extern void +printbuf_free(struct printbuf *p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/symbol_renames.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/symbol_renames.h new file mode 100644 index 000000000..28d209a08 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/libjson/symbol_renames.h @@ -0,0 +1,127 @@ +/* This is a generated file by dump_symbols.h. *DO NOT EDIT MANUALLY !* */ +#ifndef symbol_renames +#define symbol_renames + +#define array_list_expand_internal gdal_array_list_expand_internal +#define array_list_add gdal_array_list_add +#define array_list_free gdal_array_list_free +#define array_list_get_idx gdal_array_list_get_idx +#define array_list_length gdal_array_list_length +#define array_list_new gdal_array_list_new +#define array_list_put_idx gdal_array_list_put_idx +#define array_list_sort gdal_array_list_sort +#define mc_abort gdal_mc_abort +#define mc_debug gdal_mc_debug +#define mc_error gdal_mc_error +#define mc_get_debug gdal_mc_get_debug +#define mc_info gdal_mc_info +#define mc_set_debug gdal_mc_set_debug +#define mc_set_syslog gdal_mc_set_syslog +#define json_c_version gdal_json_c_version +#define json_c_version_num gdal_json_c_version_num +#define indent gdal_indent +#define json_escape_str gdal_json_escape_str +#define json_object_array_delete gdal_json_object_array_delete +#define json_object_array_entry_free gdal_json_object_array_entry_free +#define json_object_array_to_json_string gdal_json_object_array_to_json_string +#define json_object_boolean_to_json_string gdal_json_object_boolean_to_json_string +#define json_object_double_to_json_string gdal_json_object_double_to_json_string +#define json_object_generic_delete gdal_json_object_generic_delete +#define json_object_int_to_json_string gdal_json_object_int_to_json_string +#define json_object_lh_entry_free gdal_json_object_lh_entry_free +#define json_object_new gdal_json_object_new +#define json_object_object_delete gdal_json_object_object_delete +#define json_object_object_to_json_string gdal_json_object_object_to_json_string +#define json_object_string_delete gdal_json_object_string_delete +#define json_object_string_to_json_string gdal_json_object_string_to_json_string +#define json_object_array_add gdal_json_object_array_add +#define json_object_array_get_idx gdal_json_object_array_get_idx +#define json_object_array_length gdal_json_object_array_length +#define json_object_array_put_idx gdal_json_object_array_put_idx +#define json_object_array_sort gdal_json_object_array_sort +#define json_object_get gdal_json_object_get +#define json_object_get_array gdal_json_object_get_array +#define json_object_get_boolean gdal_json_object_get_boolean +#define json_object_get_double gdal_json_object_get_double +#define json_object_get_int gdal_json_object_get_int +#define json_object_get_int64 gdal_json_object_get_int64 +#define json_object_get_object gdal_json_object_get_object +#define json_object_get_string gdal_json_object_get_string +#define json_object_get_string_len gdal_json_object_get_string_len +#define json_object_get_type gdal_json_object_get_type +#define json_object_is_type gdal_json_object_is_type +#define json_object_new_array gdal_json_object_new_array +#define json_object_new_boolean gdal_json_object_new_boolean +#define json_object_new_double gdal_json_object_new_double +#define json_object_new_int gdal_json_object_new_int +#define json_object_new_int64 gdal_json_object_new_int64 +#define json_object_new_object gdal_json_object_new_object +#define json_object_new_string gdal_json_object_new_string +#define json_object_new_string_len gdal_json_object_new_string_len +#define json_object_object_add gdal_json_object_object_add +#define json_object_object_del gdal_json_object_object_del +#define json_object_object_get gdal_json_object_object_get +#define json_object_object_get_ex gdal_json_object_object_get_ex +#define json_object_object_length gdal_json_object_object_length +#define json_object_put gdal_json_object_put +#define json_object_set_serializer gdal_json_object_set_serializer +#define json_object_to_json_string gdal_json_object_to_json_string +#define json_object_to_json_string_ext gdal_json_object_to_json_string_ext +#define json_hex_chars gdal_json_hex_chars +#define json_number_chars gdal_json_number_chars +#define json_tokener_reset_level gdal_json_tokener_reset_level +#define json_tokener_error_desc gdal_json_tokener_error_desc +#define json_tokener_free gdal_json_tokener_free +#define json_tokener_get_error gdal_json_tokener_get_error +#define json_tokener_new gdal_json_tokener_new +#define json_tokener_new_ex gdal_json_tokener_new_ex +#define json_tokener_parse gdal_json_tokener_parse +#define json_tokener_parse_ex gdal_json_tokener_parse_ex +#define json_tokener_parse_verbose gdal_json_tokener_parse_verbose +#define json_tokener_reset gdal_json_tokener_reset +#define json_tokener_set_flags gdal_json_tokener_set_flags +#define json_false_str gdal_json_false_str +#define json_null_str gdal_json_null_str +#define json_true_str gdal_json_true_str +#define json_tokener_errors gdal_json_tokener_errors +#define json_object_iter_begin gdal_json_object_iter_begin +#define json_object_iter_end gdal_json_object_iter_end +#define json_object_iter_equal gdal_json_object_iter_equal +#define json_object_iter_init_default gdal_json_object_iter_init_default +#define json_object_iter_next gdal_json_object_iter_next +#define json_object_iter_peek_name gdal_json_object_iter_peek_name +#define json_object_iter_peek_value gdal_json_object_iter_peek_value +#define sscanf_is_broken_test gdal_sscanf_is_broken_test +#define json_object_from_file gdal_json_object_from_file +#define json_object_to_file gdal_json_object_to_file +#define json_object_to_file_ext gdal_json_object_to_file_ext +#define json_parse_double gdal_json_parse_double +#define json_parse_int64 gdal_json_parse_int64 +#define json_type_to_name gdal_json_type_to_name +#define json_type_name gdal_json_type_name +#define lh_abort gdal_lh_abort +#define lh_char_equal gdal_lh_char_equal +#define lh_char_hash gdal_lh_char_hash +#define lh_kchar_table_new gdal_lh_kchar_table_new +#define lh_kptr_table_new gdal_lh_kptr_table_new +#define lh_ptr_equal gdal_lh_ptr_equal +#define lh_ptr_hash gdal_lh_ptr_hash +#define lh_table_delete gdal_lh_table_delete +#define lh_table_delete_entry gdal_lh_table_delete_entry +#define lh_table_free gdal_lh_table_free +#define lh_table_insert gdal_lh_table_insert +#define lh_table_length gdal_lh_table_length +#define lh_table_lookup gdal_lh_table_lookup +#define lh_table_lookup_entry gdal_lh_table_lookup_entry +#define lh_table_lookup_ex gdal_lh_table_lookup_ex +#define lh_table_new gdal_lh_table_new +#define lh_table_resize gdal_lh_table_resize +#define printbuf_extend gdal_printbuf_extend +#define printbuf_free gdal_printbuf_free +#define printbuf_memappend gdal_printbuf_memappend +#define printbuf_memset gdal_printbuf_memset +#define printbuf_new gdal_printbuf_new +#define printbuf_reset gdal_printbuf_reset +#define sprintbuf gdal_sprintbuf + +#endif /* symbol_renames */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/makefile.vc new file mode 100644 index 000000000..09c999d65 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/makefile.vc @@ -0,0 +1,37 @@ +# $Id$ +# +# Makefile to build OGR GeoJSON driver +# +GDAL_ROOT = ..\..\.. + +GEOJSON_OBJ = \ + ogrgeojsondriver.obj \ + ogrgeojsondatasource.obj \ + ogrgeojsonlayer.obj \ + ogrgeojsonwritelayer.obj \ + ogrgeojsonutils.obj \ + ogrgeojsonreader.obj \ + ogrgeojsonwriter.obj \ + ogresrijsonreader.obj \ + ogrtopojsonreader.obj + +EXTRAFLAGS = -I. -I.. -I..\.. -Ilibjson + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +# TODO: Do we want to include GeoJSON driver by default +# or if user asks for it? Now it's on by default. + +OBJ = $(GEOJSON_OBJ) + +default: $(OBJ) + cd libjson + $(MAKE) /f makefile.vc + cd .. + +clean: + cd libjson + $(MAKE) /f makefile.vc clean + cd .. + -del *.lib + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogr_geojson.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogr_geojson.h new file mode 100644 index 000000000..b3c09632f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogr_geojson.h @@ -0,0 +1,215 @@ +/****************************************************************************** + * $Id: ogr_geojson.h 29111 2015-05-02 18:06:16Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Definitions of OGR OGRGeoJSON driver types. + * Author: Mateusz Loskot, mateusz@loskot.net + * + ****************************************************************************** + * Copyright (c) 2007, Mateusz Loskot + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef OGR_GEOJSON_H_INCLUDED +#define OGR_GEOJSON_H_INCLUDED + +#include "cpl_port.h" +#include + +#include +#include // used by OGRGeoJSONLayer +#include "ogrgeojsonutils.h" + +#define SPACE_FOR_BBOX 80 + +class OGRGeoJSONDataSource; + +/************************************************************************/ +/* OGRGeoJSONLayer */ +/************************************************************************/ + +class OGRGeoJSONLayer : public OGRLayer +{ +public: + + static const char* const DefaultName; + static const char* const DefaultFIDColumn; + static const OGRwkbGeometryType DefaultGeometryType; + + OGRGeoJSONLayer( const char* pszName, + OGRSpatialReference* poSRS, + OGRwkbGeometryType eGType, + OGRGeoJSONDataSource* poDS ); + ~OGRGeoJSONLayer(); + + // + // OGRLayer Interface + // + OGRFeatureDefn* GetLayerDefn(); + + GIntBig GetFeatureCount( int bForce = TRUE ); + void ResetReading(); + OGRFeature* GetNextFeature(); + int TestCapability( const char* pszCap ); + const char* GetFIDColumn(); + void SetFIDColumn( const char* pszFIDColumn ); + + // + // OGRGeoJSONLayer Interface + // + void AddFeature( OGRFeature* poFeature ); + void DetectGeometryType(); + +private: + + typedef std::vector FeaturesSeq; + FeaturesSeq seqFeatures_; + FeaturesSeq::iterator iterCurrent_; + + + // CPL_UNUSED OGRGeoJSONDataSource* poDS_; + OGRFeatureDefn* poFeatureDefn_; + CPLString sFIDColumn_; +}; + +/************************************************************************/ +/* OGRGeoJSONWriteLayer */ +/************************************************************************/ + +class OGRGeoJSONWriteLayer : public OGRLayer +{ +public: + OGRGeoJSONWriteLayer( const char* pszName, + OGRwkbGeometryType eGType, + char** papszOptions, + OGRGeoJSONDataSource* poDS ); + ~OGRGeoJSONWriteLayer(); + + // + // OGRLayer Interface + // + OGRFeatureDefn* GetLayerDefn() { return poFeatureDefn_; } + OGRSpatialReference* GetSpatialRef() { return NULL; } + + void ResetReading() { } + OGRFeature* GetNextFeature() { return NULL; } + OGRErr ICreateFeature( OGRFeature* poFeature ); + OGRErr CreateField(OGRFieldDefn* poField, int bApproxOK); + int TestCapability( const char* pszCap ); + +private: + + OGRGeoJSONDataSource* poDS_; + OGRFeatureDefn* poFeatureDefn_; + int nOutCounter_; + + int bWriteBBOX; + int bBBOX3D; + OGREnvelope3D sEnvelopeLayer; + + int nCoordPrecision; +}; + +/************************************************************************/ +/* OGRGeoJSONDataSource */ +/************************************************************************/ + +class OGRGeoJSONDataSource : public OGRDataSource +{ +public: + + OGRGeoJSONDataSource(); + ~OGRGeoJSONDataSource(); + + // + // OGRDataSource Interface + // + int Open( GDALOpenInfo* poOpenInfo, + GeoJSONSourceType nSrcType ); + const char* GetName(); + int GetLayerCount(); + OGRLayer* GetLayer( int nLayer ); + OGRLayer* ICreateLayer( const char* pszName, + OGRSpatialReference* poSRS = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char** papszOptions = NULL ); + int TestCapability( const char* pszCap ); + + void AddLayer( OGRGeoJSONLayer* poLayer ); + + // + // OGRGeoJSONDataSource Interface + // + int Create( const char* pszName, char** papszOptions ); + VSILFILE* GetOutputFile() const { return fpOut_; } + + enum GeometryTranslation + { + eGeometryPreserve, + eGeometryAsCollection, + }; + + void SetGeometryTranslation( GeometryTranslation type ); + + enum AttributesTranslation + { + eAtributesPreserve, + eAtributesSkip + }; + + void SetAttributesTranslation( AttributesTranslation type ); + + int GetFpOutputIsSeekable() const { return bFpOutputIsSeekable_; } + int GetBBOXInsertLocation() const { return nBBOXInsertLocation_; } + int HasOtherPages() const { return bOtherPages_; } + +private: + + // + // Private data members + // + char* pszName_; + char* pszGeoData_; + vsi_l_offset nGeoDataLen_; + OGRLayer** papoLayers_; + int nLayers_; + VSILFILE* fpOut_; + + // + // Translation/Creation control flags + // + GeometryTranslation flTransGeom_; + AttributesTranslation flTransAttrs_; + int bOtherPages_; /* ERSI Feature Service specific */ + + int bFpOutputIsSeekable_; + int nBBOXInsertLocation_; + + // + // Priavte utility functions + // + void Clear(); + int ReadFromFile( GDALOpenInfo* poOpenInfo ); + int ReadFromService( const char* pszSource ); + void LoadLayers(char** papszOpenOptions); +}; + + +#endif /* OGR_GEOJSON_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogresrijsonreader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogresrijsonreader.cpp new file mode 100644 index 000000000..63eed04a1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogresrijsonreader.cpp @@ -0,0 +1,978 @@ +/****************************************************************************** + * $Id: ogresrijsonreader.cpp 29120 2015-05-02 22:25:02Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implementation of OGRESRIJSONReader class (OGR ESRIJSON Driver) + * to read ESRI Feature Service REST data + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * Copyright (c) 2007, Mateusz Loskot + * Copyright (c) 2013, Kyle Shannon + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrgeojsonreader.h" +#include "ogrgeojsonutils.h" +#include "ogr_geojson.h" +#include // JSON-C +#include + +/************************************************************************/ +/* OGRESRIJSONReader() */ +/************************************************************************/ + +OGRESRIJSONReader::OGRESRIJSONReader() + : poGJObject_( NULL ), poLayer_( NULL ) +{ + // Take a deep breath and get to work. +} + +/************************************************************************/ +/* ~OGRESRIJSONReader() */ +/************************************************************************/ + +OGRESRIJSONReader::~OGRESRIJSONReader() +{ + if( NULL != poGJObject_ ) + { + json_object_put(poGJObject_); + } + + poGJObject_ = NULL; + poLayer_ = NULL; +} + +/************************************************************************/ +/* Parse() */ +/************************************************************************/ + +OGRErr OGRESRIJSONReader::Parse( const char* pszText ) +{ + if( NULL != pszText ) + { + json_tokener* jstok = NULL; + json_object* jsobj = NULL; + + jstok = json_tokener_new(); + jsobj = json_tokener_parse_ex(jstok, pszText, -1); + if( jstok->err != json_tokener_success) + { + CPLError( CE_Failure, CPLE_AppDefined, + "ESRIJSON parsing error: %s (at offset %d)", + json_tokener_error_desc(jstok->err), jstok->char_offset); + + json_tokener_free(jstok); + return OGRERR_CORRUPT_DATA; + } + json_tokener_free(jstok); + + /* JSON tree is shared for while lifetime of the reader object + * and will be released in the destructor. + */ + poGJObject_ = jsobj; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ReadLayers() */ +/************************************************************************/ + +void OGRESRIJSONReader::ReadLayers( OGRGeoJSONDataSource* poDS ) +{ + CPLAssert( NULL == poLayer_ ); + + if( NULL == poGJObject_ ) + { + CPLDebug( "ESRIJSON", + "Missing parset ESRIJSON data. Forgot to call Parse()?" ); + return; + } + + OGRSpatialReference* poSRS = NULL; + poSRS = OGRESRIJSONReadSpatialReference( poGJObject_ ); + + poLayer_ = new OGRGeoJSONLayer( OGRGeoJSONLayer::DefaultName, poSRS, + OGRESRIJSONGetGeometryType(poGJObject_), + poDS ); + if( poSRS != NULL ) + poSRS->Release(); + + if( !GenerateLayerDefn() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer schema generation failed." ); + + delete poLayer_; + return; + } + + OGRGeoJSONLayer* poThisLayer = NULL; + poThisLayer = ReadFeatureCollection( poGJObject_ ); + if (poThisLayer == NULL) + { + delete poLayer_; + return; + } + + CPLErrorReset(); + + poDS->AddLayer(poLayer_); +} + +/************************************************************************/ +/* GenerateFeatureDefn() */ +/************************************************************************/ + +bool OGRESRIJSONReader::GenerateLayerDefn() +{ + CPLAssert( NULL != poGJObject_ ); + CPLAssert( NULL != poLayer_->GetLayerDefn() ); + CPLAssert( 0 == poLayer_->GetLayerDefn()->GetFieldCount() ); + + bool bSuccess = true; + +/* -------------------------------------------------------------------- */ +/* Scan all features and generate layer definition. */ +/* -------------------------------------------------------------------- */ + json_object* poObjFeatures = NULL; + + poObjFeatures = OGRGeoJSONFindMemberByName( poGJObject_, "fields" ); + if( NULL != poObjFeatures && json_type_array == json_object_get_type( poObjFeatures ) ) + { + json_object* poObjFeature = NULL; + const int nFeatures = json_object_array_length( poObjFeatures ); + for( int i = 0; i < nFeatures; ++i ) + { + poObjFeature = json_object_array_get_idx( poObjFeatures, i ); + if( !GenerateFeatureDefn( poObjFeature ) ) + { + CPLDebug( "GeoJSON", "Create feature schema failure." ); + bSuccess = false; + } + } + } + else + { + poObjFeatures = OGRGeoJSONFindMemberByName( poGJObject_, "fieldAliases" ); + if( NULL != poObjFeatures && + json_object_get_type(poObjFeatures) == json_type_object ) + { + OGRFeatureDefn* poDefn = poLayer_->GetLayerDefn(); + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poObjFeatures, it ) + { + OGRFieldDefn fldDefn( it.key, OFTString ); + poDefn->AddFieldDefn( &fldDefn ); + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid FeatureCollection object. " + "Missing \'fields\' member." ); + bSuccess = false; + } + } + + return bSuccess; +} + +/************************************************************************/ +/* GenerateFeatureDefn() */ +/************************************************************************/ + +bool OGRESRIJSONReader::GenerateFeatureDefn( json_object* poObj ) +{ + OGRFeatureDefn* poDefn = poLayer_->GetLayerDefn(); + CPLAssert( NULL != poDefn ); + + bool bSuccess = false; + +/* -------------------------------------------------------------------- */ +/* Read collection of properties. */ +/* -------------------------------------------------------------------- */ + json_object* poObjName = OGRGeoJSONFindMemberByName( poObj, "name" ); + json_object* poObjType = OGRGeoJSONFindMemberByName( poObj, "type" ); + if( NULL != poObjName && NULL != poObjType ) + { + OGRFieldType eFieldType = OFTString; + if (EQUAL(json_object_get_string(poObjType), "esriFieldTypeOID")) + { + eFieldType = OFTInteger; + poLayer_->SetFIDColumn(json_object_get_string(poObjName)); + } + else if (EQUAL(json_object_get_string(poObjType), "esriFieldTypeDouble")) + { + eFieldType = OFTReal; + } + else if (EQUAL(json_object_get_string(poObjType), "esriFieldTypeSmallInteger") || + EQUAL(json_object_get_string(poObjType), "esriFieldTypeInteger") ) + { + eFieldType = OFTInteger; + } + OGRFieldDefn fldDefn( json_object_get_string(poObjName), + eFieldType); + + json_object* poObjLength = OGRGeoJSONFindMemberByName( poObj, "length" ); + if (poObjLength != NULL && json_object_get_type(poObjLength) == json_type_int ) + { + fldDefn.SetWidth(json_object_get_int(poObjLength)); + } + + poDefn->AddFieldDefn( &fldDefn ); + + bSuccess = true; // SUCCESS + } + return bSuccess; +} + +/************************************************************************/ +/* AddFeature */ +/************************************************************************/ + +bool OGRESRIJSONReader::AddFeature( OGRFeature* poFeature ) +{ + bool bAdded = false; + + if( NULL != poFeature ) + { + poLayer_->AddFeature( poFeature ); + bAdded = true; + delete poFeature; + } + + return bAdded; +} + +/************************************************************************/ +/* ReadGeometry() */ +/************************************************************************/ + +OGRGeometry* OGRESRIJSONReader::ReadGeometry( json_object* poObj ) +{ + OGRGeometry* poGeometry = NULL; + + OGRwkbGeometryType eType = poLayer_->GetGeomType(); + if (eType == wkbPoint) + poGeometry = OGRESRIJSONReadPoint( poObj ); + else if (eType == wkbLineString) + poGeometry = OGRESRIJSONReadLineString( poObj ); + else if (eType == wkbPolygon) + poGeometry = OGRESRIJSONReadPolygon( poObj ); + else if (eType == wkbMultiPoint) + poGeometry = OGRESRIJSONReadMultiPoint( poObj ); + + return poGeometry; +} + +/************************************************************************/ +/* ReadFeature() */ +/************************************************************************/ + +OGRFeature* OGRESRIJSONReader::ReadFeature( json_object* poObj ) +{ + CPLAssert( NULL != poObj ); + CPLAssert( NULL != poLayer_ ); + + OGRFeature* poFeature = NULL; + poFeature = new OGRFeature( poLayer_->GetLayerDefn() ); + +/* -------------------------------------------------------------------- */ +/* Translate ESRIJSON "attributes" object to feature attributes. */ +/* -------------------------------------------------------------------- */ + CPLAssert( NULL != poFeature ); + + json_object* poObjProps = NULL; + poObjProps = OGRGeoJSONFindMemberByName( poObj, "attributes" ); + if( NULL != poObjProps && + json_object_get_type(poObjProps) == json_type_object ) + { + int nField = -1; + OGRFieldDefn* poFieldDefn = NULL; + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poObjProps, it ) + { + nField = poFeature->GetFieldIndex(it.key); + if( nField >= 0 ) + { + poFieldDefn = poFeature->GetFieldDefnRef(nField); + if (poFieldDefn && it.val != NULL ) + { + if ( EQUAL( it.key, poLayer_->GetFIDColumn() ) ) + poFeature->SetFID( json_object_get_int( it.val ) ); + if ( poLayer_->GetLayerDefn()->GetFieldDefn(nField)->GetType() == OFTReal ) + poFeature->SetField( nField, CPLAtofM(json_object_get_string(it.val)) ); + else + poFeature->SetField( nField, json_object_get_string(it.val) ); + } + } + } + } + + OGRwkbGeometryType eType = poLayer_->GetGeomType(); + if (eType == wkbNone) + return poFeature; + +/* -------------------------------------------------------------------- */ +/* Translate geometry sub-object of ESRIJSON Feature. */ +/* -------------------------------------------------------------------- */ + json_object* poObjGeom = NULL; + + json_object* poTmp = poObj; + + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC(poTmp, it) + { + if( EQUAL( it.key, "geometry" ) ) { + if (it.val != NULL) + poObjGeom = it.val; + // we're done. They had 'geometry':null + else + return poFeature; + } + } + + if( NULL != poObjGeom ) + { + OGRGeometry* poGeometry = ReadGeometry( poObjGeom ); + if( NULL != poGeometry ) + { + poFeature->SetGeometryDirectly( poGeometry ); + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Feature object. " + "Missing \'geometry\' member." ); + delete poFeature; + return NULL; + } + + return poFeature; +} + +/************************************************************************/ +/* ReadFeatureCollection() */ +/************************************************************************/ + +OGRGeoJSONLayer* +OGRESRIJSONReader::ReadFeatureCollection( json_object* poObj ) +{ + CPLAssert( NULL != poLayer_ ); + + json_object* poObjFeatures = NULL; + poObjFeatures = OGRGeoJSONFindMemberByName( poObj, "features" ); + if( NULL == poObjFeatures ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid FeatureCollection object. " + "Missing \'features\' member." ); + return NULL; + } + + if( json_type_array == json_object_get_type( poObjFeatures ) ) + { + /* bool bAdded = false; */ + OGRFeature* poFeature = NULL; + json_object* poObjFeature = NULL; + + const int nFeatures = json_object_array_length( poObjFeatures ); + for( int i = 0; i < nFeatures; ++i ) + { + poObjFeature = json_object_array_get_idx( poObjFeatures, i ); + if (poObjFeature != NULL && + json_object_get_type(poObjFeature) == json_type_object) + { + poFeature = OGRESRIJSONReader::ReadFeature( poObjFeature ); + /* bAdded = */ AddFeature( poFeature ); + } + //CPLAssert( bAdded ); + } + //CPLAssert( nFeatures == poLayer_->GetFeatureCount() ); + } + + // We're returning class member to follow the same pattern of + // Read* functions call convention. + CPLAssert( NULL != poLayer_ ); + return poLayer_; +} + +/************************************************************************/ +/* OGRESRIJSONGetType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRESRIJSONGetGeometryType( json_object* poObj ) +{ + if( NULL == poObj ) + return wkbUnknown; + + json_object* poObjType = NULL; + poObjType = OGRGeoJSONFindMemberByName( poObj, "geometryType" ); + if( NULL == poObjType ) + { + return wkbNone; + } + + const char* name = json_object_get_string( poObjType ); + if( EQUAL( name, "esriGeometryPoint" ) ) + return wkbPoint; + else if( EQUAL( name, "esriGeometryPolyline" ) ) + return wkbLineString; + else if( EQUAL( name, "esriGeometryPolygon" ) ) + return wkbPolygon; + else if( EQUAL( name, "esriGeometryMultiPoint" ) ) + return wkbMultiPoint; + else + return wkbUnknown; +} + +/************************************************************************/ +/* OGRESRIJSONReadPoint() */ +/************************************************************************/ + +OGRPoint* OGRESRIJSONReadPoint( json_object* poObj) +{ + CPLAssert( NULL != poObj ); + + json_object* poObjX = OGRGeoJSONFindMemberByName( poObj, "x" ); + if( NULL == poObjX ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Point object. " + "Missing \'x\' member." ); + return NULL; + } + + int iTypeX = json_object_get_type(poObjX); + if ( (json_type_double != iTypeX) && (json_type_int != iTypeX) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid X coordinate. Type is not double or integer for \'%s\'.", + json_object_to_json_string(poObjX) ); + return NULL; + } + + json_object* poObjY = OGRGeoJSONFindMemberByName( poObj, "y" ); + if( NULL == poObjY ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Point object. " + "Missing \'y\' member." ); + return NULL; + } + + int iTypeY = json_object_get_type(poObjY); + if ( (json_type_double != iTypeY) && (json_type_int != iTypeY) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Y coordinate. Type is not double or integer for \'%s\'.", + json_object_to_json_string(poObjY) ); + return NULL; + } + + double dfX, dfY; + dfX = json_object_get_double( poObjX ); + dfY = json_object_get_double( poObjY ); + + bool is3d = false; + double dfZ = 0.0; + + json_object* poObjZ = OGRGeoJSONFindMemberByName( poObj, "z" ); + if( NULL != poObjZ ) + { + int iTypeZ = json_object_get_type(poObjZ); + if ( (json_type_double != iTypeZ) && (json_type_int != iTypeZ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Z coordinate. Type is not double or integer for \'%s\'.", + json_object_to_json_string(poObjZ) ); + return NULL; + } + is3d = true; + dfZ = json_object_get_double( poObjZ ); + } + + if(is3d) + { + return new OGRPoint(dfX, dfY, dfZ); + } + else + { + return new OGRPoint(dfX, dfY); + } +} + +/************************************************************************/ +/* OGRESRIJSONReaderParseZM() */ +/************************************************************************/ + +static int OGRESRIJSONReaderParseZM( json_object* poObj, int *bHasZ, + int *bHasM ) +{ + CPLAssert( NULL != poObj ); + /* + ** The esri geojson spec states that geometries other than point can + ** have the attributes hasZ and hasM. A geometry that has a z value + ** implies the 3rd number in the tuple is z. if hasM is true, but hasZ + ** is not, it is the M value, and is not supported in OGR. + */ + int bZ, bM; + json_object* poObjHasZ = OGRGeoJSONFindMemberByName( poObj, "hasZ" ); + if( poObjHasZ == NULL ) + { + bZ = FALSE; + } + else + { + if( json_object_get_type( poObjHasZ ) != json_type_boolean ) + { + bZ = FALSE; + } + else + { + bZ = json_object_get_boolean( poObjHasZ ); + } + } + + json_object* poObjHasM = OGRGeoJSONFindMemberByName( poObj, "hasM" ); + if( poObjHasM == NULL ) + { + bM = FALSE; + } + else + { + if( json_object_get_type( poObjHasM ) != json_type_boolean ) + { + bM = FALSE; + } + else + { + bM = json_object_get_boolean( poObjHasM ); + } + } + if( bHasZ != NULL ) + *bHasZ = bZ; + if( bHasM != NULL ) + *bHasM = bM; + return TRUE; +} + +/************************************************************************/ +/* OGRESRIJSONReaderParseXYZMArray() */ +/************************************************************************/ + +static int OGRESRIJSONReaderParseXYZMArray (json_object* poObjCoords, + double* pdfX, double* pdfY, + double* pdfZ, int* pnNumCoords) +{ + if (poObjCoords == NULL) + { + CPLDebug( "ESRIJSON", + "OGRESRIJSONReaderParseXYZMArray: got null object." ); + return FALSE; + } + + if( json_type_array != json_object_get_type( poObjCoords )) + { + CPLDebug( "ESRIJSON", + "OGRESRIJSONReaderParseXYZMArray: got non-array object." ); + return FALSE; + } + + int coordDimension = json_object_array_length( poObjCoords ); + /* + ** We allow 4 coordinates if M is present, but it is eventually ignored. + */ + if(coordDimension < 2 || coordDimension > 4) + { + CPLDebug( "ESRIJSON", + "OGRESRIJSONReaderParseXYZMArray: got an unexpected array object." ); + return FALSE; + } + + json_object* poObjCoord; + int iType; + double dfX, dfY, dfZ = 0.0; + + // Read X coordinate + poObjCoord = json_object_array_get_idx( poObjCoords, 0 ); + if (poObjCoord == NULL) + { + CPLDebug( "ESRIJSON", "OGRESRIJSONReaderParseXYZMArray: got null object." ); + return FALSE; + } + + iType = json_object_get_type(poObjCoord); + if ( (json_type_double != iType) && (json_type_int != iType) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid X coordinate. Type is not double or integer for \'%s\'.", + json_object_to_json_string(poObjCoord) ); + return FALSE; + } + + dfX = json_object_get_double( poObjCoord ); + + // Read Y coordinate + poObjCoord = json_object_array_get_idx( poObjCoords, 1 ); + if (poObjCoord == NULL) + { + CPLDebug( "ESRIJSON", "OGRESRIJSONReaderParseXYZMArray: got null object." ); + return FALSE; + } + + iType = json_object_get_type(poObjCoord); + if ( (json_type_double != iType) && (json_type_int != iType) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Y coordinate. Type is not double or integer for \'%s\'.", + json_object_to_json_string(poObjCoord) ); + return FALSE; + } + + dfY = json_object_get_double( poObjCoord ); + + // Read Z coordinate + if(coordDimension > 2) + { + poObjCoord = json_object_array_get_idx( poObjCoords, 2 ); + if (poObjCoord == NULL) + { + CPLDebug( "ESRIJSON", "OGRESRIJSONReaderParseXYZMArray: got null object." ); + return FALSE; + } + + iType = json_object_get_type(poObjCoord); + if ( (json_type_double != iType) && (json_type_int != iType) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Z coordinate. Type is not double or integer for \'%s\'.", + json_object_to_json_string(poObjCoord) ); + return FALSE; + } + dfZ = json_object_get_double( poObjCoord ); + } + + if( pnNumCoords != NULL ) + *pnNumCoords = coordDimension; + if( pdfX != NULL ) + *pdfX = dfX; + if( pdfY != NULL ) + *pdfY = dfY; + if( pdfZ != NULL ) + *pdfZ = dfZ; + + return TRUE; +} + +/************************************************************************/ +/* OGRESRIJSONReadLineString() */ +/************************************************************************/ + +OGRLineString* OGRESRIJSONReadLineString( json_object* poObj) +{ + CPLAssert( NULL != poObj ); + + int bHasZ = FALSE; + int bHasM = FALSE; + + if( !OGRESRIJSONReaderParseZM( poObj, &bHasZ, &bHasM ) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to parse hasZ and/or hasM from geometry" ); + } + + OGRLineString* poLine = NULL; + + json_object* poObjPaths = OGRGeoJSONFindMemberByName( poObj, "paths" ); + if( NULL == poObjPaths ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid LineString object. " + "Missing \'paths\' member." ); + return NULL; + } + + if( json_type_array != json_object_get_type( poObjPaths ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid LineString object. " + "Invalid \'paths\' member." ); + return NULL; + } + + poLine = new OGRLineString(); + const int nPaths = json_object_array_length( poObjPaths ); + for(int iPath = 0; iPath < nPaths; iPath ++) + { + json_object* poObjPath = json_object_array_get_idx( poObjPaths, iPath ); + if ( poObjPath == NULL || + json_type_array != json_object_get_type( poObjPath ) ) + { + delete poLine; + CPLDebug( "ESRIJSON", + "LineString: got non-array object." ); + return NULL; + } + + const int nPoints = json_object_array_length( poObjPath ); + for(int i = 0; i < nPoints; i++) + { + double dfX, dfY, dfZ; + int nNumCoords = 2; + json_object* poObjCoords = NULL; + + poObjCoords = json_object_array_get_idx( poObjPath, i ); + if( !OGRESRIJSONReaderParseXYZMArray (poObjCoords, &dfX, &dfY, &dfZ, &nNumCoords) ) + { + delete poLine; + return NULL; + } + + if(nNumCoords > 2 && (TRUE == bHasZ || FALSE == bHasM)) + { + poLine->addPoint( dfX, dfY, dfZ); + } + else + { + poLine->addPoint( dfX, dfY ); + } + } + } + + return poLine; +} + +/************************************************************************/ +/* OGRESRIJSONReadPolygon() */ +/************************************************************************/ + +OGRGeometry* OGRESRIJSONReadPolygon( json_object* poObj) +{ + CPLAssert( NULL != poObj ); + + + int bHasZ = FALSE; + int bHasM = FALSE; + + if( !OGRESRIJSONReaderParseZM( poObj, &bHasZ, &bHasM ) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to parse hasZ and/or hasM from geometry" ); + } + + json_object* poObjRings = OGRGeoJSONFindMemberByName( poObj, "rings" ); + if( NULL == poObjRings ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Polygon object. " + "Missing \'rings\' member." ); + return NULL; + } + + if( json_type_array != json_object_get_type( poObjRings ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Polygon object. " + "Invalid \'rings\' member." ); + return NULL; + } + + const int nRings = json_object_array_length( poObjRings ); + OGRGeometry** papoGeoms = new OGRGeometry*[nRings]; + for(int iRing = 0; iRing < nRings; iRing ++) + { + json_object* poObjRing = json_object_array_get_idx( poObjRings, iRing ); + if ( poObjRing == NULL || + json_type_array != json_object_get_type( poObjRing ) ) + { + for(int j=0;jaddRingDirectly(poLine); + papoGeoms[iRing] = poPoly; + + const int nPoints = json_object_array_length( poObjRing ); + for(int i = 0; i < nPoints; i++) + { + int nNumCoords = 2; + double dfX, dfY, dfZ; + json_object* poObjCoords = NULL; + + poObjCoords = json_object_array_get_idx( poObjRing, i ); + if( !OGRESRIJSONReaderParseXYZMArray (poObjCoords, &dfX, &dfY, &dfZ, &nNumCoords) ) + { + for(int j=0;j<=iRing;j++) + delete papoGeoms[j]; + delete[] papoGeoms; + return NULL; + } + + if(nNumCoords > 2 && (TRUE == bHasZ || FALSE == bHasM)) + { + poLine->addPoint( dfX, dfY, dfZ); + } + else + { + poLine->addPoint( dfX, dfY ); + } + } + } + + OGRGeometry* poRet = OGRGeometryFactory::organizePolygons( papoGeoms, + nRings, + NULL, + NULL); + delete[] papoGeoms; + + return poRet; +} + +/************************************************************************/ +/* OGRESRIJSONReadMultiPoint() */ +/************************************************************************/ + +OGRMultiPoint* OGRESRIJSONReadMultiPoint( json_object* poObj) +{ + CPLAssert( NULL != poObj ); + + int bHasZ = FALSE; + int bHasM = FALSE; + + if( !OGRESRIJSONReaderParseZM( poObj, &bHasZ, &bHasM ) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to parse hasZ and/or hasM from geometry" ); + } + + OGRMultiPoint* poMulti = NULL; + + json_object* poObjPoints = OGRGeoJSONFindMemberByName( poObj, "points" ); + if( NULL == poObjPoints ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid MultiPoint object. " + "Missing \'points\' member." ); + return NULL; + } + + if( json_type_array != json_object_get_type( poObjPoints ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid MultiPoint object. " + "Invalid \'points\' member." ); + return NULL; + } + + poMulti = new OGRMultiPoint(); + + const int nPoints = json_object_array_length( poObjPoints ); + for(int i = 0; i < nPoints; i++) + { + double dfX, dfY, dfZ; + int nNumCoords = 2; + json_object* poObjCoords = NULL; + + poObjCoords = json_object_array_get_idx( poObjPoints, i ); + if( !OGRESRIJSONReaderParseXYZMArray (poObjCoords, &dfX, &dfY, &dfZ, &nNumCoords) ) + { + delete poMulti; + return NULL; + } + + if(nNumCoords > 2 && (TRUE == bHasZ || FALSE == bHasM)) + { + poMulti->addGeometryDirectly( new OGRPoint(dfX, dfY, dfZ) ); + } + else + { + poMulti->addGeometryDirectly( new OGRPoint(dfX, dfY) ); + } + } + + return poMulti; +} + +/************************************************************************/ +/* OGRESRIJSONReadSpatialReference() */ +/************************************************************************/ + +OGRSpatialReference* OGRESRIJSONReadSpatialReference( json_object* poObj ) +{ +/* -------------------------------------------------------------------- */ +/* Read spatial reference definition. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference* poSRS = NULL; + + json_object* poObjSrs = OGRGeoJSONFindMemberByName( poObj, "spatialReference" ); + if( NULL != poObjSrs ) + { + json_object* poObjWkid = OGRGeoJSONFindMemberByName( poObjSrs, "wkid" ); + if (poObjWkid == NULL) + { + json_object* poObjWkt = OGRGeoJSONFindMemberByName( poObjSrs, "wkt" ); + if (poObjWkt == NULL) + return NULL; + + char* pszWKT = (char*) json_object_get_string( poObjWkt ); + poSRS = new OGRSpatialReference(); + if( OGRERR_NONE != poSRS->importFromWkt( &pszWKT ) || + poSRS->morphFromESRI() != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + + return poSRS; + } + + int nEPSG = json_object_get_int( poObjWkid ); + + poSRS = new OGRSpatialReference(); + if( OGRERR_NONE != poSRS->importFromEPSG( nEPSG ) ) + { + delete poSRS; + poSRS = NULL; + } + } + + return poSRS; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsondatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsondatasource.cpp new file mode 100644 index 000000000..3a2087ef5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsondatasource.cpp @@ -0,0 +1,602 @@ +/****************************************************************************** + * $Id: ogrgeojsondatasource.cpp 29120 2015-05-02 22:25:02Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implementation of OGRGeoJSONDataSource class (OGR GeoJSON Driver). + * Author: Mateusz Loskot, mateusz@loskot.net + * + ****************************************************************************** + * Copyright (c) 2007, Mateusz Loskot + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogr_geojson.h" +#include "ogrgeojsonutils.h" +#include "ogrgeojsonreader.h" +#include +#include // JSON-C +#include +#include +using namespace std; + +/************************************************************************/ +/* OGRGeoJSONDataSource() */ +/************************************************************************/ + +OGRGeoJSONDataSource::OGRGeoJSONDataSource() + : pszName_(NULL), pszGeoData_(NULL), nGeoDataLen_(0), + papoLayers_(NULL), nLayers_(0), fpOut_(NULL), + flTransGeom_( OGRGeoJSONDataSource::eGeometryPreserve ), + flTransAttrs_( OGRGeoJSONDataSource::eAtributesPreserve ), + bOtherPages_(FALSE), + bFpOutputIsSeekable_( FALSE ), + nBBOXInsertLocation_(0) +{ + // I've got constructed. Lunch time! +} + +/************************************************************************/ +/* ~OGRGeoJSONDataSource() */ +/************************************************************************/ + +OGRGeoJSONDataSource::~OGRGeoJSONDataSource() +{ + Clear(); + + if( NULL != fpOut_ ) + { + VSIFCloseL( fpOut_ ); + fpOut_ = NULL; + } +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRGeoJSONDataSource::Open( GDALOpenInfo* poOpenInfo, + GeoJSONSourceType nSrcType ) +{ + if( eGeoJSONSourceService == nSrcType ) + { + if( !ReadFromService( poOpenInfo->pszFilename ) ) + return FALSE; + } + else if( eGeoJSONSourceText == nSrcType ) + { + pszGeoData_ = CPLStrdup( poOpenInfo->pszFilename ); + } + else if( eGeoJSONSourceFile == nSrcType ) + { + if( !ReadFromFile( poOpenInfo ) ) + return FALSE; + } + else + { + Clear(); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Construct OGR layer and feature objects from */ +/* GeoJSON text tree. */ +/* -------------------------------------------------------------------- */ + if( NULL == pszGeoData_ || + strncmp(pszGeoData_, "{\"couchdb\":\"Welcome\"", strlen("{\"couchdb\":\"Welcome\"")) == 0 || + strncmp(pszGeoData_, "{\"db_name\":\"", strlen("{\"db_name\":\"")) == 0 || + strncmp(pszGeoData_, "{\"total_rows\":", strlen("{\"total_rows\":")) == 0 || + strncmp(pszGeoData_, "{\"rows\":[", strlen("{\"rows\":[")) == 0) + { + Clear(); + return FALSE; + } + + LoadLayers(poOpenInfo->papszOpenOptions); + if( nLayers_ == 0 ) + { + int bEmitError = TRUE; + if( eGeoJSONSourceService == nSrcType ) + { + CPLString osTmpFilename = CPLSPrintf("/vsimem/%p/%s", this, + CPLGetFilename(poOpenInfo->pszFilename)); + VSIFCloseL(VSIFileFromMemBuffer( osTmpFilename, + (GByte*)pszGeoData_, + nGeoDataLen_, + TRUE )); + pszGeoData_ = NULL; + if( GDALIdentifyDriver(osTmpFilename, NULL) ) + bEmitError = FALSE; + VSIUnlink(osTmpFilename); + } + Clear(); + + if( bEmitError ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to read GeoJSON data" ); + } + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char* OGRGeoJSONDataSource::GetName() +{ + return pszName_ ? pszName_ : ""; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int OGRGeoJSONDataSource::GetLayerCount() +{ + return nLayers_; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer* OGRGeoJSONDataSource::GetLayer( int nLayer ) +{ + if( 0 <= nLayer && nLayer < nLayers_ ) + { + return papoLayers_[nLayer]; + } + + return NULL; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer* OGRGeoJSONDataSource::ICreateLayer( const char* pszName_, + OGRSpatialReference* poSRS, + OGRwkbGeometryType eGType, + char** papszOptions ) +{ + if ( NULL == fpOut_ ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GeoJSON driver doesn't support creating a layer on a read-only datasource"); + return NULL; + } + + if ( nLayers_ != 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GeoJSON driver doesn't support creating more than one layer"); + return NULL; + } + + OGRGeoJSONWriteLayer* poLayer = NULL; + poLayer = new OGRGeoJSONWriteLayer( pszName_, eGType, papszOptions, this ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + + papoLayers_ = (OGRLayer **) + CPLRealloc( papoLayers_, sizeof(OGRLayer*) * (nLayers_ + 1) ); + + papoLayers_[nLayers_++] = poLayer; + + VSIFPrintfL( fpOut_, "{\n\"type\": \"FeatureCollection\",\n" ); + + if (poSRS) + { + const char* pszAuthority = poSRS->GetAuthorityName(NULL); + const char* pszAuthorityCode = poSRS->GetAuthorityCode(NULL); + if (pszAuthority != NULL && pszAuthorityCode != NULL && + EQUAL(pszAuthority, "EPSG")) + { + json_object* poObjCRS = json_object_new_object(); + json_object_object_add(poObjCRS, "type", + json_object_new_string("name")); + json_object* poObjProperties = json_object_new_object(); + json_object_object_add(poObjCRS, "properties", poObjProperties); + + if (strcmp(pszAuthorityCode, "4326") == 0) + { + json_object_object_add(poObjProperties, "name", + json_object_new_string("urn:ogc:def:crs:OGC:1.3:CRS84")); + } + else + { + json_object_object_add(poObjProperties, "name", + json_object_new_string(CPLSPrintf("urn:ogc:def:crs:EPSG::%s", pszAuthorityCode))); + } + + const char* pszCRS = json_object_to_json_string( poObjCRS ); + VSIFPrintfL( fpOut_, "\"crs\": %s,\n", pszCRS ); + + json_object_put(poObjCRS); + } + } + + if (bFpOutputIsSeekable_) + { + nBBOXInsertLocation_ = (int) VSIFTellL( fpOut_ ); + + char szSpaceForBBOX[SPACE_FOR_BBOX+1]; + memset(szSpaceForBBOX, ' ', SPACE_FOR_BBOX); + szSpaceForBBOX[SPACE_FOR_BBOX] = '\0'; + VSIFPrintfL( fpOut_, "%s\n", szSpaceForBBOX); + } + + VSIFPrintfL( fpOut_, "\"features\": [\n" ); + + return poLayer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGeoJSONDataSource::TestCapability( const char* pszCap ) +{ + if( EQUAL( pszCap, ODsCCreateLayer ) ) + return fpOut_ != NULL && nLayers_ == 0; + else if( EQUAL( pszCap, ODsCDeleteLayer ) ) + return FALSE; + else + return FALSE; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRGeoJSONDataSource::Create( const char* pszName, char** papszOptions ) +{ + UNREFERENCED_PARAM(papszOptions); + + CPLAssert( NULL == fpOut_ ); + + if (strcmp(pszName, "/dev/stdout") == 0) + pszName = "/vsistdout/"; + + bFpOutputIsSeekable_ = !(strcmp(pszName,"/vsistdout/") == 0 || + strncmp(pszName,"/vsigzip/", 9) == 0 || + strncmp(pszName,"/vsizip/", 8) == 0); + +/* -------------------------------------------------------------------- */ +/* File overwrite not supported. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + if( 0 == VSIStatL( pszName, &sStatBuf ) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "The GeoJSON driver does not overwrite existing files." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Create the output file. */ +/* -------------------------------------------------------------------- */ + fpOut_ = VSIFOpenL( pszName, "w" ); + if( NULL == fpOut_) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create GeoJSON datasource: %s.", + pszName ); + return FALSE; + } + + pszName_ = CPLStrdup( pszName ); + + return TRUE; +} + +/************************************************************************/ +/* SetGeometryTranslation() */ +/************************************************************************/ + +void +OGRGeoJSONDataSource::SetGeometryTranslation( GeometryTranslation type ) +{ + flTransGeom_ = type; +} + +/************************************************************************/ +/* SetAttributesTranslation() */ +/************************************************************************/ + +void OGRGeoJSONDataSource::SetAttributesTranslation( AttributesTranslation type ) +{ + flTransAttrs_ = type; +} + +/************************************************************************/ +/* PRIVATE FUNCTIONS IMPLEMENTATION */ +/************************************************************************/ + +void OGRGeoJSONDataSource::Clear() +{ + for( int i = 0; i < nLayers_; i++ ) + { + CPLAssert( NULL != papoLayers_ ); + delete papoLayers_[i]; + } + + CPLFree( papoLayers_ ); + papoLayers_ = NULL; + nLayers_ = 0; + + CPLFree( pszName_ ); + pszName_ = NULL; + + CPLFree( pszGeoData_ ); + pszGeoData_ = NULL; + nGeoDataLen_ = 0; + + if( NULL != fpOut_ ) + { + VSIFCloseL( fpOut_ ); + } + fpOut_ = NULL; +} + +/************************************************************************/ +/* ReadFromFile() */ +/************************************************************************/ + +int OGRGeoJSONDataSource::ReadFromFile( GDALOpenInfo* poOpenInfo ) +{ + GByte* pabyOut = NULL; + if( poOpenInfo->fpL == NULL || + !VSIIngestFile( poOpenInfo->fpL, poOpenInfo->pszFilename, &pabyOut, NULL, -1) ) + { + return FALSE; + } + + VSIFCloseL(poOpenInfo->fpL); + poOpenInfo->fpL = NULL; + pszGeoData_ = (char*) pabyOut; + + pszName_ = CPLStrdup( poOpenInfo->pszFilename ); + + CPLAssert( NULL != pszGeoData_ ); + return TRUE; +} + +/************************************************************************/ +/* ReadFromService() */ +/************************************************************************/ + +int OGRGeoJSONDataSource::ReadFromService( const char* pszSource ) +{ + CPLAssert( NULL == pszGeoData_ ); + CPLAssert( NULL != pszSource ); + + if( eGeoJSONProtocolUnknown == GeoJSONGetProtocolType( pszSource ) ) + { + CPLDebug( "GeoJSON", "Unknown service type (use HTTP, HTTPS, FTP)" ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Fetch the GeoJSON result. */ +/* -------------------------------------------------------------------- */ + CPLErrorReset(); + + CPLHTTPResult* pResult = NULL; + char* papsOptions[] = { (char*) "HEADERS=Accept: text/plain, application/json", NULL }; + + pResult = CPLHTTPFetch( pszSource, papsOptions ); + +/* -------------------------------------------------------------------- */ +/* Try to handle CURL/HTTP errors. */ +/* -------------------------------------------------------------------- */ + if( NULL == pResult + || 0 == pResult->nDataLen || 0 != CPLGetLastErrorNo() ) + { + CPLHTTPDestroyResult( pResult ); + return FALSE; + } + + if( 0 != pResult->nStatus ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Curl reports error: %d: %s", + pResult->nStatus, pResult->pszErrBuf ); + CPLHTTPDestroyResult( pResult ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Copy returned GeoJSON data to text buffer. */ +/* -------------------------------------------------------------------- */ + const char* pszData = reinterpret_cast(pResult->pabyData); + + if ( eGeoJSONProtocolUnknown != GeoJSONGetProtocolType( pszData ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The data that was downloaded also starts with " + "protocol prefix (http://, https:// or ftp://) " + "and cannot be processed as GeoJSON data."); + CPLHTTPDestroyResult( pResult ); + return FALSE; + } + + // Directly assign CPLHTTPResult::pabyData to pszGeoData_ + pszGeoData_ = (char*) pszData; + nGeoDataLen_ = pResult->nDataLen; + pResult->pabyData = NULL; + pResult->nDataLen = 0; + + pszName_ = CPLStrdup( pszSource ); + +/* -------------------------------------------------------------------- */ +/* Cleanup HTTP resources. */ +/* -------------------------------------------------------------------- */ + CPLHTTPDestroyResult( pResult ); + + CPLAssert( NULL != pszGeoData_ ); + return TRUE; +} + +/************************************************************************/ +/* LoadLayers() */ +/************************************************************************/ + +void OGRGeoJSONDataSource::LoadLayers(char** papszOpenOptions) +{ + if( NULL == pszGeoData_ ) + { + CPLError( CE_Failure, CPLE_ObjectNull, + "GeoJSON data buffer empty" ); + return; + } + + const char* const apszPrefix[] = { "loadGeoJSON(", "jsonp(" }; + for(size_t iP = 0; iP < sizeof(apszPrefix) / sizeof(apszPrefix[0]); iP++ ) + { + if( strncmp(pszGeoData_, apszPrefix[iP], strlen(apszPrefix[iP])) == 0 ) + { + size_t nDataLen = strlen(pszGeoData_); + memmove( pszGeoData_, pszGeoData_ + strlen(apszPrefix[iP]), + nDataLen - strlen(apszPrefix[iP]) ); + size_t i = nDataLen - strlen(apszPrefix[iP]); + pszGeoData_[i] = '\0'; + while( i > 0 && pszGeoData_[i] != ')' ) + { + i --; + } + pszGeoData_[i] = '\0'; + } + } + + + if ( !GeoJSONIsObject( pszGeoData_) ) + { + CPLDebug( "GeoJSON", "No valid GeoJSON data found in source '%s'", pszName_ ); + return; + } + + OGRErr err = OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* Is it ESRI Feature Service data ? */ +/* -------------------------------------------------------------------- */ + if ( strstr(pszGeoData_, "esriGeometry") || + strstr(pszGeoData_, "esriFieldType") ) + { + OGRESRIJSONReader reader; + err = reader.Parse( pszGeoData_ ); + if( OGRERR_NONE == err ) + { + json_object* poObj = reader.GetJSonObject(); + if( poObj && json_object_get_type(poObj) == json_type_object ) + { + json_object* poExceededTransferLimit = + json_object_object_get(poObj, "exceededTransferLimit"); + if( poExceededTransferLimit && json_object_get_type(poExceededTransferLimit) == json_type_boolean ) + bOtherPages_ = json_object_get_boolean(poExceededTransferLimit); + } + reader.ReadLayers( this ); + } + return; + } + +/* -------------------------------------------------------------------- */ +/* Is it TopoJSON data ? */ +/* -------------------------------------------------------------------- */ + if ( strstr(pszGeoData_, "\"type\"") && + strstr(pszGeoData_, "\"Topology\"") ) + { + OGRTopoJSONReader reader; + err = reader.Parse( pszGeoData_ ); + if( OGRERR_NONE == err ) + { + reader.ReadLayers( this ); + } + return; + } + +/* -------------------------------------------------------------------- */ +/* Configure GeoJSON format translator. */ +/* -------------------------------------------------------------------- */ + OGRGeoJSONReader reader; + + if( eGeometryAsCollection == flTransGeom_ ) + { + reader.SetPreserveGeometryType( false ); + CPLDebug( "GeoJSON", "Geometry as OGRGeometryCollection type." ); + } + + if( eAtributesSkip == flTransAttrs_ ) + { + reader.SetSkipAttributes( true ); + CPLDebug( "GeoJSON", "Skip all attributes." ); + } + + reader.SetFlattenNestedAttributes( + (bool)CSLFetchBoolean(papszOpenOptions, "FLATTEN_NESTED_ATTRIBUTES", FALSE), + CSLFetchNameValueDef(papszOpenOptions, "NESTED_ATTRIBUTE_SEPARATOR", "_")[0]); + +/* -------------------------------------------------------------------- */ +/* Parse GeoJSON and build valid OGRLayer instance. */ +/* -------------------------------------------------------------------- */ + err = reader.Parse( pszGeoData_ ); + if( OGRERR_NONE == err ) + { + json_object* poObj = reader.GetJSonObject(); + if( poObj && json_object_get_type(poObj) == json_type_object ) + { + json_object* poProperties = json_object_object_get(poObj, "properties"); + if( poProperties && json_object_get_type(poProperties) == json_type_object ) + { + json_object* poExceededTransferLimit = + json_object_object_get(poProperties, "exceededTransferLimit"); + if( poExceededTransferLimit && json_object_get_type(poExceededTransferLimit) == json_type_boolean ) + bOtherPages_ = json_object_get_boolean(poExceededTransferLimit); + } + } + + reader.ReadLayers( this ); + } + + return; +} + +/************************************************************************/ +/* AddLayer() */ +/************************************************************************/ + +void OGRGeoJSONDataSource::AddLayer( OGRGeoJSONLayer* poLayer ) +{ + + poLayer->DetectGeometryType(); + + /* Return layer in readable state. */ + poLayer->ResetReading(); + + papoLayers_ = (OGRLayer**)CPLRealloc( papoLayers_, sizeof(OGRLayer*) * (nLayers_ + 1)); + papoLayers_[nLayers_] = poLayer; + nLayers_ ++; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsondriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsondriver.cpp new file mode 100644 index 000000000..21c7f272d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsondriver.cpp @@ -0,0 +1,551 @@ +/****************************************************************************** + * $Id: ogrgeojsondriver.cpp 29134 2015-05-03 18:17:46Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implementation of OGRGeoJSONDriver class (OGR GeoJSON Driver). + * Author: Mateusz Loskot, mateusz@loskot.net + * + ****************************************************************************** + * Copyright (c) 2007, Mateusz Loskot + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogr_geojson.h" +#include +#include "cpl_http.h" + +class OGRESRIFeatureServiceDataset; + +/************************************************************************/ +/* OGRESRIFeatureServiceLayer */ +/************************************************************************/ + +class OGRESRIFeatureServiceLayer: public OGRLayer +{ + OGRESRIFeatureServiceDataset* poDS; + OGRFeatureDefn* poFeatureDefn; + GIntBig nFeaturesRead; + GIntBig nLastFID; + int bOtherPage; + int bUseSequentialFID; + + public: + OGRESRIFeatureServiceLayer(OGRESRIFeatureServiceDataset* poDS); + ~OGRESRIFeatureServiceLayer(); + + void ResetReading(); + OGRFeature* GetNextFeature(); + GIntBig GetFeatureCount( int bForce = TRUE ); + OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + int TestCapability( const char* pszCap ); + OGRFeatureDefn* GetLayerDefn() { return poFeatureDefn; } +}; + +/************************************************************************/ +/* OGRESRIFeatureServiceDataset */ +/************************************************************************/ + +class OGRESRIFeatureServiceDataset: public GDALDataset +{ + CPLString osURL; + GIntBig nFirstOffset, nLastOffset; + OGRGeoJSONDataSource* poCurrent; + OGRESRIFeatureServiceLayer* poLayer; + + int LoadPage(); + + public: + OGRESRIFeatureServiceDataset(const CPLString &osURL, + OGRGeoJSONDataSource* poFirst); + ~OGRESRIFeatureServiceDataset(); + + int GetLayerCount() { return 1; } + OGRLayer* GetLayer( int nLayer ) { return (nLayer == 0) ? poLayer : NULL; } + + OGRLayer* GetUnderlyingLayer() { return poCurrent->GetLayer(0); } + + int ResetReading(); + int LoadNextPage(); + + const CPLString& GetURL() { return osURL; } +}; + +/************************************************************************/ +/* OGRESRIFeatureServiceLayer() */ +/************************************************************************/ + +OGRESRIFeatureServiceLayer::OGRESRIFeatureServiceLayer(OGRESRIFeatureServiceDataset* poDS) +{ + this->poDS = poDS; + OGRFeatureDefn* poSrcFeatDefn = poDS->GetUnderlyingLayer()->GetLayerDefn(); + poFeatureDefn = new OGRFeatureDefn(poSrcFeatDefn->GetName()); + SetDescription(poFeatureDefn->GetName()); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType(wkbNone); + for(int i=0;iGetFieldCount();i++) + poFeatureDefn->AddFieldDefn(poSrcFeatDefn->GetFieldDefn(i)); + for(int i=0;iGetGeomFieldCount();i++) + poFeatureDefn->AddGeomFieldDefn(poSrcFeatDefn->GetGeomFieldDefn(i)); + nFeaturesRead = 0; + nLastFID = 0; + bOtherPage = FALSE; + bUseSequentialFID = FALSE; +} + +/************************************************************************/ +/* ~OGRESRIFeatureServiceLayer() */ +/************************************************************************/ + +OGRESRIFeatureServiceLayer::~OGRESRIFeatureServiceLayer() +{ + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRESRIFeatureServiceLayer::ResetReading() +{ + poDS->ResetReading(); + nFeaturesRead = 0; + nLastFID = 0; + bOtherPage = FALSE; + bUseSequentialFID = FALSE; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature* OGRESRIFeatureServiceLayer::GetNextFeature() +{ + while( TRUE ) + { + int bWasInFirstPage = !bOtherPage; + OGRFeature* poSrcFeat = poDS->GetUnderlyingLayer()->GetNextFeature(); + if( poSrcFeat == NULL ) + { + if( !poDS->LoadNextPage() ) + return NULL; + poSrcFeat = poDS->GetUnderlyingLayer()->GetNextFeature(); + if( poSrcFeat == NULL ) + return NULL; + bOtherPage = TRUE; + } + if( bOtherPage && bWasInFirstPage && poSrcFeat->GetFID() == 0 && + nLastFID == nFeaturesRead - 1 ) + { + bUseSequentialFID = TRUE; + } + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFrom(poSrcFeat); + if( bUseSequentialFID ) + poFeature->SetFID(nFeaturesRead); + else + poFeature->SetFID(poSrcFeat->GetFID()); + nLastFID = poFeature->GetFID(); + nFeaturesRead ++; + delete poSrcFeat; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + delete poFeature; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRESRIFeatureServiceLayer::TestCapability( const char* pszCap ) +{ + if( EQUAL(pszCap, OLCFastFeatureCount) ) + return m_poAttrQuery == NULL && m_poFilterGeom == NULL; + if( EQUAL(pszCap, OLCFastGetExtent) ) + return FALSE; + return poDS->GetUnderlyingLayer()->TestCapability(pszCap); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRESRIFeatureServiceLayer::GetFeatureCount( int bForce ) +{ + GIntBig nFeatureCount = -1; + if( m_poAttrQuery == NULL && m_poFilterGeom == NULL ) + { + CPLString osNewURL = CPLURLAddKVP(poDS->GetURL(), "returnCountOnly", "true"); + CPLHTTPResult* pResult = NULL; + CPLErrorReset(); + pResult = CPLHTTPFetch( osNewURL, NULL ); + if( pResult != NULL && pResult->nDataLen != 0 && CPLGetLastErrorNo() == 0 && + pResult->nStatus == 0 ) + { + const char* pszCount = strstr((const char*)pResult->pabyData, "\"count\""); + if( pszCount ) + { + pszCount = strchr(pszCount, ':'); + if( pszCount ) + { + pszCount++; + nFeatureCount = CPLAtoGIntBig(pszCount); + } + } + } + CPLHTTPDestroyResult( pResult ); + } + if( nFeatureCount < 0 ) + nFeatureCount = OGRLayer::GetFeatureCount(bForce); + return nFeatureCount; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRESRIFeatureServiceLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + OGRErr eErr = OGRERR_FAILURE; + CPLString osNewURL = CPLURLAddKVP(poDS->GetURL(), "returnExtentOnly", "true"); + osNewURL = CPLURLAddKVP(osNewURL, "f", "geojson"); + CPLHTTPResult* pResult = NULL; + CPLErrorReset(); + pResult = CPLHTTPFetch( osNewURL, NULL ); + if( pResult != NULL && pResult->nDataLen != 0 && CPLGetLastErrorNo() == 0 && + pResult->nStatus == 0 ) + { + const char* pszBBox = strstr((const char*)pResult->pabyData, "\"bbox\""); + if( pszBBox ) + { + pszBBox = strstr(pszBBox, ":["); + if( pszBBox ) + { + pszBBox+=2; + char** papszTokens = CSLTokenizeString2(pszBBox, ",", 0); + if( CSLCount(papszTokens) >= 4 ) + { + psExtent->MinX = CPLAtof(papszTokens[0]); + psExtent->MinY = CPLAtof(papszTokens[1]); + psExtent->MaxX = CPLAtof(papszTokens[2]); + psExtent->MaxY = CPLAtof(papszTokens[3]); + eErr = OGRERR_NONE; + } + CSLDestroy(papszTokens); + } + } + } + CPLHTTPDestroyResult( pResult ); + if( eErr == OGRERR_FAILURE ) + eErr = OGRLayer::GetExtent(psExtent, bForce); + return eErr; +} + +/************************************************************************/ +/* OGRESRIFeatureServiceDataset() */ +/************************************************************************/ + +OGRESRIFeatureServiceDataset::OGRESRIFeatureServiceDataset(const CPLString &osURL, + OGRGeoJSONDataSource* poFirst) +{ + poCurrent = poFirst; + poLayer = new OGRESRIFeatureServiceLayer(this); + this->osURL = osURL; + if( CPLURLGetValue(this->osURL, "resultRecordCount").size() == 0 ) + { + // We assume that if the server sets the exceededTransferLimit, the + // and resultRecordCount is not set, the number of features returned + // in our first request is the maximum allowed by the server + // So set it for following requests + this->osURL = CPLURLAddKVP(this->osURL, "resultRecordCount", + CPLSPrintf("%d", (int)poFirst->GetLayer(0)->GetFeatureCount())); + } + else + { + int nUserSetRecordCount = atoi(CPLURLGetValue(this->osURL, "resultRecordCount")); + if( nUserSetRecordCount > poFirst->GetLayer(0)->GetFeatureCount() ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Specificied resultRecordCount=%d is greater than the maximum %d supported by the server", + nUserSetRecordCount, (int)poFirst->GetLayer(0)->GetFeatureCount() ); + } + } + nFirstOffset = CPLAtoGIntBig(CPLURLGetValue(this->osURL, "resultOffset")); + nLastOffset = nFirstOffset; +} + +/************************************************************************/ +/* ~OGRESRIFeatureServiceDataset() */ +/************************************************************************/ + +OGRESRIFeatureServiceDataset::~OGRESRIFeatureServiceDataset() +{ + delete poCurrent; + delete poLayer; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +int OGRESRIFeatureServiceDataset::ResetReading() +{ + if( nLastOffset > nFirstOffset ) + { + nLastOffset = nFirstOffset; + return LoadPage(); + } + else + { + poCurrent->GetLayer(0)->ResetReading(); + return TRUE; + } +} + +/************************************************************************/ +/* LoadNextPage() */ +/************************************************************************/ + +int OGRESRIFeatureServiceDataset::LoadNextPage() +{ + if( !poCurrent->HasOtherPages() ) + return FALSE; + nLastOffset += poCurrent->GetLayer(0)->GetFeatureCount(); + return LoadPage(); +} + +/************************************************************************/ +/* LoadPage() */ +/************************************************************************/ + +int OGRESRIFeatureServiceDataset::LoadPage() +{ + CPLString osNewURL = CPLURLAddKVP(osURL, "resultOffset", + CPLSPrintf(CPL_FRMT_GIB, nLastOffset)); + OGRGeoJSONDataSource* poDS = NULL; + poDS = new OGRGeoJSONDataSource(); + GDALOpenInfo oOpenInfo(osNewURL, GA_ReadOnly); + if( !poDS->Open( &oOpenInfo, GeoJSONGetSourceType( &oOpenInfo ) ) || + poDS->GetLayerCount() == 0 ) + { + delete poDS; + poDS= NULL; + return FALSE; + } + delete poCurrent; + poCurrent = poDS; + return TRUE; +} + + +/************************************************************************/ +/* OGRGeoJSONDriverIdentify() */ +/************************************************************************/ + +static int OGRGeoJSONDriverIdentifyInternal( GDALOpenInfo* poOpenInfo, + GeoJSONSourceType& nSrcType ) +{ +/* -------------------------------------------------------------------- */ +/* Determine type of data source: text file (.geojson, .json), */ +/* Web Service or text passed directly and load data. */ +/* -------------------------------------------------------------------- */ + + nSrcType = GeoJSONGetSourceType( poOpenInfo ); + if( nSrcType == eGeoJSONSourceUnknown ) + return FALSE; + if( nSrcType == eGeoJSONSourceService ) + return -1; + return TRUE; +} + +/************************************************************************/ +/* OGRGeoJSONDriverIdentify() */ +/************************************************************************/ + +static int OGRGeoJSONDriverIdentify( GDALOpenInfo* poOpenInfo ) +{ + GeoJSONSourceType nSrcType; + return OGRGeoJSONDriverIdentifyInternal(poOpenInfo, nSrcType); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset* OGRGeoJSONDriverOpen( GDALOpenInfo* poOpenInfo ) +{ + GeoJSONSourceType nSrcType; + if( OGRGeoJSONDriverIdentifyInternal(poOpenInfo, nSrcType) == FALSE ) + return NULL; + + OGRGeoJSONDataSource* poDS = NULL; + poDS = new OGRGeoJSONDataSource(); + +/* -------------------------------------------------------------------- */ +/* Processing configuration options. */ +/* -------------------------------------------------------------------- */ + + // TODO: Currently, options are based on environment variables. + // This is workaround for not yet implemented Andrey's concept + // described in document 'RFC 10: OGR Open Parameters'. + + poDS->SetGeometryTranslation( OGRGeoJSONDataSource::eGeometryPreserve ); + const char* pszOpt = CPLGetConfigOption("GEOMETRY_AS_COLLECTION", NULL); + if( NULL != pszOpt && EQUALN(pszOpt, "YES", 3) ) + { + poDS->SetGeometryTranslation( + OGRGeoJSONDataSource::eGeometryAsCollection ); + } + + poDS->SetAttributesTranslation( OGRGeoJSONDataSource::eAtributesPreserve ); + pszOpt = CPLGetConfigOption("ATTRIBUTES_SKIP", NULL); + if( NULL != pszOpt && EQUALN(pszOpt, "YES", 3) ) + { + poDS->SetAttributesTranslation( + OGRGeoJSONDataSource::eAtributesSkip ); + } + +/* -------------------------------------------------------------------- */ +/* Open and start processing GeoJSON datasoruce to OGR objects. */ +/* -------------------------------------------------------------------- */ + if( !poDS->Open( poOpenInfo, nSrcType ) ) + { + delete poDS; + poDS= NULL; + } + + if( NULL != poDS && poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "GeoJSON Driver doesn't support update." ); + delete poDS; + return NULL; + } + + if( poDS != NULL && poDS->HasOtherPages() ) + { + const char* pszFSP = CSLFetchNameValue(poOpenInfo->papszOpenOptions, + "FEATURE_SERVER_PAGING"); + int bHasResultOffset = CPLURLGetValue(poOpenInfo->pszFilename, "resultOffset").size() > 0; + if( (!bHasResultOffset && (pszFSP == NULL || CSLTestBoolean(pszFSP))) || + (bHasResultOffset && pszFSP != NULL && CSLTestBoolean(pszFSP)) ) + { + return new OGRESRIFeatureServiceDataset(poOpenInfo->pszFilename, + poDS); + } + } + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRGeoJSONDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + OGRGeoJSONDataSource* poDS = new OGRGeoJSONDataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +static CPLErr OGRGeoJSONDriverDelete( const char *pszFilename ) +{ + if( VSIUnlink( pszFilename ) == 0 ) + { + return CE_None; + } + + CPLDebug( "GeoJSON", "Failed to delete \'%s\'", pszFilename); + + return CE_Failure; +} + +/************************************************************************/ +/* RegisterOGRGeoJSON() */ +/************************************************************************/ + +void RegisterOGRGeoJSON() +{ + if( !GDAL_CHECK_VERSION("OGR/GeoJSON driver") ) + return; + + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GeoJSON" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GeoJSON" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GeoJSON" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "json geojson topojson" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_geojson.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, ""); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String IntegerList Integer64List RealList StringList" ); + + poDriver->pfnOpen = OGRGeoJSONDriverOpen; + poDriver->pfnIdentify = OGRGeoJSONDriverIdentify; + poDriver->pfnCreate = OGRGeoJSONDriverCreate; + poDriver->pfnDelete = OGRGeoJSONDriverDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonlayer.cpp new file mode 100644 index 000000000..6a1e07374 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonlayer.cpp @@ -0,0 +1,260 @@ +/****************************************************************************** + * $Id: ogrgeojsonlayer.cpp 29111 2015-05-02 18:06:16Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implementation of OGRGeoJSONLayer class (OGR GeoJSON Driver). + * Author: Mateusz Loskot, mateusz@loskot.net + * + ****************************************************************************** + * Copyright (c) 2007, Mateusz Loskot + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include // for_each, find_if +#include // JSON-C +#include "ogr_geojson.h" + +/* Remove annoying warnings Microsoft Visual C++ */ +#if defined(_MSC_VER) +# pragma warning(disable:4512) +#endif + +/************************************************************************/ +/* STATIC MEMBERS DEFINITION */ +/************************************************************************/ + +const char* const OGRGeoJSONLayer::DefaultName = "OGRGeoJSON"; +const char* const OGRGeoJSONLayer::DefaultFIDColumn = "id"; +const OGRwkbGeometryType OGRGeoJSONLayer::DefaultGeometryType = wkbUnknown; + +/************************************************************************/ +/* OGRGeoJSONLayer */ +/************************************************************************/ + +OGRGeoJSONLayer::OGRGeoJSONLayer( const char* pszName, + OGRSpatialReference* poSRSIn, + OGRwkbGeometryType eGType, + CPL_UNUSED OGRGeoJSONDataSource* poDS ) + : iterCurrent_( seqFeatures_.end() ), /* poDS_( poDS ), */ poFeatureDefn_(new OGRFeatureDefn( pszName ) ) +{ + /* CPLAssert( NULL != poDS_ ); */ + CPLAssert( NULL != poFeatureDefn_ ); + + poFeatureDefn_->Reference(); + poFeatureDefn_->SetGeomType( eGType ); + if( poFeatureDefn_->GetGeomFieldCount() != 0 ) + poFeatureDefn_->GetGeomFieldDefn(0)->SetSpatialRef(poSRSIn); + SetDescription( poFeatureDefn_->GetName() ); +} + +/************************************************************************/ +/* ~OGRGeoJSONLayer */ +/************************************************************************/ + +OGRGeoJSONLayer::~OGRGeoJSONLayer() +{ + std::for_each(seqFeatures_.begin(), seqFeatures_.end(), + OGRFeature::DestroyFeature); + + if( NULL != poFeatureDefn_ ) + { + poFeatureDefn_->Release(); + } +} + +/************************************************************************/ +/* GetLayerDefn */ +/************************************************************************/ + +OGRFeatureDefn* OGRGeoJSONLayer::GetLayerDefn() +{ + return poFeatureDefn_; +} + +/************************************************************************/ +/* GetFeatureCount */ +/************************************************************************/ + +GIntBig OGRGeoJSONLayer::GetFeatureCount( int bForce ) +{ + if (m_poFilterGeom == NULL && m_poAttrQuery == NULL) + return static_cast( seqFeatures_.size() ); + else + return OGRLayer::GetFeatureCount(bForce); +} + +/************************************************************************/ +/* ResetReading */ +/************************************************************************/ + +void OGRGeoJSONLayer::ResetReading() +{ + iterCurrent_ = seqFeatures_.begin(); +} + +/************************************************************************/ +/* GetNextFeature */ +/************************************************************************/ + +OGRFeature* OGRGeoJSONLayer::GetNextFeature() +{ + while ( iterCurrent_ != seqFeatures_.end() ) + { + OGRFeature* poFeature = (*iterCurrent_); + CPLAssert( NULL != poFeature ); + ++iterCurrent_; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + OGRFeature* poFeatureCopy = poFeature->Clone(); + CPLAssert( NULL != poFeatureCopy ); + + if (poFeatureCopy->GetGeometryRef() != NULL && GetSpatialRef() != NULL) + { + poFeatureCopy->GetGeometryRef()->assignSpatialReference( GetSpatialRef() ); + } + + return poFeatureCopy; + } + } + + return NULL; +} + +/************************************************************************/ +/* TestCapability */ +/************************************************************************/ + +int OGRGeoJSONLayer::TestCapability( const char* pszCap ) +{ + if( EQUAL(pszCap, OLCFastFeatureCount) || + EQUAL(pszCap, OLCFastGetExtent) || + EQUAL(pszCap, OLCStringsAsUTF8) ) + { + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* GetFIDColumn */ +/************************************************************************/ + +const char* OGRGeoJSONLayer::GetFIDColumn() +{ + return sFIDColumn_.c_str(); +} + +/************************************************************************/ +/* SetFIDColumn */ +/************************************************************************/ + +void OGRGeoJSONLayer::SetFIDColumn( const char* pszFIDColumn ) +{ + sFIDColumn_ = pszFIDColumn; +} + +/************************************************************************/ +/* AddFeature */ +/************************************************************************/ + +void OGRGeoJSONLayer::AddFeature( OGRFeature* poFeature ) +{ + CPLAssert( NULL != poFeature ); + + // NOTE - mloskot: + // Features may not be sorted according to FID values. + + // TODO: Should we check if feature already exists? + // TODO: Think about sync operation, upload, etc. + + OGRFeature* poNewFeature = NULL; + poNewFeature = poFeature->Clone(); + + + if( -1 == poNewFeature->GetFID() ) + { + int nFID = static_cast(seqFeatures_.size()); + poNewFeature->SetFID( nFID ); + + // TODO - mlokot: We need to redesign creation of FID column + int nField = poNewFeature->GetFieldIndex( DefaultFIDColumn ); + if( -1 != nField && GetLayerDefn()->GetFieldDefn(nField)->GetType() == OFTInteger ) + { + poNewFeature->SetField( nField, nFID ); + } + } + + + if( (GIntBig)(int)poNewFeature->GetFID() != poNewFeature->GetFID() ) + SetMetadataItem(OLMD_FID64, "YES"); + + seqFeatures_.push_back( poNewFeature ); +} + +/************************************************************************/ +/* DetectGeometryType */ +/************************************************************************/ + +void OGRGeoJSONLayer::DetectGeometryType() +{ + if (poFeatureDefn_->GetGeomType() != wkbUnknown) + return; + + OGRwkbGeometryType featType = wkbUnknown; + OGRGeometry* poGeometry = NULL; + FeaturesSeq::const_iterator it = seqFeatures_.begin(); + FeaturesSeq::const_iterator end = seqFeatures_.end(); + + if( it != end ) + { + poGeometry = (*it)->GetGeometryRef(); + if( NULL != poGeometry ) + { + featType = poGeometry->getGeometryType(); + if( featType != poFeatureDefn_->GetGeomType() ) + { + poFeatureDefn_->SetGeomType( featType ); + } + } + ++it; + } + + while( it != end ) + { + poGeometry = (*it)->GetGeometryRef(); + if( NULL != poGeometry ) + { + featType = poGeometry->getGeometryType(); + if( featType != poFeatureDefn_->GetGeomType() ) + { + CPLDebug( "GeoJSON", + "Detected layer of mixed-geometry type features." ); + poFeatureDefn_->SetGeomType( DefaultGeometryType ); + break; + } + } + ++it; + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonreader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonreader.cpp new file mode 100644 index 000000000..1e9fcd578 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonreader.cpp @@ -0,0 +1,1781 @@ +/****************************************************************************** + * $Id: ogrgeojsonreader.cpp 29156 2015-05-05 10:19:17Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implementation of OGRGeoJSONReader class (OGR GeoJSON Driver). + * Author: Mateusz Loskot, mateusz@loskot.net + * + ****************************************************************************** + * Copyright (c) 2007, Mateusz Loskot + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogrgeojsonreader.h" +#include "ogrgeojsonutils.h" +#include "ogr_geojson.h" +#include // JSON-C +#include + +/************************************************************************/ +/* OGRGeoJSONReader */ +/************************************************************************/ + +OGRGeoJSONReader::OGRGeoJSONReader() + : poGJObject_( NULL ), + bGeometryPreserve_( true ), + bAttributesSkip_( false ), + bFlattenNestedAttributes_ (false), + chNestedAttributeSeparator_ (0), + bFlattenGeocouchSpatiallistFormat (-1), bFoundId (false), bFoundRev(false), bFoundTypeFeature(false), bIsGeocouchSpatiallistFormat(false) +{ + // Take a deep breath and get to work. +} + +/************************************************************************/ +/* ~OGRGeoJSONReader */ +/************************************************************************/ + +OGRGeoJSONReader::~OGRGeoJSONReader() +{ + if( NULL != poGJObject_ ) + { + json_object_put(poGJObject_); + } + + poGJObject_ = NULL; +} + +/************************************************************************/ +/* Parse */ +/************************************************************************/ + +OGRErr OGRGeoJSONReader::Parse( const char* pszText ) +{ + if( NULL != pszText ) + { + json_tokener* jstok = NULL; + json_object* jsobj = NULL; + + /* Skip UTF-8 BOM (#5630) */ + const GByte* pabyData = (const GByte*)pszText; + if( pabyData[0] == 0xEF && pabyData[1] == 0xBB && pabyData[2] == 0xBF ) + { + CPLDebug("GeoJSON", "Skip UTF-8 BOM"); + pszText += 3; + } + + jstok = json_tokener_new(); + jsobj = json_tokener_parse_ex(jstok, pszText, -1); + if( jstok->err != json_tokener_success) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GeoJSON parsing error: %s (at offset %d)", + json_tokener_error_desc(jstok->err), jstok->char_offset); + + json_tokener_free(jstok); + return OGRERR_CORRUPT_DATA; + } + json_tokener_free(jstok); + + /* JSON tree is shared for while lifetime of the reader object + * and will be released in the destructor. + */ + poGJObject_ = jsobj; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ReadLayers */ +/************************************************************************/ + +void OGRGeoJSONReader::ReadLayers( OGRGeoJSONDataSource* poDS ) +{ + if( NULL == poGJObject_ ) + { + CPLDebug( "GeoJSON", + "Missing parset GeoJSON data. Forgot to call Parse()?" ); + return; + } + + ReadLayer(poDS, OGRGeoJSONLayer::DefaultName, poGJObject_); +} + +/************************************************************************/ +/* ReadLayer */ +/************************************************************************/ + +void OGRGeoJSONReader::ReadLayer( OGRGeoJSONDataSource* poDS, + const char* pszName, + json_object* poObj ) +{ + GeoJSONObject::Type objType = OGRGeoJSONGetType( poObj ); + if( objType == GeoJSONObject::eUnknown ) + { + /* Check if the object contains key:value pairs where value */ + /* is a standard GeoJSON object. In which case, use key as the layer */ + /* name */ + if( json_type_object == json_object_get_type( poObj ) ) + { + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poObj, it ) + { + objType = OGRGeoJSONGetType( it.val ); + if( objType != GeoJSONObject::eUnknown ) + ReadLayer(poDS, it.key, it.val); + } + } + + /*CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognized GeoJSON structure." );*/ + + return; + } + + OGRSpatialReference* poSRS = NULL; + poSRS = OGRGeoJSONReadSpatialReference( poObj ); + if (poSRS == NULL ) { + // If there is none defined, we use 4326 + poSRS = new OGRSpatialReference(); + if( OGRERR_NONE != poSRS->importFromEPSG( 4326 ) ) + { + delete poSRS; + poSRS = NULL; + } + } + + OGRGeoJSONLayer* poLayer = new OGRGeoJSONLayer( pszName, poSRS, + OGRGeoJSONLayer::DefaultGeometryType, + poDS ); + if( poSRS != NULL ) + poSRS->Release(); + + if( !GenerateLayerDefn(poLayer, poObj) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer schema generation failed." ); + + delete poLayer; + return; + } + +/* -------------------------------------------------------------------- */ +/* Translate single geometry-only Feature object. */ +/* -------------------------------------------------------------------- */ + + if( GeoJSONObject::ePoint == objType + || GeoJSONObject::eMultiPoint == objType + || GeoJSONObject::eLineString == objType + || GeoJSONObject::eMultiLineString == objType + || GeoJSONObject::ePolygon == objType + || GeoJSONObject::eMultiPolygon == objType + || GeoJSONObject::eGeometryCollection == objType ) + { + OGRGeometry* poGeometry = NULL; + poGeometry = ReadGeometry( poObj ); + if( !AddFeature( poLayer, poGeometry ) ) + { + CPLDebug( "GeoJSON", + "Translation of single geometry failed." ); + delete poLayer; + return; + } + } +/* -------------------------------------------------------------------- */ +/* Translate single but complete Feature object. */ +/* -------------------------------------------------------------------- */ + else if( GeoJSONObject::eFeature == objType ) + { + OGRFeature* poFeature = NULL; + poFeature = ReadFeature( poLayer, poObj ); + if( !AddFeature( poLayer, poFeature ) ) + { + CPLDebug( "GeoJSON", + "Translation of single feature failed." ); + + delete poLayer; + return; + } + } +/* -------------------------------------------------------------------- */ +/* Translate multi-feature FeatureCollection object. */ +/* -------------------------------------------------------------------- */ + else if( GeoJSONObject::eFeatureCollection == objType ) + { + ReadFeatureCollection( poLayer, poObj ); + } + + CPLErrorReset(); + + poDS->AddLayer(poLayer); +} + +/************************************************************************/ +/* OGRGeoJSONReadSpatialReference */ +/************************************************************************/ + +OGRSpatialReference* OGRGeoJSONReadSpatialReference( json_object* poObj) { + +/* -------------------------------------------------------------------- */ +/* Read spatial reference definition. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference* poSRS = NULL; + + json_object* poObjSrs = OGRGeoJSONFindMemberByName( poObj, "crs" ); + if( NULL != poObjSrs ) + { + json_object* poObjSrsType = OGRGeoJSONFindMemberByName( poObjSrs, "type" ); + if (poObjSrsType == NULL) + return NULL; + + const char* pszSrsType = json_object_get_string( poObjSrsType ); + + // TODO: Add URL and URN types support + if( EQUALN( pszSrsType, "NAME", 4 ) ) + { + json_object* poObjSrsProps = OGRGeoJSONFindMemberByName( poObjSrs, "properties" ); + if (poObjSrsProps == NULL) + return NULL; + + json_object* poNameURL = OGRGeoJSONFindMemberByName( poObjSrsProps, "name" ); + if (poNameURL == NULL) + return NULL; + + const char* pszName = json_object_get_string( poNameURL ); + + poSRS = new OGRSpatialReference(); + if( OGRERR_NONE != poSRS->SetFromUserInput( pszName ) ) + { + delete poSRS; + poSRS = NULL; + } + } + + if( EQUALN( pszSrsType, "EPSG", 4 ) ) + { + json_object* poObjSrsProps = OGRGeoJSONFindMemberByName( poObjSrs, "properties" ); + if (poObjSrsProps == NULL) + return NULL; + + json_object* poObjCode = OGRGeoJSONFindMemberByName( poObjSrsProps, "code" ); + if (poObjCode == NULL) + return NULL; + + int nEPSG = json_object_get_int( poObjCode ); + + poSRS = new OGRSpatialReference(); + if( OGRERR_NONE != poSRS->importFromEPSG( nEPSG ) ) + { + delete poSRS; + poSRS = NULL; + } + } + + if( EQUALN( pszSrsType, "URL", 3 ) || EQUALN( pszSrsType, "LINK", 4 ) ) + { + json_object* poObjSrsProps = OGRGeoJSONFindMemberByName( poObjSrs, "properties" ); + if (poObjSrsProps == NULL) + return NULL; + + json_object* poObjURL = OGRGeoJSONFindMemberByName( poObjSrsProps, "url" ); + + if (NULL == poObjURL) { + poObjURL = OGRGeoJSONFindMemberByName( poObjSrsProps, "href" ); + } + if (poObjURL == NULL) + return NULL; + + const char* pszURL = json_object_get_string( poObjURL ); + + poSRS = new OGRSpatialReference(); + if( OGRERR_NONE != poSRS->importFromUrl( pszURL ) ) + { + delete poSRS; + poSRS = NULL; + + } + } + + + if( EQUAL( pszSrsType, "OGC" ) ) + { + json_object* poObjSrsProps = OGRGeoJSONFindMemberByName( poObjSrs, "properties" ); + if (poObjSrsProps == NULL) + return NULL; + + json_object* poObjURN = OGRGeoJSONFindMemberByName( poObjSrsProps, "urn" ); + if (poObjURN == NULL) + return NULL; + + poSRS = new OGRSpatialReference(); + if( OGRERR_NONE != poSRS->importFromURN( json_object_get_string(poObjURN) ) ) + { + delete poSRS; + poSRS = NULL; + } + } + } + + /* Strip AXIS, since geojson has (easting, northing) / (longitude, latitude) order. */ + /* According to http://www.geojson.org/geojson-spec.html#id2 : "Point coordinates are in x, y order */ + /* (easting, northing for projected coordinates, longitude, latitude for geographic coordinates)" */ + if( poSRS != NULL ) + { + OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode( "GEOGCS" ); + if( poGEOGCS != NULL ) + poGEOGCS->StripNodes( "AXIS" ); + } + + return poSRS; +} +/************************************************************************/ +/* SetPreserveGeometryType */ +/************************************************************************/ + +void OGRGeoJSONReader::SetPreserveGeometryType( bool bPreserve ) +{ + bGeometryPreserve_ = bPreserve; +} + +/************************************************************************/ +/* SetSkipAttributes */ +/************************************************************************/ + +void OGRGeoJSONReader::SetSkipAttributes( bool bSkip ) +{ + bAttributesSkip_ = bSkip; +} + +/************************************************************************/ +/* SetSkipAttributes */ +/************************************************************************/ + +void OGRGeoJSONReader::SetFlattenNestedAttributes( bool bFlatten, char chSeparator ) +{ + bFlattenNestedAttributes_ = bFlatten; + chNestedAttributeSeparator_ = chSeparator; +} + +/************************************************************************/ +/* GenerateLayerDefn() */ +/************************************************************************/ + +bool OGRGeoJSONReader::GenerateLayerDefn( OGRGeoJSONLayer* poLayer, json_object* poGJObject ) +{ + CPLAssert( NULL != poGJObject ); + CPLAssert( NULL != poLayer->GetLayerDefn() ); + CPLAssert( 0 == poLayer->GetLayerDefn()->GetFieldCount() ); + + bool bSuccess = true; + + if( bAttributesSkip_ ) + return true; + +/* -------------------------------------------------------------------- */ +/* Scan all features and generate layer definition. */ +/* -------------------------------------------------------------------- */ + GeoJSONObject::Type objType = OGRGeoJSONGetType( poGJObject ); + if( GeoJSONObject::eFeature == objType ) + { + bSuccess = GenerateFeatureDefn( poLayer, poGJObject ); + } + else if( GeoJSONObject::eFeatureCollection == objType ) + { + json_object* poObjFeatures = NULL; + poObjFeatures = OGRGeoJSONFindMemberByName( poGJObject, "features" ); + if( NULL != poObjFeatures + && json_type_array == json_object_get_type( poObjFeatures ) ) + { + json_object* poObjFeature = NULL; + const int nFeatures = json_object_array_length( poObjFeatures ); + for( int i = 0; i < nFeatures; ++i ) + { + poObjFeature = json_object_array_get_idx( poObjFeatures, i ); + if( !GenerateFeatureDefn( poLayer, poObjFeature ) ) + { + CPLDebug( "GeoJSON", "Create feature schema failure." ); + bSuccess = false; + } + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid FeatureCollection object. " + "Missing \'features\' member." ); + bSuccess = false; + } + } + +/* -------------------------------------------------------------------- */ +/* Validate and add FID column if necessary. */ +/* -------------------------------------------------------------------- */ + OGRFeatureDefn* poLayerDefn = poLayer->GetLayerDefn(); + CPLAssert( NULL != poLayerDefn ); + + /* bool bHasFID = false; */ + + for( int i = 0; i < poLayerDefn->GetFieldCount(); ++i ) + { + OGRFieldDefn* poDefn = poLayerDefn->GetFieldDefn(i); + if( EQUAL( poDefn->GetNameRef(), OGRGeoJSONLayer::DefaultFIDColumn ) + && (OFTInteger == poDefn->GetType() || OFTInteger64 == poDefn->GetType()) ) + { + poLayer->SetFIDColumn( poDefn->GetNameRef() ); + /* bHasFID = true; */ + break; + } + } + + // TODO - mloskot: This is wrong! We want to add only FID field if + // found in source layer (by default name or by FID_PROPERTY= specifier, + // the latter has to be implemented). + /* + if( !bHasFID ) + { + OGRFieldDefn fldDefn( OGRGeoJSONLayer::DefaultFIDColumn, OFTInteger ); + poLayerDefn->AddFieldDefn( &fldDefn ); + poLayer_->SetFIDColumn( fldDefn.GetNameRef() ); + } + */ + + return bSuccess; +} + +/************************************************************************/ +/* OGRGeoJSONReaderAddNewField() */ +/************************************************************************/ + +void OGRGeoJSONReaderAddOrUpdateField(OGRFeatureDefn* poDefn, + const char* pszKey, + json_object* poVal, + bool bFlattenNestedAttributes, + char chNestedAttributeSeparator) +{ + if( bFlattenNestedAttributes && + poVal != NULL && json_object_get_type(poVal) == json_type_object ) + { + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poVal, it ) + { + char szSeparator[2]; + szSeparator[0] = chNestedAttributeSeparator; + szSeparator[1] = 0; + CPLString osAttrName(CPLSPrintf("%s%s%s", pszKey, szSeparator, + it.key)); + if( it.val != NULL && json_object_get_type(it.val) == json_type_object ) + { + OGRGeoJSONReaderAddOrUpdateField(poDefn, osAttrName, it.val, + TRUE, chNestedAttributeSeparator); + } + else + { + OGRGeoJSONReaderAddOrUpdateField(poDefn, osAttrName, it.val, FALSE, 0); + } + } + return; + } + + int nIndex = poDefn->GetFieldIndex(pszKey); + if( nIndex < 0 ) + { + OGRFieldSubType eSubType; + OGRFieldDefn fldDefn( pszKey, + GeoJSONPropertyToFieldType( poVal, eSubType ) ); + fldDefn.SetSubType(eSubType); + if( eSubType == OFSTBoolean ) + fldDefn.SetWidth(1); + if( fldDefn.GetType() == OFTString ) + { + fldDefn.SetType(GeoJSONStringPropertyToFieldType( poVal )); + } + poDefn->AddFieldDefn( &fldDefn ); + } + else + { + OGRFieldDefn* poFDefn = poDefn->GetFieldDefn(nIndex); + OGRFieldType eType = poFDefn->GetType(); + if( eType == OFTInteger ) + { + OGRFieldSubType eSubType; + OGRFieldType eNewType = GeoJSONPropertyToFieldType( poVal, eSubType ); + if( eNewType == OFTInteger && + poFDefn->GetSubType() == OFSTBoolean && eSubType != OFSTBoolean ) + { + poFDefn->SetSubType(OFSTNone); + } + else if( eNewType == OFTInteger64 || eNewType == OFTReal || eNewType == OFTString ) + { + poFDefn->SetType(eNewType); + poFDefn->SetSubType(OFSTNone); + } + } + else if( eType == OFTInteger64 ) + { + OGRFieldSubType eSubType; + OGRFieldType eNewType = GeoJSONPropertyToFieldType( poVal, eSubType ); + if( eNewType == OFTReal || eNewType == OFTString ) + { + poFDefn->SetType(eNewType); + poFDefn->SetSubType(OFSTNone); + } + } + else if( eType == OFTIntegerList || eType == OFTInteger64List ) + { + OGRFieldSubType eSubType; + OGRFieldType eNewType = GeoJSONPropertyToFieldType( poVal, eSubType ); + if( eNewType == OFTInteger64List || eNewType == OFTRealList || eNewType == OFTStringList ) + poFDefn->SetType(eNewType); + } + else if( eType == OFTRealList ) + { + OGRFieldSubType eSubType; + OGRFieldType eNewType = GeoJSONPropertyToFieldType( poVal, eSubType ); + if( eNewType == OFTStringList ) + poFDefn->SetType(eNewType); + } + else if( eType == OFTDate || eType == OFTTime || eType == OFTDateTime ) + { + OGRFieldSubType eSubType; + OGRFieldType eNewType = GeoJSONPropertyToFieldType( poVal, eSubType ); + if( eNewType == OFTString ) + eNewType = GeoJSONStringPropertyToFieldType( poVal ); + if( eType != eNewType ) + { + if( eType == OFTDate && eNewType == OFTDateTime ) + { + poFDefn->SetType(OFTDateTime); + } + else if( !(eType == OFTDateTime && eNewType == OFTDate) ) + { + poFDefn->SetType(OFTString); + } + } + } + } +} + +/************************************************************************/ +/* GenerateFeatureDefn() */ +/************************************************************************/ +bool OGRGeoJSONReader::GenerateFeatureDefn( OGRGeoJSONLayer* poLayer, json_object* poObj ) +{ + OGRFeatureDefn* poDefn = poLayer->GetLayerDefn(); + CPLAssert( NULL != poDefn ); + + bool bSuccess = false; + +/* -------------------------------------------------------------------- */ +/* Read collection of properties. */ +/* -------------------------------------------------------------------- */ + json_object* poObjProps = NULL; + poObjProps = OGRGeoJSONFindMemberByName( poObj, "properties" ); + + // If there's a top-level id of type string, and no properties.id, then + // declare a id field + if( poDefn->GetFieldIndex( "id" ) < 0 ) + { + json_object* poObjId = OGRGeoJSONFindMemberByName( poObj, "id" ); + if( poObjId && json_object_get_type(poObjId) == json_type_string ) + { + int bHasRegularIdProp = FALSE; + if( NULL != poObjProps && + json_object_get_type(poObjProps) == json_type_object ) + { + bHasRegularIdProp = (json_object_object_get(poObjProps, "id") != NULL); + } + if( !bHasRegularIdProp ) + { + OGRFieldDefn fldDefn( "id", OFTString ); + poDefn->AddFieldDefn(&fldDefn); + } + } + } + + if( NULL != poObjProps && + json_object_get_type(poObjProps) == json_type_object ) + { + if (bIsGeocouchSpatiallistFormat) + { + poObjProps = json_object_object_get(poObjProps, "properties"); + if( NULL == poObjProps || + json_object_get_type(poObjProps) != json_type_object ) + { + return true; + } + } + + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poObjProps, it ) + { + int nFldIndex = poDefn->GetFieldIndex( it.key ); + if( -1 == nFldIndex ) + { + /* Detect the special kind of GeoJSON output by a spatiallist of GeoCouch */ + /* such as http://gd.iriscouch.com/cphosm/_design/geo/_rewrite/data?bbox=12.53%2C55.73%2C12.54%2C55.73 */ + if (strcmp(it.key, "_id") == 0) + bFoundId = true; + else if (bFoundId && strcmp(it.key, "_rev") == 0) + bFoundRev = true; + else if (bFoundRev && strcmp(it.key, "type") == 0 && + it.val != NULL && json_object_get_type(it.val) == json_type_string && + strcmp(json_object_get_string(it.val), "Feature") == 0) + bFoundTypeFeature = true; + else if (bFoundTypeFeature && strcmp(it.key, "properties") == 0 && + it.val != NULL && json_object_get_type(it.val) == json_type_object) + { + if (bFlattenGeocouchSpatiallistFormat < 0) + bFlattenGeocouchSpatiallistFormat = CSLTestBoolean( + CPLGetConfigOption("GEOJSON_FLATTEN_GEOCOUCH", "TRUE")); + if (bFlattenGeocouchSpatiallistFormat) + { + poDefn->DeleteFieldDefn(poDefn->GetFieldIndex("type")); + bIsGeocouchSpatiallistFormat = true; + return GenerateFeatureDefn(poLayer, poObj); + } + } + + } + OGRGeoJSONReaderAddOrUpdateField(poDefn, it.key, it.val, + bFlattenNestedAttributes_, + chNestedAttributeSeparator_); + } + + bSuccess = true; // SUCCESS + } + else + { + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poObj, it ) + { + if( strcmp(it.key, "type") != 0 && + strcmp(it.key, "geometry") != 0 && + strcmp(it.key, "centroid") != 0 && + strcmp(it.key, "bbox") != 0 && + strcmp(it.key, "center") != 0 ) + { + int nFldIndex = poDefn->GetFieldIndex( it.key ); + if( -1 == nFldIndex ) + { + OGRFieldDefn fldDefn( it.key, OFTString ); + poDefn->AddFieldDefn( &fldDefn ); + } + } + } + + bSuccess = true; // SUCCESS + /*CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Feature object. " + "Missing \'properties\' member." );*/ + } + + return bSuccess; +} + +/************************************************************************/ +/* AddFeature */ +/************************************************************************/ + +bool OGRGeoJSONReader::AddFeature( OGRGeoJSONLayer* poLayer, OGRGeometry* poGeometry ) +{ + bool bAdded = false; + + // TODO: Should we check if geometry is of type of + // wkbGeometryCollection ? + + if( NULL != poGeometry ) + { + OGRFeature* poFeature = NULL; + poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + poFeature->SetGeometryDirectly( poGeometry ); + + bAdded = AddFeature( poLayer, poFeature ); + } + + return bAdded; +} + +/************************************************************************/ +/* AddFeature */ +/************************************************************************/ + +bool OGRGeoJSONReader::AddFeature( OGRGeoJSONLayer* poLayer, OGRFeature* poFeature ) +{ + bool bAdded = false; + + if( NULL != poFeature ) + { + poLayer->AddFeature( poFeature ); + bAdded = true; + delete poFeature; + } + + return bAdded; +} + +/************************************************************************/ +/* ReadGeometry */ +/************************************************************************/ + +OGRGeometry* OGRGeoJSONReader::ReadGeometry( json_object* poObj ) +{ + OGRGeometry* poGeometry = NULL; + + poGeometry = OGRGeoJSONReadGeometry( poObj ); + +/* -------------------------------------------------------------------- */ +/* Wrap geometry with GeometryCollection as a common denominator. */ +/* Sometimes a GeoJSON text may consist of objects of different */ +/* geometry types. Users may request wrapping all geometries with */ +/* OGRGeometryCollection type by using option */ +/* GEOMETRY_AS_COLLECTION=NO|YES (NO is default). */ +/* -------------------------------------------------------------------- */ + if( NULL != poGeometry ) + { + if( !bGeometryPreserve_ + && wkbGeometryCollection != poGeometry->getGeometryType() ) + { + OGRGeometryCollection* poMetaGeometry = NULL; + poMetaGeometry = new OGRGeometryCollection(); + poMetaGeometry->addGeometryDirectly( poGeometry ); + return poMetaGeometry; + } + } + + return poGeometry; +} + +/************************************************************************/ +/* OGRGeoJSONReaderSetFieldNestedAttribute() */ +/************************************************************************/ + +static void OGRGeoJSONReaderSetFieldNestedAttribute(OGRLayer* poLayer, + OGRFeature* poFeature, + const char* pszAttrPrefix, + char chSeparator, + json_object* poVal) +{ + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poVal, it ) + { + char szSeparator[2]; + szSeparator[0] = chSeparator; + szSeparator[1] = 0; + CPLString osAttrName(CPLSPrintf("%s%s%s", pszAttrPrefix, szSeparator, + it.key)); + if( it.val != NULL && json_object_get_type(it.val) == json_type_object ) + { + OGRGeoJSONReaderSetFieldNestedAttribute(poLayer, poFeature, + osAttrName, chSeparator, + it.val); + } + else + { + int nField = poFeature->GetFieldIndex(osAttrName); + OGRGeoJSONReaderSetField(poLayer, poFeature, nField, + osAttrName, it.val, FALSE, 0); + } + } +} + +/************************************************************************/ +/* OGRGeoJSONReaderSetField() */ +/************************************************************************/ + +void OGRGeoJSONReaderSetField(OGRLayer* poLayer, + OGRFeature* poFeature, + int nField, + const char* pszAttrPrefix, + json_object* poVal, + bool bFlattenNestedAttributes, + char chNestedAttributeSeparator) +{ + if( bFlattenNestedAttributes && + poVal != NULL && json_object_get_type(poVal) == json_type_object ) + { + OGRGeoJSONReaderSetFieldNestedAttribute(poLayer, + poFeature, + pszAttrPrefix, + chNestedAttributeSeparator, + poVal); + return ; + } + + OGRFieldDefn* poFieldDefn = poFeature->GetFieldDefnRef(nField); + CPLAssert( NULL != poFieldDefn ); + OGRFieldType eType = poFieldDefn->GetType(); + + if( poVal == NULL) + { + /* nothing to do */ + } + else if( OFTInteger == eType ) + { + poFeature->SetField( nField, json_object_get_int(poVal) ); + + /* Check if FID available and set correct value. */ + if( EQUAL( poFieldDefn->GetNameRef(), poLayer->GetFIDColumn() ) ) + poFeature->SetFID( json_object_get_int(poVal) ); + } + else if( OFTInteger64 == eType ) + { + poFeature->SetField( nField, (GIntBig)json_object_get_int64(poVal) ); + + /* Check if FID available and set correct value. */ + if( EQUAL( poFieldDefn->GetNameRef(), poLayer->GetFIDColumn() ) ) + poFeature->SetFID( (GIntBig)json_object_get_int64(poVal) ); + } + else if( OFTReal == eType ) + { + poFeature->SetField( nField, json_object_get_double(poVal) ); + } + else if( OFTIntegerList == eType ) + { + if ( json_object_get_type(poVal) == json_type_array ) + { + int nLength = json_object_array_length(poVal); + int* panVal = (int*)CPLMalloc(sizeof(int) * nLength); + for(int i=0;iSetField( nField, nLength, panVal ); + CPLFree(panVal); + } + } + else if( OFTInteger64List == eType ) + { + if ( json_object_get_type(poVal) == json_type_array ) + { + int nLength = json_object_array_length(poVal); + GIntBig* panVal = (GIntBig*)CPLMalloc(sizeof(GIntBig) * nLength); + for(int i=0;iSetField( nField, nLength, panVal ); + CPLFree(panVal); + } + } + else if( OFTRealList == eType ) + { + if ( json_object_get_type(poVal) == json_type_array ) + { + int nLength = json_object_array_length(poVal); + double* padfVal = (double*)CPLMalloc(sizeof(double) * nLength); + for(int i=0;iSetField( nField, nLength, padfVal ); + CPLFree(padfVal); + } + } + else if( OFTStringList == eType ) + { + if ( json_object_get_type(poVal) == json_type_array ) + { + int nLength = json_object_array_length(poVal); + char** papszVal = (char**)CPLMalloc(sizeof(char*) * (nLength+1)); + int i; + for(i=0;iSetField( nField, papszVal ); + CSLDestroy(papszVal); + } + } + else + { + poFeature->SetField( nField, json_object_get_string(poVal) ); + } +} + +/************************************************************************/ +/* ReadFeature() */ +/************************************************************************/ + +OGRFeature* OGRGeoJSONReader::ReadFeature( OGRGeoJSONLayer* poLayer, json_object* poObj ) +{ + CPLAssert( NULL != poObj ); + + OGRFeature* poFeature = NULL; + poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + +/* -------------------------------------------------------------------- */ +/* Translate GeoJSON "properties" object to feature attributes. */ +/* -------------------------------------------------------------------- */ + CPLAssert( NULL != poFeature ); + + json_object* poObjProps = NULL; + poObjProps = OGRGeoJSONFindMemberByName( poObj, "properties" ); + if( !bAttributesSkip_ && NULL != poObjProps && + json_object_get_type(poObjProps) == json_type_object ) + { + if (bIsGeocouchSpatiallistFormat) + { + json_object* poId = json_object_object_get(poObjProps, "_id"); + if (poId != NULL && json_object_get_type(poId) == json_type_string) + poFeature->SetField( "_id", json_object_get_string(poId) ); + + json_object* poRev = json_object_object_get(poObjProps, "_rev"); + if (poRev != NULL && json_object_get_type(poRev) == json_type_string) + poFeature->SetField( "_rev", json_object_get_string(poRev) ); + + poObjProps = json_object_object_get(poObjProps, "properties"); + if( NULL == poObjProps || + json_object_get_type(poObjProps) != json_type_object ) + { + return poFeature; + } + } + + int nField = -1; + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poObjProps, it ) + { + nField = poFeature->GetFieldIndex(it.key); + OGRGeoJSONReaderSetField(poLayer, poFeature, nField, it.key, it.val, + bFlattenNestedAttributes_, + chNestedAttributeSeparator_); + } + } + + if( !bAttributesSkip_ && NULL == poObjProps ) + { + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poObj, it ) + { + int nFldIndex = poFeature->GetFieldIndex(it.key); + if( nFldIndex >= 0 ) + { + poFeature->SetField(nFldIndex, json_object_get_string(it.val) ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* If FID not set, try to use feature-level ID if available */ +/* and of integral type. Otherwise, leave unset (-1) then index */ +/* in features sequence will be used as FID. */ +/* -------------------------------------------------------------------- */ + if( -1 == poFeature->GetFID() ) + { + json_object* poObjId = NULL; + OGRFieldSubType eSubType; + poObjId = OGRGeoJSONFindMemberByName( poObj, OGRGeoJSONLayer::DefaultFIDColumn ); + if( NULL != poObjId + && EQUAL( OGRGeoJSONLayer::DefaultFIDColumn, poLayer->GetFIDColumn() ) + && (OFTInteger == GeoJSONPropertyToFieldType( poObjId, eSubType ) || + OFTInteger64 == GeoJSONPropertyToFieldType( poObjId, eSubType )) ) + { + poFeature->SetFID( (GIntBig)json_object_get_int64( poObjId ) ); + int nField = poFeature->GetFieldIndex( poLayer->GetFIDColumn() ); + if( -1 != nField ) + poFeature->SetField( nField, poFeature->GetFID() ); + } + } + + if( -1 == poFeature->GetFID() ) + { + json_object* poObjId = OGRGeoJSONFindMemberByName( poObj, "id" ); + if (poObjId != NULL && json_object_get_type(poObjId) == json_type_int) + poFeature->SetFID( (GIntBig)json_object_get_int64( poObjId ) ); + else if (poObjId != NULL && json_object_get_type(poObjId) == json_type_string && + !poFeature->IsFieldSet(poFeature->GetFieldIndex("id"))) + poFeature->SetField( "id", json_object_get_string(poObjId) ); + } + +/* -------------------------------------------------------------------- */ +/* Translate geometry sub-object of GeoJSON Feature. */ +/* -------------------------------------------------------------------- */ + json_object* poObjGeom = NULL; + + json_object* poTmp = poObj; + + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC(poTmp, it) + { + if( EQUAL( it.key, "geometry" ) ) { + if (it.val != NULL) + poObjGeom = it.val; + // we're done. They had 'geometry':null + else + return poFeature; + } + } + + if( NULL != poObjGeom ) + { + // NOTE: If geometry can not be parsed or read correctly + // then NULL geometry is assigned to a feature and + // geometry type for layer is classified as wkbUnknown. + OGRGeometry* poGeometry = ReadGeometry( poObjGeom ); + if( NULL != poGeometry ) + { + poFeature->SetGeometryDirectly( poGeometry ); + } + } + else + { + static int bWarned = FALSE; + if( !bWarned ) + { + bWarned = TRUE; + CPLDebug("GeoJSON", "Non conformant Feature object. Missing \'geometry\' member." ); + } + } + + return poFeature; +} + +/************************************************************************/ +/* ReadFeatureCollection() */ +/************************************************************************/ + +void +OGRGeoJSONReader::ReadFeatureCollection( OGRGeoJSONLayer* poLayer, json_object* poObj ) +{ + json_object* poObjFeatures = NULL; + poObjFeatures = OGRGeoJSONFindMemberByName( poObj, "features" ); + if( NULL == poObjFeatures ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid FeatureCollection object. " + "Missing \'features\' member." ); + return; + } + + if( json_type_array == json_object_get_type( poObjFeatures ) ) + { + /* bool bAdded = false; */ + OGRFeature* poFeature = NULL; + json_object* poObjFeature = NULL; + + const int nFeatures = json_object_array_length( poObjFeatures ); + for( int i = 0; i < nFeatures; ++i ) + { + poObjFeature = json_object_array_get_idx( poObjFeatures, i ); + poFeature = ReadFeature( poLayer, poObjFeature ); + /* bAdded = */ AddFeature( poLayer, poFeature ); + //CPLAssert( bAdded ); + } + //CPLAssert( nFeatures == poLayer_->GetFeatureCount() ); + } +} + +/************************************************************************/ +/* OGRGeoJSONFindMemberByName */ +/************************************************************************/ + +json_object* OGRGeoJSONFindMemberByName( json_object* poObj, + const char* pszName ) +{ + if( NULL == pszName || NULL == poObj) + return NULL; + + json_object* poTmp = poObj; + + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + if( NULL != json_object_get_object(poTmp) && + NULL != json_object_get_object(poTmp)->head ) + { + for( it.entry = json_object_get_object(poTmp)->head; + ( it.entry ? + ( it.key = (char*)it.entry->k, + it.val = (json_object*)it.entry->v, it.entry) : 0); + it.entry = it.entry->next) + { + if( EQUAL( it.key, pszName ) ) + return it.val; + } + } + + return NULL; +} + +/************************************************************************/ +/* OGRGeoJSONGetType */ +/************************************************************************/ + +GeoJSONObject::Type OGRGeoJSONGetType( json_object* poObj ) +{ + if( NULL == poObj ) + return GeoJSONObject::eUnknown; + + json_object* poObjType = NULL; + poObjType = OGRGeoJSONFindMemberByName( poObj, "type" ); + if( NULL == poObjType ) + return GeoJSONObject::eUnknown; + + const char* name = json_object_get_string( poObjType ); + if( EQUAL( name, "Point" ) ) + return GeoJSONObject::ePoint; + else if( EQUAL( name, "LineString" ) ) + return GeoJSONObject::eLineString; + else if( EQUAL( name, "Polygon" ) ) + return GeoJSONObject::ePolygon; + else if( EQUAL( name, "MultiPoint" ) ) + return GeoJSONObject::eMultiPoint; + else if( EQUAL( name, "MultiLineString" ) ) + return GeoJSONObject::eMultiLineString; + else if( EQUAL( name, "MultiPolygon" ) ) + return GeoJSONObject::eMultiPolygon; + else if( EQUAL( name, "GeometryCollection" ) ) + return GeoJSONObject::eGeometryCollection; + else if( EQUAL( name, "Feature" ) ) + return GeoJSONObject::eFeature; + else if( EQUAL( name, "FeatureCollection" ) ) + return GeoJSONObject::eFeatureCollection; + else + return GeoJSONObject::eUnknown; +} + +/************************************************************************/ +/* OGRGeoJSONReadGeometry */ +/************************************************************************/ + +OGRGeometry* OGRGeoJSONReadGeometry( json_object* poObj ) +{ + OGRGeometry* poGeometry = NULL; + + GeoJSONObject::Type objType = OGRGeoJSONGetType( poObj ); + if( GeoJSONObject::ePoint == objType ) + poGeometry = OGRGeoJSONReadPoint( poObj ); + else if( GeoJSONObject::eMultiPoint == objType ) + poGeometry = OGRGeoJSONReadMultiPoint( poObj ); + else if( GeoJSONObject::eLineString == objType ) + poGeometry = OGRGeoJSONReadLineString( poObj ); + else if( GeoJSONObject::eMultiLineString == objType ) + poGeometry = OGRGeoJSONReadMultiLineString( poObj ); + else if( GeoJSONObject::ePolygon == objType ) + poGeometry = OGRGeoJSONReadPolygon( poObj ); + else if( GeoJSONObject::eMultiPolygon == objType ) + poGeometry = OGRGeoJSONReadMultiPolygon( poObj ); + else if( GeoJSONObject::eGeometryCollection == objType ) + poGeometry = OGRGeoJSONReadGeometryCollection( poObj ); + else + { + CPLDebug( "GeoJSON", + "Unsupported geometry type detected. " + "Feature gets NULL geometry assigned." ); + } + // If we have a crs object in the current object, let's try and + // set it too. + + json_object* poObjSrs = OGRGeoJSONFindMemberByName( poObj, "crs" ); + if (poGeometry != NULL && poObjSrs != NULL) { + OGRSpatialReference* poSRS = OGRGeoJSONReadSpatialReference(poObj); + if (poSRS != NULL) { + poGeometry->assignSpatialReference(poSRS); + poSRS->Release(); + } + } + return poGeometry; +} + +/************************************************************************/ +/* OGRGeoJSONReadRawPoint */ +/************************************************************************/ + +bool OGRGeoJSONReadRawPoint( json_object* poObj, OGRPoint& point ) +{ + CPLAssert( NULL != poObj ); + + if( json_type_array == json_object_get_type( poObj ) ) + { + const int nSize = json_object_array_length( poObj ); + int iType = 0; + + if( nSize != GeoJSONObject::eMinCoordinateDimension + && nSize != GeoJSONObject::eMaxCoordinateDimension ) + { + CPLDebug( "GeoJSON", + "Invalid coord dimension. Only 2D and 3D supported." ); + return false; + } + + json_object* poObjCoord = NULL; + + // Read X coordinate + poObjCoord = json_object_array_get_idx( poObj, 0 ); + if (poObjCoord == NULL) + { + CPLDebug( "GeoJSON", "Point: got null object." ); + return false; + } + + iType = json_object_get_type(poObjCoord); + if ( (json_type_double != iType) && (json_type_int != iType) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid X coordinate. Type is not double or integer for \'%s\'.", + json_object_to_json_string(poObj) ); + return false; + } + + if (iType == json_type_double) + point.setX(json_object_get_double( poObjCoord )); + else + point.setX(json_object_get_int( poObjCoord )); + + // Read Y coordiante + poObjCoord = json_object_array_get_idx( poObj, 1 ); + if (poObjCoord == NULL) + { + CPLDebug( "GeoJSON", "Point: got null object." ); + return false; + } + + iType = json_object_get_type(poObjCoord); + if ( (json_type_double != iType) && (json_type_int != iType) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Y coordinate. Type is not double or integer for \'%s\'.", + json_object_to_json_string(poObj) ); + return false; + } + + if (iType == json_type_double) + point.setY(json_object_get_double( poObjCoord )); + else + point.setY(json_object_get_int( poObjCoord )); + + // Read Z coordinate + if( nSize == GeoJSONObject::eMaxCoordinateDimension ) + { + // Don't *expect* mixed-dimension geometries, although the + // spec doesn't explicitly forbid this. + poObjCoord = json_object_array_get_idx( poObj, 2 ); + if (poObjCoord == NULL) + { + CPLDebug( "GeoJSON", "Point: got null object." ); + return false; + } + + iType = json_object_get_type(poObjCoord); + if ( (json_type_double != iType) && (json_type_int != iType) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Z coordinate. Type is not double or integer for \'%s\'.", + json_object_to_json_string(poObj) ); + return false; + } + + if (iType == json_type_double) + point.setZ(json_object_get_double( poObjCoord )); + else + point.setZ(json_object_get_int( poObjCoord )); + } + else + { + point.flattenTo2D(); + } + return true; + } + + return false; +} + +/************************************************************************/ +/* OGRGeoJSONReadPoint */ +/************************************************************************/ + +OGRPoint* OGRGeoJSONReadPoint( json_object* poObj ) +{ + CPLAssert( NULL != poObj ); + + json_object* poObjCoords = NULL; + poObjCoords = OGRGeoJSONFindMemberByName( poObj, "coordinates" ); + if( NULL == poObjCoords ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Point object. Missing \'coordinates\' member." ); + return NULL; + } + + OGRPoint* poPoint = new OGRPoint(); + if( !OGRGeoJSONReadRawPoint( poObjCoords, *poPoint ) ) + { + CPLDebug( "GeoJSON", "Point: raw point parsing failure." ); + delete poPoint; + return NULL; + } + + return poPoint; +} + +/************************************************************************/ +/* OGRGeoJSONReadMultiPoint */ +/************************************************************************/ + +OGRMultiPoint* OGRGeoJSONReadMultiPoint( json_object* poObj ) +{ + CPLAssert( NULL != poObj ); + + OGRMultiPoint* poMultiPoint = NULL; + + json_object* poObjPoints = NULL; + poObjPoints = OGRGeoJSONFindMemberByName( poObj, "coordinates" ); + if( NULL == poObjPoints ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid MultiPoint object. " + "Missing \'coordinates\' member." ); + return NULL; + } + + if( json_type_array == json_object_get_type( poObjPoints ) ) + { + const int nPoints = json_object_array_length( poObjPoints ); + + poMultiPoint = new OGRMultiPoint(); + + for( int i = 0; i < nPoints; ++i) + { + json_object* poObjCoords = NULL; + poObjCoords = json_object_array_get_idx( poObjPoints, i ); + + OGRPoint pt; + if( poObjCoords != NULL && !OGRGeoJSONReadRawPoint( poObjCoords, pt ) ) + { + delete poMultiPoint; + CPLDebug( "GeoJSON", + "LineString: raw point parsing failure." ); + return NULL; + } + poMultiPoint->addGeometry( &pt ); + } + } + + return poMultiPoint; +} + +/************************************************************************/ +/* OGRGeoJSONReadLineString */ +/************************************************************************/ + +OGRLineString* OGRGeoJSONReadLineString( json_object* poObj , bool bRaw) +{ + CPLAssert( NULL != poObj ); + + OGRLineString* poLine = NULL; + json_object* poObjPoints = NULL; + + if( !bRaw ) + { + poObjPoints = OGRGeoJSONFindMemberByName( poObj, "coordinates" ); + if( NULL == poObjPoints ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid LineString object. " + "Missing \'coordinates\' member." ); + return NULL; + } + } + else + { + poObjPoints = poObj; + } + + if( json_type_array == json_object_get_type( poObjPoints ) ) + { + const int nPoints = json_object_array_length( poObjPoints ); + + poLine = new OGRLineString(); + poLine->setNumPoints( nPoints ); + + for( int i = 0; i < nPoints; ++i) + { + json_object* poObjCoords = NULL; + poObjCoords = json_object_array_get_idx( poObjPoints, i ); + if (poObjCoords == NULL) + { + delete poLine; + CPLDebug( "GeoJSON", + "LineString: got null object." ); + return NULL; + } + + OGRPoint pt; + if( !OGRGeoJSONReadRawPoint( poObjCoords, pt ) ) + { + delete poLine; + CPLDebug( "GeoJSON", + "LineString: raw point parsing failure." ); + return NULL; + } + if (pt.getCoordinateDimension() == 2) { + poLine->setPoint( i, pt.getX(), pt.getY()); + } else { + poLine->setPoint( i, pt.getX(), pt.getY(), pt.getZ() ); + } + + } + } + + return poLine; +} + +/************************************************************************/ +/* OGRGeoJSONReadMultiLineString */ +/************************************************************************/ + +OGRMultiLineString* OGRGeoJSONReadMultiLineString( json_object* poObj ) +{ + CPLAssert( NULL != poObj ); + + OGRMultiLineString* poMultiLine = NULL; + + json_object* poObjLines = NULL; + poObjLines = OGRGeoJSONFindMemberByName( poObj, "coordinates" ); + if( NULL == poObjLines ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid MultiLineString object. " + "Missing \'coordinates\' member." ); + return NULL; + } + + if( json_type_array == json_object_get_type( poObjLines ) ) + { + const int nLines = json_object_array_length( poObjLines ); + + poMultiLine = new OGRMultiLineString(); + + for( int i = 0; i < nLines; ++i) + { + json_object* poObjLine = NULL; + poObjLine = json_object_array_get_idx( poObjLines, i ); + + OGRLineString* poLine; + if (poObjLine != NULL) + poLine = OGRGeoJSONReadLineString( poObjLine , true ); + else + poLine = new OGRLineString(); + + if( NULL != poLine ) + { + poMultiLine->addGeometryDirectly( poLine ); + } + } + } + + return poMultiLine; +} + +/************************************************************************/ +/* OGRGeoJSONReadLinearRing */ +/************************************************************************/ + +OGRLinearRing* OGRGeoJSONReadLinearRing( json_object* poObj ) +{ + CPLAssert( NULL != poObj ); + + OGRLinearRing* poRing = NULL; + + if( json_type_array == json_object_get_type( poObj ) ) + { + const int nPoints = json_object_array_length( poObj ); + + poRing= new OGRLinearRing(); + poRing->setNumPoints( nPoints ); + + for( int i = 0; i < nPoints; ++i) + { + json_object* poObjCoords = NULL; + poObjCoords = json_object_array_get_idx( poObj, i ); + if (poObjCoords == NULL) + { + delete poRing; + CPLDebug( "GeoJSON", + "LinearRing: got null object." ); + return NULL; + } + + OGRPoint pt; + if( !OGRGeoJSONReadRawPoint( poObjCoords, pt ) ) + { + delete poRing; + CPLDebug( "GeoJSON", + "LinearRing: raw point parsing failure." ); + return NULL; + } + + if( 2 == pt.getCoordinateDimension() ) + poRing->setPoint( i, pt.getX(), pt.getY()); + else + poRing->setPoint( i, pt.getX(), pt.getY(), pt.getZ() ); + } + } + + return poRing; +} + +/************************************************************************/ +/* OGRGeoJSONReadPolygon */ +/************************************************************************/ + +OGRPolygon* OGRGeoJSONReadPolygon( json_object* poObj , bool bRaw ) +{ + CPLAssert( NULL != poObj ); + + OGRPolygon* poPolygon = NULL; + + json_object* poObjRings = NULL; + + if( !bRaw ) + { + poObjRings = OGRGeoJSONFindMemberByName( poObj, "coordinates" ); + if( NULL == poObjRings ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid Polygon object. " + "Missing \'coordinates\' member." ); + return NULL; + } + } + else + { + poObjRings = poObj; + } + + if( json_type_array == json_object_get_type( poObjRings ) ) + { + const int nRings = json_object_array_length( poObjRings ); + if( nRings > 0 ) + { + json_object* poObjPoints = NULL; + poObjPoints = json_object_array_get_idx( poObjRings, 0 ); + if (poObjPoints == NULL) + { + poPolygon = new OGRPolygon(); + poPolygon->addRingDirectly( new OGRLinearRing() ); + } + else + { + OGRLinearRing* poRing = OGRGeoJSONReadLinearRing( poObjPoints ); + if( NULL != poRing ) + { + poPolygon = new OGRPolygon(); + poPolygon->addRingDirectly( poRing ); + } + } + + for( int i = 1; i < nRings && NULL != poPolygon; ++i ) + { + poObjPoints = json_object_array_get_idx( poObjRings, i ); + if (poObjPoints == NULL) + { + poPolygon->addRingDirectly( new OGRLinearRing() ); + } + else + { + OGRLinearRing* poRing = OGRGeoJSONReadLinearRing( poObjPoints ); + if( NULL != poRing ) + { + poPolygon->addRingDirectly( poRing ); + } + } + } + } + } + + return poPolygon; +} + +/************************************************************************/ +/* OGRGeoJSONReadMultiPolygon */ +/************************************************************************/ + +OGRMultiPolygon* OGRGeoJSONReadMultiPolygon( json_object* poObj ) +{ + CPLAssert( NULL != poObj ); + + OGRMultiPolygon* poMultiPoly = NULL; + + json_object* poObjPolys = NULL; + poObjPolys = OGRGeoJSONFindMemberByName( poObj, "coordinates" ); + if( NULL == poObjPolys ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid MultiPolygon object. " + "Missing \'coordinates\' member." ); + return NULL; + } + + if( json_type_array == json_object_get_type( poObjPolys ) ) + { + const int nPolys = json_object_array_length( poObjPolys ); + + poMultiPoly = new OGRMultiPolygon(); + + for( int i = 0; i < nPolys; ++i) + { + + json_object* poObjPoly = NULL; + poObjPoly = json_object_array_get_idx( poObjPolys, i ); + if (poObjPoly == NULL) + { + poMultiPoly->addGeometryDirectly( new OGRPolygon() ); + } + else + { + OGRPolygon* poPoly = OGRGeoJSONReadPolygon( poObjPoly , true ); + if( NULL != poPoly ) + { + poMultiPoly->addGeometryDirectly( poPoly ); + } + } + } + } + + return poMultiPoly; +} +/************************************************************************/ +/* OGRGeoJSONReadGeometryCollection */ +/************************************************************************/ + +OGRGeometryCollection* OGRGeoJSONReadGeometryCollection( json_object* poObj ) +{ + CPLAssert( NULL != poObj ); + + OGRGeometry* poGeometry = NULL; + OGRGeometryCollection* poCollection = NULL; + + json_object* poObjGeoms = NULL; + poObjGeoms = OGRGeoJSONFindMemberByName( poObj, "geometries" ); + if( NULL == poObjGeoms ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid GeometryCollection object. " + "Missing \'geometries\' member." ); + return NULL; + } + + if( json_type_array == json_object_get_type( poObjGeoms ) ) + { + const int nGeoms = json_object_array_length( poObjGeoms ); + if( nGeoms > 0 ) + { + poCollection = new OGRGeometryCollection(); + } + + json_object* poObjGeom = NULL; + for( int i = 0; i < nGeoms; ++i ) + { + poObjGeom = json_object_array_get_idx( poObjGeoms, i ); + if (poObjGeom == NULL) + { + CPLDebug( "GeoJSON", "Skipping null sub-geometry"); + continue; + } + + poGeometry = OGRGeoJSONReadGeometry( poObjGeom ); + if( NULL != poGeometry ) + { + poCollection->addGeometryDirectly( poGeometry ); + } + } + } + + return poCollection; +} + +/************************************************************************/ +/* OGR_G_ExportToJson */ +/************************************************************************/ + +OGRGeometryH OGR_G_CreateGeometryFromJson( const char* pszJson ) +{ + VALIDATE_POINTER1( pszJson, "OGR_G_CreateGeometryFromJson", NULL ); + + if( NULL != pszJson ) + { + json_tokener* jstok = NULL; + json_object* poObj = NULL; + + jstok = json_tokener_new(); + poObj = json_tokener_parse_ex(jstok, pszJson, -1); + if( jstok->err != json_tokener_success) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GeoJSON parsing error: %s (at offset %d)", + json_tokener_error_desc(jstok->err), jstok->char_offset); + json_tokener_free(jstok); + return NULL; + } + json_tokener_free(jstok); + + OGRGeometry* poGeometry = NULL; + poGeometry = OGRGeoJSONReadGeometry( poObj ); + + /* Assign WGS84 if no CRS defined on geometry */ + if( poGeometry && poGeometry->getSpatialReference() == NULL ) + { + poGeometry->assignSpatialReference(OGRSpatialReference::GetWGS84SRS()); + } + + /* Release JSON tree. */ + json_object_put( poObj ); + + return (OGRGeometryH)poGeometry; + } + + /* Translation failed */ + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonreader.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonreader.h new file mode 100644 index 000000000..d1debd303 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonreader.h @@ -0,0 +1,247 @@ +/****************************************************************************** + * $Id: ogrgeojsonreader.h 29111 2015-05-02 18:06:16Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines GeoJSON reader within OGR OGRGeoJSON Driver. + * Author: Mateusz Loskot, mateusz@loskot.net + * + ****************************************************************************** + * Copyright (c) 2007, Mateusz Loskot + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef OGR_GEOJSONREADER_H_INCLUDED +#define OGR_GEOJSONREADER_H_INCLUDED + +#include +#include "ogrsf_frmts.h" +#include // JSON-C + +/************************************************************************/ +/* FORWARD DECLARATIONS */ +/************************************************************************/ + +class OGRGeometry; +class OGRRawPoint; +class OGRPoint; +class OGRMultiPoint; +class OGRLineString; +class OGRMultiLineString; +class OGRLinearRing; +class OGRPolygon; +class OGRMultiPolygon; +class OGRGeometryCollection; +class OGRFeature; +class OGRGeoJSONLayer; +class OGRSpatialReference; + +/************************************************************************/ +/* GeoJSONObject */ +/************************************************************************/ + +struct GeoJSONObject +{ + enum Type + { + eUnknown = wkbUnknown, // non-GeoJSON properties + ePoint = wkbPoint, + eLineString = wkbLineString, + ePolygon = wkbPolygon, + eMultiPoint = wkbMultiPoint, + eMultiLineString = wkbMultiLineString, + eMultiPolygon = wkbMultiPolygon, + eGeometryCollection = wkbGeometryCollection, + eFeature, + eFeatureCollection + }; + + enum CoordinateDimension + { + eMinCoordinateDimension = 2, + eMaxCoordinateDimension = 3 + }; +}; + +/************************************************************************/ +/* OGRGeoJSONReader */ +/************************************************************************/ + +class OGRGeoJSONDataSource; + +class OGRGeoJSONReader +{ +public: + + OGRGeoJSONReader(); + ~OGRGeoJSONReader(); + + void SetPreserveGeometryType( bool bPreserve ); + void SetSkipAttributes( bool bSkip ); + void SetFlattenNestedAttributes( bool bFlatten, char chSeparator ); + + OGRErr Parse( const char* pszText ); + void ReadLayers( OGRGeoJSONDataSource* poDS ); + void ReadLayer( OGRGeoJSONDataSource* poDS, + const char* pszName, + json_object* poObj ); + + json_object* GetJSonObject() { return poGJObject_; } + +private: + + json_object* poGJObject_; + + bool bGeometryPreserve_; + bool bAttributesSkip_; + bool bFlattenNestedAttributes_; + char chNestedAttributeSeparator_; + + int bFlattenGeocouchSpatiallistFormat; + bool bFoundId, bFoundRev, bFoundTypeFeature, bIsGeocouchSpatiallistFormat; + + // + // Copy operations not supported. + // + OGRGeoJSONReader( OGRGeoJSONReader const& ); + OGRGeoJSONReader& operator=( OGRGeoJSONReader const& ); + + // + // Translation utilities. + // + bool GenerateLayerDefn( OGRGeoJSONLayer* poLayer, json_object* poGJObject ); + bool GenerateFeatureDefn( OGRGeoJSONLayer* poLayer, json_object* poObj ); + bool AddFeature( OGRGeoJSONLayer* poLayer, OGRGeometry* poGeometry ); + bool AddFeature( OGRGeoJSONLayer* poLayer, OGRFeature* poFeature ); + + OGRGeometry* ReadGeometry( json_object* poObj ); + OGRFeature* ReadFeature( OGRGeoJSONLayer* poLayer, json_object* poObj ); + void ReadFeatureCollection( OGRGeoJSONLayer* poLayer, json_object* poObj ); +}; + +void OGRGeoJSONReaderSetField(OGRLayer* poLayer, + OGRFeature* poFeature, + int nField, + const char* pszAttrPrefix, + json_object* poVal, + bool bFlattenNestedAttributes, + char chNestedAttributeSeparator); +void OGRGeoJSONReaderAddOrUpdateField(OGRFeatureDefn* poDefn, + const char* pszKey, + json_object* poVal, + bool bFlattenNestedAttributes, + char chNestedAttributeSeparator); + +/************************************************************************/ +/* GeoJSON Parsing Utilities */ +/************************************************************************/ + +json_object* OGRGeoJSONFindMemberByName(json_object* poObj, const char* pszName ); +GeoJSONObject::Type OGRGeoJSONGetType( json_object* poObj ); + +/************************************************************************/ +/* GeoJSON Geometry Translators */ +/************************************************************************/ + +bool OGRGeoJSONReadRawPoint( json_object* poObj, OGRPoint& point ); +OGRGeometry* OGRGeoJSONReadGeometry( json_object* poObj ); +OGRPoint* OGRGeoJSONReadPoint( json_object* poObj ); +OGRMultiPoint* OGRGeoJSONReadMultiPoint( json_object* poObj ); +OGRLineString* OGRGeoJSONReadLineString( json_object* poObj, bool bRaw=false ); +OGRMultiLineString* OGRGeoJSONReadMultiLineString( json_object* poObj ); +OGRLinearRing* OGRGeoJSONReadLinearRing( json_object* poObj ); +OGRPolygon* OGRGeoJSONReadPolygon( json_object* poObj , bool bRaw=false); +OGRMultiPolygon* OGRGeoJSONReadMultiPolygon( json_object* poObj ); +OGRGeometryCollection* OGRGeoJSONReadGeometryCollection( json_object* poObj ); +OGRSpatialReference* OGRGeoJSONReadSpatialReference( json_object* poObj ); + + + +/************************************************************************/ +/* OGRESRIJSONReader */ +/************************************************************************/ + +class OGRESRIJSONReader +{ +public: + + OGRESRIJSONReader(); + ~OGRESRIJSONReader(); + + OGRErr Parse( const char* pszText ); + void ReadLayers( OGRGeoJSONDataSource* poDS ); + + json_object* GetJSonObject() { return poGJObject_; } + +private: + + json_object* poGJObject_; + OGRGeoJSONLayer* poLayer_; + + // + // Copy operations not supported. + // + OGRESRIJSONReader( OGRESRIJSONReader const& ); + OGRESRIJSONReader& operator=( OGRESRIJSONReader const& ); + + // + // Translation utilities. + // + bool GenerateLayerDefn(); + bool GenerateFeatureDefn( json_object* poObj ); + bool AddFeature( OGRFeature* poFeature ); + + OGRGeometry* ReadGeometry( json_object* poObj ); + OGRFeature* ReadFeature( json_object* poObj ); + OGRGeoJSONLayer* ReadFeatureCollection( json_object* poObj ); +}; + +OGRSpatialReference* OGRESRIJSONReadSpatialReference( json_object* poObj ); +OGRwkbGeometryType OGRESRIJSONGetGeometryType( json_object* poObj ); +OGRPoint* OGRESRIJSONReadPoint( json_object* poObj); +OGRLineString* OGRESRIJSONReadLineString( json_object* poObj); +OGRGeometry* OGRESRIJSONReadPolygon( json_object* poObj); +OGRMultiPoint* OGRESRIJSONReadMultiPoint( json_object* poObj); + +/************************************************************************/ +/* OGRTopoJSONReader */ +/************************************************************************/ + +class OGRTopoJSONReader +{ +public: + + OGRTopoJSONReader(); + ~OGRTopoJSONReader(); + + OGRErr Parse( const char* pszText ); + void ReadLayers( OGRGeoJSONDataSource* poDS ); + +private: + + json_object* poGJObject_; + + // + // Copy operations not supported. + // + OGRTopoJSONReader( OGRTopoJSONReader const& ); + OGRTopoJSONReader& operator=( OGRTopoJSONReader const& ); +}; + +#endif /* OGR_GEOJSONUTILS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonutils.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonutils.cpp new file mode 100644 index 000000000..d0582dccf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonutils.cpp @@ -0,0 +1,303 @@ +/****************************************************************************** + * $Id: ogrgeojsonutils.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implementation of private utilities used within OGR GeoJSON Driver. + * Author: Mateusz Loskot, mateusz@loskot.net + * + ****************************************************************************** + * Copyright (c) 2007, Mateusz Loskot + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogrgeojsonutils.h" +#include +#include +#include +#include // JSON-C + +/************************************************************************/ +/* GeoJSONIsObject() */ +/************************************************************************/ + +int GeoJSONIsObject( const char* pszText ) +{ + if( NULL == pszText ) + return FALSE; + + /* Skip UTF-8 BOM (#5630) */ + const GByte* pabyData = (const GByte*)pszText; + if( pabyData[0] == 0xEF && pabyData[1] == 0xBB && pabyData[2] == 0xBF ) + pszText += 3; + +/* -------------------------------------------------------------------- */ +/* This is a primitive test, but we need to perform it fast. */ +/* -------------------------------------------------------------------- */ + while( *pszText != '\0' && isspace( (unsigned char)*pszText ) ) + pszText++; + + const char* const apszPrefix[] = { "loadGeoJSON(", "jsonp(" }; + for(size_t iP = 0; iP < sizeof(apszPrefix) / sizeof(apszPrefix[0]); iP++ ) + { + if( strncmp(pszText, apszPrefix[iP], strlen(apszPrefix[iP])) == 0 ) + { + pszText += strlen(apszPrefix[iP]); + break; + } + } + + if( *pszText != '{' ) + return FALSE; + + return ((strstr(pszText, "\"type\"") != NULL && strstr(pszText, "\"coordinates\"") != NULL) + || (strstr(pszText, "\"type\"") != NULL && strstr(pszText, "\"Topology\"") != NULL) + || strstr(pszText, "\"FeatureCollection\"") != NULL + || strstr(pszText, "\"Feature\"") != NULL + || (strstr(pszText, "\"geometryType\"") != NULL && strstr(pszText, "\"esriGeometry") != NULL)); +} + +/************************************************************************/ +/* GeoJSONFileIsObject() */ +/************************************************************************/ + +static +int GeoJSONFileIsObject( GDALOpenInfo* poOpenInfo ) +{ + // by default read first 6000 bytes + // 6000 was chosen as enough bytes to + // enable all current tests to pass + + if( poOpenInfo->fpL == NULL || + !poOpenInfo->TryToIngest(6000) ) + { + return FALSE; + } + + if( !GeoJSONIsObject((const char*)poOpenInfo->pabyHeader) ) + { + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* GeoJSONGetSourceType() */ +/************************************************************************/ + +GeoJSONSourceType GeoJSONGetSourceType( GDALOpenInfo* poOpenInfo ) +{ + GeoJSONSourceType srcType = eGeoJSONSourceUnknown; + + // NOTE: Sometimes URL ends with .geojson token, for example + // http://example/path/2232.geojson + // It's important to test beginning of source first. + if ( eGeoJSONProtocolUnknown != GeoJSONGetProtocolType( poOpenInfo->pszFilename ) ) + { + if( (strstr(poOpenInfo->pszFilename, "SERVICE=WFS") || + strstr(poOpenInfo->pszFilename, "service=WFS") || + strstr(poOpenInfo->pszFilename, "service=wfs")) && + !strstr(poOpenInfo->pszFilename, "json") ) + return srcType; + srcType = eGeoJSONSourceService; + } + else if( EQUAL( CPLGetExtension( poOpenInfo->pszFilename ), "geojson" ) + || EQUAL( CPLGetExtension( poOpenInfo->pszFilename ), "json" ) + || EQUAL( CPLGetExtension( poOpenInfo->pszFilename ), "topojson" ) + || ((EQUALN( poOpenInfo->pszFilename, "/vsigzip/", 9) || EQUALN( poOpenInfo->pszFilename, "/vsizip/", 8)) && + (strstr( poOpenInfo->pszFilename, ".json") || strstr( poOpenInfo->pszFilename, ".JSON") || + strstr( poOpenInfo->pszFilename, ".geojson") || strstr( poOpenInfo->pszFilename, ".GEOJSON")) )) + { + if( poOpenInfo->fpL != NULL ) + srcType = eGeoJSONSourceFile; + } + else if( GeoJSONIsObject( poOpenInfo->pszFilename ) ) + { + srcType = eGeoJSONSourceText; + } + else if( GeoJSONFileIsObject( poOpenInfo ) ) + { + srcType = eGeoJSONSourceFile; + } + + return srcType; +} + +/************************************************************************/ +/* GeoJSONGetProtocolType() */ +/************************************************************************/ + +GeoJSONProtocolType GeoJSONGetProtocolType( const char* pszSource ) +{ + GeoJSONProtocolType ptclType = eGeoJSONProtocolUnknown; + + if( EQUALN( pszSource, "http:", 5 ) ) + ptclType = eGeoJSONProtocolHTTP; + else if( EQUALN( pszSource, "https:", 6 ) ) + ptclType = eGeoJSONProtocolHTTPS; + else if( EQUALN( pszSource, "ftp:", 4 ) ) + ptclType = eGeoJSONProtocolFTP; + + return ptclType; +} + +/************************************************************************/ +/* GeoJSONPropertyToFieldType() */ +/************************************************************************/ + +#define MY_INT64_MAX ((((GIntBig)0x7FFFFFFF) << 32) | 0xFFFFFFFF) +#define MY_INT64_MIN ((((GIntBig)0x80000000) << 32)) + +OGRFieldType GeoJSONPropertyToFieldType( json_object* poObject, + OGRFieldSubType& eSubType ) +{ + eSubType = OFSTNone; + + if (poObject == NULL) { return OFTString; } + + json_type type = json_object_get_type( poObject ); + + if( json_type_boolean == type ) + { + eSubType = OFSTBoolean; + return OFTInteger; + } + else if( json_type_double == type ) + return OFTReal; + else if( json_type_int == type ) + { + GIntBig nVal = json_object_get_int64(poObject); + if( nVal != (GIntBig)(int) nVal ) + { + if( nVal == MY_INT64_MIN || nVal == MY_INT64_MAX ) + { + static int bWarned = FALSE; + if( !bWarned ) + { + bWarned = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "Integer values probably ranging out of 64bit integer range " + "have been found. Will be clamped to INT64_MIN/INT64_MAX"); + } + } + return OFTInteger64; + } + else + { + return OFTInteger; + } + } + else if( json_type_string == type ) + return OFTString; + else if( json_type_array == type ) + { + int nSize = json_object_array_length(poObject); + if (nSize == 0) + return OFTStringList; /* we don't know, so let's assume it's a string list */ + OGRFieldType eType = OFTIntegerList; + int bOnlyBoolean = TRUE; + for(int i=0;igetGeometryType(); + + if( wkbPoint == eType || wkbPoint25D == eType ) + return "Point"; + else if( wkbLineString == eType || wkbLineString25D == eType ) + return "LineString"; + else if( wkbPolygon == eType || wkbPolygon25D == eType ) + return "Polygon"; + else if( wkbMultiPoint == eType || wkbMultiPoint25D == eType ) + return "MultiPoint"; + else if( wkbMultiLineString == eType || wkbMultiLineString25D == eType ) + return "MultiLineString"; + else if( wkbMultiPolygon == eType || wkbMultiPolygon25D == eType ) + return "MultiPolygon"; + else if( wkbGeometryCollection == eType || wkbGeometryCollection25D == eType ) + return "GeometryCollection"; + else + return "Unknown"; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonutils.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonutils.h new file mode 100644 index 000000000..0631ddb44 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonutils.h @@ -0,0 +1,93 @@ +/****************************************************************************** + * $Id: ogrgeojsonutils.h 28009 2014-11-26 12:50:04Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private utilities within OGR OGRGeoJSON Driver. + * Author: Mateusz Loskot, mateusz@loskot.net + * + ****************************************************************************** + * Copyright (c) 2007, Mateusz Loskot + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef OGR_GEOJSONUTILS_H_INCLUDED +#define OGR_GEOJSONUTILS_H_INCLUDED + +#include +#include // JSON-C +#include "cpl_vsi.h" +#include "gdal_priv.h" + +class OGRGeometry; + +/************************************************************************/ +/* GeoJSONSourceType */ +/************************************************************************/ + +enum GeoJSONSourceType +{ + eGeoJSONSourceUnknown = 0, + eGeoJSONSourceFile, + eGeoJSONSourceText, + eGeoJSONSourceService +}; + +GeoJSONSourceType GeoJSONGetSourceType( GDALOpenInfo* poOpenInfo ); + +/************************************************************************/ +/* GeoJSONProtocolType */ +/************************************************************************/ + +enum GeoJSONProtocolType +{ + eGeoJSONProtocolUnknown = 0, + eGeoJSONProtocolHTTP, + eGeoJSONProtocolHTTPS, + eGeoJSONProtocolFTP, +}; + +GeoJSONProtocolType GeoJSONGetProtocolType( const char* pszSource ); + +/************************************************************************/ +/* GeoJSONIsObject */ +/************************************************************************/ + +int GeoJSONIsObject( const char* pszText ); + +/************************************************************************/ +/* GeoJSONPropertyToFieldType */ +/************************************************************************/ + +OGRFieldType GeoJSONPropertyToFieldType( json_object* poObject, + OGRFieldSubType& eSubType ); + + +/************************************************************************/ +/* GeoJSONStringPropertyToFieldType */ +/************************************************************************/ + +OGRFieldType GeoJSONStringPropertyToFieldType( json_object* poObject ); + +/************************************************************************/ +/* OGRGeoJSONGetGeometryName */ +/************************************************************************/ + +const char* OGRGeoJSONGetGeometryName( OGRGeometry const* poGeometry ); + +#endif /* OGR_GEOJSONUTILS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonwritelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonwritelayer.cpp new file mode 100644 index 000000000..f47b713e6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonwritelayer.cpp @@ -0,0 +1,193 @@ +/****************************************************************************** + * $Id: ogrgeojsonwritelayer.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implementation of OGRGeoJSONWriteLayer class (OGR GeoJSON Driver). + * Author: Mateusz Loskot, mateusz@loskot.net + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * Copyright (c) 2007, Mateusz Loskot + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogr_geojson.h" +#include "ogrgeojsonwriter.h" + +/* Remove annoying warnings Microsoft Visual C++ */ +#if defined(_MSC_VER) +# pragma warning(disable:4512) +#endif + +/************************************************************************/ +/* OGRGeoJSONWriteLayer() */ +/************************************************************************/ + +OGRGeoJSONWriteLayer::OGRGeoJSONWriteLayer( const char* pszName, + OGRwkbGeometryType eGType, + char** papszOptions, + OGRGeoJSONDataSource* poDS ) + : poDS_( poDS ), poFeatureDefn_(new OGRFeatureDefn( pszName ) ), nOutCounter_( 0 ) +{ + bWriteBBOX = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "WRITE_BBOX", "FALSE")); + bBBOX3D = FALSE; + + poFeatureDefn_->Reference(); + poFeatureDefn_->SetGeomType( eGType ); + SetDescription( poFeatureDefn_->GetName() ); + + nCoordPrecision = atoi(CSLFetchNameValueDef(papszOptions, "COORDINATE_PRECISION", "-1")); +} + +/************************************************************************/ +/* ~OGRGeoJSONWriteLayer() */ +/************************************************************************/ + +OGRGeoJSONWriteLayer::~OGRGeoJSONWriteLayer() +{ + VSILFILE* fp = poDS_->GetOutputFile(); + + VSIFPrintfL( fp, "\n]" ); + + if( bWriteBBOX && sEnvelopeLayer.IsInit() ) + { + json_object* poObjBBOX = json_object_new_array(); + json_object_array_add(poObjBBOX, + json_object_new_double_with_precision(sEnvelopeLayer.MinX, nCoordPrecision)); + json_object_array_add(poObjBBOX, + json_object_new_double_with_precision(sEnvelopeLayer.MinY, nCoordPrecision)); + if( bBBOX3D ) + json_object_array_add(poObjBBOX, + json_object_new_double_with_precision(sEnvelopeLayer.MinZ, nCoordPrecision)); + json_object_array_add(poObjBBOX, + json_object_new_double_with_precision(sEnvelopeLayer.MaxX, nCoordPrecision)); + json_object_array_add(poObjBBOX, + json_object_new_double_with_precision(sEnvelopeLayer.MaxY, nCoordPrecision)); + if( bBBOX3D ) + json_object_array_add(poObjBBOX, + json_object_new_double_with_precision(sEnvelopeLayer.MaxZ, nCoordPrecision)); + + const char* pszBBOX = json_object_to_json_string( poObjBBOX ); + if( poDS_->GetFpOutputIsSeekable() ) + { + VSIFSeekL(fp, poDS_->GetBBOXInsertLocation(), SEEK_SET); + if (strlen(pszBBOX) + 9 < SPACE_FOR_BBOX) + VSIFPrintfL( fp, "\"bbox\": %s,", pszBBOX ); + VSIFSeekL(fp, 0, SEEK_END); + } + else + { + VSIFPrintfL( fp, ",\n\"bbox\": %s", pszBBOX ); + } + + json_object_put( poObjBBOX ); + } + + VSIFPrintfL( fp, "\n}\n" ); + + if( NULL != poFeatureDefn_ ) + { + poFeatureDefn_->Release(); + } +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRGeoJSONWriteLayer::ICreateFeature( OGRFeature* poFeature ) +{ + VSILFILE* fp = poDS_->GetOutputFile(); + + if( NULL == poFeature ) + { + CPLDebug( "GeoJSON", "Feature is null" ); + return OGRERR_INVALID_HANDLE; + } + + json_object* poObj = OGRGeoJSONWriteFeature( poFeature, bWriteBBOX, nCoordPrecision ); + CPLAssert( NULL != poObj ); + + if( nOutCounter_ > 0 ) + { + /* Separate "Feature" entries in "FeatureCollection" object. */ + VSIFPrintfL( fp, ",\n" ); + } + VSIFPrintfL( fp, "%s", json_object_to_json_string( poObj ) ); + + json_object_put( poObj ); + + ++nOutCounter_; + + OGRGeometry* poGeometry = poFeature->GetGeometryRef(); + if ( bWriteBBOX && !poGeometry->IsEmpty() ) + { + OGREnvelope3D sEnvelope; + poGeometry->getEnvelope(&sEnvelope); + + if( poGeometry->getCoordinateDimension() == 3 ) + bBBOX3D = TRUE; + + sEnvelopeLayer.Merge(sEnvelope); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRGeoJSONWriteLayer::CreateField(OGRFieldDefn* poField, int bApproxOK) +{ + UNREFERENCED_PARAM(bApproxOK); + + for( int i = 0; i < poFeatureDefn_->GetFieldCount(); ++i ) + { + OGRFieldDefn* poDefn = poFeatureDefn_->GetFieldDefn(i); + CPLAssert( NULL != poDefn ); + + if( EQUAL( poDefn->GetNameRef(), poField->GetNameRef() ) ) + { + CPLDebug( "GeoJSON", "Field '%s' already present in schema", + poField->GetNameRef() ); + + // TODO - mloskot: Is this return code correct? + return OGRERR_NONE; + } + } + + poFeatureDefn_->AddFieldDefn( poField ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGeoJSONWriteLayer::TestCapability( const char* pszCap ) +{ + if (EQUAL(pszCap, OLCCreateField)) + return TRUE; + else if (EQUAL(pszCap, OLCSequentialWrite)) + return TRUE; + + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonwriter.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonwriter.cpp new file mode 100644 index 000000000..0e6d364fd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonwriter.cpp @@ -0,0 +1,664 @@ +/****************************************************************************** + * $Id: ogrgeojsonwriter.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implementation of GeoJSON writer utilities (OGR GeoJSON Driver). + * Author: Mateusz Loskot, mateusz@loskot.net + * + ****************************************************************************** + * Copyright (c) 2007, Mateusz Loskot + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogrgeojsonwriter.h" +#include "ogrgeojsonutils.h" +#include "ogr_geojson.h" +#include // JSON-C +#include +#include +#include +#include + +/************************************************************************/ +/* OGRGeoJSONWriteFeature */ +/************************************************************************/ + +json_object* OGRGeoJSONWriteFeature( OGRFeature* poFeature, int bWriteBBOX, int nCoordPrecision ) +{ + CPLAssert( NULL != poFeature ); + + json_object* poObj = json_object_new_object(); + CPLAssert( NULL != poObj ); + + json_object_object_add( poObj, "type", + json_object_new_string("Feature") ); + +/* -------------------------------------------------------------------- */ +/* Write FID if available */ +/* -------------------------------------------------------------------- */ + if ( poFeature->GetFID() != OGRNullFID ) + { + json_object_object_add( poObj, "id", + json_object_new_int64(poFeature->GetFID()) ); + } + +/* -------------------------------------------------------------------- */ +/* Write feature attributes to GeoJSON "properties" object. */ +/* -------------------------------------------------------------------- */ + json_object* poObjProps = NULL; + + poObjProps = OGRGeoJSONWriteAttributes( poFeature ); + json_object_object_add( poObj, "properties", poObjProps ); + +/* -------------------------------------------------------------------- */ +/* Write feature geometry to GeoJSON "geometry" object. */ +/* Null geometries are allowed, according to the GeoJSON Spec. */ +/* -------------------------------------------------------------------- */ + json_object* poObjGeom = NULL; + + OGRGeometry* poGeometry = poFeature->GetGeometryRef(); + if ( NULL != poGeometry ) + { + poObjGeom = OGRGeoJSONWriteGeometry( poGeometry, nCoordPrecision ); + + if ( bWriteBBOX && !poGeometry->IsEmpty() ) + { + OGREnvelope3D sEnvelope; + poGeometry->getEnvelope(&sEnvelope); + + json_object* poObjBBOX = json_object_new_array(); + json_object_array_add(poObjBBOX, + json_object_new_double_with_precision(sEnvelope.MinX, nCoordPrecision)); + json_object_array_add(poObjBBOX, + json_object_new_double_with_precision(sEnvelope.MinY, nCoordPrecision)); + if (poGeometry->getCoordinateDimension() == 3) + json_object_array_add(poObjBBOX, + json_object_new_double_with_precision(sEnvelope.MinZ, nCoordPrecision)); + json_object_array_add(poObjBBOX, + json_object_new_double_with_precision(sEnvelope.MaxX, nCoordPrecision)); + json_object_array_add(poObjBBOX, + json_object_new_double_with_precision(sEnvelope.MaxY, nCoordPrecision)); + if (poGeometry->getCoordinateDimension() == 3) + json_object_array_add(poObjBBOX, + json_object_new_double_with_precision(sEnvelope.MaxZ, nCoordPrecision)); + + json_object_object_add( poObj, "bbox", poObjBBOX ); + } + } + + json_object_object_add( poObj, "geometry", poObjGeom ); + + return poObj; +} + +/************************************************************************/ +/* OGRGeoJSONWriteGeometry */ +/************************************************************************/ + +json_object* OGRGeoJSONWriteAttributes( OGRFeature* poFeature ) +{ + CPLAssert( NULL != poFeature ); + + json_object* poObjProps = json_object_new_object(); + CPLAssert( NULL != poObjProps ); + + OGRFeatureDefn* poDefn = poFeature->GetDefnRef(); + for( int nField = 0; nField < poDefn->GetFieldCount(); ++nField ) + { + json_object* poObjProp; + OGRFieldDefn* poFieldDefn = poDefn->GetFieldDefn( nField ); + CPLAssert( NULL != poFieldDefn ); + OGRFieldType eType = poFieldDefn->GetType(); + OGRFieldSubType eSubType = poFieldDefn->GetSubType(); + + if( !poFeature->IsFieldSet(nField) ) + { + poObjProp = NULL; + } + else if( OFTInteger == eType ) + { + if( eSubType == OFSTBoolean ) + poObjProp = json_object_new_boolean( + poFeature->GetFieldAsInteger( nField ) ); + else + poObjProp = json_object_new_int( + poFeature->GetFieldAsInteger( nField ) ); + } + else if( OFTInteger64 == eType ) + { + if( eSubType == OFSTBoolean ) + poObjProp = json_object_new_boolean( + (json_bool)poFeature->GetFieldAsInteger64( nField ) ); + else + poObjProp = json_object_new_int64( + poFeature->GetFieldAsInteger64( nField ) ); + } + else if( OFTReal == eType ) + { + poObjProp = json_object_new_double( + poFeature->GetFieldAsDouble(nField) ); + } + else if( OFTString == eType ) + { + poObjProp = json_object_new_string( + poFeature->GetFieldAsString(nField) ); + } + else if( OFTIntegerList == eType ) + { + int nSize = 0; + const int* panList = poFeature->GetFieldAsIntegerList(nField, &nSize); + poObjProp = json_object_new_array(); + for(int i=0;iGetFieldAsInteger64List(nField, &nSize); + poObjProp = json_object_new_array(); + for(int i=0;iGetFieldAsDoubleList(nField, &nSize); + poObjProp = json_object_new_array(); + for(int i=0;iGetFieldAsStringList(nField); + poObjProp = json_object_new_array(); + for(int i=0; papszStringList && papszStringList[i]; i++) + { + json_object_array_add(poObjProp, + json_object_new_string(papszStringList[i])); + } + } + else + { + poObjProp = json_object_new_string( + poFeature->GetFieldAsString(nField) ); + } + + json_object_object_add( poObjProps, + poFieldDefn->GetNameRef(), + poObjProp ); + } + + return poObjProps; +} + +/************************************************************************/ +/* OGRGeoJSONWriteGeometry */ +/************************************************************************/ + +json_object* OGRGeoJSONWriteGeometry( OGRGeometry* poGeometry, int nCoordPrecision ) +{ + CPLAssert( NULL != poGeometry ); + + json_object* poObj = json_object_new_object(); + CPLAssert( NULL != poObj ); + +/* -------------------------------------------------------------------- */ +/* Build "type" member of GeoJSOn "geometry" object. */ +/* -------------------------------------------------------------------- */ + + // XXX - mloskot: workaround hack for pure JSON-C API design. + char* pszName = const_cast(OGRGeoJSONGetGeometryName( poGeometry )); + json_object_object_add( poObj, "type", json_object_new_string(pszName) ); + +/* -------------------------------------------------------------------- */ +/* Build "coordinates" member of GeoJSOn "geometry" object. */ +/* -------------------------------------------------------------------- */ + json_object* poObjGeom = NULL; + + OGRwkbGeometryType eType = poGeometry->getGeometryType(); + if( wkbGeometryCollection == eType || wkbGeometryCollection25D == eType ) + { + poObjGeom = OGRGeoJSONWriteGeometryCollection( static_cast(poGeometry), nCoordPrecision ); + json_object_object_add( poObj, "geometries", poObjGeom); + } + else + { + if( wkbPoint == eType || wkbPoint25D == eType ) + poObjGeom = OGRGeoJSONWritePoint( static_cast(poGeometry), nCoordPrecision ); + else if( wkbLineString == eType || wkbLineString25D == eType ) + poObjGeom = OGRGeoJSONWriteLineString( static_cast(poGeometry), nCoordPrecision ); + else if( wkbPolygon == eType || wkbPolygon25D == eType ) + poObjGeom = OGRGeoJSONWritePolygon( static_cast(poGeometry), nCoordPrecision ); + else if( wkbMultiPoint == eType || wkbMultiPoint25D == eType ) + poObjGeom = OGRGeoJSONWriteMultiPoint( static_cast(poGeometry), nCoordPrecision ); + else if( wkbMultiLineString == eType || wkbMultiLineString25D == eType ) + poObjGeom = OGRGeoJSONWriteMultiLineString( static_cast(poGeometry), nCoordPrecision ); + else if( wkbMultiPolygon == eType || wkbMultiPolygon25D == eType ) + poObjGeom = OGRGeoJSONWriteMultiPolygon( static_cast(poGeometry), nCoordPrecision ); + else + { + CPLDebug( "GeoJSON", + "Unsupported geometry type detected. " + "Feature gets NULL geometry assigned." ); + } + + json_object_object_add( poObj, "coordinates", poObjGeom); + } + + return poObj; +} + +/************************************************************************/ +/* OGRGeoJSONWritePoint */ +/************************************************************************/ + +json_object* OGRGeoJSONWritePoint( OGRPoint* poPoint, int nCoordPrecision ) +{ + CPLAssert( NULL != poPoint ); + + json_object* poObj = NULL; + + /* Generate "coordinates" object for 2D or 3D dimension. */ + if( 3 == poPoint->getCoordinateDimension() ) + { + poObj = OGRGeoJSONWriteCoords( poPoint->getX(), + poPoint->getY(), + poPoint->getZ(), + nCoordPrecision ); + } + else if( 2 == poPoint->getCoordinateDimension() ) + { + poObj = OGRGeoJSONWriteCoords( poPoint->getX(), + poPoint->getY(), + nCoordPrecision ); + } + else + { + /* We can get here with POINT EMPTY geometries */ + } + + return poObj; +} + +/************************************************************************/ +/* OGRGeoJSONWriteLineString */ +/************************************************************************/ + +json_object* OGRGeoJSONWriteLineString( OGRLineString* poLine, int nCoordPrecision ) +{ + CPLAssert( NULL != poLine ); + + /* Generate "coordinates" object for 2D or 3D dimension. */ + json_object* poObj = NULL; + poObj = OGRGeoJSONWriteLineCoords( poLine, nCoordPrecision ); + + return poObj; +} + +/************************************************************************/ +/* OGRGeoJSONWritePolygon */ +/************************************************************************/ + +json_object* OGRGeoJSONWritePolygon( OGRPolygon* poPolygon, int nCoordPrecision ) +{ + CPLAssert( NULL != poPolygon ); + + /* Generate "coordinates" array object. */ + json_object* poObj = NULL; + poObj = json_object_new_array(); + + /* Exterior ring. */ + OGRLinearRing* poRing = poPolygon->getExteriorRing(); + if (poRing == NULL) + return poObj; + + json_object* poObjRing = NULL; + poObjRing = OGRGeoJSONWriteLineCoords( poRing, nCoordPrecision ); + if( poObjRing == NULL ) + { + json_object_put(poObj); + return NULL; + } + json_object_array_add( poObj, poObjRing ); + + /* Interior rings. */ + const int nCount = poPolygon->getNumInteriorRings(); + for( int i = 0; i < nCount; ++i ) + { + poRing = poPolygon->getInteriorRing( i ); + if (poRing == NULL) + continue; + + poObjRing = OGRGeoJSONWriteLineCoords( poRing, nCoordPrecision ); + if( poObjRing == NULL ) + { + json_object_put(poObj); + return NULL; + } + + json_object_array_add( poObj, poObjRing ); + } + + return poObj; +} + +/************************************************************************/ +/* OGRGeoJSONWriteMultiPoint */ +/************************************************************************/ + +json_object* OGRGeoJSONWriteMultiPoint( OGRMultiPoint* poGeometry, int nCoordPrecision ) +{ + CPLAssert( NULL != poGeometry ); + + /* Generate "coordinates" object for 2D or 3D dimension. */ + json_object* poObj = NULL; + poObj = json_object_new_array(); + + for( int i = 0; i < poGeometry->getNumGeometries(); ++i ) + { + OGRGeometry* poGeom = poGeometry->getGeometryRef( i ); + CPLAssert( NULL != poGeom ); + OGRPoint* poPoint = static_cast(poGeom); + + json_object* poObjPoint = NULL; + poObjPoint = OGRGeoJSONWritePoint( poPoint, nCoordPrecision ); + if( poObjPoint == NULL ) + { + json_object_put(poObj); + return NULL; + } + + json_object_array_add( poObj, poObjPoint ); + } + + return poObj; +} + +/************************************************************************/ +/* OGRGeoJSONWriteMultiLineString */ +/************************************************************************/ + +json_object* OGRGeoJSONWriteMultiLineString( OGRMultiLineString* poGeometry, int nCoordPrecision ) +{ + CPLAssert( NULL != poGeometry ); + + /* Generate "coordinates" object for 2D or 3D dimension. */ + json_object* poObj = NULL; + poObj = json_object_new_array(); + + for( int i = 0; i < poGeometry->getNumGeometries(); ++i ) + { + OGRGeometry* poGeom = poGeometry->getGeometryRef( i ); + CPLAssert( NULL != poGeom ); + OGRLineString* poLine = static_cast(poGeom); + + json_object* poObjLine = NULL; + poObjLine = OGRGeoJSONWriteLineString( poLine, nCoordPrecision ); + if( poObjLine == NULL ) + { + json_object_put(poObj); + return NULL; + } + + json_object_array_add( poObj, poObjLine ); + } + + return poObj; +} + +/************************************************************************/ +/* OGRGeoJSONWriteMultiPolygon */ +/************************************************************************/ + +json_object* OGRGeoJSONWriteMultiPolygon( OGRMultiPolygon* poGeometry, int nCoordPrecision ) +{ + CPLAssert( NULL != poGeometry ); + + /* Generate "coordinates" object for 2D or 3D dimension. */ + json_object* poObj = NULL; + poObj = json_object_new_array(); + + for( int i = 0; i < poGeometry->getNumGeometries(); ++i ) + { + OGRGeometry* poGeom = poGeometry->getGeometryRef( i ); + CPLAssert( NULL != poGeom ); + OGRPolygon* poPoly = static_cast(poGeom); + + json_object* poObjPoly = NULL; + poObjPoly = OGRGeoJSONWritePolygon( poPoly, nCoordPrecision ); + if( poObjPoly == NULL ) + { + json_object_put(poObj); + return NULL; + } + + json_object_array_add( poObj, poObjPoly ); + } + + return poObj; +} + +/************************************************************************/ +/* OGRGeoJSONWriteGeometryCollection */ +/************************************************************************/ + +json_object* OGRGeoJSONWriteGeometryCollection( OGRGeometryCollection* poGeometry, int nCoordPrecision ) +{ + CPLAssert( NULL != poGeometry ); + + /* Generate "geometries" object. */ + json_object* poObj = NULL; + poObj = json_object_new_array(); + + for( int i = 0; i < poGeometry->getNumGeometries(); ++i ) + { + OGRGeometry* poGeom = poGeometry->getGeometryRef( i ); + CPLAssert( NULL != poGeom ); + + json_object* poObjGeom = NULL; + poObjGeom = OGRGeoJSONWriteGeometry( poGeom, nCoordPrecision ); + if( poGeom == NULL ) + { + json_object_put(poObj); + return NULL; + } + + json_object_array_add( poObj, poObjGeom ); + } + + return poObj; +} +/************************************************************************/ +/* OGRGeoJSONWriteCoords */ +/************************************************************************/ + +json_object* OGRGeoJSONWriteCoords( double const& fX, double const& fY, int nCoordPrecision ) +{ + json_object* poObjCoords = NULL; + if( CPLIsInf(fX) || CPLIsInf(fY) || + CPLIsNan(fX) || CPLIsNan(fY) ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Infinite or NaN coordinate encountered"); + return NULL; + } + poObjCoords = json_object_new_array(); + json_object_array_add( poObjCoords, json_object_new_double_with_precision( fX, nCoordPrecision ) ); + json_object_array_add( poObjCoords, json_object_new_double_with_precision( fY, nCoordPrecision ) ); + + return poObjCoords; +} + +json_object* OGRGeoJSONWriteCoords( double const& fX, double const& fY, double const& fZ, int nCoordPrecision ) +{ + json_object* poObjCoords = NULL; + if( CPLIsInf(fX) || CPLIsInf(fY) || CPLIsInf(fZ) || + CPLIsNan(fX) || CPLIsNan(fY) || CPLIsNan(fZ) ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Infinite or NaN coordinate encountered"); + return NULL; + } + poObjCoords = json_object_new_array(); + json_object_array_add( poObjCoords, json_object_new_double_with_precision( fX, nCoordPrecision ) ); + json_object_array_add( poObjCoords, json_object_new_double_with_precision( fY, nCoordPrecision ) ); + json_object_array_add( poObjCoords, json_object_new_double_with_precision( fZ, nCoordPrecision ) ); + + return poObjCoords; +} + +/************************************************************************/ +/* OGRGeoJSONWriteLineCoords */ +/************************************************************************/ + +json_object* OGRGeoJSONWriteLineCoords( OGRLineString* poLine, int nCoordPrecision ) +{ + json_object* poObjPoint = NULL; + json_object* poObjCoords = json_object_new_array(); + + const int nCount = poLine->getNumPoints(); + for( int i = 0; i < nCount; ++i ) + { + if( poLine->getCoordinateDimension() == 2 ) + poObjPoint = OGRGeoJSONWriteCoords( poLine->getX(i), poLine->getY(i), nCoordPrecision ); + else + poObjPoint = OGRGeoJSONWriteCoords( poLine->getX(i), poLine->getY(i), poLine->getZ(i), nCoordPrecision ); + if( poObjPoint == NULL ) + { + json_object_put(poObjCoords); + return NULL; + } + json_object_array_add( poObjCoords, poObjPoint ); + } + + return poObjCoords; +} + +/************************************************************************/ +/* OGR_G_ExportToJson */ +/************************************************************************/ + +/** + * \brief Convert a geometry into GeoJSON format. + * + * The returned string should be freed with CPLFree() when no longer required. + * + * This method is the same as the C++ method OGRGeometry::exportToJson(). + * + * @param hGeometry handle to the geometry. + * @return A GeoJSON fragment or NULL in case of error. + */ + +char* OGR_G_ExportToJson( OGRGeometryH hGeometry ) +{ + return OGR_G_ExportToJsonEx(hGeometry, NULL); +} + +/************************************************************************/ +/* OGR_G_ExportToJsonEx */ +/************************************************************************/ + +/** + * \brief Convert a geometry into GeoJSON format. + * + * The returned string should be freed with CPLFree() when no longer required. + * + * This method is the same as the C++ method OGRGeometry::exportToJson(). + * + * @param hGeometry handle to the geometry. + * @param papszOptions a null terminated list of options. For now, only COORDINATE_PRECISION=int_number + * where int_number is the maximum number of figures after decimal separator to write in coordinates. + * @return A GeoJSON fragment or NULL in case of error. + * + * @since OGR 1.9.0 + */ + +char* OGR_G_ExportToJsonEx( OGRGeometryH hGeometry, char** papszOptions ) +{ + VALIDATE_POINTER1( hGeometry, "OGR_G_ExportToJson", NULL ); + + OGRGeometry* poGeometry = (OGRGeometry*) (hGeometry); + + int nCoordPrecision = atoi(CSLFetchNameValueDef(papszOptions, "COORDINATE_PRECISION", "-1")); + + json_object* poObj = NULL; + poObj = OGRGeoJSONWriteGeometry( poGeometry, nCoordPrecision ); + + if( NULL != poObj ) + { + char* pszJson = CPLStrdup( json_object_to_json_string( poObj ) ); + + /* Release JSON tree. */ + json_object_put( poObj ); + + return pszJson; + } + + /* Translation failed */ + return NULL; +} + +/************************************************************************/ +/* OGR_json_double_with_precision_to_string() */ +/************************************************************************/ + +static int OGR_json_double_with_precision_to_string(struct json_object *jso, + struct printbuf *pb, + CPL_UNUSED int level, + CPL_UNUSED int flags) +{ + char szBuffer[75]; + int nPrecision = (int) (size_t) jso->_userdata; + OGRFormatDouble( szBuffer, sizeof(szBuffer), jso->o.c_double, '.', + (nPrecision < 0) ? 15 : nPrecision ); + if( szBuffer[0] == 't' /*oobig */ ) + { + CPLsnprintf(szBuffer, sizeof(szBuffer), "%.18g", jso->o.c_double); + } + return printbuf_memappend(pb, szBuffer, strlen(szBuffer)); +} + +/************************************************************************/ +/* json_object_new_double_with_precision() */ +/************************************************************************/ + +json_object* json_object_new_double_with_precision(double dfVal, + int nCoordPrecision) +{ + json_object* jso = json_object_new_double(dfVal); + json_object_set_serializer(jso, OGR_json_double_with_precision_to_string, + (void*)(size_t)nCoordPrecision, NULL ); + return jso; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonwriter.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonwriter.h new file mode 100644 index 000000000..93ae2bca1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrgeojsonwriter.h @@ -0,0 +1,76 @@ +/****************************************************************************** + * $Id: ogrgeojsonwriter.h 29243 2015-05-24 15:53:26Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Defines GeoJSON reader within OGR OGRGeoJSON Driver. + * Author: Mateusz Loskot, mateusz@loskot.net + * + ****************************************************************************** + * Copyright (c) 2007, Mateusz Loskot + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef OGR_GEOJSONWRITER_H_INCLUDED +#define OGR_GEOJSONWRITER_H_INCLUDED + +#include +#include // JSON-C + +/************************************************************************/ +/* FORWARD DECLARATIONS */ +/************************************************************************/ +#ifdef __cplusplus +class OGRFeature; +class OGRGeometry; +class OGRPoint; +class OGRMultiPoint; +class OGRLineString; +class OGRMultiLineString; +class OGRLinearRing; +class OGRPolygon; +class OGRMultiPolygon; +class OGRGeometryCollection; +#endif + +CPL_C_START +json_object CPL_DLL *json_object_new_double_with_precision(double dfVal, int nCoordPrecision); +CPL_C_END + +/************************************************************************/ +/* GeoJSON Geometry Translators */ +/************************************************************************/ +#ifdef __cplusplus +json_object* OGRGeoJSONWriteFeature( OGRFeature* poFeature, int bWriteBBOX, int nCoordPrecision ); +json_object* OGRGeoJSONWriteAttributes( OGRFeature* poFeature ); +json_object* OGRGeoJSONWriteGeometry( OGRGeometry* poGeometry, int nCoordPrecision ); +json_object* OGRGeoJSONWritePoint( OGRPoint* poPoint, int nCoordPrecision ); +json_object* OGRGeoJSONWriteLineString( OGRLineString* poLine, int nCoordPrecision ); +json_object* OGRGeoJSONWritePolygon( OGRPolygon* poPolygon, int nCoordPrecision ); +json_object* OGRGeoJSONWriteMultiPoint( OGRMultiPoint* poGeometry, int nCoordPrecision ); +json_object* OGRGeoJSONWriteMultiLineString( OGRMultiLineString* poGeometry, int nCoordPrecision ); +json_object* OGRGeoJSONWriteMultiPolygon( OGRMultiPolygon* poGeometry, int nCoordPrecision ); +json_object* OGRGeoJSONWriteGeometryCollection( OGRGeometryCollection* poGeometry, int nCoordPrecision ); + +json_object* OGRGeoJSONWriteCoords( double const& fX, double const& fY, int nCoordPrecision ); +json_object* OGRGeoJSONWriteCoords( double const& fX, double const& fY, double const& fZ, int nCoordPrecision ); +json_object* OGRGeoJSONWriteLineCoords( OGRLineString* poLine, int nCoordPrecision ); +#endif + +#endif /* OGR_GEOJSONWRITER_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrtopojsonreader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrtopojsonreader.cpp new file mode 100644 index 000000000..5daeaa5fc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geojson/ogrtopojsonreader.cpp @@ -0,0 +1,625 @@ +/****************************************************************************** + * $Id: ogrtopojsonreader.cpp 28886 2015-04-12 23:09:13Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implementation of OGRTopoJSONReader class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrgeojsonreader.h" +#include "ogrgeojsonutils.h" +#include "ogr_geojson.h" +#include // JSON-C +#include + +/************************************************************************/ +/* OGRTopoJSONReader() */ +/************************************************************************/ + +OGRTopoJSONReader::OGRTopoJSONReader() + : poGJObject_( NULL ) +{ + // Take a deep breath and get to work. +} + +/************************************************************************/ +/* ~OGRTopoJSONReader() */ +/************************************************************************/ + +OGRTopoJSONReader::~OGRTopoJSONReader() +{ + if( NULL != poGJObject_ ) + { + json_object_put(poGJObject_); + } + + poGJObject_ = NULL; +} + +/************************************************************************/ +/* Parse() */ +/************************************************************************/ + +OGRErr OGRTopoJSONReader::Parse( const char* pszText ) +{ + if( NULL != pszText ) + { + json_tokener* jstok = NULL; + json_object* jsobj = NULL; + + jstok = json_tokener_new(); + jsobj = json_tokener_parse_ex(jstok, pszText, -1); + if( jstok->err != json_tokener_success) + { + CPLError( CE_Failure, CPLE_AppDefined, + "TopoJSON parsing error: %s (at offset %d)", + json_tokener_error_desc(jstok->err), jstok->char_offset); + + json_tokener_free(jstok); + return OGRERR_CORRUPT_DATA; + } + json_tokener_free(jstok); + + /* JSON tree is shared for while lifetime of the reader object + * and will be released in the destructor. + */ + poGJObject_ = jsobj; + } + + return OGRERR_NONE; +} + +typedef struct +{ + double dfScale0, dfScale1; + double dfTranslate0, dfTranslate1; +} ScalingParams; + +/************************************************************************/ +/* ParsePoint() */ +/************************************************************************/ + +static int ParsePoint(json_object* poPoint, double* pdfX, double* pdfY) +{ + if( poPoint != NULL && json_type_array == json_object_get_type(poPoint) && + json_object_array_length(poPoint) == 2 ) + { + json_object* poX = json_object_array_get_idx(poPoint, 0); + json_object* poY = json_object_array_get_idx(poPoint, 1); + if( poX != NULL && + (json_type_int == json_object_get_type(poX) || + json_type_double == json_object_get_type(poX)) && + poY != NULL && + (json_type_int == json_object_get_type(poY) || + json_type_double == json_object_get_type(poY)) ) + { + *pdfX = json_object_get_double(poX); + *pdfY = json_object_get_double(poY); + return TRUE; + } + } + return FALSE; +} + +/************************************************************************/ +/* ParseArc() */ +/************************************************************************/ + +static void ParseArc(OGRLineString* poLS, json_object* poArcsDB, int nArcID, + int bReverse, ScalingParams* psParams) +{ + json_object* poArcDB = json_object_array_get_idx(poArcsDB, nArcID); + if( poArcDB == NULL || json_type_array != json_object_get_type(poArcDB) ) + return; + int nPoints = json_object_array_length(poArcDB); + double dfAccX = 0, dfAccY = 0; + int nBaseIndice = poLS->getNumPoints(); + for(int i=0; idfScale0 + psParams->dfTranslate0; + double dfY = dfAccY * psParams->dfScale1 + psParams->dfTranslate1; + if( i == 0 ) + { + if( !bReverse && poLS->getNumPoints() > 0 ) + { + poLS->setNumPoints( nBaseIndice + nPoints - 1 ); + nBaseIndice --; + continue; + } + else if( bReverse && poLS->getNumPoints() > 0 ) + { + poLS->setNumPoints( nBaseIndice + nPoints - 1 ); + nPoints --; + if( nPoints == 0 ) + break; + } + else + poLS->setNumPoints( nBaseIndice + nPoints ); + } + + if( !bReverse ) + poLS->setPoint(nBaseIndice + i, dfX, dfY); + else + poLS->setPoint(nBaseIndice + nPoints - 1 - i, dfX, dfY); + } + } +} + +/************************************************************************/ +/* ParseLineString() */ +/************************************************************************/ + +static void ParseLineString(OGRLineString* poLS, json_object* poRing, + json_object* poArcsDB, ScalingParams* psParams) +{ + int nArcsDB = json_object_array_length(poArcsDB); + + int nArcsRing = json_object_array_length(poRing); + for(int j=0; jaddRingDirectly(poLR); + + json_object* poRing = json_object_array_get_idx(poArcsObj, i); + if( poRing != NULL && json_type_array == json_object_get_type(poRing) ) + { + ParseLineString(poLR, poRing, poArcsDB, psParams); + } + } +} + +/************************************************************************/ +/* ParseMultiLineString() */ +/************************************************************************/ + +static void ParseMultiLineString(OGRMultiLineString* poMLS, json_object* poArcsObj, + json_object* poArcsDB, ScalingParams* psParams) +{ + int nRings = json_object_array_length(poArcsObj); + for(int i=0; iaddGeometryDirectly(poLS); + + json_object* poRing = json_object_array_get_idx(poArcsObj, i); + if( poRing != NULL && json_type_array == json_object_get_type(poRing) ) + { + ParseLineString(poLS, poRing, poArcsDB, psParams); + } + } +} + +/************************************************************************/ +/* ParseMultiPolygon() */ +/************************************************************************/ + +static void ParseMultiPolygon(OGRMultiPolygon* poMultiPoly, json_object* poArcsObj, + json_object* poArcsDB, ScalingParams* psParams) +{ + int nPolys = json_object_array_length(poArcsObj); + for(int i=0; iaddGeometryDirectly(poPoly); + + json_object* poPolyArcs = json_object_array_get_idx(poArcsObj, i); + if( poPolyArcs != NULL && json_type_array == json_object_get_type(poPolyArcs) ) + { + ParsePolygon(poPoly, poPolyArcs, poArcsDB, psParams); + } + } +} + +/************************************************************************/ +/* ParseObject() */ +/************************************************************************/ + +static void ParseObject(const char* pszId, + json_object* poObj, OGRGeoJSONLayer* poLayer, + json_object* poArcsDB, ScalingParams* psParams) +{ + json_object* poType = OGRGeoJSONFindMemberByName(poObj, "type"); + if( poType == NULL || json_object_get_type(poType) != json_type_string ) + return; + const char* pszType = json_object_get_string(poType); + + json_object* poArcsObj = OGRGeoJSONFindMemberByName(poObj, "arcs"); + json_object* poCoordinatesObj = OGRGeoJSONFindMemberByName(poObj, "coordinates"); + if( strcmp(pszType, "Point") == 0 || strcmp(pszType, "MultiPoint") == 0 ) + { + if( poCoordinatesObj == NULL || json_type_array != json_object_get_type(poCoordinatesObj) ) + return; + } + else + { + if( poArcsObj == NULL || json_type_array != json_object_get_type(poArcsObj) ) + return; + } + + if( pszId == NULL ) + { + json_object* poId = OGRGeoJSONFindMemberByName(poObj, "id"); + if( poId != NULL && + (json_type_string == json_object_get_type(poId) || + json_type_int == json_object_get_type(poId)) ) + { + pszId = json_object_get_string(poId); + } + } + + OGRFeature* poFeature = new OGRFeature(poLayer->GetLayerDefn()); + if( pszId != NULL ) + poFeature->SetField("id", pszId); + + json_object* poProperties = OGRGeoJSONFindMemberByName(poObj, "properties"); + if( poProperties != NULL && json_type_object == json_object_get_type(poProperties) ) + { + int nField = -1; + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poProperties, it ) + { + nField = poFeature->GetFieldIndex(it.key); + OGRGeoJSONReaderSetField(poLayer, poFeature, nField, it.key, it.val, FALSE, 0); + } + } + + OGRGeometry* poGeom = NULL; + if( strcmp(pszType, "Point") == 0 ) + { + double dfX = 0.0, dfY = 0.0; + if( ParsePoint( poCoordinatesObj, &dfX, &dfY ) ) + { + dfX = dfX * psParams->dfScale0 + psParams->dfTranslate0; + dfY = dfY * psParams->dfScale1 + psParams->dfTranslate1; + poGeom = new OGRPoint(dfX, dfY); + } + else + poGeom = new OGRPoint(); + } + else if( strcmp(pszType, "MultiPoint") == 0 ) + { + OGRMultiPoint* poMP = new OGRMultiPoint(); + poGeom = poMP; + int nTuples = json_object_array_length(poCoordinatesObj); + for(int i=0; idfScale0 + psParams->dfTranslate0; + dfY = dfY * psParams->dfScale1 + psParams->dfTranslate1; + poMP->addGeometryDirectly(new OGRPoint(dfX, dfY)); + } + } + } + else if( strcmp(pszType, "LineString") == 0 ) + { + OGRLineString* poLS = new OGRLineString(); + poGeom = poLS; + ParseLineString(poLS, poArcsObj, poArcsDB, psParams); + } + else if( strcmp(pszType, "MultiLineString") == 0 ) + { + OGRMultiLineString* poMLS = new OGRMultiLineString(); + poGeom = poMLS; + ParseMultiLineString(poMLS, poArcsObj, poArcsDB, psParams); + } + else if( strcmp(pszType, "Polygon") == 0 ) + { + OGRPolygon* poPoly = new OGRPolygon(); + poGeom = poPoly; + ParsePolygon(poPoly, poArcsObj, poArcsDB, psParams); + } + else if( strcmp(pszType, "MultiPolygon") == 0 ) + { + OGRMultiPolygon* poMultiPoly = new OGRMultiPolygon(); + poGeom = poMultiPoly; + ParseMultiPolygon(poMultiPoly, poArcsObj, poArcsDB, psParams); + } + + if( poGeom != NULL ) + poFeature->SetGeometryDirectly(poGeom); + poLayer->AddFeature(poFeature); + delete poFeature; +} + + +/************************************************************************/ +/* EstablishLayerDefn() */ +/************************************************************************/ + +static void EstablishLayerDefn(OGRFeatureDefn* poDefn, + json_object* poObj) +{ + json_object* poObjProps = OGRGeoJSONFindMemberByName( poObj, "properties" ); + if( NULL != poObjProps && + json_object_get_type(poObjProps) == json_type_object ) + { + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poObjProps, it ) + { + OGRGeoJSONReaderAddOrUpdateField(poDefn, it.key, it.val, FALSE, 0); + } + } +} + +/************************************************************************/ +/* ParseObjectMain() */ +/************************************************************************/ + +static int ParseObjectMain(const char* pszId, json_object* poObj, + OGRGeoJSONDataSource* poDS, + OGRGeoJSONLayer **ppoMainLayer, + json_object* poArcs, + ScalingParams* psParams, + int nPassNumber) +{ + int bNeedSecondPass = FALSE; + + if( poObj != NULL && json_type_object == json_object_get_type( poObj ) ) + { + json_object* poType = OGRGeoJSONFindMemberByName(poObj, "type"); + if( poType != NULL && json_type_string == json_object_get_type( poType ) ) + { + const char* pszType = json_object_get_string(poType); + if( nPassNumber == 1 && strcmp(pszType, "GeometryCollection") == 0 ) + { + json_object* poGeometries = OGRGeoJSONFindMemberByName(poObj, "geometries"); + if( poGeometries != NULL && + json_type_array == json_object_get_type( poGeometries ) ) + { + if( pszId == NULL ) + { + json_object* poId = OGRGeoJSONFindMemberByName(poObj, "id"); + if( poId != NULL && + (json_type_string == json_object_get_type(poId) || + json_type_int == json_object_get_type(poId)) ) + { + pszId = json_object_get_string(poId); + } + } + + OGRGeoJSONLayer* poLayer = new OGRGeoJSONLayer( + pszId ? pszId : "TopoJSON", NULL, + wkbUnknown, poDS ); + OGRFeatureDefn* poDefn = poLayer->GetLayerDefn(); + { + OGRFieldDefn fldDefn( "id", OFTString ); + poDefn->AddFieldDefn( &fldDefn ); + } + + int nGeometries = json_object_array_length(poGeometries); + /* First pass to establish schema */ + for(int i=0; iAddLayer(poLayer); + } + } + else if( strcmp(pszType, "Point") == 0 || + strcmp(pszType, "MultiPoint") == 0 || + strcmp(pszType, "LineString") == 0 || + strcmp(pszType, "MultiLineString") == 0 || + strcmp(pszType, "Polygon") == 0 || + strcmp(pszType, "MultiPolygon") == 0 ) + { + if( nPassNumber == 1 ) + { + if( *ppoMainLayer == NULL ) + { + *ppoMainLayer = new OGRGeoJSONLayer( + "TopoJSON", NULL, wkbUnknown, poDS ); + { + OGRFieldDefn fldDefn( "id", OFTString ); + (*ppoMainLayer)->GetLayerDefn()->AddFieldDefn( &fldDefn ); + } + } + OGRFeatureDefn* poDefn = (*ppoMainLayer)->GetLayerDefn(); + EstablishLayerDefn(poDefn, poObj); + bNeedSecondPass = TRUE; + } + else + ParseObject(pszId, poObj, *ppoMainLayer, poArcs, psParams); + } + } + } + return bNeedSecondPass; +} + +/************************************************************************/ +/* ReadLayers() */ +/************************************************************************/ + +void OGRTopoJSONReader::ReadLayers( OGRGeoJSONDataSource* poDS ) +{ + if( NULL == poGJObject_ ) + { + CPLDebug( "TopoJSON", + "Missing parset TopoJSON data. Forgot to call Parse()?" ); + return; + } + + ScalingParams sParams; + sParams.dfScale0 = 1.0; + sParams.dfScale1 = 1.0; + sParams.dfTranslate0 = 0.0; + sParams.dfTranslate1 = 0.0; + json_object* poObjTransform = OGRGeoJSONFindMemberByName( poGJObject_, "transform" ); + if( NULL != poObjTransform && json_type_object == json_object_get_type( poObjTransform ) ) + { + json_object* poObjScale = OGRGeoJSONFindMemberByName( poObjTransform, "scale" ); + if( NULL != poObjScale && json_type_array == json_object_get_type( poObjScale ) && + json_object_array_length( poObjScale ) == 2 ) + { + json_object* poScale0 = json_object_array_get_idx(poObjScale, 0); + json_object* poScale1 = json_object_array_get_idx(poObjScale, 1); + if( poScale0 != NULL && + (json_object_get_type(poScale0) == json_type_double || + json_object_get_type(poScale0) == json_type_int) && + poScale1 != NULL && + (json_object_get_type(poScale1) == json_type_double || + json_object_get_type(poScale1) == json_type_int) ) + { + sParams.dfScale0 = json_object_get_double(poScale0); + sParams.dfScale1 = json_object_get_double(poScale1); + } + } + + json_object* poObjTranslate = OGRGeoJSONFindMemberByName( poObjTransform, "translate" ); + if( NULL != poObjTranslate && json_type_array == json_object_get_type( poObjTranslate ) && + json_object_array_length( poObjTranslate ) == 2 ) + { + json_object* poTranslate0 = json_object_array_get_idx(poObjTranslate, 0); + json_object* poTranslate1 = json_object_array_get_idx(poObjTranslate, 1); + if( poTranslate0 != NULL && + (json_object_get_type(poTranslate0) == json_type_double || + json_object_get_type(poTranslate0) == json_type_int) && + poTranslate1 != NULL && + (json_object_get_type(poTranslate1) == json_type_double || + json_object_get_type(poTranslate1) == json_type_int) ) + { + sParams.dfTranslate0 = json_object_get_double(poTranslate0); + sParams.dfTranslate1 = json_object_get_double(poTranslate1); + } + } + } + + json_object* poArcs = OGRGeoJSONFindMemberByName( poGJObject_, "arcs" ); + if( poArcs == NULL || json_type_array != json_object_get_type( poArcs ) ) + return; + + OGRGeoJSONLayer* poMainLayer = NULL; + + json_object* poObjects = OGRGeoJSONFindMemberByName( poGJObject_, "objects" ); + if( poObjects == NULL ) + return; + + if( json_type_object == json_object_get_type( poObjects ) ) + { + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + int bNeedSecondPass = FALSE; + json_object_object_foreachC( poObjects, it ) + { + json_object* poObj = it.val; + bNeedSecondPass |= ParseObjectMain(it.key, poObj, poDS, &poMainLayer, poArcs, &sParams, 1); + } + if( bNeedSecondPass ) + { + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poObjects, it ) + { + json_object* poObj = it.val; + ParseObjectMain(it.key, poObj, poDS, &poMainLayer, poArcs, &sParams, 2); + } + } + } + else if( json_type_array == json_object_get_type( poObjects ) ) + { + int nObjects = json_object_array_length(poObjects); + int bNeedSecondPass = FALSE; + for(int i=0; iAddLayer(poMainLayer); + +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/GNUmakefile new file mode 100644 index 000000000..53aa63d2b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/GNUmakefile @@ -0,0 +1,15 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrgeomediadatasource.o ogrgeomedialayer.o ogrgeomediadriver.o \ + ogrgeomediatablelayer.o ogrgeomediaselectlayer.o + +CPPFLAGS := -I.. -I../.. -I../pgeo $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_geomedia.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/drv_geomedia.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/drv_geomedia.html new file mode 100644 index 000000000..557237bac --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/drv_geomedia.html @@ -0,0 +1,65 @@ + + + + + + Geomedia MDB database + + + +

    Geomedia MDB database

    + +

    GDAL/OGR >= 1.9.0

    + +

    OGR optionally supports reading Geomedia .mdb + files via ODBC. Geomedia is a Microsoft Access + database with a set of tables defined by Intergraph for holding + geodatabase metadata, and with geometry for features held in a + BLOB column in a custom format. This drivers accesses the database via + ODBC but does not depend on any Intergraph middle-ware.

    + +

    Geomedia .mdb are accessed by passing the file name of + the .mdb file to be accessed as the data source name. On Windows, + no ODBC DSN is required. On Linux, there are problems with DSN-less + connection due to incomplete or buggy implementation of this feature + in the MDB Tools package, + So, it is required to configure Data Source Name (DSN) if the MDB + Tools driver is used (check instructions below).

    + +

    In order to facilitate compatibility with different configurations, + the GEOMEDIA_DRIVER_TEMPLATE Config Option was added to provide a way to + programmatically set the DSN programmatically with the filename as + an argument. In cases where the driver name is known, this allows for + the construction of the DSN based on that information in a manner similar + to the default (used for Windows access to the Microsoft Access Driver).

    + +

    OGR treats all feature tables as layers. Most geometry types + should be supported (arcs are not yet). Coordinate system information + is not currently supported.

    + +

    Currently the OGR Personal Geodatabase driver does not take + advantage of spatial indexes for fast spatial queries.

    + +

    By default, SQL statements are passed directly to the MDB database engine. +It's also possible to request the driver to handle SQL commands +with OGR SQL engine, +by passing "OGRSQL" string to the ExecuteSQL() +method, as name of the SQL dialect.

    + +

    How to use Geomedia driver with unixODBC and MDB Tools (on Unix and Linux)

    + +

    Starting with GDAL/OGR 1.9.0, the MDB driver is an alternate + way of reading Geomedia .mdb files without requiring unixODBC and MDB Tools

    + +

    Refer to the similar section of the PGeo driver. The prefix to use + for this driver is Geomedia:

    + +

    See also

    + +
      +
    • MDB driver page
    • +
    + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/makefile.vc new file mode 100644 index 000000000..fe0522a29 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/makefile.vc @@ -0,0 +1,14 @@ + +OBJ = ogrgeomediadriver.obj ogrgeomediadatasource.obj ogrgeomedialayer.obj \ + ogrgeomediatablelayer.obj ogrgeomediaselectlayer.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. -I..\pgeo + +default: $(OBJ) + +clean: + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogr_geomedia.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogr_geomedia.h new file mode 100644 index 000000000..091423e15 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogr_geomedia.h @@ -0,0 +1,209 @@ +/****************************************************************************** + * $Id: ogr_geomedia.h 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for Geomedia MDB driver. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GEOMEDIA_H_INCLUDED +#define _OGR_GEOMEDIA_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_odbc.h" +#include "cpl_error.h" +#include "ogr_pgeo.h" + +/************************************************************************/ +/* OGRGeomediaLayer */ +/************************************************************************/ + +class OGRGeomediaDataSource; + +class OGRGeomediaLayer : public OGRLayer +{ + protected: + OGRFeatureDefn *poFeatureDefn; + + CPLODBCStatement *poStmt; + + // Layer spatial reference system, and srid. + OGRSpatialReference *poSRS; + int nSRSId; + + GIntBig iNextShapeId; + + OGRGeomediaDataSource *poDS; + + char *pszGeomColumn; + char *pszFIDColumn; + + int *panFieldOrdinals; + + CPLErr BuildFeatureDefn( const char *pszLayerName, + CPLODBCStatement *poStmt ); + + virtual CPLODBCStatement * GetStatement() { return poStmt; } + + void LookupSRID( int ); + + public: + OGRGeomediaLayer(); + virtual ~OGRGeomediaLayer(); + + virtual void ResetReading(); + virtual OGRFeature *GetNextRawFeature(); + virtual OGRFeature *GetNextFeature(); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); + + virtual const char *GetFIDColumn(); + virtual const char *GetGeometryColumn(); +}; + +/************************************************************************/ +/* OGRGeomediaTableLayer */ +/************************************************************************/ + +class OGRGeomediaTableLayer : public OGRGeomediaLayer +{ + int bUpdateAccess; + + char *pszQuery; + + void ClearStatement(); + OGRErr ResetStatement(); + + virtual CPLODBCStatement * GetStatement(); + + public: + OGRGeomediaTableLayer( OGRGeomediaDataSource * ); + ~OGRGeomediaTableLayer(); + + CPLErr Initialize( const char *pszTableName, + const char *pszGeomCol, + OGRSpatialReference* poSRS ); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual OGRErr SetAttributeFilter( const char * ); + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRGeomediaSelectLayer */ +/************************************************************************/ + +class OGRGeomediaSelectLayer : public OGRGeomediaLayer +{ + char *pszBaseStatement; + + void ClearStatement(); + OGRErr ResetStatement(); + + virtual CPLODBCStatement * GetStatement(); + + public: + OGRGeomediaSelectLayer( OGRGeomediaDataSource *, + CPLODBCStatement * ); + ~OGRGeomediaSelectLayer(); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRGeomediaDataSource */ +/************************************************************************/ + +class OGRGeomediaDataSource : public OGRDataSource +{ + OGRGeomediaLayer **papoLayers; + int nLayers; + + OGRGeomediaLayer **papoLayersInvisible; + int nLayersWithInvisible; + + char *pszName; + + int bDSUpdate; + CPLODBCSession oSession; + + CPLString GetTableNameFromType(const char* pszTableType); + OGRSpatialReference* GetGeomediaSRS(const char* pszGCoordSystemTable, + const char* pszGCoordSystemGUID); + + public: + OGRGeomediaDataSource(); + ~OGRGeomediaDataSource(); + + int Open( const char *, int bUpdate, int bTestOpen ); + int OpenTable( const char *pszTableName, + const char *pszGeomCol, + int bUpdate ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + OGRLayer *GetLayerByName( const char* pszLayerName ); + + int TestCapability( const char * ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + // Internal use + CPLODBCSession *GetSession() { return &oSession; } +}; + +/************************************************************************/ +/* OGRGeomediaDriver */ +/************************************************************************/ + +class OGRGeomediaDriver : public OGRODBCMDBDriver +{ + public: + ~OGRGeomediaDriver(); + + const char *GetName(); + OGRDataSource *Open( const char *, int ); + + int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_Geomedia_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomediadatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomediadatasource.cpp new file mode 100644 index 000000000..acaaeb46f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomediadatasource.cpp @@ -0,0 +1,444 @@ +/****************************************************************************** + * $Id: ogrgeomediadatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGeomediaDataSource class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geomedia.h" +#include "ogrgeomediageometry.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: ogrgeomediadatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRGeomediaDataSource() */ +/************************************************************************/ + +OGRGeomediaDataSource::OGRGeomediaDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + papoLayersInvisible = NULL; + nLayers = 0; + nLayersWithInvisible = 0; +} + +/************************************************************************/ +/* ~OGRGeomediaDataSource() */ +/************************************************************************/ + +OGRGeomediaDataSource::~OGRGeomediaDataSource() + +{ + int i; + + CPLFree( pszName ); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + for( i = 0; i < nLayersWithInvisible; i++ ) + delete papoLayersInvisible[i]; + CPLFree( papoLayersInvisible ); +} + +/************************************************************************/ +/* CheckDSNStringTemplate() */ +/* The string will be used as the formatting argument of sprintf with */ +/* a string in vararg. So let's check there's only one '%s', and nothing*/ +/* else */ +/************************************************************************/ + +static int CheckDSNStringTemplate(const char* pszStr) +{ + int nPercentSFound = FALSE; + while(*pszStr) + { + if (*pszStr == '%') + { + if (pszStr[1] != 's') + { + return FALSE; + } + else + { + if (nPercentSFound) + return FALSE; + nPercentSFound = TRUE; + } + } + pszStr ++; + } + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRGeomediaDataSource::Open( const char * pszNewName, int bUpdate, + CPL_UNUSED int bTestOpen ) +{ + CPLAssert( nLayers == 0 ); + +/* -------------------------------------------------------------------- */ +/* If this is the name of an MDB file, then construct the */ +/* appropriate connection string. Otherwise clip of GEOMEDIA: to */ +/* get the DSN. */ +/* */ +/* -------------------------------------------------------------------- */ + char *pszDSN; + if( EQUALN(pszNewName,"GEOMEDIA:",9) ) + pszDSN = CPLStrdup( pszNewName + 9 ); + else + { + const char *pszDSNStringTemplate = NULL; + pszDSNStringTemplate = CPLGetConfigOption( "GEOMEDIA_DRIVER_TEMPLATE", "DRIVER=Microsoft Access Driver (*.mdb);DBQ=%s"); + if (!CheckDSNStringTemplate(pszDSNStringTemplate)) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Illegal value for GEOMEDIA_DRIVER_TEMPLATE option"); + return FALSE; + } + pszDSN = (char *) CPLMalloc(strlen(pszNewName)+strlen(pszDSNStringTemplate)+100); + sprintf( pszDSN, pszDSNStringTemplate, pszNewName ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize based on the DSN. */ +/* -------------------------------------------------------------------- */ + CPLDebug( "Geomedia", "EstablishSession(%s)", pszDSN ); + + if( !oSession.EstablishSession( pszDSN, NULL, NULL ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to initialize ODBC connection to DSN for %s,\n" + "%s", pszDSN, oSession.GetLastError() ); + CPLFree( pszDSN ); + return FALSE; + } + + CPLFree( pszDSN ); + + pszName = CPLStrdup( pszNewName ); + + bDSUpdate = bUpdate; + +/* -------------------------------------------------------------------- */ +/* Collect list of tables and their supporting info from */ +/* GAliasTable. */ +/* -------------------------------------------------------------------- */ + CPLString osGFeaturesTable = GetTableNameFromType("INGRFeatures"); + if (osGFeaturesTable.size() == 0) + return FALSE; + + CPLString osGeometryProperties = GetTableNameFromType("INGRGeometryProperties"); + CPLString osGCoordSystemTable = GetTableNameFromType("GCoordSystemTable"); + + std::vector apapszGeomColumns; + { + CPLODBCStatement oStmt( &oSession ); + oStmt.Appendf( "SELECT FeatureName, PrimaryGeometryFieldName FROM %s", osGFeaturesTable.c_str() ); + + if( !oStmt.ExecuteSQL() ) + { + CPLDebug( "GEOMEDIA", + "SELECT on %s fails, perhaps not a geomedia geodatabase?\n%s", + osGFeaturesTable.c_str(), + oSession.GetLastError() ); + return FALSE; + } + + while( oStmt.Fetch() ) + { + int i, iNew = apapszGeomColumns.size(); + char **papszRecord = NULL; + for( i = 0; i < 2; i++ ) + papszRecord = CSLAddString( papszRecord, + oStmt.GetColData(i) ); + apapszGeomColumns.resize(iNew+1); + apapszGeomColumns[iNew] = papszRecord; + } + } + + std::vector apoSRS; + if (osGeometryProperties.size() != 0 && osGCoordSystemTable.size() != 0) + { + std::vector aosGUID; + { + CPLODBCStatement oStmt( &oSession ); + oStmt.Appendf( "SELECT GCoordSystemGUID FROM %s", osGeometryProperties.c_str() ); + + if( !oStmt.ExecuteSQL() ) + { + CPLDebug( "GEOMEDIA", + "SELECT on %s fails, perhaps not a geomedia geodatabase?\n%s", + osGeometryProperties.c_str(), + oSession.GetLastError() ); + return FALSE; + } + + while( oStmt.Fetch() ) + { + aosGUID.push_back(oStmt.GetColData(0)); + } + + if (apapszGeomColumns.size() != aosGUID.size()) + { + CPLDebug( "GEOMEDIA", "%s and %s don't have the same size", + osGFeaturesTable.c_str(), osGeometryProperties.c_str() ); + return FALSE; + } + } + + int i; + for(i=0; i<(int)aosGUID.size();i++) + { + apoSRS.push_back(GetGeomediaSRS(osGCoordSystemTable, aosGUID[i])); + } + } + +/* -------------------------------------------------------------------- */ +/* Create a layer for each spatial table. */ +/* -------------------------------------------------------------------- */ + unsigned int iTable; + + papoLayers = (OGRGeomediaLayer **) CPLCalloc(apapszGeomColumns.size(), + sizeof(void*)); + + for( iTable = 0; iTable < apapszGeomColumns.size(); iTable++ ) + { + char **papszRecord = apapszGeomColumns[iTable]; + OGRGeomediaTableLayer *poLayer; + + poLayer = new OGRGeomediaTableLayer( this ); + + if( poLayer->Initialize( papszRecord[0], papszRecord[1], (apoSRS.size()) ? apoSRS[iTable] : NULL ) + != CE_None ) + { + delete poLayer; + } + else + { + papoLayers[nLayers++] = poLayer; + } + CSLDestroy(papszRecord); + } + + return TRUE; +} + + +/************************************************************************/ +/* GetTableNameFromType() */ +/************************************************************************/ + +CPLString OGRGeomediaDataSource::GetTableNameFromType(const char* pszTableType) +{ + CPLODBCStatement oStmt( &oSession ); + + oStmt.Appendf( "SELECT TableName FROM GAliasTable WHERE TableType = '%s'", pszTableType ); + + if( !oStmt.ExecuteSQL() ) + { + CPLDebug( "GEOMEDIA", + "SELECT for %s on GAliasTable fails, perhaps not a geomedia geodatabase?\n%s", + pszTableType, + oSession.GetLastError() ); + return ""; + } + + while( oStmt.Fetch() ) + { + return oStmt.GetColData(0); + } + + return ""; +} + + +/************************************************************************/ +/* GetGeomediaSRS() */ +/************************************************************************/ + +OGRSpatialReference* OGRGeomediaDataSource::GetGeomediaSRS(const char* pszGCoordSystemTable, + const char* pszGCoordSystemGUID) +{ + if (pszGCoordSystemTable == NULL || pszGCoordSystemGUID == NULL) + return NULL; + + OGRLayer* poGCoordSystemTable = GetLayerByName(pszGCoordSystemTable); + if (poGCoordSystemTable == NULL) + return NULL; + + poGCoordSystemTable->ResetReading(); + + OGRFeature* poFeature; + while((poFeature = poGCoordSystemTable->GetNextFeature()) != NULL) + { + const char* pszCSGUID = poFeature->GetFieldAsString("CSGUID"); + if (pszCSGUID && strcmp(pszCSGUID, pszGCoordSystemGUID) == 0) + { + OGRSpatialReference* poSRS = OGRGetGeomediaSRS(poFeature); + delete poFeature; + return poSRS; + } + + delete poFeature; + } + + return NULL; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGeomediaDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRGeomediaDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer *OGRGeomediaDataSource::GetLayerByName( const char* pszName ) + +{ + if (pszName == NULL) + return NULL; + OGRLayer* poLayer = OGRDataSource::GetLayerByName(pszName); + if (poLayer) + return poLayer; + + for( int i = 0; i < nLayersWithInvisible; i++ ) + { + poLayer = papoLayersInvisible[i]; + + if( strcmp( pszName, poLayer->GetName() ) == 0 ) + return poLayer; + } + + OGRGeomediaTableLayer *poGeomediaLayer; + + poGeomediaLayer = new OGRGeomediaTableLayer( this ); + + if( poGeomediaLayer->Initialize(pszName, NULL, NULL) != CE_None ) + { + delete poGeomediaLayer; + return NULL; + } + + papoLayersInvisible = (OGRGeomediaLayer**)CPLRealloc(papoLayersInvisible, + (nLayersWithInvisible+1) * sizeof(OGRGeomediaLayer*)); + papoLayersInvisible[nLayersWithInvisible++] = poGeomediaLayer; + + return poGeomediaLayer; +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRGeomediaDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Execute statement. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement *poStmt = new CPLODBCStatement( &oSession ); + + poStmt->Append( pszSQLCommand ); + if( !poStmt->ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", oSession.GetLastError() ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Are there result columns for this statement? */ +/* -------------------------------------------------------------------- */ + if( poStmt->GetColCount() == 0 ) + { + delete poStmt; + CPLErrorReset(); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a results layer. It will take ownership of the */ +/* statement. */ +/* -------------------------------------------------------------------- */ + OGRGeomediaSelectLayer *poLayer = NULL; + + poLayer = new OGRGeomediaSelectLayer( this, poStmt ); + + if( poSpatialFilter != NULL ) + poLayer->SetSpatialFilter( poSpatialFilter ); + + return poLayer; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRGeomediaDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomediadriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomediadriver.cpp new file mode 100644 index 000000000..14ea07529 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomediadriver.cpp @@ -0,0 +1,163 @@ +/****************************************************************************** + * $Id: ogrgeomediadriver.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements Personal Geodatabase driver. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2012, Even Rouault + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geomedia.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrgeomediadriver.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* ~OGRODBCDriver() */ +/************************************************************************/ + +OGRGeomediaDriver::~OGRGeomediaDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRGeomediaDriver::GetName() + +{ + return "Geomedia"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRGeomediaDriver::Open( const char * pszFilename, + int bUpdate ) + +{ + OGRGeomediaDataSource *poDS; + + if( EQUALN(pszFilename, "WALK:", strlen("WALK:")) ) + return NULL; + + if( EQUALN(pszFilename, "PGEO:", strlen("PGEO:")) ) + return NULL; + + if( !EQUALN(pszFilename,"GEOMEDIA:",9) + && !EQUAL(CPLGetExtension(pszFilename),"mdb") ) + return NULL; + + /* Disabling the attempt to guess if a MDB file is a Geomedia database */ + /* or not. See similar fix in PGeo driver for rationale. */ +#if 0 + if( !EQUALN(pszFilename,"GEOMEDIA:",9) && + EQUAL(CPLGetExtension(pszFilename),"mdb") ) + { + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (!fp) + return NULL; + GByte* pabyHeader = (GByte*) CPLMalloc(100000); + VSIFReadL(pabyHeader, 100000, 1, fp); + VSIFCloseL(fp); + + /* Look for GAliasTable table */ + const GByte pabyNeedle[] = { 'G', 0, 'A', 0, 'l', 0, 'i', 0, 'a', 0, 's', 0, 'T', 0, 'a', 0, 'b', 0, 'l', 0, 'e'}; + int bFound = FALSE; + for(int i=0;i<100000 - (int)sizeof(pabyNeedle);i++) + { + if (memcmp(pabyHeader + i, pabyNeedle, sizeof(pabyNeedle)) == 0) + { + bFound = TRUE; + break; + } + } + CPLFree(pabyHeader); + if (!bFound) + return NULL; + } +#endif + +#ifndef WIN32 + // Try to register MDB Tools driver + // + // ODBCINST.INI NOTE: + // This operation requires write access to odbcinst.ini file + // located in directory pointed by ODBCINISYS variable. + // Usually, it points to /etc, so non-root users can overwrite this + // setting ODBCINISYS with location they have write access to, e.g.: + // $ export ODBCINISYS=$HOME/etc + // $ touch $ODBCINISYS/odbcinst.ini + // + // See: http://www.unixodbc.org/internals.html + // + if ( !InstallMdbDriver() ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to install MDB driver for ODBC, MDB access may not supported.\n" ); + } + else + CPLDebug( "Geomedia", "MDB Tools driver installed successfully!"); + +#endif /* ndef WIN32 */ + + // Open data source + poDS = new OGRGeomediaDataSource(); + + if( !poDS->Open( pszFilename, bUpdate, TRUE ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGeomediaDriver::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRODBC() */ +/************************************************************************/ + +void RegisterOGRGeomedia() + +{ + OGRSFDriver* poDriver = new OGRGeomediaDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Geomedia .mdb" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "mdb" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_geomedia.html" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomedialayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomedialayer.cpp new file mode 100644 index 000000000..ad8fc20d6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomedialayer.cpp @@ -0,0 +1,351 @@ +/****************************************************************************** + * $Id: ogrgeomedialayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGeomediaLayer class, code shared between + * the direct table access, and the generic SQL results. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_geomedia.h" +#include "cpl_string.h" +#include "ogrgeomediageometry.h" + +CPL_CVSID("$Id: ogrgeomedialayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRGeomediaLayer() */ +/************************************************************************/ + +OGRGeomediaLayer::OGRGeomediaLayer() + +{ + poDS = NULL; + + pszGeomColumn = NULL; + pszFIDColumn = NULL; + + poStmt = NULL; + + iNextShapeId = 0; + + poSRS = NULL; + nSRSId = -2; // we haven't even queried the database for it yet. +} + +/************************************************************************/ +/* ~OGRGeomediaLayer() */ +/************************************************************************/ + +OGRGeomediaLayer::~OGRGeomediaLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "Geomedia", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poStmt != NULL ) + { + delete poStmt; + poStmt = NULL; + } + + if( poFeatureDefn != NULL ) + { + poFeatureDefn->Release(); + poFeatureDefn = NULL; + } + + CPLFree( pszGeomColumn ); + CPLFree( panFieldOrdinals ); + CPLFree( pszFIDColumn ); + + if( poSRS != NULL ) + { + poSRS->Release(); + poSRS = NULL; + } +} + +/************************************************************************/ +/* BuildFeatureDefn() */ +/* */ +/* Build feature definition from a set of column definitions */ +/* set on a statement. Sift out geometry and FID fields. */ +/************************************************************************/ + +CPLErr OGRGeomediaLayer::BuildFeatureDefn( const char *pszLayerName, + CPLODBCStatement *poStmt ) + +{ + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + int nRawColumns = poStmt->GetColCount(); + + poFeatureDefn->Reference(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + panFieldOrdinals = (int *) CPLMalloc( sizeof(int) * nRawColumns ); + + for( int iCol = 0; iCol < nRawColumns; iCol++ ) + { + OGRFieldDefn oField( poStmt->GetColName(iCol), OFTString ); + + oField.SetWidth( MAX(0,poStmt->GetColSize( iCol )) ); + + if( pszGeomColumn != NULL + && EQUAL(poStmt->GetColName(iCol),pszGeomColumn) ) + continue; + + if( pszGeomColumn == NULL + && EQUAL(poStmt->GetColName(iCol),"Geometry") + && (poStmt->GetColType(iCol) == SQL_BINARY || + poStmt->GetColType(iCol) == SQL_VARBINARY || + poStmt->GetColType(iCol) == SQL_LONGVARBINARY) ) + { + pszGeomColumn = CPLStrdup(poStmt->GetColName(iCol)); + continue; + } + + switch( poStmt->GetColType(iCol) ) + { + case SQL_INTEGER: + case SQL_SMALLINT: + oField.SetType( OFTInteger ); + break; + + case SQL_BINARY: + case SQL_VARBINARY: + case SQL_LONGVARBINARY: + oField.SetType( OFTBinary ); + break; + + case SQL_DECIMAL: + oField.SetType( OFTReal ); + oField.SetPrecision( poStmt->GetColPrecision(iCol) ); + break; + + case SQL_FLOAT: + case SQL_REAL: + case SQL_DOUBLE: + oField.SetType( OFTReal ); + oField.SetWidth( 0 ); + break; + + case SQL_C_DATE: + oField.SetType( OFTDate ); + break; + + case SQL_C_TIME: + oField.SetType( OFTTime ); + break; + + case SQL_C_TIMESTAMP: + oField.SetType( OFTDateTime ); + break; + + default: + /* leave it as OFTString */; + } + + poFeatureDefn->AddFieldDefn( &oField ); + panFieldOrdinals[poFeatureDefn->GetFieldCount() - 1] = iCol+1; + } + + return CE_None; +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGeomediaLayer::ResetReading() + +{ + iNextShapeId = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRGeomediaLayer::GetNextFeature() + +{ + for( ; TRUE; ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRGeomediaLayer::GetNextRawFeature() + +{ + OGRErr err = OGRERR_NONE; + + if( GetStatement() == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we are marked to restart then do so, and fetch a record. */ +/* -------------------------------------------------------------------- */ + if( !poStmt->Fetch() ) + { + delete poStmt; + poStmt = NULL; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a feature from the current result. */ +/* -------------------------------------------------------------------- */ + int iField; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + if( pszFIDColumn != NULL && poStmt->GetColId(pszFIDColumn) > -1 ) + poFeature->SetFID( + atoi(poStmt->GetColData(poStmt->GetColId(pszFIDColumn))) ); + else + poFeature->SetFID( iNextShapeId ); + + iNextShapeId++; + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Set the fields. */ +/* -------------------------------------------------------------------- */ + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + int iSrcField = panFieldOrdinals[iField]-1; + const char *pszValue = poStmt->GetColData( iSrcField ); + + if( pszValue == NULL ) + /* no value */; + else if( poFeature->GetFieldDefnRef(iField)->GetType() == OFTBinary ) + poFeature->SetField( iField, + poStmt->GetColDataLength(iSrcField), + (GByte *) pszValue ); + else + poFeature->SetField( iField, pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Try to extract a geometry. */ +/* -------------------------------------------------------------------- */ + if( pszGeomColumn != NULL ) + { + int iField = poStmt->GetColId( pszGeomColumn ); + GByte *pabyShape = (GByte *) poStmt->GetColData( iField ); + int nBytes = poStmt->GetColDataLength(iField); + OGRGeometry *poGeom = NULL; + + if( pabyShape != NULL ) + { + err = OGRCreateFromGeomedia( pabyShape, &poGeom, nBytes ); + if( OGRERR_NONE != err ) + { + CPLDebug( "Geomedia", + "Translation geomedia binary to OGR geometry failed (FID=%ld)", + (long)poFeature->GetFID() ); + } + } + + if( poGeom != NULL && OGRERR_NONE == err ) + { + poGeom->assignSpatialReference( poSRS ); + poFeature->SetGeometryDirectly( poGeom ); + } + } + + return poFeature; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRGeomediaLayer::GetFeature( GIntBig nFeatureId ) + +{ + /* This should be implemented directly! */ + + return OGRLayer::GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGeomediaLayer::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char *OGRGeomediaLayer::GetFIDColumn() + +{ + if( pszFIDColumn != NULL ) + return pszFIDColumn; + else + return ""; +} + +/************************************************************************/ +/* GetGeometryColumn() */ +/************************************************************************/ + +const char *OGRGeomediaLayer::GetGeometryColumn() + +{ + if( pszGeomColumn != NULL ) + return pszGeomColumn; + else + return ""; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomediaselectlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomediaselectlayer.cpp new file mode 100644 index 000000000..cc5a24a32 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomediaselectlayer.cpp @@ -0,0 +1,166 @@ +/****************************************************************************** + * $Id: ogrgeomediaselectlayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGeomediaSelectLayer class, layer access to the results + * of a SELECT statement executed via ExecuteSQL(). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_geomedia.h" + +CPL_CVSID("$Id: ogrgeomediaselectlayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRGeomediaSelectLayer() */ +/************************************************************************/ + +OGRGeomediaSelectLayer::OGRGeomediaSelectLayer( OGRGeomediaDataSource *poDSIn, + CPLODBCStatement * poStmtIn ) + +{ + poDS = poDSIn; + + iNextShapeId = 0; + nSRSId = -1; + poFeatureDefn = NULL; + + poStmt = poStmtIn; + pszBaseStatement = CPLStrdup( poStmtIn->GetCommand() ); + + BuildFeatureDefn( "SELECT", poStmt ); +} + +/************************************************************************/ +/* ~OGRGeomediaSelectLayer() */ +/************************************************************************/ + +OGRGeomediaSelectLayer::~OGRGeomediaSelectLayer() + +{ + ClearStatement(); + CPLFree(pszBaseStatement); +} + +/************************************************************************/ +/* ClearStatement() */ +/************************************************************************/ + +void OGRGeomediaSelectLayer::ClearStatement() + +{ + if( poStmt != NULL ) + { + delete poStmt; + poStmt = NULL; + } +} + +/************************************************************************/ +/* GetStatement() */ +/************************************************************************/ + +CPLODBCStatement *OGRGeomediaSelectLayer::GetStatement() + +{ + if( poStmt == NULL ) + ResetStatement(); + + return poStmt; +} + +/************************************************************************/ +/* ResetStatement() */ +/************************************************************************/ + +OGRErr OGRGeomediaSelectLayer::ResetStatement() + +{ + ClearStatement(); + + iNextShapeId = 0; + + CPLDebug( "ODBC", "Recreating statement." ); + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( pszBaseStatement ); + + if( poStmt->ExecuteSQL() ) + return OGRERR_NONE; + else + { + delete poStmt; + poStmt = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGeomediaSelectLayer::ResetReading() + +{ + if( iNextShapeId != 0 ) + ClearStatement(); + + OGRGeomediaLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRGeomediaSelectLayer::GetFeature( GIntBig nFeatureId ) + +{ + return OGRGeomediaLayer::GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGeomediaSelectLayer::TestCapability( const char * pszCap ) + +{ + return OGRGeomediaLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRGeomediaSelectLayer::GetFeatureCount( int bForce ) + +{ + return OGRGeomediaLayer::GetFeatureCount( bForce ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomediatablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomediatablelayer.cpp new file mode 100644 index 000000000..0cedd2215 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/geomedia/ogrgeomediatablelayer.cpp @@ -0,0 +1,305 @@ +/****************************************************************************** + * $Id: ogrgeomediatablelayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGeomediaTableLayer class, access to an existing table. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_geomedia.h" + +CPL_CVSID("$Id: ogrgeomediatablelayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRGeomediaTableLayer() */ +/************************************************************************/ + +OGRGeomediaTableLayer::OGRGeomediaTableLayer( OGRGeomediaDataSource *poDSIn ) + +{ + poDS = poDSIn; + pszQuery = NULL; + bUpdateAccess = TRUE; + iNextShapeId = 0; + nSRSId = -1; + poFeatureDefn = NULL; +} + +/************************************************************************/ +/* ~OGRGeomediaTableLayer() */ +/************************************************************************/ + +OGRGeomediaTableLayer::~OGRGeomediaTableLayer() + +{ + CPLFree( pszQuery ); + ClearStatement(); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +CPLErr OGRGeomediaTableLayer::Initialize( const char *pszTableName, + const char *pszGeomCol, + OGRSpatialReference* poSRS ) + + +{ + CPLODBCSession *poSession = poDS->GetSession(); + + CPLFree( pszGeomColumn ); + if( pszGeomCol == NULL ) + pszGeomColumn = NULL; + else + pszGeomColumn = CPLStrdup( pszGeomCol ); + + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + + this->poSRS = poSRS; + +/* -------------------------------------------------------------------- */ +/* Do we have a simple primary key? */ +/* -------------------------------------------------------------------- */ + { + CPLODBCStatement oGetKey( poSession ); + + if( oGetKey.GetPrimaryKeys( pszTableName ) && oGetKey.Fetch() ) + { + pszFIDColumn = CPLStrdup(oGetKey.GetColData( 3 )); + + if( oGetKey.Fetch() ) // more than one field in key! + { + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + CPLDebug( "Geomedia", "%s: Compound primary key, ignoring.", + pszTableName ); + } + else + CPLDebug( "Geomedia", + "%s: Got primary key %s.", + pszTableName, pszFIDColumn ); + } + else + CPLDebug( "Geomedia", "%s: no primary key", pszTableName ); + } +/* -------------------------------------------------------------------- */ +/* Get the column definitions for this table. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oGetCol( poSession ); + CPLErr eErr; + + if( !oGetCol.GetColumns( pszTableName ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GetColumns() failed on %s.\n%s", + pszTableName, poSession->GetLastError() ); + return CE_Failure; + } + + eErr = BuildFeatureDefn( pszTableName, &oGetCol ); + if( eErr != CE_None ) + return eErr; + + if( poFeatureDefn->GetFieldCount() == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No column definitions found for table '%s', layer not usable.", + pszTableName ); + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* ClearStatement() */ +/************************************************************************/ + +void OGRGeomediaTableLayer::ClearStatement() + +{ + if( poStmt != NULL ) + { + delete poStmt; + poStmt = NULL; + } +} + +/************************************************************************/ +/* GetStatement() */ +/************************************************************************/ + +CPLODBCStatement *OGRGeomediaTableLayer::GetStatement() + +{ + if( poStmt == NULL ) + ResetStatement(); + + return poStmt; +} + +/************************************************************************/ +/* ResetStatement() */ +/************************************************************************/ + +OGRErr OGRGeomediaTableLayer::ResetStatement() + +{ + ClearStatement(); + + iNextShapeId = 0; + + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( "SELECT * FROM " ); + poStmt->Append( poFeatureDefn->GetName() ); + if( pszQuery != NULL ) + poStmt->Appendf( " WHERE %s", pszQuery ); + + if( poStmt->ExecuteSQL() ) + return OGRERR_NONE; + else + { + delete poStmt; + poStmt = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGeomediaTableLayer::ResetReading() + +{ + ClearStatement(); + OGRGeomediaLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRGeomediaTableLayer::GetFeature( GIntBig nFeatureId ) + +{ + if( pszFIDColumn == NULL ) + return OGRGeomediaLayer::GetFeature( nFeatureId ); + + ClearStatement(); + + iNextShapeId = nFeatureId; + + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( "SELECT * FROM " ); + poStmt->Append( poFeatureDefn->GetName() ); + poStmt->Appendf( " WHERE %s = " CPL_FRMT_GIB, pszFIDColumn, nFeatureId ); + + if( !poStmt->ExecuteSQL() ) + { + delete poStmt; + poStmt = NULL; + return NULL; + } + + return GetNextRawFeature(); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRGeomediaTableLayer::SetAttributeFilter( const char *pszQuery ) + +{ + if( (pszQuery == NULL && this->pszQuery == NULL) + || (pszQuery != NULL && this->pszQuery != NULL + && EQUAL(pszQuery,this->pszQuery)) ) + return OGRERR_NONE; + + CPLFree( this->pszQuery ); + this->pszQuery = pszQuery ? CPLStrdup( pszQuery ) : NULL; + + ClearStatement(); + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGeomediaTableLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return m_poFilterGeom == NULL; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else + return OGRGeomediaLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRGeomediaTableLayer::GetFeatureCount( int bForce ) + +{ + if( m_poFilterGeom != NULL ) + return OGRGeomediaLayer::GetFeatureCount( bForce ); + + CPLODBCStatement oStmt( poDS->GetSession() ); + oStmt.Append( "SELECT COUNT(*) FROM " ); + oStmt.Append( poFeatureDefn->GetName() ); + + if( pszQuery != NULL ) + oStmt.Appendf( " WHERE %s", pszQuery ); + + if( !oStmt.ExecuteSQL() || !oStmt.Fetch() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GetFeatureCount() failed on query %s.\n%s", + oStmt.GetCommand(), poDS->GetSession()->GetLastError() ); + return OGRGeomediaLayer::GetFeatureCount(bForce); + } + + return atoi(oStmt.GetColData(0)); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/GNUmakefile new file mode 100644 index 000000000..9b3c4f9c8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/GNUmakefile @@ -0,0 +1,19 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrgeorssdriver.o ogrgeorssdatasource.o ogrgeorsslayer.o + +ifeq ($(HAVE_EXPAT),yes) +CPPFLAGS += -DHAVE_EXPAT +endif + +CPPFLAGS := -I.. -I../.. $(EXPAT_INCLUDE) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_georss.h + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/drv_georss.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/drv_georss.html new file mode 100644 index 000000000..6d6166908 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/drv_georss.html @@ -0,0 +1,219 @@ + + +OGR GeoRSS driver + + + + +

    GeoRSS : Geographically Encoded Objects for RSS feeds

    + +(Driver available in GDAL 1.7.0 or later)

    + +GeoRSS is a way of encoding location in RSS or Atom feeds.

    + +OGR has support for GeoRSS reading and writing. Read support is only available if GDAL is built with expat library support

    + +The driver supports RSS documents in RSS 2.0 or Atom 1.0 format.

    + +It also supports the 3 ways of encoding location : +GeoRSS simple, GeoRSS GML and W3C Geo (the later being deprecated).

    + +The driver can read and write documents without location information as well.

    + +The default datum for GeoRSS document is the WGS84 datum (EPSG:4326). Although that GeoRSS locations are encoded in latitude-longitude order in the XML file, all coordinates reported or expected by the driver are in longitude-latitude order. The longitude/latitude order used by OGR is meant for compatibily with most of the rest of OGR drivers and utilities. For locations encoded in GML, the driver will support the srsName attribute for describing other SRS.

    + +Simple and GML encoding support the notion of a box as a geometry. This will be decoded as a rectangle (Polygon geometry) in OGR Simple Feature model.

    + +A single layer is returned while reading a RSS document. +Features are retrieved from the content of <item> (RSS document) or <entry> (Atom document) elements.

    + +

    Encoding issues

    + +Expat library supports reading the following built-in encodings : +
      +
    • US-ASCII
    • +
    • UTF-8
    • +
    • UTF-16
    • +
    • ISO-8859-1
    • +
    + +OGR 1.8.0 adds supports for Windows-1252 encoding (for previous versions, altering the encoding +mentionned in the XML header to ISO-8859-1 might work in some cases).

    + +The content returned by OGR will be encoded in UTF-8, after the conversion from the +encoding mentionned in the file header is.

    + +If your GeoRSS file is not encoded in one of the previous encodings, it will not be parsed by the +GeoRSS driver. You may convert it into one of the supported encoding with the iconv utility +for example and change accordingly the encoding parameter value in the XML header.
    +

    + +When writing a GeoRSS file, the driver expects UTF-8 content to be passed in.

    + +

    Field definitions

    + +While reading a GeoRSS document, the driver will first make a full scan of the document to get the field definitions.

    + +The driver will return elements found in the base schema of RSS channel or Atom feeds. It will also return extension elements, +that are allowed in namespaces.

    + +Attributes of first level elements will be exposed as fields.

    + +Complex content (elements inside first level elements) will be returned as an XML blob.

    + +When a same element is repeated, a number will be appended at the end of the attribute name for the repetitions. This is useful for the <category> element in RSS and Atom documents for example.

    + +

    +The following content : +

    +    <item>
    +        <title>My tile</title>
    +        <link>http://www.mylink.org</link>
    +        <description>Cool descriprion !</description>
    +        <pubDate>Wed, 11 Jul 2007 15:39:21 GMT</pubDate>
    +        <guid>http://www.mylink.org/2007/07/11</guid>
    +        <category>Computer Science</category>
    +        <category>Open Source Software</category>
    +        <georss:point>49 2</georss:point>
    +        <myns:name type="my_type">My Name</myns:name>
    +        <myns:complexcontent>
    +            <myns:subelement>Subelement</myns:subelement>
    +        </myns:complexcontent>
    +    </item>
    +
    +will be interpreted in the OGR SF model as : +
    +  title (String) = My title
    +  link (String) = http://www.mylink.org
    +  description (String) = Cool descriprion !
    +  pubDate (DateTime) = 2007/07/11 15:39:21+00
    +  guid (String) = http://www.mylink.org/2007/07/11
    +  category (String) = Computer Science
    +  category2 (String) = Open Source Software
    +  myns_name (String) = My Name
    +  myns_name_type (String) = my_type
    +  myns_complexcontent (String) = <myns:subelement>Subelement</myns:subelement>
    +  POINT (2 49)
    +
    +

    + +

    Creation Issues

    + +On export, all layers are written to a single file. Update of existing +files is not supported.

    +If the output file already exits, the writing will not occur. You have to delete the existing file first.

    + +A layer that is created cannot be immediately read without closing and reopening the file. That is to say that a dataset is read-only or write-only in the same session.

    + +Supported geometries : +

      +
    • Features of type wkbPoint/wkbPoint25D.
    • +
    • Features of type wkbLineString/wkbLineString25D.
    • +
    • Features of type wkbPolygon/wkbPolygon25D.
    • +
    +Other type of geometries are not supported and will be silently ignored. +

    + +The GeoRSS writer supports the following dataset creation options: +

      +
    • FORMAT=RSS|ATOM: whether the document must be in RSS 2.0 or Atom 1.0 format. Default value : RSS

      +

    • GEOM_DIALECT=SIMPLE|GML|W3C_GEO (RSS or ATOM document): the encoding of location information. Default value : SIMPLE
      +W3C_GEO only supports point geometries.
      +SIMPLE or W3C_GEO only support geometries in geographic WGS84 coordinates.

      +

    • USE_EXTENSIONS=YES|NO. Default value : NO. If defined to YES, extension fields (that is to say fields not in the base schema of RSS or Atom documents) will be written. If the field name not found in the base schema matches the foo_bar pattern, foo will be considered as the namespace of the element, and a <foo:bar> element will be written. Otherwise, elements will be written in the <ogr:> namespace.

      +

    • WRITE_HEADER_AND_FOOTER=YES|NO. Default value : YES. If defined to NO, only <entry> or <item> elements will be written. The user will have to provide the appropriate header and footer of the document. Following options are not relevant in that case.

      +

    • HEADER (RSS or Atom document): XML content that will be put between the <channel> element and the first <item> element for a RSS document, or between the xml tag and the first <entry> element for an Atom document. If it is specified, it +will overload the following options.

      +

    • TITLE (RSS or Atom document): value put inside the <title> element in the header. If not provided, a dummy value will be used as that element is compulsory.

      +

    • DESCRIPTION (RSS document): value put inside the <description> element in the header. If not provided, a dummy value will be used as that element is compulsory.

      +

    • LINK (RSS document): value put inside the <link> element in the header. If not provided, a dummy value will be used as that element is compulsory.

      +

    • UPDATED (Atom document): value put inside the <updated> element in the header. Should be formatted as a XML datetime. If not provided, a dummy value will be used as that element is compulsory.

      +

    • AUTHOR_NAME (Atom document): value put inside the <author><name> element in the header. If not provided, a dummy value will be used as that element is compulsory.

      +

    • ID (Atom document): value put inside the <id> element in the header. If not provided, a dummy value will be used as that element is compulsory.

      +

    +

    + +

    +When translating from a source dataset, it may be necessary to rename the field names from the source dataset to the expected RSS or ATOM attribute names, such as <title>, <description>, etc... +This can be done with a OGR VRT dataset, or by using the "-sql" option of the ogr2ogr utility (see RFC21: OGR SQL type cast and field name alias) + + + +

    VSI Virtual File System API support

    + +(Some features below might require OGR >= 1.9.0)

    + +The driver supports reading and writing to files managed by VSI Virtual File System API, which include +"regular" files, as well as files in the /vsizip/ (read-write) , /vsigzip/ (read-write) , /vsicurl/ (read-only) domains.

    + +Writing to /dev/stdout or /vsistdout/ is also supported.

    + +

    Example

    + +
  • The ogrinfo utility can be used to dump the content of a GeoRSS datafile : + +
    +ogrinfo -ro -al input.xml
    +
    +
  • +


    + +

  • The ogr2ogr utility can be used to do GeoRSS to GeoRSS translation. For example, to translate a Atom document into a RSS document + +
    +ogr2ogr -f GeoRSS output.xml input.xml "select link_href as link, title, content as description, author_name as author, id as guid from georss" 
    +
    +
    +Note : in this example we map equivalent fields, from the source name to the expected name of the destination format. +
  • +


    + + +

  • The following Python script shows how to read the content of a online GeoRSS feed +
    +    #!/usr/bin/python
    +    import gdal
    +    import ogr
    +    import urllib2
    +
    +    url = 'http://earthquake.usgs.gov/eqcenter/catalogs/eqs7day-M5.xml'
    +    content = None
    +    try:
    +        handle = urllib2.urlopen(url)
    +        content = handle.read()
    +    except urllib2.HTTPError, e:
    +        print 'HTTP service for %s is down (HTTP Error: %d)' % (url, e.code)
    +    except:
    +        print 'HTTP service for %s is down.' %(url)
    +
    +    # Create in-memory file from the downloaded content
    +    gdal.FileFromMemBuffer('/vsimem/temp', content)
    +
    +    ds = ogr.Open('/vsimem/temp')
    +    lyr = ds.GetLayer(0)
    +    feat = lyr.GetNextFeature()
    +    while feat is not None:
    +        print feat.GetFieldAsString('title') + ' ' + feat.GetGeometryRef().ExportToWkt()
    +        feat.Destroy()
    +        feat = lyr.GetNextFeature()
    +
    +    ds.Destroy()
    +
    +    # Free memory associated with the in-memory file
    +    gdal.Unlink('/vsimem/temp')
    +
    +
  • + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/makefile.vc new file mode 100644 index 000000000..c70aec74d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/makefile.vc @@ -0,0 +1,18 @@ + +OBJ = ogrgeorssdriver.obj ogrgeorssdatasource.obj ogrgeorsslayer.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF EXPAT_DIR +EXTRAFLAGS = -I.. -I..\.. $(EXPAT_INCLUDE) -DHAVE_EXPAT=1 +!ELSE +EXTRAFLAGS = -I.. -I..\.. +!ENDIF + +default: $(OBJ) + +clean: + -del *.obj *.pdb + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/ogr_georss.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/ogr_georss.h new file mode 100644 index 000000000..bb7bb8fbb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/ogr_georss.h @@ -0,0 +1,226 @@ +/****************************************************************************** + * $Id $ + * + * Project: GeoRSS Translator + * Purpose: Definition of classes for OGR GeoRSS driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GEORSS_H_INCLUDED +#define _OGR_GEORSS_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "ogr_p.h" +#include "cpl_hash_set.h" + +#ifdef HAVE_EXPAT +#include "ogr_expat.h" +#endif + +class OGRGeoRSSDataSource; + +typedef enum +{ + GEORSS_ATOM, + GEORSS_RSS, + GEORSS_RSS_RDF, +} OGRGeoRSSFormat; + +typedef enum +{ + GEORSS_GML, + GEORSS_SIMPLE, + GEORSS_W3C_GEO +} OGRGeoRSSGeomDialect; + +/************************************************************************/ +/* OGRGeoRSSLayer */ +/************************************************************************/ + +class OGRGeoRSSLayer : public OGRLayer +{ + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference *poSRS; + OGRGeoRSSDataSource* poDS; + OGRGeoRSSFormat eFormat; + + int bWriteMode; + int nTotalFeatureCount; + + int eof; + int nNextFID; + VSILFILE* fpGeoRSS; /* Large file API */ + int bHasReadSchema; +#ifdef HAVE_EXPAT + XML_Parser oParser; + XML_Parser oSchemaParser; +#endif + OGRGeometry* poGlobalGeom; + int bStopParsing; + int bInFeature; + int hasFoundLat; + int hasFoundLon; +#ifdef HAVE_EXPAT + double latVal; + double lonVal; +#endif + char* pszSubElementName; + char* pszSubElementValue; + int nSubElementValueLen; +#ifdef HAVE_EXPAT + int iCurrentField; +#endif + int bInSimpleGeometry; + int bInGMLGeometry; + int bInGeoLat; + int bInGeoLong; +#ifdef HAVE_EXPAT + int bFoundGeom; + int bSameSRS; +#endif + OGRwkbGeometryType eGeomType; + char* pszGMLSRSName; + int bInTagWithSubTag; + char* pszTagWithSubTag; + int currentDepth; + int featureDepth; + int geometryDepth; +#ifdef HAVE_EXPAT + OGRFieldDefn* currentFieldDefn; + int nWithoutEventCounter; + int nDataHandlerCounter; +#endif + CPLHashSet* setOfFoundFields; + + OGRFeature* poFeature; + OGRFeature ** ppoFeatureTab; + int nFeatureTabLength; + int nFeatureTabIndex; + + private: +#ifdef HAVE_EXPAT + void AddStrToSubElementValue(const char* pszStr); +#endif + int IsStandardField(const char* pszName); + + public: + OGRGeoRSSLayer(const char *pszFilename, + const char* layerName, + OGRGeoRSSDataSource* poDS, + OGRSpatialReference *poSRSIn, + int bWriteMode = FALSE); + ~OGRGeoRSSLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRErr ICreateFeature( OGRFeature *poFeature ); + OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK ); + + OGRFeatureDefn * GetLayerDefn(); + + int TestCapability( const char * ); + + GIntBig GetFeatureCount( int bForce ); + + void LoadSchema(); + +#ifdef HAVE_EXPAT + void startElementCbk(const char *pszName, const char **ppszAttr); + void endElementCbk(const char *pszName); + void dataHandlerCbk(const char *data, int nLen); + + void startElementLoadSchemaCbk(const char *pszName, const char **ppszAttr); + void endElementLoadSchemaCbk(const char *pszName); + void dataHandlerLoadSchemaCbk(const char *data, int nLen); +#endif +}; + +/************************************************************************/ +/* OGRGeoRSSDataSource */ +/************************************************************************/ + +typedef enum +{ + GEORSS_VALIDITY_UNKNOWN, + GEORSS_VALIDITY_INVALID, + GEORSS_VALIDITY_VALID +} OGRGeoRSSValidity; + +class OGRGeoRSSDataSource : public OGRDataSource +{ + char* pszName; + + OGRGeoRSSLayer** papoLayers; + int nLayers; + + /* Export related */ + VSILFILE *fpOutput; /* Virtual file API */ + +#ifdef HAVE_EXPAT + OGRGeoRSSValidity validity; +#endif + OGRGeoRSSFormat eFormat; + OGRGeoRSSGeomDialect eGeomDialect; + int bUseExtensions; + int bWriteHeaderAndFooter; +#ifdef HAVE_EXPAT + XML_Parser oCurrentParser; + int nDataHandlerCounter; +#endif + + public: + OGRGeoRSSDataSource(); + ~OGRGeoRSSDataSource(); + + int Open( const char * pszFilename, + int bUpdate ); + + int Create( const char *pszFilename, + char **papszOptions ); + + const char* GetName() { return pszName; } + + int GetLayerCount() { return nLayers; } + OGRLayer* GetLayer( int ); + + OGRLayer * ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ); + + int TestCapability( const char * ); + + VSILFILE * GetOutputFP() { return fpOutput; } + OGRGeoRSSFormat GetFormat() { return eFormat; } + OGRGeoRSSGeomDialect GetGeomDialect() { return eGeomDialect; } + int GetUseExtensions() { return bUseExtensions; } + +#ifdef HAVE_EXPAT + void startElementValidateCbk(const char *pszName, const char **ppszAttr); + void dataHandlerValidateCbk(const char *data, int nLen); +#endif +}; + +#endif /* ndef _OGR_GeoRSS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/ogrgeorssdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/ogrgeorssdatasource.cpp new file mode 100644 index 000000000..209dde9ba --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/ogrgeorssdatasource.cpp @@ -0,0 +1,506 @@ +/****************************************************************************** + * $Id: ogrgeorssdatasource.cpp 28636 2015-03-06 19:18:43Z rouault $ + * + * Project: GeoRSS Translator + * Purpose: Implements OGRGeoRSSDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_georss.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" + +CPL_CVSID("$Id: ogrgeorssdatasource.cpp 28636 2015-03-06 19:18:43Z rouault $"); + +/************************************************************************/ +/* OGRGeoRSSDataSource() */ +/************************************************************************/ + +OGRGeoRSSDataSource::OGRGeoRSSDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + fpOutput = NULL; + + pszName = NULL; + + eFormat = GEORSS_RSS; + eGeomDialect = GEORSS_SIMPLE; + bUseExtensions = FALSE; + bWriteHeaderAndFooter = TRUE; +} + +/************************************************************************/ +/* ~OGRGeoRSSDataSource() */ +/************************************************************************/ + +OGRGeoRSSDataSource::~OGRGeoRSSDataSource() + +{ + if ( fpOutput != NULL ) + { + if (bWriteHeaderAndFooter) + { + if (eFormat == GEORSS_RSS) + { + VSIFPrintfL(fpOutput, " \n"); + VSIFPrintfL(fpOutput, "\n"); + } + else + { + VSIFPrintfL(fpOutput, "\n"); + } + } + VSIFCloseL( fpOutput); + } + + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGeoRSSDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCDeleteLayer) ) + return FALSE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRGeoRSSDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * OGRGeoRSSDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + CPL_UNUSED OGRwkbGeometryType eType, + CPL_UNUSED char ** papszOptions ) +{ + if (fpOutput == NULL) + return NULL; + + if (poSRS != NULL && eGeomDialect != GEORSS_GML) + { + OGRSpatialReference oSRS; + oSRS.SetWellKnownGeogCS("WGS84"); + if (poSRS->IsSame(&oSRS) == FALSE) + { + CPLError(CE_Failure, CPLE_NotSupported, + "For a non GML dialect, only WGS84 SRS is supported"); + return NULL; + } + } + + nLayers++; + papoLayers = (OGRGeoRSSLayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRGeoRSSLayer*)); + papoLayers[nLayers-1] = new OGRGeoRSSLayer( pszName, pszLayerName, this, poSRS, TRUE ); + + return papoLayers[nLayers-1]; +} + +#ifdef HAVE_EXPAT +/************************************************************************/ +/* startElementValidateCbk() */ +/************************************************************************/ + +void OGRGeoRSSDataSource::startElementValidateCbk(const char *pszName, const char **ppszAttr) +{ + if (validity == GEORSS_VALIDITY_UNKNOWN) + { + if (strcmp(pszName, "rss") == 0) + { + validity = GEORSS_VALIDITY_VALID; + eFormat = GEORSS_RSS; + } + else if (strcmp(pszName, "feed") == 0 || + strcmp(pszName, "atom:feed") == 0) + { + validity = GEORSS_VALIDITY_VALID; + eFormat = GEORSS_ATOM; + } + else if (strcmp(pszName, "rdf:RDF") == 0) + { + const char** ppszIter = ppszAttr; + while(*ppszIter) + { + if (strcmp(*ppszIter, "xmlns:georss") == 0) + { + validity = GEORSS_VALIDITY_VALID; + eFormat = GEORSS_RSS_RDF; + } + ppszIter += 2; + } + } + else + { + validity = GEORSS_VALIDITY_INVALID; + } + } +} + + +/************************************************************************/ +/* dataHandlerValidateCbk() */ +/************************************************************************/ + +void OGRGeoRSSDataSource::dataHandlerValidateCbk(CPL_UNUSED const char *data, + CPL_UNUSED int nLen) +{ + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)"); + XML_StopParser(oCurrentParser, XML_FALSE); + } +} + + +static void XMLCALL startElementValidateCbk(void *pUserData, const char *pszName, const char **ppszAttr) +{ + OGRGeoRSSDataSource* poDS = (OGRGeoRSSDataSource*) pUserData; + poDS->startElementValidateCbk(pszName, ppszAttr); +} + +static void XMLCALL dataHandlerValidateCbk(void *pUserData, const char *data, int nLen) +{ + OGRGeoRSSDataSource* poDS = (OGRGeoRSSDataSource*) pUserData; + poDS->dataHandlerValidateCbk(data, nLen); +} +#endif + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRGeoRSSDataSource::Open( const char * pszFilename, int bUpdateIn) + +{ + if (bUpdateIn) + { + CPLError(CE_Failure, CPLE_NotSupported, + "OGR/GeoRSS driver does not support opening a file in update mode"); + return FALSE; + } +#ifdef HAVE_EXPAT + pszName = CPLStrdup( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Try to open the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE* fp = VSIFOpenL(pszFilename, "r"); + if (fp == NULL) + return FALSE; + + validity = GEORSS_VALIDITY_UNKNOWN; + + XML_Parser oParser = OGRCreateExpatXMLParser(); + XML_SetUserData(oParser, this); + XML_SetElementHandler(oParser, ::startElementValidateCbk, NULL); + XML_SetCharacterDataHandler(oParser, ::dataHandlerValidateCbk); + oCurrentParser = oParser; + + char aBuf[BUFSIZ]; + int nDone; + unsigned int nLen; + int nCount = 0; + + /* Begin to parse the file and look for the or element */ + /* It *MUST* be the first element of an XML file */ + /* So once we have read the first element, we know if we can */ + /* handle the file or not with that driver */ + do + { + nDataHandlerCounter = 0; + nLen = (unsigned int) VSIFReadL( aBuf, 1, sizeof(aBuf), fp ); + nDone = VSIFEofL(fp); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + if (nLen <= BUFSIZ-1) + aBuf[nLen] = 0; + else + aBuf[BUFSIZ-1] = 0; + if (strstr(aBuf, " 0 ); + + XML_ParserFree(oParser); + + VSIFCloseL(fp); + + if (validity == GEORSS_VALIDITY_VALID) + { + CPLDebug("GeoRSS", "%s seems to be a GeoRSS file.", pszFilename); + + nLayers = 1; + papoLayers = (OGRGeoRSSLayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRGeoRSSLayer*)); + papoLayers[0] = new OGRGeoRSSLayer( pszName, "georss", this, NULL, FALSE ); + } + + return (validity == GEORSS_VALIDITY_VALID); +#else + char aBuf[256]; + VSILFILE* fp = VSIFOpenL(pszFilename, "r"); + if (fp) + { + unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, 255, fp ); + aBuf[nLen] = 0; + if (strstr(aBuf, "\n"); + if (eFormat == GEORSS_RSS) + { + VSIFPrintfL(fpOutput, "\n"); + VSIFPrintfL(fpOutput, " \n"); + if (pszHeader) + { + VSIFPrintfL(fpOutput, "%s", pszHeader); + } + else + { + VSIFPrintfL(fpOutput, " %s\n", pszTitle); + VSIFPrintfL(fpOutput, " %s\n", pszDescription); + VSIFPrintfL(fpOutput, " %s\n", pszLink); + } + } + else + { + VSIFPrintfL(fpOutput, "\n"); + if (pszHeader) + { + VSIFPrintfL(fpOutput, "%s", pszHeader); + } + else + { + VSIFPrintfL(fpOutput, " %s\n", pszTitle); + VSIFPrintfL(fpOutput, " %s\n", pszUpdated); + VSIFPrintfL(fpOutput, " %s\n", pszAuthorName); + VSIFPrintfL(fpOutput, " %s\n", pszId); + } + } + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/ogrgeorssdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/ogrgeorssdriver.cpp new file mode 100644 index 000000000..a661123c6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/ogrgeorssdriver.cpp @@ -0,0 +1,149 @@ +/****************************************************************************** + * $Id: ogrgeorssdriver.cpp 28636 2015-03-06 19:18:43Z rouault $ + * + * Project: GeoRSS Translator + * Purpose: Implements OGRGeoRSSDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_georss.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrgeorssdriver.cpp 28636 2015-03-06 19:18:43Z rouault $"); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRGeoRSSDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->eAccess == GA_Update || poOpenInfo->fpL == NULL ) + return NULL; + + if( strstr((const char*)poOpenInfo->pabyHeader, "pabyHeader, "pabyHeader, "Open( poOpenInfo->pszFilename, poOpenInfo->eAccess == GA_Update ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRGeoRSSDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + OGRGeoRSSDataSource *poDS = new OGRGeoRSSDataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +static CPLErr OGRGeoRSSDriverDelete( const char *pszFilename ) + +{ + if( VSIUnlink( pszFilename ) == 0 ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* RegisterOGRGeoRSS() */ +/************************************************************************/ + +void RegisterOGRGeoRSS() + +{ + if (! GDAL_CHECK_VERSION("OGR/GeoRSS driver")) + return; + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GeoRSS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GeoRSS" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GeoRSS" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_georss.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" " +" "); + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, ""); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRGeoRSSDriverOpen; + poDriver->pfnCreate = OGRGeoRSSDriverCreate; + poDriver->pfnDelete = OGRGeoRSSDriverDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/ogrgeorsslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/ogrgeorsslayer.cpp new file mode 100644 index 000000000..66818c40a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/georss/ogrgeorsslayer.cpp @@ -0,0 +1,2232 @@ +/****************************************************************************** + * $Id: ogrgeorsslayer.cpp 28900 2015-04-14 09:40:34Z rouault $ + * + * Project: GeoRSS Translator + * Purpose: Implements OGRGeoRSSLayer class. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_georss.h" +#include "cpl_conv.h" +#include "cpl_minixml.h" +#include "ogr_api.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrgeorsslayer.cpp 28900 2015-04-14 09:40:34Z rouault $"); + +static const char* apszAllowedATOMFieldNamesWithSubElements[] = { "author", "contributor", NULL }; + +static +const char* apszAllowedRSSFieldNames[] = { "title", "link", "description", "author", + "category", "category_domain", + "comments", + "enclosure_url", "enclosure_length", "enclosure_type", + "guid", "guid_isPermaLink", + "pubDate", + "source", "source_url", NULL}; + +static +const char* apszAllowedATOMFieldNames[] = { "category_term", "category_scheme", "category_label", + "content", "content_type", "content_xml_lang", "content_xml_base", + "summary", "summary_type", "summary_xml_lang", "summary_xml_base", + "author_name", "author_uri", "author_email", + "contributor_name", "contributor_uri", "contributor_email", + "link_href", "link_rel", "link_type", "link_length", + "id", "published", "rights", "source", + "title", "updated", NULL }; + +#define IS_LAT_ELEMENT(pszName) (strncmp(pszName, "geo:lat", strlen("geo:lat")) == 0 || \ + strncmp(pszName, "icbm:lat", strlen("icbm:lat")) == 0 || \ + strncmp(pszName, "geourl:lat", strlen("geourl:lat")) == 0) + +#define IS_LON_ELEMENT(pszName) (strncmp(pszName, "geo:lon", strlen("geo:lon")) == 0 || \ + strncmp(pszName, "icbm:lon", strlen("icbm:lon")) == 0 || \ + strncmp(pszName, "geourl:lon", strlen("geourl:lon")) == 0) + +#define IS_GEO_ELEMENT(pszName) (strcmp(pszName, "georss:point") == 0 || \ + strcmp(pszName, "georss:line") == 0 || \ + strcmp(pszName, "georss:box") == 0 || \ + strcmp(pszName, "georss:polygon") == 0 || \ + strcmp(pszName, "georss:where") == 0 || \ + strncmp(pszName, "gml:", strlen("gml:")) == 0 || \ + strncmp(pszName, "geo:", strlen("geo:")) == 0 || \ + strncmp(pszName, "icbm:", strlen("icbm:")) == 0 || \ + strncmp(pszName, "geourl:", strlen("geourl:")) == 0) + +/************************************************************************/ +/* OGRGeoRSSLayer() */ +/************************************************************************/ + +OGRGeoRSSLayer::OGRGeoRSSLayer( const char* pszFilename, + const char* pszLayerName, + OGRGeoRSSDataSource* poDS, + OGRSpatialReference *poSRSIn, + int bWriteMode) + +{ + eof = FALSE; + nNextFID = 0; + + this->poDS = poDS; + this->bWriteMode = bWriteMode; + + eFormat = poDS->GetFormat(); + + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + poSRS = poSRSIn; + if (poSRS) + { + poSRS->Reference(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + } + + nTotalFeatureCount = 0; + + ppoFeatureTab = NULL; + nFeatureTabIndex = 0; + nFeatureTabLength = 0; + pszSubElementName = NULL; + pszSubElementValue = NULL; + nSubElementValueLen = 0; + pszGMLSRSName = NULL; + pszTagWithSubTag = NULL; + bStopParsing = FALSE; + bHasReadSchema = FALSE; + setOfFoundFields = NULL; + poGlobalGeom = NULL; + hasFoundLat = FALSE; + hasFoundLon = FALSE; + + poFeature = NULL; + +#ifdef HAVE_EXPAT + oParser = NULL; +#endif + + if (bWriteMode == FALSE) + { + fpGeoRSS = VSIFOpenL( pszFilename, "r" ); + if( fpGeoRSS == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename); + return; + } + } + else + fpGeoRSS = NULL; + + ResetReading(); +} + +/************************************************************************/ +/* ~OGRGeoRSSLayer() */ +/************************************************************************/ + +OGRGeoRSSLayer::~OGRGeoRSSLayer() + +{ +#ifdef HAVE_EXPAT + if (oParser) + XML_ParserFree(oParser); +#endif + poFeatureDefn->Release(); + + if( poSRS != NULL ) + poSRS->Release(); + + CPLFree(pszSubElementName); + CPLFree(pszSubElementValue); + CPLFree(pszGMLSRSName); + CPLFree(pszTagWithSubTag); + if (setOfFoundFields) + CPLHashSetDestroy(setOfFoundFields); + if (poGlobalGeom) + delete poGlobalGeom; + + int i; + for(i=nFeatureTabIndex;istartElementCbk(pszName, ppszAttr); +} + +static void XMLCALL endElementCbk(void *pUserData, const char *pszName) +{ + ((OGRGeoRSSLayer*)pUserData)->endElementCbk(pszName); +} + +static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen) +{ + ((OGRGeoRSSLayer*)pUserData)->dataHandlerCbk(data, nLen); +} + +#endif + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGeoRSSLayer::ResetReading() + +{ + if (bWriteMode) + return; + + eof = FALSE; + nNextFID = 0; + if (fpGeoRSS) + { + VSIFSeekL( fpGeoRSS, 0, SEEK_SET ); +#ifdef HAVE_EXPAT + if (oParser) + XML_ParserFree(oParser); + + oParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk); + XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk); + XML_SetUserData(oParser, this); +#endif + } + bInFeature = FALSE; + hasFoundLat = FALSE; + hasFoundLon = FALSE; + bInSimpleGeometry = FALSE; + bInGMLGeometry = FALSE; + bInGeoLat = FALSE; + bInGeoLong = FALSE; + eGeomType = wkbUnknown; + CPLFree(pszSubElementName); + pszSubElementName = NULL; + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + CPLFree(pszGMLSRSName); + pszGMLSRSName = NULL; + + if (setOfFoundFields) + CPLHashSetDestroy(setOfFoundFields); + setOfFoundFields = NULL; + + int i; + for(i=nFeatureTabIndex;iSetFID( nNextFID++ ); + + bInFeature = TRUE; + hasFoundLat = FALSE; + hasFoundLon = FALSE; + bInSimpleGeometry = FALSE; + bInGMLGeometry = FALSE; + bInGeoLat = FALSE; + bInGeoLong = FALSE; + eGeomType = wkbUnknown; + geometryDepth = 0; + bInTagWithSubTag = FALSE; + + if (setOfFoundFields) + CPLHashSetDestroy(setOfFoundFields); + setOfFoundFields = CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree); + } + else if (bInFeature && bInTagWithSubTag && currentDepth == 3) + { + char* pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszNoNSName)); + + CPLFree(pszSubElementName); + pszSubElementName = NULL; + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + + iCurrentField = poFeatureDefn->GetFieldIndex(pszFieldName); + if (iCurrentField >= 0) + pszSubElementName = CPLStrdup(pszFieldName); + + CPLFree(pszFieldName); + } + else if (bInFeature && eFormat == GEORSS_ATOM && + currentDepth == 2 && OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName)) + { + CPLFree(pszTagWithSubTag); + pszTagWithSubTag = CPLStrdup(pszNoNSName); + + int count = 1; + while(CPLHashSetLookup(setOfFoundFields, pszTagWithSubTag) != NULL) + { + count ++; + CPLFree(pszTagWithSubTag); + pszTagWithSubTag = CPLStrdup(CPLSPrintf("%s%d", pszNoNSName, count)); + } + CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszTagWithSubTag)); + + bInTagWithSubTag = TRUE; + } + else if (bInGMLGeometry) + { + bSerializeTag = TRUE; + } + else if (bInSimpleGeometry || bInGeoLat || bInGeoLong) + { + /* Shouldn't happen for a valid document */ + } + else if (IS_LAT_ELEMENT(pszName)) + { + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + bInGeoLat = TRUE; + } + else if (IS_LON_ELEMENT(pszName)) + { + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + bInGeoLong = TRUE; + } + else if (strcmp(pszName, "georss:point") == 0 || + strcmp(pszName, "georss:line") == 0 || + strcmp(pszName, "geo:line") == 0 || + strcmp(pszName, "georss:polygon") == 0 || + strcmp(pszName, "georss:box") == 0) + { + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + eGeomType = strcmp(pszName, "georss:point") == 0 ? wkbPoint : + (strcmp(pszName, "georss:line") == 0 || + strcmp(pszName, "geo:line") == 0) ? wkbLineString : + (strcmp(pszName, "georss:polygon") == 0 || + strcmp(pszName, "georss:box") == 0) ? wkbPolygon : + wkbUnknown; + bInSimpleGeometry = TRUE; + geometryDepth = currentDepth; + } + else if (strcmp(pszName, "gml:Point") == 0 || + strcmp(pszName, "gml:LineString") == 0 || + strcmp(pszName, "gml:Polygon") == 0 || + strcmp(pszName, "gml:MultiPoint") == 0 || + strcmp(pszName, "gml:MultiLineString") == 0 || + strcmp(pszName, "gml:MultiPolygon") == 0 || + strcmp(pszName, "gml:Envelope") == 0) + { + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + AddStrToSubElementValue(CPLSPrintf("<%s>", pszName)); + bInGMLGeometry = TRUE; + geometryDepth = currentDepth; + CPLFree(pszGMLSRSName); + pszGMLSRSName = NULL; + for (int i = 0; ppszAttr[i]; i += 2) + { + if (strcmp(ppszAttr[i], "srsName") == 0) + { + if (pszGMLSRSName == NULL) + pszGMLSRSName = CPLStrdup(ppszAttr[i+1]); + } + } + } + else if (bInFeature && currentDepth == featureDepth + 1) + { + CPLFree(pszSubElementName); + pszSubElementName = NULL; + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + iCurrentField = -1; + + if( pszName != pszNoNSName && strncmp(pszName, "atom:", 5) == 0 ) + pszName = pszNoNSName; + + pszSubElementName = CPLStrdup(pszName); + int count = 1; + while(CPLHashSetLookup(setOfFoundFields, pszSubElementName) != NULL) + { + count ++; + CPLFree(pszSubElementName); + pszSubElementName = CPLStrdup(CPLSPrintf("%s%d", pszName, count)); + } + CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszSubElementName)); + + char* pszCompatibleName = OGRGeoRSS_GetOGRCompatibleTagName(pszSubElementName); + iCurrentField = poFeatureDefn->GetFieldIndex(pszCompatibleName); + CPLFree(pszSubElementName); + + for(int i = 0; ppszAttr[i] != NULL && ppszAttr[i+1] != NULL; i+=2) + { + char* pszAttrCompatibleName = + OGRGeoRSS_GetOGRCompatibleTagName(CPLSPrintf("%s_%s", pszCompatibleName, ppszAttr[i])); + int iAttrField = poFeatureDefn->GetFieldIndex(pszAttrCompatibleName); + if (iAttrField >= 0) + { + if (poFeatureDefn->GetFieldDefn(iAttrField)->GetType() == OFTReal) + poFeature->SetField( iAttrField, CPLAtof(ppszAttr[i+1]) ); + else + poFeature->SetField( iAttrField, ppszAttr[i+1] ); + } + CPLFree(pszAttrCompatibleName); + } + + if (iCurrentField < 0) + { + pszSubElementName = NULL; + } + else + { + pszSubElementName = CPLStrdup(pszCompatibleName); + } + CPLFree(pszCompatibleName); + } + else if (bInFeature && currentDepth > featureDepth + 1 && pszSubElementName != NULL) + { + bSerializeTag = TRUE; + } + + if (bSerializeTag) + { + AddStrToSubElementValue("<"); + AddStrToSubElementValue(pszName); + for(int i = 0; ppszAttr[i] != NULL && ppszAttr[i+1] != NULL; i+=2) + { + AddStrToSubElementValue(" "); + AddStrToSubElementValue(ppszAttr[i]); + AddStrToSubElementValue("=\""); + AddStrToSubElementValue(ppszAttr[i+1]); + AddStrToSubElementValue("\""); + } + AddStrToSubElementValue(">"); + } + + currentDepth++; +} + +/************************************************************************/ +/* OGRGeoRSSLayerTrimLeadingAndTrailingSpaces() */ +/************************************************************************/ + +static void OGRGeoRSSLayerTrimLeadingAndTrailingSpaces(char* pszStr) +{ + int i; + + /* Trim leading spaces, tabs and newlines */ + i = 0; + while(pszStr[i] != '\0' && + (pszStr[i] == ' ' || pszStr[i] == '\t' || pszStr[i] == '\n')) + i ++; + memmove(pszStr, pszStr + i, strlen(pszStr + i) + 1); + + /* Trim trailing spaces, tabs and newlines */ + i = strlen(pszStr) - 1; + while(i >= 0 && + (pszStr[i] == ' ' || pszStr[i] == '\t' || pszStr[i] == '\n')) + { + pszStr[i] = '\0'; + i --; + } +} + +/************************************************************************/ +/* endElementCbk() */ +/************************************************************************/ + +void OGRGeoRSSLayer::endElementCbk(const char *pszName) +{ + OGRGeometry* poGeom = NULL; + + if (bStopParsing) return; + + currentDepth--; + const char* pszNoNSName = pszName; + const char* pszColon = strchr(pszNoNSName, ':'); + if( pszColon ) + pszNoNSName = pszColon + 1; + + if ((eFormat == GEORSS_ATOM && currentDepth == 1 && strcmp(pszNoNSName, "entry") == 0) || + ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && + (currentDepth == 1 || currentDepth == 2) && strcmp(pszNoNSName, "item") == 0)) + { + bInFeature = FALSE; + bInTagWithSubTag = FALSE; + + if (hasFoundLat && hasFoundLon) + poFeature->SetGeometryDirectly( new OGRPoint( lonVal, latVal ) ); + else if (poFeature->GetGeometryRef() == NULL && poGlobalGeom != NULL) + poFeature->SetGeometry(poGlobalGeom); + + hasFoundLat = FALSE; + hasFoundLon = FALSE; + + if (poSRS != NULL && poFeature->GetGeometryRef() != NULL) + poFeature->GetGeometryRef()->assignSpatialReference(poSRS); + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + ppoFeatureTab = (OGRFeature**) + CPLRealloc(ppoFeatureTab, + sizeof(OGRFeature*) * (nFeatureTabLength + 1)); + ppoFeatureTab[nFeatureTabLength] = poFeature; + nFeatureTabLength++; + } + else + { + delete poFeature; + } + poFeature = NULL; + return; + } + + if (bInTagWithSubTag && currentDepth == 3) + { + char* pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszNoNSName)); + + if (iCurrentField != -1 && pszSubElementName && + strcmp(pszFieldName, pszSubElementName) == 0 && poFeature && + pszSubElementValue && nSubElementValueLen) + { + pszSubElementValue[nSubElementValueLen] = 0; + if (poFeatureDefn->GetFieldDefn(iCurrentField)->GetType() == OFTReal) + poFeature->SetField( iCurrentField, CPLAtof(pszSubElementValue) ); + else + poFeature->SetField( iCurrentField, pszSubElementValue); + } + + CPLFree(pszSubElementName); + pszSubElementName = NULL; + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + + CPLFree(pszFieldName); + } + else if (bInFeature && eFormat == GEORSS_ATOM && + currentDepth == 2 && OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName)) + { + bInTagWithSubTag = FALSE; + } + else if (bInGMLGeometry) + { + AddStrToSubElementValue(""); + if (currentDepth > geometryDepth) + { + } + else + { + pszSubElementValue[nSubElementValueLen] = 0; + CPLAssert(strncmp(pszName, "gml:", 4) == 0); + poGeom = (OGRGeometry*) OGR_G_CreateFromGML(pszSubElementValue); + + if (poGeom != NULL && !poGeom->IsEmpty() ) + { + int bSwapCoordinates = FALSE; + if (pszGMLSRSName) + { + OGRSpatialReference* poSRSFeature = new OGRSpatialReference(); + poSRSFeature->importFromURN(pszGMLSRSName); + poGeom->assignSpatialReference(poSRSFeature); + poSRSFeature->Release(); + } + else + bSwapCoordinates = TRUE; /* lat, lon WGS 84 */ + + if (bSwapCoordinates) + { + poGeom->swapXY(); + } + } + bInGMLGeometry = FALSE; + } + } + else if (bInSimpleGeometry) + { + if (currentDepth > geometryDepth) + { + /* Shouldn't happen for a valid document */ + } + else + { + if (pszSubElementValue) + { + pszSubElementValue[nSubElementValueLen] = 0; + + /* Trim any leading and trailing spaces, tabs, newlines, etc... */ + OGRGeoRSSLayerTrimLeadingAndTrailingSpaces(pszSubElementValue); + + /* Caution : Order is latitude, longitude */ + char** papszTokens = + CSLTokenizeStringComplex( pszSubElementValue, + " ,", TRUE, FALSE ); + + int nTokens = CSLCount(papszTokens); + if ((nTokens % 2) != 0 || + (eGeomType == wkbPoint && nTokens != 2) || + (eGeomType == wkbLineString && nTokens < 4) || + (strcmp(pszName, "georss:polygon") == 0 && nTokens < 6) || + (strcmp(pszName, "georss:box") == 0 && nTokens != 4)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Wrong number of coordinates in %s", + pszSubElementValue); + } + else if (eGeomType == wkbPoint) + { + poGeom = new OGRPoint( CPLAtof(papszTokens[1]), + CPLAtof(papszTokens[0]) ); + } + else if (eGeomType == wkbLineString) + { + OGRLineString* poLineString = new OGRLineString (); + poGeom = poLineString; + int i; + for(i=0;iaddPoint( CPLAtof(papszTokens[i+1]), + CPLAtof(papszTokens[i]) ); + } + } + else if (eGeomType == wkbPolygon) + { + OGRPolygon* poPolygon = new OGRPolygon(); + OGRLinearRing* poLinearRing = new OGRLinearRing(); + poGeom = poPolygon; + poPolygon->addRingDirectly(poLinearRing); + if (strcmp(pszName, "georss:polygon") == 0) + { + int i; + for(i=0;iaddPoint( CPLAtof(papszTokens[i+1]), + CPLAtof(papszTokens[i]) ); + } + } + else + { + double lat1 = CPLAtof(papszTokens[0]); + double lon1 = CPLAtof(papszTokens[1]); + double lat2 = CPLAtof(papszTokens[2]); + double lon2 = CPLAtof(papszTokens[3]); + poLinearRing->addPoint( lon1, lat1 ); + poLinearRing->addPoint( lon1, lat2 ); + poLinearRing->addPoint( lon2, lat2 ); + poLinearRing->addPoint( lon2, lat1 ); + poLinearRing->addPoint( lon1, lat1 ); + } + } + + CSLDestroy(papszTokens); + } + bInSimpleGeometry = FALSE; + } + } + else if (IS_LAT_ELEMENT(pszName)) + { + if (pszSubElementValue) + { + hasFoundLat = TRUE; + pszSubElementValue[nSubElementValueLen] = 0; + latVal = CPLAtof(pszSubElementValue); + } + bInGeoLat = FALSE; + } + else if (IS_LON_ELEMENT(pszName)) + { + if (pszSubElementValue) + { + hasFoundLon = TRUE; + pszSubElementValue[nSubElementValueLen] = 0; + lonVal = CPLAtof(pszSubElementValue); + } + bInGeoLong = FALSE; + } + else if (bInFeature && currentDepth == featureDepth + 1) + { + if (iCurrentField != -1 && pszSubElementName && + poFeature && pszSubElementValue && nSubElementValueLen) + { + pszSubElementValue[nSubElementValueLen] = 0; + if (poFeatureDefn->GetFieldDefn(iCurrentField)->GetType() == OFTDateTime) + { + OGRField sField; + if (OGRParseRFC822DateTime(pszSubElementValue, &sField)) + { + poFeature->SetField(iCurrentField, &sField); + } + else if (OGRParseXMLDateTime(pszSubElementValue, &sField)) + { + poFeature->SetField(iCurrentField, &sField); + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Could not parse %s as a valid dateTime", pszSubElementValue); + } + } + else + { + if (poFeatureDefn->GetFieldDefn(iCurrentField)->GetType() == OFTReal) + poFeature->SetField( iCurrentField, CPLAtof(pszSubElementValue) ); + else + poFeature->SetField( iCurrentField, pszSubElementValue); + } + } + + CPLFree(pszSubElementName); + pszSubElementName = NULL; + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + } + else if (bInFeature && currentDepth > featureDepth + 1 && pszSubElementName != NULL) + { + AddStrToSubElementValue(""); + } + + if (poGeom != NULL) + { + if (poFeature != NULL) + { + poFeature->SetGeometryDirectly(poGeom); + } + else if (!bInFeature) + { + if (poGlobalGeom != NULL) + delete poGlobalGeom; + poGlobalGeom = poGeom; + } + else + delete poGeom; + } + else if (!bInFeature && hasFoundLat && hasFoundLon) + { + if (poGlobalGeom != NULL) + delete poGlobalGeom; + poGlobalGeom = new OGRPoint( lonVal, latVal ); + hasFoundLat = hasFoundLon = FALSE; + } +} + +/************************************************************************/ +/* dataHandlerCbk() */ +/************************************************************************/ + +void OGRGeoRSSLayer::dataHandlerCbk(const char *data, int nLen) +{ + if (bStopParsing) return; + + if (bInGMLGeometry == TRUE || bInSimpleGeometry == TRUE || + bInGeoLat == TRUE || bInGeoLong == TRUE || + pszSubElementName != NULL) + { + char* pszNewSubElementValue = (char*) VSIRealloc(pszSubElementValue, + nSubElementValueLen + nLen + 1); + if (pszNewSubElementValue == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + pszSubElementValue = pszNewSubElementValue; + memcpy(pszSubElementValue + nSubElementValueLen, data, nLen); + nSubElementValueLen += nLen; + } +} +#endif + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRGeoRSSLayer::GetNextFeature() +{ + if (bWriteMode) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot read features when writing a GeoRSS file"); + return NULL; + } + + if (fpGeoRSS == NULL) + return NULL; + + if (!bHasReadSchema) + LoadSchema(); + + if (bStopParsing) + return NULL; + +#ifdef HAVE_EXPAT + if (nFeatureTabIndex < nFeatureTabLength) + { + return ppoFeatureTab[nFeatureTabIndex++]; + } + + if (VSIFEofL(fpGeoRSS)) + return NULL; + + char aBuf[BUFSIZ]; + + CPLFree(ppoFeatureTab); + ppoFeatureTab = NULL; + nFeatureTabLength = 0; + nFeatureTabIndex = 0; + + int nDone; + do + { + unsigned int nLen = + (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpGeoRSS ); + nDone = VSIFEofL(fpGeoRSS); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of GeoRSS file failed : %s " + "at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + bStopParsing = TRUE; + } + } while (!nDone && !bStopParsing && nFeatureTabLength == 0); + + return (nFeatureTabLength) ? ppoFeatureTab[nFeatureTabIndex++] : NULL; +#else + return NULL; +#endif +} + +/************************************************************************/ +/* OGRGeoRSSLayerIsStandardFieldInternal() */ +/************************************************************************/ + +static int OGRGeoRSSLayerIsStandardFieldInternal(const char* pszName, + const char** papszNames) +{ + unsigned int i; + for( i = 0; papszNames[i] != NULL; i++) + { + if (strcmp(pszName, papszNames[i]) == 0) + { + return TRUE; + } + + const char* pszUnderscore = strchr(papszNames[i], '_'); + if (pszUnderscore == NULL) + { + int nLen = strlen(papszNames[i]); + if (strncmp(pszName, papszNames[i], nLen) == 0) + { + int k = nLen; + while(pszName[k] >= '0' && pszName[k] <= '9') + k++; + if (pszName[k] == '\0') + return TRUE; + } + } + else + { + int nLen = pszUnderscore - papszNames[i]; + if (strncmp(pszName, papszNames[i], nLen) == 0) + { + int k = nLen; + while(pszName[k] >= '0' && pszName[k] <= '9') + k++; + if (pszName[k] == '_' && strcmp(pszName + k, pszUnderscore) == 0) + return TRUE; + } + } + } + return FALSE; +} + +/************************************************************************/ +/* OGRGeoRSSLayer::IsStandardField() */ +/************************************************************************/ + +int OGRGeoRSSLayer::IsStandardField(const char* pszName) +{ + if (eFormat == GEORSS_RSS) + { + return OGRGeoRSSLayerIsStandardFieldInternal(pszName, + apszAllowedRSSFieldNames); + } + else + { + return OGRGeoRSSLayerIsStandardFieldInternal(pszName, + apszAllowedATOMFieldNames); + } +} + +/************************************************************************/ +/* OGRGeoRSSLayerSplitComposedField() */ +/************************************************************************/ + +static void OGRGeoRSSLayerSplitComposedField(const char* pszName, + char** ppszElementName, + char** ppszNumber, + char** ppszAttributeName) +{ + *ppszElementName = CPLStrdup(pszName); + + int i = 0; + while(pszName[i] != '\0' && pszName[i] != '_' && + !(pszName[i] >= '0' && pszName[i] <= '9')) + { + i++; + } + + (*ppszElementName)[i] = '\0'; + + if (pszName[i] >= '0' && pszName[i] <= '9') + { + *ppszNumber = CPLStrdup(pszName + i); + char* pszUnderscore = strchr(*ppszNumber, '_'); + if (pszUnderscore) + { + *pszUnderscore = '\0'; + *ppszAttributeName = CPLStrdup(pszUnderscore + 1); + } + else + { + *ppszAttributeName = NULL; + } + } + else + { + *ppszNumber = CPLStrdup(""); + if (pszName[i] == '_') + { + *ppszAttributeName = CPLStrdup(pszName + i + 1); + } + else + { + *ppszAttributeName = NULL; + } + } +} + +/************************************************************************/ +/* OGRGeoRSSLayerWriteSimpleElement() */ +/************************************************************************/ + +static void OGRGeoRSSLayerWriteSimpleElement(VSILFILE* fp, + const char* pszElementName, + const char* pszNumber, + const char** papszNames, + OGRFeatureDefn* poFeatureDefn, + OGRFeature* poFeature) +{ + VSIFPrintfL(fp, " <%s", pszElementName); + + unsigned k; + for( k = 0; papszNames[k] != NULL ; k++) + { + if (strncmp(papszNames[k], pszElementName, strlen(pszElementName)) == 0 && + papszNames[k][strlen(pszElementName)] == '_') + { + const char* pszAttributeName = papszNames[k] + strlen(pszElementName) + 1; + char* pszFieldName = CPLStrdup(CPLSPrintf("%s%s_%s", pszElementName, pszNumber, pszAttributeName)); + int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName); + if (iIndex != -1 && poFeature->IsFieldSet( iIndex )) + { + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( iIndex )); + VSIFPrintfL(fp, " %s=\"%s\"", pszAttributeName, pszValue); + CPLFree(pszValue); + } + CPLFree(pszFieldName); + } + } + + char* pszFieldName = CPLStrdup(CPLSPrintf("%s%s", pszElementName, pszNumber)); + int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName); + if (iIndex != -1 && poFeature->IsFieldSet( iIndex )) + { + VSIFPrintfL(fp, ">"); + + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( iIndex )); + VSIFPrintfL(fp, "%s", pszValue); + CPLFree(pszValue); + + VSIFPrintfL(fp, "\n", pszElementName); + } + else + { + VSIFPrintfL(fp, "/>\n"); + } + CPLFree(pszFieldName); +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRGeoRSSLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + VSILFILE* fp = poDS->GetOutputFP(); + if (fp == NULL) + return CE_Failure; + + nNextFID ++; + + /* Verify that compulsory feeds are set. Otherwise put some default value in them */ + if (eFormat == GEORSS_RSS) + { + int iFieldTitle = poFeatureDefn->GetFieldIndex( "title" ); + int iFieldDescription = poFeatureDefn->GetFieldIndex( "description" ); + + VSIFPrintfL(fp, " \n"); + + if ((iFieldTitle == -1 || poFeature->IsFieldSet( iFieldTitle ) == FALSE) && + (iFieldDescription == -1 || poFeature->IsFieldSet( iFieldDescription ) == FALSE)) + { + VSIFPrintfL(fp, " Feature %d\n", nNextFID); + } + } + else + { + VSIFPrintfL(fp, " \n"); + + int iFieldId = poFeatureDefn->GetFieldIndex( "id" ); + int iFieldTitle = poFeatureDefn->GetFieldIndex( "title" ); + int iFieldUpdated = poFeatureDefn->GetFieldIndex( "updated" ); + + if (iFieldId == -1 || poFeature->IsFieldSet( iFieldId ) == FALSE) + { + VSIFPrintfL(fp, " Feature %d\n", nNextFID); + } + + if (iFieldTitle == -1 || poFeature->IsFieldSet( iFieldTitle ) == FALSE) + { + VSIFPrintfL(fp, " Title for feature %d\n", nNextFID); + } + + if (iFieldUpdated == -1 || poFeature->IsFieldSet(iFieldUpdated ) == FALSE) + { + VSIFPrintfL(fp, " 2009-01-01T00:00:00Z\n"); + } + } + + int nFieldCount = poFeatureDefn->GetFieldCount(); + int* pbUsed = (int*)CPLCalloc(sizeof(int), nFieldCount); + + for(int i = 0; i < nFieldCount; i ++) + { + OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn( i ); + const char* pszName = poFieldDefn->GetNameRef(); + + if ( ! poFeature->IsFieldSet( i ) ) + continue; + + char* pszElementName; + char* pszNumber; + char* pszAttributeName; + OGRGeoRSSLayerSplitComposedField(pszName, &pszElementName, &pszNumber, &pszAttributeName); + + int bWillSkip = FALSE; + /* Handle Atom entries with elements with sub-elements like */ + /* ......\n", pszElementName); + + int j; + for(j = i; j < nFieldCount; j ++) + { + poFieldDefn = poFeatureDefn->GetFieldDefn( j ); + if ( ! poFeature->IsFieldSet( j ) ) + continue; + + char* pszElementName2; + char* pszNumber2; + char* pszAttributeName2; + OGRGeoRSSLayerSplitComposedField(poFieldDefn->GetNameRef(), + &pszElementName2, &pszNumber2, &pszAttributeName2); + + if (strcmp(pszElementName2, pszElementName) == 0 && + strcmp(pszNumber, pszNumber2) == 0 && pszAttributeName2 != NULL) + { + pbUsed[j] = TRUE; + + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( j )); + VSIFPrintfL(fp, " <%s>%s\n", pszAttributeName2, pszValue, pszAttributeName2); + CPLFree(pszValue); + } + CPLFree(pszElementName2); + CPLFree(pszNumber2); + CPLFree(pszAttributeName2); + } + + VSIFPrintfL(fp, " \n", pszElementName); + + break; + } + } + } + + if (bWillSkip) + { + /* Do nothing */ + } + else if (eFormat == GEORSS_RSS && + strcmp(pszName, "pubDate") == 0) + { + const OGRField* psField = poFeature->GetRawFieldRef(i); + char* pszDate = OGRGetRFC822DateTime(psField); + VSIFPrintfL(fp, " <%s>%s\n", + pszName, pszDate, pszName); + CPLFree(pszDate); + } + else if (eFormat == GEORSS_ATOM && + (strcmp(pszName, "updated") == 0 || strcmp(pszName, "published") == 0)) + { + const OGRField* psField = poFeature->GetRawFieldRef(i); + char* pszDate = OGRGetXMLDateTime(psField); + VSIFPrintfL(fp, " <%s>%s\n", + pszName, pszDate, pszName); + CPLFree(pszDate); + } + else if (strcmp(pszName, "dc_date") == 0) + { + const OGRField* psField = poFeature->GetRawFieldRef(i); + char* pszDate = OGRGetXMLDateTime(psField); + VSIFPrintfL(fp, " <%s>%s\n", + "dc:date", pszDate, "dc:date"); + CPLFree(pszDate); + } + /* RSS fields with content and attributes */ + else if (eFormat == GEORSS_RSS && + (strcmp(pszElementName, "category") == 0 || + strcmp(pszElementName, "guid") == 0 || + strcmp(pszElementName, "source") == 0 )) + { + if (pszAttributeName == NULL) + { + OGRGeoRSSLayerWriteSimpleElement(fp, pszElementName, pszNumber, + apszAllowedRSSFieldNames, poFeatureDefn, poFeature); + } + } + /* RSS field with attribute only */ + else if (eFormat == GEORSS_RSS && + strcmp(pszElementName, "enclosure") == 0) + { + if (pszAttributeName != NULL && strcmp(pszAttributeName, "url") == 0) + { + OGRGeoRSSLayerWriteSimpleElement(fp, pszElementName, pszNumber, + apszAllowedRSSFieldNames, poFeatureDefn, poFeature); + } + } + /* ATOM fields with attribute only */ + else if (eFormat == GEORSS_ATOM && + (strcmp(pszElementName, "category") == 0 || strcmp(pszElementName, "link") == 0)) + { + if (pszAttributeName != NULL && + ((strcmp(pszElementName, "category") == 0 && strcmp(pszAttributeName, "term") == 0) || + (strcmp(pszElementName, "link") == 0 && strcmp(pszAttributeName, "href") == 0))) + { + OGRGeoRSSLayerWriteSimpleElement(fp, pszElementName, pszNumber, + apszAllowedATOMFieldNames, poFeatureDefn, poFeature); + } + } + else if (eFormat == GEORSS_ATOM && + (strncmp(pszName, "content", strlen("content")) == 0 || + strncmp(pszName, "summary", strlen("summary")) == 0)) + { + char* pszFieldName; + int iIndex; + if (strchr(pszName, '_') == NULL) + { + VSIFPrintfL(fp, " <%s", pszName); + + int bIsXHTML = FALSE; + pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszName, "type")); + iIndex = poFeatureDefn->GetFieldIndex(pszFieldName); + if (iIndex != -1 && poFeature->IsFieldSet( iIndex )) + { + bIsXHTML = strcmp(poFeature->GetFieldAsString( iIndex ), "xhtml") == 0; + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( iIndex )); + VSIFPrintfL(fp, " %s=\"%s\"", "type", pszValue); + CPLFree(pszValue); + } + CPLFree(pszFieldName); + + pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_lang")); + iIndex = poFeatureDefn->GetFieldIndex(pszFieldName); + if (iIndex != -1 && poFeature->IsFieldSet( iIndex )) + { + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( iIndex )); + VSIFPrintfL(fp, " %s=\"%s\"", "xml:lang", pszValue); + CPLFree(pszValue); + } + CPLFree(pszFieldName); + + pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_base")); + iIndex = poFeatureDefn->GetFieldIndex(pszFieldName); + if (iIndex != -1 && poFeature->IsFieldSet( iIndex )) + { + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( iIndex )); + VSIFPrintfL(fp, " %s=\"%s\"", "xml:base", pszValue); + CPLFree(pszValue); + } + CPLFree(pszFieldName); + + VSIFPrintfL(fp, ">"); + if (bIsXHTML) + VSIFPrintfL(fp, "%s", poFeature->GetFieldAsString(i)); + else + { + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( i )); + VSIFPrintfL(fp, "%s", pszValue); + CPLFree(pszValue); + } + VSIFPrintfL(fp, " \n", pszName); + } + } + else if (strncmp(pszName, "dc_subject", strlen("dc_subject")) == 0) + { + char* pszFieldName; + int iIndex; + if (strchr(pszName+strlen("dc_subject"), '_') == NULL) + { + VSIFPrintfL(fp, " <%s", "dc:subject"); + + pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_lang")); + iIndex = poFeatureDefn->GetFieldIndex(pszFieldName); + if (iIndex != -1 && poFeature->IsFieldSet( iIndex )) + { + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( iIndex )); + VSIFPrintfL(fp, " %s=\"%s\"", "xml:lang", pszValue); + CPLFree(pszValue); + } + CPLFree(pszFieldName); + + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( i )); + VSIFPrintfL(fp, ">%s\n", pszValue, "dc:subject"); + CPLFree(pszValue); + } + } + else + { + char* pszTagName = CPLStrdup(pszName); + if (IsStandardField(pszName) == FALSE) + { + int j; + int nCountUnderscore = 0; + for(j=0;pszTagName[j] != 0;j++) + { + if (pszTagName[j] == '_') + { + if (nCountUnderscore == 0) + pszTagName[j] = ':'; + nCountUnderscore ++; + } + else if (pszTagName[j] == ' ') + pszTagName[j] = '_'; + } + if (nCountUnderscore == 0) + { + char* pszTemp = CPLStrdup(CPLSPrintf("ogr:%s", pszTagName)); + CPLFree(pszTagName); + pszTagName = pszTemp; + } + } + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( i )); + VSIFPrintfL(fp, " <%s>%s\n", pszTagName, pszValue, pszTagName); + CPLFree(pszValue); + CPLFree(pszTagName); + } + + CPLFree(pszElementName); + CPLFree(pszNumber); + CPLFree(pszAttributeName); + } + + CPLFree(pbUsed); + + OGRGeoRSSGeomDialect eGeomDialect = poDS->GetGeomDialect(); + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if ( poGeom != NULL && !poGeom->IsEmpty() ) + { + char* pszURN = NULL; + int bSwapCoordinates = FALSE; + if (eGeomDialect == GEORSS_GML) + { + if (poSRS != NULL) + { + const char* pszAuthorityName = poSRS->GetAuthorityName(NULL); + const char* pszAuthorityCode = poSRS->GetAuthorityCode(NULL); + if (pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG") && + pszAuthorityCode != NULL) + { + if (!EQUAL(pszAuthorityCode, "4326")) + pszURN = CPLStrdup(CPLSPrintf("urn:ogc:def:crs:EPSG::%s", pszAuthorityCode)); + + /* In case the SRS is a geographic SRS and that we have no axis */ + /* defintion, we assume that the order is lon/lat */ + const char* pszAxisName = poSRS->GetAxis(NULL, 0, NULL); + if (poSRS->IsGeographic() && + (pszAxisName == NULL || EQUALN(pszAxisName, "Lon", 3))) + { + bSwapCoordinates = TRUE; + } + } + else + { + static int bOnce = FALSE; + if (!bOnce) + { + bOnce = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, "Could not translate SRS into GML urn"); + } + } + } + else + { + bSwapCoordinates = TRUE; + } + } + + char szCoord[75]; + switch( wkbFlatten(poGeom->getGeometryType()) ) + { + case wkbPoint: + { + OGRPoint* poPoint = (OGRPoint*)poGeom; + double x = poPoint->getX(); + double y = poPoint->getY(); + if (eGeomDialect == GEORSS_GML) + { + VSIFPrintfL(fp, " getCoordinateDimension() == 3) + { + OGRMakeWktCoordinate(szCoord, (bSwapCoordinates) ? y : x, (bSwapCoordinates) ? x : y, + poPoint->getZ(), 3); + VSIFPrintfL(fp, " srsDimension=\"3\">%s", szCoord); + } + else + { + OGRMakeWktCoordinate(szCoord, (bSwapCoordinates) ? y : x, (bSwapCoordinates) ? x : y, + 0, 2); + VSIFPrintfL(fp, ">%s", szCoord); + } + VSIFPrintfL(fp, "\n"); + } + else if (eGeomDialect == GEORSS_SIMPLE) + { + OGRMakeWktCoordinate(szCoord, y, x, 0, 2); + VSIFPrintfL(fp, " %s\n", szCoord); + } + else if (eGeomDialect == GEORSS_W3C_GEO) + { + OGRFormatDouble( szCoord, sizeof(szCoord), y, '.' ); + VSIFPrintfL(fp, " %s\n", szCoord); + OGRFormatDouble( szCoord, sizeof(szCoord), x, '.' ); + VSIFPrintfL(fp, " %s\n", szCoord); + } + break; + } + + case wkbLineString: + { + OGRLineString* poLineString = (OGRLineString*)poGeom; + if (eGeomDialect == GEORSS_GML) + { + VSIFPrintfL(fp, " \n"); + int n = poLineString->getNumPoints(); + for(int i=0;igetX(i); + double y = poLineString->getY(i); + OGRMakeWktCoordinate(szCoord, (bSwapCoordinates) ? y : x, (bSwapCoordinates) ? x : y, + 0, 2); + VSIFPrintfL(fp, "%s ", szCoord); + } + VSIFPrintfL(fp, "\n"); + } + else if (eGeomDialect == GEORSS_SIMPLE) + { + VSIFPrintfL(fp, " \n"); + int n = poLineString->getNumPoints(); + for(int i=0;igetX(i); + double y = poLineString->getY(i); + OGRMakeWktCoordinate(szCoord, y, x, 0, 2); + VSIFPrintfL(fp, "%s ", szCoord); + } + VSIFPrintfL(fp, "\n"); + } + else + { + /* Not supported */ + } + break; + } + + case wkbPolygon: + { + OGRPolygon* poPolygon = (OGRPolygon*)poGeom; + OGRLineString* poLineString = poPolygon->getExteriorRing(); + if (poLineString == NULL) + break; + + if (eGeomDialect == GEORSS_GML) + { + VSIFPrintfL(fp, " \n"); + int n = poLineString->getNumPoints(); + for(int i=0;igetX(i); + double y = poLineString->getY(i); + OGRMakeWktCoordinate(szCoord, (bSwapCoordinates) ? y : x, (bSwapCoordinates) ? x : y, + 0, 2); + VSIFPrintfL(fp, "%s ", szCoord); + } + VSIFPrintfL(fp, "\n"); + } + else if (eGeomDialect == GEORSS_SIMPLE) + { + VSIFPrintfL(fp, " \n"); + int n = poLineString->getNumPoints(); + for(int i=0;igetX(i); + double y = poLineString->getY(i); + OGRMakeWktCoordinate(szCoord, y, x, 0, 2); + VSIFPrintfL(fp, "%s ", szCoord); + } + VSIFPrintfL(fp, "\n"); + } + else + { + /* Not supported */ + } + break; + } + + default: + /* Not supported */ + break; + } + CPLFree(pszURN); + } + + if (eFormat == GEORSS_RSS) + VSIFPrintfL(fp, " \n"); + else + VSIFPrintfL(fp, " \n"); + + return OGRERR_NONE; +} + + + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRGeoRSSLayer::CreateField( OGRFieldDefn *poFieldDefn, + CPL_UNUSED int bApproxOK ) +{ + const char* pszName = poFieldDefn->GetNameRef(); + if (((eFormat == GEORSS_RSS && strcmp(pszName, "pubDate") == 0) || + (eFormat == GEORSS_ATOM && (strcmp(pszName, "updated") == 0 || + strcmp(pszName, "published") == 0 )) || + strcmp(pszName, "dc:date") == 0) && + poFieldDefn->GetType() != OFTDateTime) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s", pszName); + return OGRERR_FAILURE; + } + + for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + if (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), + pszName ) == 0) + { + return OGRERR_FAILURE; + } + } + + if (IsStandardField(pszName)) + { + poFeatureDefn->AddFieldDefn( poFieldDefn ); + return OGRERR_NONE; + } + + if (poDS->GetUseExtensions() == FALSE) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Field of name '%s' is not supported in %s schema. " + "Use USE_EXTENSIONS creation option to allow use of extensions.", + pszName, (eFormat == GEORSS_RSS) ? "RSS" : "ATOM"); + return OGRERR_FAILURE; + } + else + { + poFeatureDefn->AddFieldDefn( poFieldDefn ); + return OGRERR_NONE; + } +} + +#ifdef HAVE_EXPAT + +static void XMLCALL startElementLoadSchemaCbk(void *pUserData, const char *pszName, const char **ppszAttr) +{ + ((OGRGeoRSSLayer*)pUserData)->startElementLoadSchemaCbk(pszName, ppszAttr); +} + +static void XMLCALL endElementLoadSchemaCbk(void *pUserData, const char *pszName) +{ + ((OGRGeoRSSLayer*)pUserData)->endElementLoadSchemaCbk(pszName); +} + +static void XMLCALL dataHandlerLoadSchemaCbk(void *pUserData, const char *data, int nLen) +{ + ((OGRGeoRSSLayer*)pUserData)->dataHandlerLoadSchemaCbk(data, nLen); +} + + +/************************************************************************/ +/* LoadSchema() */ +/************************************************************************/ + +/** This function parses the whole file to detect the fields */ +void OGRGeoRSSLayer::LoadSchema() +{ + if (bHasReadSchema) + return; + + bHasReadSchema = TRUE; + + if (fpGeoRSS == NULL) + return; + + oSchemaParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oSchemaParser, ::startElementLoadSchemaCbk, ::endElementLoadSchemaCbk); + XML_SetCharacterDataHandler(oSchemaParser, ::dataHandlerLoadSchemaCbk); + XML_SetUserData(oSchemaParser, this); + + VSIFSeekL( fpGeoRSS, 0, SEEK_SET ); + + bInFeature = FALSE; + currentDepth = 0; + currentFieldDefn = NULL; + pszSubElementName = NULL; + pszSubElementValue = NULL; + nSubElementValueLen = 0; + bSameSRS = TRUE; + CPLFree(pszGMLSRSName); + pszGMLSRSName = NULL; + eGeomType = wkbUnknown; + bFoundGeom = FALSE; + bInTagWithSubTag = FALSE; + pszTagWithSubTag = NULL; + bStopParsing = FALSE; + nWithoutEventCounter = 0; + nTotalFeatureCount = 0; + setOfFoundFields = NULL; + + char aBuf[BUFSIZ]; + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpGeoRSS ); + nDone = VSIFEofL(fpGeoRSS); + if (XML_Parse(oSchemaParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of GeoRSS file failed : %s at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(oSchemaParser)), + (int)XML_GetCurrentLineNumber(oSchemaParser), + (int)XML_GetCurrentColumnNumber(oSchemaParser)); + bStopParsing = TRUE; + } + nWithoutEventCounter ++; + } while (!nDone && !bStopParsing && nWithoutEventCounter < 10); + + XML_ParserFree(oSchemaParser); + + if (nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + CPLAssert(poSRS == NULL); + if (bSameSRS && bFoundGeom) + { + if (pszGMLSRSName == NULL) + { + poSRS = new OGRSpatialReference(); + poSRS->SetWellKnownGeogCS( "WGS84" ); /* no AXIS definition ! */ + } + else + { + poSRS = new OGRSpatialReference(); + poSRS->importFromURN(pszGMLSRSName); + } + } + + if (eGeomType != wkbUnknown) + poFeatureDefn->SetGeomType(eGeomType); + if( poFeatureDefn->GetGeomFieldCount() != 0 ) + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + if (setOfFoundFields) + CPLHashSetDestroy(setOfFoundFields); + setOfFoundFields = NULL; + CPLFree(pszGMLSRSName); + pszGMLSRSName = NULL; + CPLFree(pszTagWithSubTag); + pszTagWithSubTag = NULL; + + VSIFSeekL( fpGeoRSS, 0, SEEK_SET ); +} + + +/************************************************************************/ +/* OGRGeoRSSIsInt() */ +/************************************************************************/ + +static int OGRGeoRSSIsInt(const char* pszStr) +{ + int i; + + while(*pszStr == ' ') + pszStr++; + + for(i=0;pszStr[i];i++) + { + if (pszStr[i] == '+' || pszStr[i] == '-') + { + if (i != 0) + return FALSE; + } + else if (!(pszStr[i] >= '0' && pszStr[i] <= '9')) + return FALSE; + } + return TRUE; +} + +/************************************************************************/ +/* startElementLoadSchemaCbk() */ +/************************************************************************/ + +void OGRGeoRSSLayer::startElementLoadSchemaCbk(const char *pszName, const char **ppszAttr) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + const char* pszNoNSName = pszName; + const char* pszColon = strchr(pszNoNSName, ':'); + if( pszColon ) + pszNoNSName = pszColon + 1; + + if ((eFormat == GEORSS_ATOM && currentDepth == 1 && strcmp(pszNoNSName, "entry") == 0) || + ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && !bInFeature && + (currentDepth == 1 || currentDepth == 2) && strcmp(pszNoNSName, "item") == 0)) + { + bInFeature = TRUE; + featureDepth = currentDepth; + + nTotalFeatureCount ++; + + if (setOfFoundFields) + CPLHashSetDestroy(setOfFoundFields); + setOfFoundFields = CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree); + } + else if (bInTagWithSubTag && currentDepth == 3) + { + char* pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszNoNSName)); + if (poFeatureDefn->GetFieldIndex(pszFieldName) == -1) + { + OGRFieldDefn newFieldDefn(pszFieldName, OFTString); + poFeatureDefn->AddFieldDefn(&newFieldDefn); + + if (poFeatureDefn->GetFieldCount() == 100) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too many fields. File probably corrupted"); + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + } + } + CPLFree(pszFieldName); + } + else if (bInFeature && eFormat == GEORSS_ATOM && + currentDepth == 2 && OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName)) + { + CPLFree(pszTagWithSubTag); + pszTagWithSubTag = CPLStrdup(pszNoNSName); + + int count = 1; + while(CPLHashSetLookup(setOfFoundFields, pszTagWithSubTag) != NULL) + { + count ++; + CPLFree(pszTagWithSubTag); + pszTagWithSubTag = CPLStrdup(CPLSPrintf("%s%d", pszNoNSName, count)); + if (pszTagWithSubTag[0] == 0) + { + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + break; + } + } + CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszTagWithSubTag)); + + bInTagWithSubTag = TRUE; + } + else if (bInFeature && currentDepth == featureDepth + 1 && !IS_GEO_ELEMENT(pszName)) + { + if( pszName != pszNoNSName && strncmp(pszName, "atom:", 5) == 0 ) + pszName = pszNoNSName; + + CPLFree(pszSubElementName); + pszSubElementName = CPLStrdup(pszName); + + int count = 1; + while(CPLHashSetLookup(setOfFoundFields, pszSubElementName) != NULL) + { + count ++; + CPLFree(pszSubElementName); + pszSubElementName = CPLStrdup(CPLSPrintf("%s%d", pszName, count)); + } + CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszSubElementName)); + + /* Create field definition for element */ + char* pszCompatibleName = OGRGeoRSS_GetOGRCompatibleTagName(pszSubElementName); + int iField = poFeatureDefn->GetFieldIndex(pszCompatibleName); + if (iField >= 0) + { + currentFieldDefn = poFeatureDefn->GetFieldDefn(iField); + } + else if ( ! ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && strcmp(pszNoNSName, "enclosure") == 0) && + ! (eFormat == GEORSS_ATOM && strcmp(pszNoNSName, "link") == 0) && + ! (eFormat == GEORSS_ATOM && strcmp(pszNoNSName, "category") == 0)) + { + OGRFieldType eFieldType; + if (((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && strcmp(pszNoNSName, "pubDate") == 0) || + (eFormat == GEORSS_ATOM && strcmp(pszNoNSName, "updated") == 0) || + (eFormat == GEORSS_ATOM && strcmp(pszNoNSName, "published") == 0) || + strcmp(pszName, "dc:date") == 0) + eFieldType = OFTDateTime; + else + eFieldType = OFTInteger; + + OGRFieldDefn newFieldDefn(pszCompatibleName, eFieldType); + poFeatureDefn->AddFieldDefn(&newFieldDefn); + currentFieldDefn = poFeatureDefn->GetFieldDefn(poFeatureDefn->GetFieldCount() - 1); + + if (poFeatureDefn->GetFieldCount() == 100) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too many fields. File probably corrupted"); + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + } + } + + /* Create field definitions for attributes */ + for(int i=0; ppszAttr[i] != NULL && ppszAttr[i+1] != NULL && !bStopParsing; i+= 2) + { + char* pszAttrCompatibleName = + OGRGeoRSS_GetOGRCompatibleTagName(CPLSPrintf("%s_%s", pszSubElementName, ppszAttr[i])); + iField = poFeatureDefn->GetFieldIndex(pszAttrCompatibleName); + OGRFieldDefn* currentAttrFieldDefn; + if (iField >= 0) + { + currentAttrFieldDefn = poFeatureDefn->GetFieldDefn(iField); + } + else + { + OGRFieldDefn newFieldDefn(pszAttrCompatibleName, OFTInteger); + poFeatureDefn->AddFieldDefn(&newFieldDefn); + currentAttrFieldDefn = poFeatureDefn->GetFieldDefn(poFeatureDefn->GetFieldCount() - 1); + + if (poFeatureDefn->GetFieldCount() == 100) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too many fields. File probably corrupted"); + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + } + } + if (currentAttrFieldDefn->GetType() == OFTInteger || + currentAttrFieldDefn->GetType() == OFTReal) + { + char* pszRemainingStr = NULL; + CPLStrtod(ppszAttr[i + 1], &pszRemainingStr); + if (pszRemainingStr == NULL || + *pszRemainingStr == 0 || + *pszRemainingStr == ' ') + { + if (currentAttrFieldDefn->GetType() == OFTInteger) + { + if (OGRGeoRSSIsInt(ppszAttr[i + 1]) == FALSE) + { + currentAttrFieldDefn->SetType(OFTReal); + } + } + } + else + { + currentAttrFieldDefn->SetType(OFTString); + } + } + CPLFree(pszAttrCompatibleName); + } + + CPLFree(pszCompatibleName); + } + else if (strcmp(pszName, "georss:point") == 0 || + strcmp(pszName, "georss:line") == 0 || + strcmp(pszName, "geo:line") == 0 || + IS_LAT_ELEMENT(pszName) || + strcmp(pszName, "georss:polygon") == 0 || + strcmp(pszName, "georss:box") == 0) + { + if (bSameSRS) + { + if (pszGMLSRSName != NULL) + bSameSRS = FALSE; + } + } + else if (strcmp(pszName, "gml:Point") == 0 || + strcmp(pszName, "gml:LineString") == 0 || + strcmp(pszName, "gml:Polygon") == 0 || + strcmp(pszName, "gml:MultiPoint") == 0 || + strcmp(pszName, "gml:MultiLineString") == 0 || + strcmp(pszName, "gml:MultiPolygon") == 0 || + strcmp(pszName, "gml:Envelope") == 0) + { + if (bSameSRS) + { + int bFoundSRS = FALSE; + for(int i = 0; ppszAttr[i] != NULL; i+=2) + { + if (strcmp(ppszAttr[i], "srsName") == 0) + { + bFoundSRS = TRUE; + if (pszGMLSRSName != NULL) + { + if (strcmp(pszGMLSRSName , ppszAttr[i+1]) != 0) + bSameSRS = FALSE; + } + else + pszGMLSRSName = CPLStrdup(ppszAttr[i+1]); + break; + } + } + if (!bFoundSRS && pszGMLSRSName != NULL) + bSameSRS = FALSE; + } + } + + if (!bInFeature || currentDepth >= featureDepth + 1) + { + int nDimension = 2; + for(int i = 0; ppszAttr[i] != NULL; i+=2) + { + if (strcmp(ppszAttr[i], "srsDimension") == 0) + { + nDimension = atoi(ppszAttr[i+1]); + break; + } + } + + OGRwkbGeometryType eFoundGeomType = wkbUnknown; + if (strcmp(pszName, "georss:point") == 0 || + IS_LAT_ELEMENT(pszName) || + strcmp(pszName, "gml:Point") == 0) + { + eFoundGeomType = wkbPoint; + } + else if (strcmp(pszName, "gml:MultiPoint") == 0) + { + eFoundGeomType = wkbMultiPoint; + } + else if (strcmp(pszName, "georss:line") == 0 || + strcmp(pszName, "geo:line") == 0 || + strcmp(pszName, "gml:LineString") == 0) + { + eFoundGeomType = wkbLineString; + } + else if (strcmp(pszName, "gml:MultiLineString") == 0) + { + eFoundGeomType = wkbMultiLineString; + } + else if (strcmp(pszName, "georss:polygon") == 0 || + strcmp(pszName, "gml:Polygon") == 0 || + strcmp(pszName, "gml:Envelope") == 0 || + strcmp(pszName, "georss:box") == 0) + { + eFoundGeomType = wkbPolygon; + } + else if (strcmp(pszName, "gml:MultiPolygon") == 0) + { + eFoundGeomType = wkbMultiPolygon; + } + + if (eFoundGeomType != wkbUnknown) + { + if (!bFoundGeom) + { + eGeomType = eFoundGeomType; + bFoundGeom = TRUE; + } + else if (wkbFlatten(eGeomType) != eFoundGeomType) + eGeomType = wkbUnknown; + + if (nDimension == 3) + eGeomType = wkbSetZ(eGeomType); + } + } + + currentDepth++; +} + +/************************************************************************/ +/* endElementLoadSchemaCbk() */ +/************************************************************************/ + +void OGRGeoRSSLayer::endElementLoadSchemaCbk(const char *pszName) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + currentDepth--; + + if (!bInFeature) + return; + + const char* pszNoNSName = pszName; + const char* pszColon = strchr(pszNoNSName, ':'); + if( pszColon ) + pszNoNSName = pszColon + 1; + + if ((eFormat == GEORSS_ATOM && currentDepth == 1 && strcmp(pszNoNSName, "entry") == 0) || + ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && + (currentDepth == 1 || currentDepth == 2) && strcmp(pszNoNSName, "item") == 0)) + { + bInFeature = FALSE; + } + else if (bInFeature && eFormat == GEORSS_ATOM && + currentDepth == 2 && OGRGeoRSSLayerATOMTagHasSubElement(pszNoNSName)) + { + bInTagWithSubTag = FALSE; + } + else if (currentDepth == featureDepth + 1 && pszSubElementName) + { + /* Patch field type */ + if (pszSubElementValue && nSubElementValueLen && currentFieldDefn) + { + pszSubElementValue[nSubElementValueLen] = 0; + if (currentFieldDefn->GetType() == OFTInteger || + currentFieldDefn->GetType() == OFTReal) + { + char* pszRemainingStr = NULL; + CPLStrtod(pszSubElementValue, &pszRemainingStr); + if (pszRemainingStr == NULL || + *pszRemainingStr == 0 || + *pszRemainingStr == ' ') + { + if (currentFieldDefn->GetType() == OFTInteger) + { + if (OGRGeoRSSIsInt(pszSubElementValue) == FALSE) + { + currentFieldDefn->SetType(OFTReal); + } + } + } + else + { + currentFieldDefn->SetType(OFTString); + } + } + } + + CPLFree(pszSubElementName); + pszSubElementName = NULL; + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + currentFieldDefn = NULL; + } +} + +/************************************************************************/ +/* dataHandlerLoadSchemaCbk() */ +/************************************************************************/ + +void OGRGeoRSSLayer::dataHandlerLoadSchemaCbk(const char *data, int nLen) +{ + if (bStopParsing) return; + + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)"); + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + + nWithoutEventCounter = 0; + + if (pszSubElementName) + { + char* pszNewSubElementValue = (char*) VSIRealloc(pszSubElementValue, nSubElementValueLen + nLen + 1); + if (pszNewSubElementValue == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + pszSubElementValue = pszNewSubElementValue; + memcpy(pszSubElementValue + nSubElementValueLen, data, nLen); + nSubElementValueLen += nLen; + if (nSubElementValueLen > 100000) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + } + } +} +#else +void OGRGeoRSSLayer::LoadSchema() +{ +} +#endif + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGeoRSSLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCFastFeatureCount) ) + return !bWriteMode && bHasReadSchema && + m_poFilterGeom == NULL && m_poAttrQuery == NULL; + + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) ) + return bWriteMode; + else if( EQUAL(pszCap,OLCCreateField) ) + return bWriteMode; + else + return FALSE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRGeoRSSLayer::GetFeatureCount( int bForce ) + +{ + if (bWriteMode) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot read features when writing a GeoRSS file"); + return 0; + } + + if (!bHasReadSchema) + LoadSchema(); + + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL ) + return OGRLayer::GetFeatureCount( bForce ); + else + return nTotalFeatureCount; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/GNUmakefile new file mode 100644 index 000000000..511477390 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrgftdriver.o ogrgftdatasource.o ogrgftlayer.o ogrgfttablelayer.o ogrgftresultlayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_gft.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/drv_gft.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/drv_gft.html new file mode 100644 index 000000000..b6758f88f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/drv_gft.html @@ -0,0 +1,188 @@ + + +GFT - Google Fusion Tables + + + + +

    GFT - Google Fusion Tables

    + +(GDAL/OGR >= 1.9.0)

    + +This driver can connect to the Google Fusion Tables service. GDAL/OGR must be built with Curl support in order to the +GFT driver to be compiled.

    + +The driver supports read and write operations.

    + +

    Dataset name syntax

    + +The minimal syntax to open a GFT datasource is :
    GFT:

    + +Additionnal optional parameters can be specified after the ':' sign such as :

    + +

      +
    • tables=table_id1[,table_id2]*: A list of table IDs. This is necessary when you need to access to public tables for example. +If you want the table ID of a public table, or any other table that is not owned by the authenticated user, you have to visit the table +in the Google Fusion Tables website and note the number at the end of the URL. +
    • protocol=https: To use HTTPS protocol for all operations. By default, HTTP is used, except for the authentication operation +where HTTPS is always used. +
    • auth=auth_key: An authentication key as described below. +
    • access=access_token: An access token as described below. +
    • refresh=refresh_token: A refresh token as described below. +
    + +If several parameters are specified, they must be separated by a space. + +

    Authentication

    + +Most operations, in particular write operations, require a valid Google +account to provide authentication information to the driver. The only +exception is read-only access to public tables.

    + +In order to create an authorization key, it is necessary to + +login and authorize access to fusion tables for a google (ie. gmail) +account. The resulting authorization key can be turned into a refresh +token for use OGR using the gdal/swig/python/scripts/gdal_auth.py script +distributed with GDAL (available in GDAL/OGR >= 1.10.0). Note that auth +tokens can only be used once, while the resulting refresh token lasts +indefinately.

    + +

    +  gdal_auth.py auth2refresh auth_token
    +
    + +This refresh token can then be either set as a configuration option +(GFT_REFRESH_TOKEN) or included in the connection string +(ie. GFT:refresh=refresh_token).

    + +Generally OAuth2 credentials can be provided via these mechanisms:

    + +

      +
    • Specifying an access token via the GFT_ACCESS_TOKEN +configuration/environment variable.

      +

    • Specifying an access token via the access= +clause in the GFT: connection string. +
    • Specifying a refresh token via the GFT_REFRESH_TOKEN +configuration/environment variable.

      +

    • Specifying an refresh token via the refresh= +clause in the GFT: connection string. +
    • Specifying a auth key via the GFT_AUTH +configuration/environment variable.

      +

    • Specifying an auth key via the auth= +clause in the GFT: connection string. +
    + +

    Geometry

    + +Geometries in GFT tables must be expressed in the geodetic WGS84 SRS. GFT allows them to be encoded in different forms : +
      +
    • A single column with a "lat lon" or "lat,lon" string, where lat et lon are expressed in decimal degrees.
    • +
    • A single column with a KML string that is the representation of a Point, a LineString or a Polygon.
    • +
    • Two columns, one with the latitude and the other one with the longitude, both values expressed in decimal degrees.
    • +
    • A single column with an address known by the geocoding service of Google Maps.
    • +
    + +Only the first 3 types are supported by OGR, not the last one.

    + +Fusion tables can have multiple geometry columns per table. By default, OGR +will use the first geometry column it finds. It is possible to select another +column as the geometry column by specifying table_name(geometry_column_name) +as the layer name passed to GetLayerByName().

    + +

    Filtering

    + +The driver will forward any spatial filter set with SetSpatialFilter() to the server. It also makes the same for attribute +filters set with SetAttributeFilter().

    + +

    Paging

    + +Features are retrieved from the server by chunks of 500 by default. This number can be altered with the GFT_PAGE_SIZE +configuration option.

    + +

    Write support

    + +Table creation and deletion is possible. Note that fields can only be added to a table in which there are no features created yet.

    + +Write support is only enabled when the datasource is opened in update mode.

    + +The mapping between the operations of the GFT service and the OGR concepts is the following : +

      +
    • OGRFeature::CreateFeature() <==> INSERT operation
    • +
    • OGRFeature::SetFeature() <==> UPDATE operation
    • +
    • OGRFeature::DeleteFeature() <==> DELETE operation
    • +
    • OGRDataSource::CreateLayer() <==> CREATE TABLE operation
    • +
    • OGRDataSource::DeleteLayer() <==> DROP TABLE operation
    • +
    + +When inserting a new feature with CreateFeature(), and if the command is successful, OGR will fetch the +returned rowid and use it as the OGR FID. OGR will also automatically reproject its geometry into the +geodetic WGS84 SRS if needed (provided that the original SRS is attached to the geometry).

    + +

    Write support and OGR transactions

    + +The above operations are by default issued to the server synchronously with the OGR API call. This however +can cause performance penalties when issuing a lot of commands due to many client/server exchanges.

    + +It is possible to surround the CreateFeature() oepration between OGRLayer::StartTransaction() and OGRLayer::CommitTransaction(). +The operations will be stored into memory and only executed at the time CommitTransaction() is called. Note that +the GFT service only supports up to 500 INSERTs and up to 1MB of content per transaction.

    + +Note : only CreateFeature() makes use of OGR transaction mechanism. SetFeature() and DeleteFeature() +will still be issued immediately.

    + +

    SQL

    + +SQL commands provided to the OGRDataSource::ExecuteSQL() call are executed on the server side, unless the OGRSQL +dialect is specified. The subset of SQL supported by the GFT service is described in the links at the end of this page.

    + +The SQL supported by the server understands only natively table id, and not the table names returned by OGR. +For conveniency, OGR will "patch" your SQL command to replace the table name by the table id however.

    + +

    Examples

    + +
  • +Listing the tables and views owned by the authenticated user: +
    +ogrinfo -ro "GFT:email=john.doe@example.com password=secret_password"
    +
    +

    + +

  • +Creating and populating a table from a shapefile: +
    +ogr2ogr -f GFT "GFT:email=john.doe@example.com password=secret_password" shapefile.shp
    +
    +

    + +

  • +Displaying the content of a public table with a spatial and attribue filters: +
    +ogrinfo -ro "GFT:tables=224453" -al -spat 67 31.5 67.5 32 -where "'Attack on' = 'ENEMY'"
    +
    +

    + +

  • +Getting the auth key: +
    +ogrinfo --config CPL_DEBUG ON "GFT:email=john.doe@example.com password=secret_password"
    +
    + +returns: +
    +HTTP: Fetch(https://www.google.com/accounts/ClientLogin)
    +HTTP: These HTTP headers were set: Content-Type: application/x-www-form-urlencoded
    +GFT: Auth key : A_HUGE_STRING_WITH_ALPHANUMERIC_AND_SPECIAL_CHARACTERS
    +
    + +Now, you can set the GFT_AUTH environment variable to that value and simply use "GFT:" as the DSN. + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/makefile.vc new file mode 100644 index 000000000..1a644294e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrgftdriver.obj ogrgftdatasource.obj ogrgftlayer.obj ogrgfttablelayer.obj ogrgftresultlayer.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogr_gft.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogr_gft.h new file mode 100644 index 000000000..6bb6b210a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogr_gft.h @@ -0,0 +1,263 @@ +/****************************************************************************** + * $Id: ogr_gft.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: GFT Translator + * Purpose: Definition of classes for OGR Google Fusion Tables driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GFT_H_INCLUDED +#define _OGR_GFT_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_http.h" + +#include + +/************************************************************************/ +/* OGRGFTLayer */ +/************************************************************************/ +class OGRGFTDataSource; + +class OGRGFTLayer : public OGRLayer +{ +protected: + OGRGFTDataSource* poDS; + + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference *poSRS; + + int nNextInSeq; + + int iGeometryField; + int iLatitudeField; + int iLongitudeField; + int bHiddenGeometryField; + + OGRFeature * GetNextRawFeature(); + + int nOffset; + int bEOF; + virtual int FetchNextRows() = 0; + + std::vector aosRows; + + int bFirstTokenIsFID; + OGRFeature* BuildFeatureFromSQL(const char* pszLine); + + static CPLString LaunderColName(const char* pszColName); + + void SetGeomFieldName(); + + public: + OGRGFTLayer(OGRGFTDataSource* poDS); + ~OGRGFTLayer(); + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn(); + + virtual int TestCapability( const char * ); + + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + + const char * GetDefaultGeometryColumnName() { return "geometry"; } + + static int ParseCSVResponse(char* pszLine, + std::vector& aosRes); + static CPLString PatchSQL(const char* pszSQL); + + int GetGeometryFieldIndex() { return iGeometryField; } + int GetLatitudeFieldIndex() { return iLatitudeField; } + int GetLongitudeFieldIndex() { return iLongitudeField; } + + int GetFeaturesToFetch() { return atoi(CPLGetConfigOption("GFT_PAGE_SIZE", "500")); } +}; + +/************************************************************************/ +/* OGRGFTTableLayer */ +/************************************************************************/ + +class OGRGFTTableLayer : public OGRGFTLayer +{ + CPLString osTableName; + CPLString osTableId; + CPLString osGeomColumnName; + + int bHasTriedCreateTable; + void CreateTableIfNecessary(); + + CPLString osWHERE; + CPLString osQuery; + + void BuildWhere(void); + + CPLString osTransaction; + int bInTransaction; + int nFeaturesInTransaction; + + int FetchDescribe(); + virtual int FetchNextRows(); + + OGRwkbGeometryType eGTypeForCreation; + + std::vector aosColumnInternalName; + + public: + OGRGFTTableLayer(OGRGFTDataSource* poDS, + const char* pszTableName, + const char* pszTableId = "", + const char* pszGeomColumnName = ""); + ~OGRGFTTableLayer(); + + virtual void ResetReading(); + + virtual OGRFeatureDefn * GetLayerDefn(); + + virtual const char * GetName() { return osTableName.c_str(); } + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + + virtual OGRFeature * GetFeature( GIntBig nFID ); + + virtual void SetSpatialFilter( OGRGeometry * ); + virtual OGRErr SetAttributeFilter( const char * ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + const CPLString& GetTableId() const { return osTableId; } + + virtual int TestCapability( const char * ); + + void SetGeometryType(OGRwkbGeometryType eGType); +}; + +/************************************************************************/ +/* OGRGFTResultLayer */ +/************************************************************************/ + +class OGRGFTResultLayer : public OGRGFTLayer +{ + CPLString osSQL; + int bGotAllRows; + + virtual int FetchNextRows(); + + public: + OGRGFTResultLayer(OGRGFTDataSource* poDS, + const char* pszSQL); + ~OGRGFTResultLayer(); + + virtual void ResetReading(); + + int RunSQL(); +}; + +/************************************************************************/ +/* OGRGFTDataSource */ +/************************************************************************/ + +class OGRGFTDataSource : public OGRDataSource +{ + char* pszName; + + OGRLayer** papoLayers; + int nLayers; + + int bReadWrite; + + int bUseHTTPS; + + CPLString osAuth; + CPLString osAccessToken; + CPLString osRefreshToken; + CPLString osAPIKey; + + void DeleteLayer( const char *pszLayerName ); + + int bMustCleanPersistant; + + static CPLStringList ParseSimpleJson(const char *pszJSon); + + public: + OGRGFTDataSource(); + ~OGRGFTDataSource(); + + int Open( const char * pszFilename, + int bUpdate ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + virtual OGRLayer *GetLayerByName(const char *); + + virtual int TestCapability( const char * ); + + virtual OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + virtual OGRErr DeleteLayer(int); + + virtual OGRLayer* ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + const CPLString& GetAccessToken() const { return osAccessToken;} + const char* GetAPIURL() const; + int IsReadWrite() const { return bReadWrite; } + char** AddHTTPOptions(char** papszOptions = NULL); + CPLHTTPResult* RunSQL(const char* pszUnescapedSQL); +}; + +/************************************************************************/ +/* OGRGFTDriver */ +/************************************************************************/ + +class OGRGFTDriver : public OGRSFDriver +{ + public: + ~OGRGFTDriver(); + + virtual const char* GetName(); + virtual OGRDataSource* Open( const char *, int ); + virtual OGRDataSource* CreateDataSource( const char * pszName, + char **papszOptions ); + virtual int TestCapability( const char * ); +}; + +char **OGRGFTCSVSplitLine( const char *pszString, char chDelimiter ); +char* OGRGFTGotoNextLine(char* pszData); + +#endif /* ndef _OGR_GFT_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgftdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgftdatasource.cpp new file mode 100644 index 000000000..c0ede5f3b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgftdatasource.cpp @@ -0,0 +1,597 @@ +/****************************************************************************** + * $Id: ogrgftdatasource.cpp 28939 2015-04-17 19:01:24Z rouault $ + * + * Project: Google Fusion Table Translator + * Purpose: Implements OGRGFTDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gft.h" + +CPL_CVSID("$Id: ogrgftdatasource.cpp 28939 2015-04-17 19:01:24Z rouault $"); + +#define GDAL_API_KEY "AIzaSyA_2h1_wXMOLHNSVeo-jf1ACME-M1XMgP0" +#define FUSION_TABLE_SCOPE "https://www.googleapis.com/Fauth/fusiontables" + +/************************************************************************/ +/* OGRGFTDataSource() */ +/************************************************************************/ + +OGRGFTDataSource::OGRGFTDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; + + bReadWrite = FALSE; + bUseHTTPS = FALSE; + + bMustCleanPersistant = FALSE; +} + +/************************************************************************/ +/* ~OGRGFTDataSource() */ +/************************************************************************/ + +OGRGFTDataSource::~OGRGFTDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + if (bMustCleanPersistant) + { + char** papszOptions = NULL; + papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT", CPLSPrintf("GFT:%p", this)); + CPLHTTPFetch( GetAPIURL(), papszOptions); + CSLDestroy(papszOptions); + } + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGFTDataSource::TestCapability( const char * pszCap ) + +{ + if( bReadWrite && EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else if( bReadWrite && EQUAL(pszCap,ODsCDeleteLayer) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRGFTDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer *OGRGFTDataSource::GetLayerByName(const char * pszLayerName) +{ + OGRLayer* poLayer = OGRDataSource::GetLayerByName(pszLayerName); + if (poLayer) + return poLayer; + + char* pszGeomColumnName = NULL; + char* pszName = CPLStrdup(pszLayerName); + char *pszLeftParenthesis = strchr(pszName, '('); + if( pszLeftParenthesis != NULL ) + { + *pszLeftParenthesis = '\0'; + pszGeomColumnName = CPLStrdup(pszLeftParenthesis+1); + int len = strlen(pszGeomColumnName); + if (len > 0 && pszGeomColumnName[len - 1] == ')') + pszGeomColumnName[len - 1] = '\0'; + } + + CPLString osTableId(pszName); + for(int i=0;iGetName(), pszName) == 0) + { + osTableId = ((OGRGFTTableLayer*)papoLayers[i])->GetTableId(); + break; + } + } + + poLayer = new OGRGFTTableLayer(this, pszLayerName, osTableId, + pszGeomColumnName); + CPLFree(pszName); + CPLFree(pszGeomColumnName); + if (poLayer->GetLayerDefn()->GetFieldCount() == 0) + { + delete poLayer; + return NULL; + } + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers ++] = poLayer; + return poLayer; +} + +/************************************************************************/ +/* OGRGFTGetOptionValue() */ +/************************************************************************/ + +CPLString OGRGFTGetOptionValue(const char* pszFilename, + const char* pszOptionName) +{ + CPLString osOptionName(pszOptionName); + osOptionName += "="; + const char* pszOptionValue = strstr(pszFilename, osOptionName); + if (!pszOptionValue) + return ""; + + CPLString osOptionValue(pszOptionValue + strlen(osOptionName)); + const char* pszSpace = strchr(osOptionValue.c_str(), ' '); + if (pszSpace) + osOptionValue.resize(pszSpace - osOptionValue.c_str()); + return osOptionValue; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRGFTDataSource::Open( const char * pszFilename, int bUpdateIn) + +{ + bReadWrite = bUpdateIn; + + pszName = CPLStrdup( pszFilename ); + + osAuth = OGRGFTGetOptionValue(pszFilename, "auth"); + if (osAuth.size() == 0) + osAuth = CPLGetConfigOption("GFT_AUTH", ""); + + osRefreshToken = OGRGFTGetOptionValue(pszFilename, "refresh"); + if (osRefreshToken.size() == 0) + osRefreshToken = CPLGetConfigOption("GFT_REFRESH_TOKEN", ""); + + osAPIKey = CPLGetConfigOption("GFT_APIKEY", GDAL_API_KEY); + + CPLString osTables = OGRGFTGetOptionValue(pszFilename, "tables"); + + bUseHTTPS = TRUE; + + osAccessToken = OGRGFTGetOptionValue(pszFilename, "access"); + if (osAccessToken.size() == 0) + osAccessToken = CPLGetConfigOption("GFT_ACCESS_TOKEN",""); + if (osAccessToken.size() == 0 && osRefreshToken.size() > 0) + { + osAccessToken.Seize(GOA2GetAccessToken(osRefreshToken, + FUSION_TABLE_SCOPE)); + if (osAccessToken.size() == 0) + return FALSE; + } + + if (osAccessToken.size() == 0 && osAuth.size() > 0) + { + osRefreshToken.Seize(GOA2GetRefreshToken(osAuth, FUSION_TABLE_SCOPE)); + if (osRefreshToken.size() == 0) + return FALSE; + } + + if (osAccessToken.size() == 0) + { + if (osTables.size() == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unauthenticated access requires explicit tables= parameter"); + return FALSE; + } + } + + if (osTables.size() != 0) + { + char** papszTables = CSLTokenizeString2(osTables, ",", 0); + for(int i=0;papszTables && papszTables[i];i++) + { + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers ++] = new OGRGFTTableLayer(this, papszTables[i], papszTables[i]); + } + CSLDestroy(papszTables); + return TRUE; + } + + /* Get list of tables */ + CPLHTTPResult * psResult = RunSQL("SHOW TABLES"); + + if (psResult == NULL) + return FALSE; + + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || + psResult->pszErrBuf != NULL || + strncmp(pszLine, "table id,name", strlen("table id,name")) != 0) + { + CPLHTTPDestroyResult(psResult); + return FALSE; + } + + pszLine = OGRGFTGotoNextLine(pszLine); + while(pszLine != NULL && *pszLine != 0) + { + char* pszNextLine = OGRGFTGotoNextLine(pszLine); + if (pszNextLine) + pszNextLine[-1] = 0; + + char** papszTokens = CSLTokenizeString2(pszLine, ",", 0); + if (CSLCount(papszTokens) == 2) + { + CPLString osTableId(papszTokens[0]); + CPLString osLayerName(papszTokens[1]); + for(int i=0;iGetName(), osLayerName) == 0) + { + osLayerName += " ("; + osLayerName += osTableId; + osLayerName += ")"; + break; + } + } + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers ++] = new OGRGFTTableLayer(this, osLayerName, osTableId); + } + CSLDestroy(papszTokens); + + pszLine = pszNextLine; + } + + CPLHTTPDestroyResult(psResult); + + return TRUE; +} + +/************************************************************************/ +/* GetAPIURL() */ +/************************************************************************/ + +const char* OGRGFTDataSource::GetAPIURL() const +{ + const char* pszAPIURL = CPLGetConfigOption("GFT_API_URL", NULL); + if (pszAPIURL) + return pszAPIURL; + else if (bUseHTTPS) + return "https://www.googleapis.com/fusiontables/v1/query"; + else + return "http://www.googleapis.com/fusiontables/v1/query"; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer *OGRGFTDataSource::ICreateLayer( const char *pszName, + CPL_UNUSED OGRSpatialReference *poSpatialRef, + OGRwkbGeometryType eGType, + char ** papszOptions ) +{ + if (!bReadWrite) + { + CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in read-only mode"); + return NULL; + } + + if (osAccessToken.size() == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in unauthenticated mode"); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we already have this layer? If so, should we blow it */ +/* away? */ +/* -------------------------------------------------------------------- */ + int iLayer; + + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszName,papoLayers[iLayer]->GetName()) ) + { + if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL + && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") ) + { + DeleteLayer( pszName ); + break; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + pszName ); + return NULL; + } + } + } + + OGRGFTTableLayer* poLayer = new OGRGFTTableLayer(this, pszName); + poLayer->SetGeometryType(eGType); + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers ++] = poLayer; + return poLayer; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +void OGRGFTDataSource::DeleteLayer( const char *pszLayerName ) + +{ + int iLayer; + +/* -------------------------------------------------------------------- */ +/* Try to find layer. */ +/* -------------------------------------------------------------------- */ + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) ) + break; + } + + if( iLayer == nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to delete layer '%s', but this layer is not known to OGR.", + pszLayerName ); + return; + } + + DeleteLayer(iLayer); +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +OGRErr OGRGFTDataSource::DeleteLayer(int iLayer) +{ + if (!bReadWrite) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + if (osAccessToken.size() == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in unauthenticated mode"); + return OGRERR_FAILURE; + } + + if( iLayer < 0 || iLayer >= nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %d not in legal range of 0 to %d.", + iLayer, nLayers-1 ); + return OGRERR_FAILURE; + } + + CPLString osTableId = ((OGRGFTTableLayer*)papoLayers[iLayer])->GetTableId(); + CPLString osLayerName = GetLayer(iLayer)->GetName(); + +/* -------------------------------------------------------------------- */ +/* Blow away our OGR structures related to the layer. This is */ +/* pretty dangerous if anything has a reference to this layer! */ +/* -------------------------------------------------------------------- */ + CPLDebug( "GFT", "DeleteLayer(%s)", osLayerName.c_str() ); + + delete papoLayers[iLayer]; + memmove( papoLayers + iLayer, papoLayers + iLayer + 1, + sizeof(void *) * (nLayers - iLayer - 1) ); + nLayers--; + +/* -------------------------------------------------------------------- */ +/* Remove from the database. */ +/* -------------------------------------------------------------------- */ + + CPLString osSQL("DROP TABLE "); + osSQL += osTableId; + + CPLHTTPResult* psResult = RunSQL( osSQL ); + + if (psResult == NULL || psResult->nStatus != 0) + { + CPLError(CE_Failure, CPLE_AppDefined, "Table deletion failed (1)"); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLHTTPDestroyResult(psResult); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* AddHTTPOptions() */ +/************************************************************************/ + +char** OGRGFTDataSource::AddHTTPOptions(char** papszOptions) +{ + bMustCleanPersistant = TRUE; + + if (strlen(osAccessToken) > 0) + papszOptions = CSLAddString(papszOptions, + CPLSPrintf("HEADERS=Authorization: Bearer %s", + osAccessToken.c_str())); + + return CSLAddString(papszOptions, CPLSPrintf("PERSISTENT=GFT:%p", this)); +} + +/************************************************************************/ +/* RunSQL() */ +/************************************************************************/ + +CPLHTTPResult * OGRGFTDataSource::RunSQL(const char* pszUnescapedSQL) +{ + CPLString osSQL("POSTFIELDS=sql="); + /* Do post escaping */ + for(int i=0;pszUnescapedSQL[i] != 0;i++) + { + const int ch = ((unsigned char*)pszUnescapedSQL)[i]; + if (ch != '&' && ch >= 32 && ch < 128) + osSQL += (char)ch; + else + osSQL += CPLSPrintf("%%%02X", ch); + } + +/* -------------------------------------------------------------------- */ +/* Provide the API Key - used to rate limit access (see */ +/* GFT_APIKEY config) */ +/* -------------------------------------------------------------------- */ + osSQL += "&key="; + osSQL += osAPIKey; + +/* -------------------------------------------------------------------- */ +/* Force old style CSV output from calls - maybe we want to */ +/* migrate to JSON output at some point? */ +/* -------------------------------------------------------------------- */ + osSQL += "&alt=csv"; + +/* -------------------------------------------------------------------- */ +/* Collection the header options and execute request. */ +/* -------------------------------------------------------------------- */ + char** papszOptions = CSLAddString(AddHTTPOptions(), osSQL); + CPLHTTPResult * psResult = CPLHTTPFetch( GetAPIURL(), papszOptions); + CSLDestroy(papszOptions); + +/* -------------------------------------------------------------------- */ +/* Check for some error conditions and report. HTML Messages */ +/* are transformed info failure. */ +/* -------------------------------------------------------------------- */ + if (psResult && psResult->pszContentType && + strncmp(psResult->pszContentType, "text/html", 9) == 0) + { + CPLDebug( "GFT", "RunSQL HTML Response:%s", psResult->pabyData ); + CPLError(CE_Failure, CPLE_AppDefined, + "HTML error page returned by server"); + CPLHTTPDestroyResult(psResult); + psResult = NULL; + } + if (psResult && psResult->pszErrBuf != NULL) + { + CPLDebug( "GFT", "RunSQL Error Message:%s", psResult->pszErrBuf ); + } + else if (psResult && psResult->nStatus != 0) + { + CPLDebug( "GFT", "RunSQL Error Status:%d", psResult->nStatus ); + } + + return psResult; +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRGFTDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Special case DELLAYER: command. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand,"DELLAYER:",9) ) + { + const char *pszLayerName = pszSQLCommand + 9; + + while( *pszLayerName == ' ' ) + pszLayerName++; + + DeleteLayer( pszLayerName ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create layer. */ +/* -------------------------------------------------------------------- */ + OGRGFTResultLayer *poLayer = NULL; + + CPLString osSQL = pszSQLCommand; + poLayer = new OGRGFTResultLayer( this, osSQL ); + if (!poLayer->RunSQL()) + { + delete poLayer; + return NULL; + } + + if( poSpatialFilter != NULL ) + poLayer->SetSpatialFilter( poSpatialFilter ); + + return poLayer; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRGFTDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} + +/************************************************************************/ +/* OGRGFTGotoNextLine() */ +/************************************************************************/ + +char* OGRGFTGotoNextLine(char* pszData) +{ + char* pszNextLine = strchr(pszData, '\n'); + if (pszNextLine) + return pszNextLine + 1; + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgftdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgftdriver.cpp new file mode 100644 index 000000000..43b7b4a33 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgftdriver.cpp @@ -0,0 +1,125 @@ +/****************************************************************************** + * $Id: ogrgftdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: GFT Translator + * Purpose: Implements OGRGFTDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gft.h" + +// g++ -g -Wall -fPIC -shared -o ogr_GFT.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/gft ogr/ogrsf_frmts/gft/*.c* -L. -lgdal + +/* http://code.google.com/intl/fr/apis/fusiontables/docs/developers_reference.html */ + +CPL_CVSID("$Id: ogrgftdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +extern "C" void RegisterOGRGFT(); + +/************************************************************************/ +/* ~OGRGFTDriver() */ +/************************************************************************/ + +OGRGFTDriver::~OGRGFTDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRGFTDriver::GetName() + +{ + return "GFT"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRGFTDriver::Open( const char * pszFilename, int bUpdate ) + +{ + if (!EQUALN(pszFilename, "GFT:", 4)) + return FALSE; + + OGRGFTDataSource *poDS = new OGRGFTDataSource(); + + if( !poDS->Open( pszFilename, bUpdate ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRGFTDriver::CreateDataSource( const char * pszName, + CPL_UNUSED char **papszOptions ) +{ + OGRGFTDataSource *poDS = new OGRGFTDataSource(); + + if( !poDS->Open( pszName, TRUE ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGFTDriver::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap, ODrCCreateDataSource)) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRGFT() */ +/************************************************************************/ + +void RegisterOGRGFT() + +{ + OGRSFDriver* poDriver = new OGRGFTDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Google Fusion Tables" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_gft.html" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgftlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgftlayer.cpp new file mode 100644 index 000000000..5338c1ecd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgftlayer.cpp @@ -0,0 +1,668 @@ +/****************************************************************************** + * $Id: ogrgftlayer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: GFT Translator + * Purpose: Implements OGRGFTLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gft.h" +#include "cpl_minixml.h" + +CPL_CVSID("$Id: ogrgftlayer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGRGFTLayer() */ +/************************************************************************/ + +OGRGFTLayer::OGRGFTLayer(OGRGFTDataSource* poDS) + +{ + this->poDS = poDS; + + nNextInSeq = 0; + + poSRS = new OGRSpatialReference(SRS_WKT_WGS84); + + poFeatureDefn = NULL; + + nOffset = 0; + bEOF = FALSE; + + iLatitudeField = iLongitudeField = -1; + iGeometryField = -1; + bHiddenGeometryField = FALSE; + + bFirstTokenIsFID = FALSE; +} + +/************************************************************************/ +/* ~OGRGFTLayer() */ +/************************************************************************/ + +OGRGFTLayer::~OGRGFTLayer() + +{ + if( poSRS != NULL ) + poSRS->Release(); + + if( poFeatureDefn != NULL ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGFTLayer::ResetReading() + +{ + nNextInSeq = 0; + nOffset = 0; + bEOF = FALSE; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRGFTLayer::GetLayerDefn() +{ + CPLAssert(poFeatureDefn); + return poFeatureDefn; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRGFTLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + GetLayerDefn(); + + while(TRUE) + { + if (nNextInSeq < nOffset || + nNextInSeq >= nOffset + (int)aosRows.size()) + { + if (bEOF) + return NULL; + + nOffset += aosRows.size(); + if (!FetchNextRows()) + return NULL; + } + + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* CSVSplitLine() */ +/* */ +/* Tokenize a CSV line into fields in the form of a string */ +/* list. This is used instead of the CPLTokenizeString() */ +/* because it provides correct CSV escaping and quoting */ +/* semantics. */ +/************************************************************************/ + +char **OGRGFTCSVSplitLine( const char *pszString, char chDelimiter ) + +{ + char **papszRetList = NULL; + char *pszToken; + int nTokenMax, nTokenLen; + + pszToken = (char *) CPLCalloc(10,1); + nTokenMax = 10; + + while( pszString != NULL && *pszString != '\0' ) + { + int bInString = FALSE; + + nTokenLen = 0; + + /* Try to find the next delimeter, marking end of token */ + for( ; *pszString != '\0'; pszString++ ) + { + + /* End if this is a delimeter skip it and break. */ + if( !bInString && *pszString == chDelimiter ) + { + pszString++; + break; + } + + if( *pszString == '"' ) + { + if( !bInString || pszString[1] != '"' ) + { + bInString = !bInString; + continue; + } + else /* doubled quotes in string resolve to one quote */ + { + pszString++; + } + } + + if( nTokenLen >= nTokenMax-2 ) + { + nTokenMax = nTokenMax * 2 + 10; + pszToken = (char *) CPLRealloc( pszToken, nTokenMax ); + } + + pszToken[nTokenLen] = *pszString; + nTokenLen++; + } + + pszToken[nTokenLen] = '\0'; + papszRetList = CSLAddString( papszRetList, pszToken ); + + /* If the last token is an empty token, then we have to catch + * it now, otherwise we won't reenter the loop and it will be lost. + */ + if ( *pszString == '\0' && *(pszString-1) == chDelimiter ) + { + papszRetList = CSLAddString( papszRetList, "" ); + } + } + + if( papszRetList == NULL ) + papszRetList = (char **) CPLCalloc(sizeof(char *),1); + + CPLFree( pszToken ); + + return papszRetList; +} +/************************************************************************/ +/* ParseKMLGeometry() */ +/************************************************************************/ + +static void ParseLineString(OGRLineString* poLS, + const char* pszCoordinates) +{ + char** papszTuples = CSLTokenizeString2(pszCoordinates, " ", 0); + for(int iTuple = 0; papszTuples && papszTuples[iTuple]; iTuple++) + { + char** papszTokens = CSLTokenizeString2(papszTuples[iTuple], ",", 0); + if (CSLCount(papszTokens) == 2) + poLS->addPoint(CPLAtof(papszTokens[0]), CPLAtof(papszTokens[1])); + else if (CSLCount(papszTokens) == 3) + poLS->addPoint(CPLAtof(papszTokens[0]), CPLAtof(papszTokens[1]), + CPLAtof(papszTokens[2])); + CSLDestroy(papszTokens); + } + CSLDestroy(papszTuples); +} + +/* Could be moved somewhere else */ + +static OGRGeometry* ParseKMLGeometry(/* const */ CPLXMLNode* psXML) +{ + OGRGeometry* poGeom = NULL; + const char* pszGeomType = psXML->pszValue; + if (strcmp(pszGeomType, "Point") == 0) + { + const char* pszCoordinates = CPLGetXMLValue(psXML, "coordinates", NULL); + if (pszCoordinates) + { + char** papszTokens = CSLTokenizeString2(pszCoordinates, ",", 0); + if (CSLCount(papszTokens) == 2) + poGeom = new OGRPoint(CPLAtof(papszTokens[0]), CPLAtof(papszTokens[1])); + else if (CSLCount(papszTokens) == 3) + poGeom = new OGRPoint(CPLAtof(papszTokens[0]), CPLAtof(papszTokens[1]), + CPLAtof(papszTokens[2])); + CSLDestroy(papszTokens); + } + } + else if (strcmp(pszGeomType, "LineString") == 0) + { + const char* pszCoordinates = CPLGetXMLValue(psXML, "coordinates", NULL); + if (pszCoordinates) + { + OGRLineString* poLS = new OGRLineString(); + ParseLineString(poLS, pszCoordinates); + poGeom = poLS; + } + } + else if (strcmp(pszGeomType, "Polygon") == 0) + { + OGRPolygon* poPoly = NULL; + CPLXMLNode* psOuterBoundary = CPLGetXMLNode(psXML, "outerBoundaryIs"); + if (psOuterBoundary) + { + CPLXMLNode* psLinearRing = CPLGetXMLNode(psOuterBoundary, "LinearRing"); + const char* pszCoordinates = CPLGetXMLValue( + psLinearRing ? psLinearRing : psOuterBoundary, "coordinates", NULL); + if (pszCoordinates) + { + OGRLinearRing* poLS = new OGRLinearRing(); + ParseLineString(poLS, pszCoordinates); + poPoly = new OGRPolygon(); + poPoly->addRingDirectly(poLS); + poGeom = poPoly; + } + + if (poPoly) + { + CPLXMLNode* psIter = psXML->psChild; + while(psIter) + { + if (psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "innerBoundaryIs") == 0) + { + CPLXMLNode* psLinearRing = CPLGetXMLNode(psIter, "LinearRing"); + const char* pszCoordinates = CPLGetXMLValue( + psLinearRing ? psLinearRing : psIter, "coordinates", NULL); + if (pszCoordinates) + { + OGRLinearRing* poLS = new OGRLinearRing(); + ParseLineString(poLS, pszCoordinates); + poPoly->addRingDirectly(poLS); + } + } + psIter = psIter->psNext; + } + } + } + } + else if (strcmp(pszGeomType, "MultiGeometry") == 0) + { + CPLXMLNode* psIter; + OGRwkbGeometryType eType = wkbUnknown; + for(psIter = psXML->psChild; psIter; psIter = psIter->psNext) + { + if (psIter->eType == CXT_Element) + { + OGRwkbGeometryType eNewType = wkbUnknown; + if (strcmp(psIter->pszValue, "Point") == 0) + { + eNewType = wkbPoint; + } + else if (strcmp(psIter->pszValue, "LineString") == 0) + { + eNewType = wkbLineString; + } + else if (strcmp(psIter->pszValue, "Polygon") == 0) + { + eNewType = wkbPolygon; + } + else + break; + if (eType == wkbUnknown) + eType = eNewType; + else if (eType != eNewType) + break; + } + } + OGRGeometryCollection* poColl = NULL; + if (psIter != NULL) + poColl = new OGRGeometryCollection(); + else if (eType == wkbPoint) + poColl = new OGRMultiPoint(); + else if (eType == wkbLineString) + poColl = new OGRMultiLineString(); + else if (eType == wkbPolygon) + poColl = new OGRMultiPolygon(); + else { + CPLAssert(0); + } + + psIter = psXML->psChild; + for(psIter = psXML->psChild; psIter; psIter = psIter->psNext) + { + if (psIter->eType == CXT_Element) + { + OGRGeometry* poSubGeom = ParseKMLGeometry(psIter); + if (poSubGeom) + poColl->addGeometryDirectly(poSubGeom); + } + } + + poGeom = poColl; + } + + return poGeom; +} + +static OGRGeometry* ParseKMLGeometry(const char* pszKML) +{ + CPLXMLNode* psXML = CPLParseXMLString(pszKML); + if (psXML == NULL) + return NULL; + + if (psXML->eType != CXT_Element) + { + CPLDestroyXMLNode(psXML); + return NULL; + } + + OGRGeometry* poGeom = ParseKMLGeometry(psXML); + + CPLDestroyXMLNode(psXML); + return poGeom; +} + + +/************************************************************************/ +/* BuildFeatureFromSQL() */ +/************************************************************************/ + +OGRFeature *OGRGFTLayer::BuildFeatureFromSQL(const char* pszLine) +{ + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + char** papszTokens = OGRGFTCSVSplitLine(pszLine, ','); + int nTokens = CSLCount(papszTokens); + CPLString osFID; + + int nAttrOffset = 0; + int iROWID = -1; + if (bFirstTokenIsFID) + { + osFID = papszTokens[0]; + nAttrOffset = 1; + } + else + { + iROWID = poFeatureDefn->GetFieldIndex("rowid"); + if (iROWID < 0) + iROWID = poFeatureDefn->GetFieldIndex("ROWID"); + } + + int nFieldCount = poFeatureDefn->GetFieldCount(); + if (nTokens == nFieldCount + bHiddenGeometryField + nAttrOffset) + { + for(int i=0;iSetField(i, pszVal); + + if (i == iGeometryField && i != iLatitudeField) + { + if (pszVal[0] == '-' || (pszVal[0] >= '0' && pszVal[0] <= '9')) + { + char** papszLatLon = CSLTokenizeString2(pszVal, " ,", 0); + if (CSLCount(papszLatLon) == 2 && + CPLGetValueType(papszLatLon[0]) != CPL_VALUE_STRING && + CPLGetValueType(papszLatLon[1]) != CPL_VALUE_STRING) + { + OGRPoint* poPoint = new OGRPoint(CPLAtof( papszLatLon[1]), + CPLAtof( papszLatLon[0])); + poPoint->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poPoint); + } + CSLDestroy(papszLatLon); + } + else if (strstr(pszVal, "") || + strstr(pszVal, "") || + strstr(pszVal, "")) + { + OGRGeometry* poGeom = ParseKMLGeometry(pszVal); + if (poGeom) + { + poGeom->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poGeom); + } + } + } + else if (i == iROWID) + { + osFID = pszVal; + } + } + } + + if (iLatitudeField >= 0 && iLongitudeField >= 0) + { + const char* pszLat = papszTokens[iLatitudeField+nAttrOffset]; + const char* pszLong = papszTokens[iLongitudeField+nAttrOffset]; + if (pszLat[0] != 0 && pszLong[0] != 0 && + CPLGetValueType(pszLat) != CPL_VALUE_STRING && + CPLGetValueType(pszLong) != CPL_VALUE_STRING) + { + OGRPoint* poPoint = new OGRPoint(CPLAtof(pszLong), CPLAtof(pszLat)); + poPoint->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poPoint); + } + } + } + else + { + CPLDebug("GFT", "Only %d columns for feature %s", nTokens, osFID.c_str()); + } + CSLDestroy(papszTokens); + + int nFID = atoi(osFID); + if (strcmp(CPLSPrintf("%d", nFID), osFID.c_str()) == 0) + poFeature->SetFID(nFID); + else + poFeature->SetFID(nNextInSeq); + + return poFeature; +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRGFTLayer::GetNextRawFeature() +{ + if (nNextInSeq < nOffset || + nNextInSeq - nOffset >= (int)aosRows.size()) + return NULL; + + OGRFeature* poFeature = BuildFeatureFromSQL(aosRows[nNextInSeq - nOffset]); + + nNextInSeq ++; + + return poFeature; +} + +/************************************************************************/ +/* SetNextByIndex() */ +/************************************************************************/ + +OGRErr OGRGFTLayer::SetNextByIndex( GIntBig nIndex ) +{ + if (nIndex < 0 || nIndex >= INT_MAX ) + return OGRERR_FAILURE; + bEOF = FALSE; + nNextInSeq = (int)nIndex; + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGFTLayer::TestCapability( const char * pszCap ) + +{ + if ( EQUAL(pszCap, OLCStringsAsUTF8) ) + return TRUE; + else if ( EQUAL(pszCap, OLCFastSetNextByIndex) ) + return TRUE; + return FALSE; +} + +/************************************************************************/ +/* ParseCSVResponse() */ +/************************************************************************/ + +int OGRGFTLayer::ParseCSVResponse(char* pszLine, + std::vector& aosRes) +{ + while(pszLine != NULL && *pszLine != 0) + { + char* pszNextLine = OGRGFTGotoNextLine(pszLine); + if (pszNextLine) + pszNextLine[-1] = 0; + + int nDoubleQuotes = 0; + char* pszIter = pszLine; + while(*pszIter) + { + if (*pszIter == '"') + { + if (pszIter[1] != '"') + nDoubleQuotes ++; + else + pszIter ++; + } + pszIter ++; + } + + if ((nDoubleQuotes % 2) == 0) + aosRes.push_back(pszLine); + else + { + CPLString osLine(pszLine); + + pszLine = pszNextLine; + while(pszLine != NULL && *pszLine != 0) + { + pszNextLine = OGRGFTGotoNextLine(pszLine); + if (pszNextLine) + pszNextLine[-1] = 0; + + osLine += "\n"; + osLine += pszLine; + + pszIter = pszLine; + while(*pszIter) + { + if (*pszIter == '"') + { + if (pszIter[1] != '"') + nDoubleQuotes ++; + else + pszIter ++; + } + pszIter ++; + } + + if ((nDoubleQuotes % 2) == 0) + { + break; + } + + pszLine = pszNextLine; + } + + aosRes.push_back(osLine); + } + + pszLine = pszNextLine; + } + + return TRUE; +} + +/************************************************************************/ +/* PatchSQL() */ +/************************************************************************/ + +CPLString OGRGFTLayer::PatchSQL(const char* pszSQL) +{ + CPLString osSQL; + + while(*pszSQL) + { + if (EQUALN(pszSQL, "COUNT(", 5) && strchr(pszSQL, ')')) + { + const char* pszNext = strchr(pszSQL, ')'); + osSQL += "COUNT()"; + pszSQL = pszNext + 1; + } + else if ((*pszSQL == '<' && pszSQL[1] == '>') || + (*pszSQL == '!' && pszSQL[1] == '=')) + { + osSQL += " NOT EQUAL TO "; + pszSQL += 2; + } + else + { + osSQL += *pszSQL; + pszSQL ++; + } + } + return osSQL; +} + +/************************************************************************/ +/* LaunderColName() */ +/************************************************************************/ + +CPLString OGRGFTLayer::LaunderColName(const char* pszColName) +{ + CPLString osLaunderedColName; + + for(int i=0;pszColName[i];i++) + { + if (pszColName[i] == '\n') + osLaunderedColName += "\\n"; + else + osLaunderedColName += pszColName[i]; + } + return osLaunderedColName; +} + +/************************************************************************/ +/* SetGeomFieldName() */ +/************************************************************************/ + +void OGRGFTLayer::SetGeomFieldName() +{ + if (iGeometryField >= 0 && poFeatureDefn->GetGeomFieldCount() > 0) + { + const char* pszGeomColName; + if (iGeometryField == poFeatureDefn->GetFieldCount()) + { + CPLAssert(bHiddenGeometryField); + pszGeomColName = GetDefaultGeometryColumnName(); + } + else + pszGeomColName = poFeatureDefn->GetFieldDefn(iGeometryField)->GetNameRef(); + poFeatureDefn->GetGeomFieldDefn(0)->SetName(pszGeomColName); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgftresultlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgftresultlayer.cpp new file mode 100644 index 000000000..37b2929a8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgftresultlayer.cpp @@ -0,0 +1,295 @@ +/****************************************************************************** + * $Id: ogrgftresultlayer.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: GFT Translator + * Purpose: Implements OGRGFTResultLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gft.h" + +CPL_CVSID("$Id: ogrgftresultlayer.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* OGRGFTResultLayer() */ +/************************************************************************/ + +OGRGFTResultLayer::OGRGFTResultLayer(OGRGFTDataSource* poDS, + const char* pszSQL) : OGRGFTLayer(poDS) + +{ + osSQL = PatchSQL(pszSQL); + + bGotAllRows = FALSE; + + poFeatureDefn = new OGRFeatureDefn( "result" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbUnknown ); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + SetDescription( poFeatureDefn->GetName() ); +} + +/************************************************************************/ +/* ~OGRGFTResultLayer() */ +/************************************************************************/ + +OGRGFTResultLayer::~OGRGFTResultLayer() + +{ +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGFTResultLayer::ResetReading() + +{ + nNextInSeq = 0; + nOffset = 0; + if (!bGotAllRows) + { + aosRows.resize(0); + bEOF = FALSE; + } +} + +/************************************************************************/ +/* FetchNextRows() */ +/************************************************************************/ + +int OGRGFTResultLayer::FetchNextRows() +{ + if (!EQUALN(osSQL.c_str(), "SELECT", 6)) + return FALSE; + + aosRows.resize(0); + + CPLString osChangedSQL(osSQL); + if (osSQL.ifind(" OFFSET ") == std::string::npos && + osSQL.ifind(" LIMIT ") == std::string::npos) + { + osChangedSQL += CPLSPrintf(" OFFSET %d LIMIT %d", + nOffset, GetFeaturesToFetch()); + } + + CPLPushErrorHandler(CPLQuietErrorHandler); + CPLHTTPResult * psResult = poDS->RunSQL(osChangedSQL); + CPLPopErrorHandler(); + + if (psResult == NULL) + { + bEOF = TRUE; + return FALSE; + } + + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || + psResult->pszErrBuf != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "RunSQL() failed"); + CPLHTTPDestroyResult(psResult); + bEOF = TRUE; + return FALSE; + } + + pszLine = OGRGFTGotoNextLine(pszLine); + if (pszLine == NULL) + { + CPLHTTPDestroyResult(psResult); + bEOF = TRUE; + return FALSE; + } + + ParseCSVResponse(pszLine, aosRows); + + CPLHTTPDestroyResult(psResult); + + bEOF = (int)aosRows.size() < GetFeaturesToFetch(); + + return TRUE; +} + +/************************************************************************/ +/* OGRGFTExtractTableID() */ +/************************************************************************/ + +static CPLString OGRGFTExtractTableID(const char* pszTableID, + CPLString& osReminder) +{ + CPLString osTableId(pszTableID); + if (osTableId.size() > 1 && + (osTableId[0] == '"' || osTableId[0] == '\'')) + { + char chFirstChar = osTableId[0]; + osTableId.erase(0, 1); + for(int i=0;i<(int)osTableId.size();i++) + { + if (osTableId[i] == chFirstChar) + { + osReminder = osTableId.substr(i+1); + osTableId.resize(i); + break; + } + } + } + else + { + for(int i=0;i<(int)osTableId.size();i++) + { + if (osTableId[i] == ' ') + { + osReminder = osTableId.substr(i); + osTableId.resize(i); + break; + } + } + } + return osTableId; +} + +/************************************************************************/ +/* RunSQL() */ +/************************************************************************/ + +int OGRGFTResultLayer::RunSQL() +{ + CPLString osChangedSQL(osSQL); + int bHasSetLimit = FALSE; + OGRGFTTableLayer* poTableLayer = NULL; + OGRFeatureDefn* poTableDefn = NULL; + CPLString osTableId; + if (EQUALN(osSQL.c_str(), "SELECT", 6)) + { + size_t nPosFROM = osSQL.ifind(" FROM "); + if (nPosFROM == std::string::npos) + { + CPLError(CE_Failure, CPLE_AppDefined, "RunSQL() failed. Missing FROM in SELECT"); + return FALSE; + } + CPLString osReminder; + nPosFROM += 6; + osTableId = OGRGFTExtractTableID(osSQL.c_str() + nPosFROM, osReminder); + + poTableLayer = (OGRGFTTableLayer*) poDS->GetLayerByName(osTableId); + if (poTableLayer != NULL) + poTableDefn = poTableLayer->GetLayerDefn(); + + if (poTableLayer != NULL && + poTableLayer->GetTableId().size() && + !EQUAL(osTableId, poTableLayer->GetTableId())) + { + osChangedSQL = osSQL; + osChangedSQL.resize(nPosFROM); + osChangedSQL += poTableLayer->GetTableId(); + osChangedSQL += osReminder; + osSQL = osChangedSQL; + CPLDebug("GFT", "Patching table name (%s) to table id (%s)", + osTableId.c_str(), poTableLayer->GetTableId().c_str()); + } + + int nFeaturesToFetch = GetFeaturesToFetch(); + if (osSQL.ifind(" OFFSET ") == std::string::npos && + osSQL.ifind(" LIMIT ") == std::string::npos && + nFeaturesToFetch > 0) + { + osChangedSQL += CPLSPrintf(" LIMIT %d", nFeaturesToFetch); + bHasSetLimit = TRUE; + } + } + else + { + bGotAllRows = bEOF = TRUE; + poFeatureDefn->SetGeomType( wkbNone ); + } + + CPLHTTPResult * psResult = poDS->RunSQL(osChangedSQL); + + if (psResult == NULL) + return FALSE; + + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || + psResult->pszErrBuf != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "RunSQL() failed"); + CPLHTTPDestroyResult(psResult); + return FALSE; + } + + if (EQUALN(osSQL.c_str(), "SELECT", 6) || + EQUAL(osSQL.c_str(), "SHOW TABLES") || + EQUALN(osSQL.c_str(), "DESCRIBE", 8)) + { + ParseCSVResponse(pszLine, aosRows); + if (aosRows.size() > 0) + { + char** papszTokens = OGRGFTCSVSplitLine(aosRows[0], ','); + for(int i=0;papszTokens && papszTokens[i];i++) + { + CPLString osLaunderedColName(LaunderColName(papszTokens[i])); + int iIndex = (poTableDefn) ? poTableDefn->GetFieldIndex(osLaunderedColName) : -1; + if (iIndex >= 0) + { + poFeatureDefn->AddFieldDefn(poTableDefn->GetFieldDefn(iIndex)); + if (iIndex == poTableLayer->GetGeometryFieldIndex()) + iGeometryField = i; + if (iIndex == poTableLayer->GetLatitudeFieldIndex()) + iLatitudeField = i; + if (iIndex == poTableLayer->GetLongitudeFieldIndex()) + iLongitudeField = i; + } + else + { + OGRFieldType eType = OFTString; + if (EQUAL(osLaunderedColName, "COUNT()")) + eType = OFTInteger; + OGRFieldDefn oFieldDefn(osLaunderedColName, eType); + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + } + CSLDestroy(papszTokens); + + aosRows.erase(aosRows.begin()); + } + + if (iLatitudeField >= 0 && iLongitudeField >= 0) + { + iGeometryField = iLatitudeField; + poFeatureDefn->SetGeomType( wkbPoint ); + } + + if (bHasSetLimit) + bGotAllRows = bEOF = (int)aosRows.size() < GetFeaturesToFetch(); + else + bGotAllRows = bEOF = TRUE; + } + + SetGeomFieldName(); + + CPLHTTPDestroyResult(psResult); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgfttablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgfttablelayer.cpp new file mode 100644 index 000000000..054b8e97e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gft/ogrgfttablelayer.cpp @@ -0,0 +1,1316 @@ +/****************************************************************************** + * $Id: ogrgfttablelayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: GFT Translator + * Purpose: Implements OGRGFTTableLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gft.h" + +CPL_CVSID("$Id: ogrgfttablelayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRGFTTableLayer() */ +/************************************************************************/ + +OGRGFTTableLayer::OGRGFTTableLayer(OGRGFTDataSource* poDS, + const char* pszTableName, + const char* pszTableId, + const char* pszGeomColumnName) : OGRGFTLayer(poDS) + +{ + osTableName = pszTableName; + osTableId = pszTableId; + osGeomColumnName = pszGeomColumnName ? pszGeomColumnName : ""; + + bHasTriedCreateTable = FALSE; + bInTransaction = FALSE; + nFeaturesInTransaction = 0; + + bFirstTokenIsFID = TRUE; + eGTypeForCreation = wkbUnknown; + + SetDescription( osTableName ); + + if (osTableId.size() == 0) + { + poFeatureDefn = new OGRFeatureDefn( osTableName ); + poFeatureDefn->Reference(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + poFeatureDefn->GetGeomFieldDefn(0)->SetName(GetDefaultGeometryColumnName()); + } +} + +/************************************************************************/ +/* ~OGRGFTTableLayer() */ +/************************************************************************/ + +OGRGFTTableLayer::~OGRGFTTableLayer() + +{ + CreateTableIfNecessary(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGFTTableLayer::ResetReading() + +{ + OGRGFTLayer::ResetReading(); + aosRows.resize(0); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGFTTableLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) + || EQUAL(pszCap,OLCDeleteFeature) ) + return poDS->IsReadWrite(); + + else if( EQUAL(pszCap,OLCCreateField) ) + return poDS->IsReadWrite(); + + else if( EQUAL(pszCap, OLCTransactions) ) + return poDS->IsReadWrite(); + + return OGRGFTLayer::TestCapability(pszCap); +} + +/************************************************************************/ +/* FetchDescribe() */ +/************************************************************************/ + +int OGRGFTTableLayer::FetchDescribe() +{ + poFeatureDefn = new OGRFeatureDefn( osTableName ); + poFeatureDefn->Reference(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + const CPLString& osAuth = poDS->GetAccessToken(); + std::vector aosHeaderAndFirstDataLine; + if (osAuth.size()) + { + CPLString osSQL("DESCRIBE "); + osSQL += osTableId; + CPLHTTPResult * psResult = poDS->RunSQL(osSQL); + + if (psResult == NULL) + return FALSE; + + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || + psResult->pszErrBuf != NULL || + strncmp(pszLine, "column id,name,type", + strlen("column id,name,type")) != 0) + { + CPLHTTPDestroyResult(psResult); + return FALSE; + } + + pszLine = OGRGFTGotoNextLine(pszLine); + + std::vector aosLines; + ParseCSVResponse(pszLine, aosLines); + for(int i=0;i<(int)aosLines.size();i++) + { + char** papszTokens = OGRGFTCSVSplitLine(aosLines[i], ','); + if (CSLCount(papszTokens) == 3) + { + aosColumnInternalName.push_back(papszTokens[0]); + + //CPLDebug("GFT", "%s %s %s", papszTokens[0], papszTokens[1], papszTokens[2]); + OGRFieldType eType = OFTString; + if (EQUAL(papszTokens[2], "number")) + eType = OFTReal; + else if (EQUAL(papszTokens[2], "datetime")) + eType = OFTDateTime; + + if (EQUAL(papszTokens[2], "location") && osGeomColumnName.size() == 0) + { + if (iGeometryField < 0) + iGeometryField = poFeatureDefn->GetFieldCount(); + else + CPLDebug("GFT", "Multiple geometry fields detected. " + "Only first encountered one is handled"); + } + + CPLString osLaunderedColName(LaunderColName(papszTokens[1])); + OGRFieldDefn oFieldDefn(osLaunderedColName, eType); + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + CSLDestroy(papszTokens); + } + + CPLHTTPDestroyResult(psResult); + } + else + { + /* http://code.google.com/intl/fr/apis/fusiontables/docs/developers_guide.html#Exploring states */ + /* that DESCRIBE should work on public tables without authentication, but it is not true... */ + CPLString osSQL("SELECT * FROM "); + osSQL += osTableId; + osSQL += " OFFSET 0 LIMIT 1"; + + CPLHTTPResult * psResult = poDS->RunSQL(osSQL); + + if (psResult == NULL) + return FALSE; + + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || psResult->pszErrBuf != NULL) + { + CPLHTTPDestroyResult(psResult); + return FALSE; + } + + ParseCSVResponse(pszLine, aosHeaderAndFirstDataLine); + if (aosHeaderAndFirstDataLine.size() > 0) + { + char** papszTokens = OGRGFTCSVSplitLine(aosHeaderAndFirstDataLine[0], ','); + for(int i=0;papszTokens && papszTokens[i];i++) + { + CPLString osLaunderedColName(LaunderColName(papszTokens[i])); + OGRFieldDefn oFieldDefn(osLaunderedColName, OFTString); + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + CSLDestroy(papszTokens); + } + + CPLHTTPDestroyResult(psResult); + } + + if (osGeomColumnName.size() > 0) + { + iGeometryField = poFeatureDefn->GetFieldIndex(osGeomColumnName); + if (iGeometryField < 0) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Cannot find column called %s", osGeomColumnName.c_str()); + } + } + + for(int i=0;iGetFieldCount();i++) + { + const char* pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + if (EQUAL(pszName, "latitude") || EQUAL(pszName, "lat") || + EQUAL(pszName, "latdec")) + iLatitudeField = i; + else if (EQUAL(pszName, "longitude") || EQUAL(pszName, "lon") || + EQUAL(pszName, "londec") || EQUAL(pszName, "long")) + iLongitudeField = i; + } + + if (iLatitudeField >= 0 && iLongitudeField >= 0) + { + if (iGeometryField < 0) + iGeometryField = iLatitudeField; + poFeatureDefn->GetFieldDefn(iLatitudeField)->SetType(OFTReal); + poFeatureDefn->GetFieldDefn(iLongitudeField)->SetType(OFTReal); + poFeatureDefn->SetGeomType( wkbPoint ); + } + else if (iGeometryField < 0 && osGeomColumnName.size() == 0) + { + iLatitudeField = iLongitudeField = -1; + + /* In the unauthentified case, we try to parse the first record to */ + /* autodetect the geometry field */ + OGRwkbGeometryType eType = wkbUnknown; + if (aosHeaderAndFirstDataLine.size() == 2) + { + char** papszTokens = OGRGFTCSVSplitLine(aosHeaderAndFirstDataLine[1], ','); + if (CSLCount(papszTokens) == poFeatureDefn->GetFieldCount()) + { + for(int i=0;iGetFieldCount();i++) + { + const char* pszVal = papszTokens[i]; + if (pszVal != NULL && + (strncmp(pszVal, "", 7) == 0 || + strncmp(pszVal, "", 12) == 0 || + strncmp(pszVal, "", 9) == 0 || + strncmp(pszVal, "", 15) == 0)) + { + if (iGeometryField < 0) + { + iGeometryField = i; + } + else + { + CPLDebug("GFT", "Multiple geometry fields detected. " + "Only first encountered one is handled"); + } + } + else if (pszVal) + { + /* http://www.google.com/fusiontables/DataSource?dsrcid=423292 */ + char** papszTokens2 = CSLTokenizeString2(pszVal, " ,", 0); + if (CSLCount(papszTokens2) == 2 && + CPLGetValueType(papszTokens2[0]) == CPL_VALUE_REAL && + CPLGetValueType(papszTokens2[1]) == CPL_VALUE_REAL && + fabs(CPLAtof(papszTokens2[0])) <= 90 && + fabs(CPLAtof(papszTokens2[1])) <= 180 ) + { + if (iGeometryField < 0) + { + iGeometryField = i; + eType = wkbPoint; + } + else + { + CPLDebug("GFT", "Multiple geometry fields detected. " + "Only first encountered one is handled"); + } + } + CSLDestroy(papszTokens2); + } + } + } + CSLDestroy(papszTokens); + } + + if (iGeometryField < 0) + poFeatureDefn->SetGeomType( wkbNone ); + else + poFeatureDefn->SetGeomType( eType ); + } + + SetGeomFieldName(); + + return TRUE; +} + +/************************************************************************/ +/* EscapeAndQuote() */ +/************************************************************************/ + +static CPLString EscapeAndQuote(const char* pszStr) +{ + char ch; + CPLString osRes("'"); + while((ch = *pszStr) != 0) + { + if (ch == '\'') + osRes += "\\\'"; + else + osRes += ch; + pszStr ++; + } + osRes += "'"; + return osRes; +} + +/************************************************************************/ +/* FetchNextRows() */ +/************************************************************************/ + +int OGRGFTTableLayer::FetchNextRows() +{ + aosRows.resize(0); + + CPLString osSQL("SELECT ROWID"); + for(int i=0;iGetFieldCount();i++) + { + osSQL += ","; + + if (i < (int)aosColumnInternalName.size()) + osSQL += aosColumnInternalName[i]; + else + { + const char* pszFieldName = + poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + osSQL += EscapeAndQuote(pszFieldName); + } + } + if (bHiddenGeometryField) + { + osSQL += ","; + osSQL += EscapeAndQuote(GetGeometryColumn()); + } + osSQL += " FROM "; + osSQL += osTableId; + if (osWHERE.size()) + { + osSQL += " "; + osSQL += osWHERE; + } + + int nFeaturesToFetch = GetFeaturesToFetch(); + if (nFeaturesToFetch > 0) + osSQL += CPLSPrintf(" OFFSET %d LIMIT %d", nOffset, nFeaturesToFetch); + + CPLPushErrorHandler(CPLQuietErrorHandler); + CPLHTTPResult * psResult = poDS->RunSQL(osSQL); + CPLPopErrorHandler(); + + if (psResult == NULL) + { + bEOF = TRUE; + return FALSE; + } + + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || psResult->pszErrBuf != NULL) + { + CPLDebug("GFT", "Error : %s", pszLine ? pszLine : psResult->pszErrBuf); + CPLHTTPDestroyResult(psResult); + bEOF = TRUE; + return FALSE; + } + + ParseCSVResponse(pszLine, aosRows); + + if (aosRows.size() > 0) + aosRows.erase(aosRows.begin()); + + if (nFeaturesToFetch > 0) + bEOF = (int)aosRows.size() < GetFeaturesToFetch(); + else + bEOF = TRUE; + + CPLHTTPDestroyResult(psResult); + + return TRUE; +} +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature * OGRGFTTableLayer::GetFeature( GIntBig nFID ) +{ + GetLayerDefn(); + + CPLString osSQL("SELECT ROWID"); + for(int i=0;iGetFieldCount();i++) + { + osSQL += ","; + + const char* pszFieldName = + poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + osSQL += EscapeAndQuote(pszFieldName); + } + if (bHiddenGeometryField) + { + osSQL += ","; + osSQL += EscapeAndQuote(GetGeometryColumn()); + } + osSQL += " FROM "; + osSQL += osTableId; + osSQL += CPLSPrintf(" WHERE ROWID='" CPL_FRMT_GIB "'", nFID); + + CPLPushErrorHandler(CPLQuietErrorHandler); + CPLHTTPResult * psResult = poDS->RunSQL(osSQL); + CPLPopErrorHandler(); + + if (psResult == NULL) + return NULL; + + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || psResult->pszErrBuf != NULL) + { + CPLHTTPDestroyResult(psResult); + return NULL; + } + + /* skip header line */ + pszLine = OGRGFTGotoNextLine(pszLine); + if (pszLine == NULL || pszLine[0] == 0) + { + CPLHTTPDestroyResult(psResult); + return NULL; + } + + int nLen = (int)strlen(pszLine); + if (nLen > 0 && pszLine[nLen-1] == '\n') + pszLine[nLen-1] = '\0'; + + OGRFeature* poFeature = BuildFeatureFromSQL(pszLine); + + CPLHTTPDestroyResult(psResult); + + return poFeature; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRGFTTableLayer::GetLayerDefn() +{ + if (poFeatureDefn == NULL) + { + if (osTableId.size() == 0) + return NULL; + FetchDescribe(); + } + + return poFeatureDefn; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRGFTTableLayer::GetFeatureCount(CPL_UNUSED int bForce) +{ + GetLayerDefn(); + + CPLString osSQL("SELECT COUNT() FROM "); + osSQL += osTableId; + if (osWHERE.size()) + { + osSQL += " "; + osSQL += osWHERE; + } + + CPLHTTPResult * psResult = poDS->RunSQL(osSQL); + + if (psResult == NULL) + return 0; + + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || + strncmp(pszLine, "count()", 7) != 0 || + psResult->pszErrBuf != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "GetFeatureCount() failed"); + CPLHTTPDestroyResult(psResult); + return 0; + } + + pszLine = OGRGFTGotoNextLine(pszLine); + if (pszLine == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "GetFeatureCount() failed"); + CPLHTTPDestroyResult(psResult); + return 0; + } + + char* pszNextLine = OGRGFTGotoNextLine(pszLine); + if (pszNextLine) + pszNextLine[-1] = 0; + + int nFeatureCount = atoi(pszLine); + + CPLHTTPDestroyResult(psResult); + + return nFeatureCount; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRGFTTableLayer::CreateField( OGRFieldDefn *poField, + CPL_UNUSED int bApproxOK ) +{ + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + if (osTableId.size() != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot add field to already created table"); + return OGRERR_FAILURE; + } + + if (poDS->GetAccessToken().size() == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in unauthenticated mode"); + return OGRERR_FAILURE; + } + + poFeatureDefn->AddFieldDefn(poField); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateTableIfNecessary() */ +/************************************************************************/ + +void OGRGFTTableLayer::CreateTableIfNecessary() +{ + if (bHasTriedCreateTable || osTableId.size() != 0) + return; + + bHasTriedCreateTable = TRUE; + + CPLString osSQL("CREATE TABLE '"); + osSQL += osTableName; + osSQL += "' ("; + + int i; + + /* If there are longitude and latitude fields, use the latitude */ + /* field as the LOCATION field */ + for(i=0;iGetFieldCount();i++) + { + const char* pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + if (EQUAL(pszName, "latitude") || EQUAL(pszName, "lat") || + EQUAL(pszName, "latdec")) + iLatitudeField = i; + else if (EQUAL(pszName, "longitude") || EQUAL(pszName, "lon") || + EQUAL(pszName, "londec") || EQUAL(pszName, "long")) + iLongitudeField = i; + } + + if (iLatitudeField >= 0 && iLongitudeField >= 0) + { + iGeometryField = iLatitudeField; + poFeatureDefn->SetGeomType( wkbPoint ); + } + /* If no longitude/latitude field exist, let's look at a column */ + /* named 'geometry' and use it as the LOCATION column if the layer */ + /* hasn't been created with a none geometry type */ + else if (iGeometryField < 0 && eGTypeForCreation != wkbNone) + { + iGeometryField = poFeatureDefn->GetFieldIndex(GetDefaultGeometryColumnName()); + poFeatureDefn->SetGeomType( eGTypeForCreation ); + } + /* The user doesn't want geometries, so don't create one */ + else if (eGTypeForCreation == wkbNone) + { + poFeatureDefn->SetGeomType( eGTypeForCreation ); + } + + for(i=0;iGetFieldCount();i++) + { + if (i > 0) + osSQL += ", "; + + const char* pszFieldName = + poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + osSQL += EscapeAndQuote(pszFieldName); + osSQL += ": "; + + if (iGeometryField == i) + { + osSQL += "LOCATION"; + } + else + { + switch(poFeatureDefn->GetFieldDefn(i)->GetType()) + { + case OFTInteger: + case OFTReal: + osSQL += "NUMBER"; + break; + default: + osSQL += "STRING"; + } + } + } + + /* If there's not yet a geometry field and the user didn't forbid */ + /* the creation of one, then let's add it to the CREATE TABLE, but */ + /* DO NOT add it to the feature defn as a feature might already have */ + /* been created with it, so it is not safe to alter it at that point. */ + /* So we set the bHiddenGeometryField flag to be able to fetch/set this */ + /* column but not try to get/set a related feature field */ + if (iGeometryField < 0 && eGTypeForCreation != wkbNone) + { + if (i > 0) + osSQL += ", "; + osSQL += EscapeAndQuote(GetDefaultGeometryColumnName()); + osSQL += ": LOCATION"; + + iGeometryField = poFeatureDefn->GetFieldCount(); + bHiddenGeometryField = TRUE; + } + osSQL += ")"; + + CPLHTTPResult * psResult = poDS->RunSQL(osSQL); + if (psResult == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Table creation failed"); + return; + } + + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || + strncmp(pszLine, "tableid", 7) != 0 || + psResult->pszErrBuf != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Table creation failed"); + CPLHTTPDestroyResult(psResult); + return; + } + + pszLine = OGRGFTGotoNextLine(pszLine); + if (pszLine == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Table creation failed"); + CPLHTTPDestroyResult(psResult); + return; + } + + char* pszNextLine = OGRGFTGotoNextLine(pszLine); + if (pszNextLine) + pszNextLine[-1] = 0; + + osTableId = pszLine; + CPLDebug("GFT", "Table %s --> id = %s", osTableName.c_str(), osTableId.c_str()); + + CPLHTTPDestroyResult(psResult); +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRGFTTableLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + if (osTableId.size() == 0) + { + CreateTableIfNecessary(); + if (osTableId.size() == 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot add feature to non-created table"); + return OGRERR_FAILURE; + } + } + + if (poDS->GetAccessToken().size() == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in unauthenticated mode"); + return OGRERR_FAILURE; + } + + CPLString osCommand; + + osCommand += "INSERT INTO "; + osCommand += osTableId; + osCommand += " ("; + + int iField; + int nFieldCount = poFeatureDefn->GetFieldCount(); + for(iField = 0; iField < nFieldCount; iField++) + { + if (iField > 0) + osCommand += ", "; + + const char* pszFieldName = + poFeatureDefn->GetFieldDefn(iField)->GetNameRef(); + osCommand += EscapeAndQuote(pszFieldName); + } + if (bHiddenGeometryField) + { + if (iField > 0) + osCommand += ", "; + osCommand += EscapeAndQuote(GetGeometryColumn()); + } + osCommand += ") VALUES ("; + for(iField = 0; iField < nFieldCount + bHiddenGeometryField; iField++) + { + if (iField > 0) + osCommand += ", "; + + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + /* If there's a geometry, let's use it in priority over the textual */ + /* content of the field. */ + if (iGeometryField != iLatitudeField && iField == iGeometryField && + (iField == nFieldCount || poGeom != NULL || !poFeature->IsFieldSet( iField ))) + { + if (poGeom == NULL) + osCommand += "''"; + else + { + char* pszKML; + if (poGeom->getSpatialReference() != NULL && + !poGeom->getSpatialReference()->IsSame(poSRS)) + { + OGRGeometry* poGeom4326 = poGeom->clone(); + poGeom4326->transformTo(poSRS); + pszKML = poGeom4326->exportToKML(); + delete poGeom4326; + } + else + { + pszKML = poGeom->exportToKML(); + } + osCommand += "'"; + osCommand += pszKML; + osCommand += "'"; + CPLFree(pszKML); + } + continue; + } + + if( !poFeature->IsFieldSet( iField ) ) + { + osCommand += "''"; + } + else + { + OGRFieldType eType = poFeatureDefn->GetFieldDefn(iField)->GetType(); + if (eType != OFTInteger && eType != OFTReal) + { + CPLString osTmp; + const char* pszVal = poFeature->GetFieldAsString(iField); + + if (!CPLIsUTF8(pszVal, -1)) + { + static int bFirstTime = TRUE; + if (bFirstTime) + { + bFirstTime = FALSE; + CPLError(CE_Warning, CPLE_AppDefined, + "%s is not a valid UTF-8 string. Forcing it to ASCII.\n" + "This warning won't be issued anymore", pszVal); + } + else + { + CPLDebug("OGR", "%s is not a valid UTF-8 string. Forcing it to ASCII", + pszVal); + } + char* pszEscaped = CPLForceToASCII(pszVal, -1, '?'); + osTmp = pszEscaped; + CPLFree(pszEscaped); + pszVal = osTmp.c_str(); + } + + osCommand += EscapeAndQuote(pszVal); + } + else + osCommand += poFeature->GetFieldAsString(iField); + } + } + + osCommand += ")"; + + //CPLDebug("GFT", "%s", osCommand.c_str()); + + if (bInTransaction) + { + nFeaturesInTransaction ++; + if (nFeaturesInTransaction > 1) + osTransaction += "; "; + osTransaction += osCommand; + return OGRERR_NONE; + } + + CPLHTTPResult * psResult = poDS->RunSQL(osCommand); + if (psResult == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Feature creation failed"); + return OGRERR_FAILURE; + } + + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || + strncmp(pszLine, "rowid", 5) != 0 || + psResult->pszErrBuf != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Feature creation failed"); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + pszLine = OGRGFTGotoNextLine(pszLine); + if (pszLine == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Feature creation failed"); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + char* pszNextLine = OGRGFTGotoNextLine(pszLine); + if (pszNextLine) + pszNextLine[-1] = 0; + + CPLDebug("GFT", "Feature id = %s", pszLine); + + int nFID = atoi(pszLine); + if (strcmp(CPLSPrintf("%d", nFID), pszLine) == 0) + poFeature->SetFID(nFID); + + CPLHTTPDestroyResult(psResult); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRGFTTableLayer::ISetFeature( OGRFeature *poFeature ) +{ + GetLayerDefn(); + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + if (osTableId.size() == 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot set feature to non-created table"); + return OGRERR_FAILURE; + } + + if (poDS->GetAccessToken().size() == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in unauthenticated mode"); + return OGRERR_FAILURE; + } + + if (poFeature->GetFID() == OGRNullFID) + { + CPLError( CE_Failure, CPLE_AppDefined, + "FID required on features given to SetFeature()." ); + return OGRERR_FAILURE; + } + + CPLString osCommand; + + osCommand += "UPDATE "; + osCommand += osTableId; + osCommand += " SET "; + + int iField; + int nFieldCount = poFeatureDefn->GetFieldCount(); + for(iField = 0; iField < nFieldCount + bHiddenGeometryField; iField++) + { + if (iField > 0) + osCommand += ", "; + + if (iField == nFieldCount) + { + osCommand += EscapeAndQuote(GetGeometryColumn()); + } + else + { + const char* pszFieldName = + poFeatureDefn->GetFieldDefn(iField)->GetNameRef(); + osCommand += EscapeAndQuote(pszFieldName); + } + + osCommand += " = "; + + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if (iGeometryField != iLatitudeField && iField == iGeometryField && + (iField == nFieldCount || poGeom != NULL || !poFeature->IsFieldSet( iField ))) + { + if (poGeom == NULL) + osCommand += "''"; + else + { + char* pszKML; + if (poGeom->getSpatialReference() != NULL && + !poGeom->getSpatialReference()->IsSame(poSRS)) + { + OGRGeometry* poGeom4326 = poGeom->clone(); + poGeom4326->transformTo(poSRS); + pszKML = poGeom4326->exportToKML(); + delete poGeom4326; + } + else + { + pszKML = poGeom->exportToKML(); + } + osCommand += "'"; + osCommand += pszKML; + osCommand += "'"; + CPLFree(pszKML); + } + continue; + } + + if( !poFeature->IsFieldSet( iField ) ) + { + osCommand += "''"; + } + else + { + OGRFieldType eType = poFeatureDefn->GetFieldDefn(iField)->GetType(); + if (eType != OFTInteger && eType != OFTReal) + { + CPLString osTmp; + const char* pszVal = poFeature->GetFieldAsString(iField); + + if (!CPLIsUTF8(pszVal, -1)) + { + static int bFirstTime = TRUE; + if (bFirstTime) + { + bFirstTime = FALSE; + CPLError(CE_Warning, CPLE_AppDefined, + "%s is not a valid UTF-8 string. Forcing it to ASCII.\n" + "This warning won't be issued anymore", pszVal); + } + else + { + CPLDebug("OGR", "%s is not a valid UTF-8 string. Forcing it to ASCII", + pszVal); + } + char* pszEscaped = CPLForceToASCII(pszVal, -1, '?'); + osTmp = pszEscaped; + CPLFree(pszEscaped); + pszVal = osTmp.c_str(); + } + + osCommand += EscapeAndQuote(pszVal); + } + else + osCommand += poFeature->GetFieldAsString(iField); + } + } + + osCommand += " WHERE ROWID = '"; + osCommand += CPLSPrintf(CPL_FRMT_GIB, poFeature->GetFID()); + osCommand += "'"; + + CPLHTTPResult * psResult = poDS->RunSQL(osCommand); + if (psResult == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Feature update failed (1)"); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* We expect a response like "affected_rows\n1". */ +/* -------------------------------------------------------------------- */ + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || + strncmp(pszLine, "affected_rows\n1\n", 16) != 0 || + psResult->pszErrBuf != NULL) + { + CPLDebug( "GFT", "%s/%s", + pszLine ? pszLine : "null", + psResult->pszErrBuf ? psResult->pszErrBuf : "null"); + CPLError(CE_Failure, CPLE_AppDefined, "Feature update failed (2)"); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLHTTPDestroyResult(psResult); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRGFTTableLayer::DeleteFeature( GIntBig nFID ) +{ + GetLayerDefn(); + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + if (osTableId.size() == 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot delete feature in non-created table"); + return OGRERR_FAILURE; + } + + if (poDS->GetAccessToken().size() == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in unauthenticated mode"); + return OGRERR_FAILURE; + } + + CPLString osCommand; + + osCommand += "DELETE FROM "; + osCommand += osTableId; + osCommand += " WHERE ROWID = '"; + osCommand += CPLSPrintf(CPL_FRMT_GIB, nFID); + osCommand += "'"; + + //CPLDebug("GFT", "%s", osCommand.c_str()); + + CPLHTTPResult * psResult = poDS->RunSQL(osCommand); + if (psResult == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Feature deletion failed (1)"); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* We expect a response like "affected_rows\n1". */ +/* -------------------------------------------------------------------- */ + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || + strncmp(pszLine, "affected_rows\n1\n", 16) != 0 || + psResult->pszErrBuf != NULL) + { + CPLDebug( "GFT", "%s/%s", + pszLine ? pszLine : "null", + psResult->pszErrBuf ? psResult->pszErrBuf : "null"); + CPLError(CE_Failure, CPLE_AppDefined, "Feature deletion failed (2)"); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLHTTPDestroyResult(psResult); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +OGRErr OGRGFTTableLayer::StartTransaction() +{ + GetLayerDefn(); + + if (bInTransaction) + { + CPLError(CE_Failure, CPLE_AppDefined, "Already in transaction"); + return OGRERR_FAILURE; + } + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + if (osTableId.size() == 0) + { + CreateTableIfNecessary(); + if (osTableId.size() == 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot add feature to non-created table"); + return OGRERR_FAILURE; + } + } + + if (poDS->GetAccessToken().size() == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in unauthenticated mode"); + return OGRERR_FAILURE; + } + + bInTransaction = TRUE; + osTransaction.resize(0); + nFeaturesInTransaction = 0; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr OGRGFTTableLayer::CommitTransaction() +{ + GetLayerDefn(); + + if (!bInTransaction) + { + CPLError(CE_Failure, CPLE_AppDefined, "Should be in transaction"); + return OGRERR_FAILURE; + } + + bInTransaction = FALSE; + + if (nFeaturesInTransaction > 0) + { + if (nFeaturesInTransaction > 1) + osTransaction += ";"; + + CPLHTTPResult * psResult = poDS->RunSQL(osTransaction); + osTransaction.resize(0); + nFeaturesInTransaction = 0; + + if (psResult == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "CommitTransaction failed"); + return OGRERR_FAILURE; + } + + char* pszLine = (char*) psResult->pabyData; + if (pszLine == NULL || + strncmp(pszLine, "rowid", 5) != 0 || + psResult->pszErrBuf != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "CommitTransaction failed : %s", + pszLine ? pszLine : psResult->pszErrBuf); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + pszLine = OGRGFTGotoNextLine(pszLine); + while(pszLine && *pszLine != 0) + { + char* pszNextLine = OGRGFTGotoNextLine(pszLine); + if (pszNextLine) + pszNextLine[-1] = 0; + //CPLDebug("GFT", "Feature id = %s", pszLine); + + pszLine = pszNextLine; + } + + CPLHTTPDestroyResult(psResult); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +OGRErr OGRGFTTableLayer::RollbackTransaction() +{ + GetLayerDefn(); + + if (!bInTransaction) + { + CPLError(CE_Failure, CPLE_AppDefined, "Should be in transaction"); + return OGRERR_FAILURE; + } + bInTransaction = FALSE; + nFeaturesInTransaction = 0; + osTransaction.resize(0); + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRGFTTableLayer::SetAttributeFilter( const char *pszQuery ) + +{ + GetLayerDefn(); + + if( pszQuery == NULL ) + osQuery = ""; + else + { + osQuery = PatchSQL(pszQuery); + } + + BuildWhere(); + + ResetReading(); + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRGFTTableLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) + +{ + GetLayerDefn(); + + if( InstallFilter( poGeomIn ) ) + { + BuildWhere(); + + ResetReading(); + } +} + +/************************************************************************/ +/* BuildWhere() */ +/* */ +/* Build the WHERE statement appropriate to the current set of */ +/* criteria (spatial and attribute queries). */ +/************************************************************************/ + +void OGRGFTTableLayer::BuildWhere() + +{ + osWHERE = ""; + + if( m_poFilterGeom != NULL && iGeometryField >= 0) + { + OGREnvelope sEnvelope; + + m_poFilterGeom->getEnvelope( &sEnvelope ); + + CPLString osQuotedGeomColumn(EscapeAndQuote(GetGeometryColumn())); + + osWHERE.Printf("WHERE ST_INTERSECTS(%s, RECTANGLE(LATLNG(%.12f, %.12f), LATLNG(%.12f, %.12f)))", + osQuotedGeomColumn.c_str(), + MAX(-90.,sEnvelope.MinY - 1e-11), MAX(-180., sEnvelope.MinX - 1e-11), + MIN(90.,sEnvelope.MaxY + 1e-11), MIN(180.,sEnvelope.MaxX + 1e-11)); + } + + if( strlen(osQuery) > 0 ) + { + if( strlen(osWHERE) == 0 ) + osWHERE = "WHERE "; + else + osWHERE += " AND "; + osWHERE += osQuery; + } +} + +/************************************************************************/ +/* SetGeometryType() */ +/************************************************************************/ + +void OGRGFTTableLayer::SetGeometryType(OGRwkbGeometryType eGType) +{ + eGTypeForCreation = eGType; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/GNUmakefile new file mode 100644 index 000000000..e1dada6f4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/GNUmakefile @@ -0,0 +1,16 @@ + + +include ../../../GDALmake.opt + +OBJ = \ + ogrgmedriver.o ogrgmedatasource.o ogrgmelayer.o ogrgmejson.o + +CPPFLAGS := $(JSON_INCLUDE) -I../geojson -I.. -I../.. \ + $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_gme.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/makefile.vc new file mode 100644 index 000000000..d79059316 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrgmedriver.obj ogrgmedatasource.obj ogrgmelayer.obj ogrgmejson.obj +EXTRAFLAGS = -I.. -I..\.. -I..\geojson -I..\geojson\libjson + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogr_gme.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogr_gme.h new file mode 100644 index 000000000..8fbdcc605 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogr_gme.h @@ -0,0 +1,213 @@ +/****************************************************************************** + * $Id: ogr_gft.h 25483 2013-01-10 17:06:59Z warmerdam $ + * + * Project: Google Maps Engine (GME) API Driver + * Purpose: GME driver declarations + * Author: Frank Warmerdam + * (derived from GFT driver by Even) + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GME_H_INCLUDED +#define _OGR_GME_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "ogrgeojsonreader.h" +#include "cpl_http.h" + +#include +#include + +#include + +/************************************************************************/ +/* OGRGMELayer */ +/************************************************************************/ +class OGRGMEDataSource; + +class OGRGMELayer : public OGRLayer +{ + OGRGMEDataSource* poDS; + + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference* poSRS; + + int iGeometryField; + int iGxIdField; + + CPLString osTableName; + CPLString osTableId; + std::map omnosIdToGMEKey; + std::map omnpoUpdatedFeatures; + std::map omnpoInsertedFeatures; + std::vector oListOfDeletedFeatures; + CPLString osGeomColumnName; + + CPLString osWhere; + CPLString osSelect; + CPLString osIntersects; + + json_object* current_feature_page; + json_object* current_features_array; + int index_in_page; + + bool bDirty; + bool bCreateTablePending; + bool bInTransaction; + unsigned int iBatchPatchSize; + OGRwkbGeometryType eGTypeForCreation; + CPLString osProjectId; + CPLString osDraftACL; + CPLString osPublishedACL; + + void GetPageOfFeatures(); + + void BuildWhere(void); + + int FetchDescribe(); + + OGRFeature *GetNextRawFeature(); + void GetPageOfFEatures(); + OGRErr BatchPatch(); + OGRErr BatchInsert(); + OGRErr BatchDelete(); + OGRErr BatchRequest(const char *osMethod, std::map &omnpoFeatures); + unsigned int GetBatchPatchSize(); + bool CreateTableIfNotCreated(); + static OGRPolygon *WindPolygonCCW( OGRPolygon *poPolygon ); + + public: + OGRGMELayer(OGRGMEDataSource* poDS, const char* pszTableId); + OGRGMELayer(OGRGMEDataSource* poDS, const char* pszTableName, char ** papszOptions); + ~OGRGMELayer(); + + virtual void ResetReading(); + void SetBatchPatchSize(unsigned int iSize); + + virtual OGRFeatureDefn * GetLayerDefn(); + + virtual const char *GetName() { return osTableName.c_str(); } + + virtual OGRFeature *GetNextFeature(); + + virtual const char *GetGeometryColumn() { return osGeomColumnName; } + + virtual int TestCapability( const char * ); + + virtual void SetSpatialFilter( OGRGeometry * ); + + virtual OGRErr SetAttributeFilter( const char * pszWhere ); + + virtual OGRErr SetIgnoredFields(const char ** papszFields ); + + virtual OGRErr SyncToDisk(); + + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature(GIntBig); + virtual OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK = TRUE ); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + void SetGeometryType(OGRwkbGeometryType eGType); +}; + +/************************************************************************/ +/* OGRGMEDataSource */ +/************************************************************************/ + +class OGRGMEDataSource : public OGRDataSource +{ + char* pszName; + + OGRLayer** papoLayers; + int nLayers; + + int bReadWrite; + + int bUseHTTPS; + + CPLString osAuth; + CPLString osAccessToken; + CPLString osRefreshToken; + CPLString osTraceToken; + CPLString osAPIKey; + CPLString osSelect; + CPLString osWhere; + CPLString osProjectId; + + void DeleteLayer( const char *pszLayerName ); + + int bMustCleanPersistant; + int nRetries; + + public: + OGRGMEDataSource(); + ~OGRGMEDataSource(); + + int Open( const char * pszFilename, + int bUpdate ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + + virtual OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + + virtual int TestCapability( const char * ); + + CPLHTTPResult* MakeRequest(const char *pszRequest, + const char *pszMoreOptions = NULL); + CPLHTTPResult* PostRequest(const char *pszRequest, + const char *pszBody); + const CPLString& GetAccessToken() const { return osAccessToken;} + const char* GetAPIURL() const; + int IsReadWrite() const { return bReadWrite; } + void AddHTTPOptions(CPLStringList &oOptions); + void AddHTTPPostOptions(CPLStringList &oOptions); + +}; + +/************************************************************************/ +/* OGRGMEDriver */ +/************************************************************************/ + +class OGRGMEDriver : public OGRSFDriver +{ + public: + ~OGRGMEDriver(); + + virtual const char* GetName(); + virtual OGRDataSource* Open( const char *, int ); + virtual OGRDataSource* CreateDataSource( const char * pszName, + char **papszOptions ); + virtual int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_GME_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmedatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmedatasource.cpp new file mode 100644 index 000000000..cf9b8a422 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmedatasource.cpp @@ -0,0 +1,598 @@ +/****************************************************************************** + * $Id$ + * + * Project: Google Maps Engine API Driver + * Purpose: OGRGMEDataSource Implementation. + * Author: Frank Warmerdam + * (derived from GFT driver by Even) + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gme.h" +#include "ogrgmejson.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id$"); + +#define GDAL_API_KEY "AIzaSyA_2h1_wXMOLHNSVeo-jf1ACME-M1XMgP0" +#define GME_TABLE_SCOPE_RO "https://www.googleapis.com/auth/mapsengine.readonly" +#define GME_TABLE_SCOPE "https://www.googleapis.com/auth/mapsengine" + +/************************************************************************/ +/* OGRGMEDataSource() */ +/************************************************************************/ + +OGRGMEDataSource::OGRGMEDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; + + bReadWrite = FALSE; + bUseHTTPS = FALSE; + + bMustCleanPersistant = FALSE; + nRetries = 0; +} + +/************************************************************************/ +/* ~OGRGMEDataSource() */ +/************************************************************************/ + +OGRGMEDataSource::~OGRGMEDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + if (bMustCleanPersistant) + { + char** papszOptions = NULL; + papszOptions = CSLSetNameValue(papszOptions, "CLOSE_PERSISTENT", CPLSPrintf("GME:%p", this)); + CPLHTTPFetch( GetAPIURL(), papszOptions); + CSLDestroy(papszOptions); + } + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGMEDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + //else if( bReadWrite && EQUAL(pszCap,ODsCDeleteLayer) ) + // return TRUE; + //else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRGMEDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* OGRGMEGetOptionValue() */ +/************************************************************************/ + +CPLString OGRGMEGetOptionValue(const char* pszFilename, + const char* pszOptionName) +{ + CPLString osOptionName(pszOptionName); + osOptionName += "="; + const char* pszOptionValue = strstr(pszFilename, osOptionName); + if (!pszOptionValue) + return ""; + + CPLString osOptionValue(pszOptionValue + strlen(osOptionName)); + const char* pszSpace = strchr(osOptionValue.c_str(), ' '); + if (pszSpace) + osOptionValue.resize(pszSpace - osOptionValue.c_str()); + return osOptionValue; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRGMEDataSource::Open( const char * pszFilename, int bUpdateIn) + +{ + bReadWrite = bUpdateIn; + + pszName = CPLStrdup( pszFilename ); + + osAuth = OGRGMEGetOptionValue(pszFilename, "auth"); + if (osAuth.size() == 0) + osAuth = CPLGetConfigOption("GME_AUTH", ""); + + osRefreshToken = OGRGMEGetOptionValue(pszFilename, "refresh"); + if (osRefreshToken.size() == 0) + osRefreshToken = CPLGetConfigOption("GME_REFRESH_TOKEN", ""); + + osAPIKey = CPLGetConfigOption("GME_APIKEY", GDAL_API_KEY); + + CPLString osTables = OGRGMEGetOptionValue(pszFilename, "tables"); + + osProjectId = OGRGMEGetOptionValue(pszFilename, "projectId"); + + osSelect = OGRGMEGetOptionValue(pszFilename, "select"); + osWhere = OGRGMEGetOptionValue(pszFilename, "where"); + + CPLString osBatchPatchSize; + osBatchPatchSize = OGRGMEGetOptionValue(pszFilename, "batchpatchsize"); + if (osBatchPatchSize.size() == 0) { + osBatchPatchSize = CPLGetConfigOption("GME_BATCH_PATCH_SIZE","50"); + } + int iBatchPatchSize = atoi( osBatchPatchSize.c_str() ); + + bUseHTTPS = TRUE; + + osAccessToken = OGRGMEGetOptionValue(pszFilename, "access"); + if (osAccessToken.size() == 0) + osAccessToken = CPLGetConfigOption("GME_ACCESS_TOKEN",""); + if (osAccessToken.size() == 0 && osRefreshToken.size() > 0) + { + osAccessToken.Seize(GOA2GetAccessToken(osRefreshToken, + GME_TABLE_SCOPE)); // TODO + if (osAccessToken.size() == 0) { + CPLDebug( "GME", "Cannot get access token"); + return FALSE; + } + } + + if (osAccessToken.size() == 0 && osAuth.size() > 0) + { + osRefreshToken.Seize(GOA2GetRefreshToken(osAuth, GME_TABLE_SCOPE)); // TODO + if (osRefreshToken.size() == 0) + CPLDebug( "GME", "Cannot get refresh token"); + return FALSE; + } + + if ((osAccessToken.size() ==0) && (osTables.size() == 0)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unauthenticated access requires explicit tables= parameter"); + return FALSE; + } + + osTraceToken = OGRGMEGetOptionValue(pszFilename, "trace"); + if (osTraceToken.size() == 0) { + CPLDebug("GME", "Looking for GME_TRACE_TOKEN"); + osTraceToken = CPLGetConfigOption("GME_TRACE_TOKEN", ""); + } + if (osTraceToken.size() != 0) { + CPLDebug("GME", "Found trace token %s", osTraceToken.c_str()); + } + + if (osTables.size() != 0) + { + char** papszTables = CSLTokenizeString2(osTables, ",", 0); + for(int i=0;papszTables && papszTables[i];i++) + { + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + OGRGMELayer *poGMELayer = new OGRGMELayer(this, papszTables[i]); + poGMELayer->SetBatchPatchSize(iBatchPatchSize); + if (poGMELayer->GetLayerDefn()) { + papoLayers[nLayers ++] = poGMELayer; + } + else { + delete poGMELayer; + } + } + CSLDestroy(papszTables); + if ( nLayers == 0 ) { + CPLError(CE_Failure, CPLE_AppDefined, + "Could not find any tables."); + return FALSE; + } + CPLDebug("GME", "Found %d layers", nLayers); + return TRUE; + } + else if (osProjectId.size() != 0) { + CPLDebug("GME", "We have a projectId: %s. UseICreateLayer to create tables.", + osProjectId.c_str()); + return TRUE; + } + CPLDebug("GME", "No table no project, giving up!"); + return FALSE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer *OGRGMEDataSource::ICreateLayer( const char *pszName, + CPL_UNUSED OGRSpatialReference *poSpatialRef, + OGRwkbGeometryType eGType, + char ** papszOptions ) +{ + if (!bReadWrite) + { + CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in read-only mode"); + return NULL; + } + + if (osAccessToken.size() == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, "Operation not available in unauthenticated mode"); + return NULL; + } + + if ((CSLFetchNameValue( papszOptions, "projectId" ) == NULL) && (osProjectId.size() != 0)) { + papszOptions = CSLAddNameValue( papszOptions, "projectId", osProjectId.c_str() ); + } + + osTraceToken = OGRGMEGetOptionValue(pszName, "trace"); + if (osTraceToken.size() == 0) { + osTraceToken = CPLGetConfigOption("GME_TRACE_TOKEN", ""); + } + if (osTraceToken.size() != 0) { + CPLDebug("GME", "Found trace token %s", osTraceToken.c_str()); + } + + OGRGMELayer* poLayer = new OGRGMELayer(this, pszName, papszOptions); + poLayer->SetGeometryType(eGType); + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers ++] = poLayer; + return poLayer; +} + +/************************************************************************/ +/* GetAPIURL() */ +/************************************************************************/ + +const char* OGRGMEDataSource::GetAPIURL() const +{ + const char* pszAPIURL = CPLGetConfigOption("GME_API_URL", NULL); + if (pszAPIURL) + return pszAPIURL; + else if (bUseHTTPS) + return "https://www.googleapis.com/mapsengine/v1"; + else + return "http://www.googleapis.com/mapsengine/v1"; +} + +/************************************************************************/ +/* AddHTTPOptions() */ +/************************************************************************/ + +void OGRGMEDataSource::AddHTTPOptions(CPLStringList &oOptions) +{ + bMustCleanPersistant = TRUE; + + if (strlen(osAccessToken) > 0) + oOptions.AddString( + CPLSPrintf("HEADERS=Authorization: Bearer %s", + osAccessToken.c_str())); + + oOptions.AddString(CPLSPrintf("PERSISTENT=GME:%p", this)); +} + +/************************************************************************/ +/* MakeRequest() */ +/************************************************************************/ + +CPLHTTPResult * OGRGMEDataSource::MakeRequest(const char *pszRequest, + const char *pszMoreOptions) +{ +/* -------------------------------------------------------------------- */ +/* Provide the API Key - used to rate limit access (see */ +/* GME_APIKEY config) */ +/* -------------------------------------------------------------------- */ + CPLString osQueryFields; + + osQueryFields += "key="; + osQueryFields += osAPIKey; + + if (pszMoreOptions) + osQueryFields += pszMoreOptions; + +/* -------------------------------------------------------------------- */ +/* Collect the header options. */ +/* -------------------------------------------------------------------- */ + CPLStringList oOptions; + oOptions.AddString("CUSTOMREQUEST=GET"); + AddHTTPOptions(oOptions); + +/* -------------------------------------------------------------------- */ +/* Build URL */ +/* -------------------------------------------------------------------- */ + CPLString osURL = GetAPIURL(); + osURL += "/"; + osURL += pszRequest; + + if (osURL.find("?") == std::string::npos) { + osURL += "?"; + } else { + osURL += "?"; + } + osURL += osQueryFields; + + // Trace the request if we have a tracing token + if (osTraceToken.size() != 0) { + CPLDebug("GME", "Using trace token %s", osTraceToken.c_str()); + osURL += "&trace="; + osURL += osTraceToken; + } + + CPLDebug( "GME", "Sleep for 1s to try and avoid qps limiting errors."); + CPLSleep( 1.0 ); + + CPLHTTPResult * psResult = CPLHTTPFetch(osURL, oOptions); + +/* -------------------------------------------------------------------- */ +/* Check for some error conditions and report. HTML Messages */ +/* are transformed info failure. */ +/* -------------------------------------------------------------------- */ + if (psResult && psResult->pszContentType && + strncmp(psResult->pszContentType, "text/html", 9) == 0) + { + CPLDebug( "GME", "MakeRequest HTML Response: %s", psResult->pabyData ); + CPLError(CE_Failure, CPLE_AppDefined, + "HTML error page returned by server"); + if (nRetries < 5) { + CPLDebug("GME", "Sleeping 30s and retrying"); + nRetries ++; + CPLSleep( 30.0 ); + psResult = MakeRequest(pszRequest, pszMoreOptions); + if (psResult) + CPLDebug( "GME", "Got a result after %d retries", nRetries ); + else + CPLDebug( "GME", "Didn't get a result after %d retries", nRetries ); + nRetries--; + } else { + CPLDebug("GME", "I've waited too long on GME. Giving up!"); + CPLHTTPDestroyResult(psResult); + psResult = NULL; + } + return psResult; + } + if (psResult && psResult->pszErrBuf != NULL) + { + CPLDebug( "GME", "MakeRequest Error Message: %s", psResult->pszErrBuf ); + CPLDebug( "GME", "error doc:\n%s\n", psResult->pabyData ? (const char*)psResult->pabyData : "null"); + json_object *error_response = OGRGMEParseJSON((const char *) psResult->pabyData); + CPLHTTPDestroyResult(psResult); + psResult = NULL; + if( error_response != NULL ) + { + json_object *error_doc = json_object_object_get(error_response, "error"); + json_object *errors_doc = json_object_object_get(error_doc, "errors"); + array_list *errors_array = json_object_get_array(errors_doc); + int nErrors = array_list_length(errors_array); + for (int i = 0; i < nErrors; i++) { + json_object *error_obj = (json_object *)array_list_get_idx(errors_array, i); + const char* reason = OGRGMEGetJSONString(error_obj, "reason", ""); + const char* domain = OGRGMEGetJSONString(error_obj, "domain", ""); + const char* message = OGRGMEGetJSONString(error_obj, "message", ""); + const char* locationType = OGRGMEGetJSONString(error_obj, "locationType", ""); + const char* location = OGRGMEGetJSONString(error_obj, "location", ""); + if ((nRetries < 10) && EQUAL(reason, "rateLimitExceeded")) { + // Sleep nRetries * 1.0s and retry + nRetries ++; + CPLDebug( "GME", "Got a %s (%d) times.", reason, nRetries ); + CPLDebug( "GME", "Sleep for %2.2f to try and avoid qps limiting errors.", 1.0 * nRetries ); + CPLSleep( 1.0 * nRetries ); + psResult = MakeRequest(pszRequest, pszMoreOptions); + if (psResult) + CPLDebug( "GME", "Got a result after %d retries", nRetries ); + else + CPLDebug( "GME", "Didn't get a result after %d retries", nRetries ); + nRetries = 0; + } + else if (EQUAL(reason, "authError")) { + CPLDebug( "GME", "Failed to GET %s: %s", pszRequest, message ); + CPLError( CE_Failure, CPLE_OpenFailed, "GME: %s", message); + } + else if (EQUAL(reason, "backendError")) { + CPLDebug( "GME", "Backend error retrying: GET %s: %s", pszRequest, message ); + psResult = MakeRequest(pszRequest, pszMoreOptions); + } + else { + int code = 444; + json_object *code_child = json_object_object_get(error_doc, "code"); + if (code_child != NULL ) + code = json_object_get_int(code_child); + + CPLDebug( "GME", "MakeRequest Error for %s: %s:%d", pszRequest, reason, code); + CPLError( CE_Failure, CPLE_AppDefined, "GME: %s %s %s: %s - %s", + domain, reason, locationType, location, message ); + } + } + json_object_put(error_response); + } + return psResult; + } + else if (psResult && psResult->nStatus != 0) + { + CPLDebug( "GME", "MakeRequest Error Status:%d", psResult->nStatus ); + } + return psResult; +} + +/************************************************************************/ +/* AddHTTPPostOptions() */ +/************************************************************************/ + +void OGRGMEDataSource::AddHTTPPostOptions(CPLStringList &oOptions) +{ + bMustCleanPersistant = TRUE; + + if (strlen(osAccessToken) > 0) + oOptions.AddString( + CPLSPrintf("HEADERS=Content-type: application/json\n" + "Authorization: Bearer %s", + osAccessToken.c_str())); + + oOptions.AddString(CPLSPrintf("PERSISTENT=GME:%p", this)); +} + +/************************************************************************/ +/* PostRequest() */ +/************************************************************************/ + +CPLHTTPResult * OGRGMEDataSource::PostRequest(const char *pszRequest, + const char *pszBody) +{ +/* -------------------------------------------------------------------- */ +/* Provide the API Key - used to rate limit access (see */ +/* GME_APIKEY config) */ +/* -------------------------------------------------------------------- */ + CPLString osQueryFields; + + osQueryFields += "key="; + osQueryFields += osAPIKey; + +/* -------------------------------------------------------------------- */ +/* Collect the header options. */ +/* -------------------------------------------------------------------- */ + CPLStringList oOptions; + oOptions.AddString("CUSTOMREQUEST=POST"); + CPLString osPostFields = "POSTFIELDS="; + osPostFields += pszBody; + oOptions.AddString(osPostFields); + + AddHTTPPostOptions(oOptions); + +/* -------------------------------------------------------------------- */ +/* Build URL */ +/* -------------------------------------------------------------------- */ + CPLString osURL = GetAPIURL(); + osURL += "/"; + osURL += pszRequest; + + if (osURL.find("?") == std::string::npos) { + osURL += "?"; + } else { + osURL += "?"; + } + osURL += osQueryFields; + + // Trace the request if we have a tracing token + if (osTraceToken.size() != 0) { + CPLDebug("GME", "Using trace token %s", osTraceToken.c_str()); + osURL += "&trace="; + osURL += osTraceToken; + } + + CPLDebug( "GME", "Sleep for 1s to try and avoid qps limiting errors."); + CPLSleep( 1.0 ); + + CPLDebug( "GME", "Posting to %s.", osURL.c_str()); + CPLHTTPResult * psResult = CPLHTTPFetch(osURL, oOptions); + +/* -------------------------------------------------------------------- */ +/* Check for some error conditions and report. HTML Messages */ +/* are transformed info failure. */ +/* -------------------------------------------------------------------- */ + if (psResult && psResult->pszContentType && + strncmp(psResult->pszContentType, "text/html", 9) == 0) + { + CPLDebug( "GME", "PostRequest HTML Response:%s", psResult->pabyData ); + CPLError(CE_Failure, CPLE_AppDefined, + "HTML error page returned by server"); + CPLHTTPDestroyResult(psResult); + psResult = NULL; + } + if (psResult && psResult->pszErrBuf != NULL) + { + CPLDebug( "GME", "PostRequest Error Message: %s", psResult->pszErrBuf ); + CPLDebug( "GME", "error doc:\n%s\n", psResult->pabyData); + json_object *error_response = OGRGMEParseJSON((const char *) psResult->pabyData); + CPLHTTPDestroyResult(psResult); + psResult = NULL; + json_object *error_doc = json_object_object_get(error_response, "error"); + json_object *errors_doc = json_object_object_get(error_doc, "errors"); + array_list *errors_array = json_object_get_array(errors_doc); + int nErrors = array_list_length(errors_array); + for (int i = 0; i < nErrors; i++) { + json_object *error_obj = (json_object *)array_list_get_idx(errors_array, i); + const char* reason = OGRGMEGetJSONString(error_obj, "reason", ""); + const char* domain = OGRGMEGetJSONString(error_obj, "domain", ""); + const char* message = OGRGMEGetJSONString(error_obj, "message", ""); + const char* locationType = OGRGMEGetJSONString(error_obj, "locationType", ""); + const char* location = OGRGMEGetJSONString(error_obj, "location", ""); + if ((nRetries < 10) && EQUAL(reason, "rateLimitExceeded")) { + // Sleep nRetries * 1.0s and retry + nRetries ++; + CPLDebug( "GME", "Got a %s (%d) times.", reason, nRetries ); + CPLDebug( "GME", "Sleep for %2.2f to try and avoid qps limiting errors.", 1.0 * nRetries ); + CPLSleep( 1.0 * nRetries ); + psResult = PostRequest(pszRequest, pszBody); + if (psResult) + CPLDebug( "GME", "Got a result after %d retries", nRetries ); + else + CPLDebug( "GME", "Didn't get a result after %d retries", nRetries ); + nRetries = 0; + } + else if (EQUAL(reason, "authError")) { + CPLDebug( "GME", "Failed to GET %s: %s", pszRequest, message ); + CPLError( CE_Failure, CPLE_OpenFailed, "GME: %s", message); + } + else if (EQUAL(reason, "backendError")) { + CPLDebug( "GME", "Backend error retrying: GET %s: %s", pszRequest, message ); + psResult = PostRequest(pszRequest, pszBody); + } + else { + int code = 444; + json_object *code_child = json_object_object_get(error_doc, "code"); + if (code_child != NULL ) + code = json_object_get_int(code_child); + + CPLError( CE_Failure, CPLE_AppDefined, "GME: %s %s %s: %s - %s", + domain, reason, locationType, location, message ); + if ((code == 400) && (EQUAL(reason, "invalid")) && (EQUAL(location, "id"))) { + CPLDebug("GME", "Got the notorious 400 - invalid id, retrying in 10s"); + CPLSleep( 10.0 ); + psResult = PostRequest(pszRequest, pszBody); + } + else { + CPLDebug( "GME", "PostRequest Error for %s: %s:%d", pszRequest, reason, code); + } + } + } + return psResult; + } + else if (psResult && psResult->nStatus != 0) + { + CPLDebug( "GME", "PostRequest Error Status:%d", psResult->nStatus ); + } + return psResult; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmedriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmedriver.cpp new file mode 100644 index 000000000..faeb50e10 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmedriver.cpp @@ -0,0 +1,122 @@ +/****************************************************************************** + * $Id$ + * + * Project: Google Maps Engine API Driver + * Purpose: OGRGMEDriver Implementation. + * Author: Frank Warmerdam + * (derived from GFT driver by Even) + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gme.h" + +CPL_CVSID("$Id$"); + +extern "C" void RegisterOGRGME(); + +/************************************************************************/ +/* ~OGRGMEDriver() */ +/************************************************************************/ + +OGRGMEDriver::~OGRGMEDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRGMEDriver::GetName() + +{ + return "GME"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRGMEDriver::Open( const char * pszFilename, int bUpdate ) + +{ + if (!EQUALN(pszFilename, "GME:", 4)) + return NULL; + + OGRGMEDataSource *poDS = new OGRGMEDataSource(); + + if( !poDS->Open( pszFilename, bUpdate ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRGMEDriver::CreateDataSource( const char * pszName, + CPL_UNUSED char **papszOptions ) +{ + OGRGMEDataSource *poDS = new OGRGMEDataSource(); + + if( !poDS->Open( pszName, TRUE ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGMEDriver::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap, ODrCCreateDataSource)) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRGME() */ +/************************************************************************/ + +void RegisterOGRGME() + +{ + OGRSFDriver* poDriver = new OGRGMEDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Google Maps Engine" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "http://trac.osgeo.org/gdal/wiki/GMEDriver" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmejson.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmejson.cpp new file mode 100644 index 000000000..c54ea4948 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmejson.cpp @@ -0,0 +1,550 @@ +/****************************************************************************** + * $Id$ + * + * Project: Google Maps Engine API Driver + * Purpose: GME GeoJSON helpper function Implementations. + * Author: Wolf Beregnheim + * (derived from Geo JSON driver by Mateusz) + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrgmejson.h" +#include + +static int json_gme_double_to_string(json_object *jso, + printbuf *pb, + int level, + int flags); + +/************************************************************************/ +/* OGRGMEFeatureToGeoJSON() */ +/************************************************************************/ +json_object* OGRGMEFeatureToGeoJSON(OGRFeature* poFeature) + +{ + if( NULL == poFeature ) + return NULL; + + json_object* pjoFeature = json_object_new_object(); + CPLAssert( NULL != pjoFeature ); + + json_object_object_add( pjoFeature, "type", + json_object_new_string("Feature") ); + + /* -------------------------------------------------------------------- */ + /* Write feature geometry to GeoJSON "geometry" object. */ + /* -------------------------------------------------------------------- */ + json_object* pjoGeometry = NULL; + OGRGeometry* poGeometry = poFeature->GetGeometryRef(); + + pjoGeometry = OGRGMEGeometryToGeoJSON(poGeometry); + if ( NULL == pjoGeometry ) { + CPLError( CE_Failure, CPLE_AppDefined, + "GME: NULL Geometry detected in feature " CPL_FRMT_GIB ". Ignoring feature.", + poFeature->GetFID() ); + json_object_put( pjoFeature ); + return NULL; + } + json_object_object_add( pjoFeature, "geometry", pjoGeometry ); + + /* -------------------------------------------------------------------- */ + /* Write feature attributes to GeoJSON "properties" object. */ + /* -------------------------------------------------------------------- */ + json_object* pjoProps = NULL; + + pjoProps = OGRGMEAttributesToGeoJSON( poFeature ); + if ( pjoProps ) + json_object_object_add( pjoFeature, "properties", pjoProps ); + + return pjoFeature; +} + +/************************************************************************/ +/* OGRGMEGeometryToGeoJSON() */ +/************************************************************************/ +json_object* OGRGMEGeometryToGeoJSON(OGRGeometry* poGeometry) + +{ + if ( NULL == poGeometry ) + return NULL; + + json_object* pjoGeometry = json_object_new_object(); + CPLAssert( NULL != pjoGeometry ); + + /* -------------------------------------------------------------------- */ + /* Build "type" member of GeoJSOn "geometry" object */ + /* and "coordinates" member of GeoJSOn "geometry" object. */ + /* -------------------------------------------------------------------- */ + const char* pszType = NULL; + OGRwkbGeometryType eType = poGeometry->getGeometryType(); + json_object* pjoCoordinates = NULL; + if( wkbGeometryCollection == eType || wkbGeometryCollection25D == eType ) + { + pszType = "GeometryCollection"; + json_object *pjoGeometries = + OGRGMEGeometryCollectionToGeoJSON(static_cast(poGeometry)); + if ( pjoGeometries ) { + json_object *pjoType = json_object_new_string(pszType); + json_object_object_add( pjoGeometry, "type", pjoType ); + json_object_object_add( pjoGeometry, "geometries", pjoGeometries ); + } + else { + json_object_put(pjoGeometry); + pjoGeometry = NULL; + } + } + else + { + if( wkbPoint == eType || wkbPoint25D == eType ) { + pszType = "Point"; + pjoCoordinates = OGRGMEPointToGeoJSON( static_cast(poGeometry) ); + } + if( wkbMultiPoint == eType || wkbMultiPoint25D == eType ) { + pszType = "MultiPoint"; + pjoCoordinates = OGRGMEMultiPointToGeoJSON( static_cast(poGeometry) ); + } + else if( wkbLineString == eType || wkbLineString25D == eType ) { + pszType = "LineString"; + pjoCoordinates = OGRGMELineStringToGeoJSON( static_cast(poGeometry) ); + } + else if( wkbMultiLineString == eType || wkbMultiLineString25D == eType ) { + pszType = "MultiLineString"; + pjoCoordinates = OGRGMEMultiLineStringToGeoJSON( static_cast(poGeometry) ); + } + else if( wkbPolygon == eType || wkbPolygon25D == eType ) { + pszType = "Polygon"; + pjoCoordinates = OGRGMEPolygonToGeoJSON( static_cast(poGeometry) ); + } + else if( wkbMultiPolygon == eType || wkbMultiPolygon25D == eType ) { + pszType = "MultiPolygon"; + pjoCoordinates = OGRGMEMultiPolygonToGeoJSON( static_cast(poGeometry) ); + } + else { + CPLDebug( "GME", "Unsupported geometry type detected. Geometry is IGNORED." ); + } + + if ( pjoCoordinates && pszType ) { + json_object *pjoType = json_object_new_string(pszType); + json_object_object_add( pjoGeometry, "type", pjoType ); + json_object_object_add( pjoGeometry, "coordinates", pjoCoordinates); + } + else { + json_object_put(pjoGeometry); + pjoGeometry = NULL; + } + } + return pjoGeometry; +} + +/************************************************************************/ +/* OGRGMEGeometryCollectionToGeoJSON() */ +/************************************************************************/ +json_object* OGRGMEGeometryCollectionToGeoJSON(OGRGeometryCollection* poGeometryCollection) + +{ + if ( NULL == poGeometryCollection ) + return NULL; + + /* Generate "geometries" object. */ + json_object* pjoGeometries = NULL; + pjoGeometries = json_object_new_array(); + + for( int i = 0; i < poGeometryCollection->getNumGeometries(); ++i ) { + OGRGeometry* poGeometry = poGeometryCollection->getGeometryRef( i ); + json_object* pjoGeometry = NULL; + pjoGeometry = OGRGMEGeometryToGeoJSON( poGeometry ); + if ( NULL != poGeometry ) + json_object_array_add( pjoGeometries, pjoGeometry ); + else + CPLError( CE_Failure, CPLE_AppDefined, "GME: Ignoring NULL geometry" ); + } + return pjoGeometries; +} + +/************************************************************************/ +/* OGRGMEPointToGeoJSON */ +/************************************************************************/ + +json_object* OGRGMEPointToGeoJSON( OGRPoint* poPoint ) +{ + if( NULL == poPoint ) + return NULL; + + json_object* pjoCoordinates = NULL; + + /* Generate "coordinates" object for 2D or 3D dimension. */ + if( 3 == poPoint->getCoordinateDimension() ) { + pjoCoordinates = OGRGMECoordsToGeoJSON( poPoint->getX(), + poPoint->getY(), + poPoint->getZ() ); + } + else if( 2 == poPoint->getCoordinateDimension() ) { + pjoCoordinates = OGRGMECoordsToGeoJSON( poPoint->getX(), + poPoint->getY() ); + } + else { + CPLError( CE_Failure, CPLE_AppDefined, "GME: Found EMPTY point, ignoring" ); + } + + return pjoCoordinates; +} + +/************************************************************************/ +/* OGRGMEMultiPointToGeoJSON */ +/************************************************************************/ + +json_object* OGRGMEMultiPointToGeoJSON( OGRMultiPoint* poGeometry ) +{ + if( NULL == poGeometry ) + return NULL; + + /* Generate "coordinates" object for 2D or 3D dimension. */ + json_object* pjoMultiPoint = NULL; + pjoMultiPoint = json_object_new_array(); + + for( int i = 0; i < poGeometry->getNumGeometries(); ++i ) + { + OGRGeometry* poGeom = poGeometry->getGeometryRef( i ); + CPLAssert( NULL != poGeom ); + OGRPoint* poPoint = static_cast(poGeom); + + json_object* pjoPoint = NULL; + pjoPoint = OGRGMEPointToGeoJSON( poPoint ); + if ( pjoPoint ) + json_object_array_add( pjoMultiPoint, pjoPoint ); + } + + return pjoMultiPoint; +} + +/************************************************************************/ +/* OGRGMELineStringToGeoJSON */ +/************************************************************************/ + +json_object* OGRGMELineStringToGeoJSON( OGRLineString* poLine ) +{ + if( NULL == poLine ) + return NULL; + + /* Generate "coordinates" object for 2D or 3D dimension. */ + json_object* pjoLineString = NULL; + pjoLineString = OGRGMELineCoordsToGeoJSON( poLine ); + + return pjoLineString; +} + +/************************************************************************/ +/* OGRGMEMultiLineStringToGeoJSON */ +/************************************************************************/ + +json_object* OGRGMEMultiLineStringToGeoJSON( OGRMultiLineString* poGeometry ) +{ + CPLAssert( NULL != poGeometry ); + + /* Generate "coordinates" object for 2D or 3D dimension. */ + json_object* pjoCoordinates = NULL; + pjoCoordinates = json_object_new_array(); + + for( int i = 0; i < poGeometry->getNumGeometries(); ++i ) + { + OGRGeometry* poGeom = poGeometry->getGeometryRef( i ); + CPLAssert( NULL != poGeom ); + OGRLineString* poLine = static_cast(poGeom); + + json_object* pjoLine = NULL; + pjoLine = OGRGMELineStringToGeoJSON( poLine ); + + json_object_array_add( pjoCoordinates, pjoLine ); + } + + return pjoCoordinates; +} + +/************************************************************************/ +/* OGRGMEPolygonToGeoJSON */ +/************************************************************************/ + +json_object* OGRGMEPolygonToGeoJSON( OGRPolygon* poPolygon ) +{ + CPLAssert( NULL != poPolygon ); + + /* Generate "coordinates" array object. */ + json_object* pjoCoordinates = NULL; + pjoCoordinates = json_object_new_array(); + + /* Exterior ring. */ + OGRLinearRing* poRing = poPolygon->getExteriorRing(); + if (poRing == NULL) { + json_object_put(pjoCoordinates); + return NULL; + } + + json_object* pjoRing = NULL; + // If the linear ring is CW re-wind it CCW + if (poRing->isClockwise() ) { + poRing->reverseWindingOrder(); + } + + pjoRing = OGRGMELineCoordsToGeoJSON( poRing ); + json_object_array_add( pjoCoordinates, pjoRing ); + + /* Interior rings. */ + const int nCount = poPolygon->getNumInteriorRings(); + for( int i = 0; i < nCount; ++i ) { + poRing = poPolygon->getInteriorRing( i ); + if (poRing == NULL) + continue; + // If the linear ring is CW re-wind it CCW + if (poRing->isClockwise() ) { + poRing->reverseWindingOrder(); + } + pjoRing = OGRGMELineCoordsToGeoJSON( poRing ); + json_object_array_add( pjoCoordinates, pjoRing ); + } + + return pjoCoordinates; +} + +/************************************************************************/ +/* OGRGMEMultiPolygonToGeoJSON */ +/************************************************************************/ + +json_object* OGRGMEMultiPolygonToGeoJSON( OGRMultiPolygon* poGeometry ) +{ + CPLAssert( NULL != poGeometry ); + + /* Generate "coordinates" object for 2D or 3D dimension. */ + json_object* pjoCoordinates = NULL; + pjoCoordinates = json_object_new_array(); + + for( int i = 0; i < poGeometry->getNumGeometries(); ++i ) + { + OGRGeometry* poGeom = poGeometry->getGeometryRef( i ); + CPLAssert( NULL != poGeom ); + OGRPolygon* poPoly = static_cast(poGeom); + + json_object* pjoPoly = NULL; + pjoPoly = OGRGMEPolygonToGeoJSON( poPoly ); + + json_object_array_add( pjoCoordinates, pjoPoly ); + } + + return pjoCoordinates; +} + +/************************************************************************/ +/* OGRGMECoordsToGeoJSON */ +/************************************************************************/ + +json_object* OGRGMECoordsToGeoJSON( double const& fX, double const& fY ) +{ + json_object* pjoCoords = NULL; + pjoCoords = json_object_new_array(); + json_object_array_add( pjoCoords, json_object_new_gme_double( fX ) ); + json_object_array_add( pjoCoords, json_object_new_gme_double( fY ) ); + + return pjoCoords; +} + +json_object* OGRGMECoordsToGeoJSON( double const& fX, double const& fY, double const& fZ ) +{ + json_object* pjoCoords = NULL; + pjoCoords = json_object_new_array(); + json_object_array_add( pjoCoords, json_object_new_gme_double( fX ) ); + json_object_array_add( pjoCoords, json_object_new_gme_double( fY ) ); + json_object_array_add( pjoCoords, json_object_new_gme_double( fZ ) ); + + return pjoCoords; +} + +/************************************************************************/ +/* OGRGMELineCoordsToGeoJSON */ +/************************************************************************/ + +json_object* OGRGMELineCoordsToGeoJSON( OGRLineString* poLine ) +{ + json_object* pjoCoords = json_object_new_array(); + + const int nCount = poLine->getNumPoints(); + for( int i = 0; i < nCount; ++i ) + { + json_object* pjoPoint = NULL; + if( poLine->getCoordinateDimension() == 2 ) + pjoPoint = OGRGMECoordsToGeoJSON( poLine->getX(i), poLine->getY(i) ); + else + pjoPoint = OGRGMECoordsToGeoJSON( poLine->getX(i), poLine->getY(i), poLine->getZ(i) ); + json_object_array_add( pjoCoords, pjoPoint ); + } + + return pjoCoords; +} + +/************************************************************************/ +/* OGRGMEAttributesToGeoJSON */ +/************************************************************************/ + +json_object* OGRGMEAttributesToGeoJSON( OGRFeature* poFeature ) +{ + if ( NULL == poFeature ) + return NULL; + json_object* pjoProperties = json_object_new_object(); + CPLAssert( NULL != pjoProperties ); + + OGRFeatureDefn* poDefn = poFeature->GetDefnRef(); + for( int nField = 0; nField < poDefn->GetFieldCount(); ++nField ) { + json_object* pjoProperty = NULL; + OGRFieldDefn* poFieldDefn = poDefn->GetFieldDefn( nField ); + if ( NULL == poFieldDefn ) + continue; + OGRFieldType eType = poFieldDefn->GetType(); + + if( !poFeature->IsFieldSet(nField) ) + pjoProperty = NULL; + + // In GME integers are encoded as strings. + else if( OFTInteger == eType ) + pjoProperty = json_object_new_string( poFeature->GetFieldAsString( nField ) ); + + else if( OFTReal == eType ) + pjoProperty = json_object_new_gme_double( poFeature->GetFieldAsDouble(nField) ); + + // Supported types are integer, double and string. So treating everything else as strings + else + pjoProperty = json_object_new_string( poFeature->GetFieldAsString(nField) ); + + json_object_object_add( pjoProperties, poFieldDefn->GetNameRef(), pjoProperty ); + } + int nGxId = poFeature->GetFieldIndex("gx_id"); + if (nGxId < 0) { + json_object* pjoProperty = NULL; + GIntBig nFID = poFeature->GetFID(); + + char acGxId[128]; + snprintf(acGxId, 128, "GDAL-" CPL_FRMT_GIB, nFID); + CPLDebug("GME", "gx_id is not set, so adding \"gx_id\": \"%s\" field.", + acGxId); + + pjoProperty = json_object_new_string( acGxId ); + json_object_object_add( pjoProperties, "gx_id", pjoProperty); + } + + return pjoProperties; +} + +/************************************************************************/ +/* json_object_new_gme_double() */ +/************************************************************************/ + +json_object* json_object_new_gme_double(double dfVal) + +{ + json_object* pjoD = json_object_new_double(dfVal); + json_object_set_serializer(pjoD, json_gme_double_to_string, NULL, NULL ); + + return pjoD; +} + +/************************************************************************/ +/* json_gme_double_to_string() */ +/************************************************************************/ + +static int json_gme_double_to_string(json_object *pjo, + printbuf *pb, + CPL_UNUSED int level, + CPL_UNUSED int flags) +{ + char buf[128], *p, *q; + int size; + + size = CPLsnprintf(buf, 128, "%.8f", json_object_get_double(pjo)); + p = strchr(buf, ','); + if (p) { + *p = '.'; + } else { + p = strchr(buf, '.'); + } + if (p) { + /* last useful digit, always keep 1 zero */ + p++; + for (q=p ; *q ; q++) { + if (*q!='0') p=q; + } + /* drop trailing zeroes */ + *(++p) = 0; + size = p-buf; + } + printbuf_memappend(pb, buf, size); + return size; +} + +/************************************************************************/ +/* OGRGMEParseJSON() */ +/************************************************************************/ + +json_object *OGRGMEParseJSON( const char* pszText ) +{ + if( NULL != pszText ) + { + json_tokener* jstok = NULL; + json_object* jsobj = NULL; + + jstok = json_tokener_new(); + jsobj = json_tokener_parse_ex(jstok, pszText, -1); + if( jstok->err != json_tokener_success) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JSON parsing error: %s (at offset %d)", + json_tokener_error_desc(jstok->err), jstok->char_offset); + + json_tokener_free(jstok); + return NULL; + } + json_tokener_free(jstok); + + /* JSON tree is shared for while lifetime of the reader object + * and will be released in the destructor. + */ + return jsobj; + } + + return NULL; +} + + +/************************************************************************/ +/* OGRGMEGetJSONString() */ +/* */ +/* Fetch a string field from a json_object (only an immediate */ +/* child). */ +/************************************************************************/ + +const char *OGRGMEGetJSONString(json_object *parent, + const char *field, + const char *default_value) +{ + json_object *child = json_object_object_get(parent, field); + if (child == NULL ) + return default_value; + + return json_object_get_string(child); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmejson.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmejson.h new file mode 100644 index 000000000..7799f64bd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmejson.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * $Id$ + * + * Project: Google Maps Engine (GME) API Driver + * Purpose: GME Geo JSON helper functions. + * Author: Wolf Bergenheim + * (derived from the GeoJSON driver by Mateusz) + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GME_JSON_H_INCLUDED +#define _OGR_GME_JSON_H_INCLUDED + +#include "ogr_feature.h" +#include "ogr_geometry.h" +#include + +json_object* OGRGMEFeatureToGeoJSON(OGRFeature* poFeature); +json_object* OGRGMEGeometryToGeoJSON(OGRGeometry* poGeometry); +json_object* OGRGMEGeometryCollectionToGeoJSON(OGRGeometryCollection* poGeometryCollection); +json_object* OGRGMEPointToGeoJSON( OGRPoint* poPoint ); +json_object* OGRGMEMultiPointToGeoJSON( OGRMultiPoint* poGeometry ); +json_object* OGRGMELineStringToGeoJSON( OGRLineString* poLine ); +json_object* OGRGMEMultiLineStringToGeoJSON( OGRMultiLineString* poGeometry ); +json_object* OGRGMEPolygonToGeoJSON( OGRPolygon* poPolygon ); +json_object* OGRGMEMultiPolygonToGeoJSON( OGRMultiPolygon* poGeometry ); +json_object* OGRGMECoordsToGeoJSON( double const& fX, double const& fY ); +json_object* OGRGMECoordsToGeoJSON( double const& fX, double const& fY, double const& fZ ); +json_object* OGRGMELineCoordsToGeoJSON( OGRLineString* poLine ); +json_object* OGRGMEAttributesToGeoJSON( OGRFeature* poFeature ); + +json_object* json_object_new_gme_double(double dfVal); + +json_object* OGRGMEParseJSON( const char* pszText ); +const char* OGRGMEGetJSONString(json_object *parent, + const char *field_name, + const char *default_value = NULL); + +#endif /* ndef _OGR_GME_JSON_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmelayer.cpp new file mode 100644 index 000000000..ecea24f1f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gme/ogrgmelayer.cpp @@ -0,0 +1,1074 @@ +/****************************************************************************** + * $Id$ + * + * Project: Google Maps Engine API Driver + * Purpose: OGRGMELayer Implementation. + * Author: Wolf Beregnheim + * Frank Warmerdam + * (derived from GFT driver by Even) + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gme.h" +#include "ogrgmejson.h" + +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: ogrgmetablelayer.cpp 25475 2013-01-09 09:09:59Z warmerdam $"); + +/************************************************************************/ +/* OGRGMELayer() */ +/************************************************************************/ + +OGRGMELayer::OGRGMELayer(OGRGMEDataSource* poDS, + const char* pszTableId) + +{ + CPLDebug("GME", "Opening existing layer %s", pszTableId); + this->poDS = poDS; + poSRS = new OGRSpatialReference(SRS_WKT_WGS84); + poFeatureDefn = NULL; + current_feature_page = NULL; + bDirty = false; + iBatchPatchSize = 50; + bCreateTablePending = false; + osTableId = pszTableId; + bInTransaction = false; + m_poFilterGeom = NULL; + iGxIdField = -1; + SetDescription( pszTableId ); +} + + +OGRGMELayer::OGRGMELayer(OGRGMEDataSource* poDS, + const char* pszTableName, + char ** papszOptions) + +{ + CPLDebug("GME", "Creating new layer %s", pszTableName); + this->poDS = poDS; + poSRS = new OGRSpatialReference(SRS_WKT_WGS84); + poFeatureDefn = NULL; + current_feature_page = NULL; + bDirty = false; + iBatchPatchSize = 50; + bCreateTablePending = true; + osTableName = pszTableName; + osProjectId = CSLFetchNameValue( papszOptions, "projectId" ); + osDraftACL = CSLFetchNameValueDef( papszOptions, "draftAccessList", "Map Editors" ); + osPublishedACL = CSLFetchNameValueDef( papszOptions, "publishedAccessList", "Map Viewers" ); + iGxIdField = -1; + SetDescription( pszTableName ); + // TODO: support tags and description +} + +/************************************************************************/ +/* ~OGRGMELayer() */ +/************************************************************************/ + +OGRGMELayer::~OGRGMELayer() + +{ + SyncToDisk(); + ResetReading(); + if( poSRS ) + poSRS->Release(); + if( poFeatureDefn ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGMELayer::ResetReading() + +{ + if (current_feature_page != NULL) + { + json_object_put(current_feature_page); + current_feature_page = NULL; + m_nFeaturesRead = 0; + + // TODO - clear current page. + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGMELayer::TestCapability( const char * pszCap ) + +{ + if(EQUAL(pszCap,OLCStringsAsUTF8)) + return TRUE; + else if(EQUAL(pszCap,OLCIgnoreFields)) + return TRUE; + else if(EQUAL(pszCap,OLCFastSpatialFilter)) + return TRUE; + else if(EQUAL(pszCap,OLCSequentialWrite)) + return TRUE; + else if(EQUAL(pszCap,OLCRandomWrite)) + return TRUE; + else if(EQUAL(pszCap,OLCDeleteFeature)) + return TRUE; + else if(EQUAL(pszCap,OLCTransactions)) + return TRUE; + return FALSE; +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr OGRGMELayer::SyncToDisk() + +{ + CPLDebug("GME", "SyncToDisk()"); + if (bDirty) { + if (omnpoInsertedFeatures.size() > 0) { + BatchInsert(); + } + if (omnpoUpdatedFeatures.size() > 0) { + BatchPatch(); + } + if (oListOfDeletedFeatures.size() > 0) { + BatchDelete(); + } + bDirty = false; + } + return OGRERR_NONE; +} + +/************************************************************************/ +/* FetchDescribe() */ +/************************************************************************/ + +int OGRGMELayer::FetchDescribe() +{ + CPLString osRequest = "tables/" + osTableId; + + CPLHTTPResult *psDescribe = poDS->MakeRequest(osRequest); + if (psDescribe == NULL) + return FALSE; + + CPLDebug("GME", "table doc = %s\n", psDescribe->pabyData); + + json_object *table_doc = + OGRGMEParseJSON((const char *) psDescribe->pabyData); + + CPLHTTPDestroyResult(psDescribe); + + osTableName = OGRGMEGetJSONString(table_doc, "name"); + + poFeatureDefn = new OGRFeatureDefn(osTableName); + poFeatureDefn->Reference(); + + json_object *schema_doc = json_object_object_get(table_doc, "schema"); + json_object *columns_doc = json_object_object_get(schema_doc, "columns"); + array_list *column_list = json_object_get_array(columns_doc); + + CPLString osLastGeomColumn; + + int field_count = array_list_length(column_list); + for( int i = 0; i < field_count; i++ ) + { + OGRwkbGeometryType eFieldGeomType = wkbNone; + + json_object *field_obj = (json_object*) + array_list_get_idx(column_list, i); + + const char* name = OGRGMEGetJSONString(field_obj, "name"); + OGRFieldDefn oFieldDefn(name, OFTString); + const char *type = OGRGMEGetJSONString(field_obj, "type"); + + if (EQUAL(type, "integer")) + oFieldDefn.SetType(OFTInteger); + else if (EQUAL(type, "double")) + oFieldDefn.SetType(OFTReal); + else if (EQUAL(type, "boolean")) + oFieldDefn.SetType(OFTInteger); + else if (EQUAL(type, "string")) + oFieldDefn.SetType(OFTString); + else if (EQUAL(type, "string")) { + if (EQUAL(name, "gx_id")) { + iGxIdField = i; + } + oFieldDefn.SetType(OFTString); + } + else if (EQUAL(type, "points")) + eFieldGeomType = wkbPoint; + else if (EQUAL(type, "linestrings")) + eFieldGeomType = wkbLineString; + else if (EQUAL(type, "polygons")) + eFieldGeomType = wkbPolygon; + else if (EQUAL(type, "mixedGeometry")) + eFieldGeomType = wkbGeometryCollection; + + if (eFieldGeomType == wkbNone) + { + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + else + { + CPLAssert(EQUAL(osGeomColumnName,"")); + osGeomColumnName = oFieldDefn.GetNameRef(); + poFeatureDefn->SetGeomType(eFieldGeomType); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + } + } + + json_object_put(table_doc); + return TRUE; +} + +/************************************************************************/ +/* GetPageOfFeatures() */ +/************************************************************************/ + +void OGRGMELayer::GetPageOfFeatures() +{ + CPLString osNextPageToken; + + if (current_feature_page != NULL) + { + osNextPageToken = OGRGMEGetJSONString(current_feature_page, + "nextPageToken", ""); + json_object_put(current_feature_page); + current_feature_page = NULL; + + // End of query results? + if (EQUAL(osNextPageToken,"")) + return; + } + + index_in_page = 0; + current_features_array = NULL; + +/* -------------------------------------------------------------------- */ +/* Fetch features. */ +/* -------------------------------------------------------------------- */ + CPLString osRequest = "tables/" + osTableId + "/features"; + CPLString osMoreOptions = "&maxResults=1000"; + + if (!EQUAL(osNextPageToken,"")) + { + osMoreOptions += "&pageToken="; + osMoreOptions += osNextPageToken; + } + if (!osSelect.empty()) { + CPLDebug( "GME", "found select=%s", osSelect.c_str()); + osMoreOptions += "&select="; + osMoreOptions += osSelect; + } + if (!osWhere.empty()) { + CPLDebug( "GME Layer", "found where=%s", osWhere.c_str()); + osMoreOptions += "&where="; + osMoreOptions += osWhere; + } + + if (!osIntersects.empty()) { + CPLDebug( "GME Layer", "found intersects=%s", osIntersects.c_str()); + osMoreOptions += "&intersects="; + osMoreOptions += osIntersects; + } + + CPLHTTPResult *psFeaturesResult = + poDS->MakeRequest(osRequest, osMoreOptions); + + if (psFeaturesResult == NULL) { + CPLDebug("GME", "Got NULL from MakeRequest. Something went wrong. You figure it out!"); + current_feature_page = NULL; + return; + } + CPLDebug("GME", + "features doc = %s...", + psFeaturesResult->pabyData); + +/* -------------------------------------------------------------------- */ +/* Parse result. */ +/* -------------------------------------------------------------------- */ + + current_feature_page = + OGRGMEParseJSON((const char *) psFeaturesResult->pabyData); + CPLHTTPDestroyResult(psFeaturesResult); + + current_features_array = + json_object_object_get(current_feature_page, "features"); +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRGMELayer::GetNextRawFeature() + +{ +/* -------------------------------------------------------------------- */ +/* Fetch a new page of features if needed. */ +/* -------------------------------------------------------------------- */ + if (current_feature_page == NULL + || index_in_page >= json_object_array_length(current_features_array)) + { + GetPageOfFeatures(); + } + + if (current_feature_page == NULL) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Identify our json feature. */ +/* -------------------------------------------------------------------- */ + json_object *feature_obj = + json_object_array_get_idx(current_features_array, index_in_page++); + if (feature_obj == NULL) + return NULL; + + OGRFeature *poFeature = new OGRFeature(poFeatureDefn); + +/* -------------------------------------------------------------------- */ +/* Handle properties. */ +/* -------------------------------------------------------------------- */ + json_object *properties_obj = + json_object_object_get(feature_obj, "properties"); + for (int iOGRField = 0; + iOGRField < poFeatureDefn->GetFieldCount(); + iOGRField++ ) + { + const char *pszValue = + OGRGMEGetJSONString( + properties_obj, + poFeatureDefn->GetFieldDefn(iOGRField)->GetNameRef(), + NULL); + if (pszValue != NULL) { + poFeature->SetField(iOGRField, pszValue); + } + } +/* -------------------------------------------------------------------- */ +/* Handle gx_id. */ +/* -------------------------------------------------------------------- */ + const char *gx_id = OGRGMEGetJSONString(properties_obj, "gx_id"); + if (gx_id) { + CPLString gmeId(gx_id); + omnosIdToGMEKey[(int)(++m_nFeaturesRead)] = gmeId; + poFeature->SetFID(m_nFeaturesRead); + CPLDebug("GME", "Mapping ids: \"%s\" to %d", gx_id, (int)m_nFeaturesRead); + } + +/* -------------------------------------------------------------------- */ +/* Handle geometry. */ +/* -------------------------------------------------------------------- */ + json_object *geometry_obj = + json_object_object_get(feature_obj, "geometry"); + OGRGeometry *poGeometry = NULL; + + if (geometry_obj != NULL) + { + poGeometry = OGRGeoJSONReadGeometry(geometry_obj); + } + + if (poGeometry != NULL) + { + poGeometry->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poGeometry); + } + + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRGMELayer::GetNextFeature() +{ + OGRFeature *poFeature = NULL; + + while( TRUE ) + { + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + break; + + // incremeted in GetNextRawFeature() + // m_nFeaturesRead++; + + if( (m_poFilterGeom == NULL + || poFeature->GetGeometryRef() == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + break; + + delete poFeature; + } + + return poFeature; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRGMELayer::GetLayerDefn() +{ + if (poFeatureDefn == NULL) + { + if (osTableId.size() == 0) + return NULL; + if (!FetchDescribe()) + return NULL; + } + + return poFeatureDefn; +} + + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRGMELayer::SetAttributeFilter( const char *pszWhere ) +{ + OGRErr eErr; + eErr = OGRLayer::SetAttributeFilter(pszWhere); + if( eErr == OGRERR_NONE ) { + if ( pszWhere ) { + char * pszEscaped = CPLEscapeString(pszWhere, -1, CPLES_URL); + osWhere = CPLString(pszEscaped); + CPLFree(pszEscaped); + } + else { + osWhere = ""; + } + } + return eErr; +} + +/************************************************************************/ +/* SetIgnoredFields() */ +/************************************************************************/ + +OGRErr OGRGMELayer::SetIgnoredFields(const char ** papszFields ) +{ + osSelect = "geometry"; + OGRErr eErr; + eErr = OGRLayer::SetIgnoredFields(papszFields); + + if( eErr == OGRERR_NONE ) { + for ( int iOGRField = 0; iOGRField < poFeatureDefn->GetFieldCount(); iOGRField++ ) + { + if (!poFeatureDefn->GetFieldDefn(iOGRField)->IsIgnored()) { + osSelect += ","; + osSelect += poFeatureDefn->GetFieldDefn(iOGRField)->GetNameRef(); + } + } + } + return eErr; +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRGMELayer::SetSpatialFilter( OGRGeometry *poGeomIn) +{ + if (poGeomIn == NULL) { + osIntersects.clear(); + OGRLayer::SetSpatialFilter( poGeomIn ); + return; + } + switch( poGeomIn->getGeometryType() ) + { + case wkbPolygon: + WindPolygonCCW((OGRPolygon *) poGeomIn); + case wkbPoint: + case wkbLineString: + if( poGeomIn == NULL ) { + osIntersects = ""; + } + else { + char * pszWkt; + poGeomIn->exportToWkt(&pszWkt); + char * pszEscaped = CPLEscapeString(pszWkt, -1, CPLES_URL); + osIntersects = CPLString(pszEscaped); + CPLFree(pszEscaped); + CPLFree(pszWkt); + } + ResetReading(); + break; + default: + m_iGeomFieldFilter = 0; + if( InstallFilter( poGeomIn ) ) + ResetReading(); + break; + } +} + +/************************************************************************/ +/* WindPolygonCCW() */ +/************************************************************************/ + +OGRPolygon* OGRGMELayer::WindPolygonCCW( OGRPolygon *poPolygon ) +{ + CPLAssert( NULL != poPolygon ); + + OGRLinearRing* poRing = poPolygon->getExteriorRing(); + if (poRing == NULL) { + return poPolygon; + } + + // If the linear ring is CW re-wind it CCW + if (poRing->isClockwise() ) { + poRing->reverseWindingOrder(); + } + + /* Interior rings. */ + const int nCount = poPolygon->getNumInteriorRings(); + for( int i = 0; i < nCount; ++i ) { + poRing = poPolygon->getInteriorRing( i ); + if (poRing == NULL) + continue; + // If the linear ring is CW re-wind it CCW + + if (poRing->isClockwise() ) { + poRing->reverseWindingOrder(); + } + } + + return poPolygon; +} + + +/************************************************************************/ +/* BatchPatch() */ +/************************************************************************/ + +OGRErr OGRGMELayer::BatchPatch() +{ + CPLDebug("GME", "BatchPatch() - <%d>", (int)oListOfDeletedFeatures.size() ); + return BatchRequest("batchPatch", omnpoUpdatedFeatures); +} + +/************************************************************************/ +/* BatchInsert() */ +/************************************************************************/ + +OGRErr OGRGMELayer::BatchInsert() +{ + CPLDebug("GME", "BatchInsert() - <%d>", (int)oListOfDeletedFeatures.size() ); + return BatchRequest("batchInsert", omnpoInsertedFeatures); +} + +/************************************************************************/ +/* BatchDelete() */ +/************************************************************************/ + +OGRErr OGRGMELayer::BatchDelete() +{ + json_object *pjoBatchDelete = json_object_new_object(); + json_object *pjoGxIds = json_object_new_array(); + std::vector::const_iterator fit; + CPLDebug("GME", "BatchDelete() - <%d>", (int)oListOfDeletedFeatures.size() ); + if (oListOfDeletedFeatures.size() == 0) { + CPLDebug("GME", "Empty list, not doing BatchDelete"); + return OGRERR_NONE; + } + for ( fit = oListOfDeletedFeatures.begin(); fit != oListOfDeletedFeatures.end(); fit++) + { + GIntBig nFID = *fit; + if (nFID > 0) { + CPLString osGxId(omnosIdToGMEKey[(int)nFID]); + CPLDebug("GME", "Deleting feature " CPL_FRMT_GIB " -> '%s'", nFID, osGxId.c_str()); + json_object *pjoGxId = json_object_new_string(osGxId.c_str()); + omnosIdToGMEKey.erase((int)nFID); + json_object_array_add( pjoGxIds, pjoGxId ); + } + } + oListOfDeletedFeatures.clear(); + if (json_object_array_length(pjoGxIds) == 0) + return OGRERR_FAILURE; + json_object_object_add( pjoBatchDelete, "gx_ids", pjoGxIds ); + const char *body = + json_object_to_json_string_ext(pjoBatchDelete, + JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY); + +/* -------------------------------------------------------------------- */ +/* POST changes */ +/* -------------------------------------------------------------------- */ + CPLString osRequest = "tables/" + osTableId + "/features/batchDelete"; + CPLHTTPResult *poBatchDeleteResult = poDS->PostRequest(osRequest, body); + if (poBatchDeleteResult) { + CPLDebug("GME", "batchDelete returned %d", poBatchDeleteResult->nStatus); + return OGRERR_NONE; + } + else { + CPLDebug("GME", "batchPatch failed, NULL was returned."); + CPLError(CE_Failure, CPLE_AppDefined, "Server error for batchDelete"); + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* BatchRequest() */ +/************************************************************************/ + +OGRErr OGRGMELayer::BatchRequest(const char *pszMethod, std::map &omnpoFeatures) +{ + json_object *pjoBatchDoc = json_object_new_object(); + json_object *pjoFeatures = json_object_new_array(); + std::map::const_iterator fit; + CPLDebug("GME", "BatchRequest('%s', <%d>)", pszMethod, (int)omnpoFeatures.size() ); + if (omnpoFeatures.size() == 0) { + CPLDebug("GME", "Empty map, not doing '%s'", pszMethod); + return OGRERR_NONE; + } + for ( fit = omnpoFeatures.begin(); fit != omnpoFeatures.end(); fit++) + { + GIntBig nFID = fit->first; + OGRFeature *poFeature = fit->second; + CPLDebug("GME", "Processing feature: " CPL_FRMT_GIB, nFID ); + json_object *pjoFeature = OGRGMEFeatureToGeoJSON(poFeature); + + if (pjoFeature != NULL) + json_object_array_add( pjoFeatures, pjoFeature ); + delete poFeature; + } + omnpoFeatures.clear(); + if (json_object_array_length(pjoFeatures) == 0) + return OGRERR_FAILURE; + json_object_object_add( pjoBatchDoc, "features", pjoFeatures ); + const char *body = + json_object_to_json_string_ext(pjoBatchDoc, + JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY); + +/* -------------------------------------------------------------------- */ +/* POST changes */ +/* -------------------------------------------------------------------- */ + CPLString osRequest = "tables/" + osTableId + "/features/" + pszMethod; + CPLHTTPResult *psBatchResult = poDS->PostRequest(osRequest, body); + if (psBatchResult) { + CPLDebug("GME", "%s returned %d", pszMethod, psBatchResult->nStatus); + return OGRERR_NONE; + } + else { + CPLDebug("GME", "%s failed, NULL was returned.", pszMethod ); + CPLError(CE_Failure, CPLE_AppDefined, "Server error for %s", pszMethod); + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* SetBatchPatchSize() */ +/************************************************************************/ + +void OGRGMELayer::SetBatchPatchSize(unsigned int iSize) + +{ + iBatchPatchSize = iSize; +} + +/************************************************************************/ +/* GetBatchPatchSize() */ +/************************************************************************/ + +unsigned int OGRGMELayer::GetBatchPatchSize() + +{ + CPLString osBatchPatchSize; + osBatchPatchSize = CPLGetConfigOption("GME_BATCH_PATCH_SIZE","0"); + int iSize = atoi( osBatchPatchSize.c_str() ); + if (iSize < 1) + return iBatchPatchSize; + else { + iBatchPatchSize = iSize; + return (unsigned int) iSize; + } +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRGMELayer::ICreateFeature( OGRFeature *poFeature ) + +{ + if (!poFeature) + return OGRERR_FAILURE; + if (!CreateTableIfNotCreated()) { + return OGRERR_FAILURE; + } + + GIntBig nFID = ++m_nFeaturesRead; + poFeature->SetFID(nFID); + + int nGxId = poFeature->GetFieldIndex("gx_id"); + CPLDebug("GME", "gx_id is field %d", iGxIdField); + CPLString osGxId; + CPLDebug("GME", "Inserting feature " CPL_FRMT_GIB " as %s", poFeature->GetFID(), osGxId.c_str()); + if (nGxId >= 0) { + iGxIdField = nGxId; + if(poFeature->IsFieldSet(iGxIdField)) { + osGxId = poFeature->GetFieldAsString(iGxIdField); + CPLDebug("GME", "Feature already has " CPL_FRMT_GIB " gx_id='%s'", poFeature->GetFID(), + osGxId.c_str()); + } + else { + osGxId = CPLSPrintf("GDAL-" CPL_FRMT_GIB "", nFID); + CPLDebug("GME", "Setting field %d as %s", iGxIdField, osGxId.c_str() ); + poFeature->SetField( iGxIdField, osGxId.c_str() ); + } + } + + if (bInTransaction) { + unsigned int iBatchSize = GetBatchPatchSize(); + if (omnpoInsertedFeatures.size() >= iBatchSize) { + CPLDebug("GME", "BatchInsert, reached BatchSize of %d", iBatchSize); + OGRErr iBatchInsertResult = BatchInsert(); + if (iBatchInsertResult != OGRERR_NONE) { + return iBatchInsertResult; + } + } + omnosIdToGMEKey[(int)poFeature->GetFID()] = osGxId; + omnpoInsertedFeatures[(int)nFID] = poFeature->Clone(); + CPLDebug("GME", "In Transaction, added feature to memory only"); + bDirty = true; + return OGRERR_NONE; + } + else { + CPLDebug("GME", "Not in Transaction, BatchInsert()"); + return BatchInsert(); + } +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRGMELayer::ISetFeature( OGRFeature *poFeature ) + +{ + if (!poFeature) + return OGRERR_FAILURE; + GIntBig nFID = poFeature->GetFID(); + if(bInTransaction) { + std::map::const_iterator fit; + fit = omnpoInsertedFeatures.find((int)nFID); + if (fit != omnpoInsertedFeatures.end()) { + omnpoInsertedFeatures[(int)nFID] = poFeature->Clone(); + CPLDebug("GME", "Updated Feature " CPL_FRMT_GIB " in Transaction", nFID); + } + else { + unsigned int iBatchSize = GetBatchPatchSize(); + if (omnpoUpdatedFeatures.size() >= iBatchSize) { + CPLDebug("GME", "BatchPatch, reached BatchSize of %d", iBatchSize); + OGRErr iBatchInsertResult = BatchPatch(); + if (iBatchInsertResult != OGRERR_NONE) { + return iBatchInsertResult; + } + } + CPLDebug("GME", "In Transaction, add update to Transaction"); + bDirty = true; + omnpoUpdatedFeatures[(int)nFID] = poFeature->Clone(); + } + return OGRERR_NONE; + } + else { + omnpoUpdatedFeatures[(int)nFID] = poFeature->Clone(); + CPLDebug("GME", "Not in Transaction, BatchPatch()"); + return BatchPatch(); + } +} + +/************************************************************************/ +/* DeleteteFeature() */ +/************************************************************************/ + +OGRErr OGRGMELayer::DeleteFeature( GIntBig nFID ) +{ + if(bInTransaction) { + std::map::iterator fit; + fit = omnpoInsertedFeatures.find((int)nFID); + if (fit != omnpoInsertedFeatures.end()) { + omnpoInsertedFeatures.erase(fit); + CPLDebug("GME", "Found " CPL_FRMT_GIB " in omnpoInsertedFeatures", nFID); + } + else { + unsigned int iBatchSize = GetBatchPatchSize(); + if (oListOfDeletedFeatures.size() >= iBatchSize) { + CPLDebug("GME", "BatchDelete, reached BatchSize of %d", iBatchSize); + OGRErr iBatchResult = BatchDelete(); + if (iBatchResult != OGRERR_NONE) { + return iBatchResult; + } + } + CPLDebug("GME", "In Transaction, adding feature to List"); + bDirty = true; + oListOfDeletedFeatures.push_back(nFID); + } + return OGRERR_NONE; + } + else { + CPLDebug("GME", "Not in Transaction, BatchDelete()"); + return BatchDelete(); + } +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRGMELayer::CreateField( OGRFieldDefn *poField, + CPL_UNUSED int bApproxOK ) +{ + CPLDebug("GME", "create field %s of type %s, pending = %d", + poField->GetNameRef(), OGRFieldDefn::GetFieldTypeName(poField->GetType()), + bCreateTablePending); + if (!bCreateTablePending) { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot add field to table after schema is defined."); + return OGRERR_FAILURE; + } + + if (poFeatureDefn == NULL) { + poFeatureDefn = new OGRFeatureDefn( osTableName ); + + poFeatureDefn->Reference(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + poFeatureDefn->GetGeomFieldDefn(0)->SetName("geometry"); + } + poFeatureDefn->AddFieldDefn(poField); + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateTableIfNotCreated() */ +/************************************************************************/ + +bool OGRGMELayer::CreateTableIfNotCreated() +{ + if (!bCreateTablePending || (osTableId.size() != 0)) { + CPLDebug("GME", "Not creating table since already created"); + CPLDebug("GME", "bCreateTablePending = %d osTableId ='%s'", + bCreateTablePending, osTableId.c_str()); + return true; + } + CPLDebug("GME", "Creating table..."); + + json_object *pjoCreateDoc = json_object_new_object(); + json_object *pjoProjectId = json_object_new_string( osProjectId.c_str() ); + json_object_object_add( pjoCreateDoc, "projectId", pjoProjectId ); + json_object *pjoName = json_object_new_string( osTableName.c_str() ); + json_object_object_add( pjoCreateDoc, "name", pjoName ); + json_object *pjoDraftACL = json_object_new_string( osDraftACL.c_str() ); + json_object_object_add( pjoCreateDoc, "draftAccessList", pjoDraftACL ); + json_object *pjoPublishedACL = json_object_new_string( osPublishedACL.c_str() ); + json_object_object_add( pjoCreateDoc, "publishedAccessList", pjoPublishedACL ); + json_object *pjoSchema = json_object_new_object(); + + json_object *pjoColumns = json_object_new_array(); + + poFeatureDefn->SetGeomType( eGTypeForCreation ); + + json_object *pjoGeometryColumn = json_object_new_object(); + json_object *pjoGeometryName = json_object_new_string( "geometry" ); + json_object *pjoGeometryType; + switch(eGTypeForCreation) { + case wkbPoint: + case wkbPoint25D: + case wkbMultiPoint: + case wkbMultiPoint25D: + pjoGeometryType = json_object_new_string( "points" ); + break; + case wkbLineString: + case wkbLineString25D: + case wkbMultiLineString: + case wkbLinearRing: + case wkbMultiLineString25D: + pjoGeometryType = json_object_new_string( "lineStrings" ); + break; + case wkbPolygon: + case wkbPolygon25D: + case wkbMultiPolygon: + case wkbGeometryCollection: + case wkbMultiPolygon25D: + pjoGeometryType = json_object_new_string( "polygons" ); + break; + case wkbGeometryCollection25D: + pjoGeometryType = json_object_new_string( "mixedGeometry" ); + break; + default: + CPLError(CE_Failure, CPLE_AppDefined, + "Unsupported Geometry type. Defaulting to Points"); + pjoGeometryType = json_object_new_string( "points" ); + poFeatureDefn->SetGeomType( wkbPoint ); + } + json_object_object_add( pjoGeometryColumn, "name", pjoGeometryName ); + json_object_object_add( pjoGeometryColumn, "type", pjoGeometryType ); + json_object_array_add( pjoColumns, pjoGeometryColumn ); + + for (int iOGRField = 0; iOGRField < poFeatureDefn->GetFieldCount(); iOGRField++ ) + { + if ((iOGRField == iGxIdField) && (iGxIdField >= 0)) + continue; // don't create the gx_id field. + const char *pszFieldName = poFeatureDefn->GetFieldDefn(iOGRField)->GetNameRef(); + if (EQUAL(pszFieldName, "gx_id")) { + iGxIdField = iOGRField; + continue; + } + json_object *pjoColumn = json_object_new_object(); + json_object *pjoFieldName = + json_object_new_string( pszFieldName ); + json_object *pjoFieldType; + + switch(poFeatureDefn->GetFieldDefn(iOGRField)->GetType()) { + case OFTInteger: + pjoFieldType = json_object_new_string( "integer" ); + break; + case OFTReal: + pjoFieldType = json_object_new_string( "double" ); + break; + default: + pjoFieldType = json_object_new_string( "string" ); + } + json_object_object_add( pjoColumn, "name", pjoFieldName ); + json_object_object_add( pjoColumn, "type", pjoFieldType ); + json_object_array_add( pjoColumns, pjoColumn ); + } + + json_object_object_add( pjoSchema, "columns", pjoColumns ); + json_object_object_add( pjoCreateDoc, "schema", pjoSchema ); + const char *body = + json_object_to_json_string_ext(pjoCreateDoc, + JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY); + + CPLDebug("GME", "Create Table Doc:\n%s", body); + +/* -------------------------------------------------------------------- */ +/* POST changes */ +/* -------------------------------------------------------------------- */ + CPLString osRequest = "tables"; + CPLHTTPResult *poCreateResult = poDS->PostRequest(osRequest, body); + if( poCreateResult == NULL || poCreateResult->pabyData == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Table creation failed."); + if( poCreateResult ) + CPLHTTPDestroyResult(poCreateResult); + return false; + } + CPLDebug("GME", "CreateTable returned %d\n%s", poCreateResult->nStatus, + poCreateResult->pabyData); + + json_object *pjoResponseDoc = OGRGMEParseJSON((const char *) poCreateResult->pabyData); + + osTableId = OGRGMEGetJSONString(pjoResponseDoc, "id", ""); + CPLHTTPDestroyResult(poCreateResult); + if (osTableId.size() == 0) { + CPLError(CE_Failure, CPLE_AppDefined, + "Table creation failed, or could not find table id."); + return false; + } + + /* + OGRFieldDefn *poGxIdField = new OGRFieldDefn("gx_id", OFTString); + + poFeatureDefn->AddFieldDefn(poGxIdField); + iGxIdField = poFeatureDefn->GetFieldCount() - 1; + CPLDebug("GME", "create field %s(%d) of type %s", + "gx_id", iGxIdField, OGRFieldDefn::GetFieldTypeName(OFTString)); + */ + bCreateTablePending = false; + CPLDebug("GME", "sleeping 3s to give GME time to create the table..."); + CPLSleep( 3.0 ); + return true; +} + +/************************************************************************/ +/* SetGeometryType() */ +/************************************************************************/ + +void OGRGMELayer::SetGeometryType(OGRwkbGeometryType eGType) +{ + eGTypeForCreation = eGType; +} + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +OGRErr OGRGMELayer::StartTransaction() +{ + if (bInTransaction) + { + CPLError(CE_Failure, CPLE_AppDefined, "Already in transaction"); + return OGRERR_FAILURE; + } + + if (!poDS->IsReadWrite()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Operation not available in read-only mode"); + return OGRERR_FAILURE; + } + + bInTransaction = TRUE; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr OGRGMELayer::CommitTransaction() +{ + if (!bInTransaction) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot commit, not in transaction"); + return OGRERR_FAILURE; + } + bInTransaction = FALSE; + return SyncToDisk(); +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +OGRErr OGRGMELayer::RollbackTransaction() +{ + if (!bInTransaction) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot rollback, not in transaction."); + return OGRERR_FAILURE; + } + bInTransaction = FALSE; + omnpoUpdatedFeatures.clear(); + omnpoInsertedFeatures.clear(); + oListOfDeletedFeatures.clear(); + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/GNUmakefile new file mode 100644 index 000000000..33cc8831c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/GNUmakefile @@ -0,0 +1,42 @@ + + +include ../../../GDALmake.opt + +CORE_OBJ = gmlpropertydefn.o gmlfeatureclass.o gmlfeature.o gmlreader.o \ + parsexsd.o resolvexlinks.o hugefileresolver.o gmlutils.o \ + gmlreadstate.o gmlhandler.o trstring.o gfstemplate.o gmlregistry.o + +OGR_OBJ = ogrgmldriver.o ogrgmldatasource.o ogrgmllayer.o + +OBJ = $(CORE_OBJ) $(OGR_OBJ) + +CPPFLAGS := -I.. -I../.. $(XERCES_INCLUDE) $(EXPAT_INCLUDE) $(SQLITE_INC) $(CPPFLAGS) +#CFLAGS := $(filter-out -Wall,$(CFLAGS)) + +ifeq ($(HAVE_XERCES),yes) +CPPFLAGS += -DHAVE_XERCES +endif + +ifeq ($(HAVE_EXPAT),yes) +CPPFLAGS += -DHAVE_EXPAT +endif + +ifeq ($(HAVE_SQLITE),yes) +CPPFLAGS += -DHAVE_SQLITE +endif + +# By default, XML validation is disabled. Uncomment the following line to +# enable XML schema validation in the parser. +#CPPFLAGS += -DOGR_GML_VALIDATION=1 + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o gmlview $(O_OBJ) + +gmlview: gmlview.cpp $(CORE_OBJ:.o=.$(OBJ_EXT)) + $(CXX) $(CPPFLAGS) $(CFLAGS) $(LNK_FLAGS) \ + gmlview.cpp $(CORE_OBJ:.o=.$(OBJ_EXT)) $(GDAL_LIB) $(LIBS) \ + -o gmlview + +$(O_OBJ): ogr_gml.h gmlreader.h gmlreaderp.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/drv_gml.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/drv_gml.html new file mode 100644 index 000000000..514d0f77f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/drv_gml.html @@ -0,0 +1,995 @@ + + +GML - Geography Markup Language + + + + +

    GML - Geography Markup Language

    + +OGR has limited support for GML reading and writing. Update of existing +files is not supported.

    + +Supported GML flavors : + + + + + + + +
    OGR versionReadWrite
    OGR >= 1.8.0GML2 and GML3 that can
    be translated into simple feature model
    GML 2.1.2 or GML 3 SF-0
    (GML 3.1.1 Compliance level SF-0)
    OGR < 1.8.0GML2 and limited GML3GML 2.1.2
    +

    + +

    Parsers

    + +The reading part of the driver only +works if OGR is built with Xerces linked in. Starting with OGR 1.7.0, when +Xerces is unavailable, read support also works if OGR is built with Expat linked in. +XML validation is disabled by default. +GML writing is always supported, even without Xerces or Expat.

    + +Note: starting with OGR 1.9.0, if both Xerces and Expat are available at build time, +the GML driver will preferentially select at runtime the Expat parser for cases where it is possible +(GML file in a compatible encoding), and default back to Xerces parser in other cases. +However, the choice of the parser can be overriden by specifying the GML_PARSER +configuration option to EXPAT or XERCES.

    + +

    CRS support

    + +Since OGR 1.8.0, the GML driver has coordinate system support. This is +only reported when all the geometries of a layer have a srsName attribute, +whose value is the same for all geometries. For srsName such as "urn:ogc:def:crs:EPSG:", +for geographic coordinate systems (as returned by WFS 1.1.0 for example), the axis order +should be (latitude, longitude) as required by the standards, but this is +unusual and can cause issues with applications unaware of axis order. So +by default, the driver will swap the coordinates so that they are in the +(longitude, latitude) order and report a SRS without axis order specified. +It is possible to get the original (latitude, longitude) order and SRS with axis order +by setting the configuration option GML_INVERT_AXIS_ORDER_IF_LAT_LONG +to NO.

    +There also situations where the srsName is of the form "EPSG:XXXX" (whereas +"urn:ogc:def:crs:EPSG::XXXX" would have been more explicit on the intent) and +the coordinates in the file are in (latitude, longitude) order. By default, OGR will +not consider the EPSG axis order and will report the coordinates in (latitude,longitude) +order. However, if you set the configuration option GML_CONSIDER_EPSG_AS_URN to +YES, the rules explained in the previous paragraph will be applied.

    +Since OGR 1.10, the above also applied for projected coordinate systems whose EPSG +preferred axis order is (northing, easting).

    + +

    Schema

    + +In contrast to most GML readers, the OGR GML reader does not require +the presence of an XML Schema definition of the feature classes (file with .xsd +extension) to be able to read the GML file. If the .xsd file is absent or +OGR is not able to parse it, the driver attempts to automatically discover +the feature classes and their associated properties by scanning the file and +looking for "known" gml objects in the gml namespace to determine the organization. +While this approach is error prone, it has the advantage of working for GML files +even if the associated schema (.xsd) file has been lost.

    + +Starting with OGR 1.10, it is possible to specify an explicit filename for the XSD +schema to use, by using "a_filename.gml,xsd=another_filename.xsd" as a connection string. +Staring with GDAL 2.0, the XSD can also be specified as the value of the XSD open option.

    + +The first time a GML file is opened, if the associated .xsd is absent or could not been +parsed correctly, it is completely scanned in order to +determine the set of featuretypes, the attributes associated with each and +other dataset level information. This information is stored in a .gfs file +with the same basename as the target gml file. Subsequent accesses to the +same GML file will use the .gfs file to predefine dataset level information +accelerating access. To a limited extent the .gfs file can be manually edited +to alter how the GML file will be parsed. Be warned that the .gfs file will +be ignored if the associated .gml file has a newer timestamp. +

    + +When prescanning the GML file to determine the list of feature types, and +fields, the contents of fields are scanned to try and determine the type of the +field. In some applications it is easier if all fields are just treated as +string fields. This can be accomplished by setting the configuration option +GML_FIELDTYPES to the value ALWAYS_STRING.

    + +Starting with GDAL 1.11, the GML_ATTRIBUTES_TO_OGR_FIELDS configuration +option can be set to YES so that attributes of GML elements are also +taken into account to create OGR fields.

    + +Configuration options can be set via the CPLSetConfigOption() function or as +environment variables.

    + +

    Particular GML application schemas

    + +OGR 1.8.0 adds support for detecting feature attributes in nested GML elements +(non-flat attribute hierarchy) that can be found in some GML profiles +such as UK Ordnance Survey MasterMap. OGR 1.8.0 also brings support for reading +IntegerList, RealList and StringList field types when a GML element has several occurences.

    + +Since OGR 1.8.0, a specialized GML driver - the NAS driver - is available to read +German AAA GML Exchange Format (NAS/ALKIS).

    + +Since OGR 1.8.0, the GML driver has partial support for reading AIXM or CityGML files.

    + +Since OGR 1.11, the GML driver supports reading : +

    + +Since OGR 2.0, the GML driver supports reading responses to CSW GetRecords +queries.

    + +

    Geometry reading

    + +When reading a feature, the driver will by default only take into account the +last recognized GML geometry found (in case they are multiples) in the XML subtree describing the feature.

    + +But, starting with OGR 1.11, if the .xsd schema is understood by the XSD parser and declares +several geometry fields, or the .gfs file declares several geometry fields, multiple geometry +fields will be reported by the GML driver according to +RFC 41.

    + +Starting with OGR 1.10, in case of multiple geometry occurences, if a geometry is in a <geometry> element, +this will be the one selected. This will make default behaviour consistent with Inspire objects.

    + + +Starting with OGR 1.8.0, the user can change the .gfs file to select the appropriate +geometry by specifying its path with the <GeometryElementPath> element. See the +description of the .gfs syntax below.

    + + + + + +OGR 1.8.0 adds support for more GML geometries including TopoCurve, +TopoSurface, MultiCurve. The TopoCurve type GML geometry can be interpreted as +either of two types of geometries. The Edge elements in it contain curves and +their corresponding nodes. By default only the curves, the main geometries, +are reported as OGRMultiLineString. To retrieve the nodes, as OGRMultiPoint, +the configuration option GML_GET_SECONDARY_GEOM should be set to the +value YES. When this is set only the secondary geometries are +reported.

    + +Starting with GDAL 2.0, Arc, ArcString, ArcByBulge, ArcByCenterPoint, Circle +and CircleByCenterPoints will be returned as circular string OGR geometries. +If they are included in other GML elements such as CurveComposite, MultiCurve, Surface, +corresponding non-linear OGR geometries will be returned as well. When +reading GML3 application schemas, declarations of geometry fields such as +CurvePropertyType, SurfacePropertyType, MultiCurvePropertyType or MultiSurfacePropertyType +will be also interpreted as being potential non-linear geometries, and corresponding +OGR geometry type will be used for the layer geometry type.

    + +

    gml:xlink resolving

    + +OGR 1.8.0 adds support for gml:xlink resolving. When the resolver finds an +element containing the tag xlink:href, it tries to find the corresponding +element with the gml:id in the same gml file, other gml file in the file system +or on the web using cURL. Set the configuration option +GML_SKIP_RESOLVE_ELEMS to NONE to enable resolution.

    + +By default the resolved file will be saved in the +same directory as the original file with the extension ".resolved.gml", if it +doesn't exist already. This behaviour can be changed using the configuration +option GML_SAVE_RESOLVED_TO. Set it to SAME to overwrite the +original file. Set it to a filename ending with .gml to save it to that +location. Any other values are ignored. If the resolver cannot write to the +file for any reason, it will try to save it to a temperary file generated using +CPLGenerateTempFilename("ResolvedGML"); if it cannot, resolution fails.

    + +Note that the resolution algorithm is not optimised for large files. For files +with more than a couple of thousand xlink:href tags, the process can go beyond +a few minutes. A rough progress is displayed through CPLDebug() for every +256 links. It can be seen by setting the environment variable CPL_DEBUG. +The resolution time can be reduced if you know any elements that +won't be needed. Mention a comma seperated list of names of such elements with +the configuration option GML_SKIP_RESOLVE_ELEMS. Set it to ALL +to skip resolving altogether (default action). Set it to NONE to +resolve all the xlinks.

    + +Starting since OGR 1.9.0 an alternative resolution method is available. +This alternative method will be activated using the configuration option +GML_SKIP_RESOLVE_ELEMS HUGE. In this case any gml:xlink will be +resolved using a temporary SQLite DB so to identify any corresponding +gml:id relation. At the end of this SQL-based process, a resolved file +will be generated exactly as in the NONE case but without their limits. +The main advantages in using an external (temporary) DBMS so to resolve +gml:xlink and gml:id relations are the followings:

      +
    • no memory size constraints. The NONE method stores the whole + GML node-tree in-memory; and this practically means that no GML + file bigger than 1 GB can be processed at all using a 32-bit + platform, due to memory allocation limits. Using a file-system + based DBMS avoids at all this issue.
    • +
    • by far better efficiency, most notably when huge GML files containing + many thousands (or even millions) of xlink:href / gml:id relational + pairs.
    • +
    • using the GML_SKIP_RESOLVE_ELEMS HUGE method realistically allows + to succesfully resolve some really huge GML file (3GB+) containing many + millions xlink:href / gml:id in a reasonable time (about an hour or so on).
    • +
    • The GML_SKIP_RESOLVE_ELEMS HUGE method supports the followind further + configuration option:
        +
      • you can use GML_GFS_TEMPLATE path_to_template.gfs + in order to unconditionally use a predefined GFS file. This option is really useful + when you are planning to import many distinct GML files in subsequent steps [-append] + and you absolutely want to preserve a fully consistent data layout for the whole GML set. + Please, pay attention not to use the -lco LAUNDER=yes setting when using GML_GFS_TEMPLATE; + this should break the correct recognition of attribute names between subsequent GML import runs. +
      • +
    • +
    + +

    TopoSurface interpretation rules [polygons and internal holes]

    + +

    +Starting since OGR 1.9.0 the GML driver is able to recognize two different interpretation +rules for TopoSurface when a polygon contains any internal hole: +

      +
    • the previously supported interpretation rule assumed that:
        +
      • each TopoSurface may be represented as a collection of many Faces
      • +
      • positive Faces [i.e. declaring orientation="+"] are assumed to + represent the Exterior Ring of some Polygon.
      • +
      • negative Faces [i.e. declaring orientation="-"] are assumed to + represent an Interior Ring (aka hole) belonging to the latest declared + Exterior Ring.
      • +
      • ordering any Edge used to represent each Ring is important: each Edge is expected + to be exactly adjacent to the next one.
      • +
      +
    • the new interpretation rule now assumes that:
        +
      • each TopoSurface may be represented as a collection of many Faces
      • +
      • the declared orientation for any Face has nothing to deal with Exterior/Interior Rings
      • +
      • each Face is now intended to represent a complete Polygon, eventually including any possible Interior + Ring (holes)
      • +
      • the relative ordering of any Edge composing the same Face is completely not relevant
      • +
      +
    + +The newest interpretation seems to fully match GML 3 standard recommendations; so this latest +is now assumed to be the default interpretation supported by OGR.

    + +

    NOTE : Using the newest interpretation requires GDAL/OGR to be built against the GEOS library.

    + +

    Using the GML_FACE_HOLE_NEGATIVE configuration option you can anyway select the +actual interpretation to be applied when parsing GML 3 Topologies: +

      +
    • setting GML_FACE_HOLE_NEGATIVE NO (default option) will activate + the newest interpretation rule
    • +
    • but explicitly setting GML_FACE_HOLE_NEGATIVE YES still enables to activate + the old interpretation rule
    • +
    +

    + +

    Encoding issues

    + +Expat library supports reading the following built-in encodings : +
      +
    • US-ASCII
    • +
    • UTF-8
    • +
    • UTF-16
    • +
    • ISO-8859-1
    • +
    + +When used with Expat library, OGR 1.8.0 adds supports for Windows-1252 encoding ( +for previous versions, altering the encoding mentionned in the XML header to +ISO-8859-1 might work in some cases).

    + +The content returned by OGR will be encoded in UTF-8, after the conversion from the +encoding mentionned in the file header is.

    + +If the GML file is not encoded in one of the previous encodings and the only parser available +is Expat, it will not be parsed by the GML driver. You may convert it into one of the supported +encodings with the iconv utility for example and change accordingly the encoding +parameter value in the XML header.

    + +When writing a GML file, the driver expects UTF-8 content to be passed in.

    + +Note: The .xsd schema files are parsed with an integrated XML parser which +does not currently understand XML encodings specified in the XML header. It expects encoding to be always +UTF-8. If attribute names in the schema file contains non-ascii characters, it is +better to use iconv utility and convert the .xsd file into UTF-8 encoding first.

    + +

    Feature id (fid / gml:id)

    + +Starting with OGR 1.8.0, the driver exposes the content of the gml:id attribute +as a string field called gml_id, when reading GML WFS documents. When creating +a GML3 document, if a field is called gml_id, its content will also be used to +write the content of the gml:id attribute of the created feature.

    + +Starting with OGR 1.9.0, the driver autodetects the presence of a fid (GML2) +(resp. gml:id (GML3)) attribute at the beginning of the file, and, if found, +exposes it by default as a fid (resp. gml_id) field. The autodetection +can be overriden by specifying the GML_EXPOSE_FID or GML_EXPOSE_GML_ID configuration +option to YES or NO.

    + +Starting with OGR 1.9.0, when creating a GML2 document, if a field is called +fid, its content will also be used to write the content of the fid attribute of the +created feature.

    + +

    Performance issues with large multi-layer GML files.

    + +There is only one GML parser per GML datasource shared among the various layers. +By default, the GML driver will restart reading from the beginning of the file, each time a layer +is accessed for the first time, which can lead to poor performance with large GML files.

    + +Starting with OGR 1.9.0, the GML_READ_MODE configuration option can be set to SEQUENTIAL_LAYERS +if all features belonging to the same layer are written sequentially in the file. The reader will then avoid +unnecessary resets when layers are read completely one after the other. To get the best performance, +the layers must be read in the order they appear in the file.

    + +If no .xsd and .gfs files are found, the parser will detect the layout of layers when +building the .gfs file. If the layers are found to be sequential, a <SequentialLayers>true</SequentialLayers> +element will be written in the .gfs file, so that the GML_READ_MODE will be automatically +initialized to SEQUENTIAL_LAYERS if not explicitly set by the user.

    + +Starting with OGR 1.9.0, the GML_READ_MODE configuration option can be set to INTERLEAVED_LAYERS to be able +to read a GML file whose features from different layers are interleaved. In the case, the semantics of the +GetNextFeature() will be slightly altered, in a way where a NULL return does not necessarily mean that +all features from the current layer have been read, but it could also mean that there is still a feature to read, but that +belongs to another layer. In that case, the file should be read with code similar to the following one : + +

    +    int nLayerCount = poDS->GetLayerCount();
    +    int bFoundFeature;
    +    do
    +    {
    +        bFoundFeature = FALSE;
    +        for( int iLayer = 0; iLayer < nLayerCount; iLayer++ )
    +        {
    +            OGRLayer   *poLayer = poDS->GetLayer(iLayer);
    +            OGRFeature *poFeature;
    +            while((poFeature = poLayer->GetNextFeature()) != NULL)
    +            {
    +                bFoundFeature = TRUE;
    +                poFeature->DumpReadable(stdout, NULL);
    +                OGRFeature::DestroyFeature(poFeature);
    +            }
    +        }
    +    } while (bInterleaved && bFoundFeature);
    +
    + +

    Open options

    + +
      +
    • XSD=filename: (GDAL >=2.0) to specify an explicit filename for the XSD +application schema to use.
    • +
    • FORCE_SRS_DETECTION=YES/NO: (GDAL >=2.0) Force a full scan to detect the SRS of layers. +This option may be needed in the case where the .gml file is accompanied with +a .xsd. Normally in that situation, OGR would not detect the SRS, because this +requires to do a full scan of the file. Defaults to NO
    • +
    • EMPTY_AS_NULL=YES/NO: (GDAL >=2.0) By default (EMPTY_AS_NULL=YES), +fields with empty content will be reported as being NULL, instead of being an empty +string. This is the historic behaviour. However this will prevent such fields to +be declared as not-nullable if the application schema declared them as mandatory. +So this option can be set to NO to have both empty strings being report as such, +and mandatory fields being reported as not nullable.
    • +
    • GML_ATTRIBUTES_TO_OGR_FIELDS=YES/NO: (GDAL >=2.0) Whether GM +Lattributes should be reported as OGR fields. Note that this option has only an +effect the first time a GML file is opened (before the .gfs file is created), +and if it has no valid associated .xsd. Defaults to NO.
    • +
    • INVERT_AXIS_ORDER_IF_LAT_LONG=YES/NO: (GDAL >=2.0) Whether to +present SRS and coordinate ordering in traditional GIS order. Defaults to YES.
    • +
    • CONSIDER_EPSG_AS_URN=YES/NO/AUTO: (GDAL >=2.0) Whether to +consider srsName like EPSG:XXXX as respecting EPSG axis order. Defaults to AUTO.
    • +
    • READ_MODE=AUTO/STANDARD/SEQUENTIAL_LAYERS/INTERLEAVED_LAYERS: (GDAL >=2.0) +Read mode. Defaults to AUTO.
    • +
    • EXPOSE_GML_ID=YES/NO/AUTO: (GDAL >=2.0) +Whether to make feature gml:id as a gml_id attribute. Defaults to AUTO.
    • +
    • EXPOSE_FID=YES/NO/AUTO: (GDAL >=2.0) +Whether to make feature fid as a fid attribute. Defaults to AUTO.
    • +
    • DOWNLOAD_SCHEMA=YES/NO: (GDAL >=2.0) +Whether to download the remote application schema if needed (only for WFS currently). Defaults to YES.
    • +
    • REGISTRY=filename: (GDAL >=2.0) +Filename of the registry with application schemas. Defaults to {GDAL_DATA}/gml_registry.xml.
    • +
    + +

    Creation Issues

    + +On export all layers are written to a single GML file all in a single +feature collection. Each layer's name is used as the element name for +objects from that layer. Geometries are always written as the +ogr:geometryProperty element on the feature.

    + +The GML writer supports the following dataset creation options: + +

      +
    • XSISCHEMAURI: If provided, this URI will be inserted as the +schema location. Note that the schema file isn't actually accessed by OGR, so +it is up to the user to ensure it will match the schema of the OGR produced +GML data file.

      + +

    • XSISCHEMA: This can be EXTERNAL, INTERNAL or OFF and defaults to +EXTERNAL. This writes a GML application schema file to a corresponding .xsd file +(with the same basename). If INTERNAL is used the schema is written within the GML +file, but this is experimental and almost certainly not valid XML. +OFF disables schema generation (and is implicit if XSISCHEMAURI is used).

      + +

    • PREFIX (OGR >= 1.10) Defaults to 'ogr'. This is the prefix for the application target namespace.

      + +
    • STRIP_PREFIX (OGR >= 1.11) Defaults to FALSE. Can be set to TRUE to avoid writing +the prefix of the application target namespace in the GML file.

      + +
    • TARGET_NAMESPACE (OGR >= 1.10) Defaults to 'http://ogr.maptools.org/'. This is the application target namespace.

      + +
    • FORMAT: (OGR >= 1.8.0) This can be set to : +
        +
      • GML3 in order to write GML files that follow GML 3.1.1 SF-0 profile.
      • +
      • GML3Deegree (OGR >= 1.9.0) in order to produce a GML 3.1.1 .XSD schema, +with a few variations with respect to what is recommended by GML3 SF-0 profile, +but that will be better accepted by some software (such as Deegree 3).
      • +
      • GML3.2(OGR >= 1.9.0) in order to write GML files that follow GML 3.2.1 SF-0 profile.
      • +

      +If not specified, GML2 will be used.
      +Starting with GDAL 2.0, non-linear geometries can be written. This is only compatible +with selecting on of that above GML3 format variant. Otherwise, such geometries will be +approximating into their closest matching linear geometry. +
      +Note: starting with OGR 1.11, fields of type StringList, RealList or IntegerList can be written. This +will cause to advertize the SF-1 profile in the .XSD schema (such types are not supported by SF-0).
      + +

      + +

    • GML3_LONGSRS=YES/NO. (OGR >= 1.8.0, only valid when FORMAT=GML3/GML3Degree/GML3.2) Default to YES. If YES, SRS with EPSG authority will +be written with the "urn:ogc:def:crs:EPSG::" prefix. +In the case, if the SRS is a geographic SRS without explicit AXIS order, but that the same SRS authority code +imported with ImportFromEPSGA() should be treated as lat/long, then the function will take care of coordinate order swapping. +If set to NO, SRS with EPSG authority will be written with the "EPSG:" prefix, even if they are in lat/long order.

      + +

    • SRSDIMENSION_LOC=POSLIST/GEOMETRY/GEOMETRY,POSLIST. (Only valid for FORMAT=GML3, GDAL >= 2.0) Default to POSLIST. +For 2.5D geometries, define the location where to attach the srsDimension attribute. +There are diverging implementations. Some put in on the <gml:posList> element, other +on the top geometry element.

      + +

    • WRITE_FEATURE_BOUNDED_BY=YES/NO. (OGR >= 1.11, only valid when FORMAT=GML3/GML3Degree/GML3.2) Default to YES. +If set to NO, the <gml:boundedBy> element will not be written for each feature.

      + +

    • SPACE_INDENTATION=YES/NO. (OGR >= 1.8.0) Default to YES. If YES, the output will be indented with spaces +for more readability, but at the expense of file size.

      + +

    • SPACE_INDENTATION=YES/NO. (OGR >= 1.8.0) Default to YES. If YES, the output will be indented with spaces +for more readability, but at the expense of file size.

      +on the top geometry element.

      + +

    • GML_ID=string. (Only valid for GML 3.2, GDAL >= 2.0) +Value of feature collection gml:id. Default value is "aFeatureCollection".

      + +

    • NAME=string. Content of GML name element. Can also be set as the +NAME metadata item on the dataset.

      + +

    • DESCRIPTION=string. Content of GML description element. Can also be set as the +DESCRIPTION metadata item on the dataset.

      + +

    + +

    VSI Virtual File System API support

    + +(Some features below might require OGR >= 1.9.0)

    + +The driver supports reading and writing to files managed by VSI Virtual File System API, which include +"regular" files, as well as files in the /vsizip/ (read-write) , /vsigzip/ (read-write) , /vsicurl/ (read-only) domains.

    + +Writing to /dev/stdout or /vsistdout/ is also supported. Note that in that case, only the content of the GML file +will be written to the standard output (and not the .xsd). The <boundedBy> element will not be written. This is also +the case if writing in /vsigzip/

    + +

    Syntax of .gfs file by example

    + +Let's consider the following test.gml file : + +
    +<?xml version="1.0" encoding="UTF-8"?>
    +<gml:FeatureCollection xmlns:gml="http://www.opengis.net/gml">
    +  <gml:featureMember>
    +    <LAYER>
    +      <attrib1>attrib1_value</attrib1>
    +      <attrib2container>
    +        <attrib2>attrib2_value</attrib2>
    +      </attrib2container>
    +      <location1container>
    +        <location1>
    +            <gml:Point><gml:coordinates>3,50</gml:coordinates></gml:Point>
    +        </location1>
    +      </location1container>
    +      <location2>
    +        <gml:Point><gml:coordinates>2,49</gml:coordinates></gml:Point>
    +      </location2>
    +    </LAYER>
    +  </gml:featureMember>
    +</gml:FeatureCollection>
    +
    + +and the following associated .gfs file. + +
    +<GMLFeatureClassList>
    +  <GMLFeatureClass>
    +    <Name>LAYER</Name>
    +    <ElementPath>LAYER</ElementPath>
    +    <GeometryElementPath>location1container|location1</GeometryElementPath>
    +    <PropertyDefn>
    +      <Name>attrib1</Name>
    +      <ElementPath>attrib1</ElementPath>
    +      <Type>String</Type>
    +      <Width>13</Width>
    +    </PropertyDefn>
    +    <PropertyDefn>
    +      <Name>attrib2</Name>
    +      <ElementPath>attrib2container|attrib2</ElementPath>
    +      <Type>String</Type>
    +      <Width>13</Width>
    +    </PropertyDefn>
    +  </GMLFeatureClass>
    +</GMLFeatureClassList>
    +
    + +Note the presence of the '|' character in the <ElementPath> and <GeometryElementPath> elements to +specify the wished field/geometry element that is a nested XML element. Nested field elements are only supported from OGR 1.8.0, as well + as specifying <GeometryElementPath> If GeometryElementPath is not specified, the +GML driver will use the last recognized geometry element.

    + +The <GeometryType> element can be specified to force the geometry type. +Accepted values are : 0 (any geometry type), 1 (point), 2 (linestring), 3 (polygon), +4 (multipoint), 5 (multilinestring), 6 (multipolygon), 7 (geometrycollection).

    + +Starting with OGR 1.11, the <GeometryElementPath> and <GeometryType> can be +specified as many times as there are geometry fields in the GML file. Another possibility +is to define a <GeomPropertyDefn>element as many times as necessary: +

    +<GMLFeatureClassList>
    +  <GMLFeatureClass>
    +    <Name>LAYER</Name>
    +    <ElementPath>LAYER</ElementPath>
    +    <GeomPropertyDefn>
    +        <Name>geometry</Name> <-- OGR geometry name -->
    +        <ElementPath>geometry</ElementPath> <!-- XML element name possibly with '|' to specify the path -->
    +        <Type>MultiPolygon</Type>
    +    </GeomPropertyDefn>
    +    <GeomPropertyDefn>
    +        <Name>referencePoint</Name>
    +        <ElementPath>referencePoint</ElementPath>
    +        <Type>Point</Type>
    +    </GeomPropertyDefn>
    +  </GMLFeatureClass>
    +</GMLFeatureClassList>
    +
    +

    + + +The output of ogrinfo test.gml -ro -al is: +

    +Layer name: LAYER
    +Geometry: Unknown (any)
    +Feature Count: 1
    +Extent: (3.000000, 50.000000) - (3.000000, 50.000000)
    +Layer SRS WKT:
    +(unknown)
    +Geometry Column = location1container|location1
    +attrib1: String (13.0)
    +attrib2: String (13.0)
    +OGRFeature(LAYER):0
    +  attrib1 (String) = attrib1_value
    +  attrib2 (String) = attrib2_value
    +  POINT (3 50)
    +
    + +

    Advanced .gfs syntax (OGR >= 1.11)

    + +

    Specifying ElementPath to find objets embedded into top level objects

    + +Let's consider the following test.gml file : +
    +<?xml version="1.0" encoding="utf-8"?>
    +<gml:FeatureCollection xmlns:xlink="http://www.w3.org/1999/xlink"
    +                       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +                       gml:id="foo" xmlns:gml="http://www.opengis.net/gml/3.2">
    +  <gml:featureMember>
    +    <TopLevelObject gml:id="TopLevelObject.1">
    +      <content>
    +        <Object gml:id="Object.1">
    +          <geometry>
    +            <gml:Polygon gml:id="Object.1.Geometry" srsName="urn:ogc:def:crs:EPSG::4326">
    +              <gml:exterior>
    +                <gml:LinearRing>
    +                  <gml:posList srsDimension="2">48 2 49 2 49 3 48 3 48 2</gml:posList>
    +                </gml:LinearRing>
    +              </gml:exterior>
    +            </gml:Polygon>
    +          </geometry>
    +          <foo>bar</foo>
    +        </Object>
    +      </content>
    +      <content>
    +        <Object gml:id="Object.2">
    +          <geometry>
    +            <gml:Polygon gml:id="Object.2.Geometry" srsName="urn:ogc:def:crs:EPSG::4326">
    +              <gml:exterior>
    +                <gml:LinearRing>
    +                  <gml:posList srsDimension="2">-48 2 -49 2 -49 3 -48 3 -48 2</gml:posList>
    +                </gml:LinearRing>
    +              </gml:exterior>
    +            </gml:Polygon>
    +          </geometry>
    +          <foo>baz</foo>
    +        </Object>
    +      </content>
    +    </TopLevelObject>
    +  </gml:featureMember>
    +</gml:FeatureCollection>
    +
    + +By default, only the TopLevelObject object would be reported and it would only +use the second geometry. This is not the desired behaviour in that instance. +You can edit the generated .gfs and modify it like the following in order to +specify a full path to the element (top level XML element being omitted) : + +
    +<GMLFeatureClassList>
    +  <GMLFeatureClass>
    +    <Name>Object</Name>
    +    <ElementPath>featureMember|TopLevelObject|content|Object</ElementPath>
    +    <GeometryType>3</GeometryType>
    +    <PropertyDefn>
    +      <Name>foo</Name>
    +      <ElementPath>foo</ElementPath>
    +      <Type>String</Type>
    +    </PropertyDefn>
    +  </GMLFeatureClass>
    +</GMLFeatureClassList>
    +
    + +

    Getting XML attributes as OGR fields

    + +The element@attribute syntax can be used in the <ElementPath> +to specify that the value of attribute 'attribute' of element 'element' must +be fetched.

    + +Let's consider the following test.gml file : + +

    +<?xml version="1.0" encoding="UTF-8"?>
    +<gml:FeatureCollection xmlns:gml="http://www.opengis.net/gml">
    +  <gml:featureMember>
    +    <LAYER>
    +      <length unit="m">5</length>
    +    </LAYER>
    +  </gml:featureMember>
    +</gml:FeatureCollection>
    +
    + +and the following associated .gfs file. + +
    +<GMLFeatureClassList>
    +  <GMLFeatureClass>
    +    <Name>LAYER</Name>
    +    <ElementPath>LAYER</ElementPath>
    +    <GeometryType>100</GeometryType> <!-- no geometry -->
    +    <PropertyDefn>
    +      <Name>length</Name>
    +      <ElementPath>length</ElementPath>
    +      <Type>Real</Type>
    +    </PropertyDefn>
    +    <PropertyDefn>
    +      <Name>length_unit</Name>
    +      <ElementPath>length@unit</ElementPath>
    +      <Type>String</Type>
    +    </PropertyDefn>
    +  </GMLFeatureClass>
    +</GMLFeatureClassList>
    +
    + +The output of ogrinfo test.gml -ro -al is: + +
    +Layer name: LAYER
    +Geometry: None
    +Feature Count: 1
    +Layer SRS WKT:
    +(unknown)
    +gml_id: String (0.0)
    +length: Real (0.0)
    +length_unit: String (0.0)
    +OGRFeature(LAYER):0
    +  gml_id (String) = (null)
    +  length (Real) = 5
    +  length_unit (String) = m
    +
    + +

    Using conditions on XML attributes

    + +A <Condition> element can be specified as a child element of a +<PropertyDefn>. The content of the Condition follows a minimalistic +XPath syntax. It must be of the form @attrname[=|!=]'attrvalue' [and|or other_cond]*. +Note that 'and' and 'or' operators cannot be mixed (their precedence is not +taken into account).

    + +Several <PropertyDefn> can be defined with the same <ElementPath>, but with +<Condition> that must be mutually exclusive.

    + +Let's consider the following testcondition.gml file : + +

    +<?xml version="1.0" encoding="utf-8" ?>
    +<ogr:FeatureCollection
    +     xmlns:ogr="http://ogr.maptools.org/"
    +     xmlns:gml="http://www.opengis.net/gml">
    +  <gml:featureMember>
    +    <ogr:testcondition fid="testcondition.0">
    +      <ogr:name lang="en">English name</ogr:name>
    +      <ogr:name lang="fr">Nom francais</ogr:name>
    +      <ogr:name lang="de">Deutsche name</ogr:name>
    +    </ogr:testcondition>
    +  </gml:featureMember>
    +</ogr:FeatureCollection>
    +
    + +and the following associated .gfs file. + +
    +<GMLFeatureClassList>
    +  <GMLFeatureClass>
    +    <Name>testcondition</Name>
    +    <ElementPath>testcondition</ElementPath>
    +    <GeometryType>100</GeometryType>
    +    <PropertyDefn>
    +      <Name>name_en</Name>
    +      <ElementPath>name</ElementPath>
    +      <Condition>@lang='en'</Condition>
    +      <Type>String</Type>
    +    </PropertyDefn>
    +    <PropertyDefn>
    +      <Name>name_fr</Name>
    +      <ElementPath>name</ElementPath>
    +      <Condition>@lang='fr'</Condition>
    +      <Type>String</Type>
    +    </PropertyDefn>
    +    <PropertyDefn>
    +      <Name>name_others_lang</Name>
    +      <ElementPath>name@lang</ElementPath>
    +      <Condition>@lang!='en' and @lang!='fr'</Condition>
    +      <Type>StringList</Type>
    +    </PropertyDefn>
    +    <PropertyDefn>
    +      <Name>name_others</Name>
    +      <ElementPath>name</ElementPath>
    +      <Condition>@lang!='en' and @lang!='fr'</Condition>
    +      <Type>StringList</Type>
    +    </PropertyDefn>
    +  </GMLFeatureClass>
    +</GMLFeatureClassList>
    +
    + +The output of ogrinfo testcondition.gml -ro -al is: +
    +Layer name: testcondition
    +Geometry: None
    +Feature Count: 1
    +Layer SRS WKT:
    +(unknown)
    +fid: String (0.0)
    +name_en: String (0.0)
    +name_fr: String (0.0)
    +name_others_lang: StringList (0.0)
    +name_others: StringList (0.0)
    +OGRFeature(testcondition):0
    +  fid (String) = testcondition.0
    +  name_en (String) = English name
    +  name_fr (String) = Nom francais
    +  name_others_lang (StringList) = (1:de)
    +  name_others (StringList) = (1:Deutsche name)
    +
    + +

    Registry for GML application schemas (OGR >= 1.11)

    + +The "data" directory of the GDAL installation contains a "gml_registry.xml" +file that links feature types of GML application schemas to .xsd or .gfs files +that contain their definition. This is used in case no valid .gfs or .xsd file +is found next to the GML file.

    + +An alternate location for the registry file can be defined by setting its full +pathname to the GML_REGISTRY configuration option.

    + +An example of such a file is : +

    +<gml_registry>
    +    <!-- Finnish National Land Survey cadastral data -->
    +    <namespace prefix="ktjkiiwfs" uri="http://xml.nls.fi/ktjkiiwfs/2010/02" useGlobalSRSName="true">
    +        <featureType elementName="KiinteistorajanSijaintitiedot"
    +                 schemaLocation="http://xml.nls.fi/XML/Schema/sovellus/ktjkii/modules/kiinteistotietojen_kyselypalvelu_WFS/Asiakasdokumentaatio/ktjkiiwfs/2010/02/KiinteistorajanSijaintitiedot.xsd"/>
    +        <featureType elementName="PalstanTunnuspisteenSijaintitiedot"
    +                 schemaLocation="http://xml.nls.fi/XML/Schema/sovellus/ktjkii/modules/kiinteistotietojen_kyselypalvelu_WFS/Asiakasdokumentaatio/ktjkiiwfs/2010/02/palstanTunnuspisteenSijaintitiedot.xsd"/>
    +        <featureType elementName="RekisteriyksikonTietoja"
    +                 schemaLocation="http://xml.nls.fi/XML/Schema/sovellus/ktjkii/modules/kiinteistotietojen_kyselypalvelu_WFS/Asiakasdokumentaatio/ktjkiiwfs/2010/02/RekisteriyksikonTietoja.xsd"/>
    +        <featureType elementName="PalstanTietoja"
    +                 schemaLocation="http://xml.nls.fi/XML/Schema/sovellus/ktjkii/modules/kiinteistotietojen_kyselypalvelu_WFS/Asiakasdokumentaatio/ktjkiiwfs/2010/02/PalstanTietoja.xsd"/>
    +    </namespace>
    +
    +    <!-- Inspire CadastralParcels schema -->
    +    <namespace prefix="cp" uri="urn:x-inspire:specification:gmlas:CadastralParcels:3.0" useGlobalSRSName="true">
    +        <featureType elementName="BasicPropertyUnit"
    +                     gfsSchemaLocation="inspire_cp_BasicPropertyUnit.gfs"/>
    +        <featureType elementName="CadastralBoundary"
    +                     gfsSchemaLocation="inspire_cp_CadastralBoundary.gfs"/>
    +        <featureType elementName="CadastralParcel"
    +                     gfsSchemaLocation="inspire_cp_CadastralParcel.gfs"/>
    +        <featureType elementName="CadastralZoning"
    +                     gfsSchemaLocation="inspire_cp_CadastralZoning.gfs"/>
    +    </namespace>
    +
    +    <!-- Czech RUIAN (VFR) schema (v1) -->
    +    <namespace prefix="vf"
    +               uri="urn:cz:isvs:ruian:schemas:VymennyFormatTypy:v1 ../ruian/xsd/vymenny_format/VymennyFormatTypy.xsd"
    +               useGlobalSRSName="true">
    +        <featureType elementName="TypSouboru"
    +                     elementValue="OB"
    +                     gfsSchemaLocation="ruian_vf_ob_v1.gfs"/>
    +        <featureType elementName="TypSouboru"
    +                     elementValue="ST"
    +                     gfsSchemaLocation="ruian_vf_st_v1.gfs"/>
    +    </namespace>
    +</gml_registry>
    +
    + +XML schema definition (.xsd) files are pointed by the schemaLocation attribute, +whereas OGR .gfs files are pointed by the gfsSchemaLocation attribute. In both +cases, the filename can be a URL (http://, https://), an absolute filename, +or a relative filename (relative to the location of gml_registry.xml).

    + +The schema is used if and only if the namespace prefix and URI are +found in the first bytes of the GML file +(e.g. xmlns:ktjkiiwfs="http://xml.nls.fi/ktjkiiwfs/2010/02"), +and that the feature type is also detected in the first bytes of the +GML file (e.g. ktjkiiwfs:KiinteistorajanSijaintitiedot). If the +element value is defined than the schema is used only if the feature +type together with the value is found in the first bytes of the GML +file (e.g. vf:TypSouboru>OB_UKSH).

    + +

    Building junction tables

    + +The ogr_build_junction_table.py +script can be used to build a junction table from +OGR layers that contain "XXXX_href" fields. + +Let's considering the following output of a GML file with links to other features : + +
    +OGRFeature(myFeature):1
    +  gml_id (String) = myFeature.1
    +  [...]
    +  otherFeature_href (StringList) = (2:#otherFeature.10,#otherFeature.20)
    +
    +OGRFeature(myFeature):2
    +  gml_id (String) = myFeature.2
    +  [...]
    +  otherFeature_href (StringList) = (2:#otherFeature.30,#otherFeature.10)
    +
    + +After running
    ogr2ogr -f PG PG:dbname=mydb my.gml
    to +import it into PostGIS and
    python ogr_build_junction_table.py PG:dbname=mydb
    , +a myfeature_otherfeature table will be created and will +contain the following content :

    + + + + + + + +
    myfeature_gml_idotherfeature_gml_id
    myFeature.1otherFeature.10
    myFeature.1otherFeature.20
    myFeature.2otherFeature.30
    myFeature.2otherFeature.10
    + +

    Reading datasets resulting from a WFS 2.0 join queries

    + +Starting with GDAL 2.0, the GML driver can read datasets resulting from a WFS 2.0 join queries.

    + +Such datasets typically look like: +

    +<wfs:FeatureCollection xmlns:xs="http://www.w3.org/2001/XMLSchema"
    +    xmlns:app="http://app.com"
    +    xmlns:wfs="http://www.opengis.net/wfs/2.0"
    +    xmlns:gml="http://www.opengis.net/gml/3.2"
    +    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +    numberMatched="unknown" numberReturned="2" timeStamp="2015-01-01T00:00:00.000Z"
    +    xsi:schemaLocation="http://www.opengis.net/gml/3.2 http://schemas.opengis.net/gml/3.2.1/gml.xsd 
    +                        http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd">
    +  <wfs:member>
    +    <wfs:Tuple>
    +      <wfs:member>
    +        <app:table1 gml:id="table1-1">
    +          <app:foo>1</app:foo>
    +        </app:table1>
    +      </wfs:member>
    +      <wfs:member>
    +        <app:table2 gml:id="table2-1">
    +          <app:bar>2</app:bar>
    +          <app:baz>foo</app:baz>
    +          <app:geometry><gml:Point gml:id="table2-2.geom.0"><gml:pos>2 49</gml:pos></gml:Point></app:geometry>
    +        </app:table2>
    +      </wfs:member>
    +    </wfs:Tuple>
    +  </wfs:member>
    +  <wfs:member>
    +    <wfs:Tuple>
    +      <wfs:member>
    +        <app:table1 gml:id="table1-2">
    +          <app:bar>2</app:bar>
    +          <app:geometry><gml:Point gml:id="table1-1.geom.0"><gml:pos>3 50</gml:pos></gml:Point></app:geometry>
    +        </app:table1>
    +      </wfs:member>
    +      <wfs:member>
    +        <app:table2 gml:id="table2-2">
    +          <app:bar>2</app:bar>
    +          <app:baz>bar</app:baz>
    +          <app:geometry><gml:Point gml:id="table2-2.geom.0"><gml:pos>2 50</gml:pos></gml:Point></app:geometry>
    +        </app:table2>
    +      </wfs:member>
    +    </wfs:Tuple>
    +  </wfs:member>
    +</wfs:FeatureCollection>
    +
    + +

    OGR will group together the attributes from the layers participating to the +join and will prefix them with the layer name. So the above example will be +read as the following:

    +
    +OGRFeature(join_table1_table2):0
    +  table1.gml_id (String) = table1-1
    +  table1.foo (Integer) = 1
    +  table1.bar (Integer) = (null)
    +  table2.gml_id (String) = table2-1
    +  table2.bar (Integer) = 2
    +  table2.baz (String) = foo
    +  table2.geometry = POINT (2 49)
    +
    +OGRFeature(join_table1_table2):1
    +  table1.gml_id (String) = table1-2
    +  table1.foo (Integer) = (null)
    +  table1.bar (Integer) = 2
    +  table2.gml_id (String) = table2-2
    +  table2.bar (Integer) = 2
    +  table2.baz (String) = bar
    +  table1.geometry = POINT (3 50)
    +  table2.geometry = POINT (2 50)
    +
    + +

    Examples

    + +The ogr2ogr utility can be used to dump the results of a Oracle query to +GML: + +
    +ogr2ogr -f GML output.gml OCI:usr/pwd@db my_feature -where "id = 0"
    +
    + +

    +The ogr2ogr utility can be used to dump the results of a PostGIS query to +GML: + +

    +ogr2ogr -f GML output.gml PG:'host=myserver dbname=warmerda' -sql "SELECT pop_1994 from canada where province_name = 'Alberta'"
    +
    +
    + + +

    See Also

    + + + +

    Credits

    +
      +
    • Implementation for GML_SKIP_RESOLVE_ELEMS HUGE was contributed by A.Furieri, with funding from Regione Toscana
    • +
    • Support for cadastral data in Finnish National Land Survey GML and Inspire GML was funded by The Information Centre of the Ministry of Agriculture and Forestry (Tike)
    • +
    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gfstemplate.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gfstemplate.cpp new file mode 100644 index 000000000..3a12ee49a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gfstemplate.cpp @@ -0,0 +1,348 @@ +/****************************************************************************** + * $Id: gfstemplate.cpp 28481 2015-02-13 17:11:15Z rouault $ + * + * Project: GML Reader + * Purpose: Implementation of GML GFS template management + * Author: Alessandro Furieri, a.furitier@lqt.it + * + ****************************************************************************** + * Copyright (c) 2011, Alessandro Furieri + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + ****************************************************************************** + * Contributor: Alessandro Furieri, a.furieri@lqt.it + * Developed for Faunalia ( http://www.faunalia.it) with funding from + * Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE + * + ****************************************************************************/ + +#include "gmlreaderp.h" +#include "ogr_gml.h" + +CPL_CVSID("$Id: gfstemplate.cpp 28481 2015-02-13 17:11:15Z rouault $"); + +/************************************************************************/ +/* GFSTemplateItem */ +/************************************************************************/ + +class GFSTemplateItem +{ +private: + char *m_pszName; + int n_nItemCount; + int n_nGeomCount; + GFSTemplateItem *pNext; +public: + GFSTemplateItem( const char *pszName ); + ~GFSTemplateItem(); + const char *GetName() { return m_pszName; } + void Update( int b_has_geom ); + int GetCount() { return n_nItemCount; } + int GetGeomCount() { return n_nGeomCount; } + void SetNext( GFSTemplateItem *pN ) { pNext = pN; } + GFSTemplateItem *GetNext() { return pNext; } +}; + +/***************************************************/ +/* gmlUpdateFeatureClasses() */ +/***************************************************/ + + void gmlUpdateFeatureClasses ( GFSTemplateList *pCC, + GMLReader *pReader, + int *pbSequentialLayers ) +{ +/* updating the FeatureClass list */ + int clIdx; + for (clIdx = 0; clIdx < pReader->GetClassCount(); clIdx++) + { + GMLFeatureClass* poClass = pReader->GetClass( clIdx ); + if (poClass != NULL) + poClass->SetFeatureCount( 0 ); + } + int m_bValid = FALSE; + GFSTemplateItem *pItem = pCC->GetFirst(); + while ( pItem != NULL ) + { + /* updating Classes */ + GMLFeatureClass* poClass = pReader->GetClass( pItem->GetName() ); + if (poClass != NULL) + { + poClass->SetFeatureCount( pItem->GetCount() ); + if ( pItem->GetGeomCount() != 0 && poClass->GetGeometryPropertyCount() == 0 ) + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( "", "", wkbUnknown, -1, TRUE ) ); + m_bValid = TRUE; + } + pItem = pItem->GetNext(); + } + if ( m_bValid == TRUE && pCC->HaveSequentialLayers() == TRUE ) + *pbSequentialLayers = TRUE; +} + +/***************************************************/ +/* GMLReader::ReArrangeTemplateClasses() */ +/***************************************************/ + +int GMLReader::ReArrangeTemplateClasses ( GFSTemplateList *pCC ) +{ +/* rearranging the final FeatureClass list [SEQUENTIAL] */ + int m_nSavedClassCount = GetClassCount(); + +/* saving the previous FeatureClass list */ + GMLFeatureClass **m_papoSavedClass = (GMLFeatureClass **) + CPLMalloc( sizeof(void*) * m_nSavedClassCount ); + int clIdx; + for (clIdx = 0; clIdx < GetClassCount(); clIdx++) + { + /* tranferring any previous FeatureClass */ + m_papoSavedClass[clIdx] = m_papoClass[clIdx]; + } + +/* cleaning the previous FeatureClass list */ + SetClassListLocked( FALSE ); + CPLFree( m_papoClass ); + m_nClassCount = 0; + m_papoClass = NULL; + + GFSTemplateItem *pItem = pCC->GetFirst(); + while ( pItem != NULL ) + { + /* + * re-inserting any required FeatureClassup + * accordingly to actual SEQUENTIAL layout + */ + GMLFeatureClass* poClass = NULL; + for( int iClass = 0; iClass < m_nSavedClassCount; iClass++ ) + { + GMLFeatureClass* poItem = m_papoSavedClass[iClass]; + if( EQUAL(poItem->GetName(), pItem->GetName() )) + { + poClass = poItem; + break; + } + } + if (poClass != NULL) + { + if (poClass->GetFeatureCount() > 0) + AddClass( poClass ); + } + pItem = pItem->GetNext(); + } + SetClassListLocked( TRUE ); + +/* destroying the saved List and any unused FeatureClass */ + for( int iClass = 0; iClass < m_nSavedClassCount; iClass++ ) + { + int bUnused = TRUE; + GMLFeatureClass* poClass = m_papoSavedClass[iClass]; + for( int iClass2 = 0; iClass2 < m_nClassCount; iClass2++ ) + { + if (m_papoClass[iClass2] == poClass) + { + bUnused = FALSE; + break; + } + } + if ( bUnused == TRUE ) + delete poClass; + } + CPLFree( m_papoSavedClass ); + return 1; +} + +/***************************************************/ +/* GMLReader::PrescanForTemplate() */ +/***************************************************/ + +int GMLReader::PrescanForTemplate () +{ + int iCount = 0; + GMLFeature *poFeature; + //int bSequentialLayers = TRUE; + GFSTemplateList *pCC = new GFSTemplateList(); + + /* processing GML features */ + while( (poFeature = NextFeature()) != NULL ) + { + GMLFeatureClass *poClass = poFeature->GetClass(); + const CPLXMLNode* const * papsGeomList = poFeature->GetGeometryList(); + int b_has_geom = FALSE; + + if( papsGeomList != NULL ) + { + int i = 0; + const CPLXMLNode *psNode = papsGeomList[i]; + while( psNode != NULL ) + { + b_has_geom = TRUE; + i++; + psNode = papsGeomList[i]; + } + } + pCC->Update( poClass->GetElementName(), b_has_geom ); + + delete poFeature; + } + + gmlUpdateFeatureClasses( pCC, this, &m_bSequentialLayers ); + if ( m_bSequentialLayers == TRUE ) + ReArrangeTemplateClasses( pCC ); + iCount = pCC->GetClassCount(); + delete pCC; + CleanupParser(); + return iCount > 0; +} + + +/***************************************************/ +/* GFSTemplateList() */ +/***************************************************/ + +GFSTemplateList::GFSTemplateList( void ) +{ + m_bSequentialLayers = TRUE; + pFirst = NULL; + pLast = NULL; +} + +/***************************************************/ +/* GFSTemplateList() */ +/***************************************************/ + +GFSTemplateList::~GFSTemplateList() +{ + GFSTemplateItem *pNext; + GFSTemplateItem *pItem = pFirst; + while ( pItem != NULL ) + { + pNext = pItem->GetNext(); + delete pItem; + pItem = pNext; + } +} + +/***************************************************/ +/* GFSTemplateList::Insert() */ +/***************************************************/ + +GFSTemplateItem *GFSTemplateList::Insert( const char *pszName ) +{ + GFSTemplateItem *pItem; + pItem = new GFSTemplateItem( pszName ); + + /* inserting into the linked list */ + if( pFirst == NULL ) + pFirst = pItem; + if( pLast != NULL ) + pLast->SetNext( pItem ); + pLast = pItem; + return pItem; +} + +/***************************************************/ +/* GFSTemplateList::Update() */ +/***************************************************/ + +void GFSTemplateList::Update( const char *pszName, int bHasGeom ) +{ + GFSTemplateItem *pItem; + + if( pFirst == NULL ) + { + /* empty List: first item */ + pItem = Insert( pszName ); + pItem->Update( bHasGeom ); + return; + } + if( EQUAL(pszName, pLast->GetName() ) ) + { + /* continuing with the current Class Item */ + pLast->Update( bHasGeom ); + return; + } + + pItem = pFirst; + while( pItem != NULL ) + { + if( EQUAL(pszName, pItem->GetName() )) + { + /* Class Item previously declared: NOT SEQUENTIAL */ + m_bSequentialLayers = FALSE; + pItem->Update( bHasGeom ); + return; + } + pItem = pItem->GetNext(); + } + + /* inserting a new Class Item */ + pItem = Insert( pszName ); + pItem->Update( bHasGeom ); +} + + +/***************************************************/ +/* GFSTemplateList::GetClassCount() */ +/***************************************************/ + +int GFSTemplateList::GetClassCount( ) +{ + int iCount = 0; + GFSTemplateItem *pItem; + + pItem = pFirst; + while( pItem != NULL ) + { + iCount++; + pItem = pItem->GetNext(); + } + + return iCount; +} + +/***************************************************/ +/* GFSTemplateItem() */ +/***************************************************/ + +GFSTemplateItem::GFSTemplateItem( const char *pszName ) +{ + m_pszName = CPLStrdup( pszName ); + n_nItemCount = 0; + n_nGeomCount = 0; + pNext = NULL; +} + +/***************************************************/ +/* ~GFSTemplateItem() */ +/***************************************************/ + +GFSTemplateItem::~GFSTemplateItem() +{ + CPLFree(m_pszName); +} + +/***************************************************/ +/* GFSTemplateItem::Update() */ +/***************************************************/ + +void GFSTemplateItem::Update( int bHasGeom ) +{ + n_nItemCount++; + if( bHasGeom == TRUE ) + n_nGeomCount++; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlfeature.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlfeature.cpp new file mode 100644 index 000000000..e6356db99 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlfeature.cpp @@ -0,0 +1,312 @@ +/********************************************************************** + * $Id: gmlfeature.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: GML Reader + * Purpose: Implementation of GMLFeature. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gmlreader.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +/************************************************************************/ +/* GMLFeature() */ +/************************************************************************/ + +GMLFeature::GMLFeature( GMLFeatureClass *poClass ) + +{ + m_poClass = poClass; + m_pszFID = NULL; + + m_nPropertyCount = 0; + m_pasProperties = NULL; + + m_nGeometryCount = 0; + m_papsGeometry = m_apsGeometry; + m_apsGeometry[0] = NULL; + m_apsGeometry[1] = NULL; + + m_papszOBProperties = NULL; +} + +/************************************************************************/ +/* ~GMLFeature() */ +/************************************************************************/ + +GMLFeature::~GMLFeature() + +{ + CPLFree( m_pszFID ); + + int i; + for( i = 0; i < m_nPropertyCount; i++ ) + { + int nSubProperties = m_pasProperties[i].nSubProperties; + if (nSubProperties == 1) + CPLFree( m_pasProperties[i].aszSubProperties[0] ); + else if (nSubProperties > 1) + { + for( int j = 0; j < nSubProperties; j++) + CPLFree( m_pasProperties[i].papszSubProperties[j] ); + CPLFree( m_pasProperties[i].papszSubProperties ); + } + } + + if (m_nGeometryCount == 1) + { + CPLDestroyXMLNode(m_apsGeometry[0]); + } + else if (m_nGeometryCount > 1) + { + for(i=0;i= m_nPropertyCount ) + { + int nClassPropertyCount = m_poClass->GetPropertyCount(); + m_pasProperties = (GMLProperty*) + CPLRealloc( m_pasProperties, + sizeof(GMLProperty) * nClassPropertyCount ); + int i; + for( i = 0; i < m_nPropertyCount; i ++ ) + { + /* Make sure papszSubProperties point to the right address in case */ + /* m_pasProperties has been relocated */ + if (m_pasProperties[i].nSubProperties <= 1) + m_pasProperties[i].papszSubProperties = m_pasProperties[i].aszSubProperties; + } + for( i = m_nPropertyCount; i < nClassPropertyCount; i++ ) + { + m_pasProperties[i].nSubProperties = 0; + m_pasProperties[i].papszSubProperties = m_pasProperties[i].aszSubProperties; + m_pasProperties[i].aszSubProperties[0] = NULL; + m_pasProperties[i].aszSubProperties[1] = NULL; + } + m_nPropertyCount = nClassPropertyCount; + } + + GMLProperty* psProperty = &m_pasProperties[iIndex]; + int nSubProperties = psProperty->nSubProperties; + if (nSubProperties == 0) + psProperty->aszSubProperties[0] = pszValue; + else if (nSubProperties == 1) + { + psProperty->papszSubProperties = (char**) CPLMalloc( + sizeof(char*) * (nSubProperties + 2) ); + psProperty->papszSubProperties[0] = psProperty->aszSubProperties[0]; + psProperty->aszSubProperties[0] = NULL; + psProperty->papszSubProperties[nSubProperties] = pszValue; + psProperty->papszSubProperties[nSubProperties + 1] = NULL; + } + else + { + psProperty->papszSubProperties = (char**) CPLRealloc( + psProperty->papszSubProperties, + sizeof(char*) * (nSubProperties + 2) ); + psProperty->papszSubProperties[nSubProperties] = pszValue; + psProperty->papszSubProperties[nSubProperties + 1] = NULL; + } + psProperty->nSubProperties ++; +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +void GMLFeature::Dump( CPL_UNUSED FILE * fp ) +{ + printf( "GMLFeature(%s):\n", m_poClass->GetName() ); + + if( m_pszFID != NULL ) + printf( " FID = %s\n", m_pszFID ); + + int i; + for( i = 0; i < m_nPropertyCount; i++ ) + { + const GMLProperty * psGMLProperty = GetProperty( i ); + printf( " %s = ", m_poClass->GetProperty( i )->GetName()); + for ( int j = 0; j < psGMLProperty->nSubProperties; j ++) + { + if (j > 0) printf(", "); + printf("%s", psGMLProperty->papszSubProperties[j]); + } + printf("\n"); + } + + for(i=0;i 0 && m_nGeometryCount <= 1 ) + { + m_papsGeometry = (CPLXMLNode **) CPLMalloc(2 * sizeof(CPLXMLNode *)); + m_papsGeometry[0] = m_apsGeometry[0]; + m_papsGeometry[1] = NULL; + m_apsGeometry[0] = NULL; + } + + if( nIdx >= m_nGeometryCount ) + { + m_papsGeometry = (CPLXMLNode **) CPLRealloc(m_papsGeometry, + (nIdx + 2) * sizeof(CPLXMLNode *)); + for( int i = m_nGeometryCount; i <= nIdx + 1; i++ ) + m_papsGeometry[i] = NULL; + m_nGeometryCount = nIdx + 1; + } + if (m_papsGeometry[nIdx] != NULL) + CPLDestroyXMLNode(m_papsGeometry[nIdx]); + m_papsGeometry[nIdx] = psGeom; +} + +/************************************************************************/ +/* GetGeometryRef() */ +/************************************************************************/ + +const CPLXMLNode* GMLFeature::GetGeometryRef( int nIdx ) const +{ + if( nIdx < 0 || nIdx >= m_nGeometryCount ) + return NULL; + return m_papsGeometry[nIdx]; +} + +/************************************************************************/ +/* AddGeometry() */ +/************************************************************************/ + +void GMLFeature::AddGeometry( CPLXMLNode* psGeom ) + +{ + if (m_nGeometryCount == 0) + { + m_apsGeometry[0] = psGeom; + } + else if (m_nGeometryCount == 1) + { + m_papsGeometry = (CPLXMLNode **) CPLMalloc( + (m_nGeometryCount + 2) * sizeof(CPLXMLNode *)); + m_papsGeometry[0] = m_apsGeometry[0]; + m_apsGeometry[0] = NULL; + m_papsGeometry[m_nGeometryCount] = psGeom; + m_papsGeometry[m_nGeometryCount + 1] = NULL; + } + else + { + m_papsGeometry = (CPLXMLNode **) CPLRealloc(m_papsGeometry, + (m_nGeometryCount + 2) * sizeof(CPLXMLNode *)); + m_papsGeometry[m_nGeometryCount] = psGeom; + m_papsGeometry[m_nGeometryCount + 1] = NULL; + } + m_nGeometryCount ++; +} + +/************************************************************************/ +/* AddOBProperty() */ +/************************************************************************/ + +void GMLFeature::AddOBProperty( const char *pszName, const char *pszValue ) + +{ + m_papszOBProperties = + CSLAddNameValue( m_papszOBProperties, pszName, pszValue ); +} + +/************************************************************************/ +/* GetOBProperty() */ +/************************************************************************/ + +const char *GMLFeature::GetOBProperty( const char *pszName ) + +{ + return CSLFetchNameValue( m_papszOBProperties, pszName ); +} + +/************************************************************************/ +/* GetOBProperties() */ +/************************************************************************/ + +char **GMLFeature::GetOBProperties() + +{ + return m_papszOBProperties; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlfeatureclass.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlfeatureclass.cpp new file mode 100644 index 000000000..91017920c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlfeatureclass.cpp @@ -0,0 +1,948 @@ +/********************************************************************** + * $Id: gmlfeatureclass.cpp 28909 2015-04-15 12:52:53Z rouault $ + * + * Project: GML Reader + * Purpose: Implementation of GMLFeatureClass. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gmlreader.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_core.h" +#include "ogr_geometry.h" +#include "cpl_string.h" + +/************************************************************************/ +/* GMLFeatureClass() */ +/************************************************************************/ + +GMLFeatureClass::GMLFeatureClass( const char *pszName ) + +{ + m_pszName = CPLStrdup( pszName ); + n_nNameLen = strlen( m_pszName ); + m_pszElementName = NULL; + n_nElementNameLen = 0; + m_nPropertyCount = 0; + m_papoProperty = NULL; + m_nGeometryPropertyCount = 0; + m_papoGeometryProperty = NULL; + m_bSchemaLocked = FALSE; + + m_pszExtraInfo = NULL; + m_bHaveExtents = FALSE; + m_nFeatureCount = -1; // unknown + + m_pszSRSName = NULL; + m_bSRSNameConsistent = TRUE; +} + +/************************************************************************/ +/* ~GMLFeatureClass() */ +/************************************************************************/ + +GMLFeatureClass::~GMLFeatureClass() + +{ + CPLFree( m_pszName ); + CPLFree( m_pszElementName ); + + for( int i = 0; i < m_nPropertyCount; i++ ) + delete m_papoProperty[i]; + CPLFree( m_papoProperty ); + + ClearGeometryProperties(); + + CPLFree( m_pszSRSName ); +} + +/************************************************************************/ +/* StealProperties() */ +/************************************************************************/ + +void GMLFeatureClass::StealProperties() +{ + m_nPropertyCount = 0; + CPLFree( m_papoProperty ); + m_papoProperty = NULL; +} + +/************************************************************************/ +/* StealGeometryProperties() */ +/************************************************************************/ + +void GMLFeatureClass::StealGeometryProperties() +{ + m_nGeometryPropertyCount = 0; + CPLFree( m_papoGeometryProperty ); + m_papoGeometryProperty = NULL; +} + +/************************************************************************/ +/* SetName() */ +/************************************************************************/ + +void GMLFeatureClass::SetName(const char* pszNewName) +{ + CPLFree( m_pszName ); + m_pszName = CPLStrdup( pszNewName ); +} + +/************************************************************************/ +/* GetProperty(int) */ +/************************************************************************/ + +GMLPropertyDefn *GMLFeatureClass::GetProperty( int iIndex ) const + +{ + if( iIndex < 0 || iIndex >= m_nPropertyCount ) + return NULL; + else + return m_papoProperty[iIndex]; +} + +/************************************************************************/ +/* GetPropertyIndex() */ +/************************************************************************/ + +int GMLFeatureClass::GetPropertyIndex( const char *pszName ) const + +{ + for( int i = 0; i < m_nPropertyCount; i++ ) + if( EQUAL(pszName,m_papoProperty[i]->GetName()) ) + return i; + + return -1; +} + +/************************************************************************/ +/* GetPropertyIndexBySrcElement() */ +/************************************************************************/ + +int GMLFeatureClass::GetPropertyIndexBySrcElement( const char *pszElement, int nLen ) const + +{ + for( int i = 0; i < m_nPropertyCount; i++ ) + if( nLen == (int)m_papoProperty[i]->GetSrcElementLen() && + memcmp(pszElement,m_papoProperty[i]->GetSrcElement(), nLen) == 0) + return i; + + return -1; +} + +/************************************************************************/ +/* AddProperty() */ +/************************************************************************/ + +int GMLFeatureClass::AddProperty( GMLPropertyDefn *poDefn ) + +{ + if( GetProperty(poDefn->GetName()) != NULL ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Field with same name (%s) already exists. Skipping newer ones", + poDefn->GetName()); + return -1; + } + + m_nPropertyCount++; + m_papoProperty = (GMLPropertyDefn **) + CPLRealloc( m_papoProperty, sizeof(void*) * m_nPropertyCount ); + + m_papoProperty[m_nPropertyCount-1] = poDefn; + + return m_nPropertyCount-1; +} + +/************************************************************************/ +/* GetGeometryProperty(int) */ +/************************************************************************/ + +GMLGeometryPropertyDefn *GMLFeatureClass::GetGeometryProperty( int iIndex ) const +{ + if( iIndex < 0 || iIndex >= m_nGeometryPropertyCount ) + return NULL; + else + return m_papoGeometryProperty[iIndex]; +} + +/************************************************************************/ +/* GetGeometryPropertyIndexBySrcElement() */ +/************************************************************************/ + +int GMLFeatureClass::GetGeometryPropertyIndexBySrcElement( const char *pszElement ) const + +{ + for( int i = 0; i < m_nGeometryPropertyCount; i++ ) + if( strcmp(pszElement,m_papoGeometryProperty[i]->GetSrcElement()) == 0) + return i; + + return -1; +} + +/************************************************************************/ +/* AddGeometryProperty() */ +/************************************************************************/ + +int GMLFeatureClass::AddGeometryProperty( GMLGeometryPropertyDefn *poDefn ) + +{ + if( GetGeometryPropertyIndexBySrcElement(poDefn->GetSrcElement()) >= 0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Field with same name (%s) already exists. Skipping newer ones", + poDefn->GetSrcElement()); + return -1; + } + + m_nGeometryPropertyCount++; + m_papoGeometryProperty = (GMLGeometryPropertyDefn **) + CPLRealloc( m_papoGeometryProperty, sizeof(void*) * m_nGeometryPropertyCount ); + + m_papoGeometryProperty[m_nGeometryPropertyCount-1] = poDefn; + + return m_nGeometryPropertyCount-1; +} + +/************************************************************************/ +/* ClearGeometryProperties() */ +/************************************************************************/ + +void GMLFeatureClass::ClearGeometryProperties() +{ + for( int i = 0; i < m_nGeometryPropertyCount; i++ ) + delete m_papoGeometryProperty[i]; + CPLFree( m_papoGeometryProperty ); + m_nGeometryPropertyCount = 0; + m_papoGeometryProperty = NULL; +} + +/************************************************************************/ +/* HasFeatureProperties() */ +/************************************************************************/ + +int GMLFeatureClass::HasFeatureProperties() +{ + for( int i = 0; i < m_nPropertyCount; i++ ) + { + if( m_papoProperty[i]->GetType() == GMLPT_FeatureProperty || + m_papoProperty[i]->GetType() == GMLPT_FeaturePropertyList ) + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* SetElementName() */ +/************************************************************************/ + +void GMLFeatureClass::SetElementName( const char *pszElementName ) + +{ + CPLFree( m_pszElementName ); + m_pszElementName = CPLStrdup( pszElementName ); + n_nElementNameLen = strlen(pszElementName); +} + +/************************************************************************/ +/* GetElementName() */ +/************************************************************************/ + +const char *GMLFeatureClass::GetElementName() const + +{ + if( m_pszElementName == NULL ) + return m_pszName; + else + return m_pszElementName; +} + +/************************************************************************/ +/* GetElementName() */ +/************************************************************************/ + +size_t GMLFeatureClass::GetElementNameLen() const + +{ + if( m_pszElementName == NULL ) + return n_nNameLen; + else + return n_nElementNameLen; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig GMLFeatureClass::GetFeatureCount() + +{ + return m_nFeatureCount; +} + +/************************************************************************/ +/* SetFeatureCount() */ +/************************************************************************/ + +void GMLFeatureClass::SetFeatureCount( GIntBig nNewCount ) + +{ + m_nFeatureCount = nNewCount; +} + +/************************************************************************/ +/* GetExtraInfo() */ +/************************************************************************/ + +const char *GMLFeatureClass::GetExtraInfo() + +{ + return m_pszExtraInfo; +} + +/************************************************************************/ +/* SetExtraInfo() */ +/************************************************************************/ + +void GMLFeatureClass::SetExtraInfo( const char *pszExtraInfo ) + +{ + CPLFree( m_pszExtraInfo ); + m_pszExtraInfo = NULL; + + if( pszExtraInfo != NULL ) + m_pszExtraInfo = CPLStrdup( pszExtraInfo ); +} + +/************************************************************************/ +/* SetExtents() */ +/************************************************************************/ + +void GMLFeatureClass::SetExtents( double dfXMin, double dfXMax, + double dfYMin, double dfYMax ) + +{ + m_dfXMin = dfXMin; + m_dfXMax = dfXMax; + m_dfYMin = dfYMin; + m_dfYMax = dfYMax; + + m_bHaveExtents = TRUE; +} + +/************************************************************************/ +/* GetExtents() */ +/************************************************************************/ + +int GMLFeatureClass::GetExtents( double *pdfXMin, double *pdfXMax, + double *pdfYMin, double *pdfYMax ) + +{ + if( m_bHaveExtents ) + { + *pdfXMin = m_dfXMin; + *pdfXMax = m_dfXMax; + *pdfYMin = m_dfYMin; + *pdfYMax = m_dfYMax; + } + + return m_bHaveExtents; +} + +/************************************************************************/ +/* SetSRSName() */ +/************************************************************************/ + +void GMLFeatureClass::SetSRSName( const char* pszSRSName ) + +{ + m_bSRSNameConsistent = TRUE; + CPLFree(m_pszSRSName); + m_pszSRSName = (pszSRSName) ? CPLStrdup(pszSRSName) : NULL; +} + +/************************************************************************/ +/* MergeSRSName() */ +/************************************************************************/ + +void GMLFeatureClass::MergeSRSName( const char* pszSRSName ) + +{ + if(!m_bSRSNameConsistent) + return; + + if( m_pszSRSName == NULL ) + { + if (pszSRSName) + m_pszSRSName = CPLStrdup(pszSRSName); + } + else + { + m_bSRSNameConsistent = pszSRSName != NULL && + strcmp(m_pszSRSName, pszSRSName) == 0; + if (!m_bSRSNameConsistent) + { + CPLFree(m_pszSRSName); + m_pszSRSName = NULL; + } + } +} + +/************************************************************************/ +/* InitializeFromXML() */ +/************************************************************************/ + +int GMLFeatureClass::InitializeFromXML( CPLXMLNode *psRoot ) + +{ +/* -------------------------------------------------------------------- */ +/* Do some rudimentary checking that this is a well formed */ +/* node. */ +/* -------------------------------------------------------------------- */ + if( psRoot == NULL + || psRoot->eType != CXT_Element + || !EQUAL(psRoot->pszValue,"GMLFeatureClass") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GMLFeatureClass::InitializeFromXML() called on %s node!", + psRoot->pszValue ); + return FALSE; + } + + if( CPLGetXMLValue( psRoot, "Name", NULL ) == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GMLFeatureClass has no element." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Collect base info. */ +/* -------------------------------------------------------------------- */ + CPLFree( m_pszName ); + m_pszName = CPLStrdup( CPLGetXMLValue( psRoot, "Name", NULL ) ); + n_nNameLen = strlen(m_pszName); + + SetElementName( CPLGetXMLValue( psRoot, "ElementPath", m_pszName ) ); + +/* -------------------------------------------------------------------- */ +/* Collect geometry properties. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psThis; + + int bHasValidGeometryName = FALSE; + int bHasValidGeometryElementPath = FALSE; + int bHasFoundGeomType = FALSE; + int bHasFoundGeomElements = FALSE; + const char* pszGName = ""; + const char* pszGPath = ""; + int nGeomType = wkbUnknown; + + for( psThis = psRoot->psChild; psThis != NULL; psThis = psThis->psNext ) + { + if( psThis->eType == CXT_Element && + EQUAL(psThis->pszValue, "GeomPropertyDefn") ) + { + const char *pszName = CPLGetXMLValue( psThis, "Name", "" ); + const char *pszElementPath = CPLGetXMLValue( psThis, "ElementPath", "" ); + const char *pszType = CPLGetXMLValue( psThis, "Type", NULL ); + int bNullable = CSLTestBoolean(CPLGetXMLValue( psThis, "Nullable", "true") ); + nGeomType = wkbUnknown; + if( pszType != NULL && !EQUAL(pszType, "0") ) + { + nGeomType = atoi(pszType); + OGRwkbGeometryType nFlattenGeomType = wkbFlatten(nGeomType); + if( nGeomType != 0 && !(nFlattenGeomType >= wkbPoint && nFlattenGeomType <= wkbMultiSurface) ) + { + nGeomType = wkbUnknown; + CPLError(CE_Warning, CPLE_AppDefined, "Unrecognised geometry type : %s", + pszType); + } + else if( nGeomType == 0 ) + nGeomType = OGRFromOGCGeomType(pszType); + } + bHasFoundGeomElements = TRUE; + AddGeometryProperty( new GMLGeometryPropertyDefn( pszName, pszElementPath, nGeomType, -1, bNullable ) ); + bHasValidGeometryName = FALSE; + bHasValidGeometryElementPath = FALSE; + bHasFoundGeomType = FALSE; + } + else if( psThis->eType == CXT_Element && + strcmp(psThis->pszValue, "GeometryName") == 0 ) + { + bHasFoundGeomElements = TRUE; + + if( bHasValidGeometryName ) + { + AddGeometryProperty( new GMLGeometryPropertyDefn( pszGName, pszGPath, nGeomType, -1, TRUE ) ); + bHasValidGeometryName = FALSE; + bHasValidGeometryElementPath = FALSE; + bHasFoundGeomType = FALSE; + pszGName = ""; + pszGPath = ""; + nGeomType = wkbUnknown; + } + pszGName = CPLGetXMLValue( psThis, NULL, "" ); + bHasValidGeometryName = TRUE; + } + else if( psThis->eType == CXT_Element && + strcmp(psThis->pszValue, "GeometryElementPath") == 0 ) + { + bHasFoundGeomElements = TRUE; + + if( bHasValidGeometryElementPath ) + { + AddGeometryProperty( new GMLGeometryPropertyDefn( pszGName, pszGPath, nGeomType, -1, TRUE ) ); + bHasValidGeometryName = FALSE; + bHasValidGeometryElementPath = FALSE; + bHasFoundGeomType = FALSE; + pszGName = ""; + pszGPath = ""; + nGeomType = wkbUnknown; + } + pszGPath = CPLGetXMLValue( psThis, NULL, "" ); + bHasValidGeometryElementPath = TRUE; + } + else if( psThis->eType == CXT_Element && + strcmp(psThis->pszValue, "GeometryType") == 0 ) + { + bHasFoundGeomElements = TRUE; + + if( bHasFoundGeomType ) + { + AddGeometryProperty( new GMLGeometryPropertyDefn( pszGName, pszGPath, nGeomType, -1, TRUE ) ); + bHasValidGeometryName = FALSE; + bHasValidGeometryElementPath = FALSE; + bHasFoundGeomType = FALSE; + pszGName = ""; + pszGPath = ""; + nGeomType = wkbUnknown; + } + const char* pszGeometryType = CPLGetXMLValue( psThis, NULL, NULL ); + nGeomType = wkbUnknown; + if( pszGeometryType != NULL && !EQUAL(pszGeometryType, "0") ) + { + nGeomType = atoi(pszGeometryType); + OGRwkbGeometryType nFlattenGeomType = wkbFlatten(nGeomType); + if( nGeomType == 100 || EQUAL(pszGeometryType, "NONE") ) + { + bHasValidGeometryElementPath = FALSE; + bHasFoundGeomType = FALSE; + break; + } + else if( nGeomType != 0 && !(nFlattenGeomType >= wkbPoint && nFlattenGeomType <= wkbMultiSurface) ) + { + nGeomType = wkbUnknown; + CPLError(CE_Warning, CPLE_AppDefined, "Unrecognised geometry type : %s", + pszGeometryType); + } + else if( nGeomType == 0 ) + nGeomType = OGRFromOGCGeomType(pszGeometryType); + } + bHasFoundGeomType = TRUE; + } + } + + /* If there was a dangling or or */ + /* that no explicit geometry information has been found, then add */ + /* a geometry field */ + if( bHasValidGeometryElementPath || bHasFoundGeomType || !bHasFoundGeomElements ) + { + AddGeometryProperty( new GMLGeometryPropertyDefn( pszGName, pszGPath, nGeomType, -1, TRUE ) ); + } + + SetSRSName( CPLGetXMLValue( psRoot, "SRSName", NULL ) ); + +/* -------------------------------------------------------------------- */ +/* Collect dataset specific info. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psDSI = CPLGetXMLNode( psRoot, "DatasetSpecificInfo" ); + if( psDSI != NULL ) + { + const char *pszValue; + + pszValue = CPLGetXMLValue( psDSI, "FeatureCount", NULL ); + if( pszValue != NULL ) + SetFeatureCount( CPLAtoGIntBig(pszValue) ); + + // Eventually we should support XML subtrees. + pszValue = CPLGetXMLValue( psDSI, "ExtraInfo", NULL ); + if( pszValue != NULL ) + SetExtraInfo( pszValue ); + + if( CPLGetXMLValue( psDSI, "ExtentXMin", NULL ) != NULL + && CPLGetXMLValue( psDSI, "ExtentXMax", NULL ) != NULL + && CPLGetXMLValue( psDSI, "ExtentYMin", NULL ) != NULL + && CPLGetXMLValue( psDSI, "ExtentYMax", NULL ) != NULL ) + { + SetExtents( CPLAtof(CPLGetXMLValue( psDSI, "ExtentXMin", "0.0" )), + CPLAtof(CPLGetXMLValue( psDSI, "ExtentXMax", "0.0" )), + CPLAtof(CPLGetXMLValue( psDSI, "ExtentYMin", "0.0" )), + CPLAtof(CPLGetXMLValue( psDSI, "ExtentYMax", "0.0" )) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Collect property definitions. */ +/* -------------------------------------------------------------------- */ + for( psThis = psRoot->psChild; psThis != NULL; psThis = psThis->psNext ) + { + if( psThis->eType == CXT_Element && + EQUAL(psThis->pszValue, "PropertyDefn") ) + { + const char *pszName = CPLGetXMLValue( psThis, "Name", NULL ); + const char *pszType = CPLGetXMLValue( psThis, "Type", "Untyped" ); + const char *pszSubType = CPLGetXMLValue( psThis, "Subtype", "" ); + const char *pszCondition = CPLGetXMLValue( psThis, "Condition", NULL ); + int bNullable = CSLTestBoolean(CPLGetXMLValue( psThis, "Nullable", "true") ); + GMLPropertyDefn *poPDefn; + + if( pszName == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GMLFeatureClass %s has a PropertyDefn without a ..", + m_pszName ); + return FALSE; + } + + poPDefn = new GMLPropertyDefn( + pszName, CPLGetXMLValue( psThis, "ElementPath", NULL ) ); + + poPDefn->SetNullable(bNullable); + if( EQUAL(pszType,"Untyped") ) + poPDefn->SetType( GMLPT_Untyped ); + else if( EQUAL(pszType,"String") ) + { + if( EQUAL(pszSubType, "Boolean") ) + { + poPDefn->SetType( GMLPT_Boolean ); + poPDefn->SetWidth( 1 ); + } + else + { + poPDefn->SetType( GMLPT_String ); + poPDefn->SetWidth( atoi( CPLGetXMLValue( psThis, "Width", "0" ) ) ); + } + } + else if( EQUAL(pszType,"Integer") ) + { + if( EQUAL(pszSubType, "Short") ) + { + poPDefn->SetType( GMLPT_Short ); + } + else if( EQUAL(pszSubType, "Integer64") ) + { + poPDefn->SetType( GMLPT_Integer64 ); + } + else + { + poPDefn->SetType( GMLPT_Integer ); + } + poPDefn->SetWidth( atoi( CPLGetXMLValue( psThis, "Width", "0" ) ) ); + } + else if( EQUAL(pszType,"Real") ) + { + if( EQUAL(pszSubType, "Float") ) + { + poPDefn->SetType( GMLPT_Float ); + } + else + { + poPDefn->SetType( GMLPT_Real ); + } + poPDefn->SetWidth( atoi( CPLGetXMLValue( psThis, "Width", "0" ) ) ); + poPDefn->SetPrecision( atoi( CPLGetXMLValue( psThis, "Precision", "0" ) ) ); + } + else if( EQUAL(pszType,"StringList") ) + { + if( EQUAL(pszSubType, "Boolean") ) + poPDefn->SetType( GMLPT_BooleanList ); + else + poPDefn->SetType( GMLPT_StringList ); + } + else if( EQUAL(pszType,"IntegerList") ) + { + if( EQUAL(pszSubType, "Integer64") ) + poPDefn->SetType( GMLPT_Integer64List ); + else + poPDefn->SetType( GMLPT_IntegerList ); + } + else if( EQUAL(pszType,"RealList") ) + poPDefn->SetType( GMLPT_RealList ); + else if( EQUAL(pszType,"Complex") ) + poPDefn->SetType( GMLPT_Complex ); + else if( EQUAL(pszType,"FeatureProperty") ) + poPDefn->SetType( GMLPT_FeatureProperty ); + else if( EQUAL(pszType,"FeaturePropertyList") ) + poPDefn->SetType( GMLPT_FeaturePropertyList ); + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised property type %s.", + pszType ); + delete poPDefn; + return FALSE; + } + if( pszCondition != NULL ) + poPDefn->SetCondition(pszCondition); + + AddProperty( poPDefn ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* SerializeToXML() */ +/************************************************************************/ + +CPLXMLNode *GMLFeatureClass::SerializeToXML() + +{ + CPLXMLNode *psRoot; + int iProperty; + +/* -------------------------------------------------------------------- */ +/* Set feature class and core information. */ +/* -------------------------------------------------------------------- */ + psRoot = CPLCreateXMLNode( NULL, CXT_Element, "GMLFeatureClass" ); + + CPLCreateXMLElementAndValue( psRoot, "Name", GetName() ); + CPLCreateXMLElementAndValue( psRoot, "ElementPath", GetElementName() ); + + if( m_nGeometryPropertyCount > 1 ) + { + for(int i=0; i < m_nGeometryPropertyCount; i++) + { + GMLGeometryPropertyDefn* poGeomFDefn = m_papoGeometryProperty[i]; + + CPLXMLNode *psPDefnNode; + psPDefnNode = CPLCreateXMLNode( psRoot, CXT_Element, "GeomPropertyDefn" ); + if( strlen(poGeomFDefn->GetName()) > 0 ) + CPLCreateXMLElementAndValue( psPDefnNode, "Name", + poGeomFDefn->GetName() ); + if( poGeomFDefn->GetSrcElement() != NULL && strlen(poGeomFDefn->GetSrcElement()) > 0 ) + CPLCreateXMLElementAndValue( psPDefnNode, "ElementPath", + poGeomFDefn->GetSrcElement() ); + + if( poGeomFDefn->GetType() != 0 /* wkbUnknown */ ) + { + char szValue[128]; + + OGRwkbGeometryType eType = (OGRwkbGeometryType)poGeomFDefn->GetType(); + + CPLString osStr(OGRToOGCGeomType(eType)); + if( wkbHasZ(eType) ) osStr += "Z"; + CPLCreateXMLNode( psPDefnNode, CXT_Comment, osStr.c_str() ); + + sprintf( szValue, "%d", eType ); + CPLCreateXMLElementAndValue( psPDefnNode, "Type", szValue ); + } + } + } + else if( m_nGeometryPropertyCount == 1 ) + { + GMLGeometryPropertyDefn* poGeomFDefn = m_papoGeometryProperty[0]; + + if( strlen(poGeomFDefn->GetName()) > 0 ) + CPLCreateXMLElementAndValue( psRoot, "GeometryName", + poGeomFDefn->GetName() ); + + if( poGeomFDefn->GetSrcElement() != NULL && strlen(poGeomFDefn->GetSrcElement()) > 0 ) + CPLCreateXMLElementAndValue( psRoot, "GeometryElementPath", + poGeomFDefn->GetSrcElement() ); + + if( poGeomFDefn->GetType() != 0 /* wkbUnknown */ ) + { + char szValue[128]; + + OGRwkbGeometryType eType = (OGRwkbGeometryType)poGeomFDefn->GetType(); + + CPLString osStr(OGRToOGCGeomType(eType)); + if( wkbHasZ(eType) ) osStr += "Z"; + CPLCreateXMLNode( psRoot, CXT_Comment, osStr.c_str() ); + + sprintf( szValue, "%d", eType ); + CPLCreateXMLElementAndValue( psRoot, "GeometryType", szValue ); + } + } + else + { + CPLCreateXMLElementAndValue( psRoot, "GeometryType", "100" ); + } + + const char* pszSRSName = GetSRSName(); + if( pszSRSName ) + { + CPLCreateXMLElementAndValue( psRoot, "SRSName", pszSRSName ); + } + +/* -------------------------------------------------------------------- */ +/* Write out dataset specific information. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psDSI; + + if( m_bHaveExtents || m_nFeatureCount != -1 || m_pszExtraInfo != NULL ) + { + psDSI = CPLCreateXMLNode( psRoot, CXT_Element, "DatasetSpecificInfo" ); + + if( m_nFeatureCount != -1 ) + { + char szValue[128]; + + sprintf( szValue, CPL_FRMT_GIB, m_nFeatureCount ); + CPLCreateXMLElementAndValue( psDSI, "FeatureCount", szValue ); + } + + if( m_bHaveExtents && + fabs(m_dfXMin) < 1e100 && + fabs(m_dfXMax) < 1e100 && + fabs(m_dfYMin) < 1e100 && + fabs(m_dfYMax) < 1e100 ) + { + char szValue[128]; + + CPLsnprintf( szValue, sizeof(szValue), "%.5f", m_dfXMin ); + CPLCreateXMLElementAndValue( psDSI, "ExtentXMin", szValue ); + + CPLsnprintf( szValue, sizeof(szValue), "%.5f", m_dfXMax ); + CPLCreateXMLElementAndValue( psDSI, "ExtentXMax", szValue ); + + CPLsnprintf( szValue, sizeof(szValue), "%.5f", m_dfYMin ); + CPLCreateXMLElementAndValue( psDSI, "ExtentYMin", szValue ); + + CPLsnprintf( szValue, sizeof(szValue), "%.5f", m_dfYMax ); + CPLCreateXMLElementAndValue( psDSI, "ExtentYMax", szValue ); + } + + if( m_pszExtraInfo ) + CPLCreateXMLElementAndValue( psDSI, "ExtraInfo", m_pszExtraInfo ); + } + +/* -------------------------------------------------------------------- */ +/* emit property information. */ +/* -------------------------------------------------------------------- */ + for( iProperty = 0; iProperty < GetPropertyCount(); iProperty++ ) + { + GMLPropertyDefn *poPDefn = GetProperty( iProperty ); + CPLXMLNode *psPDefnNode; + const char *pszTypeName = "Unknown"; + + psPDefnNode = CPLCreateXMLNode( psRoot, CXT_Element, "PropertyDefn" ); + CPLCreateXMLElementAndValue( psPDefnNode, "Name", + poPDefn->GetName() ); + CPLCreateXMLElementAndValue( psPDefnNode, "ElementPath", + poPDefn->GetSrcElement() ); + switch( poPDefn->GetType() ) + { + case GMLPT_Untyped: + pszTypeName = "Untyped"; + break; + + case GMLPT_String: + case GMLPT_Boolean: + pszTypeName = "String"; + break; + + case GMLPT_Integer: + case GMLPT_Short: + case GMLPT_Integer64: + pszTypeName = "Integer"; + break; + + case GMLPT_Real: + case GMLPT_Float: + pszTypeName = "Real"; + break; + + case GMLPT_Complex: + pszTypeName = "Complex"; + break; + + case GMLPT_IntegerList: + case GMLPT_Integer64List: + pszTypeName = "IntegerList"; + break; + + case GMLPT_RealList: + pszTypeName = "RealList"; + break; + + case GMLPT_StringList: + case GMLPT_BooleanList: + pszTypeName = "StringList"; + break; + + /* should not happen in practise for now because this is not */ + /* autodetected */ + case GMLPT_FeatureProperty: + pszTypeName = "FeatureProperty"; + break; + + /* should not happen in practise for now because this is not */ + /* autodetected */ + case GMLPT_FeaturePropertyList: + pszTypeName = "FeaturePropertyList"; + break; + } + CPLCreateXMLElementAndValue( psPDefnNode, "Type", pszTypeName ); + if( poPDefn->GetType() == GMLPT_Boolean || poPDefn->GetType() == GMLPT_BooleanList ) + CPLCreateXMLElementAndValue( psPDefnNode, "Subtype", "Boolean" ); + else if( poPDefn->GetType() == GMLPT_Short ) + CPLCreateXMLElementAndValue( psPDefnNode, "Subtype", "Short" ); + else if( poPDefn->GetType() == GMLPT_Float ) + CPLCreateXMLElementAndValue( psPDefnNode, "Subtype", "Float" ); + else if( poPDefn->GetType() == GMLPT_Integer64 || + poPDefn->GetType() == GMLPT_Integer64List ) + CPLCreateXMLElementAndValue( psPDefnNode, "Subtype", "Integer64" ); + + if( EQUAL(pszTypeName,"String") ) + { + char szMaxLength[48]; + sprintf(szMaxLength, "%d", poPDefn->GetWidth()); + CPLCreateXMLElementAndValue ( psPDefnNode, "Width", szMaxLength ); + } + if( poPDefn->GetWidth() > 0 && EQUAL(pszTypeName,"Integer") ) + { + char szLength[48]; + sprintf(szLength, "%d", poPDefn->GetWidth()); + CPLCreateXMLElementAndValue ( psPDefnNode, "Width", szLength ); + } + if( poPDefn->GetWidth() > 0 && EQUAL(pszTypeName,"Real") ) + { + char szLength[48]; + sprintf(szLength, "%d", poPDefn->GetWidth()); + CPLCreateXMLElementAndValue ( psPDefnNode, "Width", szLength ); + char szPrecision[48]; + sprintf(szPrecision, "%d", poPDefn->GetPrecision()); + CPLCreateXMLElementAndValue ( psPDefnNode, "Precision", szPrecision ); + } + } + + return psRoot; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlhandler.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlhandler.cpp new file mode 100644 index 000000000..82a00797f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlhandler.cpp @@ -0,0 +1,1850 @@ +/********************************************************************** + * $Id: gmlhandler.cpp 29217 2015-05-21 09:08:48Z rouault $ + * + * Project: GML Reader + * Purpose: Implementation of GMLHandler class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "gmlreaderp.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_hash_set.h" + +#ifdef HAVE_XERCES + +/* Must be a multiple of 4 */ +#define MAX_TOKEN_SIZE 1000 + +/************************************************************************/ +/* GMLXercesHandler() */ +/************************************************************************/ + +GMLXercesHandler::GMLXercesHandler( GMLReader *poReader ) : GMLHandler(poReader) +{ + m_nEntityCounter = 0; +} + +/************************************************************************/ +/* startElement() */ +/************************************************************************/ + +void GMLXercesHandler::startElement(CPL_UNUSED const XMLCh* const uri, + const XMLCh* const localname, + CPL_UNUSED const XMLCh* const qname, + const Attributes& attrs ) +{ + char szElementName[MAX_TOKEN_SIZE]; + + m_nEntityCounter = 0; + + /* A XMLCh character can expand to 4 bytes in UTF-8 */ + if (4 * tr_strlen( localname ) >= MAX_TOKEN_SIZE) + { + static int bWarnOnce = FALSE; + XMLCh* tempBuffer = (XMLCh*) CPLMalloc(sizeof(XMLCh) * (MAX_TOKEN_SIZE / 4 + 1)); + memcpy(tempBuffer, localname, sizeof(XMLCh) * (MAX_TOKEN_SIZE / 4)); + tempBuffer[MAX_TOKEN_SIZE / 4] = 0; + tr_strcpy( szElementName, tempBuffer ); + CPLFree(tempBuffer); + if (!bWarnOnce) + { + bWarnOnce = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, "A too big element name has been truncated"); + } + } + else + tr_strcpy( szElementName, localname ); + + if (GMLHandler::startElement(szElementName, strlen(szElementName), (void*) &attrs) == OGRERR_NOT_ENOUGH_MEMORY) + { + throw SAXNotSupportedException("Out of memory"); + } +} + +/************************************************************************/ +/* endElement() */ +/************************************************************************/ +void GMLXercesHandler::endElement(CPL_UNUSED const XMLCh* const uri, + CPL_UNUSED const XMLCh* const localname, + CPL_UNUSED const XMLCh* const qname ) +{ + m_nEntityCounter = 0; + + if (GMLHandler::endElement() == OGRERR_NOT_ENOUGH_MEMORY) + { + throw SAXNotSupportedException("Out of memory"); + } +} + +/************************************************************************/ +/* characters() */ +/************************************************************************/ + +void GMLXercesHandler::characters(const XMLCh* const chars_in, + CPL_UNUSED +#if XERCES_VERSION_MAJOR >= 3 + const XMLSize_t length +#else + const unsigned int length +#endif + ) + +{ + char* utf8String = tr_strdup(chars_in); + int nLen = strlen(utf8String); + OGRErr eErr = GMLHandler::dataHandler(utf8String, nLen); + CPLFree(utf8String); + if (eErr == OGRERR_NOT_ENOUGH_MEMORY) + { + throw SAXNotSupportedException("Out of memory"); + } +} + +/************************************************************************/ +/* fatalError() */ +/************************************************************************/ + +void GMLXercesHandler::fatalError( const SAXParseException &exception) + +{ + char *pszErrorMessage; + + pszErrorMessage = tr_strdup( exception.getMessage() ); + CPLError( CE_Failure, CPLE_AppDefined, + "XML Parsing Error: %s at line %d, column %d\n", + pszErrorMessage, (int)exception.getLineNumber(), (int)exception.getColumnNumber() ); + + CPLFree( pszErrorMessage ); +} + +/************************************************************************/ +/* startEntity() */ +/************************************************************************/ + +void GMLXercesHandler::startEntity (CPL_UNUSED const XMLCh *const name) +{ + m_nEntityCounter ++; + if (m_nEntityCounter > 1000 && !m_poReader->HasStoppedParsing()) + { + throw SAXNotSupportedException("File probably corrupted (million laugh pattern)"); + } +} + +/************************************************************************/ +/* GetFID() */ +/************************************************************************/ + +const char* GMLXercesHandler::GetFID(void* attr) +{ + const Attributes* attrs = (const Attributes*) attr; + int nFIDIndex; + XMLCh anFID[100]; + + tr_strcpy( anFID, "fid" ); + nFIDIndex = attrs->getIndex( anFID ); + if( nFIDIndex != -1 ) + { + char* pszValue = tr_strdup( attrs->getValue( nFIDIndex ) ); + osFID.assign(pszValue); + CPLFree(pszValue); + return osFID.c_str(); + } + else + { + tr_strcpy( anFID, "gml:id" ); + nFIDIndex = attrs->getIndex( anFID ); + if( nFIDIndex != -1 ) + { + char* pszValue = tr_strdup( attrs->getValue( nFIDIndex ) ); + osFID.assign(pszValue); + CPLFree(pszValue); + return osFID.c_str(); + } + } + + osFID.resize(0); + return NULL; +} + +/************************************************************************/ +/* AddAttributes() */ +/************************************************************************/ + +CPLXMLNode* GMLXercesHandler::AddAttributes(CPLXMLNode* psNode, void* attr) +{ + const Attributes* attrs = (const Attributes*) attr; + + CPLXMLNode* psLastChild = NULL; + + for(unsigned int i=0; i < attrs->getLength(); i++) + { + char* pszName = tr_strdup(attrs->getQName(i)); + char* pszValue = tr_strdup(attrs->getValue(i)); + + CPLXMLNode* psChild = CPLCreateXMLNode(NULL, CXT_Attribute, pszName); + CPLCreateXMLNode(psChild, CXT_Text, pszValue); + + CPLFree(pszName); + CPLFree(pszValue); + + if (psLastChild == NULL) + psNode->psChild = psChild; + else + psLastChild->psNext = psChild; + psLastChild = psChild; + } + + return psLastChild; +} + +/************************************************************************/ +/* GetAttributeValue() */ +/************************************************************************/ + +char* GMLXercesHandler::GetAttributeValue(void* attr, const char* pszAttributeName) +{ + const Attributes* attrs = (const Attributes*) attr; + for(unsigned int i=0; i < attrs->getLength(); i++) + { + char* pszString = tr_strdup(attrs->getQName(i)); + if (strcmp(pszString, pszAttributeName) == 0) + { + CPLFree(pszString); + return tr_strdup(attrs->getValue(i)); + } + CPLFree(pszString); + } + return NULL; +} + +/************************************************************************/ +/* GetAttributeByIdx() */ +/************************************************************************/ + +char* GMLXercesHandler::GetAttributeByIdx(void* attr, unsigned int idx, char** ppszKey) +{ + const Attributes* attrs = (const Attributes*) attr; + if( idx >= attrs->getLength() ) + { + *ppszKey = NULL; + return NULL; + } + *ppszKey = tr_strdup(attrs->getQName(idx)); + return tr_strdup(attrs->getValue(idx)); +} + +#endif + +#ifdef HAVE_EXPAT + +/************************************************************************/ +/* GMLExpatHandler() */ +/************************************************************************/ + +GMLExpatHandler::GMLExpatHandler( GMLReader *poReader, XML_Parser oParser ) : GMLHandler(poReader) + +{ + m_oParser = oParser; + m_bStopParsing = FALSE; + m_nDataHandlerCounter = 0; +} + +/************************************************************************/ +/* startElementCbk() */ +/************************************************************************/ + +void XMLCALL GMLExpatHandler::startElementCbk(void *pUserData, const char *pszName, + const char **ppszAttr) + +{ + GMLExpatHandler* pThis = ((GMLExpatHandler*)pUserData); + if (pThis->m_bStopParsing) + return; + + const char* pszIter = pszName; + char ch; + while((ch = *pszIter) != '\0') + { + if (ch == ':') + pszName = pszIter + 1; + pszIter ++; + } + + if (pThis->GMLHandler::startElement(pszName, (int)(pszIter - pszName), ppszAttr) == OGRERR_NOT_ENOUGH_MEMORY) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + pThis->m_bStopParsing = TRUE; + XML_StopParser(pThis->m_oParser, XML_FALSE); + } + +} + +/************************************************************************/ +/* endElementCbk() */ +/************************************************************************/ +void XMLCALL GMLExpatHandler::endElementCbk(void *pUserData, + CPL_UNUSED const char* pszName ) +{ + GMLExpatHandler* pThis = ((GMLExpatHandler*)pUserData); + if (pThis->m_bStopParsing) + return; + + if (pThis->GMLHandler::endElement() == OGRERR_NOT_ENOUGH_MEMORY) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + pThis->m_bStopParsing = TRUE; + XML_StopParser(pThis->m_oParser, XML_FALSE); + } +} + +/************************************************************************/ +/* dataHandlerCbk() */ +/************************************************************************/ + +void XMLCALL GMLExpatHandler::dataHandlerCbk(void *pUserData, const char *data, int nLen) + +{ + GMLExpatHandler* pThis = ((GMLExpatHandler*)pUserData); + if (pThis->m_bStopParsing) + return; + + pThis->m_nDataHandlerCounter ++; + /* The size of the buffer that is fetched and that Expat parses is */ + /* PARSER_BUF_SIZE bytes. If the dataHandlerCbk() callback is called */ + /* more than PARSER_BUF_SIZE times, this means that one byte in the */ + /* file expands to more XML text fragments, which is the sign of a */ + /* likely abuse of */ + /* Note: the counter is zeroed by ResetDataHandlerCounter() before each */ + /* new XML parsing. */ + if (pThis->m_nDataHandlerCounter >= PARSER_BUF_SIZE) + { + CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)"); + pThis->m_bStopParsing = TRUE; + XML_StopParser(pThis->m_oParser, XML_FALSE); + return; + } + + if (pThis->GMLHandler::dataHandler(data, nLen) == OGRERR_NOT_ENOUGH_MEMORY) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + pThis->m_bStopParsing = TRUE; + XML_StopParser(pThis->m_oParser, XML_FALSE); + return; + } +} + +/************************************************************************/ +/* GetFID() */ +/************************************************************************/ + +const char* GMLExpatHandler::GetFID(void* attr) +{ + const char** papszIter = (const char** )attr; + while(*papszIter) + { + if (strcmp(*papszIter, "fid") == 0 || + strcmp(*papszIter, "gml:id") == 0) + { + return papszIter[1]; + } + papszIter += 2; + } + return NULL; +} + +/************************************************************************/ +/* AddAttributes() */ +/************************************************************************/ + +CPLXMLNode* GMLExpatHandler::AddAttributes(CPLXMLNode* psNode, void* attr) +{ + const char** papszIter = (const char** )attr; + + CPLXMLNode* psLastChild = NULL; + + while(*papszIter) + { + CPLXMLNode* psChild = CPLCreateXMLNode(NULL, CXT_Attribute, papszIter[0]); + CPLCreateXMLNode(psChild, CXT_Text, papszIter[1]); + + if (psLastChild == NULL) + psNode->psChild = psChild; + else + psLastChild->psNext = psChild; + psLastChild = psChild; + + papszIter += 2; + } + + return psLastChild; +} + +/************************************************************************/ +/* GetAttributeValue() */ +/************************************************************************/ + +char* GMLExpatHandler::GetAttributeValue(void* attr, const char* pszAttributeName) +{ + const char** papszIter = (const char** )attr; + while(*papszIter) + { + if (strcmp(*papszIter, pszAttributeName) == 0) + { + return CPLStrdup(papszIter[1]); + } + papszIter += 2; + } + return NULL; +} + +/************************************************************************/ +/* GetAttributeByIdx() */ +/************************************************************************/ + +/* CAUTION: should be called with increasing idx starting from 0 and */ +/* no attempt to read beyond end of list */ +char* GMLExpatHandler::GetAttributeByIdx(void* attr, unsigned int idx, char** ppszKey) +{ + const char** papszIter = (const char** )attr; + if( papszIter[2 * idx] == NULL ) + { + *ppszKey = NULL; + return NULL; + } + *ppszKey = CPLStrdup(papszIter[2 * idx]); + return CPLStrdup(papszIter[2 * idx+1]); +} + +#endif + + +static const char* const apszGMLGeometryElements[] = +{ + "BoundingBox", /* ows:BoundingBox */ + "CompositeCurve", + "CompositeSurface", + "Curve", + "GeometryCollection", /* OGR < 1.8.0 bug... */ + "LineString", + "MultiCurve", + "MultiGeometry", + "MultiLineString", + "MultiPoint", + "MultiPolygon", + "MultiSurface", + "Point", + "Polygon", + "PolygonPatch", + "SimplePolygon", /* GML 3.3 compact encoding */ + "SimpleRectangle", /* GML 3.3 compact encoding */ + "SimpleTriangle", /* GML 3.3 compact encoding */ + "SimpleMultiPoint", /* GML 3.3 compact encoding */ + "Solid", + "Surface", + "TopoCurve", + "TopoSurface" +}; + +#define GML_GEOMETRY_TYPE_COUNT \ + (int)(sizeof(apszGMLGeometryElements) / sizeof(apszGMLGeometryElements[0])) + +struct _GeometryNamesStruct { + unsigned long nHash; + const char *pszName; +} ; + +/************************************************************************/ +/* GMLHandlerSortGeometryElements() */ +/************************************************************************/ + +static int GMLHandlerSortGeometryElements(const void *_pA, const void *_pB) +{ + GeometryNamesStruct* pA = (GeometryNamesStruct*)_pA; + GeometryNamesStruct* pB = (GeometryNamesStruct*)_pB; + CPLAssert(pA->nHash != pB->nHash); + if (pA->nHash < pB->nHash) + return -1; + else + return 1; +} + +/************************************************************************/ +/* GMLHandler() */ +/************************************************************************/ + +GMLHandler::GMLHandler( GMLReader *poReader ) + +{ + m_poReader = poReader; + m_bInCurField = FALSE; + m_nCurFieldAlloc = 0; + m_nCurFieldLen = 0; + m_pszCurField = NULL; + m_nAttributeIndex = -1; + m_nAttributeDepth = 0; + + m_pszGeometry = NULL; + m_nGeomAlloc = 0; + m_nGeomLen = 0; + m_nGeometryDepth = 0; + m_bAlreadyFoundGeometry = FALSE; + m_nGeometryPropertyIndex = 0; + + m_nDepthFeature = m_nDepth = 0; + m_inBoundedByDepth = 0; + + eAppSchemaType = APPSCHEMA_GENERIC; + + m_pszCityGMLGenericAttrName = NULL; + m_inCityGMLGenericAttrDepth = 0; + + m_bReportHref = FALSE; + m_pszHref = NULL; + m_pszUom = NULL; + m_pszValue = NULL; + m_pszKieli = NULL; + + pasGeometryNames = (GeometryNamesStruct*)CPLMalloc( + GML_GEOMETRY_TYPE_COUNT * sizeof(GeometryNamesStruct)); + for(int i=0; i= 2 && apsXMLNode[1].psNode != NULL) + CPLDestroyXMLNode(apsXMLNode[1].psNode); + + CPLFree( m_pszCurField ); + CPLFree( m_pszGeometry ); + CPLFree( m_pszCityGMLGenericAttrName ); + CPLFree( m_pszHref ); + CPLFree( m_pszUom ); + CPLFree( m_pszValue ); + CPLFree( m_pszKieli ); + CPLFree( pasGeometryNames ); +} + + +/************************************************************************/ +/* startElement() */ +/************************************************************************/ + +OGRErr GMLHandler::startElement(const char *pszName, int nLenName, void* attr) +{ + OGRErr eRet; + switch(stateStack[nStackDepth]) + { + case STATE_TOP: eRet = startElementTop(pszName, nLenName, attr); break; + case STATE_DEFAULT: eRet = startElementDefault(pszName, nLenName, attr); break; + case STATE_FEATURE: eRet = startElementFeatureAttribute(pszName, nLenName, attr); break; + case STATE_PROPERTY: eRet = startElementFeatureAttribute(pszName, nLenName, attr); break; + case STATE_FEATUREPROPERTY: eRet = startElementFeatureProperty(pszName, nLenName, attr); break; + case STATE_GEOMETRY: eRet = startElementGeometry(pszName, nLenName, attr); break; + case STATE_IGNORED_FEATURE: eRet = OGRERR_NONE; break; + case STATE_BOUNDED_BY: eRet = startElementBoundedBy(pszName, nLenName, attr); break; + case STATE_CITYGML_ATTRIBUTE: eRet = startElementCityGMLGenericAttr(pszName, nLenName, attr); break; + default: eRet = OGRERR_NONE; break; + } + m_nDepth++; + return eRet; +} + +/************************************************************************/ +/* endElement() */ +/************************************************************************/ + +OGRErr GMLHandler::endElement() +{ + m_nDepth--; + switch(stateStack[nStackDepth]) + { + case STATE_TOP: return OGRERR_NONE; break; + case STATE_DEFAULT: return endElementDefault(); break; + case STATE_FEATURE: return endElementFeature(); break; + case STATE_PROPERTY: return endElementAttribute(); break; + case STATE_FEATUREPROPERTY: return endElementFeatureProperty(); break; + case STATE_GEOMETRY: return endElementGeometry(); break; + case STATE_IGNORED_FEATURE: return endElementIgnoredFeature(); break; + case STATE_BOUNDED_BY: return endElementBoundedBy(); break; + case STATE_CITYGML_ATTRIBUTE: return endElementCityGMLGenericAttr(); break; + default: return OGRERR_NONE; break; + } +} + +/************************************************************************/ +/* dataHandler() */ +/************************************************************************/ + +OGRErr GMLHandler::dataHandler(const char *data, int nLen) +{ + switch(stateStack[nStackDepth]) + { + case STATE_TOP: return OGRERR_NONE; break; + case STATE_DEFAULT: return OGRERR_NONE; break; + case STATE_FEATURE: return OGRERR_NONE; break; + case STATE_PROPERTY: return dataHandlerAttribute(data, nLen); break; + case STATE_FEATUREPROPERTY: return OGRERR_NONE; break; + case STATE_GEOMETRY: return dataHandlerGeometry(data, nLen); break; + case STATE_IGNORED_FEATURE: return OGRERR_NONE; break; + case STATE_BOUNDED_BY: return OGRERR_NONE; break; + case STATE_CITYGML_ATTRIBUTE: return dataHandlerAttribute(data, nLen); break; + default: return OGRERR_NONE; break; + } +} + +#define PUSH_STATE(val) do { nStackDepth ++; CPLAssert(nStackDepth < STACK_SIZE); stateStack[nStackDepth] = val; } while(0) +#define POP_STATE() nStackDepth -- + +/************************************************************************/ +/* startElementBoundedBy() */ +/************************************************************************/ + +OGRErr GMLHandler::startElementBoundedBy(const char *pszName, + CPL_UNUSED int nLenName, + void* attr ) +{ + if ( m_nDepth == 2 && strcmp(pszName, "Envelope") == 0 ) + { + char* pszGlobalSRSName = GetAttributeValue(attr, "srsName"); + m_poReader->SetGlobalSRSName(pszGlobalSRSName); + CPLFree(pszGlobalSRSName); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* startElementGeometry() */ +/************************************************************************/ + +OGRErr GMLHandler::startElementGeometry(const char *pszName, int nLenName, void* attr ) +{ + if( nLenName == 9 && strcmp(pszName, "boundedBy") == 0 ) + { + m_inBoundedByDepth = m_nDepth; + + PUSH_STATE(STATE_BOUNDED_BY); + + return OGRERR_NONE; + } + + /* Create new XML Element */ + CPLXMLNode* psCurNode = (CPLXMLNode *) CPLCalloc(sizeof(CPLXMLNode),1); + psCurNode->eType = CXT_Element; + psCurNode->pszValue = (char*) CPLMalloc( nLenName+1 ); + memcpy(psCurNode->pszValue, pszName, nLenName+1); + + /* Attach element as the last child of its parent */ + NodeLastChild& sNodeLastChild = apsXMLNode[apsXMLNode.size()-1]; + CPLXMLNode* psLastChildParent = sNodeLastChild.psLastChild; + + if (psLastChildParent == NULL) + { + CPLXMLNode* psParent = sNodeLastChild.psNode; + if (psParent) + psParent->psChild = psCurNode; + } + else + { + psLastChildParent->psNext = psCurNode; + } + sNodeLastChild.psLastChild = psCurNode; + + /* Add attributes to the element */ + CPLXMLNode* psLastChildCurNode = AddAttributes(psCurNode, attr); + + /* Some CityGML lack a srsDimension="3" in posList, such as in */ + /* http://www.citygml.org/fileadmin/count.php?f=fileadmin%2Fcitygml%2Fdocs%2FFrankfurt_Street_Setting_LOD3.zip */ + /* So we have to add it manually */ + if (eAppSchemaType == APPSCHEMA_CITYGML && nLenName == 7 && + strcmp(pszName, "posList") == 0 && + CPLGetXMLValue(psCurNode, "srsDimension", NULL) == NULL) + { + CPLXMLNode* psChild = CPLCreateXMLNode(NULL, CXT_Attribute, "srsDimension"); + CPLCreateXMLNode(psChild, CXT_Text, "3"); + + if (psLastChildCurNode == NULL) + psCurNode->psChild = psChild; + else + psLastChildCurNode->psNext = psChild; + psLastChildCurNode = psChild; + } + + /* Push the element on the stack */ + NodeLastChild sNewNodeLastChild; + sNewNodeLastChild.psNode = psCurNode; + sNewNodeLastChild.psLastChild = psLastChildCurNode; + apsXMLNode.push_back(sNewNodeLastChild); + + if (m_pszGeometry) + { + CPLFree(m_pszGeometry); + m_pszGeometry = NULL; + m_nGeomAlloc = 0; + m_nGeomLen = 0; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* startElementCityGMLGenericAttr() */ +/************************************************************************/ + +OGRErr GMLHandler::startElementCityGMLGenericAttr(const char *pszName, + CPL_UNUSED int nLenName, + CPL_UNUSED void* attr ) +{ + if( strcmp(pszName, "value") == 0 ) + { + if(m_pszCurField) + { + CPLFree(m_pszCurField); + m_pszCurField = NULL; + m_nCurFieldLen = m_nCurFieldAlloc = 0; + } + m_bInCurField = TRUE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DealWithAttributes() */ +/************************************************************************/ + +void GMLHandler::DealWithAttributes(const char *pszName, int nLenName, void* attr ) +{ + GMLReadState *poState = m_poReader->GetState(); + GMLFeatureClass *poClass = poState->m_poFeature->GetClass(); + + for(unsigned int idx=0; TRUE ;idx++) + { + char* pszAttrKey = NULL; + char* pszAttrVal = NULL; + + pszAttrVal = GetAttributeByIdx(attr, idx, &pszAttrKey); + if( pszAttrVal == NULL ) + break; + + int nAttrIndex = 0; + const char* pszAttrKeyNoNS = strchr(pszAttrKey, ':'); + if( pszAttrKeyNoNS != NULL ) + pszAttrKeyNoNS ++; + + /* If attribute is referenced by the .gfs */ + if( poClass->IsSchemaLocked() && + ( (pszAttrKeyNoNS != NULL && + (nAttrIndex = + m_poReader->GetAttributeElementIndex( pszName, nLenName, pszAttrKeyNoNS )) != -1) || + ((nAttrIndex = + m_poReader->GetAttributeElementIndex( pszName, nLenName, pszAttrKey )) != -1 ) ) ) + { + nAttrIndex = FindRealPropertyByCheckingConditions( nAttrIndex, attr ); + if( nAttrIndex >= 0 ) + { + m_poReader->SetFeaturePropertyDirectly( NULL, pszAttrVal, nAttrIndex ); + pszAttrVal = NULL; + } + } + + /* Hard-coded historic cases */ + else if( strcmp(pszAttrKey, "xlink:href") == 0 ) + { + if( (m_bReportHref || m_poReader->ReportAllAttributes()) && m_bInCurField ) + { + CPLFree(m_pszHref); + m_pszHref = pszAttrVal; + pszAttrVal = NULL; + } + else if( (!poClass->IsSchemaLocked() && (m_bReportHref || m_poReader->ReportAllAttributes())) || + (poClass->IsSchemaLocked() && (nAttrIndex = + m_poReader->GetAttributeElementIndex( CPLSPrintf("%s_href", pszName ), + nLenName + 5 )) != -1) ) + { + poState->PushPath( pszName, nLenName ); + CPLString osPropNameHref = poState->osPath + "_href"; + poState->PopPath(); + m_poReader->SetFeaturePropertyDirectly( osPropNameHref, pszAttrVal, nAttrIndex ); + pszAttrVal = NULL; + } + } + else if( strcmp(pszAttrKey, "uom") == 0 ) + { + CPLFree(m_pszUom); + m_pszUom = pszAttrVal; + pszAttrVal = NULL; + } + else if( strcmp(pszAttrKey, "value") == 0 ) + { + CPLFree(m_pszValue); + m_pszValue = pszAttrVal; + pszAttrVal = NULL; + } + else /* Get language in 'kieli' attribute of 'teksti' element */ + if( eAppSchemaType == APPSCHEMA_MTKGML && + nLenName == 6 && strcmp(pszName, "teksti") == 0 && + strcmp(pszAttrKey, "kieli") == 0 ) + { + CPLFree(m_pszKieli); + m_pszKieli = pszAttrVal; + pszAttrVal = NULL; + } + + /* Should we report all attributes ? */ + else if( m_poReader->ReportAllAttributes() && !poClass->IsSchemaLocked() ) + { + poState->PushPath( pszName, nLenName ); + CPLString osPropName = poState->osPath; + poState->PopPath(); + + m_poReader->SetFeaturePropertyDirectly( + CPLSPrintf("%s@%s", osPropName.c_str(), pszAttrKeyNoNS ? pszAttrKeyNoNS : pszAttrKey), + pszAttrVal, -1 ); + pszAttrVal = NULL; + } + + CPLFree(pszAttrKey); + CPLFree(pszAttrVal); + } + +#if 0 + if( poClass->IsSchemaLocked() ) + { + poState->PushPath( pszName, nLenName ); + CPLString osPath = poState->osPath; + poState->PopPath(); + /* Find fields that match an attribute that is missing */ + for(int i=0; i < poClass->GetPropertyCount(); i++ ) + { + GMLPropertyDefn* poProp = poClass->GetProperty(i); + const char* pszSrcElement = poProp->GetSrcElement(); + if( poProp->GetType() == OFTStringList && + poProp->GetSrcElementLen() > osPath.size() && + strncmp(pszSrcElement, osPath, osPath.size()) == 0 && + pszSrcElement[osPath.size()] == '@' ) + { + char* pszAttrVal = GetAttributeValue(attr, pszSrcElement + osPath.size() + 1); + if( pszAttrVal == NULL ) + { + const char* pszCond = poProp->GetCondition(); + if( pszCond == NULL || IsConditionMatched(pszCond, attr) ) + { + m_poReader->SetFeaturePropertyDirectly( NULL, CPLStrdup(""), i ); + } + } + else + CPLFree(pszAttrVal); + } + } + } +#endif +} + +/************************************************************************/ +/* IsConditionMatched() */ +/************************************************************************/ + +/* FIXME! 'and' / 'or' operators are evaluated left to right, without */ +/* and precedence rules between them ! */ + +int GMLHandler::IsConditionMatched(const char* pszCondition, void* attr) +{ + if( pszCondition == NULL ) + return TRUE; + + int bSyntaxError = FALSE; + CPLString osCondAttr, osCondVal; + const char* pszIter = pszCondition; + int bOpEqual = TRUE; + while( *pszIter == ' ' ) + pszIter ++; + if( *pszIter != '@' ) + bSyntaxError = TRUE; + else + { + pszIter++; + while( *pszIter != '\0' && + *pszIter != ' ' && + *pszIter != '!' && + *pszIter != '=' ) + { + osCondAttr += *pszIter; + pszIter++; + } + while( *pszIter == ' ' ) + pszIter ++; + + if( *pszIter == '!' ) + { + bOpEqual = FALSE; + pszIter ++; + } + + if( *pszIter != '=' ) + bSyntaxError = TRUE; + else + { + pszIter ++; + while( *pszIter == ' ' ) + pszIter ++; + if( *pszIter != '\'' ) + bSyntaxError = TRUE; + else + { + pszIter ++; + while( *pszIter != '\0' && + *pszIter != '\'' ) + { + osCondVal += *pszIter; + pszIter++; + } + if( *pszIter != '\'' ) + bSyntaxError = TRUE; + else + { + pszIter ++; + while( *pszIter == ' ' ) + pszIter ++; + } + } + } + } + + if( bSyntaxError ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Invalid condition : %s. Must be of the form @attrname[!]='attrvalue' [and|or other_cond]*. 'and' and 'or' operators cannot be mixed", + pszCondition); + return FALSE; + } + + char* pszVal = GetAttributeValue(attr, osCondAttr); + if( pszVal == NULL ) + pszVal = CPLStrdup(""); + int bCondMet = ((bOpEqual && strcmp(pszVal, osCondVal) == 0 ) || + (!bOpEqual && strcmp(pszVal, osCondVal) != 0 )); + CPLFree(pszVal); + if( *pszIter == '\0' ) + return bCondMet; + + if( strncmp(pszIter, "and", 3) == 0 ) + { + pszIter += 3; + if( !bCondMet ) + return FALSE; + return IsConditionMatched(pszIter, attr); + } + + if( strncmp(pszIter, "or", 2) == 0 ) + { + pszIter += 2; + if( bCondMet ) + return TRUE; + return IsConditionMatched(pszIter, attr); + } + + CPLError(CE_Failure, CPLE_NotSupported, + "Invalid condition : %s. Must be of the form @attrname[!]='attrvalue' [and|or other_cond]*. 'and' and 'or' operators cannot be mixed", + pszCondition); + return FALSE; +} + +/************************************************************************/ +/* FindRealPropertyByCheckingConditions() */ +/************************************************************************/ + +int GMLHandler::FindRealPropertyByCheckingConditions(int nIdx, void* attr) +{ + GMLReadState *poState = m_poReader->GetState(); + GMLFeatureClass *poClass = poState->m_poFeature->GetClass(); + + GMLPropertyDefn* poProp = poClass->GetProperty(nIdx); + const char* pszCond = poProp->GetCondition(); + if( pszCond != NULL && !IsConditionMatched(pszCond, attr) ) + { + /* try other attributes with same source element, but with different */ + /* condition */ + const char* pszSrcElement = poProp->GetSrcElement(); + nIdx = -1; + for(int i=m_nAttributeIndex+1; i < poClass->GetPropertyCount(); i++ ) + { + poProp = poClass->GetProperty(i); + if( strcmp(poProp->GetSrcElement(), pszSrcElement) == 0 ) + { + pszCond = poProp->GetCondition(); + if( IsConditionMatched(pszCond, attr) ) + { + nIdx = i; + break; + } + } + } + } + return nIdx; +} + +/************************************************************************/ +/* startElementFeatureAttribute() */ +/************************************************************************/ + +OGRErr GMLHandler::startElementFeatureAttribute(const char *pszName, int nLenName, void* attr ) +{ + /* Reset flag */ + m_bInCurField = FALSE; + + GMLReadState *poState = m_poReader->GetState(); + +/* -------------------------------------------------------------------- */ +/* If we are collecting geometry, or if we determine this is a */ +/* geometry element then append to the geometry info. */ +/* -------------------------------------------------------------------- */ + if( IsGeometryElement( pszName ) ) + { + int bReadGeometry; + + /* If the is defined in the .gfs, use it */ + /* to read the appropriate geometry element */ + GMLFeatureClass* poClass = poState->m_poFeature->GetClass(); + m_nGeometryPropertyIndex = 0; + if( poClass->IsSchemaLocked() && + poClass->GetGeometryPropertyCount() == 0 ) + { + bReadGeometry = FALSE; + } + else if( poClass->IsSchemaLocked() && + poClass->GetGeometryPropertyCount() == 1 && + poClass->GetGeometryProperty(0)->GetSrcElement()[0] == '\0' ) + { + bReadGeometry = TRUE; + } + else if( poClass->IsSchemaLocked() && + poClass->GetGeometryPropertyCount() > 0 ) + { + m_nGeometryPropertyIndex = poClass->GetGeometryPropertyIndexBySrcElement( poState->osPath.c_str() ); + bReadGeometry = (m_nGeometryPropertyIndex >= 0); + } + else if( m_poReader->FetchAllGeometries() ) + { + bReadGeometry = TRUE; + } + else if( !poClass->IsSchemaLocked() && m_poReader->IsWFSJointLayer() ) + { + m_nGeometryPropertyIndex = poClass->GetGeometryPropertyIndexBySrcElement( poState->osPath.c_str() ); + if( m_nGeometryPropertyIndex < 0 ) + { + const char* pszElement = poState->osPath.c_str(); + CPLString osFieldName; + /* Strip member| prefix. Should always be true normally */ + if( strncmp(pszElement, "member|", strlen("member|")) == 0 ) + osFieldName = pszElement + strlen("member|"); + + /* Replace layer|property by layer_property */ + size_t iPos = osFieldName.find('|'); + if( iPos != std::string::npos ) + osFieldName[iPos] = '.'; + + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( + osFieldName, poState->osPath.c_str(), wkbUnknown, -1, TRUE ) ); + m_nGeometryPropertyIndex = poClass->GetGeometryPropertyCount(); + } + bReadGeometry = TRUE; + } + else + { + /* AIXM special case: for RouteSegment, we only want to read Curve geometries */ + /* not 'start' and 'end' geometries */ + if (eAppSchemaType == APPSCHEMA_AIXM && + strcmp(poState->m_poFeature->GetClass()->GetName(), "RouteSegment") == 0) + bReadGeometry = strcmp( pszName, "Curve") == 0; + + /* For Inspire objects : the "main" geometry is in a element */ + else if (m_bAlreadyFoundGeometry) + bReadGeometry = FALSE; + else if (strcmp( poState->osPath.c_str(), "geometry") == 0) + { + m_bAlreadyFoundGeometry = TRUE; + bReadGeometry = TRUE; + } + + else + bReadGeometry = TRUE; + } + if (bReadGeometry) + { + m_nGeometryDepth = m_nDepth; + + CPLAssert(apsXMLNode.size() == 0); + + NodeLastChild sNodeLastChild; + sNodeLastChild.psNode = NULL; + sNodeLastChild.psLastChild = NULL; + apsXMLNode.push_back(sNodeLastChild); + + PUSH_STATE(STATE_GEOMETRY); + + return startElementGeometry(pszName, nLenName, attr); + } + } + + + else if( nLenName == 9 && strcmp(pszName, "boundedBy") == 0 ) + { + m_inBoundedByDepth = m_nDepth; + + PUSH_STATE(STATE_BOUNDED_BY); + + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Is it a CityGML generic attribute ? */ +/* -------------------------------------------------------------------- */ + else if( eAppSchemaType == APPSCHEMA_CITYGML && + m_poReader->IsCityGMLGenericAttributeElement( pszName, attr ) ) + { + CPLFree(m_pszCityGMLGenericAttrName); + m_pszCityGMLGenericAttrName = GetAttributeValue(attr, "name"); + m_inCityGMLGenericAttrDepth = m_nDepth; + + PUSH_STATE(STATE_CITYGML_ATTRIBUTE); + + return OGRERR_NONE; + } + + else if( m_poReader->IsWFSJointLayer() && m_nDepth == m_nDepthFeature + 1 ) + { + } + + else if( m_poReader->IsWFSJointLayer() && m_nDepth == m_nDepthFeature + 2 ) + { + const char* pszFID = GetFID(attr); + if( pszFID ) + { + poState->PushPath( pszName, nLenName ); + CPLString osPropPath= poState->osPath + "@id"; + poState->PopPath(); + m_poReader->SetFeaturePropertyDirectly( osPropPath, CPLStrdup(pszFID), -1 ); + } + } + +/* -------------------------------------------------------------------- */ +/* If it is (or at least potentially is) a simple attribute, */ +/* then start collecting it. */ +/* -------------------------------------------------------------------- */ + else if( (m_nAttributeIndex = + m_poReader->GetAttributeElementIndex( pszName, nLenName )) != -1 ) + { + GMLFeatureClass *poClass = poState->m_poFeature->GetClass(); + if( poClass->IsSchemaLocked() && + (poClass->GetProperty(m_nAttributeIndex)->GetType() == GMLPT_FeatureProperty || + poClass->GetProperty(m_nAttributeIndex)->GetType() == GMLPT_FeaturePropertyList) ) + { + m_nAttributeDepth = m_nDepth; + PUSH_STATE(STATE_FEATUREPROPERTY); + } + else + { + /* Is this a property with a condition on an attribute value ? */ + if( poClass->IsSchemaLocked() ) + { + m_nAttributeIndex = FindRealPropertyByCheckingConditions( m_nAttributeIndex, attr ); + } + + if( m_nAttributeIndex >= 0 ) + { + if(m_pszCurField) + { + CPLFree(m_pszCurField); + m_pszCurField = NULL; + m_nCurFieldLen = m_nCurFieldAlloc = 0; + } + m_bInCurField = TRUE; + + DealWithAttributes(pszName, nLenName, attr); + + if (stateStack[nStackDepth] != STATE_PROPERTY) + { + m_nAttributeDepth = m_nDepth; + PUSH_STATE(STATE_PROPERTY); + } + } + /*else + { + DealWithAttributes(pszName, nLenName, attr); + }*/ + } + + } + else + { + DealWithAttributes(pszName, nLenName, attr); + } + + poState->PushPath( pszName, nLenName ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* startElementTop() */ +/************************************************************************/ + +OGRErr GMLHandler::startElementTop(const char *pszName, + CPL_UNUSED int nLenName, + void* attr ) +{ + if (strcmp(pszName, "CityModel") == 0 ) + { + eAppSchemaType = APPSCHEMA_CITYGML; + } + else if (strcmp(pszName, "AIXMBasicMessage") == 0) + { + eAppSchemaType = APPSCHEMA_AIXM; + m_bReportHref = TRUE; + } + else if (strcmp(pszName, "Maastotiedot") == 0) + { + eAppSchemaType = APPSCHEMA_MTKGML; + + char *pszSRSName = GetAttributeValue(attr, "srsName"); + m_poReader->SetGlobalSRSName(pszSRSName); + CPLFree(pszSRSName); + + m_bReportHref = TRUE; + + /* the schemas of MTKGML don't have (string) width, so don't set it */ + m_poReader->SetWidthFlag(FALSE); + } + + stateStack[0] = STATE_DEFAULT; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* startElementDefault() */ +/************************************************************************/ + +OGRErr GMLHandler::startElementDefault(const char *pszName, int nLenName, void* attr ) + +{ +/* -------------------------------------------------------------------- */ +/* Is it a feature? If so push a whole new state, and return. */ +/* -------------------------------------------------------------------- */ + int nClassIndex; + const char* pszFilteredClassName; + + if( nLenName == 9 && strcmp(pszName, "boundedBy") == 0 ) + { + m_inBoundedByDepth = m_nDepth; + + PUSH_STATE(STATE_BOUNDED_BY); + + return OGRERR_NONE; + } + + else if( m_poReader->ShouldLookForClassAtAnyLevel() && + ( pszFilteredClassName = m_poReader->GetFilteredClassName() ) != NULL ) + { + if( strcmp(pszName, pszFilteredClassName) == 0 ) + { + m_poReader->PushFeature( pszName, GetFID(attr), m_poReader->GetFilteredClassIndex() ); + + m_nDepthFeature = m_nDepth; + + PUSH_STATE(STATE_FEATURE); + + return OGRERR_NONE; + } + } + + /* WFS 2.0 GetFeature documents have a wfs:FeatureCollection */ + /* as a wfs:member of the top wfs:FeatureCollection. We don't want this */ + /* wfs:FeatureCollection to be recognized as a feature */ + else if( (!(nLenName == strlen("FeatureCollection") && + strcmp(pszName, "FeatureCollection") == 0)) && + (nClassIndex = m_poReader->GetFeatureElementIndex( pszName, nLenName, eAppSchemaType )) != -1 ) + { + m_bAlreadyFoundGeometry = FALSE; + + pszFilteredClassName = m_poReader->GetFilteredClassName(); + if ( pszFilteredClassName != NULL && + strcmp(pszName, pszFilteredClassName) != 0 ) + { + m_nDepthFeature = m_nDepth; + + PUSH_STATE(STATE_IGNORED_FEATURE); + + return OGRERR_NONE; + } + else + { + if( eAppSchemaType == APPSCHEMA_MTKGML ) + { + m_poReader->PushFeature( pszName, NULL, nClassIndex ); + + char* pszGID = GetAttributeValue(attr, "gid"); + if( pszGID ) + m_poReader->SetFeaturePropertyDirectly( "gid", pszGID, -1, GMLPT_String ); + } + else + m_poReader->PushFeature( pszName, GetFID(attr), nClassIndex ); + + m_nDepthFeature = m_nDepth; + + PUSH_STATE(STATE_FEATURE); + + return OGRERR_NONE; + } + } + +/* -------------------------------------------------------------------- */ +/* Push the element onto the current state's path. */ +/* -------------------------------------------------------------------- */ + m_poReader->GetState()->PushPath( pszName, nLenName ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* endElementIgnoredFeature() */ +/************************************************************************/ + +OGRErr GMLHandler::endElementIgnoredFeature() + +{ + if (m_nDepth == m_nDepthFeature) + { + POP_STATE(); + } + return OGRERR_NONE; +} + +/************************************************************************/ +/* endElementBoundedBy() */ +/************************************************************************/ +OGRErr GMLHandler::endElementBoundedBy() + +{ + if( m_inBoundedByDepth == m_nDepth) + { + POP_STATE(); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ParseAIXMElevationPoint() */ +/************************************************************************/ + +CPLXMLNode* GMLHandler::ParseAIXMElevationPoint(CPLXMLNode *psGML) +{ + const char* pszElevation = + CPLGetXMLValue( psGML, "elevation", NULL ); + if (pszElevation) + { + m_poReader->SetFeaturePropertyDirectly( "elevation", + CPLStrdup(pszElevation), -1 ); + const char* pszElevationUnit = + CPLGetXMLValue( psGML, "elevation.uom", NULL ); + if (pszElevationUnit) + { + m_poReader->SetFeaturePropertyDirectly( "elevation_uom", + CPLStrdup(pszElevationUnit), -1 ); + } + } + + const char* pszGeoidUndulation = + CPLGetXMLValue( psGML, "geoidUndulation", NULL ); + if (pszGeoidUndulation) + { + m_poReader->SetFeaturePropertyDirectly( "geoidUndulation", + CPLStrdup(pszGeoidUndulation), -1 ); + const char* pszGeoidUndulationUnit = + CPLGetXMLValue( psGML, "geoidUndulation.uom", NULL ); + if (pszGeoidUndulationUnit) + { + m_poReader->SetFeaturePropertyDirectly( "geoidUndulation_uom", + CPLStrdup(pszGeoidUndulationUnit), -1 ); + } + } + + const char* pszPos = + CPLGetXMLValue( psGML, "pos", NULL ); + const char* pszCoordinates = + CPLGetXMLValue( psGML, "coordinates", NULL ); + if (pszPos != NULL) + { + char* pszGeometry = CPLStrdup(CPLSPrintf( + "%s", + pszPos)); + CPLDestroyXMLNode(psGML); + psGML = CPLParseXMLString(pszGeometry); + CPLFree(pszGeometry); + } + else if (pszCoordinates != NULL) + { + char* pszGeometry = CPLStrdup(CPLSPrintf( + "%s", + pszCoordinates)); + CPLDestroyXMLNode(psGML); + psGML = CPLParseXMLString(pszGeometry); + CPLFree(pszGeometry); + } + else + { + CPLDestroyXMLNode(psGML); + psGML = NULL; + } + + return psGML; +} + +/************************************************************************/ +/* endElementGeometry() */ +/************************************************************************/ +OGRErr GMLHandler::endElementGeometry() + +{ + if (m_nGeomLen) + { + CPLXMLNode* psNode = (CPLXMLNode *) CPLCalloc(sizeof(CPLXMLNode),1); + psNode->eType = CXT_Text; + psNode->pszValue = m_pszGeometry; + + NodeLastChild& sNodeLastChild = apsXMLNode[apsXMLNode.size()-1]; + CPLXMLNode* psLastChildParent = sNodeLastChild.psLastChild; + if (psLastChildParent == NULL) + { + CPLXMLNode* psParent = sNodeLastChild.psNode; + if (psParent) + psParent->psChild = psNode; + } + else + psLastChildParent->psNext = psNode; + sNodeLastChild.psLastChild = psNode; + + m_pszGeometry = NULL; + m_nGeomAlloc = 0; + m_nGeomLen = 0; + } + + if( m_nDepth == m_nGeometryDepth ) + { + CPLXMLNode* psInterestNode = apsXMLNode[apsXMLNode.size()-1].psNode; + + /*char* pszXML = CPLSerializeXMLTree(psInterestNode); + CPLDebug("GML", "geometry = %s", pszXML); + CPLFree(pszXML);*/ + + apsXMLNode.pop_back(); + + /* AIXM ElevatedPoint. We want to parse this */ + /* a bit specially because ElevatedPoint is aixm: stuff and */ + /* the srsDimension of the can be set to TRUE although */ + /* they are only 2 coordinates in practice */ + if ( eAppSchemaType == APPSCHEMA_AIXM && psInterestNode != NULL && + strcmp(psInterestNode->pszValue, "ElevatedPoint") == 0 ) + { + psInterestNode = ParseAIXMElevationPoint(psInterestNode); + } + else if ( eAppSchemaType == APPSCHEMA_MTKGML && psInterestNode != NULL ) + { + if( strcmp(psInterestNode->pszValue, "Murtoviiva") == 0 ) + { + CPLFree(psInterestNode->pszValue); + psInterestNode->pszValue = CPLStrdup("gml:LineString"); + } + else if( strcmp(psInterestNode->pszValue, "Alue") == 0 ) + { + CPLFree(psInterestNode->pszValue); + psInterestNode->pszValue = CPLStrdup("gml:Polygon"); + } + else if( strcmp(psInterestNode->pszValue, "Piste") == 0 ) + { + CPLFree(psInterestNode->pszValue); + psInterestNode->pszValue = CPLStrdup("gml:Point"); + } + } + else if( psInterestNode != NULL && + strcmp(psInterestNode->pszValue, "BoundingBox") == 0 ) + { + CPLFree(psInterestNode->pszValue); + psInterestNode->pszValue = CPLStrdup("Envelope"); + for( CPLXMLNode* psChild = psInterestNode->psChild; + psChild; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Attribute && + strcmp(psChild->pszValue, "crs") == 0 ) + { + CPLFree(psChild->pszValue); + psChild->pszValue = CPLStrdup("srsName"); + break; + } + } + } + + GMLFeature* poGMLFeature = m_poReader->GetState()->m_poFeature; + if (m_poReader->FetchAllGeometries()) + poGMLFeature->AddGeometry(psInterestNode); + else + { + GMLFeatureClass* poClass = poGMLFeature->GetClass(); + if( poClass->GetGeometryPropertyCount() > 1 ) + { + poGMLFeature->SetGeometryDirectly( + m_nGeometryPropertyIndex, psInterestNode); + } + else + { + poGMLFeature->SetGeometryDirectly(psInterestNode); + } + } + + POP_STATE(); + } + + apsXMLNode.pop_back(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* endElementCityGMLGenericAttr() */ +/************************************************************************/ +OGRErr GMLHandler::endElementCityGMLGenericAttr() + +{ + if( m_pszCityGMLGenericAttrName != NULL && m_bInCurField ) + { + if( m_pszCurField != NULL ) + { + m_poReader->SetFeaturePropertyDirectly( m_pszCityGMLGenericAttrName, + m_pszCurField, -1 ); + } + m_pszCurField = NULL; + m_nCurFieldLen = m_nCurFieldAlloc = 0; + m_bInCurField = FALSE; + CPLFree(m_pszCityGMLGenericAttrName); + m_pszCityGMLGenericAttrName = NULL; + } + + if( m_inCityGMLGenericAttrDepth == m_nDepth ) + { + POP_STATE(); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* endElementAttribute() */ +/************************************************************************/ +OGRErr GMLHandler::endElementAttribute() + +{ + GMLReadState *poState = m_poReader->GetState(); + + if (m_bInCurField) + { + if (m_pszCurField == NULL && m_poReader->IsEmptyAsNull()) + { + if (m_pszValue != NULL) + { + m_poReader->SetFeaturePropertyDirectly( poState->osPath.c_str(), + m_pszValue, -1 ); + m_pszValue = NULL; + } + } + else + { + m_poReader->SetFeaturePropertyDirectly( poState->osPath.c_str(), + m_pszCurField ? m_pszCurField : CPLStrdup(""), + m_nAttributeIndex ); + m_pszCurField = NULL; + } + + if (m_pszHref != NULL) + { + CPLString osPropNameHref = poState->osPath + "_href"; + m_poReader->SetFeaturePropertyDirectly( osPropNameHref, m_pszHref, -1 ); + m_pszHref = NULL; + } + + if (m_pszUom != NULL) + { + CPLString osPropNameUom = poState->osPath + "_uom"; + m_poReader->SetFeaturePropertyDirectly( osPropNameUom, m_pszUom, -1 ); + m_pszUom = NULL; + } + + if (m_pszKieli != NULL) + { + CPLString osPropName = poState->osPath + "_kieli"; + m_poReader->SetFeaturePropertyDirectly( osPropName, m_pszKieli, -1 ); + m_pszKieli = NULL; + } + + m_nCurFieldLen = m_nCurFieldAlloc = 0; + m_bInCurField = FALSE; + m_nAttributeIndex = -1; + + CPLFree( m_pszValue ); + m_pszValue = NULL; + } + + poState->PopPath(); + + if( m_nAttributeDepth == m_nDepth ) + { + POP_STATE(); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* startElementFeatureProperty() */ +/************************************************************************/ + +OGRErr GMLHandler::startElementFeatureProperty(CPL_UNUSED const char *pszName, + CPL_UNUSED int nLenName, + void* attr ) +{ + if (m_nDepth == m_nAttributeDepth + 1) + { + const char* pszGMLId = GetFID(attr); + if( pszGMLId != NULL ) + { + m_poReader->SetFeaturePropertyDirectly( NULL, + CPLStrdup(CPLSPrintf("#%s", pszGMLId)), + m_nAttributeIndex ); + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* endElementFeatureProperty() */ +/************************************************************************/ + +OGRErr GMLHandler::endElementFeatureProperty() + +{ + if (m_nDepth == m_nAttributeDepth) + { + GMLReadState *poState = m_poReader->GetState(); + poState->PopPath(); + + POP_STATE(); + } + return OGRERR_NONE; +} + +/************************************************************************/ +/* endElementFeature() */ +/************************************************************************/ +OGRErr GMLHandler::endElementFeature() + +{ +/* -------------------------------------------------------------------- */ +/* If we are collecting a feature, and this element tag matches */ +/* element name for the class, then we have finished the */ +/* feature, and we pop the feature read state. */ +/* -------------------------------------------------------------------- */ + if( m_nDepth == m_nDepthFeature ) + { + m_poReader->PopState(); + + POP_STATE(); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise, we just pop the element off the local read states */ +/* element stack. */ +/* -------------------------------------------------------------------- */ + else + { + m_poReader->GetState()->PopPath(); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* endElementDefault() */ +/************************************************************************/ +OGRErr GMLHandler::endElementDefault() + +{ + if (m_nDepth > 0) + m_poReader->GetState()->PopPath(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* dataHandlerAttribute() */ +/************************************************************************/ + +OGRErr GMLHandler::dataHandlerAttribute(const char *data, int nLen) + +{ + int nIter = 0; + + if( m_bInCurField ) + { + // Ignore white space + if (m_nCurFieldLen == 0) + { + while (nIter < nLen) + { + char ch = data[nIter]; + if( !(ch == ' ' || ch == 10 || ch == 13 || ch == '\t') ) + break; + nIter ++; + } + } + + int nCharsLen = nLen - nIter; + + if (m_nCurFieldLen + nCharsLen + 1 > m_nCurFieldAlloc) + { + m_nCurFieldAlloc = m_nCurFieldAlloc * 4 / 3 + nCharsLen + 1; + char *pszNewCurField = (char *) + VSIRealloc( m_pszCurField, m_nCurFieldAlloc ); + if (pszNewCurField == NULL) + { + return OGRERR_NOT_ENOUGH_MEMORY; + } + m_pszCurField = pszNewCurField; + } + memcpy( m_pszCurField + m_nCurFieldLen, data + nIter, nCharsLen); + m_nCurFieldLen += nCharsLen; + m_pszCurField[m_nCurFieldLen] = '\0'; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* dataHandlerGeometry() */ +/************************************************************************/ + +OGRErr GMLHandler::dataHandlerGeometry(const char *data, int nLen) + +{ + int nIter = 0; + + // Ignore white space + if (m_nGeomLen == 0) + { + while (nIter < nLen) + { + char ch = data[nIter]; + if( !(ch == ' ' || ch == 10 || ch == 13 || ch == '\t') ) + break; + nIter ++; + } + } + + int nCharsLen = nLen - nIter; + if (nCharsLen) + { + if( m_nGeomLen + nCharsLen + 1 > m_nGeomAlloc ) + { + m_nGeomAlloc = m_nGeomAlloc * 4 / 3 + nCharsLen + 1; + char* pszNewGeometry = (char *) + VSIRealloc( m_pszGeometry, m_nGeomAlloc); + if (pszNewGeometry == NULL) + { + return OGRERR_NOT_ENOUGH_MEMORY; + } + m_pszGeometry = pszNewGeometry; + } + memcpy( m_pszGeometry+m_nGeomLen, data + nIter, nCharsLen); + m_nGeomLen += nCharsLen; + m_pszGeometry[m_nGeomLen] = '\0'; + } + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* IsGeometryElement() */ +/************************************************************************/ + +int GMLHandler::IsGeometryElement( const char *pszElement ) + +{ + int nFirst = 0; + int nLast = GML_GEOMETRY_TYPE_COUNT- 1; + unsigned long nHash = CPLHashSetHashStr(pszElement); + do + { + int nMiddle = (nFirst + nLast) / 2; + if (nHash == pasGeometryNames[nMiddle].nHash) + return strcmp(pszElement, pasGeometryNames[nMiddle].pszName) == 0; + if (nHash < pasGeometryNames[nMiddle].nHash) + nLast = nMiddle - 1; + else + nFirst = nMiddle + 1; + } while(nFirst <= nLast); + + if (eAppSchemaType == APPSCHEMA_AIXM && + strcmp( pszElement, "ElevatedPoint") == 0) + return TRUE; + + if( eAppSchemaType == APPSCHEMA_MTKGML && + ( strcmp( pszElement, "Piste") == 0 || + strcmp( pszElement, "Alue") == 0 || + strcmp( pszElement, "Murtoviiva") == 0 ) ) + return TRUE; + + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlpropertydefn.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlpropertydefn.cpp new file mode 100644 index 000000000..6eaa0945c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlpropertydefn.cpp @@ -0,0 +1,237 @@ +/********************************************************************** + * $Id: gmlpropertydefn.cpp 28481 2015-02-13 17:11:15Z rouault $ + * + * Project: GML Reader + * Purpose: Implementation of GMLPropertyDefn + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gmlreader.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +/************************************************************************/ +/* GMLPropertyDefn */ +/************************************************************************/ + +GMLPropertyDefn::GMLPropertyDefn( const char *pszName, + const char *pszSrcElement ) + +{ + m_pszName = CPLStrdup( pszName ); + if( pszSrcElement != NULL ) + { + m_nSrcElementLen = strlen( pszSrcElement ); + m_pszSrcElement = CPLStrdup( pszSrcElement ); + } + else + { + m_nSrcElementLen = 0; + m_pszSrcElement = NULL; + } + m_eType = GMLPT_Untyped; + m_nWidth = 0; + m_nPrecision = 0; + m_pszCondition = NULL; + m_bNullable = TRUE; +} + +/************************************************************************/ +/* ~GMLPropertyDefn() */ +/************************************************************************/ + +GMLPropertyDefn::~GMLPropertyDefn() + +{ + CPLFree( m_pszName ); + CPLFree( m_pszSrcElement ); + CPLFree( m_pszCondition ); +} + +/************************************************************************/ +/* SetSrcElement() */ +/************************************************************************/ + +void GMLPropertyDefn::SetSrcElement( const char *pszSrcElement ) + +{ + CPLFree( m_pszSrcElement ); + if( pszSrcElement != NULL ) + { + m_nSrcElementLen = strlen( pszSrcElement ); + m_pszSrcElement = CPLStrdup( pszSrcElement ); + } + else + { + m_nSrcElementLen = 0; + m_pszSrcElement = NULL; + } +} + +/************************************************************************/ +/* SetCondition() */ +/************************************************************************/ + +void GMLPropertyDefn::SetCondition( const char *pszCondition ) +{ + CPLFree( m_pszCondition ); + m_pszCondition = ( pszCondition != NULL ) ? CPLStrdup(pszCondition) : NULL; +} + +/************************************************************************/ +/* AnalysePropertyValue() */ +/* */ +/* Examine the passed property value, and see if we need to */ +/* make the field type more specific, or more general. */ +/************************************************************************/ + +void GMLPropertyDefn::AnalysePropertyValue( const GMLProperty* psGMLProperty, + int bSetWidth ) + +{ +/* -------------------------------------------------------------------- */ +/* Does the string consist entirely of numeric values? */ +/* -------------------------------------------------------------------- */ + int bIsReal = FALSE; + + int j; + for(j=0;jnSubProperties;j++) + { + if (j > 0) + { + if( m_eType == GMLPT_Integer ) + m_eType = GMLPT_IntegerList; + else if( m_eType == GMLPT_Integer64 ) + m_eType = GMLPT_Integer64List; + else if( m_eType == GMLPT_Real ) + m_eType = GMLPT_RealList; + else if( m_eType == GMLPT_String ) + { + m_eType = GMLPT_StringList; + m_nWidth = 0; + } + else if( m_eType == GMLPT_Boolean ) + m_eType = GMLPT_BooleanList; + } + const char* pszValue = psGMLProperty->papszSubProperties[j]; +/* -------------------------------------------------------------------- */ +/* If it is a zero length string, just return. We can't deduce */ +/* much from this. */ +/* -------------------------------------------------------------------- */ + if( *pszValue == '\0' ) + continue; + + CPLValueType valueType = CPLGetValueType(pszValue); + + if (valueType == CPL_VALUE_STRING + && m_eType != GMLPT_String + && m_eType != GMLPT_StringList ) + { + if( (m_eType == GMLPT_Untyped || m_eType == GMLPT_Boolean) && + (strcmp(pszValue, "true") == 0 || + strcmp(pszValue, "false") == 0) ) + m_eType = GMLPT_Boolean; + else if( m_eType == GMLPT_BooleanList ) + { + if( !(strcmp(pszValue, "true") == 0 || + strcmp(pszValue, "false") == 0) ) + m_eType = GMLPT_StringList; + } + else if( m_eType == GMLPT_IntegerList || + m_eType == GMLPT_Integer64List || + m_eType == GMLPT_RealList ) + m_eType = GMLPT_StringList; + else + m_eType = GMLPT_String; + } + else + bIsReal = (valueType == CPL_VALUE_REAL); + + if( m_eType == GMLPT_String ) + { + if( bSetWidth ) + { + /* grow the Width to the length of the string passed in */ + int nWidth; + nWidth = strlen(pszValue); + if ( m_nWidth < nWidth ) + SetWidth( nWidth ); + } + } + else if( m_eType == GMLPT_Untyped || m_eType == GMLPT_Integer || + m_eType == GMLPT_Integer64 ) + { + if( bIsReal ) + m_eType = GMLPT_Real; + else if( m_eType != GMLPT_Integer64 ) + { + GIntBig nVal = CPLAtoGIntBig(pszValue); + if( (GIntBig)(int)nVal != nVal ) + m_eType = GMLPT_Integer64; + else + m_eType = GMLPT_Integer; + } + } + else if( (m_eType == GMLPT_IntegerList || + m_eType == GMLPT_Integer64List) && bIsReal ) + { + m_eType = GMLPT_RealList; + } + else if( m_eType == GMLPT_IntegerList && valueType == CPL_VALUE_INTEGER ) + { + GIntBig nVal = CPLAtoGIntBig(pszValue); + if( (GIntBig)(int)nVal != nVal ) + m_eType = GMLPT_Integer64List; + } + } +} + +/************************************************************************/ +/* GMLGeometryPropertyDefn */ +/************************************************************************/ + +GMLGeometryPropertyDefn::GMLGeometryPropertyDefn( const char *pszName, + const char *pszSrcElement, + int nType, + int nAttributeIndex, + int bNullable ) +{ + m_pszName = (pszName == NULL || pszName[0] == '\0') ? + CPLStrdup(pszSrcElement) : CPLStrdup(pszName); + m_pszSrcElement = CPLStrdup(pszSrcElement); + m_nGeometryType = nType; + m_nAttributeIndex = nAttributeIndex; + m_bNullable = bNullable; +} + +/************************************************************************/ +/* ~GMLGeometryPropertyDefn */ +/************************************************************************/ + +GMLGeometryPropertyDefn::~GMLGeometryPropertyDefn() +{ + CPLFree(m_pszName); + CPLFree(m_pszSrcElement); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlreader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlreader.cpp new file mode 100644 index 000000000..e2ef1b39c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlreader.cpp @@ -0,0 +1,1596 @@ +/****************************************************************************** + * $Id: gmlreader.cpp 29217 2015-05-21 09:08:48Z rouault $ + * + * Project: GML Reader + * Purpose: Implementation of GMLReader class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gmlreader.h" +#include "cpl_error.h" +#include "cpl_string.h" +#include "gmlutils.h" +#include "gmlreaderp.h" +#include "cpl_conv.h" +#include +#include "cpl_multiproc.h" + +#define SUPPORT_GEOMETRY + +#ifdef SUPPORT_GEOMETRY +# include "ogr_geometry.h" +#endif + +/************************************************************************/ +/* ~IGMLReader() */ +/************************************************************************/ + +IGMLReader::~IGMLReader() + +{ +} + +/************************************************************************/ +/* ==================================================================== */ +/* No XERCES or EXPAT Library */ +/* ==================================================================== */ +/************************************************************************/ +#if !defined(HAVE_XERCES) && !defined(HAVE_EXPAT) + +/************************************************************************/ +/* CreateGMLReader() */ +/************************************************************************/ + +IGMLReader *CreateGMLReader(CPL_UNUSED int bUseExpatParserPreferably, + CPL_UNUSED int bInvertAxisOrderIfLatLong, + CPL_UNUSED int bConsiderEPSGAsURN, + CPL_UNUSED int bGetSecondaryGeometryOption) +{ + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create Xerces C++ or Expat based GML reader, Xerces or Expat support\n" + "not configured into GDAL/OGR." ); + return NULL; +} + +/************************************************************************/ +/* ==================================================================== */ +/* With XERCES or EXPAT Library */ +/* ==================================================================== */ +/************************************************************************/ +#else /* defined(HAVE_XERCES) || defined(HAVE_EXPAT) */ + +/************************************************************************/ +/* CreateGMLReader() */ +/************************************************************************/ + +IGMLReader *CreateGMLReader(int bUseExpatParserPreferably, + int bInvertAxisOrderIfLatLong, + int bConsiderEPSGAsURN, + int bGetSecondaryGeometryOption) + +{ + return new GMLReader(bUseExpatParserPreferably, + bInvertAxisOrderIfLatLong, + bConsiderEPSGAsURN, + bGetSecondaryGeometryOption); +} + +#endif + +OGRGMLXercesState GMLReader::m_eXercesInitState = OGRGML_XERCES_UNINITIALIZED; +int GMLReader::m_nInstanceCount = 0; +CPLMutex *GMLReader::hMutex = NULL; + +/************************************************************************/ +/* GMLReader() */ +/************************************************************************/ + +GMLReader::GMLReader( +#ifndef HAVE_EXPAT +CPL_UNUSED +#endif + int bUseExpatParserPreferably, + int bInvertAxisOrderIfLatLong, + int bConsiderEPSGAsURN, + int bGetSecondaryGeometryOption) +{ +#ifndef HAVE_XERCES + bUseExpatReader = TRUE; +#else + bUseExpatReader = FALSE; +#ifdef HAVE_EXPAT + if(bUseExpatParserPreferably) + bUseExpatReader = TRUE; +#endif +#endif + +#if defined(HAVE_EXPAT) && defined(HAVE_XERCES) + if (bUseExpatReader) + CPLDebug("GML", "Using Expat reader"); + else + CPLDebug("GML", "Using Xerces reader"); +#endif + + m_nClassCount = 0; + m_papoClass = NULL; + m_bLookForClassAtAnyLevel = FALSE; + + m_bClassListLocked = FALSE; + + m_poGMLHandler = NULL; +#ifdef HAVE_XERCES + m_poSAXReader = NULL; + m_poCompleteFeature = NULL; + m_GMLInputSource = NULL; + m_bEOF = FALSE; +#endif +#ifdef HAVE_EXPAT + oParser = NULL; + ppoFeatureTab = NULL; + nFeatureTabIndex = 0; + nFeatureTabLength = 0; + nFeatureTabAlloc = 0; + pabyBuf = NULL; +#endif + fpGML = NULL; + m_bReadStarted = FALSE; + + m_poState = NULL; + m_poRecycledState = NULL; + + m_pszFilename = NULL; + + m_bStopParsing = FALSE; + + /* A bit experimental. Not publicly advertized. See commented doc in drv_gml.html */ + m_bFetchAllGeometries = CSLTestBoolean(CPLGetConfigOption("GML_FETCH_ALL_GEOMETRIES", "NO")); + + m_bInvertAxisOrderIfLatLong = bInvertAxisOrderIfLatLong; + m_bConsiderEPSGAsURN = bConsiderEPSGAsURN; + m_bGetSecondaryGeometryOption = bGetSecondaryGeometryOption; + + m_pszGlobalSRSName = NULL; + m_bCanUseGlobalSRSName = FALSE; + + m_pszFilteredClassName = NULL; + m_nFilteredClassIndex = -1; + + m_bSequentialLayers = -1; + + /* Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer() and GMLReader::GMLReader() */ + m_bFaceHoleNegative = CSLTestBoolean(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")); + + m_bSetWidthFlag = TRUE; + + m_bReportAllAttributes = FALSE; + + m_bIsWFSJointLayer = FALSE; + m_bEmptyAsNull = TRUE; +} + +/************************************************************************/ +/* ~GMLReader() */ +/************************************************************************/ + +GMLReader::~GMLReader() + +{ + ClearClasses(); + + CPLFree( m_pszFilename ); + + CleanupParser(); + + delete m_poRecycledState; + +#ifdef HAVE_XERCES + { + CPLMutexHolderD(&hMutex); + --m_nInstanceCount; + if( m_nInstanceCount == 0 && m_eXercesInitState == OGRGML_XERCES_INIT_SUCCESSFUL ) + { + XMLPlatformUtils::Terminate(); + m_eXercesInitState = OGRGML_XERCES_UNINITIALIZED; + } + } +#endif +#ifdef HAVE_EXPAT + CPLFree(pabyBuf); +#endif + + if (fpGML) + VSIFCloseL(fpGML); + fpGML = NULL; + + CPLFree(m_pszGlobalSRSName); + + CPLFree(m_pszFilteredClassName); +} + +/************************************************************************/ +/* SetSourceFile() */ +/************************************************************************/ + +void GMLReader::SetSourceFile( const char *pszFilename ) + +{ + CPLFree( m_pszFilename ); + m_pszFilename = CPLStrdup( pszFilename ); +} + +/************************************************************************/ +/* GetSourceFileName() */ +/************************************************************************/ + +const char* GMLReader::GetSourceFileName() + +{ + return m_pszFilename; +} + +/************************************************************************/ +/* SetFP() */ +/************************************************************************/ + +void GMLReader::SetFP( VSILFILE* fp ) +{ + fpGML = fp; +} + +/************************************************************************/ +/* SetupParser() */ +/************************************************************************/ + +int GMLReader::SetupParser() + +{ + if (fpGML == NULL) + fpGML = VSIFOpenL(m_pszFilename, "rt"); + if (fpGML != NULL) + VSIFSeekL( fpGML, 0, SEEK_SET ); + + int bRet = -1; +#ifdef HAVE_EXPAT + if (bUseExpatReader) + bRet = SetupParserExpat(); +#endif + +#ifdef HAVE_XERCES + if (!bUseExpatReader) + bRet = SetupParserXerces(); +#endif + if (bRet < 0) + { + CPLError(CE_Failure, CPLE_AppDefined, "SetupParser(): shouldn't happen"); + return FALSE; + } + + if (!bRet) + return FALSE; + + m_bReadStarted = FALSE; + + // Push an empty state. + PushState( m_poRecycledState ? m_poRecycledState : new GMLReadState() ); + m_poRecycledState = NULL; + + return TRUE; +} + +#ifdef HAVE_XERCES +/************************************************************************/ +/* SetupParserXerces() */ +/************************************************************************/ + +int GMLReader::SetupParserXerces() +{ + { + CPLMutexHolderD(&hMutex); + m_nInstanceCount++; + if( m_eXercesInitState == OGRGML_XERCES_UNINITIALIZED ) + { + try + { + XMLPlatformUtils::Initialize(); + } + + catch (const XMLException& toCatch) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Exception initializing Xerces based GML reader.\n%s", + tr_strdup(toCatch.getMessage()) ); + m_eXercesInitState = OGRGML_XERCES_INIT_FAILED; + return FALSE; + } + m_eXercesInitState = OGRGML_XERCES_INIT_SUCCESSFUL; + } + if( m_eXercesInitState != OGRGML_XERCES_INIT_SUCCESSFUL ) + return FALSE; + } + + // Cleanup any old parser. + if( m_poSAXReader != NULL ) + CleanupParser(); + + // Create and initialize parser. + XMLCh* xmlUriValid = NULL; + XMLCh* xmlUriNS = NULL; + + try{ + m_poSAXReader = XMLReaderFactory::createXMLReader(); + + GMLXercesHandler* poXercesHandler = new GMLXercesHandler( this ); + m_poGMLHandler = poXercesHandler; + + m_poSAXReader->setContentHandler( poXercesHandler ); + m_poSAXReader->setErrorHandler( poXercesHandler ); + m_poSAXReader->setLexicalHandler( poXercesHandler ); + m_poSAXReader->setEntityResolver( poXercesHandler ); + m_poSAXReader->setDTDHandler( poXercesHandler ); + + xmlUriValid = XMLString::transcode("http://xml.org/sax/features/validation"); + xmlUriNS = XMLString::transcode("http://xml.org/sax/features/namespaces"); + +#if (OGR_GML_VALIDATION) + m_poSAXReader->setFeature( xmlUriValid, true); + m_poSAXReader->setFeature( xmlUriNS, true); + + m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, true ); + m_poSAXReader->setFeature( XMLUni::fgXercesSchema, true ); + +// m_poSAXReader->setDoSchema(true); +// m_poSAXReader->setValidationSchemaFullChecking(true); +#else + m_poSAXReader->setFeature( XMLUni::fgSAX2CoreValidation, false); + +#if XERCES_VERSION_MAJOR >= 3 + m_poSAXReader->setFeature( XMLUni::fgXercesSchema, false); +#else + m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, false); +#endif + +#endif + XMLString::release( &xmlUriValid ); + XMLString::release( &xmlUriNS ); + } + catch (...) + { + XMLString::release( &xmlUriValid ); + XMLString::release( &xmlUriNS ); + + CPLError( CE_Warning, CPLE_AppDefined, + "Exception initializing Xerces based GML reader.\n" ); + return FALSE; + } + + if (m_GMLInputSource == NULL && fpGML != NULL) + m_GMLInputSource = new GMLInputSource(fpGML); + + return TRUE; +} +#endif + +/************************************************************************/ +/* SetupParserExpat() */ +/************************************************************************/ + +#ifdef HAVE_EXPAT +int GMLReader::SetupParserExpat() +{ + // Cleanup any old parser. + if( oParser != NULL ) + CleanupParser(); + + oParser = OGRCreateExpatXMLParser(); + m_poGMLHandler = new GMLExpatHandler( this, oParser ); + + XML_SetElementHandler(oParser, GMLExpatHandler::startElementCbk, GMLExpatHandler::endElementCbk); + XML_SetCharacterDataHandler(oParser, GMLExpatHandler::dataHandlerCbk); + XML_SetUserData(oParser, m_poGMLHandler); + + if (pabyBuf == NULL) + pabyBuf = (char*)VSIMalloc(PARSER_BUF_SIZE); + if (pabyBuf == NULL) + return FALSE; + + return TRUE; +} +#endif + +/************************************************************************/ +/* CleanupParser() */ +/************************************************************************/ + +void GMLReader::CleanupParser() + +{ +#ifdef HAVE_XERCES + if( !bUseExpatReader && m_poSAXReader == NULL ) + return; +#endif + +#ifdef HAVE_EXPAT + if ( bUseExpatReader && oParser == NULL ) + return; +#endif + + while( m_poState ) + PopState(); + +#ifdef HAVE_XERCES + delete m_poSAXReader; + m_poSAXReader = NULL; + delete m_GMLInputSource; + m_GMLInputSource = NULL; + delete m_poCompleteFeature; + m_poCompleteFeature = NULL; + m_bEOF = FALSE; +#endif + +#ifdef HAVE_EXPAT + if (oParser) + XML_ParserFree(oParser); + oParser = NULL; + + int i; + for(i=nFeatureTabIndex;ifp = fp; + emptyString = 0; +} + +GMLBinInputStream::~ GMLBinInputStream() +{ +} + +#if XERCES_VERSION_MAJOR >= 3 +XMLFilePos GMLBinInputStream::curPos() const +{ + return (XMLFilePos)VSIFTellL(fp); +} + +XMLSize_t GMLBinInputStream::readBytes(XMLByte* const toFill, const XMLSize_t maxToRead) +{ + return (XMLSize_t)VSIFReadL(toFill, 1, maxToRead, fp); +} + +const XMLCh* GMLBinInputStream::getContentType() const +{ + return &emptyString; +} +#else +unsigned int GMLBinInputStream::curPos() const +{ + return (unsigned int)VSIFTellL(fp); +} + +unsigned int GMLBinInputStream::readBytes(XMLByte* const toFill, const unsigned int maxToRead) +{ + return (unsigned int)VSIFReadL(toFill, 1, maxToRead, fp); +} +#endif + +GMLInputSource::GMLInputSource(VSILFILE* fp, MemoryManager* const manager) : InputSource(manager) +{ + binInputStream = new GMLBinInputStream(fp); +} + +GMLInputSource::~GMLInputSource() +{ +} + +BinInputStream* GMLInputSource::makeStream() const +{ + return binInputStream; +} + +#endif // HAVE_XERCES + +/************************************************************************/ +/* NextFeatureXerces() */ +/************************************************************************/ + +#ifdef HAVE_XERCES +GMLFeature *GMLReader::NextFeatureXerces() + +{ + GMLFeature *poReturn = NULL; + + if (m_bEOF) + return NULL; + + try + { + if( !m_bReadStarted ) + { + if( m_poSAXReader == NULL ) + SetupParser(); + + m_bReadStarted = TRUE; + + if (m_GMLInputSource == NULL) + return NULL; + + if( !m_poSAXReader->parseFirst( *m_GMLInputSource, m_oToFill ) ) + return NULL; + } + + while( m_poCompleteFeature == NULL + && !m_bStopParsing + && m_poSAXReader->parseNext( m_oToFill ) ) {} + + if (m_poCompleteFeature == NULL) + m_bEOF = TRUE; + + poReturn = m_poCompleteFeature; + m_poCompleteFeature = NULL; + + } + catch (const XMLException& toCatch) + { + char *pszErrorMessage = tr_strdup( toCatch.getMessage() ); + CPLDebug( "GML", + "Error during NextFeature()! Message:\n%s", + pszErrorMessage ); + CPLFree(pszErrorMessage); + m_bStopParsing = TRUE; + } + catch (const SAXException& toCatch) + { + char *pszErrorMessage = tr_strdup( toCatch.getMessage() ); + CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage); + CPLFree(pszErrorMessage); + m_bStopParsing = TRUE; + } + + return poReturn; +} +#endif + +#ifdef HAVE_EXPAT +GMLFeature *GMLReader::NextFeatureExpat() + +{ + if (!m_bReadStarted) + { + if (oParser == NULL) + SetupParser(); + + m_bReadStarted = TRUE; + } + + if (fpGML == NULL || m_bStopParsing) + return NULL; + + if (nFeatureTabIndex < nFeatureTabLength) + { + return ppoFeatureTab[nFeatureTabIndex++]; + } + + if (VSIFEofL(fpGML)) + return NULL; + + nFeatureTabLength = 0; + nFeatureTabIndex = 0; + + int nDone; + do + { + /* Reset counter that is used to detect billion laugh attacks */ + ((GMLExpatHandler*)m_poGMLHandler)->ResetDataHandlerCounter(); + + unsigned int nLen = + (unsigned int)VSIFReadL( pabyBuf, 1, PARSER_BUF_SIZE, fpGML ); + nDone = VSIFEofL(fpGML); + + /* Some files, such as APT_AIXM.xml from https://nfdc.faa.gov/webContent/56DaySub/2015-03-05/aixm5.1.zip */ + /* end with trailing nul characters. This test is not fully bullet-proof in case */ + /* the nul characters would occur at a buffer boundary */ + while( nDone && nLen > 0 && pabyBuf[nLen-1] == '\0' ) + nLen --; + + if (XML_Parse(oParser, pabyBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of GML file failed : %s " + "at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + m_bStopParsing = TRUE; + } + if (!m_bStopParsing) + m_bStopParsing = ((GMLExpatHandler*)m_poGMLHandler)->HasStoppedParsing(); + + } while (!nDone && !m_bStopParsing && nFeatureTabLength == 0); + + return (nFeatureTabLength) ? ppoFeatureTab[nFeatureTabIndex++] : NULL; +} +#endif + +GMLFeature *GMLReader::NextFeature() +{ +#ifdef HAVE_EXPAT + if (bUseExpatReader) + return NextFeatureExpat(); +#endif + +#ifdef HAVE_XERCES + if (!bUseExpatReader) + return NextFeatureXerces(); +#endif + + CPLError(CE_Failure, CPLE_AppDefined, "NextFeature(): Should not happen"); + return NULL; +} + +/************************************************************************/ +/* PushFeature() */ +/* */ +/* Create a feature based on the named element. If the */ +/* corresponding feature class doesn't exist yet, then create */ +/* it now. A new GMLReadState will be created for the feature, */ +/* and it will be placed within that state. The state is */ +/* pushed onto the readstate stack. */ +/************************************************************************/ + +void GMLReader::PushFeature( const char *pszElement, + const char *pszFID, + int nClassIndex ) + +{ + int iClass; + + if( nClassIndex != INT_MAX ) + { + iClass = nClassIndex; + } + else + { + /* -------------------------------------------------------------------- */ + /* Find the class of this element. */ + /* -------------------------------------------------------------------- */ + for( iClass = 0; iClass < m_nClassCount; iClass++ ) + { + if( EQUAL(pszElement,m_papoClass[iClass]->GetElementName()) ) + break; + } + + /* -------------------------------------------------------------------- */ + /* Create a new feature class for this element, if there is no */ + /* existing class for it. */ + /* -------------------------------------------------------------------- */ + if( iClass == m_nClassCount ) + { + CPLAssert( !m_bClassListLocked ); + + GMLFeatureClass *poNewClass = new GMLFeatureClass( pszElement ); + + AddClass( poNewClass ); + } + } + +/* -------------------------------------------------------------------- */ +/* Create a feature of this feature class. Try to set the fid */ +/* if available. */ +/* -------------------------------------------------------------------- */ + GMLFeature *poFeature = new GMLFeature( m_papoClass[iClass] ); + if( pszFID != NULL ) + { + poFeature->SetFID( pszFID ); + } + +/* -------------------------------------------------------------------- */ +/* Create and push a new read state. */ +/* -------------------------------------------------------------------- */ + GMLReadState *poState; + + poState = m_poRecycledState ? m_poRecycledState : new GMLReadState(); + m_poRecycledState = NULL; + poState->m_poFeature = poFeature; + PushState( poState ); +} + +/************************************************************************/ +/* IsFeatureElement() */ +/* */ +/* Based on context and the element name, is this element a new */ +/* GML feature element? */ +/************************************************************************/ + +int GMLReader::GetFeatureElementIndex( const char *pszElement, int nElementLength, + GMLAppSchemaType eAppSchemaType ) + +{ + const char *pszLast = m_poState->GetLastComponent(); + size_t nLenLast = m_poState->GetLastComponentLen(); + + if( eAppSchemaType == APPSCHEMA_MTKGML ) + { + if( m_poState->m_nPathLength != 1 ) + return -1; + } + else if( (nLenLast >= 6 && EQUAL(pszLast+nLenLast-6,"member")) || + (nLenLast >= 7 && EQUAL(pszLast+nLenLast-7,"members")) ) + { + /* Default feature name */ + } + else + { + if (nLenLast == 4 && strcmp(pszLast, "dane") == 0) + { + /* Polish TBD GML */ + } + + /* Begin of OpenLS */ + else if (nLenLast == 19 && nElementLength == 15 && + strcmp(pszLast, "GeocodeResponseList") == 0 && + strcmp(pszElement, "GeocodedAddress") == 0) + { + } + else if (nLenLast == 22 && + strcmp(pszLast, "DetermineRouteResponse") == 0) + { + /* We don't want the children of RouteInstructionsList */ + /* to be a single feature. We want each RouteInstruction */ + /* to be a feature */ + if (strcmp(pszElement, "RouteInstructionsList") == 0) + return -1; + } + else if (nElementLength == 16 && nLenLast == 21 && + strcmp(pszElement, "RouteInstruction") == 0 && + strcmp(pszLast, "RouteInstructionsList") == 0) + { + } + /* End of OpenLS */ + + else if (nLenLast > 6 && strcmp(pszLast + nLenLast - 6, "_layer") == 0 && + nElementLength > 8 && strcmp(pszElement + nElementLength - 8, "_feature") == 0) + { + /* GML answer of MapServer WMS GetFeatureInfo request */ + } + + /* Begin of CSW SearchResults */ + else if (nElementLength == strlen("BriefRecord") && + nLenLast == strlen("SearchResults") && + strcmp(pszElement, "BriefRecord") == 0 && + strcmp(pszLast, "SearchResults") == 0) + { + } + else if (nElementLength == strlen("SummaryRecord") && + nLenLast == strlen("SearchResults") && + strcmp(pszElement, "SummaryRecord") == 0 && + strcmp(pszLast, "SearchResults") == 0) + { + } + else if (nElementLength == strlen("Record") && + nLenLast == strlen("SearchResults") && + strcmp(pszElement, "Record") == 0 && + strcmp(pszLast, "SearchResults") == 0) + { + } + /* End of CSW SearchResults */ + + else + { + if( m_bClassListLocked ) + { + for( int i = 0; i < m_nClassCount; i++ ) + { + if( m_poState->osPath.size() + 1 + nElementLength == m_papoClass[i]->GetElementNameLen() && + m_papoClass[i]->GetElementName()[m_poState->osPath.size()] == '|' && + memcmp(m_poState->osPath.c_str(), m_papoClass[i]->GetElementName(), m_poState->osPath.size()) == 0 && + memcmp(pszElement,m_papoClass[i]->GetElementName() + 1 + m_poState->osPath.size(), nElementLength) == 0 ) + { + return i; + } + } + } + return -1; + } + } + + // If the class list isn't locked, any element that is a featureMember + // will do. + if( !m_bClassListLocked ) + return INT_MAX; + + // otherwise, find a class with the desired element name. + for( int i = 0; i < m_nClassCount; i++ ) + { + if( nElementLength == (int)m_papoClass[i]->GetElementNameLen() && + memcmp(pszElement,m_papoClass[i]->GetElementName(), nElementLength) == 0 ) + return i; + } + + return -1; +} + +/************************************************************************/ +/* IsCityGMLGenericAttributeElement() */ +/************************************************************************/ + +int GMLReader::IsCityGMLGenericAttributeElement( const char *pszElement, void* attr ) + +{ + if( strcmp(pszElement, "stringAttribute") != 0 && + strcmp(pszElement, "intAttribute") != 0 && + strcmp(pszElement, "doubleAttribute") != 0 ) + return FALSE; + + char* pszVal = m_poGMLHandler->GetAttributeValue(attr, "name"); + if (pszVal == NULL) + return FALSE; + + GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass(); + + // If the schema is not yet locked, then any simple element + // is potentially an attribute. + if( !poClass->IsSchemaLocked() ) + { + CPLFree(pszVal); + return TRUE; + } + + for( int i = 0; i < poClass->GetPropertyCount(); i++ ) + { + if( strcmp(poClass->GetProperty(i)->GetSrcElement(),pszVal) == 0 ) + { + CPLFree(pszVal); + return TRUE; + } + } + + CPLFree(pszVal); + return FALSE; +} + +/************************************************************************/ +/* GetAttributeElementIndex() */ +/************************************************************************/ + +int GMLReader::GetAttributeElementIndex( const char *pszElement, int nLen, + const char *pszAttrKey ) + +{ + GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass(); + + // If the schema is not yet locked, then any simple element + // is potentially an attribute. + if( !poClass->IsSchemaLocked() ) + return INT_MAX; + + // Otherwise build the path to this element into a single string + // and compare against known attributes. + if( m_poState->m_nPathLength == 0 ) + { + if( pszAttrKey == NULL ) + return poClass->GetPropertyIndexBySrcElement(pszElement, nLen); + else + { + int nFullLen = nLen + 1 + strlen(pszAttrKey); + osElemPath.reserve(nFullLen); + osElemPath.assign(pszElement, nLen); + osElemPath.append(1, '@'); + osElemPath.append(pszAttrKey); + return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(), nFullLen); + } + } + else + { + int nFullLen = nLen + m_poState->osPath.size() + 1; + if( pszAttrKey != NULL ) + nFullLen += 1 + strlen(pszAttrKey); + osElemPath.reserve(nFullLen); + osElemPath.assign(m_poState->osPath); + osElemPath.append(1, '|'); + osElemPath.append(pszElement, nLen); + if( pszAttrKey != NULL ) + { + osElemPath.append(1, '@'); + osElemPath.append(pszAttrKey); + } + return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(), nFullLen); + } +} + +/************************************************************************/ +/* PopState() */ +/************************************************************************/ + +void GMLReader::PopState() + +{ + if( m_poState != NULL ) + { +#ifdef HAVE_XERCES + if( !bUseExpatReader && m_poState->m_poFeature != NULL && + m_poCompleteFeature == NULL ) + { + m_poCompleteFeature = m_poState->m_poFeature; + m_poState->m_poFeature = NULL; + } +#endif + +#ifdef HAVE_EXPAT + if ( bUseExpatReader && m_poState->m_poFeature != NULL ) + { + if (nFeatureTabLength >= nFeatureTabAlloc) + { + nFeatureTabAlloc = nFeatureTabLength * 4 / 3 + 16; + ppoFeatureTab = (GMLFeature**) + CPLRealloc(ppoFeatureTab, + sizeof(GMLFeature*) * (nFeatureTabAlloc)); + } + ppoFeatureTab[nFeatureTabLength] = m_poState->m_poFeature; + nFeatureTabLength++; + + m_poState->m_poFeature = NULL; + } +#endif + + GMLReadState *poParent; + + poParent = m_poState->m_poParentState; + + delete m_poRecycledState; + m_poRecycledState = m_poState; + m_poRecycledState->Reset(); + m_poState = poParent; + } +} + +/************************************************************************/ +/* PushState() */ +/************************************************************************/ + +void GMLReader::PushState( GMLReadState *poState ) + +{ + poState->m_poParentState = m_poState; + m_poState = poState; +} + +/************************************************************************/ +/* GetClass() */ +/************************************************************************/ + +GMLFeatureClass *GMLReader::GetClass( int iClass ) const + +{ + if( iClass < 0 || iClass >= m_nClassCount ) + return NULL; + else + return m_papoClass[iClass]; +} + +/************************************************************************/ +/* GetClass() */ +/************************************************************************/ + +GMLFeatureClass *GMLReader::GetClass( const char *pszName ) const + +{ + for( int iClass = 0; iClass < m_nClassCount; iClass++ ) + { + if( EQUAL(GetClass(iClass)->GetName(),pszName) ) + return GetClass(iClass); + } + + return NULL; +} + +/************************************************************************/ +/* AddClass() */ +/************************************************************************/ + +int GMLReader::AddClass( GMLFeatureClass *poNewClass ) + +{ + CPLAssert( GetClass( poNewClass->GetName() ) == NULL ); + + m_nClassCount++; + m_papoClass = (GMLFeatureClass **) + CPLRealloc( m_papoClass, sizeof(void*) * m_nClassCount ); + m_papoClass[m_nClassCount-1] = poNewClass; + + if( poNewClass->HasFeatureProperties() ) + m_bLookForClassAtAnyLevel = TRUE; + + return m_nClassCount-1; +} + +/************************************************************************/ +/* ClearClasses() */ +/************************************************************************/ + +void GMLReader::ClearClasses() + +{ + for( int i = 0; i < m_nClassCount; i++ ) + delete m_papoClass[i]; + CPLFree( m_papoClass ); + + m_nClassCount = 0; + m_papoClass = NULL; + m_bLookForClassAtAnyLevel = FALSE; +} + +/************************************************************************/ +/* SetFeaturePropertyDirectly() */ +/* */ +/* Set the property value on the current feature, adding the */ +/* property name to the GMLFeatureClass if required. */ +/* The pszValue ownership is passed to this function. */ +/************************************************************************/ + +void GMLReader::SetFeaturePropertyDirectly( const char *pszElement, + char *pszValue, + int iPropertyIn, + GMLPropertyType eType ) + +{ + GMLFeature *poFeature = GetState()->m_poFeature; + + CPLAssert( poFeature != NULL ); + +/* -------------------------------------------------------------------- */ +/* Does this property exist in the feature class? If not, add */ +/* it. */ +/* -------------------------------------------------------------------- */ + GMLFeatureClass *poClass = poFeature->GetClass(); + int iProperty; + + int nPropertyCount = poClass->GetPropertyCount(); + if (iPropertyIn >= 0 && iPropertyIn < nPropertyCount) + { + iProperty = iPropertyIn; + } + else + { + for( iProperty=0; iProperty < nPropertyCount; iProperty++ ) + { + if( strcmp(poClass->GetProperty( iProperty )->GetSrcElement(), + pszElement ) == 0 ) + break; + } + + if( iProperty == nPropertyCount ) + { + if( poClass->IsSchemaLocked() ) + { + CPLDebug("GML","Encountered property missing from class schema : %s.", + pszElement); + CPLFree(pszValue); + return; + } + + CPLString osFieldName; + + if( IsWFSJointLayer() ) + { + /* At that point the element path should be member|layer|property */ + + /* Strip member| prefix. Should always be true normally */ + if( strncmp(pszElement, "member|", strlen("member|")) == 0 ) + osFieldName = pszElement + strlen("member|"); + + /* Replace layer|property by layer_property */ + size_t iPos = osFieldName.find('|'); + if( iPos != std::string::npos ) + osFieldName[iPos] = '.'; + + /* Special case for gml:id on layer */ + iPos = osFieldName.find("@id"); + if( iPos != std::string::npos ) + { + osFieldName.resize(iPos); + osFieldName += ".gml_id"; + } + } + else if( strchr(pszElement,'|') == NULL ) + osFieldName = pszElement; + else + { + osFieldName = strrchr(pszElement,'|') + 1; + if( poClass->GetPropertyIndex(osFieldName) != -1 ) + osFieldName = pszElement; + } + + size_t nPos = osFieldName.find("@"); + if( nPos != std::string::npos ) + osFieldName[nPos] = '_'; + + // Does this conflict with an existing property name? + while( poClass->GetProperty(osFieldName) != NULL ) + { + osFieldName += "_"; + } + + GMLPropertyDefn *poPDefn = new GMLPropertyDefn(osFieldName,pszElement); + + if( EQUAL(CPLGetConfigOption( "GML_FIELDTYPES", ""), "ALWAYS_STRING") ) + poPDefn->SetType( GMLPT_String ); + else if( eType != GMLPT_Untyped ) + poPDefn->SetType( eType ); + + if (poClass->AddProperty( poPDefn ) < 0) + { + delete poPDefn; + CPLFree(pszValue); + return; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Set the property */ +/* -------------------------------------------------------------------- */ + poFeature->SetPropertyDirectly( iProperty, pszValue ); + +/* -------------------------------------------------------------------- */ +/* Do we need to update the property type? */ +/* -------------------------------------------------------------------- */ + if( !poClass->IsSchemaLocked() ) + { + poClass->GetProperty(iProperty)->AnalysePropertyValue( + poFeature->GetProperty(iProperty), m_bSetWidthFlag ); + } +} + +/************************************************************************/ +/* LoadClasses() */ +/************************************************************************/ + +int GMLReader::LoadClasses( const char *pszFile ) + +{ + // Add logic later to determine reasonable default schema file. + if( pszFile == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Load the raw XML file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + int nLength; + char *pszWholeText; + + fp = VSIFOpenL( pszFile, "rb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open file %s.", pszFile ); + return FALSE; + } + + VSIFSeekL( fp, 0, SEEK_END ); + nLength = (int) VSIFTellL( fp ); + VSIFSeekL( fp, 0, SEEK_SET ); + + pszWholeText = (char *) VSIMalloc(nLength+1); + if( pszWholeText == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to allocate %d byte buffer for %s,\n" + "is this really a GMLFeatureClassList file?", + nLength, pszFile ); + VSIFCloseL( fp ); + return FALSE; + } + + if( VSIFReadL( pszWholeText, nLength, 1, fp ) != 1 ) + { + VSIFree( pszWholeText ); + VSIFCloseL( fp ); + CPLError( CE_Failure, CPLE_AppDefined, + "Read failed on %s.", pszFile ); + return FALSE; + } + pszWholeText[nLength] = '\0'; + + VSIFCloseL( fp ); + + if( strstr( pszWholeText, "eType != CXT_Element + || !EQUAL(psRoot->pszValue,"GMLFeatureClassList") ) + { + CPLDestroyXMLNode(psRoot); + CPLError( CE_Failure, CPLE_AppDefined, + "File %s is not a GMLFeatureClassList document.", + pszFile ); + return FALSE; + } + + const char* pszSequentialLayers = CPLGetXMLValue(psRoot, "SequentialLayers", NULL); + if (pszSequentialLayers) + m_bSequentialLayers = CSLTestBoolean(pszSequentialLayers); + +/* -------------------------------------------------------------------- */ +/* Extract feature classes for all definitions found. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psThis; + + for( psThis = psRoot->psChild; psThis != NULL; psThis = psThis->psNext ) + { + if( psThis->eType == CXT_Element + && EQUAL(psThis->pszValue,"GMLFeatureClass") ) + { + GMLFeatureClass *poClass; + + poClass = new GMLFeatureClass(); + + if( !poClass->InitializeFromXML( psThis ) ) + { + delete poClass; + CPLDestroyXMLNode( psRoot ); + return FALSE; + } + + poClass->SetSchemaLocked( TRUE ); + + AddClass( poClass ); + } + } + + CPLDestroyXMLNode( psRoot ); + + SetClassListLocked( TRUE ); + + return TRUE; +} + +/************************************************************************/ +/* SaveClasses() */ +/************************************************************************/ + +int GMLReader::SaveClasses( const char *pszFile ) + +{ + // Add logic later to determine reasonable default schema file. + if( pszFile == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Create in memory schema tree. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRoot; + + psRoot = CPLCreateXMLNode( NULL, CXT_Element, "GMLFeatureClassList" ); + + if (m_bSequentialLayers != -1 && m_nClassCount > 1) + { + CPLCreateXMLElementAndValue( psRoot, "SequentialLayers", + m_bSequentialLayers ? "true" : "false" ); + } + + for( int iClass = 0; iClass < m_nClassCount; iClass++ ) + { + CPLAddXMLChild( psRoot, m_papoClass[iClass]->SerializeToXML() ); + } + +/* -------------------------------------------------------------------- */ +/* Serialize to disk. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp; + int bSuccess = TRUE; + char *pszWholeText = CPLSerializeXMLTree( psRoot ); + + CPLDestroyXMLNode( psRoot ); + + fp = VSIFOpenL( pszFile, "wb" ); + + if( fp == NULL ) + bSuccess = FALSE; + else if( VSIFWriteL( pszWholeText, strlen(pszWholeText), 1, fp ) != 1 ) + bSuccess = FALSE; + else + VSIFCloseL( fp ); + + CPLFree( pszWholeText ); + + return bSuccess; +} + +/************************************************************************/ +/* PrescanForSchema() */ +/* */ +/* For now we use a pretty dumb approach of just doing a normal */ +/* scan of the whole file, building up the schema information. */ +/* Eventually we hope to do a more efficient scan when just */ +/* looking for schema information. */ +/************************************************************************/ + +int GMLReader::PrescanForSchema( int bGetExtents, + int bAnalyzeSRSPerFeature, + int bOnlyDetectSRS ) + +{ + GMLFeature *poFeature; + + if( m_pszFilename == NULL ) + return FALSE; + + if( !bOnlyDetectSRS ) + { + SetClassListLocked( FALSE ); + ClearClasses(); + } + + if( !SetupParser() ) + return FALSE; + + m_bCanUseGlobalSRSName = TRUE; + + GMLFeatureClass *poLastClass = NULL; + + m_bSequentialLayers = TRUE; + + void* hCacheSRS = GML_BuildOGRGeometryFromList_CreateCache(); + + std::string osWork; + + while( (poFeature = NextFeature()) != NULL ) + { + GMLFeatureClass *poClass = poFeature->GetClass(); + + if (poLastClass != NULL && poClass != poLastClass && + poClass->GetFeatureCount() != -1) + m_bSequentialLayers = FALSE; + poLastClass = poClass; + + if( poClass->GetFeatureCount() == -1 ) + poClass->SetFeatureCount( 1 ); + else + poClass->SetFeatureCount( poClass->GetFeatureCount() + 1 ); + + const CPLXMLNode* const * papsGeometry = poFeature->GetGeometryList(); + if( !bOnlyDetectSRS && papsGeometry != NULL && papsGeometry[0] != NULL ) + { + if( poClass->GetGeometryPropertyCount() == 0 ) + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( "", "", wkbUnknown, -1, TRUE ) ); + } + +#ifdef SUPPORT_GEOMETRY + if( bGetExtents && papsGeometry != NULL ) + { + OGRGeometry *poGeometry = GML_BuildOGRGeometryFromList( + papsGeometry, TRUE, m_bInvertAxisOrderIfLatLong, + NULL, m_bConsiderEPSGAsURN, m_bGetSecondaryGeometryOption, + hCacheSRS, m_bFaceHoleNegative ); + + if( poGeometry != NULL && poClass->GetGeometryPropertyCount() > 0 ) + { + double dfXMin, dfXMax, dfYMin, dfYMax; + OGREnvelope sEnvelope; + + OGRwkbGeometryType eGType = (OGRwkbGeometryType) + poClass->GetGeometryProperty(0)->GetType(); + + if( bAnalyzeSRSPerFeature ) + { + const char* pszSRSName = GML_ExtractSrsNameFromGeometry(papsGeometry, + osWork, + m_bConsiderEPSGAsURN); + if (pszSRSName != NULL) + m_bCanUseGlobalSRSName = FALSE; + poClass->MergeSRSName(pszSRSName); + } + + // Merge geometry type into layer. + if( poClass->GetFeatureCount() == 1 && eGType == wkbUnknown ) + eGType = wkbNone; + + poClass->GetGeometryProperty(0)->SetType( + (int) OGRMergeGeometryTypesEx( + eGType, poGeometry->getGeometryType(), TRUE ) ); + + // merge extents. + if (!poGeometry->IsEmpty()) + { + poGeometry->getEnvelope( &sEnvelope ); + if( poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax) ) + { + dfXMin = MIN(dfXMin,sEnvelope.MinX); + dfXMax = MAX(dfXMax,sEnvelope.MaxX); + dfYMin = MIN(dfYMin,sEnvelope.MinY); + dfYMax = MAX(dfYMax,sEnvelope.MaxY); + } + else + { + dfXMin = sEnvelope.MinX; + dfXMax = sEnvelope.MaxX; + dfYMin = sEnvelope.MinY; + dfYMax = sEnvelope.MaxY; + } + + poClass->SetExtents( dfXMin, dfXMax, dfYMin, dfYMax ); + } + delete poGeometry; + + } +#endif /* def SUPPORT_GEOMETRY */ + } + + delete poFeature; + } + + GML_BuildOGRGeometryFromList_DestroyCache(hCacheSRS); + + for( int i = 0; i < m_nClassCount; i++ ) + { + GMLFeatureClass *poClass = m_papoClass[i]; + const char* pszSRSName = poClass->GetSRSName(); + + if (m_bCanUseGlobalSRSName) + pszSRSName = m_pszGlobalSRSName; + + OGRSpatialReference oSRS; + if (m_bInvertAxisOrderIfLatLong && GML_IsSRSLatLongOrder(pszSRSName) && + oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE) + { + OGR_SRSNode *poGEOGCS = oSRS.GetAttrNode( "GEOGCS" ); + if( poGEOGCS != NULL ) + poGEOGCS->StripNodes( "AXIS" ); + + OGR_SRSNode *poPROJCS = oSRS.GetAttrNode( "PROJCS" ); + if (poPROJCS != NULL && oSRS.EPSGTreatsAsNorthingEasting()) + poPROJCS->StripNodes( "AXIS" ); + + char* pszWKT = NULL; + if (oSRS.exportToWkt(&pszWKT) == OGRERR_NONE) + poClass->SetSRSName(pszWKT); + CPLFree(pszWKT); + + /* So when we have computed the extent, we didn't know yet */ + /* the SRS to use. Now we know it, we have to fix the extent */ + /* order */ + if (m_bCanUseGlobalSRSName) + { + double dfXMin, dfXMax, dfYMin, dfYMax; + if( poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax) ) + poClass->SetExtents( dfYMin, dfYMax, dfXMin, dfXMax ); + } + } + else if( !bAnalyzeSRSPerFeature && + pszSRSName != NULL && + poClass->GetSRSName() == NULL && + oSRS.SetFromUserInput(pszSRSName) == OGRERR_NONE ) + { + char* pszWKT = NULL; + if (oSRS.exportToWkt(&pszWKT) == OGRERR_NONE) + poClass->SetSRSName(pszWKT); + CPLFree(pszWKT); + } + } + + CleanupParser(); + + return TRUE; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void GMLReader::ResetReading() + +{ + CleanupParser(); + SetFilteredClassName(NULL); +} + +/************************************************************************/ +/* SetGlobalSRSName() */ +/************************************************************************/ + +void GMLReader::SetGlobalSRSName( const char* pszGlobalSRSName ) +{ + if (m_pszGlobalSRSName == NULL && pszGlobalSRSName != NULL) + { + const char* pszVertCS_EPSG; + if( strncmp(pszGlobalSRSName, "EPSG:", 5) == 0 && + (pszVertCS_EPSG = strstr(pszGlobalSRSName, ", EPSG:")) != NULL ) + { + m_pszGlobalSRSName = CPLStrdup(CPLSPrintf("EPSG:%d+%d", + atoi(pszGlobalSRSName + 5), + atoi(pszVertCS_EPSG + 7))); + } + else if (strncmp(pszGlobalSRSName, "EPSG:", 5) == 0 && + m_bConsiderEPSGAsURN) + { + m_pszGlobalSRSName = CPLStrdup(CPLSPrintf("urn:ogc:def:crs:EPSG::%s", + pszGlobalSRSName+5)); + } + else + { + m_pszGlobalSRSName = CPLStrdup(pszGlobalSRSName); + } + } +} + +/************************************************************************/ +/* SetFilteredClassName() */ +/************************************************************************/ + +int GMLReader::SetFilteredClassName(const char* pszClassName) +{ + CPLFree(m_pszFilteredClassName); + m_pszFilteredClassName = (pszClassName) ? CPLStrdup(pszClassName) : NULL; + + m_nFilteredClassIndex = -1; + if( m_pszFilteredClassName != NULL ) + { + for( int i = 0; i < m_nClassCount; i++ ) + { + if( strcmp(m_papoClass[i]->GetElementName(), pszClassName) == 0 ) + { + m_nFilteredClassIndex = i; + break; + } + } + } + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlreader.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlreader.h new file mode 100644 index 000000000..667c2ab7c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlreader.h @@ -0,0 +1,328 @@ +/****************************************************************************** + * $Id: gmlreader.h 29051 2015-04-29 17:18:37Z rouault $ + * + * Project: GML Reader + * Purpose: Public Declarations for OGR free GML Reader code. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _GMLREADER_H_INCLUDED +#define _GMLREADER_H_INCLUDED + +#include "cpl_port.h" +#include "cpl_vsi.h" +#include "cpl_minixml.h" + +#include + +typedef enum { + GMLPT_Untyped = 0, + GMLPT_String = 1, + GMLPT_Integer = 2, + GMLPT_Real = 3, + GMLPT_Complex = 4, + GMLPT_StringList = 5, + GMLPT_IntegerList = 6, + GMLPT_RealList = 7, + GMLPT_FeatureProperty = 8, + GMLPT_FeaturePropertyList = 9, + GMLPT_Boolean = 10, + GMLPT_BooleanList = 11, + GMLPT_Short = 12, + GMLPT_Float = 13, + GMLPT_Integer64 = 14, + GMLPT_Integer64List = 15 +} GMLPropertyType; + +/************************************************************************/ +/* GMLPropertyDefn */ +/************************************************************************/ + +typedef struct +{ + int nSubProperties; + char** papszSubProperties; + char* aszSubProperties[2]; /* Optimization in the case of nSubProperties == 1 */ +} GMLProperty; + +class CPL_DLL GMLPropertyDefn +{ + char *m_pszName; + GMLPropertyType m_eType; + int m_nWidth; + int m_nPrecision; + char *m_pszSrcElement; + size_t m_nSrcElementLen; + char *m_pszCondition; + int m_bNullable; + +public: + + GMLPropertyDefn( const char *pszName, const char *pszSrcElement=NULL ); + ~GMLPropertyDefn(); + + const char *GetName() const { return m_pszName; } + + GMLPropertyType GetType() const { return m_eType; } + void SetType( GMLPropertyType eType ) { m_eType = eType; } + void SetWidth( int nWidth) { m_nWidth = nWidth; } + int GetWidth() const { return m_nWidth; } + void SetPrecision( int nPrecision) { m_nPrecision = nPrecision; } + int GetPrecision() const { return m_nPrecision; } + void SetSrcElement( const char *pszSrcElement ); + const char *GetSrcElement() const { return m_pszSrcElement; } + size_t GetSrcElementLen() const { return m_nSrcElementLen; } + + void SetCondition( const char *pszCondition ); + const char *GetCondition() const { return m_pszCondition; } + + void SetNullable( int bNullable ) { m_bNullable = bNullable; } + int IsNullable() const { return m_bNullable; } + + void AnalysePropertyValue( const GMLProperty* psGMLProperty, + int bSetWidth = TRUE ); + + static bool IsSimpleType( GMLPropertyType eType ) + { return eType == GMLPT_String || eType == GMLPT_Integer || eType == GMLPT_Real; } +}; + +/************************************************************************/ +/* GMLGeometryPropertyDefn */ +/************************************************************************/ + +class CPL_DLL GMLGeometryPropertyDefn +{ + char *m_pszName; + char *m_pszSrcElement; + int m_nGeometryType; + int m_nAttributeIndex; + int m_bNullable; + +public: + GMLGeometryPropertyDefn( const char *pszName, const char *pszSrcElement, + int nType, int nAttributeIndex, + int bNullable ); + ~GMLGeometryPropertyDefn(); + + const char *GetName() const { return m_pszName; } + + int GetType() const { return m_nGeometryType; } + void SetType(int nType) { m_nGeometryType = nType; } + const char *GetSrcElement() const { return m_pszSrcElement; } + + int GetAttributeIndex() const { return m_nAttributeIndex; } + + int IsNullable() const { return m_bNullable; } +}; + +/************************************************************************/ +/* GMLFeatureClass */ +/************************************************************************/ +class CPL_DLL GMLFeatureClass +{ + char *m_pszName; + char *m_pszElementName; + int n_nNameLen; + int n_nElementNameLen; + int m_nPropertyCount; + GMLPropertyDefn **m_papoProperty; + + int m_nGeometryPropertyCount; + GMLGeometryPropertyDefn **m_papoGeometryProperty; + + int m_bSchemaLocked; + + GIntBig m_nFeatureCount; + + char *m_pszExtraInfo; + + int m_bHaveExtents; + double m_dfXMin; + double m_dfXMax; + double m_dfYMin; + double m_dfYMax; + + char *m_pszSRSName; + int m_bSRSNameConsistent; + +public: + GMLFeatureClass( const char *pszName = "" ); + ~GMLFeatureClass(); + + const char *GetElementName() const; + size_t GetElementNameLen() const; + void SetElementName( const char *pszElementName ); + + const char *GetName() const { return m_pszName; } + void SetName(const char* pszNewName); + int GetPropertyCount() const { return m_nPropertyCount; } + GMLPropertyDefn *GetProperty( int iIndex ) const; + int GetPropertyIndex( const char *pszName ) const; + GMLPropertyDefn *GetProperty( const char *pszName ) const + { return GetProperty( GetPropertyIndex(pszName) ); } + int GetPropertyIndexBySrcElement( const char *pszElement, int nLen ) const; + void StealProperties(); + + int GetGeometryPropertyCount() const { return m_nGeometryPropertyCount; } + GMLGeometryPropertyDefn *GetGeometryProperty( int iIndex ) const; + int GetGeometryPropertyIndexBySrcElement( const char *pszElement ) const; + void StealGeometryProperties(); + + int HasFeatureProperties(); + + int AddProperty( GMLPropertyDefn * ); + int AddGeometryProperty( GMLGeometryPropertyDefn * ); + void ClearGeometryProperties(); + + int IsSchemaLocked() const { return m_bSchemaLocked; } + void SetSchemaLocked( int bLock ) { m_bSchemaLocked = bLock; } + + const char *GetExtraInfo(); + void SetExtraInfo( const char * ); + + GIntBig GetFeatureCount(); + void SetFeatureCount( GIntBig ); + + int HasExtents() const { return m_bHaveExtents; } + void SetExtents( double dfXMin, double dfXMax, + double dFYMin, double dfYMax ); + int GetExtents( double *pdfXMin, double *pdfXMax, + double *pdFYMin, double *pdfYMax ); + + void SetSRSName( const char* pszSRSName ); + void MergeSRSName( const char* pszSRSName ); + const char *GetSRSName() { return m_pszSRSName; } + + CPLXMLNode *SerializeToXML(); + int InitializeFromXML( CPLXMLNode * ); +}; + +/************************************************************************/ +/* GMLFeature */ +/************************************************************************/ + +class CPL_DLL GMLFeature +{ + GMLFeatureClass *m_poClass; + char *m_pszFID; + + int m_nPropertyCount; + GMLProperty *m_pasProperties; + + int m_nGeometryCount; + CPLXMLNode **m_papsGeometry; /* NULL-terminated. Alias to m_apsGeometry if m_nGeometryCount <= 1 */ + CPLXMLNode *m_apsGeometry[2]; /* NULL-terminated */ + + // string list of named non-schema properties - used by NAS driver. + char **m_papszOBProperties; + +public: + GMLFeature( GMLFeatureClass * ); + ~GMLFeature(); + + GMLFeatureClass*GetClass() const { return m_poClass; } + + void SetGeometryDirectly( CPLXMLNode* psGeom ); + void SetGeometryDirectly( int nIdx, CPLXMLNode* psGeom ); + void AddGeometry( CPLXMLNode* psGeom ); + const CPLXMLNode* const * GetGeometryList() const { return m_papsGeometry; } + const CPLXMLNode* GetGeometryRef( int nIdx ) const; + + void SetPropertyDirectly( int i, char *pszValue ); + + const GMLProperty*GetProperty( int i ) const { return (i < m_nPropertyCount) ? &m_pasProperties[i] : NULL; } + + const char *GetFID() const { return m_pszFID; } + void SetFID( const char *pszFID ); + + void Dump( FILE *fp ); + + // Out of Band property handling - special stuff like relations for NAS. + void AddOBProperty( const char *pszName, const char *pszValue ); + const char *GetOBProperty( const char *pszName ); + char **GetOBProperties(); +}; + +/************************************************************************/ +/* IGMLReader */ +/************************************************************************/ +class CPL_DLL IGMLReader +{ +public: + virtual ~IGMLReader(); + + virtual int IsClassListLocked() const = 0; + virtual void SetClassListLocked( int bFlag ) = 0; + + virtual void SetSourceFile( const char *pszFilename ) = 0; + virtual void SetFP( CPL_UNUSED VSILFILE* fp ) {} + virtual const char* GetSourceFileName() = 0; + + virtual int GetClassCount() const = 0; + virtual GMLFeatureClass *GetClass( int i ) const = 0; + virtual GMLFeatureClass *GetClass( const char *pszName ) const = 0; + + virtual int AddClass( GMLFeatureClass *poClass ) = 0; + virtual void ClearClasses() = 0; + + virtual GMLFeature *NextFeature() = 0; + virtual void ResetReading() = 0; + + virtual int LoadClasses( const char *pszFile = NULL ) = 0; + virtual int SaveClasses( const char *pszFile = NULL ) = 0; + + virtual int ResolveXlinks( const char *pszFile, + int* pbOutIsTempFile, + char **papszSkip = NULL, + const int bStrict = FALSE ) = 0; + + virtual int HugeFileResolver( const char *pszFile, + int pbSqlitIsTempFile, + int iSqliteCacheMB ) = 0; + + virtual int PrescanForSchema( int bGetExtents = TRUE, + int bAnalyzeSRSPerFeature = TRUE, + int bOnlyDetectSRS = FALSE ) = 0; + virtual int PrescanForTemplate( void ) = 0; + + virtual int HasStoppedParsing() = 0; + + virtual void SetGlobalSRSName( CPL_UNUSED const char* pszGlobalSRSName ) {} + virtual const char* GetGlobalSRSName() = 0; + virtual int CanUseGlobalSRSName() = 0; + + virtual int SetFilteredClassName(const char* pszClassName) = 0; + virtual const char* GetFilteredClassName() = 0; + + virtual int IsSequentialLayers() const { return FALSE; } +}; + +IGMLReader *CreateGMLReader(int bUseExpatParserPreferably, + int bInvertAxisOrderIfLatLong, + int bConsiderEPSGAsURN, + int bGetSecondaryGeometryOption); + + +#endif /* _GMLREADER_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlreaderp.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlreaderp.h new file mode 100644 index 000000000..202b573ef --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlreaderp.h @@ -0,0 +1,567 @@ +/****************************************************************************** + * $Id: gmlreaderp.h 29217 2015-05-21 09:08:48Z rouault $ + * + * Project: GML Reader + * Purpose: Private Declarations for OGR free GML Reader code. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_GMLREADERP_H_INCLUDED +#define _CPL_GMLREADERP_H_INCLUDED + +#include "gmlreader.h" +#include "ogr_api.h" +#include "cpl_vsi.h" +#include "cpl_multiproc.h" + +#include +#include + +#define PARSER_BUF_SIZE (10*8192) + +class GMLReader; + +typedef struct _GeometryNamesStruct GeometryNamesStruct; + +/************************************************************************/ +/* GFSTemplateList */ +/************************************************************************/ + +class GFSTemplateItem; + +class GFSTemplateList +{ +private: + int m_bSequentialLayers; + GFSTemplateItem *pFirst; + GFSTemplateItem *pLast; + GFSTemplateItem *Insert( const char *pszName ); +public: + GFSTemplateList( void ); + ~GFSTemplateList(); + void Update( const char *pszName, int bHasGeom ); + GFSTemplateItem *GetFirst() { return pFirst; } + int HaveSequentialLayers() { return m_bSequentialLayers; } + int GetClassCount(); +}; + +void gmlUpdateFeatureClasses ( GFSTemplateList *pCC, + GMLReader *pReader, + int *pbSequentialLayers ); + +/************************************************************************/ +/* GMLHandler */ +/************************************************************************/ + +#define STACK_SIZE 5 + +typedef enum +{ + STATE_TOP, + STATE_DEFAULT, + STATE_FEATURE, + STATE_PROPERTY, + STATE_FEATUREPROPERTY, + STATE_GEOMETRY, + STATE_IGNORED_FEATURE, + STATE_BOUNDED_BY, + STATE_CITYGML_ATTRIBUTE +} HandlerState; + +typedef struct +{ + CPLXMLNode* psNode; + CPLXMLNode* psLastChild; +} NodeLastChild; + + +typedef enum +{ + APPSCHEMA_GENERIC, + APPSCHEMA_CITYGML, + APPSCHEMA_AIXM, + APPSCHEMA_MTKGML /* format of National Land Survey Finnish */ +} GMLAppSchemaType; + +class GMLHandler +{ + char *m_pszCurField; + size_t m_nCurFieldAlloc; + size_t m_nCurFieldLen; + int m_bInCurField; + int m_nAttributeIndex; + int m_nAttributeDepth; + + + char *m_pszGeometry; + int m_nGeomAlloc; + int m_nGeomLen; + int m_nGeometryDepth; + int m_bAlreadyFoundGeometry; + int m_nGeometryPropertyIndex; + + int m_nDepth; + int m_nDepthFeature; + + int m_inBoundedByDepth; + + char *m_pszCityGMLGenericAttrName; + int m_inCityGMLGenericAttrDepth; + + int m_bReportHref; + char *m_pszHref; + char *m_pszUom; + char *m_pszValue; + char *m_pszKieli; + + GeometryNamesStruct* pasGeometryNames; + + std::vector apsXMLNode; + + OGRErr startElementTop(const char *pszName, int nLenName, void* attr); + + OGRErr endElementIgnoredFeature(); + + OGRErr startElementBoundedBy(const char *pszName, int nLenName, void* attr); + OGRErr endElementBoundedBy(); + + OGRErr startElementFeatureAttribute(const char *pszName, int nLenName, void* attr); + OGRErr endElementFeature(); + + OGRErr startElementCityGMLGenericAttr(const char *pszName, int nLenName, void* attr); + OGRErr endElementCityGMLGenericAttr(); + + OGRErr startElementGeometry(const char *pszName, int nLenName, void* attr); + CPLXMLNode* ParseAIXMElevationPoint(CPLXMLNode*); + OGRErr endElementGeometry(); + OGRErr dataHandlerGeometry(const char *data, int nLen); + + OGRErr endElementAttribute(); + OGRErr dataHandlerAttribute(const char *data, int nLen); + + OGRErr startElementDefault(const char *pszName, int nLenName, void* attr); + OGRErr endElementDefault(); + + OGRErr startElementFeatureProperty(const char *pszName, int nLenName, void* attr); + OGRErr endElementFeatureProperty(); + + void DealWithAttributes(const char *pszName, int nLenName, void* attr ); + int IsConditionMatched(const char* pszCondition, void* attr); + int FindRealPropertyByCheckingConditions(int nIdx, void* attr); + +protected: + GMLReader *m_poReader; + GMLAppSchemaType eAppSchemaType; + + int nStackDepth; + HandlerState stateStack[STACK_SIZE]; + + std::string osFID; + virtual const char* GetFID(void* attr) = 0; + + virtual CPLXMLNode* AddAttributes(CPLXMLNode* psNode, void* attr) = 0; + + OGRErr startElement(const char *pszName, int nLenName, void* attr); + OGRErr endElement(); + OGRErr dataHandler(const char *data, int nLen); + + int IsGeometryElement( const char *pszElement ); + +public: + GMLHandler( GMLReader *poReader ); + virtual ~GMLHandler(); + + virtual char* GetAttributeValue(void* attr, const char* pszAttributeName) = 0; + virtual char* GetAttributeByIdx(void* attr, unsigned int idx, char** ppszKey) = 0; +}; + + +#if defined(HAVE_XERCES) + +// This works around problems with math.h on some platforms #defining INFINITY +#ifdef INFINITY +#undef INFINITY +#define INFINITY INFINITY_XERCES +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef XERCES_CPP_NAMESPACE_USE +XERCES_CPP_NAMESPACE_USE +#endif + +/************************************************************************/ +/* GMLBinInputStream */ +/************************************************************************/ +class GMLBinInputStream : public BinInputStream +{ + VSILFILE* fp; + XMLCh emptyString; + +public : + + GMLBinInputStream(VSILFILE* fp); + virtual ~GMLBinInputStream(); + +#if XERCES_VERSION_MAJOR >= 3 + virtual XMLFilePos curPos() const; + virtual XMLSize_t readBytes(XMLByte* const toFill, const XMLSize_t maxToRead); + virtual const XMLCh* getContentType() const ; +#else + virtual unsigned int curPos() const; + virtual unsigned int readBytes(XMLByte* const toFill, const unsigned int maxToRead); +#endif +}; + +/************************************************************************/ +/* GMLInputSource */ +/************************************************************************/ + +class GMLInputSource : public InputSource +{ + GMLBinInputStream* binInputStream; + +public: + GMLInputSource(VSILFILE* fp, + MemoryManager* const manager = XMLPlatformUtils::fgMemoryManager); + virtual ~GMLInputSource(); + + virtual BinInputStream* makeStream() const; +}; + + +/************************************************************************/ +/* XMLCh / char translation functions - trstring.cpp */ +/************************************************************************/ +int tr_strcmp( const char *, const XMLCh * ); +void tr_strcpy( XMLCh *, const char * ); +void tr_strcpy( char *, const XMLCh * ); +char *tr_strdup( const XMLCh * ); +int tr_strlen( const XMLCh * ); + +/************************************************************************/ +/* GMLXercesHandler */ +/************************************************************************/ +class GMLXercesHandler : public DefaultHandler, public GMLHandler +{ + int m_nEntityCounter; + +public: + GMLXercesHandler( GMLReader *poReader ); + + void startElement( + const XMLCh* const uri, + const XMLCh* const localname, + const XMLCh* const qname, + const Attributes& attrs + ); + void endElement( + const XMLCh* const uri, + const XMLCh* const localname, + const XMLCh* const qname + ); +#if XERCES_VERSION_MAJOR >= 3 + void characters( const XMLCh *const chars, + const XMLSize_t length ); +#else + void characters( const XMLCh *const chars, + const unsigned int length ); +#endif + + void fatalError(const SAXParseException&); + + void startEntity (const XMLCh *const name); + + virtual const char* GetFID(void* attr); + virtual CPLXMLNode* AddAttributes(CPLXMLNode* psNode, void* attr); + virtual char* GetAttributeValue(void* attr, const char* pszAttributeName); + virtual char* GetAttributeByIdx(void* attr, unsigned int idx, char** ppszKey); +}; + +#endif + + +#if defined(HAVE_EXPAT) + +#include "ogr_expat.h" + +/************************************************************************/ +/* GMLExpatHandler */ +/************************************************************************/ +class GMLExpatHandler : public GMLHandler +{ + XML_Parser m_oParser; + int m_bStopParsing; + int m_nDataHandlerCounter; + +public: + GMLExpatHandler( GMLReader *poReader, XML_Parser oParser ); + + int HasStoppedParsing() { return m_bStopParsing; } + + void ResetDataHandlerCounter() { m_nDataHandlerCounter = 0; } + + virtual const char* GetFID(void* attr); + virtual CPLXMLNode* AddAttributes(CPLXMLNode* psNode, void* attr); + virtual char* GetAttributeValue(void* attr, const char* pszAttributeName); + virtual char* GetAttributeByIdx(void* attr, unsigned int idx, char** ppszKey); + + static void XMLCALL startElementCbk(void *pUserData, const char *pszName, + const char **ppszAttr); + + static void XMLCALL endElementCbk(void *pUserData, const char *pszName); + + static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen); +}; + +#endif + +/************************************************************************/ +/* GMLReadState */ +/************************************************************************/ + +class GMLReadState +{ + std::vector aosPathComponents; + +public: + GMLReadState(); + ~GMLReadState(); + + void PushPath( const char *pszElement, int nLen = -1 ); + void PopPath(); + + const char *GetLastComponent() const { + return ( m_nPathLength == 0 ) ? "" : aosPathComponents[m_nPathLength-1].c_str(); + } + + + size_t GetLastComponentLen() const { + return ( m_nPathLength == 0 ) ? 0: aosPathComponents[m_nPathLength-1].size(); + } + + void Reset(); + + GMLFeature *m_poFeature; + GMLReadState *m_poParentState; + + std::string osPath; // element path ... | as separator. + int m_nPathLength; +}; + +/************************************************************************/ +/* GMLReader */ +/************************************************************************/ + +typedef enum +{ + OGRGML_XERCES_UNINITIALIZED, + OGRGML_XERCES_INIT_FAILED, + OGRGML_XERCES_INIT_SUCCESSFUL +} OGRGMLXercesState; + +class GMLReader : public IGMLReader +{ +private: + static OGRGMLXercesState m_eXercesInitState; + static int m_nInstanceCount; + int m_bClassListLocked; + + int m_nClassCount; + GMLFeatureClass **m_papoClass; + int m_bLookForClassAtAnyLevel; + + char *m_pszFilename; + + int bUseExpatReader; + + GMLHandler *m_poGMLHandler; + +#if defined(HAVE_XERCES) + SAX2XMLReader *m_poSAXReader; + XMLPScanToken m_oToFill; + GMLFeature *m_poCompleteFeature; + GMLInputSource *m_GMLInputSource; + int m_bEOF; + int SetupParserXerces(); + GMLFeature *NextFeatureXerces(); +#endif + +#if defined(HAVE_EXPAT) + XML_Parser oParser; + GMLFeature ** ppoFeatureTab; + int nFeatureTabLength; + int nFeatureTabIndex; + int nFeatureTabAlloc; + int SetupParserExpat(); + GMLFeature *NextFeatureExpat(); + char *pabyBuf; +#endif + + VSILFILE* fpGML; + int m_bReadStarted; + + GMLReadState *m_poState; + GMLReadState *m_poRecycledState; + + int m_bStopParsing; + + int SetupParser(); + void CleanupParser(); + + int m_bFetchAllGeometries; + + int m_bInvertAxisOrderIfLatLong; + int m_bConsiderEPSGAsURN; + int m_bGetSecondaryGeometryOption; + + int ParseFeatureType(CPLXMLNode *psSchemaNode, + const char* pszName, + const char *pszType); + + char *m_pszGlobalSRSName; + int m_bCanUseGlobalSRSName; + + char *m_pszFilteredClassName; + int m_nFilteredClassIndex; + + int m_bSequentialLayers; + + std::string osElemPath; + + int m_bFaceHoleNegative; + + int m_bSetWidthFlag; + + int m_bReportAllAttributes; + + int m_bIsWFSJointLayer; + + int m_bEmptyAsNull; + + int ParseXMLHugeFile( const char *pszOutputFilename, + const int bSqliteIsTempFile, + const int iSqliteCacheMB ); + + +public: + GMLReader(int bExpatReader, int bInvertAxisOrderIfLatLong, + int bConsiderEPSGAsURN, int bGetSecondaryGeometryOption); + virtual ~GMLReader(); + + int IsClassListLocked() const { return m_bClassListLocked; } + void SetClassListLocked( int bFlag ) + { m_bClassListLocked = bFlag; } + + void SetSourceFile( const char *pszFilename ); + void SetFP( VSILFILE* fp ); + const char* GetSourceFileName(); + + int GetClassCount() const { return m_nClassCount; } + GMLFeatureClass *GetClass( int i ) const; + GMLFeatureClass *GetClass( const char *pszName ) const; + + int AddClass( GMLFeatureClass *poClass ); + void ClearClasses(); + + GMLFeature *NextFeature(); + + int LoadClasses( const char *pszFile = NULL ); + int SaveClasses( const char *pszFile = NULL ); + + int ResolveXlinks( const char *pszFile, + int* pbOutIsTempFile, + char **papszSkip = NULL, + const int bStrict = FALSE ); + + int HugeFileResolver( const char *pszFile, + int pbSqliteIsTempFile, + int iSqliteCacheMB ); + + int PrescanForSchema(int bGetExtents = TRUE, + int bAnalyzeSRSPerFeature = TRUE, + int bOnlyDetectSRS = FALSE ); + int PrescanForTemplate( void ); + int ReArrangeTemplateClasses( GFSTemplateList *pCC ); + void ResetReading(); + +// --- + + GMLReadState *GetState() const { return m_poState; } + void PopState(); + void PushState( GMLReadState * ); + + int ShouldLookForClassAtAnyLevel() { return m_bLookForClassAtAnyLevel; } + + int GetFeatureElementIndex( const char *pszElement, int nLen, GMLAppSchemaType eAppSchemaType ); + int GetAttributeElementIndex( const char *pszElement, int nLen, const char* pszAttrKey = NULL ); + int IsCityGMLGenericAttributeElement( const char *pszElement, void* attr ); + + void PushFeature( const char *pszElement, + const char *pszFID, + int nClassIndex ); + + void SetFeaturePropertyDirectly( const char *pszElement, + char *pszValue, + int iPropertyIn, + GMLPropertyType eType = GMLPT_Untyped ); + + void SetWidthFlag(int bFlag) { m_bSetWidthFlag = bFlag; } + + int HasStoppedParsing() { return m_bStopParsing; } + + int FetchAllGeometries() { return m_bFetchAllGeometries; } + + void SetGlobalSRSName( const char* pszGlobalSRSName ) ; + const char* GetGlobalSRSName() { return m_pszGlobalSRSName; } + + int CanUseGlobalSRSName() { return m_bCanUseGlobalSRSName; } + + int SetFilteredClassName(const char* pszClassName); + const char* GetFilteredClassName() { return m_pszFilteredClassName; } + int GetFilteredClassIndex() { return m_nFilteredClassIndex; } + + int IsSequentialLayers() const { return m_bSequentialLayers == TRUE; } + + void SetReportAllAttributes(int bFlag) { m_bReportAllAttributes = bFlag; } + int ReportAllAttributes() const { return m_bReportAllAttributes; } + + void SetIsWFSJointLayer( int bFlag ) { m_bIsWFSJointLayer = bFlag; } + int IsWFSJointLayer() const { return m_bIsWFSJointLayer; } + + void SetEmptyAsNull( int bFlag ) { m_bEmptyAsNull = bFlag; } + int IsEmptyAsNull() const { return m_bEmptyAsNull; } + + static CPLMutex* hMutex; +}; + +#endif /* _CPL_GMLREADERP_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlreadstate.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlreadstate.cpp new file mode 100644 index 000000000..fa662b175 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlreadstate.cpp @@ -0,0 +1,110 @@ +/********************************************************************** + * $Id: gmlreadstate.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GML Reader + * Purpose: Implementation of GMLReadState class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gmlreaderp.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +/************************************************************************/ +/* GMLReadState() */ +/************************************************************************/ + +GMLReadState::GMLReadState() + +{ + m_poFeature = NULL; + m_poParentState = NULL; + m_nPathLength = 0; +} + +/************************************************************************/ +/* ~GMLReadState() */ +/************************************************************************/ + +GMLReadState::~GMLReadState() + +{ +} + +/************************************************************************/ +/* Reset() */ +/************************************************************************/ + +void GMLReadState::Reset() +{ + m_poFeature = NULL; + m_poParentState = NULL; + + osPath.resize(0); + m_nPathLength = 0; +} + +/************************************************************************/ +/* PushPath() */ +/************************************************************************/ + +void GMLReadState::PushPath( const char *pszElement, int nLen ) + +{ + if (m_nPathLength > 0) + osPath.append(1, '|'); + if (m_nPathLength < (int)aosPathComponents.size()) + { + if (nLen >= 0) + { + aosPathComponents[m_nPathLength].assign(pszElement, nLen); + osPath.append(pszElement, nLen); + } + else + { + aosPathComponents[m_nPathLength].assign(pszElement); + osPath.append(pszElement); + } + } + else + { + aosPathComponents.push_back(pszElement); + osPath.append(pszElement); + } + m_nPathLength ++; +} + +/************************************************************************/ +/* PopPath() */ +/************************************************************************/ + +void GMLReadState::PopPath() + +{ + CPLAssert( m_nPathLength > 0 ); + + osPath.resize(osPath.size() - (aosPathComponents[m_nPathLength-1].size() + ((m_nPathLength > 1) ? 1 : 0))); + m_nPathLength --; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlregistry.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlregistry.cpp new file mode 100644 index 000000000..ca20a2660 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlregistry.cpp @@ -0,0 +1,149 @@ +/****************************************************************************** + * $Id: gmlregistry.cpp 29240 2015-05-24 10:58:38Z rouault $ + * + * Project: GML registry + * Purpose: GML reader + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gmlregistry.h" + +/************************************************************************/ +/* Parse() */ +/************************************************************************/ + +int GMLRegistry::Parse() +{ + if( osRegistryPath.size() == 0 ) + { + const char* pszFilename = CPLFindFile( "gdal", "gml_registry.xml" ); + if( pszFilename ) + osRegistryPath = pszFilename; + } + if( osRegistryPath.size() == 0 ) + return FALSE; + CPLXMLNode* psRootNode = CPLParseXMLFile(osRegistryPath); + if( psRootNode == NULL ) + return FALSE; + CPLXMLNode *psRegistryNode = CPLGetXMLNode( psRootNode, "=gml_registry" ); + if( psRegistryNode == NULL ) + { + CPLDestroyXMLNode(psRootNode); + return FALSE; + } + CPLXMLNode* psIter = psRegistryNode->psChild; + while( psIter != NULL ) + { + if( psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "namespace") == 0 ) + { + GMLRegistryNamespace oNameSpace; + if( oNameSpace.Parse(osRegistryPath, psIter) ) + { + aoNamespaces.push_back(oNameSpace); + } + } + psIter = psIter->psNext; + } + CPLDestroyXMLNode(psRootNode); + return TRUE; +} + +/************************************************************************/ +/* Parse() */ +/************************************************************************/ + +int GMLRegistryNamespace::Parse(const char* pszRegistryFilename, CPLXMLNode* psNode) +{ + const char* pszPrefix = CPLGetXMLValue(psNode, "prefix", NULL); + const char* pszURI = CPLGetXMLValue(psNode, "uri", NULL); + if( pszPrefix == NULL || pszURI == NULL ) + return FALSE; + osPrefix = pszPrefix; + osURI = pszURI; + const char* pszUseGlobalSRSName = CPLGetXMLValue(psNode, "useGlobalSRSName", NULL); + if( pszUseGlobalSRSName != NULL && strcmp(pszUseGlobalSRSName, "true") == 0 ) + bUseGlobalSRSName = TRUE; + + CPLXMLNode* psIter = psNode->psChild; + while( psIter != NULL ) + { + if( psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "featureType") == 0 ) + { + GMLRegistryFeatureType oFeatureType; + if( oFeatureType.Parse(pszRegistryFilename, psIter) ) + { + aoFeatureTypes.push_back(oFeatureType); + } + } + psIter = psIter->psNext; + } + return TRUE; +} + +/************************************************************************/ +/* Parse() */ +/************************************************************************/ + +int GMLRegistryFeatureType::Parse(const char* pszRegistryFilename, CPLXMLNode* psNode) +{ + const char* pszElementName = CPLGetXMLValue(psNode, "elementName", NULL); + const char* pszElementValue = CPLGetXMLValue(psNode, "elementValue", NULL); + const char* pszSchemaLocation = CPLGetXMLValue(psNode, "schemaLocation", NULL); + const char* pszGFSSchemaLocation = CPLGetXMLValue(psNode, "gfsSchemaLocation", NULL); + if( pszElementName == NULL || (pszSchemaLocation == NULL && pszGFSSchemaLocation == NULL) ) + return FALSE; + osElementName = pszElementName; + + if( pszSchemaLocation != NULL ) + { + if( strncmp(pszSchemaLocation, "http://", 7) != 0 && + strncmp(pszSchemaLocation, "https://", 8) != 0 && + CPLIsFilenameRelative(pszSchemaLocation ) ) + { + pszSchemaLocation = CPLFormFilename( + CPLGetPath(pszRegistryFilename), pszSchemaLocation, NULL ); + } + osSchemaLocation = pszSchemaLocation; + } + else if( pszGFSSchemaLocation != NULL ) + { + if( strncmp(pszGFSSchemaLocation, "http://", 7) != 0 && + strncmp(pszGFSSchemaLocation, "https://", 8) != 0 && + CPLIsFilenameRelative(pszGFSSchemaLocation ) ) + { + pszGFSSchemaLocation = CPLFormFilename( + CPLGetPath(pszRegistryFilename), pszGFSSchemaLocation, NULL ); + } + osGFSSchemaLocation = pszGFSSchemaLocation; + } + + if ( pszElementValue != NULL ) + { + osElementValue = pszElementValue; + } + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlregistry.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlregistry.h new file mode 100644 index 000000000..837512c86 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlregistry.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * $Id: gmlregistry.h 29240 2015-05-24 10:58:38Z rouault $ + * + * Project: GML registry + * Purpose: GML reader + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _GMLREGISTRY_H_INCLUDED +#define _GMLREGISTRY_H_INCLUDED + +#include "cpl_string.h" +#include "cpl_minixml.h" + +#include + +class GMLRegistryFeatureType +{ + public: + CPLString osElementName; + CPLString osElementValue; + CPLString osSchemaLocation; + CPLString osGFSSchemaLocation; + + int Parse(const char* pszRegistryFilename, CPLXMLNode* psNode); +}; + +class GMLRegistryNamespace +{ + public: + GMLRegistryNamespace() : bUseGlobalSRSName(FALSE) {} + + CPLString osPrefix; + CPLString osURI; + int bUseGlobalSRSName; + std::vector aoFeatureTypes; + + int Parse(const char* pszRegistryFilename, CPLXMLNode* psNode); +}; + +class GMLRegistry +{ + CPLString osRegistryPath; + + public: + std::vector aoNamespaces; + + GMLRegistry(const CPLString& osRegistryPath) : osRegistryPath(osRegistryPath) {} + int Parse(); +}; + +#endif /* _GMLREGISTRY_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlutils.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlutils.cpp new file mode 100644 index 000000000..3236b77ef --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlutils.cpp @@ -0,0 +1,348 @@ +/****************************************************************************** + * $Id: gmlutils.cpp 27576 2014-08-06 22:23:51Z rouault $ + * + * Project: GML Utils + * Purpose: GML reader + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gmlutils.h" + +#include "cpl_string.h" +#include "ogr_api.h" +#include "ogr_p.h" +#include +#include + +/************************************************************************/ +/* GML_ExtractSrsNameFromGeometry() */ +/************************************************************************/ + +const char* GML_ExtractSrsNameFromGeometry(const CPLXMLNode* const * papsGeometry, + std::string& osWork, + int bConsiderEPSGAsURN) +{ + if (papsGeometry[0] != NULL && papsGeometry[1] == NULL) + { + const char* pszSRSName = CPLGetXMLValue((CPLXMLNode*)papsGeometry[0], "srsName", NULL); + if (pszSRSName) + { + int nLen = strlen(pszSRSName); + + if (strncmp(pszSRSName, "EPSG:", 5) == 0 && + bConsiderEPSGAsURN) + { + osWork.reserve(22 + nLen-5); + osWork.assign("urn:ogc:def:crs:EPSG::", 22); + osWork.append(pszSRSName+5, nLen-5); + return osWork.c_str(); + } + else if (strncmp(pszSRSName, "http://www.opengis.net/gml/srs/epsg.xml#", 40) == 0) + { + osWork.reserve(5 + nLen-40 ); + osWork.assign("EPSG:", 5); + osWork.append(pszSRSName+40, nLen-40); + return osWork.c_str(); + } + else + { + return pszSRSName; + } + } + } + return NULL; +} + +/************************************************************************/ +/* GML_IsSRSLatLongOrder() */ +/************************************************************************/ + +int GML_IsSRSLatLongOrder(const char* pszSRSName) +{ + if (pszSRSName == NULL) + return FALSE; + + if (strncmp(pszSRSName, "urn:", 4) == 0) + { + if (strstr(pszSRSName, ":4326") != NULL) + { + /* Shortcut ... */ + return TRUE; + } + else + { + OGRSpatialReference oSRS; + if (oSRS.importFromURN(pszSRSName) == OGRERR_NONE) + { + if (oSRS.EPSGTreatsAsLatLong() || oSRS.EPSGTreatsAsNorthingEasting()) + return TRUE; + } + } + } + return FALSE; +} + + +/************************************************************************/ +/* GML_BuildOGRGeometryFromList_CreateCache() */ +/************************************************************************/ + +class SRSDesc +{ +public: + std::string osSRSName; + int bAxisInvert; + OGRSpatialReference* poSRS; + + SRSDesc() : bAxisInvert(FALSE), poSRS(NULL) + { + } +}; + +class SRSCache +{ + std::map oMap; + SRSDesc oLastDesc; + +public: + + SRSCache() + { + } + + ~SRSCache() + { + std::map::iterator oIter; + for( oIter = oMap.begin(); oIter != oMap.end(); ++oIter ) + { + if( oIter->second.poSRS != NULL ) + oIter->second.poSRS->Release(); + } + } + + SRSDesc& Get(const std::string& osSRSName) + { + if( osSRSName == oLastDesc.osSRSName ) + return oLastDesc; + + std::map::iterator oIter = oMap.find(osSRSName); + if( oIter != oMap.end() ) + { + oLastDesc = oIter->second; + return oLastDesc; + } + + oLastDesc.osSRSName = osSRSName; + oLastDesc.bAxisInvert = GML_IsSRSLatLongOrder(osSRSName.c_str()); + oLastDesc.poSRS = new OGRSpatialReference(); + if( oLastDesc.poSRS->SetFromUserInput(osSRSName.c_str()) != OGRERR_NONE ) + { + delete oLastDesc.poSRS; + oLastDesc.poSRS = NULL; + } + oMap[osSRSName] = oLastDesc; + return oLastDesc; + } +}; + +void* GML_BuildOGRGeometryFromList_CreateCache() +{ + return new SRSCache(); +} + +/************************************************************************/ +/* GML_BuildOGRGeometryFromList_DestroyCache() */ +/************************************************************************/ + +void GML_BuildOGRGeometryFromList_DestroyCache(void* hCacheSRS) +{ + delete (SRSCache*)hCacheSRS; +} + +/************************************************************************/ +/* GML_BuildOGRGeometryFromList() */ +/************************************************************************/ + +OGRGeometry* GML_BuildOGRGeometryFromList(const CPLXMLNode* const * papsGeometry, + int bTryToMakeMultipolygons, + int bInvertAxisOrderIfLatLong, + const char* pszDefaultSRSName, + int bConsiderEPSGAsURN, + int bGetSecondaryGeometryOption, + void* hCacheSRS, + int bFaceHoleNegative) +{ + OGRGeometry* poGeom = NULL; + int i; + OGRGeometryCollection* poCollection = NULL; + for(i=0;papsGeometry[i] != NULL;i++) + { + OGRGeometry* poSubGeom = GML2OGRGeometry_XMLNode( papsGeometry[i], + bGetSecondaryGeometryOption, + 0, 0, FALSE, TRUE, + bFaceHoleNegative ); + if (poSubGeom) + { + if (poGeom == NULL) + poGeom = poSubGeom; + else + { + if (poCollection == NULL) + { + if (bTryToMakeMultipolygons && + wkbFlatten(poGeom->getGeometryType()) == wkbPolygon && + wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon) + { + OGRGeometryCollection* poGeomColl = new OGRMultiPolygon(); + poGeomColl->addGeometryDirectly(poGeom); + poGeomColl->addGeometryDirectly(poSubGeom); + poGeom = poGeomColl; + } + else if (bTryToMakeMultipolygons && + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon && + wkbFlatten(poSubGeom->getGeometryType()) == wkbPolygon) + { + OGRGeometryCollection* poGeomColl = (OGRGeometryCollection* )poGeom; + poGeomColl->addGeometryDirectly(poSubGeom); + } + else if (bTryToMakeMultipolygons && + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon && + wkbFlatten(poSubGeom->getGeometryType()) == wkbMultiPolygon) + { + OGRGeometryCollection* poGeomColl = (OGRGeometryCollection* )poGeom; + OGRGeometryCollection* poGeomColl2 = (OGRGeometryCollection* )poSubGeom; + int nCount = poGeomColl2->getNumGeometries(); + int i; + for(i=0;iaddGeometry(poGeomColl2->getGeometryRef(i)); + } + delete poSubGeom; + } + else if (bTryToMakeMultipolygons && + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon) + { + delete poGeom; + delete poSubGeom; + return GML_BuildOGRGeometryFromList(papsGeometry, FALSE, + bInvertAxisOrderIfLatLong, + pszDefaultSRSName, + bConsiderEPSGAsURN, + bGetSecondaryGeometryOption, + hCacheSRS); + } + else + { + poCollection = new OGRGeometryCollection(); + poCollection->addGeometryDirectly(poGeom); + poGeom = poCollection; + } + } + if (poCollection != NULL) + { + poCollection->addGeometryDirectly(poSubGeom); + } + } + } + } + + if( poGeom == NULL ) + return NULL; + + std::string osWork; + const char* pszSRSName = GML_ExtractSrsNameFromGeometry(papsGeometry, osWork, + bConsiderEPSGAsURN); + const char* pszNameLookup = pszSRSName; + if( pszNameLookup == NULL ) + pszNameLookup = pszDefaultSRSName; + + if (pszNameLookup != NULL) + { + SRSCache* poSRSCache = (SRSCache*)hCacheSRS; + SRSDesc& oSRSDesc = poSRSCache->Get(pszNameLookup); + poGeom->assignSpatialReference(oSRSDesc.poSRS); + if (oSRSDesc.bAxisInvert && bInvertAxisOrderIfLatLong) + poGeom->swapXY(); + } + + return poGeom; +} + +/************************************************************************/ +/* GML_GetSRSName() */ +/************************************************************************/ + +char* GML_GetSRSName(const OGRSpatialReference* poSRS, int bLongSRS, int *pbCoordSwap) +{ + *pbCoordSwap = FALSE; + if (poSRS == NULL) + return CPLStrdup(""); + + const char* pszAuthName = NULL; + const char* pszAuthCode = NULL; + const char* pszTarget = NULL; + + if (poSRS->IsProjected()) + pszTarget = "PROJCS"; + else + pszTarget = "GEOGCS"; + + char szSrsName[50]; + szSrsName[0] = 0; + + pszAuthName = poSRS->GetAuthorityName( pszTarget ); + if( NULL != pszAuthName ) + { + if( EQUAL( pszAuthName, "EPSG" ) ) + { + pszAuthCode = poSRS->GetAuthorityCode( pszTarget ); + if( NULL != pszAuthCode && strlen(pszAuthCode) < 10 ) + { + if (bLongSRS && !(((OGRSpatialReference*)poSRS)->EPSGTreatsAsLatLong() || + ((OGRSpatialReference*)poSRS)->EPSGTreatsAsNorthingEasting())) + { + OGRSpatialReference oSRS; + if (oSRS.importFromEPSGA(atoi(pszAuthCode)) == OGRERR_NONE) + { + if (oSRS.EPSGTreatsAsLatLong() || oSRS.EPSGTreatsAsNorthingEasting()) + *pbCoordSwap = TRUE; + } + } + + if (bLongSRS) + { + sprintf( szSrsName, " srsName=\"urn:ogc:def:crs:%s::%s\"", + pszAuthName, pszAuthCode ); + } + else + { + sprintf( szSrsName, " srsName=\"%s:%s\"", + pszAuthName, pszAuthCode ); + } + } + } + } + + return CPLStrdup(szSrsName); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlutils.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlutils.h new file mode 100644 index 000000000..d8466d6fb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/gmlutils.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * $Id: gmlutils.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GML Utils + * Purpose: GML reader + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_GMLUTILS_H_INCLUDED +#define _CPL_GMLUTILS_H_INCLUDED + +#include +#include +#include "cpl_minixml.h" + +#include "ogr_geometry.h" + +const char* GML_ExtractSrsNameFromGeometry(const CPLXMLNode* const * papsGeometry, + std::string& osWork, + int bConsiderEPSGAsURN); + +int GML_IsSRSLatLongOrder(const char* pszSRSName); + +void* GML_BuildOGRGeometryFromList_CreateCache(); +void GML_BuildOGRGeometryFromList_DestroyCache(void* hCacheSRS); + +OGRGeometry* GML_BuildOGRGeometryFromList(const CPLXMLNode* const * papsGeometry, + int bTryToMakeMultipolygons, + int bInvertAxisOrderIfLatLong, + const char* pszDefaultSRSName, + int bConsiderEPSGAsURN, + int bGetSecondaryGeometryOption, + void* hCacheSRS, + int bFaceHoleNegative = FALSE ); + +char* GML_GetSRSName(const OGRSpatialReference* poSRS, int bLongSRS, int *pbCoordSwap); + +#endif /* _CPL_GMLREADERP_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/hugefileresolver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/hugefileresolver.cpp new file mode 100644 index 000000000..2c3c50b7b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/hugefileresolver.cpp @@ -0,0 +1,2079 @@ +/****************************************************************************** + * $Id: hugefileresolver.cpp 27766 2014-09-28 20:13:12Z goatbar $ + * + * Project: GML Reader + * Purpose: Implementation of GMLReader::HugeFileResolver() method. + * Author: Alessandro Furieri, a.furitier@lqt.it + * + ****************************************************************************** + * Copyright (c) 2011, Alessandro Furieri + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + ****************************************************************************** + * Contributor: Alessandro Furieri, a.furieri@lqt.it + * This module implents GML_SKIP_RESOLVE_ELEMS HUGE + * Developed for Faunalia ( http://www.faunalia.it) with funding from + * Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE + * + ****************************************************************************/ + +#include "gmlreader.h" +#include "cpl_error.h" + +#include "gmlreaderp.h" +#include "gmlutils.h" +#include "cpl_conv.h" +#include "ogr_p.h" +#include "cpl_string.h" +#include "cpl_http.h" + +#include + +CPL_CVSID("$Id: hugefileresolver.cpp 27766 2014-09-28 20:13:12Z goatbar $"); + +/****************************************************/ +/* SQLite is absolutely required in order to */ +/* support the HUGE xlink:href resolver */ +/****************************************************/ + +#ifdef HAVE_SQLITE +#include +#endif + +/* sqlite3_clear_bindings() isn't available in old versions of */ +/* sqlite3 */ +#if defined(HAVE_SQLITE) && SQLITE_VERSION_NUMBER >= 3006000 + +/* an internal helper struct supporting GML tags */ +struct huge_tag +{ + CPLString *gmlTagValue; + CPLString *gmlId; + CPLString *gmlNodeFrom; + CPLString *gmlNodeTo; + int bIsNodeFromHref; + int bIsNodeToHref; + int bHasCoords; + int bHasZ; + double xNodeFrom; + double yNodeFrom; + double zNodeFrom; + double xNodeTo; + double yNodeTo; + double zNodeTo; + struct huge_tag *pNext; +}; + +/* an internal helper struct supporting GML tags xlink:href */ +struct huge_href +{ + CPLString *gmlId; + CPLString *gmlText; + const CPLXMLNode *psParent; + const CPLXMLNode *psNode; + int bIsDirectedEdge; + char cOrientation; + struct huge_href *pNext; +}; + +/* an internal helper struct supporying GML rewriting */ +struct huge_child +{ + CPLXMLNode *psChild; + struct huge_href *pItem; + struct huge_child *pNext; +}; + +/* an internal helper struct supporting GML rewriting */ +struct huge_parent +{ + CPLXMLNode *psParent; + struct huge_child *pFirst; + struct huge_child *pLast; + struct huge_parent *pNext; +}; + +/* +/ an internal helper struct supporting GML +/ resolver for Huge Files (based on SQLite) +*/ +struct huge_helper +{ + sqlite3 *hDB; + sqlite3_stmt *hNodes; + sqlite3_stmt *hEdges; + CPLString *nodeSrs; + struct huge_tag *pFirst; + struct huge_tag *pLast; + struct huge_href *pFirstHref; + struct huge_href *pLastHref; + struct huge_parent *pFirstParent; + struct huge_parent *pLastParent; +}; + +static int gmlHugeFileSQLiteInit( struct huge_helper *helper ) +{ +/* attempting to create SQLite tables */ + const char *osCommand; + char *pszErrMsg = NULL; + int rc; + sqlite3 *hDB = helper->hDB; + sqlite3_stmt *hStmt; + + /* DB table: NODES */ + osCommand = "CREATE TABLE nodes (" + " gml_id VARCHAR PRIMARY KEY, " + " x DOUBLE, " + " y DOUBLE, " + " z DOUBLE)"; + rc = sqlite3_exec( hDB, osCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create table nodes: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + + /* DB table: GML_EDGES */ + osCommand = "CREATE TABLE gml_edges (" + " gml_id VARCHAR PRIMARY KEY, " + " gml_string BLOB, " + " gml_resolved BLOB, " + " node_from_id TEXT, " + " node_from_x DOUBLE, " + " node_from_y DOUBLE, " + " node_from_z DOUBLE, " + " node_to_id TEXT, " + " node_to_x DOUBLE, " + " node_to_y DOUBLE, " + " node_to_z DOUBLE)"; + rc = sqlite3_exec( hDB, osCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create table gml_edges: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + + /* DB table: NODES / Insert cursor */ + osCommand = "INSERT OR IGNORE INTO nodes (gml_id, x, y, z) " + "VALUES (?, ?, ?, ?)"; + rc = sqlite3_prepare_v2( hDB, osCommand, -1, &hStmt, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create INSERT stmt for: nodes" ); + return FALSE; + } + helper->hNodes = hStmt; + + /* DB table: GML_EDGES / Insert cursor */ + osCommand = "INSERT INTO gml_edges " + "(gml_id, gml_string, gml_resolved, " + "node_from_id, node_from_x, node_from_y, " + "node_from_z, node_to_id, node_to_x, " + "node_to_y, node_to_z) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + rc = sqlite3_prepare_v2( hDB, osCommand, -1, &hStmt, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create INSERT stmt for: gml_edges" ); + return FALSE; + } + helper->hEdges = hStmt; + + /* starting a TRANSACTION */ + rc = sqlite3_exec( hDB, "BEGIN", NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to perform BEGIN TRANSACTION: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + + return TRUE; +} + +static int gmlHugeResolveEdgeNodes( const CPLXMLNode *psNode, + const char *pszFromId, + const char *pszToId ) +{ +/* resolves an Edge definition */ + CPLXMLNode *psDirNode_1 = NULL; + CPLXMLNode *psDirNode_2 = NULL; + CPLXMLNode *psOldNode_1 = NULL; + CPLXMLNode *psOldNode_2 = NULL; + CPLXMLNode *psNewNode_1 = NULL; + CPLXMLNode *psNewNode_2 = NULL; + int iToBeReplaced = 0; + int iReplaced = 0; + if( psNode->eType == CXT_Element && EQUAL( psNode->pszValue, "Edge" ) ) + ; + else + return FALSE; + + CPLXMLNode *psChild = psNode->psChild; + while( psChild != NULL ) + { + if( psChild->eType == CXT_Element && + EQUAL( psChild->pszValue, "directedNode" ) ) + { + char cOrientation = '+'; + CPLXMLNode *psOldNode = NULL; + CPLXMLNode *psAttr = psChild->psChild; + while( psAttr != NULL ) + { + if( psAttr->eType == CXT_Attribute && + EQUAL( psAttr->pszValue, "xlink:href" ) ) + psOldNode = psAttr; + if( psAttr->eType == CXT_Attribute && + EQUAL( psAttr->pszValue, "orientation" ) ) + { + const CPLXMLNode *psOrientation = psAttr->psChild; + if( psOrientation != NULL ) + { + if( psOrientation->eType == CXT_Text ) + cOrientation = *(psOrientation->pszValue); + } + } + psAttr = psAttr->psNext; + } + if( psOldNode != NULL ) + { + CPLXMLNode *psNewNode = CPLCreateXMLNode(NULL, CXT_Element, "Node"); + CPLXMLNode *psGMLIdNode = CPLCreateXMLNode(psNewNode, CXT_Attribute, "gml:id"); + if( cOrientation == '-' ) + CPLCreateXMLNode(psGMLIdNode, CXT_Text, pszFromId); + else + CPLCreateXMLNode(psGMLIdNode, CXT_Text, pszToId); + if( iToBeReplaced == 0 ) + { + psDirNode_1 = psChild; + psOldNode_1 = psOldNode; + psNewNode_1 = psNewNode; + } + else + { + psDirNode_2 = psChild; + psOldNode_2 = psOldNode; + psNewNode_2 = psNewNode; + } + iToBeReplaced++; + } + } + psChild = psChild->psNext; + } + + /* rewriting the Edge GML definition */ + if( psDirNode_1 != NULL) + { + if( psOldNode_1 != NULL ) + { + CPLRemoveXMLChild( psDirNode_1, psOldNode_1 ); + CPLDestroyXMLNode( psOldNode_1 ); + if( psNewNode_1 != NULL ) + { + CPLAddXMLChild( psDirNode_1, psNewNode_1 ); + iReplaced++; + } + } + } + if( psDirNode_2 != NULL) + { + if( psOldNode_2 != NULL ) + { + CPLRemoveXMLChild( psDirNode_2, psOldNode_2 ); + CPLDestroyXMLNode( psOldNode_2 ); + if( psNewNode_2 != NULL ) + { + CPLAddXMLChild( psDirNode_2, psNewNode_2 ); + iReplaced++; + } + } + } + if( iToBeReplaced != iReplaced ) + return FALSE; +return TRUE; +} + +static int gmlHugeFileResolveEdges( struct huge_helper *helper ) +{ +/* identifying any not yet resolved GML string */ + const char *osCommand; + char *pszErrMsg = NULL; + int rc; + sqlite3 *hDB = helper->hDB; + sqlite3_stmt *hQueryStmt; + sqlite3_stmt *hUpdateStmt; + int iCount = 0; + int bError = FALSE; + + /* query cursor */ + osCommand = "SELECT e.gml_id, e.gml_string, e.node_from_id, " + "e.node_from_x, e.node_from_y, e.node_from_z, " + "n1.gml_id, n1.x, n1.y, n1.z, e.node_to_id, " + "e.node_to_x, e.node_to_y, e.node_to_z, " + "n2.gml_id, n2.x, n2.y, n2.z " + "FROM gml_edges AS e " + "LEFT JOIN nodes AS n1 ON (n1.gml_id = e.node_from_id) " + "LEFT JOIN nodes AS n2 ON (n2.gml_id = e.node_to_id)"; + rc = sqlite3_prepare_v2( hDB, osCommand, -1, &hQueryStmt, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create QUERY stmt for Edge resolver" ); + return FALSE; + } + + /* update cursor */ + osCommand = "UPDATE gml_edges " + "SET gml_resolved = ?, " + "gml_string = NULL " + "WHERE gml_id = ?"; + rc = sqlite3_prepare_v2( hDB, osCommand, -1, &hUpdateStmt, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create UPDATE stmt for resolved Edges" ); + sqlite3_finalize ( hQueryStmt ); + return FALSE; + } + + /* starting a TRANSACTION */ + rc = sqlite3_exec( hDB, "BEGIN", NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to perform BEGIN TRANSACTION: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + sqlite3_finalize ( hQueryStmt ); + sqlite3_finalize ( hUpdateStmt ); + return FALSE; + } + + /* looping on the QUERY result-set */ + while ( TRUE ) + { + const char *pszGmlId; + const char *pszGmlString = NULL; + int bIsGmlStringNull; + const char *pszFromId = NULL; + int bIsFromIdNull; + double xFrom = 0.0; + int bIsXFromNull; + double yFrom = 0.0; + int bIsYFromNull; + double zFrom = 0.0; + int bIsZFromNull; + /* const char *pszNodeFromId = NULL; */ + int bIsNodeFromIdNull; + double xNodeFrom = 0.0; + int bIsXNodeFromNull; + double yNodeFrom = 0.0; + int bIsYNodeFromNull; + double zNodeFrom = 0.0; + int bIsZNodeFromNull; + const char *pszToId = NULL; + int bIsToIdNull; + double xTo = 0.0; + int bIsXToNull; + double yTo = 0.0; + int bIsYToNull; + double zTo = 0.0; + int bIsZToNull; + /* const char *pszNodeToId = NULL; */ + int bIsNodeToIdNull; + double xNodeTo = 0.0; + int bIsXNodeToNull; + double yNodeTo = 0.0; + int bIsYNodeToNull; + double zNodeTo = 0.0; + int bIsZNodeToNull; + + rc = sqlite3_step( hQueryStmt ); + if( rc == SQLITE_DONE ) + break; + else if( rc == SQLITE_ROW ) + { + bError = FALSE; + pszGmlId = (const char *) sqlite3_column_text( hQueryStmt, 0 ); + if( sqlite3_column_type( hQueryStmt, 1 ) == SQLITE_NULL ) + bIsGmlStringNull = TRUE; + else + { + pszGmlString = (const char *) sqlite3_column_blob( hQueryStmt, 1 ); + bIsGmlStringNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 2 ) == SQLITE_NULL ) + bIsFromIdNull = TRUE; + else + { + pszFromId = (const char *) sqlite3_column_text( hQueryStmt, 2 ); + bIsFromIdNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 3 ) == SQLITE_NULL ) + bIsXFromNull = TRUE; + else + { + xFrom = sqlite3_column_double( hQueryStmt, 3 ); + bIsXFromNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 4 ) == SQLITE_NULL ) + bIsYFromNull = TRUE; + else + { + yFrom = sqlite3_column_double( hQueryStmt, 4 ); + bIsYFromNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 5 ) == SQLITE_NULL ) + bIsZFromNull = TRUE; + else + { + zFrom = sqlite3_column_double( hQueryStmt, 5 ); + bIsZFromNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 6 ) == SQLITE_NULL ) + bIsNodeFromIdNull = TRUE; + else + { + /* TODO: Can sqlite3_column_text be removed? */ + /* pszNodeFromId = (const char *) */ sqlite3_column_text( hQueryStmt, 6 ); + bIsNodeFromIdNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 7 ) == SQLITE_NULL ) + bIsXNodeFromNull = TRUE; + else + { + xNodeFrom = sqlite3_column_double( hQueryStmt, 7 ); + bIsXNodeFromNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 8 ) == SQLITE_NULL ) + bIsYNodeFromNull = TRUE; + else + { + yNodeFrom = sqlite3_column_double( hQueryStmt, 8 ); + bIsYNodeFromNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 9 ) == SQLITE_NULL ) + bIsZNodeFromNull = TRUE; + else + { + zNodeFrom = sqlite3_column_double( hQueryStmt, 9 ); + bIsZNodeFromNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 10 ) == SQLITE_NULL ) + bIsToIdNull = TRUE; + else + { + pszToId = (const char *) sqlite3_column_text( hQueryStmt, 10 ); + bIsToIdNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 11 ) == SQLITE_NULL ) + bIsXToNull = TRUE; + else + { + xTo = sqlite3_column_double( hQueryStmt, 11 ); + bIsXToNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 12 ) == SQLITE_NULL ) + bIsYToNull = TRUE; + else + { + yTo = sqlite3_column_double( hQueryStmt, 12 ); + bIsYToNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 13 ) == SQLITE_NULL ) + bIsZToNull = TRUE; + else + { + zTo = sqlite3_column_double( hQueryStmt, 13 ); + bIsZToNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 14 ) == SQLITE_NULL ) + bIsNodeToIdNull = TRUE; + else + { + /* TODO: Can sqlite3_column_text be removed? */ + /* pszNodeToId = (const char *) */ sqlite3_column_text( hQueryStmt, 14 ); + bIsNodeToIdNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 15 ) == SQLITE_NULL ) + bIsXNodeToNull = TRUE; + else + { + xNodeTo = sqlite3_column_double( hQueryStmt, 15 ); + bIsXNodeToNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 16 ) == SQLITE_NULL ) + bIsYNodeToNull = TRUE; + else + { + yNodeTo = sqlite3_column_double( hQueryStmt, 16 ); + bIsYNodeToNull = FALSE; + } + if( sqlite3_column_type( hQueryStmt, 17 ) == SQLITE_NULL ) + bIsZNodeToNull = TRUE; + else + { + zNodeTo = sqlite3_column_double( hQueryStmt, 17 ); + bIsZNodeToNull = FALSE; + } + + /* checking for consistency */ + if( bIsFromIdNull == TRUE || bIsXFromNull == TRUE || + bIsYFromNull == TRUE ) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge gml:id=\"%s\": invalid Node-from", + pszGmlId ); + } + else + { + if( bIsNodeFromIdNull == TRUE ) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge gml:id=\"%s\": undeclared Node gml:id=\"%s\"", + pszGmlId, pszFromId ); + } + else if( bIsXNodeFromNull == TRUE || bIsYNodeFromNull == TRUE ) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge gml:id=\"%s\": unknown coords for Node gml:id=\"%s\"", + pszGmlId, pszFromId ); + } + else if( xFrom != xNodeFrom || yFrom != yNodeFrom ) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge gml:id=\"%s\": mismatching coords for Node gml:id=\"%s\"", + pszGmlId, pszFromId ); + } + else + { + if( bIsZFromNull == TRUE && bIsZNodeFromNull == TRUE ) +; + else if( bIsZFromNull == TRUE || bIsZNodeFromNull == TRUE ) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge gml:id=\"%s\": mismatching 2D/3D for Node gml:id=\"%s\"", + pszGmlId, pszFromId ); + } + else if( zFrom != zNodeFrom ) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge gml:id=\"%s\": mismatching Z coord for Node gml:id=\"%s\"", + pszGmlId, pszFromId ); + } + } + } + if( bIsToIdNull == TRUE || bIsXToNull == TRUE || + bIsYToNull == TRUE ) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge gml:id=\"%s\": invalid Node-to", + pszGmlId ); + } + else + { + if( bIsNodeToIdNull == TRUE ) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge gml:id=\"%s\": undeclared Node gml:id=\"%s\"", + pszGmlId, pszToId ); + } + else if( bIsXNodeToNull == TRUE || bIsYNodeToNull == TRUE ) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge gml:id=\"%s\": unknown coords for Node gml:id=\"%s\"", + pszGmlId, pszToId ); + } + else if( xTo != xNodeTo || yTo != yNodeTo ) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge gml:id=\"%s\": mismatching coords for Node gml:id=\"%s\"", + pszGmlId, pszToId ); + } + else + { + if( bIsZToNull == TRUE && bIsZNodeToNull == TRUE ) +; + else if( bIsZToNull == TRUE || bIsZNodeToNull == TRUE ) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge gml:id=\"%s\": mismatching 2D/3D for Node gml:id=\"%s\"", + pszGmlId, pszToId ); + } + else if( zTo != zNodeTo ) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge gml:id=\"%s\": mismatching Z coord for Node gml:id=\"%s\"", + pszGmlId, pszToId ); + } + } + } + + /* updating the resolved Node */ + if( bError == FALSE && bIsGmlStringNull == FALSE && + bIsFromIdNull == FALSE && bIsToIdNull == FALSE ) + { + CPLXMLNode *psNode = CPLParseXMLString( pszGmlString ); + if( psNode != NULL ) + { + if( gmlHugeResolveEdgeNodes( psNode, pszFromId, + pszToId ) == TRUE ) + { + char * gmlText = CPLSerializeXMLTree(psNode); + sqlite3_reset ( hUpdateStmt ); + sqlite3_clear_bindings ( hUpdateStmt ); + sqlite3_bind_blob( hUpdateStmt, 1, gmlText, + strlen(gmlText), SQLITE_STATIC ); + sqlite3_bind_text( hUpdateStmt, 2, pszGmlId, -1, + SQLITE_STATIC ); + rc = sqlite3_step( hUpdateStmt ); + if( rc != SQLITE_OK && rc != SQLITE_DONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "UPDATE resoved Edge \"%s\" sqlite3_step() failed:\n %s", + pszGmlId, sqlite3_errmsg(hDB) ); + } + CPLFree( gmlText ); + iCount++; + if( (iCount % 1024) == 1023 ) + { + /* committing the current TRANSACTION */ + rc = sqlite3_exec( hDB, "COMMIT", NULL, NULL, + &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to perform COMMIT TRANSACTION: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + /* restarting a new TRANSACTION */ + rc = sqlite3_exec( hDB, "BEGIN", NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to perform BEGIN TRANSACTION: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + sqlite3_finalize ( hQueryStmt ); + sqlite3_finalize ( hUpdateStmt ); + return FALSE; + } + } + } + CPLDestroyXMLNode( psNode ); + } + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Edge resolver QUERY: sqlite3_step(%s)", + sqlite3_errmsg(hDB) ); + sqlite3_finalize ( hQueryStmt ); + sqlite3_finalize ( hUpdateStmt ); + return FALSE; + } + } + sqlite3_finalize ( hQueryStmt ); + sqlite3_finalize ( hUpdateStmt ); + + /* committing the current TRANSACTION */ + rc = sqlite3_exec( hDB, "COMMIT", NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to perform COMMIT TRANSACTION: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + if( bError == TRUE ) + return FALSE; + return TRUE; +} + +static int gmlHugeFileSQLiteInsert( struct huge_helper *helper ) +{ +/* inserting any appropriate row into the SQLite DB */ + int rc; + struct huge_tag *pItem; + + /* looping on GML tags */ + pItem = helper->pFirst; + while ( pItem != NULL ) + { + if( pItem->bHasCoords == TRUE ) + { + if( pItem->gmlNodeFrom != NULL ) + { + sqlite3_reset ( helper->hNodes ); + sqlite3_clear_bindings ( helper->hNodes ); + sqlite3_bind_text( helper->hNodes, 1, + pItem->gmlNodeFrom->c_str(), -1, + SQLITE_STATIC ); + sqlite3_bind_double ( helper->hNodes, 2, pItem->xNodeFrom ); + sqlite3_bind_double ( helper->hNodes, 3, pItem->yNodeFrom ); + if( pItem->bHasZ == TRUE ) + sqlite3_bind_double ( helper->hNodes, 4, pItem->zNodeFrom ); + sqlite3_bind_null ( helper->hNodes, 5 ); + rc = sqlite3_step( helper->hNodes ); + if( rc != SQLITE_OK && rc != SQLITE_DONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "sqlite3_step() failed:\n %s (gmlNodeFrom id=%s)", + sqlite3_errmsg(helper->hDB), pItem->gmlNodeFrom->c_str() ); + return FALSE; + } + } + if( pItem->gmlNodeTo != NULL ) + { + sqlite3_reset ( helper->hNodes ); + sqlite3_clear_bindings ( helper->hNodes ); + sqlite3_bind_text( helper->hNodes, 1, pItem->gmlNodeTo->c_str(), + -1, SQLITE_STATIC ); + sqlite3_bind_double ( helper->hNodes, 2, pItem->xNodeTo ); + sqlite3_bind_double ( helper->hNodes, 3, pItem->yNodeTo ); + if ( pItem->bHasZ == TRUE ) + sqlite3_bind_double ( helper->hNodes, 4, pItem->zNodeTo ); + sqlite3_bind_null ( helper->hNodes, 5 ); + rc = sqlite3_step( helper->hNodes ); + if( rc != SQLITE_OK && rc != SQLITE_DONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "sqlite3_step() failed:\n %s (gmlNodeTo id=%s)", + sqlite3_errmsg(helper->hDB), pItem->gmlNodeTo->c_str() ); + return FALSE; + } + } + } + + /* gml:id */ + sqlite3_reset( helper->hEdges ); + sqlite3_clear_bindings( helper->hEdges ); + sqlite3_bind_text( helper->hEdges, 1, pItem->gmlId->c_str(), -1, + SQLITE_STATIC ); + if( pItem->bIsNodeFromHref == FALSE && pItem->bIsNodeToHref == FALSE ) + { + sqlite3_bind_null( helper->hEdges, 2 ); + sqlite3_bind_blob( helper->hEdges, 3, pItem->gmlTagValue->c_str(), + strlen( pItem->gmlTagValue->c_str() ), + SQLITE_STATIC ); + } + else + { + sqlite3_bind_blob( helper->hEdges, 2, pItem->gmlTagValue->c_str(), + strlen( pItem->gmlTagValue->c_str() ), + SQLITE_STATIC ); + sqlite3_bind_null( helper->hEdges, 3 ); + } + if( pItem->gmlNodeFrom != NULL ) + sqlite3_bind_text( helper->hEdges, 4, pItem->gmlNodeFrom->c_str(), + -1, SQLITE_STATIC ); + else + sqlite3_bind_null( helper->hEdges, 4 ); + if( pItem->bHasCoords == TRUE ) + { + sqlite3_bind_double( helper->hEdges, 5, pItem->xNodeFrom ); + sqlite3_bind_double( helper->hEdges, 6, pItem->yNodeFrom ); + if( pItem->bHasZ == TRUE ) + sqlite3_bind_double( helper->hEdges, 7, pItem->zNodeFrom ); + else + sqlite3_bind_null( helper->hEdges, 7 ); + } + else + { + sqlite3_bind_null( helper->hEdges, 5 ); + sqlite3_bind_null( helper->hEdges, 6 ); + sqlite3_bind_null( helper->hEdges, 7 ); + } + if( pItem->gmlNodeTo != NULL ) + sqlite3_bind_text( helper->hEdges, 8, pItem->gmlNodeTo->c_str(), + -1, SQLITE_STATIC ); + else + sqlite3_bind_null( helper->hEdges, 8 ); + if( pItem->bHasCoords == TRUE ) + { + sqlite3_bind_double( helper->hEdges, 9, pItem->xNodeTo ); + sqlite3_bind_double( helper->hEdges, 10, pItem->yNodeTo ); + if( pItem->bHasZ == TRUE ) + sqlite3_bind_double( helper->hEdges, 11, pItem->zNodeTo ); + else + sqlite3_bind_null( helper->hEdges, 11 ); + } + else + { + sqlite3_bind_null( helper->hEdges, 9 ); + sqlite3_bind_null( helper->hEdges, 10 ); + sqlite3_bind_null( helper->hEdges, 11 ); + } + rc = sqlite3_step( helper->hEdges ); + if( rc != SQLITE_OK && rc != SQLITE_DONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "sqlite3_step() failed:\n %s (edge gml:id=%s)", + sqlite3_errmsg(helper->hDB), pItem->gmlId->c_str() ); + return FALSE; + } + pItem = pItem->pNext; + } + return TRUE; +} + +static void gmlHugeFileReset( struct huge_helper *helper ) +{ +/* resetting an empty helper struct */ + struct huge_tag *pNext; + struct huge_tag *p = helper->pFirst; + + /* cleaning any previous item */ + while( p != NULL ) + { + pNext = p->pNext; + if( p->gmlTagValue != NULL ) + delete p->gmlTagValue; + if( p->gmlId != NULL ) + delete p->gmlId; + if( p->gmlNodeFrom != NULL ) + delete p->gmlNodeFrom; + if( p->gmlNodeTo != NULL ) + delete p->gmlNodeTo; + delete p; + p = pNext; + } + helper->pFirst = NULL; + helper->pLast = NULL; +} + +static void gmlHugeFileHrefReset( struct huge_helper *helper ) +{ +/* resetting an empty helper struct */ + struct huge_href *pNext; + struct huge_href *p = helper->pFirstHref; + + /* cleaning any previous item */ + while( p != NULL ) + { + pNext = p->pNext; + if( p->gmlId != NULL ) + delete p->gmlId; + if( p->gmlText != NULL ) + delete p->gmlText; + delete p; + p = pNext; + } + helper->pFirstHref = NULL; + helper->pLastHref = NULL; +} + +static int gmlHugeFileHrefCheck( struct huge_helper *helper ) +{ +/* testing for unresolved items */ + int bError = FALSE; + struct huge_href *p = helper->pFirstHref; + while( p != NULL ) + { + if( p->gmlText == NULL) + { + bError = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "Edge xlink:href\"%s\": unresolved match", + p->gmlId->c_str() ); + } + p = p->pNext; + } + if( bError == TRUE ) + return FALSE; + return TRUE; +} + +static void gmlHugeFileRewiterReset( struct huge_helper *helper ) +{ +/* resetting an empty helper struct */ + struct huge_parent *pNext; + struct huge_parent *p = helper->pFirstParent; + + /* cleaning any previous item */ + while( p != NULL ) + { + pNext = p->pNext; + struct huge_child *pChildNext; + struct huge_child *pChild = p->pFirst; + while( pChild != NULL ) + { + pChildNext = pChild->pNext; + delete pChild; + pChild = pChildNext; + } + delete p; + p = pNext; + } + helper->pFirstParent = NULL; + helper->pLastParent = NULL; +} + +static struct huge_tag *gmlHugeAddToHelper( struct huge_helper *helper, + CPLString *gmlId, + CPLString *gmlFragment ) +{ +/* adding an item into the linked list */ + struct huge_tag *pItem; + + /* checking against duplicates */ + pItem = helper->pFirst; + while( pItem != NULL ) + { + if( EQUAL( pItem->gmlId->c_str(), gmlId->c_str() ) ) + return NULL; + pItem = pItem->pNext; + } + + pItem = new struct huge_tag; + pItem->gmlId = gmlId; + pItem->gmlTagValue = gmlFragment; + pItem->gmlNodeFrom = NULL; + pItem->gmlNodeTo = NULL; + pItem->bIsNodeFromHref = FALSE; + pItem->bIsNodeToHref = FALSE; + pItem->bHasCoords = FALSE; + pItem->bHasZ = FALSE; + pItem->pNext = NULL; + + /* appending the item to the linked list */ + if ( helper->pFirst == NULL ) + helper->pFirst = pItem; + if ( helper->pLast != NULL ) + helper->pLast->pNext = pItem; + helper->pLast = pItem; + return pItem; +} + +static void gmlHugeAddPendingToHelper( struct huge_helper *helper, + CPLString *gmlId, + const CPLXMLNode *psParent, + const CPLXMLNode *psNode, + int bIsDirectedEdge, + char cOrientation ) +{ +/* inserting an item into the linked list */ + struct huge_href *pItem; + + /* checking against duplicates */ + pItem = helper->pFirstHref; + while( pItem != NULL ) + { + if( EQUAL( pItem->gmlId->c_str(), gmlId->c_str() ) && + pItem->psParent == psParent && + pItem->psNode == psNode && + pItem->cOrientation == cOrientation && + pItem->bIsDirectedEdge == bIsDirectedEdge ) + { + delete gmlId; + return; + } + pItem = pItem->pNext; + } + + pItem = new struct huge_href; + pItem->gmlId = gmlId; + pItem->gmlText = NULL; + pItem->psParent = psParent; + pItem->psNode = psNode; + pItem->bIsDirectedEdge = bIsDirectedEdge; + pItem->cOrientation = cOrientation; + pItem->pNext = NULL; + + /* appending the item to the linked list */ + if ( helper->pFirstHref == NULL ) + helper->pFirstHref = pItem; + if ( helper->pLastHref != NULL ) + helper->pLastHref->pNext = pItem; + helper->pLastHref = pItem; +} + +static int gmlHugeFindGmlId( const CPLXMLNode *psNode, CPLString **gmlId ) +{ +/* attempting to identify a gml:id value */ + *gmlId = NULL; + const CPLXMLNode *psChild = psNode->psChild; + while( psChild != NULL ) + { + if( psChild->eType == CXT_Attribute && + EQUAL( psChild->pszValue, "gml:id" ) ) + { + const CPLXMLNode *psIdValue = psChild->psChild; + if( psIdValue != NULL ) + { + if( psIdValue->eType == CXT_Text ) + { + *gmlId = new CPLString(psIdValue->pszValue); + return TRUE; + } + } + } + psChild = psChild->psNext; + } + return FALSE; +} + +static void gmlHugeFileNodeCoords( struct huge_tag *pItem, + const CPLXMLNode * psNode, + CPL_UNUSED CPLString **nodeSrs ) +{ +/* +/ this function attempts to set coordinates for items +/ when required (an is expected to be processed) +*/ + +/* attempting to fetch Node coordinates */ + CPLXMLNode *psTopoCurve = CPLCreateXMLNode(NULL, CXT_Element, "TopoCurve"); + CPLXMLNode *psDirEdge = CPLCreateXMLNode(psTopoCurve, CXT_Element, "directedEdge"); + CPLXMLNode *psEdge = CPLCloneXMLTree((CPLXMLNode *)psNode); + CPLAddXMLChild( psDirEdge, psEdge ); + OGRGeometryCollection *poColl = (OGRGeometryCollection *) + GML2OGRGeometry_XMLNode( psTopoCurve, FALSE ); + CPLDestroyXMLNode( psTopoCurve ); + if( poColl != NULL ) + { + int iCount = poColl->getNumGeometries(); + if( iCount == 1 ) + { + OGRGeometry * poChild = (OGRGeometry*)poColl->getGeometryRef(0); + int type = wkbFlatten( poChild->getGeometryType()); + if( type == wkbLineString ) + { + OGRLineString *poLine = (OGRLineString *)poChild; + int iPoints = poLine->getNumPoints(); + if( iPoints >= 2 ) + { + pItem->bHasCoords = TRUE; + pItem->xNodeFrom = poLine->getX( 0 ); + pItem->yNodeFrom = poLine->getY( 0 ); + pItem->xNodeTo = poLine->getX( iPoints - 1 ); + pItem->yNodeTo = poLine->getY( iPoints - 1 ); + if( poLine->getCoordinateDimension() == 3 ) + { + pItem->zNodeFrom = poLine->getZ( 0 ); + pItem->zNodeTo = poLine->getZ( iPoints - 1 ); + pItem->bHasZ = TRUE; + } + else + pItem->bHasZ = FALSE; + } + } + } + delete poColl; + } + + /* searching the sub-tags */ + const CPLXMLNode *psChild = psNode->psChild; + while( psChild != NULL ) + { + if( psChild->eType == CXT_Element && + EQUAL( psChild->pszValue, "directedNode" ) ) + { + char cOrientation = '+'; + const char *pszGmlId = NULL; + int bIsHref = FALSE; + const CPLXMLNode *psAttr = psChild->psChild; + while( psAttr != NULL ) + { + if( psAttr->eType == CXT_Attribute && + EQUAL( psAttr->pszValue, "xlink:href" ) ) + { + const CPLXMLNode *psHref = psAttr->psChild; + if( psHref != NULL ) + { + if( psHref->eType == CXT_Text ) + { + pszGmlId = psHref->pszValue; + bIsHref = TRUE; + } + } + } + if( psAttr->eType == CXT_Attribute && + EQUAL( psAttr->pszValue, "orientation" ) ) + { + const CPLXMLNode *psOrientation = psAttr->psChild; + if( psOrientation != NULL ) + { + if( psOrientation->eType == CXT_Text ) + { + cOrientation = *(psOrientation->pszValue); + } + } + } + if( psAttr->eType == CXT_Element && + EQUAL( psAttr->pszValue, "Node" ) ) + { + const CPLXMLNode *psId = psAttr->psChild; + while( psId != NULL ) + { + if( psId->eType == CXT_Attribute && + EQUAL( psId->pszValue, "gml:id" ) ) + { + const CPLXMLNode *psIdGml = psId->psChild; + if( psIdGml != NULL ) + { + if( psIdGml->eType == CXT_Text ) + { + pszGmlId = psIdGml->pszValue; + bIsHref = FALSE; + } + } + } + psId = psId->psNext; + } + } + psAttr = psAttr->psNext; + } + if( pszGmlId != NULL ) + { + CPLString* posNode; + if( bIsHref == TRUE ) + { + if (pszGmlId[0] != '#') + { + CPLError(CE_Warning, CPLE_NotSupported, + "Only values of xlink:href element starting with '#' are supported, " + "so %s will not be properly recognized", pszGmlId); + } + posNode = new CPLString(pszGmlId+1); + } + else + posNode = new CPLString(pszGmlId); + if( cOrientation == '-' ) + { + pItem->gmlNodeFrom = posNode; + pItem->bIsNodeFromHref = bIsHref; + } + else + { + pItem->gmlNodeTo = posNode; + pItem->bIsNodeToHref = bIsHref; + } + pszGmlId = NULL; + bIsHref = FALSE; + cOrientation = '+'; + } + } + psChild = psChild->psNext; + } +} + +static void gmlHugeFileCheckXrefs( struct huge_helper *helper, + const CPLXMLNode *psNode ) +{ +/* identifying GML nodes */ + if( psNode->eType == CXT_Element ) + { + if( EQUAL(psNode->pszValue, "Edge") == TRUE ) + { + CPLString *gmlId = NULL; + if( gmlHugeFindGmlId( psNode, &gmlId ) == TRUE ) + { + char * gmlText = CPLSerializeXMLTree((CPLXMLNode *)psNode); + CPLString *gmlValue = new CPLString(gmlText); + CPLFree( gmlText ); + struct huge_tag *pItem = gmlHugeAddToHelper( helper, gmlId, + gmlValue ); + if( pItem != NULL ) + gmlHugeFileNodeCoords( pItem, psNode, &(helper->nodeSrs) ); + else + { + delete gmlId; + delete gmlValue; + } + } + } + } + + /* recursively scanning each Child GML node */ + const CPLXMLNode *psChild = psNode->psChild; + while( psChild != NULL ) + { + if( psChild->eType == CXT_Element ) + { + if( EQUAL(psChild->pszValue, "Edge") == TRUE || + EQUAL(psChild->pszValue, "directedEdge") == TRUE ) + { + gmlHugeFileCheckXrefs( helper, psChild ); + } + if( EQUAL(psChild->pszValue, "directedFace") == TRUE ) + { + const CPLXMLNode *psFace = psChild->psChild; + if( psFace != NULL ) + { + if( psFace->eType == CXT_Element && + EQUAL(psFace->pszValue, "Face") == TRUE) + { + const CPLXMLNode *psDirEdge = psFace->psChild; + while (psDirEdge != NULL) + { + const CPLXMLNode *psEdge = psDirEdge->psChild; + while( psEdge != NULL) + { + if( psEdge->eType == CXT_Element && + EQUAL(psEdge->pszValue, "Edge") == TRUE) + gmlHugeFileCheckXrefs( helper, psEdge ); + psEdge = psEdge->psNext; + } + psDirEdge = psDirEdge->psNext; + } + } + } + } + } + psChild = psChild->psNext; + } + + /* recursively scanning each GML of the same level */ + const CPLXMLNode *psNext = psNode->psNext; + while( psNext != NULL ) + { + if( psNext->eType == CXT_Element ) + { + if( EQUAL(psNext->pszValue, "Edge") == TRUE || + EQUAL(psNext->pszValue, "directedEdge") == TRUE ) + { + gmlHugeFileCheckXrefs( helper, psNext ); + } + } + psNext = psNext->psNext; + } +} + +static void gmlHugeFileCleanUp ( struct huge_helper *helper ) +{ +/* cleaning up any SQLite handle */ + if( helper->hNodes != NULL ) + sqlite3_finalize ( helper->hNodes ); + if( helper->hEdges != NULL ) + sqlite3_finalize ( helper->hEdges ); + if( helper->hDB != NULL ) + sqlite3_close( helper->hDB ); + if( helper->nodeSrs != NULL ) + delete helper->nodeSrs; +} + +static void gmlHugeFileCheckPendingHrefs( struct huge_helper *helper, + const CPLXMLNode *psParent, + const CPLXMLNode *psNode ) +{ +/* identifying any xlink:href to be replaced */ + if( psNode->eType == CXT_Element ) + { + if( EQUAL(psNode->pszValue, "directedEdge") == TRUE ) + { + char cOrientation = '+'; + CPLXMLNode *psAttr = psNode->psChild; + while( psAttr != NULL ) + { + if( psAttr->eType == CXT_Attribute && + EQUAL( psAttr->pszValue, "orientation" ) ) + { + const CPLXMLNode *psOrientation = psAttr->psChild; + if( psOrientation != NULL ) + { + if( psOrientation->eType == CXT_Text ) + cOrientation = *(psOrientation->pszValue); + } + } + psAttr = psAttr->psNext; + } + psAttr = psNode->psChild; + while( psAttr != NULL ) + { + if( psAttr->eType == CXT_Attribute && + EQUAL( psAttr->pszValue, "xlink:href" ) ) + { + const CPLXMLNode *pszHref = psAttr->psChild; + if( pszHref != NULL ) + { + if( pszHref->eType == CXT_Text ) + { + if (pszHref->pszValue[0] != '#') + { + CPLError(CE_Warning, CPLE_NotSupported, + "Only values of xlink:href element starting with '#' are supported, " + "so %s will not be properly recognized", pszHref->pszValue); + } + CPLString *gmlId = new CPLString(pszHref->pszValue+1); + gmlHugeAddPendingToHelper( helper, gmlId, psParent, + psNode, TRUE, cOrientation ); + } + } + } + psAttr = psAttr->psNext; + } + } + } + + /* recursively scanning each Child GML node */ + const CPLXMLNode *psChild = psNode->psChild; + while( psChild != NULL ) + { + if( psChild->eType == CXT_Element ) + { + if( EQUAL(psChild->pszValue, "directedEdge") == TRUE || + EQUAL(psChild->pszValue, "directedFace") == TRUE || + EQUAL(psChild->pszValue, "Face") == TRUE) + { + gmlHugeFileCheckPendingHrefs( helper, psNode, psChild ); + } + } + psChild = psChild->psNext; + } + + /* recursively scanning each GML of the same level */ + const CPLXMLNode *psNext = psNode->psNext; + while( psNext != NULL ) + { + if( psNext->eType == CXT_Element ) + { + if( EQUAL(psNext->pszValue, "Face") == TRUE ) + { + gmlHugeFileCheckPendingHrefs( helper, psParent, psNext ); + } + } + psNext = psNext->psNext; + } +} + +static void gmlHugeSetHrefGmlText( struct huge_helper *helper, + const char *pszGmlId, + const char *pszGmlText ) +{ +/* setting the GML text for the corresponding gml:id */ + struct huge_href *pItem = helper->pFirstHref; + while( pItem != NULL ) + { + if( EQUAL( pItem->gmlId->c_str(), pszGmlId ) == TRUE ) + { + if( pItem->gmlText != NULL) + delete pItem->gmlText; + pItem->gmlText = new CPLString( pszGmlText ); + return; + } + pItem = pItem->pNext; + } +} + +static struct huge_parent *gmlHugeFindParent( struct huge_helper *helper, + CPLXMLNode *psParent ) +{ +/* inserting a GML Node (parent) to be rewritted */ + struct huge_parent *pItem = helper->pFirstParent; + + /* checking if already exists */ + while( pItem != NULL ) + { + if( pItem->psParent == psParent ) + return pItem; + pItem = pItem->pNext; + } + + /* creating a new Parent Node */ + pItem = new struct huge_parent; + pItem->psParent = psParent; + pItem->pFirst = NULL; + pItem->pLast = NULL; + pItem->pNext = NULL; + if( helper->pFirstParent == NULL ) + helper->pFirstParent = pItem; + if( helper->pLastParent != NULL ) + helper->pLastParent->pNext = pItem; + helper->pLastParent = pItem; + + /* inserting any Child node into the Parent */ + CPLXMLNode *psChild = psParent->psChild; + while( psChild != NULL ) + { + struct huge_child *pChildItem; + pChildItem = new struct huge_child; + pChildItem->psChild = psChild; + pChildItem->pItem = NULL; + pChildItem->pNext = NULL; + if( pItem->pFirst == NULL ) + pItem->pFirst = pChildItem; + if( pItem->pLast != NULL ) + pItem->pLast->pNext = pChildItem; + pItem->pLast = pChildItem; + psChild = psChild->psNext; + } + return pItem; +} + +static int gmlHugeSetChild( struct huge_parent *pParent, + struct huge_href *pItem ) +{ +/* setting a Child Node to be rewritted */ + struct huge_child *pChild = pParent->pFirst; + while( pChild != NULL ) + { + if( pChild->psChild == pItem->psNode ) + { + pChild->pItem = pItem; + return TRUE; + } + pChild = pChild->pNext; + } + return FALSE; +} + +static int gmlHugeResolveEdges( CPL_UNUSED struct huge_helper *helper, + CPL_UNUSED CPLXMLNode *psNode, + sqlite3 *hDB ) +{ +/* resolving GML xlink:href */ + CPLString osCommand; + sqlite3_stmt *hStmtEdges; + int rc; + int bIsComma = FALSE; + int bError = FALSE; + struct huge_href *pItem; + struct huge_parent *pParent; + + /* query cursor [Edges] */ + osCommand = "SELECT gml_id, gml_resolved " + "FROM gml_edges " + "WHERE gml_id IN ("; + pItem = helper->pFirstHref; + while( pItem != NULL ) + { + if( bIsComma == TRUE ) + osCommand += ", "; + else + bIsComma = TRUE; + osCommand += "'"; + osCommand += pItem->gmlId->c_str(); + osCommand += "'"; + pItem = pItem->pNext; + } + osCommand += ")"; + rc = sqlite3_prepare_v2( hDB, osCommand.c_str(), -1, &hStmtEdges, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create QUERY stmt for EDGES" ); + return FALSE; + } + while ( TRUE ) + { + rc = sqlite3_step( hStmtEdges ); + if( rc == SQLITE_DONE ) + break; + if ( rc == SQLITE_ROW ) + { + const char *pszGmlId; + const char *pszGmlText = NULL; + pszGmlId = (const char *)sqlite3_column_text ( hStmtEdges, 0 ); + if( sqlite3_column_type( hStmtEdges, 1 ) != SQLITE_NULL ) + { + pszGmlText = (const char *)sqlite3_column_text ( hStmtEdges, 1 ); + gmlHugeSetHrefGmlText( helper, pszGmlId, pszGmlText ); + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Edge xlink:href QUERY: sqlite3_step(%s)", + sqlite3_errmsg(hDB) ); + bError = TRUE; + break; + } + } + sqlite3_finalize ( hStmtEdges ); + if( bError == TRUE ) + return FALSE; + + /* indentifying any GML node to be rewritten */ + pItem = helper->pFirstHref; + while( pItem != NULL ) + { + if( pItem->gmlText == NULL || pItem->psParent == NULL || + pItem->psNode == NULL ) + { + bError = TRUE; + break; + } + pParent = gmlHugeFindParent( helper, (CPLXMLNode *)pItem->psParent ); + if( gmlHugeSetChild( pParent, pItem ) == FALSE ) + { + bError = TRUE; + break; + } + pItem = pItem->pNext; + } + + if( bError == FALSE ) + { + /* rewriting GML nodes */ + pParent = helper->pFirstParent; + while( pParent != NULL ) + { + struct huge_child *pChild; + + /* removing any Child node from the Parent */ + pChild = pParent->pFirst; + while( pChild != NULL ) + { + CPLRemoveXMLChild( pParent->psParent, pChild->psChild ); + + /* destroyng any Child Node to be rewritten */ + if( pChild->pItem != NULL ) + CPLDestroyXMLNode( pChild->psChild ); + pChild = pChild->pNext; + } + + /* rewriting the Parent Node */ + pChild = pParent->pFirst; + while( pChild != NULL ) + { + if( pChild->pItem == NULL ) + { + /* reinserting any untouched Child Node */ + CPLAddXMLChild( pParent->psParent, pChild->psChild ); + } + else + { + /* rewriting a Child Node */ + CPLXMLNode *psNewNode = CPLCreateXMLNode(NULL, CXT_Element, "directedEdge"); + if( pChild->pItem->cOrientation == '-' ) + { + CPLXMLNode *psOrientationNode = CPLCreateXMLNode(psNewNode, CXT_Attribute, "orientation"); + CPLCreateXMLNode(psOrientationNode, CXT_Text, "-"); + } + CPLXMLNode *psEdge = CPLParseXMLString(pChild->pItem->gmlText->c_str()); + CPLAddXMLChild( psNewNode, psEdge ); + CPLAddXMLChild( pParent->psParent, psNewNode ); + } + pChild = pChild->pNext; + } + pParent = pParent->pNext; + } + } + + /* resetting the Rewrite Helper to an empty state */ + gmlHugeFileRewiterReset( helper ); + if( bError == TRUE ) + return FALSE; + return TRUE; +} + +static int gmlHugeFileWriteResolved ( struct huge_helper *helper, + const char *pszOutputFilename, + GMLReader *pReader, + int *m_bSequentialLayers ) +{ +/* writing the resolved GML file */ + VSILFILE *fp; + GMLFeature *poFeature; + const char *osCommand; + int rc; + sqlite3 *hDB = helper->hDB; + sqlite3_stmt *hStmtNodes; + int bError = FALSE; + int iOutCount = 0; + +/* -------------------------------------------------------------------- */ +/* Opening the resolved GML file */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszOutputFilename, "w" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %.500s to write.", pszOutputFilename ); + return FALSE; + } + + /* query cursor [Nodes] */ + osCommand = "SELECT gml_id, x, y, z " + "FROM nodes"; + rc = sqlite3_prepare_v2( hDB, osCommand, -1, &hStmtNodes, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create QUERY stmt for NODES" ); + VSIFCloseL( fp ); + return FALSE; + } + + VSIFPrintfL ( fp, "\n" ); + VSIFPrintfL ( fp, "\n" ); + VSIFPrintfL ( fp, " \n" ); + /* exporting Nodes */ + GFSTemplateList *pCC = new GFSTemplateList(); + while ( TRUE ) + { + rc = sqlite3_step( hStmtNodes ); + if( rc == SQLITE_DONE ) + break; + if ( rc == SQLITE_ROW ) + { + const char *pszGmlId; + char* pszEscaped; + double x; + double y; + double z = 0.0; + int bHasZ = FALSE; + pszGmlId = (const char *)sqlite3_column_text ( hStmtNodes, 0 ); + x = sqlite3_column_double ( hStmtNodes, 1 ); + y = sqlite3_column_double ( hStmtNodes, 2 ); + if ( sqlite3_column_type( hStmtNodes, 3 ) == SQLITE_FLOAT ) + { + z = sqlite3_column_double ( hStmtNodes, 3 ); + bHasZ = TRUE; + } + + /* inserting a node into the resolved GML file */ + pCC->Update( "ResolvedNodes", TRUE ); + VSIFPrintfL ( fp, " \n" ); + pszEscaped = CPLEscapeString( pszGmlId, -1, CPLES_XML ); + VSIFPrintfL ( fp, " %s\n", pszEscaped ); + CPLFree(pszEscaped); + VSIFPrintfL ( fp, " \n" ); + if ( helper->nodeSrs == NULL ) + VSIFPrintfL ( fp, " ", + ( bHasZ == TRUE ) ? 3 : 2 ); + else + { + pszEscaped = CPLEscapeString( helper->nodeSrs->c_str(), -1, CPLES_XML ); + VSIFPrintfL ( fp, " ", + ( bHasZ == TRUE ) ? 3 : 2, + pszEscaped ); + CPLFree(pszEscaped); + } + if ( bHasZ == TRUE ) + VSIFPrintfL ( fp, "%1.8f %1.8f %1.8f" + "\n", + x, y, z ); + else + VSIFPrintfL ( fp, "%1.8f %1.8f" + "\n", + x, y ); + VSIFPrintfL ( fp, " \n" ); + VSIFPrintfL ( fp, " \n" ); + iOutCount++; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "ResolvedNodes QUERY: sqlite3_step(%s)", + sqlite3_errmsg(hDB) ); + sqlite3_finalize ( hStmtNodes ); + return FALSE; + } + } + sqlite3_finalize ( hStmtNodes ); + + /* processing GML features */ + while( (poFeature = pReader->NextFeature()) != NULL ) + { + GMLFeatureClass *poClass = poFeature->GetClass(); + const CPLXMLNode* const * papsGeomList = poFeature->GetGeometryList(); + int iPropCount = poClass->GetPropertyCount(); + + int b_has_geom = FALSE; + VSIFPrintfL ( fp, " <%s>\n", poClass->GetElementName() ); + + for( int iProp = 0; iProp < iPropCount; iProp++ ) + { + GMLPropertyDefn *poPropDefn = poClass->GetProperty( iProp ); + const char *pszPropName = poPropDefn->GetName(); + const GMLProperty *poProp = poFeature->GetProperty( iProp ); + + if( poProp != NULL ) + { + for( int iSub = 0; iSub < poProp->nSubProperties; iSub++ ) + { + char *gmlText = CPLEscapeString( poProp->papszSubProperties[iSub], + -1, + CPLES_XML ); + VSIFPrintfL ( fp, " <%s>%s\n", + pszPropName, gmlText, pszPropName ); + CPLFree( gmlText ); + } + } + } + + if( papsGeomList != NULL ) + { + int i = 0; + const CPLXMLNode *psNode = papsGeomList[i]; + while( psNode != NULL ) + { + char *pszResolved = NULL; + int bNotToBeResolved; + if( psNode->eType != CXT_Element ) + bNotToBeResolved = TRUE; + else + { + if( EQUAL(psNode->pszValue, "TopoCurve") == TRUE || + EQUAL(psNode->pszValue, "TopoSurface") == TRUE ) + bNotToBeResolved = FALSE; + else + bNotToBeResolved = TRUE; + } + if( bNotToBeResolved == TRUE ) + { + VSIFPrintfL ( fp, " \n" ); + pszResolved = CPLSerializeXMLTree((CPLXMLNode *)psNode); + VSIFPrintfL ( fp, " %s\n", pszResolved ); + CPLFree( pszResolved ); + VSIFPrintfL ( fp, " \n" ); + b_has_geom = TRUE; + } + else + { + gmlHugeFileCheckPendingHrefs( helper, psNode, psNode ); + if( helper->pFirstHref == NULL ) + { + VSIFPrintfL ( fp, " \n" ); + pszResolved = CPLSerializeXMLTree((CPLXMLNode *)psNode); + VSIFPrintfL ( fp, " %s\n", pszResolved ); + CPLFree( pszResolved ); + VSIFPrintfL ( fp, " \n" ); + b_has_geom = TRUE; + } + else + { + if( gmlHugeResolveEdges( helper, (CPLXMLNode *)psNode, + hDB ) == FALSE) + bError = TRUE; + if( gmlHugeFileHrefCheck( helper ) == FALSE ) + bError = TRUE; + VSIFPrintfL ( fp, " \n" ); + pszResolved = CPLSerializeXMLTree((CPLXMLNode *)psNode); + VSIFPrintfL ( fp, " %s\n", pszResolved ); + CPLFree( pszResolved ); + VSIFPrintfL ( fp, " \n" ); + b_has_geom = TRUE; + gmlHugeFileHrefReset( helper ); + } + } + i++; + psNode = papsGeomList[i]; + } + } + pCC->Update( poClass->GetElementName(), b_has_geom ); + + VSIFPrintfL ( fp, " \n", poClass->GetElementName() ); + + delete poFeature; + iOutCount++; + } + + VSIFPrintfL ( fp, " \n" ); + VSIFPrintfL ( fp, "\n" ); + + VSIFCloseL( fp ); + + gmlUpdateFeatureClasses( pCC, pReader, m_bSequentialLayers ); + if ( *m_bSequentialLayers == TRUE ) + pReader->ReArrangeTemplateClasses( pCC ); + delete pCC; + if( bError == TRUE || iOutCount == 0 ) + return FALSE; + return TRUE; +} + +/**************************************************************/ +/* */ +/* private member(s): */ +/* any other funtion is implemented as "internal" static, */ +/* so to make all the SQLite own stuff nicely "invisible" */ +/* */ +/**************************************************************/ + +int GMLReader::ParseXMLHugeFile( const char *pszOutputFilename, + const int bSqliteIsTempFile, + const int iSqliteCacheMB ) + +{ + GMLFeature *poFeature; + int iFeatureUID = 0; + int rc; + sqlite3 *hDB = NULL; + CPLString osSQLiteFilename; + const char *pszSQLiteFilename = NULL; + struct huge_helper helper; + char *pszErrMsg = NULL; + + /* initializing the helper struct */ + helper.hDB = NULL; + helper.hNodes = NULL; + helper.hEdges = NULL; + helper.nodeSrs = NULL; + helper.pFirst = NULL; + helper.pLast = NULL; + helper.pFirstHref = NULL; + helper.pLastHref = NULL; + helper.pFirstParent = NULL; + helper.pLastParent = NULL; + +/* -------------------------------------------------------------------- */ +/* Creating/Opening the SQLite DB file */ +/* -------------------------------------------------------------------- */ + osSQLiteFilename = CPLResetExtension( m_pszFilename, "sqlite" ); + pszSQLiteFilename = osSQLiteFilename.c_str(); + + VSIStatBufL statBufL; + if ( VSIStatExL ( pszSQLiteFilename, &statBufL, VSI_STAT_EXISTS_FLAG) == 0) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "sqlite3_open(%s) failed:\n\tDB-file already exists", + pszSQLiteFilename ); + return FALSE; + } + + rc = sqlite3_open( pszSQLiteFilename, &hDB ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "sqlite3_open(%s) failed: %s", + pszSQLiteFilename, sqlite3_errmsg( hDB ) ); + return FALSE; + } + helper.hDB = hDB; + +/* +* setting SQLite for max speed; this is intrinsically unsafe, +* and the DB file could be potentially damaged (power failure ...] +* +* but after all this one simply is a TEMPORARY FILE !! +* so there is no real risk condition +*/ + rc = sqlite3_exec( hDB, "PRAGMA synchronous = OFF", NULL, NULL, + &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to set PRAGMA synchronous = OFF: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + } + rc = sqlite3_exec( hDB, "PRAGMA journal_mode = OFF", NULL, NULL, + &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to set PRAGMA journal_mode = OFF: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + } + rc = sqlite3_exec( hDB, "PRAGMA locking_mode = EXCLUSIVE", NULL, NULL, + &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to set PRAGMA locking_mode = EXCLUSIVE: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + } + + /* setting the SQLite cache */ + if( iSqliteCacheMB > 0 ) + { + int cache_size = iSqliteCacheMB * 1024; + + /* refusing to allocate more than 1GB */ + if( cache_size > 1024 * 1024 ) + cache_size = 1024 * 1024; + char sqlPragma[1024]; + sprintf( sqlPragma, "PRAGMA cache_size = %d", cache_size ); + rc = sqlite3_exec( hDB, sqlPragma, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to set %s: %s", sqlPragma, pszErrMsg ); + sqlite3_free( pszErrMsg ); + } + } + + if( !SetupParser() ) + { + gmlHugeFileCleanUp ( &helper ); + return FALSE; + } + + /* creating SQLite tables and Insert cursors */ + if( gmlHugeFileSQLiteInit( &helper ) == FALSE ) + { + gmlHugeFileCleanUp ( &helper ); + return FALSE; + } + + /* processing GML features */ + while( (poFeature = NextFeature()) != NULL ) + { + const CPLXMLNode* const * papsGeomList = poFeature->GetGeometryList(); + if (papsGeomList != NULL) + { + int i = 0; + const CPLXMLNode *psNode = papsGeomList[i]; + while(psNode) + { + gmlHugeFileCheckXrefs( &helper, psNode ); + /* inserting into the SQLite DB any appropriate row */ + gmlHugeFileSQLiteInsert ( &helper ); + /* resetting an empty helper struct */ + gmlHugeFileReset ( &helper ); + i ++; + psNode = papsGeomList[i]; + } + } + iFeatureUID++; + delete poFeature; + } + + /* finalizing any SQLite Insert cursor */ + if( helper.hNodes != NULL ) + sqlite3_finalize ( helper.hNodes ); + helper.hNodes = NULL; + if( helper.hEdges != NULL ) + sqlite3_finalize ( helper.hEdges ); + helper.hEdges = NULL; + + /* confirming the still pending TRANSACTION */ + rc = sqlite3_exec( hDB, "COMMIT", NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to perform COMMIT TRANSACTION: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + +/* attempting to resolve GML strings */ + if( gmlHugeFileResolveEdges( &helper ) == FALSE ) + { + gmlHugeFileCleanUp ( &helper ); + return FALSE; + } + + /* restarting the GML parser */ + if( !SetupParser() ) + { + gmlHugeFileCleanUp ( &helper ); + return FALSE; + } + + /* output: writing the revolved GML file */ + if ( gmlHugeFileWriteResolved( &helper, pszOutputFilename, this, + &m_bSequentialLayers ) == FALSE) + { + gmlHugeFileCleanUp ( &helper ); + return FALSE; + } + + gmlHugeFileCleanUp ( &helper ); + if ( bSqliteIsTempFile == TRUE ) + VSIUnlink( pszSQLiteFilename ); + return TRUE; +} + +/**************************************************************/ +/* */ +/* an alternative resolver based on SQLite */ +/* */ +/**************************************************************/ +int GMLReader::HugeFileResolver( const char *pszFile, + int bSqliteIsTempFile, + int iSqliteCacheMB ) + +{ + // Check if the original source file is set. + if( m_pszFilename == NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GML source file needs to be set first with " + "GMLReader::SetSourceFile()." ); + return FALSE; + } + if ( ParseXMLHugeFile( pszFile, bSqliteIsTempFile, iSqliteCacheMB ) == FALSE ) + return FALSE; + + //set the source file to the resolved file + CleanupParser(); + if (fpGML) + VSIFCloseL(fpGML); + fpGML = NULL; + CPLFree( m_pszFilename ); + m_pszFilename = CPLStrdup( pszFile ); + return TRUE; +} + +#else // HAVE_SQLITE + +/**************************************************/ +/* if SQLite support isn't available we'll */ +/* simply output an error message */ +/**************************************************/ + +int GMLReader::HugeFileResolver( CPL_UNUSED const char *pszFile, + CPL_UNUSED int bSqliteIsTempFile, + CPL_UNUSED int iSqliteCacheMB ) + +{ + CPLError( CE_Failure, CPLE_NotSupported, + "OGR was built without SQLite3 support\n" + "... sorry, the HUGE GML resolver is unsupported\n" ); + return FALSE; +} + +int GMLReader::ParseXMLHugeFile( CPL_UNUSED const char *pszOutputFilename, + CPL_UNUSED const int bSqliteIsTempFile, + CPL_UNUSED const int iSqliteCacheMB ) +{ + CPLError( CE_Failure, CPLE_NotSupported, + "OGR was built without SQLite3 support\n" + "... sorry, the HUGE GML resolver is unsupported\n" ); + return FALSE; +} + +#endif // HAVE_SQLITE diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/makefile.vc new file mode 100644 index 000000000..ee14adb16 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/makefile.vc @@ -0,0 +1,46 @@ + +LL_OBJ = gmlpropertydefn.obj gmlfeatureclass.obj gmlfeature.obj \ + gmlreader.obj parsexsd.obj resolvexlinks.obj hugefileresolver.obj gmlutils.obj \ + gmlreadstate.obj gmlhandler.obj trstring.obj gfstemplate.obj gmlregistry.obj +OGR_OBJ = ogrgmldriver.obj ogrgmldatasource.obj ogrgmllayer.obj + +OBJ = $(LL_OBJ) $(OGR_OBJ) + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. $(XERCES_EXTRAFLAGS) $(EXPAT_EXTRAFLAGS) $(SQLITE_EXTRAFLAGS) + +# By default, XML validation is disabled. Uncomment the following line to +# enable XML schema validation in the parser. +#OGR_GML_VALIDATION = -DOGR_GML_VALIDATION=1 + +!IFDEF XERCES_DIR +XERCES_EXTRAFLAGS = $(XERCES_INCLUDE) -DHAVE_XERCES $(OGR_GML_VALIDATION) +!ENDIF + +!IFDEF EXPAT_DIR +EXPAT_EXTRAFLAGS = $(EXPAT_INCLUDE) -DHAVE_EXPAT +!ENDIF + +!IFDEF SQLITE_INC +SQLITE_EXTRAFLAGS = $(SQLITE_INC) -DHAVE_SQLITE +!ENDIF + +# By default, XML validation is disabled. Uncomment the following line to +# enable XML schema validation in the parser. +#ALL_C_FLAGS := -DOGR_GML_VALIDATION=1 $(ALL_C_FLAGS) + + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + +gmlview.exe: $(LL_OBJ) gmlview.obj + cl /Zi gmlview.obj $(LL_OBJ) \ + ../../ogr.lib ../ogrsf_frmts.lib ../ogrsf_frmts_sup.lib \ + $(GDAL_ROOT)/port/cpl.lib $(XERCES_LIB) $(LIBS) + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/ogr_gml.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/ogr_gml.h new file mode 100644 index 000000000..5adc21795 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/ogr_gml.h @@ -0,0 +1,222 @@ +/****************************************************************************** + * $Id: ogr_gml.h 29214 2015-05-20 13:47:29Z rouault $ + * + * Project: GML Reader + * Purpose: Declarations for OGR wrapper classes for GML, and GML<->OGR + * translation of geometry. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GML_H_INCLUDED +#define _OGR_GML_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "gmlreader.h" + +class OGRGMLDataSource; + +typedef enum +{ + STANDARD, + SEQUENTIAL_LAYERS, + INTERLEAVED_LAYERS +} ReadMode; + +/************************************************************************/ +/* OGRGMLLayer */ +/************************************************************************/ + +class OGRGMLLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + + GIntBig iNextGMLId; + int nTotalGMLCount; + int bInvalidFIDFound; + char *pszFIDPrefix; + + int bWriter; + int bSameSRS; + + OGRGMLDataSource *poDS; + + GMLFeatureClass *poFClass; + + void *hCacheSRS; + + int bUseOldFIDFormat; + + int bFaceHoleNegative; + + public: + OGRGMLLayer( const char * pszName, + int bWriter, + OGRGMLDataSource *poDS ); + + ~OGRGMLLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + GIntBig GetFeatureCount( int bForce = TRUE ); + OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + OGRErr ICreateFeature( OGRFeature *poFeature ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr CreateGeomField( OGRGeomFieldDefn *poField, + int bApproxOK = TRUE ); + + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRGMLDataSource */ +/************************************************************************/ + +class OGRGMLDataSource : public OGRDataSource +{ + OGRGMLLayer **papoLayers; + int nLayers; + + char *pszName; + + OGRGMLLayer *TranslateGMLSchema( GMLFeatureClass * ); + + char **papszCreateOptions; + + // output related parameters + VSILFILE *fpOutput; + int bFpOutputIsNonSeekable; + int bFpOutputSingleFile; + OGREnvelope3D sBoundingRect; + int bBBOX3D; + int nBoundedByLocation; + + int nSchemaInsertLocation; + int bIsOutputGML3; + int bIsOutputGML3Deegree; /* if TRUE, then bIsOutputGML3 is also TRUE */ + int bIsOutputGML32; /* if TRUE, then bIsOutputGML3 is also TRUE */ + int bIsLongSRSRequired; + int bWriteSpaceIndentation; + + OGRSpatialReference* poWriteGlobalSRS; + int bWriteGlobalSRS; + + // input related parameters. + CPLString osFilename; + CPLString osXSDFilename; + + IGMLReader *poReader; + int bOutIsTempFile; + + void InsertHeader(); + + int bExposeGMLId; + int bExposeFid; + int bIsWFS; + + int bUseGlobalSRSName; + + int m_bInvertAxisOrderIfLatLong; + int m_bConsiderEPSGAsURN; + int m_bGetSecondaryGeometryOption; + + ReadMode eReadMode; + GMLFeature *poStoredGMLFeature; + OGRGMLLayer *poLastReadLayer; + + int bEmptyAsNull; + + void FindAndParseTopElements(VSILFILE* fp); + void SetExtents(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY); + + void BuildJointClassFromXSD(); + void BuildJointClassFromScannedSchema(); + + void WriteTopElements(); + + public: + OGRGMLDataSource(); + ~OGRGMLDataSource(); + + int Open( GDALOpenInfo* poOpenInfo ); + int Create( const char *pszFile, char **papszOptions ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + int TestCapability( const char * ); + + VSILFILE *GetOutputFP() const { return fpOutput; } + IGMLReader *GetReader() const { return poReader; } + + void GrowExtents( OGREnvelope3D *psGeomBounds, int nCoordDimension ); + + int ExposeId() const { return bExposeGMLId || bExposeFid; } + + static void PrintLine(VSILFILE* fp, const char *fmt, ...) CPL_PRINT_FUNC_FORMAT (2, 3); + + int IsGML3Output() const { return bIsOutputGML3; } + int IsGML3DeegreeOutput() const { return bIsOutputGML3Deegree; } + int IsGML32Output() const { return bIsOutputGML32; } + int IsLongSRSRequired() const { return bIsLongSRSRequired; } + int WriteSpaceIndentation() const { return bWriteSpaceIndentation; } + const char *GetGlobalSRSName(); + + int GetInvertAxisOrderIfLatLong() const { return m_bInvertAxisOrderIfLatLong; } + int GetConsiderEPSGAsURN() const { return m_bConsiderEPSGAsURN; } + int GetSecondaryGeometryOption() const { return m_bGetSecondaryGeometryOption; } + + ReadMode GetReadMode() const { return eReadMode; } + void SetStoredGMLFeature(GMLFeature* poStoredGMLFeatureIn) { poStoredGMLFeature = poStoredGMLFeatureIn; } + GMLFeature* PeekStoredGMLFeature() const { return poStoredGMLFeature; } + + OGRGMLLayer* GetLastReadLayer() const { return poLastReadLayer; } + void SetLastReadLayer(OGRGMLLayer* poLayer) { poLastReadLayer = poLayer; } + + const char *GetAppPrefix(); + int RemoveAppPrefix(); + int WriteFeatureBoundedBy(); + const char *GetSRSDimensionLoc(); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poResultsSet ); + + static int CheckHeader(const char* pszStr); +}; + +#endif /* _OGR_GML_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/ogrgmldatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/ogrgmldatasource.cpp new file mode 100644 index 000000000..d8a623c27 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/ogrgmldatasource.cpp @@ -0,0 +1,2811 @@ +/****************************************************************************** + * $Id: ogrgmldatasource.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OGR + * Purpose: Implements OGRGMLDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + ****************************************************************************** + * Contributor: Alessandro Furieri, a.furieri@lqt.it + * Portions of this module implenting GML_SKIP_RESOLVE_ELEMS HUGE + * Developed for Faunalia ( http://www.faunalia.it) with funding from + * Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE + * + ****************************************************************************/ + +#include "ogr_gml.h" +#include "parsexsd.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_http.h" +#include "gmlutils.h" +#include "ogr_p.h" +#include "gmlregistry.h" +#include "gmlreaderp.h" + +#include + +CPL_CVSID("$Id: ogrgmldatasource.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +static int ExtractSRSName(const char* pszXML, char* szSRSName, + size_t sizeof_szSRSName); + +/************************************************************************/ +/* ReplaceSpaceByPct20IfNeeded() */ +/************************************************************************/ + +static CPLString ReplaceSpaceByPct20IfNeeded(const char* pszURL) +{ + /* Replace ' ' by '%20' */ + CPLString osRet = pszURL; + const char* pszNeedle = strstr(pszURL, "; "); + if (pszNeedle) + { + char* pszTmp = (char*)CPLMalloc(strlen(pszURL) + 2 +1); + int nBeforeNeedle = (int)(pszNeedle - pszURL); + memcpy(pszTmp, pszURL, nBeforeNeedle); + strcpy(pszTmp + nBeforeNeedle, ";%20"); + strcpy(pszTmp + nBeforeNeedle + strlen(";%20"), pszNeedle + strlen("; ")); + osRet = pszTmp; + CPLFree(pszTmp); + } + + return osRet; +} + +/************************************************************************/ +/* OGRGMLDataSource() */ +/************************************************************************/ + +OGRGMLDataSource::OGRGMLDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + + poReader = NULL; + fpOutput = NULL; + bFpOutputIsNonSeekable = FALSE; + bFpOutputSingleFile = FALSE; + bIsOutputGML3 = FALSE; + bIsOutputGML3Deegree = FALSE; + bIsOutputGML32 = FALSE; + bIsLongSRSRequired = FALSE; + bWriteSpaceIndentation = TRUE; + + papszCreateOptions = NULL; + bOutIsTempFile = FALSE; + + bExposeGMLId = FALSE; + bExposeFid = FALSE; + nSchemaInsertLocation = -1; + nBoundedByLocation = -1; + bBBOX3D = FALSE; + + poWriteGlobalSRS = NULL; + bWriteGlobalSRS = FALSE; + bUseGlobalSRSName = FALSE; + bIsWFS = FALSE; + + eReadMode = STANDARD; + poStoredGMLFeature = NULL; + poLastReadLayer = NULL; + + m_bInvertAxisOrderIfLatLong = FALSE; + m_bConsiderEPSGAsURN = FALSE; + m_bGetSecondaryGeometryOption = FALSE; + bEmptyAsNull = TRUE; +} + +/************************************************************************/ +/* ~OGRGMLDataSource() */ +/************************************************************************/ + +OGRGMLDataSource::~OGRGMLDataSource() + +{ + + if( fpOutput != NULL ) + { + if( nLayers == 0 ) + WriteTopElements(); + + const char* pszPrefix = GetAppPrefix(); + if( RemoveAppPrefix() ) + PrintLine( fpOutput, "" ); + else + PrintLine( fpOutput, "", pszPrefix ); + + if( bFpOutputIsNonSeekable) + { + VSIFCloseL( fpOutput ); + fpOutput = NULL; + } + + InsertHeader(); + + if( !bFpOutputIsNonSeekable + && nBoundedByLocation != -1 + && VSIFSeekL( fpOutput, nBoundedByLocation, SEEK_SET ) == 0 ) + { + if (bWriteGlobalSRS && sBoundingRect.IsInit() && IsGML3Output()) + { + int bCoordSwap = FALSE; + char* pszSRSName; + if (poWriteGlobalSRS) + pszSRSName = GML_GetSRSName(poWriteGlobalSRS, IsLongSRSRequired(), &bCoordSwap); + else + pszSRSName = CPLStrdup(""); + char szLowerCorner[75], szUpperCorner[75]; + if (bCoordSwap) + { + OGRMakeWktCoordinate(szLowerCorner, sBoundingRect.MinY, sBoundingRect.MinX, sBoundingRect.MinZ, (bBBOX3D) ? 3 : 2); + OGRMakeWktCoordinate(szUpperCorner, sBoundingRect.MaxY, sBoundingRect.MaxX, sBoundingRect.MaxZ, (bBBOX3D) ? 3 : 2); + } + else + { + OGRMakeWktCoordinate(szLowerCorner, sBoundingRect.MinX, sBoundingRect.MinY, sBoundingRect.MinZ, (bBBOX3D) ? 3 : 2); + OGRMakeWktCoordinate(szUpperCorner, sBoundingRect.MaxX, sBoundingRect.MaxY, sBoundingRect.MaxZ, (bBBOX3D) ? 3 : 2); + } + if (bWriteSpaceIndentation) + VSIFPrintfL( fpOutput, " "); + PrintLine( fpOutput, "%s%s", + (bBBOX3D) ? " srsDimension=\"3\"" : "", pszSRSName, szLowerCorner, szUpperCorner); + CPLFree(pszSRSName); + } + else if (bWriteGlobalSRS && sBoundingRect.IsInit()) + { + if (bWriteSpaceIndentation) + VSIFPrintfL( fpOutput, " "); + PrintLine( fpOutput, "" ); + if (bWriteSpaceIndentation) + VSIFPrintfL( fpOutput, " "); + PrintLine( fpOutput, "" ); + if (bWriteSpaceIndentation) + VSIFPrintfL( fpOutput, " "); + VSIFPrintfL( fpOutput, + "%.16g" + "%.16g", + sBoundingRect.MinX, sBoundingRect.MinY ); + if (bBBOX3D) + VSIFPrintfL( fpOutput, "%.16g", + sBoundingRect.MinZ ); + PrintLine( fpOutput, ""); + if (bWriteSpaceIndentation) + VSIFPrintfL( fpOutput, " "); + VSIFPrintfL( fpOutput, + "%.16g" + "%.16g", + sBoundingRect.MaxX, sBoundingRect.MaxY ); + if (bBBOX3D) + VSIFPrintfL( fpOutput, "%.16g", + sBoundingRect.MaxZ ); + PrintLine( fpOutput, ""); + if (bWriteSpaceIndentation) + VSIFPrintfL( fpOutput, " "); + PrintLine( fpOutput, "" ); + if (bWriteSpaceIndentation) + VSIFPrintfL( fpOutput, " "); + PrintLine( fpOutput, "" ); + } + else + { + if (bWriteSpaceIndentation) + VSIFPrintfL( fpOutput, " "); + if (IsGML3Output()) + PrintLine( fpOutput, "" ); + else + PrintLine( fpOutput, "missing" ); + } + } + + if (fpOutput) + VSIFCloseL( fpOutput ); + } + + CSLDestroy( papszCreateOptions ); + CPLFree( pszName ); + + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + if( poReader ) + { + if (bOutIsTempFile) + VSIUnlink(poReader->GetSourceFileName()); + delete poReader; + } + + delete poWriteGlobalSRS; + + delete poStoredGMLFeature; + + if (osXSDFilename.compare(CPLSPrintf("/vsimem/tmp_gml_xsd_%p.xsd", this)) == 0) + VSIUnlink(osXSDFilename); +} + +/************************************************************************/ +/* CheckHeader() */ +/************************************************************************/ + +int OGRGMLDataSource::CheckHeader(const char* pszStr) +{ + if( strstr(pszStr,"opengis.net/gml") == NULL && + strstr(pszStr,"") != NULL || + strstr(pszStr, "pszFilename; + const char *pszXSDFilenameTmp = strstr(poOpenInfo->pszFilename, ",xsd="); + if (pszXSDFilenameTmp != NULL) + { + osFilename.resize(pszXSDFilenameTmp - poOpenInfo->pszFilename); + osXSDFilename = pszXSDFilenameTmp + strlen(",xsd="); + } + else + osXSDFilename = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "XSD", ""); + + const char *pszFilename = osFilename.c_str(); + + pszName = CPLStrdup( poOpenInfo->pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Open the source file. */ +/* -------------------------------------------------------------------- */ + VSILFILE* fpToClose = NULL; + if( poOpenInfo->fpL != NULL ) + { + fp = poOpenInfo->fpL; + VSIFSeekL(fp, 0, SEEK_SET); + } + else + { + fpToClose = fp = VSIFOpenL( pszFilename, "r" ); + if( fp == NULL ) + return FALSE; + } + + int bExpatCompatibleEncoding = FALSE; + int bHas3D = FALSE; + int bHintConsiderEPSGAsURN = FALSE; + int bAnalyzeSRSPerFeature = TRUE; + + char szSRSName[128]; + szSRSName[0] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Load a header chunk and check for signs it is GML */ +/* -------------------------------------------------------------------- */ + + size_t nRead = VSIFReadL( szHeader, 1, sizeof(szHeader)-1, fp ); + if (nRead <= 0) + { + if( fpToClose ) + VSIFCloseL( fpToClose ); + return FALSE; + } + szHeader[nRead] = '\0'; + + /* Might be a OS-Mastermap gzipped GML, so let be nice and try to open */ + /* it transparently with /vsigzip/ */ + if ( ((GByte*)szHeader)[0] == 0x1f && ((GByte*)szHeader)[1] == 0x8b && + EQUAL(CPLGetExtension(pszFilename), "gz") && + strncmp(pszFilename, "/vsigzip/", strlen("/vsigzip/")) != 0 ) + { + if( fpToClose ) + VSIFCloseL( fpToClose ); + fpToClose = NULL; + osWithVsiGzip = "/vsigzip/"; + osWithVsiGzip += pszFilename; + + pszFilename = osWithVsiGzip; + + fp = fpToClose = VSIFOpenL( pszFilename, "r" ); + if( fp == NULL ) + return FALSE; + + nRead = VSIFReadL( szHeader, 1, sizeof(szHeader) - 1, fp ); + if (nRead <= 0) + { + VSIFCloseL( fpToClose ); + return FALSE; + } + szHeader[nRead] = '\0'; + } + +/* -------------------------------------------------------------------- */ +/* Check for a UTF-8 BOM and skip if found */ +/* */ +/* TODO: BOM is variable-lenght parameter and depends on encoding. */ +/* Add BOM detection for other encodings. */ +/* -------------------------------------------------------------------- */ + + // Used to skip to actual beginning of XML data + char* szPtr = szHeader; + + if( ( (unsigned char)szHeader[0] == 0xEF ) + && ( (unsigned char)szHeader[1] == 0xBB ) + && ( (unsigned char)szHeader[2] == 0xBF) ) + { + szPtr += 3; + } + + const char* pszEncoding = strstr(szPtr, "encoding="); + if (pszEncoding) + bExpatCompatibleEncoding = (pszEncoding[9] == '\'' || pszEncoding[9] == '"') && + (EQUALN(pszEncoding + 10, "UTF-8", 5) || + EQUALN(pszEncoding + 10, "ISO-8859-15", 11) || + (EQUALN(pszEncoding + 10, "ISO-8859-1", 10) && + pszEncoding[20] == pszEncoding[9])) ; + else + bExpatCompatibleEncoding = TRUE; /* utf-8 is the default */ + + bHas3D = strstr(szPtr, "srsDimension=\"3\"") != NULL || strstr(szPtr, "") != NULL; + +/* -------------------------------------------------------------------- */ +/* Here, we expect the opening chevrons of GML tree root element */ +/* -------------------------------------------------------------------- */ + if( szPtr[0] != '<' || !CheckHeader(szPtr) ) + { + if( fpToClose ) + VSIFCloseL( fpToClose ); + return FALSE; + } + + /* Now we definitely own the file descriptor */ + if( fp == poOpenInfo->fpL ) + poOpenInfo->fpL = NULL; + + /* Small optimization: if we parse a and */ + /* that numberOfFeatures is set, we can use it to set the FeatureCount */ + /* but *ONLY* if there's just one class ! */ + const char* pszFeatureCollection = strstr(szPtr, "wfs:FeatureCollection"); + if (pszFeatureCollection == NULL) + pszFeatureCollection = strstr(szPtr, "gml:FeatureCollection"); /* GML 3.2.1 output */ + if (pszFeatureCollection == NULL) + { + pszFeatureCollection = strstr(szPtr, " */ + /* Who knows what servers can return ? Ok, so when in the context of the WFS driver */ + /* always expose the gml:id to avoid later crashes */ + bExposeGMLId = TRUE; + bIsWFS = TRUE; + } + else + { + bExposeGMLId = strstr(szPtr, " gml:id=\"") != NULL || + strstr(szPtr, " gml:id='") != NULL; + bExposeFid = strstr(szPtr, " fid=\"") != NULL || + strstr(szPtr, " fid='") != NULL; + } + + const char* pszExposeGMLId = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, + "EXPOSE_GML_ID", CPLGetConfigOption("GML_EXPOSE_GML_ID", NULL)); + if (pszExposeGMLId) + bExposeGMLId = CSLTestBoolean(pszExposeGMLId); + + const char* pszExposeFid = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, + "EXPOSE_FID", CPLGetConfigOption("GML_EXPOSE_FID", NULL)); + if (pszExposeFid) + bExposeFid = CSLTestBoolean(pszExposeFid); + + bHintConsiderEPSGAsURN = strstr(szPtr, "xmlns:fme=\"http://www.safe.com/gml/fme\"") != NULL; + + /* MTKGML */ + if( strstr(szPtr, ""); + if( bIsWFSJointLayer ) + bExposeGMLId = FALSE; + +/* -------------------------------------------------------------------- */ +/* We assume now that it is GML. Instantiate a GMLReader on it. */ +/* -------------------------------------------------------------------- */ + + const char* pszReadMode = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, + "READ_MODE", + CPLGetConfigOption("GML_READ_MODE", "AUTO")); + if( EQUAL(pszReadMode, "AUTO") ) + pszReadMode = NULL; + if (pszReadMode == NULL || EQUAL(pszReadMode, "STANDARD")) + eReadMode = STANDARD; + else if (EQUAL(pszReadMode, "SEQUENTIAL_LAYERS")) + eReadMode = SEQUENTIAL_LAYERS; + else if (EQUAL(pszReadMode, "INTERLEAVED_LAYERS")) + eReadMode = INTERLEAVED_LAYERS; + else + { + CPLDebug("GML", "Unrecognized value for GML_READ_MODE configuration option."); + } + + m_bInvertAxisOrderIfLatLong = + CSLTestBoolean(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, + "INVERT_AXIS_ORDER_IF_LAT_LONG", + CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES"))); + + const char* pszConsiderEPSGAsURN = + CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, + "CONSIDER_EPSG_AS_URN", + CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", "AUTO")); + if( !EQUAL(pszConsiderEPSGAsURN, "AUTO") ) + m_bConsiderEPSGAsURN = CSLTestBoolean(pszConsiderEPSGAsURN); + else if (bHintConsiderEPSGAsURN) + { + /* GML produced by FME (at least CanVec GML) seem to honour EPSG axis ordering */ + CPLDebug("GML", "FME-produced GML --> consider that GML_CONSIDER_EPSG_AS_URN is set to YES"); + m_bConsiderEPSGAsURN = TRUE; + } + else + m_bConsiderEPSGAsURN = FALSE; + + m_bGetSecondaryGeometryOption = CSLTestBoolean(CPLGetConfigOption("GML_GET_SECONDARY_GEOM", "NO")); + + /* EXPAT is faster than Xerces, so when it is safe to use it, use it ! */ + /* The only interest of Xerces is for rare encodings that Expat doesn't handle */ + /* but UTF-8 is well handled by Expat */ + int bUseExpatParserPreferably = bExpatCompatibleEncoding; + + /* Override default choice */ + const char* pszGMLParser = CPLGetConfigOption("GML_PARSER", NULL); + if (pszGMLParser) + { + if (EQUAL(pszGMLParser, "EXPAT")) + bUseExpatParserPreferably = TRUE; + else if (EQUAL(pszGMLParser, "XERCES")) + bUseExpatParserPreferably = FALSE; + } + + poReader = CreateGMLReader( bUseExpatParserPreferably, + m_bInvertAxisOrderIfLatLong, + m_bConsiderEPSGAsURN, + m_bGetSecondaryGeometryOption ); + if( poReader == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "File %s appears to be GML but the GML reader can't\n" + "be instantiated, likely because Xerces or Expat support wasn't\n" + "configured in.", + pszFilename ); + VSIFCloseL( fp ); + return FALSE; + } + + poReader->SetSourceFile( pszFilename ); + ((GMLReader*)poReader)->SetIsWFSJointLayer(bIsWFSJointLayer); + bEmptyAsNull = CSLFetchBoolean(poOpenInfo->papszOpenOptions, "EMPTY_AS_NULL", TRUE); + ((GMLReader*)poReader)->SetEmptyAsNull(bEmptyAsNull); + ((GMLReader*)poReader)->SetReportAllAttributes( + CSLFetchBoolean(poOpenInfo->papszOpenOptions, "GML_ATTRIBUTES_TO_OGR_FIELDS", + CSLTestBoolean(CPLGetConfigOption("GML_ATTRIBUTES_TO_OGR_FIELDS", "NO")))); + +/* -------------------------------------------------------------------- */ +/* Find , and */ +/* -------------------------------------------------------------------- */ + + FindAndParseTopElements(fp); + + if( szSRSName[0] != '\0' ) + poReader->SetGlobalSRSName(szSRSName); + +/* -------------------------------------------------------------------- */ +/* Resolve the xlinks in the source file and save it with the */ +/* extension ".resolved.gml". The source file will to set to that. */ +/* -------------------------------------------------------------------- */ + + char *pszXlinkResolvedFilename = NULL; + const char *pszOption = CPLGetConfigOption("GML_SAVE_RESOLVED_TO", NULL); + int bResolve = TRUE; + int bHugeFile = FALSE; + if( pszOption != NULL && EQUALN( pszOption, "SAME", 4 ) ) + { + // "SAME" will overwrite the existing gml file + pszXlinkResolvedFilename = CPLStrdup( pszFilename ); + } + else if( pszOption != NULL && + CPLStrnlen( pszOption, 5 ) >= 5 && + EQUALN( pszOption - 4 + strlen( pszOption ), ".gml", 4 ) ) + { + // Any string ending with ".gml" will try and write to it + pszXlinkResolvedFilename = CPLStrdup( pszOption ); + } + else + { + // When no option is given or is not recognised, + // use the same file name with the extension changed to .resolved.gml + pszXlinkResolvedFilename = CPLStrdup( + CPLResetExtension( pszFilename, "resolved.gml" ) ); + + // Check if the file already exists. + VSIStatBufL sResStatBuf, sGMLStatBuf; + if( bCheckAuxFile && VSIStatL( pszXlinkResolvedFilename, &sResStatBuf ) == 0 ) + { + VSIStatL( pszFilename, &sGMLStatBuf ); + if( sGMLStatBuf.st_mtime > sResStatBuf.st_mtime ) + { + CPLDebug( "GML", + "Found %s but ignoring because it appears\n" + "be older than the associated GML file.", + pszXlinkResolvedFilename ); + } + else + { + poReader->SetSourceFile( pszXlinkResolvedFilename ); + bResolve = FALSE; + } + } + } + + const char *pszSkipOption = CPLGetConfigOption( "GML_SKIP_RESOLVE_ELEMS", + "ALL"); + char **papszSkip = NULL; + if( EQUAL( pszSkipOption, "ALL" ) ) + bResolve = FALSE; + else if( EQUAL( pszSkipOption, "HUGE" ) )//exactly as NONE, but intended for HUGE files + bHugeFile = TRUE; + else if( !EQUAL( pszSkipOption, "NONE" ) )//use this to resolve everything + papszSkip = CSLTokenizeString2( pszSkipOption, ",", + CSLT_STRIPLEADSPACES | + CSLT_STRIPENDSPACES ); + int bHaveSchema = FALSE; + int bSchemaDone = FALSE; + +/* -------------------------------------------------------------------- */ +/* Is some GML Feature Schema (.gfs) TEMPLATE required ? */ +/* -------------------------------------------------------------------- */ + const char *pszGFSTemplateName = + CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "GFS_TEMPLATE", + CPLGetConfigOption( "GML_GFS_TEMPLATE", NULL)); + if( pszGFSTemplateName != NULL ) + { + /* attempting to load the GFS TEMPLATE */ + bHaveSchema = poReader->LoadClasses( pszGFSTemplateName ); + } + + if( bResolve ) + { + if ( bHugeFile ) + { + bSchemaDone = TRUE; + int bSqliteIsTempFile = + CSLTestBoolean(CPLGetConfigOption( "GML_HUGE_TEMPFILE", "YES")); + int iSqliteCacheMB = atoi(CPLGetConfigOption( "OGR_SQLITE_CACHE", "0")); + if( poReader->HugeFileResolver( pszXlinkResolvedFilename, + bSqliteIsTempFile, + iSqliteCacheMB ) == FALSE ) + { + // we assume an errors have been reported. + VSIFCloseL(fp); + CPLFree( pszXlinkResolvedFilename ); + return FALSE; + } + } + else + { + poReader->ResolveXlinks( pszXlinkResolvedFilename, + &bOutIsTempFile, + papszSkip ); + } + } + + CPLFree( pszXlinkResolvedFilename ); + pszXlinkResolvedFilename = NULL; + CSLDestroy( papszSkip ); + papszSkip = NULL; + + /* If the source filename for the reader is still the GML filename, then */ + /* we can directly provide the file pointer. Otherwise we close it */ + if (strcmp(poReader->GetSourceFileName(), pszFilename) == 0) + poReader->SetFP(fp); + else + VSIFCloseL(fp); + fp = NULL; + + /* Is a prescan required ? */ + if( bHaveSchema && !bSchemaDone ) + { + /* We must detect which layers are actually present in the .gml */ + /* and how many features they have */ + if( !poReader->PrescanForTemplate() ) + { + // we assume an errors have been reported. + return FALSE; + } + } + + CPLString osGFSFilename = CPLResetExtension( pszFilename, "gfs" ); + if (strncmp(osGFSFilename, "/vsigzip/", strlen("/vsigzip/")) == 0) + osGFSFilename = osGFSFilename.substr(strlen("/vsigzip/")); + +/* -------------------------------------------------------------------- */ +/* Can we find a GML Feature Schema (.gfs) for the input file? */ +/* -------------------------------------------------------------------- */ + if( !bHaveSchema && osXSDFilename.size() == 0) + { + VSIStatBufL sGFSStatBuf; + if( bCheckAuxFile && VSIStatL( osGFSFilename, &sGFSStatBuf ) == 0 ) + { + VSIStatBufL sGMLStatBuf; + VSIStatL( pszFilename, &sGMLStatBuf ); + if( sGMLStatBuf.st_mtime > sGFSStatBuf.st_mtime ) + { + CPLDebug( "GML", + "Found %s but ignoring because it appears\n" + "be older than the associated GML file.", + osGFSFilename.c_str() ); + } + else + { + bHaveSchema = poReader->LoadClasses( osGFSFilename ); + if (bHaveSchema) + { + const char *pszXSDFilenameTmp; + pszXSDFilenameTmp = CPLResetExtension( pszFilename, "xsd" ); + if( VSIStatExL( pszXSDFilenameTmp, &sGMLStatBuf, + VSI_STAT_EXISTS_FLAG ) == 0 ) + { + CPLDebug("GML", "Using %s file, ignoring %s", + osGFSFilename.c_str(), pszXSDFilenameTmp); + } + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Can we find an xsd which might conform to tbe GML3 Level 0 */ +/* profile? We really ought to look for it based on the rules */ +/* schemaLocation in the GML feature collection but for now we */ +/* just hopes it is in the same director with the same name. */ +/* -------------------------------------------------------------------- */ + int bHasFoundXSD = FALSE; + + if( !bHaveSchema ) + { + char** papszTypeNames = NULL; + + VSIStatBufL sXSDStatBuf; + if (osXSDFilename.size() == 0) + { + osXSDFilename = CPLResetExtension( pszFilename, "xsd" ); + if( bCheckAuxFile && VSIStatExL( osXSDFilename, &sXSDStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + { + bHasFoundXSD = TRUE; + } + } + else + { + if ( strncmp(osXSDFilename, "http://", 7) == 0 || + strncmp(osXSDFilename, "https://", 8) == 0 || + VSIStatExL( osXSDFilename, &sXSDStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 ) + { + bHasFoundXSD = TRUE; + } + } + + /* If not found, try if there is a schema in the gml_registry.xml */ + /* that might match a declared namespace and featuretype */ + if( !bHasFoundXSD ) + { + GMLRegistry oRegistry(CSLFetchNameValueDef( + poOpenInfo->papszOpenOptions, "REGISTRY", + CPLGetConfigOption("GML_REGISTRY", ""))); + if( oRegistry.Parse() ) + { + CPLString osHeader(szHeader); + for( size_t iNS = 0; iNS < oRegistry.aoNamespaces.size(); iNS ++ ) + { + GMLRegistryNamespace& oNamespace = oRegistry.aoNamespaces[iNS]; + const char* pszNSToFind = + CPLSPrintf("xmlns:%s", oNamespace.osPrefix.c_str()); + const char* pszURIToFind = + CPLSPrintf("\"%s\"", oNamespace.osURI.c_str()); + /* Case sensitive comparison since below test that also */ + /* uses the namespace prefix is case sensitive */ + if( osHeader.find(pszNSToFind) != std::string::npos && + strstr(szHeader, pszURIToFind) != NULL ) + { + if( oNamespace.bUseGlobalSRSName ) + bUseGlobalSRSName = TRUE; + + for( size_t iTypename = 0; + iTypename < oNamespace.aoFeatureTypes.size(); + iTypename ++ ) + { + const char* pszElementToFind = NULL; + + GMLRegistryFeatureType& oFeatureType = + oNamespace.aoFeatureTypes[iTypename]; + + if ( oFeatureType.osElementValue.size() ) + pszElementToFind = CPLSPrintf("%s:%s>%s", + oNamespace.osPrefix.c_str(), + oFeatureType.osElementName.c_str(), + oFeatureType.osElementValue.c_str()); + else + pszElementToFind = CPLSPrintf("%s:%s", + oNamespace.osPrefix.c_str(), + oFeatureType.osElementName.c_str()); + + /* Case sensitive test since in a CadastralParcel feature */ + /* there is a property basicPropertyUnit xlink, not to be */ + /* confused with a top-level BasicPropertyUnit feature... */ + if( osHeader.find(pszElementToFind) != std::string::npos ) + { + if( oFeatureType.osSchemaLocation.size() ) + { + osXSDFilename = oFeatureType.osSchemaLocation; + if( strncmp(osXSDFilename, "http://", 7) == 0 || + strncmp(osXSDFilename, "https://", 8) == 0 || + VSIStatExL( osXSDFilename, &sXSDStatBuf, + VSI_STAT_EXISTS_FLAG ) == 0 ) + { + bHasFoundXSD = TRUE; + bHaveSchema = TRUE; + CPLDebug("GML", "Found %s for %s:%s in registry", + osXSDFilename.c_str(), + oNamespace.osPrefix.c_str(), + oFeatureType.osElementName.c_str()); + } + else + { + CPLDebug("GML", "Cannot open %s", osXSDFilename.c_str()); + } + } + else + { + bHaveSchema = poReader->LoadClasses( + oFeatureType.osGFSSchemaLocation ); + if( bHaveSchema ) + { + CPLDebug("GML", "Found %s for %s:%s in registry", + oFeatureType.osGFSSchemaLocation.c_str(), + oNamespace.osPrefix.c_str(), + oFeatureType.osElementName.c_str()); + } + } + break; + } + } + break; + } + } + } + } + + /* For WFS, try to fetch the application schema */ + if( bIsWFS && !bHaveSchema && pszSchemaLocation != NULL && + (pszSchemaLocation[0] == '\'' || pszSchemaLocation[0] == '"') && + strchr(pszSchemaLocation + 1, pszSchemaLocation[0]) != NULL ) + { + char* pszSchemaLocationTmp1 = CPLStrdup(pszSchemaLocation + 1); + int nTruncLen = (int)(strchr(pszSchemaLocation + 1, pszSchemaLocation[0]) - (pszSchemaLocation + 1)); + pszSchemaLocationTmp1[nTruncLen] = '\0'; + char* pszSchemaLocationTmp2 = CPLUnescapeString( + pszSchemaLocationTmp1, NULL, CPLES_XML); + CPLString osEscaped = ReplaceSpaceByPct20IfNeeded(pszSchemaLocationTmp2); + CPLFree(pszSchemaLocationTmp2); + pszSchemaLocationTmp2 = CPLStrdup(osEscaped); + if (pszSchemaLocationTmp2) + { + /* pszSchemaLocationTmp2 is of the form : */ + /* http://namespace1 http://namespace1_schema_location http://namespace2 http://namespace1_schema_location2 */ + /* So we try to find http://namespace1_schema_location that contains hints that it is the WFS application */ + /* schema, i.e. if it contains typename= and request=DescribeFeatureType */ + char** papszTokens = CSLTokenizeString2(pszSchemaLocationTmp2, " \r\n", 0); + int nTokens = CSLCount(papszTokens); + if ((nTokens % 2) == 0) + { + for(int i=0;ipapszOpenOptions, + "DOWNLOAD_SCHEMA", + CSLTestBoolean(CPLGetConfigOption("GML_DOWNLOAD_WFS_SCHEMA", "YES"))) ) + { + CPLHTTPResult* psResult = CPLHTTPFetch(pszEscapedURL, NULL); + if (psResult) + { + if (psResult->nStatus == 0 && psResult->pabyData != NULL) + { + bHasFoundXSD = TRUE; + osXSDFilename = + CPLSPrintf("/vsimem/tmp_gml_xsd_%p.xsd", this); + VSILFILE* fpMem = VSIFileFromMemBuffer( + osXSDFilename, psResult->pabyData, + psResult->nDataLen, TRUE); + VSIFCloseL(fpMem); + psResult->pabyData = NULL; + } + CPLHTTPDestroyResult(psResult); + } + } + break; + } + } + } + CSLDestroy(papszTokens); + } + CPLFree(pszSchemaLocationTmp2); + CPLFree(pszSchemaLocationTmp1); + } + + int bHasFeatureProperties = FALSE; + if( bHasFoundXSD ) + { + std::vector aosClasses; + int bFullyUnderstood = FALSE; + bHaveSchema = GMLParseXSD( osXSDFilename, aosClasses, bFullyUnderstood ); + + if( bHaveSchema && !bFullyUnderstood && bIsWFSJointLayer ) + { + CPLDebug("GML", "Schema found, but only partially understood. Cannot be used in a WFS join context"); + + std::vector::const_iterator iter = aosClasses.begin(); + std::vector::const_iterator eiter = aosClasses.end(); + while (iter != eiter) + { + GMLFeatureClass* poClass = *iter; + + delete poClass; + iter ++; + } + aosClasses.resize(0); + bHaveSchema = FALSE; + } + + if( bHaveSchema ) + { + CPLDebug("GML", "Using %s", osXSDFilename.c_str()); + std::vector::const_iterator iter = aosClasses.begin(); + std::vector::const_iterator eiter = aosClasses.end(); + while (iter != eiter) + { + GMLFeatureClass* poClass = *iter; + + if( poClass->HasFeatureProperties() ) + { + bHasFeatureProperties = TRUE; + break; + } + iter ++; + } + + iter = aosClasses.begin(); + while (iter != eiter) + { + GMLFeatureClass* poClass = *iter; + iter ++; + + /* We have no way of knowing if the geometry type is 25D */ + /* when examining the xsd only, so if there was a hint */ + /* it is, we force to 25D */ + if (bHas3D && poClass->GetGeometryPropertyCount() == 1) + { + poClass->GetGeometryProperty(0)->SetType( + wkbSetZ((OGRwkbGeometryType)poClass->GetGeometryProperty(0)->GetType())); + } + + int bAddClass = TRUE; + /* If typenames are declared, only register the matching classes, in case */ + /* the XSD contains more layers, but not if feature classes contain */ + /* feature properties, in which case we will have embedded features that */ + /* will be reported as top-level features */ + if( papszTypeNames != NULL && !bHasFeatureProperties ) + { + bAddClass = FALSE; + char** papszIter = papszTypeNames; + while (*papszIter && !bAddClass) + { + const char* pszTypeName = *papszIter; + if (strcmp(pszTypeName, poClass->GetName()) == 0) + bAddClass = TRUE; + papszIter ++; + } + + /* Retry by removing prefixes */ + if (!bAddClass) + { + papszIter = papszTypeNames; + while (*papszIter && !bAddClass) + { + const char* pszTypeName = *papszIter; + const char* pszColon = strchr(pszTypeName, ':'); + if (pszColon) + { + pszTypeName = pszColon + 1; + if (strcmp(pszTypeName, poClass->GetName()) == 0) + { + poClass->SetName(pszTypeName); + bAddClass = TRUE; + } + } + papszIter ++; + } + } + + } + + if (bAddClass) + poReader->AddClass( poClass ); + else + delete poClass; + } + + poReader->SetClassListLocked( TRUE ); + } + } + + if (bHaveSchema && bIsWFS) + { + if( bIsWFSJointLayer ) + { + BuildJointClassFromXSD(); + } + + /* For WFS, we can assume sequential layers */ + if (poReader->GetClassCount() > 1 && pszReadMode == NULL && + !bHasFeatureProperties) + { + CPLDebug("GML", "WFS output. Using SEQUENTIAL_LAYERS read mode"); + eReadMode = SEQUENTIAL_LAYERS; + } + /* Sometimes the returned schema contains only that we don't resolve */ + /* so ignore it */ + else if (poReader->GetClassCount() == 0) + bHaveSchema = FALSE; + } + + CSLDestroy(papszTypeNames); + } + +/* -------------------------------------------------------------------- */ +/* Force a first pass to establish the schema. Eventually we */ +/* will have mechanisms for remembering the schema and related */ +/* information. */ +/* -------------------------------------------------------------------- */ + if( !bHaveSchema || + CSLFetchBoolean(poOpenInfo->papszOpenOptions, "FORCE_SRS_DETECTION", FALSE) ) + { + int bOnlyDetectSRS = bHaveSchema; + if( !poReader->PrescanForSchema( TRUE, bAnalyzeSRSPerFeature, + bOnlyDetectSRS ) ) + { + // we assume an errors have been reported. + return FALSE; + } + if( !bHaveSchema ) + { + if( bIsWFSJointLayer && poReader->GetClassCount() == 1 ) + { + BuildJointClassFromScannedSchema(); + } + + if( bHasFoundXSD ) + { + CPLDebug("GML", "Generating %s file, ignoring %s", + osGFSFilename.c_str(), osXSDFilename.c_str()); + } + } + } + + if (poReader->GetClassCount() > 1 && poReader->IsSequentialLayers() && + pszReadMode == NULL) + { + CPLDebug("GML", "Layers are monoblock. Using SEQUENTIAL_LAYERS read mode"); + eReadMode = SEQUENTIAL_LAYERS; + } + +/* -------------------------------------------------------------------- */ +/* Save the schema file if possible. Don't make a fuss if we */ +/* can't ... could be read-only directory or something. */ +/* -------------------------------------------------------------------- */ + if( !bHaveSchema && !poReader->HasStoppedParsing() && + !EQUALN(pszFilename, "/vsitar/", strlen("/vsitar/")) && + !EQUALN(pszFilename, "/vsizip/", strlen("/vsizip/")) && + !EQUALN(pszFilename, "/vsigzip/vsi", strlen("/vsigzip/vsi")) && + !EQUALN(pszFilename, "/vsigzip//vsi", strlen("/vsigzip//vsi")) && + !EQUALN(pszFilename, "/vsicurl/", strlen("/vsicurl/")) && + !EQUALN(pszFilename, "/vsicurl_streaming/", strlen("/vsicurl_streaming/"))) + { + VSILFILE *fp = NULL; + + VSIStatBufL sGFSStatBuf; + if( VSIStatExL( osGFSFilename, &sGFSStatBuf, VSI_STAT_EXISTS_FLAG ) != 0 + && (fp = VSIFOpenL( osGFSFilename, "wt" )) != NULL ) + { + VSIFCloseL( fp ); + poReader->SaveClasses( osGFSFilename ); + } + else + { + CPLDebug("GML", + "Not saving %s files already exists or can't be created.", + osGFSFilename.c_str() ); + } + } + +/* -------------------------------------------------------------------- */ +/* Translate the GMLFeatureClasses into layers. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRGMLLayer **) + CPLCalloc( sizeof(OGRGMLLayer *), poReader->GetClassCount()); + nLayers = 0; + + if (poReader->GetClassCount() == 1 && nNumberOfFeatures != 0) + { + GMLFeatureClass *poClass = poReader->GetClass(0); + GIntBig nFeatureCount = poClass->GetFeatureCount(); + if (nFeatureCount < 0) + { + poClass->SetFeatureCount(nNumberOfFeatures); + } + else if (nFeatureCount != nNumberOfFeatures) + { + CPLDebug("GML", "Feature count in header, and actual feature count don't match"); + } + } + + if (bIsWFS && poReader->GetClassCount() == 1) + bUseGlobalSRSName = TRUE; + + while( nLayers < poReader->GetClassCount() ) + { + papoLayers[nLayers] = TranslateGMLSchema(poReader->GetClass(nLayers)); + nLayers++; + } + + + + return TRUE; +} + +/************************************************************************/ +/* BuildJointClassFromXSD() */ +/************************************************************************/ + +void OGRGMLDataSource::BuildJointClassFromXSD() +{ + CPLString osJointClassName = "join"; + for(int i=0;iGetClassCount();i++) + { + osJointClassName += "_"; + osJointClassName += poReader->GetClass(i)->GetName(); + } + GMLFeatureClass* poJointClass = new GMLFeatureClass(osJointClassName); + poJointClass->SetElementName("Tuple"); + for(int i=0;iGetClassCount();i++) + { + GMLFeatureClass* poClass = poReader->GetClass(i); + + CPLString osPropertyName; + osPropertyName.Printf("%s.%s", poClass->GetName(), "gml_id"); + GMLPropertyDefn* poNewProperty = new GMLPropertyDefn( osPropertyName ); + CPLString osSrcElement; + osSrcElement.Printf("member|%s@id", + poClass->GetName()); + poNewProperty->SetSrcElement(osSrcElement); + poNewProperty->SetType(GMLPT_String); + poJointClass->AddProperty(poNewProperty); + + int iField; + for( iField = 0; iField < poClass->GetPropertyCount(); iField++ ) + { + GMLPropertyDefn *poProperty = poClass->GetProperty( iField ); + CPLString osPropertyName; + osPropertyName.Printf("%s.%s", poClass->GetName(), poProperty->GetName()); + GMLPropertyDefn* poNewProperty = new GMLPropertyDefn( osPropertyName ); + + poNewProperty->SetType(poProperty->GetType()); + CPLString osSrcElement; + osSrcElement.Printf("member|%s|%s", + poClass->GetName(), + poProperty->GetSrcElement()); + poNewProperty->SetSrcElement(osSrcElement); + poNewProperty->SetWidth(poProperty->GetWidth()); + poNewProperty->SetPrecision(poProperty->GetPrecision()); + poNewProperty->SetNullable(poProperty->IsNullable()); + + poJointClass->AddProperty(poNewProperty); + } + for( iField = 0; iField < poClass->GetGeometryPropertyCount(); iField++ ) + { + GMLGeometryPropertyDefn *poProperty = poClass->GetGeometryProperty( iField ); + CPLString osPropertyName; + osPropertyName.Printf("%s.%s", poClass->GetName(), poProperty->GetName()); + CPLString osSrcElement; + osSrcElement.Printf("member|%s|%s", + poClass->GetName(), + poProperty->GetSrcElement()); + GMLGeometryPropertyDefn* poNewProperty = + new GMLGeometryPropertyDefn( osPropertyName, osSrcElement, + poProperty->GetType(), -1, poProperty->IsNullable() ); + poJointClass->AddGeometryProperty(poNewProperty); + } + } + poJointClass->SetSchemaLocked(TRUE); + + poReader->ClearClasses(); + poReader->AddClass( poJointClass ); +} + +/************************************************************************/ +/* BuildJointClassFromScannedSchema() */ +/************************************************************************/ + +void OGRGMLDataSource::BuildJointClassFromScannedSchema() +{ + /* Make sure that all properties of a same base feature type are */ + /* consecutive. If not, reorder */ + std::vector< std::vector > aapoProps; + GMLFeatureClass *poClass = poReader->GetClass(0); + CPLString osJointClassName = "join"; + + int iField, iSubClass; + for( iField = 0; iField < poClass->GetPropertyCount(); iField ++ ) + { + GMLPropertyDefn* poProp = poClass->GetProperty(iField); + CPLString osPrefix(poProp->GetName()); + size_t iPos = osPrefix.find('.'); + if( iPos != std::string::npos ) + osPrefix.resize(iPos); + for( iSubClass = 0; iSubClass < (int)aapoProps.size(); iSubClass ++ ) + { + CPLString osPrefixClass(aapoProps[iSubClass][0]->GetName()); + size_t iPos = osPrefixClass.find('.'); + if( iPos != std::string::npos ) + osPrefixClass.resize(iPos); + if( osPrefix == osPrefixClass ) + break; + } + if( iSubClass == (int)aapoProps.size() ) + { + osJointClassName += "_"; + osJointClassName += osPrefix; + aapoProps.push_back( std::vector() ); + } + aapoProps[iSubClass].push_back(poProp); + } + poClass->SetElementName(poClass->GetName()); + poClass->SetName(osJointClassName); + + poClass->StealProperties(); + std::vector< std::pair< CPLString, std::vector > > aapoGeomProps; + for( iSubClass = 0; iSubClass < (int)aapoProps.size(); iSubClass ++ ) + { + CPLString osPrefixClass(aapoProps[iSubClass][0]->GetName()); + size_t iPos = osPrefixClass.find('.'); + if( iPos != std::string::npos ) + osPrefixClass.resize(iPos); + aapoGeomProps.push_back( std::pair< CPLString, std::vector > + (osPrefixClass, std::vector()) ); + for( int iField = 0; iField < (int)aapoProps[iSubClass].size(); iField ++ ) + { + poClass->AddProperty(aapoProps[iSubClass][iField]); + } + } + aapoProps.resize(0); + + // Reorder geometry fields too + for( iField = 0; iField < poClass->GetGeometryPropertyCount(); iField ++ ) + { + GMLGeometryPropertyDefn* poProp = poClass->GetGeometryProperty(iField); + CPLString osPrefix(poProp->GetName()); + size_t iPos = osPrefix.find('.'); + if( iPos != std::string::npos ) + osPrefix.resize(iPos); + int iSubClass; + for( iSubClass = 0; iSubClass < (int)aapoGeomProps.size(); iSubClass ++ ) + { + if( osPrefix == aapoGeomProps[iSubClass].first ) + break; + } + if( iSubClass == (int)aapoProps.size() ) + aapoGeomProps.push_back( std::pair< CPLString, std::vector > + (osPrefix, std::vector()) ); + aapoGeomProps[iSubClass].second.push_back(poProp); + } + poClass->StealGeometryProperties(); + for( iSubClass = 0; iSubClass < (int)aapoGeomProps.size(); iSubClass ++ ) + { + for( iField = 0; iField < (int)aapoGeomProps[iSubClass].second.size(); iField ++ ) + { + poClass->AddGeometryProperty(aapoGeomProps[iSubClass].second[iField]); + } + } +} + +/************************************************************************/ +/* TranslateGMLSchema() */ +/************************************************************************/ + +OGRGMLLayer *OGRGMLDataSource::TranslateGMLSchema( GMLFeatureClass *poClass ) + +{ + OGRGMLLayer *poLayer; + +/* -------------------------------------------------------------------- */ +/* Create an empty layer. */ +/* -------------------------------------------------------------------- */ + + const char* pszSRSName = poClass->GetSRSName(); + OGRSpatialReference* poSRS = NULL; + if (pszSRSName) + { + poSRS = new OGRSpatialReference(); + if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE) + { + delete poSRS; + poSRS = NULL; + } + } + else + { + pszSRSName = GetGlobalSRSName(); + if (pszSRSName) + { + poSRS = new OGRSpatialReference(); + if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE) + { + delete poSRS; + poSRS = NULL; + } + + if (poSRS != NULL && m_bInvertAxisOrderIfLatLong && + GML_IsSRSLatLongOrder(pszSRSName)) + { + OGR_SRSNode *poGEOGCS = poSRS->GetAttrNode( "GEOGCS" ); + if( poGEOGCS != NULL ) + poGEOGCS->StripNodes( "AXIS" ); + + OGR_SRSNode *poPROJCS = poSRS->GetAttrNode( "PROJCS" ); + if (poPROJCS != NULL && poSRS->EPSGTreatsAsNorthingEasting()) + poPROJCS->StripNodes( "AXIS" ); + + if (!poClass->HasExtents() && + sBoundingRect.IsInit()) + { + poClass->SetExtents(sBoundingRect.MinY, + sBoundingRect.MaxY, + sBoundingRect.MinX, + sBoundingRect.MaxX); + } + } + } + + if (!poClass->HasExtents() && + sBoundingRect.IsInit()) + { + poClass->SetExtents(sBoundingRect.MinX, + sBoundingRect.MaxX, + sBoundingRect.MinY, + sBoundingRect.MaxY); + } + } + + /* Report a COMPD_CS only if GML_REPORT_COMPD_CS is explicitly set to TRUE */ + if( poSRS != NULL && + !CSLTestBoolean(CPLGetConfigOption("GML_REPORT_COMPD_CS", "FALSE")) ) + { + OGR_SRSNode *poCOMPD_CS = poSRS->GetAttrNode( "COMPD_CS" ); + if( poCOMPD_CS != NULL ) + { + OGR_SRSNode* poCandidateRoot = poCOMPD_CS->GetNode( "PROJCS" ); + if( poCandidateRoot == NULL ) + poCandidateRoot = poCOMPD_CS->GetNode( "GEOGCS" ); + if( poCandidateRoot != NULL ) + { + poSRS->SetRoot(poCandidateRoot->Clone()); + } + } + } + + + poLayer = new OGRGMLLayer( poClass->GetName(), FALSE, this ); + +/* -------------------------------------------------------------------- */ +/* Added attributes (properties). */ +/* -------------------------------------------------------------------- */ + if (bExposeGMLId) + { + OGRFieldDefn oField( "gml_id", OFTString ); + oField.SetNullable(FALSE); + poLayer->GetLayerDefn()->AddFieldDefn( &oField ); + } + else if (bExposeFid) + { + OGRFieldDefn oField( "fid", OFTString ); + oField.SetNullable(FALSE); + poLayer->GetLayerDefn()->AddFieldDefn( &oField ); + } + + int iField; + for( iField = 0; iField < poClass->GetGeometryPropertyCount(); iField++ ) + { + GMLGeometryPropertyDefn *poProperty = poClass->GetGeometryProperty( iField ); + OGRGeomFieldDefn oField( poProperty->GetName(), (OGRwkbGeometryType)poProperty->GetType() ); + if( poClass->GetGeometryPropertyCount() == 1 && poClass->GetFeatureCount() == 0 ) + { + oField.SetType(wkbUnknown); + } + oField.SetSpatialRef(poSRS); + oField.SetNullable(poProperty->IsNullable() ); + poLayer->GetLayerDefn()->AddGeomFieldDefn( &oField ); + } + + for( iField = 0; iField < poClass->GetPropertyCount(); iField++ ) + { + GMLPropertyDefn *poProperty = poClass->GetProperty( iField ); + OGRFieldType eFType; + + if( poProperty->GetType() == GMLPT_Untyped ) + eFType = OFTString; + else if( poProperty->GetType() == GMLPT_String ) + eFType = OFTString; + else if( poProperty->GetType() == GMLPT_Integer || + poProperty->GetType() == GMLPT_Boolean || + poProperty->GetType() == GMLPT_Short ) + eFType = OFTInteger; + else if( poProperty->GetType() == GMLPT_Integer64 ) + eFType = OFTInteger64; + else if( poProperty->GetType() == GMLPT_Real || + poProperty->GetType() == GMLPT_Float ) + eFType = OFTReal; + else if( poProperty->GetType() == GMLPT_StringList ) + eFType = OFTStringList; + else if( poProperty->GetType() == GMLPT_IntegerList || + poProperty->GetType() == GMLPT_BooleanList ) + eFType = OFTIntegerList; + else if( poProperty->GetType() == GMLPT_Integer64List ) + eFType = OFTInteger64List; + else if( poProperty->GetType() == GMLPT_RealList ) + eFType = OFTRealList; + else if( poProperty->GetType() == GMLPT_FeaturePropertyList ) + eFType = OFTStringList; + else + eFType = OFTString; + + OGRFieldDefn oField( poProperty->GetName(), eFType ); + if ( EQUALN(oField.GetNameRef(), "ogr:", 4) ) + oField.SetName(poProperty->GetName()+4); + if( poProperty->GetWidth() > 0 ) + oField.SetWidth( poProperty->GetWidth() ); + if( poProperty->GetPrecision() > 0 ) + oField.SetPrecision( poProperty->GetPrecision() ); + if( poProperty->GetType() == GMLPT_Boolean || + poProperty->GetType() == GMLPT_BooleanList ) + oField.SetSubType(OFSTBoolean); + else if( poProperty->GetType() == GMLPT_Short) + oField.SetSubType(OFSTInt16); + else if( poProperty->GetType() == GMLPT_Float) + oField.SetSubType(OFSTFloat32); + if( !bEmptyAsNull ) + oField.SetNullable(poProperty->IsNullable() ); + + poLayer->GetLayerDefn()->AddFieldDefn( &oField ); + } + + if( poSRS != NULL ) + poSRS->Release(); + + return poLayer; +} + +/************************************************************************/ +/* GetGlobalSRSName() */ +/************************************************************************/ + +const char *OGRGMLDataSource::GetGlobalSRSName() +{ + if (poReader->CanUseGlobalSRSName() || bUseGlobalSRSName) + return poReader->GetGlobalSRSName(); + else + return NULL; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRGMLDataSource::Create( const char *pszFilename, + char **papszOptions ) + +{ + if( fpOutput != NULL || poReader != NULL ) + { + CPLAssert( FALSE ); + return FALSE; + } + + if( strcmp(pszFilename,"/dev/stdout") == 0 ) + pszFilename = "/vsistdout/"; + +/* -------------------------------------------------------------------- */ +/* Read options */ +/* -------------------------------------------------------------------- */ + + CSLDestroy(papszCreateOptions); + papszCreateOptions = CSLDuplicate(papszOptions); + + const char* pszFormat = CSLFetchNameValue(papszCreateOptions, "FORMAT"); + bIsOutputGML3 = pszFormat && EQUAL(pszFormat, "GML3"); + bIsOutputGML3Deegree = pszFormat && EQUAL(pszFormat, "GML3Deegree"); + bIsOutputGML32 = pszFormat && EQUAL(pszFormat, "GML3.2"); + if (bIsOutputGML3Deegree || bIsOutputGML32) + bIsOutputGML3 = TRUE; + + bIsLongSRSRequired = + CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "GML3_LONGSRS", "YES")); + + bWriteSpaceIndentation = + CSLTestBoolean(CSLFetchNameValueDef(papszCreateOptions, "SPACE_INDENTATION", "YES")); + +/* -------------------------------------------------------------------- */ +/* Create the output file. */ +/* -------------------------------------------------------------------- */ + pszName = CPLStrdup( pszFilename ); + osFilename = pszName; + + if( strcmp(pszFilename,"/vsistdout/") == 0 || + strncmp(pszFilename,"/vsigzip/", 9) == 0 ) + { + fpOutput = VSIFOpenL(pszFilename, "wb"); + bFpOutputIsNonSeekable = TRUE; + bFpOutputSingleFile = TRUE; + } + else if ( strncmp(pszFilename,"/vsizip/", 8) == 0) + { + if (EQUAL(CPLGetExtension(pszFilename), "zip")) + { + CPLFree(pszName); + pszName = CPLStrdup(CPLFormFilename(pszFilename, "out.gml", NULL)); + } + + fpOutput = VSIFOpenL(pszName, "wb"); + bFpOutputIsNonSeekable = TRUE; + } + else + fpOutput = VSIFOpenL( pszFilename, "wb+" ); + if( fpOutput == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create GML file %s.", + pszFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Write out "standard" header. */ +/* -------------------------------------------------------------------- */ + PrintLine( fpOutput, "%s", + "" ); + + if (!bFpOutputIsNonSeekable) + nSchemaInsertLocation = (int) VSIFTellL( fpOutput ); + + const char* pszPrefix = GetAppPrefix(); + const char* pszTargetNameSpace = CSLFetchNameValueDef(papszOptions,"TARGET_NAMESPACE", "http://ogr.maptools.org/"); + + if( RemoveAppPrefix() ) + PrintLine( fpOutput, "" ); + else + PrintLine( fpOutput, "%s", + " xmlns:gml=\"http://www.opengis.net/gml\">" ); + + return TRUE; +} + + +/************************************************************************/ +/* WriteTopElements() */ +/************************************************************************/ + +void OGRGMLDataSource::WriteTopElements() +{ + const char* pszDescription = CSLFetchNameValueDef(papszCreateOptions, + "DESCRIPTION", GetMetadataItem("DESCRIPTION")); + if( pszDescription != NULL ) + { + if (bWriteSpaceIndentation) + VSIFPrintfL( fpOutput, " "); + char* pszTmp = CPLEscapeString(pszDescription, -1, CPLES_XML); + PrintLine( fpOutput, "%s", pszTmp ); + CPLFree(pszTmp); + } + + const char* pszName = CSLFetchNameValueDef(papszCreateOptions, + "NAME", GetMetadataItem("NAME")); + if( pszName != NULL ) + { + if (bWriteSpaceIndentation) + VSIFPrintfL( fpOutput, " "); + char* pszTmp = CPLEscapeString(pszName, -1, CPLES_XML); + PrintLine( fpOutput, "%s", pszTmp ); + CPLFree(pszTmp); + } + +/* -------------------------------------------------------------------- */ +/* Should we initialize an area to place the boundedBy element? */ +/* We will need to seek back to fill it in. */ +/* -------------------------------------------------------------------- */ + nBoundedByLocation = -1; + if( CSLFetchBoolean( papszCreateOptions , "BOUNDEDBY", TRUE )) + { + if (!bFpOutputIsNonSeekable ) + { + nBoundedByLocation = (int) VSIFTellL( fpOutput ); + + if( nBoundedByLocation != -1 ) + PrintLine( fpOutput, "%350s", "" ); + } + else + { + if (bWriteSpaceIndentation) + VSIFPrintfL( fpOutput, " "); + if (IsGML3Output()) + PrintLine( fpOutput, "" ); + else + PrintLine( fpOutput, "missing" ); + } + } +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRGMLDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + CPL_UNUSED char ** papszOptions ) +{ +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( fpOutput == NULL ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened for read access.\n" + "New layer %s cannot be created.\n", + pszName, pszLayerName ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Ensure name is safe as an element name. */ +/* -------------------------------------------------------------------- */ + char *pszCleanLayerName = CPLStrdup( pszLayerName ); + + CPLCleanXMLElementName( pszCleanLayerName ); + if( strcmp(pszCleanLayerName,pszLayerName) != 0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Layer name '%s' adjusted to '%s' for XML validity.", + pszLayerName, pszCleanLayerName ); + } + +/* -------------------------------------------------------------------- */ +/* Set or check validity of global SRS. */ +/* -------------------------------------------------------------------- */ + if (nLayers == 0) + { + WriteTopElements(); + if (poSRS) + poWriteGlobalSRS = poSRS->Clone(); + bWriteGlobalSRS = TRUE; + } + else if( bWriteGlobalSRS ) + { + if( poWriteGlobalSRS != NULL ) + { + if (poSRS == NULL || !poSRS->IsSame(poWriteGlobalSRS)) + { + delete poWriteGlobalSRS; + poWriteGlobalSRS = NULL; + bWriteGlobalSRS = FALSE; + } + } + else + { + if( poSRS != NULL ) + bWriteGlobalSRS = FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRGMLLayer *poLayer; + + poLayer = new OGRGMLLayer( pszCleanLayerName, TRUE, this ); + poLayer->GetLayerDefn()->SetGeomType(eType); + if( eType != wkbNone ) + { + poLayer->GetLayerDefn()->GetGeomFieldDefn(0)->SetName("geometryProperty"); + if( poSRS != NULL ) + { + /* Clone it since mapogroutput assumes that it can destroys */ + /* the SRS it has passed to use, instead of deferencing it */ + poSRS = poSRS->Clone(); + poLayer->GetLayerDefn()->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + poSRS->Dereference(); + } + } + + CPLFree( pszCleanLayerName ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRGMLLayer **) + CPLRealloc( papoLayers, sizeof(OGRGMLLayer *) * (nLayers+1) ); + + papoLayers[nLayers++] = poLayer; + + return poLayer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGMLDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCCreateGeomFieldAfterCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCCurveGeometries) ) + return bIsOutputGML3; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRGMLDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GrowExtents() */ +/************************************************************************/ + +void OGRGMLDataSource::GrowExtents( OGREnvelope3D *psGeomBounds, int nCoordDimension ) + +{ + sBoundingRect.Merge( *psGeomBounds ); + if (nCoordDimension == 3) + bBBOX3D = TRUE; +} + +/************************************************************************/ +/* InsertHeader() */ +/* */ +/* This method is used to update boundedby info for a */ +/* dataset, and insert schema descriptions depending on */ +/* selection options in effect. */ +/************************************************************************/ + +void OGRGMLDataSource::InsertHeader() + +{ + VSILFILE *fpSchema; + int nSchemaStart = 0; + + if( bFpOutputSingleFile ) + return; + +/* -------------------------------------------------------------------- */ +/* Do we want to write the schema within the GML instance doc */ +/* or to a separate file? For now we only support external. */ +/* -------------------------------------------------------------------- */ + const char *pszSchemaURI = CSLFetchNameValue(papszCreateOptions, + "XSISCHEMAURI"); + const char *pszSchemaOpt = CSLFetchNameValue( papszCreateOptions, + "XSISCHEMA" ); + + if( pszSchemaURI != NULL ) + return; + + if( pszSchemaOpt == NULL || EQUAL(pszSchemaOpt,"EXTERNAL") ) + { + const char *pszXSDFilename = CPLResetExtension( pszName, "xsd" ); + + fpSchema = VSIFOpenL( pszXSDFilename, "wt" ); + if( fpSchema == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open file %.500s for schema output.", + pszXSDFilename ); + return; + } + PrintLine( fpSchema, "" ); + } + else if( EQUAL(pszSchemaOpt,"INTERNAL") ) + { + if (fpOutput == NULL) + return; + nSchemaStart = (int) VSIFTellL( fpOutput ); + fpSchema = fpOutput; + } + else + return; + +/* ==================================================================== */ +/* Write the schema section at the end of the file. Once */ +/* complete, we will read it back in, and then move the whole */ +/* file "down" enough to insert the schema at the beginning. */ +/* ==================================================================== */ + +/* ==================================================================== */ +/* Detect if there are fields of List types. */ +/* ==================================================================== */ + int iLayer; + int bHasListFields = FALSE; + + for( iLayer = 0; !bHasListFields && iLayer < GetLayerCount(); iLayer++ ) + { + OGRFeatureDefn *poFDefn = GetLayer(iLayer)->GetLayerDefn(); + for( int iField = 0; !bHasListFields && iField < poFDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(iField); + + if( poFieldDefn->GetType() == OFTIntegerList || + poFieldDefn->GetType() == OFTInteger64List || + poFieldDefn->GetType() == OFTRealList || + poFieldDefn->GetType() == OFTStringList ) + { + bHasListFields = TRUE; + } + } /* next field */ + } /* next layer */ + +/* -------------------------------------------------------------------- */ +/* Emit the start of the schema section. */ +/* -------------------------------------------------------------------- */ + const char* pszPrefix = GetAppPrefix(); + if( pszPrefix[0] == '\0' ) + pszPrefix = "ogr"; + const char* pszTargetNameSpace = CSLFetchNameValueDef(papszCreateOptions,"TARGET_NAMESPACE", "http://ogr.maptools.org/"); + + if (IsGML3Output()) + { + PrintLine( fpSchema, + ""); + + if (IsGML32Output()) + { + PrintLine( fpSchema, + ""); + PrintLine( fpSchema, + " "); + PrintLine( fpSchema, + " %d", (bHasListFields) ? 1 : 0); + PrintLine( fpSchema, + " "); + PrintLine( fpSchema, + ""); + + PrintLine( fpSchema, + "" ); + PrintLine( fpSchema, + "" ); + } + else + { + if (!IsGML3DeegreeOutput()) + { + PrintLine( fpSchema, + ""); + PrintLine( fpSchema, + " "); + PrintLine( fpSchema, + " %d", (bHasListFields) ? 1 : 0); + PrintLine( fpSchema, + " http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd"); + PrintLine( fpSchema, + " "); + PrintLine( fpSchema, + ""); + } + + PrintLine( fpSchema, + "" ); + if (!IsGML3DeegreeOutput()) + { + PrintLine( fpSchema, + "" ); + } + } + } + else + { + PrintLine( fpSchema, + "", + pszTargetNameSpace, pszPrefix, pszTargetNameSpace ); + + PrintLine( fpSchema, + "" ); + } + +/* -------------------------------------------------------------------- */ +/* Define the FeatureCollection */ +/* -------------------------------------------------------------------- */ + if (IsGML3Output()) + { + if (IsGML32Output()) + { + PrintLine( fpSchema, + "", + pszPrefix ); + } + else if (IsGML3DeegreeOutput()) + { + PrintLine( fpSchema, + "", + pszPrefix ); + } + else + { + PrintLine( fpSchema, + "", + pszPrefix ); + } + + PrintLine( fpSchema, ""); + PrintLine( fpSchema, " " ); + if (IsGML3DeegreeOutput()) + { + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + } + else + { + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + } + PrintLine( fpSchema, " " ); + if (IsGML32Output()) + { + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + } + else + { + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + } + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, "" ); + } + else + { + PrintLine( fpSchema, + "", + pszPrefix ); + + PrintLine( fpSchema, ""); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, " " ); + PrintLine( fpSchema, "" ); + } + +/* ==================================================================== */ +/* Define the schema for each layer. */ +/* ==================================================================== */ + + for( iLayer = 0; iLayer < GetLayerCount(); iLayer++ ) + { + OGRFeatureDefn *poFDefn = GetLayer(iLayer)->GetLayerDefn(); + +/* -------------------------------------------------------------------- */ +/* Emit initial stuff for a feature type. */ +/* -------------------------------------------------------------------- */ + if (IsGML32Output()) + { + PrintLine( + fpSchema, + "", + poFDefn->GetName(), pszPrefix, poFDefn->GetName() ); + } + else + { + PrintLine( + fpSchema, + "", + poFDefn->GetName(), pszPrefix, poFDefn->GetName() ); + } + + PrintLine( fpSchema, "", poFDefn->GetName()); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + + for( int iGeomField = 0; iGeomField < poFDefn->GetGeomFieldCount(); iGeomField++ ) + { + OGRGeomFieldDefn *poFieldDefn = poFDefn->GetGeomFieldDefn(iGeomField); + + /* -------------------------------------------------------------------- */ + /* Define the geometry attribute. */ + /* -------------------------------------------------------------------- */ + const char* pszGeometryTypeName = "GeometryPropertyType"; + const char* pszComment = ""; + OGRwkbGeometryType eGType = wkbFlatten(poFieldDefn->GetType()); + switch(eGType) + { + case wkbPoint: + pszGeometryTypeName = "PointPropertyType"; + break; + case wkbLineString: + case wkbCircularString: + case wkbCompoundCurve: + if (IsGML3Output()) + { + if( eGType == wkbLineString ) + pszComment = " "; + else if( eGType == wkbCircularString ) + pszComment = " "; + else if( eGType == wkbCompoundCurve ) + pszComment = " "; + pszGeometryTypeName = "CurvePropertyType"; + } + else + pszGeometryTypeName = "LineStringPropertyType"; + break; + case wkbPolygon: + case wkbCurvePolygon: + if (IsGML3Output()) + { + if( eGType == wkbPolygon ) + pszComment = " "; + else if( eGType == wkbCurvePolygon ) + pszComment = " "; + pszGeometryTypeName = "SurfacePropertyType"; + } + else + pszGeometryTypeName = "PolygonPropertyType"; + break; + case wkbMultiPoint: + pszGeometryTypeName = "MultiPointPropertyType"; + break; + case wkbMultiLineString: + case wkbMultiCurve: + if (IsGML3Output()) + { + if( eGType == wkbMultiLineString ) + pszComment = " "; + else if( eGType == wkbMultiCurve ) + pszComment = " "; + pszGeometryTypeName = "MultiCurvePropertyType"; + } + else + pszGeometryTypeName = "MultiLineStringPropertyType"; + break; + case wkbMultiPolygon: + case wkbMultiSurface: + if (IsGML3Output()) + { + if( eGType == wkbMultiPolygon ) + pszComment = " "; + else if( eGType == wkbMultiSurface ) + pszComment = " "; + pszGeometryTypeName = "MultiSurfacePropertyType"; + } + else + pszGeometryTypeName = "MultiPolygonPropertyType"; + break; + case wkbGeometryCollection: + pszGeometryTypeName = "MultiGeometryPropertyType"; + break; + default: + break; + } + + int nMinOccurs = poFieldDefn->IsNullable() ? 0 : 1; + PrintLine( fpSchema, + " %s", + poFieldDefn->GetNameRef(), pszGeometryTypeName, nMinOccurs, pszComment ); + } + +/* -------------------------------------------------------------------- */ +/* Emit each of the attributes. */ +/* -------------------------------------------------------------------- */ + for( int iField = 0; iField < poFDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(iField); + + if( IsGML3Output() && strcmp(poFieldDefn->GetNameRef(), "gml_id") == 0 ) + continue; + else if( !IsGML3Output() && strcmp(poFieldDefn->GetNameRef(), "fid") == 0 ) + continue; + + int nMinOccurs = poFieldDefn->IsNullable() ? 0 : 1; + if( poFieldDefn->GetType() == OFTInteger || + poFieldDefn->GetType() == OFTIntegerList ) + { + int nWidth; + + if( poFieldDefn->GetWidth() > 0 ) + nWidth = poFieldDefn->GetWidth(); + else + nWidth = 16; + + PrintLine( fpSchema, " ", + poFieldDefn->GetNameRef(), + nMinOccurs, + poFieldDefn->GetType() == OFTIntegerList ? "unbounded": "1" ); + PrintLine( fpSchema, " "); + if( poFieldDefn->GetSubType() == OFSTBoolean ) + { + PrintLine( fpSchema, " "); + } + else if( poFieldDefn->GetSubType() == OFSTInt16 ) + { + PrintLine( fpSchema, " "); + } + else + { + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " ", nWidth); + } + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + } + else if( poFieldDefn->GetType() == OFTInteger64 || + poFieldDefn->GetType() == OFTInteger64List ) + { + int nWidth; + + if( poFieldDefn->GetWidth() > 0 ) + nWidth = poFieldDefn->GetWidth(); + else + nWidth = 16; + + PrintLine( fpSchema, " ", + poFieldDefn->GetNameRef(), + nMinOccurs, + poFieldDefn->GetType() == OFTInteger64List ? "unbounded": "1" ); + PrintLine( fpSchema, " "); + if( poFieldDefn->GetSubType() == OFSTBoolean ) + { + PrintLine( fpSchema, " "); + } + else if( poFieldDefn->GetSubType() == OFSTInt16 ) + { + PrintLine( fpSchema, " "); + } + else + { + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " ", nWidth); + } + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + } + else if( poFieldDefn->GetType() == OFTReal || + poFieldDefn->GetType() == OFTRealList ) + { + int nWidth, nDecimals; + + nWidth = poFieldDefn->GetWidth(); + nDecimals = poFieldDefn->GetPrecision(); + + PrintLine( fpSchema, " ", + poFieldDefn->GetNameRef(), + nMinOccurs, + poFieldDefn->GetType() == OFTRealList ? "unbounded": "1" ); + PrintLine( fpSchema, " "); + if( poFieldDefn->GetSubType() == OFSTFloat32 ) + PrintLine( fpSchema, " "); + else + PrintLine( fpSchema, " "); + if (nWidth > 0) + { + PrintLine( fpSchema, " ", nWidth); + PrintLine( fpSchema, " ", nDecimals); + } + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + } + else if( poFieldDefn->GetType() == OFTString || + poFieldDefn->GetType() == OFTStringList ) + { + PrintLine( fpSchema, " ", + poFieldDefn->GetNameRef(), + nMinOccurs, + poFieldDefn->GetType() == OFTStringList ? "unbounded": "1" ); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + if( poFieldDefn->GetWidth() != 0 ) + { + PrintLine( fpSchema, " ", poFieldDefn->GetWidth()); + } + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + } + else if( poFieldDefn->GetType() == OFTDate || poFieldDefn->GetType() == OFTDateTime ) + { + PrintLine( fpSchema, " ", + poFieldDefn->GetNameRef(), + nMinOccurs ); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + } + else + { + /* TODO */ + } + } /* next field */ + +/* -------------------------------------------------------------------- */ +/* Finish off feature type. */ +/* -------------------------------------------------------------------- */ + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, " "); + PrintLine( fpSchema, "" ); + } /* next layer */ + + PrintLine( fpSchema, "" ); + +/* ==================================================================== */ +/* Move schema to the start of the file. */ +/* ==================================================================== */ + if( fpSchema == fpOutput ) + { +/* -------------------------------------------------------------------- */ +/* Read the schema into memory. */ +/* -------------------------------------------------------------------- */ + int nSchemaSize = (int) VSIFTellL( fpOutput ) - nSchemaStart; + char *pszSchema = (char *) CPLMalloc(nSchemaSize+1); + + VSIFSeekL( fpOutput, nSchemaStart, SEEK_SET ); + + VSIFReadL( pszSchema, 1, nSchemaSize, fpOutput ); + pszSchema[nSchemaSize] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Move file data down by "schema size" bytes from after */ +/* header so we have room insert the schema. Move in pretty */ +/* big chunks. */ +/* -------------------------------------------------------------------- */ + int nChunkSize = MIN(nSchemaStart-nSchemaInsertLocation,250000); + char *pszChunk = (char *) CPLMalloc(nChunkSize); + int nEndOfUnmovedData = nSchemaStart; + + for( nEndOfUnmovedData = nSchemaStart; + nEndOfUnmovedData > nSchemaInsertLocation; ) + { + int nBytesToMove = + MIN(nChunkSize, nEndOfUnmovedData - nSchemaInsertLocation ); + + VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove, SEEK_SET ); + VSIFReadL( pszChunk, 1, nBytesToMove, fpOutput ); + VSIFSeekL( fpOutput, nEndOfUnmovedData - nBytesToMove + nSchemaSize, + SEEK_SET ); + VSIFWriteL( pszChunk, 1, nBytesToMove, fpOutput ); + + nEndOfUnmovedData -= nBytesToMove; + } + + CPLFree( pszChunk ); + +/* -------------------------------------------------------------------- */ +/* Write the schema in the opened slot. */ +/* -------------------------------------------------------------------- */ + VSIFSeekL( fpOutput, nSchemaInsertLocation, SEEK_SET ); + VSIFWriteL( pszSchema, 1, nSchemaSize, fpOutput ); + + VSIFSeekL( fpOutput, 0, SEEK_END ); + + nBoundedByLocation += nSchemaSize; + + CPLFree(pszSchema); + } +/* -------------------------------------------------------------------- */ +/* Close external schema files. */ +/* -------------------------------------------------------------------- */ + else + VSIFCloseL( fpSchema ); +} + + +/************************************************************************/ +/* PrintLine() */ +/************************************************************************/ + +void OGRGMLDataSource::PrintLine(VSILFILE* fp, const char *fmt, ...) +{ + CPLString osWork; + va_list args; + + va_start( args, fmt ); + osWork.vPrintf( fmt, args ); + va_end( args ); + +#ifdef WIN32 + const char* pszEOL = "\r\n"; +#else + const char* pszEOL = "\n"; +#endif + + VSIFPrintfL(fp, "%s%s", osWork.c_str(), pszEOL); +} + + +/************************************************************************/ +/* OGRGMLSingleFeatureLayer */ +/************************************************************************/ + +class OGRGMLSingleFeatureLayer : public OGRLayer +{ + private: + int nVal; + OGRFeatureDefn *poFeatureDefn; + int iNextShapeId; + + public: + OGRGMLSingleFeatureLayer(int nVal ); + ~OGRGMLSingleFeatureLayer() { poFeatureDefn->Release(); } + + virtual void ResetReading() { iNextShapeId = 0; } + virtual OGRFeature *GetNextFeature(); + virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; } + virtual int TestCapability( const char * ) { return FALSE; } +}; + +/************************************************************************/ +/* OGRGMLSingleFeatureLayer() */ +/************************************************************************/ + +OGRGMLSingleFeatureLayer::OGRGMLSingleFeatureLayer( int nVal ) +{ + poFeatureDefn = new OGRFeatureDefn( "SELECT" ); + poFeatureDefn->Reference(); + OGRFieldDefn oField( "Validates", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oField ); + + this->nVal = nVal; + iNextShapeId = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature * OGRGMLSingleFeatureLayer::GetNextFeature() +{ + if (iNextShapeId != 0) + return NULL; + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField(0, nVal); + poFeature->SetFID(iNextShapeId ++); + return poFeature; +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRGMLDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) +{ + if (poReader != NULL && EQUAL(pszSQLCommand, "SELECT ValidateSchema()")) + { + int bIsValid = FALSE; + if (osXSDFilename.size()) + { + CPLErrorReset(); + bIsValid = CPLValidateXML(osFilename, osXSDFilename, NULL); + } + return new OGRGMLSingleFeatureLayer(bIsValid); + } + + return OGRDataSource::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect); +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRGMLDataSource::ReleaseResultSet( OGRLayer * poResultsSet ) +{ + delete poResultsSet; +} + +/************************************************************************/ +/* ExtractSRSName() */ +/************************************************************************/ + +static int ExtractSRSName(const char* pszXML, char* szSRSName, + size_t sizeof_szSRSName) +{ + szSRSName[0] = '\0'; + + const char* pszSRSName = strstr(pszXML, "srsName=\""); + if( pszSRSName != NULL ) + { + pszSRSName += 9; + const char* pszEndQuote = strchr(pszSRSName, '"'); + if (pszEndQuote != NULL && + (size_t)(pszEndQuote - pszSRSName) < sizeof_szSRSName) + { + memcpy(szSRSName, pszSRSName, pszEndQuote - pszSRSName); + szSRSName[pszEndQuote - pszSRSName] = '\0'; + return TRUE; + } + } + return FALSE; +} + +/************************************************************************/ +/* FindAndParseTopElements() */ +/************************************************************************/ + +void OGRGMLDataSource::FindAndParseTopElements(VSILFILE* fp) +{ + /* Build a shortened XML file that contain only the global */ + /* boundedBy element, so as to be able to parse it easily */ + + char szStartTag[128]; + char* pszXML = (char*)CPLMalloc(8192 + 128 + 3 + 1); + VSIFSeekL(fp, 0, SEEK_SET); + int nRead = (int)VSIFReadL(pszXML, 1, 8192, fp); + pszXML[nRead] = 0; + + const char* pszStartTag = strchr(pszXML, '<'); + if (pszStartTag != NULL) + { + while (pszStartTag != NULL && pszStartTag[1] == '?') + pszStartTag = strchr(pszStartTag + 1, '<'); + + if (pszStartTag != NULL) + { + pszStartTag ++; + const char* pszEndTag = strchr(pszStartTag, ' '); + if (pszEndTag != NULL && pszEndTag - pszStartTag < 128 ) + { + memcpy(szStartTag, pszStartTag, pszEndTag - pszStartTag); + szStartTag[pszEndTag - pszStartTag] = '\0'; + } + else + pszStartTag = NULL; + } + } + + const char* pszDescription = strstr(pszXML, ""); + if( pszDescription ) + { + pszDescription += strlen(""); + const char* pszEndDescription = strstr(pszDescription, + ""); + if( pszEndDescription ) + { + CPLString osTmp(pszDescription); + osTmp.resize(pszEndDescription-pszDescription); + char* pszTmp = CPLUnescapeString(osTmp, NULL, CPLES_XML); + if( pszTmp ) + SetMetadataItem("DESCRIPTION", pszTmp); + CPLFree(pszTmp); + } + } + + const char* pszName = strstr(pszXML, "'); + if( pszName ) + { + pszName ++; + const char* pszEndName = strstr(pszName, ""); + if( pszEndName ) + { + CPLString osTmp(pszName); + osTmp.resize(pszEndName-pszName); + char* pszTmp = CPLUnescapeString(osTmp, NULL, CPLES_XML); + if( pszTmp ) + SetMetadataItem("NAME", pszTmp); + CPLFree(pszTmp); + } + } + + char* pszEndBoundedBy = strstr(pszXML, ""); + int bWFSBoundedBy = FALSE; + if (pszEndBoundedBy != NULL) + bWFSBoundedBy = TRUE; + else + pszEndBoundedBy = strstr(pszXML, ""); + if (pszStartTag != NULL && pszEndBoundedBy != NULL) + { + const char* pszSRSName = NULL; + char szSRSName[128]; + + szSRSName[0] = '\0'; + + /* Find a srsName somewhere for some WFS 2.0 documents */ + /* that have not it set at the element */ + /* e.g. http://geoserv.weichand.de:8080/geoserver/wfs?SERVICE=WFS&REQUEST=GetFeature&VERSION=2.0.0&TYPENAME=bvv:gmd_ex */ + if( bIsWFS ) + { + ExtractSRSName(pszXML, szSRSName, sizeof(szSRSName)); + } + + pszEndBoundedBy[strlen("")] = '\0'; + strcat(pszXML, ""); + + CPLPushErrorHandler(CPLQuietErrorHandler); + CPLXMLNode* psXML = CPLParseXMLString(pszXML); + CPLPopErrorHandler(); + CPLErrorReset(); + if (psXML != NULL) + { + CPLXMLNode* psBoundedBy = NULL; + CPLXMLNode* psIter = psXML; + while(psIter != NULL) + { + psBoundedBy = CPLGetXMLNode(psIter, bWFSBoundedBy ? + "wfs:boundedBy" : "gml:boundedBy"); + if (psBoundedBy != NULL) + break; + psIter = psIter->psNext; + } + + const char* pszLowerCorner = NULL; + const char* pszUpperCorner = NULL; + if (psBoundedBy != NULL) + { + CPLXMLNode* psEnvelope = CPLGetXMLNode(psBoundedBy, "gml:Envelope"); + if (psEnvelope) + { + pszSRSName = CPLGetXMLValue(psEnvelope, "srsName", NULL); + pszLowerCorner = CPLGetXMLValue(psEnvelope, "gml:lowerCorner", NULL); + pszUpperCorner = CPLGetXMLValue(psEnvelope, "gml:upperCorner", NULL); + } + } + + if( bIsWFS && pszSRSName == NULL && + pszLowerCorner != NULL && pszUpperCorner != NULL && + szSRSName[0] != '\0' ) + { + pszSRSName = szSRSName; + } + + if (pszSRSName != NULL && pszLowerCorner != NULL && pszUpperCorner != NULL) + { + char** papszLC = CSLTokenizeString(pszLowerCorner); + char** papszUC = CSLTokenizeString(pszUpperCorner); + if (CSLCount(papszLC) >= 2 && CSLCount(papszUC) >= 2) + { + CPLDebug("GML", "Global SRS = %s", pszSRSName); + + if (strncmp(pszSRSName, "http://www.opengis.net/gml/srs/epsg.xml#", 40) == 0) + { + std::string osWork; + osWork.assign("EPSG:", 5); + osWork.append(pszSRSName+40); + poReader->SetGlobalSRSName(osWork.c_str()); + } + else + poReader->SetGlobalSRSName(pszSRSName); + + double dfMinX = CPLAtofM(papszLC[0]); + double dfMinY = CPLAtofM(papszLC[1]); + double dfMaxX = CPLAtofM(papszUC[0]); + double dfMaxY = CPLAtofM(papszUC[1]); + + SetExtents(dfMinX, dfMinY, dfMaxX, dfMaxY); + } + CSLDestroy(papszLC); + CSLDestroy(papszUC); + } + + CPLDestroyXMLNode(psXML); + } + } + + CPLFree(pszXML); +} + +/************************************************************************/ +/* SetExtents() */ +/************************************************************************/ + +void OGRGMLDataSource::SetExtents(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY) +{ + sBoundingRect.MinX = dfMinX; + sBoundingRect.MinY = dfMinY; + sBoundingRect.MaxX = dfMaxX; + sBoundingRect.MaxY = dfMaxY; +} + +/************************************************************************/ +/* GetAppPrefix() */ +/************************************************************************/ + +const char* OGRGMLDataSource::GetAppPrefix() +{ + return CSLFetchNameValueDef(papszCreateOptions, "PREFIX", "ogr"); +} + +/************************************************************************/ +/* RemoveAppPrefix() */ +/************************************************************************/ + +int OGRGMLDataSource::RemoveAppPrefix() +{ + if( CSLTestBoolean(CSLFetchNameValueDef( + papszCreateOptions, "STRIP_PREFIX", "FALSE")) ) + return TRUE; + const char* pszPrefix = GetAppPrefix(); + return( pszPrefix[0] == '\0' ); +} + +/************************************************************************/ +/* WriteFeatureBoundedBy() */ +/************************************************************************/ + +int OGRGMLDataSource::WriteFeatureBoundedBy() +{ + return CSLTestBoolean(CSLFetchNameValueDef( + papszCreateOptions, "WRITE_FEATURE_BOUNDED_BY", "TRUE")); +} + +/************************************************************************/ +/* GetSRSDimensionLoc() */ +/************************************************************************/ + +const char* OGRGMLDataSource::GetSRSDimensionLoc() +{ + return CSLFetchNameValue(papszCreateOptions, "SRSDIMENSION_LOC"); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/ogrgmldriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/ogrgmldriver.cpp new file mode 100644 index 000000000..f8e4a8d38 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/ogrgmldriver.cpp @@ -0,0 +1,241 @@ +/****************************************************************************** + * $Id: ogrgmldriver.cpp 29240 2015-05-24 10:58:38Z rouault $ + * + * Project: OGR + * Purpose: OGRGMLDriver implementation + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gml.h" +#include "cpl_conv.h" +#include "cpl_multiproc.h" +#include "gmlreaderp.h" + +CPL_CVSID("$Id: ogrgmldriver.cpp 29240 2015-05-24 10:58:38Z rouault $"); + +/************************************************************************/ +/* OGRGMLDriverUnload() */ +/************************************************************************/ + +static void OGRGMLDriverUnload(CPL_UNUSED GDALDriver* poDriver) +{ + if( GMLReader::hMutex != NULL ) + CPLDestroyMutex( GMLReader::hMutex ); + GMLReader::hMutex = NULL; +} + +/************************************************************************/ +/* OGRGMLDriverIdentify() */ +/************************************************************************/ + +static int OGRGMLDriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->fpL == NULL ) + { + if( strstr(poOpenInfo->pszFilename, "xsd=") != NULL ) + return -1; /* must be later checked */ + return FALSE; + } + /* Might be a OS-Mastermap gzipped GML, so let be nice and try to open */ + /* it transparently with /vsigzip/ */ + else + if ( poOpenInfo->pabyHeader[0] == 0x1f && poOpenInfo->pabyHeader[1] == 0x8b && + EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "gz") && + strncmp(poOpenInfo->pszFilename, "/vsigzip/", strlen("/vsigzip/")) != 0 ) + { + return -1; /* must be later checked */ + } + else + { + const char* szPtr = (const char*)poOpenInfo->pabyHeader; + + if( ( (unsigned char)szPtr[0] == 0xEF ) + && ( (unsigned char)szPtr[1] == 0xBB ) + && ( (unsigned char)szPtr[2] == 0xBF) ) + { + szPtr += 3; + } +/* -------------------------------------------------------------------- */ +/* Here, we expect the opening chevrons of GML tree root element */ +/* -------------------------------------------------------------------- */ + if( szPtr[0] != '<' ) + return FALSE; + + if( !poOpenInfo->TryToIngest(4096) ) + return FALSE; + + return OGRGMLDataSource::CheckHeader((const char*)poOpenInfo->pabyHeader); + } +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRGMLDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + OGRGMLDataSource *poDS; + + if( poOpenInfo->eAccess == GA_Update ) + return NULL; + + if( OGRGMLDriverIdentify( poOpenInfo ) == FALSE ) + return NULL; + + poDS = new OGRGMLDataSource(); + + if( !poDS->Open( poOpenInfo ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRGMLDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + OGRGMLDataSource *poDS = new OGRGMLDataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* RegisterOGRGML() */ +/************************************************************************/ + +void RegisterOGRGML() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GML" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GML" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Geography Markup Language (GML)" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gml" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "gml xml" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_gml.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " +" " +" " +" " +" " ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" " +" " +" "); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, ""); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime IntegerList Integer64List RealList StringList" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRGMLDriverOpen; + poDriver->pfnIdentify = OGRGMLDriverIdentify; + poDriver->pfnCreate = OGRGMLDriverCreate; + poDriver->pfnUnloadDriver = OGRGMLDriverUnload; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/ogrgmllayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/ogrgmllayer.cpp new file mode 100644 index 000000000..e757f61f7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/ogrgmllayer.cpp @@ -0,0 +1,1066 @@ +/****************************************************************************** + * $Id: ogrgmllayer.cpp 28481 2015-02-13 17:11:15Z rouault $ + * + * Project: OGR + * Purpose: Implements OGRGMLLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gml.h" +#include "gmlutils.h" +#include "cpl_conv.h" +#include "cpl_port.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "ogr_api.h" + +CPL_CVSID("$Id: ogrgmllayer.cpp 28481 2015-02-13 17:11:15Z rouault $"); + +/************************************************************************/ +/* OGRGMLLayer() */ +/************************************************************************/ + +OGRGMLLayer::OGRGMLLayer( const char * pszName, + int bWriterIn, + OGRGMLDataSource *poDSIn ) + +{ + iNextGMLId = 0; + nTotalGMLCount = -1; + bInvalidFIDFound = FALSE; + pszFIDPrefix = NULL; + bFaceHoleNegative = FALSE; + + poDS = poDSIn; + + if ( EQUALN(pszName, "ogr:", 4) ) + poFeatureDefn = new OGRFeatureDefn( pszName+4 ); + else + poFeatureDefn = new OGRFeatureDefn( pszName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + bWriter = bWriterIn; + bSameSRS = FALSE; + +/* -------------------------------------------------------------------- */ +/* Reader's should get the corresponding GMLFeatureClass and */ +/* cache it. */ +/* -------------------------------------------------------------------- */ + if( !bWriter ) + poFClass = poDS->GetReader()->GetClass( pszName ); + else + poFClass = NULL; + + hCacheSRS = GML_BuildOGRGeometryFromList_CreateCache(); + + /* Compatibility option. Not advertized, because hopefully won't be needed */ + /* Just put here in provision... */ + bUseOldFIDFormat = CSLTestBoolean(CPLGetConfigOption("GML_USE_OLD_FID_FORMAT", "FALSE")); + + /* Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer() and GMLReader::GMLReader() */ + bFaceHoleNegative = CSLTestBoolean(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")); + +} + +/************************************************************************/ +/* ~OGRGMLLayer() */ +/************************************************************************/ + +OGRGMLLayer::~OGRGMLLayer() + +{ + CPLFree(pszFIDPrefix); + + if( poFeatureDefn ) + poFeatureDefn->Release(); + + GML_BuildOGRGeometryFromList_DestroyCache(hCacheSRS); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGMLLayer::ResetReading() + +{ + if (bWriter) + return; + + if (poDS->GetReadMode() == INTERLEAVED_LAYERS || + poDS->GetReadMode() == SEQUENTIAL_LAYERS) + { + /* Does the last stored feature belong to our layer ? If so, no */ + /* need to reset the reader */ + if (iNextGMLId == 0 && poDS->PeekStoredGMLFeature() != NULL && + poDS->PeekStoredGMLFeature()->GetClass() == poFClass) + return; + + delete poDS->PeekStoredGMLFeature(); + poDS->SetStoredGMLFeature(NULL); + } + + iNextGMLId = 0; + poDS->GetReader()->ResetReading(); + CPLDebug("GML", "ResetReading()"); + if ( poDS->GetLayerCount() > 1 && poDS->GetReadMode() == STANDARD ) + { + const char* pszElementName = poFClass->GetElementName(); + const char* pszLastPipe = strrchr( pszElementName, '|' ); + if ( pszLastPipe != NULL ) + pszElementName = pszLastPipe + 1; + poDS->GetReader()->SetFilteredClassName(pszElementName); + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRGMLLayer::GetNextFeature() + +{ + int i; + + if (bWriter) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot read features when writing a GML file"); + return NULL; + } + + if( poDS->GetLastReadLayer() != this ) + { + if( poDS->GetReadMode() != INTERLEAVED_LAYERS ) + ResetReading(); + poDS->SetLastReadLayer(this); + } + +/* ==================================================================== */ +/* Loop till we find and translate a feature meeting all our */ +/* requirements. */ +/* ==================================================================== */ + while( TRUE ) + { + GMLFeature *poGMLFeature = NULL; + OGRGeometry *poGeom = NULL; + + poGMLFeature = poDS->PeekStoredGMLFeature(); + if (poGMLFeature != NULL) + poDS->SetStoredGMLFeature(NULL); + else + { + poGMLFeature = poDS->GetReader()->NextFeature(); + if( poGMLFeature == NULL ) + return NULL; + + // We count reading low level GML features as a feature read for + // work checking purposes, though at least we didn't necessary + // have to turn it into an OGRFeature. + m_nFeaturesRead++; + } + +/* -------------------------------------------------------------------- */ +/* Is it of the proper feature class? */ +/* -------------------------------------------------------------------- */ + + if( poGMLFeature->GetClass() != poFClass ) + { + if( poDS->GetReadMode() == INTERLEAVED_LAYERS || + (poDS->GetReadMode() == SEQUENTIAL_LAYERS && iNextGMLId != 0) ) + { + CPLAssert(poDS->PeekStoredGMLFeature() == NULL); + poDS->SetStoredGMLFeature(poGMLFeature); + return NULL; + } + else + { + delete poGMLFeature; + continue; + } + } + +/* -------------------------------------------------------------------- */ +/* Extract the fid: */ +/* -Assumes the fids are non-negative integers with an optional */ +/* prefix */ +/* -If a prefix differs from the prefix of the first feature from */ +/* the poDS then the fids from the poDS are ignored and are */ +/* assigned serially thereafter */ +/* -------------------------------------------------------------------- */ + GIntBig nFID = -1; + const char * pszGML_FID = poGMLFeature->GetFID(); + if( bInvalidFIDFound ) + { + nFID = iNextGMLId++; + } + else if( pszGML_FID == NULL ) + { + bInvalidFIDFound = TRUE; + nFID = iNextGMLId++; + } + else if( iNextGMLId == 0 ) + { + int j = 0; + i = strlen( pszGML_FID )-1; + while( i >= 0 && pszGML_FID[i] >= '0' + && pszGML_FID[i] <= '9' && j<20) + i--, j++; + /* i points the last character of the fid */ + if( i >= 0 && j < 20 && pszFIDPrefix == NULL) + { + pszFIDPrefix = (char *) CPLMalloc(i+2); + pszFIDPrefix[i+1] = '\0'; + strncpy(pszFIDPrefix, pszGML_FID, i+1); + } + /* pszFIDPrefix now contains the prefix or NULL if no prefix is found */ + if( j < 20 && sscanf(pszGML_FID+i+1, CPL_FRMT_GIB, &nFID)==1) + { + if( iNextGMLId <= nFID ) + iNextGMLId = nFID + 1; + } + else + { + bInvalidFIDFound = TRUE; + nFID = iNextGMLId++; + } + } + else if( iNextGMLId != 0 ) + { + const char* pszFIDPrefix_notnull = pszFIDPrefix; + if (pszFIDPrefix_notnull == NULL) pszFIDPrefix_notnull = ""; + int nLenPrefix = strlen(pszFIDPrefix_notnull); + + if( strncmp(pszGML_FID, pszFIDPrefix_notnull, nLenPrefix) == 0 && + strlen(pszGML_FID+nLenPrefix) < 20 && + sscanf(pszGML_FID+nLenPrefix, CPL_FRMT_GIB, &nFID) == 1 ) + { /* fid with the prefix. Using its numerical part */ + if( iNextGMLId < nFID ) + iNextGMLId = nFID + 1; + } + else + { /* fid without the aforementioned prefix or a valid numerical part */ + bInvalidFIDFound = TRUE; + nFID = iNextGMLId++; + } + } + +/* -------------------------------------------------------------------- */ +/* Does it satisfy the spatial query, if there is one? */ +/* -------------------------------------------------------------------- */ + + OGRGeometry** papoGeometries = NULL; + const CPLXMLNode* const * papsGeometry = poGMLFeature->GetGeometryList(); + + if( poFeatureDefn->GetGeomFieldCount() > 1 ) + { + papoGeometries = (OGRGeometry**) + CPLCalloc( poFeatureDefn->GetGeomFieldCount(), sizeof(OGRGeometry*) ); + const char* pszSRSName = poDS->GetGlobalSRSName(); + for( i=0; i < poFeatureDefn->GetGeomFieldCount(); i++) + { + const CPLXMLNode* psGeom = poGMLFeature->GetGeometryRef(i); + if( psGeom != NULL ) + { + const CPLXMLNode* myGeometryList[2]; + myGeometryList[0] = psGeom; + myGeometryList[1] = NULL; + poGeom = GML_BuildOGRGeometryFromList(myGeometryList, TRUE, + poDS->GetInvertAxisOrderIfLatLong(), + pszSRSName, + poDS->GetConsiderEPSGAsURN(), + poDS->GetSecondaryGeometryOption(), + hCacheSRS, + bFaceHoleNegative ); + + /* Do geometry type changes if needed to match layer geometry type */ + if (poGeom != NULL) + { + papoGeometries[i] = OGRGeometryFactory::forceTo(poGeom, + poFeatureDefn->GetGeomFieldDefn(i)->GetType()); + poGeom = NULL; + } + else + // We assume the createFromGML() function would have already + // reported the error. + { + for( i=0; i < poFeatureDefn->GetGeomFieldCount(); i++) + { + delete papoGeometries[i]; + } + CPLFree(papoGeometries); + delete poGMLFeature; + return NULL; + } + } + } + + if( m_poFilterGeom != NULL && + m_iGeomFieldFilter >= 0 && + m_iGeomFieldFilter < poFeatureDefn->GetGeomFieldCount() && + papoGeometries[m_iGeomFieldFilter] && + !FilterGeometry( papoGeometries[m_iGeomFieldFilter] ) ) + { + for( i=0; i < poFeatureDefn->GetGeomFieldCount(); i++) + { + delete papoGeometries[i]; + } + CPLFree(papoGeometries); + delete poGMLFeature; + continue; + } + } + else if (papsGeometry[0] != NULL) + { + const char* pszSRSName = poDS->GetGlobalSRSName(); + poGeom = GML_BuildOGRGeometryFromList(papsGeometry, TRUE, + poDS->GetInvertAxisOrderIfLatLong(), + pszSRSName, + poDS->GetConsiderEPSGAsURN(), + poDS->GetSecondaryGeometryOption(), + hCacheSRS, + bFaceHoleNegative ); + + /* Do geometry type changes if needed to match layer geometry type */ + if (poGeom != NULL) + { + poGeom = OGRGeometryFactory::forceTo(poGeom, GetGeomType()); + } + else + // We assume the createFromGML() function would have already + // reported the error. + { + delete poGMLFeature; + return NULL; + } + + if( m_poFilterGeom != NULL && !FilterGeometry( poGeom ) ) + { + delete poGMLFeature; + delete poGeom; + continue; + } + } + + +/* -------------------------------------------------------------------- */ +/* Convert the whole feature into an OGRFeature. */ +/* -------------------------------------------------------------------- */ + int iField; + int iDstField = 0; + OGRFeature *poOGRFeature = new OGRFeature( poFeatureDefn ); + + poOGRFeature->SetFID( nFID ); + if (poDS->ExposeId()) + { + if (pszGML_FID) + poOGRFeature->SetField( iDstField, pszGML_FID ); + iDstField ++; + } + + int nPropertyCount = poFClass->GetPropertyCount(); + for( iField = 0; iField < nPropertyCount; iField++, iDstField ++ ) + { + const GMLProperty *psGMLProperty = poGMLFeature->GetProperty( iField ); + if( psGMLProperty == NULL || psGMLProperty->nSubProperties == 0 ) + continue; + + switch( poFClass->GetProperty(iField)->GetType() ) + { + case GMLPT_Real: + { + poOGRFeature->SetField( iDstField, CPLAtof(psGMLProperty->papszSubProperties[0]) ); + } + break; + + case GMLPT_IntegerList: + { + int nCount = psGMLProperty->nSubProperties; + int *panIntList = (int *) CPLMalloc(sizeof(int) * nCount ); + + for( i = 0; i < nCount; i++ ) + panIntList[i] = atoi(psGMLProperty->papszSubProperties[i]); + + poOGRFeature->SetField( iDstField, nCount, panIntList ); + CPLFree( panIntList ); + } + break; + + case GMLPT_Integer64List: + { + int nCount = psGMLProperty->nSubProperties; + GIntBig *panIntList = (GIntBig *) CPLMalloc(sizeof(GIntBig) * nCount ); + + for( i = 0; i < nCount; i++ ) + panIntList[i] = CPLAtoGIntBig(psGMLProperty->papszSubProperties[i]); + + poOGRFeature->SetField( iDstField, nCount, panIntList ); + CPLFree( panIntList ); + } + break; + + case GMLPT_RealList: + { + int nCount = psGMLProperty->nSubProperties; + double *padfList = (double *)CPLMalloc(sizeof(double)*nCount); + + for( i = 0; i < nCount; i++ ) + padfList[i] = CPLAtof(psGMLProperty->papszSubProperties[i]); + + poOGRFeature->SetField( iDstField, nCount, padfList ); + CPLFree( padfList ); + } + break; + + case GMLPT_StringList: + case GMLPT_FeaturePropertyList: + { + poOGRFeature->SetField( iDstField, psGMLProperty->papszSubProperties ); + } + break; + + case GMLPT_Boolean: + { + if( strcmp(psGMLProperty->papszSubProperties[0], "true") == 0 || + strcmp(psGMLProperty->papszSubProperties[0], "1") == 0 ) + { + poOGRFeature->SetField( iDstField, 1); + } + else if( strcmp(psGMLProperty->papszSubProperties[0], "false") == 0 || + strcmp(psGMLProperty->papszSubProperties[0], "0") == 0 ) + { + poOGRFeature->SetField( iDstField, 0); + } + else + poOGRFeature->SetField( iDstField, psGMLProperty->papszSubProperties[0] ); + break; + } + + case GMLPT_BooleanList: + { + int nCount = psGMLProperty->nSubProperties; + int *panIntList = (int *) CPLMalloc(sizeof(int) * nCount ); + + for( i = 0; i < nCount; i++ ) + { + panIntList[i] = ( strcmp(psGMLProperty->papszSubProperties[i], "true") == 0 || + strcmp(psGMLProperty->papszSubProperties[i], "1") == 0 ); + } + + poOGRFeature->SetField( iDstField, nCount, panIntList ); + CPLFree( panIntList ); + break; + } + + default: + poOGRFeature->SetField( iDstField, psGMLProperty->papszSubProperties[0] ); + break; + } + } + + delete poGMLFeature; + poGMLFeature = NULL; + + /* Assign the geometry before the attribute filter because */ + /* the attribute filter may use a special field like OGR_GEOMETRY */ + if( papoGeometries != NULL ) + { + for( i=0; i < poFeatureDefn->GetGeomFieldCount(); i++) + { + poOGRFeature->SetGeomFieldDirectly( i, papoGeometries[i] ); + } + CPLFree(papoGeometries); + papoGeometries = NULL; + } + else + poOGRFeature->SetGeometryDirectly( poGeom ); + + /* Assign SRS */ + for( i=0; i < poFeatureDefn->GetGeomFieldCount(); i++) + { + poGeom = poOGRFeature->GetGeomFieldRef(i); + if( poGeom != NULL ) + { + OGRSpatialReference* poSRS = poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef(); + if (poSRS != NULL) + poGeom->assignSpatialReference(poSRS); + } + } + +/* -------------------------------------------------------------------- */ +/* Test against the attribute query. */ +/* -------------------------------------------------------------------- */ + if( m_poAttrQuery != NULL + && !m_poAttrQuery->Evaluate( poOGRFeature ) ) + { + delete poOGRFeature; + continue; + } + +/* -------------------------------------------------------------------- */ +/* Wow, we got our desired feature. Return it. */ +/* -------------------------------------------------------------------- */ + + return poOGRFeature; + } + + return NULL; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRGMLLayer::GetFeatureCount( int bForce ) + +{ + if( poFClass == NULL ) + return 0; + + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL ) + return OGRLayer::GetFeatureCount( bForce ); + else + { + /* If the schema is read from a .xsd file, we haven't read */ + /* the feature count, so compute it now */ + GIntBig nFeatureCount = poFClass->GetFeatureCount(); + if (nFeatureCount < 0) + { + nFeatureCount = OGRLayer::GetFeatureCount(bForce); + poFClass->SetFeatureCount(nFeatureCount); + } + return nFeatureCount; + } +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRGMLLayer::GetExtent(OGREnvelope *psExtent, int bForce ) + +{ + double dfXMin, dfXMax, dfYMin, dfYMax; + + if( GetGeomType() == wkbNone ) + return OGRERR_FAILURE; + + if( poFClass != NULL && + poFClass->GetExtents( &dfXMin, &dfXMax, &dfYMin, &dfYMax ) ) + { + psExtent->MinX = dfXMin; + psExtent->MaxX = dfXMax; + psExtent->MinY = dfYMin; + psExtent->MaxY = dfYMax; + + return OGRERR_NONE; + } + else + return OGRLayer::GetExtent( psExtent, bForce ); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +static void GMLWriteField(OGRGMLDataSource* poDS, + VSILFILE *fp, + int bWriteSpaceIndentation, + const char* pszPrefix, + int bRemoveAppPrefix, + OGRFieldDefn* poFieldDefn, + const char* pszVal ) + +{ + const char* pszFieldName = poFieldDefn->GetNameRef(); + + while( *pszVal == ' ' ) + pszVal++; + + if (bWriteSpaceIndentation) + VSIFPrintfL(fp, " "); + + if( bRemoveAppPrefix ) + poDS->PrintLine( fp, "<%s>%s", + pszFieldName, + pszVal, + pszFieldName); + else + poDS->PrintLine( fp, "<%s:%s>%s", + pszPrefix, + pszFieldName, + pszVal, + pszPrefix, + pszFieldName); +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRGMLLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + int bIsGML3Output = poDS->IsGML3Output(); + VSILFILE *fp = poDS->GetOutputFP(); + int bWriteSpaceIndentation = poDS->WriteSpaceIndentation(); + const char* pszPrefix = poDS->GetAppPrefix(); + int bRemoveAppPrefix = poDS->RemoveAppPrefix(); + + if( !bWriter ) + return OGRERR_FAILURE; + + poFeature->FillUnsetWithDefault(TRUE, NULL); + if( !poFeature->Validate( OGR_F_VAL_ALL & ~OGR_F_VAL_GEOM_TYPE & ~OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT, TRUE ) ) + return OGRERR_FAILURE; + + if (bWriteSpaceIndentation) + VSIFPrintfL(fp, " "); + if (bIsGML3Output) + { + if( bRemoveAppPrefix ) + poDS->PrintLine( fp, "" ); + else + poDS->PrintLine( fp, "<%s:featureMember>", pszPrefix ); + } + else + poDS->PrintLine( fp, "" ); + + if( iNextGMLId == 0 ) + { + bSameSRS = TRUE; + for( int iGeomField = 1; iGeomField < poFeatureDefn->GetGeomFieldCount(); iGeomField++ ) + { + OGRGeomFieldDefn *poFieldDefn0 = poFeatureDefn->GetGeomFieldDefn(0); + OGRGeomFieldDefn *poFieldDefn = poFeatureDefn->GetGeomFieldDefn(iGeomField); + OGRSpatialReference* poSRS0 = poFieldDefn0->GetSpatialRef(); + OGRSpatialReference* poSRS = poFieldDefn->GetSpatialRef(); + if( poSRS0 != NULL && poSRS == NULL ) + bSameSRS = FALSE; + else if( poSRS0 == NULL && poSRS != NULL ) + bSameSRS = FALSE; + else if( poSRS0 != NULL && poSRS != NULL && + poSRS0 != poSRS && !poSRS0->IsSame(poSRS) ) + { + bSameSRS = FALSE; + } + } + } + + if( poFeature->GetFID() == OGRNullFID ) + poFeature->SetFID( iNextGMLId++ ); + + int nGMLIdIndex = -1; + if (bWriteSpaceIndentation) + VSIFPrintfL(fp, " "); + VSIFPrintfL(fp, "<"); + if( !bRemoveAppPrefix ) + VSIFPrintfL(fp, "%s:", pszPrefix); + if (bIsGML3Output) + { + nGMLIdIndex = poFeatureDefn->GetFieldIndex("gml_id"); + if (nGMLIdIndex >= 0 && poFeature->IsFieldSet( nGMLIdIndex ) ) + poDS->PrintLine( fp, "%s gml:id=\"%s\">", + poFeatureDefn->GetName(), + poFeature->GetFieldAsString(nGMLIdIndex) ); + else + poDS->PrintLine( fp, "%s gml:id=\"%s." CPL_FRMT_GIB "\">", + poFeatureDefn->GetName(), + poFeatureDefn->GetName(), + poFeature->GetFID() ); + } + else + { + nGMLIdIndex = poFeatureDefn->GetFieldIndex("fid"); + if (bUseOldFIDFormat) + { + poDS->PrintLine( fp, "%s fid=\"F" CPL_FRMT_GIB "\">", + poFeatureDefn->GetName(), + poFeature->GetFID() ); + } + else if (nGMLIdIndex >= 0 && poFeature->IsFieldSet( nGMLIdIndex ) ) + { + poDS->PrintLine( fp, "%s fid=\"%s\">", + poFeatureDefn->GetName(), + poFeature->GetFieldAsString(nGMLIdIndex) ); + } + else + { + poDS->PrintLine( fp, "%s fid=\"%s." CPL_FRMT_GIB "\">", + poFeatureDefn->GetName(), + poFeatureDefn->GetName(), + poFeature->GetFID() ); + } + } + + + for( int iGeomField = 0; iGeomField < poFeatureDefn->GetGeomFieldCount(); iGeomField++ ) + { + OGRGeomFieldDefn *poFieldDefn = poFeatureDefn->GetGeomFieldDefn(iGeomField); + + // Write out Geometry - for now it isn't indented properly. + /* GML geometries don't like very much the concept of empty geometry */ + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(iGeomField); + if( poGeom != NULL && !poGeom->IsEmpty()) + { + char *pszGeometry; + OGREnvelope3D sGeomBounds; + + int nCoordDimension = poGeom->getCoordinateDimension(); + + poGeom->getEnvelope( &sGeomBounds ); + if( bSameSRS ) + poDS->GrowExtents( &sGeomBounds, nCoordDimension ); + + if (poGeom->getSpatialReference() == NULL && poFieldDefn->GetSpatialRef() != NULL) + poGeom->assignSpatialReference(poFieldDefn->GetSpatialRef()); + + if (bIsGML3Output && poDS->WriteFeatureBoundedBy()) + { + int bCoordSwap; + + char* pszSRSName = GML_GetSRSName(poGeom->getSpatialReference(), poDS->IsLongSRSRequired(), &bCoordSwap); + char szLowerCorner[75], szUpperCorner[75]; + if (bCoordSwap) + { + OGRMakeWktCoordinate(szLowerCorner, sGeomBounds.MinY, sGeomBounds.MinX, sGeomBounds.MinZ, nCoordDimension); + OGRMakeWktCoordinate(szUpperCorner, sGeomBounds.MaxY, sGeomBounds.MaxX, sGeomBounds.MaxZ, nCoordDimension); + } + else + { + OGRMakeWktCoordinate(szLowerCorner, sGeomBounds.MinX, sGeomBounds.MinY, sGeomBounds.MinZ, nCoordDimension); + OGRMakeWktCoordinate(szUpperCorner, sGeomBounds.MaxX, sGeomBounds.MaxY, sGeomBounds.MaxZ, nCoordDimension); + } + if (bWriteSpaceIndentation) + VSIFPrintfL(fp, " "); + poDS->PrintLine( fp, "%s%s", + (nCoordDimension == 3) ? " srsDimension=\"3\"" : "",pszSRSName, szLowerCorner, szUpperCorner); + CPLFree(pszSRSName); + } + + char** papszOptions = (bIsGML3Output) ? CSLAddString(NULL, "FORMAT=GML3") : NULL; + if (bIsGML3Output && !poDS->IsLongSRSRequired()) + papszOptions = CSLAddString(papszOptions, "GML3_LONGSRS=NO"); + const char* pszSRSDimensionLoc = poDS->GetSRSDimensionLoc(); + if( pszSRSDimensionLoc != NULL ) + papszOptions = CSLSetNameValue(papszOptions, "SRSDIMENSION_LOC", pszSRSDimensionLoc); + if (poDS->IsGML32Output()) + { + if( poFeatureDefn->GetGeomFieldCount() > 1 ) + papszOptions = CSLAddString(papszOptions, + CPLSPrintf("GMLID=%s.%s." CPL_FRMT_GIB, + poFeatureDefn->GetName(), + poFieldDefn->GetNameRef(), + poFeature->GetFID())); + else + papszOptions = CSLAddString(papszOptions, + CPLSPrintf("GMLID=%s.geom." CPL_FRMT_GIB, + poFeatureDefn->GetName(), poFeature->GetFID())); + } + if( !bIsGML3Output && OGR_GT_IsNonLinear(poGeom->getGeometryType()) ) + { + OGRGeometry* poGeomTmp = OGRGeometryFactory::forceTo( + poGeom->clone(),OGR_GT_GetLinear(poGeom->getGeometryType())); + pszGeometry = poGeomTmp->exportToGML(papszOptions); + delete poGeomTmp; + } + else + pszGeometry = poGeom->exportToGML(papszOptions); + CSLDestroy(papszOptions); + if (bWriteSpaceIndentation) + VSIFPrintfL(fp, " "); + if( bRemoveAppPrefix ) + poDS->PrintLine( fp, "<%s>%s", + poFieldDefn->GetNameRef(), + pszGeometry, + poFieldDefn->GetNameRef() ); + else + poDS->PrintLine( fp, "<%s:%s>%s", + pszPrefix, poFieldDefn->GetNameRef(), + pszGeometry, + pszPrefix, poFieldDefn->GetNameRef() ); + CPLFree( pszGeometry ); + } + } + + // Write all "set" fields. + for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + + OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn( iField ); + + if( poFeature->IsFieldSet( iField ) && iField != nGMLIdIndex ) + { + OGRFieldType eType = poFieldDefn->GetType(); + if (eType == OFTStringList ) + { + char ** papszIter = poFeature->GetFieldAsStringList( iField ); + while( papszIter != NULL && *papszIter != NULL ) + { + char *pszEscaped = OGRGetXML_UTF8_EscapedString( *papszIter ); + GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix, + bRemoveAppPrefix, poFieldDefn, pszEscaped); + CPLFree( pszEscaped ); + + papszIter ++; + } + } + else if (eType == OFTIntegerList ) + { + int nCount = 0; + const int* panVals = poFeature->GetFieldAsIntegerList( iField, &nCount ); + if( poFieldDefn->GetSubType() == OFSTBoolean ) + { + for(int i = 0; i < nCount; i++) + { + /* 0 and 1 are OK, but the canonical representation is false and true */ + GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix, + bRemoveAppPrefix, poFieldDefn, + panVals[i] ? "true" : "false"); + } + } + else + { + for(int i = 0; i < nCount; i++) + { + GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix, + bRemoveAppPrefix, poFieldDefn, + CPLSPrintf("%d", panVals[i])); + } + } + } + else if (eType == OFTInteger64List ) + { + int nCount = 0; + const GIntBig* panVals = poFeature->GetFieldAsInteger64List( iField, &nCount ); + if( poFieldDefn->GetSubType() == OFSTBoolean ) + { + for(int i = 0; i < nCount; i++) + { + /* 0 and 1 are OK, but the canonical representation is false and true */ + GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix, + bRemoveAppPrefix, poFieldDefn, + panVals[i] ? "true" : "false"); + } + } + else + { + for(int i = 0; i < nCount; i++) + { + GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix, + bRemoveAppPrefix, poFieldDefn, + CPLSPrintf(CPL_FRMT_GIB, panVals[i])); + } + } + } + else if (eType == OFTRealList ) + { + int nCount = 0; + const double* padfVals = poFeature->GetFieldAsDoubleList( iField, &nCount ); + for(int i = 0; i < nCount; i++) + { + char szBuffer[80]; + CPLsnprintf( szBuffer, sizeof(szBuffer), "%.15g", padfVals[i]); + GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix, + bRemoveAppPrefix, poFieldDefn, szBuffer); + } + } + else if ((eType == OFTInteger || eType == OFTInteger64) && + poFieldDefn->GetSubType() == OFSTBoolean ) + { + /* 0 and 1 are OK, but the canonical representation is false and true */ + GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix, + bRemoveAppPrefix, poFieldDefn, + (poFeature->GetFieldAsInteger(iField)) ? "true" : "false"); + } + else + { + const char *pszRaw = poFeature->GetFieldAsString( iField ); + + char *pszEscaped = OGRGetXML_UTF8_EscapedString( pszRaw ); + + GMLWriteField(poDS, fp, bWriteSpaceIndentation, pszPrefix, + bRemoveAppPrefix, poFieldDefn, pszEscaped); + CPLFree( pszEscaped ); + } + } + } + + if (bWriteSpaceIndentation) + VSIFPrintfL(fp, " "); + if( bRemoveAppPrefix ) + poDS->PrintLine( fp, "", poFeatureDefn->GetName() ); + else + poDS->PrintLine( fp, "", pszPrefix, poFeatureDefn->GetName() ); + if (bWriteSpaceIndentation) + VSIFPrintfL(fp, " "); + if (bIsGML3Output) + { + if( bRemoveAppPrefix ) + poDS->PrintLine( fp, "" ); + else + poDS->PrintLine( fp, "", pszPrefix ); + } + else + poDS->PrintLine( fp, "" ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGMLLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCSequentialWrite) ) + return bWriter; + + else if( EQUAL(pszCap,OLCCreateField) ) + return bWriter && iNextGMLId == 0; + + else if( EQUAL(pszCap,OLCCreateGeomField) ) + return bWriter && iNextGMLId == 0; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + { + double dfXMin, dfXMax, dfYMin, dfYMax; + + if( poFClass == NULL ) + return FALSE; + + return poFClass->GetExtents( &dfXMin, &dfXMax, &dfYMin, &dfYMax ); + } + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + { + if( poFClass == NULL + || m_poFilterGeom != NULL + || m_poAttrQuery != NULL ) + return FALSE; + + return poFClass->GetFeatureCount() != -1; + } + + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + + else if( EQUAL(pszCap,OLCCurveGeometries) ) + return poDS->IsGML3Output(); + + else + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRGMLLayer::CreateField( OGRFieldDefn *poField, int bApproxOK ) + +{ + if( !bWriter || iNextGMLId != 0 ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Enforce XML naming semantics on element name. */ +/* -------------------------------------------------------------------- */ + OGRFieldDefn oCleanCopy( poField ); + char *pszName = CPLStrdup( poField->GetNameRef() ); + CPLCleanXMLElementName( pszName ); + + if( strcmp(pszName,poField->GetNameRef()) != 0 ) + { + if( !bApproxOK ) + { + CPLFree( pszName ); + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create field with name '%s', it would not\n" + "be valid as an XML element name.", + poField->GetNameRef() ); + return OGRERR_FAILURE; + } + + oCleanCopy.SetName( pszName ); + CPLError( CE_Warning, CPLE_AppDefined, + "Field name '%s' adjusted to '%s' to be a valid\n" + "XML element name.", + poField->GetNameRef(), pszName ); + } + + CPLFree( pszName ); + + + poFeatureDefn->AddFieldDefn( &oCleanCopy ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateGeomField() */ +/************************************************************************/ + +OGRErr OGRGMLLayer::CreateGeomField( OGRGeomFieldDefn *poField, int bApproxOK ) + +{ + if( !bWriter || iNextGMLId != 0 ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Enforce XML naming semantics on element name. */ +/* -------------------------------------------------------------------- */ + OGRGeomFieldDefn oCleanCopy( poField ); + char *pszName = CPLStrdup( poField->GetNameRef() ); + CPLCleanXMLElementName( pszName ); + + if( strcmp(pszName,poField->GetNameRef()) != 0 ) + { + if( !bApproxOK ) + { + CPLFree( pszName ); + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create field with name '%s', it would not\n" + "be valid as an XML element name.", + poField->GetNameRef() ); + return OGRERR_FAILURE; + } + + oCleanCopy.SetName( pszName ); + CPLError( CE_Warning, CPLE_AppDefined, + "Field name '%s' adjusted to '%s' to be a valid\n" + "XML element name.", + poField->GetNameRef(), pszName ); + } + + CPLFree( pszName ); + + + poFeatureDefn->AddGeomFieldDefn( &oCleanCopy ); + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/parsexsd.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/parsexsd.cpp new file mode 100644 index 000000000..71c656077 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/parsexsd.cpp @@ -0,0 +1,1020 @@ +/****************************************************************************** + * $Id: parsexsd.cpp 28909 2015-04-15 12:52:53Z rouault $ + * + * Project: GML Reader + * Purpose: Implementation of GMLParseXSD() + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "parsexsd.h" +#include "cpl_error.h" +#include "cpl_conv.h" +#include "ogr_core.h" +#include "cpl_string.h" +#include "cpl_http.h" +#include + +/************************************************************************/ +/* StripNS() */ +/* */ +/* Return potentially shortened form of string with namespace */ +/* stripped off if there is one. Returns pointer into */ +/* original string. */ +/************************************************************************/ + +const char *StripNS( const char *pszFullValue ) + +{ + const char *pszColon = strstr( pszFullValue, ":" ); + if( pszColon == NULL ) + return pszFullValue; + else + return pszColon + 1; +} + +/************************************************************************/ +/* GetSimpleTypeProperties() */ +/************************************************************************/ + +static +int GetSimpleTypeProperties(CPLXMLNode *psTypeNode, + GMLPropertyType *pGMLType, + int *pnWidth, + int *pnPrecision) +{ + const char *pszBase = + StripNS( CPLGetXMLValue( psTypeNode, + "restriction.base", "" )); + + if( EQUAL(pszBase,"decimal") ) + { + *pGMLType = GMLPT_Real; + const char *pszWidth = + CPLGetXMLValue( psTypeNode, + "restriction.totalDigits.value", "0" ); + const char *pszPrecision = + CPLGetXMLValue( psTypeNode, + "restriction.fractionDigits.value", "0" ); + *pnWidth = atoi(pszWidth); + *pnPrecision = atoi(pszPrecision); + return TRUE; + } + + else if( EQUAL(pszBase,"float") ) + { + *pGMLType = GMLPT_Float; + return TRUE; + } + + else if( EQUAL(pszBase,"double") ) + { + *pGMLType = GMLPT_Real; + return TRUE; + } + + else if( EQUAL(pszBase,"integer") ) + { + *pGMLType = GMLPT_Integer; + const char *pszWidth = + CPLGetXMLValue( psTypeNode, + "restriction.totalDigits.value", "0" ); + *pnWidth = atoi(pszWidth); + return TRUE; + } + + else if( EQUAL(pszBase,"long") ) + { + *pGMLType = GMLPT_Integer64; + const char *pszWidth = + CPLGetXMLValue( psTypeNode, + "restriction.totalDigits.value", "0" ); + *pnWidth = atoi(pszWidth); + return TRUE; + } + + else if( EQUAL(pszBase,"long") ) + { + *pGMLType = GMLPT_Integer64; + const char *pszWidth = + CPLGetXMLValue( psTypeNode, + "restriction.totalDigits.value", "0" ); + *pnWidth = atoi(pszWidth); + return TRUE; + } + + else if( EQUAL(pszBase,"string") ) + { + *pGMLType = GMLPT_String; + const char *pszWidth = + CPLGetXMLValue( psTypeNode, + "restriction.maxLength.value", "0" ); + *pnWidth = atoi(pszWidth); + return TRUE; + } + + /* TODO: Would be nice to have a proper date type */ + else if( EQUAL(pszBase,"date") || + EQUAL(pszBase,"dateTime") ) + { + *pGMLType = GMLPT_String; + return TRUE; + } + + else if( EQUAL(pszBase,"boolean") ) + { + *pGMLType = GMLPT_Boolean; + return TRUE; + } + + else if( EQUAL(pszBase,"short") ) + { + *pGMLType = GMLPT_Short; + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* LookForSimpleType() */ +/************************************************************************/ + +static +int LookForSimpleType(CPLXMLNode *psSchemaNode, + const char* pszStrippedNSType, + GMLPropertyType *pGMLType, + int *pnWidth, + int *pnPrecision) +{ + CPLXMLNode *psThis; + for( psThis = psSchemaNode->psChild; + psThis != NULL; psThis = psThis->psNext ) + { + if( psThis->eType == CXT_Element + && EQUAL(psThis->pszValue,"simpleType") + && EQUAL(CPLGetXMLValue(psThis,"name",""),pszStrippedNSType) ) + { + break; + } + } + if (psThis == NULL) + return FALSE; + + return GetSimpleTypeProperties(psThis, pGMLType, pnWidth, pnPrecision); +} + +/************************************************************************/ +/* GetSingleChildElement() */ +/************************************************************************/ + +/* Returns the child element whose name is pszExpectedValue only if */ +/* there is only one child that is an element. */ +static +CPLXMLNode* GetSingleChildElement(CPLXMLNode* psNode, const char* pszExpectedValue) +{ + CPLXMLNode* psChild = NULL; + CPLXMLNode* psIter; + + if( psNode == NULL ) + return NULL; + + psIter = psNode->psChild; + if( psIter == NULL ) + return NULL; + while( psIter != NULL ) + { + if( psIter->eType == CXT_Element ) + { + if( psChild != NULL ) + return NULL; + if( pszExpectedValue != NULL && + strcmp(psIter->pszValue, pszExpectedValue) != 0 ) + return NULL; + psChild = psIter; + } + psIter = psIter->psNext; + } + return psChild; +} + +/************************************************************************/ +/* CheckMinMaxOccursCardinality() */ +/************************************************************************/ + +static int CheckMinMaxOccursCardinality(CPLXMLNode* psNode) +{ + const char* pszMinOccurs = CPLGetXMLValue( psNode, "minOccurs", NULL ); + const char* pszMaxOccurs = CPLGetXMLValue( psNode, "maxOccurs", NULL ); + return (pszMinOccurs == NULL || EQUAL(pszMinOccurs, "0") || + EQUAL(pszMinOccurs, "1")) && + (pszMaxOccurs == NULL || EQUAL(pszMaxOccurs, "1")); +} + +/************************************************************************/ +/* GetListTypeFromSingleType() */ +/************************************************************************/ + +static GMLPropertyType GetListTypeFromSingleType(GMLPropertyType eType) +{ + if( eType == GMLPT_String ) + return GMLPT_StringList; + if( eType == GMLPT_Integer || eType == GMLPT_Short ) + return GMLPT_IntegerList; + if( eType == GMLPT_Integer64 ) + return GMLPT_Integer64List; + if( eType == GMLPT_Real || eType == GMLPT_Float ) + return GMLPT_RealList; + if( eType == GMLPT_Boolean ) + return GMLPT_BooleanList; + if( eType == GMLPT_FeatureProperty ) + return GMLPT_FeaturePropertyList; + return eType; +} + +/************************************************************************/ +/* ParseFeatureType() */ +/************************************************************************/ + +typedef struct +{ + const char* pszName; + OGRwkbGeometryType eType; +} AssocNameType; + +static const AssocNameType apsPropertyTypes [] = +{ + {"GeometryPropertyType", wkbUnknown}, + {"PointPropertyType", wkbPoint}, + {"LineStringPropertyType", wkbLineString}, + {"CurvePropertyType", wkbCompoundCurve}, + {"PolygonPropertyType", wkbPolygon}, + {"SurfacePropertyType", wkbCurvePolygon}, + {"MultiPointPropertyType", wkbMultiPoint}, + {"MultiLineStringPropertyType", wkbMultiLineString}, + {"MultiCurvePropertyType", wkbMultiCurve}, + {"MultiPolygonPropertyType", wkbMultiPolygon}, + {"MultiSurfacePropertyType", wkbMultiSurface}, + {"MultiGeometryPropertyType", wkbGeometryCollection}, + {"GeometryAssociationType", wkbUnknown}, + {NULL, wkbUnknown}, +}; + +/* Found in FME .xsd (e.g. ) */ +static const AssocNameType apsRefTypes [] = +{ + {"pointProperty", wkbPoint}, + {"curveProperty", wkbLineString}, /* should we promote to wkbCompoundCurve ? */ + {"surfaceProperty", wkbPolygon}, /* should we promote to wkbCurvePolygon ? */ + {"multiPointProperty", wkbMultiPoint}, + {"multiCurveProperty", wkbMultiLineString}, + {"multiSurfaceProperty", wkbMultiPolygon}, /* should we promote to wkbMultiSurface ? */ + {NULL, wkbUnknown}, +}; + +static +GMLFeatureClass* GMLParseFeatureType(CPLXMLNode *psSchemaNode, + const char* pszName, + CPLXMLNode *psThis); + +static +GMLFeatureClass* GMLParseFeatureType(CPLXMLNode *psSchemaNode, + const char* pszName, + const char *pszType) +{ + CPLXMLNode *psThis; + for( psThis = psSchemaNode->psChild; + psThis != NULL; psThis = psThis->psNext ) + { + if( psThis->eType == CXT_Element + && EQUAL(psThis->pszValue,"complexType") + && EQUAL(CPLGetXMLValue(psThis,"name",""),pszType) ) + { + break; + } + } + if (psThis == NULL) + return NULL; + + return GMLParseFeatureType(psSchemaNode, pszName, psThis); +} + + +static +GMLFeatureClass* GMLParseFeatureType(CPLXMLNode *psSchemaNode, + const char* pszName, + CPLXMLNode *psComplexType) +{ + +/* -------------------------------------------------------------------- */ +/* Grab the sequence of extensions greatgrandchild. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psAttrSeq = + CPLGetXMLNode( psComplexType, + "complexContent.extension.sequence" ); + + if( psAttrSeq == NULL ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* We are pretty sure this going to be a valid Feature class */ +/* now, so create it. */ +/* -------------------------------------------------------------------- */ + GMLFeatureClass *poClass = new GMLFeatureClass( pszName ); + +/* -------------------------------------------------------------------- */ +/* Loop over each of the attribute elements being defined for */ +/* this feature class. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psAttrDef; + int nAttributeIndex = 0; + + int bGotUnrecognizedType = FALSE; + + for( psAttrDef = psAttrSeq->psChild; + psAttrDef != NULL; + psAttrDef = psAttrDef->psNext ) + { + if( strcmp(psAttrDef->pszValue,"group") == 0 ) + { + /* Too complex schema for us. Aborts parsing */ + delete poClass; + return NULL; + } + + /* Parse stuff like : + + + + + as found in https://downloadagiv.blob.core.windows.net/overstromingsgebieden-en-oeverzones/2014_01/Overstromingsgebieden_en_oeverzones_2014_01_GML.zip + */ + if( strcmp(psAttrDef->pszValue,"choice") == 0 ) + { + CPLXMLNode* psChild = psAttrDef->psChild; + int bPolygon = FALSE; + int bMultiPolygon = FALSE; + for( ; psChild; psChild = psChild->psNext ) + { + if( psChild->eType != CXT_Element ) + continue; + if( strcmp(psChild->pszValue,"element") == 0 ) + { + const char* pszRef = CPLGetXMLValue( psChild, "ref", NULL ); + if( pszRef != NULL ) + { + if( strcmp(pszRef, "gml:polygonProperty") == 0 ) + bPolygon = TRUE; + else if( strcmp(pszRef, "gml:multiPolygonProperty") == 0 ) + bMultiPolygon = TRUE; + else + { + delete poClass; + return NULL; + } + } + else + { + delete poClass; + return NULL; + } + } + } + if( bPolygon && bMultiPolygon ) + { + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( + "", "", wkbMultiPolygon, nAttributeIndex, TRUE ) ); + + nAttributeIndex ++; + } + continue; + } + + if( !EQUAL(psAttrDef->pszValue,"element") ) + continue; + + /* MapServer WFS writes element type as an attribute of element */ + /* not as a simpleType definition */ + const char* pszType = CPLGetXMLValue( psAttrDef, "type", NULL ); + const char* pszElementName = CPLGetXMLValue( psAttrDef, "name", NULL ); + int bNullable = EQUAL(CPLGetXMLValue( psAttrDef, "minOccurs", "1" ), "0"); + const char* pszMaxOccurs = CPLGetXMLValue( psAttrDef, "maxOccurs", NULL ); + if (pszType != NULL) + { + const char* pszStrippedNSType = StripNS(pszType); + int nWidth = 0, nPrecision = 0; + + GMLPropertyType gmlType = GMLPT_Untyped; + if (EQUAL(pszStrippedNSType, "string") || + EQUAL(pszStrippedNSType, "Character")) + gmlType = GMLPT_String; + /* TODO: Would be nice to have a proper date type */ + else if (EQUAL(pszStrippedNSType, "date") || + EQUAL(pszStrippedNSType, "dateTime")) + gmlType = GMLPT_String; + else if (EQUAL(pszStrippedNSType, "real") || + EQUAL(pszStrippedNSType, "double") || + EQUAL(pszStrippedNSType, "decimal")) + gmlType = GMLPT_Real; + else if (EQUAL(pszStrippedNSType, "float") ) + gmlType = GMLPT_Float; + else if (EQUAL(pszStrippedNSType, "int") || + EQUAL(pszStrippedNSType, "integer")) + gmlType = GMLPT_Integer; + else if (EQUAL(pszStrippedNSType, "long")) + gmlType = GMLPT_Integer64; + else if (EQUAL(pszStrippedNSType, "short") ) + gmlType = GMLPT_Short; + else if (EQUAL(pszStrippedNSType, "boolean") ) + gmlType = GMLPT_Boolean; + else if (strcmp(pszType, "gml:FeaturePropertyType") == 0 ) + { + gmlType = GMLPT_FeatureProperty; + } + else if (strncmp(pszType, "gml:", 4) == 0) + { + const AssocNameType* psIter = apsPropertyTypes; + while(psIter->pszName) + { + if (strncmp(pszType + 4, psIter->pszName, strlen(psIter->pszName)) == 0) + { + OGRwkbGeometryType eType = psIter->eType; + + /* Look if there's a comment restricting to subclasses */ + if( psAttrDef->psNext != NULL && psAttrDef->psNext->eType == CXT_Comment ) + { + if( strstr(psAttrDef->psNext->pszValue, "restricted to Polygon") ) + eType = wkbPolygon; + else if( strstr(psAttrDef->psNext->pszValue, "restricted to LineString") ) + eType = wkbLineString; + else if( strstr(psAttrDef->psNext->pszValue, "restricted to MultiPolygon") ) + eType = wkbMultiPolygon; + else if( strstr(psAttrDef->psNext->pszValue, "restricted to MultiLineString") ) + eType = wkbMultiLineString; + } + + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( + pszElementName, pszElementName, eType, nAttributeIndex, bNullable ) ); + + nAttributeIndex ++; + + break; + } + + psIter ++; + } + + if (psIter->pszName == NULL) + { + /* Can be a non geometry gml type */ + /* Too complex schema for us. Aborts parsing */ + delete poClass; + return NULL; + } + + if (poClass->GetGeometryPropertyCount() == 0) + bGotUnrecognizedType = TRUE; + + continue; + } + + /* Integraph stuff */ + else if (strcmp(pszType, "G:Point_MultiPointPropertyType") == 0 || + strcmp(pszType, "gmgml:Point_MultiPointPropertyType") == 0) + { + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( + pszElementName, pszElementName, wkbMultiPoint, nAttributeIndex, + bNullable ) ); + + nAttributeIndex ++; + continue; + } + else if (strcmp(pszType, "G:LineString_MultiLineStringPropertyType") == 0 || + strcmp(pszType, "gmgml:LineString_MultiLineStringPropertyType") == 0) + { + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( + pszElementName, pszElementName, wkbMultiLineString, nAttributeIndex, + bNullable ) ); + + nAttributeIndex ++; + continue; + } + else if (strcmp(pszType, "G:Polygon_MultiPolygonPropertyType") == 0 || + strcmp(pszType, "gmgml:Polygon_MultiPolygonPropertyType") == 0 || + strcmp(pszType, "gmgml:Polygon_Surface_MultiSurface_CompositeSurfacePropertyType") == 0) + { + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( + pszElementName, pszElementName, wkbMultiPolygon, nAttributeIndex, + bNullable ) ); + + nAttributeIndex ++; + continue; + } + + /* ERDAS Apollo stuff (like in http://apollo.erdas.com/erdas-apollo/vector/WORLDWIDE?SERVICE=WFS&VERSION=1.0.0&REQUEST=DescribeFeatureType&TYPENAME=wfs:cntry98) */ + else if (strcmp(pszType, "wfs:MixedPolygonPropertyType") == 0) + { + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( + pszElementName, pszElementName, wkbMultiPolygon, nAttributeIndex, + bNullable) ); + + nAttributeIndex ++; + continue; + } + + else + { + gmlType = GMLPT_Untyped; + if ( ! LookForSimpleType(psSchemaNode, pszStrippedNSType, + &gmlType, &nWidth, &nPrecision) ) + { + /* Too complex schema for us. Aborts parsing */ + delete poClass; + return NULL; + } + } + + if (pszElementName == NULL) + pszElementName = "unnamed"; + const char* pszPropertyName = pszElementName; + if( gmlType == GMLPT_FeatureProperty ) + { + pszPropertyName = CPLSPrintf("%s_href", pszElementName); + } + GMLPropertyDefn *poProp = new GMLPropertyDefn( + pszPropertyName, pszElementName ); + + if( pszMaxOccurs != NULL && strcmp(pszMaxOccurs, "1") != 0 ) + gmlType = GetListTypeFromSingleType(gmlType); + + poProp->SetType( gmlType ); + poProp->SetWidth( nWidth ); + poProp->SetPrecision( nPrecision ); + poProp->SetNullable( bNullable ); + + if (poClass->AddProperty( poProp ) < 0) + delete poProp; + else + nAttributeIndex ++; + + continue; + } + + // For now we skip geometries .. fixup later. + CPLXMLNode* psSimpleType = CPLGetXMLNode( psAttrDef, "simpleType" ); + if( psSimpleType == NULL ) + { + const char* pszRef = CPLGetXMLValue( psAttrDef, "ref", NULL ); + + /* FME .xsd */ + if (pszRef != NULL && strncmp(pszRef, "gml:", 4) == 0) + { + const AssocNameType* psIter = apsRefTypes; + while(psIter->pszName) + { + if (strncmp(pszRef + 4, psIter->pszName, strlen(psIter->pszName)) == 0) + { + if (poClass->GetGeometryPropertyCount() > 0) + { + OGRwkbGeometryType eNewType = psIter->eType; + OGRwkbGeometryType eOldType = (OGRwkbGeometryType)poClass->GetGeometryProperty(0)->GetType(); + if ((eNewType == wkbMultiPoint && eOldType == wkbPoint) || + (eNewType == wkbMultiLineString && eOldType == wkbLineString) || + (eNewType == wkbMultiPolygon && eOldType == wkbPolygon)) + { + poClass->GetGeometryProperty(0)->SetType(eNewType); + } + else + { + CPLDebug("GML", "Geometry field already found ! Ignoring the following ones"); + } + } + else + { + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( + pszElementName, pszElementName, psIter->eType, nAttributeIndex, TRUE ) ); + + nAttributeIndex ++; + } + + break; + } + + psIter ++; + } + + if (psIter->pszName == NULL) + { + /* Can be a non geometry gml type */ + /* Too complex schema for us. Aborts parsing */ + delete poClass; + return NULL; + } + + if (poClass->GetGeometryPropertyCount() == 0) + bGotUnrecognizedType = TRUE; + + continue; + } + + /* Parse stuff like the following found in http://199.29.1.81:8181/miwfs/GetFeature.ashx?REQUEST=GetFeature&MAXFEATURES=1&SERVICE=WFS&VERSION=1.0.0&TYPENAME=miwfs:World : + + + + + + + + */ + CPLXMLNode* psComplexType = GetSingleChildElement( psAttrDef, "complexType" ); + CPLXMLNode* psComplexTypeSequence = GetSingleChildElement( psComplexType, "sequence" ); + CPLXMLNode* psComplexTypeSequenceElement = GetSingleChildElement( psComplexTypeSequence, "element" ); + + if( pszElementName != NULL && + CheckMinMaxOccursCardinality(psAttrDef) && + psComplexTypeSequenceElement != NULL && + CheckMinMaxOccursCardinality(psComplexTypeSequence) && + strcmp(CPLGetXMLValue( psComplexTypeSequenceElement, "ref", "" ), "gml:_Geometry") == 0 ) + { + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( + pszElementName, pszElementName, wkbUnknown, nAttributeIndex, bNullable ) ); + + nAttributeIndex ++; + + continue; + } + else + { + /* Too complex schema for us. Aborts parsing */ + delete poClass; + return NULL; + } + } + + if (pszElementName == NULL) + pszElementName = "unnamed"; + GMLPropertyDefn *poProp = new GMLPropertyDefn( + pszElementName, pszElementName ); + + GMLPropertyType eType = GMLPT_Untyped; + int nWidth = 0, nPrecision = 0; + GetSimpleTypeProperties(psSimpleType, &eType, &nWidth, &nPrecision); + + if( pszMaxOccurs != NULL && strcmp(pszMaxOccurs, "1") != 0 ) + eType = GetListTypeFromSingleType(eType); + + poProp->SetType( eType ); + poProp->SetWidth( nWidth ); + poProp->SetPrecision( nPrecision ); + poProp->SetNullable( bNullable ); + + if (poClass->AddProperty( poProp ) < 0) + delete poProp; + else + nAttributeIndex ++; + } + + /* If we have found an unknown types, let's be on the side of caution and */ + /* create a geometry field */ + if( poClass->GetGeometryPropertyCount() == 0 && + bGotUnrecognizedType ) + { + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( "", "", wkbUnknown, -1, TRUE ) ); + } + +/* -------------------------------------------------------------------- */ +/* Class complete, add to reader class list. */ +/* -------------------------------------------------------------------- */ + poClass->SetSchemaLocked( TRUE ); + + return poClass; +} + +/************************************************************************/ +/* GMLParseXMLFile() */ +/************************************************************************/ + +static +CPLXMLNode* GMLParseXMLFile(const char* pszFilename) +{ + if( strncmp(pszFilename, "http://", 7) == 0 || + strncmp(pszFilename, "https://", 8) == 0 ) + { + CPLXMLNode* psRet = NULL; + CPLHTTPResult* psResult = CPLHTTPFetch( pszFilename, NULL ); + if( psResult != NULL ) + { + if( psResult->pabyData != NULL ) + { + psRet = CPLParseXMLString( (const char*) psResult->pabyData ); + } + CPLHTTPDestroyResult(psResult); + } + return psRet; + } + else + { + return CPLParseXMLFile(pszFilename); + } +} + +/************************************************************************/ +/* CPLGetFirstChildNode() */ +/************************************************************************/ + +static +CPLXMLNode* CPLGetFirstChildNode( CPLXMLNode* psNode ) +{ + if( psNode == NULL ) + return NULL; + CPLXMLNode* psIter = psNode->psChild; + while( psIter != NULL ) + { + if( psIter->eType == CXT_Element ) + return psIter; + psIter = psIter->psNext; + } + return NULL; +} + +/************************************************************************/ +/* CPLGetLastNode() */ +/************************************************************************/ + +static +CPLXMLNode* CPLGetLastNode( CPLXMLNode* psNode ) +{ + if( psNode == NULL ) + return NULL; + CPLXMLNode* psIter = psNode; + while( psIter->psNext != NULL ) + psIter = psIter->psNext; + return psIter; +} + +/************************************************************************/ +/* CPLXMLSchemaResolveInclude() */ +/************************************************************************/ + +static +void CPLXMLSchemaResolveInclude( const char* pszMainSchemaLocation, + CPLXMLNode *psSchemaNode ) +{ + std::set osAlreadyIncluded; + + int bTryAgain; + do + { + CPLXMLNode *psThis; + CPLXMLNode *psLast = NULL; + bTryAgain = FALSE; + + for( psThis = psSchemaNode->psChild; + psThis != NULL; psThis = psThis->psNext ) + { + if( psThis->eType == CXT_Element && + EQUAL(psThis->pszValue,"include") ) + { + const char* pszSchemaLocation = + CPLGetXMLValue(psThis, "schemaLocation", NULL); + if( pszSchemaLocation != NULL && + osAlreadyIncluded.count( pszSchemaLocation) == 0 ) + { + osAlreadyIncluded.insert( pszSchemaLocation ); + + if( strncmp(pszSchemaLocation, "http://", 7) != 0 && + strncmp(pszSchemaLocation, "https://", 8) != 0 && + CPLIsFilenameRelative(pszSchemaLocation ) ) + { + pszSchemaLocation = CPLFormFilename( + CPLGetPath(pszMainSchemaLocation), pszSchemaLocation, NULL ); + } + + CPLXMLNode *psIncludedXSDTree = + GMLParseXMLFile( pszSchemaLocation ); + if( psIncludedXSDTree != NULL ) + { + CPLStripXMLNamespace( psIncludedXSDTree, NULL, TRUE ); + CPLXMLNode *psIncludedSchemaNode = + CPLGetXMLNode( psIncludedXSDTree, "=schema" ); + if( psIncludedSchemaNode != NULL ) + { + /* Substitute de node by its content */ + CPLXMLNode* psFirstChildElement = + CPLGetFirstChildNode(psIncludedSchemaNode); + if( psFirstChildElement != NULL ) + { + CPLXMLNode* psCopy = CPLCloneXMLTree(psFirstChildElement); + if( psLast != NULL ) + psLast->psNext = psCopy; + else + psSchemaNode->psChild = psCopy; + CPLXMLNode* psNext = psThis->psNext; + psThis->psNext = NULL; + CPLDestroyXMLNode(psThis); + psThis = CPLGetLastNode(psCopy); + psThis->psNext = psNext; + + /* In case the included schema also contains */ + /* includes */ + bTryAgain = TRUE; + } + + } + CPLDestroyXMLNode( psIncludedXSDTree ); + } + } + } + + psLast = psThis; + } + } while( bTryAgain ); + + const char* pszSchemaOutputName = + CPLGetConfigOption("GML_SCHEMA_OUTPUT_NAME", NULL); + if( pszSchemaOutputName != NULL ) + { + CPLSerializeXMLTreeToFile( psSchemaNode, pszSchemaOutputName ); + } +} + +/************************************************************************/ +/* GMLParseXSD() */ +/************************************************************************/ + +int GMLParseXSD( const char *pszFile, + std::vector & aosClasses, + int& bFullyUnderstood) + +{ + bFullyUnderstood = FALSE; + + if( pszFile == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Load the raw XML file. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psXSDTree = GMLParseXMLFile( pszFile ); + + if( psXSDTree == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Strip off any namespace qualifiers. */ +/* -------------------------------------------------------------------- */ + CPLStripXMLNamespace( psXSDTree, NULL, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Find root element. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psSchemaNode = CPLGetXMLNode( psXSDTree, "=schema" ); + if( psSchemaNode == NULL ) + { + CPLDestroyXMLNode( psXSDTree ); + return FALSE; + } + +/* ==================================================================== */ +/* Process each include directive. */ +/* ==================================================================== */ + CPLXMLSchemaResolveInclude( pszFile, psSchemaNode ); + + //CPLSerializeXMLTreeToFile(psSchemaNode, "/vsistdout/"); + + bFullyUnderstood = TRUE; + +/* ==================================================================== */ +/* Process each feature class definition. */ +/* ==================================================================== */ + CPLXMLNode *psThis; + + for( psThis = psSchemaNode->psChild; + psThis != NULL; psThis = psThis->psNext ) + { +/* -------------------------------------------------------------------- */ +/* Check for node. */ +/* -------------------------------------------------------------------- */ + if( psThis->eType != CXT_Element + || !EQUAL(psThis->pszValue,"element") ) + continue; + +/* -------------------------------------------------------------------- */ +/* Check the substitution group. */ +/* -------------------------------------------------------------------- */ + const char *pszSubGroup = + StripNS(CPLGetXMLValue(psThis,"substitutionGroup","")); + + // Old OGR produced elements for the feature collection. + if( EQUAL(pszSubGroup, "_FeatureCollection") ) + continue; + + if( !EQUAL(pszSubGroup, "_Feature") && + !EQUAL(pszSubGroup, "AbstractFeature") /* AbstractFeature used by GML 3.2 */ ) + { + continue; + } + +/* -------------------------------------------------------------------- */ +/* Get name */ +/* -------------------------------------------------------------------- */ + const char *pszName; + + pszName = CPLGetXMLValue( psThis, "name", NULL ); + if( pszName == NULL ) + { + continue; + } + +/* -------------------------------------------------------------------- */ +/* Get type and verify relationship with name. */ +/* -------------------------------------------------------------------- */ + const char *pszType; + + pszType = CPLGetXMLValue( psThis, "type", NULL ); + if (pszType == NULL) + { + CPLXMLNode *psComplexType = CPLGetXMLNode( psThis, "complexType" ); + if (psComplexType) + { + GMLFeatureClass* poClass = + GMLParseFeatureType(psSchemaNode, pszName, psComplexType); + if (poClass) + aosClasses.push_back(poClass); + else + bFullyUnderstood = FALSE; + } + continue; + } + if( strstr( pszType, ":" ) != NULL ) + pszType = strstr( pszType, ":" ) + 1; + if( EQUAL(pszType, pszName) ) + { + /* A few WFS servers return a type name which is the element name */ + /* without any _Type or Type suffix */ + /* e.g. : http://apollo.erdas.com/erdas-apollo/vector/Cherokee?SERVICE=WFS&VERSION=1.0.0&REQUEST=DescribeFeatureType&TYPENAME=iwfs:Air */ + } + + /* */ + else if( strlen(pszType) > 4 && + strcmp(pszType + strlen(pszType) - 4, "Type") == 0 && + strlen(pszName) > strlen(pszType) - 4 && + strncmp(pszName + strlen(pszName) - (strlen(pszType) - 4), + pszType, + strlen(pszType) - 4) == 0 ) + { + } + + else if( !EQUALN(pszType,pszName,strlen(pszName)) + || !(EQUAL(pszType+strlen(pszName),"_Type") || + EQUAL(pszType+strlen(pszName),"Type")) ) + { + continue; + } + + /* CanVec .xsd contains weird types that are not used in the related GML */ + if (strncmp(pszName, "XyZz", 4) == 0 || + strncmp(pszName, "XyZ1", 4) == 0 || + strncmp(pszName, "XyZ2", 4) == 0) + continue; + + GMLFeatureClass* poClass = + GMLParseFeatureType(psSchemaNode, pszName, pszType); + if (poClass) + aosClasses.push_back(poClass); + else + bFullyUnderstood = FALSE; + } + + CPLDestroyXMLNode( psXSDTree ); + + if( aosClasses.size() > 0 ) + { + return TRUE; + } + else + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/parsexsd.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/parsexsd.h new file mode 100644 index 000000000..125d64ade --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/parsexsd.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * $Id: parsexsd.h 28909 2015-04-15 12:52:53Z rouault $ + * + * Project: GML Reader + * Purpose: Implementation of GMLParseXSD() + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _PARSEXSD_H_INCLUDED +#define _PARSEXSD_H_INCLUDED + +#include +#include "gmlreader.h" + +int GMLParseXSD( const char *pszFile, + std::vector & aosClasses, + int& bFullyUnderstood ); + +#endif // _PARSEXSD_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/resolvexlinks.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/resolvexlinks.cpp new file mode 100644 index 000000000..984bb9b40 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/resolvexlinks.cpp @@ -0,0 +1,631 @@ +/****************************************************************************** + * $Id: resolvexlinks.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GML Reader + * Purpose: Implementation of GMLReader::ResolveXlinks() method. + * Author: Chaitanya kumar CH, chaitanya@osgeo.in + * + ****************************************************************************** + * Copyright (c) 2010, Chaitanya kumar CH + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gmlreader.h" +#include "cpl_error.h" + +CPL_CVSID("$Id: resolvexlinks.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +#include "gmlreaderp.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_http.h" + +#include + +/************************************************************************/ +/* GetID() */ +/* */ +/* Returns the reference to the gml:id of psNode. NULL if not */ +/* found. */ +/************************************************************************/ + +static const char* GetID( CPLXMLNode * psNode ) + +{ + if( psNode == NULL ) + return NULL; + + CPLXMLNode *psChild; + for( psChild = psNode->psChild; psChild != NULL; psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Attribute + && EQUAL(psChild->pszValue, "gml:id") ) + { + return psChild->psChild->pszValue; + } + } + return NULL; +} + +/************************************************************************/ +/* CompareNodeIDs() */ +/* */ +/* Compares two nodes by their IDs */ +/************************************************************************/ + +/*static int CompareNodeIDs( CPLXMLNode * psNode1, CPLXMLNode * psNode2 ) + +{ + if( psNode2 == NULL ) + return TRUE; + + if( psNode1 == NULL ) + return FALSE; + + return ( strcmp( GetID(psNode2), GetID(psNode1) ) > 0 ); +}*/ + +/************************************************************************/ +/* BuildIDIndex() */ +/* */ +/* Returns an array of nodes sorted by their gml:id strings */ +/* XXX: This method can be used to build an array of pointers to */ +/* nodes sorted by their id values. */ +/************************************************************************/ +/* +static std::vector BuildIDIndex( CPLXMLNode* psNode, + std::vector &apsNode ) + +{ + CPLXMLNode *psSibling; + for( psSibling = psNode; psSibling != NULL; psSibling = psSibling->psNext ) + { + if( GetID( psSibling ) != NULL ) + apsNode.push_back( psSibling ); + BuildIDIndex( psNode->psChild, apsNode ); + } + return NULL; +}*/ + +/************************************************************************/ +/* FindElementByID() */ +/* */ +/* Find a node with the indicated "gml:id" in the node tree and */ +/* it's siblings. */ +/************************************************************************/ + +static CPLXMLNode *FindElementByID( CPLXMLNode * psRoot, + const char *pszID ) + +{ + if( psRoot == NULL ) + return NULL; + + CPLXMLNode *psSibling, *psReturn = NULL; + +// check for id attribute + for( psSibling = psRoot; psSibling != NULL; psSibling = psSibling->psNext) + { + if( psSibling->eType == CXT_Element ) + { + // check that sibling for id value + const char* pszIDOfSibling = GetID( psSibling ); + if( pszIDOfSibling != NULL && EQUAL( pszIDOfSibling, pszID) ) + return psSibling; + } + } + +// search the child elements of all the psRoot's siblings + for( psSibling = psRoot; psSibling != NULL; psSibling = psSibling->psNext) + { + if( psSibling->eType == CXT_Element ) + { + psReturn = FindElementByID( psSibling->psChild, pszID ); + if( psReturn != NULL ) + return psReturn; + } + } + return NULL; +} + +/************************************************************************/ +/* RemoveIDs() */ +/* */ +/* Remove all the gml:id nodes. Doesn't check psRoot's siblings */ +/************************************************************************/ + +static void RemoveIDs( CPLXMLNode * psRoot ) + +{ + if( psRoot == NULL ) + return; + + CPLXMLNode *psChild = psRoot->psChild; + +// check for id attribute + while( psChild != NULL && !( psChild->eType == CXT_Attribute && EQUAL(psChild->pszValue, "gml:id"))) + psChild = psChild->psNext; + CPLRemoveXMLChild( psRoot, psChild ); + CPLDestroyXMLNode( psChild ); + +// search the child elements of psRoot + for( psChild = psRoot->psChild; psChild != NULL; psChild = psChild->psNext) + if( psChild->eType == CXT_Element ) + RemoveIDs( psChild ); +} + +/************************************************************************/ +/* TrimTree() */ +/* */ +/* Remove all nodes without a gml:id node in the descendents. */ +/* Returns TRUE if there is a gml:id node in the descendents. */ +/************************************************************************/ + +static int TrimTree( CPLXMLNode * psRoot ) + +{ + if( psRoot == NULL ) + return FALSE; + + CPLXMLNode *psChild = psRoot->psChild; + +// check for id attribute + while( psChild != NULL && !( psChild->eType == CXT_Attribute && EQUAL(psChild->pszValue, "gml:id"))) + psChild = psChild->psNext; + + if( psChild != NULL ) + return TRUE; + +// search the child elements of psRoot + int bReturn = FALSE, bRemove; + for( psChild = psRoot->psChild; psChild != NULL;) + { + CPLXMLNode* psNextChild = psChild->psNext; + if( psChild->eType == CXT_Element ) + { + bRemove = TrimTree( psChild ); + if( bRemove ) + { + bReturn = bRemove; + } + else + { + //remove this child + CPLRemoveXMLChild( psRoot, psChild ); + CPLDestroyXMLNode( psChild ); + } + } + + psChild = psNextChild; + } + return bReturn; +} + +/************************************************************************/ +/* CorrectURLs() */ +/* */ +/* Processes the node and all it's children recursively. Siblings of */ +/* psRoot are ignored. */ +/* - Replaces all every URL in URL#id pairs with pszURL. */ +/* - Leaves it alone if the paths are same or the URL is not relative. */ +/* - If it is relative, the path from pszURL is prepended. */ +/************************************************************************/ + +static void CorrectURLs( CPLXMLNode * psRoot, const char *pszURL ) + +{ + if( psRoot == NULL || pszURL == NULL ) + return; + if( pszURL[0] == '\0' ) + return; + + CPLXMLNode *psChild = psRoot->psChild; + +// check for xlink:href attribute + while( psChild != NULL && !( ( psChild->eType == CXT_Attribute ) && + ( EQUAL(psChild->pszValue, "xlink:href") )) ) + psChild = psChild->psNext; + + if( psChild != NULL && + !( strstr( psChild->psChild->pszValue, pszURL ) == psChild->psChild->pszValue + && psChild->psChild->pszValue[strlen(pszURL)] == '#' ) ) + { + //href has a different url + size_t nLen; + char *pszNew; + if( psChild->psChild->pszValue[0] == '#' ) + { + //empty URL: prepend the given URL + nLen = CPLStrnlen( pszURL, 1024 ) + + CPLStrnlen( psChild->psChild->pszValue, 1024 ) + 1; + pszNew = (char *)CPLMalloc( nLen * sizeof(char)); + CPLStrlcpy( pszNew, pszURL, nLen ); + CPLStrlcat( pszNew, psChild->psChild->pszValue, nLen ); + CPLSetXMLValue( psRoot, "#xlink:href", pszNew ); + CPLFree( pszNew ); + } + else + { + size_t nPathLen; + for( nPathLen = strlen(pszURL); + nPathLen > 0 && pszURL[nPathLen - 1] != '/' + && pszURL[nPathLen - 1] != '\\'; + nPathLen--); + + const char* pszDash = strchr( psChild->psChild->pszValue, '#' ); + if( pszDash != NULL && + strncmp( pszURL, psChild->psChild->pszValue, nPathLen ) != 0 ) + { + //different path + int nURLLen = pszDash - psChild->psChild->pszValue; + char *pszURLWithoutID = (char *)CPLMalloc( (nURLLen+1) * sizeof(char)); + strncpy( pszURLWithoutID, psChild->psChild->pszValue, nURLLen ); + pszURLWithoutID[nURLLen] = '\0'; + + if( CPLIsFilenameRelative( pszURLWithoutID ) && + strstr( pszURLWithoutID, ":" ) == NULL ) + { + //relative URL: prepend the path of pszURL + nLen = nPathLen + + CPLStrnlen( psChild->psChild->pszValue, 1024 ) + 1; + pszNew = (char *)CPLMalloc( nLen * sizeof(char)); + size_t i; + for( i = 0; i < nPathLen; i++ ) + pszNew[i] = pszURL[i]; + pszNew[nPathLen] = '\0'; + CPLStrlcat( pszNew, psChild->psChild->pszValue, nLen ); + CPLSetXMLValue( psRoot, "#xlink:href", pszNew ); + CPLFree( pszNew ); + } + CPLFree( pszURLWithoutID ); + } + } + } + +// search the child elements of psRoot + for( psChild = psRoot->psChild; psChild != NULL; psChild = psChild->psNext) + if( psChild->eType == CXT_Element ) + CorrectURLs( psChild, pszURL ); +} + +/************************************************************************/ +/* FindTreeByURL() */ +/* */ +/* Find a doc tree that is located at pszURL. */ +/* If not present in ppapsRoot, it updates it and ppapszResourceHREF. */ +/************************************************************************/ + +static CPLXMLNode *FindTreeByURL( CPLXMLNode *** ppapsRoot, + char *** ppapszResourceHREF, + const char *pszURL ) + +{ + if( *ppapsRoot == NULL || ppapszResourceHREF == NULL ) + return NULL; + +//if found in ppapszResourceHREF + int i, nItems; + char *pszLocation; + if( ( i = CSLFindString( *ppapszResourceHREF, pszURL )) >= 0 ) + { + //return corresponding psRoot + return (*ppapsRoot)[i]; + } + else + { + CPLXMLNode *psSrcTree = NULL, *psSibling; + pszLocation = CPLStrdup( pszURL ); + //if it is part of filesystem + if( CPLCheckForFile( pszLocation, NULL) ) + {//filesystem + psSrcTree = CPLParseXMLFile( pszURL ); + } + else if( CPLHTTPEnabled() ) + {//web resource + CPLErrorReset(); + CPLHTTPResult *psResult = CPLHTTPFetch( pszURL, NULL ); + if( psResult != NULL ) + { + if( psResult->nDataLen > 0 && CPLGetLastErrorNo() == 0) + psSrcTree = CPLParseXMLString( (const char*)psResult->pabyData ); + CPLHTTPDestroyResult( psResult ); + } + } + + //report error in case the resource cannot be retrieved. + if( psSrcTree == NULL ) + CPLError( CE_Failure, CPLE_NotSupported, + "Could not access %s", pszLocation ); + + CPLFree( pszLocation ); + +/************************************************************************/ +/* In the external GML resource we will only need elements */ +/* identified by a "gml:id". So trim them. */ +/************************************************************************/ + psSibling = psSrcTree; + while( psSibling != NULL ) + { + TrimTree( psSibling ); + psSibling = psSibling->psNext; + } + + //update to lists + nItems = CSLCount(*ppapszResourceHREF); + *ppapszResourceHREF = CSLAddString( *ppapszResourceHREF, pszURL ); + *ppapsRoot = (CPLXMLNode**)CPLRealloc(*ppapsRoot, + (nItems+2)*sizeof(CPLXMLNode*)); + (*ppapsRoot)[nItems] = psSrcTree; + (*ppapsRoot)[nItems+1] = NULL; + + //return the tree + return (*ppapsRoot)[nItems]; + } +} + +/************************************************************************/ +/* ResolveTree() */ +/* Resolves the xlinks in a node and it's siblings */ +/* If any error is encountered or any element is skipped(papszSkip): */ +/* If bStrict is TRUE, process is stopped and CE_Error is returned */ +/* If bStrict is FALSE, the process is continued but CE_Warning is */ +/* returned at the end. */ +/* If everything goes fine, CE_None is returned. */ +/************************************************************************/ + +static CPLErr Resolve( CPLXMLNode * psNode, + CPLXMLNode *** ppapsRoot, + char *** ppapszResourceHREF, + char ** papszSkip, + const int bStrict ) + +{ + //for each sibling + CPLXMLNode *psSibling = NULL; + CPLXMLNode *psResource = NULL; + CPLXMLNode *psTarget = NULL; + CPLErr eReturn = CE_None, eReturned; + + for( psSibling = psNode; psSibling != NULL; psSibling = psSibling->psNext ) + { + if( psSibling->eType != CXT_Element ) + continue; + + CPLXMLNode *psChild = psSibling->psChild; + while( psChild != NULL && + !( psChild->eType == CXT_Attribute && + EQUAL( psChild->pszValue, "xlink:href" ) ) ) + psChild = psChild->psNext; + + //if a child has a "xlink:href" attribute + if( psChild != NULL && psChild->psChild != NULL ) + { + if( CSLFindString( papszSkip, psSibling->pszValue ) >= 0 ) + {//Skipping a specified element + eReturn = CE_Warning; + continue; + } + + static int i = 0; + if( i-- == 0 ) + {//a way to track progress + i = 256; + CPLDebug( "GML", + "Resolving xlinks... (currently %s)", + psChild->psChild->pszValue ); + } + + char **papszTokens; + papszTokens = CSLTokenizeString2( psChild->psChild->pszValue, "#", + CSLT_ALLOWEMPTYTOKENS | + CSLT_STRIPLEADSPACES | + CSLT_STRIPENDSPACES ); + if( CSLCount( papszTokens ) != 2 || strlen(papszTokens[1]) <= 0 ) + { + CPLError( bStrict ? CE_Failure : CE_Warning, + CPLE_NotSupported, + "Error parsing the href %s.%s", + psChild->psChild->pszValue, + bStrict ? "" : " Skipping..." ); + CSLDestroy( papszTokens ); + if( bStrict ) + return CE_Failure; + eReturn = CE_Warning; + continue; + } + + //look for the resource with that URL + psResource = FindTreeByURL( ppapsRoot, + ppapszResourceHREF, + papszTokens[0] ); + if( psResource == NULL ) + { + CSLDestroy( papszTokens ); + if( bStrict ) + return CE_Failure; + eReturn = CE_Warning; + continue; + } + + //look for the element with the ID + psTarget = FindElementByID( psResource, papszTokens[1] ); + if( psTarget != NULL ) + { + //remove the xlink:href attribute + CPLRemoveXMLChild( psSibling, psChild ); + CPLDestroyXMLNode( psChild ); + + //make a copy of psTarget + CPLXMLNode *psCopy = CPLCreateXMLNode( NULL, + CXT_Element, + psTarget->pszValue ); + psCopy->psChild = CPLCloneXMLTree( psTarget->psChild ); + RemoveIDs( psCopy ); + //correct empty URLs in URL#id pairs + if( CPLStrnlen( papszTokens[0], 1 ) > 0 ) + { + CorrectURLs( psCopy, papszTokens[0] ); + } + CPLAddXMLChild( psSibling, psCopy ); + CSLDestroy( papszTokens ); + } + else + { + //element not found + CSLDestroy( papszTokens ); + CPLError( bStrict ? CE_Failure : CE_Warning, + CPLE_ObjectNull, + "Couldn't find the element with id %s.", + psChild->psChild->pszValue ); + if( bStrict ) + return CE_Failure; + eReturn = CE_Warning; + } + } + + //Recurse with the first child + eReturned=Resolve( psSibling->psChild, + ppapsRoot, + ppapszResourceHREF, + papszSkip, + bStrict ); + + if( eReturned == CE_Failure ) + return CE_Failure; + + if( eReturned == CE_Warning ) + eReturn = CE_Warning; + } + return eReturn; +} + +/************************************************************************/ +/* ResolveXlinks() */ +/* Returns TRUE for success */ +/* - Returns CE_None for success, */ +/* CE_Warning if the resolved file is saved to a different file or */ +/* CE_Failure if it could not be saved at all. */ +/* - m_pszFilename will be set to the file the resolved file was */ +/* saved to. */ +/************************************************************************/ + +int GMLReader::ResolveXlinks( const char *pszFile, + int* pbOutIsTempFile, + char **papszSkip, + const int bStrict) + +{ + *pbOutIsTempFile = FALSE; + +// Check if the original source file is set. + if( m_pszFilename == NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "GML source file needs to be set first with " + "GMLReader::SetSourceFile()." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Load the raw XML file into a XML Node tree. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode **papsSrcTree; + papsSrcTree = (CPLXMLNode **)CPLCalloc( 2, sizeof(CPLXMLNode *)); + papsSrcTree[0] = CPLParseXMLFile( m_pszFilename ); + + if( papsSrcTree[0] == NULL ) + { + CPLFree(papsSrcTree); + return FALSE; + } + + //make all the URLs absolute + CPLXMLNode *psSibling = NULL; + for( psSibling = papsSrcTree[0]; psSibling != NULL; psSibling = psSibling->psNext ) + CorrectURLs( psSibling, m_pszFilename ); + + //setup resource data structure + char **papszResourceHREF = NULL; + // "" is the href of the original source file + papszResourceHREF = CSLAddString( papszResourceHREF, m_pszFilename ); + + //call resolver + CPLErr eReturned = CE_None; + eReturned = Resolve( papsSrcTree[0], &papsSrcTree, &papszResourceHREF, papszSkip, bStrict ); + + int bReturn = TRUE; + if( eReturned != CE_Failure ) + { + char *pszTmpName = NULL; + int bTryWithTempFile = FALSE; + if( EQUALN(pszFile, "/vsitar/", strlen("/vsitar/")) || + EQUALN(pszFile, "/vsigzip/", strlen("/vsigzip/")) || + EQUALN(pszFile, "/vsizip/", strlen("/vsizip/")) ) + { + bTryWithTempFile = TRUE; + } + else if( !CPLSerializeXMLTreeToFile( papsSrcTree[0], pszFile ) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Cannot serialize resolved file %s to %s.", + m_pszFilename, pszFile ); + bTryWithTempFile = TRUE; + } + + if (bTryWithTempFile) + { + pszTmpName = CPLStrdup( CPLGenerateTempFilename( "ResolvedGML" ) ); + if( !CPLSerializeXMLTreeToFile( papsSrcTree[0], pszTmpName ) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Cannot serialize resolved file %s to %s either.", + m_pszFilename, pszTmpName ); + CPLFree( pszTmpName ); + bReturn = FALSE; + } + else + { + //set the source file to the resolved file + CPLFree( m_pszFilename ); + m_pszFilename = pszTmpName; + *pbOutIsTempFile = TRUE; + } + } + else + { + //set the source file to the resolved file + CPLFree( m_pszFilename ); + m_pszFilename = CPLStrdup( pszFile ); + } + } + else + { + bReturn = FALSE; + } + + int nItems = CSLCount( papszResourceHREF ); + CSLDestroy( papszResourceHREF ); + while( nItems > 0 ) + CPLDestroyXMLNode( papsSrcTree[--nItems] ); + CPLFree( papsSrcTree ); + + return bReturn; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/trstring.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/trstring.cpp new file mode 100644 index 000000000..def831c02 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gml/trstring.cpp @@ -0,0 +1,236 @@ +/****************************************************************************** + * $Id: trstring.cpp 22205 2011-04-18 21:17:30Z rouault $ + * + * Project: GML Reader + * Purpose: Functions for translating back and forth between XMLCh and char. + * We assume that XMLCh is a simple numeric type that we can + * correspond 1:1 with char values, but that it likely is larger + * than a char. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifdef HAVE_XERCES + +#include "gmlreaderp.h" +#include "cpl_vsi.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +/************************************************************************/ +/* tr_isascii() */ +/************************************************************************/ + +static int tr_isascii( const char * pszCString ) + +{ + while( *pszCString != '\0' ) + { + if( *((unsigned char *) pszCString) > 127 ) + return FALSE; + + pszCString++; + } + + return TRUE; +} + +/************************************************************************/ +/* tr_strcmp() */ +/************************************************************************/ + +int tr_strcmp( const char *pszCString, const XMLCh *panXMLString ) + +{ + int i = 0; + +/* -------------------------------------------------------------------- */ +/* Fast (ASCII) comparison case. */ +/* -------------------------------------------------------------------- */ + if( tr_isascii( pszCString ) ) + { + while( pszCString[i] != 0 && panXMLString[i] != 0 + && pszCString[i] == panXMLString[i] ) {} + + if( pszCString[i] == 0 && panXMLString[i] == 0 ) + return 0; + else if( pszCString[i] < panXMLString[i] ) + return -1; + else + return 1; + } + +/* -------------------------------------------------------------------- */ +/* Translated UTF8 to XMLCh for comparison. */ +/* -------------------------------------------------------------------- */ + XMLCh *panFirst = (XMLCh *) CPLCalloc(strlen(pszCString)+1,sizeof(XMLCh)); + + tr_strcpy( panFirst, pszCString ); + + while( panFirst[i] != 0 && panXMLString[i] != 0 + && panFirst[i] == panXMLString[i] ) {} + + if( panFirst[i] == 0 && panXMLString[i] == 0 ) + { + CPLFree( panFirst ); + return 0; + } + else if( panFirst[i] < panXMLString[i] ) + { + CPLFree( panFirst ); + return -1; + } + else + { + CPLFree( panFirst ); + return 1; + } +} + +/************************************************************************/ +/* tr_strcpy(const char*,const XMLCh*) */ +/************************************************************************/ + +void tr_strcpy( XMLCh *panXMLString, const char *pszCString ) + +{ +/* -------------------------------------------------------------------- */ +/* Simple (ASCII) case. */ +/* -------------------------------------------------------------------- */ + if( tr_isascii( pszCString ) ) + { + while( *pszCString != 0 ) + *(panXMLString++) = *(pszCString++); + *panXMLString = 0; + return; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise we need to do a full UTC2 to UTF-8 conversion. */ +/* -------------------------------------------------------------------- */ + int i; + wchar_t *pwszUTF16; + + pwszUTF16 = CPLRecodeToWChar( pszCString, CPL_ENC_UTF8, "WCHAR_T" ); + + for( i = 0; pwszUTF16[i] != 0; i++ ) + panXMLString[i] = pwszUTF16[i]; + + panXMLString[i] = 0; + + CPLFree( pwszUTF16 ); +} + +/************************************************************************/ +/* tr_strcpy(const XMLCh*,const char*) */ +/************************************************************************/ + +void tr_strcpy( char *pszCString, const XMLCh *panXMLString ) + +{ + int bSimpleASCII = TRUE; + const XMLCh *panXMLStringOriginal = panXMLString; + char *pszCStringOriginal = pszCString; + + while( *panXMLString != 0 ) + { + if( *panXMLString > 127 ) + bSimpleASCII = FALSE; + + *(pszCString++) = (char) *(panXMLString++); + } + *pszCString = 0; + + if( bSimpleASCII ) + return; + + panXMLString = panXMLStringOriginal; + pszCString = pszCStringOriginal; + +/* -------------------------------------------------------------------- */ +/* The simple translation was wrong, because the source is not */ +/* all simple ASCII characters. Redo using the more expensive */ +/* recoding API. */ +/* -------------------------------------------------------------------- */ + int i; + wchar_t *pwszSource = (wchar_t *) CPLCalloc(sizeof(wchar_t), + tr_strlen(panXMLStringOriginal)+1 ); + for( i = 0; panXMLString[i] != 0; i++ ) + pwszSource[i] = panXMLString[i]; + pwszSource[i] = 0; + + char *pszResult = CPLRecodeFromWChar( pwszSource, + "WCHAR_T", CPL_ENC_UTF8 ); + + strcpy( pszCString, pszResult ); + + CPLFree( pwszSource ); + CPLFree( pszResult ); +} + +/************************************************************************/ +/* tr_strlen() */ +/************************************************************************/ + +int tr_strlen( const XMLCh *panXMLString ) + +{ + int nLength = 0; + + while( *(panXMLString++) != 0 ) + nLength++; + + return nLength; +} + +/************************************************************************/ +/* tr_strdup() */ +/************************************************************************/ + +char *tr_strdup( const XMLCh *panXMLString ) + +{ +/* -------------------------------------------------------------------- */ +/* Compute maximum length. */ +/* -------------------------------------------------------------------- */ + int i, nMaxLen = 1; + + for( i = 0; panXMLString[i] != 0; i++ ) + { + if( panXMLString[i] < 128 ) + nMaxLen++; + else if( panXMLString[i] < 0x7ff ) + nMaxLen += 2; + else + nMaxLen += 4; + } + +/* -------------------------------------------------------------------- */ +/* Do the translation. */ +/* -------------------------------------------------------------------- */ + char *pszResult = (char *) CPLMalloc(nMaxLen); + tr_strcpy( pszResult, panXMLString ); + return pszResult; +} + +#endif // HAVE_XERCES diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/GNUmakefile new file mode 100644 index 000000000..fcd40a2c4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/GNUmakefile @@ -0,0 +1,11 @@ + +include ../../../GDALmake.opt + +OBJ = ogrgmtdriver.o ogrgmtdatasource.o ogrgmtlayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/drv_gmt.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/drv_gmt.html new file mode 100644 index 000000000..e2bcede48 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/drv_gmt.html @@ -0,0 +1,32 @@ + + +GMT ASCII Vectors (.gmt) + + + + +

    GMT ASCII Vectors (.gmt)

    + +OGR supports reading and writing GMT ASCII vector format. This is the +format used by the Generic Mapping Tools (GMT) package, and includes +recent additions to the format to handle more geometry types, and attributes. +Currently GMT files are only supported if they have the extension ".gmt". + +Old (simple) GMT files are treated as either point, or linestring files +depending on whether a ">" line is encountered before the first vertex. +New style files have a variety of auxiliary information including geometry +type, layer extents, coordinate system and attribute field declarations in +comments in the header, and for each feature can have attributes. + +

    Creation Issues

    + +The driver supports creating new GMT files, and appending additional features +to existing files, but update of existing features is not supported. Each +layer is created as a seperate .gmt file. + +If a name that ends with .gmt is not given, then the GMT driver will take +the layer name and add the ".gmt" extension. +

    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/makefile.vc new file mode 100644 index 000000000..04f15a41e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/makefile.vc @@ -0,0 +1,16 @@ + +OBJ = ogrgmtdriver.obj ogrgmtdatasource.obj ogrgmtlayer.obj \ + +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/ogr_gmt.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/ogr_gmt.h new file mode 100644 index 000000000..a5b61b378 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/ogr_gmt.h @@ -0,0 +1,143 @@ +/****************************************************************************** + * $Id: ogr_mem.h 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions within the OGR GMT driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRGMT_H_INCLUDED +#define _OGRGMT_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "ogr_api.h" +#include "cpl_string.h" + +/************************************************************************/ +/* OGRGmtLayer */ +/************************************************************************/ + +class OGRGmtLayer : public OGRLayer +{ + OGRSpatialReference *poSRS; + OGRFeatureDefn *poFeatureDefn; + + int iNextFID; + + OGRwkbGeometryType eWkbType; + + int bUpdate; + int bHeaderComplete; + + int bRegionComplete; + OGREnvelope sRegion; + vsi_l_offset nRegionOffset; + + VSILFILE *fp; + + int ReadLine(); + CPLString osLine; + char **papszKeyedValues; + + int ScanAheadForHole(); + int NextIsFeature(); + + OGRFeature *GetNextRawFeature(); + + OGRErr WriteGeometry( OGRGeometryH hGeom, int bHaveAngle ); + OGRErr CompleteHeader( OGRGeometry * ); + + public: + int bValidFile; + + OGRGmtLayer( const char *pszFilename, int bUpdate ); + ~OGRGmtLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + OGRErr GetExtent(OGREnvelope *psExtent, int bForce); + + OGRErr ICreateFeature( OGRFeature *poFeature ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRGmtDataSource */ +/************************************************************************/ + +class OGRGmtDataSource : public OGRDataSource +{ + OGRGmtLayer **papoLayers; + int nLayers; + + char *pszName; + + int bUpdate; + + public: + OGRGmtDataSource(); + ~OGRGmtDataSource(); + + int Open( const char *pszFilename, int bUpdate ); + int Create( const char *pszFilename, char **papszOptions ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRGmtDriver */ +/************************************************************************/ + +class OGRGmtDriver : public OGRSFDriver +{ + public: + ~OGRGmtDriver(); + + const char *GetName(); + OGRDataSource *Open( const char *, int ); + + virtual OGRDataSource *CreateDataSource( const char *pszName, + char ** = NULL ); + + int TestCapability( const char * ); +}; + + +#endif /* ndef _OGRGMT_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/ogrgmtdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/ogrgmtdatasource.cpp new file mode 100644 index 000000000..ffd25e0c0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/ogrgmtdatasource.cpp @@ -0,0 +1,258 @@ +/****************************************************************************** + * $Id: ogrgmtdatasource.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGmtDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gmt.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrgmtdatasource.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* OGRGmtDataSource() */ +/************************************************************************/ + +OGRGmtDataSource::OGRGmtDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + + bUpdate = FALSE; +} + +/************************************************************************/ +/* ~OGRGmtDataSource() */ +/************************************************************************/ + +OGRGmtDataSource::~OGRGmtDataSource() + +{ + CPLFree( pszName ); + + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRGmtDataSource::Open( const char *pszFilename, int bUpdate ) + +{ + this->bUpdate = bUpdate; + + OGRGmtLayer *poLayer = new OGRGmtLayer( pszFilename, bUpdate ); + if( !poLayer->bValidFile ) + { + delete poLayer; + return FALSE; + } + + nLayers = 1; + papoLayers = (OGRGmtLayer **) CPLMalloc(sizeof(void*)); + papoLayers[0] = poLayer; + + CPLFree (pszName); + pszName = CPLStrdup( pszFilename ); + + return TRUE; +} + +/************************************************************************/ +/* Create() */ +/* */ +/* Create a new datasource. This doesn't really do anything */ +/* currently but save the name. */ +/************************************************************************/ + +int OGRGmtDataSource::Create( const char *pszDSName, char **papszOptions ) + +{ + (void) papszOptions; + + pszName = CPLStrdup( pszDSName ); + + return TRUE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRGmtDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + CPL_UNUSED char ** papszOptions ) +{ +/* -------------------------------------------------------------------- */ +/* Establish the geometry type. Note this logic */ +/* -------------------------------------------------------------------- */ + const char *pszGeom; + + switch( wkbFlatten(eType) ) + { + case wkbPoint: + pszGeom = " @GPOINT"; + break; + case wkbLineString: + pszGeom = " @GLINESTRING"; + break; + case wkbPolygon: + pszGeom = " @GPOLYGON"; + break; + case wkbMultiPoint: + pszGeom = " @GMULTIPOINT"; + break; + case wkbMultiLineString: + pszGeom = " @GMULTILINESTRING"; + break; + case wkbMultiPolygon: + pszGeom = " @GMULTIPOLYGON"; + break; + default: + pszGeom = ""; + break; + } + +/* -------------------------------------------------------------------- */ +/* If this is the first layer for this datasource, and if the */ +/* datasource name ends in .gmt we will override the provided */ +/* layer name with the name from the gmt. */ +/* -------------------------------------------------------------------- */ + CPLString osPath = CPLGetPath( pszName ); + CPLString osFilename; + + if( EQUAL(CPLGetExtension(pszName),"gmt") ) + osFilename = pszName; + else + osFilename = CPLFormFilename( osPath, pszLayerName, "gmt" ); + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFOpenL( osFilename, "w" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "open(%s) failed: %s", + osFilename.c_str(), VSIStrerror(errno) ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Write out header. */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, "# @VGMT1.0%s\n", pszGeom ); + VSIFPrintfL( fp, "# REGION_STUB \n" ); + +/* -------------------------------------------------------------------- */ +/* Write the projection, if possible. */ +/* -------------------------------------------------------------------- */ + if( poSRS != NULL ) + { + char *pszValue = NULL; + + if( poSRS->IsProjected() + && poSRS->GetAuthorityName("PROJCS") + && EQUAL(poSRS->GetAuthorityName("PROJCS"),"EPSG") ) + { + VSIFPrintfL( fp, "# @Je%s\n", + poSRS->GetAuthorityCode("PROJCS") ); + } + else if( poSRS->IsGeographic() + && poSRS->GetAuthorityName("GEOGCS") + && EQUAL(poSRS->GetAuthorityName("GEOGCS"),"EPSG") ) + { + VSIFPrintfL( fp, "# @Je%s\n", + poSRS->GetAuthorityCode("GEOGCS") ); + } + + if( poSRS->exportToProj4( &pszValue ) == OGRERR_NONE ) + { + VSIFPrintfL( fp, "# @Jp\"%s\"\n", pszValue ); + CPLFree( pszValue ); + pszValue = NULL; + } + + if( poSRS->exportToWkt( &pszValue ) == OGRERR_NONE ) + { + char *pszEscapedWkt = CPLEscapeString( pszValue, -1, + CPLES_BackslashQuotable ); + + VSIFPrintfL( fp, "# @Jw\"%s\"\n", pszEscapedWkt ); + CPLFree( pszValue ); + CPLFree( pszEscapedWkt ); + pszValue = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Finish header and close. */ +/* -------------------------------------------------------------------- */ + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Return open layer handle. */ +/* -------------------------------------------------------------------- */ + if( Open( osFilename, TRUE ) ) + return papoLayers[nLayers-1]; + else + return NULL; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGmtDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRGmtDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/ogrgmtdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/ogrgmtdriver.cpp new file mode 100644 index 000000000..f9b4c9542 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/ogrgmtdriver.cpp @@ -0,0 +1,123 @@ +/****************************************************************************** + * $Id: ogrmemdriver.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGmtDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gmt.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrmemdriver.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* ~OGRGmtDriver() */ +/************************************************************************/ + +OGRGmtDriver::~OGRGmtDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRGmtDriver::GetName() + +{ + return "OGR_GMT"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRGmtDriver::Open( const char * pszFilename, int bUpdate ) + +{ + if( !EQUAL(CPLGetExtension(pszFilename),"gmt") ) + return NULL; + + OGRGmtDataSource *poDS = new OGRGmtDataSource(); + + if( !poDS->Open( pszFilename, bUpdate ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRGmtDriver::CreateDataSource( const char * pszName, + char **papszOptions ) + +{ + OGRGmtDataSource *poDS = new OGRGmtDataSource(); + + if( poDS->Create( pszName, papszOptions ) ) + return poDS; + else + { + delete poDS; + return NULL; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGmtDriver::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODrCCreateDataSource) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRGMT() */ +/************************************************************************/ + +void RegisterOGRGMT() + +{ + OGRSFDriver* poDriver = new OGRGmtDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GMT ASCII Vectors (.gmt)" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gmt" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_gmt.html" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/ogrgmtlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/ogrgmtlayer.cpp new file mode 100644 index 000000000..e8ca183bc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gmt/ogrgmtlayer.cpp @@ -0,0 +1,1092 @@ +/****************************************************************************** + * $Id: ogrgmtlayer.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGmtLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gmt.h" +#include "cpl_conv.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrgmtlayer.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* OGRGmtLayer() */ +/************************************************************************/ + +OGRGmtLayer::OGRGmtLayer( const char * pszFilename, int bUpdate ) + +{ + poSRS = NULL; + + iNextFID = 0; + bValidFile = FALSE; + bHeaderComplete = !bUpdate; // assume header complete in readonly mode. + eWkbType = wkbUnknown; + poFeatureDefn = NULL; + papszKeyedValues = NULL; + + this->bUpdate = bUpdate; + + bRegionComplete = FALSE; + nRegionOffset = 0; + +/* -------------------------------------------------------------------- */ +/* Open file. */ +/* -------------------------------------------------------------------- */ + if( bUpdate ) + fp = VSIFOpenL( pszFilename, "r+" ); + else + fp = VSIFOpenL( pszFilename, "r" ); + + if( fp == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + CPLString osFieldNames, osFieldTypes, osGeometryType, osRegion; + CPLString osWKT, osProj4, osEPSG; + vsi_l_offset nStartOfLine = VSIFTellL(fp); + + while( ReadLine() && osLine[0] == '#' ) + { + int iKey; + + if( strstr( osLine, "FEATURE_DATA" ) ) + { + bHeaderComplete = TRUE; + ReadLine(); + break; + } + + if( EQUALN( osLine, "# REGION_STUB ", 14 ) ) + nRegionOffset = nStartOfLine; + + for( iKey = 0; + papszKeyedValues != NULL && papszKeyedValues[iKey] != NULL; + iKey++ ) + { + if( papszKeyedValues[iKey][0] == 'N' ) + osFieldNames = papszKeyedValues[iKey] + 1; + if( papszKeyedValues[iKey][0] == 'T' ) + osFieldTypes = papszKeyedValues[iKey] + 1; + if( papszKeyedValues[iKey][0] == 'G' ) + osGeometryType = papszKeyedValues[iKey] + 1; + if( papszKeyedValues[iKey][0] == 'R' ) + osRegion = papszKeyedValues[iKey] + 1; + if( papszKeyedValues[iKey][0] == 'J' ) + { + CPLString osArg = papszKeyedValues[iKey] + 2; + if( osArg[0] == '"' && osArg[osArg.length()-1] == '"' ) + { + osArg = osArg.substr(1,osArg.length()-2); + char *pszArg = CPLUnescapeString(osArg, NULL, + CPLES_BackslashQuotable); + osArg = pszArg; + CPLFree( pszArg ); + } + + if( papszKeyedValues[iKey][1] == 'e' ) + osEPSG = osArg; + if( papszKeyedValues[iKey][1] == 'p' ) + osProj4 = osArg; + if( papszKeyedValues[iKey][1] == 'w' ) + osWKT = osArg; + } + } + + nStartOfLine = VSIFTellL(fp); + } + +/* -------------------------------------------------------------------- */ +/* Handle coordinate system. */ +/* -------------------------------------------------------------------- */ + if( osWKT.length() ) + { + char *pszWKT = (char *) osWKT.c_str(); + + poSRS = new OGRSpatialReference(); + if( poSRS->importFromWkt(&pszWKT) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + else if( osEPSG.length() ) + { + poSRS = new OGRSpatialReference(); + if( poSRS->importFromEPSG( atoi(osEPSG) ) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + else if( osProj4.length() ) + { + poSRS = new OGRSpatialReference(); + if( poSRS->importFromProj4( osProj4 ) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Create the feature definition, and set the geometry type, if */ +/* known. */ +/* -------------------------------------------------------------------- */ + poFeatureDefn = new OGRFeatureDefn( CPLGetBasename(pszFilename) ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + if( osGeometryType == "POINT" ) + poFeatureDefn->SetGeomType( wkbPoint ); + else if( osGeometryType == "MULTIPOINT" ) + poFeatureDefn->SetGeomType( wkbMultiPoint ); + else if( osGeometryType == "LINESTRING" ) + poFeatureDefn->SetGeomType( wkbLineString ); + else if( osGeometryType == "MULTILINESTRING" ) + poFeatureDefn->SetGeomType( wkbMultiLineString ); + else if( osGeometryType == "POLYGON" ) + poFeatureDefn->SetGeomType( wkbPolygon ); + else if( osGeometryType == "MULTIPOLYGON" ) + poFeatureDefn->SetGeomType( wkbMultiPolygon ); + +/* -------------------------------------------------------------------- */ +/* Process a region line. */ +/* -------------------------------------------------------------------- */ + if( osRegion.length() > 0 ) + { + char **papszTokens = CSLTokenizeStringComplex( osRegion.c_str(), + "/", FALSE, FALSE ); + + if( CSLCount(papszTokens) == 4 ) + { + sRegion.MinX = CPLAtofM(papszTokens[0]); + sRegion.MaxX = CPLAtofM(papszTokens[1]); + sRegion.MinY = CPLAtofM(papszTokens[2]); + sRegion.MaxY = CPLAtofM(papszTokens[3]); + } + + bRegionComplete = TRUE; + + CSLDestroy( papszTokens ); + } + +/* -------------------------------------------------------------------- */ +/* Process fields. */ +/* -------------------------------------------------------------------- */ + if( osFieldNames.length() || osFieldTypes.length() ) + { + char **papszFN = CSLTokenizeStringComplex( osFieldNames, "|", + TRUE, TRUE ); + char **papszFT = CSLTokenizeStringComplex( osFieldTypes, "|", + TRUE, TRUE ); + int nFieldCount = MAX(CSLCount(papszFN),CSLCount(papszFT)); + int iField; + + for( iField = 0; iField < nFieldCount; iField++ ) + { + OGRFieldDefn oField("", OFTString ); + + if( iField < CSLCount(papszFN) ) + oField.SetName( papszFN[iField] ); + else + oField.SetName( CPLString().Printf( "Field_%d", iField+1 )); + + if( iField < CSLCount(papszFT) ) + { + if( EQUAL(papszFT[iField],"integer") ) + oField.SetType( OFTInteger ); + else if( EQUAL(papszFT[iField],"double") ) + oField.SetType( OFTReal ); + else if( EQUAL(papszFT[iField],"datetime") ) + oField.SetType( OFTDateTime ); + } + + poFeatureDefn->AddFieldDefn( &oField ); + } + + CSLDestroy( papszFN ); + CSLDestroy( papszFT ); + } + + bValidFile = TRUE; +} + +/************************************************************************/ +/* ~OGRGmtLayer() */ +/************************************************************************/ + +OGRGmtLayer::~OGRGmtLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "Gmt", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + +/* -------------------------------------------------------------------- */ +/* Write out the region bounds if we know where they go, and we */ +/* are in update mode. */ +/* -------------------------------------------------------------------- */ + if( nRegionOffset != 0 && bUpdate ) + { + VSIFSeekL( fp, nRegionOffset, SEEK_SET ); + VSIFPrintfL( fp, "# @R%.12g/%.12g/%.12g/%.12g", + sRegion.MinX, + sRegion.MaxX, + sRegion.MinY, + sRegion.MaxY ); + } + +/* -------------------------------------------------------------------- */ +/* Clean up. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( papszKeyedValues ); + + if( poFeatureDefn ) + poFeatureDefn->Release(); + + if( poSRS ) + poSRS->Release(); + + if( fp != NULL ) + VSIFCloseL( fp ); +} + +/************************************************************************/ +/* ReadLine() */ +/* */ +/* Read a line into osLine. If it is a comment line with @ */ +/* keyed values, parse out the keyed values into */ +/* papszKeyedValues. */ +/************************************************************************/ + +int OGRGmtLayer::ReadLine() + +{ +/* -------------------------------------------------------------------- */ +/* Clear last line. */ +/* -------------------------------------------------------------------- */ + osLine.erase(); + if( papszKeyedValues ) + { + CSLDestroy( papszKeyedValues ); + papszKeyedValues = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read newline. */ +/* -------------------------------------------------------------------- */ + const char *pszLine = CPLReadLineL( fp ); + if( pszLine == NULL ) + return FALSE; // end of file. + + osLine = pszLine; + +/* -------------------------------------------------------------------- */ +/* If this is a comment line with keyed values, parse them. */ +/* -------------------------------------------------------------------- */ + size_t i; + + if( osLine[0] != '#' || osLine.find_first_of('@') == std::string::npos ) + return TRUE; + + for( i = 0; i < osLine.length(); i++ ) + { + if( osLine[i] == '@' ) + { + size_t iValEnd; + int bInQuotes = FALSE; + + for( iValEnd = i+2; iValEnd < osLine.length(); iValEnd++ ) + { + if( !bInQuotes && isspace((unsigned char)osLine[iValEnd]) ) + break; + + if( bInQuotes && osLine[iValEnd] == '\\' + && iValEnd < osLine.length()-1 ) + { + iValEnd++; + } + else if( osLine[iValEnd] == '"' ) + bInQuotes = !bInQuotes; + } + + CPLString osValue = osLine.substr(i+2,iValEnd-i-2); + + // Unecape contents + char *pszUEValue = CPLUnescapeString( osValue, NULL, + CPLES_BackslashQuotable ); + + CPLString osKeyValue = osLine.substr(i+1,1); + osKeyValue += pszUEValue; + CPLFree( pszUEValue ); + papszKeyedValues = CSLAddString( papszKeyedValues, osKeyValue ); + + i = iValEnd; + } + } + + return TRUE; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGmtLayer::ResetReading() + +{ + if( iNextFID != 0 ) + { + iNextFID = 0; + VSIFSeekL( fp, 0, SEEK_SET ); + ReadLine(); + } +} + +/************************************************************************/ +/* ScanAheadForHole() */ +/* */ +/* Scan ahead to see if the next geometry is a hole. If so */ +/* return TRUE, otherwise seek back to where we were and return */ +/* FALSE. */ +/************************************************************************/ + +int OGRGmtLayer::ScanAheadForHole() + +{ + CPLString osSavedLine = osLine; + vsi_l_offset nSavedLocation = VSIFTellL( fp ); + + while( ReadLine() && osLine[0] == '#' ) + { + if( papszKeyedValues != NULL && papszKeyedValues[0][0] == 'H' ) + return TRUE; + } + + VSIFSeekL( fp, nSavedLocation, SEEK_SET ); + osLine = osSavedLine; + + // We don't actually restore papszKeyedValues, but we + // assume it doesn't matter since this method is only called + // when processing the '>' line. + + return FALSE; +} + +/************************************************************************/ +/* NextIsFeature() */ +/* */ +/* Returns TRUE if the next line is a feature attribute line. */ +/* This generally indicates the end of a multilinestring or */ +/* multipolygon feature. */ +/************************************************************************/ + +int OGRGmtLayer::NextIsFeature() + +{ + CPLString osSavedLine = osLine; + vsi_l_offset nSavedLocation = VSIFTellL( fp ); + int bReturn = FALSE; + + ReadLine(); + + if( osLine[0] == '#' && strstr(osLine,"@D") != NULL ) + bReturn = TRUE; + + VSIFSeekL( fp, nSavedLocation, SEEK_SET ); + osLine = osSavedLine; + + // We don't actually restore papszKeyedValues, but we + // assume it doesn't matter since this method is only called + // when processing the '>' line. + + return bReturn; +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRGmtLayer::GetNextRawFeature() + +{ +#if 0 + int bMultiVertex = + poFeatureDefn->GetGeomType() != wkbPoint + && poFeatureDefn->GetGeomType() != wkbUnknown; +#endif + CPLString osFieldData; + OGRGeometry *poGeom = NULL; + +/* -------------------------------------------------------------------- */ +/* Read lines associated with this feature. */ +/* -------------------------------------------------------------------- */ + for( ; TRUE; ReadLine() ) + { + if( osLine.length() == 0 ) + break; + + if( osLine[0] == '>' ) + { + if( poGeom != NULL + && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ) + { + OGRMultiPolygon *poMP = (OGRMultiPolygon *) poGeom; + if( ScanAheadForHole() ) + { + // Add a hole to the current polygon. + ((OGRPolygon *) poMP->getGeometryRef( + poMP->getNumGeometries()-1 ))-> + addRingDirectly( new OGRLinearRing() ); + } + else if( !NextIsFeature() ) + { + OGRPolygon *poPoly = new OGRPolygon(); + + poPoly->addRingDirectly( new OGRLinearRing() ); + + poMP->addGeometryDirectly( poPoly ); + } + else + break; /* done geometry */ + } + else if( poGeom != NULL + && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) + { + if( ScanAheadForHole() ) + ((OGRPolygon *)poGeom)-> + addRingDirectly( new OGRLinearRing() ); + else + break; /* done geometry */ + } + else if( poGeom != NULL + && (wkbFlatten(poGeom->getGeometryType()) + == wkbMultiLineString) + && !NextIsFeature() ) + { + ((OGRMultiLineString *) poGeom)-> + addGeometryDirectly( new OGRLineString() ); + } + else if( poGeom != NULL ) + { + break; + } + else if( poFeatureDefn->GetGeomType() == wkbUnknown ) + { + poFeatureDefn->SetGeomType( wkbLineString ); + /* bMultiVertex = TRUE; */ + } + } + else if( osLine[0] == '#' ) + { + int i; + for( i = 0; + papszKeyedValues != NULL && papszKeyedValues[i] != NULL; + i++ ) + { + if( papszKeyedValues[i][0] == 'D' ) + osFieldData = papszKeyedValues[i] + 1; + } + } + else + { + // Parse point line. + double dfX, dfY, dfZ = 0.0; + int nDim = CPLsscanf( osLine, "%lf %lf %lf", &dfX, &dfY, &dfZ ); + + if( nDim >= 2 ) + { + if( poGeom == NULL ) + { + switch( poFeatureDefn->GetGeomType() ) + { + case wkbLineString: + poGeom = new OGRLineString(); + break; + + case wkbPolygon: + poGeom = new OGRPolygon(); + ((OGRPolygon *) poGeom)->addRingDirectly( + new OGRLinearRing() ); + break; + + case wkbMultiPolygon: + { + OGRPolygon *poPoly = new OGRPolygon(); + poPoly->addRingDirectly( new OGRLinearRing() ); + + poGeom = new OGRMultiPolygon(); + ((OGRMultiPolygon *) poGeom)-> + addGeometryDirectly( poPoly ); + } + break; + + case wkbMultiPoint: + poGeom = new OGRMultiPoint(); + break; + + case wkbMultiLineString: + poGeom = new OGRMultiLineString(); + ((OGRMultiLineString *) poGeom)->addGeometryDirectly( + new OGRLineString() ); + break; + + case wkbPoint: + case wkbUnknown: + default: + poGeom = new OGRPoint(); + break; + } + + } + + switch( wkbFlatten(poGeom->getGeometryType()) ) + { + case wkbPoint: + ((OGRPoint *) poGeom)->setX( dfX ); + ((OGRPoint *) poGeom)->setY( dfY ); + if( nDim == 3 ) + ((OGRPoint *) poGeom)->setZ( dfZ ); + break; + + case wkbLineString: + if( nDim == 3 ) + ((OGRLineString *)poGeom)->addPoint(dfX,dfY,dfZ); + else + ((OGRLineString *)poGeom)->addPoint(dfX,dfY); + break; + + case wkbPolygon: + case wkbMultiPolygon: + { + OGRPolygon *poPoly; + OGRLinearRing *poRing; + + if( wkbFlatten(poGeom->getGeometryType()) + == wkbMultiPolygon ) + { + OGRMultiPolygon *poMP = (OGRMultiPolygon *) poGeom; + poPoly = (OGRPolygon*) poMP->getGeometryRef( + poMP->getNumGeometries() - 1 ); + } + else + poPoly = (OGRPolygon *) poGeom; + + if( poPoly->getNumInteriorRings() == 0 ) + poRing = poPoly->getExteriorRing(); + else + poRing = poPoly->getInteriorRing( + poPoly->getNumInteriorRings()-1 ); + + if( nDim == 3 ) + poRing->addPoint(dfX,dfY,dfZ); + else + poRing->addPoint(dfX,dfY); + } + break; + + case wkbMultiLineString: + { + OGRMultiLineString *poML = (OGRMultiLineString *) poGeom; + OGRLineString *poLine; + + poLine = (OGRLineString *) + poML->getGeometryRef( poML->getNumGeometries()-1 ); + + if( nDim == 3 ) + poLine->addPoint(dfX,dfY,dfZ); + else + poLine->addPoint(dfX,dfY); + } + break; + + default: + CPLAssert( FALSE ); + } + } + } + + if( poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) + { + ReadLine(); + break; + } + } + + if( poGeom == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + poGeom->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly( poGeom ); + poFeature->SetFID( iNextFID++ ); + +/* -------------------------------------------------------------------- */ +/* Process field values. */ +/* -------------------------------------------------------------------- */ + char **papszFD = CSLTokenizeStringComplex( osFieldData, "|", TRUE, TRUE ); + int iField; + + for( iField = 0; papszFD != NULL && papszFD[iField] != NULL; iField++ ) + { + if( iField >= poFeatureDefn->GetFieldCount() ) + break; + + poFeature->SetField( iField, papszFD[iField] ); + } + + CSLDestroy( papszFD ); + + m_nFeaturesRead++; + + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRGmtLayer::GetNextFeature() + +{ + while( TRUE ) + { + OGRFeature *poFeature = GetNextRawFeature(); + + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature ) ) ) + { + return poFeature; + } + else + { + delete poFeature; + } + } + + return NULL; +} + +/************************************************************************/ +/* CompleteHeader() */ +/* */ +/* Finish writing out the header with field definitions and the */ +/* layer geometry type. */ +/************************************************************************/ + +OGRErr OGRGmtLayer::CompleteHeader( OGRGeometry *poThisGeom ) + +{ +/* -------------------------------------------------------------------- */ +/* If we don't already have a geometry type, try to work one */ +/* out and write it now. */ +/* -------------------------------------------------------------------- */ + if( poFeatureDefn->GetGeomType() == wkbUnknown + && poThisGeom != NULL ) + { + const char *pszGeom; + + poFeatureDefn->SetGeomType(wkbFlatten(poThisGeom->getGeometryType())); + + switch( wkbFlatten(poFeatureDefn->GetGeomType()) ) + { + case wkbPoint: + pszGeom = " @GPOINT"; + break; + case wkbLineString: + pszGeom = " @GLINESTRING"; + break; + case wkbPolygon: + pszGeom = " @GPOLYGON"; + break; + case wkbMultiPoint: + pszGeom = " @GMULTIPOINT"; + break; + case wkbMultiLineString: + pszGeom = " @GMULTILINESTRING"; + break; + case wkbMultiPolygon: + pszGeom = " @GMULTIPOLYGON"; + break; + default: + pszGeom = ""; + break; + } + + VSIFPrintfL( fp, "#%s\n", pszGeom ); + } + +/* -------------------------------------------------------------------- */ +/* Prepare and write the field names and types. */ +/* -------------------------------------------------------------------- */ + CPLString osFieldNames, osFieldTypes; + + int iField; + + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + if( iField > 0 ) + { + osFieldNames += "|"; + osFieldTypes += "|"; + } + + osFieldNames += poFeatureDefn->GetFieldDefn(iField)->GetNameRef(); + switch( poFeatureDefn->GetFieldDefn(iField)->GetType() ) + { + case OFTInteger: + osFieldTypes += "integer"; + break; + + case OFTReal: + osFieldTypes += "double"; + break; + + case OFTDateTime: + osFieldTypes += "datetime"; + break; + + default: + osFieldTypes += "string"; + break; + } + } + + if( poFeatureDefn->GetFieldCount() > 0 ) + { + VSIFPrintfL( fp, "# @N%s\n", osFieldNames.c_str() ); + VSIFPrintfL( fp, "# @T%s\n", osFieldTypes.c_str() ); + } + +/* -------------------------------------------------------------------- */ +/* Mark the end of the header, and start of feature data. */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL( fp, "# FEATURE_DATA\n" ); + + bHeaderComplete = TRUE; + bRegionComplete = TRUE; // no feature written, so we know them all! + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRGmtLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + if( !bUpdate ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Can't create features on read-only dataset." ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Do we need to write the header describing the fields? */ +/* -------------------------------------------------------------------- */ + if( !bHeaderComplete ) + { + OGRErr eErr = CompleteHeader( poFeature->GetGeometryRef() ); + + if( eErr != OGRERR_NONE ) + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Write out the feature */ +/* -------------------------------------------------------------------- */ + OGRGeometry *poGeom = poFeature->GetGeometryRef(); + + if( poGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Features without geometry not supported by GMT writer." ); + return OGRERR_FAILURE; + } + + if( poFeatureDefn->GetGeomType() == wkbUnknown ) + poFeatureDefn->SetGeomType(wkbFlatten(poGeom->getGeometryType())); + + // Do we need a vertex collection marker grouping vertices. + if( poFeatureDefn->GetGeomType() != wkbPoint ) + VSIFPrintfL( fp, ">\n" ); + +/* -------------------------------------------------------------------- */ +/* Write feature properties() */ +/* -------------------------------------------------------------------- */ + if( poFeatureDefn->GetFieldCount() > 0 ) + { + int iField; + CPLString osFieldData; + + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + OGRFieldType eFType=poFeatureDefn->GetFieldDefn(iField)->GetType(); + const char *pszRawValue = poFeature->GetFieldAsString(iField); + char *pszEscapedVal; + + if( iField > 0 ) + osFieldData += "|"; + + // We don't want prefix spaces for numeric values. + if( eFType == OFTInteger || eFType == OFTReal ) + while( *pszRawValue == ' ' ) + pszRawValue++; + + if( strchr(pszRawValue,' ') || strchr(pszRawValue,'|') + || strchr(pszRawValue, '\t') || strchr(pszRawValue, '\n') ) + { + pszEscapedVal = + CPLEscapeString( pszRawValue, + -1, CPLES_BackslashQuotable ); + + osFieldData += "\""; + osFieldData += pszEscapedVal; + osFieldData += "\""; + CPLFree( pszEscapedVal ); + } + else + osFieldData += pszRawValue; + } + + VSIFPrintfL( fp, "# @D%s\n", osFieldData.c_str() ); + } + +/* -------------------------------------------------------------------- */ +/* Write Geometry */ +/* -------------------------------------------------------------------- */ + return WriteGeometry( (OGRGeometryH) poGeom, TRUE ); +} + +/************************************************************************/ +/* WriteGeometry() */ +/* */ +/* Write a geometry to the file. If bHaveAngle is TRUE it */ +/* means the angle bracket preceeding the point stream has */ +/* already been written out. */ +/* */ +/* We use the C API for geometry access because of it's */ +/* simplified access to vertices and children geometries. */ +/************************************************************************/ + +OGRErr OGRGmtLayer::WriteGeometry( OGRGeometryH hGeom, int bHaveAngle ) + +{ +/* -------------------------------------------------------------------- */ +/* This is a geometry with sub-geometries. */ +/* -------------------------------------------------------------------- */ + if( OGR_G_GetGeometryCount( hGeom ) > 0 ) + { + int iGeom; + OGRErr eErr = OGRERR_NONE; + + for( iGeom = 0; + iGeom < OGR_G_GetGeometryCount(hGeom) && eErr == OGRERR_NONE; + iGeom++ ) + { + // We need to emit polygon @P and @H items while we still + // know this is a polygon and which is the outer and inner + // ring. + if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) == wkbPolygon ) + { + if( !bHaveAngle ) + { + VSIFPrintfL( fp, ">\n" ); + bHaveAngle = TRUE; + } + if( iGeom == 0 ) + VSIFPrintfL( fp, "# @P\n" ); + else + VSIFPrintfL( fp, "# @H\n" ); + } + + eErr = WriteGeometry( OGR_G_GetGeometryRef( hGeom, iGeom ), + bHaveAngle ); + bHaveAngle = FALSE; + } + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* If this is not a point we need to have an angle bracket to */ +/* mark the vertex list. */ +/* -------------------------------------------------------------------- */ + if( wkbFlatten(OGR_G_GetGeometryType(hGeom)) != wkbPoint + && !bHaveAngle ) + VSIFPrintfL( fp, ">\n" ); + +/* -------------------------------------------------------------------- */ +/* Dump vertices. */ +/* -------------------------------------------------------------------- */ + int iPoint, nPointCount = OGR_G_GetPointCount(hGeom); + int nDim = OGR_G_GetCoordinateDimension(hGeom); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + char szLine[128]; + double dfX = OGR_G_GetX( hGeom, iPoint ); + double dfY = OGR_G_GetY( hGeom, iPoint ); + double dfZ = OGR_G_GetZ( hGeom, iPoint ); + + sRegion.Merge( dfX, dfY ); + OGRMakeWktCoordinate( szLine, dfX, dfY, dfZ, nDim ); + if( VSIFPrintfL( fp, "%s\n", szLine ) < 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Gmt write failure: %s", + VSIStrerror( errno ) ); + return OGRERR_FAILURE; + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetExtent() */ +/* */ +/* Fetch extent of the data currently stored in the dataset. */ +/* The bForce flag has no effect on SHO files since that value */ +/* is always in the header. */ +/* */ +/* Returns OGRERR_NONE/OGRRERR_FAILURE. */ +/************************************************************************/ + +OGRErr OGRGmtLayer::GetExtent (OGREnvelope *psExtent, int bForce) + +{ + if( bRegionComplete && sRegion.IsInit() ) + { + *psExtent = sRegion; + return OGRERR_NONE; + } + + return OGRLayer::GetExtent( psExtent, bForce ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGmtLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return FALSE; + + else if( EQUAL(pszCap,OLCSequentialWrite) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return bRegionComplete; + + else if( EQUAL(pszCap,OLCCreateField) ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRGmtLayer::CreateField( OGRFieldDefn *poField, int bApproxOK ) + +{ + if( !bUpdate ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Can't create fields on read-only dataset." ); + return OGRERR_FAILURE; + } + + if( bHeaderComplete ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create fields after features have been created."); + return OGRERR_FAILURE; + } + + switch( poField->GetType() ) + { + case OFTInteger: + case OFTReal: + case OFTString: + case OFTDateTime: + poFeatureDefn->AddFieldDefn( poField ); + return OGRERR_NONE; + break; + + break; + + default: + if( !bApproxOK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Field %s is of unsupported type %s.", + poField->GetNameRef(), + poField->GetFieldTypeName( poField->GetType() ) ); + return OGRERR_FAILURE; + } + else if( poField->GetType() == OFTDate + || poField->GetType() == OFTTime ) + { + OGRFieldDefn oModDef( poField ); + oModDef.SetType( OFTDateTime ); + poFeatureDefn->AddFieldDefn( poField ); + return OGRERR_NONE; + } + else + { + OGRFieldDefn oModDef( poField ); + oModDef.SetType( OFTString ); + poFeatureDefn->AddFieldDefn( poField ); + return OGRERR_NONE; + } + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/GNUmakefile new file mode 100644 index 000000000..d7eac6f4b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/GNUmakefile @@ -0,0 +1,26 @@ + +include ../../../GDALmake.opt + +OBJ = ogrgeopackagedriver.o ogrgeopackagedatasource.o ogrgeopackagelayer.o \ + ogrgeopackagetablelayer.o ogrgeopackageselectlayer.o ogrgeopackageutility.o \ + gdalgeopackagerasterband.o + +ifeq ($(SPATIALITE_412_OR_LATER),yes) +CPPFLAGS += -DSPATIALITE_412_OR_LATER +endif + +ifeq ($(SQLITE_HAS_COLUMN_METADATA),yes) +CPPFLAGS += -DSQLITE_HAS_COLUMN_METADATA +endif + +CPPFLAGS := -I.. -I../sqlite -I../../../frmts/mem $(SQLITE_INC) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_geopackage.h ogrgeopackageutility.h ../sqlite/ogr_sqlite.h + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/drv_geopackage.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/drv_geopackage.html new file mode 100644 index 000000000..a888b4653 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/drv_geopackage.html @@ -0,0 +1,191 @@ + + +GeoPackage vector + + + + +

    GeoPackage vector

    + +

    This driver implements support for access to spatial tables in the +OGC GeoPackage format standard. +The GeoPackage standard uses a SQLite database file as a generic container, and the standard defines: +

    +
      +
    • Expected metadata tables (gpkg_contents, gpkg_spatial_ref_sys, gpkg_geometry_columns)
    • +
    • Binary format encoding for geometries in spatial tables (basically a GPKG standard header object followed by ISO standard well-known binary (WKB))
    • +
    • Naming and conventions for extensions (extended feature types) and indexes (how to use SQLite r-tree in an interoperable manner)
    • +
    +

    This driver reads and writes SQLite files from the file system, so it must be run by a user with read/write access to the files it is working with.

    +

    Starting with GDAL 2.0, the driver also supports reading and writing the +following non-linear geometry types :CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE and MULTISURFACE

    + +

    Starting with GDAL 2.0, GeoPackage raster/tiles are supported. See +GeoPackage raster documentation page

    + +

    Limitations

    + +
      +
    • GeoPackage only supports one geometry column per table.
    • +
    • Before GDAL 2.0, the driver did not implement the GeoPackage spatial index extension.
    • +
    • The core GeoPackage specification does not currently support non-spatial tables, but starting with GDAL 2.0, the driver allows creating and reading such tables via the Aspatial Support (gdal_aspatial) extension.
    • +
    + +

    SQL

    + +

    The driver supports OGR attribute filters, and users are expected + to provide filters in the SQLite dialect, as they will be executed directly + against the database. +

    +

    + Starting with GDAL 2.0, SQL SELECT statements passed to ExecuteSQL() are + also executed directly against the database. If Spatialite is used, a recent + version (4.2.0) is needed and use of explicit cast operators AsGPB() is required + to transform GeoPackage geometries to Spatialite geometries (the reverse conversion + from Spatialite geometries is automatically done by the GPKG driver). + It is also possible to use with any Spatialite version, but in + a slower way, by specifying the "INDIRECT_SQLITE" dialect. In which case, + GeoPackage geometries automatically appear as Spatialite geometries after translation by OGR. +

    + +

    SQL functions

    + +Starting with GDAL 2.0, the following SQL functions, from the GeoPackage specification, are available : +
      +
    • ST_MinX(geom Geometry) : returns the minimum X coordinate of the geometry
    • +
    • ST_MinY(geom Geometry) : returns the minimum Y coordinate of the geometry
    • +
    • ST_MaxX(geom Geometry) : returns the maximum X coordinate of the geometry
    • +
    • ST_MaxY(geom Geometry) : returns the maximum Y coordinate of the geometry
    • +
    • ST_IsEmpty(geom Geometry) : returns 1 is the geometry is empty (but not null), e.g. a POINT EMPTY geometry
    • +
    • ST_GeometryType(geom Geometry) : returns the geometry type : 'POINT', +'LINESTRING', 'POLYGON', 'MULTIPOLYGON', 'MULITLINESTRING', 'MULTIPOINT', 'GEOMETRYCOLLECTION'
    • +
    • ST_SRID(geom Geometry) : returns the SRID of the geometry
    • +
    • GPKG_IsAssignable(expected_geom_type String, actual_geom_type String) : mainly, needed for the 'Geometry Type Triggers Extension'
    • +
    + +The following functions, with identical syntax and semantics as in Spatialite, are also available : + +
      +
    • CreateSpatialIndex(table_name String, geom_column_name String) : creates a spatial index (RTree) on the specified table/geometry column
    • +
    • DisableSpatialIndex(table_name String, geom_column_name String) : disable an existing spatial index (RTree) on the specified table/geometry column
    • +
    + +

    Link with Spatialite

    + +

    +Starting with GDAL 2.0, if it has been compiled against Spatialite 4.2 or later, +it is also possible to use Spatialite SQL functions. Explicit transformation +from GPKG geometry binary encoding to Spatialite geometry binary encoding must be done. +

    + +
    +ogrinfo poly.gpkg -sql "SELECT ST_Buffer(CastAutomagic(geom),5) FROM poly"
    +
    + +

    +Starting with Spatialite 4.3, CastAutomagic is no longer needed. +

    + +

    Transaction support (GDAL >= 2.0)

    + +

    +The driver implements transactions at the database level, per +RFC 54 +

    + +

    Creation Issues

    + +

    When creating a new GeoPackage file, the driver will attempt to + force the database into a UTF-8 mode for text handling, satisfying + the OGR strict UTF-8 capability. For pre-existing files, the driver + will work with whatever it's given.

    + +

    Dataset Creation Options

    + +

    None related to vector

    + +

    Layer Creation Options

    + +
      +
    • GEOMETRY_NAME: Column to use for the geometry column. Default to "geom". Note: option was called GEOMETRY_COLUMN in releases before GDAL 2
    • +
    • GEOMETRY_NULLABLE: (GDAL >=2.0) Whether the values of the geometry column can be NULL. Can be set to NO so that geometry is required. Default to "YES"
    • +
    • FID: Column name to use for the OGR FID (primary key in the SQLite database). Default to "fid"
    • +
    • OVERWRITE: If set to "YES" will delete any existing layers that have the same name as the layer being created. Default to NO
    • +
    • SPATIAL_INDEX: (GDAL >=2.0) If set to "YES" will create a spatial index for this layer. Default to YES
    • +
    • PRECISION: (GDAL >=2.0) This may be "YES" to force new fields created on this +layer to try and represent the width of text fields (in terms of UTF-8 characters, not bytes), if available +using TEXT(width) types. If "NO" then the type TEXT will be used instead. The default is "YES".

      +

    • TRUNCATE_FIELDS: (GDAL >=2.0) This may be "YES" to force truncated of field values +that exceed the maximum allowed width of text fields, and also to "fix" the passed string if needed +to make it a valid UTF-8 string. If "NO" then the value is not truncated nor modified. The default is "NO".

      +

    • IDENTIFIER=string: (GDAL >=2.0) Identifier of the layer, as put in the contents table.

      +

    • DESCRIPTION=string: (GDAL >=2.0) Description of the layer, as put in the contents table.

      +

    + +

    Metadata

    + +

    (GDAL >=2.0) GDAL uses the standardized +gpkg_metadata and +gpkg_metadata_reference tables to read and write metadata, +on the dataset and layer objects.

    + +

    GDAL metadata, from the default metadata domain and possibly other metadata +domains, is serialized in a single XML document, conformant with the format used +in GDAL PAM (Persistent Auxiliary Metadata) .aux.xml files, and registered with +md_scope=dataset and md_standard_uri=http://gdal.org in gpkg_metadata. +For the dataset, this entry is referenced in gpkg_metadata_reference with a +reference_scope=geopackage. +For a layer, this entry is referenced in gpkg_metadata_reference with a +reference_scope=table and table_name={name of the table}

    + +

    Metadata not originating from GDAL can be read by the driver and will be +exposed as metadata items with keys of the form GPKG_METADATA_ITEM_XXX and +values the content of the metadata columns of the gpkg_metadata table. +Update of such metadata is not currently supported through GDAL interfaces ( +although it can be through direct SQL commands).

    + +

    The specific DESCRIPTION and IDENTIFIER metadata item of the default metadata +domain can be used in read/write to read from/update the corresponding columns of +the gpkg_contents table.

    + +

    Examples

    + +
      +
    • +Simple translation of a single shapefile into GeoPackage. The table 'abc' will +be created with the features from abc.shp and attributes from abc.dbf. +The file filename.gpkg must not already exist, as it will be created. +
      +% ogr2ogr -f GPKG filename.gpkg abc.shp
      +
      +
    • + +
    • +Translation of a directory of shapefiles into a GeoSpackage. Each file will end +up as a new table within the GPKG file. +The file filename.gpkg must not already exist, as it will be created. +
      +% ogr2ogr -f GPKG filename.gpkg ./path/to/dir
      +
      +
    • + +
    • +Translation of a PostGIS database into a GeoPackage. Each table in the database will end up +as a table in the GPKG file. +The file filename.gpkg must not already exist, as it will be created. +
      +% ogr2ogr -f GPKG filename.gpkg PG:'dbname=mydatabase host=localhost'
      +
      +
    • + +
    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/drv_geopackage_raster.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/drv_geopackage_raster.html new file mode 100644 index 000000000..09763c193 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/drv_geopackage_raster.html @@ -0,0 +1,375 @@ + + + +GeoPackage raster + + + + +

    GeoPackage raster

    + +

    Starting with GDAL 2.0, this driver implements full read/creation/update of +tables containing raster tiles in the +OGC GeoPackage format standard. +The GeoPackage standard uses a SQLite database file as a generic container, and the standard defines: +

    +
      +
    • Expected metadata tables (gpkg_contents, + gpkg_spatial_ref_sys, gpkg_tile_matrix, + gpkg_tile_matrix_set, ...)
    • +
    • Tile format encoding (PNG and JPEG for base specification, WebP as extension) and tiling conventions
    • +
    • Naming and conventions for extensions
    • +
    +

    This driver reads and writes SQLite files from the file system, so it must be +run by a user with read/write access to the files it is working with.

    + +

    The driver can also handle GeoPackage vectors. See +GeoPackage vector documentation page

    + +

    Various kind of input datasets can be converted to GeoPackage raster : +

      +
    • Single band grey level
    • +
    • Single band with R,G,B or R,G,B,A color table
    • +
    • Two bands: first band with grey level, second band with alpha channel
    • +
    • Three bands: Red, Green, Blue
    • +
    • Four band: Red, Green, Blue, Alpha
    • +
    +GeoPackage rasters only support Byte data type. + +

    +All raster extensions standardized by the GeoPackage specification are supported in read and creation : +

    +
      +
    • gpkg_webp: when storing WebP tiles, provided that GDAL is compiled against libwebp.
    • +
    • gpkg_zoom_other: when resolution of consecutive zoom levels does not vary with a factor of 2.
    • +
    + +

    Opening options

    + +

    By default, the driver will expose a GeoPackage dataset as a four band (Red,Green, +Blue,Alpha) dataset, which gives the maximum compatibility with the various encodings of +tiles that can be stored. It is possible to specify an explicit number of bands +with the BAND_COUNT opening option.

    + +

    The driver will use the geographic/projected extent indicated in the +gpkg_contents table, +and do necessary clipping, if needed, to respect +that extent. However that information being optional, if omitted, the driver +will use the extent provided by the +gpkg_tile_matrix_set, +which covers the extent at all zoom levels. The user can also specify the USE_TILE_EXTENT=YES +open option to use the actual extent of tiles at the maximum zoom level. Or +it can specify any of MINX/MINY/MAXX/MAXY to have a custom extent.

    + +The following open options are available: +

      +
    • TABLE=table_name: Name of the table containing the tiles (called +"Tile Pyramid User Data Table" +in the GeoPackage specification language). If +the GeoPackage dataset only contains one table, this option is not necessary. +Otherwise, it is required.
    • +
    • ZOOM_LEVEL=value: Integer value between 0 and the maximum filled in +the gpkg_tile_matrix table. By default, the driver will select the maximum +zoom level, such as at least one tile at that zoom level is found in the raster table.
    • +
    • BAND_COUNT=1/2/3/4: Number of bands of the dataset exposed after opening. +Some conversions will be done when possible and implemented, but this might fail +in some cases, depending on the BAND_COUNT value and the number of bands of the tile. +Defaults to 4 (which is the always safe value).
    • +
    • MINX=value: Minimum longitude/easting of the area of interest.
    • +
    • MINY=value: Minimum latitude/northing of the area of interest.
    • +
    • MAXX=value: Maximum longitude/easting of the area of interest.
    • +
    • MAXY=value: Maximum latitude/northing of the area of interest.
    • +
    • USE_TILE_EXTENT=YES/NO: Whether to use the extent of actual existing +tiles at the zoom level of the full resolution dataset. Defaults to NO.
    • +
    • TILE_FORMAT=PNG_JPEG/PNG/PNG8/JPEG/WEBP: Format used to store tiles. See +Tile format section. Only used in update mode. Defaults to PNG_JPEG.
    • +
    • QUALITY=1-100: Quality setting for JPEG and WEBP compression. Only used in update mode. Default to 75.
    • +
    • ZLEVEL=1-9: DEFLATE compression level for PNG tiles. Only used in update mode. Default to 6.
    • +
    • DITHER=YES/NO: Whether to use Floyd-Steinberg dithering (for TILE_FORMAT=PNG8). +Only used in update mode. Defaults to NO.
    • +
    + +Note: open options are typically specified with "-oo name=value" syntax in +most GDAL utilities, or with the GDALOpenEx() API call. + +

    Creation issues

    + +

    Depending of the number of bands of the input dataset and the tile format +selected, the driver will do the necessary conversions to be compatible with the +tile format.

    + +

    To add several tile tables to a GeoPackage dataset (seen as GDAL subdatasets), +or to add a tile table to an existing vector-only GeoPackage, the generic +APPEND_SUBDATASET=YES creation option must be provided.

    + +

    Fully transparent tiles will not be written to the database, as allowed by +the format.

    + +

    The driver implements the Create() and IWriteBlock() methods, so that arbitrary +writing of raster blocks is possible, enabling the direct use of GeoPackage as +the output dataset of utilities such as gdalwarp.

    + +

    On creation, raster blocks can be written only if the geotransformation +matrix has been set with SetGeoTransform() This is effectively needed to determine +the zoom level of the full resolution dataset based on the pixel resolution, dataset +and tile dimensions.

    + +

    Technical/implementation note: when a dataset is opened with a non-default area of interest +(i.e. use of MINX,MINY,MAXX,MAXY or USE_TILE_EXTENT open option), or when creating/ +opening a dataset with a non-custom tiling scheme, it is possible that GDAL blocks +do not exactly match a single GeoPackage tile. In which case, each GDAL +block will overlap four GeoPackage tiles. This is easily handled on the read side, +but on creation/update side, such configuration could cause numerous decompression/ +recompression of tiles to be done, which might cause unnecessary quality loss when using +lossy compression (JPEG, WebP). To avoid that, the driver will create a temporary +database next to the main GeoPackage file to store partial GeoPackage tiles in a +lossless (and uncompressed) way. Once a tile has received data for its four quadrants +and for all the bands (or the dataset is closed or explicitly flushed with FlushCache()), +those uncompressed tiles are definitely transferred to the GeoPackage file with +the appropriate compression. All of this is transparent to the user of GDAL API/utilities

    + +

    Tile formats

    + +

    GeoPackage can store tiles in different formats, PNG and/or JPEG for the baseline +specification, and WebP for extended GeoPackage. Support for those tile formats +depend if the underlying drivers are available in GDAL, which is generally the +case for PNG and JPEG, but not necessarily for WebP since it requires GDAL to +be compiled against the optional libwebp.

    + +

    By default, GDAL will use a mix of PNG and JPEG tiles. PNG tiles will be used +to store tiles that are not completely opaque, either because input dataset has +an alpha channel with non fully opaque content, or because tiles are partial due +to clipping at the right or bottom edges of the raster, or when a dataset is opened +with a non-default area of interest, or with a non-custom tiling scheme. On the +contrary, for fully opaque tiles, JPEG format will be used.

    + +

    It is possible to select one unique tile format by setting the creation/open +option TILE_FORMAT to one of PNG, JPEG or WEBP. When using JPEG, the alpha channel +will not be stored. When using WebP, the +gpkg_webp +extension will be registered. The lossy compression of WebP is used. +Note that a recent enough libwebp +(>=0.1.4) must be used to support alpha channel in WebP tiles.

    + +

    PNG8 can be selected to use 8-bit PNG with a color table up to 256 colors. +On creation, an optimized color table is computed for each tile. The DITHER +option can be set to YES to use Floyd/Steinberg dithering algorithm, which +spreads the quantization error on neighbouring pixels for better rendering (note +however than when zooming in, this can cause non desirable visual artifacts). +Setting it to YES will generally cause less effective compression. +Note that at that time, such an 8-bit PNG formulation is only used for fully opaque tiles, +as the median-cut algorithm currently implemented to compute the optimal color +table does not support alpha channel (even if PNG8 format would potentially allow +color table with transparency). So when selecting PNG8, non fully opaque tiles +will be stored as 32-bit PNG.

    + +

    Tiling schemes

    + +

    +By default, conversion to GeoPackage will create a custom tiling scheme, such +that the input dataset can be losslessly converted, both at the pixel and +georeferencing level (if using a lossless tile format such as PNG). That +tiling scheme is such that its origin (min_x, max_y) in the +gpkg_tile_matrix_set +table perfectly matches the top left corner of the dataset, and the selected +resolution (pixel_x_size, pixel_y_size) at the computed maximum zoom_level of the +gpkg_tile_matrix table will +match the pixel width and height of the raster. +

    +

    However to ease interoperability with other implementations, and enable use +of GeoPackage with tile servicing software, it is possible to select a predefined +tiling scheme that has world coverage. The available tiling schemes are :

    +
      +
    • GoogleCRS84Quad, as described in +OGC 07-057r7 +WMTS 1.0 specification, Annex E.3. That tiling schemes consists of a single +256x256 tile at its zoom level 0, in EPSG:4326 CRS, with extent in longitude and +latitude in the range [-180,180]. Consequently, at zoom level 0, 64 lines are +unused at the top and bottom of that tile. This may cause issues with some +implementations of the specification, and there are some ambiguities about the +exact definition of this tiling scheme. Using InspireCRS84Quad/PseudoTMS_GlobalGeodetic +instead is therefore recommended.
    • +
    • GoogleMapsCompatible, as described in WMTS 1.0 specification, Annex E.4. +That tiling schemes consists of a single 256x256 tile at its zoom level 0, +in EPSG:3857 CRS, with extent in easting and northing in the range +[-20037508.34,20037508.34].
    • +
    • InspireCRS84Quad, as described in + +Inspire View Services. +That tiling schemes consists of two 256x256 tiles at its zoom level 0, in +EPSG:4326 CRS, with extent in longitude in the range [-180,180] and in latitude +in the range [-90,90].
    • +
    • PseudoTMS_GlobalGeodetic, based on the + +global-geodetic profile of OSGeo TMS (Tile Map Service) specification. +This has exactly the same definition as InspireCRS84Quad tiling scheme. Note +however that full interoperability with TMS is not +possible due to the origin of numbering of tiles being the top left corner in +GeoPackage (consistently with WMTS convention), whereas TMS uses the bottom left +corner as origin.
    • +
    • PseudoTMS_GlobalMercator, based on the + +global-mercator profile of OSGeo TMS (Tile Map Service) specification. +That tiling schemes consists of four 256x256 tiles at its zoom level 0, +in EPSG:3857 CRS, with extent extent in easting and northing in the range +[-20037508.34,20037508.34]. The same remark as with PseudoTMS_GlobalGeodetic applies +regarding interoperability with TMS.
    • +
    + +

    In all the above tiling schemes, consecutive zoom levels defer by a resolution +of a factor of two.

    + +

    Creation options

    + +

    The following creation options are available:

    + +
      +
    • RASTER_TABLE=string. Name of tile user table. By default, based on +the filename (i.e. if filename is foo.gpkg, the table will be called "foo").
    • +
    • APPEND_SUBDATASET=YES/NO: If set to YES, an existing GeoPackage +will not be priorly destroyed, such as to be able to add new content to it. Defaults to NO.
    • +
    • RASTER_IDENTIFIER=string. Human-readable identifier (e.g. short name), put +in the identifier column of the gpkg_contents table.
    • +
    • RASTER_DESCRIPTION=string. Human-readable description, put +in the description column of the gpkg_contents table.
    • +
    • BLOCKSIZE=integer. Block size in width and height in pixels. +Defaults to 256. Maximum supported is 4096. Should not be set when using a non-custom TILING_SCHEME.
    • +
    • BLOCKXSIZE=integer. Block width in pixels. +Defaults to 256. Maximum supported is 4096.
    • +
    • BLOCKYSIZE=integer. Block height in pixels. +Defaults to 256. Maximum supported is 4096.
    • +
    • TILE_FORMAT=PNG_JPEG/PNG/PNG8/JPEG/WEBP: Format used to store tiles. See +Tile formats section. Defaults to PNG_JPEG.
    • +
    • QUALITY=1-100: Quality setting for JPEG and WEBP compression. Default to 75.
    • +
    • ZLEVEL=1-9: DEFLATE compression level for PNG tiles. Default to 6.
    • +
    • DITHER=YES/NO: Whether to use Floyd-Steinberg dithering (for TILE_FORMAT=PNG8). +Defaults to NO.
    • +
    • TILING_SCHEME=CUSTOM/GoogleCRS84Quad/GoogleMapsCompatible/InspireCRS84Quad/PseudoTMS_GlobalGeodetic/PseudoTMS_GlobalMercator. +See Tiling schemes section. Defaults to CUSTOM.
    • +
    • ZOOM_LEVEL_STRATEGY=AUTO/LOWER/UPPER. Strategy to determine zoom level. +Only used for TILING_SCHEME is different from CUSTOM. LOWER will select the +zoom level immediately below the theoretical computed non-integral zoom level, +leading to subsampling. On the contrary, UPPER will select the immedately above +zoom level, leading to oversampling. Defaults to AUTO which selects the closest +zoom level.
    • +
    • RESAMPLING=NEAREST/BILINEAR/CUBIC/CUBICSPLINE/LANCZOS/MODE/AVERAGE. +Resampling algorithm. Only used for TILING_SCHEME is different from CUSTOM. Defaults +to BILINEAR.
    • +
    + +

    Overviews

    + +

    gdaladdo / BuildOverviews() can be used to compute overviews. Power-of-two +overview factors (2,4,8,16,...) should be favored to be conformant with the +baseline GeoPackage specification. Use of other overview factors will work with the GDAL +driver, and cause the +gpkg_zoom_other extension to be registered, but +that could potentially cause interoperability problems with other +implementations that do not support that extension.

    + +

    Overviews can also be cleared with the -clean option of gdaladdo (or +BuildOverviews() with nOverviews=0)

    + +

    Metadata

    + +

    GDAL uses the standardized +gpkg_metadata and +gpkg_metadata_reference tables to read and write metadata.

    + +

    GDAL metadata, from the default metadata domain and possibly other metadata +domains, is serialized in a single XML document, conformant with the format used +in GDAL PAM (Persistent Auxiliary Metadata) .aux.xml files, and registered with +md_scope=dataset and md_standard_uri=http://gdal.org in gpkg_metadata. In +gpkg_metadata_reference, this entry is referenced with a reference_scope=table and +table_name={name of the raster table}

    + +

    It is possible to read and write metadata that applies to the global GeoPackage, +and not only to the raster table, by using the GEOPACKAGE metadata +domain.

    + +

    Metadata not originating from GDAL can be read by the driver and will be +exposed as metadata items with keys of the form GPKG_METADATA_ITEM_XXX and +values the content of the metadata columns of the gpkg_metadata table. +Update of such metadata is not currently supported through GDAL interfaces ( +although it can be through direct SQL commands).

    + +

    The specific DESCRIPTION and IDENTIFIER metadata item of the default metadata +domain can be used in read/write to read from/update the corresponding columns of +the gpkg_contents table.

    + +

    Examples

    + +
      +
    • +Simple translation of a GeoTIFF into GeoPackage. The table 'byte' will +be created with the tiles. +
      +% gdal_translate -of GPKG byte.tif byte.gpkg
      +
      +
    • + +
    • +Translation of a GeoTIFF into GeoPackage using WebP tiles +
      +% gdal_translate -of GPKG byte.tif byte.gpkg -co TILE_FORMAT=WEBP
      +
      +
    • + +
    • +Translation of a GeoTIFF into GeoPackage using GoogleMapsCompatible tiling scheme +(with reprojection and resampling if needed) +
      +% gdal_translate -of GPKG byte.tif byte.gpkg -co TILING_SCHEME=GoogleMapsCompatible
      +
      +
    • + +
    • +Building of overviews of an existing GeoPackage +
      +% gdaladdo -r cubic my.gpkg 2 4 8 16 32 64
      +
      +
    • + +
    • +Addition of a new subdataset to an existing GeoPackage, and choose a non +default name for the raster table. +
      +% gdal_translate -of GPKG new.tif existing.gpkg -co APPEND_SUBDATASET=YES -co RASTER_TABLE=new_table
      +
      +
    • + +
    • +Reprojection of an input dataset to GeoPackage +
      +% gdalwarp -of GPKG in.tif out.gpkg -t_srs EPSG:3857
      +
      +
    • + +
    • +Open a specific raster table in a GeoPackage +
      +% gdalinfo my.gpkg -oo TABLE=a_table
      +
      +
    • + +
    + +

    See Also

    + + + +

    Other notes

    + +

    Development of raster support in the GeoPackage driver was financially supported +by Safe Software.

    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/gdalgeopackagerasterband.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/gdalgeopackagerasterband.cpp new file mode 100644 index 000000000..787799094 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/gdalgeopackagerasterband.cpp @@ -0,0 +1,1935 @@ +/****************************************************************************** + * $Id: gdalgeopackagerasterband.cpp 28601 2015-03-03 11:06:40Z rouault $ + * + * Project: GeoPackage Translator + * Purpose: Implements GDALGeoPackageRasterBand class + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFpszFileNameTWARE. + ****************************************************************************/ + +#include "ogr_geopackage.h" +#include "memdataset.h" +#include "gdal_alg_priv.h" + +//#define DEBUG_VERBOSE + +/************************************************************************/ +/* GDALGeoPackageRasterBand() */ +/************************************************************************/ + +GDALGeoPackageRasterBand::GDALGeoPackageRasterBand(GDALGeoPackageDataset* poDS, + int nBand, + int nTileWidth, int nTileHeight) +{ + this->poDS = poDS; + this->nBand = nBand; + eDataType = GDT_Byte; + nBlockXSize = nTileWidth; + nBlockYSize = nTileHeight; +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +CPLErr GDALGeoPackageRasterBand::FlushCache() +{ + GDALGeoPackageDataset* poGDS = (GDALGeoPackageDataset* )poDS; + if( GDALPamRasterBand::FlushCache() != CE_None ) + return CE_Failure; + return poGDS->FlushCacheWithErrCode(); +} + +/************************************************************************/ +/* GetColorTable() */ +/************************************************************************/ + +GDALColorTable* GDALGeoPackageRasterBand::GetColorTable() +{ + GDALGeoPackageDataset* poGDS = (GDALGeoPackageDataset* )poDS; + if( poGDS->nBands == 1 ) + { + if( !poGDS->m_bTriedEstablishingCT ) + { + poGDS->m_bTriedEstablishingCT = TRUE; + if( poGDS->m_poParentDS != NULL ) + { + poGDS->m_poCT = poGDS->m_poParentDS->GetRasterBand(1)->GetColorTable(); + if( poGDS->m_poCT ) + poGDS->m_poCT = poGDS->m_poCT->Clone(); + return poGDS->m_poCT; + } + + char* pszSQL = sqlite3_mprintf("SELECT tile_data FROM '%q' " + "WHERE zoom_level = %d LIMIT 1", + poGDS->m_osRasterTable.c_str(), poGDS->m_nZoomLevel); + sqlite3_stmt* hStmt = NULL; + int rc = sqlite3_prepare(poGDS->GetDB(), pszSQL, -1, &hStmt, NULL); + if ( rc == SQLITE_OK ) + { + rc = sqlite3_step( hStmt ); + if( rc == SQLITE_ROW && sqlite3_column_type( hStmt, 0 ) == SQLITE_BLOB ) + { + const int nBytes = sqlite3_column_bytes( hStmt, 0 ); + GByte* pabyRawData = (GByte*)sqlite3_column_blob( hStmt, 0 ); + CPLString osMemFileName; + osMemFileName.Printf("/vsimem/gpkg_read_tile_%p", this); + VSILFILE * fp = VSIFileFromMemBuffer( osMemFileName.c_str(), pabyRawData, + nBytes, FALSE); + VSIFCloseL(fp); + + /* Only PNG can have color table */ + const char* apszDrivers[] = { "PNG", NULL }; + GDALDataset* poDSTile = (GDALDataset*)GDALOpenEx(osMemFileName.c_str(), + GDAL_OF_RASTER | GDAL_OF_INTERNAL, + apszDrivers, NULL, NULL); + if( poDSTile != NULL ) + { + if( poDSTile->GetRasterCount() == 1 ) + { + poGDS->m_poCT = poDSTile->GetRasterBand(1)->GetColorTable(); + if( poGDS->m_poCT != NULL ) + poGDS->m_poCT = poGDS->m_poCT->Clone(); + } + GDALClose( poDSTile ); + } + + VSIUnlink(osMemFileName); + } + } + sqlite3_free(pszSQL); + sqlite3_finalize(hStmt); + } + + return poGDS->m_poCT; + } + else + return NULL; +} + +/************************************************************************/ +/* SetColorTable() */ +/************************************************************************/ + +CPLErr GDALGeoPackageRasterBand::SetColorTable(GDALColorTable* poCT) +{ + GDALGeoPackageDataset* poGDS = (GDALGeoPackageDataset* )poDS; + if( poGDS->nBands != 1 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetColorTable() only supported for a single band dataset"); + return CE_Failure; + } + if( !poGDS->m_bNew || poGDS->m_bTriedEstablishingCT ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetColorTable() only supported on a newly created dataset"); + return CE_Failure; + } + + poGDS->m_bTriedEstablishingCT = TRUE; + delete poGDS->m_poCT; + if( poCT != NULL ) + poGDS->m_poCT = poCT->Clone(); + else + poGDS->m_poCT = NULL; + return CE_None; +} + +/************************************************************************/ +/* GetColorInterpretation() */ +/************************************************************************/ + +GDALColorInterp GDALGeoPackageRasterBand::GetColorInterpretation() +{ + GDALGeoPackageDataset* poGDS = (GDALGeoPackageDataset* )poDS; + if( poGDS->nBands == 1 ) + return GetColorTable() ? GCI_PaletteIndex : GCI_GrayIndex; + else if( poGDS->nBands == 2 ) + return (nBand == 1) ? GCI_GrayIndex : GCI_AlphaBand; + else + return (GDALColorInterp) (GCI_RedBand + (nBand - 1)); +} + +/************************************************************************/ +/* SetColorInterpretation() */ +/************************************************************************/ + +CPLErr GDALGeoPackageRasterBand::SetColorInterpretation( GDALColorInterp eInterp ) +{ + GDALGeoPackageDataset* poGDS = (GDALGeoPackageDataset* )poDS; + if( eInterp == GCI_Undefined ) + return CE_None; + if( poGDS->nBands == 1 && (eInterp == GCI_GrayIndex || eInterp == GCI_PaletteIndex) ) + return CE_None; + if( poGDS->nBands == 2 && + ((nBand == 1 && eInterp == GCI_GrayIndex) || (nBand == 2 && eInterp == GCI_AlphaBand)) ) + return CE_None; + if( poGDS->nBands >= 3 && eInterp == GCI_RedBand + nBand - 1 ) + return CE_None; + CPLError(CE_Warning, CPLE_NotSupported, "%s color interpretation not supported. Will be ignored", + GDALGetColorInterpretationName(eInterp)); + return CE_Warning; +} + +/************************************************************************/ +/* GPKGFindBestEntry() */ +/************************************************************************/ + +static int GPKGFindBestEntry(GDALColorTable* poCT, + GByte c1, GByte c2, GByte c3, GByte c4, + int nTileBandCount) +{ + int nEntries = MIN(256, poCT->GetColorEntryCount()); + int iBestIdx = 0; + int nBestDistance = 4 * 256 * 256; + for(int i=0;iGetColorEntry(i); + int nDistance = (psEntry->c1 - c1) * (psEntry->c1 - c1) + + (psEntry->c2 - c2) * (psEntry->c2 - c2) + + (psEntry->c3 - c3) * (psEntry->c3 - c3); + if( nTileBandCount == 4 ) + nDistance += (psEntry->c4 - c4) * (psEntry->c4 - c4); + if( nDistance < nBestDistance ) + { + iBestIdx = i; + nBestDistance = nDistance; + } + } + return iBestIdx; +} + +/************************************************************************/ +/* ReadTile() */ +/************************************************************************/ + +CPLErr GDALGeoPackageDataset::ReadTile(const CPLString& osMemFileName, + GByte* pabyTileData, + int* pbIsLossyFormat) +{ + const char* apszDrivers[] = { "JPEG", "PNG", "WEBP", NULL }; + int nBlockXSize, nBlockYSize; + GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize); + GDALDataset* poDSTile = (GDALDataset*)GDALOpenEx(osMemFileName.c_str(), + GDAL_OF_RASTER | GDAL_OF_INTERNAL, + apszDrivers, NULL, NULL); + if( poDSTile == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot parse tile data"); + memset(pabyTileData, 0, nBands * nBlockXSize * nBlockYSize ); + return CE_Failure; + } + + int nTileBandCount = poDSTile->GetRasterCount(); + + if( !(poDSTile->GetRasterXSize() == nBlockXSize && + poDSTile->GetRasterYSize() == nBlockYSize && + (nTileBandCount >= 1 && nTileBandCount <= 4)) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Inconsistent tiles characteristics"); + GDALClose(poDSTile); + memset(pabyTileData, 0, nBands * nBlockXSize * nBlockYSize ); + return CE_Failure; + } + + if( poDSTile->RasterIO(GF_Read, 0, 0, nBlockXSize, nBlockYSize, + pabyTileData, + nBlockXSize, nBlockYSize, + GDT_Byte, + poDSTile->GetRasterCount(), NULL, + 0, 0, 0, NULL) != CE_None ) + { + GDALClose(poDSTile); + memset(pabyTileData, 0, nBands * nBlockXSize * nBlockYSize ); + return CE_Failure; + } + + GDALColorTable* poCT = NULL; + if( nBands == 1 || nTileBandCount == 1 ) + { + poCT = poDSTile->GetRasterBand(1)->GetColorTable(); + GetRasterBand(1)->GetColorTable(); + } + + if( pbIsLossyFormat ) + *pbIsLossyFormat = !EQUAL(poDSTile->GetDriver()->GetDescription(), "PNG") || + (poCT != NULL && poCT->GetColorEntryCount() == 256) /* PNG8 */; + + /* Map RGB(A) tile to single-band color indexed */ + if( nBands == 1 && m_poCT != NULL && nTileBandCount != 1 ) + { + std::map< GUInt32, int > oMapEntryToIndex; + int nEntries = MIN(256, m_poCT->GetColorEntryCount()); + for(int i=0;iGetColorEntry(i); + GByte c1 = (GByte)psEntry->c1; + GByte c2 = (GByte)psEntry->c2; + GByte c3 = (GByte)psEntry->c3; + GUInt32 nVal = c1 + (c2 << 8) + (c3 << 16); + if( nTileBandCount == 4 ) nVal += ((GByte)psEntry->c4 << 24); + oMapEntryToIndex[nVal] = i; + } + int iBestEntryFor0 = GPKGFindBestEntry(m_poCT, 0, 0, 0, 0, nTileBandCount); + for(int i=0;i::iterator oMapEntryToIndexIter = oMapEntryToIndex.find(nVal); + if( oMapEntryToIndexIter == oMapEntryToIndex.end() ) + /* Could happen with JPEG tiles */ + pabyTileData[i] = (GByte) GPKGFindBestEntry(m_poCT, c1, c2, c3, c4, nTileBandCount); + else + pabyTileData[i] = (GByte) oMapEntryToIndexIter->second; + } + } + GDALClose( poDSTile ); + return CE_None; + } + + if( nBands == 1 && nTileBandCount == 1 && poCT != NULL && m_poCT != NULL && + !poCT->IsSame(m_poCT) ) + { + CPLError(CE_Warning, CPLE_NotSupported, "Different color tables. Unhandled for now"); + } + else if( (nBands == 1 && nTileBandCount >= 3) || + (nBands == 1 && nTileBandCount == 1 && m_poCT != NULL && poCT == NULL) || + ((nBands == 1 || nBands == 2) && nTileBandCount == 1 && m_poCT == NULL && poCT != NULL) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Inconsistent dataset and tiles band characteristics"); + } + + if( nBands == 2 ) + { + if( nTileBandCount == 1 || nTileBandCount == 3 /* assuming that the RGB is Grey,Grey,Grey */ ) + { + /* Create fully opaque alpha */ + memset(pabyTileData + 1 * nBlockXSize * nBlockYSize, + 255, nBlockXSize * nBlockYSize); + } + else if( nTileBandCount == 4 ) + { + /* Transfer alpha band */ + memcpy(pabyTileData + 1 * nBlockXSize * nBlockYSize, + pabyTileData + 3 * nBlockXSize * nBlockYSize, + nBlockXSize * nBlockYSize); + } + } + else if( nTileBandCount == 2 ) + { + /* Do Grey+Alpha -> RGBA */ + memcpy(pabyTileData + 3 * nBlockXSize * nBlockYSize, + pabyTileData + 1 * nBlockXSize * nBlockYSize, + nBlockXSize * nBlockYSize); + memcpy(pabyTileData + 1 * nBlockXSize * nBlockYSize, + pabyTileData, nBlockXSize * nBlockYSize); + memcpy(pabyTileData + 2 * nBlockXSize * nBlockYSize, + pabyTileData, nBlockXSize * nBlockYSize); + } + else if( nTileBandCount == 1 && !(nBands == 1 && m_poCT != NULL) ) + { + /* Expand color indexed to RGB(A) */ + if( poCT != NULL ) + { + int i; + GByte abyCT[4*256]; + int nEntries = MIN(256, poCT->GetColorEntryCount()); + for(i=0;iGetColorEntry(i); + abyCT[4*i] = (GByte)psEntry->c1; + abyCT[4*i+1] = (GByte)psEntry->c2; + abyCT[4*i+2] = (GByte)psEntry->c3; + abyCT[4*i+3] = (GByte)psEntry->c4; + } + for(;i<256;i++) + { + abyCT[4*i] = 0; + abyCT[4*i+1] = 0; + abyCT[4*i+2] = 0; + abyCT[4*i+3] = 0; + } + for(i=0;iGetBlockSize(&nBlockXSize, &nBlockYSize); + if( m_nShiftXPixelsMod ) + { + int i; + for(i = 0; i < 4; i ++) + { + if( m_asCachedTilesDesc[i].nRow == nRow && + m_asCachedTilesDesc[i].nCol == nCol ) + { + if( m_asCachedTilesDesc[i].nIdxWithinTileData >= 0 ) + { + return m_pabyCachedTiles + + m_asCachedTilesDesc[i].nIdxWithinTileData * 4 * nBlockXSize * nBlockYSize; + } + else + { + if( i == 0 ) + m_asCachedTilesDesc[i].nIdxWithinTileData = + (m_asCachedTilesDesc[1].nIdxWithinTileData == 0 ) ? 1 : 0; + else if( i == 1 ) + m_asCachedTilesDesc[i].nIdxWithinTileData = + (m_asCachedTilesDesc[0].nIdxWithinTileData == 0 ) ? 1 : 0; + else if( i == 2 ) + m_asCachedTilesDesc[i].nIdxWithinTileData = + (m_asCachedTilesDesc[3].nIdxWithinTileData == 2 ) ? 3 : 2; + else + m_asCachedTilesDesc[i].nIdxWithinTileData = + (m_asCachedTilesDesc[2].nIdxWithinTileData == 2 ) ? 3 : 2; + pabyData = m_pabyCachedTiles + + m_asCachedTilesDesc[i].nIdxWithinTileData * 4 * nBlockXSize * nBlockYSize; + break; + } + } + } + CPLAssert(i < 4); + } + else + pabyData = m_pabyCachedTiles; + + return ReadTile(nRow, nCol, pabyData); +} + +/************************************************************************/ +/* ReadTile() */ +/************************************************************************/ + +GByte* GDALGeoPackageDataset::ReadTile(int nRow, int nCol, GByte* pabyData, + int* pbIsLossyFormat) +{ + int rc; + int nBlockXSize, nBlockYSize; + GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize); + + if( pbIsLossyFormat ) *pbIsLossyFormat = FALSE; + + if( nRow < 0 || nCol < 0 || nRow >= m_nTileMatrixHeight || + nCol >= m_nTileMatrixWidth ) + { + memset(pabyData, 0, nBands * nBlockXSize * nBlockYSize ); + return pabyData; + } + + //CPLDebug("GPKG", "For block (blocky=%d, blockx=%d) request tile (row=%d, col=%d)", + // nBlockYOff, nBlockXOff, nRow, nCol); + char* pszSQL = sqlite3_mprintf("SELECT tile_data FROM '%q' " + "WHERE zoom_level = %d AND tile_row = %d AND tile_column = %d%s", + m_osRasterTable.c_str(), m_nZoomLevel, nRow, nCol, + m_osWHERE.size() ? CPLSPrintf(" AND (%s)", m_osWHERE.c_str()): ""); +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "%s", pszSQL); +#endif + sqlite3_stmt* hStmt = NULL; + rc = sqlite3_prepare(GetDB(), pszSQL, -1, &hStmt, NULL); + if ( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "failed to prepare SQL: %s", pszSQL); + sqlite3_free(pszSQL); + return NULL; + } + sqlite3_free(pszSQL); + rc = sqlite3_step( hStmt ); + + if( rc == SQLITE_ROW && sqlite3_column_type( hStmt, 0 ) == SQLITE_BLOB ) + { + const int nBytes = sqlite3_column_bytes( hStmt, 0 ); + GByte* pabyRawData = (GByte*)sqlite3_column_blob( hStmt, 0 ); + CPLString osMemFileName; + osMemFileName.Printf("/vsimem/gpkg_read_tile_%p", this); + VSILFILE * fp = VSIFileFromMemBuffer( osMemFileName.c_str(), pabyRawData, + nBytes, FALSE); + VSIFCloseL(fp); + + ReadTile(osMemFileName, pabyData, pbIsLossyFormat); + VSIUnlink(osMemFileName); + sqlite3_finalize(hStmt); + } + else + { + sqlite3_finalize(hStmt); + hStmt = NULL; + + if( m_hTempDB && (m_nShiftXPixelsMod || m_nShiftYPixelsMod) ) + { + const char* pszSQLNew = CPLSPrintf( + "SELECT partial_flag, tile_data_band_1, tile_data_band_2, " + "tile_data_band_3, tile_data_band_4 FROM partial_tiles WHERE " + "zoom_level = %d AND tile_row = %d AND tile_column = %d", + m_nZoomLevel, nRow, nCol); +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "%s", pszSQLNew); +#endif + rc = sqlite3_prepare_v2(m_hTempDB, pszSQLNew, strlen(pszSQLNew), &hStmt, NULL); + if ( rc != SQLITE_OK ) + { + memset(pabyData, 0, nBands * nBlockXSize * nBlockYSize ); + CPLError( CE_Failure, CPLE_AppDefined, "sqlite3_prepare(%s) failed: %s", + pszSQLNew, sqlite3_errmsg( m_hTempDB ) ); + return pabyData; + } + + rc = sqlite3_step(hStmt); + if ( rc == SQLITE_ROW ) + { + int nPartialFlag = sqlite3_column_int(hStmt, 0); + for(int iBand = 1; iBand <= nBands; iBand ++ ) + { + GByte* pabyDestBand = pabyData + (iBand - 1) * nBlockXSize * nBlockYSize; + if( nPartialFlag & (((1 << 4)-1) << (4 * (iBand - 1))) ) + { + CPLAssert( sqlite3_column_bytes(hStmt, iBand) == nBlockXSize * nBlockYSize ); + memcpy( pabyDestBand, + sqlite3_column_blob(hStmt, iBand), + nBlockXSize * nBlockYSize ); + } + else + { + memset(pabyDestBand, 0, nBlockXSize * nBlockYSize ); + } + } + } + else + { + memset(pabyData, 0, nBands * nBlockXSize * nBlockYSize ); + } + sqlite3_finalize(hStmt); + } + else + { + memset(pabyData, 0, nBands * nBlockXSize * nBlockYSize ); + } + } + + return pabyData; +} + +/************************************************************************/ +/* IReadBlock() */ +/************************************************************************/ + +CPLErr GDALGeoPackageRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, + void* pData) +{ + GDALGeoPackageDataset* poGDS = (GDALGeoPackageDataset* )poDS; + //CPLDebug("GPKG", "IReadBlock(nBand=%d,nBlockXOff=%d,nBlockYOff=%d", + // nBand,nBlockXOff,nBlockYOff); + + int nRowMin = nBlockYOff + poGDS->m_nShiftYTiles; + int nRowMax = nRowMin; + if( poGDS->m_nShiftYPixelsMod ) + nRowMax ++; + + int nColMin = nBlockXOff + poGDS->m_nShiftXTiles; + int nColMax = nColMin; + if( poGDS->m_nShiftXPixelsMod ) + nColMax ++; + + /* Optimize for left to right reading at constant row */ + if( poGDS->m_nShiftXPixelsMod ) + { + if( nRowMin == poGDS->m_asCachedTilesDesc[0].nRow && + nColMin == poGDS->m_asCachedTilesDesc[0].nCol + 1 && + poGDS->m_asCachedTilesDesc[0].nIdxWithinTileData >= 0 ) + { + CPLAssert(nRowMin == poGDS->m_asCachedTilesDesc[1].nRow); + CPLAssert(nColMin == poGDS->m_asCachedTilesDesc[1].nCol); + CPLAssert(poGDS->m_asCachedTilesDesc[0].nIdxWithinTileData == 0 || + poGDS->m_asCachedTilesDesc[0].nIdxWithinTileData == 1); + + /* 0 1 --> 1 -1 */ + /* 2 3 3 -1 */ + /* or */ + /* 1 0 --> 0 -1 */ + /* 3 2 2 -1 */ + poGDS->m_asCachedTilesDesc[0].nIdxWithinTileData = poGDS->m_asCachedTilesDesc[1].nIdxWithinTileData; + poGDS->m_asCachedTilesDesc[2].nIdxWithinTileData = poGDS->m_asCachedTilesDesc[3].nIdxWithinTileData; + } + else + { + poGDS->m_asCachedTilesDesc[0].nIdxWithinTileData = -1; + poGDS->m_asCachedTilesDesc[2].nIdxWithinTileData = -1; + } + poGDS->m_asCachedTilesDesc[0].nRow = nRowMin; + poGDS->m_asCachedTilesDesc[0].nCol = nColMin; + poGDS->m_asCachedTilesDesc[1].nRow = nRowMin; + poGDS->m_asCachedTilesDesc[1].nCol = nColMin + 1; + poGDS->m_asCachedTilesDesc[2].nRow = nRowMin + 1; + poGDS->m_asCachedTilesDesc[2].nCol = nColMin; + poGDS->m_asCachedTilesDesc[3].nRow = nRowMin + 1; + poGDS->m_asCachedTilesDesc[3].nCol = nColMin + 1; + poGDS->m_asCachedTilesDesc[1].nIdxWithinTileData = -1; + poGDS->m_asCachedTilesDesc[3].nIdxWithinTileData = -1; + + } + + for(int nRow = nRowMin; nRow <= nRowMax; nRow ++) + { + for(int nCol = nColMin; nCol <= nColMax; nCol++ ) + { + GByte* pabyTileData = poGDS->ReadTile(nRow, nCol); + if( pabyTileData == NULL ) + return CE_Failure; + + for(int iBand=1;iBand<=poGDS->nBands;iBand++) + { + GDALRasterBlock* poBlock = NULL; + GByte* pabyDest; + if( iBand == nBand ) + { + pabyDest = (GByte*)pData; + } + else + { + poBlock = + poGDS->GetRasterBand(iBand)->GetLockedBlockRef(nBlockXOff, nBlockYOff, TRUE); + if( poBlock == NULL ) + continue; + if( poBlock->GetDirty() ) + { + poBlock->DropLock(); + continue; + } + pabyDest = (GByte*) poBlock->GetDataRef(); + } + + // Composite tile data into block data + if( poGDS->m_nShiftXPixelsMod == 0 && poGDS->m_nShiftYPixelsMod == 0 ) + { + memcpy(pabyDest, + pabyTileData + (iBand - 1) * nBlockXSize * nBlockYSize, + nBlockXSize * nBlockYSize); + } + else + { + int nSrcXOffset, nSrcXSize, nSrcYOffset, nSrcYSize; + int nDstXOffset, nDstYOffset; + if( nCol == nColMin ) + { + nSrcXOffset = poGDS->m_nShiftXPixelsMod; + nSrcXSize = nBlockXSize - poGDS->m_nShiftXPixelsMod; + nDstXOffset = 0; + } + else + { + nSrcXOffset = 0; + nSrcXSize = poGDS->m_nShiftXPixelsMod; + nDstXOffset = nBlockXSize - poGDS->m_nShiftXPixelsMod; + } + if( nRow == nRowMin ) + { + nSrcYOffset = poGDS->m_nShiftYPixelsMod; + nSrcYSize = nBlockYSize - poGDS->m_nShiftYPixelsMod; + nDstYOffset = 0; + } + else + { + nSrcYOffset = 0; + nSrcYSize = poGDS->m_nShiftYPixelsMod; + nDstYOffset = nBlockYSize - poGDS->m_nShiftYPixelsMod; + } + //CPLDebug("GPKG", "Copy source tile x=%d,w=%d,y=%d,h=%d into buffet at x=%d,y=%d", + // nSrcXOffset, nSrcXSize, nSrcYOffset, nSrcYSize, nDstXOffset, nDstYOffset); + for( int y=0; yDropLock(); + + } + } + } + + return CE_None; +} + +/************************************************************************/ +/* WEBPSupports4Bands() */ +/************************************************************************/ + +static int WEBPSupports4Bands() +{ + static int bRes = -1; + if( bRes < 0 ) + { + GDALDriver* poDrv = (GDALDriver*) GDALGetDriverByName("WEBP"); + if( poDrv == NULL || CSLTestBoolean(CPLGetConfigOption("GPKG_SIMUL_WEBP_3BAND", "FALSE")) ) + bRes = FALSE; + else + { + // LOSSLESS and RGBA support appeared in the same version + bRes = strstr(poDrv->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST), "LOSSLESS") != NULL; + } + if( poDrv != NULL && !bRes ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "The version of WEBP available does not support 4-band RGBA"); + } + } + return bRes; +} + +/************************************************************************/ +/* WriteTile() */ +/************************************************************************/ + +CPLErr GDALGeoPackageDataset::WriteTile() +{ + CPLAssert(!m_bInWriteTile); + m_bInWriteTile = TRUE; + CPLErr eErr = WriteTileInternal(); + m_bInWriteTile = FALSE; + return eErr; +} + +/* should only be called by WriteTile() */ +CPLErr GDALGeoPackageDataset::WriteTileInternal() +{ + if( !(bUpdate && m_asCachedTilesDesc[0].nRow >= 0 && + m_asCachedTilesDesc[0].nCol >= 0 && + m_asCachedTilesDesc[0].nIdxWithinTileData == 0) ) + return CE_None; + + int nRow = m_asCachedTilesDesc[0].nRow; + int nCol = m_asCachedTilesDesc[0].nCol; + + int bAllDirty = TRUE; + int bAllNonDirty = TRUE; + int i; + for(i=0;iGetBlockSize(&nBlockXSize, &nBlockYSize); + + /* If all bands for that block are not dirty/written, we need to */ + /* fetch the missing ones if the tile exists */ + int bIsLossyFormat = FALSE; + if( !bAllDirty ) + { + for(i=1;i<=3;i++) + { + m_asCachedTilesDesc[i].nRow = -1; + m_asCachedTilesDesc[i].nCol = -1; + m_asCachedTilesDesc[i].nIdxWithinTileData = -1; + } + ReadTile(nRow, nCol, m_pabyCachedTiles + 4 * nBlockXSize * nBlockYSize, + &bIsLossyFormat); + for(i=0;i 0); + CPLAssert(nYOff + nBlockYSize > 0); + /* Can happen if the tile of the raster is less than the block size */ + if( nXOff >= nRasterXSize || nYOff >= nRasterYSize ) + return CE_None; + + /* Validity area of tile data in intra-tile coordinate space */ + int iXOff = 0; + int iYOff = 0; + int iXCount = nBlockXSize; + int iYCount = nBlockYSize; + + int bPartialTile = FALSE; + int nAlphaBand = (nBands == 2) ? 2 : (nBands == 4) ? 4 : 0; + if( nAlphaBand == 0 ) + { + if( nXOff < 0 ) + { + bPartialTile = TRUE; + iXOff = -nXOff; + iXCount += nXOff; + } + if( nXOff + nBlockXSize > nRasterXSize ) + { + bPartialTile = TRUE; + iXCount -= nXOff + nBlockXSize - nRasterXSize; + } + if( nYOff < 0 ) + { + bPartialTile = TRUE; + iYOff = -nYOff; + iYCount += nYOff; + } + if( nYOff + nBlockYSize > nRasterYSize ) + { + bPartialTile = TRUE; + iYCount -= nYOff + nBlockYSize - nRasterYSize; + } + CPLAssert(iXOff >= 0); + CPLAssert(iYOff >= 0); + CPLAssert(iXCount > 0); + CPLAssert(iYCount > 0); + CPLAssert(iXOff + iXCount <= nBlockXSize); + CPLAssert(iYOff + iYCount <= nBlockYSize); + } + + m_asCachedTilesDesc[0].nRow = -1; + m_asCachedTilesDesc[0].nCol = -1; + m_asCachedTilesDesc[0].nIdxWithinTileData = -1; + m_asCachedTilesDesc[0].abBandDirty[0] = FALSE; + m_asCachedTilesDesc[0].abBandDirty[1] = FALSE; + m_asCachedTilesDesc[0].abBandDirty[2] = FALSE; + m_asCachedTilesDesc[0].abBandDirty[3] = FALSE; + + CPLErr eErr = CE_Failure; + + int bAllOpaque = TRUE; + if( m_poCT == NULL && nAlphaBand != 0 ) + { + GByte byFirstAlphaVal = m_pabyCachedTiles[(nAlphaBand-1) * nBlockXSize * nBlockYSize]; + for(i=1;iGetColorTable(); + + if( m_eTF == GPKG_TF_PNG_JPEG ) + { + bTileDriverSupports1Band = TRUE; + if( bPartialTile || (nBands == 2 && !bAllOpaque) || (nBands == 4 && !bAllOpaque) || m_poCT != NULL ) + { + pszDriverName = "PNG"; + bTileDriverSupports2Bands = TRUE; + bTileDriverSupports4Bands = TRUE; + bTileDriverSupportsCT = TRUE; + } + else + pszDriverName = "JPEG"; + } + else if( m_eTF == GPKG_TF_PNG || + m_eTF == GPKG_TF_PNG8 ) + { + pszDriverName = "PNG"; + bTileDriverSupports1Band = TRUE; + bTileDriverSupports2Bands = TRUE; + bTileDriverSupports4Bands = TRUE; + bTileDriverSupportsCT = TRUE; + } + else if( m_eTF == GPKG_TF_JPEG ) + { + pszDriverName = "JPEG"; + bTileDriverSupports1Band = TRUE; + } + else if( m_eTF == GPKG_TF_WEBP ) + { + pszDriverName = "WEBP"; + bTileDriverSupports4Bands = WEBPSupports4Bands(); + } + else + CPLAssert(0); + + GDALDriver* poDriver = (GDALDriver*) GDALGetDriverByName(pszDriverName); + if( poDriver != NULL) + { + GDALDataset* poMEMDS = MEMDataset::Create("", nBlockXSize, nBlockYSize, + 0, GDT_Byte, NULL); + int nTileBands = nBands; + if( bPartialTile && nBands == 1 && m_poCT == NULL && bTileDriverSupports2Bands ) + nTileBands = 2; + else if( bPartialTile && bTileDriverSupports4Bands ) + nTileBands = 4; + else if( m_eTF == GPKG_TF_PNG8 && nBands >= 3 && bAllOpaque && !bPartialTile ) + nTileBands = 1; + else if( nBands == 2 ) + { + if ( bAllOpaque ) + { + if (bTileDriverSupports2Bands ) + nTileBands = 1; + else + nTileBands = 3; + } + else if( !bTileDriverSupports2Bands ) + { + if( bTileDriverSupports4Bands ) + nTileBands = 4; + else + nTileBands = 3; + } + } + else if( nBands == 4 && (bAllOpaque || !bTileDriverSupports4Bands) ) + nTileBands = 3; + else if( nBands == 1 && m_poCT != NULL && !bTileDriverSupportsCT ) + { + nTileBands = 3; + if( bTileDriverSupports4Bands ) + { + for(i=0;iGetColorEntryCount();i++) + { + const GDALColorEntry* psEntry = m_poCT->GetColorEntry(i); + if( psEntry->c4 == 0 ) + { + nTileBands = 4; + break; + } + } + } + } + else if( nBands == 1 && m_poCT == NULL && !bTileDriverSupports1Band ) + nTileBands = 3; + + if( bPartialTile && (nTileBands == 2 || nTileBands == 4) ) + { + int nTargetAlphaBand = nTileBands; + memset(m_pabyCachedTiles + (nTargetAlphaBand-1) * nBlockXSize * nBlockYSize, 0, + nBlockXSize * nBlockYSize); + for(int iY = iYOff; iY < iYOff + iYCount; iY ++) + { + memset(m_pabyCachedTiles + ((nTargetAlphaBand-1) * nBlockYSize + iY) * nBlockXSize + iXOff, + 255, iXCount); + } + } + + for(i=0;i= 3 ) + iSrc = (i < 3) ? 0 : 1; + int nRet = CPLPrintPointer(szDataPointer, + m_pabyCachedTiles + iSrc * nBlockXSize * nBlockYSize, + sizeof(szDataPointer)); + szDataPointer[nRet] = '\0'; + papszOptions = CSLSetNameValue(papszOptions, "DATAPOINTER", szDataPointer); + poMEMDS->AddBand(GDT_Byte, papszOptions); + if( i == 0 && nTileBands == 1 && m_poCT != NULL ) + poMEMDS->GetRasterBand(1)->SetColorTable(m_poCT); + CSLDestroy(papszOptions); + } + + if( m_eTF == GPKG_TF_PNG8 && nTileBands == 1 && nBands >= 3 ) + { + GDALDataset* poMEM_RGB_DS = MEMDataset::Create("", nBlockXSize, nBlockYSize, + 0, GDT_Byte, NULL); + for(i=0;i<3;i++) + { + char** papszOptions = NULL; + char szDataPointer[32]; + int nRet = CPLPrintPointer(szDataPointer, + m_pabyCachedTiles + i * nBlockXSize * nBlockYSize, + sizeof(szDataPointer)); + szDataPointer[nRet] = '\0'; + papszOptions = CSLSetNameValue(papszOptions, "DATAPOINTER", szDataPointer); + poMEM_RGB_DS->AddBand(GDT_Byte, papszOptions); + CSLDestroy(papszOptions); + } + + if( m_pabyHugeColorArray == NULL ) + { + if( nBlockXSize * nBlockYSize <= 65536 ) + m_pabyHugeColorArray = (GByte*) VSIMalloc(MEDIAN_CUT_AND_DITHER_BUFFER_SIZE_65536); + else + m_pabyHugeColorArray = (GByte*) VSIMalloc2(256 * 256 * 256, sizeof(int)); + } + + GDALColorTable* poCT = new GDALColorTable(); + GDALComputeMedianCutPCTInternal( poMEM_RGB_DS->GetRasterBand(1), + poMEM_RGB_DS->GetRasterBand(2), + poMEM_RGB_DS->GetRasterBand(3), + /*NULL, NULL, NULL,*/ + m_pabyCachedTiles, + m_pabyCachedTiles + nBlockXSize * nBlockYSize, + m_pabyCachedTiles + 2 * nBlockXSize * nBlockYSize, + NULL, + 256, /* max colors */ + 8, /* bit depth */ + (int*)m_pabyHugeColorArray, /* preallocated histogram */ + poCT, + NULL, NULL ); + + GDALDitherRGB2PCTInternal( poMEM_RGB_DS->GetRasterBand(1), + poMEM_RGB_DS->GetRasterBand(2), + poMEM_RGB_DS->GetRasterBand(3), + poMEMDS->GetRasterBand(1), + poCT, + 8, /* bit depth */ + (GInt16*)m_pabyHugeColorArray, /* pasDynamicColorMap */ + m_bDither, + NULL, NULL ); + poMEMDS->GetRasterBand(1)->SetColorTable(poCT); + delete poCT; + GDALClose( poMEM_RGB_DS ); + } + else if( nBands == 1 && m_poCT != NULL && nTileBands > 1 ) + { + GByte abyCT[4*256]; + int nEntries = MIN(256, m_poCT->GetColorEntryCount()); + for(i=0;iGetColorEntry(i); + abyCT[4*i] = (GByte)psEntry->c1; + abyCT[4*i+1] = (GByte)psEntry->c2; + abyCT[4*i+2] = (GByte)psEntry->c3; + abyCT[4*i+3] = (GByte)psEntry->c4; + } + for(;i<256;i++) + { + abyCT[4*i] = 0; + abyCT[4*i+1] = 0; + abyCT[4*i+2] = 0; + abyCT[4*i+3] = 0; + } + if( iYOff > 0 ) + { + memset(m_pabyCachedTiles + 0 * nBlockXSize * nBlockYSize, 0, nBlockXSize * iYOff); + memset(m_pabyCachedTiles + 1 * nBlockXSize * nBlockYSize, 0, nBlockXSize * iYOff); + memset(m_pabyCachedTiles + 2 * nBlockXSize * nBlockYSize, 0, nBlockXSize * iYOff); + memset(m_pabyCachedTiles + 3 * nBlockXSize * nBlockYSize, 0, nBlockXSize * iYOff); + } + for(int iY = iYOff; iY < iYOff + iYCount; iY ++) + { + if( iXOff > 0 ) + { + i = iY * nBlockXSize; + memset(m_pabyCachedTiles + 0 * nBlockXSize * nBlockYSize + i, 0, iXOff); + memset(m_pabyCachedTiles + 1 * nBlockXSize * nBlockYSize + i, 0, iXOff); + memset(m_pabyCachedTiles + 2 * nBlockXSize * nBlockYSize + i, 0, iXOff); + memset(m_pabyCachedTiles + 3 * nBlockXSize * nBlockYSize + i, 0, iXOff); + } + for(int iX = iXOff; iX < iXOff + iXCount; iX ++) + { + i = iY * nBlockXSize + iX; + GByte byVal = m_pabyCachedTiles[i]; + m_pabyCachedTiles[i] = abyCT[4*byVal]; + m_pabyCachedTiles[i + 1 * nBlockXSize * nBlockYSize] = abyCT[4*byVal+1]; + m_pabyCachedTiles[i + 2 * nBlockXSize * nBlockYSize] = abyCT[4*byVal+2]; + m_pabyCachedTiles[i + 3 * nBlockXSize * nBlockYSize] = abyCT[4*byVal+3]; + } + if( iXOff + iXCount < nBlockXSize ) + { + i = iY * nBlockXSize + iXOff + iXCount; + memset(m_pabyCachedTiles + 0 * nBlockXSize * nBlockYSize + i, 0, nBlockXSize - (iXOff + iXCount)); + memset(m_pabyCachedTiles + 1 * nBlockXSize * nBlockYSize + i, 0, nBlockXSize - (iXOff + iXCount)); + memset(m_pabyCachedTiles + 2 * nBlockXSize * nBlockYSize + i, 0, nBlockXSize - (iXOff + iXCount)); + memset(m_pabyCachedTiles + 3 * nBlockXSize * nBlockYSize + i, 0, nBlockXSize - (iXOff + iXCount)); + } + } + if( iYOff + iYCount < nBlockYSize ) + { + i = (iYOff + iYCount) * nBlockXSize; + memset(m_pabyCachedTiles + 0 * nBlockXSize * nBlockYSize + i, 0, nBlockXSize * (nBlockYSize - (iYOff + iYCount))); + memset(m_pabyCachedTiles + 1 * nBlockXSize * nBlockYSize + i, 0, nBlockXSize * (nBlockYSize - (iYOff + iYCount))); + memset(m_pabyCachedTiles + 2 * nBlockXSize * nBlockYSize + i, 0, nBlockXSize * (nBlockYSize - (iYOff + iYCount))); + memset(m_pabyCachedTiles + 3 * nBlockXSize * nBlockYSize + i, 0, nBlockXSize * (nBlockYSize - (iYOff + iYCount))); + } + } + + char** papszDriverOptions = CSLSetNameValue(NULL, "_INTERNAL_DATASET", "YES"); + if( EQUAL(pszDriverName, "JPEG") || EQUAL(pszDriverName, "WEBP") ) + { + papszDriverOptions = CSLSetNameValue( + papszDriverOptions, "QUALITY", CPLSPrintf("%d", m_nQuality)); + } + else if( EQUAL(pszDriverName, "PNG") ) + { + papszDriverOptions = CSLSetNameValue( + papszDriverOptions, "ZLEVEL", CPLSPrintf("%d", m_nZLevel)); + } +#ifdef DEBUG + VSIStatBufL sStat; + CPLAssert(VSIStatL(osMemFileName, &sStat) != 0); +#endif + GDALDataset* poOutDS = poDriver->CreateCopy(osMemFileName, poMEMDS, + FALSE, papszDriverOptions, NULL, NULL); + CSLDestroy( papszDriverOptions ); + if( poOutDS ) + { + GDALClose( poOutDS ); + vsi_l_offset nBlobSize; + GByte* pabyBlob = VSIGetMemFileBuffer(osMemFileName, &nBlobSize, TRUE); + + /* Create or commit and recreate transaction */ + GDALGeoPackageDataset* poMainDS = m_poParentDS ? m_poParentDS : this; + if( poMainDS->m_nTileInsertionCount == 0 ) + { + poMainDS->SoftStartTransaction(); + } + else if( poMainDS->m_nTileInsertionCount == 1000 ) + { + poMainDS->SoftCommitTransaction(); + poMainDS->SoftStartTransaction(); + poMainDS->m_nTileInsertionCount = 0; + } + poMainDS->m_nTileInsertionCount ++; + + char* pszSQL = sqlite3_mprintf("INSERT OR REPLACE INTO '%q' " + "(zoom_level, tile_row, tile_column, tile_data) VALUES (%d, %d, %d, ?)", + m_osRasterTable.c_str(), m_nZoomLevel, nRow, nCol); +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "%s", pszSQL); +#endif + sqlite3_stmt* hStmt = NULL; + int rc = sqlite3_prepare(GetDB(), pszSQL, -1, &hStmt, NULL); + if ( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "failed to prepare SQL %s: %s", + pszSQL, sqlite3_errmsg(hDB) ); + CPLFree(pabyBlob); + } + else + { + sqlite3_bind_blob( hStmt, 1, pabyBlob, (int)nBlobSize, CPLFree); + rc = sqlite3_step( hStmt ); + if( rc == SQLITE_DONE ) + eErr = CE_None; + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Failure when inserting tile (row=%d,col=%d) at zoom_level=%d : %s", + nRow, nCol, m_nZoomLevel, sqlite3_errmsg(GetDB())); + } + } + sqlite3_finalize(hStmt); + sqlite3_free(pszSQL); + } + + VSIUnlink(osMemFileName); + delete poMEMDS; + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot find driver %s", pszDriverName); + } + + return eErr; +} + +/************************************************************************/ +/* FlushRemainingShiftedTiles() */ +/************************************************************************/ + +CPLErr GDALGeoPackageDataset::FlushRemainingShiftedTiles() +{ + if( m_hTempDB == NULL ) + return CE_None; + + for(int i=0;i<=3;i++) + { + m_asCachedTilesDesc[i].nRow = -1; + m_asCachedTilesDesc[i].nCol = -1; + m_asCachedTilesDesc[i].nIdxWithinTileData = -1; + } + + int nBlockXSize, nBlockYSize; + GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize); + + CPLString osSQL = "SELECT tile_row, tile_column, partial_flag"; + for(int nBand = 1; nBand <= nBands; nBand++ ) + { + osSQL += CPLSPrintf(", tile_data_band_%d", nBand); + } + osSQL += CPLSPrintf(" FROM partial_tiles WHERE " + "zoom_level = %d AND partial_flag != 0", + m_nZoomLevel); + const char* pszSQL = osSQL.c_str(); + +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "%s", pszSQL); +#endif + sqlite3_stmt* hStmt = NULL; + int rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, strlen(pszSQL), &hStmt, NULL); + if ( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "sqlite3_prepare(%s) failed: %s", + pszSQL, sqlite3_errmsg( m_hTempDB ) ); + return CE_Failure; + } + + CPLErr eErr = CE_None; + int bGotPartialTiles = FALSE; + do + { + int rc = sqlite3_step(hStmt); + if ( rc == SQLITE_ROW ) + { + bGotPartialTiles = TRUE; + + int nRow = sqlite3_column_int(hStmt, 0); + int nCol = sqlite3_column_int(hStmt, 1); + int nPartialFlags = sqlite3_column_int(hStmt, 2); + for( int nBand = 1; nBand <= nBands; nBand++ ) + { + if( nPartialFlags & (((1 << 4)-1) << (4*(nBand - 1))) ) + { + CPLAssert( sqlite3_column_bytes(hStmt, 2 + nBand) == nBlockXSize * nBlockYSize ); + memcpy( m_pabyCachedTiles + (nBand-1) * nBlockXSize * nBlockYSize, + sqlite3_column_blob(hStmt, 2 + nBand), + nBlockXSize * nBlockYSize ); + } + else + { + memset( m_pabyCachedTiles + (nBand-1) * nBlockXSize * nBlockYSize, + 0, + nBlockXSize * nBlockYSize ); + } + } + + int nFullFlags = (1 << (4 * nBands)) - 1; + + // In case the partial flags indicate that there's some quadrant + // missing, check in the main database if there is already a tile + // In which case, use the parts of that tile that aren't in the + // temporary database + if( nPartialFlags != nFullFlags ) + { + char* pszNewSQL = sqlite3_mprintf("SELECT tile_data FROM '%q' " + "WHERE zoom_level = %d AND tile_row = %d AND tile_column = %d%s", + m_osRasterTable.c_str(), m_nZoomLevel, nRow, nCol, + m_osWHERE.size() ? CPLSPrintf(" AND (%s)", m_osWHERE.c_str()): ""); +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "%s", pszNewSQL); +#endif + sqlite3_stmt* hNewStmt = NULL; + rc = sqlite3_prepare(GetDB(), pszNewSQL, -1, &hNewStmt, NULL); + if ( rc == SQLITE_OK ) + { + rc = sqlite3_step( hNewStmt ); + if( rc == SQLITE_ROW && sqlite3_column_type( hNewStmt, 0 ) == SQLITE_BLOB ) + { + const int nBytes = sqlite3_column_bytes( hNewStmt, 0 ); + GByte* pabyRawData = (GByte*)sqlite3_column_blob( hNewStmt, 0 ); + CPLString osMemFileName; + osMemFileName.Printf("/vsimem/gpkg_read_tile_%p", this); + VSILFILE * fp = VSIFileFromMemBuffer( osMemFileName.c_str(), pabyRawData, + nBytes, FALSE); + VSIFCloseL(fp); + + int bIsLossyFormat; + ReadTile(osMemFileName, + m_pabyCachedTiles + 4 * nBlockXSize * nBlockYSize, + &bIsLossyFormat); + VSIUnlink(osMemFileName); + + int iYQuadrantMax = ( m_nShiftYPixelsMod ) ? 1 : 0; + int iXQuadrantMax = ( m_nShiftXPixelsMod ) ? 1 : 0; + for( int iYQuadrant = 0; iYQuadrant <= iYQuadrantMax; iYQuadrant ++ ) + { + for( int iXQuadrant = 0; iXQuadrant <= iXQuadrantMax; iXQuadrant ++ ) + { + for( int nBand = 1; nBand <= nBands; nBand ++ ) + { + int iQuadrantFlag = 0; + if( iXQuadrant == 0 && iYQuadrant == 0 ) + iQuadrantFlag |= (1 << 0); + if( iXQuadrant == iXQuadrantMax && iYQuadrant == 0 ) + iQuadrantFlag |= (1 << 1); + if( iXQuadrant == 0 && iYQuadrant == iYQuadrantMax ) + iQuadrantFlag |= (1 << 2); + if( iXQuadrant == iXQuadrantMax && iYQuadrant == iYQuadrantMax ) + iQuadrantFlag |= (1 << 3); + int nLocalFlag = iQuadrantFlag << (4 * (nBand - 1)); + if( !(nPartialFlags & nLocalFlag) ) + { + int nXOff, nYOff, nXSize, nYSize; + if( iXQuadrant == 0 && m_nShiftXPixelsMod != 0 ) + { + nXOff = 0; + nXSize = m_nShiftXPixelsMod; + } + else + { + nXOff = m_nShiftXPixelsMod; + nXSize = nBlockXSize - m_nShiftXPixelsMod; + } + if( iYQuadrant == 0 && m_nShiftYPixelsMod != 0 ) + { + nYOff = 0; + nYSize = m_nShiftYPixelsMod; + } + else + { + nYOff = m_nShiftYPixelsMod; + nYSize = nBlockYSize - m_nShiftYPixelsMod; + } + for( int iY = nYOff; iY < nYOff + nYSize; iY ++ ) + { + memcpy( m_pabyCachedTiles + ((nBand - 1) * nBlockYSize + iY) * nBlockXSize + nXOff, + m_pabyCachedTiles + ((4 + nBand - 1) * nBlockYSize + iY) * nBlockXSize + nXOff, + nXSize ); + } + } + } + } + } + } + sqlite3_finalize(hNewStmt); + } + sqlite3_free(pszNewSQL); + } + + m_asCachedTilesDesc[0].nRow = nRow; + m_asCachedTilesDesc[0].nCol = nCol; + m_asCachedTilesDesc[0].nIdxWithinTileData = 0; + m_asCachedTilesDesc[0].abBandDirty[0] = TRUE; + m_asCachedTilesDesc[0].abBandDirty[1] = TRUE; + m_asCachedTilesDesc[0].abBandDirty[2] = TRUE; + m_asCachedTilesDesc[0].abBandDirty[3] = TRUE; + + eErr = WriteTile(); + } + else + break; + } + while( eErr == CE_None); + + sqlite3_finalize(hStmt); + + if( bGotPartialTiles ) + { + pszSQL = CPLSPrintf("UPDATE partial_tiles SET zoom_level = %d, " + "partial_flag = 0 WHERE zoom_level = %d AND partial_flag != 0", + -1-m_nZoomLevel, m_nZoomLevel); +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "%s", pszSQL); +#endif + SQLCommand(m_hTempDB, pszSQL); + } + + return eErr; +} + +/************************************************************************/ +/* WriteShiftedTile() */ +/************************************************************************/ + +CPLErr GDALGeoPackageDataset::WriteShiftedTile(int nRow, int nCol, int nBand, + int nDstXOffset, int nDstYOffset, + int nDstXSize, int nDstYSize) +{ + CPLAssert( m_nShiftXPixelsMod || m_nShiftYPixelsMod ); + CPLAssert( nRow >= 0 ); + CPLAssert( nCol >= 0 ); + CPLAssert( nRow < m_nTileMatrixHeight ); + CPLAssert( nCol < m_nTileMatrixWidth ); + + if( m_hTempDB == NULL && + (m_poParentDS == NULL || m_poParentDS->m_hTempDB == NULL) ) + { + const char* pszBaseFilename = m_poParentDS ? + m_poParentDS->m_pszFilename : m_pszFilename; + m_osTempDBFilename = CPLResetExtension(pszBaseFilename, "gpkg.tmp"); + CPLPushErrorHandler(CPLQuietErrorHandler); + VSIUnlink(m_osTempDBFilename); + CPLPopErrorHandler(); + m_hTempDB = NULL; + sqlite3_open(m_osTempDBFilename, &m_hTempDB); + if( m_hTempDB == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot create temporary database %s", + m_osTempDBFilename.c_str()); + return CE_Failure; + } + SQLCommand(m_hTempDB, "PRAGMA synchronous = OFF"); + SQLCommand(m_hTempDB, "PRAGMA journal_mode = OFF"); + SQLCommand(m_hTempDB, "CREATE TABLE partial_tiles(" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "zoom_level INTEGER NOT NULL," + "tile_column INTEGER NOT NULL," + "tile_row INTEGER NOT NULL," + "tile_data_band_1 BLOB," + "tile_data_band_2 BLOB," + "tile_data_band_3 BLOB," + "tile_data_band_4 BLOB," + "partial_flag INTEGER NOT NULL," + "UNIQUE (zoom_level, tile_column, tile_row))" ); + SQLCommand(m_hTempDB, "CREATE INDEX partial_tiles_partial_flag_idx " + "ON partial_tiles(partial_flag)"); + + if( m_poParentDS != NULL ) + { + m_poParentDS->m_osTempDBFilename = m_osTempDBFilename; + m_poParentDS->m_hTempDB = m_hTempDB; + } + } + if( m_poParentDS != NULL ) + m_hTempDB = m_poParentDS->m_hTempDB; + + int nBlockXSize, nBlockYSize; + GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize); + + int iQuadrantFlag = 0; + if( nDstXOffset == 0 && nDstYOffset == 0 ) + iQuadrantFlag |= (1 << 0); + if( nDstXOffset + nDstXSize == nBlockXSize && nDstYOffset == 0 ) + iQuadrantFlag |= (1 << 1); + if( nDstXOffset == 0 && nDstYOffset + nDstYSize == nBlockYSize ) + iQuadrantFlag |= (1 << 2); + if( nDstXOffset + nDstXSize == nBlockXSize && nDstYOffset + nDstYSize == nBlockYSize ) + iQuadrantFlag |= (1 << 3); + int nFlags = iQuadrantFlag << (4 * (nBand - 1)); + int nFullFlags = (1 << (4 * nBands)) - 1; + int nOldFlags = 0; + + for(int i=1;i<=3;i++) + { + m_asCachedTilesDesc[i].nRow = -1; + m_asCachedTilesDesc[i].nCol = -1; + m_asCachedTilesDesc[i].nIdxWithinTileData = -1; + } + + int nExistingId = 0; + const char* pszSQL = CPLSPrintf("SELECT id, partial_flag, tile_data_band_%d FROM partial_tiles WHERE " + "zoom_level = %d AND tile_row = %d AND tile_column = %d", + nBand, m_nZoomLevel, nRow, nCol); +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "%s", pszSQL); +#endif + sqlite3_stmt* hStmt = NULL; + int rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, strlen(pszSQL), &hStmt, NULL); + if ( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "sqlite3_prepare(%s) failed: %s", + pszSQL, sqlite3_errmsg( m_hTempDB ) ); + return CE_Failure; + } + + rc = sqlite3_step(hStmt); + if ( rc == SQLITE_ROW ) + { + nExistingId = sqlite3_column_int(hStmt, 0); +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "Using partial_tile id=%d", nExistingId); +#endif + nOldFlags = sqlite3_column_int(hStmt, 1); + CPLAssert(nOldFlags != 0); + if( (nOldFlags & (((1 << 4)-1) << (4*(nBand - 1)))) == 0 ) + { + memset( m_pabyCachedTiles + (4 + nBand - 1) * nBlockXSize * nBlockYSize, + 0, + nBlockXSize * nBlockYSize ); + } + else + { + CPLAssert( sqlite3_column_bytes(hStmt, 2) == nBlockXSize * nBlockYSize ); + memcpy( m_pabyCachedTiles + (4 + nBand - 1) * nBlockXSize * nBlockYSize, + sqlite3_column_blob(hStmt, 2), + nBlockXSize * nBlockYSize ); + } + } + else + { + memset( m_pabyCachedTiles + (4 + nBand - 1) * nBlockXSize * nBlockYSize, + 0, + nBlockXSize * nBlockYSize ); + } + sqlite3_finalize(hStmt); + hStmt = NULL; + + /* Copy the updated rectangle into the full tile */ + for(int iY = nDstYOffset; iY < nDstYOffset + nDstYSize; iY ++ ) + { + memcpy( m_pabyCachedTiles + (4 + nBand - 1) * nBlockXSize * nBlockYSize + + iY * nBlockXSize + nDstXOffset, + m_pabyCachedTiles + (nBand - 1) * nBlockXSize * nBlockYSize + + iY * nBlockXSize + nDstXOffset, + nDstXSize ); + } + +#ifdef notdef + static int nCounter = 1; + GDALDataset* poLogDS = ((GDALDriver*)GDALGetDriverByName("GTiff"))->Create( + CPLSPrintf("/tmp/partial_band_%d_%d.tif", 1, nCounter++), + nBlockXSize, nBlockYSize, nBands, GDT_Byte, NULL); + poLogDS->RasterIO(GF_Write, 0, 0, nBlockXSize, nBlockYSize, + m_pabyCachedTiles + (4 + nBand - 1) * nBlockXSize * nBlockYSize, + nBlockXSize, nBlockYSize, + GDT_Byte, + 1, NULL, + 0, 0, 0, NULL); + GDALClose(poLogDS); +#endif + + if( (nOldFlags & nFlags) != 0 ) + { + CPLDebug("GPKG", + "Rewriting quadrant %d of band %d of tile (row=%d,col=%d)", + iQuadrantFlag, nBand, nRow, nCol); + } + + nFlags |= nOldFlags; + if( nFlags == nFullFlags ) + { +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "Got all quadrants for that tile"); +#endif + for( int iBand = 1; iBand <= nBands; iBand ++ ) + { + if( iBand != nBand && nExistingId ) + { + pszSQL = CPLSPrintf("SELECT tile_data_band_%d FROM partial_tiles WHERE " + "id = %d", iBand, nExistingId); +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "%s", pszSQL); +#endif + hStmt = NULL; + rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, strlen(pszSQL), &hStmt, NULL); + if ( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "sqlite3_prepare(%s) failed: %s", + pszSQL, sqlite3_errmsg( m_hTempDB ) ); + return CE_Failure; + } + + rc = sqlite3_step(hStmt); + if ( rc == SQLITE_ROW ) + { + CPLAssert( sqlite3_column_bytes(hStmt, 0) == nBlockXSize * nBlockYSize ); + memcpy( m_pabyCachedTiles + (iBand - 1) * nBlockXSize * nBlockYSize, + sqlite3_column_blob(hStmt, 0), + nBlockXSize * nBlockYSize ); + } + sqlite3_finalize(hStmt); + hStmt = NULL; + } + else + { + memcpy( m_pabyCachedTiles + (iBand - 1) * nBlockXSize * nBlockYSize, + m_pabyCachedTiles + (4 + iBand - 1) * nBlockXSize * nBlockYSize, + nBlockXSize * nBlockYSize ); + } + } + + m_asCachedTilesDesc[0].nRow = nRow; + m_asCachedTilesDesc[0].nCol = nCol; + m_asCachedTilesDesc[0].nIdxWithinTileData = 0; + m_asCachedTilesDesc[0].abBandDirty[0] = TRUE; + m_asCachedTilesDesc[0].abBandDirty[1] = TRUE; + m_asCachedTilesDesc[0].abBandDirty[2] = TRUE; + m_asCachedTilesDesc[0].abBandDirty[3] = TRUE; + + pszSQL = CPLSPrintf("UPDATE partial_tiles SET zoom_level = %d, " + "partial_flag = 0 WHERE id = %d", + -1-m_nZoomLevel, nExistingId); + SQLCommand(m_hTempDB, pszSQL); +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "%s", pszSQL); +#endif + return WriteTile(); + } + + if( nExistingId == 0 ) + { + OGRErr err; + pszSQL = CPLSPrintf("SELECT id FROM partial_tiles WHERE " + "partial_flag = 0 AND zoom_level = %d " + "AND tile_column = %d AND tile_row = %d", + -1-m_nZoomLevel, nRow, nCol); +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "%s", pszSQL); +#endif + nExistingId = SQLGetInteger(m_hTempDB, pszSQL, &err); + if( nExistingId == 0 ) + { + pszSQL = "SELECT id FROM partial_tiles WHERE partial_flag = 0 LIMIT 1"; +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "%s", pszSQL); +#endif + nExistingId = SQLGetInteger(m_hTempDB, pszSQL, &err); + } + } + + if( nExistingId == 0 ) + { + pszSQL = CPLSPrintf("INSERT INTO partial_tiles " + "(zoom_level, tile_row, tile_column, tile_data_band_%d, partial_flag) VALUES (%d, %d, %d, ?, %d)", + nBand, m_nZoomLevel, nRow, nCol, nFlags); + } + else + { + pszSQL = CPLSPrintf("UPDATE partial_tiles SET zoom_level = %d, " + "tile_row = %d, tile_column = %d, " + "tile_data_band_%d = ?, partial_flag = %d WHERE id = %d", + m_nZoomLevel, nRow, nCol, nBand, nFlags, nExistingId); + } +#ifdef DEBUG_VERBOSE + CPLDebug("GPKG", "%s", pszSQL); +#endif + + hStmt = NULL; + rc = sqlite3_prepare_v2(m_hTempDB, pszSQL, -1, &hStmt, NULL); + if ( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "failed to prepare SQL %s: %s", + pszSQL, sqlite3_errmsg(m_hTempDB) ); + return CE_Failure; + } + + sqlite3_bind_blob( hStmt, 1, + m_pabyCachedTiles + (4 + nBand - 1) * nBlockXSize * nBlockYSize, + nBlockXSize * nBlockYSize, + SQLITE_TRANSIENT ); + rc = sqlite3_step( hStmt ); + CPLErr eErr = CE_Failure; + if( rc == SQLITE_DONE ) + eErr = CE_None; + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Failure when inserting partial tile (row=%d,col=%d) at zoom_level=%d : %s", + nRow, nCol, m_nZoomLevel, sqlite3_errmsg(m_hTempDB)); + } + + sqlite3_finalize(hStmt); + + return eErr; +} + +/************************************************************************/ +/* IWriteBlock() */ +/************************************************************************/ + +CPLErr GDALGeoPackageRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, + void* pData) +{ + //CPLDebug("GPKG", "IWriteBlock(nBand=%d,nBlockXOff=%d,nBlockYOff=%d", + // nBand,nBlockXOff,nBlockYOff); + + GDALGeoPackageDataset* poGDS = (GDALGeoPackageDataset* )poDS; + if( !poGDS->bUpdate ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "IWriteBlock() not supported on dataset opened in read-only mode"); + return CE_Failure; + } + + if( !poGDS->m_bGeoTransformValid || poGDS->m_nSRID == UNKNOWN_SRID ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "IWriteBlock() not supported if georeferencing not set"); + return CE_Failure; + } + + int nRow = nBlockYOff + poGDS->m_nShiftYTiles; + int nCol = nBlockXOff + poGDS->m_nShiftXTiles; + + int nRowMin = nRow; + int nRowMax = nRowMin; + if( poGDS->m_nShiftYPixelsMod ) + nRowMax ++; + + int nColMin = nCol; + int nColMax = nColMin; + if( poGDS->m_nShiftXPixelsMod ) + nColMax ++; + + CPLErr eErr = CE_None; + + for(nRow = nRowMin; eErr == CE_None && nRow <= nRowMax; nRow ++) + { + for(nCol = nColMin; eErr == CE_None && nCol <= nColMax; nCol++ ) + { + if( nRow < 0 || nCol < 0 || nRow >= poGDS->m_nTileMatrixHeight || + nCol >= poGDS->m_nTileMatrixWidth ) + { + continue; + } + + if( poGDS->m_nShiftXPixelsMod == 0 && poGDS->m_nShiftYPixelsMod == 0 ) + { + if( !(nRow == poGDS->m_asCachedTilesDesc[0].nRow && + nCol == poGDS->m_asCachedTilesDesc[0].nCol && + poGDS->m_asCachedTilesDesc[0].nIdxWithinTileData == 0) ) + { + eErr = poGDS->WriteTile(); + + poGDS->m_asCachedTilesDesc[0].nRow = nRow; + poGDS->m_asCachedTilesDesc[0].nCol = nCol; + poGDS->m_asCachedTilesDesc[0].nIdxWithinTileData = 0; + } + } + + // Composite block data into tile, and check if all bands for this block + // are dirty, and if so write the tile + int bAllDirty = TRUE; + for(int iBand=1;iBand<=poGDS->nBands;iBand++) + { + GDALRasterBlock* poBlock = NULL; + GByte* pabySrc; + if( iBand == nBand ) + { + pabySrc = (GByte*)pData; + } + else + { + if( !(poGDS->m_nShiftXPixelsMod == 0 && poGDS->m_nShiftYPixelsMod == 0) ) + continue; + + // If the block for this band is not dirty, it might be dirty in cache + if( poGDS->m_asCachedTilesDesc[0].abBandDirty[iBand-1] ) + continue; + else + { + poBlock = + ((GDALGeoPackageRasterBand*)poGDS->GetRasterBand(iBand))-> + TryGetLockedBlockRef(nBlockXOff, nBlockYOff); + if( poBlock && poBlock->GetDirty() ) + { + pabySrc = (GByte*)poBlock->GetDataRef(), + poBlock->MarkClean(); + } + else + { + if( poBlock ) + poBlock->DropLock(); + bAllDirty = FALSE; + continue; + } + } + } + + if( poGDS->m_nShiftXPixelsMod == 0 && poGDS->m_nShiftYPixelsMod == 0 ) + poGDS->m_asCachedTilesDesc[0].abBandDirty[iBand - 1] = TRUE; + + int nDstXOffset = 0, nDstXSize = nBlockXSize, + nDstYOffset = 0, nDstYSize = nBlockYSize; + int nSrcXOffset = 0, nSrcYOffset = 0; + // Composite block data into tile data + if( poGDS->m_nShiftXPixelsMod == 0 && poGDS->m_nShiftYPixelsMod == 0 ) + { + memcpy( poGDS->m_pabyCachedTiles + (iBand - 1) * nBlockXSize * nBlockYSize, + pabySrc, nBlockXSize * nBlockYSize ); + } + else + { + if( nCol == nColMin ) + { + nDstXOffset = poGDS->m_nShiftXPixelsMod; + nDstXSize = nBlockXSize - poGDS->m_nShiftXPixelsMod; + nSrcXOffset = 0; + } + else + { + nDstXOffset = 0; + nDstXSize = poGDS->m_nShiftXPixelsMod; + nSrcXOffset = nBlockXSize - poGDS->m_nShiftXPixelsMod; + } + if( nRow == nRowMin ) + { + nDstYOffset = poGDS->m_nShiftYPixelsMod; + nDstYSize = nBlockYSize - poGDS->m_nShiftYPixelsMod; + nSrcYOffset = 0; + } + else + { + nDstYOffset = 0; + nDstYSize = poGDS->m_nShiftYPixelsMod; + nSrcYOffset = nBlockYSize - poGDS->m_nShiftYPixelsMod; + } + //CPLDebug("GPKG", "Copy source tile x=%d,w=%d,y=%d,h=%d into buffet at x=%d,y=%d", + // nDstXOffset, nDstXSize, nDstYOffset, nDstYSize, nSrcXOffset, nSrcYOffset); + for( int y=0; ym_pabyCachedTiles + (iBand - 1) * nBlockXSize * nBlockYSize + + (y + nDstYOffset) * nBlockXSize + nDstXOffset; + GByte* pSrc = pabySrc + (y + nSrcYOffset) * nBlockXSize + nSrcXOffset; + GDALCopyWords(pSrc, GDT_Byte, 1, + pDst, GDT_Byte, 1, + nDstXSize); + } + } + + if( poBlock ) + poBlock->DropLock(); + + if( !(poGDS->m_nShiftXPixelsMod == 0 && poGDS->m_nShiftYPixelsMod == 0) ) + { + poGDS->m_asCachedTilesDesc[0].nRow = -1; + poGDS->m_asCachedTilesDesc[0].nCol = -1; + poGDS->m_asCachedTilesDesc[0].nIdxWithinTileData = -1; + eErr = poGDS->WriteShiftedTile(nRow, nCol, iBand, + nDstXOffset, nDstYOffset, + nDstXSize, nDstYSize); + } + } + + if( poGDS->m_nShiftXPixelsMod == 0 && poGDS->m_nShiftYPixelsMod == 0 ) + { + if( bAllDirty ) + { + eErr = poGDS->WriteTile(); + } + } + } + } + + return eErr; +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +int GDALGeoPackageRasterBand::GetOverviewCount() +{ + GDALGeoPackageDataset* poGDS = (GDALGeoPackageDataset* )poDS; + return poGDS->m_nOverviewCount; +} + +/************************************************************************/ +/* GetOverviewCount() */ +/************************************************************************/ + +GDALRasterBand* GDALGeoPackageRasterBand::GetOverview(int nIdx) +{ + GDALGeoPackageDataset* poGDS = (GDALGeoPackageDataset* )poDS; + if( nIdx < 0 || nIdx >= poGDS->m_nOverviewCount ) + return NULL; + return poGDS->m_papoOverviewDS[nIdx]->GetRasterBand(nBand); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/geopackage_aspatial.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/geopackage_aspatial.html new file mode 100644 index 000000000..97364c21d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/geopackage_aspatial.html @@ -0,0 +1,182 @@ + + + + + GeoPackage 1.0 Extension - Aspatial Support + + + + +
    +
    +
    +
    +

    +GeoPackage 1.0 Extension

    + +

    Extension follows template from Annex I of the OGC GeoPackage 1.0 Specification.

    + +

    +Extension Title

    + +

    Aspatial Support

    + +

    +Introduction

    + +

    Support for aspatial data (ie. SQLite tables/views without a geometry column), potentially with associated metadata.

    + +

    +Extension Author

    + +

    GDAL - Geospatial Data Abstraction Library, author_name gdal.

    + +

    +Extension Name or Template

    + +
    INSERT INTO gpkg_extensions
    +  (table_name, column_name, extension_name, definition, scope)
    +VALUES
    +  (
    +    NULL,
    +    NULL,
    +    'gdal_aspatial',
    +    'http://gdal.org/geopackage_aspatial.html',
    +    'read-write'
    +  );
    +
    + +

    +Extension Type

    + +

    Extension of Existing Requirement in Clause 2.

    + +

    +Applicability

    + +

    This extension applies to any aspatial user data table or view specified in the gpkg_contents table with a lowercase data_type column value of "aspatial".

    + +

    +Scope

    + +

    Read-write

    + +

    +Requirements

    + +

    +GeoPackage

    + +

    +Contents Table - Aspatial

    + +

    The gpkg_contents table SHALL contain a row with a lowercase data_type column value of "aspatial" for each aspatial user data table or view.

    + +

    +User Data Tables

    + +

    The second component of the SQL schema for aspatial tables in an Extended GeoPackage described in clause 'Contents Table - Aspatial' above are user tables or views that contain aspatial user data.

    + +

    An Extended GeoPackage with aspatial support is not required to contain any user data tables. User data tables MAY be empty.

    + +

    An Extended GeoPackage with aspatial support MAY contain tables or views. Every such aspatial table or view MAY have a column with column type INTEGER and PRIMARY KEY AUTOINCREMENT column constraints per EXAMPLE.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Column NameTypeDescriptionNullDefaultKey
    idINTEGERAutoincrement primary keynoPK
    text_attributeTEXTText attribute of rowyes
    real_attributeREALReal attribute of rowyes
    boolean_attributeBOOLEANBoolean attribute of rowyes
    raster_or_photoBLOBPhotographyes

    An integer primary key of an aspatial table or view allows features to be linked to row level metadata records in the gpkg_metadata table by rowid values in the gpkg_metadata_reference table as described in clause 2.4.3 Metadata Reference Table.

    + +

    An aspatial table or view SHALL NOT have a geometry column.

    + +

    Columns in aspatial tables or views SHALL be defined using only the data types specified in Table 1 in Clause 1.1.1.1.3.

    + +

    +GeoPackage SQLite Configuration

    + +

    None

    + +

    +GeoPackage SQLite Extension

    + +

    None

    +
    +
    +
    + +
     
    +
    + + \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/geopackage_aspatial.md b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/geopackage_aspatial.md new file mode 100644 index 000000000..e23c98801 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/geopackage_aspatial.md @@ -0,0 +1,80 @@ +# GeoPackage 1.0 Extension + +Extension follows template from Annex I of the OGC [GeoPackage 1.0 Specification](http://www.geopackage.org/). + +## Extension Title + +Aspatial Support + +## Introduction + +Support for aspatial data (ie. SQLite tables/views without a geometry column), potentially with associated metadata. + +## Extension Author + +[GDAL - Geospatial Data Abstraction Library](http://gdal.org), author_name `gdal`. + +## Extension Name or Template + +```SQL +INSERT INTO gpkg_extensions + (table_name, column_name, extension_name, definition, scope) +VALUES + ( + NULL, + NULL, + 'gdal_aspatial', + 'http://gdal.org/geopackage_aspatial.html', + 'read-write' + ); +``` + +## Extension Type + +Extension of Existing Requirement in Clause 2. + +## Applicability + +This extension applies to any aspatial user data table or view specified in the `gpkg_contents` table with a lowercase `data_type` column value of "aspatial". + +## Scope + +Read-write + +## Requirements + +### GeoPackage + +#### Contents Table - Aspatial + +The `gpkg_contents` table SHALL contain a row with a lowercase `data_type` column value of "aspatial" for each aspatial user data table or view. + +#### User Data Tables + +The second component of the SQL schema for aspatial tables in an Extended GeoPackage described in clause 'Contents Table - Aspatial' above are user tables or views that contain aspatial user data. + +An Extended GeoPackage with aspatial support is not required to contain any user data tables. User data tables MAY be empty. + +An Extended GeoPackage with aspatial support MAY contain tables or views. Every such aspatial table or view MAY have a column with column type INTEGER and PRIMARY KEY AUTOINCREMENT column constraints per EXAMPLE. + +| Column Name | Type | Description | Null | Default | Key | +| ------------------- | ------- | ------------------------- | ---- | ------- | --- | +| `id` | INTEGER | Autoincrement primary key | no | | PK | +| `text_attribute` | TEXT | Text attribute of row | yes | | | +| `real_attribute` | REAL | Real attribute of row | yes | | | +| `boolean_attribute` | BOOLEAN | Boolean attribute of row | yes | | | +| `raster_or_photo` | BLOB | Photograph | yes | | | + +An integer primary key of an aspatial table or view allows features to be linked to row level metadata records in the `gpkg_metadata` table by [rowid](http://www.sqlite.org/lang_createtable.html#rowid) values in the `gpkg_metadata_reference` table as described in clause 2.4.3 Metadata Reference Table. + +An aspatial table or view SHALL NOT have a geometry column. + +Columns in aspatial tables or views SHALL be defined using only the data types specified in Table 1 in Clause 1.1.1.1.3. + +### GeoPackage SQLite Configuration + +None + +### GeoPackage SQLite Extension + +None \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/makefile.vc new file mode 100644 index 000000000..0a6dfb655 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/makefile.vc @@ -0,0 +1,26 @@ + +OBJ = ogrgeopackagedriver.obj ogrgeopackagedatasource.obj \ + ogrgeopackagelayer.obj ogrgeopackagetablelayer.obj ogrgeopackageselectlayer.obj ogrgeopackageutility.obj \ + gdalgeopackagerasterband.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. -I..\sqlite -I..\..\..\frmts\mem $(SQLITE_INC) $(SQLITE_HAS_COLUMN_METADATA_EXTRAFLAGS) $(SPATIALITE_412_OR_LATER_EXTRAFLAGS) + +!IFDEF SQLITE_HAS_COLUMN_METADATA +SQLITE_HAS_COLUMN_METADATA_EXTRAFLAGS = -DSQLITE_HAS_COLUMN_METADATA +!ENDIF + +!IFDEF SPATIALITE_412_OR_LATER +SPATIALITE_412_OR_LATER_EXTRAFLAGS = -DSPATIALITE_412_OR_LATER +!ENDIF + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + -del *.exe + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/ogr_geopackage.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/ogr_geopackage.h new file mode 100644 index 000000000..e86709b81 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/ogr_geopackage.h @@ -0,0 +1,522 @@ +/****************************************************************************** + * $Id$ + * + * Project: GeoPackage Translator + * Purpose: Definition of classes for OGR GeoPackage driver. + * Author: Paul Ramsey, pramsey@boundlessgeo.com + * + ****************************************************************************** + * Copyright (c) 2013, Paul Ramsey + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GEOPACKAGE_H_INCLUDED +#define _OGR_GEOPACKAGE_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "ogr_sqlite.h" +#include "ogrgeopackageutility.h" + +#define UNKNOWN_SRID -2 +#define DEFAULT_SRID 0 + +/************************************************************************/ +/* GDALGeoPackageDataset */ +/************************************************************************/ + +class OGRGeoPackageTableLayer; + +typedef struct +{ + int nRow; + int nCol; + int nIdxWithinTileData; + int abBandDirty[4]; +} CachedTileDesc; + +typedef enum +{ + GPKG_TF_PNG_JPEG, + GPKG_TF_PNG, + GPKG_TF_PNG8, + GPKG_TF_JPEG, + GPKG_TF_WEBP +} GPKGTileFormat; + +class GDALGeoPackageDataset : public OGRSQLiteBaseDataSource +{ + friend class GDALGeoPackageRasterBand; + friend class OGRGeoPackageTableLayer; + + OGRGeoPackageTableLayer** m_papoLayers; + int m_nLayers; + int m_bUtf8; + void CheckUnknownExtensions(int bCheckRasterTable = FALSE); + + int m_bNew; + + CPLString m_osRasterTable; + CPLString m_osIdentifier; + int m_bIdentifierAsCO; + CPLString m_osDescription; + int m_bDescriptionAsCO; + int m_bHasReadMetadataFromStorage; + int m_bMetadataDirty; + char **m_papszSubDatasets; + char *m_pszProjection; + int m_bRecordInsertedInGPKGContent; + int m_bGeoTransformValid; + double m_adfGeoTransform[6]; + int m_nSRID; + double m_dfTMSMinX; + double m_dfTMSMaxY; + int m_nZoomLevel; + GByte *m_pabyCachedTiles; + CachedTileDesc m_asCachedTilesDesc[4]; + int m_nShiftXTiles; + int m_nShiftXPixelsMod; + int m_nShiftYTiles; + int m_nShiftYPixelsMod; + int m_nTileMatrixWidth; + int m_nTileMatrixHeight; + + GPKGTileFormat m_eTF; + int m_nZLevel; + int m_nQuality; + int m_bDither; + + GDALColorTable* m_poCT; + int m_bTriedEstablishingCT; + GByte* m_pabyHugeColorArray; + + GDALGeoPackageDataset* m_poParentDS; + int m_nOverviewCount; + GDALGeoPackageDataset** m_papoOverviewDS; + int m_bZoomOther; + + CPLString m_osWHERE; + + sqlite3 *m_hTempDB; + CPLString m_osTempDBFilename; + + int m_bInFlushCache; + + int m_nTileInsertionCount; + + CPLString m_osTilingScheme; + + void ComputeTileAndPixelShifts(); + int InitRaster ( GDALGeoPackageDataset* poParentDS, + const char* pszTableName, + double dfMinX, + double dfMinY, + double dfMaxX, + double dfMaxY, + const char* pszContentsMinX, + const char* pszContentsMinY, + const char* pszContentsMaxX, + const char* pszContentsMaxY, + char** papszOpenOptions, + const SQLResult& oResult, + int nIdxInResult ); + int InitRaster ( GDALGeoPackageDataset* poParentDS, + const char* pszTableName, + int nZoomLevel, + int nBandCount, + double dfTMSMinX, + double dfTMSMaxY, + double dfPixelXSize, + double dfPixelYSize, + int nTileWidth, + int nTileHeight, + int nTileMatrixWidth, + int nTileMatrixHeight, + double dfGDALMinX, + double dfGDALMinY, + double dfGDALMaxX, + double dfGDALMaxY ); + + int OpenRaster( const char* pszTableName, + const char* pszIdentifier, + const char* pszDescription, + int nSRSId, + double dfMinX, + double dfMinY, + double dfMaxX, + double dfMaxY, + const char* pszContentsMinX, + const char* pszContentsMinY, + const char* pszContentsMaxX, + const char* pszContentsMaxY, + char** papszOptions ); + CPLErr FinalizeRasterRegistration(); + + CPLErr ReadTile(const CPLString& osMemFileName, + GByte* pabyTileData, + int* pbIsLossyFormat = NULL); + GByte* ReadTile(int nRow, int nCol); + GByte* ReadTile(int nRow, int nCol, GByte* pabyData, + int* pbIsLossyFormat = NULL); + + int m_bInWriteTile; + CPLErr WriteTile(); + + CPLErr WriteTileInternal(); /* should only be called by WriteTile() */ + CPLErr FlushRemainingShiftedTiles(); + CPLErr WriteShiftedTile(int nRow, int nCol, int iBand, + int nDstXOffset, int nDstYOffset, + int nDstXSize, int nDstYSize); + + int RegisterWebPExtension(); + int RegisterZoomOtherExtension(); + void ParseCompressionOptions(char** papszOptions); + + int HasMetadataTables(); + int CreateMetadataTables(); + const char* CheckMetadataDomain( const char* pszDomain ); + void WriteMetadata(CPLXMLNode* psXMLNode, /* will be destroyed by the method /*/ + const char* pszTableName); + CPLErr FlushMetadata(); + + public: + GDALGeoPackageDataset(); + ~GDALGeoPackageDataset(); + + virtual char ** GetMetadata( const char *pszDomain = NULL ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual char ** GetMetadataDomainList(); + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + virtual const char* GetProjectionRef(); + virtual CPLErr SetProjection( const char* pszProjection ); + + virtual CPLErr GetGeoTransform( double* padfGeoTransform ); + virtual CPLErr SetGeoTransform( double* padfGeoTransform ); + + virtual void FlushCache(); + CPLErr FlushCacheWithErrCode(); + virtual CPLErr IBuildOverviews( const char *, int, int *, + int, int *, GDALProgressFunc, void * ); + + virtual int GetLayerCount() { return m_nLayers; } + int Open( GDALOpenInfo* poOpenInfo ); + int Create( const char * pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eDT, + char **papszOptions ); + OGRLayer* GetLayer( int iLayer ); + int DeleteLayer( int iLayer ); + OGRLayer* ICreateLayer( const char * pszLayerName, + OGRSpatialReference * poSpatialRef, + OGRwkbGeometryType eGType, + char **papszOptions ); + int TestCapability( const char * ); + + virtual std::pair GetLayerWithGetSpatialWhereByName( const char* pszName ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + int GetSrsId( const OGRSpatialReference * poSRS ); + const char* GetSrsName( const OGRSpatialReference * poSRS ); + OGRSpatialReference* GetSpatialRef( int iSrsId ); + virtual int GetUTF8() { return m_bUtf8; } + OGRErr CreateExtensionsTableIfNecessary(); + int HasExtensionsTable(); + OGRErr CreateGDALAspatialExtension(); + void SetMetadataDirty() { m_bMetadataDirty = TRUE; } + + const char* GetGeometryTypeString(OGRwkbGeometryType eType); + + static GDALDataset* CreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, + char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ); + private: + + OGRErr PragmaCheck(const char * pszPragma, const char * pszExpected, int nRowsExpected); + OGRErr SetApplicationId(); + int OpenOrCreateDB(int flags); + int HasGDALAspatialExtension(); +}; + +/************************************************************************/ +/* GDALGeoPackageRasterBand */ +/************************************************************************/ + +class GDALGeoPackageRasterBand: public GDALPamRasterBand +{ + public: + + GDALGeoPackageRasterBand(GDALGeoPackageDataset* poDS, + int nBand, + int nTileWidth, int nTileHeight); + + virtual CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, + void* pData); + virtual CPLErr IWriteBlock(int nBlockXOff, int nBlockYOff, + void* pData); + virtual CPLErr FlushCache(); + + virtual GDALColorTable* GetColorTable(); + virtual CPLErr SetColorTable(GDALColorTable* poCT); + + virtual GDALColorInterp GetColorInterpretation(); + virtual CPLErr SetColorInterpretation( GDALColorInterp ); + + virtual int GetOverviewCount(); + virtual GDALRasterBand* GetOverview(int nIdx); +}; + +/************************************************************************/ +/* OGRGeoPackageLayer */ +/************************************************************************/ + +class OGRGeoPackageLayer : public OGRLayer, public IOGRSQLiteGetSpatialWhere +{ + protected: + GDALGeoPackageDataset *m_poDS; + + OGRFeatureDefn* m_poFeatureDefn; + int iNextShapeId; + + sqlite3_stmt *m_poQueryStatement; + int bDoStep; + + char *m_pszFidColumn; + + int iFIDCol; + int iGeomCol; + int *panFieldOrdinals; + + void ClearStatement(); + virtual OGRErr ResetStatement() = 0; + + void BuildFeatureDefn( const char *pszLayerName, + sqlite3_stmt *hStmt ); + + OGRFeature* TranslateFeature(sqlite3_stmt* hStmt); + + public: + + OGRGeoPackageLayer(GDALGeoPackageDataset* poDS); + ~OGRGeoPackageLayer(); + /************************************************************************/ + /* OGR API methods */ + + OGRFeature* GetNextFeature(); + const char* GetFIDColumn(); + void ResetReading(); + int TestCapability( const char * ); + OGRFeatureDefn* GetLayerDefn() { return m_poFeatureDefn; } + + virtual int HasFastSpatialFilter(CPL_UNUSED int iGeomCol) { return FALSE; } + virtual CPLString GetSpatialWhere(CPL_UNUSED int iGeomCol, + CPL_UNUSED OGRGeometry* poFilterGeom) { return ""; } + +}; + +/************************************************************************/ +/* OGRGeoPackageTableLayer */ +/************************************************************************/ + +class OGRGeoPackageTableLayer : public OGRGeoPackageLayer +{ + char* m_pszTableName; + int m_iSrs; + OGREnvelope* m_poExtent; + CPLString m_soColumns; + CPLString m_soFilter; + CPLString osQuery; + OGRBoolean m_bExtentChanged; + sqlite3_stmt* m_poUpdateStatement; + int m_bInsertStatementWithFID; + sqlite3_stmt* m_poInsertStatement; + int bDeferedSpatialIndexCreation; + int m_bHasSpatialIndex; + int bDropRTreeTable; + int m_anHasGeometryExtension[wkbMultiSurface+1]; + int m_bPreservePrecision; + int m_bTruncateFields; + int m_bDeferredCreation; + int m_iFIDAsRegularColumnIndex; + + CPLString m_osIdentifierLCO; + CPLString m_osDescriptionLCO; + int m_bHasReadMetadataFromStorage; + + virtual OGRErr ResetStatement(); + + void BuildWhere(void); + OGRErr RegisterGeometryColumn(); + + public: + + OGRGeoPackageTableLayer( GDALGeoPackageDataset *poDS, + const char * pszTableName ); + ~OGRGeoPackageTableLayer(); + + /************************************************************************/ + /* OGR API methods */ + + int TestCapability( const char * ); + OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK = TRUE ); + OGRErr CreateGeomField( OGRGeomFieldDefn *poGeomFieldIn, + int bApproxOK = TRUE ); + void ResetReading(); + OGRErr ICreateFeature( OGRFeature *poFeater ); + OGRErr ISetFeature( OGRFeature *poFeature ); + OGRErr DeleteFeature(GIntBig nFID); + virtual void SetSpatialFilter( OGRGeometry * ); + OGRErr SetAttributeFilter( const char *pszQuery ); + OGRErr SyncToDisk(); + OGRFeature* GetNextFeature(); + OGRFeature* GetFeature(GIntBig nFID); + OGRErr StartTransaction(); + OGRErr CommitTransaction(); + OGRErr RollbackTransaction(); + GIntBig GetFeatureCount( int ); + OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + // void SetSpatialFilter( int iGeomField, OGRGeometry * poGeomIn ); + + OGRErr ReadTableDefinition(int bIsSpatial); + void SetCreationParameters( OGRwkbGeometryType eGType, + const char* pszGeomColumnName, + int bGeomNullable, + OGRSpatialReference* poSRS, + const char* pszFIDColumnName, + const char* pszIdentifier, + const char* pszDescription ); + void SetDeferedSpatialIndexCreation( int bFlag ) + { bDeferedSpatialIndexCreation = bFlag; } + + void CreateSpatialIndexIfNecessary(); + int CreateSpatialIndex(); + int DropSpatialIndex(int bCalledFromSQLFunction = FALSE); + + virtual char ** GetMetadata( const char *pszDomain = NULL ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + virtual char ** GetMetadataDomainList(); + + virtual CPLErr SetMetadata( char ** papszMetadata, + const char * pszDomain = "" ); + virtual CPLErr SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain = "" ); + + void RenameTo(const char* pszDstTableName); + + virtual int HasFastSpatialFilter(int iGeomCol); + virtual CPLString GetSpatialWhere(int iGeomCol, + OGRGeometry* poFilterGeom); + + int HasSpatialIndex(); + void SetPrecisionFlag( int bFlag ) + { m_bPreservePrecision = bFlag; } + void SetTruncateFieldsFlag( int bFlag ) + { m_bTruncateFields = bFlag; } + OGRErr RunDeferredCreationIfNecessary(); + + /************************************************************************/ + /* GPKG methods */ + + private: + + OGRErr UpdateExtent( const OGREnvelope *poExtent ); + OGRErr SaveExtent(); + OGRErr BuildColumns(); + OGRBoolean IsGeomFieldSet( OGRFeature *poFeature ); + CPLString FeatureGenerateUpdateSQL( OGRFeature *poFeature ); + CPLString FeatureGenerateInsertSQL( OGRFeature *poFeature, int bAddFID, int bBindNullFields ); + OGRErr FeatureBindUpdateParameters( OGRFeature *poFeature, sqlite3_stmt *poStmt ); + OGRErr FeatureBindInsertParameters( OGRFeature *poFeature, sqlite3_stmt *poStmt, int bAddFID, int bBindNullFields ); + OGRErr FeatureBindParameters( OGRFeature *poFeature, sqlite3_stmt *poStmt, int *pnColCount, int bAddFID, int bBindNullFields ); + + void CheckUnknownExtensions(); + int CreateGeometryExtensionIfNecessary(OGRwkbGeometryType eGType); +}; + +/************************************************************************/ +/* OGRGeoPackageSelectLayer */ +/************************************************************************/ + +class OGRGeoPackageSelectLayer : public OGRGeoPackageLayer, public IOGRSQLiteSelectLayer +{ + OGRSQLiteSelectLayerCommonBehaviour* poBehaviour; + + virtual OGRErr ResetStatement(); + + public: + OGRGeoPackageSelectLayer( GDALGeoPackageDataset *, + CPLString osSQL, + sqlite3_stmt *, + int bUseStatementForGetNextFeature, + int bEmptyLayer ); + ~OGRGeoPackageSelectLayer(); + + virtual void ResetReading(); + + virtual OGRFeature *GetNextFeature(); + virtual GIntBig GetFeatureCount( int ); + + virtual void SetSpatialFilter( OGRGeometry * poGeom ) { SetSpatialFilter(0, poGeom); } + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + virtual OGRErr SetAttributeFilter( const char * ); + + virtual int TestCapability( const char * ); + + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE) { return GetExtent(0, psExtent, bForce); } + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); + + virtual OGRFeatureDefn * GetLayerDefn() { return OGRGeoPackageLayer::GetLayerDefn(); } + virtual char*& GetAttrQueryString() { return m_pszAttrQueryString; } + virtual OGRFeatureQuery*& GetFeatureQuery() { return m_poAttrQuery; } + virtual OGRGeometry*& GetFilterGeom() { return m_poFilterGeom; } + virtual int& GetIGeomFieldFilter() { return m_iGeomFieldFilter; } + virtual OGRSpatialReference* GetSpatialRef() { return OGRGeoPackageLayer::GetSpatialRef(); } + virtual int InstallFilter( OGRGeometry * poGeomIn ) { return OGRGeoPackageLayer::InstallFilter(poGeomIn); } + virtual int HasReadFeature() { return iNextShapeId > 0; } + virtual void BaseResetReading() { OGRGeoPackageLayer::ResetReading(); } + virtual OGRFeature *BaseGetNextFeature() { return OGRGeoPackageLayer::GetNextFeature(); } + virtual OGRErr BaseSetAttributeFilter(const char* pszQuery) { return OGRGeoPackageLayer::SetAttributeFilter(pszQuery); } + virtual GIntBig BaseGetFeatureCount(int bForce) { return OGRGeoPackageLayer::GetFeatureCount(bForce); } + virtual int BaseTestCapability( const char *pszCap ) { return OGRGeoPackageLayer::TestCapability(pszCap); } + virtual OGRErr BaseGetExtent(OGREnvelope *psExtent, int bForce) { return OGRGeoPackageLayer::GetExtent(psExtent, bForce); } + virtual OGRErr BaseGetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) { return OGRGeoPackageLayer::GetExtent(iGeomField, psExtent, bForce); } +}; + + +#endif /* _OGR_GEOPACKAGE_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/ogrgeopackagedatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/ogrgeopackagedatasource.cpp new file mode 100644 index 000000000..838da5167 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/ogrgeopackagedatasource.cpp @@ -0,0 +1,4379 @@ +/****************************************************************************** + * $Id$ + * + * Project: GeoPackage Translator + * Purpose: Implements GDALGeoPackageDataset class + * Author: Paul Ramsey + * + ****************************************************************************** + * Copyright (c) 2013, Paul Ramsey + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geopackage.h" +#include "ogr_p.h" +#include "swq.h" +#include "gdalwarper.h" + +/* 1.1.1: A GeoPackage SHALL contain 0x47503130 ("GP10" in ASCII) in the application id */ +/* http://opengis.github.io/geopackage/#_file_format */ +/* 0x47503130 = 1196437808 */ +#define GPKG_APPLICATION_ID 1196437808 + +/* "GP10" in ASCII bytes */ +static const char aGpkgId[4] = {0x47, 0x50, 0x31, 0x30}; +static const size_t szGpkgIdPos = 68; + +/************************************************************************/ +/* Tiling schemes */ +/************************************************************************/ + +typedef struct +{ + const char* pszName; + int nEPSGCode; + double dfMinX; + double dfMaxY; + int nTileXCountZoomLevel0; + int nTileYCountZoomLevel0; + int nTileWidth; + int nTileHeight; + double dfPixelXSizeZoomLevel0; + double dfPixelYSizeZoomLevel0; +} TilingSchemeDefinition; + +static const TilingSchemeDefinition asTilingShemes[] = +{ + /* See http://portal.opengeospatial.org/files/?artifact_id=35326 (WMTS 1.0), Annex E.3 */ + { "GoogleCRS84Quad", + 4326, + -180.0, 180.0, + 1, 1, + 256, 256, + 360.0 / 256, 360.0 / 256 }, + + /* See http://portal.opengeospatial.org/files/?artifact_id=35326 (WMTS 1.0), Annex E.4 */ + { "GoogleMapsCompatible", + 3857, + -(156543.0339280410*256) /2, (156543.0339280410*256) /2, + 1, 1, + 256, 256, + 156543.0339280410, 156543.0339280410 }, + + /* See InspireCRS84Quad at http://inspire.ec.europa.eu/documents/Network_Services/TechnicalGuidance_ViewServices_v3.0.pdf */ + /* This is exactly the same as PseudoTMS_GlobalGeodetic */ + { "InspireCRS84Quad", + 4326, + -180.0, 90.0, + 2, 1, + 256, 256, + 180.0 / 256, 180.0 / 256 }, + + /* See global-geodetic at http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification */ + { "PseudoTMS_GlobalGeodetic", + 4326, + -180.0, 90.0, + 2, 1, + 256, 256, + 180.0 / 256, 180.0 / 256 }, + + /* See global-mercator at http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification */ + { "PseudoTMS_GlobalMercator", + 3857, + -20037508.34, 20037508.34, + 2, 2, + 256, 256, + 78271.516, 78271.516 }, +}; + +/* Only recent versions of SQLite will let us muck with application_id */ +/* via a PRAGMA statement, so we have to write directly into the */ +/* file header here. */ +/* We do this at the *end* of initialization so that there is */ +/* data to write down to a file, and we'll have a writeable file */ +/* once we close the SQLite connection */ +OGRErr GDALGeoPackageDataset::SetApplicationId() +{ + CPLAssert( hDB != NULL ); + CPLAssert( m_pszFilename != NULL ); + +#ifdef SPATIALITE_412_OR_LATER + FinishNewSpatialite(); +#endif + /* Have to flush the file before f***ing with the header */ + CloseDB(); + + size_t szWritten = 0; + + /* Open for modification, write to application id area */ + VSILFILE *pfFile = VSIFOpenL( m_pszFilename, "rb+" ); + if( pfFile == NULL ) + return OGRERR_FAILURE; + VSIFSeekL(pfFile, szGpkgIdPos, SEEK_SET); + szWritten = VSIFWriteL(aGpkgId, 1, 4, pfFile); + VSIFCloseL(pfFile); + + /* If we didn't write out exactly four bytes, something */ + /* terrible has happened */ + if ( szWritten != 4 ) + { + return OGRERR_FAILURE; + } + + /* And re-open the file */ + if (!OpenOrCreateDB(SQLITE_OPEN_READWRITE) ) + return OGRERR_FAILURE; + + return OGRERR_NONE; +} + + +/* Returns the first row of first column of SQL as integer */ +OGRErr GDALGeoPackageDataset::PragmaCheck(const char * pszPragma, const char * pszExpected, int nRowsExpected) +{ + CPLAssert( pszPragma != NULL ); + CPLAssert( pszExpected != NULL ); + CPLAssert( nRowsExpected >= 0 ); + + char *pszErrMsg = NULL; + int nRowCount, nColCount, rc; + char **papszResult; + + rc = sqlite3_get_table( + hDB, + CPLSPrintf("PRAGMA %s", pszPragma), + &papszResult, &nRowCount, &nColCount, &pszErrMsg ); + + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "unable to execute PRAGMA %s", pszPragma); + return OGRERR_FAILURE; + } + + if ( nRowCount != nRowsExpected ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "bad result for PRAGMA %s, got %d rows, expected %d", pszPragma, nRowCount, nRowsExpected); + return OGRERR_FAILURE; + } + + if ( nRowCount > 0 && ! EQUAL(papszResult[1], pszExpected) ) + { + CPLError( CE_Failure, CPLE_AppDefined, "invalid %s (expected '%s', got '%s')", + pszPragma, pszExpected, papszResult[1]); + return OGRERR_FAILURE; + } + + sqlite3_free_table(papszResult); + + return OGRERR_NONE; +} + +static OGRErr GDALGPKGImportFromEPSG(OGRSpatialReference *poSpatialRef, + int nEPSGCode) +{ + CPLPushErrorHandler(CPLQuietErrorHandler); + OGRErr eErr = poSpatialRef->importFromEPSG(nEPSGCode); + CPLPopErrorHandler(); + CPLErrorReset(); + return eErr; +} + + +OGRSpatialReference* GDALGeoPackageDataset::GetSpatialRef(int iSrsId) +{ + SQLResult oResult; + + /* Should we do something special with undefined SRS ? */ + if( iSrsId == 0 || iSrsId == -1 ) + { + return NULL; + } + + CPLString oSQL; + oSQL.Printf("SELECT definition, organization, organization_coordsys_id FROM gpkg_spatial_ref_sys WHERE srs_id = %d", iSrsId); + + OGRErr err = SQLQuery(hDB, oSQL.c_str(), &oResult); + + if ( err != OGRERR_NONE || oResult.nRowCount != 1 ) + { + SQLResultFree(&oResult); + CPLError( CE_Warning, CPLE_AppDefined, "unable to read srs_id '%d' from gpkg_spatial_ref_sys", + iSrsId); + return NULL; + } + + const char *pszWkt = SQLResultGetValue(&oResult, 0, 0); + if ( ! pszWkt ) + { + SQLResultFree(&oResult); + CPLError( CE_Warning, CPLE_AppDefined, "null definition for srs_id '%d' in gpkg_spatial_ref_sys", + iSrsId); + return NULL; + } + + const char* pszOrganization = SQLResultGetValue(&oResult, 1, 0); + const char* pszOrganizationCoordsysID = SQLResultGetValue(&oResult, 2, 0); + + OGRSpatialReference *poSpatialRef = new OGRSpatialReference(); + // Try to import first from EPSG code, and then from WKT + if( !(pszOrganization && pszOrganizationCoordsysID && EQUAL(pszOrganization, "EPSG") && + GDALGPKGImportFromEPSG(poSpatialRef, atoi(pszOrganizationCoordsysID)) == OGRERR_NONE) && + poSpatialRef->SetFromUserInput(pszWkt) != OGRERR_NONE ) + { + CPLError( CE_Warning, CPLE_AppDefined, "unable to parse srs_id '%d' well-known text '%s'", + iSrsId, pszWkt); + SQLResultFree(&oResult); + delete poSpatialRef; + return NULL; + } + + SQLResultFree(&oResult); + return poSpatialRef; +} + +const char * GDALGeoPackageDataset::GetSrsName(const OGRSpatialReference * poSRS) +{ + const OGR_SRSNode *node; + + /* Projected coordinate system? */ + if ( (node = poSRS->GetAttrNode("PROJCS")) ) + { + return node->GetChild(0)->GetValue(); + } + /* Geographic coordinate system? */ + else if ( (node = poSRS->GetAttrNode("GEOGCS")) ) + { + return node->GetChild(0)->GetValue(); + } + /* Something odd! return empty. */ + else + { + return "Unnamed SRS"; + } +} + +int GDALGeoPackageDataset::GetSrsId(const OGRSpatialReference * cpoSRS) +{ + char *pszWKT = NULL; + char *pszSQL = NULL; + int nSRSId = DEFAULT_SRID; + const char* pszAuthorityName; + int nAuthorityCode = 0; + OGRErr err; + OGRBoolean bCanUseAuthorityCode = FALSE; + + if( cpoSRS == NULL ) + return DEFAULT_SRID; + + OGRSpatialReference *poSRS = cpoSRS->Clone(); + + pszAuthorityName = poSRS->GetAuthorityName(NULL); + + if ( pszAuthorityName == NULL || strlen(pszAuthorityName) == 0 ) + { + // Try to force identify an EPSG code + poSRS->AutoIdentifyEPSG(); + + pszAuthorityName = poSRS->GetAuthorityName(NULL); + if (pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG")) + { + const char* pszAuthorityCode = poSRS->GetAuthorityCode(NULL); + if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 ) + { + /* Import 'clean' SRS */ + poSRS->importFromEPSG( atoi(pszAuthorityCode) ); + + pszAuthorityName = poSRS->GetAuthorityName(NULL); + } + } + } + // Check whether the EPSG authority code is already mapped to a + // SRS ID. + if ( pszAuthorityName != NULL && strlen(pszAuthorityName) > 0 ) + { + // For the root authority name 'EPSG', the authority code + // should always be integral + nAuthorityCode = atoi( poSRS->GetAuthorityCode(NULL) ); + + pszSQL = sqlite3_mprintf( + "SELECT srs_id FROM gpkg_spatial_ref_sys WHERE " + "upper(organization) = upper('%q') AND organization_coordsys_id = %d", + pszAuthorityName, nAuthorityCode ); + + nSRSId = SQLGetInteger(hDB, pszSQL, &err); + sqlite3_free(pszSQL); + + // Got a match? Return it! + if ( OGRERR_NONE == err ) + { + delete poSRS; + return nSRSId; + } + + // No match, but maybe we can use the nAuthorityCode as the nSRSId? + pszSQL = sqlite3_mprintf( + "SELECT Count(*) FROM gpkg_spatial_ref_sys WHERE " + "srs_id = %d", nAuthorityCode ); + + // Yep, we can! + if ( ! SQLGetInteger(hDB, pszSQL, &err) && err == OGRERR_NONE ) + bCanUseAuthorityCode = TRUE; + sqlite3_free(pszSQL); + } + + // Translate SRS to WKT. + if( poSRS->exportToWkt( &pszWKT ) != OGRERR_NONE ) + { + delete poSRS; + CPLFree(pszWKT); + return DEFAULT_SRID; + } + + // Reuse the authority code number as SRS_ID if we can + if ( bCanUseAuthorityCode ) + { + nSRSId = nAuthorityCode; + } + // Otherwise, generate a new SRS_ID number (max + 1) + else + { + // Get the current maximum srid in the srs table. + int nMaxSRSId = SQLGetInteger(hDB, "SELECT MAX(srs_id) FROM gpkg_spatial_ref_sys", &err); + if ( OGRERR_NONE != err ) + { + CPLFree(pszWKT); + delete poSRS; + return DEFAULT_SRID; + } + + nSRSId = nMaxSRSId + 1; + } + + // Add new SRS row to gpkg_spatial_ref_sys + if( pszAuthorityName != NULL && nAuthorityCode > 0 ) + { + pszSQL = sqlite3_mprintf( + "INSERT INTO gpkg_spatial_ref_sys " + "(srs_name,srs_id,organization,organization_coordsys_id,definition) " + "VALUES ('%q', %d, upper('%q'), %d, '%q')", + GetSrsName(poSRS), nSRSId, pszAuthorityName, nAuthorityCode, pszWKT + ); + } + else + { + pszSQL = sqlite3_mprintf( + "INSERT INTO gpkg_spatial_ref_sys " + "(srs_name,srs_id,organization,organization_coordsys_id,definition) " + "VALUES ('%q', %d, upper('%q'), %d, '%q')", + GetSrsName(poSRS), nSRSId, "NONE", nSRSId, pszWKT + ); + } + + // Add new row to gpkg_spatial_ref_sys + err = SQLCommand(hDB, pszSQL); + + // Free everything that was allocated. + CPLFree(pszWKT); + sqlite3_free(pszSQL); + delete poSRS; + + return nSRSId; +} + + +/************************************************************************/ +/* GDALGeoPackageDataset() */ +/************************************************************************/ + +GDALGeoPackageDataset::GDALGeoPackageDataset() +{ + m_bNew = FALSE; + m_papoLayers = NULL; + m_nLayers = 0; + m_bUtf8 = FALSE; + m_bIdentifierAsCO = FALSE; + m_bDescriptionAsCO = FALSE; + m_bHasReadMetadataFromStorage = FALSE; + m_bMetadataDirty = FALSE; + m_papszSubDatasets = NULL; + m_pszProjection = NULL; + m_bRecordInsertedInGPKGContent = FALSE; + m_bGeoTransformValid = FALSE; + m_nSRID = -1; /* unknown cartesian */ + m_adfGeoTransform[0] = 0.0; + m_adfGeoTransform[1] = 1.0; + m_adfGeoTransform[2] = 0.0; + m_adfGeoTransform[3] = 0.0; + m_adfGeoTransform[4] = 0.0; + m_adfGeoTransform[5] = 1.0; + m_nZoomLevel = -1; + m_pabyCachedTiles = NULL; + for(int i=0;i<4;i++) + { + m_asCachedTilesDesc[i].nRow = -1; + m_asCachedTilesDesc[i].nCol = -1; + m_asCachedTilesDesc[i].nIdxWithinTileData = -1; + m_asCachedTilesDesc[i].abBandDirty[0] = FALSE; + m_asCachedTilesDesc[i].abBandDirty[1] = FALSE; + m_asCachedTilesDesc[i].abBandDirty[2] = FALSE; + m_asCachedTilesDesc[i].abBandDirty[3] = FALSE; + } + m_nShiftXTiles = 0; + m_nShiftXPixelsMod = 0; + m_nShiftYTiles = 0; + m_nShiftYPixelsMod = 0; + m_eTF = GPKG_TF_PNG_JPEG; + m_nTileMatrixWidth = 0; + m_nTileMatrixHeight = 0; + m_nZLevel = 6; + m_nQuality = 75; + m_bDither = FALSE; + m_poParentDS = NULL; + m_nOverviewCount = 0; + m_papoOverviewDS = NULL; + m_bZoomOther = FALSE; + m_bTriedEstablishingCT = FALSE; + m_pabyHugeColorArray = NULL; + m_poCT = NULL; + m_bInWriteTile = FALSE; + m_hTempDB = NULL; + m_bInFlushCache = FALSE; + m_nTileInsertionCount = 0; + m_osTilingScheme = "CUSTOM"; +} + +/************************************************************************/ +/* ~GDALGeoPackageDataset() */ +/************************************************************************/ + +GDALGeoPackageDataset::~GDALGeoPackageDataset() +{ + int i; + + SetPamFlags(0); + + if( m_poParentDS == NULL && m_osRasterTable.size() && + !m_bGeoTransformValid ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Raster table %s not correctly initialized due to missing call " + "to SetGeoTransform()", + m_osRasterTable.c_str()); + } + + FlushCache(); + FlushMetadata(); + + if( m_poParentDS != NULL ) + { + hDB = NULL; + } + else if( m_hTempDB != NULL ) + { + sqlite3_close(m_hTempDB); + m_hTempDB = NULL; + VSIUnlink(m_osTempDBFilename); + } + + for( i = 0; i < m_nLayers; i++ ) + delete m_papoLayers[i]; + for( i = 0; i < m_nOverviewCount; i++ ) + delete m_papoOverviewDS[i]; + + CPLFree( m_papoLayers ); + CPLFree( m_papoOverviewDS ); + CSLDestroy( m_papszSubDatasets ); + CPLFree(m_pszProjection); + CPLFree(m_pabyCachedTiles); + delete m_poCT; + CPLFree(m_pabyHugeColorArray); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int GDALGeoPackageDataset::Open( GDALOpenInfo* poOpenInfo ) +{ + int i; + OGRErr err; + + CPLAssert( m_nLayers == 0 ); + CPLAssert( hDB == NULL ); + + SetDescription( poOpenInfo->pszFilename ); + CPLString osFilename( poOpenInfo->pszFilename ); + CPLString osSubdatasetTableName; + if( EQUALN(poOpenInfo->pszFilename, "GPKG:", 5) ) + { + char** papszTokens = CSLTokenizeString2(poOpenInfo->pszFilename, ":", 0); + if( CSLCount(papszTokens) != 3 ) + { + CSLDestroy(papszTokens); + return FALSE; + } + + osFilename = papszTokens[1]; + osSubdatasetTableName = papszTokens[2]; + + CSLDestroy(papszTokens); + } + + bUpdate = poOpenInfo->eAccess == GA_Update; + eAccess = poOpenInfo->eAccess; /* hum annoying duplication */ + m_pszFilename = CPLStrdup( osFilename ); + + /* See if we can open the SQLite database */ + if (!OpenOrCreateDB((bUpdate) ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY) ) + return FALSE; + + /* Requirement 6: The SQLite PRAGMA integrity_check SQL command SHALL return “ok†*/ + /* http://opengis.github.io/geopackage/#_file_integrity */ + /* Disable integrity check by default, since it is expensive on big files */ + if( CSLTestBoolean(CPLGetConfigOption("OGR_GPKG_INTEGRITY_CHECK", "NO")) && + OGRERR_NONE != PragmaCheck("integrity_check", "ok", 1) ) + { + CPLError( CE_Failure, CPLE_AppDefined, "pragma integrity_check on '%s' failed", + m_pszFilename); + return FALSE; + } + + /* Requirement 7: The SQLite PRAGMA foreign_key_check() SQL with no */ + /* parameter value SHALL return an empty result set */ + /* http://opengis.github.io/geopackage/#_file_integrity */ + if ( OGRERR_NONE != PragmaCheck("foreign_key_check", "", 0) ) + { + CPLError( CE_Failure, CPLE_AppDefined, "pragma foreign_key_check on '%s' failed", + m_pszFilename); + return FALSE; + } + + /* OGR UTF-8 capability, we'll advertise UTF-8 support if we have it */ + if ( OGRERR_NONE == PragmaCheck("encoding", "UTF-8", 1) ) + { + m_bUtf8 = TRUE; + } + else + { + m_bUtf8 = FALSE; + } + + /* Check for requirement metadata tables */ + /* Requirement 10: gpkg_spatial_ref_sys must exist */ + /* Requirement 13: gpkg_contents must exist */ + static std::string aosGpkgTables[] = { + "gpkg_spatial_ref_sys", + "gpkg_contents" + }; + + for ( i = 0; i < (int)(sizeof(aosGpkgTables) / sizeof(aosGpkgTables[0])); i++ ) + { + SQLResult oResult; + char *pszSQL = sqlite3_mprintf("pragma table_info('%q')", aosGpkgTables[i].c_str()); + err = SQLQuery(hDB, pszSQL, &oResult); + sqlite3_free(pszSQL); + + if ( err != OGRERR_NONE ) + return FALSE; + + if ( oResult.nRowCount <= 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, "required GeoPackage table '%s' is missing", aosGpkgTables[i].c_str()); + SQLResultFree(&oResult); + return FALSE; + } + + SQLResultFree(&oResult); + } + + CheckUnknownExtensions(); + + int bRet = FALSE; + int bHasGPKGGeometryColumns = FALSE; + if( poOpenInfo->nOpenFlags & GDAL_OF_VECTOR ) + { + SQLResult oResult; + err = SQLQuery(hDB, "pragma table_info('gpkg_geometry_columns')", &oResult); + bHasGPKGGeometryColumns = (err == OGRERR_NONE && oResult.nRowCount > 0); + SQLResultFree(&oResult); + } + if( bHasGPKGGeometryColumns ) + { + /* Load layer definitions for all tables in gpkg_contents & gpkg_geometry_columns */ + /* and non-spatial tables as well */ + std::string osSQL = + "SELECT c.table_name, c.identifier, 1 as is_spatial, c.min_x, c.min_y, c.max_x, c.max_y " + " FROM gpkg_geometry_columns g JOIN gpkg_contents c ON (g.table_name = c.table_name)" + " WHERE c.data_type = 'features' "; + + if (HasGDALAspatialExtension()) { + osSQL += + "UNION ALL " + "SELECT table_name, identifier, 0 as is_spatial, 0 AS xmin, 0 AS ymin, 0 AS xmax, 0 AS ymax " + " FROM gpkg_contents" + " WHERE data_type = 'aspatial' "; + } + + SQLResult oResult; + err = SQLQuery(hDB, osSQL.c_str(), &oResult); + if ( err != OGRERR_NONE ) + { + SQLResultFree(&oResult); + return FALSE; + } + + if ( oResult.nRowCount > 0 ) + { + m_papoLayers = (OGRGeoPackageTableLayer**)CPLMalloc(sizeof(OGRGeoPackageTableLayer*) * oResult.nRowCount); + + for ( i = 0; i < oResult.nRowCount; i++ ) + { + const char *pszTableName = SQLResultGetValue(&oResult, 0, i); + if ( ! pszTableName ) + { + CPLError(CE_Warning, CPLE_AppDefined, "unable to read table name for layer(%d)", i); + continue; + } + int bIsSpatial = SQLResultGetValueAsInteger(&oResult, 2, i); + OGRGeoPackageTableLayer *poLayer = new OGRGeoPackageTableLayer(this, pszTableName); + if( OGRERR_NONE != poLayer->ReadTableDefinition(bIsSpatial) ) + { + delete poLayer; + CPLError(CE_Warning, CPLE_AppDefined, "unable to read table definition for '%s'", pszTableName); + continue; + } + m_papoLayers[m_nLayers++] = poLayer; + } + } + + SQLResultFree(&oResult); + bRet = TRUE; + } + + int bHasTileMatrixSet = FALSE; + if( poOpenInfo->nOpenFlags & GDAL_OF_RASTER ) + { + SQLResult oResult; + err = SQLQuery(hDB, "pragma table_info('gpkg_tile_matrix_set')", &oResult); + bHasTileMatrixSet = (err == OGRERR_NONE && oResult.nRowCount > 0); + SQLResultFree(&oResult); + } + if( bHasTileMatrixSet ) + { + SQLResult oResult; + std::string osSQL = + "SELECT c.table_name, c.identifier, c.description, c.srs_id, c.min_x, c.min_y, c.max_x, c.max_y, " + "tms.min_x, tms.min_y, tms.max_x, tms.max_y FROM gpkg_contents c JOIN gpkg_tile_matrix_set tms ON " + "c.table_name = tms.table_name WHERE data_type = 'tiles'"; + if( CSLFetchNameValue( poOpenInfo->papszOpenOptions, "TABLE") ) + osSubdatasetTableName = CSLFetchNameValue( poOpenInfo->papszOpenOptions, "TABLE"); + if( osSubdatasetTableName.size() ) + { + char* pszTmp = sqlite3_mprintf(" AND c.table_name='%q'", osSubdatasetTableName.c_str()); + osSQL += pszTmp; + sqlite3_free(pszTmp); + SetPhysicalFilename( osFilename.c_str() ); + } + + err = SQLQuery(hDB, osSQL.c_str(), &oResult); + if ( err != OGRERR_NONE ) + { + SQLResultFree(&oResult); + return FALSE; + } + + if( oResult.nRowCount == 0 && osSubdatasetTableName.size() ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table '%s' in GeoPackage dataset", + osSubdatasetTableName.c_str()); + } + else if( oResult.nRowCount == 1 ) + { + const char *pszTableName = SQLResultGetValue(&oResult, 0, 0); + const char* pszIdentifier = SQLResultGetValue(&oResult, 1, 0); + const char* pszDescription = SQLResultGetValue(&oResult, 2, 0); + const char* pszSRSId = SQLResultGetValue(&oResult, 3, 0); + const char* pszMinX = SQLResultGetValue(&oResult, 4, 0); + const char* pszMinY = SQLResultGetValue(&oResult, 5, 0); + const char* pszMaxX = SQLResultGetValue(&oResult, 6, 0); + const char* pszMaxY = SQLResultGetValue(&oResult, 7, 0); + const char* pszTMSMinX = SQLResultGetValue(&oResult, 8, 0); + const char* pszTMSMinY = SQLResultGetValue(&oResult, 9, 0); + const char* pszTMSMaxX = SQLResultGetValue(&oResult, 10, 0); + const char* pszTMSMaxY = SQLResultGetValue(&oResult, 11, 0); + if( pszTableName != NULL && pszTMSMinX != NULL && pszTMSMinY != NULL && + pszTMSMaxX != NULL && pszTMSMaxY != NULL ) + { + bRet = OpenRaster( pszTableName, pszIdentifier, pszDescription, + pszSRSId ? atoi(pszSRSId) : 0, + CPLAtof(pszTMSMinX), CPLAtof(pszTMSMinY), + CPLAtof(pszTMSMaxX), CPLAtof(pszTMSMaxY), + pszMinX, pszMinY, pszMaxX, pszMaxY, + poOpenInfo->papszOpenOptions ); + } + } + else if( oResult.nRowCount >= 1 ) + { + bRet = TRUE; + + int nSDSCount = 0; + for ( i = 0; i < oResult.nRowCount; i++ ) + { + const char *pszTableName = SQLResultGetValue(&oResult, 0, i); + const char *pszIdentifier = SQLResultGetValue(&oResult, 1, i); + if( pszTableName != NULL ) + { + m_papszSubDatasets = CSLSetNameValue( m_papszSubDatasets, + CPLSPrintf("SUBDATASET_%d_NAME", nSDSCount+1), + CPLSPrintf("GPKG:%s:%s", m_pszFilename, pszTableName) ); + if( pszIdentifier ) + m_papszSubDatasets = CSLSetNameValue( m_papszSubDatasets, + CPLSPrintf("SUBDATASET_%d_DESC", nSDSCount+1), + CPLSPrintf("%s - %s", pszTableName, pszIdentifier) ); + else + m_papszSubDatasets = CSLSetNameValue( m_papszSubDatasets, + CPLSPrintf("SUBDATASET_%d_DESC", nSDSCount+1), + pszTableName ); + } + nSDSCount ++; + } + } + + SQLResultFree(&oResult); + } + + return bRet; +} + +/************************************************************************/ +/* InitRaster() */ +/************************************************************************/ + +int GDALGeoPackageDataset::InitRaster ( GDALGeoPackageDataset* poParentDS, + const char* pszTableName, + double dfMinX, + double dfMinY, + double dfMaxX, + double dfMaxY, + const char* pszContentsMinX, + const char* pszContentsMinY, + const char* pszContentsMaxX, + const char* pszContentsMaxY, + char** papszOpenOptions, + const SQLResult& oResult, + int nIdxInResult ) +{ + m_osRasterTable = pszTableName; + m_dfTMSMinX = dfMinX; + m_dfTMSMaxY = dfMaxY; + + int nZoomLevel = atoi(SQLResultGetValue(&oResult, 0, nIdxInResult)); + double dfPixelXSize = CPLAtof(SQLResultGetValue(&oResult, 1, nIdxInResult)); + double dfPixelYSize = CPLAtof(SQLResultGetValue(&oResult, 2, nIdxInResult)); + int nTileWidth = atoi(SQLResultGetValue(&oResult, 3, nIdxInResult)); + int nTileHeight = atoi(SQLResultGetValue(&oResult, 4, nIdxInResult)); + int nTileMatrixWidth = atoi(SQLResultGetValue(&oResult, 5, nIdxInResult)); + int nTileMatrixHeight = atoi(SQLResultGetValue(&oResult, 6, nIdxInResult)); + + /* Use content bounds in priority over tile_matrix_set bounds */ + double dfGDALMinX = dfMinX; + double dfGDALMinY = dfMinY; + double dfGDALMaxX = dfMaxX; + double dfGDALMaxY = dfMaxY; + pszContentsMinX = CSLFetchNameValueDef(papszOpenOptions, "MINX", pszContentsMinX); + pszContentsMinY = CSLFetchNameValueDef(papszOpenOptions, "MINY", pszContentsMinY); + pszContentsMaxX = CSLFetchNameValueDef(papszOpenOptions, "MAXX", pszContentsMaxX); + pszContentsMaxY = CSLFetchNameValueDef(papszOpenOptions, "MAXY", pszContentsMaxY); + if( pszContentsMinX != NULL && pszContentsMinY != NULL && + pszContentsMaxX != NULL && pszContentsMaxY != NULL ) + { + dfGDALMinX = CPLAtof(pszContentsMinX); + dfGDALMinY = CPLAtof(pszContentsMinY); + dfGDALMaxX = CPLAtof(pszContentsMaxX); + dfGDALMaxY = CPLAtof(pszContentsMaxY); + } + if( dfGDALMinX >= dfGDALMaxX || dfGDALMinY >= dfGDALMaxY ) + { + return FALSE; + } + + int nBandCount = atoi(CSLFetchNameValueDef(papszOpenOptions, "BAND_COUNT", "4")); + if( nBandCount != 1 && nBandCount != 2 && nBandCount != 3 && nBandCount != 4 ) + nBandCount = 4; + + return InitRaster(poParentDS, pszTableName, nZoomLevel, nBandCount, dfMinX, dfMaxY, + dfPixelXSize, dfPixelYSize, nTileWidth, nTileHeight, + nTileMatrixWidth, nTileMatrixHeight, + dfGDALMinX, dfGDALMinY, dfGDALMaxX, dfGDALMaxY ); +} + +/************************************************************************/ +/* ComputeTileAndPixelShifts() */ +/************************************************************************/ + +void GDALGeoPackageDataset::ComputeTileAndPixelShifts() +{ + int nTileWidth, nTileHeight; + GetRasterBand(1)->GetBlockSize(&nTileWidth, &nTileHeight); + + // Compute shift between GDAL origin and TileMatrixSet origin + int nShiftXPixels = (int)floor(0.5 + (m_adfGeoTransform[0] - m_dfTMSMinX) / m_adfGeoTransform[1]); + m_nShiftXTiles = (int)floor(1.0 * nShiftXPixels / nTileWidth); + m_nShiftXPixelsMod = ((nShiftXPixels % nTileWidth) + nTileWidth) % nTileWidth; + int nShiftYPixels = (int)floor(0.5 + (m_adfGeoTransform[3] - m_dfTMSMaxY) / m_adfGeoTransform[5]); + m_nShiftYTiles = (int)floor(1.0 * nShiftYPixels / nTileHeight); + m_nShiftYPixelsMod = ((nShiftYPixels % nTileHeight) + nTileHeight) % nTileHeight; + +} + +/************************************************************************/ +/* InitRaster() */ +/************************************************************************/ + +int GDALGeoPackageDataset::InitRaster ( GDALGeoPackageDataset* poParentDS, + const char* pszTableName, + int nZoomLevel, + int nBandCount, + double dfTMSMinX, + double dfTMSMaxY, + double dfPixelXSize, + double dfPixelYSize, + int nTileWidth, + int nTileHeight, + int nTileMatrixWidth, + int nTileMatrixHeight, + double dfGDALMinX, + double dfGDALMinY, + double dfGDALMaxX, + double dfGDALMaxY ) +{ + m_osRasterTable = pszTableName; + m_dfTMSMinX = dfTMSMinX; + m_dfTMSMaxY = dfTMSMaxY; + m_nZoomLevel = nZoomLevel; + m_nTileMatrixWidth = nTileMatrixWidth; + m_nTileMatrixHeight = nTileMatrixHeight; + + m_bGeoTransformValid = TRUE; + m_adfGeoTransform[0] = dfGDALMinX; + m_adfGeoTransform[1] = dfPixelXSize; + m_adfGeoTransform[3] = dfGDALMaxY; + m_adfGeoTransform[5] = -dfPixelYSize; + double dfRasterXSize = 0.5 + (dfGDALMaxX - dfGDALMinX) / dfPixelXSize; + double dfRasterYSize = 0.5 + (dfGDALMaxY - dfGDALMinY) / dfPixelYSize; + if( dfRasterXSize > INT_MAX || dfRasterYSize > INT_MAX ) + return FALSE; + nRasterXSize = (int)dfRasterXSize; + nRasterYSize = (int)dfRasterYSize; + + m_pabyCachedTiles = (GByte*) VSIMalloc3(4 * 4, nTileWidth, nTileHeight); + if( m_pabyCachedTiles == NULL ) + { + return FALSE; + } + + for(int i = 1; i <= nBandCount; i ++) + SetBand( i, new GDALGeoPackageRasterBand(this, i, nTileWidth, nTileHeight) ); + + ComputeTileAndPixelShifts(); + + GDALPamDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); + GDALPamDataset::SetMetadataItem("ZOOM_LEVEL", CPLSPrintf("%d", m_nZoomLevel)); + + if( poParentDS ) + { + m_poParentDS = poParentDS; + bUpdate = poParentDS->bUpdate; + eAccess = poParentDS->eAccess; + hDB = poParentDS->hDB; + m_eTF = poParentDS->m_eTF; + m_nQuality = poParentDS->m_nQuality; + m_nZLevel = poParentDS->m_nZLevel; + m_bDither = poParentDS->m_bDither; + /*m_nSRID = poParentDS->m_nSRID;*/ + m_osWHERE = poParentDS->m_osWHERE; + SetDescription(CPLSPrintf("%s - zoom_level=%d", + poParentDS->GetDescription(), m_nZoomLevel)); + } + + return TRUE; +} + +/************************************************************************/ +/* GetTileFormat() */ +/************************************************************************/ + +static GPKGTileFormat GetTileFormat(const char* pszTF ) +{ + GPKGTileFormat eTF = GPKG_TF_PNG_JPEG; + if( pszTF ) + { + if( EQUAL(pszTF, "PNG_JPEG") ) + eTF = GPKG_TF_PNG_JPEG; + else if( EQUAL(pszTF, "PNG") ) + eTF = GPKG_TF_PNG; + else if( EQUAL(pszTF, "PNG8") ) + eTF = GPKG_TF_PNG8; + else if( EQUAL(pszTF, "JPEG") ) + eTF = GPKG_TF_JPEG; + else if( EQUAL(pszTF, "WEBP") ) + eTF = GPKG_TF_WEBP; + } + return eTF; +} + +/************************************************************************/ +/* OpenRaster() */ +/************************************************************************/ + +int GDALGeoPackageDataset::OpenRaster( const char* pszTableName, + const char* pszIdentifier, + const char* pszDescription, + int nSRSId, + double dfMinX, + double dfMinY, + double dfMaxX, + double dfMaxY, + const char* pszContentsMinX, + const char* pszContentsMinY, + const char* pszContentsMaxX, + const char* pszContentsMaxY, + char** papszOpenOptions ) +{ + OGRErr err; + SQLResult oResult; + + if( dfMinX >= dfMaxX || dfMinY >= dfMaxY ) + return FALSE; + + m_bRecordInsertedInGPKGContent = TRUE; + m_nSRID = nSRSId; + if( nSRSId > 0 ) + { + OGRSpatialReference* poSRS = GetSpatialRef( nSRSId ); + if( poSRS ) + { + poSRS->exportToWkt(&m_pszProjection); + delete poSRS; + } + } + + /* The NOT NULL are just in case the tables would have been built without */ + /* the mandatory constraints */ + char* pszQuotedTableName = sqlite3_mprintf("'%q'", pszTableName); + CPLString osQuotedTableName(pszQuotedTableName); + sqlite3_free(pszQuotedTableName); + char* pszSQL = sqlite3_mprintf( + "SELECT zoom_level, pixel_x_size, pixel_y_size, tile_width, tile_height, matrix_width, matrix_height FROM gpkg_tile_matrix tm " + "WHERE table_name = %s AND pixel_x_size > 0 " + "AND pixel_y_size > 0 AND tile_width > 0 AND tile_height > 0 AND matrix_width > 0 AND matrix_height > 0", + osQuotedTableName.c_str()); + CPLString osSQL(pszSQL); + const char* pszZoomLevel = CSLFetchNameValue(papszOpenOptions, "ZOOM_LEVEL"); + if( pszZoomLevel ) + { + if( bUpdate ) + osSQL += CPLSPrintf(" AND zoom_level <= %d", atoi(pszZoomLevel)); + else + { + osSQL += CPLSPrintf(" AND (zoom_level = %d OR (zoom_level < %d AND EXISTS(SELECT 1 FROM %s WHERE zoom_level = tm.zoom_level LIMIT 1)))", + atoi(pszZoomLevel), atoi(pszZoomLevel), osQuotedTableName.c_str()); + } + } + // In read-only mode, only lists non empty zoom levels + else if( !bUpdate ) + { + osSQL += CPLSPrintf(" AND EXISTS(SELECT 1 FROM %s WHERE zoom_level = tm.zoom_level LIMIT 1)", + osQuotedTableName.c_str()); + } + else if( pszZoomLevel == NULL ) + { + osSQL += CPLSPrintf(" AND zoom_level <= (SELECT MAX(zoom_level) FROM %s)", + osQuotedTableName.c_str()); + } + osSQL += " ORDER BY zoom_level DESC"; + + err = SQLQuery(hDB, osSQL.c_str(), &oResult); + if( err != OGRERR_NONE || oResult.nRowCount == 0 ) + { + if( err == OGRERR_NONE && oResult.nRowCount == 0 && + pszContentsMinX != NULL && pszContentsMinY != NULL && + pszContentsMaxX != NULL && pszContentsMaxY != NULL ) + { + SQLResultFree(&oResult); + osSQL = pszSQL; + osSQL += " ORDER BY zoom_level DESC LIMIT 1"; + err = SQLQuery(hDB, osSQL.c_str(), &oResult); + } + if( err != OGRERR_NONE || oResult.nRowCount == 0 ) + { + SQLResultFree(&oResult); + sqlite3_free(pszSQL); + return FALSE; + } + } + sqlite3_free(pszSQL); + + // If USE_TILE_EXTENT=YES, then query the tile table to find which tiles + // actually exist. + CPLString osContentsMinX, osContentsMinY, osContentsMaxX, osContentsMaxY; + if( CSLTestBoolean(CSLFetchNameValueDef(papszOpenOptions, "USE_TILE_EXTENT", "NO")) ) + { + pszSQL = sqlite3_mprintf( + "SELECT MIN(tile_column), MIN(tile_row), MAX(tile_column), MAX(tile_row) FROM '%q' WHERE zoom_level = %d", + pszTableName, atoi(SQLResultGetValue(&oResult, 0, 0))); + SQLResult oResult2; + err = SQLQuery(hDB, pszSQL, &oResult2); + sqlite3_free(pszSQL); + if ( err != OGRERR_NONE || oResult2.nRowCount == 0 ) + { + SQLResultFree(&oResult); + SQLResultFree(&oResult2); + return FALSE; + } + double dfPixelXSize = CPLAtof(SQLResultGetValue(&oResult, 1, 0)); + double dfPixelYSize = CPLAtof(SQLResultGetValue(&oResult, 2, 0)); + int nTileWidth = atoi(SQLResultGetValue(&oResult, 3, 0)); + int nTileHeight = atoi(SQLResultGetValue(&oResult, 4, 0)); + osContentsMinX = CPLSPrintf("%.18g", dfMinX + dfPixelXSize * nTileWidth * atoi(SQLResultGetValue(&oResult2, 0, 0))); + osContentsMaxY = CPLSPrintf("%.18g", dfMaxY - dfPixelYSize * nTileHeight * atoi(SQLResultGetValue(&oResult2, 1, 0))); + osContentsMaxX = CPLSPrintf("%.18g", dfMinX + dfPixelXSize * nTileWidth * (1 + atoi(SQLResultGetValue(&oResult2, 2, 0)))); + osContentsMinY = CPLSPrintf("%.18g", dfMaxY - dfPixelYSize * nTileHeight * (1 + atoi(SQLResultGetValue(&oResult2, 3, 0)))); + pszContentsMinX = osContentsMinX.c_str(); + pszContentsMinY = osContentsMinY.c_str(); + pszContentsMaxX = osContentsMaxX.c_str(); + pszContentsMaxY = osContentsMaxY.c_str(); + SQLResultFree(&oResult2); + } + + if(! InitRaster ( NULL, pszTableName, dfMinX, dfMinY, dfMaxX, dfMaxY, + pszContentsMinX, pszContentsMinY, pszContentsMaxX, pszContentsMaxY, + papszOpenOptions, oResult, 0) ) + { + SQLResultFree(&oResult); + return FALSE; + } + + CheckUnknownExtensions(TRUE); + + // Do this after CheckUnknownExtensions() so that m_eTF is set to GPKG_TF_WEBP + // if the table already registers the gpkg_webp extension + const char* pszTF = CSLFetchNameValue(papszOpenOptions, "TILE_FORMAT"); + if( pszTF ) + { + if( !bUpdate ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "DRIVER open option ignored in read-only mode"); + } + else + { + GPKGTileFormat eTF = GetTileFormat(pszTF); + if( eTF == GPKG_TF_WEBP && m_eTF != eTF ) + { + if( !RegisterWebPExtension() ) + return FALSE; + } + m_eTF = eTF; + } + } + + ParseCompressionOptions(papszOpenOptions); + + m_osWHERE = CSLFetchNameValueDef(papszOpenOptions, "WHERE", ""); + + // Set metadata + if( pszIdentifier && pszIdentifier[0] ) + GDALPamDataset::SetMetadataItem("IDENTIFIER", pszIdentifier); + if( pszDescription && pszDescription[0] ) + GDALPamDataset::SetMetadataItem("DESCRIPTION", pszDescription); + + // Add overviews + for( int i = 1; i < oResult.nRowCount; i++ ) + { + GDALGeoPackageDataset* poOvrDS = new GDALGeoPackageDataset(); + poOvrDS->InitRaster ( this, pszTableName, dfMinX, dfMinY, dfMaxX, dfMaxY, + pszContentsMinX, pszContentsMinY, pszContentsMaxX, pszContentsMaxY, + papszOpenOptions, oResult, i); + + m_papoOverviewDS = (GDALGeoPackageDataset**) CPLRealloc(m_papoOverviewDS, + sizeof(GDALGeoPackageDataset*) * (m_nOverviewCount+1)); + m_papoOverviewDS[m_nOverviewCount ++] = poOvrDS; + + int nTileWidth, nTileHeight; + poOvrDS->GetRasterBand(1)->GetBlockSize(&nTileWidth, &nTileHeight); + if( poOvrDS->GetRasterXSize() < nTileWidth && + poOvrDS->GetRasterYSize() < nTileHeight ) + { + break; + } + } + + SQLResultFree(&oResult); + + return TRUE; +} + +/************************************************************************/ +/* GetProjectionRef() */ +/************************************************************************/ + +const char* GDALGeoPackageDataset::GetProjectionRef() +{ + return (m_pszProjection) ? m_pszProjection : ""; +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +CPLErr GDALGeoPackageDataset::SetProjection( const char* pszProjection ) +{ + if( nBands == 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetProjection() not supported on a dataset with 0 band"); + return CE_Failure; + } + if( eAccess != GA_Update ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetProjection() not supported on read-only dataset"); + return CE_Failure; + } + + int nSRID; + if( pszProjection == NULL || pszProjection[0] == '\0' ) + { + nSRID = -1; + } + else + { + OGRSpatialReference oSRS; + if( oSRS.SetFromUserInput(pszProjection) != OGRERR_NONE ) + return CE_Failure; + nSRID = GetSrsId( &oSRS ); + } + + for(size_t iScheme = 0; + iScheme < sizeof(asTilingShemes)/sizeof(asTilingShemes[0]); + iScheme++ ) + { + if( EQUAL(m_osTilingScheme, asTilingShemes[iScheme].pszName) ) + { + if( nSRID != asTilingShemes[iScheme].nEPSGCode ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Projection should be EPSG:%d for %s tiling scheme", + asTilingShemes[iScheme].nEPSGCode, + m_osTilingScheme.c_str()); + return CE_Failure; + } + } + } + + m_nSRID = nSRID; + CPLFree(m_pszProjection); + m_pszProjection = pszProjection ? CPLStrdup(pszProjection) : CPLStrdup(""); + + if( m_bRecordInsertedInGPKGContent ) + { + char* pszSQL = sqlite3_mprintf("UPDATE gpkg_contents SET srs_id = %d WHERE table_name = '%q'", + m_nSRID, m_osRasterTable.c_str()); + OGRErr eErr = SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + if ( eErr != OGRERR_NONE ) + return CE_Failure; + + pszSQL = sqlite3_mprintf("UPDATE gpkg_tile_matrix_set SET srs_id = %d WHERE table_name = '%q'", + m_nSRID, m_osRasterTable.c_str()); + eErr = SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + if ( eErr != OGRERR_NONE ) + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* GetGeoTransform() */ +/************************************************************************/ + +CPLErr GDALGeoPackageDataset::GetGeoTransform( double* padfGeoTransform ) +{ + memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double)); + if( !m_bGeoTransformValid ) + return CE_Failure; + else + return CE_None; +} + +/************************************************************************/ +/* SetGeoTransform() */ +/************************************************************************/ + +CPLErr GDALGeoPackageDataset::SetGeoTransform( double* padfGeoTransform ) +{ + if( nBands == 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetGeoTransform() not supported on a dataset with 0 band"); + return CE_Failure; + } + if( eAccess != GA_Update ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetGeoTransform() not supported on read-only dataset"); + return CE_Failure; + } + if( m_bGeoTransformValid ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot modify geotransform once set"); + return CE_Failure; + } + if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0 || + padfGeoTransform[5] > 0.0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Only north-up non rotated geotransform supported"); + return CE_Failure; + } + + for(size_t iScheme = 0; + iScheme < sizeof(asTilingShemes)/sizeof(asTilingShemes[0]); + iScheme++ ) + { + if( EQUAL(m_osTilingScheme, asTilingShemes[iScheme].pszName) ) + { + double dfPixelXSizeZoomLevel0 = asTilingShemes[iScheme].dfPixelXSizeZoomLevel0; + double dfPixelYSizeZoomLevel0 = asTilingShemes[iScheme].dfPixelYSizeZoomLevel0; + for( m_nZoomLevel = 0; m_nZoomLevel < 25; m_nZoomLevel++ ) + { + double dfExpectedPixelXSize = dfPixelXSizeZoomLevel0 / (1 << m_nZoomLevel); + double dfExpectedPixelYSize = dfPixelYSizeZoomLevel0 / (1 << m_nZoomLevel); + if( fabs( padfGeoTransform[1] - dfExpectedPixelXSize ) < 1e-8 * dfExpectedPixelXSize && + fabs( fabs(padfGeoTransform[5]) - dfExpectedPixelYSize ) < 1e-8 * dfExpectedPixelYSize ) + { + break; + } + } + if( m_nZoomLevel == 25 ) + { + m_nZoomLevel = -1; + CPLError(CE_Failure, CPLE_NotSupported, + "Could not find an appropriate zoom level of %s tiling scheme that matches raster pixel size", + m_osTilingScheme.c_str()); + return CE_Failure; + } + break; + } + } + + memcpy(m_adfGeoTransform, padfGeoTransform, 6 * sizeof(double)); + m_bGeoTransformValid = TRUE; + + return FinalizeRasterRegistration(); +} + +/************************************************************************/ +/* FinalizeRasterRegistration() */ +/************************************************************************/ + +CPLErr GDALGeoPackageDataset::FinalizeRasterRegistration() +{ + OGRErr eErr; + char* pszSQL; + + m_dfTMSMinX = m_adfGeoTransform[0]; + m_dfTMSMaxY = m_adfGeoTransform[3]; + + int nTileWidth, nTileHeight; + GetRasterBand(1)->GetBlockSize(&nTileWidth, &nTileHeight); + m_nTileMatrixWidth = (nRasterXSize + nTileWidth - 1) / nTileWidth; + m_nTileMatrixHeight = (nRasterYSize + nTileHeight - 1) / nTileHeight; + + if( m_nZoomLevel < 0 ) + { + m_nZoomLevel = 0; + while( (nRasterXSize >> m_nZoomLevel) > nTileWidth || + (nRasterYSize >> m_nZoomLevel) > nTileHeight ) + m_nZoomLevel ++; + } + + double dfPixelXSizeZoomLevel0 = m_adfGeoTransform[1] * (1 << m_nZoomLevel); + double dfPixelYSizeZoomLevel0 = fabs(m_adfGeoTransform[5]) * (1 << m_nZoomLevel); + int nTileXCountZoomLevel0 = ((nRasterXSize >> m_nZoomLevel) + nTileWidth - 1) / nTileWidth; + int nTileYCountZoomLevel0 = ((nRasterYSize >> m_nZoomLevel) + nTileHeight - 1) / nTileHeight; + + for(size_t iScheme = 0; + iScheme < sizeof(asTilingShemes)/sizeof(asTilingShemes[0]); + iScheme++ ) + { + if( EQUAL(m_osTilingScheme, asTilingShemes[iScheme].pszName) ) + { + CPLAssert( m_nZoomLevel >= 0 ); + m_dfTMSMinX = asTilingShemes[iScheme].dfMinX; + m_dfTMSMaxY = asTilingShemes[iScheme].dfMaxY; + dfPixelXSizeZoomLevel0 = asTilingShemes[iScheme].dfPixelXSizeZoomLevel0; + dfPixelYSizeZoomLevel0 = asTilingShemes[iScheme].dfPixelYSizeZoomLevel0; + nTileXCountZoomLevel0 = asTilingShemes[iScheme].nTileXCountZoomLevel0; + nTileYCountZoomLevel0 = asTilingShemes[iScheme].nTileYCountZoomLevel0; + m_nTileMatrixWidth = nTileXCountZoomLevel0 * (1 << m_nZoomLevel); + m_nTileMatrixHeight = nTileYCountZoomLevel0 * (1 << m_nZoomLevel); + break; + } + } + + ComputeTileAndPixelShifts(); + + double dfGDALMinX = m_adfGeoTransform[0]; + double dfGDALMinY = m_adfGeoTransform[3] + nRasterYSize * m_adfGeoTransform[5]; + double dfGDALMaxX = m_adfGeoTransform[0] + nRasterXSize * m_adfGeoTransform[1]; + double dfGDALMaxY = m_adfGeoTransform[3]; + + SoftStartTransaction(); + + pszSQL = sqlite3_mprintf("INSERT INTO gpkg_contents " + "(table_name,data_type,identifier,description,min_x,min_y,max_x,max_y,srs_id) VALUES " + "('%q','tiles','%q','%q',%.18g,%.18g,%.18g,%.18g,%d)", + m_osRasterTable.c_str(), + m_osIdentifier.c_str(), + m_osDescription.c_str(), + dfGDALMinX, dfGDALMinY, dfGDALMaxX, dfGDALMaxY, + m_nSRID); + eErr = SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + if ( eErr != OGRERR_NONE ) + return CE_Failure; + + double dfTMSMaxX = m_dfTMSMinX + nTileXCountZoomLevel0 * nTileWidth * dfPixelXSizeZoomLevel0; + double dfTMSMinY = m_dfTMSMaxY - nTileYCountZoomLevel0 * nTileHeight * dfPixelYSizeZoomLevel0; + + pszSQL = sqlite3_mprintf("INSERT INTO gpkg_tile_matrix_set " + "(table_name,srs_id,min_x,min_y,max_x,max_y) VALUES " + "('%q',%d,%.18g,%.18g,%.18g,%.18g)", + m_osRasterTable.c_str(), m_nSRID, + m_dfTMSMinX,dfTMSMinY,dfTMSMaxX,m_dfTMSMaxY); + eErr = SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + if ( eErr != OGRERR_NONE ) + return CE_Failure; + + m_papoOverviewDS = (GDALGeoPackageDataset**) CPLCalloc(sizeof(GDALGeoPackageDataset*), + m_nZoomLevel); + + for(int i=0; i<=m_nZoomLevel; i++) + { + double dfPixelXSizeZoomLevel, dfPixelYSizeZoomLevel; + int nTileMatrixWidth, nTileMatrixHeight; + if( EQUAL(m_osTilingScheme, "CUSTOM") ) + { + dfPixelXSizeZoomLevel = m_adfGeoTransform[1] * (1 << (m_nZoomLevel-i)); + dfPixelYSizeZoomLevel = fabs(m_adfGeoTransform[5]) * (1 << (m_nZoomLevel-i)); + nTileMatrixWidth = ((nRasterXSize >> (m_nZoomLevel-i)) + nTileWidth - 1) / nTileWidth; + nTileMatrixHeight = ((nRasterYSize >> (m_nZoomLevel-i)) + nTileHeight - 1) / nTileHeight; + } + else + { + dfPixelXSizeZoomLevel = dfPixelXSizeZoomLevel0 / (1 << i); + dfPixelYSizeZoomLevel = dfPixelYSizeZoomLevel0 / (1 << i); + nTileMatrixWidth = nTileXCountZoomLevel0 * (1 << i); + nTileMatrixHeight = nTileYCountZoomLevel0 * (1 << i); + } + pszSQL = sqlite3_mprintf("INSERT INTO gpkg_tile_matrix " + "(table_name,zoom_level,matrix_width,matrix_height,tile_width,tile_height,pixel_x_size,pixel_y_size) VALUES " + "('%q',%d,%d,%d,%d,%d,%.18g,%.18g)", + m_osRasterTable.c_str(),i,nTileMatrixWidth,nTileMatrixHeight, + nTileWidth,nTileHeight,dfPixelXSizeZoomLevel,dfPixelYSizeZoomLevel); + eErr = SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + if ( eErr != OGRERR_NONE ) + return CE_Failure; + + if( i < m_nZoomLevel ) + { + GDALGeoPackageDataset* poOvrDS = new GDALGeoPackageDataset(); + poOvrDS->InitRaster ( this, m_osRasterTable, i, nBands, + m_dfTMSMinX, m_dfTMSMaxY, + dfPixelXSizeZoomLevel, dfPixelYSizeZoomLevel, + nTileWidth, nTileHeight, + nTileMatrixWidth,nTileMatrixHeight, + dfGDALMinX, dfGDALMinY, + dfGDALMaxX, dfGDALMaxY ); + + m_papoOverviewDS[m_nZoomLevel-1-i] = poOvrDS; + } + } + + SoftCommitTransaction(); + + m_nOverviewCount = m_nZoomLevel; + m_bRecordInsertedInGPKGContent = TRUE; + + return CE_None; +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void GDALGeoPackageDataset::FlushCache() +{ + FlushCacheWithErrCode(); +} + +CPLErr GDALGeoPackageDataset::FlushCacheWithErrCode() + +{ + if( m_bInFlushCache ) + return CE_None; + m_bInFlushCache = TRUE; + // Short circuit GDALPamDataset to avoid serialization to .aux.xml + GDALDataset::FlushCache(); + + for( int i = 0; i < m_nLayers; i++ ) + { + m_papoLayers[i]->RunDeferredCreationIfNecessary(); + m_papoLayers[i]->CreateSpatialIndexIfNecessary(); + } + + CPLErr eErr = CE_None; + if( bUpdate ) + { + if( m_nShiftXPixelsMod || m_nShiftYPixelsMod ) + { + eErr = FlushRemainingShiftedTiles(); + } + else + { + eErr = WriteTile(); + } + } + + GDALGeoPackageDataset* poMainDS = m_poParentDS ? m_poParentDS : this; + if( poMainDS->m_nTileInsertionCount ) + { + poMainDS->SoftCommitTransaction(); + poMainDS->m_nTileInsertionCount = 0; + } + + m_bInFlushCache = FALSE; + return eErr; +} + +/************************************************************************/ +/* IBuildOverviews() */ +/************************************************************************/ + +static int GetFloorPowerOfTwo(int n) +{ + int p2 = 1; + while( (n = n >> 1) > 0 ) + { + p2 <<= 1; + } + return p2; +} + +CPLErr GDALGeoPackageDataset::IBuildOverviews( + const char * pszResampling, + int nOverviews, int * panOverviewList, + int nBandsIn, CPL_UNUSED int * panBandList, + GDALProgressFunc pfnProgress, void * pProgressData ) +{ + if( GetAccess() != GA_Update ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Overview building not supported on a database opened in read-only mode"); + return CE_Failure; + } + if( m_poParentDS != NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Overview building not supported on overview dataset"); + return CE_Failure; + } + + if( nOverviews == 0 ) + { + for(int i=0;iFlushCache(); + char* pszSQL = sqlite3_mprintf("DELETE FROM '%q' WHERE zoom_level < %d", + m_osRasterTable.c_str(), + m_nZoomLevel); + char* pszErrMsg = NULL; + int ret = sqlite3_exec(hDB, pszSQL, NULL, NULL, &pszErrMsg); + sqlite3_free(pszSQL); + if( ret != SQLITE_OK ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Failure: %s", + pszErrMsg ? pszErrMsg : ""); + sqlite3_free(pszErrMsg); + return CE_Failure; + } + return CE_None; + } + + if( nBandsIn != nBands ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Generation of overviews in GPKG only" + "supported when operating on all bands." ); + return CE_Failure; + } + + if( m_nOverviewCount == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Image too small to support overviews"); + return CE_Failure; + } + + FlushCache(); + for(int i=0;i= 2"); + return CE_Failure; + } + + int bFound = FALSE; + int jCandidate = -1; + int nMaxOvFactor = 0; + for(int j=0;jGetRasterXSize()); + nMaxOvFactor = nOvFactor; + + if( nOvFactor == panOverviewList[i] + || nOvFactor == GDALOvLevelAdjust2( panOverviewList[i], + GetRasterXSize(), + GetRasterYSize() ) ) + { + bFound = TRUE; + break; + } + + if( jCandidate < 0 && nOvFactor > panOverviewList[i] ) + jCandidate = j; + } + + if( !bFound ) + { + /* Mostly for debug */ + if( !CSLTestBoolean(CPLGetConfigOption("ALLOW_GPKG_ZOOM_OTHER_EXTENSION", "YES")) ) + { + CPLString osOvrList; + for(int j=0;jGetRasterXSize()); + int nODSXSize = (int)(0.5 + GetRasterXSize() / (double) nOvFactor); + if( nODSXSize != poODS->GetRasterXSize() ) + { + int nOvFactorPowerOfTwo = GetFloorPowerOfTwo(nOvFactor); + nODSXSize = (int)(0.5 + GetRasterXSize() / (double) nOvFactorPowerOfTwo); + if( nODSXSize == poODS->GetRasterXSize() ) + nOvFactor = nOvFactorPowerOfTwo; + else + { + nOvFactorPowerOfTwo <<= 1; + nODSXSize = (int)(0.5 + GetRasterXSize() / (double) nOvFactorPowerOfTwo); + if( nODSXSize == poODS->GetRasterXSize() ) + nOvFactor = nOvFactorPowerOfTwo; + } + } + if( j != 0 ) + osOvrList += " "; + osOvrList += CPLSPrintf("%d", nOvFactor); + } + CPLError(CE_Failure, CPLE_NotSupported, + "Only overviews %s can be computed", osOvrList.c_str()); + return CE_Failure; + } + else + { + int nOvFactor = panOverviewList[i]; + if( jCandidate < 0 ) + jCandidate = m_nOverviewCount; + + int nOvXSize = GetRasterXSize() / nOvFactor; + int nOvYSize = GetRasterYSize() / nOvFactor; + if( nOvXSize < 8 || nOvYSize < 8) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Too big overview factor : %d. Would result in a %dx%d overview", + nOvFactor, nOvXSize, nOvYSize); + return CE_Failure; + } + if( !(jCandidate == m_nOverviewCount && nOvFactor == 2 * nMaxOvFactor) && + !m_bZoomOther ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Use of overview factor %d cause gpkg_zoom_other extension to be needed", + nOvFactor); + RegisterZoomOtherExtension(); + m_bZoomOther = TRUE; + } + + SoftStartTransaction(); + + CPLAssert(jCandidate > 0); + int nNewZoomLevel = m_papoOverviewDS[jCandidate-1]->m_nZoomLevel; + + char* pszSQL; + OGRErr eErr; + for(int k=0;k<=jCandidate;k++) + { + pszSQL = sqlite3_mprintf("UPDATE gpkg_tile_matrix SET zoom_level = %d " + "WHERE table_name = '%q' AND zoom_level = %d", + m_nZoomLevel - k + 1, + m_osRasterTable.c_str(), + m_nZoomLevel - k); + eErr = SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + if ( eErr != OGRERR_NONE ) + { + SoftRollbackTransaction(); + return CE_Failure; + } + + pszSQL = sqlite3_mprintf("UPDATE '%q' SET zoom_level = %d " + "WHERE zoom_level = %d", + m_osRasterTable.c_str(), + m_nZoomLevel - k + 1, + m_nZoomLevel - k); + eErr = SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + if ( eErr != OGRERR_NONE ) + { + SoftRollbackTransaction(); + return CE_Failure; + } + } + + double dfGDALMinX = m_adfGeoTransform[0]; + double dfGDALMinY = m_adfGeoTransform[3] + nRasterYSize * m_adfGeoTransform[5]; + double dfGDALMaxX = m_adfGeoTransform[0] + nRasterXSize * m_adfGeoTransform[1]; + double dfGDALMaxY = m_adfGeoTransform[3]; + double dfPixelXSizeZoomLevel = m_adfGeoTransform[1] * nOvFactor; + double dfPixelYSizeZoomLevel = fabs(m_adfGeoTransform[5]) * nOvFactor; + int nTileWidth, nTileHeight; + GetRasterBand(1)->GetBlockSize(&nTileWidth, &nTileHeight); + int nTileMatrixWidth = (nOvXSize + nTileWidth - 1) / nTileWidth; + int nTileMatrixHeight = (nOvYSize + nTileHeight - 1) / nTileHeight; + pszSQL = sqlite3_mprintf("INSERT INTO gpkg_tile_matrix " + "(table_name,zoom_level,matrix_width,matrix_height,tile_width,tile_height,pixel_x_size,pixel_y_size) VALUES " + "('%q',%d,%d,%d,%d,%d,%.18g,%.18g)", + m_osRasterTable.c_str(),nNewZoomLevel,nTileMatrixWidth,nTileMatrixHeight, + nTileWidth,nTileHeight,dfPixelXSizeZoomLevel,dfPixelYSizeZoomLevel); + eErr = SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + if ( eErr != OGRERR_NONE ) + { + SoftRollbackTransaction(); + return CE_Failure; + } + + SoftCommitTransaction(); + + m_nZoomLevel ++; /* this change our zoom level as well as previous overviews */ + for(int k=0;km_nZoomLevel ++; + + GDALGeoPackageDataset* poOvrDS = new GDALGeoPackageDataset(); + poOvrDS->InitRaster ( this, m_osRasterTable, + nNewZoomLevel, nBands, + m_dfTMSMinX, m_dfTMSMaxY, + dfPixelXSizeZoomLevel, dfPixelYSizeZoomLevel, + nTileWidth, nTileHeight, + nTileMatrixWidth,nTileMatrixHeight, + dfGDALMinX, dfGDALMinY, + dfGDALMaxX, dfGDALMaxY ); + m_papoOverviewDS = (GDALGeoPackageDataset**) CPLRealloc( + m_papoOverviewDS, sizeof(GDALGeoPackageDataset*) * (m_nOverviewCount+1)); + + if( jCandidate < m_nOverviewCount ) + { + memmove(m_papoOverviewDS + jCandidate + 1, + m_papoOverviewDS + jCandidate, + sizeof(GDALGeoPackageDataset*) * (m_nOverviewCount-jCandidate)); + } + m_papoOverviewDS[jCandidate] = poOvrDS; + m_nOverviewCount ++; + } + } + } + + GDALRasterBand*** papapoOverviewBands = (GDALRasterBand ***) CPLCalloc(sizeof(void*),nBands); + for( int iBand = 0; iBand < nBands; iBand++ ) + { + papapoOverviewBands[iBand] = (GDALRasterBand **) CPLCalloc(sizeof(void*),nOverviews); + int iCurOverview = 0; + for(int i=0;iGetRasterXSize(), + GetRasterXSize(), + poODS->GetRasterYSize(), + GetRasterYSize()); + + if( nOvFactor == panOverviewList[i] + || nOvFactor == GDALOvLevelAdjust2( panOverviewList[i], + GetRasterXSize(), + GetRasterYSize() ) ) + { + papapoOverviewBands[iBand][iCurOverview] = poODS->GetRasterBand(iBand+1); + iCurOverview++ ; + break; + } + } + CPLAssert(j < m_nOverviewCount); + } + CPLAssert(iCurOverview == nOverviews); + } + + CPLErr eErr = GDALRegenerateOverviewsMultiBand(nBands, papoBands, + nOverviews, papapoOverviewBands, + pszResampling, pfnProgress, pProgressData ); + + for( int iBand = 0; iBand < nBands; iBand++ ) + { + CPLFree(papapoOverviewBands[iBand]); + } + CPLFree(papapoOverviewBands); + + return eErr; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **GDALGeoPackageDataset::GetMetadataDomainList() +{ + GetMetadata(); + if( m_osRasterTable.size() != 0 ) + GetMetadata("GEOPACKAGE"); + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, + "SUBDATASETS", NULL); +} + +/************************************************************************/ +/* CheckMetadataDomain() */ +/************************************************************************/ + +const char* GDALGeoPackageDataset::CheckMetadataDomain( const char* pszDomain ) +{ + if( pszDomain != NULL && EQUAL(pszDomain, "GEOPACKAGE") && + m_osRasterTable.size() == 0 ) + { + CPLError(CE_Warning, CPLE_IllegalArg, + "Using GEOPACKAGE for a non-raster geopackage is not supported. " + "Using default domain instead"); + return NULL; + } + return pszDomain; +} + +/************************************************************************/ +/* HasMetadataTables() */ +/************************************************************************/ + +int GDALGeoPackageDataset::HasMetadataTables() +{ + OGRErr err; + int nCount = SQLGetInteger(hDB, + "SELECT COUNT(*) FROM sqlite_master WHERE name IN " + "('gpkg_metadata', 'gpkg_metadata_reference') " + "AND type IN ('table', 'view')", &err); + return ( err == OGRERR_NONE && nCount == 2 ); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **GDALGeoPackageDataset::GetMetadata( const char *pszDomain ) + +{ + pszDomain = CheckMetadataDomain(pszDomain); + if( pszDomain != NULL && EQUAL(pszDomain,"SUBDATASETS") ) + return m_papszSubDatasets; + + if( m_bHasReadMetadataFromStorage ) + return GDALPamDataset::GetMetadata( pszDomain ); + + m_bHasReadMetadataFromStorage = TRUE; + + if ( !HasMetadataTables() ) + return GDALPamDataset::GetMetadata( pszDomain ); + + char* pszSQL; + if( m_osRasterTable.size() ) + { + pszSQL = sqlite3_mprintf( + "SELECT md.metadata, md.md_standard_uri, md.mime_type, mdr.reference_scope FROM gpkg_metadata md " + "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id ) " + "WHERE mdr.reference_scope = 'geopackage' OR " + "(mdr.reference_scope = 'table' AND mdr.table_name = '%q') ORDER BY md.id", + m_osRasterTable.c_str()); + } + else + { + pszSQL = sqlite3_mprintf( + "SELECT md.metadata, md.md_standard_uri, md.mime_type, mdr.reference_scope FROM gpkg_metadata md " + "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id ) " + "WHERE mdr.reference_scope = 'geopackage' ORDER BY md.id"); + } + + SQLResult oResult; + OGRErr err = SQLQuery(hDB, pszSQL, &oResult); + sqlite3_free(pszSQL); + if ( err != OGRERR_NONE ) + { + SQLResultFree(&oResult); + return GDALPamDataset::GetMetadata( pszDomain ); + } + + char** papszMetadata = CSLDuplicate(GDALPamDataset::GetMetadata()); + + /* GDAL metadata */ + for(int i=0;ipsChild = psXMLNode; + pszXML = CPLSerializeXMLTree(psMasterXMLNode); + CPLDestroyXMLNode(psMasterXMLNode); + } + psXMLNode = NULL; + + char* pszSQL; + if( pszTableName && pszTableName[0] != '\0' ) + { + pszSQL = sqlite3_mprintf( + "SELECT md.id FROM gpkg_metadata md " + "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id ) " + "WHERE md.md_scope = 'dataset' AND md.md_standard_uri='http://gdal.org' " + "AND md.mime_type='text/xml' AND mdr.reference_scope = 'table' AND mdr.table_name = '%q'", + pszTableName); + } + else + { + pszSQL = sqlite3_mprintf( + "SELECT md.id FROM gpkg_metadata md " + "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id ) " + "WHERE md.md_scope = 'dataset' AND md.md_standard_uri='http://gdal.org' " + "AND md.mime_type='text/xml' AND mdr.reference_scope = 'geopackage'"); + } + OGRErr err; + int mdId = SQLGetInteger(hDB, pszSQL, &err); + if( err != OGRERR_NONE ) + mdId = -1; + sqlite3_free(pszSQL); + + if( bIsEmpty ) + { + if( mdId >= 0 ) + { + SQLCommand(hDB, + CPLSPrintf("DELETE FROM gpkg_metadata_reference WHERE md_file_id = %d", mdId)); + SQLCommand(hDB, + CPLSPrintf("DELETE FROM gpkg_metadata WHERE id = %d", mdId)); + } + } + else + { + if( mdId >= 0 ) + { + pszSQL = sqlite3_mprintf( + "UPDATE gpkg_metadata SET metadata = '%q' WHERE id = %d", + pszXML, mdId); + } + else + { + pszSQL = sqlite3_mprintf( + "INSERT INTO gpkg_metadata (md_scope, md_standard_uri, mime_type, metadata) VALUES " + "('dataset','http://gdal.org','text/xml','%q')", + pszXML); + } + SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + + CPLFree(pszXML); + + if( mdId < 0 ) + { + const sqlite_int64 nFID = sqlite3_last_insert_rowid( hDB ); + if( pszTableName != NULL && pszTableName[0] != '\0' ) + { + pszSQL = sqlite3_mprintf( + "INSERT INTO gpkg_metadata_reference (reference_scope, table_name, timestamp, md_file_id) VALUES " + "('table', '%q', strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ','now'), %d)", + pszTableName, (int)nFID); + } + else + { + pszSQL = sqlite3_mprintf( + "INSERT INTO gpkg_metadata_reference (reference_scope, timestamp, md_file_id) VALUES " + "('geopackage', strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ','now'), %d)", + (int)nFID); + } + } + else + { + pszSQL = sqlite3_mprintf( + "UPDATE gpkg_metadata_reference SET timestamp = strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ','now') WHERE md_file_id = %d", + mdId); + } + SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + } +} + +/************************************************************************/ +/* CreateMetadataTables() */ +/************************************************************************/ + +int GDALGeoPackageDataset::CreateMetadataTables() +{ + int bCreateTriggers = CSLTestBoolean(CPLGetConfigOption("CREATE_TRIGGERS", "YES")); + + /* From C.10. gpkg_metadata Table 35. gpkg_metadata Table Definition SQL */ + const char* pszMetadata = + "CREATE TABLE gpkg_metadata (" + "id INTEGER CONSTRAINT m_pk PRIMARY KEY ASC NOT NULL UNIQUE," + "md_scope TEXT NOT NULL DEFAULT 'dataset'," + "md_standard_uri TEXT NOT NULL," + "mime_type TEXT NOT NULL DEFAULT 'text/xml'," + "metadata TEXT NOT NULL" + ")"; + + if ( OGRERR_NONE != SQLCommand(hDB, pszMetadata) ) + return FALSE; + + /* From D.2. metadata Table 40. metadata Trigger Definition SQL */ + const char* pszMetadataTriggers = + "CREATE TRIGGER 'gpkg_metadata_md_scope_insert' " + "BEFORE INSERT ON 'gpkg_metadata' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table gpkg_metadata violates " + "constraint: md_scope must be one of undefined | fieldSession | " + "collectionSession | series | dataset | featureType | feature | " + "attributeType | attribute | tile | model | catalogue | schema | " + "taxonomy software | service | collectionHardware | " + "nonGeographicDataset | dimensionGroup') " + "WHERE NOT(NEW.md_scope IN " + "('undefined','fieldSession','collectionSession','series','dataset', " + "'featureType','feature','attributeType','attribute','tile','model', " + "'catalogue','schema','taxonomy','software','service', " + "'collectionHardware','nonGeographicDataset','dimensionGroup')); " + "END; " + "CREATE TRIGGER 'gpkg_metadata_md_scope_update' " + "BEFORE UPDATE OF 'md_scope' ON 'gpkg_metadata' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table gpkg_metadata violates " + "constraint: md_scope must be one of undefined | fieldSession | " + "collectionSession | series | dataset | featureType | feature | " + "attributeType | attribute | tile | model | catalogue | schema | " + "taxonomy software | service | collectionHardware | " + "nonGeographicDataset | dimensionGroup') " + "WHERE NOT(NEW.md_scope IN " + "('undefined','fieldSession','collectionSession','series','dataset', " + "'featureType','feature','attributeType','attribute','tile','model', " + "'catalogue','schema','taxonomy','software','service', " + "'collectionHardware','nonGeographicDataset','dimensionGroup')); " + "END"; + if ( bCreateTriggers && OGRERR_NONE != SQLCommand(hDB, pszMetadataTriggers) ) + return FALSE; + + /* From C.11. gpkg_metadata_reference Table 36. gpkg_metadata_reference Table Definition SQL */ + const char* pszMetadataReference = + "CREATE TABLE gpkg_metadata_reference (" + "reference_scope TEXT NOT NULL," + "table_name TEXT," + "column_name TEXT," + "row_id_value INTEGER," + "timestamp DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))," + "md_file_id INTEGER NOT NULL," + "md_parent_id INTEGER," + "CONSTRAINT crmr_mfi_fk FOREIGN KEY (md_file_id) REFERENCES gpkg_metadata(id)," + "CONSTRAINT crmr_mpi_fk FOREIGN KEY (md_parent_id) REFERENCES gpkg_metadata(id)" + ")"; + + if ( OGRERR_NONE != SQLCommand(hDB, pszMetadataReference) ) + return FALSE; + + /* From D.3. metadata_reference Table 41. gpkg_metadata_reference Trigger Definition SQL */ + const char* pszMetadataReferenceTriggers = + "CREATE TRIGGER 'gpkg_metadata_reference_reference_scope_insert' " + "BEFORE INSERT ON 'gpkg_metadata_reference' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table gpkg_metadata_reference " + "violates constraint: reference_scope must be one of \"geopackage\", " + "table\", \"column\", \"row\", \"row/col\"') " + "WHERE NOT NEW.reference_scope IN " + "('geopackage','table','column','row','row/col'); " + "END; " + "CREATE TRIGGER 'gpkg_metadata_reference_reference_scope_update' " + "BEFORE UPDATE OF 'reference_scope' ON 'gpkg_metadata_reference' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table gpkg_metadata_reference " + "violates constraint: referrence_scope must be one of \"geopackage\", " + "\"table\", \"column\", \"row\", \"row/col\"') " + "WHERE NOT NEW.reference_scope IN " + "('geopackage','table','column','row','row/col'); " + "END; " + "CREATE TRIGGER 'gpkg_metadata_reference_column_name_insert' " + "BEFORE INSERT ON 'gpkg_metadata_reference' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table gpkg_metadata_reference " + "violates constraint: column name must be NULL when reference_scope " + "is \"geopackage\", \"table\" or \"row\"') " + "WHERE (NEW.reference_scope IN ('geopackage','table','row') " + "AND NEW.column_name IS NOT NULL); " + "SELECT RAISE(ABORT, 'insert on table gpkg_metadata_reference " + "violates constraint: column name must be defined for the specified " + "table when reference_scope is \"column\" or \"row/col\"') " + "WHERE (NEW.reference_scope IN ('column','row/col') " + "AND NOT NEW.table_name IN ( " + "SELECT name FROM SQLITE_MASTER WHERE type = 'table' " + "AND name = NEW.table_name " + "AND sql LIKE ('%' || NEW.column_name || '%'))); " + "END; " + "CREATE TRIGGER 'gpkg_metadata_reference_column_name_update' " + "BEFORE UPDATE OF column_name ON 'gpkg_metadata_reference' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table gpkg_metadata_reference " + "violates constraint: column name must be NULL when reference_scope " + "is \"geopackage\", \"table\" or \"row\"') " + "WHERE (NEW.reference_scope IN ('geopackage','table','row') " + "AND NEW.column_nameIS NOT NULL); " + "SELECT RAISE(ABORT, 'update on table gpkg_metadata_reference " + "violates constraint: column name must be defined for the specified " + "table when reference_scope is \"column\" or \"row/col\"') " + "WHERE (NEW.reference_scope IN ('column','row/col') " + "AND NOT NEW.table_name IN ( " + "SELECT name FROM SQLITE_MASTER WHERE type = 'table' " + "AND name = NEW.table_name " + "AND sql LIKE ('%' || NEW.column_name || '%'))); " + "END; " + "CREATE TRIGGER 'gpkg_metadata_reference_row_id_value_insert' " + "BEFORE INSERT ON 'gpkg_metadata_reference' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table gpkg_metadata_reference " + "violates constraint: row_id_value must be NULL when reference_scope " + "is \"geopackage\", \"table\" or \"column\"') " + "WHERE NEW.reference_scope IN ('geopackage','table','column') " + "AND NEW.row_id_value IS NOT NULL; " + "SELECT RAISE(ABORT, 'insert on table gpkg_metadata_reference " + "violates constraint: row_id_value must exist in specified table when " + "reference_scope is \"row\" or \"row/col\"') " + "WHERE NEW.reference_scope IN ('row','row/col') " + "AND NOT EXISTS (SELECT rowid " + "FROM (SELECT NEW.table_name AS table_name) WHERE rowid = " + "NEW.row_id_value); " + "END; " + "CREATE TRIGGER 'gpkg_metadata_reference_row_id_value_update' " + "BEFORE UPDATE OF 'row_id_value' ON 'gpkg_metadata_reference' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table gpkg_metadata_reference " + "violates constraint: row_id_value must be NULL when reference_scope " + "is \"geopackage\", \"table\" or \"column\"') " + "WHERE NEW.reference_scope IN ('geopackage','table','column') " + "AND NEW.row_id_value IS NOT NULL; " + "SELECT RAISE(ABORT, 'update on table gpkg_metadata_reference " + "violates constraint: row_id_value must exist in specified table when " + "reference_scope is \"row\" or \"row/col\"') " + "WHERE NEW.reference_scope IN ('row','row/col') " + "AND NOT EXISTS (SELECT rowid " + "FROM (SELECT NEW.table_name AS table_name) WHERE rowid = " + "NEW.row_id_value); " + "END; " + "CREATE TRIGGER 'gpkg_metadata_reference_timestamp_insert' " + "BEFORE INSERT ON 'gpkg_metadata_reference' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table gpkg_metadata_reference " + "violates constraint: timestamp must be a valid time in ISO 8601 " + "\"yyyy-mm-ddThh:mm:ss.cccZ\" form') " + "WHERE NOT (NEW.timestamp GLOB " + "'[1-2][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9].[0-9][0-9][0-9]Z' " + "AND strftime('%s',NEW.timestamp) NOT NULL); " + "END; " + "CREATE TRIGGER 'gpkg_metadata_reference_timestamp_update' " + "BEFORE UPDATE OF 'timestamp' ON 'gpkg_metadata_reference' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table gpkg_metadata_reference " + "violates constraint: timestamp must be a valid time in ISO 8601 " + "\"yyyy-mm-ddThh:mm:ss.cccZ\" form') " + "WHERE NOT (NEW.timestamp GLOB " + "'[1-2][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-5][0-9].[0-9][0-9][0-9]Z' " + "AND strftime('%s',NEW.timestamp) NOT NULL); " + "END"; + if ( bCreateTriggers && OGRERR_NONE != SQLCommand(hDB, pszMetadataReferenceTriggers) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* FlushMetadata() */ +/************************************************************************/ + +CPLErr GDALGeoPackageDataset::FlushMetadata() +{ + if( !m_bMetadataDirty || m_poParentDS != NULL || + !CSLTestBoolean(CPLGetConfigOption("CREATE_METADATA_TABLES", "YES")) ) + return CE_None; + if( !HasMetadataTables() && !CreateMetadataTables() ) + return CE_Failure; + m_bMetadataDirty = FALSE; + + if( m_osRasterTable.size() ) + { + const char* pszIdentifier = GetMetadataItem("IDENTIFIER"); + const char* pszDescription = GetMetadataItem("DESCRIPTION"); + if( !m_bIdentifierAsCO && pszIdentifier != NULL && + pszIdentifier != m_osIdentifier ) + { + m_osIdentifier = pszIdentifier; + char* pszSQL = sqlite3_mprintf( + "UPDATE gpkg_contents SET identifier = '%q' WHERE table_name = '%q'", + pszIdentifier, m_osRasterTable.c_str()); + SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + } + if( !m_bDescriptionAsCO && pszDescription != NULL && + pszDescription != m_osDescription ) + { + m_osDescription = pszDescription; + char* pszSQL = sqlite3_mprintf( + "UPDATE gpkg_contents SET description = '%q' WHERE table_name = '%q'", + pszDescription, m_osRasterTable.c_str()); + SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + } + } + + char** papszMDDup = NULL; + for( char** papszIter = GetMetadata(); papszIter && *papszIter; ++papszIter ) + { + if( EQUALN(*papszIter, "IDENTIFIER=", strlen("IDENTIFIER=")) ) + continue; + if( EQUALN(*papszIter, "DESCRIPTION=", strlen("DESCRIPTION=")) ) + continue; + if( EQUALN(*papszIter, "ZOOM_LEVEL=", strlen("ZOOM_LEVEL=")) ) + continue; + if( EQUALN(*papszIter, "GPKG_METADATA_ITEM_", strlen("GPKG_METADATA_ITEM_")) ) + continue; + papszMDDup = CSLInsertString(papszMDDup, -1, *papszIter); + } + + CPLXMLNode* psXMLNode; + { + GDALMultiDomainMetadata oLocalMDMD; + char** papszDomainList = oMDMD.GetDomainList(); + char** papszIter = papszDomainList; + oLocalMDMD.SetMetadata(papszMDDup); + while( papszIter && *papszIter ) + { + if( !EQUAL(*papszIter, "") && + !EQUAL(*papszIter, "IMAGE_STRUCTURE") && + !EQUAL(*papszIter, "GEOPACKAGE") ) + oLocalMDMD.SetMetadata(oMDMD.GetMetadata(*papszIter), *papszIter); + papszIter ++; + } + psXMLNode = oLocalMDMD.Serialize(); + } + + CSLDestroy(papszMDDup); + papszMDDup = NULL; + + WriteMetadata(psXMLNode, m_osRasterTable.c_str() ); + + if( m_osRasterTable.size() ) + { + char** papszGeopackageMD = GetMetadata("GEOPACKAGE"); + + char** papszMDDup = NULL; + for( char** papszIter = papszGeopackageMD; papszIter && *papszIter; ++papszIter ) + { + papszMDDup = CSLInsertString(papszMDDup, -1, *papszIter); + } + + GDALMultiDomainMetadata oLocalMDMD; + oLocalMDMD.SetMetadata(papszMDDup); + CSLDestroy(papszMDDup); + papszMDDup = NULL; + psXMLNode = oLocalMDMD.Serialize(); + + WriteMetadata(psXMLNode, NULL); + } + + for(int i=0;iGetMetadataItem("IDENTIFIER"); + const char* pszDescription = m_papoLayers[i]->GetMetadataItem("DESCRIPTION"); + if( pszIdentifier != NULL ) + { + char* pszSQL = sqlite3_mprintf( + "UPDATE gpkg_contents SET identifier = '%q' WHERE table_name = '%q'", + pszIdentifier, m_papoLayers[i]->GetName()); + SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + } + if( pszDescription != NULL ) + { + char* pszSQL = sqlite3_mprintf( + "UPDATE gpkg_contents SET description = '%q' WHERE table_name = '%q'", + pszDescription, m_papoLayers[i]->GetName()); + SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + } + + char** papszMDDup = NULL; + for( char** papszIter = m_papoLayers[i]->GetMetadata(); papszIter && *papszIter; ++papszIter ) + { + if( EQUALN(*papszIter, "IDENTIFIER=", strlen("IDENTIFIER=")) ) + continue; + if( EQUALN(*papszIter, "DESCRIPTION=", strlen("DESCRIPTION=")) ) + continue; + if( EQUALN(*papszIter, "OLMD_FID64=", strlen("OLMD_FID64=")) ) + continue; + papszMDDup = CSLInsertString(papszMDDup, -1, *papszIter); + } + + CPLXMLNode* psXMLNode; + { + GDALMultiDomainMetadata oLocalMDMD; + char** papszDomainList = m_papoLayers[i]->GetMetadataDomainList(); + char** papszIter = papszDomainList; + oLocalMDMD.SetMetadata(papszMDDup); + while( papszIter && *papszIter ) + { + if( !EQUAL(*papszIter, "") ) + oLocalMDMD.SetMetadata(m_papoLayers[i]->GetMetadata(*papszIter), *papszIter); + papszIter ++; + } + CSLDestroy(papszDomainList); + psXMLNode = oLocalMDMD.Serialize(); + } + + CSLDestroy(papszMDDup); + papszMDDup = NULL; + + WriteMetadata(psXMLNode, m_papoLayers[i]->GetName() ); + } + + return CE_None; +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *GDALGeoPackageDataset::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + pszDomain = CheckMetadataDomain(pszDomain); + return CSLFetchNameValue( GetMetadata(pszDomain), pszName ); +} + +/************************************************************************/ +/* SetMetadata() */ +/************************************************************************/ + +CPLErr GDALGeoPackageDataset::SetMetadata( char ** papszMetadata, const char * pszDomain ) +{ + pszDomain = CheckMetadataDomain(pszDomain); + m_bMetadataDirty = TRUE; + GetMetadata(); /* force loading from storage if needed */ + return GDALPamDataset::SetMetadata(papszMetadata, pszDomain); +} + +/************************************************************************/ +/* SetMetadataItem() */ +/************************************************************************/ + +CPLErr GDALGeoPackageDataset::SetMetadataItem( const char * pszName, + const char * pszValue, + const char * pszDomain ) +{ + pszDomain = CheckMetadataDomain(pszDomain); + m_bMetadataDirty = TRUE; + GetMetadata(); /* force loading from storage if needed */ + return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain); +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int GDALGeoPackageDataset::Create( const char * pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eDT, + char **papszOptions ) +{ + CPLString osCommand; + const char *pszSpatialRefSysRecord; + + /* First, ensure there isn't any such file yet. */ + VSIStatBufL sStatBuf; + + if( nBands != 0 ) + { + if( eDT != GDT_Byte ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Only Byte supported"); + return FALSE; + } + if( nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Only 1 (Grey/ColorTable), 2 (Grey+Alpha), 3 (RGB) or 4 (RGBA) band dataset supported"); + return FALSE; + } + } + + int bFileExists = FALSE; + if( VSIStatL( pszFilename, &sStatBuf ) == 0 ) + { + bFileExists = TRUE; + if( nBands == 0 || + !CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "APPEND_SUBDATASET", "NO")) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "A file system object called '%s' already exists.", + pszFilename ); + + return FALSE; + } + } + m_pszFilename = CPLStrdup(pszFilename); + m_bNew = TRUE; + bUpdate = TRUE; + eAccess = GA_Update; /* hum annoying duplication */ + + if (!OpenOrCreateDB(bFileExists ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)) + return FALSE; + + /* Default to synchronous=off for performance for new file */ + if( !bFileExists && CPLGetConfigOption("OGR_SQLITE_SYNCHRONOUS", NULL) == NULL ) + { + sqlite3_exec( hDB, "PRAGMA synchronous = OFF", NULL, NULL, NULL ); + } + + /* OGR UTF-8 support. If we set the UTF-8 Pragma early on, it */ + /* will be written into the main file and supported henceforth */ + SQLCommand(hDB, "PRAGMA encoding = \"UTF-8\""); + + SoftStartTransaction(); + + int bCreateTriggers = CSLTestBoolean(CPLGetConfigOption("CREATE_TRIGGERS", "YES")); + int bCreateGeometryColumns = CSLTestBoolean(CPLGetConfigOption("CREATE_GEOMETRY_COLUMNS", "YES")); + if( !bFileExists ) + { + /* Requirement 2: A GeoPackage SHALL contain 0x47503130 ("GP10" in ASCII) in the application id */ + /* http://opengis.github.io/geopackage/#_file_format */ + const char *pszPragma = CPLSPrintf("PRAGMA application_id = %d", GPKG_APPLICATION_ID); + + if ( OGRERR_NONE != SQLCommand(hDB, pszPragma) ) + return FALSE; + + /* Requirement 10: A GeoPackage SHALL include a gpkg_spatial_ref_sys table */ + /* http://opengis.github.io/geopackage/#spatial_ref_sys */ + const char *pszSpatialRefSys = + "CREATE TABLE gpkg_spatial_ref_sys (" + "srs_name TEXT NOT NULL," + "srs_id INTEGER NOT NULL PRIMARY KEY," + "organization TEXT NOT NULL," + "organization_coordsys_id INTEGER NOT NULL," + "definition TEXT NOT NULL," + "description TEXT" + ")"; + + if ( OGRERR_NONE != SQLCommand(hDB, pszSpatialRefSys) ) + return FALSE; + + /* Requirement 11: The gpkg_spatial_ref_sys table in a GeoPackage SHALL */ + /* contain a record for EPSG:4326, the geodetic WGS84 SRS */ + /* http://opengis.github.io/geopackage/#spatial_ref_sys */ + pszSpatialRefSysRecord = + "INSERT INTO gpkg_spatial_ref_sys (" + "srs_name, srs_id, organization, organization_coordsys_id, definition, description" + ") VALUES (" + "'WGS 84 geodetic', 4326, 'EPSG', 4326, '" + "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]]" + "', 'longitude/latitude coordinates in decimal degrees on the WGS 84 spheroid'" + ")"; + + if ( OGRERR_NONE != SQLCommand(hDB, pszSpatialRefSysRecord) ) + return FALSE; + + /* Requirement 11: The gpkg_spatial_ref_sys table in a GeoPackage SHALL */ + /* contain a record with an srs_id of -1, an organization of “NONEâ€, */ + /* an organization_coordsys_id of -1, and definition “undefined†*/ + /* for undefined Cartesian coordinate reference systems */ + /* http://opengis.github.io/geopackage/#spatial_ref_sys */ + pszSpatialRefSysRecord = + "INSERT INTO gpkg_spatial_ref_sys (" + "srs_name, srs_id, organization, organization_coordsys_id, definition, description" + ") VALUES (" + "'Undefined cartesian SRS', -1, 'NONE', -1, 'undefined', 'undefined cartesian coordinate reference system'" + ")"; + + if ( OGRERR_NONE != SQLCommand(hDB, pszSpatialRefSysRecord) ) + return FALSE; + + /* Requirement 11: The gpkg_spatial_ref_sys table in a GeoPackage SHALL */ + /* contain a record with an srs_id of 0, an organization of “NONEâ€, */ + /* an organization_coordsys_id of 0, and definition “undefined†*/ + /* for undefined geographic coordinate reference systems */ + /* http://opengis.github.io/geopackage/#spatial_ref_sys */ + pszSpatialRefSysRecord = + "INSERT INTO gpkg_spatial_ref_sys (" + "srs_name, srs_id, organization, organization_coordsys_id, definition, description" + ") VALUES (" + "'Undefined geographic SRS', 0, 'NONE', 0, 'undefined', 'undefined geographic coordinate reference system'" + ")"; + + if ( OGRERR_NONE != SQLCommand(hDB, pszSpatialRefSysRecord) ) + return FALSE; + + /* Requirement 13: A GeoPackage file SHALL include a gpkg_contents table */ + /* http://opengis.github.io/geopackage/#_contents */ + const char *pszContents = + "CREATE TABLE gpkg_contents (" + "table_name TEXT NOT NULL PRIMARY KEY," + "data_type TEXT NOT NULL," + "identifier TEXT UNIQUE," + "description TEXT DEFAULT ''," + "last_change DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ',CURRENT_TIMESTAMP))," + "min_x DOUBLE, min_y DOUBLE," + "max_x DOUBLE, max_y DOUBLE," + "srs_id INTEGER," + "CONSTRAINT fk_gc_r_srs_id FOREIGN KEY (srs_id) REFERENCES gpkg_spatial_ref_sys(srs_id)" + ")"; + + if ( OGRERR_NONE != SQLCommand(hDB, pszContents) ) + return FALSE; + + /* Requirement 21: A GeoPackage with a gpkg_contents table row with a “features†*/ + /* data_type SHALL contain a gpkg_geometry_columns table or updateable view */ + /* http://opengis.github.io/geopackage/#_geometry_columns */ + const char *pszGeometryColumns = + "CREATE TABLE gpkg_geometry_columns (" + "table_name TEXT NOT NULL," + "column_name TEXT NOT NULL," + "geometry_type_name TEXT NOT NULL," + "srs_id INTEGER NOT NULL," + "z TINYINT NOT NULL," + "m TINYINT NOT NULL," + "CONSTRAINT pk_geom_cols PRIMARY KEY (table_name, column_name)," + "CONSTRAINT uk_gc_table_name UNIQUE (table_name)," + "CONSTRAINT fk_gc_tn FOREIGN KEY (table_name) REFERENCES gpkg_contents(table_name)," + "CONSTRAINT fk_gc_srs FOREIGN KEY (srs_id) REFERENCES gpkg_spatial_ref_sys (srs_id)" + ")"; + + if ( bCreateGeometryColumns && OGRERR_NONE != SQLCommand(hDB, pszGeometryColumns) ) + return FALSE; + + /* From C.5. gpkg_tile_matrix_set Table 28. gpkg_tile_matrix_set Table Creation SQL */ + const char *pszTileMatrixSet = + "CREATE TABLE gpkg_tile_matrix_set (" + "table_name TEXT NOT NULL PRIMARY KEY," + "srs_id INTEGER NOT NULL," + "min_x DOUBLE NOT NULL," + "min_y DOUBLE NOT NULL," + "max_x DOUBLE NOT NULL," + "max_y DOUBLE NOT NULL," + "CONSTRAINT fk_gtms_table_name FOREIGN KEY (table_name) REFERENCES gpkg_contents(table_name)," + "CONSTRAINT fk_gtms_srs FOREIGN KEY (srs_id) REFERENCES gpkg_spatial_ref_sys (srs_id)" + ")"; + + if ( OGRERR_NONE != SQLCommand(hDB, pszTileMatrixSet) ) + return FALSE; + + /* From C.6. gpkg_tile_matrix Table 29. gpkg_tile_matrix Table Creation SQL */ + const char *pszTileMatrix = + "CREATE TABLE gpkg_tile_matrix (" + "table_name TEXT NOT NULL," + "zoom_level INTEGER NOT NULL," + "matrix_width INTEGER NOT NULL," + "matrix_height INTEGER NOT NULL," + "tile_width INTEGER NOT NULL," + "tile_height INTEGER NOT NULL," + "pixel_x_size DOUBLE NOT NULL," + "pixel_y_size DOUBLE NOT NULL," + "CONSTRAINT pk_ttm PRIMARY KEY (table_name, zoom_level)," + "CONSTRAINT fk_tmm_table_name FOREIGN KEY (table_name) REFERENCES gpkg_contents(table_name)" + ")"; + + if ( OGRERR_NONE != SQLCommand(hDB, pszTileMatrix) ) + return FALSE; + + /* From D.1. gpkg_tile_matrix Table 39. gpkg_tile_matrix Trigger Definition SQL */ + const char* pszTileMatrixTrigger = + "CREATE TRIGGER 'gpkg_tile_matrix_zoom_level_insert' " + "BEFORE INSERT ON 'gpkg_tile_matrix' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table ''gpkg_tile_matrix'' violates constraint: zoom_level cannot be less than 0') " + "WHERE (NEW.zoom_level < 0); " + "END; " + "CREATE TRIGGER 'gpkg_tile_matrix_zoom_level_update' " + "BEFORE UPDATE of zoom_level ON 'gpkg_tile_matrix' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table ''gpkg_tile_matrix'' violates constraint: zoom_level cannot be less than 0') " + "WHERE (NEW.zoom_level < 0); " + "END; " + "CREATE TRIGGER 'gpkg_tile_matrix_matrix_width_insert' " + "BEFORE INSERT ON 'gpkg_tile_matrix' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table ''gpkg_tile_matrix'' violates constraint: matrix_width cannot be less than 1') " + "WHERE (NEW.matrix_width < 1); " + "END; " + "CREATE TRIGGER 'gpkg_tile_matrix_matrix_width_update' " + "BEFORE UPDATE OF matrix_width ON 'gpkg_tile_matrix' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table ''gpkg_tile_matrix'' violates constraint: matrix_width cannot be less than 1') " + "WHERE (NEW.matrix_width < 1); " + "END; " + "CREATE TRIGGER 'gpkg_tile_matrix_matrix_height_insert' " + "BEFORE INSERT ON 'gpkg_tile_matrix' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table ''gpkg_tile_matrix'' violates constraint: matrix_height cannot be less than 1') " + "WHERE (NEW.matrix_height < 1); " + "END; " + "CREATE TRIGGER 'gpkg_tile_matrix_matrix_height_update' " + "BEFORE UPDATE OF matrix_height ON 'gpkg_tile_matrix' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table ''gpkg_tile_matrix'' violates constraint: matrix_height cannot be less than 1') " + "WHERE (NEW.matrix_height < 1); " + "END; " + "CREATE TRIGGER 'gpkg_tile_matrix_pixel_x_size_insert' " + "BEFORE INSERT ON 'gpkg_tile_matrix' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table ''gpkg_tile_matrix'' violates constraint: pixel_x_size must be greater than 0') " + "WHERE NOT (NEW.pixel_x_size > 0); " + "END; " + "CREATE TRIGGER 'gpkg_tile_matrix_pixel_x_size_update' " + "BEFORE UPDATE OF pixel_x_size ON 'gpkg_tile_matrix' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table ''gpkg_tile_matrix'' violates constraint: pixel_x_size must be greater than 0') " + "WHERE NOT (NEW.pixel_x_size > 0); " + "END; " + "CREATE TRIGGER 'gpkg_tile_matrix_pixel_y_size_insert' " + "BEFORE INSERT ON 'gpkg_tile_matrix' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table ''gpkg_tile_matrix'' violates constraint: pixel_y_size must be greater than 0') " + "WHERE NOT (NEW.pixel_y_size > 0); " + "END; " + "CREATE TRIGGER 'gpkg_tile_matrix_pixel_y_size_update' " + "BEFORE UPDATE OF pixel_y_size ON 'gpkg_tile_matrix' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table ''gpkg_tile_matrix'' violates constraint: pixel_y_size must be greater than 0') " + "WHERE NOT (NEW.pixel_y_size > 0); " + "END;"; + if ( bCreateTriggers && OGRERR_NONE != SQLCommand(hDB, pszTileMatrixTrigger) ) + return FALSE; + + if( CSLTestBoolean(CPLGetConfigOption("CREATE_METADATA_TABLES", "YES")) && + !CreateMetadataTables() ) + return FALSE; + } + + if( nBands != 0 ) + { + const char* pszTableName = CPLGetBasename(m_pszFilename); + m_osRasterTable = CSLFetchNameValueDef(papszOptions, "RASTER_TABLE", pszTableName); + m_bIdentifierAsCO = CSLFetchNameValue(papszOptions, "RASTER_IDENTIFIER" ) != NULL; + m_osIdentifier = CSLFetchNameValueDef(papszOptions, "RASTER_IDENTIFIER", m_osRasterTable); + m_bDescriptionAsCO = CSLFetchNameValue(papszOptions, "RASTER_DESCRIPTION" ) != NULL; + m_osDescription = CSLFetchNameValueDef(papszOptions, "RASTER_DESCRIPTION", ""); + + /* From C.7. sample_tile_pyramid (Informative) Table 31. EXAMPLE: tiles table Create Table SQL (Informative) */ + char* pszSQL = sqlite3_mprintf("CREATE TABLE '%q' (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "zoom_level INTEGER NOT NULL," + "tile_column INTEGER NOT NULL," + "tile_row INTEGER NOT NULL," + "tile_data BLOB NOT NULL," + "UNIQUE (zoom_level, tile_column, tile_row)" + ")", m_osRasterTable.c_str()); + OGRErr eErr = SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + if ( OGRERR_NONE != eErr ) + return FALSE; + + /* From D.5. sample_tile_pyramid Table 43. tiles table Trigger Definition SQL */ + char* pszSQLTriggers = sqlite3_mprintf("CREATE TRIGGER '%q_zoom_insert' " + "BEFORE INSERT ON '%q' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table ''%q'' violates constraint: zoom_level not specified for table in gpkg_tile_matrix') " + "WHERE NOT (NEW.zoom_level IN (SELECT zoom_level FROM gpkg_tile_matrix WHERE table_name = '%q')) ; " + "END; " + "CREATE TRIGGER '%q_zoom_update' " + "BEFORE UPDATE OF zoom_level ON '%q' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table ''%q'' violates constraint: zoom_level not specified for table in gpkg_tile_matrix') " + "WHERE NOT (NEW.zoom_level IN (SELECT zoom_level FROM gpkg_tile_matrix WHERE table_name = '%q')) ; " + "END; " + "CREATE TRIGGER '%q_tile_column_insert' " + "BEFORE INSERT ON '%q' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table ''%q'' violates constraint: tile_column cannot be < 0') " + "WHERE (NEW.tile_column < 0) ; " + "SELECT RAISE(ABORT, 'insert on table ''%q'' violates constraint: tile_column must by < matrix_width specified for table and zoom level in gpkg_tile_matrix') " + "WHERE NOT (NEW.tile_column < (SELECT matrix_width FROM gpkg_tile_matrix WHERE table_name = '%q' AND zoom_level = NEW.zoom_level)); " + "END; " + "CREATE TRIGGER '%q_tile_column_update' " + "BEFORE UPDATE OF tile_column ON '%q' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table ''%q'' violates constraint: tile_column cannot be < 0') " + "WHERE (NEW.tile_column < 0) ; " + "SELECT RAISE(ABORT, 'update on table ''%q'' violates constraint: tile_column must by < matrix_width specified for table and zoom level in gpkg_tile_matrix') " + "WHERE NOT (NEW.tile_column < (SELECT matrix_width FROM gpkg_tile_matrix WHERE table_name = '%q' AND zoom_level = NEW.zoom_level)); " + "END; " + "CREATE TRIGGER '%q_tile_row_insert' " + "BEFORE INSERT ON '%q' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'insert on table ''%q'' violates constraint: tile_row cannot be < 0') " + "WHERE (NEW.tile_row < 0) ; " + "SELECT RAISE(ABORT, 'insert on table ''%q'' violates constraint: tile_row must by < matrix_height specified for table and zoom level in gpkg_tile_matrix') " + "WHERE NOT (NEW.tile_row < (SELECT matrix_height FROM gpkg_tile_matrix WHERE table_name = '%q' AND zoom_level = NEW.zoom_level)); " + "END; " + "CREATE TRIGGER '%q_tile_row_update' " + "BEFORE UPDATE OF tile_row ON '%q' " + "FOR EACH ROW BEGIN " + "SELECT RAISE(ABORT, 'update on table ''%q'' violates constraint: tile_row cannot be < 0') " + "WHERE (NEW.tile_row < 0) ; " + "SELECT RAISE(ABORT, 'update on table ''%q'' violates constraint: tile_row must by < matrix_height specified for table and zoom level in gpkg_tile_matrix') " + "WHERE NOT (NEW.tile_row < (SELECT matrix_height FROM gpkg_tile_matrix WHERE table_name = '%q' AND zoom_level = NEW.zoom_level)); " + "END; ", + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str(), + m_osRasterTable.c_str() + ); + if( bCreateTriggers ) + { + eErr = SQLCommand(hDB, pszSQLTriggers); + sqlite3_free(pszSQLTriggers); + if ( OGRERR_NONE != eErr ) + return FALSE; + } + + nRasterXSize = nXSize; + nRasterYSize = nYSize; + + const char* pszTileSize = CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", "256"); + const char* pszTileWidth = CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", pszTileSize); + const char* pszTileHeight = CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", pszTileSize); + int nTileWidth = atoi(pszTileWidth); + int nTileHeight = atoi(pszTileHeight); + if( (nTileWidth < 8 || nTileWidth > 4096 || nTileHeight < 8 || nTileHeight > 4096) && + !CSLTestBoolean(CPLGetConfigOption("GPKG_ALLOW_CRAZY_SETTINGS", "NO")) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid block dimensions: %dx%d", + nTileWidth, nTileHeight); + return FALSE; + } + + m_pabyCachedTiles = (GByte*) VSIMalloc3(4 * 4, nTileWidth, nTileHeight); + if( m_pabyCachedTiles == NULL ) + { + return FALSE; + } + + for(int i = 1; i <= nBands; i ++) + SetBand( i, new GDALGeoPackageRasterBand(this, i, nTileWidth, nTileHeight) ); + + GDALPamDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); + GDALPamDataset::SetMetadataItem("IDENTIFIER", m_osIdentifier); + if( m_osDescription.size() ) + GDALPamDataset::SetMetadataItem("DESCRIPTION", m_osDescription); + + const char* pszTF = CSLFetchNameValue(papszOptions, "TILE_FORMAT"); + if( pszTF ) + m_eTF = GetTileFormat(pszTF); + + ParseCompressionOptions(papszOptions); + + if( m_eTF == GPKG_TF_WEBP ) + { + if( !RegisterWebPExtension() ) + return FALSE; + } + + const char* pszTilingScheme = CSLFetchNameValue(papszOptions, "TILING_SCHEME"); + if( pszTilingScheme ) + { + m_osTilingScheme = pszTilingScheme; + int bFound = FALSE; + for(size_t iScheme = 0; + iScheme < sizeof(asTilingShemes)/sizeof(asTilingShemes[0]); + iScheme++ ) + { + if( EQUAL(m_osTilingScheme, asTilingShemes[iScheme].pszName) ) + { + if( nTileWidth != asTilingShemes[iScheme].nTileWidth || + nTileHeight != asTilingShemes[iScheme].nTileHeight ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Tile dimension should be %dx%d for %s tiling scheme", + asTilingShemes[iScheme].nTileWidth, + asTilingShemes[iScheme].nTileHeight, + m_osTilingScheme.c_str()); + return FALSE; + } + + /* Implicitely sets SRS */ + OGRSpatialReference oSRS; + if( oSRS.importFromEPSG(asTilingShemes[iScheme].nEPSGCode) != OGRERR_NONE ) + return FALSE; + char* pszWKT = NULL; + oSRS.exportToWkt(&pszWKT); + SetProjection(pszWKT); + CPLFree(pszWKT); + + bFound = TRUE; + break; + } + } + if( !bFound ) + m_osTilingScheme = "CUSTOM"; + } + } + + SoftCommitTransaction(); + + /* Requirement 2: A GeoPackage SHALL contain 0x47503130 ("GP10" in ASCII) */ + /* in the application id field of the SQLite database header */ + /* We have to do this after there's some content so the database file */ + /* is not zero length */ + SetApplicationId(); + + /* Default to synchronous=off for performance for new file */ + if( !bFileExists && CPLGetConfigOption("OGR_SQLITE_SYNCHRONOUS", NULL) == NULL ) + { + sqlite3_exec( hDB, "PRAGMA synchronous = OFF", NULL, NULL, NULL ); + } + + return TRUE; +} + +/************************************************************************/ +/* CreateCopy() */ +/************************************************************************/ + +typedef struct +{ + const char* pszName; + GDALResampleAlg eResampleAlg; +} WarpResamplingAlg; + +static const WarpResamplingAlg asResamplingAlg[] = +{ + { "BILINEAR", GRA_Bilinear }, + { "CUBIC", GRA_Cubic }, + { "CUBICSPLINE", GRA_CubicSpline }, + { "LANCZOS", GRA_Lanczos }, + { "MODE", GRA_Mode }, + { "AVERAGE", GRA_Average }, +}; + +GDALDataset* GDALGeoPackageDataset::CreateCopy( const char *pszFilename, + GDALDataset *poSrcDS, + int bStrict, + char ** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressData ) +{ + const char* pszTilingScheme = + CSLFetchNameValueDef(papszOptions, "TILING_SCHEME", "CUSTOM"); + + char** papszUpdatedOptions = CSLDuplicate(papszOptions); + if( CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "APPEND_SUBDATASET", "NO")) && + CSLFetchNameValue(papszOptions, "RASTER_TABLE") == NULL ) + { + papszUpdatedOptions = CSLSetNameValue(papszUpdatedOptions, + "RASTER_TABLE", + CPLGetBasename(poSrcDS->GetDescription())); + } + + if( EQUAL(pszTilingScheme, "CUSTOM") ) + { + GDALDriver* poThisDriver = (GDALDriver*)GDALGetDriverByName("GPKG"); + if( !poThisDriver ) + { + CSLDestroy(papszUpdatedOptions); + return NULL; + } + GDALDataset* poDS = poThisDriver->DefaultCreateCopy( + pszFilename, poSrcDS, bStrict, + papszUpdatedOptions, pfnProgress, pProgressData ); + CSLDestroy(papszUpdatedOptions); + return poDS; + } + + int nBands = poSrcDS->GetRasterCount(); + if( nBands != 1 && nBands != 2 && nBands != 3 && nBands != 4 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Only 1 (Grey/ColorTable), 2 (Grey+Alpha), 3 (RGB) or 4 (RGBA) band dataset supported"); + CSLDestroy(papszUpdatedOptions); + return NULL; + } + + int bFound = FALSE; + int nEPSGCode = 0; + size_t iScheme; + for(iScheme = 0; + iScheme < sizeof(asTilingShemes)/sizeof(asTilingShemes[0]); + iScheme++ ) + { + if( EQUAL(pszTilingScheme, asTilingShemes[iScheme].pszName) ) + { + nEPSGCode = asTilingShemes[iScheme].nEPSGCode; + bFound = TRUE; + break; + } + } + if( !bFound ) + { + CSLDestroy(papszUpdatedOptions); + return NULL; + } + + OGRSpatialReference oSRS; + if( oSRS.importFromEPSG(nEPSGCode) != OGRERR_NONE ) + { + CSLDestroy(papszUpdatedOptions); + return NULL; + } + char* pszWKT = NULL; + oSRS.exportToWkt(&pszWKT); + char** papszTO = CSLSetNameValue( NULL, "DST_SRS", pszWKT ); + void* hTransformArg = + GDALCreateGenImgProjTransformer2( poSrcDS, NULL, papszTO ); + if( hTransformArg == NULL ) + { + CSLDestroy(papszUpdatedOptions); + CPLFree(pszWKT); + CSLDestroy(papszTO); + return NULL; + } + + GDALTransformerInfo* psInfo = (GDALTransformerInfo*)hTransformArg; + double adfGeoTransform[6]; + double adfExtent[4]; + int nXSize, nYSize; + + if ( GDALSuggestedWarpOutput2( poSrcDS, + psInfo->pfnTransform, hTransformArg, + adfGeoTransform, + &nXSize, &nYSize, + adfExtent, 0 ) != CE_None ) + { + CSLDestroy(papszUpdatedOptions); + CPLFree(pszWKT); + CSLDestroy(papszTO); + GDALDestroyGenImgProjTransformer( hTransformArg ); + return NULL; + } + + GDALDestroyGenImgProjTransformer( hTransformArg ); + hTransformArg = NULL; + + int nZoomLevel; + double dfComputedRes = adfGeoTransform[1]; + double dfPrevRes = 0, dfRes = 0; + for(nZoomLevel = 0; nZoomLevel < 25; nZoomLevel++) + { + dfRes = asTilingShemes[iScheme].dfPixelXSizeZoomLevel0 / (1 << nZoomLevel); + if( dfComputedRes > dfRes ) + break; + dfPrevRes = dfRes; + } + if( nZoomLevel == 25 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Could not find an appropriate zoom level"); + CSLDestroy(papszUpdatedOptions); + CPLFree(pszWKT); + CSLDestroy(papszTO); + return NULL; + } + + const char* pszZoomLevelStrategy = CSLFetchNameValueDef(papszOptions, + "ZOOM_LEVEL_STRATEGY", + "AUTO"); + if( fabs( dfComputedRes - dfRes ) / dfRes > 1e-8 ) + { + if( EQUAL(pszZoomLevelStrategy, "LOWER") ) + { + if( nZoomLevel > 0 ) + nZoomLevel --; + } + else if( EQUAL(pszZoomLevelStrategy, "UPPER") ) + { + /* do nothing */ + } + else if( nZoomLevel > 0 ) + { + if( dfPrevRes / dfComputedRes < dfComputedRes / dfRes ) + nZoomLevel --; + } + } + + dfRes = asTilingShemes[iScheme].dfPixelXSizeZoomLevel0 / (1 << nZoomLevel); + + double dfMinX = adfExtent[0]; + double dfMinY = adfExtent[1]; + double dfMaxX = adfExtent[2]; + double dfMaxY = adfExtent[3]; + + nXSize = (int) ( 0.5 + ( dfMaxX - dfMinX ) / dfRes ); + nYSize = (int) ( 0.5 + ( dfMaxY - dfMinY ) / dfRes ); + adfGeoTransform[1] = dfRes; + adfGeoTransform[5] = -dfRes; + + int nTargetBands = nBands; + /* For grey level or RGB, if there's reprojection involved, add an alpha */ + /* channel */ + if( (nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() == NULL) || + nBands == 3 ) + { + OGRSpatialReference oSrcSRS; + oSrcSRS.SetFromUserInput(poSrcDS->GetProjectionRef()); + oSrcSRS.AutoIdentifyEPSG(); + if( oSrcSRS.GetAuthorityCode(NULL) == NULL || + atoi(oSrcSRS.GetAuthorityCode(NULL)) != nEPSGCode ) + { + nTargetBands ++; + } + } + + GDALGeoPackageDataset* poDS = new GDALGeoPackageDataset(); + if( !(poDS->Create( pszFilename, nXSize, nYSize, nTargetBands, GDT_Byte, + papszUpdatedOptions )) ) + { + delete poDS; + CSLDestroy(papszUpdatedOptions); + CPLFree(pszWKT); + CSLDestroy(papszTO); + return NULL; + } + CSLDestroy(papszUpdatedOptions); + papszUpdatedOptions = NULL; + poDS->SetGeoTransform(adfGeoTransform); + poDS->SetProjection(pszWKT); + CPLFree(pszWKT); + pszWKT = NULL; + + hTransformArg = + GDALCreateGenImgProjTransformer2( poSrcDS, poDS, papszTO ); + CSLDestroy(papszTO); + if( hTransformArg == NULL ) + { + delete poDS; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Warp the transformer with a linear approximator */ +/* -------------------------------------------------------------------- */ + hTransformArg = + GDALCreateApproxTransformer( GDALGenImgProjTransform, + hTransformArg, 0.125 ); + GDALApproxTransformerOwnsSubtransformer(hTransformArg, TRUE); + +/* -------------------------------------------------------------------- */ +/* Setup warp options. */ +/* -------------------------------------------------------------------- */ + GDALWarpOptions *psWO = GDALCreateWarpOptions(); + + psWO->papszWarpOptions = NULL; + psWO->eWorkingDataType = GDT_Byte; + + GDALResampleAlg eResampleAlg = GRA_Bilinear; + const char* pszResampling = CSLFetchNameValue(papszOptions, "RESAMPLING"); + if( pszResampling ) + { + for(size_t iAlg = 0; iAlg < sizeof(asResamplingAlg)/sizeof(asResamplingAlg[0]); iAlg ++) + { + if( EQUAL(pszResampling, asResamplingAlg[iAlg].pszName) ) + { + eResampleAlg = asResamplingAlg[iAlg].eResampleAlg; + break; + } + } + } + psWO->eResampleAlg = eResampleAlg; + + psWO->hSrcDS = poSrcDS; + psWO->hDstDS = poDS; + + psWO->pfnTransformer = GDALApproxTransform; + psWO->pTransformerArg = hTransformArg; + + psWO->pfnProgress = pfnProgress; + psWO->pProgressArg = pProgressData; + +/* -------------------------------------------------------------------- */ +/* Setup band mapping. */ +/* -------------------------------------------------------------------- */ + + if( nBands == 2 || nBands == 4 ) + psWO->nBandCount = nBands - 1; + else + psWO->nBandCount = nBands; + + psWO->panSrcBands = (int *) CPLMalloc(psWO->nBandCount*sizeof(int)); + psWO->panDstBands = (int *) CPLMalloc(psWO->nBandCount*sizeof(int)); + + for( int i = 0; i < psWO->nBandCount; i++ ) + { + psWO->panSrcBands[i] = i+1; + psWO->panDstBands[i] = i+1; + } + + if( nBands == 2 || nBands == 4 ) + { + psWO->nSrcAlphaBand = nBands; + } + if( nTargetBands == 2 || nTargetBands == 4 ) + { + psWO->nDstAlphaBand = nTargetBands; + } + +/* -------------------------------------------------------------------- */ +/* Initialize and execute the warp. */ +/* -------------------------------------------------------------------- */ + GDALWarpOperation oWO; + + CPLErr eErr = oWO.Initialize( psWO ); + if( eErr == CE_None ) + { + /*if( bMulti ) + eErr = oWO.ChunkAndWarpMulti( 0, 0, nXSize, nYSize ); + else*/ + eErr = oWO.ChunkAndWarpImage( 0, 0, nXSize, nYSize ); + } + if (eErr != CE_None) + { + delete poDS; + poDS = NULL; + } + + GDALDestroyTransformer( hTransformArg ); + GDALDestroyWarpOptions( psWO ); + + return poDS; +} + +/************************************************************************/ +/* ParseCompressionOptions() */ +/************************************************************************/ + +void GDALGeoPackageDataset::ParseCompressionOptions(char** papszOptions) +{ + const char* pszZLevel = CSLFetchNameValue(papszOptions, "ZLEVEL"); + if( pszZLevel ) + m_nZLevel = atoi(pszZLevel); + + const char* pszQuality = CSLFetchNameValue(papszOptions, "QUALITY"); + if( pszQuality ) + m_nQuality = atoi(pszQuality); + + const char* pszDither = CSLFetchNameValue(papszOptions, "DITHER"); + if( pszDither ) + m_bDither = CSLTestBoolean(pszDither); +} + +/************************************************************************/ +/* RegisterWebPExtension() */ +/************************************************************************/ + +int GDALGeoPackageDataset::RegisterWebPExtension() +{ + CreateExtensionsTableIfNecessary(); + + char* pszSQL = sqlite3_mprintf( + "INSERT INTO gpkg_extensions " + "(table_name, column_name, extension_name, definition, scope) " + "VALUES " + "('%q', 'tile_data', 'gpkg_webp', 'GeoPackage 1.0 Specification Annex P', 'read-write')", + m_osRasterTable.c_str()); + OGRErr eErr = SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + if ( OGRERR_NONE != eErr ) + return FALSE; + return TRUE; +} + +/************************************************************************/ +/* RegisterZoomOtherExtension() */ +/************************************************************************/ + +int GDALGeoPackageDataset::RegisterZoomOtherExtension() +{ + CreateExtensionsTableIfNecessary(); + + char* pszSQL = sqlite3_mprintf( + "INSERT INTO gpkg_extensions " + "(table_name, extension_name, definition, scope) " + "VALUES " + "('%q', 'gpkg_zoom_other', 'GeoPackage 1.0 Specification Annex O', 'read-write')", + m_osRasterTable.c_str()); + OGRErr eErr = SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + if ( OGRERR_NONE != eErr ) + return FALSE; + return TRUE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer* GDALGeoPackageDataset::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= m_nLayers ) + return NULL; + else + return m_papoLayers[iLayer]; +} + +/************************************************************************/ +/* ICreateLayer() */ +/* Options: */ +/* FID = primary key name */ +/* OVERWRITE = YES|NO, overwrite existing layer? */ +/* SPATIAL_INDEX = YES|NO, TBD */ +/************************************************************************/ + +OGRLayer* GDALGeoPackageDataset::ICreateLayer( const char * pszLayerName, + OGRSpatialReference * poSpatialRef, + OGRwkbGeometryType eGType, + char **papszOptions ) +{ + int iLayer; + +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( !bUpdate ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened read-only.\n" + "New layer %s cannot be created.\n", + m_pszFilename, pszLayerName ); + + return NULL; + } + + /* Read GEOMETRY_NAME option */ + const char* pszGeomColumnName = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME"); + if (pszGeomColumnName == NULL) /* deprecated name */ + pszGeomColumnName = CSLFetchNameValue(papszOptions, "GEOMETRY_COLUMN"); + if (pszGeomColumnName == NULL) + pszGeomColumnName = "geom"; + int bGeomNullable = CSLFetchBoolean(papszOptions, "GEOMETRY_NULLABLE", TRUE); + + /* Read FID option */ + const char* pszFIDColumnName = CSLFetchNameValue(papszOptions, "FID"); + if (pszFIDColumnName == NULL) + pszFIDColumnName = "fid"; + + if ( strspn(pszFIDColumnName, "`~!@#$%^&*()+-={}|[]\\:\";'<>?,./") > 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "The primary key (%s) name may not contain special characters or spaces", + pszFIDColumnName); + return NULL; + } + + /* Avoiding gpkg prefixes is not an official requirement, but seems wise */ + if (strncmp(pszLayerName, "gpkg", 4) == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "The layer name may not begin with 'gpkg' as it is a reserved geopackage prefix"); + return NULL; + } + + /* Pre-emptively try and avoid sqlite3 syntax errors due to */ + /* illegal characters */ + if ( strspn(pszLayerName, "`~!@#$%^&*()+-={}|[]\\:\";'<>?,./") > 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "The layer name may not contain special characters or spaces"); + return NULL; + } + + /* Check for any existing layers that already use this name */ + for( iLayer = 0; iLayer < m_nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName, m_papoLayers[iLayer]->GetName()) ) + { + const char *pszOverwrite = CSLFetchNameValue(papszOptions,"OVERWRITE"); + if( pszOverwrite != NULL && CSLTestBoolean(pszOverwrite) ) + { + DeleteLayer( iLayer ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + pszLayerName ); + return NULL; + } + } + } + + /* Create a blank layer. */ + OGRGeoPackageTableLayer *poLayer = new OGRGeoPackageTableLayer(this, pszLayerName); + + poLayer->SetCreationParameters( eGType, pszGeomColumnName, + bGeomNullable, + poSpatialRef, + pszFIDColumnName, + CSLFetchNameValue(papszOptions, "IDENTIFIER"), + CSLFetchNameValue(papszOptions, "DESCRIPTION") ); + + /* Should we create a spatial index ? */ + const char *pszSI = CSLFetchNameValue( papszOptions, "SPATIAL_INDEX" ); + int bCreateSpatialIndex = ( pszSI == NULL || CSLTestBoolean(pszSI) ); + if( eGType != wkbNone && bCreateSpatialIndex ) + { + poLayer->SetDeferedSpatialIndexCreation(TRUE); + } + + poLayer->SetPrecisionFlag( CSLFetchBoolean(papszOptions,"PRECISION",TRUE)); + poLayer->SetTruncateFieldsFlag( CSLFetchBoolean(papszOptions,"TRUNCATE_FIELDS",FALSE)); + + m_papoLayers = (OGRGeoPackageTableLayer**)CPLRealloc(m_papoLayers, sizeof(OGRGeoPackageTableLayer*) * (m_nLayers+1)); + m_papoLayers[m_nLayers++] = poLayer; + return poLayer; +} + + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +int GDALGeoPackageDataset::DeleteLayer( int iLayer ) +{ + char *pszSQL; + + if( !bUpdate || iLayer < 0 || iLayer >= m_nLayers ) + return OGRERR_FAILURE; + + CPLString osLayerName = m_papoLayers[iLayer]->GetLayerDefn()->GetName(); + + CPLDebug( "GPKG", "DeleteLayer(%s)", osLayerName.c_str() ); + + if( m_papoLayers[iLayer]->HasSpatialIndex() ) + m_papoLayers[iLayer]->DropSpatialIndex(); + + /* Delete the layer object and remove the gap in the layers list */ + delete m_papoLayers[iLayer]; + memmove( m_papoLayers + iLayer, m_papoLayers + iLayer + 1, + sizeof(void *) * (m_nLayers - iLayer - 1) ); + m_nLayers--; + + if (osLayerName.size() == 0) + return OGRERR_NONE; + + pszSQL = sqlite3_mprintf( + "DROP TABLE \"%s\"", + osLayerName.c_str()); + + SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + + pszSQL = sqlite3_mprintf( + "DELETE FROM gpkg_geometry_columns WHERE table_name = '%q'", + osLayerName.c_str()); + + SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + + pszSQL = sqlite3_mprintf( + "DELETE FROM gpkg_contents WHERE table_name = '%q'", + osLayerName.c_str()); + + SQLCommand(hDB, pszSQL); + sqlite3_free(pszSQL); + + return OGRERR_NONE; +} + + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int GDALGeoPackageDataset::TestCapability( const char * pszCap ) +{ + if ( EQUAL(pszCap,ODsCCreateLayer) || + EQUAL(pszCap,ODsCDeleteLayer) ) + { + return bUpdate; + } + else if( EQUAL(pszCap,ODsCCurveGeometries) ) + return TRUE; + return OGRSQLiteBaseDataSource::TestCapability(pszCap); +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +static const char* apszFuncsWithSideEffects[] = +{ + "CreateSpatialIndex", + "DisableSpatialIndex", +}; + +OGRLayer * GDALGeoPackageDataset::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ + m_bHasReadMetadataFromStorage = FALSE; + + FlushMetadata(); + for( int i = 0; i < m_nLayers; i++ ) + { + m_papoLayers[i]->RunDeferredCreationIfNecessary(); + m_papoLayers[i]->CreateSpatialIndexIfNecessary(); + } + + if( pszDialect != NULL && EQUAL(pszDialect,"OGRSQL") ) + return GDALDataset::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + else if( pszDialect != NULL && EQUAL(pszDialect,"INDIRECT_SQLITE") ) + return GDALDataset::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + "SQLITE" ); + +/* -------------------------------------------------------------------- */ +/* Prepare statement. */ +/* -------------------------------------------------------------------- */ + int rc; + sqlite3_stmt *hSQLStmt = NULL; + + CPLString osSQLCommand = pszSQLCommand; + + /* This will speed-up layer creation */ + /* ORDER BY are costly to evaluate and are not necessary to establish */ + /* the layer definition. */ + int bUseStatementForGetNextFeature = TRUE; + int bEmptyLayer = FALSE; + + if( osSQLCommand.ifind("SELECT ") == 0 && + osSQLCommand.ifind(" UNION ") == std::string::npos && + osSQLCommand.ifind(" INTERSECT ") == std::string::npos && + osSQLCommand.ifind(" EXCEPT ") == std::string::npos ) + { + size_t nOrderByPos = osSQLCommand.ifind(" ORDER BY "); + if( nOrderByPos != std::string::npos ) + { + osSQLCommand.resize(nOrderByPos); + bUseStatementForGetNextFeature = FALSE; + } + } + + rc = sqlite3_prepare( hDB, osSQLCommand.c_str(), osSQLCommand.size(), + &hSQLStmt, NULL ); + + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In ExecuteSQL(): sqlite3_prepare(%s):\n %s", + pszSQLCommand, sqlite3_errmsg(hDB) ); + + if( hSQLStmt != NULL ) + { + sqlite3_finalize( hSQLStmt ); + } + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we get a resultset? */ +/* -------------------------------------------------------------------- */ + rc = sqlite3_step( hSQLStmt ); + if( rc != SQLITE_ROW ) + { + if ( rc != SQLITE_DONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In ExecuteSQL(): sqlite3_step(%s):\n %s", + pszSQLCommand, sqlite3_errmsg(hDB) ); + + sqlite3_finalize( hSQLStmt ); + return NULL; + } + + if( EQUAL(pszSQLCommand, "VACUUM") ) + { + sqlite3_finalize( hSQLStmt ); + /* VACUUM rewrites the DB, so we need to reset the application id */ + SetApplicationId(); + return NULL; + } + + if( EQUALN(pszSQLCommand, "ALTER TABLE ", strlen("ALTER TABLE ")) ) + { + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + /* ALTER TABLE src_table RENAME TO dst_table */ + if( CSLCount(papszTokens) == 6 && EQUAL(papszTokens[3], "RENAME") && + EQUAL(papszTokens[4], "TO") ) + { + const char* pszSrcTableName = papszTokens[2]; + const char* pszDstTableName = papszTokens[5]; + OGRGeoPackageTableLayer* poSrcLayer = (OGRGeoPackageTableLayer*)GetLayerByName(pszSrcTableName); + if( poSrcLayer ) + { + poSrcLayer->RenameTo( pszDstTableName ); + } + } + CSLDestroy(papszTokens); + } + + if( !EQUALN(pszSQLCommand, "SELECT ", 7) ) + { + sqlite3_finalize( hSQLStmt ); + return NULL; + } + + bUseStatementForGetNextFeature = FALSE; + bEmptyLayer = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Special case for some functions which must be run */ +/* only once */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand,"SELECT ",7) ) + { + unsigned int i; + for(i=0;iSetSpatialFilter( 0, poSpatialFilter ); + + return poLayer; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void GDALGeoPackageDataset::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} + +/************************************************************************/ +/* HasExtensionsTable() */ +/************************************************************************/ + +int GDALGeoPackageDataset::HasExtensionsTable() +{ + SQLResult oResultTable; + OGRErr err = SQLQuery(hDB, + "SELECT * FROM sqlite_master WHERE name = 'gpkg_extensions' " + "AND type IN ('table', 'view')", &oResultTable); + int bHasExtensionsTable = ( err == OGRERR_NONE && oResultTable.nRowCount == 1 ); + SQLResultFree(&oResultTable); + return bHasExtensionsTable; +} + +/************************************************************************/ +/* CheckUnknownExtensions() */ +/************************************************************************/ + +void GDALGeoPackageDataset::CheckUnknownExtensions(int bCheckRasterTable) +{ + if( !HasExtensionsTable() ) + return; + + char* pszSQL; + if( !bCheckRasterTable) + pszSQL = sqlite3_mprintf( + "SELECT extension_name, definition, scope FROM gpkg_extensions WHERE table_name IS NULL AND extension_name != 'gdal_aspatial'"); + else + pszSQL = sqlite3_mprintf( + "SELECT extension_name, definition, scope FROM gpkg_extensions WHERE table_name = '%q'", + m_osRasterTable.c_str()); + + SQLResult oResultTable; + OGRErr err = SQLQuery(GetDB(), pszSQL, &oResultTable); + sqlite3_free(pszSQL); + if ( err == OGRERR_NONE && oResultTable.nRowCount > 0 ) + { + for(int i=0; iiDims == 0 && bNeedExtent ) + { + OGRGeometry *poGeom = GPkgGeometryToOGR(pabyBLOB, nBLOBLen, NULL); + if( poGeom == NULL || poGeom->IsEmpty() ) + { + sqlite3_result_null(pContext); + delete poGeom; + return FALSE; + } + OGREnvelope sEnvelope; + poGeom->getEnvelope(&sEnvelope); + psHeader->MinX = sEnvelope.MinX; + psHeader->MaxX = sEnvelope.MaxX; + psHeader->MinY = sEnvelope.MinY; + psHeader->MaxY = sEnvelope.MaxY; + delete poGeom; + } + return TRUE; +} + +/************************************************************************/ +/* OGRGeoPackageSTMinX() */ +/************************************************************************/ + +static +void OGRGeoPackageSTMinX(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + GPkgHeader sHeader; + if( !OGRGeoPackageGetHeader(pContext, argc, argv, &sHeader, TRUE) ) + return; + sqlite3_result_double( pContext, sHeader.MinX ); +} + +/************************************************************************/ +/* OGRGeoPackageSTMinY() */ +/************************************************************************/ + +static +void OGRGeoPackageSTMinY(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + GPkgHeader sHeader; + if( !OGRGeoPackageGetHeader(pContext, argc, argv, &sHeader, TRUE) ) + return; + sqlite3_result_double( pContext, sHeader.MinY ); +} + +/************************************************************************/ +/* OGRGeoPackageSTMaxX() */ +/************************************************************************/ + +static +void OGRGeoPackageSTMaxX(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + GPkgHeader sHeader; + if( !OGRGeoPackageGetHeader(pContext, argc, argv, &sHeader, TRUE) ) + return; + sqlite3_result_double( pContext, sHeader.MaxX ); +} + +/************************************************************************/ +/* OGRGeoPackageSTMaxY() */ +/************************************************************************/ + +static +void OGRGeoPackageSTMaxY(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + GPkgHeader sHeader; + if( !OGRGeoPackageGetHeader(pContext, argc, argv, &sHeader, TRUE) ) + return; + sqlite3_result_double( pContext, sHeader.MaxY ); +} + +/************************************************************************/ +/* OGRGeoPackageSTIsEmpty() */ +/************************************************************************/ + +static +void OGRGeoPackageSTIsEmpty(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + GPkgHeader sHeader; + if( !OGRGeoPackageGetHeader(pContext, argc, argv, &sHeader, FALSE) ) + return; + sqlite3_result_int( pContext, sHeader.bEmpty ); +} + +/************************************************************************/ +/* OGRGeoPackageSTGeometryType() */ +/************************************************************************/ + +static +void OGRGeoPackageSTGeometryType(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + GPkgHeader sHeader; + if( !OGRGeoPackageGetHeader(pContext, argc, argv, &sHeader, FALSE) ) + return; + + int nBLOBLen = sqlite3_value_bytes (argv[0]); + const GByte* pabyBLOB = (const GByte *) sqlite3_value_blob (argv[0]); + OGRBoolean bIs3D; + OGRwkbGeometryType eGeometryType; + if( nBLOBLen <= (int)sHeader.szHeader ) + { + sqlite3_result_null( pContext ); + return; + } + OGRErr err = OGRReadWKBGeometryType( (GByte*)pabyBLOB + sHeader.szHeader, + wkbVariantIso, &eGeometryType, &bIs3D ); + if( err != OGRERR_NONE ) + sqlite3_result_null( pContext ); + else + sqlite3_result_text( pContext, OGRToOGCGeomType(eGeometryType), -1, SQLITE_TRANSIENT ); +} + +/************************************************************************/ +/* OGRGeoPackageGPKGIsAssignable() */ +/************************************************************************/ + +static +void OGRGeoPackageGPKGIsAssignable(sqlite3_context* pContext, + CPL_UNUSED int argc, + sqlite3_value** argv) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_TEXT || + sqlite3_value_type (argv[1]) != SQLITE_TEXT ) + { + sqlite3_result_int( pContext, 0 ); + return; + } + + const char* pszExpected = (const char*)sqlite3_value_text(argv[0]); + const char* pszActual = (const char*)sqlite3_value_text(argv[1]); + int bIsAssignable = OGR_GT_IsSubClassOf( OGRFromOGCGeomType(pszActual), + OGRFromOGCGeomType(pszExpected) ); + sqlite3_result_int( pContext, bIsAssignable ); +} + +/************************************************************************/ +/* OGRGeoPackageSTSRID() */ +/************************************************************************/ + +static +void OGRGeoPackageSTSRID(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + GPkgHeader sHeader; + if( !OGRGeoPackageGetHeader(pContext, argc, argv, &sHeader, FALSE) ) + return; + sqlite3_result_int( pContext, sHeader.iSrsId ); +} + +/************************************************************************/ +/* OGRGeoPackageCreateSpatialIndex() */ +/************************************************************************/ + +static +void OGRGeoPackageCreateSpatialIndex(sqlite3_context* pContext, + CPL_UNUSED int argc, + sqlite3_value** argv) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_TEXT || + sqlite3_value_type (argv[1]) != SQLITE_TEXT ) + { + sqlite3_result_int( pContext, 0 ); + return; + } + + const char* pszTableName = (const char*)sqlite3_value_text(argv[0]); + const char* pszGeomName = (const char*)sqlite3_value_text(argv[1]); + GDALGeoPackageDataset* poDS = (GDALGeoPackageDataset* )sqlite3_user_data(pContext); + + OGRGeoPackageTableLayer* poLyr = (OGRGeoPackageTableLayer*)poDS->GetLayerByName(pszTableName); + if( poLyr == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer name"); + sqlite3_result_int( pContext, 0 ); + return; + } + if( !EQUAL(poLyr->GetGeometryColumn(), pszGeomName) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Unknown geometry column name"); + sqlite3_result_int( pContext, 0 ); + return; + } + + sqlite3_result_int( pContext, poLyr->CreateSpatialIndex() ); +} + +/************************************************************************/ +/* OGRGeoPackageDisableSpatialIndex() */ +/************************************************************************/ + +static +void OGRGeoPackageDisableSpatialIndex(sqlite3_context* pContext, + CPL_UNUSED int argc, + sqlite3_value** argv) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_TEXT || + sqlite3_value_type (argv[1]) != SQLITE_TEXT ) + { + sqlite3_result_int( pContext, 0 ); + return; + } + + const char* pszTableName = (const char*)sqlite3_value_text(argv[0]); + const char* pszGeomName = (const char*)sqlite3_value_text(argv[1]); + GDALGeoPackageDataset* poDS = (GDALGeoPackageDataset* )sqlite3_user_data(pContext); + + OGRGeoPackageTableLayer* poLyr = (OGRGeoPackageTableLayer*)poDS->GetLayerByName(pszTableName); + if( poLyr == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer name"); + sqlite3_result_int( pContext, 0 ); + return; + } + if( !EQUAL(poLyr->GetGeometryColumn(), pszGeomName) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Unknown geometry column name"); + sqlite3_result_int( pContext, 0 ); + return; + } + + sqlite3_result_int( pContext, poLyr->DropSpatialIndex(TRUE) ); +} + +/************************************************************************/ +/* GPKG_hstore_get_value() */ +/************************************************************************/ + +static +void GPKG_hstore_get_value(sqlite3_context* pContext, + CPL_UNUSED int argc, + sqlite3_value** argv) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_TEXT || + sqlite3_value_type (argv[1]) != SQLITE_TEXT ) + { + sqlite3_result_null (pContext); + return; + } + + const char* pszHStore = (const char*)sqlite3_value_text(argv[0]); + const char* pszSearchedKey = (const char*)sqlite3_value_text(argv[1]); + char* pszValue = OGRHStoreGetValue(pszHStore, pszSearchedKey); + if( pszValue != NULL ) + sqlite3_result_text( pContext, pszValue, -1, CPLFree ); + else + sqlite3_result_null( pContext ); +} + +/************************************************************************/ +/* GPKG_GDAL_GetMemFileFromBlob() */ +/************************************************************************/ + +static CPLString GPKG_GDAL_GetMemFileFromBlob(sqlite3_value** argv) +{ + int nBytes = sqlite3_value_bytes (argv[0]); + const GByte* pabyBLOB = (const GByte *) sqlite3_value_blob (argv[0]); + CPLString osMemFileName; + osMemFileName.Printf("/vsimem/GPKG_GDAL_GetMemFileFromBlob_%p", argv); + VSILFILE * fp = VSIFileFromMemBuffer( osMemFileName.c_str(), (GByte*)pabyBLOB, + nBytes, FALSE); + VSIFCloseL(fp); + return osMemFileName; +} + +/************************************************************************/ +/* GPKG_GDAL_GetMimeType() */ +/************************************************************************/ + +static +void GPKG_GDAL_GetMimeType(sqlite3_context* pContext, + CPL_UNUSED int argc, + sqlite3_value** argv) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_BLOB ) + { + sqlite3_result_null (pContext); + return; + } + + CPLString osMemFileName(GPKG_GDAL_GetMemFileFromBlob(argv)); + GDALDriver* poDriver = (GDALDriver*)GDALIdentifyDriver(osMemFileName, NULL); + if( poDriver != NULL ) + { + const char* pszRes; + if( EQUAL(poDriver->GetDescription(), "PNG") ) + pszRes = "image/png"; + else if( EQUAL(poDriver->GetDescription(), "JPEG") ) + pszRes = "image/jpeg"; + else if( EQUAL(poDriver->GetDescription(), "WEBP") ) + pszRes = "image/x-webp"; + else + pszRes = CPLSPrintf("gdal/%s", poDriver->GetDescription()); + sqlite3_result_text( pContext, pszRes, -1, SQLITE_TRANSIENT ); + } + else + sqlite3_result_null (pContext); + VSIUnlink(osMemFileName); +} + +/************************************************************************/ +/* GPKG_GDAL_GetBandCount() */ +/************************************************************************/ + +static +void GPKG_GDAL_GetBandCount(sqlite3_context* pContext, + CPL_UNUSED int argc, + sqlite3_value** argv) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_BLOB ) + { + sqlite3_result_null (pContext); + return; + } + + CPLString osMemFileName(GPKG_GDAL_GetMemFileFromBlob(argv)); + GDALDataset* poDS = (GDALDataset*) GDALOpenEx(osMemFileName, + GDAL_OF_RASTER | GDAL_OF_INTERNAL, + NULL, NULL, NULL); + if( poDS != NULL ) + { + sqlite3_result_int( pContext, poDS->GetRasterCount() ); + GDALClose( poDS ); + } + else + sqlite3_result_null (pContext); + VSIUnlink(osMemFileName); +} + +/************************************************************************/ +/* GPKG_GDAL_HasColorTable() */ +/************************************************************************/ + +static +void GPKG_GDAL_HasColorTable(sqlite3_context* pContext, + CPL_UNUSED int argc, + sqlite3_value** argv) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_BLOB ) + { + sqlite3_result_null (pContext); + return; + } + + CPLString osMemFileName(GPKG_GDAL_GetMemFileFromBlob(argv)); + GDALDataset* poDS = (GDALDataset*) GDALOpenEx(osMemFileName, + GDAL_OF_RASTER | GDAL_OF_INTERNAL, + NULL, NULL, NULL); + if( poDS != NULL ) + { + sqlite3_result_int( pContext, + poDS->GetRasterCount() == 1 && + poDS->GetRasterBand(1)->GetColorTable() != NULL ); + GDALClose( poDS ); + } + else + sqlite3_result_null (pContext); + VSIUnlink(osMemFileName); +} + +/************************************************************************/ +/* OpenOrCreateDB() */ +/************************************************************************/ + +int GDALGeoPackageDataset::OpenOrCreateDB(int flags) +{ + int bSuccess = OGRSQLiteBaseDataSource::OpenOrCreateDB(flags, FALSE); + if( !bSuccess ) + return FALSE; + +#ifdef SPATIALITE_412_OR_LATER + InitNewSpatialite(); + + // Enable Spatialite 4.3 "amphibious" mode, i.e. that spatialite functions + // that take geometries will accept GPKG encoded gometries without + // explicit conversion + sqlite3_exec(hDB, "SELECT EnableGpkgAmphibiousMode()", NULL, NULL, NULL); +#endif + + /* Used by RTree Spatial Index Extension */ + sqlite3_create_function(hDB, "ST_MinX", 1, SQLITE_ANY, NULL, + OGRGeoPackageSTMinX, NULL, NULL); + sqlite3_create_function(hDB, "ST_MinY", 1, SQLITE_ANY, NULL, + OGRGeoPackageSTMinY, NULL, NULL); + sqlite3_create_function(hDB, "ST_MaxX", 1, SQLITE_ANY, NULL, + OGRGeoPackageSTMaxX, NULL, NULL); + sqlite3_create_function(hDB, "ST_MaxY", 1, SQLITE_ANY, NULL, + OGRGeoPackageSTMaxY, NULL, NULL); + sqlite3_create_function(hDB, "ST_IsEmpty", 1, SQLITE_ANY, NULL, + OGRGeoPackageSTIsEmpty, NULL, NULL); + + /* Used by Geometry Type Triggers Extension */ + sqlite3_create_function(hDB, "ST_GeometryType", 1, SQLITE_ANY, NULL, + OGRGeoPackageSTGeometryType, NULL, NULL); + sqlite3_create_function(hDB, "GPKG_IsAssignable", 2, SQLITE_ANY, NULL, + OGRGeoPackageGPKGIsAssignable, NULL, NULL); + + /* Used by Geometry SRS ID Triggers Extension */ + sqlite3_create_function(hDB, "ST_SRID", 1, SQLITE_ANY, NULL, + OGRGeoPackageSTSRID, NULL, NULL); + + /* Spatialite-like functions */ + sqlite3_create_function(hDB, "CreateSpatialIndex", 2, SQLITE_ANY, this, + OGRGeoPackageCreateSpatialIndex, NULL, NULL); + sqlite3_create_function(hDB, "DisableSpatialIndex", 2, SQLITE_ANY, this, + OGRGeoPackageDisableSpatialIndex, NULL, NULL); + + // HSTORE functions + sqlite3_create_function(hDB, "hstore_get_value", 2, SQLITE_ANY, NULL, + GPKG_hstore_get_value, NULL, NULL); + + // Debug functions + if( CSLTestBoolean(CPLGetConfigOption("GPKG_DEBUG", "FALSE")) ) + { + sqlite3_create_function(hDB, "GDAL_GetMimeType", 1, SQLITE_ANY, NULL, + GPKG_GDAL_GetMimeType, NULL, NULL); + sqlite3_create_function(hDB, "GDAL_GetBandCount", 1, SQLITE_ANY, NULL, + GPKG_GDAL_GetBandCount, NULL, NULL); + sqlite3_create_function(hDB, "GDAL_HasColorTable", 1, SQLITE_ANY, NULL, + GPKG_GDAL_HasColorTable, NULL, NULL); + } + + return TRUE; +} + +/************************************************************************/ +/* GetLayerWithGetSpatialWhereByName() */ +/************************************************************************/ + +std::pair + GDALGeoPackageDataset::GetLayerWithGetSpatialWhereByName( const char* pszName ) +{ + OGRGeoPackageLayer* poRet = (OGRGeoPackageLayer*) GetLayerByName(pszName); + return std::pair(poRet, poRet); +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr GDALGeoPackageDataset::CommitTransaction() + +{ + if( nSoftTransactionLevel == 1 ) + { + FlushMetadata(); + for( int i = 0; i < m_nLayers; i++ ) + { + m_papoLayers[i]->RunDeferredCreationIfNecessary(); + } + } + + return OGRSQLiteBaseDataSource::CommitTransaction(); +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +OGRErr GDALGeoPackageDataset::RollbackTransaction() + +{ + if( nSoftTransactionLevel == 1 ) + { + FlushMetadata(); + for( int i = 0; i < m_nLayers; i++ ) + { + m_papoLayers[i]->RunDeferredCreationIfNecessary(); + m_papoLayers[i]->CreateSpatialIndexIfNecessary(); + m_papoLayers[i]->ResetReading(); + } + } + + return OGRSQLiteBaseDataSource::RollbackTransaction(); +} + +/************************************************************************/ +/* GetGeometryTypeString() */ +/************************************************************************/ + +const char* GDALGeoPackageDataset::GetGeometryTypeString(OGRwkbGeometryType eType) +{ + const char* pszGPKGGeomType = OGRToOGCGeomType(eType); + if( EQUAL(pszGPKGGeomType, "GEOMETRYCOLLECTION") && + CSLTestBoolean(CPLGetConfigOption("OGR_GPKG_GEOMCOLLECTION", "YES")) ) + { + pszGPKGGeomType = "GEOMCOLLECTION"; + } + return pszGPKGGeomType; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/ogrgeopackagedriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/ogrgeopackagedriver.cpp new file mode 100644 index 000000000..ac7a8aba0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpkg/ogrgeopackagedriver.cpp @@ -0,0 +1,243 @@ +/****************************************************************************** + * $Id$ + * + * Project: GeoPackage Translator + * Purpose: Implements GeoPackageDriver. + * Author: Paul Ramsey + * + ****************************************************************************** + * Copyright (c) 2013, Paul Ramsey + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geopackage.h" + +// g++ -g -Wall -fPIC -shared -o ogr_geopackage.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/gpkg ogr/ogrsf_frmts/gpkg/*.c* -L. -lgdal + + +/* "GP10" in ASCII bytes */ +static const char aGpkgId[4] = {0x47, 0x50, 0x31, 0x30}; +// static const size_t szGpkgIdPos = 68; + +/************************************************************************/ +/* OGRGeoPackageDriverIdentify() */ +/************************************************************************/ + +static int OGRGeoPackageDriverIdentify( GDALOpenInfo* poOpenInfo ) +{ + if( EQUALN(poOpenInfo->pszFilename, "GPKG:", 5) ) + return TRUE; + + /* Requirement 3: File name has to end in "gpkg" */ + /* http://opengis.github.io/geopackage/#_file_extension_name */ + if( !EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "GPKG") ) + return FALSE; + + /* Check that the filename exists and is a file */ + if( poOpenInfo->fpL == NULL) + return FALSE; + + if ( poOpenInfo->nHeaderBytes < 16 || + strncmp( (const char*)poOpenInfo->pabyHeader, "SQLite format 3", 15 ) != 0 ) + { + return FALSE; + } + + /* Requirement 2: A GeoPackage SHALL contain 0x47503130 ("GP10" in ASCII) */ + /* in the application id */ + /* http://opengis.github.io/geopackage/#_file_format */ + /* Be tolerant since some datasets don't actually follow that requirement */ + if( poOpenInfo->nHeaderBytes < 68 + 4 || + memcmp(poOpenInfo->pabyHeader + 68, aGpkgId, 4) != 0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, "GPKG: bad application_id on '%s'", + poOpenInfo->pszFilename); + } + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRGeoPackageDriverOpen( GDALOpenInfo* poOpenInfo ) +{ + if( !OGRGeoPackageDriverIdentify(poOpenInfo) ) + return NULL; + + GDALGeoPackageDataset *poDS = new GDALGeoPackageDataset(); + + if( !poDS->Open( poOpenInfo ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset* OGRGeoPackageDriverCreate( const char * pszFilename, + int nXSize, + int nYSize, + int nBands, + GDALDataType eDT, + char **papszOptions ) +{ + GDALGeoPackageDataset *poDS = new GDALGeoPackageDataset(); + + if( !poDS->Create( pszFilename, nXSize, nYSize, + nBands, eDT, papszOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +static CPLErr OGRGeoPackageDriverDelete( const char *pszFilename ) + +{ + if( VSIUnlink(pszFilename) == 0 ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* RegisterOGRGeoPackage() */ +/************************************************************************/ + +void RegisterOGRGeoPackage() +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GPKG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GPKG" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GeoPackage" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gpkg" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_geopackage.html" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte" ); + +#define COMPRESSION_OPTIONS \ +" " \ +"
  • +Reading the waypoints from a Garmin USB receiver : + +
    +ogrinfo -ro -al GPSBabel:garmin:usb:
    +
    + +
  • +Converting a shapefile to Magellan Mapsend format : + +
    +ogr2ogr -f GPSBabel GPSBabel:mapsend:out.mapsend in.shp
    +
    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/makefile.vc new file mode 100644 index 000000000..fc3a1b798 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/makefile.vc @@ -0,0 +1,16 @@ + +OBJ = ogrgpsbabeldriver.obj ogrgpsbabeldatasource.obj ogrgpsbabelwritedatasource.obj + +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/ogr_gpsbabel.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/ogr_gpsbabel.h new file mode 100644 index 000000000..1db5306b5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/ogr_gpsbabel.h @@ -0,0 +1,104 @@ +/****************************************************************************** + * $Id: ogr_gpsbabel.h 29172 2015-05-07 22:15:24Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for OGR/GPSBabel driver. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GPSBABEL_H_INCLUDED +#define _OGR_GPSBABEL_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_string.h" + +/************************************************************************/ +/* OGRGPSBabelDataSource */ +/************************************************************************/ + +class OGRGPSBabelDataSource : public OGRDataSource +{ + int nLayers; + OGRLayer* apoLayers[5]; + char *pszName; + char *pszGPSBabelDriverName; + char *pszFilename; + CPLString osTmpFileName; + GDALDataset *poGPXDS; + + public: + OGRGPSBabelDataSource(); + ~OGRGPSBabelDataSource(); + + virtual int CloseDependentDatasets(); + + virtual const char *GetName() { return pszName; } + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer *GetLayer( int ); + + virtual int TestCapability( const char * ); + + int Open ( const char* pszFilename, + const char* pszGPSBabelDriverNameIn, + char** papszOpenOptions ); + + static int IsSpecialFile(const char* pszFilename); + static int IsValidDriverName(const char* pszGPSBabelDriverName); +}; + + +/************************************************************************/ +/* OGRGPSBabelWriteDataSource */ +/************************************************************************/ + +class OGRGPSBabelWriteDataSource : public OGRDataSource +{ + char *pszName; + char *pszGPSBabelDriverName; + char *pszFilename; + CPLString osTmpFileName; + GDALDataset *poGPXDS; + + int Convert(); + + public: + OGRGPSBabelWriteDataSource(); + ~OGRGPSBabelWriteDataSource(); + + virtual const char *GetName() { return pszName; } + virtual int GetLayerCount(); + virtual OGRLayer *GetLayer( int ); + + virtual int TestCapability( const char * ); + + virtual OGRLayer *ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ); + + int Create ( const char* pszFilename, char **papszOptions ); +}; + +#endif /* ndef _OGR_GPSBABEL_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/ogrgpsbabeldatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/ogrgpsbabeldatasource.cpp new file mode 100644 index 000000000..7719727f0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/ogrgpsbabeldatasource.cpp @@ -0,0 +1,395 @@ +/****************************************************************************** + * $Id: ogrgpsbabeldatasource.cpp 29177 2015-05-09 19:29:02Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGPSBabelDataSource class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gpsbabel.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_error.h" +#include "cpl_spawn.h" + +#include + +CPL_CVSID("$Id: ogrgpsbabeldatasource.cpp 29177 2015-05-09 19:29:02Z rouault $"); + +/************************************************************************/ +/* OGRGPSBabelDataSource() */ +/************************************************************************/ + +OGRGPSBabelDataSource::OGRGPSBabelDataSource() + +{ + nLayers = 0; + pszName = NULL; + pszGPSBabelDriverName = NULL; + pszFilename = NULL; + poGPXDS = NULL; +} + +/************************************************************************/ +/* ~OGRGPSBabelDataSource() */ +/************************************************************************/ + +OGRGPSBabelDataSource::~OGRGPSBabelDataSource() + +{ + CPLFree(pszName); + CPLFree(pszGPSBabelDriverName); + CPLFree(pszFilename); + + CloseDependentDatasets(); + + if (osTmpFileName.size() > 0) + VSIUnlink(osTmpFileName.c_str()); +} + +/************************************************************************/ +/* CloseDependentDatasets() */ +/************************************************************************/ + +int OGRGPSBabelDataSource::CloseDependentDatasets() +{ + int bRet = FALSE; + if (poGPXDS) + { + bRet = TRUE; + GDALClose( (GDALDatasetH) poGPXDS ); + poGPXDS = NULL; + } + return bRet; +} + +/************************************************************************/ +/* GetArgv() */ +/************************************************************************/ + +static char** GetArgv(int bExplicitFeatures, int bWaypoints, int bRoutes, + int bTracks, const char* pszGPSBabelDriverName, + const char* pszFilename) +{ + char** argv = NULL; + argv = CSLAddString(argv, "gpsbabel"); + if (bExplicitFeatures) + { + if (bWaypoints) argv = CSLAddString(argv, "-w"); + if (bRoutes) argv = CSLAddString(argv, "-r"); + if (bTracks) argv = CSLAddString(argv, "-t"); + } + argv = CSLAddString(argv, "-i"); + argv = CSLAddString(argv, pszGPSBabelDriverName); + argv = CSLAddString(argv, "-f"); + argv = CSLAddString(argv, pszFilename); + argv = CSLAddString(argv, "-o"); + argv = CSLAddString(argv, "gpx,gpxver=1.1"); + argv = CSLAddString(argv, "-F"); + argv = CSLAddString(argv, "-"); + + return argv; +} + +/************************************************************************/ +/* IsSpecialFile() */ +/************************************************************************/ + +int OGRGPSBabelDataSource::IsSpecialFile(const char* pszFilename) +{ + return (strncmp(pszFilename, "/dev/", 5) == 0 || + strncmp(pszFilename, "usb:", 4) == 0 || + (strncmp(pszFilename, "COM", 3) == 0 && atoi(pszFilename + 3) > 0)); +} + +/************************************************************************/ +/* IsValidDriverName() */ +/************************************************************************/ + +int OGRGPSBabelDataSource::IsValidDriverName(const char* pszGPSBabelDriverName) +{ + int i; + for(i=0;pszGPSBabelDriverName[i] != '\0';i++) + { + char ch = pszGPSBabelDriverName[i]; + if (!((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || + (ch >= '0' && ch <= '9') || ch == '_' || ch == '=' || ch == '.' || ch == ',')) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid GPSBabel driver name"); + return FALSE; + } + } + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRGPSBabelDataSource::Open( const char * pszDatasourceName, + const char* pszGPSBabelDriverNameIn, + char** papszOpenOptions ) + +{ + int bExplicitFeatures = FALSE; + int bWaypoints = TRUE, bTracks = TRUE, bRoutes = TRUE; + + if (!EQUALN(pszDatasourceName, "GPSBABEL:", 9)) + { + CPLAssert(pszGPSBabelDriverNameIn); + pszGPSBabelDriverName = CPLStrdup(pszGPSBabelDriverNameIn); + pszFilename = CPLStrdup(pszDatasourceName); + } + else + { + if( CSLFetchNameValue(papszOpenOptions, "FILENAME") ) + pszFilename = CPLStrdup(CSLFetchNameValue(papszOpenOptions, + "FILENAME")); + + if( CSLFetchNameValue(papszOpenOptions, "GPSBABEL_DRIVER") ) + { + if( pszFilename == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Missing FILENAME"); + return FALSE; + } + + pszGPSBabelDriverName = CPLStrdup(CSLFetchNameValue(papszOpenOptions, + "DRIVER")); + + /* A bit of validation to avoid command line injection */ + if (!IsValidDriverName(pszGPSBabelDriverName)) + return FALSE; + } + } + + pszName = CPLStrdup( pszDatasourceName ); + + if (pszGPSBabelDriverName == NULL) + { + const char* pszSep = strchr(pszDatasourceName + 9, ':'); + if (pszSep == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Wrong syntax. Expected GPSBabel:driver_name:file_name"); + return FALSE; + } + + pszGPSBabelDriverName = CPLStrdup(pszDatasourceName + 9); + *(strchr(pszGPSBabelDriverName, ':')) = '\0'; + + /* A bit of validation to avoid command line injection */ + if (!IsValidDriverName(pszGPSBabelDriverName)) + return FALSE; + + /* Parse optionnal features= option */ + if (EQUALN(pszSep+1, "features=", 9)) + { + const char* pszNextSep = strchr(pszSep+1, ':'); + if (pszNextSep == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Wrong syntax. Expected GPSBabel:driver_name[,options]*:[features=waypoints,tracks,routes:]file_name"); + return FALSE; + } + + char* pszFeatures = CPLStrdup(pszSep+1+9); + *strchr(pszFeatures, ':') = 0; + char** papszTokens = CSLTokenizeString(pszFeatures); + char** papszIter = papszTokens; + int bErr = FALSE; + bExplicitFeatures = TRUE; + bWaypoints = bTracks = bRoutes = FALSE; + while(papszIter && *papszIter) + { + if (EQUAL(*papszIter, "waypoints")) + bWaypoints = TRUE; + else if (EQUAL(*papszIter, "tracks")) + bTracks = TRUE; + else if (EQUAL(*papszIter, "routes")) + bRoutes = TRUE; + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong value for 'features' options"); + bErr = TRUE; + } + papszIter ++; + } + CSLDestroy(papszTokens); + CPLFree(pszFeatures); + + if (bErr) + return FALSE; + + pszSep = pszNextSep; + } + + if( pszFilename == NULL ) + pszFilename = CPLStrdup(pszSep+1); + } + + const char* pszOptionUseTempFile = CPLGetConfigOption("USE_TEMPFILE", NULL); + if (pszOptionUseTempFile && CSLTestBoolean(pszOptionUseTempFile)) + osTmpFileName = CPLGenerateTempFilename(NULL); + else + osTmpFileName.Printf("/vsimem/ogrgpsbabeldatasource_%p", this); + + int bRet = FALSE; + if (IsSpecialFile(pszFilename)) + { + /* Special file : don't try to open it */ + char** argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes, + bTracks, pszGPSBabelDriverName, pszFilename); + VSILFILE* tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb"); + bRet = (CPLSpawn(argv, NULL, tmpfp, TRUE) == 0); + VSIFCloseL(tmpfp); + tmpfp = NULL; + CSLDestroy(argv); + argv = NULL; + } + else + { + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot open file %s", pszFilename); + return FALSE; + } + + char** argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes, + bTracks, pszGPSBabelDriverName, "-"); + + VSILFILE* tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb"); + + CPLPushErrorHandler(CPLQuietErrorHandler); + bRet = (CPLSpawn(argv, fp, tmpfp, TRUE) == 0); + CPLPopErrorHandler(); + + CSLDestroy(argv); + argv = NULL; + + CPLErr nLastErrorType = CPLGetLastErrorType(); + int nLastErrorNo = CPLGetLastErrorNo(); + CPLString osLastErrorMsg = CPLGetLastErrorMsg(); + + VSIFCloseL(tmpfp); + tmpfp = NULL; + + VSIFCloseL(fp); + fp = NULL; + + if (!bRet) + { + if (strstr(osLastErrorMsg.c_str(), "This format cannot be used in piped commands") == NULL) + { + CPLError(nLastErrorType, nLastErrorNo, "%s", osLastErrorMsg.c_str()); + } + else + { + VSIStatBuf sStatBuf; + if (VSIStat(pszFilename, &sStatBuf) != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Driver %s only supports real (non virtual) files", pszGPSBabelDriverName); + return FALSE; + } + + /* Try without piping in */ + argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes, + bTracks, pszGPSBabelDriverName, pszFilename); + tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb"); + bRet = (CPLSpawn(argv, NULL, tmpfp, TRUE) == 0); + VSIFCloseL(tmpfp); + tmpfp = NULL; + + CSLDestroy(argv); + argv = NULL; + } + } + } + + + if (bRet) + { + poGPXDS = (GDALDataset*) GDALOpenEx(osTmpFileName.c_str(), + GDAL_OF_VECTOR, NULL, NULL, NULL); + if (poGPXDS) + { + OGRLayer* poLayer; + + if (bWaypoints) + { + poLayer = poGPXDS->GetLayerByName("waypoints"); + if (poLayer != NULL && poLayer->GetFeatureCount() != 0) + apoLayers[nLayers++] = poLayer; + } + + if (bRoutes) + { + poLayer = poGPXDS->GetLayerByName("routes"); + if (poLayer != NULL && poLayer->GetFeatureCount() != 0) + apoLayers[nLayers++] = poLayer; + poLayer = poGPXDS->GetLayerByName("route_points"); + if (poLayer != NULL && poLayer->GetFeatureCount() != 0) + apoLayers[nLayers++] = poLayer; + } + + if (bTracks) + { + poLayer = poGPXDS->GetLayerByName("tracks"); + if (poLayer != NULL && poLayer->GetFeatureCount() != 0) + apoLayers[nLayers++] = poLayer; + poLayer = poGPXDS->GetLayerByName("track_points"); + if (poLayer != NULL && poLayer->GetFeatureCount() != 0) + apoLayers[nLayers++] = poLayer; + } + } + } + + return nLayers > 0; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGPSBabelDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRGPSBabelDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return apoLayers[iLayer]; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/ogrgpsbabeldriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/ogrgpsbabeldriver.cpp new file mode 100644 index 000000000..8f6678c2e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/ogrgpsbabeldriver.cpp @@ -0,0 +1,214 @@ +/****************************************************************************** + * $Id$ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGPSbabelDriver class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gpsbabel.h" +#include "cpl_conv.h" +#include "cpl_spawn.h" + +// g++ -g -Wall -fPIC ogr/ogrsf_frmts/gpsbabel/*.cpp -shared -o ogr_GPSBabel.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/gpsbabel -L. -lgdal + +CPL_CVSID("$Id$"); + +/************************************************************************/ +/* OGRGPSBabelDriverIdentify() */ +/************************************************************************/ + +static int OGRGPSBabelDriverIdentifyInternal( GDALOpenInfo* poOpenInfo, + const char** ppszGSPBabelDriverName ) +{ + if( EQUALN(poOpenInfo->pszFilename, "GPSBABEL:", strlen("GPSBABEL:")) ) + return TRUE; + + const char* pszGPSBabelDriverName = NULL; + if( poOpenInfo->fpL == NULL ) + return FALSE; + + if (memcmp(poOpenInfo->pabyHeader, "MsRcd", 5) == 0) + pszGPSBabelDriverName = "mapsource"; + else if (memcmp(poOpenInfo->pabyHeader, "MsRcf", 5) == 0) + pszGPSBabelDriverName = "gdb"; + else if (strstr((const char*)poOpenInfo->pabyHeader, "pabyHeader, "$GPGSA") != NULL || + strstr((const char*)poOpenInfo->pabyHeader, "$GPGGA") != NULL) + pszGPSBabelDriverName = "nmea"; + else if (EQUALN((const char*)poOpenInfo->pabyHeader, "OziExplorer",11)) + pszGPSBabelDriverName = "ozi"; + else if (strstr((const char*)poOpenInfo->pabyHeader, "Grid") && + strstr((const char*)poOpenInfo->pabyHeader, "Datum") && + strstr((const char*)poOpenInfo->pabyHeader, "Header")) + pszGPSBabelDriverName = "garmin_txt"; + else if (poOpenInfo->pabyHeader[0] == 13 && poOpenInfo->pabyHeader[10] == 'M' && poOpenInfo->pabyHeader[11] == 'S' && + (poOpenInfo->pabyHeader[12] >= '0' && poOpenInfo->pabyHeader[12] <= '9') && + (poOpenInfo->pabyHeader[13] >= '0' && poOpenInfo->pabyHeader[13] <= '9') && + poOpenInfo->pabyHeader[12] * 10 + poOpenInfo->pabyHeader[13] >= 30 && + (poOpenInfo->pabyHeader[14] == 1 || poOpenInfo->pabyHeader[14] == 2) && poOpenInfo->pabyHeader[15] == 0 && + poOpenInfo->pabyHeader[16] == 0 && poOpenInfo->pabyHeader[17] == 0) + pszGPSBabelDriverName = "mapsend"; + else if (strstr((const char*)poOpenInfo->pabyHeader, "$PMGNWPL") != NULL || + strstr((const char*)poOpenInfo->pabyHeader, "$PMGNRTE") != NULL) + pszGPSBabelDriverName = "magellan"; + else if (poOpenInfo->pabyHeader[0] == 'A' && + poOpenInfo->pabyHeader[1] >= 'A' && poOpenInfo->pabyHeader[1] <= 'Z' && + poOpenInfo->pabyHeader[2] >= 'A' && poOpenInfo->pabyHeader[2] <= 'Z' && + poOpenInfo->pabyHeader[3] >= 'A' && poOpenInfo->pabyHeader[3] <= 'Z' && + EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "igc") ) + pszGPSBabelDriverName = "igc"; + + static int bGPSBabelFound = -1; + if( pszGPSBabelDriverName != NULL && bGPSBabelFound < 0 ) + { +#ifndef WIN32 + VSIStatBufL sStat; + bGPSBabelFound = VSIStatL("/usr/bin/gpsbabel", &sStat) == 0; + if( !bGPSBabelFound ) +#endif + { + const char* const apszArgs[] = { "gpsbabel", "-V", NULL }; + CPLString osTmpFileName("/vsimem/gpsbabel_tmp.tmp"); + VSILFILE* tmpfp = VSIFOpenL(osTmpFileName, "wb"); + bGPSBabelFound = (CPLSpawn(apszArgs, NULL, tmpfp, FALSE) == 0); + VSIFCloseL(tmpfp); + VSIUnlink(osTmpFileName); + } + } + + if( bGPSBabelFound ) + *ppszGSPBabelDriverName = pszGPSBabelDriverName; + return ( *ppszGSPBabelDriverName != NULL ); +} + +static int OGRGPSBabelDriverIdentify( GDALOpenInfo* poOpenInfo ) +{ + const char* pszGPSBabelDriverName = NULL; + return OGRGPSBabelDriverIdentifyInternal(poOpenInfo, &pszGPSBabelDriverName); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRGPSBabelDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + const char* pszGPSBabelDriverName = NULL; + if (poOpenInfo->eAccess == GA_Update || + !OGRGPSBabelDriverIdentifyInternal(poOpenInfo, &pszGPSBabelDriverName)) + return NULL; + + OGRGPSBabelDataSource *poDS = new OGRGPSBabelDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, pszGPSBabelDriverName, + poOpenInfo->papszOpenOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRGPSBabelDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + OGRGPSBabelWriteDataSource *poDS = new OGRGPSBabelWriteDataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +static CPLErr OGRGPSBabelDriverDelete( const char *pszFilename ) + +{ + if( VSIUnlink( pszFilename ) == 0 ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* RegisterOGRGPSBabel() */ +/************************************************************************/ + +void RegisterOGRGPSBabel() +{ + if (! GDAL_CHECK_VERSION("OGR/GPSBabel driver")) + return; + + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GPSBabel" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GPSBabel" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GPSBabel" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_gpsbabel.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CONNECTION_PREFIX, "GPSBABEL:" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" "); + + poDriver->pfnOpen = OGRGPSBabelDriverOpen; + poDriver->pfnIdentify = OGRGPSBabelDriverIdentify; + poDriver->pfnCreate = OGRGPSBabelDriverCreate; + poDriver->pfnDelete = OGRGPSBabelDriverDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/ogrgpsbabelwritedatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/ogrgpsbabelwritedatasource.cpp new file mode 100644 index 000000000..745f47cf0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpsbabel/ogrgpsbabelwritedatasource.cpp @@ -0,0 +1,239 @@ +/****************************************************************************** + * $Id: ogrgpsbabelwritedatasource.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGPSBabelWriteDataSource class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ogr_gpsbabel.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_error.h" +#include "cpl_spawn.h" + +CPL_CVSID("$Id: ogrgpsbabelwritedatasource.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* OGRGPSBabelWriteDataSource() */ +/************************************************************************/ + +OGRGPSBabelWriteDataSource::OGRGPSBabelWriteDataSource() + +{ + pszName = NULL; + pszGPSBabelDriverName = NULL; + pszFilename = NULL; + poGPXDS = NULL; +} + +/************************************************************************/ +/* ~OGRGPSBabelWriteDataSource() */ +/************************************************************************/ + +OGRGPSBabelWriteDataSource::~OGRGPSBabelWriteDataSource() + +{ + if (poGPXDS) + GDALClose( (GDALDatasetH) poGPXDS ); + + Convert(); + + CPLFree(pszName); + CPLFree(pszGPSBabelDriverName); + CPLFree(pszFilename); +} + +/************************************************************************/ +/* Convert() */ +/************************************************************************/ + +int OGRGPSBabelWriteDataSource::Convert() +{ + int nRet = -1; + if (osTmpFileName.size() > 0 && pszFilename != NULL && pszGPSBabelDriverName != NULL) + { + if (OGRGPSBabelDataSource::IsSpecialFile(pszFilename)) + { + /* Special file : don't try to open it */ + const char* const argv[] = { "gpsbabel", "-i", "gpx", "-f", "-", + "-o", pszGPSBabelDriverName, "-F", pszFilename, NULL }; + VSILFILE* tmpfp = VSIFOpenL(osTmpFileName.c_str(), "rb"); + if (tmpfp) + { + nRet = CPLSpawn(argv, tmpfp, NULL, TRUE); + + VSIFCloseL(tmpfp); + tmpfp = NULL; + } + } + else + { + VSILFILE* fp = VSIFOpenL(pszFilename, "wb"); + if (fp == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot open file %s", pszFilename); + } + else + { + const char* const argv[] = { "gpsbabel", "-i", "gpx", "-f", "-", + "-o", pszGPSBabelDriverName, "-F", "-", NULL }; + VSILFILE* tmpfp = VSIFOpenL(osTmpFileName.c_str(), "rb"); + if (tmpfp) + { + nRet = CPLSpawn(argv, tmpfp, fp, TRUE); + + VSIFCloseL(tmpfp); + tmpfp = NULL; + } + + VSIFCloseL(fp); + fp = NULL; + } + } + + VSIUnlink(osTmpFileName.c_str()); + osTmpFileName = ""; + } + + return nRet == 0; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRGPSBabelWriteDataSource::Create( const char * pszName, + char **papszOptions ) +{ + GDALDriver* poGPXDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("GPX"); + if (poGPXDriver == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "GPX driver is necessary for GPSBabel write support"); + return FALSE; + } + + if (!EQUALN(pszName, "GPSBABEL:", 9)) + { + const char* pszOptionGPSBabelDriverName = + CSLFetchNameValue(papszOptions, "GPSBABEL_DRIVER"); + if (pszOptionGPSBabelDriverName != NULL) + pszGPSBabelDriverName = CPLStrdup(pszOptionGPSBabelDriverName); + else + { + CPLError(CE_Failure, CPLE_AppDefined, "GPSBABEL_DRIVER dataset creation option expected"); + return FALSE; + } + + pszFilename = CPLStrdup(pszName); + } + else + { + const char* pszSep = strchr(pszName + 9, ':'); + if (pszSep == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Wrong syntax. Expected GPSBabel:driver_name[,options]*:file_name"); + return FALSE; + } + + pszGPSBabelDriverName = CPLStrdup(pszName + 9); + *(strchr(pszGPSBabelDriverName, ':')) = '\0'; + + pszFilename = CPLStrdup(pszSep+1); + } + + /* A bit of validation to avoid command line injection */ + if (!OGRGPSBabelDataSource::IsValidDriverName(pszGPSBabelDriverName)) + return FALSE; + + const char* pszOptionUseTempFile = CSLFetchNameValue(papszOptions, "USE_TEMPFILE"); + if (pszOptionUseTempFile == NULL) + pszOptionUseTempFile = CPLGetConfigOption("USE_TEMPFILE", NULL); + if (pszOptionUseTempFile && CSLTestBoolean(pszOptionUseTempFile)) + osTmpFileName = CPLGenerateTempFilename(NULL); + else + osTmpFileName.Printf("/vsimem/ogrgpsbabeldatasource_%p", this); + + poGPXDS = poGPXDriver->Create(osTmpFileName.c_str(), 0, 0, 0, GDT_Unknown, papszOptions); + if (poGPXDS == NULL) + return FALSE; + + this->pszName = CPLStrdup(pszName); + + return TRUE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * OGRGPSBabelWriteDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ) +{ + if (poGPXDS) + return poGPXDS->CreateLayer(pszLayerName, poSRS, eType, papszOptions); + return NULL; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGPSBabelWriteDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRGPSBabelWriteDataSource::GetLayer( int iLayer ) + +{ + if (poGPXDS) + return poGPXDS->GetLayer(iLayer); + return NULL; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int OGRGPSBabelWriteDataSource::GetLayerCount() + +{ + if (poGPXDS) + return poGPXDS->GetLayerCount(); + return 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/GNUmakefile new file mode 100644 index 000000000..35b14ea4a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/GNUmakefile @@ -0,0 +1,19 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrgpxdriver.o ogrgpxdatasource.o ogrgpxlayer.o + +ifeq ($(HAVE_EXPAT),yes) +CPPFLAGS += -DHAVE_EXPAT +endif + +CPPFLAGS := -I.. -I../.. $(EXPAT_INCLUDE) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_gpx.h + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/drv_gpx.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/drv_gpx.html new file mode 100644 index 000000000..4b3c80cf2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/drv_gpx.html @@ -0,0 +1,290 @@ + + +GPX - GPS Exchange Format + + + + +

    GPX - GPS Exchange Format

    + +(Starting with GDAL 1.5.0)

    + +GPX (the GPS Exchange Format) is a light-weight XML data format for the interchange of GPS data (waypoints, routes, and tracks) between applications and Web services on the Internet.

    + +OGR has support for GPX reading (if GDAL is build with expat library support) and writing.

    + +Version supported are GPX 1.0 and 1.1 for reading, GPX 1.1 for writing.

    + +The OGR driver supports reading and writing of all the GPX feature types : +

      +
    • waypoints : layer of features of OGR type wkbPoint
    • +
    • routes : layer of features of OGR type wkbLineString
    • +
    • tracks : layer of features of OGR type wkbMultiLineString
    • +
    + +It also supports reading of route points and track points in standalone layers (route_points and track_points), so +that their own attributes can be used by OGR.

    + +In addition to its GPX attributes, each route point of a route has a route_fid +(foreign key to the FID of its belonging route) +and a route_point_id which is its sequence number in the route.
    +The same applies for track points with track_fid, track_seg_id and track_seg_point_id. + +All coordinates are relative to the WGS84 datum (EPSG:4326).

    + +If the environment variable GPX_ELE_AS_25D is set to YES, the elevation element will +be used to set the Z coordinates of waypoints, route points and track points.

    + +The OGR/GPX reads and writes the GPX attributes for the waypoints, routes and tracks.

    + +By default, up to 2 <link> elements can be taken into account by feature. +This default number can be changed with the GPX_N_MAX_LINKS environment variable.

    + +

    Encoding issues

    + +Expat library supports reading the following built-in encodings : +
      +
    • US-ASCII
    • +
    • UTF-8
    • +
    • UTF-16
    • +
    • ISO-8859-1
    • +
    + +OGR 1.8.0 adds supports for Windows-1252 encoding (for previous versions, altering the encoding +mentionned in the XML header to ISO-8859-1 might work in some cases).

    + +The content returned by OGR will be encoded in UTF-8, after the conversion from the +encoding mentionned in the file header is.

    + +If your GPX file is not encoded in one of the previous encodings, it will not be parsed by the +GPX driver. You may convert it into one of the supported encoding with the iconv utility +for example and change accordingly the encoding parameter value in the XML header.
    +

    + +When writing a GPX file, the driver expects UTF-8 content to be passed in.

    + +

    Extensions element reading

    + +If the <extensions> element is detected in a GPX file, OGR will expose the content +of its sub elements as fields. Complex content of sub elements will be exposed as an XML blob. +

    +The following sequence GPX content : +

    +    <extensions>
    +        <navaid:name>TOTAL RF</navaid:name>
    +        <navaid:address>BENSALEM</navaid:address>
    +        <navaid:state>PA</navaid:state>
    +        <navaid:country>US</navaid:country>
    +        <navaid:frequencies>
    +        <navaid:frequency type="CTAF" frequency="122.900" name="CTAF"/>
    +        </navaid:frequencies>
    +        <navaid:runways>
    +        <navaid:runway designation="H1" length="80" width="80" surface="ASPH-G">
    +        </navaid:runway>
    +        </navaid:runways>
    +        <navaid:magvar>12</navaid:magvar>
    +    </extensions>
    +
    +will be interpreted in the OGR SF model as : +
    +  navaid_name (String) = TOTAL RF
    +  navaid_address (String) = BENSALEM
    +  navaid_state (String) = PA
    +  navaid_country (String) = US
    +  navaid_frequencies (String) = <navaid:frequency type="CTAF" frequency="122.900" name="CTAF" ></navaid:frequency>
    +  navaid_runways (String) = <navaid:runway designation="H1" length="80" width="80" surface="ASPH-G" ></navaid:runway>
    +  navaid_magvar (Integer) = 12
    +
    +


    +Note : the GPX driver will output content of the extensions element only if it is found in the first records of the GPX file. +If extensions appear later, you can force an explicit parsing of the whole file with the GPX_USE_EXTENSIONS environment variable. +

    + +

    Creation Issues

    + +On export all layers are written to a single GPX file. Update of existing +files is not currently supported.

    +If the output file already exits, the writing will not occur. You have to delete the existing file first.

    + +Supported geometries : +

      +
    • Features of type wkbPoint/wkbPoint25D are written in the wpt element.
    • +
    • Features of type wkbLineString/wkbLineString25D are written in the rte element.
    • +
    • Features of type wkbMultiLineString/wkbMultiLineString25D are written in the trk element.
    • +
    • Other type of geometries are not supported.
    • +
    +

    + +For route points and tracks points, if there is a Z coordinate, it is used to fill the elevation element +of the corresponding points.

    + +Starting with GDAL/OGR 1.8.0, if a layer is named "track_points" with wkbPoint/wkbPoint25D geometries, the +tracks in the GPX file will be built from the sequence of features in that layer. This is the way of setting GPX attributes +for each track point, in addition to the raw coordinates. Points belonging to the same track are +identified thanks to the same value of the 'track_fid' field (and it will be broken into track segments +according to the value of the 'track_seg_id' field). They must be written in sequence so that track objects +are properly reconstructed. The 'track_name' field can be set on the first track point to fill the <name> +element of the track. +Similarly, if a layer is named "route_points" with wkbPoint/wkbPoint25D geometries, the routes in the GPX file +will be built from the sequence of points with the same value of the 'route_fid' field. The 'route_name' +field can be set on the first track point to fill the <name> element of the route.

    + +The GPX writer supports the following layer creation options: +

      +
    • FORCE_GPX_TRACK: +By default when writting a layer whose features are of type wkbLineString, the GPX driver +chooses to write them as routes. +
      If FORCE_GPX_TRACK=YES is specified, they will be written +as tracks.
    • +

    • FORCE_GPX_ROUTE: +By default when writting a layer whose features are of type wkbMultiLineString, the GPX driver +chooses to write them as tracks. +
      If FORCE_GPX_ROUTE=YES is specified, they will be written +as routes, provided that the multilines are composed of only one single line.
    • +

    +

    + +The GPX writer supports the following dataset creation options: +

      +
    • GPX_USE_EXTENSIONS: +By default, the GPX driver will discard attribute fields that do not match the GPX XML definition (name, cmt, etc...).
      +If GPX_USE_EXTENSIONS=YES is specified, extra fields will be written inside the<extensions> tag.
    • +

    • GPX_EXTENSIONS_NS: +Only used if GPX_USE_EXTENSIONS=YES and GPX_EXTENSIONS_NS_URL is set.
      +The namespace value used for extension tags. By default, "ogr".
    • +

    • GPX_EXTENSIONS_NS_URL: +Only used if GPX_USE_EXTENSIONS=YES and GPX_EXTENSIONS_NS is set.
      +The namespace URI. By default, "http://osgeo.org/gdal".
    • +

    • LINEFORMAT: (GDAL/OGR >= 1.8.0) +By default files are created with the line +termination conventions of the local platform (CR/LF on win32 or +LF on all other systems). This may be overridden through use of the +LINEFORMAT layer creation option which may have a value of CRLF +(DOS format) or LF (Unix format).
    • +

    +

    + +Waypoints, routes and tracks must be written into that order to be valid against the XML Schema. + +

    +When translating from a source dataset, it may be necessary to rename the field names from the source dataset to the expected GPX attribute names, such as <name>, <desc>, etc... +This can be done with a OGR VRT dataset, or by using the "-sql" option of the ogr2ogr utility. + +

  • + +

    Issues when translating to Shapefile

    + +
      +
    • +When translating the track_points layer to a Shapefile, the field names "track_seg_id" and "track_seg_point_id" are +truncated to 10 characters in the .DBF file, thus leading to duplicate names.

      +To avoid this, starting with GDAL 1.6.1, +you can define the GPX_SHORT_NAMES configuration option to TRUE to make them be reported respectively as "trksegid" and "trksegptid", +which will allow them to be unique once translated to DBF. The "route_point_id" field of route_points layer will also be renamed to "rteptid". +But note that no particular processing will be done for any extension field names.

      + +To translate the track_points layer of a GPX file to a set of shapefiles : +

      +    ogr2ogr --config GPX_SHORT_NAMES YES out input.gpx track_points
      +
      +
    • + +
    • +Shapefile does not support fields of type DateTime. It only supports fields of type Date. So by default, you will lose the +hour:minute:second part of the Time elements of a GPX file.

      +Starting with GDAL 1.6.0, you can use the OGR SQL CAST operator to convert the time field to a string : +

      +    ogr2ogr out input.gpx -sql "SELECT ele, CAST(time AS character(32)) FROM waypoints"
      +
      + +Starting with GDAL 1.7.0, there is a more convenient way to select all fields and ask for the conversion of the ones of a given type to strings: +
      +    ogr2ogr out input.gpx -fieldTypeToString DateTime
      +
      +
    • + +
    + +

    VSI Virtual File System API support

    + +(Some features below might require OGR >= 1.9.0)

    + +The driver supports reading and writing to files managed by VSI Virtual File System API, which include +"regular" files, as well as files in the /vsizip/ (read-write) , /vsigzip/ (read-write) , /vsicurl/ (read-only) domains.

    + +Writing to /dev/stdout or /vsistdout/ is also supported.

    + +

    Example

    + +
  • The ogrinfo utility can be used to dump the content of a GPX datafile : + +
    +ogrinfo -ro -al input.gpx
    +
    +
  • +


    + +

  • The ogr2ogr utility can be used to do GPX to GPX translation : + +
    +ogr2ogr -f GPX output.gpx input.gpx waypoints routes tracks
    +
    +
    +Note : in the case of GPX to GPX translation, you need to specify the layer names, +in order to discard the route_points and track_points layers. +
  • +


    + + +

  • Use of the <extensions> tag for output : +
    +ogr2ogr -f GPX  -dsco GPX_USE_EXTENSIONS=YES output.gpx input
    +
    +which will give an output like the following one : +
    +    <?xml version="1.0"?>
    +    <gpx version="1.1" creator="GDAL 1.5dev"
    +    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +    xmlns:ogr="http://osgeo.org/gdal"
    +    xmlns="http://www.topografix.com/GPX/1/1"
    +    xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
    +    <wpt lat="1" lon="2">
    +    <extensions>
    +        <ogr:Primary_ID>PID5</ogr:Primary_ID>
    +        <ogr:Secondary_ID>SID5</ogr:Secondary_ID>
    +    </extensions>
    +    </wpt>
    +    <wpt lat="3" lon="4">
    +    <extensions>
    +        <ogr:Primary_ID>PID4</ogr:Primary_ID>
    +        <ogr:Secondary_ID>SID4</ogr:Secondary_ID>
    +    </extensions>
    +    </wpt>
    +    </gpx>
    +
    +
  • + +
  • Use of -sql option to remap field names to the ones allowed by the GPX schema (starting with GDAL 1.6.0): +
    +ogr2ogr -f GPX output.gpx input.shp -sql "SELECT field1 AS name, field2 AS desc FROM input"
    +
    +
  • + +

    FAQ

    + +
  • How to solve "ERROR 6: Cannot create GPX layer XXXXXX with unknown geometry type" ?

    +This error happens when the layer to create does not expose a precise geometry type, but just a generic +wkbUnknown type. This is for example the case when using ogr2ogr with a SQL request to a PostgreSQL datasource. +You must then explicitly specify -nlt POINT (or LINESTRING or MULTILINESTRING). +

  • + + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/makefile.vc new file mode 100644 index 000000000..64849b49b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/makefile.vc @@ -0,0 +1,18 @@ + +OBJ = ogrgpxdriver.obj ogrgpxdatasource.obj ogrgpxlayer.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF EXPAT_DIR +EXTRAFLAGS = -I.. -I..\.. $(EXPAT_INCLUDE) -DHAVE_EXPAT=1 +!ELSE +EXTRAFLAGS = -I.. -I..\.. +!ENDIF + +default: $(OBJ) + +clean: + -del *.obj *.pdb + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/ogr_gpx.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/ogr_gpx.h new file mode 100644 index 000000000..586d2cb8e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/ogr_gpx.h @@ -0,0 +1,244 @@ +/****************************************************************************** + * $Id: ogr_gpx.h 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: GPX Translator + * Purpose: Definition of classes for OGR .gpx driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_GPX_H_INCLUDED +#define _OGR_GPX_H_INCLUDED + +#include "ogrsf_frmts.h" + +#ifdef HAVE_EXPAT +#include "ogr_expat.h" +#endif + +class OGRGPXDataSource; + + +typedef enum +{ + GPX_NONE, + GPX_WPT, + GPX_TRACK, + GPX_ROUTE, + GPX_ROUTE_POINT, + GPX_TRACK_POINT, +} GPXGeometryType; + +/************************************************************************/ +/* OGRGPXLayer */ +/************************************************************************/ + +class OGRGPXLayer : public OGRLayer +{ + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference *poSRS; + OGRGPXDataSource* poDS; + + GPXGeometryType gpxGeomType; + + int nGPXFields; + + int bWriteMode; + int nFeatures; + int eof; + int nNextFID; + VSILFILE* fpGPX; /* Large file API */ + const char* pszElementToScan; +#ifdef HAVE_EXPAT + XML_Parser oParser; + XML_Parser oSchemaParser; +#endif + int inInterestingElement; + int hasFoundLat; + int hasFoundLon; + double latVal; + double lonVal; + char* pszSubElementName; + char* pszSubElementValue; + int nSubElementValueLen; + int iCurrentField; + + OGRFeature* poFeature; + OGRFeature ** ppoFeatureTab; + int nFeatureTabLength; + int nFeatureTabIndex; + + OGRMultiLineString* multiLineString; + OGRLineString* lineString; + + int depthLevel; + int interestingDepthLevel; + + OGRFieldDefn* currentFieldDefn; + int inExtensions; + int extensionsDepthLevel; + + int inLink; + int iCountLink; + int nMaxLinks; + + int bEleAs25D; + + int trkFID; + int trkSegId; + int trkSegPtId; + + int rteFID; + int rtePtId; + + int bStopParsing; + int nWithoutEventCounter; + int nDataHandlerCounter; + + int iFirstGPXField; + + private: + void WriteFeatureAttributes( OGRFeature *poFeature, int nIdentLevel = 1 ); + void LoadExtensionsSchema(); +#ifdef HAVE_EXPAT + void AddStrToSubElementValue(const char* pszStr); +#endif + int OGRGPX_WriteXMLExtension(const char* pszTagName, + const char* pszContent); + + public: + OGRGPXLayer(const char *pszFilename, + const char* layerName, + GPXGeometryType gpxGeomType, + OGRGPXDataSource* poDS, + int bWriteMode = FALSE); + ~OGRGPXLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRErr ICreateFeature( OGRFeature *poFeature ); + OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ); + +#ifdef HAVE_EXPAT + void startElementCbk(const char *pszName, const char **ppszAttr); + void endElementCbk(const char *pszName); + void dataHandlerCbk(const char *data, int nLen); + + void startElementLoadSchemaCbk(const char *pszName, const char **ppszAttr); + void endElementLoadSchemaCbk(const char *pszName); + void dataHandlerLoadSchemaCbk(const char *data, int nLen); +#endif + + static OGRErr CheckAndFixCoordinatesValidity( double* pdfLatitude, double* pdfLongitude ); +}; + +/************************************************************************/ +/* OGRGPXDataSource */ +/************************************************************************/ + +typedef enum +{ + GPX_VALIDITY_UNKNOWN, + GPX_VALIDITY_INVALID, + GPX_VALIDITY_VALID +} OGRGPXValidity; + +class OGRGPXDataSource : public OGRDataSource +{ + char* pszName; + + OGRGPXLayer** papoLayers; + int nLayers; + + /* Export related */ + VSILFILE *fpOutput; /* Large file API */ + int bIsBackSeekable; + const char *pszEOL; + int nOffsetBounds; + double dfMinLat, dfMinLon, dfMaxLat, dfMaxLon; + + GPXGeometryType lastGPXGeomTypeWritten; + + int bUseExtensions; + char* pszExtensionsNS; + + OGRGPXValidity validity; + int nElementsRead; + char* pszVersion; +#ifdef HAVE_EXPAT + XML_Parser oCurrentParser; + int nDataHandlerCounter; +#endif + + public: + OGRGPXDataSource(); + ~OGRGPXDataSource(); + + int nLastRteId; + int nLastTrkId; + int nLastTrkSegId; + + int Open( const char * pszFilename, + int bUpdate ); + + int Create( const char *pszFilename, + char **papszOptions ); + + const char* GetName() { return pszName; } + + int GetLayerCount() { return nLayers; } + OGRLayer* GetLayer( int ); + + OGRLayer * ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ); + + int TestCapability( const char * ); + + VSILFILE * GetOutputFP() { return fpOutput; } + void SetLastGPXGeomTypeWritten(GPXGeometryType gpxGeomType) + { lastGPXGeomTypeWritten = gpxGeomType; } + GPXGeometryType GetLastGPXGeomTypeWritten() { return lastGPXGeomTypeWritten; } + + int GetUseExtensions() { return bUseExtensions; } + const char* GetExtensionsNS() { return pszExtensionsNS; } + +#ifdef HAVE_EXPAT + void startElementValidateCbk(const char *pszName, const char **ppszAttr); + void dataHandlerValidateCbk(const char *data, int nLen); +#endif + + const char* GetVersion() { return pszVersion; } + + void AddCoord(double dfLon, double dfLat); + + void PrintLine(const char *fmt, ...) CPL_PRINT_FUNC_FORMAT (2, 3); +}; + +#endif /* ndef _OGR_GPX_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/ogrgpxdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/ogrgpxdatasource.cpp new file mode 100644 index 000000000..37d80de18 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/ogrgpxdatasource.cpp @@ -0,0 +1,566 @@ +/****************************************************************************** + * $Id: ogrgpxdatasource.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: GPX Translator + * Purpose: Implements OGRGPXDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2007-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gpx.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_csv.h" + +CPL_CVSID("$Id: ogrgpxdatasource.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +#define SPACE_FOR_METADATA 160 + +/************************************************************************/ +/* OGRGPXDataSource() */ +/************************************************************************/ + +OGRGPXDataSource::OGRGPXDataSource() + +{ + lastGPXGeomTypeWritten = GPX_NONE; + bUseExtensions = FALSE; + pszExtensionsNS = NULL; + + papoLayers = NULL; + nLayers = 0; + + fpOutput = NULL; + nOffsetBounds = -1; + dfMinLat = 90; + dfMinLon = 180; + dfMaxLat = -90; + dfMaxLon = -180; + + pszName = NULL; + pszVersion = NULL; + + bIsBackSeekable = TRUE; + pszEOL = "\n"; + + nLastRteId = -1; + nLastTrkId = -1; + nLastTrkSegId = -1; +} + +/************************************************************************/ +/* ~OGRGPXDataSource() */ +/************************************************************************/ + +OGRGPXDataSource::~OGRGPXDataSource() + +{ + if ( fpOutput != NULL ) + { + if (nLastRteId != -1) + PrintLine(""); + else if (nLastTrkId != -1) + { + PrintLine(" "); + PrintLine(""); + } + PrintLine(""); + if ( bIsBackSeekable ) + { + /* Write the element in the reserved space */ + if (dfMinLon <= dfMaxLon) + { + char szMetadata[SPACE_FOR_METADATA+1]; + int nRet = CPLsnprintf(szMetadata, SPACE_FOR_METADATA, + "", + dfMinLat, dfMinLon, dfMaxLat, dfMaxLon); + if (nRet < SPACE_FOR_METADATA) + { + VSIFSeekL(fpOutput, nOffsetBounds, SEEK_SET); + VSIFWriteL(szMetadata, 1, strlen(szMetadata), fpOutput); + } + } + VSIFCloseL( fpOutput); + } + } + + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + CPLFree( pszExtensionsNS ); + CPLFree( pszName ); + CPLFree( pszVersion ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGPXDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCDeleteLayer) ) + return FALSE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRGPXDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * OGRGPXDataSource::ICreateLayer( const char * pszLayerName, + CPL_UNUSED OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ) +{ + GPXGeometryType gpxGeomType; + if (eType == wkbPoint || eType == wkbPoint25D) + { + if (EQUAL(pszLayerName, "track_points")) + gpxGeomType = GPX_TRACK_POINT; + else if (EQUAL(pszLayerName, "route_points")) + gpxGeomType = GPX_ROUTE_POINT; + else + gpxGeomType = GPX_WPT; + } + else if (eType == wkbLineString || eType == wkbLineString25D) + { + const char *pszForceGPXTrack = CSLFetchNameValue( papszOptions, "FORCE_GPX_TRACK"); + if (pszForceGPXTrack && CSLTestBoolean(pszForceGPXTrack)) + gpxGeomType = GPX_TRACK; + else + gpxGeomType = GPX_ROUTE; + } + else if (eType == wkbMultiLineString || eType == wkbMultiLineString25D) + { + const char *pszForceGPXRoute = CSLFetchNameValue( papszOptions, "FORCE_GPX_ROUTE"); + if (pszForceGPXRoute && CSLTestBoolean(pszForceGPXRoute)) + gpxGeomType = GPX_ROUTE; + else + gpxGeomType = GPX_TRACK; + } + else if (eType == wkbUnknown) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot create GPX layer %s with unknown geometry type", pszLayerName); + return NULL; + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Geometry type of `%s' not supported in GPX.\n", + OGRGeometryTypeToName(eType) ); + return NULL; + } + nLayers++; + papoLayers = (OGRGPXLayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRGPXLayer*)); + papoLayers[nLayers-1] = new OGRGPXLayer( pszName, pszLayerName, gpxGeomType, this, TRUE ); + + return papoLayers[nLayers-1]; +} + +#ifdef HAVE_EXPAT + +/************************************************************************/ +/* startElementValidateCbk() */ +/************************************************************************/ + +void OGRGPXDataSource::startElementValidateCbk(const char *pszName, const char **ppszAttr) +{ + if (validity == GPX_VALIDITY_UNKNOWN) + { + if (strcmp(pszName, "gpx") == 0) + { + int i; + validity = GPX_VALIDITY_VALID; + for(i=0; ppszAttr[i] != NULL; i+= 2) + { + if (strcmp(ppszAttr[i], "version") == 0) + { + pszVersion = CPLStrdup(ppszAttr[i+1]); + break; + } + } + } + else + { + validity = GPX_VALIDITY_INVALID; + } + } + else if (validity == GPX_VALIDITY_VALID) + { + if (strcmp(pszName, "extensions") == 0) + { + bUseExtensions = TRUE; + } + nElementsRead++; + } +} + + +/************************************************************************/ +/* dataHandlerValidateCbk() */ +/************************************************************************/ + +void OGRGPXDataSource::dataHandlerValidateCbk(CPL_UNUSED const char *data, + CPL_UNUSED int nLen) +{ + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)"); + XML_StopParser(oCurrentParser, XML_FALSE); + } +} + + +static void XMLCALL startElementValidateCbk(void *pUserData, const char *pszName, const char **ppszAttr) +{ + OGRGPXDataSource* poDS = (OGRGPXDataSource*) pUserData; + poDS->startElementValidateCbk(pszName, ppszAttr); +} + +static void XMLCALL dataHandlerValidateCbk(void *pUserData, const char *data, int nLen) +{ + OGRGPXDataSource* poDS = (OGRGPXDataSource*) pUserData; + poDS->dataHandlerValidateCbk(data, nLen); +} +#endif + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRGPXDataSource::Open( const char * pszFilename, int bUpdateIn) + +{ + if (bUpdateIn) + { + CPLError(CE_Failure, CPLE_NotSupported, + "OGR/GPX driver does not support opening a file in update mode"); + return FALSE; + } +#ifdef HAVE_EXPAT + pszName = CPLStrdup( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Try to open the file. */ +/* -------------------------------------------------------------------- */ + VSILFILE* fp = VSIFOpenL(pszFilename, "r"); + if (fp == NULL) + return FALSE; + + validity = GPX_VALIDITY_UNKNOWN; + CPLFree(pszVersion); + pszVersion = NULL; + bUseExtensions = FALSE; + nElementsRead = 0; + + XML_Parser oParser = OGRCreateExpatXMLParser(); + oCurrentParser = oParser; + XML_SetUserData(oParser, this); + XML_SetElementHandler(oParser, ::startElementValidateCbk, NULL); + XML_SetCharacterDataHandler(oParser, ::dataHandlerValidateCbk); + + char aBuf[BUFSIZ]; + int nDone; + unsigned int nLen; + int nCount = 0; + + /* Begin to parse the file and look for the element */ + /* It *MUST* be the first element of an XML file */ + /* So once we have read the first element, we know if we can */ + /* handle the file or not with that driver */ + do + { + nDataHandlerCounter = 0; + nLen = (unsigned int) VSIFReadL( aBuf, 1, sizeof(aBuf), fp ); + nDone = VSIFEofL(fp); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + if (nLen <= BUFSIZ-1) + aBuf[nLen] = 0; + else + aBuf[BUFSIZ-1] = 0; + if (strstr(aBuf, " element, now we try */ + /* to recognize if they are tags */ + /* But we stop to look for after an arbitrary number of tags */ + if (bUseExtensions) + break; + else if (nElementsRead > 200) + break; + } + else + { + /* After reading 50 * BUFSIZE bytes, and not finding whether the file */ + /* is GPX or not, we give up and fail silently */ + nCount ++; + if (nCount == 50) + break; + } + } while (!nDone && nLen > 0 ); + + XML_ParserFree(oParser); + + VSIFCloseL(fp); + + if (validity == GPX_VALIDITY_VALID) + { + CPLDebug("GPX", "%s seems to be a GPX file.", pszFilename); + if (bUseExtensions) + CPLDebug("GPX", "It uses "); + + if (pszVersion == NULL) + { + /* Default to 1.1 */ + CPLError(CE_Warning, CPLE_AppDefined, "GPX schema version is unknown. " + "The driver may not be able to handle the file correctly and will behave as if it is GPX 1.1."); + pszVersion = CPLStrdup("1.1"); + } + else if (strcmp(pszVersion, "1.0") == 0 || strcmp(pszVersion, "1.1") == 0) + { + /* Fine */ + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "GPX schema version '%s' is not handled by the driver. " + "The driver may not be able to handle the file correctly and will behave as if it is GPX 1.1.", pszVersion); + } + + nLayers = 5; + papoLayers = (OGRGPXLayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRGPXLayer*)); + papoLayers[0] = new OGRGPXLayer( pszName, "waypoints", GPX_WPT, this, FALSE ); + papoLayers[1] = new OGRGPXLayer( pszName, "routes", GPX_ROUTE, this, FALSE ); + papoLayers[2] = new OGRGPXLayer( pszName, "tracks", GPX_TRACK, this, FALSE ); + papoLayers[3] = new OGRGPXLayer( pszName, "route_points", GPX_ROUTE_POINT, this, FALSE ); + papoLayers[4] = new OGRGPXLayer( pszName, "track_points", GPX_TRACK_POINT, this, FALSE ); + } + + return (validity == GPX_VALIDITY_VALID); +#else + char aBuf[256]; + VSILFILE* fp = VSIFOpenL(pszFilename, "r"); + if (fp) + { + unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, 255, fp ); + aBuf[nLen] = 0; + if (strstr(aBuf, ""); + VSIFPrintfL(fpOutput, ""); + if (bIsBackSeekable) + { + /* Reserve space for */ + char szMetadata[SPACE_FOR_METADATA+1]; + memset(szMetadata, ' ', SPACE_FOR_METADATA); + szMetadata[SPACE_FOR_METADATA] = '\0'; + nOffsetBounds = (int) VSIFTellL(fpOutput); + PrintLine("%s", szMetadata); + } + + return TRUE; +} + +/************************************************************************/ +/* AddCoord() */ +/************************************************************************/ + +void OGRGPXDataSource::AddCoord(double dfLon, double dfLat) +{ + if (dfLon < dfMinLon) dfMinLon = dfLon; + if (dfLat < dfMinLat) dfMinLat = dfLat; + if (dfLon > dfMaxLon) dfMaxLon = dfLon; + if (dfLat > dfMaxLat) dfMaxLat = dfLat; +} + +/************************************************************************/ +/* PrintLine() */ +/************************************************************************/ + +void OGRGPXDataSource::PrintLine(const char *fmt, ...) +{ + CPLString osWork; + va_list args; + + va_start( args, fmt ); + osWork.vPrintf( fmt, args ); + va_end( args ); + + VSIFPrintfL(fpOutput, "%s%s", osWork.c_str(), pszEOL); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/ogrgpxdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/ogrgpxdriver.cpp new file mode 100644 index 000000000..3bd09a289 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/ogrgpxdriver.cpp @@ -0,0 +1,146 @@ +/****************************************************************************** + * $Id: ogrgpxdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: GPX Translator + * Purpose: Implements OGRGPXDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2007-2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gpx.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrgpxdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRGPXDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->eAccess == GA_Update || poOpenInfo->fpL == NULL ) + return NULL; + + if( strstr((const char*)poOpenInfo->pabyHeader, "Open( poOpenInfo->pszFilename, FALSE ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRGPXDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + CPL_UNUSED char **papszOptions ) +{ + OGRGPXDataSource *poDS = new OGRGPXDataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +static CPLErr OGRGPXDriverDelete( const char *pszFilename ) + +{ + if( VSIUnlink( pszFilename ) == 0 ) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* RegisterOGRGPX() */ +/************************************************************************/ + +void RegisterOGRGPX() + +{ + if (! GDAL_CHECK_VERSION("OGR/GPX driver")) + return; + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GPX" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GPX" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GPX" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gpx" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_gpx.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +#ifdef WIN32 +" " +" "); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRGPXDriverOpen; + poDriver->pfnCreate = OGRGPXDriverCreate; + poDriver->pfnDelete = OGRGPXDriverDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/ogrgpxlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/ogrgpxlayer.cpp new file mode 100644 index 000000000..1cc5ecf9c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gpx/ogrgpxlayer.cpp @@ -0,0 +1,2135 @@ +/****************************************************************************** + * $Id: ogrgpxlayer.cpp 28900 2015-04-14 09:40:34Z rouault $ + * + * Project: GPX Translator + * Purpose: Implements OGRGPXLayer class. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gpx.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_minixml.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrgpxlayer.cpp 28900 2015-04-14 09:40:34Z rouault $"); + +#define FLD_TRACK_FID 0 +#define FLD_TRACK_SEG_ID 1 +#define FLD_TRACK_PT_ID 2 +#define FLD_TRACK_NAME 3 + +#define FLD_ROUTE_FID 0 +#define FLD_ROUTE_PT_ID 1 +#define FLD_ROUTE_NAME 2 + +/************************************************************************/ +/* OGRGPXLayer() */ +/* */ +/* Note that the OGRGPXLayer assumes ownership of the passed */ +/* file pointer. */ +/************************************************************************/ + +OGRGPXLayer::OGRGPXLayer( const char* pszFilename, + const char* pszLayerName, + GPXGeometryType gpxGeomType, + OGRGPXDataSource* poDS, + int bWriteMode) + +{ + const char* gpxVersion = poDS->GetVersion(); + + int i; + + eof = FALSE; + nNextFID = 0; + + this->poDS = poDS; + this->bWriteMode = bWriteMode; + this->gpxGeomType = gpxGeomType; + + pszElementToScan = pszLayerName; + + nMaxLinks = atoi(CPLGetConfigOption("GPX_N_MAX_LINKS", "2")); + if (nMaxLinks < 0) + nMaxLinks = 2; + if (nMaxLinks > 100) + nMaxLinks = 100; + + nFeatures = 0; + + bEleAs25D = CSLTestBoolean(CPLGetConfigOption("GPX_ELE_AS_25D", "NO")); + + int bShortNames = CSLTestBoolean(CPLGetConfigOption("GPX_SHORT_NAMES", "NO")); + + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + if (gpxGeomType == GPX_TRACK_POINT) + { + /* Don't move this code. This fields must be number 0, 1 and 2 */ + /* in order to make OGRGPXLayer::startElementCbk work */ + OGRFieldDefn oFieldTrackFID("track_fid", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oFieldTrackFID ); + + OGRFieldDefn oFieldTrackSegID((bShortNames) ? "trksegid" : "track_seg_id", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oFieldTrackSegID ); + + OGRFieldDefn oFieldTrackSegPointID((bShortNames) ? "trksegptid" : "track_seg_point_id", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oFieldTrackSegPointID ); + + if (bWriteMode) + { + OGRFieldDefn oFieldName("track_name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + } + } + else if (gpxGeomType == GPX_ROUTE_POINT) + { + /* Don't move this code. See above */ + OGRFieldDefn oFieldRouteFID("route_fid", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oFieldRouteFID ); + + OGRFieldDefn oFieldRoutePointID((bShortNames) ? "rteptid" : "route_point_id", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oFieldRoutePointID ); + + if (bWriteMode) + { + OGRFieldDefn oFieldName("route_name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + } + } + + iFirstGPXField = poFeatureDefn->GetFieldCount(); + + if (gpxGeomType == GPX_WPT || + gpxGeomType == GPX_TRACK_POINT || + gpxGeomType == GPX_ROUTE_POINT) + { + poFeatureDefn->SetGeomType((bEleAs25D) ? wkbPoint25D : wkbPoint); + /* Position info */ + + OGRFieldDefn oFieldEle("ele", OFTReal ); + poFeatureDefn->AddFieldDefn( &oFieldEle ); + + OGRFieldDefn oFieldTime("time", OFTDateTime ); + poFeatureDefn->AddFieldDefn( &oFieldTime ); + + if (gpxGeomType == GPX_TRACK_POINT && + gpxVersion && strcmp(gpxVersion, "1.0") == 0) + { + OGRFieldDefn oFieldCourse("course", OFTReal ); + poFeatureDefn->AddFieldDefn( &oFieldCourse ); + + OGRFieldDefn oFieldSpeed("speed", OFTReal ); + poFeatureDefn->AddFieldDefn( &oFieldSpeed ); + } + + OGRFieldDefn oFieldMagVar("magvar", OFTReal ); + poFeatureDefn->AddFieldDefn( &oFieldMagVar ); + + OGRFieldDefn oFieldGeoidHeight("geoidheight", OFTReal ); + poFeatureDefn->AddFieldDefn( &oFieldGeoidHeight ); + + /* Description info */ + + OGRFieldDefn oFieldName("name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oFieldCmt("cmt", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldCmt ); + + OGRFieldDefn oFieldDesc("desc", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldDesc ); + + OGRFieldDefn oFieldSrc("src", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldSrc ); + + if (gpxVersion && strcmp(gpxVersion, "1.0") == 0) + { + OGRFieldDefn oFieldUrl("url", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldUrl ); + + OGRFieldDefn oFieldUrlName("urlname", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldUrlName ); + } + else + { + for(i=1;i<=nMaxLinks;i++) + { + char szFieldName[32]; + sprintf(szFieldName, "link%d_href", i); + OGRFieldDefn oFieldLinkHref( szFieldName, OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldLinkHref ); + + sprintf(szFieldName, "link%d_text", i); + OGRFieldDefn oFieldLinkText( szFieldName, OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldLinkText ); + + sprintf(szFieldName, "link%d_type", i); + OGRFieldDefn oFieldLinkType( szFieldName, OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldLinkType ); + } + } + + OGRFieldDefn oFieldSym("sym", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldSym ); + + OGRFieldDefn oFieldType("type", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldType ); + + /* Accuracy info */ + + OGRFieldDefn oFieldFix("fix", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldFix ); + + OGRFieldDefn oFieldSat("sat", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oFieldSat ); + + OGRFieldDefn oFieldHdop("hdop", OFTReal ); + poFeatureDefn->AddFieldDefn( &oFieldHdop ); + + OGRFieldDefn oFieldVdop("vdop", OFTReal ); + poFeatureDefn->AddFieldDefn( &oFieldVdop ); + + OGRFieldDefn oFieldPdop("pdop", OFTReal ); + poFeatureDefn->AddFieldDefn( &oFieldPdop ); + + OGRFieldDefn oFieldAgeofgpsdata("ageofdgpsdata", OFTReal ); + poFeatureDefn->AddFieldDefn( &oFieldAgeofgpsdata ); + + OGRFieldDefn oFieldDgpsid("dgpsid", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oFieldDgpsid ); + } + else + { + if (gpxGeomType == GPX_TRACK) + poFeatureDefn->SetGeomType((bEleAs25D) ? wkbMultiLineString25D : wkbMultiLineString); + else + poFeatureDefn->SetGeomType((bEleAs25D) ? wkbLineString25D : wkbLineString); + + OGRFieldDefn oFieldName("name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oFieldCmt("cmt", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldCmt ); + + OGRFieldDefn oFieldDesc("desc", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldDesc ); + + OGRFieldDefn oFieldSrc("src", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldSrc ); + + for(i=1;i<=nMaxLinks;i++) + { + char szFieldName[32]; + sprintf(szFieldName, "link%d_href", i); + OGRFieldDefn oFieldLinkHref( szFieldName, OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldLinkHref ); + + sprintf(szFieldName, "link%d_text", i); + OGRFieldDefn oFieldLinkText( szFieldName, OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldLinkText ); + + sprintf(szFieldName, "link%d_type", i); + OGRFieldDefn oFieldLinkType( szFieldName, OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldLinkType ); + } + + OGRFieldDefn oFieldNumber("number", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oFieldNumber ); + + OGRFieldDefn oFieldType("type", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldType ); + } + + /* Number of 'standard' GPX attributes */ + nGPXFields = poFeatureDefn->GetFieldCount(); + + ppoFeatureTab = NULL; + nFeatureTabIndex = 0; + nFeatureTabLength = 0; + pszSubElementName = NULL; + pszSubElementValue = NULL; + nSubElementValueLen = 0; + bStopParsing = FALSE; + + poSRS = new OGRSpatialReference("GEOGCS[\"WGS 84\", " + " DATUM[\"WGS_1984\"," + " SPHEROID[\"WGS 84\",6378137,298.257223563," + " AUTHORITY[\"EPSG\",\"7030\"]]," + " AUTHORITY[\"EPSG\",\"6326\"]]," + " PRIMEM[\"Greenwich\",0," + " AUTHORITY[\"EPSG\",\"8901\"]]," + " UNIT[\"degree\",0.01745329251994328," + " AUTHORITY[\"EPSG\",\"9122\"]]," + " AUTHORITY[\"EPSG\",\"4326\"]]"); + if( poFeatureDefn->GetGeomFieldCount() != 0 ) + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + poFeature = NULL; + +#ifdef HAVE_EXPAT + oParser = NULL; +#endif + + if (bWriteMode == FALSE) + { + fpGPX = VSIFOpenL( pszFilename, "r" ); + if( fpGPX == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename); + return; + } + + if (poDS->GetUseExtensions() || + CSLTestBoolean(CPLGetConfigOption("GPX_USE_EXTENSIONS", "FALSE"))) + { + LoadExtensionsSchema(); + } + } + else + fpGPX = NULL; + + ResetReading(); +} + +/************************************************************************/ +/* ~OGRGPXLayer() */ +/************************************************************************/ + +OGRGPXLayer::~OGRGPXLayer() + +{ +#ifdef HAVE_EXPAT + if (oParser) + XML_ParserFree(oParser); +#endif + poFeatureDefn->Release(); + + if( poSRS != NULL ) + poSRS->Release(); + + CPLFree(pszSubElementName); + CPLFree(pszSubElementValue); + + int i; + for(i=nFeatureTabIndex;istartElementCbk(pszName, ppszAttr); +} + +static void XMLCALL endElementCbk(void *pUserData, const char *pszName) +{ + ((OGRGPXLayer*)pUserData)->endElementCbk(pszName); +} + +static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen) +{ + ((OGRGPXLayer*)pUserData)->dataHandlerCbk(data, nLen); +} + +#endif + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRGPXLayer::ResetReading() + +{ + eof = FALSE; + nNextFID = 0; + if (fpGPX) + { + VSIFSeekL( fpGPX, 0, SEEK_SET ); +#ifdef HAVE_EXPAT + if (oParser) + XML_ParserFree(oParser); + + oParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk); + XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk); + XML_SetUserData(oParser, this); +#endif + } + hasFoundLat = FALSE; + hasFoundLon = FALSE; + inInterestingElement = FALSE; + CPLFree(pszSubElementName); + pszSubElementName = NULL; + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + + int i; + for(i=nFeatureTabIndex;iSetFID( nNextFID++ ); + poFeature->SetGeometryDirectly( new OGRPoint( lonVal, latVal ) ); + + if (gpxGeomType == GPX_ROUTE_POINT) + { + rtePtId++; + poFeature->SetField( FLD_ROUTE_FID, rteFID-1); + poFeature->SetField( FLD_ROUTE_PT_ID, rtePtId-1); + } + else if (gpxGeomType == GPX_TRACK_POINT) + { + trkSegPtId++; + + poFeature->SetField( FLD_TRACK_FID, trkFID-1); + poFeature->SetField( FLD_TRACK_SEG_ID, trkSegId-1); + poFeature->SetField( FLD_TRACK_PT_ID, trkSegPtId-1); + } + } + } + else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0) + { + interestingDepthLevel = depthLevel; + + if (poFeature) + delete poFeature; + inExtensions = FALSE; + inLink = FALSE; + iCountLink = 0; + poFeature = new OGRFeature( poFeatureDefn ); + inInterestingElement = TRUE; + + multiLineString = new OGRMultiLineString (); + lineString = NULL; + + poFeature->SetFID( nNextFID++ ); + poFeature->SetGeometryDirectly( multiLineString ); + } + else if (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trk") == 0) + { + trkFID++; + trkSegId = 0; + } + else if (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkseg") == 0) + { + trkSegId++; + trkSegPtId = 0; + } + else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0) + { + interestingDepthLevel = depthLevel; + + if (poFeature) + delete poFeature; + + poFeature = new OGRFeature( poFeatureDefn ); + inInterestingElement = TRUE; + inExtensions = FALSE; + inLink = FALSE; + iCountLink = 0; + + lineString = new OGRLineString (); + poFeature->SetFID( nNextFID++ ); + poFeature->SetGeometryDirectly( lineString ); + } + else if (gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rte") == 0) + { + rteFID++; + rtePtId = 0; + } + else if (inInterestingElement) + { + if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trkseg") == 0 && + depthLevel == interestingDepthLevel + 1) + { + if (multiLineString) + { + lineString = new OGRLineString (); + multiLineString->addGeometryDirectly( lineString ); + } + } + else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trkpt") == 0 && + depthLevel == interestingDepthLevel + 2) + { + if (lineString) + { + hasFoundLat = FALSE; + hasFoundLon = FALSE; + for (i = 0; ppszAttr[i]; i += 2) + { + if (strcmp(ppszAttr[i], "lat") == 0) + { + hasFoundLat = TRUE; + latVal = CPLAtof(ppszAttr[i + 1]); + } + else if (strcmp(ppszAttr[i], "lon") == 0) + { + hasFoundLon = TRUE; + lonVal = CPLAtof(ppszAttr[i + 1]); + } + } + + if (hasFoundLat && hasFoundLon) + { + lineString->addPoint(lonVal, latVal); + } + } + } + else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rtept") == 0 && + depthLevel == interestingDepthLevel + 1) + { + if (lineString) + { + hasFoundLat = FALSE; + hasFoundLon = FALSE; + for (i = 0; ppszAttr[i]; i += 2) + { + if (strcmp(ppszAttr[i], "lat") == 0) + { + hasFoundLat = TRUE; + latVal = CPLAtof(ppszAttr[i + 1]); + } + else if (strcmp(ppszAttr[i], "lon") == 0) + { + hasFoundLon = TRUE; + lonVal = CPLAtof(ppszAttr[i + 1]); + } + } + + if (hasFoundLat && hasFoundLon) + { + lineString->addPoint(lonVal, latVal); + } + } + } + else if (bEleAs25D && + strcmp(pszName, "ele") == 0 && + lineString != NULL && + ((gpxGeomType == GPX_ROUTE && depthLevel == interestingDepthLevel + 2) || + (gpxGeomType == GPX_TRACK && depthLevel == interestingDepthLevel + 3))) + { + CPLFree(pszSubElementName); + pszSubElementName = CPLStrdup(pszName); + } + else if (depthLevel == interestingDepthLevel + 1 && + strcmp(pszName, "extensions") == 0) + { + if (poDS->GetUseExtensions()) + { + inExtensions = TRUE; + } + } + else if (depthLevel == interestingDepthLevel + 1 || + (inExtensions && depthLevel == interestingDepthLevel + 2) ) + { + CPLFree(pszSubElementName); + pszSubElementName = NULL; + iCurrentField = -1; + + if (strcmp(pszName, "link") == 0) + { + iCountLink++; + if (iCountLink <= nMaxLinks) + { + if (ppszAttr[0] && ppszAttr[1] && + strcmp(ppszAttr[0], "href") == 0) + { + char szFieldName[32]; + sprintf(szFieldName, "link%d_href", iCountLink); + iCurrentField = poFeatureDefn->GetFieldIndex(szFieldName); + poFeature->SetField( iCurrentField, ppszAttr[1]); + } + } + else + { + static int once = 1; + if (once) + { + once = 0; + CPLError(CE_Warning, CPLE_AppDefined, + "GPX driver only reads %d links per element. Others will be ignored. " + "This can be changed with the GPX_N_MAX_LINKS environment variable", + nMaxLinks); + } + } + inLink = TRUE; + iCurrentField = -1; + } + else + { + for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + int bMatch; + if (iField >= nGPXFields) + { + char* pszCompatibleName = OGRGPX_GetOGRCompatibleTagName(pszName); + bMatch = (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), + pszCompatibleName ) == 0); + CPLFree(pszCompatibleName); + } + else + bMatch = (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), + pszName ) == 0); + + if (bMatch) + { + iCurrentField = iField; + pszSubElementName = CPLStrdup(pszName); + break; + } + } + } + } + else if (depthLevel == interestingDepthLevel + 2 && inLink) + { + char szFieldName[32]; + CPLFree(pszSubElementName); + pszSubElementName = NULL; + iCurrentField = -1; + if (iCountLink <= nMaxLinks) + { + if (strcmp(pszName, "type") == 0) + { + sprintf(szFieldName, "link%d_type", iCountLink); + iCurrentField = poFeatureDefn->GetFieldIndex(szFieldName); + pszSubElementName = CPLStrdup(pszName); + } + else if (strcmp(pszName, "text") == 0) + { + sprintf(szFieldName, "link%d_text", iCountLink); + iCurrentField = poFeatureDefn->GetFieldIndex(szFieldName); + pszSubElementName = CPLStrdup(pszName); + } + } + } + else if (inExtensions && depthLevel > interestingDepthLevel + 2) + { + AddStrToSubElementValue( + (ppszAttr[0] == NULL) ? CPLSPrintf("<%s>", pszName) : + CPLSPrintf("<%s ", pszName)); + int i; + for (i = 0; ppszAttr[i]; i += 2) + { + AddStrToSubElementValue( + CPLSPrintf("%s=\"%s\" ", ppszAttr[i], ppszAttr[i + 1])); + } + if (ppszAttr[0] != NULL) + { + AddStrToSubElementValue(">"); + } + } + } + + depthLevel++; +} + +/************************************************************************/ +/* endElementCbk() */ +/************************************************************************/ + +void OGRGPXLayer::endElementCbk(const char *pszName) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + depthLevel--; + + if (inInterestingElement) + { + if ((gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0) || + (gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0) || + (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0)) + { + int bIsValid = (hasFoundLat && hasFoundLon); + inInterestingElement = FALSE; + + if( bIsValid + && (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + if( poFeature->GetGeometryRef() != NULL ) + { + poFeature->GetGeometryRef()->assignSpatialReference( poSRS ); + + if (bEleAs25D) + { + for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + if (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), "ele" ) == 0) + { + if( poFeature->IsFieldSet( iField ) ) + { + double val = poFeature->GetFieldAsDouble( iField); + ((OGRPoint*)poFeature->GetGeometryRef())->setZ(val); + poFeature->GetGeometryRef()->setCoordinateDimension(3); + } + break; + } + } + } + } + + ppoFeatureTab = (OGRFeature**) + CPLRealloc(ppoFeatureTab, + sizeof(OGRFeature*) * (nFeatureTabLength + 1)); + ppoFeatureTab[nFeatureTabLength] = poFeature; + nFeatureTabLength++; + } + else + { + delete poFeature; + } + poFeature = NULL; + } + else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0) + { + inInterestingElement = FALSE; + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + if( poFeature->GetGeometryRef() != NULL ) + { + poFeature->GetGeometryRef()->assignSpatialReference( poSRS ); + } + + ppoFeatureTab = (OGRFeature**) + CPLRealloc(ppoFeatureTab, + sizeof(OGRFeature*) * (nFeatureTabLength + 1)); + ppoFeatureTab[nFeatureTabLength] = poFeature; + nFeatureTabLength++; + } + else + { + delete poFeature; + } + poFeature = NULL; + multiLineString = NULL; + lineString = NULL; + } + else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trkseg") == 0 && + depthLevel == interestingDepthLevel + 1) + { + lineString = NULL; + } + else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0) + { + inInterestingElement = FALSE; + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + if( poFeature->GetGeometryRef() != NULL ) + { + poFeature->GetGeometryRef()->assignSpatialReference( poSRS ); + } + + ppoFeatureTab = (OGRFeature**) + CPLRealloc(ppoFeatureTab, + sizeof(OGRFeature*) * (nFeatureTabLength + 1)); + ppoFeatureTab[nFeatureTabLength] = poFeature; + nFeatureTabLength++; + } + else + { + delete poFeature; + } + poFeature = NULL; + lineString = NULL; + } + else if (bEleAs25D && + strcmp(pszName, "ele") == 0 && + lineString != NULL && + ((gpxGeomType == GPX_ROUTE && depthLevel == interestingDepthLevel + 2) || + (gpxGeomType == GPX_TRACK && depthLevel == interestingDepthLevel + 3))) + { + poFeature->GetGeometryRef()->setCoordinateDimension(3); + + if (nSubElementValueLen) + { + pszSubElementValue[nSubElementValueLen] = 0; + + double val = CPLAtof(pszSubElementValue); + int i = lineString->getNumPoints() - 1; + if (i >= 0) + lineString->setPoint(i, lineString->getX(i), lineString->getY(i), val); + } + + CPLFree(pszSubElementName); + pszSubElementName = NULL; + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + } + else if (depthLevel == interestingDepthLevel + 1 && + strcmp(pszName, "extensions") == 0) + { + inExtensions = FALSE; + } + else if ((depthLevel == interestingDepthLevel + 1 || + (inExtensions && depthLevel == interestingDepthLevel + 2) ) && + pszSubElementName && strcmp(pszName, pszSubElementName) == 0) + { + if (poFeature && pszSubElementValue && nSubElementValueLen) + { + pszSubElementValue[nSubElementValueLen] = 0; + if (strcmp(pszSubElementName, "time") == 0) + { + OGRField sField; + if (OGRParseXMLDateTime(pszSubElementValue, &sField)) + { + poFeature->SetField(iCurrentField, &sField); + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Could not parse %s as a valid dateTime", pszSubElementValue); + } + } + else + { + poFeature->SetField( iCurrentField, pszSubElementValue); + } + } + if (strcmp(pszName, "link") == 0) + inLink = FALSE; + + CPLFree(pszSubElementName); + pszSubElementName = NULL; + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + } + else if (inLink && depthLevel == interestingDepthLevel + 2) + { + if (iCurrentField != -1 && pszSubElementName && + strcmp(pszName, pszSubElementName) == 0 && poFeature && pszSubElementValue && nSubElementValueLen) + { + pszSubElementValue[nSubElementValueLen] = 0; + poFeature->SetField( iCurrentField, pszSubElementValue); + } + + CPLFree(pszSubElementName); + pszSubElementName = NULL; + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + } + else if (inExtensions && depthLevel > interestingDepthLevel + 2) + { + AddStrToSubElementValue(CPLSPrintf("", pszName)); + } + } +} + +/************************************************************************/ +/* dataHandlerCbk() */ +/************************************************************************/ + +void OGRGPXLayer::dataHandlerCbk(const char *data, int nLen) +{ + if (bStopParsing) return; + + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + + nWithoutEventCounter = 0; + + if (pszSubElementName) + { + if (inExtensions && depthLevel > interestingDepthLevel + 2) + { + if (data[0] == '\n') + return; + } + char* pszNewSubElementValue = (char*) VSIRealloc(pszSubElementValue, nSubElementValueLen + nLen + 1); + if (pszNewSubElementValue == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + pszSubElementValue = pszNewSubElementValue; + memcpy(pszSubElementValue + nSubElementValueLen, data, nLen); + nSubElementValueLen += nLen; + if (nSubElementValueLen > 100000) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + } + } +} +#endif + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRGPXLayer::GetNextFeature() +{ + if (bWriteMode) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot read features when writing a GPX file"); + return NULL; + } + + if (fpGPX == NULL) + return NULL; + + if (bStopParsing) + return NULL; + +#ifdef HAVE_EXPAT + if (nFeatureTabIndex < nFeatureTabLength) + { + return ppoFeatureTab[nFeatureTabIndex++]; + } + + if (VSIFEofL(fpGPX)) + return NULL; + + char aBuf[BUFSIZ]; + + CPLFree(ppoFeatureTab); + ppoFeatureTab = NULL; + nFeatureTabLength = 0; + nFeatureTabIndex = 0; + nWithoutEventCounter = 0; + + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpGPX ); + nDone = VSIFEofL(fpGPX); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of GPX file failed : %s at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + bStopParsing = TRUE; + break; + } + nWithoutEventCounter ++; + } while (!nDone && nFeatureTabLength == 0 && !bStopParsing && nWithoutEventCounter < 10); + + if (nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + return (nFeatureTabLength) ? ppoFeatureTab[nFeatureTabIndex++] : NULL; +#else + return NULL; +#endif +} + +/************************************************************************/ +/* OGRGPX_GetXMLCompatibleTagName() */ +/************************************************************************/ + +static char* OGRGPX_GetXMLCompatibleTagName(const char* pszExtensionsNS, + const char* pszName) +{ + /* Skip "ogr_" for example if NS is "ogr". Useful for GPX -> GPX roundtrip */ + if (strncmp(pszName, pszExtensionsNS, strlen(pszExtensionsNS)) == 0 && + pszName[strlen(pszExtensionsNS)] == '_') + { + pszName += strlen(pszExtensionsNS) + 1; + } + + char* pszModName = CPLStrdup(pszName); + int i; + for(i=0;pszModName[i] != 0;i++) + { + if (pszModName[i] == ' ') + pszModName[i] = '_'; + } + return pszModName; +} + +/************************************************************************/ +/* OGRGPX_GetUTF8String() */ +/************************************************************************/ + +static char* OGRGPX_GetUTF8String(const char* pszString) +{ + char *pszEscaped; + if (!CPLIsUTF8(pszString, -1) && + CSLTestBoolean(CPLGetConfigOption("OGR_FORCE_ASCII", "YES"))) + { + static int bFirstTime = TRUE; + if (bFirstTime) + { + bFirstTime = FALSE; + CPLError(CE_Warning, CPLE_AppDefined, + "%s is not a valid UTF-8 string. Forcing it to ASCII.\n" + "If you still want the original string and change the XML file encoding\n" + "afterwards, you can define OGR_FORCE_ASCII=NO as configuration option.\n" + "This warning won't be issued anymore", pszString); + } + else + { + CPLDebug("OGR", "%s is not a valid UTF-8 string. Forcing it to ASCII", + pszString); + } + pszEscaped = CPLForceToASCII(pszString, -1, '?'); + } + else + pszEscaped = CPLStrdup(pszString); + + return pszEscaped; +} + +/************************************************************************/ +/* OGRGPX_WriteXMLExtension() */ +/************************************************************************/ + +int OGRGPXLayer::OGRGPX_WriteXMLExtension(const char* pszTagName, + const char* pszContent) +{ + CPLXMLNode* poXML = CPLParseXMLString(pszContent); + if (poXML) + { + char* pszTagNameWithNS; + const char* pszXMLNS = NULL; + const char* pszUnderscore = strchr(pszTagName, '_'); + pszTagNameWithNS = CPLStrdup(pszTagName); + if (pszUnderscore) + pszTagNameWithNS[pszUnderscore - pszTagName] = ':'; + + /* If we detect a Garmin GPX extension, add its xmlns */ + if (strcmp(pszTagName, "gpxx_WaypointExtension") == 0) + pszXMLNS = " xmlns:gpxx=\"http://www.garmin.com/xmlschemas/GpxExtensions/v3\""; + + /* Don't XML escape here */ + char *pszUTF8 = OGRGPX_GetUTF8String( pszContent ); + poDS->PrintLine(" <%s%s>%s", + pszTagNameWithNS, (pszXMLNS) ? pszXMLNS : "", pszUTF8, pszTagNameWithNS); + CPLFree(pszUTF8); + + CPLFree(pszTagNameWithNS); + CPLDestroyXMLNode(poXML); + + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* WriteFeatureAttributes() */ +/************************************************************************/ + +static void AddIdent(VSILFILE* fp, int nIdentLevel) +{ + int i; + for(i=0;iGetOutputFP(); + int i; + + /* Begin with standard GPX fields */ + for(i=iFirstGPXField;iGetFieldDefn( i ); + if( poFeature->IsFieldSet( i ) ) + { + const char* pszName = poFieldDefn->GetNameRef(); + if (strcmp(pszName, "time") == 0) + { + char* pszDate = OGRGetXMLDateTime(poFeature->GetRawFieldRef(i)); + AddIdent(fp, nIdentLevel); + poDS->PrintLine("", pszDate); + CPLFree(pszDate); + } + else if (strncmp(pszName, "link", 4) == 0) + { + if (strstr(pszName, "href")) + { + AddIdent(fp, nIdentLevel); + VSIFPrintfL(fp, "", poFeature->GetFieldAsString( i )); + if( poFeature->IsFieldSet( i + 1 ) ) + VSIFPrintfL(fp, "%s", poFeature->GetFieldAsString( i + 1 )); + if( poFeature->IsFieldSet( i + 2 ) ) + VSIFPrintfL(fp, "%s", poFeature->GetFieldAsString( i + 2 )); + poDS->PrintLine(""); + } + } + else if (poFieldDefn->GetType() == OFTReal) + { + char szValue[64]; + OGRFormatDouble(szValue, sizeof(szValue), poFeature->GetFieldAsDouble(i), '.'); + AddIdent(fp, nIdentLevel); + poDS->PrintLine("<%s>%s", pszName, szValue, pszName); + } + else + { + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( i )); + AddIdent(fp, nIdentLevel); + poDS->PrintLine("<%s>%s", pszName, pszValue, pszName); + CPLFree(pszValue); + } + } + } + + /* Write "extra" fields within the tag */ + int n = poFeatureDefn->GetFieldCount(); + if (i < n) + { + const char* pszExtensionsNS = poDS->GetExtensionsNS(); + AddIdent(fp, nIdentLevel); + poDS->PrintLine(""); + for(;iGetFieldDefn( i ); + if( poFeature->IsFieldSet( i ) ) + { + char* compatibleName = + OGRGPX_GetXMLCompatibleTagName(pszExtensionsNS, poFieldDefn->GetNameRef()); + + if (poFieldDefn->GetType() == OFTReal) + { + char szValue[64]; + OGRFormatDouble(szValue, sizeof(szValue), poFeature->GetFieldAsDouble(i), '.'); + AddIdent(fp, nIdentLevel + 1); + poDS->PrintLine("<%s:%s>%s", + pszExtensionsNS, + compatibleName, + szValue, + pszExtensionsNS, + compatibleName); + } + else + { + const char *pszRaw = poFeature->GetFieldAsString( i ); + + /* Try to detect XML content */ + if (pszRaw[0] == '<' && pszRaw[strlen(pszRaw) - 1] == '>') + { + if (OGRGPX_WriteXMLExtension( compatibleName, pszRaw)) + continue; + } + + /* Try to detect XML escaped content */ + else if (strncmp(pszRaw, "<", 4) == 0 && + strncmp(pszRaw + strlen(pszRaw) - 4, ">", 4) == 0) + { + char* pszUnescapedContent = CPLUnescapeString( pszRaw, NULL, CPLES_XML ); + + if (OGRGPX_WriteXMLExtension(compatibleName, pszUnescapedContent)) + { + CPLFree(pszUnescapedContent); + continue; + } + + CPLFree(pszUnescapedContent); + } + + /* Remove leading spaces for a numeric field */ + if (poFieldDefn->GetType() == OFTInteger || poFieldDefn->GetType() == OFTReal) + { + while( *pszRaw == ' ' ) + pszRaw++; + } + + char *pszEscaped = OGRGetXML_UTF8_EscapedString( pszRaw ); + AddIdent(fp, nIdentLevel + 1); + poDS->PrintLine("<%s:%s>%s", + pszExtensionsNS, + compatibleName, + pszEscaped, + pszExtensionsNS, + compatibleName); + CPLFree(pszEscaped); + } + CPLFree(compatibleName); + } + } + AddIdent(fp, nIdentLevel); + poDS->PrintLine(""); + } +} + +/************************************************************************/ +/* CheckAndFixCoordinatesValidity() */ +/************************************************************************/ + +OGRErr OGRGPXLayer::CheckAndFixCoordinatesValidity( double* pdfLatitude, double* pdfLongitude ) +{ + if (pdfLatitude != NULL && (*pdfLatitude < -90 || *pdfLatitude > 90)) + { + static int bFirstWarning = TRUE; + if (bFirstWarning) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Latitude %f is invalid. Valid range is [-90,90]. This warning will not be issued any more", + *pdfLatitude); + bFirstWarning = FALSE; + } + return CE_Failure; + } + + if (pdfLongitude != NULL && (*pdfLongitude < -180 || *pdfLongitude > 180)) + { + static int bFirstWarning = TRUE; + if (bFirstWarning) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Longitude %f has been modified to fit into range [-180,180]. This warning will not be issued any more", + *pdfLongitude); + bFirstWarning = FALSE; + } + + if (*pdfLongitude > 180) + *pdfLongitude -= ((int) ((*pdfLongitude+180)/360)*360); + else if (*pdfLongitude < -180) + *pdfLongitude += ((int) (180 - *pdfLongitude)/360)*360; + + return CE_None; + } + + return CE_None; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRGPXLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + VSILFILE* fp = poDS->GetOutputFP(); + if (fp == NULL) + return CE_Failure; + + char szLat[64]; + char szLon[64]; + char szAlt[64]; + + OGRGeometry *poGeom = poFeature->GetGeometryRef(); + + if (gpxGeomType == GPX_WPT) + { + if (poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Cannot write a 'wpt' element after a 'rte' element.\n"); + return OGRERR_FAILURE; + } + else + if (poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Cannot write a 'wpt' element after a 'trk' element.\n"); + return OGRERR_FAILURE; + } + + poDS->SetLastGPXGeomTypeWritten(gpxGeomType); + + if ( poGeom == NULL || wkbFlatten(poGeom->getGeometryType()) != wkbPoint ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Features without geometry or with non-ponctual geometries not supported by GPX writer in waypoints layer." ); + return OGRERR_FAILURE; + } + + if ( poGeom->getCoordinateDimension() == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "POINT EMPTY geometries not supported by GPX writer." ); + return OGRERR_FAILURE; + } + + OGRPoint* point = (OGRPoint*)poGeom; + double lat = point->getY(); + double lon = point->getX(); + CheckAndFixCoordinatesValidity(&lat, &lon); + poDS->AddCoord(lon, lat); + OGRFormatDouble(szLat, sizeof(szLat), lat, '.'); + OGRFormatDouble(szLon, sizeof(szLon), lon, '.'); + poDS->PrintLine("", szLat, szLon); + WriteFeatureAttributes(poFeature); + poDS->PrintLine(""); + } + else if (gpxGeomType == GPX_ROUTE) + { + if (poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK || + poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK_POINT) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Cannot write a 'rte' element after a 'trk' element.\n"); + return OGRERR_FAILURE; + } + + if (poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE_POINT && poDS->nLastRteId != -1) + { + poDS->PrintLine(""); + poDS->nLastRteId = -1; + } + + poDS->SetLastGPXGeomTypeWritten(gpxGeomType); + + OGRLineString* line = NULL; + + if ( poGeom == NULL ) + { + poDS->PrintLine(""); + WriteFeatureAttributes(poFeature); + poDS->PrintLine(""); + return OGRERR_NONE; + } + + switch( poGeom->getGeometryType() ) + { + case wkbLineString: + case wkbLineString25D: + { + line = (OGRLineString*)poGeom; + break; + } + + case wkbMultiLineString: + case wkbMultiLineString25D: + { + int nGeometries = ((OGRGeometryCollection*)poGeom)->getNumGeometries (); + if (nGeometries == 0) + { + line = NULL; + } + else if (nGeometries == 1) + { + line = (OGRLineString*) ( ((OGRGeometryCollection*)poGeom)->getGeometryRef(0) ); + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Multiline with more than one line is not supported for 'rte' element.\n"); + return OGRERR_FAILURE; + } + break; + } + + default: + { + CPLError( CE_Failure, CPLE_NotSupported, + "Geometry type of `%s' not supported for 'rte' element.\n", + OGRGeometryTypeToName(poGeom->getGeometryType()) ); + return OGRERR_FAILURE; + } + } + + int n = (line) ? line->getNumPoints() : 0; + int i; + poDS->PrintLine(""); + WriteFeatureAttributes(poFeature); + for(i=0;igetY(i); + double lon = line->getX(i); + CheckAndFixCoordinatesValidity(&lat, &lon); + poDS->AddCoord(lon, lat); + OGRFormatDouble(szLat, sizeof(szLat), lat, '.'); + OGRFormatDouble(szLon, sizeof(szLon), lon, '.'); + poDS->PrintLine(" ", szLat, szLon); + if (poGeom->getGeometryType() == wkbLineString25D || + poGeom->getGeometryType() == wkbMultiLineString25D) + { + OGRFormatDouble(szAlt, sizeof(szAlt), line->getZ(i), '.'); + poDS->PrintLine(" %s", szAlt); + } + poDS->PrintLine(" "); + } + poDS->PrintLine(""); + } + else if (gpxGeomType == GPX_TRACK) + { + if (poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE_POINT && poDS->nLastRteId != -1) + { + poDS->PrintLine(""); + poDS->nLastRteId = -1; + } + if (poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK_POINT && poDS->nLastTrkId != -1) + { + poDS->PrintLine(" "); + poDS->PrintLine(""); + poDS->nLastTrkId = -1; + poDS->nLastTrkSegId = -1; + } + + poDS->SetLastGPXGeomTypeWritten(gpxGeomType); + + if (poGeom == NULL) + { + poDS->PrintLine(""); + WriteFeatureAttributes(poFeature); + poDS->PrintLine(""); + return OGRERR_NONE; + } + + switch( poGeom->getGeometryType() ) + { + case wkbLineString: + case wkbLineString25D: + { + OGRLineString* line = (OGRLineString*)poGeom; + int n = line->getNumPoints(); + int i; + poDS->PrintLine(""); + WriteFeatureAttributes(poFeature); + poDS->PrintLine(" "); + for(i=0;igetY(i); + double lon = line->getX(i); + CheckAndFixCoordinatesValidity(&lat, &lon); + poDS->AddCoord(lon, lat); + OGRFormatDouble(szLat, sizeof(szLat), lat, '.'); + OGRFormatDouble(szLon, sizeof(szLon), lon, '.'); + poDS->PrintLine(" ", szLat, szLon); + if (line->getGeometryType() == wkbLineString25D) + { + OGRFormatDouble(szAlt, sizeof(szAlt), line->getZ(i), '.'); + poDS->PrintLine(" %s", szAlt); + } + poDS->PrintLine(" "); + } + poDS->PrintLine(" "); + poDS->PrintLine(""); + break; + } + + case wkbMultiLineString: + case wkbMultiLineString25D: + { + int nGeometries = ((OGRGeometryCollection*)poGeom)->getNumGeometries (); + poDS->PrintLine(""); + WriteFeatureAttributes(poFeature); + int j; + for(j=0;jgetGeometryRef(j) ); + int n = (line) ? line->getNumPoints() : 0; + int i; + poDS->PrintLine(" "); + for(i=0;igetY(i); + double lon = line->getX(i); + CheckAndFixCoordinatesValidity(&lat, &lon); + poDS->AddCoord(lon, lat); + OGRFormatDouble(szLat, sizeof(szLat), lat, '.'); + OGRFormatDouble(szLon, sizeof(szLon), lon, '.'); + poDS->PrintLine(" ", szLat, szLon); + if (line->getGeometryType() == wkbLineString25D) + { + OGRFormatDouble(szAlt, sizeof(szAlt), line->getZ(i), '.'); + poDS->PrintLine(" %s", szAlt); + } + poDS->PrintLine(" "); + } + poDS->PrintLine(" "); + } + poDS->PrintLine(""); + break; + } + + default: + { + CPLError( CE_Failure, CPLE_NotSupported, + "Geometry type of `%s' not supported for 'trk' element.\n", + OGRGeometryTypeToName(poGeom->getGeometryType()) ); + return OGRERR_FAILURE; + } + } + } + else if (gpxGeomType == GPX_ROUTE_POINT) + { + if (poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK || + poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK_POINT) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Cannot write a 'rte' element after a 'trk' element.\n"); + return OGRERR_FAILURE; + } + + if ( poGeom == NULL || wkbFlatten(poGeom->getGeometryType()) != wkbPoint ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Features without geometry or with non-ponctual geometries not supported by GPX writer in route_points layer." ); + return OGRERR_FAILURE; + } + + if ( poGeom->getCoordinateDimension() == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "POINT EMPTY geometries not supported by GPX writer." ); + return OGRERR_FAILURE; + } + + if ( !poFeature->IsFieldSet(FLD_ROUTE_FID) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Field %s must be set.", poFeatureDefn->GetFieldDefn(FLD_ROUTE_FID)->GetNameRef() ); + return OGRERR_FAILURE; + } + if ( poFeature->GetFieldAsInteger(FLD_ROUTE_FID) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value for field %s.", poFeatureDefn->GetFieldDefn(FLD_ROUTE_FID)->GetNameRef() ); + return OGRERR_FAILURE; + } + + poDS->SetLastGPXGeomTypeWritten(gpxGeomType); + + if ( poDS->nLastRteId != poFeature->GetFieldAsInteger(FLD_ROUTE_FID)) + { + if (poDS->nLastRteId != -1) + { + poDS->PrintLine(""); + } + poDS->PrintLine(""); + if ( poFeature->IsFieldSet(FLD_ROUTE_NAME) ) + { + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( FLD_ROUTE_NAME )); + poDS->PrintLine(" <%s>%s", + "name", pszValue, "name"); + CPLFree(pszValue); + } + } + + poDS->nLastRteId = poFeature->GetFieldAsInteger(FLD_ROUTE_FID); + + OGRPoint* point = (OGRPoint*)poGeom; + double lat = point->getY(); + double lon = point->getX(); + CheckAndFixCoordinatesValidity(&lat, &lon); + poDS->AddCoord(lon, lat); + OGRFormatDouble(szLat, sizeof(szLat), lat, '.'); + OGRFormatDouble(szLon, sizeof(szLon), lon, '.'); + poDS->PrintLine(" ", szLat, szLon); + WriteFeatureAttributes(poFeature, 2); + poDS->PrintLine(" "); + + } + else + { + if (poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE_POINT && poDS->nLastRteId != -1) + { + poDS->PrintLine(""); + poDS->nLastRteId = -1; + } + + if ( poGeom == NULL || wkbFlatten(poGeom->getGeometryType()) != wkbPoint ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Features without geometry or with non-ponctual geometries not supported by GPX writer in track_points layer." ); + return OGRERR_FAILURE; + } + + if ( poGeom->getCoordinateDimension() == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "POINT EMPTY geometries not supported by GPX writer." ); + return OGRERR_FAILURE; + } + + if ( !poFeature->IsFieldSet(FLD_TRACK_FID) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Field %s must be set.", poFeatureDefn->GetFieldDefn(FLD_TRACK_FID)->GetNameRef() ); + return OGRERR_FAILURE; + } + if ( poFeature->GetFieldAsInteger(FLD_TRACK_FID) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value for field %s.", poFeatureDefn->GetFieldDefn(FLD_TRACK_FID)->GetNameRef() ); + return OGRERR_FAILURE; + } + if ( !poFeature->IsFieldSet(FLD_TRACK_SEG_ID) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Field %s must be set.", poFeatureDefn->GetFieldDefn(FLD_TRACK_SEG_ID)->GetNameRef() ); + return OGRERR_FAILURE; + } + if ( poFeature->GetFieldAsInteger(FLD_TRACK_SEG_ID) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value for field %s.", poFeatureDefn->GetFieldDefn(FLD_TRACK_SEG_ID)->GetNameRef() ); + return OGRERR_FAILURE; + } + + poDS->SetLastGPXGeomTypeWritten(gpxGeomType); + + if ( poDS->nLastTrkId != poFeature->GetFieldAsInteger(FLD_TRACK_FID)) + { + if (poDS->nLastTrkId != -1) + { + poDS->PrintLine(" "); + poDS->PrintLine(""); + } + poDS->PrintLine(""); + + if ( poFeature->IsFieldSet(FLD_TRACK_NAME) ) + { + char* pszValue = + OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( FLD_TRACK_NAME )); + poDS->PrintLine(" <%s>%s", + "name", pszValue, "name"); + CPLFree(pszValue); + } + + poDS->PrintLine(" "); + } + else if (poDS->nLastTrkSegId != poFeature->GetFieldAsInteger(FLD_TRACK_SEG_ID)) + { + poDS->PrintLine(" "); + poDS->PrintLine(" "); + } + + poDS->nLastTrkId = poFeature->GetFieldAsInteger(FLD_TRACK_FID); + poDS->nLastTrkSegId = poFeature->GetFieldAsInteger(FLD_TRACK_SEG_ID); + + OGRPoint* point = (OGRPoint*)poGeom; + double lat = point->getY(); + double lon = point->getX(); + CheckAndFixCoordinatesValidity(&lat, &lon); + poDS->AddCoord(lon, lat); + OGRFormatDouble(szLat, sizeof(szLat), lat, '.'); + OGRFormatDouble(szLon, sizeof(szLon), lon, '.'); + poDS->PrintLine(" ", szLat, szLon); + WriteFeatureAttributes(poFeature, 3); + poDS->PrintLine(" "); + } + + return OGRERR_NONE; +} + + + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + + +OGRErr OGRGPXLayer::CreateField( OGRFieldDefn *poField, + CPL_UNUSED int bApproxOK ) +{ + for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + if (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), + poField->GetNameRef() ) == 0) + { + return OGRERR_NONE; + } + } + if (poDS->GetUseExtensions() == FALSE) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Field of name '%s' is not supported in GPX schema. " + "Use GPX_USE_EXTENSIONS creation option to allow use of the element.", + poField->GetNameRef()); + return OGRERR_FAILURE; + } + else + { + poFeatureDefn->AddFieldDefn( poField ); + return OGRERR_NONE; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGPXLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCSequentialWrite) ) + return bWriteMode; + else if( EQUAL(pszCap,OLCCreateField) ) + return bWriteMode; + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + + else + return FALSE; +} + + +/************************************************************************/ +/* LoadExtensionsSchema() */ +/************************************************************************/ + +#ifdef HAVE_EXPAT + +static void XMLCALL startElementLoadSchemaCbk(void *pUserData, const char *pszName, const char **ppszAttr) +{ + ((OGRGPXLayer*)pUserData)->startElementLoadSchemaCbk(pszName, ppszAttr); +} + +static void XMLCALL endElementLoadSchemaCbk(void *pUserData, const char *pszName) +{ + ((OGRGPXLayer*)pUserData)->endElementLoadSchemaCbk(pszName); +} + +static void XMLCALL dataHandlerLoadSchemaCbk(void *pUserData, const char *data, int nLen) +{ + ((OGRGPXLayer*)pUserData)->dataHandlerLoadSchemaCbk(data, nLen); +} + + +/** This function parses the whole file to detect the extensions fields */ +void OGRGPXLayer::LoadExtensionsSchema() +{ + oSchemaParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oSchemaParser, ::startElementLoadSchemaCbk, ::endElementLoadSchemaCbk); + XML_SetCharacterDataHandler(oSchemaParser, ::dataHandlerLoadSchemaCbk); + XML_SetUserData(oSchemaParser, this); + + VSIFSeekL( fpGPX, 0, SEEK_SET ); + + inInterestingElement = FALSE; + inExtensions = FALSE; + depthLevel = 0; + currentFieldDefn = NULL; + pszSubElementName = NULL; + pszSubElementValue = NULL; + nSubElementValueLen = 0; + nWithoutEventCounter = 0; + bStopParsing = FALSE; + + char aBuf[BUFSIZ]; + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpGPX ); + nDone = VSIFEofL(fpGPX); + if (XML_Parse(oSchemaParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of GPX file failed : %s at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(oSchemaParser)), + (int)XML_GetCurrentLineNumber(oSchemaParser), + (int)XML_GetCurrentColumnNumber(oSchemaParser)); + bStopParsing = TRUE; + break; + } + nWithoutEventCounter ++; + } while (!nDone && !bStopParsing && nWithoutEventCounter < 10); + + if (nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + XML_ParserFree(oSchemaParser); + oSchemaParser = NULL; + + VSIFSeekL( fpGPX, 0, SEEK_SET ); +} + + +/************************************************************************/ +/* startElementLoadSchemaCbk() */ +/************************************************************************/ + + +void OGRGPXLayer::startElementLoadSchemaCbk(const char *pszName, + CPL_UNUSED const char **ppszAttr) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + if (gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0) + { + inInterestingElement = TRUE; + inExtensions = FALSE; + interestingDepthLevel = depthLevel; + } + else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0) + { + inInterestingElement = TRUE; + inExtensions = FALSE; + interestingDepthLevel = depthLevel; + } + else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0) + { + inInterestingElement = TRUE; + inExtensions = FALSE; + interestingDepthLevel = depthLevel; + } + else if (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0) + { + inInterestingElement = TRUE; + inExtensions = FALSE; + interestingDepthLevel = depthLevel; + } + else if (gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0) + { + inInterestingElement = TRUE; + inExtensions = FALSE; + interestingDepthLevel = depthLevel; + } + else if (inInterestingElement) + { + if (depthLevel == interestingDepthLevel + 1 && + strcmp(pszName, "extensions") == 0) + { + inExtensions = TRUE; + extensionsDepthLevel = depthLevel; + } + else if (inExtensions && depthLevel == extensionsDepthLevel + 1) + { + CPLFree(pszSubElementName); + pszSubElementName = CPLStrdup(pszName); + + int iField; + for(iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + int bMatch; + if (iField >= nGPXFields) + { + char* pszCompatibleName = OGRGPX_GetOGRCompatibleTagName(pszName); + bMatch = (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), pszCompatibleName ) == 0); + CPLFree(pszCompatibleName); + } + else + bMatch = (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), pszName ) == 0); + + if (bMatch) + { + currentFieldDefn = poFeatureDefn->GetFieldDefn(iField); + break; + } + } + if (iField == poFeatureDefn->GetFieldCount()) + { + char* pszCompatibleName = OGRGPX_GetOGRCompatibleTagName(pszName); + OGRFieldDefn newFieldDefn(pszCompatibleName, OFTInteger); + CPLFree(pszCompatibleName); + + poFeatureDefn->AddFieldDefn(&newFieldDefn); + currentFieldDefn = poFeatureDefn->GetFieldDefn(poFeatureDefn->GetFieldCount() - 1); + + if (poFeatureDefn->GetFieldCount() == 100) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too many fields. File probably corrupted"); + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + } + } + } + } + + depthLevel++; +} + + +/************************************************************************/ +/* endElementLoadSchemaCbk() */ +/************************************************************************/ + +static int OGRGPXIsInt(const char* pszStr) +{ + int i; + + while(*pszStr == ' ') + pszStr++; + + for(i=0;pszStr[i];i++) + { + if (pszStr[i] == '+' || pszStr[i] == '-') + { + if (i != 0) + return FALSE; + } + else if (!(pszStr[i] >= '0' && pszStr[i] <= '9')) + return FALSE; + } + return TRUE; +} + + +void OGRGPXLayer::endElementLoadSchemaCbk(const char *pszName) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + depthLevel--; + + if (inInterestingElement) + { + if (gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0) + { + inInterestingElement = FALSE; + inExtensions = FALSE; + } + else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0) + { + inInterestingElement = FALSE; + inExtensions = FALSE; + } + else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0) + { + inInterestingElement = FALSE; + inExtensions = FALSE; + } + else if (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0) + { + inInterestingElement = FALSE; + inExtensions = FALSE; + } + else if (gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0) + { + inInterestingElement = FALSE; + inExtensions = FALSE; + } + else if (depthLevel == interestingDepthLevel + 1 && + strcmp(pszName, "extensions") == 0) + { + inExtensions = FALSE; + } + else if (inExtensions && depthLevel == extensionsDepthLevel + 1 && + pszSubElementName && strcmp(pszName, pszSubElementName) == 0) + { + if (pszSubElementValue && nSubElementValueLen && currentFieldDefn) + { + pszSubElementValue[nSubElementValueLen] = 0; + if (currentFieldDefn->GetType() == OFTInteger || + currentFieldDefn->GetType() == OFTReal) + { + char* pszRemainingStr = NULL; + CPLStrtod(pszSubElementValue, &pszRemainingStr); + if (pszRemainingStr == NULL || + *pszRemainingStr == 0 || + *pszRemainingStr == ' ') + { + if (currentFieldDefn->GetType() == OFTInteger) + { + if (OGRGPXIsInt(pszSubElementValue) == FALSE) + { + currentFieldDefn->SetType(OFTReal); + } + } + } + else + { + currentFieldDefn->SetType(OFTString); + } + } + } + + CPLFree(pszSubElementName); + pszSubElementName = NULL; + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + currentFieldDefn = NULL; + } + } +} + +/************************************************************************/ +/* dataHandlerLoadSchemaCbk() */ +/************************************************************************/ + +void OGRGPXLayer::dataHandlerLoadSchemaCbk(const char *data, int nLen) +{ + if (bStopParsing) return; + + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)"); + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + + nWithoutEventCounter = 0; + + if (pszSubElementName) + { + char* pszNewSubElementValue = (char*) VSIRealloc(pszSubElementValue, nSubElementValueLen + nLen + 1); + if (pszNewSubElementValue == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + pszSubElementValue = pszNewSubElementValue; + memcpy(pszSubElementValue + nSubElementValueLen, data, nLen); + nSubElementValueLen += nLen; + if (nSubElementValueLen > 100000) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + } + } +} +#else +void OGRGPXLayer::LoadExtensionsSchema() +{ +} +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/GNUmakefile new file mode 100644 index 000000000..b12fd52c6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/GNUmakefile @@ -0,0 +1,12 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrgrassdriver.o ogrgrassdatasource.o ogrgrasslayer.o + +CPPFLAGS := -DUSE_CPL -DGRASS_GISBASE=\"$(GRASS_GISBASE)\" -I.. -I../.. $(GRASS_INCLUDE) $(PG_INC) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/drv_grass.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/drv_grass.html new file mode 100644 index 000000000..32524289b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/drv_grass.html @@ -0,0 +1,131 @@ + + +GRASS Vector Format + + + + +

    GRASS Vector Format

    + +GRASS driver can read GRASS (version 6.0 and higher) vector maps. +Each GRASS vector map is represented as one datasource. A GRASS +vector map may have 0, 1 or more layers. + +

    +GRASS points are represented as wkbPoint, lines and boundaries as +wkbLineString and areas as wkbPolygon. wkbMulti* and +wkbGeometryCollection are not used. More feature types can be mixed +in one layer. If a layer contains only features of one type, it is set +appropriately and can be retrieved by OGRLayer::GetLayerDefn(); + +

    +If a geometry has more categories of the same layer attached, its +represented as more features (one for each category). + +

    +Both 2D and 3D maps are supported. + +

    Datasource name

    + +Datasource name is full path to 'head' file in GRASS vector directory. +Using names of GRASS enviroment variables it can be expressed: +
    +   $GISDBASE/$LOCATION_NAME/$MAPSET/vector/mymap/head
    +
    +where 'mymap' is name of a vector map. For example: +
    +   /home/cimrman/grass_data/jizerky/jara/vector/liptakov/head
    +
    + +

    Layer names

    + +Usualy layer numbers are used as layer names. Layer number 0 is used +for all features without any category. It is possible to optionaly +give names to GRASS layers linked to database however currently this +is not supported by grass modules. A layer name can be added in +'dbln' vector file as '/name' after layer number, for example to +original record: + +
    +1 rivers cat $GISDBASE/$LOCATION_NAME/$MAPSET/dbf/ dbf
    +
    +it is possible to assign name 'rivers' +
    +1/rivers rivers cat $GISDBASE/$LOCATION_NAME/$MAPSET/dbf/ dbf
    +
    +the layer 1 will be listed is layer 'rivers'. + +

    Attribute filter

    + +If a layer has attributes stored in a database, the query is passed to +the underlaying database driver. That means, that SQL conditions which +can be used depend on the driver and database to which the layer is +linked. For example, DBF driver has currently very limited set of SQL +expressions and PostgreSQL offeres very rich set of SQL expressions. + +

    +If a layer has no attributes linked and it has only categories, OGR +internal SQL engine is used to evaluate the expression. Category is +an integer number attached to geometry, it is sort of ID, but it is +not FID as more features in one layer can have the same category. + +

    +Evaluation is done once when the attribute filter is set. + +

    Spatial filter

    + +Bounding boxes of features stored in topology structure are used to +evaluate if a features matches current spatial filter. + +

    +Evaluation is done once when the spatial filter is set. + +

    GISBASE

    + +GISBASE is full path to the directory where GRASS is installed. By +default, GRASS driver is using the path given to gdal configure +script. A different directory can be forced by setting GISBASE +enviroment variable. GISBASE is used to find GRASS database drivers. + +

    Missing topology

    + +GRASS driver can read GRASS vector files if topology is available (AKA +level 2). If an error is reported, telling that the topology is not +available, it is necessary to build topology within GRASS using +v.build module. + +

    Random access

    + +If random access (GetFeature instead of GetNextFeature) is used on +layer with attributes, the reading of features can be quite slow. It +is because the driver has to query attributes by category for each +feature (to avoid using a lot of memory) and random access to database +is usually slow. This can be improved on GRASS side optimizing/writing +file based (DBF,SQLite) drivers. + +

    Known problem

    + +Because of bug in GRASS library, it is impossible to start/stop +database drivers in FIFO order and FILO order must be used. The GRASS +driver for OGR is written with this limit in mind and drivers are +always closed if not used and if a driver remains opened kill() is +used to terminate it. It can happen however in rare cases, that the +driver will try to stop database driver which is not the last opened +and an application hangs. This can happen if sequential read +(GetNextFeature) of a layer is not finished (reading is stopped before +last available feature is reached), features from another layer are +read and then the reading of the first layer is finished, because in +that case kill() is not used. + +

    See Also

    + + + +
    +Development of this driver was financially supported by Faunalia +(www.faunalia.it). + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/ogrgrass.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/ogrgrass.h new file mode 100644 index 000000000..5abed1540 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/ogrgrass.h @@ -0,0 +1,186 @@ +/****************************************************************************** + * $Id: ogrgrass.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for OGR/GRASS driver. + * Author: Radim Blazek, radim.blazek@gmail.com + * + ****************************************************************************** + * Copyright (c) 2005, Radim Blazek + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRGRASS_H_INCLUDED +#define _OGRGRASS_H_INCLUDED + +#include "ogrsf_frmts.h" + +extern "C" { + #include + #include + #include + #include +#if GRASS_VERSION_MAJOR >= 7 + #include +#else + #include +#endif +} + +/************************************************************************/ +/* OGRGRASSLayer */ +/************************************************************************/ +class OGRGRASSLayer : public OGRLayer +{ + public: + OGRGRASSLayer( int layer, struct Map_info * map ); + ~OGRGRASSLayer(); + + // Layer info + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + GIntBig GetFeatureCount( int ); + OGRErr GetExtent(OGREnvelope *psExtent, int bForce); + virtual OGRSpatialReference *GetSpatialRef(); + int TestCapability( const char * ); + + // Reading + void ResetReading(); + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + OGRFeature * GetNextFeature(); + OGRFeature *GetFeature( GIntBig nFeatureId ); + + // Filters + virtual OGRErr SetAttributeFilter( const char *query ); + virtual void SetSpatialFilter( OGRGeometry * poGeomIn ); + + // Write access, not supported: + virtual OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK = TRUE ); + OGRErr ISetFeature( OGRFeature *poFeature ); + OGRErr ICreateFeature( OGRFeature *poFeature ); + + private: + char *pszName; + OGRSpatialReference *poSRS; + OGRFeatureDefn *poFeatureDefn; + char *pszQuery; // Attribute filter string + + int iNextId; + int nTotalCount; + int iLayer; // Layer number + int iLayerIndex; // Layer index (in GRASS category index) + int iCatField; // Field where category (key) is stored + int nFields; + int *paFeatureIndex; // Array of indexes to category index array + + // Vector map + struct Map_info *poMap; + struct field_info *poLink; + + // Database connection + bool bHaveAttributes; + + dbString *poDbString; + dbDriver *poDriver; + dbCursor *poCursor; + + bool bCursorOpened; // Sequential database cursor opened + int iCurrentCat; // Current category in select cursor + + struct line_pnts *poPoints; + struct line_cats *poCats; + + bool StartDbDriver (); + bool StopDbDriver (); + + OGRGeometry *GetFeatureGeometry ( long nFeatureId, int *cat ); + bool SetAttributes ( OGRFeature *feature, dbTable *table ); + + // Features matching spatial filter for ALL features/elements in GRASS + char *paSpatialMatch; + bool SetSpatialMatch(); + + // Features matching attribute filter for ALL features/elements in GRASS + char *paQueryMatch; + bool OpenSequentialCursor(); + bool ResetSequentialCursor(); + bool SetQueryMatch(); +}; + +/************************************************************************/ +/* OGRGRASSDataSource */ +/************************************************************************/ +class OGRGRASSDataSource : public OGRDataSource +{ + public: + OGRGRASSDataSource(); + ~OGRGRASSDataSource(); + + int Open( const char *, int bUpdate, int bTestOpen, + int bSingleNewFile = FALSE ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); + + // Not implemented (returns NULL): + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + + private: + OGRGRASSLayer **papoLayers; + char *pszName; // Date source name + char *pszGisdbase; // GISBASE + char *pszLocation; // location name + char *pszMapset; // mapset name + char *pszMap; // name of vector map + + struct Map_info map; + int nLayers; + + int bOpened; + + static bool SplitPath ( char *, char **, char **, char **, char ** ); +}; + +/************************************************************************/ +/* OGRGRASSDriver */ +/************************************************************************/ +class OGRGRASSDriver : public OGRSFDriver +{ + public: + ~OGRGRASSDriver(); + + const char *GetName(); + OGRDataSource *Open( const char *, int ); + + int TestCapability( const char * ); + + // Not implemented (return error/NULL): + virtual OGRDataSource *CreateDataSource( const char *pszName, + char ** = NULL ); + OGRErr DeleteDataSource( const char *pszDataSource ); +}; + +#endif /* ndef _OGRGRASS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/ogrgrassdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/ogrgrassdatasource.cpp new file mode 100644 index 000000000..8677c4714 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/ogrgrassdatasource.cpp @@ -0,0 +1,319 @@ +/****************************************************************************** + * $Id: ogrgrassdatasource.cpp 28534 2015-02-21 14:34:39Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGRASSDataSource class. + * Author: Radim Blazek, radim.blazek@gmail.com + * + ****************************************************************************** + * Copyright (c) 2005, Radim Blazek + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrgrass.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrgrassdatasource.cpp 28534 2015-02-21 14:34:39Z rouault $"); + +#if GRASS_VERSION_MAJOR >= 7 +#define G__setenv G_setenv_nogisrc +#endif + +/************************************************************************/ +/* Grass2CPLErrorHook() */ +/************************************************************************/ +int Grass2OGRErrorHook( char * pszMessage, int bFatal ) +{ + if( !bFatal ) + CPLError( CE_Warning, CPLE_AppDefined, "GRASS warning: %s", pszMessage ); + else + CPLError( CE_Warning, CPLE_AppDefined, "GRASS fatal error: %s", pszMessage ); + + return 0; +} + +/************************************************************************/ +/* OGRGRASSDataSource() */ +/************************************************************************/ +OGRGRASSDataSource::OGRGRASSDataSource() +{ + pszName = NULL; + pszGisdbase = NULL; + pszLocation = NULL; + pszMapset = NULL; + pszMap = NULL; + papoLayers = NULL; + nLayers = 0; + bOpened = FALSE; +} + +/************************************************************************/ +/* ~OGRGRASSDataSource() */ +/************************************************************************/ +OGRGRASSDataSource::~OGRGRASSDataSource() +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + if ( pszName ) CPLFree( pszName ); + if ( papoLayers ) CPLFree( papoLayers ); + if ( pszGisdbase ) G_free( pszGisdbase ); + if ( pszLocation ) G_free( pszLocation ); + if ( pszMapset ) G_free( pszMapset ); + if ( pszMap ) G_free( pszMap ); + + if (bOpened) + Vect_close(&map); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +#if (GRASS_VERSION_MAJOR >= 6 && GRASS_VERSION_MINOR >= 3) || GRASS_VERSION_MAJOR >= 7 +typedef int (*GrassErrorHandler)(const char *, int); +#else +typedef int (*GrassErrorHandler)(char *, int); +#endif + +int OGRGRASSDataSource::Open( const char * pszNewName, int bUpdate, + int bTestOpen, int bSingleNewFileIn ) +{ + VSIStatBuf stat; + + CPLAssert( nLayers == 0 ); + + pszName = CPLStrdup( pszNewName ); // Released by destructor + +/* -------------------------------------------------------------------- */ +/* Do the given path contains 'vector' and 'head'? */ +/* -------------------------------------------------------------------- */ + if ( strstr(pszName,"vector") == NULL || strstr(pszName,"head") == NULL ) + { + if( !bTestOpen ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s is not GRASS vector, access failed.\n", pszName ); + } + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Is the given a regular file? */ +/* -------------------------------------------------------------------- */ + if( CPLStat( pszName, &stat ) != 0 || !VSI_ISREG(stat.st_mode) ) + { + if( !bTestOpen ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s is not GRASS vector, access failed.\n", pszName ); + } + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Parse datasource name */ +/* -------------------------------------------------------------------- */ + if ( !SplitPath(pszName, &pszGisdbase, &pszLocation, + &pszMapset, &pszMap) ) + { + if( !bTestOpen ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s is not GRASS datasource name, access failed.\n", + pszName ); + } + return FALSE; + } + + CPLDebug ( "GRASS", "Gisdbase: %s", pszGisdbase ); + CPLDebug ( "GRASS", "Location: %s", pszLocation ); + CPLDebug ( "GRASS", "Mapset: %s", pszMapset ); + CPLDebug ( "GRASS", "Map: %s", pszMap ); + +/* -------------------------------------------------------------------- */ +/* Init GRASS library */ +/* -------------------------------------------------------------------- */ + // GISBASE is path to the directory where GRASS is installed, + // it is necessary because there are database drivers. + if ( !getenv( "GISBASE" ) ) { + static char* gisbaseEnv = NULL; + const char *gisbase = GRASS_GISBASE; + CPLError( CE_Warning, CPLE_AppDefined, "GRASS warning: GISBASE " + "environment variable was not set, using:\n%s", gisbase ); + char buf[2000]; + snprintf ( buf, sizeof(buf), "GISBASE=%s", gisbase ); + buf[sizeof(buf)-1] = '\0'; + + CPLFree(gisbaseEnv); + gisbaseEnv = CPLStrdup ( buf ); + putenv( gisbaseEnv ); + } + + // Don't use GISRC file and read/write GRASS variables + // (from location G_VAR_GISRC) to memory only. + G_set_gisrc_mode ( G_GISRC_MODE_MEMORY ); + + // Init GRASS libraries (required). G_no_gisinit() doesn't check + // write permissions for mapset compare to G_gisinit() + G_no_gisinit(); + + // Set error function + G_set_error_routine ( (GrassErrorHandler) Grass2OGRErrorHook ); + +/* -------------------------------------------------------------------- */ +/* Set GRASS variables */ +/* -------------------------------------------------------------------- */ + G__setenv( "GISDBASE", pszGisdbase ); + G__setenv( "LOCATION_NAME", pszLocation ); + G__setenv( "MAPSET", pszMapset); + G_reset_mapsets(); + G_add_mapset_to_search_path ( pszMapset ); + +/* -------------------------------------------------------------------- */ +/* Open GRASS vector map */ +/* -------------------------------------------------------------------- */ +#if GRASS_VERSION_MAJOR < 7 + Vect_set_fatal_error ( GV_FATAL_PRINT ); // Print error and continue +#endif + Vect_set_open_level (2); + int level = Vect_open_old ( &map, pszMap, pszMapset); + + if ( level < 2 ) { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot open GRASS vector %s on level 2.\n", pszName ); + return FALSE; + } + + CPLDebug ( "GRASS", "Num lines = %d", Vect_get_num_lines(&map) ); + +/* -------------------------------------------------------------------- */ +/* Build a list of layers. */ +/* -------------------------------------------------------------------- */ + int ncidx = Vect_cidx_get_num_fields ( &map ); + CPLDebug ( "GRASS", "Num layers = %d", ncidx ); + + for ( int i = 0; i < ncidx; i++ ) { + // Create the layer object + OGRGRASSLayer *poLayer; + + poLayer = new OGRGRASSLayer( i, &map ); + + // Add layer to data source layer list + papoLayers = (OGRGRASSLayer **) + CPLRealloc( papoLayers, sizeof(OGRGRASSLayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + } + + bOpened = TRUE; + + return TRUE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ +OGRLayer * +OGRGRASSDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ) + +{ + CPLError( CE_Failure, CPLE_NoWriteAccess, + "CreateLayer is not supported by GRASS driver" ); + + return NULL; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ +int OGRGRASSDataSource::TestCapability( const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ +OGRLayer *OGRGRASSDataSource::GetLayer( int iLayer ) +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* SplitPath() */ +/* Split full path to cell or group to: */ +/* gisdbase, location, mapset, name */ +/* New string are allocated and should be freed when no longer needed. */ +/* */ +/* Returns: true - OK */ +/* false - failed */ +/************************************************************************/ +bool OGRGRASSDataSource::SplitPath( char *path, char **gisdbase, + char **location, char **mapset, char **map ) +{ + char *p, *ptr[5], *tmp; + int i = 0; + + CPLDebug ( "GRASS", "OGRGRASSDataSource::SplitPath" ); + + *gisdbase = *location = *mapset = *map = NULL; + + if ( !path || strlen(path) == 0 ) + return false; + + tmp = G_store ( path ); + + while ( (p = strrchr(tmp,'/')) != NULL && i < 5 ) { + *p = '\0'; + + if ( strlen(p+1) == 0 ) /* repeated '/' */ + continue; + + ptr[i++] = p+1; + } + + /* Note: empty GISDBASE == 0 is not accepted (relative path) */ + if ( i != 5 ) { + free ( tmp ); + return false; + } + + if ( strcmp(ptr[0],"head") != 0 || strcmp(ptr[2],"vector") != 0 ) { + return false; + } + + *gisdbase = G_store ( tmp ); + *location = G_store ( ptr[4] ); + *mapset = G_store ( ptr[3] ); + *map = G_store ( ptr[1] ); + + free ( tmp ); + return true; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/ogrgrassdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/ogrgrassdriver.cpp new file mode 100644 index 000000000..999da00cd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/ogrgrassdriver.cpp @@ -0,0 +1,127 @@ +/****************************************************************************** + * $Id: ogrgrassdriver.cpp 28290 2015-01-05 13:16:48Z martinl $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGRASSDriver class. + * Author: Radim Blazek, radim.blazek@gmail.com + * + ****************************************************************************** + * Copyright (c) 2005, Radim Blazek + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrgrass.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrgrassdriver.cpp 28290 2015-01-05 13:16:48Z martinl $"); + +/************************************************************************/ +/* ~OGRGRASSDriver() */ +/************************************************************************/ +OGRGRASSDriver::~OGRGRASSDriver() +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ +const char *OGRGRASSDriver::GetName() +{ + return "OGR_GRASS"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ +OGRDataSource *OGRGRASSDriver::Open( const char * pszFilename, + int bUpdate ) +{ + OGRGRASSDataSource *poDS; + + poDS = new OGRGRASSDataSource(); + + if( !poDS->Open( pszFilename, bUpdate, TRUE ) ) + { + delete poDS; + return NULL; + } + else + { + return poDS; + } +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ +OGRDataSource *OGRGRASSDriver::CreateDataSource( const char * pszName, + char **papszOptions ) +{ + CPLError( CE_Failure, CPLE_AppDefined, + "CreateDataSource is not supported by GRASS driver.\n" ); + + return NULL; +} + +/************************************************************************/ +/* DeleteDataSource() */ +/************************************************************************/ +OGRErr OGRGRASSDriver::DeleteDataSource( const char *pszDataSource ) +{ + CPLError( CE_Failure, CPLE_AppDefined, + "DeleteDataSource is not supported by GRASS driver" ); + + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ +int OGRGRASSDriver::TestCapability( const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRGRASS() */ +/************************************************************************/ +void RegisterOGRGRASS() +{ + OGRGRASSDriver *poDriver; + + if (! GDAL_CHECK_VERSION("OGR/GRASS driver")) + return; + + if( GDALGetDriverByName( "OGR_GRASS" ) == NULL ) + { + poDriver = new OGRGRASSDriver(); + + poDriver->SetDescription( "GRASS" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GRASS Vectors (5.7+)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_grass.html" ); + + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/ogrgrasslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/ogrgrasslayer.cpp new file mode 100644 index 000000000..41855b30a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/grass/ogrgrasslayer.cpp @@ -0,0 +1,1094 @@ +/****************************************************************************** + * $Id: ogrgrasslayer.cpp 28831 2015-04-01 16:46:05Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRGRASSLayer class. + * Author: Radim Blazek, radim.blazek@gmail.com + * + ****************************************************************************** + * Copyright (c) 2005, Radim Blazek + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ogrgrass.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrgrasslayer.cpp 28831 2015-04-01 16:46:05Z rouault $"); + +/************************************************************************/ +/* OGRGRASSLayer() */ +/************************************************************************/ +OGRGRASSLayer::OGRGRASSLayer( int layerIndex, struct Map_info * map ) +{ + CPLDebug ( "GRASS", "OGRGRASSLayer::OGRGRASSLayer layerIndex = %d", layerIndex ); + + iLayerIndex = layerIndex; + poMap = map; + poSRS = NULL; + iNextId = 0; + poPoints = Vect_new_line_struct(); + poCats = Vect_new_cats_struct(); + pszQuery = NULL; + paQueryMatch = NULL; + paSpatialMatch = NULL; + + iLayer = Vect_cidx_get_field_number ( poMap, iLayerIndex); + CPLDebug ( "GRASS", "iLayer = %d", iLayer ); + + poLink = Vect_get_field ( poMap, iLayer ); // May be NULL if not defined + + // Layer name + if ( poLink && poLink->name ) + { + pszName = CPLStrdup( poLink->name ); + } + else + { + char buf[20]; + sprintf ( buf, "%d", iLayer ); + pszName = CPLStrdup( buf ); + } + + // Because we don't represent centroids as any simple feature, we have to scan + // category index and create index of feature IDs pointing to category index + nTotalCount = Vect_cidx_get_type_count(poMap,iLayer, GV_POINT|GV_LINES|GV_AREA); + CPLDebug ( "GRASS", "nTotalCount = %d", nTotalCount ); + paFeatureIndex = (int *) CPLMalloc ( nTotalCount * sizeof(int) ); + + int n = Vect_cidx_get_type_count(poMap,iLayer, GV_POINTS|GV_LINES|GV_AREA); + int cnt = 0; + for ( int i = 0; i < n; i++ ) + { + int cat,type, id; + + Vect_cidx_get_cat_by_index ( poMap, iLayerIndex, i, &cat, &type, &id ); + + if ( !( type & (GV_POINT|GV_LINES|GV_AREA) ) ) continue; + paFeatureIndex[cnt++] = i; + } + + poFeatureDefn = new OGRFeatureDefn( pszName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + // Get type definition + int nTypes = Vect_cidx_get_num_types_by_index ( poMap, iLayerIndex ); + int types = 0; + for ( int i = 0; i < nTypes; i++ ) { + int type, count; + Vect_cidx_get_type_count_by_index ( poMap, iLayerIndex, i, &type, &count); + if ( !(type & (GV_POINT|GV_LINES|GV_AREA) ) ) continue; + types |= type; + CPLDebug ( "GRASS", "type = %d types = %d", type, types ); + } + + OGRwkbGeometryType eGeomType = wkbUnknown; + if ( types == GV_LINE || types == GV_BOUNDARY || types == GV_LINES ) + { + eGeomType = wkbLineString; + } + else if ( types == GV_POINT ) + { + eGeomType = wkbPoint; + } + else if ( types == GV_AREA ) + { + CPLDebug ( "GRASS", "set wkbPolygon" ); + eGeomType = wkbPolygon; + } + + if (Vect_is_3d(poMap)) + poFeatureDefn->SetGeomType ( wkbSetZ(eGeomType) ); + else + poFeatureDefn->SetGeomType ( eGeomType ); + + // Get attributes definition + poDbString = (dbString*) CPLMalloc ( sizeof(dbString) ); + poCursor = (dbCursor*) CPLMalloc ( sizeof(dbCursor) ); + bCursorOpened = FALSE; + + poDriver = NULL; + bHaveAttributes = false; + db_init_string ( poDbString ); + if ( poLink ) + { + if ( StartDbDriver() ) + { + db_set_string ( poDbString, poLink->table ); + dbTable *table; + if ( db_describe_table ( poDriver, poDbString, &table) == DB_OK ) + { + nFields = db_get_table_number_of_columns ( table ); + iCatField = -1; + for ( int i = 0; i < nFields; i++) + { + dbColumn *column = db_get_table_column ( table, i ); + int ctype = db_sqltype_to_Ctype ( db_get_column_sqltype(column) ); + + OGRFieldType ogrFtype = OFTInteger; + switch ( ctype ) { + case DB_C_TYPE_INT: + ogrFtype = OFTInteger; + break; + case DB_C_TYPE_DOUBLE: + ogrFtype = OFTReal; + break; + case DB_C_TYPE_STRING: + ogrFtype = OFTString; + break; + case DB_C_TYPE_DATETIME: + ogrFtype = OFTDateTime; + break; + } + + CPLDebug ( "GRASS", "column = %s type = %d", + db_get_column_name(column), ctype ); + + OGRFieldDefn oField ( db_get_column_name(column), ogrFtype ); + poFeatureDefn->AddFieldDefn( &oField ); + + if ( G_strcasecmp(db_get_column_name(column),poLink->key) == 0 ) + { + iCatField = i; + } + } + if ( iCatField >= 0 ) + { + bHaveAttributes = true; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot find key field" ); + db_close_database_shutdown_driver ( poDriver ); + poDriver = NULL; + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot describe table %s", + poLink->table ); + + } + db_close_database_shutdown_driver ( poDriver ); + poDriver = NULL; + } + } + + if ( !bHaveAttributes && iLayer > 0 ) // Because features in layer 0 have no cats + { + OGRFieldDefn oField("cat", OFTInteger); + poFeatureDefn->AddFieldDefn( &oField ); + } + + if ( getenv("GISBASE") ) // We have some projection info in GISBASE + { + struct Key_Value *projinfo, *projunits; + + // Note: we dont have to reset GISDBASE and LOCATION_NAME because + // OGRGRASSLayer constructor is called from OGRGRASSDataSource::Open + // where those variables are set + + projinfo = G_get_projinfo(); + projunits = G_get_projunits(); + + char *srsWkt = GPJ_grass_to_wkt ( projinfo, projunits, 0, 0); + if ( srsWkt ) + { + poSRS = new OGRSpatialReference ( srsWkt ); + G_free ( srsWkt ); + } + + G_free_key_value(projinfo); + G_free_key_value(projunits); + } +} + +/************************************************************************/ +/* ~OGRGRASSLayer() */ +/************************************************************************/ +OGRGRASSLayer::~OGRGRASSLayer() +{ + if ( bCursorOpened ) + { + db_close_cursor ( poCursor); + } + + if ( poDriver ) + { + StopDbDriver(); + } + + if ( pszName ) CPLFree ( pszName ); + if ( poFeatureDefn ) + poFeatureDefn->Release(); + if ( poSRS ) + poSRS->Release(); + + if ( pszQuery ) CPLFree ( pszQuery ); + + if ( paFeatureIndex ) CPLFree ( paFeatureIndex ); + + if ( poLink ) G_free ( poLink ); + + Vect_destroy_line_struct ( poPoints ); + Vect_destroy_cats_struct ( poCats ); + + db_free_string ( poDbString ); + CPLFree ( poDbString ); + CPLFree ( poCursor ); + + if ( paSpatialMatch ) CPLFree ( paSpatialMatch ); + if ( paQueryMatch ) CPLFree ( paQueryMatch ); +} + +/************************************************************************/ +/* StartDbDriver */ +/************************************************************************/ +bool OGRGRASSLayer::StartDbDriver() +{ + CPLDebug ( "GRASS", "StartDbDriver()" ); + + bCursorOpened = false; + + if ( !poLink ) + { + return false; + } + poDriver = db_start_driver_open_database ( poLink->driver, poLink->database ); + + if ( poDriver == NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot open database %s by driver %s, " + "check if GISBASE environment variable is set, the driver is available " + " and the database is accessible.", poLink->driver, poLink->database ); + return false; + } + return true; +} + +/************************************************************************/ +/* StopDbDriver */ +/************************************************************************/ +bool OGRGRASSLayer::StopDbDriver() +{ + if ( !poDriver ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Driver is not started" ); + return true; // I think that true is OK here + } + + // TODO!!!: Because of bug in GRASS library it is impossible + // to stop drivers in FIFO order. Until this is fixed + // we have to use kill + CPLDebug ( "GRASS", "driver PID = %d", poDriver->pid ); + +#if defined(_WIN32) || defined(__WIN32__) + db_close_database_shutdown_driver ( poDriver ); +#else + if ( kill (poDriver->pid, SIGINT) != 0 ) + { + if ( kill (poDriver->pid, SIGKILL) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot stop database " + "driver pid = %d", poDriver->pid ); + } + } +#endif + + bCursorOpened = false; + + return true; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ +void OGRGRASSLayer::ResetReading() +{ + iNextId = 0; + + if ( bCursorOpened ) { + ResetSequentialCursor(); + } +} + +/************************************************************************/ +/* SetNextByIndex() */ +/* */ +/* If we already have an FID list, we can easily resposition */ +/* ourselves in it. */ +/************************************************************************/ +OGRErr OGRGRASSLayer::SetNextByIndex( GIntBig nIndex ) +{ + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL ) + { + iNextId = 0; + int count = 0; + + while ( true ) { + if( iNextId >= nTotalCount ) break; + if ( count == nIndex ) break; + + // Attributes + if( pszQuery != NULL && !paQueryMatch[iNextId] ) { + iNextId++; + continue; + } + + // Spatial + if( m_poFilterGeom && !paSpatialMatch[iNextId] ) { + iNextId++; + continue; + } + count++; + } + } + + iNextId = nIndex; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetAttributeFilter */ +/************************************************************************/ +OGRErr OGRGRASSLayer::SetAttributeFilter( const char *query ) +{ + CPLDebug ( "GRASS", "SetAttributeFilter: %s", query ); + + if ( query == NULL ) { + // Release old if any + if ( pszQuery ) { + CPLFree ( pszQuery ); + pszQuery = NULL; + } + if ( paQueryMatch ) { + CPLFree ( paQueryMatch ); + paQueryMatch = NULL; + } + return OGRERR_NONE; + } + + paQueryMatch = (char *) CPLMalloc ( nTotalCount ); + memset ( paQueryMatch, 0x0, nTotalCount ); + pszQuery = CPLStrdup(query); + + OGRLayer::SetAttributeFilter(query); // Otherwise crash on delete + + if ( bHaveAttributes ) { + + if ( !poDriver ) + { + StartDbDriver(); + } + + if ( poDriver ) + { + if ( bCursorOpened ) + { + db_close_cursor ( poCursor ); + bCursorOpened = false; + } + OpenSequentialCursor(); + if ( bCursorOpened ) + { + SetQueryMatch(); + db_close_cursor ( poCursor ); + bCursorOpened = false; + } + else + { + CPLFree ( pszQuery ); + pszQuery = NULL; + return OGRERR_FAILURE; + } + db_close_database_shutdown_driver ( poDriver ); + poDriver = NULL; + } + else + { + CPLFree ( pszQuery ); + pszQuery = NULL; + return OGRERR_FAILURE; + } + } + else + { + // Use OGR to evaluate category match + for ( int i = 0; i < nTotalCount; i++ ) + { + OGRFeature *feature = GetFeature(i); + CPLDebug ( "GRASS", "i = %d eval = %d", i, m_poAttrQuery->Evaluate ( feature ) ); + if ( m_poAttrQuery->Evaluate ( feature ) ) + { + paQueryMatch[i] = 1; + } + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetQueryMatch */ +/************************************************************************/ +bool OGRGRASSLayer::SetQueryMatch() +{ + CPLDebug ( "GRASS", "SetQueryMatch" ); + + // NOTE: we don't have to call ResetSequentialCursor() first because + // this method is called immediately after OpenSequentialCursor() + + if ( !bCursorOpened ) { + CPLError( CE_Failure, CPLE_AppDefined, "Cursor is not opened."); + return false; + } + + int more; + int cidx = 0; // index to category index + int fidx = 0; // index to feature index (paFeatureIndex) + // number of categories in category index + int ncats = Vect_cidx_get_num_cats_by_index ( poMap, iLayerIndex ); + dbTable *table = db_get_cursor_table ( poCursor ); + while ( true ) { + if( db_fetch ( poCursor, DB_NEXT, &more) != DB_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot fetch attributes."); + return false; + } + if ( !more ) break; + + dbColumn *column = db_get_table_column ( table, iCatField ); + dbValue *value = db_get_column_value ( column ); + int cat = db_get_value_int ( value ); + + // NOTE: because of bug in GRASS library it is impossible to use + // Vect_cidx_find_next + + // Go through category index until first record of current category + // is found or a category > current is found + int cidxcat, type, id; + while ( cidx < ncats ) { + Vect_cidx_get_cat_by_index ( poMap, iLayerIndex, cidx, + &cidxcat, &type, &id ); + + if ( cidxcat < cat ) { + cidx++; + continue; + } + if ( cidxcat > cat ) break; // Not found + + // We have the category we want, check type + if ( !(type & (GV_POINT|GV_LINES|GV_AREA)) ) + { + cidx++; + continue; + } + + // Both category and type match -> find feature and set it on + while ( true ) { + if ( fidx > nTotalCount || paFeatureIndex[fidx] > cidx ) { + // should not happen + break; + } + + if ( paFeatureIndex[fidx] == cidx ) { + paQueryMatch[fidx] = 1; + fidx++; + break; + } + fidx++; + } + cidx++; + } + + if ( id < 0 ) continue; // not found + } + + return true; +} + +/************************************************************************/ +/* OpenSequentialCursor */ +/************************************************************************/ +bool OGRGRASSLayer::OpenSequentialCursor() +{ + CPLDebug ( "GRASS", "OpenSequentialCursor: %s", pszQuery ); + + if ( !poDriver ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Driver not opened."); + return false; + } + + if ( bCursorOpened ) + { + db_close_cursor ( poCursor ); + bCursorOpened = false; + } + + char buf[2000]; + sprintf ( buf, "SELECT * FROM %s ", poLink->table ); + db_set_string ( poDbString, buf); + + if ( pszQuery ) { + sprintf ( buf, "WHERE %s ", pszQuery ); + db_append_string ( poDbString, buf); + } + + sprintf ( buf, "ORDER BY %s", poLink->key); + db_append_string ( poDbString, buf); + + CPLDebug ( "GRASS", "Query: %s", db_get_string(poDbString) ); + + if ( db_open_select_cursor ( poDriver, poDbString, + poCursor, DB_SCROLL) == DB_OK ) + { + iCurrentCat = -1; + bCursorOpened = true; + CPLDebug ( "GRASS", "num rows = %d", db_get_num_rows ( poCursor ) ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot open cursor."); + return false; + } + return true; +} + +/************************************************************************/ +/* ResetSequentialCursor */ +/************************************************************************/ +bool OGRGRASSLayer::ResetSequentialCursor() +{ + CPLDebug ( "GRASS", "ResetSequentialCursor" ); + + int more; + if( db_fetch ( poCursor, DB_FIRST, &more) != DB_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot reset cursor."); + return false; + } + if( db_fetch ( poCursor, DB_PREVIOUS, &more) != DB_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot reset cursor."); + return false; + } + return true; +} + +/************************************************************************/ +/* SetSpatialFilter */ +/************************************************************************/ +void OGRGRASSLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) +{ + CPLDebug ( "GRASS", "SetSpatialFilter" ); + + OGRLayer::SetSpatialFilter ( poGeomIn ); + + if ( poGeomIn == NULL ) { + // Release old if any + if ( paSpatialMatch ) { + CPLFree ( paSpatialMatch ); + paSpatialMatch = NULL; + } + return; + } + + SetSpatialMatch(); +} + +/************************************************************************/ +/* SetSpatialMatch */ +/************************************************************************/ +bool OGRGRASSLayer::SetSpatialMatch() +{ + CPLDebug ( "GRASS", "SetSpatialMatch" ); + + if ( !paSpatialMatch ) + { + paSpatialMatch = (char *) CPLMalloc ( nTotalCount ); + } + memset ( paSpatialMatch, 0x0, nTotalCount ); + + OGRGeometry *geom; + OGRLineString *lstring = new OGRLineString(); + lstring->setNumPoints ( 5 ); + geom = lstring; + + for ( int i = 0; i < nTotalCount; i++ ) { + int cidx = paFeatureIndex[i]; + + int cat, type, id; + + Vect_cidx_get_cat_by_index ( poMap, iLayerIndex, cidx, &cat, &type, &id ); + +#if GRASS_VERSION_MAJOR >= 7 + struct bound_box box; +#else + BOUND_BOX box; +#endif + + switch ( type ) + { + case GV_POINT: + case GV_LINE: + case GV_BOUNDARY: + Vect_get_line_box ( poMap, id, &box ); + break; + + case GV_AREA: + Vect_get_area_box ( poMap, id, &box ); + break; + } + + lstring->setPoint( 0, box.W, box.N, 0. ); + lstring->setPoint( 1, box.W, box.S, 0. ); + lstring->setPoint( 2, box.E, box.S, 0. ); + lstring->setPoint( 3, box.E, box.N, 0. ); + lstring->setPoint( 4, box.W, box.N, 0. ); + + if ( FilterGeometry(geom) ) { + CPLDebug ( "GRASS", "Feature %d in filter", i ); + paSpatialMatch[i] = 1; + } + } + delete lstring; + return true; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ +OGRFeature *OGRGRASSLayer::GetNextFeature() +{ + CPLDebug ( "GRASS", "OGRGRASSLayer::GetNextFeature" ); + OGRFeature *poFeature = NULL; + + int cat; + + // Get next iNextId + while ( true ) { + if( iNextId >= nTotalCount ) // No more features + { + // Close cursor / driver if opened + if ( bCursorOpened ) + { + db_close_cursor ( poCursor); + bCursorOpened = false; + } + if ( poDriver ) + { + db_close_database_shutdown_driver ( poDriver ); + poDriver = NULL; + } + + return NULL; + } + + // Attributes + if( pszQuery != NULL && !paQueryMatch[iNextId] ) { + iNextId++; + continue; + } + + // Spatial + if( m_poFilterGeom && !paSpatialMatch[iNextId] ) { + iNextId++; + continue; + } + + break; // Attributes & spatial filter match + } + + OGRGeometry *poOGR = GetFeatureGeometry ( iNextId, &cat ); + + poFeature = new OGRFeature( poFeatureDefn ); + poFeature->SetGeometryDirectly( poOGR ); + poFeature->SetFID ( iNextId ); + iNextId++; + + // Get attributes + CPLDebug ( "GRASS", "bHaveAttributes = %d", bHaveAttributes ); + if ( bHaveAttributes ) + { + if ( !poDriver ) + { + StartDbDriver(); + } + if ( poDriver ) { + if ( !bCursorOpened ) + { + OpenSequentialCursor(); + } + if ( bCursorOpened ) + { + dbTable *table = db_get_cursor_table ( poCursor ); + if ( iCurrentCat < cat ) + { + while ( true ) { + int more; + if( db_fetch ( poCursor, DB_NEXT, &more) != DB_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot fetch attributes."); + break; + } + if ( !more ) break; + + dbColumn *column = db_get_table_column ( table, iCatField ); + dbValue *value = db_get_column_value ( column ); + iCurrentCat = db_get_value_int ( value ); + + if ( iCurrentCat >= cat ) break; + } + } + if ( cat == iCurrentCat ) + { + SetAttributes ( poFeature, table ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, "Attributes not found."); + } + } + } + } + else if ( iLayer > 0 ) // Add category + { + poFeature->SetField( 0, cat ); + } + + m_nFeaturesRead++; + return poFeature; +} +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ +OGRFeature *OGRGRASSLayer::GetFeature( GIntBig nFeatureId ) + +{ + CPLDebug ( "GRASS", "OGRGRASSLayer::GetFeature nFeatureId = %ld", nFeatureId ); + + int cat; + OGRFeature *poFeature = NULL; + + OGRGeometry *poOGR = GetFeatureGeometry ( nFeatureId, &cat ); + + poFeature = new OGRFeature( poFeatureDefn ); + poFeature->SetGeometryDirectly( poOGR ); + poFeature->SetFID ( nFeatureId ); + + // Get attributes + if ( bHaveAttributes && !poDriver ) + { + StartDbDriver(); + } + if ( poDriver ) + { + if ( bCursorOpened ) + { + db_close_cursor ( poCursor); + bCursorOpened = false; + } + CPLDebug ( "GRASS", "Open cursor for key = %d", cat ); + char buf[2000]; + sprintf ( buf, "SELECT * FROM %s WHERE %s = %d", + poLink->table, poLink->key, cat ); + db_set_string ( poDbString, buf); + if ( db_open_select_cursor ( poDriver, poDbString, + poCursor, DB_SEQUENTIAL) == DB_OK ) + { + iCurrentCat = cat; // Not important + bCursorOpened = true; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot open cursor."); + } + + if ( bCursorOpened ) + { + int more; + if( db_fetch ( poCursor, DB_NEXT, &more) != DB_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot fetch attributes."); + } + else + { + if ( !more ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Attributes not found."); + } + else + { + dbTable *table = db_get_cursor_table ( poCursor ); + SetAttributes ( poFeature, table ); + } + } + db_close_cursor ( poCursor); + bCursorOpened = false; + } + } + else if ( iLayer > 0 ) // Add category + { + poFeature->SetField( 0, cat ); + } + + m_nFeaturesRead++; + return poFeature; +} + +/************************************************************************/ +/* GetFeatureGeometry() */ +/************************************************************************/ +OGRGeometry *OGRGRASSLayer::GetFeatureGeometry ( long nFeatureId, int *cat ) +{ + CPLDebug ( "GRASS", "OGRGRASSLayer::GetFeatureGeometry nFeatureId = %ld", nFeatureId ); + + int cidx = paFeatureIndex[(int)nFeatureId]; + + int type, id; + Vect_cidx_get_cat_by_index ( poMap, iLayerIndex, cidx, cat, &type, &id ); + + //CPLDebug ( "GRASS", "cat = %d type = %d id = %d", *cat, type, id ); + + OGRGeometry *poOGR = NULL; + int bIs3D = Vect_is_3d(poMap); + + switch ( type ) { + case GV_POINT: + { + Vect_read_line ( poMap, poPoints, poCats, id); + if (bIs3D) + poOGR = new OGRPoint( poPoints->x[0], poPoints->y[0], poPoints->z[0] ); + else + poOGR = new OGRPoint( poPoints->x[0], poPoints->y[0] ); + } + break; + + case GV_LINE: + case GV_BOUNDARY: + { + Vect_read_line ( poMap, poPoints, poCats, id); + OGRLineString *poOGRLine = new OGRLineString(); + if (bIs3D) + poOGRLine->setPoints( poPoints->n_points, + poPoints->x, poPoints->y, poPoints->z ); + else + poOGRLine->setPoints( poPoints->n_points, + poPoints->x, poPoints->y ); + + poOGR = poOGRLine; + } + break; + + case GV_AREA: + { + Vect_get_area_points ( poMap, id, poPoints ); + + OGRPolygon *poOGRPoly; + poOGRPoly = new OGRPolygon(); + + OGRLinearRing *poRing; + poRing = new OGRLinearRing(); + if (bIs3D) + poRing->setPoints( poPoints->n_points, + poPoints->x, poPoints->y, poPoints->z ); + else + poRing->setPoints( poPoints->n_points, + poPoints->x, poPoints->y ); + + poOGRPoly->addRingDirectly( poRing ); + + // Islands + int nisles = Vect_get_area_num_isles ( poMap, id ); + for ( int i = 0; i < nisles; i++ ) { + int isle = Vect_get_area_isle ( poMap, id, i ); + Vect_get_isle_points ( poMap, isle, poPoints ); + + poRing = new OGRLinearRing(); + if (bIs3D) + poRing->setPoints( poPoints->n_points, + poPoints->x, poPoints->y, poPoints->z ); + else + poRing->setPoints( poPoints->n_points, + poPoints->x, poPoints->y ); + + poOGRPoly->addRingDirectly( poRing ); + } + + poOGR = poOGRPoly; + } + break; + + default: // Should not happen + { + CPLError( CE_Failure, CPLE_AppDefined, "Unknown GRASS feature type."); + return NULL; + } + } + + return poOGR; +} + +/************************************************************************/ +/* SetAttributes() */ +/************************************************************************/ +bool OGRGRASSLayer::SetAttributes ( OGRFeature *poFeature, dbTable *table ) +{ + CPLDebug ( "GRASS", "OGRGRASSLayer::SetAttributes" ); + + for ( int i = 0; i < nFields; i++) + { + dbColumn *column = db_get_table_column ( table, i ); + dbValue *value = db_get_column_value ( column ); + + int ctype = db_sqltype_to_Ctype ( db_get_column_sqltype(column) ); + + if ( !db_test_value_isnull(value) ) + { + switch ( ctype ) { + case DB_C_TYPE_INT: + poFeature->SetField( i, db_get_value_int ( value )); + break; + case DB_C_TYPE_DOUBLE: + poFeature->SetField( i, db_get_value_double ( value )); + break; + case DB_C_TYPE_STRING: + poFeature->SetField( i, db_get_value_string ( value )); + break; + case DB_C_TYPE_DATETIME: + db_convert_column_value_to_string ( column, poDbString ); + poFeature->SetField( i, db_get_string ( poDbString )); + break; + } + } + + db_convert_column_value_to_string ( column, poDbString ); + //CPLDebug ( "GRASS", "val = %s", db_get_string ( poDbString )); + } + return true; +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ +OGRErr OGRGRASSLayer::ISetFeature( OGRFeature *poFeature ) +{ + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ +OGRErr OGRGRASSLayer::ICreateFeature( OGRFeature *poFeature ) +{ + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ +GIntBig OGRGRASSLayer::GetFeatureCount( int bForce ) +{ + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL ) + return OGRLayer::GetFeatureCount( bForce ); + + return nTotalCount; +} + +/************************************************************************/ +/* GetExtent() */ +/* */ +/* Fetch extent of the data currently stored in the dataset. */ +/* The bForce flag has no effect on SHO files since that value */ +/* is always in the header. */ +/* */ +/* Returns OGRERR_NONE/OGRRERR_FAILURE. */ +/************************************************************************/ +OGRErr OGRGRASSLayer::GetExtent (OGREnvelope *psExtent, int bForce) +{ +#if GRASS_VERSION_MAJOR >= 7 + struct bound_box box; +#else + BOUND_BOX box; +#endif + + Vect_get_map_box ( poMap, &box ); + + psExtent->MinX = box.W; + psExtent->MinY = box.S; + psExtent->MaxX = box.E; + psExtent->MaxY = box.N; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ +int OGRGRASSLayer::TestCapability( const char * pszCap ) +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastSetNextByIndex) ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ +OGRErr OGRGRASSLayer::CreateField( OGRFieldDefn *poField, int bApproxOK ) +{ + CPLError( CE_Failure, CPLE_NotSupported, + "Can't create fields on a GRASS layer.\n"); + + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ +OGRSpatialReference *OGRGRASSLayer::GetSpatialRef() +{ + return poSRS; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/GNUmakefile new file mode 100644 index 000000000..705b1f927 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrgtmdriver.o ogrgtmdatasource.o ogrgtmlayer.o gtm.o gtmwaypointlayer.o gtmtracklayer.o + +CPPFLAGS :=-I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): gtm.h ogr_gtm.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/drv_gtm.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/drv_gtm.html new file mode 100644 index 000000000..f45de3b50 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/drv_gtm.html @@ -0,0 +1,69 @@ + + +GTM - GPS TrackMaker + + + + +

    GTM - GPS TrackMaker

    + +(Starting with GDAL 1.7.0)

    + +

    +GPSTrackMaker is a program that is +compatible with more than 160 GPS models. It allows you to create your +own maps. It supports vector maps and images. +

    + +

    +The OGR driver has support for reading and writing GTM 211 files +(.gtm); however, in this implementation we are not supporting images +and routes. Waypoints and tracks are supported. +

    + +

    Although GTM has support for many data, like NAD 1967, SAD 1969, +and others, the output file of the OGR driver will be using WGS 1984. +And the GTM driver will only read properly GTM files georeferenced as WGS 1984 (if not +the case a warning will be issued). +

    + +

    The OGR driver supports just POINT, LINESTRING, and MULTILINESTRING.

    + +

    Example

    + +
  • The ogrinfo utility can be used to dump the content of a GTM datafile : + +
    +ogrinfo -ro -al input.gtm
    +
    +
  • +

    +
    +

    +
  • Use of -sql option to remap field names to the ones allowed by the GTM schema: +
    +ogr2ogr -f "GPSTrackMaker" output.gtm input.shp -sql "SELECT field1 AS name, field2 AS color, field3 AS type FROM input"
    +
    +
  • +

    +
    +

    + +
  • Example for translation from PostGIS to GTM: +
    +ogr2ogr -f "GPSTrackMaker" output.gtm PG:"host=hostaddress user=username dbname=db password=mypassword" -sql "select filed1 as name, field2 as color, field3 as type, wkb_geometry from input" -nlt MULTILINESTRING
    +
    +
    +Note : You need to specify the layer type as POINT, LINESTRING, or MULTILINESTRING. +
  • + + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/gtm.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/gtm.cpp new file mode 100644 index 000000000..b815e5aaf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/gtm.cpp @@ -0,0 +1,842 @@ +/****************************************************************************** + * $Id: gtm.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GTM Driver + * Purpose: Class for reading, parsing and handling a gtmfile. + * Author: Leonardo de Paula Rosa Piga; http://lampiao.lsc.ic.unicamp.br/~piga + * + ****************************************************************************** + * Copyright (c) 2009, Leonardo de Paula Rosa Piga + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "gtm.h" + + +/************************************************************************/ +/* Methods for dealing with write on files and buffers */ +/************************************************************************/ +void appendDouble(void* pBuffer, double val) +{ + CPL_LSBPTR64(&val); + memcpy(pBuffer, &val, 8); +} + +void appendFloat(void* pBuffer, float val) +{ + CPL_LSBPTR32(&val) + memcpy(pBuffer, &val, 4); +} + +void appendInt(void* pBuffer, int val) +{ + CPL_LSBPTR32(&val) + memcpy(pBuffer, &val, 4); +} + +void appendUChar(void* pBuffer, unsigned char val) +{ + memcpy(pBuffer, &val, 1); +} + +void appendUShort(void* pBuffer, unsigned short val) +{ + CPL_LSBPTR16(&val) + memcpy(pBuffer, &val, 2); +} + +void writeUChar(VSILFILE* fp, unsigned char val) +{ + VSIFWriteL(&val, 1, 1, fp); +} + +void writeDouble(VSILFILE* fp, double val) +{ + CPL_LSBPTR64(&val) + VSIFWriteL(&val, 1, 8, fp); +} + +static double readDouble(VSILFILE* fp) +{ + double val; + VSIFReadL( &val, 1, 8, fp ); + CPL_LSBPTR64(&val) + return val; +} + +static float readFloat(VSILFILE* fp) +{ + float val; + VSIFReadL( &val, 1, 4, fp ); + CPL_LSBPTR32(&val) + return val; +} + +static int readInt(VSILFILE* fp) +{ + int val; + VSIFReadL( &val, 1, 4, fp ); + CPL_LSBPTR32(&val) + return val; +} + +static unsigned char readUChar(VSILFILE* fp) +{ + unsigned char val; + VSIFReadL( &val, 1, 1, fp ); + return val; +} + +static unsigned short readUShort(VSILFILE* fp, int *pbSuccess = NULL) +{ + unsigned short val; + if (VSIFReadL( &val, 1, 2, fp ) != 2) + { + if (pbSuccess) *pbSuccess = FALSE; + return 0; + } + if (pbSuccess) *pbSuccess = TRUE; + CPL_LSBPTR16(&val) + return val; +} + +void writeFloat(VSILFILE* fp, float val) +{ + CPL_LSBPTR32(&val) + VSIFWriteL(&val, 1, 4, fp); +} + +void writeInt(VSILFILE* fp, int val) +{ + CPL_LSBPTR32(&val) + VSIFWriteL(&val, 1, 4, fp); +} + +void writeUShort(VSILFILE* fp, unsigned short val) +{ + CPL_LSBPTR16(&val) + VSIFWriteL(&val, 1, 2, fp); +} + + +/************************************************************************/ +/* Implementation of Waypoint Function Members */ +/************************************************************************/ +Waypoint::Waypoint(double latitude, + double longitude, + double altitude, + const char* name, + const char* comment, + int icon, + GIntBig wptdate) +{ + this->latitude = latitude; + this->longitude = longitude; + this->altitude = altitude; + this->name = CPLStrdup(name); + this->comment = CPLStrdup(comment); + this->icon = icon; + this->wptdate = wptdate; +} + +Waypoint::~Waypoint() +{ + CPLFree(name); + CPLFree(comment); +} + +double Waypoint::getLatitude() +{ + return latitude; +} + +double Waypoint::getLongitude() +{ + return longitude; +} + +double Waypoint::getAltitude() +{ + return altitude; +} + +const char* Waypoint::getName() +{ + return name; +} + +const char* Waypoint::getComment() +{ + return comment; +} + +int Waypoint::getIcon() +{ + return icon; +} + +GIntBig Waypoint::getDate() +{ + return wptdate; +} + +/************************************************************************/ +/* Implementation of Track Function Members */ +/************************************************************************/ +Track::Track(const char* pszName, + unsigned char type, + int color) +{ + this->pszName = CPLStrdup(pszName); + this->type = type; + this->color = color; + nPoints = 0; + pasTrackPoints = NULL; +} + +Track::~Track() +{ + CPLFree(pszName); + pszName = NULL; + CPLFree(pasTrackPoints); +} + +const char* Track::getName() { + return pszName; +} + +unsigned char Track::getType() +{ + return type; +} + + +int Track::getColor() +{ + return color; +} + +void Track::addPoint(double x, double y, GIntBig datetime, double altitude) +{ + pasTrackPoints = (TrackPoint*) + CPLRealloc(pasTrackPoints, (nPoints + 1) * sizeof(TrackPoint)); + pasTrackPoints[nPoints].x = x; + pasTrackPoints[nPoints].y = y; + pasTrackPoints[nPoints].datetime = datetime; + pasTrackPoints[nPoints].altitude = altitude; + nPoints ++; +} + +int Track::getNumPoints() +{ + return nPoints; +} + +const TrackPoint* Track::getPoint(int pointNum) +{ + if (pointNum >=0 && pointNum < nPoints) + return &pasTrackPoints[pointNum]; + else + return NULL; +} + + +/************************************************************************/ +/* Implementation of GTM Function Members */ +/************************************************************************/ +GTM::GTM() +{ + pGTMFile = NULL; + pszFilename = NULL; + + nwptstyles = 0; + nwpts = 0; + ntcks = 0; + n_tk = 0; + n_maps = 0; + headerSize = 0; + + firstWaypointOffset = 0; + actualWaypointOffset = 0; + waypointFetched = 0; + + firstTrackpointOffset = 0; + actualTrackpointOffset = 0; + trackpointFetched = 0; + + firstTrackOffset = 0; + actualTrackOffset = 0; + trackFetched = 0; +} + +GTM::~GTM() +{ + CPLFree(pszFilename); + if (pGTMFile != NULL) + { + VSIFCloseL(pGTMFile); + pGTMFile = NULL; + } +} + +bool GTM::Open(const char* pszFilename) +{ + + if (pGTMFile != NULL) + VSIFCloseL(pGTMFile); + + CPLFree(this->pszFilename); + this->pszFilename = CPLStrdup(pszFilename); + + pGTMFile = VSIFOpenL( pszFilename, "r" ); + if (pGTMFile == NULL) + { + return FALSE; + } + return TRUE; +} + + +bool GTM::isValid() +{ + // 2 bytes - version number + // 10 bytes - "TrackMaker" string + char buffer[13]; + + char* szHeader; + short version; + +/* -------------------------------------------------------------------- */ +/* If we aren't sure it is GTM, load a header chunk and check */ +/* for signs it is GTM */ +/* -------------------------------------------------------------------- */ + size_t nRead = VSIFReadL( buffer, 1, sizeof(buffer)-1, pGTMFile ); + if (nRead <= 0) + { + VSIFCloseL( pGTMFile ); + pGTMFile = NULL; + return FALSE; + } + buffer[12] = '\0'; + +/* -------------------------------------------------------------------- */ +/* If it looks like a GZip header, this may be a .gtz file, so */ +/* try opening with the /vsigzip/ prefix */ +/* -------------------------------------------------------------------- */ + if (buffer[0] == 0x1f && ((unsigned char*)buffer)[1] == 0x8b && + strncmp(pszFilename, "/vsigzip/", strlen("/vsigzip/")) != 0) + { + char* pszGZIPFileName = (char*)CPLMalloc( + strlen("/vsigzip/") + strlen(pszFilename) + 1); + sprintf(pszGZIPFileName, "/vsigzip/%s", pszFilename); + VSILFILE* fp = VSIFOpenL(pszGZIPFileName, "rb"); + if (fp) + { + VSILFILE* pGTMFileOri = pGTMFile; + pGTMFile = fp; + if (isValid()) + { + VSIFCloseL(pGTMFileOri); + CPLFree(pszGZIPFileName); + return TRUE; + } + else + { + if (pGTMFile) + VSIFCloseL(pGTMFile); + pGTMFile = pGTMFileOri; + } + } + CPLFree(pszGZIPFileName); + } + + version = CPL_LSBINT16PTR(buffer); + /*Skip string length */ + szHeader = buffer + 2; + if (version == 211 && strcmp(szHeader, "TrackMaker") == 0 ) + { + return TRUE; + } + return FALSE; +} + +bool GTM::readHeaderNumbers() +{ + if (pGTMFile == NULL) + return FALSE; + + + /* I'm supposing that the user has already checked if the file is + valid. */ + /* Also, I'm ignoring some header parameters that are unnecessary + for my purpose. If you want more features, implement it. :-P */ + + /* Read Number of Waypoint Styles*/ + /* Seek file */ + if (VSIFSeekL(pGTMFile, NWPTSTYLES_OFFSET, SEEK_SET) != 0) + return FALSE; + /* Read nwptstyles */ + nwptstyles = readInt(pGTMFile); + if (nwptstyles < 0) + return FALSE; + + /* Read Number of Waypoints */ + /* Seek file */ + if ( VSIFSeekL(pGTMFile, NWPTS_OFFSET, SEEK_SET) != 0) + return FALSE; + /* Read nwpts */ + nwpts = readInt(pGTMFile); + if (nwpts < 0) + return FALSE; + + /* Read Number of Trackpoints */ + ntcks = readInt(pGTMFile); + if (ntcks < 0) + return FALSE; + + /* Read Number of images */ + /* Seek file */ + if ( VSIFSeekL(pGTMFile, NMAPS_OFFSET, SEEK_SET) != 0) + return FALSE; + /* read n_maps */ + n_maps = readInt(pGTMFile); + if (n_maps < 0) + return FALSE; + + /* Read Number of Tracks */ + n_tk = readInt(pGTMFile); + if (n_tk < 0) + return FALSE; + + /* Figure out the header size */ + headerSize = 99; // Constant size plus size of strings + unsigned short stringSize; + + /* Read gradfont string size */ + if ( VSIFSeekL(pGTMFile, 99, SEEK_SET) != 0) + return FALSE; + stringSize = readUShort(pGTMFile); + headerSize += stringSize + 2; // String + size field + + /* Read labelfont string size */ + if ( VSIFSeekL(pGTMFile, stringSize, SEEK_CUR) != 0) + return FALSE; + stringSize = readUShort(pGTMFile); + headerSize += stringSize + 2; // String + size field + + + /* Read userfont string size */ + if ( VSIFSeekL(pGTMFile, stringSize, SEEK_CUR) != 0) + return FALSE; + stringSize = readUShort(pGTMFile); + headerSize += stringSize + 2; // String + size field + + /* Read newdatum string size */ + if ( VSIFSeekL(pGTMFile, stringSize, SEEK_CUR) != 0) + return FALSE; + stringSize = readUShort(pGTMFile); + headerSize += stringSize + 2; // String + size field + + + +/* -------------------------------------------------------------------- */ +/* Checks if it is using WGS84 datum */ +/* -------------------------------------------------------------------- */ + /* Read newdatum string size */ + if ( VSIFSeekL(pGTMFile, headerSize + 34, SEEK_SET) != 0) + return FALSE; + if (readInt(pGTMFile) != 217) + { + CPLError( CE_Warning, CPLE_AppDefined, + "You are attempting to open a file that is not using WGS84 datum.\n" + "Coordinates will be returned as if they were WGS84, but no reprojection will be done."); + } + + /* Look for the offsets */ + /* Waypoints */ + firstWaypointOffset = findFirstWaypointOffset(); + if (firstWaypointOffset == 0) + return FALSE; + actualWaypointOffset = firstWaypointOffset; + /* Trackpoints */ + firstTrackpointOffset = findFirstTrackpointOffset(); + if (firstTrackpointOffset == 0) + return FALSE; + actualTrackpointOffset = firstTrackpointOffset; + + /* Tracks */ + firstTrackOffset = findFirstTrackOffset(); + if (firstTrackOffset == 0) + return FALSE; + actualTrackOffset = firstTrackOffset; + + return TRUE; +} + +/************************************************************************/ +/* Waypoint control functions */ +/************************************************************************/ +int GTM::getNWpts() +{ + return nwpts; +} + +bool GTM::hasNextWaypoint() +{ + return waypointFetched < nwpts; +} + +void GTM::rewindWaypoint() +{ + actualWaypointOffset = firstWaypointOffset; + waypointFetched = 0; +} + +Waypoint* GTM::fetchNextWaypoint() +{ + unsigned short stringSize; + + double latitude, longitude; + char name[11]; + char* comment; + unsigned short icon; + int i; + float altitude; + GIntBig wptdate; + + /* Point to the actual waypoint offset */ + if ( VSIFSeekL(pGTMFile, actualWaypointOffset, SEEK_SET) != 0) + return NULL; + + latitude = readDouble(pGTMFile); + longitude = readDouble(pGTMFile); + + if ( !readFile( name, 1, 10 ) ) + return NULL; + /* Trim string name */ + for (i = 9; i >= 0; --i) + { + if (name[i] != ' ') + { + name[i+1] = '\0'; + break; + } + } + if (i < 0) + name[0] = '\0'; + + /* Read String Length */ + stringSize = readUShort(pGTMFile); + /* Read Comment String */ + comment = (char*) VSIMalloc2(sizeof(char), stringSize+1); + if ( stringSize != 0 && !readFile( comment, 1, sizeof(char)*stringSize ) ) + { + CPLFree(comment); + return NULL; + } + comment[stringSize] = '\0'; + + /* Read Icon */ + icon = readUShort(pGTMFile); + + /* Display number */ + readUChar(pGTMFile); + + /* Waypoint date */ + + wptdate = readInt(pGTMFile); + if (wptdate != 0) + wptdate += GTM_EPOCH; + + /* Rotation text angle */ + readUShort(pGTMFile); + + /* Altitude */ + altitude = readFloat(pGTMFile); + + Waypoint* poWaypoint = new Waypoint(latitude, longitude, altitude, + name, comment, (int) icon, wptdate); + + + /* Set actual waypoint offset to the next it there is one */ + ++waypointFetched; + if (waypointFetched < nwpts) + { + actualWaypointOffset += 8 + 8 + 10 + 2 + stringSize + 2 + 1 + 4 + 2 + 4 + 2; + } + + CPLFree(comment); + return poWaypoint; +} + + +/************************************************************************/ +/* Track control functions */ +/************************************************************************/ +int GTM::getNTracks() +{ + return n_tk; +} + +bool GTM::hasNextTrack() +{ + return trackFetched < n_tk; +} + +void GTM::rewindTrack() +{ + actualTrackpointOffset = firstTrackpointOffset; + actualTrackOffset = firstTrackOffset; + trackFetched = 0; + trackpointFetched = 0; +} + +Track* GTM::fetchNextTrack() +{ + unsigned short stringSize; + + char* pszName; + unsigned char type; + int color; + + + /* Point to the actual track offset */ + if ( VSIFSeekL(pGTMFile, actualTrackOffset, SEEK_SET) != 0) + return NULL; + + + /* Read string length */ + stringSize = readUShort(pGTMFile); + /* Read name string */ + pszName = (char*) VSIMalloc2(sizeof(char), stringSize+1); + if ( stringSize != 0 && !readFile( pszName, 1, sizeof(char) * stringSize ) ) + { + CPLFree(pszName); + return NULL; + } + pszName[stringSize] = '\0'; + + /* Read type */ + type = readUChar(pGTMFile); + + /* Read color */ + color = readInt(pGTMFile); + + Track* poTrack = new Track(pszName, type, color); + + CPLFree(pszName); + /* Adjust actual Track offset */ + actualTrackOffset = VSIFTellL(pGTMFile) + 7; + ++trackFetched; + + /* Now, We read all trackpoints for this track */ + double latitude, longitude; + GIntBig datetime; + unsigned char start; + float altitude; + /* NOTE: Parameters are passed by reference */ + if ( !readTrackPoints(latitude, longitude, datetime, start, altitude) ) + { + delete poTrack; + return NULL; + } + + /* Check if it is the start, if not we have done something wrong */ + if (start != 1) + { + delete poTrack; + return NULL; + } + poTrack->addPoint(longitude, latitude, datetime, altitude); + + do + { + /* NOTE: Parameters are passed by reference */ + if ( !readTrackPoints(latitude, longitude, datetime, start, altitude) ) + { + delete poTrack; + return NULL; + } + if (start == 0) + poTrack->addPoint(longitude, latitude, datetime, altitude); + } while(start == 0 && trackpointFetched < ntcks); + + /* We have read one more than necessary, so we adjust the offset */ + if (trackpointFetched < ntcks) + { + actualTrackpointOffset -= 25; + --trackpointFetched; + } + + return poTrack; +} + + + +/************************************************************************/ +/* Private Methods Implementation */ +/************************************************************************/ +vsi_l_offset GTM::findFirstWaypointOffset() +{ + /* Skip header and datum */ + if ( VSIFSeekL(pGTMFile, headerSize + DATUM_SIZE, SEEK_SET) != 0) + return 0; + + /* Skip images */ + unsigned short stringSize; + for (int i = 0; i < n_maps; ++i) + { + /* Read image name string size */ + stringSize = readUShort(pGTMFile); + + /* skip image name string */ + if ( VSIFSeekL(pGTMFile, stringSize, SEEK_CUR) != 0) + return 0; + + /* read image comment string size */ + stringSize = readUShort(pGTMFile); + + /* skip image comment string */ + if ( VSIFSeekL(pGTMFile, stringSize, SEEK_CUR) != 0) + return 0; + + /* skip the others image parameters */ + if ( VSIFSeekL(pGTMFile, 30, SEEK_CUR) != 0) + return 0; + } + return VSIFTellL(pGTMFile); +} + + +vsi_l_offset GTM::findFirstTrackpointOffset() +{ + if (firstWaypointOffset == 0) + { + firstWaypointOffset = findFirstWaypointOffset(); + if (firstWaypointOffset == 0) + return 0; + } + + /*---------------------------------------------*/ + /* We are going to skip the waypoints */ + /*---------------------------------------------*/ + /* Seek file to the first Waypoint */ + if (VSIFSeekL(pGTMFile, firstWaypointOffset, SEEK_SET) != 0) + return 0; + + unsigned short stringSize; + int bSuccess; + /* Skip waypoints */ + for (int i = 0; i < nwpts; ++i) + { + /* Seek file to the string size comment field */ + if (VSIFSeekL(pGTMFile, 26, SEEK_CUR) != 0) + return 0; + /* Read string comment size */ + stringSize = readUShort(pGTMFile, &bSuccess); + + /* Skip to the next Waypoint */ + if (bSuccess == FALSE || VSIFSeekL(pGTMFile, stringSize + 15, SEEK_CUR) != 0) + return 0; + } + + /* Skip waypoint styles */ + /* If we don't have waypoints, we don't have waypoint styles, even + though the nwptstyles is telling the contrary. */ + if (nwpts != 0) + { + for (int i = 0; i < nwptstyles; ++i) + { + /* Seek file to the string size facename field */ + if (VSIFSeekL(pGTMFile, 4, SEEK_CUR) != 0) + return 0; + + /* Read string facename size */ + stringSize = readUShort(pGTMFile, &bSuccess); + + /* Skip to the next Waypoint Style*/ + if (bSuccess == FALSE || VSIFSeekL(pGTMFile, stringSize + 24, SEEK_CUR) != 0) + return 0; + } + } + /* We've found the first track. Return the offset*/ + return VSIFTellL(pGTMFile); +} + +vsi_l_offset GTM::findFirstTrackOffset() +{ + if (firstTrackpointOffset == 0) + { + firstTrackpointOffset = findFirstTrackpointOffset(); + if (firstTrackpointOffset == 0) + return 0; + } + /* First track offset is the first trackpoint offset plus number of + trackpoints time size of a trackpoint*/ + return (vsi_l_offset) (firstTrackpointOffset + ntcks * 25); +} + +bool GTM::readTrackPoints(double& latitude, double& longitude, GIntBig& datetime, + unsigned char& start, float& altitude) { + /* Point to the actual trackpoint offset */ + if ( VSIFSeekL(pGTMFile, actualTrackpointOffset, SEEK_SET) != 0) + return FALSE; + + /* Read latitude */ + latitude = readDouble(pGTMFile); + + /* Read longitude */ + longitude = readDouble(pGTMFile); + + /* Read trackpoint date */ + datetime = readInt(pGTMFile); + if (datetime != 0) + datetime += GTM_EPOCH; + + /* Read start flag */ + if ( !readFile( &start, 1, 1 ) ) + return FALSE; + + /* Read altitude */ + altitude = readFloat(pGTMFile); + + ++trackpointFetched; + if (trackpointFetched < ntcks) + { + actualTrackpointOffset += 8 + 8 + 4 + 1 + 4; + } + return TRUE; +} + +bool GTM::readFile(void* pBuffer, size_t nSize, size_t nCount) +{ + size_t nRead; + nRead = VSIFReadL( pBuffer, nSize, nCount, pGTMFile ); + if (nRead <= 0) + { + VSIFCloseL( pGTMFile ); + pGTMFile = NULL; + return FALSE; + } + return TRUE; +} + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/gtm.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/gtm.h new file mode 100644 index 000000000..f8b57aa0b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/gtm.h @@ -0,0 +1,208 @@ +/****************************************************************************** + * $Id: gtm.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: GTM Driver + * Purpose: Class for reading, parsing and handling a gtm file. + * Author: Leonardo de Paula Rosa Piga; http://lampiao.lsc.ic.unicamp.br/~piga + * + ****************************************************************************** + * Copyright (c) 2009, Leonardo de Paula Rosa Piga + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef OGR_GTM_GTM_H_INCLUDED +#define OGR_GTM_GTM_H_INCLUDED + +#include +#include +#include + +#include + +#include "ogrsf_frmts.h" +#include "cpl_conv.h" +#include "cpl_string.h" + + +#ifndef FILE_OFFSETS +#define FILE_OFFSETS +#define NWPTSTYLES_OFFSET 27 +#define NWPTSTYLES_SIZE 4 + +#define NWPTS_OFFSET 35 +#define NWPTS_SIZE 4 + +#define NTRCKS_OFFSET 39 +#define NTRCKS_SIZE 4 + +#define NMAPS_OFFSET 63 +#define NMAPS_SIZE 4 + +#define NTK_OFFSET 67 +#define NTK_SIZE 4 + +#define BOUNDS_OFFSET 47 + +#define DATUM_SIZE 58 + +/* GTM_EPOCH is defined as the unix time for the 31 dec 1989 00:00:00 */ +#define GTM_EPOCH 631065600 + +#endif + + +void appendDouble(void* pBuffer, double val); +void appendFloat(void* pBuffer, float val); +void appendInt(void* pBuffer, int val); +void appendUChar(void* pBuffer, unsigned char val); +void appendUShort(void* pBuffer, unsigned short val); + +void writeDouble(VSILFILE* fp, double val); +void writeFloat(VSILFILE* fp, float val); +void writeInt(VSILFILE* fp, int val); +void writeUChar(VSILFILE* fp, unsigned char val); +void writeUShort(VSILFILE* fp, unsigned short); + + +class Waypoint +{ +public: + Waypoint(double latitude, + double longitude, + double altitude, + const char* name, + const char* comment, + int icon, + GIntBig wptdate + ); + ~Waypoint(); + double getLatitude(); + double getLongitude(); + double getAltitude(); + const char* getName(); + const char* getComment(); + int getIcon(); + GIntBig getDate(); /* 0 if invalid */ +private: + double latitude; + double longitude; + double altitude; + char* name; + char* comment; + int icon; + GIntBig wptdate; +}; + +typedef struct +{ + double x; + double y; + GIntBig datetime; + double altitude; +} TrackPoint; + +class Track +{ +public: + Track(const char* pszName, + unsigned char type, + int color); + ~Track(); + + const char* getName(); + unsigned char getType(); + int getColor(); + + void addPoint(double x, double y, GIntBig datetime, double altitude); + int getNumPoints(); + const TrackPoint* getPoint(int pointNum); + +private: + char* pszName; + unsigned char type; + int color; + int nPoints; + TrackPoint* pasTrackPoints; + +}; + + +class GTM +{ +public: + GTM(); + ~GTM(); + bool Open(const char* pszFilename); + + + // Check wheater it is a valid GTM file or not + bool isValid(); + bool readHeaderNumbers(); + + // Waypoint control functions + Waypoint* fetchNextWaypoint(); + int getNWpts(); + bool hasNextWaypoint(); + void rewindWaypoint(); + + int getNTracks(); + bool hasNextTrack(); + void rewindTrack(); + Track* fetchNextTrack(); + +private: + // File descriptor + VSILFILE* pGTMFile; + char* pszFilename; + + // GTM Header Parameters + int nwptstyles; + int nwpts; + int ntcks; + int n_tk; + int n_maps; + int headerSize; + + // Waypoint controller + vsi_l_offset firstWaypointOffset; + vsi_l_offset actualWaypointOffset; + int waypointFetched; + + // Trackpoint controller + vsi_l_offset firstTrackpointOffset; + vsi_l_offset actualTrackpointOffset; + int trackpointFetched; + + // Track controller + vsi_l_offset firstTrackOffset; + vsi_l_offset actualTrackOffset; + int trackFetched; + + vsi_l_offset findFirstWaypointOffset(); + vsi_l_offset findFirstTrackpointOffset(); + vsi_l_offset findFirstTrackOffset(); + + bool readTrackPoints(double& latitude, double& longitude, GIntBig& datetime, + unsigned char& start, float& altitude); + bool readFile(void* pBuffer, size_t nSize, size_t nCount); + +}; + +#endif // OGR_GTM_GTM diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/gtmtracklayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/gtmtracklayer.cpp new file mode 100644 index 000000000..8d9a4f03b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/gtmtracklayer.cpp @@ -0,0 +1,365 @@ +/****************************************************************************** + * $Id: gtmtracklayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: GTM Driver + * Purpose: Implementation of GTMTrackLayer class. + * Author: Leonardo de Paula Rosa Piga; http://lampiao.lsc.ic.unicamp.br/~piga + * + * + ****************************************************************************** + * Copyright (c) 2009, Leonardo de Paula Rosa Piga + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogr_gtm.h" + + +GTMTrackLayer::GTMTrackLayer( const char* pszName, + OGRSpatialReference* poSRSIn, + CPL_UNUSED int bWriterIn, + OGRGTMDataSource* poDSIn ) +{ + poCT = NULL; + + /* We are implementing just WGS84, although GTM supports other datum + formats. */ + if( poSRSIn != NULL ) + { + poSRS = new OGRSpatialReference(NULL); + poSRS->SetWellKnownGeogCS( "WGS84" ); + if (!poSRS->IsSame(poSRSIn)) + { + poCT = OGRCreateCoordinateTransformation( poSRSIn, poSRS ); + if( poCT == NULL && poDSIn->isFirstCTError() ) + { + /* If we can't create a transformation, issue a warning - but + continue the transformation*/ + char *pszWKT = NULL; + + poSRSIn->exportToPrettyWkt( &pszWKT, FALSE ); + + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to create coordinate transformation between the\n" + "input coordinate system and WGS84. This may be because they\n" + "are not transformable, or because projection services\n" + "(PROJ.4 DLL/.so) could not be loaded.\n" + "This message will not be issued any more. \n" + "\nSource:\n%s\n", + pszWKT ); + + CPLFree( pszWKT ); + poDSIn->issuedFirstCTError(); + } + } + } + else + { + poSRS = NULL; + } + + poDS = poDSIn; + + nNextFID = 0; + nTotalFCount = poDS->getNTracks(); + + poFeatureDefn = new OGRFeatureDefn( pszName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType ( wkbLineString ); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + /* We implement just name, type, and color for tracks, if others + needed feel free to append more parameters and implement the + code */ + OGRFieldDefn oFieldName( "name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oFieldTrackType( "type", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oFieldTrackType ); + + OGRFieldDefn oFieldColor( "color", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oFieldColor ); + + this->pszName = CPLStrdup(pszName); +} + +GTMTrackLayer::~GTMTrackLayer() +{ + /* poDS, poSRS, poCT, pszName, and poFeatureDefn are released on + parent class*/ +} + + +/************************************************************************/ +/* WriteFeatureAttributes() */ +/************************************************************************/ +void GTMTrackLayer::WriteFeatureAttributes( OGRFeature *poFeature ) +{ + void* pBuffer = NULL; + char* psztrackname = NULL; + int type = 1; + unsigned int color = 0; + for (int i = 0; i < poFeatureDefn->GetFieldCount(); ++i) + { + OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn( i ); + if( poFeature->IsFieldSet( i ) ) + { + const char* pszName = poFieldDefn->GetNameRef(); + /* track name */ + if (strncmp(pszName, "name", 4) == 0) + { + psztrackname = CPLStrdup( poFeature->GetFieldAsString( i ) ); + } + /* track type */ + else if (strncmp(pszName, "type", 4) == 0) + { + type = poFeature->GetFieldAsInteger( i ); + // Check if it is a valid type + if (type < 1 || type > 30) + type = 1; + } + /* track color */ + else if (strncmp(pszName, "color", 5) == 0) + { + color = (unsigned int) poFeature->GetFieldAsInteger( i ); + if (color > 0xFFFFFF) + color = 0xFFFFFFF; + } + } + } + + if (psztrackname == NULL) + psztrackname = CPLStrdup( "" ); + + int trackNameLength = 0; + if (psztrackname != NULL) + trackNameLength = strlen(psztrackname); + + int bufferSize = 14 + trackNameLength; + pBuffer = CPLMalloc(bufferSize); + void* pBufferAux = pBuffer; + /* Write track string name size to buffer */ + appendUShort(pBufferAux, (unsigned short) trackNameLength); + pBufferAux = (char*)pBufferAux + 2; + + /* Write track name */ + strncpy((char*)pBufferAux, psztrackname, trackNameLength); + pBufferAux = (char*)pBufferAux + trackNameLength; + + /* Write track type */ + appendUChar(pBufferAux, (unsigned char) type); + pBufferAux = (char*)pBufferAux + 1; + + /* Write track color */ + appendInt(pBufferAux, color); + pBufferAux = (char*)pBufferAux + 4; + + /* Write track scale */ + appendFloat(pBufferAux, 0); + pBufferAux = (char*)pBufferAux + 4; + + /* Write track label */ + appendUChar(pBufferAux, 0); + pBufferAux = (char*)pBufferAux + 1; + + /* Write track layer */ + appendUShort(pBufferAux, 0); + + VSIFWriteL(pBuffer, bufferSize, 1, poDS->getTmpTracksFP()); + poDS->incNumTracks(); + + if (psztrackname != NULL) + CPLFree(psztrackname); + CPLFree(pBuffer); +} + +/************************************************************************/ +/* WriteTrackpoint() */ +/************************************************************************/ +inline void GTMTrackLayer::WriteTrackpoint( double lat, double lon, float altitude, bool start ) +{ + void* pBuffer = CPLMalloc(25); + void* pBufferAux = pBuffer; + //latitude + appendDouble(pBufferAux, lat); + pBufferAux = (char*)pBufferAux + 8; + //longitude + appendDouble(pBufferAux, lon); + pBufferAux = (char*)pBufferAux + 8; + //date + appendInt(pBufferAux, 0); + pBufferAux = (char*)pBufferAux + 4; + //start + appendUChar(pBufferAux, start); + pBufferAux = (char*)pBufferAux + 1; + //altitude + appendFloat(pBufferAux, altitude); + VSIFWriteL(pBuffer, 25, 1, poDS->getTmpTrackpointsFP()); + poDS->incNumTrackpoints(); + CPLFree(pBuffer); +} + + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ +OGRErr GTMTrackLayer::ICreateFeature (OGRFeature *poFeature) +{ + VSILFILE* fpTmpTrackpoints = poDS->getTmpTrackpointsFP(); + if (fpTmpTrackpoints == NULL) + return CE_Failure; + + VSILFILE* fpTmpTracks = poDS->getTmpTracksFP(); + if (fpTmpTracks == NULL) + return CE_Failure; + + OGRGeometry *poGeom = poFeature->GetGeometryRef(); + if ( poGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Features without geometry not supported by GTM writer in track layer." ); + return OGRERR_FAILURE; + } + + if (NULL != poCT) + { + poGeom = poGeom->clone(); + poGeom->transform( poCT ); + } + + switch( poGeom->getGeometryType() ) + { + case wkbLineString: + case wkbLineString25D: + { + WriteFeatureAttributes(poFeature); + OGRLineString* line = (OGRLineString*)poGeom; + for(int i = 0; i < line->getNumPoints(); ++i) + { + double lat = line->getY(i); + double lon = line->getX(i); + float altitude = 0; + CheckAndFixCoordinatesValidity(lat, lon); + poDS->checkBounds((float)lat, (float)lon); + if (line->getGeometryType() == wkbLineString25D) + altitude = (float)line->getZ(i); + WriteTrackpoint( lat, lon, altitude, i==0 ); + } + break; + } + + case wkbMultiLineString: + case wkbMultiLineString25D: + { + int nGeometries = ((OGRGeometryCollection*)poGeom)->getNumGeometries (); + for(int j = 0; j < nGeometries; ++j) + { + WriteFeatureAttributes(poFeature); + OGRLineString* line = (OGRLineString*) ( ((OGRGeometryCollection*)poGeom)->getGeometryRef(j) ); + int n = (line) ? line->getNumPoints() : 0; + for(int i = 0; i < n; ++i) + { + double lat = line->getY(i); + double lon = line->getX(i); + float altitude = 0; + CheckAndFixCoordinatesValidity(lat, lon); + if (line->getGeometryType() == wkbLineString25D) + altitude = (float) line->getZ(i); + WriteTrackpoint( lat, lon, altitude, i==0 ); + } + } + break; + } + + default: + { + CPLError( CE_Failure, CPLE_NotSupported, + "Geometry type of `%s' not supported for 'track' element.\n", + OGRGeometryTypeToName(poGeom->getGeometryType()) ); + if (NULL != poCT) + delete poGeom; + return OGRERR_FAILURE; + } + } + + if (NULL != poCT) + delete poGeom; + + return OGRERR_NONE; +} + + +OGRFeature* GTMTrackLayer::GetNextFeature() +{ + if (bError) + return NULL; + + while (poDS->hasNextTrack()) + { + Track* poTrack = poDS->fetchNextTrack(); + if (poTrack == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Could not read track. File probably corrupted"); + bError = TRUE; + return NULL; + } + OGRFeature* poFeature = new OGRFeature( poFeatureDefn ); + OGRLineString* lineString = new OGRLineString (); + + for (int i = 0; i < poTrack->getNumPoints(); ++i) + { + const TrackPoint* psTrackPoint = poTrack->getPoint(i); + lineString->addPoint(psTrackPoint->x, + psTrackPoint->y); + } + if (poSRS) + lineString->assignSpatialReference(poSRS); + poFeature->SetField( NAME, poTrack->getName()); + poFeature->SetField( TYPE, poTrack->getType()); + poFeature->SetField( COLOR, poTrack->getColor()); + poFeature->SetFID( nNextFID++ ); + delete poTrack; + + poFeature->SetGeometryDirectly(lineString); + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } + return NULL; +} + +GIntBig GTMTrackLayer::GetFeatureCount(int bForce) +{ + if (m_poFilterGeom == NULL && m_poAttrQuery == NULL) + return poDS->getNTracks(); + + return OGRLayer::GetFeatureCount(bForce); +} + + +void GTMTrackLayer::ResetReading() +{ + nNextFID = 0; + poDS->rewindTrack(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/gtmwaypointlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/gtmwaypointlayer.cpp new file mode 100644 index 000000000..1966d3c2a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/gtmwaypointlayer.cpp @@ -0,0 +1,363 @@ +/****************************************************************************** + * $Id: gtmwaypointlayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: GTM Driver + * Purpose: Implementation of gtmwaypoint class. + * Author: Leonardo de Paula Rosa Piga; http://lampiao.lsc.ic.unicamp.br/~piga + * + * + ****************************************************************************** + * Copyright (c) 2009, Leonardo de Paula Rosa Piga + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogr_gtm.h" +#include "cpl_time.h" + +GTMWaypointLayer::GTMWaypointLayer( const char* pszName, + OGRSpatialReference* poSRSIn, + CPL_UNUSED int bWriterIn, + OGRGTMDataSource* poDSIn ) +{ + poCT = NULL; + + /* We are implementing just WGS84, although GTM supports other datum + formats. */ + if( poSRSIn != NULL ) + { + poSRS = new OGRSpatialReference(NULL); + poSRS->SetWellKnownGeogCS( "WGS84" ); + if (!poSRS->IsSame(poSRSIn)) + { + poCT = OGRCreateCoordinateTransformation( poSRSIn, poSRS ); + if( poCT == NULL && poDSIn->isFirstCTError() ) + { + /* If we can't create a transformation, issue a warning - but + continue the transformation*/ + char *pszWKT = NULL; + + poSRSIn->exportToPrettyWkt( &pszWKT, FALSE ); + + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to create coordinate transformation between the\n" + "input coordinate system and WGS84. This may be because they\n" + "are not transformable, or because projection services\n" + "(PROJ.4 DLL/.so) could not be loaded.\n" + "This message will not be issued any more. \n" + "\nSource:\n%s\n", + pszWKT ); + + CPLFree( pszWKT ); + poDSIn->issuedFirstCTError(); + } + } + } + else + { + poSRS = NULL; + } + + poDS = poDSIn; + + nNextFID = 0; + nTotalFCount = poDS->getNWpts(); + + poFeatureDefn = new OGRFeatureDefn( pszName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType ( wkbPoint ); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + /* We implement just name, comment, and icon, if others needed feel + free to append more parameters */ + OGRFieldDefn oFieldName( "name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oFieldComment( "comment", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldComment ); + + OGRFieldDefn oFieldIcon( "icon", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oFieldIcon ); + + OGRFieldDefn oFieldTime( "time", OFTDateTime ); + poFeatureDefn->AddFieldDefn( &oFieldTime ); + + this->pszName = CPLStrdup(pszName); +} + +GTMWaypointLayer::~GTMWaypointLayer() +{ + +} + + +/************************************************************************/ +/* WriteFeatureAttributes() */ +/************************************************************************/ +void GTMWaypointLayer::WriteFeatureAttributes( OGRFeature *poFeature, float altitude ) +{ + void* pBuffer = NULL; + void* pBufferAux = NULL; + char psNameField[] = " "; + char* pszcomment = NULL; + int icon = 48; + int date = 0; + for (int i = 0; i < poFeatureDefn->GetFieldCount(); ++i) + { + OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn( i ); + if( poFeature->IsFieldSet( i ) ) + { + const char* pszName = poFieldDefn->GetNameRef(); + /* Waypoint name */ + if (strncmp(pszName, "name", 4) == 0) + { + strncpy (psNameField, poFeature->GetFieldAsString( i ), 10); + CPLStrlcat (psNameField, " ", sizeof(psNameField)); + } + /* Waypoint comment */ + else if (strncmp(pszName, "comment", 7) == 0) + { + pszcomment = CPLStrdup( poFeature->GetFieldAsString( i ) ); + } + /* Waypoint icon */ + else if (strncmp(pszName, "icon", 4) == 0) + { + icon = poFeature->GetFieldAsInteger( i ); + // Check if it is a valid icon + if (icon < 1 || icon > 220) + icon = 48; + } + /* Waypoint date */ + else if (EQUAL(pszName, "time")) + { + struct tm brokendowndate; + int year, month, day, hour, min, sec, TZFlag; + if (poFeature->GetFieldAsDateTime( i, &year, &month, &day, &hour, &min, &sec, &TZFlag)) + { + brokendowndate.tm_year = year - 1900; + brokendowndate.tm_mon = month - 1; + brokendowndate.tm_mday = day; + brokendowndate.tm_hour = hour; + brokendowndate.tm_min = min; + brokendowndate.tm_sec = sec; + GIntBig unixTime = CPLYMDHMSToUnixTime(&brokendowndate); + if (TZFlag != 0) + unixTime -= (TZFlag - 100) * 15; + if (unixTime <= GTM_EPOCH || (unixTime - GTM_EPOCH) != (int)(unixTime - GTM_EPOCH)) + { + CPLError(CE_Warning, CPLE_AppDefined, + "%04d/%02d/%02d %02d:%02d:%02d is not a valid datetime for GTM", + year, month, day, hour, min, sec); + } + else + { + date = (int)(unixTime - GTM_EPOCH); + } + } + } + } + } + + if (pszcomment == NULL) + pszcomment = CPLStrdup( "" ); + + int commentLength = 0; + if (pszcomment != NULL) + commentLength = strlen(pszcomment); + + int bufferSize = 27 + commentLength; + pBuffer = CPLMalloc(bufferSize); + pBufferAux = pBuffer; + /* Write waypoint name to buffer */ + strncpy((char*)pBufferAux, psNameField, 10); + + /* Write waypoint string comment size to buffer */ + pBufferAux = (char*)pBuffer+10; + appendUShort(pBufferAux, (unsigned short) commentLength); + + /* Write waypoint string comment to buffer */ + strncpy((char*)pBuffer+12, pszcomment, commentLength); + + /* Write icon to buffer */ + pBufferAux = (char*)pBuffer+12+commentLength; + appendUShort(pBufferAux, (unsigned short) icon); + + /* Write dslp to buffer */ + pBufferAux = (char*)pBufferAux + 2; + appendUChar(pBufferAux, 3); + + /* Date */ + pBufferAux = (char*)pBufferAux + 1; + appendInt(pBufferAux, date); + + /* wrot */ + pBufferAux = (char*)pBufferAux + 4; + appendUShort(pBufferAux, 0); + + /* walt */ + pBufferAux = (char*)pBufferAux + 2; + appendFloat(pBufferAux, altitude); + + /* wlayer */ + pBufferAux = (char*)pBufferAux + 4; + appendUShort(pBufferAux, 0); + + VSIFWriteL(pBuffer, bufferSize, 1, poDS->getOutputFP()); + poDS->incNumWaypoints(); + + if (pszcomment != NULL) + CPLFree(pszcomment); + CPLFree(pBuffer); +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ +OGRErr GTMWaypointLayer::ICreateFeature (OGRFeature *poFeature) +{ + VSILFILE* fp = poDS->getOutputFP(); + if (fp == NULL) + return CE_Failure; + + OGRGeometry *poGeom = poFeature->GetGeometryRef(); + if ( poGeom == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Features without geometry not supported by GTM writer in waypoints layer." ); + return OGRERR_FAILURE; + } + + if (NULL != poCT) + { + poGeom = poGeom->clone(); + poGeom->transform( poCT ); + } + + + switch( poGeom->getGeometryType() ) + { + case wkbPoint: + case wkbPoint25D: + { + OGRPoint* point = (OGRPoint*)poGeom; + double lat = point->getY(); + double lon = point->getX(); + CheckAndFixCoordinatesValidity(lat, lon); + poDS->checkBounds((float)lat, (float)lon); + writeDouble(fp, lat); + writeDouble(fp, lon); + float altitude = 0.0; + if (poGeom->getGeometryType() == wkbPoint25D) + altitude = (float) point->getZ(); + + WriteFeatureAttributes(poFeature, altitude); + break; + } + + default: + { + CPLError( CE_Failure, CPLE_NotSupported, + "Geometry type of `%s' not supported for 'waypoint' element.\n", + OGRGeometryTypeToName(poGeom->getGeometryType()) ); + return OGRERR_FAILURE; + } + } + + if (NULL != poCT) + delete poGeom; + + return OGRERR_NONE; + +} + +OGRFeature* GTMWaypointLayer::GetNextFeature() +{ + if (bError) + return NULL; + + while (poDS->hasNextWaypoint()) + { + Waypoint* poWaypoint = poDS->fetchNextWaypoint(); + if (poWaypoint == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Could not read waypoint. File probably corrupted"); + bError = TRUE; + return NULL; + } + + OGRFeature* poFeature = new OGRFeature( poFeatureDefn ); + double altitude = poWaypoint->getAltitude(); + if (altitude == 0.0) + poFeature->SetGeometryDirectly(new OGRPoint + (poWaypoint->getLongitude(), + poWaypoint->getLatitude())); + else + poFeature->SetGeometryDirectly(new OGRPoint + (poWaypoint->getLongitude(), + poWaypoint->getLatitude(), + altitude)); + + if (poSRS) + poFeature->GetGeometryRef()->assignSpatialReference(poSRS); + poFeature->SetField( NAME, poWaypoint->getName()); + poFeature->SetField( COMMENT, poWaypoint->getComment()); + poFeature->SetField( ICON, poWaypoint->getIcon()); + + GIntBig wptdate = poWaypoint->getDate(); + if (wptdate != 0) + { + struct tm brokendownTime; + CPLUnixTimeToYMDHMS(wptdate, &brokendownTime); + poFeature->SetField( DATE, + brokendownTime.tm_year + 1900, + brokendownTime.tm_mon + 1, + brokendownTime.tm_mday, + brokendownTime.tm_hour, + brokendownTime.tm_min, + brokendownTime.tm_sec); + } + + poFeature->SetFID( nNextFID++ ); + delete poWaypoint; + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } + return NULL; +} + +GIntBig GTMWaypointLayer::GetFeatureCount(int bForce) +{ + if (m_poFilterGeom == NULL && m_poAttrQuery == NULL) + return poDS->getNWpts(); + + return OGRLayer::GetFeatureCount(bForce); +} + +void GTMWaypointLayer::ResetReading() +{ + nNextFID = 0; + poDS->rewindWaypoint(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/makefile.vc new file mode 100644 index 000000000..96d4f2576 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/makefile.vc @@ -0,0 +1,11 @@ +OBJ = ogrgtmdriver.obj ogrgtmdatasource.obj ogrgtmlayer.obj gtm.obj gtmwaypointlayer.obj gtmtracklayer.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/ogr_gtm.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/ogr_gtm.h new file mode 100644 index 000000000..aac5796d9 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/ogr_gtm.h @@ -0,0 +1,262 @@ +/****************************************************************************** + * $Id: ogr_gtm.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: GTM Driver + * Purpose: Declarations for OGR wrapper classes for GTM, and OGR->GTM + * translation of geometry. + * Author: Leonardo de Paula Rosa Piga; http://lampiao.lsc.ic.unicamp.br/~piga + * + ****************************************************************************** + * Copyright (c) 2009, Leonardo de Paula Rosa Piga + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef OGR_GTM_H_INCLUDED +#define OGR_GTM_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +class OGRGTMDataSource; + +typedef enum +{ + GTM_NONE, + GTM_WPT, + GTM_TRACK +} GTMGeometryType; + +#ifndef FILE_OFFSETS +#define FILE_OFFSETS +#define NWPTSTYLES_OFFSET 27 +#define NWPTSTYLES_SIZE 4 + +#define NWPTS_OFFSET 35 +#define NWPTS_SIZE 4 + +#define NTRCKS_OFFSET 39 +#define NTRCKS_SIZE 4 + +#define NMAPS_OFFSET 63 +#define NMAPS_SIZE 4 + +#define NTK_OFFSET 67 +#define NTK_SIZE 4 + +#define BOUNDS_OFFSET 47 + +#define DATUM_SIZE 58 + +/* GTM_EPOCH is defined as the unix time for the 31 dec 1989 00:00:00 */ +#define GTM_EPOCH 631065600 + +#endif + +#ifndef MAX +# define MIN(a,b) ((ab) ? a : b) +#endif + + +#include "gtm.h" + + +/************************************************************************/ +/* OGRGTMLayer */ +/************************************************************************/ +class OGRGTMLayer : public OGRLayer +{ +public: + OGRGTMLayer(); + ~OGRGTMLayer(); + // + // OGRLayer Interface + // + OGRFeatureDefn* GetLayerDefn(); + virtual void ResetReading() = 0; + virtual OGRFeature* GetNextFeature() = 0; + virtual GIntBig GetFeatureCount(int bForce = TRUE) = 0; + virtual OGRErr ICreateFeature(OGRFeature *poFeature) = 0; + + int TestCapability( const char* pszCap ); + + OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK ); + +protected: + OGRGTMDataSource* poDS; + OGRSpatialReference* poSRS; + OGRCoordinateTransformation *poCT; + char* pszName; + + OGRFeatureDefn* poFeatureDefn; + int nNextFID; + int nTotalFCount; + + int bError; + + static OGRErr CheckAndFixCoordinatesValidity( double& pdfLatitude, double& pdfLongitude ); + +}; + + +/************************************************************************/ +/* GTMWaypointLayer */ +/************************************************************************/ +class GTMWaypointLayer : public OGRGTMLayer +{ +public: + GTMWaypointLayer( const char* pszName, + OGRSpatialReference* poSRSIn, + int bWriterIn, + OGRGTMDataSource* poDSIn ); + ~GTMWaypointLayer(); + OGRErr ICreateFeature(OGRFeature *poFeature); + void ResetReading(); + OGRFeature* GetNextFeature(); + GIntBig GetFeatureCount(int bForce = TRUE); + + enum WaypointFields{NAME, COMMENT, ICON, DATE}; +private: + void WriteFeatureAttributes( OGRFeature *poFeature, float altitude ); +}; + +/************************************************************************/ +/* GTMTrackLayer */ +/************************************************************************/ +class GTMTrackLayer : public OGRGTMLayer +{ +public: + GTMTrackLayer( const char* pszName, + OGRSpatialReference* poSRSIn, + int bWriterIn, + OGRGTMDataSource* poDSIn ); + ~GTMTrackLayer(); + OGRErr ICreateFeature(OGRFeature *poFeature); + void ResetReading(); + OGRFeature* GetNextFeature(); + GIntBig GetFeatureCount(int bForce = TRUE); + enum TrackFields{NAME, TYPE, COLOR}; + +private: + void WriteFeatureAttributes( OGRFeature *poFeature ); + void WriteTrackpoint( double lat, double lon, float altitude, bool start ); + +}; + + +/************************************************************************/ +/* OGRGTMDataSource */ +/************************************************************************/ +class OGRGTMDataSource : public OGRDataSource +{ +public: + + // OGRDataSource Interface + OGRGTMDataSource(); + ~OGRGTMDataSource(); + + int Open( const char *pszFilename, int bUpdate ); + int Create( const char *pszFilename, char **papszOptions ); + + const char* GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + + OGRLayer* GetLayer( int ); + + OGRLayer* ICreateLayer(const char *pszName, + OGRSpatialReference *poSpatialRef=NULL, + OGRwkbGeometryType eGType=wkbUnknown, + char **papszOptions=NULL); + int TestCapability( const char * ); + + // OGRGTMDataSource Methods + VSILFILE* getOutputFP() { return fpOutput; } + VSILFILE* getTmpTrackpointsFP() { return fpTmpTrackpoints; } + VSILFILE* getTmpTracksFP() { return fpTmpTracks; } + bool isFirstCTError() { return !bIssuedCTError; } + void issuedFirstCTError() { bIssuedCTError = true; } + + /* Functions to handle with waypoints */ + int getNWpts(); + bool hasNextWaypoint(); + Waypoint* fetchNextWaypoint(); + void rewindWaypoint(); + + /* Functions to handle with tracks */ + int getNTracks(); + bool hasNextTrack(); + Track* fetchNextTrack(); + void rewindTrack(); + + + /* Functions for writing ne files */ + float getMinLat() { return minlat; } + float getMaxLat() { return maxlat; } + float getMinLon() { return minlon; } + float getMaxLon() { return maxlon; } + + void checkBounds(float newLat, + float newLon); + int getNumWaypoints() { return numWaypoints; } + int getNumTrackpoints() { return numTrackpoints; } + int getTracks() { return numTracks; }; + + int incNumWaypoints() { return ++numWaypoints; } + int incNumTrackpoints() { return ++numTrackpoints; } + int incNumTracks() { return ++numTracks; }; +private: + VSILFILE* fpOutput; + + /* GTM is not a contiguous file. We need two temporary files because + trackpoints and tracks are stored separated and we don't know in + advance how many trackpoints and tracks the new file will + have. So, we create temporary file and append the at the end of + the gtm file whe everything is done, that is, in the + destructor. */ + VSILFILE* fpTmpTrackpoints; + char* pszTmpTrackpoints; + + VSILFILE* fpTmpTracks; + char* pszTmpTracks; + + GTM* poGTMFile; + char* pszName; + + OGRGTMLayer **papoLayers; + int nLayers; + + bool bIssuedCTError; + + /* Used for creating a new file */ + float minlat; + float maxlat; + float minlon; + float maxlon; + + int numWaypoints; + int numTracks; + int numTrackpoints; + + void AppendTemporaryFiles(); + void WriteWaypointStyles(); +}; + +#endif //OGR_GTM_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/ogrgtmdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/ogrgtmdatasource.cpp new file mode 100644 index 000000000..2ba2e453d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/ogrgtmdatasource.cpp @@ -0,0 +1,643 @@ +/****************************************************************************** + * $Id: ogrgtmdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: GTM Driver + * Purpose: Implementation of OGRGTMDataSource class. + * Author: Leonardo de Paula Rosa Piga; http://lampiao.lsc.ic.unicamp.br/~piga + * + * + ****************************************************************************** + * Copyright (c) 2009, Leonardo de Paula Rosa Piga + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogr_gtm.h" + +/************************************************************************/ +/* OGRGTMDataSource() */ +/************************************************************************/ + +OGRGTMDataSource::OGRGTMDataSource() +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + bIssuedCTError = FALSE; + poGTMFile = NULL; + fpOutput = NULL; + fpTmpTrackpoints = NULL; + pszTmpTrackpoints = NULL; + fpTmpTracks = NULL; + pszTmpTracks = NULL; + + minlat = 0; + maxlat = 0; + minlon = 0; + maxlon = 0; + + numWaypoints = 0; + numTracks = 0; + numTrackpoints = 0; + +} + +/************************************************************************/ +/* AppendTemporaryFiles() */ +/************************************************************************/ +void OGRGTMDataSource::AppendTemporaryFiles() +{ + if( fpOutput == NULL ) + return; + + if (numTrackpoints != 0 || numTracks != 0) + { + void* pBuffer = CPLMalloc(2048); + size_t bytes; + + // Append Trackpoints to the output file + fpTmpTrackpoints = VSIFOpenL( pszTmpTrackpoints, "r" ); + if (fpTmpTrackpoints != NULL) + { + while ( !VSIFEofL(fpTmpTrackpoints) ) + { + bytes = VSIFReadL(pBuffer, 1, 2048, fpTmpTrackpoints); + VSIFWriteL(pBuffer, bytes, 1, fpOutput); + } + VSIFCloseL( fpTmpTrackpoints ); + fpTmpTrackpoints = NULL; + } + + // Append Tracks to the output file + fpTmpTracks = VSIFOpenL( pszTmpTracks, "r" ); + if (fpTmpTracks != NULL) + { + while ( !VSIFEofL(fpTmpTracks) ) + { + bytes = VSIFReadL(pBuffer, 1, 2048, fpTmpTracks); + VSIFWriteL(pBuffer, bytes, 1, fpOutput); + } + VSIFCloseL( fpTmpTracks ); + fpTmpTracks = NULL; + } + CPLFree(pBuffer); + } +} + +/************************************************************************/ +/* WriteWaypointStyles() */ +/************************************************************************/ +void OGRGTMDataSource::WriteWaypointStyles() +{ + if( fpOutput != NULL ) + { + // We have waypoints, thus we need to write the default + // waypoint style as defined by the specification + if ( numWaypoints != 0) + { + void* pBuffer = CPLMalloc(35); + void* pBufferAux = pBuffer; + for (int i = 0; i < 4; ++i) + { + // height + appendInt(pBufferAux, -11); + pBufferAux = ((char*)pBufferAux) + 4; + // facename size + appendUShort(pBufferAux, 5); + pBufferAux = ((char*)pBufferAux) + 2; + // facename + strncpy((char*)pBufferAux, "Arial", 5); + pBufferAux = ((char*)pBufferAux) + 5; + // dspl + appendUChar(pBufferAux, (unsigned char) i); + pBufferAux = ((char*)pBufferAux) + 1; + // color + appendInt(pBufferAux, 0); + pBufferAux = ((char*)pBufferAux) + 4; + // weight + appendInt(pBufferAux, 400); + pBufferAux = ((char*)pBufferAux) + 4; + // scale1 + appendInt(pBufferAux, 0); + pBufferAux = ((char*)pBufferAux) + 4; + // border + appendUChar(pBufferAux, (i != 3) ? 0 : 139); + pBufferAux = ((char*)pBufferAux) + 1; + // background + appendUShort(pBufferAux, (i != 3) ? 0 : 0xFF); + pBufferAux = ((char*)pBufferAux) + 2; + // backcolor + appendInt(pBufferAux, (i != 3) ? 0 : 0xFFFF); + pBufferAux = ((char*)pBufferAux) + 4; + // italic, underline, strikeout + appendInt(pBufferAux, 0); + pBufferAux = ((char*)pBufferAux) + 3; + // alignment + appendUChar(pBufferAux, (i != 3) ? 0 : 1); + pBufferAux = pBuffer; + VSIFWriteL(pBuffer, 35, 1, fpOutput); + } + CPLFree(pBuffer); + } + } +} + + +/************************************************************************/ +/* ~OGRGTMDataSource() */ +/************************************************************************/ + +OGRGTMDataSource::~OGRGTMDataSource() +{ + if (fpTmpTrackpoints != NULL) + VSIFCloseL( fpTmpTrackpoints ); + + if (fpTmpTracks != NULL) + VSIFCloseL( fpTmpTracks ); + + WriteWaypointStyles(); + AppendTemporaryFiles(); + + if( fpOutput != NULL ) + { + /* Adjust header counters */ + VSIFSeekL(fpOutput, NWPTS_OFFSET, SEEK_SET); + writeInt(fpOutput, numWaypoints); + writeInt(fpOutput, numTrackpoints); + + VSIFSeekL(fpOutput, NTK_OFFSET, SEEK_SET); + writeInt(fpOutput, numTracks); + + /* Adjust header bounds */ + VSIFSeekL(fpOutput, BOUNDS_OFFSET, SEEK_SET); + writeFloat(fpOutput, maxlon); + writeFloat(fpOutput, minlon); + writeFloat(fpOutput, maxlat); + writeFloat(fpOutput, minlat); + VSIFCloseL( fpOutput ); + } + + + if (papoLayers != NULL) + { + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + } + + if (pszName != NULL) + CPLFree( pszName ); + + if (pszTmpTracks != NULL) + { + VSIUnlink( pszTmpTracks ); + CPLFree( pszTmpTracks ); + } + + if (pszTmpTrackpoints != NULL) + { + VSIUnlink( pszTmpTrackpoints ); + CPLFree( pszTmpTrackpoints ); + } + + if (poGTMFile != NULL) + delete poGTMFile; +} + + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRGTMDataSource::Open(const char* pszFilename, int bUpdate) +{ + CPLAssert( pszFilename != NULL ); + + /* Should not happen as the driver already returned if bUpdate == NULL */ + if (bUpdate) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GTM driver does not support opening in update mode"); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Create a GTM object and open the source file. */ +/* -------------------------------------------------------------------- */ + poGTMFile = new GTM(); + + if ( !poGTMFile->Open( pszFilename ) ) + { + delete poGTMFile; + poGTMFile = NULL; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Validate it by start parsing */ +/* -------------------------------------------------------------------- */ + if( !poGTMFile->isValid() ) + { + delete poGTMFile; + poGTMFile = NULL; + return FALSE; + } + + pszName = CPLStrdup( pszFilename ); +/* -------------------------------------------------------------------- */ +/* Now, we are able to read the file header and find the position */ +/* of the first waypoint and the position of the first track. */ +/* -------------------------------------------------------------------- */ + if ( !poGTMFile->readHeaderNumbers() ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* We can start reading the file elements */ +/* We are going to translate GTM features into layers */ +/* -------------------------------------------------------------------- */ + char* pszBaseFileName = CPLStrdup( CPLGetBasename(pszFilename) ); + /* We are going to create two layers, one for storing waypoints and + another for storing tracks */ + papoLayers = (OGRGTMLayer **) CPLMalloc(sizeof(void*) * 2); + + + /* Create a spatial reference for WGS8*/ + OGRSpatialReference* poSRS = new OGRSpatialReference(NULL); + poSRS->SetWellKnownGeogCS( "WGS84" ); + + + /* Waypoint layer */ + size_t layerNameSize = strlen(pszBaseFileName) + sizeof("_waypoints"); + char* pszLayerName = (char*) CPLMalloc(layerNameSize); + /* The layer name will be "_waypoints" */ + strcpy (pszLayerName, pszBaseFileName); + CPLStrlcat (pszLayerName, "_waypoints", layerNameSize); + + /* Store the layer of waypoints */ + + GTMWaypointLayer* poWaypointLayer = new GTMWaypointLayer ( pszLayerName, + poSRS, + FALSE, + this ); + papoLayers[nLayers++] = poWaypointLayer; + CPLFree(pszLayerName); + + /* Track layer */ + layerNameSize = strlen(pszBaseFileName) + sizeof("_tracks"); + pszLayerName = (char*) CPLMalloc(layerNameSize); + /* The layer name will be "_tracks" */ + strcpy (pszLayerName, pszBaseFileName); + CPLStrlcat (pszLayerName, "_tracks", layerNameSize); + + CPLFree(pszBaseFileName); + /* Store the layer of tracks */ + GTMTrackLayer* poTrackLayer = new GTMTrackLayer ( pszLayerName, + poSRS, + FALSE, + this ); + papoLayers[nLayers++] = poTrackLayer; + CPLFree(pszLayerName); + + poSRS->Release(); + return TRUE; + +} + + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRGTMDataSource::Create( const char* pszFilename, + CPL_UNUSED char** papszOptions ) +{ + CPLAssert( NULL != pszFilename ); + + if( fpOutput != NULL ) + { + CPLAssert( FALSE ); + return FALSE; + } + + +/* -------------------------------------------------------------------- */ +/* Do not override exiting file. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + + if( VSIStatL( pszFilename, &sStatBuf ) == 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "You have to delete %s before being able to create it with the GTM driver", + pszFilename); + return FALSE; + } + + +/* -------------------------------------------------------------------- */ +/* Create the output file. */ +/* -------------------------------------------------------------------- */ + pszName = CPLStrdup( pszFilename ); + + fpOutput = VSIFOpenL( pszFilename, "w" ); + if( fpOutput == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create GTM file %s.", + pszFilename ); + return FALSE; + } + + // Generate a temporary file for Trackpoints + const char* pszTmpName = CPLGenerateTempFilename(NULL); + pszTmpTrackpoints = CPLStrdup( pszTmpName ); + fpTmpTrackpoints = VSIFOpenL(pszTmpName , "w" ); + if( fpTmpTrackpoints == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create temporary file %s.", + pszTmpName ); + return FALSE; + } + // Generate a temporary file for Tracks + pszTmpName = CPLGenerateTempFilename(NULL); + pszTmpTracks = CPLStrdup( pszTmpName ); + fpTmpTracks = VSIFOpenL(pszTmpName , "w" ); + if( fpTmpTracks == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create temporary file %s.", + pszTmpName ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Output header of GTM file. */ +/* -------------------------------------------------------------------- */ + char* pszBaseFileName = CPLStrdup( CPLGetBasename(pszFilename) ); + int sizeBuffer = 175 + strlen(pszBaseFileName); + void* pBuffer = CPLCalloc(1, sizeBuffer); + void* pCurrentPos = pBuffer; + + // Write version number + appendUShort(pCurrentPos, 211); + pCurrentPos = ((char*)pCurrentPos) + 2; + // Write code + strcpy((char*)pCurrentPos, "TrackMaker"); + // gradnum + pCurrentPos = (char*) pBuffer + 14; + appendUChar(pCurrentPos, 8); + // bcolor + pCurrentPos = (char*) pBuffer + 23; + appendInt(pCurrentPos, 0xffffff); + // nwptstyles -- We just create the defaults, so four + pCurrentPos = (char*) pBuffer + 27; + appendInt(pCurrentPos, 4); + // gradfont, labelfont + pCurrentPos = (char*) pBuffer + 99; + for (int i = 0; i < 2; i++) + { + appendUShort(pCurrentPos, 5); + pCurrentPos = ((char*)pCurrentPos) + 2; + strcpy((char*)pCurrentPos, "Arial"); + pCurrentPos = ((char*)pCurrentPos) + 5; + } + appendUShort(pCurrentPos, (unsigned short) strlen(pszBaseFileName)); + pCurrentPos = ((char*)pCurrentPos) + 2; + strcpy((char*)pCurrentPos, pszBaseFileName); + + // write ndatum. We are implementing just WGS84, so write the + // correspondig value for WGS84 + pCurrentPos = ((char*) pBuffer) + 151 + strlen(pszBaseFileName); + appendInt(pCurrentPos, 217); + + VSIFWriteL(pBuffer, sizeBuffer, 1, fpOutput); + + CPLFree(pszBaseFileName); + CPLFree(pBuffer); + return TRUE; +} + + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ +OGRLayer* OGRGTMDataSource::GetLayer( int iLayer ) +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * OGRGTMDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + CPL_UNUSED char ** papszOptions ) +{ + if (eType == wkbPoint || eType == wkbPoint25D) + { + // Waypoints + nLayers++; + papoLayers = (OGRGTMLayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRGTMLayer*)); + papoLayers[nLayers-1] = new GTMWaypointLayer( pszName, poSRS, TRUE, this ); + return papoLayers[nLayers-1]; + + } + else if (eType == wkbLineString || eType == wkbLineString25D || + eType == wkbMultiLineString || eType == wkbMultiLineString25D) + { + // Tracks + nLayers++; + papoLayers = (OGRGTMLayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRGTMLayer*)); + papoLayers[nLayers-1] = new GTMTrackLayer( pszName, poSRS, TRUE, this ); + return papoLayers[nLayers-1]; + } + else if (eType == wkbUnknown) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot create GTM layer %s with unknown geometry type", pszLayerName); + return NULL; + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Geometry type of `%s' not supported in GTM.\n", + OGRGeometryTypeToName(eType) ); + return NULL; + } + +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGTMDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* Methods for creating a new gtm file */ +/************************************************************************/ +void OGRGTMDataSource::checkBounds(float newLat, + float newLon) +{ + if (minlat == 0 && maxlat == 0 && + minlon == 0 && maxlon == 0) + { + minlat = newLat; + maxlat = newLat; + minlon = newLon; + maxlon = newLon; + } + else + { + minlat = MIN(newLat, minlat); + maxlat = MAX(newLat, maxlat); + minlon = MIN(newLon, minlon); + maxlon = MAX(newLon, maxlon); + } +} + + + +/************************************************************************/ +/* Methods for reading existent file */ +/************************************************************************/ + + +/*======================================================================*/ +/* Waypoint Methods */ +/*======================================================================*/ + + +/*----------------------------------------------------------------------*/ +/* getNWpts() */ +/*----------------------------------------------------------------------*/ + +int OGRGTMDataSource::getNWpts() +{ + if (poGTMFile == NULL) + return 0; + + return poGTMFile->getNWpts(); +} + + +/*----------------------------------------------------------------------*/ +/* hasNextWaypoint() */ +/*----------------------------------------------------------------------*/ +bool OGRGTMDataSource::hasNextWaypoint() +{ + if (poGTMFile == NULL) + return FALSE; + + return poGTMFile->hasNextWaypoint(); +} + + +/*----------------------------------------------------------------------*/ +/* fetchNextWaypoint() */ +/*----------------------------------------------------------------------*/ +Waypoint* OGRGTMDataSource::fetchNextWaypoint() +{ + if (poGTMFile == NULL) + return NULL; + + return poGTMFile->fetchNextWaypoint(); +} + + +/*----------------------------------------------------------------------*/ +/* rewindWaypoint() */ +/*----------------------------------------------------------------------*/ +void OGRGTMDataSource::rewindWaypoint() +{ + if (poGTMFile == NULL) + return; + + poGTMFile->rewindWaypoint(); +} + + + +/*======================================================================*/ +/* Tracks Methods */ +/*======================================================================*/ + + +/*----------------------------------------------------------------------*/ +/* getNTracks() */ +/*----------------------------------------------------------------------*/ + +int OGRGTMDataSource::getNTracks() +{ + if (poGTMFile == NULL) + return 0; + + return poGTMFile->getNTracks(); +} + + +/*----------------------------------------------------------------------*/ +/* hasNextTrack() */ +/*----------------------------------------------------------------------*/ +bool OGRGTMDataSource::hasNextTrack() +{ + if (poGTMFile == NULL) + return FALSE; + + return poGTMFile->hasNextTrack(); +} + + +/*----------------------------------------------------------------------*/ +/* fetchNextTrack() */ +/*----------------------------------------------------------------------*/ +Track* OGRGTMDataSource::fetchNextTrack() +{ + if (poGTMFile == NULL) + return NULL; + + return poGTMFile->fetchNextTrack(); +} + + +/*----------------------------------------------------------------------*/ +/* rewindTrack() */ +/*----------------------------------------------------------------------*/ +void OGRGTMDataSource::rewindTrack() +{ + if (poGTMFile == NULL) + return; + + poGTMFile->rewindTrack(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/ogrgtmdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/ogrgtmdriver.cpp new file mode 100644 index 000000000..5881e5e7b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/ogrgtmdriver.cpp @@ -0,0 +1,125 @@ +/****************************************************************************** + * $Id: ogrgtmdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: GTM Driver + * Purpose: Implementation of OGRGTMDriver class. + * Author: Leonardo de Paula Rosa Piga; http://lampiao.lsc.ic.unicamp.br/~piga + * + ****************************************************************************** + * Copyright (c) 2009, Leonardo de Paula Rosa Piga + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogr_gtm.h" +#include "cpl_conv.h" +#include "cpl_error.h" + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRGTMDriverOpen( GDALOpenInfo* poOpenInfo ) +{ + if( poOpenInfo->eAccess == GA_Update || + poOpenInfo->fpL == NULL || + poOpenInfo->nHeaderBytes < 13) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If it looks like a GZip header, this may be a .gtz file, so */ +/* try opening with the /vsigzip/ prefix */ +/* -------------------------------------------------------------------- */ + if (poOpenInfo->pabyHeader[0] == 0x1f && ((unsigned char*)poOpenInfo->pabyHeader)[1] == 0x8b && + strncmp(poOpenInfo->pszFilename, "/vsigzip/", strlen("/vsigzip/")) != 0) + { + /* ok */ + } + else + { + short version = CPL_LSBINT16PTR(poOpenInfo->pabyHeader); + if (version != 211 || + strncmp((const char*)poOpenInfo->pabyHeader + 2, "TrackMaker", strlen("TrackMaker")) != 0 ) + { + return NULL; + } + } + + OGRGTMDataSource *poDS = new OGRGTMDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, FALSE ) ) + { + delete poDS; + poDS = NULL; + } + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRGTMDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + CPLAssert( NULL != pszName ); + CPLDebug( "GTM", "Attempt to create: %s", pszName ); + + OGRGTMDataSource *poDS = new OGRGTMDataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRGTM() */ +/************************************************************************/ + +void RegisterOGRGTM() +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "GPSTrackMaker" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "GPSTrackMaker" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "GPSTrackMaker" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "gtm gtz" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_gtm.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRGTMDriverOpen; + poDriver->pfnCreate = OGRGTMDriverCreate; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/ogrgtmlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/ogrgtmlayer.cpp new file mode 100644 index 000000000..93f3de155 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/gtm/ogrgtmlayer.cpp @@ -0,0 +1,154 @@ +/****************************************************************************** + * $Id: ogrgtmlayer.cpp 27884 2014-10-19 22:27:48Z rouault $ + * + * Project: GTM Driver + * Purpose: Implementation of OGRGTMLayer class. + * Author: Leonardo de Paula Rosa Piga; http://lampiao.lsc.ic.unicamp.br/~piga + * + ****************************************************************************** + * Copyright (c) 2009, Leonardo de Paula Rosa Piga + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_gtm.h" + +OGRGTMLayer::OGRGTMLayer() +{ + poDS = NULL; + poSRS = NULL; + poCT = NULL; + pszName = NULL; + poFeatureDefn = NULL; + nNextFID = 0; + nTotalFCount = 0; + bError = FALSE; +} + +OGRGTMLayer::~OGRGTMLayer() +{ + if (poFeatureDefn != NULL) + { + poFeatureDefn->Release(); + poFeatureDefn = NULL; + } + + if (poSRS != NULL) + { + poSRS->Release(); + poSRS = NULL; + } + + if (poCT != NULL) + { + delete poCT; + poCT = NULL; + } + + CPLFree( pszName ); +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn* OGRGTMLayer::GetLayerDefn() +{ + return poFeatureDefn; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRGTMLayer::TestCapability( const char * pszCap ) +{ + if (EQUAL(pszCap,OLCFastFeatureCount) && + m_poFilterGeom == NULL && m_poAttrQuery == NULL ) + return TRUE; + else if( EQUAL(pszCap,OLCCreateField) ) + return poDS != NULL && poDS->getOutputFP() != NULL; + else if( EQUAL(pszCap,OLCSequentialWrite) ) + return poDS != NULL && poDS->getOutputFP() != NULL; + else + return FALSE; +} + + +/************************************************************************/ +/* CheckAndFixCoordinatesValidity() */ +/************************************************************************/ + +OGRErr OGRGTMLayer::CheckAndFixCoordinatesValidity( double& pdfLatitude, double& pdfLongitude ) +{ + if (pdfLatitude < -90 || pdfLatitude > 90) + { + static int bFirstWarning = TRUE; + if (bFirstWarning) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Latitude %f is invalid. Valid range is [-90,90]. This warning will not be issued any more", + pdfLatitude); + bFirstWarning = FALSE; + } + return CE_Failure; + } + + if (pdfLongitude < -180 || pdfLongitude > 180) + { + static int bFirstWarning = TRUE; + if (bFirstWarning) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Longitude %f has been modified to fit into range [-180,180]. This warning will not be issued any more", + pdfLongitude); + bFirstWarning = FALSE; + } + + if (pdfLongitude > 180) + pdfLongitude -= ((int) ((pdfLongitude+180)/360)*360); + else if (pdfLongitude < -180) + pdfLongitude += ((int) (180 - pdfLongitude)/360)*360; + + return CE_None; + } + + return CE_None; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRGTMLayer::CreateField( OGRFieldDefn *poField, + CPL_UNUSED int bApproxOK ) +{ + for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + if (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), + poField->GetNameRef() ) == 0) + { + return OGRERR_NONE; + } + } + CPLError(CE_Failure, CPLE_NotSupported, + "Field of name '%s' is not supported. ", + poField->GetNameRef()); + return OGRERR_FAILURE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/GNUmakefile new file mode 100644 index 000000000..2c1d32b80 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrhtfdriver.o ogrhtfdatasource.o ogrhtflayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_htf.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/drv_htf.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/drv_htf.html new file mode 100644 index 000000000..95c5f9aef --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/drv_htf.html @@ -0,0 +1,72 @@ + + +HTF - Hydrographic Transfer Format + + + + +

    HTF - Hydrographic Transfer Format

    + +(GDAL/OGR >= 1.8.0)

    + +This driver reads files containg sounding data following the Hydrographic Transfer Format (HTF), +which is used by the Australian Hydrographic Office (AHO).

    + +The driver has been developed based on HTF 2.02 specification.

    + +The file must be georeferenced in UTM WGS84 to be considered valid by the driver.

    + +The driver returns 2 spatial layers : a layer named "polygon" and a layer name "sounding". +There is also a "hidden" layer, called "metadata", that can be fetched with GetLayerByName(), and +which contains a single feature, made of the header lines of the file.

    + +Polygons are used to distinguish between differing survey categories, such that any +significant changes in position/depth accuracy and/or a change in the seafloor coverage +will dictate a separate bounding polygon contains polygons.

    + +The "polygon" layer contains the following fields : +

      +
    • DESCRIPTION : Defines the polygons of each region of similar survey criteria or theme.
    • +
    • IDENTIFIER : Unique polygon identifier for this transmittal.
    • +
    • SEAFLOOR_COVERAGE : All significant seafloor features detected (full ensonification/sweep) or full coverage not achieved and uncharted features may exist.
    • +
    • POSITION_ACCURACY : +/- NNN.n metres at 95% CI (2.45) with respect to the given datum.
    • +
    • DEPTH_ACCURACY : +/- NN.n metres at 95% CI (2.00) at critical depths.
    • +
    + +The "sounding" layer should contain - at minimum - the following 20 fields : +
      +
    • REJECTED_SOUNDING : if 0 sounding is valid or if 1 the sounding has been rejected (flagged).
    • +
    • LINE_NAME : Survey line name/number as a unique identifer within the survey.
    • +
    • FIX_NUMBER : Sequential sounding fix number, unique within the survey.
    • +
    • UTC_DATE : UTC date for the sounding CCYYMMDD.
    • +
    • UTC_TIME : UTC time for the sounding HHMMSS.ss.
    • +
    • LATITUDE : Latitude position of the sounding +/-NN.nnnnnn (degrees of arc, south is negative).
    • +
    • LONGITUDE : Longitude position of the sounding +/-NNN.nnnnnn (degrees of arc, west is negative).
    • +
    • EASTING : Grid coordinate position of the sounding in metres NNNNNNN.n.
    • +
    • NORTHING : Grid coordinate position of the sounding in metres NNNNNNN.n.
    • +
    • DEPTH : Reduced sounding value in metres with corrections applied as indicated in the relevant fields, soundings are positive and drying heights are negative +/-NNNN.nn metres.
    • +
    • POSITIONING_SENSOR : Indicate position system number populated in the HTF header record.
    • +
    • DEPTH_SENSOR : Indicate depth sounder system number populated in the HTF header record.
    • +
    • TPE_POSITION : Total propagated error of the horizontal component for the sounding.
    • +
    • TPE_DEPTH : Total propagated error of the vertical component for the sounding.
    • +
    • NBA FLAG : No Bottom at Flag, if 0 not NBA depth or if 1 Depth is NBA, deeper water probably exists.
    • +
    • TIDE : Value of the tidal correction applied +/- NN.nn metres.
    • +
    • DEEP_WATER_CORRECTION : Value of the deep water sounding velocity applied +/- NN.nn metres.
    • +
    • VERTICAL BIAS_CORRECTION : Value of the vertical bias applied +/- NN.nn metres. eg transducer depth correction
    • +
    • SOUND_VELOCITY : Measured sound velocity used to process sounding in metres per second IIII.
    • +
    • PLOTTED_SOUNDING : if 0 then the reduced depth did not appear on the original fairsheet or id 1 then the reduced depth appeared on the original fairsheet.
    • +
    + +Some fields may be never set, depending on the value of the Field Population Key. Extra fields may also be added.

    + + + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/makefile.vc new file mode 100644 index 000000000..3f62bf3ab --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrhtfdriver.obj ogrhtfdatasource.obj ogrhtflayer.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/ogr_htf.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/ogr_htf.h new file mode 100644 index 000000000..9f5017b00 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/ogr_htf.h @@ -0,0 +1,172 @@ +/****************************************************************************** + * $Id: ogr_htf.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: HTF Translator + * Purpose: Definition of classes for OGR .htf driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_HTF_H_INCLUDED +#define _OGR_HTF_H_INCLUDED + +#include "ogrsf_frmts.h" + +#include + +/************************************************************************/ +/* OGRHTFLayer */ +/************************************************************************/ + +class OGRHTFLayer : public OGRLayer +{ +protected: + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference *poSRS; + + VSILFILE* fpHTF; + int bEOF; + + int nNextFID; + + virtual OGRFeature * GetNextRawFeature() = 0; + + int bHasExtent; + double dfMinX; + double dfMinY; + double dfMaxX; + double dfMaxY; + + public: + OGRHTFLayer(const char* pszFilename, int nZone, int bIsNorth); + ~OGRHTFLayer(); + + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); + + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + void SetExtent(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY); + +}; + +/************************************************************************/ +/* OGRHTFPolygonLayer */ +/************************************************************************/ + +class OGRHTFPolygonLayer : public OGRHTFLayer +{ +protected: + virtual OGRFeature * GetNextRawFeature(); + + public: + OGRHTFPolygonLayer(const char* pszFilename, int nZone, int bIsNorth); + + virtual void ResetReading(); +}; + +/************************************************************************/ +/* OGRHTFSoundingLayer */ +/************************************************************************/ + +class OGRHTFSoundingLayer : public OGRHTFLayer +{ +private: + int bHasFPK; + int nFieldsPresent; + int *panFieldPresence; + int nEastingIndex, nNorthingIndex; + int nTotalSoundings; + +protected: + virtual OGRFeature * GetNextRawFeature(); + + public: + OGRHTFSoundingLayer(const char* pszFilename, int nZone, int bIsNorth, int nTotalSoundings); + ~OGRHTFSoundingLayer(); + + virtual void ResetReading(); + + virtual int TestCapability( const char * ); + + virtual GIntBig GetFeatureCount(int bForce = TRUE); +}; + +/************************************************************************/ +/* OGRHTFMetadataLayer */ +/************************************************************************/ + +class OGRHTFMetadataLayer : public OGRLayer +{ +protected: + OGRFeatureDefn *poFeatureDefn; + OGRFeature *poFeature; + std::vector aosMD; + + int nNextFID; + + public: + OGRHTFMetadataLayer(std::vector aosMD); + ~OGRHTFMetadataLayer(); + + + virtual void ResetReading() { nNextFID = 0; } + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ) { return FALSE; } +}; + +/************************************************************************/ +/* OGRHTFDataSource */ +/************************************************************************/ + +class OGRHTFDataSource : public OGRDataSource +{ + char* pszName; + + OGRHTFLayer** papoLayers; + int nLayers; + OGRLayer *poMetadataLayer; + + public: + OGRHTFDataSource(); + ~OGRHTFDataSource(); + + int Open( const char * pszFilename ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + virtual OGRLayer* GetLayerByName( const char* pszLayerName ); + + virtual int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_HTF_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/ogrhtfdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/ogrhtfdatasource.cpp new file mode 100644 index 000000000..3aecce39d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/ogrhtfdatasource.cpp @@ -0,0 +1,232 @@ +/****************************************************************************** + * $Id: ogrhtfdatasource.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: HTF Translator + * Purpose: Implements OGRHTFDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_htf.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrhtfdatasource.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* OGRHTFDataSource() */ +/************************************************************************/ + +OGRHTFDataSource::OGRHTFDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + poMetadataLayer = NULL; + + pszName = NULL; +} + +/************************************************************************/ +/* ~OGRHTFDataSource() */ +/************************************************************************/ + +OGRHTFDataSource::~OGRHTFDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + delete poMetadataLayer; + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRHTFDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRHTFDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer* OGRHTFDataSource::GetLayerByName( const char* pszLayerName ) +{ + if (nLayers == 0) + return NULL; + if (EQUAL(pszLayerName, "polygon")) + return papoLayers[0]; + if (EQUAL(pszLayerName, "sounding")) + return papoLayers[1]; + if (EQUAL(pszLayerName, "metadata")) + return poMetadataLayer; + return NULL; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRHTFDataSource::Open( const char * pszFilename ) + +{ + pszName = CPLStrdup( pszFilename ); + +// -------------------------------------------------------------------- +// Does this appear to be a .htf file? +// -------------------------------------------------------------------- + + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return FALSE; + + const char* pszLine; + int bEndOfHTFHeader = FALSE; + int bIsSouth = FALSE; + int bGeodeticDatumIsWGS84 = FALSE; + int bIsUTM = FALSE; + int nZone = 0; + int nLines = 0; + int bHasSWEasting = FALSE, bHasSWNorthing = FALSE, bHasNEEasting = FALSE, bHasNENorthing = FALSE; + double dfSWEasting = 0, dfSWNorthing = 0, dfNEEasting = 0, dfNENorthing = 0; + std::vector aosMD; + int nTotalSoundings = 0; + while( (pszLine = CPLReadLine2L(fp, 1024, NULL)) != NULL) + { + nLines ++; + if (nLines == 1000) + { + break; + } + if (*pszLine == ';' || *pszLine == '\0') + continue; + + if (strcmp(pszLine, "END OF HTF HEADER") == 0) + { + bEndOfHTFHeader = TRUE; + break; + } + + aosMD.push_back(pszLine); + + if (strncmp(pszLine, "GEODETIC DATUM: ", 16) == 0) + { + if (strcmp(pszLine + 16, "WG84") == 0 || + strcmp(pszLine + 16, "WGS84") == 0) + bGeodeticDatumIsWGS84 = TRUE; + else + { + VSIFCloseL(fp); + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported datum : %s", pszLine + 16); + return FALSE; + } + } + else if (strncmp(pszLine, "NE LATITUDE: -", 14) == 0) + bIsSouth = TRUE; + else if (strncmp(pszLine, "GRID REFERENCE SYSTEM: ", 23) == 0) + { + if (strncmp(pszLine + 23, "UTM", 3) == 0) + bIsUTM = TRUE; + else + { + VSIFCloseL(fp); + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported grid : %s", pszLine + 23); + return FALSE; + } + } + else if (strncmp(pszLine, "GRID ZONE: ", 11) == 0) + { + nZone = atoi(pszLine + 11); + } + else if (strncmp(pszLine, "SW GRID COORDINATE - EASTING: ", 30) == 0) + { + bHasSWEasting = TRUE; + dfSWEasting = CPLAtof(pszLine + 30); + } + else if (strncmp(pszLine, "SW GRID COORDINATE - NORTHING: ", 31) == 0) + { + bHasSWNorthing = TRUE; + dfSWNorthing = CPLAtof(pszLine + 31); + } + else if (strncmp(pszLine, "NE GRID COORDINATE - EASTING: ", 30) == 0) + { + bHasNEEasting = TRUE; + dfNEEasting = CPLAtof(pszLine + 30); + } + else if (strncmp(pszLine, "NE GRID COORDINATE - NORTHING: ", 31) == 0) + { + bHasNENorthing = TRUE; + dfNENorthing = CPLAtof(pszLine + 31); + } + else if (strncmp(pszLine, "TOTAL SOUNDINGS: ", 17) == 0) + { + nTotalSoundings = atoi(pszLine + 17); + } + } + + VSIFCloseL(fp); + + if (!bEndOfHTFHeader) + return FALSE; + if (!bGeodeticDatumIsWGS84) + return FALSE; + if (!bIsUTM) + return FALSE; + if (nZone == 0) + return FALSE; + + nLayers = 2; + papoLayers = (OGRHTFLayer**) CPLMalloc(sizeof(OGRHTFLayer*) * 2); + papoLayers[0] = new OGRHTFPolygonLayer(pszFilename, nZone, !bIsSouth); + papoLayers[1] = new OGRHTFSoundingLayer(pszFilename, nZone, !bIsSouth, nTotalSoundings); + + if (bHasSWEasting && bHasSWNorthing && bHasNEEasting && bHasNENorthing) + { + papoLayers[0]->SetExtent(dfSWEasting, dfSWNorthing, dfNEEasting, dfNENorthing); + papoLayers[1]->SetExtent(dfSWEasting, dfSWNorthing, dfNEEasting, dfNENorthing); + } + + poMetadataLayer = new OGRHTFMetadataLayer(aosMD); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/ogrhtfdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/ogrhtfdriver.cpp new file mode 100644 index 000000000..a4ebf6e4d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/ogrhtfdriver.cpp @@ -0,0 +1,89 @@ +/****************************************************************************** + * $Id: ogrhtfdriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: HTF Translator + * Purpose: Implements OGRHTFDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_htf.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrhtfdriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +extern "C" void RegisterOGRHTF(); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRHTFDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->eAccess == GA_Update || + poOpenInfo->fpL == NULL ) + return NULL; + + if( strncmp((const char*)poOpenInfo->pabyHeader, "HTF HEADER", strlen("HTF HEADER")) != 0 ) + return NULL; + + OGRHTFDataSource *poDS = new OGRHTFDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRHTF() */ +/************************************************************************/ + +void RegisterOGRHTF() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "HTF" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "HTF" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Hydrographic Transfer Vector" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_htf.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRHTFDriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/ogrhtflayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/ogrhtflayer.cpp new file mode 100644 index 000000000..295ac8acb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/htf/ogrhtflayer.cpp @@ -0,0 +1,716 @@ +/****************************************************************************** + * $Id: ogrhtflayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: HTF Translator + * Purpose: Implements OGRHTFLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_htf.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: ogrhtflayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRHTFLayer() */ +/************************************************************************/ + +OGRHTFLayer::OGRHTFLayer( const char* pszFilename, int nZone, int bIsNorth ) + +{ + fpHTF = VSIFOpenL(pszFilename, "rb"); + nNextFID = 0; + bEOF = FALSE; + + poSRS = new OGRSpatialReference(SRS_WKT_WGS84); + poSRS->SetUTM( nZone, bIsNorth ); + + bHasExtent = FALSE; + dfMinX = 0; + dfMinY = 0; + dfMaxX = 0; + dfMaxY = 0; +} + +/************************************************************************/ +/* OGRHTFPolygonLayer() */ +/************************************************************************/ + +OGRHTFPolygonLayer::OGRHTFPolygonLayer( const char* pszFilename, int nZone, int bIsNorth ) : + OGRHTFLayer(pszFilename, nZone, bIsNorth) + +{ + poFeatureDefn = new OGRFeatureDefn( "polygon" ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPolygon ); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + OGRFieldDefn oField1( "DESCRIPTION", OFTString); + poFeatureDefn->AddFieldDefn( &oField1 ); + OGRFieldDefn oField2( "IDENTIFIER", OFTInteger); + poFeatureDefn->AddFieldDefn( &oField2 ); + OGRFieldDefn oField3( "SEAFLOOR_COVERAGE", OFTString); + poFeatureDefn->AddFieldDefn( &oField3 ); + OGRFieldDefn oField4( "POSITION_ACCURACY", OFTReal); + poFeatureDefn->AddFieldDefn( &oField4 ); + OGRFieldDefn oField5( "DEPTH_ACCURACY", OFTReal); + poFeatureDefn->AddFieldDefn( &oField5 ); + + ResetReading(); +} + +/************************************************************************/ +/* OGRHTFSoundingLayer() */ +/************************************************************************/ + +OGRHTFSoundingLayer::OGRHTFSoundingLayer( const char* pszFilename, int nZone, int bIsNorth, int nTotalSoundings ) : + OGRHTFLayer(pszFilename, nZone, bIsNorth) + +{ + poFeatureDefn = new OGRFeatureDefn( "sounding" ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint ); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + this->nTotalSoundings = nTotalSoundings; + bHasFPK = FALSE; + nFieldsPresent = 0; + panFieldPresence = NULL; + nEastingIndex = -1; + nNorthingIndex = -1; + + const char* pszLine; + int bSoundingHeader = FALSE; + while( fpHTF != NULL && + (pszLine = CPLReadLine2L(fpHTF, 1024, NULL)) != NULL) + { + if (strncmp(pszLine, "SOUNDING HEADER", strlen("SOUNDING HEADER")) == 0) + bSoundingHeader = TRUE; + else if (bSoundingHeader && strlen(pszLine) > 10 && + pszLine[0] == '[' && pszLine[3] == ']' && + pszLine[4] == ' ' && + strstr(pszLine + 5, " =") != NULL) + { + char* pszName = CPLStrdup(pszLine + 5); + *strstr(pszName, " =") = 0; + char* pszPtr = pszName; + for(;*pszPtr;pszPtr++) + { + if (*pszPtr == ' ') + *pszPtr = '_'; + } + OGRFieldType eType; + if (strcmp(pszName, "REJECTED_SOUNDING") == 0 || + strcmp(pszName, "FIX_NUMBER") == 0 || + strcmp(pszName, "NBA_FLAG") == 0 || + strcmp(pszName, "SOUND_VELOCITY") == 0 || + strcmp(pszName, "PLOTTED_SOUNDING") == 0) + eType = OFTInteger; + else if (strcmp(pszName, "LATITUDE") == 0 || + strcmp(pszName, "LONGITUDE") == 0 || + strcmp(pszName, "EASTING") == 0 || + strcmp(pszName, "NORTHING") == 0 || + strcmp(pszName, "DEPTH") == 0 || + strcmp(pszName, "TPE_POSITION") == 0 || + strcmp(pszName, "TPE_DEPTH") == 0 || + strcmp(pszName, "TIDE") == 0 || + strcmp(pszName, "DEEP_WATER_CORRECTION") == 0 || + strcmp(pszName, "VERTICAL_BIAS_CORRECTION") == 0) + eType = OFTReal; + else + eType = OFTString; + OGRFieldDefn oField( pszName, eType); + poFeatureDefn->AddFieldDefn( &oField); + CPLFree(pszName); + } + else if (strcmp(pszLine, "END OF SOUNDING HEADER") == 0) + { + bSoundingHeader = FALSE; + } + else if (strcmp(pszLine, "SOUNDING DATA") == 0) + { + pszLine = CPLReadLine2L(fpHTF, 1024, NULL); + if (pszLine == NULL) + break; + if (pszLine[0] == '[' && + (int)strlen(pszLine) == 2 + poFeatureDefn->GetFieldCount()) + { + bHasFPK = TRUE; + panFieldPresence = (int*)CPLMalloc(sizeof(int) * + poFeatureDefn->GetFieldCount()); + int i; + for(i=0;iGetFieldCount();i++) + { + panFieldPresence[i] = pszLine[1 + i] != '0'; + nFieldsPresent += panFieldPresence[i]; + } + } + break; + } + } + + if (!bHasFPK) + { + panFieldPresence = (int*)CPLMalloc(sizeof(int) * + poFeatureDefn->GetFieldCount()); + int i; + for(i=0;iGetFieldCount();i++) + panFieldPresence[i] = TRUE; + nFieldsPresent = poFeatureDefn->GetFieldCount(); + } + + int nIndex; + nIndex = poFeatureDefn->GetFieldIndex("EASTING"); + if (nIndex < 0 || !panFieldPresence[nIndex]) + { + CPLError(CE_Failure, CPLE_NotSupported, "Cannot find EASTING field"); + VSIFCloseL( fpHTF ); + fpHTF = NULL; + return; + } + nEastingIndex = nIndex; + nIndex = poFeatureDefn->GetFieldIndex("NORTHING"); + if (nIndex < 0 || !panFieldPresence[nIndex]) + { + CPLError(CE_Failure, CPLE_NotSupported, "Cannot find NORTHING field"); + VSIFCloseL( fpHTF ); + fpHTF = NULL; + return; + } + nNorthingIndex = nIndex; + + ResetReading(); +} + +/************************************************************************/ +/* ~OGRHTFLayer() */ +/************************************************************************/ + +OGRHTFLayer::~OGRHTFLayer() + +{ + if( poSRS != NULL ) + poSRS->Release(); + + poFeatureDefn->Release(); + + if (fpHTF) + VSIFCloseL( fpHTF ); +} + + +/************************************************************************/ +/* ~OGRHTFSoundingLayer() */ +/************************************************************************/ + +OGRHTFSoundingLayer::~OGRHTFSoundingLayer() + +{ + CPLFree(panFieldPresence); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRHTFLayer::ResetReading() + +{ + nNextFID = 0; + bEOF = FALSE; + if (fpHTF) + { + VSIFSeekL( fpHTF, 0, SEEK_SET ); + } +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRHTFPolygonLayer::ResetReading() + +{ + OGRHTFLayer::ResetReading(); + if (fpHTF) + { + const char* pszLine; + while( (pszLine = CPLReadLine2L(fpHTF, 1024, NULL)) != NULL) + { + if (strcmp(pszLine, "POLYGON DATA") == 0) + { + break; + } + } + if (pszLine == NULL) + bEOF = TRUE; + } +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRHTFSoundingLayer::ResetReading() + +{ + OGRHTFLayer::ResetReading(); + if (fpHTF) + { + const char* pszLine; + while( (pszLine = CPLReadLine2L(fpHTF, 1024, NULL)) != NULL) + { + if (strcmp(pszLine, "SOUNDING DATA") == 0) + { + if (bHasFPK) + pszLine = CPLReadLine2L(fpHTF, 1024, NULL); + break; + } + } + if (pszLine == NULL) + bEOF = TRUE; + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRHTFLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + if (fpHTF == NULL || bEOF) + return NULL; + + while(!bEOF) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } + + return NULL; +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRHTFPolygonLayer::GetNextRawFeature() +{ + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + const char* pszLine; + + OGRLinearRing oLR; + int bHastFirstCoord = FALSE; + double dfFirstEasting = 0, dfFirstNorthing = 0; + double dfIslandEasting = 0, dfIslandNorthing = 0; + int bInIsland = FALSE; + OGRPolygon* poPoly = new OGRPolygon(); + + while( (pszLine = CPLReadLine2L(fpHTF, 1024, NULL)) != NULL) + { + if (pszLine[0] == ';') + { + /* comment */ ; + } + else if (pszLine[0] == 0) + { + /* end of polygon is marked by a blank line */ + break; + } + else if (strncmp(pszLine, "POLYGON DESCRIPTION: ", + strlen("POLYGON DESCRIPTION: ")) == 0) + { + poFeature->SetField(0, pszLine + strlen("POLYGON DESCRIPTION: ")); + } + else if (strncmp(pszLine, "POLYGON IDENTIFIER: ", + strlen("POLYGON IDENTIFIER: ")) == 0) + { + poFeature->SetField(1, pszLine + strlen("POLYGON IDENTIFIER: ")); + } + else if (strncmp(pszLine, "SEAFLOOR COVERAGE: ", + strlen("SEAFLOOR COVERAGE:")) == 0) + { + const char* pszVal = pszLine + strlen("SEAFLOOR COVERAGE: "); + if (*pszVal != '*') + poFeature->SetField(2, pszVal); + } + else if (strncmp(pszLine, "POSITION ACCURACY: ", + strlen("POSITION ACCURACY:")) == 0) + { + const char* pszVal = pszLine + strlen("POSITION ACCURACY: "); + if (*pszVal != '*') + poFeature->SetField(3, pszVal); + } + else if (strncmp(pszLine, "DEPTH ACCURACY: ", + strlen("DEPTH ACCURACY:")) == 0) + { + const char* pszVal = pszLine + strlen("DEPTH ACCURACY: "); + if (*pszVal != '*') + poFeature->SetField(4, pszVal); + } + else if (strcmp(pszLine, "END OF POLYGON DATA") == 0) + { + bEOF = TRUE; + break; + } + else + { + char** papszTokens = CSLTokenizeString(pszLine); + if (CSLCount(papszTokens) == 4) + { + double dfEasting = CPLAtof(papszTokens[2]); + double dfNorthing = CPLAtof(papszTokens[3]); + if (!bHastFirstCoord) + { + bHastFirstCoord = TRUE; + dfFirstEasting = dfEasting; + dfFirstNorthing = dfNorthing; + oLR.addPoint(dfEasting, dfNorthing); + } + else if (dfFirstEasting == dfEasting && + dfFirstNorthing == dfNorthing) + { + if (!bInIsland) + { + oLR.addPoint(dfEasting, dfNorthing); + poPoly->addRing(&oLR); + oLR.empty(); + bInIsland = TRUE; + } + } + else if (bInIsland && oLR.getNumPoints() == 0) + { + dfIslandEasting = dfEasting; + dfIslandNorthing = dfNorthing; + oLR.addPoint(dfEasting, dfNorthing); + } + else if (bInIsland && dfIslandEasting == dfEasting && + dfIslandNorthing == dfNorthing) + { + oLR.addPoint(dfEasting, dfNorthing); + poPoly->addRing(&oLR); + oLR.empty(); + } + else + { + oLR.addPoint(dfEasting, dfNorthing); + } + } + CSLDestroy(papszTokens); + } + } + + if (pszLine == NULL) + bEOF = TRUE; + + if (oLR.getNumPoints() >= 3) + { + oLR.closeRings(); + poPoly->addRing(&oLR); + } + poPoly->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poPoly); + poFeature->SetFID(nNextFID++); + + return poFeature; +} + + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRHTFSoundingLayer::GetNextRawFeature() +{ + const char* pszLine; + + OGRLinearRing oLR; + + while( (pszLine = CPLReadLine2L(fpHTF, 1024, NULL)) != NULL) + { + if (pszLine[0] == ';') + { + /* comment */ ; + } + else if (pszLine[0] == 0) + { + bEOF = TRUE; + return NULL; + } + else if (strcmp(pszLine, "END OF SOUNDING DATA") == 0) + { + bEOF = TRUE; + return NULL; + } + else + break; + } + if (pszLine == NULL) + { + bEOF = TRUE; + return NULL; + } + + int i; + double dfEasting = 0, dfNorthing = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + char* pszStr = (char*)pszLine; + for(i=0;iGetFieldCount();i++) + { + if (!panFieldPresence[i]) + continue; + + char* pszSpace = strchr(pszStr, ' '); + if (pszSpace) + *pszSpace = '\0'; + + if (strcmp(pszStr, "*") != 0) + poFeature->SetField(i, pszStr); + if (i == nEastingIndex) + dfEasting = poFeature->GetFieldAsDouble(i); + else if (i == nNorthingIndex) + dfNorthing = poFeature->GetFieldAsDouble(i); + + if (pszSpace == NULL) + break; + pszStr = pszSpace + 1; + } + OGRPoint* poPoint = new OGRPoint(dfEasting, dfNorthing); + poPoint->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poPoint); + poFeature->SetFID(nNextFID++); + return poFeature; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRHTFSoundingLayer::GetFeatureCount(int bForce) +{ + if (m_poFilterGeom != NULL || m_poAttrQuery != NULL) + return OGRHTFLayer::GetFeatureCount(bForce); + + if (nTotalSoundings != 0) + return nTotalSoundings; + + ResetReading(); + if (fpHTF == NULL) + return 0; + + int nCount = 0; + const char* pszLine; + while( (pszLine = CPLReadLine2L(fpHTF, 1024, NULL)) != NULL) + { + if (pszLine[0] == ';') + { + /* comment */ ; + } + else if (pszLine[0] == 0) + break; + else if (strcmp(pszLine, "END OF SOUNDING DATA") == 0) + break; + else + nCount ++; + } + + ResetReading(); + return nCount; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRHTFLayer::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap, OLCFastGetExtent)) + return bHasExtent; + + return FALSE; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRHTFSoundingLayer::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap, OLCFastFeatureCount)) + return m_poFilterGeom == NULL && m_poAttrQuery == NULL && nTotalSoundings != 0; + + return OGRHTFLayer::TestCapability(pszCap); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRHTFLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + if (!bHasExtent) + return OGRLayer::GetExtent(psExtent, bForce); + + psExtent->MinX = dfMinX; + psExtent->MinY = dfMinY; + psExtent->MaxX = dfMaxX; + psExtent->MaxY = dfMaxY; + return OGRERR_NONE; +} + + +/************************************************************************/ +/* SetExtent() */ +/************************************************************************/ + +void OGRHTFLayer::SetExtent(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY) +{ + bHasExtent = TRUE; + this->dfMinX = dfMinX; + this->dfMinY = dfMinY; + this->dfMaxX = dfMaxX; + this->dfMaxY = dfMaxY; +} + + +/************************************************************************/ +/* OGRHTFMetadataLayer() */ +/************************************************************************/ + +OGRHTFMetadataLayer::OGRHTFMetadataLayer(std::vector aosMD) +{ + this->aosMD = aosMD; + nNextFID = 0; + + poFeatureDefn = new OGRFeatureDefn( "metadata" ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + std::vector::const_iterator iter = aosMD.begin(); + std::vector::const_iterator eiter = aosMD.end(); + while(iter != eiter) + { + const CPLString& osStr = *iter; + char* pszStr = CPLStrdup(osStr.c_str()); + char* pszSep = strstr(pszStr, ": "); + if (pszSep) + { + *pszSep = 0; + int i = 0, j = 0; + for(;pszStr[i];i++) + { + if (pszStr[i] == ' ' || pszStr[i] == '-' || pszStr[i] == '&') + { + if (j > 0 && pszStr[j-1] == '_') + continue; + pszStr[j++] = '_'; + } + else if (pszStr[i] == '(' || pszStr[i] == ')') + ; + else + pszStr[j++] = pszStr[i]; + } + pszStr[j] = 0; + OGRFieldDefn oField( pszStr, OFTString); + poFeatureDefn->AddFieldDefn( &oField ); + } + CPLFree(pszStr); + ++iter; + } + + poFeature = new OGRFeature(poFeatureDefn); + iter = aosMD.begin(); + eiter = aosMD.end(); + int nField = 0; + while(iter != eiter) + { + const CPLString& osStr = *iter; + const char* pszStr = osStr.c_str(); + const char* pszSep = strstr(pszStr, ": "); + if (pszSep) + { + if (pszSep[2] != '*') + poFeature->SetField( nField, pszSep + 2 ); + + nField ++; + } + ++iter; + } +} + +/************************************************************************/ +/* ~OGRHTFMetadataLayer() */ +/************************************************************************/ + +OGRHTFMetadataLayer::~OGRHTFMetadataLayer() +{ + delete poFeature; + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRHTFMetadataLayer::GetNextFeature() +{ + if (nNextFID == 1) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + nNextFID = 1; + return poFeature->Clone(); + } + + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/GNUmakefile new file mode 100644 index 000000000..cb8e6a92a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/GNUmakefile @@ -0,0 +1,15 @@ + + +include ../../../GDALmake.opt + +OBJ = ogridbdatasource.o ogridblayer.o ogridbdriver.o \ + ogridbtablelayer.o ogridbselectlayer.o + +IDB_DEFS = -DIT_DLLIB -DIT_DO_NOT_SIMULATE_BOOL + +CPPFLAGS := -I.. -I../.. $(IDB_INC) $(IDB_DEFS) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/drv_idb.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/drv_idb.html new file mode 100644 index 000000000..ef5f21303 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/drv_idb.html @@ -0,0 +1,50 @@ + + +Informix DataBlade Driver + + + + +

    IDB

    + +

    This driver implements support for access to spatial tables in +IBM Informix extended with the DataBlade spatial module. + +

    When opening a database, it's name should be specified in the form + +

    "IDB:dbname={dbname} server={host} user={login} pass={pass} table={layertable}".
    + +

    The IDB: prefix is used to mark the name as a IDB connection string. + +

    If the geometry_columns table exists, then all listed tables and +named views will be treated as OGR layers. Otherwise all regular user tables +will be treated as layers. + +

    Regular (non-spatial) tables can be accessed, and will return features with +attributes, but not geometry. If the table has a "st_*" field, it will +be treated as a spatial table. The type of the field is inspected to +determine how to read it. + +

    Driver supports automatic FID detection. + +

    Environment variables

    +
      +
    • INFORMIXDIR: It should be set to Informix client SDK install dir +
    • INFORMIXSERVER: Default Informix server name +
    • DB_LOCALE: Locale of Informix database +
    • CLIENT_LOCALE: Client locale +
    • IDB_OGR_FID: Set name of primary key instead of 'ogc_fid'. +
    + +

    For more information about Informix variables read documentation of +Informix Client SDK + +

    Example

    +

    This example shows using ogrinfo to list Informix DataBlade layers on a different host. + +

    +ogrinfo -ro IDB:'server=demo_on user=informix dbname=frames'
    +
    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/makefile.vc new file mode 100644 index 000000000..5263f8d12 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/makefile.vc @@ -0,0 +1,16 @@ + +OBJ = ogridbdriver.obj ogridbdatasource.obj ogridblayer.obj \ + ogridbtablelayer.obj ogridbselectlayer.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. $(IDB_INC) + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + -del *.exe diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogr_idb.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogr_idb.h new file mode 100644 index 000000000..4fc8e8219 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogr_idb.h @@ -0,0 +1,220 @@ +/****************************************************************************** + * $Id: ogr_idb.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRIDBTableLayer class, access to an existing table + * (based on ODBC and PG drivers). + * Author: Oleg Semykin, oleg.semykin@gmail.com + * + ****************************************************************************** + * Copyright (c) 2006, Oleg Semykin + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_IDB_H_INCLUDED_ +#define _OGR_IDB_H_INCLUDED_ + +#include "ogrsf_frmts.h" +#include "cpl_error.h" +#include + +/************************************************************************/ +/* OGRIDBLayer */ +/************************************************************************/ + +class OGRIDBDataSource; + +class OGRIDBLayer : public OGRLayer +{ + protected: + OGRFeatureDefn *poFeatureDefn; + + ITCursor *poCurr; + + // Layer spatial reference system, and srid. + OGRSpatialReference *poSRS; + int nSRSId; + + int iNextShapeId; + + OGRIDBDataSource *poDS; + + int bGeomColumnWKB; + char *pszGeomColumn; + char *pszFIDColumn; + + CPLErr BuildFeatureDefn( const char *pszLayerName, + ITCursor *poCurr ); + + virtual ITCursor * GetQuery() { return poCurr; } + + public: + OGRIDBLayer(); + virtual ~OGRIDBLayer(); + + virtual void ResetReading(); + virtual OGRFeature *GetNextRawFeature(); + virtual OGRFeature *GetNextFeature(); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual const char *GetFIDColumn(); + virtual const char *GetGeometryColumn(); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRIDBTableLayer */ +/************************************************************************/ + +class OGRIDBTableLayer : public OGRIDBLayer +{ + int bUpdateAccess; + + char *pszQuery; + + int bHaveSpatialExtents; + + void ClearQuery(); + OGRErr ResetQuery(); + + virtual ITCursor * GetQuery(); + + public: + OGRIDBTableLayer( OGRIDBDataSource * ); + ~OGRIDBTableLayer(); + + CPLErr Initialize( const char *pszTableName, + const char *pszGeomCol, + int bUpdate + ); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual OGRErr SetAttributeFilter( const char * ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + +#if 0 + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); +#endif + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRIDBSelectLayer */ +/************************************************************************/ + +class OGRIDBSelectLayer : public OGRIDBLayer +{ + char *pszBaseQuery; + + void ClearQuery(); + OGRErr ResetQuery(); + + virtual ITCursor * GetQuery(); + + public: + OGRIDBSelectLayer( OGRIDBDataSource *, + ITCursor * ); + ~OGRIDBSelectLayer(); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRIDBDataSource */ +/************************************************************************/ + +class OGRIDBDataSource : public OGRDataSource +{ + OGRIDBLayer **papoLayers; + int nLayers; + + char *pszName; + + int bDSUpdate; + ITConnection *poConn; + + public: + OGRIDBDataSource(); + ~OGRIDBDataSource(); + + int Open( const char *, int bUpdate, int bTestOpen ); + int OpenTable( const char *pszTableName, + const char *pszGeomCol, + int bUpdate ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + // Internal use + ITConnection * GetConnection() { return poConn; } +}; + +/************************************************************************/ +/* OGRIDBDriver */ +/************************************************************************/ + +class OGRIDBDriver : public OGRSFDriver +{ + public: + ~OGRIDBDriver(); + const char * GetName(); + OGRDataSource * Open( const char *, int ); + + OGRDataSource * CreateDataSource( const char *pszName, + char ** = NULL ); + + int TestCapability( const char * ); +}; + +ITCallbackResult +IDBErrorHandler( const ITErrorManager &err, void * userdata, long errorlevel ); + +#endif /* ndef _OGR_idb_H_INCLUDED_ */ + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridbdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridbdatasource.cpp new file mode 100644 index 000000000..5b6e8d06e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridbdatasource.cpp @@ -0,0 +1,348 @@ +/****************************************************************************** + * $Id: ogridbdatasource.cpp 26506 2013-09-30 18:17:55Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRIDBDataSource class + * (based on ODBC and PG drivers). + * Author: Oleg Semykin, oleg.semykin@gmail.com + * + ****************************************************************************** + * Copyright (c) 2006, Oleg Semykin + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_idb.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogridbdatasource.cpp 26506 2013-09-30 18:17:55Z rouault $"); +/************************************************************************/ +/* OGRIDBDataSource() */ +/************************************************************************/ + +OGRIDBDataSource::OGRIDBDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + poConn = 0; +} + +/************************************************************************/ +/* ~OGRIDBDataSource() */ +/************************************************************************/ + +OGRIDBDataSource::~OGRIDBDataSource() + +{ + int i; + + CPLFree( pszName ); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + delete poConn; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRIDBDataSource::Open( const char * pszNewName, int bUpdate, + int bTestOpen ) + +{ + CPLAssert( poConn == NULL ); + +/* -------------------------------------------------------------------- */ +/* URL: DBI:dbname=.. server=.. user=.. pass=.. table=.. */ +/* Any of params is optional, table param can repeat more than once */ +/* -------------------------------------------------------------------- */ + char * pszDbName = NULL; + char * pszServer = NULL; + char * pszUser = NULL; + char * pszPass = NULL; + char **papszTables = NULL; + char **papszGeomCol = NULL; + + char ** papszTokens = CSLTokenizeString2(pszNewName + 4, " ", 0); + char * pszToken = 0; + int i = 0; + + while ( pszToken = papszTokens[i++] ) + { + if ( EQUALN( pszToken, "dbname=", 7 ) ) + pszDbName = CPLStrdup( pszToken + 7 ); + else if ( EQUALN( pszToken, "server=", 7 ) ) + pszServer = CPLStrdup( pszToken + 7 ); + else if ( EQUALN( pszToken, "user=", 5 ) ) + pszUser = CPLStrdup( pszToken + 5 ); + else if ( EQUALN( pszToken, "pass=", 5 ) ) + pszPass = CPLStrdup( pszToken + 5 ); + else if ( EQUALN( pszToken, "table=", 6 ) ) + { + papszTables = CSLAddString( papszTables, pszToken + 6 ); + papszGeomCol = CSLAddString( papszGeomCol, "" ); + } + } + +/* -------------------------------------------------------------------- */ +/* Initialize based on the DSN. */ +/* -------------------------------------------------------------------- */ + ITDBInfo oDbInfo(pszDbName,pszUser,pszServer,pszPass); + CPLDebug( "OGR_IDB", + "Connect to: db:'%s' server:'%s', user:'%s', pass:'%s'", + pszDbName ? pszDbName : "", + pszServer ? pszServer : "", + pszUser ? pszUser : "", + pszPass ? pszPass : "" ); + + CPLFree( pszDbName ); + CPLFree( pszServer ); + CPLFree( pszUser ); + CPLFree( pszPass ); + + poConn = new ITConnection( oDbInfo ); + + poConn->AddCallback( IDBErrorHandler, 0 ); + + if( !poConn->Open() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to initialize IDB connection to %s", + pszNewName+4); + CSLDestroy( papszTables ); + return FALSE; + } + + pszName = CPLStrdup( pszNewName ); + + bDSUpdate = bUpdate; + +/* -------------------------------------------------------------------- */ +/* If no explicit list of tables was given, check for a list in */ +/* a geometry_columns table. */ +/* -------------------------------------------------------------------- */ + if( papszTables == NULL ) + { + ITCursor oCurr( *poConn ); + + if( oCurr.Prepare(" SELECT f_table_name, f_geometry_column," + " geometry_type FROM geometry_columns" ) && + oCurr.Open(ITCursor::ReadOnly) ) + { + ITRow * row = 0; + while( (row = oCurr.NextRow()) ) + { + papszTables = + CSLAddString( papszTables, row->Column(0)->Printable() ); + papszGeomCol = + CSLAddString( papszGeomCol, row->Column(1)->Printable() ); + row->Release(); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise our final resort is to return all tables as */ +/* non-spatial tables. */ +/* -------------------------------------------------------------------- */ + if( papszTables == NULL ) + { + ITCursor oTableList( *poConn ); + + if ( oTableList.Prepare("select tabname from systables where tabtype='T' and tabid > 99") && + oTableList.Open(ITCursor::ReadOnly) ) + { + ITRow * row = 0; + while( (row = oTableList.NextRow()) ) + { + papszTables = + CSLAddString( papszTables, row->Column(0)->Printable() ); + papszGeomCol = CSLAddString(papszGeomCol,""); + row->Release(); + } + } + else + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to open cursor for '%s'", + oTableList.QueryText().Data() ); + } + +/* -------------------------------------------------------------------- */ +/* If we have an explicit list of requested tables, use them */ +/* (non-spatial). */ +/* -------------------------------------------------------------------- */ + for( int iTable = 0; + papszTables != NULL && papszTables[iTable] != NULL; + iTable++ ) + { + char * pszGeomCol = NULL; + + if( strlen(papszGeomCol[iTable]) > 0 ) + pszGeomCol = papszGeomCol[iTable]; + + OpenTable( papszTables[iTable], pszGeomCol, bUpdate ); + } + + CSLDestroy( papszTables ); + CSLDestroy( papszGeomCol ); + + return TRUE; +} + +/************************************************************************/ +/* OpenTable() */ +/************************************************************************/ + +int OGRIDBDataSource::OpenTable( const char *pszNewName, + const char *pszGeomCol, + int bUpdate ) + +{ +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRIDBTableLayer *poLayer; + + poLayer = new OGRIDBTableLayer( this ); + + if( poLayer->Initialize( pszNewName, pszGeomCol, bUpdate ) ) + { + delete poLayer; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRIDBLayer **) + CPLRealloc( papoLayers, sizeof(OGRIDBLayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + + return TRUE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRIDBDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRIDBDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRIDBDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Execute statement. */ +/* -------------------------------------------------------------------- */ + ITCursor *poCurr = new ITCursor( *poConn ); + + poCurr->Prepare( pszSQLCommand ); + if( !poCurr->Open(ITCursor::ReadOnly) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error execute SQL: %s", pszSQLCommand ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Are there result columns for this statement? */ +/* -------------------------------------------------------------------- */ + if( poCurr->RowType()->ColumnCount() == 0 ) + { + delete poCurr; + CPLErrorReset(); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a results layer. It will take ownership of the */ +/* statement. */ +/* -------------------------------------------------------------------- */ + OGRIDBSelectLayer *poLayer = NULL; + + poLayer = new OGRIDBSelectLayer( this, poCurr ); + + if( poSpatialFilter != NULL ) + poLayer->SetSpatialFilter( poSpatialFilter ); + + return poLayer; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRIDBDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} + +ITCallbackResult +IDBErrorHandler( const ITErrorManager &err, void * , long ) +{ + if ( err.Error() ) + CPLError( CE_Failure, CPLE_AppDefined, + "IDB Error: %s", err.ErrorText().Data() ); + + /*if ( err.Warn() ) + CPLError( CE_Warning, CPLE_AppDefined, + "IDB Warning: %s", err.WarningText().Data() ); + */ + return IT_NOTHANDLED; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridbdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridbdriver.cpp new file mode 100644 index 000000000..f6b1c8b6d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridbdriver.cpp @@ -0,0 +1,129 @@ +/****************************************************************************** + * $Id: ogridbdriver.cpp 12396 2007-10-13 10:02:17Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRIDBDriver class. + * (based on ODBC and PG drivers). + * Author: Oleg Semykin, oleg.semykin@gmail.com + * + ****************************************************************************** + * Copyright (c) 2006, Oleg Semykin + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_idb.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogridbdriver.cpp 12396 2007-10-13 10:02:17Z rouault $"); + +/************************************************************************/ +/* ~OGRIDBDriver() */ +/************************************************************************/ + +OGRIDBDriver::~OGRIDBDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRIDBDriver::GetName() + +{ + return "IDB"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRIDBDriver::Open( const char * pszFilename, + int bUpdate ) + +{ + OGRIDBDataSource *poDS; + + if( !EQUALN(pszFilename,"IDB:",4) ) + return NULL; + + poDS = new OGRIDBDataSource(); + + if( !poDS->Open( pszFilename, bUpdate, TRUE ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRIDBDriver::CreateDataSource( const char * pszName, + char ** /* papszOptions */ ) + +{ + OGRIDBDataSource *poDS; + + if( !EQUALN(pszName,"IDB:",4) ) + return NULL; + + poDS = new OGRIDBDataSource(); + + if( !poDS->Open( pszName, TRUE, TRUE ) ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "IDB driver doesn't currently support database creation."); + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRIDBDriver::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODrCCreateDataSource) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRIDB() */ +/************************************************************************/ + +void RegisterOGRIDB() + +{ + if (! GDAL_CHECK_VERSION("IDB driver")) + return; + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( new OGRIDBDriver ); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridblayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridblayer.cpp new file mode 100644 index 000000000..1e4f6b2bc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridblayer.cpp @@ -0,0 +1,463 @@ +/****************************************************************************** + * $Id: ogridblayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRIDBLayer class, code shared between + * the direct table access, and the generic SQL results + * (based on ODBC and PG drivers). + * Author: Oleg Semykin, oleg.semykin@gmail.com + * + ****************************************************************************** + * Copyright (c) 2006, Oleg Semykin + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_idb.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogridblayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRIDBLayer() */ +/************************************************************************/ + +OGRIDBLayer::OGRIDBLayer() + +{ + poDS = NULL; + + bGeomColumnWKB = FALSE; + pszFIDColumn = NULL; + pszGeomColumn = NULL; + + poCurr = NULL; + + iNextShapeId = 0; + + poSRS = NULL; + nSRSId = -2; // we haven't even queried the database for it yet. +} + +/************************************************************************/ +/* ~OGRIDBLayer() */ +/************************************************************************/ + +OGRIDBLayer::~OGRIDBLayer() + +{ + if( poCurr != NULL ) + { + poCurr->Close(); + delete poCurr; + poCurr = NULL; + } + + if( pszGeomColumn ) + CPLFree( pszGeomColumn ); + + if(pszFIDColumn) + CPLFree( pszFIDColumn ); + + if( poFeatureDefn ) + { + poFeatureDefn->Release(); + poFeatureDefn = NULL; + } + + if( poSRS ) + poSRS->Release(); +} + +/************************************************************************/ +/* BuildFeatureDefn() */ +/* */ +/* Build feature definition from a set of column definitions */ +/* set on a statement. Sift out geometry and FID fields. */ +/************************************************************************/ + +CPLErr OGRIDBLayer::BuildFeatureDefn( const char *pszLayerName, + ITCursor *poCurr ) + +{ + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + const ITTypeInfo * poInfo = poCurr->RowType(); + int nRawColumns = poInfo->ColumnCount(); + + poFeatureDefn->Reference(); + + for( int iCol = 0; iCol < nRawColumns; iCol++ ) + { + const char * pszColName = poInfo->ColumnName(iCol); + const ITTypeInfo * poTI = poInfo->ColumnType(iCol); + const char * pszTypName = poTI->Name(); + + OGRFieldDefn oField( pszColName, OFTString ); + + oField.SetWidth( MAX(0,poTI->Bound()) ); + + if ( pszGeomColumn != NULL && EQUAL(pszColName,pszGeomColumn) ) + continue; + + if ( EQUALN("st_", pszTypName, 3) && pszGeomColumn == NULL ) + { + // We found spatial column! + pszGeomColumn = CPLStrdup(pszColName); + + if ( EQUAL("st_point", pszTypName) ) + poFeatureDefn->SetGeomType( wkbPoint ); + else if ( EQUAL("st_linestring", pszTypName) ) + poFeatureDefn->SetGeomType( wkbLineString ); + else if ( EQUAL("st_polygon", pszTypName) ) + poFeatureDefn->SetGeomType( wkbPolygon ); + else if ( EQUAL("st_multipoint", pszTypName) ) + poFeatureDefn->SetGeomType( wkbMultiPoint ); + else if ( EQUAL("st_multilinestring", pszTypName) ) + poFeatureDefn->SetGeomType( wkbMultiLineString ); + else if ( EQUAL("st_multipolygon", pszTypName) ) + poFeatureDefn->SetGeomType( wkbMultiPolygon ); + + continue; + } + + // Check other field types + if ( EQUAL( pszTypName, "blob" ) || + EQUAL( pszTypName, "byte" ) || + EQUAL( pszTypName, "opaque" ) || + EQUAL( pszTypName, "text" ) || + EQUALN( pszTypName, "list", 4 ) || + EQUALN( pszTypName, "collection", 10 ) || + EQUALN( pszTypName, "row", 3 ) || + EQUALN( pszTypName, "set", 3 ) ) + { + CPLDebug( "OGR_IDB", "'%s' column type not supported yet. Column '%s'", + pszTypName, pszColName ); + continue; + } + + if ( EQUALN( pszTypName, "st_", 3 ) ) + { + oField.SetType( OFTBinary ); + } + else if ( EQUAL( pszTypName, "date" ) ) + { + oField.SetType( OFTDate ); + } + else if ( EQUAL( pszTypName, "datetime" ) ) + { + oField.SetType( OFTDateTime ); + } + else if ( EQUAL( pszTypName, "decimal" ) || + EQUAL( pszTypName, "money" ) || + EQUAL( pszTypName, "float" ) || + EQUAL( pszTypName, "smallfloat" ) ) + { + oField.SetType( OFTReal ); + oField.SetPrecision( MAX( 0, poTI->Scale() ) ); // -1 for numeric + } + else if ( EQUAL( pszTypName, "integer" ) || + EQUAL( pszTypName, "serial" ) ) + { + oField.SetType( OFTInteger ); + // 10 as hardcoded max int32 value length + 1 sig bit + oField.SetWidth( 11 ); + } + else if ( EQUAL( pszTypName, "smallint" ) ) + { + oField.SetType( OFTInteger ); + // 5 as hardcoded max int16 value length + 1 sig bit + oField.SetWidth( 6 ); + } + else + { + // leave as string: + // *char, character, character varing, *varchar + // interval. int8, serial8 + } + + poFeatureDefn->AddFieldDefn( &oField ); + } + +/* -------------------------------------------------------------------- */ +/* If we don't already have an FID, check if there is a special */ +/* FID named column available. */ +/* -------------------------------------------------------------------- */ + if( pszFIDColumn == NULL ) + { + const char *pszOGR_FID = CPLGetConfigOption("IDB_OGR_FID","OGR_FID"); + if( poFeatureDefn->GetFieldIndex( pszOGR_FID ) != -1 ) + pszFIDColumn = CPLStrdup(pszOGR_FID); + } + + if( pszFIDColumn != NULL ) + CPLDebug( "OGR_IDB", "Using column %s as FID for table %s.", + pszFIDColumn, poFeatureDefn->GetName() ); + else + CPLDebug( "OGR_IDB", "Table %s has no identified FID column.", + poFeatureDefn->GetName() ); + + return CE_None; +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRIDBLayer::ResetReading() + +{ + iNextShapeId = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRIDBLayer::GetNextFeature() + +{ + for( ; TRUE; ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRIDBLayer::GetNextRawFeature() + +{ + if( GetQuery() == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we are marked to restart then do so, and fetch a record. */ +/* -------------------------------------------------------------------- */ + ITRow * row = poCurr->NextRow(); + if ( ! row ) + { + delete poCurr; + poCurr = NULL; + return NULL; + } + + iNextShapeId++; + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Create a feature from the current result. */ +/* -------------------------------------------------------------------- */ + int iField; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + const ITTypeInfo * poRowType = poCurr->RowType(); + int nFieldCount = poRowType->ColumnCount(); + + for ( iField = 0; iField < nFieldCount; iField++ ) + { +/* -------------------------------------------------------------------- */ +/* Handle FID column */ +/* -------------------------------------------------------------------- */ + if ( pszFIDColumn != NULL && + EQUAL( poRowType->ColumnName( iField ), pszFIDColumn ) ) + poFeature->SetFID( atoi( row->Column( iField )->Printable() ) ); + +/* -------------------------------------------------------------------- */ +/* Handle geometry */ +/* -------------------------------------------------------------------- */ + if( pszGeomColumn != NULL && + EQUAL( poRowType->ColumnName( iField ), pszGeomColumn ) ) + { + OGRGeometry *poGeom = NULL; + OGRErr eErr = OGRERR_NONE; + + ITValue * v = row->Column( iField ); + + if( ! v->IsNull() && ! bGeomColumnWKB ) + { + const char *pszGeomText = v->Printable(); + if ( pszGeomText != NULL ) + eErr = + OGRGeometryFactory::createFromWkt((char **) &pszGeomText, + poSRS, &poGeom); + } + else if( ! v->IsNull() && bGeomColumnWKB ) + { + ITDatum *rv = 0; + if ( v->QueryInterface( ITDatumIID, (void **) &rv ) == + IT_QUERYINTERFACE_SUCCESS ) + { + int nLength = rv->DataLength(); + unsigned char * wkb = (unsigned char *)rv->Data(); + + eErr = OGRGeometryFactory::createFromWkb( wkb, poSRS, &poGeom, nLength); + rv->Release(); + } + } + + v->Release(); + + + if ( eErr != OGRERR_NONE ) + { + const char *pszMessage; + + switch ( eErr ) + { + case OGRERR_NOT_ENOUGH_DATA: + pszMessage = "Not enough data to deserialize"; + break; + case OGRERR_UNSUPPORTED_GEOMETRY_TYPE: + pszMessage = "Unsupported geometry type"; + break; + case OGRERR_CORRUPT_DATA: + pszMessage = "Corrupt data"; + default: + pszMessage = "Unrecognized error"; + } + CPLError(CE_Failure, CPLE_AppDefined, + "GetNextRawFeature(): %s", pszMessage); + } + + if( poGeom != NULL ) + { + poFeature->SetGeometryDirectly( poGeom ); + } + + continue; + } + +/* -------------------------------------------------------------------- */ +/* Transfer regular data fields. */ +/* -------------------------------------------------------------------- */ + int iOGRField = + poFeatureDefn->GetFieldIndex( poRowType->ColumnName( iField ) ); + + if( iOGRField < 0 ) + continue; + + const char * pszColData = row->Column( iField )->Printable(); + + if( ! pszColData ) + continue; + + if( poFeatureDefn->GetFieldDefn(iOGRField)->GetType() == OFTBinary ) + poFeature->SetField( iOGRField, + poRowType->ColumnType( iField )->Size(), + (GByte *) pszColData ); + else + poFeature->SetField( iOGRField, pszColData ); + } + + row->Release(); + return poFeature; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRIDBLayer::GetFeature( GIntBig nFeatureId ) + +{ + /* This should be implemented directly! */ + + return OGRLayer::GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRIDBLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else if( EQUAL(pszCap,OLCTransactions) ) + return FALSE; + + else + return FALSE; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRIDBLayer::GetSpatialRef() + +{ + return poSRS; +} + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char *OGRIDBLayer::GetFIDColumn() + +{ + if( pszFIDColumn != NULL ) + return pszFIDColumn; + else + return ""; +} + +/************************************************************************/ +/* GetGeometryColumn() */ +/************************************************************************/ + +const char *OGRIDBLayer::GetGeometryColumn() + +{ + if( pszGeomColumn != NULL ) + return pszGeomColumn; + else + return ""; +} + +/* TODO Query to get layer extent */ +/* +EXECUTE FUNCTION SE_BoundingBox ('table_name', 'geom_column' ) +*/ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridbselectlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridbselectlayer.cpp new file mode 100644 index 000000000..fdeb77746 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridbselectlayer.cpp @@ -0,0 +1,178 @@ +/****************************************************************************** + * $Id: ogridbselectlayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRIDBSelectLayer class, layer access to the results + * of a SELECT statement executed via Open() + * (based on ODBC and PG drivers). + * Author: Oleg Semykin, oleg.semykin@gmail.com + * + ****************************************************************************** + * Copyright (c) 2006, Oleg Semykin + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_idb.h" + +CPL_CVSID("$Id: ogridbselectlayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRIDBSelectLayer() */ +/************************************************************************/ + +OGRIDBSelectLayer::OGRIDBSelectLayer( OGRIDBDataSource *poDSIn, + ITCursor * poCurrIn ) + +{ + poDS = poDSIn; + + iNextShapeId = 0; + nSRSId = -1; + poFeatureDefn = NULL; + + poCurr = poCurrIn; + pszBaseQuery = CPLStrdup( poCurrIn->Command() ); + + BuildFeatureDefn( "SELECT", poCurr ); +} + +/************************************************************************/ +/* ~OGRIDBSelectLayer() */ +/************************************************************************/ + +OGRIDBSelectLayer::~OGRIDBSelectLayer() + +{ + ClearQuery(); +} + +/************************************************************************/ +/* ClearQuery() */ +/************************************************************************/ + +void OGRIDBSelectLayer::ClearQuery() + +{ + if( poCurr != NULL ) + { + delete poCurr; + poCurr = NULL; + } +} + +/************************************************************************/ +/* GetQuery() */ +/************************************************************************/ + +ITCursor *OGRIDBSelectLayer::GetQuery() + +{ + if( poCurr == NULL ) + ResetQuery(); + + return poCurr; +} + +/************************************************************************/ +/* ResetQuery() */ +/************************************************************************/ + +OGRErr OGRIDBSelectLayer::ResetQuery() + +{ + ClearQuery(); + + iNextShapeId = 0; + + CPLDebug( "OGR_IDB", "Recreating statement." ); + poCurr = new ITCursor( *poDS->GetConnection() ); + + if( poCurr->Prepare( pszBaseQuery ) && + poCurr->Open( ITCursor::ReadOnly ) ) + return OGRERR_NONE; + else + { + delete poCurr; + poCurr = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRIDBSelectLayer::ResetReading() + +{ + if( iNextShapeId != 0 ) + ClearQuery(); + + OGRIDBLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRIDBSelectLayer::GetFeature( GIntBig nFeatureId ) + +{ + return OGRIDBLayer::GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRIDBSelectLayer::TestCapability( const char * pszCap ) + +{ + return OGRIDBLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetExtent() */ +/* */ +/* Since SELECT layers currently cannot ever have geometry, we */ +/* can optimize the GetExtent() method! */ +/************************************************************************/ + +OGRErr OGRIDBSelectLayer::GetExtent(OGREnvelope *, int ) + +{ + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRIDBSelectLayer::GetFeatureCount( int bForce ) + +{ + return OGRIDBLayer::GetFeatureCount( bForce ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridbtablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridbtablelayer.cpp new file mode 100644 index 000000000..b67faae14 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idb/ogridbtablelayer.cpp @@ -0,0 +1,1048 @@ +/****************************************************************************** + * $Id: ogridbtablelayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRIDBTableLayer class, access to an existing table + * (based on ODBC and PG drivers). + * Author: Oleg Semykin, oleg.semykin@gmail.com + * + ****************************************************************************** + * Copyright (c) 2006, Oleg Semykin + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_idb.h" + +CPL_CVSID("$Id: ogridbtablelayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); +/************************************************************************/ +/* OGRIDBTableLayer() */ +/************************************************************************/ + +OGRIDBTableLayer::OGRIDBTableLayer( OGRIDBDataSource *poDSIn ) + +{ + poDS = poDSIn; + + pszQuery = NULL; + + bUpdateAccess = TRUE; + bHaveSpatialExtents = FALSE; + + iNextShapeId = 0; + + poFeatureDefn = NULL; +} + +/************************************************************************/ +/* ~OGRIDBTableLayer() */ +/************************************************************************/ + +OGRIDBTableLayer::~OGRIDBTableLayer() + +{ + CPLFree( pszQuery ); + ClearQuery(); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +CPLErr OGRIDBTableLayer::Initialize( const char *pszTableName, + const char *pszGeomCol, + int bUpdate ) + +{ + bUpdateAccess = bUpdate; + + ITConnection *poConn = poDS->GetConnection(); + + if ( pszFIDColumn ) + { + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we have a simple primary key? */ +/* -------------------------------------------------------------------- */ + if ( pszFIDColumn == NULL ) + { + ITCursor oGetKey( *poConn ); + + CPLString osSql = + " select sc.colname" + " from syscolumns sc, sysindexes si, systables st" + " where st.tabid = si.tabid" + " and st.tabid = sc.tabid" + " and si.idxtype = 'U'" + " and sc.colno = si.part1" + " and si.part2 = 0" // only one-column keys + " and st.tabname='"; + osSql += pszTableName; + osSql += "'"; + + if( oGetKey.Prepare( osSql.c_str() ) && + oGetKey.Open(ITCursor::ReadOnly) ) + { + ITValue * poVal = oGetKey.Fetch(); + if ( poVal && poVal->IsNull() == false ) + { + pszFIDColumn = CPLStrdup(poVal->Printable()); + poVal->Release(); + } + + if( oGetKey.Fetch() ) // more than one field in key! + { + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + + CPLDebug("OGR_IDB", "Table %s has multiple primary key fields," + " ignoring them all.", pszTableName ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Have we been provided a geometry column? */ +/* -------------------------------------------------------------------- */ + CPLFree( pszGeomColumn ); + if( pszGeomCol == NULL ) + pszGeomColumn = NULL; + else + pszGeomColumn = CPLStrdup( pszGeomCol ); + +/* -------------------------------------------------------------------- */ +/* Get the column definitions for this table. */ +/* -------------------------------------------------------------------- */ + ITCursor oGetCol( *poConn ); + CPLErr eErr; + + CPLString sql; + sql.Printf( "select * from %s where 1=0", pszTableName ); + if( ! oGetCol.Prepare( sql.c_str() ) || + ! oGetCol.Open(ITCursor::ReadOnly) ) + return CE_Failure; + + eErr = BuildFeatureDefn( pszTableName, &oGetCol ); + if( eErr != CE_None ) + return eErr; + + if( poFeatureDefn->GetFieldCount() == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No column definitions found for table '%s', layer not usable.", + pszTableName ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Do we have XMIN, YMIN, XMAX, YMAX extent fields? */ +/* -------------------------------------------------------------------- */ + if( poFeatureDefn->GetFieldIndex( "XMIN" ) != -1 + && poFeatureDefn->GetFieldIndex( "XMAX" ) != -1 + && poFeatureDefn->GetFieldIndex( "YMIN" ) != -1 + && poFeatureDefn->GetFieldIndex( "YMAX" ) != -1 ) + { + bHaveSpatialExtents = TRUE; + CPLDebug( "OGR_IDB", "Table %s has geometry extent fields.", + pszTableName ); + } + +/* -------------------------------------------------------------------- */ +/* If we got a geometry column, does it exist? Is it binary? */ +/* -------------------------------------------------------------------- */ + if( pszGeomColumn != NULL ) + { + int iColumn = oGetCol.RowType()->ColumnId( pszGeomColumn ); + if( iColumn < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Column %s requested for geometry, but it does not exist.", + pszGeomColumn ); + CPLFree( pszGeomColumn ); + pszGeomColumn = NULL; + } + bGeomColumnWKB = TRUE; + /*else + { + if( ITCursor::GetTypeMapping( + oGetCol.GetColType( iColumn )) == SQL_C_BINARY ) + bGeomColumnWKB = TRUE; + }*/ + } + + + return CE_None; +} + +/************************************************************************/ +/* ClearQuery() */ +/************************************************************************/ + +void OGRIDBTableLayer::ClearQuery() + +{ + if( poCurr != NULL ) + { + poCurr->Close(); + delete poCurr; + poCurr = NULL; + } +} + +/************************************************************************/ +/* GetQuery() */ +/************************************************************************/ + +ITCursor *OGRIDBTableLayer::GetQuery() + +{ + if( poCurr == NULL ) + ResetQuery(); + + return poCurr; +} + +/************************************************************************/ +/* ResetQuery() */ +/************************************************************************/ + +OGRErr OGRIDBTableLayer::ResetQuery() + +{ + ClearQuery(); + + iNextShapeId = 0; + + poCurr = new ITCursor( *poDS->GetConnection() ); + + // Create list of fields + CPLString osFields; + + if ( pszGeomColumn ) + { + if ( ! osFields.empty() ) + osFields += ","; + + osFields += "st_asbinary("; + osFields += pszGeomColumn; + osFields += ") as "; + osFields += pszGeomColumn; + } + + for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if ( ! osFields.empty() ) + osFields += ","; + + osFields += poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + } + + CPLString sql; + + sql += "SELECT "; + sql += osFields; + sql += " FROM "; + sql += poFeatureDefn->GetName(); + + /* Append attribute query if we have it */ + if( pszQuery != NULL ) + { + sql += " WHERE "; + sql += pszQuery; + } + + /* If we have a spatial filter, and per record extents, query on it */ + if( m_poFilterGeom != NULL && bHaveSpatialExtents ) + { + if( pszQuery == NULL ) + sql += " WHERE"; + else + sql += " AND"; + + sql.Printf( "%s XMAX > %.8f AND XMIN < %.8f" + " AND YMAX > %.8f AND YMIN < %.8f", + sql.c_str(), + m_sFilterEnvelope.MinX, m_sFilterEnvelope.MaxX, + m_sFilterEnvelope.MinY, m_sFilterEnvelope.MaxY ); + } + + CPLDebug( "OGR_IDB", "Exec(%s)", sql.c_str() ); + if( poCurr->Prepare( sql.c_str() ) && + poCurr->Open(ITCursor::ReadOnly) ) + { + return OGRERR_NONE; + } + else + { + delete poCurr; + poCurr = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRIDBTableLayer::ResetReading() + +{ + ClearQuery(); + OGRIDBLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRIDBTableLayer::GetFeature( GIntBig nFeatureId ) + +{ + if( pszFIDColumn == NULL ) + return OGRIDBLayer::GetFeature( nFeatureId ); + + ClearQuery(); + + iNextShapeId = nFeatureId; + + poCurr = new ITCursor( *poDS->GetConnection() ); + + // Create list of fields + CPLString osFields; + + if ( poFeatureDefn->GetFieldIndex( pszFIDColumn ) == -1 ) + osFields += pszFIDColumn; + + if ( pszGeomColumn ) + { + if ( ! osFields.empty() ) + osFields += ","; + + osFields += "st_asbinary("; + osFields += pszGeomColumn; + osFields += ") as "; + osFields += pszGeomColumn; + } + + for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if ( ! osFields.empty() ) + osFields += ","; + + osFields += poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + } + + CPLString sql; + + sql.Printf( "SELECT %s FROM %s WHERE %s = %d", + osFields.c_str(), poFeatureDefn->GetName(), + pszFIDColumn, nFeatureId ); + + CPLDebug( "OGR_IDB", "ExecuteSQL(%s)", sql.c_str() ); + if( !poCurr->Prepare( sql.c_str() ) || + !poCurr->Open(ITCursor::ReadOnly) ) + { + delete poCurr; + poCurr = NULL; + return NULL; + } + + return GetNextRawFeature(); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRIDBTableLayer::SetAttributeFilter( const char *pszQuery ) + +{ + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + + if( (pszQuery == NULL && this->pszQuery == NULL) + || (pszQuery != NULL && this->pszQuery != NULL + && EQUAL(pszQuery,this->pszQuery)) ) + return OGRERR_NONE; + + CPLFree( this->pszQuery ); + this->pszQuery = CPLStrdup( pszQuery ); + + ClearQuery(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRIDBTableLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCSequentialWrite) || + EQUAL(pszCap,OLCRandomWrite) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else + return OGRIDBLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRIDBTableLayer::GetFeatureCount( int bForce ) + +{ + return OGRIDBLayer::GetFeatureCount( bForce ); +} + +/************************************************************************/ +/* GetSpatialRef() */ +/* */ +/* We override this to try and fetch the table SRID from the */ +/* geometry_columns table if the srsid is -2 (meaning we */ +/* haven't yet even looked for it). */ +/************************************************************************/ + +OGRSpatialReference *OGRIDBTableLayer::GetSpatialRef() + +{ + if( nSRSId == -2 ) + { + nSRSId = -1; + + if ( ! pszGeomColumn ) + return NULL; + + CPLString osCmd; + osCmd.Printf( " SELECT FIRST 1 srid, trim(srtext)" + " FROM spatial_ref_sys, %s" + " WHERE srid = ST_Srid(%s) ", + poFeatureDefn->GetName(), pszGeomColumn ); + + ITCursor oSridCur( *poDS->GetConnection() ); + + if( oSridCur.Prepare( osCmd.c_str() )&& + oSridCur.Open( ITCursor::ReadOnly ) ) + { + ITRow * row = static_cast( oSridCur.NextRow() ); + if ( row && ! row->IsNull() ) + { + nSRSId = atoi(row->Column(0)->Printable()); + const char * wkt = row->Column(1)->Printable(); + + if ( poSRS ) + { + // Hmm ... it should be null + delete poSRS; + } + poSRS = new OGRSpatialReference(); + if ( poSRS->importFromWkt( (char **)&wkt ) != OGRERR_NONE ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Error parse srs wkt: %s", wkt ); + delete poSRS; + poSRS = NULL; + } + } + } + } + + return OGRIDBLayer::GetSpatialRef(); +} + +#if 0 +OGRErr OGRIDBTableLayer::ISetFeature( OGRFeature *poFeature ) +{ + OGRErr eErr(OGRERR_FAILURE); + + if ( ! bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error update feature. Layer is read only." ); + return eErr; + } + + if( NULL == poFeature ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "NULL pointer to OGRFeature passed to SetFeature()." ); + return eErr; + } + + if( poFeature->GetFID() == OGRNullFID ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "FID required on features given to SetFeature()." ); + return eErr; + } + + ITStatement oQuery( *poDS->GetConnection() ); + + int bUpdateGeom = TRUE; + OGRwkbGeometryType nGeomType = poFeature->GetGeometryRef()->getGeometryType(); + CPLString osGeomFunc; + int nSrid = 0; // FIXME Obtain geometry SRID + + switch (nGeomType) + { + case wkbPoint: + osGeomFunc = "ST_PointFromText"; + break; + case wkbLineString: + osGeomFunc = "ST_LineFromText"; + break; + case wkbPolygon: + osGeomFunc = "ST_PolyFromText"; + break; + case wkbMultiPoint: + osGeomFunc = "ST_MPointFromText"; + break; + case wkbMultiLineString: + osGeomFunc = "ST_MLineFromText"; + break; + case wkbMultiPolygon: + osGeomFunc = "ST_MPolyFromText"; + break; + default: + bUpdateGeom = FALSE; + CPLDebug("OGR_IDB", "SetFeature(): Unknown geometry type. Geometry will not be updated."); + } + + + // Create query + CPLString osSql; + CPLString osFields; + + if ( pszGeomColumn && bUpdateGeom ) + { + osFields.Printf( "%s = %s( ?, %d )", pszGeomColumn, osGeomFunc.c_str(), nSrid ); + } + + for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + const char * pszFieldName = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + + // skip fid column from update + if ( EQUAL( pszFIDColumn, pszFieldName ) ) + continue; + + if ( ! osFields.empty() ) + { + osFields += ","; + } + + osFields += pszFieldName; + osFields += "=?"; + } + + osSql.Printf( "UPDATE %s SET %s WHERE %s = %d", + poFeatureDefn->GetName(), + osFields.c_str(), + pszFIDColumn, + poFeature->GetFID() ); + + if ( ! oQuery.Prepare( osSql.c_str() ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error prepare SQL.\n%s",osSql.c_str() ); + return eErr; + } + + int iParam = 0; + + if ( pszGeomColumn && bUpdateGeom ) + { + ITValue * par = oQuery.Param( iParam ); // it should be a geom value + if ( ! par ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error prepare geom param"); + return eErr; + } + + OGRGeometry * poGeom = poFeature->GetGeometryRef(); + char * wkt; + poGeom->exportToWkt( &wkt ); + + if( ! par->FromPrintable( wkt ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error prepare geom param"); + par->Release(); + return eErr; + } + + CPLFree( wkt ); + par->Release(); + + iParam++; + } + + for ( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + ITValue * par = oQuery.Param( iParam ); + if ( ! par ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error prepare param %d", iParam); + return eErr; + } + + if ( ! poFeature->IsFieldSet( i ) ) + { + if ( ! par->SetNull() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error set param %d to NULL", iParam); + par->Release(); + return eErr; + } + par->Release(); + continue; + } + + ITConversions * cv = 0; + bool res = FALSE; + + if ( par->QueryInterface( ITConversionsIID, (void **) &cv) != + IT_QUERYINTERFACE_SUCCESS ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error prepare param %d", iParam); + par->Release(); + return eErr; + } + + switch ( poFeatureDefn->GetFieldDefn( i )->GetType() ) + { + case OFTInteger: + res = cv->ConvertFrom( poFeature->GetFieldAsInteger( i ) ); + break; + + case OFTReal: + res = cv->ConvertFrom( poFeature->GetFieldAsDouble( i ) ); + break; + + case OFTIntegerList: + case OFTRealList: + case OFTStringList: + // FIXME Prepare array of values field + //cv->ConvertFrom( poFeature->GetFieldAsStringList( i ) ); + //break; + + case OFTBinary: + // FIXME Prepare binary field + + case OFTString: + case OFTDate: + case OFTTime: + case OFTDateTime: + res = cv->ConvertFrom( poFeature->GetFieldAsString( i ) ); + break; + default: + CPLError( CE_Failure, CPLE_AppDefined, + "Error prepare param %d. Unknown data type.", iParam); + cv->Release(); + par->Release(); + return eErr; + } + if ( res != TRUE ) + CPLError( CE_Failure, CPLE_AppDefined, + "Error prepare param."); + cv->Release(); + par->Release(); + } + + CPLDebug( "OGR_IDB", "ExecuteSQL(%s)", oQuery.QueryText().Data() ); + if( !oQuery.Exec() ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Error update Feature."); + return eErr; + } + + return OGRERR_NONE; +} + +#endif + +OGRErr OGRIDBTableLayer::ISetFeature( OGRFeature *poFeature ) +{ + OGRErr eErr(OGRERR_FAILURE); + + if ( ! bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error update feature. Layer is read only." ); + return eErr; + } + + if( NULL == poFeature ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "NULL pointer to OGRFeature passed to SetFeature()." ); + return eErr; + } + + if( poFeature->GetFID() == OGRNullFID ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "FID required on features given to SetFeature()." ); + return eErr; + } + + ITStatement oQuery( *poDS->GetConnection() ); + + int bUpdateGeom = TRUE; + CPLString osGeomFunc; + + if ( poFeature->GetGeometryRef() ) + { + OGRwkbGeometryType nGeomType = poFeature->GetGeometryRef()->getGeometryType(); + + switch (nGeomType) + { + case wkbPoint: + osGeomFunc = "ST_PointFromText"; + break; + case wkbLineString: + osGeomFunc = "ST_LineFromText"; + break; + case wkbPolygon: + osGeomFunc = "ST_PolyFromText"; + break; + case wkbMultiPoint: + osGeomFunc = "ST_MPointFromText"; + break; + case wkbMultiLineString: + osGeomFunc = "ST_MLineFromText"; + break; + case wkbMultiPolygon: + osGeomFunc = "ST_MPolyFromText"; + break; + default: + bUpdateGeom = FALSE; + CPLDebug("OGR_IDB", "SetFeature(): Unknown geometry type. Geometry will not be updated."); + } + } + else + bUpdateGeom = FALSE; + + + // Create query + CPLString osSql; + CPLString osFields; + + if ( pszGeomColumn && bUpdateGeom ) + { + OGRGeometry * poGeom = poFeature->GetGeometryRef(); + char * wkt; + poGeom->exportToWkt( &wkt ); + + osFields.Printf( "%s = %s( '%s', %d )", pszGeomColumn, osGeomFunc.c_str(), wkt, nSRSId ); + + CPLFree( wkt ); + } + + for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + const char * pszFieldName = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + + // skip fid column from update + if ( EQUAL( pszFIDColumn, pszFieldName ) ) + continue; + + if ( ! osFields.empty() ) + { + osFields += ","; + } + + osFields += pszFieldName; + osFields += "="; + + if ( ! poFeature->IsFieldSet( i ) ) + { + osFields += "NULL"; + continue; + } + + CPLString osVal; + + switch ( poFeatureDefn->GetFieldDefn( i )->GetType() ) + { + case OFTInteger: + osVal.Printf( "%d", poFeature->GetFieldAsInteger( i ) ); + break; + + case OFTReal: + if ( poFeatureDefn->GetFieldDefn( i )->GetPrecision() ) + { + // have a decimal format width.precision + CPLString osFormatString; + osFormatString.Printf( "%%%d.%df", + poFeatureDefn->GetFieldDefn( i )->GetWidth(), + poFeatureDefn->GetFieldDefn( i )->GetPrecision() ); + osVal.Printf( osFormatString.c_str(), poFeature->GetFieldAsDouble( i ) ); + } + else + osVal.Printf( "%f", poFeature->GetFieldAsDouble( i ) ); + break; + + case OFTIntegerList: + case OFTRealList: + case OFTStringList: + // FIXME Prepare array of values field + //cv->ConvertFrom( poFeature->GetFieldAsStringList( i ) ); + //break; + + case OFTBinary: + // FIXME Prepare binary field + + case OFTString: + case OFTDate: + case OFTTime: + case OFTDateTime: + default: + osVal.Printf( "'%s'", poFeature->GetFieldAsString( i ) ); + break; + } + osFields += osVal; + } + + osSql.Printf( "UPDATE %s SET %s WHERE %s = %d", + poFeatureDefn->GetName(), + osFields.c_str(), + pszFIDColumn, + poFeature->GetFID() ); + + if ( ! oQuery.Prepare( osSql.c_str() ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error prepare SQL.\n%s",osSql.c_str() ); + return eErr; + } + + CPLDebug( "OGR_IDB", "Exec(%s)", oQuery.QueryText().Data() ); + if( !oQuery.Exec() ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Error update Feature."); + return eErr; + } + + return OGRERR_NONE; +} + +OGRErr OGRIDBTableLayer::ICreateFeature( OGRFeature *poFeature ) +{ + OGRErr eErr(OGRERR_FAILURE); + + if ( ! bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error create feature. Layer is read only." ); + return eErr; + } + + if( NULL == poFeature ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "NULL pointer to OGRFeature passed to CreateFeature()." ); + return eErr; + } + + if( poFeature->GetFID() != OGRNullFID && pszFIDColumn == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "FID ignored on feature given to CreateFeature(). Unknown FID column." ); + return eErr; + } + + int bUpdateGeom = TRUE; + CPLString osGeomFunc; + + if ( poFeature->GetGeometryRef() ) + { + OGRwkbGeometryType nGeomType = poFeature->GetGeometryRef()->getGeometryType(); + + switch (nGeomType) + { + case wkbPoint: + osGeomFunc = "ST_PointFromText"; + break; + case wkbLineString: + osGeomFunc = "ST_LineFromText"; + break; + case wkbPolygon: + osGeomFunc = "ST_PolyFromText"; + break; + case wkbMultiPoint: + osGeomFunc = "ST_MPointFromText"; + break; + case wkbMultiLineString: + osGeomFunc = "ST_MLineFromText"; + break; + case wkbMultiPolygon: + osGeomFunc = "ST_MPolyFromText"; + break; + default: + bUpdateGeom = FALSE; + CPLDebug("OGR_IDB", "SetFeature(): Unknown geometry type. Geometry will not be updated."); + } + } + else + bUpdateGeom = FALSE; + + // Create query + CPLString osSql; + CPLString osFields; + CPLString osValues; + + if ( pszGeomColumn && bUpdateGeom ) + { + OGRGeometry * poGeom = poFeature->GetGeometryRef(); + char * wkt; + poGeom->exportToWkt( &wkt ); + + osFields += pszGeomColumn; + osValues.Printf( "%s( '%s', %d )", osGeomFunc.c_str(), wkt, nSRSId ); + + CPLFree( wkt ); + } + + for( int i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + const char * pszFieldName = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + + // Skip NULL fields + if ( ! poFeature->IsFieldSet( i ) ) + { + continue; + } + + if ( ! osFields.empty() ) + { + osFields += ","; + osValues += ","; + } + + osFields += pszFieldName; + + CPLString osVal; + + switch ( poFeatureDefn->GetFieldDefn( i )->GetType() ) + { + case OFTInteger: + osVal.Printf( "%d", poFeature->GetFieldAsInteger( i ) ); + break; + + case OFTReal: + if ( poFeatureDefn->GetFieldDefn( i )->GetPrecision() ) + { + // have a decimal format width.precision + CPLString osFormatString; + osFormatString.Printf( "%%%d.%df", + poFeatureDefn->GetFieldDefn( i )->GetWidth(), + poFeatureDefn->GetFieldDefn( i )->GetPrecision() ); + osVal.Printf( osFormatString.c_str(), poFeature->GetFieldAsDouble( i ) ); + } + else + osVal.Printf( "%f", poFeature->GetFieldAsDouble( i ) ); + break; + + case OFTIntegerList: + case OFTRealList: + case OFTStringList: + // FIXME Prepare array of values field + //cv->ConvertFrom( poFeature->GetFieldAsStringList( i ) ); + //break; + + case OFTBinary: + // FIXME Prepare binary field + + case OFTString: + case OFTDate: + case OFTTime: + case OFTDateTime: + default: + osVal.Printf( "'%s'", poFeature->GetFieldAsString( i ) ); + break; + } + osValues += osVal; + } + + osSql.Printf( "INSERT INTO %s (%s) VALUES (%s)", + poFeatureDefn->GetName(), + osFields.c_str(), + osValues.c_str() ); + + ITStatement oQuery( *poDS->GetConnection() ); + + if ( ! oQuery.Prepare( osSql.c_str() ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error prepare SQL.\n%s",osSql.c_str() ); + return eErr; + } + + CPLDebug( "OGR_IDB", "Exec(%s)", oQuery.QueryText().Data() ); + if( !oQuery.Exec() ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Error create Feature."); + return eErr; + } + + ITQuery oFidQuery( *poDS->GetConnection() ); + osSql.Printf( "SELECT MAX(%s) from %s", pszFIDColumn, poFeatureDefn->GetName() ); + + CPLDebug( "OGR_IDB", "Exec(%s)", osSql.c_str() ); + + ITRow * row = oFidQuery.ExecOneRow( osSql.c_str() ); + if( ! row || row->NumColumns() < 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Error create Feature."); + return eErr; + } + + int fid = atoi( row->Column(0)->Printable() ); + + if ( fid > 0 ) + poFeature->SetFID( fid ); + else + { + CPLError( CE_Failure, CPLE_AppDefined, "Error create Feature. Unable to get new fid" ); + return eErr; + } + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/GNUmakefile new file mode 100644 index 000000000..545929a46 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogridrisidriver.o ogridrisidatasource.o ogridrisilayer.o + +CPPFLAGS := -I.. -I../.. -I../../../frmts/idrisi $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_idrisi.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/drv_idrisi.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/drv_idrisi.html new file mode 100644 index 000000000..44644d822 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/drv_idrisi.html @@ -0,0 +1,35 @@ + + +Idrisi Vector (.VCT) + + + + +

    Idrisi Vector (.VCT)

    + +(GDAL/OGR >= 1.9.0)

    + +This driver reads Idrisi vector files with .vct extension. The driver +recognized point, lines and polygons geometries.

    + +For geographical referencing identification, the .vdc file contains information +that points to a file that holds the geographic reference details. Those files +uses extension REF and resides in the same folder as the RST image or more +likely in the Idrisi installation folders.

    + +Therefore the presence or absence of the Idrisi software in the running operation +system will determine the way that this driver will work. By setting the +environment variable IDRISIDIR pointing to the Idrisi main installation folder +will enable GDAL to find more detailed information about geographical reference +and projection in the REF files.

    + +Note that the driver recognizes the name convention used in Idrisi for UTM and +State Plane geographic reference so it doesn't need to access the REF files. That +is the case for RDC file that specify "utm-30n" or "spc87ma1" in the "ref. system" +field. Note that exporting to RST in any other geographical reference system will +generate a suggested REF content in the comment section of the RDC file.

    + +Starting with OGR 1.10, the driver can retrieve attributes from .ADC / .AVL ASCII files.

    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/generate_test_files.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/generate_test_files.c new file mode 100644 index 000000000..1f4ad26fc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/generate_test_files.c @@ -0,0 +1,248 @@ +/****************************************************************************** + * $Id$ + * + * Project: + * Purpose: Generate sample .VCT files + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include + +int main(int argc, char* argv[]) +{ + char c; + int i; + int nfeatures; + int nodes; + int nparts; + FILE* f; + double id, x, y; + double minx, maxx, miny, maxy; + + f = fopen("points.vct", "wb"); + c = 0x01; + fwrite(&c, 1, 1, f); + nfeatures = 2; + fwrite(&nfeatures, 1, sizeof(int), f); + for(i=5;i<0x105;i++) + { + c = 0; + fwrite(&c, 1, 1, f); + } + + id = 1; + x = 400000; + y = 5000000; + fwrite(&id, 1, sizeof(double), f); + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + + id = 2; + x = 600000; + y = 4000000; + fwrite(&id, 1, sizeof(double), f); + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + + fclose(f); + + + + f = fopen("lines.vct", "wb"); + c = 0x02; + fwrite(&c, 1, 1, f); + nfeatures = 2; + fwrite(&nfeatures, 1, sizeof(int), f); + for(i=5;i<0x105;i++) + { + c = 0; + fwrite(&c, 1, 1, f); + } + + id = 10; + minx = 400000; + miny = 4500000; + maxx = 600000; + maxy = 5000000; + fwrite(&id, 1, sizeof(double), f); + fwrite(&minx, 1, sizeof(double), f); + fwrite(&miny, 1, sizeof(double), f); + fwrite(&maxx, 1, sizeof(double), f); + fwrite(&maxy, 1, sizeof(double), f); + nodes = 2; + fwrite(&nodes, 1, sizeof(int), f); + x = 400000; + y = 5000000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 600000; + y = 4500000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + + id = 20; + minx = 400000; + miny = 4000000; + maxx = 600000; + maxy = 4500000; + fwrite(&id, 1, sizeof(double), f); + fwrite(&minx, 1, sizeof(double), f); + fwrite(&miny, 1, sizeof(double), f); + fwrite(&maxx, 1, sizeof(double), f); + fwrite(&maxy, 1, sizeof(double), f); + nodes = 2; + fwrite(&nodes, 1, sizeof(int), f); + x = 450000; + y = 4000000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 550000; + y = 4500000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + + fclose(f); + + + + f = fopen("polygons.vct", "wb"); + c = 0x03; + fwrite(&c, 1, 1, f); + nfeatures = 2; + fwrite(&nfeatures, 1, sizeof(int), f); + for(i=5;i<0x105;i++) + { + c = 0; + fwrite(&c, 1, 1, f); + } + + id = 1; + minx = 400000; + miny = 4000000; + maxx = 600000; + maxy = 5000000; + fwrite(&id, 1, sizeof(double), f); + fwrite(&minx, 1, sizeof(double), f); + fwrite(&miny, 1, sizeof(double), f); + fwrite(&maxx, 1, sizeof(double), f); + fwrite(&maxy, 1, sizeof(double), f); + nparts = 2; + fwrite(&nparts, 1, sizeof(int), f); + nodes = 10; + fwrite(&nodes, 1, sizeof(int), f); + + nodes = 5; + fwrite(&nodes, 1, sizeof(int), f); + + nodes = 5; + fwrite(&nodes, 1, sizeof(int), f); + + x = 400000; + y = 4000000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 400000; + y = 5000000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 600000; + y = 5000000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 600000; + y = 4000000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 400000; + y = 4000000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + + x = 450000; + y = 4250000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 450000; + y = 4750000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 550000; + y = 4750000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 550000; + y = 4250000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 450000; + y = 4250000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + + + + id = 2; + minx = 400000; + miny = 4000000; + maxx = 600000; + maxy = 5000000; + fwrite(&id, 1, sizeof(double), f); + fwrite(&minx, 1, sizeof(double), f); + fwrite(&miny, 1, sizeof(double), f); + fwrite(&maxx, 1, sizeof(double), f); + fwrite(&maxy, 1, sizeof(double), f); + nparts = 1; + fwrite(&nparts, 1, sizeof(int), f); + nodes = 5; + fwrite(&nodes, 1, sizeof(int), f); + + nodes = 5; + fwrite(&nodes, 1, sizeof(int), f); + + x = 400000; + y = 4000000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 400000; + y = 5000000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 600000; + y = 5000000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 600000; + y = 4000000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + x = 400000; + y = 4000000; + fwrite(&x, 1, sizeof(double), f); + fwrite(&y, 1, sizeof(double), f); + + + fclose(f); + + return 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/makefile.vc new file mode 100644 index 000000000..76cbeadf6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogridrisidriver.obj ogridrisidatasource.obj ogridrisilayer.obj +EXTRAFLAGS = -I.. -I..\.. -I..\..\..\frmts\idrisi + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/ogr_idrisi.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/ogr_idrisi.h new file mode 100644 index 000000000..5a0afe79d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/ogr_idrisi.h @@ -0,0 +1,125 @@ +/****************************************************************************** + * $Id: ogr_idrisi.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: Idrisi Translator + * Purpose: Definition of classes for OGR Idrisi driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_IDRISI_H_INCLUDED +#define _OGR_IDRISI_H_INCLUDED + +#include "ogrsf_frmts.h" + +/************************************************************************/ +/* OGRIdrisiLayer */ +/************************************************************************/ + +class OGRIdrisiLayer : public OGRLayer +{ +protected: + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference *poSRS; + OGRwkbGeometryType eGeomType; + + VSILFILE* fp; + VSILFILE* fpAVL; + int bEOF; + + int nNextFID; + + int bExtentValid; + double dfMinX; + double dfMinY; + double dfMaxX; + double dfMaxY; + + unsigned int nTotalFeatures; + + int Detect_AVL_ADC(const char* pszFilename); + void ReadAVLLine(OGRFeature* poFeature); + + virtual OGRFeature * GetNextRawFeature(); + + public: + OGRIdrisiLayer(const char* pszFilename, + const char* pszLayerName, VSILFILE* fp, + OGRwkbGeometryType eGeomType, const char* pszWTKString); + ~OGRIdrisiLayer(); + + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); + + void SetExtent(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); +}; + +/************************************************************************/ +/* OGRIdrisiDataSource */ +/************************************************************************/ + +class OGRIdrisiDataSource : public OGRDataSource +{ + char* pszName; + + OGRLayer** papoLayers; + int nLayers; + + public: + OGRIdrisiDataSource(); + ~OGRIdrisiDataSource(); + + int Open( const char * pszFilename ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRIdrisiDriver */ +/************************************************************************/ + +class OGRIdrisiDriver : public OGRSFDriver +{ + public: + ~OGRIdrisiDriver(); + + virtual const char* GetName(); + virtual OGRDataSource* Open( const char *, int ); + virtual int TestCapability( const char * ); +}; + + +#endif /* ndef _OGR_IDRISI_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/ogridrisidatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/ogridrisidatasource.cpp new file mode 100644 index 000000000..3b81a8c93 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/ogridrisidatasource.cpp @@ -0,0 +1,190 @@ +/****************************************************************************** + * $Id: ogridrisidatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: Idrisi Translator + * Purpose: Implements OGRIdrisiDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_idrisi.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "idrisi.h" + +CPL_CVSID("$Id: ogridrisidatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRIdrisiDataSource() */ +/************************************************************************/ + +OGRIdrisiDataSource::OGRIdrisiDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; +} + +/************************************************************************/ +/* ~OGRIdrisiDataSource() */ +/************************************************************************/ + +OGRIdrisiDataSource::~OGRIdrisiDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRIdrisiDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRIdrisiDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRIdrisiDataSource::Open( const char * pszFilename ) + +{ + pszName = CPLStrdup( pszFilename ); + + VSILFILE* fpVCT = VSIFOpenL(pszFilename, "rb"); + if (fpVCT == NULL) + return FALSE; + + char* pszWTKString = NULL; + +// -------------------------------------------------------------------- +// Look for .vdc file +// -------------------------------------------------------------------- + const char* pszVDCFilename = CPLResetExtension(pszFilename, "vdc"); + VSILFILE* fpVDC = VSIFOpenL(pszVDCFilename, "rb"); + if (fpVDC == NULL) + { + pszVDCFilename = CPLResetExtension(pszFilename, "VDC"); + fpVDC = VSIFOpenL(pszVDCFilename, "rb"); + } + + char** papszVDC = NULL; + if (fpVDC != NULL) + { + VSIFCloseL(fpVDC); + fpVDC = NULL; + + CPLPushErrorHandler(CPLQuietErrorHandler); + papszVDC = CSLLoad2(pszVDCFilename, 1024, 256, NULL); + CPLPopErrorHandler(); + CPLErrorReset(); + } + + OGRwkbGeometryType eType = wkbUnknown; + + if (papszVDC != NULL) + { + CSLSetNameValueSeparator( papszVDC, ":" ); + + const char *pszVersion = CSLFetchNameValue( papszVDC, "file format " ); + + if( pszVersion == NULL || !EQUAL( pszVersion, "IDRISI Vector A.1" ) ) + { + CSLDestroy( papszVDC ); + VSIFCloseL(fpVCT); + return FALSE; + } + + const char *pszRefSystem = CSLFetchNameValue( papszVDC, "ref. system " ); + const char *pszRefUnits = CSLFetchNameValue( papszVDC, "ref. units " ); + + if (pszRefSystem != NULL && pszRefUnits != NULL) + IdrisiGeoReference2Wkt( pszFilename, pszRefSystem, pszRefUnits, &pszWTKString); + } + + GByte chType; + if (VSIFReadL(&chType, 1, 1, fpVCT) != 1) + { + VSIFCloseL(fpVCT); + CSLDestroy( papszVDC ); + return FALSE; + } + + if (chType == 1) + eType = wkbPoint; + else if (chType == 2) + eType = wkbLineString; + else if (chType == 3) + eType = wkbPolygon; + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Unsupport geometry type : %d", + (int)chType); + VSIFCloseL(fpVCT); + CSLDestroy( papszVDC ); + return FALSE; + } + + const char *pszMinX = CSLFetchNameValue( papszVDC, "min. X " ); + const char *pszMaxX = CSLFetchNameValue( papszVDC, "max. X " ); + const char *pszMinY = CSLFetchNameValue( papszVDC, "min. Y " ); + const char *pszMaxY = CSLFetchNameValue( papszVDC, "max. Y " ); + + OGRIdrisiLayer* poLayer = new OGRIdrisiLayer(pszFilename, + CPLGetBasename(pszFilename), + fpVCT, eType, pszWTKString); + papoLayers = (OGRLayer**) CPLMalloc(sizeof(OGRLayer*)); + papoLayers[nLayers ++] = poLayer; + + if (pszMinX != NULL && pszMaxX != NULL && pszMinY != NULL && pszMaxY != NULL) + { + poLayer->SetExtent(CPLAtof(pszMinX), CPLAtof(pszMinY), CPLAtof(pszMaxX), CPLAtof(pszMaxY)); + } + + CPLFree(pszWTKString); + + CSLDestroy( papszVDC ); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/ogridrisidriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/ogridrisidriver.cpp new file mode 100644 index 000000000..62881c737 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/ogridrisidriver.cpp @@ -0,0 +1,109 @@ +/****************************************************************************** + * $I$ + * + * Project: Idrisi Translator + * Purpose: Implements OGRIdrisiDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_idrisi.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogridrisidriver.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +// g++ ogr/ogrsf_frmts/idrisi/*.cpp -Wall -g -fPIC -shared -o ogr_Idrisi.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts/idrisi -Iogr/ogrsf_frmts -Ifrmts/idrisi + +extern "C" void RegisterOGRIdrisi(); + +/************************************************************************/ +/* ~OGRIdrisiDriver() */ +/************************************************************************/ + +OGRIdrisiDriver::~OGRIdrisiDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRIdrisiDriver::GetName() + +{ + return "Idrisi"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRIdrisiDriver::Open( const char * pszFilename, int bUpdate ) + +{ + if (bUpdate) + { + return NULL; + } + +// -------------------------------------------------------------------- +// Does this appear to be a .vct file? +// -------------------------------------------------------------------- + if ( !EQUAL(CPLGetExtension(pszFilename), "vct") ) + return NULL; + + OGRIdrisiDataSource *poDS = new OGRIdrisiDataSource(); + + if( !poDS->Open( pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRIdrisiDriver::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRIdrisi() */ +/************************************************************************/ + +void RegisterOGRIdrisi() + +{ + OGRSFDriver* poDriver = new OGRIdrisiDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Idrisi Vector (.vct)" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "vct" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/ogridrisilayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/ogridrisilayer.cpp new file mode 100644 index 000000000..ee2455eab --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/idrisi/ogridrisilayer.cpp @@ -0,0 +1,618 @@ +/****************************************************************************** + * $Id: ogridrisilayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: Idrisi Translator + * Purpose: Implements OGRIdrisiLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_idrisi.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: ogridrisilayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRIdrisiLayer() */ +/************************************************************************/ + +OGRIdrisiLayer::OGRIdrisiLayer( const char* pszFilename, + const char* pszLayerName, + VSILFILE* fp, + OGRwkbGeometryType eGeomType, + const char* pszWTKString ) + +{ + this->fp = fp; + this->eGeomType = eGeomType; + nNextFID = 1; + bEOF = FALSE; + fpAVL = NULL; + + if (pszWTKString) + { + poSRS = new OGRSpatialReference(); + char* pszTmp = (char*)pszWTKString; + poSRS->importFromWkt(&pszTmp); + } + else + poSRS = NULL; + + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + poFeatureDefn->SetGeomType( eGeomType ); + + OGRFieldDefn oFieldDefn("id", OFTReal); + poFeatureDefn->AddFieldDefn( &oFieldDefn ); + + bExtentValid = FALSE; + dfMinX = dfMinY = dfMaxX = dfMaxY = 0.0; + + VSIFSeekL( fp, 1, SEEK_SET ); + if (VSIFReadL( &nTotalFeatures, sizeof(unsigned int), 1, fp ) != 1) + nTotalFeatures = 0; + CPL_LSBPTR32(&nTotalFeatures); + + if (nTotalFeatures != 0) + { + if (!Detect_AVL_ADC(pszFilename)) + { + if( fpAVL != NULL ) + VSIFCloseL( fpAVL ); + fpAVL = NULL; + } + } + + ResetReading(); +} + +/************************************************************************/ +/* ~OGRIdrisiLayer() */ +/************************************************************************/ + +OGRIdrisiLayer::~OGRIdrisiLayer() + +{ + if( poSRS != NULL ) + poSRS->Release(); + + poFeatureDefn->Release(); + + VSIFCloseL( fp ); + + if( fpAVL != NULL ) + VSIFCloseL( fpAVL ); +} + +/************************************************************************/ +/* Detect_AVL_ADC() */ +/************************************************************************/ + +int OGRIdrisiLayer::Detect_AVL_ADC(const char* pszFilename) +{ +// -------------------------------------------------------------------- +// Look for .adc file +// -------------------------------------------------------------------- + const char* pszADCFilename = CPLResetExtension(pszFilename, "adc"); + VSILFILE* fpADC = VSIFOpenL(pszADCFilename, "rb"); + if (fpADC == NULL) + { + pszADCFilename = CPLResetExtension(pszFilename, "ADC"); + fpADC = VSIFOpenL(pszADCFilename, "rb"); + } + + char** papszADC = NULL; + if (fpADC != NULL) + { + VSIFCloseL(fpADC); + fpADC = NULL; + + CPLPushErrorHandler(CPLQuietErrorHandler); + papszADC = CSLLoad2(pszADCFilename, 1024, 256, NULL); + CPLPopErrorHandler(); + CPLErrorReset(); + } + + if (papszADC == NULL) + return FALSE; + + CSLSetNameValueSeparator( papszADC, ":" ); + + const char *pszVersion = CSLFetchNameValue( papszADC, "file format " ); + if( pszVersion == NULL || !EQUAL( pszVersion, "IDRISI Values A.1" ) ) + { + CSLDestroy( papszADC ); + return FALSE; + } + + const char *pszFileType = CSLFetchNameValue( papszADC, "file type " ); + if( pszFileType == NULL || !EQUAL( pszFileType, "ascii" ) ) + { + CPLDebug("IDRISI", ".adc file found, but file type != ascii"); + CSLDestroy( papszADC ); + return FALSE; + } + + const char* pszRecords = CSLFetchNameValue( papszADC, "records " ); + if( pszRecords == NULL || atoi(pszRecords) != (int)nTotalFeatures ) + { + CPLDebug("IDRISI", ".adc file found, but 'records' not found or not " + "consistent with feature number declared in .vdc"); + CSLDestroy( papszADC ); + return FALSE; + } + + const char* pszFields = CSLFetchNameValue( papszADC, "fields " ); + if( pszFields == NULL || atoi(pszFields) <= 1 ) + { + CPLDebug("IDRISI", ".adc file found, but 'fields' not found or invalid"); + CSLDestroy( papszADC ); + return FALSE; + } + +// -------------------------------------------------------------------- +// Look for .avl file +// -------------------------------------------------------------------- + const char* pszAVLFilename = CPLResetExtension(pszFilename, "avl"); + fpAVL = VSIFOpenL(pszAVLFilename, "rb"); + if (fpAVL == NULL) + { + pszAVLFilename = CPLResetExtension(pszFilename, "AVL"); + fpAVL = VSIFOpenL(pszAVLFilename, "rb"); + } + if (fpAVL == NULL) + { + CSLDestroy( papszADC ); + return FALSE; + } + +// -------------------------------------------------------------------- +// Build layer definition +// -------------------------------------------------------------------- + + int iCurField; + char szKey[32]; + + iCurField = 0; + sprintf(szKey, "field %d ", iCurField); + + char** papszIter = papszADC; + const char* pszLine; + int bFieldFound = FALSE; + CPLString osFieldName; + while((pszLine = *papszIter) != NULL) + { + //CPLDebug("IDRISI", "%s", pszLine); + if (strncmp(pszLine, szKey, strlen(szKey)) == 0) + { + const char* pszColon = strchr(pszLine, ':'); + if (pszColon) + { + osFieldName = pszColon + 1; + bFieldFound = TRUE; + } + } + else if (bFieldFound && + strncmp(pszLine, "data type :", strlen("data type :")) == 0) + { + const char* pszFieldType = pszLine + strlen("data type :"); + + OGRFieldDefn oFieldDefn(osFieldName.c_str(), + EQUAL(pszFieldType, "integer") ? OFTInteger : + EQUAL(pszFieldType, "real") ? OFTReal : OFTString); + + if( iCurField == 0 && oFieldDefn.GetType() != OFTInteger ) + { + CSLDestroy( papszADC ); + return FALSE; + } + + if( iCurField != 0 ) + poFeatureDefn->AddFieldDefn( &oFieldDefn ); + + iCurField ++; + sprintf(szKey, "field %d ", iCurField); + } + + papszIter++; + } + + CSLDestroy(papszADC); + + return TRUE; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRIdrisiLayer::ResetReading() + +{ + nNextFID = 1; + bEOF = FALSE; + VSIFSeekL( fp, 0x105, SEEK_SET ); + if( fpAVL != NULL ) + VSIFSeekL( fpAVL, 0, SEEK_SET ); +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRIdrisiLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + while(TRUE) + { + if (bEOF) + return NULL; + + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + { + bEOF = TRUE; + return NULL; + } + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRIdrisiLayer::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap, OLCFastFeatureCount)) + return m_poFilterGeom == NULL && m_poAttrQuery == NULL; + + if (EQUAL(pszCap, OLCFastGetExtent)) + return bExtentValid; + + return FALSE; +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRIdrisiLayer::GetNextRawFeature() +{ + while(TRUE) + { + if (eGeomType == wkbPoint) + { + double dfId; + double dfX, dfY; + if (VSIFReadL(&dfId, sizeof(double), 1, fp) != 1 || + VSIFReadL(&dfX, sizeof(double), 1, fp) != 1 || + VSIFReadL(&dfY, sizeof(double), 1, fp) != 1) + { + return NULL; + } + CPL_LSBPTR64(&dfId); + CPL_LSBPTR64(&dfX); + CPL_LSBPTR64(&dfY); + + if (m_poFilterGeom != NULL && + (dfX < m_sFilterEnvelope.MinX || + dfX > m_sFilterEnvelope.MaxX || + dfY < m_sFilterEnvelope.MinY || + dfY > m_sFilterEnvelope.MaxY)) + { + nNextFID ++; + continue; + } + + OGRPoint* poGeom = new OGRPoint(dfX, dfY); + if (poSRS) + poGeom->assignSpatialReference(poSRS); + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField(0, dfId); + poFeature->SetFID(nNextFID ++); + poFeature->SetGeometryDirectly(poGeom); + ReadAVLLine(poFeature); + return poFeature; + } + else if (eGeomType == wkbLineString) + { + double dfId; + double dfMinXShape, dfMaxXShape, dfMinYShape, dfMaxYShape; + unsigned int nNodes; + + if (VSIFReadL(&dfId, sizeof(double), 1, fp) != 1 || + VSIFReadL(&dfMinXShape, sizeof(double), 1, fp) != 1 || + VSIFReadL(&dfMaxXShape, sizeof(double), 1, fp) != 1 || + VSIFReadL(&dfMinYShape, sizeof(double), 1, fp) != 1 || + VSIFReadL(&dfMaxYShape, sizeof(double), 1, fp) != 1) + { + return NULL; + } + CPL_LSBPTR64(&dfId); + CPL_LSBPTR64(&dfMinXShape); + CPL_LSBPTR64(&dfMaxXShape); + CPL_LSBPTR64(&dfMinYShape); + CPL_LSBPTR64(&dfMaxYShape); + + if (VSIFReadL(&nNodes, sizeof(unsigned int), 1, fp) != 1) + { + return NULL; + } + CPL_LSBPTR32(&nNodes); + + if (nNodes > 100 * 1000 * 1000) + return NULL; + + if (m_poFilterGeom != NULL && + (dfMaxXShape < m_sFilterEnvelope.MinX || + dfMinXShape > m_sFilterEnvelope.MaxX || + dfMaxYShape < m_sFilterEnvelope.MinY || + dfMinYShape > m_sFilterEnvelope.MaxY)) + { + nNextFID ++; + VSIFSeekL(fp, sizeof(OGRRawPoint) * nNodes, SEEK_CUR); + continue; + } + + OGRRawPoint* poRawPoints = (OGRRawPoint*)VSIMalloc2(sizeof(OGRRawPoint), nNodes); + if (poRawPoints == NULL) + { + return NULL; + } + + if ((unsigned int)VSIFReadL(poRawPoints, sizeof(OGRRawPoint), nNodes, fp) != nNodes) + { + VSIFree(poRawPoints); + return NULL; + } + +#if defined(CPL_MSB) + for(unsigned int iNode=0; iNodesetPoints(nNodes, poRawPoints, NULL); + + VSIFree(poRawPoints); + + if (poSRS) + poGeom->assignSpatialReference(poSRS); + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField(0, dfId); + poFeature->SetFID(nNextFID ++); + poFeature->SetGeometryDirectly(poGeom); + ReadAVLLine(poFeature); + return poFeature; + } + else /* if (eGeomType == wkbPolygon) */ + { + double dfId; + double dfMinXShape, dfMaxXShape, dfMinYShape, dfMaxYShape; + unsigned int nParts; + unsigned int nTotalNodes; + + if (VSIFReadL(&dfId, sizeof(double), 1, fp) != 1 || + VSIFReadL(&dfMinXShape, sizeof(double), 1, fp) != 1 || + VSIFReadL(&dfMaxXShape, sizeof(double), 1, fp) != 1 || + VSIFReadL(&dfMinYShape, sizeof(double), 1, fp) != 1 || + VSIFReadL(&dfMaxYShape, sizeof(double), 1, fp) != 1) + { + return NULL; + } + CPL_LSBPTR64(&dfId); + CPL_LSBPTR64(&dfMinXShape); + CPL_LSBPTR64(&dfMaxXShape); + CPL_LSBPTR64(&dfMinYShape); + CPL_LSBPTR64(&dfMaxYShape); + if (VSIFReadL(&nParts, sizeof(unsigned int), 1, fp) != 1 || + VSIFReadL(&nTotalNodes, sizeof(unsigned int), 1, fp) != 1) + { + return NULL; + } + CPL_LSBPTR32(&nParts); + CPL_LSBPTR32(&nTotalNodes); + + if (nParts > 100000 || nTotalNodes > 100 * 1000 * 1000) + return NULL; + + if (m_poFilterGeom != NULL && + (dfMaxXShape < m_sFilterEnvelope.MinX || + dfMinXShape > m_sFilterEnvelope.MaxX || + dfMaxYShape < m_sFilterEnvelope.MinY || + dfMinYShape > m_sFilterEnvelope.MaxY)) + { + VSIFSeekL(fp, sizeof(unsigned int) * nParts + sizeof(OGRRawPoint) * nTotalNodes, SEEK_CUR); + nNextFID ++; + continue; + } + + OGRRawPoint* poRawPoints = (OGRRawPoint*)VSIMalloc2(sizeof(OGRRawPoint), nTotalNodes); + if (poRawPoints == NULL) + { + return NULL; + } + unsigned int* panNodesCount = NULL; + if( nParts > 1 ) + { + panNodesCount = (unsigned int *)CPLMalloc(sizeof(unsigned int) * nParts); + if (VSIFReadL(panNodesCount, sizeof(unsigned int) * nParts, 1, fp) != 1) + { + VSIFree(poRawPoints); + VSIFree(panNodesCount); + return NULL; + } +#if defined(CPL_MSB) + for(unsigned int iPart=0; iPart < nParts; iPart ++) + { + CPL_LSBPTR32(&panNodesCount[iPart]); + } +#endif + } + else + { + unsigned int nNodes; + if (VSIFReadL(&nNodes, sizeof(unsigned int) * nParts, 1, fp) != 1) + { + VSIFree(poRawPoints); + return NULL; + } + CPL_LSBPTR32(&nNodes); + if( nNodes != nTotalNodes ) + { + VSIFree(poRawPoints); + return NULL; + } + } + + unsigned int iPart; + OGRPolygon* poGeom = new OGRPolygon(); + for(iPart = 0; iPart < nParts; iPart ++) + { + unsigned int nNodes = (nParts > 1) ? panNodesCount[iPart] : nTotalNodes; + if (nNodes > nTotalNodes || + (unsigned int)VSIFReadL(poRawPoints, sizeof(OGRRawPoint), nNodes, fp) != nNodes) + { + VSIFree(poRawPoints); + VSIFree(panNodesCount); + delete poGeom; + return NULL; + } + +#if defined(CPL_MSB) + for(unsigned int iNode=0; iNodeaddRingDirectly(poLR); + poLR->setPoints(nNodes, poRawPoints, NULL); + } + + VSIFree(poRawPoints); + VSIFree(panNodesCount); + + if (poSRS) + poGeom->assignSpatialReference(poSRS); + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField(0, dfId); + poFeature->SetFID(nNextFID ++); + poFeature->SetGeometryDirectly(poGeom); + ReadAVLLine(poFeature); + return poFeature; + } + } +} + +/************************************************************************/ +/* ReadAVLLine() */ +/************************************************************************/ + +void OGRIdrisiLayer::ReadAVLLine(OGRFeature* poFeature) +{ + if (fpAVL == NULL) + return; + + const char* pszLine = CPLReadLineL(fpAVL); + if (pszLine == NULL) + return; + + char** papszTokens = CSLTokenizeStringComplex(pszLine, "\t", TRUE, TRUE); + if (CSLCount(papszTokens) == poFeatureDefn->GetFieldCount()) + { + int nID = atoi(papszTokens[0]); + if (nID == poFeature->GetFID()) + { + int i; + for(i=1;iGetFieldCount();i++) + { + poFeature->SetField(i, papszTokens[i]); + } + } + } + CSLDestroy(papszTokens); +} + +/************************************************************************/ +/* SetExtent() */ +/************************************************************************/ + +void OGRIdrisiLayer::SetExtent(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY) +{ + bExtentValid = TRUE; + this->dfMinX = dfMinX; + this->dfMinY = dfMinY; + this->dfMaxX = dfMaxX; + this->dfMaxY = dfMaxY; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRIdrisiLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + if (!bExtentValid) + return OGRLayer::GetExtent(psExtent, bForce); + + psExtent->MinX = dfMinX; + psExtent->MinY = dfMinY; + psExtent->MaxX = dfMaxX; + psExtent->MaxY = dfMaxY; + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRIdrisiLayer::GetFeatureCount( int bForce ) +{ + if (nTotalFeatures > 0 && m_poFilterGeom == NULL && m_poAttrQuery == NULL) + return nTotalFeatures; + + return OGRLayer::GetFeatureCount(bForce); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/GNUmakefile new file mode 100644 index 000000000..b513f6c5e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/GNUmakefile @@ -0,0 +1,21 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrili1driver.o ogrili1datasource.o ogrili1layer.o \ + ogrili2driver.o ogrili2datasource.o ogrili2layer.o \ + ili1reader.o ili2reader.o ili2handler.o \ + imdreader.o + +ifeq ($(HAVE_GEOS),yes) +CPPFLAGS := -DHAVE_GEOS=1 $(GEOS_CFLAGS) $(CPPFLAGS) +endif + +CPPFLAGS := -I.. -I../.. $(XERCES_INCLUDE) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_ili1.h ogr_ili2.h imdreader.h ili1reader.h ili1readerp.h ili2reader.h ili2readerp.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/drv_ili.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/drv_ili.html new file mode 100644 index 000000000..c359e75fd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/drv_ili.html @@ -0,0 +1,110 @@ + + +INTERLIS + + + + +

    INTERLIS

    + +OGR has support for INTERLIS reading and writing.
    +INTERLIS is a standard which has been especially composed in order to fulfil the requirements of modelling and the integration of geodata into contemporary and future geographic information systems. + +With the usage of unified, documented geodata and the flexible exchange possibilities the following advantage may occur: +
      +
    • the standardized documentation
    • +
    • the compatible data exchange
    • +
    • the comprehensive integration of geodata e.g. from different data owners.
    • +
    • the quality proofing
    • +
    • the long term data storage
    • +
    • the contract-proof security and the availability of the software
    • +
    + +OGR supports INTERLIS 1 and INTERLIS 2 (2.2 and 2.3) with the following limitations: +
      +
    • Curves in Interlis 1 areas are converted to line segments
    • +
    • Embedded INTERLIS 2 structures are not supported
    • +
    • Incremental transfer is not supported
    • +
    • Interlis 1 Surface geometries with non-numeric IDENT field are not included in the attribute layer
    • +
    • Transfer id (TID) is used as feature id
    • +
    + +

    Model support

    + +Data is read and written into transfer files which have different formats in INTERLIS 1 (.itf) and INTERLIS 2 (.xtf). + +Models are passed in IlisMeta format by using "a_filename.xtf,models.imd" as a connection string.

    + +IlisMeta files can be be generated with the ili2c compiler. Command line example: +

    +java -jar ili2c.jar --ilidirs '%ILI_DIR;http://models.interlis.ch/;%JAR_DIR' -oIMD --out models.imd model1.ili [model2.ili ...]
    +
    + +Some possible transformations using ogr2ogr.

    +

      +
    • +Interlis 1 -> Shape: +
      +ogr2ogr -f "ESRI Shapefile" shpdir ili-bsp.itf,Beispiel.imd
      +
      +
    • + +
    • +Interlis 2 -> Shape: +
      +ogr2ogr -f "ESRI Shapefile" shpdir RoadsExdm2ien.xml,RoadsExdm2ien.imd
      +
      +or without model: +
      +ogr2ogr -f "ESRI Shapefile" shpdir RoadsExdm2ien.xml
      +
      +
    • + +
    • +Shape -> Interlis 2: +
      +ogr2ogr -f "Interlis 2" LandCover.xml,RoadsExdm2ien.imd RoadsExdm2ben.Roads.LandCover.shp
      +
      +
    • + +
    • +Importing multiple Interlis 1 files into PostGIS: +
      +ogr2ogr -f PostgreSQL PG:dbname=warmerda av_fixpunkte_ohne_LFPNachfuehrung.itf,av.imd -lco OVERWRITE=yes
      +ogr2ogr -f PostgreSQL PG:dbname=warmerda av_fixpunkte_mit_LFPNachfuehrung.itf,av.imd -append
      +
      +
    • +
    + +

    + +

    Arc interpolation

    +Converting INTERLIS arc geometries to line segements can be forced by setting the configuration +variable OGR_STROKE_CURVE to TRUE.

    +The approximation of arcs as linestrings is done by splitting the arcs into subarcs of no more than a +threshhold angle. This angle is the OGR_ARC_STEPSIZE. This defaults to +one degree, but may be overridden by setting the configuration variable +OGR_ARC_STEPSIZE.

    + + +

    OGR versions prior to 2.0

    + +Arcs are always interpolated and the interpolation angle can be configured with the environment variable ARC_DEGREES.

    + +

    OGR versions prior to 1.11

    + +For using the INTERLIS model (.ili) a Java interpreter is needed at runtime and ili2c.jar (included in the +Compiler for INTERLIS 2) must be in the Java path.

    + + +

    Other Notes

    + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili1reader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili1reader.cpp new file mode 100644 index 000000000..dbbd0cf3d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili1reader.cpp @@ -0,0 +1,596 @@ +/****************************************************************************** + * $Id: ili1reader.cpp 29140 2015-05-03 20:09:32Z pka $ + * + * Project: Interlis 1 Reader + * Purpose: Implementation of ILI1Reader class. + * Author: Pirmin Kalberer, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ili1.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_api.h" +#include "ogr_geos.h" + +#include "ili1reader.h" +#include "ili1readerp.h" + +#include + +#ifdef HAVE_GEOS +# define POLYGONIZE_AREAS +#endif + +#ifndef POLYGONIZE_AREAS +# if defined(__GNUC_PREREQ) +# warning Interlis 1 Area polygonizing disabled. Needs GEOS >= 3.1.0 +# endif +#endif + +CPL_CVSID("$Id: ili1reader.cpp 29140 2015-05-03 20:09:32Z pka $"); + +// +// ILI1Reader +// +IILI1Reader::~IILI1Reader() { +} + +ILI1Reader::ILI1Reader() { + fpItf = NULL; + nLayers = 0; + papoLayers = NULL; + curLayer = NULL; + codeBlank = '_'; + codeUndefined = '@'; + codeContinue = '\\'; +} + +ILI1Reader::~ILI1Reader() { + int i; + if (fpItf) VSIFClose( fpItf ); + + for(i=0;iReadModel(pszModelFilename); + for (FeatureDefnInfos::const_iterator it = poImdReader->featureDefnInfos.begin(); it != poImdReader->featureDefnInfos.end(); ++it) + { + //CPLDebug( "OGR_ILI", "Adding OGRILI1Layer with table '%s'", it->poTableDefn->GetName() ); + OGRILI1Layer* layer = new OGRILI1Layer(it->poTableDefn, it->poGeomFieldInfos, poDS); + AddLayer(layer); + //Create additional layers for surface and area geometries + for (GeomFieldInfos::const_iterator it2 = it->poGeomFieldInfos.begin(); it2 != it->poGeomFieldInfos.end(); ++it2) + { + if (it2->second.geomTable) + { + OGRFeatureDefn* poGeomTableDefn = it2->second.geomTable; + OGRGeomFieldDefn* poOGRGeomFieldDefn = poGeomTableDefn->GetGeomFieldDefn(0); + GeomFieldInfos oGeomFieldInfos; + // We add iliGeomType to recognize Ili1 geom tables + oGeomFieldInfos[poOGRGeomFieldDefn->GetNameRef()].iliGeomType = it2->second.iliGeomType; + //CPLDebug( "OGR_ILI", "Adding OGRILI1Layer with geometry table '%s'", it2->second.geomTable->GetName() ); + OGRILI1Layer* geomlayer = new OGRILI1Layer(poGeomTableDefn, oGeomFieldInfos, poDS); + AddLayer(geomlayer); + } + } + } + + codeBlank = poImdReader->codeBlank; + CPLDebug( "OGR_ILI", "Ili1Format blankCode '%c'", poImdReader->codeBlank ); + codeUndefined = poImdReader->codeUndefined; + CPLDebug( "OGR_ILI", "Ili1Format undefinedCode '%c'", poImdReader->codeUndefined ); + codeContinue = poImdReader->codeContinue; + CPLDebug( "OGR_ILI", "Ili1Format continueCode '%c'", poImdReader->codeContinue ); + return 0; +} + +int ILI1Reader::ReadFeatures() { + char **tokens = NULL; + const char *firsttok = NULL; + const char *pszLine; + char *topic = NULL; + int ret = TRUE; + + while (ret && (tokens = ReadParseLine())) + { + firsttok = tokens[0]; + if (EQUAL(firsttok, "SCNT")) + { + //read description + do + { + pszLine = CPLReadLine( fpItf ); + } + while (pszLine && !EQUALN(pszLine, "////", 4)); + ret = (pszLine != NULL); + } + else if (EQUAL(firsttok, "MOTR")) + { + //read model + do + { + pszLine = CPLReadLine( fpItf ); + } + while (pszLine && !EQUALN(pszLine, "////", 4)); + ret = (pszLine != NULL); + } + else if (EQUAL(firsttok, "MTID")) + { + } + else if (EQUAL(firsttok, "MODL")) + { + } + else if (EQUAL(firsttok, "TOPI")) + { + CPLFree(topic); + topic = CPLStrdup(CSLGetField(tokens, 1)); + } + else if (EQUAL(firsttok, "TABL")) + { + const char *layername = GetLayerNameString(topic, CSLGetField(tokens, 1)); + CPLDebug( "OGR_ILI", "Reading table '%s'", layername ); + curLayer = GetLayerByName(layername); + + if (curLayer == NULL) { //create one + CPLError(CE_Warning, CPLE_AppDefined, + "No model definition for table '%s' found, using default field names.", layername ); + OGRFeatureDefn* poFeatureDefn = new OGRFeatureDefn(GetLayerNameString(topic, CSLGetField(tokens, 1))); + poFeatureDefn->SetGeomType( wkbUnknown ); + GeomFieldInfos oGeomFieldInfos; + curLayer = new OGRILI1Layer(poFeatureDefn, oGeomFieldInfos, NULL); + AddLayer(curLayer); + } + if(curLayer != NULL) { + for (int i=0; i < curLayer->GetLayerDefn()->GetFieldCount(); i++) { + CPLDebug( "OGR_ILI", "Field %d: %s", i, curLayer->GetLayerDefn()->GetFieldDefn(i)->GetNameRef()); + } + } + ret = ReadTable(layername); + } + else if (EQUAL(firsttok, "ETOP")) + { + } + else if (EQUAL(firsttok, "EMOD")) + { + } + else if (EQUAL(firsttok, "ENDE")) + { + CSLDestroy(tokens); + CPLFree(topic); + return TRUE; + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, "Unexpected token: %s", firsttok ); + } + + CSLDestroy(tokens); + tokens = NULL; + } + + CSLDestroy(tokens); + CPLFree(topic); + + return ret; +} + +int ILI1Reader::ReadTable(CPL_UNUSED const char *layername) { + char **tokens = NULL; + const char *firsttok = NULL; + int ret = TRUE; + int warned = FALSE; + int fIndex; + int geomIdx = -1; + + OGRFeatureDefn *featureDef = curLayer->GetLayerDefn(); + OGRFeature *feature = NULL; + + while (ret && (tokens = ReadParseLine())) + { + firsttok = CSLGetField(tokens, 0); + if (EQUAL(firsttok, "OBJE")) + { + if (featureDef->GetFieldCount() == 0) + { + CPLError(CE_Warning, CPLE_AppDefined, + "No field definition found for table: %s", featureDef->GetName() ); + //Model not read - use heuristics + for (fIndex=1; fIndexAddFieldDefn(&oFieldDefn); + } + } + //start new feature + feature = new OGRFeature(featureDef); + + int fieldno = 0; + for (fIndex=1; fIndexGetFieldCount(); fIndex++, fieldno++) + { + if (!(tokens[fIndex][0] == codeUndefined && tokens[fIndex][1] == '\0')) { + //CPLDebug( "READ TABLE OGR_ILI", "Setting Field %d (Type %d): %s", fieldno, featureDef->GetFieldDefn(fieldno)->GetType(), tokens[fIndex]); + if (featureDef->GetFieldDefn(fieldno)->GetType() == OFTString) { + //Interlis 1 encoding is ISO 8859-1 (Latin1) -> Recode to UTF-8 + char* pszRecoded = CPLRecode(tokens[fIndex], CPL_ENC_ISO8859_1, CPL_ENC_UTF8); + //Replace space marks + for(char* pszString = pszRecoded; *pszString != '\0'; pszString++ ) { + if (*pszString == codeBlank) *pszString = ' '; + } + feature->SetField(fieldno, pszRecoded); + CPLFree(pszRecoded); + } else { + feature->SetField(fieldno, tokens[fIndex]); + } + if (featureDef->GetFieldDefn(fieldno)->GetType() == OFTReal + && fieldno > 0 + && featureDef->GetFieldDefn(fieldno-1)->GetType() == OFTReal) { + //check for Point geometry (Coord type) + // if there is no ili model read, + // we have no chance to detect the + // geometry column!! + CPLString geomfldname = featureDef->GetFieldDefn(fieldno)->GetNameRef(); + //Check if name ends with _1 + if (geomfldname.size() >= 2 && geomfldname[geomfldname.size()-2] == '_') { + geomfldname = geomfldname.substr(0, geomfldname.size()-2); + geomIdx = featureDef->GetGeomFieldIndex(geomfldname.c_str()); + if (geomIdx == -1) + { + CPLError(CE_Warning, CPLE_AppDefined, + "No matching definition for field '%s' of table %s found", geomfldname.c_str(), featureDef->GetName()); + } + } else { + geomIdx = -1; + } + if (geomIdx >= 0) { + if (featureDef->GetGeomFieldDefn(geomIdx)->GetType() == wkbPoint) { + //add Point geometry + OGRPoint *ogrPoint = new OGRPoint(CPLAtof(tokens[fIndex-1]), CPLAtof(tokens[fIndex])); + feature->SetGeomFieldDirectly(geomIdx, ogrPoint); + } else if (featureDef->GetGeomFieldDefn(geomIdx)->GetType() == wkbPoint25D && fieldno > 1 && featureDef->GetFieldDefn(fieldno-2)->GetType() == OFTReal) { + //add 3D Point geometry + OGRPoint *ogrPoint = new OGRPoint(CPLAtof(tokens[fIndex-2]), CPLAtof(tokens[fIndex-1]), CPLAtof(tokens[fIndex])); + feature->SetGeomFieldDirectly(geomIdx, ogrPoint); + } + } + } + } + } + if (!warned && featureDef->GetFieldCount() != CSLCount(tokens)-1) { + CPLError(CE_Warning, CPLE_AppDefined, + "Field count of table %s doesn't match. %d declared, %d found (e.g. ignored LINEATTR)", featureDef->GetName(), featureDef->GetFieldCount(), CSLCount(tokens)-1); + warned = TRUE; + } + if (feature->GetFieldCount() > 0) { + // USE _TID as FID. TODO: respect IDENT field from model + feature->SetFID(feature->GetFieldAsInteger64(0)); + } + curLayer->AddFeature(feature); + geomIdx = -1; //Reset + } + else if (EQUAL(firsttok, "STPT")) + { + //Find next non-Point geometry + if (geomIdx < 0) geomIdx = 0; + while (geomIdx < featureDef->GetGeomFieldCount() && featureDef->GetGeomFieldDefn(geomIdx)->GetType() == wkbPoint) { geomIdx++; } + OGRwkbGeometryType geomType = (geomIdx < featureDef->GetGeomFieldCount()) ? featureDef->GetGeomFieldDefn(geomIdx)->GetType() : wkbNone; + ReadGeom(tokens, geomIdx, geomType, feature); + } + else if (EQUAL(firsttok, "ELIN")) + { + //empty geom + } + else if (EQUAL(firsttok, "EDGE")) + { + tokens = ReadParseLine(); //STPT + //Find next non-Point geometry + do { geomIdx++; } while (geomIdx < featureDef->GetGeomFieldCount() && featureDef->GetGeomFieldDefn(geomIdx)->GetType() == wkbPoint); + ReadGeom(tokens, geomIdx, wkbMultiLineString, feature); + } + else if (EQUAL(firsttok, "PERI")) + { + } + else if (EQUAL(firsttok, "ETAB")) + { + CPLDebug( "OGR_ILI", "Total features: " CPL_FRMT_GIB, curLayer->GetFeatureCount() ); + CSLDestroy(tokens); + return TRUE; + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, "Unexpected token: %s", firsttok ); + } + + CSLDestroy(tokens); + } + + return ret; +} + +void ILI1Reader::ReadGeom(char **stgeom, int geomIdx, OGRwkbGeometryType eType, OGRFeature *feature) { + char **tokens = NULL; + const char *firsttok = NULL; + int end = FALSE; + OGRCompoundCurve *ogrCurve = NULL; //current compound curve + OGRLineString *ogrLine = NULL; //current line + OGRCircularString *arc = NULL; //current arc + OGRCurvePolygon *ogrPoly = NULL; //current polygon + OGRPoint ogrPoint; //current point + OGRMultiCurve *ogrMultiLine = NULL; //current multi line + + //CPLDebug( "OGR_ILI", "ILI1Reader::ReadGeom geomIdx: %d OGRGeometryType: %s", geomIdx, OGRGeometryTypeToName(eType)); + if (eType == wkbNone) + { + CPLError(CE_Warning, CPLE_AppDefined, "Calling ILI1Reader::ReadGeom with wkbNone" ); + } + + //Initialize geometry + + ogrCurve = new OGRCompoundCurve(); + + if (eType == wkbMultiCurve || eType == wkbMultiLineString) + { + ogrMultiLine = new OGRMultiCurve(); + } + else if (eType == wkbPolygon || eType == wkbCurvePolygon) + { + ogrPoly = new OGRCurvePolygon(); + } + + //tokens = ["STPT", "1111", "22222"] + ogrPoint.setX(CPLAtof(stgeom[1])); ogrPoint.setY(CPLAtof(stgeom[2])); + ogrLine = new OGRLineString(); + ogrLine->addPoint(&ogrPoint); + + //Parse geometry + while (!end && (tokens = ReadParseLine())) + { + firsttok = CSLGetField(tokens, 0); + if (EQUAL(firsttok, "LIPT")) + { + ogrPoint.setX(CPLAtof(tokens[1])); ogrPoint.setY(CPLAtof(tokens[2])); + if (arc) { + arc->addPoint(&ogrPoint); + ogrCurve->addCurveDirectly(arc); + arc = NULL; + } + ogrLine->addPoint(&ogrPoint); + } + else if (EQUAL(firsttok, "ARCP")) + { + //Finish line and start arc + if (ogrLine->getNumPoints() > 1) { + ogrCurve->addCurveDirectly(ogrLine); + ogrLine = new OGRLineString(); + } else { + ogrLine->empty(); + } + arc = new OGRCircularString(); + arc->addPoint(&ogrPoint); + ogrPoint.setX(CPLAtof(tokens[1])); ogrPoint.setY(CPLAtof(tokens[2])); + arc->addPoint(&ogrPoint); + } + else if (EQUAL(firsttok, "ELIN")) + { + if (!ogrLine->IsEmpty()) { + ogrCurve->addCurveDirectly(ogrLine); + } + if (!ogrCurve->IsEmpty()) { + if (ogrMultiLine) + { + ogrMultiLine->addGeometryDirectly(ogrCurve); + } + if (ogrPoly) + { + ogrPoly->addRingDirectly(ogrCurve); + } + } + end = TRUE; + } + else if (EQUAL(firsttok, "EEDG")) + { + end = TRUE; + } + else if (EQUAL(firsttok, "LATT")) + { + //Line Attributes (ignored) + } + else if (EQUAL(firsttok, "EFLA")) + { + end = TRUE; + } + else if (EQUAL(firsttok, "ETAB")) + { + end = TRUE; + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, "Unexpected token: %s", firsttok ); + } + + CSLDestroy(tokens); + } + + //Set feature geometry + if (eType == wkbMultiCurve) + { + feature->SetGeomFieldDirectly(geomIdx, ogrMultiLine); + } + else if (eType == wkbMultiLineString) + { + feature->SetGeomFieldDirectly(geomIdx, ogrMultiLine->getLinearGeometry()); + delete ogrMultiLine; + } + else if (eType == wkbCurvePolygon) + { + feature->SetGeomFieldDirectly(geomIdx, ogrPoly); + } + else if (eType == wkbPolygon) + { + feature->SetGeomFieldDirectly(geomIdx, ogrPoly->getLinearGeometry()); + delete ogrPoly; + } + else + { + feature->SetGeomFieldDirectly(geomIdx, ogrCurve); + } +} + +/************************************************************************/ +/* AddLayer() */ +/************************************************************************/ + +void ILI1Reader::AddLayer( OGRILI1Layer * poNewLayer ) + +{ + nLayers++; + + papoLayers = (OGRILI1Layer **) + CPLRealloc( papoLayers, sizeof(void*) * nLayers ); + + papoLayers[nLayers-1] = poNewLayer; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRILI1Layer *ILI1Reader::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +OGRILI1Layer *ILI1Reader::GetLayerByName( const char* pszLayerName ) + +{ + for(int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName, + papoLayers[iLayer]->GetLayerDefn()->GetName()) ) + return papoLayers[iLayer]; + } + return NULL; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int ILI1Reader::GetLayerCount() + +{ + return nLayers; +} + +/************************************************************************/ +/* Read one logical line, and return split into fields. The return */ +/* result is a stringlist, in the sense of the CSL functions. */ +/************************************************************************/ + +char ** ILI1Reader::ReadParseLine() +{ + const char *pszLine; + char **tokens; + char **conttok; + char *token; + + CPLAssert( fpItf != NULL ); + if( fpItf == NULL ) + return( NULL ); + + pszLine = CPLReadLine( fpItf ); + if( pszLine == NULL ) + return( NULL ); + + if (strlen(pszLine) == 0) return NULL; + + tokens = CSLTokenizeString2( pszLine, " ", CSLT_PRESERVEESCAPES ); + token = tokens[CSLCount(tokens)-1]; + + //Append CONT lines + while (strlen(pszLine) && token[0] == codeContinue && token[1] == '\0') + { + //remove last token + CPLFree(tokens[CSLCount(tokens)-1]); + tokens[CSLCount(tokens)-1] = NULL; + + pszLine = CPLReadLine( fpItf ); + conttok = CSLTokenizeString2( pszLine, " ", CSLT_PRESERVEESCAPES ); + if (!conttok || !EQUAL(conttok[0], "CONT")) + { + CSLDestroy(conttok); + break; + } + + //append + tokens = CSLInsertStrings(tokens, -1, &conttok[1]); + token = tokens[CSLCount(tokens)-1]; + + CSLDestroy(conttok); + } + return tokens; +} + + + +IILI1Reader *CreateILI1Reader() { + return new ILI1Reader(); +} + +void DestroyILI1Reader(IILI1Reader* reader) +{ + if (reader) + delete reader; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili1reader.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili1reader.h new file mode 100644 index 000000000..aa47acadb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili1reader.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * $Id: ili1reader.h 29093 2015-05-01 18:50:08Z pka $ + * + * Project: Interlis 1 Reader + * Purpose: Private Declarations for Reader code. + * Author: Pirmin Kalberer, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_ILI1READER_H_INCLUDED +#define _CPL_ILI1READER_H_INCLUDED + +#include "imdreader.h" + +class OGRILI1DataSource; + +class CPL_DLL IILI1Reader +{ +public: + virtual ~IILI1Reader(); + + virtual int OpenFile( const char *pszFilename ) = 0; + + virtual int ReadModel( ImdReader *poImdReader, const char *pszModelFilename, OGRILI1DataSource *poDS ) = 0; + virtual int ReadFeatures() = 0; + + virtual OGRLayer *GetLayer( int ) = 0; + virtual OGRLayer *GetLayerByName( const char* ) = 0; + virtual int GetLayerCount() = 0; +}; + +IILI1Reader *CreateILI1Reader(); +void DestroyILI1Reader(IILI1Reader* reader); + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili1readerp.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili1readerp.h new file mode 100644 index 000000000..eb1a69ba5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili1readerp.h @@ -0,0 +1,75 @@ +/****************************************************************************** + * $Id: ili1readerp.h 29093 2015-05-01 18:50:08Z pka $ + * + * Project: Interlis 1 Reader + * Purpose: Private Declarations for Reader code. + * Author: Pirmin Kalberer, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_ILI1READERP_H_INCLUDED +#define _CPL_ILI1READERP_H_INCLUDED + +#include "ili1reader.h" +#include "ogr_ili1.h" + + +class ILI1Reader; +class OGRILI1Layer; + +/************************************************************************/ +/* ILI1Reader */ +/************************************************************************/ + +class ILI1Reader : public IILI1Reader +{ +private: + FILE *fpItf; + int nLayers; + OGRILI1Layer **papoLayers; + OGRILI1Layer *curLayer; + char codeBlank; + char codeUndefined; + char codeContinue; + +public: + ILI1Reader(); + ~ILI1Reader(); + + int OpenFile( const char *pszFilename ); + int ReadModel( ImdReader *poImdReader, const char *pszModelFilename, OGRILI1DataSource *poDS ); + int ReadFeatures(); + int ReadTable(const char *layername); + void ReadGeom(char **stgeom, int geomIdx, OGRwkbGeometryType eType, OGRFeature *feature); + char **ReadParseLine(); + + void AddLayer( OGRILI1Layer * poNewLayer ); + OGRILI1Layer *GetLayer( int ); + OGRILI1Layer *GetLayerByName( const char* ); + int GetLayerCount(); + + const char* GetLayerNameString(const char* topicname, const char* tablename); +}; + + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili2handler.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili2handler.cpp new file mode 100644 index 000000000..d48dd6273 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili2handler.cpp @@ -0,0 +1,195 @@ +/****************************************************************************** + * $Id: ili2handler.cpp 27794 2014-10-04 10:13:46Z rouault $ + * + * Project: Interlis 2 Reader + * Purpose: Implementation of ILI2Handler class. + * Author: Markus Schnider, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ili2.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +#include "ili2readerp.h" +#include + +CPL_CVSID("$Id: ili2handler.cpp 27794 2014-10-04 10:13:46Z rouault $"); + +// +// constants +// +static const char* ILI2_DATASECTION = "DATASECTION"; + +// +// ILI2Handler +// +ILI2Handler::ILI2Handler( ILI2Reader *poReader ) { + m_poReader = poReader; + + XMLCh *tmpCh = XMLString::transcode("CORE"); + DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(tmpCh); + XMLString::release(&tmpCh); + + // the root element + tmpCh = XMLString::transcode("ROOT"); + dom_doc = impl->createDocument(0,tmpCh,0); + XMLString::release(&tmpCh); + + // the first element is root + dom_elem = dom_doc->getDocumentElement(); + +} + +ILI2Handler::~ILI2Handler() { + + // remove all elements + DOMNode *tmpNode = dom_doc->getFirstChild(); + while (tmpNode != NULL) { + tmpNode = dom_doc->removeChild(tmpNode); + tmpNode = dom_doc->getFirstChild(); + } + + // release the dom tree + dom_doc->release(); + +} + + +void ILI2Handler::startDocument() { + // the level counter starts with DATASECTION + level = -1; + m_nEntityCounter = 0; +} + +void ILI2Handler::endDocument() { + // nothing to do +} + +void ILI2Handler::startElement( + CPL_UNUSED const XMLCh* const uri, + CPL_UNUSED const XMLCh* const localname, + const XMLCh* const qname, + const Attributes& attrs + ) { + // start to add the layers, features with the DATASECTION + char *tmpC = NULL; + m_nEntityCounter = 0; + if ((level >= 0) || (cmpStr(ILI2_DATASECTION, tmpC = XMLString::transcode(qname)) == 0)) { + level++; + + if (level >= 2) { + + // create the dom tree + DOMElement *elem = (DOMElement*)dom_doc->createElement(qname); + + // add all attributes + unsigned int len = attrs.getLength(); + for (unsigned int index = 0; index < len; index++) + elem->setAttribute(attrs.getQName(index), attrs.getValue(index)); + dom_elem->appendChild(elem); + dom_elem = elem; + } + } + XMLString::release(&tmpC); +} + +void ILI2Handler::endElement( + CPL_UNUSED const XMLCh* const uri, + CPL_UNUSED const XMLCh* const localname, + CPL_UNUSED const XMLCh* const qname + ) { + m_nEntityCounter = 0; + if (level >= 0) { + if (level == 2) { + + // go to the parent element and parse the child element + DOMElement* childElem = dom_elem; + dom_elem = (DOMElement*)dom_elem->getParentNode(); + + m_poReader->AddFeature(childElem); + + // remove the child element + childElem = (DOMElement*)dom_elem->removeChild(childElem); + } else if (level >= 3) { + + // go to the parent element + dom_elem = (DOMElement*)dom_elem->getParentNode(); + } + level--; + } +} + +#if XERCES_VERSION_MAJOR >= 3 +/************************************************************************/ +/* characters() (xerces 3 version) */ +/************************************************************************/ + +void ILI2Handler::characters( const XMLCh *const chars, + CPL_UNUSED const XMLSize_t length ) { + // add the text element + if (level >= 3) { + char *tmpC = XMLString::transcode(chars); + + // only add the text if it is not empty + if (trim(tmpC) != "") + dom_elem->appendChild(dom_doc->createTextNode(chars)); + + XMLString::release(&tmpC); + } +} + +#else +/************************************************************************/ +/* characters() (xerces 2 version) */ +/************************************************************************/ + +void ILI2Handler::characters( const XMLCh *const chars, + CPL_UNUSED const unsigned int length ) { + + // add the text element + if (level >= 3) { + char *tmpC = XMLString::transcode(chars); + + // only add the text if it is not empty + if (trim(tmpC) != "") + dom_elem->appendChild(dom_doc->createTextNode(chars)); + + XMLString::release(&tmpC); + } +} +#endif + +void ILI2Handler::startEntity (CPL_UNUSED const XMLCh *const name) +{ + m_nEntityCounter++; + if (m_nEntityCounter > 1000) + { + throw SAXNotSupportedException ("File probably corrupted (million laugh pattern)"); + } +} + +void ILI2Handler::fatalError(const SAXParseException&) { + // FIXME Error handling +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili2reader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili2reader.cpp new file mode 100644 index 000000000..36fb7afeb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili2reader.cpp @@ -0,0 +1,710 @@ +/****************************************************************************** + * $Id: ili2reader.cpp 29140 2015-05-03 20:09:32Z pka $ + * + * Project: Interlis 2 Reader + * Purpose: Implementation of ILI2Reader class. + * Author: Markus Schnider, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ili2.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +#include "ili2reader.h" +#include "ili2readerp.h" + +//from trstring.cpp/gmlreaderp.h +char *tr_strdup( const XMLCh * ); + +using namespace std; + +CPL_CVSID("$Id: ili2reader.cpp 29140 2015-05-03 20:09:32Z pka $"); + +// +// constants +// +static const char *ILI2_TID = "TID"; +static const char *ILI2_REF = "REF"; + +static const int ILI2_STRING_TYPE = 0; +static const int ILI2_COORD_TYPE = 1; +static const int ILI2_ARC_TYPE = 2; +static const int ILI2_POLYLINE_TYPE = 4; +static const int ILI2_BOUNDARY_TYPE = 8; +static const int ILI2_AREA_TYPE = 16; // also SURFACE +static const int ILI2_GEOMCOLL_TYPE = 32; + +static const char *ILI2_COORD = "COORD"; +static const char *ILI2_ARC = "ARC"; +static const char *ILI2_POLYLINE = "POLYLINE"; +static const char *ILI2_BOUNDARY = "BOUNDARY"; +static const char *ILI2_AREA = "AREA"; +static const char *ILI2_SURFACE = "SURFACE"; + + +// +// helper functions +// +int cmpStr(string s1, string s2) { + + string::const_iterator p1 = s1.begin(); + string::const_iterator p2 = s2.begin(); + + while (p1 != s1.end() && p2 != s2.end()) { + if (toupper(*p1) != toupper(*p2)) + return (toupper(*p1) < toupper(*p2)) ? -1 : 1; + ++p1; + ++p2; + } + + return (s2.size() == s1.size()) ? 0 : + (s1.size() < s2.size()) ? -1 : 1; +} + +string ltrim(string tmpstr) { + unsigned int i = 0; + while (i < tmpstr.length() && (tmpstr[i] == ' ' || tmpstr[i] == '\t' || tmpstr[i] == '\r' || tmpstr[i] == '\n')) ++i; + return i > 0 ? tmpstr.substr(i, tmpstr.length()-i) : tmpstr; +} + +string rtrim(string tmpstr) { + if (tmpstr.length() == 0) return tmpstr; + unsigned int i = tmpstr.length() - 1; + while (tmpstr[i] == ' ' || tmpstr[i] == '\t' || tmpstr[i] == '\r' || tmpstr[i] == '\n') --i; + return i < tmpstr.length() - 1 ? tmpstr.substr(0, i+1) : tmpstr; +} + +string trim(string tmpstr) { + tmpstr = ltrim(tmpstr); + tmpstr = rtrim(tmpstr); + return tmpstr; +} + +int getGeometryTypeOfElem(DOMElement* elem) { + int type = ILI2_STRING_TYPE; + char* pszTagName = XMLString::transcode(elem->getTagName()); + + if (elem && elem->getNodeType() == DOMNode::ELEMENT_NODE) { + if (cmpStr(ILI2_COORD, pszTagName) == 0) { + type = ILI2_COORD_TYPE; + } else if (cmpStr(ILI2_ARC, pszTagName) == 0) { + type = ILI2_ARC_TYPE; + } else if (cmpStr(ILI2_POLYLINE, pszTagName) == 0) { + type = ILI2_POLYLINE_TYPE; + } else if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0) { + type = ILI2_BOUNDARY_TYPE; + } else if (cmpStr(ILI2_AREA, pszTagName) == 0) { + type = ILI2_AREA_TYPE; + } else if (cmpStr(ILI2_SURFACE, pszTagName) == 0) { + type = ILI2_AREA_TYPE; + } + } + XMLString::release(&pszTagName); + return type; +} + +char *getObjValue(DOMElement *elem) { + DOMElement *textElem = (DOMElement *)elem->getFirstChild(); + + if ((textElem != NULL) && (textElem->getNodeType() == DOMNode::TEXT_NODE)) + { + char* pszNodeValue = tr_strdup(textElem->getNodeValue()); + return pszNodeValue; + } + + return NULL; +} + +char *getREFValue(DOMElement *elem) { + XMLCh* pszIli2_ref = XMLString::transcode(ILI2_REF); + char* pszREFValue = tr_strdup(elem->getAttribute(pszIli2_ref)); + XMLString::release(&pszIli2_ref); + return pszREFValue; +} + +OGRPoint *getPoint(DOMElement *elem) { + // elem -> COORD (or ARC) + OGRPoint *pt = new OGRPoint(); + + DOMElement *coordElem = (DOMElement *)elem->getFirstChild(); + while (coordElem != NULL) { + char* pszTagName = XMLString::transcode(coordElem->getTagName()); + char* pszObjValue = getObjValue(coordElem); + if (cmpStr("C1", pszTagName) == 0) + pt->setX(CPLAtof(pszObjValue)); + else if (cmpStr("C2", pszTagName) == 0) + pt->setY(CPLAtof(pszObjValue)); + else if (cmpStr("C3", pszTagName) == 0) + pt->setZ(CPLAtof(pszObjValue)); + CPLFree(pszObjValue); + XMLString::release(&pszTagName); + coordElem = (DOMElement *)coordElem->getNextSibling(); + } + pt->flattenTo2D(); + return pt; +} + +OGRCircularString *ILI2Reader::getArc(DOMElement *elem) { + // elem -> ARC + OGRCircularString *arc = new OGRCircularString(); + // previous point -> start point + OGRPoint *ptStart = getPoint((DOMElement *)elem->getPreviousSibling()); // COORD or ARC + // end point + OGRPoint *ptEnd = new OGRPoint(); + // point on the arc + OGRPoint *ptOnArc = new OGRPoint(); + // double radius = 0; // radius + + DOMElement *arcElem = (DOMElement *)elem->getFirstChild(); + while (arcElem != NULL) { + char* pszTagName = XMLString::transcode(arcElem->getTagName()); + char* pszObjValue = getObjValue(arcElem); + if (cmpStr("C1", pszTagName) == 0) + ptEnd->setX(CPLAtof(pszObjValue)); + else if (cmpStr("C2", pszTagName) == 0) + ptEnd->setY(CPLAtof(pszObjValue)); + else if (cmpStr("C3", pszTagName) == 0) + ptEnd->setZ(CPLAtof(pszObjValue)); + else if (cmpStr("A1", pszTagName) == 0) + ptOnArc->setX(CPLAtof(pszObjValue)); + else if (cmpStr("A2", pszTagName) == 0) + ptOnArc->setY(CPLAtof(pszObjValue)); + else if (cmpStr("A3", pszTagName) == 0) + ptOnArc->setZ(CPLAtof(pszObjValue)); + else if (cmpStr("R", pszTagName) == 0) { + // radius = CPLAtof(pszObjValue); + } + CPLFree(pszObjValue); + XMLString::release(&pszTagName); + arcElem = (DOMElement *)arcElem->getNextSibling(); + } + arc->addPoint(ptStart); + arc->addPoint(ptOnArc); + arc->addPoint(ptEnd); + delete ptStart; + delete ptOnArc; + delete ptEnd; + return arc; +} + +OGRCompoundCurve *getPolyline(DOMElement *elem) { + // elem -> POLYLINE + OGRCompoundCurve *ogrCurve = new OGRCompoundCurve(); + OGRLineString *ls = new OGRLineString(); + + DOMElement *lineElem = (DOMElement *)elem->getFirstChild(); + while (lineElem != NULL) { + char* pszTagName = XMLString::transcode(lineElem->getTagName()); + if (cmpStr(ILI2_COORD, pszTagName) == 0) + { + OGRPoint* poPoint = getPoint(lineElem); + ls->addPoint(poPoint); + delete poPoint; + } + else if (cmpStr(ILI2_ARC, pszTagName) == 0) { + //Finish line and start arc + if (ls->getNumPoints() > 1) { + ogrCurve->addCurveDirectly(ls); + ls = new OGRLineString(); + } else { + ls->empty(); + } + OGRCircularString *arc = new OGRCircularString(); + // end point + OGRPoint *ptEnd = new OGRPoint(); + // point on the arc + OGRPoint *ptOnArc = new OGRPoint(); + // radius + // double radius = 0; + + DOMElement *arcElem = (DOMElement *)lineElem->getFirstChild(); + while (arcElem != NULL) { + char* pszTagName = XMLString::transcode(arcElem->getTagName()); + char* pszObjValue = getObjValue(arcElem); + if (cmpStr("C1", pszTagName) == 0) + ptEnd->setX(CPLAtof(pszObjValue)); + else if (cmpStr("C2", pszTagName) == 0) + ptEnd->setY(CPLAtof(pszObjValue)); + else if (cmpStr("C3", pszTagName) == 0) + ptEnd->setZ(CPLAtof(pszObjValue)); + else if (cmpStr("A1", pszTagName) == 0) + ptOnArc->setX(CPLAtof(pszObjValue)); + else if (cmpStr("A2", pszTagName) == 0) + ptOnArc->setY(CPLAtof(pszObjValue)); + else if (cmpStr("A3", pszTagName) == 0) + ptOnArc->setZ(CPLAtof(pszObjValue)); + else if (cmpStr("R", pszTagName) == 0) { + // radius = CPLAtof(pszObjValue); + } + CPLFree(pszObjValue); + XMLString::release(&pszTagName); + + arcElem = (DOMElement *)arcElem->getNextSibling(); + } + + OGRPoint *ptStart = getPoint((DOMElement *)lineElem->getPreviousSibling()); // COORD or ARC + arc->addPoint(ptStart); + arc->addPoint(ptOnArc); + arc->addPoint(ptEnd); + ogrCurve->addCurveDirectly(arc); + + delete ptStart; + delete ptEnd; + delete ptOnArc; + } /* else { // TODO: StructureValue in Polyline not yet supported + } */ + XMLString::release(&pszTagName); + + lineElem = (DOMElement *)lineElem->getNextSibling(); + } + + if (ls->getNumPoints() > 1) { + ogrCurve->addCurveDirectly(ls); + } + return ogrCurve; +} + +OGRCompoundCurve *getBoundary(DOMElement *elem) { + + DOMElement *lineElem = (DOMElement *)elem->getFirstChild(); + if (lineElem != NULL) + { + char* pszTagName = XMLString::transcode(lineElem->getTagName()); + if (cmpStr(ILI2_POLYLINE, pszTagName) == 0) + { + XMLString::release(&pszTagName); + return getPolyline(lineElem); + } + XMLString::release(&pszTagName); + } + + return new OGRCompoundCurve(); +} + +OGRCurvePolygon *getPolygon(DOMElement *elem) { + OGRCurvePolygon *pg = new OGRCurvePolygon(); + + DOMElement *boundaryElem = (DOMElement *)elem->getFirstChild(); // outer boundary + while (boundaryElem != NULL) { + char* pszTagName = XMLString::transcode(boundaryElem->getTagName()); + if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0) + pg->addRingDirectly(getBoundary(boundaryElem)); + XMLString::release(&pszTagName); + boundaryElem = (DOMElement *)boundaryElem->getNextSibling(); // inner boundaries + } + + return pg; +} + +OGRGeometry *ILI2Reader::getGeometry(DOMElement *elem, int type) { + OGRGeometryCollection *gm = new OGRGeometryCollection(); + + DOMElement *childElem = elem; + while (childElem != NULL) { + char* pszTagName = XMLString::transcode(childElem->getTagName()); + switch (type) { + case ILI2_COORD_TYPE : + if (cmpStr(ILI2_COORD, pszTagName) == 0) + { + delete gm; + XMLString::release(&pszTagName); + return getPoint(childElem); + } + break; + case ILI2_ARC_TYPE : + // is it possible here? It have to be a ARC or COORD before (getPreviousSibling) + if (cmpStr(ILI2_ARC, pszTagName) == 0) + { + delete gm; + XMLString::release(&pszTagName); + return getArc(childElem); + } + break; + case ILI2_POLYLINE_TYPE : + if (cmpStr(ILI2_POLYLINE, pszTagName) == 0) + { + delete gm; + XMLString::release(&pszTagName); + return getPolyline(childElem); + } + break; + case ILI2_BOUNDARY_TYPE : + if (cmpStr(ILI2_BOUNDARY, pszTagName) == 0) + { + delete gm; + XMLString::release(&pszTagName); + return getPolyline(childElem); + } + break; + case ILI2_AREA_TYPE : + if ((cmpStr(ILI2_AREA, pszTagName) == 0) || + (cmpStr(ILI2_SURFACE, pszTagName) == 0)) + { + delete gm; + XMLString::release(&pszTagName); + return getPolygon(childElem); + } + break; + default : + if (type >= ILI2_GEOMCOLL_TYPE) { + int subType = getGeometryTypeOfElem(childElem); //???? + gm->addGeometryDirectly(getGeometry(childElem, subType)); + } + break; + } + XMLString::release(&pszTagName); + + // GEOMCOLL + childElem = (DOMElement *)childElem->getNextSibling(); + } + + return gm; +} + +int ILI2Reader::ReadModel(ImdReader *poImdReader, const char *modelFilename) { + poImdReader->ReadModel(modelFilename); + for (FeatureDefnInfos::const_iterator it = poImdReader->featureDefnInfos.begin(); it != poImdReader->featureDefnInfos.end(); ++it) + { + OGRLayer* layer = new OGRILI2Layer(it->poTableDefn, it->poGeomFieldInfos, NULL); + m_listLayer.push_back(layer); + } + return 0; +} + +//Detect field name of value element +char* fieldName(DOMElement* elem) { + DOMNode *node = elem; + if (getGeometryTypeOfElem(elem)) + { + int depth = 0; // Depth of value elem node + for (node = elem; node; node = node->getParentNode()) ++depth; + //Field name is on level 4 + node = elem; + for (int d = 0; dgetParentNode(); + } + char* pszNodeName = tr_strdup(node->getNodeName()); + return pszNodeName; +} + +void ILI2Reader::setFieldDefn(OGRFeatureDefn *featureDef, DOMElement* elem) { + int type = 0; + //recursively search children + for (DOMElement *childElem = (DOMElement *)elem->getFirstChild(); + type == 0 && childElem && childElem->getNodeType() == DOMNode::ELEMENT_NODE; + childElem = (DOMElement*)childElem->getNextSibling()) { + type = getGeometryTypeOfElem(childElem); + if (type == 0) { + if (childElem->getFirstChild() && childElem->getFirstChild()->getNodeType() == DOMNode::ELEMENT_NODE) { + setFieldDefn(featureDef, childElem); + } else { + char *fName = fieldName(childElem); + if (featureDef->GetFieldIndex(fName) == -1) { + CPLDebug( "OGR_ILI", "AddFieldDefn: %s",fName ); + OGRFieldDefn oFieldDefn(fName, OFTString); + featureDef->AddFieldDefn(&oFieldDefn); + } + CPLFree(fName); + } + } + } +} + +void ILI2Reader::SetFieldValues(OGRFeature *feature, DOMElement* elem) { + int type = 0; + //recursively search children + for (DOMElement *childElem = (DOMElement *)elem->getFirstChild(); + type == 0 && childElem && childElem->getNodeType() == DOMNode::ELEMENT_NODE; + childElem = (DOMElement*)childElem->getNextSibling()) { + type = getGeometryTypeOfElem(childElem); + if (type == 0) { + if (childElem->getFirstChild() && childElem->getFirstChild()->getNodeType() == DOMNode::ELEMENT_NODE) { + SetFieldValues(feature, childElem); + } else { + char *fName = fieldName(childElem); + int fIndex = feature->GetFieldIndex(fName); + if (fIndex != -1) { + char * objVal = getObjValue(childElem); + if (objVal == NULL) + objVal = getREFValue(childElem); // only to try + feature->SetField(fIndex, objVal); + CPLFree(objVal); + } else { + CPLDebug( "OGR_ILI","Attribute '%s' not found", fName); + m_missAttrs.push_back(fName); + } + CPLFree(fName); + } + } else { + char *fName = fieldName(childElem); + int fIndex = feature->GetGeomFieldIndex(fName); + OGRGeometry *geom = getGeometry(childElem, type); + if (fIndex == -1) { // Unkown model + feature->SetGeometryDirectly(geom); + } else { + OGRwkbGeometryType geomType = feature->GetGeomFieldDefnRef(fIndex)->GetType(); + if (geomType == wkbMultiLineString || geomType == wkbPolygon) { + feature->SetGeomFieldDirectly(fIndex, geom->getLinearGeometry()); + } else { + feature->SetGeomFieldDirectly(fIndex, geom); + } + } + CPLFree(fName); + } + } +} + + +// +// ILI2Reader +// +IILI2Reader::~IILI2Reader() { +} + +ILI2Reader::ILI2Reader() { + m_poILI2Handler = NULL; + m_poSAXReader = NULL; + m_bReadStarted = FALSE; + + m_pszFilename = NULL; + + SetupParser(); +} + +ILI2Reader::~ILI2Reader() { + CPLFree( m_pszFilename ); + + CleanupParser(); + + list::const_iterator layerIt = m_listLayer.begin(); + while (layerIt != m_listLayer.end()) { + OGRILI2Layer *tmpLayer = (OGRILI2Layer *)*layerIt; + delete tmpLayer; + layerIt++; + } +} + +void ILI2Reader::SetSourceFile( const char *pszFilename ) { + CPLFree( m_pszFilename ); + m_pszFilename = CPLStrdup( pszFilename ); +} + +int ILI2Reader::SetupParser() { + + static int bXercesInitialized = FALSE; + + if( !bXercesInitialized ) + { + try + { + XMLPlatformUtils::Initialize(); + } + + catch (const XMLException& toCatch) + { + char* msg = tr_strdup(toCatch.getMessage()); + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to initialize Xerces C++ based ILI2 reader. " + "Error message:\n%s\n", msg ); + CPLFree(msg); + + return FALSE; + } + bXercesInitialized = TRUE; + } + + // Cleanup any old parser. + if( m_poSAXReader != NULL ) + CleanupParser(); + + // Create and initialize parser. + m_poSAXReader = XMLReaderFactory::createXMLReader(); + + m_poILI2Handler = new ILI2Handler( this ); + + m_poSAXReader->setContentHandler( m_poILI2Handler ); + m_poSAXReader->setErrorHandler( m_poILI2Handler ); + m_poSAXReader->setLexicalHandler( m_poILI2Handler ); + m_poSAXReader->setEntityResolver( m_poILI2Handler ); + m_poSAXReader->setDTDHandler( m_poILI2Handler ); + +/* No Validation +#if (OGR_ILI2_VALIDATION) + m_poSAXReader->setFeature( + XMLString::transcode("http://xml.org/sax/features/validation"), true); + m_poSAXReader->setFeature( + XMLString::transcode("http://xml.org/sax/features/namespaces"), true); + + m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, true ); + m_poSAXReader->setFeature( XMLUni::fgXercesSchema, true ); + +// m_poSAXReader->setDoSchema(true); +// m_poSAXReader->setValidationSchemaFullChecking(true); +#else +*/ + XMLCh *tmpCh = XMLString::transcode("http://xml.org/sax/features/validation"); + m_poSAXReader->setFeature(tmpCh, false); + XMLString::release(&tmpCh); + tmpCh = XMLString::transcode("http://xml.org/sax/features/namespaces"); + m_poSAXReader->setFeature(tmpCh, false); + XMLString::release(&tmpCh); +//#endif + + m_bReadStarted = FALSE; + + return TRUE; +} + +void ILI2Reader::CleanupParser() { + if( m_poSAXReader == NULL ) + return; + + delete m_poSAXReader; + m_poSAXReader = NULL; + + delete m_poILI2Handler; + m_poILI2Handler = NULL; + + m_bReadStarted = FALSE; +} + +int ILI2Reader::SaveClasses( const char *pszFile = NULL ) { + + // Add logic later to determine reasonable default schema file. + if( pszFile == NULL ) + return FALSE; + + // parse and create layers and features + try + { + CPLDebug( "OGR_ILI", "Parsing %s", pszFile); + m_poSAXReader->parse(pszFile); + } + catch (const SAXException& toCatch) + { + char* msg = tr_strdup(toCatch.getMessage()); + CPLError( CE_Failure, CPLE_AppDefined, + "Parsing failed: %s\n", msg ); + CPLFree(msg); + + return FALSE; + } + + if (m_missAttrs.size() != 0) { + m_missAttrs.sort(); + m_missAttrs.unique(); + string attrs = ""; + list::const_iterator it; + for (it = m_missAttrs.begin(); it != m_missAttrs.end(); ++it) + attrs += *it + ", "; + + CPLError( CE_Warning, CPLE_NotSupported, + "Failed to add new definition to existing layers, attributes not saved: %s", attrs.c_str() ); + } + + return TRUE; +} + +list ILI2Reader::GetLayers() { + return m_listLayer; +} + +int ILI2Reader::GetLayerCount() { + return m_listLayer.size(); +} + +OGRLayer* ILI2Reader::GetLayer(const char* pszName) { + for (list::reverse_iterator layerIt = m_listLayer.rbegin(); + layerIt != m_listLayer.rend(); + ++layerIt) { + OGRFeatureDefn *fDef = (*layerIt)->GetLayerDefn(); + if (cmpStr(fDef->GetName(), pszName) == 0) { + return *layerIt; + } + } + return NULL; +} + +int ILI2Reader::AddFeature(DOMElement *elem) { + bool newLayer = true; + OGRLayer *curLayer = 0; + char *pszName = tr_strdup(elem->getTagName()); + //CPLDebug( "OGR_ILI", "Reading layer: %s", pszName ); + + // test if this layer exist + curLayer = GetLayer(pszName); + newLayer = (curLayer == NULL); + + // add a layer + if (newLayer) { + CPLDebug( "OGR_ILI", "Adding layer: %s", pszName ); + OGRFeatureDefn* poFeatureDefn = new OGRFeatureDefn(pszName); + poFeatureDefn->SetGeomType( wkbUnknown ); + GeomFieldInfos oGeomFieldInfos; + curLayer = new OGRILI2Layer(poFeatureDefn, oGeomFieldInfos, NULL); + m_listLayer.push_back(curLayer); + } + + // the feature and field definition + OGRFeatureDefn *featureDef = curLayer->GetLayerDefn(); + if (newLayer) { + // add TID field + OGRFieldDefn ofieldDefn (ILI2_TID, OFTString); + featureDef->AddFieldDefn(&ofieldDefn); + + setFieldDefn(featureDef, elem); + } + + // add the features + OGRFeature *feature = new OGRFeature(featureDef); + + // assign TID + int fIndex = feature->GetFieldIndex(ILI2_TID); + if (fIndex != -1) { + XMLCh *pszIli2_tid = XMLString::transcode(ILI2_TID); + char *fChVal = tr_strdup(elem->getAttribute(pszIli2_tid)); + feature->SetField(fIndex, fChVal); + XMLString::release(&pszIli2_tid); + CPLFree(fChVal); + } else { + CPLDebug( "OGR_ILI","'%s' not found", ILI2_TID); + } + + SetFieldValues(feature, elem); + curLayer->SetFeature(feature); + + CPLFree(pszName); + + return 0; +} + +IILI2Reader *CreateILI2Reader() { + return new ILI2Reader(); +} + +void DestroyILI2Reader(IILI2Reader* reader) +{ + if (reader) + delete reader; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili2reader.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili2reader.h new file mode 100644 index 000000000..91c903436 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili2reader.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * $Id: ili2reader.h 29140 2015-05-03 20:09:32Z pka $ + * + * Project: Interlis 2 Reader + * Purpose: Public Declarations for Reader code. + * Author: Markus Schnider, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_ILI2READER_H_INCLUDED +#define _CPL_ILI2READER_H_INCLUDED + +// This works around problems with math.h on some platforms #defining INFINITY +#ifdef INFINITY +#undef INFINITY +#define INFINITY INFINITY_XERCES +#endif + +#include "imdreader.h" +#include + + +class CPL_DLL IILI2Reader +{ +public: + virtual ~IILI2Reader(); + + virtual void SetSourceFile( const char *pszFilename ) = 0; + + virtual int ReadModel( ImdReader *poImdReader, const char *modelFilename ) = 0; + virtual int SaveClasses( const char *pszFilename ) = 0; + + virtual std::list GetLayers() = 0; + virtual int GetLayerCount() = 0; +}; + +IILI2Reader *CreateILI2Reader(); +void DestroyILI2Reader(IILI2Reader* reader); + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili2readerp.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili2readerp.h new file mode 100644 index 000000000..f04e22b93 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ili2readerp.h @@ -0,0 +1,158 @@ +/****************************************************************************** + * $Id: ili2readerp.h 29140 2015-05-03 20:09:32Z pka $ + * + * Project: Interlis 2 Reader + * Purpose: Private Declarations for Reader code. + * Author: Markus Schnider, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_ILI2READERP_H_INCLUDED +#define _CPL_ILI2READERP_H_INCLUDED + +// This works around problems with math.h on some platforms #defining INFINITY +#ifdef INFINITY +#undef INFINITY +#define INFINITY INFINITY_XERCES +#endif + +#include "ili2reader.h" +#include "ogr_ili2.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if _XERCES_VERSION >= 30000 +# include +#endif + +#ifdef XERCES_CPP_NAMESPACE_USE +XERCES_CPP_NAMESPACE_USE +#endif + +int cmpStr(std::string s1, std::string s2); + +std::string ltrim(std::string tmpstr); +std::string rtrim(std::string tmpstr); +std::string trim(std::string tmpstr); + + +class ILI2Reader; + +/************************************************************************/ +/* ILI2Handler */ +/************************************************************************/ +class ILI2Handler : public DefaultHandler +{ + ILI2Reader *m_poReader; + + int level; + + DOMDocument *dom_doc; + DOMElement *dom_elem; + + int m_nEntityCounter; + +public: + ILI2Handler( ILI2Reader *poReader ); + ~ILI2Handler(); + + void startDocument(); + void endDocument(); + + void startElement( + const XMLCh* const uri, + const XMLCh* const localname, + const XMLCh* const qname, + const Attributes& attrs + ); + void endElement( + const XMLCh* const uri, + const XMLCh* const localname, + const XMLCh* const qname + ); +#if XERCES_VERSION_MAJOR >= 3 + void characters( const XMLCh *const chars, + const XMLSize_t length ); // xerces 3 +#else + void characters( const XMLCh *const chars, + const unsigned int length ); // xerces 2 +#endif + + void startEntity (const XMLCh *const name); + + void fatalError(const SAXParseException&); +}; + + +/************************************************************************/ +/* ILI2Reader */ +/************************************************************************/ + +class ILI2Reader : public IILI2Reader +{ +private: + int SetupParser(); + void CleanupParser(); + + char *m_pszFilename; + + std::list m_missAttrs; + + ILI2Handler *m_poILI2Handler; + SAX2XMLReader *m_poSAXReader; + int m_bReadStarted; + double arcIncr; + + std::list m_listLayer; + +public: + ILI2Reader(); + ~ILI2Reader(); + + void SetSourceFile( const char *pszFilename ); + int ReadModel( ImdReader *poImdReader, const char *modelFilename ); + int SaveClasses( const char *pszFile ); + + std::list GetLayers(); + int GetLayerCount(); + OGRLayer* GetLayer(const char* pszName); + + int AddFeature(DOMElement *elem); + void SetFieldValues(OGRFeature *feature, DOMElement* elem); + const char* GetLayerName(/*IOM_BASKET model, IOM_OBJECT table*/); + void AddField(OGRLayer* layer/*, IOM_BASKET model, IOM_OBJECT obj*/); + OGRCircularString *getArc(DOMElement *elem); + OGRGeometry *getGeometry(DOMElement *elem, int type); + void setFieldDefn(OGRFeatureDefn *featureDef, DOMElement* elem); +}; + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/imdreader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/imdreader.cpp new file mode 100644 index 000000000..2ece5b04f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/imdreader.cpp @@ -0,0 +1,494 @@ +/****************************************************************************** + * $Id$ + * + * Project: Interlis 1/2 Translator + * Purpose: IlisMeta model reader. + * Author: Pirmin Kalberer, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2014, Pirmin Kalberer, Sourcepole AG + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +// IlisMeta model: http://www.interlis.ch/models/core/IlisMeta07-20111222.ili + + +#include "imdreader.h" +#include "cpl_minixml.h" +#include +#include +#include + + +CPL_CVSID("$Id$"); + + +typedef std::map StrNodeMap; +typedef std::vector NodeVector; +typedef std::map NodeCountMap; +class IliClass; +typedef std::map ClassesMap; /* all classes with XML node for lookup */ + +/* Helper class for collection class infos */ +class IliClass +{ +public: + CPLXMLNode* node; + int iliVersion; + OGRFeatureDefn* poTableDefn; + StrNodeMap& oTidLookup; + ClassesMap& oClasses; + NodeCountMap& oAxisCount; + GeomFieldInfos poGeomFieldInfos; + StructFieldInfos poStructFieldInfos; + NodeVector oFields; + bool isAssocClass; + bool hasDerivedClasses; + + IliClass(CPLXMLNode* node_, int iliVersion_, StrNodeMap& oTidLookup_, ClassesMap& oClasses_, NodeCountMap& oAxisCount_) : + node(node_), iliVersion(iliVersion_), oTidLookup(oTidLookup_), oClasses(oClasses_), oAxisCount(oAxisCount_), + poGeomFieldInfos(), poStructFieldInfos(), oFields(), isAssocClass(false), hasDerivedClasses(false) + { + char* layerName = LayerName(); + poTableDefn = new OGRFeatureDefn(layerName); + CPLFree(layerName); + }; + ~IliClass() + { + delete poTableDefn; + }; + const char* GetName() { + return poTableDefn->GetName(); + } + const char* GetIliName() { + return CPLGetXMLValue( node, "TID", NULL ); + } + char* LayerName() { + const char* psClassTID = GetIliName(); + if (iliVersion == 1) + { + //Skip topic and replace . with __ + char **papszTokens = + CSLTokenizeString2( psClassTID, ".", CSLT_ALLOWEMPTYTOKENS ); + + CPLString layername; + for(int i = 1; papszTokens != NULL && papszTokens[i] != NULL; i++) + { + if (i>1) layername += "__"; + layername += papszTokens[i]; + } + CSLDestroy( papszTokens ); + return CPLStrdup(layername); + } else { + return CPLStrdup(psClassTID); + } + }; + void AddFieldNode(CPLXMLNode* node, int iOrderPos) + { + if (iOrderPos >= (int)oFields.size()) + oFields.resize(iOrderPos+1); + //CPLDebug( "OGR_ILI", "Register field with OrderPos %d to Class %s", iOrderPos, GetName()); + oFields[iOrderPos] = node; + } + void AddRoleNode(CPLXMLNode* node, int iOrderPos) + { + isAssocClass = true; + AddFieldNode(node, iOrderPos); + } + bool isEmbedded() + { + if (isAssocClass) + for (NodeVector::const_iterator it = oFields.begin(); it != oFields.end(); ++it) + { + if (*it == NULL) continue; + if (CSLTestBoolean(CPLGetXMLValue( *it, "EmbeddedTransfer", "FALSE" ))) + return true; + } + return false; + } + // Add additional Geometry table for Interlis 1 + void AddGeomTable(CPLString layerName, const char* psFieldName, OGRwkbGeometryType eType, bool bRefTIDField = false) + { + OGRFeatureDefn* poGeomTableDefn = new OGRFeatureDefn(layerName); + OGRFieldDefn fieldDef("_TID", OFTString); + poGeomTableDefn->AddFieldDefn(&fieldDef); + if (bRefTIDField) + { + OGRFieldDefn fieldDefRef("_RefTID", OFTString); + poGeomTableDefn->AddFieldDefn(&fieldDefRef); + } + poGeomTableDefn->DeleteGeomFieldDefn(0); + OGRGeomFieldDefn fieldDefGeom(psFieldName, eType); + poGeomTableDefn->AddGeomFieldDefn(&fieldDefGeom); + CPLDebug( "OGR_ILI", "Adding geometry table %s for field %s", poGeomTableDefn->GetName(), psFieldName); + poGeomFieldInfos[psFieldName].geomTable = poGeomTableDefn; + } + void AddField(const char* psName, OGRFieldType fieldType) + { + OGRFieldDefn fieldDef(psName, fieldType); + poTableDefn->AddFieldDefn(&fieldDef); + CPLDebug( "OGR_ILI", "Adding field '%s' to Class %s", psName, GetName()); + } + void AddGeomField(const char* psName, OGRwkbGeometryType geomType) + { + OGRGeomFieldDefn fieldDef(psName, geomType); + //oGFld.SetSpatialRef(geomlayer->GetSpatialRef()); + poTableDefn->AddGeomFieldDefn(&fieldDef); + CPLDebug( "OGR_ILI", "Adding geometry field '%s' to Class %s", psName, GetName()); + } + void AddCoord(const char* psName, CPLXMLNode* psTypeNode) + { + int dim = oAxisCount[psTypeNode]; + if (dim == 0) dim = 2; //Area center points have no Axis spec + if (iliVersion == 1) + { + for (int i=0; i 2) ? wkbPoint25D : wkbPoint; + AddGeomField(psName, geomType); + } + OGRFieldType GetFormattedType(CPLXMLNode* node) + { + const char* psRefSuper = CPLGetXMLValue( node, "Super.REF", NULL ); + if (psRefSuper) + return GetFormattedType(oTidLookup[psRefSuper]); + else + return OFTString; //TODO: Time, Date, etc. if possible + } + void InitFieldDefinitions() + { + // Delete default geometry field + poTableDefn->DeleteGeomFieldDefn(0); + + const char* psKind = CPLGetXMLValue( node, "Kind", NULL ); + //CPLDebug( "OGR_ILI", "InitFieldDefinitions of '%s' kind: %s", GetName(), psKind); + if (EQUAL(psKind, "Structure")) + { + // add foreign_key field + OGRFieldDefn ofieldDefn1("REF_NAME", OFTString); + poTableDefn->AddFieldDefn(&ofieldDefn1); + OGRFieldDefn ofieldDefn2("REF_ID", OFTString); + poTableDefn->AddFieldDefn(&ofieldDefn2); + } else { // Class + // add TID field + const char* psTidColName = (iliVersion == 1) ? "_TID" : "TID"; + OGRFieldDefn ofieldDefn(psTidColName, OFTString); + poTableDefn->AddFieldDefn(&ofieldDefn); + } + if (CSLTestBoolean(CPLGetXMLValue( node, "Abstract", "FALSE" ))) + hasDerivedClasses = true; + } + void AddFieldDefinitions(NodeVector oArcLineTypes) + { + for (NodeVector::const_iterator it = oFields.begin(); it != oFields.end(); ++it) + { + if (*it == NULL) continue; + const char* psName = CPLGetXMLValue( *it, "Name", NULL ); + const char* psTypeRef = CPLGetXMLValue( *it, "Type.REF", NULL ); + if (psTypeRef == NULL) //Assoc Role + AddField(psName, OFTString); //FIXME: numeric? + else + { + CPLXMLNode* psElementNode = oTidLookup[psTypeRef]; + const char* typeName = psElementNode->pszValue; + if (EQUAL(typeName, "IlisMeta07.ModelData.TextType")) + { //Kind Text,MText + AddField(psName, OFTString); + } + else if (EQUAL(typeName, "IlisMeta07.ModelData.EnumType")) + { + AddField(psName, (iliVersion == 1) ? OFTInteger : OFTString); + } + else if (EQUAL(typeName, "IlisMeta07.ModelData.BooleanType")) + { + AddField(psName, OFTString); //?? + } + else if (EQUAL(typeName, "IlisMeta07.ModelData.NumType")) + { //// Unit INTERLIS.ANYUNIT, INTERLIS.TIME, INTERLIS.h, INTERLIS.min, INTERLIS.s, INTERLIS.M, INTERLIS.d + AddField(psName, OFTReal); + } + else if (EQUAL(typeName, "IlisMeta07.ModelData.BlackboxType")) + { + AddField(psName, OFTString); + } + else if (EQUAL(typeName, "IlisMeta07.ModelData.FormattedType")) + { + AddField(psName, GetFormattedType(*it)); + } + else if (EQUAL(typeName, "IlisMeta07.ModelData.MultiValue")) + { + //min -> Multiplicity/IlisMeta07.ModelData.Multiplicity/Min + //max -> Multiplicity/IlisMeta07.ModelData.Multiplicity/Max + const char* psClassRef = CPLGetXMLValue( psElementNode, "BaseType.REF", NULL ); + if (psClassRef) + { + IliClass* psParentClass = oClasses[oTidLookup[psClassRef]]; + poStructFieldInfos[psName] = psParentClass->GetName(); + CPLDebug( "OGR_ILI", "Register table %s for struct field '%s'", poStructFieldInfos[psName].c_str(), psName); + /* Option: Embed fields if max == 1 + CPLDebug( "OGR_ILI", "Adding embedded struct members of MultiValue field '%s' from Class %s", psName, psClassRef); + AddFieldDefinitions(psParentClass->oFields); + */ + } + } + else if (EQUAL(typeName, "IlisMeta07.ModelData.CoordType")) + { + AddCoord(psName, psElementNode); + } + else if (EQUAL(typeName, "IlisMeta07.ModelData.LineType")) + { + const char* psKind = CPLGetXMLValue( psElementNode, "Kind", NULL ); + poGeomFieldInfos[psName].iliGeomType = psKind; + bool isLinearType = (std::find(oArcLineTypes.begin(), oArcLineTypes.end(), psElementNode) == oArcLineTypes.end()); + bool linearGeom = isLinearType || CSLTestBoolean(CPLGetConfigOption("OGR_STROKE_CURVE", "FALSE")); + OGRwkbGeometryType multiLineType = linearGeom ? wkbMultiLineString : wkbMultiCurve; + OGRwkbGeometryType polyType = linearGeom ? wkbPolygon : wkbCurvePolygon; + if (iliVersion == 1) + { + if (EQUAL(psKind, "Area")) + { + CPLString lineLayerName = GetName() + CPLString("_") + psName; + AddGeomTable(lineLayerName, psName, multiLineType); + + //Add geometry field for polygonized areas + AddGeomField(psName, wkbPolygon); + + //We add the area helper point geometry after polygon + //for better behaviour of clients with limited multi geometry support + CPLString areaPointGeomName = psName + CPLString("__Point"); + AddCoord(areaPointGeomName, psElementNode); + } else if (EQUAL(psKind, "Surface")) + { + CPLString geomLayerName = GetName() + CPLString("_") + psName; + AddGeomTable(geomLayerName, psName, multiLineType, true); + AddGeomField(psName, polyType); + } else { // Polyline, DirectedPolyline + AddGeomField(psName, multiLineType); + } + } else { + if (EQUAL(psKind, "Area") || EQUAL(psKind, "Surface")) + { + AddGeomField(psName, polyType); + } else { // Polyline, DirectedPolyline + AddGeomField(psName, multiLineType); + } + } + } + else + { + //ClassRefType + CPLError(CE_Warning, CPLE_NotSupported, + "Field '%s' of class %s has unsupported type %s", psName, GetName(), typeName); + } + } + } + } + FeatureDefnInfo tableDefs() + { + FeatureDefnInfo poLayerInfo; + poLayerInfo.poTableDefn = NULL; + if (!hasDerivedClasses && !isEmbedded()) + { + poLayerInfo.poTableDefn = poTableDefn; + poLayerInfo.poGeomFieldInfos = poGeomFieldInfos; + } + return poLayerInfo; + } + +}; + + +ImdReader::ImdReader(int iliVersionIn) : iliVersion(iliVersionIn), modelInfos() { + mainModelName = "OGR"; + mainTopicName = "OGR"; + codeBlank = '_'; + codeUndefined = '@'; + codeContinue = '\\'; +} + +ImdReader::~ImdReader() { +} + +void ImdReader::ReadModel(const char *pszFilename) { + CPLDebug( "OGR_ILI", "Reading model '%s'", pszFilename); + + CPLXMLNode* psRootNode = CPLParseXMLFile(pszFilename); + if( psRootNode == NULL ) + return; + CPLXMLNode *psSectionNode = CPLGetXMLNode( psRootNode, "=TRANSFER.DATASECTION" ); + if( psSectionNode == NULL ) + return; + + StrNodeMap oTidLookup; /* for fast lookup of REF relations */ + ClassesMap oClasses; + NodeCountMap oAxisCount; + NodeVector oArcLineTypes; + const char *modelName; + + /* Fill TID lookup map and IliClasses lookup map */ + CPLXMLNode* psModel = psSectionNode->psChild; + while( psModel != NULL ) + { + modelName = CPLGetXMLValue( psModel, "BID", NULL ); + //CPLDebug( "OGR_ILI", "Model: '%s'", modelName); + + CPLXMLNode* psEntry = psModel->psChild; + while( psEntry != NULL ) + { + if (psEntry->eType != CXT_Attribute) //ignore BID + { + //CPLDebug( "OGR_ILI", "Node tag: '%s'", psEntry->pszValue); + const char* psTID = CPLGetXMLValue( psEntry, "TID", NULL ); + if( psTID != NULL ) + oTidLookup[psTID] = psEntry; + + + if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.Model") && !EQUAL(modelName, "MODEL.INTERLIS")) + { + IliModelInfo modelInfo; + modelInfo.name = CPLGetXMLValue( psEntry, "Name", "OGR" ); + modelInfo.version = CPLGetXMLValue( psEntry, "Version", "" ); + modelInfo.uri = CPLGetXMLValue( psEntry, "At", "" ); + modelInfos.push_back(modelInfo); + mainModelName = modelInfo.name; //FIXME: check model inheritance + //version = CPLGetXMLValue(psEntry, "iliVersion", "0"); //1 or 2.3 + + CPLXMLNode *psFormatNode = CPLGetXMLNode( psEntry, "ili1Format" ); + if (psFormatNode != NULL) + { + psFormatNode = psFormatNode->psChild; + codeBlank = atoi(CPLGetXMLValue(psFormatNode, "blankCode", "95")); + codeUndefined = atoi(CPLGetXMLValue(psFormatNode, "undefinedCode", "64")); + codeContinue = atoi(CPLGetXMLValue(psFormatNode, "continueCode", "92")); + } + } + else if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.SubModel")) + { + mainBasketName = CPLGetXMLValue(psEntry, "TID", "OGR"); + mainTopicName = CPLGetXMLValue(psEntry, "Name", "OGR"); + } + else if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.Class") ) + { + CPLDebug( "OGR_ILI", "Class name: '%s'", psTID); + oClasses[psEntry] = new IliClass(psEntry, iliVersion, oTidLookup, oClasses, oAxisCount); + } + } + psEntry = psEntry->psNext; + } + + // 2nd pass: add fields via TransferElement entries & role associations + psEntry = psModel->psChild; + while( psEntry != NULL ) + { + if (psEntry->eType != CXT_Attribute) //ignore BID + { + //CPLDebug( "OGR_ILI", "Node tag: '%s'", psEntry->pszValue); + if( iliVersion == 1 && EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.Ili1TransferElement")) + { + const char* psClassRef = CPLGetXMLValue( psEntry, "Ili1TransferClass.REF", NULL ); + const char* psElementRef = CPLGetXMLValue( psEntry, "Ili1RefAttr.REF", NULL ); + int iOrderPos = atoi(CPLGetXMLValue( psEntry, "Ili1RefAttr.ORDER_POS", "0" ))-1; + IliClass* psParentClass = oClasses[oTidLookup[psClassRef]]; + CPLXMLNode* psElementNode = oTidLookup[psElementRef]; + psParentClass->AddFieldNode(psElementNode, iOrderPos); + } + else if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.TransferElement")) + { + const char* psClassRef = CPLGetXMLValue( psEntry, "TransferClass.REF", NULL ); + const char* psElementRef = CPLGetXMLValue( psEntry, "TransferElement.REF", NULL ); + int iOrderPos = atoi(CPLGetXMLValue( psEntry, "TransferElement.ORDER_POS", "0" ))-1; + IliClass* psParentClass = oClasses[oTidLookup[psClassRef]]; + CPLXMLNode* psElementNode = oTidLookup[psElementRef]; + psParentClass->AddFieldNode(psElementNode, iOrderPos); + } + else if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.Role")) + { + const char* psRefParent = CPLGetXMLValue( psEntry, "Association.REF", NULL ); + int iOrderPos = atoi(CPLGetXMLValue( psEntry, "Association.ORDER_POS", "0" ))-1; + IliClass* psParentClass = oClasses[oTidLookup[psRefParent]]; + if (psParentClass) + psParentClass->AddRoleNode(psEntry, iOrderPos); + } + else if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.AxisSpec")) + { + const char* psClassRef = CPLGetXMLValue( psEntry, "CoordType.REF", NULL ); + //int iOrderPos = atoi(CPLGetXMLValue( psEntry, "Axis.ORDER_POS", "0" ))-1; + CPLXMLNode* psCoordTypeNode = oTidLookup[psClassRef]; + oAxisCount[psCoordTypeNode] += 1; + } + else if( EQUAL(psEntry->pszValue, "IlisMeta07.ModelData.LinesForm")) + { + const char* psLineForm = CPLGetXMLValue( psEntry, "LineForm.REF", NULL ); + if (EQUAL(psLineForm, "INTERLIS.ARCS")) { + const char* psElementRef = CPLGetXMLValue( psEntry, "LineType.REF", NULL ); + CPLXMLNode* psElementNode = oTidLookup[psElementRef]; + oArcLineTypes.push_back(psElementNode); + } + } + } + psEntry = psEntry->psNext; + + } + + psModel = psModel->psNext; + } + + /* Analyze class inheritance & add fields to class table defn */ + for (ClassesMap::const_iterator it = oClasses.begin(); it != oClasses.end(); ++it) + { + //CPLDebug( "OGR_ILI", "Class: '%s'", it->second->GetName()); + const char* psRefSuper = CPLGetXMLValue( it->first, "Super.REF", NULL ); + if (psRefSuper) { + if (oTidLookup.find(psRefSuper) != oTidLookup.end() && + oClasses.find(oTidLookup[psRefSuper]) != oClasses.end()) { + oClasses[oTidLookup[psRefSuper]]->hasDerivedClasses = true; + } else { + CPLError(CE_Warning, CPLE_AppDefined, + "Couldn't reference super class '%s'", psRefSuper); + } + } + it->second->InitFieldDefinitions(); + it->second->AddFieldDefinitions(oArcLineTypes); + } + + /* Filter relevant classes */ + for (ClassesMap::const_iterator it = oClasses.begin(); it != oClasses.end(); ++it) + { + const char* className = it->second->GetIliName(); + FeatureDefnInfo oClassInfo = it->second->tableDefs(); + if (!EQUALN(className, "INTERLIS.", 9) && oClassInfo.poTableDefn) + featureDefnInfos.push_back(oClassInfo); + } + + CPLDestroyXMLNode(psRootNode); +} + +FeatureDefnInfo ImdReader::GetFeatureDefnInfo(const char *pszLayerName) { + FeatureDefnInfo featureDefnInfo; + for (FeatureDefnInfos::const_iterator it = featureDefnInfos.begin(); it != featureDefnInfos.end(); ++it) + { + OGRFeatureDefn* fdefn = it->poTableDefn; + if (EQUAL(fdefn->GetName(), pszLayerName)) featureDefnInfo = *it; + } + return featureDefnInfo; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/imdreader.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/imdreader.h new file mode 100644 index 000000000..d4316f263 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/imdreader.h @@ -0,0 +1,89 @@ +/****************************************************************************** + * $Id$ + * + * Project: Interlis 1/2 Translator + * Purpose: IlisMeta model reader. + * Author: Pirmin Kalberer, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2014, Pirmin Kalberer, Sourcepole AG + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _IMDREADER_H_INCLUDED +#define _IMDREADER_H_INCLUDED + +#include "cpl_vsi.h" +#include "cpl_error.h" +#include "ogr_feature.h" +#include +#include + + +class GeomFieldInfo +{ +public: + OGRFeatureDefn* geomTable; /* separate geometry table for Ili 1 */ + CPLString iliGeomType; + GeomFieldInfo() : geomTable(0) {}; +}; + +typedef std::map GeomFieldInfos; /* key: geom field name, value: ILI geom field info */ +typedef std::map StructFieldInfos; /* key: struct field name, value: struct table */ + +class FeatureDefnInfo +{ +public: + OGRFeatureDefn* poTableDefn; + GeomFieldInfos poGeomFieldInfos; + StructFieldInfos poStructFieldInfos; + FeatureDefnInfo() : poTableDefn(0) {}; +}; +typedef std::list FeatureDefnInfos; + +class IliModelInfo +{ +public: + CPLString name; + CPLString version; + CPLString uri; +}; +typedef std::list IliModelInfos; + +class ImdReader +{ +public: + int iliVersion; /* 1 or 2 */ + IliModelInfos modelInfos; + CPLString mainModelName; + CPLString mainBasketName; + CPLString mainTopicName; + FeatureDefnInfos featureDefnInfos; + char codeBlank; + char codeUndefined; + char codeContinue; +public: + ImdReader(int iliVersion); + ~ImdReader(); + void ReadModel(const char *pszFilename); + FeatureDefnInfo GetFeatureDefnInfo(const char *pszLayerName); +}; + +#endif /* _IMDREADER_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/makefile.vc new file mode 100644 index 000000000..9fa3ecaaa --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/makefile.vc @@ -0,0 +1,17 @@ + +OBJ = ili1reader.obj ili2reader.obj ili2handler.obj \ + ogrili1driver.obj ogrili1datasource.obj \ + ogrili1layer.obj ogrili2driver.obj ogrili2datasource.obj \ + ogrili2layer.obj imdreader.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I. -Iiom -I.. -I..\.. $(XERCES_INCLUDE) $(GEOS_CFLAGS) + +default: $(OBJ) + +clean: + -del *.obj *.pdb + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogr_ili1.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogr_ili1.h new file mode 100644 index 000000000..ebb34f472 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogr_ili1.h @@ -0,0 +1,131 @@ +/****************************************************************************** + * $Id: ogr_ili1.h 29109 2015-05-02 11:45:44Z rouault $ + * + * Project: Interlis 1 Translator + * Purpose: Definition of classes for OGR Interlis 1 driver. + * Author: Pirmin Kalberer, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_ILI1_H_INCLUDED +#define _OGR_ILI1_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "ili1reader.h" + + + +class OGRILI1DataSource; + +/************************************************************************/ +/* OGRILI1Layer */ +/************************************************************************/ + +class OGRILI1Layer : public OGRLayer +{ +private: +#ifdef notused + OGRSpatialReference *poSRS; +#endif + OGRFeatureDefn *poFeatureDefn; + GeomFieldInfos oGeomFieldInfos; + + int nFeatures; + OGRFeature **papoFeatures; + int nFeatureIdx; + + int bGeomsJoined; + + OGRILI1DataSource *poDS; + + public: + OGRILI1Layer( OGRFeatureDefn* poFeatureDefn, + GeomFieldInfos oGeomFieldInfos, + OGRILI1DataSource *poDS ); + + ~OGRILI1Layer(); + + OGRErr AddFeature(OGRFeature *poFeature); + + void ResetReading(); + OGRFeature * GetNextFeature(); + OGRFeature * GetNextFeatureRef(); + OGRFeature * GetFeatureRef( long nFID ); + + GIntBig GetFeatureCount( int bForce = TRUE ); + + OGRErr ICreateFeature( OGRFeature *poFeature ); + int GeometryAppend( OGRGeometry *poGeometry ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + GeomFieldInfos GetGeomFieldInfos() { return oGeomFieldInfos; } + + OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK = TRUE ); + + int TestCapability( const char * ); + + private: + void JoinGeomLayers(); + void JoinSurfaceLayer( OGRILI1Layer* poSurfaceLineLayer, int nSurfaceFieldIndex ); + OGRMultiPolygon* Polygonize( OGRGeometryCollection* poLines, bool fix_crossing_lines = false ); + void PolygonizeAreaLayer( OGRILI1Layer* poAreaLineLayer, int nAreaFieldIndex, int nPointFieldIndex ); +}; + +/************************************************************************/ +/* OGRILI1DataSource */ +/************************************************************************/ + +class OGRILI1DataSource : public OGRDataSource +{ + private: + char *pszName; + ImdReader *poImdReader; + IILI1Reader *poReader; + FILE *fpTransfer; + char *pszTopic; + int nLayers; + OGRILI1Layer** papoLayers; + + public: + OGRILI1DataSource(); + ~OGRILI1DataSource(); + + int Open( const char *, char** papszOpenOptions, int bTestOpen ); + int Create( const char *pszFile, char **papszOptions ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return poReader ? poReader->GetLayerCount() : 0; } + OGRLayer *GetLayer( int ); + OGRILI1Layer *GetLayerByName( const char* ); + + FILE *GetTransferFile() { return fpTransfer; } + + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + int TestCapability( const char * ); +}; + +#endif /* _OGR_ILI1_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogr_ili2.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogr_ili2.h new file mode 100644 index 000000000..1aa36762a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogr_ili2.h @@ -0,0 +1,118 @@ +/****************************************************************************** + * $Id: ogr_ili2.h 29109 2015-05-02 11:45:44Z rouault $ + * + * Project: Interlis 2 Translator + * Purpose: Definition of classes for OGR Interlis 2 driver. + * Author: Markus Schnider, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_ILI2_H_INCLUDED +#define _OGR_ILI2_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "imdreader.h" +#include "ili2reader.h" + +#include +#include + +class OGRILI2DataSource; + +/************************************************************************/ +/* OGRILI2Layer */ +/************************************************************************/ + +class OGRILI2Layer : public OGRLayer +{ +private: + OGRFeatureDefn *poFeatureDefn; + GeomFieldInfos oGeomFieldInfos; + std::list listFeature; + std::list::const_iterator listFeatureIt; + + OGRILI2DataSource *poDS; + + public: + OGRILI2Layer( OGRFeatureDefn* poFeatureDefn, + GeomFieldInfos oGeomFieldInfos, + OGRILI2DataSource *poDS ); + + ~OGRILI2Layer(); + + OGRErr ISetFeature(OGRFeature *poFeature); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + GIntBig GetFeatureCount( int bForce = TRUE ); + + OGRErr ICreateFeature( OGRFeature *poFeature ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + CPLString GetIliGeomType( const char* cFieldName) { return oGeomFieldInfos[cFieldName].iliGeomType; }; + + OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK = TRUE ); + + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRILI2DataSource */ +/************************************************************************/ + +class OGRILI2DataSource : public OGRDataSource +{ + private: + std::list listLayer; + + char *pszName; + ImdReader *poImdReader; + IILI2Reader *poReader; + VSILFILE *fpOutput; + + int nLayers; + OGRILI2Layer** papoLayers; + + public: + OGRILI2DataSource(); + ~OGRILI2DataSource(); + + int Open( const char *, char** papszOpenOptions, int bTestOpen ); + int Create( const char *pszFile, char **papszOptions ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return listLayer.size(); } + OGRLayer *GetLayer( int ); + + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + VSILFILE * GetOutputFP() { return fpOutput; } + int TestCapability( const char * ); +}; + +#endif /* _OGR_ILI2_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili1datasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili1datasource.cpp new file mode 100644 index 000000000..66becbdbf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili1datasource.cpp @@ -0,0 +1,338 @@ +/****************************************************************************** + * $Id: ogrili1datasource.cpp 29139 2015-05-03 20:09:20Z pka $ + * + * Project: Interlis 1 Translator + * Purpose: Implements OGRILI1DataSource class. + * Author: Pirmin Kalberer, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * Copyright (c) 2007-2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ili1.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +#include "ili1reader.h" + +#include + +CPL_CVSID("$Id: ogrili1datasource.cpp 29139 2015-05-03 20:09:20Z pka $"); + +/************************************************************************/ +/* OGRILI1DataSource() */ +/************************************************************************/ + +OGRILI1DataSource::OGRILI1DataSource() + +{ + pszName = NULL; + poImdReader = new ImdReader(1); + poReader = NULL; + fpTransfer = NULL; + pszTopic = NULL; + nLayers = 0; + papoLayers = NULL; +} + +/************************************************************************/ +/* ~OGRILI1DataSource() */ +/************************************************************************/ + +OGRILI1DataSource::~OGRILI1DataSource() + +{ + int i; + + for(i=0;i 1 ) + osModelFilename = filenames[1]; + + CSLDestroy( filenames ); + } + +/* -------------------------------------------------------------------- */ +/* Open the source file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpen( osBasename.c_str(), "r" ); + if( fp == NULL ) + { + if( !bTestOpen ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open ILI1 file `%s'.", + pszNewName ); + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If we aren't sure it is ILI1, load a header chunk and check */ +/* for signs it is ILI1 */ +/* -------------------------------------------------------------------- */ + if( bTestOpen ) + { + int nLen = (int)VSIFRead( szHeader, 1, sizeof(szHeader), fp ); + if (nLen == sizeof(szHeader)) + szHeader[sizeof(szHeader)-1] = '\0'; + else + szHeader[nLen] = '\0'; + + if( strstr(szHeader,"SCNT") == NULL ) + { + VSIFClose( fp ); + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* We assume now that it is ILI1. Close and instantiate a */ +/* ILI1Reader on it. */ +/* -------------------------------------------------------------------- */ + VSIFClose( fp ); + + poReader = CreateILI1Reader(); + if( poReader == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "File %s appears to be ILI1 but the ILI1 reader can't\n" + "be instantiated, likely because Xerces support wasn't\n" + "configured in.", + pszNewName ); + return FALSE; + } + + poReader->OpenFile( osBasename.c_str() ); + + pszName = CPLStrdup( osBasename.c_str() ); + + if (osModelFilename.length() > 0 ) + poReader->ReadModel( poImdReader, osModelFilename.c_str(), this ); + + int bResetConfigOption = FALSE; + if (EQUAL(CPLGetConfigOption("OGR_ARC_STEPSIZE", ""), "")) + { + bResetConfigOption = TRUE; + CPLSetThreadLocalConfigOption("OGR_ARC_STEPSIZE", "0.96"); + } + + //Parse model and read data - without surface join and area polygonizing + poReader->ReadFeatures(); + + if( bResetConfigOption ) + CPLSetThreadLocalConfigOption("OGR_ARC_STEPSIZE", NULL); + + return TRUE; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRILI1DataSource::Create( const char *pszFilename, + CPL_UNUSED char **papszOptions ) +{ + std::string osBasename, osModelFilename; + char **filenames = CSLTokenizeString2( pszFilename, ",", 0 ); + + osBasename = filenames[0]; + + if( CSLCount(filenames) > 1 ) + osModelFilename = filenames[1]; + + CSLDestroy( filenames ); + +/* -------------------------------------------------------------------- */ +/* Create the empty file. */ +/* -------------------------------------------------------------------- */ + fpTransfer = VSIFOpen( osBasename.c_str(), "w+b" ); + + if( fpTransfer == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create %s:\n%s", + osBasename.c_str(), VSIStrerror( errno ) ); + + return FALSE; + } + + +/* -------------------------------------------------------------------- */ +/* Parse model */ +/* -------------------------------------------------------------------- */ + if( osModelFilename.length() == 0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Creating Interlis transfer file without model definition." ); + } else { + poImdReader->ReadModel(osModelFilename.c_str()); + } + + pszTopic = CPLStrdup(poImdReader->mainTopicName.c_str()); + +/* -------------------------------------------------------------------- */ +/* Write headers */ +/* -------------------------------------------------------------------- */ + VSIFPrintf( fpTransfer, "SCNT\n" ); + VSIFPrintf( fpTransfer, "OGR/GDAL %s, INTERLIS Driver\n", GDAL_RELEASE_NAME ); + VSIFPrintf( fpTransfer, "////\n" ); + VSIFPrintf( fpTransfer, "MTID INTERLIS1\n" ); + const char* modelname = poImdReader->mainModelName.c_str(); + VSIFPrintf( fpTransfer, "MODL %s\n", modelname ); + + return TRUE; +} + +static char *ExtractTopic(const char * pszLayerName) +{ + const char *table = strchr(pszLayerName, '_'); + while (table && table[1] != '_') table = strchr(table+1, '_'); + return (table) ? CPLScanString(pszLayerName, table-pszLayerName, FALSE, FALSE) : NULL; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRILI1DataSource::ICreateLayer( const char * pszLayerName, + CPL_UNUSED OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + CPL_UNUSED char ** papszOptions ) +{ + FeatureDefnInfo featureDefnInfo = poImdReader->GetFeatureDefnInfo(pszLayerName); + const char *table = pszLayerName; + char * topic = ExtractTopic(pszLayerName); + if (nLayers) VSIFPrintf( fpTransfer, "ETAB\n" ); + if (topic) + { + table = pszLayerName+strlen(topic)+2; //after "__" + if (pszTopic == NULL || !EQUAL(topic, pszTopic)) + { + if (pszTopic) + { + VSIFPrintf( fpTransfer, "ETOP\n" ); + CPLFree(pszTopic); + } + pszTopic = topic; + VSIFPrintf( fpTransfer, "TOPI %s\n", pszTopic ); + } + else + { + CPLFree(topic); + } + } + else + { + if (pszTopic == NULL) pszTopic = CPLStrdup("Unknown"); + VSIFPrintf( fpTransfer, "TOPI %s\n", pszTopic ); + } + VSIFPrintf( fpTransfer, "TABL %s\n", table ); + + OGRFeatureDefn* poFeatureDefn = new OGRFeatureDefn(table); + poFeatureDefn->SetGeomType( eType ); + OGRILI1Layer *poLayer = new OGRILI1Layer(poFeatureDefn, featureDefnInfo.poGeomFieldInfos, this); + + nLayers ++; + papoLayers = (OGRILI1Layer**)CPLRealloc(papoLayers, sizeof(OGRILI1Layer*) * nLayers); + papoLayers[nLayers-1] = poLayer; + + return poLayer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRILI1DataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCCurveGeometries) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRILI1DataSource::GetLayer( int iLayer ) +{ + return poReader->GetLayer( iLayer ); +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRILI1Layer *OGRILI1DataSource::GetLayerByName( const char* pszLayerName ) +{ + return (OGRILI1Layer*)poReader->GetLayerByName( pszLayerName ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili1driver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili1driver.cpp new file mode 100644 index 000000000..e508222cf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili1driver.cpp @@ -0,0 +1,121 @@ +/****************************************************************************** + * $Id: ogrili1driver.cpp 29109 2015-05-02 11:45:44Z rouault $ + * + * Project: Interlis 1 Translator + * Purpose: Implements OGRILI1Layer class. + * Author: Pirmin Kalberer, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ili1.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrili1driver.cpp 29109 2015-05-02 11:45:44Z rouault $"); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRILI1DriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + OGRILI1DataSource *poDS; + + if( poOpenInfo->eAccess == GA_Update || + (!poOpenInfo->bStatOK && strchr(poOpenInfo->pszFilename, ',') == NULL) ) + return NULL; + + if( poOpenInfo->fpL != NULL ) + { + if( strstr((const char*)poOpenInfo->pabyHeader,"SCNT") == NULL ) + { + return NULL; + } + } + else if( poOpenInfo->bIsDirectory ) + return NULL; + + poDS = new OGRILI1DataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, poOpenInfo->papszOpenOptions, TRUE ) + || poDS->GetLayerCount() == 0 ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRILI1DriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + OGRILI1DataSource *poDS = new OGRILI1DataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* RegisterOGRILI1() */ +/************************************************************************/ + +void RegisterOGRILI1() { + GDALDriver *poDriver; + + if( GDALGetDriverByName( "Interlis 1" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "Interlis 1" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Interlis 1" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_ili.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "itf ili" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " ); + + poDriver->pfnOpen = OGRILI1DriverOpen; + poDriver->pfnCreate = OGRILI1DriverCreate; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili1layer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili1layer.cpp new file mode 100644 index 000000000..370d3160f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili1layer.cpp @@ -0,0 +1,618 @@ +/****************************************************************************** + * $Id: ogrili1layer.cpp 29221 2015-05-21 13:40:23Z pka $ + * + * Project: Interlis 1 Translator + * Purpose: Implements OGRILI1Layer class. + * Author: Pirmin Kalberer, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ili1.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_geos.h" + +CPL_CVSID("$Id: ogrili1layer.cpp 29221 2015-05-21 13:40:23Z pka $"); + +/************************************************************************/ +/* OGRILI1Layer() */ +/************************************************************************/ + +OGRILI1Layer::OGRILI1Layer( OGRFeatureDefn* poFeatureDefnIn, + GeomFieldInfos oGeomFieldInfosIn, + OGRILI1DataSource *poDSIn ) + +{ + poDS = poDSIn; + + poFeatureDefn = poFeatureDefnIn; + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + oGeomFieldInfos = oGeomFieldInfosIn; + + nFeatures = 0; + papoFeatures = NULL; + nFeatureIdx = 0; + + bGeomsJoined = FALSE; +} + +/************************************************************************/ +/* ~OGRILI1Layer() */ +/************************************************************************/ + +OGRILI1Layer::~OGRILI1Layer() +{ + int i; + + for(i=0;iRelease(); +} + + +OGRErr OGRILI1Layer::AddFeature (OGRFeature *poFeature) +{ + nFeatures++; + + papoFeatures = (OGRFeature **) + CPLRealloc( papoFeatures, sizeof(void*) * nFeatures ); + + papoFeatures[nFeatures-1] = poFeature; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRILI1Layer::ResetReading(){ + nFeatureIdx = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRILI1Layer::GetNextFeature() +{ + OGRFeature *poFeature; + + if (!bGeomsJoined) JoinGeomLayers(); + + while(nFeatureIdx < nFeatures) + { + poFeature = GetNextFeatureRef(); + if (poFeature) + return poFeature->Clone(); + } + return NULL; +} + +OGRFeature *OGRILI1Layer::GetNextFeatureRef() { + OGRFeature *poFeature = NULL; + if (nFeatureIdx < nFeatures) + { + poFeature = papoFeatures[nFeatureIdx++]; + //apply filters + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + } + return NULL; +} + +/************************************************************************/ +/* GetFeatureRef() */ +/************************************************************************/ + +OGRFeature *OGRILI1Layer::GetFeatureRef( long nFID ) + +{ + OGRFeature *poFeature; + + ResetReading(); + while( (poFeature = GetNextFeatureRef()) != NULL ) + { + if( poFeature->GetFID() == nFID ) + return poFeature; + } + + return NULL; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRILI1Layer::GetFeatureCount( int bForce ) +{ + if (m_poFilterGeom == NULL && m_poAttrQuery == NULL && + 1 /*poAreaLineLayer == NULL*/) + { + return nFeatures; + } + else + { + return OGRLayer::GetFeatureCount(bForce); + } +} + +static char* d2str(double val) +{ + static char strbuf[255]; + if( val == (int) val ) + sprintf( strbuf, "%d", (int) val ); + else if( fabs(val) < 370 ) + CPLsprintf( strbuf, "%.16g", val ); + else if( fabs(val) > 100000000.0 ) + CPLsprintf( strbuf, "%.16g", val ); + else + CPLsprintf( strbuf, "%.3f", val ); + return strbuf; +} + +static void AppendCoordinateList( OGRLineString *poLine, OGRILI1DataSource *poDS) +{ + int b3D = wkbHasZ(poLine->getGeometryType()); + + for( int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++ ) + { + if (iPoint == 0) VSIFPrintf( poDS->GetTransferFile(), "STPT" ); + else VSIFPrintf( poDS->GetTransferFile(), "LIPT" ); + VSIFPrintf( poDS->GetTransferFile(), " %s", d2str(poLine->getX(iPoint)) ); + VSIFPrintf( poDS->GetTransferFile(), " %s", d2str(poLine->getY(iPoint)) ); + if (b3D) VSIFPrintf( poDS->GetTransferFile(), " %s", d2str(poLine->getZ(iPoint)) ); + VSIFPrintf( poDS->GetTransferFile(), "\n" ); + } + VSIFPrintf( poDS->GetTransferFile(), "ELIN\n" ); +} + +static void AppendCoumpoundCurve( OGRCompoundCurve *poCC, OGRILI1DataSource *poDS) +{ + for( int iMember = 0; iMember < poCC->getNumCurves(); iMember++) + { + OGRCurve *poGeometry = poCC->getCurve( iMember ); + int b3D = wkbHasZ(poGeometry->getGeometryType()); + int bIsArc = (poGeometry->getGeometryType() == wkbCircularString + || poGeometry->getGeometryType() == wkbCircularStringZ ); + OGRSimpleCurve *poLine = (OGRSimpleCurve *)poGeometry; + for( int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++ ) + { + //Skip last point in curve member + if (iPoint == poLine->getNumPoints()-1 && iMember < poCC->getNumCurves()-1) + continue; + if (iMember == 0 && iPoint == 0) VSIFPrintf( poDS->GetTransferFile(), "STPT" ); + else if (bIsArc && iPoint == 1) VSIFPrintf( poDS->GetTransferFile(), "ARCP" ); + else VSIFPrintf( poDS->GetTransferFile(), "LIPT" ); + VSIFPrintf( poDS->GetTransferFile(), " %s", d2str(poLine->getX(iPoint)) ); + VSIFPrintf( poDS->GetTransferFile(), " %s", d2str(poLine->getY(iPoint)) ); + if (b3D) VSIFPrintf( poDS->GetTransferFile(), " %s", d2str(poLine->getZ(iPoint)) ); + VSIFPrintf( poDS->GetTransferFile(), "\n" ); + } + } + VSIFPrintf( poDS->GetTransferFile(), "ELIN\n" ); +} + +int OGRILI1Layer::GeometryAppend( OGRGeometry *poGeometry ) +{ + //CPLDebug( "OGR_ILI", "OGRILI1Layer::GeometryAppend OGRGeometryType: %s", OGRGeometryTypeToName(poGeometry->getGeometryType())); +/* -------------------------------------------------------------------- */ +/* 2D Point */ +/* -------------------------------------------------------------------- */ + if( poGeometry->getGeometryType() == wkbPoint ) + { + /* embedded in from non-geometry fields */ + } +/* -------------------------------------------------------------------- */ +/* 3D Point */ +/* -------------------------------------------------------------------- */ + else if( poGeometry->getGeometryType() == wkbPoint25D ) + { + /* embedded in from non-geometry fields */ + } + +/* -------------------------------------------------------------------- */ +/* LineString and LinearRing */ +/* -------------------------------------------------------------------- */ + else if( poGeometry->getGeometryType() == wkbLineString + || poGeometry->getGeometryType() == wkbLineString25D ) + { + AppendCoordinateList( (OGRLineString *) poGeometry, poDS ); + } + +/* -------------------------------------------------------------------- */ +/* Polygon */ +/* -------------------------------------------------------------------- */ + else if( poGeometry->getGeometryType() == wkbPolygon + || poGeometry->getGeometryType() == wkbPolygon25D ) + { + OGRPolygon *poPolygon = (OGRPolygon *) poGeometry; + + if( poPolygon->getExteriorRing() != NULL ) + { + if( !GeometryAppend( poPolygon->getExteriorRing() ) ) + return FALSE; + } + + for( int iRing = 0; iRing < poPolygon->getNumInteriorRings(); iRing++ ) + { + OGRLinearRing *poRing = poPolygon->getInteriorRing(iRing); + + if( !GeometryAppend( poRing ) ) + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* MultiPolygon */ +/* -------------------------------------------------------------------- */ + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon + || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString + || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint + || wkbFlatten(poGeometry->getGeometryType()) == wkbGeometryCollection + || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiCurve + || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiCurveZ ) + { + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeometry; + int iMember; + + if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon ) + { + } + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString ) + { + } + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint ) + { + } + else + { + } + + for( iMember = 0; iMember < poGC->getNumGeometries(); iMember++) + { + OGRGeometry *poMember = poGC->getGeometryRef( iMember ); + if( !GeometryAppend( poMember ) ) + return FALSE; + } + + } + else if( poGeometry->getGeometryType() == wkbCompoundCurve + || poGeometry->getGeometryType() == wkbCompoundCurveZ ) + { + AppendCoumpoundCurve( ( OGRCompoundCurve *) poGeometry, poDS ); + } else { + CPLError(CE_Warning, CPLE_AppDefined, "Skipping unknown geometry type '%s'", OGRGeometryTypeToName(poGeometry->getGeometryType())); + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRILI1Layer::ICreateFeature( OGRFeature *poFeature ) { + static long tid = -1; //system generated TID (must be unique within table) + VSIFPrintf( poDS->GetTransferFile(), "OBJE" ); + + if ( poFeatureDefn->GetFieldCount() && !EQUAL(poFeatureDefn->GetFieldDefn(0)->GetNameRef(), "TID") ) + { + //Input is not generated from an Interlis 1 source + if (poFeature->GetFID() != OGRNullFID) + tid = (int)poFeature->GetFID(); + else + ++tid; + VSIFPrintf( poDS->GetTransferFile(), " %ld", tid ); + //Embedded geometry + if( poFeature->GetGeometryRef() != NULL ) + { + OGRGeometry *poGeometry = poFeature->GetGeometryRef(); + // 2D Point + if( poGeometry->getGeometryType() == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + VSIFPrintf( poDS->GetTransferFile(), " %s", d2str(poPoint->getX()) ); + VSIFPrintf( poDS->GetTransferFile(), " %s", d2str(poPoint->getY()) ); + } + // 3D Point + else if( poGeometry->getGeometryType() == wkbPoint25D ) + { + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + VSIFPrintf( poDS->GetTransferFile(), " %s", d2str(poPoint->getX()) ); + VSIFPrintf( poDS->GetTransferFile(), " %s", d2str(poPoint->getY()) ); + VSIFPrintf( poDS->GetTransferFile(), " %s", d2str(poPoint->getZ()) ); + } + } + } + + // Write all fields. + for(int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + if ( poFeature->IsFieldSet( iField ) ) + { + const char *pszRaw = poFeature->GetFieldAsString( iField ); + if (poFeatureDefn->GetFieldDefn( iField )->GetType() == OFTString) { + //Interlis 1 encoding is ISO 8859-1 (Latin1) -> Recode from UTF-8 + char* pszString = CPLRecode(pszRaw, CPL_ENC_UTF8, CPL_ENC_ISO8859_1); + //Replace spaces + for(size_t i=0; iGetTransferFile(), " %s", pszString ); + CPLFree( pszString ); + } else { + VSIFPrintf( poDS->GetTransferFile(), " %s", pszRaw ); + } + } + else + { + VSIFPrintf( poDS->GetTransferFile(), " @" ); + } + } + VSIFPrintf( poDS->GetTransferFile(), "\n" ); + + // Write out Geometry + if( poFeature->GetGeometryRef() != NULL ) + { + GeometryAppend(poFeature->GetGeometryRef()); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRILI1Layer::TestCapability( CPL_UNUSED const char * pszCap ) { + if( EQUAL(pszCap,OLCCurveGeometries) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRILI1Layer::CreateField( OGRFieldDefn *poField, CPL_UNUSED int bApproxOK ) { + poFeatureDefn->AddFieldDefn( poField ); + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* Internal routines */ +/************************************************************************/ + +void OGRILI1Layer::JoinGeomLayers() +{ + bGeomsJoined = TRUE; + int bResetConfigOption = FALSE; + if (EQUAL(CPLGetConfigOption("OGR_ARC_STEPSIZE", ""), "")) + { + bResetConfigOption = TRUE; + CPLSetThreadLocalConfigOption("OGR_ARC_STEPSIZE", "0.96"); + } + + for (GeomFieldInfos::const_iterator it = oGeomFieldInfos.begin(); it != oGeomFieldInfos.end(); ++it) + { + OGRFeatureDefn* geomFeatureDefn = it->second.geomTable; + if (geomFeatureDefn) + { + CPLDebug( "OGR_ILI", "Join geometry table %s of field '%s'", geomFeatureDefn->GetName(), it->first.c_str() ); + OGRILI1Layer* poGeomLayer = poDS->GetLayerByName(geomFeatureDefn->GetName()); + int nGeomFieldIndex = GetLayerDefn()->GetGeomFieldIndex(it->first.c_str()); + if (it->second.iliGeomType == "Surface") + { + JoinSurfaceLayer(poGeomLayer, nGeomFieldIndex); + } + else if (it->second.iliGeomType == "Area") + { + CPLString pointField = it->first + "__Point"; + int nPointFieldIndex = GetLayerDefn()->GetGeomFieldIndex(pointField.c_str()); + PolygonizeAreaLayer(poGeomLayer, nGeomFieldIndex, nPointFieldIndex); + } + } + } + + if( bResetConfigOption ) + CPLSetThreadLocalConfigOption("OGR_ARC_STEPSIZE", NULL); +} + + +void OGRILI1Layer::JoinSurfaceLayer( OGRILI1Layer* poSurfaceLineLayer, int nSurfaceFieldIndex ) +{ + CPLDebug( "OGR_ILI", "Joining surface layer %s with geometries", GetLayerDefn()->GetName()); + OGRwkbGeometryType geomType = GetLayerDefn()->GetGeomFieldDefn(nSurfaceFieldIndex)->GetType(); + poSurfaceLineLayer->ResetReading(); + while (OGRFeature *linefeature = poSurfaceLineLayer->GetNextFeatureRef()) { + //OBJE entries with same _RefTID are polygon rings of same feature + //TODO: non-numeric _RefTID/FID is not supported yet! + GIntBig reftid = linefeature->GetFieldAsInteger64(1); //_RefTID + OGRFeature *feature = GetFeatureRef((int)reftid); + if (feature) { + OGRCurvePolygon *poly; + if (feature->GetGeomFieldRef(nSurfaceFieldIndex)) { + CPLDebug( "OGR_ILI", "Adding ring to FID " CPL_FRMT_GIB, reftid ); + poly = (OGRCurvePolygon *)feature->GetGeomFieldRef(nSurfaceFieldIndex); + } else { + poly = (geomType == wkbPolygon) ? new OGRPolygon() : new OGRCurvePolygon(); + feature->SetGeomFieldDirectly(nSurfaceFieldIndex, poly); + } + OGRMultiCurve *lines = (OGRMultiCurve*)linefeature->GetGeomFieldRef(0); + for( int i = 0; i < lines->getNumGeometries(); i++ ) { + OGRCurve *line = (OGRCurve*)lines->getGeometryRef(i); + OGRCurve *ring = (geomType == wkbPolygon) ? + OGRCurve::CastToLinearRing((OGRCurve*)line->clone()) : + (OGRCurve*)line->clone(); + poly->addRingDirectly(ring); + } + } else { + CPLError(CE_Warning, CPLE_AppDefined, "Couldn't join feature FID " CPL_FRMT_GIB, reftid ); + } + } + + ResetReading(); + poSurfaceLineLayer = 0; +} + +OGRMultiPolygon* OGRILI1Layer::Polygonize( OGRGeometryCollection* poLines, bool fix_crossing_lines ) +{ + OGRMultiPolygon *poPolygon = new OGRMultiPolygon(); + + if (poLines->getNumGeometries() == 0) return poPolygon; + +#if defined(HAVE_GEOS) + GEOSGeom *ahInGeoms = NULL; + int i = 0; + OGRGeometryCollection *poNoncrossingLines = poLines; + GEOSGeom hResultGeom = NULL; + OGRGeometry *poMP = NULL; + + if (fix_crossing_lines && poLines->getNumGeometries() > 0) + { + CPLDebug( "OGR_ILI", "Fixing crossing lines"); + //A union of the geometry collection with one line fixes invalid geometries + poNoncrossingLines = (OGRGeometryCollection*)poLines->Union(poLines->getGeometryRef(0)); + CPLDebug( "OGR_ILI", "Fixed lines: %d", poNoncrossingLines->getNumGeometries()-poLines->getNumGeometries()); + } + + GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext(); + + ahInGeoms = (GEOSGeom *) CPLCalloc(sizeof(void*),poNoncrossingLines->getNumGeometries()); + for( i = 0; i < poNoncrossingLines->getNumGeometries(); i++ ) + ahInGeoms[i] = poNoncrossingLines->getGeometryRef(i)->exportToGEOS(hGEOSCtxt); + + hResultGeom = GEOSPolygonize_r( hGEOSCtxt, + ahInGeoms, + poNoncrossingLines->getNumGeometries() ); + + for( i = 0; i < poNoncrossingLines->getNumGeometries(); i++ ) + GEOSGeom_destroy_r( hGEOSCtxt, ahInGeoms[i] ); + CPLFree( ahInGeoms ); + if (poNoncrossingLines != poLines) delete poNoncrossingLines; + + if( hResultGeom == NULL ) + { + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + return NULL; + } + + poMP = OGRGeometryFactory::createFromGEOS( hGEOSCtxt, hResultGeom ); + + GEOSGeom_destroy_r( hGEOSCtxt, hResultGeom ); + OGRGeometry::freeGEOSContext( hGEOSCtxt ); + + return (OGRMultiPolygon *) poMP; + +#endif + + return poPolygon; +} + + +void OGRILI1Layer::PolygonizeAreaLayer( OGRILI1Layer* poAreaLineLayer, int nAreaFieldIndex, int nPointFieldIndex ) +{ + //add all lines from poAreaLineLayer to collection + OGRGeometryCollection *gc = new OGRGeometryCollection(); + poAreaLineLayer->ResetReading(); + while (OGRFeature *feature = poAreaLineLayer->GetNextFeatureRef()) + gc->addGeometry(feature->GetGeometryRef()); + + //polygonize lines + CPLDebug( "OGR_ILI", "Polygonizing layer %s with %d multilines", poAreaLineLayer->GetLayerDefn()->GetName(), gc->getNumGeometries()); + poAreaLineLayer = 0; + OGRMultiPolygon* polys = Polygonize( gc , false); + CPLDebug( "OGR_ILI", "Resulting polygons: %d", polys->getNumGeometries()); + if (polys->getNumGeometries() != GetFeatureCount()) + { + CPLDebug( "OGR_ILI", "Feature count of layer %s: " CPL_FRMT_GIB, GetLayerDefn()->GetName(), GetFeatureCount()); + CPLDebug( "OGR_ILI", "Polygonizing again with crossing line fix"); + delete polys; + polys = Polygonize( gc, true ); //try again with crossing line fix + CPLDebug( "OGR_ILI", "Resulting polygons: %d", polys->getNumGeometries()); + } + delete gc; + + //associate polygon feature with data row according to centroid +#if defined(HAVE_GEOS) + int i; + OGRPolygon emptyPoly; + GEOSGeom *ahInGeoms = NULL; + + CPLDebug( "OGR_ILI", "Associating layer %s with area polygons", GetLayerDefn()->GetName()); + ahInGeoms = (GEOSGeom *) CPLCalloc(sizeof(void*), polys->getNumGeometries()); + GEOSContextHandle_t hGEOSCtxt = OGRGeometry::createGEOSContext(); + for( i = 0; i < polys->getNumGeometries(); i++ ) + { + ahInGeoms[i] = polys->getGeometryRef(i)->exportToGEOS(hGEOSCtxt); + if (!GEOSisValid_r(hGEOSCtxt, ahInGeoms[i])) ahInGeoms[i] = NULL; + } + for ( int nFidx = 0; nFidx < nFeatures; nFidx++) + { + OGRFeature *feature = papoFeatures[nFidx]; + OGRGeometry* geomRef = feature->GetGeomFieldRef(nPointFieldIndex); + if( !geomRef ) + { + continue; + } + GEOSGeom point = (GEOSGeom)(geomRef->exportToGEOS(hGEOSCtxt)); + for (i = 0; i < polys->getNumGeometries(); i++ ) + { + if (ahInGeoms[i] && GEOSWithin_r(hGEOSCtxt, point, ahInGeoms[i])) + { + feature->SetGeomField(nAreaFieldIndex, polys->getGeometryRef(i)); + break; + } + } + if (i == polys->getNumGeometries()) + { + CPLDebug( "OGR_ILI", "Association between area and point failed."); + feature->SetGeometry( &emptyPoly ); + } + GEOSGeom_destroy_r( hGEOSCtxt, point ); + } + for( i = 0; i < polys->getNumGeometries(); i++ ) + GEOSGeom_destroy_r( hGEOSCtxt, ahInGeoms[i] ); + CPLFree( ahInGeoms ); + OGRGeometry::freeGEOSContext( hGEOSCtxt ); +#endif + poAreaLineLayer = 0; + delete polys; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili2datasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili2datasource.cpp new file mode 100644 index 000000000..8d8913975 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili2datasource.cpp @@ -0,0 +1,328 @@ +/****************************************************************************** + * $Id: ogrili2datasource.cpp 29140 2015-05-03 20:09:32Z pka $ + * + * Project: Interlis 2 Translator + * Purpose: Implements OGRILI2DataSource class. + * Author: Markus Schnider, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * Copyright (c) 2007-2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ili2.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +#include "ili2reader.h" + +using namespace std; + + +CPL_CVSID("$Id: ogrili2datasource.cpp 29140 2015-05-03 20:09:32Z pka $"); + +/************************************************************************/ +/* OGRILI2DataSource() */ +/************************************************************************/ + +OGRILI2DataSource::OGRILI2DataSource() + +{ + pszName = NULL; + poImdReader = new ImdReader(2); + poReader = NULL; + fpOutput = NULL; + nLayers = 0; + papoLayers = NULL; +} + +/************************************************************************/ +/* ~OGRILI2DataSource() */ +/************************************************************************/ + +OGRILI2DataSource::~OGRILI2DataSource() + +{ + int i; + + for(i=0;i\n", poImdReader->mainBasketName.c_str()); + VSIFPrintfL(fpOutput, "\n"); + VSIFPrintfL(fpOutput, "\n"); + VSIFCloseL(fpOutput); + } + + DestroyILI2Reader( poReader ); + delete poImdReader; + CPLFree( pszName ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRILI2DataSource::Open( const char * pszNewName, char** papszOpenOptions, int bTestOpen ) + +{ + FILE *fp; + char szHeader[1000]; + CPLString osBasename; + CPLString osModelFilename; + + if( CSLFetchNameValue(papszOpenOptions, "MODEL") != NULL ) + { + osBasename = pszNewName; + osModelFilename = CSLFetchNameValue(papszOpenOptions, "MODEL"); + } + else + { + char **filenames = CSLTokenizeString2( pszNewName, ",", 0 ); + + osBasename = filenames[0]; + + if( CSLCount(filenames) > 1 ) + osModelFilename = filenames[1]; + + CSLDestroy( filenames ); + } + + pszName = CPLStrdup( osBasename ); + +/* -------------------------------------------------------------------- */ +/* Open the source file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpen( pszName, "r" ); + if( fp == NULL ) + { + if( !bTestOpen ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open ILI2 file `%s'.", + pszNewName ); + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If we aren't sure it is ILI2, load a header chunk and check */ +/* for signs it is ILI2 */ +/* -------------------------------------------------------------------- */ + if( bTestOpen ) + { + int nLen = (int)VSIFRead( szHeader, 1, sizeof(szHeader), fp ); + if (nLen == sizeof(szHeader)) + szHeader[sizeof(szHeader)-1] = '\0'; + else + szHeader[nLen] = '\0'; + + if( szHeader[0] != '<' + || strstr(szHeader,"interlis.ch/INTERLIS2") == NULL ) + { // "www.interlis.ch/INTERLIS2.3" + VSIFClose( fp ); + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* We assume now that it is ILI2. Close and instantiate a */ +/* ILI2Reader on it. */ +/* -------------------------------------------------------------------- */ + VSIFClose( fp ); + + poReader = CreateILI2Reader(); + if( poReader == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "File %s appears to be ILI2 but the ILI2 reader can't\n" + "be instantiated, likely because Xerces support wasn't\n" + "configured in.", + pszNewName ); + return FALSE; + } + + if (osModelFilename.size()) + poReader->ReadModel( poImdReader, osModelFilename ); + + poReader->SetSourceFile( pszName ); + + poReader->SaveClasses( pszName ); + + listLayer = poReader->GetLayers(); + list::const_iterator layerIt; + for (layerIt = listLayer.begin(); layerIt != listLayer.end(); ++layerIt) { + (*layerIt)->ResetReading(); + } + + return TRUE; +} + + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRILI2DataSource::Create( const char *pszFilename, + CPL_UNUSED char **papszOptions ) + +{ + char **filenames = CSLTokenizeString2( pszFilename, ",", 0 ); + pszName = CPLStrdup(filenames[0]); + const char *pszModelFilename = (CSLCount(filenames)>1) ? filenames[1] : NULL; + + if( pszModelFilename == NULL ) + { + CPLError( CE_Warning, CPLE_OpenFailed, + "Model file '%s' (%s) not found : %s.", + pszModelFilename, pszFilename, VSIStrerror( errno ) ); + CSLDestroy(filenames); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Create the output file. */ +/* -------------------------------------------------------------------- */ + + if( strcmp(pszName,"/vsistdout/") == 0 || + strncmp(pszName,"/vsigzip/", 9) == 0 ) + { + fpOutput = VSIFOpenL(pszName, "wb"); + } + else if ( strncmp(pszName,"/vsizip/", 8) == 0) + { + if (EQUAL(CPLGetExtension(pszName), "zip")) + { + CPLFree(pszName); + pszName = CPLStrdup(CPLFormFilename(pszName, "out.xtf", NULL)); + } + + fpOutput = VSIFOpenL(pszName, "wb"); + } + else + fpOutput = VSIFOpenL( pszName, "wb+" ); + if( fpOutput == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create XTF file %s.", + pszName ); + CSLDestroy(filenames); + return FALSE; + } + + +/* -------------------------------------------------------------------- */ +/* Parse model */ +/* -------------------------------------------------------------------- */ + poImdReader->ReadModel(pszModelFilename); + +/* -------------------------------------------------------------------- */ +/* Write headers */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL(fpOutput, "\n"); + VSIFPrintfL(fpOutput, "\n"); + VSIFPrintfL(fpOutput, "\n", GDAL_RELEASE_NAME); + VSIFPrintfL(fpOutput, "\n"); + for (IliModelInfos::const_iterator it = poImdReader->modelInfos.begin(); it != poImdReader->modelInfos.end(); ++it) + { + VSIFPrintfL(fpOutput, "\n", + it->name.c_str(), it->uri.c_str(), it->version.c_str()); + } + VSIFPrintfL(fpOutput, "\n"); + VSIFPrintfL(fpOutput, "\n"); + VSIFPrintfL(fpOutput, "\n"); + const char* basketName = poImdReader->mainBasketName.c_str(); + VSIFPrintfL(fpOutput, "<%s BID=\"%s\">\n", basketName, basketName); + + CSLDestroy( filenames ); + return TRUE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRILI2DataSource::ICreateLayer( const char * pszLayerName, + CPL_UNUSED OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + CPL_UNUSED char ** papszOptions ) +{ + if (fpOutput == NULL) + return NULL; + + FeatureDefnInfo featureDefnInfo = poImdReader->GetFeatureDefnInfo(pszLayerName); + OGRFeatureDefn* poFeatureDefn = featureDefnInfo.poTableDefn; + if (poFeatureDefn == NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Layer '%s' not found in model definition. Creating adhoc layer", pszLayerName); + poFeatureDefn = new OGRFeatureDefn(pszLayerName); + poFeatureDefn->SetGeomType( eType ); + } + OGRILI2Layer *poLayer = new OGRILI2Layer(poFeatureDefn, featureDefnInfo.poGeomFieldInfos, this); + + nLayers++; + papoLayers = (OGRILI2Layer**)CPLRealloc(papoLayers, sizeof(OGRILI2Layer*) * nLayers); + papoLayers[nLayers-1] = poLayer; + + return poLayer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRILI2DataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCCurveGeometries) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRILI2DataSource::GetLayer( int iLayer ) + +{ + list::const_iterator layerIt = listLayer.begin(); + int i = 0; + while (i < iLayer && layerIt != listLayer.end()) { + i++; + layerIt++; + } + + if (i == iLayer) { + OGRILI2Layer *tmpLayer = (OGRILI2Layer *)*layerIt; + return tmpLayer; + } else + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili2driver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili2driver.cpp new file mode 100644 index 000000000..28f65d719 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili2driver.cpp @@ -0,0 +1,122 @@ +/****************************************************************************** + * $Id: ogrili2driver.cpp 29109 2015-05-02 11:45:44Z rouault $ + * + * Project: Interlis 2 Translator + * Purpose: Implements OGRILI2Layer class. + * Author: Markus Schnider, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ili2.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrili2driver.cpp 29109 2015-05-02 11:45:44Z rouault $"); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRILI2DriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + OGRILI2DataSource *poDS; + + if( poOpenInfo->eAccess == GA_Update || + (!poOpenInfo->bStatOK && strchr(poOpenInfo->pszFilename, ',') == NULL) ) + return NULL; + + if( poOpenInfo->fpL != NULL ) + { + if( poOpenInfo->pabyHeader[0] != '<' + || strstr((const char*)poOpenInfo->pabyHeader,"interlis.ch/INTERLIS2") == NULL ) + { + return NULL; + } + } + else if( poOpenInfo->bIsDirectory ) + return NULL; + + poDS = new OGRILI2DataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, poOpenInfo->papszOpenOptions, TRUE ) + || poDS->GetLayerCount() == 0 ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRILI2DriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + OGRILI2DataSource *poDS = new OGRILI2DataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* RegisterOGRILI2() */ +/************************************************************************/ + +void RegisterOGRILI2() { + GDALDriver *poDriver; + + if( GDALGetDriverByName( "Interlis 2" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "Interlis 2" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Interlis 2" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_ili.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "xtf xml ili" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " ); + + poDriver->pfnOpen = OGRILI2DriverOpen; + poDriver->pfnCreate = OGRILI2DriverCreate; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili2layer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili2layer.cpp new file mode 100644 index 000000000..4e0bb61fe --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ili/ogrili2layer.cpp @@ -0,0 +1,343 @@ +/****************************************************************************** + * $Id: ogrili2layer.cpp 29140 2015-05-03 20:09:32Z pka $ + * + * Project: Interlis 2 Translator + * Purpose: Implements OGRILI2Layer class. + * Author: Markus Schnider, Sourcepole AG + * + ****************************************************************************** + * Copyright (c) 2004, Pirmin Kalberer, Sourcepole AG + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ili2.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrili2layer.cpp 29140 2015-05-03 20:09:32Z pka $"); + +/************************************************************************/ +/* OGRILI2Layer() */ +/************************************************************************/ + +OGRILI2Layer::OGRILI2Layer( OGRFeatureDefn* poFeatureDefnIn, + GeomFieldInfos oGeomFieldInfosIn, + OGRILI2DataSource *poDSIn ) +{ + poFeatureDefn = poFeatureDefnIn; + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + oGeomFieldInfos = oGeomFieldInfosIn; + + poDS = poDSIn; + + listFeatureIt = listFeature.begin(); +} + +/************************************************************************/ +/* ~OGRILI2Layer() */ +/************************************************************************/ + +OGRILI2Layer::~OGRILI2Layer() +{ + if( poFeatureDefn ) + poFeatureDefn->Release(); + + listFeatureIt = listFeature.begin(); + while(listFeatureIt != listFeature.end()) + { + OGRFeature *poFeature = *(listFeatureIt++); + delete poFeature; + } +} + + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRILI2Layer::ISetFeature (OGRFeature *poFeature) +{ + listFeature.push_back(poFeature); + return OGRERR_NONE; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRILI2Layer::ResetReading() +{ + listFeatureIt = listFeature.begin(); +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRILI2Layer::GetNextFeature() +{ + OGRFeature *poFeature = NULL; + while (listFeatureIt != listFeature.end()) + { + poFeature = *(listFeatureIt++); + //apply filters + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature->Clone(); + } + return NULL; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRILI2Layer::GetFeatureCount( int bForce ) +{ + if (m_poFilterGeom == NULL && m_poAttrQuery == NULL) + { + return listFeature.size(); + } + else + { + return OGRLayer::GetFeatureCount(bForce); + } +} + +static char* d2str(double val) +{ + static char strbuf[255]; + if( val == (int) val ) + sprintf( strbuf, "%d", (int) val ); + else if( fabs(val) < 370 ) + CPLsprintf( strbuf, "%.16g", val ); + else if( fabs(val) > 100000000.0 ) + CPLsprintf( strbuf, "%.16g", val ); + else + CPLsprintf( strbuf, "%.3f", val ); + return strbuf; +} + +static void AppendCoordinateList( OGRLineString *poLine, VSILFILE* fp ) +{ + int b3D = wkbHasZ(poLine->getGeometryType()); + + for( int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++ ) + { + VSIFPrintfL(fp, ""); + VSIFPrintfL(fp, "%s", d2str(poLine->getX(iPoint))); + VSIFPrintfL(fp, "%s", d2str(poLine->getY(iPoint))); + if (b3D) VSIFPrintfL(fp, "%s", d2str(poLine->getZ(iPoint))); + VSIFPrintfL(fp, "\n"); + } +} + +static int OGR2ILIGeometryAppend( OGRGeometry *poGeometry, VSILFILE* fp, const char *attrname, CPLString iliGeomType ) +{ + //CPLDebug( "OGR_ILI", "OGR2ILIGeometryAppend getGeometryType %s iliGeomType %s", poGeometry->getGeometryName(), iliGeomType.c_str()); +/* -------------------------------------------------------------------- */ +/* 2D/3D Point */ +/* -------------------------------------------------------------------- */ + if( poGeometry->getGeometryType() == wkbPoint || poGeometry->getGeometryType() == wkbPoint25D ) + { + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + VSIFPrintfL(fp, "<%s>\n", attrname); + VSIFPrintfL(fp, ""); + VSIFPrintfL(fp, "%s", d2str(poPoint->getX())); + VSIFPrintfL(fp, "%s", d2str(poPoint->getY())); + if( poGeometry->getGeometryType() == wkbPoint25D ) + VSIFPrintfL(fp, "%s", d2str(poPoint->getZ())); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n", attrname); + } + +/* -------------------------------------------------------------------- */ +/* LineString and LinearRing */ +/* -------------------------------------------------------------------- */ + else if( poGeometry->getGeometryType() == wkbLineString + || poGeometry->getGeometryType() == wkbLineString25D ) + { + if (attrname) VSIFPrintfL(fp, "<%s>\n", attrname); + VSIFPrintfL(fp, "\n"); + // unclipped polyline, add one sequence + // VSIFPrintfL(fp, "\n"); + AppendCoordinateList( (OGRLineString *) poGeometry, fp ); + // VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + if (attrname) VSIFPrintfL(fp, "\n", attrname); + } + +/* -------------------------------------------------------------------- */ +/* Polygon */ +/* -------------------------------------------------------------------- */ + else if( poGeometry->getGeometryType() == wkbPolygon + || poGeometry->getGeometryType() == wkbPolygon25D ) + { + OGRPolygon *poPolygon = (OGRPolygon *) poGeometry; + + if (attrname) VSIFPrintfL(fp, "<%s>\n", attrname); + if( iliGeomType == "Surface" || iliGeomType == "Area" ) + { + //VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + } + + if( poPolygon->getExteriorRing() != NULL ) + { + if( !OGR2ILIGeometryAppend( poPolygon->getExteriorRing(), fp, NULL, "" ) ) + return FALSE; + } + + for( int iRing = 0; iRing < poPolygon->getNumInteriorRings(); iRing++ ) + { + OGRLinearRing *poRing = poPolygon->getInteriorRing(iRing); + + if( !OGR2ILIGeometryAppend( poRing, fp, NULL, "" ) ) + return FALSE; + } + if( iliGeomType == "Surface" || iliGeomType == "Area" ) + { + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + //VSIFPrintfL(fp, "\n"); + } + if (attrname) VSIFPrintfL(fp, "\n", attrname); + } + +/* -------------------------------------------------------------------- */ +/* MultiPolygon */ +/* -------------------------------------------------------------------- */ + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon + || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString + || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint + || wkbFlatten(poGeometry->getGeometryType()) == wkbGeometryCollection ) + { + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeometry; + int iMember; + + if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon ) + { + } + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString ) + { + } + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint ) + { + } + else + { + } + + for( iMember = 0; iMember < poGC->getNumGeometries(); iMember++) + { + OGRGeometry *poMember = poGC->getGeometryRef( iMember ); + + if( !OGR2ILIGeometryAppend( poMember, fp, NULL, "" ) ) + return FALSE; + } + + } + + else + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRILI2Layer::ICreateFeature( OGRFeature *poFeature ) { + static char szTempBuffer[80]; + const char* tid; + int iField = 0; + if (poFeatureDefn->GetFieldCount() && EQUAL(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), "TID")) + { + tid = poFeature->GetFieldAsString(0); + ++iField; + } + else + { + sprintf( szTempBuffer, CPL_FRMT_GIB, poFeature->GetFID() ); + tid = szTempBuffer; + } + + VSILFILE* fp = poDS->GetOutputFP(); + if (fp == NULL) + return CE_Failure; + + VSIFPrintfL(fp, "<%s TID=\"%s\">\n", poFeatureDefn->GetName(), tid); + + // Write out Geometries + for( int iGeomField = 0; iGeomField < poFeatureDefn->GetGeomFieldCount(); iGeomField++ ) + { + OGRGeomFieldDefn *poFieldDefn = poFeatureDefn->GetGeomFieldDefn(iGeomField); + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(iGeomField); + if( poGeom != NULL ) + { + CPLString iliGeomType = GetIliGeomType(poFieldDefn->GetNameRef()); + OGR2ILIGeometryAppend(poGeom, fp, poFieldDefn->GetNameRef(), iliGeomType); + } + } + + // Write all "set" fields. + for( ; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + + OGRFieldDefn *poField = poFeatureDefn->GetFieldDefn( iField ); + + if( poFeature->IsFieldSet( iField ) ) + { + const char *pszRaw = poFeature->GetFieldAsString( iField ); + VSIFPrintfL(fp, "<%s>%s\n", poField->GetNameRef(), pszRaw, poField->GetNameRef()); + } + } + + VSIFPrintfL(fp, "\n", poFeatureDefn->GetName()); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRILI2Layer::TestCapability( CPL_UNUSED const char * pszCap ) { + if( EQUAL(pszCap,OLCCurveGeometries) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRILI2Layer::CreateField( OGRFieldDefn *poField, CPL_UNUSED int bApproxOK ) { + poFeatureDefn->AddFieldDefn( poField ); + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/GNUmakefile new file mode 100644 index 000000000..ab5d8c99d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/GNUmakefile @@ -0,0 +1,14 @@ + +include ../../../GDALmake.opt + +OBJ = ogringresdriver.o ogringresstatement.o ogringresdatasource.o \ + ogringrestablelayer.o ogringreslayer.o ogringresresultlayer.o + +# ogringresresultlayer.o + +CPPFLAGS := -I.. -I../.. $(INGRES_INC) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/drv_ingres.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/drv_ingres.html new file mode 100644 index 000000000..15f0ff43e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/drv_ingres.html @@ -0,0 +1,144 @@ + + +INGRES + + + + +

    INGRES

    + +

    This driver implements read and write access for spatial data in +INGRES database tables. This +functionality was introduced in GDAL/OGR 1.6.0.

    + +

    When opening a database, it's name should be specified in the form +"@driver=ingres,[host=host, instance=instance],dbname=[vnode::]dbname +[,options]". where the options can +include comma seperated +items like "host=*ip_address*","instance=*instance*", "username=*userid*", "password=*password*", +"effuser=*database_user*", "dbpwd=*database_passwd*", "timeout=*timeout*", +"tables=table1/table2".

    + +

    The driver and dbname values are required, while +the rest are optional. If username and password are not provided an attempt +is made to authenticate as the current OS user.

    + +

    If the host and instance options are both specified, +the username and password *must* be supplied as it creates a temporary +dynamic vnode connection. +The default protocal is TCP/IP. If any other protocal is expected to be used, a pre-built vnode is preferable. +If vnode and these two options are passed at the same time, an error will occur. +

    + +

    The option effuser and dbpwd are mapped to the real user name and password needs to be authorized in dbms, compared to the +username and password which are used for OS level authorization.

    + +Examples: +
    +  @driver=ingres,host=192.168.0.1, instance=II, dbname=test,userid=warmerda,password=test,effuser=frank, dbpwd=123, tables=usa/canada
    +  
    +  @driver=ingres,host=192.168.0.1, instance=II, dbname=test,userid=warmerda,password=test,tables=usa/canada
    +  
    +  @driver=ingres,dbname=test,userid=warmerda,password=test,tables=usa/canada
    +
    +  @driver=ingres,dbname=test,userid=warmerda,password=test,tables=usa/canada
    +  
    +  @driver=ingres,dbname=test,userid=warmerda,password=test,tables=usa/canada
    +  
    +  @driver=ingres,dbname=server::mapping
    +  
    +  @driver=ingres,dbname=mapping
    +
    + +

    +If the tables list is not provided, an attempt is made to enumerate all +non-system tables as layers, otherwise only the listed tables are represented +as layers. This option is primarily useful when a database has a lot of +tables, and scanning all their schemas would take a significant amount of +time.

    + +

    If an integer field exists in a table that is named "ogr_fid" it will be +used as the FID, otherwise FIDs will be assigned sequentially. This can +result in different FIDs being assigned to a given record/feature depending +on the spatial and attribute query filters in effect at a given time.

    + +

    By default, SQL statements are passed directly to the INGRES database +engine. It's also possible to request the driver to handle SQL commands +with OGR SQL engine, +by passing "OGRSQL" string to the ExecuteSQL() +method, as name of the SQL dialect.

    + +

    +The INGRES driver supports OGC SFSQL 1.1 compliant spatial types and functions, +including types: POINT, LINESTRING, POLYGON, MULTI* versions, and GEOMETRYCOLLECTION. +

    + +

    Caveats

    +
      +
    • No fast spatial index is used when reading, so spatial filters are +implemented by reading and parsing all records, and then discarding those +that do not satisfy the spatial filter.

      +

    + +

    Creation Issues

    + +The INGRES driver does not support creation of new datasets (a database +within INGRES), but it does allow creation of new layers (tables) within an +existing database instance.

    + +

      + +
    • +The INGRES driver makes no allowances for character encodings at this time.

      + +

    • +The INGRES driver is not transactional at this time.

      +

    + +

    Layer Creation Options

    + +
      +
    • + OVERWRITE: This may be "YES" to force an existing layer of the + desired name to be destroyed before creating the requested layer. +
    • +
    • + LAUNDER: This may be "YES" to force new fields created on this + layer to have their field names "laundered" into a form more + compatible with MySQL. This converts to lower case and converts + some special characters like "-" and "#" to "_". If "NO" exact names + are preserved. The default value is "YES". +
    • +
    • + PRECISION: This may be "TRUE" to attempt to preserve field + widths and precisions for the creation and reading of MySQL layers. + The default value is "TRUE". +
    • +
    • + GEOMETRY_NAME: This option specifies the name of the + geometry column. The default value is "SHAPE". +
    • +
    • + INGRES_FID: This option specifies the name of the FID column. + The default value is "OGR_FID" +
    • +
    • + GEOMETRY_TYPE: Specifies the object type for the geometry + column. It may be one of POINT, LSEG, LINE, LONG LINE, POLYGON, or + LONG POLYGON. By default POINT, LONG LINE or LONG POLYGON are used + depending on the layer type. +
    • +
    + +

    Older Versions

    +

    The INGRES GDAL driver also includes support for old INGRES spatial types, + but these are not enabled by default. It enable these, the input configure + script needs to include pointers to libraries used by the older version: +

    INGRES_LIB="-L$II_SYSTEM/ingres/lib \
    +         $II_SYSTEM/ingres/lib/iiclsadt.o \
    +         $II_SYSTEM/ingres/lib/iiuseradt.o \
    +         -liiapi.1 -lcompat.1 -lq.1 -lframe.1"
    +
    +

    + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ingres.txt b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ingres.txt new file mode 100644 index 000000000..84c10dc8d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ingres.txt @@ -0,0 +1,27 @@ +Created a user database named "warmerda" + + +--- + + +Geometry data type mapping + + +INGRES WKT +------ --- + +point <-> point +ipoint -> point (integer) +box -> polygon +ibox -> polygon +lseg -> linestring (only 2 points) +ilseg -> linestring +line -> linestring (max 124 points) +iline -> linestring +long line <-> linestring +polygon -> polygon (max 124 points, no inner rings) +ipolygon -> polygon +long polygon -> polygon (no inner rings) +circle -> polygon (approximated) +icircle -> polygon + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/makefile.vc new file mode 100644 index 000000000..d4fe6b3fb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/makefile.vc @@ -0,0 +1,31 @@ + +OBJ = ogringresdriver.obj ogringresdatasource.obj ogringreslayer.obj \ + ogringrestablelayer.obj ogringresresultlayer.obj \ + ogringresstatement.obj + +PLUGIN_DLL = ogr_Ingres.dll + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. -I$(INGRES_INC_DIR) + +default: $(OBJ) + +$(PLUGIN_DLL): $(OBJ) + link /dll /out:$(PLUGIN_DLL) $(OBJ) $(GDALLIB) $(INGRES_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +plugin: $(PLUGIN_DLL) + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) + +clean: + -del *.lib + -del *.obj *.pdb *.exp + -del *.exe + -del *.dll + -del *.manifest diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogr_ingres.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogr_ingres.h new file mode 100644 index 000000000..6b2805ef9 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogr_ingres.h @@ -0,0 +1,309 @@ +/****************************************************************************** + * $Id: ogr_ingres.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Declarations for Ingres OGR Driver Classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef OGR_INGRES_H_INCLUDED +#define OGR_INGRES_H_INCLUDED + +#include +#include "ogrsf_frmts.h" + +class OGRIngresDataSource; + +/************************************************************************/ +/* OGRIngresStatement */ +/************************************************************************/ + +class OGRIngresStatement +{ +public: + II_PTR hConn; + II_PTR hStmt; + II_PTR hTransaction; + + IIAPI_GETDESCRPARM getDescrParm; + IIAPI_GETCOLPARM getColParm; + IIAPI_DATAVALUE *pasDataBuffer; + IIAPI_GETQINFOPARM queryInfo; + + GByte *pabyWrkBuffer; + char **papszFields; + + int bDebug; + + int bHaveParm; + IIAPI_DT_ID eParmType; + int nParmLen; + GByte *pabyParmData; + + OGRIngresStatement( II_PTR hConn ); + ~OGRIngresStatement(); + + void addInputParameter( IIAPI_DT_ID eDType, int nLength, GByte *pabyData ); + + int ExecuteSQL( const char * ); + + char **GetRow(); + void DumpRow( FILE * ); + static void ReportError( IIAPI_GENPARM *, const char * = NULL ); + + int IsColumnLong(int iCol); + void ClearDynamicColumns(); + void Close(); + int SendParms(); +}; + +/************************************************************************/ +/* OGRIngresLayer */ +/************************************************************************/ + +class OGRIngresLayer : public OGRLayer +{ + protected: + OGRFeatureDefn *poFeatureDefn; + + // Layer spatial reference system, and srid. + OGRSpatialReference *poSRS; + int nSRSId; + + int iNextShapeId; + + OGRIngresDataSource *poDS; + + CPLString osQueryStatement; + + int nResultOffset; + + CPLString osGeomColumn; + CPLString osIngresGeomType; + + CPLString osFIDColumn; + + OGRIngresStatement *poResultSet; /* stmt */ + + int FetchSRSId(OGRFeatureDefn *poDefn); + OGRGeometry *TranslateGeometry( const char * ); + + public: + OGRIngresLayer(); + virtual ~OGRIngresLayer(); + + virtual void ResetReading(); + + virtual OGRFeature *GetNextFeature(); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int TestCapability( const char * ); + + virtual const char *GetFIDColumn(); + virtual const char *GetGeometryColumn(); + + /* custom methods */ + virtual OGRFeature *RecordToFeature( char **papszRow ); + virtual OGRFeature *GetNextRawFeature(); +}; + +/************************************************************************/ +/* OGRIngresTableLayer */ +/************************************************************************/ + +class OGRIngresTableLayer : public OGRIngresLayer +{ + int bUpdateAccess; + + OGRFeatureDefn *ReadTableDefinition(const char *); + + void BuildWhere(void); + char *BuildFields(void); + void BuildFullQueryStatement(void); + + CPLString osQuery; + CPLString osWHERE; + + int bLaunderColumnNames; + int bPreservePrecision; + + OGRErr PrepareOldStyleGeometry( OGRGeometry*, CPLString& ); + OGRErr PrepareNewStyleGeometry( OGRGeometry*, CPLString& ); + + public: + OGRIngresTableLayer( OGRIngresDataSource *, + const char * pszName, + int bUpdate, int nSRSId = -2 ); + ~OGRIngresTableLayer(); + + OGRErr Initialize(const char* pszTableName); + +// virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + virtual void ResetReading(); +// virtual GIntBig GetFeatureCount( int ); + + void SetSpatialFilter( OGRGeometry * ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + + void SetLaunderFlag( int bFlag ) + { bLaunderColumnNames = bFlag; } + void SetPrecisionFlag( int bFlag ) + { bPreservePrecision = bFlag; } + + virtual int TestCapability( const char * ); +// virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); +}; + +/************************************************************************/ +/* OGRIngresResultLayer */ +/************************************************************************/ + +class OGRIngresResultLayer : public OGRIngresLayer +{ + void BuildFullQueryStatement(void); + + char *pszRawStatement; + + // Layer srid. + int nSRSId; + + int nFeatureCount; + + public: + OGRIngresResultLayer( OGRIngresDataSource *, + const char * pszRawStatement, + OGRIngresStatement *hStmt ); + virtual ~OGRIngresResultLayer(); + + OGRFeatureDefn *ReadResultDefinition(); + + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); +}; + +/************************************************************************/ +/* OGRIngresDataSource */ +/************************************************************************/ + +class OGRIngresDataSource : public OGRDataSource +{ + OGRIngresLayer **papoLayers; + int nLayers; + + char *pszName; + + int bDSUpdate; + + II_PTR hConn; + + int DeleteLayer( int iLayer ); + + // We maintain a list of known SRID to reduce the number of trips to + // the database to get SRSes. + int nKnownSRID; + int *panSRID; + OGRSpatialReference **papoSRS; + + OGRIngresLayer *poActiveLayer; /* this layer has active transaction */ + + int bNewIngres; /* TRUE if new spatial library */ + + public: + OGRIngresDataSource(); + ~OGRIngresDataSource(); + + II_PTR GetConn() { return hConn; } + + + int FetchSRSId( OGRSpatialReference * poSRS ); + + OGRSpatialReference *FetchSRS( int nSRSId ); + + OGRErr InitializeMetadataTables(); + + int Open( const char *pszFullName, + char **papszOptions, int bUpdate ); + int OpenTable( const char *, int bUpdate ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + + int TestCapability( const char * ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + // nonstandard + + char *LaunderName( const char * ); + + void EstablishActiveLayer( OGRIngresLayer * ); + int IsNewIngres(); +}; + +/************************************************************************/ +/* OGRIngresDriver */ +/************************************************************************/ + +class OGRIngresDriver : public OGRSFDriver +{ + char **ParseWrappedName( const char * ); + + public: + ~OGRIngresDriver(); + + const char *GetName(); + OGRDataSource *Open( const char *, int ); + virtual OGRDataSource *CreateDataSource( const char *pszName, + char ** = NULL ); + int TestCapability( const char * ); +}; + + +#endif /* ndef OGR_PG_H_INCLUDED */ + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringresdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringresdatasource.cpp new file mode 100644 index 000000000..29479084a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringresdatasource.cpp @@ -0,0 +1,1100 @@ +/****************************************************************************** + * $Id: ogringresdatasource.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRIngresDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + +#include "ogr_ingres.h" + +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogringresdatasource.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* SetConnParam() */ +/* */ +/* Set Connection Parameters */ +/************************************************************************/ +IIAPI_STATUS +SetConnParam(II_PTR *connHandle, + II_LONG paramID, + const II_PTR paramValue) +{ + IIAPI_SETCONPRMPARM setconnParm; + IIAPI_WAITPARM waitParm = { -1 }; + + setconnParm.sc_genParm.gp_callback = NULL; + setconnParm.sc_genParm.gp_closure = NULL; + setconnParm.sc_connHandle = *connHandle; + setconnParm.sc_paramID = paramID; + setconnParm.sc_paramValue = paramValue; + + IIapi_setConnectParam(&setconnParm); + + while( setconnParm.sc_genParm.gp_completed == FALSE ) + IIapi_wait( &waitParm ); + + if (setconnParm.sc_genParm.gp_errorHandle) + { + OGRIngresStatement::ReportError( &(setconnParm.sc_genParm), + "Failed to set OpenAPI connection para." ); + return IIAPI_ST_FAILURE; + } + else + { + /* save the handle */ + *connHandle = setconnParm.sc_connHandle; + } + + return (setconnParm.sc_genParm.gp_status); +} + +/************************************************************************/ +/* OGRIngresDataSource() */ +/************************************************************************/ + +OGRIngresDataSource::OGRIngresDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + hConn = 0; + + nKnownSRID = 0; + panSRID = NULL; + papoSRS = NULL; + poActiveLayer = NULL; +} + +/************************************************************************/ +/* ~OGRIngresDataSource() */ +/************************************************************************/ + +OGRIngresDataSource::~OGRIngresDataSource() + +{ + int i; + + CPLFree( pszName ); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + +#ifdef notdef + if( hConn != NULL ) + ingres_close( hConn ); +#endif + + for( i = 0; i < nKnownSRID; i++ ) + { + if( papoSRS[i] != NULL ) + papoSRS[i]->Release(); + } + CPLFree( panSRID ); + CPLFree( papoSRS ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRIngresDataSource::Open( const char *pszFullName, + char **papszOptions, int bUpdate ) + + +{ + CPLAssert( nLayers == 0 ); + + #define MAX_TARGET_STRING_LENGTH 512 + char pszDBTarget[MAX_TARGET_STRING_LENGTH]; + +/* -------------------------------------------------------------------- */ +/* Verify we have a dbname, this parameter is required. */ +/* -------------------------------------------------------------------- */ + const char *pszDBName = CSLFetchNameValue(papszOptions,"dbname"); + + if( pszDBName == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "No DBNAME item provided in INGRES datasource name." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Do we have a table list? */ +/* -------------------------------------------------------------------- */ + char **papszTableNames = NULL; + const char *pszTables = CSLFetchNameValue(papszOptions,"tables"); + + if( pszTables != NULL ) + papszTableNames = CSLTokenizeStringComplex(pszTables,"/",TRUE,FALSE); + +/* -------------------------------------------------------------------- */ +/* Add support to dynamic vnode if passed */ +/* -------------------------------------------------------------------- */ + const char *pszHost = CSLFetchNameValue(papszOptions,"host"); + if (pszHost) + { + const char *pszInstance = CSLFetchNameValue(papszOptions,"instance"); + if (pszInstance == NULL || strlen(pszInstance) != 2) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "instance name must be specified with host." ); + return FALSE; + } + + /* + ** make sure the user name and password are passed too, + ** note it could not be zero length. + */ + const char *pszUsername = CSLFetchNameValue(papszOptions,"username"); + const char *pszPassword = CSLFetchNameValue(papszOptions,"password"); + + if (pszUsername == NULL || strlen(pszUsername) == 0) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "user name must be specified in dynamic vnode." ); + return FALSE; + } + + if (pszPassword == NULL || strlen(pszPassword) == 0) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "password must be specified in dynamic vnode." ); + return FALSE; + } + + /* + ** construct the vnode string, like : + ** @host,protocol,port[;attribute=value{;attribute=value}][[user,password]], + ** visit for detail + ** http://docs.actian.com/ingres/10.0/command-reference-guide/1207-dynamic-vnode-specificationconnect-to-remote-node + */ + sprintf(pszDBTarget, "@%s,%s,%s;%s[%s,%s]::%s ", + pszHost, /* host, compute name or IP address */ + "TCP_IP", /* protocal, default with TCP/IP */ + pszInstance, /* instance Name */ + "" , /* option, Null */ + pszUsername, /* user name, could not be empty */ + pszPassword, /* pwd */ + pszDBName /* database name */ + ); + + CPLDebug("INGRES", pszDBTarget); + } + else + { + /* Remain the database name */ + strcpy(pszDBTarget, pszDBName); + } + +/* -------------------------------------------------------------------- */ +/* Initialize the Ingres API. Should we only do this once per */ +/* program run? Really we should also try to terminate the api */ +/* on program exit. */ +/* -------------------------------------------------------------------- */ + IIAPI_INITPARM initParm; + + initParm.in_version = IIAPI_VERSION_1; + initParm.in_timeout = -1; + IIapi_initialize( &initParm ); + +/* -------------------------------------------------------------------- */ +/* check effective user and db password */ +/* -------------------------------------------------------------------- */ + hConn = NULL; + const char *pszEffuser = CSLFetchNameValue(papszOptions,"effuser"); + const char *pszDBpwd = CSLFetchNameValue(papszOptions,"dbpwd"); + if ( pszEffuser + && strlen(pszEffuser) > 0 + && pszDBpwd + && strlen(pszDBpwd) > 0 ) + { + if (SetConnParam(&hConn, IIAPI_CP_EFFECTIVE_USER, + (II_PTR)pszEffuser) != IIAPI_ST_SUCCESS + || SetConnParam(&hConn, IIAPI_CP_DBMS_PASSWORD, + (II_PTR)pszDBpwd) != IIAPI_ST_SUCCESS ) + { + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Try to connect to the database. */ +/* -------------------------------------------------------------------- */ + IIAPI_CONNPARM connParm; + IIAPI_WAITPARM waitParm = { -1 }; + + memset( &connParm, 0, sizeof(connParm) ); + connParm.co_genParm.gp_callback = NULL; + connParm.co_genParm.gp_closure = NULL; + connParm.co_target = (II_CHAR *) pszDBTarget; + connParm.co_connHandle = hConn; + connParm.co_tranHandle = NULL; + connParm.co_username = + (II_CHAR*) CSLFetchNameValue(papszOptions,"username"); + connParm.co_password = + (II_CHAR*)CSLFetchNameValue(papszOptions,"password"); + connParm.co_timeout = -1; + + if( CSLFetchNameValue(papszOptions,"timeout") != NULL ) + connParm.co_timeout = atoi(CSLFetchNameValue(papszOptions,"timeout")); + + IIapi_connect( &connParm ); + + while( connParm.co_genParm.gp_completed == FALSE ) + IIapi_wait( &waitParm ); + + hConn = connParm.co_connHandle; + + if( connParm.co_genParm.gp_status != IIAPI_ST_SUCCESS + || hConn == NULL ) + { + OGRIngresStatement::ReportError( &(connParm.co_genParm), + "Failed to connect to Ingres database." ); + return FALSE; + } + + pszName = CPLStrdup( pszFullName ); + + bDSUpdate = bUpdate; + + // Check for new or old Ingres spatial library + { + OGRIngresStatement oStmt( hConn ); + + if( oStmt.ExecuteSQL("SELECT COUNT(*) FROM iicolumns WHERE table_name = 'iiattribute' AND column_name = 'attgeomtype'" ) ) + { + char **papszFields; + while( (papszFields = oStmt.GetRow()) ) + { + CPLString osCount = papszFields[0]; + if( osCount[0] == '0' ) + { + bNewIngres = FALSE; + } + else + { + bNewIngres = TRUE; + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Get a list of available tables. */ +/* -------------------------------------------------------------------- */ + if( papszTableNames == NULL ) + { + OGRIngresStatement oStmt( hConn ); + + if( oStmt.ExecuteSQL( "select table_name from iitables where system_use = 'U' and table_name not like 'iietab_%'" ) ) + { + char **papszFields; + while( (papszFields = oStmt.GetRow()) ) + { + CPLString osTableName = papszFields[0]; + osTableName.Trim(); + papszTableNames = CSLAddString( papszTableNames, + osTableName ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Get the schema of the available tables. */ +/* -------------------------------------------------------------------- */ + int iRecord; + + for( iRecord = 0; + papszTableNames != NULL && papszTableNames[iRecord] != NULL; + iRecord++ ) + { + OpenTable( papszTableNames[iRecord], bUpdate ); + } + + CSLDestroy( papszTableNames ); + + return TRUE; +} + +/************************************************************************/ +/* OpenTable() */ +/************************************************************************/ + +int OGRIngresDataSource::OpenTable( const char *pszNewName, int bUpdate ) + +{ +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRIngresTableLayer *poLayer; + OGRErr eErr; + + poLayer = new OGRIngresTableLayer( this, pszNewName, bUpdate ); + eErr = poLayer->Initialize(pszNewName); + if (eErr == OGRERR_FAILURE) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRIngresLayer **) + CPLRealloc( papoLayers, sizeof(OGRIngresLayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + + return TRUE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRIngresDataSource::TestCapability( const char * pszCap ) + +{ + + if( EQUAL(pszCap, ODsCCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap, ODsCDeleteLayer)) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRIngresDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + + +/************************************************************************/ +/* InitializeMetadataTables() */ +/* */ +/* Create the metadata tables (SPATIAL_REF_SYS and */ +/* GEOMETRY_COLUMNS). This method "does no harm" if the tables */ +/* exist and can be called at will. */ +/************************************************************************/ + +OGRErr OGRIngresDataSource::InitializeMetadataTables() + +{ +#ifdef notdef + char szCommand[1024]; + INGRES_RES *hResult; + OGRErr eErr = OGRERR_NONE; + + sprintf( szCommand, "DESCRIBE geometry_columns" ); + if( ingres_query(GetConn(), szCommand ) ) + { + sprintf(szCommand, + "CREATE TABLE geometry_columns " + "( F_TABLE_CATALOG VARCHAR(256), " + "F_TABLE_SCHEMA VARCHAR(256), " + "F_TABLE_NAME VARCHAR(256) NOT NULL," + "F_GEOMETRY_COLUMN VARCHAR(256) NOT NULL, " + "COORD_DIMENSION INT, " + "SRID INT," + "TYPE VARCHAR(256) NOT NULL)"); + if( ingres_query(GetConn(), szCommand ) ) + { + ReportError( szCommand ); + eErr = OGRERR_FAILURE; + } + else + CPLDebug("INGRES","Creating geometry_columns metadata table"); + + } + + // make sure to attempt to free results of successful queries + hResult = ingres_store_result( GetConn() ); + if( hResult != NULL ) + { + ingres_free_result( hResult ); + hResult = NULL; + } + + sprintf( szCommand, "DESCRIBE spatial_ref_sys" ); + if( ingres_query(GetConn(), szCommand ) ) + { + sprintf(szCommand, + "CREATE TABLE spatial_ref_sys " + "(SRID INT NOT NULL, " + "AUTH_NAME VARCHAR(256), " + "AUTH_SRID INT, " + "SRTEXT VARCHAR(2048))"); + if( ingres_query(GetConn(), szCommand ) ) + { + ReportError( szCommand ); + eErr = OGRERR_FAILURE; + } + else + CPLDebug("INGRES","Creating spatial_ref_sys metadata table"); + + } + + // make sure to attempt to free results of successful queries + hResult = ingres_store_result( GetConn() ); + if( hResult != NULL ) + { + ingres_free_result( hResult ); + hResult = NULL; + } + + return eErr; +#endif + return OGRERR_NONE; +} + +/************************************************************************/ +/* FetchSRS() */ +/* */ +/* Return a SRS corresponding to a particular id. Note that */ +/* reference counting should be honoured on the returned */ +/* OGRSpatialReference, as handles may be cached. */ +/************************************************************************/ + +OGRSpatialReference *OGRIngresDataSource::FetchSRS( int nId ) +{ + char szCommand[1024]; + char **papszRow; + OGRIngresStatement oStatement(GetConn()); + + if( nId < 0 ) + return NULL; + + /* + * Only the new Ingres Geospatial library + */ + if(IsNewIngres() == FALSE) + return NULL; + +/* -------------------------------------------------------------------- */ +/* First, we look through our SRID cache, is it there? */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nKnownSRID; i++ ) + { + if( panSRID[i] == nId ) + return papoSRS[i]; + } + + OGRSpatialReference *poSRS = NULL; + + sprintf( szCommand, + "SELECT srtext FROM spatial_ref_sys WHERE srid = %d", + nId ); + + oStatement.ExecuteSQL(szCommand); + + char *pszWKT = NULL; + papszRow = NULL; + + + papszRow = oStatement.GetRow(); + + if( papszRow != NULL) + { + if(papszRow[0] != NULL ) + { + //VARCHAR uses the first two bytes for length + pszWKT = &papszRow[0][2]; + } + } + + poSRS = new OGRSpatialReference(); + if( pszWKT == NULL || poSRS->importFromWkt( &pszWKT ) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Add to the cache. */ +/* -------------------------------------------------------------------- */ + panSRID = (int *) CPLRealloc(panSRID,sizeof(int) * (nKnownSRID+1) ); + papoSRS = (OGRSpatialReference **) + CPLRealloc(papoSRS, sizeof(void*) * (nKnownSRID + 1) ); + panSRID[nKnownSRID] = nId; + papoSRS[nKnownSRID] = poSRS; + + return poSRS; +} + + + +/************************************************************************/ +/* FetchSRSId() */ +/* */ +/* Fetch the id corresponding to an SRS, and if not found, add */ +/* it to the table. */ +/************************************************************************/ + +int OGRIngresDataSource::FetchSRSId( OGRSpatialReference * poSRS ) + +{ + char **papszRow; + char szCommand[10000]; + char *pszWKT = NULL; + char *pszProj4 = NULL; + const char *pszAuthName; + const char *pszAuthID; + int nSRSId; + + if( poSRS == NULL ) + return -1; + + /* -------------------------------------------------------------------- */ + /* If it is a EPSG Spatial Reference, search with special type */ + /* -------------------------------------------------------------------- */ + pszAuthName = poSRS->GetAuthorityName(NULL); + pszAuthID = poSRS->GetAuthorityCode(NULL); + + if (pszAuthName && pszAuthID && EQUAL(pszAuthName, "EPSG")) + { + sprintf( szCommand, + "SELECT srid FROM spatial_ref_sys WHERE auth_name = 'EPSG' and auth_srid= %s", + pszAuthID ); + + + OGRIngresStatement oStateSRID(GetConn()); + oStateSRID.ExecuteSQL(szCommand); + + papszRow = oStateSRID.GetRow(); + if (papszRow == NULL) + { + CPLDebug("INGRES", "No rows exists matching EPSG:%s in spatial_ref_sys", pszAuthID ); + } + else if( papszRow != NULL && papszRow[0] != NULL ) + { + nSRSId = *((II_INT4 *)papszRow[0]); + return nSRSId; + } + } + + /* -------------------------------------------------------------------- */ + /* Translate SRS to WKT. */ + /* -------------------------------------------------------------------- */ + if( poSRS->exportToWkt( &pszWKT ) != OGRERR_NONE ) + return -1; + + /* -------------------------------------------------------------------- */ + /* Translate SRS to Proj4. */ + /* -------------------------------------------------------------------- */ + if( poSRS->exportToProj4( &pszProj4 ) != OGRERR_NONE ) + return -1; + + CPLAssert( strlen(pszProj4) + strlen(pszWKT) < sizeof(szCommand) - 500 ); + +/* -------------------------------------------------------------------- */ +/* Try to find in the existing table. */ +/* -------------------------------------------------------------------- */ + sprintf( szCommand, + "SELECT srid FROM spatial_ref_sys WHERE srtext = '%s'", + pszWKT ); + + { + OGRIngresStatement oStateSRID(GetConn()); + oStateSRID.ExecuteSQL(szCommand); + + papszRow = oStateSRID.GetRow(); + if (papszRow == NULL) + { + CPLDebug("INGRES", "No rows exist currently exist in spatial_ref_sys"); + } + else if( papszRow != NULL && papszRow[0] != NULL ) + { + nSRSId = *((II_INT4 *)papszRow[0]); + return nSRSId; + } + } + +/* -------------------------------------------------------------------- */ +/* Get the current maximum srid in the srs table. */ +/* -------------------------------------------------------------------- */ + sprintf( szCommand, + "SELECT MAX(srid) FROM spatial_ref_sys"); + + { + OGRIngresStatement oStateMaxSRID(GetConn()); + oStateMaxSRID.ExecuteSQL(szCommand); + papszRow = oStateMaxSRID.GetRow(); + + // The spatial reference created by user must be greater than + // the 10000. The below are system maintained. +#define USER_DEFINED_SR_START 10000 + if( papszRow != NULL && papszRow[0] != NULL ) + { + // if there is no row in spatial reference, a random value + // will be return, how to judge? + nSRSId = *((II_INT4 *)papszRow[0]) ; + if (nSRSId <= 0) + { + nSRSId = USER_DEFINED_SR_START+1; + } + else + { + nSRSId = *((II_INT4 *)papszRow[0]) + 1; + } + + } + else + nSRSId = USER_DEFINED_SR_START+1; + + } + + if(pszAuthName == NULL || strlen(pszAuthName) == 0) + { + poSRS->AutoIdentifyEPSG(); + pszAuthName = poSRS->GetAuthorityName(NULL); + if (pszAuthName != NULL && EQUAL(pszAuthName, "EPSG")) + { + const char* pszAuthorityCode = poSRS->GetAuthorityCode(NULL); + if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 ) + { + /* Import 'clean' SRS */ + poSRS->importFromEPSG( atoi(pszAuthorityCode) ); + + pszAuthName = poSRS->GetAuthorityName(NULL); + pszAuthID = poSRS->GetAuthorityCode(NULL); + } + } + } +/* -------------------------------------------------------------------- */ +/* Try adding the SRS to the SRS table. */ +/* -------------------------------------------------------------------- */ + if(pszAuthName != NULL) + { + sprintf( szCommand, + "INSERT INTO spatial_ref_sys (srid,auth_name,auth_srid," + "srtext,proj4text) VALUES (%d,'%s',%s,'%s','%s')", + nSRSId, pszAuthName, pszAuthID, pszWKT, pszProj4 ); + } + else + { + sprintf( szCommand, + "INSERT INTO spatial_ref_sys (srid,auth_name,auth_srid," + "srtext,proj4text) VALUES (%d,NULL,NULL,'%s','%s')", + nSRSId, pszWKT, pszProj4 ); + } + + { + OGRIngresStatement oStateNewSRID(GetConn()); + if(!oStateNewSRID.ExecuteSQL(szCommand)) + { + CPLDebug("INGRES", "Failed to create new spatial reference system"); + } + } + + return nSRSId; +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRIngresDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + + if( poSpatialFilter != NULL ) + { + CPLDebug( "OGR_INGRES", + "Spatial filter ignored for now in OGRIngresDataSource::ExecuteSQL()" ); + } + +/* -------------------------------------------------------------------- */ +/* Execute the statement. */ +/* -------------------------------------------------------------------- */ + EstablishActiveLayer( NULL ); + + OGRIngresStatement *poStatement = new OGRIngresStatement( hConn ); + + if( !poStatement->ExecuteSQL( pszSQLCommand ) ) + { + delete poStatement; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we have a tuple result? If so, instantiate a results */ +/* layer for it. */ +/* -------------------------------------------------------------------- */ + + OGRIngresResultLayer *poLayer = NULL; + + poLayer = new OGRIngresResultLayer( this, pszSQLCommand, poStatement ); + EstablishActiveLayer( poLayer ); + + return poLayer; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRIngresDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + if( poActiveLayer == poLayer ) + poActiveLayer = NULL; + + delete poLayer; +} + +/************************************************************************/ +/* LaunderName() */ +/************************************************************************/ + +char *OGRIngresDataSource::LaunderName( const char *pszSrcName ) + +{ + char *pszSafeName = CPLStrdup( pszSrcName ); + int i; + + for( i = 0; pszSafeName[i] != '\0'; i++ ) + { + pszSafeName[i] = (char) tolower( pszSafeName[i] ); + if( pszSafeName[i] == '-' || pszSafeName[i] == '#' ) + pszSafeName[i] = '_'; + } + + return pszSafeName; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +int OGRIngresDataSource::DeleteLayer( int iLayer) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Blow away our OGR structures related to the layer. This is */ +/* pretty dangerous if anything has a reference to this layer! */ +/* -------------------------------------------------------------------- */ + CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName(); + + CPLDebug( "INGRES", "DeleteLayer(%s)", osLayerName.c_str() ); + + delete papoLayers[iLayer]; + memmove( papoLayers + iLayer, papoLayers + iLayer + 1, + sizeof(void *) * (nLayers - iLayer - 1) ); + nLayers--; + +/* -------------------------------------------------------------------- */ +/* Remove from the database. */ +/* -------------------------------------------------------------------- */ + char szCommand[1024]; + OGRIngresStatement oStmt( hConn ); + + sprintf( szCommand, + "DROP TABLE %s ", + osLayerName.c_str() ); + + if( oStmt.ExecuteSQL( szCommand ) ) + { + CPLDebug("INGRES","Dropped table %s.", osLayerName.c_str()); + return OGRERR_NONE; + } + else + return OGRERR_FAILURE; +} + + + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRIngresDataSource::ICreateLayer( const char * pszLayerNameIn, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ) + +{ + const char *pszGeometryType = NULL; + const char *pszGeomColumnName; + const char *pszExpectedFIDName; + + char *pszLayerName; + int nDimension = 3; // Ingres only supports 2d currently + + + if( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) ) + pszLayerName = LaunderName( pszLayerNameIn ); + else + pszLayerName = CPLStrdup( pszLayerNameIn ); + + if( wkbFlatten(eType) == eType ) + nDimension = 2; + + CPLDebug("INGRES","Creating layer %s.", pszLayerName); + +/* -------------------------------------------------------------------- */ +/* Do we already have this layer? If so, should we blow it */ +/* away? */ +/* -------------------------------------------------------------------- */ + + int iLayer; + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName,papoLayers[iLayer]->GetLayerDefn()->GetName()) ) + { + + if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL + && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") ) + { + DeleteLayer( iLayer ); + } + else + { + CPLFree( pszLayerName ); + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + pszLayerName ); + return NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* What do we want to use for geometry and FID columns? */ +/* -------------------------------------------------------------------- */ + pszGeomColumnName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME" ); + if (!pszGeomColumnName) + pszGeomColumnName="SHAPE"; + + pszExpectedFIDName = CSLFetchNameValue( papszOptions, "INGRES_FID" ); + if (!pszExpectedFIDName) + pszExpectedFIDName="OGR_FID"; + + CPLDebug("INGRES","Geometry Column Name %s.", pszGeomColumnName); + CPLDebug("INGRES","FID Column Name %s.", pszExpectedFIDName); + +/* -------------------------------------------------------------------- */ +/* What sort of geometry column do we want to create? */ +/* -------------------------------------------------------------------- */ + pszGeometryType = CSLFetchNameValue( papszOptions, "GEOMETRY_TYPE" ); + + if( pszGeometryType != NULL ) + /* user selected type */; + + else if( wkbFlatten(eType) == wkbPoint ) + pszGeometryType = "POINT"; + + else if( wkbFlatten(eType) == wkbLineString) + { + if( IsNewIngres() ) + { + pszGeometryType = "LINESTRING"; + } + else + { + pszGeometryType = "LONG LINE"; + } + } + + else if( wkbFlatten(eType) == wkbPolygon ) + { + if( IsNewIngres() ) + { + pszGeometryType = "POLYGON"; + } + else + { + pszGeometryType = "LONG POLYGON"; + } + } + + else if( wkbFlatten(eType) == wkbMultiPolygon ) + { + if( IsNewIngres() ) + pszGeometryType = "MULTIPOLYGON"; + } + + else if( wkbFlatten(eType) == wkbMultiLineString ) + { + if( IsNewIngres() ) + pszGeometryType = "MULTILINESTRING"; + } + + else if( wkbFlatten(eType) == wkbMultiPoint ) + { + if( IsNewIngres() ) + pszGeometryType = "MULTIPOINT"; + } + + else if( wkbFlatten(eType) == wkbGeometryCollection ) + { + if( IsNewIngres() ) + pszGeometryType = "GEOMETRYCOLLECTION"; + } + + else if( wkbFlatten(eType) == wkbUnknown ) + { + if( IsNewIngres() ) + // this is also used as the generic geometry type. + pszGeometryType = "GEOMETRYCOLLECTION"; + } + + /* -------------------------------------------------------------------- */ + /* Try to get the SRS Id of this spatial reference system, */ + /* adding tot the srs table if needed. */ + /* -------------------------------------------------------------------- */ + int nSRSId = -1; + + if( poSRS != NULL && IsNewIngres() == TRUE ) + nSRSId = FetchSRSId( poSRS ); + +/* -------------------------------------------------------------------- */ +/* Form table creation command. */ +/* -------------------------------------------------------------------- */ + CPLString osCommand; + + if( pszGeometryType == NULL ) + { + osCommand.Printf( "CREATE TABLE %s ( " + " %s INTEGER )", + pszLayerName, pszExpectedFIDName ); + } + else + { + if(nSRSId != -1) + { + osCommand.Printf( "CREATE TABLE %s (" + " %s INTEGER NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS seq_%s IDENTITY (START WITH 1 INCREMENT BY 1)," + " %s %s SRID %d ) ", + pszLayerName, + pszExpectedFIDName, + pszLayerName, + pszGeomColumnName, + pszGeometryType, + nSRSId); + } + else + { + osCommand.Printf( "CREATE TABLE %s (" + " %s INTEGER NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS seq_%s IDENTITY (START WITH 1 INCREMENT BY 1)," + " %s %s )", + pszLayerName, + pszExpectedFIDName, + pszLayerName, + pszGeomColumnName, + pszGeometryType); + } + } + +/* -------------------------------------------------------------------- */ +/* Execute the create table command. */ +/* -------------------------------------------------------------------- */ + { + OGRIngresStatement oStmt( hConn ); + + if( !oStmt.ExecuteSQL( osCommand ) ) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRIngresTableLayer *poLayer; + OGRErr eErr; + + poLayer = new OGRIngresTableLayer( this, pszLayerName, TRUE, nSRSId ); + eErr = poLayer->Initialize(pszLayerName); + if (eErr == OGRERR_FAILURE) + { + delete poLayer; + return NULL; + } + + poLayer->SetLaunderFlag( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) ); + poLayer->SetPrecisionFlag( CSLFetchBoolean(papszOptions,"PRECISION",TRUE)); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRIngresLayer **) + CPLRealloc( papoLayers, sizeof(OGRIngresLayer *) * (nLayers+1) ); + + papoLayers[nLayers++] = poLayer; + + CPLFree( pszLayerName ); + + return poLayer; +} + +/************************************************************************/ +/* EstablishActiveLayer() */ +/************************************************************************/ + +void OGRIngresDataSource::EstablishActiveLayer( OGRIngresLayer *poNewLayer ) + +{ + if( poActiveLayer != poNewLayer && poActiveLayer != NULL ) + poActiveLayer->ResetReading(); + + poActiveLayer = poNewLayer; +} + +/************************************************************************/ +/* IsNewIngres() */ +/************************************************************************/ + +int OGRIngresDataSource::IsNewIngres() +{ + return bNewIngres; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringresdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringresdriver.cpp new file mode 100644 index 000000000..5ceeb48ee --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringresdriver.cpp @@ -0,0 +1,160 @@ +/****************************************************************************** + * $Id: ogringresdriver.cpp 15463 2008-10-06 18:31:28Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRIngresDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ingres.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogringresdriver.cpp 15463 2008-10-06 18:31:28Z rouault $"); + +/************************************************************************/ +/* ~OGRIngresDriver() */ +/************************************************************************/ + +OGRIngresDriver::~OGRIngresDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRIngresDriver::GetName() + +{ + return "Ingres"; +} + +/************************************************************************/ +/* ParseWrappedName() */ +/************************************************************************/ + +char **OGRIngresDriver::ParseWrappedName( const char *pszEncodedName ) + +{ + if( pszEncodedName[0] != '@' ) + return NULL; + + return CSLTokenizeStringComplex( pszEncodedName+1, ",", TRUE, FALSE ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRIngresDriver::Open( const char * pszFilename, + int bUpdate ) + +{ + OGRIngresDataSource *poDS = NULL; + char **papszOptions = ParseWrappedName( pszFilename ); + const char *pszDriver; + + pszDriver = CSLFetchNameValue( papszOptions, "driver" ); + if( pszDriver != NULL && EQUAL(pszDriver,"ingres") ) + { + poDS = new OGRIngresDataSource(); + + if( !poDS->Open( pszFilename, papszOptions, TRUE ) ) + { + delete poDS; + poDS = NULL; + } + } + + CSLDestroy( papszOptions ); + + return poDS; +} + + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRIngresDriver::CreateDataSource( const char * pszName, + char ** /* papszOptions */ ) + +{ + OGRIngresDataSource *poDS = NULL; + char **papszOpenOptions; + const char *pszDriver; + + papszOpenOptions = ParseWrappedName( pszName ); + + pszDriver = CSLFetchNameValue( papszOpenOptions, "driver" ); + + if( pszDriver != NULL && EQUAL(pszDriver,"ingres") ) + { + poDS = new OGRIngresDataSource(); + if( !poDS->Open( pszName, papszOpenOptions, TRUE ) ) + { + delete poDS; + poDS = NULL; + CPLError( CE_Failure, CPLE_AppDefined, + "Ingres driver doesn't currently support database creation.\n" + "Please create database before using." ); + } + } + + CSLDestroy( papszOpenOptions ); + + return poDS; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRIngresDriver::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + if( EQUAL(pszCap,ODsCDeleteLayer) ) + return TRUE; + if( EQUAL(pszCap,ODrCCreateDataSource) ) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRIngres() */ +/************************************************************************/ + +void RegisterOGRIngres() + +{ + if (! GDAL_CHECK_VERSION("Ingres")) + return; + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(new OGRIngresDriver); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringreslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringreslayer.cpp new file mode 100644 index 000000000..811256aa2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringreslayer.cpp @@ -0,0 +1,654 @@ +/****************************************************************************** + * $Id: ogringreslayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRIngresLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ingres.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogringreslayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRIngresLayer() */ +/************************************************************************/ + +OGRIngresLayer::OGRIngresLayer() + +{ + poDS = NULL; + + iNextShapeId = 0; + nResultOffset = 0; + + poSRS = NULL; + nSRSId = -2; // we haven't even queried the database for it yet. + + poFeatureDefn = NULL; + + poResultSet = NULL; +} + +/************************************************************************/ +/* ~OGRIngresLayer() */ +/************************************************************************/ + +OGRIngresLayer::~OGRIngresLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "Ingres", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + ResetReading(); + + if( poSRS != NULL ) + poSRS->Release(); + + if( poFeatureDefn ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRIngresLayer::ResetReading() + +{ + iNextShapeId = 0; + + if( poResultSet != NULL ) + { + delete poResultSet; + poResultSet = NULL; + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRIngresLayer::GetNextFeature() + +{ + for( ; TRUE; ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +/************************************************************************/ +/* ParseXY() */ +/************************************************************************/ + +static int ParseXY( const char **ppszNext, double *padfXY ) + +{ + int iStartY; + const char *pszNext = *ppszNext; + + for( iStartY = 0; ; iStartY++ ) + { + if( pszNext[iStartY] == '\0' ) + return FALSE; + + if( pszNext[iStartY] == ',' ) + { + iStartY++; + break; + } + } + + padfXY[0] = CPLAtof(pszNext); + padfXY[1] = CPLAtof(pszNext + iStartY); + + int iEnd; + + for( iEnd = iStartY; + pszNext[iEnd] != ')'; + iEnd++ ) + { + if( pszNext[iEnd] == '\0' ) + return FALSE; + } + + *ppszNext += iEnd; + + return TRUE; +} + +/************************************************************************/ +/* TranslateGeometry() */ +/* */ +/* This currently only supports "old style" ingres geometry in */ +/* text format. Essentially tuple lists of vertices. */ +/************************************************************************/ + +OGRGeometry *OGRIngresLayer::TranslateGeometry( const char *pszGeom ) + +{ + OGRGeometry *poGeom = NULL; + +/* -------------------------------------------------------------------- */ +/* Parse the tuple list into an array of x/y vertices. The */ +/* input may look like "(2,3)" or "((2,3),(4,5),...)". Extra */ +/* spaces may occur between tokens. */ +/* -------------------------------------------------------------------- */ + double *padfXY = NULL; + int nVertMax = 0, nVertCount = 0; + int nDepth = 0; + const char *pszNext = pszGeom; + + while( *pszNext != '\0' ) + { + while( *pszNext == ' ' ) + pszNext++; + + if( *pszNext == '(' ) + { + pszNext++; + nDepth++; + continue; + } + + if( *pszNext == ')' ) + { + pszNext++; + CPLAssert( nDepth == 1 ); + nDepth--; + break; + } + + if( *pszNext == ',' ) + { + pszNext++; + CPLAssert( nDepth == 1 ); + continue; + } + + if( nVertCount == nVertMax ) + { + nVertMax = nVertMax * 2 + 1; + padfXY = (double *) + CPLRealloc(padfXY, sizeof(double) * nVertMax * 2 ); + } + + if( !ParseXY( &pszNext, padfXY + nVertCount*2 ) ) + { + CPLDebug( "INGRES", "Error parsing geometry: %s", + pszGeom ); + CPLFree( padfXY ); + return NULL; + } + + CPLAssert( *pszNext == ')' ); + nVertCount++; + pszNext++; + nDepth--; + + while( *pszNext == ' ' ) + pszNext++; + } + + CPLAssert( nDepth == 0 ); + +/* -------------------------------------------------------------------- */ +/* Handle Box/IBox. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(osIngresGeomType,"BOX") + || EQUAL(osIngresGeomType,"IBOX") ) + { + CPLAssert( nVertCount == 2 ); + + OGRLinearRing *poRing = new OGRLinearRing(); + poRing->addPoint( padfXY[0], padfXY[1] ); + poRing->addPoint( padfXY[2], padfXY[1] ); + poRing->addPoint( padfXY[2], padfXY[3] ); + poRing->addPoint( padfXY[0], padfXY[3] ); + poRing->addPoint( padfXY[0], padfXY[1] ); + + OGRPolygon *poPolygon = new OGRPolygon(); + poPolygon->addRingDirectly( poRing ); + + poGeom = poPolygon; + } + +/* -------------------------------------------------------------------- */ +/* Handle Point/IPoint */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(osIngresGeomType,"POINT") + || EQUAL(osIngresGeomType,"IPOINT") ) + { + CPLAssert( nVertCount == 1 ); + + poGeom = new OGRPoint( padfXY[0], padfXY[1] ); + } + +/* -------------------------------------------------------------------- */ +/* Handle various linestring types. */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(osIngresGeomType,"LSEG") + || EQUAL(osIngresGeomType,"ILSEG") + || EQUAL(osIngresGeomType,"LINE") + || EQUAL(osIngresGeomType,"LONG LINE") + || EQUAL(osIngresGeomType,"ILINE") ) + { + OGRLineString *poLine = new OGRLineString(); + int iVert; + + poLine->setNumPoints( nVertCount ); + for( iVert = 0; iVert < nVertCount; iVert++ ) + poLine->setPoint( iVert, padfXY[iVert*2+0], padfXY[iVert*2+1] ); + + poGeom = poLine; + } + +/* -------------------------------------------------------------------- */ +/* Handle Polygon/IPolygon/LongPolygon. */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(osIngresGeomType,"POLYGON") + || EQUAL(osIngresGeomType,"IPOLYGON") + || EQUAL(osIngresGeomType,"LONG POLYGON") ) + { + OGRLinearRing *poLine = new OGRLinearRing(); + int iVert; + + poLine->setNumPoints( nVertCount ); + for( iVert = 0; iVert < nVertCount; iVert++ ) + poLine->setPoint( iVert, padfXY[iVert*2+0], padfXY[iVert*2+1] ); + + // INGRES polygons are implicitly closed, but OGR expects explicit + if( poLine->getX(nVertCount-1) != poLine->getX(0) + || poLine->getY(nVertCount-1) != poLine->getY(0) ) + poLine->addPoint( poLine->getX(0), poLine->getY(0) ); + + OGRPolygon *poPolygon = new OGRPolygon(); + poPolygon->addRingDirectly( poLine ); + poGeom = poPolygon; + } + + return poGeom; +} + +/************************************************************************/ +/* RecordToFeature() */ +/* */ +/* Convert the indicated record of the current result set into */ +/* a feature. */ +/************************************************************************/ + +OGRFeature *OGRIngresLayer::RecordToFeature( char **papszRow ) + +{ +/* -------------------------------------------------------------------- */ +/* Create a feature from the current result. */ +/* -------------------------------------------------------------------- */ + int iField; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + poFeature->SetFID( iNextShapeId ); + m_nFeaturesRead++; + +/* ==================================================================== */ +/* Transfer all result fields we can. */ +/* ==================================================================== */ + for( iField = 0; + iField < (int) poResultSet->getDescrParm.gd_descriptorCount; + iField++ ) + { + IIAPI_DATAVALUE *psDV = + poResultSet->pasDataBuffer + iField; + IIAPI_DESCRIPTOR *psFDesc = + poResultSet->getDescrParm.gd_descriptor + iField; + int iOGRField; + +/* -------------------------------------------------------------------- */ +/* Ignore NULL fields. */ +/* -------------------------------------------------------------------- */ + if( psDV->dv_null ) + continue; + +/* -------------------------------------------------------------------- */ +/* Handle FID. */ +/* -------------------------------------------------------------------- */ + if( osFIDColumn.size() + && EQUAL(psFDesc->ds_columnName,osFIDColumn) + && psFDesc->ds_dataType == IIAPI_INT_TYPE + && psDV->dv_length == 4 ) + { + if( papszRow[iField] == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "NULL primary key in RecordToFeature()" ); + return NULL; + } + + GInt32 nValue; + memcpy( &nValue, papszRow[iField], 4 ); + poFeature->SetFID( nValue ); + } + +/* -------------------------------------------------------------------- */ +/* Handle Ingres geometry */ +/* -------------------------------------------------------------------- */ + if( osGeomColumn.size() + && EQUAL(psFDesc->ds_columnName,osGeomColumn)) + { + if( poDS->IsNewIngres() ) + { + OGRGeometry *poGeometry = NULL; + unsigned char *pszWKB = (unsigned char *) papszRow[iField]; + +// OGRGeometryFactory::createFromWkt(&pszWKT, NULL, &poGeometry); + OGRGeometryFactory::createFromWkb(pszWKB, NULL, &poGeometry, -1); + + poFeature->SetGeometryDirectly(poGeometry); + } + else + { + poFeature->SetGeometryDirectly( + TranslateGeometry( papszRow[iField] ) ); + } + continue; + } + + +/* -------------------------------------------------------------------- */ +/* Transfer regular data fields. */ +/* -------------------------------------------------------------------- */ + iOGRField = poFeatureDefn->GetFieldIndex(psFDesc->ds_columnName); + if( iOGRField < 0 ) + continue; + + switch( psFDesc->ds_dataType ) + { + case IIAPI_CHR_TYPE: + case IIAPI_CHA_TYPE: + case IIAPI_LVCH_TYPE: + case IIAPI_LTXT_TYPE: + poFeature->SetField( iOGRField, papszRow[iField] ); + break; + + case IIAPI_VCH_TYPE: + case IIAPI_TXT_TYPE: + GUInt16 nLength; + memcpy( &nLength, papszRow[iField], 2 ); + papszRow[iField][nLength+2] = '\0'; + poFeature->SetField( iOGRField, papszRow[iField]+2 ); + break; + + case IIAPI_INT_TYPE: + if( psDV->dv_length == 8 ) + { + GIntBig nValue; + memcpy( &nValue, papszRow[iField], 8 ); + poFeature->SetField( iOGRField, (int) nValue ); + } + else if( psDV->dv_length == 4 ) + { + GInt32 nValue; + memcpy( &nValue, papszRow[iField], 4 ); + poFeature->SetField( iOGRField, nValue ); + } + else if( psDV->dv_length == 2 ) + { + GInt16 nValue; + memcpy( &nValue, papszRow[iField], 2 ); + poFeature->SetField( iOGRField, nValue ); + } + else if( psDV->dv_length == 1 ) + { + GByte nValue; + memcpy( &nValue, papszRow[iField], 1 ); + poFeature->SetField( iOGRField, nValue ); + } + break; + + case IIAPI_FLT_TYPE: + if( psDV->dv_length == 4 ) + { + float fValue; + memcpy( &fValue, papszRow[iField], 4 ); + poFeature->SetField( iOGRField, fValue ); + } + else if( psDV->dv_length == 8 ) + { + double dfValue; + memcpy( &dfValue, papszRow[iField], 8 ); + poFeature->SetField( iOGRField, dfValue ); + } + break; + + case IIAPI_DEC_TYPE: + { + IIAPI_CONVERTPARM sCParm; + char szFormatBuf[30]; + + memset( &sCParm, 0, sizeof(sCParm) ); + + memcpy( &(sCParm.cv_srcDesc), psFDesc, + sizeof(IIAPI_DESCRIPTOR) ); + memcpy( &(sCParm.cv_srcValue), psDV, + sizeof(IIAPI_DATAVALUE) ); + + sCParm.cv_dstDesc.ds_dataType = IIAPI_CHA_TYPE; + sCParm.cv_dstDesc.ds_nullable = FALSE; + sCParm.cv_dstDesc.ds_length = sizeof(szFormatBuf); + + sCParm.cv_dstValue.dv_value = szFormatBuf; + + IIapi_convertData( &sCParm ); + + poFeature->SetField( iOGRField, szFormatBuf ); + break; + } + } + } + + return poFeature; +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRIngresLayer::GetNextRawFeature() + +{ +/* -------------------------------------------------------------------- */ +/* Do we need to establish an initial query? */ +/* -------------------------------------------------------------------- */ + if( iNextShapeId == 0 && poResultSet == NULL ) + { + CPLAssert( osQueryStatement.size() != 0 ); + + poDS->EstablishActiveLayer( this ); + + poResultSet = new OGRIngresStatement( poDS->GetConn() ); + + if( !poResultSet->ExecuteSQL( osQueryStatement ) ) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Fetch next record. */ +/* -------------------------------------------------------------------- */ + char **papszRow = poResultSet->GetRow(); + if( papszRow == NULL ) + { + ResetReading(); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Process record. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = RecordToFeature( papszRow ); + + iNextShapeId++; + + return poFeature; +} + +/************************************************************************/ +/* GetFeature() */ +/* */ +/* Note that we actually override this in OGRIngresTableLayer. */ +/************************************************************************/ + +OGRFeature *OGRIngresLayer::GetFeature( GIntBig nFeatureId ) + +{ + return OGRLayer::GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRIngresLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else if( EQUAL(pszCap,OLCTransactions) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return FALSE; + + else + return FALSE; +} + + + + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char *OGRIngresLayer::GetFIDColumn() + +{ + return osFIDColumn; +} + +/************************************************************************/ +/* GetGeometryColumn() */ +/************************************************************************/ + +const char *OGRIngresLayer::GetGeometryColumn() + +{ + return osGeomColumn; +} + + +/************************************************************************/ +/* FetchSRSId() */ +/************************************************************************/ + +int OGRIngresLayer::FetchSRSId(OGRFeatureDefn *poDefn) +{ +/* -------------------------------------------------------------------- */ +/* We only support srses in the new ingres geospatial implementation.*/ +/* -------------------------------------------------------------------- */ + if( !poDS->IsNewIngres() ) + { + nSRSId = -1; + } + +/* -------------------------------------------------------------------- */ +/* If we haven't queried for the srs id yet, do so now. */ +/* -------------------------------------------------------------------- */ + if( nSRSId == -2 ) + { + char szCommand[1024]; + char **papszRow; + OGRIngresStatement oStatement(poDS->GetConn()); + + sprintf( szCommand, + "SELECT srid FROM geometry_columns " + "WHERE f_table_name = '%s' AND f_geometry_column = '%s'", + poDefn->GetName(), + GetGeometryColumn()); + + oStatement.ExecuteSQL(szCommand); + + papszRow = oStatement.GetRow(); + + if( papszRow != NULL && papszRow[0] != NULL ) + { + nSRSId = *((II_INT4 *) papszRow[0]); + } + } + + return nSRSId; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRIngresLayer::GetSpatialRef() + +{ + if( poSRS == NULL && nSRSId > -1 ) + { + poSRS = poDS->FetchSRS( nSRSId ); + if( poSRS != NULL ) + poSRS->Reference(); + else + nSRSId = -1; + } + + return poSRS; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringresresultlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringresresultlayer.cpp new file mode 100644 index 000000000..bd0b022db --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringresresultlayer.cpp @@ -0,0 +1,171 @@ +/****************************************************************************** + * $Id: ogringresresultlayer.cpp 11522 2007-05-15 14:26:10Z mloskot $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRIngresResultLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_ingres.h" + +CPL_CVSID("$Id: ogringresresultlayer.cpp 11522 2007-05-15 14:26:10Z mloskot $"); + +/************************************************************************/ +/* OGRIngresResultLayer() */ +/************************************************************************/ + +OGRIngresResultLayer::OGRIngresResultLayer( OGRIngresDataSource *poDSIn, + const char * pszRawQueryIn, + OGRIngresStatement *poResultSetIn ) +{ + poDS = poDSIn; + + iNextShapeId = 0; + + pszRawStatement = CPLStrdup(pszRawQueryIn); + + poResultSet = poResultSetIn; + + BuildFullQueryStatement(); + + poFeatureDefn = ReadResultDefinition(); +} + +/************************************************************************/ +/* ~OGRIngresResultLayer() */ +/************************************************************************/ + +OGRIngresResultLayer::~OGRIngresResultLayer() + +{ + CPLFree( pszRawStatement ); +} + +/************************************************************************/ +/* ReadResultDefinition() */ +/* */ +/* Build a schema from the current resultset. */ +/************************************************************************/ + +OGRFeatureDefn *OGRIngresResultLayer::ReadResultDefinition() + +{ +/* -------------------------------------------------------------------- */ +/* Parse the returned table information. */ +/* -------------------------------------------------------------------- */ + OGRFeatureDefn *poDefn = new OGRFeatureDefn( "sql_statement" ); + int iRawField; + + poDefn->Reference(); + + for( iRawField = 0; + iRawField < (int) poResultSet->getDescrParm.gd_descriptorCount; + iRawField++ ) + { + IIAPI_DESCRIPTOR *psFDesc = + poResultSet->getDescrParm.gd_descriptor + iRawField; + OGRFieldDefn oField( psFDesc->ds_columnName, OFTString); + + switch( psFDesc->ds_dataType ) + { + case IIAPI_CHR_TYPE: + case IIAPI_CHA_TYPE: + // string - fixed width. + oField.SetWidth( psFDesc->ds_length ); + poDefn->AddFieldDefn( &oField ); + break; + + case IIAPI_LVCH_TYPE: + case IIAPI_LTXT_TYPE: + case IIAPI_VCH_TYPE: + case IIAPI_TXT_TYPE: + // default variable length string + poDefn->AddFieldDefn( &oField ); + break; + + case IIAPI_INT_TYPE: + oField.SetType( OFTInteger ); + poDefn->AddFieldDefn( &oField ); + break; + + case IIAPI_FLT_TYPE: + oField.SetType( OFTReal ); + poDefn->AddFieldDefn( &oField ); + break; + + case IIAPI_DEC_TYPE: + oField.SetWidth( psFDesc->ds_precision ); + if( psFDesc->ds_scale == 0 ) + oField.SetType( OFTInteger ); + else + { + oField.SetType( OFTReal ); + oField.SetPrecision( psFDesc->ds_scale ); + } + poDefn->AddFieldDefn( &oField ); + break; + + default: + // any other field we ignore. + break; + } + } + + poDefn->SetGeomType( wkbNone ); + + return poDefn; +} + +/************************************************************************/ +/* BuildFullQueryStatement() */ +/************************************************************************/ + +void OGRIngresResultLayer::BuildFullQueryStatement() + +{ + osQueryStatement = pszRawStatement; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRIngresResultLayer::ResetReading() + +{ + OGRIngresLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRIngresResultLayer::GetFeatureCount( int bForce ) + +{ + // I wonder if we could do anything smart here... + // ... not till Ingres grows up (HB) + return OGRIngresLayer::GetFeatureCount( bForce ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringresstatement.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringresstatement.cpp new file mode 100644 index 000000000..1255fb69b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringresstatement.cpp @@ -0,0 +1,650 @@ +/****************************************************************************** + * $Id: ogringresstatement.cpp 23896 2012-02-04 13:29:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRIngresStatement class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ingres.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogringresstatement.cpp 23896 2012-02-04 13:29:41Z rouault $"); + +/************************************************************************/ +/* OGRIngresStatement() */ +/************************************************************************/ + +OGRIngresStatement::OGRIngresStatement( II_PTR hConn ) + +{ + this->hConn = hConn; + + pabyWrkBuffer = NULL; + papszFields = NULL; + pasDataBuffer = NULL; + hStmt = NULL; + hTransaction = NULL; + + memset( &getDescrParm, 0, sizeof(getDescrParm) ); + memset( &queryInfo, 0, sizeof(queryInfo) ); + bDebug = TRUE; + + bHaveParm = FALSE; + nParmLen = 0; + pabyParmData = NULL; + +// CPLDebug( "INGRES", "Create Statement %p", this ); +} + +/************************************************************************/ +/* ~OGRIngresStatement() */ +/************************************************************************/ + +OGRIngresStatement::~OGRIngresStatement() + +{ + Close(); +} + +void OGRIngresStatement::Close() +{ + IIAPI_WAITPARM waitParm = { -1 }; + + ClearDynamicColumns(); + + if( hStmt != NULL ) + { + IIAPI_CLOSEPARM closeParm; + + closeParm.cl_genParm.gp_callback = NULL; + closeParm.cl_genParm.gp_closure = NULL; + closeParm.cl_stmtHandle = hStmt; + + IIapi_close( &closeParm ); + + while( closeParm.cl_genParm.gp_completed == FALSE ) + IIapi_wait( &waitParm ); + + hStmt = NULL; + } + + if( hTransaction != NULL ) + { + IIAPI_COMMITPARM commitParm; + + commitParm.cm_genParm.gp_callback = NULL; + commitParm.cm_genParm.gp_closure = NULL; + commitParm.cm_tranHandle = hTransaction; + + IIapi_commit( &commitParm ); + + while( commitParm.cm_genParm.gp_completed == FALSE ) + IIapi_wait( &waitParm ); + + hTransaction = NULL; + } + + // Set the descriptorCount to zero to avoid attempting to refree it + // in another Close call. + getDescrParm.gd_descriptorCount = 0; + + CPLFree( papszFields ); + CPLFree( pabyWrkBuffer ); + CPLFree( pasDataBuffer ); + CPLFree( pabyParmData ); + + papszFields = NULL; + pabyWrkBuffer = NULL; + pasDataBuffer = NULL; + pabyParmData = NULL; +} + +/************************************************************************/ +/* ExecuteSQL() */ +/* */ +/* Execute an SQL statement. */ +/************************************************************************/ + +int OGRIngresStatement::ExecuteSQL( const char *pszStatement ) + +{ +/* -------------------------------------------------------------------- */ +/* Issue the query. */ +/* -------------------------------------------------------------------- */ + IIAPI_WAITPARM waitParm = { -1 }; + IIAPI_QUERYPARM queryParm; + + queryParm.qy_genParm.gp_callback = NULL; + queryParm.qy_genParm.gp_closure = NULL; + queryParm.qy_connHandle = hConn; + queryParm.qy_queryType = IIAPI_QT_QUERY; + queryParm.qy_queryText = (II_CHAR *) pszStatement; + queryParm.qy_parameters = bHaveParm; + queryParm.qy_tranHandle = NULL; + queryParm.qy_stmtHandle = NULL; + + if( bDebug ) + CPLDebug( "INGRES", "IIapi_query(%s)", pszStatement ); + + IIapi_query( &queryParm ); + +/* -------------------------------------------------------------------- */ +/* Capture handles for result. */ +/* -------------------------------------------------------------------- */ + while( queryParm.qy_genParm.gp_completed == FALSE ) + IIapi_wait( &waitParm ); + + if( queryParm.qy_genParm.gp_status != IIAPI_ST_SUCCESS + || hConn == NULL ) + { + ReportError( &(queryParm.qy_genParm), + CPLString().Printf( "IIapi_query(%s)", pszStatement ) ); + return FALSE; + } + + hTransaction = queryParm.qy_tranHandle; + hStmt = queryParm.qy_stmtHandle; + + if( hStmt == NULL ) + { + CPLDebug( "INGRES", "No resulting statement." ); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Do we have parameters to send? */ +/* -------------------------------------------------------------------- */ + if( bHaveParm ) + { + if( !SendParms() ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Get description of result columns. */ +/* -------------------------------------------------------------------- */ + getDescrParm.gd_genParm.gp_callback = NULL; + getDescrParm.gd_genParm.gp_closure = NULL; + getDescrParm.gd_stmtHandle = hStmt; + getDescrParm.gd_descriptorCount = 0; + getDescrParm.gd_descriptor = NULL; + + IIapi_getDescriptor( &getDescrParm ); + + while( getDescrParm.gd_genParm.gp_completed == FALSE ) + IIapi_wait( &waitParm ); + + if( getDescrParm.gd_genParm.gp_status != IIAPI_ST_SUCCESS ) + { + if( getDescrParm.gd_genParm.gp_errorHandle ) + { + if( !bDebug ) + CPLDebug( "INGRES", "IIapi_query(%s)", pszStatement ); + + ReportError( &(getDescrParm.gd_genParm), "IIapi_getDescriptor()" ); + return FALSE; + } + else + { + if( bDebug ) + CPLDebug( "INGRES", "Got gp_status = %d from getDescriptor.", + getDescrParm.gd_genParm.gp_status ); + } + } + +/* -------------------------------------------------------------------- */ +/* Get query info. */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + // For reasons I don't understand, calling getQueryInfo seems to screw + // up access to query results, so this is disabled. + queryInfo.gq_stmtHandle = hStmt; + + IIapi_getQueryInfo( &queryInfo ); + + while( queryInfo.gq_genParm.gp_completed == FALSE ) + IIapi_wait( &waitParm ); + + CPLDebug( "INGRES", + "gq_flags=%x, gq_mask=%x, gq_rowCount=%d, gq_rowStatus=%d, rowPosition=%d", + queryInfo.gq_flags, + queryInfo.gq_mask, + queryInfo.gq_rowCount, + queryInfo.gq_rowStatus, + queryInfo.gq_rowPosition ); + + if( queryInfo.gq_genParm.gp_status != IIAPI_ST_SUCCESS ) + { + ReportError( &(queryInfo.gq_genParm), "IIapi_getQueryInfo()" ); + return FALSE; + } +#endif + +/* -------------------------------------------------------------------- */ +/* Setup buffers for returned rows. */ +/* -------------------------------------------------------------------- */ + int i, nBufWidth = 0; + + for( i = 0; i < getDescrParm.gd_descriptorCount; i++ ) + nBufWidth += getDescrParm.gd_descriptor[i].ds_length + 1; + + pabyWrkBuffer = (GByte *) CPLCalloc(1,nBufWidth); + + papszFields = (char **) CPLCalloc(sizeof(char *), + getDescrParm.gd_descriptorCount+1); + + nBufWidth = 0; + for( i = 0; i < getDescrParm.gd_descriptorCount; i++ ) + { + papszFields[i] = (char *) (pabyWrkBuffer + nBufWidth); + nBufWidth += getDescrParm.gd_descriptor[i].ds_length + 1; + } + +/* -------------------------------------------------------------------- */ +/* Setup the getColumns() argument. */ +/* -------------------------------------------------------------------- */ + pasDataBuffer = (IIAPI_DATAVALUE *) + CPLCalloc(sizeof(IIAPI_DATAVALUE),getDescrParm.gd_descriptorCount); + + getColParm.gc_genParm.gp_callback = NULL; + getColParm.gc_genParm.gp_closure = NULL; + getColParm.gc_rowCount = 1; + getColParm.gc_columnCount = getDescrParm.gd_descriptorCount; + getColParm.gc_rowsReturned = 0; + getColParm.gc_columnData = pasDataBuffer; + getColParm.gc_stmtHandle = hStmt; + getColParm.gc_moreSegments = 0; + + for( i = 0; i < getDescrParm.gd_descriptorCount; i++ ) + { + getColParm.gc_columnData[i].dv_value = papszFields[i]; + } + +/* -------------------------------------------------------------------- */ +/* We don't want papszFields[] pointing to anything but */ +/* dynamically allocated data for long (blob) fields, so clear */ +/* these pointers now. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < getDescrParm.gd_descriptorCount; i++ ) + { + if( IsColumnLong( i ) ) + papszFields[i] = NULL; + } + + return TRUE; +} + +/************************************************************************/ +/* GetRow() */ +/* */ +/* Get a row from the result set and return an array of */ +/* pointers to the returned fields. NULL is returned on error */ +/* or if we have run out of rows. */ +/************************************************************************/ + +char **OGRIngresStatement::GetRow() + +{ + IIAPI_WAITPARM waitParm = { -1 }; + int iBaseCol; + + ClearDynamicColumns(); + + if( hStmt == NULL ) + return NULL; + +/* ==================================================================== */ +/* Loop over all column, processing the columns in sub-groups */ +/* so we can isolate blob columns for special handling. */ +/* ==================================================================== */ + for( iBaseCol = 0; iBaseCol < getDescrParm.gd_descriptorCount; iBaseCol++) + { + getColParm.gc_columnCount = 1; + getColParm.gc_columnData = pasDataBuffer + iBaseCol; + +/* -------------------------------------------------------------------- */ +/* Fetch column(s) */ +/* -------------------------------------------------------------------- */ + if( !IsColumnLong( iBaseCol ) ) + { + IIapi_getColumns( &getColParm ); + + while( getColParm.gc_genParm.gp_completed == FALSE ) + IIapi_wait( &waitParm ); + + if( getColParm.gc_genParm.gp_status >= IIAPI_ST_NO_DATA ) + return NULL; + + papszFields[iBaseCol][pasDataBuffer[iBaseCol].dv_length] = '\0'; + } + +/* -------------------------------------------------------------------- */ +/* blob columns may require some extra processing. */ +/* -------------------------------------------------------------------- */ + else + { + GUInt16 nSegmentLen; + char *pachData = NULL; + int nDataLen = 0; + + do { + IIapi_getColumns( &getColParm ); + + while( getColParm.gc_genParm.gp_completed == FALSE ) + IIapi_wait( &waitParm ); + + if( getColParm.gc_genParm.gp_status >= IIAPI_ST_NO_DATA ) + return NULL; + + memcpy( &nSegmentLen, pasDataBuffer[iBaseCol].dv_value, 2 ); + + pachData = (char *) CPLRealloc(pachData, + nDataLen + nSegmentLen + 1 ); + + memcpy( pachData + nDataLen, + ((char *) pasDataBuffer[iBaseCol].dv_value)+2, + nSegmentLen ); + + nDataLen += nSegmentLen; + pachData[nDataLen] = '\0'; + } while( getColParm.gc_moreSegments ); + + papszFields[iBaseCol] = pachData; + } + } + + return papszFields; +} + +/************************************************************************/ +/* ClearDynamicColumns() */ +/* */ +/* Free dynamic buffers associated with long/blob columns. */ +/************************************************************************/ + +void OGRIngresStatement::ClearDynamicColumns() + +{ + int i; + + for( i = 0; i < getDescrParm.gd_descriptorCount; i++ ) + { + if( IsColumnLong( i ) ) + { + CPLFree( papszFields[i] ); + papszFields[i] = NULL; + } + } +} + +/************************************************************************/ +/* DumpRow() */ +/************************************************************************/ + +void OGRIngresStatement::DumpRow( FILE *fp ) + +{ + int i; + + fprintf( fp, "---------------\n" ); + for( i = 0; i < getDescrParm.gd_descriptorCount; i++ ) + { + fprintf( fp, " %s = %s\n", + getDescrParm.gd_descriptor[i].ds_columnName, + papszFields[i] ); + } +} + +/************************************************************************/ +/* IsColumnLong() */ +/* */ +/* Returns TRUE if the indicated column (zero based) is a blob */ +/* type (long varchar, long byte, etc) */ +/************************************************************************/ + +int OGRIngresStatement::IsColumnLong( int iColumn ) + +{ + if( iColumn < 0 || iColumn >= getDescrParm.gd_descriptorCount ) + return FALSE; + + switch( getDescrParm.gd_descriptor[iColumn].ds_dataType ) + { + case IIAPI_LVCH_TYPE: + case IIAPI_LBYTE_TYPE: + case IIAPI_LNVCH_TYPE: + case IIAPI_LTXT_TYPE: + return TRUE; + + default: + return FALSE; + } +} + +/************************************************************************/ +/* ReportError() */ +/************************************************************************/ + +void OGRIngresStatement::ReportError( IIAPI_GENPARM *genParm, + const char *pszDescription ) + +{ + IIAPI_GETEINFOPARM getErrParm; + + /* + ** Check API call status. + */ + const char *pszCode = + (genParm->gp_status == IIAPI_ST_SUCCESS) ? + "IIAPI_ST_SUCCESS" : + (genParm->gp_status == IIAPI_ST_MESSAGE) ? + "IIAPI_ST_MESSAGE" : + (genParm->gp_status == IIAPI_ST_WARNING) ? + "IIAPI_ST_WARNING" : + (genParm->gp_status == IIAPI_ST_NO_DATA) ? + "IIAPI_ST_NO_DATA" : + (genParm->gp_status == IIAPI_ST_ERROR) ? + "IIAPI_ST_ERROR" : + (genParm->gp_status == IIAPI_ST_FAILURE) ? + "IIAPI_ST_FAILURE" : + (genParm->gp_status == IIAPI_ST_NOT_INITIALIZED) ? + "IIAPI_ST_NOT_INITIALIZED" : + (genParm->gp_status == IIAPI_ST_INVALID_HANDLE) ? + "IIAPI_ST_INVALID_HANDLE" : + (genParm->gp_status == IIAPI_ST_OUT_OF_MEMORY) ? + "IIAPI_ST_OUT_OF_MEMORY" : + "(unknown status)"; + + /* + ** Check for error information. + */ + if ( ! genParm->gp_errorHandle ) + { + CPLDebug( "INGRES", "No gp_errorHandle in ReportError(%s)", + pszDescription ); + return; + } + + getErrParm.ge_errorHandle = genParm->gp_errorHandle; + + CPLString osErrorMessage; + CPLErr eType = CE_Failure; + + osErrorMessage.Printf( "%s: %s", pszDescription, pszCode ); + + do + { + /* + ** Invoke API function call. + */ + IIapi_getErrorInfo( &getErrParm ); + + /* + ** Break out of the loop if no data or failed. + */ + if ( getErrParm.ge_status != IIAPI_ST_SUCCESS ) + break; + + /* + ** Process result. + */ + + switch( getErrParm.ge_type ) + { + case IIAPI_GE_ERROR : + eType = CE_Failure; + break; + + case IIAPI_GE_WARNING : + eType = CE_Warning; + break; + + case IIAPI_GE_MESSAGE : + eType = CE_Debug; + break; + + default: + eType = CE_Failure; + break; + } + + CPLString osMoreMsg; + + osMoreMsg.Printf( "\n'%s' 0x%x\n%s", + getErrParm.ge_SQLSTATE, getErrParm.ge_errorCode, + getErrParm.ge_message ? getErrParm.ge_message : "NULL" ); + osErrorMessage += osMoreMsg; + } while( 1 ); + + CPLError( eType, CPLE_AppDefined, "%s", osErrorMessage.c_str() ); +} + +/************************************************************************/ +/* SendParms() */ +/************************************************************************/ + +int OGRIngresStatement::SendParms() + +{ + IIAPI_SETDESCRPARM setDescrParm; + IIAPI_PUTPARMPARM putParmParm; + IIAPI_DESCRIPTOR DescrBuffer; + IIAPI_DATAVALUE DataBuffer; + IIAPI_WAITPARM waitParm = { -1 }; + +/* -------------------------------------------------------------------- */ +/* Describe the parameter. */ +/* -------------------------------------------------------------------- */ + setDescrParm.sd_genParm.gp_callback = NULL; + setDescrParm.sd_genParm.gp_closure = NULL; + setDescrParm.sd_stmtHandle = hStmt; + setDescrParm.sd_descriptorCount = 1; + setDescrParm.sd_descriptor = ( IIAPI_DESCRIPTOR * )( &DescrBuffer ); + + setDescrParm.sd_descriptor[0].ds_dataType = eParmType; + setDescrParm.sd_descriptor[0].ds_nullable = FALSE; + setDescrParm.sd_descriptor[0].ds_length = (II_UINT2) (nParmLen+2); + setDescrParm.sd_descriptor[0].ds_precision = 0; + setDescrParm.sd_descriptor[0].ds_scale = 0; + setDescrParm.sd_descriptor[0].ds_columnType = IIAPI_COL_QPARM; + setDescrParm.sd_descriptor[0].ds_columnName = NULL; + + IIapi_setDescriptor( &setDescrParm ); + + while( setDescrParm.sd_genParm.gp_completed == FALSE ) + IIapi_wait( &waitParm ); + + if( setDescrParm.sd_genParm.gp_status != IIAPI_ST_SUCCESS ) + { + ReportError( &(setDescrParm.sd_genParm), "SendParm()" ); + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Send the parameter */ +/* -------------------------------------------------------------------- */ + GByte abyChunk[2000]; + int nBytesSent = 0; + + putParmParm.pp_genParm.gp_callback = NULL; + putParmParm.pp_genParm.gp_closure = NULL; + putParmParm.pp_stmtHandle = hStmt; + putParmParm.pp_parmCount = 1; + + while( nBytesSent < nParmLen ) + { + GInt16 nLen = (GInt16) MIN((int)sizeof(abyChunk)-2,nParmLen-nBytesSent); + + // presuming we want machine local order... + memcpy( abyChunk, &nLen, sizeof(nLen) ); + memcpy( abyChunk+2, pabyParmData + nBytesSent, nLen ); + nBytesSent += nLen; + + putParmParm.pp_parmData = &DataBuffer; + putParmParm.pp_parmData[0].dv_null = FALSE; + putParmParm.pp_parmData[0].dv_length = nLen+2; + putParmParm.pp_parmData[0].dv_value = abyChunk; + + putParmParm.pp_moreSegments = nBytesSent < nParmLen; + + IIapi_putParms( &putParmParm ); + + while( putParmParm.pp_genParm.gp_completed == FALSE ) + IIapi_wait( &waitParm ); + + if ( putParmParm.pp_genParm.gp_status != IIAPI_ST_SUCCESS ) + { + ReportError( &(putParmParm.pp_genParm), "SendParm()" ); + + return FALSE; + } + } + + return TRUE; +} + +/************************************************************************/ +/* addInputParameter() */ +/* */ +/* Add data to be treated as an input parameter for the SQL */ +/* query. For now we internally only support one parameter, */ +/* but we might change that in the future. */ +/************************************************************************/ + +void OGRIngresStatement::addInputParameter( + IIAPI_DT_ID eDType, int nLength, GByte *pabyData ) + +{ + CPLAssert( !bHaveParm ); + CPLAssert( eDType == IIAPI_LVCH_TYPE || eDType == IIAPI_LBYTE_TYPE ); + // support long varchar and long byte + + bHaveParm = TRUE; + nParmLen = nLength; + eParmType = eDType; + pabyParmData = (GByte *) CPLCalloc(1,nLength+1); + memcpy( pabyParmData, pabyData, nLength); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringrestablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringrestablelayer.cpp new file mode 100644 index 000000000..416610912 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/ogringrestablelayer.cpp @@ -0,0 +1,1351 @@ +/****************************************************************************** + * $Id: ogringrestablelayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRIngresTableLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_ingres.h" + +CPL_CVSID("$Id: ogringrestablelayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRIngresTableLayer() */ +/************************************************************************/ + +OGRIngresTableLayer::OGRIngresTableLayer( OGRIngresDataSource *poDSIn, + const char * pszTableName, + int bUpdate, int nSRSIdIn ) + +{ + poDS = poDSIn; + + bUpdateAccess = bUpdate; + + iNextShapeId = 0; + + nSRSId = nSRSIdIn; + + poFeatureDefn = NULL; + bLaunderColumnNames = TRUE; +} + +/************************************************************************/ +/* ~OGRIngresTableLayer() */ +/************************************************************************/ + +OGRIngresTableLayer::~OGRIngresTableLayer() + +{ +} + + +/************************************************************************/ +/* Initialize() */ +/* */ +/* Make sure we only do a ResetReading once we really have a */ +/* FieldDefn. Otherwise, we'll segfault. After you construct */ +/* the IngresTableLayer, make sure to do pLayer->Initialize() */ +/************************************************************************/ + +OGRErr OGRIngresTableLayer::Initialize(const char * pszTableName) +{ + poFeatureDefn = ReadTableDefinition( pszTableName ); + if (poFeatureDefn) + { + ResetReading(); + return OGRERR_NONE; + } + else + { + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ReadTableDefinition() */ +/* */ +/* Build a schema from the named table. Done by querying the */ +/* catalog. */ +/************************************************************************/ + +OGRFeatureDefn *OGRIngresTableLayer::ReadTableDefinition( const char *pszTable ) + +{ + poDS->EstablishActiveLayer( NULL ); + +/* -------------------------------------------------------------------- */ +/* Fire off commands to get back the schema of the table. */ +/* -------------------------------------------------------------------- */ + CPLString osCommand; + OGRIngresStatement oStatement( poDS->GetConn() ); + + osCommand.Printf( "select column_name, column_datatype, column_length, " + "column_scale, column_ingdatatype, " + "column_internal_datatype " + "from iicolumns where table_name = '%s'", + pszTable ); + + if( !oStatement.ExecuteSQL( osCommand ) ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Parse the returned table information. */ +/* -------------------------------------------------------------------- */ + OGRFeatureDefn *poDefn = new OGRFeatureDefn( pszTable ); + SetDescription( poDefn->GetName() ); + char **papszRow; + + poDefn->Reference(); + poDefn->SetGeomType( wkbNone ); + + while( (papszRow = oStatement.GetRow()) != NULL ) + { + CPLString osFieldName = papszRow[0]; + CPLString osIngresType = papszRow[1]; + CPLString osInternalType = papszRow[5]; + GInt32 nWidth, nScale; + + osIngresType.Trim(); + osFieldName.Trim(); + osInternalType.Trim(); + + memcpy( &nWidth, papszRow[2], 4 ); + memcpy( &nScale, papszRow[3], 4 ); + + OGRFieldDefn oField(osFieldName, OFTString); + + if( osGeomColumn.size() == 0 + && (EQUAL(osInternalType,"POINT") + || EQUAL(osInternalType,"IPOINT") + || EQUAL(osInternalType,"BOX") + || EQUAL(osInternalType,"IBOX") + || EQUAL(osInternalType,"LSEG") + || EQUAL(osInternalType,"ILSEG") + || EQUAL(osInternalType,"LINE") + || EQUAL(osInternalType,"ILINE") + || EQUAL(osInternalType,"LONG LINE") + || EQUAL(osInternalType,"POLYGON") + || EQUAL(osInternalType,"IPOLYGON") + || EQUAL(osInternalType,"LONG POLYGON") + || EQUAL(osInternalType,"CIRCLE") + || EQUAL(osInternalType,"LINESTRING") + || EQUAL(osInternalType,"MULTIPOINT") + || EQUAL(osInternalType,"MULTIPOLYGON") + || EQUAL(osInternalType,"MULTILINESTRING") + || EQUAL(osInternalType,"GEOMETRYCOLLECTION") + || EQUAL(osInternalType,"ICIRCLE")) ) + { + osGeomColumn = osFieldName; + osIngresGeomType = osInternalType; + + if( strstr(osInternalType,"POINT") ) + poDefn->SetGeomType( wkbPoint ); + else if( strstr(osInternalType,"LINE") + || strstr(osInternalType,"SEG") + || strstr(osInternalType, "LINESTRING")) + poDefn->SetGeomType( wkbLineString ); + else if( strstr(osInternalType,"MULTIPOINT")) + poDefn->SetGeomType(wkbMultiPoint); + else if( strstr(osInternalType,"MULTIPOLYGON")) + poDefn->SetGeomType(wkbMultiPolygon); + else if( strstr(osInternalType,"MULTILINESTRING")) + poDefn->SetGeomType(wkbMultiLineString); + // Oddly this is the standin for a generic geometry type. + else if( strstr(osInternalType,"GEOMETRYCOLLECTION")) + poDefn->SetGeomType(wkbUnknown); + else + poDefn->SetGeomType( wkbPolygon ); + continue; + } + else if( EQUALN(osIngresType,"byte",4) + || EQUALN(osIngresType,"long byte",9) ) + { + oField.SetType( OFTBinary ); + } + else if( EQUALN(osIngresType,"varchar",7) + || EQUAL(osIngresType,"text") + || EQUALN(osIngresType,"long varchar",12) ) + { + oField.SetType( OFTString ); + oField.SetWidth( nWidth ); + } + else if( EQUALN(osIngresType,"char",4) || EQUAL(osIngresType,"c") ) + { + oField.SetType( OFTString ); + oField.SetWidth( nWidth ); + } + else if( EQUAL(osIngresType,"integer") ) + { + oField.SetType( OFTInteger ); + } + else if( EQUALN(osIngresType,"decimal", 7) ) + { + if( nScale != 0 ) + { + oField.SetType( OFTReal ); + oField.SetPrecision( nScale ); + oField.SetWidth( nWidth ); + } + else + { + oField.SetType( OFTInteger ); + oField.SetWidth( nWidth ); + } + } + else if( EQUALN(osIngresType,"float", 5) ) + { + oField.SetType( OFTReal ); + } +#ifdef notdef + else if( EQUAL(osIngresType,"date") + || EQUAL(osIngresType,"ansidate") + || EQUAL(osIngresType,"ingresdate") ) + { + oField.SetType( OFTDate ); + } +#endif + + // Is this an integer primary key field? + if( osFIDColumn.size() == 0 + && oField.GetType() == OFTInteger + && EQUAL(oField.GetNameRef(),"ogr_fid") ) + { + osFIDColumn = oField.GetNameRef(); + continue; + } + + poDefn->AddFieldDefn( &oField ); + } + + if( osFIDColumn.size() ) + CPLDebug( "Ingres", "table %s has FID column %s.", + pszTable, osFIDColumn.c_str() ); + else + CPLDebug( "Ingres", + "table %s has no FID column, FIDs will not be reliable!", + pszTable ); + + //We must close the current statement before calling this or else + //The query within FetchSRSId will fail + oStatement.Close(); + + // Fetch the SRID for this table now + // But only if it's the new Ingres Geospatial + if(poDS->IsNewIngres() == TRUE) + nSRSId = FetchSRSId(poDefn); + + return poDefn; +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRIngresTableLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) + +{ + if( !InstallFilter( poGeomIn ) ) + return; + + BuildWhere(); + + ResetReading(); +} + +/************************************************************************/ +/* BuildWhere() */ +/* */ +/* Build the WHERE statement appropriate to the current set of */ +/* criteria (spatial and attribute queries). */ +/************************************************************************/ + +void OGRIngresTableLayer::BuildWhere() + +{ + osWHERE = ""; + +#ifdef notdef + if( m_poFilterGeom != NULL && pszGeomColumn ) + { + char szEnvelope[4096]; + OGREnvelope sEnvelope; + szEnvelope[0] = '\0'; + + //POLYGON((MINX MINY, MAXX MINY, MAXX MAXY, MINX MAXY, MINX MINY)) + m_poFilterGeom->getEnvelope( &sEnvelope ); + + sprintf(szEnvelope, + "POLYGON((%.12f %.12f, %.12f %.12f, %.12f %.12f, %.12f %.12f, %.12f %.12f))", + sEnvelope.MinX, sEnvelope.MinY, + sEnvelope.MaxX, sEnvelope.MinY, + sEnvelope.MaxX, sEnvelope.MaxY, + sEnvelope.MinX, sEnvelope.MaxY, + sEnvelope.MinX, sEnvelope.MinY); + + osWHERE.Printf( "WHERE MBRIntersects(GeomFromText('%s'), %s)", + szEnvelope, + osGeomColumn.c_str() ); + + } +#endif + + if( osQuery.size() > 0 ) + { + if( osWHERE.size() == 0 ) + osWHERE = "WHERE " + osQuery; + else + osWHERE += "&& " + osQuery; + } +} + +/************************************************************************/ +/* BuildFullQueryStatement() */ +/************************************************************************/ + +void OGRIngresTableLayer::BuildFullQueryStatement() + +{ + char *pszFields = BuildFields(); + + osQueryStatement.Printf( "SELECT %s FROM %s %s", + pszFields, poFeatureDefn->GetName(), + osWHERE.c_str() ); + + CPLFree( pszFields ); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRIngresTableLayer::ResetReading() + +{ + BuildFullQueryStatement(); + + OGRIngresLayer::ResetReading(); +} + +/************************************************************************/ +/* BuildFields() */ +/* */ +/* Build list of fields to fetch, performing any required */ +/* transformations (such as on geometry). */ +/************************************************************************/ + +char *OGRIngresTableLayer::BuildFields() + +{ + int i, nSize; + char *pszFieldList; + + nSize = 25 + osGeomColumn.size() + osFIDColumn.size(); + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + nSize += strlen(poFeatureDefn->GetFieldDefn(i)->GetNameRef()) + 4; + + pszFieldList = (char *) CPLMalloc(nSize); + pszFieldList[0] = '\0'; + + if( osFIDColumn.size() + && poFeatureDefn->GetFieldIndex( osFIDColumn ) == -1 ) + sprintf( pszFieldList, "%s", osFIDColumn.c_str() ); + + if( osGeomColumn.size() ) + { + if( strlen(pszFieldList) > 0 ) + strcat( pszFieldList, ", " ); + + if( poDS->IsNewIngres() ) + { + sprintf( pszFieldList+strlen(pszFieldList), + "ASBINARY(%s) %s", osGeomColumn.c_str(), osGeomColumn.c_str() ); + } + else + { + sprintf( pszFieldList+strlen(pszFieldList), + "%s %s", osGeomColumn.c_str(), osGeomColumn.c_str() ); + } + } + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + + if( strlen(pszFieldList) > 0 ) + strcat( pszFieldList, ", " ); + + strcat( pszFieldList, pszName ); + } + + CPLAssert( (int) strlen(pszFieldList) < nSize ); + + return pszFieldList; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRIngresTableLayer::SetAttributeFilter( const char *pszQuery ) + +{ + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + + osQuery = ""; + + if( pszQuery != NULL ) + osQuery = pszQuery; + + BuildWhere(); + + ResetReading(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRIngresTableLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return osFIDColumn.size() != 0; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCCreateField) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCRandomWrite) ) + return bUpdateAccess && osFIDColumn.size() != 0; + + else if( EQUAL(pszCap,OLCDeleteFeature) ) + return bUpdateAccess && osFIDColumn.size() != 0; + + else + return OGRIngresLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* ISetFeature() */ +/* */ +/* SetFeature() is implemented by dropping the old copy of the */ +/* feature in question (if there is one) and then creating a */ +/* new one with the provided feature id. */ +/************************************************************************/ + +OGRErr OGRIngresTableLayer::ISetFeature( OGRFeature *poFeature ) + +{ + OGRErr eErr; + + if( poFeature->GetFID() == OGRNullFID ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "FID required on features given to SetFeature()." ); + return OGRERR_FAILURE; + } + + eErr = DeleteFeature( poFeature->GetFID() ); + if( eErr != OGRERR_NONE ) + return eErr; + + return CreateFeature( poFeature ); +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRIngresTableLayer::DeleteFeature( GIntBig nFID ) + +{ + CPLString osCommand; + +/* -------------------------------------------------------------------- */ +/* We can only delete features if we have a well defined FID */ +/* column to target. */ +/* -------------------------------------------------------------------- */ + if( osFIDColumn.size() == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DeleteFeature(%ld) failed. Unable to delete features " + "in tables without\n a recognised FID column.", + nFID ); + return OGRERR_FAILURE; + + } + +/* -------------------------------------------------------------------- */ +/* Form the statement to drop the record. */ +/* -------------------------------------------------------------------- */ + osCommand.Printf( "DELETE FROM %s WHERE %s = %ld", + poFeatureDefn->GetName(), osFIDColumn.c_str(), nFID ); + +/* -------------------------------------------------------------------- */ +/* Execute the delete. */ +/* -------------------------------------------------------------------- */ + poDS->EstablishActiveLayer( NULL ); + OGRIngresStatement oStmt( poDS->GetConn() ); + + if( !oStmt.ExecuteSQL( osCommand ) ) + return OGRERR_FAILURE; + else + return OGRERR_NONE; +} + +/************************************************************************/ +/* PrepareOldStyleGeometry() */ +/* */ +/* Prepare an ASCII representation of an old style geometry in */ +/* a form suitable to include in an INSERT command. */ +/************************************************************************/ + +OGRErr OGRIngresTableLayer::PrepareOldStyleGeometry( + OGRGeometry *poGeom, CPLString &osRetGeomText ) + +{ + osRetGeomText = ""; + + if( poGeom == NULL ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Point */ +/* -------------------------------------------------------------------- */ + if( EQUAL(osIngresGeomType,"POINT") + && wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poGeom; + + osRetGeomText.Printf( "(%.15g,%.15g)", poPoint->getX(), poPoint->getY() ); + return OGRERR_NONE; + } + + if( EQUAL(osIngresGeomType,"IPOINT") + && wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poGeom; + + osRetGeomText.Printf( "(%d,%d)", + (int) floor(poPoint->getX()), + (int) floor(poPoint->getY()) ); + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Line */ +/* -------------------------------------------------------------------- */ + if( wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) + { + OGRLineString *poLS = (OGRLineString *) poGeom; + CPLString osLastPoint; + int i; + + if( (EQUAL(osIngresGeomType,"LSEG") + || EQUAL(osIngresGeomType,"ILSEG")) + && poLS->getNumPoints() != 2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to place %d vertex linestring in %s field.", + poLS->getNumPoints(), + osIngresGeomType.c_str() ); + return OGRERR_FAILURE; + } + else if( EQUAL(osIngresGeomType,"LINESTRING") + && poLS->getNumPoints() > 124 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to place %d vertex linestring in %s field.", + poLS->getNumPoints(), + osIngresGeomType.c_str() ); + return OGRERR_FAILURE; + } + else if( EQUAL(osIngresGeomType,"ILINESTRING") + && poLS->getNumPoints() > 248 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to place %d vertex linestring in %s field.", + poLS->getNumPoints(), + osIngresGeomType.c_str() ); + return OGRERR_FAILURE; + } + + osRetGeomText = "("; + for( i = 0; i < poLS->getNumPoints(); i++ ) + { + CPLString osPoint; + + if( i > 0 + && poLS->getX(i) == poLS->getX(i-1) + && poLS->getY(i) == poLS->getY(i-1) ) + { + CPLDebug( "INGRES", "Dropping duplicate point in linestring."); + continue; + } + + if( EQUALN(osIngresGeomType,"I",1) ) + osPoint.Printf( "(%d,%d)", + (int) floor(poLS->getX(i)), + (int) floor(poLS->getY(i)) ); + else + osPoint.Printf( "(%.15g,%.15g)", + poLS->getX(i), poLS->getY(i) ); + + if( osPoint == osLastPoint ) + { + CPLDebug( "INGRES", + "Dropping duplicate point in linestring(2)."); + continue; + } + osLastPoint = osPoint; + + if( osRetGeomText.size() > 1 ) + osRetGeomText += "," + osPoint; + else + osRetGeomText += osPoint; + } + osRetGeomText += ")"; + + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Polygon */ +/* -------------------------------------------------------------------- */ + if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) + { + OGRPolygon *poPoly = (OGRPolygon *) poGeom; + OGRLinearRing *poLS = poPoly->getExteriorRing(); + int i, nPoints; + + if( poLS == NULL ) + return OGRERR_FAILURE; + + if( poPoly->getNumInteriorRings() > 0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "%d inner rings discarded from polygon being converted\n" + "to old ingres spatial data type '%s'.", + poPoly->getNumInteriorRings(), + osIngresGeomType.c_str() ); + } + + if( EQUAL(osIngresGeomType,"POLYGON") + && poLS->getNumPoints() > 124 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to place %d vertex linestring in %s field.", + poLS->getNumPoints(), + osIngresGeomType.c_str() ); + return OGRERR_FAILURE; + } + else if( EQUAL(osIngresGeomType,"IPOLYGON") + && poLS->getNumPoints() > 248 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to place %d vertex linestring in %s field.", + poLS->getNumPoints(), + osIngresGeomType.c_str() ); + return OGRERR_FAILURE; + } + + // INGRES geometries use *implied* closure of rings. + nPoints = poLS->getNumPoints(); + if( poLS->getX(0) == poLS->getX(nPoints-1) + && poLS->getY(0) == poLS->getY(nPoints-1) + && nPoints > 1 ) + nPoints--; + + osRetGeomText = "("; + for( i = 0; i < nPoints; i++ ) + { + CPLString osPoint; + + if( i > 0 + && poLS->getX(i) == poLS->getX(i-1) + && poLS->getY(i) == poLS->getY(i-1) ) + { + CPLDebug( "INGRES", "Dropping duplicate point in linestring."); + continue; + } + + if( EQUALN(osIngresGeomType,"I",1) ) + osPoint.Printf( "(%d,%d)", + (int) floor(poLS->getX(i)), + (int) floor(poLS->getY(i)) ); + else + osPoint.Printf( "(%.15g,%.15g)", + poLS->getX(i), poLS->getY(i) ); + + if( osRetGeomText.size() > 1 ) + osRetGeomText += "," + osPoint; + else + osRetGeomText += osPoint; + } + osRetGeomText += ")"; + + return OGRERR_NONE; + } + + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* PrepareNewStyleGeometry() */ +/* */ +/* Prepare an ASCII representation of a new style geometry in */ +/* a form suitable to include in an INSERT command. */ +/* This pretty much just uses the geometry's export to WKT function*/ +/************************************************************************/ + +OGRErr OGRIngresTableLayer::PrepareNewStyleGeometry( + OGRGeometry *poGeom, CPLString &osRetGeomText ) + +{ + OGRErr eErr = OGRERR_NONE; + osRetGeomText = ""; + + if( poGeom == NULL ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Point */ +/* -------------------------------------------------------------------- */ + if( wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) + { + osRetGeomText.Printf( "POINTFROMWKB( ~V , %d )", nSRSId ); + } +/* -------------------------------------------------------------------- */ +/* Linestring */ +/* -------------------------------------------------------------------- */ + else if( wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) + { + osRetGeomText.Printf("LINEFROMWKB( ~V , %d)", nSRSId); + } +/* -------------------------------------------------------------------- */ +/* Polygon */ +/* -------------------------------------------------------------------- */ + else if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) + { + osRetGeomText.Printf("POLYFROMWKB( ~V , %d)", nSRSId); + } +/* -------------------------------------------------------------------- */ +/* Multipoint */ +/* -------------------------------------------------------------------- */ + else if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint ) + { + osRetGeomText.Printf("MPOINTFROMWKB( ~V , %d)", nSRSId); + } +/* -------------------------------------------------------------------- */ +/* Multilinestring */ +/* -------------------------------------------------------------------- */ + else if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ) + { + osRetGeomText.Printf("MLINEFROMWKB( ~V , %d)", nSRSId); + } +/* -------------------------------------------------------------------- */ +/* Multipolygon */ +/* -------------------------------------------------------------------- */ + else if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ) + { + osRetGeomText.Printf("MPOLYFROMWKB( ~V , %d)", nSRSId); + } +/* -------------------------------------------------------------------- */ +/* Geometry collection. */ +/* -------------------------------------------------------------------- */ + else if( wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection ) + { + osRetGeomText.Printf("GEOMCOLLFROMWKB( ~V , %d)", nSRSId); + } +/* -------------------------------------------------------------------- */ +/* Fallback generic geometry handling. */ +/* -------------------------------------------------------------------- */ + else + { + CPLDebug( + "INGRES", + "Unexpected geometry type (%s), attempting to treat generically.", + poGeom->getGeometryName() ); + + osRetGeomText.Printf("GEOMETRYFROMWKB( ~V , %d)", nSRSId); + } + + return eErr; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRIngresTableLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + CPLString osCommand; + int i, bNeedComma = FALSE; + +/* -------------------------------------------------------------------- */ +/* Form the INSERT command. */ +/* -------------------------------------------------------------------- */ + osCommand.Printf( "INSERT INTO %s (", poFeatureDefn->GetName() ); + + +/* -------------------------------------------------------------------- */ +/* Accumulate fields to be inserted. */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetGeometryRef() != NULL && osGeomColumn.size() ) + { + osCommand = osCommand + osGeomColumn + " "; + bNeedComma = TRUE; + } + + if( poFeature->GetFID() != OGRNullFID && osFIDColumn.size() ) + { + if( bNeedComma ) + osCommand += ", "; + + osCommand = osCommand + osFIDColumn + " "; + bNeedComma = TRUE; + } + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if( !poFeature->IsFieldSet( i ) ) + continue; + + if( !bNeedComma ) + bNeedComma = TRUE; + else + osCommand += ", "; + + osCommand = osCommand + + poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + } + + osCommand += ") VALUES ("; + +/* -------------------------------------------------------------------- */ +/* Insert the geometry (as a place holder) */ +/* -------------------------------------------------------------------- */ + CPLString osGeomText; + + // Set the geometry + bNeedComma = FALSE; + if( poFeature->GetGeometryRef() != NULL && osGeomColumn.size() ) + { + bNeedComma = TRUE; + OGRErr localErr; + + if( poDS->IsNewIngres() ) + { + localErr = PrepareNewStyleGeometry( poFeature->GetGeometryRef(), osGeomText ); + } + else + { + localErr = PrepareOldStyleGeometry( poFeature->GetGeometryRef(), osGeomText ); + } + if( localErr == OGRERR_NONE ) + { + if( CSLTestBoolean( + CPLGetConfigOption( "INGRES_INSERT_SUB", "NO") ) ) + { + osCommand += " ~V"; + } + else if( poDS->IsNewIngres() == FALSE ) + { + osCommand += "'"; + osCommand += osGeomText; + osCommand += "'"; + osGeomText = ""; + } + else + { + osCommand += osGeomText; + //osGeomText = ""; + } + } + else + { + osGeomText = ""; + osCommand += "NULL"; /* is this sort of empty geometry legal? */ + } + } + +/* -------------------------------------------------------------------- */ +/* Set the FID */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetFID() != OGRNullFID && osFIDColumn.size() ) + { + if( bNeedComma ) + osCommand += ", "; + osCommand += CPLString().Printf( "%ld ", poFeature->GetFID() ); + bNeedComma = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Copy in the attribute values. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if( !poFeature->IsFieldSet( i ) ) + continue; + + if( bNeedComma ) + osCommand += ", "; + else + bNeedComma = TRUE; + + const char *pszStrValue = poFeature->GetFieldAsString(i); + + if( poFeatureDefn->GetFieldDefn(i)->GetType() != OFTInteger + && poFeatureDefn->GetFieldDefn(i)->GetType() != OFTReal + && poFeatureDefn->GetFieldDefn(i)->GetType() != OFTBinary ) + { + int iChar; + + //We need to quote and escape string fields. + osCommand += "'"; + + for( iChar = 0; pszStrValue[iChar] != '\0'; iChar++ ) + { + if( poFeatureDefn->GetFieldDefn(i)->GetType() != OFTIntegerList + && poFeatureDefn->GetFieldDefn(i)->GetType() != OFTRealList + && poFeatureDefn->GetFieldDefn(i)->GetWidth() > 0 + && iChar == poFeatureDefn->GetFieldDefn(i)->GetWidth() ) + { + CPLDebug( "INGRES", + "Truncated %s field value, it was too long.", + poFeatureDefn->GetFieldDefn(i)->GetNameRef() ); + break; + } + + if( pszStrValue[iChar] == '\'' ) + { + osCommand += '\''; + osCommand += pszStrValue[iChar]; + } + else + osCommand += pszStrValue[iChar]; + } + + osCommand += "'"; + } + else if( poFeatureDefn->GetFieldDefn(i)->GetType() == OFTBinary ) + { + int binaryCount = 0; + GByte* binaryData = poFeature->GetFieldAsBinary(i, &binaryCount); + char* pszHexValue = CPLBinaryToHex( binaryCount, binaryData ); + + osCommand += "x'"; + osCommand += pszHexValue; + osCommand += "'"; + + CPLFree( pszHexValue ); + } + else + { + osCommand += pszStrValue; + } + + } + + osCommand += ")"; + +/* -------------------------------------------------------------------- */ +/* Execute it. */ +/* -------------------------------------------------------------------- */ + poDS->EstablishActiveLayer( NULL ); + OGRIngresStatement oStmt( poDS->GetConn() ); + + oStmt.bDebug = FALSE; + + if( osGeomText.size() > 0 && poDS->IsNewIngres() == FALSE ) + oStmt.addInputParameter( IIAPI_LVCH_TYPE, osGeomText.size(), + (GByte *) osGeomText.c_str() ); + if( osGeomText.size() > 0 && poDS->IsNewIngres() == TRUE ) + { + GByte * pabyWKB; + int nSize = poFeature->GetGeometryRef()->WkbSize(); + pabyWKB = (GByte *) CPLMalloc(nSize); + + poFeature->GetGeometryRef()->exportToWkb(wkbNDR, pabyWKB); + + oStmt.addInputParameter( IIAPI_LBYTE_TYPE, nSize, pabyWKB ); + CPLFree(pabyWKB); +/* + * Test code + char * pszWKT; + poFeature->GetGeometryRef()->exportToWkt(&pszWKT); + oStmt.addInputParameter(IIAPI_LVCH_TYPE, strlen(pszWKT), (GByte *) pszWKT);*/ + } + + if( !oStmt.ExecuteSQL( osCommand ) ) + return OGRERR_FAILURE; + + return OGRERR_NONE; + +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRIngresTableLayer::CreateField( OGRFieldDefn *poFieldIn, + int bApproxOK ) + +{ + poDS->EstablishActiveLayer( NULL ); + + CPLString osCommand; + OGRIngresStatement oStatement( poDS->GetConn() ); + char szFieldType[256]; + OGRFieldDefn oField( poFieldIn ); + + ResetReading(); + +/* -------------------------------------------------------------------- */ +/* Do we want to "launder" the column names into friendly */ +/* format? */ +/* -------------------------------------------------------------------- */ + if( bLaunderColumnNames ) + { + char *pszSafeName = poDS->LaunderName( oField.GetNameRef() ); + + oField.SetName( pszSafeName ); + CPLFree( pszSafeName ); + } + +/* -------------------------------------------------------------------- */ +/* Work out the Ingres type. */ +/* -------------------------------------------------------------------- */ + if( oField.GetType() == OFTInteger ) + { + if( oField.GetWidth() > 0 && bPreservePrecision ) + sprintf( szFieldType, "DECIMAL(%d,0)", oField.GetWidth() ); + else + strcpy( szFieldType, "INTEGER" ); + } + else if( oField.GetType() == OFTReal ) + { + if( oField.GetWidth() > 0 && oField.GetPrecision() > 0 + && bPreservePrecision ) + sprintf( szFieldType, "DECIMAL(%d,%d)", + oField.GetWidth(), oField.GetPrecision() ); + else + strcpy( szFieldType, "FLOAT" ); + } + + else if( oField.GetType() == OFTDate ) + { + sprintf( szFieldType, "DATE" ); + } +#ifdef notdef + else if( oField.GetType() == OFTDateTime ) + { + sprintf( szFieldType, "DATETIME" ); + } +#endif + + else if( oField.GetType() == OFTTime ) + { + sprintf( szFieldType, "TIME" ); + } +#ifdef notdefa + else if( oField.GetType() == OFTBinary ) + { + sprintf( szFieldType, "LONGBLOB" ); + } +#endif + else if( oField.GetType() == OFTString ) + { + if( oField.GetWidth() == 0 ) // We need some fixed maximum. + sprintf( szFieldType, "VARCHAR(1024)" ); + else + sprintf( szFieldType, "VARCHAR(%d)", oField.GetWidth() ); + } + else if( bApproxOK ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Can't create field %s with type %s on Ingres layers. Creating as VARCHAR(1024).", + oField.GetNameRef(), + OGRFieldDefn::GetFieldTypeName(oField.GetType()) ); + strcpy( szFieldType, "VARCHAR(1024)" ); + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can't create field %s with type %s on Ingres layers.", + oField.GetNameRef(), + OGRFieldDefn::GetFieldTypeName(oField.GetType()) ); + + return OGRERR_FAILURE; + } + + osCommand.Printf( "ALTER TABLE %s ADD COLUMN %s %s", + poFeatureDefn->GetName(), oField.GetNameRef(), + szFieldType ); + + if( !oStatement.ExecuteSQL( osCommand ) ) + return OGRERR_FAILURE; + + poFeatureDefn->AddFieldDefn( &oField ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ +#ifdef notdef +OGRFeature *OGRIngresTableLayer::GetFeature( GIntBig nFeatureId ) + +{ + if( pszFIDColumn == NULL ) + return OGRIngresLayer::GetFeature( nFeatureId ); + +/* -------------------------------------------------------------------- */ +/* Discard any existing resultset. */ +/* -------------------------------------------------------------------- */ + ResetReading(); + +/* -------------------------------------------------------------------- */ +/* Prepare query command that will just fetch the one record of */ +/* interest. */ +/* -------------------------------------------------------------------- */ + char *pszFieldList = BuildFields(); + char *pszCommand = (char *) CPLMalloc(strlen(pszFieldList)+2000); + + sprintf( pszCommand, + "SELECT %s FROM %s WHERE %s = %ld", + pszFieldList, poFeatureDefn->GetName(), pszFIDColumn, + nFeatureId ); + CPLFree( pszFieldList ); + +/* -------------------------------------------------------------------- */ +/* Issue the command. */ +/* -------------------------------------------------------------------- */ + if( ingres_query( poDS->GetConn(), pszCommand ) ) + { + poDS->ReportError( pszCommand ); + return NULL; + } + CPLFree( pszCommand ); + + hResultSet = ingres_store_result( poDS->GetConn() ); + if( hResultSet == NULL ) + { + poDS->ReportError( "ingres_store_result() failed on query." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Fetch the result record. */ +/* -------------------------------------------------------------------- */ + char **papszRow; + unsigned long *panLengths; + + papszRow = ingres_fetch_row( hResultSet ); + if( papszRow == NULL ) + return NULL; + + panLengths = ingres_fetch_lengths( hResultSet ); + +/* -------------------------------------------------------------------- */ +/* Transform into a feature. */ +/* -------------------------------------------------------------------- */ + iNextShapeId = nFeatureId; + + OGRFeature *poFeature = RecordToFeature( papszRow, panLengths ); + + iNextShapeId = 0; + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + if( hResultSet != NULL ) + ingres_free_result( hResultSet ); + hResultSet = NULL; + + return poFeature; +} +#endif + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +#ifdef notdef +GIntBig OGRIngresTableLayer::GetFeatureCount( int bForce ) + +{ +/* -------------------------------------------------------------------- */ +/* Ensure any active long result is interrupted. */ +/* -------------------------------------------------------------------- */ + poDS->InterruptLongResult(); + +/* -------------------------------------------------------------------- */ +/* Issue the appropriate select command. */ +/* -------------------------------------------------------------------- */ + INGRES_RES *hResult; + const char *pszCommand; + + pszCommand = CPLSPrintf( "SELECT COUNT(*) FROM %s %s", + poFeatureDefn->GetName(), pszWHERE ); + + if( ingres_query( poDS->GetConn(), pszCommand ) ) + { + poDS->ReportError( pszCommand ); + return FALSE; + } + + hResult = ingres_store_result( poDS->GetConn() ); + if( hResult == NULL ) + { + poDS->ReportError( "ingres_store_result() failed on SELECT COUNT(*)." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Capture the result. */ +/* -------------------------------------------------------------------- */ + char **papszRow = ingres_fetch_row( hResult ); + int nCount = 0; + + if( papszRow != NULL && papszRow[0] != NULL ) + nCount = atoi(papszRow[0]); + + if( hResultSet != NULL ) + ingres_free_result( hResultSet ); + hResultSet = NULL; + + return nCount; +} +#endif + +/************************************************************************/ +/* GetExtent() */ +/* */ +/* Retrieve the MBR of the Ingres table. This should be made more */ +/* in the future when Ingres adds support for a single MBR query */ +/* like PostgreSQL. */ +/************************************************************************/ +#ifdef notdef +OGRErr OGRIngresTableLayer::GetExtent(OGREnvelope *psExtent, int bForce ) + +{ + if( GetLayerDefn()->GetGeomType() == wkbNone ) + { + psExtent->MinX = 0.0; + psExtent->MaxX = 0.0; + psExtent->MinY = 0.0; + psExtent->MaxY = 0.0; + + return OGRERR_FAILURE; + } + + OGREnvelope oEnv; + CPLString osCommand; + GBool bExtentSet = FALSE; + + osCommand.Printf( "SELECT Envelope(%s) FROM %s;", pszGeomColumn, pszGeomColumnTable); + + if (ingres_query(poDS->GetConn(), osCommand) == 0) + { + INGRES_RES* result = ingres_use_result(poDS->GetConn()); + if ( result == NULL ) + { + poDS->ReportError( "ingres_use_result() failed on extents query." ); + return OGRERR_FAILURE; + } + + INGRES_ROW row; + unsigned long *panLengths = NULL; + while ((row = ingres_fetch_row(result))) + { + if (panLengths == NULL) + { + panLengths = ingres_fetch_lengths( result ); + if ( panLengths == NULL ) + { + poDS->ReportError( "ingres_fetch_lengths() failed on extents query." ); + return OGRERR_FAILURE; + } + } + + OGRGeometry *poGeometry = NULL; + // Geometry columns will have the first 4 bytes contain the SRID. + OGRGeometryFactory::createFromWkb(((GByte *)row[0]) + 4, + NULL, + &poGeometry, + panLengths[0] - 4 ); + + if ( poGeometry != NULL ) + { + if (poGeometry && !bExtentSet) + { + poGeometry->getEnvelope(psExtent); + bExtentSet = TRUE; + } + else if (poGeometry) + { + poGeometry->getEnvelope(&oEnv); + if (oEnv.MinX < psExtent->MinX) + psExtent->MinX = oEnv.MinX; + if (oEnv.MinY < psExtent->MinY) + psExtent->MinY = oEnv.MinY; + if (oEnv.MaxX > psExtent->MaxX) + psExtent->MaxX = oEnv.MaxX; + if (oEnv.MaxY > psExtent->MaxY) + psExtent->MaxY = oEnv.MaxY; + } + delete poGeometry; + } + } + + ingres_free_result(result); + } + + return (bExtentSet ? OGRERR_NONE : OGRERR_FAILURE); +} +#endif + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/testdata.sql b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/testdata.sql new file mode 100644 index 000000000..d35abd5bf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ingres/testdata.sql @@ -0,0 +1,157 @@ +drop table mt +\g + +create table mt (id integer, cost decimal(7,2), code c, name char(12), longname vchar(100), vname varchar(100), distance float4, smallval smallint, lvname long varchar) +\g + +insert into mt (id, cost, code, name, longname, vname, distance, smallval, lvname) + values (17,1.23,'x', 'xyz', 'The xyz corp', 'The xyz corp', 12332.772, 112, + 'longvchar') +\g + +insert into mt (id, cost, code, name, longname, vname, distance, smallval, lvname) + values (18,3.22,'x', 'abc', 'ABC Cleaners', 'ABC Cleaning', 22.1232, 32000, + 'very longggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg text') +\g + + +drop table oge_point +\g +create table oge_point (id integer, geom point) +\g +insert into oge_point values (1,'(100,200)') +\g +insert into oge_point values (2,'(300.5,400)') +\g + +drop table oge_ipoint +\g +create table oge_ipoint (id integer, geom ipoint) +\g +insert into oge_ipoint values (1,'(100,200)') +\g +insert into oge_ipoint values (2,'(300,400)') +\g + + +drop table oge_box +\g + +create table oge_box (id integer, geom box) +\g + +insert into oge_box values (1, '((0,0), (10,20))' ) +\g + +insert into oge_box values (2, '((30.5,50), (34,55))' ) +\g + + +drop table oge_ibox +\g + +create table oge_ibox (id integer, geom ibox) +\g + +insert into oge_ibox values (1, '((0,0), (10,20))' ) +\g + +insert into oge_ibox values (2, '((30,50), (34,55))' ) +\g + + +drop table oge_lseg +\g + +create table oge_lseg (id integer, geom lseg) +\g + +insert into oge_lseg values (1, '((0,0), (10,20))' ) +\g + +insert into oge_lseg values (2, '((30.5,50), (34,55))' ) +\g + + +drop table oge_line +\g + +create table oge_line (id integer, geom line(4)) +\g + +insert into oge_line values +(1, '((0,0), (10,20), (70,-20))' ) +\g + +insert into oge_line values +(2, '((30.5,50), (34,55), (100,200), (125,200))' ) +\g + + +drop table oge_iline +\g + +create table oge_iline (id integer, geom iline(4)) +\g + +insert into oge_iline values +(1, '((0,0), (10,20), (70,-20))' ) +\g + +insert into oge_iline values +(2, '((30,50), (34,55), (100,200), (125,200))' ) +\g + + +drop table oge_longline +\g + +create table oge_longline (id integer, geom long line) +\g + +insert into oge_longline values +(1, '((0,0), (10,20), (70,-20))' ) +\g + +insert into oge_longline values +(2, '((30.5,50), (34,55), (100,200), (125,200))' ) +\g + + + +drop table oge_polygon +\g +create table oge_polygon (id integer, geom polygon(4)) +\g +insert into oge_polygon values +(1, '((0,0), (10,20), (70,-20))' ) +\g +insert into oge_polygon values +(2, '((30.5,50), (20,200), (100,200), (125,15.5))' ) +\g + +drop table oge_ipolygon +\g +create table oge_ipolygon (id integer, geom ipolygon(4)) +\g +insert into oge_ipolygon values +(1, '((0,0), (10,20), (70,-20))' ) +\g +insert into oge_ipolygon values +(2, '((30,50), (20,200), (100,200), (125,15))' ) +\g + +drop table oge_longpolygon +\g +create table oge_longpolygon (id integer, geom long polygon) +\g +insert into oge_longpolygon values +(1, '((0,0), (10,20), (70,-20))' ) +\g +insert into oge_longpolygon values +(2, '((30.5,50), (20,200), (100,200), (125,15.5))' ) +\g + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/GNUmakefile new file mode 100644 index 000000000..3af855fdf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/GNUmakefile @@ -0,0 +1,19 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrjmldataset.o ogrjmllayer.o ogrjmlwriterlayer.o + +ifeq ($(HAVE_EXPAT),yes) +CPPFLAGS += -DHAVE_EXPAT +endif + +CPPFLAGS := -I.. -I../.. $(EXPAT_INCLUDE) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_jml.h + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/drv_jml.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/drv_jml.html new file mode 100644 index 000000000..0e80aa2b9 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/drv_jml.html @@ -0,0 +1,79 @@ + + +OGR JML driver + + + + +

    JML: OpenJUMP JML format

    + +(Driver available in GDAL 2.0 or later)

    + +OGR has support for reading and writing .JML files used by the OpenJUMP software. +Read support is only available if GDAL is built with expat library support

    + +.jml is a variant of GML format. There is no formal definition of the format. +It supports a single layer per file, mixed geometry types, and for each feature, +a geometry and several attributes of type integer, double, string, date or object. +That object data type, used for example to store 64 bit integers, but potentially +arbitrary serialized Java objects, is converted as string when reading. +Contrary to GML, the definition of fields is embedded in the .jml file, at its +beginning.

    + +.jml doesn't support spatial reference systems.

    + +

    Encoding issues

    + +Expat library supports reading the following built-in encodings : +
      +
    • US-ASCII
    • +
    • UTF-8
    • +
    • UTF-16
    • +
    • ISO-8859-1
    • +
    • Windows-1252
    • +
    + +The content returned by OGR will be encoded in UTF-8, after the conversion from the +encoding mentionned in the file header is. But files produced by OpenJUMP are +always UTF-8 encoded.

    + +When writing a JML file, the driver expects UTF-8 content to be passed in.

    + +

    Styling

    + +OpenJUMP uses an optional string attribute called "R_G_B" to determine the color +of objects. The field value is "RRGGBB" where RR, GG, BB are respectively the +value of the red, green and blue components expressed as hexadecimal values from +00 to FF. When reading a .jml file, OGR will translate the R_G_B attribute to +the Feature Style encoding, unless a OGR_STYLE attribute is present. When +writing a .jml file, OGR will extract from the Feature Style string the color of +the PEN tool or the forecolor of the BRUSH tool to write the R_G_B attribute, +unless the R_G_B attribute is defined in the provided feature. The addition of +the R_G_B attribute can be disabled by setting the CREATE_R_G_B_FIELD layer +creation option to NO.

    + +

    Creation Issues

    + +The JML writer supports the following layer creation options: +
      +
    • CREATE_R_G_B_FIELD=YES/NO: whether the create a R_G_B field that will +contain the color of the PEN tool or the forecolor of the BRUSH tool of the OGR +Feature Style string. Default value : YES
    • +
    • CREATE_OGR_STYLE_FIELD=YES/NO: whether the create a OGR_STYLE field that will +contain the Feature Style string. Default value : NO
    • +
    +

    + +

    See Also

    + + + +

    Credits

    + +

    The author wishes to thank Jukka Rahkonen for funding the development of this +driver.

    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/makefile.vc new file mode 100644 index 000000000..b5662c723 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/makefile.vc @@ -0,0 +1,18 @@ + +OBJ = ogrjmldataset.obj ogrjmllayer.obj ogrjmlwriterlayer.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF EXPAT_DIR +EXTRAFLAGS = -I.. -I..\.. $(EXPAT_INCLUDE) -DHAVE_EXPAT=1 +!ELSE +EXTRAFLAGS = -I.. -I..\.. +!ENDIF + +default: $(OBJ) + +clean: + -del *.obj *.pdb + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/ogr_jml.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/ogr_jml.h new file mode 100644 index 000000000..924b73851 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/ogr_jml.h @@ -0,0 +1,212 @@ +/****************************************************************************** + * $Id: ogr_jml.h 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: JML .jml Translator + * Purpose: Definition of classes for OGR JML driver. + * Author: Even Rouault, even dot rouault at spatialys dot com + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_JML_H_INCLUDED +#define _OGR_JML_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "ogr_p.h" + +#ifdef HAVE_EXPAT +#include "ogr_expat.h" +#endif + +#include + +class OGRJMLDataset; + +#ifdef HAVE_EXPAT + +/************************************************************************/ +/* OGRJMLColumn */ +/************************************************************************/ + +class OGRJMLColumn +{ + public: + CPLString osName; + CPLString osType; + CPLString osElementName; + CPLString osAttributeName; + CPLString osAttributeValue; + int bIsBody; /* if false: attribute */ +}; + +/************************************************************************/ +/* OGRJMLLayer */ +/************************************************************************/ + +class OGRJMLLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + OGRJMLDataset *poDS; + + int nNextFID; + VSILFILE* fp; + int bHasReadSchema; + + XML_Parser oParser; + + int currentDepth; + int bStopParsing; + int nWithoutEventCounter; + int nDataHandlerCounter; + + int bAccumulateElementValue; + char *pszElementValue; + int nElementValueLen; + int nElementValueAlloc; + + OGRFeature* poFeature; + OGRFeature ** ppoFeatureTab; + int nFeatureTabLength; + int nFeatureTabIndex; + + int bSchemaFinished; + int nJCSGMLInputTemplateDepth; + int nCollectionElementDepth; + CPLString osCollectionElement; + int nFeatureElementDepth; + CPLString osFeatureElement; + int nGeometryElementDepth; + CPLString osGeometryElement; + int nColumnDepth; + int nNameDepth; + int nTypeDepth; + int nAttributeElementDepth; + int iAttr; + int iRGBField; + + OGRJMLColumn oCurColumn; + std::vector aoColumns; + + void AddStringToElementValue(const char *data, int nLen); + void StopAccumulate(); + + void LoadSchema(); + + public: + OGRJMLLayer(const char *pszLayerName, + OGRJMLDataset* poDS, + VSILFILE* fp ); + ~OGRJMLLayer(); + + const char *GetName() { return poFeatureDefn->GetName(); } + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRFeatureDefn * GetLayerDefn(); + + int TestCapability( const char * ); + + void startElementCbk(const char *pszName, const char **ppszAttr); + void endElementCbk(const char *pszName); + void dataHandlerCbk(const char *data, int nLen); + + void startElementLoadSchemaCbk(const char *pszName, const char **ppszAttr); + void endElementLoadSchemaCbk(const char *pszName); +}; + +#endif /* HAVE_EXPAT */ + +/************************************************************************/ +/* OGRJMLWriterLayer */ +/************************************************************************/ + +class OGRJMLWriterLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + OGRJMLDataset *poDS; + VSILFILE *fp; + int bFeaturesWritten; + int bAddRGBField; + int bAddOGRStyleField; + int bClassicGML; + int nNextFID; + + void WriteColumnDeclaration( const char* pszName, + const char* pszType ); + + public: + OGRJMLWriterLayer(const char* pszLayerName, + OGRJMLDataset* poDS, + VSILFILE* fp, + int bAddRGBField, + int bAddOGRStyleField, + int bClassicGML ); + ~OGRJMLWriterLayer(); + + void ResetReading() {} + OGRFeature * GetNextFeature() { return NULL; } + + OGRErr ICreateFeature( OGRFeature *poFeature ); + OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRJMLDataset */ +/************************************************************************/ + +class OGRJMLDataset : public GDALDataset +{ + OGRLayer *poLayer; + + VSILFILE *fp; /* Virtual file API */ + int bWriteMode; + + public: + OGRJMLDataset(); + ~OGRJMLDataset(); + + int GetLayerCount() { return poLayer != NULL ? 1 : 0; } + OGRLayer* GetLayer( int ); + + OGRLayer * ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ); + + int TestCapability( const char * ); + + static int Identify( GDALOpenInfo* poOpenInfo ); + static GDALDataset* Open( GDALOpenInfo* poOpenInfo ); + static GDALDataset* Create( const char *pszFilename, + int nBands, + int nXSize, + int nYSize, + GDALDataType eDT, + char **papszOptions ); +}; + +#endif /* ndef _OGR_JML_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/ogrjmldataset.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/ogrjmldataset.cpp new file mode 100644 index 000000000..f7fb48f27 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/ogrjmldataset.cpp @@ -0,0 +1,249 @@ +/****************************************************************************** + * $Id: ogrjmldataset.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: JML Translator + * Purpose: Implements OGRJMLDataset class + * Author: Even Rouault, even dot rouault at spatialys dot com + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_jml.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +extern "C" void RegisterOGRJML(); + +// g++ -DHAVE_EXPAT -fPIC -shared -Wall -g -DDEBUG ogr/ogrsf_frmts/jml/*.cpp -o ogr_JML.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/jml -L. -lgdal + +CPL_CVSID("$Id: ogrjmldataset.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRJMLDataset() */ +/************************************************************************/ + +OGRJMLDataset::OGRJMLDataset() + +{ + poLayer = NULL; + + fp = NULL; + + bWriteMode = FALSE; +} + +/************************************************************************/ +/* ~OGRJMLDataset() */ +/************************************************************************/ + +OGRJMLDataset::~OGRJMLDataset() + +{ + delete poLayer; + + if ( fp != NULL ) + VSIFCloseL( fp); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRJMLDataset::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return bWriteMode && poLayer == NULL; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRJMLDataset::GetLayer( int iLayer ) + +{ + if( iLayer != 0 ) + return NULL; + else + return poLayer; +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int OGRJMLDataset::Identify( GDALOpenInfo* poOpenInfo ) +{ + return poOpenInfo->nHeaderBytes != 0 && + strstr((const char*)poOpenInfo->pabyHeader, "fpL == NULL || + poOpenInfo->eAccess == GA_Update ) + return NULL; + +#ifndef HAVE_EXPAT + CPLError(CE_Failure, CPLE_NotSupported, + "OGR/JML driver has not been built with read support. Expat library required"); + return NULL; +#else + OGRJMLDataset* poDS = new OGRJMLDataset(); + poDS->SetDescription( poOpenInfo->pszFilename ); + + poDS->fp = poOpenInfo->fpL; + poOpenInfo->fpL = NULL; + + poDS->poLayer = new OGRJMLLayer( CPLGetBasename(poOpenInfo->pszFilename), poDS, poDS->fp); + + return poDS; +#endif +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset* OGRJMLDataset::Create( const char *pszFilename, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED int nBands, + CPL_UNUSED GDALDataType eDT, + CPL_UNUSED char **papszOptions ) +{ + if (strcmp(pszFilename, "/dev/stdout") == 0) + pszFilename = "/vsistdout/"; + +/* -------------------------------------------------------------------- */ +/* Do not override exiting file. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + + if( VSIStatL( pszFilename, &sStatBuf ) == 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "You have to delete %s before being able to create it with the JML driver", + pszFilename); + return NULL; + } + + OGRJMLDataset* poDS = new OGRJMLDataset(); + +/* -------------------------------------------------------------------- */ +/* Create the output file. */ +/* -------------------------------------------------------------------- */ + poDS->bWriteMode = TRUE; + poDS->SetDescription( pszFilename ); + + poDS->fp = VSIFOpenL( pszFilename, "w" ); + if( poDS->fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create JML file %s.", + pszFilename ); + delete poDS; + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * OGRJMLDataset::ICreateLayer( const char * pszLayerName, + CPL_UNUSED OGRSpatialReference *poSRS, + CPL_UNUSED OGRwkbGeometryType eType, + char ** papszOptions ) +{ + if (!bWriteMode || poLayer != NULL) + return NULL; + + int bAddRGBField = CSLTestBoolean( + CSLFetchNameValueDef(papszOptions, "CREATE_R_G_B_FIELD", "YES")); + int bAddOGRStyleField = CSLTestBoolean( + CSLFetchNameValueDef(papszOptions, "CREATE_OGR_STYLE_FIELD", "NO")); + int bClassicGML = CSLTestBoolean( + CSLFetchNameValueDef(papszOptions, "CLASSIC_GML", "NO")); + poLayer = new OGRJMLWriterLayer( pszLayerName, this, fp, + bAddRGBField, bAddOGRStyleField, + bClassicGML); + + return poLayer; +} + +/************************************************************************/ +/* RegisterOGRJML() */ +/************************************************************************/ + +extern "C" +{ + +void RegisterOGRJML() +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "JML" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "JML" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "OpenJUMP JML" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jml" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_jml.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" " ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime" ); + + poDriver->pfnOpen = OGRJMLDataset::Open; + poDriver->pfnIdentify = OGRJMLDataset::Identify; + poDriver->pfnCreate = OGRJMLDataset::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/ogrjmllayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/ogrjmllayer.cpp new file mode 100644 index 000000000..a4bd5ad2a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/ogrjmllayer.cpp @@ -0,0 +1,785 @@ +/****************************************************************************** + * $Id: ogrjmllayer.cpp 27906 2014-10-24 18:38:57Z rouault $ + * + * Project: JML Translator + * Purpose: Implements OGRJMLLayer class. + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_jml.h" +#include "cpl_conv.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrjmllayer.cpp 27906 2014-10-24 18:38:57Z rouault $"); + +#ifdef HAVE_EXPAT + +/************************************************************************/ +/* OGRJMLLayer() */ +/************************************************************************/ + +OGRJMLLayer::OGRJMLLayer( const char* pszLayerName, + OGRJMLDataset* poDS, + VSILFILE* fp ) + +{ + nNextFID = 0; + + this->poDS = poDS; + this->fp = fp; + + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + bAccumulateElementValue = FALSE; + pszElementValue = (char*)CPLCalloc(1024, 1); + nElementValueLen = 0; + nElementValueAlloc = 1024; + + ppoFeatureTab = NULL; + nFeatureTabIndex = 0; + nFeatureTabLength = 0; + + nWithoutEventCounter = 0; + nDataHandlerCounter = 0; + currentDepth = 0; + bStopParsing = FALSE; + bHasReadSchema = FALSE; + + bSchemaFinished = FALSE; + nJCSGMLInputTemplateDepth = 0; + nCollectionElementDepth = 0; + nFeatureElementDepth = 0; + nGeometryElementDepth = 0; + nColumnDepth = 0; + nNameDepth = 0; + nTypeDepth = 0; + nAttributeElementDepth = 0; + iAttr = -1; + iRGBField = -1; + + poFeature = NULL; + + oParser = NULL; +} + +/************************************************************************/ +/* ~OGRJMLLayer() */ +/************************************************************************/ + +OGRJMLLayer::~OGRJMLLayer() + +{ + if (oParser) + XML_ParserFree(oParser); + poFeatureDefn->Release(); + + CPLFree(pszElementValue); + + int i; + for(i=nFeatureTabIndex;istartElementCbk(pszName, ppszAttr); +} + +static void XMLCALL endElementCbk(void *pUserData, const char *pszName) +{ + ((OGRJMLLayer*)pUserData)->endElementCbk(pszName); +} + +static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen) +{ + ((OGRJMLLayer*)pUserData)->dataHandlerCbk(data, nLen); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRJMLLayer::ResetReading() + +{ + nNextFID = 0; + + VSIFSeekL( fp, 0, SEEK_SET ); + if (oParser) + XML_ParserFree(oParser); + + oParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk); + XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk); + XML_SetUserData(oParser, this); + + int i; + for(i=nFeatureTabIndex;i 0 && nAttributeElementDepth == 0 && + nGeometryElementDepth == 0 && osGeometryElement.compare(pszName) == 0 ) + { + nGeometryElementDepth = currentDepth; + bAccumulateElementValue = TRUE; + } + else if( nFeatureElementDepth > 0 && nAttributeElementDepth == 0 && + nGeometryElementDepth == 0 ) + { + /* We assume that attributes are present in the order they are */ + /* declared, so as a first guess, we can try the aoColumns[iAttr + 1] */ + int i = (iAttr+1 < poFeatureDefn->GetFieldCount()) ? -1 : 0; + for(; i<(int)aoColumns.size();i++) + { + const OGRJMLColumn& oColumn = + (i < 0) ? aoColumns[iAttr + 1] : aoColumns[i]; + if(oColumn.osElementName != pszName ) + continue; + + if( oColumn.bIsBody ) + { + if( oColumn.osAttributeName.size() && + ppszAttr != NULL && + oColumn.osAttributeName.compare(ppszAttr[0]) == 0 && + oColumn.osAttributeValue.compare(ppszAttr[1]) == 0 ) + { + /* value */ + + bAccumulateElementValue = TRUE; + nAttributeElementDepth = currentDepth; + iAttr = (i < 0) ? iAttr + 1 : i; + break; + } + else if( oColumn.osAttributeName.size() == 0 ) + { + /* value */ + + bAccumulateElementValue = TRUE; + nAttributeElementDepth = currentDepth; + iAttr = (i < 0) ? iAttr + 1 : i; + break; + } + } + else if( oColumn.osAttributeName.size() && + ppszAttr != NULL && + oColumn.osAttributeName.compare(ppszAttr[0]) == 0 ) + { + /* */ + + AddStringToElementValue(ppszAttr[1], strlen(ppszAttr[1])); + + nAttributeElementDepth = currentDepth; + iAttr = (i < 0) ? iAttr + 1 : i; + break; + } + } + } + else if( nGeometryElementDepth > 0 ) + { + AddStringToElementValue("<", 1); + AddStringToElementValue(pszName, (int)strlen(pszName)); + + const char** papszIter = ppszAttr; + while( papszIter && *papszIter != NULL ) + { + AddStringToElementValue(" ", 1); + AddStringToElementValue(papszIter[0], strlen(papszIter[0])); + AddStringToElementValue("=\"", 2); + AddStringToElementValue(papszIter[1], strlen(papszIter[1])); + AddStringToElementValue("\"", 1); + papszIter += 2; + } + + AddStringToElementValue(">", 1); + } + else if( nCollectionElementDepth > 0 && + nFeatureElementDepth == 0 && osFeatureElement.compare(pszName) == 0 ) + { + nFeatureElementDepth = currentDepth; + poFeature = new OGRFeature(poFeatureDefn); + } + else if( nCollectionElementDepth == 0 && osCollectionElement.compare(pszName) == 0 ) + { + nCollectionElementDepth = currentDepth; + } + + currentDepth++; +} + +/************************************************************************/ +/* StopAccumulate() */ +/************************************************************************/ + +void OGRJMLLayer::StopAccumulate() +{ + bAccumulateElementValue = FALSE; + nElementValueLen = 0; + pszElementValue[0] = '\0'; +} + +/************************************************************************/ +/* endElementCbk() */ +/************************************************************************/ + +void OGRJMLLayer::endElementCbk(const char *pszName) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + currentDepth--; + + if( nAttributeElementDepth == currentDepth ) + { + if( nElementValueLen ) + poFeature->SetField(iAttr, pszElementValue); + nAttributeElementDepth = 0; + StopAccumulate(); + } + else if( nGeometryElementDepth > 0 && currentDepth > nGeometryElementDepth ) + { + AddStringToElementValue("", 1); + } + else if( nGeometryElementDepth == currentDepth ) + { + if( nElementValueLen ) + { + OGRGeometry* poGeom = (OGRGeometry* )OGR_G_CreateFromGML(pszElementValue); + if( poGeom != NULL && + poGeom->getGeometryType() == wkbGeometryCollection && + poGeom->IsEmpty() ) + { + delete poGeom; + } + else + poFeature->SetGeometryDirectly(poGeom); + } + + nGeometryElementDepth = 0; + StopAccumulate(); + } + else if( nFeatureElementDepth == currentDepth ) + { + /* Builds a style string from R_G_B if we don't already have a */ + /* style string */ + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + int R, G, B; + if( iRGBField >= 0 && poFeature->IsFieldSet(iRGBField) && + poFeature->GetStyleString() == NULL && poGeom != NULL && + sscanf(poFeature->GetFieldAsString(iRGBField), + "%02X%02X%02X", &R, &G, &B) == 3 ) + { + OGRwkbGeometryType eGeomType = wkbFlatten(poGeom->getGeometryType()); + if( eGeomType == wkbPoint || eGeomType == wkbMultiPoint || + eGeomType == wkbLineString || eGeomType == wkbMultiLineString ) + { + poFeature->SetStyleString(CPLSPrintf("PEN(c:#%02X%02X%02X)", R, G, B)); + } + else if( eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon ) + { + poFeature->SetStyleString(CPLSPrintf("BRUSH(fc:#%02X%02X%02X)", R, G, B)); + } + } + + poFeature->SetFID(nNextFID++); + + if( (m_poFilterGeom == NULL + || FilterGeometry( poGeom ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + ppoFeatureTab = (OGRFeature**) + CPLRealloc(ppoFeatureTab, + sizeof(OGRFeature*) * (nFeatureTabLength + 1)); + ppoFeatureTab[nFeatureTabLength] = poFeature; + nFeatureTabLength++; + } + else + { + delete poFeature; + } + poFeature = NULL; + iAttr = -1; + + nFeatureElementDepth = 0; + } + else if( nCollectionElementDepth == currentDepth ) + { + nCollectionElementDepth = 0; + } +} + +/************************************************************************/ +/* AddStringToElementValue() */ +/************************************************************************/ + +void OGRJMLLayer::AddStringToElementValue(const char *data, int nLen) +{ + if( nElementValueLen + nLen + 1 > nElementValueAlloc ) + { + char* pszNewElementValue = (char*) VSIRealloc(pszElementValue, + nElementValueLen + nLen + 1 + 1000); + if (pszNewElementValue == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + nElementValueAlloc = nElementValueLen + nLen + 1 + 1000; + pszElementValue = pszNewElementValue; + } + memcpy(pszElementValue + nElementValueLen, data, nLen); + nElementValueLen += nLen; + pszElementValue[nElementValueLen] = '\0'; + if (nElementValueLen > 10000000) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + } +} + +/************************************************************************/ +/* dataHandlerCbk() */ +/************************************************************************/ + +void OGRJMLLayer::dataHandlerCbk(const char *data, int nLen) +{ + if (bStopParsing) return; + + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "File probably corrupted (million laugh pattern)"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + + nWithoutEventCounter = 0; + + if (bAccumulateElementValue) + { + AddStringToElementValue(data, nLen); + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRJMLLayer::GetNextFeature() +{ + if (!bHasReadSchema) + LoadSchema(); + + if (bStopParsing) + return NULL; + + if (nFeatureTabIndex < nFeatureTabLength) + { + return ppoFeatureTab[nFeatureTabIndex++]; + } + + if (VSIFEofL(fp)) + return NULL; + + char aBuf[BUFSIZ]; + + nFeatureTabLength = 0; + nFeatureTabIndex = 0; + + nWithoutEventCounter = 0; + + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = + (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fp ); + nDone = VSIFEofL(fp); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of JML file failed : %s " + "at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + bStopParsing = TRUE; + } + nWithoutEventCounter ++; + } while (!nDone && !bStopParsing && nFeatureTabLength == 0 && + nWithoutEventCounter < 10); + + if (nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + return (nFeatureTabLength) ? ppoFeatureTab[nFeatureTabIndex++] : NULL; +} + +static void XMLCALL startElementLoadSchemaCbk(void *pUserData, const char *pszName, + const char **ppszAttr) +{ + ((OGRJMLLayer*)pUserData)->startElementLoadSchemaCbk(pszName, ppszAttr); +} + +static void XMLCALL endElementLoadSchemaCbk(void *pUserData, const char *pszName) +{ + ((OGRJMLLayer*)pUserData)->endElementLoadSchemaCbk(pszName); +} + +/************************************************************************/ +/* LoadSchema() */ +/************************************************************************/ + +/** This function parses the beginning of the file to detect the fields */ +void OGRJMLLayer::LoadSchema() +{ + if (bHasReadSchema) + return; + + bHasReadSchema = TRUE; + + oParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oParser, ::startElementLoadSchemaCbk, + ::endElementLoadSchemaCbk); + XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk); + XML_SetUserData(oParser, this); + + VSIFSeekL( fp, 0, SEEK_SET ); + + char aBuf[BUFSIZ]; + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fp ); + nDone = VSIFEofL(fp); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of JML file failed : %s at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + bStopParsing = TRUE; + } + nWithoutEventCounter ++; + } while (!nDone && !bStopParsing && !bSchemaFinished && nWithoutEventCounter < 10); + + XML_ParserFree(oParser); + oParser = NULL; + + if (nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + if( osCollectionElement.size() == 0 || osFeatureElement.size() == 0 || + osGeometryElement.size() == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Missing CollectionElement, FeatureElement or GeometryElement"); + bStopParsing = TRUE; + } + + ResetReading(); +} + + +/************************************************************************/ +/* startElementLoadSchemaCbk() */ +/************************************************************************/ + +void OGRJMLLayer::startElementLoadSchemaCbk(const char *pszName, + const char **ppszAttr) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + if( nJCSGMLInputTemplateDepth == 0 && + strcmp(pszName, "JCSGMLInputTemplate") == 0 ) + nJCSGMLInputTemplateDepth = currentDepth; + else if( nJCSGMLInputTemplateDepth > 0 ) + { + if( nCollectionElementDepth == 0 && + strcmp(pszName, "CollectionElement") == 0 ) + { + nCollectionElementDepth = currentDepth; + bAccumulateElementValue = TRUE; + } + else if( nFeatureElementDepth == 0 && + strcmp(pszName, "FeatureElement") == 0 ) + { + nFeatureElementDepth = currentDepth; + bAccumulateElementValue = TRUE; + } + else if( nGeometryElementDepth == 0 && + strcmp(pszName, "GeometryElement") == 0 ) + { + nGeometryElementDepth = currentDepth; + bAccumulateElementValue = TRUE; + } + else if( nColumnDepth == 0 && strcmp(pszName, "column") == 0 ) + { + nColumnDepth = currentDepth; + oCurColumn.osName = ""; + oCurColumn.osType = ""; + oCurColumn.osElementName = ""; + oCurColumn.osAttributeName = ""; + oCurColumn.osAttributeValue = ""; + oCurColumn.bIsBody = FALSE; + } + else if( nColumnDepth > 0 ) + { + if( nNameDepth == 0 && strcmp(pszName, "name") == 0 ) + { + nNameDepth = currentDepth; + bAccumulateElementValue = TRUE; + } + else if( nTypeDepth == 0 && strcmp(pszName, "type") == 0 ) + { + nTypeDepth = currentDepth; + bAccumulateElementValue = TRUE; + } + else if( strcmp(pszName, "valueElement") == 0 ) + { + const char** papszIter = ppszAttr; + while( papszIter && *papszIter != NULL ) + { + if( strcmp(*papszIter, "elementName") == 0 ) + oCurColumn.osElementName = papszIter[1]; + else if( strcmp(*papszIter, "attributeName") == 0 ) + oCurColumn.osAttributeName = papszIter[1]; + else if( strcmp(*papszIter, "attributeValue") == 0 ) + oCurColumn.osAttributeValue = papszIter[1]; + papszIter += 2; + } + } + else if( strcmp(pszName, "valueLocation") == 0 ) + { + const char** papszIter = ppszAttr; + while( papszIter && *papszIter != NULL ) + { + if( strcmp(*papszIter, "position") == 0 ) + oCurColumn.bIsBody = strcmp(papszIter[1], "body") == 0; + else if( strcmp(*papszIter, "attributeName") == 0 ) + oCurColumn.osAttributeName = papszIter[1]; + papszIter += 2; + } + } + } + } + + currentDepth++; +} + +/************************************************************************/ +/* endElementLoadSchemaCbk() */ +/************************************************************************/ + +void OGRJMLLayer::endElementLoadSchemaCbk(CPL_UNUSED const char *pszName) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + currentDepth--; + + if( nJCSGMLInputTemplateDepth == currentDepth ) + { + nJCSGMLInputTemplateDepth = 0; + bSchemaFinished = TRUE; + } + else if( nCollectionElementDepth == currentDepth ) + { + nCollectionElementDepth = 0; + osCollectionElement = pszElementValue; + //CPLDebug("JML", "osCollectionElement = %s", osCollectionElement.c_str()); + StopAccumulate(); + } + else if( nFeatureElementDepth == currentDepth ) + { + nFeatureElementDepth = 0; + osFeatureElement = pszElementValue; + //CPLDebug("JML", "osFeatureElement = %s", osFeatureElement.c_str()); + StopAccumulate(); + } + else if( nGeometryElementDepth == currentDepth ) + { + nGeometryElementDepth = 0; + osGeometryElement = pszElementValue; + //CPLDebug("JML", "osGeometryElement = %s", osGeometryElement.c_str()); + StopAccumulate(); + } + else if( nColumnDepth == currentDepth ) + { + int bIsOK = TRUE; + if( oCurColumn.osName.size() == 0 ) + bIsOK = FALSE; + if( oCurColumn.osType.size() == 0 ) + bIsOK = FALSE; + if( oCurColumn.osElementName.size() == 0 ) + bIsOK = FALSE; + if( oCurColumn.bIsBody ) + { + if( oCurColumn.osAttributeName.size() == 0 && + oCurColumn.osAttributeValue.size() != 0 ) + bIsOK = FALSE; + if( oCurColumn.osAttributeName.size() != 0 && + oCurColumn.osAttributeValue.size() == 0 ) + bIsOK = FALSE; + /* Only 2 valid possibilities : */ + /* value */ + /* value */ + } + else + { + /* */ + if( oCurColumn.osAttributeName.size() == 0 ) + bIsOK = FALSE; + if( oCurColumn.osAttributeValue.size() != 0 ) + bIsOK = FALSE; + } + + if( bIsOK ) + { + OGRFieldType eType = OFTString; + if( EQUAL(oCurColumn.osType, "INTEGER") ) + eType = OFTInteger; + else if( EQUAL(oCurColumn.osType, "DOUBLE") ) + eType = OFTReal; + else if( EQUAL(oCurColumn.osType, "DATE") ) + eType = OFTDateTime; + OGRFieldDefn oField( oCurColumn.osName, eType ); + + if( oCurColumn.osName == "R_G_B" && eType == OFTString ) + iRGBField = poFeatureDefn->GetFieldCount(); + + poFeatureDefn->AddFieldDefn(&oField); + aoColumns.push_back(oCurColumn); + } + else + { + CPLDebug("JML", "Invalid column definition: name = %s, type = %s, " + "elementName = %s, attributeName = %s, attributeValue = %s, bIsBody = %d", + oCurColumn.osName.c_str(), + oCurColumn.osType.c_str(), + oCurColumn.osElementName.c_str(), + oCurColumn.osAttributeName.c_str(), + oCurColumn.osAttributeValue.c_str(), + oCurColumn.bIsBody); + } + + nColumnDepth = 0; + } + else if( nNameDepth == currentDepth ) + { + nNameDepth = 0; + oCurColumn.osName = pszElementValue; + //CPLDebug("JML", "oCurColumn.osName = %s", oCurColumn.osName.c_str()); + StopAccumulate(); + } + else if( nTypeDepth == currentDepth ) + { + nTypeDepth = 0; + oCurColumn.osType = pszElementValue; + //CPLDebug("JML", "oCurColumn.osType = %s", oCurColumn.osType.c_str()); + StopAccumulate(); + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRJMLLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + else + return FALSE; +} + +#endif /* HAVE_EXPAT */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/ogrjmlwriterlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/ogrjmlwriterlayer.cpp new file mode 100644 index 000000000..984a6b4af --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/jml/ogrjmlwriterlayer.cpp @@ -0,0 +1,360 @@ +/****************************************************************************** + * $Id: ogrjmlwriterlayer.cpp 28900 2015-04-14 09:40:34Z rouault $ + * + * Project: JML Translator + * Purpose: Implements OGRJMLWriterLayer class. + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_jml.h" +#include "cpl_conv.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrjmlwriterlayer.cpp 28900 2015-04-14 09:40:34Z rouault $"); + +/************************************************************************/ +/* OGRJMLWriterLayer() */ +/************************************************************************/ + +OGRJMLWriterLayer::OGRJMLWriterLayer( const char* pszLayerName, + OGRJMLDataset* poDS, + VSILFILE* fp, + int bAddRGBField, + int bAddOGRStyleField, + int bClassicGML ) + +{ + this->poDS = poDS; + this->fp = fp; + bFeaturesWritten = FALSE; + this->bAddRGBField = bAddRGBField; + this->bAddOGRStyleField = bAddOGRStyleField; + this->bClassicGML = bClassicGML; + nNextFID = 0; + + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + VSIFPrintfL(fp, "\n" + "\n" + "\n" + "featureCollection\n" + "feature\n" + "geometry\n" + "\n"); + +} + +/************************************************************************/ +/* ~OGRJMLWriterLayer() */ +/************************************************************************/ + +OGRJMLWriterLayer::~OGRJMLWriterLayer() +{ + if( !bFeaturesWritten ) + VSIFPrintfL(fp, "\n\n\n"); + VSIFPrintfL(fp, "\n\n"); + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* WriteColumnDeclaration() */ +/************************************************************************/ + +void OGRJMLWriterLayer::WriteColumnDeclaration( const char* pszName, + const char* pszType ) +{ + char* pszEscapedName = OGRGetXML_UTF8_EscapedString( pszName ); + if( bClassicGML ) + { + VSIFPrintfL(fp, " \n" + " %s\n" + " %s\n" + " \n" + " \n" + " \n", + pszEscapedName, pszType, pszEscapedName); + } + else + { + VSIFPrintfL(fp, " \n" + " %s\n" + " %s\n" + " \n" + " \n" + " \n", + pszEscapedName, pszType, pszEscapedName); + } + CPLFree(pszEscapedName); +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRJMLWriterLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + /* Finish column declaration if we haven't yet created a feature */ + if( !bFeaturesWritten ) + { + if( bAddOGRStyleField && poFeatureDefn->GetFieldIndex("OGR_STYLE") < 0 ) + { + WriteColumnDeclaration( "OGR_STYLE", "STRING" ); + } + if( bAddRGBField && poFeatureDefn->GetFieldIndex("R_G_B") < 0 ) + { + WriteColumnDeclaration( "R_G_B", "STRING" ); + } + VSIFPrintfL(fp, "\n\n\n"); + bFeaturesWritten = TRUE; + } + + if( bClassicGML ) + VSIFPrintfL(fp, " \n"); + VSIFPrintfL(fp, " \n"); + + /* Add geometry */ + VSIFPrintfL(fp, " \n"); + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if( poGeom != NULL ) + { + char* pszGML = poGeom->exportToGML(); + VSIFPrintfL(fp, " %s\n", pszGML); + CPLFree(pszGML); + } + else + { + VSIFPrintfL(fp, " %s\n", + ""); + } + VSIFPrintfL(fp, " \n"); + + /* Add fields */ + for(int i=0;iGetFieldCount();i++) + { + char* pszName = OGRGetXML_UTF8_EscapedString( + poFeatureDefn->GetFieldDefn(i)->GetNameRef() ); + if( bClassicGML ) + VSIFPrintfL(fp, " <%s>", pszName); + else + VSIFPrintfL(fp, " ", pszName); + if( poFeature->IsFieldSet(i) ) + { + OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType(); + if( eType == OFTString ) + { + char* pszValue = OGRGetXML_UTF8_EscapedString( + poFeature->GetFieldAsString(i) ); + VSIFPrintfL(fp, "%s", pszValue); + CPLFree(pszValue); + } + else if( eType == OFTDateTime ) + { + int nYear, nMonth, nDay, nHour, nMinute, nTZFlag; + float fSecond; + poFeature->GetFieldAsDateTime(i, &nYear, &nMonth, &nDay, + &nHour, &nMinute, &fSecond, &nTZFlag); + /* When writing time zone, OpenJUMP expects .XXX seconds */ + /* to be written */ + if( nTZFlag > 1 || OGR_GET_MS(fSecond) != 0 ) + VSIFPrintfL(fp, "%04d-%02d-%02dT%02d:%02d:%06.3f", + nYear, nMonth, nDay, + nHour, nMinute, fSecond); + else + VSIFPrintfL(fp, "%04d-%02d-%02dT%02d:%02d:%02d", + nYear, nMonth, nDay, + nHour, nMinute, (int)fSecond); + if( nTZFlag > 1 ) + { + int nOffset = (nTZFlag - 100) * 15; + int nHours = (int) (nOffset / 60); // round towards zero + int nMinutes = ABS(nOffset - nHours * 60); + + if( nOffset < 0 ) + { + VSIFPrintfL(fp, "-" ); + nHours = ABS(nHours); + } + else + VSIFPrintfL(fp, "+" ); + + VSIFPrintfL(fp, "%02d%02d", nHours, nMinutes ); + } + } + else + { + VSIFPrintfL(fp, "%s", poFeature->GetFieldAsString(i)); + } + } + if( bClassicGML ) + VSIFPrintfL(fp, "\n", pszName); + else + VSIFPrintfL(fp, "\n"); + CPLFree(pszName); + } + + /* Add OGR_STYLE from feature style string (if asked) */ + if( bAddOGRStyleField && poFeatureDefn->GetFieldIndex("OGR_STYLE") < 0 ) + { + if( bClassicGML ) + VSIFPrintfL(fp, " "); + else + VSIFPrintfL(fp, " ", "OGR_STYLE"); + if( poFeature->GetStyleString() != NULL ) + { + char* pszValue = OGRGetXML_UTF8_EscapedString( poFeature->GetStyleString() ); + VSIFPrintfL(fp, "%s", pszValue); + CPLFree(pszValue); + } + if( bClassicGML ) + VSIFPrintfL(fp, "\n"); + else + VSIFPrintfL(fp, "\n"); + } + + /* Derive R_G_B field from feature style string */ + if( bAddRGBField && poFeatureDefn->GetFieldIndex("R_G_B") < 0 ) + { + if( bClassicGML ) + VSIFPrintfL(fp, " "); + else + VSIFPrintfL(fp, " ", "R_G_B"); + if( poFeature->GetStyleString() != NULL ) + { + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + OGRwkbGeometryType eGeomType = + poGeom ? wkbFlatten(poGeom->getGeometryType()) : wkbUnknown; + OGRStyleMgr oMgr; + oMgr.InitFromFeature(poFeature); + for(int i=0;iGetType() == OGRSTCPen && + eGeomType != wkbPolygon && eGeomType != wkbMultiPolygon ) + { + GBool bIsNull; + pszColor = ((OGRStylePen*)poTool)->Color(bIsNull); + if( bIsNull ) pszColor = NULL; + } + else if( poTool->GetType() == OGRSTCBrush ) + { + GBool bIsNull; + pszColor = ((OGRStyleBrush*)poTool)->ForeColor(bIsNull); + if( bIsNull ) pszColor = NULL; + } + int R, G, B, A; + if( pszColor != NULL && + poTool->GetRGBFromString(pszColor, R, G, B, A) && A != 0 ) + { + VSIFPrintfL(fp, "%02X%02X%02X", R, G, B); + } + delete poTool; + } + } + } + if( bClassicGML ) + VSIFPrintfL(fp, "\n"); + else + VSIFPrintfL(fp, "\n"); + } + + VSIFPrintfL(fp, " \n"); + if( bClassicGML ) + VSIFPrintfL(fp, " \n"); + + poFeature->SetFID(nNextFID ++); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRJMLWriterLayer::CreateField( OGRFieldDefn *poFieldDefn, + int bApproxOK ) +{ + if( bFeaturesWritten ) + return OGRERR_FAILURE; + + if( !bAddRGBField && strcmp( poFieldDefn->GetNameRef(), "R_G_B" ) == 0 ) + return OGRERR_FAILURE; + + const char* pszType; + OGRFieldType eType = poFieldDefn->GetType(); + if( eType == OFTInteger ) + pszType = "INTEGER"; + else if( eType == OFTInteger64 ) + pszType = "OBJECT"; + else if( eType == OFTReal ) + pszType = "DOUBLE"; + else if( eType == OFTDate || eType == OFTDateTime ) + pszType = "DATE"; + else + { + if( eType != OFTString ) + { + if( bApproxOK ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Field of type %s unhandled natively. Converting to string", + OGRFieldDefn::GetFieldTypeName(eType)); + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Field of type %s unhandled natively.", + OGRFieldDefn::GetFieldTypeName(eType)); + return OGRERR_FAILURE; + } + } + pszType = "STRING"; + } + WriteColumnDeclaration( poFieldDefn->GetNameRef(), pszType ); + + poFeatureDefn->AddFieldDefn( poFieldDefn ); + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRJMLWriterLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + else if( EQUAL(pszCap,OLCSequentialWrite) ) + return TRUE; + else if( EQUAL(pszCap,OLCCreateField) ) + return !bFeaturesWritten; + else + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/GNUmakefile new file mode 100644 index 000000000..18bb0b74a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/GNUmakefile @@ -0,0 +1,26 @@ + + +include ../../../GDALmake.opt + +CORE_OBJ = ogr2kmlgeometry.o + +OGR_OBJ = ogrkmldriver.o ogrkmldatasource.o ogrkmllayer.o + +OBJ = $(CORE_OBJ) $(OGR_OBJ) + +ifeq ($(HAVE_EXPAT),yes) +OGR_OBJ += kml.o kmlnode.o kmlvector.o +CPPFLAGS += -DHAVE_EXPAT +endif + +CPPFLAGS := -I. -I.. -I../.. -I$(GDAL_ROOT)/port -I$(GDAL_ROOT)/gcore -I$(GDAL_ROOT)/alg \ + -I$(GDAL_ROOT)/ogr -I$(GDAL_ROOT)/ogrsf_frmts $(EXPAT_INCLUDE) $(CPPFLAGS) + +#CFLAGS := $(filter-out -Wall,$(CFLAGS)) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o kmlview $(O_OBJ) + +$(O_OBJ): kml.h kmlnode.h kmlutility.h kmlvector.h ogr_kml.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/drv_kml.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/drv_kml.html new file mode 100644 index 000000000..e50b070df --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/drv_kml.html @@ -0,0 +1,129 @@ + + +KML - Keyhole Markup Language + + + + +

    KML - Keyhole Markup Language

    + +

    Keyhole Markup Language (KML) is an XML-based language for managing the display of 3D geospatial data. +KML has been accepted as an OGC standard, and is supported in one way or another +on the major GeoBrowsers. +Note that KML by specification uses only a single projection, EPSG:4326. All OGR KML output will be +presented in EPSG:4326. As such OGR will create layers in the correct coordinate system and transform +any geometries.

    + +

    At this time, only vector layers are handled by the KML driver. (there are additional scripts +supplied with the GDAL project that can build other kinds of output)

    + +

    KML Reading

    +

    KML reading is only available if GDAL/OGR is built with the Expat XML Parser, otherwise only KML writing will be supported.

    + +

    Supported geometry types are Point, Linestring, Polygon, MultiPoint, +MultiLineString, MultiPolygon and MultiGeometry. There are limitations, +for example: the nested nature of folders in a source KML file is lost; folder <description> tags will +not carry through to ouput. Since GDAL 1.6.1, folders containing multiple geometry types, like POINT and POLYGON, are supported.

    + +

    KML Writing

    +

    Since not all features of KML +are able to be represented in the Simple Features geometry model, you will not be able to generate +many KML-specific attributes from within GDAL/OGR. Please try +a few test files to get a sense of what is possible.

    + +

    When outputting KML, the OGR KML driver will translate each OGR Layer into a KML Folder +(you may encounter unexpected behavior +if you try to mix the geometry types of elements in a layer, e.g. LINESTRING and +POINT data).

    + +

    The KML Driver will rename some layers, or source KML folder names, into new names +it considers valid, for example 'Layer #0', the default name of the first unnamed +Layer, becomes 'Layer__0'.

    + + +

    KML is mix of formatting and feature data. The <description> tag of a Placemark will +be displayed in most geobrowsers as an HTML-filled balloon. When writing KML, Layer element +attributes are added as simple schema fields. This best preserves feature type information.

    +

    Limited support is available for fills, line color and other styling attributes. Please try a few sample +files to get a better sense of actual behavior.

    + +

    Encoding issues

    + +Expat library supports reading the following built-in encodings : +
      +
    • US-ASCII
    • +
    • UTF-8
    • +
    • UTF-16
    • +
    • ISO-8859-1
    • +
    + +OGR 1.8.0 adds supports for Windows-1252 encoding (for previous versions, altering the encoding +mentionned in the XML header to ISO-8859-1 might work in some cases).

    + +The content returned by OGR will be encoded in UTF-8, after the conversion from the +encoding mentionned in the file header is.

    + +If your KML file is not encoded in one of the previous encodings, it will not be parsed by the +KML driver. You may convert it into one of the supported encoding with the iconv utility +for example and change accordingly the encoding parameter value in the XML header.
    +

    + +When writing a KML file, the driver expects UTF-8 content to be passed in.

    + +

    Creation Options

    +

    The following dataset creation options are supported: +

      +
    • NameField: Allows you to specify the field to use for the KML <name> element. Default value : 'Name'
    • +
      ogr2ogr -f KML output.kml input.shp -dsco NameField=RegionName
      +
    • DescriptionField: Allows you to specify the field to use for the KML <description> element. Default value : 'Description'
    • + +
    • AltitudeMode: Allows you to specify the AltitudeMode to use for KML geometries. This will only affect 3D geometries +and must be one of the valid KML options. +See the relevant KML reference material +for further information. +
      ogr2ogr -f KML output.kml input.shp -dsco AltitudeMode=absolute
      +
    • +
    + + +

    VSI Virtual File System API support

    + +(Some features below might require OGR >= 1.9.0)

    + +The driver supports reading and writing to files managed by VSI Virtual File System API, which include +"regular" files, as well as files in the /vsizip/ (read-write) , /vsigzip/ (read-write) , /vsicurl/ (read-only) domains.

    + +Writing to /dev/stdout or /vsistdout/ is also supported.

    + +

    Example

    +The ogr2ogr utility can be used to dump the results of a PostGIS query to +KML: + +
    +ogr2ogr -f KML output.kml PG:'host=myserver dbname=warmerda' -sql "SELECT pop_1994 from canada where province_name = 'Alberta'"
    +
    +

    + +

    How to dump contents of .kml file as OGR sees it: +

    +ogrinfo -ro somedisplay.kml
    +
    +

    +

    Caveats

    +

    +Google Earth seems to have some limits regarding the number of coordinates in complex geometries like polygons. +If the problem appears, then problematic geometries are displayed completely or partially covered by vertical stripes. +Unfortunately, there are no exact number given in the KML specification about this limitation, so the KML driver +will not warn about potential problems. One of possible and tested solutions is to simplify a line or a polygon to remove +some coordinates. +Here is the whole discussion about this issue on the Google KML Developer Forum, in the polygon displays with vertical stripes thread. +

    + +

    See Also

    + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kml.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kml.cpp new file mode 100644 index 000000000..aefaa0fdc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kml.cpp @@ -0,0 +1,625 @@ +/****************************************************************************** + * $Id: kml.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: KML Driver + * Purpose: Class for reading, parsing and handling a kmlfile. + * Author: Jens Oberender, j.obi@troja.net + * + ****************************************************************************** + * Copyright (c) 2007, Jens Oberender + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "kmlnode.h" +#include "kml.h" +#include "cpl_error.h" +#include "cpl_conv.h" +// std +#include +#include +#include +#include + +KML::KML() +{ + nDepth_ = 0; + validity = KML_VALIDITY_UNKNOWN; + pKMLFile_ = NULL; + sError_ = ""; + poTrunk_ = NULL; + poCurrent_ = NULL; + nNumLayers_ = -1; + papoLayers_ = NULL; +} + +KML::~KML() +{ + if( NULL != pKMLFile_ ) + VSIFCloseL(pKMLFile_); + CPLFree(papoLayers_); + + delete poTrunk_; +} + +bool KML::open(const char * pszFilename) +{ + if( NULL != pKMLFile_ ) + VSIFCloseL( pKMLFile_ ); + + pKMLFile_ = VSIFOpenL( pszFilename, "r" ); + if( NULL == pKMLFile_ ) + { + return FALSE; + } + + return TRUE; +} + +void KML::parse() +{ + std::size_t nDone = 0; + std::size_t nLen = 0; + char aBuf[BUFSIZ] = { 0 }; + + if( NULL == pKMLFile_ ) + { + sError_ = "No file given"; + return; + } + + if(poTrunk_ != NULL) { + delete poTrunk_; + poTrunk_ = NULL; + } + + if(poCurrent_ != NULL) + { + delete poCurrent_; + poCurrent_ = NULL; + } + + XML_Parser oParser = OGRCreateExpatXMLParser(); + XML_SetUserData(oParser, this); + XML_SetElementHandler(oParser, startElement, endElement); + XML_SetCharacterDataHandler(oParser, dataHandler); + oCurrentParser = oParser; + nWithoutEventCounter = 0; + + do + { + nDataHandlerCounter = 0; + nLen = (int)VSIFReadL( aBuf, 1, sizeof(aBuf), pKMLFile_ ); + nDone = VSIFEofL(pKMLFile_); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of KML file failed : %s at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + XML_ParserFree(oParser); + VSIRewindL(pKMLFile_); + return; + } + nWithoutEventCounter ++; + } while (!nDone && nLen > 0 && nWithoutEventCounter < 10); + + XML_ParserFree(oParser); + VSIRewindL(pKMLFile_); + poCurrent_ = NULL; + + if (nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + } +} + +void KML::checkValidity() +{ + std::size_t nDone = 0; + std::size_t nLen = 0; + char aBuf[BUFSIZ] = { 0 }; + + if(poTrunk_ != NULL) + { + delete poTrunk_; + poTrunk_ = NULL; + } + + if(poCurrent_ != NULL) + { + delete poCurrent_; + poCurrent_ = NULL; + } + + if(pKMLFile_ == NULL) + { + this->sError_ = "No file given"; + return; + } + + XML_Parser oParser = OGRCreateExpatXMLParser(); + XML_SetUserData(oParser, this); + XML_SetElementHandler(oParser, startElementValidate, NULL); + XML_SetCharacterDataHandler(oParser, dataHandlerValidate); + int nCount = 0; + + oCurrentParser = oParser; + + /* Parses the file until we find the first element */ + do + { + nDataHandlerCounter = 0; + nLen = (int)VSIFReadL( aBuf, 1, sizeof(aBuf), pKMLFile_ ); + nDone = VSIFEofL(pKMLFile_); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + if (nLen <= BUFSIZ-1) + aBuf[nLen] = 0; + else + aBuf[BUFSIZ-1] = 0; + if( strstr(aBuf, " 0 && validity == KML_VALIDITY_UNKNOWN && nCount < 50); + + XML_ParserFree(oParser); + VSIRewindL(pKMLFile_); + poCurrent_ = NULL; +} + +void XMLCALL KML::startElement(void* pUserData, const char* pszName, const char** ppszAttr) +{ + int i = 0; + KMLNode* poMynew = NULL; + Attribute* poAtt = NULL; + + KML* poKML = (KML*) pUserData; + + poKML->nWithoutEventCounter = 0; + + if(poKML->poTrunk_ == NULL + || (poKML->poCurrent_->getName()).compare("description") != 0) + { + if (poKML->nDepth_ == 1024) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too big depth level (%d) while parsing KML.", + poKML->nDepth_ ); + XML_StopParser(poKML->oCurrentParser, XML_FALSE); + return; + } + + poMynew = new KMLNode(); + poMynew->setName(pszName); + poMynew->setLevel(poKML->nDepth_); + + for (i = 0; ppszAttr[i]; i += 2) + { + poAtt = new Attribute(); + poAtt->sName = ppszAttr[i]; + poAtt->sValue = ppszAttr[i + 1]; + poMynew->addAttribute(poAtt); + } + + if(poKML->poTrunk_ == NULL) + poKML->poTrunk_ = poMynew; + if(poKML->poCurrent_ != NULL) + poMynew->setParent(poKML->poCurrent_); + poKML->poCurrent_ = poMynew; + + poKML->nDepth_++; + } + else + { + std::string sNewContent = "<"; + sNewContent += pszName; + for (i = 0; ppszAttr[i]; i += 2) + { + sNewContent += " "; + sNewContent += ppszAttr[i]; + sNewContent += "=\""; + sNewContent += ppszAttr[i + 1]; + sNewContent += "\""; + } + sNewContent += ">"; + if(poKML->poCurrent_->numContent() == 0) + poKML->poCurrent_->addContent(sNewContent); + else + poKML->poCurrent_->appendContent(sNewContent); + } +} + +void XMLCALL KML::startElementValidate(void* pUserData, const char* pszName, const char** ppszAttr) +{ + int i = 0; + + KML* poKML = (KML*) pUserData; + + if (poKML->validity != KML_VALIDITY_UNKNOWN) + return; + + poKML->validity = KML_VALIDITY_INVALID; + + if(strcmp(pszName, "kml") == 0 || strcmp(pszName, "Document") == 0) + { + // Check all Attributes + for (i = 0; ppszAttr[i]; i += 2) + { + // Find the namespace and determine the KML version + if(strcmp(ppszAttr[i], "xmlns") == 0) + { + // Is it KML 2.2? + if((strcmp(ppszAttr[i + 1], "http://earth.google.com/kml/2.2") == 0) || + (strcmp(ppszAttr[i + 1], "http://www.opengis.net/kml/2.2") == 0)) + { + poKML->validity = KML_VALIDITY_VALID; + poKML->sVersion_ = "2.2"; + } + else if(strcmp(ppszAttr[i + 1], "http://earth.google.com/kml/2.1") == 0) + { + poKML->validity = KML_VALIDITY_VALID; + poKML->sVersion_ = "2.1"; + } + else if(strcmp(ppszAttr[i + 1], "http://earth.google.com/kml/2.0") == 0) + { + poKML->validity = KML_VALIDITY_VALID; + poKML->sVersion_ = "2.0"; + } + else + { + CPLDebug("KML", "Unhandled xmlns value : %s. Going on though...", ppszAttr[i]); + poKML->validity = KML_VALIDITY_VALID; + poKML->sVersion_ = "?"; + } + } + } + + if (poKML->validity == KML_VALIDITY_INVALID) + { + CPLDebug("KML", "Did not find xmlns attribute in element. Going on though..."); + poKML->validity = KML_VALIDITY_VALID; + poKML->sVersion_ = "?"; + } + } +} + +void XMLCALL KML::dataHandlerValidate(void * pUserData, + CPL_UNUSED const char * pszData, + CPL_UNUSED int nLen) +{ + KML* poKML = (KML*) pUserData; + + poKML->nDataHandlerCounter ++; + if (poKML->nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)"); + XML_StopParser(poKML->oCurrentParser, XML_FALSE); + } +} + +void XMLCALL KML::endElement(void* pUserData, const char* pszName) +{ + KMLNode* poTmp = NULL; + + KML* poKML = (KML*) pUserData; + + poKML->nWithoutEventCounter = 0; + + if(poKML->poCurrent_ != NULL && + poKML->poCurrent_->getName().compare(pszName) == 0) + { + poKML->nDepth_--; + poTmp = poKML->poCurrent_; + // Split the coordinates + if(poKML->poCurrent_->getName().compare("coordinates") == 0 && + poKML->poCurrent_->numContent() == 1) + { + std::string sData = poKML->poCurrent_->getContent(0); + std::size_t nPos = 0; + std::size_t nLength = sData.length(); + const char* pszData = sData.c_str(); + while(TRUE) + { + // Cut off whitespaces + while(nPos < nLength && + (pszData[nPos] == ' ' || pszData[nPos] == '\n' + || pszData[nPos] == '\r' || pszData[nPos] == '\t' )) + nPos ++; + + if (nPos == nLength) + break; + + std::size_t nPosBegin = nPos; + + // Get content + while(nPos < nLength && + pszData[nPos] != ' ' && pszData[nPos] != '\n' && pszData[nPos] != '\r' && + pszData[nPos] != '\t') + nPos++; + + if(nPos - nPosBegin > 0) + { + std::string sTmp(pszData + nPosBegin, nPos - nPosBegin); + poKML->poCurrent_->addContent(sTmp); + } + } + if(poKML->poCurrent_->numContent() > 1) + poKML->poCurrent_->deleteContent(0); + } + else if (poKML->poCurrent_->numContent() == 1) + { + std::string sData = poKML->poCurrent_->getContent(0); + std::string sDataWithoutNL; + std::size_t nPos = 0; + std::size_t nLength = sData.length(); + const char* pszData = sData.c_str(); + std::size_t nLineStartPos = 0; + int bLineStart = TRUE; + + /* Re-assemble multi-line content by removing leading spaces for each line */ + /* I'm not sure why we do that. Shouldn't we preserve content as such ? */ + while(nPos < nLength) + { + char ch = pszData[nPos]; + if (bLineStart && (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) + nLineStartPos ++; + else if (ch == '\n' || ch == '\r') + { + if (!bLineStart) + { + std::string sTmp(pszData + nLineStartPos, nPos - nLineStartPos); + if (sDataWithoutNL.size() > 0) + sDataWithoutNL += " "; + sDataWithoutNL += sTmp; + bLineStart = TRUE; + } + nLineStartPos = nPos + 1; + } + else + { + bLineStart = FALSE; + } + nPos ++; + } + + if (nLineStartPos > 0) + { + if (nLineStartPos < nPos) + { + std::string sTmp(pszData + nLineStartPos, nPos - nLineStartPos); + if (sDataWithoutNL.size() > 0) + sDataWithoutNL += " "; + sDataWithoutNL += sTmp; + } + + poKML->poCurrent_->deleteContent(0); + poKML->poCurrent_->addContent(sDataWithoutNL); + } + } + + if(poKML->poCurrent_->getParent() != NULL) + poKML->poCurrent_ = poKML->poCurrent_->getParent(); + else + poKML->poCurrent_ = NULL; + + if(!poKML->isHandled(pszName)) + { + CPLDebug("KML", "Not handled: %s", pszName); + delete poTmp; + } + else + { + if(poKML->poCurrent_ != NULL) + poKML->poCurrent_->addChildren(poTmp); + } + } + else if(poKML->poCurrent_ != NULL) + { + std::string sNewContent = ""; + if(poKML->poCurrent_->numContent() == 0) + poKML->poCurrent_->addContent(sNewContent); + else + poKML->poCurrent_->appendContent(sNewContent); + } +} + +void XMLCALL KML::dataHandler(void* pUserData, const char* pszData, int nLen) +{ + KML* poKML = (KML*) pUserData; + + poKML->nWithoutEventCounter = 0; + + if(nLen < 1 || poKML->poCurrent_ == NULL) + return; + + poKML->nDataHandlerCounter ++; + if (poKML->nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)"); + XML_StopParser(poKML->oCurrentParser, XML_FALSE); + } + + try + { + std::string sData(pszData, nLen); + + if(poKML->poCurrent_->numContent() == 0) + poKML->poCurrent_->addContent(sData); + else + poKML->poCurrent_->appendContent(sData); + } + catch(const std::exception& ex) + { + CPLError(CE_Failure, CPLE_AppDefined, "libstdc++ exception : %s", ex.what()); + XML_StopParser(poKML->oCurrentParser, XML_FALSE); + } +} + +bool KML::isValid() +{ + checkValidity(); + + if( validity == KML_VALIDITY_VALID ) + CPLDebug("KML", "Valid: %d Version: %s", + validity == KML_VALIDITY_VALID, sVersion_.c_str()); + + return validity == KML_VALIDITY_VALID; +} + +std::string KML::getError() const +{ + return sError_; +} + +int KML::classifyNodes() +{ + return poTrunk_->classify(this); +} + +void KML::eliminateEmpty() +{ + poTrunk_->eliminateEmpty(this); +} + +void KML::print(unsigned short nNum) +{ + if( poTrunk_ != NULL ) + poTrunk_->print(nNum); +} + +bool KML::isHandled(std::string const& elem) const +{ + if( isLeaf(elem) || isFeature(elem) || isFeatureContainer(elem) + || isContainer(elem) || isRest(elem) ) + { + return true; + } + return false; +} + +bool KML::isLeaf(CPL_UNUSED std::string const& elem) const +{ + return false; +}; + +bool KML::isFeature(CPL_UNUSED std::string const& elem) const +{ + return false; +}; + +bool KML::isFeatureContainer(CPL_UNUSED std::string const& elem) const +{ + return false; +}; + +bool KML::isContainer(CPL_UNUSED std::string const& elem) const +{ + return false; +}; + +bool KML::isRest(CPL_UNUSED std::string const& elem) const +{ + return false; +}; + +void KML::findLayers(CPL_UNUSED KMLNode* poNode, CPL_UNUSED int bKeepEmptyContainers) +{ + // idle +}; + +bool KML::hasOnlyEmpty() const +{ + return poTrunk_->hasOnlyEmpty(); +} + +int KML::getNumLayers() const +{ + return nNumLayers_; +} + +bool KML::selectLayer(int nNum) { + if(this->nNumLayers_ < 1 || nNum >= this->nNumLayers_) + return FALSE; + poCurrent_ = papoLayers_[nNum]; + return TRUE; +} + +std::string KML::getCurrentName() const +{ + std::string tmp; + if( poCurrent_ != NULL ) + { + tmp = poCurrent_->getNameElement(); + } + return tmp; +} + +Nodetype KML::getCurrentType() const +{ + if(poCurrent_ != NULL) + return poCurrent_->getType(); + else + return Unknown; +} + +int KML::is25D() const +{ + if(poCurrent_ != NULL) + return poCurrent_->is25D(); + else + return Unknown; +} + +int KML::getNumFeatures() +{ + if(poCurrent_ != NULL) + return static_cast(poCurrent_->getNumFeatures()); + else + return -1; +} + +Feature* KML::getFeature(std::size_t nNum, int& nLastAsked, int &nLastCount) +{ + if(poCurrent_ != NULL) + return poCurrent_->getFeature(nNum, nLastAsked, nLastCount); + else + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kml.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kml.h new file mode 100644 index 000000000..ef0ef000b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kml.h @@ -0,0 +1,127 @@ +/****************************************************************************** + * $Id: kml.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: KML Driver + * Purpose: Class for reading, parsing and handling a kmlfile. + * Author: Jens Oberender, j.obi@troja.net + * + ****************************************************************************** + * Copyright (c) 2007, Jens Oberender + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef OGR_KML_KML_H_INCLUDED +#define OGR_KML_KML_H_INCLUDED + +#include "ogr_expat.h" +#include "cpl_vsi.h" + +// std +#include +#include +#include + +/* Workaround VC6 bug */ +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +namespace std +{ + typedef ::size_t size_t; +} +#endif + +#include "cpl_port.h" +#include "kmlutility.h" + +class KMLNode; + + +typedef enum +{ + KML_VALIDITY_UNKNOWN, + KML_VALIDITY_INVALID, + KML_VALIDITY_VALID +} OGRKMLValidity; + +class KML +{ +public: + KML(); + virtual ~KML(); + bool open(const char* pszFilename); + bool isValid(); + bool isHandled(std::string const& elem) const; + virtual bool isLeaf(std::string const& elem) const; + virtual bool isFeature(std::string const& elem) const; + virtual bool isFeatureContainer(std::string const& elem) const; + virtual bool isContainer(std::string const& elem) const; + virtual bool isRest(std::string const& elem) const; + virtual void findLayers(KMLNode* poNode, int bKeepEmptyContainers); + + bool hasOnlyEmpty() const; + + void parse(); + void print(unsigned short what = 3); + std::string getError() const; + int classifyNodes(); + void eliminateEmpty(); + int getNumLayers() const; + bool selectLayer(int); + std::string getCurrentName() const; + Nodetype getCurrentType() const; + int is25D() const; + int getNumFeatures(); + Feature* getFeature(std::size_t nNum, int& nLastAsked, int &nLastCount); + +protected: + void checkValidity(); + + static void XMLCALL startElement(void *, const char *, const char **); + static void XMLCALL startElementValidate(void *, const char *, const char **); + static void XMLCALL dataHandler(void *, const char *, int); + static void XMLCALL dataHandlerValidate(void *, const char *, int); + static void XMLCALL endElement(void *, const char *); + + // trunk of KMLnodes + KMLNode* poTrunk_; + // number of layers; + int nNumLayers_; + KMLNode** papoLayers_; + +private: + // depth of the DOM + unsigned int nDepth_; + // KML version number + std::string sVersion_; + // set to KML_VALIDITY_VALID if the beginning of the file is detected as KML + OGRKMLValidity validity; + // file descriptor + VSILFILE *pKMLFile_; + // error text ("" when everything is OK") + std::string sError_; + // current KMLNode + KMLNode *poCurrent_; + + XML_Parser oCurrentParser; + int nDataHandlerCounter; + int nWithoutEventCounter; +}; + +#endif /* OGR_KML_KML_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlnode.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlnode.cpp new file mode 100644 index 000000000..166098e88 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlnode.cpp @@ -0,0 +1,759 @@ +/****************************************************************************** + * $Id: kmlnode.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: KML Driver + * Purpose: Class for building up the node structure of the kml file. + * Author: Jens Oberender, j.obi@troja.net + * + ****************************************************************************** + * Copyright (c) 2007, Jens Oberender + * Copyright (c) 2007-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "kmlnode.h" +#include "cpl_conv.h" +// std +#include +#include + +/************************************************************************/ +/* Help functions */ +/************************************************************************/ + +std::string Nodetype2String(Nodetype const& type) +{ + if(type == Empty) + return "Empty"; + else if(type == Rest) + return "Rest"; + else if(type == Mixed) + return "Mixed"; + else if(type == Point) + return "Point"; + else if(type == LineString) + return "LineString"; + else if(type == Polygon) + return "Polygon"; + else if(type == MultiGeometry) + return "MultiGeometry"; + else if(type == MultiPoint) + return "MultiPoint"; + else if(type == MultiLineString) + return "MultiLineString"; + else if(type == MultiPolygon) + return "MultiPolygon"; + else + return "Unknown"; +} + +static +bool isNumberDigit(const char cIn) +{ + return ( cIn == '-' || cIn == '+' || + (cIn >= '0' && cIn <= '9') || + cIn == '.' || cIn == 'e' || cIn == 'E' ); +} + +static +Coordinate* ParseCoordinate(std::string const& text) +{ + int pos = 0; + const char* pszStr = text.c_str(); + Coordinate *psTmp = new Coordinate(); + + // X coordinate + psTmp->dfLongitude = CPLAtof(pszStr); + while(isNumberDigit(pszStr[pos++])); + + // Y coordinate + if(pszStr[pos - 1] != ',') + { + delete psTmp; + return NULL; + } + + psTmp->dfLatitude = CPLAtof(pszStr + pos); + while(isNumberDigit(pszStr[pos++])); + + // Z coordinate + if(pszStr[pos - 1] != ',') + { + psTmp->bHasZ = FALSE; + psTmp->dfAltitude = 0; + return psTmp; + } + + psTmp->bHasZ = TRUE; + psTmp->dfAltitude = CPLAtof(pszStr + pos); + + return psTmp; +} + +/************************************************************************/ +/* KMLNode methods */ +/************************************************************************/ + +KMLNode::KMLNode() +{ + poParent_ = NULL; + pvpoChildren_ = new std::vector; + pvsContent_ = new std::vector; + pvoAttributes_ = new std::vector; + eType_ = Unknown; + nLayerNumber_ = -1; + b25D_ = FALSE; + nNumFeatures_ = -1; +} + +KMLNode::~KMLNode() +{ + CPLAssert( NULL != pvpoChildren_ ); + CPLAssert( NULL != pvoAttributes_ ); + + kml_nodes_t::iterator itChild; + for( itChild = pvpoChildren_->begin(); + itChild != pvpoChildren_->end(); ++itChild) + { + delete (*itChild); + } + delete pvpoChildren_; + + kml_attributes_t::iterator itAttr; + for( itAttr = pvoAttributes_->begin(); + itAttr != pvoAttributes_->end(); ++itAttr) + { + delete (*itAttr); + } + delete pvoAttributes_; + + delete pvsContent_; +} + +void KMLNode::print(unsigned int what) +{ + std::string indent; + for(std::size_t l = 0; l < nLevel_; l++) + indent += " "; + + if(nLevel_ > 0) + { + if(nLayerNumber_ > -1) + { + CPLDebug("KML", "%s%s (nLevel: %d Type: %s poParent: %s pvpoChildren_: %d pvsContent_: %d pvoAttributes_: %d) <--- Layer #%d", + indent.c_str(), sName_.c_str(), (int) nLevel_, Nodetype2String(eType_).c_str(), poParent_->sName_.c_str(), + (int) pvpoChildren_->size(), (int) pvsContent_->size(), (int) pvoAttributes_->size(), nLayerNumber_); + } + else + { + CPLDebug("KML", "%s%s (nLevel: %d Type: %s poParent: %s pvpoChildren_: %d pvsContent_: %d pvoAttributes_: %d)", + indent.c_str(), sName_.c_str(), (int) nLevel_, Nodetype2String(eType_).c_str(), poParent_->sName_.c_str(), + (int) pvpoChildren_->size(), (int) pvsContent_->size(), (int) pvoAttributes_->size()); + } + } + else + { + CPLDebug("KML", "%s%s (nLevel: %d Type: %s pvpoChildren_: %d pvsContent_: %d pvoAttributes_: %d)", + indent.c_str(), sName_.c_str(), (int) nLevel_, Nodetype2String(eType_).c_str(), (int) pvpoChildren_->size(), + (int) pvsContent_->size(), (int) pvoAttributes_->size()); + } + + if(what == 1 || what == 3) + { + for(kml_content_t::size_type z = 0; z < pvsContent_->size(); z++) + CPLDebug("KML", "%s|->pvsContent_: '%s'", indent.c_str(), (*pvsContent_)[z].c_str()); + } + + if(what == 2 || what == 3) + { + for(kml_attributes_t::size_type z = 0; z < pvoAttributes_->size(); z++) + CPLDebug("KML", "%s|->pvoAttributes_: %s = '%s'", indent.c_str(), (*pvoAttributes_)[z]->sName.c_str(), (*pvoAttributes_)[z]->sValue.c_str()); + } + + for(kml_nodes_t::size_type z = 0; z < pvpoChildren_->size(); z++) + (*pvpoChildren_)[z]->print(what); +} + +//static int nDepth = 0; +//static char* genSpaces() +//{ +// static char spaces[128]; +// int i; +// for(i=0;i", genSpaces(), sName_.c_str()); + //nDepth ++; + + if(sName_.compare("Point") == 0) + eType_ = Point; + else if(sName_.compare("LineString") == 0) + eType_ = LineString; + else if(sName_.compare("Polygon") == 0) + eType_ = Polygon; + else if(poKML->isRest(sName_)) + eType_ = Empty; + else if(sName_.compare("coordinates") == 0) + { + unsigned int nCountP; + for(nCountP = 0; nCountP < pvsContent_->size(); nCountP++) + { + const char* pszCoord = (*pvsContent_)[nCountP].c_str(); + int nComma = 0; + while(TRUE) + { + pszCoord = strchr(pszCoord, ','); + if (pszCoord) + { + nComma ++; + pszCoord ++; + } + else + break; + } + if (nComma == 2) + b25D_ = TRUE; + } + } + + const kml_nodes_t::size_type size = pvpoChildren_->size(); + for(kml_nodes_t::size_type z = 0; z < size; z++) + { + //CPLDebug("KML", "%s[%d] %s", genSpaces(), z, (*pvpoChildren_)[z]->sName_.c_str()); + + // Classify pvpoChildren_ + if (!(*pvpoChildren_)[z]->classify(poKML, nRecLevel + 1)) + return FALSE; + + curr = (*pvpoChildren_)[z]->eType_; + b25D_ |= (*pvpoChildren_)[z]->b25D_; + + // Compare and return if it is mixed + if(curr != all && all != Empty && curr != Empty) + { + if (sName_.compare("MultiGeometry") == 0) + eType_ = MultiGeometry; + else + eType_ = Mixed; + } + else if(curr != Empty) + { + all = curr; + } + } + + if(eType_ == Unknown) + { + if (sName_.compare("MultiGeometry") == 0) + { + if (all == Point) + eType_ = MultiPoint; + else if (all == LineString) + eType_ = MultiLineString; + else if (all == Polygon) + eType_ = MultiPolygon; + else + eType_ = MultiGeometry; + } + else + eType_ = all; + } + + //nDepth --; + //CPLDebug("KML", "%s --> eType=%s", genSpaces(), sName_.c_str(), Nodetype2String(eType_).c_str()); + + return TRUE; +} + +void KMLNode::eliminateEmpty(KML* poKML) +{ + for(kml_nodes_t::size_type z = 0; z < pvpoChildren_->size(); z++) + { + if((*pvpoChildren_)[z]->eType_ == Empty + && (poKML->isContainer((*pvpoChildren_)[z]->sName_) + || poKML->isFeatureContainer((*pvpoChildren_)[z]->sName_))) + { + delete (*pvpoChildren_)[z]; + pvpoChildren_->erase(pvpoChildren_->begin() + z); + z--; + } + else + { + (*pvpoChildren_)[z]->eliminateEmpty(poKML); + } + } +} + +bool KMLNode::hasOnlyEmpty() const +{ + for(kml_nodes_t::size_type z = 0; z < pvpoChildren_->size(); z++) + { + if((*pvpoChildren_)[z]->eType_ != Empty) + { + return false; + } + else + { + if (!(*pvpoChildren_)[z]->hasOnlyEmpty()) + return false; + } + } + + return true; +} + +void KMLNode::setType(Nodetype oNotet) +{ + eType_ = oNotet; +} + +Nodetype KMLNode::getType() const +{ + return eType_; +} + +void KMLNode::setName(std::string const& sIn) +{ + sName_ = sIn; +} + +const std::string& KMLNode::getName() const +{ + return sName_; +} + +void KMLNode::setLevel(std::size_t nLev) +{ + nLevel_ = nLev; +} + +std::size_t KMLNode::getLevel() const +{ + return nLevel_; +} + +void KMLNode::addAttribute(Attribute *poAttr) +{ + pvoAttributes_->push_back(poAttr); +} + +void KMLNode::setParent(KMLNode* poPar) +{ + poParent_ = poPar; +} + +KMLNode* KMLNode::getParent() const +{ + return poParent_; +} + +void KMLNode::addChildren(KMLNode *poChil) +{ + pvpoChildren_->push_back(poChil); +} + +std::size_t KMLNode::countChildren() +{ + return pvpoChildren_->size(); +} + +KMLNode* KMLNode::getChild(std::size_t index) const +{ + return (*pvpoChildren_)[index]; +} + +void KMLNode::addContent(std::string const& text) +{ + pvsContent_->push_back(text); +} + +void KMLNode::appendContent(std::string const& text) +{ + std::string& tmp = (*pvsContent_)[pvsContent_->size() - 1]; + tmp += text; +} + +std::string KMLNode::getContent(std::size_t index) const +{ + return (*pvsContent_)[index]; +} + +void KMLNode::deleteContent(std::size_t index) +{ + if( index < pvsContent_->size() ) + { + pvsContent_->erase(pvsContent_->begin() + index); + } +} + +std::size_t KMLNode::numContent() +{ + return pvsContent_->size(); +} + +void KMLNode::setLayerNumber(int nNum) +{ + nLayerNumber_ = nNum; +} + +int KMLNode::getLayerNumber() const +{ + return nLayerNumber_; +} + +std::string KMLNode::getNameElement() const +{ + kml_nodes_t::size_type subsize = 0; + kml_nodes_t::size_type size = pvpoChildren_->size(); + + for( kml_nodes_t::size_type i = 0; i < size; ++i ) + { + if( (*pvpoChildren_)[i]->sName_.compare("name") == 0 ) + { + subsize = (*pvpoChildren_)[i]->pvsContent_->size(); + if( subsize > 0 ) + { + return (*(*pvpoChildren_)[i]->pvsContent_)[0]; + } + break; + } + } + return ""; +} + +std::string KMLNode::getDescriptionElement() const +{ + kml_nodes_t::size_type subsize = 0; + kml_nodes_t::size_type size = pvpoChildren_->size(); + for( kml_nodes_t::size_type i = 0; i < size; ++i ) + { + if( (*pvpoChildren_)[i]->sName_.compare("description") == 0 ) + { + subsize = (*pvpoChildren_)[i]->pvsContent_->size(); + if ( subsize > 0 ) + { + return (*(*pvpoChildren_)[i]->pvsContent_)[0]; + } + break; + } + } + return ""; +} + +std::size_t KMLNode::getNumFeatures() +{ + if (nNumFeatures_ < 0) + { + std::size_t nNum = 0; + kml_nodes_t::size_type size = pvpoChildren_->size(); + + for( kml_nodes_t::size_type i = 0; i < size; ++i ) + { + if( (*pvpoChildren_)[i]->sName_ == "Placemark" ) + nNum++; + } + nNumFeatures_ = (int)nNum; + } + return nNumFeatures_; +} + +OGRGeometry* KMLNode::getGeometry(Nodetype eType) +{ + unsigned int nCount, nCount2, nCountP; + OGRGeometry* poGeom = NULL; + KMLNode* poCoor = NULL; + Coordinate* psCoord = NULL; + + if (sName_.compare("Point") == 0) + { + // Search coordinate Element + for(nCount = 0; nCount < pvpoChildren_->size(); nCount++) + { + if((*pvpoChildren_)[nCount]->sName_.compare("coordinates") == 0) + { + poCoor = (*pvpoChildren_)[nCount]; + for(nCountP = 0; nCountP < poCoor->pvsContent_->size(); nCountP++) + { + psCoord = ParseCoordinate((*poCoor->pvsContent_)[nCountP]); + if(psCoord != NULL) + { + if (psCoord->bHasZ) + poGeom = new OGRPoint(psCoord->dfLongitude, + psCoord->dfLatitude, + psCoord->dfAltitude); + else + poGeom = new OGRPoint(psCoord->dfLongitude, + psCoord->dfLatitude); + delete psCoord; + return poGeom; + } + } + } + } + poGeom = new OGRPoint(); + } + else if (sName_.compare("LineString") == 0) + { + // Search coordinate Element + poGeom = new OGRLineString(); + for(nCount = 0; nCount < pvpoChildren_->size(); nCount++) + { + if((*pvpoChildren_)[nCount]->sName_.compare("coordinates") == 0) + { + poCoor = (*pvpoChildren_)[nCount]; + for(nCountP = 0; nCountP < poCoor->pvsContent_->size(); nCountP++) + { + psCoord = ParseCoordinate((*poCoor->pvsContent_)[nCountP]); + if(psCoord != NULL) + { + if (psCoord->bHasZ) + ((OGRLineString*)poGeom)->addPoint(psCoord->dfLongitude, + psCoord->dfLatitude, + psCoord->dfAltitude); + else + ((OGRLineString*)poGeom)->addPoint(psCoord->dfLongitude, + psCoord->dfLatitude); + delete psCoord; + } + } + } + } + } + else if (sName_.compare("Polygon") == 0) + { + //********************************* + // Search outerBoundaryIs Element + //********************************* + poGeom = new OGRPolygon(); + for(nCount = 0; nCount < pvpoChildren_->size(); nCount++) + { + if((*pvpoChildren_)[nCount]->sName_.compare("outerBoundaryIs") == 0 && + (*pvpoChildren_)[nCount]->pvpoChildren_->size() > 0) + { + poCoor = (*(*pvpoChildren_)[nCount]->pvpoChildren_)[0]; + } + } + // No outer boundary found + if(poCoor == NULL) + { + return poGeom; + } + // Search coordinate Element + OGRLinearRing* poLinearRing = NULL; + for(nCount = 0; nCount < poCoor->pvpoChildren_->size(); nCount++) + { + if((*poCoor->pvpoChildren_)[nCount]->sName_.compare("coordinates") == 0) + { + for(nCountP = 0; nCountP < (*poCoor->pvpoChildren_)[nCount]->pvsContent_->size(); nCountP++) + { + psCoord = ParseCoordinate((*(*poCoor->pvpoChildren_)[nCount]->pvsContent_)[nCountP]); + if(psCoord != NULL) + { + if (poLinearRing == NULL) + { + poLinearRing = new OGRLinearRing(); + } + if (psCoord->bHasZ) + poLinearRing->addPoint(psCoord->dfLongitude, + psCoord->dfLatitude, + psCoord->dfAltitude); + else + poLinearRing->addPoint(psCoord->dfLongitude, + psCoord->dfLatitude); + delete psCoord; + } + } + } + } + // No outer boundary coordinates found + if(poLinearRing == NULL) + { + return poGeom; + } + + ((OGRPolygon*)poGeom)->addRingDirectly(poLinearRing); + poLinearRing = NULL; + + //********************************* + // Search innerBoundaryIs Elements + //********************************* + + for(nCount2 = 0; nCount2 < pvpoChildren_->size(); nCount2++) + { + if((*pvpoChildren_)[nCount2]->sName_.compare("innerBoundaryIs") == 0) + { + if (poLinearRing) + ((OGRPolygon*)poGeom)->addRingDirectly(poLinearRing); + poLinearRing = NULL; + + if ((*pvpoChildren_)[nCount2]->pvpoChildren_->size() == 0) + continue; + + poLinearRing = new OGRLinearRing(); + + poCoor = (*(*pvpoChildren_)[nCount2]->pvpoChildren_)[0]; + // Search coordinate Element + for(nCount = 0; nCount < poCoor->pvpoChildren_->size(); nCount++) + { + if((*poCoor->pvpoChildren_)[nCount]->sName_.compare("coordinates") == 0) + { + for(nCountP = 0; nCountP < (*poCoor->pvpoChildren_)[nCount]->pvsContent_->size(); nCountP++) + { + psCoord = ParseCoordinate((*(*poCoor->pvpoChildren_)[nCount]->pvsContent_)[nCountP]); + if(psCoord != NULL) + { + if (psCoord->bHasZ) + poLinearRing->addPoint(psCoord->dfLongitude, + psCoord->dfLatitude, + psCoord->dfAltitude); + else + poLinearRing->addPoint(psCoord->dfLongitude, + psCoord->dfLatitude); + delete psCoord; + } + } + } + } + } + } + + if (poLinearRing) + ((OGRPolygon*)poGeom)->addRingDirectly(poLinearRing); + } + else if (sName_.compare("MultiGeometry") == 0) + { + if (eType == MultiPoint) + poGeom = new OGRMultiPoint(); + else if (eType == MultiLineString) + poGeom = new OGRMultiLineString(); + else if (eType == MultiPolygon) + poGeom = new OGRMultiPolygon(); + else + poGeom = new OGRGeometryCollection(); + for(nCount = 0; nCount < pvpoChildren_->size(); nCount++) + { + OGRGeometry* poSubGeom = (*pvpoChildren_)[nCount]->getGeometry(); + if (poSubGeom) + ((OGRGeometryCollection*)poGeom)->addGeometryDirectly(poSubGeom); + } + } + + return poGeom; +} + +Feature* KMLNode::getFeature(std::size_t nNum, int& nLastAsked, int &nLastCount) +{ + unsigned int nCount, nCountP = 0; + KMLNode* poFeat = NULL; + KMLNode* poTemp = NULL; + + if(nNum >= this->getNumFeatures()) + return NULL; + + if (nLastAsked + 1 != (int)nNum) + { + nCount = 0; + nCountP = 0; + } + else + { + nCount = nLastCount + 1; + nCountP = nLastAsked + 1; + } + + for(; nCount < pvpoChildren_->size(); nCount++) + { + if((*pvpoChildren_)[nCount]->sName_.compare("Placemark") == 0) + { + if(nCountP == nNum) + { + poFeat = (*pvpoChildren_)[nCount]; + break; + } + nCountP++; + } + } + + nLastAsked = nNum; + nLastCount = nCount; + + if(poFeat == NULL) + return NULL; + + // Create a feature structure + Feature *psReturn = new Feature; + // Build up the name + psReturn->sName = poFeat->getNameElement(); + // Build up the description + psReturn->sDescription = poFeat->getDescriptionElement(); + // the type + psReturn->eType = poFeat->eType_; + + std::string sElementName; + if(poFeat->eType_ == Point || + poFeat->eType_ == LineString || + poFeat->eType_ == Polygon) + sElementName = Nodetype2String(poFeat->eType_); + else if (poFeat->eType_ == MultiGeometry || + poFeat->eType_ == MultiPoint || + poFeat->eType_ == MultiLineString || + poFeat->eType_ == MultiPolygon) + sElementName = "MultiGeometry"; + else + { + delete psReturn; + return NULL; + } + + for(nCount = 0; nCount < poFeat->pvpoChildren_->size(); nCount++) + { + if((*poFeat->pvpoChildren_)[nCount]->sName_.compare(sElementName) == 0) + { + poTemp = (*poFeat->pvpoChildren_)[nCount]; + psReturn->poGeom = poTemp->getGeometry(poFeat->eType_); + if(psReturn->poGeom) + return psReturn; + else + { + delete psReturn; + return NULL; + } + } + } + + delete psReturn; + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlnode.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlnode.h new file mode 100644 index 000000000..d9d641913 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlnode.h @@ -0,0 +1,115 @@ +/****************************************************************************** + * $Id: kmlnode.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: KML Driver + * Purpose: Class for building up the node structure of the kml file. + * Author: Jens Oberender, j.obi@troja.net + * + ****************************************************************************** + * Copyright (c) 2007, Jens Oberender + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef OGR_KMLNODE_H_INCLUDED +#define OGR_KMLNODE_H_INCLUDED + +#include "kml.h" +#include "kmlutility.h" +// std +#include +#include +#include + +std::string Nodetype2String(Nodetype const& type); + +class KMLNode +{ +public: + + KMLNode(); + ~KMLNode(); + + void print(unsigned int what = 3); + int classify(KML* poKML, int nRecLevel = 0); + void eliminateEmpty(KML* poKML); + bool hasOnlyEmpty() const; + + void setType(Nodetype type); + Nodetype getType() const; + + void setName(std::string const& name); + const std::string& getName() const; + + void setLevel(std::size_t level); + std::size_t getLevel() const; + + void addAttribute(Attribute* poAttr); + + void setParent(KMLNode* poNode); + KMLNode* getParent() const; + + void addChildren(KMLNode* poNode); + std::size_t countChildren(); + + KMLNode* getChild(std::size_t index) const; + + void addContent(std::string const& text); + void appendContent(std::string const& text); + std::string getContent(std::size_t index) const; + void deleteContent(std::size_t index); + std::size_t numContent(); + + void setLayerNumber(int nNum); + int getLayerNumber() const; + + std::string getNameElement() const; + std::string getDescriptionElement() const; + + std::size_t getNumFeatures(); + Feature* getFeature(std::size_t nNum, int& nLastAsked, int &nLastCount); + + OGRGeometry* getGeometry(Nodetype eType = Unknown); + + int is25D() { return b25D_; } + +private: + + typedef std::vector kml_nodes_t; + kml_nodes_t* pvpoChildren_; + + typedef std::vector kml_content_t; + kml_content_t* pvsContent_; + + typedef std::vector kml_attributes_t; + kml_attributes_t* pvoAttributes_; + + KMLNode *poParent_; + std::size_t nLevel_; + std::string sName_; + + Nodetype eType_; + int b25D_; + + int nLayerNumber_; + int nNumFeatures_; +}; + +#endif /* KMLNODE_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlutility.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlutility.h new file mode 100644 index 000000000..cdc9768ab --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlutility.h @@ -0,0 +1,79 @@ +/****************************************************************************** + * $Id: kmlutility.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: KML Driver + * Purpose: KML driver utilities + * Author: Jens Oberender, j.obi@troja.net + * + ****************************************************************************** + * Copyright (c) 2007, Jens Oberender + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef OGR_KMLUTILITY_H_INCLUDED +#define OGR_KMLUTILITY_H_INCLUDED + +#include +#include +#include "ogr_geometry.h" + +enum Nodetype +{ + Unknown, Empty, Mixed, Point, LineString, Polygon, Rest, MultiGeometry, MultiPoint, MultiLineString, MultiPolygon +}; + +struct Attribute +{ + std::string sName; + std::string sValue; +}; + +struct Coordinate +{ + double dfLongitude; + double dfLatitude; + double dfAltitude; + int bHasZ; + + Coordinate() + : dfLongitude(0), dfLatitude(0), dfAltitude(0), bHasZ(FALSE) + {} +}; + +struct Feature +{ + Nodetype eType; + std::string sName; + std::string sDescription; + OGRGeometry* poGeom; + + Feature() + : eType(Unknown), poGeom(NULL) + {} + + ~Feature() + { + delete poGeom; + } +}; + + +#endif /* OGR_KMLUTILITY_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlvector.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlvector.cpp new file mode 100644 index 000000000..92f5b4428 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlvector.cpp @@ -0,0 +1,163 @@ +/****************************************************************************** + * $Id: kmlvector.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: KML Driver + * Purpose: Specialization of the kml class, only for vectors in kml files. + * Author: Jens Oberender, j.obi@troja.net + * + ****************************************************************************** + * Copyright (c) 2007, Jens Oberender + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "kmlvector.h" +#include "kmlnode.h" +#include "cpl_conv.h" +// std +#include + +KMLVector::~KMLVector() +{ +} + +bool KMLVector::isLeaf(std::string const& sIn) const +{ + if( sIn.compare("name") == 0 + || sIn.compare("coordinates") == 0 + || sIn.compare("altitudeMode") == 0 + || sIn.compare("description") == 0 ) + { + return true; + } + return false; +} + +// Container - FeatureContainer - Feature + +bool KMLVector::isContainer(std::string const& sIn) const +{ + if( sIn.compare("Folder") == 0 + || sIn.compare("Document") == 0 + || sIn.compare("kml") == 0 ) + { + return true; + } + return false; +} + +bool KMLVector::isFeatureContainer(std::string const& sIn) const +{ + if( sIn.compare("MultiGeometry") == 0 + || sIn.compare("Placemark") == 0 ) + { + return true; + } + return false; +} + +bool KMLVector::isFeature(std::string const& sIn) const +{ + if( sIn.compare("Polygon") == 0 + || sIn.compare("LineString") == 0 + || sIn.compare("Point") == 0 ) + { + return true; + } + return false; +} + +bool KMLVector::isRest(std::string const& sIn) const +{ + if( sIn.compare("outerBoundaryIs") == 0 + || sIn.compare("innerBoundaryIs") == 0 + || sIn.compare("LinearRing") == 0 ) + { + return true; + } + return false; +} + +void KMLVector::findLayers(KMLNode* poNode, int bKeepEmptyContainers) +{ + bool bEmpty = true; + + // Start with the trunk + if( NULL == poNode ) + { + nNumLayers_ = 0; + poNode = poTrunk_; + } + + if( isFeature(poNode->getName()) + || isFeatureContainer(poNode->getName()) + || ( isRest(poNode->getName()) + && poNode->getName().compare("kml") != 0 ) ) + { + return; + } + else if( isContainer(poNode->getName()) ) + { + for( int z = 0; z < (int) poNode->countChildren(); z++ ) + { + if( isContainer(poNode->getChild(z)->getName()) ) + { + findLayers(poNode->getChild(z), bKeepEmptyContainers); + } + else if( isFeatureContainer(poNode->getChild(z)->getName()) ) + { + bEmpty = false; + } + } + + if( bKeepEmptyContainers && poNode->getName() == "Folder" ) + { + if (!bEmpty) + poNode->eliminateEmpty(this); + } + else if(bEmpty) + { + return; + } + + Nodetype nodeType = poNode->getType(); + if( bKeepEmptyContainers || + isFeature(Nodetype2String(nodeType)) || + nodeType == Mixed || + nodeType == MultiGeometry || nodeType == MultiPoint || + nodeType == MultiLineString || nodeType == MultiPolygon) + { + poNode->setLayerNumber(nNumLayers_++); + papoLayers_ = (KMLNode**)CPLRealloc(papoLayers_, nNumLayers_ * sizeof(KMLNode*)); + papoLayers_[nNumLayers_ - 1] = poNode; + } + else + { + CPLDebug( "KML", "We have a strange type here for node %s: %s", + poNode->getName().c_str(), Nodetype2String(poNode->getType()).c_str() ); + } + } + else + { + CPLDebug("KML", "There is something wrong! Define KML_DEBUG to see details"); + if( CPLGetConfigOption("KML_DEBUG",NULL) != NULL ) + print(); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlvector.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlvector.h new file mode 100644 index 000000000..a5c3f5e43 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/kmlvector.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * $Id: kmlvector.h 23978 2012-02-14 20:42:34Z rouault $ + * + * Project: KML Driver + * Purpose: Specialization of the kml class, only for vectors in kml files. + * Author: Jens Oberender, j.obi@troja.net + * + ****************************************************************************** + * Copyright (c) 2007, Jens Oberender + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef OGR_KMLVECTOR_H_INCLUDED +#define OGR_KMLVECTOR_H_INCLUDED + +#include "kml.h" +#include "kmlnode.h" +// std +#include + +class KMLVector : public KML +{ +public: + ~KMLVector(); + + // Container - FeatureContainer - Feature + bool isFeature(std::string const& sIn) const; + bool isFeatureContainer(std::string const& sIn) const; + bool isContainer(std::string const& sIn) const; + bool isLeaf(std::string const& sIn) const; + bool isRest(std::string const& sIn) const; + void findLayers(KMLNode* poNode, int bKeepEmptyContainers); +}; + +#endif /* OGR_KMLVECTOR_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/makefile.vc new file mode 100644 index 000000000..dd3b27638 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/makefile.vc @@ -0,0 +1,22 @@ + +EXPAT_OBJ = kml.obj kmlvector.obj kmlnode.obj +OGR_OBJ = ogrkmldriver.obj ogrkmldatasource.obj ogrkmllayer.obj \ + ogr2kmlgeometry.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF EXPAT_DIR +OBJ = $(EXPAT_OBJ) $(OGR_OBJ) +EXTRAFLAGS = -I.. -I..\.. $(EXPAT_INCLUDE) -DHAVE_EXPAT=1 +!ELSE +OBJ = $(OGR_OBJ) +EXTRAFLAGS = -I.. -I..\.. +!ENDIF + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogr2kmlgeometry.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogr2kmlgeometry.cpp new file mode 100644 index 000000000..2d81ca1bd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogr2kmlgeometry.cpp @@ -0,0 +1,525 @@ +/****************************************************************************** + * $Id: ogr2kmlgeometry.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: KML Driver + * Purpose: Implementation of OGR -> KML geometries writer. + * Author: Christopher Condit, condit@sdsc.edu + * + ****************************************************************************** + * Copyright (c) 2006, Christopher Condit + * Copyright (c) 2007-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "cpl_minixml.h" +#include "ogr_geometry.h" +#include "ogr_api.h" +#include "ogr_p.h" +#include "cpl_error.h" +#include "cpl_conv.h" + + +#define EPSILON 1e-8 + +/************************************************************************/ +/* MakeKMLCoordinate() */ +/************************************************************************/ + +static void MakeKMLCoordinate( char *pszTarget, + double x, double y, double z, int b3D ) + +{ + if (y < -90 || y > 90) + { + if (y > 90 && y < 90 + EPSILON) + { + y = 90; + } + else if (y > -90 - EPSILON && y < -90) + { + y = -90; + } + else + { + static int bFirstWarning = TRUE; + if (bFirstWarning) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Latitude %f is invalid. Valid range is [-90,90]. This warning will not be issued any more", + y); + bFirstWarning = FALSE; + } + } + } + + if (x < -180 || x > 180) + { + if (x > 180 && x < 180 + EPSILON) + { + x = 180; + } + else if (x > -180 - EPSILON && x < -180) + { + x = -180; + } + else + { + static int bFirstWarning = TRUE; + if (bFirstWarning) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Longitude %f has been modified to fit into range [-180,180]. This warning will not be issued any more", + x); + bFirstWarning = FALSE; + } + + if (x > 180) + x -= ((int) ((x+180)/360)*360); + else if (x < -180) + x += ((int) (180 - x)/360)*360; + } + } + + OGRMakeWktCoordinate( pszTarget, x, y, z, b3D ? 3 : 2 ); + while( *pszTarget != '\0' ) + { + if( *pszTarget == ' ' ) + *pszTarget = ','; + pszTarget++; + } + +#ifdef notdef + if( !b3D ) + { + if( x == (int) x && y == (int) y ) + sprintf( pszTarget, "%d,%d", (int) x, (int) y ); + else if( fabs(x) < 370 && fabs(y) < 370 ) + CPLsprintf( pszTarget, "%.16g,%.16g", x, y ); + else if( fabs(x) > 100000000.0 || fabs(y) > 100000000.0 ) + CPLsprintf( pszTarget, "%.16g,%.16g", x, y ); + else + CPLsprintf( pszTarget, "%.3f,%.3f", x, y ); + } + else + { + if( x == (int) x && y == (int) y && z == (int) z ) + sprintf( pszTarget, "%d,%d,%d", (int) x, (int) y, (int) z ); + else if( fabs(x) < 370 && fabs(y) < 370 ) + CPLsprintf( pszTarget, "%.16g,%.16g,%.16g", x, y, z ); + else if( fabs(x) > 100000000.0 || fabs(y) > 100000000.0 + || fabs(z) > 100000000.0 ) + CPLsprintf( pszTarget, "%.16g,%.16g,%.16g", x, y, z ); + else + CPLsprintf( pszTarget, "%.3f,%.3f,%.3f", x, y, z ); + } +#endif +} + +/************************************************************************/ +/* _GrowBuffer() */ +/************************************************************************/ + +static void _GrowBuffer( int nNeeded, char **ppszText, int *pnMaxLength ) + +{ + if( nNeeded+1 >= *pnMaxLength ) + { + *pnMaxLength = MAX(*pnMaxLength * 2,nNeeded+1); + *ppszText = (char *) CPLRealloc(*ppszText, *pnMaxLength); + } +} + +/************************************************************************/ +/* AppendString() */ +/************************************************************************/ + +static void AppendString( char **ppszText, int *pnLength, int *pnMaxLength, + const char *pszTextToAppend ) + +{ + _GrowBuffer( *pnLength + strlen(pszTextToAppend) + 1, + ppszText, pnMaxLength ); + + strcat( *ppszText + *pnLength, pszTextToAppend ); + *pnLength += strlen( *ppszText + *pnLength ); +} + + +/************************************************************************/ +/* AppendCoordinateList() */ +/************************************************************************/ + +static void AppendCoordinateList( OGRLineString *poLine, + char **ppszText, int *pnLength, + int *pnMaxLength ) + +{ + char szCoordinate[256]= { 0 }; + int b3D = wkbHasZ(poLine->getGeometryType()); + + *pnLength += strlen(*ppszText + *pnLength); + _GrowBuffer( *pnLength + 20, ppszText, pnMaxLength ); + + strcat( *ppszText + *pnLength, "" ); + *pnLength += strlen(*ppszText + *pnLength); + + for( int iPoint = 0; iPoint < poLine->getNumPoints(); iPoint++ ) + { + MakeKMLCoordinate( szCoordinate, + poLine->getX(iPoint), + poLine->getY(iPoint), + poLine->getZ(iPoint), + b3D ); + _GrowBuffer( *pnLength + strlen(szCoordinate)+1, + ppszText, pnMaxLength ); + + if( iPoint != 0 ) + strcat( *ppszText + *pnLength, " " ); + + strcat( *ppszText + *pnLength, szCoordinate ); + *pnLength += strlen(*ppszText + *pnLength); + } + + _GrowBuffer( *pnLength + 20, ppszText, pnMaxLength ); + strcat( *ppszText + *pnLength, "" ); + *pnLength += strlen(*ppszText + *pnLength); +} + +/************************************************************************/ +/* OGR2KMLGeometryAppend() */ +/************************************************************************/ + +static int OGR2KMLGeometryAppend( OGRGeometry *poGeometry, + char **ppszText, int *pnLength, + int *pnMaxLength, char * szAltitudeMode ) + +{ +/* -------------------------------------------------------------------- */ +/* 2D Point */ +/* -------------------------------------------------------------------- */ + if( poGeometry->getGeometryType() == wkbPoint ) + { + char szCoordinate[256] = { 0 }; + OGRPoint* poPoint = static_cast(poGeometry); + + if (poPoint->getCoordinateDimension() == 0) + { + _GrowBuffer( *pnLength + 10, + ppszText, pnMaxLength ); + strcat( *ppszText + *pnLength, ""); + *pnLength += strlen( *ppszText + *pnLength ); + } + else + { + MakeKMLCoordinate( szCoordinate, + poPoint->getX(), poPoint->getY(), 0.0, FALSE ); + + _GrowBuffer( *pnLength + strlen(szCoordinate) + 60, + ppszText, pnMaxLength ); + + sprintf( *ppszText + *pnLength, + "%s", + szCoordinate ); + + *pnLength += strlen( *ppszText + *pnLength ); + } + } +/* -------------------------------------------------------------------- */ +/* 3D Point */ +/* -------------------------------------------------------------------- */ + else if( poGeometry->getGeometryType() == wkbPoint25D ) + { + char szCoordinate[256] = { 0 }; + OGRPoint *poPoint = static_cast(poGeometry); + + MakeKMLCoordinate( szCoordinate, + poPoint->getX(), poPoint->getY(), poPoint->getZ(), + TRUE ); + + if (NULL == szAltitudeMode) + { + _GrowBuffer( *pnLength + strlen(szCoordinate) + 70, + ppszText, pnMaxLength ); + + sprintf( *ppszText + *pnLength, + "%s", + szCoordinate ); + } + else + { + _GrowBuffer( *pnLength + strlen(szCoordinate) + + strlen(szAltitudeMode) + 70, + ppszText, pnMaxLength ); + + sprintf( *ppszText + *pnLength, + "%s%s", + szAltitudeMode, szCoordinate ); + } + + *pnLength += strlen( *ppszText + *pnLength ); + } +/* -------------------------------------------------------------------- */ +/* LineString and LinearRing */ +/* -------------------------------------------------------------------- */ + else if( poGeometry->getGeometryType() == wkbLineString + || poGeometry->getGeometryType() == wkbLineString25D ) + { + int bRing = EQUAL(poGeometry->getGeometryName(),"LINEARRING"); + + if( bRing ) + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + else + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + + if (NULL != szAltitudeMode) + { + AppendString( ppszText, pnLength, pnMaxLength, szAltitudeMode); + } + + AppendCoordinateList( (OGRLineString *) poGeometry, + ppszText, pnLength, pnMaxLength ); + + if( bRing ) + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + else + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + +/* -------------------------------------------------------------------- */ +/* Polygon */ +/* -------------------------------------------------------------------- */ + else if( poGeometry->getGeometryType() == wkbPolygon + || poGeometry->getGeometryType() == wkbPolygon25D ) + { + OGRPolygon* poPolygon = static_cast(poGeometry); + + AppendString( ppszText, pnLength, pnMaxLength, "" ); + + if (NULL != szAltitudeMode) + { + AppendString( ppszText, pnLength, pnMaxLength, szAltitudeMode); + } + + if( poPolygon->getExteriorRing() != NULL ) + { + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + + if( !OGR2KMLGeometryAppend( poPolygon->getExteriorRing(), + ppszText, pnLength, pnMaxLength, szAltitudeMode ) ) + { + return FALSE; + } + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + + for( int iRing = 0; iRing < poPolygon->getNumInteriorRings(); iRing++ ) + { + OGRLinearRing *poRing = poPolygon->getInteriorRing(iRing); + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + + if( !OGR2KMLGeometryAppend( poRing, ppszText, pnLength, + pnMaxLength, szAltitudeMode ) ) + { + return FALSE; + } + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + + AppendString( ppszText, pnLength, pnMaxLength, + "" ); + } + +/* -------------------------------------------------------------------- */ +/* MultiPolygon */ +/* -------------------------------------------------------------------- */ + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon + || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString + || wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint + || wkbFlatten(poGeometry->getGeometryType()) == wkbGeometryCollection ) + { + OGRGeometryCollection* poGC = NULL; + poGC = static_cast(poGeometry); + + AppendString( ppszText, pnLength, pnMaxLength, "" ); + + // XXX - mloskot + //if (NULL != szAltitudeMode) + //{ + // AppendString( ppszText, pnLength, pnMaxLength, szAltitudeMode); + //} + + for( int iMember = 0; iMember < poGC->getNumGeometries(); iMember++) + { + OGRGeometry *poMember = poGC->getGeometryRef( iMember ); + + if( !OGR2KMLGeometryAppend( poMember, ppszText, pnLength, pnMaxLength, szAltitudeMode ) ) + { + return FALSE; + } + } + + AppendString( ppszText, pnLength, pnMaxLength, "" ); + } + else + { + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* OGR_G_ExportEnvelopeToKMLTree() */ +/* */ +/* Export the envelope of a geometry as a KML:Box. */ +/************************************************************************/ + +CPLXMLNode* OGR_G_ExportEnvelopeToKMLTree( OGRGeometryH hGeometry ) +{ + VALIDATE_POINTER1( hGeometry, "OGR_G_ExportEnvelopeToKMLTree", NULL ); + + CPLXMLNode* psBox = NULL; + CPLXMLNode* psCoord = NULL; + OGREnvelope sEnvelope; + char szCoordinate[256] = { 0 }; + char* pszY = NULL; + + memset( &sEnvelope, 0, sizeof(sEnvelope) ); + ((OGRGeometry*)(hGeometry))->getEnvelope( &sEnvelope ); + + if( sEnvelope.MinX == 0 && sEnvelope.MaxX == 0 + && sEnvelope.MaxX == 0 && sEnvelope.MaxY == 0 ) + { + /* there is apparently a special way of representing a null box + geometry ... we should use it here eventually. */ + + return NULL; + } + + psBox = CPLCreateXMLNode( NULL, CXT_Element, "Box" ); + +/* -------------------------------------------------------------------- */ +/* Add minxy coordinate. */ +/* -------------------------------------------------------------------- */ + psCoord = CPLCreateXMLNode( psBox, CXT_Element, "coord" ); + + MakeKMLCoordinate( szCoordinate, sEnvelope.MinX, sEnvelope.MinY, 0.0, + FALSE ); + pszY = strstr(szCoordinate,",") + 1; + pszY[-1] = '\0'; + + CPLCreateXMLElementAndValue( psCoord, "X", szCoordinate ); + CPLCreateXMLElementAndValue( psCoord, "Y", pszY ); + +/* -------------------------------------------------------------------- */ +/* Add maxxy coordinate. */ +/* -------------------------------------------------------------------- */ + psCoord = CPLCreateXMLNode( psBox, CXT_Element, "coord" ); + + MakeKMLCoordinate( szCoordinate, sEnvelope.MaxX, sEnvelope.MaxY, 0.0, + FALSE ); + pszY = strstr(szCoordinate,",") + 1; + pszY[-1] = '\0'; + + CPLCreateXMLElementAndValue( psCoord, "X", szCoordinate ); + CPLCreateXMLElementAndValue( psCoord, "Y", pszY ); + + return psBox; +} + +/************************************************************************/ +/* OGR_G_ExportToKML() */ +/************************************************************************/ + +/** + * \brief Convert a geometry into KML format. + * + * The returned string should be freed with CPLFree() when no longer required. + * + * This method is the same as the C++ method OGRGeometry::exportToKML(). + * + * @param hGeometry handle to the geometry. + * @param pszAltitudeMode value to write in altitudeMode element, or NULL. + * @return A KML fragment or NULL in case of error. + */ + +char *OGR_G_ExportToKML( OGRGeometryH hGeometry, const char *pszAltitudeMode ) +{ + char* pszText = NULL; + int nLength = 0; + int nMaxLength = 1; + char szAltitudeMode[128]; + + // TODO - mloskot: Shouldn't we use VALIDATE_POINTER1 here? + if( hGeometry == NULL ) + return CPLStrdup( "" ); + + pszText = (char *) CPLMalloc(nMaxLength); + pszText[0] = '\0'; + + if (NULL != pszAltitudeMode && strlen(pszAltitudeMode) < 128 - (29 + 1)) + { + sprintf(szAltitudeMode, "%s", pszAltitudeMode); + } + else + { + szAltitudeMode[0] = 0; + } + + if( !OGR2KMLGeometryAppend( (OGRGeometry *) hGeometry, &pszText, + &nLength, &nMaxLength, szAltitudeMode )) + { + CPLFree( pszText ); + return NULL; + } + + return pszText; +} + +/************************************************************************/ +/* OGR_G_ExportToKMLTree() */ +/************************************************************************/ + +CPLXMLNode *OGR_G_ExportToKMLTree( OGRGeometryH hGeometry ) +{ + char *pszText = NULL; + CPLXMLNode *psTree = NULL; + + // TODO - mloskot: If passed geometry is null the pszText is non-null, + // so the condition below is false. + pszText = OGR_G_ExportToKML( hGeometry, NULL ); + if( pszText == NULL ) + return NULL; + + psTree = CPLParseXMLString( pszText ); + + CPLFree( pszText ); + + return psTree; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogr_kml.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogr_kml.h new file mode 100644 index 000000000..98a3110bf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogr_kml.h @@ -0,0 +1,170 @@ +/****************************************************************************** + * $Id: ogr_kml.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: KML Driver + * Purpose: Declarations for OGR wrapper classes for KML, and OGR->KML + * translation of geometry. + * Author: Christopher Condit, condit@sdsc.edu; + * Jens Oberender, j.obi@troja.net + * + ****************************************************************************** + * Copyright (c) 2006, Christopher Condit + * 2007, Jens Oberender + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef OGR_KML_H_INCLUDED +#define OGR_KML_H_INCLUDED + +#include "ogrsf_frmts.h" + +#ifdef HAVE_EXPAT +# include "kmlvector.h" +#endif + +class OGRKMLDataSource; + +/************************************************************************/ +/* OGRKMLLayer */ +/************************************************************************/ + +class OGRKMLLayer : public OGRLayer +{ +public: + + OGRKMLLayer( const char* pszName_, + OGRSpatialReference* poSRS, + int bWriter, + OGRwkbGeometryType eType, + OGRKMLDataSource* poDS ); + ~OGRKMLLayer(); + + // + // OGRLayer Interface + // + OGRFeatureDefn* GetLayerDefn(); + OGRErr ICreateFeature( OGRFeature* poFeature ); + OGRErr CreateField( OGRFieldDefn* poField, int bApproxOK = TRUE ); + void ResetReading(); + OGRFeature* GetNextFeature(); + GIntBig GetFeatureCount( int bForce = TRUE ); + int TestCapability( const char* pszCap ); + + // + // OGRKMLLayer Interface + // + void SetLayerNumber( int nLayer ); + + void SetClosedForWriting() { bClosedForWriting = TRUE; } + + CPLString WriteSchema(); + +private: + friend class OGRKMLDataSource; + + OGRKMLDataSource* poDS_; + OGRSpatialReference* poSRS_; + OGRCoordinateTransformation *poCT_; + + OGRFeatureDefn* poFeatureDefn_; + + int iNextKMLId_; + int nTotalKMLCount_; + int bWriter_; + int nLayerNumber_; + int nWroteFeatureCount_; + int bSchemaWritten_; + int bClosedForWriting; + char* pszName_; + + int nLastAsked; + int nLastCount; +}; + +/************************************************************************/ +/* OGRKMLDataSource */ +/************************************************************************/ + +class OGRKMLDataSource : public OGRDataSource +{ +public: + OGRKMLDataSource(); + ~OGRKMLDataSource(); + + // + // OGRDataSource Interface + // + int Open( const char* pszName, int bTestOpen ); + const char* GetName() { return pszName_; } + int GetLayerCount() { return nLayers_; } + OGRLayer* GetLayer( int nLayer ); + OGRLayer* ICreateLayer( const char* pszName, + OGRSpatialReference* poSRS = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char** papszOptions = NULL ); + int TestCapability( const char* pszCap ); + + // + // OGRKMLDataSource Interface + // + int Create( const char* pszName, char** papszOptions ); + const char* GetNameField() const { return pszNameField_; } + const char* GetDescriptionField() const { return pszDescriptionField_; } + const char* GetAltitudeMode() { return pszAltitudeMode_; } + VSILFILE* GetOutputFP() { return fpOutput_; } + void GrowExtents( OGREnvelope *psGeomBounds ); +#ifdef HAVE_EXPAT + KML* GetKMLFile() { return poKMLFile_; }; +#endif + + bool IsFirstCTError() { return !bIssuedCTError_; } + void IssuedFirstCTError() { bIssuedCTError_ = true; } + +private: + +#ifdef HAVE_EXPAT + KML* poKMLFile_; +#endif + + char* pszName_; + + OGRKMLLayer** papoLayers_; + int nLayers_; + + //The name of the field to use for the KML name element + char* pszNameField_; + char* pszDescriptionField_; + + //The KML altitude mode to use + char* pszAltitudeMode_; + + char** papszCreateOptions_; + + // output related parameters + VSILFILE* fpOutput_; + + OGREnvelope oEnvelope_; + + //Have we issued a coordinate transformation already for this datasource + bool bIssuedCTError_; +}; + +#endif /* OGR_KML_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogrkmldatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogrkmldatasource.cpp new file mode 100644 index 000000000..cfcec7f1c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogrkmldatasource.cpp @@ -0,0 +1,447 @@ +/****************************************************************************** + * $Id: ogrkmldatasource.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: KML Driver + * Purpose: Implementation of OGRKMLDataSource class. + * Author: Christopher Condit, condit@sdsc.edu; + * Jens Oberender, j.obi@troja.net + * + ****************************************************************************** + * Copyright (c) 2006, Christopher Condit + * 2007, Jens Oberender + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogr_kml.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_error.h" +#include "cpl_minixml.h" + +/************************************************************************/ +/* OGRKMLDataSource() */ +/************************************************************************/ + +OGRKMLDataSource::OGRKMLDataSource() +{ + pszName_ = NULL; + pszNameField_ = NULL; + pszDescriptionField_ = NULL; + pszAltitudeMode_ = NULL; + papoLayers_ = NULL; + nLayers_ = 0; + + fpOutput_ = NULL; + + papszCreateOptions_ = NULL; + + bIssuedCTError_ = false; + +#ifdef HAVE_EXPAT + poKMLFile_ = NULL; +#endif +} + +/************************************************************************/ +/* ~OGRKMLDataSource() */ +/************************************************************************/ + +OGRKMLDataSource::~OGRKMLDataSource() +{ + if( fpOutput_ != NULL ) + { + if( nLayers_ > 0 ) + { + if( nLayers_ == 1 && papoLayers_[0]->nWroteFeatureCount_ == 0 ) + { + VSIFPrintfL( fpOutput_, "%s\n", papoLayers_[0]->GetName() ); + } + + VSIFPrintfL( fpOutput_, "%s", "\n"); + + for( int i = 0; i < nLayers_; i++ ) + { + if( !(papoLayers_[i]->bSchemaWritten_) && papoLayers_[i]->nWroteFeatureCount_ != 0 ) + { + CPLString osRet = papoLayers_[i]->WriteSchema(); + if( osRet.size() ) + VSIFPrintfL( fpOutput_, "%s", osRet.c_str() ); + } + } + } + VSIFPrintfL( fpOutput_, "%s", "\n" ); + + VSIFCloseL( fpOutput_ ); + } + + CSLDestroy( papszCreateOptions_ ); + CPLFree( pszName_ ); + CPLFree( pszNameField_ ); + CPLFree( pszDescriptionField_ ); + CPLFree( pszAltitudeMode_ ); + + for( int i = 0; i < nLayers_; i++ ) + { + delete papoLayers_[i]; + } + + CPLFree( papoLayers_ ); + +#ifdef HAVE_EXPAT + delete poKMLFile_; +#endif +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +#ifdef HAVE_EXPAT +int OGRKMLDataSource::Open( const char * pszNewName, int bTestOpen ) +{ + CPLAssert( NULL != pszNewName ); + + int nCount = 0; + OGRKMLLayer *poLayer = NULL; + OGRwkbGeometryType poGeotype; + +/* -------------------------------------------------------------------- */ +/* Create a KML object and open the source file. */ +/* -------------------------------------------------------------------- */ + poKMLFile_ = new KMLVector(); + + if( !poKMLFile_->open( pszNewName ) ) + { + delete poKMLFile_; + poKMLFile_ = NULL; + return FALSE; + } + + pszName_ = CPLStrdup( pszNewName ); + +/* -------------------------------------------------------------------- */ +/* If we aren't sure it is KML, validate it by start parsing */ +/* -------------------------------------------------------------------- */ + if( bTestOpen && !poKMLFile_->isValid() ) + { + delete poKMLFile_; + poKMLFile_ = NULL; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Prescan the KML file so we can later work with the structure */ +/* -------------------------------------------------------------------- */ + poKMLFile_->parse(); + +/* -------------------------------------------------------------------- */ +/* Classify the nodes */ +/* -------------------------------------------------------------------- */ + if( !poKMLFile_->classifyNodes() ) + { + delete poKMLFile_; + poKMLFile_ = NULL; + return FALSE; + } + + +/* -------------------------------------------------------------------- */ +/* Eliminate the empty containers (if there is at least one */ +/* valid container !) */ +/* -------------------------------------------------------------------- */ + int bHasOnlyEmpty = poKMLFile_->hasOnlyEmpty(); + if (bHasOnlyEmpty) + CPLDebug("KML", "Has only empty containers"); + else + poKMLFile_->eliminateEmpty(); + +/* -------------------------------------------------------------------- */ +/* Find layers to use in the KML structure */ +/* -------------------------------------------------------------------- */ + poKMLFile_->findLayers(NULL, bHasOnlyEmpty); + +/* -------------------------------------------------------------------- */ +/* Print the structure */ +/* -------------------------------------------------------------------- */ + if( CPLGetConfigOption("KML_DEBUG",NULL) != NULL ) + poKMLFile_->print(3); + + nLayers_ = poKMLFile_->getNumLayers(); + +/* -------------------------------------------------------------------- */ +/* Allocate memory for the Layers */ +/* -------------------------------------------------------------------- */ + papoLayers_ = (OGRKMLLayer **) + CPLMalloc( sizeof(OGRKMLLayer *) * nLayers_ ); + + OGRSpatialReference *poSRS = new OGRSpatialReference("GEOGCS[\"WGS 84\", " + " DATUM[\"WGS_1984\"," + " SPHEROID[\"WGS 84\",6378137,298.257223563," + " AUTHORITY[\"EPSG\",\"7030\"]]," + " AUTHORITY[\"EPSG\",\"6326\"]]," + " PRIMEM[\"Greenwich\",0," + " AUTHORITY[\"EPSG\",\"8901\"]]," + " UNIT[\"degree\",0.01745329251994328," + " AUTHORITY[\"EPSG\",\"9122\"]]," + " AUTHORITY[\"EPSG\",\"4326\"]]"); + +/* -------------------------------------------------------------------- */ +/* Create the Layers and fill them */ +/* -------------------------------------------------------------------- */ + for( nCount = 0; nCount < nLayers_; nCount++ ) + { + if( !poKMLFile_->selectLayer(nCount) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "There are no layers or a layer can not be found!"); + break; + } + + if( poKMLFile_->getCurrentType() == Point ) + poGeotype = wkbPoint; + else if( poKMLFile_->getCurrentType() == LineString ) + poGeotype = wkbLineString; + else if( poKMLFile_->getCurrentType() == Polygon ) + poGeotype = wkbPolygon; + else if( poKMLFile_->getCurrentType() == MultiPoint ) + poGeotype = wkbMultiPoint; + else if( poKMLFile_->getCurrentType() == MultiLineString ) + poGeotype = wkbMultiLineString; + else if( poKMLFile_->getCurrentType() == MultiPolygon ) + poGeotype = wkbMultiPolygon; + else if( poKMLFile_->getCurrentType() == MultiGeometry ) + poGeotype = wkbGeometryCollection; + else + poGeotype = wkbUnknown; + + if (poGeotype != wkbUnknown && poKMLFile_->is25D()) + poGeotype = wkbSetZ(poGeotype); + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + CPLString sName( poKMLFile_->getCurrentName() ); + + if( sName.empty() ) + { + sName.Printf( "Layer #%d", nCount ); + } + + poLayer = new OGRKMLLayer( sName.c_str(), poSRS, FALSE, poGeotype, this ); + + poLayer->SetLayerNumber( nCount ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers_[nCount] = poLayer; + } + + poSRS->Release(); + + return TRUE; +} +#endif /* HAVE_EXPAT */ + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRKMLDataSource::Create( const char* pszName, char** papszOptions ) +{ + CPLAssert( NULL != pszName ); + + if( fpOutput_ != NULL ) + { + CPLAssert( FALSE ); + return FALSE; + } + + if (CSLFetchNameValue(papszOptions, "NameField")) + pszNameField_ = CPLStrdup(CSLFetchNameValue(papszOptions, "NameField")); + else + pszNameField_ = CPLStrdup("Name"); + + if (CSLFetchNameValue(papszOptions, "DescriptionField")) + pszDescriptionField_ = CPLStrdup(CSLFetchNameValue(papszOptions, "DescriptionField")); + else + pszDescriptionField_ = CPLStrdup("Description"); + + pszAltitudeMode_ = CPLStrdup(CSLFetchNameValue(papszOptions, "AltitudeMode")); + if( (NULL != pszAltitudeMode_) && strlen(pszAltitudeMode_) > 0) + { + //Check to see that the specified AltitudeMode is valid + if ( EQUAL(pszAltitudeMode_, "clampToGround") + || EQUAL(pszAltitudeMode_, "relativeToGround") + || EQUAL(pszAltitudeMode_, "absolute")) + { + CPLDebug("KML", "Using '%s' for AltitudeMode", pszAltitudeMode_); + } + else + { + CPLFree( pszAltitudeMode_ ); + pszAltitudeMode_ = NULL; + CPLError( CE_Warning, CPLE_AppDefined, "Invalide AltitideMode specified, ignoring" ); + } + } + else + { + CPLFree( pszAltitudeMode_ ); + pszAltitudeMode_ = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create the output file. */ +/* -------------------------------------------------------------------- */ + + if (strcmp(pszName, "/dev/stdout") == 0) + pszName = "/vsistdout/"; + + pszName_ = CPLStrdup( pszName ); + + fpOutput_ = VSIFOpenL( pszName, "wb" ); + if( fpOutput_ == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create KML file %s.", pszName ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Write out "standard" header. */ +/* -------------------------------------------------------------------- */ + VSIFPrintfL( fpOutput_, "\n" ); + + VSIFPrintfL( fpOutput_, "\n\n" ); + + return TRUE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRKMLDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + CPL_UNUSED char ** papszOptions ) +{ + CPLAssert( NULL != pszLayerName); + +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( fpOutput_ == NULL ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened for read access.\n" + "New layer %s cannot be created.\n", + pszName_, pszLayerName ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Close the previous layer (if there is one open) */ +/* -------------------------------------------------------------------- */ + if (GetLayerCount() > 0) + { + if( nLayers_ == 1 && papoLayers_[0]->nWroteFeatureCount_ == 0 ) + { + VSIFPrintfL( fpOutput_, "%s\n", papoLayers_[0]->GetName() ); + } + + VSIFPrintfL( fpOutput_, "\n"); + ((OGRKMLLayer*)GetLayer(GetLayerCount()-1))->SetClosedForWriting(); + } + +/* -------------------------------------------------------------------- */ +/* Ensure name is safe as an element name. */ +/* -------------------------------------------------------------------- */ + char *pszCleanLayerName = CPLStrdup( pszLayerName ); + + CPLCleanXMLElementName( pszCleanLayerName ); + if( strcmp(pszCleanLayerName, pszLayerName) != 0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Layer name '%s' adjusted to '%s' for XML validity.", + pszLayerName, pszCleanLayerName ); + } + + if (GetLayerCount() > 0) + { + VSIFPrintfL( fpOutput_, "%s\n", pszCleanLayerName); + } + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRKMLLayer *poLayer; + poLayer = new OGRKMLLayer( pszCleanLayerName, poSRS, TRUE, eType, this ); + + CPLFree( pszCleanLayerName ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers_ = (OGRKMLLayer **) + CPLRealloc( papoLayers_, sizeof(OGRKMLLayer *) * (nLayers_+1) ); + + papoLayers_[nLayers_++] = poLayer; + + return poLayer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRKMLDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap, ODsCCreateLayer) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRKMLDataSource::GetLayer( int iLayer ) +{ + if( iLayer < 0 || iLayer >= nLayers_ ) + return NULL; + else + return papoLayers_[iLayer]; +} + +/************************************************************************/ +/* GrowExtents() */ +/************************************************************************/ + +void OGRKMLDataSource::GrowExtents( OGREnvelope *psGeomBounds ) +{ + CPLAssert( NULL != psGeomBounds ); + + oEnvelope_.Merge( *psGeomBounds ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogrkmldriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogrkmldriver.cpp new file mode 100644 index 000000000..b46b29e1a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogrkmldriver.cpp @@ -0,0 +1,156 @@ +/****************************************************************************** + * $Id: ogrkmldriver.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: KML Driver + * Purpose: Implementation of OGRKMLDriver class. + * Author: Christopher Condit, condit@sdsc.edu; + * Jens Oberender, j.obi@troja.net + * + ****************************************************************************** + * Copyright (c) 2006, Christopher Condit + * 2007, Jens Oberender + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogr_kml.h" +#include "cpl_conv.h" +#include "cpl_error.h" + +/************************************************************************/ +/* OGRKMLDriverIdentify() */ +/************************************************************************/ + +static int OGRKMLDriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->fpL == NULL ) + return FALSE; + + return( strstr((const char*)poOpenInfo->pabyHeader, "eAccess == GA_Update ) + return NULL; + + if( !OGRKMLDriverIdentify(poOpenInfo) ) + return NULL; + + OGRKMLDataSource* poDS = NULL; + +#ifdef HAVE_EXPAT + poDS = new OGRKMLDataSource(); + + if( poDS->Open( poOpenInfo->pszFilename, TRUE ) ) + { + /*if( poDS->GetLayerCount() == 0 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "No layers in KML file: %s.", pszName ); + + delete poDS; + poDS = NULL; + }*/ + } + else + { + delete poDS; + poDS = NULL; + } +#endif + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRKMLDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + CPLAssert( NULL != pszName ); + CPLDebug( "KML", "Attempt to create: %s", pszName ); + + OGRKMLDataSource *poDS = new OGRKMLDataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRKML() */ +/************************************************************************/ + +void RegisterOGRKML() +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "KML" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "KML" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Keyhole Markup Language (KML)" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "kml" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_kml.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +""); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, "" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Real String" ); + + poDriver->pfnOpen = OGRKMLDriverOpen; + poDriver->pfnIdentify = OGRKMLDriverIdentify; + poDriver->pfnCreate = OGRKMLDriverCreate; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogrkmllayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogrkmllayer.cpp new file mode 100644 index 000000000..05603bb86 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/kml/ogrkmllayer.cpp @@ -0,0 +1,625 @@ +/****************************************************************************** + * $Id: ogrkmllayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: KML Driver + * Purpose: Implementation of OGRKMLLayer class. + * Author: Christopher Condit, condit@sdsc.edu + * Jens Oberender, j.obi@troja.net + * + ****************************************************************************** + * Copyright (c) 2006, Christopher Condit + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_kml.h" +#include "ogr_api.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" + +/* Function utility to dump OGRGeometry to KML text. */ +char *OGR_G_ExportToKML( OGRGeometryH hGeometry, const char* pszAltitudeMode ); + +/************************************************************************/ +/* OGRKMLLayer() */ +/************************************************************************/ + +OGRKMLLayer::OGRKMLLayer( const char * pszName, + OGRSpatialReference *poSRSIn, int bWriterIn, + OGRwkbGeometryType eReqType, + OGRKMLDataSource *poDSIn ) +{ + poCT_ = NULL; + + /* KML should be created as WGS84. */ + if( poSRSIn != NULL ) + { + poSRS_ = new OGRSpatialReference(NULL); + poSRS_->SetWellKnownGeogCS( "WGS84" ); + if (!poSRS_->IsSame(poSRSIn)) + { + poCT_ = OGRCreateCoordinateTransformation( poSRSIn, poSRS_ ); + if( poCT_ == NULL && poDSIn->IsFirstCTError() ) + { + /* If we can't create a transformation, issue a warning - but continue the transformation*/ + char *pszWKT = NULL; + + poSRSIn->exportToPrettyWkt( &pszWKT, FALSE ); + + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to create coordinate transformation between the\n" + "input coordinate system and WGS84. This may be because they\n" + "are not transformable, or because projection services\n" + "(PROJ.4 DLL/.so) could not be loaded.\n" + "KML geometries may not render correctly.\n" + "This message will not be issued any more. \n" + "\nSource:\n%s\n", + pszWKT ); + + CPLFree( pszWKT ); + poDSIn->IssuedFirstCTError(); + } + } + } + else + { + poSRS_ = NULL; + } + + iNextKMLId_ = 0; + nTotalKMLCount_ = -1; + nLastAsked = -1; + nLastCount = -1; + + poDS_ = poDSIn; + + poFeatureDefn_ = new OGRFeatureDefn( pszName ); + SetDescription( poFeatureDefn_->GetName() ); + poFeatureDefn_->Reference(); + poFeatureDefn_->SetGeomType( eReqType ); + if( poFeatureDefn_->GetGeomFieldCount() != 0 ) + poFeatureDefn_->GetGeomFieldDefn(0)->SetSpatialRef(poSRS_); + + OGRFieldDefn oFieldName( "Name", OFTString ); + poFeatureDefn_->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oFieldDesc( "Description", OFTString ); + poFeatureDefn_->AddFieldDefn( &oFieldDesc ); + + bWriter_ = bWriterIn; + nWroteFeatureCount_ = 0; + bClosedForWriting = (bWriterIn == FALSE); + bSchemaWritten_ = FALSE; + + pszName_ = CPLStrdup(pszName); +} + +/************************************************************************/ +/* ~OGRKMLLayer() */ +/************************************************************************/ + +OGRKMLLayer::~OGRKMLLayer() +{ + if( NULL != poFeatureDefn_ ) + poFeatureDefn_->Release(); + + if( NULL != poSRS_ ) + poSRS_->Release(); + + if( NULL != poCT_ ) + delete poCT_; + + CPLFree( pszName_ ); +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn* OGRKMLLayer::GetLayerDefn() +{ + return poFeatureDefn_; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRKMLLayer::ResetReading() +{ + iNextKMLId_ = 0; + nLastAsked = -1; + nLastCount = -1; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRKMLLayer::GetNextFeature() +{ +#ifndef HAVE_EXPAT + return NULL; +#else + /* -------------------------------------------------------------------- */ + /* Loop till we find a feature matching our criteria. */ + /* -------------------------------------------------------------------- */ + KML *poKMLFile = poDS_->GetKMLFile(); + if( poKMLFile == NULL ) + return NULL; + poKMLFile->selectLayer(nLayerNumber_); + + while(TRUE) + { + Feature *poFeatureKML = NULL; + poFeatureKML = poKMLFile->getFeature(iNextKMLId_++, nLastAsked, nLastCount); + + if(poFeatureKML == NULL) + return NULL; + + CPLAssert( poFeatureKML != NULL ); + + OGRFeature *poFeature = new OGRFeature( poFeatureDefn_ ); + + if(poFeatureKML->poGeom) + { + poFeature->SetGeometryDirectly(poFeatureKML->poGeom); + poFeatureKML->poGeom = NULL; + } + + // Add fields + poFeature->SetField( poFeatureDefn_->GetFieldIndex("Name"), poFeatureKML->sName.c_str() ); + poFeature->SetField( poFeatureDefn_->GetFieldIndex("Description"), poFeatureKML->sDescription.c_str() ); + poFeature->SetFID( iNextKMLId_ - 1 ); + + // Clean up + delete poFeatureKML; + + if( poFeature->GetGeometryRef() != NULL && poSRS_ != NULL) + { + poFeature->GetGeometryRef()->assignSpatialReference( poSRS_ ); + } + + /* Check spatial/attribute filters */ + if ((m_poFilterGeom == NULL || FilterGeometry( poFeature->GetGeometryRef() ) ) && + (m_poAttrQuery == NULL || m_poAttrQuery->Evaluate( poFeature )) ) + { + // Return the feature + return poFeature; + } + else + { + delete poFeature; + } + } + +#endif /* HAVE_EXPAT */ +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRKMLLayer::GetFeatureCount( +#ifndef HAVE_EXPAT +CPL_UNUSED +#endif + int bForce + ) +{ + int nCount = 0; + +#ifdef HAVE_EXPAT + if (m_poFilterGeom == NULL && m_poAttrQuery == NULL) + { + KML *poKMLFile = poDS_->GetKMLFile(); + if( NULL != poKMLFile ) + { + poKMLFile->selectLayer(nLayerNumber_); + nCount = poKMLFile->getNumFeatures(); + } + } + else + return OGRLayer::GetFeatureCount(bForce); +#endif + + return nCount; +} + +/************************************************************************/ +/* WriteSchema() */ +/************************************************************************/ + +CPLString OGRKMLLayer::WriteSchema() +{ + CPLString osRet; + if ( !(bSchemaWritten_) ) + { + OGRFeatureDefn *featureDefinition = GetLayerDefn(); + for (int j=0; j < featureDefinition->GetFieldCount(); j++) + { + OGRFieldDefn *fieldDefinition = featureDefinition->GetFieldDefn(j); + + if (NULL != poDS_->GetNameField() && + EQUAL(fieldDefinition->GetNameRef(), poDS_->GetNameField()) ) + continue; + + if (NULL != poDS_->GetDescriptionField() && + EQUAL(fieldDefinition->GetNameRef(), poDS_->GetDescriptionField()) ) + continue; + + if( osRet.size() == 0 ) + { + osRet += CPLSPrintf( "\n", pszName_, pszName_ ); + } + + const char* pszKMLType = NULL; + const char* pszKMLEltName = NULL; + // Match the OGR type to the GDAL type + switch (fieldDefinition->GetType()) + { + case OFTInteger: + pszKMLType = "int"; + pszKMLEltName = "SimpleField"; + break; + case OFTIntegerList: + pszKMLType = "int"; + pszKMLEltName = "SimpleArrayField"; + break; + case OFTReal: + pszKMLType = "float"; + pszKMLEltName = "SimpleField"; + break; + case OFTRealList: + pszKMLType = "float"; + pszKMLEltName = "SimpleArrayField"; + break; + case OFTString: + pszKMLType = "string"; + pszKMLEltName = "SimpleField"; + break; + case OFTStringList: + pszKMLType = "string"; + pszKMLEltName = "SimpleArrayField"; + break; + //TODO: KML doesn't handle these data types yet... + case OFTDate: + case OFTTime: + case OFTDateTime: + pszKMLType = "string"; + pszKMLEltName = "SimpleField"; + break; + + default: + pszKMLType = "string"; + pszKMLEltName = "SimpleField"; + break; + } + osRet += CPLSPrintf( "\t<%s name=\"%s\" type=\"%s\">\n", + pszKMLEltName, fieldDefinition->GetNameRef() ,pszKMLType, pszKMLEltName ); + } + if( osRet.size() ) + osRet += CPLSPrintf( "%s", "\n" ); + } + return osRet; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRKMLLayer::ICreateFeature( OGRFeature* poFeature ) +{ + CPLAssert( NULL != poFeature ); + CPLAssert( NULL != poDS_ ); + + if( !bWriter_ ) + return OGRERR_FAILURE; + + if( bClosedForWriting ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Interleaved feature adding to different layers is not supported"); + return OGRERR_FAILURE; + } + + VSILFILE *fp = poDS_->GetOutputFP(); + CPLAssert( NULL != fp ); + + if( poDS_->GetLayerCount() == 1 && nWroteFeatureCount_ == 0 ) + { + CPLString osRet = WriteSchema(); + if( osRet.size() ) + VSIFPrintfL( fp, "%s", osRet.c_str() ); + bSchemaWritten_ = TRUE; + + VSIFPrintfL( fp, "%s\n", pszName_); + } + + VSIFPrintfL( fp, " \n" ); + + if( poFeature->GetFID() == OGRNullFID ) + poFeature->SetFID( iNextKMLId_++ ); + + // Find and write the name element + if (NULL != poDS_->GetNameField()) + { + for( int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn( iField ); + + if( poFeature->IsFieldSet( iField ) + && EQUAL(poField->GetNameRef(), poDS_->GetNameField()) ) + { + const char *pszRaw = poFeature->GetFieldAsString( iField ); + while( *pszRaw == ' ' ) + pszRaw++; + + char *pszEscaped = OGRGetXML_UTF8_EscapedString( pszRaw ); + + VSIFPrintfL( fp, "\t%s\n", pszEscaped); + CPLFree( pszEscaped ); + } + } + } + + if (NULL != poDS_->GetDescriptionField()) + { + for( int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn( iField ); + + if( poFeature->IsFieldSet( iField ) + && EQUAL(poField->GetNameRef(), poDS_->GetDescriptionField()) ) + { + const char *pszRaw = poFeature->GetFieldAsString( iField ); + while( *pszRaw == ' ' ) + pszRaw++; + + char *pszEscaped = OGRGetXML_UTF8_EscapedString( pszRaw ); + + VSIFPrintfL( fp, "\t%s\n", pszEscaped); + CPLFree( pszEscaped ); + } + } + } + + OGRwkbGeometryType eGeomType = wkbNone; + if (poFeature->GetGeometryRef() != NULL) + eGeomType = wkbFlatten(poFeature->GetGeometryRef()->getGeometryType()); + if ( wkbPolygon == eGeomType + || wkbMultiPolygon == eGeomType + || wkbLineString == eGeomType + || wkbMultiLineString == eGeomType ) + { + OGRStylePen *poPen = NULL; + OGRStyleMgr oSM; + + if( poFeature->GetStyleString() != NULL ) + { + oSM.InitFromFeature( poFeature ); + + int i; + for(i=0; iGetType() == OGRSTCPen ) + { + poPen = (OGRStylePen*) poTool; + break; + } + delete poTool; + } + } + + VSIFPrintfL( fp, "\t\n" ); + } + + int bHasFoundOtherField = FALSE; + + // Write all fields as SchemaData + for( int iField = 0; iField < poFeatureDefn_->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poField = poFeatureDefn_->GetFieldDefn( iField ); + + if( poFeature->IsFieldSet( iField )) + { + if (NULL != poDS_->GetNameField() && + EQUAL(poField->GetNameRef(), poDS_->GetNameField()) ) + continue; + + if (NULL != poDS_->GetDescriptionField() && + EQUAL(poField->GetNameRef(), poDS_->GetDescriptionField()) ) + continue; + + if (!bHasFoundOtherField) + { + VSIFPrintfL( fp, "\t\n", pszName_ ); + bHasFoundOtherField = TRUE; + } + const char *pszRaw = poFeature->GetFieldAsString( iField ); + + while( *pszRaw == ' ' ) + pszRaw++; + + char *pszEscaped; + if (poFeatureDefn_->GetFieldDefn(iField)->GetType() == OFTReal) + { + pszEscaped = CPLStrdup( pszRaw ); + } + else + { + pszEscaped = OGRGetXML_UTF8_EscapedString( pszRaw ); + } + + VSIFPrintfL( fp, "\t\t%s\n", + poField->GetNameRef(), pszEscaped); + + CPLFree( pszEscaped ); + } + } + + if (bHasFoundOtherField) + { + VSIFPrintfL( fp, "\t\n" ); + } + + // Write out Geometry - for now it isn't indented properly. + if( poFeature->GetGeometryRef() != NULL ) + { + char* pszGeometry = NULL; + OGREnvelope sGeomBounds; + OGRGeometry* poWGS84Geom; + + if (NULL != poCT_) + { + poWGS84Geom = poFeature->GetGeometryRef()->clone(); + poWGS84Geom->transform( poCT_ ); + } + else + { + poWGS84Geom = poFeature->GetGeometryRef(); + } + + // TODO - porting + // pszGeometry = poFeature->GetGeometryRef()->exportToKML(); + pszGeometry = + OGR_G_ExportToKML( (OGRGeometryH)poWGS84Geom, + poDS_->GetAltitudeMode()); + + VSIFPrintfL( fp, " %s\n", pszGeometry ); + CPLFree( pszGeometry ); + + poWGS84Geom->getEnvelope( &sGeomBounds ); + poDS_->GrowExtents( &sGeomBounds ); + + if (NULL != poCT_) + { + delete poWGS84Geom; + } + } + + VSIFPrintfL( fp, " \n" ); + nWroteFeatureCount_++; + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRKMLLayer::TestCapability( const char * pszCap ) +{ + if( EQUAL(pszCap, OLCSequentialWrite) ) + { + return bWriter_; + } + else if( EQUAL(pszCap, OLCCreateField) ) + { + return bWriter_ && iNextKMLId_ == 0; + } + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + { +// if( poFClass == NULL +// || m_poFilterGeom != NULL +// || m_poAttrQuery != NULL ) + return FALSE; + +// return poFClass->GetFeatureCount() != -1; + } + + else if (EQUAL(pszCap, OLCStringsAsUTF8)) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRKMLLayer::CreateField( OGRFieldDefn *poField, + CPL_UNUSED int bApproxOK ) +{ + if( !bWriter_ || iNextKMLId_ != 0 ) + return OGRERR_FAILURE; + + OGRFieldDefn oCleanCopy( poField ); + poFeatureDefn_->AddFieldDefn( &oCleanCopy ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetLayerNumber() */ +/************************************************************************/ + +void OGRKMLLayer::SetLayerNumber( int nLayer ) +{ + nLayerNumber_ = nLayer; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/.indent.pro b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/.indent.pro new file mode 100644 index 000000000..ebc80a0b4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/.indent.pro @@ -0,0 +1,14 @@ +-bc +-br +-lp +-nce +-ncs +-npsl +-nut +-prs +-ts4 +-ip4 +-i4 +-bad +-bfda +-l79 diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/GNUmakefile new file mode 100644 index 000000000..bb7410ce2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/GNUmakefile @@ -0,0 +1,31 @@ + + +include ../../../GDALmake.opt + +CORE_OBJ = ogrlibkmlgeometry.o \ + ogrlibkmlfield.o \ + ogrlibkmlfeature.o \ + ogrlibkmlfeaturestyle.o \ + ogrlibkmlstyle.o + +OGR_OBJ = ogrlibkmldriver.o ogrlibkmldatasource.o ogrlibkmllayer.o + +OBJ = $(CORE_OBJ) $(OGR_OBJ) + +CPPFLAGS := -I. -I.. -I../.. -I$(GDAL_ROOT)/port -I$(GDAL_ROOT)/gcore -I$(GDAL_ROOT)/alg \ + -I$(GDAL_ROOT)/ogr -I$(GDAL_ROOT)/ogrsf_frmts $(LIBKML_INCLUDE) $(CPPFLAGS) + +#CFLAGS := $(filter-out -Wall,$(CFLAGS)) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogrlibkmlgeometry.h \ + ogrlibkmlfield.h \ + ogrlibkmlfeature.h \ + ogrlibkmlfeaturestyle.h \ + ogrlibkmlstyle.h \ + ogr_libkml.h + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/drv_libkml.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/drv_libkml.html new file mode 100644 index 000000000..5177f5584 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/drv_libkml.html @@ -0,0 +1,887 @@ + + + + + LIBKML Driver + + + + +

    LIBKML Driver (.kml .kmz)

    + +

    + The LIBKML driver is a client of + Libkml from Google, a + reference implementation of + KML + reading and writing, in the form of a cross platform C++ library. + You must build and install Libkml in order to use this OGR driver. + Note: you need to build libkml from its latest SVN version (libkml 1.2 isn't + enough). +

    + +

    + Note that if you build and include this LIBKML driver, it will become the + default reader of KML for ogr, overriding the previous KML driver. You can + still specify either KML or LIBKML as the ouput driver via the command line +

    + +

    + Libkml from Google provides reading services for any valid KML file. + However, please be advised that some KML facilities do not map into the + Simple Features specification OGR uses as its internal structure. + Therefore, a best effort will be made by the driver to understand the + content of a KML file read by libkml into ogr, but your mileage may vary. + Please try a few KML files as samples to get a sense of what is understood. + In particular, nesting of feature sets more than one deep will be flattened + to support ogr's internal format. +

    + +

    Datasource

    + +

    + You may specify a + datasource + as a kml file somefile.kml , + a directory somedir/ , or a kmz file somefile.kmz . +

    + +

    + By default on directory and kmz datasources, an index file of all the + layers will be read from or written to doc.kml. It contains a + + <NetworkLink> to each layer file in the datasource. This feature + can be turned off by setting the enviroment variable LIBKML_USE_DOC.KML to + "no" +

    + +

    StyleTable

    + +

    + Datasource style tables are written to the + + <Document> in a .kml, style/style.kml + in a kmz file, or style.kml in a directory, as one or more + + <Style> elements. Not all of + OGR Feature Style + can translate into KML. +

    + +

    Datasource creation options

    + +

    + Starting with OGR 1.11, the following datasource creation options can be used to generate a + + <atom:Author> element at the top Document level. +

      +
    • AUTHOR_NAME
    • +
    • AUTHOR_URI
    • +
    • AUTHOR_EMAIL
    • +
    +

    +

    + The href of an + <atom:link> element at the top Document level can be specified with the LINK creation option. +

    +

    + The + <phoneNumber> element at the top Document level can be specified with the PHONENUMBER creation option. + The value must follow the syntax of IETF RFC 3966. +

    + + +

    Container properties

    + +

    The following dataset creation options can be used to set container options : +

    +

    + +

    List style

    + +

    + The following dataset creation options can be used to control how the main folder + (folder of layers) appear in the Places panel of the Earth browser, trough a + <ListStyle> element: +

      +
    • LISTSTYLE_TYPE: can be one of "check", "radioFolder", "checkOffOnly" or "checkHideChildren". Sets the + <listItemType> element.
    • +
    • LISTSTYLE_ICON_HREF: URL of the icon to display for the main folder. Sets the href element of the + <ItemIcon> element.
    • +
    +

    + +

    Balloon style

    + +

    If a style foo is defined, it is possible to add a + <BalloonStyle> element to it, + by specifying the foo_BALLOONSTYLE_BGCOLOR and/or foo_BALLOONSTYLE_TEXT elements. +

    + +

    NetworkLinkControl

    + +

    + A + <NetworkLinkControl> element can be defined if at least one of the following dataset creation option + is specified: +

    +

    + +

    Update documents

    + +

    + When defining the dataset creation option UPDATE_TARGETHREF, a NetworkLinkControl KML + file with an + <Update> element will be generated. + See the tutorial about update.

    + + The CreateFeature() operation on a layer will be translated as a + <Create> element.

    + + The SetFeature() operation on a layer will be translated as a + <Change> element.

    + + The DeleteFeature() operation on a layer will be translated as a + <Delete> element.

    +

    + +

    Layer

    + +

    + + Layers are mapped to kml files as a + + <Document> + or + + <Folder>, and in kmz files or directorys as a seperate kml file. +

    + +

    Style

    +

    + Layer style tables can not be read from or written to a kml layer that is a + + <Folder>, otherwise they are in the + + <Document> that is the layer. +

    + +

    Schema

    + +

    + Read and write of + + <Schema> is supported for .kml files, .kmz files, and + directories. +

    + +

    Layer creation options

    + +

    + Starting with OGR 1.11, the following layer creation options can be used to generate a + + <LookAt> element at the layer level. +

      +
    • LOOKAT_LONGITUDE (required)
    • +
    • LOOKAT_LATITUDE (required)
    • +
    • LOOKAT_RANGE (required)
    • +
    • LOOKAT_HEADING
    • +
    • LOOKAT_TILT
    • +
    • LOOKAT_ALTITUDE
    • +
    • LOOKAT_ALTITUDEMODE
    • +
    +

    + +

    Alternatively, a + <Camera> element can be generated. +

      +
    • CAMERA_LONGITUDE (required)
    • +
    • CAMERA_LATITUDE (required)
    • +
    • CAMERA_ALTITUDE (required)
    • +
    • CAMERA_ALTITUDEMODE (required)
    • +
    • CAMERA_HEADING
    • +
    • CAMERA_TILT
    • +
    • CAMERA_ROLL
    • +
    +

    + +

    A + <Region> element can be generated to control when objects of the layer are visible or not. + If REGION_XMIN, REGION_YMIN, REGION_XMAX and REGION_YMAX, the region coordinates are determined from + the spatial extent of the features being written in the layer. +

      +
    • ADD_REGION=YES/NO : defaults to NO
    • +
    • REGION_XMIN (optional) : defines the west coordinate of the region.
    • +
    • REGION_YMIN (optional) : defines the south coordinate of the region.
    • +
    • REGION_XMAX (optional) : defines the east coordinate of the region.
    • +
    • REGION_YMAX (optional) : defines the north coordinate of the region.
    • +
    • REGION_MIN_LOD_PIXELS (optional) : minimum size in pixels of the region so that it is displayed. Defaults to 256.
    • +
    • REGION_MAX_LOD_PIXELS (optional) : maximum size in pixels of the region so that it is displayed. Defaults to -1 (infinite).
    • +
    • REGION_MIN_FADE_EXTENT (optional) : distance over which the geometry fades, from fully opaque to fully transparent. Defaults to 0.
    • +
    • REGION_MAX_FADE_EXTENT (optional) : distance over which the geometry fades, from fully transparent to fully opaque. Defaults to 0.
    • +
    +

    + +

    A + <ScreenOverlay> element can be added to display a logo, a legend, etc... +

      +
    • SO_HREF (required) : URL of the image to display.
    • +
    • SO_NAME (optional)
    • +
    • SO_DESCRIPTION (optional)
    • +
    • SO_OVERLAY_X (optional)
    • +
    • SO_OVERLAY_Y (optional)
    • +
    • SO_OVERLAY_XUNITS (optional)
    • +
    • SO_OVERLAY_YUNITS (optional)
    • +
    • SO_SCREEN_X (optional). Defaults to 0.05
    • +
    • SO_SCREEN_Y (optional). Defaults to 0.05
    • +
    • SO_SCREEN_XUNITS (optional). Defaults to Fraction
    • +
    • SO_SCREEN_YUNITS (optional). Defaults to Fraction
    • +
    • SO_SIZE_X (optional)
    • +
    • SO_SIZE_Y (optional)
    • +
    • SO_SIZE_XUNITS (optional)
    • +
    • SO_SIZE_YUNITS (optional)
    • +
    +

    + +

    By default, layers are written as + + <Document> elements. By settings the FOLDER layer creation option + to YES, it is also possible to write them as + <Folder> elements (only in .kml files). +

    + +

    The following layer creation options can be used to set container options : +

    +

    + +

    + The following layer creation options can be used to control how the folder + of a layer appear in the Places panel of the Earth browser, trough a + <ListStyle> element: +

      +
    • LISTSTYLE_TYPE: can be one of "check", "radioFolder", "checkOffOnly" or "checkHideChildren". Sets the + <listItemType> element.
    • +
    • LISTSTYLE_ICON_HREF: URL of the icon to display for the layer folder. Sets the href element of the + <ItemIcon> element.
    • +
    +

    + +

    Feature

    + +

    + An OGR feature + generally translates to kml as a + + <Placemark>, and vice-versa. +

    + +

    If the model field is defined, a + <Model> object within the Placemark will be generated. +

    + +

    If the networklink field is defined, a + <NetworkLink> will be generated. Other networklink_ fields are optional. +

    + +

    If the photooverlay field is defined, a + <PhotoOverlay> will be generated (provided that the camera_longitude, + camera_latitude, camera_altitude, camera_altitudemode, head and/or tilt and/or roll, + leftfov, rightfov, bottomfov, topfov, near fields are also set. The shape field is optional. +

    +

    In case the PhotoOverlay is a big image, it is highly recommended to tile it and + generate overview levels, as explained in the + PhotoOverlay tutorial. In which case, the URL should contain the "$[level]", "$[x]" and "$[y]" + sub-strings in the photooverlay field, and the imagepyramid_tilesize, + imagepyramid_maxwidth, imagepyramid_maxheight and imagepyramid_gridorigin fields should be set. +

    + +

    Placemark, Model, NetworkLink and PhotoOverlay objects can have an associated camera + if the camera_longitude, camera_latitude, camera_altitude, camera_altitudemode, head and/or tilt and/or roll + fields are defined. +

    + +

    + Starting with OGR 1.10, KML + + <GroundOverlay> elements are supported for reading (unless the + LIBKML_READ_GROUND_OVERLAY configuration option is set to FALSE). For such elements, there + are icon and drawOrder fields. +

    + +

    Style

    + +

    + Style Strings at the feature level are Mapped to KML as either a + + <Style> or + + <StyleUrl> in each + + <Placemark>. +

    +

    + When reading a kml feature and the enviroment variable LIBKML_RESOLVE_STYLE + is set to yes, styleurls are looked up in the style tables and the features + style string is set to the style from the table. This is to allow reading + of shared styles by applications, like mapserver, that do not read style + tables. +

    + +

    + When reading a kml feature and the enviroment variable LIBKML_EXTERNAL_STYLE + is set to yes, a styleurl that is external to the datasource is read from + disk or fetched from the server and parsed into the datasource style table. + If the style kml can not be read or LIBKML_EXTERNAL_STYLE is set to no then + the styleurl is copyed to the style string. +

    + +

    + When reading a kml StyleMap the default mapping is set to normal. If you + wish to use the highlighted styles set the enviroment variable + LIBKML_STYLEMAP_KEY to "highlight" +

    + +

    + When writing a kml, if there exist 2 styles of the form "astylename_normal" + and "astylename_highlight" (where astylename is any string), then a StyleMap + object will be creating from both styles and called "astylename". +

    + +

    Fields

    + +

    OGR fields (feature attributes) are mapped to kml with + + <Schema>; and + + <SimpleData>, except for some special fields as noted below. +

    + +

    Note: it is also possible to export fields as + + <Data> elements if the LIBKML_USE_SCHEMADATA configuration option is + set to NO. +

    + +

    + A rich set of environment variables are available to define how fields in + input and output, map to a KML + + <Placemark>. For example, if you want a field called 'Cities' + to map to the + + <name>; tag in KML, you can set an + environment variable. +

    + +
    +
    Name
    +
    + This String field maps to the kml tag + + <name>. The name of the ogr field can be changed with the + enviroment variable LIBKML_NAME_FIELD . +
    +
    description
    +
    This String field maps to the kml tag + + <description>. The name of the ogr field can be changed with the + enviroment variable LIBKML_DESCRIPTION_FIELD . +
    +
    timestamp
    +
    This string or datetime or date and/or time field maps to the kml tag + + <timestamp>. The name of the ogr field can be changed with the + enviroment variable LIBKML_TIMESTAMP_FIELD . +
    +
    begin
    +
    This string or datetime or date and/or time field maps to the kml tag + + <begin>. The name of the ogr field can be changed with the + enviroment variable LIBKML_BEGIN_FIELD . +
    +
    end
    +
    This string or datetime or date and/or time field maps to the kml tag + + <end>. The name of the ogr field can be changed with the + enviroment variable LIBKML_END_FIELD . +
    +
    altitudeMode
    +
    This string field maps to the kml tag + + <altitudeMode> or + + <gx:altitudeMode>. The name of the ogr field can be changed + with the enviroment variable LIBKML_ALTITUDEMODE_FIELD . +
    +
    tessellate
    +
    This integer field maps to the kml tag + + <tessellate>. The name of the ogr field can be changed with the + enviroment variable LIBKML_TESSELLATE_FIELD . +
    +
    extrude
    +
    This integer field maps to the kml tag + + <extrude>. The name of the ogr field can be changed with the + enviroment variable LIBKML_EXTRUDE_FIELD . +
    +
    visibility
    +
    This integer field maps to the kml tag + + <visibility>. The name of the ogr field can be changed with the + enviroment variable LIBKML_VISIBILITY_FIELD . +
    +
    icon
    +
    This string field maps to the kml tag + + <icon>. The name of the ogr field can be changed with the + enviroment variable LIBKML_ICON_FIELD . +
    +
    drawOrder
    +
    This integer field maps to the kml tag + + <drawOrder>. The name of the ogr field can be changed with the + enviroment variable LIBKML_DRAWORDER_FIELD . +
    +
    snippet
    +
    This integer field maps to the kml tag + + <snippet>. The name of the ogr field can be changed with the + enviroment variable LIBKML_SNIPPET_FIELD . +
    +
    heading
    +
    This real field maps to the kml tag + + <heading>. The name of the ogr field can be changed with the + enviroment variable LIBKML_HEADING_FIELD. When reading, this field is present only if a Placemark has + a Camera with a heading element. +
    +
    tilt
    +
    This real field maps to the kml tag + + <tilt>. The name of the ogr field can be changed with the + enviroment variable LIBKML_TILT_FIELD. When reading, this field is present only if a Placemark has + a Camera with a tilt element. +
    +
    roll
    +
    This real field maps to the kml tag + + <roll>. The name of the ogr field can be changed with the + enviroment variable LIBKML_ROLL_FIELD. When reading, this field is present only if a Placemark has + a Camera with a roll element. +
    + +
    model
    +
    This string field can be used to define the URL of a 3D + + <model>. The name of the ogr field can be changed with the + enviroment variable LIBKML_MODEL_FIELD. +
    +
    scale_x
    +
    This real field maps to the x element of the kml tag + + <scale> for a 3D model. The name of the ogr field can be changed with the + enviroment variable LIBKML_SCALE_X_FIELD. +
    +
    scale_y
    +
    This real field maps to the y element of the kml tag + + <scale>for a 3D model. The name of the ogr field can be changed with the + enviroment variable LIBKML_SCALE_Y_FIELD. +
    +
    scale_z
    +
    This real field maps to the z element of the kml tag + + <scale>for a 3D model. The name of the ogr field can be changed with the + enviroment variable LIBKML_SCALE_Z_FIELD. +
    + +
    networklink
    +
    This string field maps to the href element of the kml tag + + <href> of a NetworkLink. The name of the ogr field can be changed with the + enviroment variable LIBKML_NETWORKLINK_FIELD. +
    + +
    networklink_refreshvisibility
    +
    This integer field maps to kml tag + + <refreshVisibility> of a NetworkLink. The name of the ogr field can be changed with the + enviroment variable LIBKML_NETWORKLINK_REFRESHVISIBILITY_FIELD. +
    + +
    networklink_flytoview
    +
    This integer field maps to kml tag + + <flyToView> of a NetworkLink. The name of the ogr field can be changed with the + enviroment variable LIBKML_NETWORKLINK_FLYTOVIEW_FIELD. +
    + +
    networklink_refreshmode
    +
    This string field maps to kml tag + + <refreshMode> of a NetworkLink. The name of the ogr field can be changed with the + enviroment variable LIBKML_NETWORKLINK_REFRESHMODE_FIELD. +
    + +
    networklink_refreshinterval
    +
    This real field maps to kml tag + + <refreshInterval> of a NetworkLink. The name of the ogr field can be changed with the + enviroment variable LIBKML_NETWORKLINK_REFRESHINTERVAL_FIELD. +
    + +
    networklink_viewrefreshmode
    +
    This string field maps to kml tag + + <viewRefreshMode> of a NetworkLink. The name of the ogr field can be changed with the + enviroment variable LIBKML_NETWORKLINK_VIEWREFRESHMODE_FIELD. +
    + +
    networklink_viewrefreshtime
    +
    This real field maps to kml tag + + <viewRefreshTime> of a NetworkLink. The name of the ogr field can be changed with the + enviroment variable LIBKML_NETWORKLINK_VIEWREFRESHTIME_FIELD. +
    + +
    networklink_viewboundscale
    +
    This real field maps to kml tag + + <viewBoundScale> of a NetworkLink. The name of the ogr field can be changed with the + enviroment variable LIBKML_NETWORKLINK_VIEWBOUNDSCALE_FIELD. +
    + +
    networklink_viewformat
    +
    This string field maps to kml tag + + <viewFormat> of a NetworkLink. The name of the ogr field can be changed with the + enviroment variable LIBKML_NETWORKLINK_VIEWFORMAT_FIELD. +
    + +
    networklink_httpquery
    +
    This string field maps to kml tag + + <httpQuery> of a NetworkLink. The name of the ogr field can be changed with the + enviroment variable LIBKML_NETWORKLINK_HTTPQUERY_FIELD. +
    + +
    camera_longitude
    +
    This real field maps to kml tag + + <longitude> of a + <Camera>. The name of the ogr field can be changed with the + enviroment variable LIBKML_CACameraMERA_LONGITUDE_FIELD. +
    + +
    camera_latitude
    +
    This real field maps to kml tag + + <latitude> of a + <Camera>. The name of the ogr field can be changed with the + enviroment variable LIBKML_CAMERA_LATITUDE_FIELD. +
    + +
    camera_altitude
    +
    This real field maps to kml tag + + <altitude> of a + <Camera>. The name of the ogr field can be changed with the + enviroment variable LIBKML_CAMERA_ALTITUDE_FIELD. +
    + +
    camera_altitudemode
    +
    This real field maps to kml tag + + <altitudeMode> of a + <Camera>. The name of the ogr field can be changed with the + enviroment variable LIBKML_CAMERA_ALTITUDEMODE_FIELD. +
    + +
    photooverlay
    +
    This string field maps to the href element of the kml tag + + <href> of a + <PhotoOverlay>. The name of the ogr field can be changed with the + enviroment variable LIBKML_PHOTOOVERLAY_FIELD. +
    + +
    leftfov
    +
    This real field maps to to kml tag + + <LeftFov> of a + <PhotoOverlay>. The name of the ogr field can be changed with the + enviroment variable LIBKML_LEFTFOV_FIELD. +
    + +
    rightfov
    +
    This real field maps to to kml tag + + <RightFov> of a + <PhotoOverlay>. The name of the ogr field can be changed with the + enviroment variable LIBKML_RightFOV_FIELD. +
    + +
    bottomfov
    +
    This real field maps to to kml tag + + <BottomFov> of a + <PhotoOverlay>. The name of the ogr field can be changed with the + enviroment variable LIBKML_BOTTOMTFOV_FIELD. +
    + +
    topfov
    +
    This real field maps to to kml tag + + <TopFov> of a + <PhotoOverlay>. The name of the ogr field can be changed with the + enviroment variable LIBKML_TOPFOV_FIELD. +
    + +
    near
    +
    This real field maps to to kml tag + + <Near> of a + <PhotoOverlay>. The name of the ogr field can be changed with the + enviroment variable LIBKML_NEAR_FIELD. +
    + +
    shape
    +
    This string field maps to to kml tag + + <shape> of a + <PhotoOverlay>. The name of the ogr field can be changed with the + enviroment variable LIBKML_SHAPE_FIELD. +
    + +
    imagepyramid_tilesize
    +
    This integer field maps to to kml tag + + <tileSize> of a + <ImagePyramid>. The name of the ogr field can be changed with the + enviroment variable LIBKML_IMAGEPYRAMID_TILESIZE. +
    + +
    imagepyramid_maxwidth
    +
    This integer field maps to to kml tag + + <maxWidth> of a + <ImagePyramid>. The name of the ogr field can be changed with the + enviroment variable LIBKML_IMAGEPYRAMID_MAXWIDTH. +
    + +
    imagepyramid_maxheight
    +
    This integer field maps to to kml tag + + <maxHeight> of a + <ImagePyramid>. The name of the ogr field can be changed with the + enviroment variable LIBKML_IMAGEPYRAMID_MAXHEIGHT. +
    + +
    imagepyramid_gridorigin
    +
    This string field maps to to kml tag + + <gridOrigin> of a + <ImagePyramid>. The name of the ogr field can be changed with the + enviroment variable LIBKML_IMAGEPYRAMID_GRIDORIGIN. +
    + +
    OGR_STYLE
    +
    This string field maps to a features style string, OGR reads this field + if there is no style string set on the feature. +
    +
    + +

    Geometry

    + +

    + Translation of OGR + + Geometry to KML Geometry is pretty strait forwards with only a couple + of exceptions. Point to + + <Point> (unless heading and/or tilt and/or roll field names are found, in + which case a Camera object will be generated), LineString to + + <LineString>, LinearRing to + + <LinearRing>, and Polygon to + + <Polygon>. In OGR a polygon contains an array of LinearRings, + the first one being the outer ring. KML has the tags +   + <outerBoundaryIs> and  + + <innerBoundaryIs> to differentiate between the two. OGR has + several Multi types of geometry : GeometryCollection, MultiPolygon, + MultiPoint, and MultiLineString. When possible, OGR will try to map + + <MultiGeometry> to the more precise OGR geometry type (MultiPoint, MultiLineString or MultiPolygon), + and default to GeometryCollection in case of mixed content. +

    + +

    + Sometimes kml geometry will span the dateline, In applications like qgis or + mapserver this will create horizontal lines all the way around the globe. + Setting the enviroment variable LIBKML_WRAPDATELINE to "yes" will cause the + libkml driver to split the geometry at the dateline when read. +

    + +

    VSI Virtual File System API support

    + +(Some features below might require OGR >= 1.9.0)

    + +The driver supports reading and writing to files managed by VSI Virtual File System API, which include +"regular" files, as well as files in the /vsizip/ (read-write) , /vsigzip/ (read-write) , /vsicurl/ (read-only) domains.

    + +Writing to /dev/stdout or /vsistdout/ is also supported.

    + +

    Example

    + +

    + The following bash script will build a + csv file and a + vrt file, and then + translate them to KML using + ogr2ogr into a .kml file with + timestamps and styling. +

    + +
    + 
    + 
    +#!/bin/bash
    +# Copyright (c) 2010, Brian Case
    + * Copyright (c) 2010-2014, Even Rouault 
    +#
    +# Permission is hereby granted, free of charge, to any person obtaining a
    +# copy of this software and associated documentation files (the "Software"),
    +# to deal in the Software without restriction, including without limitation
    +# the rights to use, copy, modify, merge, publish, distribute, sublicense,
    +# and/or sell copies of the Software, and to permit persons to whom the
    +# Software is furnished to do so, subject to the following conditions:
    +#
    +# The above copyright notice and this permission notice shall be included
    +# in all copies or substantial portions of the Software.
    +#
    +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
    +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    +# DEALINGS IN THE SOFTWARE.
    + 
    + 
    +icon="http://maps.google.com/mapfiles/kml/shapes/shaded_dot.png"
    +rgba33="#FF9900"
    +rgba70="#FFFF00"
    +rgba150="#00FF00"
    +rgba300="#0000FF"
    +rgba500="#9900FF"
    +rgba800="#FF0000"
    + 
    +function docsv {
    + 
    +    IFS=','
    +    
    +    while read Date Time Lat Lon Mag Dep
    +    do
    +        ts=$(echo $Date | sed 's:/:-:g')T${Time%%.*}Z
    +        rgba=""
    +        
    +        if [[ $rgba == "" ]] && [[ $Dep -lt 33 ]]
    +        then
    +            rgba=$rgba33
    +        fi
    +        
    +        if [[ $rgba == "" ]] && [[ $Dep -lt 70 ]]
    +        then
    +            rgba=$rgba70
    +        fi
    +        
    +        if [[ $rgba == "" ]] && [[ $Dep -lt 150 ]]
    +        then
    +            rgba=$rgba150
    +        fi
    +        
    +        if [[ $rgba == "" ]] && [[ $Dep -lt 300 ]]
    +        then
    +            rgba=$rgba300
    +        fi
    +        
    +        if [[ $rgba == "" ]] && [[ $Dep -lt 500 ]]
    +        then
    +            rgba=$rgba500
    +        fi
    +        
    +        if [[ $rgba == "" ]]
    +        then
    +            rgba=$rgba800
    +        fi
    +        
    +        
    +        
    +        style="\"SYMBOL(s:$Mag,id:\"\"$icon\"\",c:$rgba)\""
    +        
    +        echo $Date,$Time,$Lat,$Lon,$Mag,$Dep,$ts,"$style"
    +    done
    +        
    +}
    + 
    + 
    +wget http://neic.usgs.gov/neis/gis/qed.asc -O /dev/stdout |\
    + tail -n +2 > qed.asc
    + 
    +echo Date,TimeUTC,Latitude,Longitude,Magnitude,Depth,timestamp,OGR_STYLE > qed.csv
    + 
    +docsv < qed.asc >> qed.csv
    + 
    +cat > qed.vrt << EOF
    +<OGRVRTDataSource>
    +    <OGRVRTLayer name="qed">
    +        <SrcDataSource>qed.csv</SrcDataSource>
    +        <GeometryType>wkbPoint</GeometryType>
    +        <LayerSRS>WGS84</LayerSRS>
    +        <GeometryField encoding="PointFromColumns" x="Longitude" y="Latitude"/>
    +    </OGRVRTLayer>
    +</OGRVRTDataSource>
    + 
    +EOF
    + 
    +ogr2ogr -f libkml qed.kml qed.vrt
    + 
    +  
    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/makefile.vc new file mode 100644 index 000000000..91a7aeab5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/makefile.vc @@ -0,0 +1,37 @@ +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +CORE_OBJ = ogrlibkmlgeometry.obj \ + ogrlibkmlfield.obj \ + ogrlibkmlfeature.obj \ + ogrlibkmlfeaturestyle.obj \ + ogrlibkmlstyle.obj + +PLUGIN_DLL = ogr_LIBKML.dll + +OGR_OBJ = ogrlibkmldriver.obj \ + ogrlibkmldatasource.obj \ + ogrlibkmllayer.obj + +OBJ = $(CORE_OBJ) $(OGR_OBJ) + +EXTRAFLAGS = -I.. -I..\.. $(LIBKML_INCLUDE) + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + -del *.dll + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) /LTCG \ + $(GDAL_ROOT)/gdal_i.lib $(LIBKML_LIBS) + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogr_libkml.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogr_libkml.h new file mode 100644 index 000000000..15fef92e5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogr_libkml.h @@ -0,0 +1,351 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#ifndef HAVE_OGR_LIBKML_H +#define HAVE_OGR_LIBKML_H + +#include "ogrsf_frmts.h" + +#include +#include + + +using kmldom::KmlFactory; +using kmldom::KmlPtr; +using kmldom::DocumentPtr; +using kmldom::ContainerPtr; +using kmldom::ElementPtr; +using kmldom::SchemaPtr; +using kmldom::UpdatePtr; +using kmlengine::KmzFile; +using kmlengine::KmzFilePtr; + +using kmlengine::KmlFile; +using kmlengine::KmlFilePtr; + +class OGRLIBKMLDataSource; + +CPLString OGRLIBKMLGetSanitizedNCName(const char* pszName); + +/****************************************************************************** + layer class +******************************************************************************/ + +class OGRLIBKMLLayer:public OGRLayer +{ + int bUpdate; + int bUpdated; + int nFeatures; + int iFeature; + long nFID; + const char *m_pszName; + const char *m_pszFileName; + + ContainerPtr m_poKmlLayer; + ElementPtr m_poKmlLayerRoot; + UpdatePtr m_poKmlUpdate; + + DocumentPtr m_poKmlDocument; + //OGRStyleTable *m_poStyleTable; + OGRLIBKMLDataSource *m_poOgrDS; + OGRFeatureDefn *m_poOgrFeatureDefn; + SchemaPtr m_poKmlSchema; + OGRSpatialReference *m_poOgrSRS; + + int m_bReadGroundOverlay; + int m_bUseSimpleField; + + int m_bWriteRegion; + int m_bRegionBoundsAuto; + double m_dfRegionMinLodPixels; + double m_dfRegionMaxLodPixels; + double m_dfRegionMinFadeExtent; + double m_dfRegionMaxFadeExtent; + double m_dfRegionMinX; + double m_dfRegionMinY; + double m_dfRegionMaxX; + double m_dfRegionMaxY; + + CPLString osListStyleType; + CPLString osListStyleIconHref; + + int m_bUpdateIsFolder; + + public: + OGRLIBKMLLayer ( const char *pszLayerName, + OGRSpatialReference * poSpatialRef, + OGRwkbGeometryType eGType, + OGRLIBKMLDataSource *poOgrDS, + ElementPtr poKmlRoot, + ContainerPtr poKmlContainer, + UpdatePtr poKmlUpdate, + const char *pszFileName, + int bNew, + int bUpdate); + ~OGRLIBKMLLayer ( ); + + void ResetReading ( ) { iFeature = 0; nFID = 1; }; + OGRFeature *GetNextFeature ( ); + OGRFeature *GetNextRawFeature ( ); + OGRFeatureDefn *GetLayerDefn ( ) { return m_poOgrFeatureDefn; }; + //OGRErr SetAttributeFilter (const char * ); + OGRErr ICreateFeature( OGRFeature * poOgrFeat ); + OGRErr ISetFeature( OGRFeature * poOgrFeat ); + OGRErr DeleteFeature( GIntBig nFID ); + + GIntBig GetFeatureCount ( int bForce = TRUE ); + OGRErr GetExtent ( OGREnvelope * psExtent, + int bForce = TRUE ); + + + //const char *GetInfo ( const char * ); + + OGRErr CreateField ( OGRFieldDefn * poField, + int bApproxOK = TRUE ); + + OGRErr SyncToDisk ( ); + + OGRStyleTable *GetStyleTable ( ); + void SetStyleTableDirectly ( OGRStyleTable * poStyleTable ); + void SetStyleTable ( OGRStyleTable * poStyleTable ); + const char *GetName( ) { return m_pszName; }; + int TestCapability ( const char * ); + ContainerPtr GetKmlLayer () { return m_poKmlLayer; }; + ElementPtr GetKmlLayerRoot () { return m_poKmlLayerRoot; }; + SchemaPtr GetKmlSchema () { return m_poKmlSchema; }; + const char *GetFileName ( ) { return m_pszFileName; }; + + void SetLookAt(const char* pszLookatLongitude, + const char* pszLookatLatitude, + const char* pszLookatAltitude, + const char* pszLookatHeading, + const char* pszLookatTilt, + const char* pszLookatRange, + const char* pszLookatAltitudeMode); + void SetCamera(const char* pszCameraLongitude, + const char* pszCameraLatitude, + const char* pszCameraAltitude, + const char* pszCameraHeading, + const char* pszCameraTilt, + const char* pszCameraRoll, + const char* pszCameraAltitudeMode); + + static CPLString LaunderFieldNames(CPLString osName); + + void SetWriteRegion(double dfMinLodPixels, + double dfMaxLodPixels, + double dfMinFadeExtent, + double dfMaxFadeExtent); + void SetRegionBounds(double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY); + + void SetScreenOverlay(const char* pszSOHref, + const char* pszSOName, + const char* pszSODescription, + const char* pszSOOverlayX, + const char* pszSOOverlayY, + const char* pszSOOverlayXUnits, + const char* pszSOOverlayYUnits, + const char* pszSOScreenX, + const char* pszSOScreenY, + const char* pszSOScreenXUnits, + const char* pszSOScreenYUnits, + const char* pszSOSizeX, + const char* pszSOSizeY, + const char* pszSOSizeXUnits, + const char* pszSOSizeYUnits); + + void SetListStyle(const char* pszListStyleType, + const char* pszListStyleIconHref); + + void Finalize(DocumentPtr poKmlDocument); + void SetUpdateIsFolder(int bUpdateIsFolder) { m_bUpdateIsFolder = bUpdateIsFolder; } +}; + +/****************************************************************************** + datasource class +******************************************************************************/ + +class OGRLIBKMLDataSource:public OGRDataSource +{ + char *pszName; + + /***** layers *****/ + + OGRLIBKMLLayer **papoLayers; + int nLayers; + int nAlloced; + + + int bUpdate; + int bUpdated; + CPLString osUpdateTargetHref; + + char **m_papszOptions; + + /***** for kml files *****/ + int m_isKml; + KmlPtr m_poKmlDSKml; + ContainerPtr m_poKmlDSContainer; + UpdatePtr m_poKmlUpdate; + + /***** for kmz files *****/ + + int m_isKmz; + ContainerPtr m_poKmlDocKml; + ElementPtr m_poKmlDocKmlRoot; + ContainerPtr m_poKmlStyleKml; + char *pszStylePath; + + /***** for dir *****/ + + int m_isDir; + + /***** the kml factory *****/ + + KmlFactory *m_poKmlFactory; + + /***** style table pointer *****/ + + void SetCommonOptions(ContainerPtr poKmlContainer, + char** papszOptions); + + void ParseDocumentOptions(KmlPtr poKml, + DocumentPtr poKmlDocument); + + public: + OGRLIBKMLDataSource ( KmlFactory *poKmlFactory ); + ~OGRLIBKMLDataSource ( ); + + const char *GetName ( ) { return pszName; }; + + int GetLayerCount ( ) { return nLayers; } + OGRLayer *GetLayer ( int ); + OGRLayer *GetLayerByName ( const char * ); + OGRErr DeleteLayer ( int ); + + + OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference * poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char **papszOptions = NULL ); + + OGRStyleTable *GetStyleTable ( ); + void SetStyleTableDirectly ( OGRStyleTable * poStyleTable ); + void SetStyleTable ( OGRStyleTable * poStyleTable ); + + int Open ( const char *pszFilename, + int bUpdate ); + int Create ( const char *pszFilename, + char **papszOptions ); + + void FlushCache ( ); + int TestCapability (const char * ); + + KmlFactory *GetKmlFactory() { return m_poKmlFactory; }; + + const char *GetStylePath() {return pszStylePath; }; + int ParseIntoStyleTable ( std::string * oKmlStyleKml, + const char *pszStylePath); + + //KmzFile *GetKmz() { return m_poKmlKmzfile; }; + + int IsKml() {return m_isKml;}; + int IsKmz() {return m_isKmz;}; + int IsDir() {return m_isDir;}; + + void Updated() {bUpdated = TRUE;}; + + int ParseLayers ( ContainerPtr poKmlContainer, + OGRSpatialReference *poOgrSRS ); + SchemaPtr FindSchema ( const char *pszSchemaUrl); + + private: + + /***** methods to write out various datasource types at destroy *****/ + + void WriteKml(); + void WriteKmz(); + void WriteDir(); + + /***** methods to open various datasource types *****/ + + int OpenKmz ( const char *pszFilename, + int bUpdate ); + int OpenKml ( const char *pszFilename, + int bUpdate ); + int OpenDir ( const char *pszFilename, + int bUpdate ); + + /***** methods to create various datasource types *****/ + + int CreateKml ( const char *pszFilename, + char **papszOptions ); + int CreateKmz ( const char *pszFilename, + char **papszOptions ); + int CreateDir ( const char *pszFilename, + char **papszOptions ); + + /***** methods to create layers on various datasource types *****/ + + OGRLIBKMLLayer *CreateLayerKml ( const char *pszLayerName, + OGRSpatialReference * poOgrSRS, + OGRwkbGeometryType eGType, + char **papszOptions ); + OGRLIBKMLLayer *CreateLayerKmz ( const char *pszLayerName, + OGRSpatialReference * poOgrSRS, + OGRwkbGeometryType eGType, + char **papszOptions ); + + /***** methods to delete layers on various datasource types *****/ + + OGRErr DeleteLayerKml ( int ); + OGRErr DeleteLayerKmz ( int ); + + /***** methods to write a styletable to various datasource types *****/ + + void SetStyleTable2Kml ( OGRStyleTable * poStyleTable ); + void SetStyleTable2Kmz ( OGRStyleTable * poStyleTable ); + + + + + OGRLIBKMLLayer *AddLayer ( const char *pszLayerName, + OGRSpatialReference * poSpatialRef, + OGRwkbGeometryType eGType, + OGRLIBKMLDataSource * poOgrDS, + ElementPtr poKmlRoot, + ContainerPtr poKmlContainer, + const char *pszFileName, + int bNew, + int bUpdate, + int nGuess); +}; + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmldatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmldatasource.cpp new file mode 100644 index 000000000..df3c186f5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmldatasource.cpp @@ -0,0 +1,2525 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +//#include "cpl_conv.h" +//#include "cpl_string.h" +//#include "cpl_error.h" +#include +//#include +#include +#include + +using kmldom::KmlFactory; +using kmldom::DocumentPtr; +using kmldom::FolderPtr; +using kmldom::FeaturePtr; +using kmldom::NetworkLinkPtr; +using kmldom::StyleSelectorPtr; +using kmldom::LinkPtr; +using kmldom::SchemaPtr; +using kmldom::NetworkLinkControlPtr; +using kmldom::LinkSnippetPtr; +using kmlbase::File; +using kmldom::KmlPtr; +using kmldom::SnippetPtr; +using kmlbase::Attributes; + +#include "ogr_libkml.h" +#include "ogrlibkmlstyle.h" +#include "ogr_p.h" + +/***** this was shamelessly swiped from the kml driver *****/ + +#define OGRLIBKMLSRSWKT "GEOGCS[\"WGS 84\", "\ + " DATUM[\"WGS_1984\","\ + " SPHEROID[\"WGS 84\",6378137,298.257223563,"\ + " AUTHORITY[\"EPSG\",\"7030\"]],"\ + " AUTHORITY[\"EPSG\",\"6326\"]],"\ + " PRIMEM[\"Greenwich\",0,"\ + " AUTHORITY[\"EPSG\",\"8901\"]],"\ + " UNIT[\"degree\",0.01745329251994328,"\ + " AUTHORITY[\"EPSG\",\"9122\"]],"\ + " AUTHORITY[\"EPSG\",\"4326\"]]" + +/****************************************************************************** + OGRLIBKMLDataSource Constructor + + Args: none + + Returns: nothing + +******************************************************************************/ + +OGRLIBKMLDataSource::OGRLIBKMLDataSource ( KmlFactory * poKmlFactory ) +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + nAlloced = 0; + m_papszOptions = NULL; + + bUpdated = FALSE; + + m_isKml = FALSE; + m_poKmlDSKml = NULL; + m_poKmlDSContainer = NULL; + m_poKmlUpdate = NULL; + + m_isKmz = FALSE; + m_poKmlDocKml = NULL; + pszStylePath = (char *) ""; + + m_isDir = FALSE; + + m_poKmlFactory = poKmlFactory; + + //m_poStyleTable = NULL; + +} + +/************************************************************************/ +/* OGRLIBKMLPreProcessInput() */ +/************************************************************************/ + +/* Substitute by deprecated since libkml currently */ +/* only supports Snippet but ogckml22.xsd has deprecated it in favor of snippet */ +static void OGRLIBKMLPreProcessInput(std::string& oKml) +{ + size_t nPos = 0; + while( TRUE ) + { + nPos = oKml.find("", nPos); + if( nPos == std::string::npos ) + { + break; + } + oKml[nPos+1] = 'S'; + nPos = oKml.find("", nPos); + if( nPos == std::string::npos ) + { + break; + } + oKml[nPos+2] = 'S'; + } +} + +/************************************************************************/ +/* OGRLIBKMLRemoveSpaces() */ +/************************************************************************/ + +static void OGRLIBKMLRemoveSpaces(std::string& oKml, const std::string& osNeedle) +{ + size_t nPos = 0; + while( TRUE ) + { + nPos = oKml.find("<" + osNeedle, nPos); + if( nPos == std::string::npos ) + { + break; + } + size_t nPosOri = nPos; + nPos = oKml.find(">", nPos); + if( nPos == std::string::npos || oKml[nPos+1] != '\n' ) + { + break; + } + oKml = oKml.substr(0, nPos) + ">" + oKml.substr(nPos + strlen(">\n")); + CPLString osSpaces; + for(size_t nPosTmp = nPosOri - 1; oKml[nPosTmp] == ' '; nPosTmp -- ) + { + osSpaces += ' '; + } + nPos = oKml.find(osSpaces + "", nPos); + if( nPos != std::string::npos ) + oKml = oKml.substr(0, nPos) + "" + oKml.substr(nPos + osSpaces.size() + strlen("") + osNeedle.size()); + else + break; + } +} + +/************************************************************************/ +/* OGRLIBKMLPostProcessOutput() */ +/************************************************************************/ + +/* Substitute deprecated by since libkml currently */ +/* only supports Snippet but ogckml22.xsd has deprecated it in favor of snippet */ +static void OGRLIBKMLPostProcessOutput(std::string& oKml) +{ + size_t nPos = 0; + + /* Manually add node since libkml does not produce it currently */ + /* and this is useful in some circumstances (#5407) */ + if( !(oKml[0] == '<' && oKml[1] == '?') ) + oKml = "\n" + oKml; + + while( TRUE ) + { + nPos = oKml.find("", nPos); + if( nPos == std::string::npos ) + { + break; + } + oKml[nPos+1] = 's'; + nPos = oKml.find("", nPos); + if( nPos == std::string::npos ) + { + break; + } + oKml[nPos+2] = 's'; + } + + /* Fix indentation problems */ + OGRLIBKMLRemoveSpaces(oKml, "snippet"); + OGRLIBKMLRemoveSpaces(oKml, "linkSnippet"); + OGRLIBKMLRemoveSpaces(oKml, "SimpleData"); +} + +/****************************************************************************** + method to write a single file ds .kml at ds destroy + + Args: none + + Returns: nothing + +******************************************************************************/ + +void OGRLIBKMLDataSource::WriteKml ( + ) +{ + std::string oKmlFilename = pszName; + + if ( m_poKmlDSContainer + && m_poKmlDSContainer->IsA ( kmldom::Type_Document ) ) { + DocumentPtr poKmlDocument = AsDocument ( m_poKmlDSContainer ); + int iLayer; + + ParseDocumentOptions(m_poKmlDSKml, poKmlDocument); + + for ( iLayer = 0; iLayer < nLayers; iLayer++ ) { + SchemaPtr poKmlSchema; + SchemaPtr poKmlSchema2; + + if ( ( poKmlSchema = papoLayers[iLayer]->GetKmlSchema ( ) ) ) { + size_t nKmlSchemas = poKmlDocument->get_schema_array_size ( ); + size_t iKmlSchema; + + for ( iKmlSchema = 0; iKmlSchema < nKmlSchemas; iKmlSchema++ ) { + poKmlSchema2 = + poKmlDocument->get_schema_array_at ( iKmlSchema ); + if ( poKmlSchema2 == poKmlSchema ) + break; + } + + if ( poKmlSchema2 != poKmlSchema ) + poKmlDocument->add_schema ( poKmlSchema ); + } + + papoLayers[iLayer]->Finalize(poKmlDocument); + } + } + else + { + ParseDocumentOptions(m_poKmlDSKml, NULL); + } + + std::string oKmlOut; + oKmlOut = kmldom::SerializePretty ( m_poKmlDSKml ); + OGRLIBKMLPostProcessOutput(oKmlOut); + + if (oKmlOut.size() != 0) + { + VSILFILE* fp = VSIFOpenL( oKmlFilename.c_str(), "wb" ); + if (fp == NULL) + { + CPLError ( CE_Failure, CPLE_FileIO, + "ERROR writing %s", oKmlFilename.c_str ( ) ); + return; + } + + VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp); + VSIFCloseL(fp); + } + + return; +} + +/******************************************************************************/ +/* OGRLIBKMLCreateOGCKml22() */ +/******************************************************************************/ + +static KmlPtr OGRLIBKMLCreateOGCKml22(KmlFactory* poFactory, + char** papszOptions = NULL) +{ + const char* pszAuthorName = CSLFetchNameValue(papszOptions, "AUTHOR_NAME"); + const char* pszAuthorURI = CSLFetchNameValue(papszOptions, "AUTHOR_URI"); + const char* pszAuthorEmail = CSLFetchNameValue(papszOptions, "AUTHOR_EMAIL"); + const char* pszLink = CSLFetchNameValue(papszOptions, "LINK"); + int bWithAtom = pszAuthorName != NULL || + pszAuthorURI != NULL || + pszAuthorEmail != NULL || + pszLink != NULL; + + KmlPtr kml = poFactory->CreateKml ( ); + if( bWithAtom ) + { + const char* kAttrs[] = { "xmlns", "http://www.opengis.net/kml/2.2", + "xmlns:atom", "http://www.w3.org/2005/Atom", NULL }; + kml->AddUnknownAttributes(Attributes::Create(kAttrs)); + } + else + { + const char* kAttrs[] = { "xmlns", "http://www.opengis.net/kml/2.2", NULL }; + kml->AddUnknownAttributes(Attributes::Create(kAttrs)); + } + return kml; +} + +/****************************************************************************** + method to write a ds .kmz at ds destroy + + Args: none + + Returns: nothing + +******************************************************************************/ + +void OGRLIBKMLDataSource::WriteKmz ( + ) +{ + + void* hZIP = CPLCreateZip( pszName, NULL ); + + if ( !hZIP ) { + CPLError ( CE_Failure, CPLE_NoWriteAccess, "ERROR creating %s", + pszName ); + return; + } + + /***** write out the doc.kml ****/ + + const char *pszUseDocKml = + CPLGetConfigOption ( "LIBKML_USE_DOC.KML", "yes" ); + + if ( CSLTestBoolean ( pszUseDocKml ) && (m_poKmlDocKml || m_poKmlUpdate) ) { + + /***** if we dont have the doc.kml root *****/ + /***** make it and add the container *****/ + + if ( !m_poKmlDocKmlRoot ) { + m_poKmlDocKmlRoot = OGRLIBKMLCreateOGCKml22(m_poKmlFactory, m_papszOptions); + + if( m_poKmlDocKml != NULL ) + { + AsKml( m_poKmlDocKmlRoot )->set_feature ( m_poKmlDocKml ); + } + + ParseDocumentOptions(AsKml( m_poKmlDocKmlRoot ), AsDocument(m_poKmlDocKml)); + } + + std::string oKmlOut = kmldom::SerializePretty ( m_poKmlDocKmlRoot ); + OGRLIBKMLPostProcessOutput(oKmlOut); + + if ( CPLCreateFileInZip( hZIP, "doc.kml", NULL ) != CE_None || + CPLWriteFileInZip( hZIP, oKmlOut.data(), oKmlOut.size() ) != CE_None ) + CPLError ( CE_Failure, CPLE_FileIO, + "ERROR adding %s to %s", "doc.kml", pszName ); + CPLCloseFileInZip(hZIP); + + } + + /***** loop though the layers and write them *****/ + + int iLayer; + + for ( iLayer = 0; iLayer < nLayers && m_poKmlUpdate == NULL; iLayer++ ) { + ContainerPtr poKmlContainer = papoLayers[iLayer]->GetKmlLayer ( ); + + if ( poKmlContainer->IsA ( kmldom::Type_Document ) ) { + + DocumentPtr poKmlDocument = AsDocument ( poKmlContainer ); + SchemaPtr poKmlSchema = papoLayers[iLayer]->GetKmlSchema ( ); + + if ( !poKmlDocument->get_schema_array_size ( ) && + poKmlSchema && + poKmlSchema->get_simplefield_array_size ( ) ) { + poKmlDocument->add_schema ( poKmlSchema ); + } + + papoLayers[iLayer]->Finalize(poKmlDocument); + } + + /***** if we dont have the layers root *****/ + /***** make it and add the container *****/ + + KmlPtr poKmlKml = NULL; + + if ( !( poKmlKml = AsKml( papoLayers[iLayer]->GetKmlLayerRoot ( ) ) ) ) { + + poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory); + + poKmlKml->set_feature ( poKmlContainer ); + } + + std::string oKmlOut = kmldom::SerializePretty ( poKmlKml ); + OGRLIBKMLPostProcessOutput(oKmlOut); + + if( iLayer == 0 && CSLTestBoolean ( pszUseDocKml ) ) + CPLCreateFileInZip( hZIP, "layers/", NULL ); + + const char* pszLayerFileName; + if( CSLTestBoolean ( pszUseDocKml ) ) + pszLayerFileName = CPLSPrintf("layers/%s", papoLayers[iLayer]->GetFileName ( )); + else + pszLayerFileName = papoLayers[iLayer]->GetFileName ( ); + + if ( CPLCreateFileInZip( hZIP, pszLayerFileName , NULL ) != CE_None || + CPLWriteFileInZip( hZIP, oKmlOut.data(), oKmlOut.size() ) != CE_None ) + CPLError ( CE_Failure, CPLE_FileIO, + "ERROR adding %s to %s", papoLayers[iLayer]->GetFileName ( ), pszName ); + CPLCloseFileInZip(hZIP); + + } + + /***** write the style table *****/ + + if ( m_poKmlStyleKml ) { + + KmlPtr poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory); + + poKmlKml->set_feature ( m_poKmlStyleKml ); + std::string oKmlOut = kmldom::SerializePretty ( poKmlKml ); + OGRLIBKMLPostProcessOutput(oKmlOut); + + if ( CPLCreateFileInZip( hZIP, "style/", NULL ) != CE_None || + CPLCreateFileInZip( hZIP, "style/style.kml", NULL ) != CE_None || + CPLWriteFileInZip( hZIP, oKmlOut.data(), oKmlOut.size() ) != CE_None ) + CPLError ( CE_Failure, CPLE_FileIO, + "ERROR adding %s to %s", "style/style.kml", pszName ); + CPLCloseFileInZip(hZIP); + } + + CPLCloseZip(hZIP); + + return; +} + +/****************************************************************************** + method to write a dir ds at ds destroy + + Args: none + + Returns: nothing + +******************************************************************************/ + +void OGRLIBKMLDataSource::WriteDir ( + ) +{ + + /***** write out the doc.kml ****/ + + const char *pszUseDocKml = + CPLGetConfigOption ( "LIBKML_USE_DOC.KML", "yes" ); + + if ( CSLTestBoolean ( pszUseDocKml ) && (m_poKmlDocKml || m_poKmlUpdate) ) { + + /***** if we dont have the doc.kml root *****/ + /***** make it and add the container *****/ + + if ( !m_poKmlDocKmlRoot ) { + m_poKmlDocKmlRoot = OGRLIBKMLCreateOGCKml22(m_poKmlFactory, m_papszOptions); + if( m_poKmlDocKml != NULL ) + AsKml( m_poKmlDocKmlRoot )->set_feature ( m_poKmlDocKml ); + + ParseDocumentOptions(AsKml( m_poKmlDocKmlRoot ), AsDocument(m_poKmlDocKml)); + } + + std::string oKmlOut = kmldom::SerializePretty ( m_poKmlDocKmlRoot ); + OGRLIBKMLPostProcessOutput(oKmlOut); + + const char *pszOutfile = CPLFormFilename ( pszName, "doc.kml", NULL ); + + VSILFILE* fp = VSIFOpenL( pszOutfile, "wb" ); + if (fp == NULL) + { + CPLError ( CE_Failure, CPLE_FileIO, + "ERROR Writing %s to %s", "doc.kml", pszName ); + return; + } + + VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp); + VSIFCloseL(fp); + } + + /***** loop though the layers and write them *****/ + + int iLayer; + + for ( iLayer = 0; iLayer < nLayers && m_poKmlUpdate == NULL; iLayer++ ) { + ContainerPtr poKmlContainer = papoLayers[iLayer]->GetKmlLayer ( ); + + if ( poKmlContainer->IsA ( kmldom::Type_Document ) ) { + + DocumentPtr poKmlDocument = AsDocument ( poKmlContainer ); + SchemaPtr poKmlSchema = papoLayers[iLayer]->GetKmlSchema ( ); + + if ( !poKmlDocument->get_schema_array_size ( ) && + poKmlSchema && + poKmlSchema->get_simplefield_array_size ( ) ) { + poKmlDocument->add_schema ( poKmlSchema ); + } + + papoLayers[iLayer]->Finalize(poKmlDocument); + } + + /***** if we dont have the layers root *****/ + /***** make it and add the container *****/ + + KmlPtr poKmlKml = NULL; + + if ( !( poKmlKml = AsKml( papoLayers[iLayer]->GetKmlLayerRoot ( ) ) ) ) { + + poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory); + + poKmlKml->set_feature ( poKmlContainer ); + } + + std::string oKmlOut = kmldom::SerializePretty ( poKmlKml ); + OGRLIBKMLPostProcessOutput(oKmlOut); + + const char *pszOutfile = CPLFormFilename ( pszName, + papoLayers[iLayer]-> + GetFileName ( ), + NULL ); + + VSILFILE* fp = VSIFOpenL( pszOutfile, "wb" ); + if (fp == NULL) + { + CPLError ( CE_Failure, CPLE_FileIO, + "ERROR Writing %s to %s", + papoLayers[iLayer]->GetFileName ( ), pszName ); + return; + } + + VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp); + VSIFCloseL(fp); + } + + /***** write the style table *****/ + + if ( m_poKmlStyleKml ) { + + KmlPtr poKmlKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory); + + poKmlKml->set_feature ( m_poKmlStyleKml ); + std::string oKmlOut = kmldom::SerializePretty ( poKmlKml ); + OGRLIBKMLPostProcessOutput(oKmlOut); + + const char *pszOutfile = CPLFormFilename ( pszName, + "style.kml", + NULL ); + + VSILFILE* fp = VSIFOpenL( pszOutfile, "wb" ); + if (fp == NULL) + { + CPLError ( CE_Failure, CPLE_FileIO, + "ERROR Writing %s to %s", "style.kml", pszName ); + return; + } + + VSIFWriteL(oKmlOut.data(), 1, oKmlOut.size(), fp); + VSIFCloseL(fp); + } + + return; +} + +/****************************************************************************** + method to write the datasource to disk + + Args: none + + Returns nothing + +******************************************************************************/ + +void OGRLIBKMLDataSource::FlushCache ( + ) +{ + + if ( bUpdated ) { + + /***** kml *****/ + + if ( bUpdate && IsKml ( ) ) + WriteKml ( ); + + /***** kmz *****/ + + else if ( bUpdate && IsKmz ( ) ) { + WriteKmz ( ); + } + + else if ( bUpdate && IsDir ( ) ) { + WriteDir ( ); + } + + bUpdated = FALSE; + } +} + +/****************************************************************************** + OGRLIBKMLDataSource Destructor + + Args: none + + Returns: nothing + +******************************************************************************/ + +OGRLIBKMLDataSource::~OGRLIBKMLDataSource ( ) +{ + + + /***** sync the DS to disk *****/ + + FlushCache ( ); + + CPLFree ( pszName ); + + if (! EQUAL(pszStylePath, "")) + CPLFree ( pszStylePath ); + + for ( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree ( papoLayers ); + + CSLDestroy( m_papszOptions ); + + //delete m_poStyleTable; + +} + + +/****************************************************************************** + method to parse a schemas out of a document + + Args: poKmlDocument pointer to the document to parse + + Returns: nothing + +******************************************************************************/ + +SchemaPtr OGRLIBKMLDataSource::FindSchema ( + const char *pszSchemaUrl ) +{ + char *pszID = NULL; + char *pszFile = NULL; + char *pszSchemaName = NULL; + char *pszPound; + DocumentPtr poKmlDocument = NULL; + SchemaPtr poKmlSchemaResult = NULL; + + if ( !pszSchemaUrl || !*pszSchemaUrl ) + return NULL; + + if ( *pszSchemaUrl == '#' ) { + pszID = CPLStrdup ( pszSchemaUrl + 1 ); + + /***** kml *****/ + + if ( IsKml ( ) && m_poKmlDSContainer->IsA ( kmldom::Type_Document ) ) + poKmlDocument = AsDocument ( m_poKmlDSContainer ); + + /***** kmz *****/ + + else if ( ( IsKmz ( ) || IsDir ( ) ) && m_poKmlDocKml + && m_poKmlDocKml->IsA ( kmldom::Type_Document ) ) + poKmlDocument = AsDocument ( m_poKmlDocKml ); + + } + + + else if ( ( pszPound = strchr ( (char *)pszSchemaUrl, '#' ) ) ) { + pszFile = CPLStrdup ( pszSchemaUrl ); + pszID = CPLStrdup ( pszPound + 1 ); + pszPound = strchr ( pszFile, '#' ); + *pszPound = '\0'; + } + + else { + pszSchemaName = CPLStrdup ( pszSchemaUrl ); + + /***** kml *****/ + + if ( IsKml ( ) && m_poKmlDSContainer->IsA ( kmldom::Type_Document ) ) + poKmlDocument = AsDocument ( m_poKmlDSContainer ); + + /***** kmz *****/ + + else if ( ( IsKmz ( ) || IsDir ( ) ) && m_poKmlDocKml + && m_poKmlDocKml->IsA ( kmldom::Type_Document ) ) + poKmlDocument = AsDocument ( m_poKmlDocKml ); + + } + + + if ( poKmlDocument) { + + size_t nKmlSchemas = poKmlDocument->get_schema_array_size ( ); + size_t iKmlSchema; + + for ( iKmlSchema = 0; iKmlSchema < nKmlSchemas; iKmlSchema++ ) { + SchemaPtr poKmlSchema = + poKmlDocument->get_schema_array_at ( iKmlSchema ); + if ( poKmlSchema->has_id ( ) && pszID) { + if ( EQUAL ( pszID, poKmlSchema->get_id ( ).c_str ( ) ) ) { + poKmlSchemaResult = poKmlSchema; + break; + } + } + + else if ( poKmlSchema->has_name ( ) && pszSchemaName) { + if ( EQUAL ( pszSchemaName, poKmlSchema->get_name ( ).c_str ( ) ) ) { + poKmlSchemaResult = poKmlSchema; + break; + } + } + + } + } + + if ( pszFile ) + CPLFree ( pszFile ); + if ( pszID ) + CPLFree ( pszID ); + if ( pszSchemaName ) + CPLFree ( pszSchemaName ); + + return poKmlSchemaResult; + +} + +/****************************************************************************** +Method to allocate memory for the layer array, create the layer, + and add it to the layer array + + Args: pszLayerName the name of the layer + poSpatialRef the spacial Refrance for the layer + eGType the layers geometry type + poOgrDS pointer to the datasource the layer is in + poKmlRoot pointer to the root kml element of the layer + pszFileName the filename of the layer + bNew true if its a new layer + bUpdate true if the layer is writeable + nGuess a guess at the number of additional layers + we are going to need + + Returns: Pointer to the new layer +******************************************************************************/ + +OGRLIBKMLLayer *OGRLIBKMLDataSource::AddLayer ( + const char *pszLayerName, + OGRSpatialReference * poSpatialRef, + OGRwkbGeometryType eGType, + OGRLIBKMLDataSource * poOgrDS, + ElementPtr poKmlRoot, + ContainerPtr poKmlContainer, + const char *pszFileName, + int bNew, + int bUpdate, + int nGuess ) +{ + + /***** check to see if we have enough space to store the layer *****/ + + if ( nLayers == nAlloced ) { + nAlloced += nGuess; + void *tmp = CPLRealloc ( papoLayers, + sizeof ( OGRLIBKMLLayer * ) * nAlloced ); + + papoLayers = ( OGRLIBKMLLayer ** ) tmp; + } + + /***** create the layer *****/ + + int iLayer = nLayers++; + + OGRLIBKMLLayer *poOgrLayer = new OGRLIBKMLLayer ( pszLayerName, + poSpatialRef, + eGType, + poOgrDS, + poKmlRoot, + poKmlContainer, + m_poKmlUpdate, + pszFileName, + bNew, + bUpdate ); + + /***** add the layer to the array *****/ + + papoLayers[iLayer] = poOgrLayer; + + return poOgrLayer; +} + +/****************************************************************************** + method to parse multiple layers out of a container + + Args: poKmlContainer pointer to the container to parse + poOgrSRS SRS to use when creating the layer + + Returns: number of features in the container that are not another + container + +******************************************************************************/ + +int OGRLIBKMLDataSource::ParseLayers ( + ContainerPtr poKmlContainer, + OGRSpatialReference * poOgrSRS ) +{ + int nResult = 0; + + /***** if container is null just bail now *****/ + + if ( !poKmlContainer ) + return nResult; + + size_t nKmlFeatures = poKmlContainer->get_feature_array_size ( ); + + /***** loop over the container to seperate the style, layers, etc *****/ + + size_t iKmlFeature; + + for ( iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++ ) { + FeaturePtr poKmlFeat = + poKmlContainer->get_feature_array_at ( iKmlFeature ); + + /***** container *****/ + + if ( poKmlFeat->IsA ( kmldom::Type_Container ) ) { + + /***** see if the container has a name *****/ + + std::string oKmlFeatName; + if ( poKmlFeat->has_name ( ) ) { + /* Strip leading and trailing spaces */ + const char* pszName = poKmlFeat->get_name ( ).c_str(); + while(*pszName == ' ' || *pszName == '\n' || *pszName == '\r' || *pszName == '\t' ) + pszName ++; + oKmlFeatName = pszName; + int nSize = (int)oKmlFeatName.size(); + while (nSize > 0 && + (oKmlFeatName[nSize-1] == ' ' || oKmlFeatName[nSize-1] == '\n' || + oKmlFeatName[nSize-1] == '\r' || oKmlFeatName[nSize-1] == '\t')) + { + nSize --; + oKmlFeatName.resize(nSize); + } + } + + /***** use the feature index number as the name *****/ + /***** not sure i like this c++ ich *****/ + + else { + std::stringstream oOut; + oOut << iKmlFeature; + oKmlFeatName = "Layer"; + oKmlFeatName.append(oOut.str ( )); + } + + /***** create the layer *****/ + + AddLayer ( oKmlFeatName.c_str ( ), + poOgrSRS, wkbUnknown, this, + NULL, AsContainer( poKmlFeat ), "", FALSE, bUpdate, nKmlFeatures ); + + } + + else + nResult++; + } + + return nResult; +} + +/****************************************************************************** + function to get the container from the kmlroot + + Args: poKmlRoot the root element + + Returns: root if its a container, if its a kml the container it + contains, or NULL + +******************************************************************************/ + +static ContainerPtr GetContainerFromRoot ( + KmlFactory *m_poKmlFactory, ElementPtr poKmlRoot ) +{ + ContainerPtr poKmlContainer = NULL; + + int bReadGroundOverlay = CSLTestBoolean(CPLGetConfigOption("LIBKML_READ_GROUND_OVERLAY", "YES")); + + if ( poKmlRoot ) { + + /***** skip over the we want the container *****/ + + if ( poKmlRoot->IsA ( kmldom::Type_kml ) ) { + + KmlPtr poKmlKml = AsKml ( poKmlRoot ); + + if ( poKmlKml->has_feature ( ) ) { + FeaturePtr poKmlFeat = poKmlKml->get_feature ( ); + + if ( poKmlFeat->IsA ( kmldom::Type_Container ) ) + poKmlContainer = AsContainer ( poKmlFeat ); + else if ( poKmlFeat->IsA ( kmldom::Type_Placemark ) || + (bReadGroundOverlay && poKmlFeat->IsA ( kmldom::Type_GroundOverlay )) ) + { + poKmlContainer = m_poKmlFactory->CreateDocument ( ); + poKmlContainer->add_feature ( kmldom::AsFeature(kmlengine::Clone(poKmlFeat)) ); + } + } + + } + + else if ( poKmlRoot->IsA ( kmldom::Type_Container ) ) + poKmlContainer = AsContainer ( poKmlRoot ); + } + + return poKmlContainer; +} + +/****************************************************************************** + method to parse a kml string into the style table +******************************************************************************/ + +int OGRLIBKMLDataSource::ParseIntoStyleTable ( + std::string *poKmlStyleKml, + const char *pszMyStylePath) +{ + + /***** parse the kml into the dom *****/ + + std::string oKmlErrors; + ElementPtr poKmlRoot = kmldom::Parse ( *poKmlStyleKml, &oKmlErrors ); + + if ( !poKmlRoot ) { + CPLError ( CE_Failure, CPLE_OpenFailed, + "ERROR Parseing style kml %s :%s", + pszStylePath, oKmlErrors.c_str ( ) ); + return false; + } + + ContainerPtr poKmlContainer; + + if ( !( poKmlContainer = GetContainerFromRoot ( m_poKmlFactory, poKmlRoot ) ) ) { + return false; + } + + ParseStyles ( AsDocument ( poKmlContainer ), &m_poStyleTable ); + pszStylePath = CPLStrdup(pszMyStylePath); + + + return true; +} + +/****************************************************************************** + method to open a kml file + + Args: pszFilename file to open + bUpdate update mode + + Returns: True on success, false on failure + +******************************************************************************/ + +int OGRLIBKMLDataSource::OpenKml ( + const char *pszFilename, + int bUpdate ) +{ + std::string oKmlKml; + char szBuffer[1024+1]; + + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + { + CPLError ( CE_Failure, CPLE_OpenFailed, + "Cannot open %s", pszFilename ); + return FALSE; + } + int nRead; + while ((nRead = VSIFReadL(szBuffer, 1, 1024, fp)) != 0) + { + try + { + oKmlKml.append(szBuffer, nRead); + } + catch(std::bad_alloc& e) + { + VSIFCloseL(fp); + return FALSE; + } + } + OGRLIBKMLPreProcessInput(oKmlKml); + VSIFCloseL(fp); + + CPLLocaleC oLocaleForcer; + + /***** create a SRS *****/ + + OGRSpatialReference *poOgrSRS = + new OGRSpatialReference ( OGRLIBKMLSRSWKT ); + + /***** parse the kml into the DOM *****/ + + std::string oKmlErrors; + + ElementPtr poKmlRoot = kmldom::Parse ( oKmlKml, &oKmlErrors ); + + if ( !poKmlRoot ) { + CPLError ( CE_Failure, CPLE_OpenFailed, + "ERROR Parseing kml %s :%s", + pszFilename, oKmlErrors.c_str ( ) ); + delete poOgrSRS; + + return FALSE; + } + + /***** get the container from root *****/ + + if ( !( m_poKmlDSContainer = GetContainerFromRoot ( m_poKmlFactory, poKmlRoot ) ) ) { + CPLError ( CE_Failure, CPLE_OpenFailed, + "ERROR Parseing kml %s :%s %s", + pszFilename, "This file does not fit the OGR model,", + "there is no container element at the root." ); + delete poOgrSRS; + + return FALSE; + } + + m_isKml = TRUE; + + /***** get the styles *****/ + + ParseStyles ( AsDocument ( m_poKmlDSContainer ), &m_poStyleTable ); + + /***** parse for layers *****/ + + int nPlacemarks = ParseLayers ( m_poKmlDSContainer, poOgrSRS ); + + /***** if there is placemarks in the root its a layer *****/ + + if ( nPlacemarks && !nLayers ) { + AddLayer ( CPLGetBasename ( pszFilename ), + poOgrSRS, wkbUnknown, + this, poKmlRoot, m_poKmlDSContainer, pszFilename, FALSE, bUpdate, 1 ); + } + + delete poOgrSRS; + + return TRUE; +} + +/****************************************************************************** + method to open a kmz file + + Args: pszFilename file to open + bUpdate update mode + + Returns: True on success, false on failure + +******************************************************************************/ + + +int OGRLIBKMLDataSource::OpenKmz ( + const char *pszFilename, + int bUpdate ) +{ + std::string oKmlKmz; + char szBuffer[1024+1]; + + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + { + CPLError ( CE_Failure, CPLE_OpenFailed, + "Cannot open %s", pszFilename ); + return FALSE; + } + int nRead; + while ((nRead = VSIFReadL(szBuffer, 1, 1024, fp)) != 0) + { + try + { + oKmlKmz.append(szBuffer, nRead); + } + catch(std::bad_alloc& e) + { + VSIFCloseL(fp); + return FALSE; + } + } + VSIFCloseL(fp); + + KmzFile *poKmlKmzfile = KmzFile::OpenFromString ( oKmlKmz ); + + if ( !poKmlKmzfile ) { + CPLError ( CE_Failure, CPLE_OpenFailed, + "%s is not a valid kmz file", pszFilename ); + return FALSE; + } + + CPLLocaleC oLocaleForcer; + + /***** read the doc.kml *****/ + + std::string oKmlKml; + std::string oKmlKmlPath; + if ( !poKmlKmzfile->ReadKmlAndGetPath ( &oKmlKml, &oKmlKmlPath ) ) { + + return FALSE; + } + + /***** create a SRS *****/ + + OGRSpatialReference *poOgrSRS = + new OGRSpatialReference ( OGRLIBKMLSRSWKT ); + + /***** parse the kml into the DOM *****/ + + std::string oKmlErrors; + ElementPtr poKmlDocKmlRoot = kmldom::Parse ( oKmlKml, &oKmlErrors ); + + if ( !poKmlDocKmlRoot ) { + CPLError ( CE_Failure, CPLE_OpenFailed, + "ERROR Parseing kml layer %s from %s :%s", + oKmlKmlPath.c_str ( ), + pszFilename, oKmlErrors.c_str ( ) ); + delete poOgrSRS; + + return FALSE; + } + + /***** get the child contianer from root *****/ + + ContainerPtr poKmlContainer; + + if (!(poKmlContainer = GetContainerFromRoot ( m_poKmlFactory, poKmlDocKmlRoot ))) { + CPLError ( CE_Failure, CPLE_OpenFailed, + "ERROR Parseing %s from %s :%s", + oKmlKmlPath.c_str ( ), + pszFilename, "kml contains no Containers" ); + delete poOgrSRS; + + return FALSE; + } + + /***** loop over the container looking for network links *****/ + + size_t nKmlFeatures = poKmlContainer->get_feature_array_size ( ); + size_t iKmlFeature; + int nLinks = 0; + + for ( iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++ ) { + FeaturePtr poKmlFeat = + poKmlContainer->get_feature_array_at ( iKmlFeature ); + + /***** is it a network link? *****/ + + if ( !poKmlFeat->IsA ( kmldom::Type_NetworkLink ) ) + continue; + + NetworkLinkPtr poKmlNetworkLink = AsNetworkLink ( poKmlFeat ); + + /***** does it have a link? *****/ + + if ( !poKmlNetworkLink->has_link ( ) ) + continue; + + LinkPtr poKmlLink = poKmlNetworkLink->get_link ( ); + + /***** does the link have a href? *****/ + + if ( !poKmlLink->has_href ( ) ) + continue; + + kmlengine::Href * poKmlHref = + new kmlengine::Href ( poKmlLink->get_href ( ) ); + + /***** is the link relative? *****/ + + if ( poKmlHref->IsRelativePath ( ) ) { + + nLinks++; + + std::string oKml; + if ( poKmlKmzfile-> + ReadFile ( poKmlHref->get_path ( ).c_str ( ), &oKml ) ) { + + /***** parse the kml into the DOM *****/ + + std::string oKmlErrors; + ElementPtr poKmlLyrRoot = kmldom::Parse ( oKml, &oKmlErrors ); + + if ( !poKmlLyrRoot ) { + CPLError ( CE_Failure, CPLE_OpenFailed, + "ERROR Parseing kml layer %s from %s :%s", + poKmlHref->get_path ( ).c_str ( ), + pszFilename, oKmlErrors.c_str ( ) ); + delete poKmlHref; + + continue; + } + + /***** get the container from root *****/ + + ContainerPtr poKmlLyrContainer = + GetContainerFromRoot ( m_poKmlFactory, poKmlLyrRoot ); + + if ( !poKmlLyrContainer ) + { + CPLError ( CE_Failure, CPLE_OpenFailed, + "ERROR Parseing kml layer %s from %s :%s", + poKmlHref->get_path ( ).c_str ( ), + pszFilename, oKmlErrors.c_str ( ) ); + delete poKmlHref; + + continue; + } + + /***** create the layer *****/ + + AddLayer ( CPLGetBasename + ( poKmlHref->get_path ( ).c_str ( ) ), poOgrSRS, + wkbUnknown, this, poKmlLyrRoot, poKmlLyrContainer, + poKmlHref->get_path ( ).c_str ( ), FALSE, bUpdate, + nKmlFeatures ); + + } + } + + /***** cleanup *****/ + + delete poKmlHref; + } + + /***** if the doc.kml has links store it so if were in update mode we can write it *****/ + + if ( nLinks ) { + m_poKmlDocKml = poKmlContainer; + m_poKmlDocKmlRoot = poKmlDocKmlRoot; + } + + /***** if the doc.kml has no links treat it as a normal kml file *****/ + + else { + + /* todo there could still be a seperate styles file in the kmz + if there is this would be a layer style table IF its only a single + layer + */ + + /***** get the styles *****/ + + ParseStyles ( AsDocument ( poKmlContainer ), &m_poStyleTable ); + + /***** parse for layers *****/ + + int nPlacemarks = ParseLayers ( poKmlContainer, poOgrSRS ); + + /***** if there is placemarks in the root its a layer *****/ + + if ( nPlacemarks && !nLayers ) { + AddLayer ( CPLGetBasename ( pszFilename ), + poOgrSRS, wkbUnknown, + this, poKmlDocKmlRoot, poKmlContainer, pszFilename, FALSE, bUpdate, 1 ); + } + } + + /***** read the style table if it has one *****/ + + std::string oKmlStyleKml; + if ( poKmlKmzfile->ReadFile ( "style/style.kml", &oKmlStyleKml ) ) + ParseIntoStyleTable ( &oKmlStyleKml, "style/style.kml"); + + /***** cleanup *****/ + + delete poOgrSRS; + + delete poKmlKmzfile; + m_isKmz = TRUE; + + return TRUE; +} + +/****************************************************************************** + method to open a dir + + Args: pszFilename Dir to open + bUpdate update mode + + Returns: True on success, false on failure + +******************************************************************************/ + +int OGRLIBKMLDataSource::OpenDir ( + const char *pszFilename, + int bUpdate ) +{ + + char **papszDirList = NULL; + + if ( !( papszDirList = VSIReadDir ( pszFilename ) ) ) + return FALSE; + + /***** create a SRS *****/ + + OGRSpatialReference *poOgrSRS = + new OGRSpatialReference ( OGRLIBKMLSRSWKT ); + + int nFiles = CSLCount ( papszDirList ); + int iFile; + + for ( iFile = 0; iFile < nFiles; iFile++ ) { + + /***** make sure its a .kml file *****/ + + if ( !EQUAL ( CPLGetExtension ( papszDirList[iFile] ), "kml" ) ) + continue; + + /***** read the file *****/ + std::string oKmlKml; + char szBuffer[1024+1]; + + CPLString osFilePath = + CPLFormFilename ( pszFilename, papszDirList[iFile], NULL ); + + VSILFILE* fp = VSIFOpenL(osFilePath, "rb"); + if (fp == NULL) + { + CPLError ( CE_Failure, CPLE_OpenFailed, + "Cannot open %s", osFilePath.c_str() ); + continue; + } + + int nRead; + while ((nRead = VSIFReadL(szBuffer, 1, 1024, fp)) != 0) + { + try + { + oKmlKml.append(szBuffer, nRead); + } + catch(std::bad_alloc& e) + { + VSIFCloseL(fp); + CSLDestroy ( papszDirList ); + return FALSE; + } + } + VSIFCloseL(fp); + + CPLLocaleC oLocaleForcer; + + /***** parse the kml into the DOM *****/ + + std::string oKmlErrors; + ElementPtr poKmlRoot = kmldom::Parse ( oKmlKml, &oKmlErrors ); + + if ( !poKmlRoot ) { + CPLError ( CE_Failure, CPLE_OpenFailed, + "ERROR Parseing kml layer %s from %s :%s", + osFilePath.c_str(), pszFilename, oKmlErrors.c_str ( ) ); + + continue; + } + + /***** get the cintainer from the root *****/ + + ContainerPtr poKmlContainer; + + if ( !( poKmlContainer = GetContainerFromRoot ( m_poKmlFactory, poKmlRoot ) ) ) { + CPLError ( CE_Failure, CPLE_OpenFailed, + "ERROR Parseing kml %s :%s %s", + pszFilename, + "This file does not fit the OGR model,", + "there is no container element at the root." ); + continue; + } + + /***** is it a style table? *****/ + + if ( EQUAL ( papszDirList[iFile], "style.kml" ) ) { + ParseStyles ( AsDocument ( poKmlContainer ), &m_poStyleTable ); + pszStylePath = CPLStrdup((char *) "style.kml"); + continue; + } + + + /***** create the layer *****/ + + AddLayer ( CPLGetBasename ( osFilePath.c_str() ), + poOgrSRS, wkbUnknown, + this, poKmlRoot, poKmlContainer, osFilePath.c_str(), FALSE, bUpdate, nFiles ); + + } + + delete poOgrSRS; + + CSLDestroy ( papszDirList ); + + if ( nLayers > 0 ) { + m_isDir = TRUE; + return TRUE; + } + + return FALSE; +} + +/****************************************************************************** + Method to open a datasource + + Args: pszFilename Darasource to open + bUpdate update mode + + Returns: True on success, false on failure + +******************************************************************************/ + +static int CheckIsKMZ(const char *pszFilename) +{ + char** papszFiles = VSIReadDir(pszFilename); + char** papszIter = papszFiles; + int bFoundKML = FALSE; + while(papszIter && *papszIter) + { + if (EQUAL(CPLGetExtension(*papszIter), "kml")) + { + bFoundKML = TRUE; + break; + } + else + { + CPLString osFilename(pszFilename); + osFilename += "/"; + osFilename += *papszIter; + if (CheckIsKMZ(osFilename)) + { + bFoundKML = TRUE; + break; + } + } + papszIter ++; + } + CSLDestroy(papszFiles); + return bFoundKML; +} + +int OGRLIBKMLDataSource::Open ( + const char *pszFilename, + int bUpdate ) +{ + + this->bUpdate = bUpdate; + pszName = CPLStrdup ( pszFilename ); + + /***** dir *****/ + + VSIStatBufL sStatBuf; + if ( !VSIStatExL ( pszFilename, &sStatBuf, VSI_STAT_NATURE_FLAG ) && + VSI_ISDIR ( sStatBuf.st_mode ) ) + return OpenDir ( pszFilename, bUpdate ); + + /***** kml *****/ + + else if ( EQUAL ( CPLGetExtension ( pszFilename ), "kml" ) ) + return OpenKml ( pszFilename, bUpdate ); + + /***** kmz *****/ + + else if ( EQUAL ( CPLGetExtension ( pszFilename ), "kmz" ) ) + return OpenKmz ( pszFilename, bUpdate ); + + else + { + char szBuffer[1024+1]; + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return FALSE; + + int nRead = VSIFReadL(szBuffer, 1, 1024, fp); + szBuffer[nRead] = 0; + + VSIFCloseL(fp); + + /* Does it look like a zip file ? */ + if (nRead == 1024 && + szBuffer[0] == 0x50 && szBuffer[1] == 0x4B && + szBuffer[2] == 0x03 && szBuffer[3] == 0x04) + { + CPLString osFilename("/vsizip/"); + osFilename += pszFilename; + if (!CheckIsKMZ(osFilename)) + return FALSE; + + return OpenKmz ( pszFilename, bUpdate ); + } + + if (strstr(szBuffer, "") || strstr(szBuffer, "= '0' && ch <= '9' ) + bDigitFound = TRUE; + else if( ch == ';' ) + break; + else if( !(ch == '-' || ch == '.' || ch == '(' || ch == ')') ) + return FALSE; + pszPhoneNumber ++; + } + return bDigitFound; +} + +/************************************************************************/ +/* SetCommonOptions() */ +/************************************************************************/ + +void OGRLIBKMLDataSource::SetCommonOptions(ContainerPtr poKmlContainer, + char** papszOptions) +{ + const char* pszName = CSLFetchNameValue(papszOptions, "NAME"); + if( pszName != NULL ) + poKmlContainer->set_name(pszName); + + const char* pszVisibilility = CSLFetchNameValue(papszOptions, "VISIBILITY"); + if( pszVisibilility != NULL ) + poKmlContainer->set_visibility(CSLTestBoolean(pszVisibilility)); + + const char* pszOpen = CSLFetchNameValue(papszOptions, "OPEN"); + if( pszOpen != NULL ) + poKmlContainer->set_open(CSLTestBoolean(pszOpen)); + + const char* pszSnippet = CSLFetchNameValue(papszOptions, "SNIPPET"); + if( pszSnippet != NULL ) + { + SnippetPtr poKmlSnippet = m_poKmlFactory->CreateSnippet(); + poKmlSnippet->set_text(pszSnippet); + poKmlContainer->set_snippet(poKmlSnippet); + } + + const char* pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION"); + if( pszDescription != NULL ) + poKmlContainer->set_description(pszDescription); +} + +/************************************************************************/ +/* ParseDocumentOptions() */ +/************************************************************************/ + +void OGRLIBKMLDataSource::ParseDocumentOptions(KmlPtr poKml, + DocumentPtr poKmlDocument) +{ + if( poKmlDocument != NULL ) + { + poKmlDocument->set_id("root_doc"); + + const char* pszAuthorName = CSLFetchNameValue(m_papszOptions, "AUTHOR_NAME"); + const char* pszAuthorURI = CSLFetchNameValue(m_papszOptions, "AUTHOR_URI"); + const char* pszAuthorEmail = CSLFetchNameValue(m_papszOptions, "AUTHOR_EMAIL"); + const char* pszLink = CSLFetchNameValue(m_papszOptions, "LINK"); + + if( pszAuthorName != NULL || pszAuthorURI != NULL || pszAuthorEmail != NULL ) + { + kmldom::AtomAuthorPtr author = m_poKmlFactory->CreateAtomAuthor(); + if( pszAuthorName != NULL ) + author->set_name(pszAuthorName); + if( pszAuthorURI != NULL ) + { + /* Ad-hoc validation. The ABNF is horribly complicated : http://tools.ietf.org/search/rfc3987#page-7 */ + if( strncmp(pszAuthorURI, "http://", strlen("http://")) == 0 || + strncmp(pszAuthorURI, "https://", strlen("https://")) == 0 ) + { + author->set_uri(pszAuthorURI); + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, "Invalid IRI for AUTHOR_URI"); + } + } + if( pszAuthorEmail != NULL ) + { + const char* pszArobase = strchr(pszAuthorEmail, '@'); + if( pszArobase != NULL && strchr(pszArobase + 1, '.') != NULL ) + { + author->set_email(pszAuthorEmail); + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, "Invalid email for AUTHOR_EMAIL"); + } + } + poKmlDocument->set_atomauthor(author); + } + + if( pszLink != NULL ) + { + kmldom::AtomLinkPtr link = m_poKmlFactory->CreateAtomLink(); + link->set_href(pszLink); + link->set_rel("related"); + poKmlDocument->set_atomlink(link); + } + + const char* pszPhoneNumber = CSLFetchNameValue(m_papszOptions, "PHONENUMBER"); + if( pszPhoneNumber != NULL ) + { + if( IsValidPhoneNumber(pszPhoneNumber) ) + { + if( strncmp(pszPhoneNumber, "tel:", strlen("tel:")) != 0 ) + poKmlDocument->set_phonenumber(CPLSPrintf("tel:%s", pszPhoneNumber)); + else + poKmlDocument->set_phonenumber(pszPhoneNumber); + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, "Invalid phone number"); + } + } + + SetCommonOptions(poKmlDocument, m_papszOptions); + + CPLString osListStyleType = CSLFetchNameValueDef(m_papszOptions, "LISTSTYLE_TYPE", ""); + CPLString osListStyleIconHref = CSLFetchNameValueDef(m_papszOptions, "LISTSTYLE_ICON_HREF", ""); + createkmlliststyle (m_poKmlFactory, + "root_doc", + poKmlDocument, + poKmlDocument, + osListStyleType, + osListStyleIconHref); + } + + if( poKml != NULL ) + { + if( m_poKmlUpdate != NULL ) + { + NetworkLinkControlPtr nlc = m_poKmlFactory->CreateNetworkLinkControl(); + poKml->set_networklinkcontrol ( nlc ); + if( m_poKmlUpdate->get_updateoperation_array_size() != 0 ) + { + nlc->set_update(m_poKmlUpdate); + } + } + + const char* pszNLCMinRefreshPeriod = CSLFetchNameValue(m_papszOptions, "NLC_MINREFRESHPERIOD"); + const char* pszNLCMaxSessionLength = CSLFetchNameValue(m_papszOptions, "NLC_MAXSESSIONLENGTH"); + const char* pszNLCCookie = CSLFetchNameValue(m_papszOptions, "NLC_COOKIE"); + const char* pszNLCMessage = CSLFetchNameValue(m_papszOptions, "NLC_MESSAGE"); + const char* pszNLCLinkName = CSLFetchNameValue(m_papszOptions, "NLC_LINKNAME"); + const char* pszNLCLinkDescription = CSLFetchNameValue(m_papszOptions, "NLC_LINKDESCRIPTION"); + const char* pszNLCLinkSnippet = CSLFetchNameValue(m_papszOptions, "NLC_LINKSNIPPET"); + const char* pszNLCExpires = CSLFetchNameValue(m_papszOptions, "NLC_EXPIRES"); + if( pszNLCMinRefreshPeriod != NULL || pszNLCMaxSessionLength != NULL || + pszNLCCookie != NULL || pszNLCMessage != NULL || pszNLCLinkName != NULL || + pszNLCLinkDescription != NULL || pszNLCLinkSnippet != NULL || + pszNLCExpires != NULL ) + { + NetworkLinkControlPtr nlc; + if( poKml->has_networklinkcontrol() ) + nlc = poKml->get_networklinkcontrol(); + else + { + nlc = m_poKmlFactory->CreateNetworkLinkControl(); + poKml->set_networklinkcontrol ( nlc ); + } + if( pszNLCMinRefreshPeriod != NULL ) + { + double dfVal = CPLAtof(pszNLCMinRefreshPeriod); + if( dfVal >= 0 ) + nlc->set_minrefreshperiod(dfVal); + } + if( pszNLCMaxSessionLength != NULL ) + { + double dfVal = CPLAtof(pszNLCMaxSessionLength); + nlc->set_maxsessionlength(dfVal); + } + if( pszNLCCookie != NULL ) + { + nlc->set_cookie(pszNLCCookie); + } + if( pszNLCMessage != NULL ) + { + nlc->set_message(pszNLCMessage); + } + if( pszNLCLinkName != NULL ) + { + nlc->set_linkname(pszNLCLinkName); + } + if( pszNLCLinkDescription != NULL ) + { + nlc->set_linkdescription(pszNLCLinkDescription); + } + if( pszNLCLinkSnippet != NULL ) + { + LinkSnippetPtr linksnippet = m_poKmlFactory->CreateLinkSnippet(); + linksnippet->set_text(pszNLCLinkSnippet); + nlc->set_linksnippet(linksnippet); + } + if( pszNLCExpires != NULL ) + { + OGRField sField; + if( OGRParseXMLDateTime( pszNLCExpires, &sField) ) + { + char* pszXMLDate = OGRGetXMLDateTime(&sField); + nlc->set_expires(pszXMLDate); + CPLFree(pszXMLDate); + } + } + } + } +} + +/****************************************************************************** + method to create a single file .kml ds + + Args: pszFilename the datasource to create + papszOptions datasource creation options + + Returns: True on success, false on failure + +******************************************************************************/ + +int OGRLIBKMLDataSource::CreateKml ( + CPL_UNUSED const char *pszFilename, + char **papszOptions ) +{ + m_poKmlDSKml = OGRLIBKMLCreateOGCKml22(m_poKmlFactory, papszOptions); + if( osUpdateTargetHref.size() == 0 ) + { + DocumentPtr poKmlDocument = m_poKmlFactory->CreateDocument ( ); + m_poKmlDSKml->set_feature ( poKmlDocument ); + m_poKmlDSContainer = poKmlDocument; + } + + m_isKml = TRUE; + bUpdated = TRUE; + + return true; +} + +/****************************************************************************** + method to create a .kmz ds + + Args: pszFilename the datasource to create + papszOptions datasource creation options + + Returns: True on success, false on failure + +******************************************************************************/ + +int OGRLIBKMLDataSource::CreateKmz ( + CPL_UNUSED const char *pszFilename, + CPL_UNUSED char **papszOptions ) +{ + /***** create the doc.kml *****/ + if( osUpdateTargetHref.size() == 0 ) + { + const char *pszUseDocKml = + CPLGetConfigOption ( "LIBKML_USE_DOC.KML", "yes" ); + + if ( CSLTestBoolean( pszUseDocKml ) ) { + m_poKmlDocKml = m_poKmlFactory->CreateDocument ( ); + } + } + + pszStylePath = CPLStrdup((char *) "style/style.kml"); + + m_isKmz = TRUE; + bUpdated = TRUE; + + return TRUE; +} + +/****************************************************************************** + Method to create a dir datasource + + Args: pszFilename the datasource to create + papszOptions datasource creation options + + Returns: True on success, false on failure + +******************************************************************************/ + +int OGRLIBKMLDataSource::CreateDir ( + const char *pszFilename, + CPL_UNUSED char **papszOptions ) +{ + if ( VSIMkdir ( pszFilename, 0755 ) ) { + CPLError ( CE_Failure, CPLE_AppDefined, + "ERROR Creating dir: %s for KML datasource", pszFilename ); + return FALSE; + } + + m_isDir = TRUE; + bUpdated = TRUE; + + if( osUpdateTargetHref.size() == 0 ) + { + const char *pszUseDocKml = + CPLGetConfigOption ( "LIBKML_USE_DOC.KML", "yes" ); + + if ( CSLTestBoolean( pszUseDocKml ) ) { + m_poKmlDocKml = m_poKmlFactory->CreateDocument ( ); + } + } + + pszStylePath = CPLStrdup((char *) "style.kml"); + + return TRUE; +} + + +/****************************************************************************** + method to create a datasource + + Args: pszFilename the datasource to create + papszOptions datasource creation options + + Returns: True on success, false on failure + + env vars: + LIBKML_USE_DOC.KML default: yes + +******************************************************************************/ + +int OGRLIBKMLDataSource::Create ( + const char *pszFilename, + char **papszOptions ) +{ + + int bResult = FALSE; + + if (strcmp(pszFilename, "/dev/stdout") == 0) + pszFilename = "/vsistdout/"; + + pszName = CPLStrdup ( pszFilename ); + bUpdate = TRUE; + + osUpdateTargetHref = CSLFetchNameValueDef(papszOptions, "UPDATE_TARGETHREF", ""); + if( osUpdateTargetHref.size() ) + { + m_poKmlUpdate = m_poKmlFactory->CreateUpdate(); + m_poKmlUpdate->set_targethref(osUpdateTargetHref.c_str()); + } + + m_papszOptions = CSLDuplicate(papszOptions); + + /***** kml *****/ + + if ( strcmp(pszFilename, "/vsistdout/") == 0 || + strncmp(pszFilename, "/vsigzip/", 9) == 0 || + EQUAL ( CPLGetExtension ( pszFilename ), "kml" ) ) + bResult = CreateKml ( pszFilename, papszOptions ); + + /***** kmz *****/ + + else if ( EQUAL ( CPLGetExtension ( pszFilename ), "kmz" ) ) + bResult = CreateKmz ( pszFilename, papszOptions ); + + /***** dir *****/ + + else + bResult = CreateDir ( pszFilename, papszOptions ); + + return bResult; +} + +/****************************************************************************** + method to get a layer by index + + Args: iLayer the index of the layer to get + + Returns: pointer to the layer, or NULL if the layer does not exist + +******************************************************************************/ + +OGRLayer *OGRLIBKMLDataSource::GetLayer ( + int iLayer ) +{ + if ( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; + +} + +/****************************************************************************** + method to get a layer by name + + Args: pszname name of the layer to get + + Returns: pointer to the layer, or NULL if the layer does not exist + +******************************************************************************/ + +OGRLayer *OGRLIBKMLDataSource::GetLayerByName ( + const char *pszname ) +{ + int iLayer = 0; + + for ( iLayer = 0; iLayer < nLayers; iLayer++ ) { + if ( EQUAL ( pszname, papoLayers[iLayer]->GetName ( ) ) ) + return papoLayers[iLayer]; + } + + return NULL; +} + + +/****************************************************************************** + method to DeleteLayers in a .kml datasource + + Args: iLayer index of the layer to delete + + Returns: OGRERR_NONE on success, OGRERR_FAILURE on failure + +******************************************************************************/ + +OGRErr OGRLIBKMLDataSource::DeleteLayerKml ( + int iLayer ) +{ + OGRLIBKMLLayer *poOgrLayer = ( OGRLIBKMLLayer * ) papoLayers[iLayer]; + + /***** loop over the features *****/ + + size_t nKmlFeatures = m_poKmlDSContainer->get_feature_array_size ( ); + size_t iKmlFeature; + + for ( iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++ ) { + FeaturePtr poKmlFeat = + m_poKmlDSContainer->get_feature_array_at ( iKmlFeature ); + + if ( poKmlFeat == poOgrLayer->GetKmlLayer ( ) ) { + m_poKmlDSContainer->DeleteFeatureAt ( iKmlFeature ); + break; + } + + } + + + return OGRERR_NONE; +} + +/****************************************************************************** + method to DeleteLayers in a .kmz datasource + + Args: iLayer index of the layer to delete + + Returns: OGRERR_NONE on success, OGRERR_FAILURE on failure + +******************************************************************************/ + +OGRErr OGRLIBKMLDataSource::DeleteLayerKmz ( + int iLayer ) +{ + OGRLIBKMLLayer *poOgrLayer = ( OGRLIBKMLLayer * ) papoLayers[iLayer]; + + const char *pszUseDocKml = + CPLGetConfigOption ( "LIBKML_USE_DOC.KML", "yes" ); + + if ( CSLTestBoolean ( pszUseDocKml ) && m_poKmlDocKml ) { + + /***** loop over the features *****/ + + size_t nKmlFeatures = m_poKmlDocKml->get_feature_array_size ( ); + size_t iKmlFeature; + + for ( iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++ ) { + FeaturePtr poKmlFeat = + m_poKmlDocKml->get_feature_array_at ( iKmlFeature ); + + if ( poKmlFeat->IsA ( kmldom::Type_NetworkLink ) ) { + NetworkLinkPtr poKmlNetworkLink = AsNetworkLink ( poKmlFeat ); + + /***** does it have a link? *****/ + + if ( poKmlNetworkLink->has_link ( ) ) { + LinkPtr poKmlLink = poKmlNetworkLink->get_link ( ); + + /***** does the link have a href? *****/ + + if ( poKmlLink->has_href ( ) ) { + kmlengine::Href * poKmlHref = + new kmlengine::Href ( poKmlLink->get_href ( ) ); + + /***** is the link relative? *****/ + + if ( poKmlHref->IsRelativePath ( ) ) { + + const char *pszLink = + poKmlHref->get_path ( ).c_str ( ); + + if ( EQUAL + ( pszLink, poOgrLayer->GetFileName ( ) ) ) { + m_poKmlDocKml->DeleteFeatureAt ( iKmlFeature ); + break; + } + + + } + } + } + } + } + + } + + return OGRERR_NONE; +} + +/****************************************************************************** + method to delete a layer in a datasource + + Args: iLayer index of the layer to delete + + Returns: OGRERR_NONE on success, OGRERR_FAILURE on failure + +******************************************************************************/ + +OGRErr OGRLIBKMLDataSource::DeleteLayer ( + int iLayer ) +{ + + if ( !bUpdate ) + return OGRERR_UNSUPPORTED_OPERATION; + + if ( iLayer >= nLayers ) + return OGRERR_FAILURE; + + if ( IsKml ( ) ) + DeleteLayerKml ( iLayer ); + + else if ( IsKmz ( ) ) + DeleteLayerKmz ( iLayer ); + + else if ( IsDir ( ) ) { + DeleteLayerKmz ( iLayer ); + + /***** delete the file the layer corisponds to *****/ + + const char *pszFilePath = + CPLFormFilename ( pszName, papoLayers[iLayer]->GetFileName ( ), + NULL ); + VSIStatBufL oStatBufL; + if ( !VSIStatL ( pszFilePath, &oStatBufL ) ) { + if ( VSIUnlink ( pszFilePath ) ) { + CPLError ( CE_Failure, CPLE_AppDefined, + "ERROR Deleteing Layer %s from filesystem as %s", + papoLayers[iLayer]->GetName ( ), pszFilePath ); + } + } + } + + + delete papoLayers[iLayer]; + memmove ( papoLayers + iLayer, papoLayers + iLayer + 1, + sizeof ( void * ) * ( nLayers - iLayer - 1 ) ); + nLayers--; + bUpdated = TRUE; + + return OGRERR_NONE; +} + +/****************************************************************************** + method to create a layer in a single file .kml + + Args: pszLayerName name of the layer to create + poOgrSRS the SRS of the layer + eGType the layers geometry type + papszOptions layer creation options + + Returns: return a pointer to the new layer or NULL on failure + +******************************************************************************/ + +OGRLIBKMLLayer *OGRLIBKMLDataSource::CreateLayerKml ( + const char *pszLayerName, + OGRSpatialReference * poOgrSRS, + OGRwkbGeometryType eGType, + char **papszOptions ) +{ + + OGRLIBKMLLayer *poOgrLayer = NULL; + ContainerPtr poKmlLayerContainer = NULL; + + if( m_poKmlDSContainer != NULL ) + { + if( CSLFetchBoolean( papszOptions, "FOLDER", FALSE ) ) + poKmlLayerContainer = m_poKmlFactory->CreateFolder ( ); + else + poKmlLayerContainer = m_poKmlFactory->CreateDocument ( ); + poKmlLayerContainer->set_id(OGRLIBKMLGetSanitizedNCName(pszLayerName).c_str()); + + m_poKmlDSContainer->add_feature ( poKmlLayerContainer ); + } + + /***** create the layer *****/ + + poOgrLayer = AddLayer ( pszLayerName, poOgrSRS, eGType, this, + NULL, poKmlLayerContainer, "", TRUE, bUpdate, 1 ); + + /***** add the layer name as a *****/ + if( poKmlLayerContainer != NULL ) + poKmlLayerContainer->set_name ( pszLayerName ); + else if( CSLFetchBoolean( papszOptions, "FOLDER", FALSE ) ) + { + poOgrLayer->SetUpdateIsFolder(TRUE); + } + + return poOgrLayer; +} + +/****************************************************************************** + method to create a layer in a .kmz or dir + + Args: pszLayerName name of the layer to create + poOgrSRS the SRS of the layer + eGType the layers geometry type + papszOptions layer creation options + + Returns: return a pointer to the new layer or NULL on failure + +******************************************************************************/ + +OGRLIBKMLLayer *OGRLIBKMLDataSource::CreateLayerKmz ( + const char *pszLayerName, + OGRSpatialReference * poOgrSRS, + OGRwkbGeometryType eGType, + CPL_UNUSED char **papszOptions ) +{ + OGRLIBKMLLayer *poOgrLayer = NULL; + DocumentPtr poKmlDocument = NULL; + + if( m_poKmlUpdate == NULL ) + { + /***** add a network link to doc.kml *****/ + + const char *pszUseDocKml = + CPLGetConfigOption ( "LIBKML_USE_DOC.KML", "yes" ); + + if ( CSLTestBoolean ( pszUseDocKml ) && m_poKmlDocKml ) { + + DocumentPtr poKmlDocument = AsDocument ( m_poKmlDocKml ); + + NetworkLinkPtr poKmlNetLink = m_poKmlFactory->CreateNetworkLink ( ); + LinkPtr poKmlLink = m_poKmlFactory->CreateLink ( ); + + std::string oHref; + if( IsKmz() ) + oHref.append ( "layers/" ); + oHref.append ( pszLayerName ); + oHref.append ( ".kml" ); + poKmlLink->set_href ( oHref ); + + poKmlNetLink->set_link ( poKmlLink ); + poKmlDocument->add_feature ( poKmlNetLink ); + + } + + /***** create the layer *****/ + + poKmlDocument = m_poKmlFactory->CreateDocument ( ); + poKmlDocument->set_id(OGRLIBKMLGetSanitizedNCName(pszLayerName).c_str()); + } + + poOgrLayer = AddLayer ( pszLayerName, poOgrSRS, eGType, this, + NULL, poKmlDocument, + CPLFormFilename ( NULL, pszLayerName, ".kml" ), + TRUE, bUpdate, 1 ); + + /***** add the layer name as a *****/ + if( m_poKmlUpdate == NULL ) + { + poKmlDocument->set_name ( pszLayerName ); + } + + return poOgrLayer; +} + +/****************************************************************************** +ICreateLayer() + + Args: pszLayerName name of the layer to create + poOgrSRS the SRS of the layer + eGType the layers geometry type + papszOptions layer creation options + + Returns: return a pointer to the new layer or NULL on failure + +******************************************************************************/ + +OGRLayer *OGRLIBKMLDataSource::ICreateLayer( + const char *pszLayerName, + OGRSpatialReference * poOgrSRS, + OGRwkbGeometryType eGType, + char **papszOptions ) +{ + + if ( !bUpdate ) + return NULL; + + if( (IsKmz () || IsDir ()) && EQUAL(pszLayerName, "doc") ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "'doc' is an invalid layer name in a KMZ file"); + return NULL; + } + + OGRLIBKMLLayer *poOgrLayer = NULL; + + /***** kml DS *****/ + + if ( IsKml ( ) ) { + poOgrLayer = CreateLayerKml ( pszLayerName, poOgrSRS, + eGType, papszOptions ); + + } + + else if ( IsKmz ( ) || IsDir ( ) ) { + poOgrLayer = CreateLayerKmz ( pszLayerName, poOgrSRS, + eGType, papszOptions ); + + } + + const char* pszLookatLongitude = CSLFetchNameValue(papszOptions, "LOOKAT_LONGITUDE"); + const char* pszLookatLatitude = CSLFetchNameValue(papszOptions, "LOOKAT_LATITUDE"); + const char* pszLookatAltitude = CSLFetchNameValue(papszOptions, "LOOKAT_ALTITUDE"); + const char* pszLookatHeading = CSLFetchNameValue(papszOptions, "LOOKAT_HEADING"); + const char* pszLookatTilt = CSLFetchNameValue(papszOptions, "LOOKAT_TILT"); + const char* pszLookatRange = CSLFetchNameValue(papszOptions, "LOOKAT_RANGE"); + const char* pszLookatAltitudeMode = CSLFetchNameValue(papszOptions, "LOOKAT_ALTITUDEMODE"); + if( poOgrLayer != NULL && pszLookatLongitude != NULL && pszLookatLatitude != NULL && + pszLookatRange != NULL ) + { + poOgrLayer->SetLookAt(pszLookatLongitude, + pszLookatLatitude, + pszLookatAltitude, + pszLookatHeading, + pszLookatTilt, + pszLookatRange, + pszLookatAltitudeMode); + } + else + { + const char* pszCameraLongitude = CSLFetchNameValue(papszOptions, "CAMERA_LONGITUDE"); + const char* pszCameraLatitude = CSLFetchNameValue(papszOptions, "CAMERA_LATITUDE"); + const char* pszCameraAltitude = CSLFetchNameValue(papszOptions, "CAMERA_ALTITUDE"); + const char* pszCameraHeading = CSLFetchNameValue(papszOptions, "CAMERA_HEADING"); + const char* pszCameraTilt = CSLFetchNameValue(papszOptions, "CAMERA_TILT"); + const char* pszCameraRoll = CSLFetchNameValue(papszOptions, "CAMERA_ROLL"); + const char* pszCameraAltitudeMode = CSLFetchNameValue(papszOptions, "CAMERA_ALTITUDEMODE"); + if( poOgrLayer != NULL && pszCameraLongitude != NULL && pszCameraLatitude != NULL && + pszCameraAltitude != NULL && pszCameraAltitudeMode != NULL ) + { + poOgrLayer->SetCamera(pszCameraLongitude, + pszCameraLatitude, + pszCameraAltitude, + pszCameraHeading, + pszCameraTilt, + pszCameraRoll, + pszCameraAltitudeMode); + } + } + + const char* pszRegionAdd = CSLFetchNameValueDef(papszOptions, "ADD_REGION", "FALSE"); + const char* pszRegionXMin = CSLFetchNameValue(papszOptions, "REGION_XMIN"); + const char* pszRegionYMin = CSLFetchNameValue(papszOptions, "REGION_YMIN"); + const char* pszRegionXMax = CSLFetchNameValue(papszOptions, "REGION_XMAX"); + const char* pszRegionYMax = CSLFetchNameValue(papszOptions, "REGION_YMAX"); + const char* pszRegionMinLodPixels = + CSLFetchNameValueDef(papszOptions, "REGION_MIN_LOD_PIXELS", "256"); + const char* pszRegionMaxLodPixels = + CSLFetchNameValueDef(papszOptions, "REGION_MAX_LOD_PIXELS", "-1"); + const char* pszRegionMinFadeExtent = + CSLFetchNameValueDef(papszOptions, "REGION_MIN_FADE_EXTENT", "0"); + const char* pszRegionMaxFadeExtent = + CSLFetchNameValueDef(papszOptions, "REGION_MAX_FADE_EXTENT", "0"); + if( poOgrLayer != NULL && CSLTestBoolean(pszRegionAdd) ) + { + poOgrLayer->SetWriteRegion(CPLAtof(pszRegionMinLodPixels), + CPLAtof(pszRegionMaxLodPixels), + CPLAtof(pszRegionMinFadeExtent), + CPLAtof(pszRegionMaxFadeExtent)); + if( pszRegionXMin != NULL && pszRegionYMin != NULL && + pszRegionXMax != NULL && pszRegionYMax != NULL ) + { + double xmin = CPLAtof(pszRegionXMin); + double ymin = CPLAtof(pszRegionYMin); + double xmax = CPLAtof(pszRegionXMax); + double ymax = CPLAtof(pszRegionYMax); + if( xmin < xmax && ymin < ymax ) + poOgrLayer->SetRegionBounds(xmin, ymin, xmax, ymax); + } + } + + const char* pszSOHref = CSLFetchNameValue(papszOptions, "SO_HREF"); + const char* pszSOName = CSLFetchNameValue(papszOptions, "SO_NAME"); + const char* pszSODescription = CSLFetchNameValue(papszOptions, "SO_DESCRIPTION"); + const char* pszSOOverlayX = CSLFetchNameValue(papszOptions, "SO_OVERLAY_X"); + const char* pszSOOverlayY = CSLFetchNameValue(papszOptions, "SO_OVERLAY_Y"); + const char* pszSOOverlayXUnits = CSLFetchNameValue(papszOptions, "SO_OVERLAY_XUNITS"); + const char* pszSOOverlayYUnits = CSLFetchNameValue(papszOptions, "SO_OVERLAY_YUNITS"); + const char* pszSOScreenX = CSLFetchNameValue(papszOptions, "SO_SCREEN_X"); + const char* pszSOScreenY = CSLFetchNameValue(papszOptions, "SO_SCREEN_Y"); + const char* pszSOScreenXUnits = CSLFetchNameValue(papszOptions, "SO_SCREEN_XUNITS"); + const char* pszSOScreenYUnits = CSLFetchNameValue(papszOptions, "SO_SCREEN_YUNITS"); + const char* pszSOSizeX = CSLFetchNameValue(papszOptions, "SO_SIZE_X"); + const char* pszSOSizeY = CSLFetchNameValue(papszOptions, "SO_SIZE_Y"); + const char* pszSOSizeXUnits = CSLFetchNameValue(papszOptions, "SO_SIZE_XUNITS"); + const char* pszSOSizeYUnits = CSLFetchNameValue(papszOptions, "SO_SIZE_YUNITS"); + if( poOgrLayer != NULL && pszSOHref != NULL ) + { + poOgrLayer->SetScreenOverlay(pszSOHref, + pszSOName, + pszSODescription, + pszSOOverlayX, + pszSOOverlayY, + pszSOOverlayXUnits, + pszSOOverlayYUnits, + pszSOScreenX, + pszSOScreenY, + pszSOScreenXUnits, + pszSOScreenYUnits, + pszSOSizeX, + pszSOSizeY, + pszSOSizeXUnits, + pszSOSizeYUnits); + } + + const char* pszListStyleType = CSLFetchNameValue(papszOptions, "LISTSTYLE_TYPE"); + const char* pszListStyleIconHref = CSLFetchNameValue(papszOptions, "LISTSTYLE_ICON_HREF"); + if( poOgrLayer != NULL ) + { + poOgrLayer->SetListStyle(pszListStyleType, pszListStyleIconHref); + } + + if( poOgrLayer != NULL && poOgrLayer->GetKmlLayer() != NULL ) + { + SetCommonOptions(poOgrLayer->GetKmlLayer(), papszOptions); + } + + /***** mark the dataset as updated *****/ + + if ( poOgrLayer ) + bUpdated = TRUE; + + return poOgrLayer; +} + +/****************************************************************************** + method to get a datasources style table + + Args: none + + Returns: pointer to the datasources style table, or NULL if it does + not have one + +******************************************************************************/ + +OGRStyleTable *OGRLIBKMLDataSource::GetStyleTable ( + ) +{ + + return m_poStyleTable; +} + +/****************************************************************************** + method to write a style table to a single file .kml ds + + Args: poStyleTable pointer to the style table to add + + Returns: nothing + +******************************************************************************/ + +void OGRLIBKMLDataSource::SetStyleTable2Kml ( + OGRStyleTable * poStyleTable ) +{ + if( m_poKmlDSContainer == NULL ) + return; + + /***** delete all the styles *****/ + + DocumentPtr poKmlDocument = AsDocument ( m_poKmlDSContainer ); + size_t nKmlStyles = poKmlDocument->get_styleselector_array_size ( ); + int iKmlStyle; + + for ( iKmlStyle = nKmlStyles - 1; iKmlStyle >= 0; iKmlStyle-- ) { + poKmlDocument->DeleteStyleSelectorAt ( iKmlStyle ); + } + + /***** add the new style table to the document *****/ + + styletable2kml ( poStyleTable, m_poKmlFactory, + AsContainer ( poKmlDocument ), m_papszOptions ); + + return; +} + +/****************************************************************************** + method to write a style table to a kmz ds + + Args: poStyleTable pointer to the style table to add + + Returns: nothing + +******************************************************************************/ + +void OGRLIBKMLDataSource::SetStyleTable2Kmz ( + OGRStyleTable * poStyleTable ) +{ + if( m_poKmlStyleKml != NULL || poStyleTable != NULL ) + { + /***** replace the style document with a new one *****/ + + m_poKmlStyleKml = m_poKmlFactory->CreateDocument ( ); + m_poKmlStyleKml->set_id ( "styleId" ); + + styletable2kml ( poStyleTable, m_poKmlFactory, m_poKmlStyleKml ); + } + + return; +} + +/****************************************************************************** + method to write a style table to a datasource + + Args: poStyleTable pointer to the style table to add + + Returns: nothing + + note: this method assumes ownership of the style table + +******************************************************************************/ + +void OGRLIBKMLDataSource::SetStyleTableDirectly ( + OGRStyleTable * poStyleTable ) +{ + + if ( !bUpdate ) + return; + + if ( m_poStyleTable ) + delete m_poStyleTable; + + m_poStyleTable = poStyleTable; + + /***** a kml datasource? *****/ + + if ( IsKml ( ) ) + SetStyleTable2Kml ( m_poStyleTable ); + + else if ( IsKmz ( ) || IsDir ( ) ) + SetStyleTable2Kmz ( m_poStyleTable ); + + bUpdated = TRUE; + + + + + return; +} + +/****************************************************************************** + method to write a style table to a datasource + + Args: poStyleTable pointer to the style table to add + + Returns: nothing + + note: this method copys the style table, and the user will still be + responsible for its destruction + +******************************************************************************/ + +void OGRLIBKMLDataSource::SetStyleTable ( + OGRStyleTable * poStyleTable ) +{ + + if ( !bUpdate ) + return; + + if ( poStyleTable ) + SetStyleTableDirectly ( poStyleTable->Clone ( ) ); + else + SetStyleTableDirectly ( NULL ); + return; +} + + +/****************************************************************************** + Test if capability is available. + + Args: pszCap datasource capability name to test + + Returns: nothing + + ODsCCreateLayer: True if this datasource can create new layers. + ODsCDeleteLayer: True if this datasource can delete existing layers. + +******************************************************************************/ + +int OGRLIBKMLDataSource::TestCapability ( + const char *pszCap ) +{ + + if ( EQUAL ( pszCap, ODsCCreateLayer ) ) + return bUpdate; + else if ( EQUAL ( pszCap, ODsCDeleteLayer ) ) + return bUpdate; + else + return FALSE; + +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmldriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmldriver.cpp new file mode 100644 index 000000000..11cb8df93 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmldriver.cpp @@ -0,0 +1,347 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "ogr_libkml.h" +#include "cpl_conv.h" +#include "cpl_error.h" +#include "cpl_multiproc.h" + +#include + +using kmldom::KmlFactory; + +static CPLMutex *hMutex = NULL; +static KmlFactory* m_poKmlFactory = NULL; + +/****************************************************************************** + OGRLIBKMLDriverUnload() +******************************************************************************/ + +static void OGRLIBKMLDriverUnload ( CPL_UNUSED GDALDriver* poDriver ) +{ + if( hMutex != NULL ) + CPLDestroyMutex(hMutex); + hMutex = NULL; + m_poKmlFactory = NULL; +} + +/************************************************************************/ +/* OGRLIBKMLDriverIdentify() */ +/************************************************************************/ + +static int OGRLIBKMLDriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + if( !poOpenInfo->bStatOK ) + return FALSE; + if( poOpenInfo->bIsDirectory ) + return -1; + + return( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "kml") || + EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "kmz") ); +} + +/****************************************************************************** + Open() +******************************************************************************/ + +static GDALDataset *OGRLIBKMLDriverOpen ( GDALOpenInfo* poOpenInfo ) +{ + if( OGRLIBKMLDriverIdentify(poOpenInfo) == FALSE ) + return NULL; + + { + CPLMutexHolderD(&hMutex); + if( m_poKmlFactory == NULL ) + m_poKmlFactory = KmlFactory::GetFactory ( ); + } + + OGRLIBKMLDataSource *poDS = new OGRLIBKMLDataSource ( m_poKmlFactory ); + + if ( !poDS->Open ( poOpenInfo->pszFilename, poOpenInfo->eAccess == GA_Update ) ) { + delete poDS; + + poDS = NULL; + } + + return poDS; +} + + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRLIBKMLDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + CPLAssert ( NULL != pszName ); + CPLDebug ( "LIBKML", "Attempt to create: %s", pszName ); + + { + CPLMutexHolderD(&hMutex); + if( m_poKmlFactory == NULL ) + m_poKmlFactory = KmlFactory::GetFactory ( ); + } + + OGRLIBKMLDataSource *poDS = new OGRLIBKMLDataSource ( m_poKmlFactory ); + + if ( !poDS->Create ( pszName, papszOptions ) ) { + delete poDS; + + poDS = NULL; + } + + return poDS; +} + +/****************************************************************************** + DeleteDataSource() + + note: this method recursivly deletes an entire dir if the datasource is a dir + and all the files are kml or kmz + +******************************************************************************/ + +static CPLErr OGRLIBKMLDriverDelete( const char *pszName ) +{ + + /***** dir *****/ + + VSIStatBufL sStatBuf; + if ( !VSIStatL ( pszName, &sStatBuf ) && VSI_ISDIR ( sStatBuf.st_mode ) ) { + + char **papszDirList = NULL; + + if ( !( papszDirList = VSIReadDir ( pszName ) ) ) { + if ( VSIRmdir ( pszName ) < 0 ) + return CE_Failure; + } + + int nFiles = CSLCount ( papszDirList ); + int iFile; + + for ( iFile = 0; iFile < nFiles; iFile++ ) { + if ( CE_Failure == + OGRLIBKMLDriverDelete ( papszDirList[iFile] ) ) { + CSLDestroy ( papszDirList ); + return CE_Failure; + } + } + + if ( VSIRmdir ( pszName ) < 0 ) { + CSLDestroy ( papszDirList ); + return CE_Failure; + } + + CSLDestroy ( papszDirList ); + } + + /***** kml *****/ + + else if ( EQUAL ( CPLGetExtension ( pszName ), "kml" ) ) { + if ( VSIUnlink ( pszName ) < 0 ) + return CE_Failure; + } + + /***** kmz *****/ + + else if ( EQUAL ( CPLGetExtension ( pszName ), "kmz" ) ) { + if ( VSIUnlink ( pszName ) < 0 ) + return CE_Failure; + } + + /***** do not delete other types of files *****/ + + else + return CE_Failure; + + return CE_None; +} + +/****************************************************************************** + RegisterOGRLIBKML() +******************************************************************************/ + +void RegisterOGRLIBKML ( + ) +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "LIBKML" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "LIBKML" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Keyhole Markup Language (LIBKML)" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "kml kmz" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_libkml.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" "); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" " +" " +" " +" " +" " +" " +" " +" " +" " +" "); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Real String" ); + + poDriver->pfnOpen = OGRLIBKMLDriverOpen; + poDriver->pfnIdentify = OGRLIBKMLDriverIdentify; + poDriver->pfnCreate = OGRLIBKMLDriverCreate; + poDriver->pfnDelete = OGRLIBKMLDriverDelete; + poDriver->pfnUnloadDriver = OGRLIBKMLDriverUnload; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfeature.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfeature.cpp new file mode 100644 index 000000000..6b956ba5a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfeature.cpp @@ -0,0 +1,788 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include +#include +#include "gdal.h" + +#include + +using kmldom::KmlFactory; +using kmldom::PlacemarkPtr; +using kmldom::ElementPtr; +using kmldom::GeometryPtr; +using kmldom::Geometry; +using kmldom::GroundOverlayPtr; +using kmldom::CameraPtr; +using kmldom::ModelPtr; +using kmldom::LinkPtr; +using kmldom::LocationPtr; +using kmldom::OrientationPtr; +using kmldom::ScalePtr; +using kmldom::ResourceMapPtr; +using kmldom::AliasPtr; +using kmldom::NetworkLinkPtr; +using kmldom::PhotoOverlayPtr; +using kmldom::IconPtr; +using kmldom::ViewVolumePtr; +using kmldom::ImagePyramidPtr; + +#include "ogr_libkml.h" + +#include "ogrlibkmlgeometry.h" +#include "ogrlibkmlfield.h" +#include "ogrlibkmlfeaturestyle.h" + +static CameraPtr feat2kmlcamera( const struct fieldconfig& oFC, + int iHeading, + int iTilt, + int iRoll, + OGRFeature * poOgrFeat, + KmlFactory * poKmlFactory ) +{ + int iCameraLongitudeField = poOgrFeat->GetFieldIndex(oFC.camera_longitude_field); + int iCameraLatitudeField = poOgrFeat->GetFieldIndex(oFC.camera_latitude_field); + int iCameraAltitudeField = poOgrFeat->GetFieldIndex(oFC.camera_altitude_field); + int iCameraAltitudeModeField = poOgrFeat->GetFieldIndex(oFC.camera_altitudemode_field); + if( iCameraLongitudeField >= 0 && poOgrFeat->IsFieldSet(iCameraLongitudeField) && + iCameraLatitudeField >= 0 && poOgrFeat->IsFieldSet(iCameraLatitudeField) && + ((iHeading >= 0 && poOgrFeat->IsFieldSet(iHeading)) || + (iTilt >= 0 && poOgrFeat->IsFieldSet(iTilt)) || + (iRoll >= 0 && poOgrFeat->IsFieldSet(iRoll))) ) + { + CameraPtr camera = poKmlFactory->CreateCamera(); + camera->set_latitude(poOgrFeat->GetFieldAsDouble(iCameraLatitudeField)); + camera->set_longitude(poOgrFeat->GetFieldAsDouble(iCameraLongitudeField)); + int isGX = FALSE; + int nAltitudeMode = kmldom::ALTITUDEMODE_CLAMPTOGROUND; + if( iCameraAltitudeModeField >= 0 && poOgrFeat->IsFieldSet(iCameraAltitudeModeField) ) + { + nAltitudeMode = kmlAltitudeModeFromString( + poOgrFeat->GetFieldAsString(iCameraAltitudeModeField), isGX); + camera->set_altitudemode(nAltitudeMode); + } + else if( CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + CPLError(CE_Warning, CPLE_AppDefined, "Camera should define altitudeMode != 'clampToGround'"); + if( iCameraAltitudeField >= 0 && poOgrFeat->IsFieldSet(iCameraAltitudeField)) + camera->set_altitude(poOgrFeat->GetFieldAsDouble(iCameraAltitudeField)); + else if( CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Camera should have an altitude/Z"); + camera->set_altitude(0.0); + } + if( iHeading >= 0 && poOgrFeat->IsFieldSet(iHeading) ) + camera->set_heading(poOgrFeat->GetFieldAsDouble(iHeading)); + if( iTilt >= 0 && poOgrFeat->IsFieldSet(iTilt) ) + camera->set_tilt(poOgrFeat->GetFieldAsDouble(iTilt)); + if( iRoll >= 0 && poOgrFeat->IsFieldSet(iRoll) ) + camera->set_roll(poOgrFeat->GetFieldAsDouble(iRoll)); + return camera; + } + else + return NULL; +} + + +/************************************************************************/ +/* OGRLIBKMLReplaceXYLevelInURL() */ +/************************************************************************/ + +static CPLString OGRLIBKMLReplaceLevelXYInURL(const char* pszURL, + int level, int x, int y) +{ + CPLString osRet(pszURL); + int nPos = osRet.find("$[level]"); + osRet = osRet.substr(0, nPos) + CPLSPrintf("%d", level) + osRet.substr(nPos + strlen("$[level]")); + nPos = osRet.find("$[x]"); + osRet = osRet.substr(0, nPos) + CPLSPrintf("%d", x) + osRet.substr(nPos + strlen("$[x]")); + nPos = osRet.find("$[y]"); + osRet = osRet.substr(0, nPos) + CPLSPrintf("%d", y) + osRet.substr(nPos + strlen("$[y]")); + return osRet; +} + + +/************************************************************************/ +/* IsPowerOf2 */ +/************************************************************************/ + +static int IsPowerOf2(int nVal) +{ + unsigned int nTmp = (unsigned int) nVal; + while( (nTmp & 1) == 0 ) + { + nTmp >>= 1; + } + return( nTmp == 1 ); +} + +/************************************************************************/ +/* OGRLIBKMLGetMaxDimensions() */ +/************************************************************************/ + +static void OGRLIBKMLGetMaxDimensions(const char* pszURL, + int nTileSize, + int* panMaxWidth, + int* panMaxHeight) +{ + VSIStatBufL sStat; + int nMaxLevel = 0; + *panMaxWidth = 0; + *panMaxHeight = 0; + while( TRUE ) + { + CPLString osURL = OGRLIBKMLReplaceLevelXYInURL(pszURL, nMaxLevel, 0, 0); + if( strstr(osURL, ".kmz/") ) + osURL = "/vsizip/" + osURL; + if( VSIStatL(osURL, &sStat) == 0 ) + nMaxLevel ++; + else + { + if( nMaxLevel == 0 ) + return; + break; + } + } + nMaxLevel --; + + int i; + for(i = 0; ; i++ ) + { + CPLString osURL = OGRLIBKMLReplaceLevelXYInURL(pszURL, nMaxLevel, i + 1, 0); + if( strstr(osURL, ".kmz/") ) + osURL = "/vsizip/" + osURL; + if( VSIStatL(osURL, &sStat) != 0 ) + break; + } + *panMaxWidth = (i + 1) * nTileSize; + for(i = 0; ; i++ ) + { + CPLString osURL = OGRLIBKMLReplaceLevelXYInURL(pszURL, nMaxLevel, 0, i + 1); + if( strstr(osURL, ".kmz/") ) + osURL = "/vsizip/" + osURL; + if( VSIStatL(osURL, &sStat) != 0 ) + break; + } + *panMaxHeight = (i + 1) * nTileSize; +} + +/************************************************************************/ +/* feat2kml() */ +/************************************************************************/ + +FeaturePtr feat2kml ( + OGRLIBKMLDataSource * poOgrDS, + OGRLayer * poOgrLayer, + OGRFeature * poOgrFeat, + KmlFactory * poKmlFactory, + int bUseSimpleField ) +{ + FeaturePtr poKmlFeature = NULL; + + struct fieldconfig oFC; + get_fieldconfig( &oFC ); + + /***** geometry *****/ + + OGRGeometry *poOgrGeom = poOgrFeat->GetGeometryRef ( ); + int iHeading = poOgrFeat->GetFieldIndex(oFC.headingfield), + iTilt = poOgrFeat->GetFieldIndex(oFC.tiltfield), + iRoll = poOgrFeat->GetFieldIndex(oFC.rollfield), + iModel = poOgrFeat->GetFieldIndex(oFC.modelfield); + int iNetworkLink = poOgrFeat->GetFieldIndex(oFC.networklinkfield); + int iPhotoOverlay = poOgrFeat->GetFieldIndex(oFC.photooverlayfield); + CameraPtr camera = NULL; + + /* PhotoOverlay */ + if( iPhotoOverlay >= 0 && poOgrFeat->IsFieldSet(iPhotoOverlay) && + poOgrGeom != NULL && !poOgrGeom->IsEmpty() && + wkbFlatten(poOgrGeom->getGeometryType()) == wkbPoint && + (camera = feat2kmlcamera(oFC, iHeading, iTilt, iRoll, poOgrFeat, poKmlFactory)) != NULL) + { + int iLeftFovField = poOgrFeat->GetFieldIndex(oFC.leftfovfield); + int iRightFovField = poOgrFeat->GetFieldIndex(oFC.rightfovfield); + int iBottomFovField = poOgrFeat->GetFieldIndex(oFC.bottomfovfield); + int iTopFovField = poOgrFeat->GetFieldIndex(oFC.topfovfield); + int iNearField = poOgrFeat->GetFieldIndex(oFC.nearfield); + double dfNear; + + const char* pszURL = poOgrFeat->GetFieldAsString(iPhotoOverlay); + int iImagePyramidTileSize = poOgrFeat->GetFieldIndex(oFC.imagepyramid_tilesize_field); + int iImagePyramidMaxWidth = poOgrFeat->GetFieldIndex(oFC.imagepyramid_maxwidth_field); + int iImagePyramidMaxHeight = poOgrFeat->GetFieldIndex(oFC.imagepyramid_maxheight_field); + int iImagePyramidGridOrigin = poOgrFeat->GetFieldIndex(oFC.imagepyramid_gridorigin_field); + int nTileSize = 0, nMaxWidth = 0, nMaxHeight = 0; + int bIsTiledPhotoOverlay = FALSE; + int bGridOriginIsUpperLeft = TRUE; + /* ATC 52 and 62 */ + if( strstr(pszURL, "$[x]") && strstr(pszURL, "$[y]") && strstr(pszURL, "$[level]") ) + { + bIsTiledPhotoOverlay = TRUE; + int bErrorEmitted = FALSE; + if( iImagePyramidTileSize < 0 || !poOgrFeat->IsFieldSet(iImagePyramidTileSize) ) + { + CPLDebug("LIBKML", "Missing ImagePyramid tileSize. Computing it"); + CPLString osURL = OGRLIBKMLReplaceLevelXYInURL(pszURL, 0, 0, 0); + if( strstr(osURL, ".kmz/") ) + osURL = "/vsizip/" + osURL; + GDALDatasetH hDS = GDALOpen( osURL, GA_ReadOnly ); + if( hDS != NULL ) + { + nTileSize = GDALGetRasterXSize(hDS); + if( nTileSize != GDALGetRasterYSize(hDS) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Non square tile : %dx%d", + GDALGetRasterXSize(hDS), GDALGetRasterYSize(hDS)); + nTileSize = 0; + bErrorEmitted = TRUE; + } + GDALClose(hDS); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", osURL.c_str()); + bErrorEmitted = TRUE; + } + } + else + { + nTileSize = poOgrFeat->GetFieldAsInteger(iImagePyramidTileSize); + } + if( !bErrorEmitted && (nTileSize <= 1 || !IsPowerOf2(nTileSize)) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Tile size is not a power of two: %d", nTileSize); + nTileSize = 0; + } + + if( nTileSize > 0 ) + { + if( iImagePyramidMaxWidth < 0 || !poOgrFeat->IsFieldSet(iImagePyramidMaxWidth) || + iImagePyramidMaxHeight < 0 || !poOgrFeat->IsFieldSet(iImagePyramidMaxHeight) ) + { + CPLDebug("LIBKML", "Missing ImagePyramid maxWidth and/or maxHeight. Computing it"); + OGRLIBKMLGetMaxDimensions(pszURL, nTileSize, &nMaxWidth, &nMaxHeight); + } + else + { + nMaxWidth = poOgrFeat->GetFieldAsInteger(iImagePyramidMaxWidth); + nMaxHeight = poOgrFeat->GetFieldAsInteger(iImagePyramidMaxHeight); + } + + if( nTileSize <= 0 || nMaxWidth <= 0 || nMaxHeight <= 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot generate PhotoOverlay object since there are " + "missing information to generate ImagePyramid element"); + } + } + + if( iImagePyramidGridOrigin >= 0 && poOgrFeat->IsFieldSet(iImagePyramidGridOrigin) ) + { + const char* pszGridOrigin = poOgrFeat->GetFieldAsString(iImagePyramidGridOrigin); + if( EQUAL(pszGridOrigin, "UpperLeft") ) + bGridOriginIsUpperLeft = TRUE; + else if( EQUAL(pszGridOrigin, "BottomLeft") ) + bGridOriginIsUpperLeft = FALSE; + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unhandled value for imagepyramid_gridorigin : %s. Assuming UpperLeft", + pszGridOrigin); + } + } + } + else + { + if( (iImagePyramidTileSize >= 0 && poOgrFeat->IsFieldSet(iImagePyramidTileSize)) || + (iImagePyramidMaxWidth >= 0 && poOgrFeat->IsFieldSet(iImagePyramidMaxWidth)) || + (iImagePyramidMaxHeight >= 0 && poOgrFeat->IsFieldSet(iImagePyramidMaxHeight)) || + (iImagePyramidGridOrigin >= 0 && poOgrFeat->IsFieldSet(iImagePyramidGridOrigin)) ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Ignoring any ImagePyramid information since the URL does " + "not include $[x] and/or $[y] and/or $[level]"); + } + } + + /* ATC 19 & 35 */ + if( (!bIsTiledPhotoOverlay || (nTileSize > 0 && nMaxWidth > 0 && nMaxHeight > 0)) && + iLeftFovField >= 0 && poOgrFeat->IsFieldSet(iLeftFovField) && + iRightFovField >= 0 && poOgrFeat->IsFieldSet(iRightFovField) && + iBottomFovField >= 0 && poOgrFeat->IsFieldSet(iBottomFovField) && + iTopFovField >= 0 && poOgrFeat->IsFieldSet(iTopFovField) && + iNearField >= 0 && (dfNear = poOgrFeat->GetFieldAsDouble(iNearField)) > 0 ) + { + PhotoOverlayPtr poKmlPhotoOverlay = poKmlFactory->CreatePhotoOverlay ( ); + poKmlFeature = poKmlPhotoOverlay; + + IconPtr poKmlIcon = poKmlFactory->CreateIcon ( ); + poKmlPhotoOverlay->set_icon(poKmlIcon); + poKmlIcon->set_href( pszURL ); + + ViewVolumePtr poKmlViewVolume = poKmlFactory->CreateViewVolume( ); + poKmlPhotoOverlay->set_viewvolume(poKmlViewVolume); + + double dfLeftFov = poOgrFeat->GetFieldAsDouble(iLeftFovField); + double dfRightFov = poOgrFeat->GetFieldAsDouble(iRightFovField); + double dfBottomFov = poOgrFeat->GetFieldAsDouble(iBottomFovField); + double dfTopFov = poOgrFeat->GetFieldAsDouble(iTopFovField); + + poKmlViewVolume->set_leftfov(dfLeftFov); + poKmlViewVolume->set_rightfov(dfRightFov); + poKmlViewVolume->set_bottomfov(dfBottomFov); + poKmlViewVolume->set_topfov(dfTopFov); + poKmlViewVolume->set_near(dfNear); + + if( bIsTiledPhotoOverlay ) + { + ImagePyramidPtr poKmlImagePyramid = poKmlFactory->CreateImagePyramid( ); + poKmlPhotoOverlay->set_imagepyramid(poKmlImagePyramid); + + poKmlImagePyramid->set_tilesize(nTileSize); + poKmlImagePyramid->set_maxwidth(nMaxWidth); + poKmlImagePyramid->set_maxheight(nMaxHeight); + poKmlImagePyramid->set_gridorigin( (bGridOriginIsUpperLeft) ? + kmldom::GRIDORIGIN_UPPERLEFT : kmldom::GRIDORIGIN_LOWERLEFT ); + } + + int iPhotoOverlayShapeField = poOgrFeat->GetFieldIndex(oFC.photooverlay_shape_field); + if( iPhotoOverlayShapeField >= 0 && poOgrFeat->IsFieldSet(iPhotoOverlayShapeField) ) + { + const char* pszShape = poOgrFeat->GetFieldAsString(iPhotoOverlayShapeField); + if( EQUAL(pszShape, "rectangle") ) + poKmlPhotoOverlay->set_shape(kmldom::SHAPE_RECTANGLE); + else if( EQUAL(pszShape, "cylinder") ) + poKmlPhotoOverlay->set_shape(kmldom::SHAPE_CYLINDER); + else if( EQUAL(pszShape, "sphere") ) + poKmlPhotoOverlay->set_shape(kmldom::SHAPE_SPHERE); + } + + ElementPtr poKmlElement = geom2kml ( poOgrGeom, -1, poKmlFactory ); + + poKmlPhotoOverlay->set_point ( AsPoint ( poKmlElement ) ); + } + } + + /* NetworkLink */ + if( poKmlFeature == NULL && iNetworkLink >= 0 && poOgrFeat->IsFieldSet(iNetworkLink) ) + { + NetworkLinkPtr poKmlNetworkLink = poKmlFactory->CreateNetworkLink ( ); + poKmlFeature = poKmlNetworkLink; + + int iRefreshVisibility = poOgrFeat->GetFieldIndex(oFC.networklink_refreshvisibility_field); + int iFlyToView = poOgrFeat->GetFieldIndex(oFC.networklink_flytoview_field); + + if( iRefreshVisibility >= 0 && poOgrFeat->IsFieldSet(iRefreshVisibility) ) + poKmlNetworkLink->set_refreshvisibility(poOgrFeat->GetFieldAsInteger(iRefreshVisibility)); + + if( iFlyToView >= 0 && poOgrFeat->IsFieldSet(iFlyToView) ) + poKmlNetworkLink->set_flytoview(poOgrFeat->GetFieldAsInteger(iFlyToView)); + + LinkPtr poKmlLink = poKmlFactory->CreateLink ( ); + poKmlLink->set_href( poOgrFeat->GetFieldAsString( iNetworkLink ) ); + poKmlNetworkLink->set_link(poKmlLink); + + int iRefreshMode = poOgrFeat->GetFieldIndex(oFC.networklink_refreshMode_field); + int iRefreshInterval = poOgrFeat->GetFieldIndex(oFC.networklink_refreshInterval_field); + int iViewRefreshMode = poOgrFeat->GetFieldIndex(oFC.networklink_viewRefreshMode_field); + int iViewRefreshTime = poOgrFeat->GetFieldIndex(oFC.networklink_viewRefreshTime_field); + int iViewBoundScale = poOgrFeat->GetFieldIndex(oFC.networklink_viewBoundScale_field); + int iViewFormat = poOgrFeat->GetFieldIndex(oFC.networklink_viewFormat_field); + int iHttpQuery = poOgrFeat->GetFieldIndex(oFC.networklink_httpQuery_field); + + double dfRefreshInterval = 0.0; + if( iRefreshInterval >= 0 && poOgrFeat->IsFieldSet(iRefreshInterval) ) + { + dfRefreshInterval = poOgrFeat->GetFieldAsDouble(iRefreshInterval); + if( dfRefreshInterval < 0 ) + dfRefreshInterval = 0.0; + } + + double dfViewRefreshTime = 0.0; + if( iViewRefreshTime >= 0 && poOgrFeat->IsFieldSet(iViewRefreshTime) ) + { + dfViewRefreshTime = poOgrFeat->GetFieldAsDouble(iViewRefreshTime); + if( dfViewRefreshTime < 0 ) + dfViewRefreshTime = 0.0; + } + + if( dfRefreshInterval > 0 ) /* ATC 51 */ + poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONINTERVAL); + else if( iRefreshMode >= 0 && poOgrFeat->IsFieldSet(iRefreshMode) ) + { + const char* pszRefreshMode = poOgrFeat->GetFieldAsString(iRefreshMode); + if( EQUAL(pszRefreshMode, "onChange") ) + poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONCHANGE); + else if( EQUAL(pszRefreshMode, "onInterval") ) + poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONINTERVAL); + else if( EQUAL(pszRefreshMode, "onExpire") ) + poKmlLink->set_refreshmode(kmldom::REFRESHMODE_ONEXPIRE); + } + + if( dfRefreshInterval > 0 ) /* ATC 9 */ + poKmlLink->set_refreshinterval(dfRefreshInterval); + + if( dfViewRefreshTime > 0 ) /* ATC 51 */ + poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_ONSTOP); + else if( iViewRefreshMode >= 0 && poOgrFeat->IsFieldSet(iViewRefreshMode) ) + { + const char* pszViewRefreshMode = poOgrFeat->GetFieldAsString(iViewRefreshMode); + if( EQUAL(pszViewRefreshMode, "never") ) + poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_NEVER); + else if( EQUAL(pszViewRefreshMode, "onRequest") ) + poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_ONREQUEST); + else if( EQUAL(pszViewRefreshMode, "onStop") ) + poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_ONSTOP); + else if( EQUAL(pszViewRefreshMode, "onRegion") ) + poKmlLink->set_viewrefreshmode(kmldom::VIEWREFRESHMODE_ONREGION); + } + + if( dfViewRefreshTime > 0 ) /* ATC 9 */ + poKmlLink->set_viewrefreshtime(dfViewRefreshTime); + + if( iViewBoundScale >= 0 && poOgrFeat->IsFieldSet(iViewBoundScale) ) + { + double dfViewBoundScale = poOgrFeat->GetFieldAsDouble(iViewBoundScale); + if( dfViewBoundScale > 0 ) /* ATC 9 */ + poKmlLink->set_viewboundscale(dfViewBoundScale); + } + + if( iViewFormat >= 0 && poOgrFeat->IsFieldSet(iViewFormat) ) + { + const char* pszViewFormat = poOgrFeat->GetFieldAsString(iViewFormat); + if( pszViewFormat[0] != '\0' ) /* ATC 46 */ + poKmlLink->set_viewformat(pszViewFormat); + } + + if( iHttpQuery >= 0 && poOgrFeat->IsFieldSet(iHttpQuery) ) + { + const char* pszHttpQuery = poOgrFeat->GetFieldAsString(iHttpQuery); + if( strstr(pszHttpQuery, "[clientVersion]") != NULL || + strstr(pszHttpQuery, "[kmlVersion]") != NULL || + strstr(pszHttpQuery, "[clientName]") != NULL || + strstr(pszHttpQuery, "[language]") != NULL ) /* ATC 47 */ + { + poKmlLink->set_httpquery(pszHttpQuery); + } + } + } + + /* Model */ + else if( poKmlFeature == NULL && iModel>= 0 && poOgrFeat->IsFieldSet(iModel) && + poOgrGeom != NULL && !poOgrGeom->IsEmpty() && + wkbFlatten(poOgrGeom->getGeometryType()) == wkbPoint ) + { + PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark ( ); + poKmlFeature = poKmlPlacemark; + + OGRPoint* poOgrPoint = (OGRPoint*) poOgrGeom; + ModelPtr model = poKmlFactory->CreateModel(); + + LocationPtr location = poKmlFactory->CreateLocation(); + model->set_location(location); + location->set_latitude(poOgrPoint->getY()); + location->set_longitude(poOgrPoint->getX()); + if( poOgrPoint->getCoordinateDimension() == 3 ) + location->set_altitude(poOgrPoint->getZ()); + + int isGX = FALSE; + int iAltitudeMode = poOgrFeat->GetFieldIndex(oFC.altitudeModefield); + int nAltitudeMode = kmldom::ALTITUDEMODE_CLAMPTOGROUND; + if( iAltitudeMode >= 0 && poOgrFeat->IsFieldSet(iAltitudeMode) ) + { + nAltitudeMode = kmlAltitudeModeFromString( + poOgrFeat->GetFieldAsString(iAltitudeMode), isGX); + model->set_altitudemode(nAltitudeMode); + + /* ATC 55 */ + if( nAltitudeMode != kmldom::ALTITUDEMODE_CLAMPTOGROUND && + poOgrPoint->getCoordinateDimension() != 3 ) + { + if( CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + CPLError(CE_Warning, CPLE_AppDefined, "Altitude should be defined"); + } + } + + if( (iHeading >= 0 && poOgrFeat->IsFieldSet(iHeading)) || + (iTilt >= 0 && poOgrFeat->IsFieldSet(iTilt)) || + (iRoll >= 0 && poOgrFeat->IsFieldSet(iRoll)) ) + { + OrientationPtr orientation = poKmlFactory->CreateOrientation(); + model->set_orientation(orientation); + if( iHeading >= 0 && poOgrFeat->IsFieldSet(iHeading) ) + orientation->set_heading(poOgrFeat->GetFieldAsDouble(iHeading)); + else + orientation->set_heading(0); + if( iTilt >= 0 && poOgrFeat->IsFieldSet(iTilt) ) + orientation->set_tilt(poOgrFeat->GetFieldAsDouble(iTilt)); + else + orientation->set_tilt(0); + if( iRoll >= 0 && poOgrFeat->IsFieldSet(iRoll) ) + orientation->set_roll(poOgrFeat->GetFieldAsDouble(iRoll)); + else + orientation->set_roll(0); + } + int iScaleX = poOgrFeat->GetFieldIndex(oFC.scalexfield); + int iScaleY = poOgrFeat->GetFieldIndex(oFC.scaleyfield); + int iScaleZ = poOgrFeat->GetFieldIndex(oFC.scalezfield); + + ScalePtr scale = poKmlFactory->CreateScale(); + model->set_scale(scale); + if( iScaleX >= 0 && poOgrFeat->IsFieldSet(iScaleX) ) + scale->set_x(poOgrFeat->GetFieldAsDouble(iScaleX)); + else + scale->set_x(1.0); + if( iScaleY >= 0 && poOgrFeat->IsFieldSet(iScaleY) ) + scale->set_y(poOgrFeat->GetFieldAsDouble(iScaleY)); + else + scale->set_y(1.0); + if( iScaleZ >= 0 && poOgrFeat->IsFieldSet(iScaleZ) ) + scale->set_z(poOgrFeat->GetFieldAsDouble(iScaleZ)); + else + scale->set_z(1.0); + + LinkPtr link = poKmlFactory->CreateLink(); + model->set_link(link); + const char* pszURL = poOgrFeat->GetFieldAsString(oFC.modelfield); + link->set_href( pszURL ); + + /* Collada 3D file ? */ + if( EQUAL(CPLGetExtension(pszURL), "dae") && + CSLTestBoolean(CPLGetConfigOption("LIBKML_ADD_RESOURCE_MAP", "TRUE")) ) + { + VSILFILE* fp; + int bIsURL = FALSE; + if( EQUALN(pszURL, "http://", strlen("http://")) || + EQUALN(pszURL, "https://", strlen("https://")) ) + { + bIsURL = TRUE; + fp = VSIFOpenL(CPLSPrintf("/vsicurl/%s", pszURL), "rb"); + } + else if( strstr(pszURL, ".kmz/") != NULL ) + { + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s", pszURL), "rb"); + } + else + { + fp = VSIFOpenL(pszURL, "rb"); + } + if( fp != NULL ) + { + ResourceMapPtr resourceMap = NULL; + const char* pszLine; + while( (pszLine = CPLReadLineL(fp)) != NULL ) + { + const char* pszInitFrom = strstr(pszLine, ""); + if( pszInitFrom ) + { + pszInitFrom += strlen(""); + const char* pszInitFromEnd = strstr(pszInitFrom, ""); + if( pszInitFromEnd ) + { + CPLString osImage(pszInitFrom); + osImage.resize(pszInitFromEnd - pszInitFrom); + const char* pszExtension = CPLGetExtension(osImage); + if( EQUAL(pszExtension, "jpg") || + EQUAL(pszExtension, "jpeg") || + EQUAL(pszExtension, "png") || + EQUAL(pszExtension, "gif") ) + { + if( resourceMap == NULL ) + resourceMap = poKmlFactory->CreateResourceMap(); + AliasPtr alias = poKmlFactory->CreateAlias(); + if( bIsURL && CPLIsFilenameRelative(osImage) ) + { + if( strncmp(pszURL, "http", 4) == 0 ) + alias->set_targethref(CPLSPrintf("%s/%s", CPLGetPath(pszURL), osImage.c_str())); + else + alias->set_targethref(CPLFormFilename(CPLGetPath(pszURL), osImage, NULL)); + } + else + alias->set_targethref(osImage); + alias->set_sourcehref(osImage); + resourceMap->add_alias(alias); + } + } + } + } + if( resourceMap != NULL ) + model->set_resourcemap(resourceMap); + VSIFCloseL(fp); + } + } + + poKmlPlacemark->set_geometry ( AsGeometry ( model ) ); + } + + /* Camera */ + else if( poKmlFeature == NULL && poOgrGeom != NULL && !poOgrGeom->IsEmpty() && + wkbFlatten(poOgrGeom->getGeometryType()) == wkbPoint && + poOgrFeat->GetFieldIndex(oFC.camera_longitude_field) < 0 && + ((iHeading >= 0 && poOgrFeat->IsFieldSet(iHeading)) || + (iTilt >= 0 && poOgrFeat->IsFieldSet(iTilt)) || + (iRoll >= 0 && poOgrFeat->IsFieldSet(iRoll))) ) + { + PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark ( ); + poKmlFeature = poKmlPlacemark; + + OGRPoint* poOgrPoint = (OGRPoint*) poOgrGeom; + CameraPtr camera = poKmlFactory->CreateCamera(); + camera->set_latitude(poOgrPoint->getY()); + camera->set_longitude(poOgrPoint->getX()); + int isGX = FALSE; + int iAltitudeMode = poOgrFeat->GetFieldIndex(oFC.altitudeModefield); + int nAltitudeMode = kmldom::ALTITUDEMODE_CLAMPTOGROUND; + if( iAltitudeMode >= 0 && poOgrFeat->IsFieldSet(iAltitudeMode) ) + { + nAltitudeMode = kmlAltitudeModeFromString( + poOgrFeat->GetFieldAsString(iAltitudeMode), isGX); + camera->set_altitudemode(nAltitudeMode); + } + else if( CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + CPLError(CE_Warning, CPLE_AppDefined, "Camera should define altitudeMode != 'clampToGround'"); + if( poOgrPoint->getCoordinateDimension() == 3 ) + camera->set_altitude(poOgrPoint->getZ()); + else if( CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Camera should have an altitude/Z"); + camera->set_altitude(0.0); + } + if( iHeading >= 0 && poOgrFeat->IsFieldSet(iHeading) ) + camera->set_heading(poOgrFeat->GetFieldAsDouble(iHeading)); + if( iTilt >= 0 && poOgrFeat->IsFieldSet(iTilt) ) + camera->set_tilt(poOgrFeat->GetFieldAsDouble(iTilt)); + if( iRoll >= 0 && poOgrFeat->IsFieldSet(iRoll) ) + camera->set_roll(poOgrFeat->GetFieldAsDouble(iRoll)); + poKmlPlacemark->set_abstractview(camera); + } + + else if( poKmlFeature == NULL ) + { + PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark ( ); + poKmlFeature = poKmlPlacemark; + + ElementPtr poKmlElement = geom2kml ( poOgrGeom, -1, poKmlFactory ); + + poKmlPlacemark->set_geometry ( AsGeometry ( poKmlElement ) ); + } + + if( camera == NULL ) + camera = feat2kmlcamera(oFC, iHeading, iTilt, iRoll, poOgrFeat, poKmlFactory); + if( camera != NULL ) + poKmlFeature->set_abstractview(camera); + + /***** style *****/ + + featurestyle2kml ( poOgrDS, poOgrLayer, poOgrFeat, poKmlFactory, + poKmlFeature ); + + /***** fields *****/ + + field2kml ( poOgrFeat, ( OGRLIBKMLLayer * ) poOgrLayer, poKmlFactory, + poKmlFeature, bUseSimpleField ); + + return poKmlFeature; +} + +OGRFeature *kml2feat ( + PlacemarkPtr poKmlPlacemark, + OGRLIBKMLDataSource * poOgrDS, + OGRLayer * poOgrLayer, + OGRFeatureDefn * poOgrFeatDefn, + OGRSpatialReference *poOgrSRS) +{ + + OGRFeature *poOgrFeat = new OGRFeature ( poOgrFeatDefn ); + + /***** style *****/ + + kml2featurestyle ( poKmlPlacemark, poOgrDS, poOgrLayer, poOgrFeat ); + + /***** geometry *****/ + + if ( poKmlPlacemark->has_geometry ( ) ) { + OGRGeometry *poOgrGeom = + kml2geom ( poKmlPlacemark->get_geometry ( ), poOgrSRS ); + poOgrFeat->SetGeometryDirectly ( poOgrGeom ); + + } + else if ( poKmlPlacemark->has_abstractview ( ) && + poKmlPlacemark->get_abstractview()->IsA( kmldom::Type_Camera) ) { + const CameraPtr& camera = AsCamera(poKmlPlacemark->get_abstractview()); + if( camera->has_longitude() && camera->has_latitude() ) + { + if( camera->has_altitude() ) + poOgrFeat->SetGeometryDirectly( new OGRPoint( camera->get_longitude(), camera->get_latitude(), camera->get_altitude() ) ); + else + poOgrFeat->SetGeometryDirectly( new OGRPoint( camera->get_longitude(), camera->get_latitude() ) ); + poOgrFeat->GetGeometryRef()->assignSpatialReference( poOgrSRS ); + } + } + + /***** fields *****/ + + kml2field ( poOgrFeat, AsFeature ( poKmlPlacemark ) ); + + return poOgrFeat; +} + +OGRFeature *kmlgroundoverlay2feat ( + GroundOverlayPtr poKmlOverlay, + CPL_UNUSED OGRLIBKMLDataSource * poOgrDS, + CPL_UNUSED OGRLayer * poOgrLayer, + OGRFeatureDefn * poOgrFeatDefn, + OGRSpatialReference *poOgrSRS) +{ + + OGRFeature *poOgrFeat = new OGRFeature ( poOgrFeatDefn ); + + /***** style *****/ + + //kml2featurestyle ( poKmlPlacemark, poOgrDS, poOgrLayer, poOgrFeat ); + + /***** geometry *****/ + + if ( poKmlOverlay->has_latlonbox ( ) ) { + OGRGeometry *poOgrGeom = + kml2geom_latlonbox ( poKmlOverlay->get_latlonbox ( ), poOgrSRS ); + poOgrFeat->SetGeometryDirectly ( poOgrGeom ); + + } + else if ( poKmlOverlay->has_gx_latlonquad ( ) ) { + OGRGeometry *poOgrGeom = + kml2geom_latlonquad ( poKmlOverlay->get_gx_latlonquad ( ), poOgrSRS ); + poOgrFeat->SetGeometryDirectly ( poOgrGeom ); + + } + + /***** fields *****/ + + kml2field ( poOgrFeat, AsFeature ( poKmlOverlay ) ); + + return poOgrFeat; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfeature.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfeature.h new file mode 100644 index 000000000..e7c3f3b34 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfeature.h @@ -0,0 +1,65 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include + +using kmldom::KmlFactory; +using kmldom::FeaturePtr; +using kmldom::PlacemarkPtr; + +#include "ogr_libkml.h" + + +/****************************************************************************** + function to output a ogr feature to a kml placemark +******************************************************************************/ + +FeaturePtr feat2kml ( + OGRLIBKMLDataSource *poOgrDS, + OGRLayer * poKOgrLayer, + OGRFeature * poOgrFeat, + KmlFactory * poKmlFactory, + int bUseSimpleField ); + +/****************************************************************************** + function to read a kml placemark into a ogr feature +******************************************************************************/ + +OGRFeature *kml2feat ( + PlacemarkPtr poKmlPlacemark, + OGRLIBKMLDataSource * poOgrDS, + OGRLayer * poOgrLayer, + OGRFeatureDefn * poOgrFeatDefn, + OGRSpatialReference *poOgrSRS); + +OGRFeature *kmlgroundoverlay2feat ( + GroundOverlayPtr poKmlOverlay, + OGRLIBKMLDataSource * poOgrDS, + OGRLayer * poOgrLayer, + OGRFeatureDefn * poOgrFeatDefn, + OGRSpatialReference *poOgrSRS); diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfeaturestyle.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfeaturestyle.cpp new file mode 100644 index 000000000..1c322221a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfeaturestyle.cpp @@ -0,0 +1,416 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include +#include +#include +#include +using namespace std; + +#include + +using kmldom::KmlFactory;; +using kmldom::IconStylePtr; +using kmldom::PolyStylePtr; +using kmldom::LineStylePtr; +using kmldom::LabelStylePtr; +using kmldom::StylePtr; +using kmldom::Style; +using kmldom::StyleMapPtr; +using kmldom::StyleSelectorPtr; + + +#include "ogrlibkmlfeaturestyle.h" +#include "ogrlibkmlstyle.h" + +/****************************************************************************** + function to write out a features style to kml + +args: + poOgrLayer the layer the feature is in + poOgrFeat the feature + poKmlFactory the kml dom factory + poKmlFeature the placemark to add it to + +returns: + nothing +******************************************************************************/ + +void featurestyle2kml ( + OGRLIBKMLDataSource * poOgrDS, + OGRLayer * poOgrLayer, + OGRFeature * poOgrFeat, + KmlFactory * poKmlFactory, + FeaturePtr poKmlFeature ) +{ + + /***** get the style table *****/ + + OGRStyleTable *poOgrSTBL; + + const char *pszStyleString = poOgrFeat->GetStyleString ( ); + + /***** does the feature have style? *****/ + + if ( pszStyleString && pszStyleString[0] != '\0' ) { + + /***** does it ref a style table? *****/ + + if ( *pszStyleString == '@' ) { + + const char* pszStyleName = pszStyleString + 1; + + /***** is the name in the layer style table *****/ + + OGRStyleTable *hSTBLLayer; + const char *pszTest = NULL; + + if ( ( hSTBLLayer = poOgrLayer->GetStyleTable ( ) ) ) + pszTest = hSTBLLayer->Find ( pszStyleName ); + + if ( pszTest ) { + string oTmp = "#"; + + oTmp.append ( pszStyleName ); + + poKmlFeature->set_styleurl ( oTmp ); + } + + + /***** assume its a dataset style, mayby the user will add it later *****/ + + else { + string oTmp; + + if ( poOgrDS->GetStylePath ( ) ) + oTmp.append ( poOgrDS->GetStylePath ( ) ); + oTmp.append ( "#" ); + oTmp.append ( pszStyleName ); + + poKmlFeature->set_styleurl ( oTmp ); + } + } + + /***** no style table ref *****/ + + else { + /***** parse the style string *****/ + + StylePtr poKmlStyle = addstylestring2kml ( pszStyleString, NULL, poKmlFactory, + poKmlFeature ); + + /***** add the style to the placemark *****/ + if( poKmlStyle != NULL ) + poKmlFeature->set_styleselector ( poKmlStyle ); + + } + } + + /***** get the style table *****/ + + else if ( ( poOgrSTBL = poOgrFeat->GetStyleTable ( ) ) ) { + + + StylePtr poKmlStyle = NULL; + + /***** parse the style table *****/ + + poOgrSTBL->ResetStyleStringReading ( ); + const char *pszStyleString; + + while ( ( pszStyleString = poOgrSTBL->GetNextStyle ( ) ) ) { + + if ( *pszStyleString == '@' ) { + + const char* pszStyleName = pszStyleString + 1; + + /***** is the name in the layer style table *****/ + + OGRStyleTable *poOgrSTBLLayer; + const char *pszTest = NULL; + + if ( ( poOgrSTBLLayer = poOgrLayer->GetStyleTable ( ) ) ) + poOgrSTBLLayer->Find ( pszStyleName ); + + if ( pszTest ) { + string oTmp = "#"; + + oTmp.append ( pszStyleName ); + + poKmlFeature->set_styleurl ( oTmp ); + } + + /***** assume its a dataset style, *****/ + /***** mayby the user will add it later *****/ + + else { + string oTmp; + + if ( poOgrDS->GetStylePath ( ) ) + oTmp.append ( poOgrDS->GetStylePath ( ) ); + oTmp.append ( "#" ); + oTmp.append ( pszStyleName ); + + poKmlFeature->set_styleurl ( oTmp ); + } + } + + else { + + /***** parse the style string *****/ + + poKmlStyle = addstylestring2kml ( pszStyleString, poKmlStyle, + poKmlFactory, poKmlFeature ); + if( poKmlStyle != NULL ) + { + /***** add the style to the placemark *****/ + + poKmlFeature->set_styleselector ( poKmlStyle ); + } + + } + } + } +} + + +/****************************************************************************** + function to read a kml style into ogr's featurestyle +******************************************************************************/ + +void kml2featurestyle ( + FeaturePtr poKmlFeature, + OGRLIBKMLDataSource * poOgrDS, + OGRLayer * poOgrLayer, + OGRFeature * poOgrFeat ) +{ + + /***** does the placemark have a style url? *****/ + + if ( poKmlFeature->has_styleurl ( ) ) { + + const string poKmlStyleUrl = poKmlFeature->get_styleurl ( ); + + /***** is the name in the layer style table *****/ + + char *pszUrl = CPLStrdup ( poKmlStyleUrl.c_str ( ) ); + + OGRStyleTable *poOgrSTBLLayer; + const char *pszTest = NULL; + + /***** is it a layer style ? *****/ + + if ( *pszUrl == '#' + && ( poOgrSTBLLayer = poOgrLayer->GetStyleTable ( ) ) ) + { + pszTest = poOgrSTBLLayer->Find ( pszUrl + 1 ); + } + + if ( pszTest ) { + + /***** should we resolve the style *****/ + + const char *pszResolve = CPLGetConfigOption ( "LIBKML_RESOLVE_STYLE", "no" ); + + if (CSLTestBoolean(pszResolve)) { + + poOgrFeat->SetStyleString ( pszTest ); + } + + else { + + *pszUrl = '@'; + + poOgrFeat->SetStyleString( pszUrl ); + + } + + } + + /***** is it a dataset style? *****/ + + else { + + int nPathLen = strlen ( poOgrDS->GetStylePath ( ) ); + + if ( nPathLen == 0 + || EQUALN ( pszUrl, poOgrDS->GetStylePath ( ), nPathLen )) + { + + /***** should we resolve the style *****/ + + const char *pszResolve = CPLGetConfigOption ( "LIBKML_RESOLVE_STYLE", "no" ); + + if ( CSLTestBoolean(pszResolve) + && ( poOgrSTBLLayer = poOgrDS->GetStyleTable ( ) ) + && ( pszTest = poOgrSTBLLayer->Find ( pszUrl + nPathLen + 1) ) + ) + { + + poOgrFeat->SetStyleString ( pszTest ); + } + + else { + + pszUrl[nPathLen] = '@'; + poOgrFeat->SetStyleString ( pszUrl + nPathLen ); + } + + } + + /**** its someplace else *****/ + + else { + + const char *pszFetch = CPLGetConfigOption ( "LIBKML_EXTERNAL_STYLE", "no" ); + + if ( CSLTestBoolean(pszFetch) ) { + + /***** load up the style table *****/ + + char *pszUrlTmp = CPLStrdup(pszUrl); + char *pszPound; + if ((pszPound = strchr(pszUrlTmp, '#'))) { + + *pszPound = '\0'; + } + + /***** try it as a url then a file *****/ + + VSILFILE *fp = NULL; + + if ( (fp = VSIFOpenL( CPLFormFilename( "/vsicurl/", + pszUrlTmp, + NULL), + "r" )) + || (fp = VSIFOpenL( pszUrlTmp, "r" ))) + { + + char szbuf[1025]; + std::string oStyle = ""; + + /***** loop, read and copy to a string *****/ + + size_t nRead; + + do { + + nRead = VSIFReadL(szbuf, 1, sizeof(szbuf) - 1, fp); + + if (nRead == 0) + break; + + /***** copy buf to the string *****/ + + szbuf[nRead] = '\0'; + oStyle.append( szbuf ); + + } while (!VSIFEofL(fp)); + + VSIFCloseL(fp); + + /***** parse the kml into the ds style table *****/ + + if ( poOgrDS->ParseIntoStyleTable (&oStyle, pszUrlTmp)) { + + kml2featurestyle (poKmlFeature, + poOgrDS, + poOgrLayer, + poOgrFeat ); + } + + else { + + /***** if failed just store the url *****/ + + poOgrFeat->SetStyleString ( pszUrl ); + } + } + CPLFree(pszUrlTmp); + } + + else { + + poOgrFeat->SetStyleString ( pszUrl ); + } + } + + } + CPLFree ( pszUrl ); + + } + + /***** does the placemark have a style selector *****/ + + if ( poKmlFeature->has_styleselector ( ) ) { + + StyleSelectorPtr poKmlStyleSelector = + poKmlFeature->get_styleselector ( ); + + /***** is the style a style? *****/ + + if ( poKmlStyleSelector->IsA ( kmldom::Type_Style ) ) { + StylePtr poKmlStyle = AsStyle ( poKmlStyleSelector ); + + OGRStyleMgr *poOgrSM = new OGRStyleMgr; + + /***** if were resolveing style the feature *****/ + /***** might already have styling to add too *****/ + + const char *pszResolve = CPLGetConfigOption ( "LIBKML_RESOLVE_STYLE", "no" ); + if (CSLTestBoolean(pszResolve)) { + poOgrSM->InitFromFeature ( poOgrFeat ); + } + else { + + /***** if featyurestyle gets a name tool this needs changed to the above *****/ + + poOgrSM->InitStyleString ( NULL ); + } + + /***** read the style *****/ + + kml2stylestring ( poKmlStyle, poOgrSM ); + + /***** add the style to the feature *****/ + + poOgrFeat->SetStyleString(poOgrSM->GetStyleString(NULL)); + + delete poOgrSM; + } + + /***** is the style a stylemap? *****/ + + else if ( poKmlStyleSelector->IsA ( kmldom::Type_StyleMap ) ) { + /* todo need to figure out what to do with a style map */ + } + + + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfeaturestyle.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfeaturestyle.h new file mode 100644 index 000000000..842436291 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfeaturestyle.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "ogr_libkml.h" + +using kmldom::FeaturePtr; + +void featurestyle2kml ( + OGRLIBKMLDataSource *poOgrDS, + OGRLayer * poKOgrLayer, + OGRFeature * poOgrFeat, + KmlFactory * poKmlFactory, + FeaturePtr poKmlFeature ); + +/****************************************************************************** + function to read a kml style into ogr's featurestyle +******************************************************************************/ + +void kml2featurestyle ( + FeaturePtr poKmlFeature, + OGRLIBKMLDataSource *poOgrDS, + OGRLayer * poOgrLayer, + OGRFeature *poOgrFeat); diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfield.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfield.cpp new file mode 100644 index 000000000..cc1f38bed --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfield.cpp @@ -0,0 +1,1797 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include +#include +#include "ogr_p.h" + +#include +#include + +using kmldom::ExtendedDataPtr; +using kmldom::SchemaPtr; +using kmldom::SchemaDataPtr; +using kmldom::SimpleDataPtr; +using kmldom::DataPtr; + +using kmldom::TimeStampPtr; +using kmldom::TimeSpanPtr; +using kmldom::TimePrimitivePtr; +using kmldom::SnippetPtr; + +using kmldom::PointPtr; +using kmldom::LineStringPtr; +using kmldom::PolygonPtr; +using kmldom::MultiGeometryPtr; +using kmldom::GeometryPtr; + +using kmldom::FeaturePtr; +using kmldom::GroundOverlayPtr; +using kmldom::IconPtr; +using kmldom::CameraPtr; + +using kmldom::GxTrackPtr; +using kmldom::GxMultiTrackPtr; + +#include "ogr_libkml.h" + +#include "ogrlibkmlfield.h" + +void ogr2altitudemode_rec ( + GeometryPtr poKmlGeometry, + int iAltitudeMode, + int isGX ) +{ + + PointPtr poKmlPoint; + LineStringPtr poKmlLineString; + PolygonPtr poKmlPolygon; + MultiGeometryPtr poKmlMultiGeometry; + + size_t nGeom; + size_t i; + + switch ( poKmlGeometry->Type ( ) ) { + + case kmldom::Type_Point: + poKmlPoint = AsPoint ( poKmlGeometry ); + + if ( !isGX ) + poKmlPoint->set_altitudemode ( iAltitudeMode ); + else + poKmlPoint->set_gx_altitudemode ( iAltitudeMode ); + + break; + + case kmldom::Type_LineString: + poKmlLineString = AsLineString ( poKmlGeometry ); + + if ( !isGX ) + poKmlLineString->set_altitudemode ( iAltitudeMode ); + else + poKmlLineString->set_gx_altitudemode ( iAltitudeMode ); + + break; + + case kmldom::Type_LinearRing: + break; + + case kmldom::Type_Polygon: + poKmlPolygon = AsPolygon ( poKmlGeometry ); + + if ( !isGX ) + poKmlPolygon->set_altitudemode ( iAltitudeMode ); + else + poKmlPolygon->set_gx_altitudemode ( iAltitudeMode ); + + break; + + case kmldom::Type_MultiGeometry: + poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry ); + + nGeom = poKmlMultiGeometry->get_geometry_array_size ( ); + for ( i = 0; i < nGeom; i++ ) { + ogr2altitudemode_rec ( poKmlMultiGeometry-> + get_geometry_array_at ( i ), iAltitudeMode, + isGX ); + } + + break; + + default: + break; + + } + +} + +void ogr2extrude_rec ( + int nExtrude, + GeometryPtr poKmlGeometry ) +{ + + PointPtr poKmlPoint; + LineStringPtr poKmlLineString; + PolygonPtr poKmlPolygon; + MultiGeometryPtr poKmlMultiGeometry; + + size_t nGeom; + size_t i; + + switch ( poKmlGeometry->Type ( ) ) { + case kmldom::Type_Point: + poKmlPoint = AsPoint ( poKmlGeometry ); + poKmlPoint->set_extrude ( nExtrude ); + break; + + case kmldom::Type_LineString: + poKmlLineString = AsLineString ( poKmlGeometry ); + poKmlLineString->set_extrude ( nExtrude ); + break; + + case kmldom::Type_LinearRing: + break; + + case kmldom::Type_Polygon: + poKmlPolygon = AsPolygon ( poKmlGeometry ); + poKmlPolygon->set_extrude ( nExtrude ); + break; + + case kmldom::Type_MultiGeometry: + poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry ); + + nGeom = poKmlMultiGeometry->get_geometry_array_size ( ); + for ( i = 0; i < nGeom; i++ ) { + ogr2extrude_rec ( nExtrude, + poKmlMultiGeometry-> + get_geometry_array_at ( i ) ); + } + break; + + default: + break; + + } +} + +void ogr2tessellate_rec ( + int nTessellate, + GeometryPtr poKmlGeometry ) +{ + + LineStringPtr poKmlLineString; + PolygonPtr poKmlPolygon; + MultiGeometryPtr poKmlMultiGeometry; + + size_t nGeom; + size_t i; + + switch ( poKmlGeometry->Type ( ) ) { + + case kmldom::Type_Point: + break; + + case kmldom::Type_LineString: + poKmlLineString = AsLineString ( poKmlGeometry ); + poKmlLineString->set_tessellate ( nTessellate ); + break; + + case kmldom::Type_LinearRing: + break; + + case kmldom::Type_Polygon: + poKmlPolygon = AsPolygon ( poKmlGeometry ); + + poKmlPolygon->set_tessellate ( nTessellate ); + break; + + case kmldom::Type_MultiGeometry: + poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry ); + + nGeom = poKmlMultiGeometry->get_geometry_array_size ( ); + for ( i = 0; i < nGeom; i++ ) { + ogr2tessellate_rec ( nTessellate, + poKmlMultiGeometry-> + get_geometry_array_at ( i ) ); + } + + break; + + default: + break; + + } +} + + +/************************************************************************/ +/* OGRLIBKMLSanitizeUTF8String() */ +/************************************************************************/ + +static char* OGRLIBKMLSanitizeUTF8String(const char* pszString) +{ + if (!CPLIsUTF8(pszString, -1) && + CSLTestBoolean(CPLGetConfigOption("OGR_FORCE_ASCII", "YES"))) + { + static int bFirstTime = TRUE; + if (bFirstTime) + { + bFirstTime = FALSE; + CPLError(CE_Warning, CPLE_AppDefined, + "%s is not a valid UTF-8 string. Forcing it to ASCII.\n" + "If you still want the original string and change the XML file encoding\n" + "afterwards, you can define OGR_FORCE_ASCII=NO as configuration option.\n" + "This warning won't be issued anymore", pszString); + } + else + { + CPLDebug("OGR", "%s is not a valid UTF-8 string. Forcing it to ASCII", + pszString); + } + return CPLForceToASCII(pszString, -1, '?'); + } + else + return CPLStrdup(pszString); +} + +/****************************************************************************** + function to output ogr fields in kml + + args: + poOgrFeat pointer to the feature the field is in + poOgrLayer pointer to the layer the feature is in + poKmlFactory pointer to the libkml dom factory + poKmlPlacemark pointer to the placemark to add to + + returns: + nothing + + env vars: + LIBKML_TIMESTAMP_FIELD default: OFTDate or OFTDateTime named timestamp + LIBKML_TIMESPAN_BEGIN_FIELD default: OFTDate or OFTDateTime named begin + LIBKML_TIMESPAN_END_FIELD default: OFTDate or OFTDateTime named end + LIBKML_DESCRIPTION_FIELD default: none + LIBKML_NAME_FIELD default: OFTString field named name + + +******************************************************************************/ + + + +void field2kml ( + OGRFeature * poOgrFeat, + OGRLIBKMLLayer * poOgrLayer, + KmlFactory * poKmlFactory, + FeaturePtr poKmlFeature, + int bUseSimpleField) +{ + int i; + + ExtendedDataPtr poKmlExtendedData = NULL; + SchemaDataPtr poKmlSchemaData = NULL; + if( bUseSimpleField ) + { + poKmlSchemaData = poKmlFactory->CreateSchemaData ( ); + SchemaPtr poKmlSchema = poOgrLayer->GetKmlSchema ( ); + + /***** set the url to the schema *****/ + + if ( poKmlSchema && poKmlSchema->has_id ( ) ) { + std::string oKmlSchemaID = poKmlSchema->get_id ( ); + + + std::string oKmlSchemaURL = "#"; + oKmlSchemaURL.append ( oKmlSchemaID ); + + poKmlSchemaData->set_schemaurl ( oKmlSchemaURL ); + } + } + + /***** get the field config *****/ + + struct fieldconfig oFC; + get_fieldconfig( &oFC ); + + TimeSpanPtr poKmlTimeSpan = NULL; + + int nFields = poOgrFeat->GetFieldCount ( ); + int iSkip1 = -1; + int iSkip2 = -1; + int iAltitudeMode = kmldom::ALTITUDEMODE_CLAMPTOGROUND; + int isGX = FALSE; + + for ( i = 0; i < nFields; i++ ) { + + /***** if the field is set to skip, do so *****/ + + if ( i == iSkip1 || i == iSkip2 ) + continue; + + /***** if the field isn't set just bail now *****/ + + if ( !poOgrFeat->IsFieldSet ( i ) ) + continue; + + OGRFieldDefn *poOgrFieldDef = poOgrFeat->GetFieldDefnRef ( i ); + OGRFieldType type = poOgrFieldDef->GetType ( ); + const char *name = poOgrFieldDef->GetNameRef ( ); + + SimpleDataPtr poKmlSimpleData = NULL; + DataPtr poKmlData = NULL; + OGRField sFieldDT; + + switch ( type ) { + + case OFTString: // String of ASCII chars + { + char* pszUTF8String = OGRLIBKMLSanitizeUTF8String( + poOgrFeat->GetFieldAsString ( i )); + if( pszUTF8String[0] == '\0' ) + { + CPLFree( pszUTF8String ); + continue; + } + + /***** name *****/ + + if ( EQUAL ( name, oFC.namefield ) ) { + poKmlFeature->set_name ( pszUTF8String ); + CPLFree( pszUTF8String ); + continue; + } + + /***** description *****/ + + else if ( EQUAL ( name, oFC.descfield ) ) { + poKmlFeature->set_description ( pszUTF8String ); + CPLFree( pszUTF8String ); + continue; + } + + /***** altitudemode *****/ + + else if ( EQUAL ( name, oFC.altitudeModefield ) ) { + const char *pszAltitudeMode = pszUTF8String ; + + iAltitudeMode = kmlAltitudeModeFromString(pszAltitudeMode, isGX); + + if ( poKmlFeature->IsA ( kmldom::Type_Placemark ) ) { + PlacemarkPtr poKmlPlacemark = AsPlacemark ( poKmlFeature ); + if ( poKmlPlacemark->has_geometry ( ) ) { + GeometryPtr poKmlGeometry = + poKmlPlacemark->get_geometry ( ); + + ogr2altitudemode_rec ( poKmlGeometry, iAltitudeMode, + isGX ); + + } + } + + CPLFree( pszUTF8String ); + + continue; + } + + /***** timestamp *****/ + + else if ( EQUAL ( name, oFC.tsfield ) ) { + + TimeStampPtr poKmlTimeStamp = + poKmlFactory->CreateTimeStamp ( ); + poKmlTimeStamp->set_when ( pszUTF8String ); + poKmlFeature->set_timeprimitive ( poKmlTimeStamp ); + + CPLFree( pszUTF8String ); + + continue; + } + + /***** begin *****/ + + if ( EQUAL ( name, oFC.beginfield ) ) { + + if ( !poKmlTimeSpan ) { + poKmlTimeSpan = poKmlFactory->CreateTimeSpan ( ); + poKmlFeature->set_timeprimitive ( poKmlTimeSpan ); + } + + poKmlTimeSpan->set_begin ( pszUTF8String ); + + CPLFree( pszUTF8String ); + + continue; + + } + + /***** end *****/ + + else if ( EQUAL ( name, oFC.endfield ) ) { + + if ( !poKmlTimeSpan ) { + poKmlTimeSpan = poKmlFactory->CreateTimeSpan ( ); + poKmlFeature->set_timeprimitive ( poKmlTimeSpan ); + } + + poKmlTimeSpan->set_end ( pszUTF8String ); + + CPLFree( pszUTF8String ); + + continue; + } + + /***** snippet *****/ + + else if ( EQUAL ( name, oFC.snippetfield ) ) { + + SnippetPtr snippet = poKmlFactory->CreateSnippet ( ); + snippet->set_text(pszUTF8String); + poKmlFeature->set_snippet ( snippet ); + + CPLFree( pszUTF8String ); + + continue; + + } + + /***** other special fields *****/ + + else if ( EQUAL ( name, oFC.iconfield ) || + EQUAL ( name, oFC.modelfield ) || + EQUAL ( name, oFC.networklinkfield ) || + EQUAL ( name, oFC.networklink_refreshMode_field ) || + EQUAL ( name, oFC.networklink_viewRefreshMode_field ) || + EQUAL ( name, oFC.networklink_viewFormat_field ) || + EQUAL ( name, oFC.networklink_httpQuery_field ) || + EQUAL ( name, oFC.camera_altitudemode_field ) || + EQUAL ( name, oFC.photooverlayfield ) || + EQUAL ( name, oFC.photooverlay_shape_field ) || + EQUAL ( name, oFC.imagepyramid_gridorigin_field ) ) { + + CPLFree( pszUTF8String ); + + continue; + } + + /***** other *****/ + + if( bUseSimpleField ) + { + poKmlSimpleData = poKmlFactory->CreateSimpleData ( ); + poKmlSimpleData->set_name ( name ); + poKmlSimpleData->set_text ( pszUTF8String ); + } + else + { + poKmlData = poKmlFactory->CreateData ( ); + poKmlData->set_name ( name ); + poKmlData->set_value ( pszUTF8String ); + } + + CPLFree( pszUTF8String ); + + break; + } + + /* This code checks if there's a OFTTime field with the same name */ + /* that could be used to compose a DateTime. Not sure this is really */ + /* supported in OGR data model to have 2 fields with same name... */ + case OFTDate: // Date + { + memcpy(&sFieldDT, poOgrFeat->GetRawFieldRef(i), sizeof(OGRField)); + + int iTimeField; + + for ( iTimeField = i + 1; iTimeField < nFields; iTimeField++ ) { + if ( iTimeField == iSkip1 || iTimeField == iSkip2 ) + continue; + + OGRFieldDefn *poOgrFieldDef2 = + poOgrFeat->GetFieldDefnRef ( i ); + OGRFieldType type2 = poOgrFieldDef2->GetType ( ); + const char *name2 = poOgrFieldDef2->GetNameRef ( ); + + if ( EQUAL ( name2, name ) && type2 == OFTTime && + ( EQUAL ( name, oFC.tsfield ) || + EQUAL ( name, oFC.beginfield ) || + EQUAL ( name, oFC.endfield ) ) ) { + + const OGRField* psField2 = poOgrFeat->GetRawFieldRef(iTimeField); + sFieldDT.Date.Hour = psField2->Date.Hour; + sFieldDT.Date.Minute = psField2->Date.Minute; + sFieldDT.Date.Second = psField2->Date.Second; + sFieldDT.Date.TZFlag = psField2->Date.TZFlag; + + if ( 0 > iSkip1 ) + iSkip1 = iTimeField; + else + iSkip2 = iTimeField; + } + } + + goto Do_DateTime; + + } + + /* This code checks if there's a OFTTime field with the same name */ + /* that could be used to compose a DateTime. Not sure this is really */ + /* supported in OGR data model to have 2 fields with same name... */ + case OFTTime: // Time + { + memcpy(&sFieldDT, poOgrFeat->GetRawFieldRef(i), sizeof(OGRField)); + + int iTimeField; + + for ( iTimeField = i + 1; iTimeField < nFields; iTimeField++ ) { + if ( iTimeField == iSkip1 || iTimeField == iSkip2 ) + continue; + + OGRFieldDefn *poOgrFieldDef2 = + poOgrFeat->GetFieldDefnRef ( i ); + OGRFieldType type2 = poOgrFieldDef2->GetType ( ); + const char *name2 = poOgrFieldDef2->GetNameRef ( ); + + if ( EQUAL ( name2, name ) && type2 == OFTDate && + ( EQUAL ( name, oFC.tsfield ) || + EQUAL ( name, oFC.beginfield ) || + EQUAL ( name, oFC.endfield ) ) ) { + + const OGRField* psField2 = poOgrFeat->GetRawFieldRef(iTimeField); + sFieldDT.Date.Year = psField2->Date.Year; + sFieldDT.Date.Month = psField2->Date.Month; + sFieldDT.Date.Day = psField2->Date.Day; + + if ( 0 > iSkip1 ) + iSkip1 = iTimeField; + else + iSkip2 = iTimeField; + } + } + + goto Do_DateTime; + + } + + case OFTDateTime: // Date and Time + { + memcpy(&sFieldDT, poOgrFeat->GetRawFieldRef(i), sizeof(OGRField)); + + Do_DateTime: + /***** timestamp *****/ + + if ( EQUAL ( name, oFC.tsfield ) ) { + + char *timebuf = OGRGetXMLDateTime ( &sFieldDT ); + + TimeStampPtr poKmlTimeStamp = + poKmlFactory->CreateTimeStamp ( ); + poKmlTimeStamp->set_when ( timebuf ); + poKmlFeature->set_timeprimitive ( poKmlTimeStamp ); + CPLFree( timebuf ); + + continue; + } + + /***** begin *****/ + + if ( EQUAL ( name, oFC.beginfield ) ) { + + char *timebuf = OGRGetXMLDateTime ( &sFieldDT ); + + if ( !poKmlTimeSpan ) { + poKmlTimeSpan = poKmlFactory->CreateTimeSpan ( ); + poKmlFeature->set_timeprimitive ( poKmlTimeSpan ); + } + + poKmlTimeSpan->set_begin ( timebuf ); + CPLFree( timebuf ); + + continue; + + } + + /***** end *****/ + + else if ( EQUAL ( name, oFC.endfield ) ) { + + char *timebuf = OGRGetXMLDateTime ( &sFieldDT ); + + + if ( !poKmlTimeSpan ) { + poKmlTimeSpan = poKmlFactory->CreateTimeSpan ( ); + poKmlFeature->set_timeprimitive ( poKmlTimeSpan ); + } + + poKmlTimeSpan->set_end ( timebuf ); + CPLFree( timebuf ); + + continue; + } + + /***** other *****/ + + if( bUseSimpleField ) + { + poKmlSimpleData = poKmlFactory->CreateSimpleData ( ); + poKmlSimpleData->set_name ( name ); + poKmlSimpleData->set_text ( poOgrFeat-> + GetFieldAsString ( i ) ); + } + else + { + poKmlData = poKmlFactory->CreateData ( ); + poKmlData->set_name ( name ); + poKmlData->set_value ( poOgrFeat-> + GetFieldAsString ( i ) ); + } + + break; + } + + case OFTInteger: // Simple 32bit integer + + /***** extrude *****/ + + if ( EQUAL ( name, oFC.extrudefield ) ) { + + if ( poKmlFeature->IsA ( kmldom::Type_Placemark ) ) { + PlacemarkPtr poKmlPlacemark = AsPlacemark ( poKmlFeature ); + if ( poKmlPlacemark->has_geometry ( ) + && -1 < poOgrFeat->GetFieldAsInteger ( i ) ) { + int iExtrude = poOgrFeat->GetFieldAsInteger ( i ); + if( iExtrude && + isGX == FALSE && iAltitudeMode == kmldom::ALTITUDEMODE_CLAMPTOGROUND && + CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "altitudeMode=clampToGround unsupported with extrude=1"); + } + else + { + GeometryPtr poKmlGeometry = + poKmlPlacemark->get_geometry ( ); + ogr2extrude_rec ( iExtrude, + poKmlGeometry ); + } + } + } + continue; + } + + /***** tessellate *****/ + + + if ( EQUAL ( name, oFC.tessellatefield ) ) { + + if ( poKmlFeature->IsA ( kmldom::Type_Placemark ) ) { + PlacemarkPtr poKmlPlacemark = AsPlacemark ( poKmlFeature ); + if ( poKmlPlacemark->has_geometry ( ) + && -1 < poOgrFeat->GetFieldAsInteger ( i ) ) { + int iTesselate = poOgrFeat->GetFieldAsInteger ( i ); + if( iTesselate && + !(isGX == FALSE && iAltitudeMode == kmldom::ALTITUDEMODE_CLAMPTOGROUND) && + !(isGX == TRUE && iAltitudeMode == kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR) && + CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "altitudeMode!=clampToGround && altitudeMode!=clampToSeaFloor unsupported with tesselate=1"); + } + else + { + GeometryPtr poKmlGeometry = + poKmlPlacemark->get_geometry ( ); + ogr2tessellate_rec ( iTesselate, + poKmlGeometry ); + if( isGX == FALSE && iAltitudeMode == kmldom::ALTITUDEMODE_CLAMPTOGROUND ) + ogr2altitudemode_rec ( poKmlGeometry, iAltitudeMode, + isGX ); + } + } + } + + continue; + } + + + /***** visibility *****/ + + if ( EQUAL ( name, oFC.visibilityfield ) ) { + if ( -1 < poOgrFeat->GetFieldAsInteger ( i ) ) + poKmlFeature->set_visibility ( poOgrFeat-> + GetFieldAsInteger ( i ) ); + + continue; + } + + + /***** other special fields *****/ + + else if ( EQUAL ( name, oFC.drawOrderfield ) || + EQUAL ( name, oFC.networklink_refreshvisibility_field ) || + EQUAL ( name, oFC.networklink_flytoview_field ) || + EQUAL ( name, oFC.networklink_refreshInterval_field ) || + EQUAL ( name, oFC.networklink_viewRefreshMode_field ) || + EQUAL ( name, oFC.networklink_viewRefreshTime_field ) || + EQUAL ( name, oFC.imagepyramid_tilesize_field ) || + EQUAL ( name, oFC.imagepyramid_maxwidth_field ) || + EQUAL ( name, oFC.imagepyramid_maxheight_field ) ) { + + continue; + } + + /***** other *****/ + + if( bUseSimpleField ) + { + poKmlSimpleData = poKmlFactory->CreateSimpleData ( ); + poKmlSimpleData->set_name ( name ); + poKmlSimpleData->set_text ( poOgrFeat->GetFieldAsString ( i ) ); + } + else + { + poKmlData = poKmlFactory->CreateData ( ); + poKmlData->set_name ( name ); + poKmlData->set_value ( poOgrFeat->GetFieldAsString ( i ) ); + } + + break; + + case OFTReal: // Double Precision floating point + { + if( EQUAL(name, oFC.headingfield) || + EQUAL(name, oFC.tiltfield) || + EQUAL(name, oFC.rollfield) || + EQUAL(name, oFC.scalexfield) || + EQUAL(name, oFC.scaleyfield) || + EQUAL(name, oFC.scalezfield) || + EQUAL(name, oFC.networklink_refreshInterval_field ) || + EQUAL(name, oFC.networklink_viewRefreshMode_field) || + EQUAL(name, oFC.networklink_viewRefreshTime_field) || + EQUAL(name, oFC.networklink_viewBoundScale_field) || + EQUAL(name, oFC.camera_longitude_field) || + EQUAL(name, oFC.camera_latitude_field) || + EQUAL(name, oFC.camera_altitude_field) || + EQUAL(name, oFC.leftfovfield) || + EQUAL(name, oFC.rightfovfield) || + EQUAL(name, oFC.bottomfovfield) || + EQUAL(name, oFC.topfovfield) || + EQUAL(name, oFC.nearfield) || + EQUAL(name, oFC.camera_altitude_field) ) + { + continue; + } + + char* pszStr = CPLStrdup( poOgrFeat->GetFieldAsString ( i ) ); + + if( bUseSimpleField ) + { + poKmlSimpleData = poKmlFactory->CreateSimpleData ( ); + poKmlSimpleData->set_name ( name ); + poKmlSimpleData->set_text ( pszStr ); + } + else + { + poKmlData = poKmlFactory->CreateData ( ); + poKmlData->set_name ( name ); + poKmlData->set_value ( pszStr ); + } + + CPLFree(pszStr); + + break; + } + + case OFTStringList: // Array of strings + case OFTIntegerList: // List of 32bit integers + case OFTRealList: // List of doubles + case OFTBinary: // Raw Binary data + case OFTWideStringList: // deprecated + default: + + /***** other *****/ + + if( bUseSimpleField ) + { + poKmlSimpleData = poKmlFactory->CreateSimpleData ( ); + poKmlSimpleData->set_name ( name ); + poKmlSimpleData->set_text ( poOgrFeat->GetFieldAsString ( i ) ); + } + else + { + poKmlData = poKmlFactory->CreateData ( ); + poKmlData->set_name ( name ); + poKmlData->set_value ( poOgrFeat->GetFieldAsString ( i ) ); + } + + break; + } + + if( poKmlSimpleData ) + { + poKmlSchemaData->add_simpledata ( poKmlSimpleData ); + } + else if( poKmlData ) + { + if( poKmlExtendedData == NULL ) + poKmlExtendedData = poKmlFactory->CreateExtendedData ( ); + poKmlExtendedData->add_data ( poKmlData ); + } + } + + /***** dont add it to the placemark unless there is data *****/ + + if ( bUseSimpleField && poKmlSchemaData->get_simpledata_array_size ( ) > 0 ) { + poKmlExtendedData = poKmlFactory->CreateExtendedData ( ); + poKmlExtendedData->add_schemadata ( poKmlSchemaData ); + } + if( poKmlExtendedData != NULL ) + { + poKmlFeature->set_extendeddata ( poKmlExtendedData ); + } + + return; +} + +/****************************************************************************** + recursive function to read altitude mode from the geometry +******************************************************************************/ + +int kml2altitudemode_rec ( + GeometryPtr poKmlGeometry, + int *pnAltitudeMode, + int *pbIsGX ) +{ + + PointPtr poKmlPoint; + LineStringPtr poKmlLineString; + PolygonPtr poKmlPolygon; + MultiGeometryPtr poKmlMultiGeometry; + + size_t nGeom; + size_t i; + + switch ( poKmlGeometry->Type ( ) ) { + + case kmldom::Type_Point: + poKmlPoint = AsPoint ( poKmlGeometry ); + + if ( poKmlPoint->has_altitudemode ( ) ) { + *pnAltitudeMode = poKmlPoint->get_altitudemode ( ); + return TRUE; + } + else if ( poKmlPoint->has_gx_altitudemode ( ) ) { + *pnAltitudeMode = poKmlPoint->get_gx_altitudemode ( ); + *pbIsGX = TRUE; + return TRUE; + } + + break; + + case kmldom::Type_LineString: + poKmlLineString = AsLineString ( poKmlGeometry ); + + if ( poKmlLineString->has_altitudemode ( ) ) { + *pnAltitudeMode = poKmlLineString->get_altitudemode ( ); + return TRUE; + } + else if ( poKmlLineString->has_gx_altitudemode ( ) ) { + *pnAltitudeMode = poKmlLineString->get_gx_altitudemode ( ); + *pbIsGX = TRUE; + return TRUE; + } + break; + + case kmldom::Type_LinearRing: + break; + + case kmldom::Type_Polygon: + poKmlPolygon = AsPolygon ( poKmlGeometry ); + + if ( poKmlPolygon->has_altitudemode ( ) ) { + *pnAltitudeMode = poKmlPolygon->get_altitudemode ( ); + return TRUE; + } + else if ( poKmlPolygon->has_gx_altitudemode ( ) ) { + *pnAltitudeMode = poKmlPolygon->get_gx_altitudemode ( ); + *pbIsGX = TRUE; + return TRUE; + } + + break; + + case kmldom::Type_MultiGeometry: + poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry ); + + nGeom = poKmlMultiGeometry->get_geometry_array_size ( ); + for ( i = 0; i < nGeom; i++ ) { + if ( kml2altitudemode_rec ( poKmlMultiGeometry-> + get_geometry_array_at ( i ), + pnAltitudeMode, pbIsGX ) ) + return TRUE; + } + + break; + + default: + break; + + } + + return FALSE; +} + +/****************************************************************************** + recursive function to read extrude from the geometry +******************************************************************************/ + +int kml2extrude_rec ( + GeometryPtr poKmlGeometry, + int *pnExtrude ) +{ + + PointPtr poKmlPoint; + LineStringPtr poKmlLineString; + PolygonPtr poKmlPolygon; + MultiGeometryPtr poKmlMultiGeometry; + + size_t nGeom; + size_t i; + + switch ( poKmlGeometry->Type ( ) ) { + + case kmldom::Type_Point: + poKmlPoint = AsPoint ( poKmlGeometry ); + + if ( poKmlPoint->has_extrude ( ) ) { + *pnExtrude = poKmlPoint->get_extrude ( ); + return TRUE; + } + + break; + + case kmldom::Type_LineString: + poKmlLineString = AsLineString ( poKmlGeometry ); + + if ( poKmlLineString->has_extrude ( ) ) { + *pnExtrude = poKmlLineString->get_extrude ( ); + return TRUE; + } + + break; + + case kmldom::Type_LinearRing: + break; + + case kmldom::Type_Polygon: + poKmlPolygon = AsPolygon ( poKmlGeometry ); + + if ( poKmlPolygon->has_extrude ( ) ) { + *pnExtrude = poKmlPolygon->get_extrude ( ); + return TRUE; + } + + break; + + case kmldom::Type_MultiGeometry: + poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry ); + + nGeom = poKmlMultiGeometry->get_geometry_array_size ( ); + for ( i = 0; i < nGeom; i++ ) { + if ( kml2extrude_rec ( poKmlMultiGeometry-> + get_geometry_array_at ( i ), pnExtrude ) ) + return TRUE; + } + + break; + + default: + break; + + } + + return FALSE; +} + +/****************************************************************************** + recursive function to read tessellate from the geometry +******************************************************************************/ + +int kml2tessellate_rec ( + GeometryPtr poKmlGeometry, + int *pnTessellate ) +{ + + LineStringPtr poKmlLineString; + PolygonPtr poKmlPolygon; + MultiGeometryPtr poKmlMultiGeometry; + + size_t nGeom; + size_t i; + + switch ( poKmlGeometry->Type ( ) ) { + + case kmldom::Type_Point: + break; + + case kmldom::Type_LineString: + poKmlLineString = AsLineString ( poKmlGeometry ); + + if ( poKmlLineString->has_tessellate ( ) ) { + *pnTessellate = poKmlLineString->get_tessellate ( ); + return TRUE; + } + + break; + + case kmldom::Type_LinearRing: + break; + + case kmldom::Type_Polygon: + poKmlPolygon = AsPolygon ( poKmlGeometry ); + + if ( poKmlPolygon->has_tessellate ( ) ) { + *pnTessellate = poKmlPolygon->get_tessellate ( ); + return TRUE; + } + + break; + + case kmldom::Type_MultiGeometry: + poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry ); + + nGeom = poKmlMultiGeometry->get_geometry_array_size ( ); + for ( i = 0; i < nGeom; i++ ) { + if ( kml2tessellate_rec ( poKmlMultiGeometry-> + get_geometry_array_at ( i ), + pnTessellate ) ) + return TRUE; + } + + break; + + default: + break; + + } + + return FALSE; +} + +/************************************************************************/ +/* ogrkmlSetAltitudeMode() */ +/************************************************************************/ + +static void ogrkmlSetAltitudeMode(OGRFeature* poOgrFeat, int iField, + int nAltitudeMode, int bIsGX) +{ + if ( !bIsGX ) { + switch ( nAltitudeMode ) { + case kmldom::ALTITUDEMODE_CLAMPTOGROUND: + poOgrFeat->SetField ( iField, "clampToGround" ); + break; + + case kmldom::ALTITUDEMODE_RELATIVETOGROUND: + poOgrFeat->SetField ( iField, "relativeToGround" ); + break; + + case kmldom::ALTITUDEMODE_ABSOLUTE: + poOgrFeat->SetField ( iField, "absolute" ); + break; + + } + } + + else { + switch ( nAltitudeMode ) { + case kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR: + poOgrFeat->SetField ( iField, "relativeToSeaFloor" ); + break; + + case kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR: + poOgrFeat->SetField ( iField, "clampToSeaFloor" ); + break; + } + } +} + +/************************************************************************/ +/* TrimSpaces() */ +/************************************************************************/ + +static const char* TrimSpaces(string& oText) +{ + + /* SerializePretty() adds a new line before the data */ + /* ands trailing spaces. I believe this is wrong */ + /* as it breaks round-tripping */ + + /* Trim trailing spaces */ + while (oText.size() != 0 && oText[oText.size()-1] == ' ') + oText.resize(oText.size()-1); + + /* Skip leading newline and spaces */ + const char* pszText = oText.c_str ( ); + if (pszText[0] == '\n') + pszText ++; + while (pszText[0] == ' ') + pszText ++; + + return pszText; +} + +/************************************************************************/ +/* kmldatetime2ogr() */ +/************************************************************************/ + +static void kmldatetime2ogr( OGRFeature* poOgrFeat, + const char* pszOGRField, + const std::string& osKmlDateTime ) +{ + int iField = poOgrFeat->GetFieldIndex ( pszOGRField ); + + if ( iField > -1 ) { + OGRField sField; + + if ( OGRParseXMLDateTime( osKmlDateTime.c_str ( ), &sField ) ) + poOgrFeat->SetField ( iField, &sField ); + } +} + +/****************************************************************************** + function to read kml into ogr fields +******************************************************************************/ + +void kml2field ( + OGRFeature * poOgrFeat, + FeaturePtr poKmlFeature ) +{ + + /***** get the field config *****/ + + struct fieldconfig oFC; + get_fieldconfig( &oFC ); + + /***** name *****/ + + if ( poKmlFeature->has_name ( ) ) { + const std::string oKmlName = poKmlFeature->get_name ( ); + int iField = poOgrFeat->GetFieldIndex ( oFC.namefield ); + + if ( iField > -1 ) + poOgrFeat->SetField ( iField, oKmlName.c_str ( ) ); + } + + /***** description *****/ + + if ( poKmlFeature->has_description ( ) ) { + const std::string oKmlDesc = poKmlFeature->get_description ( ); + int iField = poOgrFeat->GetFieldIndex ( oFC.descfield ); + + if ( iField > -1 ) + poOgrFeat->SetField ( iField, oKmlDesc.c_str ( ) ); + } + + if ( poKmlFeature->has_timeprimitive ( ) ) { + TimePrimitivePtr poKmlTimePrimitive = + poKmlFeature->get_timeprimitive ( ); + + /***** timestamp *****/ + + if ( poKmlTimePrimitive->IsA ( kmldom::Type_TimeStamp ) ) { + TimeStampPtr poKmlTimeStamp = AsTimeStamp ( poKmlTimePrimitive ); + + if ( poKmlTimeStamp->has_when ( ) ) { + const std::string oKmlWhen = poKmlTimeStamp->get_when ( ); + kmldatetime2ogr(poOgrFeat, oFC.tsfield, oKmlWhen ); + } + } + + /***** timespan *****/ + + if ( poKmlTimePrimitive->IsA ( kmldom::Type_TimeSpan ) ) { + TimeSpanPtr poKmlTimeSpan = AsTimeSpan ( poKmlTimePrimitive ); + + /***** begin *****/ + + if ( poKmlTimeSpan->has_begin ( ) ) { + const std::string oKmlWhen = poKmlTimeSpan->get_begin ( ); + kmldatetime2ogr(poOgrFeat, oFC.beginfield, oKmlWhen ); + } + + /***** end *****/ + + if ( poKmlTimeSpan->has_end ( ) ) { + const std::string oKmlWhen = poKmlTimeSpan->get_end ( ); + kmldatetime2ogr(poOgrFeat, oFC.endfield, oKmlWhen ); + } + } + } + + /***** placemark *****/ + + PlacemarkPtr poKmlPlacemark = AsPlacemark ( poKmlFeature ); + GroundOverlayPtr poKmlGroundOverlay = AsGroundOverlay ( poKmlFeature ); + if ( poKmlPlacemark && poKmlPlacemark->has_geometry ( ) ) { + GeometryPtr poKmlGeometry = poKmlPlacemark->get_geometry ( ); + + /***** altitudeMode *****/ + + + int bIsGX = FALSE; + int nAltitudeMode = -1; + + int iField = poOgrFeat->GetFieldIndex ( oFC.altitudeModefield ); + + if ( iField > -1 ) { + + if ( kml2altitudemode_rec ( poKmlGeometry, + &nAltitudeMode, &bIsGX ) ) { + ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode, bIsGX); + } + + } + + /***** tessellate *****/ + + int nTessellate = -1; + + kml2tessellate_rec ( poKmlGeometry, &nTessellate ); + + iField = poOgrFeat->GetFieldIndex ( oFC.tessellatefield ); + if ( iField > -1 ) + poOgrFeat->SetField ( iField, nTessellate ); + + /***** extrude *****/ + + int nExtrude = -1; + + kml2extrude_rec ( poKmlGeometry, &nExtrude ); + + iField = poOgrFeat->GetFieldIndex ( oFC.extrudefield ); + if ( iField > -1 ) + poOgrFeat->SetField ( iField, nExtrude ); + + /***** special case for gx:Track ******/ + /* we set the first timestamp as begin and the last one as end */ + if ( poKmlGeometry->Type ( ) == kmldom::Type_GxTrack && + !poKmlFeature->has_timeprimitive ( ) ) { + GxTrackPtr poKmlGxTrack = AsGxTrack ( poKmlGeometry ); + size_t nCoords = poKmlGxTrack->get_gx_coord_array_size(); + if( nCoords > 0 ) + { + kmldatetime2ogr(poOgrFeat, oFC.beginfield, + poKmlGxTrack->get_when_array_at ( 0 ).c_str() ); + kmldatetime2ogr(poOgrFeat, oFC.endfield, + poKmlGxTrack->get_when_array_at ( nCoords - 1 ).c_str() ); + } + } + + /***** special case for gx:MultiTrack ******/ + /* we set the first timestamp as begin and the last one as end */ + else if ( poKmlGeometry->Type ( ) == kmldom::Type_GxMultiTrack && + !poKmlFeature->has_timeprimitive ( ) ) { + GxMultiTrackPtr poKmlGxMultiTrack = AsGxMultiTrack ( poKmlGeometry ); + size_t nGeom = poKmlGxMultiTrack->get_gx_track_array_size ( ); + if( nGeom >= 1 ) + { + GxTrackPtr poKmlGxTrack = poKmlGxMultiTrack->get_gx_track_array_at ( 0 ); + size_t nCoords = poKmlGxTrack->get_gx_coord_array_size(); + if( nCoords > 0 ) + { + kmldatetime2ogr(poOgrFeat, oFC.beginfield, + poKmlGxTrack->get_when_array_at ( 0 ).c_str() ); + } + + poKmlGxTrack = poKmlGxMultiTrack->get_gx_track_array_at (nGeom -1); + nCoords = poKmlGxTrack->get_gx_coord_array_size(); + if( nCoords > 0 ) + { + kmldatetime2ogr(poOgrFeat, oFC.endfield, + poKmlGxTrack->get_when_array_at ( nCoords - 1 ).c_str() ); + } + } + } + } + + /***** camera *****/ + + else if ( poKmlPlacemark && + poKmlPlacemark->has_abstractview ( ) && + poKmlPlacemark->get_abstractview()->IsA( kmldom::Type_Camera) ) { + + const CameraPtr& camera = AsCamera(poKmlPlacemark->get_abstractview()); + + if( camera->has_heading() ) + { + int iField = poOgrFeat->GetFieldIndex ( oFC.headingfield ); + if ( iField > -1 ) + poOgrFeat->SetField ( iField, camera->get_heading() ); + } + + if( camera->has_tilt() ) + { + int iField = poOgrFeat->GetFieldIndex ( oFC.tiltfield ); + if ( iField > -1 ) + poOgrFeat->SetField ( iField, camera->get_tilt() ); + } + + if( camera->has_roll() ) + { + int iField = poOgrFeat->GetFieldIndex ( oFC.rollfield ); + if ( iField > -1 ) + poOgrFeat->SetField ( iField, camera->get_roll() ); + } + + int nAltitudeMode = -1; + + int iField = poOgrFeat->GetFieldIndex ( oFC.altitudeModefield ); + + if ( iField > -1 ) { + + if ( camera->has_altitudemode ( ) ) { + nAltitudeMode = camera->get_altitudemode ( ); + ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode, FALSE); + } + else if ( camera->has_gx_altitudemode ( ) ) { + nAltitudeMode = camera->get_gx_altitudemode ( ); + ogrkmlSetAltitudeMode(poOgrFeat, iField, nAltitudeMode, TRUE); + } + } + } + + /***** ground overlay *****/ + + else if ( poKmlGroundOverlay ) { + + /***** icon *****/ + + int iField = poOgrFeat->GetFieldIndex ( oFC.iconfield ); + if ( iField > -1 ) { + + if ( poKmlGroundOverlay->has_icon ( ) ) { + IconPtr icon = poKmlGroundOverlay->get_icon ( ); + if ( icon->has_href ( ) ) { + poOgrFeat->SetField ( iField, icon->get_href ( ).c_str ( ) ); + } + } + } + + /***** drawOrder *****/ + + + iField = poOgrFeat->GetFieldIndex ( oFC.drawOrderfield ); + if ( iField > -1 ) { + + if ( poKmlGroundOverlay->has_draworder ( ) ) { + poOgrFeat->SetField ( iField, poKmlGroundOverlay->get_draworder ( ) ); + } + } + + /***** altitudeMode *****/ + + iField = poOgrFeat->GetFieldIndex ( oFC.altitudeModefield ); + + if ( iField > -1 ) { + + if ( poKmlGroundOverlay->has_altitudemode ( ) ) { + switch ( poKmlGroundOverlay->get_altitudemode ( ) ) { + case kmldom::ALTITUDEMODE_CLAMPTOGROUND: + poOgrFeat->SetField ( iField, "clampToGround" ); + break; + + case kmldom::ALTITUDEMODE_RELATIVETOGROUND: + poOgrFeat->SetField ( iField, "relativeToGround" ); + break; + + case kmldom::ALTITUDEMODE_ABSOLUTE: + poOgrFeat->SetField ( iField, "absolute" ); + break; + + } + } else if ( poKmlGroundOverlay->has_gx_altitudemode ( ) ) { + switch ( poKmlGroundOverlay->get_gx_altitudemode ( ) ) { + case kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR: + poOgrFeat->SetField ( iField, "relativeToSeaFloor" ); + break; + + case kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR: + poOgrFeat->SetField ( iField, "clampToSeaFloor" ); + break; + } + } + + } + } + + /***** visibility *****/ + + int nVisibility = -1; + + if ( poKmlFeature->has_visibility ( ) ) + nVisibility = poKmlFeature->get_visibility ( ); + + int iField = poOgrFeat->GetFieldIndex ( oFC.visibilityfield ); + + if ( iField > -1 ) + poOgrFeat->SetField ( iField, nVisibility ); + + /***** snippet *****/ + + if ( poKmlFeature->has_snippet ( ) ) + { + string oText = poKmlFeature->get_snippet ( )->get_text(); + + iField = poOgrFeat->GetFieldIndex ( oFC.snippetfield ); + + if ( iField > -1 ) + poOgrFeat->SetField ( iField, TrimSpaces(oText) ); + } + + /***** extended schema *****/ + ExtendedDataPtr poKmlExtendedData = NULL; + + if ( poKmlFeature->has_extendeddata ( ) ) { + poKmlExtendedData = poKmlFeature->get_extendeddata ( ); + + /***** loop over the schemadata_arrays *****/ + + size_t nSchemaData = poKmlExtendedData->get_schemadata_array_size ( ); + + size_t iSchemaData; + + for ( iSchemaData = 0; iSchemaData < nSchemaData; iSchemaData++ ) { + SchemaDataPtr poKmlSchemaData = + poKmlExtendedData->get_schemadata_array_at ( iSchemaData ); + + /***** loop over the simpledata array *****/ + + size_t nSimpleData = + poKmlSchemaData->get_simpledata_array_size ( ); + + size_t iSimpleData; + + for ( iSimpleData = 0; iSimpleData < nSimpleData; iSimpleData++ ) { + SimpleDataPtr poKmlSimpleData = + poKmlSchemaData->get_simpledata_array_at ( iSimpleData ); + + /***** find the field index *****/ + + int iField = -1; + + if ( poKmlSimpleData->has_name ( ) ) { + const string oName = poKmlSimpleData->get_name ( ); + const char *pszName = oName.c_str ( ); + + iField = poOgrFeat->GetFieldIndex ( pszName ); + } + + /***** if it has trxt set the field *****/ + + if ( iField > -1 && poKmlSimpleData->has_text ( ) ) { + string oText = poKmlSimpleData->get_text ( ); + + poOgrFeat->SetField ( iField, TrimSpaces(oText) ); + } + } + } + + if (nSchemaData == 0 && poKmlExtendedData->get_data_array_size() > 0 ) + { + int bLaunderFieldNames = + CSLTestBoolean(CPLGetConfigOption("LIBKML_LAUNDER_FIELD_NAMES", "YES")); + size_t nDataArraySize = poKmlExtendedData->get_data_array_size(); + for(size_t i=0; i < nDataArraySize; i++) + { + const DataPtr& data = poKmlExtendedData->get_data_array_at(i); + if (data->has_name() && data->has_value()) + { + CPLString osName = data->get_name(); + if (bLaunderFieldNames) + osName = OGRLIBKMLLayer::LaunderFieldNames(osName); + int iField = poOgrFeat->GetFieldIndex ( osName ); + if (iField >= 0) + { + poOgrFeat->SetField ( iField, data->get_value().c_str() ); + } + } + } + } + } + +} + +/****************************************************************************** + function create a simplefield from a FieldDefn +******************************************************************************/ + +SimpleFieldPtr FieldDef2kml ( + OGRFieldDefn * poOgrFieldDef, + KmlFactory * poKmlFactory ) +{ + /***** get the field config *****/ + + struct fieldconfig oFC; + get_fieldconfig( &oFC ); + + const char *pszFieldName = poOgrFieldDef->GetNameRef ( ); + + if ( EQUAL ( pszFieldName, oFC.namefield ) || + EQUAL ( pszFieldName, oFC.descfield ) || + EQUAL ( pszFieldName, oFC.tsfield ) || + EQUAL ( pszFieldName, oFC.beginfield ) || + EQUAL ( pszFieldName, oFC.endfield ) || + EQUAL ( pszFieldName, oFC.altitudeModefield ) || + EQUAL ( pszFieldName, oFC.tessellatefield ) || + EQUAL ( pszFieldName, oFC.extrudefield ) || + EQUAL ( pszFieldName, oFC.visibilityfield ) || + EQUAL ( pszFieldName, oFC.drawOrderfield ) || + EQUAL ( pszFieldName, oFC.iconfield ) || + EQUAL ( pszFieldName, oFC.headingfield ) || + EQUAL ( pszFieldName, oFC.tiltfield ) || + EQUAL ( pszFieldName, oFC.rollfield ) || + EQUAL ( pszFieldName, oFC.snippetfield ) || + EQUAL ( pszFieldName, oFC.modelfield ) || + EQUAL ( pszFieldName, oFC.scalexfield ) || + EQUAL ( pszFieldName, oFC.scaleyfield ) || + EQUAL ( pszFieldName, oFC.scalezfield ) || + EQUAL ( pszFieldName, oFC.networklinkfield ) || + EQUAL ( pszFieldName, oFC.networklink_refreshvisibility_field ) || + EQUAL ( pszFieldName, oFC.networklink_flytoview_field ) || + EQUAL ( pszFieldName, oFC.networklink_refreshMode_field ) || + EQUAL ( pszFieldName, oFC.networklink_refreshInterval_field ) || + EQUAL ( pszFieldName, oFC.networklink_viewRefreshMode_field ) || + EQUAL ( pszFieldName, oFC.networklink_viewRefreshTime_field ) || + EQUAL ( pszFieldName, oFC.networklink_viewBoundScale_field ) || + EQUAL ( pszFieldName, oFC.networklink_viewFormat_field ) || + EQUAL ( pszFieldName, oFC.networklink_httpQuery_field ) || + EQUAL ( pszFieldName, oFC.camera_longitude_field ) || + EQUAL ( pszFieldName, oFC.camera_latitude_field ) || + EQUAL ( pszFieldName, oFC.camera_altitude_field ) || + EQUAL ( pszFieldName, oFC.camera_altitudemode_field ) || + EQUAL ( pszFieldName, oFC.photooverlayfield ) || + EQUAL ( pszFieldName, oFC.leftfovfield ) || + EQUAL ( pszFieldName, oFC.rightfovfield ) || + EQUAL ( pszFieldName, oFC.bottomfovfield ) || + EQUAL ( pszFieldName, oFC.topfovfield ) || + EQUAL ( pszFieldName, oFC.nearfield ) || + EQUAL ( pszFieldName, oFC.photooverlay_shape_field ) || + EQUAL ( pszFieldName, oFC.imagepyramid_tilesize_field) || + EQUAL ( pszFieldName, oFC.imagepyramid_maxwidth_field) || + EQUAL ( pszFieldName, oFC.imagepyramid_maxheight_field) || + EQUAL ( pszFieldName, oFC.imagepyramid_gridorigin_field) ) + { + return NULL; + } + + SimpleFieldPtr poKmlSimpleField = poKmlFactory->CreateSimpleField ( ); + poKmlSimpleField->set_name ( pszFieldName ); + + + SimpleDataPtr poKmlSimpleData = NULL; + + switch ( poOgrFieldDef->GetType ( ) ) { + case OFTInteger: + case OFTIntegerList: + poKmlSimpleField->set_type ( "int" ); + return poKmlSimpleField; + + case OFTReal: + case OFTRealList: + poKmlSimpleField->set_type ( "float" ); + return poKmlSimpleField; + + case OFTString: + case OFTStringList: + poKmlSimpleField->set_type ( "string" ); + return poKmlSimpleField; + + /***** kml has these types but as timestamp/timespan *****/ + + case OFTDate: + case OFTTime: + case OFTDateTime: + break; + + default: + poKmlSimpleField->set_type ( "string" ); + return poKmlSimpleField; + } + + return NULL; +} + +/****************************************************************************** + function to add the simpleFields in a schema to a featuredefn +******************************************************************************/ + +void kml2FeatureDef ( + SchemaPtr poKmlSchema, + OGRFeatureDefn * poOgrFeatureDefn ) +{ + + size_t nSimpleFields = poKmlSchema->get_simplefield_array_size ( ); + size_t iSimpleField; + + for ( iSimpleField = 0; iSimpleField < nSimpleFields; iSimpleField++ ) { + SimpleFieldPtr poKmlSimpleField = + poKmlSchema->get_simplefield_array_at ( iSimpleField ); + + const char *pszType = "string"; + string osName = "Unknown"; + string osType; + + if ( poKmlSimpleField->has_type ( ) ) { + osType = poKmlSimpleField->get_type ( ); + + pszType = osType.c_str ( ); + } + + /* FIXME? We cannot set displayname as the field name because in kml2field() we make the */ + /* lookup on fields based on their name. We would need some map if we really */ + /* want to use displayname, but that might not be a good idea because displayname */ + /* may have HTML formatting, which makes it impractical when converting to other */ + /* drivers or to make requests */ + /* Example: http://www.jasonbirch.com/files/newt_combined.kml */ + /*if ( poKmlSimpleField->has_displayname ( ) ) { + osName = poKmlSimpleField->get_displayname ( ); + } + + else*/ if ( poKmlSimpleField->has_name ( ) ) { + osName = poKmlSimpleField->get_name ( ); + } + + if ( EQUAL ( pszType, "bool" ) || + EQUAL ( pszType, "boolean" ) || + EQUAL ( pszType, "int" ) || + EQUAL ( pszType, "short" ) || + EQUAL ( pszType, "ushort" ) ) { + OGRFieldDefn oOgrFieldName ( osName.c_str(), OFTInteger ); + poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldName ); + } + else if ( EQUAL ( pszType, "uint" ) ) { + OGRFieldDefn oOgrFieldName ( osName.c_str(), OFTInteger64 ); + poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldName ); + } + else if ( EQUAL ( pszType, "float" ) || + EQUAL ( pszType, "double" ) ) { + OGRFieldDefn oOgrFieldName ( osName.c_str(), OFTReal ); + poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldName ); + } + else /* string, or any other unrecognized type */ + { + OGRFieldDefn oOgrFieldName ( osName.c_str(), OFTString ); + poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldName ); + } + } + + return; +} + +/******************************************************************************* + * function to fetch the field config options + * +*******************************************************************************/ + +void get_fieldconfig( struct fieldconfig *oFC) { + + oFC->namefield = CPLGetConfigOption ( "LIBKML_NAME_FIELD", + "Name" ); + oFC->descfield = CPLGetConfigOption ( "LIBKML_DESCRIPTION_FIELD", + "description" ); + oFC->tsfield = CPLGetConfigOption ( "LIBKML_TIMESTAMP_FIELD", + "timestamp" ); + oFC->beginfield = CPLGetConfigOption ( "LIBKML_BEGIN_FIELD", + "begin" ); + oFC->endfield = CPLGetConfigOption ( "LIBKML_END_FIELD", + "end" ); + oFC->altitudeModefield = CPLGetConfigOption ( "LIBKML_ALTITUDEMODE_FIELD", + "altitudeMode" ); + oFC->tessellatefield = CPLGetConfigOption ( "LIBKML_TESSELLATE_FIELD", + "tessellate" ); + oFC->extrudefield = CPLGetConfigOption ( "LIBKML_EXTRUDE_FIELD", + "extrude" ); + oFC->visibilityfield = CPLGetConfigOption ( "LIBKML_VISIBILITY_FIELD", + "visibility" ); + oFC->drawOrderfield = CPLGetConfigOption ( "LIBKML_DRAWORDER_FIELD", + "drawOrder" ); + oFC->iconfield = CPLGetConfigOption ( "LIBKML_ICON_FIELD", + "icon" ); + oFC->headingfield = CPLGetConfigOption( "LIBKML_HEADING_FIELD", "heading"); + oFC->tiltfield = CPLGetConfigOption( "LIBKML_TILT_FIELD", "tilt"); + oFC->rollfield = CPLGetConfigOption( "LIBKML_ROLL_FIELD", "roll"); + oFC->snippetfield = CPLGetConfigOption( "LIBKML_SNIPPET_FIELD", "snippet"); + oFC->modelfield = CPLGetConfigOption( "LIBKML_MODEL_FIELD", "model"); + oFC->scalexfield = CPLGetConfigOption( "LIBKML_SCALE_X_FIELD", "scale_x"); + oFC->scaleyfield = CPLGetConfigOption( "LIBKML_SCALE_Y_FIELD", "scale_y"); + oFC->scalezfield = CPLGetConfigOption( "LIBKML_SCALE_Z_FIELD", "scale_z"); + oFC->networklinkfield = CPLGetConfigOption( "LIBKML_NETWORKLINK_FIELD", "networklink"); + oFC->networklink_refreshvisibility_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_REFRESHVISIBILITY_FIELD", "networklink_refreshvisibility"); + oFC->networklink_flytoview_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_FLYTOVIEW_FIELD", "networklink_flytoview"); + oFC->networklink_refreshMode_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_REFRESHMODE_FIELD", "networklink_refreshmode"); + oFC->networklink_refreshInterval_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_REFRESHINTERVAL_FIELD", "networklink_refreshinterval"); + oFC->networklink_viewRefreshMode_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_VIEWREFRESHMODE_FIELD", "networklink_viewrefreshmode"); + oFC->networklink_viewRefreshTime_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_VIEWREFRESHTIME_FIELD", "networklink_viewrefreshtime"); + oFC->networklink_viewBoundScale_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_VIEWBOUNDSCALE_FIELD", "networklink_viewboundscale"); + oFC->networklink_viewFormat_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_VIEWFORMAT_FIELD", "networklink_viewformat"); + oFC->networklink_httpQuery_field = CPLGetConfigOption( "LIBKML_NETWORKLINK_HTTPQUERY_FIELD", "networklink_httpquery"); + oFC->camera_longitude_field = CPLGetConfigOption( "LIBKML_CAMERA_LONGITUDE_FIELD", "camera_longitude"); + oFC->camera_latitude_field = CPLGetConfigOption( "LIBKML_CAMERA_LATITUDE_FIELD", "camera_latitude"); + oFC->camera_altitude_field = CPLGetConfigOption( "LIBKML_CAMERA_ALTITUDE_FIELD", "camera_altitude"); + oFC->camera_altitudemode_field = CPLGetConfigOption( "LIBKML_CAMERA_ALTITUDEMODE_FIELD", "camera_altitudemode"); + oFC->photooverlayfield = CPLGetConfigOption( "LIBKML_PHOTOOVERLAY_FIELD", "photooverlay"); + oFC->leftfovfield = CPLGetConfigOption( "LIBKML_LEFTFOV_FIELD", "leftfov"); + oFC->rightfovfield = CPLGetConfigOption( "LIBKML_RIGHTFOV_FIELD", "rightfov"); + oFC->bottomfovfield = CPLGetConfigOption( "LIBKML_BOTTOMFOV_FIELD", "bottomfov"); + oFC->topfovfield = CPLGetConfigOption( "LIBKML_TOPFOV_FIELD", "topfov"); + oFC->nearfield = CPLGetConfigOption( "LIBKML_NEARFOV_FIELD", "near"); + oFC->photooverlay_shape_field = CPLGetConfigOption( "LIBKML_PHOTOOVERLAY_SHAPE_FIELD", "photooverlay_shape"); + oFC->imagepyramid_tilesize_field = CPLGetConfigOption( "LIBKML_IMAGEPYRAMID_TILESIZE", "imagepyramid_tilesize"); + oFC->imagepyramid_maxwidth_field = CPLGetConfigOption( "LIBKML_IMAGEPYRAMID_MAXWIDTH", "imagepyramid_maxwidth"); + oFC->imagepyramid_maxheight_field = CPLGetConfigOption( "LIBKML_IMAGEPYRAMID_MAXHEIGHT", "imagepyramid_maxheight"); + oFC->imagepyramid_gridorigin_field = CPLGetConfigOption( "LIBKML_IMAGEPYRAMID_GRIDORIGIN", "imagepyramid_gridorigin"); +} + +/************************************************************************/ +/* kmlAltitudeModeFromString() */ +/************************************************************************/ + +int kmlAltitudeModeFromString(const char* pszAltitudeMode, + int& isGX) +{ + isGX = FALSE; + int iAltitudeMode = kmldom::ALTITUDEMODE_CLAMPTOGROUND; + + if ( EQUAL ( pszAltitudeMode, "clampToGround" ) ) + iAltitudeMode = kmldom::ALTITUDEMODE_CLAMPTOGROUND; + + else if ( EQUAL ( pszAltitudeMode, "relativeToGround" ) ) + iAltitudeMode = kmldom::ALTITUDEMODE_RELATIVETOGROUND; + + else if ( EQUAL ( pszAltitudeMode, "absolute" ) ) + iAltitudeMode = kmldom::ALTITUDEMODE_ABSOLUTE; + + else if ( EQUAL ( pszAltitudeMode, "relativeToSeaFloor" ) ) { + iAltitudeMode = + kmldom::GX_ALTITUDEMODE_RELATIVETOSEAFLOOR; + isGX = TRUE; + } + + else if ( EQUAL ( pszAltitudeMode, "clampToSeaFloor" ) ) { + iAltitudeMode = + kmldom::GX_ALTITUDEMODE_CLAMPTOSEAFLOOR; + isGX = TRUE; + } + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "Unrecognized value for altitudeMode: %s", + pszAltitudeMode); + } + + return iAltitudeMode; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfield.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfield.h new file mode 100644 index 000000000..1fa885a8d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlfield.h @@ -0,0 +1,144 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +using kmldom::SimpleFieldPtr; +using kmldom::SchemaPtr; +using kmldom::KmlFactory; +using kmldom::FeaturePtr; +using kmldom::PlacemarkPtr; + +/****************************************************************************** + function to output ogr fields in kml + + args: + poOgrFeat pointer to the feature the field is in + poOgrLayer pointer to the layer the feature is in + poKmlFactory pointer to the libkml dom factory + poKmlPlacemark pointer to the placemark to add to + + returns: + nothing + + env vars: + LIBKML_TIMESTAMP_FIELD default: OFTDate or OFTDateTime named timestamp + LIBKML_TIMESPAN_BEGIN_FIELD default: OFTDate or OFTDateTime named begin + LIBKML_TIMESPAN_END_FIELD default: OFTDate or OFTDateTime named end + LIBKML_DESCRIPTION_FIELD default: none + LIBKML_NAME_FIELD default: OFTString field named name + + +******************************************************************************/ + +void field2kml ( + OGRFeature * poOgrFeat, + OGRLIBKMLLayer * poOgrLayer, + KmlFactory * poKmlFactory, + FeaturePtr poKmlPlacemark, + int bUseSimpleField ); + +/****************************************************************************** + function to read kml into ogr fields +******************************************************************************/ + +void kml2field ( + OGRFeature * poOgrFeat, + FeaturePtr poKmlFeature ); + +/****************************************************************************** + function create a simplefield from a FieldDefn +******************************************************************************/ + +SimpleFieldPtr FieldDef2kml ( + OGRFieldDefn *poOgrFieldDef, + KmlFactory * poKmlFactory ); + +/****************************************************************************** + function to add the simpleFields in a schema to a featuredefn +******************************************************************************/ + +void kml2FeatureDef ( + SchemaPtr poKmlSchema, + OGRFeatureDefn *poOgrFeatureDefn); + +/******************************************************************************* + * function to fetch the field config options + * +*******************************************************************************/ + +struct fieldconfig { + const char *namefield; + const char *descfield; + const char *tsfield; + const char *beginfield; + const char *endfield; + const char *altitudeModefield; + const char *tessellatefield; + const char *extrudefield; + const char *visibilityfield; + const char *drawOrderfield; + const char *iconfield; + const char *headingfield; + const char *tiltfield; + const char *rollfield; + const char *snippetfield; + const char *modelfield; + const char *scalexfield; + const char *scaleyfield; + const char *scalezfield; + const char *networklinkfield; + const char *networklink_refreshvisibility_field; + const char *networklink_flytoview_field; + const char *networklink_refreshMode_field; + const char *networklink_refreshInterval_field; + const char *networklink_viewRefreshMode_field; + const char *networklink_viewRefreshTime_field; + const char *networklink_viewBoundScale_field; + const char *networklink_viewFormat_field; + const char *networklink_httpQuery_field; + const char *camera_longitude_field; + const char *camera_latitude_field; + const char *camera_altitude_field; + const char *camera_altitudemode_field; + const char *photooverlayfield; + const char *leftfovfield; + const char *rightfovfield; + const char *bottomfovfield; + const char *topfovfield; + const char *nearfield; + const char *photooverlay_shape_field; + const char *imagepyramid_tilesize_field; + const char *imagepyramid_maxwidth_field; + const char *imagepyramid_maxheight_field; + const char *imagepyramid_gridorigin_field; +}; + +void get_fieldconfig( struct fieldconfig *oFC ); + +int kmlAltitudeModeFromString(const char* pszAltitudeMode, + int& isGX); diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlgeometry.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlgeometry.cpp new file mode 100644 index 000000000..697581e4e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlgeometry.cpp @@ -0,0 +1,860 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include +#include "ogr_p.h" +#include + +using kmldom::KmlFactory; +using kmldom::CoordinatesPtr; +using kmldom::PointPtr; +using kmldom::LatLonBoxPtr; +using kmldom::LineStringPtr; +using kmldom::LinearRingPtr; +using kmldom::OuterBoundaryIsPtr; +using kmldom::InnerBoundaryIsPtr; +using kmldom::PolygonPtr; +using kmldom::MultiGeometryPtr; +using kmldom::GeometryPtr; +using kmldom::ElementPtr; +using kmldom::GeometryPtr; +using kmldom::GxLatLonQuadPtr; +using kmldom::GxTrackPtr; +using kmldom::GxMultiTrackPtr; + +using kmlbase::Vec3; + +#include "ogrlibkmlgeometry.h" + +/****************************************************************************** + funtion to write out a ogr geometry to kml + +args: + poOgrGeom the ogr geometry + extra used in recursion, just pass -1 + poKmlFactory pointer to the libkml dom factory + +returns: + ElementPtr to the geometry created + +******************************************************************************/ + +ElementPtr geom2kml ( + OGRGeometry * poOgrGeom, + int extra, + KmlFactory * poKmlFactory ) +{ + int i; + + if ( !poOgrGeom ) { + return NULL; + } + + /***** ogr geom vars *****/ + + OGRPoint *poOgrPoint = NULL; + OGRLineString *poOgrLineString; + OGRPolygon *poOgrPolygon; + OGRGeometryCollection *poOgrMultiGeom; + + /***** libkml geom vars *****/ + + CoordinatesPtr coordinates; + PointPtr poKmlPoint; + LineStringPtr poKmlLineString; + LinearRingPtr poKmlLinearRing; + OuterBoundaryIsPtr poKmlOuterRing; + InnerBoundaryIsPtr poKmlInnerRing; + PolygonPtr poKmlPolygon; + MultiGeometryPtr poKmlMultiGeometry; + + ElementPtr poKmlGeometry; + ElementPtr poKmlTmpGeometry; + + /***** other vars *****/ + + double x, + y, + z; + + int numpoints = 0; + int nGeom; + OGRwkbGeometryType type = poOgrGeom->getGeometryType ( ); + + switch ( type ) { + + case wkbPoint: + + poOgrPoint = ( OGRPoint * ) poOgrGeom; + if (poOgrPoint->getCoordinateDimension() == 0) + { + poKmlGeometry = poKmlPoint = poKmlFactory->CreatePoint ( ); + } + else + { + x = poOgrPoint->getX ( ); + y = poOgrPoint->getY ( ); + + if ( x > 180 ) + x -= 360; + + coordinates = poKmlFactory->CreateCoordinates ( ); + coordinates->add_latlng ( y, x ); + poKmlGeometry = poKmlPoint = poKmlFactory->CreatePoint ( ); + poKmlPoint->set_coordinates ( coordinates ); + } + + break; + + case wkbPoint25D: + poOgrPoint = ( OGRPoint * ) poOgrGeom; + + x = poOgrPoint->getX ( ); + y = poOgrPoint->getY ( ); + z = poOgrPoint->getZ ( ); + + if ( x > 180 ) + x -= 360; + + coordinates = poKmlFactory->CreateCoordinates ( ); + coordinates->add_latlngalt ( y, x, z ); + poKmlGeometry = poKmlPoint = poKmlFactory->CreatePoint ( ); + poKmlPoint->set_coordinates ( coordinates ); + + break; + + case wkbLineString: + poOgrLineString = ( OGRLineString * ) poOgrGeom; + + if( extra >= 0 ) + { + ((OGRLinearRing*)poOgrGeom)->closeRings(); + } + + numpoints = poOgrLineString->getNumPoints ( ); + if( extra >= 0 ) + { + if( numpoints < 4 && + CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "A linearring should have at least 4 points"); + return NULL; + } + } + else + { + if( numpoints < 2 && + CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "A linestring should have at least 2 points"); + return NULL; + } + } + + coordinates = poKmlFactory->CreateCoordinates ( ); + + poOgrPoint = new OGRPoint ( ); + + for ( i = 0; i < numpoints; i++ ) { + poOgrLineString->getPoint ( i, poOgrPoint ); + + x = poOgrPoint->getX ( ); + y = poOgrPoint->getY ( ); + + if ( x > 180 ) + x -= 360; + + coordinates->add_latlng ( y, x ); + } + delete poOgrPoint; + + /***** check if its a wkbLinearRing *****/ + + if ( extra < 0 ) { + + poKmlGeometry = poKmlLineString = + poKmlFactory->CreateLineString ( ); + poKmlLineString->set_coordinates ( coordinates ); + + break; + } + + /***** fallthough *****/ + + case wkbLinearRing: //this case is for readability only + + poKmlLinearRing = poKmlFactory->CreateLinearRing ( ); + poKmlLinearRing->set_coordinates ( coordinates ); + + if ( !extra ) { + poKmlOuterRing = poKmlFactory->CreateOuterBoundaryIs ( ); + poKmlOuterRing->set_linearring ( poKmlLinearRing ); + poKmlGeometry = poKmlOuterRing; + } + else { + poKmlGeometry = poKmlInnerRing = + poKmlFactory->CreateInnerBoundaryIs ( ); + poKmlInnerRing->set_linearring ( poKmlLinearRing ); + } + + case wkbLineString25D: + + poOgrLineString = ( OGRLineString * ) poOgrGeom; + + if( extra >= 0 ) + { + ((OGRLinearRing*)poOgrGeom)->closeRings(); + } + + numpoints = poOgrLineString->getNumPoints ( ); + if( extra >= 0 ) + { + if( numpoints < 4 && + CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "A linearring should have at least 4 points"); + return NULL; + } + } + else + { + if( numpoints < 2 && + CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "A linestring should have at least 2 points"); + return NULL; + } + } + + coordinates = poKmlFactory->CreateCoordinates ( ); + poOgrPoint = new OGRPoint ( ); + + for ( i = 0; i < numpoints; i++ ) { + poOgrLineString->getPoint ( i, poOgrPoint ); + + x = poOgrPoint->getX ( ); + y = poOgrPoint->getY ( ); + z = poOgrPoint->getZ ( ); + + if ( x > 180 ) + x -= 360; + + coordinates->add_latlngalt ( y, x, z ); + } + delete poOgrPoint; + + /***** check if its a wkbLinearRing *****/ + + if ( extra < 0 ) { + + poKmlGeometry = poKmlLineString = + poKmlFactory->CreateLineString ( ); + poKmlLineString->set_coordinates ( coordinates ); + + break; + } + /***** fallthough *****/ + + //case wkbLinearRing25D: // this case is for readability only + + poKmlLinearRing = poKmlFactory->CreateLinearRing ( ); + poKmlLinearRing->set_coordinates ( coordinates ); + + if ( !extra ) { + poKmlGeometry = poKmlOuterRing = + poKmlFactory->CreateOuterBoundaryIs ( ); + poKmlOuterRing->set_linearring ( poKmlLinearRing ); + } + else { + poKmlGeometry = poKmlInnerRing = + poKmlFactory->CreateInnerBoundaryIs ( ); + poKmlInnerRing->set_linearring ( poKmlLinearRing ); + } + + break; + + case wkbPolygon: + + CPLErrorReset(); + if( CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) && + OGRGeometryFactory::haveGEOS() && (!poOgrGeom->IsValid() || + CPLGetLastErrorType() != CE_None) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Invalid polygon"); + return NULL; + } + poOgrPolygon = ( OGRPolygon * ) poOgrGeom; + + poKmlGeometry = poKmlPolygon = poKmlFactory->CreatePolygon ( ); + + poKmlTmpGeometry = geom2kml ( poOgrPolygon->getExteriorRing ( ), + 0, poKmlFactory ); + poKmlPolygon-> + set_outerboundaryis ( AsOuterBoundaryIs ( poKmlTmpGeometry ) ); + + nGeom = poOgrPolygon->getNumInteriorRings ( ); + for ( i = 0; i < nGeom; i++ ) { + poKmlTmpGeometry = geom2kml ( poOgrPolygon->getInteriorRing ( i ), + i + 1, poKmlFactory ); + poKmlPolygon-> + add_innerboundaryis ( AsInnerBoundaryIs ( poKmlTmpGeometry ) ); + } + + break; + + case wkbPolygon25D: + + CPLErrorReset(); + if( CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) && + OGRGeometryFactory::haveGEOS() && (!poOgrGeom->IsValid() || + CPLGetLastErrorType() != CE_None) ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Invalid polygon"); + return NULL; + } + poOgrPolygon = ( OGRPolygon * ) poOgrGeom; + + poKmlGeometry = poKmlPolygon = poKmlFactory->CreatePolygon ( ); + + poKmlTmpGeometry = geom2kml ( poOgrPolygon->getExteriorRing ( ), + 0, poKmlFactory ); + poKmlPolygon-> + set_outerboundaryis ( AsOuterBoundaryIs ( poKmlTmpGeometry ) ); + + nGeom = poOgrPolygon->getNumInteriorRings ( ); + for ( i = 0; i < nGeom; i++ ) { + poKmlTmpGeometry = geom2kml ( poOgrPolygon->getInteriorRing ( i ), + i + 1, poKmlFactory ); + poKmlPolygon-> + add_innerboundaryis ( AsInnerBoundaryIs ( poKmlTmpGeometry ) ); + } + + break; + + case wkbMultiPoint: + case wkbMultiLineString: + case wkbMultiPolygon: + case wkbGeometryCollection: + case wkbMultiPoint25D: + case wkbMultiLineString25D: + case wkbMultiPolygon25D: + case wkbGeometryCollection25D: + + poOgrMultiGeom = ( OGRGeometryCollection * ) poOgrGeom; + + nGeom = poOgrMultiGeom->getNumGeometries ( ); + + if( nGeom == 1 && + CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + { + CPLDebug("LIBKML", "Turning multiple geometry into single geometry"); + poKmlGeometry = geom2kml( poOgrMultiGeom->getGeometryRef ( 0 ), + -1, poKmlFactory ); + } + else + { + if( nGeom == 0 && + CSLTestBoolean(CPLGetConfigOption("LIBKML_STRICT_COMPLIANCE", "TRUE")) ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Empty multi geometry are not recommended"); + } + poKmlGeometry = poKmlMultiGeometry = + poKmlFactory->CreateMultiGeometry ( ); + for ( i = 0; i < nGeom; i++ ) { + poKmlTmpGeometry = geom2kml ( poOgrMultiGeom->getGeometryRef ( i ), + -1, poKmlFactory ); + poKmlMultiGeometry-> + add_geometry ( AsGeometry ( poKmlTmpGeometry ) ); + } + } + + break; + + case wkbUnknown: + case wkbNone: + default: + break; + + } + + return poKmlGeometry; +} + +/****************************************************************************** + recursive function to read a kml geometry and translate to ogr + +Args: + poKmlGeometry pointer to the kml geometry to translate + poOgrSRS pointer to the spatial ref to set on the geometry + +Returns: + pointer to the new ogr geometry object + +******************************************************************************/ + +OGRGeometry *kml2geom_rec ( + GeometryPtr poKmlGeometry, + OGRSpatialReference *poOgrSRS) + +{ + + /***** ogr geom vars *****/ + + OGRPoint *poOgrPoint; + OGRLineString *poOgrLineString; + OGRLinearRing *poOgrLinearRing; + OGRPolygon *poOgrPolygon; + OGRGeometryCollection *poOgrMultiGeometry; + OGRGeometry *poOgrGeometry = NULL; + OGRGeometry *poOgrTmpGeometry = NULL; + + + /***** libkml geom vars *****/ + + CoordinatesPtr poKmlCoordinates; + PointPtr poKmlPoint; + LineStringPtr poKmlLineString; + LinearRingPtr poKmlLinearRing; + OuterBoundaryIsPtr poKmlOuterRing; + InnerBoundaryIsPtr poKmlInnerRing; + PolygonPtr poKmlPolygon; + MultiGeometryPtr poKmlMultiGeometry; + GxTrackPtr poKmlGxTrack; + GxMultiTrackPtr poKmlGxMultiTrack; + GeometryPtr poKmlTmpGeometry; + + Vec3 oKmlVec; + + size_t nRings, + nCoords, + nGeom, + i; + + switch ( poKmlGeometry->Type ( ) ) { + case kmldom::Type_Point: + poKmlPoint = AsPoint ( poKmlGeometry ); + if ( poKmlPoint->has_coordinates ( ) ) { + poKmlCoordinates = poKmlPoint->get_coordinates ( ); + nCoords = poKmlCoordinates->get_coordinates_array_size ( ); + if (nCoords > 0) + { + oKmlVec = poKmlCoordinates->get_coordinates_array_at ( 0 ); + + if ( oKmlVec.has_altitude ( ) ) + poOgrPoint = new OGRPoint ( oKmlVec.get_longitude ( ), + oKmlVec.get_latitude ( ), + oKmlVec.get_altitude ( ) ); + else + poOgrPoint = new OGRPoint ( oKmlVec.get_longitude ( ), + oKmlVec.get_latitude ( ) ); + + poOgrGeometry = poOgrPoint; + } + else + { + poOgrGeometry = new OGRPoint(); + } + } + else + { + poOgrGeometry = new OGRPoint(); + } + + break; + + case kmldom::Type_LineString: + poKmlLineString = AsLineString ( poKmlGeometry ); + poOgrLineString = new OGRLineString ( ); + if ( poKmlLineString->has_coordinates ( ) ) { + poKmlCoordinates = poKmlLineString->get_coordinates ( ); + + nCoords = poKmlCoordinates->get_coordinates_array_size ( ); + for ( i = 0; i < nCoords; i++ ) { + oKmlVec = poKmlCoordinates->get_coordinates_array_at ( i ); + if ( oKmlVec.has_altitude ( ) ) + poOgrLineString-> + addPoint ( oKmlVec.get_longitude ( ), + oKmlVec.get_latitude ( ), + oKmlVec.get_altitude ( ) ); + else + poOgrLineString-> + addPoint ( oKmlVec.get_longitude ( ), + oKmlVec.get_latitude ( ) ); + } + } + poOgrGeometry = poOgrLineString; + + break; + case kmldom::Type_LinearRing: + poKmlLinearRing = AsLinearRing ( poKmlGeometry ); + poOgrLinearRing = new OGRLinearRing ( ); + if ( poKmlLinearRing->has_coordinates ( ) ) { + poKmlCoordinates = poKmlLinearRing->get_coordinates ( ); + + nCoords = poKmlCoordinates->get_coordinates_array_size ( ); + for ( i = 0; i < nCoords; i++ ) { + oKmlVec = poKmlCoordinates->get_coordinates_array_at ( i ); + if ( oKmlVec.has_altitude ( ) ) + poOgrLinearRing-> + addPoint ( oKmlVec.get_longitude ( ), + oKmlVec.get_latitude ( ), + oKmlVec.get_altitude ( ) ); + else + poOgrLinearRing-> + addPoint ( oKmlVec.get_longitude ( ), + oKmlVec.get_latitude ( ) ); + } + } + poOgrGeometry = poOgrLinearRing; + + break; + case kmldom::Type_Polygon: + poKmlPolygon = AsPolygon ( poKmlGeometry ); + + poOgrPolygon = new OGRPolygon ( ); + if ( poKmlPolygon->has_outerboundaryis ( ) ) { + + poKmlOuterRing = poKmlPolygon->get_outerboundaryis ( ); + poKmlLinearRing = poKmlOuterRing->get_linearring ( ); + if (poKmlLinearRing) + { + poOgrTmpGeometry = kml2geom_rec ( poKmlLinearRing, poOgrSRS ); + + poOgrPolygon-> + addRingDirectly ( ( OGRLinearRing * ) poOgrTmpGeometry ); + } + + } + nRings = poKmlPolygon->get_innerboundaryis_array_size ( ); + for ( i = 0; i < nRings; i++ ) { + poKmlInnerRing = poKmlPolygon->get_innerboundaryis_array_at ( i ); + poKmlLinearRing = poKmlInnerRing->get_linearring ( ); + if (poKmlLinearRing) + { + poOgrTmpGeometry = kml2geom_rec ( poKmlLinearRing, poOgrSRS ); + + poOgrPolygon-> + addRingDirectly ( ( OGRLinearRing * ) poOgrTmpGeometry ); + } + } + poOgrGeometry = poOgrPolygon; + + break; + case kmldom::Type_MultiGeometry: + { + poKmlMultiGeometry = AsMultiGeometry ( poKmlGeometry ); + nGeom = poKmlMultiGeometry->get_geometry_array_size ( ); + + /* Detect subgeometry type to instanciate appropriate Multi geometry type */ + kmldom::KmlDomType type = kmldom::Type_Unknown; + for ( i = 0; i < nGeom; i++ ) { + poKmlTmpGeometry = poKmlMultiGeometry->get_geometry_array_at ( i ); + if (type == kmldom::Type_Unknown) + type = poKmlTmpGeometry->Type(); + else if (type != poKmlTmpGeometry->Type()) + { + type = kmldom::Type_Unknown; + break; + } + } + + if (type == kmldom::Type_Point) + poOgrMultiGeometry = new OGRMultiPoint(); + else if (type == kmldom::Type_LineString) + poOgrMultiGeometry = new OGRMultiLineString(); + else if (type == kmldom::Type_Polygon) + poOgrMultiGeometry = new OGRMultiPolygon(); + else + poOgrMultiGeometry = new OGRGeometryCollection (); + + for ( i = 0; i < nGeom; i++ ) { + poKmlTmpGeometry = poKmlMultiGeometry->get_geometry_array_at ( i ); + poOgrTmpGeometry = kml2geom_rec ( poKmlTmpGeometry, poOgrSRS ); + + poOgrMultiGeometry->addGeometryDirectly ( poOgrTmpGeometry ); + } + poOgrGeometry = poOgrMultiGeometry; + break; + } + + case kmldom::Type_GxTrack: + poKmlGxTrack = AsGxTrack ( poKmlGeometry ); + nCoords = poKmlGxTrack->get_gx_coord_array_size(); + poOgrLineString = new OGRLineString ( ); + for ( i = 0; i < nCoords; i++ ) { + oKmlVec = poKmlGxTrack->get_gx_coord_array_at ( i ); + if ( oKmlVec.has_altitude ( ) ) + poOgrLineString-> + addPoint ( oKmlVec.get_longitude ( ), + oKmlVec.get_latitude ( ), + oKmlVec.get_altitude ( ) ); + else + poOgrLineString-> + addPoint ( oKmlVec.get_longitude ( ), + oKmlVec.get_latitude ( ) ); + } + poOgrGeometry = poOgrLineString; + break; + + case kmldom::Type_GxMultiTrack: + { + poKmlGxMultiTrack = AsGxMultiTrack ( poKmlGeometry ); + nGeom = poKmlGxMultiTrack->get_gx_track_array_size ( ); + poOgrMultiGeometry = new OGRMultiLineString(); + for( size_t j = 0; j < nGeom; j++ ) + { + poKmlGxTrack = poKmlGxMultiTrack->get_gx_track_array_at ( j ); + nCoords = poKmlGxTrack->get_gx_coord_array_size(); + poOgrLineString = new OGRLineString ( ); + for ( i = 0; i < nCoords; i++ ) { + oKmlVec = poKmlGxTrack->get_gx_coord_array_at ( i ); + if ( oKmlVec.has_altitude ( ) ) + poOgrLineString-> + addPoint ( oKmlVec.get_longitude ( ), + oKmlVec.get_latitude ( ), + oKmlVec.get_altitude ( ) ); + else + poOgrLineString-> + addPoint ( oKmlVec.get_longitude ( ), + oKmlVec.get_latitude ( ) ); + } + poOgrMultiGeometry->addGeometryDirectly(poOgrLineString); + } + poOgrGeometry = poOgrMultiGeometry; + break; + } + + default: + break; + } + + if (poOgrGeometry) + poOgrGeometry->assignSpatialReference(poOgrSRS); + + return poOgrGeometry; +} + +static +OGRGeometry *kml2geom_latlonbox_int ( + LatLonBoxPtr poKmlLatLonBox, + OGRSpatialReference *poOgrSRS) + +{ + OGRPolygon *poOgrPolygon; + double north, south, east, west; + if ( !poKmlLatLonBox->has_north ( ) || + !poKmlLatLonBox->has_south ( ) || + !poKmlLatLonBox->has_east ( ) || + !poKmlLatLonBox->has_west ( ) ) { + + return NULL; + } + poOgrPolygon = new OGRPolygon ( ); + north = poKmlLatLonBox->get_north ( ); + south = poKmlLatLonBox->get_south ( ); + east = poKmlLatLonBox->get_east ( ); + west = poKmlLatLonBox->get_west ( ); + OGRLinearRing* poOgrRing = new OGRLinearRing ( ); + poOgrRing->addPoint ( east, north, 0.0 ); + poOgrRing->addPoint ( east, south, 0.0 ); + poOgrRing->addPoint ( west, south, 0.0 ); + poOgrRing->addPoint ( west, north, 0.0 ); + poOgrRing->addPoint ( east, north, 0.0 ); + poOgrPolygon-> + addRingDirectly ( poOgrRing ); + poOgrPolygon->assignSpatialReference(poOgrSRS); + + return poOgrPolygon; +} + +static +OGRGeometry *kml2geom_latlonquad_int ( + GxLatLonQuadPtr poKmlLatLonQuad, + OGRSpatialReference *poOgrSRS) + +{ + if( !poKmlLatLonQuad->has_coordinates() ) + return NULL; + + const CoordinatesPtr& poKmlCoordinates = + poKmlLatLonQuad->get_coordinates(); + + OGRLinearRing* poOgrLinearRing = new OGRLinearRing ( ); + + size_t nCoords = poKmlCoordinates->get_coordinates_array_size ( ); + for ( size_t i = 0; i < nCoords; i++ ) { + Vec3 oKmlVec = poKmlCoordinates->get_coordinates_array_at ( i ); + if ( oKmlVec.has_altitude ( ) ) + poOgrLinearRing-> + addPoint ( oKmlVec.get_longitude ( ), + oKmlVec.get_latitude ( ), + oKmlVec.get_altitude ( ) ); + else + poOgrLinearRing-> + addPoint ( oKmlVec.get_longitude ( ), + oKmlVec.get_latitude ( ) ); + } + poOgrLinearRing->closeRings(); + + OGRPolygon *poOgrPolygon = new OGRPolygon(); + poOgrPolygon-> + addRingDirectly ( poOgrLinearRing ); + poOgrPolygon->assignSpatialReference(poOgrSRS); + + return poOgrPolygon; +} + +/****************************************************************************** + main function to read a kml geometry and translate to ogr + +Args: + poKmlGeometry pointer to the kml geometry to translate + poOgrSRS pointer to the spatial ref to set on the geometry + +Returns: + pointer to the new ogr geometry object + +******************************************************************************/ + +OGRGeometry *kml2geom ( + GeometryPtr poKmlGeometry, + OGRSpatialReference *poOgrSRS) + +{ + + /***** get the geometry *****/ + + OGRGeometry *poOgrGeometry = kml2geom_rec (poKmlGeometry, poOgrSRS); + + /***** split the geometry at the dateline? *****/ + + const char *pszWrap = CPLGetConfigOption ( "LIBKML_WRAPDATELINE", "no" ); + if (CSLTestBoolean(pszWrap)) { + + char **papszTransformOptions = NULL; + papszTransformOptions = CSLAddString( papszTransformOptions, + "WRAPDATELINE=YES"); + + /***** transform *****/ + + OGRGeometry *poOgrDstGeometry = + OGRGeometryFactory::transformWithOptions(poOgrGeometry, + NULL, + papszTransformOptions); + + /***** replace the original geom *****/ + + if (poOgrDstGeometry) { + delete poOgrGeometry; + poOgrGeometry = poOgrDstGeometry; + } + + CSLDestroy(papszTransformOptions); + } + + return poOgrGeometry; +} + +OGRGeometry *kml2geom_latlonbox ( + LatLonBoxPtr poKmlLatLonBox, + OGRSpatialReference *poOgrSRS) + +{ + + /***** get the geometry *****/ + + OGRGeometry *poOgrGeometry = kml2geom_latlonbox_int (poKmlLatLonBox, poOgrSRS); + + /***** split the geometry at the dateline? *****/ + + const char *pszWrap = CPLGetConfigOption ( "LIBKML_WRAPDATELINE", "no" ); + if (CSLTestBoolean(pszWrap)) { + + char **papszTransformOptions = NULL; + papszTransformOptions = CSLAddString( papszTransformOptions, + "WRAPDATELINE=YES"); + + /***** transform *****/ + + OGRGeometry *poOgrDstGeometry = + OGRGeometryFactory::transformWithOptions(poOgrGeometry, + NULL, + papszTransformOptions); + + /***** replace the original geom *****/ + + if (poOgrDstGeometry) { + delete poOgrGeometry; + poOgrGeometry = poOgrDstGeometry; + } + + CSLDestroy(papszTransformOptions); + } + + return poOgrGeometry; +} + +OGRGeometry *kml2geom_latlonquad ( + GxLatLonQuadPtr poKmlLatLonQuad, + OGRSpatialReference *poOgrSRS) + +{ + + /***** get the geometry *****/ + + OGRGeometry *poOgrGeometry = kml2geom_latlonquad_int (poKmlLatLonQuad, poOgrSRS); + + /***** split the geometry at the dateline? *****/ + + const char *pszWrap = CPLGetConfigOption ( "LIBKML_WRAPDATELINE", "no" ); + if (CSLTestBoolean(pszWrap)) { + + char **papszTransformOptions = NULL; + papszTransformOptions = CSLAddString( papszTransformOptions, + "WRAPDATELINE=YES"); + + /***** transform *****/ + + OGRGeometry *poOgrDstGeometry = + OGRGeometryFactory::transformWithOptions(poOgrGeometry, + NULL, + papszTransformOptions); + + /***** replace the original geom *****/ + + if (poOgrDstGeometry) { + delete poOgrGeometry; + poOgrGeometry = poOgrDstGeometry; + } + + CSLDestroy(papszTransformOptions); + } + + return poOgrGeometry; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlgeometry.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlgeometry.h new file mode 100644 index 000000000..3463bcee2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlgeometry.h @@ -0,0 +1,78 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include + +using kmldom::ElementPtr; +using kmldom::KmlFactory; +using kmldom::GeometryPtr; +using kmldom::LatLonBoxPtr; +using kmldom::GxLatLonQuadPtr; + +/******************************************************************************* + funtion to write out a ogr geometry to kml + +args: + poOgrGeom the ogr geometry + extra used in recursion, just pass -1 + poKmlFactory pointer to the libkml dom factory + +returns: + ElementPtr to the geometry created + +*******************************************************************************/ + +ElementPtr geom2kml ( + OGRGeometry * poOgrGeom, + int extra, + KmlFactory * poKmlFactory ); + + +/****************************************************************************** + function to read a kml geometry and translate to ogr + +Args: + poKmlGeometry pointer to the kml geometry to translate + poOgrSRS pointer to the spatial ref to set on the geometry + +Returns: + pointer to the new ogr geometry object + +******************************************************************************/ + +OGRGeometry *kml2geom ( + GeometryPtr poKmlGeometry, + OGRSpatialReference *poOgrSRS); + +OGRGeometry *kml2geom_latlonbox ( + LatLonBoxPtr poKmlLatLonBox, + OGRSpatialReference *poOgrSRS); + +OGRGeometry *kml2geom_latlonquad ( + GxLatLonQuadPtr poKmlLatLonQuad, + OGRSpatialReference *poOgrSRS); diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmllayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmllayer.cpp new file mode 100644 index 000000000..b98b8c12a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmllayer.cpp @@ -0,0 +1,1256 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "ogr_libkml.h" +//#include "cpl_conv.h" +//#include "cpl_string.h" +#include "cpl_error.h" + +#include + +using kmldom::KmlFactory; +using kmldom::PlacemarkPtr; +using kmldom::Placemark; +using kmldom::DocumentPtr; +using kmldom::ContainerPtr; +using kmldom::FeaturePtr; +using kmldom::GroundOverlayPtr; +using kmldom::KmlPtr; +using kmldom::Kml; +using kmlengine::KmzFile; +using kmlengine::KmlFile; +using kmlengine::Bbox; +using kmldom::ExtendedDataPtr; +using kmldom::SchemaDataPtr; +using kmldom::DataPtr; +using kmldom::CameraPtr; +using kmldom::LookAtPtr; +using kmldom::RegionPtr; +using kmldom::LatLonAltBoxPtr; +using kmldom::LodPtr; +using kmldom::ScreenOverlayPtr; +using kmldom::IconPtr; +using kmldom::CreatePtr; +using kmldom::ChangePtr; +using kmldom::DeletePtr; + +#include "ogrlibkmlfeature.h" +#include "ogrlibkmlfield.h" +#include "ogrlibkmlstyle.h" + +/************************************************************************/ +/* OGRLIBKMLGetSanitizedNCName() */ +/************************************************************************/ + +CPLString OGRLIBKMLGetSanitizedNCName(const char* pszName) +{ + CPLString osName(pszName); + /* (Approximate) validation rules for a valic NCName */ + for(size_t i = 0; i < osName.size(); i++) + { + char ch = osName[i]; + if( (ch >= 'A' && ch <= 'Z') || ch == '_' || (ch >= 'a' && ch <= 'z') ) + { + /* ok */ + } + else if ( i > 0 && (ch == '-' || ch == '.' || (ch >= '0' && ch <= '9')) ) + { + /* ok */ + } +#if 0 + /* Always false. */ + else if ( ch > 127 ) + { + /* ok : this is an approximation */ + } +#endif + else + osName[i] = '_'; + } + return osName; +} + +/****************************************************************************** + OGRLIBKMLLayer constructor + + Args: pszLayerName the name of the layer + poSpatialRef the spacial Refrance for the layer + eGType the layers geometry type + poOgrDS pointer to the datasource the layer is in + poKmlRoot pointer to the root kml element of the layer + poKmlContainer pointer to the kml container of the layer + pszFileName the filename of the layer + bNew true if its a new layer + bUpdate true if the layer is writeable + + Returns: nothing + +******************************************************************************/ + +OGRLIBKMLLayer::OGRLIBKMLLayer ( const char *pszLayerName, + OGRSpatialReference * poSpatialRef, + OGRwkbGeometryType eGType, + OGRLIBKMLDataSource * poOgrDS, + ElementPtr poKmlRoot, + ContainerPtr poKmlContainer, + UpdatePtr poKmlUpdate, + const char *pszFileName, + int bNew, + int bUpdate ) +{ + + m_poStyleTable = NULL; + iFeature = 0; + nFeatures = 0; + nFID = 1; + + this->bUpdate = bUpdate; + bUpdated = FALSE; + m_pszName = CPLStrdup ( pszLayerName ); + m_pszFileName = CPLStrdup ( pszFileName ); + m_poOgrDS = poOgrDS; + + m_poOgrSRS = new OGRSpatialReference ( NULL ); + m_poOgrSRS->SetWellKnownGeogCS ( "WGS84" ); + + m_poOgrFeatureDefn = new OGRFeatureDefn ( pszLayerName ); + SetDescription( m_poOgrFeatureDefn->GetName() ); + m_poOgrFeatureDefn->Reference ( ); + m_poOgrFeatureDefn->SetGeomType ( eGType ); + if( m_poOgrFeatureDefn->GetGeomFieldCount() != 0 ) + m_poOgrFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poOgrSRS); + + /***** store the root element pointer *****/ + + m_poKmlLayerRoot = poKmlRoot; + + /***** store the layers container *****/ + + m_poKmlLayer = poKmlContainer; + + /* update container */ + m_poKmlUpdate = poKmlUpdate; + + m_poKmlSchema = NULL; + + /***** related to Region *****/ + + m_bWriteRegion = FALSE; + m_bRegionBoundsAuto = FALSE; + m_dfRegionMinLodPixels = 0; + m_dfRegionMaxLodPixels = -1; + m_dfRegionMinFadeExtent = 0; + m_dfRegionMaxFadeExtent = 0; + m_dfRegionMinX = 200; + m_dfRegionMinY = 200; + m_dfRegionMaxX = -200; + m_dfRegionMaxY = -200; + + + m_bReadGroundOverlay = CSLTestBoolean(CPLGetConfigOption("LIBKML_READ_GROUND_OVERLAY", "YES")); + m_bUseSimpleField = CSLTestBoolean(CPLGetConfigOption("LIBKML_USE_SIMPLEFIELD", "YES")); + + m_bUpdateIsFolder = FALSE; + + /***** was the layer created from a DS::Open *****/ + + if ( !bNew ) { + + /***** get the number of features on the layer *****/ + + nFeatures = m_poKmlLayer->get_feature_array_size ( ); + + /***** get the field config *****/ + + struct fieldconfig oFC; + get_fieldconfig( &oFC ); + + /***** name field *****/ + + OGRFieldDefn oOgrFieldName ( oFC.namefield,OFTString ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldName ); + + /***** descripton field *****/ + + OGRFieldDefn oOgrFieldDesc ( oFC.descfield, OFTString ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldDesc ); + + /***** timestamp field *****/ + + OGRFieldDefn oOgrFieldTs ( oFC.tsfield, OFTDateTime ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldTs ); + + /***** timespan begin field *****/ + + OGRFieldDefn oOgrFieldBegin ( oFC.beginfield, OFTDateTime ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldBegin ); + + /***** timespan end field *****/ + + OGRFieldDefn oOgrFieldEnd ( oFC.endfield, OFTDateTime ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldEnd ); + + /***** altitudeMode field *****/ + + OGRFieldDefn oOgrFieldAltitudeMode ( oFC.altitudeModefield, OFTString ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldAltitudeMode ); + + /***** tessellate field *****/ + + OGRFieldDefn oOgrFieldTessellate ( oFC.tessellatefield, OFTInteger ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldTessellate ); + + /***** extrude field *****/ + + OGRFieldDefn oOgrFieldExtrude ( oFC.extrudefield, OFTInteger ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldExtrude ); + + /***** visibility field *****/ + + OGRFieldDefn oOgrFieldVisibility ( oFC.visibilityfield, OFTInteger ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldVisibility ); + + /***** draw order field *****/ + + OGRFieldDefn oOgrFieldDrawOrder ( oFC.drawOrderfield, OFTInteger ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldDrawOrder ); + + /***** icon field *****/ + + OGRFieldDefn oOgrFieldIcon ( oFC.iconfield, OFTString ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrFieldIcon ); + + /***** get the styles *****/ + + if ( m_poKmlLayer->IsA ( kmldom::Type_Document ) ) + ParseStyles ( AsDocument ( m_poKmlLayer ), &m_poStyleTable ); + + /***** get the schema if the layer is a Document *****/ + + if ( m_poKmlLayer->IsA ( kmldom::Type_Document ) ) { + DocumentPtr poKmlDocument = AsDocument ( m_poKmlLayer ); + + if ( poKmlDocument->get_schema_array_size ( ) ) { + m_poKmlSchema = poKmlDocument->get_schema_array_at ( 0 ); + kml2FeatureDef ( m_poKmlSchema, m_poOgrFeatureDefn ); + } + } + + /***** the schema is somewhere else *****/ + + if (m_poKmlSchema == NULL) { + + /***** try to find the correct schema *****/ + + int bHasHeading = FALSE, bHasTilt = FALSE, bHasRoll = FALSE; + int bHasSnippet = FALSE; + FeaturePtr poKmlFeature; + + /***** find the first placemark *****/ + + do { + if ( iFeature >= nFeatures ) + break; + + poKmlFeature = + m_poKmlLayer->get_feature_array_at ( iFeature++ ); + + if( poKmlFeature->Type() == kmldom::Type_Placemark ) + { + PlacemarkPtr poKmlPlacemark = AsPlacemark ( poKmlFeature ); + if( !poKmlPlacemark->has_geometry ( ) && + poKmlPlacemark->has_abstractview ( ) && + poKmlPlacemark->get_abstractview()->IsA( kmldom::Type_Camera) ) + { + const CameraPtr& camera = AsCamera(poKmlPlacemark->get_abstractview()); + if( camera->has_heading() && !bHasHeading ) + { + bHasHeading = TRUE; + OGRFieldDefn oOgrField ( oFC.headingfield, OFTReal ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrField ); + } + if( camera->has_tilt() && !bHasTilt ) + { + bHasTilt = TRUE; + OGRFieldDefn oOgrField ( oFC.tiltfield, OFTReal ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrField ); + } + if( camera->has_roll() && !bHasRoll ) + { + bHasRoll = TRUE; + OGRFieldDefn oOgrField ( oFC.rollfield, OFTReal ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrField ); + } + } + } + if( !bHasSnippet && poKmlFeature->has_snippet() ) + { + bHasSnippet = TRUE; + OGRFieldDefn oOgrField ( oFC.snippetfield, OFTString ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrField ); + } + } while ( poKmlFeature->Type ( ) != kmldom::Type_Placemark ); + + if ( iFeature <= nFeatures && poKmlFeature && + poKmlFeature->Type ( ) == kmldom::Type_Placemark && + poKmlFeature->has_extendeddata ( ) ) { + + ExtendedDataPtr poKmlExtendedData = poKmlFeature-> + get_extendeddata ( ); + + if ( poKmlExtendedData->get_schemadata_array_size ( ) > 0 ) { + SchemaDataPtr poKmlSchemaData = poKmlExtendedData-> + get_schemadata_array_at ( 0 ); + + if ( poKmlSchemaData->has_schemaurl ( ) ) { + + std::string oKmlSchemaUrl = poKmlSchemaData-> + get_schemaurl ( ); + if ( ( m_poKmlSchema = + m_poOgrDS->FindSchema ( oKmlSchemaUrl. + c_str ( ) ) ) ) { + kml2FeatureDef ( m_poKmlSchema, + m_poOgrFeatureDefn ); + } + } + } + else if ( poKmlExtendedData->get_data_array_size() > 0 ) + { + /* Use the of the first placemark to build the feature definition */ + /* If others have different fields, too bad... */ + int bLaunderFieldNames = + CSLTestBoolean(CPLGetConfigOption("LIBKML_LAUNDER_FIELD_NAMES", "YES")); + size_t nDataArraySize = poKmlExtendedData->get_data_array_size(); + for(size_t i=0; i < nDataArraySize; i++) + { + const DataPtr& data = poKmlExtendedData->get_data_array_at(i); + if (data->has_name()) + { + CPLString osName = data->get_name(); + if (bLaunderFieldNames) + osName = LaunderFieldNames(osName); + OGRFieldDefn oOgrField ( osName, + OFTString ); + m_poOgrFeatureDefn->AddFieldDefn ( &oOgrField ); + } + } + } + } + + iFeature = 0; + + } + + + + /***** check if any features are another layer *****/ + + m_poOgrDS->ParseLayers ( m_poKmlLayer, poSpatialRef ); + + } + + /***** it was from a DS::CreateLayer *****/ + + else { + + /***** mark the layer as updated *****/ + + bUpdated = TRUE; + } + +} + +/****************************************************************************** + OGRLIBKMLLayer Destructor + + Args: none + + Returns: nothing + +******************************************************************************/ + +OGRLIBKMLLayer::~OGRLIBKMLLayer ( ) +{ + + CPLFree ( ( void * )m_pszName ); + CPLFree ( ( void * )m_pszFileName ); + m_poOgrSRS->Release(); + + m_poOgrFeatureDefn->Release ( ); + + +} + + +/****************************************************************************** + Method to get the next feature on the layer + + Args: none + + Returns: The next feature, or NULL if there is no more + + this function copyed from the sqlite driver +******************************************************************************/ + +OGRFeature *OGRLIBKMLLayer::GetNextFeature() + +{ + for( ; TRUE; ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +/****************************************************************************** + Method to get the next feature on the layer + + Args: none + + Returns: The next feature, or NULL if there is no more + +******************************************************************************/ + +OGRFeature *OGRLIBKMLLayer::GetNextRawFeature ( + ) +{ + FeaturePtr poKmlFeature; + OGRFeature *poOgrFeature = NULL; + + if( m_poKmlLayer == NULL ) + return NULL; + + /***** loop over the kml features to find the next placemark *****/ + + do { + if ( iFeature >= nFeatures ) + break; + + /***** get the next kml feature in the container *****/ + + poKmlFeature = m_poKmlLayer->get_feature_array_at ( iFeature++ ); + + /***** what type of kml feature in the container? *****/ + + switch (poKmlFeature->Type ( )) { + + case kmldom::Type_Placemark: + poOgrFeature = kml2feat ( AsPlacemark ( poKmlFeature ), + m_poOgrDS, this, + m_poOgrFeatureDefn, m_poOgrSRS ); + break; + + case kmldom::Type_GroundOverlay: + if (m_bReadGroundOverlay) { + poOgrFeature = + kmlgroundoverlay2feat ( AsGroundOverlay ( poKmlFeature ), + m_poOgrDS, this, + m_poOgrFeatureDefn, + m_poOgrSRS ); + } + break; + + default: + break; + + } + + } while ( !poOgrFeature ); + + /***** set the FID on the ogr feature *****/ + + if (poOgrFeature) + poOgrFeature->SetFID(nFID ++); + + return poOgrFeature; +} + +/****************************************************************************** + method to add a feature to a layer + + Args: poOgrFeat pointer to the feature to add + + Returns: OGRERR_NONE, or OGRERR_UNSUPPORTED_OPERATION of the layer is + not writeable + +******************************************************************************/ + +OGRErr OGRLIBKMLLayer::ICreateFeature ( + OGRFeature * poOgrFeat ) +{ + + if ( !bUpdate ) + return OGRERR_UNSUPPORTED_OPERATION; + + if( m_bRegionBoundsAuto && poOgrFeat->GetGeometryRef() != NULL && + !(poOgrFeat->GetGeometryRef()->IsEmpty()) ) + { + OGREnvelope sEnvelope; + poOgrFeat->GetGeometryRef()->getEnvelope(&sEnvelope); + m_dfRegionMinX = MIN(m_dfRegionMinX, sEnvelope.MinX); + m_dfRegionMinY = MIN(m_dfRegionMinY, sEnvelope.MinY); + m_dfRegionMaxX = MAX(m_dfRegionMaxX, sEnvelope.MaxX); + m_dfRegionMaxY = MAX(m_dfRegionMaxY, sEnvelope.MaxY); + } + + FeaturePtr poKmlFeature = + feat2kml ( m_poOgrDS, this, poOgrFeat, m_poOgrDS->GetKmlFactory ( ), + m_bUseSimpleField ); + + if( m_poKmlLayer != NULL ) + m_poKmlLayer->add_feature ( poKmlFeature ); + else + { + CPLAssert( m_poKmlUpdate != NULL ); + KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory ( ); + CreatePtr poCreate = poKmlFactory->CreateCreate(); + ContainerPtr poContainer; + if( m_bUpdateIsFolder ) + poContainer = poKmlFactory->CreateFolder(); + else + poContainer = poKmlFactory->CreateDocument(); + poContainer->set_targetid(OGRLIBKMLGetSanitizedNCName(GetName())); + poContainer->add_feature ( poKmlFeature ); + poCreate->add_container(poContainer); + m_poKmlUpdate->add_updateoperation(poCreate); + } + + /***** update the layer class count of features *****/ + + if( m_poKmlLayer != NULL ) + { + nFeatures++; + + const char* pszId = CPLSPrintf("%s.%d", + OGRLIBKMLGetSanitizedNCName(GetName()).c_str(), nFeatures); + poOgrFeat->SetFID(nFeatures); + poKmlFeature->set_id(pszId); + } + else + { + if( poOgrFeat->GetFID() < 0 ) + { + static int bAlreadyWarned = FALSE; + if( !bAlreadyWarned ) + { + bAlreadyWarned = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "It is recommended to define a FID when calling CreateFeature() in a update document"); + } + } + else + { + const char* pszId = CPLSPrintf("%s." CPL_FRMT_GIB, + OGRLIBKMLGetSanitizedNCName(GetName()).c_str(), poOgrFeat->GetFID()); + poOgrFeat->SetFID(nFeatures); + poKmlFeature->set_id(pszId); + } + } + + /***** mark the layer as updated *****/ + + bUpdated = TRUE; + m_poOgrDS->Updated ( ); + + return OGRERR_NONE; +} + + +/****************************************************************************** + method to update a feature to a layer. Only work on a NetworkLinkControl/Update + + Args: poOgrFeat pointer to the feature to update + + Returns: OGRERR_NONE, or OGRERR_UNSUPPORTED_OPERATION of the layer is + not writeable + +******************************************************************************/ + +OGRErr OGRLIBKMLLayer::ISetFeature ( OGRFeature * poOgrFeat ) +{ + if( !bUpdate || m_poKmlUpdate == NULL ) + return OGRERR_UNSUPPORTED_OPERATION; + if( poOgrFeat->GetFID() == OGRNullFID ) + return OGRERR_FAILURE; + + FeaturePtr poKmlFeature = + feat2kml ( m_poOgrDS, this, poOgrFeat, m_poOgrDS->GetKmlFactory ( ), + m_bUseSimpleField ); + + KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory ( ); + ChangePtr poChange = poKmlFactory->CreateChange(); + poChange->add_object(poKmlFeature); + m_poKmlUpdate->add_updateoperation(poChange); + + const char* pszId = CPLSPrintf("%s." CPL_FRMT_GIB, + OGRLIBKMLGetSanitizedNCName(GetName()).c_str(), poOgrFeat->GetFID()); + poKmlFeature->set_targetid(pszId); + + /***** mark the layer as updated *****/ + + bUpdated = TRUE; + m_poOgrDS->Updated ( ); + + return OGRERR_NONE; +} + +/****************************************************************************** + method to delete a feature to a layer. Only work on a NetworkLinkControl/Update + + Args: nFID id of the feature to delete + + Returns: OGRERR_NONE, or OGRERR_UNSUPPORTED_OPERATION of the layer is + not writeable + +******************************************************************************/ + +OGRErr OGRLIBKMLLayer::DeleteFeature( GIntBig nFID ) +{ + if( !bUpdate || m_poKmlUpdate == NULL ) + return OGRERR_UNSUPPORTED_OPERATION; + + KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory ( ); + DeletePtr poDelete = poKmlFactory->CreateDelete(); + m_poKmlUpdate->add_updateoperation(poDelete); + PlacemarkPtr poKmlPlacemark = poKmlFactory->CreatePlacemark(); + poDelete->add_feature(poKmlPlacemark); + + const char* pszId = CPLSPrintf("%s." CPL_FRMT_GIB, + OGRLIBKMLGetSanitizedNCName(GetName()).c_str(), nFID); + poKmlPlacemark->set_targetid(pszId); + + /***** mark the layer as updated *****/ + + bUpdated = TRUE; + m_poOgrDS->Updated ( ); + + return OGRERR_NONE; +} + +/****************************************************************************** + method to get the number of features on the layer + + Args: bForce no effect as of now + + Returns: the number of features on the layer + + Note: the result can include links, folders and other items that are + not supported by OGR + +******************************************************************************/ + +GIntBig OGRLIBKMLLayer::GetFeatureCount ( + int bForce ) +{ + + + int i = 0; + if (m_poFilterGeom != NULL || m_poAttrQuery != NULL ) { + i = OGRLayer::GetFeatureCount( bForce ); + } + + else if( m_poKmlLayer != NULL ) { + size_t iKmlFeature; + size_t nKmlFeatures = m_poKmlLayer->get_feature_array_size ( ); + FeaturePtr poKmlFeature; + + /***** loop over the kml features in the container *****/ + + for ( iKmlFeature = 0; iKmlFeature < nKmlFeatures; iKmlFeature++ ) { + poKmlFeature = m_poKmlLayer->get_feature_array_at ( iKmlFeature ); + + /***** what type of kml feature? *****/ + + switch (poKmlFeature->Type ( )) { + + case kmldom::Type_Placemark: + i++; + break; + + case kmldom::Type_GroundOverlay: + if (m_bReadGroundOverlay) + i++; + break; + + default: + break; + + } + } + } + + return i; +} + +/****************************************************************************** + GetExtent() + + Args: psExtent pointer to the Envelope to store the result in + bForce no effect as of now + + Returns: nothing + +******************************************************************************/ + +OGRErr OGRLIBKMLLayer::GetExtent ( + OGREnvelope * psExtent, + int bForce ) +{ + Bbox oKmlBbox; + + if ( m_poKmlLayer != NULL && + kmlengine:: + GetFeatureBounds ( AsFeature ( m_poKmlLayer ), &oKmlBbox ) ) { + psExtent->MinX = oKmlBbox.get_west ( ); + psExtent->MinY = oKmlBbox.get_south ( ); + psExtent->MaxX = oKmlBbox.get_east ( ); + psExtent->MaxY = oKmlBbox.get_north ( ); + + return OGRERR_NONE; + } + else + return OGRLayer::GetExtent(psExtent, bForce); +} + + + + +/****************************************************************************** + Method to create a field on a layer + + Args: poField pointer to the Field Definition to add + bApproxOK no effect as of now + + Returns: OGRERR_NONE on success or OGRERR_UNSUPPORTED_OPERATION if the + layer is not writeable + +******************************************************************************/ + +OGRErr OGRLIBKMLLayer::CreateField ( + OGRFieldDefn * poField, + CPL_UNUSED int bApproxOK ) +{ + if ( !bUpdate ) + return OGRERR_UNSUPPORTED_OPERATION; + + if( m_bUseSimpleField ) + { + SimpleFieldPtr poKmlSimpleField = NULL; + + if ( (poKmlSimpleField = + FieldDef2kml ( poField, m_poOgrDS->GetKmlFactory ( ) )) != NULL ) + { + if( m_poKmlSchema == NULL ) + { + /***** create a new schema *****/ + + KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory ( ); + + m_poKmlSchema = poKmlFactory->CreateSchema ( ); + + /***** set the id on the new schema *****/ + + std::string oKmlSchemaID = OGRLIBKMLGetSanitizedNCName(m_pszName); + oKmlSchemaID.append ( ".schema" ); + m_poKmlSchema->set_id ( oKmlSchemaID ); + } + + m_poKmlSchema->add_simplefield ( poKmlSimpleField ); + } + } + + m_poOgrFeatureDefn->AddFieldDefn ( poField ); + + /***** mark the layer as updated *****/ + + bUpdated = TRUE; + m_poOgrDS->Updated ( ); + + return OGRERR_NONE; +} + + +/****************************************************************************** + method to write the datasource to disk + + Args: none + + Returns nothing + +******************************************************************************/ + +OGRErr OGRLIBKMLLayer::SyncToDisk ( + ) +{ + + return OGRERR_NONE; +} + +/****************************************************************************** + method to get a layers style table + + Args: none + + Returns: pointer to the layers style table, or NULL if it does + not have one + +******************************************************************************/ + +OGRStyleTable *OGRLIBKMLLayer::GetStyleTable ( + ) +{ + + return m_poStyleTable; +} + +/****************************************************************************** + method to write a style table to a layer + + Args: poStyleTable pointer to the style table to add + + Returns: nothing + + note: this method assumes ownership of the style table +******************************************************************************/ + +void OGRLIBKMLLayer::SetStyleTableDirectly ( + OGRStyleTable * poStyleTable ) +{ + + if ( !bUpdate || m_poKmlLayer == NULL ) + return; + + KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory ( ); + + if ( m_poStyleTable ) + delete m_poStyleTable; + + m_poStyleTable = poStyleTable; + + if ( m_poKmlLayer->IsA ( kmldom::Type_Document ) ) { + + /***** delete all the styles *****/ + + DocumentPtr poKmlDocument = AsDocument ( m_poKmlLayer ); + size_t nKmlStyles = poKmlDocument->get_schema_array_size ( ); + int iKmlStyle; + + for ( iKmlStyle = nKmlStyles - 1; iKmlStyle >= 0; iKmlStyle-- ) { + poKmlDocument->DeleteStyleSelectorAt ( iKmlStyle ); + } + + /***** add the new style table to the document *****/ + + styletable2kml ( poStyleTable, poKmlFactory, + AsContainer ( poKmlDocument ) ); + + } + + /***** mark the layer as updated *****/ + + bUpdated = TRUE; + m_poOgrDS->Updated ( ); + + return; +} + +/****************************************************************************** + method to write a style table to a layer + + Args: poStyleTable pointer to the style table to add + + Returns: nothing + + note: this method copys the style table, and the user will still be + responsible for its destruction +******************************************************************************/ + +void OGRLIBKMLLayer::SetStyleTable ( + OGRStyleTable * poStyleTable ) +{ + + if ( !bUpdate || m_poKmlLayer == NULL ) + return; + + if ( poStyleTable ) + SetStyleTableDirectly ( poStyleTable->Clone ( ) ); + else + SetStyleTableDirectly ( NULL ); + return; +} + +/****************************************************************************** + Test if capability is available. + + Args: pszCap layer capability name to test + + Returns: True if the layer supports the capability, otherwise false + +******************************************************************************/ + +int OGRLIBKMLLayer::TestCapability ( + const char *pszCap ) +{ + int result = FALSE; + + if ( EQUAL ( pszCap, OLCRandomRead ) ) + result = FALSE; + else if ( EQUAL ( pszCap, OLCSequentialWrite ) ) + result = bUpdate; + else if ( EQUAL ( pszCap, OLCRandomWrite ) ) + result = FALSE; + else if ( EQUAL ( pszCap, OLCFastFeatureCount ) ) + result = FALSE; + else if ( EQUAL ( pszCap, OLCFastSetNextByIndex ) ) + result = FALSE; + else if ( EQUAL ( pszCap, OLCCreateField ) ) + result = bUpdate; + else if ( EQUAL ( pszCap, OLCDeleteFeature ) ) + result = FALSE; + else if ( EQUAL(pszCap, OLCStringsAsUTF8) ) + result = TRUE; + + return result; +} + +/************************************************************************/ +/* LaunderFieldNames() */ +/************************************************************************/ + +CPLString OGRLIBKMLLayer::LaunderFieldNames(CPLString osName) +{ + CPLString osLaunderedName; + for(int i=0;i<(int)osName.size();i++) + { + char ch = osName[i]; + if ((ch >= '0' && ch <= '9') || + (ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch == '_')) + osLaunderedName += ch; + else + osLaunderedName += "_"; + } + return osLaunderedName; +} + +/************************************************************************/ +/* SetLookAt() */ +/************************************************************************/ + +void OGRLIBKMLLayer::SetLookAt( const char* pszLookatLongitude, + const char* pszLookatLatitude, + const char* pszLookatAltitude, + const char* pszLookatHeading, + const char* pszLookatTilt, + const char* pszLookatRange, + const char* pszLookatAltitudeMode ) +{ + KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory ( ); + LookAtPtr lookAt = poKmlFactory->CreateLookAt(); + lookAt->set_latitude(CPLAtof(pszLookatLatitude)); + lookAt->set_longitude(CPLAtof(pszLookatLongitude)); + if( pszLookatAltitude != NULL ) + lookAt->set_altitude(CPLAtof(pszLookatAltitude)); + if( pszLookatHeading != NULL ) + lookAt->set_heading(CPLAtof(pszLookatHeading)); + if( pszLookatTilt != NULL ) + { + double dfTilt = CPLAtof(pszLookatTilt); + if( dfTilt >= 0 && dfTilt <= 90 ) + lookAt->set_tilt(dfTilt); + else + CPLError(CE_Warning, CPLE_AppDefined, "Invalid value for tilt: %s", + pszLookatTilt); + } + lookAt->set_range(CPLAtof(pszLookatRange)); + if( pszLookatAltitudeMode != NULL ) + { + int isGX = FALSE; + int iAltitudeMode = kmlAltitudeModeFromString(pszLookatAltitudeMode, isGX); + if( iAltitudeMode != kmldom::ALTITUDEMODE_CLAMPTOGROUND && + pszLookatAltitude == NULL ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Lookat altitude should be present for altitudeMode = %s", + pszLookatAltitudeMode); + } + else if( isGX ) + lookAt->set_gx_altitudemode(iAltitudeMode); + else + lookAt->set_altitudemode(iAltitudeMode); + } + + m_poKmlLayer->set_abstractview(lookAt); +} + +/************************************************************************/ +/* SetCamera() */ +/************************************************************************/ + +void OGRLIBKMLLayer::SetCamera( const char* pszCameraLongitude, + const char* pszCameraLatitude, + const char* pszCameraAltitude, + const char* pszCameraHeading, + const char* pszCameraTilt, + const char* pszCameraRoll, + const char* pszCameraAltitudeMode ) +{ + int isGX = FALSE; + int iAltitudeMode = kmlAltitudeModeFromString(pszCameraAltitudeMode, isGX); + if( isGX == FALSE && iAltitudeMode == kmldom::ALTITUDEMODE_CLAMPTOGROUND ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Camera altitudeMode should be different from %s", + pszCameraAltitudeMode); + return; + } + KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory ( ); + CameraPtr camera = poKmlFactory->CreateCamera(); + camera->set_latitude(CPLAtof(pszCameraLatitude)); + camera->set_longitude(CPLAtof(pszCameraLongitude)); + camera->set_altitude(CPLAtof(pszCameraAltitude)); + if( pszCameraHeading != NULL ) + camera->set_heading(CPLAtof(pszCameraHeading)); + if( pszCameraTilt != NULL ) + { + double dfTilt = CPLAtof(pszCameraTilt); + if( dfTilt >= 0 && dfTilt <= 90 ) + camera->set_tilt(dfTilt); + else + CPLError(CE_Warning, CPLE_AppDefined, "Invalid value for tilt: %s", + pszCameraTilt); + } + if( pszCameraRoll != NULL ) + camera->set_roll(CPLAtof(pszCameraRoll)); + if( isGX ) + camera->set_gx_altitudemode(iAltitudeMode); + else + camera->set_altitudemode(iAltitudeMode); + + m_poKmlLayer->set_abstractview(camera); +} + +/************************************************************************/ +/* SetWriteRegion() */ +/************************************************************************/ + +void OGRLIBKMLLayer::SetWriteRegion(double dfMinLodPixels, + double dfMaxLodPixels, + double dfMinFadeExtent, + double dfMaxFadeExtent) +{ + m_bWriteRegion = TRUE; + m_bRegionBoundsAuto = TRUE; + m_dfRegionMinLodPixels = dfMinLodPixels; + m_dfRegionMaxLodPixels = dfMaxLodPixels; + m_dfRegionMinFadeExtent = dfMinFadeExtent; + m_dfRegionMaxFadeExtent = dfMaxFadeExtent; +} + +/************************************************************************/ +/* SetRegionBounds() */ +/************************************************************************/ + +void OGRLIBKMLLayer::SetRegionBounds(double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY) +{ + m_bRegionBoundsAuto = FALSE; + m_dfRegionMinX = dfMinX; + m_dfRegionMinY = dfMinY; + m_dfRegionMaxX = dfMaxX; + m_dfRegionMaxY = dfMaxY; +} + +/************************************************************************/ +/* Finalize() */ +/************************************************************************/ + +void OGRLIBKMLLayer::Finalize(DocumentPtr poKmlDocument) +{ + KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory ( ); + + if( m_bWriteRegion && m_dfRegionMinX < m_dfRegionMaxX ) + { + RegionPtr region = poKmlFactory->CreateRegion(); + + LatLonAltBoxPtr box = poKmlFactory->CreateLatLonAltBox(); + box->set_west(m_dfRegionMinX); + box->set_east(m_dfRegionMaxX); + box->set_south(m_dfRegionMinY); + box->set_north(m_dfRegionMaxY); + region->set_latlonaltbox(box); + + LodPtr lod = poKmlFactory->CreateLod(); + lod->set_minlodpixels(m_dfRegionMinLodPixels); + lod->set_maxlodpixels(m_dfRegionMaxLodPixels); + if( (m_dfRegionMinFadeExtent != 0 || m_dfRegionMaxFadeExtent != 0) && + m_dfRegionMinFadeExtent + m_dfRegionMaxFadeExtent < + m_dfRegionMaxLodPixels - m_dfRegionMinLodPixels ) + { + lod->set_minfadeextent(m_dfRegionMinFadeExtent); + lod->set_maxfadeextent(m_dfRegionMaxFadeExtent); + } + + region->set_lod(lod); + m_poKmlLayer->set_region(region); + } + + createkmlliststyle (poKmlFactory, + GetName(), + m_poKmlLayer, + poKmlDocument, + osListStyleType, + osListStyleIconHref); +} + +/************************************************************************/ +/* LIBKMLGetUnits() */ +/************************************************************************/ + +static int LIBKMLGetUnits(const char* pszUnits) +{ + if( EQUAL(pszUnits, "fraction") ) + return kmldom::UNITS_FRACTION; + if( EQUAL(pszUnits, "pixels") ) + return kmldom::UNITS_PIXELS; + if( EQUAL(pszUnits, "insetPixels") ) + return kmldom::UNITS_INSETPIXELS; + return kmldom::UNITS_FRACTION; +} + +/************************************************************************/ +/* LIBKMLSetVec2() */ +/************************************************************************/ + +static void LIBKMLSetVec2(kmldom::Vec2Ptr vec2, const char* pszX, const char* pszY, + const char* pszXUnits, const char* pszYUnits) +{ + double dfX = CPLAtof(pszX); + double dfY = CPLAtof(pszY); + vec2->set_x(dfX); + vec2->set_y(dfY); + if( dfX <= 1 && dfY <= 1 ) + { + if( pszXUnits == NULL ) pszXUnits = "fraction"; + if( pszYUnits == NULL ) pszYUnits = "fraction"; + } + else + { + if( pszXUnits == NULL ) pszXUnits = "pixels"; + if( pszYUnits == NULL ) pszYUnits = "pixels"; + } + vec2->set_xunits(LIBKMLGetUnits(pszXUnits)); + vec2->set_yunits(LIBKMLGetUnits(pszYUnits)); +} + +/************************************************************************/ +/* SetScreenOverlay() */ +/************************************************************************/ + +void OGRLIBKMLLayer::SetScreenOverlay(const char* pszSOHref, + const char* pszSOName, + const char* pszSODescription, + const char* pszSOOverlayX, + const char* pszSOOverlayY, + const char* pszSOOverlayXUnits, + const char* pszSOOverlayYUnits, + const char* pszSOScreenX, + const char* pszSOScreenY, + const char* pszSOScreenXUnits, + const char* pszSOScreenYUnits, + const char* pszSOSizeX, + const char* pszSOSizeY, + const char* pszSOSizeXUnits, + const char* pszSOSizeYUnits) +{ + KmlFactory *poKmlFactory = m_poOgrDS->GetKmlFactory ( ); + ScreenOverlayPtr so = poKmlFactory->CreateScreenOverlay(); + + if( pszSOName != NULL ) + so->set_name(pszSOName); + if( pszSODescription != NULL ) + so->set_description(pszSODescription); + + IconPtr icon = poKmlFactory->CreateIcon(); + icon->set_href(pszSOHref); + so->set_icon(icon); + + if( pszSOOverlayX != NULL && pszSOOverlayY != NULL ) + { + kmldom::OverlayXYPtr overlayxy = poKmlFactory->CreateOverlayXY(); + LIBKMLSetVec2(overlayxy, pszSOOverlayX, pszSOOverlayY, + pszSOOverlayXUnits, pszSOOverlayYUnits); + so->set_overlayxy(overlayxy); + } + + if( pszSOScreenX != NULL && pszSOScreenY != NULL ) + { + kmldom::ScreenXYPtr screenxy = poKmlFactory->CreateScreenXY(); + LIBKMLSetVec2(screenxy, pszSOScreenX, pszSOScreenY, + pszSOScreenXUnits, pszSOScreenYUnits); + so->set_screenxy(screenxy); + } + else + { + kmldom::ScreenXYPtr screenxy = poKmlFactory->CreateScreenXY(); + LIBKMLSetVec2(screenxy, "0.05", "0.05", NULL, NULL); + so->set_screenxy(screenxy); + } + + if( pszSOSizeX != NULL && pszSOSizeY != NULL ) + { + kmldom::SizePtr sizexy = poKmlFactory->CreateSize(); + LIBKMLSetVec2(sizexy, pszSOSizeX, pszSOSizeY, + pszSOSizeXUnits, pszSOSizeYUnits); + so->set_size(sizexy); + } + + m_poKmlLayer->add_feature(so); +} + +/************************************************************************/ +/* SetListStyle() */ +/************************************************************************/ + +void OGRLIBKMLLayer::SetListStyle(const char* pszListStyleType, + const char* pszListStyleIconHref) +{ + osListStyleType = (pszListStyleType) ? pszListStyleType : ""; + osListStyleIconHref = (pszListStyleIconHref) ? pszListStyleIconHref : ""; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlstyle.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlstyle.cpp new file mode 100644 index 000000000..b3658c090 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlstyle.cpp @@ -0,0 +1,1263 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * Copyright (c) 2011-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include + +#include + +#include +#include +#include + +using kmldom::KmlFactory;; +using kmldom::ElementPtr; +using kmldom::ObjectPtr; +using kmldom::FeaturePtr; +using kmldom::StylePtr; +using kmldom::StyleMapPtr; +using kmldom::STYLESTATE_NORMAL; +using kmldom::STYLESTATE_HIGHLIGHT; +using kmldom::StyleSelectorPtr; +using kmldom::LineStylePtr; +using kmldom::PolyStylePtr; +using kmldom::IconStylePtr; +using kmldom::IconStyleIconPtr; +using kmldom::LabelStylePtr; +using kmldom::BalloonStylePtr; +using kmldom::HotSpotPtr; +using kmlbase::Color32; +using kmldom::PairPtr; +using kmldom::KmlPtr; +using kmldom::ListStylePtr; +using kmldom::ItemIconPtr; + +#include "ogrlibkmlstyle.h" +#include "ogr_libkml.h" + +/****************************************************************************** + generic function to parse a stylestring and add to a kml style + +args: + pszStyleString the stylestring to parse + poKmlStyle the kml style to add to (or NULL) + poKmlFactory the kml dom factory + +returns: + the kml style + +******************************************************************************/ + +StylePtr addstylestring2kml ( + const char *pszStyleString, + StylePtr poKmlStyle, + KmlFactory * poKmlFactory, + FeaturePtr poKmlFeature ) +{ + + LineStylePtr poKmlLineStyle = NULL; + PolyStylePtr poKmlPolyStyle = NULL; + IconStylePtr poKmlIconStyle = NULL; + LabelStylePtr poKmlLabelStyle = NULL; + + /***** just bail now if stylestring is empty *****/ + + if ( !pszStyleString || !*pszStyleString ) { + return poKmlStyle; + } + + /***** create and init a style mamager with the style string *****/ + + OGRStyleMgr *poOgrSM = new OGRStyleMgr; + + poOgrSM->InitStyleString ( pszStyleString ); + + /***** loop though the style parts *****/ + + int i; + + for ( i = 0; i < poOgrSM->GetPartCount ( NULL ); i++ ) { + OGRStyleTool *poOgrST = poOgrSM->GetPart ( i, NULL ); + + if ( !poOgrST ) { + continue; + } + + switch ( poOgrST->GetType ( ) ) { + case OGRSTCPen: + { + GBool nullcheck; + + poKmlLineStyle = poKmlFactory->CreateLineStyle ( ); + + OGRStylePen *poStylePen = ( OGRStylePen * ) poOgrST; + + /***** pen color *****/ + + int nR, + nG, + nB, + nA; + + const char *pszcolor = poStylePen->Color ( nullcheck ); + + if ( !nullcheck + && poStylePen->GetRGBFromString ( pszcolor, nR, nG, nB, nA ) ) { + poKmlLineStyle->set_color ( Color32 ( nA, nB, nG, nR ) ); + } + poStylePen->SetUnit(OGRSTUPixel); + double dfWidth = poStylePen->Width ( nullcheck ); + + if ( nullcheck ) + dfWidth = 1.0; + + poKmlLineStyle->set_width ( dfWidth ); + + break; + } + case OGRSTCBrush: + { + GBool nullcheck; + + OGRStyleBrush *poStyleBrush = ( OGRStyleBrush * ) poOgrST; + + /***** brush color *****/ + + int nR, + nG, + nB, + nA; + + const char *pszcolor = poStyleBrush->ForeColor ( nullcheck ); + + if ( !nullcheck + && poStyleBrush->GetRGBFromString ( pszcolor, nR, nG, nB, nA ) ) { + poKmlPolyStyle = poKmlFactory->CreatePolyStyle ( ); + poKmlPolyStyle->set_color ( Color32 ( nA, nB, nG, nR ) ); + } + + + break; + } + case OGRSTCSymbol: + { + GBool nullcheck; + GBool nullcheck2; + + OGRStyleSymbol *poStyleSymbol = ( OGRStyleSymbol * ) poOgrST; + + /***** id (kml icon) *****/ + + const char *pszId = poStyleSymbol->Id ( nullcheck ); + + if ( !nullcheck ) { + if ( !poKmlIconStyle) + poKmlIconStyle = poKmlFactory->CreateIconStyle ( ); + + /***** split it at the ,'s *****/ + + char **papszTokens = + CSLTokenizeString2 ( pszId, ",", + CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES | + CSLT_STRIPENDSPACES ); + + if ( papszTokens ) { + + /***** for lack of a better solution just take the first one *****/ + //todo come up with a better idea + + if ( papszTokens[0] ) { + IconStyleIconPtr poKmlIcon = + poKmlFactory->CreateIconStyleIcon ( ); + poKmlIcon->set_href ( papszTokens[0] ); + poKmlIconStyle->set_icon ( poKmlIcon ); + } + + CSLDestroy ( papszTokens ); + + } + + + } + + /***** heading *****/ + + double heading = poStyleSymbol->Angle ( nullcheck ); + + if ( !nullcheck ) { + if ( !poKmlIconStyle) + poKmlIconStyle = poKmlFactory->CreateIconStyle ( ); + poKmlIconStyle->set_heading ( heading ); + } + + /***** scale *****/ + + double dfScale = poStyleSymbol->Size ( nullcheck ); + + if ( !nullcheck ) { + if ( !poKmlIconStyle) + poKmlIconStyle = poKmlFactory->CreateIconStyle ( ); + + poKmlIconStyle->set_scale ( dfScale ); + } + + /***** color *****/ + + int nR, + nG, + nB, + nA; + + const char *pszcolor = poStyleSymbol->Color ( nullcheck ); + + if ( !nullcheck && poOgrST->GetRGBFromString ( pszcolor, nR, nG, nB, nA ) ) { + poKmlIconStyle->set_color ( Color32 ( nA, nB, nG, nR ) ); + } + + /***** hotspot *****/ + + double dfDx = poStyleSymbol->SpacingX ( nullcheck ); + double dfDy = poStyleSymbol->SpacingY ( nullcheck2 ); + + if ( !nullcheck && !nullcheck2 ) { + if ( !poKmlIconStyle) + poKmlIconStyle = poKmlFactory->CreateIconStyle ( ); + + HotSpotPtr poKmlHotSpot = poKmlFactory->CreateHotSpot ( ); + + poKmlHotSpot->set_x ( dfDx ); + poKmlHotSpot->set_y ( dfDy ); + + poKmlIconStyle->set_hotspot ( poKmlHotSpot ); + } + + break; + } + case OGRSTCLabel: + { + GBool nullcheck; + GBool nullcheck2; + + OGRStyleLabel *poStyleLabel = ( OGRStyleLabel * ) poOgrST; + + /***** color *****/ + + int nR, + nG, + nB, + nA; + + const char *pszcolor = poStyleLabel->ForeColor ( nullcheck ); + + if ( !nullcheck + && poStyleLabel->GetRGBFromString ( pszcolor, nR, nG, nB, nA ) ) { + if( poKmlLabelStyle == NULL ) + poKmlLabelStyle = poKmlFactory->CreateLabelStyle ( ); + poKmlLabelStyle->set_color ( Color32 ( nA, nB, nG, nR ) ); + } + + /***** scale *****/ + + double dfScale = poStyleLabel->Stretch ( nullcheck ); + + if ( !nullcheck ) { + dfScale /= 100.0; + if( poKmlLabelStyle == NULL ) + poKmlLabelStyle = poKmlFactory->CreateLabelStyle ( ); + poKmlLabelStyle->set_scale ( dfScale ); + } + + /***** heading *****/ + + double heading = poStyleLabel->Angle ( nullcheck ); + + if ( !nullcheck ) { + if ( !poKmlIconStyle) { + poKmlIconStyle = poKmlFactory->CreateIconStyle ( ); + IconStyleIconPtr poKmlIcon = poKmlFactory->CreateIconStyleIcon ( ); + poKmlIconStyle->set_icon ( poKmlIcon ); + } + + poKmlIconStyle->set_heading ( heading ); + } + + /***** hotspot *****/ + + double dfDx = poStyleLabel->SpacingX ( nullcheck ); + double dfDy = poStyleLabel->SpacingY ( nullcheck2 ); + + if ( !nullcheck && !nullcheck2 ) { + if ( !poKmlIconStyle) { + poKmlIconStyle = poKmlFactory->CreateIconStyle ( ); + IconStyleIconPtr poKmlIcon = poKmlFactory->CreateIconStyleIcon ( ); + poKmlIconStyle->set_icon ( poKmlIcon ); + } + + HotSpotPtr poKmlHotSpot = poKmlFactory->CreateHotSpot ( ); + + poKmlHotSpot->set_x ( dfDx ); + poKmlHotSpot->set_y ( dfDy ); + + poKmlIconStyle->set_hotspot ( poKmlHotSpot ); + } + + /***** label text *****/ + + const char *pszText = poStyleLabel->TextString ( nullcheck ); + + if ( !nullcheck ) { + if ( poKmlFeature ) { + + poKmlFeature->set_name( pszText ); + } + } + + break; + } + case OGRSTCNone: + default: + break; + } + + delete poOgrST; + } + + if ( poKmlLineStyle || poKmlPolyStyle || poKmlIconStyle || poKmlLabelStyle ) + { + if( poKmlStyle == NULL ) + poKmlStyle = poKmlFactory->CreateStyle ( ); + + if ( poKmlLineStyle ) + poKmlStyle->set_linestyle ( poKmlLineStyle ); + + if ( poKmlPolyStyle ) + poKmlStyle->set_polystyle ( poKmlPolyStyle ); + + if ( poKmlIconStyle ) + poKmlStyle->set_iconstyle ( poKmlIconStyle ); + + if ( poKmlLabelStyle ) + poKmlStyle->set_labelstyle ( poKmlLabelStyle ); + } + + delete poOgrSM; + + return poKmlStyle; +} + +/****************************************************************************** + kml2pen +******************************************************************************/ + +OGRStylePen *kml2pen ( + LineStylePtr poKmlLineStyle, + OGRStylePen *poOgrStylePen); + +/****************************************************************************** + kml2brush +******************************************************************************/ + +OGRStyleBrush *kml2brush ( + PolyStylePtr poKmlPolyStyle, + OGRStyleBrush *poOgrStyleBrush); + +/****************************************************************************** + kml2symbol +******************************************************************************/ + +OGRStyleSymbol *kml2symbol ( + IconStylePtr poKmlIconStyle, + OGRStyleSymbol *poOgrStyleSymbol); + +/****************************************************************************** + kml2label +******************************************************************************/ + +OGRStyleLabel *kml2label ( + LabelStylePtr poKmlLabelStyle, + OGRStyleLabel *poOgrStyleLabel); + +/****************************************************************************** + kml2stylemgr +******************************************************************************/ + +void kml2stylestring ( + StylePtr poKmlStyle, + OGRStyleMgr * poOgrSM ) + +{ + + OGRStyleMgr * poOgrNewSM ; + OGRStyleTool *poOgrST = NULL; + OGRStyleTool *poOgrTmpST = NULL; + int i; + + poOgrNewSM = new OGRStyleMgr( NULL ); + + /***** linestyle / pen *****/ + + if ( poKmlStyle->has_linestyle ( ) ) { + + poOgrNewSM->InitStyleString ( NULL ); + + LineStylePtr poKmlLineStyle = poKmlStyle->get_linestyle ( ); + + poOgrTmpST = NULL; + for ( i = 0; i < poOgrSM->GetPartCount ( NULL ); i++ ) { + poOgrST = poOgrSM->GetPart ( i, NULL ); + + if ( !poOgrST ) + continue; + + if ( poOgrST->GetType ( ) == OGRSTCPen ) { + poOgrTmpST = poOgrST; + } + else { + poOgrNewSM->AddPart ( poOgrST ); + delete poOgrST; + } + } + + OGRStylePen *poOgrStylePen = kml2pen ( poKmlLineStyle, + ( OGRStylePen *) poOgrTmpST); + + poOgrNewSM->AddPart ( poOgrStylePen ); + + delete poOgrStylePen; + poOgrSM->InitStyleString ( poOgrNewSM->GetStyleString(NULL) ); + + } + + /***** polystyle / brush *****/ + + if ( poKmlStyle->has_polystyle ( ) ) { + + poOgrNewSM->InitStyleString ( NULL ); + + PolyStylePtr poKmlPolyStyle = poKmlStyle->get_polystyle ( ); + + poOgrTmpST = NULL; + for ( i = 0; i < poOgrSM->GetPartCount ( NULL ); i++ ) { + poOgrST = poOgrSM->GetPart ( i, NULL ); + + if ( !poOgrST ) + continue; + + if ( poOgrST->GetType ( ) == OGRSTCBrush ) { + poOgrTmpST = poOgrST; + } + else { + poOgrNewSM->AddPart ( poOgrST ); + delete poOgrST; + } + } + + OGRStyleBrush *poOgrStyleBrush = kml2brush ( poKmlPolyStyle, + ( OGRStyleBrush *) poOgrTmpST ); + + poOgrNewSM->AddPart ( poOgrStyleBrush ); + + delete poOgrStyleBrush; + poOgrSM->InitStyleString ( poOgrNewSM->GetStyleString(NULL) ); + + } + + /***** iconstyle / symbol *****/ + + if ( poKmlStyle->has_iconstyle ( ) ) { + + poOgrNewSM->InitStyleString ( NULL ); + + IconStylePtr poKmlIconStyle = poKmlStyle->get_iconstyle ( ); + + poOgrTmpST = NULL; + for ( i = 0; i < poOgrSM->GetPartCount ( NULL ); i++ ) { + poOgrST = poOgrSM->GetPart ( i, NULL ); + + if ( !poOgrST ) + continue; + + if ( poOgrST->GetType ( ) == OGRSTCSymbol ) { + poOgrTmpST = poOgrST; + } + else { + poOgrNewSM->AddPart ( poOgrST ); + delete poOgrST; + } + } + + OGRStyleSymbol *poOgrStyleSymbol = kml2symbol ( poKmlIconStyle, + ( OGRStyleSymbol *) poOgrTmpST ); + + poOgrNewSM->AddPart ( poOgrStyleSymbol ); + + delete poOgrStyleSymbol; + poOgrSM->InitStyleString ( poOgrNewSM->GetStyleString(NULL) ); + + } + + /***** labelstyle / label *****/ + + if ( poKmlStyle->has_labelstyle ( ) ) { + + poOgrNewSM->InitStyleString ( NULL ); + + LabelStylePtr poKmlLabelStyle = poKmlStyle->get_labelstyle ( ); + + poOgrTmpST = NULL; + for ( i = 0; i < poOgrSM->GetPartCount ( NULL ); i++ ) { + poOgrST = poOgrSM->GetPart ( i, NULL ); + + if ( !poOgrST ) + continue; + + if ( poOgrST->GetType ( ) == OGRSTCLabel ) { + poOgrTmpST = poOgrST; + } + else { + poOgrNewSM->AddPart ( poOgrST ); + delete poOgrST; + } + } + + OGRStyleLabel *poOgrStyleLabel = kml2label ( poKmlLabelStyle, + ( OGRStyleLabel *) poOgrTmpST ); + + poOgrNewSM->AddPart ( poOgrStyleLabel ); + + delete poOgrStyleLabel; + poOgrSM->InitStyleString ( poOgrNewSM->GetStyleString(NULL) ); + + } + + delete poOgrNewSM; + +} + + + +/****************************************************************************** + kml2pen +******************************************************************************/ + +OGRStylePen *kml2pen ( + LineStylePtr poKmlLineStyle, + OGRStylePen *poOgrStylePen) +{ + + if (!poOgrStylePen) + poOgrStylePen = new OGRStylePen ( ); + + /***** should always have a width in pixels *****/ + + poOgrStylePen->SetUnit(OGRSTUPixel); + + /***** width *****/ + + if ( poKmlLineStyle->has_width ( ) ) + poOgrStylePen->SetWidth ( poKmlLineStyle->get_width ( ) ); + + /***** color *****/ + + if ( poKmlLineStyle->has_color ( ) ) { + Color32 poKmlColor = poKmlLineStyle->get_color ( ); + char szColor[10] = { }; + snprintf ( szColor, sizeof ( szColor ), "#%02X%02X%02X%02X", + poKmlColor.get_red ( ), + poKmlColor.get_green ( ), + poKmlColor.get_blue ( ), poKmlColor.get_alpha ( ) ); + poOgrStylePen->SetColor ( szColor ); + } + + return poOgrStylePen; +} + +/****************************************************************************** + kml2brush +******************************************************************************/ + +OGRStyleBrush *kml2brush ( + PolyStylePtr poKmlPolyStyle, + OGRStyleBrush *poOgrStyleBrush) +{ + + if (!poOgrStyleBrush) + poOgrStyleBrush = new OGRStyleBrush ( ); + + /***** color *****/ + + if ( poKmlPolyStyle->has_color ( ) ) { + Color32 poKmlColor = poKmlPolyStyle->get_color ( ); + char szColor[10] = { }; + snprintf ( szColor, sizeof ( szColor ), "#%02X%02X%02X%02X", + poKmlColor.get_red ( ), + poKmlColor.get_green ( ), + poKmlColor.get_blue ( ), poKmlColor.get_alpha ( ) ); + poOgrStyleBrush->SetForeColor ( szColor ); + } + + return poOgrStyleBrush; +} + +/****************************************************************************** + kml2symbol +******************************************************************************/ + +OGRStyleSymbol *kml2symbol ( + IconStylePtr poKmlIconStyle, + OGRStyleSymbol *poOgrStyleSymbol) +{ + + if (!poOgrStyleSymbol) + poOgrStyleSymbol = new OGRStyleSymbol ( ); + + /***** id (kml icon) *****/ + + if ( poKmlIconStyle->has_icon ( ) ) { + IconStyleIconPtr poKmlIcon = poKmlIconStyle->get_icon ( ); + + if ( poKmlIcon->has_href ( ) ) { + std::string oIcon = "\""; + oIcon.append ( poKmlIcon->get_href ( ).c_str ( ) ); + oIcon.append ( "\"" ); + poOgrStyleSymbol->SetId ( oIcon.c_str ( ) ); + + } + } + + /***** heading *****/ + + if ( poKmlIconStyle->has_heading ( ) ) + poOgrStyleSymbol->SetAngle ( poKmlIconStyle->get_heading ( ) ); + + /***** scale *****/ + + if ( poKmlIconStyle->has_scale ( ) ) + poOgrStyleSymbol->SetSize ( poKmlIconStyle->get_scale ( ) ); + + /***** color *****/ + + if ( poKmlIconStyle->has_color ( ) ) { + Color32 poKmlColor = poKmlIconStyle->get_color ( ); + char szColor[10] = { }; + snprintf ( szColor, sizeof ( szColor ), "#%02X%02X%02X%02X", + poKmlColor.get_red ( ), + poKmlColor.get_green ( ), + poKmlColor.get_blue ( ), poKmlColor.get_alpha ( ) ); + poOgrStyleSymbol->SetColor ( szColor ); + } + + /***** hotspot *****/ + + if ( poKmlIconStyle->has_hotspot ( ) ) { + HotSpotPtr poKmlHotSpot = poKmlIconStyle->get_hotspot ( ); + + if ( poKmlHotSpot->has_x ( ) ) + poOgrStyleSymbol->SetSpacingX ( poKmlHotSpot->get_x ( ) ); + if ( poKmlHotSpot->has_y ( ) ) + poOgrStyleSymbol->SetSpacingY ( poKmlHotSpot->get_y ( ) ); + + } + + return poOgrStyleSymbol; +} + +/****************************************************************************** + kml2label +******************************************************************************/ + +OGRStyleLabel *kml2label ( + LabelStylePtr poKmlLabelStyle, + OGRStyleLabel *poOgrStyleLabel) +{ + + if (!poOgrStyleLabel) + poOgrStyleLabel = new OGRStyleLabel ( ); + + /***** color *****/ + + if ( poKmlLabelStyle->has_color ( ) ) { + Color32 poKmlColor = poKmlLabelStyle->get_color ( ); + char szColor[10] = { }; + snprintf ( szColor, sizeof ( szColor ), "#%02X%02X%02X%02X", + poKmlColor.get_red ( ), + poKmlColor.get_green ( ), + poKmlColor.get_blue ( ), poKmlColor.get_alpha ( ) ); + poOgrStyleLabel->SetForColor ( szColor ); + } + + if ( poKmlLabelStyle->has_scale ( ) ) { + double dfScale = poKmlLabelStyle->get_scale ( ); + dfScale *= 100.0; + + poOgrStyleLabel->SetStretch(dfScale); + } + + return poOgrStyleLabel; +} + +/****************************************************************************** + function to add a kml style to a style table +******************************************************************************/ + +void kml2styletable ( + OGRStyleTable * poOgrStyleTable, + StylePtr poKmlStyle ) +{ + + + /***** no reason to add it if it don't have an id *****/ + + if ( poKmlStyle->has_id ( ) ) { + + OGRStyleMgr *poOgrSM = new OGRStyleMgr ( poOgrStyleTable ); + + poOgrSM->InitStyleString ( NULL ); + + /***** read the style *****/ + + kml2stylestring ( poKmlStyle, poOgrSM ); + + /***** add the style to the style table *****/ + + const std::string oName = poKmlStyle->get_id ( ); + + + poOgrSM->AddStyle ( CPLString ( ).Printf ( "%s", + oName.c_str ( ) ), NULL ); + + /***** cleanup the style manager *****/ + + delete poOgrSM; + } + + else { + CPLError ( CE_Failure, CPLE_AppDefined, + "ERROR Parseing kml Style: No id" ); + } + + return; +} + +/****************************************************************************** + function to follow the kml stylemap if one exists +******************************************************************************/ + +StyleSelectorPtr StyleFromStyleSelector( + const StyleSelectorPtr& poKmlStyleSelector, + OGRStyleTable * poStyleTable) +{ + + /***** is it a style? *****/ + + if ( poKmlStyleSelector->IsA( kmldom::Type_Style) ) + return poKmlStyleSelector; + + /***** is it a style map? *****/ + + else if ( poKmlStyleSelector->IsA( kmldom::Type_StyleMap) ) + return StyleFromStyleMap(kmldom::AsStyleMap(poKmlStyleSelector), poStyleTable); + + /***** not a style or a style map *****/ + + return NULL; +} + + +/****************************************************************************** + function to get the container from the kmlroot + + Args: poKmlRoot the root element + + Returns: root if its a container, if its a kml the container it + contains, or NULL + +******************************************************************************/ + +static ContainerPtr MyGetContainerFromRoot ( + KmlFactory *m_poKmlFactory, ElementPtr poKmlRoot ) +{ + ContainerPtr poKmlContainer = NULL; + + if ( poKmlRoot ) { + + /***** skip over the we want the container *****/ + + if ( poKmlRoot->IsA ( kmldom::Type_kml ) ) { + + KmlPtr poKmlKml = AsKml ( poKmlRoot ); + + if ( poKmlKml->has_feature ( ) ) { + FeaturePtr poKmlFeat = poKmlKml->get_feature ( ); + + if ( poKmlFeat->IsA ( kmldom::Type_Container ) ) + poKmlContainer = AsContainer ( poKmlFeat ); + else if ( poKmlFeat->IsA ( kmldom::Type_Placemark ) ) + { + poKmlContainer = m_poKmlFactory->CreateDocument ( ); + poKmlContainer->add_feature ( kmldom::AsFeature(kmlengine::Clone(poKmlFeat)) ); + } + } + } + + else if ( poKmlRoot->IsA ( kmldom::Type_Container ) ) + poKmlContainer = AsContainer ( poKmlRoot ); + } + + return poKmlContainer; +} + + + +StyleSelectorPtr StyleFromStyleURL( + const StyleMapPtr& stylemap, + const string styleurl, + OGRStyleTable * poStyleTable) +{ + // TODO:: Parse the styleURL + + char *pszUrl = CPLStrdup ( styleurl.c_str ( ) ); + char *pszStyleMapId = CPLStrdup ( stylemap->get_id().c_str ( ) ); + + + /***** is it an interenal style ref that starts with a # *****/ + + if ( *pszUrl == '#' && poStyleTable ) { + + /***** searh the style table for the style we *****/ + /***** want and copy it back into the table *****/ + + const char *pszTest = NULL; + pszTest = poStyleTable->Find ( pszUrl + 1 ); + if ( pszTest ) { + poStyleTable->AddStyle(pszStyleMapId, pszTest); + } + } + + /***** We have a real URL and need to go out and fetch it *****/ + /***** FIXME this could be a relative path in a kmz *****/ + + else if ( strchr(pszUrl, '#') ) { + + const char *pszFetch = CPLGetConfigOption ( "LIBKML_EXTERNAL_STYLE", "no" ); + if ( CSLTestBoolean(pszFetch) ) { + + /***** Lets go out and fetch the style from the external URL *****/ + + char *pszUrlTmp = CPLStrdup(pszUrl); + char *pszPound; + char *pszRemoteStyleName = NULL; + // Chop off the stuff (style id) after the URL + if ((pszPound = strchr(pszUrlTmp, '#'))) { + *pszPound = '\0'; + pszRemoteStyleName = pszPound + 1; + } + + /***** try it as a url then a file *****/ + + VSILFILE *fp = NULL; + if ( (fp = VSIFOpenL( CPLFormFilename( "/vsicurl/", + pszUrlTmp, + NULL), "r" )) + || (fp = VSIFOpenL( pszUrlTmp, "r" )) ) + { + char szbuf[1025]; + std::string oStyle = ""; + + /***** loop, read and copy to a string *****/ + + size_t nRead; + do { + nRead = VSIFReadL(szbuf, 1, sizeof(szbuf) - 1, fp); + if (nRead == 0) + break; + + /***** copy buf to the string *****/ + + szbuf[nRead] = '\0'; + oStyle.append( szbuf ); + } while (!VSIFEofL(fp)); + + VSIFCloseL(fp); + + /***** parse the kml into the dom *****/ + + std::string oKmlErrors; + ElementPtr poKmlRoot = kmldom::Parse ( oStyle, &oKmlErrors ); + + if ( !poKmlRoot ) { + CPLError ( CE_Failure, CPLE_OpenFailed, + "ERROR Parseing style kml %s :%s", + pszUrlTmp, oKmlErrors.c_str ( ) ); + CPLFree(pszUrlTmp); + CPLFree ( pszUrl ); + CPLFree ( pszStyleMapId ); + + return NULL; + } + + /***** get the root container *****/ + + ContainerPtr poKmlContainer; + kmldom::KmlFactory* poKmlFactory = kmldom::KmlFactory::GetFactory(); + if ( !( poKmlContainer = MyGetContainerFromRoot ( poKmlFactory, poKmlRoot ) ) ) { + CPLFree(pszUrlTmp); + CPLFree ( pszUrl ); + CPLFree ( pszStyleMapId ); + + return NULL; + } + + /**** parse the styles into the table *****/ + + ParseStyles ( AsDocument ( poKmlContainer ), &poStyleTable ); + + /***** look for the style we leed to map to in the table *****/ + + const char *pszTest = NULL; + pszTest = poStyleTable->Find(pszRemoteStyleName); + + /***** if found copy it to the table as a new style *****/ + if ( pszTest ) + poStyleTable->AddStyle(pszStyleMapId, pszTest); + + } + CPLFree(pszUrlTmp); + } + } + + /***** FIXME add suport here for relative links inside kml *****/ + + CPLFree ( pszUrl ); + CPLFree ( pszStyleMapId ); + + return NULL; +} + +StyleSelectorPtr StyleFromStyleMap( + const StyleMapPtr& poKmlStyleMap, + OGRStyleTable * poStyleTable) +{ + + /***** check the config option to see if the *****/ + /***** user wants normal or highlighted mapping *****/ + + const char *pszStyleMapKey = CPLGetConfigOption ( "LIBKML_STYLEMAP_KEY", "normal" ); + int nStyleMapKey = STYLESTATE_NORMAL; + if ( EQUAL (pszStyleMapKey, "highlight")) + nStyleMapKey = STYLESTATE_HIGHLIGHT; + + /***** Loop through the stylemap pairs and look for the "normal" one *****/ + + for (size_t i = 0; i < poKmlStyleMap->get_pair_array_size(); ++i) { + PairPtr myPair = poKmlStyleMap->get_pair_array_at(i); + + /***** is it the right one of the pair? *****/ + + if ( myPair->get_key() == nStyleMapKey ) { + + if (myPair->has_styleselector()) + return StyleFromStyleSelector(myPair->get_styleselector(), poStyleTable); + + else if (myPair->has_styleurl()) + return StyleFromStyleURL(poKmlStyleMap, myPair->get_styleurl(), poStyleTable); + } + } + + return NULL; +} + +/****************************************************************************** + function to parse a style table out of a document +******************************************************************************/ + +void ParseStyles ( + DocumentPtr poKmlDocument, + OGRStyleTable ** poStyleTable ) +{ + + /***** if document is null just bail now *****/ + + if ( !poKmlDocument ) + return; + + /***** loop over the Styles *****/ + + size_t nKmlStyles = poKmlDocument->get_styleselector_array_size ( ); + size_t iKmlStyle; + + /***** Lets first build the style table. *****/ + /***** to begin this is just proper styles. *****/ + + for ( iKmlStyle = 0; iKmlStyle < nKmlStyles; iKmlStyle++ ) { + StyleSelectorPtr poKmlStyle = + poKmlDocument->get_styleselector_array_at ( iKmlStyle ); + + /***** Everything that is not a style you skip *****/ + + if ( !poKmlStyle->IsA ( kmldom::Type_Style ) ) + continue; + + /***** We need to check to see if this is the first style. if it *****/ + /***** is we will not have a style table and need to create one *****/ + + if ( !*poStyleTable ) + *poStyleTable = new OGRStyleTable ( ); + + /***** TODO:: Not sure we need to do this as we seem *****/ + /***** to cast to element and then back to style. *****/ + + ElementPtr poKmlElement = AsElement ( poKmlStyle ); + kml2styletable ( *poStyleTable, AsStyle ( poKmlElement ) ); + } + + /***** Now we have to loop back around and get the style maps. We *****/ + /***** have to do this a second time since the stylemap might matter *****/ + /***** and we are just looping reference styles that are farther *****/ + /***** down in the file. Order through the XML as it is parsed. *****/ + + for ( iKmlStyle = 0; iKmlStyle < nKmlStyles; iKmlStyle++ ) { + StyleSelectorPtr poKmlStyle = + poKmlDocument->get_styleselector_array_at ( iKmlStyle ); + + /***** Everything that is not a stylemap you skip *****/ + + if ( !poKmlStyle->IsA ( kmldom::Type_StyleMap ) ) + continue; + + /***** We need to check to see if this is the first style. if it *****/ + /***** is we will not have a style table and need to create one *****/ + + if ( !*poStyleTable ) + *poStyleTable = new OGRStyleTable ( ); + + /***** copy the style the style map points to since *****/ + + char *pszStyleMapId = CPLStrdup ( poKmlStyle->get_id().c_str ( ) ); + poKmlStyle = StyleFromStyleMap(kmldom::AsStyleMap(poKmlStyle), *poStyleTable); + if (poKmlStyle == NULL) { + CPLFree(pszStyleMapId); + continue; + } + char *pszStyleId = CPLStrdup ( poKmlStyle->get_id().c_str ( ) ); + + /***** TODO:: Not sure we need to do this as we seem *****/ + /***** to cast to element and then back to style. *****/ + + ElementPtr poKmlElement = AsElement ( poKmlStyle ); + kml2styletable ( *poStyleTable, AsStyle ( poKmlElement ) ); + + // Change the name of the new style in the style table + + const char *pszTest = NULL; + pszTest = (*poStyleTable)->Find(pszStyleId); + // If we found the style we want in the style table we... + if ( pszTest ) { + (*poStyleTable)->AddStyle(pszStyleMapId, pszTest); + (*poStyleTable)->RemoveStyle ( pszStyleId ); + } + CPLFree ( pszStyleId ); + CPLFree ( pszStyleMapId ); + } + + return; +} + +/****************************************************************************** + function to add a style table to a kml container +******************************************************************************/ + +void styletable2kml ( + OGRStyleTable * poOgrStyleTable, + KmlFactory * poKmlFactory, + ContainerPtr poKmlContainer, + char** papszOptions ) +{ + + /***** just return if the styletable is null *****/ + + if ( !poOgrStyleTable ) + return; + + std::set aoSetNormalStyles; + std::set aoSetHighlightStyles; + poOgrStyleTable->ResetStyleStringReading ( ); + const char *pszStyleString; + + /* Collect styles that end with _normal or _highlight */ + while ( poOgrStyleTable->GetNextStyle ( ) != NULL ) { + const char *pszStyleName = poOgrStyleTable->GetLastStyleName ( ); + + if( strlen(pszStyleName) > strlen("_normal") && + EQUAL(pszStyleName + strlen(pszStyleName) - strlen("_normal"), "_normal") ) + { + CPLString osName(pszStyleName); + osName.resize(strlen(pszStyleName) - strlen("_normal")); + aoSetNormalStyles.insert(osName); + } + else if( strlen(pszStyleName) > strlen("_highlight") && + EQUAL(pszStyleName + strlen(pszStyleName) - strlen("_highlight"), "_highlight") ) + { + CPLString osName(pszStyleName); + osName.resize(strlen(pszStyleName) - strlen("_highlight")); + aoSetHighlightStyles.insert(osName); + } + } + + /***** parse the style table *****/ + + poOgrStyleTable->ResetStyleStringReading ( ); + + while ( ( pszStyleString = poOgrStyleTable->GetNextStyle ( ) ) ) { + const char *pszStyleName = poOgrStyleTable->GetLastStyleName ( ); + + if( aoSetNormalStyles.find(pszStyleName) != aoSetNormalStyles.end() && + aoSetHighlightStyles.find(pszStyleName) != aoSetHighlightStyles.end() ) + { + continue; + } + + /***** add the style header to the kml *****/ + + StylePtr poKmlStyle = poKmlFactory->CreateStyle ( ); + + poKmlStyle->set_id ( pszStyleName ); + + /***** parse the style string *****/ + + addstylestring2kml ( pszStyleString, poKmlStyle, poKmlFactory, NULL ); + + /***** add balloon style *****/ + const char* pszBalloonStyleBgColor = CSLFetchNameValue(papszOptions, CPLSPrintf("%s_balloonstyle_bgcolor", pszStyleName)); + const char* pszBalloonStyleText = CSLFetchNameValue(papszOptions, CPLSPrintf("%s_balloonstyle_text", pszStyleName)); + int nR, nG, nB, nA; + OGRStylePen oStyleTool; + if( (pszBalloonStyleBgColor != NULL && + oStyleTool.GetRGBFromString ( pszBalloonStyleBgColor, nR, nG, nB, nA ) ) || + pszBalloonStyleText != NULL ) + { + BalloonStylePtr poKmlBalloonStyle = poKmlFactory->CreateBalloonStyle(); + if( pszBalloonStyleBgColor != NULL && + oStyleTool.GetRGBFromString ( pszBalloonStyleBgColor, nR, nG, nB, nA ) ) + poKmlBalloonStyle->set_bgcolor ( Color32 ( nA, nB, nG, nR ) ); + if( pszBalloonStyleText != NULL ) + poKmlBalloonStyle->set_text(pszBalloonStyleText); + poKmlStyle->set_balloonstyle ( poKmlBalloonStyle ); + } + + /***** add the style to the container *****/ + + DocumentPtr poKmlDocument = AsDocument ( poKmlContainer ); + poKmlDocument->add_styleselector ( poKmlStyle ); + + } + + /* Find style name that end with _normal and _highlight to create */ + /* a StyleMap from both */ + std::set::iterator aoSetNormalStylesIter = + aoSetNormalStyles.begin(); + for( ; aoSetNormalStylesIter != aoSetNormalStyles.end(); ++aoSetNormalStylesIter ) + { + CPLString osStyleName(*aoSetNormalStylesIter); + if( aoSetHighlightStyles.find(osStyleName) != + aoSetHighlightStyles.end() ) + { + StyleMapPtr poKmlStyleMap = poKmlFactory->CreateStyleMap ( ); + poKmlStyleMap->set_id ( osStyleName ); + + PairPtr poKmlPairNormal = poKmlFactory->CreatePair ( ); + poKmlPairNormal->set_key(STYLESTATE_NORMAL); + poKmlPairNormal->set_styleurl(CPLSPrintf("#%s_normal", osStyleName.c_str())); + poKmlStyleMap->add_pair(poKmlPairNormal); + + PairPtr poKmlPairHightlight = poKmlFactory->CreatePair ( ); + poKmlPairHightlight->set_key(STYLESTATE_HIGHLIGHT); + poKmlPairHightlight->set_styleurl(CPLSPrintf("#%s_highlight", osStyleName.c_str())); + poKmlStyleMap->add_pair(poKmlPairHightlight); + + /***** add the style to the container *****/ + DocumentPtr poKmlDocument = AsDocument ( poKmlContainer ); + poKmlDocument->add_styleselector ( poKmlStyleMap ); + } + } + + return; +} + + +/****************************************************************************** + function to add a ListStyle and select it to a container +******************************************************************************/ + +void createkmlliststyle ( + KmlFactory * poKmlFactory, + const char* pszBaseName, + ContainerPtr poKmlLayerContainer, + DocumentPtr poKmlDocument, + const CPLString& osListStyleType, + const CPLString& osListStyleIconHref) +{ + if( osListStyleType.size() || osListStyleIconHref.size() ) + { + StylePtr poKmlStyle = poKmlFactory->CreateStyle ( ); + + const char* pszStyleName = CPLSPrintf("%s_liststyle", OGRLIBKMLGetSanitizedNCName(pszBaseName).c_str()); + poKmlStyle->set_id ( pszStyleName ); + + ListStylePtr poKmlListStyle = poKmlFactory->CreateListStyle ( ); + poKmlStyle->set_liststyle ( poKmlListStyle ); + if( osListStyleType.size() ) + { + if( EQUAL(osListStyleType, "check") ) + poKmlListStyle->set_listitemtype( kmldom::LISTITEMTYPE_CHECK ); + else if( EQUAL(osListStyleType, "radioFolder") ) + poKmlListStyle->set_listitemtype( kmldom::LISTITEMTYPE_RADIOFOLDER ); + else if( EQUAL(osListStyleType, "checkOffOnly") ) + poKmlListStyle->set_listitemtype( kmldom::LISTITEMTYPE_CHECKOFFONLY ); + else if( EQUAL(osListStyleType, "checkHideChildren") ) + poKmlListStyle->set_listitemtype( kmldom::LISTITEMTYPE_CHECKHIDECHILDREN ); + else + { + CPLError(CE_Warning, CPLE_AppDefined, + "Invalid value for list style type: %s. Defaulting to Check", + osListStyleType.c_str()); + poKmlListStyle->set_listitemtype( kmldom::LISTITEMTYPE_CHECK ); + } + } + + if( osListStyleIconHref.size() ) + { + ItemIconPtr poItemIcon = poKmlFactory->CreateItemIcon ( ); + poItemIcon->set_href( osListStyleIconHref.c_str() ); + poKmlListStyle->add_itemicon(poItemIcon); + } + + poKmlDocument->add_styleselector ( poKmlStyle ); + poKmlLayerContainer->set_styleurl( CPLSPrintf("#%s", pszStyleName) ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlstyle.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlstyle.h new file mode 100644 index 000000000..01dca6609 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/libkml/ogrlibkmlstyle.h @@ -0,0 +1,99 @@ +/****************************************************************************** + * + * Project: KML Translator + * Purpose: Implements OGRLIBKMLDriver + * Author: Brian Case, rush at winkey dot org + * + ****************************************************************************** + * Copyright (c) 2010, Brian Case + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +using kmldom::KmlFactory; +using kmldom::StyleSelectorPtr; +using kmldom::StylePtr; +using kmldom::StyleMapPtr; +using kmldom::DocumentPtr; +using kmldom::ContainerPtr; +using kmldom::FeaturePtr; + +StylePtr addstylestring2kml ( + const char *stylestring, + StylePtr poKmlStyle, + KmlFactory * poKmlFactory, + FeaturePtr poKmlFeature ); + + + +/****************************************************************************** + kml2stylemgr +******************************************************************************/ + +void kml2stylestring( + StylePtr poKmlStyle, + OGRStyleMgr *poOgrSM); + + + +/****************************************************************************** + functions to follow the kml stylemap if one exists +******************************************************************************/ + +StyleSelectorPtr StyleFromStyleSelector( + const StyleSelectorPtr& styleselector, + OGRStyleTable * poStyleTable); + +StyleSelectorPtr StyleFromStyleURL( + const string styleurl, + OGRStyleTable * poStyleTable); + +StyleSelectorPtr StyleFromStyleMap( + const StyleMapPtr& stylemap, + OGRStyleTable * poStyleTable); + +/****************************************************************************** + function to parse a style table out of a document +******************************************************************************/ + +void ParseStyles ( + DocumentPtr poKmlDocument, + OGRStyleTable **poStyleTable); + +/****************************************************************************** + function to add a style table to a kml container +******************************************************************************/ + +void styletable2kml ( + OGRStyleTable * poOgrStyleTable, + KmlFactory * poKmlFactory, + ContainerPtr poKmlContainer, + char** papszOptions = NULL ); + +/****************************************************************************** + function to add a ListStyle and select it to a container +******************************************************************************/ + +void createkmlliststyle ( + KmlFactory * poKmlFactory, + const char* pszBaseName, + ContainerPtr poKmlLayerContainer, + DocumentPtr poKmlDocument, + const CPLString& osListStyleType, + const CPLString& osListStyleIconHref); diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/makefile.vc new file mode 100644 index 000000000..9f9025e42 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/makefile.vc @@ -0,0 +1,293 @@ +GDAL_ROOT = ..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF INCLUDE_OGR_FRMTS + +DIRLIST = generic geojson shape ntf sdts tiger s57 dgn mitab gml \ + avc rec mem vrt csv gmt bna kml gpx \ + geoconcept xplane georss gtm dxf pgdump gpsbabel \ + sua openair pds htf aeronavfaa edigeo svg idrisi arcgen \ + segukooa segy sxf openfilegdb wasp selafin jml \ + $(ARCOBJECTS_DIR) \ + $(OGDIDIR) $(FMEDIR) $(OCIDIR) $(PG_DIR) $(DWGDIR) \ + $(ODBCDIR) $(SQLITE_DIR) $(MYSQL_DIR) $(ILI_DIR) \ + $(SDE_DIR) $(IDB_DIR) $(NAS_DIR) $(DODSDIR) \ + $(LIBKMLDIR) $(WFSDIR) $(SOSIDIR) $(GFTDIR) \ + $(COUCHDBDIR) $(CLOUDANTDIR) $(FGDB_DIR) $(XLSDIR) $(ODSDIR) $(XLSXDIR) \ + $(INGRESDIR) $(ELASTICDIR) $(OSMDIR) $(VFKDIR) $(CARTODBDIR) \ + $(GMEDIR) $(PLSCENESDIR) $(CSWDIR) + +PLUGINDIRLIST = $(PLUGIN_ARCOBJECTS_DIR) \ + $(PLUGIN_DWG_DIR) \ + $(PLUGIN_PG_DIR) \ + $(PLUGIN_SOSI_DIR) \ + $(PLUGIN_FGDB_DIR) \ + $(PLUGIN_OCIDIR) \ + $(PLUGIN_SDE_DIR) \ + $(PLUGIN_INGRESDIR) \ + $(PLUGIN_LIBKMLDIR) + +!IFDEF OGDIDIR +OGDIDIR = ogdi +OGDIOBJ = ogdi\*.obj +!ENDIF + +!IFDEF ODBC_SUPPORTED +ODBCDIR = odbc pgeo mssqlspatial geomedia walk +ODBCOBJ = odbc\*.obj pgeo\*.obj mssqlspatial\*.obj geomedia\*.obj walk\*.obj +!ENDIF + +!IFDEF SQLITE_LIB +SQLITE_DIR = sqlite gpkg +SQLITE_OBJ = sqlite\*.obj gpkg\*.obj +!ENDIF + +!IFDEF OCI_LIB +!IF "$(OCI_PLUGIN)" != "YES" +OCIDIR = oci +OCIOBJ = oci\*.obj +!ELSE +PLUGIN_OCIDIR = oci +!ENDIF +!ENDIF + +!IFDEF INGRES_HOME +!IF "$(INGRES_PLUGIN)" != "YES" +INGRESDIR = ingres +INGRESOBJ = ingres\*.obj +!ELSE +PLUGIN_INGRESDIR = ingres +!ENDIF +!ENDIF + +!IFDEF FME_DIR +FMEDIR = fme +FMEOBJ = fme\*.obj +!ENDIF + +!IFDEF PG_INC_DIR +!IF "$(PG_PLUGIN)" != "YES" +PG_DIR = pg +PG_OBJ = pg\*.obj +!ELSE +PLUGIN_PG_DIR = pg +!ENDIF +!ENDIF + +!IFDEF DWGDIRECT +!IF "$(DWG_PLUGIN)" != "YES" +DWGDIR = dxfdwg +DWG_OBJ = dxfdwg\*.obj +!ELSE +PLUGIN_DWGDIR = dxfdwg +!ENDIF +!ENDIF + +!IFDEF SDE_LIB +!IF "$(SDE_PLUGIN)" != "YES" +SDE_DIR = sde +SDE_OBJ = sde\*.obj +!ELSE +PLUGIN_SDE_DIR = sde +!ENDIF +!ENDIF + +!IFDEF FGDB_LIB +!IF "$(FGDB_PLUGIN)" != "YES" +FGDB_DIR = filegdb +FGDB_OBJ = filegdb\*.obj +!ELSE +PLUGIN_FGDB_DIR = filegdb +!ENDIF +!ENDIF + + +!IFDEF HAS_ARCOBJECTS +!IF "$(ARCOBJECTS_PLUGIN)" != "YES" +ARCOBJECTS_DIR = arcobjects +ARCOBJECTS_OBJ = arcobjects\*.obj +!ELSE +PLUGIN_ARCOBJECTS_DIR = arcobjects +!ENDIF +!ENDIF + + +!IFDEF MYSQL_INC_DIR +MYSQL_DIR = mysql +MYSQL_OBJ = mysql\*.obj +!ENDIF + +!IFDEF ILI_ENABLED +ILI_DIR = ili +ILI_OBJ = ili\*.obj ili\iom\*.obj +!ENDIF + +!IFDEF NAS_ENABLED +NAS_DIR = nas +NAS_OBJ = nas\*.obj +!ENDIF + +!IFDEF INFORMIXDIR +IDB_DIR = idb +IDB_OBJ = idb\*.obj +!ENDIF + +!IFDEF DODS_DIR +DODSDIR = dods +DODS_OBJ = dods\*.obj +!ENDIF + +!IFDEF LIBKML_DIR +!IF "$(LIBKML_PLUGIN)" != "YES" +LIBKMLDIR = libkml +LIBKMLOBJ = libkml\*.obj +!ELSE +PLUGIN_LIBKMLDIR = libkml +!ENDIF +!ENDIF + +!IFDEF CURL_LIB +WFSDIR = wfs +WFS_OBJ = wfs\*.obj +CSWDIR = csw +CSW_OBJ = csw\*.obj +!ENDIF + +!IFDEF SOSI_INC_DIR +!IF "$(SOSI_PLUGIN)" != "YES" +SOSIDIR = sosi +SOSI_OBJ = sosi\*.obj +!ELSE +PLUGIN_SOSI_DIR = sosi +!ENDIF +!ENDIF + +!IFDEF CURL_LIB +GFTDIR = gft +GFT_OBJ = gft\*.obj +!ENDIF + +!IFDEF CURL_LIB +COUCHDBDIR = couchdb +COUCHDB_OBJ = couchdb\*.obj +CLOUDANTDIR = cloudant +CLOUDANT_OBJ = cloudant\*.obj +!ENDIF + +!IFDEF FREEXL_LIBS +XLSDIR = xls +XLS_OBJ = xls\*.obj +!ENDIF + +!IFDEF EXPAT_INCLUDE +ODSDIR = ods +ODS_OBJ = ods\*.obj +!ENDIF + +!IFDEF EXPAT_INCLUDE +XLSXDIR = xlsx +XLSX_OBJ = xlsx\*.obj +!ENDIF + +!IFDEF CURL_LIB +ELASTICDIR = elastic +ELASTIC_OBJ = elastic\*.obj +!ENDIF + +!IFDEF SQLITE_LIB +OSMDIR = osm +OSM_OBJ = osm\*.obj +VFKDIR = vfk +VFK_OBJ = vfk\*.obj +!ENDIF + +!IFDEF CURL_LIB +CARTODBDIR = cartodb +CARTODB_OBJ = cartodb\*.obj +!ENDIF + +!IFDEF CURL_LIB +GMEDIR = gme +GME_OBJ = gme\*.obj +!ENDIF + +!IFDEF CURL_LIB +PLSCENESDIR = plscenes +PLSCENES_OBJ = plscenes\*.obj +!ENDIF + +!ELSE + +DIRLIST = generic mitab + +!ENDIF + +default: + for %d in ( $(DIRLIST) ) do \ + cd %d \ + && $(MAKE) /f makefile.vc \ + && cd .. \ + || exit 1 + + lib /out:ogrsf_frmts.lib generic\*.obj shape\*.obj ntf\*.obj \ + sdts\*.obj s57\*.obj tiger\*.obj gml\*.obj \ + mitab\*.obj dgn\*.obj avc\*.obj mem\*.obj \ + vrt\*.obj csv\*.obj rec\*.obj kml\*.obj \ + gmt\*.obj bna\*.obj geoconcept\*.obj \ + geojson\*.obj geojson\libjson\*.obj \ + gpx\*.obj xplane\*.obj georss\*.obj gtm\*.obj \ + dxf\*.obj pgdump\*.obj gpsbabel\*.obj \ + sua\*.obj openair\*.obj pds\*.obj htf\*.obj \ + aeronavfaa\*.obj edigeo\*.obj svg\*.obj idrisi\*.obj \ + arcgen\*.obj segukooa\*.obj segy\*.obj sxf\*.obj \ + openfilegdb\*.obj wasp\*.obj selafin\*.obj jml\*.obj \ + $(OGDIOBJ) $(ODBCOBJ) $(SQLITE_OBJ) \ + $(FMEOBJ) $(OCIOBJ) $(PG_OBJ) $(MYSQL_OBJ) \ + $(ILI_OBJ) $(DWG_OBJ) $(SDE_OBJ) $(FGDB_OBJ) $(ARCDRIVER_OBJ) $(IDB_OBJ) \ + $(DODS_OBJ) $(NAS_OBJ) $(LIBKMLOBJ) $(WFS_OBJ) \ + $(SOSI_OBJ) $(GFT_OBJ) $(COUCHDB_OBJ) $(CLOUDANT_OBJ) $(XLS_OBJ) $(ODS_OBJ) $(XLSX_OBJ) \ + $(INGRESOBJ) $(ELASTIC_OBJ) $(OSM_OBJ) $(VFK_OBJ) $(CARTODB_OBJ) $(GME_OBJ) $(PLSCENES_OBJ) \ + $(CSW_OBJ) + lib /out:ogrsf_frmts_sup.lib \ + ..\..\frmts\iso8211\*.obj \ + ..\..\frmts\sdts\sdtsattrreader.obj \ + ..\..\frmts\sdts\sdtscatd.obj \ + ..\..\frmts\sdts\sdtsiref.obj \ + ..\..\frmts\sdts\sdtslib.obj \ + ..\..\frmts\sdts\sdtslinereader.obj \ + ..\..\frmts\sdts\sdtspointreader.obj \ + ..\..\frmts\sdts\sdtspolygonreader.obj \ + ..\..\frmts\sdts\sdtsrasterreader.obj \ + ..\..\frmts\sdts\sdtstransfer.obj \ + ..\..\frmts\sdts\sdtsindexedreader.obj \ + ..\..\frmts\sdts\sdtsxref.obj + +plugindirs: + @echo plugins: $(PLUGINDIRLIST) + -for %d in ( $(PLUGINDIRLIST) ) do \ + cd %d \ + && $(MAKE) /f makefile.vc plugin \ + && cd .. \ + || exit 1 + +clean: + -del ogrsf_frmts.lib + -del ogrsf_frmts_sup.lib + for %d in ( $(DIRLIST) ) do \ + cd %d \ + && $(MAKE) /f makefile.vc clean \ + && cd .. \ + || exit 1 + +plugins-install: + -for %d in ( $(PLUGINDIRLIST) ) do \ + cd %d \ + && $(MAKE) /f makefile.vc plugin-install \ + && cd .. \ + || exit 1 + +html-install: + copy *.html $(HTMLDIR) + -for %d in ( $(DIRLIST) ) do \ + copy %d\drv_*.html $(HTMLDIR) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/GNUmakefile new file mode 100644 index 000000000..cdddb7fa0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/GNUmakefile @@ -0,0 +1,15 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrmdbdatasource.o ogrmdblayer.o ogrmdbdriver.o \ + ogrmdbjackcess.o + +CPPFLAGS := -I.. $(JAVA_INC) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_mdb.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/drv_mdb.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/drv_mdb.html new file mode 100644 index 000000000..0a0317da0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/drv_mdb.html @@ -0,0 +1,69 @@ + + + + + + Access MDB databases + + + +

    Access MDB databases

    + +

    GDAL/OGR >= 1.9.0

    + +

    OGR optionally supports reading access .mdb + files by using the Java Jackcess library.

    + +

    This driver is primarly meant as being used on Unix platforms to overcome the + issues often met with the MDBTools library that acts as the ODBC driver for MDB databases.

    + +

    The driver can detect ESRI Personal Geodatabases and Geomedia MDB databases, and will + deal them exactly as the PGeo and + Geomedia drivers do. For other MDB databases, all the tables + will be presented as OGR layers.

    + +

    How to build the MDB driver (on Linux)

    + + You need a JDK (a JRE is not enough) to build the driver. + + On Ubuntu 10.04 with the openjdk-6-jdk package installed, + +
    ./configure --with-java=yes --with-mdb=yes
    + + On others Linux flavours, you may need to specify : + +
    ./configure --with-java=/path/to/jdk/root/path --with-jvm-lib=/path/to/libjvm/directory --with-mdb=yes
    + + where /path/to/libjvm/directory is for example /usr/lib/jvm/java-6-openjdk/jre/lib/amd64/server

    + + It is possible to add the --with-jvm-lib-add-rpath option (no value or "yes") to embed the path + to the libjvm.so in the GDAL library.

    + +

    How to run the MDB driver (on Linux)

    + + You need a JRE and 3 external JARs to run the driver. + +
      +
    1. If you didn't specify --with-jvm-lib-add-rpath at configure time, set the path of the directory that contains libjvm.so in LD_LIBRARY_PATH or in /etc/ld.so.conf.
    2. +
    3. Download jackcess-1.2.2.jar, commons-lang-2.4.jar and commons-logging-1.1.1.jar (other versions might work)
    4. +
    5. Put the 3 JARs either in the lib/ext directory of the JRE (e.g. /usr/lib/jvm/java-6-openjdk/jre/lib/ext) or in another directory + and explicitly point them with the CLASSPATH environment variable.
    6. +
    + +

    Resources

    + + + +

    See also

    + + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogr_mdb.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogr_mdb.h new file mode 100644 index 000000000..3877171b6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogr_mdb.h @@ -0,0 +1,342 @@ +/****************************************************************************** + * $Id: ogr_mdb.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for MDB driver. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_MDB_H_INCLUDED +#define _OGR_MDB_H_INCLUDED + +#include +#include + +#include "ogrsf_frmts.h" +#include "cpl_error.h" +#include "cpl_string.h" + +/************************************************************************/ +/* OGRMDBJavaEnv */ +/************************************************************************/ + +class OGRMDBJavaEnv +{ + public: + OGRMDBJavaEnv(); + ~OGRMDBJavaEnv(); + + int Init(); + + + JavaVM *jvm; + JNIEnv *env; + int bCalledFromJava; + + int ExceptionOccured(); + + jclass byteArray_class; + + jclass file_class; + jmethodID file_constructor; + jclass database_class; + jmethodID database_open; + jmethodID database_close; + jmethodID database_getTableNames; + jmethodID database_getTable; + + jclass table_class; + jmethodID table_getColumns; + jmethodID table_iterator; + jmethodID table_getRowCount; + + jclass column_class; + jmethodID column_getName; + jmethodID column_getType; + jmethodID column_getLength; + jmethodID column_isVariableLength; + + jclass datatype_class; + jmethodID datatype_getValue; + + jclass list_class; + jmethodID list_iterator; + + jclass set_class; + jmethodID set_iterator; + + jclass map_class; + jmethodID map_get; + + jclass iterator_class; + jmethodID iterator_hasNext; + jmethodID iterator_next; + + jclass object_class; + jmethodID object_toString; + jmethodID object_getClass; + + jclass boolean_class; + jmethodID boolean_booleanValue; + + jclass byte_class; + jmethodID byte_byteValue; + + jclass short_class; + jmethodID short_shortValue; + + jclass integer_class; + jmethodID integer_intValue; + + jclass float_class; + jmethodID float_floatValue; + + jclass double_class; + jmethodID double_doubleValue; +}; + +/************************************************************************/ +/* OGRMDBDatabase */ +/************************************************************************/ + +class OGRMDBTable; + +class OGRMDBDatabase +{ + OGRMDBJavaEnv* env; + jobject database; + + OGRMDBDatabase(); +public: + static OGRMDBDatabase* Open(OGRMDBJavaEnv* env, const char* pszName); + ~OGRMDBDatabase(); + + std::vector apoTableNames; + int FetchTableNames(); + OGRMDBTable* GetTable(const char* pszTableName); +}; + +/************************************************************************/ +/* OGRMDBTable */ +/************************************************************************/ + +class OGRMDBTable +{ + OGRMDBJavaEnv* env; + OGRMDBDatabase* poDB; + jobject table; + + jobject table_iterator_obj; + jobject row; + + jobject GetColumnVal(int iCol); + + CPLString osTableName; + + std::vector apoColumnNames; + std::vector apoColumnNameObjects; + std::vector apoColumnTypes; + std::vector apoColumnLengths; + +public: + OGRMDBTable(OGRMDBJavaEnv* env, OGRMDBDatabase* poDB, jobject table, const char* pszTableName); + ~OGRMDBTable(); + + OGRMDBDatabase* GetDB() { return poDB; } + + const char* GetName() { return osTableName.c_str(); } + + int GetColumnCount() { return (int)apoColumnNames.size(); } + int GetColumnIndex(const char* pszColName, int bEmitErrorIfNotFound = FALSE); + const char* GetColumnName(int iIndex) { return apoColumnNames[iIndex].c_str(); } + int GetColumnType(int iIndex) { return apoColumnTypes[iIndex]; } + int GetColumnLength(int iIndex) { return apoColumnLengths[iIndex]; } + + void DumpTable(); + + int FetchColumns(); + + int GetRowCount(); + int GetNextRow(); + void ResetReading(); + + char* GetColumnAsString(int iCol); + int GetColumnAsInt(int iCol); + double GetColumnAsDouble(int iCol); + GByte* GetColumnAsBinary(int iCol, int* pnBytes); + +}; + +typedef enum +{ + MDB_Boolean = 0x01, + MDB_Byte = 0x02, + MDB_Short = 0x03, + MDB_Int = 0x04, + MDB_Money = 0x05, + MDB_Float = 0x06, + MDB_Double = 0x07, + MDB_ShortDateTime = 0x08, + MDB_Binary = 0x09, + MDB_Text = 0x0A, + MDB_OLE = 0x0B, + MDB_Memo = 0x0C, + MDB_Unknown = 0x0D, + MDB_GUID = 0x0F, + MDB_Numeric = 0x10 +} MDBType; + +typedef enum +{ + MDB_GEOM_NONE, + MDB_GEOM_PGEO, + MDB_GEOM_GEOMEDIA +} MDBGeometryType; + +/************************************************************************/ +/* OGRMDBLayer */ +/************************************************************************/ + +class OGRMDBDataSource; + +class OGRMDBLayer : public OGRLayer +{ + protected: + OGRMDBTable* poMDBTable; + + MDBGeometryType eGeometryType; + + OGRFeatureDefn *poFeatureDefn; + + // Layer spatial reference system, and srid. + OGRSpatialReference *poSRS; + int nSRSId; + + int iNextShapeId; + + OGRMDBDataSource *poDS; + + int iGeomColumn; + char *pszGeomColumn; + char *pszFIDColumn; + + int *panFieldOrdinals; + + int bHasExtent; + OGREnvelope sExtent; + + void LookupSRID( int ); + + public: + OGRMDBLayer(OGRMDBDataSource* poDS, OGRMDBTable* poMDBTable); + virtual ~OGRMDBLayer(); + + CPLErr BuildFeatureDefn(); + + CPLErr Initialize( const char *pszTableName, + const char *pszGeomCol, + int nShapeType, + double dfExtentLeft, + double dfExtentRight, + double dfExtentBottom, + double dfExtentTop, + int nSRID, + int bHasZ ); + + CPLErr Initialize( const char *pszTableName, + const char *pszGeomCol, + OGRSpatialReference* poSRS ); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int bForce ); + virtual OGRFeature *GetNextRawFeature(); + virtual OGRFeature *GetNextFeature(); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); + + virtual const char *GetFIDColumn(); + + virtual OGRErr GetExtent( OGREnvelope *psExtent, int bForce ); +}; + +/************************************************************************/ +/* OGRMDBDataSource */ +/************************************************************************/ + +class OGRMDBDataSource : public OGRDataSource +{ + OGRMDBLayer **papoLayers; + int nLayers; + + OGRMDBLayer **papoLayersInvisible; + int nLayersWithInvisible; + + char *pszName; + + OGRMDBJavaEnv env; + + OGRMDBDatabase* poDB; + + int OpenGDB(OGRMDBTable* poGDB_GeomColumns); + int OpenGeomediaWarehouse(OGRMDBTable* poGAliasTable); + OGRSpatialReference* GetGeomediaSRS(const char* pszGCoordSystemTable, + const char* pszGCoordSystemGUID); + + public: + OGRMDBDataSource(); + ~OGRMDBDataSource(); + + int Open( const char * ); + int OpenTable( const char *pszTableName, + const char *pszGeomCol, + int bUpdate ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + OGRLayer *GetLayerByName( const char* pszLayerName ); + + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRMDBDriver */ +/************************************************************************/ + +class OGRMDBDriver : public OGRSFDriver +{ + public: + ~OGRMDBDriver(); + + const char *GetName(); + OGRDataSource *Open( const char *, int ); + + int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_MDB_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogrmdbdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogrmdbdatasource.cpp new file mode 100644 index 000000000..76a589f37 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogrmdbdatasource.cpp @@ -0,0 +1,440 @@ +/****************************************************************************** + * $Id: ogrmdbdatasource.cpp 27794 2014-10-04 10:13:46Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRMDBDataSource class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_mdb.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include +#include "ogrgeomediageometry.h" + +CPL_CVSID("$Id: ogrmdbdatasource.cpp 27794 2014-10-04 10:13:46Z rouault $"); + +/************************************************************************/ +/* OGRMDBDataSource() */ +/************************************************************************/ + +OGRMDBDataSource::OGRMDBDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + papoLayersInvisible = NULL; + nLayers = 0; + nLayersWithInvisible = 0; + poDB = NULL; +} + +/************************************************************************/ +/* ~OGRMDBDataSource() */ +/************************************************************************/ + +OGRMDBDataSource::~OGRMDBDataSource() + +{ + int i; + + CPLFree( pszName ); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + for( i = 0; i < nLayersWithInvisible; i++ ) + delete papoLayersInvisible[i]; + CPLFree( papoLayersInvisible ); + + + delete poDB; +} + + +/************************************************************************/ +/* OpenGDB() */ +/************************************************************************/ + +int OGRMDBDataSource::OpenGDB(OGRMDBTable* poGDB_GeomColumns) +{ + int iTableName = poGDB_GeomColumns->GetColumnIndex("TableName", TRUE); + int iFieldName = poGDB_GeomColumns->GetColumnIndex("FieldName", TRUE); + int iShapeType = poGDB_GeomColumns->GetColumnIndex("ShapeType", TRUE); + int iExtentLeft = poGDB_GeomColumns->GetColumnIndex("ExtentLeft", TRUE); + int iExtentRight = poGDB_GeomColumns->GetColumnIndex("ExtentRight", TRUE); + int iExtentBottom = poGDB_GeomColumns->GetColumnIndex("ExtentBottom", TRUE); + int iExtentTop = poGDB_GeomColumns->GetColumnIndex("ExtentTop", TRUE); + int iSRID = poGDB_GeomColumns->GetColumnIndex("SRID", TRUE); + int iHasZ = poGDB_GeomColumns->GetColumnIndex("HasZ", TRUE); + + if (iTableName < 0 || iFieldName < 0 || iShapeType < 0 || + iExtentLeft < 0 || iExtentRight < 0 || iExtentBottom < 0 || + iExtentTop < 0 || iSRID < 0 || iHasZ < 0) + return FALSE; + + while(poGDB_GeomColumns->GetNextRow()) + { + OGRMDBLayer *poLayer; + + char* pszTableName = poGDB_GeomColumns->GetColumnAsString(iTableName); + char* pszFieldName = poGDB_GeomColumns->GetColumnAsString(iFieldName); + if (pszTableName == NULL || pszFieldName == NULL) + { + CPLFree(pszTableName); + CPLFree(pszFieldName); + continue; + } + + OGRMDBTable* poTable = poDB->GetTable(pszTableName); + if (poTable == NULL) + { + CPLFree(pszTableName); + CPLFree(pszFieldName); + continue; + } + + poLayer = new OGRMDBLayer( this, poTable ); + + if( poLayer->Initialize( pszTableName, + pszFieldName, + poGDB_GeomColumns->GetColumnAsInt(iShapeType), + poGDB_GeomColumns->GetColumnAsDouble(iExtentLeft), + poGDB_GeomColumns->GetColumnAsDouble(iExtentRight), + poGDB_GeomColumns->GetColumnAsDouble(iExtentBottom), + poGDB_GeomColumns->GetColumnAsDouble(iExtentTop), + poGDB_GeomColumns->GetColumnAsInt(iSRID), + poGDB_GeomColumns->GetColumnAsInt(iHasZ) ) + != CE_None ) + { + delete poLayer; + } + else + { + papoLayers = (OGRMDBLayer**)CPLRealloc(papoLayers, (nLayers+1) * sizeof(OGRMDBLayer*)); + papoLayers[nLayers++] = poLayer; + } + + CPLFree(pszTableName); + CPLFree(pszFieldName); + } + + return TRUE; +} + +/************************************************************************/ +/* OpenGeomediaWarehouse() */ +/************************************************************************/ + +int OGRMDBDataSource::OpenGeomediaWarehouse(OGRMDBTable* poGAliasTable) +{ + int iTableName = poGAliasTable->GetColumnIndex("TableName", TRUE); + int iTableType = poGAliasTable->GetColumnIndex("TableType", TRUE); + + if (iTableName < 0 || iTableType < 0) + return FALSE; + + char* pszFeatureTableName = NULL; + char* pszGeometryProperties = NULL; + char* pszGCoordSystemTable = NULL; + while(poGAliasTable->GetNextRow()) + { + char* pszTableType = poGAliasTable->GetColumnAsString(iTableType); + if (pszTableType == NULL) + continue; + + if (strcmp(pszTableType, "INGRFeatures") == 0) + { + pszFeatureTableName = poGAliasTable->GetColumnAsString(iTableName); + } + else if (strcmp(pszTableType, "INGRGeometryProperties") == 0) + { + pszGeometryProperties = poGAliasTable->GetColumnAsString(iTableName); + } + else if (strcmp(pszTableType, "GCoordSystemTable") == 0) + { + pszGCoordSystemTable = poGAliasTable->GetColumnAsString(iTableName); + } + + CPLFree(pszTableType); + } + + if (pszFeatureTableName == NULL) + { + CPLFree(pszGeometryProperties); + CPLFree(pszGCoordSystemTable); + return FALSE; + } + + OGRMDBTable* poGFeaturesTable = poDB->GetTable(pszFeatureTableName); + CPLFree(pszFeatureTableName); + pszFeatureTableName = NULL; + + OGRMDBTable* poGeometryPropertiesTable; + if (pszGeometryProperties) + poGeometryPropertiesTable = poDB->GetTable(pszGeometryProperties); + else + poGeometryPropertiesTable = NULL; + CPLFree(pszGeometryProperties); + pszGeometryProperties = NULL; + + if (poGFeaturesTable == NULL) + { + delete poGeometryPropertiesTable; + CPLFree(pszGCoordSystemTable); + return FALSE; + } + + int iFeatureName = poGFeaturesTable->GetColumnIndex("FeatureName", TRUE); + int iGeometryType = poGFeaturesTable->GetColumnIndex("GeometryType", TRUE); + int iPrimaryGeometryFieldName = poGFeaturesTable->GetColumnIndex("PrimaryGeometryFieldName", TRUE); + + if (iFeatureName < 0 || iGeometryType < 0 || iPrimaryGeometryFieldName < 0) + { + delete poGeometryPropertiesTable; + delete poGFeaturesTable; + CPLFree(pszGCoordSystemTable); + return FALSE; + } + + if (poGeometryPropertiesTable != NULL && poGeometryPropertiesTable->GetRowCount() != poGFeaturesTable->GetRowCount()) + { + delete poGeometryPropertiesTable; + poGeometryPropertiesTable = NULL; + } + + int iGCoordSystemGUID = -1; + if (poGeometryPropertiesTable) + { + iGCoordSystemGUID = poGeometryPropertiesTable->GetColumnIndex("GCoordSystemGUID", TRUE); + if (iGCoordSystemGUID < 0) + { + delete poGeometryPropertiesTable; + delete poGFeaturesTable; + CPLFree(pszGCoordSystemTable); + return FALSE; + } + } + + while(poGFeaturesTable->GetNextRow() && + (poGeometryPropertiesTable == NULL || poGeometryPropertiesTable->GetNextRow())) + { + char* pszFeatureName = poGFeaturesTable->GetColumnAsString(iFeatureName); + //int nGeometryType = poGFeaturesTable->GetColumnAsInt(iGeometryType); + char* pszGeometryFieldName = poGFeaturesTable->GetColumnAsString(iPrimaryGeometryFieldName); + char* pszGCoordSystemGUID; + if (poGeometryPropertiesTable) + pszGCoordSystemGUID = poGeometryPropertiesTable->GetColumnAsString(iGCoordSystemGUID); + else + pszGCoordSystemGUID = NULL; + if (pszFeatureName && pszGeometryFieldName) + { + OGRMDBTable* poTable = poDB->GetTable(pszFeatureName); + if (poTable) + { + OGRMDBLayer* poLayer = new OGRMDBLayer( this, poTable ); + + if( poLayer->Initialize( pszFeatureName, + pszGeometryFieldName, + GetGeomediaSRS(pszGCoordSystemTable, pszGCoordSystemGUID) ) + != CE_None ) + { + delete poLayer; + } + else + { + papoLayers = (OGRMDBLayer**)CPLRealloc(papoLayers, (nLayers+1) * sizeof(OGRMDBLayer*)); + papoLayers[nLayers++] = poLayer; + } + } + } + CPLFree(pszFeatureName); + CPLFree(pszGeometryFieldName); + CPLFree(pszGCoordSystemGUID); + } + + delete poGeometryPropertiesTable; + delete poGFeaturesTable; + CPLFree(pszGCoordSystemTable); + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRMDBDataSource::Open( const char * pszNewName ) + +{ + CPLAssert( nLayers == 0 ); + + pszName = CPLStrdup( pszNewName ); + + if (!env.Init()) + return FALSE; + + poDB = OGRMDBDatabase::Open(&env, pszNewName); + if (!poDB) + return FALSE; + + poDB->FetchTableNames(); + + /* Is it a ESRI Personal Geodatabase ? */ + OGRMDBTable* poGDB_GeomColumns = poDB->GetTable("GDB_GeomColumns"); + if (poGDB_GeomColumns && !CSLTestBoolean(CPLGetConfigOption("MDB_RAW", "OFF"))) + { + int nRet = OpenGDB(poGDB_GeomColumns); + delete poGDB_GeomColumns; + return nRet; + } + delete poGDB_GeomColumns; + + /* Is it a Geomedia warehouse ? */ + OGRMDBTable* poGAliasTable = poDB->GetTable("GAliasTable"); + if (poGAliasTable && !CSLTestBoolean(CPLGetConfigOption("MDB_RAW", "OFF"))) + { + int nRet = OpenGeomediaWarehouse(poGAliasTable); + delete poGAliasTable; + return nRet; + } + delete poGAliasTable; + + /* Well, no, just a regular MDB */ + int nTables = (int) poDB->apoTableNames.size(); + for(int i=0;iGetTable(poDB->apoTableNames[i]); + if (poTable == NULL) + continue; + + OGRMDBLayer* poLayer = new OGRMDBLayer( this, poTable ); + if( poLayer->BuildFeatureDefn() != CE_None ) + { + delete poLayer; + continue; + } + + papoLayers = (OGRMDBLayer**)CPLRealloc(papoLayers, (nLayers+1) * sizeof(OGRMDBLayer*)); + papoLayers[nLayers++] = poLayer; + } + + return TRUE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRMDBDataSource::TestCapability( CPL_UNUSED const char * pszCap ) + +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRMDBDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRMDBDataSource::GetLayerByName( const char* pszName ) + +{ + if (pszName == NULL) + return NULL; + OGRLayer* poLayer = OGRDataSource::GetLayerByName(pszName); + if (poLayer) + return poLayer; + + for( int i = 0; i < nLayersWithInvisible; i++ ) + { + poLayer = papoLayersInvisible[i]; + + if( strcmp( pszName, poLayer->GetName() ) == 0 ) + return poLayer; + } + + OGRMDBTable* poTable = poDB->GetTable(pszName); + if (poTable == NULL) + return NULL; + + OGRMDBLayer* poMDBLayer = new OGRMDBLayer( this, poTable ); + if( poMDBLayer->BuildFeatureDefn() != CE_None ) + { + delete poMDBLayer; + return NULL; + } + + papoLayersInvisible = (OGRMDBLayer**)CPLRealloc(papoLayersInvisible, + (nLayersWithInvisible+1) * sizeof(OGRMDBLayer*)); + papoLayersInvisible[nLayersWithInvisible++] = poMDBLayer; + + return poMDBLayer; +} + +/************************************************************************/ +/* GetGeomediaSRS() */ +/************************************************************************/ + +OGRSpatialReference* OGRMDBDataSource::GetGeomediaSRS(const char* pszGCoordSystemTable, + const char* pszGCoordSystemGUID) +{ + if (pszGCoordSystemTable == NULL || pszGCoordSystemGUID == NULL) + return NULL; + + OGRLayer* poGCoordSystemTable = GetLayerByName(pszGCoordSystemTable); + if (poGCoordSystemTable == NULL) + return NULL; + + poGCoordSystemTable->ResetReading(); + + OGRFeature* poFeature; + while((poFeature = poGCoordSystemTable->GetNextFeature()) != NULL) + { + const char* pszCSGUID = poFeature->GetFieldAsString("CSGUID"); + if (pszCSGUID && strcmp(pszCSGUID, pszGCoordSystemGUID) == 0) + { + OGRSpatialReference* poSRS = OGRGetGeomediaSRS(poFeature); + delete poFeature; + return poSRS; + } + + delete poFeature; + } + + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogrmdbdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogrmdbdriver.cpp new file mode 100644 index 000000000..866372b6c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogrmdbdriver.cpp @@ -0,0 +1,125 @@ +/****************************************************************************** + * $Id: ogrmdbdriver.cpp 27794 2014-10-04 10:13:46Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements Personal Geodatabase driver. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_mdb.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrmdbdriver.cpp 27794 2014-10-04 10:13:46Z rouault $"); + +// g++ -fPIC -g -Wall ogr/ogrsf_frmts/mdb/*.cpp -shared -o ogr_MDB.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/mdb -L. -lgdal -I/usr/lib/jvm/java-6-openjdk/include -I/usr/lib/jvm/java-6-openjdk/include/linux -L/usr/lib/jvm/java-6-openjdk/jre/lib/amd64/server -ljvm + +extern "C" void RegisterOGRMDB(); + +/************************************************************************/ +/* ~OGRODBCDriver() */ +/************************************************************************/ + +OGRMDBDriver::~OGRMDBDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRMDBDriver::GetName() + +{ + return "MDB"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRMDBDriver::Open( const char * pszFilename, + int bUpdate ) + +{ + OGRMDBDataSource *poDS; + + if( bUpdate ) + return NULL; + + if( EQUALN(pszFilename, "PGEO:", strlen("PGEO:")) ) + return NULL; + + if( EQUALN(pszFilename, "GEOMEDIA:", strlen("GEOMEDIA:")) ) + return NULL; + + if( EQUALN(pszFilename, "WALK:", strlen("WALK:")) ) + return NULL; + + if( !EQUAL(CPLGetExtension(pszFilename),"mdb") ) + return NULL; + + VSIStatBuf sStat; + if (VSIStat(pszFilename, &sStat) != 0) + return NULL; + + // Open data source + poDS = new OGRMDBDataSource(); + + if( !poDS->Open( pszFilename ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRMDBDriver::TestCapability( CPL_UNUSED const char * pszCap ) + +{ + return FALSE; +} + + +/************************************************************************/ +/* RegisterOGRMDB() */ +/************************************************************************/ + +void RegisterOGRMDB() + +{ + OGRSFDriver* poDriver = new OGRMDBDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Access MDB (PGeo and Geomedia capable)" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "mdb" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_mdb.html" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogrmdbjackcess.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogrmdbjackcess.cpp new file mode 100644 index 000000000..5908944ee --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogrmdbjackcess.cpp @@ -0,0 +1,752 @@ +/****************************************************************************** + * $Id: ogrmdbjackcess.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRMDBJavaEnv class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_mdb.h" + +CPL_CVSID("$Id: ogrmdbjackcess.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +static JavaVM *jvm_static = NULL; +static JNIEnv *env_static = NULL; + +/************************************************************************/ +/* OGRMDBJavaEnv() */ +/************************************************************************/ + +OGRMDBJavaEnv::OGRMDBJavaEnv() +{ + jvm = NULL; + env = NULL; + bCalledFromJava = FALSE; + + byteArray_class = NULL; + + file_class = NULL; + file_constructor = NULL; + database_class = NULL; + database_open = NULL; + database_close = NULL; + database_getTableNames = NULL; + database_getTable = NULL; + + table_class = NULL; + table_getColumns = NULL; + table_iterator = NULL; + table_getRowCount = NULL; + + column_class = NULL; + column_getName = NULL; + column_getType = NULL; + column_getLength = NULL; + column_isVariableLength = NULL; + + datatype_class = NULL; + datatype_getValue = NULL; + + list_class = NULL; + list_iterator = NULL; + + set_class = NULL; + set_iterator = NULL; + + map_class = NULL; + map_get = NULL; + + iterator_class = NULL; + iterator_hasNext = NULL; + iterator_next = NULL; + + object_class = NULL; + object_toString = NULL; + object_getClass = NULL; + + boolean_class = NULL; + boolean_booleanValue = NULL; + + byte_class = NULL; + byte_byteValue = NULL; + + short_class = NULL; + short_shortValue = NULL; + + integer_class = NULL; + integer_intValue = NULL; + + float_class = NULL; + float_floatValue = NULL; + + double_class = NULL; + double_doubleValue = NULL; +} + +/************************************************************************/ +/* ~OGRMDBJavaEnv() */ +/************************************************************************/ + +OGRMDBJavaEnv::~OGRMDBJavaEnv() +{ + if (jvm) + { + env->DeleteLocalRef(byteArray_class); + + env->DeleteLocalRef(file_class); + env->DeleteLocalRef(database_class); + + env->DeleteLocalRef(table_class); + + env->DeleteLocalRef(column_class); + + env->DeleteLocalRef(datatype_class); + + env->DeleteLocalRef(list_class); + + env->DeleteLocalRef(set_class); + + env->DeleteLocalRef(map_class); + + env->DeleteLocalRef(iterator_class); + + env->DeleteLocalRef(object_class); + + env->DeleteLocalRef(boolean_class); + env->DeleteLocalRef(byte_class); + env->DeleteLocalRef(short_class); + env->DeleteLocalRef(integer_class); + env->DeleteLocalRef(float_class); + env->DeleteLocalRef(double_class); + + /*if (!bCalledFromJava) + { + CPLDebug("MDB", "Destroying JVM"); + int ret = jvm->DestroyJavaVM(); + CPLDebug("MDB", "ret=%d", ret); + }*/ + } +} + +#define CHECK(x, y) do {x = y; if (!x) { CPLError(CE_Failure, CPLE_AppDefined, #y " failed"); return FALSE;} } while(0) + +/************************************************************************/ +/* Init() */ +/************************************************************************/ + +int OGRMDBJavaEnv::Init() +{ + if (jvm_static == NULL) + { + JavaVM* vmBuf[1]; + jsize nVMs; + + /* Are we already called from Java ? */ + if (JNI_GetCreatedJavaVMs(vmBuf, 1, &nVMs) == JNI_OK && nVMs == 1) + { + jvm = vmBuf[0]; + if (jvm->GetEnv((void **)&env, JNI_VERSION_1_2) == JNI_OK) + { + bCalledFromJava = TRUE; + } + else + { + jvm = NULL; + env = NULL; + } + } + else + { + JavaVMInitArgs args; + JavaVMOption options[1]; + args.version = JNI_VERSION_1_2; + const char* pszClassPath = CPLGetConfigOption("CLASSPATH", NULL); + CPLString osClassPathOption; + if (pszClassPath) + { + args.nOptions = 1; + osClassPathOption.Printf("-Djava.class.path=%s", pszClassPath); + options[0].optionString = (char*) osClassPathOption.c_str(); + args.options = options; + } + else + args.nOptions = 0; + args.ignoreUnrecognized = JNI_FALSE; + + int ret = JNI_CreateJavaVM(&jvm, (void **)&env, &args); + if (ret != 0 || jvm == NULL || env == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "JNI_CreateJavaVM failed (%d)", ret); + return FALSE; + } + + jvm_static = jvm; + env_static = env; + } + } + else + { + jvm = jvm_static; + env = env_static; + } + + CHECK(byteArray_class, env->FindClass("[B")); + CHECK(file_class, env->FindClass("java/io/File")); + CHECK(file_constructor, env->GetMethodID(file_class, "", "(Ljava/lang/String;)V")); + CHECK(database_class, env->FindClass("com/healthmarketscience/jackcess/Database")); + + CHECK(database_open, env->GetStaticMethodID(database_class, "open", "(Ljava/io/File;Z)Lcom/healthmarketscience/jackcess/Database;")); + CHECK(database_close, env->GetMethodID(database_class, "close", "()V")); + CHECK(database_getTableNames, env->GetMethodID(database_class, "getTableNames", "()Ljava/util/Set;")); + CHECK(database_getTable, env->GetMethodID(database_class, "getTable", "(Ljava/lang/String;)Lcom/healthmarketscience/jackcess/Table;")); + + CHECK(table_class, env->FindClass("com/healthmarketscience/jackcess/Table")); + CHECK(table_getColumns, env->GetMethodID(table_class, "getColumns", "()Ljava/util/List;")); + CHECK(table_iterator, env->GetMethodID(table_class, "iterator", "()Ljava/util/Iterator;")); + CHECK(table_getRowCount, env->GetMethodID(table_class, "getRowCount", "()I")); + + CHECK(column_class, env->FindClass("com/healthmarketscience/jackcess/Column")); + CHECK(column_getName, env->GetMethodID(column_class, "getName", "()Ljava/lang/String;")); + CHECK(column_getType, env->GetMethodID(column_class, "getType", "()Lcom/healthmarketscience/jackcess/DataType;")); + CHECK(column_getLength, env->GetMethodID(column_class, "getLength", "()S")); + CHECK(column_isVariableLength, env->GetMethodID(column_class, "isVariableLength", "()Z")); + + CHECK(datatype_class, env->FindClass("com/healthmarketscience/jackcess/DataType")); + CHECK(datatype_getValue, env->GetMethodID(datatype_class, "getValue", "()B")); + + CHECK(list_class, env->FindClass("java/util/List")); + CHECK(list_iterator, env->GetMethodID(list_class, "iterator", "()Ljava/util/Iterator;")); + + CHECK(set_class, env->FindClass("java/util/Set")); + CHECK(set_iterator, env->GetMethodID(set_class, "iterator", "()Ljava/util/Iterator;")); + + CHECK(map_class, env->FindClass("java/util/Map")); + CHECK(map_get, env->GetMethodID(map_class, "get", "(Ljava/lang/Object;)Ljava/lang/Object;")); + + CHECK(iterator_class, env->FindClass("java/util/Iterator")); + CHECK(iterator_hasNext, env->GetMethodID(iterator_class, "hasNext", "()Z")); + CHECK(iterator_next, env->GetMethodID(iterator_class, "next", "()Ljava/lang/Object;")); + + CHECK(object_class, env->FindClass("java/lang/Object")); + CHECK(object_toString, env->GetMethodID(object_class, "toString", "()Ljava/lang/String;")); + CHECK(object_getClass, env->GetMethodID(object_class, "getClass", "()Ljava/lang/Class;")); + + CHECK(boolean_class, env->FindClass("java/lang/Boolean")); + CHECK(boolean_booleanValue, env->GetMethodID(boolean_class, "booleanValue", "()Z")); + + CHECK(byte_class, env->FindClass("java/lang/Byte")); + CHECK(byte_byteValue, env->GetMethodID(byte_class, "byteValue", "()B")); + + CHECK(short_class, env->FindClass("java/lang/Short")); + CHECK(short_shortValue, env->GetMethodID(short_class, "shortValue", "()S")); + + CHECK(integer_class, env->FindClass("java/lang/Integer")); + CHECK(integer_intValue, env->GetMethodID(integer_class, "intValue", "()I")); + + CHECK(float_class, env->FindClass("java/lang/Float")); + CHECK(float_floatValue, env->GetMethodID(float_class, "floatValue", "()F")); + + CHECK(double_class, env->FindClass("java/lang/Double")); + CHECK(double_doubleValue, env->GetMethodID(integer_class, "doubleValue", "()D")); + + return TRUE; +} + + +/************************************************************************/ +/* ExceptionOccured() */ +/************************************************************************/ + +int OGRMDBJavaEnv::ExceptionOccured() +{ + jthrowable exc = env->ExceptionOccurred(); + if (exc) + { + env->ExceptionDescribe(); + env->ExceptionClear(); + return TRUE; + } + return FALSE; +} + + +/************************************************************************/ +/* OGRMDBDatabase() */ +/************************************************************************/ + +OGRMDBDatabase::OGRMDBDatabase() +{ + env = NULL; + database = NULL; +} + +/************************************************************************/ +/* ~OGRMDBDatabase() */ +/************************************************************************/ + +OGRMDBDatabase::~OGRMDBDatabase() +{ + if (database) + { + CPLDebug("MDB", "Closing database"); + env->env->CallVoidMethod(database, env->database_close); + + env->env->DeleteGlobalRef(database); + } +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRMDBDatabase* OGRMDBDatabase::Open(OGRMDBJavaEnv* env, const char* pszName) +{ + jstring jstr = env->env->NewStringUTF(pszName); + jobject file = env->env->NewObject(env->file_class, env->file_constructor, jstr); + if (env->ExceptionOccured()) return NULL; + env->env->ReleaseStringUTFChars(jstr, NULL); + + jobject database = env->env->CallStaticObjectMethod(env->database_class, env->database_open, file, JNI_TRUE); + + env->env->DeleteLocalRef(file); + + if (env->ExceptionOccured()) return NULL; + if (database == NULL) + return NULL; + + OGRMDBDatabase* poDB = new OGRMDBDatabase(); + poDB->env = env; + poDB->database = env->env->NewGlobalRef(database); + env->env->DeleteLocalRef(database); + return poDB; +} + +/************************************************************************/ +/* FetchTableNames() */ +/************************************************************************/ + +int OGRMDBDatabase::FetchTableNames() +{ + if (env->bCalledFromJava) + env->Init(); + + jobject table_set = env->env->CallObjectMethod(database, env->database_getTableNames); + if (env->ExceptionOccured()) return FALSE; + jobject iterator = env->env->CallObjectMethod(table_set, env->set_iterator); + if (env->ExceptionOccured()) return FALSE; + + while( env->env->CallBooleanMethod(iterator, env->iterator_hasNext) ) + { + if (env->ExceptionOccured()) return FALSE; + jstring table_name_jstring = (jstring) env->env->CallObjectMethod(iterator, env->iterator_next); + if (env->ExceptionOccured()) return FALSE; + jboolean is_copy; + const char* table_name_str = env->env->GetStringUTFChars(table_name_jstring, &is_copy); + + apoTableNames.push_back(table_name_str); + //CPLDebug("MDB", "Table %s", table_name_str); + + env->env->ReleaseStringUTFChars(table_name_jstring, table_name_str); + env->env->DeleteLocalRef(table_name_jstring); + } + env->env->DeleteLocalRef(iterator); + env->env->DeleteLocalRef(table_set); + return TRUE; +} + +/************************************************************************/ +/* GetTable() */ +/************************************************************************/ + +OGRMDBTable* OGRMDBDatabase::GetTable(const char* pszTableName) +{ + if (env->bCalledFromJava) + env->Init(); + + jstring table_name_jstring = env->env->NewStringUTF(pszTableName); + jobject table = env->env->CallObjectMethod(database, env->database_getTable, table_name_jstring); + if (env->ExceptionOccured()) return NULL; + env->env->DeleteLocalRef(table_name_jstring); + + if (!table) + return NULL; + + jobject global_table = env->env->NewGlobalRef(table); + env->env->DeleteLocalRef(table); + table = global_table; + + OGRMDBTable* poTable = new OGRMDBTable(env, this, table, pszTableName); + if (!poTable->FetchColumns()) + { + delete poTable; + return NULL; + } + return poTable; +} + +/************************************************************************/ +/* OGRMDBTable() */ +/************************************************************************/ + +OGRMDBTable::OGRMDBTable(OGRMDBJavaEnv* env, OGRMDBDatabase* poDB, jobject table, const char* pszTableName ) +{ + this->env = env; + this->poDB = poDB; + this->table = table; + osTableName = pszTableName; + table_iterator_obj = NULL; + row = NULL; +} + +/************************************************************************/ +/* ~OGRMDBTable() */ +/************************************************************************/ + +OGRMDBTable::~OGRMDBTable() +{ + if (env) + { + //CPLDebug("MDB", "Freeing table %s", osTableName.c_str()); + if (env->bCalledFromJava) + env->Init(); + + int i; + for(i=0;i<(int)apoColumnNameObjects.size();i++) + env->env->DeleteGlobalRef(apoColumnNameObjects[i]); + + env->env->DeleteGlobalRef(table_iterator_obj); + env->env->DeleteGlobalRef(row); + env->env->DeleteGlobalRef(table); + } +} + +/************************************************************************/ +/* FetchColumns() */ +/************************************************************************/ + +int OGRMDBTable::FetchColumns() +{ + if (env->bCalledFromJava) + env->Init(); + + jobject column_lists = env->env->CallObjectMethod(table, env->table_getColumns); + if (env->ExceptionOccured()) return FALSE; + + jobject iterator_cols = env->env->CallObjectMethod(column_lists, env->list_iterator); + if (env->ExceptionOccured()) return FALSE; + + while( env->env->CallBooleanMethod(iterator_cols, env->iterator_hasNext) ) + { + if (env->ExceptionOccured()) return FALSE; + + jobject column = env->env->CallObjectMethod(iterator_cols, env->iterator_next); + if (env->ExceptionOccured()) return FALSE; + + jstring column_name_jstring = (jstring) env->env->CallObjectMethod(column, env->column_getName); + if (env->ExceptionOccured()) return FALSE; + jboolean is_copy; + const char* column_name_str = env->env->GetStringUTFChars(column_name_jstring, &is_copy); + apoColumnNames.push_back(column_name_str); + env->env->ReleaseStringUTFChars(column_name_jstring, column_name_str); + + apoColumnNameObjects.push_back((jstring) env->env->NewGlobalRef(column_name_jstring)); + env->env->DeleteLocalRef(column_name_jstring); + + jobject column_type = env->env->CallObjectMethod(column, env->column_getType); + if (env->ExceptionOccured()) return FALSE; + int type = env->env->CallByteMethod(column_type, env->datatype_getValue); + if (env->ExceptionOccured()) return FALSE; + apoColumnTypes.push_back(type); + + int isvariablelength = env->env->CallBooleanMethod(column, env->column_isVariableLength); + if (env->ExceptionOccured()) return FALSE; + if (!isvariablelength) + { + int length = env->env->CallShortMethod(column, env->column_getLength); + if (env->ExceptionOccured()) return FALSE; + apoColumnLengths.push_back(length); + } + else + apoColumnLengths.push_back(0); + + //CPLDebug("MDB", "Column %s, type = %d", apoColumnNames[apoColumnNames.size()-1].c_str(), type); + + env->env->DeleteLocalRef(column_type); + + env->env->DeleteLocalRef(column); + } + env->env->DeleteLocalRef(iterator_cols); + env->env->DeleteLocalRef(column_lists); + + return TRUE; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRMDBTable::ResetReading() +{ + if (env->bCalledFromJava) + env->Init(); + + env->env->DeleteGlobalRef(table_iterator_obj); + table_iterator_obj = NULL; + env->env->DeleteGlobalRef(row); + row = NULL; +} + +/************************************************************************/ +/* GetNextRow() */ +/************************************************************************/ + +int OGRMDBTable::GetNextRow() +{ + if (env->bCalledFromJava) + env->Init(); + + if (table_iterator_obj == NULL) + { + table_iterator_obj = env->env->CallObjectMethod(table, env->table_iterator); + if (env->ExceptionOccured()) return FALSE; + if (table_iterator_obj) + { + jobject global_table_iterator_obj = env->env->NewGlobalRef(table_iterator_obj); + env->env->DeleteLocalRef(table_iterator_obj); + table_iterator_obj = global_table_iterator_obj; + } + } + if (table_iterator_obj == NULL) + return FALSE; + + if (!env->env->CallBooleanMethod(table_iterator_obj, env->iterator_hasNext)) + return FALSE; + if (env->ExceptionOccured()) return FALSE; + + if (row) + { + env->env->DeleteGlobalRef(row); + row = NULL; + } + + row = env->env->CallObjectMethod(table_iterator_obj, env->iterator_next); + if (env->ExceptionOccured()) return FALSE; + if (row == NULL) + return FALSE; + + jobject global_row = env->env->NewGlobalRef(row); + env->env->DeleteLocalRef(row); + row = global_row; + + return TRUE; +} + +/************************************************************************/ +/* GetColumnVal() */ +/************************************************************************/ + +jobject OGRMDBTable::GetColumnVal(int iCol) +{ + if (row == NULL) + return NULL; + + jobject val = env->env->CallObjectMethod(row, env->map_get, apoColumnNameObjects[iCol]); + if (env->ExceptionOccured()) return NULL; + return val; +} + +/************************************************************************/ +/* GetColumnAsString() */ +/************************************************************************/ + +char* OGRMDBTable::GetColumnAsString(int iCol) +{ + jobject val = GetColumnVal(iCol); + if (!val) return NULL; + + jstring val_jstring = (jstring) env->env->CallObjectMethod(val, env->object_toString); + if (env->ExceptionOccured()) return NULL; + jboolean is_copy; + const char* val_str = env->env->GetStringUTFChars(val_jstring, &is_copy); + char* dup_str = (val_str) ? CPLStrdup(val_str) : NULL; + env->env->ReleaseStringUTFChars(val_jstring, val_str); + env->env->DeleteLocalRef(val_jstring); + + env->env->DeleteLocalRef(val); + + return dup_str; +} + +/************************************************************************/ +/* GetColumnAsInt() */ +/************************************************************************/ + +int OGRMDBTable::GetColumnAsInt(int iCol) +{ + jobject val = GetColumnVal(iCol); + if (!val) return 0; + + int int_val = 0; + if (apoColumnTypes[iCol] == MDB_Boolean) + int_val = env->env->CallBooleanMethod(val, env->boolean_booleanValue); + else if (apoColumnTypes[iCol] == MDB_Byte) + int_val = env->env->CallByteMethod(val, env->byte_byteValue); + else if (apoColumnTypes[iCol] == MDB_Short) + int_val = env->env->CallShortMethod(val, env->short_shortValue); + else if (apoColumnTypes[iCol] == MDB_Int) + int_val = env->env->CallIntMethod(val, env->integer_intValue); + if (env->ExceptionOccured()) return 0; + + env->env->DeleteLocalRef(val); + + return int_val; +} + +/************************************************************************/ +/* GetColumnAsDouble() */ +/************************************************************************/ + +double OGRMDBTable::GetColumnAsDouble(int iCol) +{ + jobject val = GetColumnVal(iCol); + if (!val) return 0; + + double double_val = 0; + if (apoColumnTypes[iCol] == MDB_Double) + double_val = env->env->CallDoubleMethod(val, env->double_doubleValue); + else if (apoColumnTypes[iCol] == MDB_Float) + double_val = env->env->CallFloatMethod(val, env->float_floatValue); + if (env->ExceptionOccured()) return 0; + + env->env->DeleteLocalRef(val); + + return double_val; +} + +/************************************************************************/ +/* GetColumnAsBinary() */ +/************************************************************************/ + +GByte* OGRMDBTable::GetColumnAsBinary(int iCol, int* pnBytes) +{ + *pnBytes = 0; + + jobject val = GetColumnVal(iCol); + if (!val) return NULL; + + if (!env->env->IsInstanceOf(val, env->byteArray_class)) + return NULL; + + jbyteArray byteArray = (jbyteArray) val; + *pnBytes = env->env->GetArrayLength(byteArray); + if (env->ExceptionOccured()) return NULL; + jboolean is_copy; + jbyte* elts = env->env->GetByteArrayElements(byteArray, &is_copy); + if (env->ExceptionOccured()) return NULL; + + GByte* pData = (GByte*)CPLMalloc(*pnBytes); + memcpy(pData, elts, *pnBytes); + + env->env->ReleaseByteArrayElements(byteArray, elts, JNI_ABORT); + + env->env->DeleteLocalRef(val); + + return pData; +} + +/************************************************************************/ +/* DumpTable() */ +/************************************************************************/ + +void OGRMDBTable::DumpTable() +{ + ResetReading(); + int iRow = 0; + int nCols = apoColumnNames.size(); + while(GetNextRow()) + { + printf("Row = %d\n", iRow ++); + for(int i=0;ibCalledFromJava) + env->Init(); + int nRowCount = env->env->CallIntMethod(table, env->table_getRowCount); + if (env->ExceptionOccured()) return 0; + return nRowCount; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogrmdblayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogrmdblayer.cpp new file mode 100644 index 000000000..73d4541e3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mdb/ogrmdblayer.cpp @@ -0,0 +1,620 @@ +/****************************************************************************** + * $Id: ogrmdblayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRMDBLayer class + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_mdb.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogrpgeogeometry.h" +#include "ogrgeomediageometry.h" + +CPL_CVSID("$Id: ogrmdblayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRMDBLayer() */ +/************************************************************************/ + +OGRMDBLayer::OGRMDBLayer(OGRMDBDataSource* poDS, OGRMDBTable* poMDBTable) + +{ + this->poDS = poDS; + this->poMDBTable = poMDBTable; + + eGeometryType = MDB_GEOM_NONE; + + iGeomColumn = -1; + pszGeomColumn = NULL; + pszFIDColumn = NULL; + + panFieldOrdinals = NULL; + + poFeatureDefn = NULL; + + iNextShapeId = 0; + + poSRS = NULL; + nSRSId = -2; // we haven't even queried the database for it yet. + + bHasExtent = FALSE; +} + +/************************************************************************/ +/* ~OGRMDBLayer() */ +/************************************************************************/ + +OGRMDBLayer::~OGRMDBLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "MDB", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poFeatureDefn != NULL ) + { + poFeatureDefn->Release(); + poFeatureDefn = NULL; + } + + CPLFree( pszGeomColumn ); + CPLFree( pszFIDColumn ); + + CPLFree( panFieldOrdinals ); + + if( poSRS != NULL ) + { + poSRS->Release(); + poSRS = NULL; + } + + delete poMDBTable; +} + +/************************************************************************/ +/* BuildFeatureDefn() */ +/* */ +/* Build feature definition from a set of column definitions */ +/* set on a statement. Sift out geometry and FID fields. */ +/************************************************************************/ + +CPLErr OGRMDBLayer::BuildFeatureDefn() + +{ + poFeatureDefn = new OGRFeatureDefn( poMDBTable->GetName() ); + SetDescription( poFeatureDefn->GetName() ); + + poFeatureDefn->Reference(); + + + int nRawColumns = poMDBTable->GetColumnCount(); + panFieldOrdinals = (int *) CPLMalloc( sizeof(int) * nRawColumns ); + + for( int iCol = 0; iCol < nRawColumns; iCol++ ) + { + const char* pszColName = poMDBTable->GetColumnName(iCol); + OGRFieldDefn oField(pszColName, OFTString ); + + if( pszGeomColumn != NULL + && EQUAL(pszColName,pszGeomColumn) ) + continue; + + if( eGeometryType == MDB_GEOM_PGEO + && pszFIDColumn == NULL + && EQUAL(pszColName,"OBJECTID") ) + { + pszFIDColumn = CPLStrdup(pszColName); + } + + if( eGeometryType == MDB_GEOM_PGEO + && pszGeomColumn == NULL + && EQUAL(pszColName,"Shape") ) + { + pszGeomColumn = CPLStrdup(pszColName); + continue; + } + + switch( poMDBTable->GetColumnType(iCol) ) + { + case MDB_Boolean: + oField.SetType( OFTInteger ); + oField.SetWidth(1); + break; + + case MDB_Byte: + case MDB_Short: + case MDB_Int: + oField.SetType( OFTInteger ); + break; + + case MDB_Binary: + case MDB_OLE: + oField.SetType( OFTBinary ); + break; + + case MDB_Float: + case MDB_Double: + oField.SetType( OFTReal ); + break; + + case MDB_Text: + oField.SetWidth(poMDBTable->GetColumnLength(iCol)); + break; + + default: + /* leave it as OFTString */; + } + + poFeatureDefn->AddFieldDefn( &oField ); + panFieldOrdinals[poFeatureDefn->GetFieldCount() - 1] = iCol+1; + } + + if( poFeatureDefn->GetGeomFieldCount() > 0 ) + { + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + if( pszGeomColumn != NULL ) + poFeatureDefn->GetGeomFieldDefn(0)->SetName(pszGeomColumn); + } + + return CE_None; +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRMDBLayer::ResetReading() + +{ + iNextShapeId = 0; + poMDBTable->ResetReading(); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRMDBLayer::GetFeatureCount(int bForce) +{ + if (m_poFilterGeom != NULL || m_poAttrQuery != NULL) + return OGRLayer::GetFeatureCount(bForce); + return poMDBTable->GetRowCount(); +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRMDBLayer::GetNextFeature() + +{ + for( ; TRUE; ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRMDBLayer::GetNextRawFeature() + +{ + OGRErr err = OGRERR_NONE; + + if( !poMDBTable->GetNextRow() ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create a feature from the current result. */ +/* -------------------------------------------------------------------- */ + int iField; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + if( pszFIDColumn != NULL && poMDBTable->GetColumnIndex(pszFIDColumn) > -1 ) + poFeature->SetFID( + poMDBTable->GetColumnAsInt(poMDBTable->GetColumnIndex(pszFIDColumn)) ); + else + poFeature->SetFID( iNextShapeId ); + + iNextShapeId++; + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Set the fields. */ +/* -------------------------------------------------------------------- */ + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + int iSrcField = panFieldOrdinals[iField]-1; + char *pszValue = poMDBTable->GetColumnAsString( iSrcField ); + OGRFieldType eType = poFeature->GetFieldDefnRef(iField)->GetType(); + + if( pszValue == NULL ) + /* no value */; + else if( eType == OFTBinary ) + { + int nBytes = 0; + GByte* pData = poMDBTable->GetColumnAsBinary( iSrcField, &nBytes); + poFeature->SetField( iField, + nBytes, + pData ); + CPLFree(pData); + } + else if ( eType == OFTInteger && EQUAL(pszValue, "true")) + { + poFeature->SetField( iField, 1 ); + } + else + { + poFeature->SetField( iField, pszValue ); + } + + CPLFree(pszValue); + } + + if( !(m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + +/* -------------------------------------------------------------------- */ +/* Try to extract a geometry. */ +/* -------------------------------------------------------------------- */ + if( eGeometryType == MDB_GEOM_PGEO && iGeomColumn >= 0) + { + int nBytes = 0; + GByte* pData = poMDBTable->GetColumnAsBinary( iGeomColumn, &nBytes); + OGRGeometry *poGeom = NULL; + + if( pData != NULL ) + { + err = OGRCreateFromShapeBin( pData, &poGeom, nBytes ); + if( OGRERR_NONE != err ) + { + CPLDebug( "MDB", + "Translation shape binary to OGR geometry failed (FID=%ld)", + (long)poFeature->GetFID() ); + } + } + + CPLFree(pData); + + if( poGeom != NULL && OGRERR_NONE == err ) + { + poGeom->assignSpatialReference( poSRS ); + poFeature->SetGeometryDirectly( poGeom ); + } + } + else if( eGeometryType == MDB_GEOM_GEOMEDIA && iGeomColumn >= 0) + { + int nBytes = 0; + GByte* pData = poMDBTable->GetColumnAsBinary( iGeomColumn, &nBytes); + OGRGeometry *poGeom = NULL; + + if( pData != NULL ) + { + err = OGRCreateFromGeomedia( pData, &poGeom, nBytes ); + if( OGRERR_NONE != err ) + { + CPLDebug( "MDB", + "Translation geomedia binary to OGR geometry failed (FID=%ld)", + (long)poFeature->GetFID() ); + } + } + + CPLFree(pData); + + if( poGeom != NULL && OGRERR_NONE == err ) + { + poGeom->assignSpatialReference( poSRS ); + poFeature->SetGeometryDirectly( poGeom ); + } + } + + return poFeature; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRMDBLayer::GetFeature( GIntBig nFeatureId ) + +{ + /* This should be implemented directly! */ + + return OGRLayer::GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRMDBLayer::TestCapability( const char * pszCap ) + +{ + /*if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else*/ + if( EQUAL(pszCap,OLCFastFeatureCount) || + EQUAL(pszCap,OLCFastGetExtent) ) + return m_poFilterGeom == NULL && m_poAttrQuery == NULL; + + else + return FALSE; +} + +/************************************************************************/ +/* LookupSRID() */ +/************************************************************************/ + +void OGRMDBLayer::LookupSRID( int nSRID ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch the corresponding WKT from the SpatialRef table. */ +/* -------------------------------------------------------------------- */ + OGRMDBDatabase* poDB = poMDBTable->GetDB(); + OGRMDBTable* poSRSTable = poDB->GetTable("GDB_SpatialRefs"); + if (poSRSTable == NULL) + return; + + int iSRTEXT = poSRSTable->GetColumnIndex("SRTEXT", TRUE); + int iSRID = poSRSTable->GetColumnIndex("SRID", TRUE); + + if (iSRTEXT < 0 || iSRID < 0) + { + delete poSRSTable; + return; + } + + char* pszSRText = NULL; + while(poSRSTable->GetNextRow()) + { + int nTableSRID = poSRSTable->GetColumnAsInt(iSRID); + if (nTableSRID == nSRID) + { + pszSRText = poSRSTable->GetColumnAsString(iSRTEXT); + break; + } + } + + if (pszSRText == NULL) + { + delete poSRSTable; + return; + } + +/* -------------------------------------------------------------------- */ +/* Check that it isn't just a GUID. We don't know how to */ +/* translate those. */ +/* -------------------------------------------------------------------- */ + + if( pszSRText[0] == '{' ) + { + CPLDebug( "MDB", "Ignoreing GUID SRTEXT: %s", pszSRText ); + delete poSRSTable; + CPLFree(pszSRText); + return; + } + +/* -------------------------------------------------------------------- */ +/* Turn it into an OGRSpatialReference. */ +/* -------------------------------------------------------------------- */ + poSRS = new OGRSpatialReference(); + + char* pszSRTextPtr = pszSRText; + if( poSRS->importFromWkt( &pszSRTextPtr ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "importFromWKT() failed on SRS '%s'.", + pszSRText); + delete poSRS; + poSRS = NULL; + } + else if( poSRS->morphFromESRI() != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "morphFromESRI() failed on SRS." ); + delete poSRS; + poSRS = NULL; + } + else + nSRSId = nSRID; + + delete poSRSTable; + CPLFree(pszSRText); +} + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char *OGRMDBLayer::GetFIDColumn() + +{ + if( pszFIDColumn != NULL ) + return pszFIDColumn; + else + return ""; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +CPLErr OGRMDBLayer::Initialize( CPL_UNUSED const char *pszTableName, + const char *pszGeomCol, + int nShapeType, + double dfExtentLeft, + double dfExtentRight, + double dfExtentBottom, + double dfExtentTop, + int nSRID, + int bHasZ ) + + +{ + CPLFree( pszGeomColumn ); + + if( pszGeomCol == NULL ) + pszGeomColumn = NULL; + else + { + pszGeomColumn = CPLStrdup( pszGeomCol ); + iGeomColumn = poMDBTable->GetColumnIndex( pszGeomColumn ); + } + + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + + bHasExtent = TRUE; + sExtent.MinX = dfExtentLeft; + sExtent.MaxX = dfExtentRight; + sExtent.MinY = dfExtentBottom; + sExtent.MaxY = dfExtentTop; + + LookupSRID( nSRID ); + + eGeometryType = MDB_GEOM_PGEO; + + CPLErr eErr; + eErr = BuildFeatureDefn(); + if( eErr != CE_None ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Setup geometry type. */ +/* -------------------------------------------------------------------- */ + OGRwkbGeometryType eOGRType; + + switch( nShapeType ) + { + case ESRI_LAYERGEOMTYPE_NULL: + eOGRType = wkbNone; + break; + + case ESRI_LAYERGEOMTYPE_POINT: + eOGRType = wkbPoint; + break; + + case ESRI_LAYERGEOMTYPE_MULTIPOINT: + eOGRType = wkbMultiPoint; + break; + + case ESRI_LAYERGEOMTYPE_POLYLINE: + eOGRType = wkbLineString; + break; + + case ESRI_LAYERGEOMTYPE_POLYGON: + case ESRI_LAYERGEOMTYPE_MULTIPATCH: + eOGRType = wkbPolygon; + break; + + default: + CPLDebug("MDB", "Unexpected value for shape type : %d", nShapeType); + eOGRType = wkbUnknown; + break; + } + + if( eOGRType != wkbUnknown && eOGRType != wkbNone && bHasZ ) + eOGRType = wkbSetZ(eOGRType); + + poFeatureDefn->SetGeomType(eOGRType); + + return CE_None; +} + + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +CPLErr OGRMDBLayer::Initialize( CPL_UNUSED const char *pszTableName, + const char *pszGeomCol, + OGRSpatialReference* poSRS ) + + +{ + CPLFree( pszGeomColumn ); + + if( pszGeomCol == NULL ) + pszGeomColumn = NULL; + else + { + pszGeomColumn = CPLStrdup( pszGeomCol ); + iGeomColumn = poMDBTable->GetColumnIndex( pszGeomColumn ); + } + + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + + eGeometryType = MDB_GEOM_GEOMEDIA; + + this->poSRS = poSRS; + + CPLErr eErr; + eErr = BuildFeatureDefn(); + if( eErr != CE_None ) + return eErr; + + return CE_None; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRMDBLayer::GetExtent( OGREnvelope *psExtent, int bForce ) + +{ + if (bHasExtent) + { + *psExtent = sExtent; + return OGRERR_NONE; + } + else + { + return OGRLayer::GetExtent(psExtent, bForce); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/GNUmakefile new file mode 100644 index 000000000..c25552e0e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrmemdriver.o ogrmemdatasource.o ogrmemlayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +$(O_OBJ): ogr_mem.h + +clean: + rm -f *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/drv_memory.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/drv_memory.html new file mode 100644 index 000000000..d31c0ee65 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/drv_memory.html @@ -0,0 +1,38 @@ + + +Memory + + + + +

    Memory

    + +This driver implements read and write access layers of features contained +entirely in memory. This is primarily useful as a high performance, and highly +malleable working data store. All update options, geometry types, and +field types are supported.

    + +There is no way to open an existing Memory datastore. It must be created +with CreateDataSource() and populated and used from that handle. When the +datastore is closed all contents are freed and destroyed.

    + +The driver does not implement spatial or attribute indexing, so spatial and +attribute queries are still evaluated against all features. Fetching features +by feature id should be very fast (just an array lookup and feature copy). +

    + +

    Creation Issues

    + +Any name may be used for a created datasource. There are no datasource +or layer creation options supported. Layer names need to be unique, but +are not otherwise constrained.

    + +Feature ids passed to CreateFeature() are preserved unless they exceed +10000000 in which case they will be reset to avoid a requirement for an +excessively large and sparse feature array.

    + +New fields can be added to layer that already has features.

    + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/makefile.vc new file mode 100644 index 000000000..f9b807bc4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = ogrmemdriver.obj ogrmemdatasource.obj ogrmemlayer.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/ogr_mem.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/ogr_mem.h new file mode 100644 index 000000000..0da4f5643 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/ogr_mem.h @@ -0,0 +1,139 @@ +/****************************************************************************** + * $Id: ogr_mem.h 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions within the OGR Memory driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRMEM_H_INCLUDED +#define _OGRMEM_H_INCLUDED + +#include "ogrsf_frmts.h" + +/************************************************************************/ +/* OGRMemLayer */ +/************************************************************************/ +class OGRMemDataSource; + +class OGRMemLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + + GIntBig nFeatureCount; + GIntBig nMaxFeatureCount; + OGRFeature **papoFeatures; + + GIntBig iNextReadFID; + GIntBig iNextCreateFID; + + int bUpdatable; + int bAdvertizeUTF8; + + int bHasHoles; + + public: + OGRMemLayer( const char * pszName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eGeomType ); + ~OGRMemLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + + OGRFeature *GetFeature( GIntBig nFeatureId ); + OGRErr ISetFeature( OGRFeature *poFeature ); + OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + GIntBig GetFeatureCount( int ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr DeleteField( int iField ); + virtual OGRErr ReorderFields( int* panMap ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + virtual OGRErr CreateGeomField( OGRGeomFieldDefn *poGeomField, + int bApproxOK = TRUE ); + + int TestCapability( const char * ); + + void SetUpdatable(int bUpdatableIn) { bUpdatable = bUpdatableIn; } + void SetAdvertizeUTF8(int bAdvertizeUTF8In) { bAdvertizeUTF8 = bAdvertizeUTF8In; } + + GIntBig GetNextReadFID() { return iNextReadFID; } +}; + +/************************************************************************/ +/* OGRMemDataSource */ +/************************************************************************/ + +class OGRMemDataSource : public OGRDataSource +{ + OGRMemLayer **papoLayers; + int nLayers; + + char *pszName; + + public: + OGRMemDataSource( const char *, char ** ); + ~OGRMemDataSource(); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + OGRErr DeleteLayer( int iLayer ); + + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRMemDriver */ +/************************************************************************/ + +class OGRMemDriver : public OGRSFDriver +{ + public: + ~OGRMemDriver(); + + const char *GetName(); + OGRDataSource *Open( const char *, int ); + + virtual OGRDataSource *CreateDataSource( const char *pszName, + char ** = NULL ); + + int TestCapability( const char * ); +}; + + +#endif /* ndef _OGRMEM_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/ogrmemdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/ogrmemdatasource.cpp new file mode 100644 index 000000000..30627d399 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/ogrmemdatasource.cpp @@ -0,0 +1,143 @@ +/****************************************************************************** + * $Id: ogrmemdatasource.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRMemDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_mem.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrmemdatasource.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +/************************************************************************/ +/* OGRMemDataSource() */ +/************************************************************************/ + +OGRMemDataSource::OGRMemDataSource( const char *pszFilename, + CPL_UNUSED char **papszOptions) +{ + pszName = CPLStrdup(pszFilename); + papoLayers = NULL; + nLayers = 0; +} + +/************************************************************************/ +/* ~OGRMemDataSource() */ +/************************************************************************/ + +OGRMemDataSource::~OGRMemDataSource() + +{ + CPLFree( pszName ); + + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRMemDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + CPL_UNUSED char ** papszOptions ) +{ +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRMemLayer *poLayer; + + poLayer = new OGRMemLayer( pszLayerName, poSRS, eType ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRMemLayer **) + CPLRealloc( papoLayers, sizeof(OGRMemLayer *) * (nLayers+1) ); + + papoLayers[nLayers++] = poLayer; + + return poLayer; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +OGRErr OGRMemDataSource::DeleteLayer( int iLayer ) + +{ + if( iLayer >= 0 && iLayer < nLayers ) + { + delete papoLayers[iLayer]; + + for( int i = iLayer+1; i < nLayers; i++ ) + papoLayers[i-1] = papoLayers[i]; + + nLayers--; + + return OGRERR_NONE; + } + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRMemDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCDeleteLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCCreateGeomFieldAfterCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCCurveGeometries) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRMemDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/ogrmemdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/ogrmemdriver.cpp new file mode 100644 index 000000000..1e5e30b5b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/ogrmemdriver.cpp @@ -0,0 +1,100 @@ +/****************************************************************************** + * $Id: ogrmemdriver.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRMemDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_mem.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrmemdriver.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* ~OGRMemDriver() */ +/************************************************************************/ + +OGRMemDriver::~OGRMemDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRMemDriver::GetName() + +{ + return "Memory"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRMemDriver::Open( CPL_UNUSED const char * pszFilename, int ) +{ + return NULL; +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRMemDriver::CreateDataSource( const char * pszName, + char **papszOptions ) + +{ + return new OGRMemDataSource( pszName, papszOptions ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRMemDriver::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODrCCreateDataSource) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRMem() */ +/************************************************************************/ + +void RegisterOGRMEM() + +{ + OGRSFDriver* poDriver = new OGRMemDriver; + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime Time IntegerList Integer64List RealList StringList Binary" ); + + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( poDriver ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/ogrmemlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/ogrmemlayer.cpp new file mode 100644 index 000000000..4ab1676f3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mem/ogrmemlayer.cpp @@ -0,0 +1,662 @@ +/****************************************************************************** + * $Id: ogrmemlayer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRMemLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_mem.h" +#include "cpl_conv.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrmemlayer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGRMemLayer() */ +/************************************************************************/ + +OGRMemLayer::OGRMemLayer( const char * pszName, OGRSpatialReference *poSRSIn, + OGRwkbGeometryType eReqType ) + +{ + iNextReadFID = 0; + iNextCreateFID = 0; + + nFeatureCount = 0; + nMaxFeatureCount = 0; + papoFeatures = NULL; + + poFeatureDefn = new OGRFeatureDefn( pszName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->SetGeomType( eReqType ); + if( eReqType != wkbNone && poSRSIn != NULL ) + { + OGRSpatialReference* poSRS = poSRSIn->Clone(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + poSRS->Release(); + } + poFeatureDefn->Reference(); + + bUpdatable = TRUE; + bAdvertizeUTF8 = FALSE; + bHasHoles = FALSE; +} + +/************************************************************************/ +/* ~OGRMemLayer() */ +/************************************************************************/ + +OGRMemLayer::~OGRMemLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "Mem", CPL_FRMT_GIB " features read on layer '%s'.", + m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + for( GIntBig i = 0; i < nMaxFeatureCount; i++ ) + { + if( papoFeatures[i] != NULL ) + delete papoFeatures[i]; + } + CPLFree( papoFeatures ); + + if( poFeatureDefn ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRMemLayer::ResetReading() + +{ + iNextReadFID = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRMemLayer::GetNextFeature() + +{ + while( iNextReadFID < nMaxFeatureCount ) + { + OGRFeature *poFeature = papoFeatures[iNextReadFID++]; + + if( poFeature == NULL ) + continue; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeomFieldRef(m_iGeomFieldFilter) ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature ) ) ) + { + m_nFeaturesRead++; + return poFeature->Clone(); + } + } + + return NULL; +} + +/************************************************************************/ +/* SetNextByIndex() */ +/************************************************************************/ + +OGRErr OGRMemLayer::SetNextByIndex( GIntBig nIndex ) + +{ + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL || bHasHoles ) + return OGRLayer::SetNextByIndex( nIndex ); + + if (nIndex < 0 || nIndex >= nMaxFeatureCount) + return OGRERR_FAILURE; + + iNextReadFID = nIndex; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRMemLayer::GetFeature( GIntBig nFeatureId ) + +{ + if( nFeatureId < 0 || nFeatureId >= nMaxFeatureCount ) + return NULL; + else if( papoFeatures[nFeatureId] == NULL ) + return NULL; + else + return papoFeatures[nFeatureId]->Clone(); +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRMemLayer::ISetFeature( OGRFeature *poFeature ) + +{ + if (!bUpdatable) + return OGRERR_FAILURE; + + if( poFeature == NULL ) + return OGRERR_FAILURE; + + if( poFeature->GetFID() == OGRNullFID ) + { + while( iNextCreateFID < nMaxFeatureCount + && papoFeatures[iNextCreateFID] != NULL ) + iNextCreateFID++; + poFeature->SetFID( iNextCreateFID++ ); + } + else if ( poFeature->GetFID() < OGRNullFID ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "negative FID are not supported"); + return OGRERR_FAILURE; + } + + if( poFeature->GetFID() >= nMaxFeatureCount ) + { + GIntBig nNewCount = MAX(2*nMaxFeatureCount+10, poFeature->GetFID() + 1 ); + if( (GIntBig)(size_t)(sizeof(OGRFeature *) * nNewCount) != + (GIntBig)sizeof(OGRFeature *) * nNewCount ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate array of " CPL_FRMT_GIB " elements", nNewCount); + return OGRERR_FAILURE; + } + + OGRFeature** papoNewFeatures = (OGRFeature **) + VSIRealloc( papoFeatures, (size_t)(sizeof(OGRFeature *) * nNewCount) ); + if (papoNewFeatures == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot allocate array of " CPL_FRMT_GIB " elements", nNewCount); + return OGRERR_FAILURE; + } + papoFeatures = papoNewFeatures; + memset( papoFeatures + nMaxFeatureCount, 0, + sizeof(OGRFeature *) * (size_t)(nNewCount - nMaxFeatureCount) ); + nMaxFeatureCount = nNewCount; + } + + if( papoFeatures[poFeature->GetFID()] != NULL ) + { + delete papoFeatures[poFeature->GetFID()]; + papoFeatures[poFeature->GetFID()] = NULL; + nFeatureCount--; + } + + papoFeatures[poFeature->GetFID()] = poFeature->Clone(); + int i; + for(i = 0; i < poFeatureDefn->GetGeomFieldCount(); i ++) + { + OGRGeometry* poGeom = papoFeatures[poFeature->GetFID()]->GetGeomFieldRef(i); + if( poGeom != NULL && poGeom->getSpatialReference() == NULL ) + { + poGeom->assignSpatialReference( + poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef()); + } + } + nFeatureCount++; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRMemLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + if (!bUpdatable) + return OGRERR_FAILURE; + + if( poFeature->GetFID() != OGRNullFID && + poFeature->GetFID() != iNextCreateFID ) + bHasHoles = TRUE; + + if( poFeature->GetFID() != OGRNullFID + && poFeature->GetFID() >= 0 + && poFeature->GetFID() < nMaxFeatureCount ) + { + if( papoFeatures[poFeature->GetFID()] != NULL ) + poFeature->SetFID( OGRNullFID ); + } + + if( poFeature->GetFID() > 10000000 ) + poFeature->SetFID( OGRNullFID ); + + return SetFeature( poFeature ); +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRMemLayer::DeleteFeature( GIntBig nFID ) + +{ + if (!bUpdatable) + return OGRERR_FAILURE; + + if( nFID < 0 || nFID >= nMaxFeatureCount + || papoFeatures[nFID] == NULL ) + { + return OGRERR_FAILURE; + } + else + { + bHasHoles = TRUE; + + delete papoFeatures[nFID]; + papoFeatures[nFID] = NULL; + nFeatureCount--; + return OGRERR_NONE; + } +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRMemLayer::GetFeatureCount( int bForce ) + +{ + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL ) + return OGRLayer::GetFeatureCount( bForce ); + else + return nFeatureCount; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRMemLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return bUpdatable; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return m_poFilterGeom == NULL && m_poAttrQuery == NULL; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else if( EQUAL(pszCap,OLCDeleteFeature) ) + return bUpdatable; + + else if( EQUAL(pszCap,OLCCreateField) || + EQUAL(pszCap,OLCCreateGeomField) || + EQUAL(pszCap,OLCDeleteField) || + EQUAL(pszCap,OLCReorderFields) || + EQUAL(pszCap,OLCAlterFieldDefn) ) + return bUpdatable; + + else if( EQUAL(pszCap,OLCFastSetNextByIndex) ) + return m_poFilterGeom == NULL && m_poAttrQuery == NULL && !bHasHoles; + + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return bAdvertizeUTF8; + + else if( EQUAL(pszCap,OLCCurveGeometries) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRMemLayer::CreateField( OGRFieldDefn *poField, + CPL_UNUSED int bApproxOK ) +{ + if (!bUpdatable) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* simple case, no features exist yet. */ +/* -------------------------------------------------------------------- */ + if( nFeatureCount == 0 ) + { + poFeatureDefn->AddFieldDefn( poField ); + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Add field definition and setup remap definition. */ +/* -------------------------------------------------------------------- */ + int *panRemap; + GIntBig i; + + poFeatureDefn->AddFieldDefn( poField ); + + panRemap = (int *) CPLMalloc(sizeof(int) * poFeatureDefn->GetFieldCount()); + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if( i < poFeatureDefn->GetFieldCount() - 1 ) + panRemap[i] = (int)i; + else + panRemap[i] = -1; + } + +/* -------------------------------------------------------------------- */ +/* Remap all the internal features. Hopefully there aren't any */ +/* external features referring to our OGRFeatureDefn! */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nMaxFeatureCount; i++ ) + { + if( papoFeatures[i] != NULL ) + papoFeatures[i]->RemapFields( NULL, panRemap ); + } + + CPLFree( panRemap ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteField() */ +/************************************************************************/ + +OGRErr OGRMemLayer::DeleteField( int iField ) +{ + if (!bUpdatable) + return OGRERR_FAILURE; + + if (iField < 0 || iField >= poFeatureDefn->GetFieldCount()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Update all the internal features. Hopefully there aren't any */ +/* external features referring to our OGRFeatureDefn! */ +/* -------------------------------------------------------------------- */ + for( GIntBig i = 0; i < nMaxFeatureCount; i++ ) + { + if( papoFeatures[i] == NULL ) + continue; + + OGRField* poFieldRaw = papoFeatures[i]->GetRawFieldRef(iField); + if( papoFeatures[i]->IsFieldSet(iField) ) + { + /* Little trick to unallocate the field */ + OGRField sField; + sField.Set.nMarker1 = OGRUnsetMarker; + sField.Set.nMarker2 = OGRUnsetMarker; + papoFeatures[i]->SetField(iField, &sField); + } + + if (iField < poFeatureDefn->GetFieldCount() - 1) + { + memmove( poFieldRaw, poFieldRaw + 1, + sizeof(OGRField) * (poFeatureDefn->GetFieldCount() - 1 - iField) ); + } + } + + return poFeatureDefn->DeleteFieldDefn( iField ); +} + +/************************************************************************/ +/* ReorderFields() */ +/************************************************************************/ + +OGRErr OGRMemLayer::ReorderFields( int* panMap ) +{ + if (!bUpdatable) + return OGRERR_FAILURE; + + if (poFeatureDefn->GetFieldCount() == 0) + return OGRERR_NONE; + + OGRErr eErr = OGRCheckPermutation(panMap, poFeatureDefn->GetFieldCount()); + if (eErr != OGRERR_NONE) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Remap all the internal features. Hopefully there aren't any */ +/* external features referring to our OGRFeatureDefn! */ +/* -------------------------------------------------------------------- */ + for( GIntBig i = 0; i < nMaxFeatureCount; i++ ) + { + if( papoFeatures[i] != NULL ) + papoFeatures[i]->RemapFields( NULL, panMap ); + } + + return poFeatureDefn->ReorderFieldDefns( panMap ); +} + +/************************************************************************/ +/* AlterFieldDefn() */ +/************************************************************************/ + +OGRErr OGRMemLayer::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) +{ + if (!bUpdatable) + return OGRERR_FAILURE; + + if (iField < 0 || iField >= poFeatureDefn->GetFieldCount()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(iField); + + if ((nFlags & ALTER_TYPE_FLAG) && + poFieldDefn->GetType() != poNewFieldDefn->GetType()) + { + if ((poNewFieldDefn->GetType() == OFTDate || + poNewFieldDefn->GetType() == OFTTime || + poNewFieldDefn->GetType() == OFTDateTime) && + (poFieldDefn->GetType() == OFTDate || + poFieldDefn->GetType() == OFTTime || + poFieldDefn->GetType() == OFTDateTime)) + { + /* do nothing on features */ + } + else if (poNewFieldDefn->GetType() == OFTInteger64 && + poFieldDefn->GetType() == OFTInteger) + { + /* -------------------------------------------------------------------- */ + /* Update all the internal features. Hopefully there aren't any */ + /* external features referring to our OGRFeatureDefn! */ + /* -------------------------------------------------------------------- */ + for( GIntBig i = 0; i < nMaxFeatureCount; i++ ) + { + if( papoFeatures[i] == NULL ) + continue; + + OGRField* poFieldRaw = papoFeatures[i]->GetRawFieldRef(iField); + if( papoFeatures[i]->IsFieldSet(iField) ) + { + poFieldRaw->Integer64 = poFieldRaw->Integer; + } + } + } + else if (poNewFieldDefn->GetType() == OFTReal && + poFieldDefn->GetType() == OFTInteger) + { + /* -------------------------------------------------------------------- */ + /* Update all the internal features. Hopefully there aren't any */ + /* external features referring to our OGRFeatureDefn! */ + /* -------------------------------------------------------------------- */ + for( GIntBig i = 0; i < nMaxFeatureCount; i++ ) + { + if( papoFeatures[i] == NULL ) + continue; + + OGRField* poFieldRaw = papoFeatures[i]->GetRawFieldRef(iField); + if( papoFeatures[i]->IsFieldSet(iField) ) + { + poFieldRaw->Real = poFieldRaw->Integer; + } + } + } + else if (poNewFieldDefn->GetType() == OFTReal && + poFieldDefn->GetType() == OFTInteger64) + { + /* -------------------------------------------------------------------- */ + /* Update all the internal features. Hopefully there aren't any */ + /* external features referring to our OGRFeatureDefn! */ + /* -------------------------------------------------------------------- */ + for( GIntBig i = 0; i < nMaxFeatureCount; i++ ) + { + if( papoFeatures[i] == NULL ) + continue; + + OGRField* poFieldRaw = papoFeatures[i]->GetRawFieldRef(iField); + if( papoFeatures[i]->IsFieldSet(iField) ) + { + poFieldRaw->Real = (double) poFieldRaw->Integer64; + } + } + } + else + { + if (poNewFieldDefn->GetType() != OFTString) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can only convert from OFTInteger to OFTReal, or from anything to OFTString"); + return OGRERR_FAILURE; + } + + /* -------------------------------------------------------------------- */ + /* Update all the internal features. Hopefully there aren't any */ + /* external features referring to our OGRFeatureDefn! */ + /* -------------------------------------------------------------------- */ + for( GIntBig i = 0; i < nMaxFeatureCount; i++ ) + { + if( papoFeatures[i] == NULL ) + continue; + + OGRField* poFieldRaw = papoFeatures[i]->GetRawFieldRef(iField); + if( papoFeatures[i]->IsFieldSet(iField) ) + { + char* pszVal = CPLStrdup(papoFeatures[i]->GetFieldAsString(iField)); + + /* Little trick to unallocate the field */ + OGRField sField; + sField.Set.nMarker1 = OGRUnsetMarker; + sField.Set.nMarker2 = OGRUnsetMarker; + papoFeatures[i]->SetField(iField, &sField); + + poFieldRaw->String = pszVal; + } + } + } + + poFieldDefn->SetType(poNewFieldDefn->GetType()); + } + + if (nFlags & ALTER_NAME_FLAG) + poFieldDefn->SetName(poNewFieldDefn->GetNameRef()); + if (nFlags & ALTER_WIDTH_PRECISION_FLAG) + { + poFieldDefn->SetWidth(poNewFieldDefn->GetWidth()); + poFieldDefn->SetPrecision(poNewFieldDefn->GetPrecision()); + } + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* CreateGeomField() */ +/************************************************************************/ + +OGRErr OGRMemLayer::CreateGeomField( OGRGeomFieldDefn *poGeomField, + CPL_UNUSED int bApproxOK ) +{ + if (!bUpdatable) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* simple case, no features exist yet. */ +/* -------------------------------------------------------------------- */ + if( nFeatureCount == 0 ) + { + poFeatureDefn->AddGeomFieldDefn( poGeomField ); + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Add field definition and setup remap definition. */ +/* -------------------------------------------------------------------- */ + int *panRemap; + GIntBig i; + + poFeatureDefn->AddGeomFieldDefn( poGeomField ); + + panRemap = (int *) CPLMalloc(sizeof(int) * poFeatureDefn->GetGeomFieldCount()); + for( i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + if( i < poFeatureDefn->GetGeomFieldCount() - 1 ) + panRemap[i] = (int) i; + else + panRemap[i] = -1; + } + +/* -------------------------------------------------------------------- */ +/* Remap all the internal features. Hopefully there aren't any */ +/* external features referring to our OGRFeatureDefn! */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nMaxFeatureCount; i++ ) + { + if( papoFeatures[i] != NULL ) + papoFeatures[i]->RemapGeomFields( NULL, panRemap ); + } + + CPLFree( panRemap ); + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/GNUmakefile new file mode 100644 index 000000000..6f8f49451 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/GNUmakefile @@ -0,0 +1,28 @@ + + +include ../../../GDALmake.opt + +OBJ = mitab_rawbinblock.o mitab_mapheaderblock.o \ + mitab_mapindexblock.o mitab_mapobjectblock.o \ + mitab_mapcoordblock.o mitab_feature.o mitab_feature_mif.o \ + mitab_mapfile.o mitab_idfile.o mitab_datfile.o \ + mitab_tabfile.o mitab_miffile.o mitab_utils.o \ + mitab_imapinfofile.o mitab_middatafile.o mitab_bounds.o \ + mitab_maptoolblock.o mitab_tooldef.o mitab_coordsys.o \ + mitab_spatialref.o mitab_ogr_driver.o mitab_indfile.o \ + mitab_tabview.o mitab_ogr_datasource.o mitab_geometry.o \ + mitab_tabseamless.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) -DOGR \ + -DMITAB_USE_OFTDATETIME + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): mitab.h mitab_priv.h mitab_ogr_driver.h + +update: + copymatch.sh ~/pkg/mitab *.TXT + copymatch.sh ~/pkg/mitab/mitab *.cpp *.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/HISTORY.TXT b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/HISTORY.TXT new file mode 100644 index 000000000..c6b310c22 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/HISTORY.TXT @@ -0,0 +1,940 @@ +MITAB Library - Revision History +================================ + +This is a human-readable revision history which will attempt to document +required changes for users to migrate from one version of MITAB to the +next. Developers are strongly encouraged to document their changes and +their impacts on the users here. (Please add the most recent changes to +the top of the list.) + +For a complete change history, please see the CVS log comments. + + +CHANGES - Current Version: +-------------------------- + +Version 2.0-dev (CVS) +--------------------- + +- IMPORTANT - BACKWARDS-INCOMPATIBLE CHANGE: + OGRLayer::SetFeature() is intended to be used only in random write mode. + However, MITAB 1.x had a poorly named SetFeature() that was really doing + the job of OGRLayer::CreateFeature(). In order to implement random write + support through OGRLayer::SetFeature() in a way that is consistent with + the OGRLayer methods, we had to fix this error from the early MITAB days. + + In MITAB 2.x, we have replaced the old SetFeature() with a CreateFeature(), + and a new SetFeature() has been added that's in line with the + OGRLayer::SetFeature() signature and behavior. + Application code that was writing features using SetFeature() with + MITAB 1.x will need to be changed to use CreateFeature() with MITAB 2.x. + + Note that the return value of CreateFeature() is an OGRErr code and not + a feature id as was the case with the MITAB 1.x SetFeature() implementation. + The feature id of the created feature is returned by updating the id of the + passed in feature instead of through the return value of the method as + was the case with the old SetFeature() method in MITAB 1.x. + +- Added Visual Studio Project files in contrib/vcproj, contributed by + Tony Wells (bug 1877) + +- Updated contrib/mitab_dyn.pas and contrib/mitab.vb for V1.7.0 changes + +- Fixed const char * warnings with GCC 4.3 (GDAL ticket #2325) + +- Fixed problem with corrupt pointer if file not found (bug 1899) + +- Fixed tabdump build problem if DEBUG option not provided (bug 1898) + +- Fixed deprecated warnings generated by mitab.h (bug 1907) + +- Fixed multi-line text rendering (bug 1916) + +- Fixed multi-line text height problem (bug 1919) + +- Fixed the text font size problem (bug 1918) + +- Add text outline color support (halo background in MapInfo) (bug 1921) + +- Add font text styles support (bold, italic, etc.) (bug 1922) + +- Added Font Point styles support (halo, border) (bug 1925) + +- C API: Added mitab_c_get_feature_count_by_type() (bug 1952) + +- Fixed bug: MITAB doesn't support writing DateTime type (bug 1948) + +- Fixed bug with the characters ",\n in the tab2tab application. (bug + 1945) + +- mitab.h: Export MITABSpatialRef2CoordSys() and MITABCoordSys2SpatialRef() + from the DLL. Mostly for use in GDAL + +- Added support to use OFTDateTime/OFTDate/OFTTime type when compiled with + OGR and fixed reading/writing support for these types. (Bug 1948 and + GDAL ticket #2585) + + +Version 1.7.0-beta1 (2007-02-29) +-------------------------------- + +- Added support for V8.x large object types: large REGION, PLINE MULTIPLE, + MULTIPOINT and COLLECTION (bug 1496) + +- Added support for v9.x Time and DateTime fields (based on patch from + AJD, bug 1754) + +- Added bounds for Peruvian coordinate systems PSAD56 in mitab_bounds.cpp + (came with patch from AJD sent for bug 1754) + +- Use %.15g instead of %.16g as number precision in .MIF output + +- Fixed the internal compressed coordinate range detection test to prevent + possible overflow of the compressed integer coordinate values leading + to some coordinate errors in very rare cases while writing to TAB (bug 1854) + + +Version 1.6.4 (2007-12-10) +-------------------------- + +- Upgrade of OGR and CPL libs to the version from GDAL/OGR 1.4.3 + +- Fixed leak of SRS in OGRGeometry (bug 1816) + +- Fix asDatumInfoList[] and asSpheroidInfoList[] defns/refs (bug 1826) + +- Added reporting access mode to error message in TABINDFile::Open() + (GDAL changeset r12460, ticket 1620) + +- Fixed leaks in ITABFeature???::Set???FromStyleString() (GDAL ticket 1696) + + +Version 1.6.3 (2007-10-12) +-------------------------- + +- Remove static variables that interfere with reentrancy. + http://trac.osgeo.org/gdal/ticket/1883 + + +Version 1.6.3-beta2 (2007-09-18) +-------------------------------- + +- Fixed another issue related to splitting of object blocks with the + optimized spatial index. The compressed coordinate origin values were + not initialized properly before being written (bug 1732) + + +Version 1.6.3-beta1 (2007-09-14) +-------------------------------- + +- Fixed the splitting of object blocks with the optimized spatial + index mode that was producing files with misaligned bytes that + confused MapInfo (bug 1732) + Note that even if this bug is (hopefully) fixed, the optimized spatial + index is still disabled by default in this release and we still use + the quick (i.e. traditional) spatial index mode by default. + +- Fixed problem with MIF parser being confused by special attribute + names (bug 1795) + + +Version 1.6.2 (2007-07-12) +-------------------------- + +*** *** *** *** *** +- IMPORTANT: The optimized spatial index introduced in V1.6.0 has been + disabled by default in this release after we found that the current block + splitting technique often produces files that confuse MapInfo (bug 1732). + The Quick Spatial Index Mode is used by default until bug 1732 is fixed. +*** *** *** *** *** + +- Fixed error reading compressed text objects introduced in v1.6.0 (bug 1722) + +- Fixed memory leaks when reading multipoint objects from .MIF files + +- Added IMapInfoFile::SetCharset() method (bug 1734) + +- Added TABFile::TwoPointLineAsPolyline() to allow writing two point lines + as polylines (bug 1735) + +- Added missing cast in isspace() calls to avoid failed assertion on Windows + (MITAB bug 1737, GDAL ticket 1678)) + +- Fixed memory leak when writing .TAB with new (optimized) spatial index + introduced in v1.6.0 (bug 1725) + +- Fixed another issue related to attempting to read past EOF while writing + collections (bug 1657) + +- Fixed a problem writing collections where collection objects were trashed + during object block splitting (bug 1728) + +- Fixed support for predefined datums with non-greenwich prime meridians per + http://trac.osgeo.org/gdal/ticket/1416 + +- Updated mitab_dyn.pas to support both Delphi and Kylix (contrib by + UffeK and MaximV, bug 1746) + +- Added support for building libmitab.so shared lib (contrib by UffeK and + MaximV, bug 1746) + +- Avoid deprecation warnings for some common functions with VC7+ (bug 1749) + + +Version 1.6.1 (2007-03-30) +-------------------------- + +- Added "Quick Spatial Index Mode" which generates a non-optimal spatial + index (similar to MITAB 1.5 and older) but results in faster write + time than the optimized index generated by v1.6.0+ (bug 1669) + (Thanks to Safe Software for funding this.) + +- Added mitab_c_set_quick_spatial_index_mode() to C API (bug 1669) + +- Fixed problem writing collections where MITAB was sometimes trying to + read past EOF in write mode (bug 1657). + +- Fixed another problem writing collections when the header of objects + part of a collection were split on multiple blocks (bug 1663) + +- Added missing NULL pointer checks in SetPenFromStyleString(), + SetBrushFromStyleString() and SetSymbolFromStyleString() (bug 1670) + + +Version 1.6.0 (2007-02-15) +-------------------------- + +- Improved generation of the .map's internal spatial index and object + data blocks to produce an optimal r-tree, resulting in performance + improvements of up to 30 times faster on some spatial searches, + matching the perfomance of files generated by MapInfo. (bug 1585) + (Thanks to Aircom International (Andrew Blake) for funding this.) + +- Fixed problem with null brush bg color becoming black when parsing + style strings (bug 1603) + +- Added mitab_is_field_indexed() and mitab_is_field_unique() to C API + (bug 1621) + + +Version 1.5.1 (2006-07-25) +-------------------------- + +- Fixed initialization of MBR of TABCollection members (bug 1520) + +- Added mapping for datum name "North_American_Datum_1927". + +- Fixed problem with uninitialized affine parameters written to .MAP header, + resulting in files not readable in MapInfo, introduced by bug 1155 in + MITAB 1.5.0 (bug 1254, 1319) + +- Fixed problem writing PLINE MULTIPLE to TAB format introduced in + MITAB 1.5.0 (bug 1466). + +- tab2tab: Set dataset bounds only after setting spatialref to prevent + default coordsys bounds from taking precedence when bounds are + available in source dataset (bug 1511) + +- Coordsys false easting and northing are in the units of the coordsys, not + necessarily meters. Adjusted mitab_coordsys.cpp to reflect this. + http://bugzilla.remotesensing.org/show_bug.cgi?id=1113 + +- Similar problem in mitab_spatialref.cpp for TAB files also fixed. + http://bugzilla.remotesensing.org/show_bug.cgi?id=1113 + + + +Version 1.5.0 (2006-02-16) +-------------------------- + +- Updated contrib/mitab_dyn.pas with new C API functions. Note that other + definition files for VB and .NET have *not* been updated for this release. + +- Fixed crash when attempting to write TABPolyline object with an invalid + geometry (GDAL bug 1059) + +- cast strstr() results as per email from Charles Savage for VC8. + +- Fixed build problem (link with -ldl not available) on MacOSX (bug 1167) + +- Fixed problem with white space parsing in mitab_miffile.cpp (bug GDAL:954) + +- layers with just regions can't be set as type wkbPolygon because they may + have multipolygons (bug GDAL:958) + http://bugzilla.remotesensing.org/show_bug.cgi?id=958 + +- Fixed support for multi character delimiters in .mid file (bug 1266) + +- Fixed reading Mills.mif (TABPoint::ReadGeometryFromMIFFFile()), the check + for SYMBOL didn't check if pszLine was non-NULL. + +- Fixed memory leak of m_pszDelimiter in mitab_middatafile.cpp. + +- Fixed OGR problems with setting bounds on MIF files, and wrong bounds + on tab files (bug 1198) + + +Version 1.5.0-beta1 (2005-10-07) +-------------------------------- + +- Added read/write support for MapInfo Collections in MIF and TAB (bug 1126) + (Thanks to funding from Comcast (Andy Canfield) and an initial patch and + analysis of collections by Jim Hope of Innogistic Software) + +- New C API methods to access collection members (bug 1126): + - mitab_c_get_collection_region_ref() + - mitab_c_get_collection_polyline_ref() + - mitab_c_get_collection_multipoint_ref() + - mitab_c_set_collection_region() + - mitab_c_set_collection_polyline() + - mitab_c_set_collection_multipoint() + +- Support for writing affine projection params in .MAP header (AJD, bug 1155) + +- New C API methods to get/set various projection parameters (AJD, bug 1155): + - mitab_c_get_projection_info() + - mitab_c_set_projection_info() + - mitab_c_get_datum_info() + - mitab_c_set_datum_info() + - mitab_c_get_affine_params() + - mitab_c_set_affine_params() + +- All datum definitions in mitab_spatialref.cpp now have the correct + names (Anthony D, bug 1155) + +- Added bounds entries for Finnish KKJ and Swedish projections (AJD, bug 1155) + +- mitab_ogr_datasource.cpp: avoid leak of CPLReadDirectory() result. + +- gcc4 related warnings in mitab_coordsys.cpp and mitab_utils.cpp avoided. + +- Change the way \n and \ are handled internally. Now they are stored + unescaped in memory and escaped only when writing MIF files. (bug 1107) + +- Improved handling of Danish modified TM proj#21-24 (HSS, bugs 976,1010) + +- Upgraded CPL and OGR source from latest GDAL CVS (as of 2005-05-12) + +- Produce a fatal error if creating an index of a size that exceeds the + .IND file format limitation (tree depth > 255) (OGR Bug 839) + +- Added mitab_c_set_symbol_angle() and mitab_c_get_symbol_angle() for + point symbols of type TABFC_FontPoint (bug 1002) + +- Added rule in cpl/GNUmakefile to copy cpl_config.h from cpl_config.h.in. (FW) + +- Changed to use OGRLayers spatial filtering support. (FW) + +- A few fixes to support for modified TM projections #21-24 (AJD, bug 1155) + +- Fixed missing initialization of default .MID file delimiter ("\t") + (Anthony D - bugs 1155 and 37) + + +Version 1.4.0 (2005-04-01) +-------------------------- + + - Added bounds entry to match datum 1011 based on MapInfo's "Svenska + rikssystemet, 2,5 gon väst (RT 90 7 parametrar)" (bug 997) + + - Added initial version of .NET bindings, contributed by Graham Sims in + mitab/contrib/dotnet directory (bug 992) + + - Added support for Datum Id field in .MAP header, this is new in MapInfo + 7.8+ and solves problem with multiple datums with same params (bug 910) + + - Fixed internal integer coordinates rounding error (bug 894) + + - Fixed potential memory leaks in error conditions in mitab_feature.cpp + and mitab_rawbinblock.cpp (bug 881) + + - Support pen width bigger than 7. Note that any pen bigger than 10 are + considered as point, not pixel. (bug 683) + + - Updated list of datum parameters from data made available by Bill Wemple + from MapInfo on directionsmag.com: 7 new datum defns and 1 fixed. + List of ellipsoids also updated (Bug 608, patch by Uffe K.) + + - Do not output a BG color in style string for transparent brushes (bug 693) + + - mitab_tabfile.cpp: Several fixes for TABFile::Open() when loading the + tabfile fails. (provided by Stephen Cheesman). + + - mitab_mapfile.cpp: Fixed error return value in LoadNextMatchingObjectBlock() + per http://bugzilla.remotesensing.org/show_bug.cgi?id=615. NFW + + - mitab_ogr_datasource.cpp: Modified CreateLayer() to use + -1000,-1000,1000,1000 bounds for GEOGCS much like in mitab_bounds.cpp. + This ensures that geographic files in the range 0-360 works as well as + -180 to 180. + + +Version 1.3.0 (2004-07-07) +-------------------------- + + - Use tab as default delimiter (if none is explicitly specified) when + reading .MIF/.MID files (Anthony D, bug 37) We still generate new files + using comma as delimiter. + + - C API: Added mitab_c_get_feature_count() and mitab_c_get_field_as_double(), + added ability to create a feature of type TABFC_NoGeom (Anthony D) + + - Added support for reading affine coordsys parameters in extended + .map header block of v500+ (Anthony D, bug 41) + + - Added mitab_c_getlibversion() to C API. (Uffe K. - bug 21) + + - Write 3D geometries to MIF/tab as if they were 2D. NFW + + - Flush out .mif header on Close() if no features written. NFW + + - Fixed previous flushing logic to only do so for writable files. NFW + + - Removed obsolete mif2mif.cpp mif2tab.cpp mif2tabbatch.cpp. They are + all replaced by the more up to date tab2tab.cpp + + - Modified the OGR creation logic to properly support creating single + files sets rather than directories full of files. NFW + + - Fixed memory leaks with seamless tab files (bug 283) + + - Call CPLReadLine(NULL) in MIDDATAFile::Close() to cleanup working buffer. + + - Removed special bailout for m_poMAPFile == NULL in TABFile::Close(). + It is important to cleanup other used memory. + + - mitab_coordsys.cpp: fixed small memory leak for non-earth coordinate + systems. (bug supplied by Stephen Cheeseman, Geosoft). + + - Added mitab_c_load_coordsys_table() to C API (bug 469) + + - mitab_ogr_datasource.cpp: Don't report single layer that hasn't been + "created" yet via GetLayerCount(). Fixes up single file generation issues. + + +Version 1.2.4 (2003-07-23) +-------------------------- + + - Added 'indexed' and 'unique' parameters to mitab_c_add_field(). + Applications written for older versions of the C API will have to + be updated to pass these two new parameters. + + - Ensure that a blank line in a mid file is treated as one field containing + an empty string instead of being zero fields. NFW + + - Fixes to support mif/mid files produced by Geomedia V5 (bug 1631): + - Do not produce an error if .mid data records end with a stray ',' + - Treat tabs (\t) as a blank space delimiter when reading coordinate + values in .mif file + + - Fixed sign of rotation parameters in TOWGS84 parameters in WKT. MapInfo + uses EPSG 9607's 7 parameter datum shift method, but TOWGS84 is defined + to hold EPSG 9606 style parameters. Only the rotational signs differ. NFW + + - Fixed spelling of Provisional_South_American_Datum_1956 in datum list. NFW + + - Fixed bug parsing ellipsoid out of CoordSys strings for coordinate + systems with datum 999 or 9999. NFW + + - Fixed leak of TABMAPObjHdr when writing NONE geometries in SetFeature(). + + - Increased MIF reader line buffer size to 10000 chars + + - Added support for writing TABFC_NoGeom (NONE) geometries via the C API. + + - Fixed crash when reading .mif file that doesn't contain a DATA line + (Thanks to Riccardo Cohen for the fix). + + - Implement DeleteDataSource() method on the driver object. NFW + + - mitab_spatialref.cpp, mitab_coodsys.cpp: Fixed up regional mercator logic. + It was screwing up transverse mercator with non-zero latitude when writing + to tab, and wasn't being written properly to .mif. NFW + + - mitab_feature.cpp: fixed C++ style true/false where TRUE/FALSE required. + As per GDAL bugzilla 311. NFW + + - Fixed problem scanning node in TABINDNode::FindNext() - bug 2176, FW + + +Version 1.2.3 - (2002-10-15) +---------------------------- + +- Support auto-addition of a dummy "FID" column if TAB files are created + with no fields, instead of producing an error. A corresponding change + has not yet been applied to the MIF driver. NFW + +- Fixed lots of places that check for errors by examining CPLGetLastErrorNo(). + A warning will trigger all these places as an error. Changed to check that + the last error type was CE_Failure and add a CPLErrorReset() at the + beginning of many affected sections. NFW + +- Modified TABGetBasename() (in mitab_utils.cpp) to stop at the first + forward or backslash otherwise "C:/warmerda/mi\can_caps.tab" will result + in a basename of "mi\can_caps.tab". NFW + +- Modified datum list in mitab_spatialref.cpp with a bunch of new EPSG/OGC + datum names provided by Siro Martello from Cadcorp. NFW + +- Fix memory leak of m_pszIndex in mitab_miffile.cpp, the poSpatialRef + in TABFile::SetMIFCoordSys(), and a leak in MITABExtractCoordSysBounds(). NFW + +- Added untested support in mitab_spatialref.cpp, and mitab_coordsys.cpp for + projections Regional Mercator (26), Polyconic (27), Azimuthal Equidistant - + All origin latitudes (28), and Lambert Azimuthal Equal Area - any aspect + (29). NFW + + +Version 1.2.2 - (2002-07-06) +---------------------------- + +- Added a validation in TABMAPFile::GetFeatureId(). When there's no .map file + or no spatial indexes or no geometry, the function return -1 like an Eof. + This prevent the reading of a file set to NULL. (bug 169 (MapServer)) + +- Treat Region as MultiPolygon in mitab_c_set_point() to support Regions + with multiple islands and holes. Passing a part number <= 0 (second + parameter in mitab_c_set_point()) will result in adding a new island + (i.e. polygon). By default, parts > 1 are treated as holes in the + last island (polygon) that was created. + +- Added IsInteriorRing() function in the TABRegion class to validate if a ring + in a polygon is an external or an internal ring. The function returns + true if the specified ring is an interior ring. The function + mitab_c_is_interior_ring() in mitab_capi.c has been created too to call + IsInteriorRing() if the feature is a region. + +- In mitab_mapheaderblock.cpp, the Coordsys2Int() function used integer values + to check for overflow. However, in some case, integer was to small and caused + problems with applications that called SetSpatialFilter() with a filter + much larger than the file's bounds. So, this function now uses doubles + internally for comparison, but still returns and receives integers. + +- Add SetSpatialFilter function in the TABSeamless class, to use seamless + layer in the right way. Now, the filter is applied on each data file and + on the index file. (Bug 164 (MapServer)) + + +Version 1.2.1 - (2002-05-08) +---------------------------- + +- Implement SetMIFCoordSys in the MIFFile class. The coordsys was not + written when calling mitab_c_create for MIF files because + MIFFile::SetMIFCoordSys always returned -1 (Bug 984). + +- Added support for TABFC_FontPoint and TABFC_CustomPoint in + mitab_c_create_feature(), mitab_c_set_points(), mitab_c_set_font() and + mitab_c_get_font(). + +- mitab_capi.h: Use stdcall by default for all public mitab.dll functions + instead of cdecl. Should still work fine for C/C++ users, and will + allow VB/Delphi users to use the same DLL with Bo T.'s interface files. + + +Version 1.2.0 - (2002-05-03) +---------------------------- + +- Made changes in TABRelation to avoid an infinite loop. The m_poCurFeature + object have been deleted and the function GetFeatureRef() have been replace + by GetFeature(). This new function return a TABFeature object that must be + control by the calling method. (bug 706) + +- Made important changes in order to support writing objects in 16 bits + coordinate format. New TABMAPObjHdr-derived classes are used to hold + object info in memory until block is full. + This was required in order to fix a problem when very small regions + were written in 32 bits coordinates when 16 bits should have been used + because MBR < 65536. In this case MapInfo would attempt to rewrite the + modified object info in 16 bits format while the coordinates blocks are + still in 32 bits format... resulting in a corrupted the file. + Note that at this point only feature types that use separate coordinate + blocks can be saved in 16 bits format (PLINE, MULTIPLINE and REGION). + +- Prevent writing of coordinates outside of the +/-1e9 integer bounds. + If coordinates outside of that range are written then the values are + written as +/-1e9 instead and a warning (ERROR 503) is produced when + the file is closed. Coordinates outside of the +/-1e9 bounds limits + seemed to confuse MapInfo. + +- Disabled the warning (ERROR 503) when coordinates outside of the +/-1e9 + integer bounds in SetCoordFilter(). + +- Modified the conversion of double to integer coordinates to fix an old + problem where coordinates written by MITAB would be a little bit off + sometimes compared with the same values written by MapInfo. + +- Modified mitab_capi.cpp to correctly produce "point" style arcs from + mitab_c_set_arc(). It now sets a point geometry ensuring that the + TABArc::ValidateMapInfoType() method will succeed. As per DMSG Bug 644. NFW + +- Fixed some TABView issues (bug 703): + - Support "select * ..." syntax + - Accept source table names with or without the .tab extension + - Avoid crash if .IND file for related table is missing (i.e. deleted) + +- The OGRDriver Create method will now accept the FORMAT=MIF option to + create a dataset of mif files instead of tab files. NFW + +- Prevent an infinite loop of calls to LoadNextMatchingObjectBlock() in + GetNextFeatureId() if no objects found in spatial index when using a + spatial filter. + +- False Easting/Northing should be in the linear units of measure in MapInfo, + but in OGRSpatialReference/WKT they are always in meters. Convert + accordingly in mitab_coordsys.cpp and mitab_spatialref.cpp. NFW + +- Added support for reading and writing new V650 Multipoint for both TAB and + MIF. + +- Added support for Cassini Soldner projection (projection 30). NFW + +- Add EOF validation in MIDDATAFile::GetLastLine() to fix EOF problems + while reading some MIF/MID files (Bug 819, JSL) + +- New VB, Pascal, and MapBasic interfaces contributed by Bo Thomsen. + See contrib/README_VB.TXT + +- Added to C API: mitab_c_get_field_width(), mitab_c_get_field_precision() + + +Version 1.1.3 - (2001-11-02) +---------------------------- + +- mitab_tabview.cpp: Use VSIUnlink() instead of unlink(). NFW. + +- mitab_utils.cpp: Don't use multi-byte support if _WIN32 and unix are + defined to try and preserve cygwin support. NFW + +- C API: Added mitab_c_get_text(). + Added mitab_c_get_mif_coordsys() + Added mitab_c_get_projinfo() and mitab_c_set_projinfo() + Changed mitab_c_create() to make bounds optional and allow using + default projection bounds if available + +- GetLabelStyleString(): take line spacing and num. of lines into account + when calculating text height. + +- Test for NULL geometries if spatial filter enabled in GetNextFeature() + (objects with NONE geometry would have caused a crash when a spatial + filter was set). + +- Fixed a few memory leaks in mitab_miffile.cpp. NFW. + +- Fixed mitab_capi.c to not delete the spatial ref unless dereferencing it + drops the count to 0. NFW. + +- Modified TABFile::SetSpatialRef() to clone the passed in OGRSpatialReference + instead of taking a reference to it, so it will work with stack allocated + OGRSpatialReferences or in cases where the caller doesn't check ref counts. + NFW. + +- TABPolyline::ValidateMapInfoType(): return TAB_GEOM_NONE if numpoints < 2 + +- Substantial additions to lots of files adding support for efficient + spatial queries by utilizing the spatial indexes when reading. NFW. + +- TABText should not produce an error if reading a 0-length text. + +- Seamless files: when reading on Unix, replace '\\' with '/' in file path + read from the index table. + +- Modified mitab_miffile.cpp to keep track of the feature id of the + preloaded text line in m_nPreloadedId instead of m_nCurFeatureId to fix + serious problems with access through IMapInfo::GetNextFeature() - this + affects applications using the pure OGRLayer API to access mif files. NFW + +- mitab_middatafile.cpp: Use VSIRewind() instead of rewind() to ensure that + IO remains virtualized. NFW + +- mitab_miffile.cpp: modified to return extents collected by PreParse. Made + distinction between extents and projection bounds explicit. NFW + +- mitab_spatialref.cpp: added OGC name for datum 12. + + +Version 1.1.2 - (2001-06-24) +---------------------------- + +- Support reading and writing TEXT objects with strings longer than 512 + bytes. MITABMAPCoordBlock's Read/WriteBytes() had to be fixed to + allow reading/writing data split over more than 2 blocks. + +- StyleString fixes: include font name in text style string, and placed + brush before pen in region style strings. + +- C API: added get methods for all pen, brush, font and symbol properties. + +- Fixed MIF Text object output: negative text angles were lost. Also use + TABText::SetTextAngle() when reading MIF instead of setting class members + directly so that negative angles get converted to the [0..360] range. + + +Version 1.1.1 - (2001-05-01) +---------------------------- + +- Added implementation for OGRLayer::GetExtent() that returns extents + of the data, which is differents from the bounds of the projection. + +- Added code to set TOWGS84 values for mitab_coordsys.cpp and + mitab_spatialref.cpp. + + See http://bugzilla.remotesensing.org/show_bug.cgi?id=41 + +- Fixed serious bug in TABFile::SetSpatialRef() (mitab_spatialref.cpp). A + core dump could occur for type 999 and 9999 datums. Thanks to Stephen + Cheesman of Geosoft for finding and pointing this out. (NFW) + +- Modified TABINDFile/TABINDNode to support update (i.e. read/write) access + and created 'tabindex' program to test creation of indexes on existing + datasets. + +- Modified reading of TABRegion to maintain outside/inside ring relationship + when it's available in the source file. + +- Fixed default for BRUSH when brush index=0 in the source file. It should + be BRUSH(1,0,16777215) and not BRUSH(2,16777215,16777215). + + +Version 1.1.0 - (2001-03-08) +---------------------------- + +- Modified mitab_imapinfofile.cpp so that variable width fields (nWidth=0) + is translated to 254 characters, instead of 255 so it will actually be + a legal field width in TAB. (NFW) + +- Make default and max char field with 254 chars in TABFile::AddFieldNative() + +- Added a warning (TAB_WarningBoundsOverflow) on close of .MAP file if any + object was written with coordinates outside of the file's predefined + bounds. (Causing an overflow of the +/-1e9 integer coord. range) + +- Use MI defaults for Pen, Brush, Font, Symbol when creating new objects + and when reading objects with no Pen, Brush, etc. set (used to just set + everything to zero which was inconsistent with MI's behavior) + New defaults are: PEN(1,2,0), BRUSH(2,16777215,16777215), + FONT("Arial",0,0,0), SYMBOL(35,0,12) + +- Accept "TABLE TYPE LINKED" and handle them the same way as type NATIVE + i.e. the link to the RDBMS is simply ignored. + +- Added support for Swiss Oblique Mercator/Cylindrical. (NFW) + +- Expand tabs (NFW) + +- Added projection bounds lookup tables, automatically called by + TABFile::SetProjInfo() (and SetSpatialRef() by the same way). + TABFile::IsBoundsSet() can be used after setting projection to + establish if some default bounds were found or not for the projection. + +- Fixed IMapInfoFile::CreateFeature(), was leaking feature memory. (NFW) + +- Fixed small memory leak in OGRTABDataSource::CreateLayer() (NFW) + +- Support SpatialFilter inside IMapInfoFile::GetNextFeature(). + TAB files should support spatial filter via their spatial index for + optimal performance. + +- Avoid unnecessary cloning of features in IMapInfoFile::GetNextFeature() and + IMapInfoFile::GetFeature(). + +- Added TABText::Get/SetTextLineEndPoint() and properly read/write label + line end point in TAB files. + +- Fixed writing MIF header: for decimal fields, an extra field of type + logical with same name was always added because of a missing break; (Kieron) + +- Added support for reading seamless TAB files (TABSeamless class) + +- Fixed problem creating new files with mixed case extensions (e.g. ".Tab") + + +Version 1.0.4 - (2000-11-14) +---------------------------- + +- Fixed a problem writing indexes - When a new entry was inserted at the + beginning of an index node (changing this node's key) then the parent was + not properly updated and some records were not queriable through that index. + +- Handle '\t' just like a space when parsing mif files. + +- Fixed MIFFile::GotoFeature() to avoid calling ResetReading() before moving + forward. + +- Fixed writing of drawing tool blocks - when more than 512 bytes worth of + drawing tool info had to be written, an error was produced instead of + automatically allocating a new block, resulting in a truncated file. + + +Version 1.0.3 - (2000-10-19) +---------------------------- + +- Fixed reading of MAPCoordBlocks to accept text strings that overlap on + 2 data blocks. + +- Added graceful recovery from NULL OGRSpatialReference pointers in + mitab_capi.cpp, and mitab_spatialref.cpp (NFW). + +- Added what I hope to be working support for NonEarth translation + (into LOCAL_CS in WKT format) (NFW). + +- Initialize m_dWidth to 0 in TABText constructor. (NFW) + +- Made TABFile::GetBounds() return bounds corresponding to the +/- 1e9 + integer coordinate limits instead of using min/max int. coord. from header + + +Version 1.0.2 - (2000-10-03) +---------------------------- + +- Avoid warnings building with gcc -Wall -O2 (NFW). + +- Modified generation of .MAP spatial index to generate a balanced tree + instead of the old 'chained list of nodes' that exceeded the 255 levels + limit on the tree depth with files with > 100,000 objects. + There would still be room for improvement by implementing splitting of + object data blocks. + +- Handle m_nOriginQuadrant value of 0 the same way as quadrant 3 + +- Added bDeletedFlag to TABFeature with get/set methods + +- Maintain and/or compute values of regions and polyline center/label point + (Used to always use the center of the MBR and this was not correct) + +- Fixes for old Version 100 files: + - Set valid Scale/displacement when reading V100 .map header. + Use m_nCoordPrecision to define scale since scale in header is 0. + - Accept "FORMAT: DBF" in .tab header file. + +- Made all open() methods completely case-insensitive on filenames on Unix + (added TABAdjustCaseSensitiveFilename()) + +- Added new MapInfo 6.0 datums in datum list. + +- Added OGR Feature Style String support when reading + +- Added support for writing arcs, ellipses and rectangles using the C API + +- Flushed tab2mif.cpp and mif2tab.cpp. There is now a single tab2tab.cpp + conversion program that can generate both TAB or MIF output, depending + on output filename extension + +- Added #define MITAB_VERSION for apps that want to report library version. + + +Version 1.0.1 - (2000-04-21) +---------------------------- + +- Added C API documentation (mitab_capi.cpp) + functions to read vertices + and field information and included C API in public release. + +- Modified fltering of new table field name to accept extended chars with + accents. + + +Version 1.0 - (2000-03-26) +-------------------------- + +- Changed to use same MIT/X-Consortium license as other CPL + OGR libs. + +- Fixed problem opening datasets with 0 features in them. + +- Added support for reading TAB datasets with "Table Type DBF"... only + type "NATIVE" was supported until now. + +- Produce only a warning when unsupported feature types encountered in the + .MAP file, and return a valid feature with a NONE geometry. + +- Done more tests (and required changes) to fix the way the quadrant + setting affects the way to read arc angles... hopefully it's right + this time! + +- TABText objects now return their text string with any "\n" escaped on + 2 chars (i.e. "\"+"n"). TABText::SetText() also expects newlines to + be escaped in the string it receives. + +- Fixed some projection problems, including possible crashes with some + 9999 datums with big parameter values or with some unrecognized projections. + +- Outside/inside ring relationship from a TABRegion's OGRPolygon is now + properly written to the .MAP file's coord block section header. + Prior to MapInfo 5.0, this info was ignored, but now it has to be valid + otherwise some parts of a region won't be displayed properly in MI5.0 + +- Added support to write indexed fields and create joined tables (TABView). + +- Added validation on field names (no special chars) on write and + produce a warning if name had to be fixed. + +- Added support for V450 object types with num_points > 32767 and for + pen width in points. + This also resulted in changes to the ITABFeaturePen methods, mainly the + Get/SetPenStyle() method has been removed since this style value was + actually a part of the point width value. + Finally, the 'style' parameter was also removed in the + mitab_c_set_pen() C API method. + +- Support for Version 450 objects forced creation of version 500 .MAP files + with 1024 bytes header (.MAP version 400 were produced before). + +- MIF format now reads and writes bounds in coordsys (used to ignore them) + + +Version 0.2 - (1999-12-20) +-------------------------- + + - Added TABView class to support views on pairs of .TAB files (read-only) + - Currently supports only 2 tables with a many-to-1 relation through + an integer field. + + - Added GetFeatureCountByType() method. Current implementation does + not work (returns all zeros) for MIF files + + - Added static ImapInfoFile::SmartOpen() to automatically detect file type, + open a new file for read and return a new object of the appropriate + type (TABFile/TABView/MIFFile) to handle it. + + - Changed TABFile::Open(), MIFFile::Open() to use a bTestOpenNoError flag + + - OGRMIDDataSource/OGRMIDDriver were removed. Now all file types + handled by MITAB go through the OGRTABDataSource/OGRTABDriver interface + + - Fixed problem reading/writing dates (bytes were reversed) + Also, Dates are now returned as "YYYYMMDD", and accepted as one + of "YYYY/MM/DD", "DD/MM/YYYY", or "YYYYMMDD" + + - Fixed TABFile::GetBounds() that could sometimes return Min values > Max + + - Rounded rectangles with radius bigger than MBR: we used to return a + corrected radius value, but it's now changed to return the real radius + value (even if it's too big) since this is what MapInfo appears to do + when exporting from TAB to MIF + + - Fixed some problems that had appeared following a change in OGR's + addGeometry()... use the new addGeometryDirectly() + more error checking. + + - TABRegions geometry now returned as OGRMultiPolygon instead of OGRPolygon + with multiple rings. This change had to be done because OGRPolygons + MUST have only one outer ring with a set of inner rings, and this + cannot be guaranteed to be the case with a MapInfo file. + Also added TABRegion::GetNumRings() and TABRegion::GetRing() to + make it simpler to manipulate the complex geometry as if it was a + simple collection of rings which is actually what we have in the + MapInfo model. + + - Fixed some problems with MIF generation of Float fields and PLINE MULTIPLE + + - Run a bunch of read/write tests for both .TAB and .MIF through Purify... + fixed all memory leaks found. + +Version 0.1 - (1999-12-04) +-------------------------- + + - First official pre-release version + + - Still lacks good API docs. + + +------------ +$Id: HISTORY.TXT,v 1.223 2008/11/17 22:06:20 aboudreault Exp $ +------------ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/README.TXT b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/README.TXT new file mode 100644 index 000000000..6823244bd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/README.TXT @@ -0,0 +1,235 @@ + MITAB Library overview + ====================== + +Note: until there is good complete documentation for the MITAB library, +this README file is the only source of information for integrating the +MITAB library in your application. + +Please also visit the following URLs: + + - The library's web page: + http://mitab.maptools.org/ + + - The OGR architecture documentation: + http://ogr.maptools.org/ogr_arch.html + + +COPYRIGHT AND LICENSE TERMS: +---------------------------- + +The most part of the MITAB library is + Copyright (c) 1998-2005, Daniel Morissette (morissette@dmsolutions.ca) +it also contains parts and uses support libraries that are + Copyright (c) 1998-2005, Frank Warmerdam (warmerdam@pobox.com) +and + Copyright (c) 1999, 2000, Stephane Villeneuve (stephane.v@videotron.ca) + +The MITAB library, and its supporting libraries (OGR and CPL) are freely +available under the following OpenSource license terms: + + ********************************************************************** + * Copyright (c) 1998-2005, Daniel Morissette + * Copyright (c) 1998-2005, Frank Warmerdam + * Copyright (c) 1999,2000, Stephane Villeneuve + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + + +USING THE TAB2TAB CONVERSION PROGRAM: +------------------------------------- + +MITAB comes with a conversion program called TAB2TAB that can do tab-to-mif +and mif-to-tab translations: + +Usage: tab2tab + Converts TAB or MIF file to TAB or MIF format. + The extension of (.tab or .mif) defines the output format. + + +COMPILING THE LIBRARY: +---------------------- + +When you extract the ZIP (or .tar.gz) file, you will get 3 directories +and the makefiles to compile the library using VC++ 6 under Windows or +using GNU make and GCC on Unix. + +The MITAB directory contains the core of the library and the TAB2TAB +conversion program. +The OGR and CPL directories are support libraries used by MITAB and probably +won't be of much interest to you at the beginning. + +To compile the lib on Windows: + In a DOS prompt, setup the VC++ environment variables by executing + VCVARS32.BAT (somewhere in your VC++ install) and start the build + using: + nmake -f makefile.vc + + This should automagically compile the 3 sub-directories and the test + program (tab2tab.exe, tabdump.exe, mitabc_test.exe) in the MITAB directory. + + If you plan to use MITAB from Visual Basic or non-C environments, then + have a look at contrib/README_VB.TXT + +To compile the library on Unix: + + The main directory contains a GNUmakefile whose default target will + compile the contents of the 3 sub-directories and the test programs + in the mitab directory. + + Note about byte ordering: by default, the library is built for systems + with LSB first (Intel) byte ordering. To build the library on systems + that use MSB first byte ordering (such as SUN systems), you should add + the "-DCPL_MSB" flag to the compile flags in the "GNUmake.opt" file. + + +USING THE LIBRARY IN YOUR PROGRAMS: +----------------------------------- + +There are 2 interfaces to access the library: + +1- The C API. The C API is a simplified interface which allows you to + build simple applications quickly but may not give you access to every + property of every object type. + See the C API documentation on http://mitab.maptools.org/ + There are also interface definition files for various environments + in the contrib directory (mainly VB, Pascal and MapBasic... thanks to + Bo Thomsen) + +2- The C++ API will give you full access to every feature of the MapInfo + data model. Unfortunately there is no complete documentation for the + library's C++ classes yet, but tab2tab.cpp is a good example that + shows how to open TAB files for read and write. Also, the main + classes you will need to deal with live in the header MITAB.H. + + +The rest of this file covers mostly the C++ API features but may also apply +to the C API indirectly. + + +TO READ FILES: +-------------- + +To open a .TAB or .MIF file for read, you can use the static method: + + IMapInfoFile *IMapInfoFile::SmartOpen(const char *pszFname, + GBool bTestOpenNoError); + +This function returns NULL if the file cannot be opened. If the open +was succesful, then it returns a new object of the type corresponding +to the type of file that was opened: + + class TABFile: Class to handle .TAB datasets for read/write access. + Note that you cannot use it to modify exsiting datasets + (not yet!). + + class TABView: Class to handle views on pairs of .TAB files linked + through an indexed field of type integer. + + class MIFFile: Class to handle MIF files for read/write. + + +The method IMapInfoFile::GetFileClass() can be used to establish the +type of object that has been returned by SmartOpen(). + + +TO CREATE NEW FILES: +-------------------- + +For write access, you create an instance of TABFile or MIFFile and use +it to open the new file and write to it. Note that only sequential write is +supported. + + +FEATURE CLASSES: +---------------- + +On read access, GetFeatureRef() returns object of classes derived from +class TABFeature. You can tell the type of an object using the method +TABFeature::GetFeatureClass(). + +The following table lists the various feature types and the geometry +types that can be returned and that are accepted (for writing) by each +of them: + + Feature Type Returns (read mode) Accepts (write mode) + ------------ ------------------- -------------------- + + TABPoint OGRPoint OGRPoint + + TABFontPoint OGRPoint OGRPoint + + TABCustomPoint OGRPoint OGRPoint + + TABPolyline OGRLineString or OGRLineString or + OGRMultilineString OGRMultilineString + + TABRegion OGRPolygon (with a OGRPolygon (with 1 or more + single ring) or rings) or OGRMultiPolygon + OGRMultiPolygon (for + multiple rings) + + TABRectangle OGRPolygon OGRPolygon + + TABEllipse OGRPolygon OGRPolygon or + OGRPoint corresponding to the + ellipse center + + TABArc OGRLineString OGRLineString or + OGRPoint corresponding to the + arc's defining ellipse center + + TABText OGRPoint (the lower- OGRPoint + left corner of the + text) + + TABMultiPoint OGRMultiPoint OGRMultiPoint + + TABCollection OGRCollection with In write mode, the geometry + 3 optional components: cannot be set directly as an + 1- 0 or 1 OGRPolygon OGRGeometry. The following methods + or OGRMultiPolygon must be used instead: + 2- 0 or 1 OGRLineString SetRegionDirectly() + or OGRMultiLineString SetPolylineDirectly() + 3- 0 or 1 OGRMultiPoint SetMultiPointDirectly() + + +NOTE ABOUT "\n" IN STRINGS ON TABText OBJECTS: +---------------------------------------------- + +The text strings on text object may contain embedded "\n" characters in +MapInfo. + +In those cases, the library returns strings in which the "\n" character +is escaped on two characters: "\" + "n" + +The library will also automatically convert any "\"+"n" sequence in +strings into a binary "\n" when it writes text objects to TAB files. + +This also implies that a single "\" character also has to be converted +to "\"+"\". + +Also note that this conversion does not apply to table fields of type +"Char" since there appears to be no special treatment in MapInfo for +the "\n" sequence in char attribute field values. + + +------------ +$Id: README.TXT,v 1.12 2005/10/07 14:00:07 dmorissette Exp $ +------------ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/TODO.TXT b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/TODO.TXT new file mode 100644 index 000000000..de6e846ab --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/TODO.TXT @@ -0,0 +1,39 @@ + +MITAB Library - ToDo, Open Questions: +------------------------------------- + + - API Documentation + + - MIF output: bounds are missing in Coordsys NonEarth + + - ??? Produce a Warning when bounds not set on first SetFeature() + + - Support MetaData in .TAB header + + - TABView support: + - Complete and tested only for cases in which 2 tables + are linked with a many-to-1 relation through an integer field. + - The merge operation involves 3 features in memory and cloning the + geometry... this could perhaps be optimized but would require changes + at the lower-level in the lib. + - What is the proper behavior when no record in RelTable for a feature + from MainTable? For now we leave fields unset + + - "Table Type DBF" are supported for reading, but no charset conversion + is done... however, when the same datasets are exported to MIF by MapInfo + it apparently converts the DBF file's charset (e.g. CodePage863) to + the dataset charset (e.g. WindowsLatin1) + + - Will we ever want to support Table Type ACCESS? + + - When regions with multiple rings are read from MIF, the inside/outside + ring relationship is not reconstructed. This may cause display problems + when MIF files containing regions are converted to TAB: it has been + reported that some regions may not be filled properly when viewed in + MapInfo Versions 5.0 and up when the inside/outside ring relationship + is not properly written. + + +------------ +$Id: TODO.TXT 11171 2007-04-02 19:04:13Z dmorissette $ +------------ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/drv_mitab.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/drv_mitab.html new file mode 100644 index 000000000..05e81a6a1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/drv_mitab.html @@ -0,0 +1,148 @@ + + + + +MapInfo TAB and MIF/MID + + + + +

    MapInfo TAB and MIF/MID

    + +

    +MapInfo datasets in native (TAB) format and in interchange (MIF/MID) format +are supported for reading and writing. Starting with GDAL 2.0, update of +existing TAB files is supported (append of new features, modifications and +deletions of existing features, adding/renaming/deleting fields, ...). +Update of existing MIF/MID files is not supported. +

    + +

    +Note: In the rest of this document "MIF/MID File" is used to refer to a +pair of .MIF + .MID files, and "TAB file" refers to the set of files for a +MapInfo table in binary form (usually with extensions .TAB, .DAT, .MAP, .ID, +.IND). +

    + +

    +The MapInfo driver treats a whole directory of files as a dataset, and +a single file within that directory as a layer. In this case the directory +name should be used as the dataset name. +

    + +

    +However, it is also possible to use one of the files (.tab or .mif) in a +MapInfo set as the dataset name, and then it will be treated as a dataset +with one single layer. +

    + +

    +MapInfo coordinate system information is supported for reading and writing. +

    + + +

    Creation Issues

    + +

    +The TAB File format requires that the bounds (geographical extents) of a new +file be set before writing the first feature. +

    + +

    +There is currently no automated setting of valid default bounds for +each spatial reference system, so for the time being, the MapInfo driver sets the following +default bounds when a new layer is created: +

    +
      +
    • For a file in LAT/LON (geographic) coordinates: BOUNDS (-180, -90) (180, 90)
    • +
    • For any other projection: BOUNDS (-30000000, -15000000) (30000000, 15000000)
    • +
    + +

    +Starting with GDAL 2.0, it is possible to override those bounds through two +mechanisms. +

      +
    • specify a user-defined file that contain projection definitions with bounds. +The name of this file must be specified with the MITAB_BOUNDS_FILE configuration +option. +This allows users to override the +default bounds for existing projections, and to define bounds for new projections +not listed in the hard-coded table in the driver. +The format of the file is a simple text file with one CoordSys string +per line. The CoordSys lines should follow the MIF specs, and MUST +include the optional Bounds definition at the end of the line, e.g. +
      +# Lambert 93 French bounds
      +CoordSys Earth Projection 3, 33, "m", 3, 46.5, 44, 49.00000000002, 700000, 6600000 Bounds (75000, 6000000) (1275000, 7200000)
      +
      + +It is also possible to establish a mapping between a source CoordSys and a +target CoordSys with bounds. Such a mapping is specified by adding a line starting +with "Source = " followed by a CoordSys (spaces before or after the equal sign +do not matter). The following line should start with "Destination = " +followed by a CoordSys with bounds, e.g. +
      +# Map generic Lambert 93 to French Lambert 93, Europe bounds
      +Source      = CoordSys Earth Projection 3, 33, "m", 3, 46.5, 44, 49, 700000, 6600000
      +Destination = CoordSys Earth Projection 3, 33, "m", 3, 46.5, 44, 49.00000000001, 700000, 6600000 Bounds (-792421, 5278231) (3520778, 9741029)
      +
      + +
    • +
    • use the BOUNDS layer creation option (see below)
    • +
    +

    + +

    +If no coordinate system is provided when creating a layer, the projection +case is used, not geographic, which can result in very low precision if +the coordinates really are geographic. You can add "-a_srs WGS84" to the +ogr2ogr commandline during a translation to force geographic mode. +

    + +

    +MapInfo feature attributes suffer a number of limitations: +

    +
      +
    • Only Integer, Real and String field types can be created. The various +list, and binary field types cannot be created.
    • +
    • For String fields, the field width is used to establish storage size in +the .dat file. This means that strings longer than the field width will be +truncated
    • +
    • String fields without an assigned width are treated as 254 characters.
    • +
    + +

    Dataset Creation Options

    + +
      +
    • FORMAT=MIF: To create MIF/MID instead of TAB files (TAB is the default).
    • +
    • SPATIAL_INDEX_MODE=QUICK/OPTIMIZED: The default is QUICK force +"quick spatial index mode". In this mode writing files can be about 5 times faster, but +spatial queries can be up to 30 times slower. This can be set to OPTIMIZED in +GDAL 2.0 to generate optimized spatial index.
    • +
    + +

    Layer Creation Options

    + +
      +
    • BOUNDS=xmin,ymin,xmax,ymax: (GDAL >=2.0) Define custom layer bounds +to increase the accuracy of the coordinates. Note: the geometry of written features +must be within the defined box.
    • +
    + +

    Compatability

    + +

    +Before v1.8.0 , the driver was incorrectly using a "." as the delimiter for id: +parameters and starting with v1.8.0 the driver uses a comma as the delimiter +was per the OGR Feature Style Specification. +

    +

    See Also

    + + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/makefile.vc new file mode 100644 index 000000000..950bcdd07 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/makefile.vc @@ -0,0 +1,32 @@ +# +# makefile.vc - MapInfo TAB Read/Write library makefile +# +# To use the makefile: +# - Open a DOS prompt window +# - Run the VCVARS32.BAT script to initialize the VC++ environment variables +# - Start the build with: nmake /f makefile.vc +# +# $Id: makefile.vc 15758 2008-11-18 18:02:14Z dmorissette $ +# + +OBJ = mitab_rawbinblock.obj mitab_mapheaderblock.obj \ + mitab_mapindexblock.obj mitab_indfile.obj \ + mitab_tabview.obj mitab_bounds.obj \ + mitab_mapobjectblock.obj mitab_mapcoordblock.obj \ + mitab_feature.obj mitab_feature_mif.obj \ + mitab_mapfile.obj mitab_idfile.obj mitab_datfile.obj \ + mitab_tabfile.obj mitab_miffile.obj \ + mitab_utils.obj mitab_imapinfofile.obj mitab_middatafile.obj \ + mitab_maptoolblock.obj mitab_coordsys.obj \ + mitab_tooldef.obj mitab_spatialref.obj mitab_ogr_driver.obj \ + mitab_ogr_datasource.obj mitab_geometry.obj \ + mitab_tabseamless.obj + +EXTRAFLAGS = -I.. -I..\.. -DOGR -DMITAB_USE_OFTDATETIME +GDAL_ROOT = ..\..\.. +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab.h new file mode 100644 index 000000000..73c9a2885 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab.h @@ -0,0 +1,1908 @@ +/********************************************************************** + * $Id: mitab.h,v 1.121 2010-10-08 18:38:13 aboudreault Exp $ + * + * Name: mitab.h + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Header file containing public definitions for the library. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2005, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab.h,v $ + * Revision 1.121 2010-10-08 18:38:13 aboudreault + * Added attribute index support for the sql queries in mapinfo tab format (GDAL bug #3687) + * + * Revision 1.120 2010-01-07 20:39:11 aboudreault + * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141) + * + * Revision 1.119 2009-07-28 21:35:29 aboudreault + * Added functions to get the file version (bug 1961) + * + * Revision 1.118 2009-03-10 13:50:02 aboudreault + * Fixed Overflow of FIDs in Seamless tables (bug 2015) + * + * Revision 1.117 2008-10-29 12:55:10 dmorissette + * Update version to 2.0.0-dev (2008-10) for GDAL 1.6.0 release + * + * Revision 1.116 2008/08/22 16:14:19 fwarmerdam + * export spatialref/coordsys transformers + * + * Revision 1.115 2008/07/21 14:09:41 aboudreault + * Add font text styles support (bold, italic, etc.) (bug 1922) + * + * Revision 1.114 2008/07/17 14:09:30 aboudreault + * Add text outline color support (halo background in MapInfo) + * + * Revision 1.113 2008/07/01 14:33:17 aboudreault + * * Fixed deprecated warnings generated by mitab.h by moving SetFontName to + * mitab_feature.cpp. + * + * Revision 1.112 2008/03/05 20:35:39 dmorissette + * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859) + * + * Revision 1.111 2008/02/29 21:27:41 dmorissette + * Update to v1.7.0-beta1 + * + * Revision 1.110 2008/02/20 21:35:30 dmorissette + * Added support for V800 COLLECTION of large objects (bug 1496) + * + * Revision 1.109 2008/02/13 21:10:43 dmorissette + * Fixed error in TAB_GEOM_GET_VERSION() macro logic + * + * Revision 1.108 2008/02/05 22:21:59 dmorissette + * Added macro TAB_GEOM_GET_VERSION() + * + * Revision 1.107 2008/02/01 19:55:55 dmorissette + * Set version to 1.7.0-dev + * + * Revision 1.106 2008/02/01 19:36:31 dmorissette + * Initial support for V800 REGION and MULTIPLINE (bug 1496) + * + * Revision 1.105 2008/01/29 21:56:39 dmorissette + * Update dataset version properly for Date/Time/DateTime field types (#1754) + * + * Revision 1.104 2007/12/11 04:26:29 dmorissette + * Update for 1.6.4 release + * + * Revision 1.103 2007/10/12 15:47:48 dmorissette + * Updated for 1.6.3 release + * + * Revision 1.102 2007/09/18 18:13:42 dmorissette + * Updated for 1.6.3-beta2 + * + * Revision 1.101 2007/09/14 20:03:08 dmorissette + * Removed stray ReadGeometryFromMAPFile() declaration + * + * Revision 1.100 2007/09/14 19:42:39 dmorissette + * Updated for 1.6.3-beta1 + * + * Revision 1.99 2007/09/14 18:30:18 dmorissette + * Fixed the splitting of object blocks with the optimized spatial + * index mode that was producing files with misaligned bytes that + * confused MapInfo (bug 1732) + * + * Revision 1.98 2007/09/12 20:22:31 dmorissette + * Added TABFeature::CreateFromMapInfoType() + * + * ... + * + * Revision 1.1 1999/07/12 04:18:23 daniel + * Initial checkin + * + **********************************************************************/ + +#ifndef _MITAB_H_INCLUDED_ +#define _MITAB_H_INCLUDED_ + +#include "mitab_priv.h" +#include "ogr_feature.h" +#include "ogr_featurestyle.h" +#include "ogrsf_frmts.h" + +/*--------------------------------------------------------------------- + * Current version of the MITAB library... always useful! + *--------------------------------------------------------------------*/ +#define MITAB_VERSION "2.0.0-dev (2008-10)" +#define MITAB_VERSION_INT 2000000 /* version x.y.z -> xxxyyyzzz */ + +#ifndef PI +# define PI 3.14159265358979323846 +#endif + +#ifndef ROUND_INT +# define ROUND_INT(dX) ((int)((dX) < 0.0 ? (dX)-0.5 : (dX)+0.5 )) +#endif + + +#define MITAB_AREA(x1, y1, x2, y2) ((double)((x2)-(x1))*(double)((y2)-(y1))) + +class TABFeature; + +/*--------------------------------------------------------------------- + * Codes for the GetFileClass() in the IMapInfoFile-derived classes + *--------------------------------------------------------------------*/ +typedef enum +{ + TABFC_IMapInfoFile = 0, + TABFC_TABFile, + TABFC_TABView, + TABFC_TABSeamless, + TABFC_MIFFile +} TABFileClass; + + +/*--------------------------------------------------------------------- + * class IMapInfoFile + * + * Virtual base class for the TABFile and MIFFile classes. + * + * This is the definition of the public interface methods that should + * be available for any type of MapInfo dataset. + *--------------------------------------------------------------------*/ + +class IMapInfoFile : public OGRLayer +{ + private: + + protected: + GIntBig m_nCurFeatureId; + TABFeature *m_poCurFeature; + GBool m_bBoundsSet; + + char *m_pszCharset; + + TABFeature* CreateTABFeature(OGRFeature *poFeature); + + public: + IMapInfoFile() ; + virtual ~IMapInfoFile(); + + virtual TABFileClass GetFileClass() {return TABFC_IMapInfoFile;} + + virtual int Open(const char *pszFname, const char* pszAccess, + GBool bTestOpenNoError = FALSE ); + + virtual int Open(const char *pszFname, TABAccess eAccess, + GBool bTestOpenNoError = FALSE ) = 0; + virtual int Close() = 0; + + virtual int SetQuickSpatialIndexMode(CPL_UNUSED GBool bQuickSpatialIndexMode=TRUE) {return -1;} + + virtual const char *GetTableName() = 0; + + /////////////// + // Static method to detect file type, create an object to read that + // file and open it. + static IMapInfoFile *SmartOpen(const char *pszFname, + GBool bUpdate = FALSE, + GBool bTestOpenNoError = FALSE); + + /////////////// + // OGR methods for read support + virtual void ResetReading() = 0; + virtual GIntBig GetFeatureCount (int bForce) = 0; + virtual OGRFeature *GetNextFeature(); + virtual OGRFeature *GetFeature(GIntBig nFeatureId); + virtual OGRErr ICreateFeature(OGRFeature *poFeature); + virtual int TestCapability( const char * pszCap ) =0; + virtual int GetExtent(OGREnvelope *psExtent, int bForce) =0; + + /////////////// + // Read access specific stuff + // + virtual GIntBig GetNextFeatureId(GIntBig nPrevId) = 0; + virtual TABFeature *GetFeatureRef(GIntBig nFeatureId) = 0; + virtual OGRFeatureDefn *GetLayerDefn() = 0; + + virtual TABFieldType GetNativeFieldType(int nFieldId) = 0; + + virtual int GetBounds(double &dXMin, double &dYMin, + double &dXMax, double &dYMax, + GBool bForce = TRUE ) = 0; + + virtual OGRSpatialReference *GetSpatialRef() = 0; + + virtual int GetFeatureCountByType(int &numPoints, int &numLines, + int &numRegions, int &numTexts, + GBool bForce = TRUE ) = 0; + + virtual GBool IsFieldIndexed(int nFieldId) = 0; + virtual GBool IsFieldUnique(int nFieldId) = 0; + + /////////////// + // Write access specific stuff + // + GBool IsBoundsSet() {return m_bBoundsSet;} + virtual int SetBounds(double dXMin, double dYMin, + double dXMax, double dYMax) = 0; + virtual int SetFeatureDefn(OGRFeatureDefn *poFeatureDefn, + TABFieldType *paeMapInfoNativeFieldTypes = NULL)=0; + virtual int AddFieldNative(const char *pszName, TABFieldType eMapInfoType, + int nWidth=0, int nPrecision=0, + GBool bIndexed=FALSE, GBool bUnique=FALSE, + int bApproxOK = TRUE) = 0; + virtual OGRErr CreateField( OGRFieldDefn *poField, int bApproxOK = TRUE ); + + virtual int SetSpatialRef(OGRSpatialReference *poSpatialRef) = 0; + + virtual OGRErr CreateFeature(TABFeature *poFeature) = 0; + + virtual int SetFieldIndexed(int nFieldId) = 0; + + virtual int SetCharset(const char* charset); + + /////////////// + // semi-private. + virtual int GetProjInfo(TABProjInfo *poPI) = 0; + virtual int SetProjInfo(TABProjInfo *poPI) = 0; + virtual int SetMIFCoordSys(const char *pszMIFCoordSys) = 0; + + static int GetTABType( OGRFieldDefn *poField, TABFieldType* peTABType, + int *pnWidth); + +#ifdef DEBUG + virtual void Dump(FILE *fpOut = NULL) = 0; +#endif +}; + +/*--------------------------------------------------------------------- + * class TABFile + * + * The main class for TAB datasets. External programs should use this + * class to open a TAB dataset and read/write features from/to it. + * + *--------------------------------------------------------------------*/ +class TABFile: public IMapInfoFile +{ + private: + char *m_pszFname; + TABAccess m_eAccessMode; + char **m_papszTABFile; + int m_nVersion; + int *m_panIndexNo; + TABTableType m_eTableType; // NATIVE (.DAT) or DBF + + TABDATFile *m_poDATFile; // Attributes file + TABMAPFile *m_poMAPFile; // Object Geometry file + TABINDFile *m_poINDFile; // Attributes index file + + OGRFeatureDefn *m_poDefn; + OGRSpatialReference *m_poSpatialRef; + int bUseSpatialTraversal; + + int m_nLastFeatureId; + + GIntBig *m_panMatchingFIDs; + int m_iMatchingFID; + + int m_bNeedTABRewrite; + + int m_bLastOpWasRead; + int m_bLastOpWasWrite; + /////////////// + // Private Read access specific stuff + // + int ParseTABFileFirstPass(GBool bTestOpenNoError); + int ParseTABFileFields(); + + /////////////// + // Private Write access specific stuff + // + int WriteTABFile(); + + public: + TABFile(); + virtual ~TABFile(); + + virtual TABFileClass GetFileClass() {return TABFC_TABFile;} + + virtual int Open(const char *pszFname, const char* pszAccess, + GBool bTestOpenNoError = FALSE ) { return IMapInfoFile::Open(pszFname, pszAccess, bTestOpenNoError); } + virtual int Open(const char *pszFname, TABAccess eAccess, + GBool bTestOpenNoError = FALSE ); + virtual int Close(); + + virtual int SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode=TRUE); + + virtual const char *GetTableName() + {return m_poDefn?m_poDefn->GetName():"";}; + + virtual void ResetReading(); + virtual int TestCapability( const char * pszCap ); + virtual GIntBig GetFeatureCount (int bForce); + virtual int GetExtent(OGREnvelope *psExtent, int bForce); + + /* Implement OGRLayer's SetFeature() for random write, only with TABFile */ + virtual OGRErr ISetFeature( OGRFeature * ); + virtual OGRErr DeleteFeature(GIntBig nFeatureId); + + virtual OGRErr DeleteField( int iField ); + virtual OGRErr ReorderFields( int* panMap ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + + virtual OGRErr SyncToDisk(); + + /////////////// + // Read access specific stuff + // + + int GetNextFeatureId_Spatial( int nPrevId ); + + virtual GIntBig GetNextFeatureId(GIntBig nPrevId); + virtual TABFeature *GetFeatureRef(GIntBig nFeatureId); + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual TABFieldType GetNativeFieldType(int nFieldId); + + virtual int GetBounds(double &dXMin, double &dYMin, + double &dXMax, double &dYMax, + GBool bForce = TRUE ); + + virtual OGRSpatialReference *GetSpatialRef(); + + static OGRSpatialReference* GetSpatialRefFromTABProj(const TABProjInfo& sTABProj); + static int GetTABProjFromSpatialRef(const OGRSpatialReference* poSpatialRef, + TABProjInfo& sTABProj, int& nParmCount); + + virtual int GetFeatureCountByType(int &numPoints, int &numLines, + int &numRegions, int &numTexts, + GBool bForce = TRUE); + + virtual GBool IsFieldIndexed(int nFieldId); + virtual GBool IsFieldUnique(int /*nFieldId*/) {return FALSE;}; + + virtual int GetVersion() { return m_nVersion; }; + + /////////////// + // Write access specific stuff + // + virtual int SetBounds(double dXMin, double dYMin, + double dXMax, double dYMax); + virtual int SetFeatureDefn(OGRFeatureDefn *poFeatureDefn, + TABFieldType *paeMapInfoNativeFieldTypes = NULL); + virtual int AddFieldNative(const char *pszName, TABFieldType eMapInfoType, + int nWidth=0, int nPrecision=0, + GBool bIndexed=FALSE, GBool bUnique=FALSE, + int bApproxOK = TRUE); + virtual int SetSpatialRef(OGRSpatialReference *poSpatialRef); + + virtual OGRErr CreateFeature(TABFeature *poFeature); + + virtual int SetFieldIndexed(int nFieldId); + + /////////////// + // semi-private. + virtual int GetProjInfo(TABProjInfo *poPI) + { return m_poMAPFile->GetHeaderBlock()->GetProjInfo( poPI ); } + virtual int SetProjInfo(TABProjInfo *poPI); + virtual int SetMIFCoordSys(const char *pszMIFCoordSys); + + int GetFieldIndexNumber(int nFieldId); + TABINDFile *GetINDFileRef(); + + TABMAPFile *GetMAPFileRef() { return m_poMAPFile; } + + int WriteFeature(TABFeature *poFeature); + +#ifdef DEBUG + virtual void Dump(FILE *fpOut = NULL); +#endif +}; + + +/*--------------------------------------------------------------------- + * class TABView + * + * TABView is used to handle special type of .TAB files that are + * composed of a number of .TAB datasets linked through some indexed + * fields. + * + * NOTE: The current implementation supports only TABViews composed + * of 2 TABFiles linked through an indexed field of integer type. + * It is unclear if any other type of views could exist anyways. + *--------------------------------------------------------------------*/ +class TABView: public IMapInfoFile +{ + private: + char *m_pszFname; + TABAccess m_eAccessMode; + char **m_papszTABFile; + char *m_pszVersion; + + char **m_papszTABFnames; + TABFile **m_papoTABFiles; + int m_numTABFiles; + int m_nMainTableIndex; // The main table is the one that also + // contains the geometries + char **m_papszFieldNames; + char **m_papszWhereClause; + + TABRelation *m_poRelation; + GBool m_bRelFieldsCreated; + + /////////////// + // Private Read access specific stuff + // + int ParseTABFile(const char *pszDatasetPath, + GBool bTestOpenNoError = FALSE); + + int OpenForRead(const char *pszFname, + GBool bTestOpenNoError = FALSE ); + + /////////////// + // Private Write access specific stuff + // + int OpenForWrite(const char *pszFname ); + int WriteTABFile(); + + + public: + TABView(); + virtual ~TABView(); + + virtual TABFileClass GetFileClass() {return TABFC_TABView;} + + virtual int Open(const char *pszFname, const char* pszAccess, + GBool bTestOpenNoError = FALSE ) { return IMapInfoFile::Open(pszFname, pszAccess, bTestOpenNoError); } + virtual int Open(const char *pszFname, TABAccess eAccess, + GBool bTestOpenNoError = FALSE ); + virtual int Close(); + + virtual int SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode=TRUE); + + virtual const char *GetTableName() + {return m_poRelation?m_poRelation->GetFeatureDefn()->GetName():"";}; + + virtual void ResetReading(); + virtual int TestCapability( const char * pszCap ); + virtual GIntBig GetFeatureCount (int bForce); + virtual int GetExtent(OGREnvelope *psExtent, int bForce); + + /////////////// + // Read access specific stuff + // + + virtual GIntBig GetNextFeatureId(GIntBig nPrevId); + virtual TABFeature *GetFeatureRef(GIntBig nFeatureId); + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual TABFieldType GetNativeFieldType(int nFieldId); + + virtual int GetBounds(double &dXMin, double &dYMin, + double &dXMax, double &dYMax, + GBool bForce = TRUE ); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int GetFeatureCountByType(int &numPoints, int &numLines, + int &numRegions, int &numTexts, + GBool bForce = TRUE); + + virtual GBool IsFieldIndexed(int nFieldId); + virtual GBool IsFieldUnique(int nFieldId); + + /////////////// + // Write access specific stuff + // + virtual int SetBounds(double dXMin, double dYMin, + double dXMax, double dYMax); + virtual int SetFeatureDefn(OGRFeatureDefn *poFeatureDefn, + TABFieldType *paeMapInfoNativeFieldTypes=NULL); + virtual int AddFieldNative(const char *pszName, + TABFieldType eMapInfoType, + int nWidth=0, int nPrecision=0, + GBool bIndexed=FALSE, GBool bUnique=FALSE, + int bApproxOK = TRUE); + virtual int SetSpatialRef(OGRSpatialReference *poSpatialRef); + + virtual OGRErr CreateFeature(TABFeature *poFeature); + + virtual int SetFieldIndexed(int nFieldId); + + /////////////// + // semi-private. + virtual int GetProjInfo(TABProjInfo *poPI) + { return m_nMainTableIndex!=-1? + m_papoTABFiles[m_nMainTableIndex]->GetProjInfo(poPI):-1; } + virtual int SetProjInfo(TABProjInfo *poPI) + { return m_nMainTableIndex!=-1? + m_papoTABFiles[m_nMainTableIndex]->SetProjInfo(poPI):-1; } + virtual int SetMIFCoordSys(const char * /*pszMIFCoordSys*/) {return -1;}; + +#ifdef DEBUG + virtual void Dump(FILE *fpOut = NULL); +#endif +}; + + +/*--------------------------------------------------------------------- + * class TABSeamless + * + * TABSeamless is used to handle seamless .TAB files that are + * composed of a main .TAB file in which each feature is the MBR of + * a base table. + * + * TABSeamless are supported for read access only. + *--------------------------------------------------------------------*/ +class TABSeamless: public IMapInfoFile +{ + private: + char *m_pszFname; + char *m_pszPath; + TABAccess m_eAccessMode; + OGRFeatureDefn *m_poFeatureDefnRef; + + TABFile *m_poIndexTable; + int m_nTableNameField; + int m_nCurBaseTableId; + TABFile *m_poCurBaseTable; + GBool m_bEOF; + + /////////////// + // Private Read access specific stuff + // + int OpenForRead(const char *pszFname, + GBool bTestOpenNoError = FALSE ); + int OpenBaseTable(TABFeature *poIndexFeature, + GBool bTestOpenNoError = FALSE); + int OpenBaseTable(int nTableId, GBool bTestOpenNoError = FALSE); + int OpenNextBaseTable(GBool bTestOpenNoError =FALSE); + GIntBig EncodeFeatureId(int nTableId, int nBaseFeatureId); + int ExtractBaseTableId(GIntBig nEncodedFeatureId); + int ExtractBaseFeatureId(GIntBig nEncodedFeatureId); + + public: + TABSeamless(); + virtual ~TABSeamless(); + + virtual TABFileClass GetFileClass() {return TABFC_TABSeamless;} + + virtual int Open(const char *pszFname, const char* pszAccess, + GBool bTestOpenNoError = FALSE ) { return IMapInfoFile::Open(pszFname, pszAccess, bTestOpenNoError); } + virtual int Open(const char *pszFname, TABAccess eAccess, + GBool bTestOpenNoError = FALSE ); + virtual int Close(); + + virtual const char *GetTableName() + {return m_poFeatureDefnRef?m_poFeatureDefnRef->GetName():"";}; + + virtual void SetSpatialFilter( OGRGeometry * ); + + virtual void ResetReading(); + virtual int TestCapability( const char * pszCap ); + virtual GIntBig GetFeatureCount (int bForce); + virtual int GetExtent(OGREnvelope *psExtent, int bForce); + + /////////////// + // Read access specific stuff + // + + virtual GIntBig GetNextFeatureId(GIntBig nPrevId); + virtual TABFeature *GetFeatureRef(GIntBig nFeatureId); + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual TABFieldType GetNativeFieldType(int nFieldId); + + virtual int GetBounds(double &dXMin, double &dYMin, + double &dXMax, double &dYMax, + GBool bForce = TRUE ); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int GetFeatureCountByType(int &numPoints, int &numLines, + int &numRegions, int &numTexts, + GBool bForce = TRUE); + + virtual GBool IsFieldIndexed(int nFieldId); + virtual GBool IsFieldUnique(int nFieldId); + + /////////////// + // Write access specific stuff + // + virtual int SetBounds(CPL_UNUSED double dXMin, CPL_UNUSED double dYMin, + CPL_UNUSED double dXMax, CPL_UNUSED double dYMax) {return -1;} + virtual int SetFeatureDefn(CPL_UNUSED OGRFeatureDefn *poFeatureDefn, + CPL_UNUSED TABFieldType *paeMapInfoNativeFieldTypes=NULL) + {return -1;} + virtual int AddFieldNative(CPL_UNUSED const char *pszName, + CPL_UNUSED TABFieldType eMapInfoType, + CPL_UNUSED int nWidth=0, + CPL_UNUSED int nPrecision=0, + CPL_UNUSED GBool bIndexed=FALSE, + CPL_UNUSED GBool bUnique=FALSE, + CPL_UNUSED int bApproxOK = TRUE) {return -1;} + + virtual int SetSpatialRef(CPL_UNUSED OGRSpatialReference *poSpatialRef) {return -1;} + + virtual OGRErr CreateFeature(CPL_UNUSED TABFeature *poFeature) + {return OGRERR_UNSUPPORTED_OPERATION;} + + virtual int SetFieldIndexed(CPL_UNUSED int nFieldId) {return -1;} + + /////////////// + // semi-private. + virtual int GetProjInfo(TABProjInfo *poPI) + { return m_poIndexTable?m_poIndexTable->GetProjInfo(poPI):-1; } + virtual int SetProjInfo(CPL_UNUSED TABProjInfo *poPI) { return -1; } + virtual int SetMIFCoordSys(const char * /*pszMIFCoordSys*/) {return -1;}; + +#ifdef DEBUG + virtual void Dump(FILE *fpOut = NULL); +#endif +}; + + +/*--------------------------------------------------------------------- + * class MIFFile + * + * The main class for (MID/MIF) datasets. External programs should use this + * class to open a (MID/MIF) dataset and read/write features from/to it. + * + *--------------------------------------------------------------------*/ +class MIFFile: public IMapInfoFile +{ + private: + char *m_pszFname; + TABAccess m_eAccessMode; + int m_nVersion; /* Dataset version: 300, 450, 600, 900, etc. */ + char *m_pszDelimiter; + char *m_pszUnique; + char *m_pszIndex; + char *m_pszCoordSys; + + TABFieldType *m_paeFieldType; + GBool *m_pabFieldIndexed; + GBool *m_pabFieldUnique; + + double m_dfXMultiplier; + double m_dfYMultiplier; + double m_dfXDisplacement; + double m_dfYDisplacement; + + /* these are the projection bounds, possibly much broader than extents */ + double m_dXMin; + double m_dYMin; + double m_dXMax; + double m_dYMax; + + /* extents, as cached by MIFFile::PreParseFile() */ + int m_bExtentsSet; + OGREnvelope m_sExtents; + + int m_nPoints; + int m_nLines; + int m_nRegions; + int m_nTexts; + + int m_nPreloadedId; // preloaded mif line is for this feature id + MIDDATAFile *m_poMIDFile; // Mid file + MIDDATAFile *m_poMIFFile; // Mif File + + OGRFeatureDefn *m_poDefn; + OGRSpatialReference *m_poSpatialRef; + + int m_nFeatureCount; + int m_nWriteFeatureId; + int m_nAttribut; + + /////////////// + // Private Read access specific stuff + // + int ReadFeatureDefn(); + int ParseMIFHeader(int* pbIsEmpty); + void PreParseFile(); + int AddFields(const char *pszLine); + int GotoFeature(int nFeatureId); + int NextFeature(); + + /////////////// + // Private Write access specific stuff + // + GBool m_bPreParsed; + GBool m_bHeaderWrote; + + int WriteMIFHeader(); + void UpdateExtents(double dfX,double dfY); + + public: + MIFFile(); + virtual ~MIFFile(); + + virtual TABFileClass GetFileClass() {return TABFC_MIFFile;} + + virtual int Open(const char *pszFname, const char* pszAccess, + GBool bTestOpenNoError = FALSE ) { return IMapInfoFile::Open(pszFname, pszAccess, bTestOpenNoError); } + virtual int Open(const char *pszFname, TABAccess eAccess, + GBool bTestOpenNoError = FALSE ); + virtual int Close(); + + virtual const char *GetTableName() + {return m_poDefn?m_poDefn->GetName():"";}; + + virtual int TestCapability( const char * pszCap ) ; + virtual GIntBig GetFeatureCount (int bForce); + virtual void ResetReading(); + virtual int GetExtent(OGREnvelope *psExtent, int bForce); + + /////////////// + // Read access specific stuff + // + + virtual GIntBig GetNextFeatureId(GIntBig nPrevId); + virtual TABFeature *GetFeatureRef(GIntBig nFeatureId); + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual TABFieldType GetNativeFieldType(int nFieldId); + + virtual int GetBounds(double &dXMin, double &dYMin, + double &dXMax, double &dYMax, + GBool bForce = TRUE ); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int GetFeatureCountByType(int &numPoints, int &numLines, + int &numRegions, int &numTexts, + GBool bForce = TRUE); + + virtual GBool IsFieldIndexed(int nFieldId); + virtual GBool IsFieldUnique(int nFieldId); + + virtual int GetVersion() { return m_nVersion; }; + + /////////////// + // Write access specific stuff + // + virtual int SetBounds(double dXMin, double dYMin, + double dXMax, double dYMax); + virtual int SetFeatureDefn(OGRFeatureDefn *poFeatureDefn, + TABFieldType *paeMapInfoNativeFieldTypes = NULL); + virtual int AddFieldNative(const char *pszName, TABFieldType eMapInfoType, + int nWidth=0, int nPrecision=0, + GBool bIndexed=FALSE, GBool bUnique=FALSE, + int bApproxOK = TRUE); + /* TODO */ + virtual int SetSpatialRef(OGRSpatialReference *poSpatialRef); + + virtual OGRErr CreateFeature(TABFeature *poFeature); + + virtual int SetFieldIndexed(int nFieldId); + + /////////////// + // semi-private. + virtual int GetProjInfo(TABProjInfo * /*poPI*/){return -1;} + /* { return m_poMAPFile->GetHeaderBlock()->GetProjInfo( poPI ); }*/ + virtual int SetProjInfo(TABProjInfo * /*poPI*/){return -1;} + /* { return m_poMAPFile->GetHeaderBlock()->SetProjInfo( poPI ); }*/ + virtual int SetMIFCoordSys(const char * pszMIFCoordSys); + +#ifdef DEBUG + virtual void Dump(FILE * /*fpOut*/ = NULL) {}; +#endif +}; + +/*--------------------------------------------------------------------- + * Define some error codes specific to this lib. + *--------------------------------------------------------------------*/ +#define TAB_WarningFeatureTypeNotSupported 501 +#define TAB_WarningInvalidFieldName 502 +#define TAB_WarningBoundsOverflow 503 + +/*--------------------------------------------------------------------- + * Codes for the feature classes + *--------------------------------------------------------------------*/ +typedef enum +{ + TABFCNoGeomFeature = 0, + TABFCPoint = 1, + TABFCFontPoint = 2, + TABFCCustomPoint = 3, + TABFCText = 4, + TABFCPolyline = 5, + TABFCArc = 6, + TABFCRegion = 7, + TABFCRectangle = 8, + TABFCEllipse = 9, + TABFCMultiPoint = 10, + TABFCCollection = 11, + TABFCDebugFeature +} TABFeatureClass; + +/*--------------------------------------------------------------------- + * Definitions for text attributes + *--------------------------------------------------------------------*/ +typedef enum TABTextJust_t +{ + TABTJLeft = 0, // Default: Left Justification + TABTJCenter, + TABTJRight +} TABTextJust; + +typedef enum TABTextSpacing_t +{ + TABTSSingle = 0, // Default: Single spacing + TABTS1_5, // 1.5 + TABTSDouble +} TABTextSpacing; + +typedef enum TABTextLineType_t +{ + TABTLNoLine = 0, // Default: No line + TABTLSimple, + TABTLArrow +} TABTextLineType; + +typedef enum TABFontStyle_t // Can be OR'ed +{ // except box and halo are mutually exclusive + TABFSNone = 0, + TABFSBold = 0x0001, + TABFSItalic = 0x0002, + TABFSUnderline = 0x0004, + TABFSStrikeout = 0x0008, + TABFSOutline = 0x0010, + TABFSShadow = 0x0020, + TABFSInverse = 0x0040, + TABFSBlink = 0x0080, + TABFSBox = 0x0100, // See note about box vs halo below. + TABFSHalo = 0x0200, // MIF uses 256, see MIF docs, App.A + TABFSAllCaps = 0x0400, // MIF uses 512 + TABFSExpanded = 0x0800 // MIF uses 1024 +} TABFontStyle; + +/* TABFontStyle enum notes: + * + * The enumeration values above correspond to the values found in a .MAP + * file. However, they differ a little from what is found in a MIF file: + * Values 0x01 to 0x80 are the same in .MIF and .MAP files. + * Values 0x200 to 0x800 in .MAP are 0x100 to 0x400 in .MIF + * + * What about TABFSBox (0x100) ? + * TABFSBox is stored just like the other styles in .MAP files but it is not + * explicitly stored in a MIF file. + * If a .MIF FONT() clause contains the optional BG color, then this implies + * that either Halo or Box was set. Thus if TABFSHalo (value 256 in MIF) + * is not set in the style, then this implies that TABFSBox should be set. + */ + +typedef enum TABCustSymbStyle_t // Can be OR'ed +{ + TABCSNone = 0, // Transparent BG, use default colors + TABCSBGOpaque = 0x01, // White pixels are opaque + TABCSApplyColor = 0x02 // non-white pixels drawn using symbol color +} TABCustSymbStyle; + +/*===================================================================== + Base classes to be used to add supported drawing tools to each feature type + =====================================================================*/ + +class ITABFeaturePen +{ + protected: + int m_nPenDefIndex; + TABPenDef m_sPenDef; + public: + ITABFeaturePen(); + ~ITABFeaturePen() {}; + int GetPenDefIndex() {return m_nPenDefIndex;}; + TABPenDef *GetPenDefRef() {return &m_sPenDef;}; + + GByte GetPenWidthPixel(); + double GetPenWidthPoint(); + int GetPenWidthMIF(); + GByte GetPenPattern() {return m_sPenDef.nLinePattern;}; + GInt32 GetPenColor() {return m_sPenDef.rgbColor;}; + + void SetPenWidthPixel(GByte val); + void SetPenWidthPoint(double val); + void SetPenWidthMIF(int val); + + void SetPenPattern(GByte val) {m_sPenDef.nLinePattern=val;}; + void SetPenColor(GInt32 clr) {m_sPenDef.rgbColor = clr;}; + + const char *GetPenStyleString(); + void SetPenFromStyleString(const char *pszStyleString); + + void DumpPenDef(FILE *fpOut = NULL); +}; + +class ITABFeatureBrush +{ + protected: + int m_nBrushDefIndex; + TABBrushDef m_sBrushDef; + public: + ITABFeatureBrush(); + ~ITABFeatureBrush() {}; + int GetBrushDefIndex() {return m_nBrushDefIndex;}; + TABBrushDef *GetBrushDefRef() {return &m_sBrushDef;}; + + GInt32 GetBrushFGColor() {return m_sBrushDef.rgbFGColor;}; + GInt32 GetBrushBGColor() {return m_sBrushDef.rgbBGColor;}; + GByte GetBrushPattern() {return m_sBrushDef.nFillPattern;}; + GByte GetBrushTransparent() {return m_sBrushDef.bTransparentFill;}; + + void SetBrushFGColor(GInt32 clr) { m_sBrushDef.rgbFGColor = clr;}; + void SetBrushBGColor(GInt32 clr) { m_sBrushDef.rgbBGColor = clr;}; + void SetBrushPattern(GByte val) { m_sBrushDef.nFillPattern=val;}; + void SetBrushTransparent(GByte val) + {m_sBrushDef.bTransparentFill=val;}; + + const char *GetBrushStyleString(); + void SetBrushFromStyleString(const char *pszStyleString); + + void DumpBrushDef(FILE *fpOut = NULL); +}; + +class ITABFeatureFont +{ + protected: + int m_nFontDefIndex; + TABFontDef m_sFontDef; + public: + ITABFeatureFont(); + ~ITABFeatureFont() {}; + int GetFontDefIndex() {return m_nFontDefIndex;}; + TABFontDef *GetFontDefRef() {return &m_sFontDef;}; + + const char *GetFontNameRef() {return m_sFontDef.szFontName;}; + + void SetFontName(const char *pszName); + + void DumpFontDef(FILE *fpOut = NULL); +}; + +class ITABFeatureSymbol +{ + protected: + int m_nSymbolDefIndex; + TABSymbolDef m_sSymbolDef; + public: + ITABFeatureSymbol(); + ~ITABFeatureSymbol() {}; + int GetSymbolDefIndex() {return m_nSymbolDefIndex;}; + TABSymbolDef *GetSymbolDefRef() {return &m_sSymbolDef;}; + + GInt16 GetSymbolNo() {return m_sSymbolDef.nSymbolNo;}; + GInt16 GetSymbolSize() {return m_sSymbolDef.nPointSize;}; + GInt32 GetSymbolColor() {return m_sSymbolDef.rgbColor;}; + + void SetSymbolNo(GInt16 val) { m_sSymbolDef.nSymbolNo = val;}; + void SetSymbolSize(GInt16 val) { m_sSymbolDef.nPointSize = val;}; + void SetSymbolColor(GInt32 clr) { m_sSymbolDef.rgbColor = clr;}; + + const char *GetSymbolStyleString(double dfAngle = 0.0); + void SetSymbolFromStyleString(const char *pszStyleString); + + void DumpSymbolDef(FILE *fpOut = NULL); +}; + + +/*===================================================================== + Feature Classes + =====================================================================*/ + +/*--------------------------------------------------------------------- + * class TABFeature + * + * Extend the OGRFeature to support MapInfo specific extensions related + * to geometry types, representation strings, etc. + * + * TABFeature will be used as a base class for all the feature classes. + * + * This class will also be used to instanciate objects with no Geometry + * (i.e. type TAB_GEOM_NONE) which is a valid case in MapInfo. + * + * The logic to read/write the object from/to the .DAT and .MAP files is also + * implemented as part of this class and derived classes. + *--------------------------------------------------------------------*/ +class TABFeature: public OGRFeature +{ + protected: + TABGeomType m_nMapInfoType; + + double m_dXMin; + double m_dYMin; + double m_dXMax; + double m_dYMax; + + GBool m_bDeletedFlag; + + void CopyTABFeatureBase(TABFeature *poDestFeature); + + // Compr. Origin is set for TAB files by ValidateCoordType() + GInt32 m_nXMin; + GInt32 m_nYMin; + GInt32 m_nXMax; + GInt32 m_nYMax; + GInt32 m_nComprOrgX; + GInt32 m_nComprOrgY; + + virtual int UpdateMBR(TABMAPFile *poMapFile = NULL); + + public: + TABFeature(OGRFeatureDefn *poDefnIn ); + virtual ~TABFeature(); + + static TABFeature *CreateFromMapInfoType(int nMapInfoType, + OGRFeatureDefn *poDefn); + + virtual TABFeature *CloneTABFeature(OGRFeatureDefn *pNewDefn = NULL); + virtual TABFeatureClass GetFeatureClass() { return TABFCNoGeomFeature; }; + virtual TABGeomType GetMapInfoType() { return m_nMapInfoType; }; + virtual TABGeomType ValidateMapInfoType(CPL_UNUSED TABMAPFile *poMapFile = NULL) + {m_nMapInfoType=TAB_GEOM_NONE; + return m_nMapInfoType;}; + GBool IsRecordDeleted() { return m_bDeletedFlag; }; + void SetRecordDeleted(GBool bDeleted) { m_bDeletedFlag=bDeleted; }; + + /*----------------------------------------------------------------- + * TAB Support + *----------------------------------------------------------------*/ + + virtual int ReadRecordFromDATFile(TABDATFile *poDATFile); + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int WriteRecordToDATFile(TABDATFile *poDATFile, + TABINDFile *poINDFile, int *panIndexNo); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + GBool ValidateCoordType(TABMAPFile * poMapFile); + void ForceCoordTypeAndOrigin(TABGeomType nMapInfoType, GBool bCompr, + GInt32 nComprOrgX, GInt32 nComprOrgY, + GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax); + + /*----------------------------------------------------------------- + * Mid/Mif Support + *----------------------------------------------------------------*/ + + virtual int ReadRecordFromMIDFile(MIDDATAFile *fp); + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + + virtual int WriteRecordToMIDFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + void ReadMIFParameters(MIDDATAFile *fp); + void WriteMIFParameters(MIDDATAFile *fp); + + /*----------------------------------------------------------------- + *----------------------------------------------------------------*/ + + void SetMBR(double dXMin, double dYMin, + double dXMax, double dYMax); + void GetMBR(double &dXMin, double &dYMin, + double &dXMax, double &dYMax); + void SetIntMBR(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax); + void GetIntMBR(GInt32 &nXMin, GInt32 &nYMin, + GInt32 &nXMax, GInt32 &nYMax); + + virtual void DumpMID(FILE *fpOut = NULL); + virtual void DumpMIF(FILE *fpOut = NULL); + +}; + + +/*--------------------------------------------------------------------- + * class TABPoint + * + * Feature class to handle old style MapInfo point symbols: + * + * TAB_GEOM_SYMBOL_C 0x01 + * TAB_GEOM_SYMBOL 0x02 + * + * Feature geometry will be a OGRPoint + * + * The symbol number is in the range [31..67], with 31=None and corresponds + * to one of the 35 predefined "Old MapInfo Symbols" + * + * NOTE: This class is also used as a base class for the other point + * symbol types TABFontPoint and TABCustomPoint. + *--------------------------------------------------------------------*/ +class TABPoint: public TABFeature, + public ITABFeatureSymbol +{ + public: + TABPoint(OGRFeatureDefn *poDefnIn); + virtual ~TABPoint(); + + virtual TABFeatureClass GetFeatureClass() { return TABFCPoint; }; + virtual TABGeomType ValidateMapInfoType(TABMAPFile *poMapFile = NULL); + + virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = NULL ); + + double GetX(); + double GetY(); + + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + virtual const char *GetStyleString(); + + virtual void DumpMIF(FILE *fpOut = NULL); +}; + + +/*--------------------------------------------------------------------- + * class TABFontPoint + * + * Feature class to handle MapInfo Font Point Symbol types: + * + * TAB_GEOM_FONTSYMBOL_C 0x28 + * TAB_GEOM_FONTSYMBOL 0x29 + * + * Feature geometry will be a OGRPoint + * + * The symbol number refers to a character code in the specified Windows + * Font (e.g. "Windings"). + *--------------------------------------------------------------------*/ +class TABFontPoint: public TABPoint, + public ITABFeatureFont +{ + protected: + double m_dAngle; + GInt16 m_nFontStyle; // Bold/shadow/halo/etc. + + public: + TABFontPoint(OGRFeatureDefn *poDefnIn); + virtual ~TABFontPoint(); + + virtual TABFeatureClass GetFeatureClass() { return TABFCFontPoint; }; + + virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = NULL ); + + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + virtual const char *GetStyleString(); + + GBool QueryFontStyle(TABFontStyle eStyleToQuery); + void ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStatus); + + int GetFontStyleMIFValue(); + void SetFontStyleMIFValue(int nStyle); + int GetFontStyleTABValue() {return m_nFontStyle;}; + void SetFontStyleTABValue(int nStyle){m_nFontStyle=(GInt16)nStyle;}; + + // GetSymbolAngle(): Return angle in degrees counterclockwise + double GetSymbolAngle() {return m_dAngle;}; + void SetSymbolAngle(double dAngle); +}; + + +/*--------------------------------------------------------------------- + * class TABCustomPoint + * + * Feature class to handle MapInfo Custom Point Symbol (Bitmap) types: + * + * TAB_GEOM_CUSTOMSYMBOL_C 0x2b + * TAB_GEOM_CUSTOMSYMBOL 0x2c + * + * Feature geometry will be a OGRPoint + * + * The symbol name is the name of a BMP file stored in the "CustSymb" + * directory (e.g. "arrow.BMP"). The symbol number has no meaning for + * this symbol type. + *--------------------------------------------------------------------*/ +class TABCustomPoint: public TABPoint, + public ITABFeatureFont +{ + protected: + GByte m_nCustomStyle; // Show BG/Apply Color + + public: + GByte m_nUnknown_; + + public: + TABCustomPoint(OGRFeatureDefn *poDefnIn); + virtual ~TABCustomPoint(); + + virtual TABFeatureClass GetFeatureClass() { return TABFCCustomPoint; }; + + virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = NULL ); + + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + virtual const char *GetStyleString(); + + const char *GetSymbolNameRef() { return GetFontNameRef(); }; + void SetSymbolName(const char *pszName) {SetFontName(pszName);}; + + GByte GetCustomSymbolStyle() {return m_nCustomStyle;} + void SetCustomSymbolStyle(GByte nStyle) {m_nCustomStyle = nStyle;} +}; + + +/*--------------------------------------------------------------------- + * class TABPolyline + * + * Feature class to handle the various MapInfo line types: + * + * TAB_GEOM_LINE_C 0x04 + * TAB_GEOM_LINE 0x05 + * TAB_GEOM_PLINE_C 0x07 + * TAB_GEOM_PLINE 0x08 + * TAB_GEOM_MULTIPLINE_C 0x25 + * TAB_GEOM_MULTIPLINE 0x26 + * TAB_GEOM_V450_MULTIPLINE_C 0x31 + * TAB_GEOM_V450_MULTIPLINE 0x32 + * + * Feature geometry can be either a OGRLineString or a OGRMultiLineString + *--------------------------------------------------------------------*/ +class TABPolyline: public TABFeature, + public ITABFeaturePen +{ + private: + GBool m_bCenterIsSet; + double m_dCenterX, m_dCenterY; + GBool m_bWriteTwoPointLineAsPolyline; + + public: + TABPolyline(OGRFeatureDefn *poDefnIn); + virtual ~TABPolyline(); + + virtual TABFeatureClass GetFeatureClass() { return TABFCPolyline; }; + virtual TABGeomType ValidateMapInfoType(TABMAPFile *poMapFile = NULL); + + virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = NULL ); + + /* 2 methods to simplify access to rings in a multiple polyline + */ + int GetNumParts(); + OGRLineString *GetPartRef(int nPartIndex); + + GBool TwoPointLineAsPolyline(); + void TwoPointLineAsPolyline(GBool bTwoPointLineAsPolyline); + + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + virtual const char *GetStyleString(); + + virtual void DumpMIF(FILE *fpOut = NULL); + + int GetCenter(double &dX, double &dY); + void SetCenter(double dX, double dY); + + // MapInfo-specific attributes... made available through public vars + // for now. + GBool m_bSmooth; + +}; + +/*--------------------------------------------------------------------- + * class TABRegion + * + * Feature class to handle the MapInfo region types: + * + * TAB_GEOM_REGION_C 0x0d + * TAB_GEOM_REGION 0x0e + * TAB_GEOM_V450_REGION_C 0x2e + * TAB_GEOM_V450_REGION 0x2f + * + * Feature geometry will be returned as OGRPolygon (with a single ring) + * or OGRMultiPolygon (for multiple rings). + * + * REGIONs with multiple rings are returned as OGRMultiPolygon instead of + * as OGRPolygons since OGRPolygons require that the first ring be the + * outer ring, and the other all be inner rings, but this is not guaranteed + * inside MapInfo files. However, when writing features, OGRPolygons with + * multiple rings will be accepted without problem. + *--------------------------------------------------------------------*/ +class TABRegion: public TABFeature, + public ITABFeaturePen, + public ITABFeatureBrush +{ + GBool m_bSmooth; + private: + GBool m_bCenterIsSet; + double m_dCenterX, m_dCenterY; + + int ComputeNumRings(TABMAPCoordSecHdr **ppasSecHdrs, + TABMAPFile *poMAPFile); + int AppendSecHdrs(OGRPolygon *poPolygon, + TABMAPCoordSecHdr * &pasSecHdrs, + TABMAPFile *poMAPFile, + int &iLastRing); + + public: + TABRegion(OGRFeatureDefn *poDefnIn); + virtual ~TABRegion(); + + virtual TABFeatureClass GetFeatureClass() { return TABFCRegion; }; + virtual TABGeomType ValidateMapInfoType(TABMAPFile *poMapFile = NULL); + + virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = NULL ); + + /* 2 methods to make the REGION's geometry look like a single collection + * of OGRLinearRings + */ + int GetNumRings(); + OGRLinearRing *GetRingRef(int nRequestedRingIndex); + GBool IsInteriorRing(int nRequestedRingIndex); + + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + virtual const char *GetStyleString(); + + virtual void DumpMIF(FILE *fpOut = NULL); + + int GetCenter(double &dX, double &dY); + void SetCenter(double dX, double dY); +}; + + +/*--------------------------------------------------------------------- + * class TABRectangle + * + * Feature class to handle the MapInfo rectangle types: + * + * TAB_GEOM_RECT_C 0x13 + * TAB_GEOM_RECT 0x14 + * TAB_GEOM_ROUNDRECT_C 0x16 + * TAB_GEOM_ROUNDRECT 0x17 + * + * A rectangle is defined by the coords of its 2 opposite corners (the MBR) + * Its corners can optionaly be rounded, in which case a X and Y rounding + * radius will be defined. + * + * Feature geometry will be OGRPolygon + *--------------------------------------------------------------------*/ +class TABRectangle: public TABFeature, + public ITABFeaturePen, + public ITABFeatureBrush +{ + private: + virtual int UpdateMBR(TABMAPFile *poMapFile = NULL); + + public: + TABRectangle(OGRFeatureDefn *poDefnIn); + virtual ~TABRectangle(); + + virtual TABFeatureClass GetFeatureClass() { return TABFCRectangle; }; + virtual TABGeomType ValidateMapInfoType(TABMAPFile *poMapFile = NULL); + + virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = NULL ); + + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + virtual const char *GetStyleString(); + + virtual void DumpMIF(FILE *fpOut = NULL); + + // MapInfo-specific attributes... made available through public vars + // for now. + GBool m_bRoundCorners; + double m_dRoundXRadius; + double m_dRoundYRadius; + +}; + + +/*--------------------------------------------------------------------- + * class TABEllipse + * + * Feature class to handle the MapInfo ellipse types: + * + * TAB_GEOM_ELLIPSE_C 0x19 + * TAB_GEOM_ELLIPSE 0x1a + * + * An ellipse is defined by the coords of its 2 opposite corners (the MBR) + * + * Feature geometry can be either an OGRPoint defining the center of the + * ellipse, or an OGRPolygon defining the ellipse itself. + * + * When an ellipse is read, the returned geometry is a OGRPolygon representing + * the ellipse with 2 degrees line segments. + * + * In the case of the OGRPoint, then the X/Y Radius MUST be set, but. + * However with an OGRPolygon, if the X/Y radius are not set (== 0) then + * the MBR of the polygon will be used to define the ellipse parameters + * and the center of the MBR is used as the center of the ellipse... + * (i.e. the polygon vertices themselves will be ignored). + *--------------------------------------------------------------------*/ +class TABEllipse: public TABFeature, + public ITABFeaturePen, + public ITABFeatureBrush +{ + private: + virtual int UpdateMBR(TABMAPFile *poMapFile = NULL); + + public: + TABEllipse(OGRFeatureDefn *poDefnIn); + virtual ~TABEllipse(); + + virtual TABFeatureClass GetFeatureClass() { return TABFCEllipse; }; + virtual TABGeomType ValidateMapInfoType(TABMAPFile *poMapFile = NULL); + + virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = NULL ); + + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + virtual const char *GetStyleString(); + + virtual void DumpMIF(FILE *fpOut = NULL); + + // MapInfo-specific attributes... made available through public vars + // for now. + double m_dCenterX; + double m_dCenterY; + double m_dXRadius; + double m_dYRadius; + +}; + + +/*--------------------------------------------------------------------- + * class TABArc + * + * Feature class to handle the MapInfo arc types: + * + * TAB_GEOM_ARC_C 0x0a + * TAB_GEOM_ARC 0x0b + * + * In MapInfo, an arc is defined by the coords of the MBR corners of its + * defining ellipse, which in this case is different from the arc's MBR, + * and a start and end angle in degrees. + * + * Feature geometry can be either an OGRLineString or an OGRPoint. + * + * In any case, X/Y radius X/Y center, and start/end angle (in degrees + * counterclockwise) MUST be set. + * + * When an arc is read, the returned geometry is an OGRLineString + * representing the arc with 2 degrees line segments. + *--------------------------------------------------------------------*/ +class TABArc: public TABFeature, + public ITABFeaturePen +{ + private: + double m_dStartAngle; // In degrees, counterclockwise, + double m_dEndAngle; // starting at 3 o'clock + + virtual int UpdateMBR(TABMAPFile *poMapFile = NULL); + + public: + TABArc(OGRFeatureDefn *poDefnIn); + virtual ~TABArc(); + + virtual TABFeatureClass GetFeatureClass() { return TABFCArc; }; + virtual TABGeomType ValidateMapInfoType(TABMAPFile *poMapFile = NULL); + + virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = NULL ); + + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + virtual const char *GetStyleString(); + + virtual void DumpMIF(FILE *fpOut = NULL); + + double GetStartAngle() { return m_dStartAngle; }; + double GetEndAngle() { return m_dEndAngle; }; + void SetStartAngle(double dAngle); + void SetEndAngle(double dAngle); + + // MapInfo-specific attributes... made available through public vars + // for now. + double m_dCenterX; + double m_dCenterY; + double m_dXRadius; + double m_dYRadius; +}; + + +/*--------------------------------------------------------------------- + * class TABText + * + * Feature class to handle the MapInfo text types: + * + * TAB_GEOM_TEXT_C 0x10 + * TAB_GEOM_TEXT 0x11 + * + * Feature geometry is an OGRPoint corresponding to the lower-left + * corner of the text MBR BEFORE ROTATION. + * + * Text string, and box height/width (box before rotation is applied) + * are required in a valid text feature and MUST be set. + * Text angle and other styles are optional. + *--------------------------------------------------------------------*/ +class TABText: public TABFeature, + public ITABFeatureFont, + public ITABFeaturePen +{ + protected: + char *m_pszString; + + double m_dAngle; + double m_dHeight; + double m_dWidth; + double m_dfLineEndX; + double m_dfLineEndY; + GBool m_bLineEndSet; + void UpdateTextMBR(); + + GInt32 m_rgbForeground; + GInt32 m_rgbBackground; + GInt32 m_rgbOutline; + GInt32 m_rgbShadow; + + GInt16 m_nTextAlignment; // Justification/Vert.Spacing/arrow + GInt16 m_nFontStyle; // Bold/italic/underlined/shadow/... + + const char *GetLabelStyleString(); + + virtual int UpdateMBR(TABMAPFile *poMapFile = NULL); + + public: + TABText(OGRFeatureDefn *poDefnIn); + virtual ~TABText(); + + virtual TABFeatureClass GetFeatureClass() { return TABFCText; }; + virtual TABGeomType ValidateMapInfoType(TABMAPFile *poMapFile = NULL); + + virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = NULL ); + + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + virtual const char *GetStyleString(); + + virtual void DumpMIF(FILE *fpOut = NULL); + + const char *GetTextString(); + double GetTextAngle(); + double GetTextBoxHeight(); + double GetTextBoxWidth(); + GInt32 GetFontFGColor(); + GInt32 GetFontBGColor(); + GInt32 GetFontOColor(); + GInt32 GetFontSColor(); + void GetTextLineEndPoint(double &dX, double &dY); + + TABTextJust GetTextJustification(); + TABTextSpacing GetTextSpacing(); + TABTextLineType GetTextLineType(); + GBool QueryFontStyle(TABFontStyle eStyleToQuery); + + void SetTextString(const char *pszStr); + void SetTextAngle(double dAngle); + void SetTextBoxHeight(double dHeight); + void SetTextBoxWidth(double dWidth); + void SetFontFGColor(GInt32 rgbColor); + void SetFontBGColor(GInt32 rgbColor); + void SetFontOColor(GInt32 rgbColor); + void SetFontSColor(GInt32 rgbColor); + void SetTextLineEndPoint(double dX, double dY); + + void SetTextJustification(TABTextJust eJust); + void SetTextSpacing(TABTextSpacing eSpacing); + void SetTextLineType(TABTextLineType eLineType); + void ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStatus); + + int GetFontStyleMIFValue(); + void SetFontStyleMIFValue(int nStyle, GBool bBGColorSet=FALSE); + GBool IsFontBGColorUsed(); + GBool IsFontOColorUsed(); + GBool IsFontSColorUsed(); + GBool IsFontBold(); + GBool IsFontItalic(); + GBool IsFontUnderline(); + int GetFontStyleTABValue() {return m_nFontStyle;}; + void SetFontStyleTABValue(int nStyle){m_nFontStyle=(GInt16)nStyle;}; + +}; + + +/*--------------------------------------------------------------------- + * class TABMultiPoint + * + * Feature class to handle MapInfo Multipoint features: + * + * TAB_GEOM_MULTIPOINT_C 0x34 + * TAB_GEOM_MULTIPOINT 0x35 + * + * Feature geometry will be a OGRMultiPoint + * + * The symbol number is in the range [31..67], with 31=None and corresponds + * to one of the 35 predefined "Old MapInfo Symbols" + *--------------------------------------------------------------------*/ +class TABMultiPoint: public TABFeature, + public ITABFeatureSymbol +{ + private: + // We call it center, but it's more like a label point + // Its value default to be the location of the first point + GBool m_bCenterIsSet; + double m_dCenterX, m_dCenterY; + + public: + TABMultiPoint(OGRFeatureDefn *poDefnIn); + virtual ~TABMultiPoint(); + + virtual TABFeatureClass GetFeatureClass() { return TABFCMultiPoint; }; + virtual TABGeomType ValidateMapInfoType(TABMAPFile *poMapFile = NULL); + + virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = NULL ); + + int GetXY(int i, double &dX, double &dY); + int GetNumPoints(); + + int GetCenter(double &dX, double &dY); + void SetCenter(double dX, double dY); + + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + virtual const char *GetStyleString(); + + virtual void DumpMIF(FILE *fpOut = NULL); +}; + +/*--------------------------------------------------------------------- + * + * class TABCollection + * + * Feature class to handle MapInfo Collection features: + * + * TAB_GEOM_COLLECTION_C 0x37 + * TAB_GEOM_COLLECTION 0x38 + * + * Feature geometry will be a OGRCollection + * + * **** IMPORTANT NOTE: **** + * + * The current implementation does not allow setting the Geometry via + * OGRFeature::SetGeometry*(). The geometries must be set via the + * TABCollection::SetRegion/Pline/MpointDirectly() methods which will take + * care of keeping the OGRFeature's geometry in sync. + * + * If we ever want to support creating collections via the OGR interface then + * something should be added in TABCollection::WriteGeometryToMapFile(), or + * perhaps in ValidateMapInfoType(), or even better in a custom + * TABCollection::SetGeometry*()... but then this last option may not work + * unless OGRFeature::SetGeometry*() are made virtual in OGR. + * + *--------------------------------------------------------------------*/ +class TABCollection: public TABFeature, + public ITABFeatureSymbol +{ + private: + TABRegion *m_poRegion; + TABPolyline *m_poPline; + TABMultiPoint *m_poMpoint; + + void EmptyCollection(); + int ReadLabelAndMBR(TABMAPCoordBlock *poCoordBlock, + GBool bComprCoord, + GInt32 nComprOrgX, GInt32 nComprOrgY, + GInt32 &pnMinX, GInt32 &pnMinY, + GInt32 &pnMaxX, GInt32 &pnMaxY, + GInt32 &pnLabelX, GInt32 &pnLabelY ); + int WriteLabelAndMBR(TABMAPCoordBlock *poCoordBlock, + GBool bComprCoord, + GInt32 nMinX, GInt32 nMinY, + GInt32 nMaxX, GInt32 nMaxY, + GInt32 nLabelX, GInt32 nLabelY ); + int SyncOGRGeometryCollection(GBool bSyncRegion, + GBool bSyncPline, + GBool bSyncMpoint); + + public: + TABCollection(OGRFeatureDefn *poDefnIn); + virtual ~TABCollection(); + + virtual TABFeatureClass GetFeatureClass() { return TABFCCollection; }; + virtual TABGeomType ValidateMapInfoType(TABMAPFile *poMapFile = NULL); + + virtual TABFeature *CloneTABFeature(OGRFeatureDefn *poNewDefn = NULL ); + + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + virtual const char *GetStyleString(); + + virtual void DumpMIF(FILE *fpOut = NULL); + + TABRegion *GetRegionRef() {return m_poRegion; }; + TABPolyline *GetPolylineRef() {return m_poPline; }; + TABMultiPoint *GetMultiPointRef() {return m_poMpoint; }; + + int SetRegionDirectly(TABRegion *poRegion); + int SetPolylineDirectly(TABPolyline *poPline); + int SetMultiPointDirectly(TABMultiPoint *poMpoint); +}; + + +/*--------------------------------------------------------------------- + * class TABDebugFeature + * + * Feature class to use for testing purposes... this one does not + * correspond to any MapInfo type... it's just used to dump info about + * feature types that are not implemented yet. + *--------------------------------------------------------------------*/ +class TABDebugFeature: public TABFeature +{ + private: + GByte m_abyBuf[512]; + int m_nSize; + int m_nCoordDataPtr; // -1 if none + int m_nCoordDataSize; + + public: + TABDebugFeature(OGRFeatureDefn *poDefnIn); + virtual ~TABDebugFeature(); + + virtual TABFeatureClass GetFeatureClass() { return TABFCDebugFeature; }; + + virtual int ReadGeometryFromMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + virtual int WriteGeometryToMAPFile(TABMAPFile *poMapFile, TABMAPObjHdr *, + GBool bCoordDataOnly=FALSE, + TABMAPCoordBlock **ppoCoordBlock=NULL); + + virtual int ReadGeometryFromMIFFile(MIDDATAFile *fp); + virtual int WriteGeometryToMIFFile(MIDDATAFile *fp); + + virtual void DumpMIF(FILE *fpOut = NULL); +}; + +/* -------------------------------------------------------------------- */ +/* Some stuff related to spatial reference system handling. */ +/* */ +/* In GDAL we make use of the coordsys transformation from */ +/* other places (sometimes even from plugins), so we */ +/* deliberately export these two functions from the DLL. */ +/* -------------------------------------------------------------------- */ + +char CPL_DLL *MITABSpatialRef2CoordSys( OGRSpatialReference * ); +OGRSpatialReference CPL_DLL * MITABCoordSys2SpatialRef( const char * ); + +GBool MITABExtractCoordSysBounds( const char * pszCoordSys, + double &dXMin, double &dYMin, + double &dXMax, double &dYMax ); +int MITABCoordSys2TABProjInfo(const char * pszCoordSys, TABProjInfo *psProj); + +typedef struct { + int nDatumEPSGCode; + int nMapInfoDatumID; + const char *pszOGCDatumName; + int nEllipsoid; + double dfShiftX; + double dfShiftY; + double dfShiftZ; + double dfDatumParm0; /* RotX */ + double dfDatumParm1; /* RotY */ + double dfDatumParm2; /* RotZ */ + double dfDatumParm3; /* Scale Factor */ + double dfDatumParm4; /* Prime Meridian */ +} MapInfoDatumInfo; + +typedef struct +{ + int nMapInfoId; + const char *pszMapinfoName; + double dfA; /* semi major axis in meters */ + double dfInvFlattening; /* Inverse flattening */ +} MapInfoSpheroidInfo; + + +/*--------------------------------------------------------------------- + * The following are used for coordsys bounds lookup + *--------------------------------------------------------------------*/ + +GBool MITABLookupCoordSysBounds(TABProjInfo *psCS, + double &dXMin, double &dYMin, + double &dXMax, double &dYMax, + int bOnlyUserTable = FALSE); +int MITABLoadCoordSysTable(const char *pszFname); +void MITABFreeCoordSysTable(); +GBool MITABCoordSysTableLoaded(); + +#endif /* _MITAB_H_INCLUDED_ */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_bounds.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_bounds.cpp new file mode 100644 index 000000000..14f12b06e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_bounds.cpp @@ -0,0 +1,1347 @@ +/********************************************************************** + * $Id: mitab_bounds.cpp,v 1.8 2008-01-29 20:53:10 dmorissette Exp $ + * + * Name: mitab_bounds.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of bound lookup tables for known projections. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 2001, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_bounds.cpp,v $ + * Revision 1.8 2008-01-29 20:53:10 dmorissette + * Added bounds for PSAD56 (Patch from AJD sent for bug #1754) + * + * Revision 1.7 2005/09/29 18:31:28 dmorissette + * New bounds entry for Finnish KKJ and Swedish projections (AJD, bug 1155) + * + * Revision 1.6 2005/03/31 22:00:38 dmorissette + * Added bounds entry to match datum 1011 based on MapInfo's "Svenska + * rikssystemet, 2,5 gon väst (RT 90 7 parametrar)" (bug 997) + * + * Revision 1.5 2005/03/22 23:24:54 dmorissette + * Added support for datum id in .MAP header (bug 910) + * + * Revision 1.4 2004/06/30 20:29:03 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.3 2001/02/14 21:17:33 daniel + * Check only if first char is "#" for comments in MITABLoadCoordSysTable() + * + * Revision 1.2 2001/01/23 22:06:50 daniel + * Added MITABCoordSysTableLoaded() + * + * Revision 1.1 2001/01/23 21:23:41 daniel + * Added projection bounds lookup table, called from TABFile::SetProjInfo() + * + **********************************************************************/ + +#include "mitab.h" + +typedef struct +{ + TABProjInfo sProj; /* Projection/datum definition */ + double dXMin; /* Default bounds for that coordsys */ + double dYMin; + double dXMax; + double dYMax; +} MapInfoBoundsInfo; + +typedef struct +{ + TABProjInfo sProjIn; + MapInfoBoundsInfo sBoundsInfo; +} MapInfoRemapProjInfo; + +/*----------------------------------------------------------------- + * List of known coordsys bounds. + * 0xff in nEllipsoidId or nUnitsId fields means any value can match. + * + * __TODO__: nDatumId is always set to zero in this list, we'd have to + * reprocess the whole list to properly set all datum ids and accelerate + * bounds lookups + *----------------------------------------------------------------*/ +static MapInfoRemapProjInfo *gpasExtBoundsList = NULL; +static int nExtBoundsListCount = -1; +static const MapInfoBoundsInfo gasBoundsList[] = { +{{1, 0xff, 0xff, {0,0,0,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -1000, -1000, 1000, 1000}, /* Lat/Lon */ + +{{2, 29, 0, {-85.5,13,0,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -18500.7190263237, -4067.43878447928, 30025.7571082958, 4067.43878447928}, +{{2, 29, 0, {20,0,0,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26284.8753911183, -3963.19059194305, 23518.0464025796, 3963.19059194305}, +{{2, 7, 7, {0,30,0,0,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -34706360.1398239, -7364918.36397399, 34706360.1398239, 7364918.36397399}, +{{3, 0, 3, {-109.5,44.25,45,49,1968503.937,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -376322393.49652, -357755728.255206, 380259401.37052, 398826066.611833}, +{{3, 0, 3, {-111.5,36.6666666667,37.2166666667,38.35,1640419.948,9842519.685}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -281416969.95067, -245782664.730374, 284697809.84667, 320332115.066966}, +{{3, 0, 3, {-111.5,38.3333333333,39.0166666667,40.65,1640419.948,6561679.79}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -299820220.76226, -269235441.863424, 303101060.65826, 333685839.557096}, +{{3, 0, 3, {-111.5,40.3333333333,40.7166666667,41.7833333333,1640419.948,3280839.895}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -313479418.366583, -287610480.882755, 316760258.262583, 342629195.746411}, +{{3, 0, 3, {-120.5,41.6666666667,42.3333333333,44,4921259.843,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -329872743.533369, -311905915.324464, 339715263.219369, 357682091.428273}, +{{3, 0, 3, {-120.5,43.6666666667,44.3333333333,46,8202099.738,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -348623368.682272, -335442185.295993, 365027568.158272, 378208751.544552}, +{{3, 0, 3, {-81,31.8333333333,32.5,34.8333333333,2000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -248789436.724623, -218682485.302253, 252789436.724623, 282896388.146993}, +{{3, 0, 3, {-84.3666666667,41.5,42.1,43.6666666667,13123359.58,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -318674512.717618, -308729738.1419, 344921231.877618, 354866006.453336}, +{{3, 0, 3, {-84.3666666667,43.3166666667,44.1833333333,45.7,19685039.37,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -334588341.234808, -332680007.139814, 373958419.974808, 375866754.069803}, +{{3, 0, 3, {-87,44.7833333333,45.4833333333,47.0833333333,26246719.16,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -343541891.912548, -349200540.720143, 396035330.232548, 390376681.424953}, +{{3, 0, 7, {-100,39.8333333333,40,43,500000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -96293653.747449, -89392122.913416, 97293653.747449, 104195184.581482}, +{{3, 0, 7, {-100,43.8333333333,44.4166666667,45.6833333333,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -107757768.605122, -101845373.546917, 108957768.605122, 114870163.663327}, +{{3, 0, 7, {-100.3333333333,29.6666666667,30.1166666667,31.8833333333,700000,3000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -70389655.9882633, -57319094.8848422, 71789655.9882633, 84860217.0916844}, +{{3, 0, 7, {-100.3333333333,42.3333333333,42.8333333333,44.4,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -102916158.881298, -96669936.9429582, 104116158.881298, 110362380.819638}, +{{3, 0, 7, {-100.5,45.6666666667,46.1833333333,47.4833333333,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -114111552.091083, -108589212.496103, 115311552.091083, 120833891.686064}, +{{3, 0, 7, {-100.5,47,47.4333333333,48.7333333333,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -118782512.590452, -113525792.131232, 119982512.590452, 125239233.049672}, +{{3, 0, 7, {-101.5,34,34.65,36.1833333333,200000,1000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -80190916.4774175, -70255345.5878226, 80590916.4774175, 90526487.3670124}, +{{3, 0, 7, {-105.5,36.6666666667,37.2333333333,38.4333333333,914401.8289,304800.6096}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -85492362.7230086, -77749948.5363837, 87321166.3808086, 95063580.5674335}, +{{3, 0, 7, {-105.5,37.8333333333,38.45,39.75,914401.8289,304800.6096}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -88909656.3330413, -81520557.8132071, 90738459.9908412, 98127558.5106754}, +{{3, 0, 7, {-105.5,39.3333333333,39.7166666667,40.7833333333,914401.8289,304800.6096}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -92173099.6583073, -85136649.2531605, 94001903.3161073, 101038353.721254}, +{{3, 0, 7, {-109.5,44.25,45,49,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -114703065.537737, -109043945.972187, 115903065.537737, 121562185.103287}, +{{3, 0, 7, {-111.5,36.6666666667,37.2166666667,38.35,500000,3000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -85775892.4411146, -74914556.209806, 86775892.4411146, 97637228.6724232}, +{{3, 0, 7, {-111.5,38.3333333333,39.0166666667,40.65,500000,2000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -91385203.2884872, -82062962.6799637, 92385203.2884872, 101707443.897011}, +{{3, 0, 7, {-111.5,40.3333333333,40.7166666667,41.7833333333,500000,1000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -95548526.7182849, -87663674.5730598, 96548526.7182849, 104433378.86351}, +{{3, 0, 7, {-116.25,32.1666666667,32.7833333333,33.8833333333,2000000,500000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -73735809.4129763, -65399717.6233228, 77735809.4129763, 86071901.2026297}, +{{3, 0, 7, {-118,33.5,34.0333333333,35.4666666667,2000000,500000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -76848317.8487893, -69006561.7099004, 80848317.8487893, 88690073.9876782}, +{{3, 0, 7, {-119,35.3333333333,36,37.25,2000000,500000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -81316774.1198701, -74083546.7405704, 85316774.1198701, 92550001.4991699}, +{{3, 0, 7, {-120.5,36.5,37.0666666667,38.4333333333,2000000,500000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -84187586.7378795, -77301811.5655565, 88187586.7378795, 95073361.9102026}, +{{3, 0, 7, {-120.5,41.6666666667,42.3333333333,44,1500000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -100545212.229117, -95068922.9908967, 103545212.229117, 109021501.467338}, +{{3, 0, 7, {-120.5,43.6666666667,44.3333333333,46,2500000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -106260402.774499, -102242778.078219, 111260402.774499, 115278027.47078}, +{{3, 0, 7, {-120.5,45.3333333333,45.8333333333,47.3333333333,500000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -113297926.255298, -107613973.979824, 114297926.255298, 119981878.530772}, +{{3, 0, 7, {-120.8333333333,47,47.5,48.7333333333,500000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -119009737.681158, -113655959.077325, 120009737.681158, 125363516.284991}, +{{3, 0, 7, {-122,37.6666666667,38.3333333333,39.8333333333,2000000,500000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -87776918.3325266, -81257129.4018421, 91776918.3325266, 98296707.2632112}, +{{3, 0, 7, {-122,39.3333333333,40,41.6666666667,2000000,500000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -92797918.1664438, -86741363.5256259, 96797918.1664438, 102854472.807262}, +{{3, 0, 7, {-176,51,51.8333333333,53.8333333333,1000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -137707686.600156, -133658946.217207, 139707686.600156, 143756426.983104}, +{{3, 0, 7, {-66.4333333333,17.8333333333,18.0333333333,18.4333333333,200000,200000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -56733778.1428648, -37322071.9454256, 57133778.1428648, 58748927.6361153}, +{{3, 0, 7, {-70.5,41,41.2833333333,41.4833333333,500000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -95953926.4298888, -89161935.9801186, 96953926.4298888, 103745916.879659}, +{{3, 0, 7, {-71.5,41,41.7166666667,42.6833333333,200000,750000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -98769146.9690858, -91041445.2286386, 99169146.9690858, 106896848.709533}, +{{3, 0, 7, {-72.75,40.8333333333,41.2,41.8666666667,304800.6096,152400.3048}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -96604898.0590896, -89468373.8450348, 97214499.2782896, 104351023.492344}, +{{3, 0, 7, {-74,40.1666666667,40.6666666667,41.0333333333,300000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -94551938.838961, -87389402.5378217, 95151938.838961, 102314475.1401}, +{{3, 0, 7, {-77,37.6666666667,38.3,39.45,400000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -88804178.7516629, -81148556.0144016, 89604178.7516629, 97259801.4889241}, +{{3, 0, 7, {-77.75,39.3333333333,39.9333333333,40.9666666667,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -93070575.1551149, -86055381.3970464, 94270575.1551149, 101285768.913183}, +{{3, 0, 7, {-77.75,40.1666666667,40.8833333333,41.95,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -95953285.96374, -89173937.5969296, 97153285.96374, 103932634.33055}, +{{3, 0, 7, {-78.5,36.3333333333,36.7666666667,37.9666666667,3500000,1000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -81693800.1596124, -75717098.3537021, 88693800.1596124, 94670501.9655228}, +{{3, 0, 7, {-78.5,37.6666666667,38.0333333333,39.2,3500000,2000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -84998823.9067757, -78398508.2242156, 91998823.9067757, 98599139.5893358}, +{{3, 0, 7, {-79,33.75,34.3333333333,36.1666666667,609601.22,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -79389023.1316542, -70798838.7584427, 80608225.5716542, 89198409.9448658}, +{{3, 0, 7, {-79.5,38.5,39,40.25,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -90694785.2338336, -83456997.4383959, 91894785.2338336, 99132573.0292712}, +{{3, 0, 7, {-81,31.8333333333,32.5,34.8333333333,609600,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -75831020.313665, -66654421.5201266, 77050220.313665, 86226819.1072034}, +{{3, 0, 7, {-81,37,37.4833333333,38.8833333333,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -86731868.6538258, -79080928.1042007, 87931868.6538258, 95582809.203451}, +{{3, 0, 7, {-82.5,38,38.7333333333,40.0333333333,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -90013727.4321062, -82680858.3947178, 91213727.4321062, 98546596.4694945}, +{{3, 0, 7, {-82.5,39.6666666667,40.4333333333,41.7,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -94896218.4967998, -88010766.8984708, 96096218.4967998, 102981670.095129}, +{{3, 0, 7, {-84.25,37.5,37.9666666667,38.9666666667,500000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -87593445.377663, -79948138.9064763, 88593445.377663, 96238751.8488498}, +{{3, 0, 7, {-84.3666666667,41.5,42.1,43.6666666667,4000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -97131991.4763139, -94100824.185651, 105131991.476314, 108163158.766977}, +{{3, 0, 7, {-84.3666666667,43.3166666667,44.1833333333,45.7,6000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -101982526.408346, -101400866.176215, 113982526.408346, 114564186.640476}, +{{3, 0, 7, {-84.5,29,29.5833333333,30.75,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -68981040.7901211, -58469775.5831551, 70181040.7901211, 80692305.9970871}, +{{3, 0, 7, {-85.75,36.3333333333,36.7333333333,37.9333333333,500000,500000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -84608108.5314714, -76125029.0762607, 85608108.5314714, 94091187.9866821}, +{{3, 0, 7, {-86,34.3333333333,35.25,36.4166666667,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -80782527.8217375, -72373896.5103859, 81982527.8217375, 90391159.1330892}, +{{3, 0, 7, {-87,44.7833333333,45.4833333333,47.0833333333,8000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -104711568.654913, -106436324.8115, 120711568.654913, 118986812.498326}, +{{3, 0, 7, {-90,42,42.7333333333,44.0666666667,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -102206338.477554, -95896048.3229835, 103406338.477554, 109716628.632125}, +{{3, 0, 7, {-90,43.8333333333,44.25,45.5,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -107154105.286573, -101222137.29396, 108354105.286573, 114286073.279185}, +{{3, 0, 7, {-90,45.1666666667,45.5666666667,46.7666666667,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -111693759.01384, -106048829.503351, 112893759.01384, 118538688.524328}, +{{3, 0, 7, {-91.3333333333,25.5,26.1666666667,27.8333333333,1000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -63537867.0156485, -51847003.2875935, 65537867.0156485, 77228730.7437035}, +{{3, 0, 7, {-91.3333333333,28.5,29.3,30.7,1000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -68286769.6701213, -58065091.5621744, 70286769.6701213, 80508447.7780682}, +{{3, 0, 7, {-92,32.6666666667,33.3,34.7666666667,400000,400000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -76844795.0772024, -67240210.8482154, 77644795.0772024, 87249379.3061895}, +{{3, 0, 7, {-92,34.3333333333,34.9333333333,36.2333333333,400000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -80385696.4806497, -71723301.2825226, 81185696.4806497, 89848091.6787767}, +{{3, 0, 7, {-92.5,30.5,31.1666666667,32.6666666667,1000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -71843187.1479918, -62437428.102125, 73843187.1479918, 83248946.1938587}, +{{3, 0, 7, {-93.1,46.5,47.0333333333,48.6333333333,800000,100000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -117632875.188048, -112398161.714158, 119232875.188048, 124467588.661938}, +{{3, 0, 7, {-93.5,40,40.6166666667,41.7833333333,500000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -95397349.164015, -88468304.6501118, 96397349.164015, 103326393.677918}, +{{3, 0, 7, {-93.5,41.5,42.0666666667,43.2666666667,1500000,1000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -98941207.3645198, -92381467.3382571, 101941207.36452, 108500947.390783}, +{{3, 0, 7, {-94,43,43.7833333333,45.2166666667,800000,100000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -105671574.194712, -99704447.3453072, 107271574.194712, 113238701.044116}, +{{3, 0, 7, {-94.25,45,45.6166666667,47.05,800000,100000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -112092591.799175, -106546357.855489, 113692591.799175, 119238825.742861}, +{{3, 0, 7, {-98,33.3333333333,33.9333333333,35.2333333333,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -77870984.1222039, -69071740.6080561, 79070984.1222039, 87870227.6363517}, +{{3, 0, 7, {-98,35,35.5666666667,36.7666666667,600000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -81588328.3763743, -73324068.4505433, 82788328.3763743, 91052588.3022054}, +{{3, 0, 7, {-98,38.3333333333,38.7166666667,39.7833333333,400000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -89841929.3777815, -82323689.9068513, 90641929.3777815, 98160168.8487116}, +{{3, 0, 7, {-98.5,25.6666666667,26.1666666667,27.8333333333,300000,5000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -64237867.0156485, -46865470.5583462, 64837867.0156485, 82210263.4729508}, +{{3, 0, 7, {-98.5,31.6666666667,32.1333333333,33.9666666667,600000,2000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -74535881.4099808, -63171655.6562241, 75735881.4099808, 87100107.1637374}, +{{3, 0, 7, {-98.5,36.6666666667,38.5666666667,37.2666666667,400000,400000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -86225535.4567815, -77888976.4101326, 87025535.4567815, 95362094.5034304}, +{{3, 0, 7, {-99,27.8333333333,28.3833333333,30.2833333333,600000,4000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -67541882.489505, -52618369.9310933, 68741882.489505, 83665395.0479167}, +{{3, 0, 8, {-100,39.8333333333,40,43,1640416.667,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -315923429.002755, -293280656.591766, 319204262.336755, 341847034.747745}, +{{3, 0, 8, {-100,43.8333333333,44.4166666667,45.6833333333,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -353535279.165304, -334137696.37851, 357472279.165304, 376869861.952098}, +{{3, 0, 8, {-100.3333333333,29.6666666667,30.1166666667,31.8833333333,2296583.333,9842500}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -230936729.688494, -188054397.134687, 235529896.354494, 278412228.908301}, +{{3, 0, 8, {-100.3333333333,42.3333333333,42.8333333333,44.4,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -337650764.596392, -317157951.453689, 341587764.596392, 362080577.739096}, +{{3, 0, 8, {-100.5,45.6666666667,46.1833333333,47.4833333333,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -374380983.81883, -356263107.997632, 378317983.81883, 396435859.640027}, +{{3, 0, 8, {-100.5,47,47.4333333333,48.7333333333,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -389705626.72384, -372459203.017216, 393642626.72384, 410889050.430464}, +{{3, 0, 8, {-101.5,34,34.65,36.1833333333,656166.6667,3280833.333}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -263093031.809627, -230496079.649714, 264405365.143027, 297002317.30294}, +{{3, 0, 8, {-105.5,36.6666666667,37.2333333333,38.4333333333,3000000,1000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -280486193.367387, -255084622.823115, 286486193.367387, 311887763.911659}, +{{3, 0, 8, {-105.5,37.8333333333,38.45,39.75,3000000,1000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -291697764.152969, -267455363.425493, 297697764.152969, 321940164.880445}, +{{3, 0, 8, {-105.5,39.3333333333,39.7166666667,40.7833333333,3000000,1000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -302404577.795946, -279319156.758073, 308404577.795946, 331489998.833818}, +{{3, 0, 8, {-109.5,44.25,45,49,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -376321640.851725, -357755012.743749, 380258640.851725, 398825268.9597}, +{{3, 0, 8, {-111.5,36.6666666667,37.2166666667,38.35,1640416.667,9842500}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -281416407.11689, -245782173.165005, 284697240.45089, 320331474.402775}, +{{3, 0, 8, {-111.5,38.3333333333,39.0166666667,40.65,1640416.667,6561666.667}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -299819621.121978, -269234903.392181, 303100454.455978, 333685172.185776}, +{{3, 0, 8, {-111.5,40.3333333333,40.7166666667,41.7833333333,1640416.667,3280833.333}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -313478791.407906, -287609905.662114, 316759624.741906, 342628510.487699}, +{{3, 0, 8, {-116.25,32.1666666667,32.7833333333,33.8833333333,6561666.667,1640416.667}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -241914901.382073, -214565573.568852, 255038234.716073, 282387562.529294}, +{{3, 0, 8, {-118,33.5,34.0333333333,35.4666666667,6561666.667,1640416.667}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -252126522.80857, -226399027.876232, 265249856.14257, 290977351.074907}, +{{3, 0, 8, {-119,35.3333333333,36,37.25,6561666.667,1640416.667}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -266786783.091274, -243055769.597688, 279910116.425274, 303641129.91886}, +{{3, 0, 8, {-120.5,36.5,37.0666666667,38.4333333333,6561666.667,1640416.667}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -276205440.822193, -253614360.110997, 289328774.156193, 311919854.86739}, +{{3, 0, 8, {-120.5,41.6666666667,42.3333333333,44,4921250,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -329872083.788362, -311905291.512634, 339714583.788362, 357681376.064091}, +{{3, 0, 8, {-120.5,43.6666666667,44.3333333333,46,8202083.333,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -348622671.436336, -335441514.411622, 365026838.102336, 378207995.127049}, +{{3, 0, 8, {-120.5,45.3333333333,45.8333333333,47.3333333333,1640416.667,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -371711613.055591, -353063512.965473, 374992446.389591, 393640546.479709}, +{{3, 0, 8, {-120.8333333333,47,47.5,48.7333333333,1640416.667,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -390451114.375266, -372886259.072857, 393731947.709266, 411296803.011675}, +{{3, 0, 8, {-122,37.6666666667,38.3333333333,39.8333333333,6561666.667,1640416.667}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -287981439.562298, -266591098.71221, 301104772.896298, 322495113.746385}, +{{3, 0, 8, {-122,39.3333333333,40,41.6666666667,6561666.667,1640416.667}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -304454503.184074, -284583956.833324, 317577836.518074, 337448382.868824}, +{{3, 0, 8, {-176,51,51.8333333333,53.8333333333,3280833.333,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -451795968.454344, -438512726.047621, 458357635.120344, 471640877.527066}, +{{3, 0, 8, {-66.4333333333,17.8333333333,18.0333333333,18.4333333333,656166.6667,656166.6667}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -186134070.457016, -122447497.707584, 187446403.790416, 192745440.086188}, +{{3, 0, 8, {-70.5,41,41.2833333333,41.4833333333,1640416.667,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -314808840.29506, -292525451.628106, 318089673.62906, 340373062.296015}, +{{3, 0, 8, {-71.5,41,41.7166666667,42.6833333333,656166.6667,2460625}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -324045109.681042, -298691808.220958, 325357443.014442, 350710744.474526}, +{{3, 0, 8, {-72.75,40.8333333333,41.2,41.8666666667,1000000,500000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -316944569.715526, -293530823.189916, 318944569.715526, 342358316.241135}, +{{3, 0, 8, {-74,40.1666666667,40.6666666667,41.0333333333,984250,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -310209152.674158, -286710064.82617, 312177652.674158, 335676740.522146}, +{{3, 0, 8, {-77,37.6666666667,38.3,39.45,1312333.333,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -291351709.788081, -266234887.523916, 293976376.454081, 319093198.718245}, +{{3, 0, 8, {-77.75,39.3333333333,39.9333333333,40.9666666667,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -305349045.321406, -282333363.800143, 309286045.321406, 332301726.842669}, +{{3, 0, 8, {-77.75,40.1666666667,40.8833333333,41.95,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -314806739.032704, -292564826.932593, 318743739.032704, 340985651.132814}, +{{3, 0, 8, {-78.5,36.3333333333,36.7666666667,37.9666666667,11482916.67,3280833.333}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -268023742.686995, -248415180.182438, 290989576.026995, 310598138.531553}, +{{3, 0, 8, {-78.5,37.6666666667,38.0333333333,39.2,11482916.67,6561666.667}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -278866974.764147, -257212439.065281, 301832808.104147, 323487343.803013}, +{{3, 0, 8, {-79,33.75,34.3333333333,36.1666666667,2000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -260462153.393719, -232279190.159991, 264462153.393719, 292645116.627447}, +{{3, 0, 8, {-79.5,38.5,39,40.25,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -297554474.554669, -273808499.095804, 301491474.554669, 325237450.013534}, +{{3, 0, 8, {-81,31.8333333333,32.5,34.8333333333,1999996,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -248788939.145749, -218682047.937282, 252788931.145749, 282895822.354217}, +{{3, 0, 8, {-81,37,37.4833333333,38.8833333333,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -284552805.74176, -259451344.955198, 288489805.74176, 313591266.528322}, +{{3, 0, 8, {-82.5,38,38.7333333333,40.0333333333,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -295320037.416835, -271262116.250003, 299257037.416835, 323314958.583666}, +{{3, 0, 8, {-82.5,39.6666666667,40.4333333333,41.7,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -311338676.851584, -288748657.732733, 315275676.851584, 337865695.970435}, +{{3, 0, 8, {-84.25,37.5,37.9666666667,38.9666666667,1640416.667,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -287379495.376216, -262296519.062331, 290660328.710216, 315743305.024101}, +{{3, 0, 8, {-84.3666666667,41.5,42.1,43.6666666667,13123333.33,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -318673875.371873, -308729120.682423, 344920542.031873, 354865296.721323}, +{{3, 0, 8, {-84.3666666667,43.3166666667,44.1833333333,45.7,19685000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -334587672.058047, -332679341.779799, 373957672.058047, 375866002.336295}, +{{3, 0, 8, {-84.5,29,29.5833333333,30.75,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -226315297.992256, -191829588.725735, 230252297.992256, 264738007.258777}, +{{3, 0, 8, {-85.75,36.3333333333,36.7333333333,37.9333333333,1640416.667,1640416.667}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -277585102.740002, -249753532.894032, 280865936.074002, 308697505.919973}, +{{3, 0, 8, {-86,34.3333333333,35.25,36.4166666667,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -265034010.028484, -237446692.134491, 268971010.028484, 296558327.922477}, +{{3, 0, 8, {-87,44.7833333333,45.4833333333,47.0833333333,26246666.67,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -343541204.825326, -349199842.319061, 396034538.165326, 390375900.67159}, +{{3, 0, 8, {-90,42,42.7333333333,44.0666666667,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -335321962.15511, -314618951.872988, 339258962.15511, 359961972.437231}, +{{3, 0, 8, {-90,43.8333333333,44.25,45.5,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -351554760.427697, -332092962.105268, 355491760.427697, 374953558.750127}, +{{3, 0, 8, {-90,45.1666666667,45.5666666667,46.7666666667,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -366448607.697906, -347928534.795579, 370385607.697906, 388905680.600232}, +{{3, 0, 8, {-91.3333333333,25.5,26.1666666667,27.8333333333,3280833.333,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -208457152.034173, -170101376.61938, 215018818.700173, 253374594.114967}, +{{3, 0, 8, {-91.3333333333,28.5,29.3,30.7,3280833.333,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -224037510.159723, -190501887.900234, 230599176.825723, 264134799.085212}, +{{3, 0, 8, {-92,32.6666666667,33.3,34.7666666667,1312333.333,1312333.333}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -252114965.182788, -220603925.09152, 254739631.848788, 286250671.940057}, +{{3, 0, 8, {-92,34.3333333333,34.9333333333,36.2333333333,1312333.333,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -263732072.537265, -235312197.62441, 266356739.203265, 294776614.11612}, +{{3, 0, 8, {-92.5,30.5,31.1666666667,32.6666666667,3280833.333,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -235705523.16837, -204846795.365055, 242267189.83437, 273125917.637685}, +{{3, 0, 8, {-93.1,46.5,47.0333333333,48.6333333333,2624666.667,328083.3333}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -385933858.012454, -368759635.557233, 391183191.346454, 408357413.801674}, +{{3, 0, 8, {-93.5,40,40.6166666667,41.7833333333,1640416.667,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -312982803.048606, -290249762.839575, 316263636.382606, 338996676.591636}, +{{3, 0, 8, {-93.5,41.5,42.0666666667,43.2666666667,4921250,3280833.333}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -324609611.161762, -303088197.425932, 334452111.161762, 355973524.897592}, +{{3, 0, 8, {-94,43,43.7833333333,45.2166666667,2624666.667,328083.3333}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -346690823.003483, -327113674.332095, 351940156.337483, 371517305.008871}, +{{3, 0, 8, {-94.25,45,45.6166666667,47.05,2624666.667,328083.3333}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -367757111.594128, -349560842.397585, 373006444.928128, 391202714.124671}, +{{3, 0, 8, {-98,33.3333333333,33.9333333333,35.2333333333,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -255481720.407597, -226612868.978264, 259418720.407597, 288287571.836931}, +{{3, 0, 8, {-98,35,35.5666666667,36.7666666667,1968500,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -267677707.348155, -240564047.908157, 271614707.348155, 298728366.788152}, +{{3, 0, 8, {-98,38.3333333333,38.7166666667,39.7833333333,1312333.333,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -294756396.633938, -270090305.969395, 297381063.299938, 322047153.964481}, +{{3, 0, 8, {-98.5,25.6666666667,26.1666666667,27.8333333333,984250,16404166.67}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -210753735.367173, -153757797.986841, 212722235.367173, 269718172.747506}, +{{3, 0, 8, {-98.5,31.6666666667,32.1333333333,33.9666666667,1968500,6561666.667}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -244539804.259245, -207255673.598462, 248476804.259245, 285760934.920029}, +{{3, 0, 8, {-98.5,36.6666666667,38.5666666667,37.2666666667,1312333.333,1312333.333}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -282891610.911457, -255540750.10591, 285516277.577457, 312867138.383004}, +{{3, 0, 8, {-99,27.8333333333,28.3833333333,30.2833333333,1968500,13123333.33}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -221593659.467651, -172632102.018929, 225530659.467651, 274492216.916373}, +{{3, 2, 7, {135,-24,-18,-36,0,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -63926410.6698201, -76575533.9276959, 63926410.6698201, 51277287.4119443}, +{{3, 2, 7, {145,-37,-36,-38,2500000,4500000}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -81753864.458242, -88226525.7784545, 86753864.458242, 80281203.1380295}, +{{3, 2, 7, {147,0,-32.666,-35.333,1000000,10000000}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -76161714.4889037, -80605291.2154594, 78161714.4889037, 73718137.7623481}, +{{3, 28, 7, {23,-23,-18,-32,0,0}, 0,-134.73,-110.92,-292.66, {0,0,0,1,0}, 0,0,0,0,0,0,0,0}, -61566719.246568, -72414618.8198515, 61566719.246568, 47796808.4991836}, +{{3, 29, 0, {110,10,25,40,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -45730.409093877, -38000.6689484435, 45730.409093877, 53460.1492393104}, +{{3, 29, 0, {132.5,-10,-21.5,-33.5,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -40383.2188994685, -49161.7062224971, 40383.2188994685, 31604.73157644}, +{{3, 29, 0, {25,35,40,65,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -84908.0213013157, -80759.383210806, 84908.0213013157, 89056.6593918254}, +{{3, 29, 0, {47.5,25,15,35,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -38011.5438463059, -29679.737315404, 38011.5438463059, 46069.8166618704}, +{{3, 29, 0, {95,40,20,60,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -55750.1705370063, -51403.1783693689, 55750.1705370063, 60097.1627046438}, +{{3, 30, 7, {0,42.165,41.560387840948,42.76766346965,234.358,185861.369}, 0,-168,-60,320, {0,0,0,0,2.337229166667}, 0,0,0,0,0,0,0,0}, -98847613.927946, -91608686.7437833, 98848082.643946, 106087009.828109}, +{{3, 30, 7, {0,42.165,41.560387840948,42.76766346965,234.358,4185861.369}, 0,-168,-60,320, {0,0,0,0,2.337229166667}, 0,0,0,0,0,0,0,0}, -98847613.927946, -87608686.7437833, 98848082.643946, 110087009.828109}, +{{3, 30, 7, {0,44.1,43.199291275544,44.996093814511,600000,200000}, 0,-168,-60,320, {0,0,0,0,2.337229166667}, 0,0,0,0,0,0,0,0}, -104503824.398662, -98311919.3139696, 105703824.398662, 111895729.483354}, +{{3, 30, 7, {0,44.1,43.199291275544,44.996093814511,600000,3200000}, 0,-168,-60,320, {0,0,0,0,2.337229166667}, 0,0,0,0,0,0,0,0}, -104503824.398662, -95311919.3139696, 105703824.398662, 114895729.483354}, +{{3, 30, 7, {0,46.8,45.898918964419,47.696014502038,600000,200000}, 0,-168,-60,320, {0,0,0,0,2.337229166667}, 0,0,0,0,0,0,0,0}, -113967455.416715, -108367759.648713, 115167455.416715, 120767151.184716}, +{{3, 30, 7, {0,46.8,45.898918964419,47.696014502038,600000,2200000}, 0,-168,-60,320, {0,0,0,0,2.337229166667}, 0,0,0,0,0,0,0,0}, -113967455.416715, -106367759.648713, 115167455.416715, 122767151.184716}, +{{3, 30, 7, {0,49.5,48.598522847174,50.395911631678,600000,1200000}, 0,-168,-60,320, {0,0,0,0,2.337229166667}, 0,0,0,0,0,0,0,0}, -124264257.877732, -118206641.203686, 125464257.877732, 131521874.551778}, +{{3, 30, 7, {0,49.5,48.598522847174,50.395911631678,600000,200000}, 0,-168,-60,320, {0,0,0,0,2.337229166667}, 0,0,0,0,0,0,0,0}, -124264257.877732, -119206641.203686, 125464257.877732, 130521874.551778}, +{{3, 4, 7, {17,29.77930555,42,56,2679984.29,-484330}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -119981631.268354, -115489555.67494, 125341599.848354, 129833675.441769}, +{{3, 4, 7, {4.3569397222,90,49.8333333333,51.1666666667,150000.01256,5400088.4378}, 0,81,120,129, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -128761663.907607, -123511575.482367, 129061663.932727, 134311752.357967}, +{{3, 4, 7, {4.367975,90,49.8333333333,51.1666666667,150000,5400000}, 0,81,120,129, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -128761663.920167, -123511663.920167, 129061663.920167, 134311663.920167}, +{{3, 6, 7, {23,-23,-18,-32,0,0}, 0,-136,-108,-292, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -61564419.064164, -72412828.2264313, 61564419.064164, 47794154.9885407}, +{{3, 7, 7, {-68.5,44,46,60,0.99999912,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -139220351.696306, -133454155.398424, 139220353.696304, 144986549.994186}, +{{3, 7, 7, {-96,23,20,60,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -89717066.7318629, -80920710.9207624, 89717066.7318629, 98513422.5429635}, +{{3, 7, 7, {-96,23,33,45,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -89295530.2887796, -79679575.056002, 89295530.2887796, 98911485.5215573}, +{{3, 7, 7, {-96,39,33,45,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -89295530.2887796, -81466209.2421514, 89295530.2887796, 97124851.3354078}, +{{3, 7, 8, {-100,41.3333333333,41.85,42.8166666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -324055199.858072, -302686222.906259, 328055199.858072, 349424176.809884}, +{{3, 7, 8, {-100,43.8333333333,44.4166666667,45.6833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -353482329.11673, -334115632.201571, 357482329.11673, 376849026.031889}, +{{3, 7, 8, {-100.3333333333,29.6666666667,30.1166666667,31.8833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -231224626.213763, -197887504.402976, 235224626.213763, 268561748.024549}, +{{3, 7, 8, {-100.3333333333,42.3333333333,42.8333333333,44.4,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -337599541.873847, -317137605.090357, 341599541.873847, 362061478.657338}, +{{3, 7, 8, {-100.5,45.6666666667,46.1833333333,47.4833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -374325734.927512, -356238757.711242, 378325734.927512, 396412712.143782}, +{{3, 7, 8, {-100.5,47,47.4333333333,48.7333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -389648664.421659, -372433148.680405, 393648664.421659, 410864180.162913}, +{{3, 7, 8, {-101.5,34,34.65,36.1833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -261737418.19268, -233764458.26155, 265737418.19268, 293710378.12381}, +{{3, 7, 8, {-105.5,36.6666666667,37.2333333333,38.4333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -281472391.565436, -256070159.37991, 285472391.565436, 310874623.750962}, +{{3, 7, 8, {-105.5,37.8333333333,38.45,39.75,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -292682801.597057, -268439748.039304, 296682801.597057, 320925855.154811}, +{{3, 7, 8, {-105.5,39.3333333333,39.7166666667,40.7833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -303388496.172414, -280302427.035458, 307388496.172414, 330474565.30937}, +{{3, 7, 8, {-109.5,44,44.8666666667,46.4,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -360164256.91475, -341067435.395783, 364164256.91475, 383261078.433716}, +{{3, 7, 8, {-109.5,45.8333333333,46.45,47.8833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -378353013.025643, -360433206.501952, 382353013.025643, 400272819.549334}, +{{3, 7, 8, {-109.5,47,47.85,48.7166666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -392158606.473501, -375000733.125562, 396158606.473501, 413316479.821439}, +{{3, 7, 8, {-111.5,36.6666666667,37.2166666667,38.35,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -281043066.114894, -255610253.091634, 285043066.114894, 310475879.138154}, +{{3, 7, 8, {-111.5,38.3333333333,39.0166666667,40.65,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -299444370.803389, -275780256.64851, 303444370.803389, 327108484.958269}, +{{3, 7, 8, {-111.5,40.3333333333,40.7166666667,41.7833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -313102102.853289, -290872992.274509, 317102102.853289, 339331213.432069}, +{{3, 7, 8, {-116.25,32.1666666667,32.7833333333,33.8833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -246466336.059689, -216195067.142672, 250466336.059689, 280737604.976705}, +{{3, 7, 8, {-118,33.5,34.0333333333,35.4666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -256676923.297906, -228027497.817793, 260676923.297906, 289326348.778019}, +{{3, 7, 8, {-118.3333333333,34.1333333333,33.8666666667,34.4166666667,4186692.58,4160926.74}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -250023955.862257, -219155253.035527, 258397341.022257, 289266043.848988}, +{{3, 7, 8, {-119,35.3333333333,36,37.25,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -271335689.379641, -244682757.78249, 275335689.379641, 301988620.976793}, +{{3, 7, 8, {-120.5,36.5,37.0666666667,38.4333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -280753380.155715, -255240388.034962, 284753380.155715, 310266372.276468}, +{{3, 7, 8, {-120.5,41.6666666667,42.3333333333,44,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -332774131.475311, -311885463.700041, 336774131.475311, 357662799.250581}, +{{3, 7, 8, {-120.5,43.6666666667,44.3333333333,46,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -354803161.418059, -335419308.868092, 358803161.418059, 378187013.968026}, +{{3, 7, 8, {-120.5,45.3333333333,45.8333333333,47.3333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -371328614.306844, -353039494.544633, 375328614.306844, 393617734.069055}, +{{3, 7, 8, {-120.8333333333,47,47.5,48.7333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -390066021.607763, -372860157.907014, 394066021.607763, 411271885.308512}, +{{3, 7, 8, {-122,37.6666666667,38.3333333333,39.8333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -292528160.248962, -268215917.695861, 296528160.248962, 320840402.802063}, +{{3, 7, 8, {-122,39.3333333333,40,41.6666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -308999499.517601, -286207062.905244, 312999499.517601, 335791936.129957}, +{{3, 7, 8, {-176,51,51.8333333333,53.8333333333,3000000,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -452044058.020189, -438479429.247576, 458044058.020189, 471608686.792802}, +{{3, 7, 8, {-66.4333333333,18.4333333333,18.0333333333,18.4333333333,500000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -186287246.382945, -123317624.596919, 187287246.382945, 191870266.743549}, +{{3, 7, 8, {-66.4333333333,18.4333333333,18.0333333333,18.4333333333,500000,100000}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -186287246.382945, -123217624.596919, 187287246.382945, 191970266.743549}, +{{3, 7, 8, {-70.5,41,41.2833333333,41.4833333333,800000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -315632009.889084, -292507560.721895, 317232009.889084, 340356459.056272}, +{{3, 7, 8, {-71.5,41,41.7166666667,42.6833333333,600000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -324083153.067583, -301133676.4023, 325283153.067583, 348232629.732865}, +{{3, 7, 8, {-72.75,40.8333333333,41.2,41.8666666667,600000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -317327164.550983, -294012777.09286, 318527164.550983, 341841552.009105}, +{{3, 7, 8, {-74,40.5,40.6666666667,41.0333333333,2000000,100000}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -309176711.160841, -286714165.719965, 313176711.160841, 335639256.601717}, +{{3, 7, 8, {-77,37.8333333333,38.3,39.45,800000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -291849291.606495, -266280179.434122, 293449291.606495, 319018403.778868}, +{{3, 7, 8, {-77.75,39.3333333333,39.9333333333,40.9666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -305301262.728919, -282316435.790654, 309301262.728919, 332286089.667183}, +{{3, 7, 8, {-77.75,40.1666666667,40.8833333333,41.95,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -314757958.485081, -292546908.072089, 318757958.485081, 340969008.898072}, +{{3, 7, 8, {-78.5,36.3333333333,36.7666666667,37.9666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -277493267.234049, -251681955.591517, 281493267.234049, 307304578.87658}, +{{3, 7, 8, {-78.5,37.6666666667,38.0333333333,39.2,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -288335380.041045, -263758935.876341, 292335380.041045, 316911824.205748}, +{{3, 7, 8, {-79,33.75,34.3333333333,36.1666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -260450504.74125, -232266866.952514, 264450504.74125, 292634142.529987}, +{{3, 7, 8, {-79.5,38.5,39,40.25,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -297507509.009345, -273792382.933741, 301507509.009345, 325222635.08495}, +{{3, 7, 8, {-81,31.8333333333,32.3333333333,33.6666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -244165860.608621, -213488972.345334, 248165860.608621, 278842748.871908}, +{{3, 7, 8, {-81,33,33.7666666667,34.9666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -253843797.715595, -224716073.21255, 257843797.715595, 286971522.21864}, +{{3, 7, 8, {-81,37,37.4833333333,38.8833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -284507191.09272, -259436571.044567, 288507191.09272, 313577811.140872}, +{{3, 7, 8, {-82.5,38,38.7333333333,40.0333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -295273305.101978, -271246233.791215, 299273305.101978, 323300376.41274}, +{{3, 7, 8, {-82.5,39.6666666667,40.4333333333,41.7,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -311290263.635938, -288731104.977908, 315290263.635938, 337849422.293968}, +{{3, 7, 8, {-84.25,37.5,37.9666666667,38.9666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -287005538.125454, -262281485.830837, 291005538.125454, 315729590.42007}, +{{3, 7, 8, {-84.5,29,29.5833333333,30.75,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -226275614.557084, -191820690.371176, 230275614.557084, 264730538.742992}, +{{3, 7, 8, {-85.75,36.3333333333,36.7333333333,37.9333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -277212156.180922, -251379920.019457, 281212156.180922, 307044392.342388}, +{{3, 7, 8, {-86,34.6666666667,35.25,36.4166666667,2000000,100000}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -264990397.68906, -237455247.728469, 268990397.68906, 296525547.64965}, +{{3, 7, 8, {-90,42,42.7333333333,44.0666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -335270989.964058, -314598855.147961, 339270989.964058, 359943124.780154}, +{{3, 7, 8, {-90,43.8333333333,44.25,45.5,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -351502026.802048, -332071112.456575, 355502026.802048, 374932941.147521}, +{{3, 7, 8, {-90,45.1666666667,45.5666666667,46.7666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -366394237.721428, -347905058.027747, 370394237.721428, 388883417.41511}, +{{3, 7, 8, {-91.3333333333,25.6666666667,26.1666666667,27.8333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -209731525.091816, -170154762.702282, 213731525.091816, 253308287.48135}, +{{3, 7, 8, {-91.3333333333,28.6666666667,29.3,30.67,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -225224715.171064, -190451550.801895, 229224715.171064, 263997879.540232}, +{{3, 7, 8, {-92,32.6666666667,33.3,34.7666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -251416565.855924, -221904841.787545, 255416565.855924, 284928289.924303}, +{{3, 7, 8, {-92,34.3333333333,34.9333333333,36.2333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -263032493.27188, -235299609.879708, 267032493.27188, 294765376.664052}, +{{3, 7, 8, {-92.5,30.6666666667,31.1666666667,32.6666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -236977085.666136, -204897455.65052, 240977085.666136, 273056715.681752}, +{{3, 7, 8, {-93.1,46.5,47.0333333333,48.6333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -386533413.283953, -369062013.990686, 390533413.283953, 408004812.577219}, +{{3, 7, 8, {-93.5,40,40.6166666667,41.7833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -312606167.17083, -290232070.277002, 316606167.17083, 338980264.064658}, +{{3, 7, 8, {-93.5,41.5,42.0666666667,43.2666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -327512222.250473, -306349761.225561, 331512222.250473, 352674683.275385}, +{{3, 7, 8, {-94,43,43.7833333333,45.2166666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -347294715.331617, -327420366.588323, 351294715.331617, 371169064.074911}, +{{3, 7, 8, {-94.25,45,45.6166666667,47.05,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -368358691.706647, -349865234.580916, 372358691.706647, 390852148.832379}, +{{3, 7, 8, {-97.5,31.6666666667,32.1333333333,33.9666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -244498272.760526, -213806618.399239, 248498272.760526, 279189927.121813}, +{{3, 7, 8, {-98,33.3333333333,33.9333333333,35.2333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -255439079.607865, -226601046.512961, 259439079.607865, 288277112.70277}, +{{3, 7, 8, {-98,35,35.5666666667,36.7666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -267633825.34702, -240550993.657801, 271633825.34702, 298716657.03624}, +{{3, 7, 8, {-98,38.3333333333,38.7166666667,39.7833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -294053624.246013, -270074545.620282, 298053624.246013, 322032702.871744}, +{{3, 7, 8, {-98.5,25.6666666667,26.1666666667,27.8333333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -209731525.091816, -170154762.702282, 213731525.091816, 253308287.48135}, +{{3, 7, 8, {-98.5,36.6666666667,38.5666666667,37.2666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -282190068.629093, -256838547.449771, 286190068.629093, 311541589.808415}, +{{3, 7, 8, {-99,27.8333333333,28.3833333333,30.2833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -221554461.124832, -185747019.927933, 225554461.124832, 261361902.321731}, +{{3, 7, 8, {-99.5,39.6666666667,40.2833333333,41.7166666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -310633016.402964, -288042234.606454, 314633016.402964, 337223798.199475}, +{{3, 8, 8, {-84.3333333333,41.5,42.1,43.6666666667,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -329791001.829521, -308721404.605132, 333791001.829521, 354860599.053909}, +{{3, 8, 8, {-84.3333333333,43.3166666667,44.1833333333,45.7,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -352264889.757925, -332670121.355321, 356264889.757925, 375859658.160529}, +{{3, 8, 8, {-87,44.7833333333,45.4833333333,47.0833333333,2000000,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -367778976.228709, -349189556.133667, 371778976.228709, 390368396.32375}, +{{4, 7, 7, {0,90,90,0,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -9020145.99449487, -9020145.99449487, 9020145.99449487, 9020145.99449487}, +{{6, 2, 1, {134,-90,-18,-36,0,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25412.1329074842, -30820.2630332478, 25412.1329074842, 20004.0027817205}, +{{6, 2, 1, {147,-32.5,-29.5,-35.5,0,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -23611.4811992266, -33623.6505009443, 23611.4811992266, 13599.3118975089}, +{{6, 2, 7, {134,-90,-18,-36,0,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25412132.9074842, -30820263.0332478, 25412132.9074842, 20004002.7817205}, +{{6, 7, 7, {-96,23,20,60,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -21729866.6858831, -12546277.7889483, 21729866.6858831, 30913455.5828178}, +{{6, 7, 7, {-96,23,29.5,45.5,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -22421877.8189369, -12546277.7889483, 22421877.8189369, 32297477.8489255}, +{{7, 0, 7, {-133.6666666667,57,-36.8698976458,0.9999,5000000,-5000000}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, 715546.220413176, -14325907.9924165, 9284453.77958682, 4325907.99241646}, +{{7, 0, 8, {-133.6666666667,57,-36.8698976458,0.9999,16404166.67,-16404166.67}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, 2347587.89480556, -47000916.4751197, 30460745.4451944, 14192583.1351197}, +{{7, 7, 8, {-133.6666666667,57,-36.8698976458,0.9999,16404166.6667,-16404166.6667}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, 2347270.72925546, -47001747.5537593, 30461062.6041445, 14193414.2203593}, +{{8, 0, 3, {-110.1666666667,31,0.9999,700000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26361414.1707081, -44066918.5002725, 27761414.1707081, 21556214.9175316}, +{{8, 0, 3, {-111.9166666667,31,0.9999,700000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26361414.1707081, -44066918.5002725, 27761414.1707081, 21556214.9175316}, +{{8, 0, 3, {-113.75,31,0.9999333333,700000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26362316.3071587, -44068387.5429912, 27762316.3071587, 21556933.5291715}, +{{8, 0, 7, {-104.3333333333,31,0.9999090909,165000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8083394.03137459, -13431718.8763978, 8413394.03137459, 6570394.04308942}, +{{8, 0, 7, {-105,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604573, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-105.1666666667,40.5,0.9999375,200000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8048628.38213008, -14486109.042394, 8448628.38213009, 5516572.17078242}, +{{8, 0, 7, {-106.25,31,0.9999,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748319.03923183, -13431596.758883, 8748319.03923182, 6570334.30686364}, +{{8, 0, 7, {-107.3333333333,40.5,0.9999375,400000,100000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7848628.38213008, -14386109.042394, 8648628.38213008, 5616572.17078242}, +{{8, 0, 7, {-107.8333333333,31,0.9999166667,830000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7418456.52523935, -13431820.641665, 9078456.52523936, 6570443.82360611}, +{{8, 0, 7, {-108.75,40.5,0.9999375,600000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7648628.38213009, -14486109.042394, 8848628.38213009, 5516572.17078242}, +{{8, 0, 7, {-110.0833333333,40.5,0.9999375,800000,100000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7448628.38213009, -14386109.042394, 9048628.38213008, 5616572.17078242}, +{{8, 0, 7, {-110.1666666667,31,0.9999,213360,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8034959.03923183, -13431596.758883, 8461679.03923182, 6570334.30686364}, +{{8, 0, 7, {-111,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-111.9166666667,31,0.9999,213360,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8034959.03923183, -13431596.758883, 8461679.03923182, 6570334.30686364}, +{{8, 0, 7, {-112.1666666667,41.6666666667,0.9999473684,200000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8048709.78798227, -14615809.9655587, 8448709.78798228, 5387068.65441496}, +{{8, 0, 7, {-113.75,31,0.9999333333,213360,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8035234.01042198, -13432044.5231037, 8461954.01042198, 6570553.33969148}, +{{8, 0, 7, {-114,41.6666666667,0.9999473684,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748709.78798228, -14615809.9655587, 8748709.78798228, 5387068.65441496}, +{{8, 0, 7, {-115.5833333333,34.75,0.9999,200000,8000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8048319.03923182, -5847439.17666561, 8448319.03923183, 14154491.8890811}, +{{8, 0, 7, {-115.75,41.6666666667,0.9999333333,800000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7448594.01042197, -14615604.8204071, 9048594.01042198, 5386993.0423881}, +{{8, 0, 7, {-116.6666666667,34.75,0.9999,500000,6000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748319.03923183, -7847439.17666561, 8748319.03923183, 12154491.8890811}, +{{8, 0, 7, {-117,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-118.5833333333,34.75,0.9999,800000,4000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7448319.03923183, -9847439.17666561, 9048319.03923182, 10154491.8890811}, +{{8, 0, 7, {-123,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604573, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-129,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-135,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604573, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-141,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604573, 9997964.94315451}, +{{8, 0, 7, {-142,54,0.9999,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748319.03923183, -15986282.9700963, 8748319.03923183, 4015648.0956504}, +{{8, 0, 7, {-146,54,0.9999,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748319.03923182, -15986282.9700963, 8748319.03923183, 4015648.0956504}, +{{8, 0, 7, {-147,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604573, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-15,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-150,54,0.9999,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748319.03923183, -15986282.9700963, 8748319.03923183, 4015648.0956504}, +{{8, 0, 7, {-153,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-154,54,0.9999,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748319.03923182, -15986282.9700963, 8748319.03923183, 4015648.0956504}, +{{8, 0, 7, {-155.5,18.8333333333,0.9999666667,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748868.98243704, -12084782.4965247, 8748868.98243704, 7918482.16531935}, +{{8, 0, 7, {-156.6666666667,20.3333333333,0.9999666667,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748868.98243704, -12250825.7354572, 8748868.98243703, 7752438.92638694}, +{{8, 0, 7, {-158,21.1666666667,0.99999,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7749061.46218765, -12343372.1823083, 8749061.46218765, 7660359.23726966}, +{{8, 0, 7, {-158,54,0.9999,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748319.03923182, -15986282.9700963, 8748319.03923182, 4015648.0956504}, +{{8, 0, 7, {-159,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-159.5,21.8333333333,0.99999,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7749061.46218765, -12417187.1755718, 8749061.46218765, 7586544.24400615}, +{{8, 0, 7, {-160.1666666667,21.6666666667,1,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7749143.95362718, -12398856.862762, 8749143.95362719, 7605074.59613055}, +{{8, 0, 7, {-162,54,0.9999,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748319.03923183, -15986282.9700963, 8748319.03923183, 4015648.0956504}, +{{8, 0, 7, {-165,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604573, 9997964.94315451}, +{{8, 0, 7, {-166,54,0.9999,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748319.03923182, -15986282.9700963, 8748319.03923183, 4015648.0956504}, +{{8, 0, 7, {-170,54,0.9999,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748319.03923182, -15986282.9700963, 8748319.03923183, 4015648.0956504}, +{{8, 0, 7, {-171,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-177,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-2,49,0.9996012717,400000,-100000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7845854.7864821, -15525440.3489618, 8645854.7864821, 4470514.97634689}, +{{8, 0, 7, {-21,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-27,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-33,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604573, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-39,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-45,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-51,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-55.5,0,0.9999,304800,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943519.03923183, -10000965.5328733, 8553119.03923183, 10000965.5328733}, +{{8, 0, 7, {-57,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-58.5,0,0.9999,304800,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943519.03923183, -10000965.5328733, 8553119.03923182, 10000965.5328733}, +{{8, 0, 7, {-61.5,0,0.9999,304800,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943519.03923182, -10000965.5328733, 8553119.03923183, 10000965.5328733}, +{{8, 0, 7, {-63,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-64.5,0,0.9999,304800,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943519.03923182, -10000965.5328733, 8553119.03923182, 10000965.5328733}, +{{8, 0, 7, {-67.5,0,0.9999,304800,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943519.03923183, -10000965.5328733, 8553119.03923182, 10000965.5328733}, +{{8, 0, 7, {-68.5,43.6666666667,0.9999,300000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948319.03923182, -14837267.8945768, 8548319.03923182, 5164663.17116993}, +{{8, 0, 7, {-69,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-70.1666666667,42.8333333333,0.9999666667,900000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7348868.98243704, -14745678.889568, 9148868.98243704, 5257585.77227607}, +{{8, 0, 7, {-70.5,0,0.9999,304800,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943519.03923183, -10000965.5328733, 8553119.03923182, 10000965.5328733}, +{{8, 0, 7, {-71.5,41.0833333333,0.99999375,100000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8149092.39647748, -14551702.6314571, 8349092.39647748, 5452103.80286384}, +{{8, 0, 7, {-71.6666666667,42.5,0.9999666667,300000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948868.98243704, -14708651.3754574, 8548868.98243704, 5294613.28638668}, +{{8, 0, 7, {-72.5,42.5,0.9999642857,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748849.34122529, -14708616.3529911, 8748849.34122528, 5294600.67949221}, +{{8, 0, 7, {-73.5,0,0.9999,304800,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943519.03923182, -10000965.5328733, 8553119.03923183, 10000965.5328733}, +{{8, 0, 7, {-74.5,38.8333333333,0.9999,150000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8098319.03923183, -14300537.2022438, 8398319.03923182, 5701393.86350293}, +{{8, 0, 7, {-74.5,38.8333333334,0.9999,150000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8098319.03923183, -14300537.2022549, 8398319.03923182, 5701393.86349183}, +{{8, 0, 7, {-75,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-75.4166666667,38,0.999995,200000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8049102.70790742, -14209392.7012779, 8449102.70790742, 5794438.73795742}, +{{8, 0, 7, {-76.5,0,0.9999,304800,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943519.03923183, -10000965.5328733, 8553119.03923182, 10000965.5328733}, +{{8, 0, 7, {-76.5833333333,40,0.9999375,250000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7998628.38213008, -14430592.791401, 8498628.38213008, 5572088.42177536}, +{{8, 0, 7, {-78.5833333333,40,0.9999375,350000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7898628.38213008, -14430592.791401, 8598628.38213008, 5572088.42177536}, +{{8, 0, 7, {-79.5,0,0.9999,304800,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943519.03923182, -10000965.5328733, 8553119.03923183, 10000965.5328733}, +{{8, 0, 7, {-81,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604573, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-81,24.3333333333,0.9999411765,200000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8048658.71010783, -12693427.8789688, 8448658.71010783, 7309326.87866157}, +{{8, 0, 7, {-82,24.3333333333,0.9999411765,200000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8048658.71010783, -12693427.8789688, 8448658.71010783, 7309326.87866157}, +{{8, 0, 7, {-82.1666666667,30,0.9999,200000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8048319.03923182, -13320746.9194058, 8448319.03923183, 6681184.14634087}, +{{8, 0, 7, {-84.1666666667,30,0.9999,700000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7548319.03923183, -13320746.9194058, 8948319.03923182, 6681184.14634087}, +{{8, 0, 7, {-85.6666666667,37.5,0.9999666667,100000,250000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8148868.98243704, -13903496.0736175, 8348868.98243704, 6099768.58822661}, +{{8, 0, 7, {-85.8333333333,30.5,0.99996,200000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8048813.98786904, -13376972.3620314, 8448813.98786904, 6626158.9396028}, +{{8, 0, 7, {-87,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-87.0833333333,37.5,0.9999666667,900000,250000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7348868.98243704, -13903496.0736175, 9148868.98243703, 6099768.58822661}, +{{8, 0, 7, {-87.5,30,0.9999333333,600000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7648594.01042198, -13321190.988266, 8848594.01042197, 6681406.87452919}, +{{8, 0, 7, {-88.3333333333,36.6666666667,0.999975,300000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948937.72502834, -14061133.6597386, 8548937.72502835, 5942297.70086748}, +{{8, 0, 7, {-88.8333333333,29.5,0.99995,300000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948731.4964295, -13265991.6727511, 8548731.4964295, 6736939.58956854}, +{{8, 0, 7, {-9,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {-90.1666666667,36.6666666667,0.9999411765,700000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7548658.71010783, -14060658.0510941, 8948658.71010783, 5942096.70653633}, +{{8, 0, 7, {-90.3333333333,29.5,0.99995,700000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7548731.49642951, -13265991.6727511, 8948731.4964295, 6736939.58956854}, +{{8, 0, 7, {-90.5,35.8333333333,0.9999333333,250000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7998594.01042198, -13968084.2221719, 8498594.01042197, 6034513.64062328}, +{{8, 0, 7, {-92.5,35.8333333333,0.9999333333,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748594.01042198, -13968084.2221719, 8748594.01042197, 6034513.64062328}, +{{8, 0, 7, {-93,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604573, 9997964.94315451}, +{{8, 0, 7, {-94.5,36.1666666667,0.9999411765,850000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7398658.71010783, -14005177.9421902, 9098658.71010783, 5997576.81544022}, +{{8, 0, 7, {-99,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {105,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, 2035.0568454858, 8745844.29604573, 19997964.9431545}, +{{8, 0, 7, {111,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, 2035.0568454858, 8745844.29604574, 19997964.9431545}, +{{8, 0, 7, {117,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, 2035.0568454858, 8745844.29604574, 19997964.9431545}, +{{8, 0, 7, {12,0,0.99995,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7748731.4964295, -10001465.6311598, 8748731.4964295, 10001465.6311598}, +{{8, 0, 7, {123,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, 2035.0568454858, 8745844.29604573, 19997964.9431545}, +{{8, 0, 7, {129,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, 2035.0568454858, 8745844.29604574, 19997964.9431545}, +{{8, 0, 7, {135,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604573, 2035.0568454858, 8745844.29604573, 19997964.9431545}, +{{8, 0, 7, {141,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604573, 2035.0568454858, 8745844.29604574, 19997964.9431545}, +{{8, 0, 7, {147,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, 2035.0568454858, 8745844.29604573, 19997964.9431545}, +{{8, 0, 7, {15,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {15,0,1,900000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7349143.95362719, -10001965.7294463, 9149143.95362719, 10001965.7294463}, +{{8, 0, 7, {153,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, 2035.0568454858, 8745844.29604574, 19997964.9431545}, +{{8, 0, 7, {159,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, 2035.0568454858, 8745844.29604574, 19997964.9431545}, +{{8, 0, 7, {165,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604573, 2035.0568454858, 8745844.29604574, 19997964.9431545}, +{{8, 0, 7, {9,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, -9997964.94315451, 8745844.29604574, 9997964.94315451}, +{{8, 0, 7, {9.5,0,0.99995,200000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8048731.4964295, -10001465.6311598, 8448731.49642951, 10001465.6311598}, +{{8, 0, 7, {99,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29604574, 2035.0568454858, 8745844.29604574, 19997964.9431545}, +{{8, 0, 8, {-104.3333333333,31,0.9999090909,541337.5,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26520268.5846015, -44067231.0136484, 27602943.5846015, 21556367.7897025}, +{{8, 0, 8, {-105.1666666667,40.5,0.9999375,656166.6667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26406208.2836718, -47526509.4165876, 27718541.6170718, 18098953.863642}, +{{8, 0, 8, {-106.25,31,0.9999,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25420943.3808798, -44066830.3664355, 28701776.7148797, 21556171.8051018}, +{{8, 0, 8, {-107.3333333333,40.5,0.9999375,1312333.333,328083.3333,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25750041.6173718, -47198426.0832876, 28374708.2833718, 18427037.196942}, +{{8, 0, 8, {-107.8333333333,31,0.9999166667,2723091.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -24338719.4495561, -44067564.8885294, 29784902.7835561, 21556531.111281}, +{{8, 0, 8, {-108.75,40.5,0.9999375,1968500,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25093874.9503718, -47526509.4165876, 29030874.9503718, 18098953.863642}, +{{8, 0, 8, {-110.0833333333,40.5,0.9999375,2624666.667,328083.3333,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -24437708.2833718, -47198426.0832876, 29687041.6173718, 18427037.196942}, +{{8, 0, 8, {-110.1666666667,31,0.9999,699998.6,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26361361.4478798, -44066830.3664355, 27761358.6478797, 21556171.8051018}, +{{8, 0, 8, {-111.9166666667,31,0.9999,699998.6,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26361361.4478797, -44066830.3664355, 27761358.6478797, 21556171.8051018}, +{{8, 0, 8, {-112.1666666667,41.6666666667,0.9999473684,656166.6667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26406475.3627052, -47952036.5286703, 27718808.6961052, 17674074.4103597}, +{{8, 0, 8, {-113.75,31,0.9999333333,699998.6,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26362263.5825261, -44068299.4062161, 27762260.7825261, 21556890.4153045}, +{{8, 0, 8, {-114,41.6666666667,0.9999473684,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25422225.3624052, -47952036.5286703, 28703058.6964052, 17674074.4103597}, +{{8, 0, 8, {-115.5833333333,34.75,0.9999,656166.6667,26246666.67,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26405193.3811797, -19184473.3621104, 27717526.7145797, 46438528.8094268}, +{{8, 0, 8, {-115.75,41.6666666667,0.9999333333,2624666.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -24437595.5155261, -47951363.4816189, 29686928.8495261, 17673826.3399016}, +{{8, 0, 8, {-116.6666666667,34.75,0.9999,1640416.667,19685000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25420943.3808797, -25746140.0321104, 28701776.7148797, 39876862.1394268}, +{{8, 0, 8, {-118.5833333333,34.75,0.9999,2624666.667,13123333.33,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -24436693.3808797, -32307806.7021104, 29686026.7148797, 33315195.4694268}, +{{8, 0, 8, {-142,54,0.9999,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25420943.3808797, -52448330.0443909, 28701776.7148797, 13174672.1271464}, +{{8, 0, 8, {-146,54,0.9999,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25420943.3808797, -52448330.0443909, 28701776.7148797, 13174672.1271464}, +{{8, 0, 8, {-150,54,0.9999,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25420943.3808797, -52448330.0443909, 28701776.7148797, 13174672.1271464}, +{{8, 0, 8, {-154,54,0.9999,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25420943.3808797, -52448330.0443909, 28701776.7148797, 13174672.1271464}, +{{8, 0, 8, {-155.5,18.8333333333,0.9999666667,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25422747.6528788, -39648157.2406816, 28703580.9868788, 25979220.2373852}, +{{8, 0, 8, {-156.6666666667,20.3333333333,0.9999666667,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25422747.6528789, -40192917.4337457, 28703580.9868788, 25434460.0443211}, +{{8, 0, 8, {-158,21.1666666667,0.99999,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25423379.1468606, -40496546.9014566, 28704212.4808606, 25132361.9309422}, +{{8, 0, 8, {-158,54,0.9999,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25420943.3808797, -52448330.0443909, 28701776.7148797, 13174672.1271464}, +{{8, 0, 8, {-159.5,21.8333333333,0.99999,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25423379.1468606, -40738721.5918553, 28704212.4808606, 24890187.2405435}, +{{8, 0, 8, {-160.1666666667,21.6666666667,1,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25423649.7875252, -40678582.8905784, 28704483.1215252, 24950982.2374716}, +{{8, 0, 8, {-162,54,0.9999,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25420943.3808797, -52448330.0443909, 28701776.7148797, 13174672.1271464}, +{{8, 0, 8, {-166,54,0.9999,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25420943.3808797, -52448330.0443909, 28701776.7148797, 13174672.1271464}, +{{8, 0, 8, {-170,54,0.9999,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25420943.3808797, -52448330.0443909, 28701776.7148797, 13174672.1271464}, +{{8, 0, 8, {-68.5,43.6666666667,0.9999,984250,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26077110.0478797, -48678603.0841239, 28045610.0478797, 16944399.0874133}, +{{8, 0, 8, {-70.1666666667,42.8333333333,0.9999666667,2952750,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -24110414.3198788, -48378114.8235244, 30015914.3198788, 17249262.6545424}, +{{8, 0, 8, {-71.5,41.0833333333,0.99999375,328083.3333,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26735813.9708099, -47741711.0500389, 27391980.6374099, 17887443.8932291}, +{{8, 0, 8, {-71.6666666667,42.5,0.9999666667,984250,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26078914.3198788, -48256633.7209798, 28047414.3198788, 17370743.757087}, +{{8, 0, 8, {-72.5,42.5,0.9999642857,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25422683.2133366, -48256518.8181048, 28703516.5473366, 17370702.3959674}, +{{8, 0, 8, {-74.5,38.8333333333,0.9999,492125,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26569235.0478797, -46917679.1376948, 27553485.0478797, 18705323.0338425}, +{{8, 0, 8, {-74.5,38.8333333334,0.9999,492125,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26569235.0478797, -46917679.1377312, 27553485.0478797, 18705323.0338061}, +{{8, 0, 8, {-75.4166666667,38,0.999995,656166.6667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26407764.4674929, -46618649.2207758, 27720097.8008929, 19010587.7594486}, +{{8, 0, 8, {-76.5833333333,40,0.9999375,820208.3333,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26242166.6170718, -47344369.8497883, 27882583.2836718, 18281093.4304413}, +{{8, 0, 8, {-78.5833333333,40,0.9999375,1148291.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25914083.2833718, -47344369.8497883, 28210666.6173718, 18281093.4304413}, +{{8, 0, 8, {-81,24.3333333333,0.9999411765,656166.6667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26406307.7847121, -41645021.2995836, 27718641.1181121, 23980683.2677422}, +{{8, 0, 8, {-82,24.3333333333,0.9999411765,656166.6667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26406307.7847121, -41645021.2995836, 27718641.1181121, 23980683.2677422}, +{{8, 0, 8, {-82.1666666667,30,0.9999,656166.6667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26405193.3811797, -43703150.5180839, 27717526.7145797, 21919851.6534533}, +{{8, 0, 8, {-84.1666666667,30,0.9999,2296583.333,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -24764776.7148797, -43703150.5180839, 29357943.3808797, 21919851.6534533}, +{{8, 0, 8, {-85.6666666667,37.5,0.9999666667,328083.3333,820208.3333,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26735080.9865789, -45615053.3682267, 27391247.6531788, 20012324.1098401}, +{{8, 0, 8, {-85.8333333333,30.5,0.99996,656166.6667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26406817.225167, -43887616.8244314, 27719150.558567, 21739323.1210135}, +{{8, 0, 8, {-87.0833333333,37.5,0.9999666667,2952750,820208.3333,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -24110414.3198788, -45615053.3682267, 30015914.3198788, 20012324.1098401}, +{{8, 0, 8, {-87.5,30,0.9999333333,1968500,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25093762.1825261, -43704607.4340027, 29030762.1825261, 21920582.3875179}, +{{8, 0, 8, {-88.3333333333,36.6666666667,0.999975,984250,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26079139.8528638, -46132236.0153258, 28047639.8528638, 19495688.373596}, +{{8, 0, 8, {-88.8333333333,29.5,0.99995,984250,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26078463.2512025, -43523507.6796842, 28046963.2512025, 22102775.9701094}, +{{8, 0, 8, {-90.1666666667,36.6666666667,0.9999411765,2296583.333,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -24765891.1184121, -46130675.6226312, 29359057.7844121, 19495028.9446946}, +{{8, 0, 8, {-90.3333333333,29.5,0.99995,2296583.333,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -24766129.9182025, -43523507.6796842, 29359296.5842025, 22102775.9701094}, +{{8, 0, 8, {-90.5,35.8333333333,0.9999333333,820208.3333,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26242053.8492261, -45826956.318909, 27882470.5158261, 19798233.5026115}, +{{8, 0, 8, {-92.5,35.8333333333,0.9999333333,1640416.667,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25421845.5155261, -45826956.318909, 28702678.8495261, 19798233.5026115}, +{{8, 0, 8, {-94.5,36.1666666667,0.9999411765,2788708.333,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -24273766.1184121, -45948654.6320023, 29851182.7844121, 19677049.9353235}, +{{8, 10, 7, {12,0,1,4500000,0,0}, 0,582,105,414, {-1.04,-0.35,3.08,8.3,0}, 0,0,0,0,0,0,0,0}, -3748143.32560618, -10000855.7646457, 12748143.3256062, 10000855.7646457}, +{{8, 10, 7, {123,0,0.9996,500000,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7744844.06827594, -9996855.42233989, 8744844.06827594, 9996855.42233989}, +{{8, 10, 7, {124,26,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -12876121.1385208, 8247318.51127362, 7123590.21961776}, +{{8, 10, 7, {127.5,26,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -12876121.1385208, 8247318.51127362, 7123590.21961776}, +{{8, 10, 7, {129,0,0.9996,500000,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7744844.06827594, -9996855.42233989, 8744844.06827594, 9996855.42233989}, +{{8, 10, 7, {129.5,33,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -13651876.9787944, 8247318.51127362, 6347834.37934419}, +{{8, 10, 7, {131,26,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127363, -12876121.1385208, 8247318.51127362, 7123590.21961776}, +{{8, 10, 7, {131,33,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127363, -13651876.9787944, 8247318.51127362, 6347834.37934419}, +{{8, 10, 7, {132.166666,36,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -13984603.2178459, 8247318.51127362, 6015108.14029263}, +{{8, 10, 7, {133.5,33,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -13651876.9787944, 8247318.51127363, 6347834.37934419}, +{{8, 10, 7, {134.333333,36,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -13984603.2178459, 8247318.51127362, 6015108.14029263}, +{{8, 10, 7, {135,0,0.9996,500000,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7744844.06827594, -9996855.42233989, 8744844.06827594, 9996855.42233989}, +{{8, 10, 7, {136,20,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127363, -12211786.0141993, 8247318.51127362, 7787925.34393926}, +{{8, 10, 7, {136,36,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127363, -13984603.2178459, 8247318.51127362, 6015108.14029263}, +{{8, 10, 7, {137.166666,36,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -13984603.2178459, 8247318.51127362, 6015108.14029263}, +{{8, 10, 7, {138.5,36,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127363, -13984603.2178459, 8247318.51127362, 6015108.14029263}, +{{8, 10, 7, {139.833333,36,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -13984603.2178459, 8247318.51127362, 6015108.14029263}, +{{8, 10, 7, {140.25,44,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -14872697.8989466, 8247318.51127362, 5127013.45919198}, +{{8, 10, 7, {140.833333,40,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -14428497.5605602, 8247318.51127362, 5571213.7975784}, +{{8, 10, 7, {141,0,0.9996,500000,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7744844.06827594, -9996855.42233989, 8744844.06827595, 9996855.42233989}, +{{8, 10, 7, {142,26,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -12876121.1385208, 8247318.51127362, 7123590.21961776}, +{{8, 10, 7, {142.25,44,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -14872697.8989466, 8247318.51127362, 5127013.45919198}, +{{8, 10, 7, {144.25,44,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127363, -14872697.8989466, 8247318.51127362, 5127013.45919198}, +{{8, 10, 7, {147,0,0.9996,500000,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7744844.06827594, -9996855.42233989, 8744844.06827593, 9996855.42233989}, +{{8, 10, 7, {15,0,1,5500000,0,0}, 0,582,105,414, {-1.04,-0.35,3.08,8.3,0}, 0,0,0,0,0,0,0,0}, -2748143.32560618, -10000855.7646457, 13748143.3256062, 10000855.7646457}, +{{8, 10, 7, {15.8082777778,0,1,1500000,0,0}, 0,498,-36,568, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -6748143.32560618, -10000855.7646457, 9748143.32560618, 10000855.7646457}, +{{8, 10, 7, {15.8082777778,0,1,1500000,0,0}, 0,419.3836,99.3335,591.3451, {-0.850389, -1.817277, 7.862238, -0.99496, 0}, 0,0,0,0,0,0,0,0}, -6748143.32560618, -10000855.7646457, 9748143.32560618, 10000855.7646457}, +{{8, 10, 7, {153,0,0.9996,500000,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7744844.06827594, -9996855.42233989, 8744844.06827594, 9996855.42233989}, +{{8, 10, 7, {154,26,0.9999,0,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8247318.51127362, -12876121.1385208, 8247318.51127362, 7123590.21961776}, +{{8, 10, 7, {159,0,0.9996,500000,0,0}, 0,-128,481,664, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7744844.06827594, -9996855.42233989, 8744844.06827594, 9996855.42233989}, +{{8, 10, 7, {3,0,1,1500000,0,0}, 0,582,105,414, {-1.04,-0.35,3.08,8.3,0}, 0,0,0,0,0,0,0,0}, -6748143.32560618, -10000855.7646457, 9748143.32560618, 10000855.7646457}, +{{8, 10, 7, {6,0,1,2500000,0,0}, 0,582,105,414, {-1.04,-0.35,3.08,8.3,0}, 0,0,0,0,0,0,0,0}, -5748143.32560618, -10000855.7646457, 10748143.3256062, 10000855.7646457}, +{{8, 10, 7, {9,0,1,3500000,0,0}, 0,582,105,414, {-1.04,-0.35,3.08,8.3,0}, 0,0,0,0,0,0,0,0}, -4748143.32560618, -10000855.7646457, 11748143.3256062, 10000855.7646457}, +{{8, 13, 7, {-8,53.5,1.000035,200000,250000,0}, 0,506,-122,611, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8048349.95153666, -15680948.9714353, 8448349.95153666, 4321303.19013035}, +{{8, 2, 7, {105,0,0.9996,500000,10000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {105,0,0.9996,500000,10000000,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {111,0,0.9996,500000,10000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {111,0,0.9996,500000,10000000,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {117,0,0.9996,500000,10000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {117,0,0.9996,500000,10000000,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {123,0,0.9996,500000,10000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {123,0,0.9996,500000,10000000,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {129,0,0.9996,500000,10000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {129,0,0.9996,500000,10000000,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {135,0,0.9996,500000,10000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {135,0,0.9996,500000,10000000,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {141,0,0.9996,500000,10000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491762, 19998000.5903039}, +{{8, 2, 7, {141,0,0.9996,500000,10000000,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491762, 19998000.5903039}, +{{8, 2, 7, {141,0,0.99994,300000,5000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948679.10409615, -5001401.27077682, 8548679.10409616, 15001401.2707768}, +{{8, 2, 7, {143,0,0.99994,300000,5000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948679.10409616, -5001401.27077682, 8548679.10409615, 15001401.2707768}, +{{8, 2, 7, {145,0,0.99994,300000,5000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948679.10409615, -5001401.27077682, 8548679.10409616, 15001401.2707768}, +{{8, 2, 7, {147,0,0.9996,500000,10000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491762, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {147,0,0.9996,500000,10000000,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491762, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {147,0,0.99994,300000,5000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948679.10409616, -5001401.27077682, 8548679.10409615, 15001401.2707768}, +{{8, 2, 7, {149,0,0.99994,300000,5000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948679.10409615, -5001401.27077682, 8548679.10409616, 15001401.2707768}, +{{8, 2, 7, {149.0092948333,0,1.000086,200000,4510193.4939,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8049883.48350812, -5492668.06907988, 8449883.48350812, 14513055.0568799}, +{{8, 2, 7, {151,0,0.99994,300000,5000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948679.10409615, -5001401.27077682, 8548679.10409616, 15001401.2707768}, +{{8, 2, 7, {153,0,0.9996,500000,10000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {153,0,0.9996,500000,10000000,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {153,0,0.99994,300000,5000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948679.10409616, -5001401.27077682, 8548679.10409616, 15001401.2707768}, +{{8, 2, 7, {155,0,0.99994,300000,5000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948679.10409615, -5001401.27077682, 8548679.10409615, 15001401.2707768}, +{{8, 2, 7, {159,0,0.9996,500000,10000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {159,0,0.9996,500000,10000000,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {165,0,0.9996,500000,10000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {165,0,0.9996,500000,10000000,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {99,0,0.9996,500000,10000000,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 2, 7, {99,0,0.9996,500000,10000000,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 24, 7, {-33,0,0.9996,500000,10000000,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 24, 7, {-39,0,0.9996,500000,10000000,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 24, 7, {-45,0,0.9996,500000,10000000,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 24, 7, {-51,0,0.9996,500000,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, +{{8, 24, 7, {-51,0,0.9996,500000,10000000,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 24, 7, {-57,0,0.9996,500000,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, +{{8, 24, 7, {-57,0,0.9996,500000,10000000,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 24, 7, {-63,0,0.9996,500000,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, +{{8, 24, 7, {-63,0,0.9996,500000,10000000,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 24, 7, {-69,0,0.9996,500000,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, +{{8, 24, 7, {-69,0,0.9996,500000,10000000,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 24, 7, {-75,0,0.9996,500000,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, +{{8, 24, 7, {-75,0,0.9996,500000,10000000,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 24, 7, {-81,0,0.9996,500000,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, +{{8, 24, 7, {-81,0,0.9996,500000,10000000,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, 1999.40969607119, 8745874.38491761, 19998000.5903039}, +{{8, 28, 7, {-105,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-105,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-111,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-111,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-117,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-117,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-123,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-123,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-129,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-129,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-135,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597411, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-135,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597411, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-141,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597413, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-141,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597413, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-147,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597411, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-147,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597411, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-15,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-15,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-153,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-153,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-159,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-159,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-165,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-165,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-171,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597413, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-171,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597413, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-177,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-177,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-21,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-21,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-27,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-27,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-3,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-3,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-33,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-33,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-39,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-39,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-45,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-45,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-51,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-51,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-57,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-57,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-63,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-63,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-69,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-69,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-75,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-75,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-8.1319061111,39.6666666667,1,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8249143.95355554, -14394484.2826752, 8249143.95355554, 5609447.17638195}, +{{8, 28, 7, {-81,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597411, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-81,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597411, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-87,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-87,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-9,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-9,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-93,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-93,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {-99,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {-99,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {105,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {105,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {111,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {111,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {117,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {117,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {123,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {123,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {129,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {129,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {135,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {135,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {141,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597411, -9997964.94323674, 8745844.29597413, 9997964.94323674}, +{{8, 28, 7, {141,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597411, 2035.05676326129, 8745844.29597413, 19997964.9432367}, +{{8, 28, 7, {147,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597411, 9997964.94323674}, +{{8, 28, 7, {147,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597411, 19997964.9432367}, +{{8, 28, 7, {15,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {15,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {153,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {153,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {159,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {159,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {165,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {165,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {17,0,1,0,0,0}, 0,-134.73,-110.92,-292.66, {0,0,0,1,0}, 0,0,0,0,0,0,0,0}, -8249143.95355554, -10001965.7295286, 8249143.95355554, 10001965.7295286}, +{{8, 28, 7, {171,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597413, 9997964.94323674}, +{{8, 28, 7, {171,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597413, 19997964.9432367}, +{{8, 28, 7, {177,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {177,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {19,0,1,0,0,0}, 0,-134.73,-110.92,-292.66, {0,0,0,1,0}, 0,0,0,0,0,0,0,0}, -8249143.95355554, -10001965.7295286, 8249143.95355554, 10001965.7295286}, +{{8, 28, 7, {19,0,1,0,3700000,0}, 0,-134.73,-110.92,-292.66, {0,0,0,1,0}, 0,0,0,0,0,0,0,0}, -8249143.95355554, -6301965.72952855, 8249143.95355554, 13701965.7295286}, +{{8, 28, 7, {21,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {21,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {21,0,1,0,0,0}, 0,-134.73,-110.92,-292.66, {0,0,0,1,0}, 0,0,0,0,0,0,0,0}, -8249143.95355554, -10001965.7295286, 8249143.95355554, 10001965.7295286}, +{{8, 28, 7, {23,0,1,0,0,0}, 0,-134.73,-110.92,-292.66, {0,0,0,1,0}, 0,0,0,0,0,0,0,0}, -8249143.95355554, -10001965.7295286, 8249143.95355554, 10001965.7295286}, +{{8, 28, 7, {25,0,1,0,0,0}, 0,-134.73,-110.92,-292.66, {0,0,0,1,0}, 0,0,0,0,0,0,0,0}, -8249143.95355554, -10001965.7295286, 8249143.95355554, 10001965.7295286}, +{{8, 28, 7, {27,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {27,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {27,0,1,0,0,0}, 0,-134.73,-110.92,-292.66, {0,0,0,1,0}, 0,0,0,0,0,0,0,0}, -8249143.95355554, -10001965.7295286, 8249143.95355554, 10001965.7295286}, +{{8, 28, 7, {29,0,1,0,0,0}, 0,-134.73,-110.92,-292.66, {0,0,0,1,0}, 0,0,0,0,0,0,0,0}, -8249143.95355554, -10001965.7295286, 8249143.95355554, 10001965.7295286}, +{{8, 28, 7, {3,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {3,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {31,0,1,0,0,0}, 0,-134.73,-110.92,-292.66, {0,0,0,1,0}, 0,0,0,0,0,0,0,0}, -8249143.95355554, -10001965.7295286, 8249143.95355554, 10001965.7295286}, +{{8, 28, 7, {33,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {33,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {39,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {39,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {45,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {45,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {51,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {51,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {57,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {57,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {6.166666667,49.83333333,1,80000,100000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8169143.95355554, -15424274.8616842, 8329143.95355554, 4579656.59737291}, +{{8, 28, 7, {63,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {63,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {69,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {69,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {75,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {75,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {81,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {81,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {87,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {87,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {9,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {9,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {93,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {93,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 28, 7, {99,0,0.9996,500000,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, -9997964.94323674, 8745844.29597412, 9997964.94323674}, +{{8, 28, 7, {99,0,0.9996,500000,10000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745844.29597412, 2035.05676326129, 8745844.29597412, 19997964.9432367}, +{{8, 29, 0, {78,0,1,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -5125.7804133582, -6214.93337007411, 5125.7804133582, 6214.93337007411}, +{{8, 3, 7, {-171,0,1,32500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 24250718.46099, -10002137.4977586, 40749281.53901, 10002137.4977586}, +{{8, 3, 7, {-177,0,1,31500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 23250718.46099, -10002137.4977586, 39749281.53901, 10002137.4977586}, +{{8, 3, 7, {105,0,1,18500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 10250718.46099, -10002137.4977586, 26749281.53901, 10002137.4977586}, +{{8, 3, 7, {111,0,1,19500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 11250718.46099, -10002137.4977586, 27749281.53901, 10002137.4977586}, +{{8, 3, 7, {117,0,1,20500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 12250718.46099, -10002137.4977586, 28749281.53901, 10002137.4977586}, +{{8, 3, 7, {123,0,1,21500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 13250718.46099, -10002137.4977586, 29749281.53901, 10002137.4977586}, +{{8, 3, 7, {129,0,1,22500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 14250718.46099, -10002137.4977586, 30749281.53901, 10002137.4977586}, +{{8, 3, 7, {135,0,1,23500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 15250718.46099, -10002137.4977586, 31749281.53901, 10002137.4977586}, +{{8, 3, 7, {141,0,1,24500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 16250718.46099, -10002137.4977586, 32749281.53901, 10002137.4977586}, +{{8, 3, 7, {147,0,1,25500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 17250718.46099, -10002137.4977586, 33749281.53901, 10002137.4977586}, +{{8, 3, 7, {15,0,1,3500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, -4749281.53900998, -10002137.4977586, 11749281.53901, 10002137.4977586}, +{{8, 3, 7, {153,0,1,26500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 18250718.46099, -10002137.4977586, 34749281.53901, 10002137.4977586}, +{{8, 3, 7, {159,0,1,27500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 19250718.46099, -10002137.4977586, 35749281.53901, 10002137.4977586}, +{{8, 3, 7, {165,0,1,28500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 20250718.46099, -10002137.4977586, 36749281.53901, 10002137.4977586}, +{{8, 3, 7, {171,0,1,29500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 21250718.46099, -10002137.4977586, 37749281.53901, 10002137.4977586}, +{{8, 3, 7, {177,0,1,30500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 22250718.46099, -10002137.4977586, 38749281.53901, 10002137.4977586}, +{{8, 3, 7, {21,0,1,4500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, -3749281.53900997, -10002137.4977586, 12749281.53901, 10002137.4977586}, +{{8, 3, 7, {27,0,1,5500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, -2749281.53900998, -10002137.4977586, 13749281.53901, 10002137.4977586}, +{{8, 3, 7, {3,0,1,1500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, -6749281.53900998, -10002137.4977586, 9749281.53900998, 10002137.4977586}, +{{8, 3, 7, {33,0,1,6500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, -1749281.53900998, -10002137.4977586, 14749281.53901, 10002137.4977586}, +{{8, 3, 7, {39,0,1,7500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, -749281.539009975, -10002137.4977586, 15749281.53901, 10002137.4977586}, +{{8, 3, 7, {45,0,1,8500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 250718.460990024, -10002137.4977586, 16749281.53901, 10002137.4977586}, +{{8, 3, 7, {51,0,1,9500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 1250718.46099002, -10002137.4977586, 17749281.53901, 10002137.4977586}, +{{8, 3, 7, {57,0,1,10500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 2250718.46099002, -10002137.4977586, 18749281.53901, 10002137.4977586}, +{{8, 3, 7, {63,0,1,11500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 3250718.46099002, -10002137.4977586, 19749281.53901, 10002137.4977586}, +{{8, 3, 7, {69,0,1,12500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 4250718.46099003, -10002137.4977586, 20749281.53901, 10002137.4977586}, +{{8, 3, 7, {75,0,1,13500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 5250718.46099002, -10002137.4977586, 21749281.53901, 10002137.4977586}, +{{8, 3, 7, {81,0,1,14500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 6250718.46099003, -10002137.4977586, 22749281.53901, 10002137.4977586}, +{{8, 3, 7, {87,0,1,15500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 7250718.46099003, -10002137.4977586, 23749281.53901, 10002137.4977586}, +{{8, 3, 7, {9,0,1,2500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, -5749281.53900998, -10002137.4977586, 10749281.53901, 10002137.4977586}, +{{8, 3, 7, {93,0,1,16500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 8250718.46099003, -10002137.4977586, 24749281.53901, 10002137.4977586}, +{{8, 3, 7, {99,0,1,17500000,0,0}, 0,24,-123,-94, {-0.02,0.25,0.13,1.1,0}, 0,0,0,0,0,0,0,0}, 9250718.46099002, -10002137.4977586, 25749281.53901, 10002137.4977586}, +{{8, 4, 4, {171.5,-44,1,500000,500000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8521796.21510011, -5108460.96074551, 9521796.2151001, 16768810.0348996}, +{{8, 4, 4, {175.5,-39,1,300000,400000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8721796.2151001, -5815782.48470089, 9321796.21510011, 16061488.5109442}, +{{8, 4, 7, {-15,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {-3,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {-33,0,0.9996,500000,10000000,0}, 0,-206,172,-6, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, 1712.61611072717, 8746230.6469039, 19998287.3838893}, +{{8, 4, 7, {-39,0,0.9996,500000,10000000,0}, 0,-206,172,-6, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, 1712.61611072717, 8746230.6469039, 19998287.3838893}, +{{8, 4, 7, {-45,0,0.9996,500000,10000000,0}, 0,-206,172,-6, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, 1712.61611072717, 8746230.6469039, 19998287.3838893}, +{{8, 4, 7, {-51,0,0.9996,500000,0,0}, 0,-206,172,-6, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {-51,0,0.9996,500000,10000000,0}, 0,-206,172,-6, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, 1712.61611072717, 8746230.6469039, 19998287.3838893}, +{{8, 4, 7, {-57,0,0.9996,500000,0,0}, 0,-206,172,-6, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {-57,0,0.9996,500000,10000000,0}, 0,-206,172,-6, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, 1712.61611072717, 8746230.6469039, 19998287.3838893}, +{{8, 4, 7, {-63,0,0.9996,500000,0,0}, 0,-206,172,-6, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {-63,0,0.9996,500000,10000000,0}, 0,-206,172,-6, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, 1712.61611072717, 8746230.6469039, 19998287.3838893}, +{{8, 4, 7, {-69,0,0.9996,500000,0,0}, 0,-206,172,-6, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {-69,0,0.9996,500000,10000000,0}, 0,-206,172,-6, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, 1712.61611072717, 8746230.6469039, 19998287.3838893}, +{{8, 4, 7, {-75,0,0.9996,500000,10000000,0}, 0,-206,172,-6, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, 1712.61611072717, 8746230.6469039, 19998287.3838893}, +{{8, 4, 7, {-8.1319061111,39.6666666667,1,0,0,0}, 0,-303,-62,105, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8249530.45908754, -14394881.7325041, 8249530.45908754, 5609694.86591381}, +{{8, 4, 7, {-8.1319061111,39.6666666667,1,180.598,-86.99,0}, 0,-223,110,37, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8249349.86108754, -14394968.7225041, 8249711.05708754, 5609607.87591381}, +{{8, 4, 7, {-8.1319061111,39.6666666667,1,200000,300000,0}, 0,-303,-62,105, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8049530.45908754, -14094881.7325041, 8449530.45908754, 5909694.86591381}, +{{8, 4, 7, {-9,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {114.178555,22.3121333,0.99995,836694.05,819069.8,0}, 0,-156,-271,-189, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7412423.93256458, -11650990.6841779, 9085812.03256459, 8352585.68541009}, +{{8, 4, 7, {114.178555,22.3121333,1,836694.05,819069.8,0}, 0,-156,-271,-189, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7412836.40908754, -11651614.2183788, 9086224.50908754, 8352962.38003909}, +{{8, 4, 7, {15,0,0.9996,2520000,0,0}, 0,-225,-65,9, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -5726230.6469039, -9998287.38388927, 10766230.6469039, 9998287.38388927}, +{{8, 4, 7, {15,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {167.738861778,-45.563726167,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908753, -4254598.13766705, 8549530.45908754, 15749978.4607509}, +{{8, 4, 7, {168.342872,-46.600009611,1,300002.66,699999.58,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949527.79908754, -4139408.83270982, 8549533.11908754, 15865167.7657081}, +{{8, 4, 7, {168.398641194,-45.132902583,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908753, -4302480.81326099, 8549530.45908754, 15702095.7851569}, +{{8, 4, 7, {168.606267,-43.977802889,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908753, -4430843.14592791, 8549530.45908754, 15573733.45249}, +{{8, 4, 7, {169.467755083,-44.735267972,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4346671.56116845, 8549530.45908754, 15657905.0372495}, +{{8, 4, 7, {170.260925833,-43.110128139,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4527247.70696346, 8549530.45908754, 15477328.8914545}, +{{8, 4, 7, {170.282589111,-45.861513361,0.99996,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949200.47786918, -4221302.42846714, 8549200.47786917, 15782473.9868868}, +{{8, 4, 7, {170.628595167,-45.816196611,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4226536.32741985, 8549530.45908754, 15778040.2709981}, +{{8, 4, 7, {170.9799935,-42.886322361,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4552111.65805664, 8549530.45908754, 15452464.9403613}, +{{8, 4, 7, {171.057250833,-44.402220361,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4383682.10598832, 8549530.45908753, 15620894.4924296}, +{{8, 4, 7, {171.360748472,-43.748711556,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4456298.18308237, 8549530.45908754, 15548278.4153355}, +{{8, 4, 7, {171.549771306,-42.333694278,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4613502.29276327, 8549530.45908753, 15391074.3056546}, +{{8, 4, 7, {171.581260056,-41.810802861,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908753, -4671584.03492437, 8549530.45908754, 15332992.5634935}, +{{8, 4, 7, {172.109028194,-41.289911528,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4729438.31462099, 8549530.45908753, 15275138.2837969}, +{{8, 4, 7, {172.6720465,-40.714759056,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4793313.13554601, 8549530.45908754, 15211263.4628719}, +{{8, 4, 7, {172.727193583,-43.590637583,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4473861.66485194, 8549530.45908753, 15530714.933566}, +{{8, 4, 7, {173.010133389,-42.689116583,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4574019.640636, 8549530.45908754, 15430556.9577819}, +{{8, 4, 7, {173.299316806,-41.274544722,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908753, -4731144.99254062, 8549530.45908754, 15273431.6058773}, +{{8, 4, 7, {173.802074111,-41.544486667,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4701163.86142202, 8549530.45908754, 15303412.7369959}, +{{8, 4, 7, {174.22801175,-39.135758306,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4968639.63588687, 8549530.45908754, 15035936.962531}, +{{8, 4, 7, {174.764339361,-36.879865278,0.9999,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7948705.50604163, -5218451.08592923, 8548705.50604163, 14784125.0548288}, +{{8, 4, 7, {174.776623111,-41.301319639,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908753, -4728171.29681831, 8549530.45908754, 15276405.3015996}, +{{8, 4, 7, {175.488099611,-40.241947139,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908753, -4845817.49876657, 8549530.45908754, 15158759.0996513}, +{{8, 4, 7, {175.640036806,-39.512470389,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4926815.26039816, 8549530.45908754, 15077761.3380198}, +{{8, 4, 7, {175.647349667,-40.925532639,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -4769905.95772054, 8549530.45908754, 15234670.6406974}, +{{8, 4, 7, {176.46619725,-37.761249806,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908753, -5121221.11127107, 8549530.45908754, 14883355.4871468}, +{{8, 4, 7, {176.673680528,-39.650929306,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908753, -4911442.20418344, 8549530.45908754, 15093134.3942345}, +{{8, 4, 7, {177.885636278,-38.624702778,1,300000,700000,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7949530.45908754, -5025375.11246985, 8549530.45908753, 14979201.4859481}, +{{8, 4, 7, {21,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {21,0,1,1500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -6749530.45908754, -10002288.299209, 9749530.45908754, 10002288.299209}, +{{8, 4, 7, {24,0,1,2500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -5749530.45908754, -10002288.299209, 10749530.4590875, 10002288.299209}, +{{8, 4, 7, {27,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {27,0,1,3500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -4749530.45908754, -10002288.299209, 11749530.4590875, 10002288.299209}, +{{8, 4, 7, {3,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {30,0,1,4500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -3749530.45908754, -10002288.299209, 12749530.4590875, 10002288.299209}, +{{8, 4, 7, {33,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {39,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {45,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, +{{8, 4, 7, {9,0,0.9996,1500000,0,0}, 0,-225,-65,9, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -6746230.6469039, -9998287.38388927, 9746230.6469039, 9998287.38388927}, +{{8, 4, 7, {9,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746230.6469039, -9998287.38388927, 8746230.6469039, 9998287.38388927}, + +// Added bounds for PSAD56 (AJD, Encom 2005) +{{8, 4, 7, {-75,0,0.9996,500000,0,0}, 0,-288, 175, -376, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, // PSAD56 18N (Encom 2005) +{{8, 4, 7, {-69,0,0.9996,500000,0,0}, 0,-288, 175, -376, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, // PASD56 19N (Encom 2005) +{{8, 4, 7, {-63,0,0.9996,500000,0,0}, 0,-288, 175, -376, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, // PSAD56 20N (Encom 2005) +{{8, 4, 7, {-57,0,0.9996,500000,0,0}, 0,-288, 175, -376, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, // PSAD56 21N (Encom 2005) +{{8, 4, 7, {-81,0,0.9996,500000,10000000,0}, 0,-288, 175, -376, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, // PSAD56 17S (Encom 2005) +{{8, 4, 7, {-75,0,0.9996,500000,10000000,0}, 0,-288, 175, -376, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, // PASD56 18S (Encom 2005) +{{8, 4, 7, {-69,0,0.9996,500000,10000000,0}, 0,-288, 175, -376, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, // PSAD56 19S (Encom 2005) +{{8, 4, 7, {-63,0,0.9996,500000,10000000,0}, 0,-288, 175, -376, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, // PSAD56 20S (Encom 2005) +{{8, 4, 7, {-80.5,0,0.99983008,222000,1426834.743,0}, 0,-288, 175, -376, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, // PERU WEST ZONE (Encom 2005) +{{8, 4, 7, {-76,0,0.99932994,720000,1039979.159,0}, 0,-288, 175, -376, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, // PERU CENTRAL ZONE (Encom 2005) +{{8, 4, 7, {-70.5,0,0.99952992,1324000,1040084.558,0}, 0,-288, 175, -376, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7745874.38491761, -9998000.59030393, 8745874.38491761, 9998000.59030393}, // PERU EAST ZONE (Encom 2005) + +{{8, 6, 7, {17,0,1,0,0,0}, 0,-136,-108,-292, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8249527.70018454, -10001867.5518777, 8249527.70018454, 10001867.5518777}, +{{8, 6, 7, {19,0,1,0,0,0}, 0,-136,-108,-292, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8249527.70018454, -10001867.5518777, 8249527.70018454, 10001867.5518777}, +{{8, 6, 7, {19,0,1,0,3700000,0}, 0,-136,-108,-292, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8249527.70018454, -6301867.55187774, 8249527.70018454, 13701867.5518777}, +{{8, 6, 7, {21,0,1,0,0,0}, 0,-136,-108,-292, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8249527.70018454, -10001867.5518777, 8249527.70018454, 10001867.5518777}, +{{8, 6, 7, {23,0,1,0,0,0}, 0,-136,-108,-292, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8249527.70018454, -10001867.5518777, 8249527.70018454, 10001867.5518777}, +{{8, 6, 7, {25,0,1,0,0,0}, 0,-136,-108,-292, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8249527.70018454, -10001867.5518777, 8249527.70018454, 10001867.5518777}, +{{8, 6, 7, {27,0,1,0,0,0}, 0,-136,-108,-292, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8249527.70018454, -10001867.5518777, 8249527.70018454, 10001867.5518777}, +{{8, 6, 7, {29,0,1,0,0,0}, 0,-136,-108,-292, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8249527.70018454, -10001867.5518777, 8249527.70018454, 10001867.5518777}, +{{8, 6, 7, {31,0,1,0,0,0}, 0,-136,-108,-292, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -8249527.70018454, -10001867.5518777, 8249527.70018454, 10001867.5518777}, +{{8, 7, 7, {-102,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-105,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578484, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-105,0,0.9996,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578484, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-105,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-108,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-111,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-111,0,0.9996,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-111,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-114,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-117,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-117,0,0.9996,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-117,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-120,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-123,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578484, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-123,0,0.9996,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578484, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-123,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-126,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-129,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-129,0,0.9996,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-129,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-132,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-135,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578484, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-135,0,0.9996,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578484, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-135,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-138,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463713, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-141,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578484, 9997887.28799036}, +{{8, 7, 7, {-141,0,0.9996,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578484, 9997887.28799036}, +{{8, 7, 7, {-141,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463713, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-147,0,0.9996,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578484, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-15,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-153,0,0.9996,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-159,0,0.9996,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-165,0,0.9996,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578484, 9997887.28799036}, +{{8, 7, 7, {-171,0,0.9996,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-177,0,0.9996,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-21,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-27,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-33,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578484, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-39,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-45,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-51,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-53,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-55.5,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-56,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-57,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-58.5,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-61.5,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-63,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-64.5,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-67.5,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-69,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-69,0,0.9996,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-70.5,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-73.5,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-75,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-75,0,0.9996,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-76.5,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-79.5,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-81,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578484, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-81,0,0.9996,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578484, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-81,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-82.5,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463713, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-84,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-87,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-87,0,0.9996,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-87,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463713, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-9,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-90,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-93,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578484, 9997887.28799036}, +{{8, 7, 7, {-93,0,0.9996,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578484, 9997887.28799036}, +{{8, 7, 7, {-93,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-96,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 7, {-99,0,0.9996,500000,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-99,0,0.9996,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7746096.41578485, -9997887.28799036, 8746096.41578485, 9997887.28799036}, +{{8, 7, 7, {-99,0,0.9999,304800,0,0}, 0,-10,158,187, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7943771.23463712, -10000887.8544033, 8553371.23463712, 10000887.8544033}, +{{8, 7, 8, {-104.3333333333,31,0.9999090909,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562433.5032163, -44066372.5582056, 27562433.5032163, 21556716.5402837}, +{{8, 7, 8, {-105.1666666667,40.6666666667,0.9999411765,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563301.8965776, -47586452.7340335, 27563301.8965776, 18038742.112075}, +{{8, 7, 8, {-106.25,31,0.9999,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -44065971.9187976, 27562187.458972, 21556520.5525122}, +{{8, 7, 8, {-107.3333333333,40.6666666667,0.9999411765,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563301.8965776, -47586452.7340335, 27563301.8965776, 18038742.112075}, +{{8, 7, 8, {-107.8333333333,31,0.9999166667,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562638.5414399, -44066706.4265826, 27562638.5414399, 21556879.8645045}, +{{8, 7, 8, {-108.75,40.6666666667,0.9999411765,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563301.8965776, -47586452.7340335, 27563301.8965776, 18038742.112075}, +{{8, 7, 8, {-110.0833333333,40.6666666667,0.9999411765,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563301.8965776, -47586452.7340335, 27563301.8965776, 18038742.112075}, +{{8, 7, 8, {-110.1666666667,31,0.9999,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -44065971.9187976, 27562187.458972, 21556520.5525122}, +{{8, 7, 8, {-111.9166666667,31,0.9999,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -44065971.9187976, 27562187.458972, 21556520.5525122}, +{{8, 7, 8, {-112.1666666667,41.6666666667,0.9999473684,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563469.4796944, -47951082.5341968, 27563469.4796945, 17674518.6804598}, +{{8, 7, 8, {-113.75,31,0.9999333333,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563089.6212014, -44067440.9299605, 27563089.6212014, 21557239.1743409}, +{{8, 7, 8, {-114,41.6666666667,0.9999473684,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563469.4796945, -47951082.5341968, 27563469.4796945, 17674518.6804598}, +{{8, 7, 8, {-115.5833333333,34.75,0.9999,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -45430239.3046952, 27562187.458972, 20192253.1666146}, +{{8, 7, 8, {-115.75,41.6666666667,0.9999333333,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563089.6212014, -47950409.5005354, 27563089.6212014, 17674270.6037659}, +{{8, 7, 8, {-116.6666666667,34.75,0.9999,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -45430239.3046952, 27562187.458972, 20192253.1666146}, +{{8, 7, 8, {-118.5833333333,34.75,0.9999,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -45430239.3046952, 27562187.458972, 20192253.1666146}, +{{8, 7, 8, {-142,54,0.9999,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -52447364.8503891, 27562187.458972, 13175127.6209207}, +{{8, 7, 8, {-146,54,0.9999,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -52447364.8503891, 27562187.458972, 13175127.6209207}, +{{8, 7, 8, {-150,54,0.9999,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -52447364.8503891, 27562187.458972, 13175127.6209207}, +{{8, 7, 8, {-154,54,0.9999,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -52447364.8503891, 27562187.458972, 13175127.6209207}, +{{8, 7, 8, {-155.5,18.8333333333,0.9999666667,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563991.7861373, -39647492.5170134, 27563991.7861373, 25979375.2268425}, +{{8, 7, 8, {-156.6666666667,20.3333333333,0.9999666667,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563991.7861374, -40192224.7269965, 27563991.7861373, 25434643.0168594}, +{{8, 7, 8, {-158,21.1666666667,0.99999,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26564623.2994273, -40495839.074036, 27564623.2994273, 25132560.0122577}, +{{8, 7, 8, {-158,54,0.9999,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -52447364.8503891, 27562187.458972, 13175127.6209207}, +{{8, 7, 8, {-159.5,21.8333333333,0.99999,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26564623.2994273, -40738001.9175015, 27564623.2994273, 24890397.1687922}, +{{8, 7, 8, {-160.1666666667,21.6666666667,1,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26564893.9483668, -40677866.1507286, 27564893.9483668, 24951189.2261189}, +{{8, 7, 8, {-162,54,0.9999,700000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26362187.458972, -52447364.8503891, 27762187.458972, 13175127.6209207}, +{{8, 7, 8, {-166,54,0.9999,500000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -52447364.8503891, 27562187.458972, 13175127.6209207}, +{{8, 7, 8, {-170,54,0.9999,600000,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26462187.458972, -52447364.8503891, 27662187.458972, 13175127.6209207}, +{{8, 7, 8, {-68.5,43.8333333333,0.9999,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -48738387.3899167, 27562187.458972, 16884105.0813931}, +{{8, 7, 8, {-70.1666666667,42.8333333333,0.9999666667,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563991.7861373, -48377155.1233007, 27563991.7861373, 17249712.6205552}, +{{8, 7, 8, {-71.5,41.0833333333,0.99999375,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26564724.7927796, -47740760.2162526, 27564724.7927796, 17887884.9789988}, +{{8, 7, 8, {-71.6666666667,42.5,0.9999666667,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563991.7861373, -48255675.547233, 27563991.7861373, 17371192.1966229}, +{{8, 7, 8, {-72.5,42.5,0.9999642857,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563927.3446249, -48255560.6466395, 27563927.3446248, 17371150.8344356}, +{{8, 7, 8, {-74.3333333333,40,0.9999666667,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563991.7861373, -47344806.5947758, 27563991.7861373, 18282061.1490801}, +{{8, 7, 8, {-74.6666666667,38.8333333333,0.999975,2000000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -25064217.3260181, -46920262.1007603, 29064217.3260181, 18707152.5497028}, +{{8, 7, 8, {-75.4166666667,38,0.999995,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26564758.6238971, -46617719.2903186, 27564758.6238971, 19011007.941252}, +{{8, 7, 8, {-76.5833333333,40,0.9999375,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563202.392495, -47343425.6569741, 27563202.392495, 18281527.9039123}, +{{8, 7, 8, {-78.5833333333,40,0.9999375,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563202.392495, -47343425.6569741, 27563202.392495, 18281527.9039123}, +{{8, 7, 8, {-81,24.3333333333,0.9999411765,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563301.8965776, -41644259.2047719, 27563301.8965776, 23980935.6413366}, +{{8, 7, 8, {-82,24.3333333333,0.9999411765,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563301.8965776, -41644259.2047719, 27563301.8965776, 23980935.6413366}, +{{8, 7, 8, {-82.1666666667,30,0.9999,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -43702304.8531186, 27562187.458972, 21920187.6181912}, +{{8, 7, 8, {-84.1666666667,30,0.9999,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26562187.458972, -43702304.8531186, 27562187.458972, 21920187.6181912}, +{{8, 7, 8, {-85.6666666667,37.5,0.9999666667,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563991.7861373, -46434335.8091192, 27563991.7861373, 19192531.9347367}, +{{8, 7, 8, {-85.8333333333,30.5,0.99996,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563811.3526089, -43886764.6396605, 27563811.3526089, 21739665.5749719}, +{{8, 7, 8, {-87.0833333333,37.5,0.9999666667,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563991.7861373, -46434335.8091192, 27563991.7861373, 19192531.9347367}, +{{8, 7, 8, {-87.5,30,0.9999333333,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563089.6212014, -43703761.7408458, 27563089.6212014, 21920918.3634556}, +{{8, 7, 8, {-88.3333333333,36.6666666667,0.999975,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26564217.3260181, -46131317.1820757, 27564217.3260181, 19496097.4683874}, +{{8, 7, 8, {-88.8333333333,29.6666666667,0.999996,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26564785.688791, -43585279.1789159, 27564785.688791, 22043513.6817101}, +{{8, 7, 8, {-90.1666666667,36.6666666667,0.9999411765,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563301.8965776, -46129756.82046, 27563301.8965776, 19495438.0256486}, +{{8, 7, 8, {-90.3333333333,30.5,0.9999411765,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563301.8965776, -43885938.5041009, 27563301.8965776, 21739256.3420077}, +{{8, 7, 8, {-90.5,35.8333333333,0.9999333333,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563089.6212014, -45826045.0626944, 27563089.6212014, 19798635.041607}, +{{8, 7, 8, {-92.5,35.8333333333,0.9999333333,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563089.6212014, -45826045.0626944, 27563089.6212014, 19798635.041607}, +{{8, 7, 8, {-94.5,36.1666666667,0.9999411765,500000,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26563301.8965776, -45947740.2967367, 27563301.8965776, 19677454.5493719}, +{{8, 9, 7, {-2,49,0.9996012717,400000,-100000,0}, 0,375,-111,431, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -7845061.1011034, -15524202.1641258, 8645061.1011034, 4470074.53373206}, +{{8, 10,7, {15.808277777800001, 0.0, 1.0, 1500000, 0.0, 0.0}, 0, 419.3836, 99.3335, 591.3451, {-0.850389, -1.817277, 7.862238, -0.99496, 0}, 0,0,0,0,0,0,0,0}, -1e9, -1e9, 1e9, 1e9}, // Encom 2005 (AJD) - to Support Swedish +{{9, 2, 7, {132,0,-36,-18,0,0}, 0,-133,-48,148, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20488603.5475955, -35940818.6722945, 20488603.5475955, 5036388.42289653}, +{{9, 2, 7, {132,0,-36,-18,0,0}, 0,-134,-48,149, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20488603.5475955, -35940818.6722945, 20488603.5475955, 5036388.42289653}, +{{9, 28, 7, {23,-23,-18,-32,0,0}, 0,-134.73,-110.92,-292.66, {0,0,0,1,0}, 0,0,0,0,0,0,0,0}, -21500589.3169017, -34197433.4604502, 21500589.3169017, 7592182.07528184}, +{{9, 29, 0, {110,10,25,40,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -11362.8133775181, -3609.79239961301, 11362.8133775181, 19115.8343554232}, +{{9, 29, 0, {132.5,-10,-21.5,-33.5,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -12557.486987589, -21368.9438624525, 12557.486987589, 3746.0301127255}, +{{9, 29, 0, {25,35,40,65,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -9044.42142037436, -4777.87654505631, 9044.42142037436, 13310.9662956924}, +{{9, 29, 0, {47.5,25,15,35,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -13390.3137726925, -4887.09300250369, 13390.3137726925, 21675.1568723853}, +{{9, 29, 0, {78,23,22,33,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -12554.0796051134, -4624.86888238625, 12554.0796051134, 20483.2903278405}, +{{9, 29, 0, {95,40,20,60,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -10369.4020979755, -5634.63503836129, 10369.4020979755, 15104.1691575898}, +{{9, 6, 7, {23,-23,-18,-32,0,0}, 0,-136,-108,-292, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -21500683.2503467, -34197901.8785539, 21500683.2503467, 7591931.73862156}, +{{9, 7, 7, {-154,50,55,65,0,0}, 0,-5,135,172, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -13752073.5330064, -8947886.69972899, 13752073.5330064, 18556260.3662838}, +{{9, 7, 7, {-157,3,8,18,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -24775369.1253827, -6004013.76934506, 34739284.1330387, 43890186.5919339}, +{{9, 7, 7, {-96,23,20,60,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -16687870.3794973, -7101873.97827787, 16687870.3794973, 26273866.7807168}, +{{9, 7, 7, {-96,23,29.5,45.5,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -16900972.6938504, -6971893.13585582, 16900972.6938504, 26830052.251845}, +{{11, 7, 7, {0,0,0,0,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -40075452.7386388, -14691640.6260036, 40075452.7386388, 14691640.6260036}, +{{12, 7, 7, {0,0,0,0,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -34012036.7392828, -8625248.51472, 34012036.7392828, 8625248.51472}, +{{13, 7, 7, {0,0,0,0,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -36080583.9779795, -9020145.99431898, 36080583.9779795, 9020145.99431898}, +{{14, 29, 0, {-60,0,0,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -17523.9133905318, -5257.17401715949, 24533.4787467445, 5257.17401715949}, +{{14, 7, 7, {0,0,0,0,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -33842774.0824052, -8460693.52060123, 33842774.0824052, 8460693.52060123}, +{{15, 7, 7, {0,0,0,0,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -35347572.9807667, -8836893.24519168, 35347572.9807667, 8836893.24519168}, +{{16, 7, 7, {0,0,0,0,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -40075452.7386388, -10018863.1846597, 40075452.7386388, 10018863.1846597}, +{{18, 4, 7, {173,-41,2510000,6023150,0,0}, 0,84,-22,209, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, 1001587.21174105, 4254077.4935345, 4082370.4786608, 7690559.24125294}, +{{20, 0, 7, {0,0,1,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -100000000, -100000000, 100000000, 100000000}, +{{20, 10, 7, {5.387638889,52.156160556,0.9999079,155000,463000,0}, 0,593,26,478, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -99845000, -99537000, 100155000, 100463000}, +{{20, 28, 7, {0,-90,0.994,2000000,2000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -98000000, -98000000, 102000000, 102000000}, +{{20, 28, 7, {0,90,0.994,2000000,2000000,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -98000000, -98000000, 102000000, 102000000}, +{{21, 4, 7, {9,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -688662.0096834, -1193504.42233962, 42993.625661512, 466993.370035908}, +{{22, 4, 7, {9,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -2981314.87489887, -52667593.719744, 93265053.7016014, 912774.431510712}, +{{23, 4, 7, {15,0,0.9996,500000,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -142680.459281219, -57483.8396628927, 56778.2298793441, 142611.243106357}, +{{24, 4, 7, {21,0,1,1500000,0,0}, 0,-96.062, -82.428, -121.754, {-4.801, -0.345, 1.376, 1.496, 0}, 0,0,0,0,0,0,0,0}, 1250000, 6500000, 1750000, 8000000}, // Encom 2005 (AJD) - to support Finnish KKJ Zone 1 +{{24, 4, 7, {24,0,1,2500000,0,0}, 0,-96.062, -82.428, -121.754, {-4.801, -0.345, 1.376, 1.496, 0}, 0,0,0,0,0,0,0,0}, 2250000, 6500000, 2750000, 8000000}, // Encom 2005 (AJD) - to support Finnish KKJ Zone 2 +{{24, 4, 7, {27,0,1,3500000,0,0}, 0,-96.062, -82.428, -121.754, {-4.801, -0.345, 1.376, 1.496, 0}, 0,0,0,0,0,0,0,0}, 2850000, 6500000, 3850000, 8000000}, // Encom 2005 (AJD) - to support Finnish KKJ Zone 3 +{{24, 4, 7, {30,0,1,4500000,0,0}, 0,-96.062, -82.428, -121.754, {-4.801, -0.345, 1.376, 1.496, 0}, 0,0,0,0,0,0,0,0}, 4250000, 6500000, 4750000, 8000000}, // Encom 2005 (AJD) - to support Finnish KKJ Zone 4 +{{25, 10, 7, {7.4395833333,46.9524055555,0,0,0,0}, 0,660.077,13.551,369.344, {0.804816,0.577692,0.952236,5.66,0}, 0,0,0,0,0,0,0,0}, -100000000, -100000000, 100000000, 100000000}, +{{25, 10, 7, {7.4395833333,46.9524055555,600000,200000,0,0}, 0,660.077,13.551,369.344, {0.804816,0.577692,0.952236,5.66,0}, 0,0,0,0,0,0,0,0}, -99400000, -99800000, 100600000, 100200000}, +{{26, 29, 0, {-60,0,0,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20751.2174140408, -12383.4550682633, 29051.7043796571, 12383.4550682633}, +{{26, 29, 0, {-85.5,0,0,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -18987.3639338473, -12383.4550682633, 30815.5578598506, 12383.4550682633}, +{{26, 29, 0, {20,0,0,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -26284.8753911183, -12383.4550682633, 23518.0464025796, 12383.4550682633}, +{{26, 4, 7, {0,60,0,0,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20089005.2889087, -9990138.39344294, 20089005.2889087, 9990138.39344294}, +{{26, 4, 7, {0,70,0,0,0,0}, 0,-87,-98,-121, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -13747868.5914983, -6836730.23471024, 13747868.5914983, 6836730.23471024}, +{{26, 7, 7, {0,0,0,0,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -40075452.7386388, -19928981.8895549, 40075452.7386388, 19928981.8895549}, +{{27, 11, 7, {78,0,1200000,0,0,0}, 0,289,734,257, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -18834804.515364, -15340324.8240999, 21234804.515364, 15340324.8240999}, +{{27, 24, 7, {-37,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-38,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-39,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-41,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-42,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-43,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-45,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-48,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-49,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-51,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-52,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-54,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-56,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-59,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-62,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-63,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-65,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 24, 7, {-70,0,0,0,0,0}, 0,-57,1,-41, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037580.5994203, -15342381.4311037, 20037580.5994203, 15342381.4311037}, +{{27, 28, 7, {-100,40,0,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037508.3427892, -19771855.3330942, 20037508.3427892, 10912797.2721118}, +{{27, 28, 7, {0,0,0,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -20037508.3427892, -15342326.302603, 20037508.3427892, 15342326.302603}, +{{27, 28, 7, {78,0,1200000,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -18837508.3427892, -15342326.302603, 21237508.3427892, 15342326.302603}, +{{28, 0, 7, {-100,-80,180,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -19889431.1404394, -20026376.3929389, 19889431.1404395, 20026376.3929385}, +{{28, 0, 7, {-100,-80,90,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -10014588.6119777, -10018754.1713946, 10014588.6119777, 10018754.1713946}, +{{28, 0, 7, {-100,40,180,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -19610690.9824974, -20026376.3936683, 19610690.9824974, 20026376.3936695}, +{{28, 0, 7, {-100,40,90,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -10018734.8959557, -10018754.1713946, 10018734.8959557, 10018754.1713946}, +{{28, 0, 7, {0,-60,180,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -19758589.084291, -20026376.393667, 19758589.084291, 20026376.3936708}, +{{28, 0, 7, {0,-60,90,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -10018731.6857622, -10018754.1713946, 10018731.6857622, 10018754.1713946}, +{{28, 0, 7, {0,80,180,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -19889431.1404395, -20026376.3929385, 19889431.1404395, 20026376.3929389}, +{{28, 0, 7, {0,80,90,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -10014588.6119777, -10018754.1713946, 10014588.6119777, 10018754.1713946}, +{{28, 7, 7, {0,-90,90,0,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -10018863.1846597, -10018863.1846597, 10018863.1846597, 10018863.1846597}, +{{28, 7, 7, {0,90,90,0,0,0}, 0,-8,160,176, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, -10018863.1846597, -10018863.1846597, 10018863.1846597, 10018863.1846597}, + +{{0xff, 0, 0, {0,0,0,0,0,0}, 0,0,0,0, {0,0,0,0,0}, 0,0,0,0,0,0,0,0}, 0, 0, 0, 0} +}; + + +#define TAB_EQUAL(a, b, eps) (fabs((a)-(b)) < eps) + +static char szPreviousMitabBoundsFile[2048] = { 0 }; +static VSIStatBufL sStatBoundsFile; + +/********************************************************************** + * MITABLookupCoordSysBounds() + * + * Lookup bounds for specified TABProjInfo struct. + * + * This can modify that passed TABProjInfo struct if a match is found + * in an external bound file with proj remapping. + * + * Returns TRUE if valid bounds were found, FALSE otherwise. + **********************************************************************/ +GBool MITABLookupCoordSysBounds(TABProjInfo *psCS, + double &dXMin, double &dYMin, + double &dXMax, double &dYMax, + int bOnlyUserTable) +{ + GBool bFound = FALSE; + const MapInfoBoundsInfo *psList; + + /*----------------------------------------------------------------- + * Try to load the user defined table if not loaded yet . + *----------------------------------------------------------------*/ + const char * pszMitabBoundsFile = CPLGetConfigOption("MITAB_BOUNDS_FILE", NULL); + if (pszMitabBoundsFile != NULL && pszMitabBoundsFile[0] != '\0' ) + { + if( strcmp(pszMitabBoundsFile, szPreviousMitabBoundsFile) != 0) + { + CPLStrlcpy(szPreviousMitabBoundsFile, pszMitabBoundsFile, + sizeof(szPreviousMitabBoundsFile)); + MITABLoadCoordSysTable(pszMitabBoundsFile); + if( VSIStatL(pszMitabBoundsFile, &sStatBoundsFile) != 0 ) + { + sStatBoundsFile.st_mtime = 0; + } + } + else + { + /* Reload file if its modification file has changed */ + VSIStatBufL sStat; + if( VSIStatL(pszMitabBoundsFile, &sStat) == 0 ) + { + if( sStat.st_mtime != sStatBoundsFile.st_mtime ) + { + MITABLoadCoordSysTable(pszMitabBoundsFile); + memcpy(&sStatBoundsFile, &sStat, sizeof(sStat)); + } + } + } + } + else if ( szPreviousMitabBoundsFile[0] != '\0' ) + { + MITABFreeCoordSysTable(); + strcpy(szPreviousMitabBoundsFile, ""); + } + + for(int iLoop=0; !bFound && iLoop < 2; iLoop++) + { + /* MapInfo uses a hack to differentiate some SRS that have the same */ + /* definition, but different bounds, e.g. Lambet 93 France with French */ + /* Bounds or with European bounds. It alters slightly one of the projection */ + /* parameters, e.g. std_parallel_1 = 49.00000000001 or 49.00000000002 */ + double eps = (iLoop == 0) ? 1e-12 : 1e-6; + + /*----------------------------------------------------------------- + * Lookup table... + * Lookup external file if one was loaded, then lookup internal table. + * + * Note that entries in lookup table with 0xff for projId, UnitsId, + * means ignore that param, and 0xff in ellipsoidId means ignore the + * whole datum. + *----------------------------------------------------------------*/ + for( int i = 0 ; !bFound && i < nExtBoundsListCount; i++) + { + TABProjInfo *p = &(gpasExtBoundsList[i].sProjIn); + + if (p->nProjId == psCS->nProjId && + (p->nUnitsId == 0xff || p->nUnitsId == psCS->nUnitsId) && + (p->nEllipsoidId == 0xff || + (p->nEllipsoidId == psCS->nEllipsoidId && + ( (p->nDatumId > 0 && p->nDatumId == psCS->nDatumId) || + ((p->nDatumId <= 0 || psCS->nDatumId <= 0) && + TAB_EQUAL(p->dDatumShiftX, psCS->dDatumShiftX, eps) && + TAB_EQUAL(p->dDatumShiftY, psCS->dDatumShiftY, eps) && + TAB_EQUAL(p->dDatumShiftZ, psCS->dDatumShiftZ, eps) && + TAB_EQUAL(p->adDatumParams[0], psCS->adDatumParams[0], eps) && + TAB_EQUAL(p->adDatumParams[1], psCS->adDatumParams[1], eps) && + TAB_EQUAL(p->adDatumParams[2], psCS->adDatumParams[2], eps) && + TAB_EQUAL(p->adDatumParams[3], psCS->adDatumParams[3], eps) && + TAB_EQUAL(p->adDatumParams[4], psCS->adDatumParams[4], eps) )))) && + (TAB_EQUAL(p->adProjParams[0], psCS->adProjParams[0], eps) && + TAB_EQUAL(p->adProjParams[1], psCS->adProjParams[1], eps) && + TAB_EQUAL(p->adProjParams[2], psCS->adProjParams[2], eps) && + TAB_EQUAL(p->adProjParams[3], psCS->adProjParams[3], eps) && + TAB_EQUAL(p->adProjParams[4], psCS->adProjParams[4], eps) && + TAB_EQUAL(p->adProjParams[5], psCS->adProjParams[5], eps) ) ) + { + memcpy(psCS, &gpasExtBoundsList[i].sBoundsInfo.sProj, + sizeof(TABProjInfo)); + dXMin = gpasExtBoundsList[i].sBoundsInfo.dXMin; + dYMin = gpasExtBoundsList[i].sBoundsInfo.dYMin; + dXMax = gpasExtBoundsList[i].sBoundsInfo.dXMax; + dYMax = gpasExtBoundsList[i].sBoundsInfo.dYMax; + bFound = TRUE; + } + } + + psList = gasBoundsList; + for( ; !bOnlyUserTable && !bFound && psList->sProj.nProjId!=0xff; psList++) + { + const TABProjInfo *p = &(psList->sProj); + + if (p->nProjId == psCS->nProjId && + (p->nUnitsId == 0xff || p->nUnitsId == psCS->nUnitsId) && + (p->nEllipsoidId == 0xff || + (p->nEllipsoidId == psCS->nEllipsoidId && + ( (p->nDatumId > 0 && p->nDatumId == psCS->nDatumId) || + ((p->nDatumId <= 0 || psCS->nDatumId <= 0) && + TAB_EQUAL(p->dDatumShiftX, psCS->dDatumShiftX, eps) && + TAB_EQUAL(p->dDatumShiftY, psCS->dDatumShiftY, eps) && + TAB_EQUAL(p->dDatumShiftZ, psCS->dDatumShiftZ, eps) && + TAB_EQUAL(p->adDatumParams[0], psCS->adDatumParams[0], eps) && + TAB_EQUAL(p->adDatumParams[1], psCS->adDatumParams[1], eps) && + TAB_EQUAL(p->adDatumParams[2], psCS->adDatumParams[2], eps) && + TAB_EQUAL(p->adDatumParams[3], psCS->adDatumParams[3], eps) && + TAB_EQUAL(p->adDatumParams[4], psCS->adDatumParams[4], eps) )))) && + (TAB_EQUAL(p->adProjParams[0], psCS->adProjParams[0], eps) && + TAB_EQUAL(p->adProjParams[1], psCS->adProjParams[1], eps) && + TAB_EQUAL(p->adProjParams[2], psCS->adProjParams[2], eps) && + TAB_EQUAL(p->adProjParams[3], psCS->adProjParams[3], eps) && + TAB_EQUAL(p->adProjParams[4], psCS->adProjParams[4], eps) && + TAB_EQUAL(p->adProjParams[5], psCS->adProjParams[5], eps) ) ) + { + dXMin = psList->dXMin; + dYMin = psList->dYMin; + dXMax = psList->dXMax; + dYMax = psList->dYMax; + bFound = TRUE; + } + } + } + + return bFound; +} + + +/********************************************************************** + * MITABLoadCoordSysTable() + * + * Load a Coordsys bounds lookup table from an external file. + * The entries from that table will be looked up in priority BEFORE the + * entries from gasBoundsList[] defined above. + * + * This allows users to override the default bounds for existing + * projections, and to define bounds for new projections not listed in + * the table above. + * + * The format of the file is a simple text file with one CoordSys string + * per line. The CoordSys lines should follow the MIF specs, and MUST + * include the optional Bounds definition at the end of the line. + * + * e.g. + * CoordSys Earth Projection 8, 24, "m", -63, 0, 0.9996, 500000, 0 Bounds \ + * (-7746230.6469039, -9998287.383889269) (8746230.6469039, 9998287.383889269) + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int MITABLoadCoordSysTable(const char *pszFname) +{ + VSILFILE *fp; + int nStatus = 0, iLine = 0; + + MITABFreeCoordSysTable(); + + if ((fp = VSIFOpenL(pszFname, "rt")) != NULL) + { + const char *pszLine; + int iEntry=0, numEntries=100; + + gpasExtBoundsList = (MapInfoRemapProjInfo *)CPLMalloc(numEntries* + sizeof(MapInfoRemapProjInfo)); + + while( (pszLine = CPLReadLineL(fp)) != NULL) + { + double dXMin, dYMin, dXMax, dYMax; + int bHasProjIn = FALSE; + TABProjInfo sProjIn; + TABProjInfo sProj; + + iLine++; + + if (strlen(pszLine) < 10 || EQUALN(pszLine, "#", 1)) + continue; // Skip empty lines/comments + + if( EQUALN(pszLine, "Source", strlen("Source")) ) + { + const char* pszEqual = strchr(pszLine, '='); + if( !pszEqual ) + { + CPLError(CE_Warning, CPLE_IllegalArg, "Invalid format at line %d", iLine); + break; + } + pszLine = pszEqual + 1; + if ((nStatus = MITABCoordSys2TABProjInfo(pszLine, &sProjIn)) != 0) + { + break; // Abort and return + } + if( strstr(pszLine, "Bounds") != NULL ) + { + CPLError(CE_Warning, CPLE_IllegalArg, "Unexpected Bounds parameter at line %d", + iLine); + } + bHasProjIn = TRUE; + + iLine++; + pszLine = CPLReadLineL(fp); + if( pszLine == NULL || + !EQUALN(pszLine, "Destination", strlen("Destination")) || + (pszEqual = strchr(pszLine, '=')) == NULL ) + { + CPLError(CE_Warning, CPLE_IllegalArg, "Invalid format at line %d", iLine); + break; + } + pszLine = pszEqual + 1; + } + + if ((nStatus = MITABCoordSys2TABProjInfo(pszLine, &sProj)) != 0) + { + break; // Abort and return + } + + if (!MITABExtractCoordSysBounds(pszLine, dXMin,dYMin,dXMax,dYMax)) + { + CPLError(CE_Warning, CPLE_IllegalArg, + "Missing Bounds parameters in line %d of %s", + iLine, pszFname); + continue; // Just skip this line. + } + + if (iEntry >= numEntries-1) + { + numEntries+= 100; + gpasExtBoundsList = + (MapInfoRemapProjInfo *)CPLRealloc(gpasExtBoundsList, + numEntries* sizeof(MapInfoRemapProjInfo)); + } + + gpasExtBoundsList[iEntry].sProjIn = (bHasProjIn) ? sProjIn : sProj; + gpasExtBoundsList[iEntry].sBoundsInfo.sProj = sProj; + gpasExtBoundsList[iEntry].sBoundsInfo.dXMin = dXMin; + gpasExtBoundsList[iEntry].sBoundsInfo.dYMin = dYMin; + gpasExtBoundsList[iEntry].sBoundsInfo.dXMax = dXMax; + gpasExtBoundsList[iEntry].sBoundsInfo.dYMax = dYMax; + iEntry ++; + } + nExtBoundsListCount = iEntry; + + VSIFCloseL(fp); + } + else + { + CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", pszFname); + } + + return nStatus; +} + + +/********************************************************************** + * MITABFreeCoordSysTable() + * + * Flush memory used by the coordsys table loaded by MITABLoadCoordSysTable() + **********************************************************************/ +void MITABFreeCoordSysTable() +{ + CPLFree(gpasExtBoundsList); + gpasExtBoundsList = NULL; + nExtBoundsListCount = -1; +} + +/********************************************************************** + * MITABCoordSysTableLoaded() + * + * Returns TRUE if a coordsys table was loaded, FALSE otherwise. + **********************************************************************/ +GBool MITABCoordSysTableLoaded() +{ + return (nExtBoundsListCount >= 0); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_coordsys.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_coordsys.cpp new file mode 100644 index 000000000..da4919127 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_coordsys.cpp @@ -0,0 +1,497 @@ +/********************************************************************** + * $Id: mitab_coordsys.cpp,v 1.42 2011-06-11 00:35:00 fwarmerdam Exp $ + * + * Name: mitab_coordsys.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation translation between MIF CoordSys format, and + * and OGRSpatialRef format. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 1999-2001, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_coordsys.cpp,v $ + * Revision 1.42 2011-06-11 00:35:00 fwarmerdam + * add support for reading google mercator (#4115) + * + * Revision 1.41 2010-10-07 18:46:26 aboudreault + * Fixed bad use of CPLAtof when locale setting doesn't use . for float (GDAL bug #3775) + * + * Revision 1.40 2010-09-07 16:48:08 aboudreault + * Removed incomplete patch for affine params support in mitab. (bug 1155) + * + * Revision 1.39 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.38 2010-07-05 18:32:48 aboudreault + * Fixed memory leaks in mitab_capi.cpp and mitab_coordsys.cpp + * + * Revision 1.37 2010-07-05 17:20:14 aboudreault + * Added Krovak projection suppoprt (bug 2230) + * + * Revision 1.36 2007-11-21 21:15:45 dmorissette + * Fix asDatumInfoList[] and asSpheroidInfoList[] defns/refs (bug 1826) + * + * Revision 1.35 2007/06/21 13:23:43 fwarmerdam + * Fixed support for predefined datums with non-greenwich prime meridians + * + * Revision 1.34 2006/03/10 19:50:45 fwarmerdam + * Coordsys false easting and northing are in the units of the coordsys, not + * necessarily meters. Adjusted mitab_coordsys.cpp to reflect this. + * http://bugzilla.remotesensing.org/show_bug.cgi?id=1113 + * + * Revision 1.33 2005/09/29 20:13:57 dmorissette + * MITABCoordSys2SpatialRef() patches from Anthony D (bug 1155): + * Improved support for modified TM projections 21-24. + * Added support for affine parameters (inside #ifdef MITAB_AFFINE_PARAMS since + * affine params cannot be stored directly in OGRSpatialReference) + * + * Revision 1.32 2005/08/07 21:00:38 fwarmerdam + * Initialize adfDatumParm[] to avoid warnings with gcc 4. + * + * Revision 1.31 2005/05/12 22:07:52 dmorissette + * Improved handling of Danish modified TM proj#21-24 (hss, bugs 976,1010) + * + * Revision 1.30 2005/03/22 23:24:54 dmorissette + * Added support for datum id in .MAP header (bug 910) + * + * Revision 1.29 2004/06/03 19:36:53 fwarmerdam + * fixed memory leak processing non-earth coordsys + * + * Revision 1.28 2003/03/21 14:20:42 warmerda + * fixed up regional mercator handling, was screwing up transverse mercator + * + * Revision 1.27 2003/01/09 17:33:26 warmerda + * fixed ellipsoid extraction for datum 999/9999 + * + * Revision 1.26 2002/12/12 20:12:18 warmerda + * fixed signs of rotational parameters for TOWGS84 in WKT + * + * Revision 1.25 2002/10/15 14:33:30 warmerda + * Added untested support in mitab_spatialref.cpp, and mitab_coordsys.cpp for + * projections Regional Mercator (26), Polyconic (27), Azimuthal Equidistant - + * All origin latitudes (28), and Lambert Azimuthal Equal Area - any aspect (29). + * + * Revision 1.24 2002/09/23 13:16:04 warmerda + * fixed leak in MITABExtractCoordSysBounds() + * + * Revision 1.23 2002/04/01 19:49:24 warmerda + * added support for cassini/soldner - proj 30 + * + * Revision 1.22 2002/03/01 19:00:15 warmerda + * False Easting/Northing should be in the linear units of measure in MapInfo, + * but in OGRSpatialReference/WKT they are always in meters. Convert accordingly. + * + * Revision 1.21 2001/04/04 21:43:19 warmerda + * added code to set WGS84 values + * + * Revision 1.20 2001/01/23 21:23:42 daniel + * Added projection bounds lookup table, called from TABFile::SetProjInfo() + * + * Revision 1.19 2001/01/22 16:00:53 warmerda + * reworked swiss projection support + * + * Revision 1.18 2001/01/19 21:56:18 warmerda + * added untested support for Swiss Oblique Mercator + **********************************************************************/ + +#include "mitab.h" +#include "mitab_utils.h" + +extern const MapInfoDatumInfo asDatumInfoList[]; +extern const MapInfoSpheroidInfo asSpheroidInfoList[]; + +/************************************************************************/ +/* MITABCoordSys2SpatialRef() */ +/* */ +/* Convert a MIF COORDSYS string into a new OGRSpatialReference */ +/* object. */ +/************************************************************************/ + +OGRSpatialReference *MITABCoordSys2SpatialRef( const char * pszCoordSys ) + +{ + TABProjInfo sTABProj; + if(MITABCoordSys2TABProjInfo(pszCoordSys, &sTABProj) < 0 ) + return NULL; + OGRSpatialReference* poSR = TABFile::GetSpatialRefFromTABProj(sTABProj); + +/* -------------------------------------------------------------------- */ +/* Report on translation. */ +/* -------------------------------------------------------------------- */ + char *pszWKT; + + poSR->exportToWkt( &pszWKT ); + if( pszWKT != NULL ) + { + CPLDebug( "MITAB", + "This CoordSys value:\n%s\nwas translated to:\n%s\n", + pszCoordSys, pszWKT ); + CPLFree( pszWKT ); + } + + return poSR; +} + +/************************************************************************/ +/* MITABSpatialRef2CoordSys() */ +/* */ +/* Converts a OGRSpatialReference object into a MIF COORDSYS */ +/* string. */ +/* */ +/* The function returns a newly allocated string that should be */ +/* CPLFree()'d by the caller. */ +/************************************************************************/ + +char *MITABSpatialRef2CoordSys( OGRSpatialReference * poSR ) + +{ + if( poSR == NULL ) + return NULL; + + TABProjInfo sTABProj; + int nParmCount; + TABFile::GetTABProjFromSpatialRef(poSR, sTABProj, nParmCount); + +/* -------------------------------------------------------------------- */ +/* Do coordsys lookup */ +/* -------------------------------------------------------------------- */ + double dXMin, dYMin, dXMax, dYMax; + int bHasBounds = FALSE; + if (sTABProj.nProjId > 1 && + MITABLookupCoordSysBounds(&sTABProj, dXMin, dYMin, dXMax, dYMax, TRUE) == TRUE) + { + bHasBounds = TRUE; + } + +/*----------------------------------------------------------------- + * Translate the units + *----------------------------------------------------------------*/ + const char *pszMIFUnits = TABUnitIdToString(sTABProj.nUnitsId); + +/* -------------------------------------------------------------------- */ +/* Build coordinate system definition. */ +/* -------------------------------------------------------------------- */ + CPLString osCoordSys; + + if( sTABProj.nProjId != 0 ) + { + osCoordSys.Printf( + "Earth Projection %d", + sTABProj.nProjId ); + + } + else + osCoordSys.Printf( + "NonEarth Units" ); + +/* -------------------------------------------------------------------- */ +/* Append Datum */ +/* -------------------------------------------------------------------- */ + if( sTABProj.nProjId != 0 ) + { + osCoordSys += CPLSPrintf( + ", %d", + sTABProj.nDatumId ); + + if( sTABProj.nDatumId == 999 || sTABProj.nDatumId == 9999 ) + { + osCoordSys += CPLSPrintf( + ", %d, %.15g, %.15g, %.15g", + sTABProj.nEllipsoidId, + sTABProj.dDatumShiftX, sTABProj.dDatumShiftY, sTABProj.dDatumShiftZ ); + } + + if( sTABProj.nDatumId == 9999 ) + { + osCoordSys += CPLSPrintf( + ", %.15g, %.15g, %.15g, %.15g, %.15g", + sTABProj.adDatumParams[0], sTABProj.adDatumParams[1], sTABProj.adDatumParams[2], + sTABProj.adDatumParams[3], sTABProj.adDatumParams[4] ); + } + } + +/* -------------------------------------------------------------------- */ +/* Append units. */ +/* -------------------------------------------------------------------- */ + if( sTABProj.nProjId != 1 && pszMIFUnits != NULL ) + { + if( sTABProj.nProjId != 0 ) + osCoordSys += "," ; + + osCoordSys += CPLSPrintf( + " \"%s\"", + pszMIFUnits ); + } + +/* -------------------------------------------------------------------- */ +/* Append Projection Parms. */ +/* -------------------------------------------------------------------- */ + for( int iParm = 0; iParm < nParmCount; iParm++ ) + osCoordSys += CPLSPrintf( + ", %.15g", + sTABProj.adProjParams[iParm] ); + +/* -------------------------------------------------------------------- */ +/* Append user bounds */ +/* -------------------------------------------------------------------- */ + if (bHasBounds) + { + if( fabs(dXMin - (int)floor(dXMin+0.5)) < 1e-8 && + fabs(dYMin - (int)floor(dYMin+0.5)) < 1e-8 && + fabs(dXMax - (int)floor(dXMax+0.5)) < 1e-8 && + fabs(dYMax - (int)floor(dYMax+0.5)) < 1e-8 ) + { + osCoordSys += CPLSPrintf(" Bounds (%d, %d) (%d, %d)", + (int)dXMin, (int)dYMin, (int)dXMax, (int)dYMax); + } + else + { + osCoordSys += CPLSPrintf(" Bounds (%f, %f) (%f, %f)", + dXMin, dYMin, dXMax, dYMax); + } + } + +/* -------------------------------------------------------------------- */ +/* Report on translation */ +/* -------------------------------------------------------------------- */ + char *pszWKT = NULL; + + poSR->exportToWkt( &pszWKT ); + if( pszWKT != NULL ) + { + CPLDebug( "MITAB", + "This WKT Projection:\n%s\n\ntranslates to:\n%s\n", + pszWKT, osCoordSys.c_str() ); + CPLFree( pszWKT ); + } + + return( CPLStrdup( osCoordSys.c_str() ) ); +} + + +/************************************************************************/ +/* MITABExtractCoordSysBounds */ +/* */ +/* Return TRUE if MIF coordsys string contains a BOUNDS parameter and */ +/* Set x/y min/max values. */ +/************************************************************************/ + +GBool MITABExtractCoordSysBounds( const char * pszCoordSys, + double &dXMin, double &dYMin, + double &dXMax, double &dYMax ) + +{ + char **papszFields; + + if( pszCoordSys == NULL ) + return FALSE; + + papszFields = CSLTokenizeStringComplex( pszCoordSys, " ,()", TRUE, FALSE ); + + int iBounds = CSLFindString( papszFields, "Bounds" ); + + if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields)) + { + dXMin = CPLAtof(papszFields[++iBounds]); + dYMin = CPLAtof(papszFields[++iBounds]); + dXMax = CPLAtof(papszFields[++iBounds]); + dYMax = CPLAtof(papszFields[++iBounds]); + CSLDestroy( papszFields ); + return TRUE; + } + + CSLDestroy( papszFields ); + return FALSE; +} + + +/********************************************************************** + * MITABCoordSys2TABProjInfo() + * + * Convert a MIF COORDSYS string into a TABProjInfo structure. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int MITABCoordSys2TABProjInfo(const char * pszCoordSys, TABProjInfo *psProj) + +{ + char **papszFields; + + // Set all fields to zero, equivalent of NonEarth Units "mi" + memset(psProj, 0, sizeof(TABProjInfo)); + + if( pszCoordSys == NULL ) + return -1; + + /*----------------------------------------------------------------- + * Parse the passed string into words. + *----------------------------------------------------------------*/ + while(*pszCoordSys == ' ') pszCoordSys++; // Eat leading spaces + if( EQUALN(pszCoordSys,"CoordSys",8) ) + pszCoordSys += 9; + + papszFields = CSLTokenizeStringComplex( pszCoordSys, " ,", TRUE, FALSE ); + + /*----------------------------------------------------------------- + * Clip off Bounds information. + *----------------------------------------------------------------*/ + int iBounds = CSLFindString( papszFields, "Bounds" ); + + while( iBounds != -1 && papszFields[iBounds] != NULL ) + { + CPLFree( papszFields[iBounds] ); + papszFields[iBounds] = NULL; + iBounds++; + } + + /*----------------------------------------------------------------- + * Fetch the projection. + *----------------------------------------------------------------*/ + char **papszNextField; + + if( CSLCount( papszFields ) >= 3 + && EQUAL(papszFields[0],"Earth") + && EQUAL(papszFields[1],"Projection") ) + { + int nProjId = atoi(papszFields[2]); + if (nProjId>=3000) nProjId -=3000; + else if (nProjId>=2000) nProjId -=2000; + else if (nProjId>=1000) nProjId -=1000; + + psProj->nProjId = (GByte)nProjId; + papszNextField = papszFields + 3; + } + else if (CSLCount( papszFields ) >= 2 + && EQUAL(papszFields[0],"NonEarth") ) + { + // NonEarth Units "..." Bounds (x, y) (x, y) + psProj->nProjId = 0; + papszNextField = papszFields + 2; + + if( papszNextField[0] != NULL && EQUAL(papszNextField[0],"Units") ) + papszNextField++; + } + else + { + // Invalid projection string ??? + if (CSLCount(papszFields) > 0) + CPLError(CE_Warning, CPLE_IllegalArg, + "Failed parsing CoordSys: '%s'", pszCoordSys); + CSLDestroy(papszFields); + return -1; + } + + /*----------------------------------------------------------------- + * Fetch the datum information. + *----------------------------------------------------------------*/ + int nDatum = 0; + + if( psProj->nProjId != 0 && CSLCount(papszNextField) > 0 ) + { + nDatum = atoi(papszNextField[0]); + papszNextField++; + } + + if( (nDatum == 999 || nDatum == 9999) + && CSLCount(papszNextField) >= 4 ) + { + psProj->nEllipsoidId = (GByte)atoi(papszNextField[0]); + psProj->dDatumShiftX = CPLAtof(papszNextField[1]); + psProj->dDatumShiftY = CPLAtof(papszNextField[2]); + psProj->dDatumShiftZ = CPLAtof(papszNextField[3]); + papszNextField += 4; + + if( nDatum == 9999 + && CSLCount(papszNextField) >= 5 ) + { + psProj->adDatumParams[0] = CPLAtof(papszNextField[0]); + psProj->adDatumParams[1] = CPLAtof(papszNextField[1]); + psProj->adDatumParams[2] = CPLAtof(papszNextField[2]); + psProj->adDatumParams[3] = CPLAtof(papszNextField[3]); + psProj->adDatumParams[4] = CPLAtof(papszNextField[4]); + papszNextField += 5; + } + } + else if (nDatum != 999 && nDatum != 9999) + { + /*----------------------------------------------------------------- + * Find the datum, and collect it's parameters if possible. + *----------------------------------------------------------------*/ + int iDatum; + const MapInfoDatumInfo *psDatumInfo = NULL; + + for(iDatum=0; asDatumInfoList[iDatum].nMapInfoDatumID != -1; iDatum++) + { + if( asDatumInfoList[iDatum].nMapInfoDatumID == nDatum ) + { + psDatumInfo = asDatumInfoList + iDatum; + break; + } + } + + if( asDatumInfoList[iDatum].nMapInfoDatumID == -1 + && nDatum != 999 && nDatum != 9999 ) + { + /* use WGS84 */ + psDatumInfo = asDatumInfoList + 0; + } + + if( psDatumInfo != NULL ) + { + psProj->nEllipsoidId = (GByte)psDatumInfo->nEllipsoid; + psProj->nDatumId = (GInt16)psDatumInfo->nMapInfoDatumID; + psProj->dDatumShiftX = psDatumInfo->dfShiftX; + psProj->dDatumShiftY = psDatumInfo->dfShiftY; + psProj->dDatumShiftZ = psDatumInfo->dfShiftZ; + psProj->adDatumParams[0] = psDatumInfo->dfDatumParm0; + psProj->adDatumParams[1] = psDatumInfo->dfDatumParm1; + psProj->adDatumParams[2] = psDatumInfo->dfDatumParm2; + psProj->adDatumParams[3] = psDatumInfo->dfDatumParm3; + psProj->adDatumParams[4] = psDatumInfo->dfDatumParm4; + } + } + + /*----------------------------------------------------------------- + * Fetch the units string. + *----------------------------------------------------------------*/ + if( CSLCount(papszNextField) > 0 ) + { + psProj->nUnitsId = (GByte)TABUnitIdFromString(papszNextField[0]); + papszNextField++; + } + + /*----------------------------------------------------------------- + * Finally the projection parameters. + *----------------------------------------------------------------*/ + for(int iParam=0; iParam < 6 && CSLCount(papszNextField) > 0; iParam++) + { + psProj->adProjParams[iParam] = CPLAtof(papszNextField[0]); + papszNextField++; + } + + CSLDestroy(papszFields); + + return 0; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_datfile.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_datfile.cpp new file mode 100644 index 000000000..d9a417cd1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_datfile.cpp @@ -0,0 +1,2627 @@ +/********************************************************************** + * $Id: mitab_datfile.cpp,v 1.22 2010-07-07 19:00:15 aboudreault Exp $ + * + * Name: mitab_datfile.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABIDFile class used to handle + * reading/writing of the .DAT file + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2001, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_datfile.cpp,v $ + * Revision 1.22 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.21 2009-06-08 20:30:46 dmorissette + * Fixed threading issue (static buffer) in Date and DateTime code (GDAL + * ticket #1883) + * + * Revision 1.20 2008-11-27 20:50:22 aboudreault + * Improved support for OGR date/time types. New Read/Write methods (bug 1948) + * Added support of OGR date/time types for MIF features. + * + * Revision 1.19 2008/01/29 20:46:32 dmorissette + * Added support for v9 Time and DateTime fields (byg 1754) + * + * Revision 1.18 2007/10/09 17:43:16 fwarmerdam + * Remove static variables that interfere with reentrancy. (GDAL #1883) + * + * Revision 1.17 2004/06/30 20:29:03 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.16 2001/05/01 12:32:03 daniel + * Get rid of leading spaces in WriteDateField(). + * + * Revision 1.15 2000/04/27 15:42:03 daniel + * Map variable field length (width=0) coming from OGR to acceptable default + * + * Revision 1.14 2000/02/28 16:52:52 daniel + * Added support for writing indexes, removed validation on field name in + * NATIVE tables, and remove trailing spaces in DBF char field values + * + * Revision 1.13 2000/01/28 07:31:49 daniel + * Validate char field width (must be <= 254 chars) + * + * Revision 1.12 2000/01/16 19:08:48 daniel + * Added support for reading 'Table Type DBF' tables + * + * Revision 1.11 2000/01/15 22:30:43 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.10 1999/12/20 18:59:20 daniel + * Dates again... now returned as "YYYYMMDD" + * + * Revision 1.9 1999/12/16 17:11:45 daniel + * Date fields: return as "YYYY/MM/DD", and accept 3 diff. formats as input + * + * Revision 1.8 1999/12/14 03:58:29 daniel + * Fixed date read/write (bytes were reversed) + * + * Revision 1.7 1999/11/09 07:34:35 daniel + * Return default values when deleted attribute records are encountered + * + * Revision 1.6 1999/10/19 06:09:25 daniel + * Removed obsolete GetFieldDef() method + * + * Revision 1.5 1999/10/01 03:56:28 daniel + * Avoid multiple InitWriteHeader() calls (caused a leak) and added a fix + * in WriteCharField() to prevent reading bytes past end of string buffer + * + * Revision 1.4 1999/10/01 02:02:36 warmerda + * Added assertions to try and track TABRawBinBlock leak. + * + * Revision 1.3 1999/09/26 14:59:36 daniel + * Implemented write support + * + * Revision 1.2 1999/09/20 18:43:20 daniel + * Use binary access to open file. + * + * Revision 1.1 1999/07/12 04:18:23 daniel + * Initial checkin + * + **********************************************************************/ + +#include "mitab.h" +#include "ogr_p.h" + +/*===================================================================== + * class TABDATFile + * + * Note that the .DAT files are .DBF files with some exceptions: + * + * All fields in the DBF header are defined as 'C' type (strings), + * even for binary integers. So we have to look in the associated .TAB + * file to find the real field definition. + * + * Even though binary integers are defined as 'C' type, they are stored + * in binary form inside a 4 bytes string field. + *====================================================================*/ + + +/********************************************************************** + * TABDATFile::TABDATFile() + * + * Constructor. + **********************************************************************/ +TABDATFile::TABDATFile() +{ + m_fp = NULL; + m_pszFname = NULL; + m_eTableType = TABTableNative; + + m_poHeaderBlock = NULL; + m_poRecordBlock = NULL; + m_pasFieldDef = NULL; + + m_numFields = -1; + m_numRecords = -1; + m_nFirstRecordPtr = 0; + m_nBlockSize = 0; + m_nRecordSize = -1; + m_nCurRecordId = -1; + m_bCurRecordDeletedFlag = FALSE; + m_bWriteHeaderInitialized = FALSE; + m_bWriteEOF = FALSE; + + m_bUpdated = FALSE; +} + +/********************************************************************** + * TABDATFile::~TABDATFile() + * + * Destructor. + **********************************************************************/ +TABDATFile::~TABDATFile() +{ + Close(); +} + +/********************************************************************** + * TABDATFile::Open() + * + * Compatibility layer with new interface. + * Return 0 on success, -1 in case of failure. + **********************************************************************/ + +int TABDATFile::Open(const char *pszFname, const char* pszAccess, TABTableType eTableType) +{ + if( EQUALN(pszAccess, "r", 1) ) + return Open(pszFname, TABRead, eTableType); + else if( EQUALN(pszAccess, "w", 1) ) + return Open(pszFname, TABWrite, eTableType); + else + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: access mode \"%s\" not supported", pszAccess); + return -1; + } +} + +/********************************************************************** + * TABDATFile::Open() + * + * Open a .DAT file, and initialize the structures to be ready to read + * records from it. + * + * We currently support NATIVE and DBF tables for reading, and only + * NATIVE tables for writing. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABDATFile::Open(const char *pszFname, TABAccess eAccess, + TABTableType eTableType /*=TABNativeTable*/) +{ + int i; + + if (m_fp) + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: object already contains an open file"); + return -1; + } + + /*----------------------------------------------------------------- + * Validate access mode and make sure we use binary access. + *----------------------------------------------------------------*/ + const char* pszAccess = NULL; + if (eAccess == TABRead && (eTableType==TABTableNative || + eTableType==TABTableDBF) ) + { + pszAccess = "rb"; + } + else if (eAccess == TABWrite && eTableType==TABTableNative) + { + pszAccess = "wb+"; + } + else if (eAccess == TABReadWrite && eTableType==TABTableNative) + { + pszAccess = "rb+"; + } + else + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: access mode \"%d\" not supported with eTableType=%d", + eAccess, eTableType); + return -1; + } + m_eAccessMode = eAccess; + + /*----------------------------------------------------------------- + * Open file for reading + *----------------------------------------------------------------*/ + m_pszFname = CPLStrdup(pszFname); + m_fp = VSIFOpenL(m_pszFname, pszAccess); + m_eTableType = eTableType; + + if (m_fp == NULL) + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed for %s", m_pszFname); + CPLFree(m_pszFname); + m_pszFname = NULL; + return -1; + } + + if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) + { + /*------------------------------------------------------------ + * READ ACCESS: + * Read .DAT file header (record size, num records, etc...) + * m_poHeaderBlock will be reused later to read field definition + *-----------------------------------------------------------*/ + m_poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE); + m_poHeaderBlock->ReadFromFile(m_fp, 0, 32); + + m_poHeaderBlock->ReadByte(); // Table type ??? 0x03 + m_poHeaderBlock->ReadByte(); // Last update year + m_poHeaderBlock->ReadByte(); // Last update month + m_poHeaderBlock->ReadByte(); // Last update day + + m_numRecords = m_poHeaderBlock->ReadInt32(); + m_nFirstRecordPtr = m_poHeaderBlock->ReadInt16(); + m_nRecordSize = m_poHeaderBlock->ReadInt16(); + + m_numFields = m_nFirstRecordPtr/32 - 1; + + /*------------------------------------------------------------- + * Read the field definitions + * First 32 bytes field definition starts at byte 32 in file + *------------------------------------------------------------*/ + m_pasFieldDef = (TABDATFieldDef*)CPLCalloc(m_numFields, + sizeof(TABDATFieldDef)); + + for(i=0; iGotoByteInFile((i+1)*32); + m_poHeaderBlock->ReadBytes(11, (GByte*)m_pasFieldDef[i].szName); + m_pasFieldDef[i].szName[10] = '\0'; + m_pasFieldDef[i].cType = (char)m_poHeaderBlock->ReadByte(); + + m_poHeaderBlock->ReadInt32(); // Skip Bytes 12-15 + m_pasFieldDef[i].byLength = m_poHeaderBlock->ReadByte(); + m_pasFieldDef[i].byDecimals = m_poHeaderBlock->ReadByte(); + + m_pasFieldDef[i].eTABType = TABFUnknown; + } + + /*------------------------------------------------------------- + * Establish a good record block size to use based on record size, and + * then create m_poRecordBlock + * Record block size has to be a multiple of record size. + *------------------------------------------------------------*/ + m_nBlockSize = ((1024/m_nRecordSize)+1)*m_nRecordSize; + m_nBlockSize = MIN(m_nBlockSize, (m_numRecords*m_nRecordSize)); + + CPLAssert( m_poRecordBlock == NULL ); + m_poRecordBlock = new TABRawBinBlock(m_eAccessMode, FALSE); + m_poRecordBlock->InitNewBlock(m_fp, m_nBlockSize); + m_poRecordBlock->SetFirstBlockPtr(m_nFirstRecordPtr); + + m_bWriteHeaderInitialized = TRUE; + } + else + { + /*------------------------------------------------------------ + * WRITE ACCESS: + * Set acceptable defaults for all class members. + * The real header initialization will be done when the first + * record is written + *-----------------------------------------------------------*/ + m_poHeaderBlock = NULL; + + m_numRecords = 0; + m_nFirstRecordPtr = 0; + m_nRecordSize = 0; + m_numFields = 0; + m_pasFieldDef = NULL; + m_bWriteHeaderInitialized = FALSE; + } + + return 0; +} + +/********************************************************************** + * TABDATFile::Close() + * + * Close current file, and release all memory used. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABDATFile::Close() +{ + if (m_fp == NULL) + return 0; + + /*---------------------------------------------------------------- + * Write access: Update the header with number of records, etc. + * and add a CTRL-Z char at the end of the file. + *---------------------------------------------------------------*/ + if (m_eAccessMode != TABRead ) + { + SyncToDisk(); + } + + // Delete all structures + if (m_poHeaderBlock) + { + delete m_poHeaderBlock; + m_poHeaderBlock = NULL; + } + + if (m_poRecordBlock) + { + delete m_poRecordBlock; + m_poRecordBlock = NULL; + } + + // Close file + VSIFCloseL(m_fp); + m_fp = NULL; + + CPLFree(m_pszFname); + m_pszFname = NULL; + + CPLFree(m_pasFieldDef); + m_pasFieldDef = NULL; + + m_numFields = -1; + m_numRecords = -1; + m_nFirstRecordPtr = 0; + m_nBlockSize = 0; + m_nRecordSize = -1; + m_nCurRecordId = -1; + m_bWriteHeaderInitialized = FALSE; + m_bWriteEOF = FALSE; + m_bUpdated = FALSE; + + return 0; +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +int TABDATFile::SyncToDisk() +{ + if( m_eAccessMode == TABRead ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SyncToDisk() can be used only with Write access."); + return -1; + } + + if( !m_bUpdated && m_bWriteHeaderInitialized ) + return 0; + + // No need to call. CommitRecordToFile(). It is normally called by + // TABFeature::WriteRecordToDATFile() + if( WriteHeader() != 0 ) + return -1; + + m_bUpdated = FALSE; + return 0; +} + +/********************************************************************** + * TABDATFile::InitWriteHeader() + * + * Init the header members to be ready to write the header and data records + * to a newly created data file. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABDATFile::InitWriteHeader() +{ + int i; + + if (m_eAccessMode == TABRead || m_bWriteHeaderInitialized) + return 0; + + /*------------------------------------------------------------ + * Compute values for Record size, header size, etc. + *-----------------------------------------------------------*/ + m_nFirstRecordPtr = (m_numFields+1)*32 + 1; + + m_nRecordSize = 1; + for(i=0; iInitNewBlock(m_fp, m_nBlockSize); + m_poRecordBlock->SetFirstBlockPtr(m_nFirstRecordPtr); + + /*------------------------------------------------------------- + * Make sure this init. will be performed only once + *------------------------------------------------------------*/ + m_bWriteHeaderInitialized = TRUE; + + return 0; +} + +/********************************************************************** + * TABDATFile::WriteHeader() + * + * Init the header members to be ready to write the header and data records + * to a newly created data file. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABDATFile::WriteHeader() +{ + int i; + + if (m_eAccessMode == TABRead) + { + CPLError(CE_Failure, CPLE_NotSupported, + "WriteHeader() can be used only with Write access."); + return -1; + } + + if (!m_bWriteHeaderInitialized) + InitWriteHeader(); + + /*------------------------------------------------------------ + * Create a single block that will be used to generate the whole header. + *-----------------------------------------------------------*/ + if (m_poHeaderBlock == NULL) + m_poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE); + m_poHeaderBlock->InitNewBlock(m_fp, m_nFirstRecordPtr, 0); + + /*------------------------------------------------------------ + * First 32 bytes: main header block + *-----------------------------------------------------------*/ + m_poHeaderBlock->WriteByte(0x03); // Table type ??? 0x03 + + // __TODO__ Write the correct update date value + m_poHeaderBlock->WriteByte(99); // Last update year + m_poHeaderBlock->WriteByte(9); // Last update month + m_poHeaderBlock->WriteByte(9); // Last update day + + m_poHeaderBlock->WriteInt32(m_numRecords); + m_poHeaderBlock->WriteInt16((GInt16)m_nFirstRecordPtr); + m_poHeaderBlock->WriteInt16((GInt16)m_nRecordSize); + + m_poHeaderBlock->WriteZeros(20); // Pad rest with zeros + + /*------------------------------------------------------------- + * Field definitions follow. Each field def is 32 bytes. + *------------------------------------------------------------*/ + for(i=0; iWriteBytes(11, (GByte*)m_pasFieldDef[i].szName); + m_poHeaderBlock->WriteByte(m_pasFieldDef[i].cType); + + m_poHeaderBlock->WriteInt32(0); // Skip Bytes 12-15 + + m_poHeaderBlock->WriteByte(m_pasFieldDef[i].byLength); + m_poHeaderBlock->WriteByte(m_pasFieldDef[i].byDecimals); + + m_poHeaderBlock->WriteZeros(14); // Pad rest with zeros + } + + /*------------------------------------------------------------- + * Header ends with a 0x0d character. + *------------------------------------------------------------*/ + m_poHeaderBlock->WriteByte(0x0d); + + /*------------------------------------------------------------- + * Write the block to the file and return. + *------------------------------------------------------------*/ + return m_poHeaderBlock->CommitToFile(); +} + + + +/********************************************************************** + * TABDATFile::GetNumFields() + * + * Return the number of fields in this table. + * + * Returns a value >= 0 on success, -1 on error. + **********************************************************************/ +int TABDATFile::GetNumFields() +{ + return m_numFields; +} + +/********************************************************************** + * TABDATFile::GetNumRecords() + * + * Return the number of records in this table. + * + * Returns a value >= 0 on success, -1 on error. + **********************************************************************/ +int TABDATFile::GetNumRecords() +{ + return m_numRecords; +} + +/********************************************************************** + * TABDATFile::GetRecordBlock() + * + * Return a TABRawBinBlock reference positioned at the beginning of the + * specified record and ready to read (or write) field values from/to it. + * In read access, the returned block is guaranteed to contain at least one + * full record of data, and in write access, it is at least big enough to + * hold one full record. + * + * Note that record ids are positive and start at 1. + * + * In Write access, CommitRecordToFile() MUST be called after the + * data items have been written to the record, otherwise the record + * will never make it to the file. + * + * Returns a reference to the TABRawBinBlock on success or NULL on error. + * The returned pointer is a reference to a block object owned by this + * TABDATFile object and should not be freed by the caller. + **********************************************************************/ +TABRawBinBlock *TABDATFile::GetRecordBlock(int nRecordId) +{ + m_bCurRecordDeletedFlag = FALSE; + m_bWriteEOF = FALSE; + + if (m_eAccessMode == TABRead || nRecordId <= m_numRecords) + { + /*------------------------------------------------------------- + * READ ACCESS + *------------------------------------------------------------*/ + int nFileOffset; + + nFileOffset = m_nFirstRecordPtr+(nRecordId-1)*m_nRecordSize; + + /*------------------------------------------------------------- + * Move record block pointer to the right location + *------------------------------------------------------------*/ + if ( m_poRecordBlock == NULL || + nRecordId < 1 || nRecordId > m_numRecords || + m_poRecordBlock->GotoByteInFile(nFileOffset) != 0 ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed reading .DAT record block for record #%d in %s", + nRecordId, m_pszFname); + return NULL; + } + + /*------------------------------------------------------------- + * The first char of the record is a ' ' for an active record, or + * '*' for a deleted one. + * In the case of a deleted record, we simply return default + * values for each attribute... this is what MapInfo seems to do + * when it takes a .TAB with deleted records and exports it to .MIF + *------------------------------------------------------------*/ + if (m_poRecordBlock->ReadByte() != ' ') + { + m_bCurRecordDeletedFlag = TRUE; + } + } + else if (nRecordId > 0) + { + /*------------------------------------------------------------- + * WRITE ACCESS + *------------------------------------------------------------*/ + int nFileOffset; + + /*------------------------------------------------------------- + * Before writing the first record, we must generate the file + * header. We will also initialize class members such as record + * size, etc. and will create m_poRecordBlock. + *------------------------------------------------------------*/ + if (!m_bWriteHeaderInitialized) + { + WriteHeader(); + } + + m_bUpdated = TRUE; + + m_numRecords = MAX(nRecordId, m_numRecords); + if( nRecordId == m_numRecords ) + m_bWriteEOF = TRUE; + + nFileOffset = m_nFirstRecordPtr+(nRecordId-1)*m_nRecordSize; + + m_poRecordBlock->InitNewBlock(m_fp, m_nRecordSize, nFileOffset); + + /*------------------------------------------------------------- + * The first char of the record is the active/deleted flag. + * Automatically set it to ' ' (active). + *------------------------------------------------------------*/ + m_poRecordBlock->WriteByte(' '); + + } + + m_nCurRecordId = nRecordId; + + return m_poRecordBlock; +} + +/********************************************************************** + * TABDATFile::CommitRecordToFile() + * + * Commit the data record previously initialized with GetRecordBlock() + * to the file. This function must be called after writing the data + * values to a record otherwise the record will never make it to the + * file. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABDATFile::CommitRecordToFile() +{ + if (m_eAccessMode == TABRead || m_poRecordBlock == NULL) + return -1; + + if (m_poRecordBlock->CommitToFile() != 0) + return -1; + + /* If this is the end of file, write EOF character */ + if (m_bWriteEOF) + { + m_bWriteEOF = FALSE; + char cEOF = 26; + if (VSIFSeekL(m_fp, 0L, SEEK_END) == 0) + VSIFWriteL(&cEOF, 1, 1, m_fp); + } + + return 0; +} + +/********************************************************************** + * TABDATFile::MarkAsDeleted() + + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABDATFile::MarkAsDeleted() +{ + if (m_eAccessMode == TABRead || m_poRecordBlock == NULL) + return -1; + + int nFileOffset; + nFileOffset = m_nFirstRecordPtr+(m_nCurRecordId-1)*m_nRecordSize; + + if (m_poRecordBlock->GotoByteInFile(nFileOffset) != 0) + return -1; + + m_poRecordBlock->WriteByte('*'); + + if (m_poRecordBlock->CommitToFile() != 0) + return -1; + + m_bCurRecordDeletedFlag = TRUE; + m_bUpdated = TRUE; + + return 0; +} + +/********************************************************************** + * TABDATFile::MarkRecordAsExisting() + + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABDATFile::MarkRecordAsExisting() +{ + if (m_eAccessMode == TABRead || m_poRecordBlock == NULL) + return -1; + + int nFileOffset; + nFileOffset = m_nFirstRecordPtr+(m_nCurRecordId-1)*m_nRecordSize; + + if (m_poRecordBlock->GotoByteInFile(nFileOffset) != 0) + return -1; + + m_poRecordBlock->WriteByte(' '); + + m_bCurRecordDeletedFlag = FALSE; + m_bUpdated = TRUE; + + return 0; +} + + +/********************************************************************** + * TABDATFile::ValidateFieldInfoFromTAB() + * + * Check that the value read from the .TAB file by the caller are + * consistent with what is found in the .DAT header. + * + * Note that field ids are positive and start at 0. + * + * We have to use this function when opening a file for reading since + * the .DAT file does not contain the full field types information... + * a .DAT file is actually a .DBF file in which the .DBF types are + * handled in a special way... type 'C' fields are used to store binary + * values for most MapInfo types. + * + * For TABTableDBF, we actually have no validation to do since all types + * are stored as strings internally, so we'll just convert from string. + * + * Returns a value >= 0 if OK, -1 on error. + **********************************************************************/ +int TABDATFile::ValidateFieldInfoFromTAB(int iField, const char *pszName, + TABFieldType eType, + int nWidth, int nPrecision) +{ + int i = iField; // Just to make things shorter + + if (m_pasFieldDef == NULL || iField < 0 || iField >= m_numFields) + { + CPLError(CE_Failure, CPLE_FileIO, + "Invalid field %d (%s) in .TAB header. %s contains only %d fields.", + iField+1, pszName, m_pszFname, m_pasFieldDef? m_numFields:0); + return -1; + } + + /*----------------------------------------------------------------- + * We used to check that the .TAB field name matched the .DAT + * name stored internally, but apparently some tools that rename table + * field names only update the .TAB file and not the .DAT, so we won't + * do that name validation any more... we'll just check the type. + * + * With TABTableNative, we have to validate the field sizes as well + * because .DAT files use char fields to store binary values. + * With TABTableDBF, no need to validate field type since all + * fields are stored as strings internally. + *----------------------------------------------------------------*/ + if ((m_eTableType == TABTableNative && + ((eType == TABFChar && (m_pasFieldDef[i].cType != 'C' || + m_pasFieldDef[i].byLength != nWidth )) || + (eType == TABFDecimal && (m_pasFieldDef[i].cType != 'N' || + m_pasFieldDef[i].byLength != nWidth|| + m_pasFieldDef[i].byDecimals!=nPrecision)) || + (eType == TABFInteger && (m_pasFieldDef[i].cType != 'C' || + m_pasFieldDef[i].byLength != 4 )) || + (eType == TABFSmallInt && (m_pasFieldDef[i].cType != 'C' || + m_pasFieldDef[i].byLength != 2 )) || + (eType == TABFFloat && (m_pasFieldDef[i].cType != 'C' || + m_pasFieldDef[i].byLength != 8 )) || + (eType == TABFDate && (m_pasFieldDef[i].cType != 'C' || + m_pasFieldDef[i].byLength != 4 )) || + (eType == TABFTime && (m_pasFieldDef[i].cType != 'C' || + m_pasFieldDef[i].byLength != 4 )) || + (eType == TABFDateTime && (m_pasFieldDef[i].cType != 'C' || + m_pasFieldDef[i].byLength != 8 )) || + (eType == TABFLogical && (m_pasFieldDef[i].cType != 'L' || + m_pasFieldDef[i].byLength != 1 )) ) )) + { + CPLError(CE_Failure, CPLE_FileIO, + "Definition of field %d (%s) from .TAB file does not match " + "what is found in %s (name=%s, type=%c, width=%d, prec=%d)", + iField+1, pszName, m_pszFname, + m_pasFieldDef[i].szName, m_pasFieldDef[i].cType, + m_pasFieldDef[i].byLength, m_pasFieldDef[i].byDecimals); + return -1; + } + + m_pasFieldDef[i].eTABType = eType; + + return 0; +} + +/********************************************************************** + * TABDATFileSetFieldDefinition() + * + **********************************************************************/ +static int TABDATFileSetFieldDefinition(TABDATFieldDef* psFieldDef, + const char *pszName, TABFieldType eType, + int nWidth, int nPrecision) +{ + + /*----------------------------------------------------------------- + * Validate field width... must be <= 254 + *----------------------------------------------------------------*/ + if (nWidth > 254) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Invalid size (%d) for field '%s'. " + "Size must be 254 or less.", nWidth, pszName); + return -1; + } + + /*----------------------------------------------------------------- + * Map fields with width=0 (variable length in OGR) to a valid default + *----------------------------------------------------------------*/ + if (eType == TABFDecimal && nWidth == 0) + nWidth=20; + else if (nWidth == 0) + nWidth=254; /* char fields */ + + strncpy(psFieldDef->szName, pszName, 10); + psFieldDef->szName[10] = '\0'; + psFieldDef->eTABType = eType; + psFieldDef->byLength = (GByte)nWidth; + psFieldDef->byDecimals = (GByte)nPrecision; + + switch(eType) + { + case TABFChar: + psFieldDef->cType = 'C'; + break; + case TABFDecimal: + psFieldDef->cType = 'N'; + break; + case TABFInteger: + psFieldDef->cType = 'C'; + psFieldDef->byLength = 4; + break; + case TABFSmallInt: + psFieldDef->cType = 'C'; + psFieldDef->byLength = 2; + break; + case TABFFloat: + psFieldDef->cType = 'C'; + psFieldDef->byLength = 8; + break; + case TABFDate: + psFieldDef->cType = 'C'; + psFieldDef->byLength = 4; + break; + case TABFTime: + psFieldDef->cType = 'C'; + psFieldDef->byLength = 4; + break; + case TABFDateTime: + psFieldDef->cType = 'C'; + psFieldDef->byLength = 8; + break; + case TABFLogical: + psFieldDef->cType = 'L'; + psFieldDef->byLength = 1; + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported field type for field `%s'", pszName); + return -1; + } + + return 0; +} + +/********************************************************************** + * TABDATFile::AddField() + * + * Create a new field (column) in a newly created table. This function + * must be called after the file has been opened, but before writing the + * first record. + * + * Returns the new field index (a value >= 0) if OK, -1 on error. + **********************************************************************/ +int TABDATFile::AddField(const char *pszName, TABFieldType eType, + int nWidth, int nPrecision /*=0*/) +{ + if (m_eAccessMode == TABRead || m_eTableType != TABTableNative) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Operation not supported on read-only files or on non-native table."); + return -1; + } + + TABDATFieldDef sFieldDef; + if( TABDATFileSetFieldDefinition(&sFieldDef, pszName, eType, + nWidth, nPrecision) < 0 ) + return -1; + + if (m_numFields < 0) + m_numFields = 0; + + m_numFields++; + m_pasFieldDef = (TABDATFieldDef*)CPLRealloc(m_pasFieldDef, + m_numFields*sizeof(TABDATFieldDef)); + memcpy(&m_pasFieldDef[m_numFields-1], &sFieldDef, sizeof(sFieldDef)); + + /* If there are already records, we cannot update in place */ + /* so create a temporary .dat.tmp in which we create the new structure */ + /* and then copy the widen records */ + if( m_numRecords > 0 ) + { + TABDATFile oTempFile; + CPLString osOriginalFile(m_pszFname); + CPLString osTmpFile(m_pszFname); + osTmpFile += ".tmp"; + if( oTempFile.Open( osTmpFile.c_str(), TABWrite ) != 0 ) + return -1; + + int i; + /* Create field structure */ + for(i = 0; i < m_numFields; i++) + { + oTempFile.AddField(m_pasFieldDef[i].szName, + m_pasFieldDef[i].eTABType, + m_pasFieldDef[i].byLength, + m_pasFieldDef[i].byDecimals); + } + + GByte* pabyRecord = (GByte*)CPLMalloc(m_nRecordSize); + + /* Copy records */ + for(int j = 0; j < m_numRecords; j++) + { + if( GetRecordBlock(1+j) == NULL || + oTempFile.GetRecordBlock(1+j) == NULL ) + { + CPLFree(pabyRecord); + oTempFile.Close(); + VSIUnlink(osTmpFile); + return -1; + } + if (m_bCurRecordDeletedFlag) + { + oTempFile.MarkAsDeleted(); + } + else + { + if( m_poRecordBlock->ReadBytes(m_nRecordSize-1, pabyRecord) != 0 || + oTempFile.m_poRecordBlock->WriteBytes(m_nRecordSize-1, pabyRecord) != 0 || + oTempFile.m_poRecordBlock->WriteZeros(m_pasFieldDef[m_numFields-1].byLength) != 0 ) + { + CPLFree(pabyRecord); + oTempFile.Close(); + VSIUnlink(osTmpFile); + return -1; + } + oTempFile.CommitRecordToFile(); + } + } + + CPLFree(pabyRecord); + + /* Close temporary file */ + oTempFile.Close(); + + /* Backup field definitions as we will need to set the TABFieldType */ + TABDATFieldDef* pasFieldDefTmp = (TABDATFieldDef*)CPLMalloc(m_numFields*sizeof(TABDATFieldDef)); + memcpy(pasFieldDefTmp, m_pasFieldDef, m_numFields*sizeof(TABDATFieldDef)); + + /* Close ourselves */ + m_numFields--; /* so that Close() doesn't see the new field */ + Close(); + + /* Move temporary file as main .data file and reopen it */ + VSIUnlink(osOriginalFile); + VSIRename(osTmpFile, osOriginalFile); + if( Open( osOriginalFile, TABReadWrite ) < 0 ) + { + CPLFree(pasFieldDefTmp); + return -1; + } + + /* Restore saved TABFieldType */ + for(i = 0; i < m_numFields; i++) + { + m_pasFieldDef[i].eTABType = pasFieldDefTmp[i].eTABType; + } + CPLFree(pasFieldDefTmp); + } + + return 0; +} + + +/************************************************************************/ +/* DeleteField() */ +/************************************************************************/ + +int TABDATFile::DeleteField( int iField ) +{ + if (m_eAccessMode == TABRead || m_eTableType != TABTableNative) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Operation not supported on read-only files or on non-native table."); + return -1; + } + + if( iField < 0 || iField >= m_numFields ) + { + CPLError(CE_Failure, CPLE_IllegalArg, "Invalid field index: %d", iField); + return -1; + } + + /* If no records have been written, then just remove from the field */ + /* definition array */ + if( m_numRecords <= 0 ) + { + if( iField < m_numFields-1 ) + { + memmove(m_pasFieldDef + iField, m_pasFieldDef + iField + 1, + (m_numFields-1-iField)*sizeof(TABDATFieldDef)); + } + m_numFields --; + return 0; + } + + if( m_numFields == 1 ) + { + CPLError(CE_Failure, CPLE_IllegalArg, "Cannot delete the single remaining field."); + return -1; + } + + /* Otherwise we need to do a temporary file */ + TABDATFile oTempFile; + CPLString osOriginalFile(m_pszFname); + CPLString osTmpFile(m_pszFname); + osTmpFile += ".tmp"; + if( oTempFile.Open( osTmpFile.c_str(), TABWrite ) != 0 ) + return -1; + + int i; + /* Create field structure */ + int nRecordSizeBefore = 0; + int nRecordSizeAfter = 0; + for(i = 0; i < m_numFields; i++) + { + if( i != iField ) + { + if( i < iField ) nRecordSizeBefore += m_pasFieldDef[i].byLength; + else if( i > iField ) nRecordSizeAfter += m_pasFieldDef[i].byLength; + oTempFile.AddField(m_pasFieldDef[i].szName, + m_pasFieldDef[i].eTABType, + m_pasFieldDef[i].byLength, + m_pasFieldDef[i].byDecimals); + } + } + + CPLAssert(nRecordSizeBefore + m_pasFieldDef[iField].byLength + nRecordSizeAfter == m_nRecordSize - 1); + + GByte* pabyRecord = (GByte*)CPLMalloc(m_nRecordSize); + + /* Copy records */ + for(int j = 0; j < m_numRecords; j++) + { + if( GetRecordBlock(1+j) == NULL || + oTempFile.GetRecordBlock(1+j) == NULL ) + { + CPLFree(pabyRecord); + oTempFile.Close(); + VSIUnlink(osTmpFile); + return -1; + } + if (m_bCurRecordDeletedFlag) + { + oTempFile.MarkAsDeleted(); + } + else + { + if( m_poRecordBlock->ReadBytes(m_nRecordSize-1, pabyRecord) != 0 || + (nRecordSizeBefore > 0 && oTempFile.m_poRecordBlock->WriteBytes(nRecordSizeBefore, pabyRecord) != 0) || + (nRecordSizeAfter > 0 && oTempFile.m_poRecordBlock->WriteBytes(nRecordSizeAfter, + pabyRecord + nRecordSizeBefore + m_pasFieldDef[iField].byLength) != 0) ) + { + CPLFree(pabyRecord); + oTempFile.Close(); + VSIUnlink(osTmpFile); + return -1; + } + oTempFile.CommitRecordToFile(); + } + } + + CPLFree(pabyRecord); + + /* Close temporary file */ + oTempFile.Close(); + + /* Backup field definitions as we will need to set the TABFieldType */ + TABDATFieldDef* pasFieldDefTmp = (TABDATFieldDef*)CPLMalloc(m_numFields*sizeof(TABDATFieldDef)); + memcpy(pasFieldDefTmp, m_pasFieldDef, m_numFields*sizeof(TABDATFieldDef)); + + /* Close ourselves */ + Close(); + + /* Move temporary file as main .data file and reopen it */ + VSIUnlink(osOriginalFile); + VSIRename(osTmpFile, osOriginalFile); + if( Open( osOriginalFile, TABReadWrite ) < 0 ) + { + CPLFree(pasFieldDefTmp); + return -1; + } + + /* Restore saved TABFieldType */ + for(i = 0; i < m_numFields; i++) + { + if( i < iField ) + m_pasFieldDef[i].eTABType = pasFieldDefTmp[i].eTABType; + else + m_pasFieldDef[i].eTABType = pasFieldDefTmp[i+1].eTABType; + } + CPLFree(pasFieldDefTmp); + + return 0; +} + +/************************************************************************/ +/* ReorderFields() */ +/************************************************************************/ + +int TABDATFile::ReorderFields( int* panMap ) +{ + if (m_eAccessMode == TABRead || m_eTableType != TABTableNative) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Operation not supported on read-only files or on non-native table."); + return -1; + } + + if( m_numFields == 0) + return 0; + + OGRErr eErr = OGRCheckPermutation(panMap, m_numFields); + if (eErr != OGRERR_NONE) + return -1; + + /* If no records have been written, then just reorder the field */ + /* definition array */ + if( m_numRecords <= 0 ) + { + TABDATFieldDef* pasFieldDefTmp = (TABDATFieldDef*)CPLMalloc(m_numFields*sizeof(TABDATFieldDef)); + memcpy(pasFieldDefTmp, m_pasFieldDef, m_numFields*sizeof(TABDATFieldDef)); + for(int i = 0; i < m_numFields; i++) + { + memcpy(m_pasFieldDef + i, pasFieldDefTmp + panMap[i], + sizeof(TABDATFieldDef)); + } + CPLFree(pasFieldDefTmp); + return 0; + } + + // We could theoretically update in place, but a sudden interruption + // would leave the file in a undefined state. + + TABDATFile oTempFile; + CPLString osOriginalFile(m_pszFname); + CPLString osTmpFile(m_pszFname); + osTmpFile += ".tmp"; + if( oTempFile.Open( osTmpFile.c_str(), TABWrite ) != 0 ) + return -1; + + int i; + /* Create field structure */ + int* panOldOffset = (int*)CPLMalloc(m_numFields * sizeof(int)); + for(i = 0; i < m_numFields; i++) + { + int iBefore = panMap[i]; + if( i == 0 ) + panOldOffset[i] = 0; + else + panOldOffset[i] = panOldOffset[i-1] + m_pasFieldDef[i-1].byLength; + oTempFile.AddField(m_pasFieldDef[iBefore].szName, + m_pasFieldDef[iBefore].eTABType, + m_pasFieldDef[iBefore].byLength, + m_pasFieldDef[iBefore].byDecimals); + } + + GByte* pabyRecord = (GByte*)CPLMalloc(m_nRecordSize); + + /* Copy records */ + for(int j = 0; j < m_numRecords; j++) + { + if( GetRecordBlock(1+j) == NULL || + oTempFile.GetRecordBlock(1+j) == NULL ) + { + CPLFree(pabyRecord); + CPLFree(panOldOffset); + oTempFile.Close(); + VSIUnlink(osTmpFile); + return -1; + } + if (m_bCurRecordDeletedFlag) + { + oTempFile.MarkAsDeleted(); + } + else + { + if( m_poRecordBlock->ReadBytes(m_nRecordSize-1, pabyRecord) != 0 ) + { + CPLFree(pabyRecord); + CPLFree(panOldOffset); + oTempFile.Close(); + VSIUnlink(osTmpFile); + return -1; + } + for(i = 0; i < m_numFields; i++) + { + int iBefore = panMap[i]; + if( oTempFile.m_poRecordBlock->WriteBytes( + m_pasFieldDef[iBefore].byLength, pabyRecord + panOldOffset[iBefore]) != 0 ) + { + CPLFree(pabyRecord); + CPLFree(panOldOffset); + oTempFile.Close(); + VSIUnlink(osTmpFile); + return -1; + } + } + + oTempFile.CommitRecordToFile(); + } + } + + CPLFree(pabyRecord); + CPLFree(panOldOffset); + + /* Close temporary file */ + oTempFile.Close(); + + /* Backup field definitions as we will need to set the TABFieldType */ + TABDATFieldDef* pasFieldDefTmp = (TABDATFieldDef*)CPLMalloc(m_numFields*sizeof(TABDATFieldDef)); + memcpy(pasFieldDefTmp, m_pasFieldDef, m_numFields*sizeof(TABDATFieldDef)); + + /* Close ourselves */ + Close(); + + /* Move temporary file as main .data file and reopen it */ + VSIUnlink(osOriginalFile); + VSIRename(osTmpFile, osOriginalFile); + if( Open( osOriginalFile, TABReadWrite ) < 0 ) + { + CPLFree(pasFieldDefTmp); + return -1; + } + + /* Restore saved TABFieldType */ + for(i = 0; i < m_numFields; i++) + { + int iBefore = panMap[i]; + m_pasFieldDef[i].eTABType = pasFieldDefTmp[iBefore].eTABType; + } + CPLFree(pasFieldDefTmp); + + return 0; +} + +/************************************************************************/ +/* AlterFieldDefn() */ +/************************************************************************/ + +int TABDATFile::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) +{ + if (m_eAccessMode == TABRead || m_eTableType != TABTableNative) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Operation not supported on read-only files or on non-native table."); + return -1; + } + + if( iField < 0 || iField >= m_numFields ) + { + CPLError(CE_Failure, CPLE_IllegalArg, "Invalid field index: %d", iField); + return -1; + } + + TABFieldType eTABType = m_pasFieldDef[iField].eTABType; + int nWidth = m_pasFieldDef[iField].byLength ; + int nWidthDummy; + if( (nFlags & ALTER_TYPE_FLAG) ) + { + if( IMapInfoFile::GetTABType( poNewFieldDefn, &eTABType, &nWidthDummy ) < 0 ) + return -1; + } + if( (nFlags & ALTER_WIDTH_PRECISION_FLAG) ) + { + TABFieldType eTABTypeDummy; + if( IMapInfoFile::GetTABType( poNewFieldDefn, &eTABTypeDummy, &nWidth ) < 0 ) + return -1; + } + + if ((nFlags & ALTER_TYPE_FLAG) && + eTABType != m_pasFieldDef[iField].eTABType) + { + if ( eTABType != TABFChar ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can only convert to OFTString"); + return -1; + } + if( (nFlags & ALTER_WIDTH_PRECISION_FLAG) == 0 ) + nWidth = 254; + } + + if (nFlags & ALTER_WIDTH_PRECISION_FLAG) + { + if( eTABType != TABFChar && nWidth != m_pasFieldDef[iField].byLength ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Resizing only supported on String fields"); + return -1; + } + } + + if (nFlags & ALTER_NAME_FLAG) + { + strncpy(m_pasFieldDef[iField].szName, poNewFieldDefn->GetNameRef(), 10); + m_pasFieldDef[iField].szName[10] = '\0'; + /* If renaming is the only operation, then nothing more to do */ + if( nFlags == ALTER_NAME_FLAG ) + { + m_bUpdated = TRUE; + return 0; + } + } + + if( m_numRecords <= 0) + { + if( (nFlags & ALTER_TYPE_FLAG) && + eTABType != m_pasFieldDef[iField].eTABType) + { + TABDATFieldDef sFieldDef; + TABDATFileSetFieldDefinition(&sFieldDef, + m_pasFieldDef[iField].szName, eTABType, + m_pasFieldDef[iField].byLength, + m_pasFieldDef[iField].byDecimals); + memcpy(&m_pasFieldDef[iField], &sFieldDef, sizeof(sFieldDef)); + } + if (nFlags & ALTER_WIDTH_PRECISION_FLAG) + { + m_pasFieldDef[iField].byLength = (GByte)nWidth; + } + return 0; + } + + /* Otherwise we need to do a temporary file */ + TABDATFile oTempFile; + CPLString osOriginalFile(m_pszFname); + CPLString osTmpFile(m_pszFname); + osTmpFile += ".tmp"; + if( oTempFile.Open( osTmpFile.c_str(), TABWrite ) != 0 ) + return -1; + + int i; + /* Create field structure */ + int nRecordSizeBefore = 0; + int nRecordSizeAfter = 0; + TABDATFieldDef sFieldDef; + TABDATFileSetFieldDefinition(&sFieldDef, + m_pasFieldDef[iField].szName, + eTABType, + nWidth, + m_pasFieldDef[iField].byDecimals); + + for(i = 0; i < m_numFields; i++) + { + if( i != iField ) + { + if( i < iField ) nRecordSizeBefore += m_pasFieldDef[i].byLength; + else if( i > iField ) nRecordSizeAfter += m_pasFieldDef[i].byLength; + oTempFile.AddField(m_pasFieldDef[i].szName, + m_pasFieldDef[i].eTABType, + m_pasFieldDef[i].byLength, + m_pasFieldDef[i].byDecimals); + } + else + { + oTempFile.AddField(sFieldDef.szName, + sFieldDef.eTABType, + sFieldDef.byLength, + sFieldDef.byDecimals); + } + } + + GByte* pabyRecord = (GByte*)CPLMalloc(m_nRecordSize); + char* pabyNewField = (char*)CPLMalloc(sFieldDef.byLength + 1); + + /* Copy records */ + for(int j = 0; j < m_numRecords; j++) + { + if( GetRecordBlock(1+j) == NULL || + oTempFile.GetRecordBlock(1+j) == NULL ) + { + CPLFree(pabyRecord); + CPLFree(pabyNewField); + oTempFile.Close(); + VSIUnlink(osTmpFile); + return -1; + } + if (m_bCurRecordDeletedFlag) + { + oTempFile.MarkAsDeleted(); + } + else + { + if( nRecordSizeBefore > 0 && + (m_poRecordBlock->ReadBytes(nRecordSizeBefore, pabyRecord) != 0 || + oTempFile.m_poRecordBlock->WriteBytes(nRecordSizeBefore, pabyRecord) != 0) ) + { + CPLFree(pabyRecord); + CPLFree(pabyNewField); + oTempFile.Close(); + VSIUnlink(osTmpFile); + return -1; + } + + memset(pabyNewField, 0, sFieldDef.byLength + 1); + if( m_pasFieldDef[iField].eTABType == TABFChar ) + { + strncpy(pabyNewField, ReadCharField(m_pasFieldDef[iField].byLength), sFieldDef.byLength); + } + else if( m_pasFieldDef[iField].eTABType == TABFInteger ) + { + snprintf(pabyNewField, sFieldDef.byLength, "%d", ReadIntegerField(m_pasFieldDef[iField].byLength)); + } + else if( m_pasFieldDef[iField].eTABType == TABFSmallInt ) + { + snprintf(pabyNewField, sFieldDef.byLength, "%d", ReadSmallIntField(m_pasFieldDef[iField].byLength)); + } + else if( m_pasFieldDef[iField].eTABType == TABFFloat ) + { + CPLsnprintf(pabyNewField, sFieldDef.byLength, "%.18f", ReadFloatField(m_pasFieldDef[iField].byLength)); + } + else if( m_pasFieldDef[iField].eTABType == TABFDecimal ) + { + CPLsnprintf(pabyNewField, sFieldDef.byLength, "%.18f", ReadFloatField(m_pasFieldDef[iField].byLength)); + } + else if( m_pasFieldDef[iField].eTABType == TABFLogical ) + { + strncpy(pabyNewField, ReadLogicalField(m_pasFieldDef[iField].byLength), sFieldDef.byLength); + } + else if( m_pasFieldDef[iField].eTABType == TABFDate ) + { + strncpy(pabyNewField, ReadDateField(m_pasFieldDef[iField].byLength), sFieldDef.byLength); + } + else if( m_pasFieldDef[iField].eTABType == TABFTime ) + { + strncpy(pabyNewField, ReadTimeField(m_pasFieldDef[iField].byLength), sFieldDef.byLength); + } + else if( m_pasFieldDef[iField].eTABType == TABFDateTime ) + { + strncpy(pabyNewField, ReadDateTimeField(m_pasFieldDef[iField].byLength), sFieldDef.byLength); + } + + if( oTempFile.m_poRecordBlock->WriteBytes(sFieldDef.byLength, (GByte*)pabyNewField) != 0 || + (nRecordSizeAfter > 0 && + (m_poRecordBlock->ReadBytes(nRecordSizeAfter, pabyRecord) != 0 || + oTempFile.m_poRecordBlock->WriteBytes(nRecordSizeAfter, pabyRecord) != 0)) ) + { + CPLFree(pabyRecord); + CPLFree(pabyNewField); + oTempFile.Close(); + VSIUnlink(osTmpFile); + return -1; + } + oTempFile.CommitRecordToFile(); + } + } + + CPLFree(pabyRecord); + CPLFree(pabyNewField); + + /* Close temporary file */ + oTempFile.Close(); + + /* Backup field definitions as we will need to set the TABFieldType */ + TABDATFieldDef* pasFieldDefTmp = (TABDATFieldDef*)CPLMalloc(m_numFields*sizeof(TABDATFieldDef)); + memcpy(pasFieldDefTmp, m_pasFieldDef, m_numFields*sizeof(TABDATFieldDef)); + + /* Close ourselves */ + Close(); + + /* Move temporary file as main .data file and reopen it */ + VSIUnlink(osOriginalFile); + VSIRename(osTmpFile, osOriginalFile); + if( Open( osOriginalFile, TABReadWrite ) < 0 ) + { + CPLFree(pasFieldDefTmp); + return -1; + } + + /* Restore saved TABFieldType */ + for(i = 0; i < m_numFields; i++) + { + if( i != iField ) + m_pasFieldDef[i].eTABType = pasFieldDefTmp[i].eTABType; + else + m_pasFieldDef[i].eTABType = eTABType; + } + CPLFree(pasFieldDefTmp); + + return 0; +} + + +/********************************************************************** + * TABDATFile::GetFieldType() + * + * Returns the native field type for field # nFieldId as previously set + * by ValidateFieldInfoFromTAB(). + * + * Note that field ids are positive and start at 0. + **********************************************************************/ +TABFieldType TABDATFile::GetFieldType(int nFieldId) +{ + if (m_pasFieldDef == NULL || nFieldId < 0 || nFieldId >= m_numFields) + return TABFUnknown; + + return m_pasFieldDef[nFieldId].eTABType; +} + +/********************************************************************** + * TABDATFile::GetFieldWidth() + * + * Returns the width for field # nFieldId as previously read from the + * .DAT header. + * + * Note that field ids are positive and start at 0. + **********************************************************************/ +int TABDATFile::GetFieldWidth(int nFieldId) +{ + if (m_pasFieldDef == NULL || nFieldId < 0 || nFieldId >= m_numFields) + return 0; + + return m_pasFieldDef[nFieldId].byLength; +} + +/********************************************************************** + * TABDATFile::GetFieldPrecision() + * + * Returns the precision for field # nFieldId as previously read from the + * .DAT header. + * + * Note that field ids are positive and start at 0. + **********************************************************************/ +int TABDATFile::GetFieldPrecision(int nFieldId) +{ + if (m_pasFieldDef == NULL || nFieldId < 0 || nFieldId >= m_numFields) + return 0; + + return m_pasFieldDef[nFieldId].byDecimals; +} + +/********************************************************************** + * TABDATFile::ReadCharField() + * + * Read the character field value at the current position in the data + * block. + * + * Use GetRecordBlock() to position the data block to the beginning of + * a record before attempting to read values. + * + * nWidth is the field length, as defined in the .DAT header. + * + * Returns a reference to an internal buffer that will be valid only until + * the next field is read, or "" if the operation failed, in which case + * CPLError() will have been called. + **********************************************************************/ +const char *TABDATFile::ReadCharField(int nWidth) +{ + // If current record has been deleted, then return an acceptable + // default value. + if (m_bCurRecordDeletedFlag) + return ""; + + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't read field value: file is not opened."); + return ""; + } + + if (nWidth < 1 || nWidth > 255) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Illegal width for a char field: %d", nWidth); + return ""; + } + + if (m_poRecordBlock->ReadBytes(nWidth, (GByte*)m_szBuffer) != 0) + return ""; + + m_szBuffer[nWidth] = '\0'; + + // NATIVE tables are padded with '\0' chars, but DBF tables are padded + // with spaces... get rid of the trailing spaces. + if (m_eTableType == TABTableDBF) + { + int nLen = strlen(m_szBuffer)-1; + while(nLen>=0 && m_szBuffer[nLen] == ' ') + m_szBuffer[nLen--] = '\0'; + } + + return m_szBuffer; +} + +/********************************************************************** + * TABDATFile::ReadIntegerField() + * + * Read the integer field value at the current position in the data + * block. + * + * Note: nWidth is used only with TABTableDBF types. + * + * CPLError() will have been called if something fails. + **********************************************************************/ +GInt32 TABDATFile::ReadIntegerField(int nWidth) +{ + // If current record has been deleted, then return an acceptable + // default value. + if (m_bCurRecordDeletedFlag) + return 0; + + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't read field value: file is not opened."); + return 0; + } + + if (m_eTableType == TABTableDBF) + return atoi(ReadCharField(nWidth)); + + return m_poRecordBlock->ReadInt32(); +} + +/********************************************************************** + * TABDATFile::ReadSmallIntField() + * + * Read the smallint field value at the current position in the data + * block. + * + * Note: nWidth is used only with TABTableDBF types. + * + * CPLError() will have been called if something fails. + **********************************************************************/ +GInt16 TABDATFile::ReadSmallIntField(int nWidth) +{ + // If current record has been deleted, then return an acceptable + // default value. + if (m_bCurRecordDeletedFlag) + return 0; + + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't read field value: file is not opened."); + return 0; + } + + if (m_eTableType == TABTableDBF) + return (GInt16)atoi(ReadCharField(nWidth)); + + return m_poRecordBlock->ReadInt16(); +} + +/********************************************************************** + * TABDATFile::ReadFloatField() + * + * Read the float field value at the current position in the data + * block. + * + * Note: nWidth is used only with TABTableDBF types. + * + * CPLError() will have been called if something fails. + **********************************************************************/ +double TABDATFile::ReadFloatField(int nWidth) +{ + // If current record has been deleted, then return an acceptable + // default value. + if (m_bCurRecordDeletedFlag) + return 0.0; + + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't read field value: file is not opened."); + return 0.0; + } + + if (m_eTableType == TABTableDBF) + return CPLAtof(ReadCharField(nWidth)); + + return m_poRecordBlock->ReadDouble(); +} + +/********************************************************************** + * TABDATFile::ReadLogicalField() + * + * Read the logical field value at the current position in the data + * block. + * + * The file contains either 0 or 1, and we return a string with + * "F" (false) or "T" (true) + * + * Note: nWidth is used only with TABTableDBF types. + * + * CPLError() will have been called if something fails. + **********************************************************************/ +const char *TABDATFile::ReadLogicalField(int nWidth) +{ + GByte bValue; + + // If current record has been deleted, then return an acceptable + // default value. + if (m_bCurRecordDeletedFlag) + return "F"; + + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't read field value: file is not opened."); + return ""; + } + + if (m_eTableType == TABTableDBF) + { + const char *pszVal = ReadCharField(nWidth); + bValue = (pszVal && strchr("1YyTt", pszVal[0]) != NULL); + } + else + { + // In Native tables, we are guaranteed it is 1 byte with 0/1 value + bValue = m_poRecordBlock->ReadByte(); + } + + return bValue? "T":"F"; +} + +/********************************************************************** + * TABDATFile::ReadDateField() + * + * Read the logical field value at the current position in the data + * block. + * + * A date field is a 4 bytes binary value in which the first byte is + * the day, followed by 1 byte for the month, and 2 bytes for the year. + * + * We return an 8 chars string in the format "YYYYMMDD" + * + * Note: nWidth is used only with TABTableDBF types. + * + * Returns a reference to an internal buffer that will be valid only until + * the next field is read, or "" if the operation failed, in which case + * CPLError() will have been called. + **********************************************************************/ +const char *TABDATFile::ReadDateField(int nWidth) +{ + int nDay, nMonth, nYear, status; + nDay = nMonth = nYear = 0; + + if ((status = ReadDateField(nWidth, &nYear, &nMonth, &nDay)) == -1) + return ""; + + sprintf(m_szBuffer, "%4.4d%2.2d%2.2d", nYear, nMonth, nDay); + + return m_szBuffer; +} + +int TABDATFile::ReadDateField(int nWidth, int *nYear, int *nMonth, int *nDay) +{ + // If current record has been deleted, then return an acceptable + // default value. + if (m_bCurRecordDeletedFlag) + return -1; + + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't read field value: file is not opened."); + return -1; + } + + // With .DBF files, the value should already be + // stored in YYYYMMDD format according to DBF specs. + if (m_eTableType == TABTableDBF) + { + strcpy(m_szBuffer,ReadCharField(nWidth)); + sscanf(m_szBuffer, "%4d%2d%2d", nYear, nMonth, nDay); + } + else + { + *nYear = m_poRecordBlock->ReadInt16(); + *nMonth = m_poRecordBlock->ReadByte(); + *nDay = m_poRecordBlock->ReadByte(); + } + + if (CPLGetLastErrorNo() != 0 || (*nYear==0 && *nMonth==0 && *nDay==0)) + return -1; + + return 0; +} + +/********************************************************************** + * TABDATFile::ReadTimeField() + * + * Read the Time field value at the current position in the data + * block. + * + * A time field is a 4 bytes binary value which represents the number + * of milliseconds since midnight. + * + * We return a 9 char string in the format "HHMMSSMMM" + * + * Note: nWidth is used only with TABTableDBF types. + * + * Returns a reference to an internal buffer that will be valid only until + * the next field is read, or "" if the operation failed, in which case + * CPLError() will have been called. + **********************************************************************/ +const char *TABDATFile::ReadTimeField(int nWidth) +{ + int nHour, nMinute, nSecond, nMS, status; + nHour = nMinute = nSecond = nMS = 0; + + if ((status = ReadTimeField(nWidth, &nHour, &nMinute, &nSecond, &nMS)) == -1) + return ""; + + sprintf(m_szBuffer, "%2.2d%2.2d%2.2d%3.3d", nHour, nMinute, nSecond, nMS); + + return m_szBuffer; +} + +int TABDATFile::ReadTimeField(int nWidth, int *nHour, int *nMinute, + int *nSecond, int *nMS) +{ + GInt32 nS = 0; + // If current record has been deleted, then return an acceptable + // default value. + if (m_bCurRecordDeletedFlag) + return -1; + + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't read field value: file is not opened."); + return -1; + } + + // With .DBF files, the value should already be stored in + // HHMMSSMMM format according to DBF specs. + if (m_eTableType == TABTableDBF) + { + strcpy(m_szBuffer,ReadCharField(nWidth)); + sscanf(m_szBuffer,"%2d%2d%2d%3d", + nHour, nMinute, nSecond, nMS); + } + else + { + nS = m_poRecordBlock->ReadInt32(); // Convert time from ms to sec + } + + // nS is set to -1 when the value is 'not set' + if (CPLGetLastErrorNo() != 0 || nS < 0 || (nS>86400000)) + return -1; + + *nHour = int(nS/3600000); + *nMinute = int((nS/1000 - *nHour*3600)/60); + *nSecond = int(nS/1000 - *nHour*3600 - *nMinute*60); + *nMS = int(nS-*nHour*3600000-*nMinute*60000-*nSecond*1000); + + return 0; +} + +/********************************************************************** + * TABDATFile::ReadDateTimeField() + * + * Read the DateTime field value at the current position in the data + * block. + * + * A datetime field is an 8 bytes binary value in which the first byte is + * the day, followed by 1 byte for the month, and 2 bytes for the year. After + * this is 4 bytes which represents the number of milliseconds since midnight. + * + * We return an 17 chars string in the format "YYYYMMDDhhmmssmmm" + * + * Note: nWidth is used only with TABTableDBF types. + * + * Returns a reference to an internal buffer that will be valid only until + * the next field is read, or "" if the operation failed, in which case + * CPLError() will have been called. + **********************************************************************/ +const char *TABDATFile::ReadDateTimeField(int nWidth) +{ + int nDay, nMonth, nYear, nHour, nMinute, nSecond, nMS, status; + nDay = nMonth = nYear = nHour = nMinute = nSecond = nMS = 0; + + if ((status = ReadDateTimeField(nWidth, &nYear, &nMonth, &nDay, &nHour, + &nMinute, &nSecond, &nMS)) == -1) + return ""; + + sprintf(m_szBuffer, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d%3.3d", + nYear, nMonth, nDay, nHour, nMinute, nSecond, nMS); + + return m_szBuffer; +} + +int TABDATFile::ReadDateTimeField(int nWidth, int *nYear, int *nMonth, int *nDay, + int *nHour, int *nMinute, int *nSecond, int *nMS) +{ + GInt32 nS = 0; + // If current record has been deleted, then return an acceptable + // default value. + if (m_bCurRecordDeletedFlag) + return -1; + + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't read field value: file is not opened."); + return -1; + } + + // With .DBF files, the value should already be stored in + // YYYYMMDD format according to DBF specs. + if (m_eTableType == TABTableDBF) + { + strcpy(m_szBuffer,ReadCharField(nWidth)); + sscanf(m_szBuffer, "%4d%2d%2d%2d%2d%2d%3d", + nYear, nMonth, nDay, nHour, nMinute, nSecond, nMS); + } + else + { + *nYear = m_poRecordBlock->ReadInt16(); + *nMonth = m_poRecordBlock->ReadByte(); + *nDay = m_poRecordBlock->ReadByte(); + nS = m_poRecordBlock->ReadInt32(); + } + + if (CPLGetLastErrorNo() != 0 || + (*nYear==0 && *nMonth==0 && *nDay==0) || (nS>86400000)) + return -1; + + *nHour = int(nS/3600000); + *nMinute = int((nS/1000 - *nHour*3600)/60); + *nSecond = int(nS/1000 - *nHour*3600 - *nMinute*60); + *nMS = int(nS-*nHour*3600000-*nMinute*60000-*nSecond*1000); + + return 0; +} + +/********************************************************************** + * TABDATFile::ReadDecimalField() + * + * Read the decimal field value at the current position in the data + * block. + * + * A decimal field is a floating point value with a fixed number of digits + * stored as a character string. + * + * nWidth is the field length, as defined in the .DAT header. + * + * We return the value as a binary double. + * + * CPLError() will have been called if something fails. + **********************************************************************/ +double TABDATFile::ReadDecimalField(int nWidth) +{ + const char *pszVal; + + // If current record has been deleted, then return an acceptable + // default value. + if (m_bCurRecordDeletedFlag) + return 0.0; + + pszVal = ReadCharField(nWidth); + + return CPLAtof(pszVal); +} + + +/********************************************************************** + * TABDATFile::WriteCharField() + * + * Write the character field value at the current position in the data + * block. + * + * Use GetRecordBlock() to position the data block to the beginning of + * a record before attempting to write values. + * + * nWidth is the field length, as defined in the .DAT header. + * + * Returns 0 on success, or -1 if the operation failed, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABDATFile::WriteCharField(const char *pszStr, int nWidth, + TABINDFile *poINDFile, int nIndexNo) +{ + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't write field value: GetRecordBlock() has not been called."); + return -1; + } + + if (nWidth < 1 || nWidth > 255) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Illegal width for a char field: %d", nWidth); + return -1; + } + + // + // Write the buffer after making sure that we don't try to read + // past the end of the source buffer. The rest of the field will + // be padded with zeros if source string is shorter than specified + // field width. + // + int nLen = strlen(pszStr); + nLen = MIN(nLen, nWidth); + + if ((nLen>0 && m_poRecordBlock->WriteBytes(nLen, (GByte*)pszStr) != 0) || + (nWidth-nLen > 0 && m_poRecordBlock->WriteZeros(nWidth-nLen)!=0) ) + return -1; + + // Update Index + if (poINDFile && nIndexNo > 0) + { + GByte *pKey = poINDFile->BuildKey(nIndexNo, pszStr); + if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0) + return -1; + } + + return 0; +} + +/********************************************************************** + * TABDATFile::WriteIntegerField() + * + * Write the integer field value at the current position in the data + * block. + * + * CPLError() will have been called if something fails. + **********************************************************************/ +int TABDATFile::WriteIntegerField(GInt32 nValue, + TABINDFile *poINDFile, int nIndexNo) +{ + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't write field value: GetRecordBlock() has not been called."); + return -1; + } + + // Update Index + if (poINDFile && nIndexNo > 0) + { + GByte *pKey = poINDFile->BuildKey(nIndexNo, nValue); + if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0) + return -1; + } + + return m_poRecordBlock->WriteInt32(nValue); +} + +/********************************************************************** + * TABDATFile::WriteSmallIntField() + * + * Write the smallint field value at the current position in the data + * block. + * + * CPLError() will have been called if something fails. + **********************************************************************/ +int TABDATFile::WriteSmallIntField(GInt16 nValue, + TABINDFile *poINDFile, int nIndexNo) +{ + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't write field value: GetRecordBlock() has not been called."); + return -1; + } + + // Update Index + if (poINDFile && nIndexNo > 0) + { + GByte *pKey = poINDFile->BuildKey(nIndexNo, nValue); + if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0) + return -1; + } + + return m_poRecordBlock->WriteInt16(nValue); +} + +/********************************************************************** + * TABDATFile::WriteFloatField() + * + * Write the float field value at the current position in the data + * block. + * + * CPLError() will have been called if something fails. + **********************************************************************/ +int TABDATFile::WriteFloatField(double dValue, + TABINDFile *poINDFile, int nIndexNo) +{ + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't write field value: GetRecordBlock() has not been called."); + return -1; + } + + // Update Index + if (poINDFile && nIndexNo > 0) + { + GByte *pKey = poINDFile->BuildKey(nIndexNo, dValue); + if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0) + return -1; + } + + return m_poRecordBlock->WriteDouble(dValue); +} + +/********************************************************************** + * TABDATFile::WriteLogicalField() + * + * Write the logical field value at the current position in the data + * block. + * + * The value written to the file is either 0 or 1, but this function + * takes as input a string with "F" (false) or "T" (true) + * + * CPLError() will have been called if something fails. + **********************************************************************/ +int TABDATFile::WriteLogicalField(const char *pszValue, + TABINDFile *poINDFile, int nIndexNo) +{ + GByte bValue; + + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't write field value: GetRecordBlock() has not been called."); + return -1; + } + + if (EQUALN(pszValue, "T", 1)) + bValue = 1; + else + bValue = 0; + + // Update Index + if (poINDFile && nIndexNo > 0) + { + GByte *pKey = poINDFile->BuildKey(nIndexNo, (int)bValue); + if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0) + return -1; + } + + return m_poRecordBlock->WriteByte(bValue); +} + +/********************************************************************** + * TABDATFile::WriteDateField() + * + * Write the date field value at the current position in the data + * block. + * + * A date field is a 4 bytes binary value in which the first byte is + * the day, followed by 1 byte for the month, and 2 bytes for the year. + * + * The expected input is a 10 chars string in the format "YYYY/MM/DD" + * or "DD/MM/YYYY" or "YYYYMMDD" + * + * Returns 0 on success, or -1 if the operation failed, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABDATFile::WriteDateField(const char *pszValue, + TABINDFile *poINDFile, int nIndexNo) +{ + int nDay, nMonth, nYear; + char **papszTok = NULL; + + /*----------------------------------------------------------------- + * Get rid of leading spaces. + *----------------------------------------------------------------*/ + while ( *pszValue == ' ' ) { pszValue++; } + + /*----------------------------------------------------------------- + * Try to automagically detect date format, one of: + * "YYYY/MM/DD", "DD/MM/YYYY", or "YYYYMMDD" + *----------------------------------------------------------------*/ + + if (strlen(pszValue) == 8) + { + /*------------------------------------------------------------- + * "YYYYMMDD" + *------------------------------------------------------------*/ + char szBuf[9]; + strcpy(szBuf, pszValue); + nDay = atoi(szBuf+6); + szBuf[6] = '\0'; + nMonth = atoi(szBuf+4); + szBuf[4] = '\0'; + nYear = atoi(szBuf); + } + else if (strlen(pszValue) == 10 && + (papszTok = CSLTokenizeStringComplex(pszValue, "/", + FALSE, FALSE)) != NULL && + CSLCount(papszTok) == 3 && + (strlen(papszTok[0]) == 4 || strlen(papszTok[2]) == 4) ) + { + /*------------------------------------------------------------- + * Either "YYYY/MM/DD" or "DD/MM/YYYY" + *------------------------------------------------------------*/ + if (strlen(papszTok[0]) == 4) + { + nYear = atoi(papszTok[0]); + nMonth = atoi(papszTok[1]); + nDay = atoi(papszTok[2]); + } + else + { + nYear = atoi(papszTok[2]); + nMonth = atoi(papszTok[1]); + nDay = atoi(papszTok[0]); + } + } + else if (strlen(pszValue) == 0) + { + nYear = nMonth = nDay = 0; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid date field value `%s'. Date field values must " + "be in the format `YYYY/MM/DD', `MM/DD/YYYY' or `YYYYMMDD'", + pszValue); + CSLDestroy(papszTok); + return -1; + } + CSLDestroy(papszTok); + + return WriteDateField(nYear, nMonth, nDay, poINDFile, nIndexNo); +} + +int TABDATFile::WriteDateField(int nYear, int nMonth, int nDay, + TABINDFile *poINDFile, int nIndexNo) +{ + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't write field value: GetRecordBlock() has not been called."); + return -1; + } + + m_poRecordBlock->WriteInt16((GInt16)nYear); + m_poRecordBlock->WriteByte((GByte)nMonth); + m_poRecordBlock->WriteByte((GByte)nDay); + + if (CPLGetLastErrorNo() != 0) + return -1; + + // Update Index + if (poINDFile && nIndexNo > 0) + { + GByte *pKey = poINDFile->BuildKey(nIndexNo, (nYear*0x10000 + + nMonth * 0x100 + nDay)); + if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0) + return -1; + } + + return 0; +} + +/********************************************************************** + * TABDATFile::WriteTimeField() + * + * Write the date field value at the current position in the data + * block. + * + * A time field is a 4 byte binary value which represents the number + * of milliseconds since midnight. + * + * The expected input is a 10 chars string in the format "HH:MM:SS" + * or "HHMMSSmmm" + * + * Returns 0 on success, or -1 if the operation failed, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABDATFile::WriteTimeField(const char *pszValue, + TABINDFile *poINDFile, int nIndexNo) +{ + int nHour, nMin, nSec, nMS; + char **papszTok = NULL; + + /*----------------------------------------------------------------- + * Get rid of leading spaces. + *----------------------------------------------------------------*/ + while ( *pszValue == ' ' ) { pszValue++; } + + /*----------------------------------------------------------------- + * Try to automagically detect time format, one of: + * "HH:MM:SS", or "HHMMSSmmm" + *----------------------------------------------------------------*/ + + if (strlen(pszValue) == 8) + { + /*------------------------------------------------------------- + * "HH:MM:SS" + *------------------------------------------------------------*/ + char szBuf[9]; + strcpy(szBuf, pszValue); + szBuf[2]=0; + szBuf[5]=0; + nHour = atoi(szBuf); + nMin = atoi(szBuf+3); + nSec = atoi(szBuf+6); + nMS = 0; + } + else if (strlen(pszValue) == 9) + { + /*------------------------------------------------------------- + * "HHMMSSmmm" + *------------------------------------------------------------*/ + char szBuf[4]; + strncpy(szBuf,pszValue,2); + szBuf[2]=0; + nHour = atoi(szBuf); + + strncpy(szBuf,pszValue+2,2); + szBuf[2]=0; + nMin = atoi(szBuf); + + strncpy(szBuf,pszValue+4,2); + szBuf[2]=0; + nSec = atoi(szBuf); + + strncpy(szBuf,pszValue+6,3); + szBuf[3]=0; + nMS = atoi(szBuf); + } + else if (strlen(pszValue) == 0) + { + nHour = nMin = nSec = nMS = -1; // Write -1 to .DAT file if value is not set + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid time field value `%s'. Time field values must " + "be in the format `HH:MM:SS', or `HHMMSSmmm'", + pszValue); + CSLDestroy(papszTok); + return -1; + } + CSLDestroy(papszTok); + + return WriteTimeField(nHour, nMin, nSec, nMS, poINDFile, nIndexNo); +} + +int TABDATFile::WriteTimeField(int nHour, int nMinute, int nSecond, int nMS, + TABINDFile *poINDFile, int nIndexNo) +{ + GInt32 nS = -1; + + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't write field value: GetRecordBlock() has not been called."); + return -1; + } + + nS = (nHour*3600+nMinute*60+nSecond)*1000+nMS; + if (nS < 0) + nS = -1; + m_poRecordBlock->WriteInt32(nS); + + if (CPLGetLastErrorNo() != 0) + return -1; + + // Update Index + if (poINDFile && nIndexNo > 0) + { + GByte *pKey = poINDFile->BuildKey(nIndexNo, (nS)); + if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0) + return -1; + } + + return 0; +} + +/********************************************************************** + * TABDATFile::WriteDateTimeField() + * + * Write the DateTime field value at the current position in the data + * block. + * + * A datetime field is a 8 bytes binary value in which the first byte is + * the day, followe +d by 1 byte for the month, and 2 bytes for the year. + * After this the time value is stored as a 4 byte integer + * (milliseconds since midnight) + * + * The expected input is a 10 chars string in the format "YYYY/MM/DD HH:MM:SS" + * or "DD/MM/YYYY HH:MM:SS" or "YYYYMMDDhhmmssmmm" + * + * Returns 0 on success, or -1 if the operation failed, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABDATFile::WriteDateTimeField(const char *pszValue, + TABINDFile *poINDFile, int nIndexNo) +{ + int nDay, nMonth, nYear, nHour, nMin, nSec, nMS; + char **papszTok = NULL; + + /*----------------------------------------------------------------- + * Get rid of leading spaces. + *----------------------------------------------------------------*/ + while ( *pszValue == ' ' ) { pszValue++; } + + /*----------------------------------------------------------------- + * Try to automagically detect date format, one of: + * "YYYY/MM/DD HH:MM:SS", "DD/MM/YYYY HH:MM:SS", or "YYYYMMDDhhmmssmmm" + *----------------------------------------------------------------*/ + + if (strlen(pszValue) == 17) + { + /*------------------------------------------------------------- + * "YYYYMMDDhhmmssmmm" + *------------------------------------------------------------*/ + char szBuf[18]; + strcpy(szBuf, pszValue); + nMS = atoi(szBuf+14); + szBuf[14]=0; + nSec = atoi(szBuf+12); + szBuf[12]=0; + nMin = atoi(szBuf+10); + szBuf[10]=0; + nHour = atoi(szBuf+8); + szBuf[8]=0; + nDay = atoi(szBuf+6); + szBuf[6] = 0; + nMonth = atoi(szBuf+4); + szBuf[4] = 0; + nYear = atoi(szBuf); + } + else if (strlen(pszValue) == 19 && + (papszTok = CSLTokenizeStringComplex(pszValue, "/ :", + FALSE, FALSE)) != NULL && + CSLCount(papszTok) == 6 && + (strlen(papszTok[0]) == 4 || strlen(papszTok[2]) == 4) ) + { + /*------------------------------------------------------------- + * Either "YYYY/MM/DD HH:MM:SS" or "DD/MM/YYYY HH:MM:SS" + *------------------------------------------------------------*/ + if (strlen(papszTok[0]) == 4) + { + nYear = atoi(papszTok[0]); + nMonth= atoi(papszTok[1]); + nDay = atoi(papszTok[2]); + nHour = atoi(papszTok[3]); + nMin = atoi(papszTok[4]); + nSec = atoi(papszTok[5]); + nMS = 0; + } + else + { + nYear = atoi(papszTok[2]); + nMonth= atoi(papszTok[1]); + nDay = atoi(papszTok[0]); + nHour = atoi(papszTok[3]); + nMin = atoi(papszTok[4]); + nSec = atoi(papszTok[5]); + nMS = 0; + } + } + else if (strlen(pszValue) == 0) + { + nYear = nMonth = nDay = 0; + nHour = nMin = nSec = nMS = 0; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid date field value `%s'. Date field values must " + "be in the format `YYYY/MM/DD HH:MM:SS', " + "`MM/DD/YYYY HH:MM:SS' or `YYYYMMDDhhmmssmmm'", + pszValue); + CSLDestroy(papszTok); + return -1; + } + CSLDestroy(papszTok); + + return WriteDateTimeField(nYear, nMonth, nDay, nHour, nMin, nSec, nMS, + poINDFile, nIndexNo); +} + +int TABDATFile::WriteDateTimeField(int nYear, int nMonth, int nDay, + int nHour, int nMinute, int nSecond, int nMS, + TABINDFile *poINDFile, int nIndexNo) +{ + GInt32 nS = (nHour*3600+nMinute*60+nSecond)*1000+nMS; + + if (m_poRecordBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Can't write field value: GetRecordBlock() has not been called."); + return -1; + } + + m_poRecordBlock->WriteInt16((GInt16)nYear); + m_poRecordBlock->WriteByte((GByte)nMonth); + m_poRecordBlock->WriteByte((GByte)nDay); + m_poRecordBlock->WriteInt32(nS); + + if (CPLGetLastErrorNo() != 0) + return -1; + + // Update Index + if (poINDFile && nIndexNo > 0) + { + // __TODO__ (see bug #1844) + // Indexing on DateTime Fields not currently supported, that will + // require passing the 8 bytes datetime value to BuildKey() here... + CPLAssert(FALSE); + GByte *pKey = poINDFile->BuildKey(nIndexNo, (nYear*0x10000 + + nMonth * 0x100 + nDay)); + if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0) + return -1; + } + + return 0; +} + +/********************************************************************** + * TABDATFile::WriteDecimalField() + * + * Write the decimal field value at the current position in the data + * block. + * + * A decimal field is a floating point value with a fixed number of digits + * stored as a character string. + * + * nWidth is the field length, as defined in the .DAT header. + * + * CPLError() will have been called if something fails. + **********************************************************************/ +int TABDATFile::WriteDecimalField(double dValue, int nWidth, int nPrec, + TABINDFile *poINDFile, int nIndexNo) +{ + char szFormat[10]; + const char *pszVal; + + sprintf(szFormat, "%%%d.%df", nWidth, nPrec); + pszVal = CPLSPrintf(szFormat, dValue); + if ((int)strlen(pszVal) > nWidth) + pszVal += strlen(pszVal) - nWidth; + + // Update Index + if (poINDFile && nIndexNo > 0) + { + GByte *pKey = poINDFile->BuildKey(nIndexNo, dValue); + if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0) + return -1; + } + + return m_poRecordBlock->WriteBytes(nWidth, (GByte*)pszVal); +} + + + +/********************************************************************** + * TABDATFile::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABDATFile::Dump(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABDATFile::Dump() -----\n"); + + if (m_fp == NULL) + { + fprintf(fpOut, "File is not opened.\n"); + } + else + { + fprintf(fpOut, "File is opened: %s\n", m_pszFname); + fprintf(fpOut, "m_numFields = %d\n", m_numFields); + fprintf(fpOut, "m_numRecords = %d\n", m_numRecords); + } + + fflush(fpOut); +} + +#endif // DEBUG + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_feature.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_feature.cpp new file mode 100644 index 000000000..7877ce40d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_feature.cpp @@ -0,0 +1,9087 @@ +/********************************************************************** + * $Id: mitab_feature.cpp,v 1.100 2010-10-12 19:55:32 aboudreault Exp $ + * + * Name: mitab_feature.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the feature classes specific to MapInfo files. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2002, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_feature.cpp,v $ + * Revision 1.100 2010-10-12 19:55:32 aboudreault + * Fixed style string ID parameter to use the proper delimiter as per OGR Feature Style Specification + * + * Revision 1.99 2010-10-08 19:36:44 aboudreault + * Fixed memory leak (GDAL bug #3045) + * + * Revision 1.98 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.97 2010-07-06 13:56:26 aboudreault + * Fixed TABText::GetLabelStyleString() doesn't escape the double quote character (bug 2236) + * + * Revision 1.96 2009-07-30 13:13:43 dmorissette + * Fixed incorrect text justification returned by GetLabelStyleString() + * for TABTJRight (bug 2085) + * + * Revision 1.95 2008-11-27 20:50:22 aboudreault + * Improved support for OGR date/time types. New Read/Write methods (bug 1948) + * Added support of OGR date/time types for MIF features. + * + * Revision 1.94 2008/11/18 16:47:44 dmorissette + * Fixed compile warning when MITAB_USE_OFTDATETIME is set + * + * Revision 1.93 2008/11/17 22:06:21 aboudreault + * Added support to use OFTDateTime/OFTDate/OFTTime type when compiled with + * OGR and fixed reading/writing support for these types. + * + * Revision 1.92 2008/07/29 13:06:17 aboudreault + * Added Font Point styles support (halo, border) (bug 1925) + * + * Revision 1.91 2008/07/21 19:23:03 aboudreault + * Fixed another small error with expanded text. + * + * Revision 1.90 2008/07/21 18:17:19 dmorissette + * Fixed a few compile warnings + * + * Revision 1.89 2008/07/21 17:59:28 aboudreault + * Fixed error in GetLabelStyleString() function: when text style expanded is + * set, no space is needed after the last char. + * + * Revision 1.88 2008/07/21 14:09:41 aboudreault + * Add font text styles support (bold, italic, etc.) (bug 1922) + * + * Revision 1.87 2008/07/17 14:09:30 aboudreault + * Add text outline color support (halo background in MapInfo) + * + * Revision 1.86 2008/07/14 17:51:21 aboudreault + * Fixed the text font size problem (bug 1918) + * + * Revision 1.85 2008/07/14 16:09:10 aboudreault + * Fixed multi-line text height problem (bug 1919) + * + * Revision 1.84 2008/07/14 14:39:52 aboudreault + * Fixed multi-line text rendering by adding support of the end of line char + * "\n" as well as the "\\n". + * + * Revision 1.83 2008/07/01 14:33:17 aboudreault + * * Fixed deprecated warnings generated by mitab.h by moving SetFontName to + * mitab_feature.cpp. + * + * Revision 1.82 2008/02/22 20:20:44 dmorissette + * Fixed the internal compressed coordinate range detection test to prevent + * possible overflow of the compressed integer coordinate values leading + * to some coord. errors in very rare cases while writing to TAB (bug 1854) + * + * Revision 1.81 2008/02/20 21:35:30 dmorissette + * Added support for V800 COLLECTION of large objects (bug 1496) + * + * Revision 1.80 2008/02/05 22:22:48 dmorissette + * Added support for TAB_GEOM_V800_MULTIPOINT (bug 1496) + * + * Revision 1.79 2008/02/01 19:36:31 dmorissette + * Initial support for V800 REGION and MULTIPLINE (bug 1496) + * + * Revision 1.78 2008/01/29 20:46:32 dmorissette + * Added support for v9 Time and DateTime fields (byg 1754) + * + * Revision 1.77 2007/12/11 04:21:54 dmorissette + * Fixed leaks in ITABFeature???::Set???FromStyleString() (GDAL ticket 1696) + * + * Revision 1.76 2007/09/18 17:43:56 dmorissette + * Fixed another index splitting issue: compr coordinates origin was not + * stored in the TABFeature in ReadGeometry... (bug 1732) + * + * Revision 1.75 2007/09/14 19:29:24 dmorissette + * Completed handling of bCoordBlockDataOnly arg to Read/WriteGeometry() + * functions to avoid screwing up pen/brush ref counters (bug 1732) + * + * Revision 1.74 2007/09/14 18:30:19 dmorissette + * Fixed the splitting of object blocks with the optimized spatial + * index mode that was producing files with misaligned bytes that + * confused MapInfo (bug 1732) + * + * Revision 1.73 2007/09/12 20:22:31 dmorissette + * Added TABFeature::CreateFromMapInfoType() + * + * Revision 1.72 2007/06/12 14:17:16 dmorissette + * Added TABFile::TwoPointLineAsPolyline() to allow writing two point lines + * as polylines (bug 1735) + * + * Revision 1.71 2007/06/11 17:57:06 dmorissette + * Removed stray calls to poMapFile->GetCurObjBlock() + * + * Revision 1.70 2007/06/11 14:52:30 dmorissette + * Return a valid m_nCoordDatasize value for Collection objects to prevent + * trashing of collection data during object splitting (bug 1728) + * + * Revision 1.69 2007/02/28 20:41:40 dmorissette + * Added missing NULL pointer checks in SetPenFromStyleString(), + * SetBrushFromStyleString() and SetSymbolFromStyleString() (bug 1670) + * + * Revision 1.68 2007/02/22 18:35:53 dmorissette + * Fixed problem writing collections where MITAB was sometimes trying to + * read past EOF in write mode (bug 1657). + * + * Revision 1.67 2006/11/28 18:49:07 dmorissette + * Completed changes to split TABMAPObjectBlocks properly and produce an + * optimal spatial index (bug 1585) + * + * Revision 1.66 2006/10/17 14:34:31 dmorissette + * Fixed problem with null brush bg color (bug 1603) + * + * Revision 1.65 2006/07/25 13:22:58 dmorissette + * Fixed initialization of MBR of TABCollection members (bug 1520) + * + * Revision 1.64 2006/06/29 19:49:35 dmorissette + * Fixed problem writing PLINE MULTIPLE to TAB format introduced in + * MITAB 1.5.0 (bug 1466). + * + * Revision 1.63 2006/02/08 05:02:57 dmorissette + * Fixed crash when attempting to write TABPolyline object with an invalid + * geometry (GDAL bug 1059) + * + * ... + * + * Revision 1.1 1999/07/12 04:18:24 daniel + * Initial checkin + * + **********************************************************************/ + +#include "mitab.h" +#include "mitab_utils.h" +#include "mitab_geometry.h" + +/*===================================================================== + * class TABFeature + *====================================================================*/ + + +/********************************************************************** + * TABFeature::TABFeature() + * + * Constructor. + **********************************************************************/ +TABFeature::TABFeature(OGRFeatureDefn *poDefnIn): + OGRFeature(poDefnIn) +{ + m_nMapInfoType = TAB_GEOM_NONE; + m_bDeletedFlag = FALSE; + + SetMBR(0.0, 0.0, 0.0, 0.0); +} + +/********************************************************************** + * TABFeature::~TABFeature() + * + * Destructor. + **********************************************************************/ +TABFeature::~TABFeature() +{ +} + + +/********************************************************************** + * TABFeature::CreateFromMapInfoType() + * + * Factory that creates a TABFeature of the right class for the specified + * MapInfo Type + * + **********************************************************************/ +TABFeature *TABFeature::CreateFromMapInfoType(int nMapInfoType, + OGRFeatureDefn *poDefn) +{ + TABFeature *poFeature = NULL; + + /*----------------------------------------------------------------- + * Create new feature object of the right type + *----------------------------------------------------------------*/ + switch(nMapInfoType) + { + case TAB_GEOM_NONE: + poFeature = new TABFeature(poDefn); + break; + case TAB_GEOM_SYMBOL_C: + case TAB_GEOM_SYMBOL: + poFeature = new TABPoint(poDefn); + break; + case TAB_GEOM_FONTSYMBOL_C: + case TAB_GEOM_FONTSYMBOL: + poFeature = new TABFontPoint(poDefn); + break; + case TAB_GEOM_CUSTOMSYMBOL_C: + case TAB_GEOM_CUSTOMSYMBOL: + poFeature = new TABCustomPoint(poDefn); + break; + case TAB_GEOM_LINE_C: + case TAB_GEOM_LINE: + case TAB_GEOM_PLINE_C: + case TAB_GEOM_PLINE: + case TAB_GEOM_MULTIPLINE_C: + case TAB_GEOM_MULTIPLINE: + case TAB_GEOM_V450_MULTIPLINE_C: + case TAB_GEOM_V450_MULTIPLINE: + case TAB_GEOM_V800_MULTIPLINE_C: + case TAB_GEOM_V800_MULTIPLINE: + poFeature = new TABPolyline(poDefn); + break; + case TAB_GEOM_ARC_C: + case TAB_GEOM_ARC: + poFeature = new TABArc(poDefn); + break; + + case TAB_GEOM_REGION_C: + case TAB_GEOM_REGION: + case TAB_GEOM_V450_REGION_C: + case TAB_GEOM_V450_REGION: + case TAB_GEOM_V800_REGION_C: + case TAB_GEOM_V800_REGION: + poFeature = new TABRegion(poDefn); + break; + case TAB_GEOM_RECT_C: + case TAB_GEOM_RECT: + case TAB_GEOM_ROUNDRECT_C: + case TAB_GEOM_ROUNDRECT: + poFeature = new TABRectangle(poDefn); + break; + case TAB_GEOM_ELLIPSE_C: + case TAB_GEOM_ELLIPSE: + poFeature = new TABEllipse(poDefn); + break; + case TAB_GEOM_TEXT_C: + case TAB_GEOM_TEXT: + poFeature = new TABText(poDefn); + break; + case TAB_GEOM_MULTIPOINT_C: + case TAB_GEOM_MULTIPOINT: + case TAB_GEOM_V800_MULTIPOINT_C: + case TAB_GEOM_V800_MULTIPOINT: + poFeature = new TABMultiPoint(poDefn); + break; + case TAB_GEOM_COLLECTION_C: + case TAB_GEOM_COLLECTION: + case TAB_GEOM_V800_COLLECTION_C: + case TAB_GEOM_V800_COLLECTION: + poFeature = new TABCollection(poDefn); + break; + default: + /*------------------------------------------------------------- + * Unsupported feature type... we still return a valid feature + * with NONE geometry after producing a Warning. + * Callers can trap that case by checking CPLGetLastErrorNo() + * against TAB_WarningFeatureTypeNotSupported + *------------------------------------------------------------*/ +// poFeature = new TABDebugFeature(poDefn); + poFeature = new TABFeature(poDefn); + + CPLError(CE_Warning, TAB_WarningFeatureTypeNotSupported, + "Unsupported object type %d (0x%2.2x). Feature will be " + "returned with NONE geometry.", + nMapInfoType, nMapInfoType); + } + + return poFeature; +} + + +/********************************************************************** + * TABFeature::CopyTABFeatureBase() + * + * Used by CloneTABFeature() to copy the basic (fields, geometry, etc.) + * TABFeature members. + * + * The newly created feature is owned by the caller, and will have it's own + * reference to the OGRFeatureDefn. + * + * It is possible to create the clone with a different OGRFeatureDefn, + * in this case, the fields won't be copied of course. + * + **********************************************************************/ +void TABFeature::CopyTABFeatureBase(TABFeature *poDestFeature) +{ + /*----------------------------------------------------------------- + * Copy fields only if OGRFeatureDefn is the same + *----------------------------------------------------------------*/ + OGRFeatureDefn *poThisDefnRef = GetDefnRef(); + + if (poThisDefnRef == poDestFeature->GetDefnRef()) + { + for( int i = 0; i < poThisDefnRef->GetFieldCount(); i++ ) + { + poDestFeature->SetField( i, GetRawFieldRef( i ) ); + } + } + + /*----------------------------------------------------------------- + * Copy the geometry + *----------------------------------------------------------------*/ + poDestFeature->SetGeometry( GetGeometryRef() ); + + double dXMin, dYMin, dXMax, dYMax; + GetMBR(dXMin, dYMin, dXMax, dYMax); + poDestFeature->SetMBR(dXMin, dYMin, dXMax, dYMax); + + GInt32 nXMin, nYMin, nXMax, nYMax; + GetIntMBR(nXMin, nYMin, nXMax, nYMax); + poDestFeature->SetIntMBR(nXMin, nYMin, nXMax, nYMax); + + // m_nMapInfoType is not carried but it is not required anyways. + // it will default to TAB_GEOM_NONE +} + + +/********************************************************************** + * TABFeature::CloneTABFeature() + * + * Duplicate feature, including stuff specific to each TABFeature type. + * + * The newly created feature is owned by the caller, and will have it's own + * reference to the OGRFeatureDefn. + * + * It is possible to create the clone with a different OGRFeatureDefn, + * in this case, the fields won't be copied of course. + * + * This method calls the generic TABFeature::CopyTABFeatureBase() and + * then copies any members specific to its own type. + **********************************************************************/ +TABFeature *TABFeature::CloneTABFeature(OGRFeatureDefn *poNewDefn/*=NULL*/) +{ + /*----------------------------------------------------------------- + * Alloc new feature and copy the base stuff + *----------------------------------------------------------------*/ + TABFeature *poNew = new TABFeature(poNewDefn ? poNewDefn : GetDefnRef()); + + CopyTABFeatureBase(poNew); + + /*----------------------------------------------------------------- + * And members specific to this class + *----------------------------------------------------------------*/ + // Nothing to do for this class + + return poNew; +} + +/********************************************************************** + * TABFeature::SetMBR() + * + * Set the values for the MBR corners for this feature. + **********************************************************************/ +void TABFeature::SetMBR(double dXMin, double dYMin, + double dXMax, double dYMax) +{ + m_dXMin = MIN(dXMin, dXMax); + m_dYMin = MIN(dYMin, dYMax); + m_dXMax = MAX(dXMin, dXMax); + m_dYMax = MAX(dYMin, dYMax); +} + +/********************************************************************** + * TABFeature::GetMBR() + * + * Return the values for the MBR corners for this feature. + **********************************************************************/ +void TABFeature::GetMBR(double &dXMin, double &dYMin, + double &dXMax, double &dYMax) +{ + dXMin = m_dXMin; + dYMin = m_dYMin; + dXMax = m_dXMax; + dYMax = m_dYMax; +} + +/********************************************************************** + * TABFeature::SetIntMBR() + * + * Set the integer coordinates values of the MBR of this feature. + **********************************************************************/ +void TABFeature::SetIntMBR(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax) +{ + m_nXMin = nXMin; + m_nYMin = nYMin; + m_nXMax = nXMax; + m_nYMax = nYMax; +} + +/********************************************************************** + * TABFeature::GetIntMBR() + * + * Return the integer coordinates values of the MBR of this feature. + **********************************************************************/ +void TABFeature::GetIntMBR(GInt32 &nXMin, GInt32 &nYMin, + GInt32 &nXMax, GInt32 &nYMax) +{ + nXMin = m_nXMin; + nYMin = m_nYMin; + nXMax = m_nXMax; + nYMax = m_nYMax; +} + +/********************************************************************** + * TABFeature::ReadRecordFromDATFile() + * + * Fill the fields part of the feature from the contents of the + * table record pointed to by poDATFile. + * + * It is assumed that poDATFile currently points to the beginning of + * the table record and that this feature's OGRFeatureDefn has been + * properly initialized for this table. + **********************************************************************/ +int TABFeature::ReadRecordFromDATFile(TABDATFile *poDATFile) +{ + int iField, numFields, nValue; + double dValue; + const char *pszValue; +#ifdef MITAB_USE_OFTDATETIME + int nYear, nMonth, nDay, nHour, nMin, nMS, status; + nYear = nMonth = nDay = nHour = nMin = nMS = 0; +#endif + + CPLAssert(poDATFile); + + numFields = poDATFile->GetNumFields(); + + for(iField=0; iFieldGetFieldType(iField)) + { + case TABFChar: + pszValue = poDATFile->ReadCharField(poDATFile-> + GetFieldWidth(iField)); + SetField(iField, pszValue); + break; + case TABFDecimal: + dValue = poDATFile->ReadDecimalField(poDATFile-> + GetFieldWidth(iField)); + SetField(iField, dValue); + break; + case TABFInteger: + nValue = poDATFile->ReadIntegerField(poDATFile-> + GetFieldWidth(iField)); + SetField(iField, nValue); + break; + case TABFSmallInt: + nValue = poDATFile->ReadSmallIntField(poDATFile-> + GetFieldWidth(iField)); + SetField(iField, nValue); + break; + case TABFFloat: + dValue = poDATFile->ReadFloatField(poDATFile-> + GetFieldWidth(iField)); + SetField(iField, dValue); + break; + case TABFLogical: + pszValue = poDATFile->ReadLogicalField(poDATFile-> + GetFieldWidth(iField)); + SetField(iField, pszValue); + break; + case TABFDate: +#ifdef MITAB_USE_OFTDATETIME + if ((status = poDATFile->ReadDateField(poDATFile->GetFieldWidth(iField), + &nYear, &nMonth, &nDay)) == 0) + { + SetField(iField, nYear, nMonth, nDay, 0, 0, 0, 0); + } +#else + pszValue = poDATFile->ReadDateField(poDATFile-> + GetFieldWidth(iField)); + SetField(iField, pszValue); +#endif + break; + case TABFTime: + { +#ifdef MITAB_USE_OFTDATETIME + int nSec; + if ((status = poDATFile->ReadTimeField(poDATFile->GetFieldWidth(iField), + &nHour, &nMin, &nSec, &nMS)) == 0) + { + SetField(iField, nYear, nMonth, nDay, nHour, nMin, nSec + nMS / 1000.0f, 0); + } +#else + pszValue = poDATFile->ReadTimeField(poDATFile-> + GetFieldWidth(iField)); + SetField(iField, pszValue); +#endif + break; + } + case TABFDateTime: +#ifdef MITAB_USE_OFTDATETIME + int nSec; + if ((status = poDATFile->ReadDateTimeField(poDATFile->GetFieldWidth(iField), + &nYear, &nMonth, &nDay, + &nHour, &nMin, &nSec, &nMS)) == 0) + { + SetField(iField, nYear, nMonth, nDay, nHour, nMin, nSec + nMS / 1000.0f, 0); + } +#else + pszValue = poDATFile->ReadDateTimeField(poDATFile-> + GetFieldWidth(iField)); + SetField(iField, pszValue); +#endif + break; + default: + // Other type??? Impossible! + CPLError(CE_Failure, CPLE_AssertionFailed, + "Unsupported field type!"); + } + + } + + return 0; +} + +/********************************************************************** + * TABFeature::WriteRecordToDATFile() + * + * Write the attribute part of the feature to the .DAT file. + * + * It is assumed that poDATFile currently points to the beginning of + * the table record and that this feature's OGRFeatureDefn has been + * properly initialized for this table. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFeature::WriteRecordToDATFile(TABDATFile *poDATFile, + TABINDFile *poINDFile, int *panIndexNo) +{ + int iField, numFields, nStatus=0; +#ifdef MITAB_USE_OFTDATETIME + int nYear, nMon, nDay, nHour, nMin, nTZFlag; + nYear = nMon = nDay = nHour = nMin = nTZFlag = 0; + float fSec = 0; +#endif + + CPLAssert(poDATFile); + CPLAssert(panIndexNo || GetDefnRef()->GetFieldCount() == 0); + + numFields = poDATFile->GetNumFields(); + + poDATFile->MarkRecordAsExisting(); + + for(iField=0; nStatus == 0 && iField= GetDefnRef()->GetFieldCount() ) + { + CPLAssert( poDATFile->GetFieldType(iField) == TABFInteger + && iField == 0 ); + nStatus = poDATFile->WriteIntegerField( (int)GetFID(), poINDFile, 0 ); + continue; + } + + switch(poDATFile->GetFieldType(iField)) + { + case TABFChar: + nStatus = poDATFile->WriteCharField(GetFieldAsString(iField), + poDATFile->GetFieldWidth(iField), + poINDFile, panIndexNo[iField]); + break; + case TABFDecimal: + nStatus = poDATFile->WriteDecimalField(GetFieldAsDouble(iField), + poDATFile->GetFieldWidth(iField), + poDATFile->GetFieldPrecision(iField), + poINDFile, panIndexNo[iField]); + break; + case TABFInteger: + nStatus = poDATFile->WriteIntegerField(GetFieldAsInteger(iField), + poINDFile, panIndexNo[iField]); + break; + case TABFSmallInt: + nStatus = poDATFile->WriteSmallIntField((GInt16)GetFieldAsInteger(iField), + poINDFile, panIndexNo[iField]); + break; + case TABFFloat: + nStatus = poDATFile->WriteFloatField(GetFieldAsDouble(iField), + poINDFile, panIndexNo[iField]); + break; + case TABFLogical: + nStatus = poDATFile->WriteLogicalField(GetFieldAsString(iField), + poINDFile, panIndexNo[iField]); + break; + case TABFDate: +#ifdef MITAB_USE_OFTDATETIME + if (IsFieldSet(iField)) + { + GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, + &nHour, &nMin, &fSec, &nTZFlag); + } + else + nYear = nMon = nDay = 0; + + nStatus = poDATFile->WriteDateField(nYear, nMon, nDay, + poINDFile, panIndexNo[iField]); +#else + nStatus = poDATFile->WriteDateField(GetFieldAsString(iField), + poINDFile, panIndexNo[iField]); +#endif + break; + case TABFTime: +#ifdef MITAB_USE_OFTDATETIME + if (IsFieldSet(iField)) + { + GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, + &nHour, &nMin, &fSec, &nTZFlag); + } + else + { + nHour = nMin = 0; + fSec = 0; + } + nStatus = poDATFile->WriteTimeField(nHour, nMin, (int)fSec, + OGR_GET_MS(fSec), + poINDFile, panIndexNo[iField]); + +#else + nStatus = poDATFile->WriteTimeField(GetFieldAsString(iField), + poINDFile, panIndexNo[iField]); +#endif + break; + case TABFDateTime: +#ifdef MITAB_USE_OFTDATETIME + if (IsFieldSet(iField)) + { + GetFieldAsDateTime(iField, &nYear, &nMon, &nDay, + &nHour, &nMin, &fSec, &nTZFlag); + } + else + { + nYear = nMon = nDay = nHour = nMin = 0; + fSec = 0; + } + + nStatus = poDATFile->WriteDateTimeField(nYear, nMon, nDay, + nHour, nMin, (int)fSec, + OGR_GET_MS(fSec), + poINDFile, panIndexNo[iField]); +#else + nStatus = poDATFile->WriteDateTimeField(GetFieldAsString(iField), + poINDFile, panIndexNo[iField]); +#endif + break; + default: + // Other type??? Impossible! + CPLError(CE_Failure, CPLE_AssertionFailed, + "Unsupported field type!"); + } + + } + + if (poDATFile->CommitRecordToFile() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABFeature::ReadGeometryFromMAPFile() + * + * In derived classes, this method should be reimplemented to + * fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that before calling ReadGeometryFromMAPFile(), poMAPFile + * currently points to the beginning of a map object. + * + * bCoordBlockDataOnly=TRUE is used when this method is called to copy only + * the CoordBlock data during splitting of object blocks. In this case we + * need to process only the information related to the CoordBlock. One + * important thing to avoid is reading/writing pen/brush/symbol definitions + * as that would screw up their ref counters. + * + * ppoCoordBlock is used by TABCollection and by index splitting code + * to provide a CoordBlock to use instead of the one from the poMAPFile and + * return the current pointer at the end of the call. + * + * The current implementation does nothing since instances of TABFeature + * objects contain no geometry (i.e. TAB_GEOM_NONE). + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABFeature::ReadGeometryFromMAPFile(TABMAPFile * /*poMapFile*/, + TABMAPObjHdr * /*poObjHdr*/, + GBool /*bCoordBlockDataOnly=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + /*----------------------------------------------------------------- + * Nothing to do... instances of TABFeature objects contain no geometry. + *----------------------------------------------------------------*/ + + return 0; +} + + +/********************************************************************** + * TABFeature::UpdateMBR() + * + * Fetch envelope of poGeom and update MBR. + * Integer coord MBR is updated only if poMapFile is not NULL. + * + * Returns 0 on success, or -1 if there is no geometry in object + **********************************************************************/ +int TABFeature::UpdateMBR(TABMAPFile * poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + + poGeom = GetGeometryRef(); + + if (poGeom) + { + OGREnvelope oEnv; + poGeom->getEnvelope(&oEnv); + + m_dXMin = oEnv.MinX; + m_dYMin = oEnv.MinY; + m_dXMax = oEnv.MaxX; + m_dYMax = oEnv.MaxY; + + if (poMapFile) + { + poMapFile->Coordsys2Int(oEnv.MinX, oEnv.MinY, m_nXMin, m_nYMin); + poMapFile->Coordsys2Int(oEnv.MaxX, oEnv.MaxY, m_nXMax, m_nYMax); + } + + return 0; + } + + return -1; +} + +/********************************************************************** + * TABFeature::ValidateCoordType() + * + * Checks the feature envelope to establish if the feature should be + * written using Compressed coordinates or not and adjust m_nMapInfoType + * accordingly. Calling this method also sets (initializes) m_nXMin, m_nYMin, + * m_nXMax, m_nYMax + * + * This function should be used only by the ValidateMapInfoType() + * implementations. + * + * Returns TRUE if coord. should be compressed, FALSE otherwise + **********************************************************************/ +GBool TABFeature::ValidateCoordType(TABMAPFile * poMapFile) +{ + GBool bCompr = FALSE; + + /*------------------------------------------------------------- + * Decide if coordinates should be compressed or not. + *------------------------------------------------------------*/ + if (UpdateMBR(poMapFile) == 0) + { + /* Test for max range < 65535 here instead of < 65536 to avoid + * compressed coordinate overflows in some boundary situations + */ + if ((m_nXMax - m_nXMin) < 65535 && (m_nYMax-m_nYMin) < 65535) + { + bCompr = TRUE; + } + m_nComprOrgX = (m_nXMin + m_nXMax) / 2; + m_nComprOrgY = (m_nYMin + m_nYMax) / 2; + } + + /*------------------------------------------------------------- + * Adjust native type + *------------------------------------------------------------*/ + if (bCompr && ((m_nMapInfoType%3) == 2)) + m_nMapInfoType = (TABGeomType)(m_nMapInfoType - 1); // compr = 1, 4, 7, ... + else if (!bCompr && ((m_nMapInfoType%3) == 1)) + m_nMapInfoType = (TABGeomType)(m_nMapInfoType + 1); // non-compr = 2, 5, 8, ... + + return bCompr; +} + +/********************************************************************** + * TABFeature::ForceCoordTypeAndOrigin() + * + * This function is used by TABCollection::ValidateMapInfoType() to force + * the coord type and compressed origin of all members of a collection + * to be the same. (A replacement for ValidateCoordType() for this + * specific case) + **********************************************************************/ +void TABFeature::ForceCoordTypeAndOrigin(TABGeomType nMapInfoType, GBool bCompr, + GInt32 nComprOrgX, GInt32 nComprOrgY, + GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax) +{ + /*------------------------------------------------------------- + * Set Compressed Origin and adjust native type + *------------------------------------------------------------*/ + m_nComprOrgX = nComprOrgX; + m_nComprOrgY = nComprOrgY; + + m_nMapInfoType = nMapInfoType; + + if (bCompr && ((m_nMapInfoType%3) == 2)) + m_nMapInfoType = (TABGeomType)(m_nMapInfoType - 1); // compr = 1, 4, 7, ... + else if (!bCompr && ((m_nMapInfoType%3) == 1)) + m_nMapInfoType = (TABGeomType)(m_nMapInfoType + 1); // non-compr = 2, 5, 8, ... + + m_nXMin = nXMin; + m_nYMin = nYMin; + m_nXMax = nXMax; + m_nYMax = nYMax; +} + +/********************************************************************** + * TABFeature::WriteGeometryToMAPFile() + * + * + * In derived classes, this method should be reimplemented to + * write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that before calling WriteGeometryToMAPFile(), poMAPFile + * currently points to a valid map object. + * + * bCoordBlockDataOnly=TRUE is used when this method is called to copy only + * the CoordBlock data during splitting of object blocks. In this case we + * need to process only the information related to the CoordBlock. One + * important thing to avoid is reading/writing pen/brush/symbol definitions + * as that would screw up their ref counters. + * + * ppoCoordBlock is used by TABCollection and by index splitting code + * to provide a CoordBlock to use instead of the one from the poMAPFile and + * return the current pointer at the end of the call. + * + * The current implementation does nothing since instances of TABFeature + * objects contain no geometry (i.e. TAB_GEOM_NONE). + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABFeature::WriteGeometryToMAPFile(TABMAPFile * /* poMapFile*/, + TABMAPObjHdr * /*poObjHdr*/, + GBool /*bCoordBlockDataOnly=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + /*----------------------------------------------------------------- + * Nothing to do... instances of TABFeature objects contain no geometry. + *----------------------------------------------------------------*/ + + return 0; +} + +/********************************************************************** + * TABFeature::DumpMID() + * + * Dump feature attributes in a format similar to .MID data records. + **********************************************************************/ +void TABFeature::DumpMID(FILE *fpOut /*=NULL*/) +{ + OGRFeatureDefn *poDefn = GetDefnRef(); + + if (fpOut == NULL) + fpOut = stdout; + + for( int iField = 0; iField < GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFDefn = poDefn->GetFieldDefn(iField); + + fprintf( fpOut, " %s (%s) = %s\n", + poFDefn->GetNameRef(), + OGRFieldDefn::GetFieldTypeName(poFDefn->GetType()), + GetFieldAsString( iField ) ); + } + + fflush(fpOut); +} + +/********************************************************************** + * TABFeature::DumpMIF() + * + * Dump feature geometry in a format similar to .MIF files. + **********************************************************************/ +void TABFeature::DumpMIF(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + /*----------------------------------------------------------------- + * Generate output... not much to do, feature contains no geometry. + *----------------------------------------------------------------*/ + fprintf(fpOut, "NONE\n" ); + + fflush(fpOut); +} + + +/*===================================================================== + * class TABPoint + *====================================================================*/ + + +/********************************************************************** + * TABPoint::TABPoint() + * + * Constructor. + **********************************************************************/ +TABPoint::TABPoint(OGRFeatureDefn *poDefnIn): + TABFeature(poDefnIn) +{ +} + +/********************************************************************** + * TABPoint::~TABPoint() + * + * Destructor. + **********************************************************************/ +TABPoint::~TABPoint() +{ +} + +/********************************************************************** + * TABPoint::CloneTABFeature() + * + * Duplicate feature, including stuff specific to each TABFeature type. + * + * This method calls the generic TABFeature::CloneTABFeature() and + * then copies any members specific to its own type. + **********************************************************************/ +TABFeature *TABPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/) +{ + /*----------------------------------------------------------------- + * Alloc new feature and copy the base stuff + *----------------------------------------------------------------*/ + TABPoint *poNew = new TABPoint(poNewDefn ? poNewDefn : GetDefnRef()); + + CopyTABFeatureBase(poNew); + + /*----------------------------------------------------------------- + * And members specific to this class + *----------------------------------------------------------------*/ + // ITABFeatureSymbol + *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef(); + + return poNew; +} + + +/********************************************************************** + * TABPoint::ValidateMapInfoType() + * + * Check the feature's geometry part and return the corresponding + * mapinfo object type code. The m_nMapInfoType member will also + * be updated for further calls to GetMapInfoType(); + * + * Returns TAB_GEOM_NONE if the geometry is not compatible with what + * is expected for this object class. + **********************************************************************/ +TABGeomType TABPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + * __TODO__ For now we always write in uncompressed format (until we + * find that this is not correct... note that at this point the + * decision to use compressed/uncompressed will likely be based on + * the distance between the point and the object block center in + * integer coordinates being > 32767 or not... remains to be verified) + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + { + switch(GetFeatureClass()) + { + case TABFCFontPoint: + m_nMapInfoType = TAB_GEOM_FONTSYMBOL; + break; + case TABFCCustomPoint: + m_nMapInfoType = TAB_GEOM_CUSTOMSYMBOL; + break; + case TABFCPoint: + default: + m_nMapInfoType = TAB_GEOM_SYMBOL; + break; + } + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPoint: Missing or Invalid Geometry!"); + m_nMapInfoType = TAB_GEOM_NONE; + } + + UpdateMBR(poMapFile); + + return m_nMapInfoType; +} + +/********************************************************************** + * TABPoint::ReadGeometryFromMAPFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to the beginning of + * a map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABPoint::ReadGeometryFromMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + double dX, dY; + OGRGeometry *poGeometry; + + /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */ + if (bCoordBlockDataOnly) + return 0; + + /*----------------------------------------------------------------- + * Fetch and validate geometry type + *----------------------------------------------------------------*/ + m_nMapInfoType = poObjHdr->m_nType; + + if (m_nMapInfoType != TAB_GEOM_SYMBOL && + m_nMapInfoType != TAB_GEOM_SYMBOL_C ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)", + m_nMapInfoType, m_nMapInfoType); + return -1; + } + + /*----------------------------------------------------------------- + * Read object information + *----------------------------------------------------------------*/ + TABMAPObjPoint *poPointHdr = (TABMAPObjPoint *)poObjHdr; + + m_nSymbolDefIndex = poPointHdr->m_nSymbolId; // Symbol index + + poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef); + + /*----------------------------------------------------------------- + * Create and fill geometry object + *----------------------------------------------------------------*/ + poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY); + poGeometry = new OGRPoint(dX, dY); + + SetGeometryDirectly(poGeometry); + + SetMBR(dX, dY, dX, dY); + SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + + return 0; +} + +/********************************************************************** + * TABPoint::WriteGeometryToMAPFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to a valid map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABPoint::WriteGeometryToMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + GInt32 nX, nY; + OGRGeometry *poGeom; + OGRPoint *poPoint; + + /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */ + if (bCoordBlockDataOnly) + return 0; + + /*----------------------------------------------------------------- + * We assume that ValidateMapInfoType() was called already and that + * the type in poObjHdr->m_nType is valid. + *----------------------------------------------------------------*/ + CPLAssert(m_nMapInfoType == poObjHdr->m_nType); + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + poPoint = (OGRPoint*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPoint: Missing or Invalid Geometry!"); + return -1; + } + + poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY); + + /*----------------------------------------------------------------- + * Copy object information + *----------------------------------------------------------------*/ + TABMAPObjPoint *poPointHdr = (TABMAPObjPoint *)poObjHdr; + + poPointHdr->m_nX = nX; + poPointHdr->m_nY = nY; + poPointHdr->SetMBR(nX, nY, nX, nY); + + m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef); + poPointHdr->m_nSymbolId = (GByte)m_nSymbolDefIndex; // Symbol index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * TABPoint::GetX() + * + * Return this point's X coordinate. + **********************************************************************/ +double TABPoint::GetX() +{ + OGRGeometry *poGeom; + OGRPoint *poPoint=NULL; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + poPoint = (OGRPoint*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPoint: Missing or Invalid Geometry!"); + return 0.0; + } + + return poPoint->getX(); +} + +/********************************************************************** + * TABPoint::GetY() + * + * Return this point's Y coordinate. + **********************************************************************/ +double TABPoint::GetY() +{ + OGRGeometry *poGeom; + OGRPoint *poPoint=NULL; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + poPoint = (OGRPoint*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPoint: Missing or Invalid Geometry!"); + return 0.0; + } + + return poPoint->getY(); +} + + +/********************************************************************** + * TABPoint::GetStyleString() + * + * Return style string for this feature. + * + * Style String is built only once during the first call to GetStyleString(). + **********************************************************************/ +const char *TABPoint::GetStyleString() +{ + if (m_pszStyleString == NULL) + { + m_pszStyleString = CPLStrdup(GetSymbolStyleString()); + } + + return m_pszStyleString; +} + + +/********************************************************************** + * TABPoint::DumpMIF() + * + * Dump feature geometry in a format similar to .MIF POINTs. + **********************************************************************/ +void TABPoint::DumpMIF(FILE *fpOut /*=NULL*/) +{ + OGRGeometry *poGeom; + OGRPoint *poPoint; + + if (fpOut == NULL) + fpOut = stdout; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + poPoint = (OGRPoint*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPoint: Missing or Invalid Geometry!"); + return; + } + + /*----------------------------------------------------------------- + * Generate output + *----------------------------------------------------------------*/ + fprintf(fpOut, "POINT %.15g %.15g\n", poPoint->getX(), poPoint->getY() ); + + DumpSymbolDef(fpOut); + + /*----------------------------------------------------------------- + * Handle stuff specific to derived classes + *----------------------------------------------------------------*/ + if (GetFeatureClass() == TABFCFontPoint) + { + TABFontPoint *poFeature = (TABFontPoint *)this; + fprintf(fpOut, " m_nFontStyle = 0x%2.2x (%d)\n", + poFeature->GetFontStyleTABValue(), + poFeature->GetFontStyleTABValue()); + + poFeature->DumpFontDef(fpOut); + } + if (GetFeatureClass() == TABFCCustomPoint) + { + TABCustomPoint *poFeature = (TABCustomPoint *)this; + + fprintf(fpOut, " m_nUnknown_ = 0x%2.2x (%d)\n", + poFeature->m_nUnknown_, poFeature->m_nUnknown_); + fprintf(fpOut, " m_nCustomStyle = 0x%2.2x (%d)\n", + poFeature->GetCustomSymbolStyle(), + poFeature->GetCustomSymbolStyle()); + + poFeature->DumpFontDef(fpOut); + } + + fflush(fpOut); +} + +/*===================================================================== + * class TABFontPoint + *====================================================================*/ + + +/********************************************************************** + * TABFontPoint::TABFontPoint() + * + * Constructor. + **********************************************************************/ +TABFontPoint::TABFontPoint(OGRFeatureDefn *poDefnIn): + TABPoint(poDefnIn) +{ + m_nFontStyle = 0; + m_dAngle = 0.0; +} + +/********************************************************************** + * TABFontPoint::~TABFontPoint() + * + * Destructor. + **********************************************************************/ +TABFontPoint::~TABFontPoint() +{ +} + +/********************************************************************** + * TABFontPoint::CloneTABFeature() + * + * Duplicate feature, including stuff specific to each TABFeature type. + * + * This method calls the generic TABFeature::CloneTABFeature() and + * then copies any members specific to its own type. + **********************************************************************/ +TABFeature *TABFontPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/) +{ + /*----------------------------------------------------------------- + * Alloc new feature and copy the base stuff + *----------------------------------------------------------------*/ + TABFontPoint *poNew = new TABFontPoint(poNewDefn ? poNewDefn : + GetDefnRef()); + + CopyTABFeatureBase(poNew); + + /*----------------------------------------------------------------- + * And members specific to this class + *----------------------------------------------------------------*/ + // ITABFeatureSymbol + *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef(); + + // ITABFeatureFont + *(poNew->GetFontDefRef()) = *GetFontDefRef(); + + poNew->SetSymbolAngle( GetSymbolAngle() ); + poNew->SetFontStyleTABValue( GetFontStyleTABValue() ); + + return poNew; +} + +/********************************************************************** + * TABFontPoint::ReadGeometryFromMAPFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to the beginning of + * a map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABFontPoint::ReadGeometryFromMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + double dX, dY; + OGRGeometry *poGeometry; + + /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */ + if (bCoordBlockDataOnly) + return 0; + + /*----------------------------------------------------------------- + * Fetch and validate geometry type + *----------------------------------------------------------------*/ + m_nMapInfoType = poObjHdr->m_nType; + + if (m_nMapInfoType != TAB_GEOM_FONTSYMBOL && + m_nMapInfoType != TAB_GEOM_FONTSYMBOL_C ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)", + m_nMapInfoType, m_nMapInfoType); + return -1; + } + + /*----------------------------------------------------------------- + * Read object information + * NOTE: This symbol type does not contain a reference to a + * SymbolDef block in the file, but we still use the m_sSymbolDef + * structure to store the information inside the class so that the + * ITABFeatureSymbol methods work properly for the class user. + *----------------------------------------------------------------*/ + TABMAPObjFontPoint *poPointHdr = (TABMAPObjFontPoint *)poObjHdr; + + m_nSymbolDefIndex = -1; + m_sSymbolDef.nRefCount = 0; + + m_sSymbolDef.nSymbolNo = poPointHdr->m_nSymbolId; // shape + m_sSymbolDef.nPointSize = poPointHdr->m_nPointSize; // point size + + m_nFontStyle = poPointHdr->m_nFontStyle; // font style + + m_sSymbolDef.rgbColor = (poPointHdr->m_nR*256*256 + + poPointHdr->m_nG*256 + + poPointHdr->m_nB); + + /*------------------------------------------------------------- + * Symbol Angle, in thenths of degree. + * Contrary to arc start/end angles, no conversion based on + * origin quadrant is required here + *------------------------------------------------------------*/ + m_dAngle = poPointHdr->m_nAngle/10.0; + + m_nFontDefIndex = poPointHdr->m_nFontId; // Font name index + + poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef); + + /*----------------------------------------------------------------- + * Create and fill geometry object + *----------------------------------------------------------------*/ + poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY); + poGeometry = new OGRPoint(dX, dY); + + SetGeometryDirectly(poGeometry); + + SetMBR(dX, dY, dX, dY); + SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + + return 0; +} + +/********************************************************************** + * TABFontPoint::WriteGeometryToMAPFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to a valid map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABFontPoint::WriteGeometryToMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + GInt32 nX, nY; + OGRGeometry *poGeom; + OGRPoint *poPoint; + + /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */ + if (bCoordBlockDataOnly) + return 0; + + /*----------------------------------------------------------------- + * We assume that ValidateMapInfoType() was called already and that + * the type in poObjHdr->m_nType is valid. + *----------------------------------------------------------------*/ + CPLAssert(m_nMapInfoType == poObjHdr->m_nType); + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + poPoint = (OGRPoint*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABFontPoint: Missing or Invalid Geometry!"); + return -1; + } + + poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY); + + /*----------------------------------------------------------------- + * Copy object information + * NOTE: This symbol type does not contain a reference to a + * SymbolDef block in the file, but we still use the m_sSymbolDef + * structure to store the information inside the class so that the + * ITABFeatureSymbol methods work properly for the class user. + *----------------------------------------------------------------*/ + TABMAPObjFontPoint *poPointHdr = (TABMAPObjFontPoint *)poObjHdr; + + poPointHdr->m_nX = nX; + poPointHdr->m_nY = nY; + poPointHdr->SetMBR(nX, nY, nX, nY); + + poPointHdr->m_nSymbolId = (GByte)m_sSymbolDef.nSymbolNo; // shape + poPointHdr->m_nPointSize = (GByte)m_sSymbolDef.nPointSize; // point size + poPointHdr->m_nFontStyle = m_nFontStyle; // font style + + poPointHdr->m_nR = (GByte)COLOR_R(m_sSymbolDef.rgbColor); + poPointHdr->m_nG = (GByte)COLOR_G(m_sSymbolDef.rgbColor); + poPointHdr->m_nB = (GByte)COLOR_B(m_sSymbolDef.rgbColor); + + /*------------------------------------------------------------- + * Symbol Angle, in thenths of degree. + * Contrary to arc start/end angles, no conversion based on + * origin quadrant is required here + *------------------------------------------------------------*/ + poPointHdr->m_nAngle = (GInt16)ROUND_INT(m_dAngle * 10.0); + + // Write Font Def + m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef); + poPointHdr->m_nFontId = (GByte)m_nFontDefIndex; // Font name index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABFontPoint::QueryFontStyle() + * + * Return TRUE if the specified font style attribute is turned ON, + * or FALSE otherwise. See enum TABFontStyle for the list of styles + * that can be queried on. + **********************************************************************/ +GBool TABFontPoint::QueryFontStyle(TABFontStyle eStyleToQuery) +{ + return (m_nFontStyle & (int)eStyleToQuery) ? TRUE: FALSE; +} + +void TABFontPoint::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn) +{ + if (bStyleOn) + m_nFontStyle |= (int)eStyleToToggle; + else + m_nFontStyle &= ~(int)eStyleToToggle; +} + +/********************************************************************** + * TABFontPoint::GetFontStyleMIFValue() + * + * Return the Font Style value for this object using the style values + * that are used in a MIF FONT() clause. See MIF specs (appendix A). + * + * The reason why we have to differentiate between the TAB and the MIF font + * style values is that in TAB, TABFSBox is included in the style value + * as code 0x100, but in MIF it is not included, instead it is implied by + * the presence of the BG color in the FONT() clause (the BG color is + * present only when TABFSBox or TABFSHalo is set). + * This also has the effect of shifting all the other style values > 0x100 + * by 1 byte. + * + * NOTE: Even if there is no BG color for font symbols, we inherit this + * problem because Font Point styles use the same codes as Text Font styles. + **********************************************************************/ +int TABFontPoint::GetFontStyleMIFValue() +{ + // The conversion is simply to remove bit 0x100 from the value and shift + // down all values past this bit. + return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00-0x0100))/2; +} + +void TABFontPoint:: SetFontStyleMIFValue(int nStyle) +{ + m_nFontStyle = (GByte)((nStyle & 0xff) + (nStyle & 0x7f00)*2); +} + +/********************************************************************** + * TABFontPoint::SetSymbolAngle() + * + * Set the symbol angle value in degrees, making sure the value is + * always in the range [0..360] + **********************************************************************/ +void TABFontPoint::SetSymbolAngle(double dAngle) +{ + while(dAngle < 0.0) dAngle += 360.0; + while(dAngle > 360.0) dAngle -= 360.0; + + m_dAngle = dAngle; +} + + +/********************************************************************** + * TABFontPoint::GetStyleString() + * + * Return style string for this feature. + * + * Style String is built only once during the first call to GetStyleString(). + **********************************************************************/ +const char *TABFontPoint::GetStyleString() +{ + if (m_pszStyleString == NULL) + { + /* Get the SymbolStyleString, and add the outline Color + (halo/border in MapInfo Symbol terminology) */ + char *pszSymbolStyleString = CPLStrdup(GetSymbolStyleString(GetSymbolAngle())); + int nStyleStringlen = strlen(pszSymbolStyleString); + pszSymbolStyleString[nStyleStringlen-1] = '\0'; + + const char *outlineColor; + if (m_nFontStyle & 16) + outlineColor = ",o:#000000"; + else if (m_nFontStyle & 512) + outlineColor = ",o:#ffffff"; + else + outlineColor = ""; + + m_pszStyleString = CPLStrdup(CPLSPrintf("%s%s)", + pszSymbolStyleString, + outlineColor)); + CPLFree(pszSymbolStyleString); + } + + return m_pszStyleString; +} + + +/*===================================================================== + * class TABCustomPoint + *====================================================================*/ + + +/********************************************************************** + * TABCustomPoint::TABCustomPoint() + * + * Constructor. + **********************************************************************/ +TABCustomPoint::TABCustomPoint(OGRFeatureDefn *poDefnIn): + TABPoint(poDefnIn) +{ + m_nUnknown_ = m_nCustomStyle = 0; +} + +/********************************************************************** + * TABCustomPoint::~TABCustomPoint() + * + * Destructor. + **********************************************************************/ +TABCustomPoint::~TABCustomPoint() +{ +} + +/********************************************************************** + * TABCustomPoint::CloneTABFeature() + * + * Duplicate feature, including stuff specific to each TABFeature type. + * + * This method calls the generic TABFeature::CloneTABFeature() and + * then copies any members specific to its own type. + **********************************************************************/ +TABFeature *TABCustomPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn/*=NULL*/) +{ + /*----------------------------------------------------------------- + * Alloc new feature and copy the base stuff + *----------------------------------------------------------------*/ + TABCustomPoint *poNew = new TABCustomPoint(poNewDefn ? poNewDefn : + GetDefnRef()); + + CopyTABFeatureBase(poNew); + + /*----------------------------------------------------------------- + * And members specific to this class + *----------------------------------------------------------------*/ + // ITABFeatureSymbol + *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef(); + + // ITABFeatureFont + *(poNew->GetFontDefRef()) = *GetFontDefRef(); + + poNew->SetCustomSymbolStyle( GetCustomSymbolStyle() ); + + return poNew; +} + +/********************************************************************** + * TABCustomPoint::ReadGeometryFromMAPFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to the beginning of + * a map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABCustomPoint::ReadGeometryFromMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + double dX, dY; + OGRGeometry *poGeometry; + + /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */ + if (bCoordBlockDataOnly) + return 0; + + /*----------------------------------------------------------------- + * Fetch and validate geometry type + *----------------------------------------------------------------*/ + m_nMapInfoType = poObjHdr->m_nType; + + if (m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL && + m_nMapInfoType != TAB_GEOM_CUSTOMSYMBOL_C ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)", + m_nMapInfoType, m_nMapInfoType); + return -1; + } + + /*----------------------------------------------------------------- + * Read object information + *----------------------------------------------------------------*/ + TABMAPObjCustomPoint *poPointHdr = (TABMAPObjCustomPoint *)poObjHdr; + + m_nUnknown_ = poPointHdr->m_nUnknown_; // ??? + m_nCustomStyle = poPointHdr->m_nCustomStyle;// 0x01=Show BG, + // 0x02=Apply Color + + m_nSymbolDefIndex = poPointHdr->m_nSymbolId; // Symbol index + poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef); + + m_nFontDefIndex = poPointHdr->m_nFontId; // Font index + poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef); + + /*----------------------------------------------------------------- + * Create and fill geometry object + *----------------------------------------------------------------*/ + poMapFile->Int2Coordsys(poPointHdr->m_nX, poPointHdr->m_nY, dX, dY); + poGeometry = new OGRPoint(dX, dY); + + SetGeometryDirectly(poGeometry); + + SetMBR(dX, dY, dX, dY); + SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + + return 0; +} + +/********************************************************************** + * TABCustomPoint::WriteGeometryToMAPFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to a valid map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABCustomPoint::WriteGeometryToMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + GInt32 nX, nY; + OGRGeometry *poGeom; + OGRPoint *poPoint; + + /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */ + if (bCoordBlockDataOnly) + return 0; + + /*----------------------------------------------------------------- + * We assume that ValidateMapInfoType() was called already and that + * the type in poObjHdr->m_nType is valid. + *----------------------------------------------------------------*/ + CPLAssert(m_nMapInfoType == poObjHdr->m_nType); + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + poPoint = (OGRPoint*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABCustomPoint: Missing or Invalid Geometry!"); + return -1; + } + + poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY); + + /*----------------------------------------------------------------- + * Copy object information + *----------------------------------------------------------------*/ + TABMAPObjCustomPoint *poPointHdr = (TABMAPObjCustomPoint *)poObjHdr; + + poPointHdr->m_nX = nX; + poPointHdr->m_nY = nY; + poPointHdr->SetMBR(nX, nY, nX, nY); + poPointHdr->m_nUnknown_ = m_nUnknown_; + poPointHdr->m_nCustomStyle = m_nCustomStyle;// 0x01=Show BG, + // 0x02=Apply Color + + m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef); + poPointHdr->m_nSymbolId = (GByte)m_nSymbolDefIndex; // Symbol index + + m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef); + poPointHdr->m_nFontId = (GByte)m_nFontDefIndex; // Font index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * TABCustomPoint::GetStyleString() + * + * Return style string for this feature. + * + * Style String is built only once during the first call to GetStyleString(). + **********************************************************************/ +const char *TABCustomPoint::GetStyleString() +{ + if (m_pszStyleString == NULL) + { + m_pszStyleString = CPLStrdup(GetSymbolStyleString()); + } + + return m_pszStyleString; +} + +/*===================================================================== + * class TABPolyline + *====================================================================*/ + + +/********************************************************************** + * TABPolyline::TABPolyline() + * + * Constructor. + **********************************************************************/ +TABPolyline::TABPolyline(OGRFeatureDefn *poDefnIn): + TABFeature(poDefnIn) +{ + m_bCenterIsSet = FALSE; + m_bSmooth = FALSE; + m_bWriteTwoPointLineAsPolyline = FALSE; +} + +/********************************************************************** + * TABPolyline::~TABPolyline() + * + * Destructor. + **********************************************************************/ +TABPolyline::~TABPolyline() +{ +} + +/********************************************************************** + * TABPolyline::CloneTABFeature() + * + * Duplicate feature, including stuff specific to each TABFeature type. + * + * This method calls the generic TABFeature::CloneTABFeature() and + * then copies any members specific to its own type. + **********************************************************************/ +TABFeature *TABPolyline::CloneTABFeature(OGRFeatureDefn *poNewDefn/*=NULL*/) +{ + /*----------------------------------------------------------------- + * Alloc new feature and copy the base stuff + *----------------------------------------------------------------*/ + TABPolyline *poNew = new TABPolyline(poNewDefn ? poNewDefn : GetDefnRef()); + + CopyTABFeatureBase(poNew); + + /*----------------------------------------------------------------- + * And members specific to this class + *----------------------------------------------------------------*/ + // ITABFeaturePen + *(poNew->GetPenDefRef()) = *GetPenDefRef(); + + poNew->m_bSmooth = m_bSmooth; + poNew->m_bCenterIsSet = m_bCenterIsSet; + poNew->m_dCenterX = m_dCenterX; + poNew->m_dCenterY = m_dCenterY; + + return poNew; +} + +/********************************************************************** + * TABPolyline::GetNumParts() + * + * Return the total number of parts in this object. + * + * Returns 0 if the geometry contained in the object is invalid or missing. + **********************************************************************/ +int TABPolyline::GetNumParts() +{ + OGRGeometry *poGeom; + int numParts = 0; + + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) + { + /*------------------------------------------------------------- + * Simple polyline + *------------------------------------------------------------*/ + numParts = 1; + } + else if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString) + { + /*------------------------------------------------------------- + * Multiple polyline + *------------------------------------------------------------*/ + OGRMultiLineString *poMultiLine = (OGRMultiLineString*)poGeom; + numParts = poMultiLine->getNumGeometries(); + } + + return numParts; +} + +/********************************************************************** + * TABPolyline::GetPartRef() + * + * Returns a reference to the specified OGRLineString number, hiding the + * complexity of dealing with OGRMultiLineString vs OGRLineString cases. + * + * Returns NULL if the geometry contained in the object is invalid or + * missing or if the specified part index is invalid. + **********************************************************************/ +OGRLineString *TABPolyline::GetPartRef(int nPartIndex) +{ + OGRGeometry *poGeom; + + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString && nPartIndex==0) + { + /*------------------------------------------------------------- + * Simple polyline + *------------------------------------------------------------*/ + return (OGRLineString *)poGeom; + } + else if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString) + { + /*------------------------------------------------------------- + * Multiple polyline + *------------------------------------------------------------*/ + OGRMultiLineString *poMultiLine = (OGRMultiLineString*)poGeom; + if (nPartIndex >= 0 && + nPartIndex < poMultiLine->getNumGeometries()) + { + return (OGRLineString*)poMultiLine->getGeometryRef(nPartIndex); + } + else + return NULL; + } + + return NULL; +} + +/********************************************************************** + * TABPolyline::ValidateMapInfoType() + * + * Check the feature's geometry part and return the corresponding + * mapinfo object type code. The m_nMapInfoType member will also + * be updated for further calls to GetMapInfoType(); + * + * Returns TAB_GEOM_NONE if the geometry is not compatible with what + * is expected for this object class. + **********************************************************************/ +TABGeomType TABPolyline::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + OGRMultiLineString *poMultiLine = NULL; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) + { + /*------------------------------------------------------------- + * Simple polyline + *------------------------------------------------------------*/ + OGRLineString *poLine = (OGRLineString*)poGeom; + if ( TAB_REGION_PLINE_REQUIRES_V800(1, poLine->getNumPoints()) ) + { + m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE; + } + else if ( poLine->getNumPoints() > TAB_REGION_PLINE_300_MAX_VERTICES) + { + m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE; + } + else if ( poLine->getNumPoints() > 2 ) + { + m_nMapInfoType = TAB_GEOM_PLINE; + } + else if ( (poLine->getNumPoints() == 2) && + (m_bWriteTwoPointLineAsPolyline == TRUE) ) + { + m_nMapInfoType = TAB_GEOM_PLINE; + } + else if ( (poLine->getNumPoints() == 2) && + (m_bWriteTwoPointLineAsPolyline == FALSE) ) + { + m_nMapInfoType = TAB_GEOM_LINE; + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPolyline: Geometry must contain at least 2 points."); + m_nMapInfoType = TAB_GEOM_NONE; + } + } + else if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString) + { + /*------------------------------------------------------------- + * Multiple polyline... validate all components + *------------------------------------------------------------*/ + int iLine, numLines; + GInt32 numPointsTotal = 0; + poMultiLine = (OGRMultiLineString*)poGeom; + numLines = poMultiLine->getNumGeometries(); + + m_nMapInfoType = TAB_GEOM_MULTIPLINE; + + for(iLine=0; iLine < numLines; iLine++) + { + poGeom = poMultiLine->getGeometryRef(iLine); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) != wkbLineString) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPolyline: Object contains an invalid Geometry!"); + m_nMapInfoType = TAB_GEOM_NONE; + numPointsTotal = 0; + break; + } + OGRLineString *poLine = (OGRLineString*)poGeom; + numPointsTotal += poLine->getNumPoints(); + } + + if ( TAB_REGION_PLINE_REQUIRES_V800(numLines, numPointsTotal) ) + m_nMapInfoType = TAB_GEOM_V800_MULTIPLINE; + else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES) + m_nMapInfoType = TAB_GEOM_V450_MULTIPLINE; + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPolyline: Missing or Invalid Geometry!"); + m_nMapInfoType = TAB_GEOM_NONE; + } + + /*----------------------------------------------------------------- + * Decide if coordinates should be compressed or not. + * + * __TODO__ We never write type LINE (2 points line) as compressed + * for the moment. If we ever do it, then the decision to write + * a 2 point line in compressed coordinates or not should take into + * account the location of the object block MBR, so this would be + * better handled directly by TABMAPObjLine::WriteObject() since the + * object block center is not known until it is written to disk. + *----------------------------------------------------------------*/ + if (m_nMapInfoType != TAB_GEOM_LINE) + { + ValidateCoordType(poMapFile); + } + else + { + UpdateMBR(poMapFile); + } + + return m_nMapInfoType; +} + + +/********************************************************************** + * TABPolyline::ReadGeometryFromMAPFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to the beginning of + * a map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABPolyline::ReadGeometryFromMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock **ppoCoordBlock/*=NULL*/) +{ + GInt32 nX, nY; + double dX, dY, dXMin, dYMin, dXMax, dYMax; + OGRGeometry *poGeometry; + OGRLineString *poLine; + GBool bComprCoord = poObjHdr->IsCompressedType(); + TABMAPCoordBlock *poCoordBlock = NULL; + + /*----------------------------------------------------------------- + * Fetch and validate geometry type + *----------------------------------------------------------------*/ + m_nMapInfoType = poObjHdr->m_nType; + + if (m_nMapInfoType == TAB_GEOM_LINE || + m_nMapInfoType == TAB_GEOM_LINE_C ) + { + /*============================================================= + * LINE (2 vertices) + *============================================================*/ + TABMAPObjLine *poLineHdr = (TABMAPObjLine *)poObjHdr; + + m_bSmooth = FALSE; + + poGeometry = poLine = new OGRLineString(); + poLine->setNumPoints(2); + + poMapFile->Int2Coordsys(poLineHdr->m_nX1, poLineHdr->m_nY1, + dXMin, dYMin); + poLine->setPoint(0, dXMin, dYMin); + + poMapFile->Int2Coordsys(poLineHdr->m_nX2, poLineHdr->m_nY2, + dXMax, dYMax); + poLine->setPoint(1, dXMax, dYMax); + + if (!bCoordBlockDataOnly) + { + m_nPenDefIndex = poLineHdr->m_nPenId; // Pen index + poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef); + } + } + else if (m_nMapInfoType == TAB_GEOM_PLINE || + m_nMapInfoType == TAB_GEOM_PLINE_C ) + { + /*============================================================= + * PLINE ( > 2 vertices) + *============================================================*/ + int i, numPoints, nStatus; + GUInt32 nCoordDataSize; + GInt32 nCoordBlockPtr; + + /*------------------------------------------------------------- + * Copy data from poObjHdr + *------------------------------------------------------------*/ + TABMAPObjPLine *poPLineHdr = (TABMAPObjPLine *)poObjHdr; + + nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr; + nCoordDataSize = poPLineHdr->m_nCoordDataSize; + //numLineSections = poPLineHdr->m_numLineSections; // Always 1 + m_bSmooth = poPLineHdr->m_bSmooth; + + // Centroid/label point + poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY, + dX, dY); + SetCenter(dX, dY); + + // Compressed coordinate origin (useful only in compressed case!) + m_nComprOrgX = poPLineHdr->m_nComprOrgX; + m_nComprOrgY = poPLineHdr->m_nComprOrgY; + + // MBR + poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, + dXMin, dYMin); + poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, + dXMax, dYMax); + + if (!bCoordBlockDataOnly) + { + m_nPenDefIndex = poPLineHdr->m_nPenId; // Pen index + poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef); + } + + /*------------------------------------------------------------- + * Create Geometry and read coordinates + *------------------------------------------------------------*/ + numPoints = nCoordDataSize/(bComprCoord?4:8); + + if (ppoCoordBlock != NULL && *ppoCoordBlock != NULL) + poCoordBlock = *ppoCoordBlock; + else + poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr); + if (poCoordBlock == NULL) + { + CPLError(CE_Failure, CPLE_FileIO, + "Can't access coordinate block at offset %d", + nCoordBlockPtr); + return -1; + } + + poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY); + + poGeometry = poLine = new OGRLineString(); + poLine->setNumPoints(numPoints); + + nStatus = 0; + for(i=0; nStatus == 0 && iReadIntCoord(bComprCoord, nX, nY); + if (nStatus != 0) + break; + poMapFile->Int2Coordsys(nX, nY, dX, dY); + poLine->setPoint(i, dX, dY); + } + + if (nStatus != 0) + { + // Failed ... error message has already been produced + delete poGeometry; + return nStatus; + } + + } + else if (m_nMapInfoType == TAB_GEOM_MULTIPLINE || + m_nMapInfoType == TAB_GEOM_MULTIPLINE_C || + m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE || + m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C || + m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE || + m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C ) + { + /*============================================================= + * PLINE MULTIPLE + *============================================================*/ + int i, iSection; + GInt32 nCoordBlockPtr, numLineSections; + GInt32 /* nCoordDataSize, */ numPointsTotal, *panXY; + OGRMultiLineString *poMultiLine; + TABMAPCoordSecHdr *pasSecHdrs; + int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType); + + /*------------------------------------------------------------- + * Copy data from poObjHdr + *------------------------------------------------------------*/ + TABMAPObjPLine *poPLineHdr = (TABMAPObjPLine *)poObjHdr; + + nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr; + /* nCoordDataSize = poPLineHdr->m_nCoordDataSize; */ + numLineSections = poPLineHdr->m_numLineSections; + m_bSmooth = poPLineHdr->m_bSmooth; + + // Centroid/label point + poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY, + dX, dY); + SetCenter(dX, dY); + + // Compressed coordinate origin (useful only in compressed case!) + m_nComprOrgX = poPLineHdr->m_nComprOrgX; + m_nComprOrgY = poPLineHdr->m_nComprOrgY; + + // MBR + poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, + dXMin, dYMin); + poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, + dXMax, dYMax); + + if (!bCoordBlockDataOnly) + { + m_nPenDefIndex = poPLineHdr->m_nPenId; // Pen index + poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef); + } + + /*------------------------------------------------------------- + * Read data from the coord. block + *------------------------------------------------------------*/ + pasSecHdrs = (TABMAPCoordSecHdr*)CPLMalloc(numLineSections* + sizeof(TABMAPCoordSecHdr)); + + if (ppoCoordBlock != NULL && *ppoCoordBlock != NULL) + poCoordBlock = *ppoCoordBlock; + else + poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr); + + if (poCoordBlock == NULL || + poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion, + numLineSections, + pasSecHdrs, numPointsTotal) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed reading coordinate data at offset %d", + nCoordBlockPtr); + CPLFree(pasSecHdrs); + return -1; + } + + poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY); + + panXY = (GInt32*)CPLMalloc(numPointsTotal*2*sizeof(GInt32)); + + if (poCoordBlock->ReadIntCoords(bComprCoord,numPointsTotal,panXY) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed reading coordinate data at offset %d", + nCoordBlockPtr); + CPLFree(pasSecHdrs); + CPLFree(panXY); + return -1; + } + + /*------------------------------------------------------------- + * Create a Geometry collection with one line geometry for + * each coordinates section + * If object contains only one section, then return a simple LineString + *------------------------------------------------------------*/ + if (numLineSections > 1) + poGeometry = poMultiLine = new OGRMultiLineString(); + else + poGeometry = poMultiLine = NULL; + + for(iSection=0; iSectionsetNumPoints(numSectionVertices); + + for(i=0; iInt2Coordsys(*pnXYPtr, *(pnXYPtr+1), dX, dY); + poLine->setPoint(i, dX, dY); + pnXYPtr += 2; + } + + if (poGeometry==NULL) + poGeometry = poLine; + else if (poMultiLine->addGeometryDirectly(poLine) != OGRERR_NONE) + { + CPLAssert(FALSE); // Just in case lower-level lib is modified + } + poLine = NULL; + } + + CPLFree(pasSecHdrs); + CPLFree(panXY); + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)", + m_nMapInfoType, m_nMapInfoType); + return -1; + } + + SetGeometryDirectly(poGeometry); + + SetMBR(dXMin, dYMin, dXMax, dYMax); + SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + + /* Return a ref to coord block so that caller can continue reading + * after the end of this object (used by TABCollection and index splitting) + */ + if (ppoCoordBlock) + *ppoCoordBlock = poCoordBlock; + + return 0; +} + +/********************************************************************** + * TABPolyline::WriteGeometryToMAPFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to a valid map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABPolyline::WriteGeometryToMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock **ppoCoordBlock/*=NULL*/) +{ + GInt32 nX, nY; + OGRGeometry *poGeom; + OGRLineString *poLine=NULL; + TABMAPCoordBlock *poCoordBlock = NULL; + + /*----------------------------------------------------------------- + * We assume that ValidateMapInfoType() was called already and that + * the type in poObjHdr->m_nType is valid. + *----------------------------------------------------------------*/ + CPLAssert(m_nMapInfoType == poObjHdr->m_nType); + CPLErrorReset(); + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + + if ((m_nMapInfoType == TAB_GEOM_LINE || + m_nMapInfoType == TAB_GEOM_LINE_C ) && + poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString && + (poLine = (OGRLineString*)poGeom)->getNumPoints() == 2) + { + /*============================================================= + * LINE (2 vertices) + *============================================================*/ + TABMAPObjLine *poLineHdr = (TABMAPObjLine *)poObjHdr; + + poMapFile->Coordsys2Int(poLine->getX(0), poLine->getY(0), + poLineHdr->m_nX1, poLineHdr->m_nY1); + poMapFile->Coordsys2Int(poLine->getX(1), poLine->getY(1), + poLineHdr->m_nX2, poLineHdr->m_nY2); + poLineHdr->SetMBR(poLineHdr->m_nX1, poLineHdr->m_nY1, + poLineHdr->m_nX2, poLineHdr->m_nY2 ); + + if (!bCoordBlockDataOnly) + { + m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef); + poLineHdr->m_nPenId = (GByte)m_nPenDefIndex; // Pen index + } + + } + else if ((m_nMapInfoType == TAB_GEOM_PLINE || + m_nMapInfoType == TAB_GEOM_PLINE_C ) && + poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) + { + /*============================================================= + * PLINE ( > 2 vertices and less than 32767 vertices) + *============================================================*/ + int i, numPoints, nStatus; + GUInt32 nCoordDataSize; + GInt32 nCoordBlockPtr; + GBool bCompressed = poObjHdr->IsCompressedType(); + + /*------------------------------------------------------------- + * Process geometry first... + *------------------------------------------------------------*/ + poLine = (OGRLineString*)poGeom; + numPoints = poLine->getNumPoints(); + CPLAssert(numPoints <= TAB_REGION_PLINE_300_MAX_VERTICES); + + if (ppoCoordBlock != NULL && *ppoCoordBlock != NULL) + poCoordBlock = *ppoCoordBlock; + else + poCoordBlock = poMapFile->GetCurCoordBlock(); + poCoordBlock->StartNewFeature(); + nCoordBlockPtr = poCoordBlock->GetCurAddress(); + poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY); + + nStatus = 0; + for(i=0; nStatus == 0 && iCoordsys2Int(poLine->getX(i), poLine->getY(i), nX, nY); + if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, + bCompressed)) != 0) + { + // Failed ... error message has already been produced + return nStatus; + } + } + + nCoordDataSize = poCoordBlock->GetFeatureDataSize(); + + /*------------------------------------------------------------- + * Copy info to poObjHdr + *------------------------------------------------------------*/ + TABMAPObjPLine *poPLineHdr = (TABMAPObjPLine *)poObjHdr; + + poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr; + poPLineHdr->m_nCoordDataSize = nCoordDataSize; + poPLineHdr->m_numLineSections = 1; + + poPLineHdr->m_bSmooth = m_bSmooth; + + // MBR + poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax); + + // Polyline center/label point + double dX, dY; + if (GetCenter(dX, dY) != -1) + { + poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX, + poPLineHdr->m_nLabelY); + } + else + { + poPLineHdr->m_nLabelX = m_nComprOrgX; + poPLineHdr->m_nLabelY = m_nComprOrgY; + } + + // Compressed coordinate origin (useful only in compressed case!) + poPLineHdr->m_nComprOrgX = m_nComprOrgX; + poPLineHdr->m_nComprOrgY = m_nComprOrgY; + + if (!bCoordBlockDataOnly) + { + m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef); + poPLineHdr->m_nPenId = (GByte)m_nPenDefIndex; // Pen index + } + + } + else if ((m_nMapInfoType == TAB_GEOM_MULTIPLINE || + m_nMapInfoType == TAB_GEOM_MULTIPLINE_C || + m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE || + m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C || + m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE || + m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C) && + poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString || + wkbFlatten(poGeom->getGeometryType()) == wkbLineString) ) + { + /*============================================================= + * PLINE MULTIPLE (or single PLINE with more than 32767 vertices) + *============================================================*/ + int nStatus=0, i, iLine; + GInt32 numPointsTotal, numPoints; + GUInt32 nCoordDataSize; + GInt32 nCoordBlockPtr, numLines; + OGRMultiLineString *poMultiLine=NULL; + TABMAPCoordSecHdr *pasSecHdrs; + OGREnvelope sEnvelope; + GBool bCompressed = poObjHdr->IsCompressedType(); + + CPLAssert(m_nMapInfoType == TAB_GEOM_MULTIPLINE || + m_nMapInfoType == TAB_GEOM_MULTIPLINE_C || + m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE || + m_nMapInfoType == TAB_GEOM_V450_MULTIPLINE_C || + m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE || + m_nMapInfoType == TAB_GEOM_V800_MULTIPLINE_C); + /*------------------------------------------------------------- + * Process geometry first... + *------------------------------------------------------------*/ + if (ppoCoordBlock != NULL && *ppoCoordBlock != NULL) + poCoordBlock = *ppoCoordBlock; + else + poCoordBlock = poMapFile->GetCurCoordBlock(); + poCoordBlock->StartNewFeature(); + nCoordBlockPtr = poCoordBlock->GetCurAddress(); + poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY); + + if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString) + { + poMultiLine = (OGRMultiLineString*)poGeom; + numLines = poMultiLine->getNumGeometries(); + } + else + { + poMultiLine = NULL; + numLines = 1; + } + + /*------------------------------------------------------------- + * Build and write array of coord sections headers + *------------------------------------------------------------*/ + pasSecHdrs = (TABMAPCoordSecHdr*)CPLCalloc(numLines, + sizeof(TABMAPCoordSecHdr)); + + /*------------------------------------------------------------- + * In calculation of nDataOffset, we have to take into account that + * V450 header section uses int32 instead of int16 for numVertices + * and we add another 2 bytes to align with a 4 bytes boundary. + *------------------------------------------------------------*/ + int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType); + + int nTotalHdrSizeUncompressed; + if (nVersion >= 450) + nTotalHdrSizeUncompressed = 28 * numLines; + else + nTotalHdrSizeUncompressed = 24 * numLines; + + numPointsTotal = 0; + for(iLine=0; iLine < numLines; iLine++) + { + if (poMultiLine) + poGeom = poMultiLine->getGeometryRef(iLine); + + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) + { + poLine = (OGRLineString*)poGeom; + numPoints = poLine->getNumPoints(); + poLine->getEnvelope(&sEnvelope); + + pasSecHdrs[iLine].numVertices = poLine->getNumPoints(); + pasSecHdrs[iLine].numHoles = 0; // It's a line! + + poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY, + pasSecHdrs[iLine].nXMin, + pasSecHdrs[iLine].nYMin); + poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY, + pasSecHdrs[iLine].nXMax, + pasSecHdrs[iLine].nYMax); + pasSecHdrs[iLine].nDataOffset = nTotalHdrSizeUncompressed + + numPointsTotal*4*2; + pasSecHdrs[iLine].nVertexOffset = numPointsTotal; + + numPointsTotal += numPoints; + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPolyline: Object contains an invalid Geometry!"); + nStatus = -1; + } + + } + + if (nStatus == 0) + nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numLines, + pasSecHdrs, bCompressed); + + CPLFree(pasSecHdrs); + pasSecHdrs = NULL; + + if (nStatus != 0) + return nStatus; // Error has already been reported. + + /*------------------------------------------------------------- + * Then write the coordinates themselves... + *------------------------------------------------------------*/ + for(iLine=0; nStatus == 0 && iLine < numLines; iLine++) + { + if (poMultiLine) + poGeom = poMultiLine->getGeometryRef(iLine); + + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) + { + poLine = (OGRLineString*)poGeom; + numPoints = poLine->getNumPoints(); + + for(i=0; nStatus == 0 && iCoordsys2Int(poLine->getX(i), poLine->getY(i), + nX, nY); + if ((nStatus=poCoordBlock->WriteIntCoord(nX, nY, + bCompressed)) != 0) + { + // Failed ... error message has already been produced + return nStatus; + } + } + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPolyline: Object contains an invalid Geometry!"); + return -1; + } + + } + + nCoordDataSize = poCoordBlock->GetFeatureDataSize(); + + /*------------------------------------------------------------- + * ... and finally copy info to poObjHdr + *------------------------------------------------------------*/ + TABMAPObjPLine *poPLineHdr = (TABMAPObjPLine *)poObjHdr; + + poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr; + poPLineHdr->m_nCoordDataSize = nCoordDataSize; + poPLineHdr->m_numLineSections = numLines; + + poPLineHdr->m_bSmooth = m_bSmooth; + + // MBR + poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax); + + // Polyline center/label point + double dX, dY; + if (GetCenter(dX, dY) != -1) + { + poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX, + poPLineHdr->m_nLabelY); + } + else + { + poPLineHdr->m_nLabelX = m_nComprOrgX; + poPLineHdr->m_nLabelY = m_nComprOrgY; + } + + // Compressed coordinate origin (useful only in compressed case!) + poPLineHdr->m_nComprOrgX = m_nComprOrgX; + poPLineHdr->m_nComprOrgY = m_nComprOrgY; + + if (!bCoordBlockDataOnly) + { + m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef); + poPLineHdr->m_nPenId = (GByte)m_nPenDefIndex; // Pen index + } + + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPolyline: Object contains an invalid Geometry!"); + return -1; + } + + if (CPLGetLastErrorType() == CE_Failure ) + return -1; + + /* Return a ref to coord block so that caller can continue writing + * after the end of this object (used by index splitting) + */ + if (ppoCoordBlock) + *ppoCoordBlock = poCoordBlock; + + return 0; +} + +/********************************************************************** + * TABPolyline::GetStyleString() + * + * Return style string for this feature. + * + * Style String is built only once during the first call to GetStyleString(). + **********************************************************************/ +const char *TABPolyline::GetStyleString() +{ + if (m_pszStyleString == NULL) + { + m_pszStyleString = CPLStrdup(GetPenStyleString()); + } + + return m_pszStyleString; +} + + +/********************************************************************** + * TABPolyline::DumpMIF() + * + * Dump feature geometry in a format similar to .MIF PLINEs. + **********************************************************************/ +void TABPolyline::DumpMIF(FILE *fpOut /*=NULL*/) +{ + OGRGeometry *poGeom; + OGRMultiLineString *poMultiLine = NULL; + OGRLineString *poLine = NULL; + int i, numPoints; + + if (fpOut == NULL) + fpOut = stdout; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) + { + /*------------------------------------------------------------- + * Generate output for simple polyline + *------------------------------------------------------------*/ + poLine = (OGRLineString*)poGeom; + numPoints = poLine->getNumPoints(); + fprintf(fpOut, "PLINE %d\n", numPoints); + for(i=0; igetX(i), poLine->getY(i)); + } + else if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString) + { + /*------------------------------------------------------------- + * Generate output for multiple polyline + *------------------------------------------------------------*/ + int iLine, numLines; + poMultiLine = (OGRMultiLineString*)poGeom; + numLines = poMultiLine->getNumGeometries(); + fprintf(fpOut, "PLINE MULTIPLE %d\n", numLines); + for(iLine=0; iLine < numLines; iLine++) + { + poGeom = poMultiLine->getGeometryRef(iLine); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) + { + poLine = (OGRLineString*)poGeom; + numPoints = poLine->getNumPoints(); + fprintf(fpOut, " %d\n", numPoints); + for(i=0; igetX(i),poLine->getY(i)); + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPolyline: Object contains an invalid Geometry!"); + return; + } + + } + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPolyline: Missing or Invalid Geometry!"); + return; + } + + if (m_bCenterIsSet) + fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY); + + // Finish with PEN/BRUSH/etc. clauses + DumpPenDef(); + + fflush(fpOut); +} + +/********************************************************************** + * TABPolyline::GetCenter() + * + * Returns the center point of the line. Compute one if it was not + * explicitly set: + * + * In MapInfo, for a simple or multiple polyline (pline), the center point + * in the object definition is supposed to be either the center point of + * the pline or the first section of a multiple pline (if an odd number of + * points in the pline or first section), or the midway point between the + * two central points (if an even number of points involved). + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABPolyline::GetCenter(double &dX, double &dY) +{ + if (!m_bCenterIsSet) + { + OGRGeometry *poGeom; + OGRLineString *poLine = NULL; + + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) + { + poLine = (OGRLineString *)poGeom; + } + else if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString) + { + OGRMultiLineString *poMultiLine = (OGRMultiLineString*)poGeom; + if (poMultiLine->getNumGeometries() > 0) + poLine = (OGRLineString *)poMultiLine->getGeometryRef(0); + } + + if (poLine && poLine->getNumPoints() > 0) + { + int i = poLine->getNumPoints()/2; + if (poLine->getNumPoints() % 2 == 0) + { + // Return the midway between the 2 center points + m_dCenterX = (poLine->getX(i-1) + poLine->getX(i))/2.0; + m_dCenterY = (poLine->getY(i-1) + poLine->getY(i))/2.0; + } + else + { + // Return the center point + m_dCenterX = poLine->getX(i); + m_dCenterY = poLine->getY(i); + } + m_bCenterIsSet = TRUE; + } + } + + if (!m_bCenterIsSet) + return -1; + + dX = m_dCenterX; + dY = m_dCenterY; + return 0; +} + +/********************************************************************** + * TABPolyline::SetCenter() + * + * Set the X,Y coordinates to use as center point for the line. + **********************************************************************/ +void TABPolyline::SetCenter(double dX, double dY) +{ + m_dCenterX = dX; + m_dCenterY = dY; + m_bCenterIsSet = TRUE; +} + +/********************************************************************** + * TABPolyline::TwoPointLineAsPolyline() + * + * Returns the value of m_bWriteTwoPointLineAsPolyline + **********************************************************************/ +GBool TABPolyline::TwoPointLineAsPolyline() +{ + return m_bWriteTwoPointLineAsPolyline; +} + +/********************************************************************** +* TABPolyline::TwoPointLineAsPolyline() +* +* Sets the value of m_bWriteTwoPointLineAsPolyline +**********************************************************************/ +void TABPolyline::TwoPointLineAsPolyline(GBool bTwoPointLineAsPolyline) +{ + m_bWriteTwoPointLineAsPolyline = bTwoPointLineAsPolyline; +} + + +/*===================================================================== + * class TABRegion + *====================================================================*/ + +/********************************************************************** + * TABRegion::TABRegion() + * + * Constructor. + **********************************************************************/ +TABRegion::TABRegion(OGRFeatureDefn *poDefnIn): + TABFeature(poDefnIn) +{ + m_bCenterIsSet = FALSE; + m_bSmooth = FALSE; +} + +/********************************************************************** + * TABRegion::~TABRegion() + * + * Destructor. + **********************************************************************/ +TABRegion::~TABRegion() +{ +} + +/********************************************************************** + * TABRegion::CloneTABFeature() + * + * Duplicate feature, including stuff specific to each TABFeature type. + * + * This method calls the generic TABFeature::CopyTABFeatureBase() and + * then copies any members specific to its own type. + **********************************************************************/ +TABFeature *TABRegion::CloneTABFeature(OGRFeatureDefn *poNewDefn/*=NULL*/) +{ + /*----------------------------------------------------------------- + * Alloc new feature and copy the base stuff + *----------------------------------------------------------------*/ + TABRegion *poNew = new TABRegion(poNewDefn ? poNewDefn : GetDefnRef()); + + CopyTABFeatureBase(poNew); + + /*----------------------------------------------------------------- + * And members specific to this class + *----------------------------------------------------------------*/ + // ITABFeaturePen + *(poNew->GetPenDefRef()) = *GetPenDefRef(); + + // ITABFeatureBrush + *(poNew->GetBrushDefRef()) = *GetBrushDefRef(); + + poNew->m_bSmooth = m_bSmooth; + poNew->m_bCenterIsSet = m_bCenterIsSet; + poNew->m_dCenterX = m_dCenterX; + poNew->m_dCenterY = m_dCenterY; + + return poNew; +} + +/********************************************************************** + * TABRegion::ValidateMapInfoType() + * + * Check the feature's geometry part and return the corresponding + * mapinfo object type code. The m_nMapInfoType member will also + * be updated for further calls to GetMapInfoType(); + * + * Returns TAB_GEOM_NONE if the geometry is not compatible with what + * is expected for this object class. + **********************************************************************/ +TABGeomType TABRegion::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon || + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) + { + GInt32 numPointsTotal=0, numRings=GetNumRings(); + for(int i=0; igetNumPoints(); + } + if ( TAB_REGION_PLINE_REQUIRES_V800(numRings, numPointsTotal) ) + m_nMapInfoType = TAB_GEOM_V800_REGION; + else if (numPointsTotal > TAB_REGION_PLINE_300_MAX_VERTICES) + m_nMapInfoType = TAB_GEOM_V450_REGION; + else + m_nMapInfoType = TAB_GEOM_REGION; + + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRegion: Missing or Invalid Geometry!"); + m_nMapInfoType = TAB_GEOM_NONE; + } + + /*----------------------------------------------------------------- + * Decide if coordinates should be compressed or not. + *----------------------------------------------------------------*/ + ValidateCoordType(poMapFile); + + return m_nMapInfoType; +} + +/********************************************************************** + * TABRegion::ReadGeometryFromMAPFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to the beginning of + * a map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABRegion::ReadGeometryFromMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock **ppoCoordBlock/*=NULL*/) +{ + double dX, dY, dXMin, dYMin, dXMax, dYMax; + OGRGeometry *poGeometry; + OGRLinearRing *poRing; + TABMAPCoordBlock *poCoordBlock = NULL; + + /*----------------------------------------------------------------- + * Fetch and validate geometry type + *----------------------------------------------------------------*/ + m_nMapInfoType = poObjHdr->m_nType; + + if (m_nMapInfoType == TAB_GEOM_REGION || + m_nMapInfoType == TAB_GEOM_REGION_C || + m_nMapInfoType == TAB_GEOM_V450_REGION || + m_nMapInfoType == TAB_GEOM_V450_REGION_C || + m_nMapInfoType == TAB_GEOM_V800_REGION || + m_nMapInfoType == TAB_GEOM_V800_REGION_C ) + { + /*============================================================= + * REGION (Similar to PLINE MULTIPLE) + *============================================================*/ + int i, iSection; + GInt32 nCoordBlockPtr, numLineSections; + GInt32 /* nCoordDataSize, */ numPointsTotal, *panXY; + OGRMultiPolygon *poMultiPolygon = NULL; + OGRPolygon *poPolygon = NULL; + TABMAPCoordSecHdr *pasSecHdrs; + GBool bComprCoord = poObjHdr->IsCompressedType(); + int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType); + + /*------------------------------------------------------------- + * Copy data from poObjHdr + *------------------------------------------------------------*/ + TABMAPObjPLine *poPLineHdr = (TABMAPObjPLine *)poObjHdr; + + nCoordBlockPtr = poPLineHdr->m_nCoordBlockPtr; + /* nCoordDataSize = poPLineHdr->m_nCoordDataSize; */ + numLineSections = poPLineHdr->m_numLineSections; + m_bSmooth = poPLineHdr->m_bSmooth; + + // Centroid/label point + poMapFile->Int2Coordsys(poPLineHdr->m_nLabelX, poPLineHdr->m_nLabelY, + dX, dY); + SetCenter(dX, dY); + + // Compressed coordinate origin (useful only in compressed case!) + m_nComprOrgX = poPLineHdr->m_nComprOrgX; + m_nComprOrgY = poPLineHdr->m_nComprOrgY; + + // MBR + poMapFile->Int2Coordsys(poPLineHdr->m_nMinX, poPLineHdr->m_nMinY, + dXMin, dYMin); + poMapFile->Int2Coordsys(poPLineHdr->m_nMaxX, poPLineHdr->m_nMaxY, + dXMax, dYMax); + + if (!bCoordBlockDataOnly) + { + m_nPenDefIndex = poPLineHdr->m_nPenId; // Pen index + poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef); + m_nBrushDefIndex = poPLineHdr->m_nBrushId; // Brush index + poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef); + } + + /*------------------------------------------------------------- + * Read data from the coord. block + *------------------------------------------------------------*/ + pasSecHdrs = (TABMAPCoordSecHdr*)CPLMalloc(numLineSections* + sizeof(TABMAPCoordSecHdr)); + + if (ppoCoordBlock != NULL && *ppoCoordBlock != NULL) + poCoordBlock = *ppoCoordBlock; + else + poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr); + + if (poCoordBlock) + poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY); + + if (poCoordBlock == NULL || + poCoordBlock->ReadCoordSecHdrs(bComprCoord, nVersion, + numLineSections, + pasSecHdrs, numPointsTotal) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed reading coordinate data at offset %d", + nCoordBlockPtr); + CPLFree(pasSecHdrs); + return -1; + } + + panXY = (GInt32*)CPLMalloc(numPointsTotal*2*sizeof(GInt32)); + + if (poCoordBlock->ReadIntCoords(bComprCoord,numPointsTotal,panXY) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed reading coordinate data at offset %d", + nCoordBlockPtr); + CPLFree(pasSecHdrs); + CPLFree(panXY); + return -1; + } + + /*------------------------------------------------------------- + * Decide if we should return an OGRPolygon or an OGRMultiPolygon + * depending on the number of outer rings found in CoordSecHdr blocks. + * The CoodSecHdr block for each outer ring in the region has a flag + * indicating the number of inner rings that follow. + * In older versions of the format, the count of inner rings was + * always zero, so in this case we would always return MultiPolygons. + * + * Note: The current implementation assumes that there cannot be + * holes inside holes (i.e. multiple levels of inner rings)... if + * that case was encountered then we would return an OGRMultiPolygon + * in which the topological relationship between the rings would + * be lost. + *------------------------------------------------------------*/ + int numOuterRings = 0; + for(iSection=0; iSection 1) + poGeometry = poMultiPolygon = new OGRMultiPolygon; + else + poGeometry = NULL; // Will be set later + + /*------------------------------------------------------------- + * OK, build the OGRGeometry object. + *------------------------------------------------------------*/ + int numHolesToRead = 0; + poPolygon = NULL; + for(iSection=0; iSectionsetNumPoints(numSectionVertices); + + for(i=0; iInt2Coordsys(*pnXYPtr, *(pnXYPtr+1), dX, dY); + poRing->setPoint(i, dX, dY); + pnXYPtr += 2; + } + + poPolygon->addRingDirectly(poRing); + poRing = NULL; + + if (numHolesToRead < 1) + { + if (numOuterRings > 1) + { + poMultiPolygon->addGeometryDirectly(poPolygon); + } + else + { + poGeometry = poPolygon; + CPLAssert(iSection == numLineSections-1); + } + + poPolygon = NULL; // We'll alloc a new polygon next loop. + } + + } + + CPLFree(pasSecHdrs); + CPLFree(panXY); + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)", + m_nMapInfoType, m_nMapInfoType); + return -1; + } + + SetGeometryDirectly(poGeometry); + + SetMBR(dXMin, dYMin, dXMax, dYMax); + SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + + /* Return a ref to coord block so that caller can continue reading + * after the end of this object (used by TABCollection and index splitting) + */ + if (ppoCoordBlock) + *ppoCoordBlock = poCoordBlock; + + return 0; +} + +/********************************************************************** + * TABRegion::WriteGeometryToMAPFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to a valid map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABRegion::WriteGeometryToMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock **ppoCoordBlock/*=NULL*/) +{ + GInt32 nX, nY; + OGRGeometry *poGeom; + TABMAPCoordBlock *poCoordBlock=NULL; + + /*----------------------------------------------------------------- + * We assume that ValidateMapInfoType() was called already and that + * the type in poObjHdr->m_nType is valid. + *----------------------------------------------------------------*/ + CPLAssert(m_nMapInfoType == poObjHdr->m_nType); + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + + if ((m_nMapInfoType == TAB_GEOM_REGION || + m_nMapInfoType == TAB_GEOM_REGION_C || + m_nMapInfoType == TAB_GEOM_V450_REGION || + m_nMapInfoType == TAB_GEOM_V450_REGION_C || + m_nMapInfoType == TAB_GEOM_V800_REGION || + m_nMapInfoType == TAB_GEOM_V800_REGION_C) && + poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon || + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) + { + /*============================================================= + * REGIONs are similar to PLINE MULTIPLE + * + * We accept both OGRPolygons (with one or multiple rings) and + * OGRMultiPolygons as input. + *============================================================*/ + int nStatus=0, i, iRing; + int numRingsTotal; + GUInt32 nCoordDataSize; + GInt32 nCoordBlockPtr; + TABMAPCoordSecHdr *pasSecHdrs = NULL; + GBool bCompressed = poObjHdr->IsCompressedType(); + + /*------------------------------------------------------------- + * Process geometry first... + *------------------------------------------------------------*/ + if (ppoCoordBlock != NULL && *ppoCoordBlock != NULL) + poCoordBlock = *ppoCoordBlock; + else + poCoordBlock = poMapFile->GetCurCoordBlock(); + poCoordBlock->StartNewFeature(); + nCoordBlockPtr = poCoordBlock->GetCurAddress(); + poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY); + +#ifdef TABDUMP + printf("TABRegion::WriteGeometryToMAPFile(): ComprOrgX,Y= (%d,%d)\n", + m_nComprOrgX, m_nComprOrgY); +#endif + /*------------------------------------------------------------- + * Fetch total number of rings and build array of coord + * sections headers. + *------------------------------------------------------------*/ + numRingsTotal = ComputeNumRings(&pasSecHdrs, poMapFile); + if (numRingsTotal == 0) + nStatus = -1; + + /*------------------------------------------------------------- + * Write the Coord. Section Header + *------------------------------------------------------------*/ + int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType); + + if (nStatus == 0) + nStatus = poCoordBlock->WriteCoordSecHdrs(nVersion, numRingsTotal, + pasSecHdrs, bCompressed); + + CPLFree(pasSecHdrs); + pasSecHdrs = NULL; + + if (nStatus != 0) + return nStatus; // Error has already been reported. + + /*------------------------------------------------------------- + * Go through all the rings in our OGRMultiPolygon or OGRPolygon + * to write the coordinates themselves... + *------------------------------------------------------------*/ + + for(iRing=0; iRing < numRingsTotal; iRing++) + { + OGRLinearRing *poRing; + + poRing = GetRingRef(iRing); + if (poRing == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRegion: Object Geometry contains NULL rings!"); + return -1; + } + + int numPoints = poRing->getNumPoints(); + + for(i=0; nStatus == 0 && iCoordsys2Int(poRing->getX(i), poRing->getY(i), + nX, nY); + if ((nStatus=poCoordBlock->WriteIntCoord(nX, nY, + bCompressed)) != 0) + { + // Failed ... error message has already been produced + return nStatus; + } + } + }/* for iRing*/ + + nCoordDataSize = poCoordBlock->GetFeatureDataSize(); + + /*------------------------------------------------------------- + * ... and finally copy info to poObjHdr + *------------------------------------------------------------*/ + TABMAPObjPLine *poPLineHdr = (TABMAPObjPLine *)poObjHdr; + + poPLineHdr->m_nCoordBlockPtr = nCoordBlockPtr; + poPLineHdr->m_nCoordDataSize = nCoordDataSize; + poPLineHdr->m_numLineSections = numRingsTotal; + + poPLineHdr->m_bSmooth = m_bSmooth; + + // MBR + poPLineHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax); + + // Region center/label point + double dX, dY; + if (GetCenter(dX, dY) != -1) + { + poMapFile->Coordsys2Int(dX, dY, poPLineHdr->m_nLabelX, + poPLineHdr->m_nLabelY); + } + else + { + poPLineHdr->m_nLabelX = m_nComprOrgX; + poPLineHdr->m_nLabelY = m_nComprOrgY; + } + + // Compressed coordinate origin (useful only in compressed case!) + poPLineHdr->m_nComprOrgX = m_nComprOrgX; + poPLineHdr->m_nComprOrgY = m_nComprOrgY; + + if (!bCoordBlockDataOnly) + { + m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef); + poPLineHdr->m_nPenId = (GByte)m_nPenDefIndex; // Pen index + + m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef); + poPLineHdr->m_nBrushId = (GByte)m_nBrushDefIndex; // Brush index + } + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRegion: Object contains an invalid Geometry!"); + return -1; + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + /* Return a ref to coord block so that caller can continue writing + * after the end of this object (used by index splitting) + */ + if (ppoCoordBlock) + *ppoCoordBlock = poCoordBlock; + + return 0; +} + + +/********************************************************************** + * TABRegion::GetNumRings() + * + * Return the total number of rings in this object making it look like + * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection + * of rings... hides the complexity of handling OGRMultiPolygons vs + * OGRPolygons, etc. + * + * Returns 0 if the geometry contained in the object is invalid or missing. + **********************************************************************/ +int TABRegion::GetNumRings() +{ + return ComputeNumRings(NULL, NULL); +} + +int TABRegion::ComputeNumRings(TABMAPCoordSecHdr **ppasSecHdrs, + TABMAPFile *poMapFile) +{ + OGRGeometry *poGeom; + int numRingsTotal = 0, iLastSect = 0; + + if (ppasSecHdrs) + *ppasSecHdrs = NULL; + poGeom = GetGeometryRef(); + + if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon || + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) + { + /*------------------------------------------------------------- + * Calculate total number of rings... + *------------------------------------------------------------*/ + OGRPolygon *poPolygon=NULL; + OGRMultiPolygon *poMultiPolygon = NULL; + + if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon) + { + poMultiPolygon = (OGRMultiPolygon *)poGeom; + for(int iPoly=0; iPolygetNumGeometries(); iPoly++) + { + // We are guaranteed that all parts are OGRPolygons + poPolygon = (OGRPolygon*)poMultiPolygon->getGeometryRef(iPoly); + if (poPolygon == NULL) + continue; + + numRingsTotal += poPolygon->getNumInteriorRings()+1; + + if (ppasSecHdrs) + { + if (AppendSecHdrs(poPolygon, *ppasSecHdrs, + poMapFile, iLastSect) != 0) + return 0; // An error happened, return count=0 + } + + }/*for*/ + } + else + { + poPolygon = (OGRPolygon*)poGeom; + numRingsTotal = poPolygon->getNumInteriorRings()+1; + + if (ppasSecHdrs) + { + if (AppendSecHdrs(poPolygon, *ppasSecHdrs, + poMapFile, iLastSect) != 0) + return 0; // An error happened, return count=0 + } + } + } + + /*----------------------------------------------------------------- + * If we're generating section header blocks, then init the + * coordinate offset values. + * + * In calculation of nDataOffset, we have to take into account that + * V450 header section uses int32 instead of int16 for numVertices + * and we add another 2 bytes to align with a 4 bytes boundary. + *------------------------------------------------------------*/ + int nTotalHdrSizeUncompressed; + if (m_nMapInfoType == TAB_GEOM_V450_REGION || + m_nMapInfoType == TAB_GEOM_V450_REGION_C || + m_nMapInfoType == TAB_GEOM_V800_REGION || + m_nMapInfoType == TAB_GEOM_V800_REGION_C) + nTotalHdrSizeUncompressed = 28 * numRingsTotal; + else + nTotalHdrSizeUncompressed = 24 * numRingsTotal; + + if (ppasSecHdrs) + { + int numPointsTotal = 0; + CPLAssert(iLastSect == numRingsTotal); + for (int iRing=0; iRinggetNumInteriorRings()+1; + + pasSecHdrs = (TABMAPCoordSecHdr*)CPLRealloc(pasSecHdrs, + (iLastRing+numRingsInPolygon)* + sizeof(TABMAPCoordSecHdr)); + + for(iRing=0; iRing < numRingsInPolygon; iRing++) + { + OGRLinearRing *poRing; + OGREnvelope sEnvelope; + + if (iRing == 0) + poRing = poPolygon->getExteriorRing(); + else + poRing = poPolygon->getInteriorRing(iRing-1); + + if (poRing == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Assertion Failed: Encountered NULL ring in OGRPolygon"); + return -1; + } + + poRing->getEnvelope(&sEnvelope); + + pasSecHdrs[iLastRing].numVertices = poRing->getNumPoints(); + + if (iRing == 0) + pasSecHdrs[iLastRing].numHoles = numRingsInPolygon-1; + else + pasSecHdrs[iLastRing].numHoles = 0; + + poMapFile->Coordsys2Int(sEnvelope.MinX, sEnvelope.MinY, + pasSecHdrs[iLastRing].nXMin, + pasSecHdrs[iLastRing].nYMin); + poMapFile->Coordsys2Int(sEnvelope.MaxX, sEnvelope.MaxY, + pasSecHdrs[iLastRing].nXMax, + pasSecHdrs[iLastRing].nYMax); + + iLastRing++; + }/* for iRing*/ + + return 0; +} + +/********************************************************************** + * TABRegion::GetRingRef() + * + * Returns a reference to the specified ring number making it look like + * all parts of the OGRMultiPolygon (or OGRPolygon) are a single collection + * of rings... hides the complexity of handling OGRMultiPolygons vs + * OGRPolygons, etc. + * + * Returns NULL if the geometry contained in the object is invalid or + * missing or if the specified ring index is invalid. + **********************************************************************/ +OGRLinearRing *TABRegion::GetRingRef(int nRequestedRingIndex) +{ + OGRGeometry *poGeom; + OGRLinearRing *poRing = NULL; + + poGeom = GetGeometryRef(); + + if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon || + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) + { + /*------------------------------------------------------------- + * Establish number of polygons based on geometry type + *------------------------------------------------------------*/ + OGRPolygon *poPolygon=NULL; + OGRMultiPolygon *poMultiPolygon = NULL; + int iCurRing = 0; + int numOGRPolygons = 0; + + if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon) + { + poMultiPolygon = (OGRMultiPolygon *)poGeom; + numOGRPolygons = poMultiPolygon->getNumGeometries(); + } + else + { + poPolygon = (OGRPolygon*)poGeom; + numOGRPolygons = 1; + } + + /*------------------------------------------------------------- + * Loop through polygons until we find the requested ring. + *------------------------------------------------------------*/ + iCurRing = 0; + for(int iPoly=0; poRing == NULL && iPoly < numOGRPolygons; iPoly++) + { + if (poMultiPolygon) + poPolygon = (OGRPolygon*)poMultiPolygon->getGeometryRef(iPoly); + else + poPolygon = (OGRPolygon*)poGeom; + + int numIntRings = poPolygon->getNumInteriorRings(); + + if (iCurRing == nRequestedRingIndex) + { + poRing = poPolygon->getExteriorRing(); + } + else if (nRequestedRingIndex > iCurRing && + nRequestedRingIndex-(iCurRing+1) < numIntRings) + { + poRing = poPolygon->getInteriorRing(nRequestedRingIndex- + (iCurRing+1) ); + } + iCurRing += numIntRings+1; + } + } + + return poRing; +} + +/********************************************************************** + * TABRegion::RingIsHole() + * + * Return false if the requested ring index is the first of a polygon + **********************************************************************/ +GBool TABRegion::IsInteriorRing(int nRequestedRingIndex) +{ + OGRGeometry *poGeom; + OGRLinearRing *poRing = NULL; + + poGeom = GetGeometryRef(); + + if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon || + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) + { + /*------------------------------------------------------------- + * Establish number of polygons based on geometry type + *------------------------------------------------------------*/ + OGRPolygon *poPolygon=NULL; + OGRMultiPolygon *poMultiPolygon = NULL; + int iCurRing = 0; + int numOGRPolygons = 0; + + if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon) + { + poMultiPolygon = (OGRMultiPolygon *)poGeom; + numOGRPolygons = poMultiPolygon->getNumGeometries(); + } + else + { + poPolygon = (OGRPolygon*)poGeom; + numOGRPolygons = 1; + } + + /*------------------------------------------------------------- + * Loop through polygons until we find the requested ring. + *------------------------------------------------------------*/ + iCurRing = 0; + for(int iPoly=0; poRing == NULL && iPoly < numOGRPolygons; iPoly++) + { + if (poMultiPolygon) + poPolygon = (OGRPolygon*)poMultiPolygon->getGeometryRef(iPoly); + else + poPolygon = (OGRPolygon*)poGeom; + + int numIntRings = poPolygon->getNumInteriorRings(); + + if (iCurRing == nRequestedRingIndex) + { + return FALSE; + } + else if (nRequestedRingIndex > iCurRing && + nRequestedRingIndex-(iCurRing+1) < numIntRings) + { + return TRUE; + } + iCurRing += numIntRings+1; + } + } + + return FALSE; +} + +/********************************************************************** + * TABRegion::GetStyleString() + * + * Return style string for this feature. + * + * Style String is built only once during the first call to GetStyleString(). + **********************************************************************/ +const char *TABRegion::GetStyleString() +{ + if (m_pszStyleString == NULL) + { + // Since GetPen/BrushStyleString() use CPLSPrintf(), we need + // to use temporary buffers + char *pszPen = CPLStrdup(GetPenStyleString()); + char *pszBrush = CPLStrdup(GetBrushStyleString()); + + m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen)); + + CPLFree(pszPen); + CPLFree(pszBrush); + } + + return m_pszStyleString; +} + + + +/********************************************************************** + * TABRegion::DumpMIF() + * + * Dump feature geometry in a format similar to .MIF REGIONs. + **********************************************************************/ +void TABRegion::DumpMIF(FILE *fpOut /*=NULL*/) +{ + OGRGeometry *poGeom; + int i, numPoints; + + if (fpOut == NULL) + fpOut = stdout; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon || + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon)) + { + /*------------------------------------------------------------- + * Generate output for region + * + * Note that we want to handle both OGRPolygons and OGRMultiPolygons + * that's why we use the GetNumRings()/GetRingRef() interface. + *------------------------------------------------------------*/ + int iRing, numRingsTotal = GetNumRings(); + + fprintf(fpOut, "REGION %d\n", numRingsTotal); + + for(iRing=0; iRing < numRingsTotal; iRing++) + { + OGRLinearRing *poRing; + + poRing = GetRingRef(iRing); + + if (poRing == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRegion: Object Geometry contains NULL rings!"); + return; + } + + numPoints = poRing->getNumPoints(); + fprintf(fpOut, " %d\n", numPoints); + for(i=0; igetX(i),poRing->getY(i)); + } + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRegion: Missing or Invalid Geometry!"); + return; + } + + if (m_bCenterIsSet) + fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY); + + // Finish with PEN/BRUSH/etc. clauses + DumpPenDef(); + DumpBrushDef(); + + fflush(fpOut); +} + +/********************************************************************** + * TABRegion::GetCenter() + * + * Returns the center/label point of the region. + * Compute one using OGRPolygonLabelPoint() if it was not explicitly set + * before. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABRegion::GetCenter(double &dX, double &dY) +{ + if (!m_bCenterIsSet) + { + /*------------------------------------------------------------- + * Calculate label point. If we have a multipolygon then we use + * the first OGRPolygon in the feature to calculate the point. + *------------------------------------------------------------*/ + OGRPoint oLabelPoint; + OGRPolygon *poPolygon=NULL; + OGRGeometry *poGeom; + + poGeom = GetGeometryRef(); + if (poGeom == NULL) + return -1; + + if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon) + { + OGRMultiPolygon *poMultiPolygon = (OGRMultiPolygon *)poGeom; + if (poMultiPolygon->getNumGeometries() > 0) + poPolygon = (OGRPolygon*)poMultiPolygon->getGeometryRef(0); + } + else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) + { + poPolygon = (OGRPolygon*)poGeom; + } + + if (poPolygon != NULL && + OGRPolygonLabelPoint(poPolygon, &oLabelPoint) == OGRERR_NONE) + { + m_dCenterX = oLabelPoint.getX(); + m_dCenterY = oLabelPoint.getY(); + } + else + { + OGREnvelope oEnv; + poGeom->getEnvelope(&oEnv); + m_dCenterX = (oEnv.MaxX + oEnv.MinX)/2.0; + m_dCenterY = (oEnv.MaxY + oEnv.MinY)/2.0; + } + + m_bCenterIsSet = TRUE; + } + + if (!m_bCenterIsSet) + return -1; + + dX = m_dCenterX; + dY = m_dCenterY; + return 0; +} + +/********************************************************************** + * TABRegion::SetCenter() + * + * Set the X,Y coordinates to use as center/label point for the region. + **********************************************************************/ +void TABRegion::SetCenter(double dX, double dY) +{ + m_dCenterX = dX; + m_dCenterY = dY; + m_bCenterIsSet = TRUE; +} + + +/*===================================================================== + * class TABRectangle + *====================================================================*/ + +/********************************************************************** + * TABRectangle::TABRectangle() + * + * Constructor. + **********************************************************************/ +TABRectangle::TABRectangle(OGRFeatureDefn *poDefnIn): + TABFeature(poDefnIn) +{ + m_bRoundCorners = FALSE; + m_dRoundXRadius = m_dRoundYRadius = 0.0; +} + +/********************************************************************** + * TABRectangle::~TABRectangle() + * + * Destructor. + **********************************************************************/ +TABRectangle::~TABRectangle() +{ +} + +/********************************************************************** + * TABRectangle::CloneTABFeature() + * + * Duplicate feature, including stuff specific to each TABFeature type. + * + * This method calls the generic TABFeature::CopyTABFeatureBase() and + * then copies any members specific to its own type. + **********************************************************************/ +TABFeature *TABRectangle::CloneTABFeature(OGRFeatureDefn *poNewDefn/*=NULL*/) +{ + /*----------------------------------------------------------------- + * Alloc new feature and copy the base stuff + *----------------------------------------------------------------*/ + TABRectangle *poNew = new TABRectangle(poNewDefn ? poNewDefn : + GetDefnRef()); + + CopyTABFeatureBase(poNew); + + /*----------------------------------------------------------------- + * And members specific to this class + *----------------------------------------------------------------*/ + // ITABFeaturePen + *(poNew->GetPenDefRef()) = *GetPenDefRef(); + + // ITABFeatureBrush + *(poNew->GetBrushDefRef()) = *GetBrushDefRef(); + + poNew->m_bRoundCorners = m_bRoundCorners; + poNew->m_dRoundXRadius = m_dRoundXRadius; + poNew->m_dRoundYRadius = m_dRoundYRadius; + + return poNew; +} + +/********************************************************************** + * TABRectangle::ValidateMapInfoType() + * + * Check the feature's geometry part and return the corresponding + * mapinfo object type code. The m_nMapInfoType member will also + * be updated for further calls to GetMapInfoType(); + * + * Returns TAB_GEOM_NONE if the geometry is not compatible with what + * is expected for this object class. + **********************************************************************/ +TABGeomType TABRectangle::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) + { + if (m_bRoundCorners && m_dRoundXRadius!=0.0 && m_dRoundYRadius!=0.0) + m_nMapInfoType = TAB_GEOM_ROUNDRECT; + else + m_nMapInfoType = TAB_GEOM_RECT; + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRectangle: Missing or Invalid Geometry!"); + m_nMapInfoType = TAB_GEOM_NONE; + } + + /*----------------------------------------------------------------- + * Decide if coordinates should be compressed or not. + *----------------------------------------------------------------*/ + // __TODO__ For now we always write uncompressed for this class... + // ValidateCoordType(poMapFile); + UpdateMBR(poMapFile); + + return m_nMapInfoType; +} + +/********************************************************************** + * TABRectangle::UpdateMBR() + * + * Update the feature MBR members using the geometry + * + * Returns 0 on success, or -1 if there is no geometry in object + **********************************************************************/ +int TABRectangle::UpdateMBR(TABMAPFile * poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + OGREnvelope sEnvelope; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) + poGeom->getEnvelope(&sEnvelope); + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRectangle: Missing or Invalid Geometry!"); + return -1; + } + + /*----------------------------------------------------------------- + * Note that we will simply use the rectangle's MBR and don't really + * read the polygon geometry... this should be OK unless the + * polygon geometry was not really a rectangle. + *----------------------------------------------------------------*/ + m_dXMin = sEnvelope.MinX; + m_dYMin = sEnvelope.MinY; + m_dXMax = sEnvelope.MaxX; + m_dYMax = sEnvelope.MaxY; + + if (poMapFile) + { + poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin); + poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax); + } + + return 0; +} + +/********************************************************************** + * TABRectangle::ReadGeometryFromMAPFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to the beginning of + * a map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABRectangle::ReadGeometryFromMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + double dXMin, dYMin, dXMax, dYMax; + OGRPolygon *poPolygon; + OGRLinearRing *poRing; + + /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */ + if (bCoordBlockDataOnly) + return 0; + + /*----------------------------------------------------------------- + * Fetch and validate geometry type + *----------------------------------------------------------------*/ + m_nMapInfoType = poObjHdr->m_nType; + + if (m_nMapInfoType != TAB_GEOM_RECT && + m_nMapInfoType != TAB_GEOM_RECT_C && + m_nMapInfoType != TAB_GEOM_ROUNDRECT && + m_nMapInfoType != TAB_GEOM_ROUNDRECT_C) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)", + m_nMapInfoType, m_nMapInfoType); + return -1; + } + + /*----------------------------------------------------------------- + * Read object information + *----------------------------------------------------------------*/ + TABMAPObjRectEllipse *poRectHdr = (TABMAPObjRectEllipse *)poObjHdr; + + // Read the corners radius + + if (m_nMapInfoType == TAB_GEOM_ROUNDRECT || + m_nMapInfoType == TAB_GEOM_ROUNDRECT_C) + { + // Read the corner's diameters + poMapFile->Int2CoordsysDist(poRectHdr->m_nCornerWidth, + poRectHdr->m_nCornerHeight, + m_dRoundXRadius, m_dRoundYRadius); + + // Divide by 2 since we store the corner's radius + m_dRoundXRadius /= 2.0; + m_dRoundYRadius /= 2.0; + + m_bRoundCorners = TRUE; + } + else + { + m_bRoundCorners = FALSE; + m_dRoundXRadius = m_dRoundYRadius = 0.0; + } + + // A rectangle is defined by its MBR + + poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, + dXMin, dYMin); + poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, + dXMax, dYMax); + + m_nPenDefIndex = poRectHdr->m_nPenId; // Pen index + poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef); + + m_nBrushDefIndex = poRectHdr->m_nBrushId; // Brush index + poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef); + + /*----------------------------------------------------------------- + * Call SetMBR() and GetMBR() now to make sure that min values are + * really smaller than max values. + *----------------------------------------------------------------*/ + SetMBR(dXMin, dYMin, dXMax, dYMax); + GetMBR(dXMin, dYMin, dXMax, dYMax); + + /* Copy int MBR to feature class members */ + SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + + /*----------------------------------------------------------------- + * Create and fill geometry object + *----------------------------------------------------------------*/ + poPolygon = new OGRPolygon; + poRing = new OGRLinearRing(); + if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0) + { + /*------------------------------------------------------------- + * For rounded rectangles, we generate arcs with 45 line + * segments for each corner. We start with lower-left corner + * and proceed counterclockwise + * We also have to make sure that rounding radius is not too + * large for the MBR in the generated polygon... however, we + * always return the true X/Y radius (not adjusted) since this + * is the way MapInfo seems to do it when a radius bigger than + * the MBR is passed from TBA to MIF. + *------------------------------------------------------------*/ + double dXRadius = MIN(m_dRoundXRadius, (dXMax-dXMin)/2.0); + double dYRadius = MIN(m_dRoundYRadius, (dYMax-dYMin)/2.0); + TABGenerateArc(poRing, 45, + dXMin + dXRadius, dYMin + dYRadius, dXRadius, dYRadius, + PI, 3.0*PI/2.0); + TABGenerateArc(poRing, 45, + dXMax - dXRadius, dYMin + dYRadius, dXRadius, dYRadius, + 3.0*PI/2.0, 2.0*PI); + TABGenerateArc(poRing, 45, + dXMax - dXRadius, dYMax - dYRadius, dXRadius, dYRadius, + 0.0, PI/2.0); + TABGenerateArc(poRing, 45, + dXMin + dXRadius, dYMax - dYRadius, dXRadius, dYRadius, + PI/2.0, PI); + + TABCloseRing(poRing); + } + else + { + poRing->addPoint(dXMin, dYMin); + poRing->addPoint(dXMax, dYMin); + poRing->addPoint(dXMax, dYMax); + poRing->addPoint(dXMin, dYMax); + poRing->addPoint(dXMin, dYMin); + } + + poPolygon->addRingDirectly(poRing); + SetGeometryDirectly(poPolygon); + + return 0; +} + +/********************************************************************** + * TABRectangle::WriteGeometryToMAPFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to a valid map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABRectangle::WriteGeometryToMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + + /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */ + if (bCoordBlockDataOnly) + return 0; + + /*----------------------------------------------------------------- + * We assume that ValidateMapInfoType() was called already and that + * the type in poObjHdr->m_nType is valid. + *----------------------------------------------------------------*/ + CPLAssert(m_nMapInfoType == poObjHdr->m_nType); + + /*----------------------------------------------------------------- + * Fetch and validate geometry and update MBR + * Note that we will simply use the geometry's MBR and don't really + * read the polygon geometry... this should be OK unless the + * polygon geometry was not really a rectangle. + *----------------------------------------------------------------*/ + if (UpdateMBR(poMapFile) != 0) + return -1; /* Error already reported */ + + /*----------------------------------------------------------------- + * Copy object information + *----------------------------------------------------------------*/ + TABMAPObjRectEllipse *poRectHdr = (TABMAPObjRectEllipse *)poObjHdr; + + if (m_nMapInfoType == TAB_GEOM_ROUNDRECT || + m_nMapInfoType == TAB_GEOM_ROUNDRECT_C) + { + poMapFile->Coordsys2IntDist(m_dRoundXRadius*2.0, m_dRoundYRadius*2.0, + poRectHdr->m_nCornerWidth, + poRectHdr->m_nCornerHeight); + } + else + { + poRectHdr->m_nCornerWidth = poRectHdr->m_nCornerHeight = 0; + } + + // A rectangle is defined by its MBR (values were set in UpdateMBR()) + poRectHdr->m_nMinX = m_nXMin; + poRectHdr->m_nMinY = m_nYMin; + poRectHdr->m_nMaxX = m_nXMax; + poRectHdr->m_nMaxY = m_nYMax; + + m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef); + poRectHdr->m_nPenId = (GByte)m_nPenDefIndex; // Pen index + + m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef); + poRectHdr->m_nBrushId = (GByte)m_nBrushDefIndex; // Brush index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABRectangle::GetStyleString() + * + * Return style string for this feature. + * + * Style String is built only once during the first call to GetStyleString(). + **********************************************************************/ +const char *TABRectangle::GetStyleString() +{ + if (m_pszStyleString == NULL) + { + // Since GetPen/BrushStyleString() use CPLSPrintf(), we need + // to use temporary buffers + char *pszPen = CPLStrdup(GetPenStyleString()); + char *pszBrush = CPLStrdup(GetBrushStyleString()); + + m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen)); + + CPLFree(pszPen); + CPLFree(pszBrush); + } + + return m_pszStyleString; +} + +/********************************************************************** + * TABRectangle::DumpMIF() + * + * Dump feature geometry in a format similar to .MIF REGIONs. + **********************************************************************/ +void TABRectangle::DumpMIF(FILE *fpOut /*=NULL*/) +{ + OGRGeometry *poGeom; + OGRPolygon *poPolygon = NULL; + int i, numPoints; + + if (fpOut == NULL) + fpOut = stdout; + + /*----------------------------------------------------------------- + * Output RECT or ROUNDRECT parameters + *----------------------------------------------------------------*/ + double dXMin, dYMin, dXMax, dYMax; + GetMBR(dXMin, dYMin, dXMax, dYMax); + if (m_bRoundCorners) + fprintf(fpOut, "(ROUNDRECT %.15g %.15g %.15g %.15g %.15g %.15g)\n", + dXMin, dYMin, dXMax, dYMax, + m_dRoundXRadius, m_dRoundYRadius); + else + fprintf(fpOut, "(RECT %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax, dYMax); + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) + { + /*------------------------------------------------------------- + * Generate rectangle output as a region + * We could also output as a RECT or ROUNDRECT in a real MIF generator + *------------------------------------------------------------*/ + int iRing, numIntRings; + poPolygon = (OGRPolygon*)poGeom; + numIntRings = poPolygon->getNumInteriorRings(); + fprintf(fpOut, "REGION %d\n", numIntRings+1); + // In this loop, iRing=-1 for the outer ring. + for(iRing=-1; iRing < numIntRings; iRing++) + { + OGRLinearRing *poRing; + + if (iRing == -1) + poRing = poPolygon->getExteriorRing(); + else + poRing = poPolygon->getInteriorRing(iRing); + + if (poRing == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRectangle: Object Geometry contains NULL rings!"); + return; + } + + numPoints = poRing->getNumPoints(); + fprintf(fpOut, " %d\n", numPoints); + for(i=0; igetX(i),poRing->getY(i)); + } + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRectangle: Missing or Invalid Geometry!"); + return; + } + + // Finish with PEN/BRUSH/etc. clauses + DumpPenDef(); + DumpBrushDef(); + + fflush(fpOut); +} + + +/*===================================================================== + * class TABEllipse + *====================================================================*/ + +/********************************************************************** + * TABEllipse::TABEllipse() + * + * Constructor. + **********************************************************************/ +TABEllipse::TABEllipse(OGRFeatureDefn *poDefnIn): + TABFeature(poDefnIn) +{ +} + +/********************************************************************** + * TABEllipse::~TABEllipse() + * + * Destructor. + **********************************************************************/ +TABEllipse::~TABEllipse() +{ +} + +/********************************************************************** + * TABEllipse::CloneTABFeature() + * + * Duplicate feature, including stuff specific to each TABFeature type. + * + * This method calls the generic TABFeature::CopyTABFeatureBase() and + * then copies any members specific to its own type. + **********************************************************************/ +TABFeature *TABEllipse::CloneTABFeature(OGRFeatureDefn *poNewDefn/*=NULL*/) +{ + /*----------------------------------------------------------------- + * Alloc new feature and copy the base stuff + *----------------------------------------------------------------*/ + TABEllipse *poNew = new TABEllipse(poNewDefn ? poNewDefn : + GetDefnRef()); + + CopyTABFeatureBase(poNew); + + /*----------------------------------------------------------------- + * And members specific to this class + *----------------------------------------------------------------*/ + // ITABFeaturePen + *(poNew->GetPenDefRef()) = *GetPenDefRef(); + + // ITABFeatureBrush + *(poNew->GetBrushDefRef()) = *GetBrushDefRef(); + + poNew->m_dCenterX = m_dCenterX; + poNew->m_dCenterY = m_dCenterY; + poNew->m_dXRadius = m_dXRadius; + poNew->m_dYRadius = m_dYRadius; + + return poNew; +} + +/********************************************************************** + * TABEllipse::ValidateMapInfoType() + * + * Check the feature's geometry part and return the corresponding + * mapinfo object type code. The m_nMapInfoType member will also + * be updated for further calls to GetMapInfoType(); + * + * Returns TAB_GEOM_NONE if the geometry is not compatible with what + * is expected for this object class. + **********************************************************************/ +TABGeomType TABEllipse::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if ( (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) || + (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) ) + { + m_nMapInfoType = TAB_GEOM_ELLIPSE; + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABEllipse: Missing or Invalid Geometry!"); + m_nMapInfoType = TAB_GEOM_NONE; + } + + /*----------------------------------------------------------------- + * Decide if coordinates should be compressed or not. + *----------------------------------------------------------------*/ + // __TODO__ For now we always write uncompressed for this class... + // ValidateCoordType(poMapFile); + UpdateMBR(poMapFile); + + return m_nMapInfoType; +} + +/********************************************************************** + * TABEllipse::UpdateMBR() + * + * Update the feature MBR members using the geometry + * + * Returns 0 on success, or -1 if there is no geometry in object + **********************************************************************/ +int TABEllipse::UpdateMBR(TABMAPFile * poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + OGREnvelope sEnvelope; + + /*----------------------------------------------------------------- + * Fetch and validate geometry... Polygon and point are accepted. + * Note that we will simply use the ellipse's MBR and don't really + * read the polygon geometry... this should be OK unless the + * polygon geometry was not really an ellipse. + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if ( (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) || + (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) ) + poGeom->getEnvelope(&sEnvelope); + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABEllipse: Missing or Invalid Geometry!"); + return -1; + } + + /*----------------------------------------------------------------- + * We use the center of the MBR as the ellipse center, and the + * X/Y radius to define the MBR size. If X/Y radius are null then + * we'll try to use the MBR to recompute them. + *----------------------------------------------------------------*/ + double dXCenter, dYCenter; + dXCenter = (sEnvelope.MaxX + sEnvelope.MinX)/2.0; + dYCenter = (sEnvelope.MaxY + sEnvelope.MinY)/2.0; + if (m_dXRadius == 0.0 && m_dYRadius == 0.0) + { + m_dXRadius = ABS(sEnvelope.MaxX - sEnvelope.MinX) / 2.0; + m_dYRadius = ABS(sEnvelope.MaxY - sEnvelope.MinY) / 2.0; + } + + m_dXMin = dXCenter - m_dXRadius; + m_dYMin = dYCenter - m_dYRadius; + m_dXMax = dXCenter + m_dXRadius; + m_dYMax = dYCenter + m_dYRadius; + + if (poMapFile) + { + poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin); + poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax); + } + + return 0; +} + +/********************************************************************** + * TABEllipse::ReadGeometryFromMAPFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to the beginning of + * a map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABEllipse::ReadGeometryFromMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + double dXMin, dYMin, dXMax, dYMax; + OGRPolygon *poPolygon; + OGRLinearRing *poRing; + + /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */ + if (bCoordBlockDataOnly) + return 0; + + /*----------------------------------------------------------------- + * Fetch and validate geometry type + *----------------------------------------------------------------*/ + m_nMapInfoType = poObjHdr->m_nType; + + if (m_nMapInfoType != TAB_GEOM_ELLIPSE && + m_nMapInfoType != TAB_GEOM_ELLIPSE_C ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)", + m_nMapInfoType, m_nMapInfoType); + return -1; + } + + /*----------------------------------------------------------------- + * Read object information + *----------------------------------------------------------------*/ + TABMAPObjRectEllipse *poRectHdr = (TABMAPObjRectEllipse *)poObjHdr; + + // An ellipse is defined by its MBR + + poMapFile->Int2Coordsys(poRectHdr->m_nMinX, poRectHdr->m_nMinY, + dXMin, dYMin); + poMapFile->Int2Coordsys(poRectHdr->m_nMaxX, poRectHdr->m_nMaxY, + dXMax, dYMax); + + m_nPenDefIndex = poRectHdr->m_nPenId; // Pen index + poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef); + + m_nBrushDefIndex = poRectHdr->m_nBrushId; // Brush index + poMapFile->ReadBrushDef(m_nBrushDefIndex, &m_sBrushDef); + + /*----------------------------------------------------------------- + * Save info about the ellipse def. inside class members + *----------------------------------------------------------------*/ + m_dCenterX = (dXMin + dXMax) / 2.0; + m_dCenterY = (dYMin + dYMax) / 2.0; + m_dXRadius = ABS( (dXMax - dXMin) / 2.0 ); + m_dYRadius = ABS( (dYMax - dYMin) / 2.0 ); + + SetMBR(dXMin, dYMin, dXMax, dYMax); + + SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + + /*----------------------------------------------------------------- + * Create and fill geometry object + *----------------------------------------------------------------*/ + poPolygon = new OGRPolygon; + poRing = new OGRLinearRing(); + + + /*----------------------------------------------------------------- + * For the OGR geometry, we generate an ellipse with 2 degrees line + * segments. + *----------------------------------------------------------------*/ + TABGenerateArc(poRing, 180, + m_dCenterX, m_dCenterY, + m_dXRadius, m_dYRadius, + 0.0, 2.0*PI); + TABCloseRing(poRing); + + poPolygon->addRingDirectly(poRing); + SetGeometryDirectly(poPolygon); + + return 0; +} + +/********************************************************************** + * TABEllipse::WriteGeometryToMAPFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to a valid map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABEllipse::WriteGeometryToMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */ + if (bCoordBlockDataOnly) + return 0; + + /*----------------------------------------------------------------- + * We assume that ValidateMapInfoType() was called already and that + * the type in poObjHdr->m_nType is valid. + *----------------------------------------------------------------*/ + CPLAssert(m_nMapInfoType == poObjHdr->m_nType); + + /*----------------------------------------------------------------- + * Fetch and validate geometry... Polygon and point are accepted. + * Note that we will simply use the ellipse's MBR and don't really + * read the polygon geometry... this should be OK unless the + * polygon geometry was not really an ellipse. + * + * We use the center of the MBR as the ellipse center, and the + * X/Y radius to define the MBR size. If X/Y radius are null then + * we'll try to use the MBR to recompute them. + *----------------------------------------------------------------*/ + if (UpdateMBR(poMapFile) != 0) + return -1; /* Error already reported */ + + /*----------------------------------------------------------------- + * Copy object information + *----------------------------------------------------------------*/ + TABMAPObjRectEllipse *poRectHdr = (TABMAPObjRectEllipse *)poObjHdr; + + // Reset RoundRect Corner members... just in case (unused for ellipse) + poRectHdr->m_nCornerWidth = poRectHdr->m_nCornerHeight = 0; + + // An ellipse is defined by its MBR (values were set in UpdateMBR()) + poRectHdr->m_nMinX = m_nXMin; + poRectHdr->m_nMinY = m_nYMin; + poRectHdr->m_nMaxX = m_nXMax; + poRectHdr->m_nMaxY = m_nYMax; + + m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef); + poRectHdr->m_nPenId = (GByte)m_nPenDefIndex; // Pen index + + m_nBrushDefIndex = poMapFile->WriteBrushDef(&m_sBrushDef); + poRectHdr->m_nBrushId = (GByte)m_nBrushDefIndex; // Brush index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABEllipse::GetStyleString() + * + * Return style string for this feature. + * + * Style String is built only once during the first call to GetStyleString(). + **********************************************************************/ +const char *TABEllipse::GetStyleString() +{ + if (m_pszStyleString == NULL) + { + // Since GetPen/BrushStyleString() use CPLSPrintf(), we need + // to use temporary buffers + char *pszPen = CPLStrdup(GetPenStyleString()); + char *pszBrush = CPLStrdup(GetBrushStyleString()); + + m_pszStyleString = CPLStrdup(CPLSPrintf("%s;%s", pszBrush, pszPen)); + + CPLFree(pszPen); + CPLFree(pszBrush); + } + + return m_pszStyleString; +} + + +/********************************************************************** + * TABEllipse::DumpMIF() + * + * Dump feature geometry in a format similar to .MIF REGIONs. + **********************************************************************/ +void TABEllipse::DumpMIF(FILE *fpOut /*=NULL*/) +{ + OGRGeometry *poGeom; + OGRPolygon *poPolygon = NULL; + int i, numPoints; + + if (fpOut == NULL) + fpOut = stdout; + + /*----------------------------------------------------------------- + * Output ELLIPSE parameters + *----------------------------------------------------------------*/ + double dXMin, dYMin, dXMax, dYMax; + GetMBR(dXMin, dYMin, dXMax, dYMax); + fprintf(fpOut, "(ELLIPSE %.15g %.15g %.15g %.15g)\n", dXMin, dYMin, dXMax, dYMax); + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) + { + /*------------------------------------------------------------- + * Generate ellipse output as a region + * We could also output as an ELLIPSE in a real MIF generator + *------------------------------------------------------------*/ + int iRing, numIntRings; + poPolygon = (OGRPolygon*)poGeom; + numIntRings = poPolygon->getNumInteriorRings(); + fprintf(fpOut, "REGION %d\n", numIntRings+1); + // In this loop, iRing=-1 for the outer ring. + for(iRing=-1; iRing < numIntRings; iRing++) + { + OGRLinearRing *poRing; + + if (iRing == -1) + poRing = poPolygon->getExteriorRing(); + else + poRing = poPolygon->getInteriorRing(iRing); + + if (poRing == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABEllipse: Object Geometry contains NULL rings!"); + return; + } + + numPoints = poRing->getNumPoints(); + fprintf(fpOut, " %d\n", numPoints); + for(i=0; igetX(i),poRing->getY(i)); + } + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABEllipse: Missing or Invalid Geometry!"); + return; + } + + // Finish with PEN/BRUSH/etc. clauses + DumpPenDef(); + DumpBrushDef(); + + fflush(fpOut); +} + + +/*===================================================================== + * class TABArc + *====================================================================*/ + +/********************************************************************** + * TABArc::TABArc() + * + * Constructor. + **********************************************************************/ +TABArc::TABArc(OGRFeatureDefn *poDefnIn): + TABFeature(poDefnIn) +{ + m_dStartAngle = m_dEndAngle = 0.0; + m_dCenterX = m_dCenterY = m_dXRadius = m_dYRadius = 0.0; + +} + +/********************************************************************** + * TABArc::~TABArc() + * + * Destructor. + **********************************************************************/ +TABArc::~TABArc() +{ +} + +/********************************************************************** + * TABArc::CloneTABFeature() + * + * Duplicate feature, including stuff specific to each TABFeature type. + * + * This method calls the generic TABFeature::CopyTABFeatureBase() and + * then copies any members specific to its own type. + **********************************************************************/ +TABFeature *TABArc::CloneTABFeature(OGRFeatureDefn *poNewDefn/*=NULL*/) +{ + /*----------------------------------------------------------------- + * Alloc new feature and copy the base stuff + *----------------------------------------------------------------*/ + TABArc *poNew = new TABArc(poNewDefn ? poNewDefn : GetDefnRef()); + + CopyTABFeatureBase(poNew); + + /*----------------------------------------------------------------- + * And members specific to this class + *----------------------------------------------------------------*/ + // ITABFeaturePen + *(poNew->GetPenDefRef()) = *GetPenDefRef(); + + poNew->SetStartAngle( GetStartAngle() ); + poNew->SetEndAngle( GetEndAngle() ); + + poNew->m_dCenterX = m_dCenterX; + poNew->m_dCenterY = m_dCenterY; + poNew->m_dXRadius = m_dXRadius; + poNew->m_dYRadius = m_dYRadius; + + return poNew; +} + +/********************************************************************** + * TABArc::ValidateMapInfoType() + * + * Check the feature's geometry part and return the corresponding + * mapinfo object type code. The m_nMapInfoType member will also + * be updated for further calls to GetMapInfoType(); + * + * Returns TAB_GEOM_NONE if the geometry is not compatible with what + * is expected for this object class. + **********************************************************************/ +TABGeomType TABArc::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if ( (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) || + (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) ) + { + m_nMapInfoType = TAB_GEOM_ARC; + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABArc: Missing or Invalid Geometry!"); + m_nMapInfoType = TAB_GEOM_NONE; + } + + /*----------------------------------------------------------------- + * Decide if coordinates should be compressed or not. + *----------------------------------------------------------------*/ + // __TODO__ For now we always write uncompressed for this class... + // ValidateCoordType(poMapFile); + UpdateMBR(poMapFile); + + return m_nMapInfoType; +} + +/********************************************************************** + * TABArc::UpdateMBR() + * + * Update the feature MBR members using the geometry + * + * Returns 0 on success, or -1 if there is no geometry in object + **********************************************************************/ +int TABArc::UpdateMBR(TABMAPFile * poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + OGREnvelope sEnvelope; + + poGeom = GetGeometryRef(); + if ( (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) ) + { + /*------------------------------------------------------------- + * POLYGON geometry: + * Note that we will simply use the ellipse's MBR and don't really + * read the polygon geometry... this should be OK unless the + * polygon geometry was not really an ellipse. + * In the case of a polygon geometry. the m_dCenterX/Y values MUST + * have been set by the caller. + *------------------------------------------------------------*/ + poGeom->getEnvelope(&sEnvelope); + } + else if ( (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) ) + { + /*------------------------------------------------------------- + * In the case of a POINT GEOMETRY, we will make sure the the + * feature's m_dCenterX/Y are in sync with the point's X,Y coords. + * + * In this case we have to reconstruct the arc inside a temporary + * geometry object in order to find its real MBR. + *------------------------------------------------------------*/ + OGRPoint *poPoint = (OGRPoint *)poGeom; + m_dCenterX = poPoint->getX(); + m_dCenterY = poPoint->getY(); + + OGRLineString oTmpLine; + int numPts=0; + if (m_dEndAngle < m_dStartAngle) + numPts = (int) ABS( ((m_dEndAngle+360)-m_dStartAngle)/2 ) + 1; + else + numPts = (int) ABS( (m_dEndAngle-m_dStartAngle)/2 ) + 1; + numPts = MAX(2, numPts); + + TABGenerateArc(&oTmpLine, numPts, + m_dCenterX, m_dCenterY, + m_dXRadius, m_dYRadius, + m_dStartAngle*PI/180.0, m_dEndAngle*PI/180.0); + + oTmpLine.getEnvelope(&sEnvelope); + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABArc: Missing or Invalid Geometry!"); + return -1; + } + + // Update the Arc's MBR + m_dXMin = sEnvelope.MinX; + m_dYMin = sEnvelope.MinY; + m_dXMax = sEnvelope.MaxX; + m_dYMax = sEnvelope.MaxY; + + if (poMapFile) + { + poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin); + poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax); + } + + return 0; +} + +/********************************************************************** + * TABArc::ReadGeometryFromMAPFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to the beginning of + * a map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABArc::ReadGeometryFromMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + double dXMin, dYMin, dXMax, dYMax; + OGRLineString *poLine; + int numPts; + + /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */ + if (bCoordBlockDataOnly) + return 0; + + /*----------------------------------------------------------------- + * Fetch and validate geometry type + *----------------------------------------------------------------*/ + m_nMapInfoType = poObjHdr->m_nType; + + if (m_nMapInfoType != TAB_GEOM_ARC && + m_nMapInfoType != TAB_GEOM_ARC_C ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)", + m_nMapInfoType, m_nMapInfoType); + return -1; + } + + + /*----------------------------------------------------------------- + * Read object information + *----------------------------------------------------------------*/ + TABMAPObjArc *poArcHdr = (TABMAPObjArc *)poObjHdr; + + /*------------------------------------------------------------- + * Start/End angles + * Since the angles are specified for integer coordinates, and + * that these coordinates can have the X axis reversed, we have to + * adjust the angle values for the change in the X axis + * direction. + * + * This should be necessary only when X axis is flipped. + * __TODO__ Why is order of start/end values reversed as well??? + *------------------------------------------------------------*/ + + /*------------------------------------------------------------- + * OK, Arc angles again!!!!!!!!!!!! + * After some tests in 1999-11, it appeared that the angle values + * ALWAYS had to be flipped (read order= end angle followed by + * start angle), no matter which quadrant the file is in. + * This does not make any sense, so I suspect that there is something + * that we are missing here! + * + * 2000-01-14.... Again!!! Based on some sample data files: + * File Ver Quadr ReflXAxis Read_Order Adjust_Angle + * test_symb.tab 300 2 1 end,start X=yes Y=no + * alltypes.tab: 300 1 0 start,end X=no Y=no + * arcs.tab: 300 2 0 end,start X=yes Y=no + * + * Until we prove it wrong, the rule would be: + * -> Quadrant 1 and 3, angles order = start, end + * -> Quadrant 2 and 4, angles order = end, start + * + Always adjust angles for x and y axis based on quadrant. + * + * This was confirmed using some more files in which the quadrant was + * manually changed, but whether these are valid results is + * discutable. + * + * The ReflectXAxis flag seems to have no effect here... + *------------------------------------------------------------*/ + + /*------------------------------------------------------------- + * In version 100 .tab files (version 400 .map), it is possible + * to have a quadrant value of 0 and it should be treated the + * same way as quadrant 3 + *------------------------------------------------------------*/ + if ( poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant==1 || + poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant==3 || + poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant==0 ) + { + // Quadrants 1 and 3 ... read order = start, end + m_dStartAngle = poArcHdr->m_nStartAngle/10.0; + m_dEndAngle = poArcHdr->m_nEndAngle/10.0; + } + else + { + // Quadrants 2 and 4 ... read order = end, start + m_dStartAngle = poArcHdr->m_nEndAngle/10.0; + m_dEndAngle = poArcHdr->m_nStartAngle/10.0; + } + + if ( poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant==2 || + poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant==3 || + poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant==0 ) + { + // X axis direction is flipped... adjust angle + m_dStartAngle = (m_dStartAngle<=180.0) ? (180.0-m_dStartAngle): + (540.0-m_dStartAngle); + m_dEndAngle = (m_dEndAngle<=180.0) ? (180.0-m_dEndAngle): + (540.0-m_dEndAngle); + } + + if (poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant==3 || + poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant==4 || + poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant==0 ) + { + // Y axis direction is flipped... this reverses angle direction + // Unfortunately we never found any file that contains this case, + // but this should be the behavior to expect!!! + // + // 2000-01-14: some files in which quadrant was set to 3 and 4 + // manually seemed to confirm that this is the right thing to do. + m_dStartAngle = 360.0 - m_dStartAngle; + m_dEndAngle = 360.0 - m_dEndAngle; + } + + // An arc is defined by its defining ellipse's MBR: + + poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMinX, + poArcHdr->m_nArcEllipseMinY , dXMin, dYMin); + poMapFile->Int2Coordsys(poArcHdr->m_nArcEllipseMaxX, + poArcHdr->m_nArcEllipseMaxY , dXMax, dYMax); + + m_dCenterX = (dXMin + dXMax) / 2.0; + m_dCenterY = (dYMin + dYMax) / 2.0; + m_dXRadius = ABS( (dXMax - dXMin) / 2.0 ); + m_dYRadius = ABS( (dYMax - dYMin) / 2.0 ); + + // Read the Arc's MBR and use that as this feature's MBR + poMapFile->Int2Coordsys(poArcHdr->m_nMinX, poArcHdr->m_nMinY, + dXMin, dYMin); + poMapFile->Int2Coordsys(poArcHdr->m_nMaxX, poArcHdr->m_nMaxY, + dXMax, dYMax); + SetMBR(dXMin, dYMin, dXMax, dYMax); + + m_nPenDefIndex = poArcHdr->m_nPenId; // Pen index + poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef); + + + /*----------------------------------------------------------------- + * Create and fill geometry object + * For the OGR geometry, we generate an arc with 2 degrees line + * segments. + *----------------------------------------------------------------*/ + poLine = new OGRLineString; + + if (m_dEndAngle < m_dStartAngle) + numPts = (int) ABS( ((m_dEndAngle+360.0)-m_dStartAngle)/2.0 ) + 1; + else + numPts = (int) ABS( (m_dEndAngle-m_dStartAngle)/2.0 ) + 1; + numPts = MAX(2, numPts); + + TABGenerateArc(poLine, numPts, + m_dCenterX, m_dCenterY, + m_dXRadius, m_dYRadius, + m_dStartAngle*PI/180.0, m_dEndAngle*PI/180.0); + + SetGeometryDirectly(poLine); + + return 0; +} + +/********************************************************************** + * TABArc::WriteGeometryToMAPFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to a valid map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABArc::WriteGeometryToMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + /* Nothing to do for bCoordBlockDataOnly (used by index splitting) */ + if (bCoordBlockDataOnly) + return 0; + + /*----------------------------------------------------------------- + * We assume that ValidateMapInfoType() was called already and that + * the type in poObjHdr->m_nType is valid. + *----------------------------------------------------------------*/ + CPLAssert(m_nMapInfoType == poObjHdr->m_nType); + + /*----------------------------------------------------------------- + * Fetch and validate geometry + * In the case of ARCs, this is all done inside UpdateMBR() + *----------------------------------------------------------------*/ + if (UpdateMBR(poMapFile) != 0) + return -1; /* Error already reported */ + + /*----------------------------------------------------------------- + * Copy object information + *----------------------------------------------------------------*/ + TABMAPObjArc *poArcHdr = (TABMAPObjArc *)poObjHdr; + + /*------------------------------------------------------------- + * Start/End angles + * Since we ALWAYS produce files in quadrant 1 then we can + * ignore the special angle conversion required by flipped axis. + * + * See the notes about Arc angles in TABArc::ReadGeometryFromMAPFile() + *------------------------------------------------------------*/ + CPLAssert(poMapFile->GetHeaderBlock()->m_nCoordOriginQuadrant == 1); + + poArcHdr->m_nStartAngle = ROUND_INT(m_dStartAngle*10.0); + poArcHdr->m_nEndAngle = ROUND_INT(m_dEndAngle*10.0); + + // An arc is defined by its defining ellipse's MBR: + poMapFile->Coordsys2Int(m_dCenterX-m_dXRadius, m_dCenterY-m_dYRadius, + poArcHdr->m_nArcEllipseMinX, + poArcHdr->m_nArcEllipseMinY); + poMapFile->Coordsys2Int(m_dCenterX+m_dXRadius, m_dCenterY+m_dYRadius, + poArcHdr->m_nArcEllipseMaxX, + poArcHdr->m_nArcEllipseMaxY); + + // Pass the Arc's actual MBR (values were set in UpdateMBR()) + poArcHdr->m_nMinX = m_nXMin; + poArcHdr->m_nMinY = m_nYMin; + poArcHdr->m_nMaxX = m_nXMax; + poArcHdr->m_nMaxY = m_nYMax; + + m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef); + poArcHdr->m_nPenId = (GByte)m_nPenDefIndex; // Pen index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABArc::SetStart/EndAngle() + * + * Set the start/end angle values in degrees, making sure the values are + * always in the range [0..360] + **********************************************************************/ +void TABArc::SetStartAngle(double dAngle) +{ + while(dAngle < 0.0) dAngle += 360.0; + while(dAngle > 360.0) dAngle -= 360.0; + + m_dStartAngle = dAngle; +} + +void TABArc::SetEndAngle(double dAngle) +{ + while(dAngle < 0.0) dAngle += 360.0; + while(dAngle > 360.0) dAngle -= 360.0; + + m_dEndAngle = dAngle; +} + + +/********************************************************************** + * TABArc::GetStyleString() + * + * Return style string for this feature. + * + * Style String is built only once during the first call to GetStyleString(). + **********************************************************************/ +const char *TABArc::GetStyleString() +{ + if (m_pszStyleString == NULL) + { + m_pszStyleString = CPLStrdup(GetPenStyleString()); + } + + return m_pszStyleString; +} + +/********************************************************************** + * TABArc::DumpMIF() + * + * Dump feature geometry in a format similar to .MIF REGIONs. + **********************************************************************/ +void TABArc::DumpMIF(FILE *fpOut /*=NULL*/) +{ + OGRGeometry *poGeom; + OGRLineString *poLine = NULL; + int i, numPoints; + + if (fpOut == NULL) + fpOut = stdout; + + /*----------------------------------------------------------------- + * Output ARC parameters + *----------------------------------------------------------------*/ + fprintf(fpOut, "(ARC %.15g %.15g %.15g %.15g %d %d)\n", + m_dCenterX - m_dXRadius, m_dCenterY - m_dYRadius, + m_dCenterX + m_dXRadius, m_dCenterY + m_dYRadius, + (int)m_dStartAngle, (int)m_dEndAngle); + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) + { + /*------------------------------------------------------------- + * Generate arc output as a simple polyline + * We could also output as an ELLIPSE in a real MIF generator + *------------------------------------------------------------*/ + poLine = (OGRLineString*)poGeom; + numPoints = poLine->getNumPoints(); + fprintf(fpOut, "PLINE %d\n", numPoints); + for(i=0; igetX(i), poLine->getY(i)); + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABArc: Missing or Invalid Geometry!"); + return; + } + + // Finish with PEN/BRUSH/etc. clauses + DumpPenDef(); + + fflush(fpOut); +} + + + +/*===================================================================== + * class TABText + *====================================================================*/ + +/********************************************************************** + * TABText::TABText() + * + * Constructor. + **********************************************************************/ +TABText::TABText(OGRFeatureDefn *poDefnIn): + TABFeature(poDefnIn) +{ + m_pszString = NULL; + + m_dAngle = m_dHeight = 0.0; + m_dfLineEndX = m_dfLineEndY = 0.0; + m_bLineEndSet = FALSE; + + m_rgbForeground = 0x000000; + m_rgbBackground = 0xffffff; + m_rgbOutline = 0xffffff; + m_rgbShadow = 0x808080; + + m_nTextAlignment = 0; + m_nFontStyle = 0; + m_dWidth = 0; +} + +/********************************************************************** + * TABText::~TABText() + * + * Destructor. + **********************************************************************/ +TABText::~TABText() +{ + CPLFree(m_pszString); +} + +/********************************************************************** + * TABText::CloneTABFeature() + * + * Duplicate feature, including stuff specific to each TABFeature type. + * + * This method calls the generic TABFeature::CopyTABFeatureBase() and + * then copies any members specific to its own type. + **********************************************************************/ +TABFeature *TABText::CloneTABFeature(OGRFeatureDefn *poNewDefn/*=NULL*/) +{ + /*----------------------------------------------------------------- + * Alloc new feature and copy the base stuff + *----------------------------------------------------------------*/ + TABText *poNew = new TABText(poNewDefn ? poNewDefn : GetDefnRef()); + + CopyTABFeatureBase(poNew); + + /*----------------------------------------------------------------- + * And members specific to this class + *----------------------------------------------------------------*/ + // ITABFeaturePen + *(poNew->GetPenDefRef()) = *GetPenDefRef(); + + // ITABFeatureFont + *(poNew->GetFontDefRef()) = *GetFontDefRef(); + + + poNew->SetTextString( GetTextString() ); + poNew->SetTextAngle( GetTextAngle() ); + poNew->SetTextBoxHeight( GetTextBoxHeight() ); + poNew->SetTextBoxWidth( GetTextBoxWidth() ); + poNew->SetFontStyleTABValue( GetFontStyleTABValue() ); + poNew->SetFontBGColor( GetFontBGColor() ); + poNew->SetFontFGColor( GetFontFGColor() ); + poNew->SetFontOColor( GetFontOColor() ); + poNew->SetFontSColor( GetFontSColor() ); + + poNew->SetTextJustification( GetTextJustification() ); + poNew->SetTextSpacing( GetTextSpacing() ); + // Note: Text arrow/line coordinates are not transported... but + // we ignore them most of the time anyways. + poNew->SetTextLineType( TABTLNoLine ); + + return poNew; +} + +/********************************************************************** + * TABText::ValidateMapInfoType() + * + * Check the feature's geometry part and return the corresponding + * mapinfo object type code. The m_nMapInfoType member will also + * be updated for further calls to GetMapInfoType(); + * + * Returns TAB_GEOM_NONE if the geometry is not compatible with what + * is expected for this object class. + **********************************************************************/ +TABGeomType TABText::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + { + m_nMapInfoType = TAB_GEOM_TEXT; + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABText: Missing or Invalid Geometry!"); + m_nMapInfoType = TAB_GEOM_NONE; + } + + /*----------------------------------------------------------------- + * Decide if coordinates should be compressed or not. + *----------------------------------------------------------------*/ + // __TODO__ For now we always write uncompressed for this class... + // ValidateCoordType(poMapFile); + UpdateMBR(poMapFile); + + return m_nMapInfoType; +} + +/********************************************************************** + * TABText::ReadGeometryFromMAPFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to the beginning of + * a map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABText::ReadGeometryFromMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock **ppoCoordBlock/*=NULL*/) +{ + double dXMin, dYMin, dXMax, dYMax; + OGRGeometry *poGeometry; + + /*----------------------------------------------------------------- + * Fetch and validate geometry type + *----------------------------------------------------------------*/ + m_nMapInfoType = poObjHdr->m_nType; + + if (m_nMapInfoType != TAB_GEOM_TEXT && + m_nMapInfoType != TAB_GEOM_TEXT_C ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)", + m_nMapInfoType, m_nMapInfoType); + return -1; + } + + /*============================================================= + * TEXT + *============================================================*/ + int nStringLen; + GInt32 nCoordBlockPtr; + double dJunk; + + /*----------------------------------------------------------------- + * Read object information + *----------------------------------------------------------------*/ + TABMAPObjText *poTextHdr = (TABMAPObjText *)poObjHdr; + + nCoordBlockPtr = poTextHdr->m_nCoordBlockPtr; // String position + nStringLen = poTextHdr->m_nCoordDataSize; // String length + m_nTextAlignment = poTextHdr->m_nTextAlignment; // just./spacing/arrow + + /*------------------------------------------------------------- + * Text Angle, in thenths of degree. + * Contrary to arc start/end angles, no conversion based on + * origin quadrant is required here + *------------------------------------------------------------*/ + m_dAngle = poTextHdr->m_nAngle/10.0; + + m_nFontStyle = poTextHdr->m_nFontStyle; // Font style + + m_rgbForeground = (poTextHdr->m_nFGColorR*256*256 + + poTextHdr->m_nFGColorG*256 + + poTextHdr->m_nFGColorB); + m_rgbBackground = (poTextHdr->m_nBGColorR*256*256 + + poTextHdr->m_nBGColorG*256 + + poTextHdr->m_nBGColorB); + m_rgbOutline = m_rgbBackground; + // In MapInfo, the shadow color is always gray (128,128,128) + m_rgbShadow = 0x808080; + + // arrow endpoint + poMapFile->Int2Coordsys(poTextHdr->m_nLineEndX, poTextHdr->m_nLineEndY, + m_dfLineEndX, m_dfLineEndY); + m_bLineEndSet = TRUE; + + // Text Height + poMapFile->Int2CoordsysDist(0, poTextHdr->m_nHeight, dJunk, m_dHeight); + + if (!bCoordBlockDataOnly) + { + m_nFontDefIndex = poTextHdr->m_nFontId; // Font name index + poMapFile->ReadFontDef(m_nFontDefIndex, &m_sFontDef); + } + + // MBR after rotation + poMapFile->Int2Coordsys(poTextHdr->m_nMinX, poTextHdr->m_nMinY, + dXMin, dYMin); + poMapFile->Int2Coordsys(poTextHdr->m_nMaxX, poTextHdr->m_nMaxY, + dXMax, dYMax); + + if (!bCoordBlockDataOnly) + { + m_nPenDefIndex = poTextHdr->m_nPenId; // Pen index for line + poMapFile->ReadPenDef(m_nPenDefIndex, &m_sPenDef); + } + + /*------------------------------------------------------------- + * Read text string from the coord. block + * Note that the string may contain binary '\n' and '\\' chars + * that we keep to an unescaped form internally. This is to + * be like OGR drivers. See bug 1107 for details. + *------------------------------------------------------------*/ + char *pszTmpString = (char*)CPLMalloc((nStringLen+1)*sizeof(char)); + + if (nStringLen > 0) + { + TABMAPCoordBlock *poCoordBlock; + CPLAssert(nCoordBlockPtr > 0); + if (ppoCoordBlock != NULL && *ppoCoordBlock != NULL) + poCoordBlock = *ppoCoordBlock; + else + poCoordBlock = poMapFile->GetCoordBlock(nCoordBlockPtr); + if (poCoordBlock == NULL || + poCoordBlock->ReadBytes(nStringLen,(GByte*)pszTmpString) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed reading text string at offset %d", + nCoordBlockPtr); + CPLFree(pszTmpString); + return -1; + } + + /* Return a ref to coord block so that caller can continue reading + * after the end of this object (used by index splitting) + */ + if (ppoCoordBlock) + *ppoCoordBlock = poCoordBlock; + } + + pszTmpString[nStringLen] = '\0'; + + CPLFree(m_pszString); + m_pszString = pszTmpString; // This string was Escaped before 20050714 + + + /* Set/retrieve the MBR to make sure Mins are smaller than Maxs + */ + SetMBR(dXMin, dYMin, dXMax, dYMax); + GetMBR(dXMin, dYMin, dXMax, dYMax); + + /* Copy int MBR to feature class members */ + SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + + /*----------------------------------------------------------------- + * Create an OGRPoint Geometry... + * The point X,Y values will be the coords of the lower-left corner before + * rotation is applied. (Note that the rotation in MapInfo is done around + * the upper-left corner) + * We need to calculate the true lower left corner of the text based + * on the MBR after rotation, the text height and the rotation angle. + *----------------------------------------------------------------*/ + double dCos, dSin, dX, dY; + dSin = sin(m_dAngle*PI/180.0); + dCos = cos(m_dAngle*PI/180.0); + if (dSin > 0.0 && dCos > 0.0) + { + dX = dXMin + m_dHeight * dSin; + dY = dYMin; + } + else if (dSin > 0.0 && dCos < 0.0) + { + dX = dXMax; + dY = dYMin - m_dHeight * dCos; + } + else if (dSin < 0.0 && dCos < 0.0) + { + dX = dXMax + m_dHeight * dSin; + dY = dYMax; + } + else // dSin < 0 && dCos > 0 + { + dX = dXMin; + dY = dYMax - m_dHeight * dCos; + } + + poGeometry = new OGRPoint(dX, dY); + + SetGeometryDirectly(poGeometry); + + /*----------------------------------------------------------------- + * Compute Text Width: the width of the Text MBR before rotation + * in ground units... unfortunately this value is not stored in the + * file, so we have to compute it with the MBR after rotation and + * the height of the MBR before rotation: + * With W = Width of MBR before rotation + * H = Height of MBR before rotation + * dX = Width of MBR after rotation + * dY = Height of MBR after rotation + * teta = rotation angle + * + * For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use: + * W = H * (dX - H * sin(teta)) / (H * cos(teta)) + * + * and for other teta values, use: + * W = H * (dY - H * cos(teta)) / (H * sin(teta)) + *----------------------------------------------------------------*/ + dSin = ABS(dSin); + dCos = ABS(dCos); + if (m_dHeight == 0.0) + m_dWidth = 0.0; + else if ( dCos > dSin ) + m_dWidth = m_dHeight * ((dXMax-dXMin) - m_dHeight*dSin) / + (m_dHeight*dCos); + else + m_dWidth = m_dHeight * ((dYMax-dYMin) - m_dHeight*dCos) / + (m_dHeight*dSin); + m_dWidth = ABS(m_dWidth); + + return 0; +} + +/********************************************************************** + * TABText::WriteGeometryToMAPFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to a valid map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABText::WriteGeometryToMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock **ppoCoordBlock/*=NULL*/) +{ + GInt32 nX, nY, nXMin, nYMin, nXMax, nYMax; + OGRGeometry *poGeom; + OGRPoint *poPoint; + GInt32 nCoordBlockPtr; + TABMAPCoordBlock *poCoordBlock; + int nStringLen; + + /*----------------------------------------------------------------- + * We assume that ValidateMapInfoType() was called already and that + * the type in poObjHdr->m_nType is valid. + *----------------------------------------------------------------*/ + CPLAssert(m_nMapInfoType == poObjHdr->m_nType); + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + poPoint = (OGRPoint*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABText: Missing or Invalid Geometry!"); + return -1; + } + + poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY); + + /*----------------------------------------------------------------- + * Write string to a coord block first... + * Note that the string may contain unescaped '\n' and '\\' + * that we have to keep like that for the MAP file. + * See MapTools bug 1107 for more details. + *----------------------------------------------------------------*/ + if (ppoCoordBlock != NULL && *ppoCoordBlock != NULL) + poCoordBlock = *ppoCoordBlock; + else + poCoordBlock = poMapFile->GetCurCoordBlock(); + poCoordBlock->StartNewFeature(); + nCoordBlockPtr = poCoordBlock->GetCurAddress(); + + // This string was escaped before 20050714 + char *pszTmpString = m_pszString; + + nStringLen = strlen(pszTmpString); + + if (nStringLen > 0) + { + poCoordBlock->WriteBytes(nStringLen, (GByte *)pszTmpString); + } + else + { + nCoordBlockPtr = 0; + } + + pszTmpString = NULL; + + /*----------------------------------------------------------------- + * Copy object information + *----------------------------------------------------------------*/ + TABMAPObjText *poTextHdr = (TABMAPObjText *)poObjHdr; + + poTextHdr->m_nCoordBlockPtr = nCoordBlockPtr; // String position + poTextHdr->m_nCoordDataSize = nStringLen; // String length + poTextHdr->m_nTextAlignment = m_nTextAlignment; // just./spacing/arrow + + /*----------------------------------------------------------------- + * Text Angle, (written in thenths of degrees) + * Contrary to arc start/end angles, no conversion based on + * origin quadrant is required here + *----------------------------------------------------------------*/ + poTextHdr->m_nAngle = ROUND_INT(m_dAngle*10.0); + + poTextHdr->m_nFontStyle = m_nFontStyle; // Font style/effect + + poTextHdr->m_nFGColorR = (GByte)COLOR_R(m_rgbForeground); + poTextHdr->m_nFGColorG = (GByte)COLOR_G(m_rgbForeground); + poTextHdr->m_nFGColorB = (GByte)COLOR_B(m_rgbForeground); + + poTextHdr->m_nBGColorR = (GByte)COLOR_R(m_rgbBackground); + poTextHdr->m_nBGColorG = (GByte)COLOR_G(m_rgbBackground); + poTextHdr->m_nBGColorB = (GByte)COLOR_B(m_rgbBackground); + + /*----------------------------------------------------------------- + * The OGRPoint's X,Y values were the coords of the lower-left corner + * before rotation was applied. (Note that the rotation in MapInfo is + * done around the upper-left corner) + * The Feature's MBR is the MBR of the text after rotation... that's + * what MapInfo uses to define the text location. + *----------------------------------------------------------------*/ + double dXMin, dYMin, dXMax, dYMax; + // Make sure Feature MBR is in sync with other params + + UpdateMBR(); + GetMBR(dXMin, dYMin, dXMax, dYMax); + + poMapFile->Coordsys2Int(dXMin, dYMin, nXMin, nYMin); + poMapFile->Coordsys2Int(dXMax, dYMax, nXMax, nYMax); + + // Label line end point + double dX, dY; + GetTextLineEndPoint(dX, dY); // Make sure a default line end point is set + poMapFile->Coordsys2Int(m_dfLineEndX, m_dfLineEndY, + poTextHdr->m_nLineEndX, poTextHdr->m_nLineEndY); + + // Text Height + poMapFile->Coordsys2IntDist(0.0, m_dHeight, nX, nY); + poTextHdr->m_nHeight = nY; + + if (!bCoordBlockDataOnly) + { + // Font name + m_nFontDefIndex = poMapFile->WriteFontDef(&m_sFontDef); + poTextHdr->m_nFontId = (GByte)m_nFontDefIndex; // Font name index + } + + // MBR after rotation + poTextHdr->SetMBR(nXMin, nYMin, nXMax, nYMax); + + if (!bCoordBlockDataOnly) + { + m_nPenDefIndex = poMapFile->WritePenDef(&m_sPenDef); + poTextHdr->m_nPenId = (GByte)m_nPenDefIndex; // Pen index for line/arrow + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + /* Return a ref to coord block so that caller can continue writing + * after the end of this object (used by index splitting) + */ + if (ppoCoordBlock) + *ppoCoordBlock = poCoordBlock; + + return 0; +} + + +/********************************************************************** + * TABText::GetTextString() + * + * Return ref to text string value. + * + * Returned string is a reference to the internal string buffer and should + * not be modified or freed by the caller. + **********************************************************************/ +const char *TABText::GetTextString() +{ + if (m_pszString == NULL) + return ""; + + return m_pszString; +} + +/********************************************************************** + * TABText::SetTextString() + * + * Set new text string value. + * + * Note: The text string may contain "\n" chars or "\\" chars + * and we expect to receive them in a 2 chars escaped form as + * described in the MIF format specs. + **********************************************************************/ +void TABText::SetTextString(const char *pszNewStr) +{ + CPLFree(m_pszString); + m_pszString = CPLStrdup(pszNewStr); +} + +/********************************************************************** + * TABText::GetTextAngle() + * + * Return text angle in degrees. + **********************************************************************/ +double TABText::GetTextAngle() +{ + return m_dAngle; +} + +void TABText::SetTextAngle(double dAngle) +{ + // Make sure angle is in the range [0..360] + while(dAngle < 0.0) dAngle += 360.0; + while(dAngle > 360.0) dAngle -= 360.0; + m_dAngle = dAngle; + UpdateMBR(); +} + +/********************************************************************** + * TABText::GetTextBoxHeight() + * + * Return text height in Y axis coord. units of the text box before rotation. + **********************************************************************/ +double TABText::GetTextBoxHeight() +{ + return m_dHeight; +} + +void TABText::SetTextBoxHeight(double dHeight) +{ + m_dHeight = dHeight; + UpdateMBR(); +} + +/********************************************************************** + * TABText::GetTextBoxWidth() + * + * Return text width in X axis coord. units. of the text box before rotation. + * + * If value has not been set, then we force a default value that assumes + * that one char's box width is 60% of its height... and we ignore + * the multiline case. This should not matter when the user PROPERLY sets + * the value. + **********************************************************************/ +double TABText::GetTextBoxWidth() +{ + if (m_dWidth == 0.0 && m_pszString) + { + m_dWidth = 0.6 * m_dHeight * strlen(m_pszString); + } + return m_dWidth; +} + +void TABText::SetTextBoxWidth(double dWidth) +{ + m_dWidth = dWidth; + UpdateMBR(); +} + +/********************************************************************** + * TABText::GetTextLineEndPoint() + * + * Return X,Y coordinates of the text label line end point. + * Default is the center of the text MBR. + **********************************************************************/ +void TABText::GetTextLineEndPoint(double &dX, double &dY) +{ + if (!m_bLineEndSet) + { + // Set default location at center of text MBR + double dXMin, dYMin, dXMax, dYMax; + UpdateMBR(); + GetMBR(dXMin, dYMin, dXMax, dYMax); + m_dfLineEndX = (dXMin + dXMax) /2.0; + m_dfLineEndY = (dYMin + dYMax) /2.0; + m_bLineEndSet = TRUE; + } + + // Return values + dX = m_dfLineEndX; + dY = m_dfLineEndY; +} + +void TABText::SetTextLineEndPoint(double dX, double dY) +{ + m_dfLineEndX = dX; + m_dfLineEndY = dY; + m_bLineEndSet = TRUE; +} + +/********************************************************************** + * TABText::UpdateMBR() + * + * Update the feature MBR using the text origin (OGRPoint geometry), the + * rotation angle, and the Width/height before rotation. + * + * This function cannot perform properly unless all the above have been set. + * + * Returns 0 on success, or -1 if there is no geometry in object + **********************************************************************/ +int TABText::UpdateMBR(TABMAPFile * poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + OGRPoint *poPoint=NULL; + + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + { + double dSin, dCos, dX0, dY0, dX1, dY1; + double dX[4], dY[4]; + poPoint = (OGRPoint *)poGeom; + + dX0 = poPoint->getX(); + dY0 = poPoint->getY(); + + dSin = sin(m_dAngle*PI/180.0); + dCos = cos(m_dAngle*PI/180.0); + + GetTextBoxWidth(); // Force default width value if necessary. + + dX[0] = dX0; + dY[0] = dY0; + dX[1] = dX0 + m_dWidth; + dY[1] = dY0; + dX[2] = dX0 + m_dWidth; + dY[2] = dY0 + m_dHeight; + dX[3] = dX0; + dY[3] = dY0 + m_dHeight; + + SetMBR(dX0, dY0, dX0, dY0); + for(int i=0; i<4; i++) + { + // Rotate one of the box corners + dX1 = dX0 + (dX[i]-dX0)*dCos - (dY[i]-dY0)*dSin; + dY1 = dY0 + (dX[i]-dX0)*dSin + (dY[i]-dY0)*dCos; + + // And update feature MBR with rotated coordinate + if (dX1 < m_dXMin) m_dXMin = dX1; + if (dX1 > m_dXMax) m_dXMax = dX1; + if (dY1 < m_dYMin) m_dYMin = dY1; + if (dY1 > m_dYMax) m_dYMax = dY1; + } + + if (poMapFile) + { + poMapFile->Coordsys2Int(m_dXMin, m_dYMin, m_nXMin, m_nYMin); + poMapFile->Coordsys2Int(m_dXMax, m_dYMax, m_nXMax, m_nYMax); + } + + return 0; + } + + return -1; +} + +/********************************************************************** + * TABText::GetFontBGColor() + * + * Return background color. + **********************************************************************/ +GInt32 TABText::GetFontBGColor() +{ + return m_rgbBackground; +} + +void TABText::SetFontBGColor(GInt32 rgbColor) +{ + m_rgbBackground = rgbColor; +} + +/********************************************************************** + * TABText::GetFontOColor() + * + * Return outline color. + **********************************************************************/ +GInt32 TABText::GetFontOColor() +{ + return m_rgbOutline; +} + +void TABText::SetFontOColor(GInt32 rgbColor) +{ + m_rgbOutline = rgbColor; +} + +/********************************************************************** + * TABText::GetFontSColor() + * + * Return shadow color. + **********************************************************************/ +GInt32 TABText::GetFontSColor() +{ + return m_rgbShadow; +} + +void TABText::SetFontSColor(GInt32 rgbColor) +{ + m_rgbShadow = rgbColor; +} + +/********************************************************************** + * TABText::GetFontFGColor() + * + * Return foreground color. + **********************************************************************/ +GInt32 TABText::GetFontFGColor() +{ + return m_rgbForeground; +} + +void TABText::SetFontFGColor(GInt32 rgbColor) +{ + m_rgbForeground = rgbColor; +} + +/********************************************************************** + * TABText::GetTextJustification() + * + * Return text justification. Default is TABTJLeft + **********************************************************************/ +TABTextJust TABText::GetTextJustification() +{ + TABTextJust eJust = TABTJLeft; + + if (m_nTextAlignment & 0x0200) + eJust = TABTJCenter; + else if (m_nTextAlignment & 0x0400) + eJust = TABTJRight; + + return eJust; +} + +void TABText::SetTextJustification(TABTextJust eJustification) +{ + // Flush current value... default is TABTJLeft + m_nTextAlignment &= ~ 0x0600; + // ... and set new one. + if (eJustification == TABTJCenter) + m_nTextAlignment |= 0x0200; + else if (eJustification == TABTJRight) + m_nTextAlignment |= 0x0400; +} + +/********************************************************************** + * TABText::GetTextSpacing() + * + * Return text vertical spacing factor. Default is TABTSSingle + **********************************************************************/ +TABTextSpacing TABText::GetTextSpacing() +{ + TABTextSpacing eSpacing = TABTSSingle; + + if (m_nTextAlignment & 0x0800) + eSpacing = TABTS1_5; + else if (m_nTextAlignment & 0x1000) + eSpacing = TABTSDouble; + + return eSpacing; +} + +void TABText::SetTextSpacing(TABTextSpacing eSpacing) +{ + // Flush current value... default is TABTSSingle + m_nTextAlignment &= ~ 0x1800; + // ... and set new one. + if (eSpacing == TABTS1_5) + m_nTextAlignment |= 0x0800; + else if (eSpacing == TABTSDouble) + m_nTextAlignment |= 0x1000; +} + +/********************************************************************** + * TABText::GetTextLineType() + * + * Return text line (arrow) type. Default is TABTLNoLine + **********************************************************************/ +TABTextLineType TABText::GetTextLineType() +{ + TABTextLineType eLine = TABTLNoLine; + + if (m_nTextAlignment & 0x2000) + eLine = TABTLSimple; + else if (m_nTextAlignment & 0x4000) + eLine = TABTLArrow; + + return eLine; +} + +void TABText::SetTextLineType(TABTextLineType eLineType) +{ + // Flush current value... default is TABTLNoLine + m_nTextAlignment &= ~ 0x6000; + // ... and set new one. + if (eLineType == TABTLSimple) + m_nTextAlignment |= 0x2000; + else if (eLineType == TABTLArrow) + m_nTextAlignment |= 0x4000; +} + +/********************************************************************** + * TABText::QueryFontStyle() + * + * Return TRUE if the specified font style attribute is turned ON, + * or FALSE otherwise. See enum TABFontStyle for the list of styles + * that can be queried on. + **********************************************************************/ +GBool TABText::QueryFontStyle(TABFontStyle eStyleToQuery) +{ + return (m_nFontStyle & (int)eStyleToQuery) ? TRUE: FALSE; +} + +void TABText::ToggleFontStyle(TABFontStyle eStyleToToggle, GBool bStyleOn) +{ + if (bStyleOn) + m_nFontStyle |= (int)eStyleToToggle; + else + m_nFontStyle &= ~ (int)eStyleToToggle; +} + + +/********************************************************************** + * TABText::GetFontStyleMIFValue() + * + * Return the Font Style value for this object using the style values + * that are used in a MIF FONT() clause. See MIF specs (appendix A). + * + * The reason why we have to differentiate between the TAB and the MIF font + * style values is that in TAB, TABFSBox is included in the style value + * as code 0x100, but in MIF it is not included, instead it is implied by + * the presence of the BG color in the FONT() clause (the BG color is + * present only when TABFSBox or TABFSHalo is set). + * This also has the effect of shifting all the other style values > 0x100 + * by 1 byte. + **********************************************************************/ +int TABText::GetFontStyleMIFValue() +{ + // The conversion is simply to remove bit 0x100 from the value and shift + // down all values past this bit. + return (m_nFontStyle & 0xff) + (m_nFontStyle & (0xff00-0x0100))/2; +} + +void TABText:: SetFontStyleMIFValue(int nStyle, GBool bBGColorSet) +{ + m_nFontStyle = (GInt16)((nStyle & 0xff) + (nStyle & 0x7f00)*2); + // When BG color is set, then either BOX or HALO should be set. + if (bBGColorSet && !QueryFontStyle(TABFSHalo)) + ToggleFontStyle(TABFSBox, TRUE); +} + +int TABText::IsFontBGColorUsed() +{ + // Font BG color is used only when BOX is set. + return (QueryFontStyle(TABFSBox)); +} + +int TABText::IsFontOColorUsed() +{ + // Font outline color is used only when HALO is set. + return (QueryFontStyle(TABFSHalo)); +} + +int TABText::IsFontSColorUsed() +{ + // Font shadow color is used only when Shadow is set. + return (QueryFontStyle(TABFSShadow)); +} + +int TABText::IsFontBold() +{ + // Font bold is used only when Bold is set. + return (QueryFontStyle(TABFSBold)); +} + +int TABText::IsFontItalic() +{ + // Font italic is used only when Italic is set. + return (QueryFontStyle(TABFSItalic)); +} + +int TABText::IsFontUnderline() +{ + // Font underline is used only when Underline is set. + return (QueryFontStyle(TABFSUnderline)); +} + +/********************************************************************** + * TABText::GetLabelStyleString() + * + * This is not the correct location, it should be in ITABFeatureFont, + * but it's really more easy to put it here. This fct return a complete + * string for the representation with the string to display + **********************************************************************/ +const char *TABText::GetLabelStyleString() +{ + const char *pszStyle = NULL; + int nStringLen = strlen(GetTextString()); + // ALL Caps, Extpanded need to modify the string value + char *pszTextString = (char*)CPLMalloc((nStringLen+1)*sizeof(char)); + /* char szPattern[20]; */ + int nJustification = 1; + + strcpy(pszTextString, GetTextString()); + /* szPattern[0] = '\0'; */ + + switch(GetTextJustification()) + { + case TABTJCenter: + nJustification = 2; + break; + case TABTJRight: + nJustification = 3; + break; + case TABTJLeft: + default: + nJustification = 1; + break; + } + + // Compute real font size, taking number of lines ("\\n", "\n") and line + // spacing into account. + int numLines = 1; + for (int i=0; pszTextString[i]; + numLines += ((pszTextString[i]=='\n' || + (pszTextString[i]=='\\' && pszTextString[i+1]=='n')) && + pszTextString[i+1] != '\0' ),++i); + + double dHeight = GetTextBoxHeight()/numLines; + + // In all cases, take out 20% of font height to account for line spacing + if (numLines > 1) + { + switch(GetTextSpacing()) + { + case TABTS1_5: + dHeight *= (0.80 * 0.69); + break; + case TABTSDouble: + dHeight *= (0.66 * 0.69); + break; + default: + dHeight *= 0.69; + } + } + else + { + dHeight *= 0.69; + } + + if (QueryFontStyle(TABFSAllCaps)) + for (int i=0; pszTextString[i];++i) + if (isalpha(pszTextString[i])) + pszTextString[i] = (char)toupper(pszTextString[i]); + + /* Escape the double quote chars and expand the text */ + char *pszTmpTextString; + int j = 0; + + if (QueryFontStyle(TABFSExpanded)) + pszTmpTextString = (char*)CPLMalloc(((nStringLen*4)+1)*sizeof(char)); + else + pszTmpTextString = (char*)CPLMalloc(((nStringLen*2)+1)*sizeof(char)); + + for (int i =0; i < nStringLen; ++i,++j) + { + if (pszTextString[i] == '"') + { + pszTmpTextString[j] = '\\'; + pszTmpTextString[j+1] = pszTextString[i]; + ++j; + } + else + pszTmpTextString[j] = pszTextString[i]; + + if (QueryFontStyle(TABFSExpanded)) + { + pszTmpTextString[j+1] = ' '; + ++j; + } + } + + pszTmpTextString[j] = '\0'; + CPLFree(pszTextString); + pszTextString = (char*)CPLMalloc((strlen(pszTmpTextString)+1)*sizeof(char)); + strcpy(pszTextString, pszTmpTextString); + CPLFree(pszTmpTextString); + + const char *pszBGColor = IsFontBGColorUsed() ? CPLSPrintf(",b:#%6.6x", + GetFontBGColor()) :""; + const char *pszOColor = IsFontOColorUsed() ? CPLSPrintf(",o:#%6.6x", + GetFontOColor()) :""; + const char *pszSColor = IsFontSColorUsed() ? CPLSPrintf(",h:#%6.6x", + GetFontSColor()) :""; + const char *pszBold = IsFontBold() ? ",bo:1" :""; + const char *pszItalic = IsFontItalic() ? ",it:1" :""; + const char *pszUnderline = IsFontUnderline() ? ",un:1" : ""; + + pszStyle=CPLSPrintf("LABEL(t:\"%s\",a:%f,s:%fg,c:#%6.6x%s%s%s%s%s%s,p:%d,f:\"%s\")", + pszTextString,GetTextAngle(), dHeight, + GetFontFGColor(),pszBGColor,pszOColor,pszSColor, + pszBold,pszItalic,pszUnderline,nJustification,GetFontNameRef()); + + CPLFree(pszTextString); + return pszStyle; + +} + +/********************************************************************** + * TABText::GetStyleString() + * + * Return style string for this feature. + * + * Style String is built only once during the first call to GetStyleString(). + **********************************************************************/ +const char *TABText::GetStyleString() +{ + if (m_pszStyleString == NULL) + { + m_pszStyleString = CPLStrdup(GetLabelStyleString()); + } + + return m_pszStyleString; +} + + + +/********************************************************************** + * TABText::DumpMIF() + * + * Dump feature geometry in a format similar to .MIF REGIONs. + **********************************************************************/ +void TABText::DumpMIF(FILE *fpOut /*=NULL*/) +{ + OGRGeometry *poGeom; + OGRPoint *poPoint = NULL; + + if (fpOut == NULL) + fpOut = stdout; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + { + /*------------------------------------------------------------- + * Generate output for text object + *------------------------------------------------------------*/ + poPoint = (OGRPoint*)poGeom; + + fprintf(fpOut, "TEXT \"%s\" %.15g %.15g\n", m_pszString?m_pszString:"", + poPoint->getX(), poPoint->getY()); + + fprintf(fpOut, " m_pszString = '%s'\n", m_pszString); + fprintf(fpOut, " m_dAngle = %.15g\n", m_dAngle); + fprintf(fpOut, " m_dHeight = %.15g\n", m_dHeight); + fprintf(fpOut, " m_rgbForeground = 0x%6.6x (%d)\n", + m_rgbForeground, m_rgbForeground); + fprintf(fpOut, " m_rgbBackground = 0x%6.6x (%d)\n", + m_rgbBackground, m_rgbBackground); + fprintf(fpOut, " m_nTextAlignment = 0x%4.4x\n", m_nTextAlignment); + fprintf(fpOut, " m_nFontStyle = 0x%4.4x\n", m_nFontStyle); + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABText: Missing or Invalid Geometry!"); + return; + } + + // Finish with PEN/BRUSH/etc. clauses + DumpPenDef(); + DumpFontDef(); + + fflush(fpOut); +} + +/*===================================================================== + * class TABMultiPoint + *====================================================================*/ + +/********************************************************************** + * TABMultiPoint::TABMultiPoint() + * + * Constructor. + **********************************************************************/ +TABMultiPoint::TABMultiPoint(OGRFeatureDefn *poDefnIn): + TABFeature(poDefnIn) +{ + m_bCenterIsSet = FALSE; +} + +/********************************************************************** + * TABMultiPoint::~TABMultiPoint() + * + * Destructor. + **********************************************************************/ +TABMultiPoint::~TABMultiPoint() +{ +} + +/********************************************************************** + * TABMultiPoint::CloneTABFeature() + * + * Duplicate feature, including stuff specific to each TABFeature type. + * + * This method calls the generic TABFeature::CloneTABFeature() and + * then copies any members specific to its own type. + **********************************************************************/ +TABFeature *TABMultiPoint::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/) +{ + /*----------------------------------------------------------------- + * Alloc new feature and copy the base stuff + *----------------------------------------------------------------*/ + TABMultiPoint *poNew = new TABMultiPoint(poNewDefn?poNewDefn:GetDefnRef()); + + CopyTABFeatureBase(poNew); + + /*----------------------------------------------------------------- + * And members specific to this class + *----------------------------------------------------------------*/ + // ITABFeatureSymbol + *(poNew->GetSymbolDefRef()) = *GetSymbolDefRef(); + + poNew->m_bCenterIsSet = m_bCenterIsSet; + poNew->m_dCenterX = m_dCenterX; + poNew->m_dCenterY = m_dCenterY; + + return poNew; +} + + +/********************************************************************** + * TABMultiPoint::ValidateMapInfoType() + * + * Check the feature's geometry part and return the corresponding + * mapinfo object type code. The m_nMapInfoType member will also + * be updated for further calls to GetMapInfoType(); + * + * Returns TAB_GEOM_NONE if the geometry is not compatible with what + * is expected for this object class. + **********************************************************************/ +TABGeomType TABMultiPoint::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) + { + OGRMultiPoint *poMPoint = (OGRMultiPoint*)poGeom; + + if (poMPoint->getNumGeometries() > TAB_MULTIPOINT_650_MAX_VERTICES) + m_nMapInfoType = TAB_GEOM_V800_MULTIPOINT; + else + m_nMapInfoType = TAB_GEOM_MULTIPOINT; + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABMultiPoint: Missing or Invalid Geometry!"); + m_nMapInfoType = TAB_GEOM_NONE; + } + + /*----------------------------------------------------------------- + * Decide if coordinates should be compressed or not. + *----------------------------------------------------------------*/ + ValidateCoordType(poMapFile); + + return m_nMapInfoType; +} + + + +/********************************************************************** + * TABMultiPoint::ReadGeometryFromMAPFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to the beginning of + * a map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABMultiPoint::ReadGeometryFromMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock **ppoCoordBlock/*=NULL*/) +{ + GInt32 nX, nY; + double dX, dY, dXMin, dYMin, dXMax, dYMax; + OGRGeometry *poGeometry=NULL; + GBool bComprCoord = poObjHdr->IsCompressedType(); + TABMAPCoordBlock *poCoordBlock = NULL; + + /*----------------------------------------------------------------- + * Fetch and validate geometry type + *----------------------------------------------------------------*/ + m_nMapInfoType = poObjHdr->m_nType; + + /*----------------------------------------------------------------- + * Read object information + *----------------------------------------------------------------*/ + if (m_nMapInfoType == TAB_GEOM_MULTIPOINT || + m_nMapInfoType == TAB_GEOM_MULTIPOINT_C || + m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT || + m_nMapInfoType == TAB_GEOM_V800_MULTIPOINT_C ) + { + /*------------------------------------------------------------- + * Copy data from poObjHdr + *------------------------------------------------------------*/ + TABMAPObjMultiPoint *poMPointHdr = (TABMAPObjMultiPoint *)poObjHdr; + + // MBR + poMapFile->Int2Coordsys(poMPointHdr->m_nMinX, poMPointHdr->m_nMinY, + dXMin, dYMin); + poMapFile->Int2Coordsys(poMPointHdr->m_nMaxX, poMPointHdr->m_nMaxY, + dXMax, dYMax); + + if (!bCoordBlockDataOnly) + { + m_nSymbolDefIndex = poMPointHdr->m_nSymbolId; // Symbol index + poMapFile->ReadSymbolDef(m_nSymbolDefIndex, &m_sSymbolDef); + } + + // Centroid/label point + poMapFile->Int2Coordsys(poMPointHdr->m_nLabelX, poMPointHdr->m_nLabelY, + dX, dY); + SetCenter(dX, dY); + + // Compressed coordinate origin (useful only in compressed case!) + m_nComprOrgX = poMPointHdr->m_nComprOrgX; + m_nComprOrgY = poMPointHdr->m_nComprOrgY; + + /*------------------------------------------------------------- + * Read Point Coordinates + *------------------------------------------------------------*/ + OGRMultiPoint *poMultiPoint; + poGeometry = poMultiPoint = new OGRMultiPoint(); + + if (ppoCoordBlock != NULL && *ppoCoordBlock != NULL) + poCoordBlock = *ppoCoordBlock; + else + poCoordBlock = poMapFile->GetCoordBlock(poMPointHdr->m_nCoordBlockPtr); + poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, + m_nComprOrgY); + + for(int iPoint=0; iPointm_nNumPoints; iPoint++) + { + if (poCoordBlock->ReadIntCoord(bComprCoord, nX, nY) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed reading coordinate data at offset %d", + poMPointHdr->m_nCoordBlockPtr); + return -1; + } + + poMapFile->Int2Coordsys(nX, nY, dX, dY); + OGRPoint *poPoint = new OGRPoint(dX, dY); + + if (poMultiPoint->addGeometryDirectly(poPoint) != OGRERR_NONE) + { + CPLAssert(FALSE); // Just in case lower-level lib is modified + } + } + + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)", + m_nMapInfoType, m_nMapInfoType); + return -1; + } + + SetGeometryDirectly(poGeometry); + + SetMBR(dXMin, dYMin, dXMax, dYMax); + + /* Copy int MBR to feature class members */ + SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + + /* Return a ref to coord block so that caller can continue reading + * after the end of this object (used by TABCollection and index splitting) + */ + if (ppoCoordBlock) + *ppoCoordBlock = poCoordBlock; + + return 0; +} + +/********************************************************************** + * TABMultiPoint::WriteGeometryToMAPFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to a valid map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABMultiPoint::WriteGeometryToMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock **ppoCoordBlock/*=NULL*/) +{ + GInt32 nX, nY; + OGRGeometry *poGeom; + OGRMultiPoint *poMPoint; + + /*----------------------------------------------------------------- + * We assume that ValidateMapInfoType() was called already and that + * the type in poObjHdr->m_nType is valid. + *----------------------------------------------------------------*/ + CPLAssert(m_nMapInfoType == poObjHdr->m_nType); + + TABMAPObjMultiPoint *poMPointHdr = (TABMAPObjMultiPoint *)poObjHdr; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) + poMPoint = (OGRMultiPoint*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABMultiPoint: Missing or Invalid Geometry!"); + return -1; + } + + poMPointHdr->m_nNumPoints = poMPoint->getNumGeometries(); + + /*----------------------------------------------------------------- + * Write data to coordinate block + *----------------------------------------------------------------*/ + int iPoint, nStatus; + TABMAPCoordBlock *poCoordBlock; + GBool bCompressed = poObjHdr->IsCompressedType(); + + if (ppoCoordBlock != NULL && *ppoCoordBlock != NULL) + poCoordBlock = *ppoCoordBlock; + else + poCoordBlock = poMapFile->GetCurCoordBlock(); + poCoordBlock->StartNewFeature(); + poMPointHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress(); + poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY); + + + for(iPoint=0, nStatus=0; + nStatus == 0 && iPoint < poMPointHdr->m_nNumPoints; iPoint++) + { + poGeom = poMPoint->getGeometryRef(iPoint); + + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + { + OGRPoint *poPoint = (OGRPoint*)poGeom; + + poMapFile->Coordsys2Int(poPoint->getX(), poPoint->getY(), nX, nY); + if (iPoint == 0) + { + // Default to the first point, we may use explicit value below + poMPointHdr->m_nLabelX = nX; + poMPointHdr->m_nLabelY = nY; + } + + if ((nStatus = poCoordBlock->WriteIntCoord(nX, nY, + bCompressed)) != 0) + { + // Failed ... error message has already been produced + return nStatus; + } + + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABMultiPoint: Invalid Geometry, expecting OGRPoint!"); + return -1; + } + } + + /*----------------------------------------------------------------- + * Copy object information + *----------------------------------------------------------------*/ + + // Compressed coordinate origin (useful only in compressed case!) + poMPointHdr->m_nComprOrgX = m_nComprOrgX; + poMPointHdr->m_nComprOrgY = m_nComprOrgY; + + poMPointHdr->m_nCoordDataSize = poCoordBlock->GetFeatureDataSize(); + poMPointHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax); + + // Center/label point (default value already set above) + double dX, dY; + if (GetCenter(dX, dY) != -1) + { + poMapFile->Coordsys2Int(dX, dY, poMPointHdr->m_nLabelX, + poMPointHdr->m_nLabelY); + } + + if (!bCoordBlockDataOnly) + { + m_nSymbolDefIndex = poMapFile->WriteSymbolDef(&m_sSymbolDef); + poMPointHdr->m_nSymbolId = (GByte)m_nSymbolDefIndex; // Symbol index + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + /* Return a ref to coord block so that caller can continue writing + * after the end of this object (used by index splitting) + */ + if (ppoCoordBlock) + *ppoCoordBlock = poCoordBlock; + + return 0; +} + + +/********************************************************************** + * TABMultiPoint::GetXY() + * + * Return this point's X,Y coordinates. + **********************************************************************/ +int TABMultiPoint::GetXY(int i, double &dX, double &dY) +{ + OGRGeometry *poGeom; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) + { + OGRMultiPoint *poMPoint = (OGRMultiPoint*)poGeom; + + if (i >= 0 && i < poMPoint->getNumGeometries() && + (poGeom = poMPoint->getGeometryRef(i)) != NULL && + wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint*)poGeom; + + dX = poPoint->getX(); + dY = poPoint->getY(); + } + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABMultiPoint: Missing or Invalid Geometry!"); + dX = dY = 0.0; + return -1; + } + + return 0; +} + +/********************************************************************** + * TABMultiPoint::GetNumPoints() + * + * Return the number of points in this multipoint object + **********************************************************************/ +int TABMultiPoint::GetNumPoints() +{ + OGRGeometry *poGeom; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) + { + OGRMultiPoint *poMPoint = (OGRMultiPoint*)poGeom; + + return poMPoint->getNumGeometries(); + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABMultiPoint: Missing or Invalid Geometry!"); + return 0; + } +} + + +/********************************************************************** + * TABMultiPoint::GetStyleString() + * + * Return style string for this feature. + * + * Style String is built only once during the first call to GetStyleString(). + **********************************************************************/ +const char *TABMultiPoint::GetStyleString() +{ + if (m_pszStyleString == NULL) + { + m_pszStyleString = CPLStrdup(GetSymbolStyleString()); + } + + return m_pszStyleString; +} + +/********************************************************************** + * TABMultiPoint::GetCenter() + * + * Returns the center point (or label point?) of the object. Compute one + * if it was not explicitly set: + * + * The default seems to be to use the first point in the collection as + * the center.. so we'll use that. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMultiPoint::GetCenter(double &dX, double &dY) +{ + if (!m_bCenterIsSet && GetNumPoints() > 0) + { + // The default seems to be to use the first point in the collection + // as the center... so we'll use that. + if (GetXY(0, m_dCenterX, m_dCenterY) == 0) + m_bCenterIsSet = TRUE; + } + + if (!m_bCenterIsSet) + return -1; + + dX = m_dCenterX; + dY = m_dCenterY; + return 0; +} + +/********************************************************************** + * TABMultiPoint::SetCenter() + * + * Set the X,Y coordinates to use as center point (or label point?) + **********************************************************************/ +void TABMultiPoint::SetCenter(double dX, double dY) +{ + m_dCenterX = dX; + m_dCenterY = dY; + m_bCenterIsSet = TRUE; +} + + +/********************************************************************** + * TABMultiPoint::DumpMIF() + * + * Dump feature geometry in a format similar to .MIF POINTs. + **********************************************************************/ +void TABMultiPoint::DumpMIF(FILE *fpOut /*=NULL*/) +{ + OGRGeometry *poGeom; + OGRMultiPoint *poMPoint; + + if (fpOut == NULL) + fpOut = stdout; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) + poMPoint = (OGRMultiPoint*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABMultiPoint: Missing or Invalid Geometry!"); + return; + } + + /*----------------------------------------------------------------- + * Generate output + *----------------------------------------------------------------*/ + fprintf(fpOut, "MULTIPOINT %d\n", poMPoint->getNumGeometries()); + + for (int iPoint=0; iPoint < poMPoint->getNumGeometries(); iPoint++) + { + poGeom = poMPoint->getGeometryRef(iPoint); + + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + { + OGRPoint *poPoint = (OGRPoint*)poGeom; + fprintf(fpOut, " %.15g %.15g\n", poPoint->getX(), poPoint->getY() ); + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABMultiPoint: Invalid Geometry, expecting OGRPoint!"); + return; + } + } + + DumpSymbolDef(fpOut); + + if (m_bCenterIsSet) + fprintf(fpOut, "Center %.15g %.15g\n", m_dCenterX, m_dCenterY); + + fflush(fpOut); +} + +/*===================================================================== + * class TABCollection + *====================================================================*/ + +/********************************************************************** + * TABCollection::TABCollection() + * + * Constructor. + **********************************************************************/ +TABCollection::TABCollection(OGRFeatureDefn *poDefnIn): + TABFeature(poDefnIn) +{ + m_poRegion = NULL; + m_poPline = NULL; + m_poMpoint = NULL; +} + +/********************************************************************** + * TABCollection::~TABCollection() + * + * Destructor. + **********************************************************************/ +TABCollection::~TABCollection() +{ + EmptyCollection(); +} + +/********************************************************************** + * TABCollection::EmptyCollection() + * + * Delete/free all collection components. + **********************************************************************/ +void TABCollection::EmptyCollection() +{ + + if (m_poRegion) + { + delete m_poRegion; + m_poRegion = NULL; + } + + if (m_poPline) + { + delete m_poPline; + m_poPline = NULL; + } + + if (m_poMpoint) + { + delete m_poMpoint; + m_poMpoint = NULL; + } + + // Empty OGR Geometry Collection as well + SyncOGRGeometryCollection(TRUE, TRUE, TRUE); + +} + +/********************************************************************** + * TABCollection::CloneTABFeature() + * + * Duplicate feature, including stuff specific to each TABFeature type. + * + * This method calls the generic TABFeature::CloneTABFeature() and + * then copies any members specific to its own type. + **********************************************************************/ +TABFeature *TABCollection::CloneTABFeature(OGRFeatureDefn *poNewDefn /*=NULL*/) +{ + /*----------------------------------------------------------------- + * Alloc new feature and copy the base stuff + *----------------------------------------------------------------*/ + TABCollection *poNew = new TABCollection(poNewDefn?poNewDefn:GetDefnRef()); + + CopyTABFeatureBase(poNew); + + /*----------------------------------------------------------------- + * And members specific to this class + *----------------------------------------------------------------*/ + + if (m_poRegion) + poNew->SetRegionDirectly((TABRegion*)m_poRegion->CloneTABFeature()); + + if (m_poPline) + poNew->SetPolylineDirectly((TABPolyline*)m_poPline->CloneTABFeature()); + + if (m_poMpoint) + poNew->SetMultiPointDirectly((TABMultiPoint*)m_poMpoint->CloneTABFeature()); + + return poNew; +} + + +/********************************************************************** + * TABCollection::ValidateMapInfoType() + * + * Check the feature's geometry part and return the corresponding + * mapinfo object type code. The m_nMapInfoType member will also + * be updated for further calls to GetMapInfoType(); + * + * Returns TAB_GEOM_NONE if the geometry is not compatible with what + * is expected for this object class. + **********************************************************************/ +TABGeomType TABCollection::ValidateMapInfoType(TABMAPFile *poMapFile /*=NULL*/) +{ + OGRGeometry *poGeom; + int nRegionType=TAB_GEOM_NONE, nPLineType=TAB_GEOM_NONE, + nMPointType=TAB_GEOM_NONE, nVersion = 650; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection) + { + m_nMapInfoType = TAB_GEOM_COLLECTION; + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABCollection: Missing or Invalid Geometry!"); + m_nMapInfoType = TAB_GEOM_NONE; + } + + /*----------------------------------------------------------------- + * Decide if coordinates should be compressed or not. + *----------------------------------------------------------------*/ + GBool bComprCoord = ValidateCoordType(poMapFile); + + /*----------------------------------------------------------------- + * Since all members of the collection share the same compressed coord + * origin, we should force the compressed origin in all components + * to be the same. + * This also implies that ValidateMapInfoType() should *NOT* be called + * again until the collection components are written by WriteGeom...() + *----------------------------------------------------------------*/ + + // First pass to figure collection type... + if (m_poRegion) + { + m_poRegion->ValidateCoordType(poMapFile); + nRegionType = m_poRegion->ValidateMapInfoType(poMapFile); + if (TAB_GEOM_GET_VERSION(nRegionType) > nVersion) + nVersion = TAB_GEOM_GET_VERSION(nRegionType); + } + + if (m_poPline) + { + m_poPline->ValidateCoordType(poMapFile); + nPLineType = m_poPline->ValidateMapInfoType(poMapFile); + if (TAB_GEOM_GET_VERSION(nPLineType) > nVersion) + nVersion = TAB_GEOM_GET_VERSION(nPLineType); + } + + if (m_poMpoint) + { + m_poMpoint->ValidateCoordType(poMapFile); + nMPointType = m_poMpoint->ValidateMapInfoType(poMapFile); + if (TAB_GEOM_GET_VERSION(nMPointType) > nVersion) + nVersion = TAB_GEOM_GET_VERSION(nMPointType); + } + + // Need to upgrade native type of collection? + if (nVersion == 800) + { + m_nMapInfoType = TAB_GEOM_V800_COLLECTION; + } + + // Make another pass updating native type and coordinates type and origin + // of each component + if (m_poRegion && nRegionType != TAB_GEOM_NONE) + { + GInt32 nXMin=0, nYMin=0, nXMax=0, nYMax=0; + m_poRegion->GetIntMBR(nXMin, nYMin, nXMax, nYMax); + m_poRegion->ForceCoordTypeAndOrigin((nVersion == 800 ? + TAB_GEOM_V800_REGION: + TAB_GEOM_V450_REGION), + bComprCoord, + m_nComprOrgX, m_nComprOrgY, + nXMin, nYMin, nXMax, nYMax); + } + + + if (m_poPline && nPLineType != TAB_GEOM_NONE) + { + GInt32 nXMin, nYMin, nXMax, nYMax; + m_poPline->GetIntMBR(nXMin, nYMin, nXMax, nYMax); + m_poPline->ForceCoordTypeAndOrigin((nVersion == 800 ? + TAB_GEOM_V800_MULTIPLINE: + TAB_GEOM_V450_MULTIPLINE), + bComprCoord, + m_nComprOrgX, m_nComprOrgY, + nXMin, nYMin, nXMax, nYMax); + } + + if (m_poMpoint && nMPointType != TAB_GEOM_NONE) + { + GInt32 nXMin, nYMin, nXMax, nYMax; + m_poMpoint->GetIntMBR(nXMin, nYMin, nXMax, nYMax); + m_poMpoint->ForceCoordTypeAndOrigin((nVersion == 800 ? + TAB_GEOM_V800_MULTIPOINT: + TAB_GEOM_MULTIPOINT), + bComprCoord, + m_nComprOrgX, m_nComprOrgY, + nXMin, nYMin, nXMax, nYMax); + } + + + return m_nMapInfoType; +} + + +/********************************************************************** + * TABCollection::ReadLabelAndMBR() + * + * Reads the label and MBR elements of the header of a collection component + * + * Returns 0 on success, -1 on failure. + **********************************************************************/ +int TABCollection::ReadLabelAndMBR(TABMAPCoordBlock *poCoordBlock, + GBool bComprCoord, + GInt32 nComprOrgX, GInt32 nComprOrgY, + GInt32 &pnMinX, GInt32 &pnMinY, + GInt32 &pnMaxX, GInt32 &pnMaxY, + GInt32 &pnLabelX, GInt32 &pnLabelY ) +{ + // + // The sections in the collection's coord blocks start with center/label + // point + MBR that are normally found in the object data blocks + // of regular region/pline/mulitpoint objects. + // + + if (bComprCoord) + { + // Region center/label point, relative to compr. coord. origin + // No it's not relative to the Object block center + pnLabelX = poCoordBlock->ReadInt16(); + pnLabelY = poCoordBlock->ReadInt16(); + + pnLabelX += nComprOrgX; + pnLabelY += nComprOrgY; + + pnMinX = nComprOrgX + poCoordBlock->ReadInt16(); // Read MBR + pnMinY = nComprOrgY + poCoordBlock->ReadInt16(); + pnMaxX = nComprOrgX + poCoordBlock->ReadInt16(); + pnMaxY = nComprOrgY + poCoordBlock->ReadInt16(); + } + else + { + // Region center/label point, relative to compr. coord. origin + // No it's not relative to the Object block center + pnLabelX = poCoordBlock->ReadInt32(); + pnLabelY = poCoordBlock->ReadInt32(); + + pnMinX = poCoordBlock->ReadInt32(); // Read MBR + pnMinY = poCoordBlock->ReadInt32(); + pnMaxX = poCoordBlock->ReadInt32(); + pnMaxY = poCoordBlock->ReadInt32(); + } + + return 0; +} + +/********************************************************************** + * TABCollection::WriteLabelAndMBR() + * + * Writes the label and MBR elements of the header of a collection component + * + * Returns 0 on success, -1 on failure. + **********************************************************************/ +int TABCollection::WriteLabelAndMBR(TABMAPCoordBlock *poCoordBlock, + GBool bComprCoord, + GInt32 nMinX, GInt32 nMinY, + GInt32 nMaxX, GInt32 nMaxY, + GInt32 nLabelX, GInt32 nLabelY ) +{ + int nStatus; + + // + // The sections in the collection's coord blocks start with center/label + // point + MBR that are normally found in the object data blocks + // of regular region/pline/mulitpoint objects. + // + + if ((nStatus = poCoordBlock->WriteIntCoord(nLabelX, nLabelY, + bComprCoord)) != 0 || + (nStatus = poCoordBlock->WriteIntCoord(nMinX, nMinY, + bComprCoord)) != 0 || + (nStatus = poCoordBlock->WriteIntCoord(nMaxX, nMaxY, + bComprCoord)) != 0 ) + { + // Failed ... error message has already been produced + return nStatus; + } + + return 0; +} + + +/********************************************************************** + * TABCollection::ReadGeometryFromMAPFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to the beginning of + * a map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABCollection::ReadGeometryFromMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock **ppoCoordBlock/*=NULL*/) +{ + double dXMin, dYMin, dXMax, dYMax; + GBool bComprCoord = poObjHdr->IsCompressedType(); + TABMAPCoordBlock* poCoordBlock = NULL; + int nCurCoordBlockPtr; + + /*----------------------------------------------------------------- + * Fetch and validate geometry type + *----------------------------------------------------------------*/ + m_nMapInfoType = poObjHdr->m_nType; + + if (m_nMapInfoType != TAB_GEOM_COLLECTION && + m_nMapInfoType != TAB_GEOM_COLLECTION_C && + m_nMapInfoType != TAB_GEOM_V800_COLLECTION && + m_nMapInfoType != TAB_GEOM_V800_COLLECTION_C ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "ReadGeometryFromMAPFile(): unsupported geometry type %d (0x%2.2x)", + m_nMapInfoType, m_nMapInfoType); + return -1; + } + + int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType); + + // Make sure collection is empty + EmptyCollection(); + + /*------------------------------------------------------------- + * Copy data from poObjHdr + *------------------------------------------------------------*/ + TABMAPObjCollection *poCollHdr = (TABMAPObjCollection *)poObjHdr; + + // MBR + poMapFile->Int2Coordsys(poCollHdr->m_nMinX, poCollHdr->m_nMinY, + dXMin, dYMin); + poMapFile->Int2Coordsys(poCollHdr->m_nMaxX, poCollHdr->m_nMaxY, + dXMax, dYMax); + + SetMBR(dXMin, dYMin, dXMax, dYMax); + + SetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + + nCurCoordBlockPtr = poCollHdr->m_nCoordBlockPtr; + if (ppoCoordBlock != NULL && *ppoCoordBlock != NULL) + poCoordBlock = *ppoCoordBlock; + else + poCoordBlock = poMapFile->GetCoordBlock(nCurCoordBlockPtr); + + // Compressed coordinate origin (useful only in compressed case!) + m_nComprOrgX = poCollHdr->m_nComprOrgX; + m_nComprOrgY = poCollHdr->m_nComprOrgY; + + /*----------------------------------------------------------------- + * Region Component + *----------------------------------------------------------------*/ + if(poCollHdr->m_nNumRegSections > 0) + { + // + // Build fake coord section header to pass to TABRegion::ReadGeom...() + // + TABMAPObjPLine oRegionHdr; + + oRegionHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX; + oRegionHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY; + + // + // The region section in the coord block starts with center/label + // point + MBR that are normally found in the object data blocks + // of regular region objects. + // + + // In V800 the mini-header starts with a copy of num_parts + if (nVersion >= 800) + { + /* int numParts; */ + /* numParts = poCoordBlock->ReadInt32(); */ + CPLAssert(poCoordBlock->ReadInt32() == poCollHdr->m_nNumRegSections); + } + + ReadLabelAndMBR(poCoordBlock, bComprCoord, + oRegionHdr.m_nComprOrgX, oRegionHdr.m_nComprOrgY, + oRegionHdr.m_nMinX, oRegionHdr.m_nMinY, + oRegionHdr.m_nMaxX, oRegionHdr.m_nMaxY, + oRegionHdr.m_nLabelX, oRegionHdr.m_nLabelY); + + // Set CoordBlockPtr so that TABRegion continues reading here + oRegionHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress(); + + if (bComprCoord) + oRegionHdr.m_nType = TAB_GEOM_V450_REGION_C; + else + oRegionHdr.m_nType = TAB_GEOM_V450_REGION; + if (nVersion == 800) + oRegionHdr.m_nType = (TABGeomType)(oRegionHdr.m_nType + (TAB_GEOM_V800_REGION - TAB_GEOM_V450_REGION)); + + oRegionHdr.m_numLineSections = poCollHdr->m_nNumRegSections; + oRegionHdr.m_nPenId = poCollHdr->m_nRegionPenId; + oRegionHdr.m_nBrushId = poCollHdr->m_nRegionBrushId; + oRegionHdr.m_bSmooth = 0; // TODO + + // + // Use a TABRegion to read/store the Region coord data + // + m_poRegion = new TABRegion(GetDefnRef()); + if (m_poRegion->ReadGeometryFromMAPFile(poMapFile, &oRegionHdr, + bCoordBlockDataOnly, + &poCoordBlock) != 0) + return -1; + + // Set new coord block ptr for next object + if (poCoordBlock) + nCurCoordBlockPtr = poCoordBlock->GetCurAddress(); + } + + + /*----------------------------------------------------------------- + * PLine Component + *----------------------------------------------------------------*/ + if(poCollHdr->m_nNumPLineSections > 0) + { + // + // Build fake coord section header to pass to TABPolyline::ReadGeom..() + // + TABMAPObjPLine oPLineHdr; + + oPLineHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX; + oPLineHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY; + + // + // The pline section in the coord block starts with center/label + // point + MBR that are normally found in the object data blocks + // of regular pline objects. + // + + // In V800 the mini-header starts with a copy of num_parts + if (nVersion >= 800) + { + /* int numParts; */ + /* numParts = poCoordBlock->ReadInt32(); */ + CPLAssert(poCoordBlock->ReadInt32() == poCollHdr->m_nNumPLineSections); + } + + ReadLabelAndMBR(poCoordBlock, bComprCoord, + oPLineHdr.m_nComprOrgX, oPLineHdr.m_nComprOrgY, + oPLineHdr.m_nMinX, oPLineHdr.m_nMinY, + oPLineHdr.m_nMaxX, oPLineHdr.m_nMaxY, + oPLineHdr.m_nLabelX, oPLineHdr.m_nLabelY); + + // Set CoordBlockPtr so that TABRegion continues reading here + oPLineHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress(); + + if (bComprCoord) + oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE_C; + else + oPLineHdr.m_nType = TAB_GEOM_V450_MULTIPLINE; + if (nVersion == 800) + oPLineHdr.m_nType = (TABGeomType) (oPLineHdr.m_nType + (TAB_GEOM_V800_MULTIPLINE - + TAB_GEOM_V450_MULTIPLINE)); + + oPLineHdr.m_numLineSections = poCollHdr->m_nNumPLineSections; + oPLineHdr.m_nPenId = poCollHdr->m_nPolylinePenId; + oPLineHdr.m_bSmooth = 0; // TODO + + // + // Use a TABPolyline to read/store the Polyline coord data + // + m_poPline = new TABPolyline(GetDefnRef()); + if (m_poPline->ReadGeometryFromMAPFile(poMapFile, &oPLineHdr, + bCoordBlockDataOnly, + &poCoordBlock) != 0) + return -1; + + // Set new coord block ptr for next object + if (poCoordBlock) + nCurCoordBlockPtr = poCoordBlock->GetCurAddress(); + } + + /*----------------------------------------------------------------- + * MultiPoint Component + *----------------------------------------------------------------*/ + if(poCollHdr->m_nNumMultiPoints > 0) + { + // + // Build fake coord section header to pass to TABMultiPoint::ReadGeom() + // + TABMAPObjMultiPoint oMPointHdr; + + oMPointHdr.m_nComprOrgX = poCollHdr->m_nComprOrgX; + oMPointHdr.m_nComprOrgY = poCollHdr->m_nComprOrgY; + + // + // The pline section in the coord block starts with center/label + // point + MBR that are normally found in the object data blocks + // of regular pline objects. + // + ReadLabelAndMBR(poCoordBlock, bComprCoord, + oMPointHdr.m_nComprOrgX, oMPointHdr.m_nComprOrgY, + oMPointHdr.m_nMinX, oMPointHdr.m_nMinY, + oMPointHdr.m_nMaxX, oMPointHdr.m_nMaxY, + oMPointHdr.m_nLabelX, oMPointHdr.m_nLabelY); + + // Set CoordBlockPtr so that TABRegion continues reading here + oMPointHdr.m_nCoordBlockPtr = poCoordBlock->GetCurAddress(); + + if (bComprCoord) + oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT_C; + else + oMPointHdr.m_nType = TAB_GEOM_MULTIPOINT; + if (nVersion == 800) + oMPointHdr.m_nType = (TABGeomType) (oMPointHdr.m_nType + (TAB_GEOM_V800_MULTIPOINT - + TAB_GEOM_MULTIPOINT)); + + oMPointHdr.m_nNumPoints = poCollHdr->m_nNumMultiPoints; + oMPointHdr.m_nSymbolId = poCollHdr->m_nMultiPointSymbolId; + + // + // Use a TABMultiPoint to read/store the coord data + // + m_poMpoint = new TABMultiPoint(GetDefnRef()); + if (m_poMpoint->ReadGeometryFromMAPFile(poMapFile, &oMPointHdr, + bCoordBlockDataOnly, + &poCoordBlock) != 0) + return -1; + + // Set new coord block ptr for next object (not really useful here) + if (poCoordBlock) + nCurCoordBlockPtr = poCoordBlock->GetCurAddress(); + } + + /*----------------------------------------------------------------- + * Set the main OGRFeature Geometry + * (this is actually duplicating geometries from each member) + *----------------------------------------------------------------*/ + if (SyncOGRGeometryCollection(TRUE, TRUE, TRUE) != 0) + return -1; + + /* Return a ref to coord block so that caller can continue reading + * after the end of this object (used by index splitting) + */ + if (ppoCoordBlock) + *ppoCoordBlock = poCoordBlock; + + return 0; +} + +/********************************************************************** + * TABCollection::WriteGeometryToMAPFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to a valid map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABCollection::WriteGeometryToMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool bCoordBlockDataOnly /*=FALSE*/, + TABMAPCoordBlock **ppoCoordBlock/*=NULL*/) +{ + /*----------------------------------------------------------------- + * Note that the current implementation does not allow setting the + * Geometry via OGRFeature::SetGeometry(). The geometries must be set + * via the SetRegion/Pline/MpointDirectly() methods which will take + * care of keeping the OGRFeature's geometry in sync. + * + * TODO: If we ever want to support sync'ing changes from the OGRFeature's + * geometry to the m_poRegion/Pline/Mpoint then a call should be added + * here, or perhaps in ValidateMapInfoType(), or even better in + * custom TABCollection::SetGeometry*()... but then this last option + * won't work unless OGRFeature::SetGeometry*() are made virtual in OGR. + *----------------------------------------------------------------*/ + + + /*----------------------------------------------------------------- + * We assume that ValidateMapInfoType() was called already and that + * the type in poObjHdr->m_nType is valid. + *----------------------------------------------------------------*/ + CPLAssert(m_nMapInfoType == poObjHdr->m_nType); + + TABMAPObjCollection *poCollHdr = (TABMAPObjCollection *)poObjHdr; + + /*----------------------------------------------------------------- + * Write data to coordinate block for each component... + * + * Note that at this point, the caller (TABFile) has called + * TABCollection::ValidateMapInfoType() which in turn has called + * each component's respective ValidateMapInfoType() and + * ForceCoordTypeAndCoordOrigin() so the objects are ready to have + * their respective WriteGeometryToMapFile() called. + *----------------------------------------------------------------*/ + TABMAPCoordBlock *poCoordBlock; + GBool bCompressed = poObjHdr->IsCompressedType(); + // TODO: ??? Do we need to track overall collection coord data size??? + int nTotalFeatureDataSize = 0; + + int nVersion = TAB_GEOM_GET_VERSION(m_nMapInfoType); + + if (ppoCoordBlock != NULL && *ppoCoordBlock != NULL) + poCoordBlock = *ppoCoordBlock; + else + poCoordBlock = poMapFile->GetCurCoordBlock(); + poCoordBlock->StartNewFeature(); + poCollHdr->m_nCoordBlockPtr = poCoordBlock->GetCurAddress(); + poCoordBlock->SetComprCoordOrigin(m_nComprOrgX, m_nComprOrgY); + + /*----------------------------------------------------------------- + * Region component + *----------------------------------------------------------------*/ + if (m_poRegion && m_poRegion->GetMapInfoType() != TAB_GEOM_NONE) + { + CPLAssert(m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION || + m_poRegion->GetMapInfoType() == TAB_GEOM_V450_REGION_C || + m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION || + m_poRegion->GetMapInfoType() == TAB_GEOM_V800_REGION_C ); + + TABMAPObjPLine *poRegionHdr = (TABMAPObjPLine *) + TABMAPObjHdr::NewObj(m_poRegion->GetMapInfoType(), -1); + + // Update count of objects by type in header + if (!bCoordBlockDataOnly) + poMapFile->UpdateMapHeaderInfo(m_poRegion->GetMapInfoType()); + + // Write a placeholder for centroid/label point and MBR mini-header + // and we'll come back later to write the real values. + // + // Note that the call to WriteGeometryToMAPFile() below will call + // StartNewFeature() as well, so we need to track the current + // value before calling it + + poCoordBlock->StartNewFeature(); + int nMiniHeaderPtr = poCoordBlock->GetCurAddress(); + + // In V800 the mini-header starts with a copy of num_parts + if (nVersion >= 800) + { + poCoordBlock->WriteInt32(0); + } + WriteLabelAndMBR(poCoordBlock, bCompressed, + 0, 0, 0, 0, 0, 0); + nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize(); + + if (m_poRegion->WriteGeometryToMAPFile(poMapFile, poRegionHdr, + bCoordBlockDataOnly, + &poCoordBlock) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing Region part in collection."); + delete poRegionHdr; + return -1; + } + + nTotalFeatureDataSize += poRegionHdr->m_nCoordDataSize; + + // Come back to write the real values in the mini-header + int nEndOfObjectPtr = poCoordBlock->GetCurAddress(); + poCoordBlock->StartNewFeature(); + + if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0) + { + delete poRegionHdr; + return -1; + } + + // In V800 the mini-header starts with a copy of num_parts + if (nVersion >= 800) + { + poCoordBlock->WriteInt32(poRegionHdr->m_numLineSections); + } + WriteLabelAndMBR(poCoordBlock, bCompressed, + poRegionHdr->m_nMinX, poRegionHdr->m_nMinY, + poRegionHdr->m_nMaxX, poRegionHdr->m_nMaxY, + poRegionHdr->m_nLabelX, poRegionHdr->m_nLabelY); + + // And finally move the pointer back to the end of this component + if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0) + { + delete poRegionHdr; + return -1; + } + + // Copy other header members to the main collection header + // TODO: Does m_nRegionDataSize need to include the centroid+mbr + // mini-header??? + poCollHdr->m_nRegionDataSize = poRegionHdr->m_nCoordDataSize; + poCollHdr->m_nNumRegSections = poRegionHdr->m_numLineSections; + + if (!bCoordBlockDataOnly) + { + poCollHdr->m_nRegionPenId = poRegionHdr->m_nPenId; + poCollHdr->m_nRegionBrushId = poRegionHdr->m_nBrushId; + // TODO: Smooth flag = poRegionHdr->m_bSmooth; + } + + delete poRegionHdr; + } + else + { + // No Region component. Set corresponding header fields to 0 + + poCollHdr->m_nRegionDataSize = 0; + poCollHdr->m_nNumRegSections = 0; + poCollHdr->m_nRegionPenId = 0; + poCollHdr->m_nRegionBrushId = 0; + } + + /*----------------------------------------------------------------- + * PLine component + *----------------------------------------------------------------*/ + if (m_poPline && m_poPline->GetMapInfoType() != TAB_GEOM_NONE) + { + CPLAssert(m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE || + m_poPline->GetMapInfoType() == TAB_GEOM_V450_MULTIPLINE_C || + m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE || + m_poPline->GetMapInfoType() == TAB_GEOM_V800_MULTIPLINE_C ); + + TABMAPObjPLine *poPlineHdr = (TABMAPObjPLine *) + TABMAPObjHdr::NewObj(m_poPline->GetMapInfoType(), -1); + + // Update count of objects by type in header + if (!bCoordBlockDataOnly) + poMapFile->UpdateMapHeaderInfo(m_poPline->GetMapInfoType()); + + // Write a placeholder for centroid/label point and MBR mini-header + // and we'll come back later to write the real values. + // + // Note that the call to WriteGeometryToMAPFile() below will call + // StartNewFeature() as well, so we need to track the current + // value before calling it + + poCoordBlock->StartNewFeature(); + int nMiniHeaderPtr = poCoordBlock->GetCurAddress(); + + // In V800 the mini-header starts with a copy of num_parts + if (nVersion >= 800) + { + poCoordBlock->WriteInt32(0); + } + WriteLabelAndMBR(poCoordBlock, bCompressed, + 0, 0, 0, 0, 0, 0); + nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize(); + + if (m_poPline->WriteGeometryToMAPFile(poMapFile, poPlineHdr, + bCoordBlockDataOnly, + &poCoordBlock) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing Region part in collection."); + delete poPlineHdr; + return -1; + } + + nTotalFeatureDataSize += poPlineHdr->m_nCoordDataSize; + + // Come back to write the real values in the mini-header + int nEndOfObjectPtr = poCoordBlock->GetCurAddress(); + poCoordBlock->StartNewFeature(); + + if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0) + { + delete poPlineHdr; + return -1; + } + + // In V800 the mini-header starts with a copy of num_parts + if (nVersion >= 800) + { + poCoordBlock->WriteInt32(poPlineHdr->m_numLineSections); + } + WriteLabelAndMBR(poCoordBlock, bCompressed, + poPlineHdr->m_nMinX, poPlineHdr->m_nMinY, + poPlineHdr->m_nMaxX, poPlineHdr->m_nMaxY, + poPlineHdr->m_nLabelX, poPlineHdr->m_nLabelY); + + // And finally move the pointer back to the end of this component + if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0) + { + delete poPlineHdr; + return -1; + } + + // Copy other header members to the main collection header + // TODO: Does m_nRegionDataSize need to include the centroid+mbr + // mini-header??? + poCollHdr->m_nPolylineDataSize = poPlineHdr->m_nCoordDataSize; + poCollHdr->m_nNumPLineSections = poPlineHdr->m_numLineSections; + if (!bCoordBlockDataOnly) + { + poCollHdr->m_nPolylinePenId = poPlineHdr->m_nPenId; + // TODO: Smooth flag = poPlineHdr->m_bSmooth; + } + + delete poPlineHdr; + } + else + { + // No Polyline component. Set corresponding header fields to 0 + + poCollHdr->m_nPolylineDataSize = 0; + poCollHdr->m_nNumPLineSections = 0; + poCollHdr->m_nPolylinePenId = 0; + } + + + /*----------------------------------------------------------------- + * MultiPoint component + *----------------------------------------------------------------*/ + if (m_poMpoint && m_poMpoint->GetMapInfoType() != TAB_GEOM_NONE) + { + CPLAssert(m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT || + m_poMpoint->GetMapInfoType() == TAB_GEOM_MULTIPOINT_C || + m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT || + m_poMpoint->GetMapInfoType() == TAB_GEOM_V800_MULTIPOINT_C ); + + TABMAPObjMultiPoint *poMpointHdr = (TABMAPObjMultiPoint *) + TABMAPObjHdr::NewObj(m_poMpoint->GetMapInfoType(), -1); + + // Update count of objects by type in header + if (!bCoordBlockDataOnly) + poMapFile->UpdateMapHeaderInfo(m_poMpoint->GetMapInfoType()); + + // Write a placeholder for centroid/label point and MBR mini-header + // and we'll come back later to write the real values. + // + // Note that the call to WriteGeometryToMAPFile() below will call + // StartNewFeature() as well, so we need to track the current + // value before calling it + + poCoordBlock->StartNewFeature(); + int nMiniHeaderPtr = poCoordBlock->GetCurAddress(); + + WriteLabelAndMBR(poCoordBlock, bCompressed, + 0, 0, 0, 0, 0, 0); + nTotalFeatureDataSize += poCoordBlock->GetFeatureDataSize(); + + if (m_poMpoint->WriteGeometryToMAPFile(poMapFile, poMpointHdr, + bCoordBlockDataOnly, + &poCoordBlock) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing Region part in collection."); + delete poMpointHdr; + return -1; + } + + nTotalFeatureDataSize += poMpointHdr->m_nCoordDataSize; + + // Come back to write the real values in the mini-header + int nEndOfObjectPtr = poCoordBlock->GetCurAddress(); + poCoordBlock->StartNewFeature(); + + if (poCoordBlock->GotoByteInFile(nMiniHeaderPtr, TRUE, TRUE) != 0) + { + delete poMpointHdr; + return -1; + } + + WriteLabelAndMBR(poCoordBlock, bCompressed, + poMpointHdr->m_nMinX, poMpointHdr->m_nMinY, + poMpointHdr->m_nMaxX, poMpointHdr->m_nMaxY, + poMpointHdr->m_nLabelX, poMpointHdr->m_nLabelY); + + // And finally move the pointer back to the end of this component + if (poCoordBlock->GotoByteInFile(nEndOfObjectPtr, TRUE, TRUE) != 0) + { + delete poMpointHdr; + return -1; + } + + // Copy other header members to the main collection header + // TODO: Does m_nRegionDataSize need to include the centroid+mbr + // mini-header??? + poCollHdr->m_nMPointDataSize = poMpointHdr->m_nCoordDataSize; + poCollHdr->m_nNumMultiPoints = poMpointHdr->m_nNumPoints; + if (!bCoordBlockDataOnly) + { + poCollHdr->m_nMultiPointSymbolId = poMpointHdr->m_nSymbolId; + } + + delete poMpointHdr; + } + else + { + // No Multipoint component. Set corresponding header fields to 0 + + poCollHdr->m_nMPointDataSize = 0; + poCollHdr->m_nNumMultiPoints = 0; + poCollHdr->m_nMultiPointSymbolId = 0; + } + + + /*----------------------------------------------------------------- + * Copy object information + *----------------------------------------------------------------*/ + + // Compressed coordinate origin (useful only in compressed case!) + poCollHdr->m_nComprOrgX = m_nComprOrgX; + poCollHdr->m_nComprOrgY = m_nComprOrgY; + + poCollHdr->m_nCoordDataSize = nTotalFeatureDataSize; + + poCollHdr->SetMBR(m_nXMin, m_nYMin, m_nXMax, m_nYMax); + + + if (CPLGetLastErrorNo() != 0) + return -1; + + /* Return a ref to coord block so that caller can continue writing + * after the end of this object (used by index splitting) + */ + if (ppoCoordBlock) + *ppoCoordBlock = poCoordBlock; + + return 0; +} + + +/********************************************************************** + * TABCollection::SyncOGRGeometryCollection() + * + * Copy the region/pline/multipoint's geometries to the OGRFeature's + * geometry. + **********************************************************************/ +int TABCollection::SyncOGRGeometryCollection(GBool bSyncRegion, + GBool bSyncPline, + GBool bSyncMpoint) +{ + OGRGeometry *poThisGeom = GetGeometryRef(); + OGRGeometryCollection *poGeomColl; + + // poGeometry is defined in the OGRFeature class + if (poThisGeom == NULL) + { + poThisGeom = poGeomColl = new OGRGeometryCollection(); + SetGeometryDirectly(poGeomColl); + } + else if (wkbFlatten(poThisGeom->getGeometryType())==wkbGeometryCollection) + { + poGeomColl = (OGRGeometryCollection *)poThisGeom; + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABCollection: Invalid Geometry. Type must be OGRCollection."); + return -1; + } + + /*----------------------------------------------------------------- + * Start by removing geometries that need to be replaced + * In theory there should be a single geometry of each type, but + * just in case, we'll loop over the whole collection and delete all + * instances of each type if there are some. + *----------------------------------------------------------------*/ + int numGeometries = poGeomColl->getNumGeometries(); + for (int i=0; igetGeometryRef(i); + if (!poGeom) + continue; + + if ( (bSyncRegion && + (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon || + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon) ) || + (bSyncPline && + (wkbFlatten(poGeom->getGeometryType()) == wkbLineString || + wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)) || + (bSyncMpoint && + (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) ) ) + { + // Remove this geometry + poGeomColl->removeGeometry(i); + + // Unless this was the last geometry, we need to restart + // scanning the collection since we modified it + if (i != numGeometries-1) + { + i=0; + numGeometries = poGeomColl->getNumGeometries(); + } + } + } + + /*----------------------------------------------------------------- + * Copy TAB Feature geometries to OGRGeometryCollection + *----------------------------------------------------------------*/ + if(bSyncRegion && m_poRegion && m_poRegion->GetGeometryRef() != NULL) + poGeomColl->addGeometry(m_poRegion->GetGeometryRef()); + + if(bSyncPline && m_poPline && m_poPline->GetGeometryRef() != NULL) + poGeomColl->addGeometry(m_poPline->GetGeometryRef()); + + if(bSyncMpoint && m_poMpoint && m_poMpoint->GetGeometryRef() != NULL) + poGeomColl->addGeometry(m_poMpoint->GetGeometryRef()); + + return 0; +} + + +/********************************************************************** + * TABCollection::SetRegionDirectly() + * + * Set the region component of the collection, deleting the current + * region component if there is one. The object is then owned by the + * TABCollection object. Passing NULL just deletes it. + * + * Note that an intentional side-effect is that calling this method + * with the same poRegion pointer that is already owned by this object + * will force resync'ing the OGR Geometry member. + **********************************************************************/ +int TABCollection::SetRegionDirectly(TABRegion *poRegion) +{ + if (m_poRegion && m_poRegion != poRegion) + delete m_poRegion; + m_poRegion = poRegion; + + // Update OGRGeometryCollection component as well + return SyncOGRGeometryCollection(TRUE, FALSE, FALSE); +} + +/********************************************************************** + * TABCollection::SetPolylineDirectly() + * + * Set the polyline component of the collection, deleting the current + * polyline component if there is one. The object is then owned by the + * TABCollection object. Passing NULL just deletes it. + * + * Note that an intentional side-effect is that calling this method + * with the same poPline pointer that is already owned by this object + * will force resync'ing the OGR Geometry member. + **********************************************************************/ +int TABCollection::SetPolylineDirectly(TABPolyline *poPline) +{ + if (m_poPline && m_poPline != poPline) + delete m_poPline; + m_poPline = poPline; + + // Update OGRGeometryCollection component as well + return SyncOGRGeometryCollection(FALSE, TRUE, FALSE); +} + +/********************************************************************** + * TABCollection::SetMultiPointDirectly() + * + * Set the multipoint component of the collection, deleting the current + * multipoint component if there is one. The object is then owned by the + * TABCollection object. Passing NULL just deletes it. + * + * Note that an intentional side-effect is that calling this method + * with the same poMpoint pointer that is already owned by this object + * will force resync'ing the OGR Geometry member. + **********************************************************************/ +int TABCollection::SetMultiPointDirectly(TABMultiPoint *poMpoint) +{ + if (m_poMpoint && m_poMpoint != poMpoint) + delete m_poMpoint; + m_poMpoint = poMpoint; + + // Update OGRGeometryCollection component as well + return SyncOGRGeometryCollection(FALSE, FALSE, TRUE); +} + + +/********************************************************************** + * TABCollection::GetStyleString() + * + * Return style string for this feature. + * + * Style String is built only once during the first call to GetStyleString(). + **********************************************************************/ +const char *TABCollection::GetStyleString() +{ + if (m_pszStyleString == NULL) + { + m_pszStyleString = CPLStrdup(GetSymbolStyleString()); + } + + return m_pszStyleString; +} + + +/********************************************************************** + * TABCollection::DumpMIF() + * + * Dump feature geometry + **********************************************************************/ +void TABCollection::DumpMIF(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + /*----------------------------------------------------------------- + * Generate output + *----------------------------------------------------------------*/ + int numParts = 0; + if (m_poRegion) numParts++; + if (m_poPline) numParts++; + if (m_poMpoint) numParts++; + + fprintf(fpOut, "COLLECTION %d\n", numParts); + + if (m_poRegion) + m_poRegion->DumpMIF(fpOut); + + if (m_poPline) + m_poPline->DumpMIF(fpOut); + + if (m_poMpoint) + m_poMpoint->DumpMIF(fpOut); + + + DumpSymbolDef(fpOut); + + fflush(fpOut); +} + +/*===================================================================== + * class TABDebugFeature + *====================================================================*/ + +/********************************************************************** + * TABDebugFeature::TABDebugFeature() + * + * Constructor. + **********************************************************************/ +TABDebugFeature::TABDebugFeature(OGRFeatureDefn *poDefnIn): + TABFeature(poDefnIn) +{ +} + +/********************************************************************** + * TABDebugFeature::~TABDebugFeature() + * + * Destructor. + **********************************************************************/ +TABDebugFeature::~TABDebugFeature() +{ +} + +/********************************************************************** + * TABDebugFeature::ReadGeometryFromMAPFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to the beginning of + * a map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABDebugFeature::ReadGeometryFromMAPFile(TABMAPFile *poMapFile, + TABMAPObjHdr *poObjHdr, + GBool /*bCoordBlockDataOnly=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + TABMAPObjectBlock *poObjBlock; + TABMAPHeaderBlock *poHeader; + + /*----------------------------------------------------------------- + * Fetch geometry type + *----------------------------------------------------------------*/ + m_nMapInfoType = poObjHdr->m_nType; + + poObjBlock = poMapFile->GetCurObjBlock(); + poHeader = poMapFile->GetHeaderBlock(); + + /*----------------------------------------------------------------- + * If object type has coords in a type 3 block, then its position + * follows + *----------------------------------------------------------------*/ + if (poHeader->MapObjectUsesCoordBlock(m_nMapInfoType)) + { + m_nCoordDataPtr = poObjBlock->ReadInt32(); + m_nCoordDataSize = poObjBlock->ReadInt32(); + } + else + { + m_nCoordDataPtr = -1; + m_nCoordDataSize = 0; + } + + m_nSize = poHeader->GetMapObjectSize(m_nMapInfoType); + if (m_nSize > 0) + { + poObjBlock->GotoByteRel(-5); // Go back to beginning of header + poObjBlock->ReadBytes(m_nSize, m_abyBuf); + } + + return 0; +} + +/********************************************************************** + * TABDebugFeature::WriteGeometryToMAPFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that poMAPFile currently points to a valid map object. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABDebugFeature::WriteGeometryToMAPFile(TABMAPFile * /*poMapFile*/, + TABMAPObjHdr * /*poObjHdr*/, + GBool /*bCoordBlockDataOnly=FALSE*/, + TABMAPCoordBlock ** /*ppoCoordBlock=NULL*/) +{ + // Nothing to do here! + + CPLError(CE_Failure, CPLE_NotSupported, + "TABDebugFeature::WriteGeometryToMAPFile() not implemented.\n"); + + return -1; +} + +/********************************************************************** + * TABDebugFeature::DumpMIF() + * + * Dump feature contents... available only in DEBUG mode. + **********************************************************************/ +void TABDebugFeature::DumpMIF(FILE *fpOut /*=NULL*/) +{ + int i; + + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABDebugFeature (type = 0x%2.2x) -----\n", + GetMapInfoType()); + fprintf(fpOut, " Object size: %d bytes\n", m_nSize); + fprintf(fpOut, " m_nCoordDataPtr = %d\n", m_nCoordDataPtr); + fprintf(fpOut, " m_nCoordDataSize = %d\n", m_nCoordDataSize); + fprintf(fpOut, " "); + + for(i=0; i 0? + (m_sPenDef.nPointWidth+10): m_sPenDef.nPixelWidth ); +} + +void ITABFeaturePen::SetPenWidthMIF(int val) +{ + if (val > 10) + { + m_sPenDef.nPointWidth = MIN((val-10), 2037); + m_sPenDef.nPixelWidth = 0; + } + else + { + m_sPenDef.nPixelWidth = (GByte)MIN(MAX(val, 1), 7); + m_sPenDef.nPointWidth = 0; + } +} + +/********************************************************************** + * ITABFeaturePen::GetPenStyleString() + * + * Return a PEN() string. All representations info for the pen are here. + **********************************************************************/ +const char *ITABFeaturePen::GetPenStyleString() +{ + const char *pszStyle = NULL; + int nOGRStyle = 0; + char szPattern[20]; + + szPattern[0] = '\0'; + + // For now, I only add the 25 first styles + switch (GetPenPattern()) + { + case 1: + nOGRStyle =1; + break; + case 2: + nOGRStyle = 0; + break; + case 3: + nOGRStyle = 3; + strcpy(szPattern,"1 1"); + break; + case 4: + nOGRStyle = 3; + strcpy(szPattern,"2 1"); + break; + case 5: + nOGRStyle = 3; + strcpy(szPattern,"3 1"); + break; + case 6: + nOGRStyle = 3; + strcpy(szPattern,"6 1"); + break; + case 7: + nOGRStyle = 4; + strcpy(szPattern,"12 2"); + break; + case 8: + nOGRStyle = 4; + strcpy(szPattern,"24 4"); + break; + case 9: + nOGRStyle = 3; + strcpy(szPattern,"4 3"); + break; + case 10: + nOGRStyle = 5; + strcpy(szPattern,"1 4"); + break; + case 11: + nOGRStyle = 3; + strcpy(szPattern,"4 6"); + break; + case 12: + nOGRStyle = 3; + strcpy(szPattern,"6 4"); + break; + case 13: + nOGRStyle = 4; + strcpy(szPattern,"12 12"); + break; + case 14: + nOGRStyle = 6; + strcpy(szPattern,"8 2 1 2"); + break; + case 15: + nOGRStyle = 6; + strcpy(szPattern,"12 1 1 1"); + break; + case 16: + nOGRStyle = 6; + strcpy(szPattern,"12 1 3 1"); + break; + case 17: + nOGRStyle = 6; + strcpy(szPattern,"24 6 4 6"); + break; + case 18: + nOGRStyle = 7; + strcpy(szPattern,"24 3 3 3 3 3"); + break; + case 19: + nOGRStyle = 7; + strcpy(szPattern,"24 3 3 3 3 3 3 3"); + break; + case 20: + nOGRStyle = 7; + strcpy(szPattern,"6 3 1 3 1 3"); + break; + case 21: + nOGRStyle = 7; + strcpy(szPattern,"12 2 1 2 1 2"); + break; + case 22: + nOGRStyle = 7; + strcpy(szPattern,"12 2 1 2 1 2 1 2"); + break; + case 23: + nOGRStyle = 6; + strcpy(szPattern,"4 1 1 1"); + break; + case 24: + nOGRStyle = 7; + strcpy(szPattern,"4 1 1 1 1"); + break; + case 25: + nOGRStyle = 6; + strcpy(szPattern,"4 1 1 1 2 1 1 1"); + break; + + default: + nOGRStyle = 0; + break; + } + + if (strlen(szPattern) != 0) + { + if(m_sPenDef.nPointWidth > 0) + pszStyle =CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\"mapinfo-pen-%d," + "ogr-pen-%d\",p:\"%spx\")", + ((int)GetPenWidthPoint()), + m_sPenDef.rgbColor,GetPenPattern(),nOGRStyle, + szPattern); + else + pszStyle =CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\"mapinfo-pen-%d," + "ogr-pen-%d\",p:\"%spx\")", + GetPenWidthPixel(), + m_sPenDef.rgbColor,GetPenPattern(),nOGRStyle, + szPattern); + } + else + { + if(m_sPenDef.nPointWidth > 0) + pszStyle =CPLSPrintf("PEN(w:%dpt,c:#%6.6x,id:\"" + "mapinfo-pen-%d,ogr-pen-%d\")", + ((int)GetPenWidthPoint()), + m_sPenDef.rgbColor,GetPenPattern(),nOGRStyle); + else + pszStyle =CPLSPrintf("PEN(w:%dpx,c:#%6.6x,id:\"" + "mapinfo-pen-%d,ogr-pen-%d\")", + GetPenWidthPixel(), + m_sPenDef.rgbColor,GetPenPattern(),nOGRStyle); + } + + return pszStyle; +} + +/********************************************************************** + * ITABFeaturePen::SetPenFromStyleString() + * + * Init the Pen properties from a style string. + **********************************************************************/ +void ITABFeaturePen::SetPenFromStyleString(const char *pszStyleString) +{ + int numParts, i; + GBool bIsNull = 0; + + const char *pszPenName, *pszPenPattern; + + double nPenWidth; + + GInt32 nPenColor; + const char *pszPenColor; + + int nPenId; + const char* pszPenId; + + // Use the Style Manager to retreive all the information we need. + OGRStyleMgr *poStyleMgr = new OGRStyleMgr(NULL); + OGRStyleTool *poStylePart; + + // Init the StyleMgr with the StyleString. + poStyleMgr->InitStyleString(pszStyleString); + + // Retreive the Pen info. + numParts = poStyleMgr->GetPartCount(); + for(i=0; iGetPart(i); + if( poStylePart == NULL ) + continue; + + if(poStylePart->GetType() == OGRSTCPen) + { + break; + } + else + { + delete poStylePart; + poStylePart = NULL; + } + } + + // If the no Pen found, do nothing. + if(i >= numParts) + { + delete poStyleMgr; + return; + } + + OGRStylePen *poPenStyle = (OGRStylePen*)poStylePart; + + // With Pen, we always want to output points or pixels (which are the same, + // so just use points). + // + // It's very important to set the output unit of the feature. + // The default value is meter. If we don't do it all numerical values + // will be assumed to be converted from the input unit to meter when we + // will get them via GetParam...() functions. + // See OGRStyleTool::Parse() for more details. + poPenStyle->SetUnit(OGRSTUPoints, 1); + + // Get the Pen Id or pattern + pszPenName = poPenStyle->Id(bIsNull); + if (bIsNull) pszPenName = NULL; + + // Set the width + if(poPenStyle->Width(bIsNull)) + { + nPenWidth = poPenStyle->Width(bIsNull); + // Width < 10 is a pixel + if(nPenWidth > 10) + SetPenWidthPoint(nPenWidth); + else + SetPenWidthPixel((GByte)nPenWidth); + } + + //Set the color + pszPenColor = poPenStyle->Color(bIsNull); + if(pszPenColor != NULL) + { + if(pszPenColor[0] == '#') + pszPenColor++; + // The Pen color is an Hexa string that need to be convert in a int + nPenColor = strtol(pszPenColor, NULL, 16); + SetPenColor(nPenColor); + } + + // Set the Id of the Pen, use Pattern if necessary. + if(pszPenName && + (strstr(pszPenName, "mapinfo-pen-") || strstr(pszPenName, "ogr-pen-")) ) + { + pszPenId = strstr(pszPenName, "mapinfo-pen-"); + if( pszPenId != NULL ) + { + nPenId = atoi(pszPenId+12); + SetPenPattern((GByte)nPenId); + } + else + { + pszPenId = strstr(pszPenName, "ogr-pen-"); + if( pszPenId != NULL ) + { + nPenId = atoi(pszPenId+8); + if(nPenId == 0) + nPenId = 2; + SetPenPattern((GByte)nPenId); + } + } + } + else + { + // If no Pen Id, use the Pen Pattern to retreive the Id. + pszPenPattern = poPenStyle->Pattern(bIsNull); + if (bIsNull) + pszPenPattern = NULL; + else + { + if(strcmp(pszPenPattern, "1 1") == 0) + SetPenPattern(3); + else if(strcmp(pszPenPattern, "2 1") == 0) + SetPenPattern(4); + else if(strcmp(pszPenPattern, "3 1") == 0) + SetPenPattern(5); + else if(strcmp(pszPenPattern, "6 1") == 0) + SetPenPattern(6); + else if(strcmp(pszPenPattern, "12 2") == 0) + SetPenPattern(7); + else if(strcmp(pszPenPattern, "24 4") == 0) + SetPenPattern(8); + else if(strcmp(pszPenPattern, "4 3") == 0) + SetPenPattern(9); + else if(strcmp(pszPenPattern, "1 4") == 0) + SetPenPattern(10); + else if(strcmp(pszPenPattern, "4 6") == 0) + SetPenPattern(11); + else if(strcmp(pszPenPattern, "6 4") == 0) + SetPenPattern(12); + else if(strcmp(pszPenPattern, "12 12") == 0) + SetPenPattern(13); + else if(strcmp(pszPenPattern, "8 2 1 2") == 0) + SetPenPattern(14); + else if(strcmp(pszPenPattern, "12 1 1 1") == 0) + SetPenPattern(15); + else if(strcmp(pszPenPattern, "12 1 3 1") == 0) + SetPenPattern(16); + else if(strcmp(pszPenPattern, "24 6 4 6") == 0) + SetPenPattern(17); + else if(strcmp(pszPenPattern, "24 3 3 3 3 3") == 0) + SetPenPattern(18); + else if(strcmp(pszPenPattern, "24 3 3 3 3 3 3 3") == 0) + SetPenPattern(19); + else if(strcmp(pszPenPattern, "6 3 1 3 1 3") == 0) + SetPenPattern(20); + else if(strcmp(pszPenPattern, "12 2 1 2 1 2") == 0) + SetPenPattern(21); + else if(strcmp(pszPenPattern, "12 2 1 2 1 2 1 2") == 0) + SetPenPattern(22); + else if(strcmp(pszPenPattern, "4 1 1 1") == 0) + SetPenPattern(23); + else if(strcmp(pszPenPattern, "4 1 1 1 1") == 0) + SetPenPattern(24); + else if(strcmp(pszPenPattern, "4 1 1 1 2 1 1 1") == 0) + SetPenPattern(25); + } + } + + delete poStyleMgr; + delete poStylePart; + + return; +} + +/********************************************************************** + * ITABFeaturePen::DumpPenDef() + * + * Dump pen definition information. + **********************************************************************/ +void ITABFeaturePen::DumpPenDef(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, " m_nPenDefIndex = %d\n", m_nPenDefIndex); + fprintf(fpOut, " m_sPenDef.nRefCount = %d\n", m_sPenDef.nRefCount); + fprintf(fpOut, " m_sPenDef.nPixelWidth = %d\n", m_sPenDef.nPixelWidth); + fprintf(fpOut, " m_sPenDef.nLinePattern = %d\n", m_sPenDef.nLinePattern); + fprintf(fpOut, " m_sPenDef.nPointWidth = %d\n", m_sPenDef.nPointWidth); + fprintf(fpOut, " m_sPenDef.rgbColor = 0x%6.6x (%d)\n", + m_sPenDef.rgbColor, m_sPenDef.rgbColor); + + fflush(fpOut); +} + +/*===================================================================== + * class ITABFeatureBrush + *====================================================================*/ + +/********************************************************************** + * ITABFeatureBrush::ITABFeatureBrush() + **********************************************************************/ + +ITABFeatureBrush::ITABFeatureBrush() +{ + static const TABBrushDef csDefaultBrush = MITAB_BRUSH_DEFAULT; + + m_nBrushDefIndex=-1; + + /* MI default is BRUSH(2,16777215,16777215) */ + m_sBrushDef = csDefaultBrush; +} + + +/********************************************************************** + * ITABFeatureBrush::GetBrushStyleString() + * + * Return a Brush() string. All representations info for the Brush are here. + **********************************************************************/ +const char *ITABFeatureBrush::GetBrushStyleString() +{ + const char *pszStyle = NULL; + int nOGRStyle = 0; + /* char szPattern[20]; */ + //* szPattern[0] = '\0'; */ + + if (m_sBrushDef.nFillPattern == 1) + nOGRStyle = 1; + else if (m_sBrushDef.nFillPattern == 3) + nOGRStyle = 2; + else if (m_sBrushDef.nFillPattern == 4) + nOGRStyle = 3; + else if (m_sBrushDef.nFillPattern == 5) + nOGRStyle = 5; + else if (m_sBrushDef.nFillPattern == 6) + nOGRStyle = 4; + else if (m_sBrushDef.nFillPattern == 7) + nOGRStyle = 6; + else if (m_sBrushDef.nFillPattern == 8) + nOGRStyle = 7; + + + if (GetBrushTransparent()) + { + /* Omit BG Color for transparent brushes */ + pszStyle =CPLSPrintf("BRUSH(fc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")", + m_sBrushDef.rgbFGColor, + m_sBrushDef.nFillPattern,nOGRStyle); + } + else + { + pszStyle =CPLSPrintf("BRUSH(fc:#%6.6x,bc:#%6.6x,id:\"mapinfo-brush-%d,ogr-brush-%d\")", + m_sBrushDef.rgbFGColor, + m_sBrushDef.rgbBGColor, + m_sBrushDef.nFillPattern,nOGRStyle); + } + + return pszStyle; + +} + + +/********************************************************************** + * ITABFeatureBrush::SetBrushFromStyleString() + * + * Set all Brush elements from a StyleString. + * Use StyleMgr to do so. + **********************************************************************/ +void ITABFeatureBrush::SetBrushFromStyleString(const char *pszStyleString) +{ + int numParts, i; + GBool bIsNull = 0; + + const char *pszBrushId; + int nBrushId; + + const char *pszBrushColor; + int nBrushColor; + + // Use the Style Manager to retreive all the information we need. + OGRStyleMgr *poStyleMgr = new OGRStyleMgr(NULL); + OGRStyleTool *poStylePart; + + // Init the StyleMgr with the StyleString. + poStyleMgr->InitStyleString(pszStyleString); + + // Retreive the Brush info. + numParts = poStyleMgr->GetPartCount(); + for(i=0; iGetPart(i); + if( poStylePart == NULL ) + continue; + + if(poStylePart->GetType() == OGRSTCBrush) + { + break; + } + else + { + delete poStylePart; + poStylePart = NULL; + } + } + + // If the no Brush found, do nothing. + if(i >= numParts) + { + delete poStyleMgr; + return; + } + + OGRStyleBrush *poBrushStyle = (OGRStyleBrush*)poStylePart; + + // Set the Brush Id (FillPattern) + pszBrushId = poBrushStyle->Id(bIsNull); + if(bIsNull) pszBrushId = NULL; + + if(pszBrushId && + (strstr(pszBrushId, "mapinfo-brush-") || + strstr(pszBrushId, "ogr-brush-")) ) + { + if(strstr(pszBrushId, "mapinfo-brush-")) + { + nBrushId = atoi(pszBrushId+14); + SetBrushPattern((GByte)nBrushId); + } + else if(strstr(pszBrushId, "ogr-brush-")) + { + nBrushId = atoi(pszBrushId+10); + if(nBrushId > 1) + nBrushId++; + SetBrushPattern((GByte)nBrushId); + } + } + + // Set the BackColor, if not set, then it's transparent + pszBrushColor = poBrushStyle->BackColor(bIsNull); + if(bIsNull) pszBrushColor = NULL; + + if(pszBrushColor) + { + if(pszBrushColor[0] == '#') + pszBrushColor++; + nBrushColor = strtol(pszBrushColor, NULL, 16); + SetBrushBGColor((GInt32)nBrushColor); + } + else + { + SetBrushTransparent(1); + } + + // Set the ForeColor + pszBrushColor = poBrushStyle->ForeColor(bIsNull); + if(bIsNull) pszBrushColor = NULL; + + if(pszBrushColor) + { + if(pszBrushColor[0] == '#') + pszBrushColor++; + nBrushColor = strtol(pszBrushColor, NULL, 16); + SetBrushFGColor((GInt32)nBrushColor); + } + + delete poStyleMgr; + delete poStylePart; + + return; +} + +/********************************************************************** + * ITABFeatureBrush::DumpBrushDef() + * + * Dump Brush definition information. + **********************************************************************/ +void ITABFeatureBrush::DumpBrushDef(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, " m_nBrushDefIndex = %d\n", m_nBrushDefIndex); + fprintf(fpOut, " m_sBrushDef.nRefCount = %d\n", m_sBrushDef.nRefCount); + fprintf(fpOut, " m_sBrushDef.nFillPattern = %d\n", + (int)m_sBrushDef.nFillPattern); + fprintf(fpOut, " m_sBrushDef.bTransparentFill = %d\n", + (int)m_sBrushDef.bTransparentFill); + fprintf(fpOut, " m_sBrushDef.rgbFGColor = 0x%6.6x (%d)\n", + m_sBrushDef.rgbFGColor, m_sBrushDef.rgbFGColor); + fprintf(fpOut, " m_sBrushDef.rgbBGColor = 0x%6.6x (%d)\n", + m_sBrushDef.rgbBGColor, m_sBrushDef.rgbBGColor); + + fflush(fpOut); +} + +/*===================================================================== + * class ITABFeatureFont + *====================================================================*/ + +/********************************************************************** + * ITABFeatureFont::ITABFeatureFont() + **********************************************************************/ + +ITABFeatureFont::ITABFeatureFont() +{ + static const TABFontDef csDefaultFont = MITAB_FONT_DEFAULT; + + m_nFontDefIndex=-1; + + /* MI default is Font("Arial",0,0,0) */ + m_sFontDef = csDefaultFont; +} + +/********************************************************************** + * ITABFeatureFont::SetFontName() + **********************************************************************/ +void ITABFeatureFont::SetFontName(const char *pszName) +{ + strncpy( m_sFontDef.szFontName, pszName, 32); + m_sFontDef.szFontName[32] = '\0'; +} + +/********************************************************************** + * ITABFeatureFont::DumpFontDef() + * + * Dump Font definition information. + **********************************************************************/ +void ITABFeatureFont::DumpFontDef(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, " m_nFontDefIndex = %d\n", m_nFontDefIndex); + fprintf(fpOut, " m_sFontDef.nRefCount = %d\n", m_sFontDef.nRefCount); + fprintf(fpOut, " m_sFontDef.szFontName = '%s'\n", m_sFontDef.szFontName); + + fflush(fpOut); +} + + +/*===================================================================== + * class ITABFeatureSymbol + *====================================================================*/ + +/********************************************************************** + * ITABFeatureSymbol::ITABFeatureSymbol() + **********************************************************************/ + +ITABFeatureSymbol::ITABFeatureSymbol() +{ + static const TABSymbolDef csDefaultSymbol = MITAB_SYMBOL_DEFAULT; + + m_nSymbolDefIndex=-1; + + /* MI default is Symbol(35,0,12) */ + m_sSymbolDef = csDefaultSymbol; +} + +/********************************************************************** + * ITABFeatureSymbol::GetSymbolStyleString() + * + * Return a Symbol() string. All representations info for the Symbol are here. + **********************************************************************/ +const char *ITABFeatureSymbol::GetSymbolStyleString(double dfAngle) +{ + const char *pszStyle = NULL; + int nOGRStyle = 1; + /* char szPattern[20]; */ + int nAngle = 0; + /* szPattern[0] = '\0'; */ + + if (m_sSymbolDef.nSymbolNo == 31) + nOGRStyle = 0; + else if (m_sSymbolDef.nSymbolNo == 32) + nOGRStyle = 6; + else if (m_sSymbolDef.nSymbolNo == 33) + { + nAngle = 45; + nOGRStyle = 6; + } + else if (m_sSymbolDef.nSymbolNo == 34) + nOGRStyle = 4; + else if (m_sSymbolDef.nSymbolNo == 35) + nOGRStyle = 10; + else if (m_sSymbolDef.nSymbolNo == 36) + nOGRStyle = 8; + else if (m_sSymbolDef.nSymbolNo == 37) + { + nAngle = 180; + nOGRStyle = 8; + } + else if (m_sSymbolDef.nSymbolNo == 38) + nOGRStyle = 5; + else if (m_sSymbolDef.nSymbolNo == 39) + { + nAngle = 45; + nOGRStyle = 5; + } + else if (m_sSymbolDef.nSymbolNo == 40) + nOGRStyle = 3; + else if (m_sSymbolDef.nSymbolNo == 41) + nOGRStyle = 9; + else if (m_sSymbolDef.nSymbolNo == 42) + nOGRStyle = 7; + else if (m_sSymbolDef.nSymbolNo == 43) + { + nAngle = 180; + nOGRStyle = 7; + } + else if (m_sSymbolDef.nSymbolNo == 44) + nOGRStyle = 6; + else if (m_sSymbolDef.nSymbolNo == 45) + nOGRStyle = 8; + else if (m_sSymbolDef.nSymbolNo == 46) + nOGRStyle = 4; + else if (m_sSymbolDef.nSymbolNo == 49) + nOGRStyle = 1; + else if (m_sSymbolDef.nSymbolNo == 50) + nOGRStyle = 2; + + nAngle += (int)dfAngle; + + pszStyle=CPLSPrintf("SYMBOL(a:%d,c:#%6.6x,s:%dpt,id:\"mapinfo-sym-%d,ogr-sym-%d\")", + nAngle, + m_sSymbolDef.rgbColor, + m_sSymbolDef.nPointSize, + m_sSymbolDef.nSymbolNo, + nOGRStyle); + + return pszStyle; + +} + +/********************************************************************** + * ITABFeatureSymbol::SetSymbolFromStyleString() + * + * Set all Symbol var from a StyleString. Use StyleMgr to do so. + **********************************************************************/ +void ITABFeatureSymbol::SetSymbolFromStyleString(const char *pszStyleString) +{ + int numParts, i; + GBool bIsNull = 0; + + const char *pszSymbolId; + int nSymbolId; + + const char *pszSymbolColor; + int nSymbolColor; + + double dSymbolSize; + + // Use the Style Manager to retreive all the information we need. + OGRStyleMgr *poStyleMgr = new OGRStyleMgr(NULL); + OGRStyleTool *poStylePart; + + // Init the StyleMgr with the StyleString. + poStyleMgr->InitStyleString(pszStyleString); + + // Retreive the Symbol info. + numParts = poStyleMgr->GetPartCount(); + for(i=0; iGetPart(i); + if( poStylePart == NULL ) + continue; + + if(poStylePart->GetType() == OGRSTCSymbol) + { + break; + } + else + { + delete poStylePart; + poStylePart = NULL; + } + } + + // If the no Symbol found, do nothing. + if(i >= numParts) + { + delete poStyleMgr; + return; + } + + OGRStyleSymbol *poSymbolStyle = (OGRStyleSymbol*)poStylePart; + + // With Symbol, we always want to output points + // + // It's very important to set the output unit of the feature. + // The default value is meter. If we don't do it all numerical values + // will be assumed to be converted from the input unit to meter when we + // will get them via GetParam...() functions. + // See OGRStyleTool::Parse() for more details. + poSymbolStyle->SetUnit(OGRSTUPoints, (72.0 * 39.37)); + + // Set the Symbol Id (SymbolNo) + pszSymbolId = poSymbolStyle->Id(bIsNull); + if(bIsNull) pszSymbolId = NULL; + + if(pszSymbolId && + (strstr(pszSymbolId, "mapinfo-sym-") || + strstr(pszSymbolId, "ogr-sym-")) ) + { + if(strstr(pszSymbolId, "mapinfo-sym-")) + { + nSymbolId = atoi(pszSymbolId+12); + SetSymbolNo((GByte)nSymbolId); + } + else if(strstr(pszSymbolId, "ogr-sym-")) + { + nSymbolId = atoi(pszSymbolId+8); + + // The OGR symbol is not the MapInfo one + // Here's some mapping + switch (nSymbolId) + { + case 0: + SetSymbolNo(31); + break; + case 1: + SetSymbolNo(49); + break; + case 2: + SetSymbolNo(50); + break; + case 3: + SetSymbolNo(40); + break; + case 4: + SetSymbolNo(34); + break; + case 5: + SetSymbolNo(38); + break; + case 6: + SetSymbolNo(32); + break; + case 7: + SetSymbolNo(42); + break; + case 8: + SetSymbolNo(36); + break; + case 9: + SetSymbolNo(41); + break; + case 10: + SetSymbolNo(35); + break; + } + } + } + + // Set SymbolSize + dSymbolSize = poSymbolStyle->Size(bIsNull); + if(dSymbolSize) + { + SetSymbolSize((GInt16)dSymbolSize); + } + + // Set Symbol Color + pszSymbolColor = poSymbolStyle->Color(bIsNull); + if(pszSymbolColor) + { + if(pszSymbolColor[0] == '#') + pszSymbolColor++; + nSymbolColor = strtol(pszSymbolColor, NULL, 16); + SetSymbolColor((GInt32)nSymbolColor); + } + + delete poStyleMgr; + delete poStylePart; + + return; +} + +/********************************************************************** + * ITABFeatureSymbol::DumpSymbolDef() + * + * Dump Symbol definition information. + **********************************************************************/ +void ITABFeatureSymbol::DumpSymbolDef(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, " m_nSymbolDefIndex = %d\n", m_nSymbolDefIndex); + fprintf(fpOut, " m_sSymbolDef.nRefCount = %d\n", m_sSymbolDef.nRefCount); + fprintf(fpOut, " m_sSymbolDef.nSymbolNo = %d\n", m_sSymbolDef.nSymbolNo); + fprintf(fpOut, " m_sSymbolDef.nPointSize = %d\n",m_sSymbolDef.nPointSize); + fprintf(fpOut, " m_sSymbolDef._unknown_ = %d\n", + (int)m_sSymbolDef._nUnknownValue_); + fprintf(fpOut, " m_sSymbolDef.rgbColor = 0x%6.6x (%d)\n", + m_sSymbolDef.rgbColor, m_sSymbolDef.rgbColor); + + fflush(fpOut); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_feature_mif.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_feature_mif.cpp new file mode 100644 index 000000000..c801fc3cb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_feature_mif.cpp @@ -0,0 +1,2412 @@ +/********************************************************************** + * $Id: mitab_feature_mif.cpp,v 1.39 2010-09-07 16:07:53 aboudreault Exp $ + * + * Name: mitab_feature.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of R/W Fcts for (Mid/Mif) in feature classes + * specific to MapInfo files. + * Author: Stephane Villeneuve, stephane.v@videotron.ca + * + ********************************************************************** + * Copyright (c) 1999-2002, Stephane Villeneuve + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_feature_mif.cpp,v $ + * Revision 1.39 2010-09-07 16:07:53 aboudreault + * Added the use of OGRGeometryFactory::organizePolygons for mif features + * + * Revision 1.38 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.37 2008-12-17 14:55:20 aboudreault + * Fixed mitab mif/mid importer fails when a Text geometry have an empty + * text value (bug 1978) + * + * Revision 1.36 2008-11-27 20:50:22 aboudreault + * Improved support for OGR date/time types. New Read/Write methods (bug 1948) + * Added support of OGR date/time types for MIF features. + * + * Revision 1.35 2008/09/23 14:56:03 aboudreault + * Fixed an error related to the " character when converting mif to tab file. + * + * Revision 1.34 2008/09/23 13:45:03 aboudreault + * Fixed bug with the characters ",\n in the tab2tab application. (bug 1945) + * + * Revision 1.33 2008/02/01 20:30:59 dmorissette + * Use %.15g instead of %.16g as number precision in .MIF output + * + * Revision 1.32 2007/06/07 20:27:21 dmorissette + * Fixed memory leaks when reading multipoint objects from .MIF files + * + * Revision 1.31 2006/01/27 13:44:44 fwarmerdam + * fixed Mills.mif reading, crash at file end + * + * Revision 1.30 2006/01/26 21:26:36 fwarmerdam + * fixed bug with multi character delimeters in .mid file + * + * Revision 1.29 2005/10/04 19:36:10 dmorissette + * Added support for reading collections from MIF files (bug 1126) + * + * Revision 1.28 2005/10/04 15:44:31 dmorissette + * First round of support for Collection objects. Currently supports reading + * from .TAB/.MAP and writing to .MIF. Still lacks symbol support and write + * support. (Based in part on patch and docs from Jim Hope, bug 1126) + * + * Revision 1.27 2005/10/04 15:35:52 dmorissette + * Fixed an instance of hardcoded delimiter (",") in WriteRecordToMIDFile() + * (patch by KB Kieron, bug 1126) + * + * Revision 1.26 2005/07/14 16:15:05 jlacroix + * \n and \ are now unescaped internally. + * + * Revision 1.25 2003/12/19 07:52:34 fwarmerdam + * write 3d as 2d + * + * Revision 1.24 2002/11/27 22:51:52 daniel + * Bug 1631:Do not produce an error if .mid data records end with a stray ',' + * Treat tabs (\t) as a blank space delimiter when reading .mif coordinates + * + * Revision 1.23 2002/10/29 21:09:20 warmerda + * Ensure that a blank line in a mid file is treated as one field containing + * an empty string. + * + * Revision 1.22 2002/04/26 14:16:49 julien + * Finishing the implementation of Multipoint (support for MIF) + * + * Revision 1.21 2002/03/26 01:48:40 daniel + * Added Multipoint object type (V650) + * + * Revision 1.20 2002/01/23 20:31:21 daniel + * Fixed warning produced by CPLAssert() in non-DEBUG mode. + * + * Revision 1.19 2001/06/25 01:50:42 daniel + * Fixed MIF Text object output: negative text angles were lost. Also use + * TABText::SetTextAngle() when reading MIF instead of setting class members + * directly so that negative angles get converted to the [0..360] range. + * + * Revision 1.18 2001/02/28 07:15:09 daniel + * Added support for text label line end point + * + * Revision 1.17 2001/01/22 16:03:58 warmerda + * expanded tabs + * + * Revision 1.16 2000/10/03 19:29:51 daniel + * Include OGR StyleString stuff (implemented by Stephane) + * + * Revision 1.15 2000/09/28 16:39:44 warmerda + * avoid warnings for unused, and unitialized variables + * + * Revision 1.14 2000/09/19 17:23:53 daniel + * Maintain and/or compute valid region and polyline center/label point + * + * Revision 1.13 2000/03/27 03:33:45 daniel + * Treat SYMBOL line as optional when reading TABPoint + * + * Revision 1.12 2000/02/28 16:56:32 daniel + * Support pen width in points (width values 11 to 2047) + * + * Revision 1.11 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.10 2000/01/14 23:51:37 daniel + * Fixed handling of "\n" in TABText strings... now the external interface + * of the lib returns and expects escaped "\"+"n" as described in MIF specs + * + * Revision 1.9 1999/12/19 17:37:14 daniel + * Fixed memory leaks + * + * Revision 1.8 1999/12/19 01:02:50 stephane + * Add a test on the CENTER information + * + * Revision 1.7 1999/12/18 23:23:23 stephane + * Change the format of the output double from %g to %.16g + * + * Revision 1.6 1999/12/18 08:22:57 daniel + * Removed stray break statement in PLINE MULTIPLE write code + * + * Revision 1.5 1999/12/18 07:21:30 daniel + * Fixed test on geometry type when writing OGRMultiLineStrings + * + * Revision 1.4 1999/12/18 07:11:57 daniel + * Return regions as OGRMultiPolygons instead of multiple rings OGRPolygons + * + * Revision 1.3 1999/12/16 17:16:44 daniel + * Use addRing/GeometryDirectly() (prevents leak), and rounded rectangles + * always return real corner radius from file even if it is bigger than MBR + * + * Revision 1.2 1999/11/11 01:22:05 stephane + * Remove DebugFeature call, Point Reading error, add IsValidFeature() to + * test correctly if we are on a feature + * + * Revision 1.1 1999/11/08 19:20:30 stephane + * First version + * + * Revision 1.1 1999/11/08 04:16:07 stephane + * First Revision + * + * + **********************************************************************/ + +#include "mitab.h" +#include "mitab_utils.h" +#include + +/*===================================================================== + * class TABFeature + *====================================================================*/ + +/************************************************************************/ +/* MIDTokenize() */ +/* */ +/* We implement a special tokenize function so we can handle */ +/* multibyte delimeters (ie. MITAB bug 1266). */ +/* */ +/* http://bugzilla.maptools.org/show_bug.cgi?id=1266 */ +/************************************************************************/ +static char **MIDTokenize( const char *pszLine, const char *pszDelim ) + +{ + char **papszResult = NULL; + int iChar, iTokenChar = 0, bInQuotes = FALSE; + char *pszToken = (char *) CPLMalloc(strlen(pszLine)+1); + int nDelimLen = strlen(pszDelim); + + for( iChar = 0; pszLine[iChar] != '\0'; iChar++ ) + { + if( bInQuotes && pszLine[iChar] == '"' && pszLine[iChar+1] == '"' ) + { + pszToken[iTokenChar++] = '"'; + iChar++; + } + else if( pszLine[iChar] == '"' ) + { + bInQuotes = !bInQuotes; + } + else if( !bInQuotes && strncmp(pszLine+iChar,pszDelim,nDelimLen) == 0 ) + { + pszToken[iTokenChar++] = '\0'; + papszResult = CSLAddString( papszResult, pszToken ); + + iChar += strlen(pszDelim) - 1; + iTokenChar = 0; + } + else + { + pszToken[iTokenChar++] = pszLine[iChar]; + } + } + + pszToken[iTokenChar++] = '\0'; + papszResult = CSLAddString( papszResult, pszToken ); + + CPLFree( pszToken ); + + return papszResult; +} + +/********************************************************************** + * TABFeature::ReadRecordFromMIDFile() + * + * This method is used to read the Record (Attributs) for all type of + * feature included in a mid/mif file. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABFeature::ReadRecordFromMIDFile(MIDDATAFile *fp) +{ + const char *pszLine; + char **papszToken; + int nFields,i; + OGRFieldDefn *poFDefn = NULL; +#ifdef MITAB_USE_OFTDATETIME + int nYear, nMonth, nDay, nHour, nMin, nSec, nMS, nTZFlag; + nYear = nMonth = nDay = nHour = nMin = nSec = nMS = nTZFlag = 0; +#endif + + nFields = GetFieldCount(); + + pszLine = fp->GetLastLine(); + + if (pszLine == NULL) + { + CPLError(CE_Failure, CPLE_FileIO, + "Unexpected EOF while reading attribute record from MID file."); + return -1; + } + + papszToken = MIDTokenize( pszLine, fp->GetDelimiter() ); + + // Ensure that a blank line in a mid file is treated as one field + // containing an empty string. + if( nFields == 1 && CSLCount(papszToken) == 0 && pszLine[0] == '\0' ) + papszToken = CSLAddString(papszToken,""); + + // Make sure we found at least the expected number of field values. + // Note that it is possible to have a stray delimiter at the end of + // the line (mif/mid files from Geomedia), so don't produce an error + // if we find more tokens than expected. + if (CSLCount(papszToken) < nFields) + { + CSLDestroy(papszToken); + return -1; + } + + for (i=0;iGetType()) + { +#ifdef MITAB_USE_OFTDATETIME + case OFTTime: + { + if (strlen(papszToken[i]) == 9) + { + sscanf(papszToken[i],"%2d%2d%2d%3d",&nHour, &nMin, &nSec, &nMS); + SetField(i, nYear, nMonth, nDay, nHour, nMin, nSec + nMS / 1000.0f, + 0); + } + break; + } + case OFTDate: + { + if (strlen(papszToken[i]) == 8) + { + sscanf(papszToken[i], "%4d%2d%2d", &nYear, &nMonth, &nDay); + SetField(i, nYear, nMonth, nDay, nHour, nMin, nSec, 0); + } + break; + } + case OFTDateTime: + { + if (strlen(papszToken[i]) == 17) + { + sscanf(papszToken[i], "%4d%2d%2d%2d%2d%2d%3d", + &nYear, &nMonth, &nDay, &nHour, &nMin, &nSec, &nMS); + SetField(i, nYear, nMonth, nDay, nHour, nMin, nSec + nMS / 1000.0f, + 0); + } + break; + } +#endif + + default: + SetField(i,papszToken[i]); + } + } + + fp->GetLine(); + + CSLDestroy(papszToken); + + return 0; +} + +/********************************************************************** + * TABFeature::WriteRecordToMIDFile() + * + * This methode is used to write the Record (Attributs) for all type + * of feature included in a mid file. + * + * Return 0 on success, -1 on error + **********************************************************************/ +int TABFeature::WriteRecordToMIDFile(MIDDATAFile *fp) +{ + int iField, numFields; + OGRFieldDefn *poFDefn = NULL; +#ifdef MITAB_USE_OFTDATETIME + char szBuffer[20]; + int nYear, nMonth, nDay, nHour, nMin, nMS, nTZFlag; + nYear = nMonth = nDay = nHour = nMin = nMS = nTZFlag = 0; + float fSec = 0.0f; +#endif + + CPLAssert(fp); + + const char *delimiter = fp->GetDelimiter(); + + numFields = GetFieldCount(); + + for(iField=0; iFieldWriteLine("%s", delimiter); + poFDefn = GetFieldDefnRef( iField ); + + switch(poFDefn->GetType()) + { + case OFTString: + { + int nStringLen = strlen(GetFieldAsString(iField)); + char *pszString = (char*)CPLMalloc((nStringLen+1)*sizeof(char)); + strcpy(pszString, GetFieldAsString(iField)); + char *pszWorkString = (char*)CPLMalloc((2*(nStringLen)+1)*sizeof(char)); + int j = 0; + for (int i =0; i < nStringLen; ++i) + { + if (pszString[i] == '"') + { + pszWorkString[j] = pszString[i]; + ++j; + pszWorkString[j] = pszString[i]; + } + else if (pszString[i] == '\n') + { + pszWorkString[j] = '\\'; + ++j; + pszWorkString[j] = 'n'; + } + else + pszWorkString[j] = pszString[i]; + ++j; + } + + pszWorkString[j] = '\0'; + CPLFree(pszString); + pszString = (char*)CPLMalloc((strlen(pszWorkString)+1)*sizeof(char)); + strcpy(pszString, pszWorkString); + CPLFree(pszWorkString); + fp->WriteLine("\"%s\"",pszString); + CPLFree(pszString); + break; + } +#ifdef MITAB_USE_OFTDATETIME + case OFTTime: + { + if (!IsFieldSet(iField)) + { + szBuffer[0] = '\0'; + } + else + { + GetFieldAsDateTime(iField, &nYear, &nMonth, &nDay, + &nHour, &nMin, &fSec, &nTZFlag); + sprintf(szBuffer, "%2.2d%2.2d%2.2d%3.3d", nHour, nMin, + (int)fSec, OGR_GET_MS(fSec)); + } + fp->WriteLine("%s",szBuffer); + break; + } + case OFTDate: + { + if (!IsFieldSet(iField)) + { + szBuffer[0] = '\0'; + } + else + { + GetFieldAsDateTime(iField, &nYear, &nMonth, &nDay, + &nHour, &nMin, &fSec, &nTZFlag); + sprintf(szBuffer, "%4.4d%2.2d%2.2d", nYear, nMonth, nDay); + } + fp->WriteLine("%s",szBuffer); + break; + } + case OFTDateTime: + { + if (!IsFieldSet(iField)) + { + szBuffer[0] = '\0'; + } + else + { + GetFieldAsDateTime(iField, &nYear, &nMonth, &nDay, + &nHour, &nMin, &fSec, &nTZFlag); + sprintf(szBuffer, "%4.4d%2.2d%2.2d%2.2d%2.2d%2.2d%3.3d", + nYear, nMonth, nDay, nHour, nMin, + (int)fSec, OGR_GET_MS(fSec)); + } + fp->WriteLine("%s",szBuffer); + break; + } +#endif + default: + fp->WriteLine("%s",GetFieldAsString(iField)); + } + } + + fp->WriteLine("\n"); + + return 0; +} + +/********************************************************************** + * TABFeature::ReadGeometryFromMIFFile() + * + * In derived classes, this method should be reimplemented to + * fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MAP object pointed to by poMAPFile. + * + * It is assumed that before calling ReadGeometryFromMAPFile(), poMAPFile + * currently points to the beginning of a map object. + * + * The current implementation does nothing since instances of TABFeature + * objects contain no geometry (i.e. TAB_GEOM_NONE). + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABFeature::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + const char *pszLine; + + /* Go to the first line of the next feature */ + + while (((pszLine = fp->GetLine()) != NULL) && + fp->IsValidFeature(pszLine) == FALSE) + ; + + return 0; +} + +/********************************************************************** + * TABFeature::WriteGeometryToMIFFile() + * + * + * In derived classes, this method should be reimplemented to + * write the geometry and representation (color, etc...) part of the + * feature to the .MAP object pointed to by poMAPFile. + * + * It is assumed that before calling WriteGeometryToMAPFile(), poMAPFile + * currently points to a valid map object. + * + * The current implementation does nothing since instances of TABFeature + * objects contain no geometry. + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABFeature::WriteGeometryToMIFFile(MIDDATAFile *fp) +{ + fp->WriteLine("NONE\n"); + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + OGRGeometry *poGeometry; + + char **papszToken; + const char *pszLine; + double dfX,dfY; + papszToken = CSLTokenizeString2(fp->GetSavedLine(), + " \t", CSLT_HONOURSTRINGS); + + if (CSLCount(papszToken) !=3) + { + CSLDestroy(papszToken); + return -1; + } + + dfX = fp->GetXTrans(CPLAtof(papszToken[1])); + dfY = fp->GetYTrans(CPLAtof(papszToken[2])); + + CSLDestroy(papszToken); + papszToken = NULL; + + // Read optional SYMBOL line... + pszLine = fp->GetLastLine(); + if( pszLine != NULL ) + papszToken = CSLTokenizeStringComplex(pszLine," ,()\t", + TRUE,FALSE); + if (CSLCount(papszToken) == 4 && EQUAL(papszToken[0], "SYMBOL") ) + { + SetSymbolNo((GInt16)atoi(papszToken[1])); + SetSymbolColor((GInt32)atoi(papszToken[2])); + SetSymbolSize((GInt16)atoi(papszToken[3])); + } + + CSLDestroy(papszToken); + papszToken = NULL; + + // scan until we reach 1st line of next feature + // Since SYMBOL is optional, we have to test IsValidFeature() on that + // line as well. + while (pszLine && fp->IsValidFeature(pszLine) == FALSE) + { + pszLine = fp->GetLine(); + } + + poGeometry = new OGRPoint(dfX, dfY); + + SetGeometryDirectly(poGeometry); + + SetMBR(dfX, dfY, dfX, dfY); + + + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABPoint::WriteGeometryToMIFFile(MIDDATAFile *fp) +{ + OGRGeometry *poGeom; + OGRPoint *poPoint; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + poPoint = (OGRPoint*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPoint: Missing or Invalid Geometry!"); + return -1; + } + + fp->WriteLine("Point %.15g %.15g\n",poPoint->getX(),poPoint->getY()); + fp->WriteLine(" Symbol (%d,%d,%d)\n",GetSymbolNo(),GetSymbolColor(), + GetSymbolSize()); + + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABFontPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + OGRGeometry *poGeometry; + + char **papszToken; + const char *pszLine; + double dfX,dfY; + papszToken = CSLTokenizeString2(fp->GetSavedLine(), + " \t", CSLT_HONOURSTRINGS); + + if (CSLCount(papszToken) !=3) + { + CSLDestroy(papszToken); + return -1; + } + + dfX = fp->GetXTrans(CPLAtof(papszToken[1])); + dfY = fp->GetYTrans(CPLAtof(papszToken[2])); + + CSLDestroy(papszToken); + + papszToken = CSLTokenizeStringComplex(fp->GetLastLine()," ,()\t", + TRUE,FALSE); + + if (CSLCount(papszToken) !=7) + { + CSLDestroy(papszToken); + return -1; + } + + SetSymbolNo((GInt16)atoi(papszToken[1])); + SetSymbolColor((GInt32)atoi(papszToken[2])); + SetSymbolSize((GInt16)atoi(papszToken[3])); + SetFontName(papszToken[4]); + SetFontStyleMIFValue(atoi(papszToken[5])); + SetSymbolAngle(CPLAtof(papszToken[6])); + + CSLDestroy(papszToken); + + poGeometry = new OGRPoint(dfX, dfY); + + SetGeometryDirectly(poGeometry); + + SetMBR(dfX, dfY, dfX, dfY); + + /* Go to the first line of the next feature */ + + while (((pszLine = fp->GetLine()) != NULL) && + fp->IsValidFeature(pszLine) == FALSE) + ; + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABFontPoint::WriteGeometryToMIFFile(MIDDATAFile *fp) +{ + OGRGeometry *poGeom; + OGRPoint *poPoint; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + poPoint = (OGRPoint*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABFontPoint: Missing or Invalid Geometry!"); + return -1; + } + + fp->WriteLine("Point %.15g %.15g\n",poPoint->getX(),poPoint->getY()); + fp->WriteLine(" Symbol (%d,%d,%d,\"%s\",%d,%.15g)\n", + GetSymbolNo(),GetSymbolColor(), + GetSymbolSize(),GetFontNameRef(),GetFontStyleMIFValue(), + GetSymbolAngle()); + + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABCustomPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + OGRGeometry *poGeometry; + + char **papszToken; + const char *pszLine; + double dfX,dfY; + + papszToken = CSLTokenizeString2(fp->GetSavedLine(), + " \t", CSLT_HONOURSTRINGS); + + + if (CSLCount(papszToken) !=3) + { + CSLDestroy(papszToken); + return -1; + } + + dfX = fp->GetXTrans(CPLAtof(papszToken[1])); + dfY = fp->GetYTrans(CPLAtof(papszToken[2])); + + CSLDestroy(papszToken); + + papszToken = CSLTokenizeStringComplex(fp->GetLastLine()," ,()\t", + TRUE,FALSE); + if (CSLCount(papszToken) !=5) + { + + CSLDestroy(papszToken); + return -1; + } + + SetFontName(papszToken[1]); + SetSymbolColor((GInt32)atoi(papszToken[2])); + SetSymbolSize((GInt16)atoi(papszToken[3])); + m_nCustomStyle = (GByte)atoi(papszToken[4]); + + CSLDestroy(papszToken); + + poGeometry = new OGRPoint(dfX, dfY); + + SetGeometryDirectly(poGeometry); + + SetMBR(dfX, dfY, dfX, dfY); + + /* Go to the first line of the next feature */ + + while (((pszLine = fp->GetLine()) != NULL) && + fp->IsValidFeature(pszLine) == FALSE) + ; + + return 0; + +} + +/********************************************************************** + * + **********************************************************************/ +int TABCustomPoint::WriteGeometryToMIFFile(MIDDATAFile *fp) +{ + OGRGeometry *poGeom; + OGRPoint *poPoint; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + poPoint = (OGRPoint*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABCustomPoint: Missing or Invalid Geometry!"); + return -1; + } + + + fp->WriteLine("Point %.15g %.15g\n",poPoint->getX(),poPoint->getY()); + fp->WriteLine(" Symbol (\"%s\",%d,%d,%d)\n",GetFontNameRef(), + GetSymbolColor(), GetSymbolSize(),m_nCustomStyle); + + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABPolyline::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + const char *pszLine; + char **papszToken; + OGRLineString *poLine; + OGRMultiLineString *poMultiLine; + GBool bMultiple = FALSE; + int nNumPoints,nNumSec=0,i,j; + OGREnvelope sEnvelope; + + + papszToken = CSLTokenizeString2(fp->GetLastLine(), + " \t", CSLT_HONOURSTRINGS); + + if (CSLCount(papszToken) < 1) + { + CSLDestroy(papszToken); + return -1; + } + + if (EQUALN(papszToken[0],"LINE",4)) + { + if (CSLCount(papszToken) != 5) + return -1; + + poLine = new OGRLineString(); + poLine->setNumPoints(2); + poLine->setPoint(0, fp->GetXTrans(CPLAtof(papszToken[1])), + fp->GetYTrans(CPLAtof(papszToken[2]))); + poLine->setPoint(1, fp->GetXTrans(CPLAtof(papszToken[3])), + fp->GetYTrans(CPLAtof(papszToken[4]))); + SetGeometryDirectly(poLine); + poLine->getEnvelope(&sEnvelope); + SetMBR(sEnvelope.MinX, sEnvelope.MinY,sEnvelope.MaxX,sEnvelope.MaxY); + } + else if (EQUALN(papszToken[0],"PLINE",5)) + { + switch (CSLCount(papszToken)) + { + case 1: + bMultiple = FALSE; + pszLine = fp->GetLine(); + nNumPoints = atoi(pszLine); + break; + case 2: + bMultiple = FALSE; + nNumPoints = atoi(papszToken[1]); + break; + case 3: + if (EQUALN(papszToken[1],"MULTIPLE",8)) + { + bMultiple = TRUE; + nNumSec = atoi(papszToken[2]); + pszLine = fp->GetLine(); + nNumPoints = atoi(pszLine); + break; + } + else + { + CSLDestroy(papszToken); + return -1; + } + break; + case 4: + if (EQUALN(papszToken[1],"MULTIPLE",8)) + { + bMultiple = TRUE; + nNumSec = atoi(papszToken[2]); + nNumPoints = atoi(papszToken[3]); + break; + } + else + { + CSLDestroy(papszToken); + return -1; + } + break; + default: + CSLDestroy(papszToken); + return -1; + break; + } + + if (bMultiple) + { + poMultiLine = new OGRMultiLineString(); + for (j=0;jGetLine()); + if (nNumPoints < 2) + { + CPLError(CE_Failure, CPLE_FileIO, + "Invalid number of vertices (%d) in PLINE " + "MULTIPLE segment.", nNumPoints); + return -1; + } + poLine->setNumPoints(nNumPoints); + for (i=0;iGetLine(), + " \t", CSLT_HONOURSTRINGS); + poLine->setPoint(i,fp->GetXTrans(CPLAtof(papszToken[0])), + fp->GetYTrans(CPLAtof(papszToken[1]))); + } + if (poMultiLine->addGeometryDirectly(poLine) != OGRERR_NONE) + { + CPLAssert(FALSE); // Just in case OGR is modified + } + } + if (SetGeometryDirectly(poMultiLine) != OGRERR_NONE) + { + CPLAssert(FALSE); // Just in case OGR is modified + } + poMultiLine->getEnvelope(&sEnvelope); + SetMBR(sEnvelope.MinX, sEnvelope.MinY, + sEnvelope.MaxX,sEnvelope.MaxY); + } + else + { + poLine = new OGRLineString(); + poLine->setNumPoints(nNumPoints); + for (i=0;iGetLine(), + " \t", CSLT_HONOURSTRINGS); + + if (CSLCount(papszToken) != 2) + return -1; + poLine->setPoint(i,fp->GetXTrans(CPLAtof(papszToken[0])), + fp->GetYTrans(CPLAtof(papszToken[1]))); + } + SetGeometryDirectly(poLine); + poLine->getEnvelope(&sEnvelope); + SetMBR(sEnvelope.MinX, sEnvelope.MinY, + sEnvelope.MaxX,sEnvelope.MaxY); + } + } + + CSLDestroy(papszToken); + papszToken = NULL; + + while (((pszLine = fp->GetLine()) != NULL) && + fp->IsValidFeature(pszLine) == FALSE) + { + papszToken = CSLTokenizeStringComplex(pszLine,"() ,", + TRUE,FALSE); + + if (CSLCount(papszToken) >= 1) + { + if (EQUALN(papszToken[0],"PEN",3)) + { + + if (CSLCount(papszToken) == 4) + { + SetPenWidthMIF(atoi(papszToken[1])); + SetPenPattern((GByte)atoi(papszToken[2])); + SetPenColor((GInt32)atoi(papszToken[3])); + } + + } + else if (EQUALN(papszToken[0],"SMOOTH",6)) + { + m_bSmooth = TRUE; + } + } + CSLDestroy(papszToken); + } + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABPolyline::WriteGeometryToMIFFile(MIDDATAFile *fp) +{ + OGRGeometry *poGeom; + OGRMultiLineString *poMultiLine = NULL; + OGRLineString *poLine = NULL; + int nNumPoints,i; + + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) + { + /*------------------------------------------------------------- + * Simple polyline + *------------------------------------------------------------*/ + poLine = (OGRLineString*)poGeom; + nNumPoints = poLine->getNumPoints(); + if (nNumPoints == 2) + { + fp->WriteLine("Line %.15g %.15g %.15g %.15g\n",poLine->getX(0),poLine->getY(0), + poLine->getX(1),poLine->getY(1)); + } + else + { + + fp->WriteLine("Pline %d\n",nNumPoints); + for (i=0;iWriteLine("%.15g %.15g\n",poLine->getX(i),poLine->getY(i)); + } + } + } + else if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString) + { + /*------------------------------------------------------------- + * Multiple polyline... validate all components + *------------------------------------------------------------*/ + int iLine, numLines; + poMultiLine = (OGRMultiLineString*)poGeom; + numLines = poMultiLine->getNumGeometries(); + + fp->WriteLine("PLINE MULTIPLE %d\n", numLines); + + for(iLine=0; iLine < numLines; iLine++) + { + poGeom = poMultiLine->getGeometryRef(iLine); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbLineString) + { + poLine = (OGRLineString*)poGeom; + nNumPoints = poLine->getNumPoints(); + + fp->WriteLine(" %d\n",nNumPoints); + for (i=0;iWriteLine("%.15g %.15g\n",poLine->getX(i),poLine->getY(i)); + } + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPolyline: Object contains an invalid Geometry!"); + } + } + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABPolyline: Missing or Invalid Geometry!"); + } + + if (GetPenPattern()) + fp->WriteLine(" Pen (%d,%d,%d)\n",GetPenWidthMIF(),GetPenPattern(), + GetPenColor()); + if (m_bSmooth) + fp->WriteLine(" Smooth\n"); + + return 0; + +} + +/********************************************************************** + * TABRegion::ReadGeometryFromMIFFile() + * + * Fill the geometry and representation (color, etc...) part of the + * feature from the contents of the .MIF file + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABRegion::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + double dX, dY; + OGRLinearRing *poRing; + OGRGeometry *poGeometry = NULL; + OGRPolygon **tabPolygons = NULL; + int i,iSection, numLineSections=0; + char **papszToken; + const char *pszLine; + OGREnvelope sEnvelope; + + m_bSmooth = FALSE; + /*============================================================= + * REGION (Similar to PLINE MULTIPLE) + *============================================================*/ + papszToken = CSLTokenizeString2(fp->GetLastLine(), + " \t", CSLT_HONOURSTRINGS); + + if (CSLCount(papszToken) ==2) + numLineSections = atoi(papszToken[1]); + CSLDestroy(papszToken); + papszToken = NULL; + + if (numLineSections > 0) + tabPolygons = new OGRPolygon*[numLineSections]; + + for(iSection=0; iSectionGetLine()) != NULL) + { + numSectionVertices = atoi(pszLine); + } + + poRing = new OGRLinearRing(); + poRing->setNumPoints(numSectionVertices); + + for(i=0; iGetLine(); + if (pszLine) + { + papszToken = CSLTokenizeStringComplex(pszLine," ,\t", + TRUE,FALSE); + if (CSLCount(papszToken) == 2) + { + dX = fp->GetXTrans(CPLAtof(papszToken[0])); + dY = fp->GetYTrans(CPLAtof(papszToken[1])); + poRing->setPoint(i, dX, dY); + } + CSLDestroy(papszToken); + papszToken = NULL; + } + } + + poRing->closeRings(); + + tabPolygons[iSection]->addRingDirectly(poRing); + + if (numLineSections == 1) + poGeometry = tabPolygons[iSection]; + + poRing = NULL; + } + + if (numLineSections > 1) + { + int isValidGeometry; + const char* papszOptions[] = { "METHOD=DEFAULT", NULL }; + poGeometry = OGRGeometryFactory::organizePolygons( + (OGRGeometry**)tabPolygons, numLineSections, &isValidGeometry, papszOptions ); + + if (!isValidGeometry) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Geometry of polygon cannot be translated to Simple Geometry. " + "All polygons will be contained in a multipolygon.\n"); + } + } + + if (tabPolygons) + delete[] tabPolygons; + + SetGeometryDirectly(poGeometry); + poGeometry->getEnvelope(&sEnvelope); + + SetMBR(sEnvelope.MinX, sEnvelope.MinY, sEnvelope.MaxX, sEnvelope.MaxY); + + while (((pszLine = fp->GetLine()) != NULL) && + fp->IsValidFeature(pszLine) == FALSE) + { + papszToken = CSLTokenizeStringComplex(pszLine,"() ,", + TRUE,FALSE); + + if (CSLCount(papszToken) > 1) + { + if (EQUALN(papszToken[0],"PEN",3)) + { + + if (CSLCount(papszToken) == 4) + { + SetPenWidthMIF(atoi(papszToken[1])); + SetPenPattern((GByte)atoi(papszToken[2])); + SetPenColor((GInt32)atoi(papszToken[3])); + } + + } + else if (EQUALN(papszToken[0],"BRUSH", 5)) + { + if (CSLCount(papszToken) >= 3) + { + SetBrushFGColor((GInt32)atoi(papszToken[2])); + SetBrushPattern((GByte)atoi(papszToken[1])); + + if (CSLCount(papszToken) == 4) + SetBrushBGColor(atoi(papszToken[3])); + else + SetBrushTransparent(TRUE); + } + + } + else if (EQUALN(papszToken[0],"CENTER",6)) + { + if (CSLCount(papszToken) == 3) + { + SetCenter(fp->GetXTrans(CPLAtof(papszToken[1])), + fp->GetYTrans(CPLAtof(papszToken[2])) ); + } + } + } + CSLDestroy(papszToken); + papszToken = NULL; + } + + + return 0; +} + +/********************************************************************** + * TABRegion::WriteGeometryToMIFFile() + * + * Write the geometry and representation (color, etc...) part of the + * feature to the .MIF file + * + * Returns 0 on success, -1 on error, in which case CPLError() will have + * been called. + **********************************************************************/ +int TABRegion::WriteGeometryToMIFFile(MIDDATAFile *fp) +{ + OGRGeometry *poGeom; + + poGeom = GetGeometryRef(); + + if (poGeom && (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon || + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ) ) + { + /*============================================================= + * REGIONs are similar to PLINE MULTIPLE + * + * We accept both OGRPolygons (with one or multiple rings) and + * OGRMultiPolygons as input. + *============================================================*/ + int i, iRing, numRingsTotal, numPoints; + + numRingsTotal = GetNumRings(); + + fp->WriteLine("Region %d\n",numRingsTotal); + + for(iRing=0; iRing < numRingsTotal; iRing++) + { + OGRLinearRing *poRing; + + poRing = GetRingRef(iRing); + if (poRing == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRegion: Object Geometry contains NULL rings!"); + return -1; + } + + numPoints = poRing->getNumPoints(); + + fp->WriteLine(" %d\n",numPoints); + for(i=0; iWriteLine("%.15g %.15g\n",poRing->getX(i), poRing->getY(i)); + } + } + + if (GetPenPattern()) + fp->WriteLine(" Pen (%d,%d,%d)\n", + GetPenWidthMIF(),GetPenPattern(), + GetPenColor()); + + + if (GetBrushPattern()) + { + if (GetBrushTransparent() == 0) + fp->WriteLine(" Brush (%d,%d,%d)\n",GetBrushPattern(), + GetBrushFGColor(),GetBrushBGColor()); + else + fp->WriteLine(" Brush (%d,%d)\n",GetBrushPattern(), + GetBrushFGColor()); + } + + if (m_bCenterIsSet) + { + fp->WriteLine(" Center %.15g %.15g\n", m_dCenterX, m_dCenterY); + } + + + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRegion: Object contains an invalid Geometry!"); + return -1; + } + + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABRectangle::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + const char *pszLine; + char **papszToken; + double dXMin, dYMin, dXMax, dYMax; + OGRPolygon *poPolygon; + OGRLinearRing *poRing; + + papszToken = CSLTokenizeString2(fp->GetLastLine(), + " \t", CSLT_HONOURSTRINGS); + + if (CSLCount(papszToken) < 5) + { + CSLDestroy(papszToken); + return -1; + } + + dXMin = fp->GetXTrans(CPLAtof(papszToken[1])); + dXMax = fp->GetXTrans(CPLAtof(papszToken[3])); + dYMin = fp->GetYTrans(CPLAtof(papszToken[2])); + dYMax = fp->GetYTrans(CPLAtof(papszToken[4])); + + /*----------------------------------------------------------------- + * Call SetMBR() and GetMBR() now to make sure that min values are + * really smaller than max values. + *----------------------------------------------------------------*/ + SetMBR(dXMin, dYMin, dXMax, dYMax); + GetMBR(dXMin, dYMin, dXMax, dYMax); + + m_bRoundCorners = FALSE; + m_dRoundXRadius = 0.0; + m_dRoundYRadius = 0.0; + + if (EQUALN(papszToken[0],"ROUNDRECT",9)) + { + m_bRoundCorners = TRUE; + if (CSLCount(papszToken) == 6) + m_dRoundXRadius = m_dRoundYRadius = CPLAtof(papszToken[5])/2.0; + else + { + CSLDestroy(papszToken); + papszToken = CSLTokenizeString2(fp->GetLine(), + " \t", CSLT_HONOURSTRINGS); + if (CSLCount(papszToken) !=1 ) + m_dRoundXRadius = m_dRoundYRadius = CPLAtof(papszToken[1])/2.0; + } + } + CSLDestroy(papszToken); + papszToken = NULL; + + /*----------------------------------------------------------------- + * Create and fill geometry object + *----------------------------------------------------------------*/ + + poPolygon = new OGRPolygon; + poRing = new OGRLinearRing(); + if (m_bRoundCorners && m_dRoundXRadius != 0.0 && m_dRoundYRadius != 0.0) + { + /*------------------------------------------------------------- + * For rounded rectangles, we generate arcs with 45 line + * segments for each corner. We start with lower-left corner + * and proceed counterclockwise + * We also have to make sure that rounding radius is not too + * large for the MBR however, we + * always return the true X/Y radius (not adjusted) since this + * is the way MapInfo seems to do it when a radius bigger than + * the MBR is passed from TBA to MIF. + *------------------------------------------------------------*/ + double dXRadius = MIN(m_dRoundXRadius, (dXMax-dXMin)/2.0); + double dYRadius = MIN(m_dRoundYRadius, (dYMax-dYMin)/2.0); + TABGenerateArc(poRing, 45, + dXMin + dXRadius, dYMin + dYRadius, dXRadius, dYRadius, + PI, 3.0*PI/2.0); + TABGenerateArc(poRing, 45, + dXMax - dXRadius, dYMin + dYRadius, dXRadius, dYRadius, + 3.0*PI/2.0, 2.0*PI); + TABGenerateArc(poRing, 45, + dXMax - dXRadius, dYMax - dYRadius, dXRadius, dYRadius, + 0.0, PI/2.0); + TABGenerateArc(poRing, 45, + dXMin + dXRadius, dYMax - dYRadius, dXRadius, dYRadius, + PI/2.0, PI); + + TABCloseRing(poRing); + } + else + { + poRing->addPoint(dXMin, dYMin); + poRing->addPoint(dXMax, dYMin); + poRing->addPoint(dXMax, dYMax); + poRing->addPoint(dXMin, dYMax); + poRing->addPoint(dXMin, dYMin); + } + + poPolygon->addRingDirectly(poRing); + SetGeometryDirectly(poPolygon); + + + while (((pszLine = fp->GetLine()) != NULL) && + fp->IsValidFeature(pszLine) == FALSE) + { + papszToken = CSLTokenizeStringComplex(pszLine,"() ,", + TRUE,FALSE); + + if (CSLCount(papszToken) > 1) + { + if (EQUALN(papszToken[0],"PEN",3)) + { + if (CSLCount(papszToken) == 4) + { + SetPenWidthMIF(atoi(papszToken[1])); + SetPenPattern((GByte)atoi(papszToken[2])); + SetPenColor((GInt32)atoi(papszToken[3])); + } + + } + else if (EQUALN(papszToken[0],"BRUSH", 5)) + { + if (CSLCount(papszToken) >=3) + { + SetBrushFGColor((GInt32)atoi(papszToken[2])); + SetBrushPattern((GByte)atoi(papszToken[1])); + + if (CSLCount(papszToken) == 4) + SetBrushBGColor(atoi(papszToken[3])); + else + SetBrushTransparent(TRUE); + } + + } + } + CSLDestroy(papszToken); + papszToken = NULL; + } + + return 0; + +} + + +/********************************************************************** + * + **********************************************************************/ +int TABRectangle::WriteGeometryToMIFFile(MIDDATAFile *fp) +{ + OGRGeometry *poGeom; + OGRPolygon *poPolygon; + OGREnvelope sEnvelope; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon) + poPolygon = (OGRPolygon*)poGeom; + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRectangle: Missing or Invalid Geometry!"); + return -1; + } + /*----------------------------------------------------------------- + * Note that we will simply use the rectangle's MBR and don't really + * read the polygon geometry... this should be OK unless the + * polygon geometry was not really a rectangle. + *----------------------------------------------------------------*/ + poPolygon->getEnvelope(&sEnvelope); + + if (m_bRoundCorners == TRUE) + { + fp->WriteLine("Roundrect %.15g %.15g %.15g %.15g %.15g\n", + sEnvelope.MinX, sEnvelope.MinY, + sEnvelope.MaxX, sEnvelope.MaxY, m_dRoundXRadius*2.0); + } + else + { + fp->WriteLine("Rect %.15g %.15g %.15g %.15g\n", + sEnvelope.MinX, sEnvelope.MinY, + sEnvelope.MaxX, sEnvelope.MaxY); + } + + if (GetPenPattern()) + fp->WriteLine(" Pen (%d,%d,%d)\n",GetPenWidthMIF(),GetPenPattern(), + GetPenColor()); + + if (GetBrushPattern()) + { + if (GetBrushTransparent() == 0) + fp->WriteLine(" Brush (%d,%d,%d)\n",GetBrushPattern(), + GetBrushFGColor(),GetBrushBGColor()); + else + fp->WriteLine(" Brush (%d,%d)\n",GetBrushPattern(), + GetBrushFGColor()); + } + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABEllipse::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + const char *pszLine; + char **papszToken; + double dXMin, dYMin, dXMax, dYMax; + OGRPolygon *poPolygon; + OGRLinearRing *poRing; + + papszToken = CSLTokenizeString2(fp->GetLastLine(), + " \t", CSLT_HONOURSTRINGS); + + if (CSLCount(papszToken) != 5) + { + CSLDestroy(papszToken); + return -1; + } + + dXMin = fp->GetXTrans(CPLAtof(papszToken[1])); + dXMax = fp->GetXTrans(CPLAtof(papszToken[3])); + dYMin = fp->GetYTrans(CPLAtof(papszToken[2])); + dYMax = fp->GetYTrans(CPLAtof(papszToken[4])); + + CSLDestroy(papszToken); + papszToken = NULL; + + /*----------------------------------------------------------------- + * Save info about the ellipse def. inside class members + *----------------------------------------------------------------*/ + m_dCenterX = (dXMin + dXMax) / 2.0; + m_dCenterY = (dYMin + dYMax) / 2.0; + m_dXRadius = ABS( (dXMax - dXMin) / 2.0 ); + m_dYRadius = ABS( (dYMax - dYMin) / 2.0 ); + + SetMBR(dXMin, dYMin, dXMax, dYMax); + + /*----------------------------------------------------------------- + * Create and fill geometry object + *----------------------------------------------------------------*/ + poPolygon = new OGRPolygon; + poRing = new OGRLinearRing(); + + /*----------------------------------------------------------------- + * For the OGR geometry, we generate an ellipse with 2 degrees line + * segments. + *----------------------------------------------------------------*/ + TABGenerateArc(poRing, 180, + m_dCenterX, m_dCenterY, + m_dXRadius, m_dYRadius, + 0.0, 2.0*PI); + TABCloseRing(poRing); + + poPolygon->addRingDirectly(poRing); + SetGeometryDirectly(poPolygon); + + while (((pszLine = fp->GetLine()) != NULL) && + fp->IsValidFeature(pszLine) == FALSE) + { + papszToken = CSLTokenizeStringComplex(pszLine,"() ,", + TRUE,FALSE); + + if (CSLCount(papszToken) > 1) + { + if (EQUALN(papszToken[0],"PEN",3)) + { + if (CSLCount(papszToken) == 4) + { + SetPenWidthMIF(atoi(papszToken[1])); + SetPenPattern((GByte)atoi(papszToken[2])); + SetPenColor((GInt32)atoi(papszToken[3])); + } + + } + else if (EQUALN(papszToken[0],"BRUSH", 5)) + { + if (CSLCount(papszToken) >= 3) + { + SetBrushFGColor((GInt32)atoi(papszToken[2])); + SetBrushPattern((GByte)atoi(papszToken[1])); + + if (CSLCount(papszToken) == 4) + SetBrushBGColor(atoi(papszToken[3])); + else + SetBrushTransparent(TRUE); + + } + + } + } + CSLDestroy(papszToken); + papszToken = NULL; + } + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABEllipse::WriteGeometryToMIFFile(MIDDATAFile *fp) +{ + OGRGeometry *poGeom; + OGREnvelope sEnvelope; + + poGeom = GetGeometryRef(); + if ( (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) || + (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) ) + poGeom->getEnvelope(&sEnvelope); + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABEllipse: Missing or Invalid Geometry!"); + return -1; + } + + fp->WriteLine("Ellipse %.15g %.15g %.15g %.15g\n",sEnvelope.MinX, sEnvelope.MinY, + sEnvelope.MaxX,sEnvelope.MaxY); + + if (GetPenPattern()) + fp->WriteLine(" Pen (%d,%d,%d)\n",GetPenWidthMIF(),GetPenPattern(), + GetPenColor()); + + if (GetBrushPattern()) + { + if (GetBrushTransparent() == 0) + fp->WriteLine(" Brush (%d,%d,%d)\n",GetBrushPattern(), + GetBrushFGColor(),GetBrushBGColor()); + else + fp->WriteLine(" Brush (%d,%d)\n",GetBrushPattern(), + GetBrushFGColor()); + } + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABArc::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + const char *pszLine; + OGRLineString *poLine; + char **papszToken; + double dXMin,dXMax, dYMin,dYMax; + int numPts; + + papszToken = CSLTokenizeString2(fp->GetLastLine(), + " \t", CSLT_HONOURSTRINGS); + + if (CSLCount(papszToken) == 5) + { + dXMin = fp->GetXTrans(CPLAtof(papszToken[1])); + dXMax = fp->GetXTrans(CPLAtof(papszToken[3])); + dYMin = fp->GetYTrans(CPLAtof(papszToken[2])); + dYMax = fp->GetYTrans(CPLAtof(papszToken[4])); + + CSLDestroy(papszToken); + papszToken = CSLTokenizeString2(fp->GetLine(), + " \t", CSLT_HONOURSTRINGS); + if (CSLCount(papszToken) != 2) + { + CSLDestroy(papszToken); + return -1; + } + + m_dStartAngle = CPLAtof(papszToken[0]); + m_dEndAngle = CPLAtof(papszToken[1]); + } + else if (CSLCount(papszToken) == 7) + { + dXMin = fp->GetXTrans(CPLAtof(papszToken[1])); + dXMax = fp->GetXTrans(CPLAtof(papszToken[3])); + dYMin = fp->GetYTrans(CPLAtof(papszToken[2])); + dYMax = fp->GetYTrans(CPLAtof(papszToken[4])); + m_dStartAngle = CPLAtof(papszToken[5]); + m_dEndAngle = CPLAtof(papszToken[6]); + } + else + { + CSLDestroy(papszToken); + return -1; + } + + CSLDestroy(papszToken); + papszToken = NULL; + + /*------------------------------------------------------------- + * Start/End angles + * Since the angles are specified for integer coordinates, and + * that these coordinates can have the X axis reversed, we have to + * adjust the angle values for the change in the X axis + * direction. + * + * This should be necessary only when X axis is flipped. + * __TODO__ Why is order of start/end values reversed as well??? + *------------------------------------------------------------*/ + + if (fp->GetXMultiplier() <= 0.0) + { + m_dStartAngle = 360.0 - m_dStartAngle; + m_dEndAngle = 360.0 - m_dEndAngle; + } + + m_dCenterX = (dXMin + dXMax) / 2.0; + m_dCenterY = (dYMin + dYMax) / 2.0; + m_dXRadius = ABS( (dXMax - dXMin) / 2.0 ); + m_dYRadius = ABS( (dYMax - dYMin) / 2.0 ); + + /*----------------------------------------------------------------- + * Create and fill geometry object + * For the OGR geometry, we generate an arc with 2 degrees line + * segments. + *----------------------------------------------------------------*/ + poLine = new OGRLineString; + + if (m_dEndAngle < m_dStartAngle) + numPts = (int) ABS( ((m_dEndAngle+360.0)-m_dStartAngle)/2.0 ) + 1; + else + numPts = (int) ABS( (m_dEndAngle-m_dStartAngle)/2.0 ) + 1; + numPts = MAX(2, numPts); + + TABGenerateArc(poLine, numPts, + m_dCenterX, m_dCenterY, + m_dXRadius, m_dYRadius, + m_dStartAngle*PI/180.0, m_dEndAngle*PI/180.0); + + SetMBR(dXMin, dYMin, dXMax, dYMax); + SetGeometryDirectly(poLine); + + while (((pszLine = fp->GetLine()) != NULL) && + fp->IsValidFeature(pszLine) == FALSE) + { + papszToken = CSLTokenizeStringComplex(pszLine,"() ,", + TRUE,FALSE); + + if (CSLCount(papszToken) > 1) + { + if (EQUALN(papszToken[0],"PEN",3)) + { + + if (CSLCount(papszToken) == 4) + { + SetPenWidthMIF(atoi(papszToken[1])); + SetPenPattern((GByte)atoi(papszToken[2])); + SetPenColor((GInt32)atoi(papszToken[3])); + } + + } + } + CSLDestroy(papszToken); + papszToken = NULL; + } + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABArc::WriteGeometryToMIFFile(MIDDATAFile *fp) +{ + /*------------------------------------------------------------- + * Start/End angles + * Since we ALWAYS produce files in quadrant 1 then we can + * ignore the special angle conversion required by flipped axis. + *------------------------------------------------------------*/ + + + // Write the Arc's actual MBR + fp->WriteLine("Arc %.15g %.15g %.15g %.15g\n", m_dCenterX-m_dXRadius, + m_dCenterY-m_dYRadius, m_dCenterX+m_dXRadius, + m_dCenterY+m_dYRadius); + + fp->WriteLine(" %.15g %.15g\n",m_dStartAngle,m_dEndAngle); + + if (GetPenPattern()) + fp->WriteLine(" Pen (%d,%d,%d)\n",GetPenWidthMIF(),GetPenPattern(), + GetPenColor()); + + + return 0; + +} + +/********************************************************************** + * + **********************************************************************/ +int TABText::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + double dXMin, dYMin, dXMax, dYMax; + OGRGeometry *poGeometry; + const char *pszLine; + char **papszToken; + const char *pszString; + char *pszTmpString; + int bXYBoxRead = 0; + int tokenLen; + + papszToken = CSLTokenizeString2(fp->GetLastLine(), + " \t", CSLT_HONOURSTRINGS); + if (CSLCount(papszToken) == 1) + { + CSLDestroy(papszToken); + papszToken = CSLTokenizeString2(fp->GetLine(), + " \t", CSLT_HONOURSTRINGS); + tokenLen = CSLCount(papszToken); + if (tokenLen == 4) + { + pszString = NULL; + bXYBoxRead = 1; + } + else if (tokenLen == 0) + { + pszString = NULL; + } + else if (tokenLen != 1) + { + CSLDestroy(papszToken); + return -1; + } + else + { + pszString = papszToken[0]; + } + } + else if (CSLCount(papszToken) == 2) + { + pszString = papszToken[1]; + } + else + { + CSLDestroy(papszToken); + return -1; + } + + /*------------------------------------------------------------- + * Note: The text string may contain escaped "\n" chars, and we + * sstore them in memory in the UnEscaped form to be OGR + * compliant. See Maptools bug 1107 for more details. + *------------------------------------------------------------*/ + pszTmpString = CPLStrdup(pszString); + m_pszString = TABUnEscapeString(pszTmpString, TRUE); + if (pszTmpString != m_pszString) + CPLFree(pszTmpString); + + if (!bXYBoxRead) + { + CSLDestroy(papszToken); + papszToken = CSLTokenizeString2(fp->GetLine(), + " \t", CSLT_HONOURSTRINGS); + } + + if (CSLCount(papszToken) != 4) + { + CSLDestroy(papszToken); + return -1; + } + else + { + dXMin = fp->GetXTrans(CPLAtof(papszToken[0])); + dXMax = fp->GetXTrans(CPLAtof(papszToken[2])); + dYMin = fp->GetYTrans(CPLAtof(papszToken[1])); + dYMax = fp->GetYTrans(CPLAtof(papszToken[3])); + + m_dHeight = dYMax - dYMin; //SetTextBoxHeight(dYMax - dYMin); + m_dWidth = dXMax - dXMin; //SetTextBoxWidth(dXMax - dXMin); + + if (m_dHeight <0.0) + m_dHeight*=-1.0; + if (m_dWidth <0.0) + m_dWidth*=-1.0; + } + + CSLDestroy(papszToken); + papszToken = NULL; + + /* Set/retrieve the MBR to make sure Mins are smaller than Maxs + */ + + SetMBR(dXMin, dYMin, dXMax, dYMax); + GetMBR(dXMin, dYMin, dXMax, dYMax); + + while (((pszLine = fp->GetLine()) != NULL) && + fp->IsValidFeature(pszLine) == FALSE) + { + papszToken = CSLTokenizeStringComplex(pszLine,"() ,", + TRUE,FALSE); + + if (CSLCount(papszToken) > 1) + { + if (EQUALN(papszToken[0],"FONT",4)) + { + if (CSLCount(papszToken) >= 5) + { + SetFontName(papszToken[1]); + SetFontFGColor(atoi(papszToken[4])); + if (CSLCount(papszToken) ==6) + { + SetFontBGColor(atoi(papszToken[5])); + SetFontStyleMIFValue(atoi(papszToken[2]),TRUE); + } + else + SetFontStyleMIFValue(atoi(papszToken[2])); + + // papsztoken[3] = Size ??? + } + + } + else if (EQUALN(papszToken[0],"SPACING",7)) + { + if (CSLCount(papszToken) >= 2) + { + if (EQUALN(papszToken[1],"2",1)) + { + SetTextSpacing(TABTSDouble); + } + else if (EQUALN(papszToken[1],"1.5",3)) + { + SetTextSpacing(TABTS1_5); + } + } + + if (CSLCount(papszToken) == 7) + { + if (EQUALN(papszToken[2],"LAbel",5)) + { + if (EQUALN(papszToken[4],"simple",6)) + { + SetTextLineType(TABTLSimple); + SetTextLineEndPoint(fp->GetXTrans(CPLAtof(papszToken[5])), + fp->GetYTrans(CPLAtof(papszToken[6]))); + } + else if (EQUALN(papszToken[4],"arrow", 5)) + { + SetTextLineType(TABTLArrow); + SetTextLineEndPoint(fp->GetXTrans(CPLAtof(papszToken[5])), + fp->GetYTrans(CPLAtof(papszToken[6]))); + } + } + } + } + else if (EQUALN(papszToken[0],"Justify",7)) + { + if (CSLCount(papszToken) == 2) + { + if (EQUALN( papszToken[1],"Center",6)) + { + SetTextJustification(TABTJCenter); + } + else if (EQUALN( papszToken[1],"Right",5)) + { + SetTextJustification(TABTJRight); + } + + } + + } + else if (EQUALN(papszToken[0],"Angle",5)) + { + if (CSLCount(papszToken) == 2) + { + SetTextAngle(CPLAtof(papszToken[1])); + } + + } + else if (EQUALN(papszToken[0],"LAbel",5)) + { + if (CSLCount(papszToken) == 5) + { + if (EQUALN(papszToken[2],"simple",6)) + { + SetTextLineType(TABTLSimple); + SetTextLineEndPoint(fp->GetXTrans(CPLAtof(papszToken[3])), + fp->GetYTrans(CPLAtof(papszToken[4]))); + } + else if (EQUALN(papszToken[2],"arrow", 5)) + { + SetTextLineType(TABTLArrow); + SetTextLineEndPoint(fp->GetXTrans(CPLAtof(papszToken[3])), + fp->GetYTrans(CPLAtof(papszToken[4]))); + } + } + + + // What I do with the XY coordonate + } + } + CSLDestroy(papszToken); + papszToken = NULL; + } + /*----------------------------------------------------------------- + * Create an OGRPoint Geometry... + * The point X,Y values will be the coords of the lower-left corner before + * rotation is applied. (Note that the rotation in MapInfo is done around + * the upper-left corner) + * We need to calculate the true lower left corner of the text based + * on the MBR after rotation, the text height and the rotation angle. + *---------------------------------------------------------------- */ + double dCos, dSin, dX, dY; + dSin = sin(m_dAngle*PI/180.0); + dCos = cos(m_dAngle*PI/180.0); + if (dSin > 0.0 && dCos > 0.0) + { + dX = dXMin + m_dHeight * dSin; + dY = dYMin; + } + else if (dSin > 0.0 && dCos < 0.0) + { + dX = dXMax; + dY = dYMin - m_dHeight * dCos; + } + else if (dSin < 0.0 && dCos < 0.0) + { + dX = dXMax + m_dHeight * dSin; + dY = dYMax; + } + else // dSin < 0 && dCos > 0 + { + dX = dXMin; + dY = dYMax - m_dHeight * dCos; + } + + + poGeometry = new OGRPoint(dX, dY); + + SetGeometryDirectly(poGeometry); + + /*----------------------------------------------------------------- + * Compute Text Width: the width of the Text MBR before rotation + * in ground units... unfortunately this value is not stored in the + * file, so we have to compute it with the MBR after rotation and + * the height of the MBR before rotation: + * With W = Width of MBR before rotation + * H = Height of MBR before rotation + * dX = Width of MBR after rotation + * dY = Height of MBR after rotation + * teta = rotation angle + * + * For [-PI/4..teta..+PI/4] or [3*PI/4..teta..5*PI/4], we'll use: + * W = H * (dX - H * sin(teta)) / (H * cos(teta)) + * + * and for other teta values, use: + * W = H * (dY - H * cos(teta)) / (H * sin(teta)) + *---------------------------------------------------------------- */ + dSin = ABS(dSin); + dCos = ABS(dCos); + if (m_dHeight == 0.0) + m_dWidth = 0.0; + else if ( dCos > dSin ) + m_dWidth = m_dHeight * ((dXMax-dXMin) - m_dHeight*dSin) / + (m_dHeight*dCos); + else + m_dWidth = m_dHeight * ((dYMax-dYMin) - m_dHeight*dCos) / + (m_dHeight*dSin); + m_dWidth = ABS(m_dWidth); + + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABText::WriteGeometryToMIFFile(MIDDATAFile *fp) +{ + double dXMin,dYMin,dXMax,dYMax; + char *pszTmpString; + + /*------------------------------------------------------------- + * Note: The text string may contain unescaped "\n" chars or + * "\\" chars and we expect to receive them in an unescaped + * form. Those characters are unescaped in memory to be like + * other OGR drivers. See MapTools bug 1107 for more details. + *------------------------------------------------------------*/ + pszTmpString = TABEscapeString(m_pszString); + if(pszTmpString == NULL) + fp->WriteLine("Text \"\"\n" ); + else + fp->WriteLine("Text \"%s\"\n", pszTmpString ); + if (pszTmpString != m_pszString) + CPLFree(pszTmpString); + + // UpdateTextMBR(); + GetMBR(dXMin, dYMin, dXMax, dYMax); + fp->WriteLine(" %.15g %.15g %.15g %.15g\n",dXMin, dYMin,dXMax, dYMax); + + if (IsFontBGColorUsed()) + fp->WriteLine(" Font (\"%s\",%d,%d,%d,%d)\n", GetFontNameRef(), + GetFontStyleMIFValue(),0,GetFontFGColor(), + GetFontBGColor()); + else + fp->WriteLine(" Font (\"%s\",%d,%d,%d)\n", GetFontNameRef(), + GetFontStyleMIFValue(),0,GetFontFGColor()); + + switch (GetTextSpacing()) + { + case TABTS1_5: + fp->WriteLine(" Spacing 1.5\n"); + break; + case TABTSDouble: + fp->WriteLine(" Spacing 2.0\n"); + break; + case TABTSSingle: + default: + break; + } + + switch (GetTextJustification()) + { + case TABTJCenter: + fp->WriteLine(" Justify Center\n"); + break; + case TABTJRight: + fp->WriteLine(" Justify Right\n"); + break; + case TABTJLeft: + default: + break; + } + + if (ABS(GetTextAngle()) > 0.000001) + fp->WriteLine(" Angle %.15g\n",GetTextAngle()); + + switch (GetTextLineType()) + { + case TABTLSimple: + if (m_bLineEndSet) + fp->WriteLine(" Label Line Simple %.15g %.15g \n", + m_dfLineEndX, m_dfLineEndY ); + break; + case TABTLArrow: + if (m_bLineEndSet) + fp->WriteLine(" Label Line Arrow %.15g %.15g \n", + m_dfLineEndX, m_dfLineEndY ); + break; + case TABTLNoLine: + default: + break; + } + return 0; + +} + +/********************************************************************** + * + **********************************************************************/ +int TABMultiPoint::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + OGRPoint *poPoint; + OGRMultiPoint *poMultiPoint; + char **papszToken; + const char *pszLine; + int nNumPoint, i; + double dfX,dfY; + OGREnvelope sEnvelope; + + papszToken = CSLTokenizeString2(fp->GetLastLine(), + " \t", CSLT_HONOURSTRINGS); + + if (CSLCount(papszToken) !=2) + { + CSLDestroy(papszToken); + return -1; + } + + nNumPoint = atoi(papszToken[1]); + poMultiPoint = new OGRMultiPoint; + + CSLDestroy(papszToken); + papszToken = NULL; + + // Get each point and add them to the multipoint feature + for(i=0; iGetLine(); + papszToken = CSLTokenizeString2(fp->GetLastLine(), + " \t", CSLT_HONOURSTRINGS); + if (CSLCount(papszToken) !=2) + { + CSLDestroy(papszToken); + return -1; + } + + dfX = fp->GetXTrans(CPLAtof(papszToken[0])); + dfY = fp->GetXTrans(CPLAtof(papszToken[1])); + poPoint = new OGRPoint(dfX, dfY); + if ( poMultiPoint->addGeometryDirectly( poPoint ) != OGRERR_NONE) + { + CPLAssert(FALSE); // Just in case OGR is modified + } + + // Set center + if(i == 0) + { + SetCenter( dfX, dfY ); + } + CSLDestroy(papszToken); + } + + if( SetGeometryDirectly( poMultiPoint ) != OGRERR_NONE) + { + CPLAssert(FALSE); // Just in case OGR is modified + } + + poMultiPoint->getEnvelope(&sEnvelope); + SetMBR(sEnvelope.MinX, sEnvelope.MinY, + sEnvelope.MaxX,sEnvelope.MaxY); + + // Read optional SYMBOL line... + + while (((pszLine = fp->GetLine()) != NULL) && + fp->IsValidFeature(pszLine) == FALSE) + { + papszToken = CSLTokenizeStringComplex(pszLine," ,()\t", + TRUE,FALSE); + if (CSLCount(papszToken) == 4 && EQUAL(papszToken[0], "SYMBOL") ) + { + SetSymbolNo((GInt16)atoi(papszToken[1])); + SetSymbolColor((GInt32)atoi(papszToken[2])); + SetSymbolSize((GInt16)atoi(papszToken[3])); + } + CSLDestroy(papszToken); + } + + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABMultiPoint::WriteGeometryToMIFFile(MIDDATAFile *fp) +{ + OGRGeometry *poGeom; + OGRPoint *poPoint; + OGRMultiPoint *poMultiPoint; + int nNumPoints, iPoint; + + /*----------------------------------------------------------------- + * Fetch and validate geometry + *----------------------------------------------------------------*/ + poGeom = GetGeometryRef(); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) + { + poMultiPoint = (OGRMultiPoint*)poGeom; + nNumPoints = poMultiPoint->getNumGeometries(); + + fp->WriteLine("MultiPoint %d\n", nNumPoints); + + for(iPoint=0; iPoint < nNumPoints; iPoint++) + { + /*------------------------------------------------------------ + * Validate each point + *-----------------------------------------------------------*/ + poGeom = poMultiPoint->getGeometryRef(iPoint); + if (poGeom && wkbFlatten(poGeom->getGeometryType()) == wkbPoint) + { + poPoint = (OGRPoint*)poGeom; + fp->WriteLine("%.15g %.15g\n",poPoint->getX(),poPoint->getY()); + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABMultiPoint: Missing or Invalid Geometry!"); + return -1; + } + } + // Write symbol + fp->WriteLine(" Symbol (%d,%d,%d)\n",GetSymbolNo(),GetSymbolColor(), + GetSymbolSize()); + } + + return 0; +} + + +/********************************************************************** + * + **********************************************************************/ +int TABCollection::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + char **papszToken; + const char *pszLine; + int numParts, i; + OGREnvelope sEnvelope; + + /*----------------------------------------------------------------- + * Fetch number of parts in "COLLECTION %d" line + *----------------------------------------------------------------*/ + papszToken = CSLTokenizeString2(fp->GetLastLine(), + " \t", CSLT_HONOURSTRINGS); + + if (CSLCount(papszToken) !=2) + { + CSLDestroy(papszToken); + return -1; + } + + numParts = atoi(papszToken[1]); + CSLDestroy(papszToken); + papszToken = NULL; + + // Make sure collection is empty + EmptyCollection(); + + pszLine = fp->GetLine(); + + /*----------------------------------------------------------------- + * Read each part and add them to the feature + *----------------------------------------------------------------*/ + for (i=0; i < numParts; i++) + { + if (pszLine == NULL) + { + CPLError(CE_Failure, CPLE_FileIO, + "Unexpected EOF while reading TABCollection from MIF file."); + return -1; + } + + while(*pszLine == ' ' || *pszLine == '\t') + pszLine++; // skip leading spaces + + if (*pszLine == '\0') + continue; // Skip blank lines + + if (EQUALN(pszLine,"REGION",6)) + { + m_poRegion = new TABRegion(GetDefnRef()); + if (m_poRegion->ReadGeometryFromMIFFile(fp) != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "TABCollection: Error reading REGION part."); + delete m_poRegion; + m_poRegion = NULL; + return -1; + } + } + else if (EQUALN(pszLine,"LINE",4) || + EQUALN(pszLine,"PLINE",5)) + { + m_poPline = new TABPolyline(GetDefnRef()); + if (m_poPline->ReadGeometryFromMIFFile(fp) != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "TABCollection: Error reading PLINE part."); + delete m_poPline; + m_poPline = NULL; + return -1; + } + } + else if (EQUALN(pszLine,"MULTIPOINT",10)) + { + m_poMpoint = new TABMultiPoint(GetDefnRef()); + if (m_poMpoint->ReadGeometryFromMIFFile(fp) != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "TABCollection: Error reading MULTIPOINT part."); + delete m_poMpoint; + m_poMpoint = NULL; + return -1; + } + } + else + { + CPLError(CE_Failure, CPLE_FileIO, + "Reading TABCollection from MIF failed, expecting one " + "of REGION, PLINE or MULTIPOINT, got: '%s'", + pszLine); + return -1; + } + + pszLine = fp->GetLastLine(); + } + + /*----------------------------------------------------------------- + * Set the main OGRFeature Geometry + * (this is actually duplicating geometries from each member) + *----------------------------------------------------------------*/ + // use addGeometry() rather than addGeometryDirectly() as this clones + // the added geometry so won't leave dangling ptrs when the above features + // are deleted + + OGRGeometryCollection *poGeomColl = new OGRGeometryCollection(); + if(m_poRegion && m_poRegion->GetGeometryRef() != NULL) + poGeomColl->addGeometry(m_poRegion->GetGeometryRef()); + + if(m_poPline && m_poPline->GetGeometryRef() != NULL) + poGeomColl->addGeometry(m_poPline->GetGeometryRef()); + + if(m_poMpoint && m_poMpoint->GetGeometryRef() != NULL) + poGeomColl->addGeometry(m_poMpoint->GetGeometryRef()); + + this->SetGeometryDirectly(poGeomColl); + + poGeomColl->getEnvelope(&sEnvelope); + SetMBR(sEnvelope.MinX, sEnvelope.MinY, + sEnvelope.MaxX, sEnvelope.MaxY); + + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABCollection::WriteGeometryToMIFFile(MIDDATAFile *fp) +{ + int numParts = 0; + if (m_poRegion) numParts++; + if (m_poPline) numParts++; + if (m_poMpoint) numParts++; + + fp->WriteLine("COLLECTION %d\n", numParts); + + if (m_poRegion) + { + if (m_poRegion->WriteGeometryToMIFFile(fp) != 0) + return -1; + } + + if (m_poPline) + { + if (m_poPline->WriteGeometryToMIFFile(fp) != 0) + return -1; + } + + if (m_poMpoint) + { + if (m_poMpoint->WriteGeometryToMIFFile(fp) != 0) + return -1; + } + + return 0; +} + +/********************************************************************** + * + **********************************************************************/ +int TABDebugFeature::ReadGeometryFromMIFFile(MIDDATAFile *fp) +{ + const char *pszLine; + + + /* Go to the first line of the next feature */ + printf("%s\n", fp->GetLastLine()); + + while (((pszLine = fp->GetLine()) != NULL) && + fp->IsValidFeature(pszLine) == FALSE) + ; + + return 0; +} + + +/********************************************************************** + * + **********************************************************************/ +int TABDebugFeature::WriteGeometryToMIFFile(CPL_UNUSED MIDDATAFile *fp){ return -1; } diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_geometry.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_geometry.cpp new file mode 100644 index 000000000..abd7c7d33 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_geometry.cpp @@ -0,0 +1,438 @@ +/********************************************************************** + * $Id: mitab_geometry.cpp,v 1.5 2004-06-30 20:29:04 dmorissette Exp $ + * + * Name: mitab_geometry.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Geometry manipulation functions. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * Based on functions from mapprimitive.c/mapsearch.c in the source + * of UMN MapServer by Stephen Lime (http://mapserver.gis.umn.edu/) + ********************************************************************** + * Copyright (c) 1999-2001, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_geometry.cpp,v $ + * Revision 1.5 2004-06-30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.4 2001/12/18 23:42:28 daniel + * Added a test in OGRPolygonLabelPoint() to prevent returning a point + * outside of the polygon MBR (bug 673). + * + * Revision 1.3 2001/01/22 16:03:58 warmerda + * expanded tabs + * + * Revision 1.2 2000/09/28 16:39:44 warmerda + * avoid warnings for unused, and unitialized variables + * + * Revision 1.1 2000/09/19 17:19:40 daniel + * Initial Revision + * + **********************************************************************/ + +#include "ogr_geometry.h" + +#define OGR_NUM_RINGS(poly) (poly->getNumInteriorRings()+1) +#define OGR_GET_RING(poly, i) (i==0?poly->getExteriorRing():poly->getInteriorRing(i-1)) + + +/********************************************************************** + * OGRPointInRing() + * + * Returns TRUE is point is inside ring, FALSE otherwise + * + * Adapted version of msPointInPolygon() from MapServer's mapsearch.c + **********************************************************************/ +GBool OGRPointInRing(OGRPoint *poPoint, OGRLineString *poRing) +{ + int i, j, numpoints; + GBool status = FALSE; + double x, y; + + numpoints = poRing->getNumPoints(); + x = poPoint->getX(); + y = poPoint->getY(); + + for (i = 0, j = numpoints-1; i < numpoints; j = i++) + { + if ((((poRing->getY(i)<=y) && (ygetY(j))) || + ((poRing->getY(j)<=y) && (ygetY(i)))) && + (x < (poRing->getX(j) - poRing->getX(i)) * (y - poRing->getY(i)) / + (poRing->getY(j) - poRing->getY(i)) + poRing->getX(i))) + status = !status; + } + + return status; +} + +/********************************************************************** + * OGRIntersectPointPolygon() + * + * Instead of using ring orientation we count the number of parts the + * point falls in. If odd the point is in the polygon, if 0 or even + * then the point is in a hole or completely outside. + * + * Returns TRUE is point is inside polygon, FALSE otherwise + * + * Adapted version of msIntersectPointPolygon() from MapServer's mapsearch.c + **********************************************************************/ +GBool OGRIntersectPointPolygon(OGRPoint *poPoint, OGRPolygon *poPoly) +{ + int i; + GBool status = FALSE; + + for(i=0; i (max) ? CLIP_RIGHT : CLIP_MIDDLE)); +#define ROUND(a) ( (a) + 0.5 ) +#define SWAP( a, b, t) ( (t) = (a), (a) = (b), (b) = (t) ) +#define EDGE_CHECK( x0, x, x1) ((x) < MIN( (x0), (x1)) ? CLIP_LEFT : ((x) > MAX( (x0), (x1)) ? CLIP_RIGHT : CLIP_MIDDLE )) + +#define NUM_SCANLINES 5 + +int OGRPolygonLabelPoint(OGRPolygon *poPoly, OGRPoint *poLabelPoint) +{ + double slope; + OGRRawPoint point1, point2; + int i, j, k, nfound; + double x, y, *xintersect, temp; + double hi_y, lo_y; + int wrong_order, n; + double len, max_len=0; + double skip; + OGREnvelope oEnv; + + if (poPoly == NULL) + return OGRERR_FAILURE; + + poPoly->getEnvelope(&oEnv); + + poLabelPoint->setX((oEnv.MaxX + oEnv.MinX)/2.0); + poLabelPoint->setY((oEnv.MaxY + oEnv.MinY)/2.0); + + //if(get_centroid(p, lp, &miny, &maxy) == -1) return(-1); + + if(OGRIntersectPointPolygon(poLabelPoint, poPoly) == TRUE) /* cool, done */ + return OGRERR_NONE; + + /* do it the hard way - scanline */ + + skip = (oEnv.MaxY - oEnv.MinY)/NUM_SCANLINES; + + n=0; + for(j=0; jgetNumPoints(); + } + + xintersect = (double *)calloc(n, sizeof(double)); + if (xintersect == NULL) + return OGRERR_FAILURE; + + for(k=1; k<=NUM_SCANLINES; k++) + { + /* sample the shape in the y direction */ + + y = oEnv.MaxY - k*skip; + + /* need to find a y that won't intersect any vertices exactly */ + hi_y = y - 1; /* first initializing lo_y, hi_y to be any 2 pnts on either side of lp->y */ + lo_y = y + 1; + for(j=0; j= y)) + break; /* already initialized */ + for(i=0; i < poRing->getNumPoints(); i++) + { + if((lo_y < y) && (hi_y >= y)) + break; /* already initialized */ + if(poRing->getY(i) < y) + lo_y = poRing->getY(i); + if(poRing->getY(i) >= y) + hi_y = poRing->getY(i); + } + } + + n=0; + for(j=0; jgetNumPoints(); i++) + { + if((poRing->getY(i) < y) && + ((y - poRing->getY(i)) < (y - lo_y))) + lo_y = poRing->getY(i); + if((poRing->getY(i) >= y) && + ((poRing->getY(i) - y) < (hi_y - y))) + hi_y = poRing->getY(i); + } + } + + if(lo_y == hi_y) + return OGRERR_FAILURE; + else + y = (hi_y + lo_y)/2.0; + + nfound = 0; + for(j=0; jgetX(poRing->getNumPoints()-1); + point1.y = poRing->getY(poRing->getNumPoints()-1); + + for(i=0; i < poRing->getNumPoints(); i++) + { + point2.x = poRing->getX(i); + point2.y = poRing->getY(i); + + if(EDGE_CHECK(point1.y, y, point2.y) == CLIP_MIDDLE) + { + if(point1.y == point2.y) + continue; /* ignore horizontal edges */ + else + slope = (point2.x - point1.x) / (point2.y - point1.y); + + x = point1.x + (y - point1.y)*slope; + xintersect[nfound++] = x; + } /* End of checking this edge */ + + point1 = point2; /* Go on to next edge */ + } + } /* Finished the scanline */ + + /* First, sort the intersections */ + do + { + wrong_order = 0; + for(i=0; i < nfound-1; i++) + { + if(xintersect[i] > xintersect[i+1]) + { + wrong_order = 1; + SWAP(xintersect[i], xintersect[i+1], temp); + } + } + } while(wrong_order); + + /* Great, now find longest span */ + point1.y = point2.y = y; + for(i=0; i < nfound; i += 2) + { + point1.x = xintersect[i]; + point2.x = xintersect[i+1]; + /* len = length(point1, point2); */ + len = ABS((point2.x - point1.x)); + if(len > max_len) + { + max_len = len; + poLabelPoint->setX( (point1.x + point2.x)/2 ); + poLabelPoint->setY( y ); + } + } + } + + free(xintersect); + + /* __TODO__ Bug 673 + * There seem to be some polygons for which the label is returned + * completely outside of the polygon's MBR and this messes the + * file bounds, etc. + * Until we find the source of the problem, we'll at least validate + * the label point to make sure that it overlaps the polygon MBR. + */ + if( poLabelPoint->getX() < oEnv.MinX + || poLabelPoint->getY() < oEnv.MinY + || poLabelPoint->getX() > oEnv.MaxX + || poLabelPoint->getY() > oEnv.MaxY ) + { + // Reset label coordinates to center of MBR, just in case + poLabelPoint->setX((oEnv.MaxX + oEnv.MinX)/2.0); + poLabelPoint->setY((oEnv.MaxY + oEnv.MinY)/2.0); + + // And return an error + return OGRERR_FAILURE; + } + + if(max_len > 0) + return OGRERR_NONE; + else + return OGRERR_FAILURE; +} + + +/********************************************************************** + * OGRGetCentroid() + * + * Calculate polygon gravity center. + * + * Returns OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise. + * + * Adapted version of get_centroid() from MapServer's mapprimitive.c + **********************************************************************/ + +int OGRGetCentroid(OGRPolygon *poPoly, OGRPoint *poCentroid) +{ + int i,j; + double cent_weight_x=0.0, cent_weight_y=0.0; + double len, total_len=0; + + for(i=0; igetX(0); + y2 = poRing->getY(0); + + for(j=1; jgetNumPoints(); j++) + { + x1 = x2; + y1 = y2; + x2 = poRing->getX(j); + y2 = poRing->getY(j); + + len = sqrt( pow((x2-x1),2) + pow((y2-y1),2) ); + cent_weight_x += len * ((x1 + x2)/2.0); + cent_weight_y += len * ((y1 + y2)/2.0); + total_len += len; + } + } + + if(total_len == 0) + return(OGRERR_FAILURE); + + poCentroid->setX( cent_weight_x / total_len ); + poCentroid->setY( cent_weight_y / total_len ); + + return OGRERR_NONE; +} + + + +/********************************************************************** + * OGRPolylineCenterPoint() + * + * Return the center point of a polyline. + * + * In MapInfo, for a simple or multiple polyline (pline), the center point + * in the object definition is supposed to be either the center point of + * the pline or the first section of a multiple pline (if an odd number of + * points in the pline or first section), or the midway point between the + * two central points (if an even number of points involved). + * + * Returns OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise. + **********************************************************************/ + +int OGRPolylineCenterPoint(OGRLineString *poLine, OGRPoint *poLabelPoint) +{ + if (poLine == NULL || poLine->getNumPoints() < 2) + return OGRERR_FAILURE; + + if (poLine->getNumPoints() % 2 == 0) + { + // Return the midway between the 2 center points + int i = poLine->getNumPoints()/2; + poLabelPoint->setX( (poLine->getX(i-1) + poLine->getX(i))/2.0 ); + poLabelPoint->setY( (poLine->getY(i-1) + poLine->getY(i))/2.0 ); + } + else + { + // Return the center point + poLine->getPoint(poLine->getNumPoints()/2, poLabelPoint); + } + + return OGRERR_NONE; +} + + +/********************************************************************** + * OGRPolylineLabelPoint() + * + * Generate a label point on a polyline: The center of the longest segment. + * + * Returns OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise. + **********************************************************************/ + +int OGRPolylineLabelPoint(OGRLineString *poLine, OGRPoint *poLabelPoint) +{ + double segment_length, max_segment_length = 0.0; + double x1, y1, x2, y2; + + if (poLine == NULL || poLine->getNumPoints() < 2) + return OGRERR_FAILURE; + + max_segment_length = -1.0; + + x2 = poLine->getX(0); + y2 = poLine->getY(0); + + for(int i=1; igetNumPoints(); i++) + { + x1 = x2; + y1 = y2; + x2 = poLine->getX(i); + y2 = poLine->getY(i); + + segment_length = pow((x2-x1),2) + pow((y2-y1),2); + if (segment_length > max_segment_length) + { + max_segment_length = segment_length; + poLabelPoint->setX( (x1 + x2)/2.0 ); + poLabelPoint->setY( (y1 + y2)/2.0 ); + } + } + + return OGRERR_NONE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_geometry.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_geometry.h new file mode 100644 index 000000000..65e0cb63d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_geometry.h @@ -0,0 +1,53 @@ +/********************************************************************** + * $Id: mitab_geometry.h,v 1.2 2004-06-30 20:29:04 dmorissette Exp $ + * + * Name: mitab_geometry.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Geometry manipulation functions. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * Based on functions from mapprimitive.c/mapsearch.c in the source + * of UMN MapServer by Stephen Lime (http://mapserver.gis.umn.edu/) + ********************************************************************** + * Copyright (c) 1999, 2000, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_geometry.h,v $ + * Revision 1.2 2004-06-30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.1 2000/09/19 17:19:40 daniel + * Initial Revision + * + **********************************************************************/ + +#ifndef _MITAB_GEOMETRY_H_INCLUDED +#define _MITAB_GEOMETRY_H_INCLUDED + +#include "ogr_geometry.h" + +GBool OGRPointInRing(OGRPoint *poPoint, OGRLineString *poRing); +GBool OGRIntersectPointPolygon(OGRPoint *poPoint, OGRPolygon *poPoly); +int OGRPolygonLabelPoint(OGRPolygon *poPoly, OGRPoint *poLabelPoint); +int OGRPolylineCenterPoint(OGRLineString *poLine, OGRPoint *poLabelPoint); +int OGRPolylineLabelPoint(OGRLineString *poLine, OGRPoint *poLabelPoint); + +#endif /* ndef _MITAB_GEOMETRY_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_idfile.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_idfile.cpp new file mode 100644 index 000000000..4c14be35d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_idfile.cpp @@ -0,0 +1,440 @@ +/********************************************************************** + * $Id: mitab_idfile.cpp,v 1.8 2006-11-28 18:49:08 dmorissette Exp $ + * + * Name: mitab_idfile.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABIDFile class used to handle + * reading/writing of the .ID file attached to a .MAP file + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999, 2000, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_idfile.cpp,v $ + * Revision 1.8 2006-11-28 18:49:08 dmorissette + * Completed changes to split TABMAPObjectBlocks properly and produce an + * optimal spatial index (bug 1585) + * + * Revision 1.7 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.6 2000/01/18 22:08:56 daniel + * Allow opening of 0-size .ID file (dataset with 0 features) + * + * Revision 1.5 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.4 1999/09/26 14:59:36 daniel + * Implemented write support + * + * Revision 1.3 1999/09/20 18:43:01 daniel + * Use binary acces to open file. + * + * Revision 1.2 1999/09/16 02:39:16 daniel + * Completed read support for most feature types + * + * Revision 1.1 1999/07/12 04:18:24 daniel + * Initial checkin + * + **********************************************************************/ + +#include "mitab.h" +#include "mitab_utils.h" + +/*===================================================================== + * class TABIDFile + *====================================================================*/ + + +/********************************************************************** + * TABIDFile::TABIDFile() + * + * Constructor. + **********************************************************************/ +TABIDFile::TABIDFile() +{ + m_fp = NULL; + m_pszFname = NULL; + m_poIDBlock = NULL; + m_nMaxId = -1; +} + +/********************************************************************** + * TABIDFile::~TABIDFile() + * + * Destructor. + **********************************************************************/ +TABIDFile::~TABIDFile() +{ + Close(); +} + +/********************************************************************** + * TABIDFile::Open() + * + * Compatibility layer with new interface. + * Return 0 on success, -1 in case of failure. + **********************************************************************/ + +int TABIDFile::Open(const char *pszFname, const char* pszAccess) +{ + if( EQUALN(pszAccess, "r", 1) ) + return Open(pszFname, TABRead); + else if( EQUALN(pszAccess, "w", 1) ) + return Open(pszFname, TABWrite); + else + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: access mode \"%s\" not supported", pszAccess); + return -1; + } +} + +/********************************************************************** + * TABIDFile::Open() + * + * Open a .ID file, and initialize the structures to be ready to read + * objects from it. + * + * If the filename that is passed in contains a .MAP extension then + * the extension will be changed to .ID before trying to open the file. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABIDFile::Open(const char *pszFname, TABAccess eAccess) +{ + int nLen; + + if (m_fp) + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: object already contains an open file"); + return -1; + } + + /*----------------------------------------------------------------- + * Validate access mode and make sure we use binary access. + * Note that in Write mode we need TABReadWrite since we do random + * updates in the index as data blocks are split + *----------------------------------------------------------------*/ + const char* pszAccess = NULL; + if (eAccess == TABRead) + { + m_eAccessMode = TABRead; + pszAccess = "rb"; + } + else if (eAccess == TABWrite) + { + m_eAccessMode = TABReadWrite; + pszAccess = "wb+"; + } + else if (eAccess == TABReadWrite) + { + m_eAccessMode = TABReadWrite; + pszAccess = "rb+"; + } + else + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: access mode \"%d\" not supported", eAccess); + return -1; + } + + /*----------------------------------------------------------------- + * Change .MAP extension to .ID if necessary + *----------------------------------------------------------------*/ + m_pszFname = CPLStrdup(pszFname); + + nLen = strlen(m_pszFname); + if (nLen > 4 && strcmp(m_pszFname+nLen-4, ".MAP")==0) + strcpy(m_pszFname+nLen-4, ".ID"); + else if (nLen > 4 && strcmp(m_pszFname+nLen-4, ".map")==0) + strcpy(m_pszFname+nLen-4, ".id"); + + /*----------------------------------------------------------------- + * Change .MAP extension to .ID if necessary + *----------------------------------------------------------------*/ +#ifndef _WIN32 + TABAdjustFilenameExtension(m_pszFname); +#endif + + /*----------------------------------------------------------------- + * Open file + *----------------------------------------------------------------*/ + m_fp = VSIFOpenL(m_pszFname, pszAccess); + + if (m_fp == NULL) + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed for %s", m_pszFname); + CPLFree(m_pszFname); + m_pszFname = NULL; + return -1; + } + + if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) + { + /*------------------------------------------------------------- + * READ access: + * Establish the number of object IDs from the size of the file + *------------------------------------------------------------*/ + VSIStatBufL sStatBuf; + if ( VSIStatL(m_pszFname, &sStatBuf) == -1 ) + { + CPLError(CE_Failure, CPLE_FileIO, + "stat() failed for %s\n", m_pszFname); + Close(); + return -1; + } + + m_nMaxId = (int)(sStatBuf.st_size/4); + m_nBlockSize = MIN(1024, m_nMaxId*4); + + /*------------------------------------------------------------- + * Read the first block from the file + *------------------------------------------------------------*/ + m_poIDBlock = new TABRawBinBlock(m_eAccessMode, FALSE); + + if (m_nMaxId == 0) + { + // .ID file size = 0 ... just allocate a blank block but + // it won't get really used anyways. + m_nBlockSize = 512; + m_poIDBlock->InitNewBlock(m_fp, m_nBlockSize, 0); + } + else if (m_poIDBlock->ReadFromFile(m_fp, 0, m_nBlockSize) != 0) + { + // CPLError() has already been called. + Close(); + return -1; + } + } + else + { + /*------------------------------------------------------------- + * WRITE access: + * Get ready to write to the file + *------------------------------------------------------------*/ + m_poIDBlock = new TABRawBinBlock(m_eAccessMode, FALSE); + m_nMaxId = 0; + m_nBlockSize = 1024; + m_poIDBlock->InitNewBlock(m_fp, m_nBlockSize, 0); + } + + return 0; +} + +/********************************************************************** + * TABIDFile::Close() + * + * Close current file, and release all memory used. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABIDFile::Close() +{ + if (m_fp == NULL) + return 0; + + /*---------------------------------------------------------------- + * Write access: commit latest changes to the file. + *---------------------------------------------------------------*/ + if (m_eAccessMode != TABRead) + SyncToDisk(); + + // Delete all structures + delete m_poIDBlock; + m_poIDBlock = NULL; + + // Close file + VSIFCloseL(m_fp); + m_fp = NULL; + + CPLFree(m_pszFname); + m_pszFname = NULL; + + return 0; +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +int TABIDFile::SyncToDisk() +{ + if( m_eAccessMode == TABRead ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SyncToDisk() can be used only with Write access."); + return -1; + } + + if( m_poIDBlock == NULL) + return 0; + + return m_poIDBlock->CommitToFile(); +} + +/********************************************************************** + * TABIDFile::GetObjPtr() + * + * Return the offset in the .MAP file where the map object with the + * specified id is located. + * + * Note that object ids are positive and start at 1. + * + * An object Id of '0' means that object has no geometry. + * + * Returns a value >= 0 on success, -1 on error. + **********************************************************************/ +GInt32 TABIDFile::GetObjPtr(GInt32 nObjId) +{ + if (m_poIDBlock == NULL) + return -1; + + if (nObjId < 1 || nObjId > m_nMaxId) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "GetObjPtr(): Invalid object ID %d (valid range is [1..%d])", + nObjId, m_nMaxId); + return -1; + } + + if (m_poIDBlock->GotoByteInFile( (nObjId-1)*4 ) != 0) + return -1; + + return m_poIDBlock->ReadInt32(); +} + +/********************************************************************** + * TABIDFile::SetObjPtr() + * + * Set the offset in the .MAP file where the map object with the + * specified id is located. + * + * Note that object ids are positive and start at 1. + * + * An object Id of '0' means that object has no geometry. + * + * Returns a value of 0 on success, -1 on error. + **********************************************************************/ +int TABIDFile::SetObjPtr(GInt32 nObjId, GInt32 nObjPtr) +{ + if (m_poIDBlock == NULL) + return -1; + + if (m_eAccessMode == TABRead) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetObjPtr() can be used only with Write access."); + return -1; + } + + if (nObjId < 1) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "SetObjPtr(): Invalid object ID %d (must be greater than zero)", + nObjId); + return -1; + } + + /*----------------------------------------------------------------- + * GotoByteInFile() will automagically commit current block and init + * a new one if necessary. + *----------------------------------------------------------------*/ + GInt32 nLastIdBlock = ((m_nMaxId-1)*4) / m_nBlockSize; + GInt32 nTargetIdBlock = ((nObjId-1)*4) / m_nBlockSize; + if (m_nMaxId > 0 && nTargetIdBlock <= nLastIdBlock) + { + /* Pass second arg to GotoByteInFile() to force reading from file + * when going back to blocks already committed + */ + if (m_poIDBlock->GotoByteInFile( (nObjId-1)*4, TRUE ) != 0) + return -1; + } + else + { + /* If we reach EOF then a new empty block will have to be allocated + */ + if (m_poIDBlock->GotoByteInFile( (nObjId-1)*4 ) != 0) + return -1; + } + + m_nMaxId = MAX(m_nMaxId, nObjId); + + return m_poIDBlock->WriteInt32(nObjPtr); +} + + +/********************************************************************** + * TABIDFile::GetMaxObjId() + * + * Return the value of the biggest valid object id. + * + * Note that object ids are positive and start at 1. + * + * Returns a value >= 0 on success, -1 on error. + **********************************************************************/ +GInt32 TABIDFile::GetMaxObjId() +{ + return m_nMaxId; +} + + +/********************************************************************** + * TABIDFile::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABIDFile::Dump(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABIDFile::Dump() -----\n"); + + if (m_fp == NULL) + { + fprintf(fpOut, "File is not opened.\n"); + } + else + { + fprintf(fpOut, "File is opened: %s\n", m_pszFname); + fprintf(fpOut, "Current index block follows ...\n\n"); + m_poIDBlock->Dump(fpOut); + fprintf(fpOut, "... end of index block.\n\n"); + + } + + fflush(fpOut); +} + +#endif // DEBUG + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_imapinfofile.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_imapinfofile.cpp new file mode 100644 index 000000000..3ceb68887 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_imapinfofile.cpp @@ -0,0 +1,596 @@ +/********************************************************************** + * $Id: mitab_imapinfofile.cpp,v 1.31 2010-01-07 20:39:12 aboudreault Exp $ + * + * Name: mitab_imapinfo + * Project: MapInfo mid/mif Tab Read/Write library + * Language: C++ + * Purpose: Implementation of the IMapInfoFile class, super class of + * of MIFFile and TABFile + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2008, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_imapinfofile.cpp,v $ + * Revision 1.31 2010-01-07 20:39:12 aboudreault + * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141) + * + * Revision 1.30 2009-01-23 16:50:27 aboudreault + * Fixed wrong return value of IMapInfoFile::SetCharset() method (bug 1987) + * + * Revision 1.29 2008/11/27 20:50:22 aboudreault + * Improved support for OGR date/time types. New Read/Write methods (bug 1948) + * Added support of OGR date/time types for MIF features. + * + * Revision 1.28 2008/11/17 22:06:21 aboudreault + * Added support to use OFTDateTime/OFTDate/OFTTime type when compiled with + * OGR and fixed reading/writing support for these types. + * + * Revision 1.27 2008/09/26 14:40:24 aboudreault + * Fixed bug: MITAB doesn't support writing DateTime type (bug 1948) + * + * Revision 1.26 2008/03/07 20:16:17 dmorissette + * Fixed typos in comments + * + * Revision 1.25 2008/03/05 20:35:39 dmorissette + * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859) + * + * Revision 1.24 2007/06/21 14:00:23 dmorissette + * Added missing cast in isspace() calls to avoid failed assertion on Windows + * (MITAB bug 1737, GDAL ticket 1678)) + * + * Revision 1.23 2007/06/12 14:43:19 dmorissette + * Use iswspace instead of sispace in IMapInfoFile::SmartOpen() (bug 1737) + * + * Revision 1.22 2007/06/12 13:52:37 dmorissette + * Added IMapInfoFile::SetCharset() method (bug 1734) + * + * Revision 1.21 2005/05/19 21:10:50 fwarmerdam + * changed to use OGRLayers spatial filter support + * + * Revision 1.20 2005/05/19 15:27:00 jlacroix + * Implement a method to set the StyleString of a TABFeature. + * This is done via the ITABFeaturePen, Brush and Symbol classes. + * + * Revision 1.19 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.18 2003/12/19 07:55:55 fwarmerdam + * treat 3D features as 2D on write + * + * Revision 1.17 2001/09/14 19:14:43 warmerda + * added attribute query support + * + * Revision 1.16 2001/09/14 03:23:55 warmerda + * Substantial upgrade to support spatial queries using spatial indexes + * + * Revision 1.15 2001/07/03 23:11:21 daniel + * Test for NULL geometries if spatial filter enabled in GetNextFeature(). + * + * Revision 1.14 2001/03/09 04:16:02 daniel + * Added TABSeamless for reading seamless TAB files + * + * Revision 1.13 2001/02/27 19:59:05 daniel + * Enabled spatial filter in IMapInfoFile::GetNextFeature(), and avoid + * unnecessary feature cloning in GetNextFeature() and GetFeature() + * + * Revision 1.12 2001/02/06 22:03:24 warmerda + * fixed memory leak of whole features in CreateFeature + * + * Revision 1.11 2001/01/23 21:23:42 daniel + * Added projection bounds lookup table, called from TABFile::SetProjInfo() + * + * Revision 1.10 2001/01/22 16:03:58 warmerda + * expanded tabs + * + * Revision 1.9 2000/11/30 20:27:56 warmerda + * make variable length string fields 254 wide, not 255 + * + * Revision 1.8 2000/02/28 03:11:35 warmerda + * fix support for zero width fields + * + * Revision 1.7 2000/02/02 20:14:03 warmerda + * made safer when encountering geometryless features + * + * Revision 1.6 2000/01/26 18:17:35 warmerda + * added CreateField method + * + * Revision 1.5 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.4 2000/01/11 19:06:25 daniel + * Added support for conversion of collections in CreateFeature() + * + * Revision 1.3 1999/12/14 02:14:50 daniel + * Added static SmartOpen() method + TABView support + * + * Revision 1.2 1999/11/08 19:15:44 stephane + * Add headers method + * + * Revision 1.1 1999/11/08 04:17:27 stephane + * First Revision + * + **********************************************************************/ + +#include "mitab.h" +#include "mitab_utils.h" + +#ifdef __HP_aCC +# include /* iswspace() */ +#else +# include /* iswspace() */ +#endif + +/********************************************************************** + * IMapInfoFile::IMapInfoFile() + * + * Constructor. + **********************************************************************/ +IMapInfoFile::IMapInfoFile() +{ + m_nCurFeatureId = 0; + m_poCurFeature = NULL; + m_bBoundsSet = FALSE; + m_pszCharset = NULL; +} + + +/********************************************************************** + * IMapInfoFile::~IMapInfoFile() + * + * Destructor. + **********************************************************************/ +IMapInfoFile::~IMapInfoFile() +{ + if (m_poCurFeature) + { + delete m_poCurFeature; + m_poCurFeature = NULL; + } + + CPLFree(m_pszCharset); + m_pszCharset = NULL; +} + +/********************************************************************** + * IMapInfoFile::Open() + * + * Compatibility layer with new interface. + * Return 0 on success, -1 in case of failure. + **********************************************************************/ + +int IMapInfoFile::Open(const char *pszFname, const char* pszAccess, + GBool bTestOpenNoError) +{ + if( EQUALN(pszAccess, "r", 1) ) + return Open(pszFname, TABRead, bTestOpenNoError); + else if( EQUALN(pszAccess, "w", 1) ) + return Open(pszFname, TABWrite, bTestOpenNoError); + else + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: access mode \"%s\" not supported", pszAccess); + return -1; + } +} + +/********************************************************************** + * IMapInfoFile::SmartOpen() + * + * Use this static method to automatically open any flavour of MapInfo + * dataset. This method will detect the file type, create an object + * of the right type, and open the file. + * + * Call GetFileClass() on the returned object if you need to find out + * its exact type. (To access format-specific methods for instance) + * + * Returns the new object ptr. , or NULL if the open failed. + **********************************************************************/ +IMapInfoFile *IMapInfoFile::SmartOpen(const char *pszFname, + GBool bUpdate, + GBool bTestOpenNoError /*=FALSE*/) +{ + IMapInfoFile *poFile = NULL; + int nLen = 0; + + if (pszFname) + nLen = strlen(pszFname); + + if (nLen > 4 && (EQUAL(pszFname + nLen-4, ".MIF") || + EQUAL(pszFname + nLen-4, ".MID") ) ) + { + /*------------------------------------------------------------- + * MIF/MID file + *------------------------------------------------------------*/ + poFile = new MIFFile; + } + else if (nLen > 4 && EQUAL(pszFname + nLen-4, ".TAB")) + { + /*------------------------------------------------------------- + * .TAB file ... is it a TABFileView or a TABFile? + * We have to read the .tab header to find out. + *------------------------------------------------------------*/ + VSILFILE *fp; + const char *pszLine; + char *pszAdjFname = CPLStrdup(pszFname); + GBool bFoundFields = FALSE, bFoundView=FALSE, bFoundSeamless=FALSE; + + TABAdjustFilenameExtension(pszAdjFname); + fp = VSIFOpenL(pszAdjFname, "r"); + while(fp && (pszLine = CPLReadLineL(fp)) != NULL) + { + while (isspace((unsigned char)*pszLine)) pszLine++; + if (EQUALN(pszLine, "Fields", 6)) + bFoundFields = TRUE; + else if (EQUALN(pszLine, "create view", 11)) + bFoundView = TRUE; + else if (EQUALN(pszLine, "\"\\IsSeamless\" = \"TRUE\"", 21)) + bFoundSeamless = TRUE; + } + + if (bFoundView) + poFile = new TABView; + else if (bFoundFields && bFoundSeamless) + poFile = new TABSeamless; + else if (bFoundFields) + poFile = new TABFile; + + if (fp) + VSIFCloseL(fp); + + CPLFree(pszAdjFname); + } + + /*----------------------------------------------------------------- + * Perform the open() call + *----------------------------------------------------------------*/ + if (poFile && poFile->Open(pszFname, bUpdate ? TABReadWrite : TABRead, bTestOpenNoError) != 0) + { + delete poFile; + poFile = NULL; + } + + if (!bTestOpenNoError && poFile == NULL) + { + CPLError(CE_Failure, CPLE_FileIO, + "%s could not be opened as a MapInfo dataset.", pszFname); + } + + return poFile; +} + + + +/********************************************************************** + * IMapInfoFile::GetNextFeature() + * + * Standard OGR GetNextFeature implementation. This method is used + * to retreive the next OGRFeature. + **********************************************************************/ +OGRFeature *IMapInfoFile::GetNextFeature() +{ + OGRFeature *poFeatureRef; + OGRGeometry *poGeom; + GIntBig nFeatureId; + + while( (nFeatureId = GetNextFeatureId(m_nCurFeatureId)) != -1 ) + { + poFeatureRef = GetFeatureRef(nFeatureId); + if (poFeatureRef == NULL) + return NULL; + else if( (m_poFilterGeom == NULL || + ((poGeom = poFeatureRef->GetGeometryRef()) != NULL && + FilterGeometry( poGeom ))) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeatureRef )) ) + { + // Avoid cloning feature... return the copy owned by the class + CPLAssert(poFeatureRef == m_poCurFeature); + m_poCurFeature = NULL; + if( poFeatureRef->GetGeometryRef() != NULL ) + poFeatureRef->GetGeometryRef()->assignSpatialReference(GetSpatialRef()); + return poFeatureRef; + } + } + return NULL; +} + +/********************************************************************** + * IMapInfoFile::CreateTABFeature() + * + * Instanciate a TABFeature* from a OGRFeature* (or NULL on error) + **********************************************************************/ + +TABFeature* IMapInfoFile::CreateTABFeature(OGRFeature *poFeature) +{ + TABFeature *poTABFeature; + OGRGeometry *poGeom; + OGRwkbGeometryType eGType; + TABPoint *poTABPointFeature = NULL; + TABRegion *poTABRegionFeature = NULL; + TABPolyline *poTABPolylineFeature = NULL; + + /*----------------------------------------------------------------- + * MITAB won't accept new features unless they are in a type derived + * from TABFeature... so we have to do our best to map to the right + * feature type based on the geometry type. + *----------------------------------------------------------------*/ + poGeom = poFeature->GetGeometryRef(); + if( poGeom != NULL ) + eGType = poGeom->getGeometryType(); + else + eGType = wkbNone; + + switch( wkbFlatten(eGType) ) + { + /*------------------------------------------------------------- + * POINT + *------------------------------------------------------------*/ + case wkbPoint: + poTABFeature = new TABPoint(poFeature->GetDefnRef()); + if(poFeature->GetStyleString()) + { + poTABPointFeature = (TABPoint*)poTABFeature; + poTABPointFeature->SetSymbolFromStyleString( + poFeature->GetStyleString()); + } + break; + /*------------------------------------------------------------- + * REGION + *------------------------------------------------------------*/ + case wkbPolygon: + case wkbMultiPolygon: + poTABFeature = new TABRegion(poFeature->GetDefnRef()); + if(poFeature->GetStyleString()) + { + poTABRegionFeature = (TABRegion*)poTABFeature; + poTABRegionFeature->SetPenFromStyleString( + poFeature->GetStyleString()); + + poTABRegionFeature->SetBrushFromStyleString( + poFeature->GetStyleString()); + } + break; + /*------------------------------------------------------------- + * LINE/PLINE/MULTIPLINE + *------------------------------------------------------------*/ + case wkbLineString: + case wkbMultiLineString: + poTABFeature = new TABPolyline(poFeature->GetDefnRef()); + if(poFeature->GetStyleString()) + { + poTABPolylineFeature = (TABPolyline*)poTABFeature; + poTABPolylineFeature->SetPenFromStyleString( + poFeature->GetStyleString()); + } + break; + /*------------------------------------------------------------- + * Collection types that are not directly supported... convert + * to multiple features in output file through recursive calls. + *------------------------------------------------------------*/ + case wkbGeometryCollection: + case wkbMultiPoint: + { + OGRErr eStatus = OGRERR_NONE; + int i; + OGRGeometryCollection *poColl = (OGRGeometryCollection*)poGeom; + OGRFeature *poTmpFeature = poFeature->Clone(); + + for (i=0; eStatus==OGRERR_NONE && igetNumGeometries(); i++) + { + poTmpFeature->SetFID(OGRNullFID); + poTmpFeature->SetGeometry(poColl->getGeometryRef(i)); + eStatus = ICreateFeature(poTmpFeature); + } + delete poTmpFeature; + return NULL; + } + break; + /*------------------------------------------------------------- + * Unsupported type.... convert to MapInfo geometry NONE + *------------------------------------------------------------*/ + case wkbUnknown: + default: + poTABFeature = new TABFeature(poFeature->GetDefnRef()); + break; + } + + if( poGeom != NULL ) + poTABFeature->SetGeometryDirectly(poGeom->clone()); + + for (int i=0; i< poFeature->GetDefnRef()->GetFieldCount();i++) + { + poTABFeature->SetField(i,poFeature->GetRawFieldRef( i )); + } + + poTABFeature->SetFID(poFeature->GetFID()); + + return poTABFeature; +} + +/********************************************************************** + * IMapInfoFile::ICreateFeature() + * + * Standard OGR CreateFeature implementation. This method is used + * to create a new feature in current dataset + **********************************************************************/ +OGRErr IMapInfoFile::ICreateFeature(OGRFeature *poFeature) +{ + TABFeature *poTABFeature; + OGRErr eErr; + + poTABFeature = CreateTABFeature(poFeature); + if( poTABFeature == NULL ) /* MultiGeometry */ + return OGRERR_NONE; + + eErr = CreateFeature(poTABFeature); + if( eErr == OGRERR_NONE ) + poFeature->SetFID(poTABFeature->GetFID()); + + delete poTABFeature; + + return eErr; +} + +/********************************************************************** + * IMapInfoFile::GetFeature() + * + * Standard OGR GetFeature implementation. This method is used + * to get the wanted (nFeatureId) feature, a NULL value will be + * returned on error. + **********************************************************************/ +OGRFeature *IMapInfoFile::GetFeature(GIntBig nFeatureId) +{ + OGRFeature *poFeatureRef; + + /*fprintf(stderr, "GetFeature(%ld)\n", nFeatureId);*/ + + poFeatureRef = GetFeatureRef(nFeatureId); + if (poFeatureRef) + { + // Avoid cloning feature... return the copy owned by the class + CPLAssert(poFeatureRef == m_poCurFeature); + m_poCurFeature = NULL; + + return poFeatureRef; + } + else + return NULL; +} + +/************************************************************************/ +/* GetTABType() */ +/* */ +/* Create a native field based on a generic OGR definition. */ +/************************************************************************/ + +int IMapInfoFile::GetTABType( OGRFieldDefn *poField, + TABFieldType* peTABType, + int *pnWidth) +{ + TABFieldType eTABType; + int nWidth = poField->GetWidth(); + + if( poField->GetType() == OFTInteger ) + { + eTABType = TABFInteger; + if( nWidth == 0 ) + nWidth = 12; + } + else if( poField->GetType() == OFTReal ) + { + if( nWidth == 0 && poField->GetPrecision() == 0) + { + eTABType = TABFFloat; + nWidth = 32; + } + else + { + eTABType = TABFDecimal; + } + } + else if( poField->GetType() == OFTDate ) + { + eTABType = TABFDate; + if( nWidth == 0 ) + nWidth = 10; + } + else if( poField->GetType() == OFTTime ) + { + eTABType = TABFTime; + if( nWidth == 0 ) + nWidth = 9; + } + else if( poField->GetType() == OFTDateTime ) + { + eTABType = TABFDateTime; + if( nWidth == 0 ) + nWidth = 19; + } + else if( poField->GetType() == OFTString ) + { + eTABType = TABFChar; + if( nWidth == 0 ) + nWidth = 254; + else + nWidth = MIN(254,nWidth); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "IMapInfoFile::CreateField() called with unsupported field" + " type %d.\n" + "Note that Mapinfo files don't support list field types.\n", + poField->GetType() ); + + return -1; + } + + *peTABType = eTABType; + *pnWidth = nWidth; + + return 0; +} + +/************************************************************************/ +/* CreateField() */ +/* */ +/* Create a native field based on a generic OGR definition. */ +/************************************************************************/ + +OGRErr IMapInfoFile::CreateField( OGRFieldDefn *poField, int bApproxOK ) + +{ + TABFieldType eTABType; + int nWidth; + + if( GetTABType( poField, &eTABType, &nWidth ) < 0 ) + return OGRERR_FAILURE; + + if( AddFieldNative( poField->GetNameRef(), eTABType, + nWidth, poField->GetPrecision(), FALSE, FALSE, bApproxOK ) > -1 ) + return OGRERR_NONE; + else + return OGRERR_FAILURE; +} + + +/********************************************************************** + * IMapInfoFile::SetCharset() + * + * Set the charset for the tab header. + * + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int IMapInfoFile::SetCharset(const char* pszCharset) +{ + if(pszCharset && strlen(pszCharset) > 0) + { + CPLFree(m_pszCharset); + m_pszCharset = CPLStrdup(pszCharset); + return 0; + } + return -1; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_indfile.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_indfile.cpp new file mode 100644 index 000000000..8ec0a9604 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_indfile.cpp @@ -0,0 +1,2159 @@ +/********************************************************************** + * $Id: mitab_indfile.cpp,v 1.14 2010-07-07 19:00:15 aboudreault Exp $ + * + * Name: mitab_indfile.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABINDFile class used to handle + * access to .IND file (table field indexes) attached to a .DAT file + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2001, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_indfile.cpp,v $ + * Revision 1.14 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.13 2008-01-29 20:46:32 dmorissette + * Added support for v9 Time and DateTime fields (byg 1754) + * + * Revision 1.12 2007/12/11 03:43:03 dmorissette + * Added reporting access mode to error message in TABINDFile::Open() + * (GDAL changeset r12460, ticket 1620) + * + * Revision 1.11 2005/04/29 19:08:56 dmorissette + * Produce an error if m_nSubtreeDepth > 255 when creating a .IND (OGR bug 839) + * + * Revision 1.10 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.9 2003/07/24 02:45:57 daniel + * Fixed problem scanning node in TABINDNode::FindNext() - bug 2176, FW + * + * Revision 1.8 2001/05/01 03:38:23 daniel + * Added update support (allows creating new index in existing IND files). + * + * Revision 1.7 2000/11/13 22:17:57 daniel + * When a (child) node's first entry is replaced by InsertEntry() then make + * sure that node's key is updated in its parent node. + * + * Revision 1.6 2000/03/01 00:32:00 daniel + * Added support for float keys, and completed support for generating indexes + * + * Revision 1.5 2000/02/28 16:57:42 daniel + * Added support for writing indexes + * + * Revision 1.4 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.3 1999/12/14 05:52:05 daniel + * Fixed compile error on Windows + * + * Revision 1.2 1999/12/14 02:19:42 daniel + * Completed .IND support for simple TABViews + * + * Revision 1.1 1999/11/20 15:49:07 daniel + * Initial version + * + **********************************************************************/ + +#include "mitab.h" +#include "mitab_utils.h" + +#include /* toupper() */ + +/*===================================================================== + * class TABINDFile + *====================================================================*/ + +#define IND_MAGIC_COOKIE 24242424 + +/********************************************************************** + * TABINDFile::TABINDFile() + * + * Constructor. + **********************************************************************/ +TABINDFile::TABINDFile() +{ + m_fp = NULL; + m_pszFname = NULL; + m_eAccessMode = TABRead; + m_numIndexes = 0; + m_papoIndexRootNodes = NULL; + m_papbyKeyBuffers = NULL; + m_oBlockManager.SetName("IND"); +} + +/********************************************************************** + * TABINDFile::~TABINDFile() + * + * Destructor. + **********************************************************************/ +TABINDFile::~TABINDFile() +{ + Close(); +} + +/********************************************************************** + * TABINDFile::Open() + * + * Open a .IND file, read the header and the root nodes for all the + * field indexes, and be ready to search the indexes. + * + * If the filename that is passed in contains a .DAT extension then + * the extension will be changed to .IND before trying to open the file. + * + * Note that we pass a pszAccess flag, but only read access is supported + * for now (and there are no plans to support write.) + * + * Set bTestOpenNoError=TRUE to silently return -1 with no error message + * if the file cannot be opened because it does not exist. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABINDFile::Open(const char *pszFname, const char *pszAccess, + GBool bTestOpenNoError /*=FALSE*/) +{ + int nLen; + + if (m_fp) + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: object already contains an open file"); + return -1; + } + + /*----------------------------------------------------------------- + * Validate access mode and make sure we use binary access. + * Note that for write access, we actually need read/write access to + * the file. + *----------------------------------------------------------------*/ + if (EQUALN(pszAccess, "r", 1) && strchr(pszAccess, '+') != NULL) + { + m_eAccessMode = TABReadWrite; + pszAccess = "rb+"; + } + else if (EQUALN(pszAccess, "r", 1)) + { + m_eAccessMode = TABRead; + pszAccess = "rb"; + } + else if (EQUALN(pszAccess, "w", 1)) + { + m_eAccessMode = TABWrite; + pszAccess = "wb+"; + } + else + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: access mode \"%s\" not supported", pszAccess); + return -1; + } + + /*----------------------------------------------------------------- + * Change .DAT (or .TAB) extension to .IND if necessary + *----------------------------------------------------------------*/ + m_pszFname = CPLStrdup(pszFname); + + nLen = strlen(m_pszFname); + if (nLen > 4 && !EQUAL(m_pszFname+nLen-4, ".IND") ) + strcpy(m_pszFname+nLen-4, ".ind"); + +#ifndef _WIN32 + TABAdjustFilenameExtension(m_pszFname); +#endif + + /*----------------------------------------------------------------- + * Open file + *----------------------------------------------------------------*/ + m_fp = VSIFOpenL(m_pszFname, pszAccess); + + if (m_fp == NULL) + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed for %s (%s)", m_pszFname, pszAccess); + + CPLFree(m_pszFname); + m_pszFname = NULL; + return -1; + } + + /*----------------------------------------------------------------- + * Reset block manager to allocate first block at byte 512, after header. + *----------------------------------------------------------------*/ + m_oBlockManager.Reset(); + m_oBlockManager.AllocNewBlock(); + + /*----------------------------------------------------------------- + * Read access: Read the header block + * This will also alloc and init the array of index root nodes. + *----------------------------------------------------------------*/ + if ((m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) && + ReadHeader() != 0) + { + // Failed reading header... CPLError() has already been called + Close(); + return -1; + } + + /*----------------------------------------------------------------- + * Write access: Init class members and write a dummy header block + *----------------------------------------------------------------*/ + if (m_eAccessMode == TABWrite) + { + m_numIndexes = 0; + + if (WriteHeader() != 0) + { + // Failed writing header... CPLError() has already been called + Close(); + return -1; + } + } + + return 0; +} + +/********************************************************************** + * TABINDFile::Close() + * + * Close current file, and release all memory used. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABINDFile::Close() +{ + if (m_fp == NULL) + return 0; + + /*----------------------------------------------------------------- + * In Write Mode, commit all indexes to the file + *----------------------------------------------------------------*/ + if (m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite) + { + WriteHeader(); + + for(int iIndex=0; iIndexCommitToFile(); + } + } + } + + /*----------------------------------------------------------------- + * Free index nodes in memory + *----------------------------------------------------------------*/ + for (int iIndex=0; iIndexReadFromFile(m_fp, 0, 512) != 0) + { + // CPLError() has already been called. + delete poHeaderBlock; + return -1; + } + + poHeaderBlock->GotoByteInBlock(0); + GUInt32 nMagicCookie = poHeaderBlock->ReadInt32(); + if (nMagicCookie != IND_MAGIC_COOKIE) + { + CPLError(CE_Failure, CPLE_FileIO, + "%s: Invalid Magic Cookie: got %d, expected %d", + m_pszFname, nMagicCookie, IND_MAGIC_COOKIE); + delete poHeaderBlock; + return -1; + } + + poHeaderBlock->GotoByteInBlock(12); + m_numIndexes = poHeaderBlock->ReadInt16(); + if (m_numIndexes < 1 || m_numIndexes > 29) + { + CPLError(CE_Failure, CPLE_FileIO, + "Invalid number of indexes (%d) in file %s", + m_numIndexes, m_pszFname); + delete poHeaderBlock; + return -1; + } + + /*----------------------------------------------------------------- + * Alloc and init the array of index root nodes. + *----------------------------------------------------------------*/ + m_papoIndexRootNodes = (TABINDNode**)CPLCalloc(m_numIndexes, + sizeof(TABINDNode*)); + + m_papbyKeyBuffers = (GByte **)CPLCalloc(m_numIndexes, sizeof(GByte*)); + + /* First index def. starts at byte 48 */ + poHeaderBlock->GotoByteInBlock(48); + + for(int iIndex=0; iIndexReadInt32(); + poHeaderBlock->ReadInt16(); // skip... max. num of entries per node + int nTreeDepth = poHeaderBlock->ReadByte(); + int nKeyLength = poHeaderBlock->ReadByte(); + poHeaderBlock->GotoByteRel(8); // skip next 8 bytes; + + /*------------------------------------------------------------- + * And init root node for this index. + * Note that if nRootNodePtr==0 then this means that the + * corresponding index does not exist (i.e. has been deleted?) + * so we simply do not allocate the root node in this case. + * An error will be produced if the user tries to access this index + * later during execution. + *------------------------------------------------------------*/ + if (nRootNodePtr > 0) + { + m_papoIndexRootNodes[iIndex] = new TABINDNode(m_eAccessMode); + if (m_papoIndexRootNodes[iIndex]->InitNode(m_fp, nRootNodePtr, + nKeyLength, nTreeDepth, + FALSE, + &m_oBlockManager)!= 0) + { + // CPLError has already been called + delete poHeaderBlock; + return -1; + } + + // Alloc a temporary key buffer for this index. + // This buffer will be used by the BuildKey() method + m_papbyKeyBuffers[iIndex] = (GByte *)CPLCalloc(nKeyLength+1, + sizeof(GByte)); + } + else + { + m_papoIndexRootNodes[iIndex] = NULL; + m_papbyKeyBuffers[iIndex] = NULL; + } + } + + /*----------------------------------------------------------------- + * OK, we won't need the header block any more... free it. + *----------------------------------------------------------------*/ + delete poHeaderBlock; + + return 0; +} + + +/********************************************************************** + * TABINDFile::WriteHeader() + * + * (private method) + * Write the header block based on current index information. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABINDFile::WriteHeader() +{ + CPLAssert(m_fp); + CPLAssert(m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite); + + /*----------------------------------------------------------------- + * Write the 48 bytes of file header + *----------------------------------------------------------------*/ + TABRawBinBlock *poHeaderBlock; + poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE); + poHeaderBlock->InitNewBlock(m_fp, 512, 0); + + poHeaderBlock->WriteInt32( IND_MAGIC_COOKIE ); + + poHeaderBlock->WriteInt16( 100 ); // ??? + poHeaderBlock->WriteInt16( 512 ); // ??? + poHeaderBlock->WriteInt32( 0 ); // ??? + + poHeaderBlock->WriteInt16( (GInt16)m_numIndexes ); + + poHeaderBlock->WriteInt16( 0x15e7); // ??? + + poHeaderBlock->WriteInt16( 10 ); // ??? + poHeaderBlock->WriteInt16( 0x611d); // ??? + + poHeaderBlock->WriteZeros( 28 ); + + /*----------------------------------------------------------------- + * The first index definition starts at byte 48 + *----------------------------------------------------------------*/ + for(int iIndex=0; iIndexWriteInt32(poRootNode->GetNodeBlockPtr()); + poHeaderBlock->WriteInt16((GInt16)poRootNode->GetMaxNumEntries()); + poHeaderBlock->WriteByte( (GByte)poRootNode->GetSubTreeDepth()); + poHeaderBlock->WriteByte( (GByte)poRootNode->GetKeyLength()); + + poHeaderBlock->WriteZeros( 8 ); + + /*--------------------------------------------------------- + * Look for overflow of the SubTreeDepth field (byte) + *--------------------------------------------------------*/ + if (poRootNode->GetSubTreeDepth() > 255) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Index no %d is too large and will not be useable. " + "(SubTreeDepth = %d, cannot exceed 255).", + iIndex+1, poRootNode->GetSubTreeDepth()); + return -1; + } + } + else + { + /*--------------------------------------------------------- + * NULL Root Node: This index has likely been deleted + *--------------------------------------------------------*/ + poHeaderBlock->WriteZeros( 16 ); + } + } + + /*----------------------------------------------------------------- + * OK, we won't need the header block any more... write and free it. + *----------------------------------------------------------------*/ + if (poHeaderBlock->CommitToFile() != 0) + return -1; + + delete poHeaderBlock; + + return 0; +} + +/********************************************************************** + * TABINDFile::ValidateIndexNo() + * + * Private method to validate the index no parameter of some methods... + * returns 0 if index no. is OK, or produces an error ands returns -1 + * if index no is not valid. + **********************************************************************/ +int TABINDFile::ValidateIndexNo(int nIndexNumber) +{ + if (m_fp == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABINDFile: File has not been opened yet!"); + return -1; + } + + if (nIndexNumber < 1 || nIndexNumber > m_numIndexes || + m_papoIndexRootNodes == NULL || + m_papoIndexRootNodes[nIndexNumber-1] == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "No field index number %d in %s: Valid range is [1..%d].", + nIndexNumber, m_pszFname, m_numIndexes); + return -1; + } + + return 0; // Index seems valid +} + +/********************************************************************** + * TABINDFile::SetIndexFieldType() + * + * Sets the field type for the specified index. + * This information will then be used in building the key values, etc. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABINDFile::SetIndexFieldType(int nIndexNumber, TABFieldType eType) +{ + if (ValidateIndexNo(nIndexNumber) != 0) + return -1; + + return m_papoIndexRootNodes[nIndexNumber-1]->SetFieldType(eType); +} + +/********************************************************************** + * TABINDFile::SetIndexUnique() + * + * Indicate that an index's keys are unique. This allows for some + * optimization with read access. By default, an index is treated as if + * its keys could have duplicates. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABINDFile::SetIndexUnique(int nIndexNumber, GBool bUnique/*=TRUE*/) +{ + if (ValidateIndexNo(nIndexNumber) != 0) + return -1; + + m_papoIndexRootNodes[nIndexNumber-1]->SetUnique(bUnique); + + return 0; +} + +/********************************************************************** + * TABINDFile::BuildKey() + * + * Encode a field value in the form required to be compared with index + * keys in the specified index. + * + * Note that index numbers are positive values starting at 1. + * + * Returns a reference to an internal buffer that is valid only until the + * next call to BuildKey(). (should not be freed by the caller). + * Returns NULL if field index is invalid. + * + * The first flavour of the function handles integer type of values, this + * corresponds to MapInfo types: integer, smallint, logical and date + **********************************************************************/ +GByte *TABINDFile::BuildKey(int nIndexNumber, GInt32 nValue) +{ + if (ValidateIndexNo(nIndexNumber) != 0) + return NULL; + + int nKeyLength = m_papoIndexRootNodes[nIndexNumber-1]->GetKeyLength(); + + /*----------------------------------------------------------------- + * Convert all int values to MSB using the right number of bytes + * Note: + * The most significant bit has to be unset for negative values, + * and to be set for positive ones... that's the reverse of what it + * should usually be. Adding 0x80 to the MSB byte will do the job. + *----------------------------------------------------------------*/ + switch(nKeyLength) + { + case 1: + m_papbyKeyBuffers[nIndexNumber-1][0] = (GByte)(nValue & 0xff)+0x80; + break; + case 2: + m_papbyKeyBuffers[nIndexNumber-1][0] = + (GByte)(nValue/0x100 & 0xff)+0x80; + m_papbyKeyBuffers[nIndexNumber-1][1] = (GByte)(nValue & 0xff); + break; + case 4: + m_papbyKeyBuffers[nIndexNumber-1][0] = + (GByte)(nValue/0x1000000 &0xff)+0x80; + m_papbyKeyBuffers[nIndexNumber-1][1] = (GByte)(nValue/0x10000 & 0xff); + m_papbyKeyBuffers[nIndexNumber-1][2] = (GByte)(nValue/0x100 &0xff); + m_papbyKeyBuffers[nIndexNumber-1][3] = (GByte)(nValue & 0xff); + break; + default: + CPLError(CE_Failure, CPLE_AssertionFailed, + "BuildKey(): %d bytes integer key length not supported", + nKeyLength); + break; + } + + return m_papbyKeyBuffers[nIndexNumber-1]; +} + +/********************************************************************** + * TABINDFile::BuildKey() + * + * BuildKey() for string fields + **********************************************************************/ +GByte *TABINDFile::BuildKey(int nIndexNumber, const char *pszStr) +{ + if (ValidateIndexNo(nIndexNumber) != 0 || pszStr == NULL) + return NULL; + + int nKeyLength = m_papoIndexRootNodes[nIndexNumber-1]->GetKeyLength(); + + /*----------------------------------------------------------------- + * Strings keys are all in uppercase, and padded with '\0' + *----------------------------------------------------------------*/ + int i=0; + for (i=0; iGetKeyLength(); + CPLAssert(nKeyLength == 8 && sizeof(double) == 8); + + /*----------------------------------------------------------------- + * Convert double and decimal values... + * Reverse the sign of the value, and convert to MSB + *----------------------------------------------------------------*/ + dValue = -dValue; + +#ifndef CPL_MSB + CPL_SWAPDOUBLE(&dValue); +#endif + + memcpy(m_papbyKeyBuffers[nIndexNumber-1], (GByte*)(&dValue), nKeyLength); + + return m_papbyKeyBuffers[nIndexNumber-1]; +} + + +/********************************************************************** + * TABINDFile::FindFirst() + * + * Search one of the indexes for a key value. + * + * Note that index numbers are positive values starting at 1. + * + * Return value: + * - the key's corresponding record number in the .DAT file (greater than 0) + * - 0 if the key was not found + * - or -1 if an error happened + **********************************************************************/ +GInt32 TABINDFile::FindFirst(int nIndexNumber, GByte *pKeyValue) +{ + if (ValidateIndexNo(nIndexNumber) != 0) + return -1; + + return m_papoIndexRootNodes[nIndexNumber-1]->FindFirst(pKeyValue); +} + +/********************************************************************** + * TABINDFile::FindNext() + * + * Continue the Search for pKeyValue previously initiated by FindFirst(). + * NOTE: FindFirst() MUST have been previously called for this call to + * work... + * + * Note that index numbers are positive values starting at 1. + * + * Return value: + * - the key's corresponding record number in the .DAT file (greater than 0) + * - 0 if the key was not found + * - or -1 if an error happened + **********************************************************************/ +GInt32 TABINDFile::FindNext(int nIndexNumber, GByte *pKeyValue) +{ + if (ValidateIndexNo(nIndexNumber) != 0) + return -1; + + return m_papoIndexRootNodes[nIndexNumber-1]->FindNext(pKeyValue); +} + + +/********************************************************************** + * TABINDFile::CreateIndex() + * + * Create a new index with the specified field type and size. + * Field size applies only to char field type... the other types have a + * predefined key length. + * + * Key length is limited to 128 chars. char fields longer than 128 chars + * will have their key truncated to 128 bytes. + * + * Note that a .IND file can contain only a maximum of 29 indexes. + * + * Returns the new field index on success (greater than 0), or -1 on error. + **********************************************************************/ +int TABINDFile::CreateIndex(TABFieldType eType, int nFieldSize) +{ + int i, nNewIndexNo = -1; + + if (m_fp == NULL || + (m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite)) + return -1; + + // __TODO__ + // We'll need more work in TABDATFile::WriteDateTimeField() before + // we can support indexes on fields of type DateTime (see bug #1844) + if (eType == TABFDateTime) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Index on fields of type DateTime not supported yet."); + return -1; + } + + /*----------------------------------------------------------------- + * Look for an empty slot in the current array, if there is none + * then extend the array. + *----------------------------------------------------------------*/ + for(i=0; m_papoIndexRootNodes && i= 29) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot add new index to %s. A dataset can contain only a " + "maximum of 29 indexes.", m_pszFname); + return -1; + } + + if (nNewIndexNo == -1) + { + /*------------------------------------------------------------- + * Add a slot for new index at the end of the nodes array. + *------------------------------------------------------------*/ + m_numIndexes++; + m_papoIndexRootNodes = (TABINDNode**)CPLRealloc( m_papoIndexRootNodes, + m_numIndexes* + sizeof(TABINDNode*)); + + m_papbyKeyBuffers = (GByte **)CPLRealloc(m_papbyKeyBuffers, + m_numIndexes*sizeof(GByte*)); + + nNewIndexNo = m_numIndexes-1; + } + + /*----------------------------------------------------------------- + * Alloc and init new node + * The call to InitNode() automatically allocates storage space for + * the node in the file. + * New nodes are created with a subtree_depth=1 since they start as + * leaf nodes, i.e. their entries point directly to .DAT records + *----------------------------------------------------------------*/ + int nKeyLength = ((eType == TABFInteger) ? 4: + (eType == TABFSmallInt) ? 2: + (eType == TABFFloat) ? 8: + (eType == TABFDecimal) ? 8: + (eType == TABFDate) ? 4: + (eType == TABFTime) ? 4: + (eType == TABFDateTime) ? 8: + (eType == TABFLogical) ? 4: MIN(128,nFieldSize)); + + m_papoIndexRootNodes[nNewIndexNo] = new TABINDNode(m_eAccessMode); + if (m_papoIndexRootNodes[nNewIndexNo]->InitNode(m_fp, 0, nKeyLength, + 1, // subtree depth=1 + FALSE, // not unique + &m_oBlockManager, + NULL, 0, 0)!= 0) + { + // CPLError has already been called + return -1; + } + + // Alloc a temporary key buffer for this index. + // This buffer will be used by the BuildKey() method + m_papbyKeyBuffers[nNewIndexNo] = (GByte *)CPLCalloc(nKeyLength+1, + sizeof(GByte)); + + // Return 1-based index number + return nNewIndexNo+1; +} + + +/********************************************************************** + * TABINDFile::AddEntry() + * + * Add an .DAT record entry for pKeyValue in the specified index. + * + * Note that index numbers are positive values starting at 1. + * nRecordNo is the .DAT record number, record numbers start at 1. + * + * Returns 0 on success, -1 on error + **********************************************************************/ +int TABINDFile::AddEntry(int nIndexNumber, GByte *pKeyValue, GInt32 nRecordNo) +{ + if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) || + ValidateIndexNo(nIndexNumber) != 0) + return -1; + + return m_papoIndexRootNodes[nIndexNumber-1]->AddEntry(pKeyValue,nRecordNo); +} + + +/********************************************************************** + * TABINDFile::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABINDFile::Dump(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABINDFile::Dump() -----\n"); + + if (m_fp == NULL) + { + fprintf(fpOut, "File is not opened.\n"); + } + else + { + fprintf(fpOut, "File is opened: %s\n", m_pszFname); + fprintf(fpOut, " m_numIndexes = %d\n", m_numIndexes); + for(int i=0; iDump(fpOut); + } + } + + } + + fflush(fpOut); +} + +#endif // DEBUG + + + + + +/*===================================================================== + * class TABINDNode + *====================================================================*/ + +/********************************************************************** + * TABINDNode::TABINDNode() + * + * Constructor. + **********************************************************************/ +TABINDNode::TABINDNode(TABAccess eAccessMode /*=TABRead*/) +{ + m_fp = NULL; + m_poCurChildNode = NULL; + m_nSubTreeDepth = 0; + m_nKeyLength = 0; + m_eFieldType = TABFUnknown; + m_poDataBlock = NULL; + m_numEntriesInNode = 0; + m_nCurIndexEntry = 0; + m_nPrevNodePtr = 0; + m_nNextNodePtr = 0; + m_poBlockManagerRef = NULL; + m_poParentNodeRef = NULL; + m_bUnique = FALSE; + + m_eAccessMode = eAccessMode; +} + +/********************************************************************** + * TABINDNode::~TABINDNode() + * + * Destructor. + **********************************************************************/ +TABINDNode::~TABINDNode() +{ + if (m_poCurChildNode) + delete m_poCurChildNode; + + if (m_poDataBlock) + delete m_poDataBlock; +} + +/********************************************************************** + * TABINDNode::InitNode() + * + * Init a node... this function can be used either to initialize a new + * node, or to make it point to a new data block in the file. + * + * By default, this call will read the data from the file at the + * specified location if necessary, and leave the object ready to be searched. + * + * In write access, if the block does not exist (i.e. nBlockPtr=0) then a + * new one is created and initialized. + * + * poParentNode is used in write access in order to update the parent node + * when this node becomes full and has to be split. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABINDNode::InitNode(VSILFILE *fp, int nBlockPtr, + int nKeyLength, int nSubTreeDepth, + GBool bUnique, + TABBinBlockManager *poBlockMgr /*=NULL*/, + TABINDNode *poParentNode /*=NULL*/, + int nPrevNodePtr /*=0*/, int nNextNodePtr /*=0*/) +{ + /*----------------------------------------------------------------- + * If the block already points to the right block, then don't do + * anything here. + *----------------------------------------------------------------*/ + if (m_fp == fp && nBlockPtr> 0 && m_nCurDataBlockPtr == nBlockPtr) + return 0; + + // Keep track of some info + m_fp = fp; + m_nKeyLength = nKeyLength; + m_nSubTreeDepth = nSubTreeDepth; + m_nCurDataBlockPtr = nBlockPtr; + m_bUnique = bUnique; + + // Do not overwrite the following values if we receive NULL (the defaults) + if (poBlockMgr) + m_poBlockManagerRef = poBlockMgr; + if (poParentNode) + m_poParentNodeRef = poParentNode; + + // Set some defaults + m_numEntriesInNode = 0; + m_nPrevNodePtr = nPrevNodePtr; + m_nNextNodePtr = nNextNodePtr; + + m_nCurIndexEntry = 0; + + /*----------------------------------------------------------------- + * Init RawBinBlock + * The node's buffer has to be created with read/write access since + * the index is a very dynamic structure! + *----------------------------------------------------------------*/ + if (m_poDataBlock == NULL) + m_poDataBlock = new TABRawBinBlock(TABReadWrite, TRUE); + + if ((m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite) && + nBlockPtr == 0 && m_poBlockManagerRef) + { + /*------------------------------------------------------------- + * Write access: Create and init a new block + *------------------------------------------------------------*/ + m_nCurDataBlockPtr = m_poBlockManagerRef->AllocNewBlock(); + m_poDataBlock->InitNewBlock(m_fp, 512, m_nCurDataBlockPtr); + + m_poDataBlock->WriteInt32( m_numEntriesInNode ); + m_poDataBlock->WriteInt32( m_nPrevNodePtr ); + m_poDataBlock->WriteInt32( m_nNextNodePtr ); + } + else + { + CPLAssert(m_nCurDataBlockPtr > 0); + /*------------------------------------------------------------- + * Read the data block from the file, applies to read access, or + * to write access (to modify an existing block) + *------------------------------------------------------------*/ + if (m_poDataBlock->ReadFromFile(m_fp, m_nCurDataBlockPtr, 512) != 0) + { + // CPLError() has already been called. + return -1; + } + + m_poDataBlock->GotoByteInBlock(0); + m_numEntriesInNode = m_poDataBlock->ReadInt32(); + m_nPrevNodePtr = m_poDataBlock->ReadInt32(); + m_nNextNodePtr = m_poDataBlock->ReadInt32(); + } + + // m_poDataBlock is now positioned at the beginning of the key entries + + return 0; +} + + +/********************************************************************** + * TABINDNode::GotoNodePtr() + * + * Move to the specified node ptr, and read the new node data from the file. + * + * This is just a cover funtion on top of InitNode() + **********************************************************************/ +int TABINDNode::GotoNodePtr(GInt32 nNewNodePtr) +{ + // First flush current changes if any. + if ((m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite) && + m_poDataBlock && m_poDataBlock->CommitToFile() != 0) + return -1; + + CPLAssert(nNewNodePtr % 512 == 0); + + // Then move to the requested location. + return InitNode(m_fp, nNewNodePtr, m_nKeyLength, m_nSubTreeDepth, + m_bUnique); +} + +/********************************************************************** + * TABINDNode::ReadIndexEntry() + * + * Read the key value and record/node ptr for the specified index entry + * inside the current node data. + * + * nEntryNo is the 0-based index of the index entry that we are interested + * in inside the current node. + * + * Returns the record/node ptr, and copies the key value inside the + * buffer pointed to by *pKeyValue... this assumes that *pKeyValue points + * to a buffer big enough to hold the key value (m_nKeyLength bytes). + * If pKeyValue == NULL, then this parameter is ignored and the key value + * is not copied. + **********************************************************************/ +GInt32 TABINDNode::ReadIndexEntry(int nEntryNo, GByte *pKeyValue) +{ + GInt32 nRecordPtr = 0; + if (nEntryNo >= 0 && nEntryNo < m_numEntriesInNode) + { + if (pKeyValue) + { + m_poDataBlock->GotoByteInBlock(12 + nEntryNo*(m_nKeyLength+4)); + m_poDataBlock->ReadBytes(m_nKeyLength, pKeyValue); + } + else + { + m_poDataBlock->GotoByteInBlock(12 + nEntryNo*(m_nKeyLength+4)+ + m_nKeyLength); + } + + nRecordPtr = m_poDataBlock->ReadInt32(); + } + + return nRecordPtr; +} + +/********************************************************************** + * TABINDNode::IndexKeyCmp() + * + * Compare the specified index entry with the key value, and + * return 0 if equal, an integer less than 0 if key is smaller than + * index entry, and an integer greater than 0 if key is bigger than + * index entry. + * + * nEntryNo is the 0-based index of the index entry that we are interested + * in inside the current node. + **********************************************************************/ +int TABINDNode::IndexKeyCmp(GByte *pKeyValue, int nEntryNo) +{ + CPLAssert(pKeyValue); + CPLAssert(nEntryNo >= 0 && nEntryNo < m_numEntriesInNode); + + m_poDataBlock->GotoByteInBlock(12 + nEntryNo*(m_nKeyLength+4)); + + return memcmp(pKeyValue, m_poDataBlock->GetCurDataPtr(), m_nKeyLength); +} + +/********************************************************************** + * TABINDNode::SetFieldType() + * + * Sets the field type for the current index and recursively set all + * children as well. + * This information will then be used in building the key values, etc. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABINDNode::SetFieldType(TABFieldType eType) +{ + if (m_fp == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABINDNode::SetFieldType(): File has not been opened yet!"); + return -1; + } + + /*----------------------------------------------------------------- + * Validate field type with key length + *----------------------------------------------------------------*/ + if ((eType == TABFInteger && m_nKeyLength != 4) || + (eType == TABFSmallInt && m_nKeyLength != 2) || + (eType == TABFFloat && m_nKeyLength != 8) || + (eType == TABFDecimal && m_nKeyLength != 8) || + (eType == TABFDate && m_nKeyLength != 4) || + (eType == TABFTime && m_nKeyLength != 4) || + (eType == TABFDateTime && m_nKeyLength != 8) || + (eType == TABFLogical && m_nKeyLength != 4) ) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Index key length (%d) does not match field type (%s).", + m_nKeyLength, TABFIELDTYPE_2_STRING(eType) ); + return -1; + } + + m_eFieldType = eType; + + /*----------------------------------------------------------------- + * Pass the field type info to child nodes + *----------------------------------------------------------------*/ + if (m_poCurChildNode) + return m_poCurChildNode->SetFieldType(eType); + + return 0; +} + +/********************************************************************** + * TABINDNode::FindFirst() + * + * Start a new search in this node and its children for a key value. + * If the index is not unique, then FindNext() can be used to return + * the other values that correspond to the key. + * + * Return value: + * - the key's corresponding record number in the .DAT file (greater than 0) + * - 0 if the key was not found + * - or -1 if an error happened + **********************************************************************/ +GInt32 TABINDNode::FindFirst(GByte *pKeyValue) +{ + if (m_poDataBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABINDNode::Search(): Node has not been initialized yet!"); + return -1; + } + + /*----------------------------------------------------------------- + * Unless something has been broken, this method will be called by our + * parent node after it has established that we are the best candidate + * to contain the first instance of the key value. So there is no + * need to look in the previous or next nodes in the chain... if the + * value is not found in the current node block then it is not present + * in the index at all. + * + * m_nCurIndexEntry will be used to keep track of the search pointer + * when FindNext() will be used. + *----------------------------------------------------------------*/ + m_nCurIndexEntry = 0; + + if (m_nSubTreeDepth == 1) + { + /*------------------------------------------------------------- + * Leaf node level... we look for an exact match + *------------------------------------------------------------*/ + while(m_nCurIndexEntry < m_numEntriesInNode) + { + int nCmpStatus = IndexKeyCmp(pKeyValue, m_nCurIndexEntry); + if (nCmpStatus > 0) + { + /* Not there yet... (pKey > IndexEntry) */ + m_nCurIndexEntry++; + } + else if (nCmpStatus == 0) + { + /* Found it! Return the record number */ + return ReadIndexEntry(m_nCurIndexEntry, NULL); + } + else + { + /* Item does not exist... return 0 */ + return 0; + } + } + } + else + { + /*------------------------------------------------------------- + * Index Node: Find the child node that is the best candidate to + * contain the value + * + * In the index tree at the node level, for each node entry inside + * the parent node, the key value (in the parent) corresponds to + * the value of the first key that you will find when you access + * the corresponding child node. + * + * This means that to find the child that contains the searched + * key, we look for the first index key >= pKeyValue and the child + * node that we are looking for is the one that precedes it. + * + * If the first key in the list is >= pKeyValue then this means + * that the pKeyValue does not exist in our children and we just + * return 0. We do not bother searching the previous node at the + * same level since this is the responsibility of our parent. + * + * The same way if the last indexkey in this node is < pKeyValue + * we won't bother searching the next node since this should also + * be taken care of by our parent. + *------------------------------------------------------------*/ + while(m_nCurIndexEntry < m_numEntriesInNode) + { + int nCmpStatus = IndexKeyCmp(pKeyValue, m_nCurIndexEntry); + + if (nCmpStatus > 0 && m_nCurIndexEntry+1 < m_numEntriesInNode) + { + /* Not there yet... (pKey > IndexEntry) */ + m_nCurIndexEntry++; + } + else + { + /*----------------------------------------------------- + * We either found an indexkey >= pKeyValue or reached + * the last entry in this node... still have to decide + * what we're going to do... + *----------------------------------------------------*/ + if (nCmpStatus < 0 && m_nCurIndexEntry == 0) + { + /*------------------------------------------------- + * First indexkey in block is > pKeyValue... + * the key definitely does not exist in our children. + * However, we still want to drill down the rest of the + * tree because this function is also used when looking + * for a node to insert a new value. + *-------------------------------------------------*/ + // Nothing special to do... just continue processing. + } + + /*----------------------------------------------------- + * If we found an node for which pKeyValue < indexkey + * (or pKeyValue <= indexkey for non-unique indexes) then + * we access the preceding child node. + * + * Note that for indexkey == pKeyValue in non-unique indexes + * we also check in the preceding node because when keys + * are not unique then there are chances that the requested + * key could also be found at the end of the preceding node. + * In this case, if we don't find the key in the preceding + * node then we'll do a second search in the current node. + *----------------------------------------------------*/ + int numChildrenToVisit=1; + if (m_nCurIndexEntry > 0 && + (nCmpStatus < 0 || (nCmpStatus==0 && !m_bUnique)) ) + { + m_nCurIndexEntry--; + if (nCmpStatus == 0) + numChildrenToVisit = 2; + } + + /*----------------------------------------------------- + * OK, now it's time to load/access the candidate child nodes. + *----------------------------------------------------*/ + int nRetValue = 0; + for(int iChild=0; nRetValue==0 && + iChild 0) + m_nCurIndexEntry++; + + int nChildNodePtr = ReadIndexEntry(m_nCurIndexEntry, NULL); + if (nChildNodePtr == 0) + { + /* Invalid child node??? */ + nRetValue = 0; + continue; + } + else if (m_poCurChildNode == NULL) + { + /* Child node has never been initialized...do it now!*/ + + m_poCurChildNode = new TABINDNode(m_eAccessMode); + if ( m_poCurChildNode->InitNode(m_fp, nChildNodePtr, + m_nKeyLength, + m_nSubTreeDepth-1, + m_bUnique, + m_poBlockManagerRef, + this) != 0 || + m_poCurChildNode->SetFieldType(m_eFieldType)!=0) + { + // An error happened... and was already reported + return -1; + } + } + + if (m_poCurChildNode->GotoNodePtr(nChildNodePtr) != 0) + { + // An error happened and has already been reported + return -1; + } + + nRetValue = m_poCurChildNode->FindFirst(pKeyValue); + }/*for iChild*/ + + return nRetValue; + + }/*else*/ + + }/*while numEntries*/ + + // No node was found that contains the key value. + // We should never get here... only leaf nodes should return 0 + CPLAssert(FALSE); + return 0; + } + + return 0; // Not found +} + +/********************************************************************** + * TABINDNode::FindNext() + * + * Continue the search previously started by FindFirst() in this node + * and its children for a key value. + * + * Return value: + * - the key's corresponding record number in the .DAT file (greater than 0) + * - 0 if the key was not found + * - or -1 if an error happened + **********************************************************************/ +GInt32 TABINDNode::FindNext(GByte *pKeyValue) +{ + if (m_poDataBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABINDNode::Search(): Node has not been initialized yet!"); + return -1; + } + + /*----------------------------------------------------------------- + * m_nCurIndexEntry is the index of the last item that has been + * returned by FindFirst()/FindNext(). + *----------------------------------------------------------------*/ + + if (m_nSubTreeDepth == 1) + { + /*------------------------------------------------------------- + * Leaf node level... check if the next entry is an exact match + *------------------------------------------------------------*/ + m_nCurIndexEntry++; + if (m_nCurIndexEntry >= m_numEntriesInNode && m_nNextNodePtr > 0) + { + // We're at the end of a node ... continue with next node + GotoNodePtr(m_nNextNodePtr); + m_nCurIndexEntry = 0; + } + + if (m_nCurIndexEntry < m_numEntriesInNode && + IndexKeyCmp(pKeyValue, m_nCurIndexEntry) == 0) + { + /* Found it! Return the record number */ + return ReadIndexEntry(m_nCurIndexEntry, NULL); + } + else + { + /* No more items with that key... return 0 */ + return 0; + } + } + else + { + /*------------------------------------------------------------- + * Index Node: just pass the search to this child node. + *------------------------------------------------------------*/ + while(m_nCurIndexEntry < m_numEntriesInNode) + { + if (m_poCurChildNode != NULL) + return m_poCurChildNode->FindNext(pKeyValue); + } + } + + // No more nodes were found that contain the key value. + return 0; +} + + +/********************************************************************** + * TABINDNode::CommitToFile() + * + * For write access, write current block and its children to file. + * + * note: TABRawBinBlock::CommitToFile() does nothing unless the block has + * been modified. (it has an internal bModified flag) + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABINDNode::CommitToFile() +{ + if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) || + m_poDataBlock == NULL) + return -1; + + if (m_poCurChildNode) + { + if (m_poCurChildNode->CommitToFile() != 0) + return -1; + + m_nSubTreeDepth = m_poCurChildNode->GetSubTreeDepth() + 1; + } + + return m_poDataBlock->CommitToFile(); +} + +/********************************************************************** + * TABINDNode::AddEntry() + * + * Add an .DAT record entry for pKeyValue in this index + * + * nRecordNo is the .DAT record number, record numbers start at 1. + * + * In order to insert a new value, the root node first does a FindFirst() + * that will load the whole tree branch up to the insertion point. + * Then AddEntry() is recursively called up to the leaf node level for + * the insertion of the actual value. + * If the leaf node is full then it will be split and if necessary the + * split will propagate up in the tree through the pointer that each node + * has on its parent. + * + * If bAddInThisNodeOnly=TRUE, then the entry is added only locally and + * we do not try to update the child node. This is used when the parent + * of a node that is being splitted has to be updated. + * + * bInsertAfterCurChild forces the insertion to happen immediately after + * the m_nCurIndexEntry. This works only when bAddInThisNodeOnly=TRUE. + * The default is to search the node for a an insertion point. + * + * Returns 0 on success, -1 on error + **********************************************************************/ +int TABINDNode::AddEntry(GByte *pKeyValue, GInt32 nRecordNo, + GBool bAddInThisNodeOnly /*=FALSE*/, + GBool bInsertAfterCurChild /*=FALSE*/, + GBool bMakeNewEntryCurChild /*=FALSE*/) +{ + if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) || + m_poDataBlock == NULL) + return -1; + + /*----------------------------------------------------------------- + * If I'm the root node, then do a FindFirst() to init all the nodes + * and to make all of them point ot the insertion point. + *----------------------------------------------------------------*/ + if (m_poParentNodeRef == NULL && !bAddInThisNodeOnly) + { + if (FindFirst(pKeyValue) < 0) + return -1; // Error happened and has already been reported. + } + + if (m_poCurChildNode && !bAddInThisNodeOnly) + { + CPLAssert(m_nSubTreeDepth > 1); + /*------------------------------------------------------------- + * Propagate the call down to our children + * Note: this recursive call could result in new levels of nodes + * being added under our feet by SplitRootnode() so it is very + * important to return right after this call or we might not be + * able to recognize this node at the end of the call! + *------------------------------------------------------------*/ + return m_poCurChildNode->AddEntry(pKeyValue, nRecordNo); + } + else + { + /*------------------------------------------------------------- + * OK, we're a leaf node... this is where the real work happens!!! + *------------------------------------------------------------*/ + CPLAssert(m_nSubTreeDepth == 1 || bAddInThisNodeOnly); + + /*------------------------------------------------------------- + * First thing to do is make sure that there is room for a new + * entry in this node, and to split it if necessary. + *------------------------------------------------------------*/ + if (GetNumEntries() == GetMaxNumEntries()) + { + if (m_poParentNodeRef == NULL) + { + /*----------------------------------------------------- + * Splitting the root node adds one level to the tree, so + * after splitting we just redirect the call to our child. + *----------------------------------------------------*/ + if (SplitRootNode() != 0) + return -1; // Error happened and has already been reported + + CPLAssert(m_poCurChildNode); + CPLAssert(m_nSubTreeDepth > 1); + return m_poCurChildNode->AddEntry(pKeyValue, nRecordNo, + bAddInThisNodeOnly, + bInsertAfterCurChild, + bMakeNewEntryCurChild); + } + else + { + /*----------------------------------------------------- + * Splitting a regular node will leave it 50% full. + *----------------------------------------------------*/ + if (SplitNode() != 0) + return -1; + } + } + + /*------------------------------------------------------------- + * Insert new key/value at the right position in node. + *------------------------------------------------------------*/ + if (InsertEntry(pKeyValue, nRecordNo, + bInsertAfterCurChild, bMakeNewEntryCurChild) != 0) + return -1; + } + + return 0; +} + +/********************************************************************** + * TABINDNode::InsertEntry() + * + * (private method) + * + * Insert a key/value pair in the current node buffer. + * + * Returns 0 on success, -1 on error + **********************************************************************/ +int TABINDNode::InsertEntry(GByte *pKeyValue, GInt32 nRecordNo, + GBool bInsertAfterCurChild /*=FALSE*/, + GBool bMakeNewEntryCurChild /*=FALSE*/) +{ + int iInsertAt=0; + + if (GetNumEntries() >= GetMaxNumEntries()) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Node is full! Cannot insert key!"); + return -1; + } + + /*----------------------------------------------------------------- + * Find the spot where the key belongs + *----------------------------------------------------------------*/ + if (bInsertAfterCurChild) + { + iInsertAt = m_nCurIndexEntry+1; + } + else + { + while(iInsertAt < m_numEntriesInNode) + { + int nCmpStatus = IndexKeyCmp(pKeyValue, iInsertAt); + if (nCmpStatus <= 0) + { + break; + } + iInsertAt++; + } + } + + m_poDataBlock->GotoByteInBlock(12 + iInsertAt*(m_nKeyLength+4)); + + /*----------------------------------------------------------------- + * Shift all entries that follow in the array + *----------------------------------------------------------------*/ + if (iInsertAt < m_numEntriesInNode) + { + // Since we use memmove() directly, we need to inform + // m_poDataBlock that the upper limit of the buffer will move + m_poDataBlock->GotoByteInBlock(12 + (m_numEntriesInNode+1)* + (m_nKeyLength+4)); + m_poDataBlock->GotoByteInBlock(12 + iInsertAt*(m_nKeyLength+4)); + + memmove(m_poDataBlock->GetCurDataPtr()+(m_nKeyLength+4), + m_poDataBlock->GetCurDataPtr(), + (m_numEntriesInNode-iInsertAt)*(m_nKeyLength+4)); + + } + + /*----------------------------------------------------------------- + * Write new entry + *----------------------------------------------------------------*/ + m_poDataBlock->WriteBytes(m_nKeyLength, pKeyValue); + m_poDataBlock->WriteInt32(nRecordNo); + + m_numEntriesInNode++; + m_poDataBlock->GotoByteInBlock(0); + m_poDataBlock->WriteInt32(m_numEntriesInNode); + + if (bMakeNewEntryCurChild) + m_nCurIndexEntry = iInsertAt; + else if (m_nCurIndexEntry >= iInsertAt) + m_nCurIndexEntry++; + + /*----------------------------------------------------------------- + * If we replaced the first entry in the node, then this node's key + * changes and we have to update the reference in the parent node. + *----------------------------------------------------------------*/ + if (iInsertAt == 0 && m_poParentNodeRef) + { + if (m_poParentNodeRef->UpdateCurChildEntry(GetNodeKey(), + GetNodeBlockPtr()) != 0) + return -1; + } + + return 0; +} + + +/********************************************************************** + * TABINDNode::UpdateCurChildEntry() + * + * Update the key for the current child node. This method is called by + * the child when its first entry (defining its node key) is changed. + * + * Returns 0 on success, -1 on error + **********************************************************************/ +int TABINDNode::UpdateCurChildEntry(GByte *pKeyValue, GInt32 nRecordNo) +{ + + /*----------------------------------------------------------------- + * Update current child entry with the info for the first node. + * + * For some reason, the key for first entry of the first node of each + * level has to be set to 0 except for the leaf level. + *----------------------------------------------------------------*/ + m_poDataBlock->GotoByteInBlock(12 + m_nCurIndexEntry*(m_nKeyLength+4)); + + if (m_nCurIndexEntry == 0 && m_nSubTreeDepth > 1 && m_nPrevNodePtr == 0) + { + m_poDataBlock->WriteZeros(m_nKeyLength); + } + else + { + m_poDataBlock->WriteBytes(m_nKeyLength, pKeyValue); + } + m_poDataBlock->WriteInt32(nRecordNo); + + return 0; +} + + + +/********************************************************************** + * TABINDNode::UpdateSplitChild() + * + * Update the key and/or record ptr information corresponding to the + * current child node. + * + * Returns 0 on success, -1 on error + **********************************************************************/ +int TABINDNode::UpdateSplitChild(GByte *pKeyValue1, GInt32 nRecordNo1, + GByte *pKeyValue2, GInt32 nRecordNo2, + int nNewCurChildNo /* 1 or 2 */) +{ + + /*----------------------------------------------------------------- + * Update current child entry with the info for the first node. + * + * For some reason, the key for first entry of the first node of each + * level has to be set to 0 except for the leaf level. + *----------------------------------------------------------------*/ + m_poDataBlock->GotoByteInBlock(12 + m_nCurIndexEntry*(m_nKeyLength+4)); + + if (m_nCurIndexEntry == 0 && m_nSubTreeDepth > 1 && m_nPrevNodePtr == 0) + { + m_poDataBlock->WriteZeros(m_nKeyLength); + } + else + { + m_poDataBlock->WriteBytes(m_nKeyLength, pKeyValue1); + } + m_poDataBlock->WriteInt32(nRecordNo1); + + /*----------------------------------------------------------------- + * Add an entry for the second node after the current one and ask + * AddEntry() to update m_nCurIndexEntry if the new node should + * become the new current child. + *----------------------------------------------------------------*/ + if (AddEntry(pKeyValue2, nRecordNo2, + TRUE, /* bInThisNodeOnly */ + TRUE, /* bInsertAfterCurChild */ + (nNewCurChildNo==2)) != 0) + { + return -1; + } + + return 0; +} + + +/********************************************************************** + * TABINDNode::SplitNode() + * + * (private method) + * + * Split a node, update the references in the parent node, etc. + * Note that Root Nodes cannot be split using this method... SplitRootNode() + * should be used instead. + * + * The node is split in a way that the current child stays inside this + * node object, and a new node is created for the other half of the + * entries. This way, the object references in this node's parent and in its + * current child all remain valid. The new node is not kept in memory, + * it is written to disk right away. + * + * Returns 0 on success, -1 on error + **********************************************************************/ +int TABINDNode::SplitNode() +{ + TABINDNode *poNewNode=NULL; + int numInNode1, numInNode2; + + CPLAssert(m_numEntriesInNode >= 2); + CPLAssert(m_poParentNodeRef); // This func. does not work for root nodes + + /*----------------------------------------------------------------- + * Prepare new node + *----------------------------------------------------------------*/ + numInNode1 = (m_numEntriesInNode+1)/2; + numInNode2 = m_numEntriesInNode - numInNode1; + + poNewNode = new TABINDNode(m_eAccessMode); + + if (m_nCurIndexEntry < numInNode1) + { + /*------------------------------------------------------------- + * We will move the second half of the array to a new node. + *------------------------------------------------------------*/ + if (poNewNode->InitNode(m_fp, 0, m_nKeyLength, + m_nSubTreeDepth, m_bUnique, + m_poBlockManagerRef, m_poParentNodeRef, + GetNodeBlockPtr(), m_nNextNodePtr)!= 0 || + poNewNode->SetFieldType(m_eFieldType) != 0 ) + { + return -1; + } + + // We have to update m_nPrevNodePtr in the node that used to follow + // the current node and will now follow the new node. + if (m_nNextNodePtr) + { + TABINDNode *poTmpNode = new TABINDNode(m_eAccessMode); + if (poTmpNode->InitNode(m_fp, m_nNextNodePtr, + m_nKeyLength, m_nSubTreeDepth, + m_bUnique, m_poBlockManagerRef, + m_poParentNodeRef) != 0 || + poTmpNode->SetPrevNodePtr(poNewNode->GetNodeBlockPtr()) != 0 || + poTmpNode->CommitToFile() != 0) + { + return -1; + } + delete poTmpNode; + } + + m_nNextNodePtr = poNewNode->GetNodeBlockPtr(); + + // Move half the entries to the new block + m_poDataBlock->GotoByteInBlock(12 + numInNode1*(m_nKeyLength+4)); + + if (poNewNode->SetNodeBufferDirectly(numInNode2, + m_poDataBlock->GetCurDataPtr()) != 0) + return -1; + +#ifdef DEBUG + // Just in case, reset space previously used by moved entries + memset(m_poDataBlock->GetCurDataPtr(), 0, numInNode2*(m_nKeyLength+4)); +#endif + // And update current node members + m_numEntriesInNode = numInNode1; + + // Update parent node with new children info + if (m_poParentNodeRef) + { + if (m_poParentNodeRef->UpdateSplitChild(GetNodeKey(), + GetNodeBlockPtr(), + poNewNode->GetNodeKey(), + poNewNode->GetNodeBlockPtr(), 1) != 0) + return -1; + } + + } + else + { + /*------------------------------------------------------------- + * We will move the first half of the array to a new node. + *------------------------------------------------------------*/ + if (poNewNode->InitNode(m_fp, 0, m_nKeyLength, + m_nSubTreeDepth, m_bUnique, + m_poBlockManagerRef, m_poParentNodeRef, + m_nPrevNodePtr, GetNodeBlockPtr())!= 0 || + poNewNode->SetFieldType(m_eFieldType) != 0 ) + { + return -1; + } + + // We have to update m_nNextNodePtr in the node that used to precede + // the current node and will now precede the new node. + if (m_nPrevNodePtr) + { + TABINDNode *poTmpNode = new TABINDNode(m_eAccessMode); + if (poTmpNode->InitNode(m_fp, m_nPrevNodePtr, + m_nKeyLength, m_nSubTreeDepth, + m_bUnique, m_poBlockManagerRef, + m_poParentNodeRef) != 0 || + poTmpNode->SetNextNodePtr(poNewNode->GetNodeBlockPtr()) != 0 || + poTmpNode->CommitToFile() != 0) + { + return -1; + } + delete poTmpNode; + } + + m_nPrevNodePtr = poNewNode->GetNodeBlockPtr(); + + // Move half the entries to the new block + m_poDataBlock->GotoByteInBlock(12 + 0); + + if (poNewNode->SetNodeBufferDirectly(numInNode1, + m_poDataBlock->GetCurDataPtr()) != 0) + return -1; + + // Shift the second half of the entries to beginning of buffer + memmove (m_poDataBlock->GetCurDataPtr(), + m_poDataBlock->GetCurDataPtr()+numInNode1*(m_nKeyLength+4), + numInNode2*(m_nKeyLength+4)); + +#ifdef DEBUG + // Just in case, reset space previously used by moved entries + memset(m_poDataBlock->GetCurDataPtr()+numInNode2*(m_nKeyLength+4), + 0, numInNode1*(m_nKeyLength+4)); +#endif + + // And update current node members + m_numEntriesInNode = numInNode2; + m_nCurIndexEntry -= numInNode1; + + // Update parent node with new children info + if (m_poParentNodeRef) + { + if (m_poParentNodeRef->UpdateSplitChild(poNewNode->GetNodeKey(), + poNewNode->GetNodeBlockPtr(), + GetNodeKey(), + GetNodeBlockPtr(), 2) != 0) + return -1; + } + + } + + /*----------------------------------------------------------------- + * Update current node header + *----------------------------------------------------------------*/ + m_poDataBlock->GotoByteInBlock(0); + m_poDataBlock->WriteInt32(m_numEntriesInNode); + m_poDataBlock->WriteInt32(m_nPrevNodePtr); + m_poDataBlock->WriteInt32(m_nNextNodePtr); + + /*----------------------------------------------------------------- + * Flush and destroy temporary node + *----------------------------------------------------------------*/ + if (poNewNode->CommitToFile() != 0) + return -1; + + delete poNewNode; + + return 0; +} + +/********************************************************************** + * TABINDNode::SplitRootNode() + * + * (private method) + * + * Split a Root Node. + * First, a level of nodes must be added to the tree, then the contents + * of what used to be the root node is moved 1 level down and then that + * node is split like a regular node. + * + * Returns 0 on success, -1 on error + **********************************************************************/ +int TABINDNode::SplitRootNode() +{ + /*----------------------------------------------------------------- + * Since a root note cannot be split, we add a level of nodes + * under it and we'll do the split at that level. + *----------------------------------------------------------------*/ + TABINDNode *poNewNode = new TABINDNode(m_eAccessMode); + + if (poNewNode->InitNode(m_fp, 0, m_nKeyLength, + m_nSubTreeDepth, m_bUnique, m_poBlockManagerRef, + this, 0, 0)!= 0 || + poNewNode->SetFieldType(m_eFieldType) != 0) + { + return -1; + } + + // Move all entries to the new child + m_poDataBlock->GotoByteInBlock(12 + 0); + if (poNewNode->SetNodeBufferDirectly(m_numEntriesInNode, + m_poDataBlock->GetCurDataPtr(), + m_nCurIndexEntry, + m_poCurChildNode) != 0) + { + return -1; + } + +#ifdef DEBUG + // Just in case, reset space previously used by moved entries + memset(m_poDataBlock->GetCurDataPtr(), 0, + m_numEntriesInNode*(m_nKeyLength+4)); +#endif + + /*----------------------------------------------------------------- + * Rewrite current node. (the new root node) + *----------------------------------------------------------------*/ + m_numEntriesInNode = 0; + m_nSubTreeDepth++; + + m_poDataBlock->GotoByteInBlock(0); + m_poDataBlock->WriteInt32(m_numEntriesInNode); + + InsertEntry(poNewNode->GetNodeKey(), poNewNode->GetNodeBlockPtr()); + + /*----------------------------------------------------------------- + * Keep a reference to the new child + *----------------------------------------------------------------*/ + m_poCurChildNode = poNewNode; + m_nCurIndexEntry = 0; + + /*----------------------------------------------------------------- + * And finally force the child to split itself + *----------------------------------------------------------------*/ + return m_poCurChildNode->SplitNode(); +} + +/********************************************************************** + * TABINDNode::SetNodeBufferDirectly() + * + * (private method) + * + * Set the key/value part of the nodes buffer and the pointers to the + * current child direclty. This is used when copying info to a new node + * in SplitNode() and SplitRootNode() + * + * Returns 0 on success, -1 on error + **********************************************************************/ +int TABINDNode::SetNodeBufferDirectly(int numEntries, GByte *pBuf, + int nCurIndexEntry/*=0*/, + TABINDNode *poCurChild/*=NULL*/) +{ + m_poDataBlock->GotoByteInBlock(0); + m_poDataBlock->WriteInt32(numEntries); + + m_numEntriesInNode = numEntries; + + m_poDataBlock->GotoByteInBlock(12); + if ( m_poDataBlock->WriteBytes(numEntries*(m_nKeyLength+4), pBuf) != 0) + { + return -1; // An error msg should have been reported already + } + + m_nCurIndexEntry = nCurIndexEntry; + m_poCurChildNode = poCurChild; + if (m_poCurChildNode) + m_poCurChildNode->m_poParentNodeRef = this; + + return 0; +} + +/********************************************************************** + * TABINDNode::GetNodeKey() + * + * Returns a reference to the key for the first entry in the node, which + * is also the key for this node at the level above it in the tree. + * + * Returns NULL if node is empty. + **********************************************************************/ +GByte* TABINDNode::GetNodeKey() +{ + if (m_poDataBlock == NULL || m_numEntriesInNode == 0) + return NULL; + + m_poDataBlock->GotoByteInBlock(12); + + return m_poDataBlock->GetCurDataPtr(); +} + +/********************************************************************** + * TABINDNode::SetPrevNodePtr() + * + * Update the m_nPrevNodePtr member. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABINDNode::SetPrevNodePtr(GInt32 nPrevNodePtr) +{ + if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) || + m_poDataBlock == NULL) + return -1; + + if (m_nPrevNodePtr == nPrevNodePtr) + return 0; // Nothing to do. + + m_poDataBlock->GotoByteInBlock(4); + return m_poDataBlock->WriteInt32(nPrevNodePtr); +} + +/********************************************************************** + * TABINDNode::SetNextNodePtr() + * + * Update the m_nNextNodePtr member. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABINDNode::SetNextNodePtr(GInt32 nNextNodePtr) +{ + if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) || + m_poDataBlock == NULL) + return -1; + + if (m_nNextNodePtr == nNextNodePtr) + return 0; // Nothing to do. + + m_poDataBlock->GotoByteInBlock(8); + return m_poDataBlock->WriteInt32(nNextNodePtr); +} + + + +/********************************************************************** + * TABINDNode::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABINDNode::Dump(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABINDNode::Dump() -----\n"); + + if (m_fp == NULL) + { + fprintf(fpOut, "Node is not initialized.\n"); + } + else + { + fprintf(fpOut, " m_numEntriesInNode = %d\n", m_numEntriesInNode); + fprintf(fpOut, " m_nCurDataBlockPtr = %d\n", m_nCurDataBlockPtr); + fprintf(fpOut, " m_nPrevNodePtr = %d\n", m_nPrevNodePtr); + fprintf(fpOut, " m_nNextNodePtr = %d\n", m_nNextNodePtr); + fprintf(fpOut, " m_nSubTreeDepth = %d\n", m_nSubTreeDepth); + fprintf(fpOut, " m_nKeyLength = %d\n", m_nKeyLength); + fprintf(fpOut, " m_eFieldtype = %s\n", + TABFIELDTYPE_2_STRING(m_eFieldType) ); + if (m_nSubTreeDepth > 0) + { + GByte aKeyValBuf[255]; + GInt32 nRecordPtr, nValue; + TABINDNode oChildNode; + + if (m_nKeyLength > 254) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Dump() cannot handle keys longer than 254 chars."); + return; + } + + fprintf(fpOut, "\n"); + for (int i=0; i 1) + { + fprintf(fpOut, " >>>> Child %d of %d <<<<<\n", i, + m_numEntriesInNode); + } + else + { + fprintf(fpOut, " >>>> Record (leaf) %d of %d <<<<<\n", i, + m_numEntriesInNode); + } + + if (m_eFieldType == TABFChar) + { + nRecordPtr = ReadIndexEntry(i, aKeyValBuf); + fprintf(fpOut, " nRecordPtr = %d\n", nRecordPtr); + fprintf(fpOut, " Char Val= \"%s\"\n", (char*)aKeyValBuf); + } + else if (m_nKeyLength != 4) + { + GInt32 nInt32; + GInt16 nInt16; + GUInt32 nUInt32; + memcpy(&nInt32, aKeyValBuf, 4); + memcpy(&nInt16, aKeyValBuf + 2, 2); + memcpy(&nUInt32, aKeyValBuf, 4); + nRecordPtr = ReadIndexEntry(i, aKeyValBuf); + fprintf(fpOut, " nRecordPtr = %d\n", nRecordPtr); + fprintf(fpOut, " Int Value = %d\n", nInt32); + fprintf(fpOut, " Int16 Val= %d\n",nInt16); + fprintf(fpOut, " Hex Val= 0x%8.8x\n",nUInt32); + } + else + { + nRecordPtr = ReadIndexEntry(i, (GByte*)&nValue); + fprintf(fpOut, " nRecordPtr = %d\n", nRecordPtr); + fprintf(fpOut, " Int Value = %d\n", nValue); + fprintf(fpOut, " Hex Value = 0x%8.8x\n",nValue); + } + + if (m_nSubTreeDepth > 1) + { + oChildNode.InitNode(m_fp, nRecordPtr, m_nKeyLength, + m_nSubTreeDepth - 1, FALSE); + oChildNode.SetFieldType(m_eFieldType); + oChildNode.Dump(fpOut); + } + } + } + } + + fflush(fpOut); +} + +#endif // DEBUG diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapcoordblock.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapcoordblock.cpp new file mode 100644 index 000000000..b4a36e85c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapcoordblock.cpp @@ -0,0 +1,920 @@ +/********************************************************************** + * $Id: mitab_mapcoordblock.cpp,v 1.18 2010-07-07 19:00:15 aboudreault Exp $ + * + * Name: mitab_mapcoordblock.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABMAPCoordBlock class used to handle + * reading/writing of the .MAP files' coordinate blocks + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2001, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_mapcoordblock.cpp,v $ + * Revision 1.18 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.17 2008-02-01 19:36:31 dmorissette + * Initial support for V800 REGION and MULTIPLINE (bug 1496) + * + * Revision 1.16 2007/02/23 18:56:44 dmorissette + * Fixed another problem writing collections when the header of objects + * part of a collection were split on multiple blocks. Fix WriteBytes() + * to reload next coord block in TABReadWrite mode if there is one (bug 1663) + * + * Revision 1.15 2006/11/28 18:49:08 dmorissette + * Completed changes to split TABMAPObjectBlocks properly and produce an + * optimal spatial index (bug 1585) + * + * Revision 1.14 2005/10/06 19:15:31 dmorissette + * Collections: added support for reading/writing pen/brush/symbol ids and + * for writing collection objects to .TAB/.MAP (bug 1126) + * + * Revision 1.13 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.12 2002/08/27 17:18:23 warmerda + * improved CPL error testing + * + * Revision 1.11 2001/11/17 21:54:06 daniel + * Made several changes in order to support writing objects in 16 bits coordinate format. + * New TABMAPObjHdr-derived classes are used to hold object info in mem until block is full. + * + * Revision 1.10 2001/05/09 17:45:12 daniel + * Support reading and writing data blocks > 512 bytes (for text objects). + * + * Revision 1.9 2000/10/10 19:05:12 daniel + * Fixed ReadBytes() to allow strings overlapping on 2 blocks + * + * Revision 1.8 2000/02/28 16:58:55 daniel + * Added V450 object types with num_points > 32767 and pen width in points + * + * Revision 1.7 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.6 1999/11/08 04:29:31 daniel + * Fixed problem with compressed coord. offset for regions and multiplines + * + * Revision 1.5 1999/10/06 15:19:11 daniel + * Do not automatically init. curr. feature MBR when block is initialized + * + * Revision 1.4 1999/10/06 13:18:55 daniel + * Fixed uninitialized class members + * + * Revision 1.3 1999/09/29 04:25:42 daniel + * Fixed typo in GetFeatureMBR() + * + * Revision 1.2 1999/09/26 14:59:36 daniel + * Implemented write support + * + * Revision 1.1 1999/07/12 04:18:24 daniel + * Initial checkin + * + **********************************************************************/ + +#include "mitab.h" + +/*===================================================================== + * class TABMAPCoordBlock + *====================================================================*/ + +#define MAP_COORD_HEADER_SIZE 8 + +/********************************************************************** + * TABMAPCoordBlock::TABMAPCoordBlock() + * + * Constructor. + **********************************************************************/ +TABMAPCoordBlock::TABMAPCoordBlock(TABAccess eAccessMode /*= TABRead*/): + TABRawBinBlock(eAccessMode, TRUE) +{ + m_nComprOrgX = m_nComprOrgY = m_nNextCoordBlock = m_numDataBytes = 0; + + m_numBlocksInChain = 1; // Current block counts as 1 + + m_poBlockManagerRef = NULL; + + m_nTotalDataSize = 0; + m_nFeatureDataSize = 0; + + m_nFeatureXMin = m_nMinX = 1000000000; + m_nFeatureYMin = m_nMinY = 1000000000; + m_nFeatureXMax = m_nMaxX = -1000000000; + m_nFeatureYMax = m_nMaxY = -1000000000; + +} + +/********************************************************************** + * TABMAPCoordBlock::~TABMAPCoordBlock() + * + * Destructor. + **********************************************************************/ +TABMAPCoordBlock::~TABMAPCoordBlock() +{ + +} + + +/********************************************************************** + * TABMAPCoordBlock::InitBlockFromData() + * + * Perform some initialization on the block after its binary data has + * been set or changed (or loaded from a file). + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPCoordBlock::InitBlockFromData(GByte *pabyBuf, + int nBlockSize, int nSizeUsed, + GBool bMakeCopy /* = TRUE */, + VSILFILE *fpSrc /* = NULL */, + int nOffset /* = 0 */) +{ + int nStatus; +#ifdef DEBUG_VERBOSE + CPLDebug("MITAB", "Instanciating COORD block to/from offset %d", nOffset); +#endif + /*----------------------------------------------------------------- + * First of all, we must call the base class' InitBlockFromData() + *----------------------------------------------------------------*/ + nStatus = TABRawBinBlock::InitBlockFromData(pabyBuf, nBlockSize, nSizeUsed, + bMakeCopy, fpSrc, nOffset); + if (nStatus != 0) + return nStatus; + + /*----------------------------------------------------------------- + * Validate block type + *----------------------------------------------------------------*/ + if (m_nBlockType != TABMAP_COORD_BLOCK) + { + CPLError(CE_Failure, CPLE_FileIO, + "InitBlockFromData(): Invalid Block Type: got %d expected %d", + m_nBlockType, TABMAP_COORD_BLOCK); + CPLFree(m_pabyBuf); + m_pabyBuf = NULL; + return -1; + } + + /*----------------------------------------------------------------- + * Init member variables + *----------------------------------------------------------------*/ + GotoByteInBlock(0x002); + m_numDataBytes = ReadInt16(); /* Excluding 8 bytes header */ + + m_nNextCoordBlock = ReadInt32(); + + // Set the real SizeUsed based on numDataBytes + m_nSizeUsed = m_numDataBytes + MAP_COORD_HEADER_SIZE; + + /*----------------------------------------------------------------- + * The read ptr is now located at the beginning of the data part. + *----------------------------------------------------------------*/ + GotoByteInBlock(MAP_COORD_HEADER_SIZE); + + return 0; +} + +/********************************************************************** + * TABMAPCoordBlock::CommitToFile() + * + * Commit the current state of the binary block to the file to which + * it has been previously attached. + * + * This method makes sure all values are properly set in the map object + * block header and then calls TABRawBinBlock::CommitToFile() to do + * the actual writing to disk. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPCoordBlock::CommitToFile() +{ + int nStatus = 0; + + CPLErrorReset(); + + if ( m_pabyBuf == NULL ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "CommitToFile(): Block has not been initialized yet!"); + return -1; + } + + /*----------------------------------------------------------------- + * Nothing to do here if block has not been modified + *----------------------------------------------------------------*/ + if (!m_bModified) + return 0; + + /*----------------------------------------------------------------- + * Make sure 8 bytes block header is up to date. + *----------------------------------------------------------------*/ + GotoByteInBlock(0x000); + + WriteInt16(TABMAP_COORD_BLOCK); // Block type code + WriteInt16((GInt16)(m_nSizeUsed - MAP_COORD_HEADER_SIZE)); // num. bytes used + WriteInt32(m_nNextCoordBlock); + + if( CPLGetLastErrorType() == CE_Failure ) + nStatus = CPLGetLastErrorNo(); + + /*----------------------------------------------------------------- + * OK, call the base class to write the block to disk. + *----------------------------------------------------------------*/ + if (nStatus == 0) + { +#ifdef DEBUG_VERBOSE + CPLDebug("MITAB", "Commiting COORD block to offset %d", m_nFileOffset); +#endif + nStatus = TABRawBinBlock::CommitToFile(); + } + + return nStatus; +} + +/********************************************************************** + * TABMAPCoordBlock::InitNewBlock() + * + * Initialize a newly created block so that it knows to which file it + * is attached, its block size, etc . and then perform any specific + * initialization for this block type, including writing a default + * block header, etc. and leave the block ready to receive data. + * + * This is an alternative to calling ReadFromFile() or InitBlockFromData() + * that puts the block in a stable state without loading any initial + * data in it. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPCoordBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize, + int nFileOffset /* = 0*/) +{ + CPLErrorReset(); +#ifdef DEBUG_VERBOSE + CPLDebug("MITAB", "Instanciating new COORD block at offset %d", nFileOffset); +#endif + /*----------------------------------------------------------------- + * Start with the default initialisation + *----------------------------------------------------------------*/ + if ( TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0) + return -1; + + /*----------------------------------------------------------------- + * And then set default values for the block header. + * + * IMPORTANT: Do not reset m_nComprOrg here because its value needs to be + * maintained between blocks in the same chain. + *----------------------------------------------------------------*/ + m_nNextCoordBlock = 0; + + m_numDataBytes = 0; + + // m_nMin/Max are used to keep track of current block MBR + // FeatureMin/Max should not be reset here since feature coords can + // be split on several blocks + m_nMinX = 1000000000; + m_nMinY = 1000000000; + m_nMaxX = -1000000000; + m_nMaxY = -1000000000; + + if (m_eAccess != TABRead && nFileOffset != 0) + { + GotoByteInBlock(0x000); + + WriteInt16(TABMAP_COORD_BLOCK); // Block type code + WriteInt16(0); // num. bytes used, excluding header + WriteInt32(0); // Pointer to next coord block + } + + if (CPLGetLastErrorType() == CE_Failure ) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPObjectBlock::SetNextCoordBlock() + * + * Set the address (offset from beginning of file) of the coord. block + * that follows the current one. + **********************************************************************/ +void TABMAPCoordBlock::SetNextCoordBlock(GInt32 nNextCoordBlockAddress) +{ + m_nNextCoordBlock = nNextCoordBlockAddress; + m_bModified = TRUE; +} + + +/********************************************************************** + * TABMAPObjectBlock::SetComprCoordOrigin() + * + * Set the Compressed integer coordinates space origin to be used when + * reading compressed coordinates using ReadIntCoord(). + **********************************************************************/ +void TABMAPCoordBlock::SetComprCoordOrigin(GInt32 nX, GInt32 nY) +{ + m_nComprOrgX = nX; + m_nComprOrgY = nY; +} + +/********************************************************************** + * TABMAPObjectBlock::ReadIntCoord() + * + * Read the next pair of integer coordinates value from the block, and + * apply the translation relative to the origin of the coord. space + * previously set using SetComprCoordOrigin() if bCompressed=TRUE. + * + * This means that the returned coordinates are always absolute integer + * coordinates, even when the source coords are in compressed form. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPCoordBlock::ReadIntCoord(GBool bCompressed, + GInt32 &nX, GInt32 &nY) +{ + if (bCompressed) + { + nX = m_nComprOrgX + ReadInt16(); + nY = m_nComprOrgY + ReadInt16(); + } + else + { + nX = ReadInt32(); + nY = ReadInt32(); + } + + if (CPLGetLastErrorType() == CE_Failure) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPObjectBlock::ReadIntCoords() + * + * Read the specified number of pairs of X,Y integer coordinates values + * from the block, and apply the translation relative to the origin of + * the coord. space previously set using SetComprCoordOrigin() if + * bCompressed=TRUE. + * + * This means that the returned coordinates are always absolute integer + * coordinates, even when the source coords are in compressed form. + * + * panXY should point to an array big enough to receive the specified + * number of coordinates. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPCoordBlock::ReadIntCoords(GBool bCompressed, int numCoordPairs, + GInt32 *panXY) +{ + int i, numValues = numCoordPairs*2; + + if (bCompressed) + { + for(i=0; i= 450) + nTotalHdrSizeUncompressed = 28 * numSections; + else + nTotalHdrSizeUncompressed = 24 * numSections; + + numVerticesTotal = 0; + + for(i=0; i= 450) + pasHdrs[i].numVertices = ReadInt32(); + else + pasHdrs[i].numVertices = ReadInt16(); + if (nVersion >= 800) + pasHdrs[i].numHoles = ReadInt32(); + else + pasHdrs[i].numHoles = ReadInt16(); + ReadIntCoord(bCompressed, pasHdrs[i].nXMin, pasHdrs[i].nYMin); + ReadIntCoord(bCompressed, pasHdrs[i].nXMax, pasHdrs[i].nYMax); + pasHdrs[i].nDataOffset = ReadInt32(); + + if (CPLGetLastErrorType() != 0) + return -1; + + numVerticesTotal += pasHdrs[i].numVertices; + + + pasHdrs[i].nVertexOffset = (pasHdrs[i].nDataOffset - + nTotalHdrSizeUncompressed ) / 8; +#ifdef TABDUMP + printf("READING pasHdrs[%d] @ %d = \n" + " { numVertices = %d, numHoles = %d, \n" + " nXMin=%d, nYMin=%d, nXMax=%d, nYMax=%d,\n" + " nDataOffset=%d, nVertexOffset=%d }\n", + i, nHdrAddress, pasHdrs[i].numVertices, pasHdrs[i].numHoles, + pasHdrs[i].nXMin, pasHdrs[i].nYMin, pasHdrs[i].nXMax, + pasHdrs[i].nYMax, pasHdrs[i].nDataOffset, + pasHdrs[i].nVertexOffset); + printf(" dX = %d, dY = %d (center = %d , %d)\n", + pasHdrs[i].nXMax - pasHdrs[i].nXMin, + pasHdrs[i].nYMax - pasHdrs[i].nYMin, + m_nComprOrgX, m_nComprOrgY); +#endif + } + + for(i=0; i numVerticesTotal) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Unsupported case or corrupt file: MULTIPLINE/REGION " + "object vertices do not appear to be grouped together."); + return -1; + } + } + + return 0; +} + +/********************************************************************** + * TABMAPObjectBlock::WriteCoordSecHdrs() + * + * Write a set of coordinate section headers for PLINE MULTIPLE or REGIONs. + * pasHdrs should point to an array of numSections TABMAPCoordSecHdr + * structures that have been properly initialized. + * + * In V450 the numVertices is stored on an int32 instead of an int16 + * + * In V800 the numHoles is stored on an int32 instead of an int16 + * + * At the end of the call, this TABMAPCoordBlock object will be ready to + * receive the coordinate data. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPCoordBlock::WriteCoordSecHdrs(int nVersion, + int numSections, + TABMAPCoordSecHdr *pasHdrs, + GBool bCompressed /*=FALSE*/) +{ + int i; + + CPLErrorReset(); + + for(i=0; i= 450) + WriteInt32(pasHdrs[i].numVertices); + else + WriteInt16((GInt16)pasHdrs[i].numVertices); + if (nVersion >= 800) + WriteInt32(pasHdrs[i].numHoles); + else + WriteInt16((GInt16)pasHdrs[i].numHoles); + WriteIntCoord(pasHdrs[i].nXMin, pasHdrs[i].nYMin, bCompressed); + WriteIntCoord(pasHdrs[i].nXMax, pasHdrs[i].nYMax, bCompressed); + WriteInt32(pasHdrs[i].nDataOffset); + + if (CPLGetLastErrorType() == CE_Failure ) + return -1; + } + + return 0; +} + + +/********************************************************************** + * TABMAPCoordBlock::WriteIntCoord() + * + * Write a pair of integer coordinates values to the current position in the + * the block. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ + +int TABMAPCoordBlock::WriteIntCoord(GInt32 nX, GInt32 nY, + GBool bCompressed /*=FALSE*/) +{ + + if ((!bCompressed && (WriteInt32(nX) != 0 || WriteInt32(nY) != 0 ) ) || + (bCompressed && (WriteInt16((GInt16)(nX - m_nComprOrgX)) != 0 || + WriteInt16((GInt16)(nY - m_nComprOrgY)) != 0) ) ) + { + return -1; + } + + /*----------------------------------------------------------------- + * Update block MBR + *----------------------------------------------------------------*/ + //__TODO__ Do we still need to track the block MBR??? + if (nX < m_nMinX) + m_nMinX = nX; + if (nX > m_nMaxX) + m_nMaxX = nX; + + if (nY < m_nMinY) + m_nMinY = nY; + if (nY > m_nMaxY) + m_nMaxY = nY; + + /*------------------------------------------------------------- + * Also keep track of current feature MBR. + *------------------------------------------------------------*/ + if (nX < m_nFeatureXMin) + m_nFeatureXMin = nX; + if (nX > m_nFeatureXMax) + m_nFeatureXMax = nX; + + if (nY < m_nFeatureYMin) + m_nFeatureYMin = nY; + if (nY > m_nFeatureYMax) + m_nFeatureYMax = nY; + + return 0; +} + +/********************************************************************** + * TABMAPCoordBlock::SetMAPBlockManagerRef() + * + * Pass a reference to the block manager object for the file this + * block belongs to. The block manager will be used by this object + * when it needs to automatically allocate a new block. + **********************************************************************/ +void TABMAPCoordBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr) +{ + m_poBlockManagerRef = poBlockMgr; +}; + + +/********************************************************************** + * TABMAPCoordBlock::ReadBytes() + * + * Cover function for TABRawBinBlock::ReadBytes() that will automagically + * load the next coordinate block in the chain before reading the + * requested bytes if we are at the end of the current block and if + * m_nNextCoordBlock is a valid block. + * + * Then the control is passed to TABRawBinBlock::ReadBytes() to finish the + * work: + * Copy the number of bytes from the data block's internal buffer to + * the user's buffer pointed by pabyDstBuf. + * + * Passing pabyDstBuf = NULL will only move the read pointer by the + * specified number of bytes as if the copy had happened... but it + * won't crash. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPCoordBlock::ReadBytes(int numBytes, GByte *pabyDstBuf) +{ + int nStatus; + + if (m_pabyBuf && + m_nCurPos >= (m_numDataBytes+MAP_COORD_HEADER_SIZE) && + m_nNextCoordBlock > 0) + { + // We're at end of current block... advance to next block. + + if ( (nStatus=GotoByteInFile(m_nNextCoordBlock, TRUE)) != 0) + { + // Failed.... an error has already been reported. + return nStatus; + } + + GotoByteInBlock(MAP_COORD_HEADER_SIZE); // Move pointer past header + m_numBlocksInChain++; + } + + if (m_pabyBuf && + m_nCurPos < (m_numDataBytes+MAP_COORD_HEADER_SIZE) && + m_nCurPos+numBytes > (m_numDataBytes+MAP_COORD_HEADER_SIZE) && + m_nNextCoordBlock > 0) + { + // Data overlaps on more than one block + // Read until end of this block and then recursively call ReadBytes() + // for the rest. + int numBytesInThisBlock = + (m_numDataBytes+MAP_COORD_HEADER_SIZE)-m_nCurPos; + nStatus = TABRawBinBlock::ReadBytes(numBytesInThisBlock, pabyDstBuf); + if (nStatus == 0) + nStatus = TABMAPCoordBlock::ReadBytes(numBytes-numBytesInThisBlock, + pabyDstBuf+numBytesInThisBlock); + return nStatus; + } + + + return TABRawBinBlock::ReadBytes(numBytes, pabyDstBuf); +} + + +/********************************************************************** + * TABMAPCoordBlock::WriteBytes() + * + * Cover function for TABRawBinBlock::WriteBytes() that will automagically + * CommitToFile() the current block and create a new one if we are at + * the end of the current block. + * + * Then the control is passed to TABRawBinBlock::WriteBytes() to finish the + * work. + * + * Passing pabySrcBuf = NULL will only move the write pointer by the + * specified number of bytes as if the copy had happened... but it + * won't crash. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPCoordBlock::WriteBytes(int nBytesToWrite, GByte *pabySrcBuf) +{ + if (m_eAccess != TABWrite && m_eAccess != TABReadWrite ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "WriteBytes(): Block does not support write operations."); + return -1; + } + + if (m_poBlockManagerRef && (m_nBlockSize - m_nCurPos) < nBytesToWrite) + { + if (nBytesToWrite <= (m_nBlockSize-MAP_COORD_HEADER_SIZE)) + { + // Data won't fit in this block but can fit inside a single + // block, so we'll allocate a new block for it. This will + // prevent us from overlapping coordinate values on 2 blocks, but + // still allows strings longer than one block (see 'else' below). + // + + if ( m_nNextCoordBlock != 0 ) + { + // We're in read/write mode and there is already an allocated + // block following this one in the chain ... just reload it + // and continue writing to it + + CPLAssert( m_eAccess == TABReadWrite ); + + if (CommitToFile() != 0 || + ReadFromFile(m_fp, m_nNextCoordBlock, m_nBlockSize) != 0) + { + // An error message should have already been reported. + return -1; + } + } + else + { + // Need to alloc a new block. + + int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock("COORD"); + SetNextCoordBlock(nNewBlockOffset); + + if (CommitToFile() != 0 || + InitNewBlock(m_fp, m_nBlockSize, nNewBlockOffset) != 0) + { + // An error message should have already been reported. + return -1; + } + + m_numBlocksInChain++; + } + } + else + { + // Data to write is longer than one block... so we'll have to + // split it over multiple block through multiple calls. + // + int nStatus = 0; + while(nStatus == 0 && nBytesToWrite > 0) + { + int nBytes = m_nBlockSize-MAP_COORD_HEADER_SIZE; + if ( (m_nBlockSize - m_nCurPos) > 0 ) + { + // Use free room in current block + nBytes = (m_nBlockSize - m_nCurPos); + } + + nBytes = MIN(nBytes, nBytesToWrite); + + // The following call will result in a new block being + // allocated in the if() block above. + nStatus = TABMAPCoordBlock::WriteBytes(nBytes, + pabySrcBuf); + + nBytesToWrite -= nBytes; + pabySrcBuf += nBytes; + } + return nStatus; + } + } + + if (m_nCurPos >= MAP_COORD_HEADER_SIZE) + { + // Keep track of Coordinate data... this means ignore header bytes + // that could be written. + m_nTotalDataSize += nBytesToWrite; + m_nFeatureDataSize += nBytesToWrite; + } + + return TABRawBinBlock::WriteBytes(nBytesToWrite, pabySrcBuf); +} + +/********************************************************************** + * TABMAPObjectBlock::SeekEnd() + * + * Move read/write pointer to end of used part of the block + **********************************************************************/ +void TABMAPCoordBlock::SeekEnd() +{ + m_nCurPos = m_nSizeUsed; +} + +/********************************************************************** + * TABMAPCoordBlock::StartNewFeature() + * + * Reset all member vars that are used to keep track of data size + * and MBR for the current feature. This is info is not needed by + * the coord blocks themselves, but it helps a lot the callers to + * have this class take care of that for them. + * + * See Also: GetFeatureDataSize() and GetFeatureMBR() + **********************************************************************/ +void TABMAPCoordBlock::StartNewFeature() +{ + m_nFeatureDataSize = 0; + + m_nFeatureXMin = 1000000000; + m_nFeatureYMin = 1000000000; + m_nFeatureXMax = -1000000000; + m_nFeatureYMax = -1000000000; +} + +/********************************************************************** + * TABMAPCoordBlock::GetFeatureMBR() + * + * Return the MBR of all the coords written using WriteIntCoord() since + * the last call to StartNewFeature(). + **********************************************************************/ +void TABMAPCoordBlock::GetFeatureMBR(GInt32 &nXMin, GInt32 &nYMin, + GInt32 &nXMax, GInt32 &nYMax) +{ + nXMin = m_nFeatureXMin; + nYMin = m_nFeatureYMin; + nXMax = m_nFeatureXMax; + nYMax = m_nFeatureYMax; +} + + +/********************************************************************** + * TABMAPCoordBlock::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABMAPCoordBlock::Dump(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABMAPCoordBlock::Dump() -----\n"); + if (m_pabyBuf == NULL) + { + fprintf(fpOut, "Block has not been initialized yet."); + } + else + { + fprintf(fpOut,"Coordinate Block (type %d) at offset %d.\n", + m_nBlockType, m_nFileOffset); + fprintf(fpOut," m_numDataBytes = %d\n", m_numDataBytes); + fprintf(fpOut," m_nNextCoordBlock = %d\n", m_nNextCoordBlock); + } + + fflush(fpOut); +} + +#endif // DEBUG + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapfile.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapfile.cpp new file mode 100644 index 000000000..958aa0ad4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapfile.cpp @@ -0,0 +1,3271 @@ +/********************************************************************** + * $Id: mitab_mapfile.cpp,v 1.46 2010-07-07 19:00:15 aboudreault Exp $ + * + * Name: mitab_mapfile.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABMAPFile class used to handle + * reading/writing of the .MAP files at the MapInfo object level + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2002, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_mapfile.cpp,v $ + * Revision 1.46 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.45 2010-01-08 22:02:51 aboudreault + * Fixed error issued when reading empty TAB with spatial index active (bug 2136) + * + * Revision 1.44 2009-03-03 20:44:23 dmorissette + * Use transparent brush in DumpSpatialIndexToMIF() + * + * Revision 1.43 2008/02/20 21:35:30 dmorissette + * Added support for V800 COLLECTION of large objects (bug 1496) + * + * Revision 1.42 2008/02/01 19:36:31 dmorissette + * Initial support for V800 REGION and MULTIPLINE (bug 1496) + * + * Revision 1.41 2007/11/08 18:57:56 dmorissette + * Upgrade of OGR and CPL libs to the version from GDAL/OGR 1.4.3 + * + * Revision 1.40 2007/09/14 18:30:19 dmorissette + * Fixed the splitting of object blocks with the optimized spatial + * index mode that was producing files with misaligned bytes that + * confused MapInfo (bug 1732) + * + * Revision 1.39 2007/07/11 15:51:52 dmorissette + * Fixed duplicate 'int i' definition build errors in SplitObjBlock() + * + * Revision 1.38 2007/06/12 12:50:39 dmorissette + * Use Quick Spatial Index by default until bug 1732 is fixed (broken files + * produced by current coord block splitting technique). + * + * Revision 1.37 2007/06/05 13:23:57 dmorissette + * Fixed memory leak when writing .TAB with new (optimized) spatial index + * introduced in v1.6.0 (bug 1725) + * + * Revision 1.36 2007/03/21 21:15:56 dmorissette + * Added SetQuickSpatialIndexMode() which generates a non-optimal spatial + * index but results in faster write time (bug 1669) + * + * Revision 1.35 2006/11/28 18:49:08 dmorissette + * Completed changes to split TABMAPObjectBlocks properly and produce an + * optimal spatial index (bug 1585) + * + * Revision 1.34 2006/11/20 20:05:58 dmorissette + * First pass at improving generation of spatial index in .map file (bug 1585) + * New methods for insertion and splittung in the spatial index are done. + * Also implemented a method to dump the spatial index to .mif/.mid + * Still need to implement splitting of TABMapObjectBlock to get optimal + * results. + * + * Revision 1.33 2006/09/05 23:05:08 dmorissette + * Added TABMAPFile::DumpSpatialIndex() (bug 1585) + * + * Revision 1.32 2005/10/06 19:15:31 dmorissette + * Collections: added support for reading/writing pen/brush/symbol ids and + * for writing collection objects to .TAB/.MAP (bug 1126) + * + * Revision 1.31 2004/09/22 13:07:58 fwarmerdam + * fixed return value in LoadNextMatchingObjectBlock() per rso bug 615 + * + * Revision 1.30 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.29 2003/08/12 23:17:21 dmorissette + * Added reading of v500+ coordsys affine params (Anthony D. - Encom) + * + * Revision 1.28 2002/08/27 17:18:43 warmerda + * improved CPL error testing + * + * Revision 1.27 2002/07/30 13:54:12 julien + * TABMAPFile::GetFeatureId() now return -1 when there's no geometry. (bug 169) + * + * Revision 1.26 2002/04/25 16:05:24 julien + * Disabled the overflow warning in SetCoordFilter() by adding bIgnoreOverflow + * variable in Coordsys2Int of the TABMAPFile class and TABMAPHeaderBlock class + * + * Revision 1.25 2002/03/26 19:27:43 daniel + * Got rid of tabs in source + * + * Revision 1.24 2002/03/26 01:48:40 daniel + * Added Multipoint object type (V650) + * + * Revision 1.23 2002/02/20 13:53:40 daniel + * Prevent an infinite loop of calls to LoadNextMatchingObjectBlock() in + * GetNextFeatureId() if no objects found in spatial index. + * + * Revision 1.22 2001/11/19 15:04:41 daniel + * Prevent writing of coordinates outside of the +/-1e9 integer bounds. + * + * Revision 1.21 2001/11/17 21:54:06 daniel + * Made several changes in order to support writing objects in 16 bits + * coordinate format. New TABMAPObjHdr-derived classes are used to hold + * object info in mem until block is full. + * + * Revision 1.20 2001/09/18 20:33:52 warmerda + * fixed case of spatial search on file with just one object block + * + * Revision 1.19 2001/09/14 03:23:55 warmerda + * Substantial upgrade to support spatial queries using spatial indexes + * + * Revision 1.18 2001/03/15 03:57:51 daniel + * Added implementation for new OGRLayer::GetExtent(), returning data MBR. + * + * Revision 1.17 2000/11/23 21:11:07 daniel + * OOpps... VC++ didn't like the way TABPenDef, etc. were initialized + * + * Revision 1.16 2000/11/23 20:47:46 daniel + * Use MI defaults for Pen, Brush, Font, Symbol instead of all zeros + * + * Revision 1.15 2000/11/22 04:03:10 daniel + * Added warning when objects written outside of the +/-1e9 int. coord. range + * + * Revision 1.14 2000/11/15 04:13:49 daniel + * Fixed writing of TABMAPToolBlock to allocate a new block when full + * + * Revision 1.13 2000/05/19 06:44:55 daniel + * Modified generation of spatial index to split index nodes and produce a + * more balanced tree. + * + * Revision 1.12 2000/03/13 05:58:01 daniel + * Create 1024 bytes V500 .MAP header + limit m_nMaxCoordBufSize for V450 obj. + * + * Revision 1.11 2000/02/28 17:00:00 daniel + * Added V450 object types + * + * Revision 1.10 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.9 1999/12/19 17:37:52 daniel + * Fixed memory leaks + * + * Revision 1.8 1999/11/14 04:43:31 daniel + * Support dataset with no .MAP/.ID files + * + * Revision 1.7 1999/10/19 22:57:17 daniel + * Create m_poCurObjBlock only when needed to avoid empty blocks in files + * and problems with MBR in header block of files with only "NONE" geometries + * + * Revision 1.6 1999/10/06 13:17:46 daniel + * Update m_nMaxCoordBufSize in header block + * + * Revision 1.5 1999/10/01 03:52:22 daniel + * Avoid producing an unused block in the file when closing it. + * + * Revision 1.4 1999/09/26 14:59:36 daniel + * Implemented write support + * + * Revision 1.3 1999/09/20 18:42:42 daniel + * Use binary access to open file. + * + * Revision 1.2 1999/09/16 02:39:16 daniel + * Completed read support for most feature types + * + * Revision 1.1 1999/07/12 04:18:24 daniel + * Initial checkin + * + **********************************************************************/ + +#include "mitab.h" + +/*===================================================================== + * class TABMAPFile + *====================================================================*/ + + +/********************************************************************** + * TABMAPFile::TABMAPFile() + * + * Constructor. + **********************************************************************/ +TABMAPFile::TABMAPFile() +{ + m_nMinTABVersion = 300; + m_fp = NULL; + m_pszFname = NULL; + m_poHeader = NULL; + m_poSpIndex = NULL; + m_poSpIndexLeaf = NULL; +/* See bug 1732: Optimized spatial index produces broken files because + * of the way CoordBlocks are split. For now we have to force using the + * Quick (old) spatial index mode by default until bug 1732 is fixed. + */ + m_bQuickSpatialIndexMode = TRUE; +// m_bQuickSpatialIndexMode = FALSE; + + m_poCurObjBlock = NULL; + m_nCurObjPtr = -1; + m_nCurObjType = TAB_GEOM_UNSET; + m_nCurObjId = -1; + m_poCurCoordBlock = NULL; + m_poToolDefTable = NULL; + + m_bUpdated = FALSE; + m_bLastOpWasRead = FALSE; + m_bLastOpWasWrite = FALSE; + + m_oBlockManager.SetName("MAP"); +} + +/********************************************************************** + * TABMAPFile::~TABMAPFile() + * + * Destructor. + **********************************************************************/ +TABMAPFile::~TABMAPFile() +{ + Close(); +} + +/********************************************************************** + * TABMAPFile::Open() + * + * Compatibility layer with new interface. + * Return 0 on success, -1 in case of failure. + **********************************************************************/ + +int TABMAPFile::Open(const char *pszFname, const char* pszAccess, GBool bNoErrorMsg) +{ + if( EQUALN(pszAccess, "r", 1) ) + return Open(pszFname, TABRead, bNoErrorMsg); + else if( EQUALN(pszAccess, "w", 1) ) + return Open(pszFname, TABWrite, bNoErrorMsg); + else + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: access mode \"%s\" not supported", pszAccess); + return -1; + } +} + +/********************************************************************** + * TABMAPFile::Open() + * + * Open a .MAP file, and initialize the structures to be ready to read + * objects from it. + * + * Since .MAP and .ID files are optional, you can set bNoErrorMsg=TRUE to + * disable the error message and receive an return value of 1 if file + * cannot be opened. + * In this case, only the methods MoveToObjId() and GetCurObjType() can + * be used. They will behave as if the .ID file contained only null + * references, so all object will look like they have NONE geometries. + * + * Returns 0 on success, 1 when the .map file does not exist, -1 on error. + **********************************************************************/ +int TABMAPFile::Open(const char *pszFname, TABAccess eAccess, + GBool bNoErrorMsg /* = FALSE */) +{ + VSILFILE *fp=NULL; + TABRawBinBlock *poBlock=NULL; + + if (m_fp) + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: object already contains an open file"); + return -1; + } + + m_nMinTABVersion = 300; + m_fp = NULL; + m_poHeader = NULL; + m_poIdIndex = NULL; + m_poSpIndex = NULL; + m_poToolDefTable = NULL; + m_eAccessMode = eAccess; + m_bUpdated = FALSE; + m_bLastOpWasRead = FALSE; + m_bLastOpWasWrite = FALSE; + + /*----------------------------------------------------------------- + * Open file + *----------------------------------------------------------------*/ + const char* pszAccess = ( eAccess == TABRead ) ? "rb" : + ( eAccess == TABWrite ) ? "wb+" : + "rb+"; + fp = VSIFOpenL(pszFname, pszAccess); + + m_oBlockManager.Reset(); + + if (fp != NULL && (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite)) + { + /*----------------------------------------------------------------- + * Read access: try to read header block + * First try with a 512 bytes block to check the .map version. + * If it's version 500 or more then read again a 1024 bytes block + *----------------------------------------------------------------*/ + poBlock = TABCreateMAPBlockFromFile(fp, 0, 512, TRUE, m_eAccessMode); + + if (poBlock && poBlock->GetBlockClass() == TABMAP_HEADER_BLOCK && + ((TABMAPHeaderBlock*)poBlock)->m_nMAPVersionNumber >= 500) + { + // Version 500 or higher. Read 1024 bytes block instead of 512 + delete poBlock; + poBlock = TABCreateMAPBlockFromFile(fp, 0, 1024, TRUE, m_eAccessMode); + } + + if (poBlock==NULL || poBlock->GetBlockClass() != TABMAP_HEADER_BLOCK) + { + if (poBlock) + delete poBlock; + poBlock = NULL; + VSIFCloseL(fp); + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: %s does not appear to be a valid .MAP file", + pszFname); + return -1; + } + } + else if (fp != NULL && m_eAccessMode == TABWrite) + { + /*----------------------------------------------------------------- + * Write access: create a new header block + * .MAP files of Version 500 and up appear to have a 1024 bytes + * header. The last 512 bytes are usually all zeros. + *----------------------------------------------------------------*/ + poBlock = new TABMAPHeaderBlock(m_eAccessMode); + poBlock->InitNewBlock(fp, 1024, m_oBlockManager.AllocNewBlock("HEADER") ); + + // Alloc a second 512 bytes of space since oBlockManager deals + // with 512 bytes blocks. + m_oBlockManager.AllocNewBlock("HEADER"); + + m_bUpdated = TRUE; + } + else if (bNoErrorMsg) + { + /*----------------------------------------------------------------- + * .MAP does not exist... produce no error message, but set + * the class members so that MoveToObjId() and GetCurObjType() + * can be used to return only NONE geometries. + *----------------------------------------------------------------*/ + m_fp = NULL; + m_nCurObjType = TAB_GEOM_NONE; + + /* Create a false header block that will return default + * values for projection and coordsys conversion stuff... + */ + m_poHeader = new TABMAPHeaderBlock(m_eAccessMode); + m_poHeader->InitNewBlock(NULL, 512, 0 ); + + return 1; + } + else + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed for %s", pszFname); + return -1; + } + + /*----------------------------------------------------------------- + * File appears to be valid... set the various class members + *----------------------------------------------------------------*/ + m_fp = fp; + m_poHeader = (TABMAPHeaderBlock*)poBlock; + m_pszFname = CPLStrdup(pszFname); + + /*----------------------------------------------------------------- + * Create a TABMAPObjectBlock, in READ mode only or in UPDATE mode + * if there's an object + * + * In WRITE mode, the object block will be created only when needed. + * We do not create the object block in the open() call because + * files that contained only "NONE" geometries ended up with empty + * object and spatial index blocks. + *----------------------------------------------------------------*/ + + if (m_eAccessMode == TABRead || + (m_eAccessMode == TABReadWrite && m_poHeader->m_nFirstIndexBlock != 0 )) + { + m_poCurObjBlock = new TABMAPObjectBlock(m_eAccessMode); + m_poCurObjBlock->InitNewBlock(m_fp, 512); + } + else + { + m_poCurObjBlock = NULL; + } + + /*----------------------------------------------------------------- + * Open associated .ID (object id index) file + *----------------------------------------------------------------*/ + m_poIdIndex = new TABIDFile; + if (m_poIdIndex->Open(pszFname, m_eAccessMode) != 0) + { + // Failed... an error has already been reported + Close(); + return -1; + } + + /*----------------------------------------------------------------- + * Default Coord filter is the MBR of the whole file + * This is currently unused but could eventually be used to handle + * spatial filters more efficiently. + *----------------------------------------------------------------*/ + if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) + { + ResetCoordFilter(); + } + + /*----------------------------------------------------------------- + * We could scan a file through its quad tree index... but we don't! + * + * In read mode, we just ignore the spatial index. + * + * In write mode the index is created and maintained as new object + * blocks are added inside CommitObjBlock(). + *----------------------------------------------------------------*/ + m_poSpIndex = NULL; + + if (m_eAccessMode == TABReadWrite) + { + /* We don't allow quick mode in read/write mode */ + m_bQuickSpatialIndexMode = FALSE; + + if( m_poHeader->m_nFirstIndexBlock != 0 ) + { + TABRawBinBlock *poBlock; + poBlock = GetIndexObjectBlock( m_poHeader->m_nFirstIndexBlock ); + if( poBlock == NULL || (poBlock->GetBlockType() != TABMAP_INDEX_BLOCK && + poBlock->GetBlockType() != TABMAP_OBJECT_BLOCK) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find first index block at offset %d", + m_poHeader->m_nFirstIndexBlock ); + delete poBlock; + } + else if( poBlock->GetBlockType() == TABMAP_INDEX_BLOCK ) + { + m_poSpIndex = (TABMAPIndexBlock *)poBlock; + m_poSpIndex->SetMBR(m_poHeader->m_nXMin, m_poHeader->m_nYMin, + m_poHeader->m_nXMax, m_poHeader->m_nYMax); + } + else /* if( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK ) */ + { + /* This can happen if the file created by MapInfo contains just */ + /* a few objects */ + delete poBlock; + } + } + } + + /*----------------------------------------------------------------- + * Initialization of the Drawing Tools table will be done automatically + * as Read/Write calls are done later. + *----------------------------------------------------------------*/ + m_poToolDefTable = NULL; + + if( m_eAccessMode == TABReadWrite ) + { + InitDrawingTools(); + } + + if( m_eAccessMode == TABReadWrite ) + { + VSIStatBufL sStatBuf; + VSIStatL(m_pszFname, &sStatBuf); + m_oBlockManager.SetLastPtr((int)(((sStatBuf.st_size-1)/512)*512)); + + /* Read chain of garbage blocks */ + if( m_poHeader->m_nFirstGarbageBlock != 0 ) + { + int nCurGarbBlock = m_poHeader->m_nFirstGarbageBlock; + m_oBlockManager.PushGarbageBlockAsLast(nCurGarbBlock); + while(TRUE) + { + GUInt16 nBlockType; + int nNextGarbBlockPtr; + if( VSIFSeekL(fp, nCurGarbBlock, SEEK_SET) != 0 || + VSIFReadL(&nBlockType, sizeof(nBlockType), 1, fp) != 1 || + VSIFReadL(&nNextGarbBlockPtr, sizeof(nNextGarbBlockPtr), 1, fp) != 1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read garbage block at offset %d", + nCurGarbBlock); + break; + } + if( nBlockType != TABMAP_GARB_BLOCK ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Got block type (%d) instead of %d at offset %d", + nBlockType, TABMAP_GARB_BLOCK, nCurGarbBlock); + } + if( nNextGarbBlockPtr == 0 ) + break; + nCurGarbBlock = nNextGarbBlockPtr; + m_oBlockManager.PushGarbageBlockAsLast(nCurGarbBlock); + } + } + } + + /*----------------------------------------------------------------- + * Make sure all previous calls succeded. + *----------------------------------------------------------------*/ + if (CPLGetLastErrorNo() != 0) + { + // Open Failed... an error has already been reported + Close(); + return -1; + } + + return 0; +} + +/********************************************************************** + * TABMAPFile::Close() + * + * Close current file, and release all memory used. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::Close() +{ + // Check if file is opened... it is possible to have a fake header + // without an actual file attached to it. + if (m_fp == NULL && m_poHeader == NULL) + return 0; + + /*---------------------------------------------------------------- + * Write access: commit latest changes to the file. + *---------------------------------------------------------------*/ + if (m_eAccessMode != TABRead) + { + SyncToDisk(); + } + + // Delete all structures + if (m_poHeader) + delete m_poHeader; + m_poHeader = NULL; + + if (m_poIdIndex) + { + m_poIdIndex->Close(); + delete m_poIdIndex; + m_poIdIndex = NULL; + } + + if (m_poCurObjBlock) + { + delete m_poCurObjBlock; + m_poCurObjBlock = NULL; + m_nCurObjPtr = -1; + m_nCurObjType = TAB_GEOM_UNSET; + m_nCurObjId = -1; + } + + if (m_poCurCoordBlock) + { + delete m_poCurCoordBlock; + m_poCurCoordBlock = NULL; + } + + if (m_poSpIndex) + { + delete m_poSpIndex; + m_poSpIndex = NULL; + m_poSpIndexLeaf = NULL; + } + + if (m_poToolDefTable) + { + delete m_poToolDefTable; + m_poToolDefTable = NULL; + } + + // Close file + if (m_fp) + VSIFCloseL(m_fp); + m_fp = NULL; + + CPLFree(m_pszFname); + m_pszFname = NULL; + + return 0; +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +int TABMAPFile::SyncToDisk() +{ + if( m_eAccessMode == TABRead ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SyncToDisk() can be used only with Write access."); + return -1; + } + + if( !m_bUpdated) + return 0; + + // Start by committing current object and coord blocks + // Nothing happens if none has been created yet. + if( CommitObjAndCoordBlocks(FALSE) != 0 ) + return -1; + + // Write the drawing tools definitions now. + if( CommitDrawingTools() != 0 ) + return -1; + + // Commit spatial index blocks + if( CommitSpatialIndex() != 0 ) + return -1; + + // Update header fields and commit + if (m_poHeader) + { + // OK, with V450 files, objects are not limited to 32k nodes + // any more, and this means that m_nMaxCoordBufSize can become + // huge, and actually more huge than can be held in memory. + // MapInfo counts m_nMaxCoordBufSize=0 for V450 objects, but + // until this is cleanly implented, we will just prevent + // m_nMaxCoordBufSizefrom going beyond 512k in V450 files. + if (m_nMinTABVersion >= 450) + { + m_poHeader->m_nMaxCoordBufSize = + MIN(m_poHeader->m_nMaxCoordBufSize, 512*1024); + } + + // Write Ref to beginning of the chain of garbage blocks + m_poHeader->m_nFirstGarbageBlock = + m_oBlockManager.GetFirstGarbageBlock(); + + if( m_poHeader->CommitToFile() != 0 ) + return -1; + } + + // Check for overflow of internal coordinates and produce a warning + // if that happened... + if (m_poHeader && m_poHeader->m_bIntBoundsOverflow) + { + double dBoundsMinX, dBoundsMinY, dBoundsMaxX, dBoundsMaxY; + Int2Coordsys(-1000000000, -1000000000, dBoundsMinX, dBoundsMinY); + Int2Coordsys(1000000000, 1000000000, dBoundsMaxX, dBoundsMaxY); + + CPLError(CE_Warning, TAB_WarningBoundsOverflow, + "Some objects were written outside of the file's " + "predefined bounds.\n" + "These objects may have invalid coordinates when the file " + "is reopened.\n" + "Predefined bounds: (%.15g,%.15g)-(%.15g,%.15g)\n", + dBoundsMinX, dBoundsMinY, dBoundsMaxX, dBoundsMaxY ); + } + + if( m_poIdIndex != NULL && m_poIdIndex->SyncToDisk() != 0 ) + return -1; + + m_bUpdated = FALSE; + + return 0; +} + +/********************************************************************** + * TABMAPFile::ReOpenReadWrite() + **********************************************************************/ +int TABMAPFile::ReOpenReadWrite() +{ + char* pszFname = m_pszFname; + m_pszFname = NULL; + Close(); + if( Open(pszFname, TABReadWrite) < 0 ) + { + CPLFree(pszFname); + return -1; + } + CPLFree(pszFname); + return 0; +} + +/********************************************************************** + * TABMAPFile::SetQuickSpatialIndexMode() + * + * Select "quick spatial index mode". + * + * The default behavior of MITAB is to generate an optimized spatial index, + * but this results in slower write speed. + * + * Applications that want faster write speed and do not care + * about the performance of spatial queries on the resulting file can + * use SetQuickSpatialIndexMode() to require the creation of a non-optimal + * spatial index (actually emulating the type of spatial index produced + * by MITAB before version 1.6.0). In this mode writing files can be + * about 5 times faster, but spatial queries can be up to 30 times slower. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode/*=TRUE*/) +{ + if (m_eAccessMode != TABWrite) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetQuickSpatialIndexMode() failed: file not opened for write access."); + return -1; + } + + if (m_poCurObjBlock != NULL || m_poSpIndex != NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetQuickSpatialIndexMode() must be called before writing the first object."); + return -1; + } + + m_bQuickSpatialIndexMode = bQuickSpatialIndexMode; + + return 0; +} + +/************************************************************************/ +/* PushBlock() */ +/* */ +/* Install a new block (object or spatial) as being current - */ +/* whatever that means. This method is only intended to ever */ +/* be called from LoadNextMatchingObjectBlock(). */ +/************************************************************************/ + +TABRawBinBlock *TABMAPFile::PushBlock( int nFileOffset ) + +{ + TABRawBinBlock *poBlock; + + poBlock = GetIndexObjectBlock( nFileOffset ); + if( poBlock == NULL ) + return NULL; + + if( poBlock->GetBlockType() == TABMAP_INDEX_BLOCK ) + { + TABMAPIndexBlock *poIndex = (TABMAPIndexBlock *) poBlock; + + if( m_poSpIndexLeaf == NULL ) + { + delete m_poSpIndex; + m_poSpIndexLeaf = m_poSpIndex = poIndex; + } + else + { + CPLAssert( + m_poSpIndexLeaf->GetEntry( + m_poSpIndexLeaf->GetCurChildIndex())->nBlockPtr + == nFileOffset ); + + m_poSpIndexLeaf->SetCurChildRef( poIndex, + m_poSpIndexLeaf->GetCurChildIndex() ); + poIndex->SetParentRef( m_poSpIndexLeaf ); + m_poSpIndexLeaf = poIndex; + } + } + else + { + CPLAssert( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK ); + + if( m_poCurObjBlock != NULL ) + delete m_poCurObjBlock; + + m_poCurObjBlock = (TABMAPObjectBlock *) poBlock; + + m_nCurObjPtr = nFileOffset; + m_nCurObjType = TAB_GEOM_NONE; + m_nCurObjId = -1; + } + + return poBlock; +} + +/************************************************************************/ +/* LoadNextMatchingObjectBlock() */ +/* */ +/* Advance through the spatial indices till the next object */ +/* block is loaded that matching the spatial query extents. */ +/************************************************************************/ + +int TABMAPFile::LoadNextMatchingObjectBlock( int bFirstObject ) + +{ + // If we are just starting, verify the stack is empty. + if( bFirstObject ) + { + CPLAssert( m_poSpIndexLeaf == NULL ); + + /* m_nFirstIndexBlock set to 0 means that there is no feature */ + if ( m_poHeader->m_nFirstIndexBlock == 0 ) + return FALSE; + + if( m_poSpIndex != NULL ) + { + m_poSpIndex->UnsetCurChild(); + m_poSpIndexLeaf = m_poSpIndex; + } + else + { + if( PushBlock( m_poHeader->m_nFirstIndexBlock ) == NULL ) + return FALSE; + + if( m_poSpIndex == NULL ) + { + CPLAssert( m_poCurObjBlock != NULL ); + return TRUE; + } + } + } + + while( m_poSpIndexLeaf != NULL ) + { + int iEntry = m_poSpIndexLeaf->GetCurChildIndex(); + + if( iEntry >= m_poSpIndexLeaf->GetNumEntries()-1 ) + { + TABMAPIndexBlock *poParent = m_poSpIndexLeaf->GetParentRef(); + if( m_poSpIndexLeaf == m_poSpIndex ) + m_poSpIndex->UnsetCurChild(); + else + delete m_poSpIndexLeaf; + m_poSpIndexLeaf = poParent; + + if( poParent != NULL ) + { + poParent->SetCurChildRef( NULL, poParent->GetCurChildIndex() ); + } + continue; + } + + m_poSpIndexLeaf->SetCurChildRef( NULL, ++iEntry ); + + TABMAPIndexEntry *psEntry = m_poSpIndexLeaf->GetEntry( iEntry ); + TABRawBinBlock *poBlock; + + if( psEntry->XMax < m_XMinFilter + || psEntry->YMax < m_YMinFilter + || psEntry->XMin > m_XMaxFilter + || psEntry->YMin > m_YMaxFilter ) + continue; + + poBlock = PushBlock( psEntry->nBlockPtr ); + if( poBlock == NULL ) + return FALSE; + else if( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK ) + return TRUE; + else { + /* continue processing new index block */ + } + } + + return m_poSpIndexLeaf != NULL; +} + +/************************************************************************/ +/* ResetReading() */ +/* */ +/* Ensure that any resources related to a spatial traversal of */ +/* the file are recovered, and the state reinitialized to the */ +/* initial conditions. */ +/************************************************************************/ + +void TABMAPFile::ResetReading() + +{ + if( m_bLastOpWasWrite ) + CommitObjAndCoordBlocks( FALSE ); + + if (m_poSpIndex) + { + m_poSpIndex->UnsetCurChild(); + } + m_poSpIndexLeaf = NULL; + + m_bLastOpWasWrite = FALSE; + m_bLastOpWasRead = FALSE; +} + +/************************************************************************/ +/* GetNextFeatureId() */ +/* */ +/* Fetch the next feature id based on a traversal of the */ +/* spatial index. */ +/************************************************************************/ + +int TABMAPFile::GetNextFeatureId( int nPrevId ) + +{ + if( m_bLastOpWasWrite ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GetNextFeatureId() cannot be called after write operation"); + return -1; + } + if( m_eAccessMode == TABWrite ) + { + if( ReOpenReadWrite() < 0 ) + return -1; + } + m_bLastOpWasRead = TRUE; + +/* -------------------------------------------------------------------- */ +/* m_fp is NULL when all geometry are NONE and/or there's */ +/* no .map file and/or there's no spatial indexes */ +/* -------------------------------------------------------------------- */ + if( m_fp == NULL ) + return -1; + + if( nPrevId == 0 ) + nPrevId = -1; + +/* -------------------------------------------------------------------- */ +/* This should always be true if we are being called properly. */ +/* -------------------------------------------------------------------- */ + if( nPrevId != -1 && m_nCurObjId != nPrevId ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "TABMAPFile::GetNextFeatureId(%d) called out of sequence.", + nPrevId ); + return -1; + } + + CPLAssert( nPrevId == -1 || m_poCurObjBlock != NULL ); + +/* -------------------------------------------------------------------- */ +/* Ensure things are initialized properly if this is a request */ +/* for the first feature. */ +/* -------------------------------------------------------------------- */ + if( nPrevId == -1 ) + { + m_nCurObjId = -1; + } + +/* -------------------------------------------------------------------- */ +/* Try to advance to the next object in the current object */ +/* block. */ +/* -------------------------------------------------------------------- */ + if( nPrevId == -1 + || m_poCurObjBlock->AdvanceToNextObject(m_poHeader) == -1 ) + { + // If not, try to advance to the next object block, and get + // first object from it. Note that some object blocks actually + // have no objects, so we may have to advance to additional + // object blocks till we find a non-empty one. + GBool bFirstCall = (nPrevId == -1); + do + { + if( !LoadNextMatchingObjectBlock( bFirstCall ) ) + return -1; + + bFirstCall = FALSE; + } while( m_poCurObjBlock->AdvanceToNextObject(m_poHeader) == -1 ); + } + + m_nCurObjType = m_poCurObjBlock->GetCurObjectType(); + m_nCurObjId = m_poCurObjBlock->GetCurObjectId(); + m_nCurObjPtr = m_poCurObjBlock->GetStartAddress() + + m_poCurObjBlock->GetCurObjectOffset(); + + CPLAssert( m_nCurObjId != -1 ); + + return m_nCurObjId; +} + +/********************************************************************** + * TABMAPFile::Int2Coordsys() + * + * Convert from long integer (internal) to coordinates system units + * as defined in the file's coordsys clause. + * + * Note that the false easting/northing and the conversion factor from + * datum to coordsys units are not included in the calculation. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::Int2Coordsys(GInt32 nX, GInt32 nY, double &dX, double &dY) +{ + if (m_poHeader == NULL) + return -1; + + return m_poHeader->Int2Coordsys(nX, nY, dX, dY); +} + +/********************************************************************** + * TABMAPFile::Coordsys2Int() + * + * Convert from coordinates system units as defined in the file's + * coordsys clause to long integer (internal) coordinates. + * + * Note that the false easting/northing and the conversion factor from + * datum to coordsys units are not included in the calculation. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::Coordsys2Int(double dX, double dY, GInt32 &nX, GInt32 &nY, + GBool bIgnoreOverflow/*=FALSE*/) +{ + if (m_poHeader == NULL) + return -1; + + return m_poHeader->Coordsys2Int(dX, dY, nX, nY, bIgnoreOverflow); +} + +/********************************************************************** + * TABMAPFile::Int2CoordsysDist() + * + * Convert a pair of X,Y size (or distance) values from long integer + * (internal) to coordinates system units as defined in the file's coordsys + * clause. + * + * The difference with Int2Coordsys() is that this function only applies + * the scaling factor: it does not apply the displacement. + * + * Since the calculations on the X and Y values are independent, either + * one can be omitted (i.e. passed as 0) + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::Int2CoordsysDist(GInt32 nX, GInt32 nY, double &dX, double &dY) +{ + if (m_poHeader == NULL) + return -1; + + return m_poHeader->Int2CoordsysDist(nX, nY, dX, dY); +} + +/********************************************************************** + * TABMAPFile::Coordsys2IntDist() + * + * Convert a pair of X,Y size (or distance) values from coordinates + * system units as defined in the file's coordsys clause to long + * integer (internal) coordinate units. + * + * The difference with Int2Coordsys() is that this function only applies + * the scaling factor: it does not apply the displacement. + * + * Since the calculations on the X and Y values are independent, either + * one can be omitted (i.e. passed as 0) + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::Coordsys2IntDist(double dX, double dY, GInt32 &nX, GInt32 &nY) +{ + if (m_poHeader == NULL) + return -1; + + return m_poHeader->Coordsys2IntDist(dX, dY, nX, nY); +} + +/********************************************************************** + * TABMAPFile::SetCoordsysBounds() + * + * Set projection coordinates bounds of the newly created dataset. + * + * This function must be called after creating a new dataset and before any + * feature can be written to it. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::SetCoordsysBounds(double dXMin, double dYMin, + double dXMax, double dYMax) +{ + int nStatus = 0; + + if (m_poHeader == NULL) + return -1; + + nStatus = m_poHeader->SetCoordsysBounds(dXMin, dYMin, dXMax, dYMax); + + if (nStatus == 0) + ResetCoordFilter(); + + return nStatus; +} + +/********************************************************************** + * TABMAPFile::GetMaxObjId() + * + * Return the value of the biggest valid object id. + * + * Note that object ids are positive and start at 1. + * + * Returns a value >= 0 on success, -1 on error. + **********************************************************************/ +GInt32 TABMAPFile::GetMaxObjId() +{ + if (m_poIdIndex) + return m_poIdIndex->GetMaxObjId(); + + return -1; +} + +/********************************************************************** + * TABMAPFile::MoveToObjId() + * + * Get ready to work with the object with the specified id. The object + * data pointer (inside m_poCurObjBlock) will be moved to the first byte + * of data for this map object. + * + * The object type and id (i.e. table row number) will be accessible + * using GetCurObjType() and GetCurObjId(). + * + * Note that object ids are positive and start at 1. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::MoveToObjId(int nObjId) +{ + int nFileOffset; + + if( m_bLastOpWasWrite ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MoveToObjId() cannot be called after write operation"); + return -1; + } + if( m_eAccessMode == TABWrite ) + { + if( ReOpenReadWrite() < 0 ) + return -1; + } + m_bLastOpWasRead = TRUE; + + /*----------------------------------------------------------------- + * In non creation mode, since the .MAP/.ID are optional, if the + * file is not opened then we can still act as if one existed and + * make any object id look like a TAB_GEOM_NONE + *----------------------------------------------------------------*/ + if (m_fp == NULL && m_eAccessMode != TABWrite) + { + CPLAssert(m_poIdIndex == NULL && m_poCurObjBlock == NULL); + m_nCurObjPtr = 0; + m_nCurObjId = nObjId; + m_nCurObjType = TAB_GEOM_NONE; + + return 0; + } + + if (m_poIdIndex == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "MoveToObjId(): file not opened!"); + m_nCurObjPtr = m_nCurObjId = -1; + m_nCurObjType = TAB_GEOM_UNSET; + return -1; + } + + /*----------------------------------------------------------------- + * Move map object pointer to the right location. Fetch location + * from the index file, unless we are already pointing at it. + *----------------------------------------------------------------*/ + if( m_nCurObjId == nObjId ) + nFileOffset = m_nCurObjPtr; + else + nFileOffset = m_poIdIndex->GetObjPtr(nObjId); + + if (nFileOffset != 0 && m_poCurObjBlock == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "MoveToObjId(): no current object block!"); + m_nCurObjPtr = m_nCurObjId = -1; + m_nCurObjType = TAB_GEOM_UNSET; + return -1; + } + + if (nFileOffset == 0) + { + /*--------------------------------------------------------- + * Object with no geometry... this is a valid case. + *--------------------------------------------------------*/ + m_nCurObjPtr = 0; + m_nCurObjId = nObjId; + m_nCurObjType = TAB_GEOM_NONE; + } + else if ( m_poCurObjBlock->GotoByteInFile(nFileOffset, TRUE) == 0) + { + /*------------------------------------------------------------- + * OK, it worked, read the object type and row id. + *------------------------------------------------------------*/ + m_nCurObjPtr = nFileOffset; + m_nCurObjType = (TABGeomType)m_poCurObjBlock->ReadByte(); + m_nCurObjId = m_poCurObjBlock->ReadInt32(); + + // Do a consistency check... + if (m_nCurObjId != nObjId) + { + if( m_nCurObjId == (nObjId | 0x40000000) ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Object %d is marked as deleted in the .MAP file but not in the .ID file." + "File may be corrupt.", + nObjId); + } + else + { + CPLError(CE_Failure, CPLE_FileIO, + "Object ID from the .ID file (%d) differs from the value " + "in the .MAP file (%d). File may be corrupt.", + nObjId, m_nCurObjId); + } + m_nCurObjPtr = m_nCurObjId = -1; + m_nCurObjType = TAB_GEOM_UNSET; + return -1; + } + } + else + { + /*--------------------------------------------------------- + * Failed positioning input file... CPLError has been called. + *--------------------------------------------------------*/ + m_nCurObjPtr = m_nCurObjId = -1; + m_nCurObjType = TAB_GEOM_UNSET; + return -1; + } + + return 0; +} + + +/********************************************************************** + * TABMAPFile::MarkAsDeleted() + + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::MarkAsDeleted() +{ + if (m_eAccessMode == TABRead || m_poCurObjBlock == NULL) + return -1; + + if ( m_nCurObjPtr <= 0 ) + return 0; + + /* Goto offset for object id */ + if ( m_poCurObjBlock->GotoByteInFile(m_nCurObjPtr + 1, TRUE) != 0) + return -1; + + /* Mark object as deleted */ + m_poCurObjBlock->WriteInt32(m_nCurObjId | 0x40000000); + + int ret = 0; + if( m_poCurObjBlock->CommitToFile() != 0 ) + ret = -1; + + /* Update index entry to reflect delete state as well */ + if( m_poIdIndex->SetObjPtr(m_nCurObjId, 0) != 0 ) + ret = -1; + + m_nCurObjPtr = m_nCurObjId = -1; + m_nCurObjType = TAB_GEOM_UNSET; + m_bUpdated = TRUE; + + return ret; +} + + +/********************************************************************** + * TABMAPFile::UpdateMapHeaderInfo() + * + * Update .map header information (counter of objects by type and minimum + * required version) in light of a new object to be written to the file. + * + * Called only by PrepareNewObj() and by the TABCollection class. + **********************************************************************/ +void TABMAPFile::UpdateMapHeaderInfo(TABGeomType nObjType) +{ + /*----------------------------------------------------------------- + * Update count of objects by type in the header block + *----------------------------------------------------------------*/ + if (nObjType == TAB_GEOM_SYMBOL || + nObjType == TAB_GEOM_FONTSYMBOL || + nObjType == TAB_GEOM_CUSTOMSYMBOL || + nObjType == TAB_GEOM_MULTIPOINT || + nObjType == TAB_GEOM_V800_MULTIPOINT || + nObjType == TAB_GEOM_SYMBOL_C || + nObjType == TAB_GEOM_FONTSYMBOL_C || + nObjType == TAB_GEOM_CUSTOMSYMBOL_C || + nObjType == TAB_GEOM_MULTIPOINT_C || + nObjType == TAB_GEOM_V800_MULTIPOINT_C ) + { + m_poHeader->m_numPointObjects++; + } + else if (nObjType == TAB_GEOM_LINE || + nObjType == TAB_GEOM_PLINE || + nObjType == TAB_GEOM_MULTIPLINE || + nObjType == TAB_GEOM_V450_MULTIPLINE || + nObjType == TAB_GEOM_V800_MULTIPLINE || + nObjType == TAB_GEOM_ARC || + nObjType == TAB_GEOM_LINE_C || + nObjType == TAB_GEOM_PLINE_C || + nObjType == TAB_GEOM_MULTIPLINE_C || + nObjType == TAB_GEOM_V450_MULTIPLINE_C || + nObjType == TAB_GEOM_V800_MULTIPLINE_C || + nObjType == TAB_GEOM_ARC_C) + { + m_poHeader->m_numLineObjects++; + } + else if (nObjType == TAB_GEOM_REGION || + nObjType == TAB_GEOM_V450_REGION || + nObjType == TAB_GEOM_V800_REGION || + nObjType == TAB_GEOM_RECT || + nObjType == TAB_GEOM_ROUNDRECT || + nObjType == TAB_GEOM_ELLIPSE || + nObjType == TAB_GEOM_REGION_C || + nObjType == TAB_GEOM_V450_REGION_C || + nObjType == TAB_GEOM_V800_REGION_C || + nObjType == TAB_GEOM_RECT_C || + nObjType == TAB_GEOM_ROUNDRECT_C || + nObjType == TAB_GEOM_ELLIPSE_C) + { + m_poHeader->m_numRegionObjects++; + } + else if (nObjType == TAB_GEOM_TEXT || + nObjType == TAB_GEOM_TEXT_C) + { + m_poHeader->m_numTextObjects++; + } + + /*----------------------------------------------------------------- + * Check forminimum TAB file version number + *----------------------------------------------------------------*/ + int nVersion = TAB_GEOM_GET_VERSION(nObjType); + + if (nVersion > m_nMinTABVersion ) + { + m_nMinTABVersion = nVersion; + } + +} + +/********************************************************************** + * TABMAPFile::PrepareNewObj() + * + * Get ready to write a new object described by poObjHdr (using the + * poObjHdr's m_nId (featureId), m_nType and IntMBR members which must + * have been set by the caller). + * + * Depending on whether "quick spatial index mode" is selected, we either: + * + * 1- Walk through the spatial index to find the best place to insert the + * new object, update the spatial index references, and prepare the object + * data block to be ready to write the object to it. + * ... or ... + * 2- prepare the current object data block to be ready to write the + * object to it. If the object block is full then it is inserted in the + * spatial index and committed to disk, and a new obj block is created. + * + * m_poCurObjBlock will be set to be ready to receive the new object, and + * a new block will be created if necessary (in which case the current + * block contents will be committed to disk, etc.) The actual ObjHdr + * data won't be written to m_poCurObjBlock until CommitNewObj() is called. + * + * If this object type uses coordinate blocks, then the coordinate block + * will be prepared to receive coordinates. + * + * This function will also take care of updating the .ID index entry for + * the new object. + * + * Note that object ids are positive and start at 1. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::PrepareNewObj(TABMAPObjHdr *poObjHdr) +{ + m_nCurObjPtr = m_nCurObjId = -1; + m_nCurObjType = TAB_GEOM_UNSET; + + if (m_eAccessMode == TABRead || + m_poIdIndex == NULL || m_poHeader == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "PrepareNewObj() failed: file not opened for write access."); + return -1; + } + + if (m_bLastOpWasRead ) + { + m_bLastOpWasRead = FALSE; + if( m_poSpIndex) + { + m_poSpIndex->UnsetCurChild(); + } + } + + /*----------------------------------------------------------------- + * For objects with no geometry, we just update the .ID file and return + *----------------------------------------------------------------*/ + if (poObjHdr->m_nType == TAB_GEOM_NONE) + { + m_nCurObjType = poObjHdr->m_nType; + m_nCurObjId = poObjHdr->m_nId; + m_nCurObjPtr = 0; + m_poIdIndex->SetObjPtr(m_nCurObjId, 0); + + return 0; + } + + /*----------------------------------------------------------------- + * Update count of objects by type in the header block and minimum + * required version. + *----------------------------------------------------------------*/ + UpdateMapHeaderInfo(poObjHdr->m_nType); + + + /*----------------------------------------------------------------- + * Depending on the selected spatial index mode, we will either insert + * new objects via the spatial index (slower write but results in optimal + * spatial index) or directly in the current ObjBlock (faster write + * but non-optimal spatial index) + *----------------------------------------------------------------*/ + if ( !m_bQuickSpatialIndexMode ) + { + if (PrepareNewObjViaSpatialIndex(poObjHdr) != 0) + return -1; /* Error already reported */ + } + else + { + if (PrepareNewObjViaObjBlock(poObjHdr) != 0) + return -1; /* Error already reported */ + } + + /*----------------------------------------------------------------- + * Prepare ObjBlock for this new object. + * Real data won't be written to the object block until CommitNewObj() + * is called. + *----------------------------------------------------------------*/ + m_nCurObjPtr = m_poCurObjBlock->PrepareNewObject(poObjHdr); + if (m_nCurObjPtr < 0 ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing object header for feature id %d", + poObjHdr->m_nId); + return -1; + } + + m_nCurObjType = poObjHdr->m_nType; + m_nCurObjId = poObjHdr->m_nId; + + /*----------------------------------------------------------------- + * Update .ID Index + *----------------------------------------------------------------*/ + m_poIdIndex->SetObjPtr(m_nCurObjId, m_nCurObjPtr); + + /*----------------------------------------------------------------- + * Prepare Coords block... + * create a new TABMAPCoordBlock if it was not done yet. + *----------------------------------------------------------------*/ + PrepareCoordBlock(m_nCurObjType, m_poCurObjBlock, &m_poCurCoordBlock); + + if (CPLGetLastErrorNo() != 0 && CPLGetLastErrorType() == CE_Failure) + return -1; + + m_bUpdated = TRUE; + m_bLastOpWasWrite = TRUE; + + return 0; +} + +/********************************************************************** + * TABMAPFile::PrepareNewObjViaSpatialIndex() + * + * Used by TABMAPFile::PrepareNewObj() to walk through the spatial index + * to find the best place to insert the new object, update the spatial + * index references, and prepare the object data block to be ready to + * write the object to it. + * + * This method is used when "quick spatial index mode" is NOT selected, + * i.e. when we want to produce a file with an optimal spatial index + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::PrepareNewObjViaSpatialIndex(TABMAPObjHdr *poObjHdr) +{ + int nObjSize; + GInt32 nObjBlockForInsert = -1; + + /*----------------------------------------------------------------- + * Create spatial index if we don't have one yet. + * We do not create the index and object data blocks in the open() + * call because files that contained only "NONE" geometries ended up + * with empty object and spatial index blocks. + *----------------------------------------------------------------*/ + if (m_poSpIndex == NULL) + { + // Spatial Index not created yet... + m_poSpIndex = new TABMAPIndexBlock(m_eAccessMode); + + m_poSpIndex->InitNewBlock(m_fp, 512, + m_oBlockManager.AllocNewBlock("INDEX")); + m_poSpIndex->SetMAPBlockManagerRef(&m_oBlockManager); + + if( m_eAccessMode == TABReadWrite && m_poHeader->m_nFirstIndexBlock != 0 ) + { + /* This can happen if the file created by MapInfo contains just */ + /* a few objects */ + TABRawBinBlock *poBlock; + poBlock = GetIndexObjectBlock( m_poHeader->m_nFirstIndexBlock ); + CPLAssert( poBlock != NULL && poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK); + delete poBlock; + + if (m_poSpIndex->AddEntry(m_poHeader->m_nXMin, m_poHeader->m_nYMin, + m_poHeader->m_nXMax, m_poHeader->m_nYMax, + m_poHeader->m_nFirstIndexBlock) != 0) + return -1; + + delete m_poCurObjBlock; + m_poCurObjBlock = NULL; + delete m_poCurCoordBlock; + m_poCurCoordBlock = NULL; + } + + m_poHeader->m_nFirstIndexBlock = m_poSpIndex->GetNodeBlockPtr(); + + /* We'll also need to create an object data block (later) */ + nObjBlockForInsert = -1; + + CPLAssert(m_poCurObjBlock == NULL); + } + else + /*----------------------------------------------------------------- + * Search the spatial index to find the best place to insert this + * new object. + *----------------------------------------------------------------*/ + { + nObjBlockForInsert=m_poSpIndex->ChooseLeafForInsert(poObjHdr->m_nMinX, + poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, + poObjHdr->m_nMaxY); + if (nObjBlockForInsert == -1) + { + /* ChooseLeafForInsert() should not fail unless file is corrupt*/ + CPLError(CE_Failure, CPLE_AssertionFailed, + "ChooseLeafForInsert() Failed?!?!"); + return -1; + } + } + + + if (nObjBlockForInsert == -1) + { + /*------------------------------------------------------------- + * Create a new object data block from scratch + *------------------------------------------------------------*/ + m_poCurObjBlock = new TABMAPObjectBlock(TABReadWrite); + + int nBlockOffset = m_oBlockManager.AllocNewBlock("OBJECT"); + + m_poCurObjBlock->InitNewBlock(m_fp, 512, nBlockOffset); + + /*------------------------------------------------------------- + * Insert new object block in index, based on MBR of poObjHdr + *------------------------------------------------------------*/ + if (m_poSpIndex->AddEntry(poObjHdr->m_nMinX, + poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, + poObjHdr->m_nMaxY, + m_poCurObjBlock->GetStartAddress()) != 0) + return -1; + + m_poCurObjBlock->SetMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + + m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth, + (GByte)m_poSpIndex->GetCurMaxDepth()+1); + } + else + { + /*------------------------------------------------------------- + * Load existing object and Coord blocks, unless we've already + * got the right object block in memory + *------------------------------------------------------------*/ + if (m_poCurObjBlock && + m_poCurObjBlock->GetStartAddress() != nObjBlockForInsert) + { + /* Got a block in memory but it's not the right one, flush it */ + if (CommitObjAndCoordBlocks(TRUE) != 0 ) + return -1; + } + + if (m_poCurObjBlock == NULL) + { + if (LoadObjAndCoordBlocks(nObjBlockForInsert) != 0) + return -1; + } + + /* If we have compressed objects, we don't want to change the center */ + m_poCurObjBlock->LockCenter(); + + // Check if the ObjBlock know its MBR. If not (new block, or the current + // block was the good one but retrieved without the index), get the value + // from the index and set it. + GInt32 nMinX, nMinY, nMaxX, nMaxY; + m_poCurObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY); + if( nMinX > nMaxX ) + { + m_poSpIndex->GetCurLeafEntryMBR(m_poCurObjBlock->GetStartAddress(), + nMinX, nMinY, nMaxX, nMaxY); + m_poCurObjBlock->SetMBR(nMinX, nMinY, nMaxX, nMaxY); + } + } + + /*----------------------------------------------------------------- + * Fetch new object size, make sure there is enough room in obj. + * block for new object, update spatial index and split if necessary. + *----------------------------------------------------------------*/ + nObjSize = m_poHeader->GetMapObjectSize(poObjHdr->m_nType); + + + /*----------------------------------------------------------------- + * But first check if we can recover space from this block in case + * there are deleted objects in it. + *----------------------------------------------------------------*/ + if (m_poCurObjBlock->GetNumUnusedBytes() < nObjSize ) + { + TABMAPObjHdr *poExistingObjHdr=NULL; + TABMAPObjHdr **papoSrcObjHdrs = NULL; + int i, numSrcObj = 0; + int nObjectSpace = 0; + + /* First pass to enumerate valid objects and compute their accumulated + required size. */ + m_poCurObjBlock->Rewind(); + while ((poExistingObjHdr = TABMAPObjHdr::ReadNextObj(m_poCurObjBlock, + m_poHeader)) != NULL) + { + if (papoSrcObjHdrs == NULL || numSrcObj%10 == 0) + { + // Realloc the array... by steps of 10 + papoSrcObjHdrs = (TABMAPObjHdr**)CPLRealloc(papoSrcObjHdrs, + (numSrcObj+10)* + sizeof(TABMAPObjHdr*)); + } + papoSrcObjHdrs[numSrcObj++] = poExistingObjHdr; + + nObjectSpace += m_poHeader->GetMapObjectSize(poExistingObjHdr->m_nType); + } + + /* Check that there's really some place that can be recovered */ + if( nObjectSpace < 512 - 20 - m_poCurObjBlock->GetNumUnusedBytes() ) + { +#ifdef DEBUG_VERBOSE + CPLDebug("MITAB", "Compacting block at offset %d, %d objects valid, recovering %d bytes", + m_poCurObjBlock->GetStartAddress(), numSrcObj, + (512 - 20 - m_poCurObjBlock->GetNumUnusedBytes()) - nObjectSpace); +#endif + m_poCurObjBlock->ClearObjects(); + + for(i=0; iPrepareNewObject(papoSrcObjHdrs[i]); + if (nObjPtr < 0 || + m_poCurObjBlock->CommitNewObject(papoSrcObjHdrs[i]) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing object header for feature id %d", + papoSrcObjHdrs[i]->m_nId); + return -1; + } + + /*----------------------------------------------------------------- + * Update .ID Index + *----------------------------------------------------------------*/ + m_poIdIndex->SetObjPtr(papoSrcObjHdrs[i]->m_nId, nObjPtr); + } + } + + /* Cleanup papoSrcObjHdrs[] */ + for(i=0; iGetNumUnusedBytes() >= nObjSize ) + { + /*------------------------------------------------------------- + * New object fits in current block, just update the spatial index + *------------------------------------------------------------*/ + GInt32 nMinX, nMinY, nMaxX, nMaxY; + m_poCurObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY); + + /* Need to calculate the enlarged MBR that includes new object */ + nMinX = MIN(nMinX, poObjHdr->m_nMinX); + nMinY = MIN(nMinY, poObjHdr->m_nMinY); + nMaxX = MAX(nMaxX, poObjHdr->m_nMaxX); + nMaxY = MAX(nMaxY, poObjHdr->m_nMaxY); + + m_poCurObjBlock->SetMBR(nMinX, nMinY, nMaxX, nMaxY); + + if (m_poSpIndex->UpdateLeafEntry(m_poCurObjBlock->GetStartAddress(), + nMinX, nMinY, nMaxX, nMaxY) != 0) + return -1; + } + else + { + /*------------------------------------------------------------- + * OK, the new object won't fit in the current block, need to split + * and update index. + * Split() does its job so that the current obj block will remain + * the best candidate to receive the new object. It also flushes + * everything to disk and will update m_poCurCoordBlock to point to + * the last coord block in the chain, ready to accept new data + *------------------------------------------------------------*/ + TABMAPObjectBlock *poNewObjBlock; + poNewObjBlock= SplitObjBlock(poObjHdr, nObjSize); + + if (poNewObjBlock == NULL) + return -1; /* Split failed, error already reported. */ + + /*------------------------------------------------------------- + * Update index with info about m_poCurObjectBlock *first* + * This is important since UpdateLeafEntry() needs the chain of + * index nodes preloaded by ChooseLeafEntry() in order to do its job + *------------------------------------------------------------*/ + GInt32 nMinX, nMinY, nMaxX, nMaxY; + m_poCurObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY); + CPLAssert(nMinX <= nMaxX); + + /* Need to calculate the enlarged MBR that includes new object */ + nMinX = MIN(nMinX, poObjHdr->m_nMinX); + nMinY = MIN(nMinY, poObjHdr->m_nMinY); + nMaxX = MAX(nMaxX, poObjHdr->m_nMaxX); + nMaxY = MAX(nMaxY, poObjHdr->m_nMaxY); + + m_poCurObjBlock->SetMBR(nMinX, nMinY, nMaxX, nMaxY); + + if (m_poSpIndex->UpdateLeafEntry(m_poCurObjBlock->GetStartAddress(), + nMinX, nMinY, nMaxX, nMaxY) != 0) + return -1; + + /*------------------------------------------------------------- + * Add new obj block to index + *------------------------------------------------------------*/ + poNewObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY); + CPLAssert(nMinX <= nMaxX); + + if (m_poSpIndex->AddEntry(nMinX, nMinY, nMaxX, nMaxY, + poNewObjBlock->GetStartAddress()) != 0) + return -1; + m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth, + (GByte)m_poSpIndex->GetCurMaxDepth()+1); + + /*------------------------------------------------------------- + * Delete second object block, no need to commit to file first since + * it's already been committed to disk by Split() + *------------------------------------------------------------*/ + delete poNewObjBlock; + } + + return 0; +} + +/********************************************************************** + * TABMAPFile::PrepareNewObjViaObjBlock() + * + * Used by TABMAPFile::PrepareNewObj() to prepare the current object + * data block to be ready to write the object to it. If the object block + * is full then it is inserted in the spatial index and committed to disk, + * and a new obj block is created. + * + * This method is used when "quick spatial index mode" is selected, + * i.e. faster write, but non-optimal spatial index. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::PrepareNewObjViaObjBlock(TABMAPObjHdr *poObjHdr) +{ + int nObjSize; + + /*------------------------------------------------------------- + * We will need an object block... check if it exists and + * create it if it has not been created yet (first time for this file). + * We do not create the object block in the open() call because + * files that contained only "NONE" geometries ended up with empty + * object and spatial index blocks. + * Note: A coord block will be created only if needed later. + *------------------------------------------------------------*/ + if (m_poCurObjBlock == NULL) + { + m_poCurObjBlock = new TABMAPObjectBlock(m_eAccessMode); + + int nBlockOffset = m_oBlockManager.AllocNewBlock("OBJECT"); + + m_poCurObjBlock->InitNewBlock(m_fp, 512, nBlockOffset); + + // The reference to the first object block should + // actually go through the index blocks... this will be + // updated when file is closed. + m_poHeader->m_nFirstIndexBlock = nBlockOffset; + } + + /*----------------------------------------------------------------- + * Fetch new object size, make sure there is enough room in obj. + * block for new object, and save/create a new one if necessary. + *----------------------------------------------------------------*/ + nObjSize = m_poHeader->GetMapObjectSize(poObjHdr->m_nType); + if (m_poCurObjBlock->GetNumUnusedBytes() < nObjSize ) + { + /*------------------------------------------------------------- + * OK, the new object won't fit in the current block. Add the + * current block to the spatial index, commit it to disk and init + * a new block + *------------------------------------------------------------*/ + CommitObjAndCoordBlocks(FALSE); + + if (m_poCurObjBlock->InitNewBlock(m_fp,512, + m_oBlockManager.AllocNewBlock("OBJECT"))!=0) + return -1; /* Error already reported */ + + /*------------------------------------------------------------- + * Coord block has been committed to disk but not deleted. + * Delete it to require the creation of a new coord block chain + * as needed. + *-------------------------------------------------------------*/ + if (m_poCurCoordBlock) + { + delete m_poCurCoordBlock; + m_poCurCoordBlock = NULL; + } + + } + + return 0; +} + +/********************************************************************** + * TABMAPFile::CommitNewObj() + * + * Commit object header data to the ObjBlock. Should be called after + * PrepareNewObj, once all members of the ObjHdr have been set. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::CommitNewObj(TABMAPObjHdr *poObjHdr) +{ + /* Update this now so that PrepareCoordBlock() doesn't try to old an older */ + /* block */ + if( m_poCurCoordBlock != NULL ) + m_poCurObjBlock->AddCoordBlockRef(m_poCurCoordBlock->GetStartAddress()); + + /* So that GetExtent() is up-to-date */ + if( m_poSpIndex != NULL ) + { + m_poSpIndex->GetMBR(m_poHeader->m_nXMin, m_poHeader->m_nYMin, + m_poHeader->m_nXMax, m_poHeader->m_nYMax); + } + + return m_poCurObjBlock->CommitNewObject(poObjHdr); +} + + +/********************************************************************** + * TABMAPFile::CommitObjAndCoordBlocks() + * + * Commit the TABMAPObjBlock and TABMAPCoordBlock to disk. + * + * The objects are deleted from memory if bDeleteObjects==TRUE. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::CommitObjAndCoordBlocks(GBool bDeleteObjects /*=FALSE*/) +{ + int nStatus = 0; + + /*----------------------------------------------------------------- + * First check that a objBlock has been created. It is possible to have + * no object block in files that contain only "NONE" geometries. + *----------------------------------------------------------------*/ + if (m_poCurObjBlock == NULL) + return 0; + + if (m_eAccessMode == TABRead) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "CommitObjAndCoordBlocks() failed: file not opened for write access."); + return -1; + } + + if (!m_bLastOpWasWrite) + { + if (bDeleteObjects) + { + delete m_poCurCoordBlock; + m_poCurCoordBlock = NULL; + delete m_poCurObjBlock; + m_poCurObjBlock = NULL; + } + return 0; + } + m_bLastOpWasWrite = FALSE; + + /*----------------------------------------------------------------- + * We need to flush the coord block if there was one + * since a list of coord blocks can belong to only one obj. block + *----------------------------------------------------------------*/ + if (m_poCurCoordBlock) + { + // Update the m_nMaxCoordBufSize member in the header block + // + int nTotalCoordSize = m_poCurCoordBlock->GetNumBlocksInChain()*512; + if (nTotalCoordSize > m_poHeader->m_nMaxCoordBufSize) + m_poHeader->m_nMaxCoordBufSize = nTotalCoordSize; + + // Update the references to this coord block in the MAPObjBlock + // + m_poCurObjBlock->AddCoordBlockRef(m_poCurCoordBlock-> + GetStartAddress()); + nStatus = m_poCurCoordBlock->CommitToFile(); + + if (bDeleteObjects) + { + delete m_poCurCoordBlock; + m_poCurCoordBlock = NULL; + } + } + + /*----------------------------------------------------------------- + * Commit the obj block + *----------------------------------------------------------------*/ + if (nStatus == 0) + { + nStatus = m_poCurObjBlock->CommitToFile(); + } + + + /*----------------------------------------------------------------- + * Update the spatial index ** only in "quick spatial index" mode ** + * In the (default) optimized spatial index mode, the spatial index + * is already maintained up to date as part of inserting the objects in + * PrepareNewObj(). + * + * Spatial index will be created here if it was not done yet. + *----------------------------------------------------------------*/ + if (nStatus == 0 && m_bQuickSpatialIndexMode) + { + GInt32 nXMin, nYMin, nXMax, nYMax; + + if (m_poSpIndex == NULL) + { + // Spatial Index not created yet... + m_poSpIndex = new TABMAPIndexBlock(m_eAccessMode); + + m_poSpIndex->InitNewBlock(m_fp, 512, + m_oBlockManager.AllocNewBlock("INDEX")); + m_poSpIndex->SetMAPBlockManagerRef(&m_oBlockManager); + + m_poHeader->m_nFirstIndexBlock = m_poSpIndex->GetNodeBlockPtr(); + } + + m_poCurObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax); + nStatus = m_poSpIndex->AddEntry(nXMin, nYMin, nXMax, nYMax, + m_poCurObjBlock->GetStartAddress()); + + m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth, + (GByte)m_poSpIndex->GetCurMaxDepth()+1); + } + + /*----------------------------------------------------------------- + * Delete obj block only if requested + *----------------------------------------------------------------*/ + if (bDeleteObjects) + { + delete m_poCurObjBlock; + m_poCurObjBlock = NULL; + } + + return nStatus; +} + +/********************************************************************** + * TABMAPFile::LoadObjAndCoordBlocks() + * + * Load the TABMAPObjBlock at specified address and corresponding + * TABMAPCoordBlock, ready to write new objects to them. + * + * It is assumed that pre-existing m_poCurObjBlock and m_poCurCoordBlock + * have been flushed to disk already using CommitObjAndCoordBlocks() + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::LoadObjAndCoordBlocks(GInt32 nBlockPtr) +{ + TABRawBinBlock *poBlock = NULL; + + /*----------------------------------------------------------------- + * In Write mode, if an object block is already in memory then flush it + *----------------------------------------------------------------*/ + if (m_eAccessMode != TABRead && m_poCurObjBlock != NULL) + { + int nStatus = CommitObjAndCoordBlocks(TRUE); + if (nStatus != 0) + return nStatus; + } + + /*----------------------------------------------------------------- + * Load Obj Block + *----------------------------------------------------------------*/ + poBlock = TABCreateMAPBlockFromFile(m_fp, + nBlockPtr, + 512, TRUE, TABReadWrite); + if (poBlock != NULL && + poBlock->GetBlockClass() == TABMAP_OBJECT_BLOCK) + { + m_poCurObjBlock = (TABMAPObjectBlock*)poBlock; + poBlock = NULL; + } + else + { + CPLError(CE_Failure, CPLE_FileIO, + "LoadObjAndCoordBlocks() failed for object block at %d.", + nBlockPtr); + return -1; + } + + /*----------------------------------------------------------------- + * Load the last coord block in the chain + *----------------------------------------------------------------*/ + if (m_poCurObjBlock->GetLastCoordBlockAddress() == 0) + { + m_poCurCoordBlock = NULL; + return 0; + } + + poBlock = TABCreateMAPBlockFromFile(m_fp, + m_poCurObjBlock->GetLastCoordBlockAddress(), + 512, TRUE, TABReadWrite); + if (poBlock != NULL && poBlock->GetBlockClass() == TABMAP_COORD_BLOCK) + { + m_poCurCoordBlock = (TABMAPCoordBlock*)poBlock; + m_poCurCoordBlock->SetMAPBlockManagerRef(&m_oBlockManager); + poBlock = NULL; + } + else + { + CPLError(CE_Failure, CPLE_FileIO, + "LoadObjAndCoordBlocks() failed for coord block at %d.", + m_poCurObjBlock->GetLastCoordBlockAddress()); + return -1; + } + + return 0; +} + +/********************************************************************** + * TABMAPFile::SplitObjBlock() + * + * Split m_poCurObjBlock using Guttman algorithm. + * + * SplitObjBlock() doe its job so that the current obj block will remain + * the best candidate to receive the new object to add. It also flushes + * everything to disk and will update m_poCurCoordBlock to point to the + * last coord block in the chain, ready to accept new data + * + * Updates to the spatial index are left to the caller. + * + * Returns the TABMAPObjBlock of the second block for use by the caller + * in updating the spatial index, or NULL in case of error. + **********************************************************************/ +TABMAPObjectBlock *TABMAPFile::SplitObjBlock(TABMAPObjHdr *poObjHdrToAdd, + int nSizeOfObjToAdd) +{ + TABMAPObjHdr **papoSrcObjHdrs = NULL, *poObjHdr=NULL; + int i, numSrcObj = 0; + + /*----------------------------------------------------------------- + * Read all object headers + *----------------------------------------------------------------*/ + m_poCurObjBlock->Rewind(); + while ((poObjHdr = TABMAPObjHdr::ReadNextObj(m_poCurObjBlock, + m_poHeader)) != NULL) + { + if (papoSrcObjHdrs == NULL || numSrcObj%10 == 0) + { + // Realloc the array... by steps of 10 + papoSrcObjHdrs = (TABMAPObjHdr**)CPLRealloc(papoSrcObjHdrs, + (numSrcObj+10)* + sizeof(TABMAPObjHdr*)); + } + papoSrcObjHdrs[numSrcObj++] = poObjHdr; + } + /* PickSeedsForSplit (reasonably) assumes at least 2 nodes */ + CPLAssert(numSrcObj > 1); + + /*----------------------------------------------------------------- + * Reset current obj and coord block + *----------------------------------------------------------------*/ + GInt32 nFirstSrcCoordBlock = m_poCurObjBlock->GetFirstCoordBlockAddress(); + + m_poCurObjBlock->InitNewBlock(m_fp, 512, + m_poCurObjBlock->GetStartAddress()); + + TABMAPCoordBlock *poSrcCoordBlock = m_poCurCoordBlock; + m_poCurCoordBlock = NULL; + + /*----------------------------------------------------------------- + * Create new obj and coord block + *----------------------------------------------------------------*/ + TABMAPObjectBlock *poNewObjBlock = new TABMAPObjectBlock(m_eAccessMode); + poNewObjBlock->InitNewBlock(m_fp, 512, m_oBlockManager.AllocNewBlock("OBJECT")); + + /* Use existing center of other block in case we have compressed objects + and freeze it */ + poNewObjBlock->SetCenterFromOtherBlock(m_poCurObjBlock); + + /* Coord block will be alloc'd automatically*/ + TABMAPCoordBlock *poNewCoordBlock = NULL; + + /*----------------------------------------------------------------- + * Pick Seeds for each block + *----------------------------------------------------------------*/ + TABMAPIndexEntry *pasSrcEntries = + (TABMAPIndexEntry*)CPLMalloc(numSrcObj*sizeof(TABMAPIndexEntry)); + for (i=0; im_nMinX; + pasSrcEntries[i].YMin = papoSrcObjHdrs[i]->m_nMinY; + pasSrcEntries[i].XMax = papoSrcObjHdrs[i]->m_nMaxX; + pasSrcEntries[i].YMax = papoSrcObjHdrs[i]->m_nMaxY; + } + + int nSeed1, nSeed2; + TABMAPIndexBlock::PickSeedsForSplit(pasSrcEntries, numSrcObj, -1, + poObjHdrToAdd->m_nMinX, + poObjHdrToAdd->m_nMinY, + poObjHdrToAdd->m_nMaxX, + poObjHdrToAdd->m_nMaxY, + nSeed1, nSeed2); + CPLFree(pasSrcEntries); + pasSrcEntries = NULL; + + /*----------------------------------------------------------------- + * Assign the seeds to their respective block + *----------------------------------------------------------------*/ + // Insert nSeed1 in this block + poObjHdr = papoSrcObjHdrs[nSeed1]; + if (MoveObjToBlock(poObjHdr, poSrcCoordBlock, + m_poCurObjBlock, &m_poCurCoordBlock) <= 0) + return NULL; + + // Move nSeed2 to 2nd block + poObjHdr = papoSrcObjHdrs[nSeed2]; + if (MoveObjToBlock(poObjHdr, poSrcCoordBlock, + poNewObjBlock, &poNewCoordBlock) <= 0) + return NULL; + + /*----------------------------------------------------------------- + * Go through the rest of the entries and assign them to one + * of the 2 blocks + * + * Criteria is minimal area difference. + * Resolve ties by adding the entry to the block with smaller total + * area, then to the one with fewer entries, then to either. + *----------------------------------------------------------------*/ + for(int iEntry=0; iEntryGetMapObjectSize(poObjHdr->m_nType); + + // If one of the two blocks is almost full then all remaining + // entries should go to the other block + if (m_poCurObjBlock->GetNumUnusedBytes() < nObjSize+nSizeOfObjToAdd ) + { + if (MoveObjToBlock(poObjHdr, poSrcCoordBlock, + poNewObjBlock, &poNewCoordBlock) <= 0) + return NULL; + continue; + } + else if (poNewObjBlock->GetNumUnusedBytes() < nObjSize+nSizeOfObjToAdd) + { + if (MoveObjToBlock(poObjHdr, poSrcCoordBlock, + m_poCurObjBlock, &m_poCurCoordBlock) <= 0) + return NULL; + continue; + } + + // Decide which of the two blocks to put this entry in + GInt32 nXMin, nYMin, nXMax, nYMax; + m_poCurObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax); + CPLAssert( nXMin <= nXMax ); + double dAreaDiff1 = + TABMAPIndexBlock::ComputeAreaDiff(nXMin, nYMin, + nXMax, nYMax, + poObjHdr->m_nMinX, + poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, + poObjHdr->m_nMaxY); + + poNewObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax); + CPLAssert( nXMin <= nXMax ); + double dAreaDiff2 = + TABMAPIndexBlock::ComputeAreaDiff(nXMin, nYMin, nXMax, nYMax, + poObjHdr->m_nMinX, + poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, + poObjHdr->m_nMaxY); + + if (dAreaDiff1 < dAreaDiff2) + { + // This entry stays in this block + if (MoveObjToBlock(poObjHdr, poSrcCoordBlock, + m_poCurObjBlock, &m_poCurCoordBlock) <= 0) + return NULL; + } + else + { + // This entry goes to new block + if (MoveObjToBlock(poObjHdr, poSrcCoordBlock, + poNewObjBlock, &poNewCoordBlock) <= 0) + return NULL; + } + } + + /* Cleanup papoSrcObjHdrs[] */ + for(i=0; iCommitToFile() != 0) + return NULL; + delete poNewCoordBlock; + } + + /*----------------------------------------------------------------- + * Release unused coord. data blocks + *----------------------------------------------------------------*/ + if (poSrcCoordBlock) + { + if (poSrcCoordBlock->GetStartAddress() != nFirstSrcCoordBlock) + { + if (poSrcCoordBlock->GotoByteInFile(nFirstSrcCoordBlock, TRUE) != 0) + return NULL; + } + + int nNextCoordBlock = poSrcCoordBlock->GetNextCoordBlock(); + while(poSrcCoordBlock != NULL) + { + // Mark this block as deleted + if (poSrcCoordBlock->CommitAsDeleted(m_oBlockManager. + GetFirstGarbageBlock()) != 0) + return NULL; + m_oBlockManager.PushGarbageBlockAsFirst(poSrcCoordBlock->GetStartAddress()); + + // Advance to next + if (nNextCoordBlock > 0) + { + if (poSrcCoordBlock->GotoByteInFile(nNextCoordBlock, TRUE) != 0) + return NULL; + nNextCoordBlock = poSrcCoordBlock->GetNextCoordBlock(); + } + else + { + // end of chain + delete poSrcCoordBlock; + poSrcCoordBlock = NULL; + } + } + } + + + if (poNewObjBlock->CommitToFile() != 0) + return NULL; + + return poNewObjBlock; +} + +/********************************************************************** + * TABMAPFile::MoveObjToBlock() + * + * Moves an object and its coord data to a new ObjBlock. Used when + * splitting Obj Blocks. + * + * May update the value of ppoCoordBlock if a new coord block had to + * be created. + * + * Returns the address where new object is stored on success, -1 on error. + **********************************************************************/ +int TABMAPFile::MoveObjToBlock(TABMAPObjHdr *poObjHdr, + TABMAPCoordBlock *poSrcCoordBlock, + TABMAPObjectBlock *poDstObjBlock, + TABMAPCoordBlock **ppoDstCoordBlock) +{ + /*----------------------------------------------------------------- + * Copy Coord data if applicable + * We use a temporary TABFeature object to handle the reading/writing + * of coord block data. + *----------------------------------------------------------------*/ + if (m_poHeader->MapObjectUsesCoordBlock(poObjHdr->m_nType)) + { + TABMAPObjHdrWithCoord *poObjHdrCoord =(TABMAPObjHdrWithCoord*)poObjHdr; + OGRFeatureDefn * poDummyDefn = new OGRFeatureDefn; + // Ref count defaults to 0... set it to 1 + poDummyDefn->Reference(); + + TABFeature *poFeature = + TABFeature::CreateFromMapInfoType(poObjHdr->m_nType, poDummyDefn); + + + if (PrepareCoordBlock(poObjHdrCoord->m_nType, + poDstObjBlock, ppoDstCoordBlock) != 0) + return -1; + + GInt32 nSrcCoordPtr = poObjHdrCoord->m_nCoordBlockPtr; + + /* Copy Coord data + * poObjHdrCoord->m_nCoordBlockPtr will be set by WriteGeometry... + * We pass second arg to GotoByteInFile() to force reading from file + * if nSrcCoordPtr is not in current block + */ + if (poSrcCoordBlock->GotoByteInFile(nSrcCoordPtr, TRUE) != 0 || + poFeature->ReadGeometryFromMAPFile(this, poObjHdr, + TRUE /* bCoordDataOnly */, + &poSrcCoordBlock) != 0 || + poFeature->WriteGeometryToMAPFile(this, poObjHdr, + TRUE /* bCoordDataOnly */, + ppoDstCoordBlock) != 0) + { + delete poFeature; + delete poDummyDefn; + return -1; + } + + + // Update the references to dest coord block in the MAPObjBlock + // in case new block has been alloc'd since PrepareCoordBlock() + // + poDstObjBlock->AddCoordBlockRef((*ppoDstCoordBlock)->GetStartAddress()); + /* Cleanup */ + delete poFeature; + poDummyDefn->Release(); + } + + /*----------------------------------------------------------------- + * Prepare and Write ObjHdr to this ObjBlock + *----------------------------------------------------------------*/ + int nObjPtr = poDstObjBlock->PrepareNewObject(poObjHdr); + if (nObjPtr < 0 || + poDstObjBlock->CommitNewObject(poObjHdr) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing object header for feature id %d", + poObjHdr->m_nId); + return -1; + } + + /*----------------------------------------------------------------- + * Update .ID Index + *----------------------------------------------------------------*/ + m_poIdIndex->SetObjPtr(poObjHdr->m_nId, nObjPtr); + + return nObjPtr; +} + +/********************************************************************** + * TABMAPFile::PrepareCoordBlock() + * + * Prepare the coord block to receive an object of specified type if one + * is needed, and update corresponding members in ObjBlock. + * + * May update the value of ppoCoordBlock and Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::PrepareCoordBlock(int nObjType, + TABMAPObjectBlock *poObjBlock, + TABMAPCoordBlock **ppoCoordBlock) +{ + + /*----------------------------------------------------------------- + * Prepare Coords block... + * create a new TABMAPCoordBlock if it was not done yet. + * Note that in write mode, TABCollections require read/write access + * to the coord block. + *----------------------------------------------------------------*/ + if (m_poHeader->MapObjectUsesCoordBlock(nObjType)) + { + if (*ppoCoordBlock == NULL) + { + *ppoCoordBlock = new TABMAPCoordBlock(m_eAccessMode==TABWrite? + TABReadWrite: + m_eAccessMode); + (*ppoCoordBlock)->InitNewBlock(m_fp, 512, + m_oBlockManager.AllocNewBlock("COORD")); + (*ppoCoordBlock)->SetMAPBlockManagerRef(&m_oBlockManager); + + // Set the references to this coord block in the MAPObjBlock + poObjBlock->AddCoordBlockRef((*ppoCoordBlock)->GetStartAddress()); + } + /* If we are not at the end of the chain of coordinate blocks, then */ + /* reload us */ + else if( (*ppoCoordBlock)->GetStartAddress() != poObjBlock->GetLastCoordBlockAddress() ) + { + TABRawBinBlock* poBlock = TABCreateMAPBlockFromFile(m_fp, + poObjBlock->GetLastCoordBlockAddress(), + 512, TRUE, TABReadWrite); + if (poBlock != NULL && poBlock->GetBlockClass() == TABMAP_COORD_BLOCK) + { + delete *ppoCoordBlock; + *ppoCoordBlock = (TABMAPCoordBlock*)poBlock; + (*ppoCoordBlock)->SetMAPBlockManagerRef(&m_oBlockManager); + } + else + { + delete poBlock; + CPLError(CE_Failure, CPLE_FileIO, + "LoadObjAndCoordBlocks() failed for coord block at %d.", + poObjBlock->GetLastCoordBlockAddress()); + return -1; + } + } + + if ((*ppoCoordBlock)->GetNumUnusedBytes() < 4) + { + int nNewBlockOffset = m_oBlockManager.AllocNewBlock("COORD"); + (*ppoCoordBlock)->SetNextCoordBlock(nNewBlockOffset); + (*ppoCoordBlock)->CommitToFile(); + (*ppoCoordBlock)->InitNewBlock(m_fp, 512, nNewBlockOffset); + poObjBlock->AddCoordBlockRef((*ppoCoordBlock)->GetStartAddress()); + } + + // Make sure read/write pointer is at the end of the block + (*ppoCoordBlock)->SeekEnd(); + + if (CPLGetLastErrorNo() != 0 && CPLGetLastErrorType() == CE_Failure) + return -1; + } + + return 0; +} + +/********************************************************************** + * TABMAPFile::GetCurObjType() + * + * Return the MapInfo object type of the object that the m_poCurObjBlock + * is pointing to. This value is set after a call to MoveToObjId(). + * + * Returns a value >= 0 on success, -1 on error. + **********************************************************************/ +TABGeomType TABMAPFile::GetCurObjType() +{ + return m_nCurObjType; +} + +/********************************************************************** + * TABMAPFile::GetCurObjId() + * + * Return the MapInfo object id of the object that the m_poCurObjBlock + * is pointing to. This value is set after a call to MoveToObjId(). + * + * Returns a value >= 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::GetCurObjId() +{ + return m_nCurObjId; +} + +/********************************************************************** + * TABMAPFile::GetCurObjBlock() + * + * Return the m_poCurObjBlock. If MoveToObjId() has previously been + * called then m_poCurObjBlock points to the beginning of the current + * object data. + * + * Returns a reference to an object owned by this TABMAPFile object, or + * NULL on error. + **********************************************************************/ +TABMAPObjectBlock *TABMAPFile::GetCurObjBlock() +{ + return m_poCurObjBlock; +} + +/********************************************************************** + * TABMAPFile::GetCurCoordBlock() + * + * Return the m_poCurCoordBlock. This function should be used after + * PrepareNewObj() to get the reference to the coord block that has + * just been initialized. + * + * Returns a reference to an object owned by this TABMAPFile object, or + * NULL on error. + **********************************************************************/ +TABMAPCoordBlock *TABMAPFile::GetCurCoordBlock() +{ + return m_poCurCoordBlock; +} + +/********************************************************************** + * TABMAPFile::GetCoordBlock() + * + * Return a TABMAPCoordBlock object ready to read coordinates from it. + * The block that contains nFileOffset will automatically be + * loaded, and if nFileOffset is the beginning of a new block then the + * pointer will be moved to the beginning of the data. + * + * The contents of the returned object is only valid until the next call + * to GetCoordBlock(). + * + * Returns a reference to an object owned by this TABMAPFile object, or + * NULL on error. + **********************************************************************/ +TABMAPCoordBlock *TABMAPFile::GetCoordBlock(int nFileOffset) +{ + if (m_poCurCoordBlock == NULL) + { + m_poCurCoordBlock = new TABMAPCoordBlock(m_eAccessMode); + m_poCurCoordBlock->InitNewBlock(m_fp, 512); + m_poCurCoordBlock->SetMAPBlockManagerRef(&m_oBlockManager); + } + + /*----------------------------------------------------------------- + * Use GotoByteInFile() to go to the requested location. This will + * force loading the block if necessary and reading its header. + * If nFileOffset is at the beginning of the requested block, then + * we make sure to move the read pointer past the 8 bytes header + * to be ready to read coordinates data + *----------------------------------------------------------------*/ + if ( m_poCurCoordBlock->GotoByteInFile(nFileOffset, TRUE) != 0) + { + // Failed... an error has already been reported. + return NULL; + } + + if (nFileOffset % 512 == 0) + m_poCurCoordBlock->GotoByteInBlock(8); // Skip Header + + return m_poCurCoordBlock; +} + +/********************************************************************** + * TABMAPFile::GetHeaderBlock() + * + * Return a reference to the MAP file's header block. + * + * The returned pointer is a reference to an object owned by this TABMAPFile + * object and should not be deleted by the caller. + * + * Return NULL if file has not been opened yet. + **********************************************************************/ +TABMAPHeaderBlock *TABMAPFile::GetHeaderBlock() +{ + return m_poHeader; +} + +/********************************************************************** + * TABMAPFile::GetIDFileRef() + * + * Return a reference to the .ID file attached to this .MAP file + * + * The returned pointer is a reference to an object owned by this TABMAPFile + * object and should not be deleted by the caller. + * + * Return NULL if file has not been opened yet. + **********************************************************************/ +TABIDFile *TABMAPFile::GetIDFileRef() +{ + return m_poIdIndex; +} + +/********************************************************************** + * TABMAPFile::GetIndexBlock() + * + * Return a reference to the requested index or object block.. + * + * Ownership of the returned block is turned over to the caller, who should + * delete it when no longer needed. The type of the block can be determined + * with the GetBlockType() method. + * + * @param nFileOffset the offset in the map file of the spatial index + * block or object block to load. + * + * @return The requested TABMAPIndexBlock, TABMAPObjectBlock or NULL if the + * read fails for some reason. + **********************************************************************/ +TABRawBinBlock *TABMAPFile::GetIndexObjectBlock( int nFileOffset ) +{ + /*---------------------------------------------------------------- + * Read from the file + *---------------------------------------------------------------*/ + GByte abyData[512]; + + if (VSIFSeekL(m_fp, nFileOffset, SEEK_SET) != 0 + || VSIFReadL(abyData, sizeof(GByte), 512, m_fp) != 512 ) + { + CPLError(CE_Failure, CPLE_FileIO, + "GetIndexBlock() failed reading %d bytes at offset %d.", + 512, nFileOffset); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create and initialize depending on the block type. */ +/* -------------------------------------------------------------------- */ + int nBlockType = abyData[0]; + TABRawBinBlock *poBlock; + + if( nBlockType == TABMAP_INDEX_BLOCK ) + { + TABMAPIndexBlock* poIndexBlock = new TABMAPIndexBlock(m_eAccessMode); + poBlock = poIndexBlock; + poIndexBlock->SetMAPBlockManagerRef(&m_oBlockManager); + } + else + poBlock = new TABMAPObjectBlock(m_eAccessMode); + + if( poBlock->InitBlockFromData(abyData, 512, 512, + TRUE, m_fp, nFileOffset) == -1 ) + { + delete poBlock; + poBlock = NULL; + } + + return poBlock; +} + +/********************************************************************** + * TABMAPFile::InitDrawingTools() + * + * Init the drawing tools for this file. + * + * In Read mode, this will load the drawing tools from the file. + * + * In Write mode, this function will init an empty the tool def table. + * + * Reutrns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::InitDrawingTools() +{ + int nStatus = 0; + + if (m_poHeader == NULL) + return -1; // File not opened yet! + + /*------------------------------------------------------------- + * We want to perform this initialisation only ONCE + *------------------------------------------------------------*/ + if (m_poToolDefTable != NULL) + return 0; + + /*------------------------------------------------------------- + * Create a new ToolDefTable... no more initialization is required + * unless we want to read tool blocks from file. + *------------------------------------------------------------*/ + m_poToolDefTable = new TABToolDefTable; + + if ((m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) && + m_poHeader->m_nFirstToolBlock != 0) + { + TABMAPToolBlock *poBlock; + + poBlock = new TABMAPToolBlock(TABRead); + poBlock->InitNewBlock(m_fp, 512); + + /*------------------------------------------------------------- + * Use GotoByteInFile() to go to the first block's location. This will + * force loading the block if necessary and reading its header. + * Also make sure to move the read pointer past the 8 bytes header + * to be ready to read drawing tools data + *------------------------------------------------------------*/ + if ( poBlock->GotoByteInFile(m_poHeader->m_nFirstToolBlock)!= 0) + { + // Failed... an error has already been reported. + delete poBlock; + return -1; + } + + poBlock->GotoByteInBlock(8); + + nStatus = m_poToolDefTable->ReadAllToolDefs(poBlock); + delete poBlock; + } + + return nStatus; +} + + +/********************************************************************** + * TABMAPFile::CommitDrawingTools() + * + * Write the drawing tools for this file. + * + * This function applies only to write access mode. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::CommitDrawingTools() +{ + int nStatus = 0; + + if (m_eAccessMode == TABRead || m_poHeader == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "CommitDrawingTools() failed: file not opened for write access."); + return -1; + } + + if (m_poToolDefTable == NULL || + (m_poToolDefTable->GetNumPen() + + m_poToolDefTable->GetNumBrushes() + + m_poToolDefTable->GetNumFonts() + + m_poToolDefTable->GetNumSymbols()) == 0) + { + return 0; // Nothing to do! + } + + /*------------------------------------------------------------- + * Create a new TABMAPToolBlock and update header fields + *------------------------------------------------------------*/ + TABMAPToolBlock *poBlock; + + poBlock = new TABMAPToolBlock(m_eAccessMode); + if( m_poHeader->m_nFirstToolBlock != 0 ) + poBlock->InitNewBlock(m_fp, 512, m_poHeader->m_nFirstToolBlock); + else + poBlock->InitNewBlock(m_fp, 512, m_oBlockManager.AllocNewBlock("TOOL")); + poBlock->SetMAPBlockManagerRef(&m_oBlockManager); + + m_poHeader->m_nFirstToolBlock = poBlock->GetStartAddress(); + + m_poHeader->m_numPenDefs = (GByte)m_poToolDefTable->GetNumPen(); + m_poHeader->m_numBrushDefs = (GByte)m_poToolDefTable->GetNumBrushes(); + m_poHeader->m_numFontDefs = (GByte)m_poToolDefTable->GetNumFonts(); + m_poHeader->m_numSymbolDefs = (GByte)m_poToolDefTable->GetNumSymbols(); + + /*------------------------------------------------------------- + * Do the actual work and delete poBlock + * (Note that poBlock will have already been committed to the file + * by WriteAllToolDefs() ) + *------------------------------------------------------------*/ + nStatus = m_poToolDefTable->WriteAllToolDefs(poBlock); + + m_poHeader->m_numMapToolBlocks = (GInt16)poBlock->GetNumBlocksInChain(); + + delete poBlock; + + return nStatus; +} + + +/********************************************************************** + * TABMAPFile::ReadPenDef() + * + * Fill the TABPenDef structure with the definition of the specified pen + * index... (1-based pen index) + * + * If nPenIndex==0 or is invalid, then the structure is cleared. + * + * Returns 0 on success, -1 on error (i.e. Pen not found). + **********************************************************************/ +int TABMAPFile::ReadPenDef(int nPenIndex, TABPenDef *psDef) +{ + TABPenDef *psTmp; + + if (m_poToolDefTable == NULL && InitDrawingTools() != 0) + return -1; + + if (psDef && m_poToolDefTable && + (psTmp = m_poToolDefTable->GetPenDefRef(nPenIndex)) != NULL) + { + *psDef = *psTmp; + } + else if (psDef) + { + /* Init to MapInfo default */ + static const TABPenDef csDefaultPen = MITAB_PEN_DEFAULT; + *psDef = csDefaultPen; + return -1; + } + return 0; +} + +/********************************************************************** + * TABMAPFile::WritePenDef() + * + * Write a Pen Tool to the map file and return the pen index that has + * been attributed to this Pen tool definition, or -1 if something went + * wrong + * + * Note that the returned index is a 1-based index. A value of 0 + * indicates "none" in MapInfo. + + * Returns a value >= 0 on success, -1 on error + **********************************************************************/ +int TABMAPFile::WritePenDef(TABPenDef *psDef) +{ + if (psDef == NULL || + (m_poToolDefTable == NULL && InitDrawingTools() != 0) || + m_poToolDefTable==NULL ) + { + return -1; + } + + return m_poToolDefTable->AddPenDefRef(psDef); +} + + +/********************************************************************** + * TABMAPFile::ReadBrushDef() + * + * Fill the TABBrushDef structure with the definition of the specified Brush + * index... (1-based Brush index) + * + * If nBrushIndex==0 or is invalid, then the structure is cleared. + * + * Returns 0 on success, -1 on error (i.e. Brush not found). + **********************************************************************/ +int TABMAPFile::ReadBrushDef(int nBrushIndex, TABBrushDef *psDef) +{ + TABBrushDef *psTmp; + + if (m_poToolDefTable == NULL && InitDrawingTools() != 0) + return -1; + + if (psDef && m_poToolDefTable && + (psTmp = m_poToolDefTable->GetBrushDefRef(nBrushIndex)) != NULL) + { + *psDef = *psTmp; + } + else if (psDef) + { + /* Init to MapInfo default */ + static const TABBrushDef csDefaultBrush = MITAB_BRUSH_DEFAULT; + *psDef = csDefaultBrush; + return -1; + } + return 0; +} + +/********************************************************************** + * TABMAPFile::WriteBrushDef() + * + * Write a Brush Tool to the map file and return the Brush index that has + * been attributed to this Brush tool definition, or -1 if something went + * wrong + * + * Note that the returned index is a 1-based index. A value of 0 + * indicates "none" in MapInfo. + + * Returns a value >= 0 on success, -1 on error + **********************************************************************/ +int TABMAPFile::WriteBrushDef(TABBrushDef *psDef) +{ + if (psDef == NULL || + (m_poToolDefTable == NULL && InitDrawingTools() != 0) || + m_poToolDefTable==NULL ) + { + return -1; + } + + return m_poToolDefTable->AddBrushDefRef(psDef); +} + + +/********************************************************************** + * TABMAPFile::ReadFontDef() + * + * Fill the TABFontDef structure with the definition of the specified Font + * index... (1-based Font index) + * + * If nFontIndex==0 or is invalid, then the structure is cleared. + * + * Returns 0 on success, -1 on error (i.e. Font not found). + **********************************************************************/ +int TABMAPFile::ReadFontDef(int nFontIndex, TABFontDef *psDef) +{ + TABFontDef *psTmp; + + if (m_poToolDefTable == NULL && InitDrawingTools() != 0) + return -1; + + if (psDef && m_poToolDefTable && + (psTmp = m_poToolDefTable->GetFontDefRef(nFontIndex)) != NULL) + { + *psDef = *psTmp; + } + else if (psDef) + { + /* Init to MapInfo default */ + static const TABFontDef csDefaultFont = MITAB_FONT_DEFAULT; + *psDef = csDefaultFont; + return -1; + } + return 0; +} + +/********************************************************************** + * TABMAPFile::WriteFontDef() + * + * Write a Font Tool to the map file and return the Font index that has + * been attributed to this Font tool definition, or -1 if something went + * wrong + * + * Note that the returned index is a 1-based index. A value of 0 + * indicates "none" in MapInfo. + + * Returns a value >= 0 on success, -1 on error + **********************************************************************/ +int TABMAPFile::WriteFontDef(TABFontDef *psDef) +{ + if (psDef == NULL || + (m_poToolDefTable == NULL && InitDrawingTools() != 0) || + m_poToolDefTable==NULL ) + { + return -1; + } + + return m_poToolDefTable->AddFontDefRef(psDef); +} + +/********************************************************************** + * TABMAPFile::ReadSymbolDef() + * + * Fill the TABSymbolDef structure with the definition of the specified Symbol + * index... (1-based Symbol index) + * + * If nSymbolIndex==0 or is invalid, then the structure is cleared. + * + * Returns 0 on success, -1 on error (i.e. Symbol not found). + **********************************************************************/ +int TABMAPFile::ReadSymbolDef(int nSymbolIndex, TABSymbolDef *psDef) +{ + TABSymbolDef *psTmp; + + if (m_poToolDefTable == NULL && InitDrawingTools() != 0) + return -1; + + if (psDef && m_poToolDefTable && + (psTmp = m_poToolDefTable->GetSymbolDefRef(nSymbolIndex)) != NULL) + { + *psDef = *psTmp; + } + else if (psDef) + { + /* Init to MapInfo default */ + static const TABSymbolDef csDefaultSymbol = MITAB_SYMBOL_DEFAULT; + *psDef = csDefaultSymbol; + return -1; + } + return 0; +} + +/********************************************************************** + * TABMAPFile::WriteSymbolDef() + * + * Write a Symbol Tool to the map file and return the Symbol index that has + * been attributed to this Symbol tool definition, or -1 if something went + * wrong + * + * Note that the returned index is a 1-based index. A value of 0 + * indicates "none" in MapInfo. + + * Returns a value >= 0 on success, -1 on error + **********************************************************************/ +int TABMAPFile::WriteSymbolDef(TABSymbolDef *psDef) +{ + if (psDef == NULL || + (m_poToolDefTable == NULL && InitDrawingTools() != 0) || + m_poToolDefTable==NULL ) + { + return -1; + } + + return m_poToolDefTable->AddSymbolDefRef(psDef); +} + +#define ORDER_MIN_MAX(type,min,max) \ + { if( (max) < (min) ) \ + { type temp = (max); (max) = (min); (min) = temp; } } + +/********************************************************************** + * TABMAPFile::SetCoordFilter() + * + * Set the MBR of the area of interest... only objects that at least + * overlap with that area will be returned. + * + * @param sMin minimum x/y the file's projection coord. + * @param sMax maximum x/y the file's projection coord. + **********************************************************************/ +void TABMAPFile::SetCoordFilter(TABVertex sMin, TABVertex sMax) +{ + m_sMinFilter = sMin; + m_sMaxFilter = sMax; + + Coordsys2Int(sMin.x, sMin.y, m_XMinFilter, m_YMinFilter, TRUE); + Coordsys2Int(sMax.x, sMax.y, m_XMaxFilter, m_YMaxFilter, TRUE); + + ORDER_MIN_MAX(int,m_XMinFilter,m_XMaxFilter); + ORDER_MIN_MAX(int,m_YMinFilter,m_YMaxFilter); + ORDER_MIN_MAX(double,m_sMinFilter.x,m_sMaxFilter.x); + ORDER_MIN_MAX(double,m_sMinFilter.y,m_sMaxFilter.y); +} + +/********************************************************************** + * TABMAPFile::ResetCoordFilter() + * + * Reset the MBR of the area of interest to be the extents as defined + * in the header. + **********************************************************************/ + +void TABMAPFile::ResetCoordFilter() + +{ + m_XMinFilter = m_poHeader->m_nXMin; + m_YMinFilter = m_poHeader->m_nYMin; + m_XMaxFilter = m_poHeader->m_nXMax; + m_YMaxFilter = m_poHeader->m_nYMax; + Int2Coordsys(m_XMinFilter, m_YMinFilter, + m_sMinFilter.x, m_sMinFilter.y); + Int2Coordsys(m_XMaxFilter, m_YMaxFilter, + m_sMaxFilter.x, m_sMaxFilter.y); + + ORDER_MIN_MAX(int,m_XMinFilter,m_XMaxFilter); + ORDER_MIN_MAX(int,m_YMinFilter,m_YMaxFilter); + ORDER_MIN_MAX(double,m_sMinFilter.x,m_sMaxFilter.x); + ORDER_MIN_MAX(double,m_sMinFilter.y,m_sMaxFilter.y); +} + +/********************************************************************** + * TABMAPFile::GetCoordFilter() + * + * Get the MBR of the area of interest, as previously set by + * SetCoordFilter(). + * + * @param sMin vertex into which the minimum x/y values put in coordsys space. + * @param sMax vertex into which the maximum x/y values put in coordsys space. + **********************************************************************/ +void TABMAPFile::GetCoordFilter(TABVertex &sMin, TABVertex &sMax) +{ + sMin = m_sMinFilter; + sMax = m_sMaxFilter; +} + +/********************************************************************** + * TABMAPFile::CommitSpatialIndex() + * + * Write the spatial index blocks tree for this file. + * + * This function applies only to write access mode. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPFile::CommitSpatialIndex() +{ + if (m_eAccessMode == TABRead || m_poHeader == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "CommitSpatialIndex() failed: file not opened for write access."); + return -1; + } + + if (m_poSpIndex == NULL) + { + return 0; // Nothing to do! + } + + /*------------------------------------------------------------- + * Update header fields and commit index block + * (it's children will be recursively committed as well) + *------------------------------------------------------------*/ + // Add 1 to Spatial Index Depth to account to the MapObjectBlocks + m_poHeader->m_nMaxSpIndexDepth = MAX(m_poHeader->m_nMaxSpIndexDepth, + (GByte)m_poSpIndex->GetCurMaxDepth()+1); + + m_poSpIndex->GetMBR(m_poHeader->m_nXMin, m_poHeader->m_nYMin, + m_poHeader->m_nXMax, m_poHeader->m_nYMax); + + return m_poSpIndex->CommitToFile(); +} + + +/********************************************************************** + * TABMAPFile::GetMinTABFileVersion() + * + * Returns the minimum TAB file version number that can contain all the + * objects stored in this file. + **********************************************************************/ +int TABMAPFile::GetMinTABFileVersion() +{ + int nToolVersion = 0; + + if (m_poToolDefTable) + nToolVersion = m_poToolDefTable->GetMinVersionNumber(); + + return MAX(nToolVersion, m_nMinTABVersion); +} + + +/********************************************************************** + * TABMAPFile::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABMAPFile::Dump(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABMAPFile::Dump() -----\n"); + + if (m_fp == NULL) + { + fprintf(fpOut, "File is not opened.\n"); + } + else + { + fprintf(fpOut, "File is opened: %s\n", m_pszFname); + fprintf(fpOut, "Coordsys filter = (%g,%g)-(%g,%g)\n", + m_sMinFilter.x, m_sMinFilter.y, m_sMaxFilter.x,m_sMaxFilter.y); + fprintf(fpOut, "Int coord filter = (%d,%d)-(%d,%d)\n", + m_XMinFilter, m_YMinFilter, m_XMaxFilter,m_YMaxFilter); + + fprintf(fpOut, "\nFile Header follows ...\n\n"); + m_poHeader->Dump(fpOut); + fprintf(fpOut, "... end of file header.\n\n"); + + fprintf(fpOut, "Associated .ID file ...\n\n"); + m_poIdIndex->Dump(fpOut); + fprintf(fpOut, "... end of ID file dump.\n\n"); + } + + fflush(fpOut); +} + +#endif // DEBUG + + +/********************************************************************** + * TABMAPFile::DumpSpatialIndexToMIF() + * + * Dump the spatial index tree... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABMAPFile::DumpSpatialIndexToMIF(TABMAPIndexBlock *poNode, + FILE *fpMIF, FILE *fpMID, + int nParentId /*=-1*/, + int nIndexInNode /*=-1*/, + int nCurDepth /*=0*/, + int nMaxDepth /*=-1*/) +{ + if (poNode == NULL) + { + if (m_poHeader && m_poHeader->m_nFirstIndexBlock != 0) + { + TABRawBinBlock *poBlock; + + poBlock = GetIndexObjectBlock(m_poHeader->m_nFirstIndexBlock); + if (poBlock && poBlock->GetBlockType() == TABMAP_INDEX_BLOCK) + poNode = (TABMAPIndexBlock *)poBlock; + } + + if (poNode == NULL) + return; + } + + + /*------------------------------------------------------------- + * Report info on current tree node + *------------------------------------------------------------*/ + int numEntries = poNode->GetNumEntries(); + GInt32 nXMin, nYMin, nXMax, nYMax; + double dXMin, dYMin, dXMax, dYMax; + + poNode->RecomputeMBR(); + poNode->GetMBR(nXMin, nYMin, nXMax, nYMax); + + Int2Coordsys(nXMin, nYMin, dXMin, dYMin); + Int2Coordsys(nXMax, nYMax, dXMax, dYMax); + + VSIFPrintf(fpMIF, "RECT %g %g %g %g\n", dXMin, dYMin, dXMax, dYMax); + VSIFPrintf(fpMIF, " Brush(1, 0)\n"); /* No fill */ + + VSIFPrintf(fpMID, "%d,%d,%d,%d,%g,%d,%d,%d,%d\n", + poNode->GetStartAddress(), + nParentId, + nIndexInNode, + nCurDepth, + MITAB_AREA(nXMin, nYMin, nXMax, nYMax), + nXMin, nYMin, nXMax, nYMax); + + if (nMaxDepth != 0) + { + /*------------------------------------------------------------- + * Loop through all entries, dumping each of them + *------------------------------------------------------------*/ + for(int i=0; iGetEntry(i); + + TABRawBinBlock *poBlock; + poBlock = GetIndexObjectBlock( psEntry->nBlockPtr ); + if( poBlock == NULL ) + continue; + + if( poBlock->GetBlockType() == TABMAP_INDEX_BLOCK ) + { + /* Index block, dump recursively */ + DumpSpatialIndexToMIF((TABMAPIndexBlock *)poBlock, + fpMIF, fpMID, + poNode->GetStartAddress(), + i, nCurDepth+1, nMaxDepth-1); + } + else + { + /* Object block, dump directly */ + CPLAssert( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK ); + + Int2Coordsys(psEntry->XMin, psEntry->YMin, dXMin, dYMin); + Int2Coordsys(psEntry->XMax, psEntry->YMax, dXMax, dYMax); + + VSIFPrintf(fpMIF, "RECT %g %g %g %g\n", dXMin, dYMin, dXMax, dYMax); + VSIFPrintf(fpMIF, " Brush(1, 0)\n"); /* No fill */ + + VSIFPrintf(fpMID, "%d,%d,%d,%d,%g,%d,%d,%d,%d\n", + psEntry->nBlockPtr, + poNode->GetStartAddress(), + i, + nCurDepth+1, + MITAB_AREA(psEntry->XMin, psEntry->YMin, + psEntry->XMax, psEntry->YMax), + psEntry->XMin, psEntry->YMin, + psEntry->XMax, psEntry->YMax); + } + + delete poBlock; + } + + } + +} + +#endif // DEBUG diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapheaderblock.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapheaderblock.cpp new file mode 100644 index 000000000..0313ef141 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapheaderblock.cpp @@ -0,0 +1,1093 @@ +/********************************************************************** + * $Id: mitab_mapheaderblock.cpp,v 1.33 2008-02-01 19:36:31 dmorissette Exp $ + * + * Name: mitab_mapheaderblock.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABHeaderBlock class used to handle + * reading/writing of the .MAP files' header block + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2002, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_mapheaderblock.cpp,v $ + * Revision 1.33 2008-02-01 19:36:31 dmorissette + * Initial support for V800 REGION and MULTIPLINE (bug 1496) + * + * Revision 1.32 2006/11/28 18:49:08 dmorissette + * Completed changes to split TABMAPObjectBlocks properly and produce an + * optimal spatial index (bug 1585) + * + * Revision 1.31 2005/09/29 20:16:54 dmorissette + * Support for writing affine projection params in .MAP header (AJD, bug 1155) + * + * Revision 1.30 2005/05/12 20:46:15 dmorissette + * Initialize m_sProj.nDatumId in InitNewBlock(). (hss/geh) + * + * Revision 1.29 2005/03/22 23:24:54 dmorissette + * Added support for datum id in .MAP header (bug 910) + * + * Revision 1.28 2004/12/15 22:52:49 dmorissette + * Revert back to using doubles for range check in CoordSys2Int(). Hopefully + * I got it right this time. (bug 894) + * + * Revision 1.27 2004/12/08 23:27:35 dmorissette + * Fixed coordinates rounding error in Coordsys2Int() (bug 894) + * + * Revision 1.26 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.25 2003/08/12 23:17:21 dmorissette + * Added reading of v500+ coordsys affine params (Anthony D. - Encom) + * + * Revision 1.24 2002/06/28 18:32:37 julien + * Add SetSpatialFilter() in TABSeamless class (Bug 164, MapServer) + * Use double for comparison in Coordsys2Int() in mitab_mapheaderblock.cpp + * + * Revision 1.23 2002/04/25 16:05:24 julien + * Disabled the overflow warning in SetCoordFilter() by adding bIgnoreOverflow + * variable in Coordsys2Int of the TABMAPFile class and TABMAPHeaderBlock class + * + * Revision 1.22 2002/03/26 01:48:40 daniel + * Added Multipoint object type (V650) + * + * Revision 1.21 2001/12/05 22:23:06 daniel + * Can't use rint() on Windows... replace rint() with (int)(val+0.5) + * + * Revision 1.20 2001/12/05 21:56:15 daniel + * Mod. CoordSys2Int() to use rint() for double to integer coord. conversion. + * + * Revision 1.19 2001/11/19 15:05:42 daniel + * Prevent writing of coordinates outside of the +/-1e9 integer bounds. + * + * Revision 1.18 2000/12/07 03:58:20 daniel + * Pass first arg of pow() as double + * + * Revision 1.17 2000/09/19 19:35:53 daniel + * Set default scale/displacement when reading V100 headers + * + * Revision 1.16 2000/07/10 14:56:52 daniel + * Handle m_nOriginQuadrant==0 as quadrant 3 (reverse x and y axis) + * + * Revision 1.15 2000/03/13 05:59:25 daniel + * Switch from V400 to V500 .MAP header (1024 bytes) + * + * Revision 1.14 2000/02/28 17:01:05 daniel + * Use a #define for header version number + * + * Revision 1.13 2000/02/07 18:09:10 daniel + * OOpppps ... test on version number was reversed! + * + * Revision 1.12 2000/02/07 17:41:02 daniel + * Ignore the values of 5 last datum params in version=200 headers + * + * Revision 1.11 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.10 2000/01/15 05:37:47 daniel + * Use a #define for default quadrant value in new files + * + * Revision 1.9 1999/10/19 16:27:10 warmerda + * Default unitsid to 7 (meters) instead of 0 (miles). + * + * Revision 1.8 1999/10/19 06:05:35 daniel + * Removed obsolete code segments in the coord. conversion functions. + * + * Revision 1.7 1999/10/06 13:21:37 daniel + * Reworked int<->coordsys coords. conversion... hopefully it's OK this time! + * + * Revision 1.6 1999/10/01 03:47:38 daniel + * Better defaults for header fields, and more complete Dump() for debugging + * + * Revision 1.5 1999/09/29 04:25:03 daniel + * Set default scale so that default coord range is +/-1000000.000 + * + * Revision 1.4 1999/09/26 14:59:36 daniel + * Implemented write support + * + * Revision 1.3 1999/09/21 03:36:33 warmerda + * slight modification to dump precision + * + * Revision 1.2 1999/09/16 02:39:16 daniel + * Completed read support for most feature types + * + * Revision 1.1 1999/07/12 04:18:24 daniel + * Initial checkin + * + **********************************************************************/ + +#include "mitab.h" + +#ifdef WIN32 +inline double round(double r) { + return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5); +} +#endif + +/*--------------------------------------------------------------------- + * Set various constants used in generating the header block. + *--------------------------------------------------------------------*/ +#define HDR_MAGIC_COOKIE 42424242 +#define HDR_VERSION_NUMBER 500 +#define HDR_DATA_BLOCK_SIZE 512 + +#define HDR_DEF_ORG_QUADRANT 1 // N-E Quadrant +#define HDR_DEF_REFLECTXAXIS 0 + +/*--------------------------------------------------------------------- + * The header block starts with an array of map object lenght constants. + *--------------------------------------------------------------------*/ +#define HDR_OBJ_LEN_ARRAY_SIZE 73 +static GByte gabyObjLenArray[ HDR_OBJ_LEN_ARRAY_SIZE ] = { + 0x00,0x0a,0x0e,0x15,0x0e,0x16,0x1b,0xa2, + 0xa6,0xab,0x1a,0x2a,0x2f,0xa5,0xa9,0xb5, + 0xa7,0xb5,0xd9,0x0f,0x17,0x23,0x13,0x1f, + 0x2b,0x0f,0x17,0x23,0x4f,0x57,0x63,0x9c, + 0xa4,0xa9,0xa0,0xa8,0xad,0xa4,0xa8,0xad, + 0x16,0x1a,0x39,0x0d,0x11,0x37,0xa5,0xa9, + 0xb5,0xa4,0xa8,0xad,0xb2,0xb6,0xdc,0xbd, + 0xbd,0xf4,0x2b,0x2f,0x55,0xc8,0xcc,0xd8, + 0xc7,0xcb,0xd0,0xd3,0xd7,0xfd,0xc2,0xc2, + 0xf9}; + + + +/*===================================================================== + * class TABMAPHeaderBlock + *====================================================================*/ + + +/********************************************************************** + * TABMAPHeaderBlock::TABMAPHeaderBlock() + * + * Constructor. + **********************************************************************/ +TABMAPHeaderBlock::TABMAPHeaderBlock(TABAccess eAccessMode /*= TABRead*/): + TABRawBinBlock(eAccessMode, TRUE) +{ + InitMembersWithDefaultValues(); + + /* We don't want to reset it once it is set */ + m_bIntBoundsOverflow = FALSE; +} + +/********************************************************************** + * TABMAPHeaderBlock::~TABMAPHeaderBlock() + * + * Destructor. + **********************************************************************/ +TABMAPHeaderBlock::~TABMAPHeaderBlock() +{ + +} + +/********************************************************************** + * TABMAPHeaderBlock::InitMembersWithDefaultValues() + **********************************************************************/ +void TABMAPHeaderBlock::InitMembersWithDefaultValues() +{ + int i; + + /*----------------------------------------------------------------- + * Set acceptable default values for member vars. + *----------------------------------------------------------------*/ + m_nMAPVersionNumber = HDR_VERSION_NUMBER; + m_nBlockSize = HDR_DATA_BLOCK_SIZE; + + m_dCoordsys2DistUnits = 1.0; + m_nXMin = -1000000000; + m_nYMin = -1000000000; + m_nXMax = 1000000000; + m_nYMax = 1000000000; + m_bIntBoundsOverflow = FALSE; + + m_nFirstIndexBlock = 0; + m_nFirstGarbageBlock = 0; + m_nFirstToolBlock = 0; + + m_numPointObjects = 0; + m_numLineObjects = 0; + m_numRegionObjects = 0; + m_numTextObjects = 0; + m_nMaxCoordBufSize = 0; + + m_nDistUnitsCode = 7; // Meters + m_nMaxSpIndexDepth = 0; + m_nCoordPrecision = 3; // ??? 3 Digits of precision + m_nCoordOriginQuadrant = HDR_DEF_ORG_QUADRANT; // ??? N-E quadrant + m_nReflectXAxisCoord = HDR_DEF_REFLECTXAXIS; + m_nMaxObjLenArrayId = HDR_OBJ_LEN_ARRAY_SIZE-1; // See gabyObjLenArray[] + m_numPenDefs = 0; + m_numBrushDefs = 0; + m_numSymbolDefs = 0; + m_numFontDefs = 0; + m_numMapToolBlocks = 0; + + m_sProj.nProjId = 0; + m_sProj.nEllipsoidId = 0; + m_sProj.nUnitsId = 7; + m_sProj.nDatumId = 0; + m_XScale = 1000.0; // Default coord range (before SetCoordSysBounds()) + m_YScale = 1000.0; // will be [-1000000.000 .. 1000000.000] + m_XDispl = 0.0; + m_YDispl = 0.0; + m_XPrecision = 0.0; // not specified + m_YPrecision = 0.0; // not specified + + for(i=0; i<6; i++) + m_sProj.adProjParams[i] = 0.0; + + m_sProj.dDatumShiftX = 0.0; + m_sProj.dDatumShiftY = 0.0; + m_sProj.dDatumShiftZ = 0.0; + for(i=0; i<5; i++) + m_sProj.adDatumParams[i] = 0.0; + + m_sProj.nAffineFlag = 0; // Only in version 500 and up + m_sProj.nAffineUnits = 7; + m_sProj.dAffineParamA = 0.0; + m_sProj.dAffineParamB = 0.0; + m_sProj.dAffineParamC = 0.0; + m_sProj.dAffineParamD = 0.0; + m_sProj.dAffineParamE = 0.0; + m_sProj.dAffineParamF = 0.0; +} + + +/********************************************************************** + * TABMAPHeaderBlock::InitBlockFromData() + * + * Perform some initialization on the block after its binary data has + * been set or changed (or loaded from a file). + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPHeaderBlock::InitBlockFromData(GByte *pabyBuf, + int nBlockSize, int nSizeUsed, + GBool bMakeCopy /* = TRUE */, + VSILFILE *fpSrc /* = NULL */, + int nOffset /* = 0 */) +{ + int i, nStatus; + GInt32 nMagicCookie; + + /*----------------------------------------------------------------- + * First of all, we must call the base class' InitBlockFromData() + *----------------------------------------------------------------*/ + nStatus = TABRawBinBlock::InitBlockFromData(pabyBuf, + nBlockSize, nSizeUsed, + bMakeCopy, + fpSrc, nOffset); + if (nStatus != 0) + return nStatus; + + /*----------------------------------------------------------------- + * Validate block type + * Header blocks have a magic cookie at byte 0x100 + *----------------------------------------------------------------*/ + GotoByteInBlock(0x100); + nMagicCookie = ReadInt32(); + if (nMagicCookie != HDR_MAGIC_COOKIE) + { + CPLError(CE_Failure, CPLE_FileIO, + "ReadFromFile(): Invalid Magic Cookie: got %d expected %d", + nMagicCookie, HDR_MAGIC_COOKIE); + CPLFree(m_pabyBuf); + m_pabyBuf = NULL; + return -1; + } + + /*----------------------------------------------------------------- + * Init member variables + * Instead of having over 30 get/set methods, we'll make all data + * members public and we will initialize them here. + * For this reason, this class should be used with care. + *----------------------------------------------------------------*/ + GotoByteInBlock(0x104); + m_nMAPVersionNumber = ReadInt16(); + m_nBlockSize = ReadInt16(); + + m_dCoordsys2DistUnits = ReadDouble(); + m_nXMin = ReadInt32(); + m_nYMin = ReadInt32(); + m_nXMax = ReadInt32(); + m_nYMax = ReadInt32(); + if( m_nXMin > m_nXMax || m_nYMin > m_nYMax ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Reading corrupted MBR from .map header"); + CPLErrorReset(); + } + + GotoByteInBlock(0x130); // Skip 16 unknown bytes + + m_nFirstIndexBlock = ReadInt32(); + m_nFirstGarbageBlock = ReadInt32(); + m_nFirstToolBlock = ReadInt32(); + + m_numPointObjects = ReadInt32(); + m_numLineObjects = ReadInt32(); + m_numRegionObjects = ReadInt32(); + m_numTextObjects = ReadInt32(); + m_nMaxCoordBufSize = ReadInt32(); + + GotoByteInBlock(0x15e); // Skip 14 unknown bytes + + m_nDistUnitsCode = ReadByte(); + m_nMaxSpIndexDepth = ReadByte(); + m_nCoordPrecision = ReadByte(); + m_nCoordOriginQuadrant = ReadByte(); + m_nReflectXAxisCoord = ReadByte(); + m_nMaxObjLenArrayId = ReadByte(); // See gabyObjLenArray[] + m_numPenDefs = ReadByte(); + m_numBrushDefs = ReadByte(); + m_numSymbolDefs = ReadByte(); + m_numFontDefs = ReadByte(); + m_numMapToolBlocks = ReadInt16(); + + /* DatumId was never set (always 0) until MapInfo 7.8. See bug 910 + * MAP Version Number is 500 in this case. + */ + if (m_nMAPVersionNumber >= 500) + m_sProj.nDatumId = ReadInt16(); + else + { + ReadInt16(); // Skip. + m_sProj.nDatumId = 0; + } + ReadByte(); // Skip unknown byte + m_sProj.nProjId = ReadByte(); + m_sProj.nEllipsoidId = ReadByte(); + m_sProj.nUnitsId = ReadByte(); + m_XScale = ReadDouble(); + m_YScale = ReadDouble(); + m_XDispl = ReadDouble(); + m_YDispl = ReadDouble(); + + /* In V.100 files, the scale and displacement do not appear to be set. + * we'll use m_nCoordPrecision to define the scale factor instead. + */ + if (m_nMAPVersionNumber <= 100) + { + m_XScale = m_YScale = pow(10.0, m_nCoordPrecision); + m_XDispl = m_YDispl = 0.0; + } + + for(i=0; i<6; i++) + m_sProj.adProjParams[i] = ReadDouble(); + + m_sProj.dDatumShiftX = ReadDouble(); + m_sProj.dDatumShiftY = ReadDouble(); + m_sProj.dDatumShiftZ = ReadDouble(); + for(i=0; i<5; i++) + { + /* In V.200 files, the next 5 datum params are unused and they + * sometimes contain junk bytes... in this case we set adDatumParams[] + * to 0 for the rest of the lib to be happy. + */ + m_sProj.adDatumParams[i] = ReadDouble(); + if (m_nMAPVersionNumber <= 200) + m_sProj.adDatumParams[i] = 0.0; + } + + m_sProj.nAffineFlag = 0; + if (m_nMAPVersionNumber >= 500 && m_nSizeUsed > 512) + { + // Read Affine parameters A,B,C,D,E,F + // only if version 500+ and block is larger than 512 bytes + int nInUse = ReadByte(); + if (nInUse) + { + m_sProj.nAffineFlag = 1; + m_sProj.nAffineUnits = ReadByte(); + GotoByteInBlock(0x0208); // Skip unused bytes + m_sProj.dAffineParamA = ReadDouble(); + m_sProj.dAffineParamB = ReadDouble(); + m_sProj.dAffineParamC = ReadDouble(); + m_sProj.dAffineParamD = ReadDouble(); + m_sProj.dAffineParamE = ReadDouble(); + m_sProj.dAffineParamF = ReadDouble(); + } + } + + UpdatePrecision(); + + return 0; +} + + +/********************************************************************** + * TABMAPHeaderBlock::Int2Coordsys() + * + * Convert from long integer (internal) to coordinates system units + * as defined in the file's coordsys clause. + * + * Note that the false easting/northing and the conversion factor from + * datum to coordsys units are not included in the calculation. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPHeaderBlock::Int2Coordsys(GInt32 nX, GInt32 nY, + double &dX, double &dY) +{ + if (m_pabyBuf == NULL) + return -1; + + // For some obscure reason, some guy decided that it would be + // more fun to be able to define our own origin quadrant! + // + // In version 100 .tab files (version 400 .map), it is possible to have + // a quadrant 0 and it should be treated the same way as quadrant 3 + + if (m_nCoordOriginQuadrant==2 || m_nCoordOriginQuadrant==3 || + m_nCoordOriginQuadrant==0 ) + dX = -1.0 * (nX + m_XDispl) / m_XScale; + else + dX = (nX - m_XDispl) / m_XScale; + + if (m_nCoordOriginQuadrant==3 || m_nCoordOriginQuadrant==4|| + m_nCoordOriginQuadrant==0) + dY = -1.0 * (nY + m_YDispl) / m_YScale; + else + dY = (nY - m_YDispl) / m_YScale; + + // Round coordinates to the desired precision + if (m_XPrecision > 0 && m_YPrecision > 0) + { + dX = round(dX*m_XPrecision)/m_XPrecision; + dY = round(dY*m_YPrecision)/m_YPrecision; + } +//printf("Int2Coordsys: (%d, %d) -> (%.10g, %.10g)\n", nX, nY, dX, dY); + + return 0; +} + +/********************************************************************** + * TABMAPHeaderBlock::Coordsys2Int() + * + * Convert from coordinates system units as defined in the file's + * coordsys clause to long integer (internal) coordinates. + * + * Note that the false easting/northing and the conversion factor from + * datum to coordsys units are not included in the calculation. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPHeaderBlock::Coordsys2Int(double dX, double dY, + GInt32 &nX, GInt32 &nY, + GBool bIgnoreOverflow /*=FALSE*/) +{ + if (m_pabyBuf == NULL) + return -1; + + // For some obscure reason, some guy decided that it would be + // more fun to be able to define our own origin quadrant! + // + // In version 100 .tab files (version 400 .map), it is possible to have + // a quadrant 0 and it should be treated the same way as quadrant 3 + + /*----------------------------------------------------------------- + * NOTE: double values must be used here, the limit of integer value + * have been reached some times due to the very big numbers used here. + *----------------------------------------------------------------*/ + double dTempX, dTempY; + + if (m_nCoordOriginQuadrant==2 || m_nCoordOriginQuadrant==3 || + m_nCoordOriginQuadrant==0 ) + dTempX = (double)(-1.0*dX*m_XScale - m_XDispl); + else + dTempX = (double)(dX*m_XScale + m_XDispl); + + if (m_nCoordOriginQuadrant==3 || m_nCoordOriginQuadrant==4 || + m_nCoordOriginQuadrant==0 ) + dTempY = (double)(-1.0*dY*m_YScale - m_YDispl); + else + dTempY = (double)(dY*m_YScale + m_YDispl); + + /*----------------------------------------------------------------- + * Make sure we'll never output coordinates outside of the valid + * integer coordinates range: (-1e9, -1e9) - (1e9, 1e9) + * Integer coordinates outside of that range will confuse MapInfo. + *----------------------------------------------------------------*/ + GBool bIntBoundsOverflow = FALSE; + if (dTempX < -1000000000) + { + dTempX = -1000000000; + bIntBoundsOverflow = TRUE; + } + if (dTempX > 1000000000) + { + dTempX = 1000000000; + bIntBoundsOverflow = TRUE; + } + if (dTempY < -1000000000) + { + dTempY = -1000000000; + bIntBoundsOverflow = TRUE; + } + if (dTempY > 1000000000) + { + dTempY = 1000000000; + bIntBoundsOverflow = TRUE; + } + + nX = (GInt32) ROUND_INT(dTempX); + nY = (GInt32) ROUND_INT(dTempY); + + if (bIntBoundsOverflow && !bIgnoreOverflow) + { + m_bIntBoundsOverflow = TRUE; +#ifdef DEBUG + CPLError(CE_Warning, TAB_WarningBoundsOverflow, + "Integer bounds overflow: (%f, %f) -> (%d, %d)\n", + dX, dY, nX, nY); +#endif + } + + return 0; +} + +/********************************************************************** + * TABMAPHeaderBlock::ComprInt2Coordsys() + * + * Convert from compressed integer (internal) to coordinates system units + * as defined in the file's coordsys clause. + * The difference between long integer and compressed integer coords is + * that compressed coordinates are scaled displacement relative to an + * object centroid. + * + * Note that the false easting/northing and the conversion factor from + * datum to coordsys units are not included in the calculation. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPHeaderBlock::ComprInt2Coordsys(GInt32 nCenterX, GInt32 nCenterY, + int nDeltaX, int nDeltaY, + double &dX, double &dY) +{ + if (m_pabyBuf == NULL) + return -1; + + return Int2Coordsys(nCenterX+nDeltaX, nCenterY+nDeltaY, dX, dY); +} + + +/********************************************************************** + * TABMAPHeaderBlock::Int2CoordsysDist() + * + * Convert a pair of X and Y size (or distance) value from long integer + * (internal) to coordinates system units as defined in the file's + * coordsys clause. + * + * The difference with Int2Coordsys() is that this function only applies + * the scaling factor: it does not apply the displacement. + * + * Since the calculations on the X and Y values are independent, either + * one can be omitted (i.e. passed as 0) + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPHeaderBlock::Int2CoordsysDist(GInt32 nX, GInt32 nY, + double &dX, double &dY) +{ + if (m_pabyBuf == NULL) + return -1; + + dX = nX / m_XScale; + dY = nY / m_YScale; + + return 0; +} + +/********************************************************************** + * TABMAPHeaderBlock::Coordsys2IntDist() + * + * Convert a pair of X and Y size (or distance) values from coordinates + * system units as defined in the file's coordsys clause to long integer + * (internal) coordinates. + * + * The difference with Coordsys2Int() is that this function only applies + * the scaling factor: it does not apply the displacement. + * + * Since the calculations on the X and Y values are independent, either + * one can be omitted (i.e. passed as 0) + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPHeaderBlock::Coordsys2IntDist(double dX, double dY, + GInt32 &nX, GInt32 &nY) +{ + if (m_pabyBuf == NULL) + return -1; + + nX = (GInt32)(dX*m_XScale); + nY = (GInt32)(dY*m_YScale); + + return 0; +} + + +/********************************************************************** + * TABMAPHeaderBlock::SetCoordsysBounds() + * + * Take projection coordinates bounds of the newly created dataset and + * compute new values for the X/Y Scales and X/Y displacement. + * + * This function must be called after creating a new dataset and before any + * of the coordinates conversion functions can be used. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPHeaderBlock::SetCoordsysBounds(double dXMin, double dYMin, + double dXMax, double dYMax) +{ +//printf("SetCoordsysBounds(%10g, %10g, %10g, %10g)\n", dXMin, dYMin, dXMax, dYMax); + /*----------------------------------------------------------------- + * Check for 0-width or 0-height bounds + *----------------------------------------------------------------*/ + if (dXMax == dXMin) + { + dXMin -= 1.0; + dXMax += 1.0; + } + + if (dYMax == dYMin) + { + dYMin -= 1.0; + dYMax += 1.0; + } + + /*----------------------------------------------------------------- + * X and Y scales are used to map coordsys coordinates to integer + * internal coordinates. We want to find the scale and displacement + * values that will result in an integer coordinate range of + * (-1e9, -1e9) - (1e9, 1e9) + * + * Note that we ALWAYS generate datasets with the OriginQuadrant = 1 + * so that we avoid reverted X/Y axis complications, etc. + *----------------------------------------------------------------*/ + m_XScale = 2e9 / (dXMax - dXMin); + m_YScale = 2e9 / (dYMax - dYMin); + + m_XDispl = -1.0 * m_XScale * (dXMax + dXMin) / 2; + m_YDispl = -1.0 * m_YScale * (dYMax + dYMin) / 2; + + m_nXMin = -1000000000; + m_nYMin = -1000000000; + m_nXMax = 1000000000; + m_nYMax = 1000000000; + + UpdatePrecision(); + + return 0; +} + +/********************************************************************** + * TABMAPHeaderBlock::GetMapObjectSize() + * + * Return the size of the object body for the specified object type. + * The value is looked up in the first 256 bytes of the header. + **********************************************************************/ +int TABMAPHeaderBlock::GetMapObjectSize(int nObjType) +{ + if (m_pabyBuf == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Block has not been initialized yet!"); + return -1; + } + + if (nObjType < 0 || nObjType > 255) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Invalid object type %d", nObjType); + return -1; + } + + // Byte 0x80 is set for objects that have coordinates inside type 3 blocks + return (m_pabyBuf[nObjType] & 0x7f); +} + +/********************************************************************** + * TABMAPHeaderBlock::MapObjectUsesCoordBlock() + * + * Return TRUE if the specified map object type has coordinates stored + * inside type 3 coordinate blocks. + * The info is looked up in the first 256 bytes of the header. + **********************************************************************/ +GBool TABMAPHeaderBlock::MapObjectUsesCoordBlock(int nObjType) +{ + if (m_pabyBuf == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Block has not been initialized yet!"); + return FALSE; + } + + if (nObjType < 0 || nObjType > 255) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Invalid object type %d", nObjType); + return FALSE; + } + + // Byte 0x80 is set for objects that have coordinates inside type 3 blocks + + return ((m_pabyBuf[nObjType] & 0x80) != 0) ? TRUE: FALSE; +} + + +/********************************************************************** + * TABMAPHeaderBlock::GetProjInfo() + * + * Fill the psProjInfo structure with the projection parameters previously + * read from this header block. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPHeaderBlock::GetProjInfo(TABProjInfo *psProjInfo) +{ + if (m_pabyBuf == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Block has not been initialized yet!"); + return -1; + } + + if (psProjInfo) + *psProjInfo = m_sProj; + + return 0; +} + +/********************************************************************** + * TABMAPHeaderBlock::SetProjInfo() + * + * Set the projection parameters for this dataset. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPHeaderBlock::SetProjInfo(TABProjInfo *psProjInfo) +{ + if (m_pabyBuf == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Block has not been initialized yet!"); + return -1; + } + + if (psProjInfo) + m_sProj = *psProjInfo; + + return 0; +} + + +/********************************************************************** + * TABMAPHeaderBlock::CommitToFile() + * + * Commit the current state of the binary block to the file to which + * it has been previously attached. + * + * This method makes sure all values are properly set in the header + * block buffer and then calls TABRawBinBlock::CommitToFile() to do + * the actual writing to disk. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPHeaderBlock::CommitToFile() +{ + int i, nStatus = 0; + + if ( m_pabyBuf == NULL || m_nBlockSize != HDR_DATA_BLOCK_SIZE ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRawBinBlock::CommitToFile(): Block has not been initialized yet!"); + return -1; + } + + /*----------------------------------------------------------------- + * Reconstruct header to make sure it is in sync with members variables. + *----------------------------------------------------------------*/ + GotoByteInBlock(0x000); + WriteBytes(HDR_OBJ_LEN_ARRAY_SIZE, gabyObjLenArray); + m_nMaxObjLenArrayId = HDR_OBJ_LEN_ARRAY_SIZE-1; + + GotoByteInBlock(0x100); + WriteInt32(HDR_MAGIC_COOKIE); + + if (m_sProj.nAffineFlag && m_nMAPVersionNumber<500) + { + // Must be at least version 500 to support affine params + // Default value for HDR_VERSION_NUMBER is 500 so this error should + // never happen unless the caller changed the value, in which case they + // deserve to get a failure + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRawBinBlock::CommitToFile(): .MAP version 500 or more is " + "required for affine projection parameter support."); + return -1; + } + + WriteInt16(m_nMAPVersionNumber); + + WriteInt16(HDR_DATA_BLOCK_SIZE); + + WriteDouble(m_dCoordsys2DistUnits); + WriteInt32(m_nXMin); + WriteInt32(m_nYMin); + WriteInt32(m_nXMax); + WriteInt32(m_nYMax); + if( m_nXMin > m_nXMax || m_nYMin > m_nYMax ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Writing corrupted MBR into .map header"); + } + + WriteZeros(16); // ??? + + WriteInt32(m_nFirstIndexBlock); + WriteInt32(m_nFirstGarbageBlock); + WriteInt32(m_nFirstToolBlock); + + WriteInt32(m_numPointObjects); + WriteInt32(m_numLineObjects); + WriteInt32(m_numRegionObjects); + WriteInt32(m_numTextObjects); + WriteInt32(m_nMaxCoordBufSize); + + WriteZeros(14); // ??? + + WriteByte(m_nDistUnitsCode); + WriteByte(m_nMaxSpIndexDepth); + WriteByte(m_nCoordPrecision); + WriteByte(m_nCoordOriginQuadrant); + WriteByte(m_nReflectXAxisCoord); + WriteByte(m_nMaxObjLenArrayId); // See gabyObjLenArray[] + WriteByte(m_numPenDefs); + WriteByte(m_numBrushDefs); + WriteByte(m_numSymbolDefs); + WriteByte(m_numFontDefs); + WriteInt16(m_numMapToolBlocks); + + WriteInt16(m_sProj.nDatumId); + WriteZeros(1); // ??? + + WriteByte(m_sProj.nProjId); + WriteByte(m_sProj.nEllipsoidId); + WriteByte(m_sProj.nUnitsId); + WriteDouble(m_XScale); + WriteDouble(m_YScale); + WriteDouble(m_XDispl); + WriteDouble(m_YDispl); + + for(i=0; i<6; i++) + WriteDouble(m_sProj.adProjParams[i]); + + WriteDouble(m_sProj.dDatumShiftX); + WriteDouble(m_sProj.dDatumShiftY); + WriteDouble(m_sProj.dDatumShiftZ); + for(i=0; i<5; i++) + WriteDouble(m_sProj.adDatumParams[i]); + + if (m_sProj.nAffineFlag) + { + WriteByte(1); // In Use Flag + WriteByte(m_sProj.nAffineUnits); + WriteZeros(6); + WriteDouble(m_sProj.dAffineParamA); + WriteDouble(m_sProj.dAffineParamB); + WriteDouble(m_sProj.dAffineParamC); + WriteDouble(m_sProj.dAffineParamD); + WriteDouble(m_sProj.dAffineParamE); + WriteDouble(m_sProj.dAffineParamF); + + WriteZeros(456); // Pad rest of block with zeros (Bounds info here ?) + } + + /*----------------------------------------------------------------- + * OK, call the base class to write the block to disk. + *----------------------------------------------------------------*/ + if (nStatus == 0) + { +#ifdef DEBUG_VERBOSE + CPLDebug("MITAB", "Commiting HEADER block to offset %d", m_nFileOffset); +#endif + nStatus = TABRawBinBlock::CommitToFile(); + } + + return nStatus; +} + +/********************************************************************** + * TABMAPHeaderBlock::InitNewBlock() + * + * Initialize a newly created block so that it knows to which file it + * is attached, its block size, etc . and then perform any specific + * initialization for this block type, including writing a default + * block header, etc. and leave the block ready to receive data. + * + * This is an alternative to calling ReadFromFile() or InitBlockFromData() + * that puts the block in a stable state without loading any initial + * data in it. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPHeaderBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize, + int nFileOffset /* = 0*/) +{ + /*----------------------------------------------------------------- + * Start with the default initialisation + *----------------------------------------------------------------*/ + if ( TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0) + return -1; + + /*----------------------------------------------------------------- + * Set acceptable default values for member vars. + *----------------------------------------------------------------*/ + InitMembersWithDefaultValues(); + + /*----------------------------------------------------------------- + * And Set the map object length array in the buffer... + *----------------------------------------------------------------*/ + if (m_eAccess != TABRead) + { + GotoByteInBlock(0x000); + WriteBytes(HDR_OBJ_LEN_ARRAY_SIZE, gabyObjLenArray); + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPHeaderBlock::UpdatePrecision() + * + * Update x and y maximum achievable precision given current scales + * (m_XScale and m_YScale) + **********************************************************************/ +void TABMAPHeaderBlock::UpdatePrecision() +{ + m_XPrecision = pow(10.0, round(log10(m_XScale))); + m_YPrecision = pow(10.0, round(log10(m_YScale))); +} + +/********************************************************************** + * TABMAPHeaderBlock::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABMAPHeaderBlock::Dump(FILE *fpOut /*=NULL*/) +{ + int i; + + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABMAPHeaderBlock::Dump() -----\n"); + + if (m_pabyBuf == NULL) + { + fprintf(fpOut, "Block has not been initialized yet."); + } + else + { + fprintf(fpOut,"Version %d header block.\n", m_nMAPVersionNumber); + fprintf(fpOut," m_nBlockSize = %d\n", m_nBlockSize); + fprintf(fpOut," m_nFirstIndexBlock = %d\n", m_nFirstIndexBlock); + fprintf(fpOut," m_nFirstGarbageBlock = %d\n", m_nFirstGarbageBlock); + fprintf(fpOut," m_nFirstToolBlock = %d\n", m_nFirstToolBlock); + fprintf(fpOut," m_numPointObjects = %d\n", m_numPointObjects); + fprintf(fpOut," m_numLineObjects = %d\n", m_numLineObjects); + fprintf(fpOut," m_numRegionObjects = %d\n", m_numRegionObjects); + fprintf(fpOut," m_numTextObjects = %d\n", m_numTextObjects); + fprintf(fpOut," m_nMaxCoordBufSize = %d\n", m_nMaxCoordBufSize); + + fprintf(fpOut,"\n"); + fprintf(fpOut," m_dCoordsys2DistUnits = %g\n", m_dCoordsys2DistUnits); + fprintf(fpOut," m_nXMin = %d\n", m_nXMin); + fprintf(fpOut," m_nYMin = %d\n", m_nYMin); + fprintf(fpOut," m_nXMax = %d\n", m_nXMax); + fprintf(fpOut," m_nYMax = %d\n", m_nYMax); + fprintf(fpOut," m_XScale = %g\n", m_XScale); + fprintf(fpOut," m_YScale = %g\n", m_YScale); + fprintf(fpOut," m_XDispl = %g\n", m_XDispl); + fprintf(fpOut," m_YDispl = %g\n", m_YDispl); + + fprintf(fpOut,"\n"); + fprintf(fpOut," m_nDistUnistCode = %d\n", m_nDistUnitsCode); + fprintf(fpOut," m_nMaxSpIndexDepth = %d\n", m_nMaxSpIndexDepth); + fprintf(fpOut," m_nCoordPrecision = %d\n", m_nCoordPrecision); + fprintf(fpOut," m_nCoordOriginQuadrant= %d\n",m_nCoordOriginQuadrant); + fprintf(fpOut," m_nReflecXAxisCoord = %d\n", m_nReflectXAxisCoord); + fprintf(fpOut," m_nMaxObjLenArrayId = %d\n", m_nMaxObjLenArrayId); + fprintf(fpOut," m_numPenDefs = %d\n", m_numPenDefs); + fprintf(fpOut," m_numBrushDefs = %d\n", m_numBrushDefs); + fprintf(fpOut," m_numSymbolDefs = %d\n", m_numSymbolDefs); + fprintf(fpOut," m_numFontDefs = %d\n", m_numFontDefs); + fprintf(fpOut," m_numMapToolBlocks = %d\n", m_numMapToolBlocks); + + fprintf(fpOut,"\n"); + fprintf(fpOut," m_sProj.nDatumId = %d\n", m_sProj.nDatumId); + fprintf(fpOut," m_sProj.nProjId = %d\n", (int)m_sProj.nProjId); + fprintf(fpOut," m_sProj.nEllipsoidId = %d\n", + (int)m_sProj.nEllipsoidId); + fprintf(fpOut," m_sProj.nUnitsId = %d\n", (int)m_sProj.nUnitsId); + fprintf(fpOut," m_sProj.adProjParams ="); + for(i=0; i<6; i++) + fprintf(fpOut, " %g", m_sProj.adProjParams[i]); + fprintf(fpOut,"\n"); + + fprintf(fpOut," m_sProj.dDatumShiftX = %.15g\n", m_sProj.dDatumShiftX); + fprintf(fpOut," m_sProj.dDatumShiftY = %.15g\n", m_sProj.dDatumShiftY); + fprintf(fpOut," m_sProj.dDatumShiftZ = %.15g\n", m_sProj.dDatumShiftZ); + fprintf(fpOut," m_sProj.adDatumParams ="); + for(i=0; i<5; i++) + fprintf(fpOut, " %.15g", m_sProj.adDatumParams[i]); + fprintf(fpOut,"\n"); + + // Dump array of map object lengths... optional + if (FALSE) + { + fprintf(fpOut, "-- Header bytes 00-FF: Array of map object lenghts --\n"); + for(i=0; i<256; i++) + { + fprintf(fpOut, "0x%2.2x", (int)m_pabyBuf[i]); + if (i != 255) + fprintf(fpOut, ","); + if ((i+1)%16 == 0) + fprintf(fpOut, "\n"); + } + } + + } + + fflush(fpOut); +} + +#endif // DEBUG diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapindexblock.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapindexblock.cpp new file mode 100644 index 000000000..62bc0abae --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapindexblock.cpp @@ -0,0 +1,1582 @@ +/********************************************************************** + * $Id: mitab_mapindexblock.cpp,v 1.14 2010-07-07 19:00:15 aboudreault Exp $ + * + * Name: mitab_mapindexblock.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABMAPIndexBlock class used to handle + * reading/writing of the .MAP files' index blocks + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999, 2000, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_mapindexblock.cpp,v $ + * Revision 1.14 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.13 2007-04-02 18:58:03 dmorissette + * Fixed uninitialized variable warning in PickSeedsForSplit() + * + * Revision 1.12 2006/12/14 20:03:02 dmorissette + * Improve write performance by keeping track of changes to index blocks + * and committing to disk only if modified (related to bug 1585) + * + * Revision 1.11 2006/11/28 18:49:08 dmorissette + * Completed changes to split TABMAPObjectBlocks properly and produce an + * optimal spatial index (bug 1585) + * + * Revision 1.10 2006/11/20 20:05:58 dmorissette + * First pass at improving generation of spatial index in .map file (bug 1585) + * New methods for insertion and splittung in the spatial index are done. + * Also implemented a method to dump the spatial index to .mif/.mid + * Still need to implement splitting of TABMapObjectBlock to get optimal + * results. + * + * Revision 1.9 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.8 2001/09/14 03:23:55 warmerda + * Substantial upgrade to support spatial queries using spatial indexes + * + * Revision 1.7 2000/05/23 17:02:54 daniel + * Removed unused variables + * + * Revision 1.6 2000/05/19 06:45:10 daniel + * Modified generation of spatial index to split index nodes and produce a + * more balanced tree. + * + * Revision 1.5 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.4 1999/10/01 03:46:31 daniel + * Added ReadAllEntries() and more complete Dump() for debugging files + * + * Revision 1.3 1999/09/29 04:23:51 daniel + * Fixed typo in GetMBR() + * + * Revision 1.2 1999/09/26 14:59:37 daniel + * Implemented write support + * + * Revision 1.1 1999/07/12 04:18:25 daniel + * Initial checkin + * + **********************************************************************/ + +#include "mitab.h" + +/*===================================================================== + * class TABMAPIndexBlock + *====================================================================*/ + + +/********************************************************************** + * TABMAPIndexBlock::TABMAPIndexBlock() + * + * Constructor. + **********************************************************************/ +TABMAPIndexBlock::TABMAPIndexBlock(TABAccess eAccessMode /*= TABRead*/): + TABRawBinBlock(eAccessMode, TRUE) +{ + m_numEntries = 0; + + m_nMinX = 1000000000; + m_nMinY = 1000000000; + m_nMaxX = -1000000000; + m_nMaxY = -1000000000; + + m_poCurChild = NULL; + m_nCurChildIndex = -1; + m_poParentRef = NULL; + m_poBlockManagerRef = NULL; +} + +/********************************************************************** + * TABMAPIndexBlock::~TABMAPIndexBlock() + * + * Destructor. + **********************************************************************/ +TABMAPIndexBlock::~TABMAPIndexBlock() +{ + UnsetCurChild(); +} + +/********************************************************************** + * TABMAPIndexBlock::UnsetCurChild() + **********************************************************************/ + +void TABMAPIndexBlock::UnsetCurChild() +{ + if (m_poCurChild) + { + if (m_eAccess == TABWrite || m_eAccess == TABReadWrite) + m_poCurChild->CommitToFile(); + delete m_poCurChild; + m_poCurChild = NULL; + } + m_nCurChildIndex = -1; +} + +/********************************************************************** + * TABMAPIndexBlock::InitBlockFromData() + * + * Perform some initialization on the block after its binary data has + * been set or changed (or loaded from a file). + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPIndexBlock::InitBlockFromData(GByte *pabyBuf, + int nBlockSize, int nSizeUsed, + GBool bMakeCopy /* = TRUE */, + VSILFILE *fpSrc /* = NULL */, + int nOffset /* = 0 */) +{ + int nStatus; + + /*----------------------------------------------------------------- + * First of all, we must call the base class' InitBlockFromData() + *----------------------------------------------------------------*/ + nStatus = TABRawBinBlock::InitBlockFromData(pabyBuf, + nBlockSize, nSizeUsed, + bMakeCopy, + fpSrc, nOffset); + if (nStatus != 0) + return nStatus; + + /*----------------------------------------------------------------- + * Validate block type + *----------------------------------------------------------------*/ + if (m_nBlockType != TABMAP_INDEX_BLOCK) + { + CPLError(CE_Failure, CPLE_FileIO, + "InitBlockFromData(): Invalid Block Type: got %d expected %d", + m_nBlockType, TABMAP_INDEX_BLOCK); + CPLFree(m_pabyBuf); + m_pabyBuf = NULL; + return -1; + } + + /*----------------------------------------------------------------- + * Init member variables + *----------------------------------------------------------------*/ + GotoByteInBlock(0x002); + m_numEntries = ReadInt16(); + + if (m_numEntries > 0) + ReadAllEntries(); + + return 0; +} + +/********************************************************************** + * TABMAPIndexBlock::CommitToFile() + * + * Commit the current state of the binary block to the file to which + * it has been previously attached. + * + * This method makes sure all values are properly set in the map object + * block header and then calls TABRawBinBlock::CommitToFile() to do + * the actual writing to disk. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPIndexBlock::CommitToFile() +{ + int nStatus = 0; + + if ( m_pabyBuf == NULL ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "CommitToFile(): Block has not been initialized yet!"); + return -1; + } + + /*----------------------------------------------------------------- + * Commit child first + *----------------------------------------------------------------*/ + if (m_poCurChild) + { + if (m_poCurChild->CommitToFile() != 0) + return -1; + } + + /*----------------------------------------------------------------- + * Nothing to do here if block has not been modified + *----------------------------------------------------------------*/ + if (!m_bModified) + return 0; + + /*----------------------------------------------------------------- + * Make sure 4 bytes block header is up to date. + *----------------------------------------------------------------*/ + GotoByteInBlock(0x000); + + WriteInt16(TABMAP_INDEX_BLOCK); // Block type code + WriteInt16((GInt16)m_numEntries); + + nStatus = CPLGetLastErrorNo(); + + /*----------------------------------------------------------------- + * Loop through all entries, writing each of them, and calling + * CommitToFile() (recursively) on any child index entries we may + * encounter. + *----------------------------------------------------------------*/ + for(int i=0; nStatus == 0 && i 4+(20*m_numEntries) ) + { + // End of BLock + return -1; + } + + psEntry->XMin = ReadInt32(); + psEntry->YMin = ReadInt32(); + psEntry->XMax = ReadInt32(); + psEntry->YMax = ReadInt32(); + psEntry->nBlockPtr = ReadInt32(); + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPIndexBlock::ReadAllEntries() + * + * Init the block by reading all entries from the data block. + * + * Returns 0 if succesful or -1 on error. + **********************************************************************/ +int TABMAPIndexBlock::ReadAllEntries() +{ + CPLAssert(m_numEntries <= TAB_MAX_ENTRIES_INDEX_BLOCK); + if (m_numEntries == 0) + return 0; + + if (GotoByteInBlock( 0x004 ) != 0) + return -1; + + for(int i=0; iXMin); + WriteInt32(psEntry->YMin); + WriteInt32(psEntry->XMax); + WriteInt32(psEntry->YMax); + WriteInt32(psEntry->nBlockPtr); + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPIndexBlock::GetNumFreeEntries() + * + * Return the number of available entries in this block. + * + * __TODO__ This function could eventually be improved to search + * children leaves as well. + **********************************************************************/ +int TABMAPIndexBlock::GetNumFreeEntries() +{ + /* nMaxEntries = (m_nBlockSize-4)/20;*/ + + return (TAB_MAX_ENTRIES_INDEX_BLOCK - m_numEntries); +} + +/********************************************************************** + * TABMAPIndexBlock::GetEntry() + * + * Fetch a reference to the requested entry. + * + * @param iIndex index of entry, must be from 0 to GetNumEntries()-1. + * + * @return a reference to the internal copy of the entry, or NULL if out + * of range. + **********************************************************************/ +TABMAPIndexEntry *TABMAPIndexBlock::GetEntry( int iIndex ) +{ + if( iIndex < 0 || iIndex >= m_numEntries ) + return NULL; + + return m_asEntries + iIndex; +} + +/********************************************************************** + * TABMAPIndexBlock::GetCurMaxDepth() + * + * Return maximum depth in the currently loaded part of the index tree + **********************************************************************/ +int TABMAPIndexBlock::GetCurMaxDepth() +{ + if (m_poCurChild) + return m_poCurChild->GetCurMaxDepth() + 1; + + return 1; /* No current child... this node counts for one. */ +} + +/********************************************************************** + * TABMAPIndexBlock::GetMBR() + * + * Return the MBR for the current block. + **********************************************************************/ +void TABMAPIndexBlock::GetMBR(GInt32 &nXMin, GInt32 &nYMin, + GInt32 &nXMax, GInt32 &nYMax) +{ + nXMin = m_nMinX; + nYMin = m_nMinY; + nXMax = m_nMaxX; + nYMax = m_nMaxY; +} + +/********************************************************************** + * TABMAPIndexBlock::SetMBR() + * + **********************************************************************/ +void TABMAPIndexBlock::SetMBR(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax) +{ + m_nMinX = nXMin; + m_nMinY = nYMin; + m_nMaxX = nXMax; + m_nMaxY = nYMax; +} + +/********************************************************************** + * TABMAPIndexBlock::InsertEntry() + * + * Add a new entry to this index block. It is assumed that there is at + * least one free slot available, so if the block has to be split then it + * should have been done prior to calling this function. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPIndexBlock::InsertEntry(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax, + GInt32 nBlockPtr) +{ + if (m_eAccess != TABWrite && m_eAccess != TABReadWrite) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Failed adding index entry: File not opened for write access."); + return -1; + } + + if (GetNumFreeEntries() < 1) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Current Block Index is full, cannot add new entry."); + return -1; + } + + /*----------------------------------------------------------------- + * Update count of entries and store new entry. + *----------------------------------------------------------------*/ + m_numEntries++; + CPLAssert(m_numEntries <= TAB_MAX_ENTRIES_INDEX_BLOCK); + + m_asEntries[m_numEntries-1].XMin = nXMin; + m_asEntries[m_numEntries-1].YMin = nYMin; + m_asEntries[m_numEntries-1].XMax = nXMax; + m_asEntries[m_numEntries-1].YMax = nYMax; + m_asEntries[m_numEntries-1].nBlockPtr = nBlockPtr; + + m_bModified = TRUE; + + return 0; +} + +/********************************************************************** + * TABMAPIndexBlock::ChooseSubEntryForInsert() + * + * Select the entry in this index block in which the new entry should + * be inserted. The criteria used is to select the node whose MBR needs + * the least enlargement to include the new entry. We resolve ties by + * chosing the entry with the rectangle of smallest area. + * (This is the ChooseSubtree part of Guttman's "ChooseLeaf" algorithm.) + * + * Returns the index of the best candidate or -1 of node is empty. + **********************************************************************/ +int TABMAPIndexBlock::ChooseSubEntryForInsert(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax) +{ + GInt32 i, nBestCandidate=-1; + + double dOptimalAreaDiff=0; + + double dNewEntryArea = MITAB_AREA(nXMin, nYMin, nXMax, nYMax); + + for(i=0; i= m_asEntries[i].XMin && + nYMin >= m_asEntries[i].YMin && + nXMax <= m_asEntries[i].XMax && + nYMax <= m_asEntries[i].YMax ); + + if (bIsContained) + { + /* If new entry is fully contained in this entry then + * the area difference will be the difference between the area + * of the entry to insert and the area of m_asEntries[i] + * + * The diff value is negative in this case. + */ + dAreaDiff = dNewEntryArea - dAreaBefore; + } + else + { + /* Need to calculate the expanded MBR to calculate the area + * difference. + */ + GInt32 nXMin2, nYMin2, nXMax2, nYMax2; + nXMin2 = MIN(m_asEntries[i].XMin, nXMin); + nYMin2 = MIN(m_asEntries[i].YMin, nYMin); + nXMax2 = MAX(m_asEntries[i].XMax, nXMax); + nYMax2 = MAX(m_asEntries[i].YMax, nYMax); + + dAreaDiff = MITAB_AREA(nXMin2,nYMin2,nXMax2,nYMax2) - dAreaBefore; + } + + /* Is this a better candidate? + * Note, possible Optimization: In case of tie, we could to pick the + * candidate with the smallest area + */ + + if (/* No best candidate yet */ + (nBestCandidate == -1) + /* or current candidate is contained and best candidate is not contained */ + || (dAreaDiff < 0 && dOptimalAreaDiff >= 0) + /* or if both are either contained or not contained then use the one + * with the smallest area diff, which means maximum coverage in the case + * of contained rects, or minimum area increase when not contained + */ + || (((dOptimalAreaDiff < 0 && dAreaDiff < 0) || + (dOptimalAreaDiff > 0 && dAreaDiff > 0)) && + ABS(dAreaDiff) < ABS(dOptimalAreaDiff)) ) + { + nBestCandidate = i; + dOptimalAreaDiff = dAreaDiff; + } + + } + + return nBestCandidate; +} + +/********************************************************************** + * TABMAPIndexBlock::ChooseLeafForInsert() + * + * Recursively search the tree until we find the best leaf to + * contain the specified object MBR. + * + * Returns the nBlockPtr of the selected leaf node entry (should be a + * ref to a TABMAPObjectBlock) or -1 on error. + * + * After this call, m_poCurChild will be pointing at the selected child + * node, for use by later calls to UpdateLeafEntry() + **********************************************************************/ +GInt32 TABMAPIndexBlock::ChooseLeafForInsert(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax) +{ + GBool bFound = FALSE; + + if (m_numEntries < 0) + return -1; + + /*----------------------------------------------------------------- + * Look for the best candidate to contain the new entry + *----------------------------------------------------------------*/ + + // Make sure blocks currently in memory are written to disk. + // TODO: Could we avoid deleting m_poCurChild if it's already + // the best candidate for insert? + if (m_poCurChild) + { + m_poCurChild->CommitToFile(); + delete m_poCurChild; + m_poCurChild = NULL; + m_nCurChildIndex = -1; + } + + int nBestCandidate = ChooseSubEntryForInsert(nXMin,nYMin,nXMax,nYMax); + + CPLAssert(nBestCandidate != -1); + if (nBestCandidate == -1) + return -1; /* This should never happen! */ + + // Try to load corresponding child... if it fails then we are + // likely in a leaf node, so we'll add the new entry in the current + // node. + TABRawBinBlock *poBlock = NULL; + + // Prevent error message if referred block not committed yet. + CPLPushErrorHandler(CPLQuietErrorHandler); + + poBlock = TABCreateMAPBlockFromFile(m_fp, + m_asEntries[nBestCandidate].nBlockPtr, + 512, TRUE, TABReadWrite); + if (poBlock != NULL && poBlock->GetBlockClass() == TABMAP_INDEX_BLOCK) + { + m_poCurChild = (TABMAPIndexBlock*)poBlock; + poBlock = NULL; + m_nCurChildIndex = nBestCandidate; + m_poCurChild->SetParentRef(this); + m_poCurChild->SetMAPBlockManagerRef(m_poBlockManagerRef); + bFound = TRUE; + } + + if (poBlock) + delete poBlock; + + CPLPopErrorHandler(); + CPLErrorReset(); + + + if (bFound) + { + /*------------------------------------------------------------- + * Found a child leaf... pass the call to it. + *------------------------------------------------------------*/ + return m_poCurChild->ChooseLeafForInsert(nXMin, nYMin, nXMax, nYMax); + } + + /*------------------------------------------------------------- + * Found no child index node... we must be at the leaf level + * (leaf points at map object data blocks) so we return a ref + * to the TABMAPObjBlock for insertion + *------------------------------------------------------------*/ + return m_asEntries[nBestCandidate].nBlockPtr; +} + + +/********************************************************************** + * TABMAPIndexBlock::GetCurLeafEntryMBR() + * + * Get the MBR for specified nBlockPtr in the leaf at the end of the + * chain of m_poCurChild refs. + * + * This method requires that the chain of m_poCurChild refs already point + * to a leaf that contains the specified nBlockPtr, it is usually called + * right after ChooseLeafForInsert(). + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPIndexBlock::GetCurLeafEntryMBR(GInt32 nBlockPtr, + GInt32 &nXMin, GInt32 &nYMin, + GInt32 &nXMax, GInt32 &nYMax) +{ + if (m_poCurChild) + { + /* Pass the call down to current child */ + return m_poCurChild->GetCurLeafEntryMBR(nBlockPtr, + nXMin, nYMin, nXMax, nYMax); + } + + /* We're at the leaf level, look for the entry */ + for(int i=0; iUpdateLeafEntry(nBlockPtr, + nXMin, nYMin, nXMax, nYMax); + } + + /* We're at the leaf level, look for the entry to update */ + for(int i=0; iXMin != nXMin || + psEntry->YMin != nYMin || + psEntry->XMax != nXMax || + psEntry->YMax != nYMax ) + { + /* MBR changed. Update MBR of entry */ + psEntry->XMin = nXMin; + psEntry->YMin = nYMin; + psEntry->XMax = nXMax; + psEntry->YMax = nYMax; + + m_bModified = TRUE; + + /* Update MBR of this node and all parents */ + RecomputeMBR(); + } + + return 0; + } + } + + /* Not found! This should not happen if method is used properly. */ + CPLError(CE_Failure, CPLE_AssertionFailed, + "Entry to update not found in UpdateLeafEntry()!"); + return -1; +} + + +/********************************************************************** + * TABMAPIndexBlock::AddEntry() + * + * Recursively search the tree until we encounter the best leaf to + * contain the specified object MBR and add the new entry to it. + * + * In the even that the selected leaf node would be full, then it will be + * split and this split can propagate up to its parent, etc. + * + * If bAddInThisNodeOnly=TRUE, then the entry is added only locally and + * we do not try to update the child node. This is used when the parent + * of a node that is being splitted has to be updated. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPIndexBlock::AddEntry(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax, + GInt32 nBlockPtr, + GBool bAddInThisNodeOnly /*=FALSE*/) +{ + GBool bFound = FALSE; + + if (m_eAccess != TABWrite && m_eAccess != TABReadWrite) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Failed adding index entry: File not opened for write access."); + return -1; + } + + /*----------------------------------------------------------------- + * Look for the best candidate to contain the new entry + *----------------------------------------------------------------*/ + + /*----------------------------------------------------------------- + * If bAddInThisNodeOnly=TRUE then we add the entry only locally + * and do not need to look for the proper leaf to insert it. + *----------------------------------------------------------------*/ + if (bAddInThisNodeOnly) + bFound = TRUE; + + if (!bFound && m_numEntries > 0) + { + // Make sure blocks currently in memory are written to disk. + if (m_poCurChild) + { + m_poCurChild->CommitToFile(); + delete m_poCurChild; + m_poCurChild = NULL; + m_nCurChildIndex = -1; + } + + int nBestCandidate = ChooseSubEntryForInsert(nXMin,nYMin,nXMax,nYMax); + + CPLAssert(nBestCandidate != -1); + + if (nBestCandidate != -1) + { + // Try to load corresponding child... if it fails then we are + // likely in a leaf node, so we'll add the new entry in the current + // node. + TABRawBinBlock *poBlock = NULL; + + // Prevent error message if referred block not committed yet. + CPLPushErrorHandler(CPLQuietErrorHandler); + + poBlock = TABCreateMAPBlockFromFile(m_fp, + m_asEntries[nBestCandidate].nBlockPtr, + 512, TRUE, TABReadWrite); + if (poBlock != NULL && poBlock->GetBlockClass() == TABMAP_INDEX_BLOCK) + { + m_poCurChild = (TABMAPIndexBlock*)poBlock; + poBlock = NULL; + m_nCurChildIndex = nBestCandidate; + m_poCurChild->SetParentRef(this); + m_poCurChild->SetMAPBlockManagerRef(m_poBlockManagerRef); + bFound = TRUE; + } + + if (poBlock) + delete poBlock; + + CPLPopErrorHandler(); + CPLErrorReset(); + } + } + + if (bFound && !bAddInThisNodeOnly) + { + /*------------------------------------------------------------- + * Found a child leaf... pass the call to it. + *------------------------------------------------------------*/ + if (m_poCurChild->AddEntry(nXMin, nYMin, nXMax, nYMax, nBlockPtr) != 0) + return -1; + } + else + { + /*------------------------------------------------------------- + * Found no child to store new object... we're likely at the leaf + * level so we'll store new object in current node + *------------------------------------------------------------*/ + + /*------------------------------------------------------------- + * First thing to do is make sure that there is room for a new + * entry in this node, and to split it if necessary. + *------------------------------------------------------------*/ + if (GetNumFreeEntries() < 1) + { + if (m_poParentRef == NULL) + { + /*----------------------------------------------------- + * Splitting the root node adds one level to the tree, so + * after splitting we just redirect the call to the new + * child that's just been created. + *----------------------------------------------------*/ + if (SplitRootNode(nXMin, nYMin, nXMax, nYMax) != 0) + return -1; // Error happened and has already been reported + + CPLAssert(m_poCurChild); + return m_poCurChild->AddEntry(nXMin, nYMin, nXMax, nYMax, + nBlockPtr, TRUE); + } + else + { + /*----------------------------------------------------- + * Splitting a regular node + *----------------------------------------------------*/ + if (SplitNode(nXMin, nYMin, nXMax, nYMax) != 0) + return -1; + } + } + + if (InsertEntry(nXMin, nYMin, nXMax, nYMax, nBlockPtr) != 0) + return -1; + } + + /*----------------------------------------------------------------- + * Update current node MBR and the reference to it in our parent. + *----------------------------------------------------------------*/ + RecomputeMBR(); + + return 0; +} + +/********************************************************************** + * TABMAPIndexBlock::ComputeAreaDiff() + * + * (static method, also used by the TABMAPObjBlock class) + * + * Compute the area difference between two MBRs. Used in the SplitNode + * algorithm to decide to which of the two nodes an entry should be added. + * + * The returned AreaDiff value is positive if NodeMBR has to be enlarged + * and negative if new Entry is fully contained in the NodeMBR. + **********************************************************************/ +double TABMAPIndexBlock::ComputeAreaDiff(GInt32 nNodeXMin, GInt32 nNodeYMin, + GInt32 nNodeXMax, GInt32 nNodeYMax, + GInt32 nEntryXMin, GInt32 nEntryYMin, + GInt32 nEntryXMax, GInt32 nEntryYMax) +{ + + double dAreaDiff=0; + + double dNodeAreaBefore = MITAB_AREA(nNodeXMin, + nNodeYMin, + nNodeXMax, + nNodeYMax); + + /* Does the node fully contain the new entry's MBR ? + */ + GBool bIsContained = (nEntryXMin >= nNodeXMin && + nEntryYMin >= nNodeYMin && + nEntryXMax <= nNodeXMax && + nEntryYMax <= nNodeYMax ); + + if (bIsContained) + { + /* If new entry is fully contained in this entry then + * the area difference will be the difference between the area + * of the entry to insert and the area of the node + */ + dAreaDiff = MITAB_AREA(nEntryXMin, nEntryYMin, + nEntryXMax, nEntryYMax) - dNodeAreaBefore; + } + else + { + /* Need to calculate the expanded MBR to calculate the area + * difference. + */ + nNodeXMin = MIN(nNodeXMin, nEntryXMin); + nNodeYMin = MIN(nNodeYMin, nEntryYMin); + nNodeXMax = MAX(nNodeXMax, nEntryXMax); + nNodeYMax = MAX(nNodeYMax, nEntryYMax); + + dAreaDiff = MITAB_AREA(nNodeXMin,nNodeYMin, + nNodeXMax,nNodeYMax) - dNodeAreaBefore; + } + + return dAreaDiff; +} + + + +/********************************************************************** + * TABMAPIndexBlock::PickSeedsForSplit() + * + * (static method, also used by the TABMAPObjBlock class) + * + * Pick two seeds to use to start splitting this node. + * + * Guttman's LinearPickSeed: + * - Along each dimension find the entry whose rectangle has the + * highest low side, and the one with the lowest high side + * - Calculate the separation for each pair + * - Normalize the separation by dividing by the extents of the + * corresponding dimension + * - Choose the pair with the greatest normalized separation along + * any dimension + **********************************************************************/ +int TABMAPIndexBlock::PickSeedsForSplit(TABMAPIndexEntry *pasEntries, + int numEntries, + int nSrcCurChildIndex, + GInt32 nNewEntryXMin, + GInt32 nNewEntryYMin, + GInt32 nNewEntryXMax, + GInt32 nNewEntryYMax, + int &nSeed1, int &nSeed2) +{ + GInt32 nSrcMinX=0, nSrcMinY=0, nSrcMaxX=0, nSrcMaxY=0; + int nLowestMaxX=-1, nHighestMinX=-1, nLowestMaxY=-1, nHighestMinY=-1; + GInt32 nLowestMaxXId=-1, nHighestMinXId=-1, nLowestMaxYId=-1, nHighestMinYId=-1; + + nSeed1=-1; + nSeed2=-1; + + // Along each dimension find the entry whose rectangle has the + // highest low side, and the one with the lowest high side + for(int iEntry=0; iEntry nHighestMinX) + { + nHighestMinX = pasEntries[iEntry].XMin; + nHighestMinXId = iEntry; + } + + if (nLowestMaxYId == -1 || + pasEntries[iEntry].YMax < nLowestMaxY) + { + nLowestMaxY = pasEntries[iEntry].YMax; + nLowestMaxYId = iEntry; + } + + if (nHighestMinYId == -1 || + pasEntries[iEntry].YMin > nHighestMinY) + { + nHighestMinY = pasEntries[iEntry].YMin; + nHighestMinYId = iEntry; + } + + // Also keep track of MBR of all entries + if (iEntry == 0) + { + nSrcMinX = pasEntries[iEntry].XMin; + nSrcMinY = pasEntries[iEntry].YMin; + nSrcMaxX = pasEntries[iEntry].XMax; + nSrcMaxY = pasEntries[iEntry].YMax; + } + else + { + nSrcMinX = MIN(nSrcMinX, pasEntries[iEntry].XMin); + nSrcMinY = MIN(nSrcMinY ,pasEntries[iEntry].YMin); + nSrcMaxX = MAX(nSrcMaxX ,pasEntries[iEntry].XMax); + nSrcMaxY = MAX(nSrcMaxY ,pasEntries[iEntry].YMax); + } + } + + int nSrcWidth, nSrcHeight; + nSrcWidth = ABS(nSrcMaxX - nSrcMinX); + nSrcHeight = ABS(nSrcMaxY - nSrcMinY); + + // Calculate the separation for each pair (note that it may be negative + // in case of overlap) + // Normalize the separation by dividing by the extents of the + // corresponding dimension + double dX, dY; + + dX = (nSrcWidth == 0) ? 0 : (double)(nHighestMinX - nLowestMaxX) / nSrcWidth; + dY = (nSrcHeight == 0) ? 0 : (double)(nHighestMinY - nLowestMaxY) / nSrcHeight; + + // Choose the pair with the greatest normalized separation along + // any dimension + if (dX > dY) + { + nSeed1 = nHighestMinXId; + nSeed2 = nLowestMaxXId; + } + else + { + nSeed1 = nHighestMinYId; + nSeed2 = nLowestMaxYId; + } + + // If nSeed1==nSeed2 then just pick any two (giving pref to current child) + if (nSeed1 == nSeed2) + { + if (nSeed1 != nSrcCurChildIndex && nSrcCurChildIndex != -1) + nSeed1 = nSrcCurChildIndex; + else if (nSeed1 != 0) + nSeed1 = 0; + else + nSeed1 = 1; + } + + // Decide which of the two seeds best matches the new entry. That seed and + // the new entry will stay in current node (new entry will be added by the + // caller later). The other seed will go in the 2nd node + double dAreaDiff1, dAreaDiff2; + dAreaDiff1 = ComputeAreaDiff(pasEntries[nSeed1].XMin, + pasEntries[nSeed1].YMin, + pasEntries[nSeed1].XMax, + pasEntries[nSeed1].YMax, + nNewEntryXMin, nNewEntryYMin, + nNewEntryXMax, nNewEntryYMax); + + dAreaDiff2 = ComputeAreaDiff(pasEntries[nSeed2].XMin, + pasEntries[nSeed2].YMin, + pasEntries[nSeed2].XMax, + pasEntries[nSeed2].YMax, + nNewEntryXMin, nNewEntryYMin, + nNewEntryXMax, nNewEntryYMax); + + /* Note that we want to keep this node's current child in here. + * Since splitting happens only during an addentry() operation and + * then both the current child and the New Entry should fit in the same + * area. + */ + if (nSeed1 != nSrcCurChildIndex && + (dAreaDiff1 > dAreaDiff2 || nSeed2 == nSrcCurChildIndex)) + { + // Seed2 stays in this node, Seed1 moves to new node + // ... swap Seed1 and Seed2 indices + int nTmp = nSeed1; + nSeed1 = nSeed2; + nSeed2 = nTmp; + } + + return 0; +} + + +/********************************************************************** + * TABMAPIndexBlock::SplitNode() + * + * Split current Node, update the references in the parent node, etc. + * Note that Root Nodes cannot be split using this method... SplitRootNode() + * should be used instead. + * + * nNewEntry* are the coord. of the new entry that + * will be added after the split. The split is done so that the current + * node will be the one in which the new object should be stored. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPIndexBlock::SplitNode(GInt32 nNewEntryXMin, GInt32 nNewEntryYMin, + GInt32 nNewEntryXMax, GInt32 nNewEntryYMax) +{ + CPLAssert(m_poBlockManagerRef); + + /*----------------------------------------------------------------- + * Create a 2nd node + *----------------------------------------------------------------*/ + TABMAPIndexBlock *poNewNode = new TABMAPIndexBlock(m_eAccess); + if (poNewNode->InitNewBlock(m_fp, 512, + m_poBlockManagerRef->AllocNewBlock("INDEX")) != 0) + { + return -1; + } + poNewNode->SetMAPBlockManagerRef(m_poBlockManagerRef); + + /*----------------------------------------------------------------- + * Make a temporary copy of the entries in current node + *----------------------------------------------------------------*/ + int nSrcEntries = m_numEntries; + TABMAPIndexEntry *pasSrcEntries = (TABMAPIndexEntry*)CPLMalloc(m_numEntries*sizeof(TABMAPIndexEntry)); + memcpy(pasSrcEntries, &m_asEntries, m_numEntries*sizeof(TABMAPIndexEntry)); + + int nSrcCurChildIndex = m_nCurChildIndex; + + /*----------------------------------------------------------------- + * Pick Seeds for each node + *----------------------------------------------------------------*/ + int nSeed1, nSeed2; + PickSeedsForSplit(pasSrcEntries, nSrcEntries, nSrcCurChildIndex, + nNewEntryXMin, nNewEntryYMin, + nNewEntryXMax, nNewEntryYMax, + nSeed1, nSeed2); + + /*----------------------------------------------------------------- + * Reset number of entries in this node and start moving new entries + *----------------------------------------------------------------*/ + m_numEntries = 0; + + // Insert nSeed1 in this node + InsertEntry(pasSrcEntries[nSeed1].XMin, + pasSrcEntries[nSeed1].YMin, + pasSrcEntries[nSeed1].XMax, + pasSrcEntries[nSeed1].YMax, + pasSrcEntries[nSeed1].nBlockPtr); + + // Move nSeed2 to 2nd node + poNewNode->InsertEntry(pasSrcEntries[nSeed2].XMin, + pasSrcEntries[nSeed2].YMin, + pasSrcEntries[nSeed2].XMax, + pasSrcEntries[nSeed2].YMax, + pasSrcEntries[nSeed2].nBlockPtr); + + // Update cur child index if necessary + if (nSeed1 == nSrcCurChildIndex) + m_nCurChildIndex = m_numEntries-1; + + /*----------------------------------------------------------------- + * Go through the rest of the entries and assign them to one + * of the 2 nodes. + * + * Criteria is minimal area difference. + * Resolve ties by adding the entry to the node with smaller total + * area, then to the one with fewer entries, then to either. + *----------------------------------------------------------------*/ + for(int iEntry=0; iEntry= TAB_MAX_ENTRIES_INDEX_BLOCK-1) + { + poNewNode->InsertEntry(pasSrcEntries[iEntry].XMin, + pasSrcEntries[iEntry].YMin, + pasSrcEntries[iEntry].XMax, + pasSrcEntries[iEntry].YMax, + pasSrcEntries[iEntry].nBlockPtr); + continue; + } + else if (poNewNode->GetNumEntries() >= TAB_MAX_ENTRIES_INDEX_BLOCK-1) + { + InsertEntry(pasSrcEntries[iEntry].XMin, + pasSrcEntries[iEntry].YMin, + pasSrcEntries[iEntry].XMax, + pasSrcEntries[iEntry].YMax, + pasSrcEntries[iEntry].nBlockPtr); + continue; + } + + + // Decide which of the two nodes to put this entry in + double dAreaDiff1, dAreaDiff2; + RecomputeMBR(); + dAreaDiff1 = ComputeAreaDiff(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY, + pasSrcEntries[iEntry].XMin, + pasSrcEntries[iEntry].YMin, + pasSrcEntries[iEntry].XMax, + pasSrcEntries[iEntry].YMax); + + GInt32 nXMin2, nYMin2, nXMax2, nYMax2; + poNewNode->RecomputeMBR(); + poNewNode->GetMBR(nXMin2, nYMin2, nXMax2, nYMax2); + dAreaDiff2 = ComputeAreaDiff(nXMin2, nYMin2, nXMax2, nYMax2, + pasSrcEntries[iEntry].XMin, + pasSrcEntries[iEntry].YMin, + pasSrcEntries[iEntry].XMax, + pasSrcEntries[iEntry].YMax); + if (dAreaDiff1 < dAreaDiff2) + { + // This entry stays in this node + InsertEntry(pasSrcEntries[iEntry].XMin, + pasSrcEntries[iEntry].YMin, + pasSrcEntries[iEntry].XMax, + pasSrcEntries[iEntry].YMax, + pasSrcEntries[iEntry].nBlockPtr); + } + else + { + // This entry goes to new node + poNewNode->InsertEntry(pasSrcEntries[iEntry].XMin, + pasSrcEntries[iEntry].YMin, + pasSrcEntries[iEntry].XMax, + pasSrcEntries[iEntry].YMax, + pasSrcEntries[iEntry].nBlockPtr); + } + } + + /*----------------------------------------------------------------- + * Recompute MBR and update current node info in parent + *----------------------------------------------------------------*/ + RecomputeMBR(); + poNewNode->RecomputeMBR(); + + /*----------------------------------------------------------------- + * Add second node info to parent and then flush it to disk. + * This may trigger splitting of parent + *----------------------------------------------------------------*/ + CPLAssert(m_poParentRef); + int nMinX, nMinY, nMaxX, nMaxY; + poNewNode->GetMBR(nMinX, nMinY, nMaxX, nMaxY); + m_poParentRef->AddEntry(nMinX, nMinY, nMaxX, nMaxY, + poNewNode->GetNodeBlockPtr(), TRUE); + poNewNode->CommitToFile(); + delete poNewNode; + + CPLFree(pasSrcEntries); + + return 0; +} + +/********************************************************************** + * TABMAPIndexBlock::SplitRootNode() + * + * (private method) + * + * Split a Root Node. + * First, a level of nodes must be added to the tree, then the contents + * of what used to be the root node is moved 1 level down and then that + * node is split like a regular node. + * + * Returns 0 on success, -1 on error + **********************************************************************/ +int TABMAPIndexBlock::SplitRootNode(GInt32 nNewEntryXMin, GInt32 nNewEntryYMin, + GInt32 nNewEntryXMax, GInt32 nNewEntryYMax) +{ + CPLAssert(m_poBlockManagerRef); + CPLAssert(m_poParentRef == NULL); + + /*----------------------------------------------------------------- + * Since a root note cannot be split, we add a level of nodes + * under it and we'll do the split at that level. + *----------------------------------------------------------------*/ + TABMAPIndexBlock *poNewNode = new TABMAPIndexBlock(m_eAccess); + + if (poNewNode->InitNewBlock(m_fp, 512, + m_poBlockManagerRef->AllocNewBlock("INDEX")) != 0) + { + return -1; + } + poNewNode->SetMAPBlockManagerRef(m_poBlockManagerRef); + + // Move all entries to the new child + int nSrcEntries = m_numEntries; + m_numEntries = 0; + for(int iEntry=0; iEntryInsertEntry(m_asEntries[iEntry].XMin, + m_asEntries[iEntry].YMin, + m_asEntries[iEntry].XMax, + m_asEntries[iEntry].YMax, + m_asEntries[iEntry].nBlockPtr); + } + + /*----------------------------------------------------------------- + * Transfer current child object to new node. + *----------------------------------------------------------------*/ + if (m_poCurChild) + { + poNewNode->SetCurChildRef(m_poCurChild, m_nCurChildIndex); + m_poCurChild->SetParentRef(poNewNode); + m_poCurChild = NULL; + m_nCurChildIndex = -1; + } + + /*----------------------------------------------------------------- + * Place info about new child in current node. + *----------------------------------------------------------------*/ + poNewNode->RecomputeMBR(); + int nMinX, nMinY, nMaxX, nMaxY; + poNewNode->GetMBR(nMinX, nMinY, nMaxX, nMaxY); + InsertEntry(nMinX, nMinY, nMaxX, nMaxY, poNewNode->GetNodeBlockPtr()); + + /*----------------------------------------------------------------- + * Keep a reference to the new child + *----------------------------------------------------------------*/ + poNewNode->SetParentRef(this); + m_poCurChild = poNewNode; + m_nCurChildIndex = m_numEntries -1; + + /*----------------------------------------------------------------- + * And finally force the child to split itself + *----------------------------------------------------------------*/ + return m_poCurChild->SplitNode(nNewEntryXMin, nNewEntryYMin, + nNewEntryXMax, nNewEntryYMax); +} + + +/********************************************************************** + * TABMAPIndexBlock::RecomputeMBR() + * + * Recompute current block MBR, and update info in parent. + **********************************************************************/ +void TABMAPIndexBlock::RecomputeMBR() +{ + GInt32 nMinX, nMinY, nMaxX, nMaxY; + + nMinX = 1000000000; + nMinY = 1000000000; + nMaxX = -1000000000; + nMaxY = -1000000000; + + for(int i=0; i nMaxX) + nMaxX = m_asEntries[i].XMax; + + if (m_asEntries[i].YMin < nMinY) + nMinY = m_asEntries[i].YMin; + if (m_asEntries[i].YMax > nMaxY) + nMaxY = m_asEntries[i].YMax; + } + + if (m_nMinX != nMinX || + m_nMinY != nMinY || + m_nMaxX != nMaxX || + m_nMaxY != nMaxY ) + { + m_nMinX = nMinX; + m_nMinY = nMinY; + m_nMaxX = nMaxX; + m_nMaxY = nMaxY; + + m_bModified = TRUE; + + if (m_poParentRef) + m_poParentRef->UpdateCurChildMBR(m_nMinX, m_nMinY, + m_nMaxX, m_nMaxY, + GetNodeBlockPtr()); + } + +} + +/********************************************************************** + * TABMAPIndexBlock::UpateCurChildMBR() + * + * Update current child MBR info, and propagate info in parent. + * + * nBlockPtr is passed only to validate the consistency of the tree. + **********************************************************************/ +void TABMAPIndexBlock::UpdateCurChildMBR(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax, + CPL_UNUSED GInt32 nBlockPtr) +{ + CPLAssert(m_poCurChild); + CPLAssert(m_asEntries[m_nCurChildIndex].nBlockPtr == nBlockPtr); + + if (m_asEntries[m_nCurChildIndex].XMin == nXMin && + m_asEntries[m_nCurChildIndex].YMin == nYMin && + m_asEntries[m_nCurChildIndex].XMax == nXMax && + m_asEntries[m_nCurChildIndex].YMax == nYMax) + { + return; /* Nothing changed... nothing to do */ + } + + m_bModified = TRUE; + + m_asEntries[m_nCurChildIndex].XMin = nXMin; + m_asEntries[m_nCurChildIndex].YMin = nYMin; + m_asEntries[m_nCurChildIndex].XMax = nXMax; + m_asEntries[m_nCurChildIndex].YMax = nYMax; + + m_nMinX = 1000000000; + m_nMinY = 1000000000; + m_nMaxX = -1000000000; + m_nMaxY = -1000000000; + + for(int i=0; i m_nMaxX) + m_nMaxX = m_asEntries[i].XMax; + + if (m_asEntries[i].YMin < m_nMinY) + m_nMinY = m_asEntries[i].YMin; + if (m_asEntries[i].YMax > m_nMaxY) + m_nMaxY = m_asEntries[i].YMax; + } + + if (m_poParentRef) + m_poParentRef->UpdateCurChildMBR(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY, + GetNodeBlockPtr()); + +} + + +/********************************************************************** + * TABMAPIndexBlock::SetMAPBlockManagerRef() + * + * Pass a reference to the block manager object for the file this + * block belongs to. The block manager will be used by this object + * when it needs to automatically allocate a new block. + **********************************************************************/ +void TABMAPIndexBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr) +{ + m_poBlockManagerRef = poBlockMgr; +}; + +/********************************************************************** + * TABMAPIndexBlock::SetParentRef() + * + * Used to pass a reference to this node's parent. + **********************************************************************/ +void TABMAPIndexBlock::SetParentRef(TABMAPIndexBlock *poParent) +{ + m_poParentRef = poParent; +} + +/********************************************************************** + * TABMAPIndexBlock::SetCurChildRef() + * + * Used to transfer a child object from one node to another + **********************************************************************/ +void TABMAPIndexBlock::SetCurChildRef(TABMAPIndexBlock *poChild, + int nChildIndex) +{ + m_poCurChild = poChild; + m_nCurChildIndex = nChildIndex; +} + +/********************************************************************** + * TABMAPIndexBlock::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABMAPIndexBlock::Dump(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABMAPIndexBlock::Dump() -----\n"); + if (m_pabyBuf == NULL) + { + fprintf(fpOut, "Block has not been initialized yet."); + } + else + { + fprintf(fpOut,"Index Block (type %d) at offset %d.\n", + m_nBlockType, m_nFileOffset); + fprintf(fpOut," m_numEntries = %d\n", m_numEntries); + + /*------------------------------------------------------------- + * Loop through all entries, dumping each of them + *------------------------------------------------------------*/ + if (m_numEntries > 0) + ReadAllEntries(); + + for(int i=0; i (%d, %d) - (%d, %d)\n", + m_asEntries[i].nBlockPtr, + m_asEntries[i].XMin, m_asEntries[i].YMin, + m_asEntries[i].XMax, m_asEntries[i].YMax ); + } + + } + + fflush(fpOut); +} +#endif // DEBUG diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapobjectblock.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapobjectblock.cpp new file mode 100644 index 000000000..34ad29d63 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_mapobjectblock.cpp @@ -0,0 +1,2098 @@ +/********************************************************************** + * $Id: mitab_mapobjectblock.cpp,v 1.23 2010-07-07 19:00:15 aboudreault Exp $ + * + * Name: mitab_mapobjectblock.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABMAPObjectBlock class used to handle + * reading/writing of the .MAP files' object data blocks + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2001, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_mapobjectblock.cpp,v $ + * Revision 1.23 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.22 2008-02-20 21:35:30 dmorissette + * Added support for V800 COLLECTION of large objects (bug 1496) + * + * Revision 1.21 2008/02/05 22:22:48 dmorissette + * Added support for TAB_GEOM_V800_MULTIPOINT (bug 1496) + * + * Revision 1.20 2008/02/01 19:36:31 dmorissette + * Initial support for V800 REGION and MULTIPLINE (bug 1496) + * + * Revision 1.19 2007/09/18 17:43:56 dmorissette + * Fixed another index splitting issue: compr coordinates origin was not + * stored in the TABFeature in ReadGeometry... (bug 1732) + * + * Revision 1.18 2007/06/11 14:52:31 dmorissette + * Return a valid m_nCoordDatasize value for Collection objects to prevent + * trashing of collection data during object splitting (bug 1728) + * + * Revision 1.17 2007/05/22 14:53:10 dmorissette + * Fixed error reading compressed text objects introduced in v1.6.0 (bug 1722) + * + * Revision 1.16 2006/11/28 18:49:08 dmorissette + * Completed changes to split TABMAPObjectBlocks properly and produce an + * optimal spatial index (bug 1585) + * + * Revision 1.15 2005/10/06 19:15:31 dmorissette + * Collections: added support for reading/writing pen/brush/symbol ids and + * for writing collection objects to .TAB/.MAP (bug 1126) + * + * Revision 1.14 2005/10/04 15:44:31 dmorissette + * First round of support for Collection objects. Currently supports reading + * from .TAB/.MAP and writing to .MIF. Still lacks symbol support and write + * support. (Based in part on patch and docs from Jim Hope, bug 1126) + * + * Revision 1.13 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.12 2002/03/26 01:48:40 daniel + * Added Multipoint object type (V650) + * + * Revision 1.11 2001/12/05 22:40:27 daniel + * Init MBR to 0 in TABMAPObjHdr and modif. SetMBR() to validate min/max + * + * Revision 1.10 2001/11/19 15:07:06 daniel + * Handle the case of TAB_GEOM_NONE with the new TABMAPObjHdr classes. + * + * Revision 1.9 2001/11/17 21:54:06 daniel + * Made several changes in order to support writing objects in 16 bits + * coordinate format. New TABMAPObjHdr-derived classes are used to hold + * object info in mem until block is full. + * + * Revision 1.8 2001/09/19 19:19:11 warmerda + * modified AdvanceToNextObject() to skip deleted objects + * + * Revision 1.7 2001/09/14 03:23:55 warmerda + * Substantial upgrade to support spatial queries using spatial indexes + * + * Revision 1.6 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.5 1999/10/19 06:07:29 daniel + * Removed obsolete comment. + * + * Revision 1.4 1999/10/18 15:41:00 daniel + * Added WriteIntMBRCoord() + * + * Revision 1.3 1999/09/29 04:23:06 daniel + * Fixed typo in GetMBR() + * + * Revision 1.2 1999/09/26 14:59:37 daniel + * Implemented write support + * + * Revision 1.1 1999/07/12 04:18:25 daniel + * Initial checkin + * + **********************************************************************/ + +#include "mitab.h" + +/*===================================================================== + * class TABMAPObjectBlock + *====================================================================*/ + +#define MAP_OBJECT_HEADER_SIZE 20 + +/********************************************************************** + * TABMAPObjectBlock::TABMAPObjectBlock() + * + * Constructor. + **********************************************************************/ +TABMAPObjectBlock::TABMAPObjectBlock(TABAccess eAccessMode /*= TABRead*/): + TABRawBinBlock(eAccessMode, TRUE) +{ + m_bLockCenter = FALSE; +} + +/********************************************************************** + * TABMAPObjectBlock::~TABMAPObjectBlock() + * + * Destructor. + **********************************************************************/ +TABMAPObjectBlock::~TABMAPObjectBlock() +{ + + m_nMinX = 1000000000; + m_nMinY = 1000000000; + m_nMaxX = -1000000000; + m_nMaxY = -1000000000; +} + + +/********************************************************************** + * TABMAPObjectBlock::InitBlockFromData() + * + * Perform some initialization on the block after its binary data has + * been set or changed (or loaded from a file). + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPObjectBlock::InitBlockFromData(GByte *pabyBuf, + int nBlockSize, int nSizeUsed, + GBool bMakeCopy /* = TRUE */, + VSILFILE *fpSrc /* = NULL */, + int nOffset /* = 0 */) +{ + int nStatus; + + /*----------------------------------------------------------------- + * First of all, we must call the base class' InitBlockFromData() + *----------------------------------------------------------------*/ + nStatus = TABRawBinBlock::InitBlockFromData(pabyBuf, + nBlockSize, nSizeUsed, + bMakeCopy, + fpSrc, nOffset); + if (nStatus != 0) + return nStatus; + + /*----------------------------------------------------------------- + * Validate block type + *----------------------------------------------------------------*/ + if (m_nBlockType != TABMAP_OBJECT_BLOCK) + { + CPLError(CE_Failure, CPLE_FileIO, + "InitBlockFromData(): Invalid Block Type: got %d expected %d", + m_nBlockType, TABMAP_OBJECT_BLOCK); + CPLFree(m_pabyBuf); + m_pabyBuf = NULL; + return -1; + } + + /*----------------------------------------------------------------- + * Init member variables + *----------------------------------------------------------------*/ + GotoByteInBlock(0x002); + m_numDataBytes = ReadInt16(); /* Excluding 4 bytes header */ + + m_nCenterX = ReadInt32(); + m_nCenterY = ReadInt32(); + + m_nFirstCoordBlock = ReadInt32(); + m_nLastCoordBlock = ReadInt32(); + + m_nCurObjectOffset = -1; + m_nCurObjectId = -1; + m_nCurObjectType = TAB_GEOM_UNSET; + + m_nMinX = 1000000000; + m_nMinY = 1000000000; + m_nMaxX = -1000000000; + m_nMaxY = -1000000000; + m_bLockCenter = FALSE; + + /*----------------------------------------------------------------- + * Set real value for m_nSizeUsed to allow random update + * (By default TABRawBinBlock thinks all 512 bytes are used) + *----------------------------------------------------------------*/ + m_nSizeUsed = m_numDataBytes + MAP_OBJECT_HEADER_SIZE; + + return 0; +} + +/************************************************************************ + * ClearObjects() + * + * Cleans existing objects from the block. This method is used when + * compacting a page that has deleted records. + ************************************************************************/ +void TABMAPObjectBlock::ClearObjects() +{ + GotoByteInBlock(MAP_OBJECT_HEADER_SIZE); + WriteZeros(m_nBlockSize - MAP_OBJECT_HEADER_SIZE); + GotoByteInBlock(MAP_OBJECT_HEADER_SIZE); + m_nSizeUsed = MAP_OBJECT_HEADER_SIZE; + m_bModified = TRUE; +} + +/************************************************************************ + * LockCenter() + * + * Prevents the m_nCenterX and m_nCenterY to be adjusted by other methods. + * Useful when editing pages that have compressed geometries. + * This is a bit band-aid. Proper support of compressed geometries should + * handle center moves. + ************************************************************************/ +void TABMAPObjectBlock::LockCenter() +{ + m_bLockCenter = TRUE; +} + +/************************************************************************ + * SetCenterFromOtherBlock() + * + * Sets the m_nCenterX and m_nCenterY from the one of another block and + * lock them. See LockCenter() as well. + * Used when splitting a page. + ************************************************************************/ +void TABMAPObjectBlock::SetCenterFromOtherBlock(TABMAPObjectBlock* poOtherObjBlock) +{ + m_nCenterX = poOtherObjBlock->m_nCenterX; + m_nCenterY = poOtherObjBlock->m_nCenterY; + LockCenter(); +} + +/************************************************************************/ +/* Rewind() */ +/************************************************************************/ +void TABMAPObjectBlock::Rewind( ) +{ + m_nCurObjectId = -1; + m_nCurObjectOffset = -1; + m_nCurObjectType = TAB_GEOM_UNSET; +} + +/************************************************************************/ +/* AdvanceToNextObject() */ +/************************************************************************/ + +int TABMAPObjectBlock::AdvanceToNextObject( TABMAPHeaderBlock *poHeader ) + +{ + if( m_nCurObjectId == -1 ) + { + m_nCurObjectOffset = 20; + } + else + { + m_nCurObjectOffset += poHeader->GetMapObjectSize( m_nCurObjectType ); + } + + + + if( m_nCurObjectOffset + 5 < m_numDataBytes + 20 ) + { + GotoByteInBlock( m_nCurObjectOffset ); + m_nCurObjectType = (TABGeomType)ReadByte(); + } + else + { + m_nCurObjectType = TAB_GEOM_UNSET; + } + + if( m_nCurObjectType <= 0 || m_nCurObjectType >= TAB_GEOM_MAX_TYPE ) + { + m_nCurObjectType = TAB_GEOM_UNSET; + m_nCurObjectId = -1; + m_nCurObjectOffset = -1; + } + else + { + m_nCurObjectId = ReadInt32(); + + // Is this object marked as deleted? If so, skip it. + // I check both the top bits but I have only seen this occur + // with the second highest bit set (ie. in usa/states.tab). NFW. + + if( (((GUInt32)m_nCurObjectId) & (GUInt32) 0xC0000000) != 0 ) + { + m_nCurObjectId = AdvanceToNextObject( poHeader ); + } + } + + return m_nCurObjectId; +} + +/********************************************************************** + * TABMAPObjectBlock::CommitToFile() + * + * Commit the current state of the binary block to the file to which + * it has been previously attached. + * + * This method makes sure all values are properly set in the map object + * block header and then calls TABRawBinBlock::CommitToFile() to do + * the actual writing to disk. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPObjectBlock::CommitToFile() +{ + int nStatus = 0; + + if ( m_pabyBuf == NULL ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABMAPObjectBlock::CommitToFile(): Block has not been initialized yet!"); + return -1; + } + + /*----------------------------------------------------------------- + * Nothing to do here if block has not been modified + *----------------------------------------------------------------*/ + if (!m_bModified) + return 0; + + /*----------------------------------------------------------------- + * Make sure 20 bytes block header is up to date. + *----------------------------------------------------------------*/ + GotoByteInBlock(0x000); + + WriteInt16(TABMAP_OBJECT_BLOCK); // Block type code + m_numDataBytes = m_nSizeUsed - MAP_OBJECT_HEADER_SIZE; + WriteInt16((GInt16)m_numDataBytes); // num. bytes used + + WriteInt32(m_nCenterX); + WriteInt32(m_nCenterY); + + WriteInt32(m_nFirstCoordBlock); + WriteInt32(m_nLastCoordBlock); + + nStatus = CPLGetLastErrorNo(); + + /*----------------------------------------------------------------- + * OK, all object data has already been written in the block. + * Call the base class to write the block to disk. + *----------------------------------------------------------------*/ + if (nStatus == 0) + { +#ifdef DEBUG_VERBOSE + CPLDebug("MITAB", "Commiting OBJECT block to offset %d", m_nFileOffset); +#endif + nStatus = TABRawBinBlock::CommitToFile(); + } + + return nStatus; +} + +/********************************************************************** + * TABMAPObjectBlock::InitNewBlock() + * + * Initialize a newly created block so that it knows to which file it + * is attached, its block size, etc . and then perform any specific + * initialization for this block type, including writing a default + * block header, etc. and leave the block ready to receive data. + * + * This is an alternative to calling ReadFromFile() or InitBlockFromData() + * that puts the block in a stable state without loading any initial + * data in it. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPObjectBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize, + int nFileOffset /* = 0*/) +{ + /*----------------------------------------------------------------- + * Start with the default initialisation + *----------------------------------------------------------------*/ + if ( TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0) + return -1; + + /*----------------------------------------------------------------- + * And then set default values for the block header. + *----------------------------------------------------------------*/ + // Set block MBR to extreme values to force an update on the first + // UpdateMBR() call. + m_nMinX = 1000000000; + m_nMaxX = -1000000000; + m_nMinY = 1000000000; + m_nMaxY = -1000000000; + + // Reset current object refs + m_nCurObjectId = -1; + m_nCurObjectOffset = -1; + m_nCurObjectType = TAB_GEOM_UNSET; + + m_numDataBytes = 0; /* Data size excluding header */ + m_nCenterX = m_nCenterY = 0; + m_nFirstCoordBlock = 0; + m_nLastCoordBlock = 0; + + if (m_eAccess != TABRead && nFileOffset != 0) + { + GotoByteInBlock(0x000); + + WriteInt16(TABMAP_OBJECT_BLOCK);// Block type code + WriteInt16(0); // num. bytes used, excluding header + + // MBR center here... will be written in CommitToFile() + WriteInt32(0); + WriteInt32(0); + + // First/last coord block ref... will be written in CommitToFile() + WriteInt32(0); + WriteInt32(0); + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPObjectBlock::ReadCoord() + * + * Read the next pair of integer coordinates value from the block, and + * apply the translation relative to to the center of the data block + * if bCompressed=TRUE. + * + * This means that the returned coordinates are always absolute integer + * coordinates, even when the source coords are in compressed form. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPObjectBlock::ReadIntCoord(GBool bCompressed, + GInt32 &nX, GInt32 &nY) +{ + if (bCompressed) + { + nX = m_nCenterX + ReadInt16(); + nY = m_nCenterY + ReadInt16(); + } + else + { + nX = ReadInt32(); + nY = ReadInt32(); + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPObjectBlock::WriteIntCoord() + * + * Write a pair of integer coordinates values to the current position in the + * the block. If bCompr=TRUE then the coordinates are written relative to + * the object block center... otherwise they're written as 32 bits int. + * + * This function does not maintain the block's MBR and center... it is + * assumed to have been set before the first call to WriteIntCoord() + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPObjectBlock::WriteIntCoord(GInt32 nX, GInt32 nY, + GBool bCompressed /*=FALSE*/) +{ + + /*----------------------------------------------------------------- + * Write coords to the file. + *----------------------------------------------------------------*/ + if ((!bCompressed && (WriteInt32(nX) != 0 || WriteInt32(nY) != 0 ) ) || + (bCompressed && (WriteInt16((GInt16)(nX - m_nCenterX)) != 0 || + WriteInt16((GInt16)(nY - m_nCenterY)) != 0) ) ) + { + return -1; + } + + return 0; +} + +/********************************************************************** + * TABMAPObjectBlock::WriteIntMBRCoord() + * + * Write 2 pairs of integer coordinates values to the current position + * in the the block after making sure that min values are smaller than + * max values. Use this function to write MBR coordinates for an object. + * + * If bCompr=TRUE then the coordinates are written relative to + * the object block center... otherwise they're written as 32 bits int. + * + * This function does not maintain the block's MBR and center... it is + * assumed to have been set before the first call to WriteIntCoord() + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPObjectBlock::WriteIntMBRCoord(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax, + GBool bCompressed /*=FALSE*/) +{ + if (WriteIntCoord(MIN(nXMin, nXMax), MIN(nYMin, nYMax), + bCompressed) != 0 || + WriteIntCoord(MAX(nXMin, nXMax), MAX(nYMin, nYMax), + bCompressed) != 0 ) + { + return -1; + } + + return 0; +} + + +/********************************************************************** + * TABMAPObjectBlock::UpdateMBR() + * + * Update the block's MBR and center. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPObjectBlock::UpdateMBR(GInt32 nX, GInt32 nY) +{ + + if (nX < m_nMinX) + m_nMinX = nX; + if (nX > m_nMaxX) + m_nMaxX = nX; + + if (nY < m_nMinY) + m_nMinY = nY; + if (nY > m_nMaxY) + m_nMaxY = nY; + + if( !m_bLockCenter ) + { + m_nCenterX = (m_nMinX + m_nMaxX) /2; + m_nCenterY = (m_nMinY + m_nMaxY) /2; + } + + return 0; +} + +/********************************************************************** + * TABMAPObjectBlock::AddCoordBlockRef() + * + * Update the first/last coord block fields in this object to contain + * the specified block address. + **********************************************************************/ +void TABMAPObjectBlock::AddCoordBlockRef(GInt32 nNewBlockAddress) +{ + /*----------------------------------------------------------------- + * Normally, new blocks are added to the end of the list, except + * the first one which is the beginning and the end of the list at + * the same time. + *----------------------------------------------------------------*/ + if (m_nFirstCoordBlock == 0) + m_nFirstCoordBlock = nNewBlockAddress; + + m_nLastCoordBlock = nNewBlockAddress; + m_bModified = TRUE; +} + +/********************************************************************** + * TABMAPObjectBlock::SetMBR() + * + * Set the MBR for the current block. + **********************************************************************/ +void TABMAPObjectBlock::SetMBR(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax) +{ + m_nMinX = nXMin; + m_nMinY = nYMin; + m_nMaxX = nXMax; + m_nMaxY = nYMax; + + if( !m_bLockCenter ) + { + m_nCenterX = (m_nMinX + m_nMaxX) /2; + m_nCenterY = (m_nMinY + m_nMaxY) /2; + } +} + +/********************************************************************** + * TABMAPObjectBlock::GetMBR() + * + * Return the MBR for the current block. + **********************************************************************/ +void TABMAPObjectBlock::GetMBR(GInt32 &nXMin, GInt32 &nYMin, + GInt32 &nXMax, GInt32 &nYMax) +{ + nXMin = m_nMinX; + nYMin = m_nMinY; + nXMax = m_nMaxX; + nYMax = m_nMaxY; +} + + +/********************************************************************** + * TABMAPObjectBlock::PrepareNewObject() + * + * Prepare this block to receive this new object. We only reserve space for + * it in this call. Actual data will be written only when CommitNewObject() + * is called. + * + * Returns the position at which the new object starts + **********************************************************************/ +int TABMAPObjectBlock::PrepareNewObject(TABMAPObjHdr *poObjHdr) +{ + int nStartAddress = 0; + + // Nothing to do for NONE objects + if (poObjHdr->m_nType == TAB_GEOM_NONE) + { + return 0; + } + + // Maintain MBR of this object block. + UpdateMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY); + UpdateMBR(poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + + /*----------------------------------------------------------------- + * Keep track of object type, ID and start address for use by + * CommitNewObject() + *----------------------------------------------------------------*/ + nStartAddress = GetFirstUnusedByteOffset(); + + // Backup MBR and bLockCenter as they will be reset by GotoByteInFile() + // that will call InitBlockFromData() + GInt32 nXMin, nYMin, nXMax, nYMax; + GetMBR(nXMin, nYMin, nXMax, nYMax); + int bLockCenter = m_bLockCenter; + GotoByteInFile(nStartAddress); + m_bLockCenter = bLockCenter; + SetMBR(nXMin, nYMin, nXMax, nYMax); + m_nCurObjectOffset = nStartAddress - GetStartAddress(); + + m_nCurObjectType = poObjHdr->m_nType; + m_nCurObjectId = poObjHdr->m_nId; + + return nStartAddress; +} + +/********************************************************************** + * TABMAPObjectBlock::CommitCurObjData() + * + * Write the ObjHdr to this block. This is usually called after + * PrepareNewObject() once all members of the ObjHdr have + * been set. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPObjectBlock::CommitNewObject(TABMAPObjHdr *poObjHdr) +{ + int nStatus = 0; + + // Nothing to do for NONE objects + if (poObjHdr->m_nType == TAB_GEOM_NONE) + { + return 0; + } + + CPLAssert(m_nCurObjectId == poObjHdr->m_nId); + GotoByteInBlock(m_nCurObjectOffset); + + nStatus = poObjHdr->WriteObj(this); + + if (nStatus == 0) + m_numDataBytes = m_nSizeUsed - MAP_OBJECT_HEADER_SIZE; + + return nStatus; +} + +/********************************************************************** + * TABMAPObjectBlock::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABMAPObjectBlock::Dump(FILE *fpOut, GBool bDetails) +{ + CPLErrorReset(); + + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABMAPObjectBlock::Dump() -----\n"); + if (m_pabyBuf == NULL) + { + fprintf(fpOut, "Block has not been initialized yet."); + } + else + { + fprintf(fpOut,"Object Data Block (type %d) at offset %d.\n", + m_nBlockType, m_nFileOffset); + fprintf(fpOut," m_numDataBytes = %d\n", m_numDataBytes); + fprintf(fpOut," m_nCenterX = %d\n", m_nCenterX); + fprintf(fpOut," m_nCenterY = %d\n", m_nCenterY); + fprintf(fpOut," m_nFirstCoordBlock = %d\n", m_nFirstCoordBlock); + fprintf(fpOut," m_nLastCoordBlock = %d\n", m_nLastCoordBlock); + } + + if (bDetails) + { + /* We need the mapfile's header block */ + TABRawBinBlock *poBlock; + TABMAPHeaderBlock *poHeader; + TABMAPObjHdr *poObjHdr; + + poBlock = TABCreateMAPBlockFromFile(m_fp, 0, 512); + if (poBlock==NULL || poBlock->GetBlockClass() != TABMAP_HEADER_BLOCK) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Failed reading header block."); + return; + } + poHeader = (TABMAPHeaderBlock *)poBlock; + + Rewind(); + while((poObjHdr = TABMAPObjHdr::ReadNextObj(this, poHeader)) != NULL) + { + fprintf(fpOut, + " object id=%d, type=%d, offset=%d (%d), size=%d\n" + " MBR=(%d, %d, %d, %d)\n", + m_nCurObjectId, m_nCurObjectType, m_nCurObjectOffset, + m_nFileOffset + m_nCurObjectOffset, + poHeader->GetMapObjectSize( m_nCurObjectType ), + poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX,poObjHdr->m_nMaxY); + delete poObjHdr; + } + + delete poHeader; + } + + fflush(fpOut); +} + +#endif // DEBUG + + + +/*===================================================================== + * class TABMAPObjHdr and family + *====================================================================*/ + +/********************************************************************** + * class TABMAPObjHdr + * + * Virtual base class... contains static methods used to allocate instance + * of the derived classes. + * + **********************************************************************/ + + +/********************************************************************** + * TABMAPObjHdr::NewObj() + * + * Alloc a new object of specified type or NULL for NONE types or if type + * is not supported. + **********************************************************************/ +TABMAPObjHdr *TABMAPObjHdr::NewObj(TABGeomType nNewObjType, GInt32 nId /*=0*/) +{ + TABMAPObjHdr *poObj = NULL; + + switch(nNewObjType) + { + case TAB_GEOM_NONE: + poObj = new TABMAPObjNone; + break; + case TAB_GEOM_SYMBOL_C: + case TAB_GEOM_SYMBOL: + poObj = new TABMAPObjPoint; + break; + case TAB_GEOM_FONTSYMBOL_C: + case TAB_GEOM_FONTSYMBOL: + poObj = new TABMAPObjFontPoint; + break; + case TAB_GEOM_CUSTOMSYMBOL_C: + case TAB_GEOM_CUSTOMSYMBOL: + poObj = new TABMAPObjCustomPoint; + break; + case TAB_GEOM_LINE_C: + case TAB_GEOM_LINE: + poObj = new TABMAPObjLine; + break; + case TAB_GEOM_PLINE_C: + case TAB_GEOM_PLINE: + case TAB_GEOM_REGION_C: + case TAB_GEOM_REGION: + case TAB_GEOM_MULTIPLINE_C: + case TAB_GEOM_MULTIPLINE: + case TAB_GEOM_V450_REGION_C: + case TAB_GEOM_V450_REGION: + case TAB_GEOM_V450_MULTIPLINE_C: + case TAB_GEOM_V450_MULTIPLINE: + case TAB_GEOM_V800_REGION_C: + case TAB_GEOM_V800_REGION: + case TAB_GEOM_V800_MULTIPLINE_C: + case TAB_GEOM_V800_MULTIPLINE: + poObj = new TABMAPObjPLine; + break; + case TAB_GEOM_ARC_C: + case TAB_GEOM_ARC: + poObj = new TABMAPObjArc; + break; + case TAB_GEOM_RECT_C: + case TAB_GEOM_RECT: + case TAB_GEOM_ROUNDRECT_C: + case TAB_GEOM_ROUNDRECT: + case TAB_GEOM_ELLIPSE_C: + case TAB_GEOM_ELLIPSE: + poObj = new TABMAPObjRectEllipse; + break; + case TAB_GEOM_TEXT_C: + case TAB_GEOM_TEXT: + poObj = new TABMAPObjText; + break; + case TAB_GEOM_MULTIPOINT_C: + case TAB_GEOM_MULTIPOINT: + case TAB_GEOM_V800_MULTIPOINT_C: + case TAB_GEOM_V800_MULTIPOINT: + poObj = new TABMAPObjMultiPoint; + break; + case TAB_GEOM_COLLECTION_C: + case TAB_GEOM_COLLECTION: + case TAB_GEOM_V800_COLLECTION_C: + case TAB_GEOM_V800_COLLECTION: + poObj = new TABMAPObjCollection(); + break; + default: + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABMAPObjHdr::NewObj(): Unsupported object type %d", + nNewObjType); + } + + if (poObj) + { + poObj->m_nType = nNewObjType; + poObj->m_nId = nId; + poObj->m_nMinX = poObj->m_nMinY = poObj->m_nMaxX = poObj->m_nMaxY = 0; + } + + return poObj; +} + + +/********************************************************************** + * TABMAPObjHdr::ReadNextObj() + * + * Read next object in this block and allocate/init a new object for it + * if succesful. + * Returns NULL in case of error or if we reached end of block. + **********************************************************************/ +TABMAPObjHdr *TABMAPObjHdr::ReadNextObj(TABMAPObjectBlock *poObjBlock, + TABMAPHeaderBlock *poHeader) +{ + TABMAPObjHdr *poObjHdr = NULL; + + if (poObjBlock->AdvanceToNextObject(poHeader) != -1) + { + poObjHdr=TABMAPObjHdr::NewObj(poObjBlock->GetCurObjectType()); + if (poObjHdr && + ((poObjHdr->m_nId = poObjBlock->GetCurObjectId()) == -1 || + poObjHdr->ReadObj(poObjBlock) != 0 ) ) + { + // Failed reading object in block... an error was already produced + delete poObjHdr; + return NULL; + } + } + + return poObjHdr; +} + +/********************************************************************** + * TABMAPObjHdr::IsCompressedType() + * + * Returns TRUE if the current object type uses compressed coordinates + * or FALSE otherwise. + **********************************************************************/ +GBool TABMAPObjHdr::IsCompressedType() +{ + // Compressed types are 1, 4, 7, etc. + return ((m_nType % 3) == 1 ? TRUE : FALSE); +} + +/********************************************************************** + * TABMAPObjHdr::WriteObjTypeAndId() + * + * Writetype+object id information... should be called only by the derived + * classes' WriteObj() methods. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjHdr::WriteObjTypeAndId(TABMAPObjectBlock *poObjBlock) +{ + poObjBlock->WriteByte((GByte)m_nType); + return poObjBlock->WriteInt32(m_nId); +} + +/********************************************************************** + * TABMAPObjHdr::SetMBR() + * + **********************************************************************/ +void TABMAPObjHdr::SetMBR(GInt32 nMinX, GInt32 nMinY, + GInt32 nMaxX, GInt32 nMaxY) +{ + m_nMinX = MIN(nMinX, nMaxX); + m_nMinY = MIN(nMinY, nMaxY); + m_nMaxX = MAX(nMinX, nMaxX); + m_nMaxY = MAX(nMinY, nMaxY); +} + + +/********************************************************************** + * class TABMAPObjLine + * + * Applies to 2-points LINEs only + **********************************************************************/ + +/********************************************************************** + * TABMAPObjLine::ReadObj() + * + * Read Object information starting after the object id which should + * have been read by TABMAPObjHdr::ReadNextObj() already. + * This function should be called only by TABMAPObjHdr::ReadNextObj(). + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjLine::ReadObj(TABMAPObjectBlock *poObjBlock) +{ + poObjBlock->ReadIntCoord(IsCompressedType(), m_nX1, m_nY1); + poObjBlock->ReadIntCoord(IsCompressedType(), m_nX2, m_nY2); + + m_nPenId = poObjBlock->ReadByte(); // Pen index + + SetMBR(m_nX1, m_nY1, m_nX2, m_nY2); + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPObjLine::WriteObj() + * + * Write Object information with the type+object id + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjLine::WriteObj(TABMAPObjectBlock *poObjBlock) +{ + // Write object type and id + TABMAPObjHdr::WriteObjTypeAndId(poObjBlock); + + poObjBlock->WriteIntCoord(m_nX1, m_nY1, IsCompressedType()); + poObjBlock->WriteIntCoord(m_nX2, m_nY2, IsCompressedType()); + + poObjBlock->WriteByte(m_nPenId); // Pen index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * class TABMAPObjPLine + * + * Applies to PLINE, MULTIPLINE and REGION object types + **********************************************************************/ + +/********************************************************************** + * TABMAPObjPLine::ReadObj() + * + * Read Object information starting after the object id which should + * have been read by TABMAPObjHdr::ReadNextObj() already. + * This function should be called only by TABMAPObjHdr::ReadNextObj(). + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjPLine::ReadObj(TABMAPObjectBlock *poObjBlock) +{ + m_nCoordBlockPtr = poObjBlock->ReadInt32(); + m_nCoordDataSize = poObjBlock->ReadInt32(); + + if (m_nCoordDataSize & 0x80000000) + { + m_bSmooth = TRUE; + m_nCoordDataSize &= 0x7FFFFFFF; //Take smooth flag out of the value + } + else + { + m_bSmooth = FALSE; + } + +#ifdef TABDUMP + printf("TABMAPObjPLine::ReadObj: m_nCoordDataSize = %d @ %d\n", + m_nCoordDataSize, m_nCoordBlockPtr); +#endif + + // Number of line segments applies only to MULTIPLINE/REGION but not PLINE + if (m_nType == TAB_GEOM_PLINE_C || + m_nType == TAB_GEOM_PLINE ) + { + m_numLineSections = 1; + } + else if (m_nType == TAB_GEOM_V800_REGION || + m_nType == TAB_GEOM_V800_REGION_C || + m_nType == TAB_GEOM_V800_MULTIPLINE || + m_nType == TAB_GEOM_V800_MULTIPLINE_C ) + { + /* V800 REGIONS/MULTIPLINES use an int32 */ + m_numLineSections = poObjBlock->ReadInt32(); + /* ... followed by 33 unknown bytes */ + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadByte(); + } + else + { + /* V300 and V450 REGIONS/MULTIPLINES use an int16 */ + m_numLineSections = poObjBlock->ReadInt16(); + } + +#ifdef TABDUMP + printf("PLINE/REGION: id=%d, type=%d, " + "CoordBlockPtr=%d, CoordDataSize=%d, numLineSect=%d, bSmooth=%d\n", + m_nId, m_nType, m_nCoordBlockPtr, m_nCoordDataSize, + m_numLineSections, m_bSmooth); +#endif + + if (IsCompressedType()) + { + // Region center/label point, relative to compr. coord. origin + // No it's not relative to the Object block center + m_nLabelX = poObjBlock->ReadInt16(); + m_nLabelY = poObjBlock->ReadInt16(); + + // Compressed coordinate origin (present only in compressed case!) + m_nComprOrgX = poObjBlock->ReadInt32(); + m_nComprOrgY = poObjBlock->ReadInt32(); + + m_nLabelX += m_nComprOrgX; + m_nLabelY += m_nComprOrgY; + + m_nMinX = m_nComprOrgX + poObjBlock->ReadInt16(); // Read MBR + m_nMinY = m_nComprOrgY + poObjBlock->ReadInt16(); + m_nMaxX = m_nComprOrgX + poObjBlock->ReadInt16(); + m_nMaxY = m_nComprOrgY + poObjBlock->ReadInt16(); + } + else + { + // Region center/label point, relative to compr. coord. origin + // No it's not relative to the Object block center + m_nLabelX = poObjBlock->ReadInt32(); + m_nLabelY = poObjBlock->ReadInt32(); + + m_nMinX = poObjBlock->ReadInt32(); // Read MBR + m_nMinY = poObjBlock->ReadInt32(); + m_nMaxX = poObjBlock->ReadInt32(); + m_nMaxY = poObjBlock->ReadInt32(); + } + + + if ( ! IsCompressedType() ) + { + // Init. Compr. Origin to a default value in case type is ever changed + m_nComprOrgX = (m_nMinX + m_nMaxX) / 2; + m_nComprOrgY = (m_nMinY + m_nMaxY) / 2; + } + + m_nPenId = poObjBlock->ReadByte(); // Pen index + + if (m_nType == TAB_GEOM_REGION || + m_nType == TAB_GEOM_REGION_C || + m_nType == TAB_GEOM_V450_REGION || + m_nType == TAB_GEOM_V450_REGION_C || + m_nType == TAB_GEOM_V800_REGION || + m_nType == TAB_GEOM_V800_REGION_C ) + { + m_nBrushId = poObjBlock->ReadByte(); // Brush index... REGION only + } + else + { + m_nBrushId = 0; + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * TABMAPObjPLine::WriteObj() + * + * Write Object information with the type+object id + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjPLine::WriteObj(TABMAPObjectBlock *poObjBlock) +{ + // Write object type and id + TABMAPObjHdr::WriteObjTypeAndId(poObjBlock); + + poObjBlock->WriteInt32(m_nCoordBlockPtr); + + // Combine smooth flag in the coord data size. + if (m_bSmooth) + poObjBlock->WriteInt32( m_nCoordDataSize | 0x80000000 ); + else + poObjBlock->WriteInt32( m_nCoordDataSize ); + + // Number of line segments applies only to MULTIPLINE/REGION but not PLINE + if (m_nType == TAB_GEOM_V800_REGION || + m_nType == TAB_GEOM_V800_REGION_C || + m_nType == TAB_GEOM_V800_MULTIPLINE || + m_nType == TAB_GEOM_V800_MULTIPLINE_C ) + { + /* V800 REGIONS/MULTIPLINES use an int32 */ + poObjBlock->WriteInt32(m_numLineSections); + /* ... followed by 33 unknown bytes */ + poObjBlock->WriteZeros(33); + } + else if (m_nType != TAB_GEOM_PLINE_C && + m_nType != TAB_GEOM_PLINE ) + { + /* V300 and V450 REGIONS/MULTIPLINES use an int16 */ + poObjBlock->WriteInt16((GInt16)m_numLineSections); + } + + if (IsCompressedType()) + { + // Region center/label point, relative to compr. coord. origin + // No it's not relative to the Object block center + poObjBlock->WriteInt16((GInt16)(m_nLabelX - m_nComprOrgX)); + poObjBlock->WriteInt16((GInt16)(m_nLabelY - m_nComprOrgY)); + + // Compressed coordinate origin (present only in compressed case!) + poObjBlock->WriteInt32(m_nComprOrgX); + poObjBlock->WriteInt32(m_nComprOrgY); + } + else + { + // Region center/label point + poObjBlock->WriteInt32(m_nLabelX); + poObjBlock->WriteInt32(m_nLabelY); + } + + // MBR + if (IsCompressedType()) + { + // MBR relative to PLINE origin (and not object block center) + poObjBlock->WriteInt16((GInt16)(m_nMinX - m_nComprOrgX)); + poObjBlock->WriteInt16((GInt16)(m_nMinY - m_nComprOrgY)); + poObjBlock->WriteInt16((GInt16)(m_nMaxX - m_nComprOrgX)); + poObjBlock->WriteInt16((GInt16)(m_nMaxY - m_nComprOrgY)); + } + else + { + poObjBlock->WriteInt32(m_nMinX); + poObjBlock->WriteInt32(m_nMinY); + poObjBlock->WriteInt32(m_nMaxX); + poObjBlock->WriteInt32(m_nMaxY); + } + + poObjBlock->WriteByte(m_nPenId); // Pen index + + if (m_nType == TAB_GEOM_REGION || + m_nType == TAB_GEOM_REGION_C || + m_nType == TAB_GEOM_V450_REGION || + m_nType == TAB_GEOM_V450_REGION_C || + m_nType == TAB_GEOM_V800_REGION || + m_nType == TAB_GEOM_V800_REGION_C ) + { + poObjBlock->WriteByte(m_nBrushId); // Brush index... REGION only + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * class TABMAPObjPoint + * + **********************************************************************/ + +/********************************************************************** + * TABMAPObjPoint::ReadObj() + * + * Read Object information starting after the object id + **********************************************************************/ +int TABMAPObjPoint::ReadObj(TABMAPObjectBlock *poObjBlock) +{ + poObjBlock->ReadIntCoord(IsCompressedType(), m_nX, m_nY); + + m_nSymbolId = poObjBlock->ReadByte(); // Symbol index + + SetMBR(m_nX, m_nY, m_nX, m_nY); + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPObjPoint::WriteObj() + * + * Write Object information with the type+object id + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjPoint::WriteObj(TABMAPObjectBlock *poObjBlock) +{ + // Write object type and id + TABMAPObjHdr::WriteObjTypeAndId(poObjBlock); + + poObjBlock->WriteIntCoord(m_nX, m_nY, IsCompressedType()); + + poObjBlock->WriteByte(m_nSymbolId); // Symbol index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * class TABMAPObjFontPoint + * + **********************************************************************/ + +/********************************************************************** + * TABMAPObjFontPoint::ReadObj() + * + * Read Object information starting after the object id + **********************************************************************/ +int TABMAPObjFontPoint::ReadObj(TABMAPObjectBlock *poObjBlock) +{ + m_nSymbolId = poObjBlock->ReadByte(); // Symbol index + m_nPointSize = poObjBlock->ReadByte(); + m_nFontStyle = poObjBlock->ReadInt16(); // font style + + m_nR = poObjBlock->ReadByte(); + m_nG = poObjBlock->ReadByte(); + m_nB = poObjBlock->ReadByte(); + + poObjBlock->ReadByte(); // ??? BG Color ??? + poObjBlock->ReadByte(); // ??? + poObjBlock->ReadByte(); // ??? + + m_nAngle = poObjBlock->ReadInt16(); + + poObjBlock->ReadIntCoord(IsCompressedType(), m_nX, m_nY); + + m_nFontId = poObjBlock->ReadByte(); // Font name index + + SetMBR(m_nX, m_nY, m_nX, m_nY); + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPObjFontPoint::WriteObj() + * + * Write Object information with the type+object id + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjFontPoint::WriteObj(TABMAPObjectBlock *poObjBlock) +{ + // Write object type and id + TABMAPObjHdr::WriteObjTypeAndId(poObjBlock); + + poObjBlock->WriteByte(m_nSymbolId); // symbol shape + poObjBlock->WriteByte(m_nPointSize); + poObjBlock->WriteInt16(m_nFontStyle); // font style + + poObjBlock->WriteByte( m_nR ); + poObjBlock->WriteByte( m_nG ); + poObjBlock->WriteByte( m_nB ); + + poObjBlock->WriteByte( 0 ); + poObjBlock->WriteByte( 0 ); + poObjBlock->WriteByte( 0 ); + + poObjBlock->WriteInt16(m_nAngle); + + poObjBlock->WriteIntCoord(m_nX, m_nY, IsCompressedType()); + + poObjBlock->WriteByte(m_nFontId); // Font name index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * class TABMAPObjCustomPoint + * + **********************************************************************/ + +/********************************************************************** + * TABMAPObjCustomPoint::ReadObj() + * + * Read Object information starting after the object id + **********************************************************************/ +int TABMAPObjCustomPoint::ReadObj(TABMAPObjectBlock *poObjBlock) +{ + m_nUnknown_ = poObjBlock->ReadByte(); // ??? + m_nCustomStyle = poObjBlock->ReadByte(); // 0x01=Show BG, 0x02=Apply Color + + poObjBlock->ReadIntCoord(IsCompressedType(), m_nX, m_nY); + + m_nSymbolId = poObjBlock->ReadByte(); // Symbol index + m_nFontId = poObjBlock->ReadByte(); // Font index + + SetMBR(m_nX, m_nY, m_nX, m_nY); + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPObjCustomPoint::WriteObj() + * + * Write Object information with the type+object id + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjCustomPoint::WriteObj(TABMAPObjectBlock *poObjBlock) +{ + // Write object type and id + TABMAPObjHdr::WriteObjTypeAndId(poObjBlock); + + poObjBlock->WriteByte(m_nUnknown_); // ??? + poObjBlock->WriteByte(m_nCustomStyle); // 0x01=Show BG, 0x02=Apply Color + poObjBlock->WriteIntCoord(m_nX, m_nY, IsCompressedType()); + + poObjBlock->WriteByte(m_nSymbolId); // Symbol index + poObjBlock->WriteByte(m_nFontId); // Font index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * class TABMAPObjRectEllipse + * + **********************************************************************/ + +/********************************************************************** + * TABMAPObjRectEllipse::ReadObj() + * + * Read Object information starting after the object id + **********************************************************************/ +int TABMAPObjRectEllipse::ReadObj(TABMAPObjectBlock *poObjBlock) +{ + if (m_nType == TAB_GEOM_ROUNDRECT || + m_nType == TAB_GEOM_ROUNDRECT_C) + { + if (IsCompressedType()) + { + m_nCornerWidth = poObjBlock->ReadInt16(); + m_nCornerHeight = poObjBlock->ReadInt16(); + } + else + { + m_nCornerWidth = poObjBlock->ReadInt32(); + m_nCornerHeight = poObjBlock->ReadInt32(); + } + } + + poObjBlock->ReadIntCoord(IsCompressedType(), m_nMinX, m_nMinY); + poObjBlock->ReadIntCoord(IsCompressedType(), m_nMaxX, m_nMaxY); + + m_nPenId = poObjBlock->ReadByte(); // Pen index + m_nBrushId = poObjBlock->ReadByte(); // Brush index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPObjRectEllipse::WriteObj() + * + * Write Object information with the type+object id + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjRectEllipse::WriteObj(TABMAPObjectBlock *poObjBlock) +{ + // Write object type and id + TABMAPObjHdr::WriteObjTypeAndId(poObjBlock); + + if (m_nType == TAB_GEOM_ROUNDRECT || + m_nType == TAB_GEOM_ROUNDRECT_C) + { + if (IsCompressedType()) + { + poObjBlock->WriteInt16((GInt16)m_nCornerWidth); + poObjBlock->WriteInt16((GInt16)m_nCornerHeight); + } + else + { + poObjBlock->WriteInt32(m_nCornerWidth); + poObjBlock->WriteInt32(m_nCornerHeight); + } + } + + poObjBlock->WriteIntMBRCoord(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY, + IsCompressedType()); + + poObjBlock->WriteByte(m_nPenId); // Pen index + poObjBlock->WriteByte(m_nBrushId); // Brush index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * class TABMAPObjArc + * + **********************************************************************/ + +/********************************************************************** + * TABMAPObjArc::ReadObj() + * + * Read Object information starting after the object id + **********************************************************************/ +int TABMAPObjArc::ReadObj(TABMAPObjectBlock *poObjBlock) +{ + m_nStartAngle = poObjBlock->ReadInt16(); + m_nEndAngle = poObjBlock->ReadInt16(); + + // An arc is defined by its defining ellipse's MBR: + poObjBlock->ReadIntCoord(IsCompressedType(), + m_nArcEllipseMinX, m_nArcEllipseMinY); + poObjBlock->ReadIntCoord(IsCompressedType(), + m_nArcEllipseMaxX, m_nArcEllipseMaxY); + + // Read the Arc's actual MBR + poObjBlock->ReadIntCoord(IsCompressedType(), m_nMinX, m_nMinY); + poObjBlock->ReadIntCoord(IsCompressedType(), m_nMaxX, m_nMaxY); + + m_nPenId = poObjBlock->ReadByte(); // Pen index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPObjArc::WriteObj() + * + * Write Object information with the type+object id + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjArc::WriteObj(TABMAPObjectBlock *poObjBlock) +{ + // Write object type and id + TABMAPObjHdr::WriteObjTypeAndId(poObjBlock); + + poObjBlock->WriteInt16((GInt16)m_nStartAngle); + poObjBlock->WriteInt16((GInt16)m_nEndAngle); + + // An arc is defined by its defining ellipse's MBR: + poObjBlock->WriteIntMBRCoord(m_nArcEllipseMinX, m_nArcEllipseMinY, + m_nArcEllipseMaxX, m_nArcEllipseMaxY, + IsCompressedType()); + + // Write the Arc's actual MBR + poObjBlock->WriteIntMBRCoord(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY, + IsCompressedType()); + + poObjBlock->WriteByte(m_nPenId); // Pen index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + + +/********************************************************************** + * class TABMAPObjText + * + **********************************************************************/ + +/********************************************************************** + * TABMAPObjText::ReadObj() + * + * Read Object information starting after the object id + **********************************************************************/ +int TABMAPObjText::ReadObj(TABMAPObjectBlock *poObjBlock) +{ + m_nCoordBlockPtr = poObjBlock->ReadInt32(); // String position + m_nCoordDataSize = poObjBlock->ReadInt16(); // String length + m_nTextAlignment = poObjBlock->ReadInt16(); // just./spacing/arrow + + m_nAngle = poObjBlock->ReadInt16(); // Tenths of degree + + m_nFontStyle = poObjBlock->ReadInt16(); // Font style/effect + + m_nFGColorR = poObjBlock->ReadByte(); + m_nFGColorG = poObjBlock->ReadByte(); + m_nFGColorB = poObjBlock->ReadByte(); + + m_nBGColorR = poObjBlock->ReadByte(); + m_nBGColorG = poObjBlock->ReadByte(); + m_nBGColorB = poObjBlock->ReadByte(); + + // Label line end point + poObjBlock->ReadIntCoord(IsCompressedType(), m_nLineEndX, m_nLineEndY); + + // Text Height + if (IsCompressedType()) + m_nHeight = poObjBlock->ReadInt16(); + else + m_nHeight = poObjBlock->ReadInt32(); + + // Font name + m_nFontId = poObjBlock->ReadByte(); // Font name index + + // MBR after rotation + poObjBlock->ReadIntCoord(IsCompressedType(), m_nMinX, m_nMinY); + poObjBlock->ReadIntCoord(IsCompressedType(), m_nMaxX, m_nMaxY); + + m_nPenId = poObjBlock->ReadByte(); // Pen index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPObjText::WriteObj() + * + * Write Object information with the type+object id + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjText::WriteObj(TABMAPObjectBlock *poObjBlock) +{ + // Write object type and id + TABMAPObjHdr::WriteObjTypeAndId(poObjBlock); + + poObjBlock->WriteInt32(m_nCoordBlockPtr); // String position + poObjBlock->WriteInt16((GInt16)m_nCoordDataSize); // String length + poObjBlock->WriteInt16((GInt16)m_nTextAlignment); // just./spacing/arrow + + poObjBlock->WriteInt16((GInt16)m_nAngle); // Tenths of degree + + poObjBlock->WriteInt16(m_nFontStyle); // Font style/effect + + poObjBlock->WriteByte(m_nFGColorR ); + poObjBlock->WriteByte(m_nFGColorG ); + poObjBlock->WriteByte(m_nFGColorB ); + + poObjBlock->WriteByte(m_nBGColorR ); + poObjBlock->WriteByte(m_nBGColorG ); + poObjBlock->WriteByte(m_nBGColorB ); + + // Label line end point + poObjBlock->WriteIntCoord(m_nLineEndX, m_nLineEndY, IsCompressedType()); + + // Text Height + if (IsCompressedType()) + poObjBlock->WriteInt16((GInt16)m_nHeight); + else + poObjBlock->WriteInt32(m_nHeight); + + // Font name + poObjBlock->WriteByte(m_nFontId); // Font name index + + // MBR after rotation + poObjBlock->WriteIntMBRCoord(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY, + IsCompressedType()); + + poObjBlock->WriteByte(m_nPenId); // Pen index + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * class TABMAPObjMultiPoint + * + * Applies to PLINE, MULTIPLINE and REGION object types + **********************************************************************/ + +/********************************************************************** + * TABMAPObjMultiPoint::ReadObj() + * + * Read Object information starting after the object id which should + * have been read by TABMAPObjHdr::ReadNextObj() already. + * This function should be called only by TABMAPObjHdr::ReadNextObj(). + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjMultiPoint::ReadObj(TABMAPObjectBlock *poObjBlock) +{ + m_nCoordBlockPtr = poObjBlock->ReadInt32(); + m_nNumPoints = poObjBlock->ReadInt32(); + + if (IsCompressedType()) + { + m_nCoordDataSize = m_nNumPoints * 2 * 2; + } + else + { + m_nCoordDataSize = m_nNumPoints * 2 * 4; + } + + +#ifdef TABDUMP + printf("MULTIPOINT: id=%d, type=%d, " + "CoordBlockPtr=%d, CoordDataSize=%d, numPoints=%d\n", + m_nId, m_nType, m_nCoordBlockPtr, m_nCoordDataSize, m_nNumPoints); +#endif + + // ????? + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadByte(); + poObjBlock->ReadByte(); + poObjBlock->ReadByte(); + + if (m_nType == TAB_GEOM_V800_MULTIPOINT || + m_nType == TAB_GEOM_V800_MULTIPOINT_C ) + { + /* V800 MULTIPOINTS have another 33 unknown bytes... all zeros */ + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadByte(); + } + + m_nSymbolId = poObjBlock->ReadByte(); + + // ????? + poObjBlock->ReadByte(); + + if (IsCompressedType()) + { + // Region center/label point, relative to compr. coord. origin + // No it's not relative to the Object block center + m_nLabelX = poObjBlock->ReadInt16(); + m_nLabelY = poObjBlock->ReadInt16(); + + // Compressed coordinate origin + m_nComprOrgX = poObjBlock->ReadInt32(); + m_nComprOrgY = poObjBlock->ReadInt32(); + + m_nLabelX += m_nComprOrgX; + m_nLabelY += m_nComprOrgY; + + m_nMinX = m_nComprOrgX + poObjBlock->ReadInt16(); // Read MBR + m_nMinY = m_nComprOrgY + poObjBlock->ReadInt16(); + m_nMaxX = m_nComprOrgX + poObjBlock->ReadInt16(); + m_nMaxY = m_nComprOrgY + poObjBlock->ReadInt16(); + } + else + { + // Region center/label point + m_nLabelX = poObjBlock->ReadInt32(); + m_nLabelY = poObjBlock->ReadInt32(); + + m_nMinX = poObjBlock->ReadInt32(); // Read MBR + m_nMinY = poObjBlock->ReadInt32(); + m_nMaxX = poObjBlock->ReadInt32(); + m_nMaxY = poObjBlock->ReadInt32(); + + // Init. Compr. Origin to a default value in case type is ever changed + m_nComprOrgX = (m_nMinX + m_nMaxX) / 2; + m_nComprOrgY = (m_nMinY + m_nMaxY) / 2; + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * TABMAPObjMultiPoint::WriteObj() + * + * Write Object information with the type+object id + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjMultiPoint::WriteObj(TABMAPObjectBlock *poObjBlock) +{ + // Write object type and id + TABMAPObjHdr::WriteObjTypeAndId(poObjBlock); + + poObjBlock->WriteInt32(m_nCoordBlockPtr); + + // Number of points + poObjBlock->WriteInt32(m_nNumPoints); + + // unknown bytes + poObjBlock->WriteZeros(15); + + if (m_nType == TAB_GEOM_V800_MULTIPOINT || + m_nType == TAB_GEOM_V800_MULTIPOINT_C ) + { + /* V800 MULTIPOINTS have another 33 unknown bytes... all zeros */ + poObjBlock->WriteZeros(33); + } + + // Symbol Id + poObjBlock->WriteByte(m_nSymbolId); + + // ???? + poObjBlock->WriteByte(0); + + // MBR + if (IsCompressedType()) + { + // Region center/label point, relative to compr. coord. origin + // No it's not relative to the Object block center + poObjBlock->WriteInt16((GInt16)(m_nLabelX - m_nComprOrgX)); + poObjBlock->WriteInt16((GInt16)(m_nLabelY - m_nComprOrgY)); + + poObjBlock->WriteInt32(m_nComprOrgX); + poObjBlock->WriteInt32(m_nComprOrgY); + + // MBR relative to object origin (and not object block center) + poObjBlock->WriteInt16((GInt16)(m_nMinX - m_nComprOrgX)); + poObjBlock->WriteInt16((GInt16)(m_nMinY - m_nComprOrgY)); + poObjBlock->WriteInt16((GInt16)(m_nMaxX - m_nComprOrgX)); + poObjBlock->WriteInt16((GInt16)(m_nMaxY - m_nComprOrgY)); + } + else + { + // Region center/label point + poObjBlock->WriteInt32(m_nLabelX); + poObjBlock->WriteInt32(m_nLabelY); + + poObjBlock->WriteInt32(m_nMinX); + poObjBlock->WriteInt32(m_nMinY); + poObjBlock->WriteInt32(m_nMaxX); + poObjBlock->WriteInt32(m_nMaxY); + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * class TABMAPObjCollection + * + **********************************************************************/ + +/********************************************************************** + * TABMAPObjCollection::ReadObj() + * + * Read Object information starting after the object id which should + * have been read by TABMAPObjHdr::ReadNextObj() already. + * This function should be called only by TABMAPObjHdr::ReadNextObj(). + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjCollection::ReadObj(TABMAPObjectBlock *poObjBlock) +{ + int SIZE_OF_REGION_PLINE_MINI_HDR = 24, SIZE_OF_MPOINT_MINI_HDR = 24; + int nVersion = TAB_GEOM_GET_VERSION(m_nType); + + /* Figure the size of the mini-header that we find for each of the + * 3 optional components (center x,y and mbr) + */ + if (IsCompressedType()) + { + /* 6 * int16 */ + SIZE_OF_REGION_PLINE_MINI_HDR = SIZE_OF_MPOINT_MINI_HDR = 12; + } + else + { + /* 6 * int32 */ + SIZE_OF_REGION_PLINE_MINI_HDR = SIZE_OF_MPOINT_MINI_HDR = 24; + } + + if (nVersion >= 800) + { + /* extra 4 bytes for num_segments in Region/Pline mini-headers */ + SIZE_OF_REGION_PLINE_MINI_HDR += 4; + } + + m_nCoordBlockPtr = poObjBlock->ReadInt32(); // pointer into coord block + m_nNumMultiPoints = poObjBlock->ReadInt32(); // no. points in multi point + m_nRegionDataSize = poObjBlock->ReadInt32(); // size of region data inc. section hdrs + m_nPolylineDataSize = poObjBlock->ReadInt32(); // size of multipline data inc. section hdrs + + if (nVersion < 800) + { + // Num Region/Pline section headers (int16 in V650) + m_nNumRegSections = poObjBlock->ReadInt16(); + m_nNumPLineSections = poObjBlock->ReadInt16(); + } + else + { + // Num Region/Pline section headers (int32 in V800) + m_nNumRegSections = poObjBlock->ReadInt32(); + m_nNumPLineSections = poObjBlock->ReadInt32(); + } + + + if (IsCompressedType()) + { + m_nMPointDataSize = m_nNumMultiPoints * 2 * 2; + } + else + { + m_nMPointDataSize = m_nNumMultiPoints * 2 * 4; + } + + /* NB. MapInfo counts 2 extra bytes per Region and Pline section header + * in the RegionDataSize and PolylineDataSize values but those 2 extra + * bytes are not present in the section hdr (possibly due to an alignment + * to a 4 byte boundary in memory in MapInfo?). The real data size in + * the CoordBlock is actually 2 bytes shorter per section header than + * what is written in RegionDataSize and PolylineDataSize values. + * + * We'll adjust the values in memory to be the corrected values. + */ + m_nRegionDataSize = m_nRegionDataSize - (2 * m_nNumRegSections); + m_nPolylineDataSize = m_nPolylineDataSize - (2 * m_nNumPLineSections); + + /* Compute total coord block data size, required when splitting blocks */ + m_nCoordDataSize = 0; + + if(m_nNumRegSections > 0) + { + m_nCoordDataSize += SIZE_OF_REGION_PLINE_MINI_HDR + m_nRegionDataSize; + } + if(m_nNumPLineSections > 0) + { + m_nCoordDataSize += SIZE_OF_REGION_PLINE_MINI_HDR + m_nPolylineDataSize; + } + if(m_nNumMultiPoints > 0) + { + m_nCoordDataSize += SIZE_OF_MPOINT_MINI_HDR + m_nMPointDataSize; + } + + +#ifdef TABDUMP + printf("COLLECTION: id=%d, type=%d (0x%x), " + "CoordBlockPtr=%d, numRegionSections=%d (size=%d+%d), " + "numPlineSections=%d (size=%d+%d), numPoints=%d (size=%d+%d)\n", + m_nId, m_nType, m_nType, m_nCoordBlockPtr, + m_nNumRegSections, m_nRegionDataSize, SIZE_OF_REGION_PLINE_MINI_HDR, + m_nNumPLineSections, m_nPolylineDataSize, SIZE_OF_REGION_PLINE_MINI_HDR, + m_nNumMultiPoints, m_nMPointDataSize, SIZE_OF_MPOINT_MINI_HDR); +#endif + + if (nVersion >= 800) + { + // Extra byte in V800 files... value always 4??? + int nValue = poObjBlock->ReadByte(); + if (nValue != 4) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABMAPObjCollection::ReadObj(): Byte 29 in Collection " + "object header not equal to 4 as expected. Value is %d. " + "Please report this error to the MITAB list so that " + "MITAB can be extended to support this case.", + nValue); + // We don't return right away, the error should be caught at the + // end of this function. + } + } + + // ??? All zeros ??? + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadInt32(); + poObjBlock->ReadByte(); + poObjBlock->ReadByte(); + poObjBlock->ReadByte(); + + m_nMultiPointSymbolId = poObjBlock->ReadByte(); + + poObjBlock->ReadByte(); // ??? + m_nRegionPenId = poObjBlock->ReadByte(); + m_nPolylinePenId = poObjBlock->ReadByte(); + m_nRegionBrushId = poObjBlock->ReadByte(); + + if (IsCompressedType()) + { +#ifdef TABDUMP + printf("COLLECTION: READING ComprOrg @ %d\n", + poObjBlock->GetCurAddress()); +#endif + // Compressed coordinate origin + m_nComprOrgX = poObjBlock->ReadInt32(); + m_nComprOrgY = poObjBlock->ReadInt32(); + + m_nMinX = m_nComprOrgX + poObjBlock->ReadInt16(); // Read MBR + m_nMinY = m_nComprOrgY + poObjBlock->ReadInt16(); + m_nMaxX = m_nComprOrgX + poObjBlock->ReadInt16(); + m_nMaxY = m_nComprOrgY + poObjBlock->ReadInt16(); +#ifdef TABDUMP + printf("COLLECTION: ComprOrgX,Y= (%d,%d)\n", + m_nComprOrgX, m_nComprOrgY); +#endif + } + else + { + m_nMinX = poObjBlock->ReadInt32(); // Read MBR + m_nMinY = poObjBlock->ReadInt32(); + m_nMaxX = poObjBlock->ReadInt32(); + m_nMaxY = poObjBlock->ReadInt32(); + + // Init. Compr. Origin to a default value in case type is ever changed + m_nComprOrgX = (m_nMinX + m_nMaxX) / 2; + m_nComprOrgY = (m_nMinY + m_nMaxY) / 2; + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + + +/********************************************************************** + * TABMAPObjCollection::WriteObj() + * + * Write Object information with the type+object id + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABMAPObjCollection::WriteObj(TABMAPObjectBlock *poObjBlock) +{ + // Write object type and id + TABMAPObjHdr::WriteObjTypeAndId(poObjBlock); + + int nVersion = TAB_GEOM_GET_VERSION(m_nType); + + /* NB. MapInfo counts 2 extra bytes per Region and Pline section header + * in the RegionDataSize and PolylineDataSize values but those 2 extra + * bytes are not present in the section hdr (possibly due to an alignment + * to a 4 byte boundary in memory in MapInfo?). The real data size in + * the CoordBlock is actually 2 bytes shorter per section header than + * what is written in RegionDataSize and PolylineDataSize values. + * + * The values in memory are the corrected values so we need to add 2 bytes + * per section header in the values that we write on disk to emulate + * MapInfo's behavior. + */ + GInt32 nRegionDataSizeMI = m_nRegionDataSize + (2*m_nNumRegSections); + GInt32 nPolylineDataSizeMI = m_nPolylineDataSize+(2*m_nNumPLineSections); + + poObjBlock->WriteInt32(m_nCoordBlockPtr); // pointer into coord block + poObjBlock->WriteInt32(m_nNumMultiPoints); // no. points in multi point + poObjBlock->WriteInt32(nRegionDataSizeMI); // size of region data inc. section hdrs + poObjBlock->WriteInt32(nPolylineDataSizeMI); // size of Mpolyline data inc. sction hdrs + + if (nVersion < 800) + { + // Num Region/Pline section headers (int16 in V650) + poObjBlock->WriteInt16((GInt16)m_nNumRegSections); + poObjBlock->WriteInt16((GInt16)m_nNumPLineSections); + } + else + { + // Num Region/Pline section headers (int32 in V800) + poObjBlock->WriteInt32(m_nNumRegSections); + poObjBlock->WriteInt32(m_nNumPLineSections); + } + + if (nVersion >= 800) + { + // Extra byte in V800 files... value always 4??? + poObjBlock->WriteByte(4); + } + + // Unknown data ????? + poObjBlock->WriteInt32(0); + poObjBlock->WriteInt32(0); + poObjBlock->WriteInt32(0); + poObjBlock->WriteByte(0); + poObjBlock->WriteByte(0); + poObjBlock->WriteByte(0); + + poObjBlock->WriteByte(m_nMultiPointSymbolId); + + poObjBlock->WriteByte(0); + poObjBlock->WriteByte(m_nRegionPenId); + poObjBlock->WriteByte(m_nPolylinePenId); + poObjBlock->WriteByte(m_nRegionBrushId); + + if (IsCompressedType()) + { +#ifdef TABDUMP + printf("COLLECTION: WRITING ComprOrgX,Y= (%d,%d) @ %d\n", + m_nComprOrgX, m_nComprOrgY, poObjBlock->GetCurAddress()); +#endif + // Compressed coordinate origin + poObjBlock->WriteInt32(m_nComprOrgX); + poObjBlock->WriteInt32(m_nComprOrgY); + + poObjBlock->WriteInt16((GInt16)(m_nMinX - m_nComprOrgX)); // MBR + poObjBlock->WriteInt16((GInt16)(m_nMinY - m_nComprOrgY)); + poObjBlock->WriteInt16((GInt16)(m_nMaxX - m_nComprOrgX)); + poObjBlock->WriteInt16((GInt16)(m_nMaxY - m_nComprOrgY)); + } + else + { + poObjBlock->WriteInt32(m_nMinX); // MBR + poObjBlock->WriteInt32(m_nMinY); + poObjBlock->WriteInt32(m_nMaxX); + poObjBlock->WriteInt32(m_nMaxY); + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_maptoolblock.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_maptoolblock.cpp new file mode 100644 index 000000000..700cae2f8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_maptoolblock.cpp @@ -0,0 +1,461 @@ +/********************************************************************** + * $Id: mitab_maptoolblock.cpp,v 1.8 2010-07-07 19:00:15 aboudreault Exp $ + * + * Name: mitab_maptoollock.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABMAPToolBlock class used to handle + * reading/writing of the .MAP files' drawing tool blocks + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999, 2000, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_maptoolblock.cpp,v $ + * Revision 1.8 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.7 2006-11-28 18:49:08 dmorissette + * Completed changes to split TABMAPObjectBlocks properly and produce an + * optimal spatial index (bug 1585) + * + * Revision 1.6 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.5 2000/11/15 04:13:50 daniel + * Fixed writing of TABMAPToolBlock to allocate a new block when full + * + * Revision 1.4 2000/02/28 17:03:30 daniel + * Changed TABMAPBlockManager to TABBinBlockManager + * + * Revision 1.3 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.2 1999/09/26 14:59:37 daniel + * Implemented write support + * + * Revision 1.1 1999/09/16 02:39:17 daniel + * Completed read support for most feature types + * + **********************************************************************/ + +#include "mitab.h" + +/*===================================================================== + * class TABMAPToolBlock + *====================================================================*/ + +#define MAP_TOOL_HEADER_SIZE 8 + +/********************************************************************** + * TABMAPToolBlock::TABMAPToolBlock() + * + * Constructor. + **********************************************************************/ +TABMAPToolBlock::TABMAPToolBlock(TABAccess eAccessMode /*= TABRead*/): + TABRawBinBlock(eAccessMode, TRUE) +{ + m_nNextToolBlock = m_numDataBytes = 0; + + m_numBlocksInChain = 1; // Current block counts as 1 + + m_poBlockManagerRef = NULL; +} + +/********************************************************************** + * TABMAPToolBlock::~TABMAPToolBlock() + * + * Destructor. + **********************************************************************/ +TABMAPToolBlock::~TABMAPToolBlock() +{ + +} + + +/********************************************************************** + * TABMAPToolBlock::EndOfChain() + * + * Return TRUE if we reached the end of the last block in the chain + * TABMAPToolBlocks, or FALSE if there is still data to be read from + * this chain. + **********************************************************************/ +GBool TABMAPToolBlock::EndOfChain() +{ + if (m_pabyBuf && + (m_nCurPos < (m_numDataBytes+MAP_TOOL_HEADER_SIZE) || + m_nNextToolBlock > 0 ) ) + { + return FALSE; // There is still data to be read. + } + + return TRUE; +} + +/********************************************************************** + * TABMAPToolBlock::InitBlockFromData() + * + * Perform some initialization on the block after its binary data has + * been set or changed (or loaded from a file). + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPToolBlock::InitBlockFromData(GByte *pabyBuf, + int nBlockSize, int nSizeUsed, + GBool bMakeCopy /* = TRUE */, + VSILFILE *fpSrc /* = NULL */, + int nOffset /* = 0 */) +{ + int nStatus; + + /*----------------------------------------------------------------- + * First of all, we must call the base class' InitBlockFromData() + *----------------------------------------------------------------*/ + nStatus = TABRawBinBlock::InitBlockFromData(pabyBuf, nBlockSize, nSizeUsed, + bMakeCopy, fpSrc, nOffset); + if (nStatus != 0) + return nStatus; + + /*----------------------------------------------------------------- + * Validate block type + *----------------------------------------------------------------*/ + if (m_nBlockType != TABMAP_TOOL_BLOCK) + { + CPLError(CE_Failure, CPLE_FileIO, + "InitBlockFromData(): Invalid Block Type: got %d expected %d", + m_nBlockType, TABMAP_TOOL_BLOCK); + CPLFree(m_pabyBuf); + m_pabyBuf = NULL; + return -1; + } + + /*----------------------------------------------------------------- + * Init member variables + *----------------------------------------------------------------*/ + GotoByteInBlock(0x002); + m_numDataBytes = ReadInt16(); /* Excluding 8 bytes header */ + + m_nNextToolBlock = ReadInt32(); + + /*----------------------------------------------------------------- + * The read ptr is now located at the beginning of the data part. + *----------------------------------------------------------------*/ + GotoByteInBlock(MAP_TOOL_HEADER_SIZE); + + return 0; +} + +/********************************************************************** + * TABMAPToolBlock::CommitToFile() + * + * Commit the current state of the binary block to the file to which + * it has been previously attached. + * + * This method makes sure all values are properly set in the map object + * block header and then calls TABRawBinBlock::CommitToFile() to do + * the actual writing to disk. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPToolBlock::CommitToFile() +{ + int nStatus = 0; + + if ( m_pabyBuf == NULL ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "CommitToFile(): Block has not been initialized yet!"); + return -1; + } + + /*----------------------------------------------------------------- + * Nothing to do here if block has not been modified + *----------------------------------------------------------------*/ + if (!m_bModified) + return 0; + + /*----------------------------------------------------------------- + * Make sure 8 bytes block header is up to date. + *----------------------------------------------------------------*/ + GotoByteInBlock(0x000); + + WriteInt16(TABMAP_TOOL_BLOCK); // Block type code + WriteInt16((GInt16)(m_nSizeUsed - MAP_TOOL_HEADER_SIZE)); // num. bytes used + WriteInt32(m_nNextToolBlock); + + nStatus = CPLGetLastErrorNo(); + + /*----------------------------------------------------------------- + * OK, call the base class to write the block to disk. + *----------------------------------------------------------------*/ + if (nStatus == 0) + { +#ifdef DEBUG_VERBOSE + CPLDebug("MITAB", "Commiting TOOL block to offset %d", m_nFileOffset); +#endif + nStatus = TABRawBinBlock::CommitToFile(); + } + + return nStatus; +} + +/********************************************************************** + * TABMAPToolBlock::InitNewBlock() + * + * Initialize a newly created block so that it knows to which file it + * is attached, its block size, etc . and then perform any specific + * initialization for this block type, including writing a default + * block header, etc. and leave the block ready to receive data. + * + * This is an alternative to calling ReadFromFile() or InitBlockFromData() + * that puts the block in a stable state without loading any initial + * data in it. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPToolBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize, + int nFileOffset /* = 0*/) +{ +#ifdef DEBUG_VERBOSE + CPLDebug("MITAB", "Instanciating new TOOL block at offset %d", nFileOffset); +#endif + + /*----------------------------------------------------------------- + * Start with the default initialisation + *----------------------------------------------------------------*/ + if ( TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0) + return -1; + + /*----------------------------------------------------------------- + * And then set default values for the block header. + *----------------------------------------------------------------*/ + m_nNextToolBlock = 0; + + m_numDataBytes = 0; + + GotoByteInBlock(0x000); + + if (m_eAccess != TABRead) + { + WriteInt16(TABMAP_TOOL_BLOCK); // Block type code + WriteInt16(0); // num. bytes used, excluding header + WriteInt32(0); // Pointer to next tool block + } + + if (CPLGetLastErrorNo() != 0) + return -1; + + return 0; +} + +/********************************************************************** + * TABMAPToolBlock::SetNextToolBlock() + * + * Set the address (offset from beginning of file) of the drawing tool block + * that follows the current one. + **********************************************************************/ +void TABMAPToolBlock::SetNextToolBlock(GInt32 nNextToolBlockAddress) +{ + m_nNextToolBlock = nNextToolBlockAddress; +} + +/********************************************************************** + * TABMAPToolBlock::SetMAPBlockManagerRef() + * + * Pass a reference to the block manager object for the file this + * block belongs to. The block manager will be used by this object + * when it needs to automatically allocate a new block. + **********************************************************************/ +void TABMAPToolBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr) +{ + m_poBlockManagerRef = poBlockMgr; +}; + + +/********************************************************************** + * TABMAPToolBlock::ReadBytes() + * + * Cover function for TABRawBinBlock::ReadBytes() that will automagically + * load the next coordinate block in the chain before reading the + * requested bytes if we are at the end of the current block and if + * m_nNextToolBlock is a valid block. + * + * Then the control is passed to TABRawBinBlock::ReadBytes() to finish the + * work: + * Copy the number of bytes from the data block's internal buffer to + * the user's buffer pointed by pabyDstBuf. + * + * Passing pabyDstBuf = NULL will only move the read pointer by the + * specified number of bytes as if the copy had happened... but it + * won't crash. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPToolBlock::ReadBytes(int numBytes, GByte *pabyDstBuf) +{ + int nStatus; + + if (m_pabyBuf && + m_nCurPos >= (m_numDataBytes+MAP_TOOL_HEADER_SIZE) && + m_nNextToolBlock > 0) + { + if ( (nStatus=GotoByteInFile(m_nNextToolBlock)) != 0) + { + // Failed.... an error has already been reported. + return nStatus; + } + + GotoByteInBlock(MAP_TOOL_HEADER_SIZE); // Move pointer past header + m_numBlocksInChain++; + } + + return TABRawBinBlock::ReadBytes(numBytes, pabyDstBuf); +} + +/********************************************************************** + * TABMAPToolBlock::WriteBytes() + * + * Cover function for TABRawBinBlock::WriteBytes() that will automagically + * CommitToFile() the current block and create a new one if we are at + * the end of the current block. + * + * Then the control is passed to TABRawBinBlock::WriteBytes() to finish the + * work. + * + * Passing pabySrcBuf = NULL will only move the write pointer by the + * specified number of bytes as if the copy had happened... but it + * won't crash. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPToolBlock::WriteBytes(int nBytesToWrite, GByte *pabySrcBuf) +{ + if (m_eAccess == TABWrite && m_poBlockManagerRef && + (m_nBlockSize - m_nCurPos) < nBytesToWrite) + { + int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock("TOOL"); + SetNextToolBlock(nNewBlockOffset); + + if (CommitToFile() != 0 || + InitNewBlock(m_fp, 512, nNewBlockOffset) != 0) + { + // An error message should have already been reported. + return -1; + } + + m_numBlocksInChain ++; + } + + return TABRawBinBlock::WriteBytes(nBytesToWrite, pabySrcBuf); +} + +/********************************************************************** + * TABMAPToolBlock::CheckAvailableSpace() + * + * Check if an object of the specified type can fit in + * current block. If it can't fit then force committing current block + * and allocating a new one. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABMAPToolBlock::CheckAvailableSpace(int nToolType) +{ + int nBytesToWrite = 0; + + switch(nToolType) + { + case TABMAP_TOOL_PEN: + nBytesToWrite = 11; + break; + case TABMAP_TOOL_BRUSH: + nBytesToWrite = 13; + break; + case TABMAP_TOOL_FONT: + nBytesToWrite = 37; + break; + case TABMAP_TOOL_SYMBOL: + nBytesToWrite = 13; + break; + default: + CPLAssert(FALSE); + } + + if (GetNumUnusedBytes() < nBytesToWrite) + { + int nNewBlockOffset = m_poBlockManagerRef->AllocNewBlock("TOOL"); + SetNextToolBlock(nNewBlockOffset); + + if (CommitToFile() != 0 || + InitNewBlock(m_fp, 512, nNewBlockOffset) != 0) + { + // An error message should have already been reported. + return -1; + } + + m_numBlocksInChain ++; + } + + return 0; +} + + + + +/********************************************************************** + * TABMAPToolBlock::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABMAPToolBlock::Dump(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABMAPToolBlock::Dump() -----\n"); + if (m_pabyBuf == NULL) + { + fprintf(fpOut, "Block has not been initialized yet."); + } + else + { + fprintf(fpOut,"Tool Block (type %d) at offset %d.\n", + m_nBlockType, m_nFileOffset); + fprintf(fpOut," m_numDataBytes = %d\n", m_numDataBytes); + fprintf(fpOut," m_nNextToolBlock = %d\n", m_nNextToolBlock); + } + + fflush(fpOut); +} + +#endif // DEBUG + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_middatafile.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_middatafile.cpp new file mode 100644 index 000000000..5aaae3427 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_middatafile.cpp @@ -0,0 +1,331 @@ +/********************************************************************** + * $Id: mitab_middatafile.cpp,v 1.15 2010-10-12 19:02:40 aboudreault Exp $ + * + * Name: mitab_datfile.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the MIDDATAFile class used to handle + * reading/writing of the MID/MIF files + * Author: Stephane Villeneuve, stephane.v@videotron.ca + * + ********************************************************************** + * Copyright (c) 1999, 2000, Stephane Villeneuve + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_middatafile.cpp,v $ + * Revision 1.15 2010-10-12 19:02:40 aboudreault + * Fixed incomplet patch to handle differently indented lines in mif files (gdal #3694) + * + * Revision 1.14 2006-01-27 13:54:06 fwarmerdam + * fixed memory leak + * + * Revision 1.13 2005/10/04 19:36:10 dmorissette + * Added support for reading collections from MIF files (bug 1126) + * + * Revision 1.12 2005/09/29 19:46:55 dmorissette + * Use "\t" as default delimiter in constructor (Anthony D - bugs 1155 and 37) + * + * Revision 1.11 2004/05/20 13:50:06 fwarmerdam + * Call CPLReadLine(NULL) in Close() method to clean up working buffer. + * + * Revision 1.10 2002/04/26 14:16:49 julien + * Finishing the implementation of Multipoint (support for MIF) + * + * Revision 1.9 2002/04/24 18:37:39 daniel + * Added return statement at end of GetLastLine() + * + * Revision 1.8 2002/04/22 13:49:09 julien + * Add EOF validation in MIDDATAFile::GetLastLine() (Bug 819) + * + * Revision 1.7 2001/09/19 14:49:49 warmerda + * use VSIRewind() instead of rewind() + * + * Revision 1.6 2001/01/22 16:03:58 warmerda + * expanded tabs + * + * Revision 1.5 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.4 1999/12/19 17:41:29 daniel + * Fixed a memory leak + * + * Revision 1.3 1999/11/14 17:43:32 stephane + * Add ifdef to remove CPLError if OGR is define + * + * Revision 1.2 1999/11/11 01:22:05 stephane + * Remove DebugFeature call, Point Reading error, add IsValidFeature() to + * test correctly if we are on a feature + * + * Revision 1.1 1999/11/08 04:16:07 stephane + * First Revision + * + * + **********************************************************************/ + +#include "mitab.h" + +/*===================================================================== + * class MIDDATAFile + * + *====================================================================*/ + +MIDDATAFile::MIDDATAFile() +{ + m_fp = NULL; + m_szLastRead[0] = '\0'; + m_szSavedLine[0] = '\0'; + m_pszDelimiter = "\t"; // Encom 2003 (was NULL) + + m_dfXMultiplier = 1.0; + m_dfYMultiplier = 1.0; + m_dfXDisplacement = 0.0; + m_dfYDisplacement = 0.0; + +} + +MIDDATAFile::~MIDDATAFile() +{ + Close(); +} + +void MIDDATAFile::SaveLine(const char *pszLine) +{ + if (pszLine == NULL) + { + m_szSavedLine[0] = '\0'; + } + else + { + strncpy(m_szSavedLine,pszLine,MIDMAXCHAR); + } +} + +const char *MIDDATAFile::GetSavedLine() +{ + return m_szSavedLine; +} + +int MIDDATAFile::Open(const char *pszFname, const char *pszAccess) +{ + if (m_fp) + { + return -1; + } + + /*----------------------------------------------------------------- + * Validate access mode and make sure we use Text access. + *----------------------------------------------------------------*/ + if (EQUALN(pszAccess, "r", 1)) + { + m_eAccessMode = TABRead; + pszAccess = "rt"; + } + else if (EQUALN(pszAccess, "w", 1)) + { + m_eAccessMode = TABWrite; + pszAccess = "wt"; + } + else + { + return -1; + } + + /*----------------------------------------------------------------- + * Open file for reading + *----------------------------------------------------------------*/ + m_pszFname = CPLStrdup(pszFname); + m_fp = VSIFOpenL(m_pszFname, pszAccess); + + if (m_fp == NULL) + { + CPLFree(m_pszFname); + m_pszFname = NULL; + return -1; + } + + SetEof(FALSE); + return 0; +} + +int MIDDATAFile::Rewind() +{ + if (m_fp == NULL || m_eAccessMode == TABWrite) + return -1; + + else + { + VSIRewindL(m_fp); + SetEof(FALSE); + } + return 0; +} + +int MIDDATAFile::Close() +{ + if (m_fp == NULL) + return 0; + + // Close file + VSIFCloseL(m_fp); + m_fp = NULL; + + // clear readline buffer. + CPLReadLineL( NULL ); + + CPLFree(m_pszFname); + m_pszFname = NULL; + + return 0; + +} + +const char *MIDDATAFile::GetLine() +{ + const char *pszLine; + + if (m_eAccessMode == TABRead) + { + + pszLine = CPLReadLineL(m_fp); + + if (pszLine == NULL) + { + SetEof(TRUE); + m_szLastRead[0] = '\0'; + } + else + { + // skip leading spaces + while(pszLine && (*pszLine == ' ' || *pszLine == '\t') ) + pszLine++; + + strncpy(m_szLastRead,pszLine,MIDMAXCHAR); + } + //if (pszLine) + // printf("%s\n",pszLine); + return pszLine; + } + else + { + CPLAssert(FALSE); + } + return NULL; +} + +const char *MIDDATAFile::GetLastLine() +{ + // Return NULL if EOF + if(GetEof()) + { + return NULL; + } + else if (m_eAccessMode == TABRead) + { + // printf("%s\n",m_szLastRead); + return m_szLastRead; + } + + // We should never get here (Read/Write mode not implemented) + CPLAssert(FALSE); + return NULL; +} + +void MIDDATAFile::WriteLine(const char *pszFormat,...) +{ + va_list args; + + if (m_eAccessMode == TABWrite && m_fp) + { + va_start(args, pszFormat); + CPLString osStr; + osStr.vPrintf( pszFormat, args ); + VSIFWriteL( osStr.c_str(), 1, osStr.size(), m_fp); + va_end(args); + } + else + { + CPLAssert(FALSE); + } +} + + +void MIDDATAFile::SetTranslation(double dfXMul,double dfYMul, + double dfXTran, + double dfYTran) +{ + m_dfXMultiplier = dfXMul; + m_dfYMultiplier = dfYMul; + m_dfXDisplacement = dfXTran; + m_dfYDisplacement = dfYTran; +} + +double MIDDATAFile::GetXTrans(double dfX) +{ + return (dfX * m_dfXMultiplier) + m_dfXDisplacement; +} + +double MIDDATAFile::GetYTrans(double dfY) +{ + return (dfY * m_dfYMultiplier) + m_dfYDisplacement; +} + + +GBool MIDDATAFile::IsValidFeature(const char *pszString) +{ + char **papszToken ; + + papszToken = CSLTokenizeString(pszString); + + // printf("%s\n",pszString); + + if (CSLCount(papszToken) == 0) + { + CSLDestroy(papszToken); + return FALSE; + } + + if (EQUAL(papszToken[0],"NONE") || EQUAL(papszToken[0],"POINT") || + EQUAL(papszToken[0],"LINE") || EQUAL(papszToken[0],"PLINE") || + EQUAL(papszToken[0],"REGION") || EQUAL(papszToken[0],"ARC") || + EQUAL(papszToken[0],"TEXT") || EQUAL(papszToken[0],"RECT") || + EQUAL(papszToken[0],"ROUNDRECT") || EQUAL(papszToken[0],"ELLIPSE") || + EQUAL(papszToken[0],"MULTIPOINT")|| EQUAL(papszToken[0],"COLLECTION") ) + { + CSLDestroy(papszToken); + return TRUE; + } + + CSLDestroy(papszToken); + return FALSE; + +} + + +GBool MIDDATAFile::GetEof() +{ + return m_bEof; +} + + +void MIDDATAFile::SetEof(GBool bEof) +{ + m_bEof = bEof; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_miffile.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_miffile.cpp new file mode 100644 index 000000000..813e7eeda --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_miffile.cpp @@ -0,0 +1,2295 @@ +/********************************************************************** + * $Id: mitab_miffile.cpp,v 1.58 2011-09-22 21:57:46 dmorissette Exp $ + * + * Name: mitab_miffile.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the MIDFile class. + * To be used by external programs to handle reading/writing of + * features from/to MID/MIF datasets. + * Author: Stephane Villeneuve, stephane.v@videotron.ca + * + ********************************************************************** + * Copyright (c) 1999-2003, Stephane Villeneuve + * Copyright (c) 2011-2013, Even Rouault + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_miffile.cpp,v $ + * Revision 1.58 2011-09-22 21:57:46 dmorissette + * Fixed problem with tab delimiter used in MIF files (GDAL #4257) + * + * Revision 1.57 2010-10-15 12:06:44 aboudreault + * Fixed crash when trying to get the same mitab mif feature twice (GDAL #3765) + * + * Revision 1.56 2010-10-12 19:02:40 aboudreault + * Fixed incomplet patch to handle differently indented lines in mif files (gdal #3694) + * + * Revision 1.55 2010-10-08 18:50:52 aboudreault + * Fixed handle differently indented lines in mif files. (GDAL bug #3694) + * + * Revision 1.54 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.53 2010-05-07 19:39:19 aboudreault + * Fixed MIF driver: doesn't create a layer defn at layer creation (bug 2180) + * + * Revision 1.52 2010-01-07 20:39:12 aboudreault + * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141) + * + * Revision 1.51 2009-07-27 14:08:41 dmorissette + * Fixed dataset version check in AddFieldNative for type TABFDateTime + * + * Revision 1.50 2008-12-17 14:55:20 aboudreault + * Fixed mitab mif/mid importer fails when a Text geometry have an empty + * text value (bug 1978) + * + * Revision 1.49 2008/11/17 22:06:21 aboudreault + * Added support to use OFTDateTime/OFTDate/OFTTime type when compiled with + * OGR and fixed reading/writing support for these types. + * + * Revision 1.48 2008/09/26 14:40:24 aboudreault + * Fixed bug: MITAB doesn't support writing DateTime type (bug 1948) + * + * Revision 1.47 2008/03/05 20:35:39 dmorissette + * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859) + * + * Revision 1.46 2008/02/01 20:30:59 dmorissette + * Use %.15g instead of %.16g as number precision in .MIF output + * + * Revision 1.45 2008/01/29 21:56:39 dmorissette + * Update dataset version properly for Date/Time/DateTime field types (#1754) + * + * Revision 1.44 2008/01/29 20:46:32 dmorissette + * Added support for v9 Time and DateTime fields (byg 1754) + * + * Revision 1.43 2007/09/14 15:35:21 dmorissette + * Fixed problem with MIF parser being confused by special attribute + * names (bug 1795) + * + * Revision 1.42 2007/06/12 13:52:37 dmorissette + * Added IMapInfoFile::SetCharset() method (bug 1734) + * + * Revision 1.41 2005/10/13 20:12:03 fwarmerdam + * layers with just regions can't be set as type wkbPolygon because they may + * have multipolygons (bug GDAL:958) + * http://bugzilla.remotesensing.org/show_bug.cgi?id=958 + * + * Revision 1.40 2005/10/12 14:03:02 fwarmerdam + * Fixed problem with white space parsing in mitab_miffile.cpp (bug GDAL:954) + * + * Revision 1.39 2005/10/04 19:36:10 dmorissette + * Added support for reading collections from MIF files (bug 1126) + * + * Revision 1.38 2004/02/27 21:04:14 fwarmerdam + * dont write MIF header if file is readonly - gdal bugzilla 509 + * + * Revision 1.37 2003/12/19 07:54:50 fwarmerdam + * write mif header on close if not already written out + * + * Revision 1.36 2003/08/13 02:49:02 dmorissette + * Use tab as default delimiter if not explicitly specified (Anthony D, bug 37) + * + * Revision 1.35 2003/01/30 22:42:39 daniel + * Fixed crash in ParseMIFHeader() when .mif doesn't contain a DATA line + * + * Revision 1.34 2002/09/23 12:53:29 warmerda + * fix memory leak of m_pszIndex + * + * Revision 1.33 2002/05/08 15:10:48 julien + * Implement MIFFile::SetMIFCoordSys in mitab_capi.cpp (Bug 984) + * + * Revision 1.32 2002/04/26 14:16:49 julien + * Finishing the implementation of Multipoint (support for MIF) + * + * Revision 1.31 2001/09/19 21:39:15 warmerda + * get extents efficiently + * + * Revision 1.30 2001/09/19 14:31:22 warmerda + * added m_nPreloadedId to keep track of preloaded line + * + * Revision 1.29 2001/09/14 19:14:43 warmerda + * added attribute query support + * + * Revision 1.28 2001/08/10 17:49:01 warmerda + * fixed a few memory leaks + * + * Revision 1.27 2001/03/15 03:57:51 daniel + * Added implementation for new OGRLayer::GetExtent(), returning data MBR. + * + * Revision 1.26 2001/03/09 04:14:19 daniel + * Fixed problem creating new files with mixed case extensions (e.g. ".Tab") + * + * Revision 1.25 2001/03/09 03:51:48 daniel + * Fixed writing MIF header: missing break; for decimal fields + * + * Revision 1.24 2001/02/27 19:59:05 daniel + * Enabled spatial filter in IMapInfoFile::GetNextFeature(), and avoid + * unnecessary feature cloning in GetNextFeature() and GetFeature() + * + * Revision 1.23 2001/01/23 21:23:42 daniel + * Added projection bounds lookup table, called from TABFile::SetProjInfo() + * + * Revision 1.22 2001/01/22 16:03:58 warmerda + * expanded tabs + * + * Revision 1.21 2000/12/15 05:38:38 daniel + * Produce Warning instead of an error when nWidth>254 in AddFieldNative() + * + * Revision 1.20 2000/11/14 06:15:37 daniel + * Handle '\t' as spaces in parsing, and fixed GotoFeature() to avoid calling + * ResetReading() when reading forward. + * + * Revision 1.19 2000/07/04 01:50:40 warmerda + * Removed unprotected debugging printf. + * + * Revision 1.18 2000/06/28 00:32:04 warmerda + * Make GetFeatureCountByType() actually work if bForce is TRUE + * Collect detailed (by feature type) feature counts in PreParse(). + * + * Revision 1.17 2000/04/27 15:46:25 daniel + * Make SetFeatureDefn() use AddFieldNative(), scan field names for invalid + * chars, and map field width=0 (variable length in OGR) to valid defaults + * + * Revision 1.16 2000/03/27 03:37:59 daniel + * Handle bounds in CoordSys for read and write, + handle point SYMBOL line as + * optional + fixed reading of bounds in PreParseFile() + * + * Revision 1.15 2000/02/28 17:05:06 daniel + * Added support for index and unique directives for read and write + * + * Revision 1.14 2000/01/28 07:32:25 daniel + * Validate char field width (must be <= 254 chars) + * + * Revision 1.13 2000/01/24 19:51:33 warmerda + * AddFieldNative should not fail for read-only datasets + * + * Revision 1.12 2000/01/18 23:13:41 daniel + * Implemented AddFieldNative() + * + * ... + * + * Revision 1.1 1999/11/08 04:16:07 stephane + * First Revision + * + **********************************************************************/ + +#include "mitab.h" +#include "mitab_utils.h" +#include + +/*===================================================================== + * class MIFFile + *====================================================================*/ + + +/********************************************************************** + * MIFFile::MIFFile() + * + * Constructor. + **********************************************************************/ +MIFFile::MIFFile() +{ + m_pszFname = NULL; + m_nVersion = 300; + + // Tab is default delimiter in MIF spec if not explicitly specified. Use + // that by default for read mode. In write mode, we will use "," as + // delimiter since it's more common than tab (we do this in Open()) + m_pszDelimiter = CPLStrdup("\t"); + + m_pszUnique = NULL; + m_pszIndex = NULL; + m_pszCoordSys = NULL; + + m_paeFieldType = NULL; + m_pabFieldIndexed = NULL; + m_pabFieldUnique = NULL; + + m_dfXMultiplier = 1.0; + m_dfYMultiplier = 1.0; + m_dfXDisplacement = 0.0; + m_dfYDisplacement = 0.0; + + m_poMIDFile = NULL; + m_poMIFFile = NULL; + m_nPreloadedId = 0; + + m_poDefn = NULL; + m_poSpatialRef = NULL; + + m_nCurFeatureId = 0; + m_nFeatureCount = 0; + m_nWriteFeatureId = -1; + m_poCurFeature = NULL; + + m_bPreParsed = FALSE; + m_nAttribut = 0; + m_bHeaderWrote = FALSE; + m_nPoints = m_nLines = m_nRegions = m_nTexts = 0; + + m_bExtentsSet = FALSE; +} + +/********************************************************************** + * MIFFile::~MIFFile() + * + * Destructor. + **********************************************************************/ +MIFFile::~MIFFile() +{ + Close(); +} + +/********************************************************************** + * MIFFile::Open() + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int MIFFile::Open(const char *pszFname, TABAccess eAccess, + GBool bTestOpenNoError /*=FALSE*/ ) +{ + char *pszTmpFname = NULL; + int nFnameLen = 0; + + CPLErrorReset(); + + if (m_poMIFFile) + { + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: object already contains an open file"); + + return -1; + } + + /*----------------------------------------------------------------- + * Validate access mode + *----------------------------------------------------------------*/ + const char* pszAccess = NULL; + if (eAccess == TABRead) + { + m_eAccessMode = TABRead; + pszAccess = "rt"; + } + else if (eAccess == TABWrite) + { + m_eAccessMode = TABWrite; + pszAccess = "wt"; + + // In write mode, use "," as delimiter since it's more common than tab + CPLFree(m_pszDelimiter); + m_pszDelimiter = CPLStrdup(","); + } + else + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed: access mode \"%d\" not supported", eAccess); + else + CPLErrorReset(); + + return -1; + } + + /*----------------------------------------------------------------- + * Make sure filename has a .MIF or .MID extension... + *----------------------------------------------------------------*/ + m_pszFname = CPLStrdup(pszFname); + nFnameLen = strlen(m_pszFname); + if (nFnameLen > 4 && (strcmp(m_pszFname+nFnameLen-4, ".MID")==0 || + strcmp(m_pszFname+nFnameLen-4, ".MIF")==0 ) ) + strcpy(m_pszFname+nFnameLen-4, ".MIF"); + else if (nFnameLen > 4 && (EQUAL(m_pszFname+nFnameLen-4, ".mid") || + EQUAL(m_pszFname+nFnameLen-4, ".mif") ) ) + strcpy(m_pszFname+nFnameLen-4, ".mif"); + else + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed for %s: invalid filename extension", + m_pszFname); + else + CPLErrorReset(); + + return -1; + } + + pszTmpFname = CPLStrdup(m_pszFname); + + /*----------------------------------------------------------------- + * Open .MIF file + *----------------------------------------------------------------*/ + +#ifndef _WIN32 + /*----------------------------------------------------------------- + * On Unix, make sure extension uses the right cases + * We do it even for write access because if a file with the same + * extension already exists we want to overwrite it. + *----------------------------------------------------------------*/ + TABAdjustFilenameExtension(pszTmpFname); +#endif + + m_poMIFFile = new MIDDATAFile; + + if (m_poMIFFile->Open(pszTmpFname, pszAccess) != 0) + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_NotSupported, + "Unable to open %s.", pszTmpFname); + else + CPLErrorReset(); + + CPLFree(pszTmpFname); + Close(); + + return -1; + } + + /*----------------------------------------------------------------- + * Read MIF File Header + *----------------------------------------------------------------*/ + int bIsEmpty = FALSE; + if (m_eAccessMode == TABRead && ParseMIFHeader(&bIsEmpty) != 0) + { + Close(); + + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_NotSupported, + "Failed parsing header in %s.", m_pszFname); + else + CPLErrorReset(); + + CPLFree(pszTmpFname); + + return -1; + } + + if ( m_nAttribut > 0 || m_eAccessMode == TABWrite ) + { + /*----------------------------------------------------------------- + * Open .MID file + *----------------------------------------------------------------*/ + if (nFnameLen > 4 && strcmp(pszTmpFname+nFnameLen-4, ".MIF")==0) + strcpy(pszTmpFname+nFnameLen-4, ".MID"); + else + strcpy(pszTmpFname+nFnameLen-4, ".mid"); + +#ifndef _WIN32 + TABAdjustFilenameExtension(pszTmpFname); +#endif + + m_poMIDFile = new MIDDATAFile; + + if (m_poMIDFile->Open(pszTmpFname, pszAccess) !=0) + { + if (m_eAccessMode == TABWrite) + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_NotSupported, + "Unable to open %s.", pszTmpFname); + else + CPLErrorReset(); + + CPLFree(pszTmpFname); + Close(); + + return -1; + } + else + { + CPLDebug("MITAB", + "%s is not found, although %d attributes are declared", + pszTmpFname, m_nAttribut); + delete m_poMIDFile; + m_poMIDFile = NULL; + } + } + } + + CPLFree(pszTmpFname); + pszTmpFname = NULL; + + /*----------------------------------------------------------------- + * In write access, set some defaults + *----------------------------------------------------------------*/ + if (m_eAccessMode == TABWrite) + { + m_nVersion = 300; + m_pszCharset = CPLStrdup("Neutral"); + } + + /* Put the MID file at the correct location, on the first feature */ + if (m_eAccessMode == TABRead && (m_poMIDFile != NULL && !bIsEmpty && m_poMIDFile->GetLine() == NULL)) + { + Close(); + + if (bTestOpenNoError) + CPLErrorReset(); + + return -1; + } + + m_poMIFFile->SetTranslation(m_dfXMultiplier,m_dfYMultiplier, + m_dfXDisplacement, m_dfYDisplacement); + if( m_poMIDFile != NULL ) + m_poMIDFile->SetTranslation(m_dfXMultiplier,m_dfYMultiplier, + m_dfXDisplacement, m_dfYDisplacement); + m_poMIFFile->SetDelimiter(m_pszDelimiter); + if( m_poMIDFile != NULL ) + m_poMIDFile->SetDelimiter(m_pszDelimiter); + + /*------------------------------------------------------------- + * Set geometry type if the geometry objects are uniform. + *------------------------------------------------------------*/ + int numPoints=0, numRegions=0, numTexts=0, numLines=0; + + if( GetFeatureCountByType( numPoints, numLines, numRegions, numTexts, + FALSE ) == 0 ) + { + numPoints += numTexts; + if( numPoints > 0 && numLines == 0 && numRegions == 0 ) + m_poDefn->SetGeomType( wkbPoint ); + else if( numPoints == 0 && numLines > 0 && numRegions == 0 ) + m_poDefn->SetGeomType( wkbLineString ); + else + { + /* we leave it unknown indicating a mixture */ + } + } + + /* A newly created layer should have OGRFeatureDefn */ + if (m_poDefn == NULL) + { + char *pszFeatureClassName = TABGetBasename(m_pszFname); + m_poDefn = new OGRFeatureDefn(pszFeatureClassName); + CPLFree(pszFeatureClassName); + // Ref count defaults to 0... set it to 1 + m_poDefn->Reference(); + } + + return 0; +} + +/********************************************************************** + * MIFFile::ParseMIFHeader() + * + * Scan the header of a MIF file, and store any useful information into + * class members. The main piece of information being the fields + * definition that we use to build the OGRFeatureDefn for this file. + * + * This private method should be used only during the Open() call. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int MIFFile::ParseMIFHeader(int* pbIsEmpty) +{ + GBool bColumns = FALSE, bAllColumnsRead = FALSE; + int nColumns = 0; + GBool bCoordSys = FALSE; + char *pszTmp; + + + const char *pszLine; + char **papszToken; + + *pbIsEmpty = FALSE; + + char *pszFeatureClassName = TABGetBasename(m_pszFname); + m_poDefn = new OGRFeatureDefn(pszFeatureClassName); + CPLFree(pszFeatureClassName); + // Ref count defaults to 0... set it to 1 + m_poDefn->Reference(); + + + if (m_eAccessMode != TABRead) + { + CPLError(CE_Failure, CPLE_NotSupported, + "ParseMIDFile() can be used only with Read access."); + return -1; + } + + + /*----------------------------------------------------------------- + * Parse header until we find the "Data" line + *----------------------------------------------------------------*/ + while (((pszLine = m_poMIFFile->GetLine()) != NULL) && + ((bAllColumnsRead == FALSE) || !EQUALN(pszLine,"Data",4))) + { + if (bColumns == TRUE && nColumns >0) + { + if (AddFields(pszLine) == 0) + { + nColumns--; + if (nColumns == 0) + { + bAllColumnsRead = TRUE; + bColumns = FALSE; + } + } + else + { + bColumns = FALSE; + } + } + else if (EQUALN(pszLine,"VERSION",7)) + { + papszToken = CSLTokenizeStringComplex(pszLine," ()\t",TRUE,FALSE); + bColumns = FALSE; bCoordSys = FALSE; + if (CSLCount(papszToken) == 2) + m_nVersion = atoi(papszToken[1]); + + CSLDestroy(papszToken); + + } + else if (EQUALN(pszLine,"CHARSET",7)) + { + papszToken = CSLTokenizeStringComplex(pszLine," ()\t",TRUE,FALSE); + bColumns = FALSE; bCoordSys = FALSE; + + if (CSLCount(papszToken) == 2) + { + CPLFree(m_pszCharset); + m_pszCharset = CPLStrdup(papszToken[1]); + } + CSLDestroy(papszToken); + + } + else if (EQUALN(pszLine,"DELIMITER",9)) + { + papszToken = CSLTokenizeStringComplex(pszLine," ()\t",TRUE,FALSE); + bColumns = FALSE; bCoordSys = FALSE; + + if (CSLCount(papszToken) == 2) + { + CPLFree(m_pszDelimiter); + m_pszDelimiter = CPLStrdup(papszToken[1]); + } + CSLDestroy(papszToken); + + } + else if (EQUALN(pszLine,"UNIQUE",6)) + { + bColumns = FALSE; bCoordSys = FALSE; + + m_pszUnique = CPLStrdup(pszLine + 6); + } + else if (EQUALN(pszLine,"INDEX",5)) + { + bColumns = FALSE; bCoordSys = FALSE; + + m_pszIndex = CPLStrdup(pszLine + 5); + } + else if (EQUALN(pszLine,"COORDSYS",8) ) + { + bCoordSys = TRUE; + m_pszCoordSys = CPLStrdup(pszLine + 9); + + // Extract bounds if present + char **papszFields; + papszFields = CSLTokenizeStringComplex(m_pszCoordSys, " ,()\t", + TRUE, FALSE ); + int iBounds = CSLFindString( papszFields, "Bounds" ); + if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields)) + { + m_dXMin = CPLAtof(papszFields[++iBounds]); + m_dYMin = CPLAtof(papszFields[++iBounds]); + m_dXMax = CPLAtof(papszFields[++iBounds]); + m_dYMax = CPLAtof(papszFields[++iBounds]); + m_bBoundsSet = TRUE; + } + CSLDestroy( papszFields ); + } + else if (EQUALN(pszLine,"TRANSFORM",9)) + { + papszToken = CSLTokenizeStringComplex(pszLine," ,\t",TRUE,FALSE); + bColumns = FALSE; bCoordSys = FALSE; + + if (CSLCount(papszToken) == 5) + { + m_dfXMultiplier = CPLAtof(papszToken[1]); + m_dfYMultiplier = CPLAtof(papszToken[2]); + m_dfXDisplacement = CPLAtof(papszToken[3]); + m_dfYDisplacement = CPLAtof(papszToken[4]); + + if (m_dfXMultiplier == 0.0) + m_dfXMultiplier = 1.0; + if (m_dfYMultiplier == 0.0) + m_dfYMultiplier = 1.0; + } + CSLDestroy(papszToken); + } + else if (EQUALN(pszLine,"COLUMNS",7)) + { + papszToken = CSLTokenizeStringComplex(pszLine," ()\t",TRUE,FALSE); + bCoordSys = FALSE; + bColumns = TRUE; + if (CSLCount(papszToken) == 2) + { + nColumns = atoi(papszToken[1]); + m_nAttribut = nColumns; + if (nColumns == 0) + { + // Permit to 0 columns + bAllColumnsRead = TRUE; + bColumns = FALSE; + } + } + else + { + bColumns = FALSE; + m_nAttribut = 0; + } + CSLDestroy(papszToken); + } + else if (bCoordSys == TRUE) + { + pszTmp = m_pszCoordSys; + m_pszCoordSys = CPLStrdup(CPLSPrintf("%s %s",m_pszCoordSys, + pszLine)); + CPLFree(pszTmp); + //printf("Reading CoordSys\n"); + // Reading CoordSys + } + + } + + if (!bAllColumnsRead) + { + CPLError(CE_Failure, CPLE_NotSupported, + "COLUMNS keyword not found or invalid number of columns read in %s. File may be corrupt.", + m_pszFname); + return -1; + } + + if ((pszLine = m_poMIFFile->GetLastLine()) == NULL || + EQUALN(m_poMIFFile->GetLastLine(),"DATA",4) == FALSE) + { + CPLError(CE_Failure, CPLE_NotSupported, + "DATA keyword not found in %s. File may be corrupt.", + m_pszFname); + return -1; + } + + /*----------------------------------------------------------------- + * Move pointer to first line of first object + *----------------------------------------------------------------*/ + while (((pszLine = m_poMIFFile->GetLine()) != NULL) && + m_poMIFFile->IsValidFeature(pszLine) == FALSE) + ; + + *pbIsEmpty = (pszLine == NULL); + + /*----------------------------------------------------------------- + * Check for Unique and Indexed flags + *----------------------------------------------------------------*/ + if (m_pszIndex) + { + papszToken = CSLTokenizeStringComplex(m_pszIndex," ,\t",TRUE,FALSE); + for(int i=0; papszToken && papszToken[i]; i++) + { + int nVal = atoi(papszToken[i]); + if (nVal > 0 && nVal <= m_poDefn->GetFieldCount()) + m_pabFieldIndexed[nVal-1] = TRUE; + } + CSLDestroy(papszToken); + } + + if (m_pszUnique) + { + papszToken = CSLTokenizeStringComplex(m_pszUnique," ,\t",TRUE,FALSE); + for(int i=0; papszToken && papszToken[i]; i++) + { + int nVal = atoi(papszToken[i]); + if (nVal > 0 && nVal <= m_poDefn->GetFieldCount()) + m_pabFieldUnique[nVal-1] = TRUE; + } + CSLDestroy(papszToken); + } + + return 0; + +} + +/************************************************************************/ +/* AddFields() */ +/************************************************************************/ + +int MIFFile::AddFields(const char *pszLine) +{ + char **papszToken; + int nStatus = 0,numTok; + + CPLAssert(m_bHeaderWrote == FALSE); + papszToken = CSLTokenizeStringComplex(pszLine," (,)\t",TRUE,FALSE); + numTok = CSLCount(papszToken); + + if (numTok >= 3 && EQUAL(papszToken[1], "char")) + { + /*------------------------------------------------- + * CHAR type + *------------------------------------------------*/ + nStatus = AddFieldNative(papszToken[0], TABFChar, + atoi(papszToken[2])); + } + else if (numTok >= 2 && EQUAL(papszToken[1], "integer")) + { + if (numTok == 2) + { + /*------------------------------------------------- + * INTEGER type without a specified width + *------------------------------------------------*/ + nStatus = AddFieldNative(papszToken[0], TABFInteger); + } + else if (numTok > 2) + { + /*------------------------------------------------- + * INTEGER type with a specified width + *------------------------------------------------*/ + nStatus = AddFieldNative(papszToken[0], TABFInteger, atoi(papszToken[2])); + } + } + else if (numTok >= 2 && EQUAL(papszToken[1], "smallint")) + { + if (numTok == 2) + { + /*------------------------------------------------- + * SMALLINT type without a specified width + *------------------------------------------------*/ + nStatus = AddFieldNative(papszToken[0], TABFSmallInt); + } + else if (numTok > 2) + { + /*------------------------------------------------- + * SMALLINT type with a specified width + *------------------------------------------------*/ + nStatus = AddFieldNative(papszToken[0], TABFSmallInt, atoi(papszToken[2])); + } + } + else if (numTok >= 4 && EQUAL(papszToken[1], "decimal")) + { + /*------------------------------------------------- + * DECIMAL type + *------------------------------------------------*/ + nStatus = AddFieldNative(papszToken[0], TABFDecimal, + atoi(papszToken[2]), atoi(papszToken[3])); + } + else if (numTok >= 2 && EQUAL(papszToken[1], "float")) + { + /*------------------------------------------------- + * FLOAT type + *------------------------------------------------*/ + nStatus = AddFieldNative(papszToken[0], TABFFloat); + } + else if (numTok >= 2 && EQUAL(papszToken[1], "date")) + { + /*------------------------------------------------- + * DATE type (returned as a string: "DD/MM/YYYY" or "YYYYMMDD") + *------------------------------------------------*/ + nStatus = AddFieldNative(papszToken[0], TABFDate); + } + else if (numTok >= 2 && EQUAL(papszToken[1], "time")) + { + /*------------------------------------------------- + * TIME type (v900, returned as a string: "HH:MM:SS" or "HHMMSSmmm") + *------------------------------------------------*/ + nStatus = AddFieldNative(papszToken[0], TABFTime); + } + else if (numTok >= 2 && EQUAL(papszToken[1], "datetime")) + { + /*------------------------------------------------- + * DATETIME type (v900, returned as a string: "DD/MM/YYYY HH:MM:SS", + * "YYYY/MM/DD HH:MM:SS" or "YYYYMMDDHHMMSSmmm") + *------------------------------------------------*/ + nStatus = AddFieldNative(papszToken[0], TABFDateTime); + } + else if (numTok >= 2 && EQUAL(papszToken[1], "logical")) + { + /*------------------------------------------------- + * LOGICAL type (value "T" or "F") + *------------------------------------------------*/ + nStatus = AddFieldNative(papszToken[0], TABFLogical); + } + else + nStatus = -1; // Unrecognized field type or line corrupt + + CSLDestroy(papszToken); + papszToken = NULL; + + if (nStatus != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed to parse field definition in file %s", m_pszFname); + return -1; + } + + return 0; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig MIFFile::GetFeatureCount (int bForce) +{ + + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL ) + return OGRLayer::GetFeatureCount( bForce ); + else + { + if (bForce == TRUE) + PreParseFile(); + + if (m_bPreParsed) + return m_nFeatureCount; + else + return -1; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void MIFFile::ResetReading() + +{ + const char *pszLine; + + m_poMIFFile->Rewind(); + + while ((pszLine = m_poMIFFile->GetLine()) != NULL) + if (EQUALN(pszLine,"DATA",4)) + break; + + while ((pszLine = m_poMIFFile->GetLine()) != NULL) + { + if (m_poMIFFile->IsValidFeature(pszLine)) + break; + } + + if( m_poMIDFile != NULL ) + { + m_poMIDFile->Rewind(); + m_poMIDFile->GetLine(); + } + + // We're positioned on first feature. Feature Ids start at 1. + if (m_poCurFeature) + { + delete m_poCurFeature; + m_poCurFeature = NULL; + } + + m_nCurFeatureId = 0; + m_nPreloadedId = 1; +} + +/************************************************************************/ +/* PreParseFile() */ +/************************************************************************/ + +void MIFFile::PreParseFile() +{ + char **papszToken = NULL; + const char *pszLine; + + GBool bPLine = FALSE; + GBool bText = FALSE; + + if (m_bPreParsed == TRUE) + return; + + m_poMIFFile->Rewind(); + + while ((pszLine = m_poMIFFile->GetLine()) != NULL) + if (EQUALN(pszLine,"DATA",4)) + break; + + m_nPoints = m_nLines = m_nRegions = m_nTexts = 0; + + while ((pszLine = m_poMIFFile->GetLine()) != NULL) + { + if (m_poMIFFile->IsValidFeature(pszLine)) + { + bPLine = FALSE; + bText = FALSE; + m_nFeatureCount++; + } + + CSLDestroy(papszToken); + papszToken = CSLTokenizeString2(pszLine, " \t", CSLT_HONOURSTRINGS); + + if (EQUALN(pszLine,"POINT",5)) + { + m_nPoints++; + if (CSLCount(papszToken) == 3) + { + UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[1])), + m_poMIFFile->GetYTrans(CPLAtof(papszToken[2]))); + } + + } + else if (EQUALN(pszLine,"LINE",4) || + EQUALN(pszLine,"RECT",4) || + EQUALN(pszLine,"ROUNDRECT",9) || + EQUALN(pszLine,"ARC",3) || + EQUALN(pszLine,"ELLIPSE",7)) + { + if (CSLCount(papszToken) == 5) + { + m_nLines++; + UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[1])), + m_poMIFFile->GetYTrans(CPLAtof(papszToken[2]))); + UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[3])), + m_poMIFFile->GetYTrans(CPLAtof(papszToken[4]))); + } + } + else if (EQUALN(pszLine,"REGION",6) ) + { + m_nRegions++; + bPLine = TRUE; + } + else if( EQUALN(pszLine,"PLINE",5)) + { + m_nLines++; + bPLine = TRUE; + } + else if (EQUALN(pszLine,"TEXT",4)) + { + m_nTexts++; + bText = TRUE; + } + else if (bPLine == TRUE) + { + if (CSLCount(papszToken) == 2 && + strchr("-.0123456789", papszToken[0][0]) != NULL) + { + UpdateExtents( m_poMIFFile->GetXTrans(CPLAtof(papszToken[0])), + m_poMIFFile->GetYTrans(CPLAtof(papszToken[1]))); + } + } + else if (bText == TRUE) + { + if (CSLCount(papszToken) == 4 && + strchr("-.0123456789", papszToken[0][0]) != NULL) + { + UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[0])), + m_poMIFFile->GetYTrans(CPLAtof(papszToken[1]))); + UpdateExtents(m_poMIFFile->GetXTrans(CPLAtof(papszToken[2])), + m_poMIFFile->GetYTrans(CPLAtof(papszToken[3]))); + } + } + + } + + CSLDestroy(papszToken); + + m_poMIFFile->Rewind(); + + while ((pszLine = m_poMIFFile->GetLine()) != NULL) + if (EQUALN(pszLine,"DATA",4)) + break; + + while ((pszLine = m_poMIFFile->GetLine()) != NULL) + { + if (m_poMIFFile->IsValidFeature(pszLine)) + break; + } + + if( m_poMIDFile != NULL ) + { + m_poMIDFile->Rewind(); + m_poMIDFile->GetLine(); + } + + m_bPreParsed = TRUE; + +} + +/********************************************************************** + * MIFFile::WriteMIFHeader() + * + * Generate the .MIF header. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int MIFFile::WriteMIFHeader() +{ + int iField; + GBool bFound; + + if (m_eAccessMode != TABWrite) + { + CPLError(CE_Failure, CPLE_NotSupported, + "WriteMIFHeader() can be used only with Write access."); + return -1; + } + + if (m_poDefn==NULL || m_poDefn->GetFieldCount() == 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "File %s must contain at least 1 attribute field.", + m_pszFname); + return -1; + } + + /*----------------------------------------------------------------- + * Start writing header. + *----------------------------------------------------------------*/ + m_bHeaderWrote = TRUE; + m_poMIFFile->WriteLine("Version %d\n", m_nVersion); + m_poMIFFile->WriteLine("Charset \"%s\"\n", m_pszCharset); + + // Delimiter is not required if you use \t as delimiter + if ( !EQUAL(m_pszDelimiter, "\t") ) + m_poMIFFile->WriteLine("Delimiter \"%s\"\n", m_pszDelimiter); + + bFound = FALSE; + for(iField=0; iFieldGetFieldCount(); iField++) + { + if (m_pabFieldUnique[iField]) + { + if (!bFound) + m_poMIFFile->WriteLine("Unique %d", iField+1); + else + m_poMIFFile->WriteLine(",%d", iField+1); + bFound = TRUE; + } + } + if (bFound) + m_poMIFFile->WriteLine("\n"); + + bFound = FALSE; + for(iField=0; iFieldGetFieldCount(); iField++) + { + if (m_pabFieldIndexed[iField]) + { + if (!bFound) + m_poMIFFile->WriteLine("Index %d", iField+1); + else + m_poMIFFile->WriteLine(",%d", iField+1); + bFound = TRUE; + } + } + if (bFound) + m_poMIFFile->WriteLine("\n"); + + if (m_pszCoordSys && m_bBoundsSet) + { + m_poMIFFile->WriteLine("CoordSys %s " + "Bounds (%.15g, %.15g) (%.15g, %.15g)\n", + m_pszCoordSys, + m_dXMin, m_dYMin, m_dXMax, m_dYMax); + } + else if (m_pszCoordSys) + { + m_poMIFFile->WriteLine("CoordSys %s\n",m_pszCoordSys); + } + + /*----------------------------------------------------------------- + * Column definitions + *----------------------------------------------------------------*/ + CPLAssert(m_paeFieldType); + + m_poMIFFile->WriteLine("Columns %d\n", m_poDefn->GetFieldCount()); + + for(iField=0; iFieldGetFieldCount(); iField++) + { + OGRFieldDefn *poFieldDefn; + poFieldDefn = m_poDefn->GetFieldDefn(iField); + + switch(m_paeFieldType[iField]) + { + case TABFInteger: + m_poMIFFile->WriteLine(" %s Integer\n", + poFieldDefn->GetNameRef()); + break; + case TABFSmallInt: + m_poMIFFile->WriteLine(" %s SmallInt\n", + poFieldDefn->GetNameRef()); + break; + case TABFFloat: + m_poMIFFile->WriteLine(" %s Float\n", + poFieldDefn->GetNameRef()); + break; + case TABFDecimal: + m_poMIFFile->WriteLine(" %s Decimal(%d,%d)\n", + poFieldDefn->GetNameRef(), + poFieldDefn->GetWidth(), + poFieldDefn->GetPrecision()); + break; + case TABFLogical: + m_poMIFFile->WriteLine(" %s Logical\n", + poFieldDefn->GetNameRef()); + break; + case TABFDate: + m_poMIFFile->WriteLine(" %s Date\n", + poFieldDefn->GetNameRef()); + break; + case TABFTime: + m_poMIFFile->WriteLine(" %s Time\n", + poFieldDefn->GetNameRef()); + break; + case TABFDateTime: + m_poMIFFile->WriteLine(" %s DateTime\n", + poFieldDefn->GetNameRef()); + break; + case TABFChar: + default: + m_poMIFFile->WriteLine(" %s Char(%d)\n", + poFieldDefn->GetNameRef(), + poFieldDefn->GetWidth()); + } + } + + /*----------------------------------------------------------------- + * Ready to write objects + *----------------------------------------------------------------*/ + m_poMIFFile->WriteLine("Data\n\n"); + + return 0; +} + +/********************************************************************** + * MIFFile::Close() + * + * Close current file, and release all memory used. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int MIFFile::Close() +{ + /* flush .mif header if not already written */ + if ( m_poDefn != NULL && m_bHeaderWrote == FALSE + && m_eAccessMode != TABRead ) + { + WriteMIFHeader(); + } + + if (m_poMIDFile) + { + m_poMIDFile->Close(); + delete m_poMIDFile; + m_poMIDFile = NULL; + } + + if (m_poMIFFile) + { + m_poMIFFile->Close(); + delete m_poMIFFile; + m_poMIFFile = NULL; + } + + if (m_poCurFeature) + { + delete m_poCurFeature; + m_poCurFeature = NULL; + } + + /*----------------------------------------------------------------- + * Note: we have to check the reference count before deleting + * m_poSpatialRef and m_poDefn + *----------------------------------------------------------------*/ + if (m_poDefn && m_poDefn->Dereference() == 0) + delete m_poDefn; + m_poDefn = NULL; + + if (m_poSpatialRef && m_poSpatialRef->Dereference() == 0) + delete m_poSpatialRef; + m_poSpatialRef = NULL; + + CPLFree(m_pszCoordSys); + m_pszCoordSys = NULL; + + CPLFree(m_pszDelimiter); + m_pszDelimiter = NULL; + + CPLFree(m_pszFname); + m_pszFname = NULL; + + m_nVersion = 0; + + CPLFree(m_pszCharset); + m_pszCharset = NULL; + + CPLFree(m_pabFieldIndexed); + m_pabFieldIndexed = NULL; + CPLFree(m_pabFieldUnique); + m_pabFieldUnique = NULL; + + CPLFree( m_pszIndex ); + m_pszIndex = NULL; + + CPLFree(m_paeFieldType); + m_paeFieldType = NULL; + + m_nCurFeatureId = 0; + m_nPreloadedId = 0; + m_nFeatureCount =0; + + m_bBoundsSet = FALSE; + + return 0; +} + +/********************************************************************** + * MIFFile::GetNextFeatureId() + * + * Returns feature id that follows nPrevId, or -1 if it is the + * last feature id. Pass nPrevId=-1 to fetch the first valid feature id. + **********************************************************************/ +GIntBig MIFFile::GetNextFeatureId(GIntBig nPrevId) +{ + if (m_eAccessMode != TABRead) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GetNextFeatureId() can be used only with Read access."); + return -1; + } + + if (nPrevId <= 0 && m_poMIFFile->GetLastLine() != NULL) + return 1; // Feature Ids start at 1 + else if (nPrevId > 0 && m_poMIFFile->GetLastLine() != NULL) + return nPrevId + 1; + else + return -1; +} + +/********************************************************************** + * MIFFile::GotoFeature() + * + * Private method to move MIF and MID pointers ready to read specified + * feature. Note that Feature Ids start at 1. + * + * Returns 0 on success, -1 on error (likely request for invalid feature id) + **********************************************************************/ +int MIFFile::GotoFeature(int nFeatureId) +{ + + if (nFeatureId < 1) + return -1; + + if (nFeatureId == m_nPreloadedId) // CorrectPosition + { + return 0; + } + else + { + if (nFeatureId < m_nPreloadedId || m_nCurFeatureId == 0) + ResetReading(); + + while(m_nPreloadedId < nFeatureId) + { + if (NextFeature() == FALSE) + return -1; + } + + CPLAssert(m_nPreloadedId == nFeatureId); + + return 0; + } +} + +/********************************************************************** + * MIFFile::NextFeature() + **********************************************************************/ + +GBool MIFFile::NextFeature() +{ + const char *pszLine; + while ((pszLine = m_poMIFFile->GetLine()) != NULL) + { + if (m_poMIFFile->IsValidFeature(pszLine)) + { + if( m_poMIDFile != NULL ) + m_poMIDFile->GetLine(); + m_nPreloadedId++; + return TRUE; + } + } + return FALSE; +} + +/********************************************************************** + * MIFFile::GetFeatureRef() + * + * Fill and return a TABFeature object for the specified feature id. + * + * The retruned pointer is a reference to an object owned and maintained + * by this MIFFile object. It should not be altered or freed by the + * caller and its contents is guaranteed to be valid only until the next + * call to GetFeatureRef() or Close(). + * + * Returns NULL if the specified feature id does not exist of if an + * error happened. In any case, CPLError() will have been called to + * report the reason of the failure. + **********************************************************************/ +TABFeature *MIFFile::GetFeatureRef(GIntBig nFeatureId) +{ + const char *pszLine; + + if (m_eAccessMode != TABRead) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GetFeatureRef() can be used only with Read access."); + return NULL; + } + + /*----------------------------------------------------------------- + * Make sure file is opened and Validate feature id by positioning + * the read pointers for the .MAP and .DAT files to this feature id. + *----------------------------------------------------------------*/ + if (m_poMIFFile == NULL) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "GetFeatureRef() failed: file is not opened!"); + return NULL; + } + + if ( (GIntBig)(int)nFeatureId != nFeatureId || GotoFeature((int)nFeatureId)!= 0 ) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "GetFeatureRef() failed: invalid feature id " CPL_FRMT_GIB, + nFeatureId); + return NULL; + } + + + /*----------------------------------------------------------------- + * Create new feature object of the right type + *----------------------------------------------------------------*/ + if ((pszLine = m_poMIFFile->GetLastLine()) != NULL) + { + // Delete previous feature... we'll start we a clean one. + if (m_poCurFeature) + delete m_poCurFeature; + m_poCurFeature = NULL; + + m_nCurFeatureId = m_nPreloadedId; + + if (EQUALN(pszLine,"NONE",4)) + { + m_poCurFeature = new TABFeature(m_poDefn); + } + else if (EQUALN(pszLine,"POINT",5)) + { + // Special case, we need to know two lines to decide the type + char **papszToken; + papszToken = CSLTokenizeString2(pszLine, " \t", CSLT_HONOURSTRINGS); + + if (CSLCount(papszToken) !=3) + { + CSLDestroy(papszToken); + CPLError(CE_Failure, CPLE_NotSupported, + "GetFeatureRef() failed: invalid point line: '%s'", + pszLine); + return NULL; + } + + m_poMIFFile->SaveLine(pszLine); + + if ((pszLine = m_poMIFFile->GetLine()) != NULL) + { + CSLDestroy(papszToken); + papszToken = CSLTokenizeStringComplex(pszLine," ,()\t", + TRUE,FALSE); + if (CSLCount(papszToken)> 0 &&EQUALN(papszToken[0],"SYMBOL",6)) + { + switch (CSLCount(papszToken)) + { + case 4: + m_poCurFeature = new TABPoint(m_poDefn); + break; + case 7: + m_poCurFeature = new TABFontPoint(m_poDefn); + break; + case 5: + m_poCurFeature = new TABCustomPoint(m_poDefn); + break; + default: + CSLDestroy(papszToken); + CPLError(CE_Failure, CPLE_NotSupported, + "GetFeatureRef() failed: invalid symbol " + "line: '%s'", pszLine); + return NULL; + break; + } + + } + } + CSLDestroy(papszToken); + + if (m_poCurFeature == NULL) + { + // No symbol clause... default to TABPoint + m_poCurFeature = new TABPoint(m_poDefn); + } + } + else if (EQUALN(pszLine,"LINE",4) || + EQUALN(pszLine,"PLINE",5)) + { + m_poCurFeature = new TABPolyline(m_poDefn); + } + else if (EQUALN(pszLine,"REGION",6)) + { + m_poCurFeature = new TABRegion(m_poDefn); + } + else if (EQUALN(pszLine,"ARC",3)) + { + m_poCurFeature = new TABArc(m_poDefn); + } + else if (EQUALN(pszLine,"TEXT",4)) + { + m_poCurFeature = new TABText(m_poDefn); + } + else if (EQUALN(pszLine,"RECT",4) || + EQUALN(pszLine,"ROUNDRECT",9)) + { + m_poCurFeature = new TABRectangle(m_poDefn); + } + else if (EQUALN(pszLine,"ELLIPSE",7)) + { + m_poCurFeature = new TABEllipse(m_poDefn); + } + else if (EQUALN(pszLine,"MULTIPOINT",10)) + { + m_poCurFeature = new TABMultiPoint(m_poDefn); + } + else if (EQUALN(pszLine,"COLLECTION",10)) + { + m_poCurFeature = new TABCollection(m_poDefn); + } + else + { + if (!EQUAL(pszLine,"")) + CPLError(CE_Failure, CPLE_NotSupported, + "Error during reading, unknown type %s.", + pszLine); + + //m_poCurFeature = new TABDebugFeature(m_poDefn); + return NULL; + } + } + + CPLAssert(m_poCurFeature); + if (m_poCurFeature == NULL) + return NULL; + + /*----------------------------------------------------------------- + * Read fields from the .DAT file + * GetRecordBlock() has already been called above... + *----------------------------------------------------------------*/ + if (m_poMIDFile != NULL && m_poCurFeature->ReadRecordFromMIDFile(m_poMIDFile) != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Error during reading Record."); + + delete m_poCurFeature; + m_poCurFeature = NULL; + return NULL; + } + + /*----------------------------------------------------------------- + * Read geometry from the .MAP file + * MoveToObjId() has already been called above... + *----------------------------------------------------------------*/ + if (m_poCurFeature->ReadGeometryFromMIFFile(m_poMIFFile) != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Error during reading Geometry."); + + delete m_poCurFeature; + m_poCurFeature = NULL; + return NULL; + } + + /* If the feature geometry is Text, and the value is empty(""), transform + it to a geometry none */ + if (m_poCurFeature->GetFeatureClass() == TABFCText) + { + TABFeature *poTmpFeature; + TABText *poTextFeature = (TABText*)m_poCurFeature; + if (strlen(poTextFeature->GetTextString()) == 0) + { + poTmpFeature = new TABFeature(m_poDefn); + for( int i = 0; i < m_poDefn->GetFieldCount(); i++ ) + { + poTmpFeature->SetField( i, m_poCurFeature->GetRawFieldRef( i ) ); + } + delete m_poCurFeature; + m_poCurFeature = poTmpFeature; + } + } + + /*--------------------------------------------------------------------- + * The act of reading the geometry causes the first line of the + * next object to be preloaded. Set the preloaded id appropriately. + *--------------------------------------------------------------------- */ + if( m_poMIFFile->GetLastLine() != NULL ) + m_nPreloadedId++; + else + m_nPreloadedId = 0; + + /* Update the Current Feature ID */ + m_poCurFeature->SetFID(m_nCurFeatureId); + + return m_poCurFeature; +} + +/********************************************************************** + * MIFFile::CreateFeature() + * + * Write a new feature to this dataset. The passed in feature is updated + * with the new feature id. + * + * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an + * error happened in which case, CPLError() will have been called to + * report the reason of the failure. + **********************************************************************/ +OGRErr MIFFile::CreateFeature(TABFeature *poFeature) +{ + int nFeatureId = -1; + + if (m_eAccessMode != TABWrite) + { + CPLError(CE_Failure, CPLE_NotSupported, + "CreateFeature() can be used only with Write access."); + return OGRERR_UNSUPPORTED_OPERATION; + } + + /*----------------------------------------------------------------- + * Make sure file is opened and establish new feature id. + *----------------------------------------------------------------*/ + if (m_poMIDFile == NULL) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "CreateFeature() failed: file is not opened!"); + return OGRERR_FAILURE; + } + + if (m_bHeaderWrote == FALSE) + { + /*------------------------------------------------------------- + * OK, this is the first feature in the dataset... make sure the + * .MID schema has been initialized. + *------------------------------------------------------------*/ + if (m_poDefn == NULL) + SetFeatureDefn(poFeature->GetDefnRef(), NULL); + + WriteMIFHeader(); + nFeatureId = 1; + } + else + { + nFeatureId = ++ m_nWriteFeatureId; + } + + + /*----------------------------------------------------------------- + * Write geometry to the .Mif file + *----------------------------------------------------------------*/ + if (m_poMIFFile == NULL || + poFeature->WriteGeometryToMIFFile(m_poMIFFile) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing geometry for feature id %d in %s", + nFeatureId, m_pszFname); + return OGRERR_FAILURE; + } + + if (m_poMIDFile == NULL || + poFeature->WriteRecordToMIDFile(m_poMIDFile) != 0 ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing attributes for feature id %d in %s", + nFeatureId, m_pszFname); + return OGRERR_FAILURE; + } + + poFeature->SetFID(nFeatureId); + + return OGRERR_NONE; +} + + + +/********************************************************************** + * MIFFile::GetLayerDefn() + * + * Returns a reference to the OGRFeatureDefn that will be used to create + * features in this dataset. + * + * Returns a reference to an object that is maintained by this MIFFile + * object (and thus should not be modified or freed by the caller) or + * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file + * opened yet) + **********************************************************************/ +OGRFeatureDefn *MIFFile::GetLayerDefn() +{ + return m_poDefn; +} + +/********************************************************************** + * MIFFile::SetFeatureDefn() + * + * Pass a reference to the OGRFeatureDefn that will be used to create + * features in this dataset. This function should be called after + * creating a new dataset, but before writing the first feature. + * All features that will be written to this dataset must share this same + * OGRFeatureDefn. + * + * This function will use poFeatureDefn to create a local copy that + * will be used to build the .MID file, etc. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int MIFFile::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn, + TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */) +{ + int numFields; + int nStatus = 0; + + /*----------------------------------------------------------------- + * Check that call happens at the right time in dataset's life. + *----------------------------------------------------------------*/ + if ( m_eAccessMode == TABWrite && m_bHeaderWrote ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetFeatureDefn() must be called after opening a new " + "dataset, but before writing the first feature to it."); + return -1; + } + + /*----------------------------------------------------------------- + * Delete current feature defn if there is already one. + * AddFieldNative() will take care of creating a new one for us. + *----------------------------------------------------------------*/ + if (m_poDefn && m_poDefn->Dereference() == 0) + delete m_poDefn; + m_poDefn = NULL; + + /*----------------------------------------------------------------- + * Copy field information + *----------------------------------------------------------------*/ + numFields = poFeatureDefn->GetFieldCount(); + + for(int iField=0; iFieldGetFieldDefn(iField); + + if (paeMapInfoNativeFieldTypes) + { + eMapInfoType = paeMapInfoNativeFieldTypes[iField]; + } + else + { + /*--------------------------------------------------------- + * Map OGRFieldTypes to MapInfo native types + *--------------------------------------------------------*/ + switch(poFieldDefn->GetType()) + { + case OFTInteger: + eMapInfoType = TABFInteger; + break; + case OFTReal: + eMapInfoType = TABFFloat; + break; + case OFTDateTime: + eMapInfoType = TABFDateTime; + break; + case OFTDate: + eMapInfoType = TABFDate; + break; + case OFTTime: + eMapInfoType = TABFTime; + break; + case OFTString: + default: + eMapInfoType = TABFChar; + } + } + + nStatus = AddFieldNative(poFieldDefn->GetNameRef(), eMapInfoType, + poFieldDefn->GetWidth(), + poFieldDefn->GetPrecision(), FALSE, FALSE); + } + + return nStatus; +} + +/********************************************************************** + * MIFFile::AddFieldNative() + * + * Create a new field using a native mapinfo data type... this is an + * alternative to defining fields through the OGR interface. + * This function should be called after creating a new dataset, but before + * writing the first feature. + * + * This function will build/update the OGRFeatureDefn that will have to be + * used when writing features to this dataset. + * + * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn(). + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int MIFFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType, + int nWidth /*=0*/, int nPrecision /*=0*/, + GBool bIndexed /*=FALSE*/, GBool bUnique/*=FALSE*/, int bApproxOK ) +{ + OGRFieldDefn *poFieldDefn; + char *pszCleanName = NULL; + int nStatus = 0; + char szNewFieldName[31+1]; /* 31 is the max characters for a field name*/ + int nRenameNum = 1; + + /*----------------------------------------------------------------- + * Check that call happens at the right time in dataset's life. + *----------------------------------------------------------------*/ + if ( m_eAccessMode == TABWrite && m_bHeaderWrote ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "AddFieldNative() must be called after opening a new " + "dataset, but before writing the first feature to it."); + return -1; + } + + /*----------------------------------------------------------------- + * Validate field width... must be <= 254 + *----------------------------------------------------------------*/ + if (nWidth > 254) + { + CPLError(CE_Warning, CPLE_IllegalArg, + "Invalid size (%d) for field '%s'. " + "Size must be 254 or less.", nWidth, pszName); + nWidth = 254; + } + + /*----------------------------------------------------------------- + * Map fields with width=0 (variable length in OGR) to a valid default + *----------------------------------------------------------------*/ + if (eMapInfoType == TABFDecimal && nWidth == 0) + nWidth=20; + else if (eMapInfoType == TABFChar && nWidth == 0) + nWidth=254; /* char fields */ + + /*----------------------------------------------------------------- + * Create new OGRFeatureDefn if not done yet... + *----------------------------------------------------------------*/ + if (m_poDefn == NULL) + { + char *pszFeatureClassName = TABGetBasename(m_pszFname); + m_poDefn = new OGRFeatureDefn(pszFeatureClassName); + CPLFree(pszFeatureClassName); + // Ref count defaults to 0... set it to 1 + m_poDefn->Reference(); + } + + /*----------------------------------------------------------------- + * Make sure field name is valid... check for special chars, etc. + * (pszCleanName will have to be freed.) + *----------------------------------------------------------------*/ + pszCleanName = TABCleanFieldName(pszName); + + if( !bApproxOK && + ( m_poDefn->GetFieldIndex(pszCleanName) >= 0 || + !EQUAL(pszName, pszCleanName) ) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Failed to add field named '%s'", + pszName ); + } + + strncpy(szNewFieldName, pszCleanName, 31); + szNewFieldName[31] = '\0'; + + while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 10) + sprintf( szNewFieldName, "%.29s_%.1d", pszCleanName, nRenameNum++ ); + + while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 100) + sprintf( szNewFieldName, "%.29s%.2d", pszCleanName, nRenameNum++ ); + + if (m_poDefn->GetFieldIndex(szNewFieldName) >= 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Too many field names like '%s' when truncated to 31 letters " + "for MapInfo format.", pszCleanName ); + } + + if( !EQUAL(pszCleanName,szNewFieldName) ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Normalized/laundered field name: '%s' to '%s'", + pszCleanName, + szNewFieldName ); + } + + + /*----------------------------------------------------------------- + * Map MapInfo native types to OGR types + *----------------------------------------------------------------*/ + poFieldDefn = NULL; + + switch(eMapInfoType) + { + case TABFChar: + /*------------------------------------------------- + * CHAR type + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString); + poFieldDefn->SetWidth(nWidth); + break; + case TABFInteger: + /*------------------------------------------------- + * INTEGER type + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger); + poFieldDefn->SetWidth(nWidth); + break; + case TABFSmallInt: + /*------------------------------------------------- + * SMALLINT type + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger); + poFieldDefn->SetWidth(nWidth); + break; + case TABFDecimal: + /*------------------------------------------------- + * DECIMAL type + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal); + poFieldDefn->SetWidth(nWidth); + poFieldDefn->SetPrecision(nPrecision); + break; + case TABFFloat: + /*------------------------------------------------- + * FLOAT type + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal); + break; + case TABFDate: + /*------------------------------------------------- + * DATE type (V450, returned as a string: "DD/MM/YYYY" or "YYYYMMDD") + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, +#ifdef MITAB_USE_OFTDATETIME + OFTDate); +#else + OFTString); +#endif + poFieldDefn->SetWidth(10); + m_nVersion = MAX(m_nVersion, 450); + break; + case TABFTime: + /*------------------------------------------------- + * TIME type (v900, returned as a string: "HH:MM:SS" or "HHMMSSmmm") + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, +#ifdef MITAB_USE_OFTDATETIME + OFTTime); +#else + OFTString); +#endif + poFieldDefn->SetWidth(9); + m_nVersion = MAX(m_nVersion, 900); + break; + case TABFDateTime: + /*------------------------------------------------- + * DATETIME type (v900, returned as a string: "DD/MM/YYYY HH:MM:SS", + * "YYYY/MM/DD HH:MM:SS" or "YYYYMMDDHHMMSSmmm") + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, +#ifdef MITAB_USE_OFTDATETIME + OFTDateTime); +#else + OFTString); +#endif + poFieldDefn->SetWidth(19); + m_nVersion = MAX(m_nVersion, 900); + break; + case TABFLogical: + /*------------------------------------------------- + * LOGICAL type (value "T" or "F") + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString); + poFieldDefn->SetWidth(1); + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported type for field %s", pszName); + return -1; + } + + /*----------------------------------------------------- + * Add the FieldDefn to the FeatureDefn + *----------------------------------------------------*/ + m_poDefn->AddFieldDefn(poFieldDefn); + delete poFieldDefn; + + /*----------------------------------------------------------------- + * Keep track of native field type + *----------------------------------------------------------------*/ + m_paeFieldType = (TABFieldType *)CPLRealloc(m_paeFieldType, + m_poDefn->GetFieldCount()* + sizeof(TABFieldType)); + m_paeFieldType[m_poDefn->GetFieldCount()-1] = eMapInfoType; + + /*----------------------------------------------------------------- + * Extend array of Indexed/Unique flags + *----------------------------------------------------------------*/ + m_pabFieldIndexed = (GBool *)CPLRealloc(m_pabFieldIndexed, + m_poDefn->GetFieldCount()* + sizeof(GBool)); + m_pabFieldUnique = (GBool *)CPLRealloc(m_pabFieldUnique, + m_poDefn->GetFieldCount()* + sizeof(GBool)); + m_pabFieldIndexed[m_poDefn->GetFieldCount()-1] = bIndexed; + m_pabFieldUnique[m_poDefn->GetFieldCount()-1] = bUnique; + + CPLFree(pszCleanName); + return nStatus; +} + + +/********************************************************************** + * MIFFile::GetNativeFieldType() + * + * Returns the native MapInfo field type for the specified field. + * + * Returns TABFUnknown if file is not opened, or if specified field index is + * invalid. + **********************************************************************/ +TABFieldType MIFFile::GetNativeFieldType(int nFieldId) +{ + if ( m_poDefn==NULL || m_paeFieldType==NULL || + nFieldId < 0 || nFieldId >= m_poDefn->GetFieldCount()) + return TABFUnknown; + + return m_paeFieldType[nFieldId]; +} + +/************************************************************************ + * MIFFile::SetFieldIndexed() + ************************************************************************/ + +int MIFFile::SetFieldIndexed( int nFieldId ) + +{ + if ( m_poDefn==NULL || m_pabFieldIndexed==NULL || + nFieldId < 0 || nFieldId >= m_poDefn->GetFieldCount()) + return -1; + + m_pabFieldIndexed[nFieldId] = TRUE; + + return 0; +} + +/************************************************************************ + * MIFFile::IsFieldIndexed() + ************************************************************************/ + +GBool MIFFile::IsFieldIndexed( int nFieldId ) + +{ + if ( m_poDefn==NULL || m_pabFieldIndexed==NULL || + nFieldId < 0 || nFieldId >= m_poDefn->GetFieldCount()) + return FALSE; + + return m_pabFieldIndexed[nFieldId]; +} + +/************************************************************************ + * MIFFile::IsFieldUnique() + ************************************************************************/ + +GBool MIFFile::IsFieldUnique( int nFieldId ) + +{ + if ( m_poDefn==NULL || m_pabFieldUnique==NULL || + nFieldId < 0 || nFieldId >= m_poDefn->GetFieldCount()) + return FALSE; + + return m_pabFieldUnique[nFieldId]; +} + + +/************************************************************************/ +/* MIFFile::SetSpatialRef() */ +/************************************************************************/ + +int MIFFile::SetSpatialRef( OGRSpatialReference * poSpatialRef ) + +{ + CPLFree( m_pszCoordSys ); + + char* pszCoordSys = MITABSpatialRef2CoordSys( poSpatialRef ); + if( pszCoordSys ) + { + SetMIFCoordSys(pszCoordSys); + CPLFree(pszCoordSys); + } + + return( m_pszCoordSys != NULL ); +} + + +/************************************************************************/ +/* MIFFile::SetMIFCoordSys() */ +/************************************************************************/ + +int MIFFile::SetMIFCoordSys(const char * pszMIFCoordSys) + +{ + char **papszFields, *pszCoordSys; + int iBounds; + + // Extract the word 'COORDSYS' if present + if (EQUALN(pszMIFCoordSys,"COORDSYS",8) ) + { + pszCoordSys = CPLStrdup(pszMIFCoordSys + 9); + } + else + { + pszCoordSys = CPLStrdup(pszMIFCoordSys); + } + + // Extract bounds if present + papszFields = CSLTokenizeStringComplex(pszCoordSys, " ,()\t", + TRUE, FALSE ); + iBounds = CSLFindString( papszFields, "Bounds" ); + if (iBounds >= 0 && iBounds + 4 < CSLCount(papszFields)) + { + m_dXMin = CPLAtof(papszFields[++iBounds]); + m_dYMin = CPLAtof(papszFields[++iBounds]); + m_dXMax = CPLAtof(papszFields[++iBounds]); + m_dYMax = CPLAtof(papszFields[++iBounds]); + m_bBoundsSet = TRUE; + + char* pszBounds = strstr(pszCoordSys, " Bounds"); + if( pszBounds == NULL ) + pszBounds = strstr(pszCoordSys, "Bounds"); + pszCoordSys[pszBounds - pszCoordSys] = '\0'; + } + CSLDestroy( papszFields ); + + // Assign the CoordSys + CPLFree( m_pszCoordSys ); + + m_pszCoordSys = CPLStrdup(pszCoordSys); + CPLFree(pszCoordSys); + + return( m_pszCoordSys != NULL ); +} + +/************************************************************************/ +/* MIFFile::GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *MIFFile::GetSpatialRef() + +{ + if( m_poSpatialRef == NULL ) + m_poSpatialRef = MITABCoordSys2SpatialRef( m_pszCoordSys ); + + return m_poSpatialRef; +} + +/********************************************************************** + * MIFFile::UpdateExtents() + * + * Private Methode used to update the dataset extents + **********************************************************************/ +void MIFFile::UpdateExtents(double dfX, double dfY) +{ + if (m_bExtentsSet == FALSE) + { + m_bExtentsSet = TRUE; + m_sExtents.MinX = m_sExtents.MaxX = dfX; + m_sExtents.MinY = m_sExtents.MaxY = dfY; + } + else + { + if (dfX < m_sExtents.MinX) + m_sExtents.MinX = dfX; + if (dfX > m_sExtents.MaxX) + m_sExtents.MaxX = dfX; + if (dfY < m_sExtents.MinY) + m_sExtents.MinY = dfY; + if (dfY > m_sExtents.MaxY) + m_sExtents.MaxY = dfY; + } +} + +/********************************************************************** + * MIFFile::SetBounds() + * + * Set projection coordinates bounds of the newly created dataset. + * + * This function must be called after creating a new dataset and before any + * feature can be written to it. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int MIFFile::SetBounds(double dXMin, double dYMin, + double dXMax, double dYMax) +{ + if (m_eAccessMode != TABWrite) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetBounds() can be used only with Write access."); + return -1; + } + + m_dXMin = dXMin; + m_dXMax = dXMax; + m_dYMin = dYMin; + m_dYMax = dYMax; + m_bBoundsSet = TRUE; + + return 0; +} + + +/********************************************************************** + * MIFFile::GetFeatureCountByType() + * + * Return number of features of each type. + * + * NOTE: The current implementation always returns -1 for MIF files + * since this would require scanning the whole file. + * + * When properly implemented, the bForce flag will force scanning the + * whole file by default. + * + * Returns 0 on success, or silently returns -1 (with no error) if this + * information is not available. + **********************************************************************/ +int MIFFile::GetFeatureCountByType(int &numPoints, int &numLines, + int &numRegions, int &numTexts, + GBool bForce ) +{ + if( m_bPreParsed || bForce ) + { + PreParseFile(); + + numPoints = m_nPoints; + numLines = m_nLines; + numRegions = m_nRegions; + numTexts = m_nTexts; + return 0; + } + else + { + numPoints = numLines = numRegions = numTexts = 0; + return -1; + } +} + +/********************************************************************** + * MIFFile::GetBounds() + * + * Fetch projection coordinates bounds of a dataset. + * + * Pass bForce=FALSE to avoid a scan of the whole file if the bounds + * are not already available. + * + * Returns 0 on success, -1 on error or if bounds are not available and + * bForce=FALSE. + **********************************************************************/ +int MIFFile::GetBounds(double &dXMin, double &dYMin, + double &dXMax, double &dYMax, + GBool bForce /*= TRUE*/ ) +{ + + if (m_bBoundsSet == FALSE && bForce == FALSE) + { + return -1; + } + else if (m_bBoundsSet == FALSE) + { + PreParseFile(); + } + + if (m_bBoundsSet == FALSE) + { + return -1; + } + + dXMin = m_dXMin; + dXMax = m_dXMax; + dYMin = m_dYMin; + dYMax = m_dYMax; + + return 0; +} + +/********************************************************************** + * MIFFile::GetExtent() + * + * Fetch extent of the data currently stored in the dataset. We collect + * this information while preparsing the file ... often already done for + * other reasons, and if not it is still faster than fully reading all + * the features just to count them. + * + * Returns OGRERR_NONE/OGRRERR_FAILURE. + **********************************************************************/ +OGRErr MIFFile::GetExtent (OGREnvelope *psExtent, int bForce) +{ + if (bForce == TRUE) + PreParseFile(); + + if (m_bPreParsed) + { + *psExtent = m_sExtents; + return OGRERR_NONE; + } + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int MIFFile::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return m_bPreParsed; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return m_bPreParsed; + + else if( EQUAL(pszCap,OLCCreateField) ) + return TRUE; + + else + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_ogr_datasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_ogr_datasource.cpp new file mode 100644 index 000000000..e60f19329 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_ogr_datasource.cpp @@ -0,0 +1,538 @@ +/********************************************************************** + * $Id: mitab_ogr_datasource.cpp,v 1.12 2007-03-22 20:01:36 dmorissette Exp $ + * + * Name: mitab_ogr_datasource.cpp + * Project: MapInfo Mid/Mif, Tab ogr support + * Language: C++ + * Purpose: Implementation of OGRTABDataSource. + * Author: Stephane Villeneuve, stephane.v@videotron.ca + * and Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 1999, 2000, Stephane Villeneuve + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_ogr_datasource.cpp,v $ + * Revision 1.12 2007-03-22 20:01:36 dmorissette + * Added SPATIAL_INDEX_MODE=QUICK creation option (MITAB bug 1669) + * + * Revision 1.11 2006/01/27 14:27:35 fwarmerdam + * fixed ogr bounds setting problems (bug 1198) + * + * Revision 1.10 2005/09/20 04:40:02 fwarmerdam + * fixed CPLReadDir memory leak + * + * Revision 1.9 2004/10/15 01:52:30 fwarmerdam + * ModifiedICreateLayer() to use -1000,-1000,1000,1000 bounds for GEOGCS + * much like in mitab_bounds.cpp. This ensures that geographic files in + * the range 0-360 works as well as -180 to 180. + * + * Revision 1.8 2004/07/07 15:42:46 fwarmerdam + * fixed up some single layer creation issues + * + * Revision 1.7 2004/02/27 21:06:03 fwarmerdam + * Better support for "single file" creation ... don't allow other layers to + * be created. But *do* single file to satisfy the first layer creation request + * made. Also, allow creating a datasource "on" an existing directory. + * + * Revision 1.6 2003/03/21 14:20:49 warmerda + * fixed email + * + * Revision 1.5 2002/02/08 16:52:16 warmerda + * added support for FORMAT=MIF option for creating layers + * + * Revision 1.4 2001/02/06 22:13:54 warmerda + * fixed memory leak in OGRTABDataSource::ICreateLayer() + * + * Revision 1.3 2001/01/22 16:03:58 warmerda + * expanded tabs + * + * Revision 1.2 2000/07/04 01:46:23 warmerda + * Avoid warnings on unused arguments. + * + * Revision 1.1 2000/01/26 18:17:09 warmerda + * New + * + **********************************************************************/ + +#include "mitab_ogr_driver.h" + + +/*======================================================================= + * OGRTABDataSource + * + * We need one single OGRDataSource/Driver set of classes to handle all + * the MapInfo file types. They all deal with the IMapInfoFile abstract + * class. + *=====================================================================*/ + +/************************************************************************/ +/* OGRTABDataSource() */ +/************************************************************************/ + +OGRTABDataSource::OGRTABDataSource() + +{ + m_pszName = NULL; + m_pszDirectory = NULL; + m_nLayerCount = 0; + m_papoLayers = NULL; + m_papszOptions = NULL; + m_bCreateMIF = FALSE; + m_bSingleFile = FALSE; + m_bSingleLayerAlreadyCreated = FALSE; + m_bQuickSpatialIndexMode = -1; + m_bUpdate = FALSE; +} + +/************************************************************************/ +/* ~OGRTABDataSource() */ +/************************************************************************/ + +OGRTABDataSource::~OGRTABDataSource() + +{ + CPLFree( m_pszName ); + CPLFree( m_pszDirectory ); + + for( int i = 0; i < m_nLayerCount; i++ ) + delete m_papoLayers[i]; + + CPLFree( m_papoLayers ); + CSLDestroy( m_papszOptions ); +} + +/************************************************************************/ +/* Create() */ +/* */ +/* Create a new dataset (directory or file). */ +/************************************************************************/ + +int OGRTABDataSource::Create( const char * pszName, char **papszOptions ) + +{ + VSIStatBufL sStat; + const char *pszOpt; + + CPLAssert( m_pszName == NULL ); + + m_pszName = CPLStrdup( pszName ); + m_papszOptions = CSLDuplicate( papszOptions ); + m_bUpdate = TRUE; + + if( (pszOpt=CSLFetchNameValue(papszOptions,"FORMAT")) != NULL + && EQUAL(pszOpt, "MIF") ) + m_bCreateMIF = TRUE; + else if( EQUAL(CPLGetExtension(pszName),"mif") + || EQUAL(CPLGetExtension(pszName),"mid") ) + m_bCreateMIF = TRUE; + + if( (pszOpt=CSLFetchNameValue(papszOptions,"SPATIAL_INDEX_MODE")) != NULL ) + { + if ( EQUAL(pszOpt, "QUICK") ) + m_bQuickSpatialIndexMode = TRUE; + else if ( EQUAL(pszOpt, "OPTIMIZED") ) + m_bQuickSpatialIndexMode = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Create a new empty directory. */ +/* -------------------------------------------------------------------- */ + if( strlen(CPLGetExtension(pszName)) == 0 ) + { + if( VSIStatL( pszName, &sStat ) == 0 ) + { + if( !VSI_ISDIR(sStat.st_mode) ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Attempt to create dataset named %s,\n" + "but that is an existing file.\n", + pszName ); + return FALSE; + } + } + else + { + if( VSIMkdir( pszName, 0755 ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create directory %s.\n", + pszName ); + return FALSE; + } + } + + m_pszDirectory = CPLStrdup(pszName); + } + +/* -------------------------------------------------------------------- */ +/* Create a new single file. */ +/* -------------------------------------------------------------------- */ + else + { + IMapInfoFile *poFile; + + if( m_bCreateMIF ) + poFile = new MIFFile; + else + poFile = new TABFile; + + if( poFile->Open( m_pszName, TABWrite, FALSE ) != 0 ) + { + delete poFile; + return FALSE; + } + + m_nLayerCount = 1; + m_papoLayers = (IMapInfoFile **) CPLMalloc(sizeof(void*)); + m_papoLayers[0] = poFile; + + m_pszDirectory = CPLStrdup( CPLGetPath(pszName) ); + m_bSingleFile = TRUE; + } + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/* */ +/* Open an existing file, or directory of files. */ +/************************************************************************/ + +int OGRTABDataSource::Open( GDALOpenInfo* poOpenInfo, int bTestOpen ) + +{ + CPLAssert( m_pszName == NULL ); + + m_pszName = CPLStrdup( poOpenInfo->pszFilename ); + m_bUpdate = (poOpenInfo->eAccess == GA_Update ); + +/* -------------------------------------------------------------------- */ +/* If it is a file, try to open as a Mapinfo file. */ +/* -------------------------------------------------------------------- */ + if( !poOpenInfo->bIsDirectory ) + { + IMapInfoFile *poFile; + + poFile = IMapInfoFile::SmartOpen( m_pszName, m_bUpdate, bTestOpen ); + if( poFile == NULL ) + return FALSE; + + poFile->SetDescription( poFile->GetName() ); + + m_nLayerCount = 1; + m_papoLayers = (IMapInfoFile **) CPLMalloc(sizeof(void*)); + m_papoLayers[0] = poFile; + + m_pszDirectory = CPLStrdup( CPLGetPath(m_pszName) ); + + m_bSingleFile = TRUE; + m_bSingleLayerAlreadyCreated = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise, we need to scan the whole directory for files */ +/* ending in .tab or .mif. */ +/* -------------------------------------------------------------------- */ + else + { + char **papszFileList = CPLReadDir( m_pszName ); + + m_pszDirectory = CPLStrdup( m_pszName ); + + for( int iFile = 0; + papszFileList != NULL && papszFileList[iFile] != NULL; + iFile++ ) + { + IMapInfoFile *poFile; + const char *pszExtension = CPLGetExtension(papszFileList[iFile]); + char *pszSubFilename; + + if( !EQUAL(pszExtension,"tab") && !EQUAL(pszExtension,"mif") ) + continue; + + pszSubFilename = CPLStrdup( + CPLFormFilename( m_pszDirectory, papszFileList[iFile], NULL )); + + poFile = IMapInfoFile::SmartOpen( pszSubFilename, m_bUpdate, bTestOpen ); + CPLFree( pszSubFilename ); + + if( poFile == NULL ) + { + CSLDestroy( papszFileList ); + return FALSE; + } + poFile->SetDescription( poFile->GetName() ); + + m_nLayerCount++; + m_papoLayers = (IMapInfoFile **) + CPLRealloc(m_papoLayers,sizeof(void*)*m_nLayerCount); + m_papoLayers[m_nLayerCount-1] = poFile; + } + + CSLDestroy( papszFileList ); + + if( m_nLayerCount == 0 ) + { + if( !bTestOpen ) + CPLError( CE_Failure, CPLE_OpenFailed, + "No mapinfo files found in directory %s.\n", + m_pszDirectory ); + + return FALSE; + } + } + + return TRUE; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int OGRTABDataSource::GetLayerCount() + +{ + if( m_bSingleFile && !m_bSingleLayerAlreadyCreated ) + return 0; + else + return m_nLayerCount; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRTABDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= GetLayerCount() ) + return NULL; + else + return m_papoLayers[iLayer]; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRTABDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRSIn, + OGRwkbGeometryType /* eGeomTypeIn */, + char ** papszOptions ) + +{ + IMapInfoFile *poFile; + char *pszFullFilename; + const char *pszOpt = NULL; + + if( !m_bUpdate ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot create layer on read-only dataset."); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* If it's a single file mode file, then we may have already */ +/* instantiated the low level layer. We would just need to */ +/* reset the coordinate system and (potentially) bounds. */ +/* -------------------------------------------------------------------- */ + if( m_bSingleFile ) + { + if( m_bSingleLayerAlreadyCreated ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create new layers in this single file dataset."); + return NULL; + } + + m_bSingleLayerAlreadyCreated = TRUE; + + poFile = (IMapInfoFile *) m_papoLayers[0]; + } + + else + { + if( m_bCreateMIF ) + { + pszFullFilename = CPLStrdup( CPLFormFilename( m_pszDirectory, + pszLayerName, "mif" ) ); + + poFile = new MIFFile; + } + else + { + pszFullFilename = CPLStrdup( CPLFormFilename( m_pszDirectory, + pszLayerName, "tab" ) ); + + poFile = new TABFile; + } + + if( poFile->Open( pszFullFilename, TABWrite, FALSE ) != 0 ) + { + CPLFree( pszFullFilename ); + delete poFile; + return FALSE; + } + + m_nLayerCount++; + m_papoLayers = (IMapInfoFile **) + CPLRealloc(m_papoLayers,sizeof(void*)*m_nLayerCount); + m_papoLayers[m_nLayerCount-1] = poFile; + + CPLFree( pszFullFilename ); + } + + poFile->SetDescription( poFile->GetName() ); + +/* -------------------------------------------------------------------- */ +/* Assign the coordinate system (if provided) and set */ +/* reasonable bounds. */ +/* -------------------------------------------------------------------- */ + if( poSRSIn != NULL ) + { + poFile->SetSpatialRef( poSRSIn ); + // SetSpatialRef() has cloned the passed geometry + poFile->GetLayerDefn()->GetGeomFieldDefn(0)->SetSpatialRef(poFile->GetSpatialRef()); + } + + // Pull out the bounds if supplied + if( (pszOpt=CSLFetchNameValue(papszOptions, "BOUNDS")) != NULL ) { + double dfBounds[4]; + if( CPLsscanf(pszOpt, "%lf,%lf,%lf,%lf", &dfBounds[0], + &dfBounds[1], + &dfBounds[2], + &dfBounds[3]) != 4 ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Invalid BOUNDS parameter, expected min_x,min_y,max_x,max_y\n" ); + } + else + { + poFile->SetBounds( dfBounds[0], dfBounds[1], dfBounds[2], dfBounds[3] ); + } + } + + if( !poFile->IsBoundsSet() && !m_bCreateMIF ) + { + if( poSRSIn != NULL && poSRSIn->GetRoot() != NULL + && EQUAL(poSRSIn->GetRoot()->GetValue(),"GEOGCS") ) + poFile->SetBounds( -1000, -1000, 1000, 1000 ); + else + poFile->SetBounds( -30000000, -15000000, 30000000, 15000000 ); + } + + if (m_bQuickSpatialIndexMode == TRUE && poFile->SetQuickSpatialIndexMode(TRUE) != 0) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Setting Quick Spatial Index Mode failed."); + } + else if (m_bQuickSpatialIndexMode == FALSE && poFile->SetQuickSpatialIndexMode(FALSE) != 0) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Setting Normal Spatial Index Mode failed."); + } + + return poFile; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRTABDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return m_bUpdate && (!m_bSingleFile || !m_bSingleLayerAlreadyCreated); + else + return FALSE; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **OGRTABDataSource::GetFileList() +{ + VSIStatBufL sStatBuf; + CPLStringList osList; + + VSIStatL( m_pszName, &sStatBuf ); + if( VSI_ISDIR(sStatBuf.st_mode) ) + { + static const char *apszExtensions[] = + { "mif", "mid", "tab", "map", "ind", "dat", "id", NULL }; + char **papszDirEntries = CPLReadDir( m_pszName ); + int iFile; + + for( iFile = 0; + papszDirEntries != NULL && papszDirEntries[iFile] != NULL; + iFile++ ) + { + if( CSLFindString( (char **) apszExtensions, + CPLGetExtension(papszDirEntries[iFile])) != -1) + { + osList.AddString( CPLFormFilename( m_pszName, + papszDirEntries[iFile], + NULL ) ); + } + } + + CSLDestroy( papszDirEntries ); + } + else + { + static const char* apszMIFExtensions[] = { "mif", "mid", NULL }; + static const char* apszTABExtensions[] = { "tab", "map", "ind", "dat", "id", NULL }; + const char** papszExtensions; + if( EQUAL(CPLGetExtension(m_pszName), "mif") || + EQUAL(CPLGetExtension(m_pszName), "mid") ) + { + papszExtensions = apszMIFExtensions; + } + else + { + papszExtensions = apszTABExtensions; + } + const char** papszIter = papszExtensions; + while( *papszIter ) + { + const char *pszFile = CPLResetExtension(m_pszName, *papszIter ); + if( VSIStatL( pszFile, &sStatBuf ) != 0) + { + pszFile = CPLResetExtension(m_pszName, CPLString(*papszIter).toupper() ); + if( VSIStatL( pszFile, &sStatBuf ) != 0) + { + pszFile = NULL; + } + } + if( pszFile ) + osList.AddString( pszFile ); + papszIter ++; + } + } + return osList.StealList(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_ogr_driver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_ogr_driver.cpp new file mode 100644 index 000000000..5565fcfee --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_ogr_driver.cpp @@ -0,0 +1,272 @@ +/********************************************************************** + * $Id: mitab_ogr_driver.cpp,v 1.11 2005-05-21 03:15:18 fwarmerdam Exp $ + * + * Name: mitab_ogr_driver.cpp + * Project: MapInfo Mid/Mif, Tab ogr support + * Language: C++ + * Purpose: Implementation of the MIDDATAFile class used to handle + * reading/writing of the MID/MIF files + * Author: Stephane Villeneuve, stephane.v@videotron.ca + * + ********************************************************************** + * Copyright (c) 1999, 2000, Stephane Villeneuve + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_ogr_driver.cpp,v $ + * Revision 1.11 2005-05-21 03:15:18 fwarmerdam + * Removed unused stat buffer. + * + * Revision 1.10 2004/02/27 21:06:03 fwarmerdam + * Better support for "single file" creation ... don't allow other layers to + * be created. But *do* single file to satisfy the first layer creation request + * made. Also, allow creating a datasource "on" an existing directory. + * + * Revision 1.9 2003/03/20 15:57:46 warmerda + * Added delete datasource support + * + * Revision 1.8 2001/01/22 16:03:58 warmerda + * expanded tabs + * + * Revision 1.7 2000/01/26 18:17:00 warmerda + * reimplement OGR driver + * + * Revision 1.6 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.5 1999/12/15 17:05:24 warmerda + * Only create OGRTABDataSource if SmartOpen() result is non-NULL. + * + * Revision 1.4 1999/12/15 16:28:17 warmerda + * fixed a few type problems + * + * Revision 1.3 1999/12/14 02:22:29 daniel + * Merged TAB+MIF DataSource/Driver into ane using IMapInfoFile class + * + * Revision 1.2 1999/11/12 02:44:36 stephane + * added comment, change Register name. + * + * Revision 1.1 1999/11/08 21:05:51 svillene + * first revision + * + * Revision 1.1 1999/11/08 04:16:07 stephane + * First Revision + * + **********************************************************************/ + +#include "mitab_ogr_driver.h" + + +/************************************************************************/ +/* OGRTABDriverIdentify() */ +/************************************************************************/ + +static int OGRTABDriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + /* Files not ending with .tab, .mif or .mid are not handled by this driver */ + if( !poOpenInfo->bStatOK ) + return FALSE; + if( poOpenInfo->bIsDirectory ) + return -1; /* unsure */ + if( poOpenInfo->fpL == NULL ) + return FALSE; + if (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "MIF") || + EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "MID") ) + { + return TRUE; + } + if (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "TAB") ) + { + for( int i = 0; i < poOpenInfo->nHeaderBytes; i++) + { + const char* pszLine = (const char*)poOpenInfo->pabyHeader + i; + if (EQUALN(pszLine, "Fields", 6)) + return TRUE; + else if (EQUALN(pszLine, "create view", 11)) + return TRUE; + else if (EQUALN(pszLine, "\"\\IsSeamless\" = \"TRUE\"", 21)) + return TRUE; + } + } + return FALSE; +} + +/************************************************************************/ +/* OGRTABDriver::Open() */ +/************************************************************************/ + +static GDALDataset *OGRTABDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + OGRTABDataSource *poDS; + + if( OGRTABDriverIdentify(poOpenInfo) == FALSE ) + { + return NULL; + } + + if (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "MIF") || + EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "MID") ) + { + if( poOpenInfo->eAccess == GA_Update ) + return NULL; + } + + poDS = new OGRTABDataSource(); + if( poDS->Open( poOpenInfo, TRUE ) ) + return poDS; + else + { + delete poDS; + return NULL; + } +} + + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRTABDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + OGRTABDataSource *poDS; + +/* -------------------------------------------------------------------- */ +/* Try to create the data source. */ +/* -------------------------------------------------------------------- */ + poDS = new OGRTABDataSource(); + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +static CPLErr OGRTABDriverDelete( const char *pszDataSource ) + +{ + GDALDataset* poDS; + { + // Make sure that the file opened by GDALOpenInfo is closed + // when the object goes out of scope + GDALOpenInfo oOpenInfo(pszDataSource, GA_ReadOnly); + poDS = OGRTABDriverOpen(&oOpenInfo); + } + if( poDS == NULL ) + return CE_Failure; + char** papszFileList = poDS->GetFileList(); + delete poDS; + + char** papszIter = papszFileList; + while( papszIter && *papszIter ) + { + VSIUnlink( *papszIter ); + papszIter ++; + } + CSLDestroy(papszFileList); + + VSIStatBufL sStatBuf; + if( VSIStatL( pszDataSource, &sStatBuf ) == 0 && + VSI_ISDIR(sStatBuf.st_mode) ) + { + VSIRmdir( pszDataSource ); + } + + return CE_None; +} + +/************************************************************************/ +/* OGRTABDriverUnload() */ +/************************************************************************/ + +static void OGRTABDriverUnload(CPL_UNUSED GDALDriver* poDriver) +{ + MITABFreeCoordSysTable(); +} + +/************************************************************************/ +/* RegisterOGRTAB() */ +/************************************************************************/ + +extern "C" +{ + +void RegisterOGRTAB() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "MapInfo File" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "MapInfo File" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "MapInfo File" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "tab mif mid" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_mitab.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " +" " +""); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Real String Date DateTime Time" ); + + poDriver->pfnOpen = OGRTABDriverOpen; + poDriver->pfnIdentify = OGRTABDriverIdentify; + poDriver->pfnCreate = OGRTABDriverCreate; + poDriver->pfnDelete = OGRTABDriverDelete; + poDriver->pfnUnloadDriver = OGRTABDriverUnload; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_ogr_driver.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_ogr_driver.h new file mode 100644 index 000000000..ad7c857f9 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_ogr_driver.h @@ -0,0 +1,130 @@ +/********************************************************************** + * $Id: mitab_ogr_driver.h,v 1.14 2007-03-22 20:01:36 dmorissette Exp $ + * + * Name: mitab_ogr_drive.h + * Project: Mid/mif tab ogr support + * Language: C++ + * Purpose: Header file containing public definitions for the library. + * Author: Stephane Villeneuve, stephane.v@videotron.ca + * + ********************************************************************** + * Copyright (c) 1999, 2000, Stephane Villeneuve + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_ogr_driver.h,v $ + * Revision 1.14 2007-03-22 20:01:36 dmorissette + * Added SPATIAL_INDEX_MODE=QUICK creation option (MITAB bug 1669) + * + * Revision 1.13 2004/07/07 16:11:39 fwarmerdam + * fixed up some single layer creation issues + * + * Revision 1.12 2004/02/27 21:06:03 fwarmerdam + * Better support for "single file" creation ... don't allow other layers to + * be created. But *do* single file to satisfy the first layer creation request + * made. Also, allow creating a datasource "on" an existing directory. + * + * Revision 1.11 2003/03/20 15:57:46 warmerda + * Added delete datasource support + * + * Revision 1.10 2002/02/08 16:52:16 warmerda + * added support for FORMAT=MIF option for creating layers + * + * Revision 1.9 2001/09/14 03:22:58 warmerda + * added RegisterOGRTAB() prototype + * + * Revision 1.8 2001/01/22 16:03:59 warmerda + * expanded tabs + * + * Revision 1.7 2000/01/26 18:17:00 warmerda + * reimplement OGR driver + * + * Revision 1.6 2000/01/15 22:30:44 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.5 1999/12/15 16:28:17 warmerda + * fixed a few type problems + * + * Revision 1.4 1999/12/15 16:15:05 warmerda + * Avoid unused parameter warnings. + * + * Revision 1.3 1999/12/14 02:23:05 daniel + * Merged TAB+MIF DataSource/Driver into one using IMapInfoFile class + * + * Revision 1.2 1999/11/12 02:44:36 stephane + * added comment, change Register name. + * + * Revision 1.1 1999/11/08 21:05:51 svillene + * first revision + * + **********************************************************************/ + +#include "mitab.h" +#include "ogrsf_frmts.h" + +#ifndef _MITAB_OGR_DRIVER_H_INCLUDED_ +#define _MITAB_OGR_DRIVER_H_INCLUDED_ + +/*===================================================================== + * OGRTABDataSource Class + * + * These classes handle all the file types supported by the MITAB lib. + * through the IMapInfoFile interface. + *====================================================================*/ +class OGRTABDataSource : public OGRDataSource +{ + private: + char *m_pszName; + char *m_pszDirectory; + + int m_nLayerCount; + IMapInfoFile **m_papoLayers; + + char **m_papszOptions; + int m_bCreateMIF; + int m_bSingleFile; + int m_bSingleLayerAlreadyCreated; + GBool m_bQuickSpatialIndexMode; + int m_bUpdate; + + public: + OGRTABDataSource(); + virtual ~OGRTABDataSource(); + + int Open( GDALOpenInfo* poOpenInfo, int bTestOpen ); + int Create( const char *pszName, char ** papszOptions ); + + const char *GetName() { return m_pszName; } + int GetLayerCount(); + OGRLayer *GetLayer( int ); + int TestCapability( const char * ); + + OGRLayer *ICreateLayer(const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + char **GetFileList(); +}; + +void CPL_DLL RegisterOGRTAB(); + +#endif /* _MITAB_OGR_DRIVER_H_INCLUDED_ */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_priv.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_priv.h new file mode 100644 index 000000000..ff0d13057 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_priv.h @@ -0,0 +1,1951 @@ +/********************************************************************** + * $Id: mitab_priv.h,v 1.55 2010-01-07 20:39:12 aboudreault Exp $ + * + * Name: mitab_priv.h + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Header file containing private definitions for the library. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2003, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_priv.h,v $ + * Revision 1.55 2010-01-07 20:39:12 aboudreault + * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141) + * + * Revision 1.54 2008-11-27 20:50:23 aboudreault + * Improved support for OGR date/time types. New Read/Write methods (bug 1948) + * Added support of OGR date/time types for MIF features. + * + * Revision 1.53 2008/03/05 20:35:39 dmorissette + * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859) + * + * Revision 1.52 2008/02/20 21:35:30 dmorissette + * Added support for V800 COLLECTION of large objects (bug 1496) + * + * Revision 1.51 2008/02/05 22:22:48 dmorissette + * Added support for TAB_GEOM_V800_MULTIPOINT (bug 1496) + * + * Revision 1.50 2008/02/01 19:36:31 dmorissette + * Initial support for V800 REGION and MULTIPLINE (bug 1496) + * + * Revision 1.49 2008/01/29 20:46:32 dmorissette + * Added support for v9 Time and DateTime fields (byg 1754) + * + * Revision 1.48 2007/10/09 17:43:16 fwarmerdam + * Remove static variables that interfere with reentrancy. (GDAL #1883) + * + * Revision 1.47 2007/06/12 12:50:39 dmorissette + * Use Quick Spatial Index by default until bug 1732 is fixed (broken files + * produced by current coord block splitting technique). + * + * Revision 1.46 2007/06/11 14:52:31 dmorissette + * Return a valid m_nCoordDatasize value for Collection objects to prevent + * trashing of collection data during object splitting (bug 1728) + * + * Revision 1.45 2007/03/21 21:15:56 dmorissette + * Added SetQuickSpatialIndexMode() which generates a non-optimal spatial + * index but results in faster write time (bug 1669) + * + * Revision 1.44 2007/02/22 18:35:53 dmorissette + * Fixed problem writing collections where MITAB was sometimes trying to + * read past EOF in write mode (bug 1657). + * + * Revision 1.43 2006/11/28 18:49:08 dmorissette + * Completed changes to split TABMAPObjectBlocks properly and produce an + * optimal spatial index (bug 1585) + * + * Revision 1.42 2006/11/20 20:05:58 dmorissette + * First pass at improving generation of spatial index in .map file (bug 1585) + * New methods for insertion and splittung in the spatial index are done. + * Also implemented a method to dump the spatial index to .mif/.mid + * Still need to implement splitting of TABMapObjectBlock to get optimal + * results. + * + * Revision 1.41 2006/09/05 23:05:08 dmorissette + * Added TABMAPFile::DumpSpatialIndex() (bug 1585) + * + * Revision 1.40 2005/10/06 19:15:31 dmorissette + * Collections: added support for reading/writing pen/brush/symbol ids and + * for writing collection objects to .TAB/.MAP (bug 1126) + * + * Revision 1.39 2005/10/04 15:44:31 dmorissette + * First round of support for Collection objects. Currently supports reading + * from .TAB/.MAP and writing to .MIF. Still lacks symbol support and write + * support. (Based in part on patch and docs from Jim Hope, bug 1126) + * + * Revision 1.38 2005/03/22 23:24:54 dmorissette + * Added support for datum id in .MAP header (bug 910) + * + * Revision 1.37 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.36 2003/08/12 23:17:21 dmorissette + * Added reading of v500+ coordsys affine params (Anthony D. - Encom) + * + * Revision 1.35 2003/01/18 20:25:44 daniel + * Increased MIDMAXCHAR value to 10000 + * + * Revision 1.34 2002/04/25 16:05:24 julien + * Disabled the overflow warning in SetCoordFilter() by adding bIgnoreOverflow + * variable in Coordsys2Int of the TABMAPFile class and TABMAPHeaderBlock class + * + * Revision 1.33 2002/04/22 13:49:09 julien + * Add EOF validation in MIDDATAFile::GetLastLine() (Bug 819) + * + * Revision 1.32 2002/03/26 19:27:43 daniel + * Got rid of tabs in source + * + * Revision 1.31 2002/03/26 01:48:40 daniel + * Added Multipoint object type (V650) + * + * Revision 1.30 2002/02/22 20:44:51 julien + * Prevent infinite loop with TABRelation by suppress the m_poCurFeature object + * from the class and setting it in the calling function and add GetFeature in + * the class. (bug 706) + * + * Revision 1.29 2001/11/19 15:07:54 daniel + * Added TABMAPObjNone to handle the case of TAB_GEOM_NONE + * + * Revision 1.28 2001/11/17 21:54:06 daniel + * Made several changes in order to support writing objects in 16 bits + * coordinate format. New TABMAPObjHdr-derived classes are used to hold + * object info in mem until block is full. + * + * Revision 1.27 2001/09/18 20:33:52 warmerda + * fixed case of spatial search on file with just one object block + * + * Revision 1.26 2001/09/14 03:23:55 warmerda + * Substantial upgrade to support spatial queries using spatial indexes + * + * Revision 1.25 2001/05/01 18:28:10 daniel + * Fixed default BRUSH, should be BRUSH(1,0,16777215). + * + * Revision 1.24 2001/05/01 03:39:51 daniel + * Added SetLastPtr() to TABBinBlockManager. + * + * Revision 1.23 2001/03/15 03:57:51 daniel + * Added implementation for new OGRLayer::GetExtent(), returning data MBR. + * + * Revision 1.22 2000/11/23 21:11:07 daniel + * OOpps... VC++ didn't like the way TABPenDef, etc. were initialized + * + * Revision 1.21 2000/11/23 20:47:46 daniel + * Use MI defaults for Pen, Brush, Font, Symbol instead of all zeros + * + * Revision 1.20 2000/11/15 04:13:50 daniel + * Fixed writing of TABMAPToolBlock to allocate a new block when full + * + * Revision 1.19 2000/11/13 22:19:30 daniel + * Added TABINDNode::UpdateCurChildEntry() + * + * Revision 1.18 2000/05/19 06:45:25 daniel + * Modified generation of spatial index to split index nodes and produce a + * more balanced tree. + * + * Revision 1.17 2000/03/01 00:30:03 daniel + * Completed support for joined tables + * + * Revision 1.16 2000/02/28 16:53:23 daniel + * Added support for indexed, unique, and for new V450 object types + * + * ... + * + * Revision 1.1 1999/07/12 04:18:25 daniel + * Initial checkin + * + **********************************************************************/ + +#ifndef _MITAB_PRIV_H_INCLUDED_ +#define _MITAB_PRIV_H_INCLUDED_ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_feature.h" + +class TABFile; +class TABFeature; +class TABMAPToolBlock; +class TABMAPIndexBlock; + +/*--------------------------------------------------------------------- + * Access mode: Read or Write + *--------------------------------------------------------------------*/ +typedef enum +{ + TABRead, + TABWrite, + TABReadWrite +} TABAccess; + +/*--------------------------------------------------------------------- + * Supported .MAP block types (the first byte at the beginning of a block) + *--------------------------------------------------------------------*/ +#define TAB_RAWBIN_BLOCK -1 +#define TABMAP_HEADER_BLOCK 0 +#define TABMAP_INDEX_BLOCK 1 +#define TABMAP_OBJECT_BLOCK 2 +#define TABMAP_COORD_BLOCK 3 +#define TABMAP_GARB_BLOCK 4 +#define TABMAP_TOOL_BLOCK 5 +#define TABMAP_LAST_VALID_BLOCK_TYPE 5 + +/*--------------------------------------------------------------------- + * Drawing Tool types + *--------------------------------------------------------------------*/ +#define TABMAP_TOOL_PEN 1 +#define TABMAP_TOOL_BRUSH 2 +#define TABMAP_TOOL_FONT 3 +#define TABMAP_TOOL_SYMBOL 4 + +/*--------------------------------------------------------------------- + * Limits related to .TAB version number. If we pass any of those limits + * then we have to use larger object types + *--------------------------------------------------------------------*/ +#define TAB_REGION_PLINE_300_MAX_VERTICES 32767 + +#define TAB_REGION_PLINE_450_MAX_SEGMENTS 32767 +#define TAB_REGION_PLINE_450_MAX_VERTICES 1048575 + +#define TAB_MULTIPOINT_650_MAX_VERTICES 1048576 + +/* Use this macro to test whether the number of segments and vertices + * in this object exceeds the V450/650 limits and requires a V800 object + */ +#define TAB_REGION_PLINE_REQUIRES_V800(numSegments, numVerticesTotal) \ + ((numSegments) > TAB_REGION_PLINE_450_MAX_SEGMENTS || \ + ((numSegments)*3 + numVerticesTotal) > TAB_REGION_PLINE_450_MAX_VERTICES ) + + +/*--------------------------------------------------------------------- + * Codes for the known MapInfo Geometry types + *--------------------------------------------------------------------*/ +typedef enum +{ + TAB_GEOM_UNSET = -1, + + TAB_GEOM_NONE = 0, + TAB_GEOM_SYMBOL_C = 0x01, + TAB_GEOM_SYMBOL = 0x02, + TAB_GEOM_LINE_C = 0x04, + TAB_GEOM_LINE = 0x05, + TAB_GEOM_PLINE_C = 0x07, + TAB_GEOM_PLINE = 0x08, + TAB_GEOM_ARC_C = 0x0a, + TAB_GEOM_ARC = 0x0b, + TAB_GEOM_REGION_C = 0x0d, + TAB_GEOM_REGION = 0x0e, + TAB_GEOM_TEXT_C = 0x10, + TAB_GEOM_TEXT = 0x11, + TAB_GEOM_RECT_C = 0x13, + TAB_GEOM_RECT = 0x14, + TAB_GEOM_ROUNDRECT_C = 0x16, + TAB_GEOM_ROUNDRECT = 0x17, + TAB_GEOM_ELLIPSE_C = 0x19, + TAB_GEOM_ELLIPSE = 0x1a, + TAB_GEOM_MULTIPLINE_C = 0x25, + TAB_GEOM_MULTIPLINE = 0x26, + TAB_GEOM_FONTSYMBOL_C = 0x28, + TAB_GEOM_FONTSYMBOL = 0x29, + TAB_GEOM_CUSTOMSYMBOL_C = 0x2b, + TAB_GEOM_CUSTOMSYMBOL = 0x2c, +/* Version 450 object types: */ + TAB_GEOM_V450_REGION_C = 0x2e, + TAB_GEOM_V450_REGION = 0x2f, + TAB_GEOM_V450_MULTIPLINE_C = 0x31, + TAB_GEOM_V450_MULTIPLINE = 0x32, +/* Version 650 object types: */ + TAB_GEOM_MULTIPOINT_C = 0x34, + TAB_GEOM_MULTIPOINT = 0x35, + TAB_GEOM_COLLECTION_C = 0x37, + TAB_GEOM_COLLECTION = 0x38, +/* Version 800 object types: */ + TAB_GEOM_UNKNOWN1_C = 0x3a, // ??? + TAB_GEOM_UNKNOWN1 = 0x3b, // ??? + TAB_GEOM_V800_REGION_C = 0x3d, + TAB_GEOM_V800_REGION = 0x3e, + TAB_GEOM_V800_MULTIPLINE_C = 0x40, + TAB_GEOM_V800_MULTIPLINE = 0x41, + TAB_GEOM_V800_MULTIPOINT_C = 0x43, + TAB_GEOM_V800_MULTIPOINT = 0x44, + TAB_GEOM_V800_COLLECTION_C = 0x46, + TAB_GEOM_V800_COLLECTION = 0x47, + TAB_GEOM_MAX_TYPE /* TODo: Does this need to be 0x80? */ +} TABGeomType; + +#define TAB_GEOM_GET_VERSION(nGeomType) \ + (((nGeomType) < TAB_GEOM_V450_REGION_C) ? 300: \ + ((nGeomType) < TAB_GEOM_MULTIPOINT_C) ? 450: \ + ((nGeomType) < TAB_GEOM_UNKNOWN1_C) ? 650: 800 ) + + +/*--------------------------------------------------------------------- + * struct TABMAPIndexEntry - Entries found in type 1 blocks of .MAP files + * + * We will use this struct to rebuild the geographic index in memory + *--------------------------------------------------------------------*/ +typedef struct TABMAPIndexEntry_t +{ + // These members refer to the info we find in the file + GInt32 XMin; + GInt32 YMin; + GInt32 XMax; + GInt32 YMax; + GInt32 nBlockPtr; +}TABMAPIndexEntry; + +#define TAB_MAX_ENTRIES_INDEX_BLOCK ((512-4)/20) + + +/*--------------------------------------------------------------------- + * TABVertex + *--------------------------------------------------------------------*/ +typedef struct TABVertex_t +{ + double x; + double y; +} TABVertex; + +/*--------------------------------------------------------------------- + * TABTableType - Attribute table format + *--------------------------------------------------------------------*/ +typedef enum +{ + TABTableNative, // The default + TABTableDBF, + TABTableAccess +} TABTableType; + +/*--------------------------------------------------------------------- + * TABFieldType - Native MapInfo attribute field types + *--------------------------------------------------------------------*/ +typedef enum +{ + TABFUnknown = 0, + TABFChar, + TABFInteger, + TABFSmallInt, + TABFDecimal, + TABFFloat, + TABFDate, + TABFLogical, + TABFTime, + TABFDateTime +} TABFieldType; + +#define TABFIELDTYPE_2_STRING(type) \ + (type == TABFChar ? "Char" : \ + type == TABFInteger ? "Integer" : \ + type == TABFSmallInt ? "SmallInt" : \ + type == TABFDecimal ? "Decimal" : \ + type == TABFFloat ? "Float" : \ + type == TABFDate ? "Date" : \ + type == TABFLogical ? "Logical" : \ + type == TABFTime ? "Time" : \ + type == TABFDateTime ? "DateTime" : \ + "Unknown field type" ) + +/*--------------------------------------------------------------------- + * TABDATFieldDef + *--------------------------------------------------------------------*/ +typedef struct TABDATFieldDef_t +{ + char szName[11]; + char cType; + GByte byLength; + GByte byDecimals; + + TABFieldType eTABType; +} TABDATFieldDef; + +/*--------------------------------------------------------------------- + * TABMAPCoordSecHdr + * struct used in the TABMAPCoordBlock to store info about the coordinates + * for a section of a PLINE MULTIPLE or a REGION. + *--------------------------------------------------------------------*/ +typedef struct TABMAPCoordSecHdr_t +{ + GInt32 numVertices; + GInt32 numHoles; + GInt32 nXMin; + GInt32 nYMin; + GInt32 nXMax; + GInt32 nYMax; + + GInt32 nDataOffset; + int nVertexOffset; +} TABMAPCoordSecHdr; + +/*--------------------------------------------------------------------- + * TABProjInfo + * struct used to store the projection parameters from the .MAP header + *--------------------------------------------------------------------*/ +typedef struct TABProjInfo_t +{ + GByte nProjId; // See MapInfo Ref. Manual, App. F and G + GByte nEllipsoidId; + GByte nUnitsId; + double adProjParams[6]; // params in same order as in .MIF COORDSYS + + GInt16 nDatumId; // Datum Id added in MapInfo 7.8+ (.map V500) + double dDatumShiftX; // Before that, we had to always lookup datum + double dDatumShiftY; // parameters to establish datum id + double dDatumShiftZ; + double adDatumParams[5]; + + // Affine parameters only in .map version 500 and up + GByte nAffineFlag; // 0=No affine param, 1=Affine params + GByte nAffineUnits; + double dAffineParamA; // Affine params + double dAffineParamB; + double dAffineParamC; + double dAffineParamD; + double dAffineParamE; + double dAffineParamF; + +} TABProjInfo; + + +/*--------------------------------------------------------------------- + * TABPenDef - Pen definition information + *--------------------------------------------------------------------*/ +typedef struct TABPenDef_t +{ + GInt32 nRefCount; + GByte nPixelWidth; + GByte nLinePattern; + int nPointWidth; + GInt32 rgbColor; +} TABPenDef; + +/* MI Default = PEN(1,2,0) */ +#define MITAB_PEN_DEFAULT {0, 1, 2, 0, 0x000000} + +/*--------------------------------------------------------------------- + * TABBrushDef - Brush definition information + *--------------------------------------------------------------------*/ +typedef struct TABBrushDef_t +{ + GInt32 nRefCount; + GByte nFillPattern; + GByte bTransparentFill; // 1 = Transparent + GInt32 rgbFGColor; + GInt32 rgbBGColor; +} TABBrushDef; + +/* MI Default = BRUSH(1,0,16777215) */ +#define MITAB_BRUSH_DEFAULT {0, 1, 0, 0, 0xffffff} + +/*--------------------------------------------------------------------- + * TABFontDef - Font Name information + *--------------------------------------------------------------------*/ +typedef struct TABFontDef_t +{ + GInt32 nRefCount; + char szFontName[33]; +} TABFontDef; + +/* MI Default = FONT("Arial",0,0,0) */ +#define MITAB_FONT_DEFAULT {0, "Arial"} + +/*--------------------------------------------------------------------- + * TABSymbolDef - Symbol definition information + *--------------------------------------------------------------------*/ +typedef struct TABSymbolDef_t +{ + GInt32 nRefCount; + GInt16 nSymbolNo; + GInt16 nPointSize; + GByte _nUnknownValue_;// Style??? + GInt32 rgbColor; +} TABSymbolDef; + +/* MI Default = SYMBOL(35,0,12) */ +#define MITAB_SYMBOL_DEFAULT {0, 35, 12, 0, 0x000000} + +/*--------------------------------------------------------------------- + * class TABToolDefTable + * + * Class to handle the list of Drawing Tool Definitions for a dataset + * + * This class also contains methods to read tool defs from the file and + * write them to the file. + *--------------------------------------------------------------------*/ + +class TABToolDefTable +{ + protected: + TABPenDef **m_papsPen; + int m_numPen; + int m_numAllocatedPen; + TABBrushDef **m_papsBrush; + int m_numBrushes; + int m_numAllocatedBrushes; + TABFontDef **m_papsFont; + int m_numFonts; + int m_numAllocatedFonts; + TABSymbolDef **m_papsSymbol; + int m_numSymbols; + int m_numAllocatedSymbols; + + public: + TABToolDefTable(); + ~TABToolDefTable(); + + int ReadAllToolDefs(TABMAPToolBlock *poToolBlock); + int WriteAllToolDefs(TABMAPToolBlock *poToolBlock); + + TABPenDef *GetPenDefRef(int nIndex); + int AddPenDefRef(TABPenDef *poPenDef); + int GetNumPen(); + + TABBrushDef *GetBrushDefRef(int nIndex); + int AddBrushDefRef(TABBrushDef *poBrushDef); + int GetNumBrushes(); + + TABFontDef *GetFontDefRef(int nIndex); + int AddFontDefRef(TABFontDef *poFontDef); + int GetNumFonts(); + + TABSymbolDef *GetSymbolDefRef(int nIndex); + int AddSymbolDefRef(TABSymbolDef *poSymbolDef); + int GetNumSymbols(); + + int GetMinVersionNumber(); +}; + +/*===================================================================== + Classes to handle Object Headers inside TABMAPObjectBlocks + =====================================================================*/ + +class TABMAPObjectBlock; +class TABMAPHeaderBlock; + +class TABMAPObjHdr +{ + public: + TABGeomType m_nType; + GInt32 m_nId; + GInt32 m_nMinX; /* Object MBR */ + GInt32 m_nMinY; + GInt32 m_nMaxX; + GInt32 m_nMaxY; + + TABMAPObjHdr() {}; + virtual ~TABMAPObjHdr() {}; + + static TABMAPObjHdr *NewObj(TABGeomType nNewObjType, GInt32 nId=0); + static TABMAPObjHdr *ReadNextObj(TABMAPObjectBlock *poObjBlock, + TABMAPHeaderBlock *poHeader); + + GBool IsCompressedType(); + int WriteObjTypeAndId(TABMAPObjectBlock *); + void SetMBR(GInt32 nMinX, GInt32 nMinY, GInt32 nMaxX, GInt32 mMaxY); + + virtual int WriteObj(TABMAPObjectBlock *) {return -1;}; + +// protected: + virtual int ReadObj(TABMAPObjectBlock *) {return -1;}; +}; + +class TABMAPObjHdrWithCoord: public TABMAPObjHdr +{ + public: + GInt32 m_nCoordBlockPtr; + GInt32 m_nCoordDataSize; + + /* Eventually this class may have methods to help maintaining refs to + * coord. blocks when splitting object blocks. + */ +}; + + +class TABMAPObjNone: public TABMAPObjHdr +{ + public: + + TABMAPObjNone() {}; + virtual ~TABMAPObjNone() {}; + + virtual int WriteObj(TABMAPObjectBlock *) {return 0;}; + +// protected: + virtual int ReadObj(TABMAPObjectBlock *) {return 0;}; +}; + + +class TABMAPObjPoint: public TABMAPObjHdr +{ + public: + GInt32 m_nX; + GInt32 m_nY; + GByte m_nSymbolId; + + TABMAPObjPoint() {}; + virtual ~TABMAPObjPoint() {}; + + virtual int WriteObj(TABMAPObjectBlock *); + +// protected: + virtual int ReadObj(TABMAPObjectBlock *); +}; + +class TABMAPObjFontPoint: public TABMAPObjPoint +{ + public: + GByte m_nPointSize; + GInt16 m_nFontStyle; + GByte m_nR; + GByte m_nG; + GByte m_nB; + GInt16 m_nAngle; /* In tenths of degree */ + GByte m_nFontId; + + TABMAPObjFontPoint() {}; + virtual ~TABMAPObjFontPoint() {}; + + virtual int WriteObj(TABMAPObjectBlock *); + +// protected: + virtual int ReadObj(TABMAPObjectBlock *); +}; + +class TABMAPObjCustomPoint: public TABMAPObjPoint +{ + public: + GByte m_nUnknown_; + GByte m_nCustomStyle; + GByte m_nFontId; + + TABMAPObjCustomPoint() {}; + virtual ~TABMAPObjCustomPoint() {}; + + virtual int WriteObj(TABMAPObjectBlock *); + +// protected: + virtual int ReadObj(TABMAPObjectBlock *); +}; + + +class TABMAPObjLine: public TABMAPObjHdr +{ + public: + GInt32 m_nX1; + GInt32 m_nY1; + GInt32 m_nX2; + GInt32 m_nY2; + GByte m_nPenId; + + TABMAPObjLine() {}; + virtual ~TABMAPObjLine() {}; + + virtual int WriteObj(TABMAPObjectBlock *); + +// protected: + virtual int ReadObj(TABMAPObjectBlock *); +}; + +class TABMAPObjPLine: public TABMAPObjHdrWithCoord +{ + public: + GInt32 m_numLineSections; /* MULTIPLINE/REGION only. Not in PLINE */ + GInt32 m_nLabelX; /* Centroid/label location */ + GInt32 m_nLabelY; + GInt32 m_nComprOrgX; /* Present only in compressed coord. case */ + GInt32 m_nComprOrgY; + GByte m_nPenId; + GByte m_nBrushId; + GBool m_bSmooth; /* TRUE if (m_nCoordDataSize & 0x80000000) */ + + TABMAPObjPLine() {}; + virtual ~TABMAPObjPLine() {}; + + virtual int WriteObj(TABMAPObjectBlock *); + +// protected: + virtual int ReadObj(TABMAPObjectBlock *); +}; + +class TABMAPObjRectEllipse: public TABMAPObjHdr +{ + public: + GInt32 m_nCornerWidth; /* For rounded rect only */ + GInt32 m_nCornerHeight; + GByte m_nPenId; + GByte m_nBrushId; + + TABMAPObjRectEllipse() {}; + virtual ~TABMAPObjRectEllipse() {}; + + virtual int WriteObj(TABMAPObjectBlock *); + +// protected: + virtual int ReadObj(TABMAPObjectBlock *); +}; + +class TABMAPObjArc: public TABMAPObjHdr +{ + public: + GInt32 m_nStartAngle; + GInt32 m_nEndAngle; + GInt32 m_nArcEllipseMinX; /* MBR of the arc defining ellipse */ + GInt32 m_nArcEllipseMinY; /* Only present in arcs */ + GInt32 m_nArcEllipseMaxX; + GInt32 m_nArcEllipseMaxY; + GByte m_nPenId; + + TABMAPObjArc() {}; + virtual ~TABMAPObjArc() {}; + + virtual int WriteObj(TABMAPObjectBlock *); + +// protected: + virtual int ReadObj(TABMAPObjectBlock *); +}; + + +class TABMAPObjText: public TABMAPObjHdrWithCoord +{ + public: + /* String and its len stored in the nCoordPtr and nCoordSize */ + + GInt16 m_nTextAlignment; + GInt32 m_nAngle; + GInt16 m_nFontStyle; + + GByte m_nFGColorR; + GByte m_nFGColorG; + GByte m_nFGColorB; + GByte m_nBGColorR; + GByte m_nBGColorG; + GByte m_nBGColorB; + + GInt32 m_nLineEndX; + GInt32 m_nLineEndY; + + GInt32 m_nHeight; + GByte m_nFontId; + + GByte m_nPenId; + + TABMAPObjText() {}; + virtual ~TABMAPObjText() {}; + + virtual int WriteObj(TABMAPObjectBlock *); + +// protected: + virtual int ReadObj(TABMAPObjectBlock *); +}; + + +class TABMAPObjMultiPoint: public TABMAPObjHdrWithCoord +{ + public: + GInt32 m_nNumPoints; + GInt32 m_nComprOrgX; /* Present only in compressed coord. case */ + GInt32 m_nComprOrgY; + GByte m_nSymbolId; + GInt32 m_nLabelX; /* Not sure if it's a label point, but */ + GInt32 m_nLabelY; /* it's similar to what we find in PLINE */ + + TABMAPObjMultiPoint() {}; + virtual ~TABMAPObjMultiPoint() {}; + + virtual int WriteObj(TABMAPObjectBlock *); + +// protected: + virtual int ReadObj(TABMAPObjectBlock *); +}; + +class TABMAPObjCollection: public TABMAPObjHdrWithCoord +{ + public: + GInt32 m_nRegionDataSize; + GInt32 m_nPolylineDataSize; + GInt32 m_nMPointDataSize; + GInt32 m_nComprOrgX; /* Present only in compressed coord. case */ + GInt32 m_nComprOrgY; + GInt32 m_nNumMultiPoints; + GInt32 m_nNumRegSections; + GInt32 m_nNumPLineSections; + + GByte m_nMultiPointSymbolId; + GByte m_nRegionPenId; + GByte m_nRegionBrushId; + GByte m_nPolylinePenId; + + TABMAPObjCollection() {}; + virtual ~TABMAPObjCollection() + {} + + virtual int WriteObj(TABMAPObjectBlock *); + +// protected: + virtual int ReadObj(TABMAPObjectBlock *); + + private: + // private copy ctor and assignment operator to prevent shallow copying + TABMAPObjCollection& operator=(const TABMAPObjCollection& rhs); + TABMAPObjCollection(const TABMAPObjCollection& rhs); +}; + +/*===================================================================== + Classes to handle .MAP files low-level blocks + =====================================================================*/ + +typedef struct TABBlockRef_t +{ + GInt32 nBlockPtr; + struct TABBlockRef_t *psPrev; + struct TABBlockRef_t *psNext; +} TABBlockRef; + +/*--------------------------------------------------------------------- + * class TABBinBlockManager + * + * This class is used to keep track of allocated blocks and is used + * by various classes that need to allocate a new block in a .MAP file. + *--------------------------------------------------------------------*/ +class TABBinBlockManager +{ + protected: + int m_nBlockSize; + GInt32 m_nLastAllocatedBlock; + TABBlockRef *m_psGarbageBlocksFirst; + TABBlockRef *m_psGarbageBlocksLast; + char m_szName[32]; /* for debug purposes */ + + public: + TABBinBlockManager(int nBlockSize=512); + ~TABBinBlockManager(); + + GInt32 AllocNewBlock(const char* pszReason = ""); + void Reset(); + void SetLastPtr(int nBlockPtr) {m_nLastAllocatedBlock=nBlockPtr; }; + + void PushGarbageBlockAsFirst(GInt32 nBlockPtr); + void PushGarbageBlockAsLast(GInt32 nBlockPtr); + GInt32 GetFirstGarbageBlock(); + GInt32 PopGarbageBlock(); + + void SetName(const char* pszName); +}; + +/*--------------------------------------------------------------------- + * class TABRawBinBlock + * + * This is the base class used for all other data block types... it + * contains all the base functions to handle binary data. + *--------------------------------------------------------------------*/ + +class TABRawBinBlock +{ + protected: + VSILFILE *m_fp; /* Associated file handle */ + TABAccess m_eAccess; /* Read/Write access mode */ + + int m_nBlockType; + + GByte *m_pabyBuf; /* Buffer to contain the block's data */ + int m_nBlockSize; /* Size of current block (and buffer) */ + int m_nSizeUsed; /* Number of bytes used in buffer */ + GBool m_bHardBlockSize;/* TRUE=Blocks MUST always be nSize bytes */ + /* FALSE=last block may be less than nSize */ + int m_nFileOffset; /* Location of current block in the file */ + int m_nCurPos; /* Next byte to read from m_pabyBuf[] */ + int m_nFirstBlockPtr;/* Size of file header when different from */ + /* block size (used by GotoByteInFile()) */ + int m_nFileSize; + + int m_bModified; /* Used only to detect changes */ + + public: + TABRawBinBlock(TABAccess eAccessMode = TABRead, + GBool bHardBlockSize = TRUE); + virtual ~TABRawBinBlock(); + + virtual int ReadFromFile(VSILFILE *fpSrc, int nOffset, int nSize = 512); + virtual int CommitToFile(); + int CommitAsDeleted(GInt32 nNextBlockPtr); + + virtual int InitBlockFromData(GByte *pabyBuf, + int nBlockSize, int nSizeUsed, + GBool bMakeCopy = TRUE, + VSILFILE *fpSrc = NULL, int nOffset = 0); + virtual int InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset=0); + + int GetBlockType(); + virtual int GetBlockClass() { return TAB_RAWBIN_BLOCK; }; + + GInt32 GetStartAddress() {return m_nFileOffset;}; +#ifdef DEBUG + virtual void Dump(FILE *fpOut = NULL); +#endif + void DumpBytes(GInt32 nValue, int nOffset=0, FILE *fpOut=NULL); + + int GotoByteRel(int nOffset); + int GotoByteInBlock(int nOffset); + int GotoByteInFile(int nOffset, + GBool bForceReadFromFile = FALSE, + GBool bOffsetIsEndOfData = FALSE); + void SetFirstBlockPtr(int nOffset); + + int GetNumUnusedBytes(); + int GetFirstUnusedByteOffset(); + int GetCurAddress(); + + virtual int ReadBytes(int numBytes, GByte *pabyDstBuf); + GByte ReadByte(); + GInt16 ReadInt16(); + GInt32 ReadInt32(); + float ReadFloat(); + double ReadDouble(); + + virtual int WriteBytes(int nBytesToWrite, GByte *pBuf); + int WriteByte(GByte byValue); + int WriteInt16(GInt16 n16Value); + int WriteInt32(GInt32 n32Value); + int WriteFloat(float fValue); + int WriteDouble(double dValue); + int WriteZeros(int nBytesToWrite); + int WritePaddedString(int nFieldSize, const char *pszString); + + void SetModifiedFlag(GBool bModified) {m_bModified=bModified;}; + + // This semi-private method gives a direct access to the internal + // buffer... to be used with extreme care!!!!!!!!! + GByte * GetCurDataPtr() { return (m_pabyBuf + m_nCurPos); } ; +}; + + +/*--------------------------------------------------------------------- + * class TABMAPHeaderBlock + * + * Class to handle Read/Write operation on .MAP Header Blocks + *--------------------------------------------------------------------*/ + +class TABMAPHeaderBlock: public TABRawBinBlock +{ + void InitMembersWithDefaultValues(); + void UpdatePrecision(); + + protected: + TABProjInfo m_sProj; + + public: + TABMAPHeaderBlock(TABAccess eAccessMode = TABRead); + ~TABMAPHeaderBlock(); + + virtual int CommitToFile(); + + virtual int InitBlockFromData(GByte *pabyBuf, + int nBlockSize, int nSizeUsed, + GBool bMakeCopy = TRUE, + VSILFILE *fpSrc = NULL, int nOffset = 0); + virtual int InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset=0); + + virtual int GetBlockClass() { return TABMAP_HEADER_BLOCK; }; + + int Int2Coordsys(GInt32 nX, GInt32 nY, double &dX, double &dY); + int Coordsys2Int(double dX, double dY, GInt32 &nX, GInt32 &nY, + GBool bIgnoreOverflow=FALSE); + int ComprInt2Coordsys(GInt32 nCenterX, GInt32 nCenterY, + int nDeltaX, int nDeltaY, + double &dX, double &dY); + int Int2CoordsysDist(GInt32 nX, GInt32 nY, double &dX, double &dY); + int Coordsys2IntDist(double dX, double dY, GInt32 &nX, GInt32 &nY); + int SetCoordsysBounds(double dXMin, double dYMin, + double dXMax, double dYMax); + + int GetMapObjectSize(int nObjType); + GBool MapObjectUsesCoordBlock(int nObjType); + + int GetProjInfo(TABProjInfo *psProjInfo); + int SetProjInfo(TABProjInfo *psProjInfo); + +#ifdef DEBUG + virtual void Dump(FILE *fpOut = NULL); +#endif + + // Instead of having over 30 get/set methods, we'll make all data + // members public and we will initialize them in the overloaded + // LoadFromFile(). For this reason, this class should be used with care. + + GInt16 m_nMAPVersionNumber; + GInt16 m_nBlockSize; + + double m_dCoordsys2DistUnits; + GInt32 m_nXMin; + GInt32 m_nYMin; + GInt32 m_nXMax; + GInt32 m_nYMax; + GBool m_bIntBoundsOverflow; // Set to TRUE if coordinates + // outside of bounds were written + + GInt32 m_nFirstIndexBlock; + GInt32 m_nFirstGarbageBlock; + GInt32 m_nFirstToolBlock; + GInt32 m_numPointObjects; + GInt32 m_numLineObjects; + GInt32 m_numRegionObjects; + GInt32 m_numTextObjects; + GInt32 m_nMaxCoordBufSize; + + GByte m_nDistUnitsCode; // See Appendix F + GByte m_nMaxSpIndexDepth; + GByte m_nCoordPrecision; // Num. decimal places on coord. + GByte m_nCoordOriginQuadrant; + GByte m_nReflectXAxisCoord; + GByte m_nMaxObjLenArrayId; // See gabyObjLenArray[] + GByte m_numPenDefs; + GByte m_numBrushDefs; + GByte m_numSymbolDefs; + GByte m_numFontDefs; + GInt16 m_numMapToolBlocks; + + double m_XScale; + double m_YScale; + double m_XDispl; + double m_YDispl; + double m_XPrecision; // maximum achievable precision along X axis depending on bounds extent + double m_YPrecision; // maximum achievable precision along Y axis depending on bounds extent +}; + +/*--------------------------------------------------------------------- + * class TABMAPIndexBlock + * + * Class to handle Read/Write operation on .MAP Index Blocks (Type 01) + *--------------------------------------------------------------------*/ + +class TABMAPIndexBlock: public TABRawBinBlock +{ + protected: + int m_numEntries; + TABMAPIndexEntry m_asEntries[TAB_MAX_ENTRIES_INDEX_BLOCK]; + + int ReadNextEntry(TABMAPIndexEntry *psEntry); + int WriteNextEntry(TABMAPIndexEntry *psEntry); + + // Use these to keep track of current block's MBR + GInt32 m_nMinX; + GInt32 m_nMinY; + GInt32 m_nMaxX; + GInt32 m_nMaxY; + + TABBinBlockManager *m_poBlockManagerRef; + + // Info about child currently loaded + TABMAPIndexBlock *m_poCurChild; + int m_nCurChildIndex; + // Also need to know about its parent + TABMAPIndexBlock *m_poParentRef; + + int ReadAllEntries(); + + public: + TABMAPIndexBlock(TABAccess eAccessMode = TABRead); + ~TABMAPIndexBlock(); + + virtual int InitBlockFromData(GByte *pabyBuf, + int nBlockSize, int nSizeUsed, + GBool bMakeCopy = TRUE, + VSILFILE *fpSrc = NULL, int nOffset = 0); + virtual int InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset=0); + virtual int CommitToFile(); + + virtual int GetBlockClass() { return TABMAP_INDEX_BLOCK; }; + + void UnsetCurChild(); + + int GetNumFreeEntries(); + int GetNumEntries() {return m_numEntries;}; + TABMAPIndexEntry *GetEntry( int iIndex ); + int AddEntry(GInt32 XMin, GInt32 YMin, + GInt32 XMax, GInt32 YMax, + GInt32 nBlockPtr, + GBool bAddInThisNodeOnly=FALSE); + int GetCurMaxDepth(); + void GetMBR(GInt32 &nXMin, GInt32 &nYMin, + GInt32 &nXMax, GInt32 &nYMax); + void SetMBR(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax); + + GInt32 GetNodeBlockPtr() { return GetStartAddress();}; + + void SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr); + void SetParentRef(TABMAPIndexBlock *poParent); + void SetCurChildRef(TABMAPIndexBlock *poChild, int nChildIndex); + + int GetCurChildIndex() { return m_nCurChildIndex; } + TABMAPIndexBlock *GetCurChild() { return m_poCurChild; } + TABMAPIndexBlock *GetParentRef() { return m_poParentRef; } + + int SplitNode(GInt32 nNewEntryXMin, GInt32 nNewEntryYMin, + GInt32 nNewEntryXMax, GInt32 nNewEntryYMax); + int SplitRootNode(GInt32 nNewEntryXMin, GInt32 nNewEntryYMin, + GInt32 nNewEntryXMax, GInt32 nNewEntryYMax); + void UpdateCurChildMBR(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax, + GInt32 nBlockPtr); + void RecomputeMBR(); + int InsertEntry(GInt32 XMin, GInt32 YMin, + GInt32 XMax, GInt32 YMax, GInt32 nBlockPtr); + int ChooseSubEntryForInsert(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax); + GInt32 ChooseLeafForInsert(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax); + int UpdateLeafEntry(GInt32 nBlockPtr, + GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax); + int GetCurLeafEntryMBR(GInt32 nBlockPtr, + GInt32 &nXMin, GInt32 &nYMin, + GInt32 &nXMax, GInt32 &nYMax); + + // Static utility functions for node splitting, also used by + // the TABMAPObjectBlock class. + static double ComputeAreaDiff(GInt32 nNodeXMin, GInt32 nNodeYMin, + GInt32 nNodeXMax, GInt32 nNodeYMax, + GInt32 nEntryXMin, GInt32 nEntryYMin, + GInt32 nEntryXMax, GInt32 nEntryYMax); + static int PickSeedsForSplit(TABMAPIndexEntry *pasEntries, + int numEntries, + int nSrcCurChildIndex, + GInt32 nNewEntryXMin, + GInt32 nNewEntryYMin, + GInt32 nNewEntryXMax, + GInt32 nNewEntryYMax, + int &nSeed1, int &nSeed2); +#ifdef DEBUG + virtual void Dump(FILE *fpOut = NULL); +#endif + +}; + +/*--------------------------------------------------------------------- + * class TABMAPObjectBlock + * + * Class to handle Read/Write operation on .MAP Object data Blocks (Type 02) + *--------------------------------------------------------------------*/ + +class TABMAPObjectBlock: public TABRawBinBlock +{ + protected: + int m_numDataBytes; /* Excluding first 4 bytes header */ + GInt32 m_nFirstCoordBlock; + GInt32 m_nLastCoordBlock; + GInt32 m_nCenterX; + GInt32 m_nCenterY; + + // In order to compute block center, we need to keep track of MBR + GInt32 m_nMinX; + GInt32 m_nMinY; + GInt32 m_nMaxX; + GInt32 m_nMaxY; + + // Keep track of current object either in read or read/write mode + int m_nCurObjectOffset; // -1 if there is no current object. + int m_nCurObjectId; // -1 if there is no current object. + TABGeomType m_nCurObjectType; // TAB_GEOM_UNSET if there is no current object. + + int m_bLockCenter; + + public: + TABMAPObjectBlock(TABAccess eAccessMode = TABRead); + ~TABMAPObjectBlock(); + + virtual int CommitToFile(); + virtual int InitBlockFromData(GByte *pabyBuf, + int nBlockSize, int nSizeUsed, + GBool bMakeCopy = TRUE, + VSILFILE *fpSrc = NULL, int nOffset = 0); + virtual int InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset=0); + + virtual int GetBlockClass() { return TABMAP_OBJECT_BLOCK; }; + + virtual int ReadIntCoord(GBool bCompressed, GInt32 &nX, GInt32 &nY); + int WriteIntCoord(GInt32 nX, GInt32 nY, GBool bCompressed); + int WriteIntMBRCoord(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax, + GBool bCompressed); + int UpdateMBR(GInt32 nX, GInt32 nY); + + int PrepareNewObject(TABMAPObjHdr *poObjHdr); + int CommitNewObject(TABMAPObjHdr *poObjHdr); + + void AddCoordBlockRef(GInt32 nCoordBlockAddress); + GInt32 GetFirstCoordBlockAddress() { return m_nFirstCoordBlock; } + GInt32 GetLastCoordBlockAddress() { return m_nLastCoordBlock; } + + void GetMBR(GInt32 &nXMin, GInt32 &nYMin, + GInt32 &nXMax, GInt32 &nYMax); + void SetMBR(GInt32 nXMin, GInt32 nYMin, + GInt32 nXMax, GInt32 nYMax); + + void Rewind(); + void ClearObjects(); + void LockCenter(); + void SetCenterFromOtherBlock(TABMAPObjectBlock* poOtherObjBlock); + int AdvanceToNextObject( TABMAPHeaderBlock * ); + int GetCurObjectOffset() { return m_nCurObjectOffset; } + int GetCurObjectId() { return m_nCurObjectId; } + TABGeomType GetCurObjectType() { return m_nCurObjectType; } + +#ifdef DEBUG + virtual void Dump(FILE *fpOut = NULL) { Dump(fpOut, FALSE); }; + void Dump(FILE *fpOut, GBool bDetails); +#endif + +}; + +/*--------------------------------------------------------------------- + * class TABMAPCoordBlock + * + * Class to handle Read/Write operation on .MAP Coordinate Blocks (Type 03) + *--------------------------------------------------------------------*/ + +class TABMAPCoordBlock: public TABRawBinBlock +{ + protected: + int m_numDataBytes; /* Excluding first 8 bytes header */ + GInt32 m_nNextCoordBlock; + int m_numBlocksInChain; + + GInt32 m_nComprOrgX; + GInt32 m_nComprOrgY; + + // In order to compute block center, we need to keep track of MBR + GInt32 m_nMinX; + GInt32 m_nMinY; + GInt32 m_nMaxX; + GInt32 m_nMaxY; + + TABBinBlockManager *m_poBlockManagerRef; + + int m_nTotalDataSize; // Num bytes in whole chain of blocks + int m_nFeatureDataSize; // Num bytes for current feature coords + + GInt32 m_nFeatureXMin; // Used to keep track of current + GInt32 m_nFeatureYMin; // feature MBR. + GInt32 m_nFeatureXMax; + GInt32 m_nFeatureYMax; + + public: + TABMAPCoordBlock(TABAccess eAccessMode = TABRead); + ~TABMAPCoordBlock(); + + virtual int InitBlockFromData(GByte *pabyBuf, + int nBlockSize, int nSizeUsed, + GBool bMakeCopy = TRUE, + VSILFILE *fpSrc = NULL, int nOffset = 0); + virtual int InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset=0); + virtual int CommitToFile(); + + virtual int GetBlockClass() { return TABMAP_COORD_BLOCK; }; + + void SetMAPBlockManagerRef(TABBinBlockManager *poBlockManager); + virtual int ReadBytes(int numBytes, GByte *pabyDstBuf); + virtual int WriteBytes(int nBytesToWrite, GByte *pBuf); + void SetComprCoordOrigin(GInt32 nX, GInt32 nY); + int ReadIntCoord(GBool bCompressed, GInt32 &nX, GInt32 &nY); + int ReadIntCoords(GBool bCompressed, int numCoords, GInt32 *panXY); + int ReadCoordSecHdrs(GBool bCompressed, int nVersion, + int numSections, TABMAPCoordSecHdr *pasHdrs, + GInt32 &numVerticesTotal); + int WriteCoordSecHdrs(int nVersion, int numSections, + TABMAPCoordSecHdr *pasHdrs, + GBool bCompressed); + + void SetNextCoordBlock(GInt32 nNextCoordBlockAddress); + GInt32 GetNextCoordBlock() { return m_nNextCoordBlock; }; + + int WriteIntCoord(GInt32 nX, GInt32 nY, GBool bCompressed); + + int GetNumBlocksInChain() { return m_numBlocksInChain; }; + + void ResetTotalDataSize() {m_nTotalDataSize = 0;}; + int GetTotalDataSize() {return m_nTotalDataSize;}; + + void SeekEnd(); + void StartNewFeature(); + int GetFeatureDataSize() {return m_nFeatureDataSize;}; +//__TODO__ Can we flush GetFeatureMBR() and all MBR tracking in this class??? + void GetFeatureMBR(GInt32 &nXMin, GInt32 &nYMin, + GInt32 &nXMax, GInt32 &nYMax); + +#ifdef DEBUG + virtual void Dump(FILE *fpOut = NULL); +#endif + +}; + +/*--------------------------------------------------------------------- + * class TABMAPToolBlock + * + * Class to handle Read/Write operation on .MAP Drawing Tool Blocks (Type 05) + * + * In addition to handling the I/O, this class also maintains the list + * of Tool definitions in memory. + *--------------------------------------------------------------------*/ + +class TABMAPToolBlock: public TABRawBinBlock +{ + protected: + int m_numDataBytes; /* Excluding first 8 bytes header */ + GInt32 m_nNextToolBlock; + int m_numBlocksInChain; + + TABBinBlockManager *m_poBlockManagerRef; + + public: + TABMAPToolBlock(TABAccess eAccessMode = TABRead); + ~TABMAPToolBlock(); + + virtual int InitBlockFromData(GByte *pabyBuf, + int nBlockSize, int nSizeUsed, + GBool bMakeCopy = TRUE, + VSILFILE *fpSrc = NULL, int nOffset = 0); + virtual int InitNewBlock(VSILFILE *fpSrc, int nBlockSize, int nFileOffset=0); + virtual int CommitToFile(); + + virtual int GetBlockClass() { return TABMAP_TOOL_BLOCK; }; + + void SetMAPBlockManagerRef(TABBinBlockManager *poBlockManager); + virtual int ReadBytes(int numBytes, GByte *pabyDstBuf); + virtual int WriteBytes(int nBytesToWrite, GByte *pBuf); + + void SetNextToolBlock(GInt32 nNextCoordBlockAddress); + + GBool EndOfChain(); + int GetNumBlocksInChain() { return m_numBlocksInChain; }; + + int CheckAvailableSpace(int nToolType); + +#ifdef DEBUG + virtual void Dump(FILE *fpOut = NULL); +#endif + +}; + + +/*===================================================================== + Classes to deal with .MAP files at the MapInfo object level + =====================================================================*/ + +/*--------------------------------------------------------------------- + * class TABIDFile + * + * Class to handle Read/Write operation on .ID files... the .ID file + * contains an index to the objects in the .MAP file by object id. + *--------------------------------------------------------------------*/ + +class TABIDFile +{ + private: + char *m_pszFname; + VSILFILE *m_fp; + TABAccess m_eAccessMode; + + TABRawBinBlock *m_poIDBlock; + int m_nBlockSize; + GInt32 m_nMaxId; + + public: + TABIDFile(); + ~TABIDFile(); + + int Open(const char *pszFname, const char* pszAccess); + int Open(const char *pszFname, TABAccess eAccess); + int Close(); + + int SyncToDisk(); + + GInt32 GetObjPtr(GInt32 nObjId); + int SetObjPtr(GInt32 nObjId, GInt32 nObjPtr); + GInt32 GetMaxObjId(); + +#ifdef DEBUG + void Dump(FILE *fpOut = NULL); +#endif + +}; + +/*--------------------------------------------------------------------- + * class TABMAPFile + * + * Class to handle Read/Write operation on .MAP files... this class hides + * all the dealings with blocks, indexes, etc. + * Use this class to deal with MapInfo objects directly. + *--------------------------------------------------------------------*/ + +class TABMAPFile +{ + private: + int m_nMinTABVersion; + char *m_pszFname; + VSILFILE *m_fp; + TABAccess m_eAccessMode; + + TABBinBlockManager m_oBlockManager; + + TABMAPHeaderBlock *m_poHeader; + + // Members used to access objects using the spatial index + TABMAPIndexBlock *m_poSpIndex; + + // Defaults to FALSE, i.e. optimized spatial index + GBool m_bQuickSpatialIndexMode; + + // Member used to access objects using the object ids (.ID file) + TABIDFile *m_poIdIndex; + + // Current object data block. + TABMAPObjectBlock *m_poCurObjBlock; + int m_nCurObjPtr; + TABGeomType m_nCurObjType; + int m_nCurObjId; + TABMAPCoordBlock *m_poCurCoordBlock; + + // Drawing Tool Def. table (takes care of all drawing tools in memory) + TABToolDefTable *m_poToolDefTable; + + // Coordinates filter... default is MBR of the whole file + TABVertex m_sMinFilter; + TABVertex m_sMaxFilter; + GInt32 m_XMinFilter; + GInt32 m_YMinFilter; + GInt32 m_XMaxFilter; + GInt32 m_YMaxFilter; + + int m_bUpdated; + int m_bLastOpWasRead; + int m_bLastOpWasWrite; + + int CommitObjAndCoordBlocks(GBool bDeleteObjects =FALSE); + int LoadObjAndCoordBlocks(GInt32 nBlockPtr); + TABMAPObjectBlock *SplitObjBlock(TABMAPObjHdr *poObjHdrToAdd, + int nSizeOfObjToAdd); + int MoveObjToBlock(TABMAPObjHdr *poObjHdr, + TABMAPCoordBlock *poSrcCoordBlock, + TABMAPObjectBlock *poDstObjBlock, + TABMAPCoordBlock **ppoDstCoordBlock); + int PrepareCoordBlock(int nObjType, + TABMAPObjectBlock *poObjBlock, + TABMAPCoordBlock **ppoCoordBlock); + + int InitDrawingTools(); + int CommitDrawingTools(); + + int CommitSpatialIndex(); + + // Stuff related to traversing spatial index. + TABMAPIndexBlock *m_poSpIndexLeaf; + + int LoadNextMatchingObjectBlock(int bFirstObject); + TABRawBinBlock *PushBlock( int nFileOffset ); + + int ReOpenReadWrite(); + + public: + TABMAPFile(); + ~TABMAPFile(); + + int Open(const char *pszFname, const char* pszAccess, + GBool bNoErrorMsg = FALSE ); + int Open(const char *pszFname, TABAccess eAccess, + GBool bNoErrorMsg = FALSE ); + int Close(); + + int SyncToDisk(); + + int SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode = TRUE); + + int Int2Coordsys(GInt32 nX, GInt32 nY, double &dX, double &dY); + int Coordsys2Int(double dX, double dY, GInt32 &nX, GInt32 &nY, + GBool bIgnoreOveflow=FALSE); + int Int2CoordsysDist(GInt32 nX, GInt32 nY, double &dX, double &dY); + int Coordsys2IntDist(double dX, double dY, GInt32 &nX, GInt32 &nY); + void SetCoordFilter(TABVertex sMin, TABVertex sMax); + void GetCoordFilter(TABVertex &sMin, TABVertex &sMax); + void ResetCoordFilter(); + int SetCoordsysBounds(double dXMin, double dYMin, + double dXMax, double dYMax); + + GInt32 GetMaxObjId(); + int MoveToObjId(int nObjId); + void UpdateMapHeaderInfo(TABGeomType nObjType); + int PrepareNewObj(TABMAPObjHdr *poObjHdr); + int PrepareNewObjViaSpatialIndex(TABMAPObjHdr *poObjHdr); + int PrepareNewObjViaObjBlock(TABMAPObjHdr *poObjHdr); + int CommitNewObj(TABMAPObjHdr *poObjHdr); + + void ResetReading(); + int GetNextFeatureId( int nPrevId ); + + int MarkAsDeleted(); + + TABGeomType GetCurObjType(); + int GetCurObjId(); + TABMAPObjectBlock *GetCurObjBlock(); + TABMAPCoordBlock *GetCurCoordBlock(); + TABMAPCoordBlock *GetCoordBlock(int nFileOffset); + TABMAPHeaderBlock *GetHeaderBlock(); + TABIDFile *GetIDFileRef(); + TABRawBinBlock *GetIndexObjectBlock(int nFileOffset); + + int ReadPenDef(int nPenIndex, TABPenDef *psDef); + int ReadBrushDef(int nBrushIndex, TABBrushDef *psDef); + int ReadFontDef(int nFontIndex, TABFontDef *psDef); + int ReadSymbolDef(int nSymbolIndex, TABSymbolDef *psDef); + int WritePenDef(TABPenDef *psDef); + int WriteBrushDef(TABBrushDef *psDef); + int WriteFontDef(TABFontDef *psDef); + int WriteSymbolDef(TABSymbolDef *psDef); + + int GetMinTABFileVersion(); + +#ifdef DEBUG + void Dump(FILE *fpOut = NULL); + void DumpSpatialIndexToMIF(TABMAPIndexBlock *poNode, + FILE *fpMIF, FILE *fpMID, + int nIndexInNode=-1, + int nParentId=-1, + int nCurDepth=0, + int nMaxDepth=-1); +#endif + +}; + + + +/*--------------------------------------------------------------------- + * class TABINDNode + * + * An index node in a .IND file. + * + * This class takes care of reading child nodes as necessary when looking + * for a given key value in the index tree. + *--------------------------------------------------------------------*/ + +class TABINDNode +{ + private: + VSILFILE *m_fp; + TABAccess m_eAccessMode; + TABINDNode *m_poCurChildNode; + TABINDNode *m_poParentNodeRef; + + TABBinBlockManager *m_poBlockManagerRef; + + int m_nSubTreeDepth; + int m_nKeyLength; + TABFieldType m_eFieldType; + GBool m_bUnique; + + GInt32 m_nCurDataBlockPtr; + int m_nCurIndexEntry; + TABRawBinBlock *m_poDataBlock; + int m_numEntriesInNode; + GInt32 m_nPrevNodePtr; + GInt32 m_nNextNodePtr; + + int GotoNodePtr(GInt32 nNewNodePtr); + GInt32 ReadIndexEntry(int nEntryNo, GByte *pKeyValue); + int IndexKeyCmp(GByte *pKeyValue, int nEntryNo); + + int InsertEntry(GByte *pKeyValue, GInt32 nRecordNo, + GBool bInsertAfterCurChild=FALSE, + GBool bMakeNewEntryCurChild=FALSE); + int SetNodeBufferDirectly(int numEntries, GByte *pBuf, + int nCurIndexEntry=0, + TABINDNode *poCurChild=NULL); + + public: + TABINDNode(TABAccess eAccessMode = TABRead); + ~TABINDNode(); + + int InitNode(VSILFILE *fp, int nBlockPtr, + int nKeyLength, int nSubTreeDepth, GBool bUnique, + TABBinBlockManager *poBlockMgr=NULL, + TABINDNode *poParentNode=NULL, + int nPrevNodePtr=0, int nNextNodePtr=0); + + int SetFieldType(TABFieldType eType); + TABFieldType GetFieldType() {return m_eFieldType;}; + + void SetUnique(GBool bUnique){m_bUnique = bUnique;}; + GBool IsUnique() {return m_bUnique;}; + + int GetKeyLength() {return m_nKeyLength;}; + int GetSubTreeDepth() {return m_nSubTreeDepth;}; + GInt32 GetNodeBlockPtr() {return m_nCurDataBlockPtr;}; + int GetNumEntries() {return m_numEntriesInNode;}; + int GetMaxNumEntries() {return (512-12)/(m_nKeyLength+4);}; + + GInt32 FindFirst(GByte *pKeyValue); + GInt32 FindNext(GByte *pKeyValue); + + int CommitToFile(); + + int AddEntry(GByte *pKeyValue, GInt32 nRecordNo, + GBool bAddInThisNodeOnly=FALSE, + GBool bInsertAfterCurChild=FALSE, + GBool bMakeNewEntryCurChild=FALSE); + int SplitNode(); + int SplitRootNode(); + GByte* GetNodeKey(); + int UpdateCurChildEntry(GByte *pKeyValue, GInt32 nRecordNo); + int UpdateSplitChild(GByte *pKeyValue1, GInt32 nRecordNo1, + GByte *pKeyValue2, GInt32 nRecordNo2, + int nNewCurChildNo /* 1 or 2 */); + + int SetNodeBlockPtr(GInt32 nThisNodePtr); + int SetPrevNodePtr(GInt32 nPrevNodePtr); + int SetNextNodePtr(GInt32 nNextNodePtr); + +#ifdef DEBUG + void Dump(FILE *fpOut = NULL); +#endif + +}; + + +/*--------------------------------------------------------------------- + * class TABINDFile + * + * Class to handle table field index (.IND) files... we use this + * class as the main entry point to open and search the table field indexes. + * Note that .IND files are supported for read access only. + *--------------------------------------------------------------------*/ + +class TABINDFile +{ + private: + char *m_pszFname; + VSILFILE *m_fp; + TABAccess m_eAccessMode; + + TABBinBlockManager m_oBlockManager; + + int m_numIndexes; + TABINDNode **m_papoIndexRootNodes; + GByte **m_papbyKeyBuffers; + + int ValidateIndexNo(int nIndexNumber); + int ReadHeader(); + int WriteHeader(); + + public: + TABINDFile(); + ~TABINDFile(); + + int Open(const char *pszFname, const char *pszAccess, + GBool bTestOpenNoError=FALSE); + int Close(); + + int GetNumIndexes() {return m_numIndexes;}; + int SetIndexFieldType(int nIndexNumber, TABFieldType eType); + int SetIndexUnique(int nIndexNumber, GBool bUnique=TRUE); + GByte *BuildKey(int nIndexNumber, GInt32 nValue); + GByte *BuildKey(int nIndexNumber, const char *pszStr); + GByte *BuildKey(int nIndexNumber, double dValue); + GInt32 FindFirst(int nIndexNumber, GByte *pKeyValue); + GInt32 FindNext(int nIndexNumber, GByte *pKeyValue); + + int CreateIndex(TABFieldType eType, int nFieldSize); + int AddEntry(int nIndexNumber, GByte *pKeyValue, GInt32 nRecordNo); + +#ifdef DEBUG + void Dump(FILE *fpOut = NULL); +#endif + +}; + + +/*--------------------------------------------------------------------- + * class TABDATFile + * + * Class to handle Read/Write operation on .DAT files... the .DAT file + * contains the table of attribute field values. + *--------------------------------------------------------------------*/ + +class TABDATFile +{ + private: + char *m_pszFname; + VSILFILE *m_fp; + TABAccess m_eAccessMode; + TABTableType m_eTableType; + + TABRawBinBlock *m_poHeaderBlock; + int m_numFields; + TABDATFieldDef *m_pasFieldDef; + + TABRawBinBlock *m_poRecordBlock; + int m_nBlockSize; + int m_nRecordSize; + int m_nCurRecordId; + GBool m_bCurRecordDeletedFlag; + + GInt32 m_numRecords; + GInt32 m_nFirstRecordPtr; + GBool m_bWriteHeaderInitialized; + GBool m_bWriteEOF; + + int m_bUpdated; + + int InitWriteHeader(); + int WriteHeader(); + + // We know that character strings are limited to 254 chars in MapInfo + // Using a buffer pr. class instance to avoid threading issues with the library + char m_szBuffer[256]; + + public: + TABDATFile(); + ~TABDATFile(); + + int Open(const char *pszFname, const char* pszAccess, + TABTableType eTableType =TABTableNative); + int Open(const char *pszFname, TABAccess eAccess, + TABTableType eTableType =TABTableNative); + int Close(); + + int GetNumFields(); + TABFieldType GetFieldType(int nFieldId); + int GetFieldWidth(int nFieldId); + int GetFieldPrecision(int nFieldId); + int ValidateFieldInfoFromTAB(int iField, const char *pszName, + TABFieldType eType, + int nWidth, int nPrecision); + + int AddField(const char *pszName, TABFieldType eType, + int nWidth, int nPrecision=0); + + int DeleteField( int iField ); + int ReorderFields( int* panMap ); + int AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + + int SyncToDisk(); + + GInt32 GetNumRecords(); + TABRawBinBlock *GetRecordBlock(int nRecordId); + GBool IsCurrentRecordDeleted() { return m_bCurRecordDeletedFlag;}; + int CommitRecordToFile(); + + int MarkAsDeleted(); + int MarkRecordAsExisting(); + + const char *ReadCharField(int nWidth); + GInt32 ReadIntegerField(int nWidth); + GInt16 ReadSmallIntField(int nWidth); + double ReadFloatField(int nWidth); + double ReadDecimalField(int nWidth); + const char *ReadLogicalField(int nWidth); + const char *ReadDateField(int nWidth); + int ReadDateField(int nWidth, int *nYear, int *nMonth, int *nDay); + const char *ReadTimeField(int nWidth); + int ReadTimeField(int nWidth, int *nHour, int *nMinute, + int *nSecond, int *nMS); + const char *ReadDateTimeField(int nWidth); + int ReadDateTimeField(int nWidth, int *nYear, int *nMonth, int *nDay, + int *nHour, int *nMinute, int *nSecond, int *nMS); + + int WriteCharField(const char *pszValue, int nWidth, + TABINDFile *poINDFile, int nIndexNo); + int WriteIntegerField(GInt32 nValue, + TABINDFile *poINDFile, int nIndexNo); + int WriteSmallIntField(GInt16 nValue, + TABINDFile *poINDFile, int nIndexNo); + int WriteFloatField(double dValue, + TABINDFile *poINDFile, int nIndexNo); + int WriteDecimalField(double dValue, int nWidth, int nPrecision, + TABINDFile *poINDFile, int nIndexNo); + int WriteLogicalField(const char *pszValue, + TABINDFile *poINDFile, int nIndexNo); + int WriteDateField(const char *pszValue, + TABINDFile *poINDFile, int nIndexNo); + int WriteDateField(int nYear, int nMonth, int nDay, + TABINDFile *poINDFile, int nIndexNo); + int WriteTimeField(const char *pszValue, + TABINDFile *poINDFile, int nIndexNo); + int WriteTimeField(int nHour, int nMinute, int nSecond, int nMS, + TABINDFile *poINDFile, int nIndexNo); + int WriteDateTimeField(const char *pszValue, + TABINDFile *poINDFile, int nIndexNo); + int WriteDateTimeField(int nYear, int nMonth, int nDay, + int nHour, int nMinute, int nSecond, int nMS, + TABINDFile *poINDFile, int nIndexNo); + +#ifdef DEBUG + void Dump(FILE *fpOut = NULL); +#endif + +}; + + +/*--------------------------------------------------------------------- + * class TABRelation + * + * Class that maintains a relation between 2 tables through a field + * in each table (the SQL "where table1.field1=table2.field2" found in + * TABView datasets). + * + * An instance of this class is used to read data records from the + * combined tables as if they were a single one. + *--------------------------------------------------------------------*/ + +class TABRelation +{ + private: + /* Information about the main table. + */ + TABFile *m_poMainTable; + char *m_pszMainFieldName; + int m_nMainFieldNo; + + /* Information about the related table. + * NOTE: The related field MUST be indexed. + */ + TABFile *m_poRelTable; + char *m_pszRelFieldName; + int m_nRelFieldNo; + + TABINDFile *m_poRelINDFileRef; + int m_nRelFieldIndexNo; + + int m_nUniqueRecordNo; + + /* Main and Rel table field map: + * For each field in the source tables, -1 means that the field is not + * selected, and a value >=0 is the index of this field in the combined + * FeatureDefn + */ + int *m_panMainTableFieldMap; + int *m_panRelTableFieldMap; + + OGRFeatureDefn *m_poDefn; + + void ResetAllMembers(); + GByte *BuildFieldKey(TABFeature *poFeature, int nFieldNo, + TABFieldType eType, int nIndexNo); + + public: + TABRelation(); + ~TABRelation(); + + int Init(const char *pszViewName, + TABFile *poMainTable, TABFile *poRelTable, + const char *pszMainFieldName, + const char *pszRelFieldName, + char **papszSelectedFields); + int CreateRelFields(); + + OGRFeatureDefn *GetFeatureDefn() {return m_poDefn;}; + TABFieldType GetNativeFieldType(int nFieldId); + TABFeature *GetFeature(int nFeatureId); + + int WriteFeature(TABFeature *poFeature, int nFeatureId=-1); + + int SetFeatureDefn(OGRFeatureDefn *poFeatureDefn, + TABFieldType *paeMapInfoNativeFieldTypes=NULL); + int AddFieldNative(const char *pszName, TABFieldType eMapInfoType, + int nWidth=0, int nPrecision=0, + GBool bIndexed=FALSE, GBool bUnique=FALSE, int bApproxOK=TRUE); + + int SetFieldIndexed(int nFieldId); + GBool IsFieldIndexed(int nFieldId); + GBool IsFieldUnique(int nFieldId); + + const char *GetMainFieldName() {return m_pszMainFieldName;}; + const char *GetRelFieldName() {return m_pszRelFieldName;}; +}; + + +/*--------------------------------------------------------------------- + * class MIDDATAFile + * + * Class to handle a file pointer with a copy of the latest readed line + * + *--------------------------------------------------------------------*/ + +class MIDDATAFile +{ + public: + MIDDATAFile(); + ~MIDDATAFile(); + + int Open(const char *pszFname, const char *pszAccess); + int Close(); + + const char *GetLine(); + const char *GetLastLine(); + int Rewind(); + void SaveLine(const char *pszLine); + const char *GetSavedLine(); + void WriteLine(const char*, ...) CPL_PRINT_FUNC_FORMAT (2, 3); + GBool IsValidFeature(const char *pszString); + +// Translation information + void SetTranslation(double, double, double, double); + double GetXTrans(double); + double GetYTrans(double); + double GetXMultiplier(){return m_dfXMultiplier;} + const char *GetDelimiter(){return m_pszDelimiter;} + void SetDelimiter(const char *pszDelimiter){m_pszDelimiter=pszDelimiter;} + + void SetEof(GBool bEof); + GBool GetEof(); + + private: + VSILFILE *m_fp; + const char *m_pszDelimiter; + + // Set limit for the length of a line +#define MIDMAXCHAR 10000 + char m_szLastRead[MIDMAXCHAR]; + char m_szSavedLine[MIDMAXCHAR]; + + char *m_pszFname; + TABAccess m_eAccessMode; + double m_dfXMultiplier; + double m_dfYMultiplier; + double m_dfXDisplacement; + double m_dfYDisplacement; + GBool m_bEof; +}; + + + +/*===================================================================== + Function prototypes + =====================================================================*/ + +TABRawBinBlock *TABCreateMAPBlockFromFile(VSILFILE *fpSrc, int nOffset, + int nSize = 512, + GBool bHardBlockSize = TRUE, + TABAccess eAccessMode = TABRead); + + +#endif /* _MITAB_PRIV_H_INCLUDED_ */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_rawbinblock.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_rawbinblock.cpp new file mode 100644 index 000000000..9b72e128e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_rawbinblock.cpp @@ -0,0 +1,1324 @@ +/********************************************************************** + * $Id: mitab_rawbinblock.cpp,v 1.11 2007-06-11 14:40:03 dmorissette Exp $ + * + * Name: mitab_rawbinblock.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABRawBinBlock class used to handle + * reading/writing blocks in the .MAP files + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999, 2000, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_rawbinblock.cpp,v $ + * Revision 1.11 2007-06-11 14:40:03 dmorissette + * Fixed another issue related to attempting to read past EOF while writing + * collections (bug 1657) + * + * Revision 1.10 2007/02/22 18:35:53 dmorissette + * Fixed problem writing collections where MITAB was sometimes trying to + * read past EOF in write mode (bug 1657). + * + * Revision 1.9 2006/11/28 18:49:08 dmorissette + * Completed changes to split TABMAPObjectBlocks properly and produce an + * optimal spatial index (bug 1585) + * + * Revision 1.8 2005/10/06 19:15:31 dmorissette + * Collections: added support for reading/writing pen/brush/symbol ids and + * for writing collection objects to .TAB/.MAP (bug 1126) + * + * Revision 1.7 2004/12/01 18:25:03 dmorissette + * Fixed potential memory leaks in error conditions (bug 881) + * + * Revision 1.6 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.5 2000/02/28 17:06:06 daniel + * Added m_bModified flag + * + * Revision 1.4 2000/01/15 22:30:45 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.3 1999/09/26 14:59:37 daniel + * Implemented write support + * + * Revision 1.2 1999/09/16 02:39:17 daniel + * Completed read support for most feature types + * + * Revision 1.1 1999/07/12 04:18:25 daniel + * Initial checkin + * + **********************************************************************/ + +#include "mitab.h" + +/*===================================================================== + * class TABRawBinBlock + *====================================================================*/ + + +/********************************************************************** + * TABRawBinBlock::TABRawBinBlock() + * + * Constructor. + **********************************************************************/ +TABRawBinBlock::TABRawBinBlock(TABAccess eAccessMode /*= TABRead*/, + GBool bHardBlockSize /*= TRUE*/) +{ + m_fp = NULL; + m_pabyBuf = NULL; + m_nFirstBlockPtr = 0; + m_nBlockSize = m_nSizeUsed = m_nFileOffset = m_nCurPos = 0; + m_bHardBlockSize = bHardBlockSize; + m_nFileSize = -1; + + m_bModified = FALSE; + + m_eAccess = eAccessMode; + +} + +/********************************************************************** + * TABRawBinBlock::~TABRawBinBlock() + * + * Destructor. + **********************************************************************/ +TABRawBinBlock::~TABRawBinBlock() +{ + if (m_pabyBuf) + CPLFree(m_pabyBuf); +} + + +/********************************************************************** + * TABRawBinBlock::ReadFromFile() + * + * Load data from the specified file location and initialize the block. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABRawBinBlock::ReadFromFile(VSILFILE *fpSrc, int nOffset, + int nSize /*= 512*/) +{ + GByte *pabyBuf; + + if (fpSrc == NULL || nSize == 0) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRawBinBlock::ReadFromFile(): Assertion Failed!"); + return -1; + } + + m_fp = fpSrc; + + VSIFSeekL(fpSrc, 0, SEEK_END); + m_nFileSize = (int)VSIFTellL(m_fp); + + m_nFileOffset = nOffset; + m_nCurPos = 0; + m_bModified = FALSE; + + /*---------------------------------------------------------------- + * Alloc a buffer to contain the data + *---------------------------------------------------------------*/ + pabyBuf = (GByte*)CPLMalloc(nSize*sizeof(GByte)); + + /*---------------------------------------------------------------- + * Read from the file + *---------------------------------------------------------------*/ + if (VSIFSeekL(fpSrc, nOffset, SEEK_SET) != 0 || + (m_nSizeUsed = VSIFReadL(pabyBuf, sizeof(GByte), nSize, fpSrc) ) == 0 || + (m_bHardBlockSize && m_nSizeUsed != nSize ) ) + { + CPLError(CE_Failure, CPLE_FileIO, + "ReadFromFile() failed reading %d bytes at offset %d.", + nSize, nOffset); + CPLFree(pabyBuf); + return -1; + } + + /*---------------------------------------------------------------- + * Init block with the data we just read + *---------------------------------------------------------------*/ + return InitBlockFromData(pabyBuf, nSize, m_nSizeUsed, + FALSE, fpSrc, nOffset); +} + + +/********************************************************************** + * TABRawBinBlock::CommitToFile() + * + * Commit the current state of the binary block to the file to which + * it has been previously attached. + * + * Derived classes may want to (optionally) reimplement this method if + * they need to do special processing before committing the block to disk. + * + * For files created with bHardBlockSize=TRUE, a complete block of + * the specified size is always written, otherwise only the number of + * used bytes in the block will be written to disk. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABRawBinBlock::CommitToFile() +{ + int nStatus = 0; + + if (m_fp == NULL || m_nBlockSize <= 0 || m_pabyBuf == NULL || + m_nFileOffset < 0) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABRawBinBlock::CommitToFile(): Block has not been initialized yet!"); + return -1; + } + + /*---------------------------------------------------------------- + * If block has not been modified, then just return... nothing to do. + *---------------------------------------------------------------*/ + if (!m_bModified) + return 0; + + /*---------------------------------------------------------------- + * Move the output file pointer to the right position... + *---------------------------------------------------------------*/ + if (VSIFSeekL(m_fp, m_nFileOffset, SEEK_SET) != 0) + { + /*------------------------------------------------------------ + * Moving pointer failed... we may need to pad with zeros if + * block destination is beyond current end of file. + *-----------------------------------------------------------*/ + int nCurPos; + nCurPos = (int)VSIFTellL(m_fp); + + if (nCurPos < m_nFileOffset && + VSIFSeekL(m_fp, 0L, SEEK_END) == 0 && + (nCurPos = (int)VSIFTellL(m_fp)) < m_nFileOffset) + { + GByte cZero = 0; + + while(nCurPos < m_nFileOffset && nStatus == 0) + { + if (VSIFWriteL(&cZero, 1, 1, m_fp) != 1) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing 1 byte at offset %d.", nCurPos); + nStatus = -1; + break; + } + nCurPos++; + } + } + + if (nCurPos != m_nFileOffset) + nStatus = -1; // Error message will follow below + + } + + /*---------------------------------------------------------------- + * At this point we are ready to write to the file. + * + * If m_bHardBlockSize==FALSE, then we do not write a complete block; + * we write only the part of the block that was used. + *---------------------------------------------------------------*/ + int numBytesToWrite = m_bHardBlockSize?m_nBlockSize:m_nSizeUsed; + + /*CPLDebug("MITAB", "Commiting to offset %d", m_nFileOffset);*/ + + if (nStatus != 0 || + VSIFWriteL(m_pabyBuf,sizeof(GByte), + numBytesToWrite, m_fp) != (size_t)numBytesToWrite ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing %d bytes at offset %d.", + numBytesToWrite, m_nFileOffset); + return -1; + } + if( m_nFileOffset + numBytesToWrite > m_nFileSize ) + { + m_nFileSize = m_nFileOffset + numBytesToWrite; + } + + VSIFFlushL(m_fp); + + m_bModified = FALSE; + + return 0; +} + +/********************************************************************** + * TABRawBinBlock::CommitAsDeleted() + * + * Commit current block to file using block type 4 (garbage block) + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABRawBinBlock::CommitAsDeleted(GInt32 nNextBlockPtr) +{ + int nStatus = 0; + + CPLErrorReset(); + + if ( m_pabyBuf == NULL ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "CommitAsDeleted(): Block has not been initialized yet!"); + return -1; + } + + /*----------------------------------------------------------------- + * Create deleted block header + *----------------------------------------------------------------*/ + GotoByteInBlock(0x000); + WriteInt16(TABMAP_GARB_BLOCK); // Block type code + WriteInt32(nNextBlockPtr); + + if( CPLGetLastErrorType() == CE_Failure ) + nStatus = CPLGetLastErrorNo(); + + /*----------------------------------------------------------------- + * OK, call the base class to write the block to disk. + *----------------------------------------------------------------*/ + if (nStatus == 0) + { +#ifdef DEBUG_VERBOSE + CPLDebug("MITAB", "Commiting GARBAGE block to offset %d", m_nFileOffset); +#endif + nStatus = TABRawBinBlock::CommitToFile(); + m_nSizeUsed = 0; + } + + return nStatus; +} + +/********************************************************************** + * TABRawBinBlock::InitBlockFromData() + * + * Set the binary data buffer and initialize the block. + * + * Calling ReadFromFile() will automatically call InitBlockFromData() to + * complete the initialization of the block after the data is read from the + * file. Derived classes should implement their own version of + * InitBlockFromData() if they need specific initialization... in this + * case the derived InitBlockFromData() should call + * TABRawBinBlock::InitBlockFromData() before doing anything else. + * + * By default, the buffer will be copied, but if bMakeCopy = FALSE then + * it won't be copied, and the object will keep a reference to the + * user's buffer... and this object will eventually free the user's buffer. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABRawBinBlock::InitBlockFromData(GByte *pabyBuf, + int nBlockSize, int nSizeUsed, + GBool bMakeCopy /* = TRUE */, + VSILFILE *fpSrc /* = NULL */, + int nOffset /* = 0 */) +{ + m_fp = fpSrc; + m_nFileOffset = nOffset; + m_nCurPos = 0; + m_bModified = FALSE; + + /*---------------------------------------------------------------- + * Alloc or realloc the buffer to contain the data if necessary + *---------------------------------------------------------------*/ + if (!bMakeCopy) + { + if (m_pabyBuf != NULL) + CPLFree(m_pabyBuf); + m_pabyBuf = pabyBuf; + m_nBlockSize = nBlockSize; + m_nSizeUsed = nSizeUsed; + } + else if (m_pabyBuf == NULL || nBlockSize != m_nBlockSize) + { + m_pabyBuf = (GByte*)CPLRealloc(m_pabyBuf, nBlockSize*sizeof(GByte)); + m_nBlockSize = nBlockSize; + m_nSizeUsed = nSizeUsed; + memcpy(m_pabyBuf, pabyBuf, m_nSizeUsed); + } + + /*---------------------------------------------------------------- + * Extract block type... header block (first block in a file) has + * no block type, so we assign one by default. + *---------------------------------------------------------------*/ + if (m_nFileOffset == 0) + m_nBlockType = TABMAP_HEADER_BLOCK; + else + { + // Block type will be validated only if GetBlockType() is called + m_nBlockType = (int)m_pabyBuf[0]; + } + + return 0; +} + +/********************************************************************** + * TABRawBinBlock::InitNewBlock() + * + * Initialize the block so that it knows to which file is is attached, + * its block size, etc. + * + * This is an alternative to calling ReadFromFile() or InitBlockFromData() + * that puts the block in a stable state without loading any initial + * data in it. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABRawBinBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize, + int nFileOffset /* = 0*/) +{ + m_fp = fpSrc; + m_nBlockSize = nBlockSize; + m_nSizeUsed = 0; + m_nCurPos = 0; + m_bModified = FALSE; + + if (nFileOffset > 0) + m_nFileOffset = nFileOffset; + else + m_nFileOffset = 0; + + if( m_fp != NULL && m_nFileSize < 0 && m_eAccess == TABReadWrite ) + { + int nCurPos = (int)VSIFTellL(m_fp); + VSIFSeekL(fpSrc, 0, SEEK_END); + m_nFileSize = (int)VSIFTellL(m_fp); + VSIFSeekL(fpSrc, nCurPos, SEEK_SET); + } + + m_nBlockType = -1; + + m_pabyBuf = (GByte*)CPLRealloc(m_pabyBuf, m_nBlockSize*sizeof(GByte)); + memset(m_pabyBuf, 0, m_nBlockSize); + + return 0; +} + + +/********************************************************************** + * TABRawBinBlock::GetBlockType() + * + * Return the block type for the current object. + * + * Returns a block type >= 0 if succesful or -1 if an error happened, in + * which case CPLError() will have been called. + **********************************************************************/ +int TABRawBinBlock::GetBlockType() +{ + if (m_pabyBuf == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GetBlockType(): Block has not been initialized."); + return -1; + } + + if (m_nBlockType > TABMAP_LAST_VALID_BLOCK_TYPE) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GetBlockType(): Unsupported block type %d.", + m_nBlockType); + return -1; + } + + return m_nBlockType; +} + +/********************************************************************** + * TABRawBinBlock::GotoByteInBlock() + * + * Move the block pointer to the specified position relative to the + * beginning of the block. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABRawBinBlock::GotoByteInBlock(int nOffset) +{ + if ( (m_eAccess == TABRead && nOffset > m_nSizeUsed) || + (m_eAccess != TABRead && nOffset > m_nBlockSize) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GotoByteInBlock(): Attempt to go past end of data block."); + return -1; + } + + if (nOffset < 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GotoByteInBlock(): Attempt to go before start of data block."); + return -1; + } + + m_nCurPos = nOffset; + + m_nSizeUsed = MAX(m_nSizeUsed, m_nCurPos); + + return 0; +} + +/********************************************************************** + * TABRawBinBlock::GotoByteRel() + * + * Move the block pointer by the specified number of bytes relative + * to its current position. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABRawBinBlock::GotoByteRel(int nOffset) +{ + return GotoByteInBlock(m_nCurPos + nOffset); +} + +/********************************************************************** + * TABRawBinBlock::GotoByteInFile() + * + * Move the block pointer to the specified position relative to the + * beginning of the file. + * + * In read access, the current block may be reloaded to contain a right + * block of binary data if necessary. + * + * In write mode, the current block may automagically be committed to + * disk and a new block initialized if necessary. + * + * bForceReadFromFile is used in write mode to read the new block data from + * file instead of creating an empty block. (Useful for TABCollection + * or other cases that need to do random access in the file in write mode.) + * + * bOffsetIsEndOfData is set to TRUE to indicate that the nOffset + * to which we are attempting to go is the end of the used data in this + * block (we are positioninig ourselves to append data), so if the nOffset + * corresponds to the beginning of a 512 bytes block then we should really + * be positioning ourselves at the end of the block that ends at this + * address instead of at the beginning of the blocks that starts at this + * address. This case can happen when going back and forth to write collection + * objects to a Coordblock and is documented in bug 1657. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABRawBinBlock::GotoByteInFile(int nOffset, + GBool bForceReadFromFile /*=FALSE*/, + GBool bOffsetIsEndOfData /*=FALSE*/) +{ + int nNewBlockPtr; + + if (nOffset < 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GotoByteInFile(): Attempt to go before start of file."); + return -1; + } + + nNewBlockPtr = ( (nOffset-m_nFirstBlockPtr)/m_nBlockSize)*m_nBlockSize + + m_nFirstBlockPtr; + + if (m_eAccess == TABRead) + { + if ( (nOffset=m_nFileOffset+m_nSizeUsed) && + ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0) + { + // Failed reading new block... error has already been reported. + return -1; + } + } + else if (m_eAccess == TABWrite) + { + if ( (nOffset=m_nFileOffset+m_nBlockSize) && + (CommitToFile() != 0 || + InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0 ) ) + { + // Failed reading new block... error has already been reported. + return -1; + } + } + else if (m_eAccess == TABReadWrite) + { + // TODO: THIS IS NOT REAL read/write access (it's more extended write) + // Currently we try to read from file only if explicitly requested. + // If we ever want true read/write mode we should implement + // more smarts to detect whether the caller wants an existing block to + // be read, or a new one to be created from scratch. + // CommitToFile() should only be called only if something changed. + // + if (bOffsetIsEndOfData && nOffset%m_nBlockSize == 0) + { + /* We're trying to go byte 512 of a block that's full of data. + * In this case it's okay to place the m_nCurPos at byte 512 + * which is past the end of the block. + */ + + /* Make sure we request the block that ends with requested + * address and not the following block that doesn't exist + * yet on disk */ + nNewBlockPtr -= m_nBlockSize; + + if ( (nOffset < m_nFileOffset || + nOffset > m_nFileOffset+m_nBlockSize) && + (CommitToFile() != 0 || + (!bForceReadFromFile && + InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0) || + (bForceReadFromFile && + ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0) ) ) + { + // Failed reading new block... error has already been reported. + return -1; + } + } + else + { + if( !bForceReadFromFile && m_nFileSize > 0 && + nOffset < m_nFileSize ) + { + bForceReadFromFile = TRUE; + if ( !(nOffset < m_nFileOffset || + nOffset >= m_nFileOffset+m_nBlockSize) ) + { + if ( (nOffset=m_nFileOffset+m_nSizeUsed) && + (CommitToFile() != 0 || + ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0) ) + { + // Failed reading new block... error has already been reported. + return -1; + } + } + } + + if ( (nOffset < m_nFileOffset || + nOffset >= m_nFileOffset+m_nBlockSize) && + (CommitToFile() != 0 || + (!bForceReadFromFile && + InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0) || + (bForceReadFromFile && + ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0) ) ) + { + // Failed reading new block... error has already been reported. + return -1; + } + } + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Access mode not supported yet!"); + return -1; + } + + m_nCurPos = nOffset-m_nFileOffset; + + m_nSizeUsed = MAX(m_nSizeUsed, m_nCurPos); + + return 0; +} + + +/********************************************************************** + * TABRawBinBlock::SetFirstBlockPtr() + * + * Set the position in the file at which the first block starts. + * This value will usually be the header size and needs to be specified + * only if the header size is different from the other blocks size. + * + * This value will be used by GotoByteInFile() to properly align the data + * blocks that it loads automatically when a requested position is outside + * of the block currently in memory. + **********************************************************************/ +void TABRawBinBlock::SetFirstBlockPtr(int nOffset) +{ + m_nFirstBlockPtr = nOffset; +} + + +/********************************************************************** + * TABRawBinBlock::GetNumUnusedBytes() + * + * Return the number of unused bytes in this block. + **********************************************************************/ +int TABRawBinBlock::GetNumUnusedBytes() +{ + return (m_nBlockSize - m_nSizeUsed); +} + +/********************************************************************** + * TABRawBinBlock::GetFirstUnusedByteOffset() + * + * Return the position of the first unused byte in this block relative + * to the beginning of the file, or -1 if the block is full. + **********************************************************************/ +int TABRawBinBlock::GetFirstUnusedByteOffset() +{ + if (m_nSizeUsed < m_nBlockSize) + return m_nFileOffset + m_nSizeUsed; + else + return -1; +} + +/********************************************************************** + * TABRawBinBlock::GetCurAddress() + * + * Return the current pointer position, relative to beginning of file. + **********************************************************************/ +int TABRawBinBlock::GetCurAddress() +{ + return (m_nFileOffset + m_nCurPos); +} + +/********************************************************************** + * TABRawBinBlock::ReadBytes() + * + * Copy the number of bytes from the data block's internal buffer to + * the user's buffer pointed by pabyDstBuf. + * + * Passing pabyDstBuf = NULL will only move the read pointer by the + * specified number of bytes as if the copy had happened... but it + * won't crash. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABRawBinBlock::ReadBytes(int numBytes, GByte *pabyDstBuf) +{ + /*---------------------------------------------------------------- + * Make sure block is initialized with Read access and that the + * operation won't go beyond the buffer's size. + *---------------------------------------------------------------*/ + if (m_pabyBuf == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "ReadBytes(): Block has not been initialized."); + return -1; + } + + if (m_nCurPos + numBytes > m_nSizeUsed) + { + CPLError(CE_Failure, CPLE_AppDefined, + "ReadBytes(): Attempt to read past end of data block."); + return -1; + } + + if (pabyDstBuf) + { + memcpy(pabyDstBuf, m_pabyBuf + m_nCurPos, numBytes); + } + + m_nCurPos += numBytes; + + return 0; +} + +/********************************************************************** + * TABRawBinBlock::Read() + * + * MapInfo files are binary files with LSB first (Intel) byte + * ordering. The following functions will read from the input file + * and return a value with the bytes ordered properly for the current + * platform. + **********************************************************************/ +GByte TABRawBinBlock::ReadByte() +{ + GByte byValue; + + ReadBytes(1, (GByte*)(&byValue)); + + return byValue; +} + +GInt16 TABRawBinBlock::ReadInt16() +{ + GInt16 n16Value; + + ReadBytes(2, (GByte*)(&n16Value)); + +#ifdef CPL_MSB + return (GInt16)CPL_SWAP16(n16Value); +#else + return n16Value; +#endif +} + +GInt32 TABRawBinBlock::ReadInt32() +{ + GInt32 n32Value; + + ReadBytes(4, (GByte*)(&n32Value)); + +#ifdef CPL_MSB + return (GInt32)CPL_SWAP32(n32Value); +#else + return n32Value; +#endif +} + +float TABRawBinBlock::ReadFloat() +{ + float fValue; + + ReadBytes(4, (GByte*)(&fValue)); + +#ifdef CPL_MSB + *(GUInt32*)(&fValue) = CPL_SWAP32(*(GUInt32*)(&fValue)); +#endif + return fValue; +} + +double TABRawBinBlock::ReadDouble() +{ + double dValue; + + ReadBytes(8, (GByte*)(&dValue)); + +#ifdef CPL_MSB + CPL_SWAPDOUBLE(&dValue); +#endif + + return dValue; +} + + + +/********************************************************************** + * TABRawBinBlock::WriteBytes() + * + * Copy the number of bytes from the user's buffer pointed by pabySrcBuf + * to the data block's internal buffer. + * Note that this call only writes to the memory buffer... nothing is + * written to the file until WriteToFile() is called. + * + * Passing pabySrcBuf = NULL will only move the write pointer by the + * specified number of bytes as if the copy had happened... but it + * won't crash. + * + * Returns 0 if succesful or -1 if an error happened, in which case + * CPLError() will have been called. + **********************************************************************/ +int TABRawBinBlock::WriteBytes(int nBytesToWrite, GByte *pabySrcBuf) +{ + /*---------------------------------------------------------------- + * Make sure block is initialized with Write access and that the + * operation won't go beyond the buffer's size. + *---------------------------------------------------------------*/ + if (m_pabyBuf == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "WriteBytes(): Block has not been initialized."); + return -1; + } + + if (m_eAccess == TABRead ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "WriteBytes(): Block does not support write operations."); + return -1; + } + + if (m_nCurPos + nBytesToWrite > m_nBlockSize) + { + CPLError(CE_Failure, CPLE_AppDefined, + "WriteBytes(): Attempt to write past end of data block."); + return -1; + } + + /*---------------------------------------------------------------- + * Everything is OK... copy the data + *---------------------------------------------------------------*/ + if (pabySrcBuf) + { + memcpy(m_pabyBuf + m_nCurPos, pabySrcBuf, nBytesToWrite); + } + + m_nCurPos += nBytesToWrite; + + m_nSizeUsed = MAX(m_nSizeUsed, m_nCurPos); + + m_bModified = TRUE; + + return 0; +} + + +/********************************************************************** + * TABRawBinBlock::Write() + * + * Arc/Info files are binary files with MSB first (Motorola) byte + * ordering. The following functions will reorder the byte for the + * value properly and write that to the output file. + * + * If a problem happens, then CPLError() will be called and + * CPLGetLastErrNo() can be used to test if a write operation was + * succesful. + **********************************************************************/ +int TABRawBinBlock::WriteByte(GByte byValue) +{ + return WriteBytes(1, (GByte*)&byValue); +} + +int TABRawBinBlock::WriteInt16(GInt16 n16Value) +{ +#ifdef CPL_MSB + n16Value = (GInt16)CPL_SWAP16(n16Value); +#endif + + return WriteBytes(2, (GByte*)&n16Value); +} + +int TABRawBinBlock::WriteInt32(GInt32 n32Value) +{ +#ifdef CPL_MSB + n32Value = (GInt32)CPL_SWAP32(n32Value); +#endif + + return WriteBytes(4, (GByte*)&n32Value); +} + +int TABRawBinBlock::WriteFloat(float fValue) +{ +#ifdef CPL_MSB + *(GUInt32*)(&fValue) = CPL_SWAP32(*(GUInt32*)(&fValue)); +#endif + + return WriteBytes(4, (GByte*)&fValue); +} + +int TABRawBinBlock::WriteDouble(double dValue) +{ +#ifdef CPL_MSB + CPL_SWAPDOUBLE(&dValue); +#endif + + return WriteBytes(8, (GByte*)&dValue); +} + + +/********************************************************************** + * TABRawBinBlock::WriteZeros() + * + * Write a number of zeros (sepcified in bytes) at the current position + * in the file. + * + * If a problem happens, then CPLError() will be called and + * CPLGetLastErrNo() can be used to test if a write operation was + * succesful. + **********************************************************************/ +int TABRawBinBlock::WriteZeros(int nBytesToWrite) +{ + char acZeros[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i; + int nStatus = 0; + + /* Write by 8 bytes chunks. The last chunk may be less than 8 bytes + */ + for(i=0; nStatus == 0 && i< nBytesToWrite; i+=8) + { + nStatus = WriteBytes(MIN(8,(nBytesToWrite-i)), (GByte*)acZeros); + } + + return nStatus; +} + +/********************************************************************** + * TABRawBinBlock::WritePaddedString() + * + * Write a string and pad the end of the field (up to nFieldSize) with + * spaces number of spaces at the current position in the file. + * + * If a problem happens, then CPLError() will be called and + * CPLGetLastErrNo() can be used to test if a write operation was + * succesful. + **********************************************************************/ +int TABRawBinBlock::WritePaddedString(int nFieldSize, const char *pszString) +{ + char acSpaces[8] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}; + int i, nLen, numSpaces; + int nStatus = 0; + + nLen = strlen(pszString); + nLen = MIN(nLen, nFieldSize); + numSpaces = nFieldSize - nLen; + + if (nLen > 0) + nStatus = WriteBytes(nLen, (GByte*)pszString); + + /* Write spaces by 8 bytes chunks. The last chunk may be less than 8 bytes + */ + for(i=0; nStatus == 0 && i< numSpaces; i+=8) + { + nStatus = WriteBytes(MIN(8,(numSpaces-i)), (GByte*)acSpaces); + } + + return nStatus; +} + +/********************************************************************** + * TABRawBinBlock::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABRawBinBlock::Dump(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABRawBinBlock::Dump() -----\n"); + if (m_pabyBuf == NULL) + { + fprintf(fpOut, "Block has not been initialized yet."); + } + else + { + if( m_nBlockType == TABMAP_GARB_BLOCK ) + { + fprintf(fpOut,"Garbage Block (type %d) at offset %d.\n", + m_nBlockType, m_nFileOffset); + int nNextGarbageBlock; + memcpy(&nNextGarbageBlock, m_pabyBuf + 2, 4); + CPL_LSBPTR32(&nNextGarbageBlock); + fprintf(fpOut," m_nNextGarbageBlock = %d\n", nNextGarbageBlock); + } + else + { + fprintf(fpOut, "Block (type %d) size=%d bytes at offset %d in file.\n", + m_nBlockType, m_nBlockSize, m_nFileOffset); + fprintf(fpOut, "Current pointer at byte %d\n", m_nCurPos); + } + } + + fflush(fpOut); +} + +#endif // DEBUG + + +/********************************************************************** + * DumpBytes() + * + * Read and dump the contents of an Binary file. + **********************************************************************/ +void TABRawBinBlock::DumpBytes(GInt32 nValue, int nOffset /*=0*/, + FILE *fpOut /*=NULL*/) +{ + GInt32 anVal[2]; + GInt16 n16Val1, n16Val2; + float fValue; + char *pcValue; + double dValue; + + pcValue = (char*)&nValue; + memcpy(&fValue, &nValue, 4); + + memcpy(&n16Val1, pcValue + 2, sizeof(GInt16)); + memcpy(&n16Val2, pcValue, sizeof(GInt16)); + + anVal[0] = anVal[1] = 0; + + /* For double precision values, we only use the first half + * of the height bytes... and leave the other 4 bytes as zeros! + * It's a bit of a hack, but it seems to be enough for the + * precision of the values we print! + */ +#ifdef CPL_MSB + anVal[0] = nValue; +#else + anVal[1] = nValue; +#endif + memcpy(&dValue, anVal, 8); + + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "%d\t0x%8.8x %-5d\t%-6d %-6d %5.3e d=%5.3e", + nOffset, nValue, nValue, + n16Val1, n16Val2, fValue, dValue); + + printf("\t[%c%c%c%c]\n", isprint(pcValue[0])?pcValue[0]:'.', + isprint(pcValue[1])?pcValue[1]:'.', + isprint(pcValue[2])?pcValue[2]:'.', + isprint(pcValue[3])?pcValue[3]:'.'); +} + + + +/********************************************************************** + * TABCreateMAPBlockFromFile() + * + * Load data from the specified file location and create and initialize + * a TABMAP*Block of the right type to handle it. + * + * Returns the new object if succesful or NULL if an error happened, in + * which case CPLError() will have been called. + **********************************************************************/ +TABRawBinBlock *TABCreateMAPBlockFromFile(VSILFILE *fpSrc, int nOffset, + int nSize /*= 512*/, + GBool bHardBlockSize /*= TRUE */, + TABAccess eAccessMode /*= TABRead*/) +{ + TABRawBinBlock *poBlock = NULL; + GByte *pabyBuf; + + if (fpSrc == NULL || nSize == 0) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "TABCreateMAPBlockFromFile(): Assertion Failed!"); + return NULL; + } + + /*---------------------------------------------------------------- + * Alloc a buffer to contain the data + *---------------------------------------------------------------*/ + pabyBuf = (GByte*)CPLMalloc(nSize*sizeof(GByte)); + + /*---------------------------------------------------------------- + * Read from the file + *---------------------------------------------------------------*/ + if (VSIFSeekL(fpSrc, nOffset, SEEK_SET) != 0 || + VSIFReadL(pabyBuf, sizeof(GByte), nSize, fpSrc)!=(unsigned int)nSize ) + { + CPLError(CE_Failure, CPLE_FileIO, + "TABCreateMAPBlockFromFile() failed reading %d bytes at offset %d.", + nSize, nOffset); + CPLFree(pabyBuf); + return NULL; + } + + /*---------------------------------------------------------------- + * Create an object of the right type + * Header block is different: it does not start with the object + * type byte but it is always the first block in a file + *---------------------------------------------------------------*/ + if (nOffset == 0) + { + poBlock = new TABMAPHeaderBlock(eAccessMode); + } + else + { + switch(pabyBuf[0]) + { + case TABMAP_INDEX_BLOCK: + poBlock = new TABMAPIndexBlock(eAccessMode); + break; + case TABMAP_OBJECT_BLOCK: + poBlock = new TABMAPObjectBlock(eAccessMode); + break; + case TABMAP_COORD_BLOCK: + poBlock = new TABMAPCoordBlock(eAccessMode); + break; + case TABMAP_TOOL_BLOCK: + poBlock = new TABMAPToolBlock(eAccessMode); + break; + case TABMAP_GARB_BLOCK: + default: + poBlock = new TABRawBinBlock(eAccessMode, bHardBlockSize); + break; + } + } + + /*---------------------------------------------------------------- + * Init new object with the data we just read + *---------------------------------------------------------------*/ + if (poBlock->InitBlockFromData(pabyBuf, nSize, nSize, + FALSE, fpSrc, nOffset) != 0) + { + // Some error happened... and CPLError() has been called + delete poBlock; + poBlock = NULL; + } + + return poBlock; +} + +/*===================================================================== + * class TABBinBlockManager + *====================================================================*/ + + +/********************************************************************** + * TABBinBlockManager::TABBinBlockManager() + * + * Constructor. + **********************************************************************/ +TABBinBlockManager::TABBinBlockManager(int nBlockSize /*=512*/) +{ + + m_nBlockSize=nBlockSize; + m_nLastAllocatedBlock = -1; + m_psGarbageBlocksFirst = NULL; + m_psGarbageBlocksLast = NULL; + m_szName[0] = '\0'; +} + +/********************************************************************** + * TABBinBlockManager::~TABBinBlockManager() + * + * Destructor. + **********************************************************************/ +TABBinBlockManager::~TABBinBlockManager() +{ + Reset(); +} + +/********************************************************************** + * TABBinBlockManager::SetName() + **********************************************************************/ +void TABBinBlockManager::SetName(const char* pszName) +{ + strncpy(m_szName, pszName, sizeof(m_szName)); + m_szName[sizeof(m_szName)-1] = '\0'; +} + +/********************************************************************** + * TABBinBlockManager::AllocNewBlock() + * + * Returns and reserves the address of the next available block, either a + * brand new block at end of file, or recycle a garbage block if one is + * available. + **********************************************************************/ +GInt32 TABBinBlockManager::AllocNewBlock(CPL_UNUSED const char* pszReason) +{ + // Try to reuse garbage blocks first + if (GetFirstGarbageBlock() > 0) + { + int nRetValue = PopGarbageBlock(); +#ifdef DEBUG_VERBOSE + CPLDebug("MITAB", "AllocNewBlock(%s, %s) = %d (recycling garbage block)", m_szName, pszReason, nRetValue); +#endif + return nRetValue; + } + + // ... or alloc a new block at EOF + if (m_nLastAllocatedBlock==-1) + m_nLastAllocatedBlock = 0; + else + m_nLastAllocatedBlock+=m_nBlockSize; + +#ifdef DEBUG_VERBOSE + CPLDebug("MITAB", "AllocNewBlock(%s, %s) = %d", m_szName, pszReason, m_nLastAllocatedBlock); +#endif + return m_nLastAllocatedBlock; +} + +/********************************************************************** + * TABBinBlockManager::Reset() + * + **********************************************************************/ +void TABBinBlockManager::Reset() +{ + m_nLastAllocatedBlock = -1; + + // Flush list of garbage blocks + while (m_psGarbageBlocksFirst != NULL) + { + TABBlockRef *psNext = m_psGarbageBlocksFirst->psNext; + CPLFree(m_psGarbageBlocksFirst); + m_psGarbageBlocksFirst = psNext; + } + m_psGarbageBlocksLast = NULL; +} + +/********************************************************************** + * TABBinBlockManager::PushGarbageBlockAsFirst() + * + * Insert a garbage block at the head of the list of garbage blocks. + **********************************************************************/ +void TABBinBlockManager::PushGarbageBlockAsFirst(GInt32 nBlockPtr) +{ + TABBlockRef *psNewBlockRef = (TABBlockRef *)CPLMalloc(sizeof(TABBlockRef)); + + psNewBlockRef->nBlockPtr = nBlockPtr; + psNewBlockRef->psPrev = NULL; + psNewBlockRef->psNext = m_psGarbageBlocksFirst; + + if( m_psGarbageBlocksFirst != NULL ) + m_psGarbageBlocksFirst->psPrev = psNewBlockRef; + m_psGarbageBlocksFirst = psNewBlockRef; + if( m_psGarbageBlocksLast == NULL ) + m_psGarbageBlocksLast = m_psGarbageBlocksFirst; +} + +/********************************************************************** + * TABBinBlockManager::PushGarbageBlockAsLast() + * + * Insert a garbage block at the tail of the list of garbage blocks. + **********************************************************************/ +void TABBinBlockManager::PushGarbageBlockAsLast(GInt32 nBlockPtr) +{ + TABBlockRef *psNewBlockRef = (TABBlockRef *)CPLMalloc(sizeof(TABBlockRef)); + + psNewBlockRef->nBlockPtr = nBlockPtr; + psNewBlockRef->psPrev = m_psGarbageBlocksLast; + psNewBlockRef->psNext = NULL; + + if( m_psGarbageBlocksLast != NULL ) + m_psGarbageBlocksLast->psNext = psNewBlockRef; + m_psGarbageBlocksLast = psNewBlockRef; + if( m_psGarbageBlocksFirst == NULL ) + m_psGarbageBlocksFirst = m_psGarbageBlocksLast; +} + +/********************************************************************** + * TABBinBlockManager::GetFirstGarbageBlock() + * + * Return address of the block at the head of the list of garbage blocks + * or 0 if the list is empty. + **********************************************************************/ +GInt32 TABBinBlockManager::GetFirstGarbageBlock() +{ + if (m_psGarbageBlocksFirst) + return m_psGarbageBlocksFirst->nBlockPtr; + + return 0; +} + +/********************************************************************** + * TABBinBlockManager::PopGarbageBlock() + * + * Return address of the block at the head of the list of garbage blocks + * and remove that block from the list. + * Retuns 0 if the list is empty. + **********************************************************************/ +GInt32 TABBinBlockManager::PopGarbageBlock() +{ + GInt32 nBlockPtr = 0; + + if (m_psGarbageBlocksFirst) + { + nBlockPtr = m_psGarbageBlocksFirst->nBlockPtr; + TABBlockRef *psNext = m_psGarbageBlocksFirst->psNext; + CPLFree(m_psGarbageBlocksFirst); + if( psNext != NULL ) + psNext->psPrev = NULL; + else + m_psGarbageBlocksLast = NULL; + m_psGarbageBlocksFirst = psNext; + } + + return nBlockPtr; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_spatialref.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_spatialref.cpp new file mode 100644 index 000000000..066b74769 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_spatialref.cpp @@ -0,0 +1,2102 @@ +/********************************************************************** + * $Id: mitab_spatialref.cpp,v 1.55 2011-06-11 00:35:00 fwarmerdam Exp $ + * + * Name: mitab_spatialref.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the SpatialRef stuff in the TABFile class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 1999-2001, Frank Warmerdam + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_spatialref.cpp,v $ + * Revision 1.55 2011-06-11 00:35:00 fwarmerdam + * add support for reading google mercator (#4115) + * + * Revision 1.54 2010-10-07 18:46:26 aboudreault + * Fixed bad use of CPLAtof when locale setting doesn't use . for float (GDAL bug #3775) + * + * Revision 1.53 2010-09-07 16:48:08 aboudreault + * Removed incomplete patch for affine params support in mitab. (bug 1155) + * + * Revision 1.52 2010-07-08 17:21:12 aboudreault + * Put back New_Zealand Datum in asDatumInfoList + * + * Revision 1.51 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.50 2010-07-05 17:20:14 aboudreault + * Added Krovak projection suppoprt (bug 2230) + * + * Revision 1.49 2009-10-15 16:16:37 fwarmerdam + * add the default EPSG/OGR name for new zealand datums (gdal #3187) + * + * Revision 1.48 2007/11/21 21:15:45 dmorissette + * Fix asDatumInfoList[] and asSpheroidInfoList[] defns/refs (bug 1826) + * + * Revision 1.47 2006/07/10 17:58:48 fwarmerdam + * North_American_Datum_1927 support + * + * Revision 1.46 2006/07/07 19:41:32 dmorissette + * Fixed problem with uninitialized sTABProj.nAffineFlag (bug 1254,1319) + * + * Revision 1.45 2006/05/09 20:21:29 fwarmerdam + * Coordsys false easting and northing are in the units of the coordsys, not + * necessarily meters. Adjusted mitab_spatialref.cpp to reflect this. + * http://bugzilla.remotesensing.org/show_bug.cgi?id=1113 + * + * Revision 1.44 2005/09/29 20:15:36 dmorissette + * More improvements to handling of modified TM projections 21-24. + * Added correct name stings to all datum definitions (Anthony D, bug 1155) + * + * Revision 1.43 2005/05/12 22:07:52 dmorissette + * Improved handling of Danish modified TM proj#21-24 (hss, bugs 976,1010) + * + * Revision 1.42 2005/03/22 23:24:54 dmorissette + * Added support for datum id in .MAP header (bug 910) + * + * Revision 1.41 2004/10/11 20:50:04 dmorissette + * 7 new datum defns, 1 fixed and list of ellipsoids updated (Bug 608,Uffe K.) + * + * Revision 1.40 2003/03/21 14:20:42 warmerda + * fixed up regional mercator handling, was screwing up transverse mercator + * + * Revision 1.39 2002/12/19 20:46:01 warmerda + * fixed spelling of Provisional_South_American_Datum_1956 + * + * Revision 1.38 2002/12/12 20:12:18 warmerda + * fixed signs of rotational parameters for TOWGS84 in WKT + * + * Revision 1.37 2002/10/15 14:33:30 warmerda + * Added untested support in mitab_spatialref.cpp, and mitab_coordsys.cpp for + * projections Regional Mercator (26), Polyconic (27), Azimuthal Equidistant - + * All origin latitudes (28), and Lambert Azimuthal Equal Area - any aspect + * (29). + * + * Revision 1.36 2002/09/05 15:38:16 warmerda + * one more ogc datum name + * + * Revision 1.35 2002/09/05 15:23:22 warmerda + * added some EPSG datum names provided by Siro Martello @ Cadcorp + * + * Revision 1.34 2002/04/01 19:49:24 warmerda + * added support for cassini/soldner - proj 30 + * + * Revision 1.33 2002/03/01 19:00:15 warmerda + * False Easting/Northing should be in the linear units of measure in MapInfo, + * but in OGRSpatialReference/WKT they are always in meters. Convert accordingly. + * + * Revision 1.32 2001/10/25 16:13:41 warmerda + * Added OGC string for datum 12 + * + * Revision 1.31 2001/08/10 21:25:59 warmerda + * SetSpatialRef() now makes a clone of the srs instead of taking a ref to it + * + * Revision 1.30 2001/04/23 17:38:06 warmerda + * fixed use of freed points bug for datum 999/9999 + * + * Revision 1.29 2001/04/04 21:43:19 warmerda + * added code to set WGS84 values + * + * Revision 1.28 2001/01/23 21:23:42 daniel + * Added projection bounds lookup table, called from TABFile::SetProjInfo() + * + * Revision 1.27 2001/01/22 16:00:53 warmerda + * reworked swiss projection support + * + * Revision 1.26 2001/01/19 21:56:18 warmerda + * added untested support for Swiss Oblique Mercator + * + * Revision 1.25 2000/12/05 14:56:55 daniel + * Added some missing unit names (aliases) in TABFile::SetSpatialRef() + * + * Revision 1.24 2000/10/16 21:44:50 warmerda + * added nonearth support + * + * Revision 1.23 2000/10/16 18:01:20 warmerda + * added check for NULL on passed in spatial ref + * + * Revision 1.22 2000/10/02 14:46:36 daniel + * Added 7 parameter datums with id 1000+ + * + * Revision 1.21 2000/09/29 22:09:18 daniel + * Added new datums/ellipsoid from MapInfo V6.0 + * + * Revision 1.20 2000/09/28 16:39:44 warmerda + * avoid warnings for unused, and unitialized variables + * + * Revision 1.19 2000/02/07 17:43:17 daniel + * Fixed offset in parsing of custom datum string in SetSpatialRef() + * + * Revision 1.18 2000/02/04 05:30:50 daniel + * Fixed problem in GetSpatialRef() with szDatumName[] buffer size and added + * use of an epsilon in comparing of datum parameters. + * + * Revision 1.17 2000/01/15 22:30:45 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.16 1999/12/21 20:01:47 warmerda + * added support for DATUM 0 + * + * Revision 1.15 1999/11/11 02:56:17 warmerda + * fixed problems with stereographic + * + * Revision 1.14 1999/11/10 20:13:12 warmerda + * implement spheroid table + * + * Revision 1.13 1999/11/09 22:31:38 warmerda + * initial implementation of MIF CoordSys support + * + * Revision 1.12 1999/10/19 16:31:32 warmerda + * Improved mile support. + * + * Revision 1.11 1999/10/19 16:27:50 warmerda + * Added support for Mile (units=0). Also added support for nonearth + * projections. + * + * Revision 1.10 1999/10/05 18:56:08 warmerda + * fixed lots of bugs with projection parameters + * + * Revision 1.9 1999/10/04 21:17:47 warmerda + * Make sure that asDatumInfoList comparisons include the ellipsoid code. + * Don't include OGC name for local NAD27 values. Put NAD83 ahead of GRS80 + * so it will be used in preference even though they are identical parms. + * + * Revision 1.8 1999/10/04 19:46:42 warmerda + * assorted changes, including rework of units + * + * Revision 1.7 1999/09/28 04:52:17 daniel + * Added missing param in sprintf() format for szDatumName[] + * + * Revision 1.6 1999/09/28 02:51:46 warmerda + * Added ellipsoid codes, and bulk of write implementation. + * + * Revision 1.5 1999/09/27 21:23:41 warmerda + * added more projections + * + * Revision 1.4 1999/09/24 04:01:28 warmerda + * remember nMIDatumId changes + * + * Revision 1.3 1999/09/23 19:51:38 warmerda + * added datum mapping table support + * + * Revision 1.2 1999/09/22 23:04:59 daniel + * Handle reference count on OGRSpatialReference properly + * + * Revision 1.1 1999/09/21 19:39:22 daniel + * Moved Get/SetSpatialRef() to a separate file + * + **********************************************************************/ + +#include "mitab.h" + +/* -------------------------------------------------------------------- */ +/* This table was automatically generated by doing translations */ +/* between mif and tab for each datum, and extracting the */ +/* parameters from the tab file. The EPSG codes and OGC names */ +/* were added afterwards and may be incomplete or inaccurate. */ +/* -------------------------------------------------------------------- */ + +extern const MapInfoDatumInfo asDatumInfoList[]; +extern const MapInfoSpheroidInfo asSpheroidInfoList[]; + +const MapInfoDatumInfo asDatumInfoList[] = +{ + +{ 0, 104, "WGS_1984", 28,0, 0, 0, 0, 0, 0, 0, 0}, +{ 6269, 74, "North_American_Datum_1983", 0, 0, 0, 0, 0, 0, 0, 0, 0}, + +{ 0, 0, "", 29, 0, 0, 0, 0, 0, 0, 0, 0}, // Datum ignore + +{ 6201, 1, "Adindan", 6, -162, -12, 206, 0, 0, 0, 0, 0}, +{ 6205, 2, "Afgooye", 3, -43, -163, 45, 0, 0, 0, 0, 0}, +{ 6204, 3, "Ain_el_Abd_1970", 4, -150, -251, -2, 0, 0, 0, 0, 0}, +{ 0, 4, "Anna_1_Astro_1965", 2, -491, -22, 435, 0, 0, 0, 0, 0}, +{ 6209, 5, "Arc_1950", 15,-143, -90, -294,0, 0, 0, 0, 0}, +{ 6210, 6, "Arc_1960", 6, -160, -8, -300,0, 0, 0, 0, 0}, +{ 0, 7, "Ascension_Islands", 4, -207, 107, 52, 0, 0, 0, 0, 0}, +{ 0, 8, "Astro_Beacon_E", 4, 145, 75, -272,0, 0, 0, 0, 0}, +{ 0, 9, "Astro_B4_Sorol_Atoll", 4, 114, -116, -333,0, 0, 0, 0, 0}, +{ 0, 10, "Astro_Dos_71_4", 4, -320, 550, -494,0, 0, 0, 0, 0}, +{ 0, 11, "Astronomic_Station_1952", 4, 124, -234, -25, 0, 0, 0, 0, 0}, +{ 6202, 12, "Australian_Geodetic_Datum_66",2, -133, -48, 148, 0, 0, 0, 0, 0}, +{ 6203, 13, "Australian_Geodetic_Datum_84",2, -134, -48, 149, 0, 0, 0, 0, 0}, +{ 0, 14, "Bellevue_Ign", 4, -127, -769, 472, 0, 0, 0, 0, 0}, +{ 6216, 15, "Bermuda_1957", 7, -73, 213, 296, 0, 0, 0, 0, 0}, +{ 6218, 16, "Bogota", 4, 307, 304, -318,0, 0, 0, 0, 0}, +{ 6221, 17, "Campo_Inchauspe", 4, -148, 136, 90, 0, 0, 0, 0, 0}, +{ 0, 18, "Canton_Astro_1966", 4, 298, -304, -375,0, 0, 0, 0, 0}, +{ 6222, 19, "Cape", 6, -136, -108, -292,0, 0, 0, 0, 0}, +{ 6717, 20, "Cape_Canaveral", 7, -2, 150, 181, 0, 0, 0, 0, 0}, +{ 6223, 21, "Carthage", 6, -263, 6, 431, 0, 0, 0, 0, 0}, +{ 6672, 22, "Chatham_1971", 4, 175, -38, 113, 0, 0, 0, 0, 0}, +{ 6224, 23, "Chua", 4, -134, 229, -29, 0, 0, 0, 0, 0}, +{ 6225, 24, "Corrego_Alegre", 4, -206, 172, -6, 0, 0, 0, 0, 0}, +{ 6211, 25, "Batavia", 10,-377,681, -50, 0, 0, 0, 0, 0}, +{ 0, 26, "Dos_1968", 4, 230, -199, -752,0, 0, 0, 0, 0}, +{ 6719, 27, "Easter_Island_1967", 4, 211, 147, 111, 0, 0, 0, 0, 0}, +{ 6230, 28, "European_Datum_1950", 4, -87, -98, -121,0, 0, 0, 0, 0}, +{ 6668, 29, "European_Datum_1979", 4, -86, -98, -119,0, 0, 0, 0, 0}, +{ 6233, 30, "Gandajika_1970", 4, -133, -321, 50, 0, 0, 0, 0, 0}, +{ 6272, 31, "New_Zealand_GD49", 4, 84, -22, 209, 0, 0, 0, 0, 0}, +{ 6272, 31, "New_Zealand_Geodetic_Datum_1949",4,84, -22, 209, 0, 0, 0, 0, 0}, +{ 0, 32, "GRS_67", 21,0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 33, "GRS_80", 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 6171, 33, "Reseau_Geodesique_Francais_1993",0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 6675, 34, "Guam_1963", 7, -100, -248, 259, 0, 0, 0, 0, 0}, +{ 0, 35, "Gux_1_Astro", 4, 252, -209, -751,0, 0, 0, 0, 0}, +{ 6254, 36, "Hito_XVIII_1963", 4, 16, 196, 93, 0, 0, 0, 0, 0}, +{ 6658, 37, "Hjorsey_1955", 4, -73, 46, -86, 0, 0, 0, 0, 0}, +{ 6738, 38, "Hong_Kong_1963", 4, -156, -271, -189,0, 0, 0, 0, 0}, +{ 6236, 39, "Hu_Tzu_Shan", 4, -634, -549, -201,0, 0, 0, 0, 0}, +{ 0, 40, "Indian_Thailand_Vietnam", 11,214, 836, 303, 0, 0, 0, 0, 0}, +{ 0, 41, "Indian_Bangladesh", 11,289, 734, 257, 0, 0, 0, 0, 0}, +{ 0, 42, "Ireland_1965", 13,506, -122, 611, 0, 0, 0, 0, 0}, +{ 0, 43, "ISTS_073_Astro_1969", 4, 208, -435, -229,0, 0, 0, 0, 0}, +{ 6725, 44, "Johnston_Island_1961", 4, 191, -77, -204,0, 0, 0, 0, 0}, +{ 6244, 45, "Kandawala", 11,-97, 787, 86, 0, 0, 0, 0, 0}, +{ 0, 46, "Kerguyelen_Island", 4, 145, -187, 103, 0, 0, 0, 0, 0}, +{ 6245, 47, "Kertau", 17,-11, 851, 5, 0, 0, 0, 0, 0}, +{ 0, 48, "L_C_5_Astro", 7, 42, 124, 147, 0, 0, 0, 0, 0}, +{ 6251, 49, "Liberia_1964", 6, -90, 40, 88, 0, 0, 0, 0, 0}, +{ 0, 50, "Luzon_Phillippines", 7, -133, -77, -51, 0, 0, 0, 0, 0}, +{ 0, 51, "Luzon_Mindanao_Island", 7, -133, -79, -72, 0, 0, 0, 0, 0}, +{ 6256, 52, "Mahe_1971", 6, 41, -220, -134,0, 0, 0, 0, 0}, +{ 0, 53, "Marco_Astro", 4, -289, -124, 60, 0, 0, 0, 0, 0}, +{ 6262, 54, "Massawa", 10,639, 405, 60, 0, 0, 0, 0, 0}, +{ 6261, 55, "Merchich", 16,31, 146, 47, 0, 0, 0, 0, 0}, +{ 0, 56, "Midway_Astro_1961", 4, 912, -58, 1227,0, 0, 0, 0, 0}, +{ 6263, 57, "Minna", 6, -92, -93, 122, 0, 0, 0, 0, 0}, +{ 0, 58, "Nahrwan_Masirah_Island", 6, -247, -148, 369, 0, 0, 0, 0, 0}, +{ 0, 59, "Nahrwan_Un_Arab_Emirates", 6, -249, -156, 381, 0, 0, 0, 0, 0}, +{ 0, 60, "Nahrwan_Saudi_Arabia", 6, -231, -196, 482, 0, 0, 0, 0, 0}, +{ 6271, 61, "Naparima_1972", 4, -2, 374, 172, 0, 0, 0, 0, 0}, +{ 6267, 62, "NAD_1927", 7, -8, 160, 176, 0, 0, 0, 0, 0}, +{ 6267, 62, "North_American_Datum_1927", 7, -8, 160, 176, 0, 0, 0, 0, 0}, +{ 0, 63, "NAD_27_Alaska", 7, -5, 135, 172, 0, 0, 0, 0, 0}, +{ 0, 64, "NAD_27_Bahamas", 7, -4, 154, 178, 0, 0, 0, 0, 0}, +{ 0, 65, "NAD_27_San_Salvador", 7, 1, 140, 165, 0, 0, 0, 0, 0}, +{ 0, 66, "NAD_27_Canada", 7, -10, 158, 187, 0, 0, 0, 0, 0}, +{ 0, 67, "NAD_27_Canal_Zone", 7, 0, 125, 201, 0, 0, 0, 0, 0}, +{ 0, 68, "NAD_27_Caribbean", 7, -7, 152, 178, 0, 0, 0, 0, 0}, +{ 0, 69, "NAD_27_Central_America", 7, 0, 125, 194, 0, 0, 0, 0, 0}, +{ 0, 70, "NAD_27_Cuba", 7, -9, 152, 178, 0, 0, 0, 0, 0}, +{ 0, 71, "NAD_27_Greenland", 7, 11, 114, 195, 0, 0, 0, 0, 0}, +{ 0, 72, "NAD_27_Mexico", 7, -12, 130, 190, 0, 0, 0, 0, 0}, +{ 0, 73, "NAD_27_Michigan", 8, -8, 160, 176, 0, 0, 0, 0, 0}, +{ 0, 75, "Observatorio_1966", 4, -425, -169, 81, 0, 0, 0, 0, 0}, +{ 0, 76, "Old_Egyptian", 22,-130, 110, -13, 0, 0, 0, 0, 0}, +{ 6135, 77, "Old_Hawaiian", 7, 61, -285, -181,0, 0, 0, 0, 0}, +{ 0, 78, "Oman", 6, -346, -1, 224, 0, 0, 0, 0, 0}, +{ 6277, 79, "OSGB_1936", 9, 375, -111, 431, 0, 0, 0, 0, 0}, +{ 0, 80, "Pico_De_Las_Nieves", 4, -307, -92, 127, 0, 0, 0, 0, 0}, +{ 6729, 81, "Pitcairn_Astro_1967", 4, 185, 165, 42, 0, 0, 0, 0, 0}, +{ 6248, 82, "Provisional_South_American", 4, -288, 175, -376,0, 0, 0, 0, 0}, +{ 6139, 83, "Puerto_Rico", 7, 11, 72, -101,0, 0, 0, 0, 0}, +{ 6614, 84, "Qatar_National", 4, -128, -283, 22, 0, 0, 0, 0, 0}, +{ 6287, 85, "Qornoq", 4, 164, 138, -189, 0, 0, 0, 0, 0}, +{ 6627, 86, "Reunion", 4, 94, -948,-1262,0, 0, 0, 0, 0}, +{ 6265, 87, "Monte_Mario", 4, -225, -65, 9, 0, 0, 0, 0, 0}, +{ 0, 88, "Santo_Dos", 4, 170, 42, 84, 0, 0, 0, 0, 0}, +{ 0, 89, "Sao_Braz", 4, -203, 141, 53, 0, 0, 0, 0, 0}, +{ 6292, 90, "Sapper_Hill_1943", 4, -355, 16, 74, 0, 0, 0, 0, 0}, +{ 6293, 91, "Schwarzeck", 14,616, 97, -251, 0, 0, 0, 0, 0}, +{ 6618, 92, "South_American_Datum_1969", 24,-57, 1, -41, 0, 0, 0, 0, 0}, +{ 0, 93, "South_Asia", 19,7, -10, -26, 0, 0, 0, 0, 0}, +{ 0, 94, "Southeast_Base", 4, -499, -249,314, 0, 0, 0, 0, 0}, +{ 0, 95, "Southwest_Base", 4, -104, 167, -38, 0, 0, 0, 0, 0}, +{ 6298, 96, "Timbalai_1948", 11,-689, 691, -46, 0, 0, 0, 0, 0}, +{ 6301, 97, "Tokyo", 10,-128, 481, 664, 0, 0, 0, 0, 0}, +{ 0, 98, "Tristan_Astro_1968", 4, -632, 438, -609, 0, 0, 0, 0, 0}, +{ 6731, 99, "Viti_Levu_1916", 6, 51, 391, -36, 0, 0, 0, 0, 0}, +{ 0, 100, "Wake_Entiwetok_1960", 23,101, 52, -39, 0, 0, 0, 0, 0}, +{ 0, 101, "WGS_60", 26,0, 0, 0, 0, 0, 0, 0, 0}, +{ 6760, 102, "WGS_66", 27,0, 0, 0, 0, 0, 0, 0, 0}, +{ 6322, 103, "WGS_1972", 1, 0, 8, 10, 0, 0, 0, 0, 0}, +{ 6326, 104, "WGS_1984", 28,0, 0, 0, 0, 0, 0, 0, 0}, +{ 6309, 105, "Yacare", 4, -155, 171, 37, 0, 0, 0, 0, 0}, +{ 6311, 106, "Zanderij", 4, -265, 120, -358, 0, 0, 0, 0, 0}, +{ 0, 107, "NTF", 30,-168, -60, 320, 0, 0, 0, 0, 0}, +{ 6231, 108, "European_Datum_1987", 4, -83, -96, -113, 0, 0, 0, 0, 0}, +{ 0, 109, "Netherlands_Bessel", 10,593, 26, 478, 0, 0, 0, 0, 0}, +{ 0, 110, "Belgium_Hayford", 4, 81, 120, 129, 0, 0, 0, 0, 0}, +{ 0, 111, "NWGL_10", 1, -1, 15, 1, 0, 0, 0, 0, 0}, +{ 6124, 112, "Rikets_koordinatsystem_1990",10,498, -36, 568, 0, 0, 0, 0, 0}, +{ 0, 113, "Lisboa_DLX", 4, -303, -62, 105, 0, 0, 0, 0, 0}, +{ 0, 114, "Melrica_1973_D73", 4, -223, 110, 37, 0, 0, 0, 0, 0}, +{ 0, 115, "Euref_98", 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 6283, 116, "GDA94", 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 6283, 116, "Geocentric_Datum_of_Australia_1994", 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 6167, 117, "NZGD2000", 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 6167, 117, "New_Zealand_Geodetic_Datum_2000",0,0, 0, 0, 0, 0, 0, 0, 0}, +{ 6169, 118, "America_Samoa", 7, -115, 118, 426, 0, 0, 0, 0, 0}, +{ 0, 119, "Antigua_Astro_1965", 6, -270, 13, 62, 0, 0, 0, 0, 0}, +{ 6713, 120, "Ayabelle_Lighthouse", 6, -79, -129, 145, 0, 0, 0, 0, 0}, +{ 6219, 121, "Bukit_Rimpah", 10,-384, 664, -48, 0, 0, 0, 0, 0}, +{ 0, 122, "Estonia_1937", 10,374, 150, 588, 0, 0, 0, 0, 0}, +{ 6155, 123, "Dabola", 6, -83, 37, 124, 0, 0, 0, 0, 0}, +{ 6736, 124, "Deception_Island", 6, 260, 12, -147, 0, 0, 0, 0, 0}, +{ 0, 125, "Fort_Thomas_1955", 6, -7, 215, 225, 0, 0, 0, 0, 0}, +{ 0, 126, "Graciosa_base_1948", 4, -104, 167, -38, 0, 0, 0, 0, 0}, +{ 6255, 127, "Herat_North", 4, -333, -222,114, 0, 0, 0, 0, 0}, +{ 0, 128, "Hermanns_Kogel", 10,682, -203, 480, 0, 0, 0, 0, 0}, +{ 6240, 129, "Indian", 50,283, 682, 231, 0, 0, 0, 0, 0}, +{ 6239, 130, "Indian_1954", 11,217, 823, 299, 0, 0, 0, 0, 0}, +{ 6131, 131, "Indian_1960", 11,198, 881, 317, 0, 0, 0, 0, 0}, +{ 6240, 132, "Indian_1975", 11,210, 814, 289, 0, 0, 0, 0, 0}, +{ 6238, 133, "Indonesian_Datum_1974", 4, -24, -15, 5, 0, 0, 0, 0, 0}, +{ 0, 134, "ISTS061_Astro_1968", 4, -794, 119, -298, 0, 0, 0, 0, 0}, +{ 0, 135, "Kusaie_Astro_1951", 4, 647, 1777, -1124,0, 0, 0, 0, 0}, +{ 6250, 136, "Leigon", 6, -130, 29, 364, 0, 0, 0, 0, 0}, +{ 0, 137, "Montserrat_Astro_1958", 6, 174, 359, 365, 0, 0, 0, 0, 0}, +{ 6266, 138, "Mporaloko", 6, -74, -130, 42, 0, 0, 0, 0, 0}, +{ 0, 139, "North_Sahara_1959", 6, -186, -93, 310, 0, 0, 0, 0, 0}, +{ 0, 140, "Observatorio_Met_1939", 4, -425, -169,81, 0, 0, 0, 0, 0}, +{ 6620, 141, "Point_58", 6, -106, -129,165, 0, 0, 0, 0, 0}, +{ 6282, 142, "Pointe_Noire", 6, -148, 51, -291, 0, 0, 0, 0, 0}, +{ 6615, 143, "Porto_Santo_1936", 4, -499, -249,314, 0, 0, 0, 0, 0}, +{ 6616, 144, "Selvagem_Grande_1938", 4, -289, -124,60, 0, 0, 0, 0, 0}, +{ 0, 145, "Sierra_Leone_1960", 6, -88, 4, 101, 0, 0, 0, 0, 0}, +{ 6156, 146, "S_JTSK_Ferro", 10, 589, 76, 480, 0, 0, 0, 0, 0}, +{ 6297, 147, "Tananarive_1925", 4, -189, -242,-91, 0, 0, 0, 0, 0}, +{ 0, 148, "Voirol_1874", 6, -73, -247,227, 0, 0, 0, 0, 0}, +{ 0, 149, "Virol_1960", 6, -123, -206,219, 0, 0, 0, 0, 0}, +{ 6148, 150, "Hartebeesthoek94", 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 6122, 151, "ATS77", 51, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 6612, 152, "JGD2000", 0, 0, 0, 0, 0, 0, 0, 0, 0}, +{ 0, 157, "WGS_1984", 54, 0, 0, 0, 0, 0, 0, 0, 0}, // Google merc +{ 0, 1000,"DHDN_Potsdam_Rauenberg", 10,582, 105, 414, -1.04, -0.35, 3.08, 8.3, 0}, +{ 6284, 1001,"Pulkovo_1942", 3, 24, -123, -94, -0.02, 0.25, 0.13, 1.1, 0}, +{ 6275, 1002,"NTF_Paris_Meridian", 30,-168, -60, 320, 0, 0, 0, 0, 2.337229166667}, +{ 0, 1003,"Switzerland_CH_1903", 10,660.077,13.551, 369.344, 0.804816, 0.577692, 0.952236, 5.66,0}, +{ 6237, 1004,"Hungarian_Datum_1972", 21,-56, 75.77, 15.31, -0.37, -0.2, -0.21, -1.01, 0}, +{ 0, 1005,"Cape_7_Parameter", 28,-134.73,-110.92, -292.66, 0, 0, 0, 1, 0}, +{ 6203, 1006,"AGD84_7_Param_Aust", 2, -117.763,-51.51, 139.061, -0.292, -0.443, -0.277, -0.191, 0}, +{ 0, 1007,"AGD66_7_Param_ACT", 2, -129.193,-41.212, 130.73, -0.246, -0.374, -0.329, -2.955, 0}, +{ 0, 1008,"AGD66_7_Param_TAS", 2, -120.271,-64.543, 161.632, -0.2175, 0.0672, 0.1291, 2.4985, 0}, +{ 0, 1009,"AGD66_7_Param_VIC_NSW", 2, -119.353,-48.301, 139.484, -0.415, -0.26, -0.437, -0.613, 0}, +{ 6272, 1010,"NZGD_7_Param_49", 4, 59.47, -5.04, 187.44, -0.47, 0.1, -1.024, -4.5993, 0}, +{ 0, 1011,"Rikets_Tri_7_Param_1990", 10,419.3836, 99.3335, 591.3451, -0.850389, -1.817277, 7.862238, -0.99496, 0}, +{ 0, 1012,"Russia_PZ90", 52, -1.08,-0.27,-0.9,0, 0, -0.16,-0.12, 0}, +{ 0, 1013,"Russia_SK42", 52, 23.92,-141.27,-80.9, 0, -0.35,-0.82, -0.12, 0}, +{ 0, 1014,"Russia_SK95", 52, 24.82,-131.21,-82.66,0,0,-0.16,-0.12, 0}, +{ 6301, 1015,"Tokyo", 10, -146.414, 507.337, 680.507,0,0,0,0,0}, +{ 0, 1016,"Finnish_KKJ", 4, -96.062, -82.428, -121.754, -4.801, -0.345, 1.376, 1.496, 0}, +{ 6610, 1017,"Xian 1980", 53, 24, -123, -94, -0.02, -0.25, 0.13, 1.1, 0}, +{ 0, 1018,"Lithuanian Pulkovo 1942", 4, -40.59527, -18.54979, -69.33956, -2.508, -1.8319, 2.6114, -4.2991, 0}, +{ 0, 1019,"Belgian 1972 7 Parameter", 4, -99.059, 53.322, -112.486, -0.419, 0.83, -1.885, 0.999999, 0}, +{ 6818, 1020,"S-JTSK with Ferro prime meridian", 10, 589, 76, 480, 0, 0, 0, 0, -17.666666666667}, + +{ -1, -1, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0} +}; + +/* -------------------------------------------------------------------- */ +/* This table was hand entered from Appendix I of the mapinfo 6 */ +/* manuals. */ +/* -------------------------------------------------------------------- */ + +const MapInfoSpheroidInfo asSpheroidInfoList[] = +{ +{ 9,"Airy 1930", 6377563.396, 299.3249646}, +{13,"Airy 1930 (modified for Ireland 1965", 6377340.189, 299.3249646}, +{51,"ATS77 (Average Terrestrial System 1977)", 6378135, 298.257}, +{ 2,"Australian", 6378160.0, 298.25}, +{10,"Bessel 1841", 6377397.155, 299.1528128}, +{35,"Bessel 1841 (modified for NGO 1948)", 6377492.0176, 299.15281}, +{14,"Bessel 1841 (modified for Schwarzeck)", 6377483.865, 299.1528128}, +{36,"Clarke 1858", 6378293.639, 294.26068}, +{ 7,"Clarke 1866", 6378206.4, 294.9786982}, +{ 8,"Clarke 1866 (modified for Michigan)", 6378450.047484481,294.9786982}, +{ 6,"Clarke 1880", 6378249.145, 293.465}, +{15,"Clarke 1880 (modified for Arc 1950)", 6378249.145326, 293.4663076}, +{30,"Clarke 1880 (modified for IGN)", 6378249.2, 293.4660213}, +{37,"Clarke 1880 (modified for Jamaica)", 6378249.136, 293.46631}, +{16,"Clarke 1880 (modified for Merchich)", 6378249.2, 293.46598}, +{38,"Clarke 1880 (modified for Palestine)", 6378300.79, 293.46623}, +{39,"Everest (Brunei and East Malaysia)", 6377298.556, 300.8017}, +{11,"Everest (India 1830)", 6377276.345, 300.8017}, +{40,"Everest (India 1956)", 6377301.243, 300.80174}, +{50,"Everest (Pakistan)", 6377309.613, 300.8017}, +{17,"Everest (W. Malaysia and Singapore 1948)", 6377304.063, 300.8017}, +{48,"Everest (West Malaysia 1969)", 6377304.063, 300.8017}, +{18,"Fischer 1960", 6378166.0, 298.3}, +{19,"Fischer 1960 (modified for South Asia)", 6378155.0, 298.3}, +{20,"Fischer 1968", 6378150.0, 298.3}, +{21,"GRS 67", 6378160.0, 298.247167427}, +{ 0,"GRS 80", 6378137.0, 298.257222101}, +{ 5,"Hayford", 6378388.0, 297.0}, +{22,"Helmert 1906", 6378200.0, 298.3}, +{23,"Hough", 6378270.0, 297.0}, +{31,"IAG 75", 6378140.0, 298.257222}, +{41,"Indonesian", 6378160.0, 298.247}, +{ 4,"International 1924", 6378388.0, 297.0}, +{49,"Irish (WOFO)", 6377542.178, 299.325}, +{ 3,"Krassovsky", 6378245.0, 298.3}, +{32,"MERIT 83", 6378137.0, 298.257}, +{33,"New International 1967", 6378157.5, 298.25}, +{42,"NWL 9D", 6378145.0, 298.25}, +{43,"NWL 10D", 6378135.0, 298.26}, +{44,"OSU86F", 6378136.2, 298.25722}, +{45,"OSU91A", 6378136.3, 298.25722}, +{46,"Plessis 1817", 6376523.0, 308.64}, +{52,"PZ90", 6378136.0, 298.257839303}, +{24,"South American", 6378160.0, 298.25}, +{12,"Sphere", 6370997.0, 0.0}, +{47,"Struve 1860", 6378297.0, 294.73}, +{34,"Walbeck", 6376896.0, 302.78}, +{25,"War Office", 6378300.583, 296.0}, +{26,"WGS 60", 6378165.0, 298.3}, +{27,"WGS 66", 6378145.0, 298.25}, +{ 1,"WGS 72", 6378135.0, 298.26}, +{28,"WGS 84", 6378137.0, 298.257223563}, +{29,"WGS 84 (MAPINFO Datum 0)", 6378137.01, 298.257223563}, +{54,"WGS 84 (MAPINFO Datum 157)", 6378137.01, 298.257223563}, +{-1,NULL, 0.0, 0.0} +}; + +/* For LCC, standard parallel 1 and 2 can be switched indifferently */ +/* So the MapInfo order and the EPSG order are not generally identical */ +/* which may cause recognition problems when reading in MapInfo */ +/* This table contains the parameters in the order expected by MapInfo */ +typedef struct +{ + int nEPSGCode; + int bReverseStdP; + int nMapInfoDatumID; + double dfCenterLong; + double dfCenterLat; + double dfStdP1; + double dfStdP2; +} MapInfoLCCSRS; + +static const MapInfoLCCSRS asMapInfoLCCSRSList[] = { +{2154,1,33,3,46.5,44,49}, +{2154,1,33,3,46.5,44,49.00000000001}, +{2154,1,33,3,46.5,44,49.00000000002}, +{2225,1,74,-122,39.3333333333,40,41.6666666667}, +{2226,1,74,-122,37.6666666667,38.3333333333,39.8333333333}, +{2227,1,74,-120.5,36.5,37.0666666667,38.4333333333}, +{2228,1,74,-119,35.3333333333,36,37.25}, +{2229,1,74,-118,33.5,34.0333333333,35.4666666667}, +{2230,1,74,-116.25,32.1666666667,32.7833333333,33.8833333333}, +{2231,1,74,-105.5,39.3333333333,39.7166666667,40.7833333333}, +{2232,1,74,-105.5,37.8333333333,38.45,39.75}, +{2233,1,74,-105.5,36.6666666667,37.2333333333,38.4333333333}, +{2234,1,74,-72.75,40.8333333333,41.2,41.8666666667}, +{2238,1,74,-84.5,29,29.5833333333,30.75}, +{2246,0,74,-84.25,37.5,37.9666666667,38.9666666667}, +{2247,1,74,-85.75,36.3333333333,36.7333333333,37.9333333333}, +{2248,1,74,-77,37.6666666667,38.3,39.45}, +{2249,1,74,-71.5,41,41.7166666667,42.6833333333}, +{2250,1,74,-70.5,41,41.2833333333,41.4833333333}, +{2251,1,74,-87,44.7833333333,45.4833333333,47.0833333333}, +{2252,1,74,-84.3666666667,43.3166666667,44.1833333333,45.7}, +{2253,1,74,-84.3666666667,41.5,42.1,43.6666666667}, +{2256,1,74,-109.5,44.25,45,49}, +{2263,1,74,-74,40.1666666667,40.6666666667,41.0333333333}, +{2264,1,74,-79,33.75,34.3333333333,36.1666666667}, +{2265,1,74,-100.5,47,47.4333333333,48.7333333333}, +{2266,1,74,-100.5,45.6666666667,46.1833333333,47.4833333333}, +{2267,1,74,-98,35,35.5666666667,36.7666666667}, +{2268,1,74,-98,33.3333333333,33.9333333333,35.2333333333}, +{2269,1,74,-120.5,43.6666666667,44.3333333333,46}, +{2270,1,74,-120.5,41.6666666667,42.3333333333,44}, +{2271,1,74,-77.75,40.1666666667,40.8833333333,41.95}, +{2272,1,74,-77.75,39.3333333333,39.9333333333,40.9666666667}, +{2273,1,74,-81,31.8333333333,32.5,34.8333333333}, +{2274,1,74,-86,34.3333333333,35.25,36.4166666667}, +{2275,1,74,-101.5,34,34.65,36.1833333333}, +{2276,1,74,-98.5,31.6666666667,32.1333333333,33.9666666667}, +{2277,1,74,-100.3333333333,29.6666666667,30.1166666667,31.8833333333}, +{2278,1,74,-99,27.8333333333,28.3833333333,30.2833333333}, +{2279,1,74,-98.5,25.6666666667,26.1666666667,27.8333333333}, +{2280,1,74,-111.5,40.3333333333,40.7166666667,41.7833333333}, +{2281,1,74,-111.5,38.3333333333,39.0166666667,40.65}, +{2282,1,74,-111.5,36.6666666667,37.2166666667,38.35}, +{2283,1,74,-78.5,37.6666666667,38.0333333333,39.2}, +{2284,1,74,-78.5,36.3333333333,36.7666666667,37.9666666667}, +{2285,1,74,-120.8333333333,47,47.5,48.7333333333}, +{2286,1,74,-120.5,45.3333333333,45.8333333333,47.3333333333}, +{2287,1,74,-90,45.1666666667,45.5666666667,46.7666666667}, +{2288,1,74,-90,43.8333333333,44.25,45.5}, +{2289,1,74,-90,42,42.7333333333,44.0666666667}, +{26740,1,63,-176,51,51.8333333333,53.8333333333}, +{26741,1,62,-122,39.3333333333,40,41.6666666667}, +{26742,1,62,-122,37.6666666667,38.3333333333,39.8333333333}, +{26743,1,62,-120.5,36.5,37.0666666667,38.4333333333}, +{26744,1,62,-119,35.3333333333,36,37.25}, +{26745,1,62,-118,33.5,34.0333333333,35.4666666667}, +{26746,1,62,-116.25,32.1666666667,32.7833333333,33.8833333333}, +{26747,1,62,-118.3333333333,34.1333333333,33.8666666667,34.4166666667}, +{26751,1,62,-92,34.3333333333,34.9333333333,36.2333333333}, +{26752,1,62,-92,32.6666666667,33.3,34.7666666667}, +{26753,0,62,-105.5,39.3333333333,39.7166666667,40.7833333333}, +{26754,1,62,-105.5,37.8333333333,38.45,39.75}, +{26755,1,62,-105.5,36.6666666667,37.2333333333,38.4333333333}, +{26756,1,62,-72.75,40.8333333333,41.2,41.8666666667}, +{26760,1,62,-84.5,29,29.5833333333,30.75}, +{26775,1,62,-93.5,41.5,42.0666666667,43.2666666667}, +{26776,1,62,-93.5,40,40.6166666667,41.7833333333}, +{26777,1,62,-98,38.3333333333,38.7166666667,39.7833333333}, +{26778,0,62,-98.5,36.6666666667,38.5666666667,37.2666666667}, +{26779,0,62,-84.25,37.5,37.9666666667,38.9666666667}, +{26780,0,62,-85.75,36.3333333333,36.7333333333,37.9333333333}, +{26781,0,62,-92.5,30.6666666667,31.1666666667,32.6666666667}, +{26785,0,62,-77,37.8333333333,38.3,39.45}, +{26786,0,62,-71.5,41,41.7166666667,42.6833333333}, +{26788,0,73,-87,44.7833333333,45.4833333333,47.0833333333}, +{26789,0,73,-84.3333333333,43.3166666667,44.1833333333,45.7}, +{26790,0,73,-84.3333333333,41.5,42.1,43.6666666667}, +{26791,0,62,-93.1,46.5,47.0333333333,48.6333333333}, +{26792,0,62,-94.25,45,45.6166666667,47.05}, +{26793,0,62,-94,43,43.7833333333,45.2166666667}, +{26940,1,74,-176,51,51.8333333333,53.8333333333}, +{26941,1,74,-122,39.3333333333,40,41.6666666667}, +{26942,1,74,-122,37.6666666667,38.3333333333,39.8333333333}, +{26943,1,74,-120.5,36.5,37.0666666667,38.4333333333}, +{26944,1,74,-119,35.3333333333,36,37.25}, +{26945,1,74,-118,33.5,34.0333333333,35.4666666667}, +{26946,1,74,-116.25,32.1666666667,32.7833333333,33.8833333333}, +{26951,1,74,-92,34.3333333333,34.9333333333,36.2333333333}, +{26952,1,74,-92,32.6666666667,33.3,34.7666666667}, +{26953,1,74,-105.5,39.3333333333,39.7166666667,40.7833333333}, +{26954,1,74,-105.5,37.8333333333,38.45,39.75}, +{26955,1,74,-105.5,36.6666666667,37.2333333333,38.4333333333}, +{26956,1,74,-72.75,40.8333333333,41.2,41.8666666667}, +{26960,1,74,-84.5,29,29.5833333333,30.75}, +{26975,1,74,-93.5,41.5,42.0666666667,43.2666666667}, +{26976,1,74,-93.5,40,40.6166666667,41.7833333333}, +{26977,1,74,-98,38.3333333333,38.7166666667,39.7833333333}, +{26978,0,74,-98.5,36.6666666667,38.5666666667,37.2666666667}, +{26980,1,74,-85.75,36.3333333333,36.7333333333,37.9333333333}, +{26981,1,74,-92.5,30.5,31.1666666667,32.6666666667}, +{26982,1,74,-91.3333333333,28.5,29.3,30.7}, +{26985,1,74,-77,37.6666666667,38.3,39.45}, +{26986,1,74,-71.5,41,41.7166666667,42.6833333333}, +{26987,1,74,-70.5,41,41.2833333333,41.4833333333}, +{26988,1,74,-87,44.7833333333,45.4833333333,47.0833333333}, +{26989,1,74,-84.3666666667,43.3166666667,44.1833333333,45.7}, +{26990,1,74,-84.3666666667,41.5,42.1,43.6666666667}, +{26991,1,74,-93.1,46.5,47.0333333333,48.6333333333}, +{26992,1,74,-94.25,45,45.6166666667,47.05}, +{26993,1,74,-94,43,43.7833333333,45.2166666667}, +{3111,0,116,145,-37,-36,-38}, +{31370,1,1019,4.3674866667,90,49.8333339000,51.1666672333}, +{32001,1,62,-109.5,47,47.85,48.7166666667}, +{32002,1,62,-109.5,45.8333333333,46.45,47.8833333333}, +{32003,1,62,-109.5,44,44.8666666667,46.4}, +{32005,0,62,-100,41.3333333333,41.85,42.8166666667}, +{32006,0,62,-99.5,39.6666666667,40.2833333333,41.7166666667}, +{32018,1,62,-74,40.5,40.6666666667,41.0333333333}, +{32019,0,62,-79,33.75,34.3333333333,36.1666666667}, +{32020,0,62,-100.5,47,47.4333333333,48.7333333333}, +{32021,0,62,-100.5,45.6666666667,46.1833333333,47.4833333333}, +{32022,0,62,-82.5,39.6666666667,40.4333333333,41.7}, +{32023,0,62,-82.5,38,38.7333333333,40.0333333333}, +{32024,0,62,-98,35,35.5666666667,36.7666666667}, +{32025,0,62,-98,33.3333333333,33.9333333333,35.2333333333}, +{32026,0,62,-120.5,43.6666666667,44.3333333333,46}, +{32027,0,62,-120.5,41.6666666667,42.3333333333,44}, +{32028,0,62,-77.75,40.1666666667,40.8833333333,41.95}, +{32031,0,62,-81,33,33.7666666667,34.9666666667}, +{32033,0,62,-81,31.8333333333,32.3333333333,33.6666666667}, +{32034,0,62,-100,43.8333333333,44.4166666667,45.6833333333}, +{32035,0,62,-100.3333333333,42.3333333333,42.8333333333,44.4}, +{32036,0,62,-86,34.6666666667,35.25,36.4166666667}, +{32037,0,62,-101.5,34,34.65,36.1833333333}, +{32038,0,62,-97.5,31.6666666667,32.1333333333,33.9666666667}, +{32039,0,62,-100.3333333333,29.6666666667,30.1166666667,31.8833333333}, +{32040,0,62,-99,27.8333333333,28.3833333333,30.2833333333}, +{32041,0,62,-98.5,25.6666666667,26.1666666667,27.8333333333}, +{32042,0,62,-111.5,40.3333333333,40.7166666667,41.7833333333}, +{32043,0,62,-111.5,38.3333333333,39.0166666667,40.65}, +{32044,0,62,-111.5,36.6666666667,37.2166666667,38.35}, +{32046,0,62,-78.5,37.6666666667,38.0333333333,39.2}, +{32047,0,62,-78.5,36.3333333333,36.7666666667,37.9666666667}, +{32048,0,62,-120.8333333333,47,47.5,48.7333333333}, +{32049,0,62,-120.5,45.3333333333,45.8333333333,47.3333333333}, +{32050,0,62,-79.5,38.5,39,40.25}, +{32051,0,62,-81,37,37.4833333333,38.8833333333}, +{32052,0,62,-90,45.1666666667,45.5666666667,46.7666666667}, +{32053,0,62,-90,43.8333333333,44.25,45.5}, +{32054,0,62,-90,42,42.7333333333,44.0666666667}, +{32059,0,62,-66.4333333333,18.4333333333,18.0333333333,18.4333333333}, +{32060,0,62,-66.4333333333,18.4333333333,18.0333333333,18.4333333333}, +{32100,1,74,-109.5,44.25,45,49}, +{32104,1,74,-100,39.8333333333,40,43}, +{32118,1,74,-74,40.1666666667,40.6666666667,41.0333333333}, +{32119,1,74,-79,33.75,34.3333333333,36.1666666667}, +{32120,1,74,-100.5,47,47.4333333333,48.7333333333}, +{32121,1,74,-100.5,45.6666666667,46.1833333333,47.4833333333}, +{32122,1,74,-82.5,39.6666666667,40.4333333333,41.7}, +{32123,1,74,-82.5,38,38.7333333333,40.0333333333}, +{32124,1,74,-98,35,35.5666666667,36.7666666667}, +{32125,1,74,-98,33.3333333333,33.9333333333,35.2333333333}, +{32126,1,74,-120.5,43.6666666667,44.3333333333,46}, +{32127,1,74,-120.5,41.6666666667,42.3333333333,44}, +{32128,1,74,-77.75,40.1666666667,40.8833333333,41.95}, +{32129,1,74,-77.75,39.3333333333,39.9333333333,40.9666666667}, +{32133,1,74,-81,31.8333333333,32.5,34.8333333333}, +{32134,1,74,-100,43.8333333333,44.4166666667,45.6833333333}, +{32135,1,74,-100.3333333333,42.3333333333,42.8333333333,44.4}, +{32136,1,74,-86,34.3333333333,35.25,36.4166666667}, +{32137,1,74,-101.5,34,34.65,36.1833333333}, +{32138,1,74,-98.5,31.6666666667,32.1333333333,33.9666666667}, +{32139,1,74,-100.3333333333,29.6666666667,30.1166666667,31.8833333333}, +{32140,1,74,-99,27.8333333333,28.3833333333,30.2833333333}, +{32141,1,74,-98.5,25.6666666667,26.1666666667,27.8333333333}, +{32142,1,74,-111.5,40.3333333333,40.7166666667,41.7833333333}, +{32143,1,74,-111.5,38.3333333333,39.0166666667,40.65}, +{32144,1,74,-111.5,36.6666666667,37.2166666667,38.35}, +{32146,1,74,-78.5,37.6666666667,38.0333333333,39.2}, +{32147,1,74,-78.5,36.3333333333,36.7666666667,37.9666666667}, +{32148,1,74,-120.8333333333,47,47.5,48.7333333333}, +{32149,1,74,-120.5,45.3333333333,45.8333333333,47.3333333333}, +{32150,1,74,-79.5,38.5,39,40.25}, +{32151,1,74,-81,37,37.4833333333,38.8833333333}, +{32152,1,74,-90,45.1666666667,45.5666666667,46.7666666667}, +{32153,1,74,-90,43.8333333333,44.25,45.5}, +{32154,1,74,-90,42,42.7333333333,44.0666666667}, +{32161,1,74,-66.4333333333,17.8333333333,18.0333333333,18.4333333333}, +{3300,1,115,24,57.51755394,58,59.33333333}, +{3301,1,115,24,57.51755393056,58,59.33333333}, +{3797,0,66,-70,44,50,46}, +{3798,0,74,-70,44,50,46}, +{3799,0,74,-70,44,50,46}, +{3942,0,33,3,42,41.25,42.75}, +{3943,0,33,3,43,42.25,43.75}, +{3944,0,33,3,44,43.25,44.75}, +{3945,0,33,3,45,44.25,45.75}, +{3946,0,33,3,46,45.25,46.75}, +{3947,0,33,3,47,46.25,47.75}, +{3948,0,33,3,48,47.25,48.75}, +{3949,0,33,3,49,48.25,49.75}, +{3950,0,33,3,50,49.25,50.75}, +{42101,0,104,-95,0,49,77}, +{42103,0,104,-100,0,33,45}, +{42304,0,74,-95,49,49,77}, +{0,0,0,110,10,25,40}, +{0,0,0,132.5,-10,-21.5,-33.5}, +{0,0,0,25,35,40,65}, +{0,0,0,47.5,25,15,35}, +{0,0,0,95,40,20,60}, +{0,0,1002,0,42.165,41.5603877778,42.76766333}, +{0,0,1002,0,42.165,41.5603877778,42.767663333}, +{0,0,1002,0,42.165,41.560387778,42.76766333}, +{0,0,1002,0,42.165,41.560387778,42.767663333}, +{0,0,1002,0,42.165,41.56038778,42.76766333}, +{0,0,1002,0,42.165,41.560387840948,42.76766346965}, +{0,0,1002,0,44.1,43.199291275544,44.996093814511}, +{0,0,1002,0,44.1,43.1992913889,44.99609389}, +{0,0,1002,0,44.1,43.199291389,44.99609389}, +{0,0,1002,0,44.1,43.19929139,44.99609389}, +{0,0,1002,0,46.8,45.8989188889,47.69601444}, +{0,0,1002,0,46.8,45.898918889,47.69601444}, +{0,0,1002,0,46.8,45.89891889,47.69601444}, +{0,0,1002,0,46.8,45.898918964419,47.696014502038}, +{0,0,1002,0,49.5,48.5985227778,50.39591167}, +{0,0,1002,0,49.5,48.598522778,50.39591167}, +{0,0,1002,0,49.5,48.59852278,50.39591167}, +{0,0,1002,0,49.5,48.598522847174,50.395911631678}, +{0,0,1005,23,-23,-18,-32}, +{0,0,1022,2.7,36,37.575,34.425}, +{0,0,104,13.33333333,47.5,46,49}, +{0,0,104,13.33333333,48,46,49}, +{0,0,104,-19,65,64.25,65.75}, +{0,0,104,36.0,25.0,37.5,40.5}, +{0,0,104,36,25,37.5,40.5}, +{0,0,104,70,-50,-68.5,-74.5}, +{0,0,110,4.367975,90,49.8333333333,51.1666666667}, +{0,0,115,10,52,35,45}, +{0,0,116,135,-24,-18,-36}, +{0,0,116,135,-32,-28,-36}, +{0,0,12,135,-24,-18,-36}, +{0,0,12,145,-37,-36,-38}, +{0,0,13,135,-24,-18,-36}, +{0,0,19,23,-23,-18,-32}, +{0,0,28,17,29.77930555,42,56}, +{0,0,28,19,29.77930555,42,56}, +{0,0,28,36.0,25.0,37.5,40.5}, +{0,0,33,13.5,0,52.6666666667,55.3333333333}, +{0,0,33,15,0,56.5,60.5}, +{0,0,33,15,0,58,66}, +{0,0,33,15,0,63.5,67.5}, +{0,0,33,15.5,0,56.6666666667,59.3333333333}, +{0,0,33,15.5,0,60.6666666667,63.3333333333}, +{0,0,33,16.5,0,60.6666666667,63.3333333333}, +{0,0,33,18.5,0,64.6666666667,67.3333333333}, +{0,0,33,19,0,64.6666666667,67.3333333333}, +{0,0,55,-5.4,22.5,20.9075742561,24.0921050540}, +{0,0,55,-5.4,26.1,24.5075340813,27.6921073632}, +{0,0,55,-5.4,29.7,28.1063294800,31.2932791054}, +{0,0,55,-5.4,33.3,31.72786641202,34.8717272112}, +{0,0,62,-70.5,41,41.2833333333,41.4833333333}, +{0,0,62,-77.75,39.3333333333,39.9333333333,40.9666666667}, +{0,0,62,-91.3333333333,25.6666666667,26.1666666667,27.8333333333}, +{0,0,62,-91.3333333333,28.6666666667,29.3,30.67}, +{0,0,62,-96,23,20,60}, +{0,0,62,-96,23,33,45}, +{0,0,62,-96,39,33,45}, +{0,0,66,-68.5,44,46,60}, +{0,0,74,-100.3333333333,42.3333333333,42.8333333333,44.4}, +{0,0,74,-100,39.8333333333,40,43}, +{0,0,74,-100,43.8333333333,44.4166666667,45.6833333333}, +{0,0,74,-109.5,44.25,45,49}, +{0,0,74,-111.5,36.6666666667,37.2166666667,38.35}, +{0,0,74,-111.5,38.3333333333,39.0166666667,40.65}, +{0,0,74,-111.5,40.3333333333,40.7166666667,41.7833333333}, +{0,0,74,-120.5,41.6666666667,42.3333333333,44}, +{0,0,74,-120.5,43.6666666667,44.3333333333,46}, +{0,0,74,-176,51,51.8333333333,53.8333333333}, +{0,0,74,-66.4333333333,17.8333333333,18.0333333333,18.4333333333}, +{0,0,74,-68.5,44,46,60}, +{0,0,74,-79.5,38.5,39,40.25}, +{0,0,74,-81,31.8333333333,32.5,34.8333333333}, +{0,0,74,-81,37,37.4833333333,38.8833333333}, +{0,0,74,-82.5,38,38.7333333333,40.0333333333}, +{0,0,74,-82.5,39.6666666667,40.4333333333,41.7}, +{0,0,74,-84.25,37.5,37.9666666667,38.9666666667}, +{0,0,74,-84.3666666667,41.5,42.1,43.6666666667}, +{0,0,74,-84.3666666667,43.3166666667,44.1833333333,45.7}, +{0,0,74,-87,44.7833333333,45.4833333333,47.0833333333}, +{0,0,74,-91.3333333333,25.5,26.1666666667,27.8333333333}, +{0,0,74,-91.3333333333,28.5,29.3,30.7}, +{0,0,74,-92,32.6666666667,33.3,34.7666666667}, +{0,0,74,-92,34.3333333333,34.9333333333,36.2333333333}, +{0,0,74,-92.5,30.5,31.1666666667,32.6666666667}, +{0,0,74,-93.1,46.5,47.0333333333,48.6333333333}, +{0,0,74,-93.5,40,40.6166666667,41.7833333333}, +{0,0,74,-93.5,41.5,42.0666666667,43.2666666667}, +{0,0,74,-94.25,45,45.6166666667,47.05}, +{0,0,74,-94,43,43.7833333333,45.2166666667}, +{0,0,74,-98,38.3333333333,38.7166666667,39.7833333333}, +{0,0,74,-98.5,36.6666666667,38.5666666667,37.2666666667}, +}; + +/********************************************************************** + * TABFile::GetSpatialRef() + * + * Returns a reference to an OGRSpatialReference for this dataset. + * If the projection parameters have not been parsed yet, then we will + * parse them before returning. + * + * The returned object is owned and maintained by this TABFile and + * should not be modified or freed by the caller. + * + * Returns NULL if the SpatialRef cannot be accessed. + **********************************************************************/ +OGRSpatialReference *TABFile::GetSpatialRef() +{ + if (m_poMAPFile == NULL ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "GetSpatialRef() failed: file has not been opened yet."); + return NULL; + } + + /*----------------------------------------------------------------- + * If projection params have already been processed, just use them. + *----------------------------------------------------------------*/ + if (m_poSpatialRef != NULL) + return m_poSpatialRef; + + + /*----------------------------------------------------------------- + * Fetch the parameters from the header. + *----------------------------------------------------------------*/ + TABMAPHeaderBlock *poHeader; + TABProjInfo sTABProj; + + if ((poHeader = m_poMAPFile->GetHeaderBlock()) == NULL || + poHeader->GetProjInfo( &sTABProj ) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "GetSpatialRef() failed reading projection parameters."); + return NULL; + } + + m_poSpatialRef = GetSpatialRefFromTABProj(sTABProj); + return m_poSpatialRef; +} + +/********************************************************************** + * TABFile::GetSpatialRefFromTABProj() + **********************************************************************/ + +OGRSpatialReference* TABFile::GetSpatialRefFromTABProj(const TABProjInfo& sTABProj) +{ + /*----------------------------------------------------------------- + * Get the units name, and translation factor. + *----------------------------------------------------------------*/ + const char *pszUnitsName; + const char *pszUnitsConv; + /* double dfConv = 1.0; */ + + switch( sTABProj.nUnitsId ) + { + case 0: + pszUnitsName = "Mile"; + pszUnitsConv = "1609.344"; + break; + + case 1: + pszUnitsName = "Kilometer"; + pszUnitsConv = "1000.0"; + break; + + case 2: + pszUnitsName = "IINCH"; + pszUnitsConv = "0.0254"; + break; + + case 3: + pszUnitsName = SRS_UL_FOOT; + pszUnitsConv = SRS_UL_FOOT_CONV; + break; + + case 4: + pszUnitsName = "IYARD"; + pszUnitsConv = "0.9144"; + break; + + case 5: + pszUnitsName = "Millimeter"; + pszUnitsConv = "0.001"; + break; + + case 6: + pszUnitsName = "Centimeter"; + pszUnitsConv = "0.01"; + break; + + case 7: + pszUnitsName = SRS_UL_METER; + pszUnitsConv = "1.0"; + break; + + case 8: + pszUnitsName = SRS_UL_US_FOOT; + pszUnitsConv = SRS_UL_US_FOOT_CONV; + break; + + case 9: + pszUnitsName = SRS_UL_NAUTICAL_MILE; + pszUnitsConv = SRS_UL_NAUTICAL_MILE_CONV; + break; + + case 30: + pszUnitsName = SRS_UL_LINK; + pszUnitsConv = SRS_UL_LINK_CONV; + break; + + case 31: + pszUnitsName = SRS_UL_CHAIN; + pszUnitsConv = SRS_UL_CHAIN_CONV; + break; + + case 32: + pszUnitsName = SRS_UL_ROD; + pszUnitsConv = SRS_UL_ROD_CONV; + break; + + default: + pszUnitsName = SRS_UL_METER; + pszUnitsConv = "1.0"; + break; + } + + /* dfConv = CPLAtof(pszUnitsConv); */ + + /*----------------------------------------------------------------- + * Transform them into an OGRSpatialReference. + *----------------------------------------------------------------*/ + OGRSpatialReference* poSpatialRef = new OGRSpatialReference; + + /*----------------------------------------------------------------- + * Handle the PROJCS style projections, but add the datum later. + *----------------------------------------------------------------*/ + switch( sTABProj.nProjId ) + { + /*-------------------------------------------------------------- + * NonEarth ... we return with an empty SpatialRef. Eventually + * we might want to include the units, but not for now. + *-------------------------------------------------------------*/ + case 0: + poSpatialRef->SetLocalCS( "Nonearth" ); + break; + + /*-------------------------------------------------------------- + * lat/long .. just add the GEOGCS later. + *-------------------------------------------------------------*/ + case 1: + break; + + /*-------------------------------------------------------------- + * Cylindrical Equal Area + *-------------------------------------------------------------*/ + case 2: + poSpatialRef->SetCEA( sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + sTABProj.adProjParams[3] ); + break; + + /*-------------------------------------------------------------- + * Lambert Conic Conformal + *-------------------------------------------------------------*/ + case 3: + poSpatialRef->SetLCC( sTABProj.adProjParams[2], + sTABProj.adProjParams[3], + sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[4], + sTABProj.adProjParams[5] ); + break; + + /*-------------------------------------------------------------- + * Lambert Azimuthal Equal Area + *-------------------------------------------------------------*/ + case 4: + case 29: + poSpatialRef->SetLAEA( sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + 0.0, 0.0 ); + break; + + /*-------------------------------------------------------------- + * Azimuthal Equidistant (Polar aspect only) + *-------------------------------------------------------------*/ + case 5: + case 28: + poSpatialRef->SetAE( sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + 0.0, 0.0 ); + break; + + /*-------------------------------------------------------------- + * Equidistant Conic + *-------------------------------------------------------------*/ + case 6: + poSpatialRef->SetEC( sTABProj.adProjParams[2], + sTABProj.adProjParams[3], + sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[4], + sTABProj.adProjParams[5] ); + break; + + /*-------------------------------------------------------------- + * Hotine Oblique Mercator + *-------------------------------------------------------------*/ + case 7: + poSpatialRef->SetHOM( sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + 90.0, + sTABProj.adProjParams[3], + sTABProj.adProjParams[4], + sTABProj.adProjParams[5] ); + break; + + /*-------------------------------------------------------------- + * Transverse Mercator + *-------------------------------------------------------------*/ + case 8: + poSpatialRef->SetTM( sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + sTABProj.adProjParams[3], + sTABProj.adProjParams[4] ); + break; + + /*---------------------------------------------------------------- + * Transverse Mercator,(modified for Danish System 34 Jylland-Fyn) + *---------------------------------------------------------------*/ + case 21: + poSpatialRef->SetTMVariant( SRS_PT_TRANSVERSE_MERCATOR_MI_21, + sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + sTABProj.adProjParams[3], + sTABProj.adProjParams[4] ); + break; + + /*-------------------------------------------------------------- + * Transverse Mercator,(modified for Danish System 34 Sjaelland) + *-------------------------------------------------------------*/ + case 22: + poSpatialRef->SetTMVariant( SRS_PT_TRANSVERSE_MERCATOR_MI_22, + sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + sTABProj.adProjParams[3], + sTABProj.adProjParams[4] ); + break; + + /*---------------------------------------------------------------- + * Transverse Mercator,(modified for Danish System 34/45 Bornholm) + *---------------------------------------------------------------*/ + case 23: + poSpatialRef->SetTMVariant( SRS_PT_TRANSVERSE_MERCATOR_MI_23, + sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + sTABProj.adProjParams[3], + sTABProj.adProjParams[4] ); + break; + + /*-------------------------------------------------------------- + * Transverse Mercator,(modified for Finnish KKJ) + *-------------------------------------------------------------*/ + case 24: + poSpatialRef->SetTMVariant( SRS_PT_TRANSVERSE_MERCATOR_MI_24, + sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + sTABProj.adProjParams[3], + sTABProj.adProjParams[4] ); + break; + + /*-------------------------------------------------------------- + * Albers Conic Equal Area + *-------------------------------------------------------------*/ + case 9: + poSpatialRef->SetACEA( sTABProj.adProjParams[2], + sTABProj.adProjParams[3], + sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[4], + sTABProj.adProjParams[5] ); + break; + + /*-------------------------------------------------------------- + * Mercator + *-------------------------------------------------------------*/ + case 10: + poSpatialRef->SetMercator( 0.0, sTABProj.adProjParams[0], + 1.0, 0.0, 0.0 ); + break; + + /*-------------------------------------------------------------- + * Miller Cylindrical + *-------------------------------------------------------------*/ + case 11: + poSpatialRef->SetMC( 0.0, sTABProj.adProjParams[0], + 0.0, 0.0 ); + break; + + /*-------------------------------------------------------------- + * Robinson + *-------------------------------------------------------------*/ + case 12: + poSpatialRef->SetRobinson( sTABProj.adProjParams[0], + 0.0, 0.0 ); + break; + + /*-------------------------------------------------------------- + * Mollweide + *-------------------------------------------------------------*/ + case 13: + poSpatialRef->SetMollweide( sTABProj.adProjParams[0], + 0.0, 0.0 ); + break; + + /*-------------------------------------------------------------- + * Eckert IV + *-------------------------------------------------------------*/ + case 14: + poSpatialRef->SetEckertIV( sTABProj.adProjParams[0], 0.0, 0.0 ); + break; + + /*-------------------------------------------------------------- + * Eckert VI + *-------------------------------------------------------------*/ + case 15: + poSpatialRef->SetEckertVI( sTABProj.adProjParams[0], 0.0, 0.0 ); + break; + + /*-------------------------------------------------------------- + * Sinusoidal + *-------------------------------------------------------------*/ + case 16: + poSpatialRef->SetSinusoidal( sTABProj.adProjParams[0], + 0.0, 0.0 ); + break; + + /*-------------------------------------------------------------- + * Gall Stereographic + *-------------------------------------------------------------*/ + case 17: + poSpatialRef->SetGS( sTABProj.adProjParams[0], 0.0, 0.0 ); + break; + + /*-------------------------------------------------------------- + * New Zealand Map Grid + *-------------------------------------------------------------*/ + case 18: + poSpatialRef->SetNZMG( sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + sTABProj.adProjParams[3] ); + break; + + /*-------------------------------------------------------------- + * Lambert Conic Conformal (Belgium) + *-------------------------------------------------------------*/ + case 19: + poSpatialRef->SetLCCB( sTABProj.adProjParams[2], + sTABProj.adProjParams[3], + sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[4], + sTABProj.adProjParams[5] ); + break; + + /*-------------------------------------------------------------- + * Stereographic + *-------------------------------------------------------------*/ + case 20: + case 31: /* this is called Double Stereographic, whats the diff? */ + poSpatialRef->SetStereographic( sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + sTABProj.adProjParams[3], + sTABProj.adProjParams[4] ); + break; + + /*-------------------------------------------------------------- + * Swiss Oblique Mercator / Cylindrical + *-------------------------------------------------------------*/ + case 25: + poSpatialRef->SetSOC( sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + sTABProj.adProjParams[3] ); + break; + + /*-------------------------------------------------------------- + * Regional Mercator (regular mercator with a latitude). + *-------------------------------------------------------------*/ + case 26: + poSpatialRef->SetMercator( sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + 1.0, 0.0, 0.0 ); + break; + + /*-------------------------------------------------------------- + * Polyconic + *-------------------------------------------------------------*/ + case 27: + poSpatialRef->SetPolyconic( sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + sTABProj.adProjParams[3] ); + break; + + /*-------------------------------------------------------------- + * Cassini/Soldner + *-------------------------------------------------------------*/ + case 30: + poSpatialRef->SetCS( sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + sTABProj.adProjParams[3] ); + break; + + /*-------------------------------------------------------------- + * Krovak + *-------------------------------------------------------------*/ + case 32: + poSpatialRef->SetKrovak( sTABProj.adProjParams[1], // dfCenterLat + sTABProj.adProjParams[0], // dfCenterLong + sTABProj.adProjParams[3], // dfAzimuth + sTABProj.adProjParams[2], // dfPseudoStdParallelLat + 1.0, // dfScale + sTABProj.adProjParams[4], // dfFalseEasting + sTABProj.adProjParams[5] ); // dfFalseNorthing + break; + + /*-------------------------------------------------------------- + * Equidistant Cylindrical / Equirectangular + *-------------------------------------------------------------*/ + case 33: + poSpatialRef->SetEquirectangular( sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[2], + sTABProj.adProjParams[3] ); + break; + + default: + break; + } + + /*----------------------------------------------------------------- + * Collect units definition. + *----------------------------------------------------------------*/ + if( sTABProj.nProjId != 1 && poSpatialRef->GetRoot() != NULL ) + { + OGR_SRSNode *poUnits = new OGR_SRSNode("UNIT"); + + poSpatialRef->GetRoot()->AddChild(poUnits); + + poUnits->AddChild( new OGR_SRSNode( pszUnitsName ) ); + poUnits->AddChild( new OGR_SRSNode( pszUnitsConv ) ); + } + + /*----------------------------------------------------------------- + * Local (nonearth) coordinate systems have no Geographic relationship + * so we just return from here. + *----------------------------------------------------------------*/ + if( sTABProj.nProjId == 0 ) + return poSpatialRef; + + /*----------------------------------------------------------------- + * Set the datum. We are only given the X, Y and Z shift for + * the datum, so for now we just synthesize a name from this. + * It would be better if we could lookup a name based on the shift. + * + * Since we have already encountered files in which adDatumParams[] values + * were in the order of 1e-150 when they should have actually been zeros, + * we will use an epsilon in our scan instead of looking for equality. + *----------------------------------------------------------------*/ +#define TAB_EQUAL(a, b) (((a)<(b) ? ((b)-(a)) : ((a)-(b))) < 1e-10) + char szDatumName[160]; + int iDatumInfo; + const MapInfoDatumInfo *psDatumInfo = NULL; + + for( iDatumInfo = 0; + asDatumInfoList[iDatumInfo].nMapInfoDatumID != -1; + iDatumInfo++ ) + { + psDatumInfo = asDatumInfoList + iDatumInfo; + + if( TAB_EQUAL(psDatumInfo->nEllipsoid, sTABProj.nEllipsoidId) && + ((sTABProj.nDatumId > 0 && + sTABProj.nDatumId == psDatumInfo->nMapInfoDatumID) || + (sTABProj.nDatumId <= 0 + && TAB_EQUAL(psDatumInfo->dfShiftX, sTABProj.dDatumShiftX) + && TAB_EQUAL(psDatumInfo->dfShiftY, sTABProj.dDatumShiftY) + && TAB_EQUAL(psDatumInfo->dfShiftZ, sTABProj.dDatumShiftZ) + && TAB_EQUAL(psDatumInfo->dfDatumParm0,sTABProj.adDatumParams[0]) + && TAB_EQUAL(psDatumInfo->dfDatumParm1,sTABProj.adDatumParams[1]) + && TAB_EQUAL(psDatumInfo->dfDatumParm2,sTABProj.adDatumParams[2]) + && TAB_EQUAL(psDatumInfo->dfDatumParm3,sTABProj.adDatumParams[3]) + && TAB_EQUAL(psDatumInfo->dfDatumParm4,sTABProj.adDatumParams[4])))) + break; + + psDatumInfo = NULL; + } + + if( psDatumInfo == NULL ) + { + if( sTABProj.adDatumParams[0] == 0.0 + && sTABProj.adDatumParams[1] == 0.0 + && sTABProj.adDatumParams[2] == 0.0 + && sTABProj.adDatumParams[3] == 0.0 + && sTABProj.adDatumParams[4] == 0.0 ) + { + sprintf( szDatumName, + "MIF 999,%d,%.15g,%.15g,%.15g", + sTABProj.nEllipsoidId, + sTABProj.dDatumShiftX, + sTABProj.dDatumShiftY, + sTABProj.dDatumShiftZ ); + } + else + { + sprintf( szDatumName, + "MIF 9999,%d,%.15g,%.15g,%.15g,%.15g,%.15g,%.15g,%.15g,%.15g", + sTABProj.nEllipsoidId, + sTABProj.dDatumShiftX, + sTABProj.dDatumShiftY, + sTABProj.dDatumShiftZ, + sTABProj.adDatumParams[0], + sTABProj.adDatumParams[1], + sTABProj.adDatumParams[2], + sTABProj.adDatumParams[3], + sTABProj.adDatumParams[4] ); + } + } + else if( strlen(psDatumInfo->pszOGCDatumName) > 0 ) + { + strncpy( szDatumName, psDatumInfo->pszOGCDatumName, + sizeof(szDatumName) ); + + /* For LCC, standard parallel 1 and 2 can be switched indifferently */ + /* So the MapInfo order and the EPSG order are not generally identical */ + /* which may cause recognition problems when reading in MapInfo */ + if( sTABProj.nProjId == 3 ) + { + double dfCenterLong = sTABProj.adProjParams[0]; + double dfCenterLat = sTABProj.adProjParams[1]; + double dfStdP1 = sTABProj.adProjParams[2]; + double dfStdP2 = sTABProj.adProjParams[3]; + + for(size_t i=0;iSetLCC( sTABProj.adProjParams[3], + sTABProj.adProjParams[2], + sTABProj.adProjParams[1], + sTABProj.adProjParams[0], + sTABProj.adProjParams[4], + sTABProj.adProjParams[5] ); + } + if( asMapInfoLCCSRSList[i].nEPSGCode > 0 ) + poSpatialRef->SetAuthority( "PROJCS", "EPSG", + asMapInfoLCCSRSList[i].nEPSGCode ); + break; + } + } + } + } + } + else + { + sprintf( szDatumName, "MIF %d", psDatumInfo->nMapInfoDatumID ); + } + + /*----------------------------------------------------------------- + * Set the spheroid. + *----------------------------------------------------------------*/ + double dfSemiMajor=0.0, dfInvFlattening=0.0; + const char *pszSpheroidName = NULL; + + for( int i = 0; asSpheroidInfoList[i].nMapInfoId != -1; i++ ) + { + if( asSpheroidInfoList[i].nMapInfoId == sTABProj.nEllipsoidId ) + { + dfSemiMajor = asSpheroidInfoList[i].dfA; + dfInvFlattening = asSpheroidInfoList[i].dfInvFlattening; + pszSpheroidName = asSpheroidInfoList[i].pszMapinfoName; + break; + } + } + + // use WGS 84 if nothing is known. + if( pszSpheroidName == NULL ) + { + pszSpheroidName = "unknown"; + dfSemiMajor = 6378137.0; + dfInvFlattening = 298.257223563; + } + + /*----------------------------------------------------------------- + * Set the prime meridian. + *----------------------------------------------------------------*/ + double dfPMOffset = 0.0; + const char *pszPMName = "Greenwich"; + + if( /*sTABProj.nDatumId == 9999 ||*/ sTABProj.adDatumParams[4] != 0.0 ) + { + dfPMOffset = sTABProj.adDatumParams[4]; + + pszPMName = "non-Greenwich"; + } + + /*----------------------------------------------------------------- + * Create a GEOGCS definition. + *----------------------------------------------------------------*/ + + poSpatialRef->SetGeogCS( "unnamed", + szDatumName, + pszSpheroidName, + dfSemiMajor, dfInvFlattening, + pszPMName, dfPMOffset, + SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV)); + + if( psDatumInfo != NULL ) + { + poSpatialRef->SetTOWGS84( psDatumInfo->dfShiftX, + psDatumInfo->dfShiftY, + psDatumInfo->dfShiftZ, + psDatumInfo->dfDatumParm0 == 0 ? 0 : -psDatumInfo->dfDatumParm0, /* avoids 0 to be transformed into -0 */ + psDatumInfo->dfDatumParm1 == 0 ? 0 : -psDatumInfo->dfDatumParm1, + psDatumInfo->dfDatumParm2 == 0 ? 0 : -psDatumInfo->dfDatumParm2, + psDatumInfo->dfDatumParm3 ); + } + else + { + poSpatialRef->SetTOWGS84( sTABProj.dDatumShiftX, + sTABProj.dDatumShiftY, + sTABProj.dDatumShiftZ, + sTABProj.adDatumParams[0] == 0 ? 0 : -sTABProj.adDatumParams[0], + sTABProj.adDatumParams[1] == 0 ? 0 : -sTABProj.adDatumParams[1], + sTABProj.adDatumParams[2] == 0 ? 0 : -sTABProj.adDatumParams[2], + sTABProj.adDatumParams[3] ); + } + + /*----------------------------------------------------------------- + * Special case for Google Mercator (datum=157, ellipse=54, gdal #4115) + *----------------------------------------------------------------*/ + if( sTABProj.nProjId == 10 + && sTABProj.nDatumId == 157 + && sTABProj.nEllipsoidId == 54 ) + { + poSpatialRef->SetNode( "PROJCS", "WGS 84 / Pseudo-Mercator" ); + poSpatialRef->SetExtension( "PROJCS", "PROJ4", "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs" ); + } + + /*----------------------------------------------------------------- + * Special case for France Lambert-93 + *----------------------------------------------------------------*/ + if( sTABProj.nProjId == 3 + && sTABProj.nDatumId == 33 + && sTABProj.nEllipsoidId == 0 + && TAB_EQUAL(poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), 3.0) + && TAB_EQUAL(poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), 46.5) ) + { + poSpatialRef->SetNode( "PROJCS", "RGF93 / Lambert-93" ); + poSpatialRef->SetNode( "PROJCS|GEOGCS", "RGF93"); + poSpatialRef->SetNode( "PROJCS|GEOGCS|DATUM", "Reseau_Geodesique_Francais_1993"); + } + + return poSpatialRef; +} + +/********************************************************************** + * TABFile::SetSpatialRef() + * + * Set the OGRSpatialReference for this dataset. + * A reference to the OGRSpatialReference will be kept, and it will also + * be converted into a TABProjInfo to be stored in the .MAP header. + * + * Returns 0 on success, and -1 on error. + **********************************************************************/ +int TABFile::SetSpatialRef(OGRSpatialReference *poSpatialRef) +{ + if (m_eAccessMode != TABWrite) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetSpatialRef() can be used only with Write access."); + return -1; + } + + if (m_poMAPFile == NULL ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetSpatialRef() failed: file has not been opened yet."); + return -1; + } + + if( poSpatialRef == NULL ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetSpatialRef() failed: Called with NULL poSpatialRef."); + return -1; + } + + /*----------------------------------------------------------------- + * Keep a copy of the OGRSpatialReference... + * Note: we have to take the reference count into account... + *----------------------------------------------------------------*/ + if (m_poSpatialRef && m_poSpatialRef->Dereference() == 0) + delete m_poSpatialRef; + + m_poSpatialRef = poSpatialRef->Clone(); + + TABProjInfo sTABProj; + int nParmCount; + GetTABProjFromSpatialRef(poSpatialRef, sTABProj, nParmCount); + + /*----------------------------------------------------------------- + * Set the new parameters in the .MAP header. + * This will also trigger lookup of default bounds for the projection. + *----------------------------------------------------------------*/ + if ( SetProjInfo( &sTABProj ) != 0 ) + { + CPLError(CE_Failure, CPLE_FileIO, + "SetSpatialRef() failed setting projection parameters."); + return -1; + } + + return 0; +} + +int TABFile::GetTABProjFromSpatialRef(const OGRSpatialReference* poSpatialRef, + TABProjInfo& sTABProj, int& nParmCount) +{ + /*----------------------------------------------------------------- + * Initialize TABProjInfo + *----------------------------------------------------------------*/ + sTABProj.nProjId = 0; + sTABProj.nEllipsoidId = 0; /* how will we set this? */ + sTABProj.nUnitsId = 7; + sTABProj.adProjParams[0] = sTABProj.adProjParams[1] = 0.0; + sTABProj.adProjParams[2] = sTABProj.adProjParams[3] = 0.0; + sTABProj.adProjParams[4] = sTABProj.adProjParams[5] = 0.0; + + sTABProj.nDatumId = 0; + sTABProj.dDatumShiftX = 0.0; + sTABProj.dDatumShiftY = 0.0; + sTABProj.dDatumShiftZ = 0.0; + sTABProj.adDatumParams[0] = 0.0; + sTABProj.adDatumParams[1] = 0.0; + sTABProj.adDatumParams[2] = 0.0; + sTABProj.adDatumParams[3] = 0.0; + sTABProj.adDatumParams[4] = 0.0; + + sTABProj.nAffineFlag = 0; + sTABProj.nAffineUnits = 7; + sTABProj.dAffineParamA = 0.0; + sTABProj.dAffineParamB = 0.0; + sTABProj.dAffineParamC = 0.0; + sTABProj.dAffineParamD = 0.0; + sTABProj.dAffineParamE = 0.0; + sTABProj.dAffineParamF = 0.0; + + /*----------------------------------------------------------------- + * Get the linear units and conversion. + *----------------------------------------------------------------*/ + char *pszLinearUnits; + double dfLinearConv; + + dfLinearConv = poSpatialRef->GetLinearUnits( &pszLinearUnits ); + if( dfLinearConv == 0.0 ) + dfLinearConv = 1.0; + + /*----------------------------------------------------------------- + * Transform the projection and projection parameters. + *----------------------------------------------------------------*/ + const char *pszProjection = poSpatialRef->GetAttrValue("PROJECTION"); + double *parms = sTABProj.adProjParams; + nParmCount = 0; + + if( pszProjection == NULL && poSpatialRef->GetAttrNode("GEOGCS") == NULL) + { + /* nonearth */ + sTABProj.nProjId = 0; + } + + else if( pszProjection == NULL ) + { + sTABProj.nProjId = 1; + } + + else if( EQUAL(pszProjection,SRS_PT_ALBERS_CONIC_EQUAL_AREA) ) + { + sTABProj.nProjId = 9; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_CENTER,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0); + parms[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 6; + } + + else if( EQUAL(pszProjection,SRS_PT_AZIMUTHAL_EQUIDISTANT) ) + { + sTABProj.nProjId = 5; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_CENTER,0.0); + parms[2] = 90.0; + nParmCount = 3; + + if( ABS((ABS(parms[1]) - 90)) > 0.001 ) + sTABProj.nProjId = 28; + } + + else if( EQUAL(pszProjection,SRS_PT_CYLINDRICAL_EQUAL_AREA) ) + { + sTABProj.nProjId = 2; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); + nParmCount = 2; + } + + else if( EQUAL(pszProjection,SRS_PT_ECKERT_IV) ) + { + sTABProj.nProjId = 14; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + nParmCount = 1; + } + + else if( EQUAL(pszProjection,SRS_PT_ECKERT_VI) ) + { + sTABProj.nProjId = 15; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + nParmCount = 1; + } + + else if( EQUAL(pszProjection,SRS_PT_EQUIDISTANT_CONIC) ) + { + sTABProj.nProjId = 6; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_CENTER,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0); + parms[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 6; + } + + else if( EQUAL(pszProjection,SRS_PT_GALL_STEREOGRAPHIC) ) + { + sTABProj.nProjId = 17; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + nParmCount = 1; + } + + else if( EQUAL(pszProjection,SRS_PT_HOTINE_OBLIQUE_MERCATOR) ) + { + sTABProj.nProjId = 7; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_CENTER,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_AZIMUTH,0.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); + parms[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 6; + } + + else if( EQUAL(pszProjection,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) + { + sTABProj.nProjId = 4; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_CENTER,0.0); + parms[2] = 90.0; + nParmCount = 3; + + if( ABS((ABS(parms[1]) - 90)) > 0.001 ) + sTABProj.nProjId = 28; + } + + else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ) + { + sTABProj.nProjId = 3; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0); + parms[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 6; + } + + else if( EQUAL(pszProjection,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM) ) + { + sTABProj.nProjId = 19; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0); + parms[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 6; + } + + else if( EQUAL(pszProjection,SRS_PT_MERCATOR_1SP) ) + { + sTABProj.nProjId = 10; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); + nParmCount = 1; // FIXME for MIF export ? + + if( parms[1] != 0.0 ) + { + sTABProj.nProjId = 26; + nParmCount = 2; // FIXME for MIF export ? + } + } + + else if( EQUAL(pszProjection,SRS_PT_MILLER_CYLINDRICAL) ) + { + sTABProj.nProjId = 11; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER,0.0); + nParmCount = 1; + } + + else if( EQUAL(pszProjection,SRS_PT_MOLLWEIDE) ) + { + sTABProj.nProjId = 13; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + nParmCount = 1; + } + + else if( EQUAL(pszProjection,SRS_PT_NEW_ZEALAND_MAP_GRID) ) + { + sTABProj.nProjId = 18; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 4; + } + + else if( EQUAL(pszProjection,SRS_PT_SWISS_OBLIQUE_CYLINDRICAL) ) + { + sTABProj.nProjId = 25; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 4; + } + + else if( EQUAL(pszProjection,SRS_PT_ROBINSON) ) + { + sTABProj.nProjId = 12; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + nParmCount = 1; + } + + else if( EQUAL(pszProjection,SRS_PT_SINUSOIDAL) ) + { + sTABProj.nProjId = 16; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + nParmCount = 1; + } + + else if( EQUAL(pszProjection,SRS_PT_STEREOGRAPHIC) ) + { + sTABProj.nProjId = 20; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 5; + } + + else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR) ) + { + sTABProj.nProjId = 8; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 5; + } + + else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_21) ) // Encom 2003 + { + sTABProj.nProjId = 21; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 5; + } + + else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_22) ) // Encom 2003 + { + sTABProj.nProjId = 22; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 5; + } + + else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_23) ) // Encom 2003 + { + sTABProj.nProjId = 23; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 5; + } + + else if( EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR_MI_24) ) // Encom 2003 + { + sTABProj.nProjId = 24; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_SCALE_FACTOR,1.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 5; + } + + else if( EQUAL(pszProjection,SRS_PT_CASSINI_SOLDNER) ) + { + sTABProj.nProjId = 30; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 4; + } + + else if( EQUAL(pszProjection,SRS_PT_POLYCONIC) ) + { + sTABProj.nProjId = 27; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 4; + } + + else if( EQUAL(pszProjection,SRS_PT_KROVAK) ) + { + sTABProj.nProjId = 32; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_PSEUDO_STD_PARALLEL_1,0.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_AZIMUTH,0.0); + parms[4] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[5] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 6; + } + + else if( EQUAL(pszProjection,SRS_PT_EQUIRECTANGULAR) ) + { + sTABProj.nProjId = 33; + parms[0] = poSpatialRef->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0); + parms[1] = poSpatialRef->GetProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0); + parms[2] = poSpatialRef->GetProjParm(SRS_PP_FALSE_EASTING,0.0); + parms[3] = poSpatialRef->GetProjParm(SRS_PP_FALSE_NORTHING,0.0); + nParmCount = 4; + } + + /* ============================================================== + * Translate Datum and Ellipsoid + * ============================================================== */ + const char *pszWKTDatum = poSpatialRef->GetAttrValue("DATUM"); + const MapInfoDatumInfo *psDatumInfo = NULL; + + int nDatumEPSGCode = -1; + const char *pszDatumAuthority = poSpatialRef->GetAuthorityName("DATUM"); + const char *pszDatumCode = poSpatialRef->GetAuthorityCode("DATUM"); + + if (pszDatumCode && pszDatumAuthority && EQUAL(pszDatumAuthority, "EPSG")) + { + nDatumEPSGCode = atoi(pszDatumCode); + } + + /*----------------------------------------------------------------- + * Default to WGS84 if we have no datum at all. + *----------------------------------------------------------------*/ + if( pszWKTDatum == NULL ) + { + CPLDebug("MITAB", "Cannot find MapInfo datum matching %d. Defaulting to WGS 84", + nDatumEPSGCode); + psDatumInfo = asDatumInfoList+0; /* WGS 84 */ + // From MIF export code. FIXME? + //if( nProjection == 1 ) + // nProjection = 0; + } + + /*----------------------------------------------------------------- + * We know the MIF datum number, and need to look it up to + * translate into datum parameters. + *----------------------------------------------------------------*/ + else if( EQUALN(pszWKTDatum,"MIF ",4) + && atoi(pszWKTDatum+4) != 999 + && atoi(pszWKTDatum+4) != 9999 ) + { + int i; + + int nDatum = atoi(pszWKTDatum+4); + for( i = 0; asDatumInfoList[i].nMapInfoDatumID != -1; i++ ) + { + if( nDatum == asDatumInfoList[i].nMapInfoDatumID ) + { + psDatumInfo = asDatumInfoList + i; + break; + } + } + + if( psDatumInfo == NULL ) + { + CPLDebug("MITAB", "Cannot find MapInfo datum matching %s. Defaulting to WGS 84", + pszWKTDatum); + psDatumInfo = asDatumInfoList+0; /* WGS 84 */ + } + } + + /*----------------------------------------------------------------- + * We have the MIF datum parameters, and apply those directly. + *----------------------------------------------------------------*/ + else if( EQUALN(pszWKTDatum,"MIF ",4) + && (atoi(pszWKTDatum+4) == 999 || atoi(pszWKTDatum+4) == 9999) ) + { + char **papszFields; + + sTABProj.nDatumId = atoi(pszWKTDatum+4); + papszFields = + CSLTokenizeStringComplex( pszWKTDatum+4, ",", FALSE, TRUE); + + if( CSLCount(papszFields) >= 5 ) + { + sTABProj.nEllipsoidId = (GByte)atoi(papszFields[1]); + sTABProj.dDatumShiftX = CPLAtof(papszFields[2]); + sTABProj.dDatumShiftY = CPLAtof(papszFields[3]); + sTABProj.dDatumShiftZ = CPLAtof(papszFields[4]); + } + + if( CSLCount(papszFields) >= 10 ) + { + sTABProj.adDatumParams[0] = CPLAtof(papszFields[5]); + sTABProj.adDatumParams[1] = CPLAtof(papszFields[6]); + sTABProj.adDatumParams[2] = CPLAtof(papszFields[7]); + sTABProj.adDatumParams[3] = CPLAtof(papszFields[8]); + sTABProj.adDatumParams[4] = CPLAtof(papszFields[9]); + } + + if( CSLCount(papszFields) < 5 ) + { + CPLDebug("MITAB", "Cannot find MapInfo datum matching %s. Defaulting to WGS 84", + pszWKTDatum); + psDatumInfo = asDatumInfoList+0; /* WGS 84 */ + } + + CSLDestroy( papszFields ); + } + + /*----------------------------------------------------------------- + * We have a "real" datum name, and possibly an EPSG code for the + * datum. Try to look it up (using EPSG code first) and get the + * parameters. If we don't find it with either just use WGS84. + *----------------------------------------------------------------*/ + else + { + int i; + + for( i = 0; asDatumInfoList[i].nMapInfoDatumID != -1; i++ ) + { + if ( (nDatumEPSGCode > 0 && asDatumInfoList[i].nDatumEPSGCode == nDatumEPSGCode) || + EQUAL(pszWKTDatum,asDatumInfoList[i].pszOGCDatumName) ) + { + psDatumInfo = asDatumInfoList + i; + break; + } + } + + if( psDatumInfo == NULL ) + { + CPLDebug("MITAB", "Cannot find MapInfo datum matching %s,%d. Defaulting to WGS 84", + pszWKTDatum, nDatumEPSGCode); + psDatumInfo = asDatumInfoList+0; /* WGS 84 */ + } + } + + if( psDatumInfo != NULL ) + { + sTABProj.nEllipsoidId = (GByte)psDatumInfo->nEllipsoid; + sTABProj.nDatumId = (GInt16)psDatumInfo->nMapInfoDatumID; + sTABProj.dDatumShiftX = psDatumInfo->dfShiftX; + sTABProj.dDatumShiftY = psDatumInfo->dfShiftY; + sTABProj.dDatumShiftZ = psDatumInfo->dfShiftZ; + sTABProj.adDatumParams[0] = psDatumInfo->dfDatumParm0; + sTABProj.adDatumParams[1] = psDatumInfo->dfDatumParm1; + sTABProj.adDatumParams[2] = psDatumInfo->dfDatumParm2; + sTABProj.adDatumParams[3] = psDatumInfo->dfDatumParm3; + sTABProj.adDatumParams[4] = psDatumInfo->dfDatumParm4; + + /* For LCC, standard parallel 1 and 2 can be switched indifferently */ + /* So the MapInfo order and the EPSG order are not generally identical */ + /* which may cause recognition problems when reading in MapInfo */ + if( sTABProj.nProjId == 3 ) + { + double dfCenterLong = parms[0]; + double dfCenterLat = parms[1]; + double dfStdP1 = parms[2]; + double dfStdP2 = parms[3]; + + for(size_t i=0;i + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_tabfile.cpp,v $ + * Revision 1.78 2010-10-08 18:40:12 aboudreault + * Fixed missing initializations that cause crashes + * + * Revision 1.77 2010-10-08 18:38:13 aboudreault + * Added attribute index support for the sql queries in mapinfo tab format (GDAL bug #3687) + * + * Revision 1.76 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.75 2010-07-05 14:58:33 aboudreault + * Fixed bad feature count after we deleted a feature in MapInfo (bug 2227) + * + * Revision 1.74 2010-01-07 20:39:12 aboudreault + * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141) + * + * Revision 1.73 2008-11-27 20:50:23 aboudreault + * Improved support for OGR date/time types. New Read/Write methods (bug 1948) + * Added support of OGR date/time types for MIF features. + * + * Revision 1.72 2008/11/17 22:06:21 aboudreault + * Added support to use OFTDateTime/OFTDate/OFTTime type when compiled with + * OGR and fixed reading/writing support for these types. + * + * Revision 1.71 2008/09/26 14:40:24 aboudreault + * Fixed bug: MITAB doesn't support writing DateTime type (bug 1948) + * + * Revision 1.70 2008/06/13 18:39:21 aboudreault + * Fixed problem with corrupt pointer if file not found (bug 1899) and + * fixed tabdump build problem if DEBUG option not provided (bug 1898) + * + * Revision 1.69 2008/03/05 20:59:10 dmorissette + * Purged CVS logs in header + * + * Revision 1.68 2008/03/05 20:35:39 dmorissette + * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859) + * + * Revision 1.67 2008/01/29 21:56:39 dmorissette + * Update dataset version properly for Date/Time/DateTime field types (#1754) + * + * Revision 1.66 2008/01/29 20:46:32 dmorissette + * Added support for v9 Time and DateTime fields (byg 1754) + * + * Revision 1.65 2007/09/12 20:22:31 dmorissette + * Added TABFeature::CreateFromMapInfoType() + * + * Revision 1.64 2007/06/21 14:00:23 dmorissette + * Added missing cast in isspace() calls to avoid failed assertion on Windows + * (MITAB bug 1737, GDAL ticket 1678)) + * + * Revision 1.63 2007/06/12 13:52:38 dmorissette + * Added IMapInfoFile::SetCharset() method (bug 1734) + * + * Revision 1.62 2007/06/12 12:50:40 dmorissette + * Use Quick Spatial Index by default until bug 1732 is fixed (broken files + * produced by current coord block splitting technique). + * + * Revision 1.61 2007/03/21 21:15:56 dmorissette + * Added SetQuickSpatialIndexMode() which generates a non-optimal spatial + * index but results in faster write time (bug 1669) + * + * ... + * + * Revision 1.1 1999/07/12 04:18:25 daniel + * Initial checkin + * + **********************************************************************/ + +#include "mitab.h" +#include "mitab_utils.h" +#include "cpl_minixml.h" +#include "ogr_p.h" + +#include /* isspace() */ + +#define UNSUPPORTED_OP_READ_ONLY "%s : unsupported operation on a read-only datasource." + +/*===================================================================== + * class TABFile + *====================================================================*/ + + +/********************************************************************** + * TABFile::TABFile() + * + * Constructor. + **********************************************************************/ +TABFile::TABFile() +{ + m_eAccessMode = TABRead; + m_pszFname = NULL; + m_papszTABFile = NULL; + m_nVersion = 300; + m_eTableType = TABTableNative; + + m_poMAPFile = NULL; + m_poDATFile = NULL; + m_poINDFile = NULL; + m_poDefn = NULL; + m_poSpatialRef = NULL; + m_poCurFeature = NULL; + m_nCurFeatureId = 0; + m_nLastFeatureId = 0; + m_panIndexNo = NULL; + + bUseSpatialTraversal = FALSE; + + m_panMatchingFIDs = NULL; + m_iMatchingFID = 0; + + m_bNeedTABRewrite = FALSE; + m_bLastOpWasRead = FALSE; + m_bLastOpWasWrite = FALSE; +} + +/********************************************************************** + * TABFile::~TABFile() + * + * Destructor. + **********************************************************************/ +TABFile::~TABFile() +{ + Close(); +} + + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig TABFile::GetFeatureCount (int bForce) +{ + + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL || bForce) + return OGRLayer::GetFeatureCount( bForce ); + else + return m_nLastFeatureId; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ +void TABFile::ResetReading() +{ + CPLFree(m_panMatchingFIDs); + m_panMatchingFIDs = NULL; + m_iMatchingFID = 0; + + m_nCurFeatureId = 0; + if( m_poMAPFile != NULL ) + m_poMAPFile->ResetReading(); + +/* -------------------------------------------------------------------- */ +/* Decide whether to operate in spatial traversal mode or not, */ +/* and ensure the current spatial filter is applied to the map */ +/* file object. */ +/* -------------------------------------------------------------------- */ + if( m_poMAPFile ) + { + bUseSpatialTraversal = FALSE; + + m_poMAPFile->ResetCoordFilter(); + + if( m_poFilterGeom != NULL ) + { + OGREnvelope sEnvelope; + TABVertex sMin, sMax; + /* TABMAPHeaderBlock *poHeader; */ + /* poHeader = m_poMAPFile->GetHeaderBlock(); */ + + m_poFilterGeom->getEnvelope( &sEnvelope ); + m_poMAPFile->GetCoordFilter( sMin, sMax ); + + if( sEnvelope.MinX > sMin.x + || sEnvelope.MinY > sMin.y + || sEnvelope.MaxX < sMax.x + || sEnvelope.MaxY < sMax.y ) + { + bUseSpatialTraversal = TRUE; + sMin.x = sEnvelope.MinX; + sMin.y = sEnvelope.MinY; + sMax.x = sEnvelope.MaxX; + sMax.y = sEnvelope.MaxY; + m_poMAPFile->SetCoordFilter( sMin, sMax ); + } + } + } + + m_bLastOpWasRead = FALSE; + m_bLastOpWasWrite = FALSE; +} + +/********************************************************************** + * TABFile::Open() + * + * Open a .TAB dataset and the associated files, and initialize the + * structures to be ready to read features from (or write to) it. + * + * Supported access modes are "r" (read-only) and "w" (create new dataset or + * update). + * + * Set bTestOpenNoError=TRUE to silently return -1 with no error message + * if the file cannot be opened. This is intended to be used in the + * context of a TestOpen() function. The default value is FALSE which + * means that an error is reported if the file cannot be opened. + * + * Note that dataset extents will have to be set using SetBounds() before + * any feature can be written to a newly created dataset. + * + * In read mode, a valid dataset must have at least a .TAB and a .DAT file. + * The .MAP and .ID files are optional and if they do not exist then + * all features will be returned with NONE geometry. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFile::Open(const char *pszFname, TABAccess eAccess, + GBool bTestOpenNoError /*=FALSE*/ ) +{ + char *pszTmpFname = NULL; + int nFnameLen = 0; + + CPLErrorReset(); + + if (m_poMAPFile) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Open() failed: object already contains an open file"); + + return -1; + } + + m_eAccessMode = eAccess; + + /*----------------------------------------------------------------- + * Make sure filename has a .TAB extension... + *----------------------------------------------------------------*/ + m_pszFname = CPLStrdup(pszFname); + nFnameLen = strlen(m_pszFname); + + if (nFnameLen > 4 && (strcmp(m_pszFname+nFnameLen-4, ".TAB")==0 || + strcmp(m_pszFname+nFnameLen-4, ".MAP")==0 || + strcmp(m_pszFname+nFnameLen-4, ".DAT")==0 ) ) + strcpy(m_pszFname+nFnameLen-4, ".TAB"); + else if (nFnameLen > 4 && (EQUAL(m_pszFname+nFnameLen-4, ".tab") || + EQUAL(m_pszFname+nFnameLen-4, ".map") || + EQUAL(m_pszFname+nFnameLen-4, ".dat") ) ) + strcpy(m_pszFname+nFnameLen-4, ".tab"); + else + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed for %s: invalid filename extension", + m_pszFname); + else + CPLErrorReset(); + + CPLFree(m_pszFname); + m_pszFname = NULL; + return -1; + } + + pszTmpFname = CPLStrdup(m_pszFname); + + +#ifndef _WIN32 + /*----------------------------------------------------------------- + * On Unix, make sure extension uses the right cases + * We do it even for write access because if a file with the same + * extension already exists we want to overwrite it. + *----------------------------------------------------------------*/ + TABAdjustFilenameExtension(m_pszFname); +#endif + + /*----------------------------------------------------------------- + * Handle .TAB file... depends on access mode. + *----------------------------------------------------------------*/ + if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite ) + { + /*------------------------------------------------------------- + * Open .TAB file... since it's a small text file, we will just load + * it as a stringlist in memory. + *------------------------------------------------------------*/ + m_papszTABFile = TAB_CSLLoad(m_pszFname); + if (m_papszTABFile == NULL) + { + if (!bTestOpenNoError) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed opening %s.", m_pszFname); + } + CPLFree(m_pszFname); + m_pszFname = NULL; + CSLDestroy(m_papszTABFile); + m_papszTABFile = NULL; + CPLFree( pszTmpFname ); + return -1; + } + + /*------------------------------------------------------------- + * Do a first pass on the TAB header to establish the type of + * dataset we have (NATIVE, DBF, etc.)... and also to know if + * it is a supported type. + *------------------------------------------------------------*/ + if ( ParseTABFileFirstPass(bTestOpenNoError) != 0 ) + { + // No need to produce an error... it's already been done if + // necessary... just cleanup and exit. + + CPLFree(m_pszFname); + m_pszFname = NULL; + CSLDestroy(m_papszTABFile); + m_papszTABFile = NULL; + CPLFree( pszTmpFname ); + + return -1; + } + } + else + { + /*------------------------------------------------------------- + * In Write access mode, the .TAB file will be written during the + * Close() call... we will just set some defaults here. + *------------------------------------------------------------*/ + m_nVersion = 300; + CPLFree(m_pszCharset); + m_pszCharset = CPLStrdup("Neutral"); + m_eTableType = TABTableNative; + + /*------------------------------------------------------------- + * Do initial setup of feature definition. + *------------------------------------------------------------*/ + char *pszFeatureClassName = TABGetBasename(m_pszFname); + m_poDefn = new OGRFeatureDefn(pszFeatureClassName); + m_poDefn->Reference(); + CPLFree(pszFeatureClassName); + + m_bNeedTABRewrite = TRUE; + } + + + /*----------------------------------------------------------------- + * Open .DAT file (or .DBF) + *----------------------------------------------------------------*/ + if (nFnameLen > 4 && strcmp(pszTmpFname+nFnameLen-4, ".TAB")==0) + { + if (m_eTableType == TABTableDBF) + strcpy(pszTmpFname+nFnameLen-4, ".DBF"); + else // Default is NATIVE + strcpy(pszTmpFname+nFnameLen-4, ".DAT"); + } + else + { + if (m_eTableType == TABTableDBF) + strcpy(pszTmpFname+nFnameLen-4, ".dbf"); + else // Default is NATIVE + strcpy(pszTmpFname+nFnameLen-4, ".dat"); + } + +#ifndef _WIN32 + TABAdjustFilenameExtension(pszTmpFname); +#endif + + m_poDATFile = new TABDATFile; + + if ( m_poDATFile->Open(pszTmpFname, eAccess, m_eTableType) != 0) + { + // Open Failed... an error has already been reported, just return. + CPLFree(pszTmpFname); + Close(); + if (bTestOpenNoError) + CPLErrorReset(); + + return -1; + } + + m_nLastFeatureId = m_poDATFile->GetNumRecords(); + + + /*----------------------------------------------------------------- + * Parse .TAB file field defs and build FeatureDefn (only in read access) + *----------------------------------------------------------------*/ + if ( (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) && ParseTABFileFields() != 0) + { + // Failed... an error has already been reported, just return. + CPLFree(pszTmpFname); + Close(); + if (bTestOpenNoError) + CPLErrorReset(); + + return -1; + } + + + /*----------------------------------------------------------------- + * Open .MAP (and .ID) file + * Note that the .MAP and .ID files are optional. Failure to open them + * is not an error... it simply means that all features will be returned + * with NONE geometry. + *----------------------------------------------------------------*/ + if (nFnameLen > 4 && strcmp(pszTmpFname+nFnameLen-4, ".DAT")==0) + strcpy(pszTmpFname+nFnameLen-4, ".MAP"); + else + strcpy(pszTmpFname+nFnameLen-4, ".map"); + +#ifndef _WIN32 + TABAdjustFilenameExtension(pszTmpFname); +#endif + + m_poMAPFile = new TABMAPFile; + if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) + { + /*------------------------------------------------------------- + * Read access: .MAP/.ID are optional... try to open but return + * no error if files do not exist. + *------------------------------------------------------------*/ + if (m_poMAPFile->Open(pszTmpFname, eAccess, TRUE) < 0) + { + // File exists, but Open Failed... + // we have to produce an error message + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_FileIO, + "Open() failed for %s", pszTmpFname); + else + CPLErrorReset(); + + CPLFree(pszTmpFname); + Close(); + return -1; + } + + /*------------------------------------------------------------- + * Set geometry type if the geometry objects are uniform. + *------------------------------------------------------------*/ + int numPoints=0, numRegions=0, numTexts=0, numLines=0; + + GetFeatureCountByType( numPoints, numLines, numRegions, numTexts); + + numPoints += numTexts; + if( numPoints > 0 && numLines == 0 && numRegions == 0 ) + m_poDefn->SetGeomType( wkbPoint ); + else if( numPoints == 0 && numLines > 0 && numRegions == 0 ) + m_poDefn->SetGeomType( wkbLineString ); + else { + /* we leave it unknown indicating a mixture */ + } + } + else if (m_poMAPFile->Open(pszTmpFname, eAccess) != 0) + { + // Open Failed for write... + // an error has already been reported, just return. + CPLFree(pszTmpFname); + Close(); + if (bTestOpenNoError) + CPLErrorReset(); + + return -1; + } + + /*----------------------------------------------------------------- + * Initializing the attribute index (.IND) support + *----------------------------------------------------------------*/ + + CPLXMLNode *psRoot = CPLCreateXMLNode( NULL, CXT_Element, "OGRMILayerAttrIndex" ); + CPLCreateXMLElementAndValue( psRoot, "MIIDFilename", CPLResetExtension( pszFname, "IND" ) ); + OGRFeatureDefn *poLayerDefn = GetLayerDefn(); + int iField, iIndexIndex, bHasIndex = 0; + for( iField = 0; iField < poLayerDefn->GetFieldCount(); iField++ ) + { + iIndexIndex = GetFieldIndexNumber(iField); + if (iIndexIndex > 0) + { + CPLXMLNode *psIndex = CPLCreateXMLNode( psRoot, CXT_Element, "OGRMIAttrIndex" ); + CPLCreateXMLElementAndValue( psIndex, "FieldIndex", CPLSPrintf( "%d", iField ) ); + CPLCreateXMLElementAndValue( psIndex, "FieldName", + poLayerDefn->GetFieldDefn(iField)->GetNameRef() ); + CPLCreateXMLElementAndValue( psIndex, "IndexIndex", CPLSPrintf( "%d", iIndexIndex ) ); + bHasIndex = 1; + } + } + + if (bHasIndex) + { + char *pszRawXML = CPLSerializeXMLTree( psRoot ); + InitializeIndexSupport( pszRawXML ); + CPLFree( pszRawXML ); + } + + CPLDestroyXMLNode( psRoot ); + + CPLFree(pszTmpFname); + pszTmpFname = NULL; + + if( m_poDefn != NULL && m_eAccessMode != TABWrite ) + m_poDefn->GetGeomFieldDefn(0)->SetSpatialRef(GetSpatialRef()); + + return 0; +} + + +/********************************************************************** + * TABFile::ParseTABFileFirstPass() + * + * Do a first pass in the TAB header file to establish the table type, etc. + * and store any useful information into class members. + * + * This private method should be used only during the Open() call. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFile::ParseTABFileFirstPass(GBool bTestOpenNoError) +{ + int iLine, numLines, numFields = 0; + char **papszTok=NULL; + GBool bInsideTableDef = FALSE, bFoundTableFields=FALSE; + + if (m_eAccessMode == TABWrite) + { + CPLError(CE_Failure, CPLE_NotSupported, + "ParseTABFile() can be used only with Read access."); + return -1; + } + + numLines = CSLCount(m_papszTABFile); + + for(iLine=0; iLine2048 || iLine+numFields >= numLines) + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_FileIO, + "Invalid number of fields (%s) at line %d in file %s", + papszTok[1], iLine+1, m_pszFname); + + CSLDestroy(papszTok); + return -1; + } + + bInsideTableDef = FALSE; + }/* end of fields section*/ + else + { + // Simply Ignore unrecognized lines + } + } + + CSLDestroy(papszTok); + + if (m_pszCharset == NULL) + m_pszCharset = CPLStrdup("Neutral"); + + if (numFields == 0) + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_NotSupported, + "%s contains no table field definition. " + "This type of .TAB file cannot be read by this library.", + m_pszFname); + return -1; + } + + return 0; +} + +/********************************************************************** + * TABFile::ParseTABFileFields() + * + * Extract the field definition from the TAB header file, validate + * with what we have in the previously opened .DAT or .DBF file, and + * finally build the m_poDefn OGRFeatureDefn for this dataset. + * + * This private method should be used only during the Open() call and after + * ParseTABFileFirstPass() has been called. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFile::ParseTABFileFields() +{ + int iLine, numLines=0, numTok, nStatus; + char **papszTok=NULL; + OGRFieldDefn *poFieldDefn; + + if (m_eAccessMode == TABWrite) + { + CPLError(CE_Failure, CPLE_NotSupported, + "ParseTABFile() can be used only with Read access."); + return -1; + } + + char *pszFeatureClassName = TABGetBasename(m_pszFname); + m_poDefn = new OGRFeatureDefn(pszFeatureClassName); + CPLFree(pszFeatureClassName); + // Ref count defaults to 0... set it to 1 + m_poDefn->Reference(); + + /*------------------------------------------------------------- + * Scan for fields. + *------------------------------------------------------------*/ + numLines = CSLCount(m_papszTABFile); + for(iLine=0; iLine 2048 || + iLine+numFields >= numLines) + { + CPLError(CE_Failure, CPLE_FileIO, + "Invalid number of fields (%s) at line %d in file %s", + pszStr+7, iLine+1, m_pszFname); + CSLDestroy(papszTok); + return -1; + } + + // Alloc the array to keep track of indexed fields + m_panIndexNo = (int *)CPLCalloc(numFields, sizeof(int)); + + iLine++; + poFieldDefn = NULL; + for(iField=0; iField= 3 && EQUAL(papszTok[1], "char")) + { + /*------------------------------------------------- + * CHAR type + *------------------------------------------------*/ + nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, + papszTok[0], + TABFChar, + atoi(papszTok[2]), + 0); + poFieldDefn = new OGRFieldDefn(papszTok[0], OFTString); + poFieldDefn->SetWidth(atoi(papszTok[2])); + } + else if (numTok >= 2 && EQUAL(papszTok[1], "integer")) + { + /*------------------------------------------------- + * INTEGER type + *------------------------------------------------*/ + nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, + papszTok[0], + TABFInteger, + 0, + 0); + poFieldDefn = new OGRFieldDefn(papszTok[0], OFTInteger); + if( numTok > 2 && atoi(papszTok[2]) > 0 ) + poFieldDefn->SetWidth( atoi(papszTok[2]) ); + } + else if (numTok >= 2 && EQUAL(papszTok[1], "smallint")) + { + /*------------------------------------------------- + * SMALLINT type + *------------------------------------------------*/ + nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, + papszTok[0], + TABFSmallInt, + 0, + 0); + poFieldDefn = new OGRFieldDefn(papszTok[0], OFTInteger); + if( numTok > 2 && atoi(papszTok[2]) > 0 ) + poFieldDefn->SetWidth( atoi(papszTok[2]) ); + } + else if (numTok >= 4 && EQUAL(papszTok[1], "decimal")) + { + /*------------------------------------------------- + * DECIMAL type + *------------------------------------------------*/ + nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, + papszTok[0], + TABFDecimal, + atoi(papszTok[2]), + atoi(papszTok[3])); + poFieldDefn = new OGRFieldDefn(papszTok[0], OFTReal); + poFieldDefn->SetWidth(atoi(papszTok[2])); + poFieldDefn->SetPrecision(atoi(papszTok[3])); + } + else if (numTok >= 2 && EQUAL(papszTok[1], "float")) + { + /*------------------------------------------------- + * FLOAT type + *------------------------------------------------*/ + nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, + papszTok[0], + TABFFloat, + 0, 0); + poFieldDefn = new OGRFieldDefn(papszTok[0], OFTReal); + } + else if (numTok >= 2 && EQUAL(papszTok[1], "date")) + { + /*------------------------------------------------- + * DATE type (returned as a string: "DD/MM/YYYY") + *------------------------------------------------*/ + nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, + papszTok[0], + TABFDate, + 0, + 0); + poFieldDefn = new OGRFieldDefn(papszTok[0], +#ifdef MITAB_USE_OFTDATETIME + OFTDate); +#else + OFTString); +#endif + poFieldDefn->SetWidth(10); + } + else if (numTok >= 2 && EQUAL(papszTok[1], "time")) + { + /*------------------------------------------------- + * TIME type (returned as a string: "HH:MM:SS") + *------------------------------------------------*/ + nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, + papszTok[0], + TABFTime, + 0, + 0); + poFieldDefn = new OGRFieldDefn(papszTok[0], +#ifdef MITAB_USE_OFTDATETIME + OFTTime); +#else + OFTString); +#endif + poFieldDefn->SetWidth(9); + } + else if (numTok >= 2 && EQUAL(papszTok[1], "datetime")) + { + /*------------------------------------------------- + * DATETIME type (returned as a string: "DD/MM/YYYY HH:MM:SS") + *------------------------------------------------*/ + nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, + papszTok[0], + TABFDateTime, + 0, + 0); + poFieldDefn = new OGRFieldDefn(papszTok[0], +#ifdef MITAB_USE_OFTDATETIME + OFTDateTime); +#else + OFTString); +#endif + poFieldDefn->SetWidth(19); + } + else if (numTok >= 2 && EQUAL(papszTok[1], "logical")) + { + /*------------------------------------------------- + * LOGICAL type (value "T" or "F") + *------------------------------------------------*/ + nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, + papszTok[0], + TABFLogical, + 0, + 0); + poFieldDefn = new OGRFieldDefn(papszTok[0], OFTString); + poFieldDefn->SetWidth(1); + } + else + nStatus = -1; // Unrecognized field type or line corrupt + + if (nStatus != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed to parse field definition at line %d in file %s", + iLine+1, m_pszFname); + CSLDestroy(papszTok); + delete poFieldDefn; + return -1; + } + /*----------------------------------------------------- + * Keep track of index number if present + *----------------------------------------------------*/ + if (numTok >= 4 && EQUAL(papszTok[numTok-2], "index")) + { + m_panIndexNo[iField] = atoi(papszTok[numTok-1]); + } + else + { + m_panIndexNo[iField] = 0; + } + + /*----------------------------------------------------- + * Add the FieldDefn to the FeatureDefn and continue with + * the next one. + *----------------------------------------------------*/ + m_poDefn->AddFieldDefn(poFieldDefn); + // AddFieldDenf() takes a copy, so we delete the original + if (poFieldDefn) delete poFieldDefn; + poFieldDefn = NULL; + } + + /*--------------------------------------------------------- + * OK, we're done... end the loop now. + *--------------------------------------------------------*/ + break; + }/* end of fields section*/ + else + { + // Simply Ignore unrecognized lines + } + + } + + CSLDestroy(papszTok); + + if (m_poDefn->GetFieldCount() == 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "%s contains no table field definition. " + "This type of .TAB file cannot be read by this library.", + m_pszFname); + return -1; + } + + return 0; +} + +/********************************************************************** + * TABFile::WriteTABFile() + * + * Generate the .TAB file using mainly the attribute fields definition. + * + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFile::WriteTABFile() +{ + VSILFILE *fp; + + if (m_poMAPFile == NULL || m_eAccessMode == TABRead) + { + CPLError(CE_Failure, CPLE_NotSupported, + "WriteTABFile() can be used only with Write access."); + return -1; + } + if (!m_bNeedTABRewrite ) + { + return 0; + } + + // First update file version number... + int nMapObjVersion = m_poMAPFile->GetMinTABFileVersion(); + m_nVersion = MAX(m_nVersion, nMapObjVersion); + + if ( (fp = VSIFOpenL(m_pszFname, "wt")) != NULL) + { + VSIFPrintfL(fp, "!table\n"); + VSIFPrintfL(fp, "!version %d\n", m_nVersion); + VSIFPrintfL(fp, "!charset %s\n", m_pszCharset); + VSIFPrintfL(fp, "\n"); + + if (m_poDefn && m_poDefn->GetFieldCount() > 0) + { + int iField; + OGRFieldDefn *poFieldDefn; + const char *pszFieldType; + + VSIFPrintfL(fp, "Definition Table\n"); + VSIFPrintfL(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset); + VSIFPrintfL(fp, " Fields %d\n", m_poDefn->GetFieldCount()); + + for(iField=0; iFieldGetFieldCount(); iField++) + { + poFieldDefn = m_poDefn->GetFieldDefn(iField); + switch(GetNativeFieldType(iField)) + { + case TABFChar: + pszFieldType = CPLSPrintf("Char (%d)", + poFieldDefn->GetWidth()); + break; + case TABFDecimal: + pszFieldType = CPLSPrintf("Decimal (%d,%d)", + poFieldDefn->GetWidth(), + poFieldDefn->GetPrecision()); + break; + case TABFInteger: + if( poFieldDefn->GetWidth() == 0 ) + pszFieldType = "Integer"; + else + pszFieldType = CPLSPrintf("Integer (%d)", + poFieldDefn->GetWidth()); + break; + case TABFSmallInt: + if( poFieldDefn->GetWidth() == 0 ) + pszFieldType = "SmallInt"; + else + pszFieldType = CPLSPrintf("SmallInt (%d)", + poFieldDefn->GetWidth()); + break; + case TABFFloat: + pszFieldType = "Float"; + break; + case TABFLogical: + pszFieldType = "Logical"; + break; + case TABFDate: + pszFieldType = "Date"; + break; + case TABFTime: + pszFieldType = "Time"; + break; + case TABFDateTime: + pszFieldType = "DateTime"; + break; + default: + // Unsupported field type!!! This should never happen. + CPLError(CE_Failure, CPLE_AssertionFailed, + "WriteTABFile(): Unsupported field type"); + VSIFCloseL(fp); + return -1; + } + + if (GetFieldIndexNumber(iField) == 0) + { + VSIFPrintfL(fp, " %s %s ;\n", poFieldDefn->GetNameRef(), + pszFieldType ); + } + else + { + VSIFPrintfL(fp, " %s %s Index %d ;\n", + poFieldDefn->GetNameRef(), pszFieldType, + GetFieldIndexNumber(iField) ); + } + + } + } + else + { + VSIFPrintfL(fp, "Definition Table\n"); + VSIFPrintfL(fp, " Type NATIVE Charset \"%s\"\n", m_pszCharset); + VSIFPrintfL(fp, " Fields 1\n"); + VSIFPrintfL(fp, " FID Integer ;\n" ); + } + + VSIFCloseL(fp); + + m_bNeedTABRewrite = FALSE; + } + else + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed to create file `%s'", m_pszFname); + return -1; + } + + return 0; +} + +/********************************************************************** + * TABFile::Close() + * + * Close current file, and release all memory used. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFile::Close() +{ + CPLErrorReset(); + + // Commit the latest changes to the file... + + // In Write access, it's time to write the .TAB file. + if (m_eAccessMode != TABRead) + { + WriteTABFile(); + } + + if (m_poMAPFile) + { + m_poMAPFile->Close(); + delete m_poMAPFile; + m_poMAPFile = NULL; + } + + if (m_poDATFile) + { + m_poDATFile->Close(); + delete m_poDATFile; + m_poDATFile = NULL; + } + + if (m_poINDFile) + { + m_poINDFile->Close(); + delete m_poINDFile; + m_poINDFile = NULL; + } + + if (m_poCurFeature) + { + delete m_poCurFeature; + m_poCurFeature = NULL; + } + + if (m_poDefn ) + m_poDefn->Release(); + m_poDefn = NULL; + + if (m_poSpatialRef) + m_poSpatialRef->Release(); + m_poSpatialRef = NULL; + + CSLDestroy(m_papszTABFile); + m_papszTABFile = NULL; + + CPLFree(m_pszFname); + m_pszFname = NULL; + + CPLFree(m_pszCharset); + m_pszCharset = NULL; + + CPLFree(m_panIndexNo); + m_panIndexNo = NULL; + + CPLFree(m_panMatchingFIDs); + m_panMatchingFIDs = NULL; + + return 0; +} + +/********************************************************************** + * TABFile::SetQuickSpatialIndexMode() + * + * Select "quick spatial index mode". + * + * The default behavior of MITAB is to generate an optimized spatial index, + * but this results in slower write speed. + * + * Applications that want faster write speed and do not care + * about the performance of spatial queries on the resulting file can + * use SetQuickSpatialIndexMode() to require the creation of a non-optimal + * spatial index (actually emulating the type of spatial index produced + * by MITAB before version 1.6.0). In this mode writing files can be + * about 5 times faster, but spatial queries can be up to 30 times slower. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFile::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode/*=TRUE*/) +{ + if (m_eAccessMode != TABWrite || m_poMAPFile == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetQuickSpatialIndexMode() failed: file not opened for write access."); + return -1; + } + + + return m_poMAPFile->SetQuickSpatialIndexMode(bQuickSpatialIndexMode); +} + + + +/********************************************************************** + * TABFile::GetNextFeatureId() + * + * Returns feature id that follows nPrevId, or -1 if it is the + * last feature id. Pass nPrevId=-1 to fetch the first valid feature id. + **********************************************************************/ +GIntBig TABFile::GetNextFeatureId(GIntBig nPrevId) +{ + if( m_bLastOpWasWrite ) + ResetReading(); + m_bLastOpWasRead = TRUE; + + if( (GIntBig)(int)nPrevId != nPrevId ) + return -1; + + /*----------------------------------------------------------------- + * Are we using spatial rather than .ID based traversal? + *----------------------------------------------------------------*/ + if( bUseSpatialTraversal ) + return m_poMAPFile->GetNextFeatureId( (int)nPrevId ); + + /*----------------------------------------------------------------- + * Should we use an attribute index traversal? + *----------------------------------------------------------------*/ + if( m_poAttrQuery != NULL) + { + if( m_panMatchingFIDs == NULL ) + { + m_iMatchingFID = 0; + m_panMatchingFIDs = m_poAttrQuery->EvaluateAgainstIndices( this, + NULL ); + } + if( m_panMatchingFIDs != NULL ) + { + if( m_panMatchingFIDs[m_iMatchingFID] == OGRNullFID ) + return OGRNullFID; + + return m_panMatchingFIDs[m_iMatchingFID++] + 1; + } + } + + /*----------------------------------------------------------------- + * Establish what the next logical feature ID should be + *----------------------------------------------------------------*/ + int nFeatureId = -1; + + if (nPrevId <= 0 && m_nLastFeatureId > 0) + nFeatureId = 1; // Feature Ids start at 1 + else if (nPrevId > 0 && nPrevId < m_nLastFeatureId) + nFeatureId = (int)nPrevId + 1; + else + { + // This was the last feature + return OGRNullFID; + } + + /*----------------------------------------------------------------- + * Skip any feature with NONE geometry and a deleted attribute record + *----------------------------------------------------------------*/ + while(nFeatureId <= m_nLastFeatureId) + { + if ( m_poMAPFile->MoveToObjId(nFeatureId) != 0 || + m_poDATFile->GetRecordBlock(nFeatureId) == NULL ) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "GetNextFeatureId() failed: unable to set read pointer " + "to feature id %d", nFeatureId); + return -1; + } + +// __TODO__ Add a test here to check if object is deleted, +// i.e. 0x40 set on object_id in object block + if (m_poMAPFile->GetCurObjType() != TAB_GEOM_NONE || + m_poDATFile->IsCurrentRecordDeleted() == FALSE) + { + // This feature contains at least a geometry or some attributes... + // return its id. + return nFeatureId; + } + + nFeatureId++; + } + + // If we reached this point, then we kept skipping deleted features + // and stopped when EOF was reached. + return -1; +} + +/********************************************************************** + * TABFile::GetNextFeatureId_Spatial() + * + * Returns feature id that follows nPrevId, or -1 if it is the + * last feature id, but by traversing the spatial tree instead of the + * direct object index. Generally speaking the feature id's will be + * returned in an unordered fashion. + **********************************************************************/ +int TABFile::GetNextFeatureId_Spatial(int nPrevId) +{ + if (m_eAccessMode != TABRead) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GetNextFeatureId_Spatial() can be used only with Read access."); + return -1; + } + + if( m_poMAPFile == NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "GetNextFeatureId_Spatial() requires availability of .MAP file." ); + return -1; + } + + return m_poMAPFile->GetNextFeatureId( nPrevId ); +} + +/********************************************************************** + * TABFile::GetFeatureRef() + * + * Fill and return a TABFeature object for the specified feature id. + * + * The retruned pointer is a reference to an object owned and maintained + * by this TABFile object. It should not be altered or freed by the + * caller and its contents is guaranteed to be valid only until the next + * call to GetFeatureRef() or Close(). + * + * Returns NULL if the specified feature id does not exist of if an + * error happened. In any case, CPLError() will have been called to + * report the reason of the failure. + * + * If an unsupported object type is encountered (likely from a newer version + * of MapInfo) then a valid feature will be returned with a NONE geometry, + * and a warning will be produced with code TAB_WarningFeatureTypeNotSupported + * CPLGetLastErrorNo() should be used to detect that case. + **********************************************************************/ +TABFeature *TABFile::GetFeatureRef(GIntBig nFeatureId) +{ + CPLErrorReset(); + + /*----------------------------------------------------------------- + * Make sure file is opened and Validate feature id by positioning + * the read pointers for the .MAP and .DAT files to this feature id. + *----------------------------------------------------------------*/ + if (m_poMAPFile == NULL) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "GetFeatureRef() failed: file is not opened!"); + return NULL; + } + + if( m_bLastOpWasWrite ) + ResetReading(); + m_bLastOpWasRead = TRUE; + + if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId || + m_poMAPFile->MoveToObjId((int)nFeatureId) != 0 || + m_poDATFile->GetRecordBlock((int)nFeatureId) == NULL ) + { + // CPLError(CE_Failure, CPLE_IllegalArg, + // "GetFeatureRef() failed: invalid feature id %d", + // nFeatureId); + return NULL; + } + + if( m_poDATFile->IsCurrentRecordDeleted() ) + { + if( m_poMAPFile->GetCurObjType() != TAB_GEOM_NONE ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Valid .MAP record " CPL_FRMT_GIB " found, but .DAT is marked as deleted. File likely corrupt", + nFeatureId); + } + return NULL; + } + + /*----------------------------------------------------------------- + * Flush current feature object + * __TODO__ try to reuse if it is already of the right type + *----------------------------------------------------------------*/ + if (m_poCurFeature) + { + delete m_poCurFeature; + m_poCurFeature = NULL; + } + + /*----------------------------------------------------------------- + * Create new feature object of the right type + * Unsupported object types are returned as raw TABFeature (i.e. NONE + * geometry) + *----------------------------------------------------------------*/ + m_poCurFeature = TABFeature::CreateFromMapInfoType(m_poMAPFile->GetCurObjType(), + m_poDefn); + + /*----------------------------------------------------------------- + * Read fields from the .DAT file + * GetRecordBlock() has already been called above... + *----------------------------------------------------------------*/ + if (m_poCurFeature->ReadRecordFromDATFile(m_poDATFile) != 0) + { + delete m_poCurFeature; + m_poCurFeature = NULL; + return NULL; + } + + /*----------------------------------------------------------------- + * Read geometry from the .MAP file + * MoveToObjId() has already been called above... + *----------------------------------------------------------------*/ + TABMAPObjHdr *poObjHdr = + TABMAPObjHdr::NewObj(m_poMAPFile->GetCurObjType(), + m_poMAPFile->GetCurObjId()); + // Note that poObjHdr==NULL is a valid case if geometry type is NONE + + if ((poObjHdr && poObjHdr->ReadObj(m_poMAPFile->GetCurObjBlock()) != 0) || + m_poCurFeature->ReadGeometryFromMAPFile(m_poMAPFile, poObjHdr) != 0) + { + delete m_poCurFeature; + m_poCurFeature = NULL; + if (poObjHdr) + delete poObjHdr; + return NULL; + } + if (poObjHdr) // May be NULL if feature geometry type is NONE + delete poObjHdr; + + m_nCurFeatureId = nFeatureId; + m_poCurFeature->SetFID(m_nCurFeatureId); + + m_poCurFeature->SetRecordDeleted(m_poDATFile->IsCurrentRecordDeleted()); + + return m_poCurFeature; +} + +/********************************************************************** + * TABFile::DeleteFeature() + * + * Standard OGR DeleteFeature implementation. + **********************************************************************/ +OGRErr TABFile::DeleteFeature(GIntBig nFeatureId) +{ + CPLErrorReset(); + + if (m_eAccessMode == TABRead) + { + CPLError(CE_Failure, CPLE_NotSupported, + "DeleteFeature() cannot be used in read-only access."); + return OGRERR_FAILURE; + } + + /*----------------------------------------------------------------- + * Make sure file is opened and establish new feature id. + *----------------------------------------------------------------*/ + if (m_poMAPFile == NULL) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "DeleteFeature() failed: file is not opened!"); + return OGRERR_FAILURE; + } + + if( m_bLastOpWasWrite ) + ResetReading(); + + if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId || + m_poMAPFile->MoveToObjId((int)nFeatureId) != 0 || + m_poDATFile->GetRecordBlock((int)nFeatureId) == NULL ) + { + /*CPLError(CE_Failure, CPLE_IllegalArg, + "DeleteFeature() failed: invalid feature id " CPL_FRMT_GIB, + nFeatureId);*/ + return OGRERR_NON_EXISTING_FEATURE; + } + + if( m_poDATFile->IsCurrentRecordDeleted() ) + { + /*CPLError(CE_Failure, CPLE_IllegalArg, + "DeleteFeature() failed: record is already deleted!");*/ + return OGRERR_NON_EXISTING_FEATURE; + } + + if (m_poCurFeature) + { + delete m_poCurFeature; + m_poCurFeature = NULL; + } + + if( m_poMAPFile->MarkAsDeleted() != 0 || + m_poDATFile->MarkAsDeleted() != 0 ) + { + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/********************************************************************** + * TABFile::WriteFeature() + * + * Write a feature to this dataset. + * + * Returns 0 on success, or -1 if an error happened in which case, + * CPLError() will have been called to + * report the reason of the failure. + **********************************************************************/ +int TABFile::WriteFeature(TABFeature *poFeature) +{ + + m_bLastOpWasWrite = TRUE; + + /*----------------------------------------------------------------- + * Make sure file is opened and establish new feature id. + *----------------------------------------------------------------*/ + if (m_poMAPFile == NULL) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "WriteFeature() failed: file is not opened!"); + return -1; + } + + int nFeatureId; + if ( poFeature->GetFID() >= 0 ) + { + nFeatureId = (int)poFeature->GetFID(); + } + else if (m_nLastFeatureId < 1) + { + /*------------------------------------------------------------- + * Special hack to write out at least one field if none are in + * OGRFeatureDefn. + *------------------------------------------------------------*/ + if( m_poDATFile->GetNumFields() == 0 ) + { + CPLError(CE_Warning, CPLE_IllegalArg, + "MapInfo tables must contain at least 1 column, adding dummy FID column."); + CPLErrorReset(); + m_poDATFile->AddField("FID", TABFInteger, 10, 0 ); + } + + nFeatureId = 1; + } + else + { + nFeatureId = m_nLastFeatureId + 1; + } + + poFeature->SetFID(nFeatureId); + + + /*----------------------------------------------------------------- + * Write fields to the .DAT file and update .IND if necessary + *----------------------------------------------------------------*/ + if (m_poDATFile->GetRecordBlock(nFeatureId) == NULL || + poFeature->WriteRecordToDATFile(m_poDATFile, m_poINDFile, + m_panIndexNo) != 0 ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing attributes for feature id %d in %s", + nFeatureId, m_pszFname); + return -1; + } + + /*----------------------------------------------------------------- + * Write geometry to the .MAP file + * The call to PrepareNewObj() takes care of the .ID file. + *----------------------------------------------------------------*/ + TABMAPObjHdr *poObjHdr = + TABMAPObjHdr::NewObj(poFeature->ValidateMapInfoType(m_poMAPFile), + nFeatureId); + + if ( poObjHdr == NULL || m_poMAPFile == NULL ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing geometry for feature id %d in %s", + nFeatureId, m_pszFname); + if (poObjHdr) + delete poObjHdr; + return -1; + } + + /*----------------------------------------------------------------- + * ValidateMapInfoType() may have returned TAB_GEOM_NONE if feature + * contained an invalid geometry for its class. Need to catch that + * case and return the error. + *----------------------------------------------------------------*/ + if (poObjHdr->m_nType == TAB_GEOM_NONE && + poFeature->GetFeatureClass() != TABFCNoGeomFeature ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Invalid geometry for feature id %d in %s", + nFeatureId, m_pszFname); + return -1; + } + + /*----------------------------------------------------------------- + * The ValidateMapInfoType() call above has forced calculation of the + * feature's IntMBR. Store that value in the ObjHdr for use by + * PrepareNewObj() to search the best node to insert the feature. + *----------------------------------------------------------------*/ + if ( poObjHdr && poObjHdr->m_nType != TAB_GEOM_NONE) + { + poFeature->GetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY, + poObjHdr->m_nMaxX, poObjHdr->m_nMaxY); + } + +/* + if( m_nCurFeatureId < m_nLastFeatureId ) + { + delete GetFeatureRef(m_nLastFeatureId); + m_poCurFeature = NULL; + }*/ + + if ( m_poMAPFile->PrepareNewObj(poObjHdr) != 0 || + poFeature->WriteGeometryToMAPFile(m_poMAPFile, poObjHdr) != 0 || + m_poMAPFile->CommitNewObj(poObjHdr) != 0 ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing geometry for feature id %d in %s", + nFeatureId, m_pszFname); + if (poObjHdr) + delete poObjHdr; + return -1; + } + + m_nLastFeatureId = MAX(m_nLastFeatureId, nFeatureId); + m_nCurFeatureId = nFeatureId; + + delete poObjHdr; + + return 0; +} + + +/********************************************************************** + * TABFile::CreateFeature() + * + * Write a new feature to this dataset. The passed in feature is updated + * with the new feature id. + * + * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an + * error happened in which case, CPLError() will have been called to + * report the reason of the failure. + **********************************************************************/ +OGRErr TABFile::CreateFeature(TABFeature *poFeature) +{ + CPLErrorReset(); + + if (m_eAccessMode == TABRead) + { + CPLError(CE_Failure, CPLE_NotSupported, + "CreateFeature() cannot be used in read-only access."); + return OGRERR_FAILURE; + } + + GIntBig nFeatureId = poFeature->GetFID(); + if (nFeatureId != OGRNullFID ) + { + if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId ) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "CreateFeature() failed: invalid feature id " CPL_FRMT_GIB, + nFeatureId); + return OGRERR_FAILURE; + } + + if( m_poDATFile->GetRecordBlock((int)nFeatureId) == NULL || + !m_poDATFile->IsCurrentRecordDeleted() ) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "CreateFeature() failed: cannot re-write already existing feature " CPL_FRMT_GIB, + nFeatureId); + return OGRERR_FAILURE; + } + } + + if (WriteFeature(poFeature) < 0) + return OGRERR_FAILURE; + + return OGRERR_NONE; +} + +/********************************************************************** + * TABFile::ISetFeature() + * + * Implementation of OGRLayer's SetFeature() + **********************************************************************/ +OGRErr TABFile::ISetFeature( OGRFeature *poFeature ) + +{ + CPLErrorReset(); + + if (m_eAccessMode == TABRead) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() cannot be used in read-only access."); + return -1; + } + + /*----------------------------------------------------------------- + * Make sure file is opened. + *----------------------------------------------------------------*/ + if (m_poMAPFile == NULL) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "SetFeature() failed: file is not opened!"); + return OGRERR_FAILURE; + } + + GIntBig nFeatureId = poFeature->GetFID(); + if (nFeatureId == OGRNullFID ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() must be used on a feature with a FID."); + return OGRERR_FAILURE; + } + if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId ) + { + /*CPLError(CE_Failure, CPLE_IllegalArg, + "SetFeature() failed: invalid feature id " CPL_FRMT_GIB, + nFeatureId);*/ + return OGRERR_NON_EXISTING_FEATURE; + } + + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if( poGeom != NULL && + ((wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) || + (wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeature() failed: setting MultiPoint or GeometryCollection not supported"); + return OGRERR_FAILURE; + } + + TABFeature* poTABFeature = CreateTABFeature(poFeature); + if( poTABFeature == NULL ) + return OGRERR_FAILURE; + + if( m_bLastOpWasWrite ) + ResetReading(); + + if (m_poDATFile->GetRecordBlock((int)nFeatureId) == NULL ) + { + /*CPLError(CE_Failure, CPLE_IllegalArg, + "SetFeature() failed: invalid feature id " CPL_FRMT_GIB, + nFeatureId);*/ + delete poTABFeature; + return OGRERR_NON_EXISTING_FEATURE; + } + + /* If the object is not already deleted, delete it */ + if( !(m_poDATFile->IsCurrentRecordDeleted()) ) + { + OGRFeature* poOldFeature = GetFeature(nFeatureId); + if( poOldFeature != NULL ) + { + /* Optimization: if old and new features are the same, do nothing */ + if( poOldFeature->Equal(poFeature) ) + { + CPLDebug("MITAB", "Un-modified object " CPL_FRMT_GIB, nFeatureId); + delete poTABFeature; + delete poOldFeature; + return OGRERR_NONE; + } + + /* Optimization: if old and new geometries are the same, just */ + /* rewrite the attributes */ + OGRGeometry* poOldGeom = poOldFeature->GetGeometryRef(); + OGRGeometry* poNewGeom = poFeature->GetGeometryRef(); + if( (poOldGeom == NULL && poNewGeom == NULL ) || + (poOldGeom != NULL && poNewGeom != NULL && poOldGeom->Equals(poNewGeom)) ) + { + const char* pszOldStyle = poOldFeature->GetStyleString(); + const char* pszNewStyle = poFeature->GetStyleString(); + if( (pszOldStyle == NULL && pszNewStyle == NULL) || + (pszOldStyle != NULL && pszNewStyle != NULL && EQUAL(pszOldStyle, pszNewStyle)) ) + { + CPLDebug("MITAB", "Rewrite only attributes for object " CPL_FRMT_GIB, nFeatureId); + if (poTABFeature->WriteRecordToDATFile(m_poDATFile, m_poINDFile, + m_panIndexNo) != 0 ) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed writing attributes for feature id " CPL_FRMT_GIB " in %s", + nFeatureId, m_pszFname); + delete poTABFeature; + delete poOldFeature; + return OGRERR_FAILURE; + } + + delete poTABFeature; + delete poOldFeature; + return OGRERR_NONE; + } + } + + delete poOldFeature; + } + + if (DeleteFeature(nFeatureId) != OGRERR_NONE) + { + delete poTABFeature; + return OGRERR_FAILURE; + } + } + + int nStatus = WriteFeature(poTABFeature); + + delete poTABFeature; + + if (nStatus < 0) + return OGRERR_FAILURE; + + return OGRERR_NONE; +} + + +/********************************************************************** + * TABFile::GetLayerDefn() + * + * Returns a reference to the OGRFeatureDefn that will be used to create + * features in this dataset. + * + * Returns a reference to an object that is maintained by this TABFile + * object (and thus should not be modified or freed by the caller) or + * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file + * opened yet) + **********************************************************************/ +OGRFeatureDefn *TABFile::GetLayerDefn() +{ + return m_poDefn; +} + +/********************************************************************** + * TABFile::SetFeatureDefn() + * + * Pass a reference to the OGRFeatureDefn that will be used to create + * features in this dataset. This function should be called after + * creating a new dataset, but before writing the first feature. + * All features that will be written to this dataset must share this same + * OGRFeatureDefn. + * + * A reference to the OGRFeatureDefn will be kept and will be used to + * build the .DAT file, etc. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFile::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn, + TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */) +{ + int iField, numFields; + OGRFieldDefn *poFieldDefn; + TABFieldType eMapInfoType = TABFUnknown; + int nStatus = 0; + + if (m_eAccessMode != TABWrite) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetFeatureDefn() can be used only with Write access."); + return -1; + } + + /*----------------------------------------------------------------- + * Keep a reference to the OGRFeatureDefn... we'll have to take the + * reference count into account when we are done with it. + *----------------------------------------------------------------*/ + if (m_poDefn && m_poDefn->Dereference() == 0) + delete m_poDefn; + + m_poDefn = poFeatureDefn; + m_poDefn->Reference(); + + /*----------------------------------------------------------------- + * Pass field information to the .DAT file, after making sure that + * it has been created and that it does not contain any field + * definition yet. + *----------------------------------------------------------------*/ + if (m_poDATFile== NULL || m_poDATFile->GetNumFields() > 0 ) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetFeatureDefn() can be called only once in a newly " + "created dataset."); + return -1; + } + + numFields = poFeatureDefn->GetFieldCount(); + for(iField=0; nStatus==0 && iField < numFields; iField++) + { + poFieldDefn = m_poDefn->GetFieldDefn(iField); + + /*------------------------------------------------------------- + * Make sure field name is valid... check for special chars, etc. + *------------------------------------------------------------*/ + char *pszCleanName = TABCleanFieldName(poFieldDefn->GetNameRef()); + if (!EQUAL(pszCleanName, poFieldDefn->GetNameRef())) + poFieldDefn->SetName(pszCleanName); + CPLFree(pszCleanName); + pszCleanName = NULL; + + if (paeMapInfoNativeFieldTypes) + { + eMapInfoType = paeMapInfoNativeFieldTypes[iField]; + } + else + { + /*--------------------------------------------------------- + * Map OGRFieldTypes to MapInfo native types + *--------------------------------------------------------*/ + switch(poFieldDefn->GetType()) + { + case OFTInteger: + eMapInfoType = TABFInteger; + break; + case OFTReal: + if( poFieldDefn->GetWidth()>0 || poFieldDefn->GetPrecision()>0 ) + eMapInfoType = TABFDecimal; + else + eMapInfoType = TABFFloat; + break; + case OFTDateTime: + eMapInfoType = TABFDateTime; + break; + case OFTDate: + eMapInfoType = TABFDate; + break; + case OFTTime: + eMapInfoType = TABFTime; + break; + case OFTString: + default: + eMapInfoType = TABFChar; + } + } + + nStatus = m_poDATFile->AddField(poFieldDefn->GetNameRef(), + eMapInfoType, + poFieldDefn->GetWidth(), + poFieldDefn->GetPrecision()); + } + + /*----------------------------------------------------------------- + * Alloc the array to keep track of indexed fields (default=NOT indexed) + *----------------------------------------------------------------*/ + m_panIndexNo = (int *)CPLCalloc(numFields, sizeof(int)); + + return nStatus; +} + +/********************************************************************** + * TABFile::AddFieldNative() + * + * Create a new field using a native mapinfo data type... this is an + * alternative to defining fields through the OGR interface. + * This function should be called after creating a new dataset. + * + * This function will build/update the OGRFeatureDefn that will have to be + * used when writing features to this dataset. + * + * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn(). + * + * Note: The bUnique flag has no effect on TABFiles. See the TABView class. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType, + int nWidth /*=0*/, int nPrecision /*=0*/, + GBool bIndexed /*=FALSE*/, GBool /*bUnique=FALSE*/, int bApproxOK) +{ + OGRFieldDefn *poFieldDefn; + int nStatus = 0; + char *pszCleanName = NULL; + char szNewFieldName[31+1]; /* 31 is the max characters for a field name*/ + int nRenameNum = 1; + + if (m_eAccessMode == TABRead || m_poDATFile == NULL) + { + CPLError(CE_Failure, CPLE_NotSupported, + "AddFieldNative() cannot be used only with Read access."); + return -1; + } + + m_bNeedTABRewrite = TRUE; + + /*----------------------------------------------------------------- + * Validate field width... must be <= 254 + *----------------------------------------------------------------*/ + if (nWidth > 254) + { + CPLError(CE_Warning, CPLE_IllegalArg, + "Invalid size (%d) for field '%s'. " + "Size must be 254 or less.", nWidth, pszName); + nWidth=254; + } + + /*----------------------------------------------------------------- + * Map fields with width=0 (variable length in OGR) to a valid default + *----------------------------------------------------------------*/ + if (eMapInfoType == TABFDecimal && nWidth == 0) + nWidth=20; + else if (nWidth == 0) + nWidth=254; /* char fields */ + + /*----------------------------------------------------------------- + * Make sure field name is valid... check for special chars, etc. + * (pszCleanName will have to be freed.) + *----------------------------------------------------------------*/ + pszCleanName = TABCleanFieldName(pszName); + + if( !bApproxOK && + ( m_poDefn->GetFieldIndex(pszCleanName) >= 0 || + !EQUAL(pszName, pszCleanName) ) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Failed to add field named '%s'", + pszName ); + } + + strncpy(szNewFieldName, pszCleanName, 31); + szNewFieldName[31] = '\0'; + + while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 10) + sprintf( szNewFieldName, "%.29s_%.1d", pszCleanName, nRenameNum++ ); + + while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 100) + sprintf( szNewFieldName, "%.29s%.2d", pszCleanName, nRenameNum++ ); + + if (m_poDefn->GetFieldIndex(szNewFieldName) >= 0) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Too many field names like '%s' when truncated to 31 letters " + "for MapInfo format.", pszCleanName ); + } + + if( !EQUAL(pszCleanName,szNewFieldName) ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Normalized/laundered field name: '%s' to '%s'", + pszCleanName, + szNewFieldName ); + } + + /*----------------------------------------------------------------- + * Map MapInfo native types to OGR types + *----------------------------------------------------------------*/ + poFieldDefn = NULL; + + switch(eMapInfoType) + { + case TABFChar: + /*------------------------------------------------- + * CHAR type + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString); + poFieldDefn->SetWidth(nWidth); + break; + case TABFInteger: + /*------------------------------------------------- + * INTEGER type + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger); + if (nWidth <= 10) + poFieldDefn->SetWidth(nWidth); + break; + case TABFSmallInt: + /*------------------------------------------------- + * SMALLINT type + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger); + if (nWidth <= 5) + poFieldDefn->SetWidth(nWidth); + break; + case TABFDecimal: + /*------------------------------------------------- + * DECIMAL type + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal); + poFieldDefn->SetWidth(nWidth); + poFieldDefn->SetPrecision(nPrecision); + break; + case TABFFloat: + /*------------------------------------------------- + * FLOAT type + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal); + break; + case TABFDate: + /*------------------------------------------------- + * DATE type (V450, returned as a string: "DD/MM/YYYY") + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, +#ifdef MITAB_USE_OFTDATETIME + OFTDate); +#else + OFTString); +#endif + poFieldDefn->SetWidth(10); + m_nVersion = MAX(m_nVersion, 450); + break; + case TABFTime: + /*------------------------------------------------- + * TIME type (V900, returned as a string: "HH:MM:SS") + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, +#ifdef MITAB_USE_OFTDATETIME + OFTTime); +#else + OFTString); +#endif + poFieldDefn->SetWidth(8); + m_nVersion = MAX(m_nVersion, 900); + break; + case TABFDateTime: + /*------------------------------------------------- + * DATETIME type (V900, returned as a string: "DD/MM/YYYY HH:MM:SS") + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, +#ifdef MITAB_USE_OFTDATETIME + OFTDateTime); +#else + OFTString); +#endif + poFieldDefn->SetWidth(19); + m_nVersion = MAX(m_nVersion, 900); + break; + case TABFLogical: + /*------------------------------------------------- + * LOGICAL type (value "T" or "F") + *------------------------------------------------*/ + poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString); + poFieldDefn->SetWidth(1); + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported type for field %s", szNewFieldName); + CPLFree(pszCleanName); + return -1; + } + + /*----------------------------------------------------- + * Add the FieldDefn to the FeatureDefn + *----------------------------------------------------*/ + m_poDefn->AddFieldDefn(poFieldDefn); + delete poFieldDefn; + + /*----------------------------------------------------- + * ... and pass field info to the .DAT file. + *----------------------------------------------------*/ + nStatus = m_poDATFile->AddField(szNewFieldName, eMapInfoType, + nWidth, nPrecision); + + /*----------------------------------------------------------------- + * Extend the array to keep track of indexed fields (default=NOT indexed) + *----------------------------------------------------------------*/ + m_panIndexNo = (int *)CPLRealloc(m_panIndexNo, + m_poDefn->GetFieldCount()*sizeof(int)); + m_panIndexNo[m_poDefn->GetFieldCount()-1] = 0; + + /*----------------------------------------------------------------- + * Index the field if requested + *----------------------------------------------------------------*/ + if (nStatus == 0 && bIndexed) + nStatus = SetFieldIndexed(m_poDefn->GetFieldCount()-1); + + if (nStatus == 0 && m_eAccessMode == TABReadWrite) + nStatus = WriteTABFile(); + + CPLFree(pszCleanName); + return nStatus; +} + + +/********************************************************************** + * TABFile::GetNativeFieldType() + * + * Returns the native MapInfo field type for the specified field. + * + * Returns TABFUnknown if file is not opened, or if specified field index is + * invalid. + * + * Note that field ids are positive and start at 0. + **********************************************************************/ +TABFieldType TABFile::GetNativeFieldType(int nFieldId) +{ + if (m_poDATFile) + { + return m_poDATFile->GetFieldType(nFieldId); + } + return TABFUnknown; +} + + + +/********************************************************************** + * TABFile::GetFieldIndexNumber() + * + * Returns the field's index number that was specified in the .TAB header + * or 0 if the specified field is not indexed. + * + * Note that field ids are positive and start at 0 + * and valid index ids are positive and start at 1. + **********************************************************************/ +int TABFile::GetFieldIndexNumber(int nFieldId) +{ + if (m_panIndexNo == NULL || nFieldId < 0 || + m_poDATFile== NULL || nFieldId >= m_poDATFile->GetNumFields()) + return 0; // no index + + return m_panIndexNo[nFieldId]; +} + +/************************************************************************ + * TABFile::SetFieldIndexed() + * + * Request that a field be indexed. This will create the .IND file if + * necessary, etc. + * + * Note that field ids are positive and start at 0. + * + * Returns 0 on success, -1 on error. + ************************************************************************/ +int TABFile::SetFieldIndexed( int nFieldId ) +{ + /*----------------------------------------------------------------- + * Make sure things are OK + *----------------------------------------------------------------*/ + if (m_pszFname == NULL || m_eAccessMode != TABWrite || m_poDefn == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetFieldIndexed() must be called after opening a new " + "dataset, but before writing the first feature to it."); + return -1; + } + + if (m_panIndexNo == NULL || nFieldId < 0 || + m_poDATFile== NULL || nFieldId >= m_poDATFile->GetNumFields()) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Invalid field number in SetFieldIndexed()."); + return -1; + } + + /*----------------------------------------------------------------- + * If field is already indexed then just return + *----------------------------------------------------------------*/ + if (m_panIndexNo[nFieldId] != 0) + return 0; // Nothing to do + + + /*----------------------------------------------------------------- + * Create .IND file if it's not done yet. + * + * Note: We can pass the .TAB's filename directly and the + * TABINDFile class will automagically adjust the extension. + *----------------------------------------------------------------*/ + if (m_poINDFile == NULL) + { + m_poINDFile = new TABINDFile; + + if ( m_poINDFile->Open(m_pszFname, "w", TRUE) != 0) + { + // File could not be opened... + delete m_poINDFile; + m_poINDFile = NULL; + return -1; + } + } + + /*----------------------------------------------------------------- + * Init new index. + *----------------------------------------------------------------*/ + int nNewIndexNo; + OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(nFieldId); + + if (poFieldDefn == NULL || + (nNewIndexNo = m_poINDFile->CreateIndex(GetNativeFieldType(nFieldId), + poFieldDefn->GetWidth()) ) < 1) + { + // Failed... an error has already been reported. + return -1; + } + + m_panIndexNo[nFieldId] = nNewIndexNo; + + return 0; +} + +/************************************************************************ + * TABFile::IsFieldIndexed() + * + * Returns TRUE if field is indexed, or FALSE otherwise. + ************************************************************************/ +GBool TABFile::IsFieldIndexed( int nFieldId ) +{ + return (GetFieldIndexNumber(nFieldId) > 0 ? TRUE:FALSE); +} + + + +/********************************************************************** + * TABFile::GetINDFileRef() + * + * Opens the .IND file for this dataset and returns a reference to + * the handle. + * If the .IND file has already been opened then the same handle is + * returned directly. + * If the .IND file does not exist then the function silently returns NULL. + * + * Note that the returned TABINDFile handle is only a reference to an + * object that is owned by this class. Callers can use it but cannot + * destroy the object. The object will remain valid for as long as + * the TABFile will remain open. + **********************************************************************/ +TABINDFile *TABFile::GetINDFileRef() +{ + if (m_pszFname == NULL) + return NULL; + + if (m_eAccessMode == TABRead && m_poINDFile == NULL) + { + /*------------------------------------------------------------- + * File is not opened yet... do it now. + * + * Note: We can pass the .TAB's filename directly and the + * TABINDFile class will automagically adjust the extension. + *------------------------------------------------------------*/ + m_poINDFile = new TABINDFile; + + if ( m_poINDFile->Open(m_pszFname, "r", TRUE) != 0) + { + // File could not be opened... probably does not exist + delete m_poINDFile; + m_poINDFile = NULL; + } + else if (m_panIndexNo && m_poDATFile) + { + /*--------------------------------------------------------- + * Pass type information for each indexed field. + *--------------------------------------------------------*/ + for(int i=0; iGetNumFields(); i++) + { + if (m_panIndexNo[i] > 0) + { + m_poINDFile->SetIndexFieldType(m_panIndexNo[i], + GetNativeFieldType(i)); + } + } + } + } + + return m_poINDFile; +} + + +/********************************************************************** + * TABFile::SetBounds() + * + * Set projection coordinates bounds of the newly created dataset. + * + * This function must be called after creating a new dataset and before any + * feature can be written to it. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFile::SetBounds(double dXMin, double dYMin, + double dXMax, double dYMax) +{ + if (m_eAccessMode != TABWrite) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetBounds() can be used only with Write access."); + return -1; + } + + /*----------------------------------------------------------------- + * Check that dataset has been created but no feature set yet. + *----------------------------------------------------------------*/ + if (m_poMAPFile && m_nLastFeatureId < 1) + { + m_poMAPFile->SetCoordsysBounds(dXMin, dYMin, dXMax, dYMax); + + m_bBoundsSet = TRUE; + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetBounds() can be called only after dataset has been " + "created and before any feature is set."); + return -1; + } + + return 0; +} + + +/********************************************************************** + * TABFile::GetBounds() + * + * Fetch projection coordinates bounds of a dataset. + * + * The bForce flag has no effect on TAB files since the bounds are + * always in the header. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFile::GetBounds(double &dXMin, double &dYMin, + double &dXMax, double &dYMax, + GBool /*bForce = TRUE*/) +{ + TABMAPHeaderBlock *poHeader; + + if (m_poMAPFile && (poHeader=m_poMAPFile->GetHeaderBlock()) != NULL) + { + /*------------------------------------------------------------- + * Projection bounds correspond to the +/- 1e9 integer coord. limits + *------------------------------------------------------------*/ + double dX0, dX1, dY0, dY1; + m_poMAPFile->Int2Coordsys(-1000000000, -1000000000, + dX0, dY0); + m_poMAPFile->Int2Coordsys(1000000000, 1000000000, + dX1, dY1); + /*------------------------------------------------------------- + * ... and make sure that Min < Max + *------------------------------------------------------------*/ + dXMin = MIN(dX0, dX1); + dXMax = MAX(dX0, dX1); + dYMin = MIN(dY0, dY1); + dYMax = MAX(dY0, dY1); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "GetBounds() can be called only after dataset has been opened."); + return -1; + } + + return 0; +} + + +/********************************************************************** + * TABFile::GetExtent() + * + * Fetch extent of the data currently stored in the dataset. + * + * The bForce flag has no effect on TAB files since that value is + * always in the header. + * + * Returns OGRERR_NONE/OGRRERR_FAILURE. + **********************************************************************/ +OGRErr TABFile::GetExtent (OGREnvelope *psExtent, + CPL_UNUSED int bForce) +{ + TABMAPHeaderBlock *poHeader; + + if (m_poMAPFile && (poHeader=m_poMAPFile->GetHeaderBlock()) != NULL) + { + double dX0, dX1, dY0, dY1; + /*------------------------------------------------------------- + * Fetch extent of the data from the .map header block + * this value is different from the projection bounds. + *------------------------------------------------------------*/ + m_poMAPFile->Int2Coordsys(poHeader->m_nXMin, poHeader->m_nYMin, + dX0, dY0); + m_poMAPFile->Int2Coordsys(poHeader->m_nXMax, poHeader->m_nYMax, + dX1, dY1); + + /*------------------------------------------------------------- + * ... and make sure that Min < Max + *------------------------------------------------------------*/ + psExtent->MinX = MIN(dX0, dX1); + psExtent->MaxX = MAX(dX0, dX1); + psExtent->MinY = MIN(dY0, dY1); + psExtent->MaxY = MAX(dY0, dY1); + + return OGRERR_NONE; + } + + return OGRERR_FAILURE; +} + +/********************************************************************** + * TABFile::GetFeatureCountByType() + * + * Return number of features of each type. + * + * Note that the sum of the 4 returned values may be different from + * the total number of features since features with NONE geometry + * are not taken into account here. + * + * Note: the bForce flag has nmo effect on .TAB files since the info + * is always in the header. + * + * Returns 0 on success, or silently returns -1 (with no error) if this + * information is not available. + **********************************************************************/ +int TABFile::GetFeatureCountByType(int &numPoints, int &numLines, + int &numRegions, int &numTexts, + GBool /* bForce = TRUE*/ ) +{ + TABMAPHeaderBlock *poHeader; + + if (m_poMAPFile && (poHeader=m_poMAPFile->GetHeaderBlock()) != NULL) + { + numPoints = poHeader->m_numPointObjects; + numLines = poHeader->m_numLineObjects; + numRegions = poHeader->m_numRegionObjects; + numTexts = poHeader->m_numTextObjects; + } + else + { + numPoints = numLines = numRegions = numTexts = 0; + return -1; + } + + return 0; +} + + +/********************************************************************** + * TABFile::SetMIFCoordSys() + * + * Set projection for a new file using a MIF coordsys string. + * + * This function must be called after creating a new dataset and before any + * feature can be written to it. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFile::SetMIFCoordSys(const char *pszMIFCoordSys) +{ + if (m_eAccessMode != TABWrite) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetMIFCoordSys() can be used only with Write access."); + return -1; + } + + /*----------------------------------------------------------------- + * Check that dataset has been created but no feature set yet. + *----------------------------------------------------------------*/ + if (m_poMAPFile && m_nLastFeatureId < 1) + { + OGRSpatialReference *poSpatialRef; + + poSpatialRef = MITABCoordSys2SpatialRef( pszMIFCoordSys ); + + if (poSpatialRef) + { + double dXMin, dYMin, dXMax, dYMax; + if (SetSpatialRef(poSpatialRef) == 0) + { + if (MITABExtractCoordSysBounds(pszMIFCoordSys, + dXMin, dYMin, + dXMax, dYMax) == TRUE) + { + // If the coordsys string contains bounds, then use them + if (SetBounds(dXMin, dYMin, dXMax, dYMax) != 0) + { + // Failed Setting Bounds... an error should have + // been already reported. + return -1; + } + } + } + else + { + // Failed setting poSpatialRef... and error should have + // been reported. + return -1; + } + + // Release our handle on poSpatialRef + if( poSpatialRef->Dereference() == 0 ) + delete poSpatialRef; + } + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetMIFCoordSys() can be called only after dataset has been " + "created and before any feature is set."); + return -1; + } + + return 0; +} + +/********************************************************************** + * TABFile::SetProjInfo() + * + * Set projection for a new file using a TABProjInfo structure. + * + * This function must be called after creating a new dataset and before any + * feature can be written to it. + * + * This call will also trigger a lookup of default bounds for the specified + * projection (except nonearth), and reset the m_bBoundsValid flag. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABFile::SetProjInfo(TABProjInfo *poPI) +{ + if (m_eAccessMode != TABWrite) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SetProjInfo() can be used only with Write access."); + return -1; + } + + /*----------------------------------------------------------------- + * Lookup default bounds and reset m_bBoundsSet flag + *----------------------------------------------------------------*/ + double dXMin, dYMin, dXMax, dYMax; + + m_bBoundsSet = FALSE; + if (MITABLookupCoordSysBounds(poPI, dXMin, dYMin, dXMax, dYMax) == TRUE) + { + SetBounds(dXMin, dYMin, dXMax, dYMax); + } + + /*----------------------------------------------------------------- + * Check that dataset has been created but no feature set yet. + *----------------------------------------------------------------*/ + if (m_poMAPFile && m_nLastFeatureId < 1) + { + if (m_poMAPFile->GetHeaderBlock()->SetProjInfo( poPI ) != 0) + return -1; + } + else + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetProjInfo() can be called only after dataset has been " + "created and before any feature is set."); + return -1; + } + + return 0; +} + + +/************************************************************************/ +/* DeleteField() */ +/************************************************************************/ + +OGRErr TABFile::DeleteField( int iField ) +{ + if( m_poDATFile == NULL || !TestCapability(OLCDeleteField) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "DeleteField"); + return OGRERR_FAILURE; + } + + if (iField < 0 || iField >= m_poDefn->GetFieldCount()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + + if ( m_poDATFile->DeleteField( iField ) == 0 ) + { + m_bNeedTABRewrite = TRUE; + + /* Delete from the array of indexed fields */ + if( iField < m_poDefn->GetFieldCount() - 1 ) + { + memmove(m_panIndexNo + iField, m_panIndexNo + iField + 1, + (m_poDefn->GetFieldCount() - 1 - iField) * sizeof(int)); + } + + m_poDefn->DeleteFieldDefn( iField ); + + if (m_eAccessMode == TABReadWrite) + WriteTABFile(); + + return OGRERR_NONE; + } + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* ReorderFields() */ +/************************************************************************/ + +OGRErr TABFile::ReorderFields( int* panMap ) +{ + if( m_poDATFile == NULL || !TestCapability(OLCDeleteField) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "ReorderFields"); + return OGRERR_FAILURE; + } + if (m_poDefn->GetFieldCount() == 0) + return OGRERR_NONE; + + OGRErr eErr = OGRCheckPermutation(panMap, m_poDefn->GetFieldCount()); + if (eErr != OGRERR_NONE) + return eErr; + + if ( m_poDATFile->ReorderFields( panMap ) == 0 ) + { + m_bNeedTABRewrite = TRUE; + + int* panNewIndexedField = (int*) CPLMalloc(sizeof(int)*m_poDefn->GetFieldCount()); + for(int i=0;iGetFieldCount();i++) + { + panNewIndexedField[i] = m_panIndexNo[panMap[i]]; + } + CPLFree(m_panIndexNo); + m_panIndexNo = panNewIndexedField; + + m_poDefn->ReorderFieldDefns( panMap ); + + if (m_eAccessMode == TABReadWrite) + WriteTABFile(); + + return OGRERR_NONE; + } + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* AlterFieldDefn() */ +/************************************************************************/ + +OGRErr TABFile::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) +{ + if( m_poDATFile == NULL || !TestCapability(OLCDeleteField) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "AlterFieldDefn"); + return OGRERR_FAILURE; + } + + if (iField < 0 || iField >= m_poDefn->GetFieldCount()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + + if ( m_poDATFile->AlterFieldDefn( iField, poNewFieldDefn, nFlags ) == 0 ) + { + m_bNeedTABRewrite = TRUE; + + OGRFieldDefn* poFieldDefn = m_poDefn->GetFieldDefn(iField); + if ((nFlags & ALTER_TYPE_FLAG) && + poNewFieldDefn->GetType() != poFieldDefn->GetType()) + { + poFieldDefn->SetType(poNewFieldDefn->GetType()); + if( (nFlags & ALTER_WIDTH_PRECISION_FLAG) == 0 ) + poFieldDefn->SetWidth(254); + } + if (nFlags & ALTER_NAME_FLAG) + poFieldDefn->SetName(poNewFieldDefn->GetNameRef()); + if ((nFlags & ALTER_WIDTH_PRECISION_FLAG) && + poFieldDefn->GetType() == OFTString) + { + poFieldDefn->SetWidth(m_poDATFile->GetFieldWidth(iField)); + } + + if (m_eAccessMode == TABReadWrite) + WriteTABFile(); + + return OGRERR_NONE; + } + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr TABFile::SyncToDisk() +{ + /* Silently return */ + if( m_eAccessMode == TABRead ) + return OGRERR_NONE; + + if( WriteTABFile() != 0 ) + return OGRERR_FAILURE; + + if( m_poMAPFile->SyncToDisk() != 0 ) + return OGRERR_FAILURE; + + if( m_poDATFile->SyncToDisk() != 0 ) + return OGRERR_FAILURE; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int TABFile::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) ) + return m_eAccessMode != TABRead; + + else if( EQUAL(pszCap,OLCRandomWrite) ) + return m_eAccessMode != TABRead; + + else if( EQUAL(pszCap,OLCDeleteFeature) ) + return m_eAccessMode != TABRead; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return m_poFilterGeom == NULL + && m_poAttrQuery == NULL; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return TRUE; + + else if( EQUAL(pszCap,OLCCreateField) ) + return m_eAccessMode != TABRead; + + else if( EQUAL(pszCap,OLCDeleteField) ) + return m_eAccessMode != TABRead; + + else if( EQUAL(pszCap,OLCReorderFields) ) + return m_eAccessMode != TABRead; + + else if( EQUAL(pszCap,OLCAlterFieldDefn) ) + return m_eAccessMode != TABRead; + + else + return FALSE; +} + +/********************************************************************** + * TABFile::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABFile::Dump(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABFile::Dump() -----\n"); + + if (m_poMAPFile == NULL) + { + fprintf(fpOut, "File is not opened.\n"); + } + else + { + fprintf(fpOut, "File is opened: %s\n", m_pszFname); + fprintf(fpOut, "Associated TABLE file ...\n\n"); + m_poDATFile->Dump(fpOut); + fprintf(fpOut, "... end of TABLE file dump.\n\n"); + if( GetSpatialRef() != NULL ) + { + char *pszWKT; + + GetSpatialRef()->exportToWkt( &pszWKT ); + fprintf( fpOut, "SRS = %s\n", pszWKT ); + OGRFree( pszWKT ); + } + fprintf(fpOut, "Associated .MAP file ...\n\n"); + m_poMAPFile->Dump(fpOut); + fprintf(fpOut, "... end of .MAP file dump.\n\n"); + + } + + fflush(fpOut); +} + +#endif // DEBUG diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_tabseamless.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_tabseamless.cpp new file mode 100644 index 000000000..fa40e1841 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_tabseamless.cpp @@ -0,0 +1,890 @@ +/********************************************************************** + * $Id: mitab_tabseamless.cpp,v 1.10 2010-07-07 19:00:15 aboudreault Exp $ + * + * Name: mitab_tabseamless.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABSeamless class, used to handle seamless + * .TAB datasets. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2004, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_tabseamless.cpp,v $ + * Revision 1.10 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.9 2009-03-10 13:50:02 aboudreault + * Fixed Overflow of FIDs in Seamless tables (bug 2015) + * + * Revision 1.8 2009-03-04 21:22:44 dmorissette + * Set m_nCurFeatureId=-1 in TABSeamless::ResetReading() (bug 2017) + * + * Revision 1.7 2007-06-21 14:00:23 dmorissette + * Added missing cast in isspace() calls to avoid failed assertion on Windows + * (MITAB bug 1737, GDAL ticket 1678)) + * + * Revision 1.6 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.5 2004/03/12 16:29:05 dmorissette + * Fixed 2 memory leaks (bug 283) + * + * Revision 1.4 2002/06/28 18:32:37 julien + * Add SetSpatialFilter() in TABSeamless class (Bug 164, MapServer) + * Use double for comparison in Coordsys2Int() in mitab_mapheaderblock.cpp + * + * Revision 1.3 2001/09/19 14:21:36 daniel + * On Unix: replace '\\' in file path read from tab index with '/' + * + * Revision 1.2 2001/03/15 03:57:51 daniel + * Added implementation for new OGRLayer::GetExtent(), returning data MBR. + * + * Revision 1.1 2001/03/09 04:38:04 danmo + * Update from master - version 1.1.0 + * + * Revision 1.1 2001/03/09 04:16:02 daniel + * Added TABSeamless for reading seamless TAB files + * + **********************************************************************/ + +#include "mitab.h" +#include "mitab_utils.h" + +#include /* isspace() */ + +/*===================================================================== + * class TABSeamless + * + * Support for seamless vector datasets. + * + * The current implementation has some limitations (base assumptions): + * - Read-only + * - Base tables can only be of type TABFile + * - Feature Ids are build using the id of the base table in the main + * index table (upper 32 bits) and the actual feature id of each object + * inside the base tables (lower 32 bits). + * - Only relative paths are supported for base tables names. + * + *====================================================================*/ + + +/********************************************************************** + * TABSeamless::TABSeamless() + * + * Constructor. + **********************************************************************/ +TABSeamless::TABSeamless() +{ + m_pszFname = NULL; + m_pszPath = NULL; + m_eAccessMode = TABRead; + m_poFeatureDefnRef = NULL; + m_poCurFeature = NULL; + m_nCurFeatureId = -1; + + m_poIndexTable = NULL; + m_nTableNameField = -1; + m_nCurBaseTableId = -1; + m_poCurBaseTable = NULL; + m_bEOF = FALSE; +} + +/********************************************************************** + * TABSeamless::~TABSeamless() + * + * Destructor. + **********************************************************************/ +TABSeamless::~TABSeamless() +{ + Close(); +} + + +void TABSeamless::ResetReading() +{ + if (m_poIndexTable) + OpenBaseTable(-1); // Asking for first table resets everything + + // Reset m_nCurFeatureId so that next pass via GetNextFeatureId() + // will start from the beginning + m_nCurFeatureId = -1; +} + + +/********************************************************************** + * TABSeamless::Open() + * + * Open a seamless .TAB dataset and initialize the structures to be ready + * to read features from it. + * + * Seamless .TAB files are composed of a main .TAB file in which each + * feature is the MBR of a base table. + * + * Set bTestOpenNoError=TRUE to silently return -1 with no error message + * if the file cannot be opened. This is intended to be used in the + * context of a TestOpen() function. The default value is FALSE which + * means that an error is reported if the file cannot be opened. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABSeamless::Open(const char *pszFname, TABAccess eAccess, + GBool bTestOpenNoError /*= FALSE*/ ) +{ + char nStatus = 0; + + if (m_poIndexTable) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Open() failed: object already contains an open file"); + return -1; + } + + /*----------------------------------------------------------------- + * Validate access mode and call the right open method + *----------------------------------------------------------------*/ + if (eAccess == TABRead) + { + m_eAccessMode = TABRead; + nStatus = (char)OpenForRead(pszFname, bTestOpenNoError); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Open() failed: access mode \"%d\" not supported", eAccess); + return -1; + } + + return nStatus; +} + + +/********************************************************************** + * TABSeamless::OpenForRead() + * + * Open for reading + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABSeamless::OpenForRead(const char *pszFname, + GBool bTestOpenNoError /*= FALSE*/ ) +{ + int nFnameLen = 0; + + m_eAccessMode = TABRead; + + /*----------------------------------------------------------------- + * Read main .TAB (text) file + *----------------------------------------------------------------*/ + m_pszFname = CPLStrdup(pszFname); + +#ifndef _WIN32 + /*----------------------------------------------------------------- + * On Unix, make sure extension uses the right cases + * We do it even for write access because if a file with the same + * extension already exists we want to overwrite it. + *----------------------------------------------------------------*/ + TABAdjustFilenameExtension(m_pszFname); +#endif + + /*----------------------------------------------------------------- + * Open .TAB file... since it's a small text file, we will just load + * it as a stringlist in memory. + *----------------------------------------------------------------*/ + char **papszTABFile = TAB_CSLLoad(m_pszFname); + if (papszTABFile == NULL) + { + if (!bTestOpenNoError) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed opening %s.", m_pszFname); + } + + CPLFree(m_pszFname); + CSLDestroy(papszTABFile); + return -1; + } + + /*------------------------------------------------------------- + * Look for a metadata line with "\IsSeamless" = "TRUE". + * If there is no such line, then we may have a valid .TAB file, + * but we do not support it in this class. + *------------------------------------------------------------*/ + GBool bSeamlessFound = FALSE; + for (int i=0; !bSeamlessFound && papszTABFile && papszTABFile[i]; i++) + { + const char *pszStr = papszTABFile[i]; + while(*pszStr != '\0' && isspace((unsigned char)*pszStr)) + pszStr++; + if (EQUALN(pszStr, "\"\\IsSeamless\" = \"TRUE\"", 21)) + bSeamlessFound = TRUE; + } + CSLDestroy(papszTABFile); + + if ( !bSeamlessFound ) + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_NotSupported, + "%s does not appear to be a Seamless TAB File. " + "This type of .TAB file cannot be read by this library.", + m_pszFname); + else + CPLErrorReset(); + + CPLFree(m_pszFname); + + return -1; + } + + /*----------------------------------------------------------------- + * OK, this appears to be a valid seamless TAB dataset... + * Extract the path component from the main .TAB filename + * to build the filename of the base tables + *----------------------------------------------------------------*/ + m_pszPath = CPLStrdup(m_pszFname); + nFnameLen = strlen(m_pszPath); + for( ; nFnameLen > 0; nFnameLen--) + { + if (m_pszPath[nFnameLen-1] == '/' || + m_pszPath[nFnameLen-1] == '\\' ) + { + break; + } + m_pszPath[nFnameLen-1] = '\0'; + } + + /*----------------------------------------------------------------- + * Open the main Index table and look for the "Table" field that + * should contain the path to the base table for each rectangle MBR + *----------------------------------------------------------------*/ + m_poIndexTable = new TABFile; + if (m_poIndexTable->Open(m_pszFname, m_eAccessMode, bTestOpenNoError) != 0) + { + // Open Failed... an error has already been reported, just return. + if (bTestOpenNoError) + CPLErrorReset(); + Close(); + return -1; + } + + OGRFeatureDefn *poDefn = m_poIndexTable->GetLayerDefn(); + if (poDefn == NULL || + (m_nTableNameField = poDefn->GetFieldIndex("Table")) == -1) + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_NotSupported, + "Open Failed: Field 'Table' not found in Seamless " + "Dataset '%s'. This is type of file not currently " + "supported.", + m_pszFname); + Close(); + return -1; + } + + /*----------------------------------------------------------------- + * We need to open the first table to get its FeatureDefn + *----------------------------------------------------------------*/ + if (OpenBaseTable(-1, bTestOpenNoError) != 0 ) + { + // Open Failed... an error has already been reported, just return. + if (bTestOpenNoError) + CPLErrorReset(); + Close(); + return -1; + } + + CPLAssert(m_poCurBaseTable); + m_poFeatureDefnRef = m_poCurBaseTable->GetLayerDefn(); + m_poFeatureDefnRef->Reference(); + + return 0; +} + + +/********************************************************************** + * TABSeamless::Close() + * + * Close current file, and release all memory used. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABSeamless::Close() +{ + if (m_poIndexTable) + delete m_poIndexTable; // Automatically closes. + m_poIndexTable = NULL; + + if (m_poFeatureDefnRef ) + m_poFeatureDefnRef->Release(); + m_poFeatureDefnRef = NULL; + + if (m_poCurFeature) + delete m_poCurFeature; + m_poCurFeature = NULL; + m_nCurFeatureId = -1; + + CPLFree(m_pszFname); + m_pszFname = NULL; + + CPLFree(m_pszPath); + m_pszPath = NULL; + + m_nTableNameField = -1; + m_nCurBaseTableId = -1; + + if (m_poCurBaseTable) + delete m_poCurBaseTable; + m_poCurBaseTable = NULL; + + return 0; +} + +/********************************************************************** + * TABSeamless::OpenBaseTable() + * + * Open the base table for specified IndexFeature. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABSeamless::OpenBaseTable(TABFeature *poIndexFeature, + GBool bTestOpenNoError /*=FALSE*/) +{ + CPLAssert(poIndexFeature); + + /*----------------------------------------------------------------- + * Fetch table id. We actually use the index feature's ids as the + * base table ids. + *----------------------------------------------------------------*/ + GIntBig nTableId64 = poIndexFeature->GetFID(); + int nTableId = (int)nTableId64; + CPLAssert((GIntBig)nTableId == nTableId64); + + if (m_nCurBaseTableId == nTableId && m_poCurBaseTable != NULL) + { + // The right table is already opened. Not much to do! + m_poCurBaseTable->ResetReading(); + return 0; + } + + // Close current base table + if (m_poCurBaseTable) + delete m_poCurBaseTable; + m_nCurBaseTableId = -1; + + m_bEOF = FALSE; + + /*----------------------------------------------------------------- + * Build full path to the table and open it. + * __TODO__ For now we assume that all table filename paths are relative + * but we may have to deal with absolute filenames as well. + *----------------------------------------------------------------*/ + const char *pszName = poIndexFeature->GetFieldAsString(m_nTableNameField); + char *pszFname = CPLStrdup(CPLSPrintf("%s%s", m_pszPath, pszName)); + +#ifndef _WIN32 + // On Unix, replace any '\\' in path with '/' + char *pszPtr = pszFname; + while((pszPtr = strchr(pszPtr, '\\')) != NULL) + { + *pszPtr = '/'; + pszPtr++; + } +#endif + + m_poCurBaseTable = new TABFile; + if (m_poCurBaseTable->Open(pszFname, m_eAccessMode, bTestOpenNoError) != 0) + { + // Open Failed... an error has already been reported, just return. + if (bTestOpenNoError) + CPLErrorReset(); + delete m_poCurBaseTable; + m_poCurBaseTable = NULL; + CPLFree(pszFname); + return -1; + } + + // Set the spatial filter to the new table + if( m_poFilterGeom != NULL && m_poCurBaseTable ) + { + m_poCurBaseTable->SetSpatialFilter( m_poFilterGeom ); + } + + m_nCurBaseTableId = nTableId; + CPLFree(pszFname); + + return 0; +} + +/********************************************************************** + * TABSeamless::OpenBaseTable() + * + * Open the base table for specified IndexFeature. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABSeamless::OpenBaseTable(int nTableId, GBool bTestOpenNoError /*=FALSE*/) +{ + + if (nTableId == -1) + { + // Open first table from dataset + m_poIndexTable->ResetReading(); + if (OpenNextBaseTable(bTestOpenNoError) != 0) + { + // Open Failed... an error has already been reported. + if (bTestOpenNoError) + CPLErrorReset(); + return -1; + } + } + else if (nTableId == m_nCurBaseTableId && m_poCurBaseTable != NULL) + { + // The right table is already opened. Not much to do! + m_poCurBaseTable->ResetReading(); + return 0; + } + else + { + TABFeature *poIndexFeature = m_poIndexTable->GetFeatureRef(nTableId); + + if (poIndexFeature) + { + if (OpenBaseTable(poIndexFeature, bTestOpenNoError) != 0) + { + // Open Failed... an error has already been reported. + if (bTestOpenNoError) + CPLErrorReset(); + return -1; + } + } + } + + return 0; +} + +/********************************************************************** + * TABSeamless::OpenNextBaseTable() + * + * Open the next base table in the dataset, using GetNextFeature() so that + * the spatial filter is respected. + * + * m_bEOF will be set if there are no more base tables to read. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABSeamless::OpenNextBaseTable(GBool bTestOpenNoError /*=FALSE*/) +{ + CPLAssert(m_poIndexTable); + + TABFeature *poIndexFeature = (TABFeature*)m_poIndexTable->GetNextFeature(); + + if (poIndexFeature) + { + if (OpenBaseTable(poIndexFeature, bTestOpenNoError) != 0) + { + // Open Failed... an error has already been reported. + if (bTestOpenNoError) + CPLErrorReset(); + delete poIndexFeature; + return -1; + } + delete poIndexFeature; + m_bEOF = FALSE; + } + else + { + // Reached EOF + m_bEOF = TRUE; + } + + return 0; +} + + +/********************************************************************** + * TABSeamless::EncodeFeatureId() + * + * Combine the table id + feature id into a single feature id that should + * be unique amongst all base tables in this seamless dataset. + **********************************************************************/ +GIntBig TABSeamless::EncodeFeatureId(int nTableId, int nBaseFeatureId) +{ + if (nTableId == -1 || nBaseFeatureId == -1) + return -1; + + /* Feature encoding is now based on the numbers of bits on the number + of features in the index table. */ + + return (((GIntBig)nTableId<<32) + nBaseFeatureId); +} + +int TABSeamless::ExtractBaseTableId(GIntBig nEncodedFeatureId) +{ + if (nEncodedFeatureId == -1) + return -1; + + return ((int)(nEncodedFeatureId>>32)); +} + +int TABSeamless::ExtractBaseFeatureId(GIntBig nEncodedFeatureId) +{ + if (nEncodedFeatureId == -1) + return -1; + + return ((int)(nEncodedFeatureId & 0xffffffff)); +} + +/********************************************************************** + * TABSeamless::GetNextFeatureId() + * + * Returns feature id that follows nPrevId, or -1 if it is the + * last feature id. Pass nPrevId=-1 to fetch the first valid feature id. + **********************************************************************/ +GIntBig TABSeamless::GetNextFeatureId(GIntBig nPrevId) +{ + if (m_poIndexTable == NULL) + return -1; // File is not opened yet + + if (nPrevId == -1 || m_nCurBaseTableId != ExtractBaseTableId(nPrevId)) + { + if (OpenBaseTable(ExtractBaseTableId(nPrevId)) != 0) + return -1; + } + + int nId = ExtractBaseFeatureId(nPrevId); + do + { + nId = (int) m_poCurBaseTable->GetNextFeatureId(nId); + if (nId != -1) + return EncodeFeatureId(m_nCurBaseTableId, nId); // Found one! + else + OpenNextBaseTable(); // Skip to next tile and loop again + + } while (nId == -1 && !m_bEOF && m_poCurBaseTable); + + return -1; +} + +/********************************************************************** + * TABSeamless::GetFeatureRef() + * + * Fill and return a TABFeature object for the specified feature id. + * + * The returned pointer is a reference to an object owned and maintained + * by this TABSeamless object. It should not be altered or freed by the + * caller and its contents is guaranteed to be valid only until the next + * call to GetFeatureRef() or Close(). + * + * Returns NULL if the specified feature id does not exist of if an + * error happened. In any case, CPLError() will have been called to + * report the reason of the failure. + **********************************************************************/ +TABFeature *TABSeamless::GetFeatureRef(GIntBig nFeatureId) +{ + if (m_poIndexTable == NULL) + return NULL; // File is not opened yet + + if (nFeatureId == m_nCurFeatureId && m_poCurFeature) + return m_poCurFeature; + + if (m_nCurBaseTableId != ExtractBaseTableId(nFeatureId)) + { + if (OpenBaseTable(ExtractBaseTableId(nFeatureId)) != 0) + return NULL; + } + + if (m_poCurBaseTable) + { + if (m_poCurFeature) + delete m_poCurFeature; + m_poCurFeature = NULL; + + TABFeature* poCurFeature = (TABFeature*)m_poCurBaseTable->GetFeature(ExtractBaseFeatureId(nFeatureId)); + if( poCurFeature == NULL ) + return NULL; + m_poCurFeature = new TABFeature(m_poFeatureDefnRef); + m_poCurFeature->SetFrom(poCurFeature); + delete poCurFeature; + + m_nCurFeatureId = nFeatureId; + + m_poCurFeature->SetFID(nFeatureId); + + return m_poCurFeature; + } + + return NULL; +} + + +/********************************************************************** + * TABSeamless::GetLayerDefn() + * + * Returns a reference to the OGRFeatureDefn that will be used to create + * features in this dataset. + * + * Returns a reference to an object that is maintained by this TABSeamless + * object (and thus should not be modified or freed by the caller) or + * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file + * opened yet) + **********************************************************************/ +OGRFeatureDefn *TABSeamless::GetLayerDefn() +{ + return m_poFeatureDefnRef; +} + +/********************************************************************** + * TABSeamless::GetNativeFieldType() + * + * Returns the native MapInfo field type for the specified field. + * + * Returns TABFUnknown if file is not opened, or if specified field index is + * invalid. + * + * Note that field ids are positive and start at 0. + **********************************************************************/ +TABFieldType TABSeamless::GetNativeFieldType(int nFieldId) +{ + if (m_poCurBaseTable) + return m_poCurBaseTable->GetNativeFieldType(nFieldId); + + return TABFUnknown; +} + + + +/********************************************************************** + * TABSeamless::IsFieldIndexed() + * + * Returns TRUE if field is indexed, or FALSE otherwise. + **********************************************************************/ +GBool TABSeamless::IsFieldIndexed(int nFieldId) +{ + if (m_poCurBaseTable) + return m_poCurBaseTable->IsFieldIndexed(nFieldId); + + return FALSE; +} + +/********************************************************************** + * TABSeamless::IsFieldUnique() + * + * Returns TRUE if field is in the Unique table, or FALSE otherwise. + **********************************************************************/ +GBool TABSeamless::IsFieldUnique(int nFieldId) +{ + if (m_poCurBaseTable) + return m_poCurBaseTable->IsFieldUnique(nFieldId); + + return FALSE; +} + + +/********************************************************************** + * TABSeamless::GetBounds() + * + * Fetch projection coordinates bounds of a dataset. + * + * The bForce flag has no effect on TAB files since the bounds are + * always in the header. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABSeamless::GetBounds(double &dXMin, double &dYMin, + double &dXMax, double &dYMax, + GBool bForce /*= TRUE*/) +{ + if (m_poIndexTable == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GetBounds() can be called only after dataset has been opened."); + return -1; + } + + return m_poIndexTable->GetBounds(dXMin, dYMin, dXMax, dYMax, bForce); +} + +/********************************************************************** + * TABSeamless::GetExtent() + * + * Fetch extent of the data currently stored in the dataset. + * + * The bForce flag has no effect on TAB files since that value is + * always in the header. + * + * Returns OGRERR_NONE/OGRRERR_FAILURE. + **********************************************************************/ +OGRErr TABSeamless::GetExtent (OGREnvelope *psExtent, int bForce) +{ + if (m_poIndexTable == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GetExtent() can be called only after dataset has been opened."); + return OGRERR_FAILURE; + } + + return m_poIndexTable->GetExtent(psExtent, bForce); + +} + +/********************************************************************** + * TABSeamless::GetFeatureCountByType() + * + * Return number of features of each type. + * + * Note that the sum of the 4 returned values may be different from + * the total number of features since features with NONE geometry + * are not taken into account here. + * + * Returns 0 on success, or silently returns -1 (with no error) if this + * information is not available. + **********************************************************************/ +int TABSeamless::GetFeatureCountByType(CPL_UNUSED int &numPoints, + CPL_UNUSED int &numLines, + CPL_UNUSED int &numRegions, + CPL_UNUSED int &numTexts, + CPL_UNUSED GBool bForce /*= TRUE*/) +{ + /*----------------------------------------------------------------- + * __TODO__ This should be implemented to return -1 if force=false, + * or scan all the base tables if force=true + *----------------------------------------------------------------*/ + + return -1; +} + +GIntBig TABSeamless::GetFeatureCount(int bForce) +{ + /*----------------------------------------------------------------- + * __TODO__ This should be implemented to return -1 if force=false, + * or scan all the base tables if force=true + *----------------------------------------------------------------*/ + + return OGRLayer::GetFeatureCount(bForce); +} + + +/********************************************************************** + * TABSeamless::GetSpatialRef() + * + * Returns a reference to an OGRSpatialReference for this dataset. + * If the projection parameters have not been parsed yet, then we will + * parse them before returning. + * + * The returned object is owned and maintained by this TABFile and + * should not be modified or freed by the caller. + * + * Returns NULL if the SpatialRef cannot be accessed. + **********************************************************************/ +OGRSpatialReference *TABSeamless::GetSpatialRef() +{ + if (m_poIndexTable == NULL) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "GetSpatialRef() failed: file has not been opened yet."); + return NULL; + } + + return m_poIndexTable->GetSpatialRef(); +} + + + +/********************************************************************** + * IMapInfoFile::SetSpatialFilter() + * + * Standard OGR SetSpatialFiltere implementation. This methode is used + * to set a SpatialFilter for this OGRLayer + **********************************************************************/ +void TABSeamless::SetSpatialFilter (OGRGeometry * poGeomIn ) + +{ + IMapInfoFile::SetSpatialFilter( poGeomIn ); + + if( m_poIndexTable ) + m_poIndexTable->SetSpatialFilter( poGeomIn ); + + if( m_poCurBaseTable ) + m_poCurBaseTable->SetSpatialFilter( poGeomIn ); +} + + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int TABSeamless::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return TRUE; + + else + return FALSE; +} + + + +/********************************************************************** + * TABSeamless::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABSeamless::Dump(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABSeamless::Dump() -----\n"); + + if (m_poIndexTable == NULL) + { + fprintf(fpOut, "File is not opened.\n"); + } + else + { + fprintf(fpOut, "File is opened: %s\n", m_pszFname); + + } + + fflush(fpOut); +} + +#endif // DEBUG diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_tabview.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_tabview.cpp new file mode 100644 index 000000000..e1b2a1eed --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_tabview.cpp @@ -0,0 +1,2114 @@ +/********************************************************************** + * $Id: mitab_tabview.cpp,v 1.22 2010-07-07 19:00:15 aboudreault Exp $ + * + * Name: mitab_tabfile.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABView class, used to handle .TAB + * datasets composed of a number of .TAB files linked through + * indexed fields. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2002, Daniel Morissette + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_tabview.cpp,v $ + * Revision 1.22 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.21 2010-07-05 19:01:20 aboudreault + * Reverted last SetFeature change in mitab_capi.cpp and fixed another memory leak + * + * Revision 1.20 2010-01-07 20:39:12 aboudreault + * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141) + * + * Revision 1.19 2008-03-05 20:35:39 dmorissette + * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859) + * + * Revision 1.18 2008/01/29 20:46:32 dmorissette + * Added support for v9 Time and DateTime fields (byg 1754) + * + * Revision 1.17 2007/06/21 14:00:23 dmorissette + * Added missing cast in isspace() calls to avoid failed assertion on Windows + * (MITAB bug 1737, GDAL ticket 1678)) + * + * Revision 1.16 2007/06/12 13:52:38 dmorissette + * Added IMapInfoFile::SetCharset() method (bug 1734) + * + * Revision 1.15 2007/06/12 12:50:40 dmorissette + * Use Quick Spatial Index by default until bug 1732 is fixed (broken files + * produced by current coord block splitting technique). + * + * Revision 1.14 2007/03/21 21:15:56 dmorissette + * Added SetQuickSpatialIndexMode() which generates a non-optimal spatial + * index but results in faster write time (bug 1669) + * + * Revision 1.13 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.12 2002/02/22 20:44:51 julien + * Prevent infinite loop with TABRelation by suppress the m_poCurFeature object + * from the class and setting it in the calling function and add GetFeature in + * the class. (bug 706) + * + * Revision 1.11 2002/01/10 05:13:22 daniel + * Prevent crash if .IND file is deleted (but 703) + * + * Revision 1.10 2002/01/10 04:52:58 daniel + * Support 'select * ...' syntax + 'open table..." directives with/without .tab + * + * Revision 1.9 2001/06/27 19:52:26 warmerda + * use VSIUnlink() instead of unlink() + * + * Revision 1.8 2001/03/15 03:57:51 daniel + * Added implementation for new OGRLayer::GetExtent(), returning data MBR. + * + * Revision 1.7 2000/09/28 16:39:44 warmerda + * avoid warnings for unused, and unitialized variables + * + * Revision 1.6 2000/02/28 17:12:22 daniel + * Write support for joined tables and indexed fields + * + * Revision 1.5 2000/01/15 22:30:45 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.4 1999/12/19 17:40:16 daniel + * Init + delete m_poRelation properly + * + * Revision 1.3 1999/12/14 05:53:00 daniel + * Fixed compile warnings + * + * Revision 1.2 1999/12/14 04:04:10 daniel + * Added bforceFlags to GetBounds() and GetFeatureCountByType() + * + * Revision 1.1 1999/12/14 02:10:32 daniel + * Initial revision + * + **********************************************************************/ + +#include "mitab.h" +#include "mitab_utils.h" + +#include /* isspace() */ + +/*===================================================================== + * class TABView + *====================================================================*/ + + +/********************************************************************** + * TABView::TABView() + * + * Constructor. + **********************************************************************/ +TABView::TABView() +{ + m_pszFname = NULL; + m_eAccessMode = TABRead; + m_papszTABFile = NULL; + m_pszVersion = NULL; + + m_numTABFiles = 0; + m_papszTABFnames = NULL; + m_papoTABFiles = NULL; + m_nMainTableIndex = -1; + + m_papszFieldNames = NULL; + m_papszWhereClause = NULL; + + m_poRelation = NULL; + m_bRelFieldsCreated = FALSE; +} + +/********************************************************************** + * TABView::~TABView() + * + * Destructor. + **********************************************************************/ +TABView::~TABView() +{ + Close(); +} + + +GIntBig TABView::GetFeatureCount (int bForce) +{ + + if (m_nMainTableIndex != -1) + return m_papoTABFiles[m_nMainTableIndex]->GetFeatureCount( bForce ); + + return 0; +} + +void TABView::ResetReading() +{ + if (m_nMainTableIndex != -1) + m_papoTABFiles[m_nMainTableIndex]->ResetReading(); +} + + +/********************************************************************** + * TABView::Open() + * + * Open a .TAB dataset and the associated files, and initialize the + * structures to be ready to read features from it. + * + * This class is used to open .TAB files that define a view on + * two other .TAB files. Regular .TAB datasets should be opened using + * the TABFile class instead. + * + * Set bTestOpenNoError=TRUE to silently return -1 with no error message + * if the file cannot be opened. This is intended to be used in the + * context of a TestOpen() function. The default value is FALSE which + * means that an error is reported if the file cannot be opened. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABView::Open(const char *pszFname, TABAccess eAccess, + GBool bTestOpenNoError /*= FALSE*/ ) +{ + char nStatus = 0; + + if (m_numTABFiles > 0) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "Open() failed: object already contains an open file"); + return -1; + } + + /*----------------------------------------------------------------- + * Validate access mode and call the right open method + *----------------------------------------------------------------*/ + if (eAccess == TABRead) + { + m_eAccessMode = TABRead; + nStatus = (char)OpenForRead(pszFname, bTestOpenNoError); + } + else if (eAccess == TABWrite) + { + m_eAccessMode = TABWrite; + nStatus = (char)OpenForWrite(pszFname); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Open() failed: access mode \"%d\" not supported", eAccess); + return -1; + } + + return nStatus; +} + + +/********************************************************************** + * TABView::OpenForRead() + * + * Open for reading + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABView::OpenForRead(const char *pszFname, + GBool bTestOpenNoError /*= FALSE*/ ) +{ + char *pszPath = NULL; + int nFnameLen = 0; + + m_eAccessMode = TABRead; + + /*----------------------------------------------------------------- + * Read main .TAB (text) file + *----------------------------------------------------------------*/ + m_pszFname = CPLStrdup(pszFname); + +#ifndef _WIN32 + /*----------------------------------------------------------------- + * On Unix, make sure extension uses the right cases + * We do it even for write access because if a file with the same + * extension already exists we want to overwrite it. + *----------------------------------------------------------------*/ + TABAdjustFilenameExtension(m_pszFname); +#endif + + /*----------------------------------------------------------------- + * Open .TAB file... since it's a small text file, we will just load + * it as a stringlist in memory. + *----------------------------------------------------------------*/ + m_papszTABFile = TAB_CSLLoad(m_pszFname); + if (m_papszTABFile == NULL) + { + if (!bTestOpenNoError) + { + CPLError(CE_Failure, CPLE_FileIO, + "Failed opening %s.", m_pszFname); + } + + CPLFree(m_pszFname); + return -1; + } + + /*------------------------------------------------------------- + * Look for a line with the "create view" keyword. + * If there is no "create view", then we may have a valid .TAB file, + * but we do not support it in this class. + *------------------------------------------------------------*/ + GBool bCreateViewFound = FALSE; + for (int i=0; + !bCreateViewFound && m_papszTABFile && m_papszTABFile[i]; + i++) + { + const char *pszStr = m_papszTABFile[i]; + while(*pszStr != '\0' && isspace((unsigned char)*pszStr)) + pszStr++; + if (EQUALN(pszStr, "create view", 11)) + bCreateViewFound = TRUE; + } + + if ( !bCreateViewFound ) + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_NotSupported, + "%s contains no table view definition. " + "This type of .TAB file cannot be read by this library.", + m_pszFname); + else + CPLErrorReset(); + + CPLFree(m_pszFname); + + return -1; + } + + /*----------------------------------------------------------------- + * OK, this appears to be a valid TAB view dataset... + * Extract the path component from the main .TAB filename + * to build the filename of the sub-tables + *----------------------------------------------------------------*/ + pszPath = CPLStrdup(m_pszFname); + nFnameLen = strlen(pszPath); + for( ; nFnameLen > 0; nFnameLen--) + { + if (pszPath[nFnameLen-1] == '/' || + pszPath[nFnameLen-1] == '\\' ) + { + break; + } + pszPath[nFnameLen-1] = '\0'; + } + + /*----------------------------------------------------------------- + * Extract the useful info from the TAB header + *----------------------------------------------------------------*/ + if (ParseTABFile(pszPath, bTestOpenNoError) != 0) + { + // Failed parsing... an error has already been produced if necessary + CPLFree(pszPath); + Close(); + return -1; + } + CPLFree(pszPath); + pszPath = NULL; + + /*----------------------------------------------------------------- + * __TODO__ For now, we support only 2 files linked through a single + * field... so we'll do some validation first to make sure + * that what we found in the header respects these limitations. + *----------------------------------------------------------------*/ + if (m_numTABFiles != 2) + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_NotSupported, + "Open Failed: Dataset %s defines a view on %d tables. " + "This is not currently supported.", + m_pszFname, m_numTABFiles); + Close(); + return -1; + } + + /*----------------------------------------------------------------- + * Open all the tab files listed in the view + *----------------------------------------------------------------*/ + m_papoTABFiles = (TABFile**)CPLCalloc(m_numTABFiles, sizeof(TABFile*)); + + for (int iFile=0; iFile < m_numTABFiles; iFile++) + { +#ifndef _WIN32 + TABAdjustFilenameExtension(m_papszTABFnames[iFile]); +#endif + + m_papoTABFiles[iFile] = new TABFile; + + if ( m_papoTABFiles[iFile]->Open(m_papszTABFnames[iFile], + m_eAccessMode, bTestOpenNoError) != 0) + { + // Open Failed... an error has already been reported, just return. + if (bTestOpenNoError) + CPLErrorReset(); + Close(); + return -1; + } + } + + /*----------------------------------------------------------------- + * Create TABRelation... this will build FeatureDefn, etc. + * __TODO__ For now this assumes only 2 tables in the view... + *----------------------------------------------------------------*/ + m_poRelation = new TABRelation; + + CPLAssert(m_nMainTableIndex == 0); + CPLAssert(CSLCount(m_papszWhereClause) == 5); + char *pszTableName = TABGetBasename(m_pszFname); + if ( m_poRelation->Init(pszTableName, + m_papoTABFiles[0], m_papoTABFiles[1], + m_papszWhereClause[4], m_papszWhereClause[2], + m_papszFieldNames) != 0 ) + { + // An error should already have been reported + CPLFree(pszTableName); + Close(); + return -1; + } + CPLFree(pszTableName); + + return 0; +} + + +/********************************************************************** + * TABView::OpenForWrite() + * + * Create a new TABView dataset + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABView::OpenForWrite(const char *pszFname) +{ + int nFnameLen = 0; + + m_eAccessMode = TABWrite; + + /*----------------------------------------------------------------- + * Read main .TAB (text) file + *----------------------------------------------------------------*/ + m_pszFname = CPLStrdup(pszFname); + +#ifndef _WIN32 + /*----------------------------------------------------------------- + * On Unix, make sure extension uses the right cases + * We do it even for write access because if a file with the same + * extension already exists we want to overwrite it. + *----------------------------------------------------------------*/ + TABAdjustFilenameExtension(m_pszFname); +#endif + + /*----------------------------------------------------------------- + * Extract the path component from the main .TAB filename + *----------------------------------------------------------------*/ + char *pszPath = CPLStrdup(m_pszFname); + nFnameLen = strlen(pszPath); + for( ; nFnameLen > 0; nFnameLen--) + { + if (pszPath[nFnameLen-1] == '/' || + pszPath[nFnameLen-1] == '\\' ) + { + break; + } + pszPath[nFnameLen-1] = '\0'; + } + + char *pszBasename = TABGetBasename(m_pszFname); + + /*----------------------------------------------------------------- + * Create the 2 TAB files for the view. + * + * __TODO__ For now, we support only 2 files linked through a single + * field... not sure if anything else than that can be useful + * anyways. + *----------------------------------------------------------------*/ + m_numTABFiles = 2; + m_papszTABFnames = NULL; + m_nMainTableIndex = 0; + m_bRelFieldsCreated = FALSE; + + m_papoTABFiles = (TABFile**)CPLCalloc(m_numTABFiles, sizeof(TABFile*)); + + for (int iFile=0; iFile < m_numTABFiles; iFile++) + { + m_papszTABFnames = CSLAppendPrintf(m_papszTABFnames, "%s%s%d.tab", + pszPath, pszBasename, iFile+1); +#ifndef _WIN32 + TABAdjustFilenameExtension(m_papszTABFnames[iFile]); +#endif + + m_papoTABFiles[iFile] = new TABFile; + + if ( m_papoTABFiles[iFile]->Open(m_papszTABFnames[iFile], m_eAccessMode) != 0) + { + // Open Failed... an error has already been reported, just return. + CPLFree(pszPath); + CPLFree(pszBasename); + Close(); + return -1; + } + } + + /*----------------------------------------------------------------- + * Create TABRelation... + *----------------------------------------------------------------*/ + m_poRelation = new TABRelation; + + if ( m_poRelation->Init(pszBasename, + m_papoTABFiles[0], m_papoTABFiles[1], + NULL, NULL, NULL) != 0 ) + { + // An error should already have been reported + CPLFree(pszPath); + CPLFree(pszBasename); + Close(); + return -1; + } + + CPLFree(pszPath); + CPLFree(pszBasename); + + return 0; +} + + + +/********************************************************************** + * TABView::ParseTABFile() + * + * Scan the lines of the TAB file, and store any useful information into + * class members. The main piece of information being the sub-table + * names, and the list of fields to include in the view that we will + * use to build the OGRFeatureDefn for this file. + * + * It is assumed that the TAB header file is already loaded in m_papszTABFile + * + * This private method should be used only during the Open() call. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABView::ParseTABFile(const char *pszDatasetPath, + GBool bTestOpenNoError /*=FALSE*/) +{ + int iLine, numLines; + char **papszTok=NULL; + GBool bInsideTableDef = FALSE; + + if (m_eAccessMode != TABRead) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "ParseTABFile() can be used only with Read access."); + return -1; + } + + numLines = CSLCount(m_papszTABFile); + + for(iLine=0; iLine= 3) + { + // Source table name may be either "filename" or "filename.tab" + int nLen = strlen(papszTok[2]); + if (nLen > 4 && EQUAL(papszTok[2]+nLen-4, ".tab")) + papszTok[2][nLen-4] = '\0'; + + m_papszTABFnames = CSLAppendPrintf(m_papszTABFnames, + "%s%s.tab", + pszDatasetPath, papszTok[2]); + } + else if (EQUAL(papszTok[0], "create") && + EQUAL(papszTok[1], "view") ) + { + bInsideTableDef = TRUE; + } + else if (bInsideTableDef && + (EQUAL(papszTok[0],"Select"))) + { + /*--------------------------------------------------------- + * We found the list of table fields (comma-delimited list) + *--------------------------------------------------------*/ + int iTok; + for(iTok=1; papszTok[iTok] != NULL; iTok++) + m_papszFieldNames = CSLAddString(m_papszFieldNames, + papszTok[iTok]); + + } + else if (bInsideTableDef && + (EQUAL(papszTok[0],"where"))) + { + /*--------------------------------------------------------- + * We found the where clause that relates the 2 tables + * Something in the form: + * where table1.field1=table2.field2 + * The tokenized array will contain: + * {"where", "table1", "field1", "table2", "field2"} + *--------------------------------------------------------*/ + m_papszWhereClause =CSLTokenizeStringComplex(m_papszTABFile[iLine], + " \t(),;=.", + TRUE, FALSE); + + /*--------------------------------------------------------- + * For now we are very limiting on the format of the WHERE + * clause... we will be more permitting as we learn more about + * what it can contain... (I don't want to implement a full SQL + * parser here!!!). If you encountered this error, + * (and are reading this!) please report the test dataset + * that produced the error and I'll see if we can support it. + *--------------------------------------------------------*/ + if (CSLCount( m_papszWhereClause ) != 5) + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_NotSupported, + "WHERE clause in %s is not in a supported format: \"%s\"", + m_pszFname, m_papszTABFile[iLine]); + return -1; + } + } + else + { + // Simply Ignore unrecognized lines + } + } + + CSLDestroy(papszTok); + + /*----------------------------------------------------------------- + * The main table is the one from which we read the geometries, etc... + * For now we assume it is always the first one in the list + *----------------------------------------------------------------*/ + m_nMainTableIndex = 0; + + /*----------------------------------------------------------------- + * Make sure all required class members are set + *----------------------------------------------------------------*/ + m_numTABFiles = CSLCount(m_papszTABFnames); + + if (m_pszCharset == NULL) + m_pszCharset = CPLStrdup("Neutral"); + if (m_pszVersion == NULL) + m_pszVersion = CPLStrdup("100"); + + if (CSLCount(m_papszFieldNames) == 0 ) + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_NotSupported, + "%s: header contains no table field definition. " + "This type of .TAB file cannot be read by this library.", + m_pszFname); + return -1; + } + + if (CSLCount(m_papszWhereClause) == 0 ) + { + if (!bTestOpenNoError) + CPLError(CE_Failure, CPLE_NotSupported, + "%s: WHERE clause not found or missing in header. " + "This type of .TAB file cannot be read by this library.", + m_pszFname); + return -1; + } + return 0; +} + + +/********************************************************************** + * TABView::WriteTABFile() + * + * Generate the TAB header file. This is usually done during the + * Close() call. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABView::WriteTABFile() +{ + VSILFILE *fp; + + CPLAssert(m_eAccessMode == TABWrite); + CPLAssert(m_numTABFiles == 2); + CPLAssert(GetLayerDefn()); + + char *pszTable = TABGetBasename(m_pszFname); + char *pszTable1 = TABGetBasename(m_papszTABFnames[0]); + char *pszTable2 = TABGetBasename(m_papszTABFnames[1]); + + if ( (fp = VSIFOpenL(m_pszFname, "wt")) != NULL) + { + // Version is always 100, no matter what the sub-table's version is + VSIFPrintfL(fp, "!Table\n"); + VSIFPrintfL(fp, "!Version 100\n"); + + VSIFPrintfL(fp, "Open Table \"%s\" Hide\n", pszTable1); + VSIFPrintfL(fp, "Open Table \"%s\" Hide\n", pszTable2); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "Create View %s As\n", pszTable); + VSIFPrintfL(fp, "Select "); + + OGRFeatureDefn *poDefn = GetLayerDefn(); + for(int iField=0; iFieldGetFieldCount(); iField++) + { + OGRFieldDefn *poFieldDefn = poDefn->GetFieldDefn(iField); + if (iField == 0) + VSIFPrintfL(fp, "%s", poFieldDefn->GetNameRef()); + else + VSIFPrintfL(fp, ",%s", poFieldDefn->GetNameRef()); + } + VSIFPrintfL(fp, "\n"); + + VSIFPrintfL(fp, "From %s, %s\n", pszTable2, pszTable1); + VSIFPrintfL(fp, "Where %s.%s=%s.%s\n", pszTable2, + m_poRelation->GetRelFieldName(), + pszTable1, + m_poRelation->GetMainFieldName()); + + + VSIFCloseL(fp); + } + else + { + CPLFree(pszTable); + CPLFree(pszTable1); + CPLFree(pszTable2); + + CPLError(CE_Failure, CPLE_FileIO, + "Failed to create file `%s'", m_pszFname); + return -1; + } + + CPLFree(pszTable); + CPLFree(pszTable1); + CPLFree(pszTable2); + + return 0; +} + + +/********************************************************************** + * TABView::Close() + * + * Close current file, and release all memory used. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABView::Close() +{ + // In write access, the main .TAB file has not been written yet. + if (m_eAccessMode == TABWrite && m_poRelation) + WriteTABFile(); + + for(int i=0; m_papoTABFiles && iSetQuickSpatialIndexMode(bQuickSpatialIndexMode) != 0) + { + // An error has already been reported, just return. + return -1; + } + } + + return 0; +} + + +/********************************************************************** + * TABView::GetNextFeatureId() + * + * Returns feature id that follows nPrevId, or -1 if it is the + * last feature id. Pass nPrevId=-1 to fetch the first valid feature id. + **********************************************************************/ +GIntBig TABView::GetNextFeatureId(GIntBig nPrevId) +{ + if (m_nMainTableIndex != -1) + return m_papoTABFiles[m_nMainTableIndex]->GetNextFeatureId(nPrevId); + + return -1; +} + +/********************************************************************** + * TABView::GetFeatureRef() + * + * Fill and return a TABFeature object for the specified feature id. + * + * The retruned pointer is a reference to an object owned and maintained + * by this TABView object. It should not be altered or freed by the + * caller and its contents is guaranteed to be valid only until the next + * call to GetFeatureRef() or Close(). + * + * Returns NULL if the specified feature id does not exist of if an + * error happened. In any case, CPLError() will have been called to + * report the reason of the failure. + **********************************************************************/ +TABFeature *TABView::GetFeatureRef(GIntBig nFeatureId) +{ + + /*----------------------------------------------------------------- + * Make sure file is opened + *----------------------------------------------------------------*/ + if (m_poRelation == NULL) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "GetFeatureRef() failed: file is not opened!"); + return NULL; + } + + if( (GIntBig)(int)nFeatureId != nFeatureId ) + return NULL; + + if(m_poCurFeature) + { + delete m_poCurFeature; + m_poCurFeature = NULL; + } + + m_poCurFeature = m_poRelation->GetFeature((int)nFeatureId); + m_nCurFeatureId = nFeatureId; + m_poCurFeature->SetFID(m_nCurFeatureId); + return m_poCurFeature; +} + + +/********************************************************************** + * TABView::CreateFeature() + * + * Write a new feature to this dataset. The passed in feature is updated + * with the new feature id. + * + * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an + * error happened in which case, CPLError() will have been called to + * report the reason of the failure. + **********************************************************************/ +OGRErr TABView::CreateFeature(TABFeature *poFeature) +{ + if (m_eAccessMode != TABWrite) + { + CPLError(CE_Failure, CPLE_NotSupported, + "CreateFeature() can be used only with Write access."); + return OGRERR_UNSUPPORTED_OPERATION; + } + + if (m_poRelation == NULL) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "CreateFeature() failed: file is not opened!"); + return OGRERR_FAILURE; + } + + /*----------------------------------------------------------------- + * If we're about to write the first feature, then we must finish + * the initialization of the view first by creating the MI_refnum fields + *----------------------------------------------------------------*/ + if (!m_bRelFieldsCreated) + { + if (m_poRelation->CreateRelFields() != 0) + return OGRERR_FAILURE; + m_bRelFieldsCreated = TRUE; + } + + int nFeatureId = m_poRelation->WriteFeature(poFeature); + if (nFeatureId < 0) + return OGRERR_FAILURE; + + poFeature->SetFID(nFeatureId); + + return OGRERR_NONE; +} + + + +/********************************************************************** + * TABView::GetLayerDefn() + * + * Returns a reference to the OGRFeatureDefn that will be used to create + * features in this dataset. + * + * Returns a reference to an object that is maintained by this TABView + * object (and thus should not be modified or freed by the caller) or + * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file + * opened yet) + **********************************************************************/ +OGRFeatureDefn *TABView::GetLayerDefn() +{ + if (m_poRelation) + return m_poRelation->GetFeatureDefn(); + + return NULL; +} + +/********************************************************************** + * TABView::SetFeatureDefn() + * + * Set the FeatureDefn for this dataset. + * + * For now, fields passed through SetFeatureDefn will not be mapped + * properly, so this function can be used only with an empty feature defn. + **********************************************************************/ +int TABView::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn, + CPL_UNUSED TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */) +{ + if (m_poRelation) + return m_poRelation->SetFeatureDefn(poFeatureDefn); + + return -1; +} + + +/********************************************************************** + * TABView::GetNativeFieldType() + * + * Returns the native MapInfo field type for the specified field. + * + * Returns TABFUnknown if file is not opened, or if specified field index is + * invalid. + * + * Note that field ids are positive and start at 0. + **********************************************************************/ +TABFieldType TABView::GetNativeFieldType(int nFieldId) +{ + if (m_poRelation) + return m_poRelation->GetNativeFieldType(nFieldId); + + return TABFUnknown; +} + + +/********************************************************************** + * TABView::AddFieldNative() + * + * Create a new field using a native mapinfo data type... this is an + * alternative to defining fields through the OGR interface. + * This function should be called after creating a new dataset, but before + * writing the first feature. + * + * This function will build/update the OGRFeatureDefn that will have to be + * used when writing features to this dataset. + * + * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn(). + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABView::AddFieldNative(const char *pszName, TABFieldType eMapInfoType, + int nWidth /*=0*/, int nPrecision /*=0*/, + GBool bIndexed /*=FALSE*/, GBool bUnique/*=FALSE*/, int bApproxOK) +{ + if (m_poRelation) + return m_poRelation->AddFieldNative(pszName, eMapInfoType, + nWidth, nPrecision, + bIndexed, bUnique, bApproxOK); + + return -1; +} + +/********************************************************************** + * TABView::SetFieldIndexed() + * + * Request that a field be indexed. This will create the .IND file if + * necessary, etc. + * + * Note that field ids are positive and start at 0. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABView::SetFieldIndexed(int nFieldId) +{ + if (m_poRelation) + return m_poRelation->SetFieldIndexed(nFieldId); + + return -1; +} + +/********************************************************************** + * TABView::IsFieldIndexed() + * + * Returns TRUE if field is indexed, or FALSE otherwise. + **********************************************************************/ +GBool TABView::IsFieldIndexed(int nFieldId) +{ + if (m_poRelation) + return m_poRelation->IsFieldIndexed(nFieldId); + + return FALSE; +} + +/********************************************************************** + * TABView::IsFieldUnique() + * + * Returns TRUE if field is in the Unique table, or FALSE otherwise. + **********************************************************************/ +GBool TABView::IsFieldUnique(int nFieldId) +{ + if (m_poRelation) + return m_poRelation->IsFieldUnique(nFieldId); + + return FALSE; +} + + +/********************************************************************** + * TABView::GetBounds() + * + * Fetch projection coordinates bounds of a dataset. + * + * The bForce flag has no effect on TAB files since the bounds are + * always in the header. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABView::GetBounds(double &dXMin, double &dYMin, + double &dXMax, double &dYMax, + GBool bForce /*= TRUE*/) +{ + if (m_nMainTableIndex == -1) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GetBounds() can be called only after dataset has been opened."); + return -1; + } + + return m_papoTABFiles[m_nMainTableIndex]->GetBounds(dXMin, dYMin, + dXMax, dYMax, + bForce); +} + +/********************************************************************** + * TABView::GetExtent() + * + * Fetch extent of the data currently stored in the dataset. + * + * The bForce flag has no effect on TAB files since that value is + * always in the header. + * + * Returns OGRERR_NONE/OGRRERR_FAILURE. + **********************************************************************/ +OGRErr TABView::GetExtent (OGREnvelope *psExtent, int bForce) +{ + if (m_nMainTableIndex == -1) + { + CPLError(CE_Failure, CPLE_AppDefined, + "GetExtent() can be called only after dataset has been opened."); + return -1; + } + + return m_papoTABFiles[m_nMainTableIndex]->GetExtent(psExtent, bForce); + +} + +/********************************************************************** + * TABView::GetFeatureCountByType() + * + * Return number of features of each type. + * + * Note that the sum of the 4 returned values may be different from + * the total number of features since features with NONE geometry + * are not taken into account here. + * + * Note: the bForce flag has nmo effect on .TAB files since the info + * is always in the header. + * + * Returns 0 on success, or silently returns -1 (with no error) if this + * information is not available. + **********************************************************************/ +int TABView::GetFeatureCountByType(int &numPoints, int &numLines, + int &numRegions, int &numTexts, + GBool bForce /*= TRUE*/) +{ + if (m_nMainTableIndex == -1) + return -1; + + return m_papoTABFiles[m_nMainTableIndex]->GetFeatureCountByType(numPoints, + numLines, + numRegions, + numTexts, + bForce); +} + + +/********************************************************************** + * TABView::GetSpatialRef() + * + * Returns a reference to an OGRSpatialReference for this dataset. + * If the projection parameters have not been parsed yet, then we will + * parse them before returning. + * + * The returned object is owned and maintained by this TABFile and + * should not be modified or freed by the caller. + * + * Returns NULL if the SpatialRef cannot be accessed. + **********************************************************************/ +OGRSpatialReference *TABView::GetSpatialRef() +{ + if (m_nMainTableIndex == -1) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "GetSpatialRef() failed: file has not been opened yet."); + return NULL; + } + + return m_papoTABFiles[m_nMainTableIndex]->GetSpatialRef(); +} + +/********************************************************************** + * TABView::SetSpatialRef() + **********************************************************************/ +int TABView::SetSpatialRef(OGRSpatialReference *poSpatialRef) +{ + if (m_nMainTableIndex == -1) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetSpatialRef() failed: file has not been opened yet."); + return -1; + } + + return m_papoTABFiles[m_nMainTableIndex]->SetSpatialRef(poSpatialRef); +} + + + +/********************************************************************** + * TABView::SetBounds() + **********************************************************************/ +int TABView::SetBounds(double dXMin, double dYMin, + double dXMax, double dYMax) +{ + if (m_nMainTableIndex == -1) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "SetBounds() failed: file has not been opened yet."); + return -1; + } + + return m_papoTABFiles[m_nMainTableIndex]->SetBounds(dXMin, dYMin, + dXMax, dYMax); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int TABView::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite)) + return TRUE; + + else if( EQUAL(pszCap,OLCRandomWrite)) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return m_poFilterGeom == NULL; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return TRUE; + + else + return FALSE; +} + + + + + + +/********************************************************************** + * TABView::Dump() + * + * Dump block contents... available only in DEBUG mode. + **********************************************************************/ +#ifdef DEBUG + +void TABView::Dump(FILE *fpOut /*=NULL*/) +{ + if (fpOut == NULL) + fpOut = stdout; + + fprintf(fpOut, "----- TABView::Dump() -----\n"); + + if (m_numTABFiles > 0) + { + fprintf(fpOut, "File is not opened.\n"); + } + else + { + fprintf(fpOut, "File is opened: %s\n", m_pszFname); + fprintf(fpOut, "View contains %d tables\n", m_numTABFiles); + + } + + fflush(fpOut); +} + +#endif // DEBUG + + + +/*===================================================================== + * class TABRelation + *====================================================================*/ + + +/********************************************************************** + * TABRelation::TABRelation() + * + * Constructor. + **********************************************************************/ +TABRelation::TABRelation() +{ + m_poMainTable = NULL; + m_pszMainFieldName = NULL; + m_nMainFieldNo = -1; + + m_poRelTable = NULL; + m_pszRelFieldName = NULL; + m_nRelFieldNo = -1; + m_nRelFieldIndexNo = -1; + m_poRelINDFileRef = NULL; + + m_nUniqueRecordNo = 0; + + m_panMainTableFieldMap = NULL; + m_panRelTableFieldMap = NULL; + + m_poDefn = NULL; +} + +/********************************************************************** + * TABRelation::~TABRelation() + * + * Destructor. + **********************************************************************/ +TABRelation::~TABRelation() +{ + ResetAllMembers(); +} + +/********************************************************************** + * TABRelation::ResetAllMembers() + * + * Reset all class members. + **********************************************************************/ +void TABRelation::ResetAllMembers() +{ + m_poMainTable = NULL; + CPLFree(m_pszMainFieldName); + m_pszMainFieldName = NULL; + m_nMainFieldNo = -1; + + m_poRelTable = NULL; + CPLFree(m_pszRelFieldName); + m_pszRelFieldName = NULL; + m_nRelFieldNo = -1; + m_nRelFieldIndexNo = -1; + + m_nUniqueRecordNo = 0; + + // No need to close m_poRelINDFileRef since we only got a ref. to it + m_poRelINDFileRef = NULL; + + CPLFree(m_panMainTableFieldMap); + m_panMainTableFieldMap = NULL; + CPLFree(m_panRelTableFieldMap); + m_panRelTableFieldMap = NULL; + + /*----------------------------------------------------------------- + * Note: we have to check the reference count before deleting m_poDefn + *----------------------------------------------------------------*/ + if (m_poDefn && m_poDefn->Dereference() == 0) + delete m_poDefn; + m_poDefn = NULL; + +} + +/********************************************************************** + * TABRelation::Init() + * + * Set the details of the relation: the main and related tables, the fields + * through which they will be connected, and the list of fields to select. + * After this call, we are ready to read data records. + * + * For write access, Init() is called with pszMain/RelFieldName and + * **papszSelectedFields passed as NULL. They will have to be set through + * other methods before a first feature can be written. + * + * A new OGRFeatureDefn is also built for the combined tables. + * + * Returns 0 on success, or -1 or error. + **********************************************************************/ +int TABRelation::Init(const char *pszViewName, + TABFile *poMainTable, TABFile *poRelTable, + const char *pszMainFieldName, + const char *pszRelFieldName, + char **papszSelectedFields) +{ + if (poMainTable == NULL || poRelTable == NULL) + return -1; + + // We'll need the feature Defn later... + OGRFeatureDefn *poMainDefn, *poRelDefn; + + poMainDefn = poMainTable->GetLayerDefn(); + poRelDefn = poRelTable->GetLayerDefn(); + + /*----------------------------------------------------------------- + * Keep info for later use about source tables, etc. + *----------------------------------------------------------------*/ + ResetAllMembers(); + + m_poMainTable = poMainTable; + if (pszMainFieldName) + { + m_pszMainFieldName = CPLStrdup(pszMainFieldName); + m_nMainFieldNo = poMainDefn->GetFieldIndex(pszMainFieldName); + } + + m_poRelTable = poRelTable; + if (pszRelFieldName) + { + m_pszRelFieldName = CPLStrdup(pszRelFieldName); + m_nRelFieldNo = poRelDefn->GetFieldIndex(pszRelFieldName); + m_nRelFieldIndexNo = poRelTable->GetFieldIndexNumber(m_nRelFieldNo); + m_poRelINDFileRef = poRelTable->GetINDFileRef(); + + if (m_nRelFieldIndexNo >= 0 && m_poRelINDFileRef == NULL) + { + CPLError(CE_Failure, CPLE_FileIO, + "Field %s is indexed but the .IND file is missing.", + pszRelFieldName); + return -1; + } + } + + /*----------------------------------------------------------------- + * Init field maps. For each field in each table, a -1 means that + * the field is not selected, and a value >=0 is the index of the + * field in the view's FeatureDefn + *----------------------------------------------------------------*/ + int i; + int numFields1 = (poMainDefn?poMainDefn->GetFieldCount():0); + int numFields2 = (poRelDefn?poRelDefn->GetFieldCount():0); + + m_panMainTableFieldMap = (int*)CPLMalloc((numFields1+1)*sizeof(int)); + for(i=0; iGetFieldDefn(i); + + papszSelectedFields = CSLAddString(papszSelectedFields, + poFieldDefn->GetNameRef()); + } + + for(i=0; iGetFieldDefn(i); + + if (CSLFindString(papszSelectedFields, + poFieldDefn->GetNameRef()) != -1) + continue; // Avoid duplicate field name in view + + papszSelectedFields = CSLAddString(papszSelectedFields, + poFieldDefn->GetNameRef()); + } + + } + + /*----------------------------------------------------------------- + * Create new FeatureDefn and copy selected fields definitions + * while updating the appropriate field maps. + *----------------------------------------------------------------*/ + int nIndex, numSelFields = CSLCount(papszSelectedFields); + OGRFieldDefn *poFieldDefn; + + m_poDefn = new OGRFeatureDefn(pszViewName); + // Ref count defaults to 0... set it to 1 + m_poDefn->Reference(); + + for(i=0; iGetFieldIndex(papszSelectedFields[i])) >=0) + { + /* Field from the main table + */ + poFieldDefn = poMainDefn->GetFieldDefn(nIndex); + m_poDefn->AddFieldDefn(poFieldDefn); + m_panMainTableFieldMap[nIndex] = m_poDefn->GetFieldCount()-1; + } + else if (poRelDefn && + (nIndex=poRelDefn->GetFieldIndex(papszSelectedFields[i]))>=0) + { + /* Field from the related table + */ + poFieldDefn = poRelDefn->GetFieldDefn(nIndex); + m_poDefn->AddFieldDefn(poFieldDefn); + m_panRelTableFieldMap[nIndex] = m_poDefn->GetFieldCount()-1; + } + else + { + // Hummm... field does not exist... likely an unsupported feature! + // At least send a warning and ignore the field. + CPLError(CE_Warning, CPLE_IllegalArg, + "Selected Field %s not found in source tables %s and %s", + papszSelectedFields[i], + poMainDefn->GetName(), poRelDefn->GetName()); + } + } + + return 0; +} + + +/********************************************************************** + * TABRelation::CreateRelFields() + * + * For write access, create the integer fields in each table that will + * link them, and setup everything to be ready to write the first feature. + * + * This function should be called just before writing the first feature. + * + * Returns 0 on success, or -1 or error. + **********************************************************************/ +int TABRelation::CreateRelFields() +{ + int i; + + /*----------------------------------------------------------------- + * Create the field in each table. + * The default name is "MI_refnum" but if a field with the same name + * already exists then we'll try to generate a unique name. + *----------------------------------------------------------------*/ + m_pszMainFieldName = CPLStrdup("MI_Refnum "); + strcpy(m_pszMainFieldName, "MI_Refnum"); + i = 1; + while(m_poDefn->GetFieldIndex(m_pszMainFieldName) >= 0) + { + sprintf(m_pszMainFieldName, "MI_Refnum_%d", i++); + } + m_pszRelFieldName = CPLStrdup(m_pszMainFieldName); + + m_nMainFieldNo = m_nRelFieldNo = -1; + if (m_poMainTable->AddFieldNative(m_pszMainFieldName, + TABFInteger, 0, 0) == 0) + m_nMainFieldNo = m_poMainTable->GetLayerDefn()->GetFieldCount()-1; + + if (m_poRelTable->AddFieldNative(m_pszRelFieldName, + TABFInteger, 0, 0) == 0) + m_nRelFieldNo = m_poRelTable->GetLayerDefn()->GetFieldCount()-1; + + if (m_nMainFieldNo == -1 || m_nRelFieldNo == -1) + return -1; + + if (m_poMainTable->SetFieldIndexed(m_nMainFieldNo) == -1) + return -1; + + if ((m_nRelFieldIndexNo=m_poRelTable->SetFieldIndexed(m_nRelFieldNo)) ==-1) + return -1; + + m_poRelINDFileRef = m_poRelTable->GetINDFileRef(); + + /*----------------------------------------------------------------- + * Update field maps + *----------------------------------------------------------------*/ + OGRFeatureDefn *poMainDefn, *poRelDefn; + + poMainDefn = m_poMainTable->GetLayerDefn(); + poRelDefn = m_poRelTable->GetLayerDefn(); + + m_panMainTableFieldMap = (int*)CPLRealloc(m_panMainTableFieldMap, + poMainDefn->GetFieldCount()*sizeof(int)); + m_panMainTableFieldMap[poMainDefn->GetFieldCount()-1] = -1; + + m_panRelTableFieldMap = (int*)CPLRealloc(m_panRelTableFieldMap, + poRelDefn->GetFieldCount()*sizeof(int)); + m_panRelTableFieldMap[poRelDefn->GetFieldCount()-1] = -1; + + /*----------------------------------------------------------------- + * Make sure the first unique field (in poRelTable) is indexed since + * it is the one against which we will try to match records. + *----------------------------------------------------------------*/ + if ( m_poRelTable->SetFieldIndexed(0) == -1) + return -1; + + return 0; +} + +/********************************************************************** + * TABRelation::GetFeature() + * + * Fill and return a TABFeature object for the specified feature id. + * + * The retuned pointer is a new TABFeature that will have to be freed + * by the caller. + * + * Returns NULL if the specified feature id does not exist of if an + * error happened. In any case, CPLError() will have been called to + * report the reason of the failure. + * + * __TODO__ The current implementation fetches the features from each table + * and creates a 3rd feature to merge them. There would be room for + * optimization, at least by avoiding the duplication of the geometry + * which can be big sometimes... but this would imply changes at the + * lower-level in the lib. and we won't go there yet. + **********************************************************************/ +TABFeature *TABRelation::GetFeature(int nFeatureId) +{ + TABFeature *poMainFeature; + TABFeature *poCurFeature; + + /*----------------------------------------------------------------- + * Make sure init() has been called + *----------------------------------------------------------------*/ + if (m_poMainTable == NULL || m_poRelTable == NULL) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "GetFeatureRef() failed: object not initialized yet!"); + return NULL; + } + + /*----------------------------------------------------------------- + * Read main feature and create a new one of the right type + *----------------------------------------------------------------*/ + if ((poMainFeature = m_poMainTable->GetFeatureRef(nFeatureId)) == NULL) + { + // Feature cannot be read from main table... + // an error has already been reported. + return NULL; + } + + poCurFeature = poMainFeature->CloneTABFeature(m_poDefn); + + /*----------------------------------------------------------------- + * Keep track of FID and copy the geometry + *----------------------------------------------------------------*/ + poCurFeature->SetFID(nFeatureId); + + if (poCurFeature->GetFeatureClass() != TABFCNoGeomFeature) + { + OGRGeometry *poGeom; + poGeom = poMainFeature->GetGeometryRef(); + poCurFeature->SetGeometry(poGeom); + } + + /*----------------------------------------------------------------- + * Fetch feature from related table + * + * __TODO__ Right now we support only many-to-1 relationships, but + * it might be possible to have several related entries + * for a single key, and in this case we should return + * one new feature for each of them. + *----------------------------------------------------------------*/ + TABFeature *poRelFeature=NULL; + GByte *pKey = BuildFieldKey(poMainFeature, m_nMainFieldNo, + m_poMainTable->GetNativeFieldType(m_nMainFieldNo), + m_nRelFieldIndexNo); + int i; + int nRelFeatureId = m_poRelINDFileRef->FindFirst(m_nRelFieldIndexNo, pKey); + + if (nRelFeatureId > 0) + poRelFeature = m_poRelTable->GetFeatureRef(nRelFeatureId); + + /*----------------------------------------------------------------- + * Copy fields from poMainFeature + *----------------------------------------------------------------*/ + for(i=0; iGetFieldCount(); i++) + { + if (m_panMainTableFieldMap[i] != -1) + { + poCurFeature->SetField(m_panMainTableFieldMap[i], + poMainFeature->GetRawFieldRef(i)); + } + } + + /*----------------------------------------------------------------- + * Copy fields from poRelFeature... + * + * NOTE: For now, if no corresponding feature is found in RelTable + * then we will just leave the corresponding fields unset. + *----------------------------------------------------------------*/ + for(i=0; poRelFeature && iGetFieldCount(); i++) + { + if (m_panRelTableFieldMap[i] != -1) + { + poCurFeature->SetField(m_panRelTableFieldMap[i], + poRelFeature->GetRawFieldRef(i)); + } + } + + return poCurFeature; +} + + + +/********************************************************************** + * TABRelation::BuildFieldKey() + * + * Return the index key for the specified field in poFeature. + * Simply maps the call to the proper method in the TABINDFile class. + * + * Returns a reference to a TABINDFile internal buffer that should not + * be freed by the caller. + **********************************************************************/ +GByte *TABRelation::BuildFieldKey(TABFeature *poFeature, int nFieldNo, + TABFieldType eType, int nIndexNo) +{ + GByte *pKey = NULL; + + switch(eType) + { + case TABFChar: + pKey = m_poRelINDFileRef->BuildKey(nIndexNo, + poFeature->GetFieldAsString(nFieldNo)); + break; + + case TABFDecimal: + case TABFFloat: + pKey = m_poRelINDFileRef->BuildKey(nIndexNo, + poFeature->GetFieldAsDouble(nFieldNo)); + break; + + // __TODO__ DateTime fields are 8 bytes long, not supported yet by + // the indexing code (see bug #1844). + case TABFDateTime: + CPLError(CE_Failure, CPLE_NotSupported, + "TABRelation on field of type DateTime not supported yet."); + break; + + case TABFInteger: + case TABFSmallInt: + case TABFDate: + case TABFTime: + case TABFLogical: + default: + pKey = m_poRelINDFileRef->BuildKey(nIndexNo, + poFeature->GetFieldAsInteger(nFieldNo)); + break; + } + + return pKey; +} + + +/********************************************************************** + * TABRelation::GetNativeFieldType() + * + * Returns the native MapInfo field type for the specified field. + * + * Returns TABFUnknown if file is not opened, or if specified field index is + * invalid. + * + * Note that field ids are positive and start at 0. + **********************************************************************/ +TABFieldType TABRelation::GetNativeFieldType(int nFieldId) +{ + int i, numFields; + + if (m_poMainTable==NULL || m_poRelTable==NULL || + m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL) + return TABFUnknown; + + /*----------------------------------------------------------------- + * Look for nFieldId in the field maps and call the corresponding + * TAB file's GetNativeFieldType() + *----------------------------------------------------------------*/ + numFields = m_poMainTable->GetLayerDefn()->GetFieldCount(); + for(i=0; iGetNativeFieldType(i); + } + } + + numFields = m_poRelTable->GetLayerDefn()->GetFieldCount(); + for(i=0; iGetNativeFieldType(i); + } + } + + return TABFUnknown; +} + + +/********************************************************************** + * TABRelation::AddFieldNative() + * + * Create a new field using a native mapinfo data type... this is an + * alternative to defining fields through the OGR interface. + * This function should be called after creating a new dataset, but before + * writing the first feature. + * + * This function will build/update the OGRFeatureDefn that will have to be + * used when writing features to this dataset. + * + * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn(). + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABRelation::AddFieldNative(const char *pszName, TABFieldType eMapInfoType, + int nWidth /*=0*/, int nPrecision /*=0*/, + GBool bIndexed /*=FALSE*/, GBool bUnique/*=FALSE*/, int bApproxOK) +{ + if (m_poMainTable==NULL || m_poRelTable==NULL || + m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL) + return -1; + + if (!bUnique) + { + /*------------------------------------------------------------- + * Add field to poMainTable and to m_poDefn + *------------------------------------------------------------*/ + if (m_poMainTable->AddFieldNative(pszName, eMapInfoType, + nWidth, nPrecision, + bIndexed, bUnique, bApproxOK) != 0) + return -1; + + OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn(); + + m_panMainTableFieldMap = (int*)CPLRealloc(m_panMainTableFieldMap, + poMainDefn->GetFieldCount()*sizeof(int)); + + m_poDefn->AddFieldDefn(poMainDefn->GetFieldDefn(poMainDefn-> + GetFieldCount()-1)); + + m_panMainTableFieldMap[poMainDefn->GetFieldCount()-1] = + m_poDefn->GetFieldCount()-1; + } + else + { + /*------------------------------------------------------------- + * Add field to poRelTable and to m_poDefn + *------------------------------------------------------------*/ + if (m_poRelTable->AddFieldNative(pszName, eMapInfoType, + nWidth, nPrecision, + bIndexed, bUnique, bApproxOK) != 0) + return -1; + + OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn(); + + m_panRelTableFieldMap = (int*)CPLRealloc(m_panRelTableFieldMap, + poRelDefn->GetFieldCount()*sizeof(int)); + + m_poDefn->AddFieldDefn(poRelDefn->GetFieldDefn(poRelDefn-> + GetFieldCount()-1)); + + m_panRelTableFieldMap[poRelDefn->GetFieldCount()-1] = + m_poDefn->GetFieldCount()-1; + + // The first field in this table must be indexed. + if (poRelDefn->GetFieldCount() == 1) + m_poRelTable->SetFieldIndexed(0); + } + + return 0; +} + + +/********************************************************************** + * TABRelation::IsFieldIndexed() + * + * Returns TRUE is specified field is indexed. + * + * Note that field ids are positive and start at 0. + **********************************************************************/ +GBool TABRelation::IsFieldIndexed(int nFieldId) +{ + int i, numFields; + + if (m_poMainTable==NULL || m_poRelTable==NULL || + m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL) + return FALSE; + + /*----------------------------------------------------------------- + * Look for nFieldId in the field maps and call the corresponding + * TAB file's GetNativeFieldType() + *----------------------------------------------------------------*/ + numFields = m_poMainTable->GetLayerDefn()->GetFieldCount(); + for(i=0; iIsFieldIndexed(i); + } + } + + numFields = m_poRelTable->GetLayerDefn()->GetFieldCount(); + for(i=0; iIsFieldIndexed(i); + } + } + + return FALSE; +} + +/********************************************************************** + * TABRelation::SetFieldIndexed() + * + * Request that the specified field be indexed. This will create the .IND + * file, etc. + * + * Note that field ids are positive and start at 0. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABRelation::SetFieldIndexed(int nFieldId) +{ + int i, numFields; + + if (m_poMainTable==NULL || m_poRelTable==NULL || + m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL) + return -1; + + /*----------------------------------------------------------------- + * Look for nFieldId in the field maps and call the corresponding + * TAB file's GetNativeFieldType() + *----------------------------------------------------------------*/ + numFields = m_poMainTable->GetLayerDefn()->GetFieldCount(); + for(i=0; iSetFieldIndexed(i); + } + } + + numFields = m_poRelTable->GetLayerDefn()->GetFieldCount(); + for(i=0; iSetFieldIndexed(i); + } + } + + return -1; +} + +/********************************************************************** + * TABRelation::IsFieldUnique() + * + * Returns TRUE is specified field is part of the unique table (poRelTable). + * + * Note that field ids are positive and start at 0. + **********************************************************************/ +GBool TABRelation::IsFieldUnique(int nFieldId) +{ + int i, numFields; + + if (m_poMainTable==NULL || m_poRelTable==NULL || + m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL) + return FALSE; + + /*----------------------------------------------------------------- + * Look for nFieldId in the poRelTable field map + *----------------------------------------------------------------*/ + numFields = m_poRelTable->GetLayerDefn()->GetFieldCount(); + for(i=0; i 0) on success, or -1 if an + * error happened in which case, CPLError() will have been called to + * report the reason of the failure. + **********************************************************************/ +int TABRelation::WriteFeature(TABFeature *poFeature, int nFeatureId /*=-1*/) +{ + TABFeature *poMainFeature=NULL; + + if (nFeatureId != -1) + { + CPLError(CE_Failure, CPLE_NotSupported, + "WriteFeature(): random access not implemented yet."); + return -1; + } + + CPLAssert(m_poMainTable && m_poRelTable); + + // We'll need the feature Defn later... + OGRFeatureDefn *poMainDefn, *poRelDefn; + + poMainDefn = m_poMainTable->GetLayerDefn(); + poRelDefn = m_poRelTable->GetLayerDefn(); + + /*----------------------------------------------------------------- + * Create one feature for each table + * Copy the geometry only to the feature from the main table + *----------------------------------------------------------------*/ + poMainFeature = poFeature->CloneTABFeature(poMainDefn); + + if (poFeature->GetFeatureClass() != TABFCNoGeomFeature) + { + OGRGeometry *poGeom; + poGeom = poFeature->GetGeometryRef(); + poMainFeature->SetGeometry(poGeom); + } + + /*----------------------------------------------------------------- + * Copy fields to poMainFeature + *----------------------------------------------------------------*/ + for(int i=0; iGetFieldCount(); i++) + { + if (m_panMainTableFieldMap[i] != -1) + { + poMainFeature->SetField(i, + poFeature->GetRawFieldRef(m_panMainTableFieldMap[i])); + } + } + + /*----------------------------------------------------------------- + * Look for a record id for the unique fields, and write a new + * record if necessary + *----------------------------------------------------------------*/ + int nRecordNo = 0; + int nUniqueIndexNo=-1; + if (m_panMainTableFieldMap[0] != -1) + nUniqueIndexNo =m_poRelTable->GetFieldIndexNumber( 0 ); + + if (nUniqueIndexNo > 0) + { + GByte *pKey = BuildFieldKey(poFeature, 0, + m_poRelTable->GetNativeFieldType(0), + nUniqueIndexNo); + + if ((nRecordNo=m_poRelINDFileRef->FindFirst(nUniqueIndexNo, pKey))==-1) + return -1; + + if (nRecordNo == 0) + { + /*--------------------------------------------------------- + * No record in poRelTable yet for this unique value... + * add one now... + *--------------------------------------------------------*/ + TABFeature *poRelFeature = new TABFeature(poRelDefn); + + for(int i=0; iGetFieldCount(); i++) + { + if (m_panRelTableFieldMap[i] != -1) + { + poRelFeature->SetField(i, + poFeature->GetRawFieldRef(m_panRelTableFieldMap[i])); + } + } + + nRecordNo = ++m_nUniqueRecordNo; + + poRelFeature->SetField(m_nRelFieldNo, nRecordNo); + + if (m_poRelTable->CreateFeature(poRelFeature) == OGRERR_NONE) + return -1; + + delete poRelFeature; + } + } + + + /*----------------------------------------------------------------- + * Write poMainFeature to the main table + *----------------------------------------------------------------*/ + poMainFeature->SetField(m_nMainFieldNo, nRecordNo); + + if (m_poMainTable->CreateFeature(poMainFeature) != OGRERR_NONE) + nFeatureId = (int) poMainFeature->GetFID(); + else + nFeatureId = -1; + + delete poMainFeature; + + return nFeatureId; +} + + +/********************************************************************** + * TABFile::SetFeatureDefn() + * + * NOT FULLY IMPLEMENTED YET... + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABRelation::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn, + CPL_UNUSED TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */) +{ + if (m_poDefn && m_poDefn->GetFieldCount() > 0) + { + CPLAssert(m_poDefn==NULL); + return -1; + } + + /*----------------------------------------------------------------- + * Keep a reference to the OGRFeatureDefn... we'll have to take the + * reference count into account when we are done with it. + *----------------------------------------------------------------*/ + if (m_poDefn && m_poDefn->Dereference() == 0) + delete m_poDefn; + + m_poDefn = poFeatureDefn; + m_poDefn->Reference(); + + return 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_tooldef.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_tooldef.cpp new file mode 100644 index 000000000..bf7847f8b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_tooldef.cpp @@ -0,0 +1,751 @@ +/********************************************************************** + * $Id: mitab_tooldef.cpp,v 1.7 2010-07-07 19:00:15 aboudreault Exp $ + * + * Name: mitab_tooldef.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Implementation of the TABToolDefTable class used to handle + * a dataset's table of drawing tool blocks + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999, 2000, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_tooldef.cpp,v $ + * Revision 1.7 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.6 2004-06-30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.5 2000/11/15 04:13:50 daniel + * Fixed writing of TABMAPToolBlock to allocate a new block when full + * + * Revision 1.4 2000/02/28 17:06:54 daniel + * Support pen width in points and V450 check + * + * Revision 1.3 2000/01/15 22:30:45 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.2 1999/10/18 15:39:21 daniel + * Handle case of "no pen" or "no brush" in AddPen/BrushRef() + * + * Revision 1.1 1999/09/26 14:59:37 daniel + * Implemented write support + * + **********************************************************************/ + +#include "mitab.h" +#include "mitab_utils.h" + +/*===================================================================== + * class TABToolDefTable + *====================================================================*/ + +/********************************************************************** + * TABToolDefTable::TABToolDefTable() + * + * Constructor. + **********************************************************************/ +TABToolDefTable::TABToolDefTable() +{ + m_papsPen = NULL; + m_papsBrush = NULL; + m_papsFont = NULL; + m_papsSymbol = NULL; + m_numPen = 0; + m_numBrushes = 0; + m_numFonts = 0; + m_numSymbols = 0; + m_numAllocatedPen = 0; + m_numAllocatedBrushes = 0; + m_numAllocatedFonts = 0; + m_numAllocatedSymbols = 0; + +} + +/********************************************************************** + * TABToolDefTable::~TABToolDefTable() + * + * Destructor. + **********************************************************************/ +TABToolDefTable::~TABToolDefTable() +{ + int i; + + for(i=0; m_papsPen && i < m_numPen; i++) + CPLFree(m_papsPen[i]); + CPLFree(m_papsPen); + + for(i=0; m_papsBrush && i < m_numBrushes; i++) + CPLFree(m_papsBrush[i]); + CPLFree(m_papsBrush); + + for(i=0; m_papsFont && i < m_numFonts; i++) + CPLFree(m_papsFont[i]); + CPLFree(m_papsFont); + + for(i=0; m_papsSymbol && i < m_numSymbols; i++) + CPLFree(m_papsSymbol[i]); + CPLFree(m_papsSymbol); + +} + + +/********************************************************************** + * TABToolDefTable::ReadAllToolDefs() + * + * Read all tool definition blocks until we reach the end of the chain. + * This function will be called only once per dataset, after that + * we keep all the tool definitions in memory. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABToolDefTable::ReadAllToolDefs(TABMAPToolBlock *poBlock) +{ + int nStatus = 0; + int nDefType; + + /*----------------------------------------------------------------- + * Loop until we reach the end of the chain of blocks... we assume + * that the first block of data is already pre-loaded. + *----------------------------------------------------------------*/ + while( ! poBlock->EndOfChain() ) + { + nDefType = poBlock->ReadByte(); + switch(nDefType) + { + case TABMAP_TOOL_PEN: // PEN + if (m_numPen >= m_numAllocatedPen) + { + // Realloc array by blocks of 20 items + m_numAllocatedPen += 20; + m_papsPen = (TABPenDef**)CPLRealloc(m_papsPen, + m_numAllocatedPen*sizeof(TABPenDef*)); + } + m_papsPen[m_numPen] = (TABPenDef*)CPLCalloc(1, sizeof(TABPenDef)); + + m_papsPen[m_numPen]->nRefCount = poBlock->ReadInt32(); + m_papsPen[m_numPen]->nPixelWidth = poBlock->ReadByte(); + m_papsPen[m_numPen]->nLinePattern = poBlock->ReadByte(); + m_papsPen[m_numPen]->nPointWidth = poBlock->ReadByte(); + m_papsPen[m_numPen]->rgbColor = poBlock->ReadByte()*256*256+ + poBlock->ReadByte()*256 + + poBlock->ReadByte(); + + // Adjust width value... + // High bits for point width values > 255 are stored in the + // pixel width byte + if (m_papsPen[m_numPen]->nPixelWidth > 7) + { + m_papsPen[m_numPen]->nPointWidth += + (m_papsPen[m_numPen]->nPixelWidth-8)*0x100; + m_papsPen[m_numPen]->nPixelWidth = 1; + } + + m_numPen++; + + break; + case TABMAP_TOOL_BRUSH: // BRUSH + if (m_numBrushes >= m_numAllocatedBrushes) + { + // Realloc array by blocks of 20 items + m_numAllocatedBrushes += 20; + m_papsBrush = (TABBrushDef**)CPLRealloc(m_papsBrush, + m_numAllocatedBrushes*sizeof(TABBrushDef*)); + } + m_papsBrush[m_numBrushes] = + (TABBrushDef*)CPLCalloc(1,sizeof(TABBrushDef)); + + m_papsBrush[m_numBrushes]->nRefCount = poBlock->ReadInt32(); + m_papsBrush[m_numBrushes]->nFillPattern = poBlock->ReadByte(); + m_papsBrush[m_numBrushes]->bTransparentFill = poBlock->ReadByte(); + m_papsBrush[m_numBrushes]->rgbFGColor =poBlock->ReadByte()*256*256+ + poBlock->ReadByte()*256 + + poBlock->ReadByte(); + m_papsBrush[m_numBrushes]->rgbBGColor =poBlock->ReadByte()*256*256+ + poBlock->ReadByte()*256 + + poBlock->ReadByte(); + + m_numBrushes++; + + break; + case TABMAP_TOOL_FONT: // FONT NAME + if (m_numFonts >= m_numAllocatedFonts) + { + // Realloc array by blocks of 20 items + m_numAllocatedFonts += 20; + m_papsFont = (TABFontDef**)CPLRealloc(m_papsFont, + m_numAllocatedFonts*sizeof(TABFontDef*)); + } + m_papsFont[m_numFonts] = + (TABFontDef*)CPLCalloc(1,sizeof(TABFontDef)); + + m_papsFont[m_numFonts]->nRefCount = poBlock->ReadInt32(); + poBlock->ReadBytes(32, (GByte*)m_papsFont[m_numFonts]->szFontName); + m_papsFont[m_numFonts]->szFontName[32] = '\0'; + + m_numFonts++; + + break; + case TABMAP_TOOL_SYMBOL: // SYMBOL + if (m_numSymbols >= m_numAllocatedSymbols) + { + // Realloc array by blocks of 20 items + m_numAllocatedSymbols += 20; + m_papsSymbol = (TABSymbolDef**)CPLRealloc(m_papsSymbol, + m_numAllocatedSymbols*sizeof(TABSymbolDef*)); + } + m_papsSymbol[m_numSymbols] = + (TABSymbolDef*)CPLCalloc(1,sizeof(TABSymbolDef)); + + m_papsSymbol[m_numSymbols]->nRefCount = poBlock->ReadInt32(); + m_papsSymbol[m_numSymbols]->nSymbolNo = poBlock->ReadInt16(); + m_papsSymbol[m_numSymbols]->nPointSize = poBlock->ReadInt16(); + m_papsSymbol[m_numSymbols]->_nUnknownValue_ = poBlock->ReadByte(); + m_papsSymbol[m_numSymbols]->rgbColor = poBlock->ReadByte()*256*256+ + poBlock->ReadByte()*256 + + poBlock->ReadByte(); + + m_numSymbols++; + + break; + default: + /* Unsupported Tool type!!! */ + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported drawing tool type: `%d'", nDefType); + nStatus = -1; + } + + if (CPLGetLastErrorNo() != 0) + { + // An error happened reading this tool definition... stop now. + nStatus = -1; + } + } + + return nStatus; +} + + +/********************************************************************** + * TABToolDefTable::WriteAllToolDefs() + * + * Write all tool definition structures to the TABMAPToolBlock. + * + * Note that at the end of this call, poBlock->CommitToFile() will have + * been called. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABToolDefTable::WriteAllToolDefs(TABMAPToolBlock *poBlock) +{ + int i, nStatus = 0; + + /*----------------------------------------------------------------- + * Write Pen Defs + *----------------------------------------------------------------*/ + for(i=0; nStatus == 0 && i< m_numPen; i++) + { + // The pen width is encoded over 2 bytes + GByte byPixelWidth=1, byPointWidth=0; + if (m_papsPen[i]->nPointWidth > 0) + { + byPointWidth = (GByte)(m_papsPen[i]->nPointWidth & 0xff); + if (m_papsPen[i]->nPointWidth > 255) + byPixelWidth = 8 + (GByte)(m_papsPen[i]->nPointWidth/0x100); + } + else + byPixelWidth = MIN(MAX(m_papsPen[i]->nPixelWidth, 1), 7); + + poBlock->CheckAvailableSpace(TABMAP_TOOL_PEN); + poBlock->WriteByte(TABMAP_TOOL_PEN); // Def Type = Pen + poBlock->WriteInt32(m_papsPen[i]->nRefCount); + + poBlock->WriteByte(byPixelWidth); + poBlock->WriteByte(m_papsPen[i]->nLinePattern); + poBlock->WriteByte(byPointWidth); + poBlock->WriteByte((GByte)COLOR_R(m_papsPen[i]->rgbColor)); + poBlock->WriteByte((GByte)COLOR_G(m_papsPen[i]->rgbColor)); + poBlock->WriteByte((GByte)COLOR_B(m_papsPen[i]->rgbColor)); + + if (CPLGetLastErrorNo() != 0) + { + // An error happened reading this tool definition... stop now. + nStatus = -1; + } + } + + /*----------------------------------------------------------------- + * Write Brush Defs + *----------------------------------------------------------------*/ + for(i=0; nStatus == 0 && i< m_numBrushes; i++) + { + poBlock->CheckAvailableSpace(TABMAP_TOOL_BRUSH); + + poBlock->WriteByte(TABMAP_TOOL_BRUSH); // Def Type = Brush + poBlock->WriteInt32(m_papsBrush[i]->nRefCount); + + poBlock->WriteByte(m_papsBrush[i]->nFillPattern); + poBlock->WriteByte(m_papsBrush[i]->bTransparentFill); + poBlock->WriteByte((GByte)COLOR_R(m_papsBrush[i]->rgbFGColor)); + poBlock->WriteByte((GByte)COLOR_G(m_papsBrush[i]->rgbFGColor)); + poBlock->WriteByte((GByte)COLOR_B(m_papsBrush[i]->rgbFGColor)); + poBlock->WriteByte((GByte)COLOR_R(m_papsBrush[i]->rgbBGColor)); + poBlock->WriteByte((GByte)COLOR_G(m_papsBrush[i]->rgbBGColor)); + poBlock->WriteByte((GByte)COLOR_B(m_papsBrush[i]->rgbBGColor)); + + if (CPLGetLastErrorNo() != 0) + { + // An error happened reading this tool definition... stop now. + nStatus = -1; + } + } + + /*----------------------------------------------------------------- + * Write Font Defs + *----------------------------------------------------------------*/ + for(i=0; nStatus == 0 && i< m_numFonts; i++) + { + poBlock->CheckAvailableSpace(TABMAP_TOOL_FONT); + + poBlock->WriteByte(TABMAP_TOOL_FONT); // Def Type = Font name + poBlock->WriteInt32(m_papsFont[i]->nRefCount); + + poBlock->WriteBytes(32, (GByte*)m_papsFont[i]->szFontName); + + if (CPLGetLastErrorNo() != 0) + { + // An error happened reading this tool definition... stop now. + nStatus = -1; + } + } + + /*----------------------------------------------------------------- + * Write Symbol Defs + *----------------------------------------------------------------*/ + for(i=0; nStatus == 0 && i< m_numSymbols; i++) + { + poBlock->CheckAvailableSpace(TABMAP_TOOL_SYMBOL); + + poBlock->WriteByte(TABMAP_TOOL_SYMBOL); // Def Type = Symbol + poBlock->WriteInt32(m_papsSymbol[i]->nRefCount); + + poBlock->WriteInt16(m_papsSymbol[i]->nSymbolNo); + poBlock->WriteInt16(m_papsSymbol[i]->nPointSize); + poBlock->WriteByte(m_papsSymbol[i]->_nUnknownValue_); + poBlock->WriteByte((GByte)COLOR_R(m_papsSymbol[i]->rgbColor)); + poBlock->WriteByte((GByte)COLOR_G(m_papsSymbol[i]->rgbColor)); + poBlock->WriteByte((GByte)COLOR_B(m_papsSymbol[i]->rgbColor)); + + if (CPLGetLastErrorNo() != 0) + { + // An error happened reading this tool definition... stop now. + nStatus = -1; + } + } + + if (nStatus == 0) + nStatus = poBlock->CommitToFile(); + + return nStatus; +} + + + +/********************************************************************** + * TABToolDefTable::GetNumPen() + * + * Return the number of valid pen indexes for this .MAP file + **********************************************************************/ +int TABToolDefTable::GetNumPen() +{ + return m_numPen; +} + +/********************************************************************** + * TABToolDefTable::GetPenDefRef() + * + * Return a reference to the specified Pen tool definition, or NULL if + * specified index is invalid. + * + * Note that nIndex is a 1-based index. A value of 0 indicates "none" + * in MapInfo. + **********************************************************************/ +TABPenDef *TABToolDefTable::GetPenDefRef(int nIndex) +{ + if (nIndex >0 && nIndex <= m_numPen) + return m_papsPen[nIndex-1]; + + return NULL; +} + +/********************************************************************** + * TABToolDefTable::AddPenDefRef() + * + * Either create a new PenDefRef or add a reference to an existing one. + * + * Return the pen index that has been attributed to this Pen tool + * definition, or -1 if something went wrong + * + * Note that nIndex is a 1-based index. A value of 0 indicates "none" + * in MapInfo. + **********************************************************************/ +int TABToolDefTable::AddPenDefRef(TABPenDef *poNewPenDef) +{ + int i, nNewPenIndex = 0; + TABPenDef *poDef; + + if (poNewPenDef == NULL) + return -1; + + /*----------------------------------------------------------------- + * Check for "none" case: pattern = 0 (pattern 0 does not exist!) + *----------------------------------------------------------------*/ + if (poNewPenDef->nLinePattern < 1) + return 0; + + /*----------------------------------------------------------------- + * Start by searching the list of existing pens + *----------------------------------------------------------------*/ + for (i=0; nNewPenIndex == 0 && inPixelWidth == poNewPenDef->nPixelWidth && + poDef->nLinePattern == poNewPenDef->nLinePattern && + poDef->nPointWidth == poNewPenDef->nPointWidth && + poDef->rgbColor == poNewPenDef->rgbColor) + { + nNewPenIndex = i+1; // Fount it! + poDef->nRefCount++; + } + } + + /*----------------------------------------------------------------- + * OK, we did not find a match, then create a new entry + *----------------------------------------------------------------*/ + if (nNewPenIndex == 0) + { + if (m_numPen >= m_numAllocatedPen) + { + // Realloc array by blocks of 20 items + m_numAllocatedPen += 20; + m_papsPen = (TABPenDef**)CPLRealloc(m_papsPen, + m_numAllocatedPen*sizeof(TABPenDef*)); + } + m_papsPen[m_numPen] = (TABPenDef*)CPLCalloc(1, sizeof(TABPenDef)); + + *m_papsPen[m_numPen] = *poNewPenDef; + m_papsPen[m_numPen]->nRefCount = 1; + nNewPenIndex = ++m_numPen; + } + + return nNewPenIndex; +} + +/********************************************************************** + * TABToolDefTable::GetNumBrushes() + * + * Return the number of valid Brush indexes for this .MAP file + **********************************************************************/ +int TABToolDefTable::GetNumBrushes() +{ + return m_numBrushes; +} + +/********************************************************************** + * TABToolDefTable::GetBrushDefRef() + * + * Return a reference to the specified Brush tool definition, or NULL if + * specified index is invalid. + * + * Note that nIndex is a 1-based index. A value of 0 indicates "none" + * in MapInfo. + **********************************************************************/ +TABBrushDef *TABToolDefTable::GetBrushDefRef(int nIndex) +{ + if (nIndex >0 && nIndex <= m_numBrushes) + return m_papsBrush[nIndex-1]; + + return NULL; +} + +/********************************************************************** + * TABToolDefTable::AddBrushDefRef() + * + * Either create a new BrushDefRef or add a reference to an existing one. + * + * Return the Brush index that has been attributed to this Brush tool + * definition, or -1 if something went wrong + * + * Note that nIndex is a 1-based index. A value of 0 indicates "none" + * in MapInfo. + **********************************************************************/ +int TABToolDefTable::AddBrushDefRef(TABBrushDef *poNewBrushDef) +{ + int i, nNewBrushIndex = 0; + TABBrushDef *poDef; + + if (poNewBrushDef == NULL) + return -1; + + /*----------------------------------------------------------------- + * Check for "none" case: pattern = 0 (pattern 0 does not exist!) + *----------------------------------------------------------------*/ + if (poNewBrushDef->nFillPattern < 1) + return 0; + + /*----------------------------------------------------------------- + * Start by searching the list of existing Brushs + *----------------------------------------------------------------*/ + for (i=0; nNewBrushIndex == 0 && inFillPattern == poNewBrushDef->nFillPattern && + poDef->bTransparentFill == poNewBrushDef->bTransparentFill && + poDef->rgbFGColor == poNewBrushDef->rgbFGColor && + poDef->rgbBGColor == poNewBrushDef->rgbBGColor) + { + nNewBrushIndex = i+1; // Fount it! + poDef->nRefCount++; + } + } + + /*----------------------------------------------------------------- + * OK, we did not find a match, then create a new entry + *----------------------------------------------------------------*/ + if (nNewBrushIndex == 0) + { + if (m_numBrushes >= m_numAllocatedBrushes) + { + // Realloc array by blocks of 20 items + m_numAllocatedBrushes += 20; + m_papsBrush = (TABBrushDef**)CPLRealloc(m_papsBrush, + m_numAllocatedBrushes*sizeof(TABBrushDef*)); + } + m_papsBrush[m_numBrushes]=(TABBrushDef*)CPLCalloc(1, + sizeof(TABBrushDef)); + + *m_papsBrush[m_numBrushes] = *poNewBrushDef; + m_papsBrush[m_numBrushes]->nRefCount = 1; + nNewBrushIndex = ++m_numBrushes; + } + + return nNewBrushIndex; +} + +/********************************************************************** + * TABToolDefTable::GetNumFonts() + * + * Return the number of valid Font indexes for this .MAP file + **********************************************************************/ +int TABToolDefTable::GetNumFonts() +{ + return m_numFonts; +} + +/********************************************************************** + * TABToolDefTable::GetFontDefRef() + * + * Return a reference to the specified Font tool definition, or NULL if + * specified index is invalid. + * + * Note that nIndex is a 1-based index. A value of 0 indicates "none" + * in MapInfo. + **********************************************************************/ +TABFontDef *TABToolDefTable::GetFontDefRef(int nIndex) +{ + if (nIndex >0 && nIndex <= m_numFonts) + return m_papsFont[nIndex-1]; + + return NULL; +} + +/********************************************************************** + * TABToolDefTable::AddFontDefRef() + * + * Either create a new FontDefRef or add a reference to an existing one. + * + * Return the Font index that has been attributed to this Font tool + * definition, or -1 if something went wrong + * + * Note that nIndex is a 1-based index. A value of 0 indicates "none" + * in MapInfo. + **********************************************************************/ +int TABToolDefTable::AddFontDefRef(TABFontDef *poNewFontDef) +{ + int i, nNewFontIndex = 0; + TABFontDef *poDef; + + if (poNewFontDef == NULL) + return -1; + + /*----------------------------------------------------------------- + * Start by searching the list of existing Fonts + *----------------------------------------------------------------*/ + for (i=0; nNewFontIndex == 0 && iszFontName, poNewFontDef->szFontName)) + { + nNewFontIndex = i+1; // Fount it! + poDef->nRefCount++; + } + } + + /*----------------------------------------------------------------- + * OK, we did not find a match, then create a new entry + *----------------------------------------------------------------*/ + if (nNewFontIndex == 0) + { + if (m_numFonts >= m_numAllocatedFonts) + { + // Realloc array by blocks of 20 items + m_numAllocatedFonts += 20; + m_papsFont = (TABFontDef**)CPLRealloc(m_papsFont, + m_numAllocatedFonts*sizeof(TABFontDef*)); + } + m_papsFont[m_numFonts]=(TABFontDef*)CPLCalloc(1, + sizeof(TABFontDef)); + + *m_papsFont[m_numFonts] = *poNewFontDef; + m_papsFont[m_numFonts]->nRefCount = 1; + nNewFontIndex = ++m_numFonts; + } + + return nNewFontIndex; +} + +/********************************************************************** + * TABToolDefTable::GetNumSymbols() + * + * Return the number of valid Symbol indexes for this .MAP file + **********************************************************************/ +int TABToolDefTable::GetNumSymbols() +{ + return m_numSymbols; +} + +/********************************************************************** + * TABToolDefTable::GetSymbolDefRef() + * + * Return a reference to the specified Symbol tool definition, or NULL if + * specified index is invalid. + * + * Note that nIndex is a 1-based index. A value of 0 indicates "none" + * in MapInfo. + **********************************************************************/ +TABSymbolDef *TABToolDefTable::GetSymbolDefRef(int nIndex) +{ + if (nIndex >0 && nIndex <= m_numSymbols) + return m_papsSymbol[nIndex-1]; + + return NULL; +} + + +/********************************************************************** + * TABToolDefTable::AddSymbolDefRef() + * + * Either create a new SymbolDefRef or add a reference to an existing one. + * + * Return the Symbol index that has been attributed to this Symbol tool + * definition, or -1 if something went wrong + * + * Note that nIndex is a 1-based index. A value of 0 indicates "none" + * in MapInfo. + **********************************************************************/ +int TABToolDefTable::AddSymbolDefRef(TABSymbolDef *poNewSymbolDef) +{ + int i, nNewSymbolIndex = 0; + TABSymbolDef *poDef; + + if (poNewSymbolDef == NULL) + return -1; + + /*----------------------------------------------------------------- + * Start by searching the list of existing Symbols + *----------------------------------------------------------------*/ + for (i=0; nNewSymbolIndex == 0 && inSymbolNo == poNewSymbolDef->nSymbolNo && + poDef->nPointSize == poNewSymbolDef->nPointSize && + poDef->_nUnknownValue_ == poNewSymbolDef->_nUnknownValue_ && + poDef->rgbColor == poNewSymbolDef->rgbColor ) + { + nNewSymbolIndex = i+1; // Fount it! + poDef->nRefCount++; + } + } + + /*----------------------------------------------------------------- + * OK, we did not find a match, then create a new entry + *----------------------------------------------------------------*/ + if (nNewSymbolIndex == 0) + { + if (m_numSymbols >= m_numAllocatedSymbols) + { + // Realloc array by blocks of 20 items + m_numAllocatedSymbols += 20; + m_papsSymbol = (TABSymbolDef**)CPLRealloc(m_papsSymbol, + m_numAllocatedSymbols*sizeof(TABSymbolDef*)); + } + m_papsSymbol[m_numSymbols]=(TABSymbolDef*)CPLCalloc(1, + sizeof(TABSymbolDef)); + + *m_papsSymbol[m_numSymbols] = *poNewSymbolDef; + m_papsSymbol[m_numSymbols]->nRefCount = 1; + nNewSymbolIndex = ++m_numSymbols; + } + + return nNewSymbolIndex; +} + + +/********************************************************************** + * TABToolDefTable::GetMinVersionNumber() + * + * Returns the minimum file version number that can accept all the + * tool objects currently defined. + * + * Default is 300, and currently 450 can be returned if file contains + * pen widths defined in points. + **********************************************************************/ +int TABToolDefTable::GetMinVersionNumber() +{ + int i, nVersion = 300; + + /*----------------------------------------------------------------- + * Scan Pen Defs + *----------------------------------------------------------------*/ + for(i=0; i< m_numPen; i++) + { + if (m_papsPen[i]->nPointWidth > 0 ) + { + nVersion = MAX(nVersion, 450); // Raise version to 450 + } + } + + return nVersion; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_utils.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_utils.cpp new file mode 100644 index 000000000..16afa3ab0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_utils.cpp @@ -0,0 +1,763 @@ +/********************************************************************** + * $Id: mitab_utils.cpp,v 1.26 2011-06-16 15:53:12 fwarmerdam Exp $ + * + * Name: mitab_utils.cpp + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Misc. util. functions for the library + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2001, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_utils.cpp,v $ + * Revision 1.26 2011-06-16 15:53:12 fwarmerdam + * improve TABBasename() for filenames with an embedded dot (gdal #4123) + * + * Revision 1.25 2010-07-07 19:00:15 aboudreault + * Cleanup Win32 Compile Warnings (GDAL bug #2930) + * + * Revision 1.24 2010-07-05 17:41:07 aboudreault + * Fixed TABCleanFieldName() function should allow char '#' in field name (bug 2231) + * + * Revision 1.23 2010-01-07 20:39:12 aboudreault + * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141) + * + * Revision 1.22 2008-07-21 16:04:58 dmorissette + * Fixed const char * warnings with GCC 4.3 (GDAL ticket #2325) + * + * Revision 1.21 2006/12/01 16:53:15 dmorissette + * Wrapped stuff with !defined(WIN32CE) (done by mloskot in OGR) + * + * Revision 1.20 2005/08/07 21:02:14 fwarmerdam + * avoid warnings about testing for characters > 255. + * + * Revision 1.19 2004/06/30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.18 2002/08/28 14:19:22 warmerda + * fix TABGetBasename() for mixture of path divider types like 'mi/abc\def.tab' + * + * Revision 1.17 2001/06/27 19:52:54 warmerda + * avoid multi byte support if _WIN32 and unix defined for cygwin support + * + * Revision 1.16 2001/01/23 21:23:42 daniel + * Added projection bounds lookup table, called from TABFile::SetProjInfo() + * + * Revision 1.15 2001/01/19 06:06:18 daniel + * Don't filter chars in TABCleanFieldName() if we're on a DBCS system + * + * Revision 1.14 2000/09/28 16:39:44 warmerda + * avoid warnings for unused, and unitialized variables + * + * Revision 1.13 2000/09/20 18:35:51 daniel + * Fixed TABAdjustFilenameExtension() to also handle basename and path + * using TABAdjustCaseSensitiveFilename() + * + * Revision 1.12 2000/04/18 04:19:22 daniel + * Now accept extended chars with accents in TABCleanFieldName() + * + * Revision 1.11 2000/02/28 17:08:56 daniel + * Avoid using isalnum() in TABCleanFieldName + * + * Revision 1.10 2000/02/18 20:46:35 daniel + * Added TABCleanFieldName() + * + * Revision 1.9 2000/01/15 22:30:45 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.8 2000/01/14 23:46:59 daniel + * Added TABEscapeString()/TABUnEscapeString() + * + * Revision 1.7 1999/12/16 06:10:24 daniel + * TABGetBasename(): make sure last '/' of path is removed + * + * Revision 1.6 1999/12/14 02:08:37 daniel + * Added TABGetBasename() + TAB_CSLLoad() + * + * Revision 1.5 1999/11/08 04:30:59 stephane + * Modify TABGenerateArc() + * + * Revision 1.4 1999/09/29 17:59:21 daniel + * Definition for PI was gone on Windows + * + * Revision 1.3 1999/09/16 02:39:17 daniel + * Completed read support for most feature types + * + * Revision 1.2 1999/07/12 05:44:59 daniel + * Added include math.h for VC++ + * + * Revision 1.1 1999/07/12 04:18:25 daniel + * Initial checkin + * + **********************************************************************/ + +#include "mitab.h" +#include "mitab_utils.h" +#include "cpl_conv.h" + +#include /* sin()/cos() */ +#include /* toupper()/tolower() */ + +#if defined(_WIN32) && !defined(unix) && !defined(WIN32CE) +# include /* Multibyte chars stuff */ +#endif + + +/********************************************************************** + * TABGenerateArc() + * + * Generate the coordinates for an arc and ADD the coordinates to the + * geometry object. If the geometry already contains some points then + * these won't be lost. + * + * poLine can be a OGRLineString or one of its derived classes, such as + * OGRLinearRing + * numPoints is the number of points to generate. + * Angles are specified in radians, valid values are in the range [0..2*PI] + * + * Arcs are always generated counterclockwise, even if StartAngle > EndAngle + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABGenerateArc(OGRLineString *poLine, int numPoints, + double dCenterX, double dCenterY, + double dXRadius, double dYRadius, + double dStartAngle, double dEndAngle) +{ + double dX, dY, dAngleStep, dAngle=0.0; + int i; + + // Adjust angles to go counterclockwise + if (dEndAngle < dStartAngle) + dEndAngle += 2.0*PI; + + dAngleStep = (dEndAngle-dStartAngle)/(numPoints-1.0); + + for(i=0; iaddPoint(dX, dY); + } + + // Complete the arc with the last EndAngle, to make sure that + // the arc is correcly close. + + dX = dCenterX + dXRadius*cos(dAngle); + dY = dCenterY + dYRadius*sin(dAngle); + poLine->addPoint(dX,dY); + + + return 0; +} + + +/********************************************************************** + * TABCloseRing() + * + * Check if a ring is closed, and add a point to close it if necessary. + * + * Returns 0 on success, -1 on error. + **********************************************************************/ +int TABCloseRing(OGRLineString *poRing) +{ + if ( poRing->getNumPoints() > 0 && !poRing->get_IsClosed() ) + { + poRing->addPoint(poRing->getX(0), poRing->getY(0)); + } + + return 0; +} + +/********************************************************************** + * TABAdjustCaseSensitiveFilename() + * + * Scan a filename and its path, adjust uppercase/lowercases if + * necessary. + * + * Returns TRUE if file found, or FALSE if it could not be located with + * a case-insensitive search. + * + * This function works on the original buffer and returns a reference to it. + * It does nothing on Windows systems where filenames are not case sensitive. + **********************************************************************/ +GBool TABAdjustCaseSensitiveFilename(char *pszFname) +{ + +#ifdef _WIN32 + /*----------------------------------------------------------------- + * Nothing to do on Windows + *----------------------------------------------------------------*/ + return TRUE; + +#else + /*----------------------------------------------------------------- + * Unix case. + *----------------------------------------------------------------*/ + VSIStatBufL sStatBuf; + char *pszTmpPath = NULL; + int nTotalLen, iTmpPtr; + GBool bValidPath; + + /*----------------------------------------------------------------- + * First check if the filename is OK as is. + *----------------------------------------------------------------*/ + if (VSIStatL(pszFname, &sStatBuf) == 0) + { + return TRUE; + } + + /*----------------------------------------------------------------- + * OK, file either does not exist or has the wrong cases... we'll + * go backwards until we find a portion of the path that is valid. + *----------------------------------------------------------------*/ + pszTmpPath = CPLStrdup(pszFname); + nTotalLen = strlen(pszTmpPath); + iTmpPtr = nTotalLen; + bValidPath = FALSE; + + while(iTmpPtr > 0 && !bValidPath) + { + /*------------------------------------------------------------- + * Move back to the previous '/' separator + *------------------------------------------------------------*/ + pszTmpPath[--iTmpPtr] = '\0'; + while( iTmpPtr > 0 && pszTmpPath[iTmpPtr-1] != '/' ) + { + pszTmpPath[--iTmpPtr] = '\0'; + } + + if (iTmpPtr > 0 && VSIStatL(pszTmpPath, &sStatBuf) == 0) + bValidPath = TRUE; + } + + CPLAssert(iTmpPtr >= 0); + + /*----------------------------------------------------------------- + * Assume that CWD is valid... so an empty path is a valid path + *----------------------------------------------------------------*/ + if (iTmpPtr == 0) + bValidPath = TRUE; + + /*----------------------------------------------------------------- + * OK, now that we have a valid base, reconstruct the whole path + * by scanning all the sub-directories. + * If we get to a point where a path component does not exist then + * we simply return the rest of the path as is. + *----------------------------------------------------------------*/ + while(bValidPath && (int)strlen(pszTmpPath) < nTotalLen) + { + char **papszDir=NULL; + int iEntry, iLastPartStart; + + iLastPartStart = iTmpPtr; + papszDir = CPLReadDir(pszTmpPath); + + /*------------------------------------------------------------- + * Add one component to the current path + *------------------------------------------------------------*/ + pszTmpPath[iTmpPtr] = pszFname[iTmpPtr]; + iTmpPtr++; + for( ; pszFname[iTmpPtr] != '\0' && pszFname[iTmpPtr]!='/'; iTmpPtr++) + { + pszTmpPath[iTmpPtr] = pszFname[iTmpPtr]; + } + + while(iLastPartStart < iTmpPtr && pszTmpPath[iLastPartStart] == '/') + iLastPartStart++; + + /*------------------------------------------------------------- + * And do a case insensitive search in the current dir... + *------------------------------------------------------------*/ + for(iEntry=0; papszDir && papszDir[iEntry]; iEntry++) + { + if (EQUAL(pszTmpPath+iLastPartStart, papszDir[iEntry])) + { + /* Fount it! */ + strcpy(pszTmpPath+iLastPartStart, papszDir[iEntry]); + break; + } + } + + if (iTmpPtr > 0 && VSIStatL(pszTmpPath, &sStatBuf) != 0) + bValidPath = FALSE; + + CSLDestroy(papszDir); + } + + /*----------------------------------------------------------------- + * We reached the last valid path component... just copy the rest + * of the path as is. + *----------------------------------------------------------------*/ + if (iTmpPtr < nTotalLen-1) + { + strncpy(pszTmpPath+iTmpPtr, pszFname+iTmpPtr, nTotalLen-iTmpPtr); + } + + /*----------------------------------------------------------------- + * Update the source buffer and return. + *----------------------------------------------------------------*/ + strcpy(pszFname, pszTmpPath); + CPLFree(pszTmpPath); + + return bValidPath; + +#endif +} + + + + +/********************************************************************** + * TABAdjustFilenameExtension() + * + * Because Unix filenames are case sensitive and MapInfo datasets often have + * mixed cases filenames, we use this function to find the right filename + * to use ot open a specific file. + * + * This function works directly on the source string, so the filename it + * contains at the end of the call is the one that should be used. + * + * Returns TRUE if one of the extensions worked, and FALSE otherwise. + * If none of the extensions worked then the original extension will NOT be + * restored. + **********************************************************************/ +GBool TABAdjustFilenameExtension(char *pszFname) +{ + VSIStatBufL sStatBuf; + int i; + + /*----------------------------------------------------------------- + * First try using filename as provided + *----------------------------------------------------------------*/ + if (VSIStatL(pszFname, &sStatBuf) == 0) + { + return TRUE; + } + + /*----------------------------------------------------------------- + * Try using uppercase extension (we assume that fname contains a '.') + *----------------------------------------------------------------*/ + for(i = strlen(pszFname)-1; i >= 0 && pszFname[i] != '.'; i--) + { + pszFname[i] = (char)toupper(pszFname[i]); + } + + if (VSIStatL(pszFname, &sStatBuf) == 0) + { + return TRUE; + } + + /*----------------------------------------------------------------- + * Try using lowercase extension + *----------------------------------------------------------------*/ + for(i = strlen(pszFname)-1; i >= 0 && pszFname[i] != '.'; i--) + { + pszFname[i] = (char)tolower(pszFname[i]); + } + + if (VSIStatL(pszFname, &sStatBuf) == 0) + { + return TRUE; + } + + /*----------------------------------------------------------------- + * None of the extensions worked! + * Try adjusting cases in the whole path and filename + *----------------------------------------------------------------*/ + return TABAdjustCaseSensitiveFilename(pszFname); +} + + + +/********************************************************************** + * TABGetBasename() + * + * Extract the basename part of a complete file path. + * + * Returns a newly allocated string without the leading path (dirs) and + * the extenstion. The returned string should be freed using CPLFree(). + **********************************************************************/ +char *TABGetBasename(const char *pszFname) +{ + const char *pszTmp = NULL; + + /*----------------------------------------------------------------- + * Skip leading path or use whole name if no path dividers are + * encountered. + *----------------------------------------------------------------*/ + pszTmp = pszFname + strlen(pszFname) - 1; + while ( pszTmp != pszFname + && *pszTmp != '/' && *pszTmp != '\\' ) + pszTmp--; + + if( pszTmp != pszFname ) + pszTmp++; + + /*----------------------------------------------------------------- + * Now allocate our own copy and remove extension + *----------------------------------------------------------------*/ + char *pszBasename = CPLStrdup(pszTmp); + int i; + for(i=strlen(pszBasename)-1; i >= 0; i-- ) + { + if (pszBasename[i] == '.') + { + pszBasename[i] = '\0'; + break; + } + } + + return pszBasename; +} + + + +/********************************************************************** + * TAB_CSLLoad() + * + * Same as CSLLoad(), but does not produce an error if it fails... it + * just returns NULL silently instead. + * + * Load a test file into a stringlist. + * + * Lines are limited in length by the size of the CPLReadLine() buffer. + **********************************************************************/ +char **TAB_CSLLoad(const char *pszFname) +{ + VSILFILE *fp; + const char *pszLine; + char **papszStrList=NULL; + + fp = VSIFOpenL(pszFname, "rt"); + + if (fp) + { + while(!VSIFEofL(fp)) + { + if ( (pszLine = CPLReadLineL(fp)) != NULL ) + { + papszStrList = CSLAddString(papszStrList, pszLine); + } + } + + VSIFCloseL(fp); + } + + return papszStrList; +} + + + +/********************************************************************** + * TABUnEscapeString() + * + * Convert a string that can possibly contain escaped "\n" chars in + * into into a new one with binary newlines in it. + * + * Tries to work on hte original buffer unless bSrcIsConst=TRUE, in + * which case the original is always untouched and a copy is allocated + * ONLY IF NECESSARY. This means that the caller should compare the + * return value and the source (pszString) to see if a copy was returned, + * in which case the caller becomes responsible of freeing both the + * source and the copy. + **********************************************************************/ +char *TABUnEscapeString(char *pszString, GBool bSrcIsConst) +{ + + /*----------------------------------------------------------------- + * First check if we need to do any replacement + *----------------------------------------------------------------*/ + if (pszString == NULL || strstr(pszString, "\\n") == NULL) + { + return pszString; + } + + /*----------------------------------------------------------------- + * Yes, we need to replace at least one "\n" + * We try to work on the original buffer unless we have bSrcIsConst=TRUE + * + * Note that we do not worry about freeing the source buffer when we + * return a copy... it is up to the caller to decide if the source needs + * to be freed based on context and by comparing pszString with + * the returned pointer (pszWorkString) to see if they are identical. + *----------------------------------------------------------------*/ + char *pszWorkString = NULL; + int i =0; + int j =0; + + if (bSrcIsConst) + { + // We have to create a copy to work on. + pszWorkString = (char *)CPLMalloc(sizeof(char) * + (strlen(pszString) +1)); + } + else + { + // We'll work on the original. + pszWorkString = pszString; + } + + + while (pszString[i]) + { + if (pszString[i] =='\\' && + pszString[i+1] == 'n') + { + pszWorkString[j++] = '\n'; + i+= 2; + } + else if (pszString[i] =='\\' && + pszString[i+1] == '\\') + { + pszWorkString[j++] = '\\'; + i+= 2; + } + else + { + pszWorkString[j++] = pszString[i++]; + } + } + pszWorkString[j++] = '\0'; + + return pszWorkString; +} + +/********************************************************************** + * TABEscapeString() + * + * Convert a string that can possibly contain binary "\n" chars in + * into into a new one with escaped newlines ("\\" + "n") in it. + * + * The function returns the original string pointer if it did not need to + * be modified, or a copy that has to be freed by the caller if the + * string had to be modified. + * + * It is up to the caller to decide if the returned string needs to be + * freed by comparing the source (pszString) pointer with the returned + * pointer (pszWorkString) to see if they are identical. + **********************************************************************/ +char *TABEscapeString(char *pszString) +{ + /*----------------------------------------------------------------- + * First check if we need to do any replacement + *----------------------------------------------------------------*/ + if (pszString == NULL || strchr(pszString, '\n') == NULL) + { + return pszString; + } + + /*----------------------------------------------------------------- + * OK, we need to do some replacements... alloc a copy big enough + * to hold the worst possible case + *----------------------------------------------------------------*/ + char *pszWorkString = (char *)CPLMalloc(2*sizeof(char) * + (strlen(pszString) +1)); + + int i =0; + int j =0; + + while (pszString[i]) + { + if (pszString[i] =='\n') + { + pszWorkString[j++] = '\\'; + pszWorkString[j++] = 'n'; + i++; + } + else if (pszString[i] =='\\') + { + pszWorkString[j++] = '\\'; + pszWorkString[j++] = '\\'; + i++; + } + else + { + pszWorkString[j++] = pszString[i++]; + } + } + pszWorkString[j++] = '\0'; + + return pszWorkString; +} + +/********************************************************************** + * TABCleanFieldName() + * + * Return a copy of pszSrcName that contains only valid characters for a + * TAB field name. All invalid characters are replaced by '_'. + * + * The returned string should be freed by the caller. + **********************************************************************/ +char *TABCleanFieldName(const char *pszSrcName) +{ + char *pszNewName; + int numInvalidChars = 0; + + pszNewName = CPLStrdup(pszSrcName); + + if (strlen(pszNewName) > 31) + { + pszNewName[31] = '\0'; + CPLError(CE_Warning, TAB_WarningInvalidFieldName, + "Field name '%s' is longer than the max of 31 characters. " + "'%s' will be used instead.", pszSrcName, pszNewName); + } + +#if defined(_WIN32) && !defined(unix) && !defined(WIN32CE) + /*----------------------------------------------------------------- + * On Windows, check if we're using a double-byte codepage, and + * if so then just keep the field name as is... + *----------------------------------------------------------------*/ + if (_getmbcp() != 0) + return pszNewName; +#endif + + /*----------------------------------------------------------------- + * According to the MapInfo User's Guide (p. 240, v5.5) + * New Table Command: + * Name: + * Displays the field name in the name box. You can also enter new field + * names here. Defaults are Field1, Field2, etc. A field name can contain + * up to 31 alphanumeric characters. Use letters, numbers, and the + * underscore. Do not use spaces; instead, use the underscore character + * (_) to separate words in a field name. Use upper and lower case for + * legibility, but MapInfo is not case-sensitive. + * + * It was also verified that extended chars with accents are also + * accepted. + *----------------------------------------------------------------*/ + for(int i=0; pszSrcName && pszSrcName[i] != '\0'; i++) + { + if ( pszSrcName[i]=='#' ) + { + if (i == 0) + { + pszNewName[i] = '_'; + numInvalidChars++; + } + } + else if ( !( pszSrcName[i] == '_' || + (i!=0 && pszSrcName[i]>='0' && pszSrcName[i]<='9') || + (pszSrcName[i]>='a' && pszSrcName[i]<='z') || + (pszSrcName[i]>='A' && pszSrcName[i]<='Z') || + (GByte)pszSrcName[i]>=192 ) ) + { + pszNewName[i] = '_'; + numInvalidChars++; + } + } + + if (numInvalidChars > 0) + { + CPLError(CE_Warning, TAB_WarningInvalidFieldName, + "Field name '%s' contains invalid characters. " + "'%s' will be used instead.", pszSrcName, pszNewName); + } + + return pszNewName; +} + + +/********************************************************************** + * MapInfo Units string to numeric ID conversion + **********************************************************************/ +typedef struct +{ + int nUnitId; + const char *pszAbbrev; +} MapInfoUnitsInfo; + +static MapInfoUnitsInfo gasUnitsList[] = +{ + {0, "mi"}, + {1, "km"}, + {2, "in"}, + {3, "ft"}, + {4, "yd"}, + {5, "mm"}, + {6, "cm"}, + {7, "m"}, + {8, "survey ft"}, + {8, "survey foot"}, // alternate + {13, NULL}, + {9, "nmi"}, + {30, "li"}, + {31, "ch"}, + {32, "rd"}, + {-1, NULL} +}; + + +/********************************************************************** + * TABUnitIdToString() + * + * Return the MIF units name for specified units id. + * Return "" if no match found. + * + * The returned string should not be freed by the caller. + **********************************************************************/ +const char *TABUnitIdToString(int nId) +{ + MapInfoUnitsInfo *psList; + + psList = gasUnitsList; + + while(psList->nUnitId != -1) + { + if (psList->nUnitId == nId) + return psList->pszAbbrev; + psList++; + } + + return ""; +} + +/********************************************************************** + * TABUnitIdFromString() + * + * Return the units ID for specified MIF units name + * + * Returns -1 if no match found. + **********************************************************************/ +int TABUnitIdFromString(const char *pszName) +{ + MapInfoUnitsInfo *psList; + + psList = gasUnitsList; + + if( pszName == NULL ) + return 13; + + while(psList->nUnitId != -1) + { + if (psList->pszAbbrev != NULL && + EQUAL(psList->pszAbbrev, pszName)) + return psList->nUnitId; + psList++; + } + + return -1; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_utils.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_utils.h new file mode 100644 index 000000000..b00e9e210 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mitab/mitab_utils.h @@ -0,0 +1,99 @@ +/********************************************************************** + * $Id: mitab_utils.h,v 1.10 2004-06-30 20:29:04 dmorissette Exp $ + * + * Name: mitab_utils.h + * Project: MapInfo TAB Read/Write library + * Language: C++ + * Purpose: Header file containing definitions of misc. util functions. + * Author: Daniel Morissette, dmorissette@dmsolutions.ca + * + ********************************************************************** + * Copyright (c) 1999-2001, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * $Log: mitab_utils.h,v $ + * Revision 1.10 2004-06-30 20:29:04 dmorissette + * Fixed refs to old address danmo@videotron.ca + * + * Revision 1.9 2001/01/23 21:23:42 daniel + * Added projection bounds lookup table, called from TABFile::SetProjInfo() + * + * Revision 1.8 2000/02/18 20:46:58 daniel + * Added TABCleanFieldName() + * + * Revision 1.7 2000/01/15 22:30:45 daniel + * Switch to MIT/X-Consortium OpenSource license + * + * Revision 1.6 2000/01/14 23:47:00 daniel + * Added TABEscapeString()/TABUnEscapeString() + * + * Revision 1.5 1999/12/14 02:08:16 daniel + * Added TABGetBasename() + TAB_CSLLoad() + * + * Revision 1.4 1999/09/28 13:33:32 daniel + * Moved definition for PI to mitab.h + * + * Revision 1.3 1999/09/26 14:59:38 daniel + * Implemented write support + * + * Revision 1.2 1999/09/16 02:39:17 daniel + * Completed read support for most feature types + * + * Revision 1.1 1999/07/12 04:18:25 daniel + * Initial checkin + * + **********************************************************************/ + +#ifndef _MITAB_UTILS_H_INCLUDED_ +#define _MITAB_UTILS_H_INCLUDED_ + +#include "ogr_geometry.h" + +#define COLOR_R(color) ((color&0xff0000)/0x10000) +#define COLOR_G(color) ((color&0xff00)/0x100) +#define COLOR_B(color) (color&0xff) + +/*===================================================================== + Function prototypes + =====================================================================*/ + +int TABGenerateArc(OGRLineString *poLine, int numPoints, + double dCenterX, double dCenterY, + double dXRadius, double dYRadius, + double dStartAngle, double dEndAngle); +int TABCloseRing(OGRLineString *poRing); + + +GBool TABAdjustFilenameExtension(char *pszFname); +char *TABGetBasename(const char *pszFname); +char **TAB_CSLLoad(const char *pszFname); + +char *TABEscapeString(char *pszString); +char *TABUnEscapeString(char *pszString, GBool bSrcIsConst); + +char *TABCleanFieldName(const char *pszSrcName); + +const char *TABUnitIdToString(int nId); +int TABUnitIdFromString(const char *pszName); + +#endif /* _MITAB_UTILS_H_INCLUDED_ */ + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/GNUmakefile new file mode 100644 index 000000000..4c478ab37 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/GNUmakefile @@ -0,0 +1,12 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrmssqlspatialdatasource.o ogrmssqlspatiallayer.o ogrmssqlspatialtablelayer.o ogrmssqlspatialselectlayer.o ogrmssqlspatialdriver.o ogrmssqlgeometryparser.o ogrmssqlgeometryvalidator.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/drv_mssqlspatial.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/drv_mssqlspatial.html new file mode 100644 index 000000000..5d5fb65d9 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/drv_mssqlspatial.html @@ -0,0 +1,136 @@ + + +MSSQLSpatial - Microsoft SQL Server Spatial Database + + + + +

    MSSQLSpatial - Microsoft SQL Server Spatial Database

    + +

    This driver implements support for access to spatial tables in Microsoft SQL + Server 2008+ which contains the geometry and geography data types to repesent + the geometry columns.

    + +

    Connecting to a database

    + +To connect to a MSSQL datasource, use a connection string specifying the database name, +with additional parameters as necessary. The connection strings must be prefixed + with 'MSSQL:'.
    +
    +MSSQL:server=.\MSSQLSERVER2008;database=dbname;trusted_connection=yes
    +In addition to the standard parameters of the + ODBC driver connection string + format the following custom parameters can also be used in the following syntax: + +
      + +
    • Tables=schema1.table1(geometry column1),schema2.table2(geometry column2): + By using this parameter you can specify the subset of the layers to be used by the driver. If this parameter is not set, the layers are retrieved from the geometry_columns metadata table. You can omit specifying the schema and the geometry column portions of the syntax.
    • +
    • GeometryFormat=native|wkb|wkt|wkbzm: + The desired format in which the geometries should be retrieved from the server. The default value is 'native' in this case the native SqlGeometry and SqlGeography serialization format is used. When using the 'wkb' or 'wkt' setting the geometry representation is converted to 'Well Known Binary' and 'Well Known Text' at the server. This conversion requires a significant overhead at the server and makes the feature access slower than using the native format. The wkbzm format can only be used with SQL Server 2012.
    • + +
    +

    The parameter names are not case sensitive in the connection strings.

    +

    Specifying the + Database parameter is required by the driver in order to select the proper database.

    +

    The connection may contain the optional + Driver parameter if a custom SQL server driver should be loaded (like FreeTDS). The default is {SQL Server}

    + +

    Layers

    + +

    Starting with GDAL 1.11 if the user defines the environment variable +MSSQLSPATIAL_LIST_ALL_TABLES=YES (and does not specify Tables= in the connection string), +all regular user tables will be treated as layers. This option is useful if you want tables with +with no spatial data

    + +

    By default the MSSQL driver will only look for layers that are registered in the geometry_columns metadata table. +Starting with GDAL 1.10 if the user defines the environment variable +MSSQLSPATIAL_USE_GEOMETRY_COLUMNS=NO then the driver will look for all user spatial tables found in the system catalog

    + +

    SQL statements

    + +

    The MS SQL Spatial driver passes SQL statements directly to MS SQL by default, +rather than evaluating them internally when using the ExecuteSQL() call on the +OGRDataSource, or the -sql command option to ogr2ogr. Attribute query +expressions are also passed directly through to MSSQL. +It's also possible to request the OGR MSSQL driver to handle SQL commands +with the OGR SQL engine, by passing "OGRSQL" +string to the ExecuteSQL() method, as the name of the SQL dialect.

    + +

    The MSSQL driver in OGR supports the OGRLayer::StartTrasaction(), +OGRLayer::CommitTransaction() and OGRLayer::RollbackTransaction() +calls in the normal SQL sense.

    + +

    Creation Issues

    + +

    This driver doesn't support creating new databases, you might want to use the Microsoft SQL Server Client Tools for this purpose, but it does allow creation of new layers within an +existing database.

    + +

    Layer Creation Options

    + +
      +
    • +GEOM_TYPE: The GEOM_TYPE layer creation option can be set to +one of "geometry" or "geography". If this option is not specified the default value is "geometry". + So as to create the geometry column with "geography" type, this parameter should + be set "geography". In this case the layer must have a valid spatial referece of + one of the geography coordinate systems defined in the + sys.spatial_reference_systems SQL Server metadata table. Projected + coordinate systems are not supported in this case.
    • +
    • OVERWRITE: This may be "YES" to force an existing layer of the +desired name to be destroyed before creating the requested layer.
    • +
    • LAUNDER: This may be "YES" to force new fields created on this +layer to have their field names "laundered" into a form more compatible with +MSSQL. This converts to lower case and converts some special characters +like "-" and "#" to "_". If "NO" exact names are preserved. +The default value is "YES". If enabled the table (layer) name will also be laundered.
    • +
    • PRECISION: This may be "YES" to force new fields created on this +layer to try and represent the width and precision information, if available +using numeric(width,precision) or char(width) types. If "NO" then the types +float, int and varchar will be used instead. The default is "YES".
    • +
    • DIM={2,3}: Control the dimension of the layer. Defaults to 3.
    • +
    • GEOMETRY_NAME: Set the name of geometry column in the new table. If +omitted it defaults to ogr_geometry.. Note: option was called GEOM_NAME in releases before GDAL 2
    • +
    • SCHEMA: Set name of schema for new table. +If this parameter is not supported the default schema "dbo" is used.
    • +
    • SRID: Set the spatial reference id of the new table explicitly. +The corresponding entry should already be added to the spatial_ref_sys metadata table. If this parameter is not set the SRID is derived from the authority code of source layer SRS.
    • +
    • SPATIAL_INDEX: (From GDAL 2.0.0) Boolean flag (YES/NO) to enable/disable the automatic creation of a spatial index on the newly created layers (enabled by default).
    • +
    • UPLOAD_GEOM_FORMAT: (From GDAL 2.0.0) Specify the geometry format (wkb or wkt) when creating or modifying features. The default is wkb.
    • +
    • FID: (From GDAL 2.0.0) Name of the FID column to create. Defaults to ogr_fid.
    • +
    + +

    Spatial Index Creation

    + +

    By default the MS SQL Spatial driver doesn't add spatial indexes to the tables during the layer creation. However you should create a spatial index by using the + following sql option:

    + +
    create spatial index on schema.table
    + +

    The spatial index can also be dropped by using the following syntax:

    + +
    drop spatial index on schema.table
    + +

    Transaction support (GDAL >= 2.0)

    + +

    +The driver implements transactions at the dataset level, per +RFC 54 +

    + +

    Examples

    + +

    Creating a layer from an OGR data source

    +
    +ogr2ogr -overwrite -f MSSQLSpatial "MSSQL:server=.\MSSQLSERVER2008;database=geodb;trusted_connection=yes" "rivers.tab"
    + +

    Connecting to a layer and dump the contents

    +
    +ogrinfo -al "MSSQL:server=.\MSSQLSERVER2008;database=geodb;tables=rivers;trusted_connection=yes"
    + +

    Creating a spatial index

    +
    +ogrinfo -sql "create spatial index on rivers" "MSSQL:server=.\MSSQLSERVER2008;database=geodb;trusted_connection=yes"
    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/makefile.vc new file mode 100644 index 000000000..9455ca54b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = ogrmssqlspatialdriver.obj ogrmssqlspatialdatasource.obj ogrmssqlspatiallayer.obj ogrmssqlspatialtablelayer.obj ogrmssqlspatialselectlayer.obj ogrmssqlgeometryparser.obj ogrmssqlgeometryvalidator.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. + +default: $(OBJ) + +clean: + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogr_mssqlspatial.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogr_mssqlspatial.h new file mode 100644 index 000000000..96e031cc3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogr_mssqlspatial.h @@ -0,0 +1,401 @@ +/****************************************************************************** + * $Id: ogr_mssqlspatial.h 29185 2015-05-12 10:45:44Z tamas $ + * + * Project: MSSQL Spatial driver + * Purpose: Definition of classes for OGR MSSQL Spatial driver. + * Author: Tamas Szekeres, szekerest at gmail.com + * + ****************************************************************************** + * Copyright (c) 2010, Tamas Szekeres + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_MSSQLSPATIAL_H_INCLUDED +#define _OGR_MSSQLSPATIAL_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_odbc.h" +#include "cpl_error.h" + +class OGRMSSQLSpatialDataSource; + +/* layer status */ +#define MSSQLLAYERSTATUS_ORIGINAL 0 +#define MSSQLLAYERSTATUS_INITIAL 1 +#define MSSQLLAYERSTATUS_CREATED 2 +#define MSSQLLAYERSTATUS_DISABLED 3 + +/* geometry format to transfer geometry column */ +#define MSSQLGEOMETRY_NATIVE 0 +#define MSSQLGEOMETRY_WKB 1 +#define MSSQLGEOMETRY_WKT 2 +#define MSSQLGEOMETRY_WKBZM 3 /* SQL Server 2012 */ + +/* geometry column types */ +#define MSSQLCOLTYPE_GEOMETRY 0 +#define MSSQLCOLTYPE_GEOGRAPHY 1 +#define MSSQLCOLTYPE_BINARY 2 +#define MSSQLCOLTYPE_TEXT 3 + +/************************************************************************/ +/* OGRMSSQLAppendEscaped( ) */ +/************************************************************************/ + +void OGRMSSQLAppendEscaped( CPLODBCStatement* poStatement, const char* pszStrValue); + +/************************************************************************/ +/* OGRMSSQLGeometryParser */ +/************************************************************************/ + +class OGRMSSQLGeometryValidator +{ +protected: + int bIsValid; + OGRGeometry* poValidGeometry; + OGRGeometry* poOriginalGeometry; + +public: + OGRMSSQLGeometryValidator(OGRGeometry* poGeom); + ~OGRMSSQLGeometryValidator(); + + int ValidatePoint(OGRPoint * poGeom); + int ValidateMultiPoint(OGRMultiPoint * poGeom); + int ValidateLineString(OGRLineString * poGeom); + int ValidateLinearRing(OGRLinearRing * poGeom); + int ValidateMultiLineString(OGRMultiLineString * poGeom); + int ValidatePolygon(OGRPolygon* poGeom); + int ValidateMultiPolygon(OGRMultiPolygon* poGeom); + int ValidateGeometryCollection(OGRGeometryCollection* poGeom); + int ValidateGeometry(OGRGeometry* poGeom); + + OGRGeometry* GetValidGeometryRef(); + int IsValid() { return bIsValid; }; +}; + +/************************************************************************/ +/* OGRMSSQLGeometryParser */ +/************************************************************************/ + +class OGRMSSQLGeometryParser +{ +protected: + unsigned char* pszData; + /* serialization propeties */ + char chProps; + /* point array */ + int nPointSize; + int nPointPos; + int nNumPoints; + /* figure array */ + int nFigurePos; + int nNumFigures; + /* shape array */ + int nShapePos; + int nNumShapes; + int nSRSId; + /* geometry or geography */ + int nColType; + +protected: + OGRPoint* ReadPoint(int iShape); + OGRMultiPoint* ReadMultiPoint(int iShape); + OGRLineString* ReadLineString(int iShape); + OGRMultiLineString* ReadMultiLineString(int iShape); + OGRPolygon* ReadPolygon(int iShape); + OGRMultiPolygon* ReadMultiPolygon(int iShape); + OGRGeometryCollection* ReadGeometryCollection(int iShape); + +public: + OGRMSSQLGeometryParser( int nGeomColumnType ); + OGRErr ParseSqlGeometry(unsigned char* pszInput, int nLen, + OGRGeometry **poGeom); + int GetSRSId() { return nSRSId; }; +}; + + +/************************************************************************/ +/* OGRMSSQLSpatialLayer */ +/************************************************************************/ + +class OGRMSSQLSpatialLayer : public OGRLayer +{ + protected: + OGRFeatureDefn *poFeatureDefn; + + CPLODBCStatement *poStmt; + + // Layer spatial reference system, and srid. + OGRSpatialReference *poSRS; + int nSRSId; + + GIntBig iNextShapeId; + + OGRMSSQLSpatialDataSource *poDS; + + int nGeomColumnType; + char *pszGeomColumn; + char *pszFIDColumn; + + int bIsIdentityFid; + + int nLayerStatus; + + int *panFieldOrdinals; + + CPLErr BuildFeatureDefn( const char *pszLayerName, + CPLODBCStatement *poStmt ); + + virtual CPLODBCStatement * GetStatement() { return poStmt; } + + public: + OGRMSSQLSpatialLayer(); + virtual ~OGRMSSQLSpatialLayer(); + + virtual void ResetReading(); + virtual OGRFeature *GetNextRawFeature(); + virtual OGRFeature *GetNextFeature(); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; } + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual const char *GetFIDColumn(); + virtual const char *GetGeometryColumn(); + + virtual int TestCapability( const char * ); + char* GByteArrayToHexString( const GByte* pabyData, int nLen); + + void SetLayerStatus( int nStatus ) { nLayerStatus = nStatus; } + int GetLayerStatus() { return nLayerStatus; } +}; + +/************************************************************************/ +/* OGRMSSQLSpatialTableLayer */ +/************************************************************************/ + +class OGRMSSQLSpatialTableLayer : public OGRMSSQLSpatialLayer +{ + int bUpdateAccess; + int bLaunderColumnNames; + int bPreservePrecision; + int bNeedSpatialIndex; + + int nUploadGeometryFormat; + + char *pszQuery; + + void ClearStatement(); + CPLODBCStatement* BuildStatement(const char* pszColumns); + + CPLString BuildFields(); + + virtual CPLODBCStatement * GetStatement(); + + char *pszTableName; + char *pszLayerName; + char *pszSchemaName; + + OGRwkbGeometryType eGeomType; + + public: + OGRMSSQLSpatialTableLayer( OGRMSSQLSpatialDataSource * ); + ~OGRMSSQLSpatialTableLayer(); + + CPLErr Initialize( const char *pszSchema, + const char *pszTableName, + const char *pszGeomCol, + int nCoordDimension, + int nSRId, + const char *pszSRText, + OGRwkbGeometryType eType); + + OGRErr CreateSpatialIndex(); + void DropSpatialIndex(); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual const char* GetName(); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + + const char* GetTableName() { return pszTableName; } + const char* GetLayerName() { return pszLayerName; } + const char* GetSchemaName() { return pszSchemaName; } + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual int TestCapability( const char * ); + + void SetLaunderFlag( int bFlag ) + { bLaunderColumnNames = bFlag; } + void SetPrecisionFlag( int bFlag ) + { bPreservePrecision = bFlag; } + void SetSpatialIndexFlag( int bFlag ) + { bNeedSpatialIndex = bFlag; } + void SetUploadGeometryFormat( int nGeometryFormat ) + { nUploadGeometryFormat = nGeometryFormat; } + void AppendFieldValue(CPLODBCStatement *poStatement, + OGRFeature* poFeature, int i, int *bind_num, void **bind_buffer); + + int FetchSRSId(); +}; + +/************************************************************************/ +/* OGRMSSQLSpatialSelectLayer */ +/************************************************************************/ + +class OGRMSSQLSpatialSelectLayer : public OGRMSSQLSpatialLayer +{ + char *pszBaseStatement; + + void ClearStatement(); + OGRErr ResetStatement(); + + virtual CPLODBCStatement * GetStatement(); + + public: + OGRMSSQLSpatialSelectLayer( OGRMSSQLSpatialDataSource *, + CPLODBCStatement * ); + ~OGRMSSQLSpatialSelectLayer(); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRODBCDataSource */ +/************************************************************************/ + +class OGRMSSQLSpatialDataSource : public OGRDataSource +{ + OGRMSSQLSpatialTableLayer **papoLayers; + int nLayers; + + char *pszName; + + char *pszCatalog; + + int bDSUpdate; + CPLODBCSession oSession; + + int nGeometryFormat; + + int bUseGeometryColumns; + + int bListAllTables; + + // We maintain a list of known SRID to reduce the number of trips to + // the database to get SRSes. + int nKnownSRID; + int *panSRID; + OGRSpatialReference **papoSRS; + + public: + OGRMSSQLSpatialDataSource(); + ~OGRMSSQLSpatialDataSource(); + + const char *GetCatalog() { return pszCatalog; } + + int ParseValue(char** pszValue, char* pszSource, const char* pszKey, + int nStart, int nNext, int nTerm, int bRemove); + + int Open( const char *, int bUpdate, int bTestOpen ); + int OpenTable( const char *pszSchemaName, const char *pszTableName, + const char *pszGeomCol,int nCoordDimension, + int nSRID, const char *pszSRText, + OGRwkbGeometryType eType, int bUpdate ); + + const char *GetName() { return pszName; } + int GetLayerCount(); + OGRLayer *GetLayer( int ); + OGRLayer *GetLayerByName( const char* pszLayerName ); + + int GetGeometryFormat() { return nGeometryFormat; } + int UseGeometryColumns() { return bUseGeometryColumns; } + + virtual int DeleteLayer( int iLayer ); + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + int TestCapability( const char * ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + char *LaunderName( const char *pszSrcName ); + OGRErr InitializeMetadataTables(); + + OGRSpatialReference* FetchSRS( int nId ); + int FetchSRSId( OGRSpatialReference * poSRS ); + + OGRErr StartTransaction(CPL_UNUSED int bForce); + OGRErr CommitTransaction(); + OGRErr RollbackTransaction(); + + // Internal use + CPLODBCSession *GetSession() { return &oSession; } + +}; + +/************************************************************************/ +/* OGRODBCDriver */ +/************************************************************************/ + +class OGRMSSQLSpatialDriver : public OGRSFDriver +{ + public: + ~OGRMSSQLSpatialDriver(); + + const char *GetName(); + OGRDataSource *Open( const char *, int ); + + virtual OGRDataSource *CreateDataSource( const char *pszName, + char ** = NULL ); + + int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_MSSQLSPATIAL_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlgeometryparser.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlgeometryparser.cpp new file mode 100644 index 000000000..51af7bb5c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlgeometryparser.cpp @@ -0,0 +1,553 @@ +/****************************************************************************** + * $Id: ogrmssqlgeometryparser.cpp 24918 2012-09-07 12:02:01Z tamas $ + * + * Project: MSSQL Spatial driver + * Purpose: Implements OGRMSSQLGeometryParser class to parse native SqlGeometries. + * Author: Tamas Szekeres, szekerest at gmail.com + * + ****************************************************************************** + * Copyright (c) 2010, Tamas Szekeres + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_mssqlspatial.h" + +CPL_CVSID("$Id: ogrmssqlgeometryparser.cpp 24918 2012-09-07 12:02:01Z tamas $"); + +/* SqlGeometry serialization format + +Simple Point (SerializationProps & IsSinglePoint) + [SRID][0x01][SerializationProps][Point][z][m] + +Simple Line Segment (SerializationProps & IsSingleLineSegment) + [SRID][0x01][SerializationProps][Point1][Point2][z1][z2][m1][m2] + +Complex Geometries + [SRID][0x01][SerializationProps][NumPoints][Point1]..[PointN][z1]..[zN][m1]..[mN] + [NumFigures][Figure]..[Figure][NumShapes][Shape]..[Shape] + +SRID + Spatial Reference Id (4 bytes) + +SerializationProps (bitmask) 1 byte + 0x01 = HasZValues + 0x02 = HasMValues + 0x04 = IsValid + 0x08 = IsSinglePoint + 0x10 = IsSingleLineSegment + 0x20 = IsWholeGlobe + +Point (2-4)x8 bytes, size depends on SerializationProps & HasZValues & HasMValues + [x][y] - SqlGeometry + [latitude][longitude] - SqlGeography + +Figure + [FigureAttribute][PointOffset] + +FigureAttribute (1 byte) + 0x00 = Interior Ring + 0x01 = Stroke + 0x02 = Exterior Ring + +Shape + [ParentFigureOffset][FigureOffset][ShapeType] + +ShapeType (1 byte) + 0x00 = Unknown + 0x01 = Point + 0x02 = LineString + 0x03 = Polygon + 0x04 = MultiPoint + 0x05 = MultiLineString + 0x06 = MultiPolygon + 0x07 = GeometryCollection + +*/ + +/************************************************************************/ +/* Geometry parser macros */ +/************************************************************************/ + +#define SP_NONE 0 +#define SP_HASZVALUES 1 +#define SP_HASMVALUES 2 +#define SP_ISVALID 4 +#define SP_ISSINGLEPOINT 8 +#define SP_ISSINGLELINESEGMENT 0x10 +#define SP_ISWHOLEGLOBE 0x20 + +#define ST_UNKNOWN 0 +#define ST_POINT 1 +#define ST_LINESTRING 2 +#define ST_POLYGON 3 +#define ST_MULTIPOINT 4 +#define ST_MULTILINESTRING 5 +#define ST_MULTIPOLYGON 6 +#define ST_GEOMETRYCOLLECTION 7 + +#define ReadInt32(nPos) (*((unsigned int*)(pszData + (nPos)))) + +#define ReadByte(nPos) (pszData[nPos]) + +#define ReadDouble(nPos) (*((double*)(pszData + (nPos)))) + +#define ParentOffset(iShape) (ReadInt32(nShapePos + (iShape) * 9 )) +#define FigureOffset(iShape) (ReadInt32(nShapePos + (iShape) * 9 + 4)) +#define ShapeType(iShape) (ReadByte(nShapePos + (iShape) * 9 + 8)) + +#define NextFigureOffset(iShape) (iShape + 1 < nNumShapes? FigureOffset((iShape) +1) : nNumFigures) + +#define FigureAttribute(iFigure) (ReadByte(nFigurePos + (iFigure) * 5)) +#define PointOffset(iFigure) (ReadInt32(nFigurePos + (iFigure) * 5 + 1)) +#define NextPointOffset(iFigure) (iFigure + 1 < nNumFigures? PointOffset((iFigure) +1) : nNumPoints) + +#define ReadX(iPoint) (ReadDouble(nPointPos + 16 * (iPoint))) +#define ReadY(iPoint) (ReadDouble(nPointPos + 16 * (iPoint) + 8)) +#define ReadZ(iPoint) (ReadDouble(nPointPos + 16 * nNumPoints + 8 * (iPoint))) +#define ReadM(iPoint) (ReadDouble(nPointPos + 24 * nNumPoints + 8 * (iPoint))) + +/************************************************************************/ +/* OGRMSSQLGeometryParser() */ +/************************************************************************/ + +OGRMSSQLGeometryParser::OGRMSSQLGeometryParser(int nGeomColumnType) +{ + nColType = nGeomColumnType; +} + +/************************************************************************/ +/* ReadPoint() */ +/************************************************************************/ + +OGRPoint* OGRMSSQLGeometryParser::ReadPoint(int iShape) +{ + int iFigure = FigureOffset(iShape); + if ( iFigure < nNumFigures ) + { + int iPoint = PointOffset(iFigure); + if ( iPoint < nNumPoints ) + { + if (nColType == MSSQLCOLTYPE_GEOGRAPHY) + { + if ( chProps & SP_HASZVALUES ) + return new OGRPoint( ReadY(iPoint), ReadX(iPoint), ReadZ(iPoint) ); + else + return new OGRPoint( ReadY(iPoint), ReadX(iPoint) ); + } + else + { + if ( chProps & SP_HASZVALUES ) + return new OGRPoint( ReadX(iPoint), ReadY(iPoint), ReadZ(iPoint) ); + else + return new OGRPoint( ReadX(iPoint), ReadY(iPoint) ); + } + } + } + return NULL; +} + +/************************************************************************/ +/* ReadMultiPoint() */ +/************************************************************************/ + +OGRMultiPoint* OGRMSSQLGeometryParser::ReadMultiPoint(int iShape) +{ + int i; + OGRMultiPoint* poMultiPoint = new OGRMultiPoint(); + OGRGeometry* poGeom; + + for (i = iShape + 1; i < nNumShapes; i++) + { + poGeom = NULL; + if (ParentOffset(i) == (unsigned int)iShape) + { + if ( ShapeType(i) == ST_POINT ) + poGeom = ReadPoint(i); + } + if ( poGeom ) + poMultiPoint->addGeometryDirectly( poGeom ); + } + + return poMultiPoint; +} + +/************************************************************************/ +/* ReadLineString() */ +/************************************************************************/ + +OGRLineString* OGRMSSQLGeometryParser::ReadLineString(int iShape) +{ + int iFigure, iPoint, iNextPoint, i; + iFigure = FigureOffset(iShape); + + OGRLineString* poLineString = new OGRLineString(); + iPoint = PointOffset(iFigure); + iNextPoint = NextPointOffset(iFigure); + poLineString->setNumPoints(iNextPoint - iPoint); + i = 0; + while (iPoint < iNextPoint) + { + if (nColType == MSSQLCOLTYPE_GEOGRAPHY) + { + if ( chProps & SP_HASZVALUES ) + poLineString->setPoint(i, ReadY(iPoint), ReadX(iPoint), ReadZ(iPoint) ); + else + poLineString->setPoint(i, ReadY(iPoint), ReadX(iPoint) ); + } + else + { + if ( chProps & SP_HASZVALUES ) + poLineString->setPoint(i, ReadX(iPoint), ReadY(iPoint), ReadZ(iPoint) ); + else + poLineString->setPoint(i, ReadX(iPoint), ReadY(iPoint) ); + } + + ++iPoint; + ++i; + } + + return poLineString; +} + +/************************************************************************/ +/* ReadMultiLineString() */ +/************************************************************************/ + +OGRMultiLineString* OGRMSSQLGeometryParser::ReadMultiLineString(int iShape) +{ + int i; + OGRMultiLineString* poMultiLineString = new OGRMultiLineString(); + OGRGeometry* poGeom; + + for (i = iShape + 1; i < nNumShapes; i++) + { + poGeom = NULL; + if (ParentOffset(i) == (unsigned int)iShape) + { + if ( ShapeType(i) == ST_LINESTRING ) + poGeom = ReadLineString(i); + } + if ( poGeom ) + poMultiLineString->addGeometryDirectly( poGeom ); + } + + return poMultiLineString; +} + +/************************************************************************/ +/* ReadPolygon() */ +/************************************************************************/ + +OGRPolygon* OGRMSSQLGeometryParser::ReadPolygon(int iShape) +{ + int iFigure, iPoint, iNextPoint, i; + int iNextFigure = NextFigureOffset(iShape); + + OGRPolygon* poPoly = new OGRPolygon(); + for (iFigure = FigureOffset(iShape); iFigure < iNextFigure; iFigure++) + { + OGRLinearRing* poRing = new OGRLinearRing(); + iPoint = PointOffset(iFigure); + iNextPoint = NextPointOffset(iFigure); + poRing->setNumPoints(iNextPoint - iPoint); + i = 0; + while (iPoint < iNextPoint) + { + if (nColType == MSSQLCOLTYPE_GEOGRAPHY) + { + if ( chProps & SP_HASZVALUES ) + poRing->setPoint(i, ReadY(iPoint), ReadX(iPoint), ReadZ(iPoint) ); + else + poRing->setPoint(i, ReadY(iPoint), ReadX(iPoint) ); + } + else + { + if ( chProps & SP_HASZVALUES ) + poRing->setPoint(i, ReadX(iPoint), ReadY(iPoint), ReadZ(iPoint) ); + else + poRing->setPoint(i, ReadX(iPoint), ReadY(iPoint) ); + } + + ++iPoint; + ++i; + } + poPoly->addRingDirectly( poRing ); + } + return poPoly; +} + +/************************************************************************/ +/* ReadMultiPolygon() */ +/************************************************************************/ + +OGRMultiPolygon* OGRMSSQLGeometryParser::ReadMultiPolygon(int iShape) +{ + int i; + OGRMultiPolygon* poMultiPolygon = new OGRMultiPolygon(); + OGRGeometry* poGeom; + + for (i = iShape + 1; i < nNumShapes; i++) + { + poGeom = NULL; + if (ParentOffset(i) == (unsigned int)iShape) + { + if ( ShapeType(i) == ST_POLYGON ) + poGeom = ReadPolygon(i); + } + if ( poGeom ) + poMultiPolygon->addGeometryDirectly( poGeom ); + } + + return poMultiPolygon; +} + +/************************************************************************/ +/* ReadGeometryCollection() */ +/************************************************************************/ + +OGRGeometryCollection* OGRMSSQLGeometryParser::ReadGeometryCollection(int iShape) +{ + int i; + OGRGeometryCollection* poGeomColl = new OGRGeometryCollection(); + OGRGeometry* poGeom; + + for (i = iShape + 1; i < nNumShapes; i++) + { + poGeom = NULL; + if (ParentOffset(i) == (unsigned int)iShape) + { + switch (ShapeType(i)) + { + case ST_POINT: + poGeom = ReadPoint(i); + break; + case ST_LINESTRING: + poGeom = ReadLineString(i); + break; + case ST_POLYGON: + poGeom = ReadPolygon(i); + break; + case ST_MULTIPOINT: + poGeom = ReadMultiPoint(i); + break; + case ST_MULTILINESTRING: + poGeom = ReadMultiLineString(i); + break; + case ST_MULTIPOLYGON: + poGeom = ReadMultiPolygon(i); + break; + case ST_GEOMETRYCOLLECTION: + poGeom = ReadGeometryCollection(i); + break; + } + } + if ( poGeom ) + poGeomColl->addGeometryDirectly( poGeom ); + } + + return poGeomColl; +} + +/************************************************************************/ +/* ParseSqlGeometry() */ +/************************************************************************/ + + +OGRErr OGRMSSQLGeometryParser::ParseSqlGeometry(unsigned char* pszInput, + int nLen, OGRGeometry **poGeom) +{ + if (nLen < 10) + return OGRERR_NOT_ENOUGH_DATA; + + pszData = pszInput; + + /* store the SRS id for further use */ + nSRSId = ReadInt32(0); + + if ( ReadByte(4) != 1 ) + { + return OGRERR_CORRUPT_DATA; + } + + chProps = ReadByte(5); + + if ( chProps & SP_HASMVALUES ) + nPointSize = 32; + else if ( chProps & SP_HASZVALUES ) + nPointSize = 24; + else + nPointSize = 16; + + if ( chProps & SP_ISSINGLEPOINT ) + { + // single point geometry + nNumPoints = 1; + nPointPos = 6; + + if (nLen < 6 + nPointSize) + { + return OGRERR_NOT_ENOUGH_DATA; + } + + if (nColType == MSSQLCOLTYPE_GEOGRAPHY) + { + if (chProps & SP_HASZVALUES) + *poGeom = new OGRPoint(ReadY(0), ReadX(0), ReadZ(0)); + else + *poGeom = new OGRPoint(ReadY(0), ReadX(0)); + } + else + { + if (chProps & SP_HASZVALUES) + *poGeom = new OGRPoint(ReadX(0), ReadY(0), ReadZ(0)); + else + *poGeom = new OGRPoint(ReadX(0), ReadY(0)); + } + } + else if ( chProps & SP_ISSINGLELINESEGMENT ) + { + // single line segment with 2 points + nNumPoints = 2; + nPointPos = 6; + + if (nLen < 6 + 2 * nPointSize) + { + return OGRERR_NOT_ENOUGH_DATA; + } + + OGRLineString* line = new OGRLineString(); + line->setNumPoints(2); + + if (nColType == MSSQLCOLTYPE_GEOGRAPHY) + { + if ( chProps & SP_HASZVALUES ) + { + line->setPoint(0, ReadY(0), ReadX(0), ReadZ(0)); + line->setPoint(1, ReadY(1), ReadX(1), ReadZ(1)); + } + else + { + line->setPoint(0, ReadY(0), ReadX(0)); + line->setPoint(1, ReadY(1), ReadX(1)); + } + } + else + { + if ( chProps & SP_HASZVALUES ) + { + line->setPoint(0, ReadX(0), ReadY(0), ReadZ(0)); + line->setPoint(1, ReadX(1), ReadY(1), ReadZ(1)); + } + else + { + line->setPoint(0, ReadX(0), ReadY(0)); + line->setPoint(1, ReadX(1), ReadY(1)); + } + } + + *poGeom = line; + } + else + { + // complex geometries + nNumPoints = ReadInt32(6); + + if ( nNumPoints <= 0 ) + { + return OGRERR_NONE; + } + + // position of the point array + nPointPos = 10; + + // position of the figures + nFigurePos = nPointPos + nPointSize * nNumPoints + 4; + + if (nLen < nFigurePos) + { + return OGRERR_NOT_ENOUGH_DATA; + } + + nNumFigures = ReadInt32(nFigurePos - 4); + + if ( nNumFigures <= 0 ) + { + return OGRERR_NONE; + } + + // position of the shapes + nShapePos = nFigurePos + 5 * nNumFigures + 4; + + if (nLen < nShapePos) + { + return OGRERR_NOT_ENOUGH_DATA; + } + + nNumShapes = ReadInt32(nShapePos - 4); + + if (nLen < nShapePos + 9 * nNumShapes) + { + return OGRERR_NOT_ENOUGH_DATA; + } + + if ( nNumShapes <= 0 ) + { + return OGRERR_NONE; + } + + // pick up the root shape + if ( ParentOffset(0) != 0xFFFFFFFF) + { + return OGRERR_CORRUPT_DATA; + } + + // determine the shape type + switch (ShapeType(0)) + { + case ST_POINT: + *poGeom = ReadPoint(0); + break; + case ST_LINESTRING: + *poGeom = ReadLineString(0); + break; + case ST_POLYGON: + *poGeom = ReadPolygon(0); + break; + case ST_MULTIPOINT: + *poGeom = ReadMultiPoint(0); + break; + case ST_MULTILINESTRING: + *poGeom = ReadMultiLineString(0); + break; + case ST_MULTIPOLYGON: + *poGeom = ReadMultiPolygon(0); + break; + case ST_GEOMETRYCOLLECTION: + *poGeom = ReadGeometryCollection(0); + break; + default: + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + } + + return OGRERR_NONE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlgeometryvalidator.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlgeometryvalidator.cpp new file mode 100644 index 000000000..f2e46e599 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlgeometryvalidator.cpp @@ -0,0 +1,493 @@ +/****************************************************************************** + * $Id: ogrmssqlgeometryvalidator.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: MSSQL Spatial driver + * Purpose: Implements OGRMSSQLGeometryValidator class to create valid SqlGeometries. + * Author: Tamas Szekeres, szekerest at gmail.com + * + ****************************************************************************** + * Copyright (c) 2010, Tamas Szekeres + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_mssqlspatial.h" + +CPL_CVSID("$Id: ogrmssqlgeometryvalidator.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRMSSQLGeometryValidator() */ +/************************************************************************/ + +OGRMSSQLGeometryValidator::OGRMSSQLGeometryValidator(OGRGeometry *poGeom) +{ + poOriginalGeometry = poGeom; + poValidGeometry = NULL; + bIsValid = ValidateGeometry(poGeom); +} + +/************************************************************************/ +/* ~OGRMSSQLGeometryValidator() */ +/************************************************************************/ + +OGRMSSQLGeometryValidator::~OGRMSSQLGeometryValidator() +{ + if (poValidGeometry) + delete poValidGeometry; +} + +/************************************************************************/ +/* ValidatePoint() */ +/************************************************************************/ + +int OGRMSSQLGeometryValidator::ValidatePoint(CPL_UNUSED OGRPoint* poGeom) +{ + return TRUE; +} + +/************************************************************************/ +/* ValidateMultiPoint() */ +/************************************************************************/ + +int OGRMSSQLGeometryValidator::ValidateMultiPoint(CPL_UNUSED OGRMultiPoint* poGeom) +{ + return TRUE; +} + +/************************************************************************/ +/* ValidateLineString() */ +/************************************************************************/ + +int OGRMSSQLGeometryValidator::ValidateLineString(OGRLineString * poGeom) +{ + OGRPoint* poPoint0 = NULL; + int i; + int bResult = FALSE; + + for (i = 0; i < poGeom->getNumPoints(); i++) + { + if (poPoint0 == NULL) + { + poPoint0 = new OGRPoint(); + poGeom->getPoint(i, poPoint0); + continue; + } + + if (poPoint0->getX() == poGeom->getX(i) && poPoint0->getY() == poGeom->getY(i)) + continue; + + bResult = TRUE; + break; + } + + if (!bResult) + { + if (poValidGeometry) + delete poValidGeometry; + + poValidGeometry = NULL; + + // create a compatible geometry + if (poPoint0 != NULL) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Linestring has no distinct points constructing point geometry instead." ); + + // create a point + poValidGeometry = poPoint0; + poPoint0 = NULL; + } + else + { + CPLError( CE_Warning, CPLE_NotSupported, + "Linestring has no points. Removing the geometry from the output." ); + } + } + + if (poPoint0) + delete poPoint0; + + return bResult; +} + +/************************************************************************/ +/* ValidateLinearRing() */ +/************************************************************************/ + +int OGRMSSQLGeometryValidator::ValidateLinearRing(OGRLinearRing * poGeom) +{ + OGRPoint* poPoint0 = NULL; + OGRPoint* poPoint1 = NULL; + int i; + int bResult = FALSE; + + poGeom->closeRings(); + + for (i = 0; i < poGeom->getNumPoints(); i++) + { + if (poPoint0 == NULL) + { + poPoint0 = new OGRPoint(); + poGeom->getPoint(i, poPoint0); + continue; + } + + if (poPoint0->getX() == poGeom->getX(i) && poPoint0->getY() == poGeom->getY(i)) + continue; + + if (poPoint1 == NULL) + { + poPoint1 = new OGRPoint(); + poGeom->getPoint(i, poPoint1); + continue; + } + + if (poPoint1->getX() == poGeom->getX(i) && poPoint1->getY() == poGeom->getY(i)) + continue; + + bResult = TRUE; + break; + } + + if (!bResult) + { + if (poValidGeometry) + delete poValidGeometry; + + poValidGeometry = NULL; + + // create a compatible geometry + if (poPoint1 != NULL) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Linear ring has only 2 distinct points constructing linestring geometry instead." ); + + // create a linestring + poValidGeometry = new OGRLineString(); + ((OGRLineString*)poValidGeometry)->setNumPoints( 2 ); + ((OGRLineString*)poValidGeometry)->addPoint(poPoint0); + ((OGRLineString*)poValidGeometry)->addPoint(poPoint1); + } + else if (poPoint0 != NULL) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Linear ring has no distinct points constructing point geometry instead." ); + + // create a point + poValidGeometry = poPoint0; + poPoint0 = NULL; + } + else + { + CPLError( CE_Warning, CPLE_NotSupported, + "Linear ring has no points. Removing the geometry from the output." ); + } + } + + if (poPoint0) + delete poPoint0; + + if (poPoint1) + delete poPoint1; + + return bResult; +} + +/************************************************************************/ +/* ValidateMultiLineString() */ +/************************************************************************/ + +int OGRMSSQLGeometryValidator::ValidateMultiLineString(OGRMultiLineString * poGeom) +{ + int i, j; + OGRGeometry* poLineString; + OGRGeometryCollection* poGeometries = NULL; + + for (i = 0; i < poGeom->getNumGeometries(); i++) + { + poLineString = poGeom->getGeometryRef(i); + if (poLineString->getGeometryType() != wkbLineString && poLineString->getGeometryType() != wkbLineString25D) + { + // non linestring geometry + if (!poGeometries) + { + poGeometries = new OGRGeometryCollection(); + for (j = 0; j < i; j++) + poGeometries->addGeometry(poGeom->getGeometryRef(j)); + } + if (ValidateGeometry(poLineString)) + poGeometries->addGeometry(poLineString); + else + poGeometries->addGeometry(poValidGeometry); + + continue; + } + + if (!ValidateLineString((OGRLineString*)poLineString)) + { + // non valid linestring + if (!poGeometries) + { + poGeometries = new OGRGeometryCollection(); + for (j = 0; j < i; j++) + poGeometries->addGeometry(poGeom->getGeometryRef(j)); + } + + poGeometries->addGeometry(poValidGeometry); + continue; + } + + if (poGeometries) + poGeometries->addGeometry(poLineString); + } + + if (poGeometries) + { + if (poValidGeometry) + delete poValidGeometry; + + poValidGeometry = poGeometries; + } + + return (poValidGeometry == NULL); +} + +/************************************************************************/ +/* ValidatePolygon() */ +/************************************************************************/ + +int OGRMSSQLGeometryValidator::ValidatePolygon(OGRPolygon* poGeom) +{ + int i,j; + OGRLinearRing* poRing = poGeom->getExteriorRing(); + OGRGeometry* poInteriorRing; + + if (poRing == NULL) + return FALSE; + + OGRGeometryCollection* poGeometries = NULL; + + if (!ValidateLinearRing(poRing)) + { + if (poGeom->getNumInteriorRings() > 0) + { + poGeometries = new OGRGeometryCollection(); + poGeometries->addGeometryDirectly(poValidGeometry); + } + } + + for (i = 0; i < poGeom->getNumInteriorRings(); i++) + { + poInteriorRing = poGeom->getInteriorRing(i); + if (!ValidateLinearRing((OGRLinearRing*)poInteriorRing)) + { + if (!poGeometries) + { + poGeometries = new OGRGeometryCollection(); + poGeometries->addGeometry(poRing); + for (j = 0; j < i; j++) + poGeometries->addGeometry(poGeom->getInteriorRing(j)); + } + + poGeometries->addGeometry(poValidGeometry); + continue; + } + + if (poGeometries) + poGeometries->addGeometry(poInteriorRing); + } + + if (poGeometries) + { + if (poValidGeometry) + delete poValidGeometry; + + poValidGeometry = poGeometries; + } + + return (poValidGeometry == NULL); +} + +/************************************************************************/ +/* ValidateMultiPolygon() */ +/************************************************************************/ + +int OGRMSSQLGeometryValidator::ValidateMultiPolygon(OGRMultiPolygon* poGeom) +{ + int i, j; + OGRGeometry* poPolygon; + OGRGeometryCollection* poGeometries = NULL; + + for (i = 0; i < poGeom->getNumGeometries(); i++) + { + poPolygon = poGeom->getGeometryRef(i); + if (poPolygon->getGeometryType() != wkbPolygon && poPolygon->getGeometryType() != wkbPolygon25D) + { + // non polygon geometry + if (!poGeometries) + { + poGeometries = new OGRGeometryCollection(); + for (j = 0; j < i; j++) + poGeometries->addGeometry(poGeom->getGeometryRef(j)); + } + if (ValidateGeometry(poPolygon)) + poGeometries->addGeometry(poPolygon); + else + poGeometries->addGeometry(poValidGeometry); + + continue; + } + + if (!ValidatePolygon((OGRPolygon*)poPolygon)) + { + // non valid polygon + if (!poGeometries) + { + poGeometries = new OGRGeometryCollection(); + for (j = 0; j < i; j++) + poGeometries->addGeometry(poGeom->getGeometryRef(j)); + } + + poGeometries->addGeometry(poValidGeometry); + continue; + } + + if (poGeometries) + poGeometries->addGeometry(poPolygon); + } + + if (poGeometries) + { + if (poValidGeometry) + delete poValidGeometry; + + poValidGeometry = poGeometries; + } + + return poValidGeometry == NULL; +} + +/************************************************************************/ +/* ValidateGeometryCollection() */ +/************************************************************************/ + +int OGRMSSQLGeometryValidator::ValidateGeometryCollection(OGRGeometryCollection* poGeom) +{ + int i, j; + OGRGeometry* poGeometry; + OGRGeometryCollection* poGeometries = NULL; + + for (i = 0; i < poGeom->getNumGeometries(); i++) + { + poGeometry = poGeom->getGeometryRef(i); + + if (!ValidateGeometry(poGeometry)) + { + // non valid geometry + if (!poGeometries) + { + poGeometries = new OGRGeometryCollection(); + for (j = 0; j < i; j++) + poGeometries->addGeometry(poGeom->getGeometryRef(j)); + } + + if (poValidGeometry) + poGeometries->addGeometry(poValidGeometry); + continue; + } + + if (poGeometries) + poGeometries->addGeometry(poGeometry); + } + + if (poGeometries) + { + if (poValidGeometry) + delete poValidGeometry; + + poValidGeometry = poGeometries; + } + + return (poValidGeometry == NULL); +} + +/************************************************************************/ +/* ValidateGeometry() */ +/************************************************************************/ + +int OGRMSSQLGeometryValidator::ValidateGeometry(OGRGeometry* poGeom) +{ + if (!poGeom) + return FALSE; + + switch (poGeom->getGeometryType()) + { + case wkbPoint: + case wkbPoint25D: + return ValidatePoint((OGRPoint*)poGeom); + case wkbLineString: + case wkbLineString25D: + return ValidateLineString((OGRLineString*)poGeom); + case wkbPolygon: + case wkbPolygon25D: + return ValidatePolygon((OGRPolygon*)poGeom); + case wkbMultiPoint: + case wkbMultiPoint25D: + return ValidateMultiPoint((OGRMultiPoint*)poGeom); + case wkbMultiLineString: + case wkbMultiLineString25D: + return ValidateMultiLineString((OGRMultiLineString*)poGeom); + case wkbMultiPolygon: + case wkbMultiPolygon25D: + return ValidateMultiPolygon((OGRMultiPolygon*)poGeom); + case wkbGeometryCollection: + case wkbGeometryCollection25D: + return ValidateGeometryCollection((OGRGeometryCollection*)poGeom); + case wkbLinearRing: + return ValidateLinearRing((OGRLinearRing*)poGeom); + default: + return FALSE; + } +} + +/************************************************************************/ +/* GetValidGeometryRef() */ +/************************************************************************/ +OGRGeometry* OGRMSSQLGeometryValidator::GetValidGeometryRef() +{ + if (bIsValid || poOriginalGeometry == NULL) + return poOriginalGeometry; + + if (poValidGeometry) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Invalid geometry has been converted from %s to %s.", + poOriginalGeometry->getGeometryName(), + poValidGeometry->getGeometryName() ); + } + else + { + CPLError( CE_Warning, CPLE_NotSupported, + "Invalid geometry has been converted from %s to null.", + poOriginalGeometry->getGeometryName()); + } + + return poValidGeometry; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatialdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatialdatasource.cpp new file mode 100644 index 000000000..250f6a39c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatialdatasource.cpp @@ -0,0 +1,1471 @@ +/****************************************************************************** + * $Id: ogrmssqlspatialdatasource.cpp 29185 2015-05-12 10:45:44Z tamas $ + * + * Project: MSSQL Spatial driver + * Purpose: Implements OGRMSSQLSpatialDataSource class.. + * Author: Tamas Szekeres, szekerest at gmail.com + * + ****************************************************************************** + * Copyright (c) 2010, Tamas Szekeres + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_mssqlspatial.h" + +CPL_CVSID("$Id: ogrmssqlspatialdatasource.cpp 29185 2015-05-12 10:45:44Z tamas $"); + +/************************************************************************/ +/* OGRMSSQLSpatialDataSource() */ +/************************************************************************/ + +OGRMSSQLSpatialDataSource::OGRMSSQLSpatialDataSource() + +{ + pszName = NULL; + pszCatalog = NULL; + papoLayers = NULL; + nLayers = 0; + + nKnownSRID = 0; + panSRID = NULL; + papoSRS = NULL; + + nGeometryFormat = MSSQLGEOMETRY_NATIVE; + + bUseGeometryColumns = CSLTestBoolean(CPLGetConfigOption("MSSQLSPATIAL_USE_GEOMETRY_COLUMNS", "YES")); + bListAllTables = CSLTestBoolean(CPLGetConfigOption("MSSQLSPATIAL_LIST_ALL_TABLES", "NO")); +} + +/************************************************************************/ +/* ~OGRMSSQLSpatialDataSource() */ +/************************************************************************/ + +OGRMSSQLSpatialDataSource::~OGRMSSQLSpatialDataSource() + +{ + int i; + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + CPLFree( pszName ); + CPLFree( pszCatalog ); + + for( i = 0; i < nKnownSRID; i++ ) + { + if( papoSRS[i] != NULL ) + papoSRS[i]->Release(); + } + CPLFree( panSRID ); + CPLFree( papoSRS ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRMSSQLSpatialDataSource::TestCapability( const char * pszCap ) + +{ +#if (ODBCVER >= 0x0300) + if ( EQUAL(pszCap,ODsCTransactions) ) + return TRUE; +#endif + if( EQUAL(pszCap,ODsCCreateLayer) || EQUAL(pszCap,ODsCDeleteLayer) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRMSSQLSpatialDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer *OGRMSSQLSpatialDataSource::GetLayerByName( const char* pszLayerName ) + +{ + if (!pszLayerName) + return NULL; + + char *pszTableName = NULL; + char *pszSchemaName = NULL; + + const char* pszDotPos = strstr(pszLayerName,"."); + if ( pszDotPos != NULL ) + { + int length = pszDotPos - pszLayerName; + pszSchemaName = (char*)CPLMalloc(length+1); + strncpy(pszSchemaName, pszLayerName, length); + pszSchemaName[length] = '\0'; + pszTableName = CPLStrdup( pszDotPos + 1 ); //skip "." + } + else + { + pszSchemaName = CPLStrdup("dbo"); + pszTableName = CPLStrdup( pszLayerName ); + } + + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszTableName,papoLayers[iLayer]->GetTableName()) && + EQUAL(pszSchemaName,papoLayers[iLayer]->GetSchemaName()) ) + { + CPLFree( pszSchemaName ); + CPLFree( pszTableName ); + return papoLayers[iLayer]; + } + } + + CPLFree( pszSchemaName ); + CPLFree( pszTableName ); + + return NULL; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +int OGRMSSQLSpatialDataSource::DeleteLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Blow away our OGR structures related to the layer. This is */ +/* pretty dangerous if anything has a reference to this layer! */ +/* -------------------------------------------------------------------- */ + const char* pszTableName = papoLayers[iLayer]->GetTableName(); + const char* pszSchemaName = papoLayers[iLayer]->GetSchemaName(); + + CPLODBCStatement oStmt( &oSession ); + if (bUseGeometryColumns) + oStmt.Appendf( "DELETE FROM geometry_columns WHERE f_table_schema = '%s' AND f_table_name = '%s'\n", + pszSchemaName, pszTableName ); + oStmt.Appendf("DROP TABLE [%s].[%s]", pszSchemaName, pszTableName ); + + CPLDebug( "MSSQLSpatial", "DeleteLayer(%s)", pszTableName ); + + papoLayers[iLayer]->SetSpatialIndexFlag(FALSE); + + delete papoLayers[iLayer]; + memmove( papoLayers + iLayer, papoLayers + iLayer + 1, + sizeof(void *) * (nLayers - iLayer - 1) ); + nLayers--; + + if ( strlen(pszTableName) == 0 ) + return OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* Remove from the database. */ +/* -------------------------------------------------------------------- */ + + int bInTransaction = oSession.IsInTransaction(); + if (!bInTransaction) + oSession.BeginTransaction(); + + if( !oStmt.ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error deleting layer: %s", GetSession()->GetLastError() ); + + if (!bInTransaction) + oSession.RollbackTransaction(); + + return OGRERR_FAILURE; + } + + if (!bInTransaction) + oSession.CommitTransaction(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateLayer() */ +/************************************************************************/ + +OGRLayer * OGRMSSQLSpatialDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ) + +{ + char *pszTableName = NULL; + char *pszSchemaName = NULL; + const char *pszGeomType = NULL; + const char *pszGeomColumn = NULL; + int nCoordDimension = 3; + char *pszFIDColumnName = NULL; + + /* determine the dimension */ + if( eType == wkbFlatten(eType) ) + nCoordDimension = 2; + + if( CSLFetchNameValue( papszOptions, "DIM") != NULL ) + nCoordDimension = atoi(CSLFetchNameValue( papszOptions, "DIM")); + + /* MSSQL Schema handling: + Extract schema name from input layer name or passed with -lco SCHEMA. + Set layer name to "schema.table" or to "table" if schema is not + specified + */ + const char* pszDotPos = strstr(pszLayerName,"."); + if ( pszDotPos != NULL ) + { + int length = pszDotPos - pszLayerName; + pszSchemaName = (char*)CPLMalloc(length+1); + strncpy(pszSchemaName, pszLayerName, length); + pszSchemaName[length] = '\0'; + + if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) ) + pszTableName = LaunderName( pszDotPos + 1 ); //skip "." + else + pszTableName = CPLStrdup( pszDotPos + 1 ); //skip "." + } + else + { + pszSchemaName = NULL; + if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) ) + pszTableName = LaunderName( pszLayerName ); //skip "." + else + pszTableName = CPLStrdup( pszLayerName ); //skip "." + } + + if( CSLFetchNameValue( papszOptions, "SCHEMA" ) != NULL ) + { + CPLFree(pszSchemaName); + pszSchemaName = CPLStrdup(CSLFetchNameValue( papszOptions, "SCHEMA" )); + } + + if (pszSchemaName == NULL) + pszSchemaName = CPLStrdup("dbo"); + +/* -------------------------------------------------------------------- */ +/* Do we already have this layer? If so, should we blow it */ +/* away? */ +/* -------------------------------------------------------------------- */ + int iLayer; + + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszTableName,papoLayers[iLayer]->GetTableName()) && + EQUAL(pszSchemaName,papoLayers[iLayer]->GetSchemaName()) ) + { + if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL + && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") ) + { + if (!pszSchemaName) + pszSchemaName = CPLStrdup(papoLayers[iLayer]->GetSchemaName()); + + DeleteLayer( iLayer ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + pszLayerName ); + + CPLFree( pszSchemaName ); + CPLFree( pszTableName ); + return NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Handle the GEOM_TYPE option. */ +/* -------------------------------------------------------------------- */ + if ( eType != wkbNone ) + { + pszGeomType = CSLFetchNameValue( papszOptions, "GEOM_TYPE" ); + + if( !pszGeomType ) + pszGeomType = "geometry"; + + if( !EQUAL(pszGeomType, "geometry") + && !EQUAL(pszGeomType, "geography")) + { + CPLError( CE_Failure, CPLE_AppDefined, + "FORMAT=%s not recognised or supported.", + pszGeomType ); + + CPLFree( pszSchemaName ); + CPLFree( pszTableName ); + return NULL; + } + + /* determine the geometry column name */ + pszGeomColumn = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME"); + if (!pszGeomColumn) + pszGeomColumn = CSLFetchNameValue( papszOptions, "GEOM_NAME"); + if (!pszGeomColumn) + pszGeomColumn = "ogr_geometry"; + } + int bGeomNullable = CSLFetchBoolean(papszOptions, "GEOMETRY_NULLABLE", TRUE); + +/* -------------------------------------------------------------------- */ +/* Initialize the metadata tables */ +/* -------------------------------------------------------------------- */ + + if (InitializeMetadataTables() != OGRERR_NONE) + { + CPLFree( pszSchemaName ); + CPLFree( pszTableName ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to get the SRS Id of this spatial reference system, */ +/* adding to the srs table if needed. */ +/* -------------------------------------------------------------------- */ + int nSRSId = 0; + + if( CSLFetchNameValue( papszOptions, "SRID") != NULL ) + nSRSId = atoi(CSLFetchNameValue( papszOptions, "SRID")); + + if( nSRSId == 0 && poSRS != NULL ) + nSRSId = FetchSRSId( poSRS ); + +/* -------------------------------------------------------------------- */ +/* Create a new table and create a new entry in the geometry, */ +/* geometry_columns metadata table. */ +/* -------------------------------------------------------------------- */ + + CPLODBCStatement oStmt( &oSession ); + + if( eType != wkbNone && bUseGeometryColumns) + { + const char *pszGeometryType = OGRToOGCGeomType(eType); + + oStmt.Appendf( "DELETE FROM geometry_columns WHERE f_table_schema = '%s' " + "AND f_table_name = '%s'\n", pszSchemaName, pszTableName ); + + oStmt.Appendf("INSERT INTO [geometry_columns] ([f_table_catalog], [f_table_schema] ,[f_table_name], " + "[f_geometry_column],[coord_dimension],[srid],[geometry_type]) VALUES ('%s', '%s', '%s', '%s', %d, %d, '%s')\n", + pszCatalog, pszSchemaName, pszTableName, pszGeomColumn, nCoordDimension, nSRSId, pszGeometryType ); + } + + if (!EQUAL(pszSchemaName,"dbo")) + { + // creating the schema if not exists + oStmt.Appendf("IF NOT EXISTS (SELECT name from sys.schemas WHERE name = '%s') EXEC sp_executesql N'CREATE SCHEMA [%s]'\n", pszSchemaName, pszSchemaName); + } + + /* determine the FID column name */ + const char* pszFIDColumnNameIn = CSLFetchNameValueDef(papszOptions, "FID", "ogr_fid"); + if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) ) + pszFIDColumnName = LaunderName( pszFIDColumnNameIn ); + else + pszFIDColumnName = CPLStrdup( pszFIDColumnNameIn ); + + int bFID64 = CSLFetchBoolean(papszOptions, "FID64", FALSE); + const char* pszFIDType = bFID64 ? "bigint": "int"; + + if( eType == wkbNone ) + { + oStmt.Appendf("CREATE TABLE [%s].[%s] ([%s] [%s] IDENTITY(1,1) NOT NULL, " + "CONSTRAINT [PK_%s] PRIMARY KEY CLUSTERED ([%s] ASC))", + pszSchemaName, pszTableName, pszFIDColumnName, pszFIDType, pszTableName, pszFIDColumnName); + } + else + { + oStmt.Appendf("CREATE TABLE [%s].[%s] ([%s] [%s] IDENTITY(1,1) NOT NULL, " + "[%s] [%s] %s, CONSTRAINT [PK_%s] PRIMARY KEY CLUSTERED ([%s] ASC))", + pszSchemaName, pszTableName, pszFIDColumnName, pszFIDType, pszGeomColumn, pszGeomType, + bGeomNullable? "NULL":"NOT NULL", pszTableName, pszFIDColumnName); + } + + CPLFree( pszFIDColumnName ); + + int bInTransaction = oSession.IsInTransaction(); + if (!bInTransaction) + oSession.BeginTransaction(); + + if( !oStmt.ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error creating layer: %s", GetSession()->GetLastError() ); + + if (!bInTransaction) + oSession.RollbackTransaction(); + + return NULL; + } + + if (!bInTransaction) + oSession.CommitTransaction(); + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRMSSQLSpatialTableLayer *poLayer; + + poLayer = new OGRMSSQLSpatialTableLayer( this ); + + if (bInTransaction) + poLayer->SetLayerStatus(MSSQLLAYERSTATUS_INITIAL); + else + poLayer->SetLayerStatus(MSSQLLAYERSTATUS_CREATED); + + poLayer->SetLaunderFlag( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) ); + poLayer->SetPrecisionFlag( CSLFetchBoolean(papszOptions,"PRECISION",TRUE)); + + const char *pszSI = CSLFetchNameValue( papszOptions, "SPATIAL_INDEX" ); + int bCreateSpatialIndex = ( pszSI == NULL || CSLTestBoolean(pszSI) ); + poLayer->SetSpatialIndexFlag( bCreateSpatialIndex ); + + const char *pszUploadGeometryFormat = CSLFetchNameValue( papszOptions, "UPLOAD_GEOM_FORMAT" ); + if (pszUploadGeometryFormat) + { + if (EQUALN(pszUploadGeometryFormat,"wkb",5)) + poLayer->SetUploadGeometryFormat(MSSQLGEOMETRY_WKB); + else if (EQUALN(pszUploadGeometryFormat, "wkt",3)) + poLayer->SetUploadGeometryFormat(MSSQLGEOMETRY_WKT); + } + + char *pszWKT = NULL; + if( poSRS && poSRS->exportToWkt( &pszWKT ) != OGRERR_NONE ) + { + CPLFree(pszWKT); + pszWKT = NULL; + } + + if( bFID64 ) + poLayer->SetMetadataItem(OLMD_FID64, "YES"); + + if (poLayer->Initialize(pszSchemaName, pszTableName, pszGeomColumn, nCoordDimension, nSRSId, pszWKT, eType) == OGRERR_FAILURE) + { + CPLFree( pszSchemaName ); + CPLFree( pszTableName ); + CPLFree( pszWKT ); + return NULL; + } + + CPLFree( pszSchemaName ); + CPLFree( pszTableName ); + CPLFree( pszWKT ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRMSSQLSpatialTableLayer **) + CPLRealloc( papoLayers, sizeof(OGRMSSQLSpatialTableLayer *) * (nLayers+1) ); + + papoLayers[nLayers++] = poLayer; + + + return poLayer; +} + +/************************************************************************/ +/* OpenTable() */ +/************************************************************************/ + +int OGRMSSQLSpatialDataSource::OpenTable( const char *pszSchemaName, const char *pszTableName, + const char *pszGeomCol, int nCoordDimension, + int nSRID, const char *pszSRText, OGRwkbGeometryType eType, + CPL_UNUSED int bUpdate ) +{ +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRMSSQLSpatialTableLayer *poLayer = new OGRMSSQLSpatialTableLayer( this ); + + if( poLayer->Initialize( pszSchemaName, pszTableName, pszGeomCol, nCoordDimension, nSRID, pszSRText, eType ) ) + { + delete poLayer; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRMSSQLSpatialTableLayer **) + CPLRealloc( papoLayers, sizeof(OGRMSSQLSpatialTableLayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + + return TRUE; +} + + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int OGRMSSQLSpatialDataSource::GetLayerCount() +{ + return nLayers; +} + +/************************************************************************/ +/* ParseValue() */ +/************************************************************************/ + +int OGRMSSQLSpatialDataSource::ParseValue(char** pszValue, char* pszSource, const char* pszKey, int nStart, int nNext, int nTerm, int bRemove) +{ + int nLen = strlen(pszKey); + if ((*pszValue) == NULL && nStart + nLen < nNext && + EQUALN(pszSource + nStart, pszKey, nLen)) + { + *pszValue = (char*)CPLMalloc( sizeof(char) * (nNext - nStart - nLen + 1) ); + if (*pszValue) + strncpy(*pszValue, pszSource + nStart + nLen, nNext - nStart - nLen); + (*pszValue)[nNext - nStart - nLen] = 0; + + if (bRemove) + { + // remove the value from the source string + if (pszSource[nNext] == ';') + memmove( pszSource + nStart, pszSource + nNext + 1, nTerm - nNext); + else + memmove( pszSource + nStart, pszSource + nNext, nTerm - nNext + 1); + } + return TRUE; + } + return FALSE; +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRMSSQLSpatialDataSource::Open( const char * pszNewName, int bUpdate, + int bTestOpen ) + +{ + CPLAssert( nLayers == 0 ); + + if( !EQUALN(pszNewName,"MSSQL:",6) ) + { + if( !bTestOpen ) + CPLError( CE_Failure, CPLE_AppDefined, + "%s does not conform to MSSSQLSpatial naming convention," + " MSSQL:*\n", pszNewName ); + return FALSE; + } + + /* Determine if the connection string contains specific values */ + char* pszTableSpec = NULL; + char* pszGeometryFormat = NULL; + char* pszConnectionName = CPLStrdup(pszNewName + 6); + char* pszDriver = NULL; + int nCurrent, nNext, nTerm; + nCurrent = nNext = nTerm = strlen(pszConnectionName); + + while (nCurrent > 0) + { + --nCurrent; + if (pszConnectionName[nCurrent] == ';') + { + nNext = nCurrent; + continue; + } + + if (ParseValue(&pszCatalog, pszConnectionName, "database=", + nCurrent, nNext, nTerm, FALSE)) + continue; + + if (ParseValue(&pszTableSpec, pszConnectionName, "tables=", + nCurrent, nNext, nTerm, TRUE)) + continue; + + if (ParseValue(&pszDriver, pszConnectionName, "driver=", + nCurrent, nNext, nTerm, FALSE)) + continue; + + if (ParseValue(&pszGeometryFormat, pszConnectionName, + "geometryformat=", nCurrent, nNext, nTerm, TRUE)) + { + if (EQUALN(pszGeometryFormat,"wkbzm",5)) + nGeometryFormat = MSSQLGEOMETRY_WKBZM; + else if (EQUALN(pszGeometryFormat, "wkb",3)) + nGeometryFormat = MSSQLGEOMETRY_WKB; + else if (EQUALN(pszGeometryFormat,"wkt",3)) + nGeometryFormat = MSSQLGEOMETRY_WKT; + else if (EQUALN(pszGeometryFormat,"native",6)) + nGeometryFormat = MSSQLGEOMETRY_NATIVE; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid geometry type specified: %s," + " MSSQL:*\n", pszGeometryFormat ); + + CPLFree(pszTableSpec); + CPLFree(pszGeometryFormat); + CPLFree(pszConnectionName); + CPLFree(pszDriver); + return FALSE; + } + + CPLFree(pszGeometryFormat); + pszGeometryFormat = NULL; + continue; + } + } + + /* Determine if the connection string contains the catalog portion */ + if( pszCatalog == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "'%s' does not contain the 'database' portion\n", pszNewName ); + + CPLFree(pszTableSpec); + CPLFree(pszGeometryFormat); + CPLFree(pszConnectionName); + CPLFree(pszDriver); + return FALSE; + } + + pszName = CPLStrdup(pszNewName); + + char **papszTableNames=NULL; + char **papszSchemaNames=NULL; + char **papszGeomColumnNames=NULL; + char **papszCoordDimensions=NULL; + char **papszSRIds=NULL; + char **papszSRTexts=NULL; + + /* Determine if the connection string contains the TABLES portion */ + if( pszTableSpec != NULL ) + { + char **papszTableList; + int i; + + papszTableList = CSLTokenizeString2( pszTableSpec, ",", 0 ); + + for( i = 0; i < CSLCount(papszTableList); i++ ) + { + char **papszQualifiedParts; + + // Get schema and table name + papszQualifiedParts = CSLTokenizeString2( papszTableList[i], + ".", 0 ); + + /* Find the geometry column name if specified */ + if( CSLCount( papszQualifiedParts ) >= 1 ) + { + char* pszGeomColumnName = NULL; + char* pos = strchr(papszQualifiedParts[CSLCount( papszQualifiedParts ) - 1], '('); + if (pos != NULL) + { + *pos = '\0'; + pszGeomColumnName = pos+1; + int len = strlen(pszGeomColumnName); + if (len > 0) + pszGeomColumnName[len - 1] = '\0'; + } + papszGeomColumnNames = CSLAddString( papszGeomColumnNames, + pszGeomColumnName ? pszGeomColumnName : ""); + } + + if( CSLCount( papszQualifiedParts ) == 2 ) + { + papszSchemaNames = CSLAddString( papszSchemaNames, + papszQualifiedParts[0] ); + papszTableNames = CSLAddString( papszTableNames, + papszQualifiedParts[1] ); + } + else if( CSLCount( papszQualifiedParts ) == 1 ) + { + papszSchemaNames = CSLAddString( papszSchemaNames, "dbo"); + papszTableNames = CSLAddString( papszTableNames, + papszQualifiedParts[0] ); + } + + CSLDestroy(papszQualifiedParts); + } + + CSLDestroy(papszTableList); + } + + CPLFree(pszTableSpec); + + /* Initialize the SQL Server connection. */ + int nResult; + if ( pszDriver != NULL ) + { + /* driver has been specified */ + CPLDebug( "OGR_MSSQLSpatial", "EstablishSession(Connection:\"%s\")", pszConnectionName); + nResult = oSession.EstablishSession( pszConnectionName, "", "" ); + } + else + { + /* no driver has been specified, defautls to SQL Server */ + CPLDebug( "OGR_MSSQLSpatial", "EstablishSession(Connection:\"%s\")", pszConnectionName); + nResult = oSession.EstablishSession( CPLSPrintf("DRIVER=SQL Server;%s", pszConnectionName), "", "" ); + } + + CPLFree(pszDriver); + + if( !nResult ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to initialize connection to the server for %s,\n" + "%s", pszNewName, oSession.GetLastError() ); + + CSLDestroy( papszTableNames ); + CSLDestroy( papszSchemaNames ); + CSLDestroy( papszGeomColumnNames ); + CSLDestroy( papszCoordDimensions ); + CSLDestroy( papszSRIds ); + CSLDestroy( papszSRTexts ); + CPLFree(pszGeometryFormat); + CPLFree(pszConnectionName); + return FALSE; + } + + char** papszTypes = NULL; + + /* read metadata for the specified tables */ + if (papszTableNames != NULL && bUseGeometryColumns) + { + for( int iTable = 0; + papszTableNames != NULL && papszTableNames[iTable] != NULL; + iTable++ ) + { + CPLODBCStatement oStmt( &oSession ); + + /* Use join to make sure the existence of the referred column/table */ + oStmt.Appendf( "SELECT f_geometry_column, coord_dimension, g.srid, srtext, geometry_type FROM dbo.geometry_columns g JOIN INFORMATION_SCHEMA.COLUMNS ON f_table_schema = TABLE_SCHEMA and f_table_name = TABLE_NAME and f_geometry_column = COLUMN_NAME left outer join dbo.spatial_ref_sys s on g.srid = s.srid WHERE f_table_schema = '%s' AND f_table_name = '%s'", papszSchemaNames[iTable], papszTableNames[iTable]); + + if( oStmt.ExecuteSQL() ) + { + while( oStmt.Fetch() ) + { + if (papszGeomColumnNames == NULL) + papszGeomColumnNames = CSLAddString( papszGeomColumnNames, oStmt.GetColData(0) ); + else if (*papszGeomColumnNames[iTable] == 0) + { + CPLFree(papszGeomColumnNames[iTable]); + papszGeomColumnNames[iTable] = CPLStrdup( oStmt.GetColData(0) ); + } + + papszCoordDimensions = + CSLAddString( papszCoordDimensions, oStmt.GetColData(1, "2") ); + papszSRIds = + CSLAddString( papszSRIds, oStmt.GetColData(2, "0") ); + papszSRTexts = + CSLAddString( papszSRTexts, oStmt.GetColData(3, "") ); + papszTypes = + CSLAddString( papszTypes, oStmt.GetColData(4, "GEOMETRY") ); + } + } + else + { + /* probably the table is missing at all */ + InitializeMetadataTables(); + } + } + } + + /* if requesting all user database table then this takes priority */ + if (papszTableNames == NULL && bListAllTables) + { + CPLODBCStatement oStmt( &oSession ); + + oStmt.Append( "select sys.schemas.name, sys.schemas.name + '.' + sys.objects.name, sys.columns.name from sys.columns join sys.types on sys.columns.system_type_id = sys.types.system_type_id and sys.columns.user_type_id = sys.types.user_type_id join sys.objects on sys.objects.object_id = sys.columns.object_id join sys.schemas on sys.objects.schema_id = sys.schemas.schema_id where (sys.types.name = 'geometry' or sys.types.name = 'geography') and (sys.objects.type = 'U' or sys.objects.type = 'V') union all select sys.schemas.name, sys.schemas.name + '.' + sys.objects.name, '' from sys.objects join sys.schemas on sys.objects.schema_id = sys.schemas.schema_id where not exists (select * from sys.columns sc1 join sys.types on sc1.system_type_id = sys.types.system_type_id where (sys.types.name = 'geometry' or sys.types.name = 'geography') and sys.objects.object_id = sc1.object_id) and (sys.objects.type = 'U' or sys.objects.type = 'V')" ); + + if( oStmt.ExecuteSQL() ) + { + while( oStmt.Fetch() ) + { + papszSchemaNames = + CSLAddString( papszSchemaNames, oStmt.GetColData(0) ); + papszTableNames = + CSLAddString( papszTableNames, oStmt.GetColData(1) ); + papszGeomColumnNames = + CSLAddString( papszGeomColumnNames, oStmt.GetColData(2) ); + } + } + } + + /* Determine the available tables if not specified. */ + if (papszTableNames == NULL && bUseGeometryColumns) + { + CPLODBCStatement oStmt( &oSession ); + + /* Use join to make sure the existence of the referred column/table */ + oStmt.Append( "SELECT f_table_schema, f_table_name, f_geometry_column, coord_dimension, g.srid, srtext, geometry_type FROM dbo.geometry_columns g JOIN INFORMATION_SCHEMA.COLUMNS ON f_table_schema = TABLE_SCHEMA and f_table_name = TABLE_NAME and f_geometry_column = COLUMN_NAME left outer join dbo.spatial_ref_sys s on g.srid = s.srid"); + + if( oStmt.ExecuteSQL() ) + { + while( oStmt.Fetch() ) + { + papszSchemaNames = + CSLAddString( papszSchemaNames, oStmt.GetColData(0, "dbo") ); + papszTableNames = + CSLAddString( papszTableNames, oStmt.GetColData(1) ); + papszGeomColumnNames = + CSLAddString( papszGeomColumnNames, oStmt.GetColData(2) ); + papszCoordDimensions = + CSLAddString( papszCoordDimensions, oStmt.GetColData(3, "2") ); + papszSRIds = + CSLAddString( papszSRIds, oStmt.GetColData(4, "0") ); + papszSRTexts = + CSLAddString( papszSRTexts, oStmt.GetColData(5, "") ); + papszTypes = + CSLAddString( papszTypes, oStmt.GetColData(6, "GEOMETRY") ); + } + } + else + { + /* probably the table is missing at all */ + InitializeMetadataTables(); + } + } + + /* Query catalog for tables having geometry columns */ + if (papszTableNames == NULL) + { + CPLODBCStatement oStmt( &oSession ); + + oStmt.Append( "SELECT sys.schemas.name, sys.schemas.name + '.' + sys.objects.name, sys.columns.name from sys.columns join sys.types on sys.columns.system_type_id = sys.types.system_type_id and sys.columns.user_type_id = sys.types.user_type_id join sys.objects on sys.objects.object_id = sys.columns.object_id join sys.schemas on sys.objects.schema_id = sys.schemas.schema_id where (sys.types.name = 'geometry' or sys.types.name = 'geography') and (sys.objects.type = 'U' or sys.objects.type = 'V')"); + + if( oStmt.ExecuteSQL() ) + { + while( oStmt.Fetch() ) + { + papszSchemaNames = + CSLAddString( papszSchemaNames, oStmt.GetColData(0) ); + papszTableNames = + CSLAddString( papszTableNames, oStmt.GetColData(1) ); + papszGeomColumnNames = + CSLAddString( papszGeomColumnNames, oStmt.GetColData(2) ); + } + } + } + + int nSRId, nCoordDimension; + OGRwkbGeometryType eType; + + for( int iTable = 0; + papszTableNames != NULL && papszTableNames[iTable] != NULL; + iTable++ ) + { + if (papszSRIds != NULL) + nSRId = atoi(papszSRIds[iTable]); + else + nSRId = -1; + + if (papszCoordDimensions != NULL) + nCoordDimension = atoi(papszCoordDimensions[iTable]); + else + nCoordDimension = 2; + + if (papszTypes != NULL) + eType = OGRFromOGCGeomType(papszTypes[iTable]); + else + eType = wkbUnknown; + + if( strlen(papszGeomColumnNames[iTable]) > 0 ) + OpenTable( papszSchemaNames[iTable], papszTableNames[iTable], papszGeomColumnNames[iTable], + nCoordDimension, nSRId, papszSRTexts? papszSRTexts[iTable] : NULL, eType, bUpdate ); + else + OpenTable( papszSchemaNames[iTable], papszTableNames[iTable], NULL, + nCoordDimension, nSRId, papszSRTexts? papszSRTexts[iTable] : NULL, wkbNone, bUpdate ); + } + + CSLDestroy( papszTableNames ); + CSLDestroy( papszSchemaNames ); + CSLDestroy( papszGeomColumnNames ); + CSLDestroy( papszCoordDimensions ); + CSLDestroy( papszSRIds ); + CSLDestroy( papszSRTexts ); + CSLDestroy( papszTypes ); + + CPLFree(pszGeometryFormat); + CPLFree(pszConnectionName); + + bDSUpdate = bUpdate; + + return TRUE; +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRMSSQLSpatialDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Special case DELLAYER: command. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand,"DELLAYER:",9) ) + { + const char *pszLayerName = pszSQLCommand + 9; + + while( *pszLayerName == ' ' ) + pszLayerName++; + + OGRLayer* poLayer = GetLayerByName(pszLayerName); + + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( papoLayers[iLayer] == poLayer ) + { + DeleteLayer( iLayer ); + break; + } + } + return NULL; + } + + CPLDebug( "MSSQLSpatial", "ExecuteSQL(%s) called.", pszSQLCommand ); + + if( EQUALN(pszSQLCommand, "DROP SPATIAL INDEX ON ", 22) ) + { + /* Handle command to drop a spatial index. */ + OGRMSSQLSpatialTableLayer *poLayer = new OGRMSSQLSpatialTableLayer( this ); + + if (poLayer) + { + if( poLayer->Initialize( "dbo", pszSQLCommand + 22, NULL, 0, 0, NULL, wkbUnknown ) != CE_None ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to initialize layer '%s'", pszSQLCommand + 22 ); + } + poLayer->DropSpatialIndex(); + delete poLayer; + } + return NULL; + } + else if( EQUALN(pszSQLCommand, "CREATE SPATIAL INDEX ON ", 24) ) + { + /* Handle command to create a spatial index. */ + OGRMSSQLSpatialTableLayer *poLayer = new OGRMSSQLSpatialTableLayer( this ); + + if (poLayer) + { + if( poLayer->Initialize( "dbo", pszSQLCommand + 24, NULL, 0, 0, NULL, wkbUnknown ) != CE_None ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to initialize layer '%s'", pszSQLCommand + 24 ); + } + poLayer->CreateSpatialIndex(); + delete poLayer; + } + return NULL; + } + + /* Execute the command natively */ + CPLODBCStatement *poStmt = new CPLODBCStatement( &oSession ); + poStmt->Append( pszSQLCommand ); + + if( !poStmt->ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", oSession.GetLastError() ); + delete poStmt; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Are there result columns for this statement? */ +/* -------------------------------------------------------------------- */ + if( poStmt->GetColCount() == 0 ) + { + delete poStmt; + CPLErrorReset(); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a results layer. It will take ownership of the */ +/* statement. */ +/* -------------------------------------------------------------------- */ + + OGRMSSQLSpatialSelectLayer *poLayer = NULL; + + poLayer = new OGRMSSQLSpatialSelectLayer( this, poStmt ); + + if( poSpatialFilter != NULL ) + poLayer->SetSpatialFilter( poSpatialFilter ); + + return poLayer; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRMSSQLSpatialDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} + +/************************************************************************/ +/* LaunderName() */ +/************************************************************************/ + +char *OGRMSSQLSpatialDataSource::LaunderName( const char *pszSrcName ) + +{ + char *pszSafeName = CPLStrdup( pszSrcName ); + int i; + + for( i = 0; pszSafeName[i] != '\0'; i++ ) + { + pszSafeName[i] = (char) tolower( pszSafeName[i] ); + if( pszSafeName[i] == '-' || pszSafeName[i] == '#' ) + pszSafeName[i] = '_'; + } + + return pszSafeName; +} + +/************************************************************************/ +/* InitializeMetadataTables() */ +/* */ +/* Create the metadata tables (SPATIAL_REF_SYS and */ +/* GEOMETRY_COLUMNS). */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialDataSource::InitializeMetadataTables() + +{ + if (bUseGeometryColumns) + { + CPLODBCStatement oStmt( &oSession ); + + oStmt.Append( "IF NOT EXISTS (SELECT * FROM sys.objects WHERE " + "object_id = OBJECT_ID(N'[dbo].[geometry_columns]') AND type in (N'U')) " + "CREATE TABLE geometry_columns (f_table_catalog varchar(128) not null, " + "f_table_schema varchar(128) not null, f_table_name varchar(256) not null, " + "f_geometry_column varchar(256) not null, coord_dimension integer not null, " + "srid integer not null, geometry_type varchar(30) not null, " + "CONSTRAINT geometry_columns_pk PRIMARY KEY (f_table_catalog, " + "f_table_schema, f_table_name, f_geometry_column));\n" ); + + oStmt.Append( "IF NOT EXISTS (SELECT * FROM sys.objects " + "WHERE object_id = OBJECT_ID(N'[dbo].[spatial_ref_sys]') AND type in (N'U')) " + "CREATE TABLE spatial_ref_sys (srid integer not null " + "PRIMARY KEY, auth_name varchar(256), auth_srid integer, srtext varchar(2048), proj4text varchar(2048))" ); + + int bInTransaction = oSession.IsInTransaction(); + if (!bInTransaction) + oSession.BeginTransaction(); + + if( !oStmt.ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error initializing the metadata tables : %s", GetSession()->GetLastError() ); + + if (!bInTransaction) + oSession.RollbackTransaction(); + + return OGRERR_FAILURE; + } + + if (!bInTransaction) + oSession.CommitTransaction(); + } + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* FetchSRS() */ +/* */ +/* Return a SRS corresponding to a particular id. Note that */ +/* reference counting should be honoured on the returned */ +/* OGRSpatialReference, as handles may be cached. */ +/************************************************************************/ + +OGRSpatialReference *OGRMSSQLSpatialDataSource::FetchSRS( int nId ) + +{ + if( nId <= 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* First, we look through our SRID cache, is it there? */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nKnownSRID; i++ ) + { + if( panSRID[i] == nId ) + return papoSRS[i]; + } + + OGRSpatialReference *poSRS = NULL; + +/* -------------------------------------------------------------------- */ +/* Try looking up in spatial_ref_sys table */ +/* -------------------------------------------------------------------- */ + if (bUseGeometryColumns) + { + CPLODBCStatement oStmt( GetSession() ); + oStmt.Appendf( "SELECT srtext FROM spatial_ref_sys WHERE srid = %d", nId ); + + if( oStmt.ExecuteSQL() && oStmt.Fetch() ) + { + if ( oStmt.GetColData( 0 ) ) + { + poSRS = new OGRSpatialReference(); + char* pszWKT = (char*)oStmt.GetColData( 0 ); + if( poSRS->importFromWkt( &pszWKT ) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Try looking up the EPSG list */ +/* -------------------------------------------------------------------- */ + if (!poSRS) + { + poSRS = new OGRSpatialReference(); + if( poSRS->importFromEPSG( nId ) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Add to the cache. */ +/* -------------------------------------------------------------------- */ + if (poSRS) + { + panSRID = (int *) CPLRealloc(panSRID,sizeof(int) * (nKnownSRID+1) ); + papoSRS = (OGRSpatialReference **) + CPLRealloc(papoSRS, sizeof(void*) * (nKnownSRID + 1) ); + panSRID[nKnownSRID] = nId; + papoSRS[nKnownSRID] = poSRS; + nKnownSRID++; + } + + return poSRS; +} + +/************************************************************************/ +/* FetchSRSId() */ +/* */ +/* Fetch the id corresponding to an SRS, and if not found, add */ +/* it to the table. */ +/************************************************************************/ + +int OGRMSSQLSpatialDataSource::FetchSRSId( OGRSpatialReference * poSRS) + +{ + char *pszWKT = NULL; + int nSRSId = 0; + const char* pszAuthorityName; + + if( poSRS == NULL ) + return 0; + + OGRSpatialReference oSRS(*poSRS); + poSRS = NULL; + + pszAuthorityName = oSRS.GetAuthorityName(NULL); + + if( pszAuthorityName == NULL || strlen(pszAuthorityName) == 0 ) + { +/* -------------------------------------------------------------------- */ +/* Try to identify an EPSG code */ +/* -------------------------------------------------------------------- */ + oSRS.AutoIdentifyEPSG(); + + pszAuthorityName = oSRS.GetAuthorityName(NULL); + if (pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG")) + { + const char* pszAuthorityCode = oSRS.GetAuthorityCode(NULL); + if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 ) + { + /* Import 'clean' SRS */ + oSRS.importFromEPSG( atoi(pszAuthorityCode) ); + + pszAuthorityName = oSRS.GetAuthorityName(NULL); + } + } + } +/* -------------------------------------------------------------------- */ +/* Check whether the EPSG authority code is already mapped to a */ +/* SRS ID. */ +/* -------------------------------------------------------------------- */ + int nAuthorityCode = 0; + if( pszAuthorityName != NULL && EQUAL( pszAuthorityName, "EPSG" ) ) + { + /* For the root authority name 'EPSG', the authority code + * should always be integral + */ + nAuthorityCode = atoi( oSRS.GetAuthorityCode(NULL) ); + + CPLODBCStatement oStmt( &oSession ); + oStmt.Appendf("SELECT srid FROM spatial_ref_sys WHERE " + "auth_name = '%s' AND auth_srid = %d", + pszAuthorityName, + nAuthorityCode ); + + if( oStmt.ExecuteSQL() && oStmt.Fetch() && oStmt.GetColData( 0 ) ) + { + nSRSId = atoi(oStmt.GetColData( 0 )); + return nSRSId; + } + } + +/* -------------------------------------------------------------------- */ +/* Translate SRS to WKT. */ +/* -------------------------------------------------------------------- */ + if( oSRS.exportToWkt( &pszWKT ) != OGRERR_NONE ) + { + CPLFree(pszWKT); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* Try to find in the existing table. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oStmt( &oSession ); + + oStmt.Append( "SELECT srid FROM spatial_ref_sys WHERE srtext = "); + OGRMSSQLAppendEscaped(&oStmt, pszWKT); + +/* -------------------------------------------------------------------- */ +/* We got it! Return it. */ +/* -------------------------------------------------------------------- */ + if( oStmt.ExecuteSQL() ) + { + if ( oStmt.Fetch() && oStmt.GetColData( 0 ) ) + { + nSRSId = atoi(oStmt.GetColData( 0 )); + CPLFree(pszWKT); + return nSRSId; + } + } + else + { + /* probably the table is missing at all */ + if( InitializeMetadataTables() != OGRERR_NONE ) + { + CPLFree(pszWKT); + return 0; + } + } + +/* -------------------------------------------------------------------- */ +/* Try adding the SRS to the SRS table. */ +/* -------------------------------------------------------------------- */ + char *pszProj4 = NULL; + if( oSRS.exportToProj4( &pszProj4 ) != OGRERR_NONE ) + { + CPLFree( pszProj4 ); + CPLFree(pszWKT); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* Check whether the auth_code can be used as srid. */ +/* -------------------------------------------------------------------- */ + nSRSId = nAuthorityCode; + + oStmt.Clear(); + + int bInTransaction = oSession.IsInTransaction(); + if (!bInTransaction) + oSession.BeginTransaction(); + + if (nAuthorityCode > 0) + { + oStmt.Appendf("SELECT srid FROM spatial_ref_sys where srid = %d", nAuthorityCode); + if ( oStmt.ExecuteSQL() && oStmt.Fetch()) + { + nSRSId = 0; + } + } + +/* -------------------------------------------------------------------- */ +/* Get the current maximum srid in the srs table. */ +/* -------------------------------------------------------------------- */ + + if (nSRSId == 0) + { + oStmt.Clear(); + oStmt.Append("SELECT COALESCE(MAX(srid) + 1, 32768) FROM spatial_ref_sys where srid between 32768 and 65536"); + + if ( oStmt.ExecuteSQL() && oStmt.Fetch() && oStmt.GetColData( 0 ) ) + { + nSRSId = atoi(oStmt.GetColData( 0 )); + } + } + + if (nSRSId == 0) + { + /* unable to allocate srid */ + if (!bInTransaction) + oSession.RollbackTransaction(); + CPLFree( pszProj4 ); + CPLFree(pszWKT); + return 0; + } + + oStmt.Clear(); + if( nAuthorityCode > 0 ) + { + oStmt.Appendf( + "INSERT INTO spatial_ref_sys (srid, auth_srid, auth_name, srtext, proj4text) " + "VALUES (%d, %d, ", nSRSId, nAuthorityCode ); + OGRMSSQLAppendEscaped(&oStmt, pszAuthorityName); + oStmt.Append(", "); + OGRMSSQLAppendEscaped(&oStmt, pszWKT); + oStmt.Append(", "); + OGRMSSQLAppendEscaped(&oStmt, pszProj4); + oStmt.Append(")"); + } + else + { + oStmt.Appendf( + "INSERT INTO spatial_ref_sys (srid,srtext,proj4text) VALUES (%d, ", nSRSId); + OGRMSSQLAppendEscaped(&oStmt, pszWKT); + oStmt.Append(", "); + OGRMSSQLAppendEscaped(&oStmt, pszProj4); + oStmt.Append(")"); + } + + /* Free everything that was allocated. */ + CPLFree( pszProj4 ); + CPLFree( pszWKT); + + if ( oStmt.ExecuteSQL() ) + { + if (!bInTransaction) + oSession.CommitTransaction(); + } + else + { + if (!bInTransaction) + oSession.RollbackTransaction(); + } + + return nSRSId; +} + +/************************************************************************/ +/* StartTransaction() */ +/* */ +/* Should only be called by user code. Not driver internals. */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialDataSource::StartTransaction(CPL_UNUSED int bForce) +{ + if (!oSession.BeginTransaction()) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to start transaction: %s", oSession.GetLastError() ); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CommitTransaction() */ +/* */ +/* Should only be called by user code. Not driver internals. */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialDataSource::CommitTransaction() +{ + if (!oSession.CommitTransaction()) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to commit transaction: %s", oSession.GetLastError() ); + + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( papoLayers[iLayer]->GetLayerStatus() == MSSQLLAYERSTATUS_INITIAL ) + papoLayers[iLayer]->SetLayerStatus(MSSQLLAYERSTATUS_DISABLED); + } + return OGRERR_FAILURE; + } + + /* set the status for the newly created layers */ + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( papoLayers[iLayer]->GetLayerStatus() == MSSQLLAYERSTATUS_INITIAL ) + papoLayers[iLayer]->SetLayerStatus(MSSQLLAYERSTATUS_CREATED); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* RollbackTransaction() */ +/* */ +/* Should only be called by user code. Not driver internals. */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialDataSource::RollbackTransaction() +{ + /* set the status for the newly created layers */ + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( papoLayers[iLayer]->GetLayerStatus() == MSSQLLAYERSTATUS_INITIAL ) + papoLayers[iLayer]->SetLayerStatus(MSSQLLAYERSTATUS_DISABLED); + } + + if (!oSession.RollbackTransaction()) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to roll back transaction: %s", oSession.GetLastError() ); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatialdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatialdriver.cpp new file mode 100644 index 000000000..81a129d2c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatialdriver.cpp @@ -0,0 +1,159 @@ +/****************************************************************************** + * $Id: ogrmssqlspatialdriver.cpp 29025 2015-04-26 11:50:20Z tamas $ + * + * Project: MSSQL Spatial driver + * Purpose: Definition of classes for OGR MSSQL Spatial driver. + * Author: Tamas Szekeres, szekerest at gmail.com + * + ****************************************************************************** + * Copyright (c) 2010, Tamas Szekeres + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_mssqlspatial.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrmssqlspatialdriver.cpp 29025 2015-04-26 11:50:20Z tamas $"); + +/************************************************************************/ +/* ~OGRMSSQLSpatialDriver() */ +/************************************************************************/ + +OGRMSSQLSpatialDriver::~OGRMSSQLSpatialDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRMSSQLSpatialDriver::GetName() + +{ + return "MSSQLSpatial"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRMSSQLSpatialDriver::Open( const char * pszFilename, int bUpdate ) + +{ + OGRMSSQLSpatialDataSource *poDS; + + if( !EQUALN(pszFilename,"MSSQL:",6) ) + return NULL; + + poDS = new OGRMSSQLSpatialDataSource(); + + if( !poDS->Open( pszFilename, bUpdate, TRUE ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRMSSQLSpatialDriver::CreateDataSource( const char * pszName, + CPL_UNUSED char **papszOptions ) +{ + OGRMSSQLSpatialDataSource *poDS = new OGRMSSQLSpatialDataSource(); + + if( !EQUALN(pszName,"MSSQL:",6) ) + return NULL; + + if( !poDS->Open( pszName, TRUE, TRUE ) ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "MSSQL Spatial driver doesn't currently support database creation.\n" + "Please create database with the Microsoft SQL Server Client Tools." ); + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRMSSQLSpatialDriver::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODrCCreateDataSource) ) + return TRUE; + else + return FALSE; +} + + +/************************************************************************/ +/* RegisterOGRMSSQLSpatial() */ +/************************************************************************/ + +void RegisterOGRMSSQLSpatial() + +{ + if (! GDAL_CHECK_VERSION("OGR/MSSQLSpatial driver")) + return; + OGRSFDriver* poDriver = new OGRMSSQLSpatialDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Microsoft SQL Server Spatial Database" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_mssqlspatial.html" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, ""); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" " +" " +" "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date Time DateTime Binary" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_DEFAULT_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatiallayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatiallayer.cpp new file mode 100644 index 000000000..83e1b6692 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatiallayer.cpp @@ -0,0 +1,601 @@ +/****************************************************************************** + * $Id: ogrmssqlspatiallayer.cpp 29185 2015-05-12 10:45:44Z tamas $ + * + * Project: MSSQL Spatial driver + * Purpose: Definition of classes for OGR MSSQL Spatial driver. + * Author: Tamas Szekeres, szekerest at gmail.com + * + ****************************************************************************** + * Copyright (c) 2010, Tamas Szekeres + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_mssqlspatial.h" + +CPL_CVSID("$Id: ogrmssqlspatiallayer.cpp 29185 2015-05-12 10:45:44Z tamas $"); +/************************************************************************/ +/* OGRMSSQLSpatialLayer() */ +/************************************************************************/ + +OGRMSSQLSpatialLayer::OGRMSSQLSpatialLayer() + +{ + poDS = NULL; + + nGeomColumnType = -1; + pszGeomColumn = NULL; + pszFIDColumn = NULL; + bIsIdentityFid = FALSE; + panFieldOrdinals = NULL; + + poStmt = NULL; + + iNextShapeId = 0; + + poSRS = NULL; + nSRSId = -1; // we haven't even queried the database for it yet. + nLayerStatus = MSSQLLAYERSTATUS_ORIGINAL; +} + +/************************************************************************/ +/* ~OGRMSSQLSpatialLayer() */ +/************************************************************************/ + +OGRMSSQLSpatialLayer::~OGRMSSQLSpatialLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "OGR_MSSQLSpatial", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poStmt ) + { + delete poStmt; + poStmt = NULL; + } + + CPLFree( pszGeomColumn ); + CPLFree( pszFIDColumn ); + CPLFree( panFieldOrdinals ); + + if( poFeatureDefn ) + { + poFeatureDefn->Release(); + poFeatureDefn = NULL; + } + + if( poSRS ) + poSRS->Release(); +} + +/************************************************************************/ +/* BuildFeatureDefn() */ +/* */ +/* Build feature definition from a set of column definitions */ +/* set on a statement. Sift out geometry and FID fields. */ +/************************************************************************/ + +CPLErr OGRMSSQLSpatialLayer::BuildFeatureDefn( const char *pszLayerName, + CPLODBCStatement *poStmt ) + +{ + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + int nRawColumns = poStmt->GetColCount(); + + poFeatureDefn->Reference(); + + CPLFree(panFieldOrdinals); + panFieldOrdinals = (int *) CPLMalloc( sizeof(int) * nRawColumns ); + + for( int iCol = 0; iCol < nRawColumns; iCol++ ) + { + if ( pszGeomColumn == NULL ) + { + /* need to identify the geometry column */ + if ( EQUAL(poStmt->GetColTypeName( iCol ), "geometry") ) + { + nGeomColumnType = MSSQLCOLTYPE_GEOMETRY; + pszGeomColumn = CPLStrdup( poStmt->GetColName(iCol) ); + if (poFeatureDefn->GetGeomFieldCount() == 1) + poFeatureDefn->GetGeomFieldDefn(0)->SetNullable( poStmt->GetColNullable(iCol) ); + continue; + } + else if ( EQUAL(poStmt->GetColTypeName( iCol ), "geography") ) + { + nGeomColumnType = MSSQLCOLTYPE_GEOGRAPHY; + pszGeomColumn = CPLStrdup( poStmt->GetColName(iCol) ); + if (poFeatureDefn->GetGeomFieldCount() == 1) + poFeatureDefn->GetGeomFieldDefn(0)->SetNullable( poStmt->GetColNullable(iCol) ); + continue; + } + } + else + { + if( EQUAL(poStmt->GetColName(iCol),pszGeomColumn) ) + { + if (poFeatureDefn->GetGeomFieldCount() == 1) + poFeatureDefn->GetGeomFieldDefn(0)->SetNullable( poStmt->GetColNullable(iCol) ); + continue; + } + } + + if( pszFIDColumn != NULL) + { + if (EQUAL(poStmt->GetColName(iCol), pszFIDColumn) ) + { + if (EQUALN(poStmt->GetColTypeName( iCol ), "bigint", 6)) + SetMetadataItem(OLMD_FID64, "YES"); + + if ( EQUAL(poStmt->GetColTypeName( iCol ), "int identity") || + EQUAL(poStmt->GetColTypeName( iCol ), "bigint identity")) + bIsIdentityFid = TRUE; + /* skip FID */ + continue; + } + } + else + { + if (EQUAL(poStmt->GetColTypeName( iCol ), "int identity")) + { + pszFIDColumn = CPLStrdup( poStmt->GetColName(iCol) ); + bIsIdentityFid = TRUE; + continue; + } + else if (EQUAL(poStmt->GetColTypeName( iCol ), "bigint identity")) + { + pszFIDColumn = CPLStrdup( poStmt->GetColName(iCol) ); + bIsIdentityFid = TRUE; + SetMetadataItem(OLMD_FID64, "YES"); + continue; + } + } + + OGRFieldDefn oField( poStmt->GetColName(iCol), OFTString ); + oField.SetWidth( MAX(0,poStmt->GetColSize( iCol )) ); + + switch( CPLODBCStatement::GetTypeMapping(poStmt->GetColType(iCol)) ) + { + case SQL_C_SSHORT: + case SQL_C_USHORT: + case SQL_C_SLONG: + case SQL_C_ULONG: + oField.SetType( OFTInteger ); + break; + + case SQL_C_SBIGINT: + case SQL_C_UBIGINT: + oField.SetType( OFTInteger64 ); + break; + + case SQL_C_BINARY: + oField.SetType( OFTBinary ); + break; + + case SQL_C_NUMERIC: + oField.SetType( OFTReal ); + oField.SetPrecision( poStmt->GetColPrecision(iCol) ); + break; + + case SQL_C_FLOAT: + case SQL_C_DOUBLE: + oField.SetType( OFTReal ); + oField.SetWidth( 0 ); + break; + + case SQL_C_DATE: + oField.SetType( OFTDate ); + break; + + case SQL_C_TIME: + oField.SetType( OFTTime ); + break; + + case SQL_C_TIMESTAMP: + oField.SetType( OFTDateTime ); + break; + + default: + /* leave it as OFTString */; + } + + oField.SetNullable( poStmt->GetColNullable(iCol) ); + + if ( poStmt->GetColColumnDef(iCol) ) + { + /* process default value specification */ + if ( EQUAL(poStmt->GetColColumnDef(iCol), "(getdate())") ) + oField.SetDefault( "CURRENT_TIMESTAMP" ); + else if ( EQUALN(poStmt->GetColColumnDef(iCol), "(CONVERT([time],getdate(),0))", 25) ) + oField.SetDefault( "CURRENT_TIME" ); + else if ( EQUALN(poStmt->GetColColumnDef(iCol), "(CONVERT([date],getdate(),0))", 25) ) + oField.SetDefault( "CURRENT_DATE" ); + else + { + char* pszDefault = CPLStrdup(poStmt->GetColColumnDef(iCol)); + int nLen = strlen(pszDefault); + if (nLen >= 1 && pszDefault[0] == '(' && pszDefault[nLen-1] == ')') + { + /* all default values are encapsulated in backets by MSSQL server */ + if (nLen >= 4 && pszDefault[1] == '(' && pszDefault[nLen-2] == ')') + { + /* for numeric values double brackets are used */ + pszDefault[nLen-2] = '\0'; + oField.SetDefault(pszDefault + 2); + } + else + { + pszDefault[nLen-1] = '\0'; + oField.SetDefault(pszDefault + 1); + } + } + else + oField.SetDefault( pszDefault ); + + CPLFree(pszDefault); + } + } + + poFeatureDefn->AddFieldDefn( &oField ); + panFieldOrdinals[poFeatureDefn->GetFieldCount() - 1] = iCol; + } + +/* -------------------------------------------------------------------- */ +/* If we don't already have an FID, check if there is a special */ +/* FID named column available. */ +/* -------------------------------------------------------------------- */ + if( pszFIDColumn == NULL ) + { + const char *pszOGR_FID = CPLGetConfigOption("MSSQLSPATIAL_OGR_FID","OGR_FID"); + if( poFeatureDefn->GetFieldIndex( pszOGR_FID ) != -1 ) + pszFIDColumn = CPLStrdup(pszOGR_FID); + } + + if( pszFIDColumn != NULL ) + CPLDebug( "OGR_MSSQLSpatial", "Using column %s as FID for table %s.", + pszFIDColumn, poFeatureDefn->GetName() ); + else + CPLDebug( "OGR_MSSQLSpatial", "Table %s has no identified FID column.", + poFeatureDefn->GetName() ); + + return CE_None; +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRMSSQLSpatialLayer::ResetReading() + +{ + iNextShapeId = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRMSSQLSpatialLayer::GetNextFeature() + +{ + while( TRUE ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRMSSQLSpatialLayer::GetNextRawFeature() + +{ + if( GetStatement() == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we are marked to restart then do so, and fetch a record. */ +/* -------------------------------------------------------------------- */ + if( !poStmt->Fetch() ) + { + delete poStmt; + poStmt = NULL; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a feature from the current result. */ +/* -------------------------------------------------------------------- */ + int iField; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + if( pszFIDColumn != NULL && poStmt->GetColId(pszFIDColumn) > -1 ) + poFeature->SetFID( + CPLAtoGIntBig(poStmt->GetColData(poStmt->GetColId(pszFIDColumn))) ); + else + poFeature->SetFID( iNextShapeId ); + + iNextShapeId++; + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Set the fields. */ +/* -------------------------------------------------------------------- */ + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + if ( poFeatureDefn->GetFieldDefn(iField)->IsIgnored() ) + continue; + + int iSrcField = panFieldOrdinals[iField]; + const char *pszValue = poStmt->GetColData( iSrcField ); + + if( pszValue == NULL ) + /* no value */; + else if( poFeature->GetFieldDefnRef(iField)->GetType() == OFTBinary ) + poFeature->SetField( iField, + poStmt->GetColDataLength(iSrcField), + (GByte *) pszValue ); + else + poFeature->SetField( iField, pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Try to extract a geometry. */ +/* -------------------------------------------------------------------- */ + if( pszGeomColumn != NULL && !poFeatureDefn->IsGeometryIgnored()) + { + int iField = poStmt->GetColId( pszGeomColumn ); + const char *pszGeomText = poStmt->GetColData( iField ); + OGRGeometry *poGeom = NULL; + OGRErr eErr = OGRERR_NONE; + + if( pszGeomText != NULL ) + { + int nLength = poStmt->GetColDataLength( iField ); + + if ( nGeomColumnType == MSSQLCOLTYPE_GEOMETRY || + nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY || + nGeomColumnType == MSSQLCOLTYPE_BINARY) + { + switch ( poDS->GetGeometryFormat() ) + { + case MSSQLGEOMETRY_NATIVE: + { + OGRMSSQLGeometryParser oParser( nGeomColumnType ); + eErr = oParser.ParseSqlGeometry( + (unsigned char *) pszGeomText, nLength, &poGeom ); + nSRSId = oParser.GetSRSId(); + } + break; + case MSSQLGEOMETRY_WKB: + case MSSQLGEOMETRY_WKBZM: + eErr = OGRGeometryFactory::createFromWkb((unsigned char *) pszGeomText, + NULL, &poGeom, nLength); + break; + case MSSQLGEOMETRY_WKT: + eErr = OGRGeometryFactory::createFromWkt((char **) &pszGeomText, + NULL, &poGeom); + break; + } + } + else if (nGeomColumnType == MSSQLCOLTYPE_TEXT) + { + eErr = OGRGeometryFactory::createFromWkt((char **) &pszGeomText, + NULL, &poGeom); + } + } + + if ( eErr != OGRERR_NONE ) + { + const char *pszMessage; + + switch ( eErr ) + { + case OGRERR_NOT_ENOUGH_DATA: + pszMessage = "Not enough data to deserialize"; + break; + case OGRERR_UNSUPPORTED_GEOMETRY_TYPE: + pszMessage = "Unsupported geometry type"; + break; + case OGRERR_CORRUPT_DATA: + pszMessage = "Corrupt data"; + break; + default: + pszMessage = "Unrecognized error"; + } + CPLError(CE_Failure, CPLE_AppDefined, + "GetNextRawFeature(): %s", pszMessage); + } + + if( poGeom != NULL ) + { + if ( GetSpatialRef() ) + poGeom->assignSpatialReference( poSRS ); + + poFeature->SetGeometryDirectly( poGeom ); + } + } + + return poFeature; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRMSSQLSpatialLayer::GetFeature( GIntBig nFeatureId ) + +{ + /* This should be implemented directly! */ + + return OGRLayer::GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRMSSQLSpatialLayer::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialLayer::StartTransaction() + +{ + if (!poDS->GetSession()->BeginTransaction()) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to start transaction: %s", poDS->GetSession()->GetLastError() ); + return OGRERR_FAILURE; + } + return OGRERR_NONE; +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialLayer::CommitTransaction() + +{ + if (!poDS->GetSession()->CommitTransaction()) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to commit transaction: %s", poDS->GetSession()->GetLastError() ); + return OGRERR_FAILURE; + } + return OGRERR_NONE; +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialLayer::RollbackTransaction() + +{ + if (!poDS->GetSession()->RollbackTransaction()) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to roll back transaction: %s", poDS->GetSession()->GetLastError() ); + return OGRERR_FAILURE; + } + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRMSSQLSpatialLayer::GetSpatialRef() + +{ + if( poSRS == NULL && nSRSId > 0 ) + { + poSRS = poDS->FetchSRS( nSRSId ); + if( poSRS != NULL ) + poSRS->Reference(); + else + nSRSId = 0; + } + + return poSRS; +} + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char *OGRMSSQLSpatialLayer::GetFIDColumn() + +{ + GetLayerDefn(); + + if( pszFIDColumn != NULL ) + return pszFIDColumn; + else + return ""; +} + +/************************************************************************/ +/* GetGeometryColumn() */ +/************************************************************************/ + +const char *OGRMSSQLSpatialLayer::GetGeometryColumn() + +{ + GetLayerDefn(); + + if( pszGeomColumn != NULL ) + return pszGeomColumn; + else + return ""; +} + +/************************************************************************/ +/* GByteArrayToHexString() */ +/************************************************************************/ + +char* OGRMSSQLSpatialLayer::GByteArrayToHexString( const GByte* pabyData, int nLen) +{ + char* pszTextBuf; + + pszTextBuf = (char *) CPLMalloc(nLen*2+3); + + int iSrc, iDst=0; + + for( iSrc = 0; iSrc < nLen; iSrc++ ) + { + if( iSrc == 0 ) + { + sprintf( pszTextBuf+iDst, "0x%02x", pabyData[iSrc] ); + iDst += 4; + } + else + { + sprintf( pszTextBuf+iDst, "%02x", pabyData[iSrc] ); + iDst += 2; + } + } + pszTextBuf[iDst] = 0; + + return pszTextBuf; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatialselectlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatialselectlayer.cpp new file mode 100644 index 000000000..a00c5bce4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatialselectlayer.cpp @@ -0,0 +1,231 @@ +/****************************************************************************** + * $Id: ogrmssqlspatialselectlayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: MSSQL Spatial driver + * Purpose: Implements OGRMSSQLSpatialSelectLayer class, layer access to the results + * of a SELECT statement executed via ExecuteSQL(). + * Author: Tamas Szekeres, szekerest at gmail.com + * + ****************************************************************************** + * Copyright (c) 2010, Tamas Szekeres + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_mssqlspatial.h" + +CPL_CVSID("$Id: ogrmssqlspatialselectlayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); +/************************************************************************/ +/* OGRMSSQLSpatialSelectLayer() */ +/************************************************************************/ + +OGRMSSQLSpatialSelectLayer::OGRMSSQLSpatialSelectLayer( OGRMSSQLSpatialDataSource *poDSIn, + CPLODBCStatement * poStmtIn ) + +{ + poDS = poDSIn; + + iNextShapeId = 0; + nSRSId = -1; + poFeatureDefn = NULL; + + poStmt = poStmtIn; + pszBaseStatement = CPLStrdup( poStmtIn->GetCommand() ); + + /* identify the geometry column */ + pszGeomColumn = NULL; + int iImageCol = -1; + for ( int iColumn = 0; iColumn < poStmt->GetColCount(); iColumn++ ) + { + if ( EQUAL(poStmt->GetColTypeName( iColumn ), "image") ) + { + SQLCHAR szTableName[256]; + SQLSMALLINT nTableNameLength = 0; + + SQLColAttribute(poStmt->GetStatement(), (SQLSMALLINT)(iColumn + 1), SQL_DESC_TABLE_NAME, + szTableName, sizeof(szTableName), + &nTableNameLength, NULL); + + if (nTableNameLength > 0) + { + OGRLayer *poBaseLayer = poDS->GetLayerByName((const char*)szTableName); + if (poBaseLayer != NULL && EQUAL(poBaseLayer->GetGeometryColumn(), poStmt->GetColName(iColumn))) + { + nGeomColumnType = MSSQLCOLTYPE_BINARY; + pszGeomColumn = CPLStrdup(poStmt->GetColName(iColumn)); + /* copy spatial reference */ + if (!poSRS && poBaseLayer->GetSpatialRef()) + poSRS = poBaseLayer->GetSpatialRef()->Clone(); + break; + } + } + else if (iImageCol == -1) + iImageCol = iColumn; + } + else if ( EQUAL(poStmt->GetColTypeName( iColumn ), "geometry") ) + { + nGeomColumnType = MSSQLCOLTYPE_GEOMETRY; + pszGeomColumn = CPLStrdup(poStmt->GetColName(iColumn)); + break; + } + else if ( EQUAL(poStmt->GetColTypeName( iColumn ), "geography") ) + { + nGeomColumnType = MSSQLCOLTYPE_GEOGRAPHY; + pszGeomColumn = CPLStrdup(poStmt->GetColName(iColumn)); + break; + } + } + + if (pszGeomColumn == NULL && iImageCol >= 0) + { + /* set the image col as geometry column as the last resort */ + nGeomColumnType = MSSQLCOLTYPE_BINARY; + pszGeomColumn = CPLStrdup(poStmt->GetColName(iImageCol)); + } + + BuildFeatureDefn( "SELECT", poStmt ); + + if ( GetSpatialRef() && poFeatureDefn->GetGeomFieldCount() == 1) + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef( poSRS ); +} + +/************************************************************************/ +/* ~OGRMSSQLSpatialSelectLayer() */ +/************************************************************************/ + +OGRMSSQLSpatialSelectLayer::~OGRMSSQLSpatialSelectLayer() + +{ + ClearStatement(); + CPLFree(pszBaseStatement); +} + +/************************************************************************/ +/* ClearStatement() */ +/************************************************************************/ + +void OGRMSSQLSpatialSelectLayer::ClearStatement() + +{ + if( poStmt != NULL ) + { + delete poStmt; + poStmt = NULL; + } +} + +/************************************************************************/ +/* GetStatement() */ +/************************************************************************/ + +CPLODBCStatement *OGRMSSQLSpatialSelectLayer::GetStatement() + +{ + if( poStmt == NULL ) + ResetStatement(); + + return poStmt; +} + +/************************************************************************/ +/* ResetStatement() */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialSelectLayer::ResetStatement() + +{ + ClearStatement(); + + iNextShapeId = 0; + + CPLDebug( "OGR_MSSQLSpatial", "Recreating statement." ); + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( pszBaseStatement ); + + if( poStmt->ExecuteSQL() ) + return OGRERR_NONE; + else + { + delete poStmt; + poStmt = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRMSSQLSpatialSelectLayer::ResetReading() + +{ + if( iNextShapeId != 0 ) + ClearStatement(); + + OGRMSSQLSpatialLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRMSSQLSpatialSelectLayer::GetFeature( GIntBig nFeatureId ) + +{ + return OGRMSSQLSpatialLayer::GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRMSSQLSpatialSelectLayer::TestCapability( const char * pszCap ) + +{ + return OGRMSSQLSpatialLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetExtent() */ +/* */ +/* Since SELECT layers currently cannot ever have geometry, we */ +/* can optimize the GetExtent() method! */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialSelectLayer::GetExtent(OGREnvelope *, int ) + +{ + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRMSSQLSpatialSelectLayer::GetFeatureCount( int bForce ) + +{ + return OGRMSSQLSpatialLayer::GetFeatureCount( bForce ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatialtablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatialtablelayer.cpp new file mode 100644 index 000000000..1eedd1403 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mssqlspatial/ogrmssqlspatialtablelayer.cpp @@ -0,0 +1,1463 @@ +/****************************************************************************** + * $Id: ogrmssqlspatialtablelayer.cpp 29185 2015-05-12 10:45:44Z tamas $ + * + * Project: MSSQL Spatial driver + * Purpose: Implements OGRMSSQLSpatialTableLayer class, access to an existing table. + * Author: Tamas Szekeres, szekerest at gmail.com + * + ****************************************************************************** + * Copyright (c) 2010, Tamas Szekeres + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_mssqlspatial.h" + +CPL_CVSID("$Id: ogrmssqlspatialtablelayer.cpp 29185 2015-05-12 10:45:44Z tamas $"); + +/************************************************************************/ +/* OGRMSSQLAppendEscaped( ) */ +/************************************************************************/ + +void OGRMSSQLAppendEscaped( CPLODBCStatement* poStatement, const char* pszStrValue) +{ + if (!pszStrValue) + poStatement->Append("null"); + + size_t iIn, iOut , nTextLen = strlen(pszStrValue); + char *pszEscapedText = (char *) VSIMalloc(nTextLen*2 + 3); + + pszEscapedText[0] = '\''; + + for( iIn = 0, iOut = 1; iIn < nTextLen; iIn++ ) + { + switch( pszStrValue[iIn] ) + { + case '\'': + pszEscapedText[iOut++] = '\''; // double quote + pszEscapedText[iOut++] = pszStrValue[iIn]; + break; + + default: + pszEscapedText[iOut++] = pszStrValue[iIn]; + break; + } + } + + pszEscapedText[iOut++] = '\''; + + pszEscapedText[iOut] = '\0'; + + poStatement->Append(pszEscapedText); + + CPLFree( pszEscapedText ); +} + +/************************************************************************/ +/* OGRMSSQLSpatialTableLayer() */ +/************************************************************************/ + +OGRMSSQLSpatialTableLayer::OGRMSSQLSpatialTableLayer( OGRMSSQLSpatialDataSource *poDSIn ) + +{ + poDS = poDSIn; + + pszQuery = NULL; + + bUpdateAccess = TRUE; + + iNextShapeId = 0; + + nSRSId = -1; + + poFeatureDefn = NULL; + + pszTableName = NULL; + pszLayerName = NULL; + pszSchemaName = NULL; + + eGeomType = wkbNone; + + bNeedSpatialIndex = FALSE; + nUploadGeometryFormat = MSSQLGEOMETRY_WKB; +} + +/************************************************************************/ +/* ~OGRMSSQLSpatialTableLayer() */ +/************************************************************************/ + +OGRMSSQLSpatialTableLayer::~OGRMSSQLSpatialTableLayer() + +{ + if ( bNeedSpatialIndex && nLayerStatus == MSSQLLAYERSTATUS_CREATED ) + { + /* recreate spatial index */ + DropSpatialIndex(); + CreateSpatialIndex(); + } + + CPLFree( pszTableName ); + CPLFree( pszLayerName ); + CPLFree( pszSchemaName ); + + CPLFree( pszQuery ); + ClearStatement(); +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRMSSQLSpatialTableLayer::GetName() + +{ + return pszLayerName; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ +OGRFeatureDefn* OGRMSSQLSpatialTableLayer::GetLayerDefn() +{ + if (poFeatureDefn) + return poFeatureDefn; + + CPLODBCSession *poSession = poDS->GetSession(); +/* -------------------------------------------------------------------- */ +/* Do we have a simple primary key? */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oGetKey( poSession ); + + if( oGetKey.GetPrimaryKeys( pszTableName, poDS->GetCatalog(), pszSchemaName ) + && oGetKey.Fetch() ) + { + pszFIDColumn = CPLStrdup(oGetKey.GetColData( 3 )); + + if( oGetKey.Fetch() ) // more than one field in key! + { + oGetKey.Clear(); + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + + CPLDebug( "OGR_MSSQLSpatial", "Table %s has multiple primary key fields, " + "ignoring them all.", pszTableName ); + } + } + + +/* -------------------------------------------------------------------- */ +/* Get the column definitions for this table. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oGetCol( poSession ); + CPLErr eErr; + + if( !oGetCol.GetColumns( pszTableName, poDS->GetCatalog(), pszSchemaName ) ) + return NULL; + + eErr = BuildFeatureDefn( pszLayerName, &oGetCol ); + if( eErr != CE_None ) + return NULL; + + if (eGeomType != wkbNone) + poFeatureDefn->SetGeomType(eGeomType); + + if ( GetSpatialRef() && poFeatureDefn->GetGeomFieldCount() == 1) + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef( poSRS ); + + if( poFeatureDefn->GetFieldCount() == 0 && + pszFIDColumn == NULL && pszGeomColumn == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No column definitions found for table '%s', layer not usable.", + pszLayerName ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* If we got a geometry column, does it exist? Is it binary? */ +/* -------------------------------------------------------------------- */ + if( pszGeomColumn != NULL ) + { + int iColumn = oGetCol.GetColId( pszGeomColumn ); + if( iColumn < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Column %s requested for geometry, but it does not exist.", + pszGeomColumn ); + CPLFree( pszGeomColumn ); + pszGeomColumn = NULL; + } + else + { + if ( nGeomColumnType < 0 ) + { + /* last attempt to identify the geometry column type */ + if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "geometry") ) + nGeomColumnType = MSSQLCOLTYPE_GEOMETRY; + else if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "geography") ) + nGeomColumnType = MSSQLCOLTYPE_GEOGRAPHY; + else if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "varchar") ) + nGeomColumnType = MSSQLCOLTYPE_TEXT; + else if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "nvarchar") ) + nGeomColumnType = MSSQLCOLTYPE_TEXT; + else if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "text") ) + nGeomColumnType = MSSQLCOLTYPE_TEXT; + else if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "ntext") ) + nGeomColumnType = MSSQLCOLTYPE_TEXT; + else if ( EQUAL(oGetCol.GetColTypeName( iColumn ), "image") ) + nGeomColumnType = MSSQLCOLTYPE_BINARY; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Column type %s is not supported for geometry column.", + oGetCol.GetColTypeName( iColumn ) ); + CPLFree( pszGeomColumn ); + pszGeomColumn = NULL; + } + } + } + } + + return poFeatureDefn; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +CPLErr OGRMSSQLSpatialTableLayer::Initialize( const char *pszSchema, + const char *pszLayerName, + const char *pszGeomCol, + CPL_UNUSED int nCoordDimension, + int nSRId, + const char *pszSRText, + OGRwkbGeometryType eType ) +{ + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + +/* -------------------------------------------------------------------- */ +/* Parse out schema name if present in layer. We assume a */ +/* schema is provided if there is a dot in the name, and that */ +/* it is in the form . */ +/* -------------------------------------------------------------------- */ + const char *pszDot = strstr(pszLayerName,"."); + if( pszDot != NULL ) + { + pszTableName = CPLStrdup(pszDot + 1); + pszSchemaName = CPLStrdup(pszLayerName); + pszSchemaName[pszDot - pszLayerName] = '\0'; + this->pszLayerName = CPLStrdup(pszLayerName); + } + else + { + pszTableName = CPLStrdup(pszLayerName); + pszSchemaName = CPLStrdup(pszSchema); + if ( EQUAL(pszSchemaName, "dbo") ) + this->pszLayerName = CPLStrdup(pszLayerName); + else + this->pszLayerName = CPLStrdup(CPLSPrintf("%s.%s", pszSchemaName, pszTableName)); + } + SetDescription( this->pszLayerName ); + +/* -------------------------------------------------------------------- */ +/* Have we been provided a geometry column? */ +/* -------------------------------------------------------------------- */ + CPLFree( pszGeomColumn ); + if( pszGeomCol == NULL ) + GetLayerDefn(); /* fetch geom colum if not specified */ + else + pszGeomColumn = CPLStrdup( pszGeomCol ); + + if (eType != wkbNone) + eGeomType = eType; + + +/* -------------------------------------------------------------------- */ +/* Try to find out the spatial reference */ +/* -------------------------------------------------------------------- */ + + nSRSId = nSRId; + + if (pszSRText) + { + /* Process srtext directly if specified */ + poSRS = new OGRSpatialReference(); + if( poSRS->importFromWkt( (char**)&pszSRText ) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + + if (!poSRS) + { + if (nSRSId < 0) + nSRSId = FetchSRSId(); + + GetSpatialRef(); + } + + return CE_None; +} + +/************************************************************************/ +/* FetchSRSId() */ +/************************************************************************/ + +int OGRMSSQLSpatialTableLayer::FetchSRSId() +{ + if ( poDS->UseGeometryColumns() ) + { + CPLODBCStatement oStatement = CPLODBCStatement( poDS->GetSession() ); + oStatement.Appendf( "select srid from geometry_columns " + "where f_table_schema = '%s' and f_table_name = '%s'", + pszSchemaName, pszTableName ); + + if( oStatement.ExecuteSQL() && oStatement.Fetch() ) + { + if ( oStatement.GetColData( 0 ) ) + nSRSId = atoi( oStatement.GetColData( 0 ) ); + } + } + + return nSRSId; +} + +/************************************************************************/ +/* CreateSpatialIndex() */ +/* */ +/* Create a spatial index on the geometry column of the layer */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialTableLayer::CreateSpatialIndex() +{ + GetLayerDefn(); + + CPLODBCStatement oStatement( poDS->GetSession() ); + + if (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY) + { + OGREnvelope oExt; + if (GetExtent(&oExt, TRUE) != OGRERR_NONE) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to get extent for spatial index." ); + return OGRERR_FAILURE; + } + + if (oExt.MinX == oExt.MaxX || oExt.MinY == oExt.MaxY) + return OGRERR_NONE; /* skip creating index */ + + oStatement.Appendf("CREATE SPATIAL INDEX [ogr_%s_%s_%s_sidx] ON [%s].[%s] ( [%s] ) " + "USING GEOMETRY_GRID WITH (BOUNDING_BOX =(%.15g, %.15g, %.15g, %.15g))", + pszSchemaName, pszTableName, pszGeomColumn, + pszSchemaName, pszTableName, pszGeomColumn, + oExt.MinX, oExt.MinY, oExt.MaxX, oExt.MaxY ); + } + else if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY) + { + oStatement.Appendf("CREATE SPATIAL INDEX [ogr_%s_%s_%s_sidx] ON [%s].[%s] ( [%s] ) " + "USING GEOGRAPHY_GRID", + pszSchemaName, pszTableName, pszGeomColumn, + pszSchemaName, pszTableName, pszGeomColumn ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Spatial index is not supported on the geometry column '%s'", pszGeomColumn); + return OGRERR_FAILURE; + } + + if( !oStatement.ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to create the spatial index, %s.", + poDS->GetSession()->GetLastError()); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DropSpatialIndex() */ +/* */ +/* Drop the spatial index on the geometry column of the layer */ +/************************************************************************/ + +void OGRMSSQLSpatialTableLayer::DropSpatialIndex() +{ + GetLayerDefn(); + + CPLODBCStatement oStatement( poDS->GetSession() ); + + oStatement.Appendf("IF EXISTS (SELECT * FROM sys.indexes " + "WHERE object_id = OBJECT_ID(N'[%s].[%s]') AND name = N'ogr_%s_%s_%s_sidx') " + "DROP INDEX [ogr_%s_%s_%s_sidx] ON [%s].[%s]", + pszSchemaName, pszTableName, + pszSchemaName, pszTableName, pszGeomColumn, + pszSchemaName, pszTableName, pszGeomColumn, + pszSchemaName, pszTableName ); + + if( !oStatement.ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to drop the spatial index, %s.", + poDS->GetSession()->GetLastError()); + return; + } +} + +/************************************************************************/ +/* BuildFields() */ +/* */ +/* Build list of fields to fetch, performing any required */ +/* transformations (such as on geometry). */ +/************************************************************************/ + +CPLString OGRMSSQLSpatialTableLayer::BuildFields() + +{ + int i = 0; + int nColumn = 0; + CPLString osFieldList; + + GetLayerDefn(); + + if( pszFIDColumn && poFeatureDefn->GetFieldIndex( pszFIDColumn ) == -1 ) + { + /* Always get the FID column */ + osFieldList += "["; + osFieldList += pszFIDColumn; + osFieldList += "]"; + ++nColumn; + } + + if( pszGeomColumn && !poFeatureDefn->IsGeometryIgnored()) + { + if( nColumn > 0 ) + osFieldList += ", "; + + osFieldList += "["; + osFieldList += pszGeomColumn; + if (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY || + nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY) + { + if ( poDS->GetGeometryFormat() == MSSQLGEOMETRY_WKB ) + { + osFieldList += "].STAsBinary() as ["; + osFieldList += pszGeomColumn; + } + else if ( poDS->GetGeometryFormat() == MSSQLGEOMETRY_WKT ) + { + osFieldList += "].AsTextZM() as ["; + osFieldList += pszGeomColumn; + } + else if ( poDS->GetGeometryFormat() == MSSQLGEOMETRY_WKBZM ) + { + /* SQL Server 2012 */ + osFieldList += "].AsBinaryZM() as ["; + osFieldList += pszGeomColumn; + } + } + osFieldList += "]"; + + ++nColumn; + } + + if (poFeatureDefn->GetFieldCount() > 0) + { + /* need to reconstruct the field ordinals list */ + CPLFree(panFieldOrdinals); + panFieldOrdinals = (int *) CPLMalloc( sizeof(int) * poFeatureDefn->GetFieldCount() ); + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if ( poFeatureDefn->GetFieldDefn(i)->IsIgnored() ) + continue; + + const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + + if( nColumn > 0 ) + osFieldList += ", "; + + osFieldList += "["; + osFieldList += pszName; + osFieldList += "]"; + + panFieldOrdinals[i] = nColumn; + + ++nColumn; + } + } + + return osFieldList; +} + +/************************************************************************/ +/* ClearStatement() */ +/************************************************************************/ + +void OGRMSSQLSpatialTableLayer::ClearStatement() + +{ + if( poStmt != NULL ) + { + delete poStmt; + poStmt = NULL; + } +} + +/************************************************************************/ +/* GetStatement() */ +/************************************************************************/ + +CPLODBCStatement *OGRMSSQLSpatialTableLayer::GetStatement() + +{ + if( poStmt == NULL ) + { + poStmt = BuildStatement(BuildFields()); + iNextShapeId = 0; + } + + return poStmt; +} + + +/************************************************************************/ +/* BuildStatement() */ +/************************************************************************/ + +CPLODBCStatement* OGRMSSQLSpatialTableLayer::BuildStatement(const char* pszColumns) + +{ + CPLODBCStatement* poStatement = new CPLODBCStatement( poDS->GetSession() ); + poStatement->Append( "select " ); + poStatement->Append( pszColumns ); + poStatement->Append( " from " ); + poStatement->Append( pszSchemaName ); + poStatement->Append( "." ); + poStatement->Append( pszTableName ); + + /* Append attribute query if we have it */ + if( pszQuery != NULL ) + poStatement->Appendf( " where (%s)", pszQuery ); + + /* If we have a spatial filter, query on it */ + if ( m_poFilterGeom != NULL ) + { + if (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY + || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY) + { + if( pszQuery == NULL ) + poStatement->Append( " where" ); + else + poStatement->Append( " and" ); + + poStatement->Appendf(" [%s].STIntersects(", pszGeomColumn ); + + if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY) + poStatement->Append( "geography::" ); + else + poStatement->Append( "geometry::" ); + + if ( m_sFilterEnvelope.MinX == m_sFilterEnvelope.MaxX || + m_sFilterEnvelope.MinY == m_sFilterEnvelope.MaxY) + poStatement->Appendf("STGeomFromText('POINT(%.15g %.15g)',%d)) = 1", + m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY, nSRSId >= 0? nSRSId : 0); + else + poStatement->Appendf( "STGeomFromText('POLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))',%d)) = 1", + m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY, + m_sFilterEnvelope.MaxX, m_sFilterEnvelope.MinY, + m_sFilterEnvelope.MaxX, m_sFilterEnvelope.MaxY, + m_sFilterEnvelope.MinX, m_sFilterEnvelope.MaxY, + m_sFilterEnvelope.MinX, m_sFilterEnvelope.MinY, + nSRSId >= 0? nSRSId : 0 ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Spatial filter is supported only on geometry and geography column types." ); + + delete poStatement; + return NULL; + } + } + + CPLDebug( "OGR_MSSQLSpatial", "ExecuteSQL(%s)", poStatement->GetCommand() ); + if( poStatement->ExecuteSQL() ) + return poStatement; + else + { + delete poStatement; + return NULL; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRMSSQLSpatialTableLayer::ResetReading() + +{ + ClearStatement(); + OGRMSSQLSpatialLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRMSSQLSpatialTableLayer::GetFeature( GIntBig nFeatureId ) + +{ + if( pszFIDColumn == NULL ) + return OGRMSSQLSpatialLayer::GetFeature( nFeatureId ); + + ClearStatement(); + + iNextShapeId = nFeatureId; + + poStmt = new CPLODBCStatement( poDS->GetSession() ); + CPLString osFields = BuildFields(); + poStmt->Appendf( "select %s from %s where %s = " CPL_FRMT_GIB, osFields.c_str(), + poFeatureDefn->GetName(), pszFIDColumn, nFeatureId ); + + if( !poStmt->ExecuteSQL() ) + { + delete poStmt; + poStmt = NULL; + return NULL; + } + + return GetNextRawFeature(); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialTableLayer::SetAttributeFilter( const char *pszQuery ) + +{ + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + + if( (pszQuery == NULL && this->pszQuery == NULL) + || (pszQuery != NULL && this->pszQuery != NULL + && EQUAL(pszQuery,this->pszQuery)) ) + return OGRERR_NONE; + + CPLFree( this->pszQuery ); + this->pszQuery = (pszQuery) ? CPLStrdup( pszQuery ) : NULL; + + ClearStatement(); + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRMSSQLSpatialTableLayer::TestCapability( const char * pszCap ) + +{ + if ( bUpdateAccess ) + { + if( EQUAL(pszCap,OLCSequentialWrite) || EQUAL(pszCap,OLCCreateField) + || EQUAL(pszCap,OLCDeleteFeature) ) + return TRUE; + + else if( EQUAL(pszCap,OLCRandomWrite) ) + return (pszFIDColumn != NULL); + } + +#if (ODBCVER >= 0x0300) + if( EQUAL(pszCap,OLCTransactions) ) + return TRUE; +#else + if( EQUAL(pszCap,OLCTransactions) ) + return FALSE; +#endif + + if( EQUAL(pszCap,OLCIgnoreFields) ) + return TRUE; + + if( EQUAL(pszCap,OLCRandomRead) ) + return (pszFIDColumn != NULL); + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return TRUE; + else + return OGRMSSQLSpatialLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRMSSQLSpatialTableLayer::GetFeatureCount( int bForce ) + +{ + GetLayerDefn(); + + if( TestCapability(OLCFastFeatureCount) == FALSE ) + return OGRMSSQLSpatialLayer::GetFeatureCount( bForce ); + + ClearStatement(); + + CPLODBCStatement* poStatement = BuildStatement( "count(*)" ); + + if (poStatement == NULL || !poStatement->Fetch()) + { + delete poStatement; + return OGRMSSQLSpatialLayer::GetFeatureCount( bForce ); + } + + GIntBig nRet = CPLAtoGIntBig(poStatement->GetColData( 0 )); + delete poStatement; + return nRet; +} + + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialTableLayer::CreateField( OGRFieldDefn *poFieldIn, + int bApproxOK ) + +{ + char szFieldType[256]; + OGRFieldDefn oField( poFieldIn ); + + GetLayerDefn(); + +/* -------------------------------------------------------------------- */ +/* Do we want to "launder" the column names into MSSQL */ +/* friendly format? */ +/* -------------------------------------------------------------------- */ + if( bLaunderColumnNames ) + { + char *pszSafeName = poDS->LaunderName( oField.GetNameRef() ); + + oField.SetName( pszSafeName ); + CPLFree( pszSafeName ); + } + +/* -------------------------------------------------------------------- */ +/* Identify the MSSQL type. */ +/* -------------------------------------------------------------------- */ + + if( oField.GetType() == OFTInteger ) + { + if( oField.GetWidth() > 0 && bPreservePrecision ) + sprintf( szFieldType, "numeric(%d,0)", oField.GetWidth() ); + else + strcpy( szFieldType, "int" ); + } + else if( oField.GetType() == OFTInteger64 ) + { + if( oField.GetWidth() > 0 && bPreservePrecision ) + sprintf( szFieldType, "numeric(%d,0)", oField.GetWidth() ); + else + strcpy( szFieldType, "bigint" ); + } + else if( oField.GetType() == OFTReal ) + { + if( oField.GetWidth() > 0 && oField.GetPrecision() > 0 + && bPreservePrecision ) + sprintf( szFieldType, "numeric(%d,%d)", + oField.GetWidth(), oField.GetPrecision() ); + else + strcpy( szFieldType, "float" ); + } + else if( oField.GetType() == OFTString ) + { + if( oField.GetWidth() == 0 || !bPreservePrecision ) + strcpy( szFieldType, "nvarchar(MAX)" ); + else + sprintf( szFieldType, "nvarchar(%d)", oField.GetWidth() ); + } + else if( oField.GetType() == OFTDate ) + { + strcpy( szFieldType, "date" ); + } + else if( oField.GetType() == OFTTime ) + { + strcpy( szFieldType, "time(7)" ); + } + else if( oField.GetType() == OFTDateTime ) + { + strcpy( szFieldType, "datetime" ); + } + else if( oField.GetType() == OFTBinary ) + { + strcpy( szFieldType, "image" ); + } + else if( bApproxOK ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Can't create field %s with type %s on MSSQL layers. Creating as varchar.", + oField.GetNameRef(), + OGRFieldDefn::GetFieldTypeName(oField.GetType()) ); + strcpy( szFieldType, "varchar" ); + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can't create field %s with type %s on MSSQL layers.", + oField.GetNameRef(), + OGRFieldDefn::GetFieldTypeName(oField.GetType()) ); + + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Create the new field. */ +/* -------------------------------------------------------------------- */ + + CPLODBCStatement oStmt( poDS->GetSession() ); + + oStmt.Appendf( "ALTER TABLE [%s].[%s] ADD [%s] %s", + pszSchemaName, pszTableName, oField.GetNameRef(), szFieldType); + + if ( !oField.IsNullable() ) + { + oStmt.Append(" NOT NULL"); + } + if ( oField.GetDefault() != NULL && !oField.IsDefaultDriverSpecific() ) + { + /* process default value specifications */ + if ( EQUAL(oField.GetDefault(), "CURRENT_TIME") ) + oStmt.Append(" DEFAULT(CONVERT([time],getdate()))"); + else if ( EQUAL(oField.GetDefault(), "CURRENT_DATE") ) + oStmt.Append( " DEFAULT(CONVERT([date],getdate()))" ); + else + oStmt.Appendf(" DEFAULT(%s)", oField.GetDefault()); + } + + if( !oStmt.ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error creating field %s, %s", oField.GetNameRef(), + poDS->GetSession()->GetLastError() ); + + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Add the field to the OGRFeatureDefn. */ +/* -------------------------------------------------------------------- */ + + poFeatureDefn->AddFieldDefn( &oField ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ISetFeature() */ +/* */ +/* SetFeature() is implemented by an UPDATE SQL command */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialTableLayer::ISetFeature( OGRFeature *poFeature ) + +{ + OGRErr eErr = OGRERR_FAILURE; + + GetLayerDefn(); + + if( NULL == poFeature ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "NULL pointer to OGRFeature passed to SetFeature()." ); + return eErr; + } + + if( poFeature->GetFID() == OGRNullFID ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "FID required on features given to SetFeature()." ); + return eErr; + } + + if( !pszFIDColumn ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to update features in tables without\n" + "a recognised FID column."); + return eErr; + + } + + ClearStatement(); + +/* -------------------------------------------------------------------- */ +/* Form the UPDATE command. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oStmt( poDS->GetSession() ); + + oStmt.Appendf( "UPDATE [%s].[%s] SET ", pszSchemaName, pszTableName); + + OGRMSSQLGeometryValidator oValidator(poFeature->GetGeometryRef()); + OGRGeometry *poGeom = oValidator.GetValidGeometryRef(); + + if (poFeature->GetGeometryRef() != poGeom) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Geometry with FID = " CPL_FRMT_GIB " has been modified.", poFeature->GetFID() ); + } + + int nFieldCount = poFeatureDefn->GetFieldCount(); + int bind_num = 0; + void** bind_buffer = (void**)CPLMalloc(sizeof(void*) * nFieldCount); + + + int bNeedComma = FALSE; + if(poGeom != NULL && pszGeomColumn != NULL) + { + oStmt.Appendf( "[%s] = ", pszGeomColumn ); + + if (nUploadGeometryFormat == MSSQLGEOMETRY_WKB) + { + int nWKBLen = poGeom->WkbSize(); + GByte *pabyWKB = (GByte *) CPLMalloc(nWKBLen + 1); + + if( poGeom->exportToWkb( wkbNDR, pabyWKB ) == OGRERR_NONE && (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY + || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)) + { + int nRetCode = SQLBindParameter(oStmt.GetStatement(), (SQLUSMALLINT)(bind_num + 1), + SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, + nWKBLen, 0, (SQLPOINTER)pabyWKB, nWKBLen, (SQLLEN*)&nWKBLen); + if ( nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO ) + { + if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY) + { + oStmt.Append( "geography::STGeomFromWKB(?" ); + oStmt.Appendf(",%d)", nSRSId ); + } + else + { + oStmt.Append( "geometry::STGeomFromWKB(?" ); + oStmt.Appendf(",%d).MakeValid()", nSRSId ); + } + bind_buffer[bind_num] = pabyWKB; + ++bind_num; + } + else + { + oStmt.Append( "null" ); + CPLFree(pabyWKB); + } + } + else + { + oStmt.Append( "null" ); + CPLFree(pabyWKB); + } + } + else if (nUploadGeometryFormat == MSSQLGEOMETRY_WKT) + { + char *pszWKT = NULL; + if( poGeom->exportToWkt( &pszWKT ) == OGRERR_NONE && (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY + || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)) + { + size_t nLen = 0; + while(pszWKT[nLen] != '\0') + nLen ++; + + int nRetCode = SQLBindParameter(oStmt.GetStatement(), (SQLUSMALLINT)(bind_num + 1), + SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, + nLen, 0, (SQLPOINTER)pszWKT, 0, NULL); + if ( nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO ) + { + if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY) + { + oStmt.Append( "geography::STGeomFromText(?" ); + oStmt.Appendf(",%d)", nSRSId ); + } + else + { + oStmt.Append( "geometry::STGeomFromText(?" ); + oStmt.Appendf(",%d).MakeValid()", nSRSId ); + } + bind_buffer[bind_num] = pszWKT; + ++bind_num; + } + else + { + oStmt.Append( "null" ); + CPLFree(pszWKT); + } + } + else + { + oStmt.Append( "null" ); + CPLFree(pszWKT); + } + } + else + oStmt.Append( "null" ); + + bNeedComma = TRUE; + } + + int i; + for( i = 0; i < nFieldCount; i++ ) + { + if (bNeedComma) + oStmt.Appendf( ", [%s] = ", poFeatureDefn->GetFieldDefn(i)->GetNameRef() ); + else + { + oStmt.Appendf( "[%s] = ", poFeatureDefn->GetFieldDefn(i)->GetNameRef() ); + bNeedComma = TRUE; + } + + if( !poFeature->IsFieldSet( i ) ) + oStmt.Append( "null" ); + else + AppendFieldValue(&oStmt, poFeature, i, &bind_num, bind_buffer); + } + + /* Add the WHERE clause */ + oStmt.Appendf( " WHERE [%s] = " CPL_FRMT_GIB, pszFIDColumn, poFeature->GetFID()); + +/* -------------------------------------------------------------------- */ +/* Execute the update. */ +/* -------------------------------------------------------------------- */ + + if( !oStmt.ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Error updating feature with FID:" CPL_FRMT_GIB ", %s", poFeature->GetFID(), + poDS->GetSession()->GetLastError() ); + + for( i = 0; i < bind_num; i++ ) + CPLFree(bind_buffer[i]); + CPLFree(bind_buffer); + + return OGRERR_FAILURE; + } + + for( i = 0; i < bind_num; i++ ) + CPLFree(bind_buffer[i]); + CPLFree(bind_buffer); + + if (oStmt.GetRowCountAffected() < 1) + return OGRERR_NON_EXISTING_FEATURE; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialTableLayer::DeleteFeature( GIntBig nFID ) + +{ + GetLayerDefn(); + + if( pszFIDColumn == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DeleteFeature() without any FID column." ); + return OGRERR_FAILURE; + } + + if( nFID == OGRNullFID ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DeleteFeature() with unset FID fails." ); + return OGRERR_FAILURE; + } + + ClearStatement(); + +/* -------------------------------------------------------------------- */ +/* Drop the record with this FID. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oStatement( poDS->GetSession() ); + + oStatement.Appendf("DELETE FROM [%s] WHERE [%s] = " CPL_FRMT_GIB, + poFeatureDefn->GetName(), pszFIDColumn, nFID); + + if( !oStatement.ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to delete feature with FID " CPL_FRMT_GIB " failed. %s", + nFID, poDS->GetSession()->GetLastError() ); + + return OGRERR_FAILURE; + } + + if (oStatement.GetRowCountAffected() < 1) + return OGRERR_NON_EXISTING_FEATURE; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRMSSQLSpatialTableLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + GetLayerDefn(); + + if( NULL == poFeature ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "NULL pointer to OGRFeature passed to CreateFeature()." ); + return OGRERR_FAILURE; + } + + ClearStatement(); + + CPLODBCStatement oStatement( poDS->GetSession() ); + + /* the fid values are retieved from the source layer */ + if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL && bIsIdentityFid ) + oStatement.Appendf("SET IDENTITY_INSERT [%s].[%s] ON;", pszSchemaName, pszTableName ); + +/* -------------------------------------------------------------------- */ +/* Form the INSERT command. */ +/* -------------------------------------------------------------------- */ + + oStatement.Appendf( "INSERT INTO [%s].[%s] ", pszSchemaName, pszTableName ); + + OGRMSSQLGeometryValidator oValidator(poFeature->GetGeometryRef()); + OGRGeometry *poGeom = oValidator.GetValidGeometryRef(); + + if (poFeature->GetGeometryRef() != poGeom) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Geometry with FID = " CPL_FRMT_GIB " has been modified.", poFeature->GetFID() ); + } + + int bNeedComma = FALSE; + + if (poGeom != NULL && pszGeomColumn != NULL) + { + oStatement.Append("(["); + oStatement.Append( pszGeomColumn ); + oStatement.Append("]"); + bNeedComma = TRUE; + } + + if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL ) + { + if( (GIntBig)(int)poFeature->GetFID() != poFeature->GetFID() && + GetMetadataItem(OLMD_FID64) == NULL ) + { + /* MSSQL server doesn't support modifying pk columns without recreating the field */ + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to create feature with large integer fid. " + "The FID64 layer creation option should be used." ); + return OGRERR_FAILURE; + } + + if (bNeedComma) + oStatement.Appendf( ", [%s]", pszFIDColumn ); + else + { + oStatement.Appendf( "([%s]", pszFIDColumn ); + bNeedComma = TRUE; + } + } + + int nFieldCount = poFeatureDefn->GetFieldCount(); + + int bind_num = 0; + void** bind_buffer = (void**)CPLMalloc(sizeof(void*) * (nFieldCount + 1)); + + int i; + for( i = 0; i < nFieldCount; i++ ) + { + if( !poFeature->IsFieldSet( i ) ) + continue; + + if (bNeedComma) + oStatement.Appendf( ", [%s]", poFeatureDefn->GetFieldDefn(i)->GetNameRef() ); + else + { + oStatement.Appendf( "([%s]", poFeatureDefn->GetFieldDefn(i)->GetNameRef() ); + bNeedComma = TRUE; + } + } + + if (oStatement.GetCommand()[strlen(oStatement.GetCommand()) - 1] != ']') + { + /* no fields were added */ + oStatement.Appendf( "DEFAULT VALUES;" ); + } + else + { + oStatement.Appendf( ") VALUES (" ); + + /* Set the geometry */ + bNeedComma = FALSE; + if(poGeom != NULL && pszGeomColumn != NULL) + { + if (nUploadGeometryFormat == MSSQLGEOMETRY_WKB) + { + int nWKBLen = poGeom->WkbSize(); + GByte *pabyWKB = (GByte *) CPLMalloc(nWKBLen + 1); + + if( poGeom->exportToWkb( wkbNDR, pabyWKB ) == OGRERR_NONE && (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY + || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)) + { + int nRetCode = SQLBindParameter(oStatement.GetStatement(), (SQLUSMALLINT)(bind_num + 1), + SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, + nWKBLen, 0, (SQLPOINTER)pabyWKB, nWKBLen, (SQLLEN*)&nWKBLen); + if ( nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO ) + { + if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY) + { + oStatement.Append( "geography::STGeomFromWKB(?" ); + oStatement.Appendf(",%d)", nSRSId ); + } + else + { + oStatement.Append( "geometry::STGeomFromWKB(?" ); + oStatement.Appendf(",%d).MakeValid()", nSRSId ); + } + bind_buffer[bind_num] = pabyWKB; + ++bind_num; + } + else + { + oStatement.Append( "null" ); + CPLFree(pabyWKB); + } + } + else + { + oStatement.Append( "null" ); + CPLFree(pabyWKB); + } + } + else if (nUploadGeometryFormat == MSSQLGEOMETRY_WKT) + { + char *pszWKT = NULL; + if( poGeom->exportToWkt( &pszWKT ) == OGRERR_NONE && (nGeomColumnType == MSSQLCOLTYPE_GEOMETRY + || nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)) + { + size_t nLen = 0; + while(pszWKT[nLen] != '\0') + nLen ++; + + int nRetCode = SQLBindParameter(oStatement.GetStatement(), (SQLUSMALLINT)(bind_num + 1), + SQL_PARAM_INPUT, SQL_C_CHAR, SQL_LONGVARCHAR, + nLen, 0, (SQLPOINTER)pszWKT, 0, NULL); + if ( nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO ) + { + if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY) + { + oStatement.Append( "geography::STGeomFromText(?" ); + oStatement.Appendf(",%d)", nSRSId ); + } + else + { + oStatement.Append( "geometry::STGeomFromText(?" ); + oStatement.Appendf(",%d).MakeValid()", nSRSId ); + } + bind_buffer[bind_num] = pszWKT; + ++bind_num; + } + else + { + oStatement.Append( "null" ); + CPLFree(pszWKT); + } + } + else + { + oStatement.Append( "null" ); + CPLFree(pszWKT); + } + } + else + oStatement.Append( "null" ); + + bNeedComma = TRUE; + } + + /* Set the FID */ + if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL ) + { + if (bNeedComma) + oStatement.Appendf( ", " CPL_FRMT_GIB, poFeature->GetFID() ); + else + { + oStatement.Appendf( CPL_FRMT_GIB, poFeature->GetFID() ); + bNeedComma = TRUE; + } + } + + for( i = 0; i < nFieldCount; i++ ) + { + if( !poFeature->IsFieldSet( i ) ) + continue; + + if (bNeedComma) + oStatement.Append( ", " ); + else + bNeedComma = TRUE; + + AppendFieldValue(&oStatement, poFeature, i, &bind_num, bind_buffer); + } + + oStatement.Append( ");" ); + } + + if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL && bIsIdentityFid ) + oStatement.Appendf("SET IDENTITY_INSERT [%s].[%s] OFF;", pszSchemaName, pszTableName ); + +/* -------------------------------------------------------------------- */ +/* Execute the insert. */ +/* -------------------------------------------------------------------- */ + + if( !oStatement.ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "INSERT command for new feature failed. %s", + poDS->GetSession()->GetLastError() ); + + for( i = 0; i < bind_num; i++ ) + CPLFree(bind_buffer[i]); + CPLFree(bind_buffer); + + return OGRERR_FAILURE; + } + + for( i = 0; i < bind_num; i++ ) + CPLFree(bind_buffer[i]); + CPLFree(bind_buffer); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* AppendFieldValue() */ +/* */ +/* Used by CreateFeature() and SetFeature() to format a */ +/* non-empty field value */ +/************************************************************************/ + +void OGRMSSQLSpatialTableLayer::AppendFieldValue(CPLODBCStatement *poStatement, + OGRFeature* poFeature, int i, int *bind_num, void **bind_buffer) +{ + int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType(); + + // We need special formatting for integer list values. + if( nOGRFieldType == OFTIntegerList ) + { + //TODO + poStatement->Append( "null" ); + return; + } + + // We need special formatting for real list values. + else if( nOGRFieldType == OFTRealList ) + { + //TODO + poStatement->Append( "null" ); + return; + } + + // We need special formatting for string list values. + else if( nOGRFieldType == OFTStringList ) + { + //TODO + poStatement->Append( "null" ); + return; + } + + // Binary formatting + if( nOGRFieldType == OFTBinary ) + { + int nLen = 0; + GByte* pabyData = poFeature->GetFieldAsBinary( i, &nLen ); + char* pszBytes = GByteArrayToHexString( pabyData, nLen); + poStatement->Append( pszBytes ); + CPLFree(pszBytes); + return; + } + + // Flag indicating NULL or not-a-date date value + // e.g. 0000-00-00 - there is no year 0 + OGRBoolean bIsDateNull = FALSE; + + const char *pszStrValue = poFeature->GetFieldAsString(i); + + // Check if date is NULL: 0000-00-00 + if( nOGRFieldType == OFTDate ) + { + if( EQUALN( pszStrValue, "0000", 4 ) ) + { + pszStrValue = "null"; + bIsDateNull = TRUE; + } + } + else if ( nOGRFieldType == OFTReal ) + { + char* pszComma = strchr((char*)pszStrValue, ','); + if (pszComma) + *pszComma = '.'; + } + + if( nOGRFieldType != OFTInteger && nOGRFieldType != OFTInteger64 && nOGRFieldType != OFTReal + && !bIsDateNull ) + { + if (nOGRFieldType == OFTString) + { + // bind UTF8 as unicode parameter + wchar_t* buffer = CPLRecodeToWChar( pszStrValue, CPL_ENC_UTF8, CPL_ENC_UCS2); + int nRetCode = SQLBindParameter(poStatement->GetStatement(), (SQLUSMALLINT)((*bind_num) + 1), + SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WVARCHAR, + wcslen(buffer) + 1, 0, (SQLPOINTER)buffer, 0, NULL); + if ( nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO ) + { + poStatement->Append( "?" ); + bind_buffer[*bind_num] = buffer; + ++(*bind_num); + } + else + { + OGRMSSQLAppendEscaped(poStatement, pszStrValue); + CPLFree(buffer); + } + } + else + OGRMSSQLAppendEscaped(poStatement, pszStrValue); + } + else + { + poStatement->Append( pszStrValue ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/GNUmakefile new file mode 100644 index 000000000..e2ea885e3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/GNUmakefile @@ -0,0 +1,15 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrmysqldriver.o ogrmysqldatasource.o \ + ogrmysqltablelayer.o ogrmysqllayer.o ogrmysqlresultlayer.o + +CPPFLAGS := -I.. -I../.. $(MYSQL_INC) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_mysql.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/drv_mysql.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/drv_mysql.html new file mode 100644 index 000000000..a5d4a511c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/drv_mysql.html @@ -0,0 +1,197 @@ + + +MySQL + + + + +

    MySQL

    + +This driver implements read and write access for spatial data in +MySQL tables. This functionality +was introduced in GDAL/OGR 1.3.2.

    + +When opening a database, it's name should be specified in the form +"MYSQL:dbname[,options]" where the options can include comma seperated +items like "user=*userid*", "password=*password*", "host=*host*" and +"port=*port*".

    + +As well, a "tables=*table*;*table*..." option can be +added to restrict access to a specific list of tables in the database. This +option is primarily useful when a database has a lot of tables, and scanning +all their schemas would take a significant amount of time.

    + +Currently all regular user tables are assumed to be layers from an OGR +point of view, with the table names as the layer names. Named views are +not currently supported.

    + +If a single integer field is a primary key, it will be used as the FID +otherwise the FID will be assigned sequentially, and fetches by FID will +be extremely slow.

    + +

    By default, SQL statements are passed directly to the MySQL database engine. +It's also possible to request the driver to handle SQL commands +with OGR SQL engine, +by passing "OGRSQL" string to the ExecuteSQL() +method, as name of the SQL dialect.

    + +

    Caveats

    +
      +
    • + In the case of a layer defined by a SQL statement, fields either named + "OGC_FID" or those that are defined as NOT NULL, are a PRIMARY KEY, and + are an integer-like field will be assumed to be the FID. +
    • +
    • + Geometry fields are read from MySQL using WKB format. Versions older + than 5.0.16 of MySQL are known to have issues with some WKB + generation and may not work properly. +
    • +
    • + The OGR_FID column, which can be overridden with the MYSQL_FID layer + creation option, is implemented as a + INT UNIQUE NOT NULL AUTO_INCREMENT field. This + appears to implicitly create an index on the field. +
    • +
    • + The geometry column, which defaults to SHAPE and can be overridden + with the GEOMETRY_NAME layer creation option, is created as a + NOT NULL column in unless SPATIAL_INDEX is disabled. By default + a spatial index is created at the point the table is created. +
    • +
    • + SRS information is stored using the OGC Simple Features for SQL layout, with + geometry_columns and spatial_ref_sys metadata tables being + created in the specified database if they do not already exist. The + spatial_ref_sys table is not pre-populated with SRS and + EPSG values like PostGIS. If no EPSG code is found for a given table, + the MAX(SRID) value will be used. +
    • + +
    • + Connection timeouts to the server can be specified with the MYSQL_TIMEOUT + environment variable. For example, SET MYSQL_TIMEOUT=3600. It is possible this + variable only has an impact when the OS of the MySQL server is Windows. +
    • +
    • + The MySQL driver opens a connection to the database using CLIENT_INTERACTIVE mode. + You can adjust this setting (interactive_timeout) in your mysql.ini or mysql.cnf + file of your server to your liking. +
    • +
    • + We are using WKT to insert geometries into the database. + If you are inserting big geometries, you will need to be aware of the max_allowed_packet + parameter in the MySQL configuration. By default it is set to 1M, but this will not + be large enough for really big geometries. If you get an error message like: + Got a packet bigger than 'max_allowed_packet' bytes, you will need to increase + this parameter. +
    • + +
    + +

    Creation Issues

    + +The MySQL driver does not support creation of new datasets (a database +within MySQL), but it does allow creation of new layers within an +existing database.

    + +By default, the MySQL driver will attempt to preserve the precision +of OGR features when creating and reading MySQL layers. For integer fields +with a specified width, it will use DECIMAL as the MySQL field +type with a specified precision of 0. For real fields, it will use +DOUBLE with the specified width and precision. For string fields +with a specified width, VARCHAR will be used.

    + +The MySQL driver makes no allowances for character encodings at this time.

    + +The MySQL driver is not transactional at this time.

    + + + + + +

    Layer Creation Options

    + +
      +
    • + OVERWRITE: This may be "YES" to force an existing layer of the + desired name to be destroyed before creating the requested layer. +
    • +
    • + LAUNDER: This may be "YES" to force new fields created on this + layer to have their field names "laundered" into a form more + compatible with MySQL. This converts to lower case and converts + some special characters like "-" and "#" to "_". If "NO" exact names + are preserved. The default value is "YES". +
    • +
    • + PRECISION: This may be "TRUE" to attempt to preserve field + widths and precisions for the creation and reading of MySQL layers. + The default value is "TRUE". +
    • +
    • + GEOMETRY_NAME: This option specifies the name of the + geometry column. The default value is "SHAPE". +
    • +
    • + FID: This option specifies the name of the FID column. + The default value is "OGR_FID". Note: option was called MYSQL_FID in releases before GDAL 2 +
    • +
    • + FID64: (GDAL >= 2.0) This may be "TRUE" to create a FID column that can support + 64 bit identifiers. The default value is "FALSE". +
    • +
    • + SPATIAL_INDEX: May be "NO" to stop automatic creation of + a spatial index on the geometry column, allowing NULL geometries + and possibly faster loading. +
    • +
    • + ENGINE: Optionally specify database engine to use. In MySQL + 4.x this must be set to MyISAM for spatial tables. +
    • +
    + +The following example datasource name opens the database schema +westholland with password psv9570 for userid root +on the port 3306. No hostname is provided, so localhost is assumed. +The tables= directive means that only the bedrijven table is scanned and +presented as a layer for use.

    + +

    +MYSQL:westholland,user=root,password=psv9570,port=3306,tables=bedrijven
    +
    + +The following example uses ogr2ogr to create copy the world_borders layer +from a shapefile into a MySQL table. It overwrites a table with the existing +name borders2, sets a layer creation option to specify the geometry +column name to SHAPE2. + +
    +ogr2ogr -f MySQL MySQL:test,user=root world_borders.shp -nln borders2 -update -overwrite -lco GEOMETRY_NAME=SHAPE2 
    +
    + +The following example uses ogrinfo to return some summary information about the borders2 +layer in the test database. +
    +ogrinfo MySQL:test,user=root borders2 -so
    +
    +    Layer name: borders2
    +    Geometry: Polygon
    +    Feature Count: 3784
    +    Extent: (-180.000000, -90.000000) - (180.000000, 83.623596)
    +    Layer SRS WKT:
    +    GEOGCS["GCS_WGS_1984",
    +        DATUM["WGS_1984",
    +            SPHEROID["WGS_84",6378137,298.257223563]],
    +        PRIMEM["Greenwich",0],
    +        UNIT["Degree",0.017453292519943295]]
    +    FID Column = OGR_FID
    +    Geometry Column = SHAPE2
    +    cat: Real (0.0)
    +    fips_cntry: String (80.0)
    +    cntry_name: String (80.0)
    +    area: Real (15.2)
    +    pop_cntry: Real (15.2)
    +
    +
    diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/makefile.vc
    new file mode 100644
    index 000000000..daac7107d
    --- /dev/null
    +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/makefile.vc
    @@ -0,0 +1,16 @@
    +
    +OBJ	=	ogrmysqldriver.obj ogrmysqldatasource.obj ogrmysqllayer.obj \
    +		ogrmysqltablelayer.obj ogrmysqlresultlayer.obj
    +
    +GDAL_ROOT	=	..\..\..
    +
    +!INCLUDE $(GDAL_ROOT)\nmake.opt
    +
    +EXTRAFLAGS = -I.. -I..\.. -I$(MYSQL_INC_DIR)
    +
    +default:	$(OBJ)
    +
    +clean:
    +	-del *.lib
    +	-del *.obj *.pdb
    +	-del *.exe
    diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogr_mysql.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogr_mysql.h
    new file mode 100644
    index 000000000..c1d4c4d65
    --- /dev/null
    +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogr_mysql.h
    @@ -0,0 +1,261 @@
    +/******************************************************************************
    + * $Id: ogr_mysql.h 29019 2015-04-25 20:34:19Z rouault $
    + *
    + * Project:  OpenGIS Simple Features Reference Implementation
    + * Purpose:  Declarations for MySQL OGR Driver Classes.
    + * Author:   Frank Warmerdam, warmerdam@pobox.com
    + * Author:   Howard Butler, hobu@hobu.net
    + *
    + ******************************************************************************
    + * Copyright (c) 2004, Frank Warmerdam 
    + * Copyright (c) 2008-2012, Even Rouault 
    + *
    + * Permission is hereby granted, free of charge, to any person obtaining a
    + * copy of this software and associated documentation files (the "Software"),
    + * to deal in the Software without restriction, including without limitation
    + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    + * and/or sell copies of the Software, and to permit persons to whom the
    + * Software is furnished to do so, subject to the following conditions:
    + *
    + * The above copyright notice and this permission notice shall be included
    + * in all copies or substantial portions of the Software.
    + *
    + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
    + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    + * DEALINGS IN THE SOFTWARE.
    + ****************************************************************************/
    +
    +#ifndef _OGR_MYSQL_H_INCLUDED
    +#define _OGR_MYSQL_H_INCLUDED
    +
    +#include 
    +#include 
    +
    +/* my_global.h from mysql 5.1 declares the min and max macros. */
    +/* This conflicts with templates in g++-4.3.2 header files. Grrr */
    +#ifdef min
    +#undef min
    +#endif
    +
    +#ifdef max
    +#undef max
    +#endif
    +
    +#ifdef bool
    +#undef bool
    +#endif
    +
    +#include "ogrsf_frmts.h"
    +
    +/************************************************************************/
    +/*                            OGRMySQLLayer                             */
    +/************************************************************************/
    +
    +class OGRMySQLDataSource;
    +    
    +class OGRMySQLLayer : public OGRLayer
    +{
    +  protected:
    +    OGRFeatureDefn     *poFeatureDefn;
    +
    +    // Layer spatial reference system, and srid.
    +    OGRSpatialReference *poSRS;
    +    int                 nSRSId;
    +
    +    GIntBig             iNextShapeId;
    +
    +    OGRMySQLDataSource    *poDS;
    + 
    +    char               *pszQueryStatement;
    +
    +    int                 nResultOffset;
    +
    +    char                *pszGeomColumn;
    +    char                *pszGeomColumnTable;
    +    int                 nGeomType;
    +
    +    int                 bHasFid;
    +    char                *pszFIDColumn;
    +
    +    MYSQL_RES           *hResultSet;
    +
    +	int                 FetchSRSId();
    +
    +  public:
    +                        OGRMySQLLayer();
    +    virtual             ~OGRMySQLLayer();
    +
    +    virtual void        ResetReading();
    +
    +    virtual OGRFeature *GetNextFeature();
    +
    +    virtual OGRFeature *GetFeature( GIntBig nFeatureId );
    +    
    +    OGRFeatureDefn *    GetLayerDefn() { return poFeatureDefn; }
    +
    +    virtual OGRSpatialReference *GetSpatialRef();
    +
    +    virtual const char *GetFIDColumn();
    +    virtual const char *GetGeometryColumn();
    +
    +    /* custom methods */
    +    virtual OGRFeature *RecordToFeature( char **papszRow, unsigned long * );
    +    virtual OGRFeature *GetNextRawFeature();
    +};
    +
    +/************************************************************************/
    +/*                          OGRMySQLTableLayer                          */
    +/************************************************************************/
    +
    +class OGRMySQLTableLayer : public OGRMySQLLayer
    +{
    +    int                 bUpdateAccess;
    +
    +    OGRFeatureDefn     *ReadTableDefinition(const char *);
    +
    +    void                BuildWhere(void);
    +    char               *BuildFields(void);
    +    void                BuildFullQueryStatement(void);
    +
    +    char                *pszQuery;
    +    char                *pszWHERE;
    +
    +    int                 bLaunderColumnNames;
    +    int                 bPreservePrecision;
    +    
    +  public:
    +                        OGRMySQLTableLayer( OGRMySQLDataSource *,
    +                                         const char * pszName,
    +                                         int bUpdate, int nSRSId = -2 );
    +                        ~OGRMySQLTableLayer();
    +
    +    OGRErr              Initialize(const char* pszTableName);
    +    
    +    virtual OGRFeature *GetFeature( GIntBig nFeatureId );
    +    virtual void        ResetReading();
    +    virtual GIntBig     GetFeatureCount( int );
    +
    +    void                SetSpatialFilter( OGRGeometry * );
    +
    +    virtual OGRErr      SetAttributeFilter( const char * );
    +    virtual OGRErr      ICreateFeature( OGRFeature *poFeature );
    +    virtual OGRErr      DeleteFeature( GIntBig nFID );
    +    virtual OGRErr      ISetFeature( OGRFeature *poFeature );
    +    
    +    virtual OGRErr      CreateField( OGRFieldDefn *poField,
    +                                     int bApproxOK = TRUE );
    +
    +    void                SetLaunderFlag( int bFlag )
    +                                { bLaunderColumnNames = bFlag; }
    +    void                SetPrecisionFlag( int bFlag )
    +                                { bPreservePrecision = bFlag; }    
    +
    +    virtual int         TestCapability( const char * );
    +	virtual OGRErr      GetExtent(OGREnvelope *psExtent, int bForce = TRUE);
    +};
    +
    +/************************************************************************/
    +/*                         OGRMySQLResultLayer                          */
    +/************************************************************************/
    +
    +class OGRMySQLResultLayer : public OGRMySQLLayer
    +{
    +    void                BuildFullQueryStatement(void);
    +
    +    char                *pszRawStatement;
    +    
    +    // Layer srid.
    +    int                 nSRSId;
    +    
    +  public:
    +                        OGRMySQLResultLayer( OGRMySQLDataSource *,
    +                                             const char * pszRawStatement,
    +                                             MYSQL_RES *hResultSetIn );
    +    virtual             ~OGRMySQLResultLayer();
    +
    +    OGRFeatureDefn     *ReadResultDefinition();
    +
    +
    +    virtual void        ResetReading();
    +    virtual GIntBig     GetFeatureCount( int );
    +
    +    virtual int         TestCapability( const char * );
    +};
    +
    +/************************************************************************/
    +/*                          OGRMySQLDataSource                          */
    +/************************************************************************/
    +
    +class OGRMySQLDataSource : public OGRDataSource
    +{
    +    OGRMySQLLayer       **papoLayers;
    +    int                 nLayers;
    +    
    +    char               *pszName;
    +
    +    int                 bDSUpdate;
    +
    +    int                 nSoftTransactionLevel;
    +
    +    MYSQL              *hConn;
    +
    +    int                DeleteLayer( int iLayer );
    +
    +    // We maintain a list of known SRID to reduce the number of trips to
    +    // the database to get SRSes. 
    +    int                 nKnownSRID;
    +    int                *panSRID;
    +    OGRSpatialReference **papoSRS;
    +
    +    OGRMySQLLayer      *poLongResultLayer;
    +    
    +  public:
    +                        OGRMySQLDataSource();
    +                        ~OGRMySQLDataSource();
    +
    +    MYSQL              *GetConn() { return hConn; }
    +
    +
    +    int                 FetchSRSId( OGRSpatialReference * poSRS );
    +
    +    OGRSpatialReference *FetchSRS( int nSRSId );
    +
    +    OGRErr              InitializeMetadataTables();
    +
    +    int                 Open( const char *, char** papszOpenOptions, int bUpdate );
    +    int                 OpenTable( const char *, int bUpdate );
    +
    +    const char          *GetName() { return pszName; }
    +    int                 GetLayerCount() { return nLayers; }
    +    OGRLayer            *GetLayer( int );
    +
    +    virtual OGRLayer    *ICreateLayer( const char *, 
    +                                      OGRSpatialReference * = NULL,
    +                                      OGRwkbGeometryType = wkbUnknown,
    +                                      char ** = NULL );
    +
    +
    +    int                 TestCapability( const char * );
    +
    +    virtual OGRLayer *  ExecuteSQL( const char *pszSQLCommand,
    +                                    OGRGeometry *poSpatialFilter,
    +                                    const char *pszDialect );
    +    virtual void        ReleaseResultSet( OGRLayer * poLayer );
    +
    +    // nonstandard
    +
    +    void                ReportError( const char * = NULL );
    +    
    +    char               *LaunderName( const char * );
    +
    +    void                RequestLongResult( OGRMySQLLayer * );
    +    void                InterruptLongResult();
    +};
    +
    +#endif /* ndef _OGR_MYSQL_H_INCLUDED */
    +
    +
    diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqldatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqldatasource.cpp
    new file mode 100644
    index 000000000..2b42b27fb
    --- /dev/null
    +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqldatasource.cpp
    @@ -0,0 +1,1093 @@
    +/******************************************************************************
    + * $Id: ogrmysqldatasource.cpp 29019 2015-04-25 20:34:19Z rouault $
    + *
    + * Project:  OpenGIS Simple Features Reference Implementation
    + * Purpose:  Implements OGRMySQLDataSource class.
    + * Author:   Frank Warmerdam, warmerdam@pobox.com
    + * Author:   Howard Butler, hobu@hobu.net
    + *
    + ******************************************************************************
    + * Copyright (c) 2004, Frank Warmerdam 
    + * Copyright (c) 2008-2013, Even Rouault 
    + *
    + * Permission is hereby granted, free of charge, to any person obtaining a
    + * copy of this software and associated documentation files (the "Software"),
    + * to deal in the Software without restriction, including without limitation
    + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    + * and/or sell copies of the Software, and to permit persons to whom the
    + * Software is furnished to do so, subject to the following conditions:
    + *
    + * The above copyright notice and this permission notice shall be included
    + * in all copies or substantial portions of the Software.
    + *
    + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
    + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    + * DEALINGS IN THE SOFTWARE.
    + ****************************************************************************/
    +
    +
    +#include 
    +#include "ogr_mysql.h"
    +#include 
    +
    +#include "cpl_conv.h"
    +#include "cpl_string.h"
    +
    +CPL_CVSID("$Id: ogrmysqldatasource.cpp 29019 2015-04-25 20:34:19Z rouault $");
    +/************************************************************************/
    +/*                         OGRMySQLDataSource()                         */
    +/************************************************************************/
    +
    +OGRMySQLDataSource::OGRMySQLDataSource()
    +
    +{
    +    pszName = NULL;
    +    papoLayers = NULL;
    +    nLayers = 0;
    +    hConn = 0;
    +    nSoftTransactionLevel = 0;
    +
    +    nKnownSRID = 0;
    +    panSRID = NULL;
    +    papoSRS = NULL;
    +
    +    poLongResultLayer = NULL;
    +}
    +
    +/************************************************************************/
    +/*                        ~OGRMySQLDataSource()                         */
    +/************************************************************************/
    +
    +OGRMySQLDataSource::~OGRMySQLDataSource()
    +
    +{
    +    int         i;
    +
    +    InterruptLongResult();
    +
    +    CPLFree( pszName );
    +
    +    for( i = 0; i < nLayers; i++ )
    +        delete papoLayers[i];
    +    
    +    CPLFree( papoLayers );
    +
    +    if( hConn != NULL )
    +        mysql_close( hConn );
    +
    +    for( i = 0; i < nKnownSRID; i++ )
    +    {
    +        if( papoSRS[i] != NULL )
    +            papoSRS[i]->Release();
    +    }
    +    CPLFree( panSRID );
    +    CPLFree( papoSRS );
    +}
    +
    +/************************************************************************/
    +/*                            ReportError()                             */
    +/************************************************************************/
    +
    +void OGRMySQLDataSource::ReportError( const char *pszDescription )
    +
    +{
    +    if( pszDescription )
    +        CPLError( CE_Failure, CPLE_AppDefined, 
    +                  "MySQL error message:%s Description: %s", 
    +                  mysql_error( hConn ), 
    +                  pszDescription );
    +    else
    +        CPLError( CE_Failure, CPLE_AppDefined, 
    +                  "%s", mysql_error( hConn ) );
    +}
    +
    +/************************************************************************/
    +/*                                Open()                                */
    +/************************************************************************/
    +
    +int OGRMySQLDataSource::Open( const char * pszNewName, char** papszOpenOptions,
    +                              int bUpdate )
    +
    +{
    +    CPLAssert( nLayers == 0 );
    +    
    +/* -------------------------------------------------------------------- */
    +/*      Use options process to get .my.cnf file contents.               */
    +/* -------------------------------------------------------------------- */
    +    int nPort = 0, i;
    +    char **papszTableNames=NULL;
    +    std::string oHost, oPassword, oUser, oDB;
    +
    +    CPLString osNewName(pszNewName);
    +    const char* apszOpenOptions[] = { "dbname", "port", "user", "password",
    +                                      "host", "tables" };
    +    for(int i=0; i <(int)(sizeof(apszOpenOptions)/sizeof(char*));i++)
    +    {
    +        const char* pszVal = CSLFetchNameValue(papszOpenOptions, apszOpenOptions[i]);
    +        if( pszVal )
    +        {
    +            if( osNewName[osNewName.size()-1] != ':' )
    +                osNewName += ",";
    +            if( i > 0 )
    +            {
    +                osNewName += apszOpenOptions[i];
    +                osNewName += "=";
    +            }
    +            if( EQUAL(apszOpenOptions[i], "tables") )
    +            {
    +                for( ; *pszVal; ++pszVal )
    +                {
    +                    if( *pszVal == ',' )
    +                        osNewName += ";";
    +                    else
    +                        osNewName += *pszVal;
    +                }
    +            }
    +            else
    +                osNewName += pszVal;
    +        }
    +    }
    +    
    +/* -------------------------------------------------------------------- */
    +/*      Parse out connection information.                               */
    +/* -------------------------------------------------------------------- */
    +    char **papszItems = CSLTokenizeString2( osNewName+6, ",", 
    +                                            CSLT_HONOURSTRINGS );
    +
    +    if( CSLCount(papszItems) < 1 )
    +    {
    +        CSLDestroy( papszItems );
    +        CPLError( CE_Failure, CPLE_AppDefined, 
    +                  "MYSQL: request missing databasename." );
    +        return FALSE;
    +    }
    +
    +    oDB = papszItems[0];
    +
    +    for( i = 1; papszItems[i] != NULL; i++ )
    +    {
    +        if( EQUALN(papszItems[i],"user=",5) )
    +            oUser = papszItems[i] + 5;
    +        else if( EQUALN(papszItems[i],"password=",9) )
    +            oPassword = papszItems[i] + 9;
    +        else if( EQUALN(papszItems[i],"host=",5) )
    +            oHost = papszItems[i] + 5;
    +        else if( EQUALN(papszItems[i],"port=",5) )
    +            nPort = atoi(papszItems[i] + 5);
    +        else if( EQUALN(papszItems[i],"tables=",7) )
    +        {
    +            papszTableNames = CSLTokenizeStringComplex( 
    +                papszItems[i] + 7, ";", FALSE, FALSE );
    +        }
    +        else
    +            CPLError( CE_Warning, CPLE_AppDefined, 
    +                      "'%s' in MYSQL datasource definition not recognised and ignored.", papszItems[i] );
    +    }
    +
    +    CSLDestroy( papszItems );
    +
    +/* -------------------------------------------------------------------- */
    +/*      Try to establish connection.                                    */
    +/* -------------------------------------------------------------------- */
    +    hConn = mysql_init( NULL );
    +
    +    if( hConn == NULL )
    +    {
    +        CPLError( CE_Failure, CPLE_AppDefined,
    +                  "mysql_init() failed." );
    +    }
    +
    +/* -------------------------------------------------------------------- */
    +/*      Set desired options on the connection: charset and timeout.     */
    +/* -------------------------------------------------------------------- */
    +    if( hConn )
    +    {
    +        const char *pszTimeoutLength = 
    +            CPLGetConfigOption( "MYSQL_TIMEOUT", "0" );  
    +        
    +        unsigned int timeout = atoi(pszTimeoutLength);        
    +        mysql_options(hConn, MYSQL_OPT_CONNECT_TIMEOUT, (char*)&timeout);
    +
    +        mysql_options(hConn, MYSQL_SET_CHARSET_NAME, "utf8" );
    +    }
    +    
    +/* -------------------------------------------------------------------- */
    +/*      Perform connection.                                             */
    +/* -------------------------------------------------------------------- */
    +    if( hConn
    +        && mysql_real_connect( hConn, 
    +                               oHost.length() ? oHost.c_str() : NULL,
    +                               oUser.length() ? oUser.c_str() : NULL,
    +                               oPassword.length() ? oPassword.c_str() : NULL,
    +                               oDB.length() ? oDB.c_str() : NULL,
    +                               nPort, NULL, CLIENT_INTERACTIVE ) == NULL )
    +    {
    +        CPLError( CE_Failure, CPLE_AppDefined,
    +                  "MySQL connect failed for: %s\n%s", 
    +                  pszNewName + 6, mysql_error( hConn ) );
    +        mysql_close( hConn );
    +        hConn = NULL;
    +    }
    +
    +    if( hConn == NULL )
    +    {
    +        CSLDestroy( papszTableNames );
    +        return FALSE;
    +    }
    +    else
    +    {
    +        // Enable automatic reconnection
    +        // Must be called after mysql_real_connect() on MySQL < 5.0.19
    +        // and at any point on more recent versions.
    +        my_bool reconnect = 1;
    +        mysql_options(hConn, MYSQL_OPT_RECONNECT, &reconnect);
    +    }
    +    
    +    pszName = CPLStrdup( pszNewName );
    +    
    +    bDSUpdate = bUpdate;
    +
    +/* -------------------------------------------------------------------- */
    +/*      Get a list of available tables.                                 */
    +/* -------------------------------------------------------------------- */
    +    if( papszTableNames == NULL )
    +    {
    +        MYSQL_RES *hResultSet;
    +        MYSQL_ROW papszRow;
    +
    +        if( mysql_query( hConn, "SHOW TABLES" ) )
    +        {
    +            ReportError( "SHOW TABLES Failed" );
    +            return FALSE;
    +        }
    +
    +        hResultSet = mysql_store_result( hConn );
    +        if( hResultSet == NULL )
    +        {
    +            ReportError( "mysql_store_result() failed on SHOW TABLES result.");
    +            return FALSE;
    +        }
    +    
    +        while( (papszRow = mysql_fetch_row( hResultSet )) != NULL )
    +        {
    +            if( papszRow[0] == NULL )
    +                continue;
    +
    +            if( EQUAL(papszRow[0],"spatial_ref_sys")
    +                || EQUAL(papszRow[0],"geometry_columns") )
    +                continue;
    +
    +            papszTableNames = CSLAddString(papszTableNames, papszRow[0] );
    +        }
    +
    +        mysql_free_result( hResultSet );
    +    }
    +
    +/* -------------------------------------------------------------------- */
    +/*      Get the schema of the available tables.                         */
    +/* -------------------------------------------------------------------- */
    +    int iRecord;
    +
    +    for( iRecord = 0; 
    +         papszTableNames != NULL && papszTableNames[iRecord] != NULL;
    +         iRecord++ )
    +    {
    +        //  FIXME: This should be fixed to deal with tables 
    +        //  for which we can't open because the name is bad/ 
    +        OpenTable( papszTableNames[iRecord], bUpdate );
    +    }
    +
    +    CSLDestroy( papszTableNames );
    +    
    +    return nLayers > 0 || bUpdate;
    +}
    +
    +/************************************************************************/
    +/*                             OpenTable()                              */
    +/************************************************************************/
    +
    +int OGRMySQLDataSource::OpenTable( const char *pszNewName, int bUpdate )
    +
    +{
    +/* -------------------------------------------------------------------- */
    +/*      Create the layer object.                                        */
    +/* -------------------------------------------------------------------- */
    +    OGRMySQLTableLayer  *poLayer;
    +    OGRErr eErr;
    +
    +    poLayer = new OGRMySQLTableLayer( this, pszNewName, bUpdate );
    +    eErr = poLayer->Initialize(pszNewName);
    +    if (eErr == OGRERR_FAILURE)
    +        return FALSE;
    +
    +/* -------------------------------------------------------------------- */
    +/*      Add layer to data source layer list.                            */
    +/* -------------------------------------------------------------------- */
    +    papoLayers = (OGRMySQLLayer **)
    +        CPLRealloc( papoLayers,  sizeof(OGRMySQLLayer *) * (nLayers+1) );
    +    papoLayers[nLayers++] = poLayer;
    +    
    +    return TRUE;
    +}
    +
    +/************************************************************************/
    +/*                           TestCapability()                           */
    +/************************************************************************/
    +
    +int OGRMySQLDataSource::TestCapability( const char * pszCap )
    +
    +{
    +	
    +    if( EQUAL(pszCap, ODsCCreateLayer) )
    +        return TRUE;
    +	if( EQUAL(pszCap, ODsCDeleteLayer))
    +		return TRUE;
    +    else
    +        return FALSE;
    +}
    +
    +/************************************************************************/
    +/*                              GetLayer()                              */
    +/************************************************************************/
    +
    +OGRLayer *OGRMySQLDataSource::GetLayer( int iLayer )
    +
    +{
    +    if( iLayer < 0 || iLayer >= nLayers )
    +        return NULL;
    +    else
    +        return papoLayers[iLayer];
    +}
    +
    +
    +/************************************************************************/
    +/*                      InitializeMetadataTables()                      */
    +/*                                                                      */
    +/*      Create the metadata tables (SPATIAL_REF_SYS and                 */
    +/*      GEOMETRY_COLUMNS). This method "does no harm" if the tables     */
    +/*      exist and can be called at will.                                */
    +/************************************************************************/
    +
    +OGRErr OGRMySQLDataSource::InitializeMetadataTables()
    +
    +{
    +    const char*      pszCommand;
    +    MYSQL_RES       *hResult;
    +    OGRErr	    eErr = OGRERR_NONE;
    + 
    +    pszCommand = "DESCRIBE geometry_columns";
    +    if( mysql_query(GetConn(), pszCommand ) )
    +    {
    +        pszCommand =
    +                "CREATE TABLE geometry_columns "
    +                "( F_TABLE_CATALOG VARCHAR(256), "
    +                "F_TABLE_SCHEMA VARCHAR(256), "
    +                "F_TABLE_NAME VARCHAR(256) NOT NULL," 
    +                "F_GEOMETRY_COLUMN VARCHAR(256) NOT NULL, "
    +                "COORD_DIMENSION INT, "
    +                "SRID INT,"
    +                "TYPE VARCHAR(256) NOT NULL)";
    +        if( mysql_query(GetConn(), pszCommand ) )
    +        {
    +            ReportError( pszCommand );
    +            eErr = OGRERR_FAILURE;
    +        }
    +        else
    +            CPLDebug("MYSQL","Creating geometry_columns metadata table");
    + 
    +    }
    + 
    +    // make sure to attempt to free results of successful queries
    +    hResult = mysql_store_result( GetConn() );
    +    if( hResult != NULL )
    +    {
    +        mysql_free_result( hResult );
    +        hResult = NULL;   
    +    }
    + 
    +    pszCommand = "DESCRIBE spatial_ref_sys";
    +    if( mysql_query(GetConn(), pszCommand ) )
    +    {
    +        pszCommand =
    +                "CREATE TABLE spatial_ref_sys "
    +                "(SRID INT NOT NULL, "
    +                "AUTH_NAME VARCHAR(256), "
    +                "AUTH_SRID INT, "
    +                "SRTEXT VARCHAR(2048))";
    +        if( mysql_query(GetConn(), pszCommand ) )
    +        {
    +            ReportError( pszCommand );
    +            eErr = OGRERR_FAILURE;
    +        }
    +        else
    +            CPLDebug("MYSQL","Creating spatial_ref_sys metadata table");
    + 
    +    }    
    + 
    +    // make sure to attempt to free results of successful queries
    +    hResult = mysql_store_result( GetConn() );
    +    if( hResult != NULL )
    +    {
    +        mysql_free_result( hResult );
    +        hResult = NULL;
    +    }
    + 
    +    return eErr;
    +}
    +
    +/************************************************************************/
    +/*                              FetchSRS()                              */
    +/*                                                                      */
    +/*      Return a SRS corresponding to a particular id.  Note that       */
    +/*      reference counting should be honoured on the returned           */
    +/*      OGRSpatialReference, as handles may be cached.                  */
    +/************************************************************************/
    +
    +OGRSpatialReference *OGRMySQLDataSource::FetchSRS( int nId )
    +{
    +    char         szCommand[128];
    +    char           **papszRow;  
    +    MYSQL_RES       *hResult;
    +            
    +    if( nId < 0 )
    +        return NULL;
    +
    +/* -------------------------------------------------------------------- */
    +/*      First, we look through our SRID cache, is it there?             */
    +/* -------------------------------------------------------------------- */
    +    int  i;
    +
    +    for( i = 0; i < nKnownSRID; i++ )
    +    {
    +        if( panSRID[i] == nId )
    +            return papoSRS[i];
    +    }
    +
    +    OGRSpatialReference *poSRS = NULL;
    + 
    +    // make sure to attempt to free any old results
    +    hResult = mysql_store_result( GetConn() );
    +    if( hResult != NULL )
    +        mysql_free_result( hResult );
    +    hResult = NULL;   
    +                        
    +    sprintf( szCommand,
    +         "SELECT srtext FROM spatial_ref_sys WHERE srid = %d",
    +         nId );
    +    
    +    if( !mysql_query( GetConn(), szCommand ) )
    +        hResult = mysql_store_result( GetConn() );
    +        
    +    char  *pszWKT = NULL;
    +    papszRow = NULL;
    +    
    +
    +    if( hResult != NULL )
    +        papszRow = mysql_fetch_row( hResult );
    +
    +    if( papszRow != NULL && papszRow[0] != NULL )
    +    {
    +        pszWKT = CPLStrdup(papszRow[0]);
    +    }
    +
    +    if( hResult != NULL )
    +        mysql_free_result( hResult );
    +    hResult = NULL;
    +
    +    poSRS = new OGRSpatialReference();
    +    char* pszWKTOri = pszWKT;
    +    if( pszWKT == NULL || poSRS->importFromWkt( &pszWKT ) != OGRERR_NONE )
    +    {
    +        delete poSRS;
    +        poSRS = NULL;
    +    }
    +
    +    CPLFree(pszWKTOri);
    +
    +/* -------------------------------------------------------------------- */
    +/*      Add to the cache.                                               */
    +/* -------------------------------------------------------------------- */
    +    panSRID = (int *) CPLRealloc(panSRID,sizeof(int) * (nKnownSRID+1) );
    +    papoSRS = (OGRSpatialReference **) 
    +        CPLRealloc(papoSRS, sizeof(void*) * (nKnownSRID + 1) );
    +    panSRID[nKnownSRID] = nId;
    +    papoSRS[nKnownSRID] = poSRS;
    +    nKnownSRID ++;
    +
    +    return poSRS;
    +}
    +
    +
    +
    +/************************************************************************/
    +/*                             FetchSRSId()                             */
    +/*                                                                      */
    +/*      Fetch the id corresponding to an SRS, and if not found, add     */
    +/*      it to the table.                                                */
    +/************************************************************************/
    +
    +int OGRMySQLDataSource::FetchSRSId( OGRSpatialReference * poSRS )
    +
    +{
    +    char           **papszRow;  
    +    MYSQL_RES       *hResult=NULL;
    +    
    +    CPLString            osCommand;
    +    char                *pszWKT = NULL;
    +    int                 nSRSId;
    +
    +    if( poSRS == NULL )
    +        return -1;
    +
    +/* -------------------------------------------------------------------- */
    +/*      Translate SRS to WKT.                                           */
    +/* -------------------------------------------------------------------- */
    +    if( poSRS->exportToWkt( &pszWKT ) != OGRERR_NONE )
    +        return -1;
    +    
    +/* -------------------------------------------------------------------- */
    +/*      Try to find in the existing table.                              */
    +/* -------------------------------------------------------------------- */
    +    osCommand.Printf( 
    +             "SELECT srid FROM spatial_ref_sys WHERE srtext = '%s'",
    +             pszWKT );
    +
    +    if( !mysql_query( GetConn(), osCommand ) )
    +        hResult = mysql_store_result( GetConn() );
    +
    +    if (!mysql_num_rows(hResult))
    +    {
    +        CPLDebug("MYSQL", "No rows exist currently exist in spatial_ref_sys");
    +        mysql_free_result( hResult );
    +        hResult = NULL;
    +    }
    +    papszRow = NULL;
    +    if( hResult != NULL )
    +        papszRow = mysql_fetch_row( hResult );
    +        
    +    if( papszRow != NULL && papszRow[0] != NULL )
    +    {
    +        nSRSId = atoi(papszRow[0]);
    +        if( hResult != NULL )
    +            mysql_free_result( hResult );
    +        hResult = NULL;
    +        CPLFree(pszWKT);
    +        return nSRSId;
    +    }
    +
    +    // make sure to attempt to free results of successful queries
    +    hResult = mysql_store_result( GetConn() );
    +    if( hResult != NULL )
    +        mysql_free_result( hResult );
    +    hResult = NULL;
    +
    +/* -------------------------------------------------------------------- */
    +/*      Get the current maximum srid in the srs table.                  */
    +/* -------------------------------------------------------------------- */
    +    osCommand = "SELECT MAX(srid) FROM spatial_ref_sys";
    +    if( !mysql_query( GetConn(), osCommand ) )
    +    {
    +        hResult = mysql_store_result( GetConn() );
    +        papszRow = mysql_fetch_row( hResult );
    +    }
    +        
    +    if( papszRow != NULL && papszRow[0] != NULL )
    +    {
    +        nSRSId = atoi(papszRow[0]) + 1;
    +    }
    +    else
    +        nSRSId = 1;
    +
    +    if( hResult != NULL )
    +        mysql_free_result( hResult );
    +    hResult = NULL;
    +
    +/* -------------------------------------------------------------------- */
    +/*      Try adding the SRS to the SRS table.                            */
    +/* -------------------------------------------------------------------- */
    +    osCommand.Printf(
    +             "INSERT INTO spatial_ref_sys (srid,srtext) VALUES (%d,'%s')",
    +             nSRSId, pszWKT );
    +
    +    if( !mysql_query( GetConn(), osCommand ) )
    +        hResult = mysql_store_result( GetConn() );
    +
    +    // make sure to attempt to free results of successful queries
    +    hResult = mysql_store_result( GetConn() );
    +    if( hResult != NULL )
    +        mysql_free_result( hResult );
    +    hResult = NULL;
    +
    +    CPLFree(pszWKT);
    +
    +    return nSRSId;
    +}
    +
    +/************************************************************************/
    +/*                             ExecuteSQL()                             */
    +/************************************************************************/
    +
    +OGRLayer * OGRMySQLDataSource::ExecuteSQL( const char *pszSQLCommand,
    +                                        OGRGeometry *poSpatialFilter,
    +                                        const char *pszDialect )
    +
    +{
    +    if( poSpatialFilter != NULL )
    +    {
    +        CPLDebug( "OGR_MYSQL", 
    +          "Spatial filter ignored for now in OGRMySQLDataSource::ExecuteSQL()" );
    +    }
    +
    +/* -------------------------------------------------------------------- */
    +/*      Use generic implementation for recognized dialects              */
    +/* -------------------------------------------------------------------- */
    +    if( IsGenericSQLDialect(pszDialect) )
    +        return OGRDataSource::ExecuteSQL( pszSQLCommand, 
    +                                          poSpatialFilter, 
    +                                          pszDialect );
    +
    +/* -------------------------------------------------------------------- */
    +/*      Special case DELLAYER: command.                                 */
    +/* -------------------------------------------------------------------- */
    +#ifdef notdef
    +    if( EQUALN(pszSQLCommand,"DELLAYER:",9) )
    +    {
    +        const char *pszLayerName = pszSQLCommand + 9;
    +
    +        while( *pszLayerName == ' ' )
    +            pszLayerName++;
    +
    +        DeleteLayer( pszLayerName );
    +        return NULL;
    +    }
    +#endif
    +
    +/* -------------------------------------------------------------------- */
    +/*      Make sure there isn't an active transaction already.            */
    +/* -------------------------------------------------------------------- */
    +    InterruptLongResult();
    +
    +/* -------------------------------------------------------------------- */
    +/*      Execute the statement.                                          */
    +/* -------------------------------------------------------------------- */
    +    MYSQL_RES *hResultSet;
    +
    +    if( mysql_query( hConn, pszSQLCommand ) )
    +    {
    +        ReportError( pszSQLCommand );
    +        return NULL;
    +    }
    +
    +    hResultSet = mysql_use_result( hConn );
    +    if( hResultSet == NULL )
    +    {
    +        if( mysql_field_count( hConn ) == 0 )
    +        {
    +            CPLDebug( "MYSQL", "Command '%s' succeeded, %d rows affected.", 
    +                      pszSQLCommand, 
    +                      (int) mysql_affected_rows(hConn) );
    +            return NULL;
    +        }
    +        else
    +        {
    +            ReportError( pszSQLCommand );
    +            return NULL;
    +        }
    +    }
    +
    +/* -------------------------------------------------------------------- */
    +/*      Do we have a tuple result? If so, instantiate a results         */
    +/*      layer for it.                                                   */
    +/* -------------------------------------------------------------------- */
    +
    +    OGRMySQLResultLayer *poLayer = NULL;
    +
    +    poLayer = new OGRMySQLResultLayer( this, pszSQLCommand, hResultSet );
    +        
    +    return poLayer;
    +}
    +
    +/************************************************************************/
    +/*                          ReleaseResultSet()                          */
    +/************************************************************************/
    +
    +void OGRMySQLDataSource::ReleaseResultSet( OGRLayer * poLayer )
    +
    +{
    +    delete poLayer;
    +}
    +
    +/************************************************************************/
    +/*                            LaunderName()                             */
    +/************************************************************************/
    +
    +char *OGRMySQLDataSource::LaunderName( const char *pszSrcName )
    +
    +{
    +    char    *pszSafeName = CPLStrdup( pszSrcName );
    +    int     i;
    +
    +    for( i = 0; pszSafeName[i] != '\0'; i++ )
    +    {
    +        pszSafeName[i] = (char) tolower( pszSafeName[i] );
    +        if( pszSafeName[i] == '-' || pszSafeName[i] == '#' )
    +            pszSafeName[i] = '_';
    +    }
    +
    +    return pszSafeName;
    +}
    +
    +/************************************************************************/
    +/*                         RequestLongResult()                          */
    +/*                                                                      */
    +/*      Layers need to use mysql_use_result() instead of                */
    +/*      mysql_store_result() so that we won't have to load entire       */
    +/*      result sets into RAM.  But only one "streamed" resultset can    */
    +/*      be active on a database connection at a time.  So we need to    */
    +/*      maintain a way of closing off an active streaming resultset     */
    +/*      before any other sort of query with a resultset is              */
    +/*      executable.  This method (and InterruptLongResult())            */
    +/*      implement that exclusion.                                       */
    +/************************************************************************/
    +
    +void OGRMySQLDataSource::RequestLongResult( OGRMySQLLayer * poNewLayer )
    +
    +{
    +    InterruptLongResult();
    +    poLongResultLayer = poNewLayer;
    +}
    +
    +/************************************************************************/
    +/*                        InterruptLongResult()                         */
    +/************************************************************************/
    +
    +void OGRMySQLDataSource::InterruptLongResult()
    +
    +{
    +    if( poLongResultLayer != NULL )
    +    {
    +        poLongResultLayer->ResetReading();
    +        poLongResultLayer = NULL;
    +    }
    +}
    +
    +
    +/************************************************************************/
    +/*                            DeleteLayer()                             */
    +/************************************************************************/
    +
    +int OGRMySQLDataSource::DeleteLayer( int iLayer)
    +
    +{
    +    if( iLayer < 0 || iLayer >= nLayers )
    +        return OGRERR_FAILURE;
    +        
    +/* -------------------------------------------------------------------- */
    +/*      Blow away our OGR structures related to the layer.  This is     */
    +/*      pretty dangerous if anything has a reference to this layer!     */
    +/* -------------------------------------------------------------------- */
    +    CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName();
    +    
    +    CPLDebug( "MYSQL", "DeleteLayer(%s)", osLayerName.c_str() );
    +
    +    delete papoLayers[iLayer];
    +    memmove( papoLayers + iLayer, papoLayers + iLayer + 1,
    +             sizeof(void *) * (nLayers - iLayer - 1) );
    +    nLayers--;
    +
    +/* -------------------------------------------------------------------- */
    +/*      Remove from the database.                                       */
    +/* -------------------------------------------------------------------- */
    +    CPLString osCommand;
    +
    +    osCommand.Printf(
    +             "DROP TABLE `%s` ",
    +             osLayerName.c_str() );
    +
    +    if( !mysql_query(GetConn(), osCommand ) )
    +    {
    +        CPLDebug("MYSQL","Dropped table %s.", osLayerName.c_str());
    +        return OGRERR_NONE;
    +    }
    +    else
    +    {
    +        ReportError( osCommand );
    +        return OGRERR_FAILURE;
    +    }
    +
    +}
    +
    +/************************************************************************/
    +/*                           ICreateLayer()                             */
    +/************************************************************************/
    +
    +OGRLayer *
    +OGRMySQLDataSource::ICreateLayer( const char * pszLayerNameIn,
    +                              OGRSpatialReference *poSRS,
    +                              OGRwkbGeometryType eType,
    +                              char ** papszOptions )
    +
    +{
    +    MYSQL_RES           *hResult=NULL;
    +    CPLString            osCommand;
    +    const char          *pszGeometryType;
    +    const char		*pszGeomColumnName;
    +    const char		*pszExpectedFIDName;
    +    char                *pszLayerName;
    +    // int                 nDimension = 3; // MySQL only supports 2d currently
    +
    +
    +/* -------------------------------------------------------------------- */
    +/*      Make sure there isn't an active transaction already.            */
    +/* -------------------------------------------------------------------- */
    +    InterruptLongResult();
    +
    +
    +    if( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) )
    +        pszLayerName = LaunderName( pszLayerNameIn );
    +    else
    +        pszLayerName = CPLStrdup( pszLayerNameIn );
    +
    +    // if( wkbFlatten(eType) == eType )
    +    //    nDimension = 2;
    +
    +    CPLDebug("MYSQL","Creating layer %s.", pszLayerName);
    +
    +/* -------------------------------------------------------------------- */
    +/*      Do we already have this layer?  If so, should we blow it        */
    +/*      away?                                                           */
    +/* -------------------------------------------------------------------- */
    +
    +    int iLayer;
    +    for( iLayer = 0; iLayer < nLayers; iLayer++ )
    +    {
    +        if( EQUAL(pszLayerName,papoLayers[iLayer]->GetLayerDefn()->GetName()) )
    +        {
    +
    +            if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL
    +                && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") )
    +            {
    +                DeleteLayer( iLayer );
    +            }
    +            else
    +            {
    +                CPLError( CE_Failure, CPLE_AppDefined,
    +                          "Layer %s already exists, CreateLayer failed.\n"
    +                          "Use the layer creation option OVERWRITE=YES to "
    +                          "replace it.",
    +                          pszLayerName );
    +                CPLFree( pszLayerName );
    +                return NULL;
    +            }
    +        }
    +    }
    +
    +    pszGeomColumnName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME" );
    +    if (!pszGeomColumnName)
    +        pszGeomColumnName="SHAPE";
    +
    +    pszExpectedFIDName = CSLFetchNameValue( papszOptions, "FID" );
    +    if (!pszExpectedFIDName)
    +        pszExpectedFIDName = CSLFetchNameValue( papszOptions, "MYSQL_FID" );
    +    if (!pszExpectedFIDName)
    +        pszExpectedFIDName="OGR_FID";
    +
    +    int bFID64 = CSLFetchBoolean(papszOptions, "FID64", FALSE);
    +    const char* pszFIDType = bFID64 ? "BIGINT": "INT";
    +    
    +
    +    CPLDebug("MYSQL","Geometry Column Name %s.", pszGeomColumnName);
    +    CPLDebug("MYSQL","FID Column Name %s.", pszExpectedFIDName);
    +
    +    if( wkbFlatten(eType) == wkbNone )
    +    {
    +        osCommand.Printf(
    +                 "CREATE TABLE `%s` ( "
    +                 "   %s %s UNIQUE NOT NULL AUTO_INCREMENT )",
    +                 pszLayerName, pszExpectedFIDName, pszFIDType );
    +    }
    +    else
    +    {
    +        osCommand.Printf(
    +                 "CREATE TABLE `%s` ( "
    +                 "   %s %s UNIQUE NOT NULL AUTO_INCREMENT, "
    +                 "   %s GEOMETRY NOT NULL )",
    +                 pszLayerName, pszExpectedFIDName, pszFIDType, pszGeomColumnName );
    +    }
    +
    +    if( CSLFetchNameValue( papszOptions, "ENGINE" ) != NULL )
    +    {
    +        osCommand += " ENGINE = ";
    +        osCommand += CSLFetchNameValue( papszOptions, "ENGINE" );
    +    }
    +	
    +    if( !mysql_query(GetConn(), osCommand ) )
    +    {
    +        if( mysql_field_count( GetConn() ) == 0 )
    +            CPLDebug("MYSQL","Created table %s.", pszLayerName);
    +        else
    +        {
    +            ReportError( osCommand );
    +            return NULL;
    +        }
    +    }
    +    else
    +    {
    +        ReportError( osCommand );
    +        return NULL;
    +    }
    +
    +    // make sure to attempt to free results of successful queries
    +    hResult = mysql_store_result( GetConn() );
    +    if( hResult != NULL )
    +        mysql_free_result( hResult );
    +    hResult = NULL;
    +    
    +    // Calling this does no harm
    +    InitializeMetadataTables();
    +    
    +/* -------------------------------------------------------------------- */
    +/*      Try to get the SRS Id of this spatial reference system,         */
    +/*      adding tot the srs table if needed.                             */
    +/* -------------------------------------------------------------------- */
    +    int nSRSId = -1;
    +
    +    if( poSRS != NULL )
    +        nSRSId = FetchSRSId( poSRS );
    +
    +/* -------------------------------------------------------------------- */
    +/*      Sometimes there is an old crufty entry in the geometry_columns  */
    +/*      table if things were not properly cleaned up before.  We make   */
    +/*      an effort to clean out such cruft.                              */
    +/*                                                                      */
    +/* -------------------------------------------------------------------- */
    +    osCommand.Printf(
    +             "DELETE FROM geometry_columns WHERE f_table_name = '%s'",
    +             pszLayerName );
    +
    +    if( mysql_query(GetConn(), osCommand ) )
    +    {
    +        ReportError( osCommand );
    +        return NULL;
    +    }
    +
    +    // make sure to attempt to free results of successful queries
    +    hResult = mysql_store_result( GetConn() );
    +    if( hResult != NULL )
    +        mysql_free_result( hResult );
    +    hResult = NULL;   
    +        
    +/* -------------------------------------------------------------------- */
    +/*      Attempt to add this table to the geometry_columns table, if     */
    +/*      it is a spatial layer.                                          */
    +/* -------------------------------------------------------------------- */
    +    if( eType != wkbNone )
    +    {
    +        int nCoordDimension;
    +        if( eType == wkbFlatten(eType) )
    +            nCoordDimension = 2;
    +        else
    +            nCoordDimension = 3;
    +
    +        pszGeometryType = OGRToOGCGeomType(eType);
    +
    +        if( nSRSId == -1 )
    +            osCommand.Printf(
    +                     "INSERT INTO geometry_columns "
    +                     " (F_TABLE_NAME, "
    +                     "  F_GEOMETRY_COLUMN, "
    +                     "  COORD_DIMENSION, "
    +                     "  TYPE) values "
    +                     "  ('%s', '%s', %d, '%s')",
    +                     pszLayerName,
    +                     pszGeomColumnName,
    +                     nCoordDimension,
    +                     pszGeometryType );
    +        else
    +            osCommand.Printf(
    +                     "INSERT INTO geometry_columns "
    +                     " (F_TABLE_NAME, "
    +                     "  F_GEOMETRY_COLUMN, "
    +                     "  COORD_DIMENSION, "
    +                     "  SRID, "
    +                     "  TYPE) values "
    +                     "  ('%s', '%s', %d, %d, '%s')",
    +                     pszLayerName,
    +                     pszGeomColumnName,
    +                     nCoordDimension,
    +                     nSRSId,
    +                     pszGeometryType );
    +
    +        if( mysql_query(GetConn(), osCommand ) )
    +        {
    +            ReportError( osCommand );
    +            return NULL;
    +        }
    +
    +        // make sure to attempt to free results of successful queries
    +        hResult = mysql_store_result( GetConn() );
    +        if( hResult != NULL )
    +            mysql_free_result( hResult );
    +        hResult = NULL;   
    +    }
    +
    +/* -------------------------------------------------------------------- */
    +/*      Create the spatial index.                                       */
    +/*                                                                      */
    +/*      We're doing this before we add geometry and record to the table */
    +/*      so this may not be exactly the best way to do it.               */
    +/* -------------------------------------------------------------------- */
    +    const char *pszSI = CSLFetchNameValue( papszOptions, "SPATIAL_INDEX" );
    +
    +    if( eType != wkbNone && (pszSI == NULL || CSLTestBoolean(pszSI)) )
    +    {
    +        osCommand.Printf(
    +                 "ALTER TABLE `%s` ADD SPATIAL INDEX(`%s`) ",
    +                 pszLayerName,
    +                 pszGeomColumnName);
    +
    +        if( mysql_query(GetConn(), osCommand ) )
    +        {
    +            ReportError( osCommand );
    +            return NULL;
    +        }
    +
    +        // make sure to attempt to free results of successful queries
    +        hResult = mysql_store_result( GetConn() );
    +        if( hResult != NULL )
    +            mysql_free_result( hResult );
    +        hResult = NULL;   
    +    }
    +        
    +/* -------------------------------------------------------------------- */
    +/*      Create the layer object.                                        */
    +/* -------------------------------------------------------------------- */
    +    OGRMySQLTableLayer     *poLayer;
    +    OGRErr                  eErr;
    +
    +    poLayer = new OGRMySQLTableLayer( this, pszLayerName, TRUE, nSRSId );
    +    eErr = poLayer->Initialize(pszLayerName);
    +    if (eErr == OGRERR_FAILURE)
    +        return NULL;
    +    if( eType != wkbNone )
    +        poLayer->GetLayerDefn()->GetGeomFieldDefn(0)->SetNullable(FALSE);
    +
    +    poLayer->SetLaunderFlag( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) );
    +    poLayer->SetPrecisionFlag( CSLFetchBoolean(papszOptions,"PRECISION",TRUE));
    +
    +/* -------------------------------------------------------------------- */
    +/*      Add layer to data source layer list.                            */
    +/* -------------------------------------------------------------------- */
    +    papoLayers = (OGRMySQLLayer **)
    +        CPLRealloc( papoLayers,  sizeof(OGRMySQLLayer *) * (nLayers+1) );
    +
    +    papoLayers[nLayers++] = poLayer;
    +
    +    CPLFree( pszLayerName );
    +
    +    return poLayer;
    +}
    diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqldriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqldriver.cpp
    new file mode 100644
    index 000000000..307133ac6
    --- /dev/null
    +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqldriver.cpp
    @@ -0,0 +1,193 @@
    +/******************************************************************************
    + * $Id: ogrmysqldriver.cpp 29019 2015-04-25 20:34:19Z rouault $
    + *
    + * Project:  OpenGIS Simple Features Reference Implementation
    + * Purpose:  Implements OGRMySQLDriver class.
    + * Author:   Frank Warmerdam, warmerdam@pobox.com
    + *
    + ******************************************************************************
    + * Copyright (c) 2004, Frank Warmerdam 
    + *
    + * Permission is hereby granted, free of charge, to any person obtaining a
    + * copy of this software and associated documentation files (the "Software"),
    + * to deal in the Software without restriction, including without limitation
    + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    + * and/or sell copies of the Software, and to permit persons to whom the
    + * Software is furnished to do so, subject to the following conditions:
    + *
    + * The above copyright notice and this permission notice shall be included
    + * in all copies or substantial portions of the Software.
    + *
    + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
    + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    + * DEALINGS IN THE SOFTWARE.
    + ****************************************************************************/
    +
    +#include "ogr_mysql.h"
    +#include "cpl_conv.h"
    +#include "cpl_multiproc.h"
    +
    +CPL_CVSID("$Id: ogrmysqldriver.cpp 29019 2015-04-25 20:34:19Z rouault $");
    +
    +static CPLMutex* hMutex = NULL;
    +static int   bInitialized = FALSE;
    +
    +/************************************************************************/
    +/*                        OGRMySQLDriverUnload()                        */
    +/************************************************************************/
    +
    +static void OGRMySQLDriverUnload( CPL_UNUSED GDALDriver* poDriver )
    +{
    +    if( bInitialized )
    +    {
    +        mysql_library_end();
    +        bInitialized = FALSE;
    +    }
    +    if( hMutex != NULL )
    +    {
    +        CPLDestroyMutex(hMutex);
    +        hMutex = NULL;
    +    }
    +}
    +
    +/************************************************************************/
    +/*                         OGRMySQLDriverIdentify()                     */
    +/************************************************************************/
    +
    +static int OGRMySQLDriverIdentify( GDALOpenInfo* poOpenInfo )
    +
    +{
    +    return EQUALN(poOpenInfo->pszFilename,"MYSQL:",6);
    +}
    + 
    +/************************************************************************/
    +/*                                Open()                                */
    +/************************************************************************/
    +
    +static GDALDataset *OGRMySQLDriverOpen( GDALOpenInfo* poOpenInfo )
    +
    +{
    +    OGRMySQLDataSource     *poDS;
    +
    +    if( !OGRMySQLDriverIdentify(poOpenInfo) )
    +        return NULL;
    + 
    +    {
    +        CPLMutexHolderD(&hMutex);
    +        if( !bInitialized )
    +        {
    +            if ( mysql_library_init( 0, NULL, NULL ) )
    +            {
    +                CPLError( CE_Failure, CPLE_AppDefined, "Could not initialize MySQL library" );
    +                return NULL;
    +            }
    +            bInitialized = TRUE;
    +        }
    +    }
    +
    +    poDS = new OGRMySQLDataSource();
    +
    +    if( !poDS->Open( poOpenInfo->pszFilename, poOpenInfo->papszOpenOptions,
    +                     poOpenInfo->eAccess == GA_Update ) )
    +    {
    +        delete poDS;
    +        return NULL;
    +    }
    +    else
    +        return poDS;
    +}
    +
    +
    +/************************************************************************/
    +/*                               Create()                               */
    +/************************************************************************/
    +
    +static GDALDataset *OGRMySQLDriverCreate( const char * pszName,
    +                                          CPL_UNUSED int nBands,
    +                                          CPL_UNUSED int nXSize,
    +                                          CPL_UNUSED int nYSize,
    +                                          CPL_UNUSED GDALDataType eDT,
    +                                          CPL_UNUSED char **papszOptions )
    +{
    +    OGRMySQLDataSource     *poDS;
    +
    +    poDS = new OGRMySQLDataSource();
    +
    +
    +    if( !poDS->Open( pszName, NULL, TRUE ) )
    +    {
    +        delete poDS;
    +        CPLError( CE_Failure, CPLE_AppDefined, 
    +         "MySQL driver doesn't currently support database creation.\n"
    +                  "Please create database before using." );
    +        return NULL;
    +    }
    +
    +    return poDS;
    +}
    +
    +/************************************************************************/
    +/*                          RegisterOGRMySQL()                          */
    +/************************************************************************/
    +
    +void RegisterOGRMySQL()
    +
    +{
    +    if (! GDAL_CHECK_VERSION("MySQL driver"))
    +        return;
    +  
    +    GDALDriver  *poDriver;
    +
    +    if( GDALGetDriverByName( "MySQL" ) == NULL )
    +    {
    +        poDriver = new GDALDriver();
    +
    +        poDriver->SetDescription( "MySQL" );
    +        poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" );
    +        poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
    +                                   "MySQL" );
    +        poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
    +                                   "drv_mysql.html" );
    +
    +        poDriver->SetMetadataItem( GDAL_DMD_CONNECTION_PREFIX, "MYSQL:" );
    +
    +        poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST,
    +""
    +"  ");
    +
    +        poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, "");
    +
    +        poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST,
    +    ""
    +    "  ");
    +        
    +        poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime Time Binary" );
    +        poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_FIELDS, "YES" );
    +        poDriver->SetMetadataItem( GDAL_DCAP_DEFAULT_FIELDS, "YES" );
    +
    +        poDriver->pfnOpen = OGRMySQLDriverOpen;
    +        poDriver->pfnIdentify = OGRMySQLDriverIdentify;
    +        poDriver->pfnCreate = OGRMySQLDriverCreate;
    +        poDriver->pfnUnloadDriver = OGRMySQLDriverUnload;
    +
    +        GetGDALDriverManager()->RegisterDriver( poDriver );
    +    }
    +}
    diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqllayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqllayer.cpp
    new file mode 100644
    index 000000000..4f6b020a9
    --- /dev/null
    +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqllayer.cpp
    @@ -0,0 +1,389 @@
    +/******************************************************************************
    + * $Id: ogrmysqllayer.cpp 28375 2015-01-30 12:06:11Z rouault $
    + *
    + * Project:  OpenGIS Simple Features Reference Implementation
    + * Purpose:  Implements OGRMySQLLayer class.
    + * Author:   Frank Warmerdam, warmerdam@pobox.com
    + * Author:   Howard Butler, hobu@hobu.net
    + *
    + ******************************************************************************
    + * Copyright (c) 2004, Frank Warmerdam 
    + *
    + * Permission is hereby granted, free of charge, to any person obtaining a
    + * copy of this software and associated documentation files (the "Software"),
    + * to deal in the Software without restriction, including without limitation
    + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    + * and/or sell copies of the Software, and to permit persons to whom the
    + * Software is furnished to do so, subject to the following conditions:
    + *
    + * The above copyright notice and this permission notice shall be included
    + * in all copies or substantial portions of the Software.
    + *
    + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
    + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    + * DEALINGS IN THE SOFTWARE.
    + ****************************************************************************/
    +
    +#include "ogr_mysql.h"
    +#include "cpl_conv.h"
    +#include "cpl_string.h"
    +
    +CPL_CVSID("$Id: ogrmysqllayer.cpp 28375 2015-01-30 12:06:11Z rouault $");
    +
    +/************************************************************************/
    +/*                           OGRMySQLLayer()                            */
    +/************************************************************************/
    +
    +OGRMySQLLayer::OGRMySQLLayer()
    +
    +{
    +    poDS = NULL;
    +
    +    pszGeomColumn = NULL;
    +    pszGeomColumnTable = NULL;
    +    pszFIDColumn = NULL;
    +    pszQueryStatement = NULL;
    +
    +    bHasFid = FALSE;
    +    pszFIDColumn = NULL;
    +
    +    iNextShapeId = 0;
    +    nResultOffset = 0;
    +
    +    poSRS = NULL;
    +    nSRSId = -2; // we haven't even queried the database for it yet. 
    +
    +    poFeatureDefn = NULL;
    +
    +    hResultSet = NULL;
    +}
    +
    +/************************************************************************/
    +/*                           ~OGRMySQLLayer()                           */
    +/************************************************************************/
    +
    +OGRMySQLLayer::~OGRMySQLLayer()
    +
    +{
    +    if( m_nFeaturesRead > 0 && poFeatureDefn != NULL )
    +    {
    +        CPLDebug( "MySQL", "%d features read on layer '%s'.",
    +                  (int) m_nFeaturesRead, 
    +                  poFeatureDefn->GetName() );
    +    }
    +
    +    ResetReading();
    +
    +    CPLFree( pszGeomColumn );
    +    CPLFree( pszGeomColumnTable );
    +    CPLFree( pszFIDColumn );
    +    CPLFree( pszQueryStatement );
    +
    +    if( poSRS != NULL )
    +        poSRS->Release();
    +
    +    if( poFeatureDefn )
    +        poFeatureDefn->Release();
    +}
    +
    +/************************************************************************/
    +/*                            ResetReading()                            */
    +/************************************************************************/
    +
    +void OGRMySQLLayer::ResetReading()
    +
    +{
    +    iNextShapeId = 0;
    +
    +    if( hResultSet != NULL )
    +    {
    +        mysql_free_result( hResultSet );
    +        hResultSet = NULL;
    +
    +        poDS->InterruptLongResult();
    +    }
    +}
    +
    +/************************************************************************/
    +/*                           GetNextFeature()                           */
    +/************************************************************************/
    +
    +OGRFeature *OGRMySQLLayer::GetNextFeature()
    +
    +{
    +
    +    for( ; TRUE; )
    +    {
    +        OGRFeature      *poFeature;
    +
    +        poFeature = GetNextRawFeature();
    +        if( poFeature == NULL )
    +            return NULL;
    +
    +        if( (m_poFilterGeom == NULL
    +            || FilterGeometry( poFeature->GetGeometryRef() ) )
    +            && (m_poAttrQuery == NULL
    +                || m_poAttrQuery->Evaluate( poFeature )) )
    +            return poFeature;
    +
    +        delete poFeature;
    +    }
    +}
    +/************************************************************************/
    +/*                          RecordToFeature()                           */
    +/*                                                                      */
    +/*      Convert the indicated record of the current result set into     */
    +/*      a feature.                                                      */
    +/************************************************************************/
    +
    +OGRFeature *OGRMySQLLayer::RecordToFeature( char **papszRow,
    +                                            unsigned long *panLengths )
    +
    +{
    +    mysql_field_seek( hResultSet, 0 );
    +
    +/* -------------------------------------------------------------------- */
    +/*      Create a feature from the current result.                       */
    +/* -------------------------------------------------------------------- */
    +    int         iField;
    +    OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
    +
    +    poFeature->SetFID( iNextShapeId );
    +    m_nFeaturesRead++;
    +
    +/* ==================================================================== */
    +/*      Transfer all result fields we can.                              */
    +/* ==================================================================== */
    +    for( iField = 0; 
    +         iField < (int) mysql_num_fields(hResultSet);
    +         iField++ )
    +    {
    +        int     iOGRField;
    +        MYSQL_FIELD *psMSField = mysql_fetch_field(hResultSet);
    +
    +/* -------------------------------------------------------------------- */
    +/*      Handle FID.                                                     */
    +/* -------------------------------------------------------------------- */
    +        if( bHasFid && EQUAL(psMSField->name,pszFIDColumn) )
    +        {
    +            if( papszRow[iField] == NULL )
    +            {
    +                CPLError( CE_Failure, CPLE_AppDefined,
    +                          "NULL primary key in RecordToFeature()" );
    +                return NULL;
    +            }
    +
    +            poFeature->SetFID( CPLAtoGIntBig(papszRow[iField]) );
    +        }
    +
    +        if( papszRow[iField] == NULL ) 
    +        {
    +//            CPLDebug("MYSQL", "%s was null for %d", psMSField->name,
    +//                     iNextShapeId);
    +            continue;
    +        }
    +
    +/* -------------------------------------------------------------------- */
    +/*      Handle MySQL geometry                                           */
    +/* -------------------------------------------------------------------- */
    +        if( pszGeomColumn && EQUAL(psMSField->name,pszGeomColumn))
    +        {
    +            OGRGeometry *poGeometry = NULL;
    +            
    +            // Geometry columns will have the first 4 bytes contain the SRID.
    +            OGRGeometryFactory::createFromWkb(
    +                ((GByte *)papszRow[iField]) + 4, 
    +                NULL,
    +                &poGeometry,
    +                panLengths[iField] - 4 );
    +
    +            if( poGeometry != NULL )
    +            {
    +                poGeometry->assignSpatialReference( GetSpatialRef() );
    +                poFeature->SetGeometryDirectly( poGeometry );
    +            }
    +            continue;
    +        }
    +
    +
    +/* -------------------------------------------------------------------- */
    +/*      Transfer regular data fields.                                   */
    +/* -------------------------------------------------------------------- */
    +        iOGRField = poFeatureDefn->GetFieldIndex(psMSField->name);
    +        if( iOGRField < 0 )
    +            continue;
    +
    +        OGRFieldDefn *psFieldDefn = poFeatureDefn->GetFieldDefn( iOGRField );
    +
    +        if( psFieldDefn->GetType() == OFTBinary )
    +        {
    +            poFeature->SetField( iOGRField, panLengths[iField], 
    +                                 (GByte *) papszRow[iField] );
    +        }
    +        else
    +        {
    +            poFeature->SetField( iOGRField, papszRow[iField] );
    +        }
    +    }
    +
    +    return poFeature;
    +}
    +
    +/************************************************************************/
    +/*                         GetNextRawFeature()                          */
    +/************************************************************************/
    +
    +OGRFeature *OGRMySQLLayer::GetNextRawFeature()
    +
    +{
    +/* -------------------------------------------------------------------- */
    +/*      Do we need to establish an initial query?                       */
    +/* -------------------------------------------------------------------- */
    +    if( iNextShapeId == 0 && hResultSet == NULL )
    +    {
    +        CPLAssert( pszQueryStatement != NULL );
    +
    +        poDS->RequestLongResult( this );
    +
    +        if( mysql_query( poDS->GetConn(), pszQueryStatement ) )
    +        {
    +            poDS->ReportError( pszQueryStatement );
    +            return NULL;
    +        }
    +
    +        hResultSet = mysql_use_result( poDS->GetConn() );
    +        if( hResultSet == NULL )
    +        {
    +            poDS->ReportError( "mysql_use_result() failed on query." );
    +            return FALSE;
    +        }
    +    }
    +
    +/* -------------------------------------------------------------------- */
    +/*      Fetch next record.                                              */
    +/* -------------------------------------------------------------------- */
    +    char **papszRow;
    +    unsigned long *panLengths;
    +
    +    papszRow = mysql_fetch_row( hResultSet );
    +    if( papszRow == NULL )
    +    {
    +        ResetReading();
    +        return NULL;
    +    }
    +
    +    panLengths = mysql_fetch_lengths( hResultSet );
    +
    +/* -------------------------------------------------------------------- */
    +/*      Process record.                                                 */
    +/* -------------------------------------------------------------------- */
    +    OGRFeature *poFeature = RecordToFeature( papszRow, panLengths );
    +
    +    iNextShapeId++;
    +
    +    return poFeature;
    +}
    +
    +/************************************************************************/
    +/*                             GetFeature()                             */
    +/*                                                                      */
    +/*      Note that we actually override this in OGRMySQLTableLayer.      */
    +/************************************************************************/
    +
    +OGRFeature *OGRMySQLLayer::GetFeature( GIntBig nFeatureId )
    +
    +{
    +    return OGRLayer::GetFeature( nFeatureId );
    +}
    +
    +/************************************************************************/
    +/*                            GetFIDColumn()                            */
    +/************************************************************************/
    +
    +const char *OGRMySQLLayer::GetFIDColumn() 
    +
    +{
    +    if( pszFIDColumn != NULL )
    +        return pszFIDColumn;
    +    else
    +        return "";
    +}
    +
    +/************************************************************************/
    +/*                         GetGeometryColumn()                          */
    +/************************************************************************/
    +
    +const char *OGRMySQLLayer::GetGeometryColumn() 
    +
    +{
    +    if( pszGeomColumn != NULL )
    +        return pszGeomColumn;
    +    else
    +        return "";
    +}
    +
    +
    +/************************************************************************/
    +/*                         FetchSRSId()                                 */
    +/************************************************************************/
    +
    +int OGRMySQLLayer::FetchSRSId()
    +{
    +	CPLString        osCommand;
    +    char           **papszRow;  
    +    
    +    if( hResultSet != NULL )
    +        mysql_free_result( hResultSet );
    +		hResultSet = NULL;
    +				
    +    osCommand.Printf(
    +             "SELECT srid FROM geometry_columns "
    +             "WHERE f_table_name = '%s'",
    +             pszGeomColumnTable );
    +
    +    if( !mysql_query( poDS->GetConn(), osCommand ) )
    +        hResultSet = mysql_store_result( poDS->GetConn() );
    +
    +    papszRow = NULL;
    +    if( hResultSet != NULL )
    +        papszRow = mysql_fetch_row( hResultSet );
    +        
    +
    +    if( papszRow != NULL && papszRow[0] != NULL )
    +    {
    +        nSRSId = atoi(papszRow[0]);
    +    }
    +
    +    // make sure to free our results
    +    if( hResultSet != NULL )
    +        mysql_free_result( hResultSet );
    +		hResultSet = NULL;
    +        
    +	return nSRSId;
    +}
    +
    +/************************************************************************/
    +/*                           GetSpatialRef()                            */
    +/************************************************************************/
    +
    +OGRSpatialReference *OGRMySQLLayer::GetSpatialRef()
    +
    +{
    +
    +
    +    if( poSRS == NULL && nSRSId > -1 )
    +    {
    +        poSRS = poDS->FetchSRS( nSRSId );
    +        if( poSRS != NULL )
    +            poSRS->Reference();
    +        else
    +            nSRSId = -1;
    +    }
    +
    +    return poSRS;
    +
    +}
    diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqlresultlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqlresultlayer.cpp
    new file mode 100644
    index 000000000..1037e3a5c
    --- /dev/null
    +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqlresultlayer.cpp
    @@ -0,0 +1,317 @@
    +/******************************************************************************
    + * $Id: ogrmysqlresultlayer.cpp 28375 2015-01-30 12:06:11Z rouault $
    + *
    + * Project:  OpenGIS Simple Features Reference Implementation
    + * Purpose:  Implements OGRMySQLResultLayer class.
    + * Author:   Frank Warmerdam, warmerdam@pobox.com
    + * Author:   Howard Butler, hobu@hobu.net
    + *
    + ******************************************************************************
    + * Copyright (c) 2004, Frank Warmerdam 
    + * Copyright (c) 2008-2010, Even Rouault 
    + *
    + * Permission is hereby granted, free of charge, to any person obtaining a
    + * copy of this software and associated documentation files (the "Software"),
    + * to deal in the Software without restriction, including without limitation
    + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    + * and/or sell copies of the Software, and to permit persons to whom the
    + * Software is furnished to do so, subject to the following conditions:
    + *
    + * The above copyright notice and this permission notice shall be included
    + * in all copies or substantial portions of the Software.
    + *
    + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
    + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    + * DEALINGS IN THE SOFTWARE.
    + ****************************************************************************/
    +
    +#include "cpl_conv.h"
    +#include "ogr_mysql.h"
    +
    +CPL_CVSID("$Id: ogrmysqlresultlayer.cpp 28375 2015-01-30 12:06:11Z rouault $");
    +
    +/************************************************************************/
    +/*                        OGRMySQLResultLayer()                         */
    +/************************************************************************/
    +
    +OGRMySQLResultLayer::OGRMySQLResultLayer( OGRMySQLDataSource *poDSIn, 
    +                                          const char * pszRawQueryIn,
    +                                          MYSQL_RES *hResultSetIn )
    +{
    +    poDS = poDSIn;
    +
    +    iNextShapeId = 0;
    +
    +    pszRawStatement = CPLStrdup(pszRawQueryIn);
    +
    +    hResultSet = hResultSetIn;
    +
    +    BuildFullQueryStatement();
    +
    +    poFeatureDefn = ReadResultDefinition();
    +}
    +
    +/************************************************************************/
    +/*                        ~OGRMySQLResultLayer()                        */
    +/************************************************************************/
    +
    +OGRMySQLResultLayer::~OGRMySQLResultLayer()
    +
    +{
    +    CPLFree( pszRawStatement );
    +}
    +
    +/************************************************************************/
    +/*                        ReadResultDefinition()                        */
    +/*                                                                      */
    +/*      Build a schema from the current resultset.                      */
    +/************************************************************************/
    +
    +OGRFeatureDefn *OGRMySQLResultLayer::ReadResultDefinition()
    +
    +{
    +
    +/* -------------------------------------------------------------------- */
    +/*      Parse the returned table information.                           */
    +/* -------------------------------------------------------------------- */
    +    OGRFeatureDefn *poDefn = new OGRFeatureDefn( "sql_statement" );
    +    SetDescription( poDefn->GetName() );
    +    int            iRawField;
    +
    +    poDefn->Reference();
    +    int width;
    +    int precision;
    +
    +    mysql_field_seek( hResultSet, 0 );
    +    for( iRawField = 0; 
    +         iRawField < (int) mysql_num_fields(hResultSet); 
    +         iRawField++ )
    +    {
    +        MYSQL_FIELD *psMSField = mysql_fetch_field( hResultSet );
    +        OGRFieldDefn    oField( psMSField->name, OFTString);
    +
    +        switch( psMSField->type )
    +        {
    +          case FIELD_TYPE_TINY:
    +          case FIELD_TYPE_SHORT:
    +          case FIELD_TYPE_LONG:
    +          case FIELD_TYPE_INT24:
    +          case FIELD_TYPE_LONGLONG:
    +            oField.SetType( OFTInteger );
    +            width = (int)psMSField->length;
    +            oField.SetWidth(width);
    +            poDefn->AddFieldDefn( &oField );
    +            break;
    +
    +          case FIELD_TYPE_DECIMAL:
    +#ifdef FIELD_TYPE_NEWDECIMAL
    +          case FIELD_TYPE_NEWDECIMAL:
    +#endif
    +            oField.SetType( OFTReal );
    +            
    +            // a bunch of hackery to munge the widths that MySQL gives 
    +            // us into corresponding widths and precisions for OGR
    +            precision =    (int)psMSField->decimals;
    +            width = (int)psMSField->length;
    +            if (!precision)
    +                width = width - 1;
    +            width = width - precision;
    +            
    +            oField.SetWidth(width);
    +            oField.SetPrecision(precision);
    +            poDefn->AddFieldDefn( &oField );
    +            break;
    +
    +          case FIELD_TYPE_FLOAT:
    +          case FIELD_TYPE_DOUBLE:
    +         /* MYSQL_FIELD is always reporting ->length = 22 and ->decimals = 31
    +            for double type regardless of the data it returned. In an example,
    +            the data it returned had only 5 or 6 decimal places which were
    +            exactly as entered into the database but reported the decimals
    +            as 31. */
    +         /* Assuming that a length of 22 means no particular width and 31
    +            decimals means no particular precision. */
    +            width = (int)psMSField->length;
    +            precision = (int)psMSField->decimals;
    +            oField.SetType( OFTReal );
    +            if( width != 22 )
    +                oField.SetWidth(width);
    +            if( precision != 31 )
    +                oField.SetPrecision(precision);
    +            poDefn->AddFieldDefn( &oField );
    +            break;
    +
    +          case FIELD_TYPE_DATE:
    +            oField.SetType( OFTDate );
    +            oField.SetWidth(0);
    +            poDefn->AddFieldDefn( &oField );
    +            break;
    +
    +          case FIELD_TYPE_TIME:
    +            oField.SetType( OFTTime );
    +            oField.SetWidth(0);
    +            poDefn->AddFieldDefn( &oField );
    +            break;
    +
    +          case FIELD_TYPE_TIMESTAMP:
    +          case FIELD_TYPE_DATETIME:
    +            oField.SetType( OFTDateTime );
    +            oField.SetWidth(0);
    +            poDefn->AddFieldDefn( &oField );
    +            break;
    +
    +          case FIELD_TYPE_YEAR:
    +          case FIELD_TYPE_STRING:
    +          case FIELD_TYPE_VAR_STRING:
    +            oField.SetType( OFTString );
    +            oField.SetWidth((int)psMSField->length);
    +            poDefn->AddFieldDefn( &oField );
    +            break;
    +
    +          case FIELD_TYPE_TINY_BLOB:
    +          case FIELD_TYPE_MEDIUM_BLOB:
    +          case FIELD_TYPE_LONG_BLOB:
    +          case FIELD_TYPE_BLOB:
    +            if( psMSField->charsetnr == 63 )
    +                oField.SetType( OFTBinary );
    +            else
    +                oField.SetType( OFTString );
    +            oField.SetWidth((int)psMSField->max_length);
    +            poDefn->AddFieldDefn( &oField );
    +            break;
    +                        
    +          case FIELD_TYPE_GEOMETRY:
    +            if (pszGeomColumn == NULL)
    +            {
    +                pszGeomColumnTable = CPLStrdup( psMSField->table);
    +                pszGeomColumn = CPLStrdup( psMSField->name);
    +            }
    +            break;
    +            
    +          default:
    +            // any other field we ignore. 
    +            break;
    +        }
    +        
    +        // assume a FID name first, and if it isn't there
    +        // take a field that is not null, a primary key, 
    +        // and is an integer-like field
    +        if( EQUAL(psMSField->name,"ogc_fid") )
    +        {
    +            bHasFid = TRUE;
    +            pszFIDColumn = CPLStrdup(oField.GetNameRef());
    +            continue;
    +        } else  
    +        if (IS_NOT_NULL(psMSField->flags)
    +            && IS_PRI_KEY(psMSField->flags)
    +            && 
    +                (
    +                    psMSField->type == FIELD_TYPE_TINY
    +                    || psMSField->type == FIELD_TYPE_SHORT
    +                    || psMSField->type == FIELD_TYPE_LONG
    +                    || psMSField->type == FIELD_TYPE_INT24
    +                    || psMSField->type == FIELD_TYPE_LONGLONG
    +                )
    +            )
    +        {
    +           bHasFid = TRUE;
    +           pszFIDColumn = CPLStrdup(oField.GetNameRef());
    +           continue;
    +        }
    +    }
    +
    +
    +    poDefn->SetGeomType( wkbNone );
    +
    +    if (pszGeomColumn) 
    +    {
    +        char*        pszType=NULL;
    +        CPLString    osCommand;
    +        char           **papszRow;  
    +         
    +        // set to unknown first
    +        poDefn->SetGeomType( wkbUnknown );
    +        
    +        osCommand.Printf(
    +                "SELECT type FROM geometry_columns WHERE f_table_name='%s'",
    +                pszGeomColumnTable );
    +
    +        if( hResultSet != NULL )
    +            mysql_free_result( hResultSet );
    +     		hResultSet = NULL;
    +
    +        if( !mysql_query( poDS->GetConn(), osCommand ) )
    +            hResultSet = mysql_store_result( poDS->GetConn() );
    +
    +        papszRow = NULL;
    +        if( hResultSet != NULL )
    +            papszRow = mysql_fetch_row( hResultSet );
    +
    +
    +        if( papszRow != NULL && papszRow[0] != NULL )
    +        {
    +            pszType = papszRow[0];
    +
    +            OGRwkbGeometryType nGeomType = OGRFromOGCGeomType(pszType);
    +
    +            poDefn->SetGeomType( nGeomType );
    +
    +        } 
    +
    +		nSRSId = FetchSRSId();
    +    } 
    +
    +
    +    return poDefn;
    +}
    +
    +/************************************************************************/
    +/*                      BuildFullQueryStatement()                       */
    +/************************************************************************/
    +
    +void OGRMySQLResultLayer::BuildFullQueryStatement()
    +
    +{
    +    if( pszQueryStatement != NULL )
    +    {
    +        CPLFree( pszQueryStatement );
    +        pszQueryStatement = NULL;
    +    }
    +
    +    pszQueryStatement = CPLStrdup(pszRawStatement);
    +}
    +
    +/************************************************************************/
    +/*                            ResetReading()                            */
    +/************************************************************************/
    +
    +void OGRMySQLResultLayer::ResetReading()
    +
    +{
    +    OGRMySQLLayer::ResetReading();
    +}
    +
    +/************************************************************************/
    +/*                          GetFeatureCount()                           */
    +/************************************************************************/
    +
    +GIntBig OGRMySQLResultLayer::GetFeatureCount( int bForce )
    +
    +{
    +    // I wonder if we could do anything smart here...
    +    // ... not till MySQL grows up (HB)
    +    return OGRMySQLLayer::GetFeatureCount( bForce );
    +}
    +
    +/************************************************************************/
    +/*                           TestCapability()                           */
    +/************************************************************************/
    +
    +int OGRMySQLResultLayer::TestCapability( CPL_UNUSED const char * pszCap )
    +{
    +    return FALSE;
    +}
    diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqltablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqltablelayer.cpp
    new file mode 100644
    index 000000000..1a6ce0361
    --- /dev/null
    +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/mysql/ogrmysqltablelayer.cpp
    @@ -0,0 +1,1296 @@
    +/******************************************************************************
    + * $Id: ogrmysqltablelayer.cpp 28809 2015-03-28 17:10:07Z rouault $
    + *
    + * Project:  OpenGIS Simple Features Reference Implementation
    + * Purpose:  Implements OGRMySQLTableLayer class.
    + * Author:   Frank Warmerdam, warmerdam@pobox.com
    + * Author:   Howard Butler, hobu@hobu.net
    + *
    + ******************************************************************************
    + * Copyright (c) 2004, Frank Warmerdam 
    + * Copyright (c) 2008-2013, Even Rouault 
    + *
    + * Permission is hereby granted, free of charge, to any person obtaining a
    + * copy of this software and associated documentation files (the "Software"),
    + * to deal in the Software without restriction, including without limitation
    + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    + * and/or sell copies of the Software, and to permit persons to whom the
    + * Software is furnished to do so, subject to the following conditions:
    + *
    + * The above copyright notice and this permission notice shall be included
    + * in all copies or substantial portions of the Software.
    + *
    + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
    + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    + * DEALINGS IN THE SOFTWARE.
    + ****************************************************************************/
    +
    +#include "cpl_conv.h"
    +#include "cpl_string.h"
    +#include "ogr_mysql.h"
    +
    +CPL_CVSID("$Id: ogrmysqltablelayer.cpp 28809 2015-03-28 17:10:07Z rouault $");
    +
    +/************************************************************************/
    +/*                         OGRMySQLTableLayer()                         */
    +/************************************************************************/
    +
    +OGRMySQLTableLayer::OGRMySQLTableLayer( OGRMySQLDataSource *poDSIn,
    +                                        CPL_UNUSED const char * pszTableName,
    +                                        int bUpdate, int nSRSIdIn )
    +{
    +    poDS = poDSIn;
    +
    +    pszQuery = NULL;
    +    pszWHERE = CPLStrdup( "" );
    +    pszQueryStatement = NULL;
    +
    +    bUpdateAccess = bUpdate;
    +
    +    iNextShapeId = 0;
    +
    +    nSRSId = nSRSIdIn;
    +
    +    poFeatureDefn = NULL;
    +    bLaunderColumnNames = TRUE;
    +    
    +    SetDescription( pszTableName );
    +}
    +
    +/************************************************************************/
    +/*                        ~OGRMySQLTableLayer()                         */
    +/************************************************************************/
    +
    +OGRMySQLTableLayer::~OGRMySQLTableLayer()
    +
    +{
    +    CPLFree( pszQuery );
    +    CPLFree( pszWHERE );
    +}
    +
    +
    +/************************************************************************/
    +/*                        Initialize()                                  */
    +/*                                                                      */
    +/*      Make sure we only do a ResetReading once we really have a       */
    +/*      FieldDefn.  Otherwise, we'll segfault.  After you construct     */
    +/*      the MySQLTableLayer, make sure to do pLayer->Initialize()       */
    +/************************************************************************/
    +
    +OGRErr  OGRMySQLTableLayer::Initialize(const char * pszTableName)
    +{
    +    poFeatureDefn = ReadTableDefinition( pszTableName );   
    +    if (poFeatureDefn)
    +    {
    +        ResetReading();
    +        return OGRERR_NONE;
    +    }
    +    else
    +    {
    +        return OGRERR_FAILURE;
    +    }
    +}
    +
    +/************************************************************************/
    +/*                        ReadTableDefinition()                         */
    +/*                                                                      */
    +/*      Build a schema from the named table.  Done by querying the      */
    +/*      catalog.                                                        */
    +/************************************************************************/
    +
    +OGRFeatureDefn *OGRMySQLTableLayer::ReadTableDefinition( const char *pszTable )
    +
    +{
    +    MYSQL_RES    *hResult;
    +    CPLString     osCommand;
    +    
    +/* -------------------------------------------------------------------- */
    +/*      Fire off commands to get back the schema of the table.          */
    +/* -------------------------------------------------------------------- */
    +    osCommand.Printf("DESCRIBE `%s`", pszTable );
    +    pszGeomColumnTable = CPLStrdup(pszTable);
    +    if( mysql_query( poDS->GetConn(), osCommand ) )
    +    {
    +        poDS->ReportError( "DESCRIBE Failed" );
    +        return FALSE;
    +    }
    +
    +    hResult = mysql_store_result( poDS->GetConn() );
    +    if( hResult == NULL )
    +    {
    +        poDS->ReportError( "mysql_store_result() failed on DESCRIBE result." );
    +        return FALSE;
    +    }
    +
    +/* -------------------------------------------------------------------- */
    +/*      Parse the returned table information.                           */
    +/* -------------------------------------------------------------------- */
    +    OGRFeatureDefn *poDefn = new OGRFeatureDefn( pszTable );
    +    char           **papszRow;
    +    OGRwkbGeometryType eForcedGeomType = wkbUnknown;
    +    int bGeomColumnNotNullable = FALSE;
    +
    +    poDefn->Reference();
    +
    +    while( (papszRow = mysql_fetch_row( hResult )) != NULL )
    +    {
    +        const char      *pszType;
    +        OGRFieldDefn    oField( papszRow[0], OFTString);
    +        int             nLenType;
    +
    +        pszType = papszRow[1];
    +
    +        if( pszType == NULL )
    +            continue;
    +
    +        nLenType = (int)strlen(pszType);
    +
    +        if( EQUAL(pszType,"varbinary")
    +            || (nLenType>=4 && EQUAL(pszType+nLenType-4,"blob")))
    +        {
    +            oField.SetType( OFTBinary );
    +        }
    +        else if( EQUAL(pszType,"varchar") 
    +                 || (nLenType>=4 && EQUAL(pszType+nLenType-4,"enum"))
    +                 || (nLenType>=3 && EQUAL(pszType+nLenType-3,"set")) )
    +        {
    +            oField.SetType( OFTString );
    +
    +        }
    +        else if( EQUALN(pszType,"char",4)  )
    +        {
    +            oField.SetType( OFTString );
    +            char ** papszTokens;
    +
    +            papszTokens = CSLTokenizeString2(pszType,"(),",0);
    +            if (CSLCount(papszTokens) >= 2)
    +            {
    +                /* width is the second */
    +                oField.SetWidth(atoi(papszTokens[1]));
    +            }
    +
    +            CSLDestroy( papszTokens );
    +            oField.SetType( OFTString );
    +
    +        }
    +        
    +        if(nLenType>=4 && EQUAL(pszType+nLenType-4,"text"))
    +        {
    +            oField.SetType( OFTString );            
    +        }
    +        else if( EQUALN(pszType,"varchar",6)  )
    +        {
    +            /* 
    +               pszType is usually in the form "varchar(15)" 
    +               so we'll split it up and get the width and precision
    +            */
    +            
    +            oField.SetType( OFTString );
    +            char ** papszTokens;
    +
    +            papszTokens = CSLTokenizeString2(pszType,"(),",0);
    +            if (CSLCount(papszTokens) >= 2)
    +            {
    +                /* width is the second */
    +                oField.SetWidth(atoi(papszTokens[1]));
    +            }
    +
    +            CSLDestroy( papszTokens );
    +            oField.SetType( OFTString );
    +        }
    +        else if( EQUALN(pszType,"int", 3) )
    +        {
    +            oField.SetType( OFTInteger );
    +        }
    +        else if( EQUALN(pszType,"tinyint", 7) )
    +        {
    +            oField.SetType( OFTInteger );
    +        }
    +        else if( EQUALN(pszType,"smallint", 8) )
    +        {
    +            oField.SetType( OFTInteger );
    +        }
    +        else if( EQUALN(pszType,"mediumint",9) )
    +        {
    +            oField.SetType( OFTInteger );
    +        }
    +        else if( EQUALN(pszType,"bigint",6) )
    +        {
    +            oField.SetType( OFTInteger64 );
    +        }
    +        else if( EQUALN(pszType,"decimal",7) )
    +        {
    +            /* 
    +               pszType is usually in the form "decimal(15,2)" 
    +               so we'll split it up and get the width and precision
    +            */
    +            oField.SetType( OFTReal );
    +            char ** papszTokens;
    +
    +            papszTokens = CSLTokenizeString2(pszType,"(),",0);
    +            if (CSLCount(papszTokens) >= 3)
    +            {
    +                /* width is the second and precision is the third */
    +                oField.SetWidth(atoi(papszTokens[1]));
    +                oField.SetPrecision(atoi(papszTokens[2]));
    +            }
    +            CSLDestroy( papszTokens );
    +
    +
    +        }
    +        else if( EQUALN(pszType,"float", 5) )
    +        {
    +            oField.SetType( OFTReal );
    +        }
    +        else if( EQUAL(pszType,"double") )
    +        {
    +            oField.SetType( OFTReal );
    +        }
    +        else if( EQUALN(pszType,"double",6) )
    +        {
    +            // double can also be double(15,2)
    +            // so we'll handle this case here after 
    +            // we check for just a regular double 
    +            // without a width and precision specified
    +            
    +            char ** papszTokens=NULL;
    +            papszTokens = CSLTokenizeString2(pszType,"(),",0);
    +            if (CSLCount(papszTokens) >= 3)
    +            {
    +                /* width is the second and precision is the third */
    +                oField.SetWidth(atoi(papszTokens[1]));
    +                oField.SetPrecision(atoi(papszTokens[2]));
    +            }
    +            CSLDestroy( papszTokens );  
    +
    +            oField.SetType( OFTReal );
    +        }
    +        else if( EQUAL(pszType,"decimal") )
    +        {
    +            oField.SetType( OFTReal );
    +        }
    +        else if( EQUAL(pszType, "date") )
    +        {
    +            oField.SetType( OFTDate );
    +        }
    +        else if( EQUAL(pszType, "time") )
    +        {
    +            oField.SetType( OFTTime );
    +        }
    +        else if( EQUAL(pszType, "datetime") 
    +                 || EQUAL(pszType, "timestamp") )
    +        {
    +            oField.SetType( OFTDateTime );
    +        }
    +        else if( EQUAL(pszType, "year") )  
    +        {
    +            oField.SetType( OFTString );
    +            oField.SetWidth( 10 );
    +        }
    +        else if( EQUAL(pszType, "geometry") ||
    +                 OGRFromOGCGeomType(pszType) != wkbUnknown)
    +        {
    +            if (pszGeomColumn == NULL)
    +            {
    +                pszGeomColumn = CPLStrdup(papszRow[0]);
    +                eForcedGeomType = OGRFromOGCGeomType(pszType);
    +                bGeomColumnNotNullable = ( papszRow[2] != NULL && EQUAL(papszRow[2], "NO") );
    +            }
    +            else
    +            {
    +                CPLDebug("MYSQL",
    +                         "Ignoring %s as geometry column. Another one(%s) has already been found before",
    +                         papszRow[0], pszGeomColumn);
    +            }
    +            continue;
    +        }
    +        // Is this an integer primary key field?
    +        if( !bHasFid && papszRow[3] != NULL && EQUAL(papszRow[3],"PRI") 
    +            && (oField.GetType() == OFTInteger || oField.GetType() == OFTInteger64) )
    +        {
    +            bHasFid = TRUE;
    +            pszFIDColumn = CPLStrdup(oField.GetNameRef());
    +            if( oField.GetType() == OFTInteger64 )
    +                SetMetadataItem(OLMD_FID64, "YES");
    +            continue;
    +        }
    +        
    +        // Is not nullable ?
    +        if( papszRow[2] != NULL && EQUAL(papszRow[2], "NO") )
    +            oField.SetNullable(FALSE);
    +        
    +        // Has default ?
    +        const char* pszDefault = papszRow[4];
    +        if( pszDefault != NULL )
    +        {
    +            if( !EQUAL(pszDefault, "NULL") &&
    +                !EQUALN(pszDefault, "CURRENT_", strlen("CURRENT_")) &&
    +                pszDefault[0] != '(' &&
    +                pszDefault[0] != '\'' &&
    +                CPLGetValueType(pszDefault) == CPL_VALUE_STRING )
    +            {
    +                int nYear, nMonth, nDay, nHour, nMinute;
    +                float fSecond;
    +                if( oField.GetType() == OFTDateTime &&
    +                    sscanf(pszDefault, "%d-%d-%d %d:%d:%f", &nYear, &nMonth, &nDay,
    +                                &nHour, &nMinute, &fSecond) == 6 )
    +                {
    +                    oField.SetDefault(CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%02d'",
    +                                            nYear, nMonth, nDay, nHour, nMinute, (int)(fSecond+0.5)));
    +                }
    +                else
    +                {
    +                    CPLString osDefault("'");
    +                    char* pszTmp = CPLEscapeString(pszDefault, -1, CPLES_SQL);
    +                    osDefault += pszTmp;
    +                    CPLFree(pszTmp);
    +                    osDefault += "'";
    +                    oField.SetDefault(osDefault);
    +                }
    +            }
    +            else
    +            {
    +                oField.SetDefault(pszDefault);
    +            }
    +        }
    +        
    +        poDefn->AddFieldDefn( &oField );
    +    }
    +
    +    // set to none for now... if we have a geometry column it will be set layer.
    +    poDefn->SetGeomType( wkbNone );
    +
    +    if( hResult != NULL )
    +    {
    +        mysql_free_result( hResult );
    +        hResultSet = NULL;
    +    }
    +
    +    if( bHasFid )
    +        CPLDebug( "MySQL", "table %s has FID column %s.",
    +                  pszTable, pszFIDColumn );
    +    else
    +        CPLDebug( "MySQL", 
    +                  "table %s has no FID column, FIDs will not be reliable!",
    +                  pszTable );
    +
    +    if (pszGeomColumn) 
    +    {
    +        char*        pszType=NULL;
    +        
    +        // set to unknown first
    +        poDefn->SetGeomType( wkbUnknown );
    +        
    +        osCommand = "SELECT type, coord_dimension FROM geometry_columns WHERE f_table_name='";
    +        osCommand += pszTable;
    +        osCommand += "'";
    +        
    +        hResult = NULL;
    +        if( !mysql_query( poDS->GetConn(), osCommand ) )
    +            hResult = mysql_store_result( poDS->GetConn() );
    +
    +        papszRow = NULL;
    +        if( hResult != NULL )
    +            papszRow = mysql_fetch_row( hResult );
    +
    +        if( papszRow != NULL && papszRow[0] != NULL )
    +        {
    +
    +            pszType = papszRow[0];
    +
    +            OGRwkbGeometryType nGeomType = OGRFromOGCGeomType(pszType);
    +
    +            if( papszRow[1] != NULL && atoi(papszRow[1]) == 3 )
    +                nGeomType = wkbSetZ(nGeomType);
    +
    +            poDefn->SetGeomType( nGeomType );
    +
    +        }
    +        else if (eForcedGeomType != wkbUnknown)
    +            poDefn->SetGeomType(eForcedGeomType);
    +
    +        if( bGeomColumnNotNullable )
    +            poDefn->GetGeomFieldDefn(0)->SetNullable(FALSE);
    +        
    +        if( hResult != NULL )
    +            mysql_free_result( hResult );   //Free our query results for finding type.
    +			hResult = NULL;
    +    } 
    + 
    +    // Fetch the SRID for this table now
    +    nSRSId = FetchSRSId(); 
    +    return poDefn;
    +}
    +
    +/************************************************************************/
    +/*                          SetSpatialFilter()                          */
    +/************************************************************************/
    +
    +void OGRMySQLTableLayer::SetSpatialFilter( OGRGeometry * poGeomIn )
    +
    +{
    +    if( !InstallFilter( poGeomIn ) )
    +        return;
    +
    +    BuildWhere();
    +
    +    ResetReading();
    +}
    +
    +
    +
    +/************************************************************************/
    +/*                             BuildWhere()                             */
    +/*                                                                      */
    +/*      Build the WHERE statement appropriate to the current set of     */
    +/*      criteria (spatial and attribute queries).                       */
    +/************************************************************************/
    +
    +void OGRMySQLTableLayer::BuildWhere()
    +
    +{
    +    CPLFree( pszWHERE );
    +    pszWHERE = (char*)CPLMalloc(500 + ((pszQuery) ? strlen(pszQuery) : 0));
    +    pszWHERE[0] = '\0';
    +
    +    if( m_poFilterGeom != NULL && pszGeomColumn )
    +    {
    +        char szEnvelope[400];
    +        OGREnvelope  sEnvelope;
    +        szEnvelope[0] = '\0';
    +        
    +        //POLYGON((MINX MINY, MAXX MINY, MAXX MAXY, MINX MAXY, MINX MINY))
    +        m_poFilterGeom->getEnvelope( &sEnvelope );
    +        
    +        CPLsnprintf(szEnvelope, sizeof(szEnvelope),
    +                "POLYGON((%.18g %.18g, %.18g %.18g, %.18g %.18g, %.18g %.18g, %.18g %.18g))",
    +                sEnvelope.MinX, sEnvelope.MinY,
    +                sEnvelope.MaxX, sEnvelope.MinY,
    +                sEnvelope.MaxX, sEnvelope.MaxY,
    +                sEnvelope.MinX, sEnvelope.MaxY,
    +                sEnvelope.MinX, sEnvelope.MinY);
    +
    +        sprintf( pszWHERE,
    +                 "WHERE MBRIntersects(GeomFromText('%s'), `%s`)",
    +                 szEnvelope,
    +                 pszGeomColumn);
    +
    +    }
    +
    +    if( pszQuery != NULL )
    +    {
    +        if( strlen(pszWHERE) == 0 )
    +            sprintf( pszWHERE, "WHERE %s ", pszQuery  );
    +        else
    +            sprintf( pszWHERE+strlen(pszWHERE), "&& (%s) ", pszQuery );
    +    }
    +}
    +
    +/************************************************************************/
    +/*                      BuildFullQueryStatement()                       */
    +/************************************************************************/
    +
    +void OGRMySQLTableLayer::BuildFullQueryStatement()
    +
    +{
    +    if( pszQueryStatement != NULL )
    +    {
    +        CPLFree( pszQueryStatement );
    +        pszQueryStatement = NULL;
    +    }
    +
    +    char *pszFields = BuildFields();
    +
    +    pszQueryStatement = (char *) 
    +        CPLMalloc(strlen(pszFields)+strlen(pszWHERE)
    +                  +strlen(poFeatureDefn->GetName()) + 40);
    +    sprintf( pszQueryStatement,
    +             "SELECT %s FROM `%s` %s", 
    +             pszFields, poFeatureDefn->GetName(), pszWHERE );
    +    
    +    CPLFree( pszFields );
    +}
    +
    +/************************************************************************/
    +/*                            ResetReading()                            */
    +/************************************************************************/
    +
    +void OGRMySQLTableLayer::ResetReading()
    +
    +{
    +    BuildFullQueryStatement();
    +
    +    OGRMySQLLayer::ResetReading();
    +}
    +
    +/************************************************************************/
    +/*                            BuildFields()                             */
    +/*                                                                      */
    +/*      Build list of fields to fetch, performing any required          */
    +/*      transformations (such as on geometry).                          */
    +/************************************************************************/
    +
    +char *OGRMySQLTableLayer::BuildFields()
    +
    +{
    +    int         i, nSize;
    +    char        *pszFieldList;
    +
    +    nSize = 25;
    +    if( pszGeomColumn )
    +        nSize += strlen(pszGeomColumn);
    +
    +    if( bHasFid )
    +        nSize += strlen(pszFIDColumn);
    +        
    +
    +    for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
    +        nSize += strlen(poFeatureDefn->GetFieldDefn(i)->GetNameRef()) + 6;
    +
    +    pszFieldList = (char *) CPLMalloc(nSize);
    +    pszFieldList[0] = '\0';
    +
    +    if( bHasFid && poFeatureDefn->GetFieldIndex( pszFIDColumn ) == -1 )
    +        sprintf( pszFieldList, "`%s`", pszFIDColumn );
    +
    +    if( pszGeomColumn )
    +    {
    +        if( strlen(pszFieldList) > 0 )
    +            strcat( pszFieldList, ", " );
    +
    +		/* ------------------------------------------------------------ */
    +		/*      Geometry returned from MySQL is as WKB, with the        */
    +        /*      first 4 bytes being an int that defines the SRID        */
    +        /*      and the rest being the WKB.                             */
    +		/* ------------------------------------------------------------ */            
    +        sprintf( pszFieldList+strlen(pszFieldList), 
    +                 "`%s` `%s`", pszGeomColumn, pszGeomColumn );
    +    }
    +
    +    for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
    +    {
    +        const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
    +
    +        if( strlen(pszFieldList) > 0 )
    +            strcat( pszFieldList, ", " );
    +
    +        strcat( pszFieldList, "`");
    +        strcat( pszFieldList, pszName );
    +        strcat( pszFieldList, "`");
    +    }
    +
    +    CPLAssert( (int) strlen(pszFieldList) < nSize );
    +
    +    return pszFieldList;
    +}
    +
    +/************************************************************************/
    +/*                         SetAttributeFilter()                         */
    +/************************************************************************/
    +
    +OGRErr OGRMySQLTableLayer::SetAttributeFilter( const char *pszQuery )
    +
    +{
    +    CPLFree(m_pszAttrQueryString);
    +    m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL;
    +
    +    CPLFree( this->pszQuery );
    +
    +    if( pszQuery == NULL || strlen(pszQuery) == 0 )
    +        this->pszQuery = NULL;
    +    else
    +        this->pszQuery = CPLStrdup( pszQuery );
    +
    +    BuildWhere();
    +
    +    ResetReading();
    +
    +    return OGRERR_NONE;
    +}
    +
    +/************************************************************************/
    +/*                           TestCapability()                           */
    +/************************************************************************/
    +
    +int OGRMySQLTableLayer::TestCapability( const char * pszCap )
    +
    +{
    +    if( EQUAL(pszCap,OLCRandomRead) )
    +        return bHasFid;
    +
    +    else if( EQUAL(pszCap,OLCFastFeatureCount) )
    +        return TRUE;
    +
    +    else if( EQUAL(pszCap,OLCFastSpatialFilter) )
    +        return TRUE;
    +
    +    else if( EQUAL(pszCap,OLCFastGetExtent) )
    +        return TRUE;
    +
    +    else if( EQUAL(pszCap,OLCCreateField) )
    +        return bUpdateAccess;
    +
    +    else if( EQUAL(pszCap,OLCDeleteFeature) )
    +        return bUpdateAccess;
    +
    +    else if( EQUAL(pszCap,OLCRandomWrite) )
    +        return bUpdateAccess;
    +
    +    else if( EQUAL(pszCap,OLCSequentialWrite) )
    +        return bUpdateAccess;
    +
    +    else
    +        return FALSE;
    +}
    +
    +/************************************************************************/
    +/*                             ISetFeature()                             */
    +/*                                                                      */
    +/*      SetFeature() is implemented by dropping the old copy of the     */
    +/*      feature in question (if there is one) and then creating a       */
    +/*      new one with the provided feature id.                           */
    +/************************************************************************/
    +
    +OGRErr OGRMySQLTableLayer::ISetFeature( OGRFeature *poFeature )
    +
    +{
    +    OGRErr eErr;
    +
    +    if( poFeature->GetFID() == OGRNullFID )
    +    {
    +        CPLError( CE_Failure, CPLE_AppDefined,
    +                  "FID required on features given to SetFeature()." );
    +        return OGRERR_FAILURE;
    +    }
    +
    +    eErr = DeleteFeature( poFeature->GetFID() );
    +    if( eErr != OGRERR_NONE )
    +        return eErr;
    +
    +    return CreateFeature( poFeature );
    +}
    +
    +/************************************************************************/
    +/*                           DeleteFeature()                            */
    +/************************************************************************/
    +
    +OGRErr OGRMySQLTableLayer::DeleteFeature( GIntBig nFID )
    +
    +{
    +    MYSQL_RES           *hResult=NULL;
    +    CPLString           osCommand;
    +
    +
    +/* -------------------------------------------------------------------- */
    +/*      We can only delete features if we have a well defined FID       */
    +/*      column to target.                                               */
    +/* -------------------------------------------------------------------- */
    +    if( !bHasFid )
    +    {
    +        CPLError( CE_Failure, CPLE_AppDefined,
    +                  "DeleteFeature(" CPL_FRMT_GIB ") failed.  Unable to delete features "
    +                  "in tables without\n a recognised FID column.",
    +                  nFID );
    +        return OGRERR_FAILURE;
    +
    +    }
    +
    +/* -------------------------------------------------------------------- */
    +/*      Form the statement to drop the record.                          */
    +/* -------------------------------------------------------------------- */
    +    osCommand.Printf( "DELETE FROM `%s` WHERE `%s` = " CPL_FRMT_GIB,
    +                      poFeatureDefn->GetName(), pszFIDColumn, nFID );
    +                      
    +/* -------------------------------------------------------------------- */
    +/*      Execute the delete.                                             */
    +/* -------------------------------------------------------------------- */
    +    poDS->InterruptLongResult();
    +    if( mysql_query(poDS->GetConn(), osCommand.c_str() ) ){   
    +        poDS->ReportError(  osCommand.c_str() );
    +        return OGRERR_FAILURE;   
    +    }
    +
    +    // make sure to attempt to free results of successful queries
    +    hResult = mysql_store_result( poDS->GetConn() );
    +    if( hResult != NULL )
    +        mysql_free_result( hResult );
    +    hResult = NULL;
    +    
    +    return mysql_affected_rows( poDS->GetConn() ) > 0 ? OGRERR_NONE : OGRERR_NON_EXISTING_FEATURE;
    +}
    +
    +
    +/************************************************************************/
    +/*                       ICreateFeature()                                */
    +/************************************************************************/
    +
    +OGRErr OGRMySQLTableLayer::ICreateFeature( OGRFeature *poFeature )
    +
    +{
    +    MYSQL_RES           *hResult=NULL;
    +    CPLString           osCommand;
    +    int                 i, bNeedComma = FALSE;
    +
    +/* -------------------------------------------------------------------- */
    +/*      Form the INSERT command.                                        */
    +/* -------------------------------------------------------------------- */
    +    osCommand.Printf( "INSERT INTO `%s` (", poFeatureDefn->GetName() );
    +
    +    if( poFeature->GetGeometryRef() != NULL )
    +    {
    +        osCommand = osCommand + "`" + pszGeomColumn + "` ";
    +        bNeedComma = TRUE;
    +    }
    +
    +    if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
    +    {
    +        if( bNeedComma )
    +            osCommand += ", ";
    +        
    +        osCommand = osCommand + "`" + pszFIDColumn + "` ";
    +        bNeedComma = TRUE;
    +    }
    +
    +    for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
    +    {
    +        if( !poFeature->IsFieldSet( i ) )
    +            continue;
    +
    +        if( !bNeedComma )
    +            bNeedComma = TRUE;
    +        else
    +            osCommand += ", ";
    +
    +        osCommand = osCommand + "`"
    +             + poFeatureDefn->GetFieldDefn(i)->GetNameRef() + "`";
    +    }
    +
    +    osCommand += ") VALUES (";
    +
    +    // Set the geometry 
    +    bNeedComma = poFeature->GetGeometryRef() != NULL;
    +    if( poFeature->GetGeometryRef() != NULL)
    +    {
    +        char    *pszWKT = NULL;
    +
    +        if( poFeature->GetGeometryRef() != NULL )
    +        {
    +            OGRGeometry *poGeom = (OGRGeometry *) poFeature->GetGeometryRef();
    +            
    +            poGeom->closeRings();
    +            poGeom->flattenTo2D();
    +            poGeom->exportToWkt( &pszWKT );
    +        }
    +
    +        if( pszWKT != NULL )
    +        {
    +
    +            osCommand += 
    +                CPLString().Printf(
    +                    "GeometryFromText('%s',%d) ", pszWKT, nSRSId );
    +
    +            OGRFree( pszWKT );
    +        }
    +        else
    +            osCommand += "''";
    +    }
    +
    +
    +    // Set the FID 
    +    if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL )
    +    {
    +        if( (GIntBig)(int)poFeature->GetFID() != poFeature->GetFID() &&
    +            GetMetadataItem(OLMD_FID64) == NULL )
    +        {
    +            CPLString osCommand2;
    +            osCommand2.Printf(
    +                     "ALTER TABLE `%s` MODIFY COLUMN `%s` BIGINT UNIQUE NOT NULL AUTO_INCREMENT",
    +                     poFeatureDefn->GetName(), pszFIDColumn );
    +
    +            if( mysql_query(poDS->GetConn(), osCommand2 ) )
    +            {
    +                poDS->ReportError( osCommand2 );
    +                return OGRERR_FAILURE;
    +            }
    +
    +            // make sure to attempt to free results of successful queries
    +            hResult = mysql_store_result( poDS->GetConn() );
    +            if( hResult != NULL )
    +                mysql_free_result( hResult );
    +            hResult = NULL;   
    +
    +            SetMetadataItem(OLMD_FID64, "YES");
    +        }
    +        
    +        if( bNeedComma )
    +            osCommand += ", ";
    +        osCommand += CPLString().Printf( CPL_FRMT_GIB, poFeature->GetFID() );
    +        bNeedComma = TRUE;
    +    }
    +
    +    for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ )
    +    {
    +        if( !poFeature->IsFieldSet( i ) )
    +            continue;
    +
    +        if( bNeedComma )
    +            osCommand += ", ";
    +        else
    +            bNeedComma = TRUE;
    +
    +        const char *pszStrValue = poFeature->GetFieldAsString(i);
    +
    +        if( poFeatureDefn->GetFieldDefn(i)->GetType() != OFTInteger
    +                 && poFeatureDefn->GetFieldDefn(i)->GetType() != OFTInteger64 
    +                 && poFeatureDefn->GetFieldDefn(i)->GetType() != OFTReal
    +                 && poFeatureDefn->GetFieldDefn(i)->GetType() != OFTBinary )
    +        {
    +            int         iChar;
    +
    +            //We need to quote and escape string fields. 
    +            osCommand += "'";
    +
    +            for( iChar = 0; pszStrValue[iChar] != '\0'; iChar++ )
    +            {
    +                if( poFeatureDefn->GetFieldDefn(i)->GetType() != OFTIntegerList
    +                    && poFeatureDefn->GetFieldDefn(i)->GetType() != OFTInteger64List
    +                    && poFeatureDefn->GetFieldDefn(i)->GetType() != OFTRealList
    +                    && poFeatureDefn->GetFieldDefn(i)->GetWidth() > 0
    +                    && iChar == poFeatureDefn->GetFieldDefn(i)->GetWidth() )
    +                {
    +                    CPLDebug( "MYSQL",
    +                              "Truncated %s field value, it was too long.",
    +                              poFeatureDefn->GetFieldDefn(i)->GetNameRef() );
    +                    break;
    +                }
    +
    +                if( pszStrValue[iChar] == '\\'
    +                    || pszStrValue[iChar] == '\'' )
    +                {
    +                    osCommand += '\\';
    +                    osCommand += pszStrValue[iChar];
    +                }
    +                else
    +                    osCommand += pszStrValue[iChar];
    +            }
    +
    +            osCommand += "'";
    +        }
    +        else if( poFeatureDefn->GetFieldDefn(i)->GetType() == OFTBinary )
    +        {
    +            int binaryCount = 0;
    +            GByte* binaryData = poFeature->GetFieldAsBinary(i, &binaryCount);
    +            char* pszHexValue = CPLBinaryToHex( binaryCount, binaryData );
    +
    +            osCommand += "x'";
    +            osCommand += pszHexValue;
    +            osCommand += "'";
    +
    +            CPLFree( pszHexValue );
    +        }
    +        else
    +        {
    +            osCommand += pszStrValue;
    +        }
    +
    +    }
    +
    +    osCommand += ")";
    +    
    +    //CPLDebug("MYSQL", "%s", osCommand.c_str());
    +    int nQueryResult = mysql_query(poDS->GetConn(), osCommand.c_str() );
    +    const my_ulonglong nFID = mysql_insert_id( poDS->GetConn() );
    +    
    +    if( nQueryResult ){   
    +        int eErrorCode = mysql_errno(poDS->GetConn());
    +        if (eErrorCode == 1153) {//ER_NET_PACKET_TOO_LARGE)
    +            poDS->ReportError("CreateFeature failed because the MySQL server " \
    +                              "cannot read the entire query statement.  Increase " \
    +                              "the size of statements your server will allow by " \
    +                              "altering the 'max_allowed_packet' parameter in "\
    +                              "your MySQL server configuration.");
    +        }
    +        else
    +        {
    +        CPLDebug("MYSQL","Error number %d", eErrorCode);
    +            poDS->ReportError(  osCommand.c_str() );
    +        }
    +
    +        // make sure to attempt to free results
    +        hResult = mysql_store_result( poDS->GetConn() );
    +        if( hResult != NULL )
    +            mysql_free_result( hResult );
    +        hResult = NULL;
    +            
    +        return OGRERR_FAILURE;   
    +    }
    +
    +    if( nFID > 0 ) {
    +        poFeature->SetFID( nFID );
    +    }
    +
    +    // make sure to attempt to free results of successful queries
    +    hResult = mysql_store_result( poDS->GetConn() );
    +    if( hResult != NULL )
    +        mysql_free_result( hResult );
    +    hResult = NULL;
    +    
    +    return OGRERR_NONE;
    +
    +}
    +/************************************************************************/
    +/*                            CreateField()                             */
    +/************************************************************************/
    +
    +OGRErr OGRMySQLTableLayer::CreateField( OGRFieldDefn *poFieldIn, int bApproxOK )
    +
    +{
    +
    +    MYSQL_RES           *hResult=NULL;
    +    CPLString            osCommand;
    +    
    +    char                szFieldType[256];
    +    OGRFieldDefn        oField( poFieldIn );
    +
    +/* -------------------------------------------------------------------- */
    +/*      Do we want to "launder" the column names into Postgres          */
    +/*      friendly format?                                                */
    +/* -------------------------------------------------------------------- */
    +    if( bLaunderColumnNames )
    +    {
    +        char    *pszSafeName = poDS->LaunderName( oField.GetNameRef() );
    +
    +        oField.SetName( pszSafeName );
    +        CPLFree( pszSafeName );
    +
    +    }
    +
    +/* -------------------------------------------------------------------- */
    +/*      Work out the MySQL type.                                        */
    +/* -------------------------------------------------------------------- */
    +    if( oField.GetType() == OFTInteger )
    +    {
    +        if( oField.GetWidth() > 0 && bPreservePrecision )
    +            sprintf( szFieldType, "DECIMAL(%d,0)", oField.GetWidth() );
    +        else
    +            strcpy( szFieldType, "INTEGER" );
    +    }
    +    else if( oField.GetType() == OFTInteger64 )
    +    {
    +        if( oField.GetWidth() > 0 && bPreservePrecision )
    +            sprintf( szFieldType, "DECIMAL(%d,0)", oField.GetWidth() );
    +        else
    +            strcpy( szFieldType, "BIGINT" );
    +    }
    +    else if( oField.GetType() == OFTReal )
    +    {
    +        if( oField.GetWidth() > 0 && oField.GetPrecision() > 0
    +            && bPreservePrecision )
    +            sprintf( szFieldType, "DOUBLE(%d,%d)",
    +                     oField.GetWidth(), oField.GetPrecision() );
    +        else
    +            strcpy( szFieldType, "DOUBLE" );
    +    }
    +
    +    else if( oField.GetType() == OFTDate )
    +    {
    +        oField.SetDefault(NULL);
    +        sprintf( szFieldType, "DATE" );
    +    }
    +
    +    else if( oField.GetType() == OFTDateTime )
    +    {
    +        if( oField.GetDefault() != NULL && EQUAL(oField.GetDefault(), "CURRENT_TIMESTAMP") )
    +            sprintf( szFieldType, "TIMESTAMP" );
    +        else
    +            sprintf( szFieldType, "DATETIME" );
    +    }
    +
    +    else if( oField.GetType() == OFTTime )
    +    {
    +        oField.SetDefault(NULL);
    +        sprintf( szFieldType, "TIME" );
    +    }
    +
    +    else if( oField.GetType() == OFTBinary )
    +    {
    +        sprintf( szFieldType, "LONGBLOB" );
    +    }
    +
    +    else if( oField.GetType() == OFTString )
    +    {
    +        if( oField.GetWidth() == 0 || !bPreservePrecision )
    +        {
    +            if( oField.GetDefault() != NULL )
    +                strcpy( szFieldType, "VARCHAR(256)" );
    +            else
    +                strcpy( szFieldType, "TEXT" );
    +        }
    +        else
    +            sprintf( szFieldType, "VARCHAR(%d)", oField.GetWidth() );
    +    }
    +    else if( bApproxOK )
    +    {
    +        CPLError( CE_Warning, CPLE_NotSupported,
    +                  "Can't create field %s with type %s on MySQL layers.  Creating as TEXT.",
    +                  oField.GetNameRef(),
    +                  OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
    +        strcpy( szFieldType, "TEXT" );
    +        oField.SetWidth(0);
    +        oField.SetPrecision(0);
    +    }
    +    else
    +    {
    +        CPLError( CE_Failure, CPLE_NotSupported,
    +                  "Can't create field %s with type %s on MySQL layers.",
    +                  oField.GetNameRef(),
    +                  OGRFieldDefn::GetFieldTypeName(oField.GetType()) );
    +
    +        return OGRERR_FAILURE;
    +    }
    +
    +    osCommand.Printf(
    +             "ALTER TABLE `%s` ADD COLUMN `%s` %s%s",
    +             poFeatureDefn->GetName(), oField.GetNameRef(), szFieldType,
    +             (!oField.IsNullable()) ? " NOT NULL" : "");
    +    if( oField.GetDefault() != NULL && !oField.IsDefaultDriverSpecific() )
    +    {
    +        osCommand += " DEFAULT ";
    +        osCommand += oField.GetDefault();
    +    }
    +
    +    if( mysql_query(poDS->GetConn(), osCommand ) )
    +    {
    +        poDS->ReportError( osCommand );
    +        return OGRERR_FAILURE;
    +    }
    +
    +    // make sure to attempt to free results of successful queries
    +    hResult = mysql_store_result( poDS->GetConn() );
    +    if( hResult != NULL )
    +        mysql_free_result( hResult );
    +    hResult = NULL;   
    +
    +    poFeatureDefn->AddFieldDefn( &oField );    
    +    
    +    return OGRERR_NONE;
    +}
    +
    +
    +/************************************************************************/
    +/*                             GetFeature()                             */
    +/************************************************************************/
    +
    +OGRFeature *OGRMySQLTableLayer::GetFeature( GIntBig nFeatureId )
    +
    +{
    +    if( pszFIDColumn == NULL )
    +        return OGRMySQLLayer::GetFeature( nFeatureId );
    +
    +/* -------------------------------------------------------------------- */
    +/*      Discard any existing resultset.                                 */
    +/* -------------------------------------------------------------------- */
    +    ResetReading();
    +
    +/* -------------------------------------------------------------------- */
    +/*      Prepare query command that will just fetch the one record of    */
    +/*      interest.                                                       */
    +/* -------------------------------------------------------------------- */
    +    char        *pszFieldList = BuildFields();
    +    CPLString    osCommand;
    +
    +    osCommand.Printf(
    +             "SELECT %s FROM `%s` WHERE `%s` = " CPL_FRMT_GIB, 
    +             pszFieldList, poFeatureDefn->GetName(), pszFIDColumn, 
    +             nFeatureId );
    +    CPLFree( pszFieldList );
    +
    +/* -------------------------------------------------------------------- */
    +/*      Issue the command.                                              */
    +/* -------------------------------------------------------------------- */
    +    if( mysql_query( poDS->GetConn(), osCommand ) )
    +    {
    +        poDS->ReportError( osCommand );
    +        return NULL;
    +    }
    +
    +    hResultSet = mysql_store_result( poDS->GetConn() );
    +    if( hResultSet == NULL )
    +    {
    +        poDS->ReportError( "mysql_store_result() failed on query." );
    +        return NULL;
    +    }
    +
    +/* -------------------------------------------------------------------- */
    +/*      Fetch the result record.                                        */
    +/* -------------------------------------------------------------------- */
    +    char **papszRow;
    +    unsigned long *panLengths;
    +
    +    papszRow = mysql_fetch_row( hResultSet );
    +    if( papszRow == NULL )
    +        return NULL;
    +
    +    panLengths = mysql_fetch_lengths( hResultSet );
    +
    +/* -------------------------------------------------------------------- */
    +/*      Transform into a feature.                                       */
    +/* -------------------------------------------------------------------- */
    +    iNextShapeId = nFeatureId;
    +
    +    OGRFeature *poFeature = RecordToFeature( papszRow, panLengths );
    +
    +    iNextShapeId = 0;
    +
    +/* -------------------------------------------------------------------- */
    +/*      Cleanup                                                         */
    +/* -------------------------------------------------------------------- */
    +    if( hResultSet != NULL )
    +        mysql_free_result( hResultSet );
    + 		hResultSet = NULL;
    +
    +    return poFeature;
    +}
    +
    +/************************************************************************/
    +/*                          GetFeatureCount()                           */
    +/*                                                                      */
    +/*      If a spatial filter is in effect, we turn control over to       */
    +/*      the generic counter.  Otherwise we return the total count.      */
    +/*      Eventually we should consider implementing a more efficient     */
    +/*      way of counting features matching a spatial query.              */
    +/************************************************************************/
    +
    +GIntBig OGRMySQLTableLayer::GetFeatureCount( CPL_UNUSED int bForce )
    +{
    +/* -------------------------------------------------------------------- */
    +/*      Ensure any active long result is interrupted.                   */
    +/* -------------------------------------------------------------------- */
    +    poDS->InterruptLongResult();
    +
    +/* -------------------------------------------------------------------- */
    +/*      Issue the appropriate select command.                           */
    +/* -------------------------------------------------------------------- */
    +    MYSQL_RES    *hResult;
    +    const char         *pszCommand;
    +
    +    pszCommand = CPLSPrintf( "SELECT COUNT(*) FROM `%s` %s", 
    +                             poFeatureDefn->GetName(), pszWHERE );
    +
    +    if( mysql_query( poDS->GetConn(), pszCommand ) )
    +    {
    +        poDS->ReportError( pszCommand );
    +        return FALSE;
    +    }
    +
    +    hResult = mysql_store_result( poDS->GetConn() );
    +    if( hResult == NULL )
    +    {
    +        poDS->ReportError( "mysql_store_result() failed on SELECT COUNT(*)." );
    +        return FALSE;
    +    }
    +    
    +/* -------------------------------------------------------------------- */
    +/*      Capture the result.                                             */
    +/* -------------------------------------------------------------------- */
    +    char **papszRow = mysql_fetch_row( hResult );
    +    GIntBig nCount = 0;
    +
    +    if( papszRow != NULL && papszRow[0] != NULL )
    +        nCount = CPLAtoGIntBig(papszRow[0]);
    +
    +    if( hResult != NULL )
    +        mysql_free_result( hResult );
    +    hResult = NULL;
    +    
    +    return nCount;
    +}
    +
    +/************************************************************************/
    +/*                          GetExtent()					*/
    +/*                                                                      */
    +/*      Retrieve the MBR of the MySQL table.  This should be made more  */
    +/*      in the future when MySQL adds support for a single MBR query    */
    +/*      like PostgreSQL.						*/
    +/************************************************************************/
    +
    +OGRErr OGRMySQLTableLayer::GetExtent(OGREnvelope *psExtent, CPL_UNUSED int bForce )
    +{
    +	if( GetLayerDefn()->GetGeomType() == wkbNone )
    +    {
    +        psExtent->MinX = 0.0;
    +        psExtent->MaxX = 0.0;
    +        psExtent->MinY = 0.0;
    +        psExtent->MaxY = 0.0;
    +
    +        return OGRERR_FAILURE;
    +    }
    +
    +	OGREnvelope oEnv;
    +	CPLString   osCommand;
    +	GBool       bExtentSet = FALSE;
    +
    +	osCommand.Printf( "SELECT Envelope(`%s`) FROM `%s`;", pszGeomColumn, pszGeomColumnTable);
    +
    +	if (mysql_query(poDS->GetConn(), osCommand) == 0)
    +	{
    +		MYSQL_RES* result = mysql_use_result(poDS->GetConn());
    +		if ( result == NULL )
    +        {
    +            poDS->ReportError( "mysql_use_result() failed on extents query." );
    +            return OGRERR_FAILURE;
    +        }
    +
    +		MYSQL_ROW row; 
    +		unsigned long *panLengths = NULL;
    +		while ((row = mysql_fetch_row(result)))
    +		{
    +			if (panLengths == NULL)
    +			{
    +				panLengths = mysql_fetch_lengths( result );
    +				if ( panLengths == NULL )
    +				{
    +					poDS->ReportError( "mysql_fetch_lengths() failed on extents query." );
    +					return OGRERR_FAILURE;
    +				}
    +			}
    +
    +			OGRGeometry *poGeometry = NULL;
    +			// Geometry columns will have the first 4 bytes contain the SRID.
    +			OGRGeometryFactory::createFromWkb(((GByte *)row[0]) + 4, 
    +											  NULL,
    +											  &poGeometry,
    +											  panLengths[0] - 4 );
    +
    +			if ( poGeometry != NULL )
    +			{
    +				if (poGeometry && !bExtentSet)
    +				{
    +					poGeometry->getEnvelope(psExtent);
    +					bExtentSet = TRUE;
    +				}
    +				else if (poGeometry)
    +				{
    +					poGeometry->getEnvelope(&oEnv);
    +					if (oEnv.MinX < psExtent->MinX) 
    +						psExtent->MinX = oEnv.MinX;
    +					if (oEnv.MinY < psExtent->MinY) 
    +						psExtent->MinY = oEnv.MinY;
    +					if (oEnv.MaxX > psExtent->MaxX) 
    +						psExtent->MaxX = oEnv.MaxX;
    +					if (oEnv.MaxY > psExtent->MaxY) 
    +						psExtent->MaxY = oEnv.MaxY;
    +				}
    +				delete poGeometry;
    +			}
    +		}
    +
    +		mysql_free_result(result);      
    +	}
    +
    +	return (bExtentSet ? OGRERR_NONE : OGRERR_FAILURE);
    +}
    diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/GNUmakefile
    new file mode 100644
    index 000000000..00e2833f2
    --- /dev/null
    +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/GNUmakefile
    @@ -0,0 +1,19 @@
    +
    +include ../../../GDALmake.opt
    +
    +OBJ =	ogrnasdriver.o ogrnasdatasource.o ogrnaslayer.o \
    +	nashandler.o nasreader.o ogrnasrelationlayer.o
    +
    +CPPFLAGS :=	-I../gml -I.. -I../.. -DHAVE_XERCES=1 \
    +		 $(XERCES_INCLUDE) $(CPPFLAGS)
    +
    +# By default, XML validation is disabled.  Uncomment the following line to
    +# enable XML schema validation in the parser.
    +#CPPFLAGS +=  -DOGR_GML_VALIDATION=1
    +
    +default:	$(O_OBJ:.o=.$(OBJ_EXT))
    +
    +clean:
    +	rm -f *.o $(O_OBJ)
    +
    +$(O_OBJ):	ogr_nas.h nasreaderp.h ../gml/gmlreader.h ../gml/gmlreaderp.h
    diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/drv_nas.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/drv_nas.html
    new file mode 100644
    index 000000000..289862757
    --- /dev/null
    +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/drv_nas.html
    @@ -0,0 +1,33 @@
    +
    +
    +NAS - Normbasierte AustauschSchnittstelle
    +
    +
    +
    +
    +

    NAS - ALKIS

    + +The NAS reader reads the NAS/ALKIS format used for cadastral data in Germany. +The format is a GML profile with fairly complex GML3 objects not easily read +with the general OGR GML driver.

    + +This driver depends on GDAL/OGR being built with the Xerces XML parsing +library.

    + +This driver was implemented within the context of the PostNAS project +which has more information on it's use.

    + +The driver looks for "opengis.net/gml" and one of the strings semicolon +separated strings listed in the option NAS_INDICATOR (which defaults to +"NAS-Operationen.xsd;NAS-Operationen_optional.xsd;AAA-Fachschema.xsd") to +determine if a input is a NAS file and ignores all files without any matches. + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/makefile.vc new file mode 100644 index 000000000..13b2310ae --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/makefile.vc @@ -0,0 +1,19 @@ + +OBJ = ogrnasdriver.obj ogrnasdatasource.obj ogrnaslayer.obj \ + nashandler.obj nasreader.obj \ + ogrnasrelationlayer.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF XERCES_DIR +EXTRAFLAGS = -I.. -I..\.. -I..\gml $(XERCES_INCLUDE) -DHAVE_XERCES=1 +!ENDIF + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/nas_schema.vrt b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/nas_schema.vrt new file mode 100644 index 000000000..572b4843a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/nas_schema.vrt @@ -0,0 +1,1305 @@ + + + @dummy@ + @dummy@ + wkbPoint + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + @dummy@ + @dummy@ + wkbLineString + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbLineString + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPoint + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + @dummy@ + @dummy@ + wkbPoint + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPoint + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + @dummy@ + @dummy@ + wkbLineString + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbLineString + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbLineString + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbLineString + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbLineString + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPoint + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPoint + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbLineString + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + @dummy@ + @dummy@ + wkbPoint + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbLineString + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPoint + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPoint + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPoint + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + @dummy@ + @dummy@ + wkbUnknown + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbLineString + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + + + + + @dummy@ + @dummy@ + wkbLineString + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + @dummy@ + @dummy@ + wkbPolygon + PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],AUTHORITY["EPSG","25832"],AXIS["Easting",EAST],AXIS["Northing",NORTH]] + + + + + + + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/nashandler.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/nashandler.cpp new file mode 100644 index 000000000..f8ddacfb4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/nashandler.cpp @@ -0,0 +1,748 @@ +/********************************************************************** + * $Id: nashandler.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: NAS Reader + * Purpose: Implementation of NASHandler class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "nasreaderp.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +#define MAX_TOKEN_SIZE 1000 + +/* + Update modes: + + + + + + + + + + + [...] + + + + + + + + adv:lebenszeitintervall/adv:AA_Lebenszeitintervall/adv:endet + 2012-08-14T12:32:30Z + + + adv:anlass + 000000 + + + + + + + +*/ + + +/************************************************************************/ +/* NASHandler() */ +/************************************************************************/ + +NASHandler::NASHandler( NASReader *poReader ) + +{ + m_poReader = poReader; + m_pszCurField = NULL; + m_pszGeometry = NULL; + m_nGeomAlloc = m_nGeomLen = 0; + m_nDepthFeature = m_nDepthElement = m_nDepth = 0; + m_bIgnoreFeature = FALSE; + m_bInUpdate = FALSE; + m_bInUpdateProperty = FALSE; +} + +/************************************************************************/ +/* ~NASHandler() */ +/************************************************************************/ + +NASHandler::~NASHandler() + +{ + CPLFree( m_pszCurField ); + CPLFree( m_pszGeometry ); +} + +/************************************************************************/ +/* GetAttributes() */ +/************************************************************************/ + +CPLString NASHandler::GetAttributes(const Attributes* attrs) +{ + CPLString osRes; + char *pszString; + + for(unsigned int i=0; i < attrs->getLength(); i++) + { + osRes += " "; + pszString = tr_strdup(attrs->getQName(i)); + osRes += pszString; + CPLFree( pszString ); + osRes += "=\""; + pszString = tr_strdup(attrs->getValue(i)); + osRes += pszString; + CPLFree( pszString ); + osRes += "\""; + } + return osRes; +} + + +/************************************************************************/ +/* startElement() */ +/************************************************************************/ + +void NASHandler::startElement(CPL_UNUSED const XMLCh* const uri, + const XMLCh* const localname, + CPL_UNUSED const XMLCh* const qname, + const Attributes& attrs ) + +{ + char szElementName[MAX_TOKEN_SIZE]; + GMLReadState *poState = m_poReader->GetState(); + const char *pszLast = NULL; + + tr_strcpy( szElementName, localname ); + + if ( ( m_bIgnoreFeature && m_nDepth >= m_nDepthFeature ) || + ( m_osIgnoredElement != "" && m_nDepth >= m_nDepthElement ) ) + { + m_nDepth ++; + return; + } + + // ignore attributes of external references and "objektkoordinaten" + // (see PostNAS #3 and #15) + if ( EQUAL( szElementName, "zeigtAufExternes" ) || + EQUAL( szElementName, "objektkoordinaten" ) ) + { + m_osIgnoredElement = szElementName; + m_nDepthElement = m_nDepth; + m_nDepth ++; + + return; + } + +#ifdef DEBUG_VERBOSE + CPLDebug("NAS", + "%*sstartElement %s m_bIgnoreFeature:%d depth:%d depthFeature:%d featureClass:%s", + m_nDepth, "", szElementName, + m_bIgnoreFeature, m_nDepth, m_nDepthFeature, + poState->m_poFeature ? poState->m_poFeature->GetClass()->GetElementName() : "(no feature)" + ); +#endif + +/* -------------------------------------------------------------------- */ +/* If we are in the midst of collecting a feature attribute */ +/* value, then this must be a complex attribute which we don't */ +/* try to collect for now, so just terminate the field */ +/* collection. */ +/* -------------------------------------------------------------------- */ + if( m_pszCurField != NULL ) + { + CPLFree( m_pszCurField ); + m_pszCurField = NULL; + } + +/* -------------------------------------------------------------------- */ +/* If we are collecting geometry, or if we determine this is a */ +/* geometry element then append to the geometry info. */ +/* -------------------------------------------------------------------- */ + if( m_pszGeometry != NULL + || IsGeometryElement( szElementName ) ) + { + int nLNLen = tr_strlen( localname ); + CPLString osAttributes = GetAttributes( &attrs ); + + /* should save attributes too! */ + + if( m_pszGeometry == NULL ) + m_nGeometryDepth = poState->m_nPathLength; + + if( m_nGeomLen + nLNLen + 4 + (int)osAttributes.size() > m_nGeomAlloc ) + { + m_nGeomAlloc = (int) (m_nGeomAlloc * 1.3 + nLNLen + osAttributes.size() + 1000); + m_pszGeometry = (char *) + CPLRealloc( m_pszGeometry, m_nGeomAlloc); + } + + strcpy( m_pszGeometry+m_nGeomLen, "<" ); + tr_strcpy( m_pszGeometry+m_nGeomLen+1, localname ); + + if( osAttributes.size() > 0 ) + { + strcat( m_pszGeometry+m_nGeomLen, " " ); + strcat( m_pszGeometry+m_nGeomLen, osAttributes ); + } + + strcat( m_pszGeometry+m_nGeomLen, ">" ); + m_nGeomLen += strlen(m_pszGeometry+m_nGeomLen); + } + +/* -------------------------------------------------------------------- */ +/* Is this the ogc:Filter element in a update operation */ +/* (wfs:Delete, wfsext:Replace or wfs:Update)? */ +/* specialized sort of feature. */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(szElementName,"Filter") + && (pszLast = m_poReader->GetState()->GetLastComponent()) != NULL + && (EQUAL(pszLast,"Delete") || EQUAL(pszLast,"Replace") || EQUAL(pszLast,"Update")) ) + { + const char* pszFilteredClassName = m_poReader->GetFilteredClassName(); + if ( pszFilteredClassName != NULL && + strcmp("Delete", pszFilteredClassName) != 0 ) + { + m_bIgnoreFeature = TRUE; + m_nDepthFeature = m_nDepth; + m_nDepth ++; + + return; + } + + m_bIgnoreFeature = FALSE; + + m_poReader->PushFeature( "Delete", attrs ); + + m_nDepthFeature = m_nDepth; + m_nDepth ++; + + CPLAssert( m_osLastTypeName != "" ); + m_poReader->SetFeaturePropertyDirectly( "typeName", CPLStrdup(m_osLastTypeName) ); + m_poReader->SetFeaturePropertyDirectly( "context", CPLStrdup(pszLast) ); + + if( EQUAL( pszLast, "Replace" ) ) + { + CPLAssert( m_osLastReplacingFID != "" ); + CPLAssert( m_osLastSafeToIgnore != "" ); + m_poReader->SetFeaturePropertyDirectly( "replacedBy", CPLStrdup(m_osLastReplacingFID) ); + m_poReader->SetFeaturePropertyDirectly( "safeToIgnore", CPLStrdup(m_osLastSafeToIgnore) ); + } + else if( EQUAL( pszLast, "Update" ) ) + { + CPLAssert( m_osLastEnded != "" ); + CPLAssert( m_osLastOccasion != "" ); + m_poReader->SetFeaturePropertyDirectly( "endet", CPLStrdup(m_osLastEnded) ); + m_poReader->SetFeaturePropertyDirectly( "anlass", CPLStrdup(m_osLastOccasion) ); + m_osLastEnded = ""; + m_osLastOccasion = ""; + } + + return; + } + +/* -------------------------------------------------------------------- */ +/* Is it a feature? If so push a whole new state, and return. */ +/* -------------------------------------------------------------------- */ + else if( m_poReader->IsFeatureElement( szElementName ) ) + { + m_osLastTypeName = szElementName; + + const char* pszFilteredClassName = m_poReader->GetFilteredClassName(); + + pszLast = m_poReader->GetState()->GetLastComponent(); + if( pszLast != NULL && EQUAL(pszLast,"Replace") ) + { + int nIndex; + XMLCh Name[100]; + + tr_strcpy( Name, "gml:id" ); + nIndex = attrs.getIndex( Name ); + + CPLAssert( nIndex!=-1 ); + CPLAssert( m_osLastReplacingFID=="" ); + + // Capture "gml:id" attribute as part of the property value - + // primarily this is for the wfsext:Replace operation's attribute. + char *pszReplacingFID = tr_strdup( attrs.getValue( nIndex ) ); + m_osLastReplacingFID = pszReplacingFID; + CPLFree( pszReplacingFID ); + +#ifdef DEBUG_VERBOSE + CPLDebug("NAS", "%*s### Replace typeName=%s replacedBy=%s", m_nDepth, "", m_osLastTypeName.c_str(), m_osLastReplacingFID.c_str() ); +#endif + } + + if ( pszFilteredClassName != NULL && + strcmp(szElementName, pszFilteredClassName) != 0 ) + { + m_bIgnoreFeature = TRUE; + m_nDepthFeature = m_nDepth; + m_nDepth ++; + + return; + } + + m_bIgnoreFeature = FALSE; + + m_poReader->PushFeature( szElementName, attrs ); + + m_nDepthFeature = m_nDepth; + m_nDepth ++; + + return; + } + +/* -------------------------------------------------------------------- */ +/* If it is the wfs:Delete or wfs:Update element, then remember */ +/* the typeName attribute so we can assign it to the feature that */ +/* will be produced when we process the Filter element. */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(szElementName,"Delete") || EQUAL(szElementName,"Update") ) + { + int nIndex; + XMLCh Name[100]; + + tr_strcpy( Name, "typeName" ); + nIndex = attrs.getIndex( Name ); + + if( nIndex != -1 ) + { + char *pszTypeName = tr_strdup( attrs.getValue( nIndex ) ); + m_osLastTypeName = pszTypeName; + CPLFree( pszTypeName ); + } + + m_osLastSafeToIgnore = ""; + m_osLastReplacingFID = ""; + + if( EQUAL(szElementName,"Update") ) + { + m_bInUpdate = TRUE; + } + } + + else if ( m_bInUpdate && EQUAL(szElementName, "Property") ) + { + m_bInUpdateProperty = TRUE; + } + + else if ( m_bInUpdateProperty && ( EQUAL(szElementName, "Name" ) || EQUAL(szElementName, "Value" ) ) ) + { + // collect attribute name or value + CPLFree( m_pszCurField ); + m_pszCurField = CPLStrdup(""); + } + +/* -------------------------------------------------------------------- */ +/* If it is the wfsext:Replace element, then remember the */ +/* safeToIgnore attribute so we can assign it to the feature */ +/* that will be produced when we process the Filter element. */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(szElementName,"Replace") ) + { + int nIndex; + XMLCh Name[100]; + + tr_strcpy( Name, "safeToIgnore" ); + nIndex = attrs.getIndex( Name ); + + if( nIndex != -1 ) + { + char *pszSafeToIgnore = tr_strdup( attrs.getValue( nIndex ) ); + m_osLastSafeToIgnore = pszSafeToIgnore; + CPLFree( pszSafeToIgnore ); + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, "NAS: safeToIgnore attribute missing" ); + m_osLastSafeToIgnore = "false"; + } + + m_osLastReplacingFID = ""; + } + +/* -------------------------------------------------------------------- */ +/* If it is (or at least potentially is) a simple attribute, */ +/* then start collecting it. */ +/* -------------------------------------------------------------------- */ + else if( m_poReader->IsAttributeElement( szElementName ) ) + { + CPLFree( m_pszCurField ); + m_pszCurField = CPLStrdup(""); + + // Capture href as OB property. + m_poReader->CheckForRelations( szElementName, attrs, &m_pszCurField ); + + // Capture "fid" attribute as part of the property value - + // primarily this is for wfs:Delete operation's FeatureId attribute. + if( EQUAL(szElementName,"FeatureId") ) + m_poReader->CheckForFID( attrs, &m_pszCurField ); + } + +/* -------------------------------------------------------------------- */ +/* Push the element onto the current state's path. */ +/* -------------------------------------------------------------------- */ + poState->PushPath( szElementName ); + + m_nDepth ++; +} + +/************************************************************************/ +/* endElement() */ +/************************************************************************/ +void NASHandler::endElement(CPL_UNUSED const XMLCh* const uri, + const XMLCh* const localname, + CPL_UNUSED const XMLCh* const qname ) + +{ + char szElementName[MAX_TOKEN_SIZE]; + GMLReadState *poState = m_poReader->GetState(); + const char *pszLast; + + tr_strcpy( szElementName, localname ); + + + m_nDepth --; + + if (m_bIgnoreFeature && m_nDepth >= m_nDepthFeature) + { + if (m_nDepth == m_nDepthFeature) + { + m_bIgnoreFeature = FALSE; + m_nDepthFeature = 0; + } + return; + } + + if ( m_osIgnoredElement != "" && m_nDepth >= m_nDepthElement ) + { + if ( m_nDepth == m_nDepthElement ) + { + m_osIgnoredElement = ""; + m_nDepthElement = 0; + } + return; + } + +#ifdef DEBUG_VERBOSE + CPLDebug("NAS", + "%*sendElement %s m_bIgnoreFeature:%d depth:%d depthFeature:%d featureClass:%s", + m_nDepth, "", szElementName, + m_bIgnoreFeature, m_nDepth, m_nDepthFeature, + poState->m_poFeature ? poState->m_poFeature->GetClass()->GetElementName() : "(no feature)" + ); +#endif + + if( m_bInUpdateProperty ) + { + if( EQUAL( szElementName, "Name" ) ) + { + CPLAssert( m_osLastPropertyName == "" ); + m_osLastPropertyName = m_pszCurField; + m_pszCurField = NULL; + } + else if( EQUAL( szElementName, "Value" ) ) + { + CPLAssert( m_osLastPropertyValue == "" ); + m_osLastPropertyValue = m_pszCurField; + m_pszCurField = NULL; + } + else if( EQUAL( szElementName, "Property" ) ) + { + if( EQUAL( m_osLastPropertyName, "adv:lebenszeitintervall/adv:AA_Lebenszeitintervall/adv:endet" ) ) + { + CPLAssert( m_osLastPropertyValue != "" ); + m_osLastEnded = m_osLastPropertyValue; + } + else if( EQUAL( m_osLastPropertyName, "adv:anlass" ) ) + { + CPLAssert( m_osLastPropertyValue != "" ); + m_osLastOccasion = m_osLastPropertyValue; + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "NAS: Expected property name or value instead of %s", + m_osLastPropertyName.c_str() ); + } + + m_osLastPropertyName = ""; + m_osLastPropertyValue = ""; + m_bInUpdateProperty = FALSE; + } + + poState->PopPath(); + + return; + } + + if ( m_bInUpdate && EQUAL( szElementName, "Update" ) ) + { + m_bInUpdate = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Is this closing off an attribute value? We assume so if */ +/* we are collecting an attribute value and got to this point. */ +/* We don't bother validating that the closing tag matches the */ +/* opening tag. */ +/* -------------------------------------------------------------------- */ + if( m_pszCurField != NULL ) + { + CPLAssert( poState->m_poFeature != NULL ); + + m_poReader->SetFeaturePropertyDirectly( poState->osPath.c_str(), m_pszCurField ); + m_pszCurField = NULL; + } + +/* -------------------------------------------------------------------- */ +/* If we are collecting Geometry than store it, and consider if */ +/* this is the end of the geometry. */ +/* -------------------------------------------------------------------- */ + if( m_pszGeometry != NULL ) + { + int nLNLen = tr_strlen( localname ); + + /* should save attributes too! */ + + if( m_nGeomLen + nLNLen + 4 > m_nGeomAlloc ) + { + m_nGeomAlloc = (int) (m_nGeomAlloc * 1.3 + nLNLen + 1000); + m_pszGeometry = (char *) + CPLRealloc( m_pszGeometry, m_nGeomAlloc); + } + + strcat( m_pszGeometry+m_nGeomLen, "" ); + m_nGeomLen += strlen(m_pszGeometry+m_nGeomLen); + + if( poState->m_nPathLength == m_nGeometryDepth+1 ) + { + if( poState->m_poFeature != NULL ) + { + CPLXMLNode* psNode = CPLParseXMLString(m_pszGeometry); + if (psNode) + { + /* workaround for common malformed gml:pos with just a + * elevation value instead of a full 3D coordinate: + * + * + * 41.394 + * + * + */ + const char *pszPos; + if( (pszPos = CPLGetXMLValue( psNode, "=Point.pos", NULL ) ) != NULL + && strstr(pszPos, " ") == NULL ) + { + CPLSetXMLValue( psNode, "pos", CPLSPrintf("0 0 %s", pszPos) ); + } + + if ( poState->m_poFeature->GetGeometryList() && + poState->m_poFeature->GetGeometryList()[0] ) + { + int iId = poState->m_poFeature->GetClass()->GetPropertyIndex( "gml_id" ); + const GMLProperty *poIdProp = poState->m_poFeature->GetProperty(iId); +#ifdef DEBUG_VERBOSE + char *pszOldGeom = CPLSerializeXMLTree( poState->m_poFeature->GetGeometryList()[0] ); + + CPLDebug("NAS", "Overwriting other geometry (%s; replace:%s; with:%s)", + poIdProp && poIdProp->nSubProperties>0 && poIdProp->papszSubProperties[0] ? poIdProp->papszSubProperties[0] : "(null)", + m_pszGeometry, + pszOldGeom + ); + + CPLFree( pszOldGeom ); +#else + CPLError( CE_Warning, CPLE_AppDefined, "NAS: Overwriting other geometry (%s)", + poIdProp && poIdProp->nSubProperties>0 && poIdProp->papszSubProperties[0] ? poIdProp->papszSubProperties[0] : "(null)" ); +#endif + } + + poState->m_poFeature->SetGeometryDirectly( psNode ); + } + else + CPLError( CE_Warning, CPLE_AppDefined, "NAS: Invalid geometry skipped" ); + } + else + CPLError( CE_Warning, CPLE_AppDefined, "NAS: Skipping geometry without feature" ); + + CPLFree( m_pszGeometry ); + m_pszGeometry = NULL; + m_nGeomAlloc = m_nGeomLen = 0; + } + } + +/* -------------------------------------------------------------------- */ +/* If we are collecting a feature, and this element tag matches */ +/* element name for the class, then we have finished the */ +/* feature, and we pop the feature read state. */ +/* -------------------------------------------------------------------- */ + if( m_nDepth == m_nDepthFeature && poState->m_poFeature != NULL + && EQUAL(szElementName, + poState->m_poFeature->GetClass()->GetElementName()) ) + { + m_nDepthFeature = 0; + m_poReader->PopState(); + } + +/* -------------------------------------------------------------------- */ +/* Ends of a wfs:Delete or wfs:Update should be triggered on the */ +/* close of the element. */ +/* -------------------------------------------------------------------- */ + else if( m_nDepth == m_nDepthFeature + && poState->m_poFeature != NULL + && EQUAL(szElementName,"Filter") + && (pszLast=poState->m_poFeature->GetClass()->GetElementName()) != NULL + && ( EQUAL(pszLast, "Delete") || EQUAL(pszLast, "Update") ) ) + { + m_nDepthFeature = 0; + m_poReader->PopState(); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise, we just pop the element off the local read states */ +/* element stack. */ +/* -------------------------------------------------------------------- */ + else + { + if( EQUAL(szElementName,poState->GetLastComponent()) ) + poState->PopPath(); + else + { + CPLAssert( FALSE ); + } + } +} + +/************************************************************************/ +/* characters() */ +/************************************************************************/ + +#if XERCES_VERSION_MAJOR >= 3 +void NASHandler::characters( const XMLCh *const chars_in, + CPL_UNUSED const XMLSize_t length ) +#else +void NASHandler::characters( const XMLCh* const chars_in, + CPL_UNUSED const unsigned int length ) +#endif +{ + const XMLCh *chars = chars_in; + + if( m_pszCurField != NULL ) + { + int nCurFieldLength = strlen(m_pszCurField); + + if (nCurFieldLength == 0) + { + // Ignore white space + while( *chars == ' ' || *chars == 10 || *chars == 13 || *chars == '\t') + chars++; + } + + char *pszTranslated = tr_strdup(chars); + + if( m_pszCurField == NULL ) + { + m_pszCurField = pszTranslated; + nCurFieldLength = strlen(m_pszCurField); + } + else + { + m_pszCurField = (char *) + CPLRealloc( m_pszCurField, + nCurFieldLength+strlen(pszTranslated)+1 ); + strcpy( m_pszCurField + nCurFieldLength, pszTranslated ); + CPLFree( pszTranslated ); + } + } + else if( m_pszGeometry != NULL ) + { + if (m_nGeomLen == 0) + { + // Ignore white space + while( *chars == ' ' || *chars == 10 || *chars == 13 || *chars == '\t') + chars++; + } + + int nCharsLen = tr_strlen(chars); + + if( m_nGeomLen + nCharsLen*4 + 4 > m_nGeomAlloc ) + { + m_nGeomAlloc = (int) (m_nGeomAlloc * 1.3 + nCharsLen*4 + 1000); + m_pszGeometry = (char *) + CPLRealloc( m_pszGeometry, m_nGeomAlloc); + } + + tr_strcpy( m_pszGeometry+m_nGeomLen, chars ); + m_nGeomLen += strlen(m_pszGeometry+m_nGeomLen); + } +} + +/************************************************************************/ +/* fatalError() */ +/************************************************************************/ + +void NASHandler::fatalError( const SAXParseException &exception) + +{ + char *pszErrorMessage; + + pszErrorMessage = tr_strdup( exception.getMessage() ); + CPLError( CE_Failure, CPLE_AppDefined, + "XML Parsing Error: %s at line %d, column %d\n", + pszErrorMessage, (int)exception.getLineNumber(), (int)exception.getColumnNumber() ); + + CPLFree( pszErrorMessage ); +} + +/************************************************************************/ +/* IsGeometryElement() */ +/************************************************************************/ + +int NASHandler::IsGeometryElement( const char *pszElement ) + +{ + return strcmp(pszElement,"Polygon") == 0 + || strcmp(pszElement,"MultiPolygon") == 0 + || strcmp(pszElement,"MultiPoint") == 0 + || strcmp(pszElement,"MultiLineString") == 0 + || strcmp(pszElement,"MultiSurface") == 0 + || strcmp(pszElement,"GeometryCollection") == 0 + || strcmp(pszElement,"Point") == 0 + || strcmp(pszElement,"Curve") == 0 + || strcmp(pszElement,"MultiCurve") == 0 + || strcmp(pszElement,"CompositeCurve") == 0 + || strcmp(pszElement,"Surface") == 0 + || strcmp(pszElement,"PolygonPatch") == 0 + || strcmp(pszElement,"LineString") == 0; +} + +// vim: set sw=4 expandtab : diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/nasreader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/nasreader.cpp new file mode 100644 index 000000000..b950c7422 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/nasreader.cpp @@ -0,0 +1,1099 @@ +/****************************************************************************** + * $Id: nasreader.cpp 29051 2015-04-29 17:18:37Z rouault $ + * + * Project: NAS Reader + * Purpose: Implementation of NASReader class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "gmlreader.h" +#include "cpl_error.h" +#include "cpl_string.h" +#include "gmlutils.h" +#include "cpl_multiproc.h" + +#define SUPPORT_GEOMETRY + +#ifdef SUPPORT_GEOMETRY +# include "ogr_geometry.h" +#endif + +/************************************************************************/ +/* ==================================================================== */ +/* With XERCES Library */ +/* ==================================================================== */ +/************************************************************************/ + +#include "nasreaderp.h" +#include "cpl_conv.h" + +CPLMutex *NASReader::hMutex = NULL; + +/************************************************************************/ +/* CreateGMLReader() */ +/************************************************************************/ + +IGMLReader *CreateNASReader() + +{ + return new NASReader(); +} + +/************************************************************************/ +/* GMLReader() */ +/************************************************************************/ + +NASReader::NASReader() + +{ + m_nClassCount = 0; + m_papoClass = NULL; + + m_bClassListLocked = FALSE; + + m_poNASHandler = NULL; + m_poSAXReader = NULL; + m_bReadStarted = FALSE; + + m_poState = NULL; + m_poCompleteFeature = NULL; + + m_pszFilename = NULL; + m_pszFilteredClassName = NULL; +} + +/************************************************************************/ +/* ~NASReader() */ +/************************************************************************/ + +NASReader::~NASReader() + +{ + ClearClasses(); + + CPLFree( m_pszFilename ); + + CleanupParser(); + + if (CSLTestBoolean(CPLGetConfigOption("NAS_XERCES_TERMINATE", "FALSE"))) + XMLPlatformUtils::Terminate(); + + CPLFree( m_pszFilteredClassName ); +} + +/************************************************************************/ +/* SetSourceFile() */ +/************************************************************************/ + +void NASReader::SetSourceFile( const char *pszFilename ) + +{ + CPLFree( m_pszFilename ); + m_pszFilename = CPLStrdup( pszFilename ); +} + +/************************************************************************/ +/* GetSourceFileName() */ +/************************************************************************/ + +const char* NASReader::GetSourceFileName() + +{ + return m_pszFilename; +} + +/************************************************************************/ +/* SetupParser() */ +/************************************************************************/ + +int NASReader::SetupParser() + +{ + { + CPLMutexHolderD(&hMutex); + static int bXercesInitialized = -1; + + if( bXercesInitialized < 0) + { + try + { + XMLPlatformUtils::Initialize(); + } + + catch (const XMLException& toCatch) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Exception initializing Xerces based GML reader.\n%s", + tr_strdup(toCatch.getMessage()) ); + bXercesInitialized = FALSE; + return FALSE; + } + bXercesInitialized = TRUE; + } + if( !bXercesInitialized ) + return FALSE; + } + + // Cleanup any old parser. + if( m_poSAXReader != NULL ) + CleanupParser(); + + // Create and initialize parser. + XMLCh* xmlUriValid = NULL; + XMLCh* xmlUriNS = NULL; + + try{ + m_poSAXReader = XMLReaderFactory::createXMLReader(); + + m_poNASHandler = new NASHandler( this ); + + m_poSAXReader->setContentHandler( m_poNASHandler ); + m_poSAXReader->setErrorHandler( m_poNASHandler ); + m_poSAXReader->setLexicalHandler( m_poNASHandler ); + m_poSAXReader->setEntityResolver( m_poNASHandler ); + m_poSAXReader->setDTDHandler( m_poNASHandler ); + + xmlUriValid = XMLString::transcode("http://xml.org/sax/features/validation"); + xmlUriNS = XMLString::transcode("http://xml.org/sax/features/namespaces"); + +#if (OGR_GML_VALIDATION) + m_poSAXReader->setFeature( xmlUriValid, true); + m_poSAXReader->setFeature( xmlUriNS, true); + + m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, true ); + m_poSAXReader->setFeature( XMLUni::fgXercesSchema, true ); + +// m_poSAXReader->setDoSchema(true); +// m_poSAXReader->setValidationSchemaFullChecking(true); +#else + m_poSAXReader->setFeature( XMLUni::fgSAX2CoreValidation, false); + +#if XERCES_VERSION_MAJOR >= 3 + m_poSAXReader->setFeature( XMLUni::fgXercesSchema, false); +#else + m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, false); +#endif + +#endif + XMLString::release( &xmlUriValid ); + XMLString::release( &xmlUriNS ); + } + catch (...) + { + XMLString::release( &xmlUriValid ); + XMLString::release( &xmlUriNS ); + + CPLError( CE_Warning, CPLE_AppDefined, + "Exception initializing Xerces based GML reader.\n" ); + return FALSE; + } + + m_bReadStarted = FALSE; + + // Push an empty state. + PushState( new GMLReadState() ); + + return TRUE; +} + +/************************************************************************/ +/* CleanupParser() */ +/************************************************************************/ + +void NASReader::CleanupParser() + +{ + if( m_poSAXReader == NULL ) + return; + + while( m_poState ) + PopState(); + + delete m_poSAXReader; + m_poSAXReader = NULL; + + delete m_poNASHandler; + m_poNASHandler = NULL; + + delete m_poCompleteFeature; + m_poCompleteFeature = NULL; + + m_bReadStarted = FALSE; +} + +/************************************************************************/ +/* NextFeature() */ +/************************************************************************/ + +GMLFeature *NASReader::NextFeature() + +{ + GMLFeature *poReturn = NULL; + + try + { + if( !m_bReadStarted ) + { + if( m_poSAXReader == NULL ) + SetupParser(); + + if( !m_poSAXReader->parseFirst( m_pszFilename, m_oToFill ) ) + return NULL; + m_bReadStarted = TRUE; + } + + while( m_poCompleteFeature == NULL + && m_poSAXReader->parseNext( m_oToFill ) ) {} + + poReturn = m_poCompleteFeature; + m_poCompleteFeature = NULL; + + } + catch (const XMLException& toCatch) + { + CPLDebug( "NAS", + "Error during NextFeature()! Message:\n%s", + tr_strdup( toCatch.getMessage() ) ); + } + + return poReturn; +} + +/************************************************************************/ +/* PushFeature() */ +/* */ +/* Create a feature based on the named element. If the */ +/* corresponding feature class doesn't exist yet, then create */ +/* it now. A new GMLReadState will be created for the feature, */ +/* and it will be placed within that state. The state is */ +/* pushed onto the readstate stack. */ +/************************************************************************/ + +void NASReader::PushFeature( const char *pszElement, + const Attributes &attrs ) + +{ + int iClass; + +/* -------------------------------------------------------------------- */ +/* Find the class of this element. */ +/* -------------------------------------------------------------------- */ + for( iClass = 0; iClass < GetClassCount(); iClass++ ) + { + if( EQUAL(pszElement,GetClass(iClass)->GetElementName()) ) + break; + } + +/* -------------------------------------------------------------------- */ +/* Create a new feature class for this element, if there is no */ +/* existing class for it. */ +/* -------------------------------------------------------------------- */ + if( iClass == GetClassCount() ) + { + CPLAssert( !IsClassListLocked() ); + + GMLFeatureClass *poNewClass = new GMLFeatureClass( pszElement ); + + iClass = AddClass( poNewClass ); + } + +/* -------------------------------------------------------------------- */ +/* Create a feature of this feature class. */ +/* -------------------------------------------------------------------- */ + GMLFeature *poFeature = new GMLFeature( GetClass( iClass ) ); + +/* -------------------------------------------------------------------- */ +/* Create and push a new read state. */ +/* -------------------------------------------------------------------- */ + GMLReadState *poState; + + poState = new GMLReadState(); + poState->m_poFeature = poFeature; + PushState( poState ); + +/* -------------------------------------------------------------------- */ +/* Check for gml:id, and if found push it as an attribute named */ +/* gml_id. */ +/* -------------------------------------------------------------------- */ + int nFIDIndex; + XMLCh anFID[100]; + + tr_strcpy( anFID, "gml:id" ); + nFIDIndex = attrs.getIndex( anFID ); + if( nFIDIndex != -1 ) + { + char *pszFID = tr_strdup( attrs.getValue( nFIDIndex ) ); + SetFeaturePropertyDirectly( "gml_id", pszFID ); + } + +} + +/************************************************************************/ +/* IsFeatureElement() */ +/* */ +/* Based on context and the element name, is this element a new */ +/* GML feature element? */ +/************************************************************************/ + +int NASReader::IsFeatureElement( const char *pszElement ) + +{ + CPLAssert( m_poState != NULL ); + + const char *pszLast = m_poState->GetLastComponent(); + int nLen = strlen(pszLast); + + // There seem to be two major NAS classes of feature identifiers + // -- either a wfs:Insert or a gml:featureMember. + + if( (nLen < 6 || !EQUAL(pszLast+nLen-6,"Insert")) + && (nLen < 13 || !EQUAL(pszLast+nLen-13,"featureMember")) + && (nLen < 7 || !EQUAL(pszLast+nLen-7,"Replace")) ) + return FALSE; + + // If the class list isn't locked, any element that is a featureMember + // will do. + if( !IsClassListLocked() ) + return TRUE; + + // otherwise, find a class with the desired element name. + for( int i = 0; i < GetClassCount(); i++ ) + { + if( EQUAL(pszElement,GetClass(i)->GetElementName()) ) + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* IsAttributeElement() */ +/************************************************************************/ + +int NASReader::IsAttributeElement( const char *pszElement ) + +{ + if( m_poState->m_poFeature == NULL ) + return FALSE; + + GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass(); + + // If the schema is not yet locked, then any simple element + // is potentially an attribute. + if( !poClass->IsSchemaLocked() ) + return TRUE; + + // Otherwise build the path to this element into a single string + // and compare against known attributes. + CPLString osElemPath; + + if( m_poState->m_nPathLength == 0 ) + osElemPath = pszElement; + else + { + osElemPath = m_poState->osPath; + osElemPath += "|"; + osElemPath += pszElement; + } + + for( int i = 0; i < poClass->GetPropertyCount(); i++ ) + if( EQUAL(poClass->GetProperty(i)->GetSrcElement(),osElemPath) ) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* PopState() */ +/************************************************************************/ + +void NASReader::PopState() + +{ + if( m_poState != NULL ) + { + if( m_poState->m_poFeature != NULL && m_poCompleteFeature == NULL ) + { + m_poCompleteFeature = m_poState->m_poFeature; + m_poState->m_poFeature = NULL; + } + + GMLReadState *poParent; + + poParent = m_poState->m_poParentState; + + delete m_poState; + m_poState = poParent; + } +} + +/************************************************************************/ +/* PushState() */ +/************************************************************************/ + +void NASReader::PushState( GMLReadState *poState ) + +{ + poState->m_poParentState = m_poState; + m_poState = poState; +} + +/************************************************************************/ +/* GetClass() */ +/************************************************************************/ + +GMLFeatureClass *NASReader::GetClass( int iClass ) const + +{ + if( iClass < 0 || iClass >= m_nClassCount ) + return NULL; + else + return m_papoClass[iClass]; +} + +/************************************************************************/ +/* GetClass() */ +/************************************************************************/ + +GMLFeatureClass *NASReader::GetClass( const char *pszName ) const + +{ + for( int iClass = 0; iClass < m_nClassCount; iClass++ ) + { + if( EQUAL(GetClass(iClass)->GetName(),pszName) ) + return GetClass(iClass); + } + + return NULL; +} + +/************************************************************************/ +/* AddClass() */ +/************************************************************************/ + +int NASReader::AddClass( GMLFeatureClass *poNewClass ) + +{ + CPLAssert( GetClass( poNewClass->GetName() ) == NULL ); + + m_nClassCount++; + m_papoClass = (GMLFeatureClass **) + CPLRealloc( m_papoClass, sizeof(void*) * m_nClassCount ); + + // keep delete the last entry + if( m_nClassCount > 1 && EQUAL( m_papoClass[m_nClassCount-2]->GetName(), "Delete" ) ) + { + m_papoClass[m_nClassCount-1] = m_papoClass[m_nClassCount-2]; + m_papoClass[m_nClassCount-2] = poNewClass; + return m_nClassCount-2; + } + else + { + m_papoClass[m_nClassCount-1] = poNewClass; + return m_nClassCount-1; + } +} + +/************************************************************************/ +/* ClearClasses() */ +/************************************************************************/ + +void NASReader::ClearClasses() + +{ + for( int i = 0; i < m_nClassCount; i++ ) + delete m_papoClass[i]; + CPLFree( m_papoClass ); + + m_nClassCount = 0; + m_papoClass = NULL; +} + +/************************************************************************/ +/* SetFeatureProperty() */ +/* */ +/* Set the property value on the current feature, adding the */ +/* property name to the GMLFeatureClass if required. */ +/* The pszValue ownership is passed to this function. */ +/************************************************************************/ + +void NASReader::SetFeaturePropertyDirectly( const char *pszElement, + char *pszValue ) + +{ + GMLFeature *poFeature = GetState()->m_poFeature; + + CPLAssert( poFeature != NULL ); + +/* -------------------------------------------------------------------- */ +/* Does this property exist in the feature class? If not, add */ +/* it. */ +/* -------------------------------------------------------------------- */ + GMLFeatureClass *poClass = poFeature->GetClass(); + int iProperty; + + for( iProperty=0; iProperty < poClass->GetPropertyCount(); iProperty++ ) + { + if( EQUAL(poClass->GetProperty( iProperty )->GetSrcElement(), + pszElement ) ) + break; + } + + if( iProperty == poClass->GetPropertyCount() ) + { + if( poClass->IsSchemaLocked() ) + { + CPLDebug("NAS", "Encountered property missing from class schema."); + CPLFree(pszValue); + return; + } + + CPLString osFieldName; + + if( strchr(pszElement,'|') == NULL ) + osFieldName = pszElement; + else + { + osFieldName = strrchr(pszElement,'|') + 1; + if( poClass->GetPropertyIndex(osFieldName) != -1 ) + osFieldName = pszElement; + } + + // Does this conflict with an existing property name? + while( poClass->GetProperty(osFieldName) != NULL ) + { + osFieldName += "_"; + } + + GMLPropertyDefn *poPDefn = new GMLPropertyDefn(osFieldName,pszElement); + + if( EQUAL(CPLGetConfigOption( "GML_FIELDTYPES", ""), "ALWAYS_STRING") ) + poPDefn->SetType( GMLPT_String ); + + poClass->AddProperty( poPDefn ); + } + + if ( GMLPropertyDefn::IsSimpleType( poClass->GetProperty( iProperty )->GetType() ) ) + { + const GMLProperty *poProp = poFeature->GetProperty(iProperty); + if ( poProp && poProp->nSubProperties > 0 ) + { + int iId = poClass->GetPropertyIndex( "gml_id" ); + const GMLProperty *poIdProp = poFeature->GetProperty(iId); + + CPLDebug("NAS", + "Overwriting existing property %s.%s of value '%s' with '%s' (gml_id: %s).", + poClass->GetName(), pszElement, + poProp->papszSubProperties[0], pszValue, + poIdProp && poIdProp->nSubProperties>0 && poIdProp->papszSubProperties[0] ? poIdProp->papszSubProperties[0] : "(null)" ); + } + } + +/* -------------------------------------------------------------------- */ +/* We want to handle specially to ensure it is zero */ +/* filled, and treated as a string depspite the numeric */ +/* content. https://trac.wheregroup.com/PostNAS/ticket/9 */ +/* -------------------------------------------------------------------- */ + if( strcmp(poClass->GetProperty(iProperty)->GetName(),"lage") == 0 ) + { + if( strlen(pszValue) < 5 ) + { + CPLString osValue = "00000"; + osValue += pszValue; + poFeature->SetPropertyDirectly( iProperty, CPLStrdup(osValue + osValue.size() - 5) ); + CPLFree(pszValue); + } + else + poFeature->SetPropertyDirectly( iProperty, pszValue ); + + if( !poClass->IsSchemaLocked() ) + { + poClass->GetProperty(iProperty)->SetWidth( 5 ); + poClass->GetProperty(iProperty)->SetType( GMLPT_String ); + } + return; + } + else if( strcmp(poClass->GetProperty(iProperty)->GetName(),"kartendarstellung") == 0 || + strcmp(poClass->GetProperty(iProperty)->GetName(),"rechtsbehelfsverfahren") == 0 ) + { + poFeature->SetPropertyDirectly( iProperty, + CPLStrdup( EQUAL( pszValue, "true" ) ? "1" : "0" ) ); + CPLFree(pszValue); + + if( !poClass->IsSchemaLocked() ) + { + poClass->GetProperty(iProperty)->SetType( GMLPT_Integer ); + } + return; + } + +/* -------------------------------------------------------------------- */ +/* Set the property */ +/* -------------------------------------------------------------------- */ + poFeature->SetPropertyDirectly( iProperty, pszValue ); + +/* -------------------------------------------------------------------- */ +/* Do we need to update the property type? */ +/* -------------------------------------------------------------------- */ + if( !poClass->IsSchemaLocked() ) + { + // Special handling for punktkennung per NAS #12 + if( strcmp(poClass->GetProperty(iProperty)->GetName(), + "punktkennung") == 0) + { + poClass->GetProperty(iProperty)->SetWidth( 15 ); + poClass->GetProperty(iProperty)->SetType( GMLPT_String ); + } + // Special handling for artDerFlurstuecksgrenze per http://trac.osgeo.org/gdal/ticket/4255 + else if( strcmp(poClass->GetProperty(iProperty)->GetName(), + "artDerFlurstuecksgrenze") == 0) + { + poClass->GetProperty(iProperty)->SetType( GMLPT_IntegerList ); + } + else + poClass->GetProperty(iProperty)->AnalysePropertyValue( + poFeature->GetProperty(iProperty)); + } +} + +/************************************************************************/ +/* LoadClasses() */ +/************************************************************************/ + +int NASReader::LoadClasses( const char *pszFile ) + +{ + // Add logic later to determine reasonable default schema file. + if( pszFile == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Load the raw XML file. */ +/* -------------------------------------------------------------------- */ + FILE *fp; + int nLength; + char *pszWholeText; + + fp = VSIFOpen( pszFile, "rb" ); + + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open file %s.", pszFile ); + return FALSE; + } + + VSIFSeek( fp, 0, SEEK_END ); + nLength = VSIFTell( fp ); + VSIFSeek( fp, 0, SEEK_SET ); + + pszWholeText = (char *) VSIMalloc(nLength+1); + if( pszWholeText == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to allocate %d byte buffer for %s,\n" + "is this really a GMLFeatureClassList file?", + nLength, pszFile ); + VSIFClose( fp ); + return FALSE; + } + + if( VSIFRead( pszWholeText, nLength, 1, fp ) != 1 ) + { + VSIFree( pszWholeText ); + VSIFClose( fp ); + CPLError( CE_Failure, CPLE_AppDefined, + "Read failed on %s.", pszFile ); + return FALSE; + } + pszWholeText[nLength] = '\0'; + + VSIFClose( fp ); + + if( strstr( pszWholeText, "" ) == NULL ) + { + VSIFree( pszWholeText ); + CPLError( CE_Failure, CPLE_AppDefined, + "File %s does not contain a GMLFeatureClassList tree.", + pszFile ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Convert to XML parse tree. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRoot; + + psRoot = CPLParseXMLString( pszWholeText ); + VSIFree( pszWholeText ); + + // We assume parser will report errors via CPL. + if( psRoot == NULL ) + return FALSE; + + if( psRoot->eType != CXT_Element + || !EQUAL(psRoot->pszValue,"GMLFeatureClassList") ) + { + CPLDestroyXMLNode(psRoot); + CPLError( CE_Failure, CPLE_AppDefined, + "File %s is not a GMLFeatureClassList document.", + pszFile ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Extract feature classes for all definitions found. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psThis; + + for( psThis = psRoot->psChild; psThis != NULL; psThis = psThis->psNext ) + { + if( psThis->eType == CXT_Element + && EQUAL(psThis->pszValue,"GMLFeatureClass") ) + { + GMLFeatureClass *poClass; + + poClass = new GMLFeatureClass(); + + if( !poClass->InitializeFromXML( psThis ) ) + { + delete poClass; + CPLDestroyXMLNode( psRoot ); + return FALSE; + } + + poClass->SetSchemaLocked( TRUE ); + + AddClass( poClass ); + } + } + + CPLDestroyXMLNode( psRoot ); + + SetClassListLocked( TRUE ); + + return TRUE; +} + +/************************************************************************/ +/* SaveClasses() */ +/************************************************************************/ + +int NASReader::SaveClasses( const char *pszFile ) + +{ + // Add logic later to determine reasonable default schema file. + if( pszFile == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Create in memory schema tree. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRoot; + + psRoot = CPLCreateXMLNode( NULL, CXT_Element, "GMLFeatureClassList" ); + + for( int iClass = 0; iClass < GetClassCount(); iClass++ ) + { + GMLFeatureClass *poClass = GetClass( iClass ); + + CPLAddXMLChild( psRoot, poClass->SerializeToXML() ); + } + +/* -------------------------------------------------------------------- */ +/* Serialize to disk. */ +/* -------------------------------------------------------------------- */ + FILE *fp; + int bSuccess = TRUE; + char *pszWholeText = CPLSerializeXMLTree( psRoot ); + + CPLDestroyXMLNode( psRoot ); + + fp = VSIFOpen( pszFile, "wb" ); + + if( fp == NULL ) + bSuccess = FALSE; + else if( VSIFWrite( pszWholeText, strlen(pszWholeText), 1, fp ) != 1 ) + bSuccess = FALSE; + else + VSIFClose( fp ); + + CPLFree( pszWholeText ); + + return bSuccess; +} + +/************************************************************************/ +/* PrescanForSchema() */ +/* */ +/* For now we use a pretty dumb approach of just doing a normal */ +/* scan of the whole file, building up the schema information. */ +/* Eventually we hope to do a more efficient scan when just */ +/* looking for schema information. */ +/************************************************************************/ + +int NASReader::PrescanForSchema( int bGetExtents, + CPL_UNUSED int bAnalyzeSRSPerFeature, + CPL_UNUSED int bOnlyDetectSRS ) +{ + GMLFeature *poFeature; + + if( m_pszFilename == NULL ) + return FALSE; + + SetClassListLocked( FALSE ); + + ClearClasses(); + if( !SetupParser() ) + return FALSE; + + std::string osWork; + + while( (poFeature = NextFeature()) != NULL ) + { + GMLFeatureClass *poClass = poFeature->GetClass(); + + if( poClass->GetFeatureCount() == -1 ) + poClass->SetFeatureCount( 1 ); + else + poClass->SetFeatureCount( poClass->GetFeatureCount() + 1 ); + +#ifdef SUPPORT_GEOMETRY + if( bGetExtents ) + { + OGRGeometry *poGeometry = NULL; + + const CPLXMLNode* const * papsGeometry = poFeature->GetGeometryList(); + if( papsGeometry[0] != NULL ) + { + poGeometry = (OGRGeometry*) OGR_G_CreateFromGMLTree(papsGeometry[0]); + poGeometry = ConvertGeometry(poGeometry); + } + + if( poGeometry != NULL ) + { + double dfXMin, dfXMax, dfYMin, dfYMax; + OGREnvelope sEnvelope; + + if( poClass->GetGeometryPropertyCount() == 0 ) + poClass->AddGeometryProperty( new GMLGeometryPropertyDefn( "", "", wkbUnknown, -1, TRUE ) ); + + OGRwkbGeometryType eGType = (OGRwkbGeometryType) + poClass->GetGeometryProperty(0)->GetType(); + + // Merge SRSName into layer. + const char* pszSRSName = GML_ExtractSrsNameFromGeometry(papsGeometry, osWork, FALSE); +// if (pszSRSName != NULL) +// m_bCanUseGlobalSRSName = FALSE; + poClass->MergeSRSName(pszSRSName); + + // Merge geometry type into layer. + if( poClass->GetFeatureCount() == 1 && eGType == wkbUnknown ) + eGType = wkbNone; + + poClass->GetGeometryProperty(0)->SetType( + (int) OGRMergeGeometryTypesEx( + eGType, poGeometry->getGeometryType(), TRUE ) ); + + // merge extents. + poGeometry->getEnvelope( &sEnvelope ); + delete poGeometry; + if( poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax) ) + { + dfXMin = MIN(dfXMin,sEnvelope.MinX); + dfXMax = MAX(dfXMax,sEnvelope.MaxX); + dfYMin = MIN(dfYMin,sEnvelope.MinY); + dfYMax = MAX(dfYMax,sEnvelope.MaxY); + } + else + { + dfXMin = sEnvelope.MinX; + dfXMax = sEnvelope.MaxX; + dfYMin = sEnvelope.MinY; + dfYMax = sEnvelope.MaxY; + } + + poClass->SetExtents( dfXMin, dfXMax, dfYMin, dfYMax ); + } + else + { + if( poClass->GetGeometryPropertyCount() == 1 && + poClass->GetGeometryProperty(0)->GetType() == (int) wkbUnknown + && poClass->GetFeatureCount() == 1 ) + { + poClass->ClearGeometryProperties(); + } + } +#endif /* def SUPPORT_GEOMETRY */ + } + + delete poFeature; + } + + CleanupParser(); + + return GetClassCount() > 0; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void NASReader::ResetReading() + +{ + CleanupParser(); + SetFilteredClassName(NULL); +} + +/************************************************************************/ +/* CheckForFID() */ +/* */ +/* Merge the fid attribute into the current field text. */ +/************************************************************************/ + +void NASReader::CheckForFID( const Attributes &attrs, + char **ppszCurField ) + +{ + int nIndex; + XMLCh Name[100]; + + tr_strcpy( Name, "fid" ); + nIndex = attrs.getIndex( Name ); + + if( nIndex != -1 ) + { + char *pszFID = tr_strdup( attrs.getValue( nIndex ) ); + CPLString osCurField = *ppszCurField; + + osCurField += pszFID; + CPLFree( pszFID ); + + CPLFree( *ppszCurField ); + *ppszCurField = CPLStrdup(osCurField); + } +} + +/************************************************************************/ +/* CheckForRelations() */ +/************************************************************************/ + +void NASReader::CheckForRelations( const char *pszElement, + const Attributes &attrs, + char **ppszCurField ) + +{ + GMLFeature *poFeature = GetState()->m_poFeature; + + CPLAssert( poFeature != NULL ); + + int nIndex; + XMLCh Name[100]; + + tr_strcpy( Name, "xlink:href" ); + nIndex = attrs.getIndex( Name ); + + if( nIndex != -1 ) + { + char *pszHRef = tr_strdup( attrs.getValue( nIndex ) ); + + if( EQUALN(pszHRef,"urn:adv:oid:", 12 ) ) + { + poFeature->AddOBProperty( pszElement, pszHRef ); + CPLFree( *ppszCurField ); + *ppszCurField = CPLStrdup( pszHRef + 12 ); + } + + CPLFree( pszHRef ); + } +} + +/************************************************************************/ +/* HugeFileResolver() */ +/* Returns TRUE for success */ +/************************************************************************/ + +int NASReader::HugeFileResolver( CPL_UNUSED const char *pszFile, + CPL_UNUSED int bSqliteIsTempFile, + CPL_UNUSED int iSqliteCacheMB ) +{ + CPLDebug( "NAS", "HugeFileResolver() not currently implemented for NAS." ); + return FALSE; +} + +/************************************************************************/ +/* PrescanForTemplate() */ +/* Returns TRUE for success */ +/************************************************************************/ + +int NASReader::PrescanForTemplate( void ) + +{ + CPLDebug( "NAS", "PrescanForTemplate() not currently implemented for NAS." ); + return FALSE; +} + +/************************************************************************/ +/* ResolveXlinks() */ +/* Returns TRUE for success */ +/************************************************************************/ + +int NASReader::ResolveXlinks( CPL_UNUSED const char *pszFile, + CPL_UNUSED int* pbOutIsTempFile, + CPL_UNUSED char **papszSkip, + CPL_UNUSED const int bStrict ) +{ + CPLDebug( "NAS", "ResolveXlinks() not currently implemented for NAS." ); + return FALSE; +} + +/************************************************************************/ +/* SetFilteredClassName() */ +/************************************************************************/ + +int NASReader::SetFilteredClassName(const char* pszClassName) +{ + CPLFree(m_pszFilteredClassName); + m_pszFilteredClassName = (pszClassName) ? CPLStrdup(pszClassName) : NULL; + return TRUE; +} + +/************************************************************************/ +/* ConvertGeometry() */ +/************************************************************************/ + +OGRGeometry* NASReader::ConvertGeometry(OGRGeometry* poGeom) +{ + //poGeom = OGRGeometryFactory::forceToLineString( poGeom, false ); + if( poGeom != NULL ) + { + if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ) + { + poGeom = OGRGeometryFactory::forceTo(poGeom, wkbLineString); + } + } + return poGeom; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/nasreaderp.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/nasreaderp.h new file mode 100644 index 000000000..a577920c8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/nasreaderp.h @@ -0,0 +1,242 @@ +/****************************************************************************** + * $Id: nasreaderp.h 29051 2015-04-29 17:18:37Z rouault $ + * + * Project: NAS Reader + * Purpose: Private Declarations for OGR NAS Reader code. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_NASREADERP_H_INCLUDED +#define _CPL_NASREADERP_H_INCLUDED + +#include "gmlreader.h" +#include "gmlreaderp.h" +#include "ogr_api.h" +#include "ogr_geometry.h" +#include "cpl_string.h" + +IGMLReader *CreateNASReader(); + +class NASReader; +class OGRNASRelationLayer; + +CPL_C_START +OGRGeometryH OGR_G_CreateFromGML3( const char *pszGML ); +CPL_C_END + +/************************************************************************/ +/* NASHandler */ +/************************************************************************/ +class NASHandler : public DefaultHandler +{ + NASReader *m_poReader; + + char *m_pszCurField; + + char *m_pszGeometry; + int m_nGeomAlloc; + int m_nGeomLen; + + int m_nGeometryDepth; + int IsGeometryElement( const char * ); + + int m_nDepth; + int m_nDepthFeature; + int m_bIgnoreFeature; + int m_bInUpdate; + int m_bInUpdateProperty; + int m_nDepthElement; + CPLString m_osIgnoredElement; + + CPLString m_osLastTypeName; + CPLString m_osLastReplacingFID; + CPLString m_osLastSafeToIgnore; + CPLString m_osLastPropertyName; + CPLString m_osLastPropertyValue; + CPLString m_osLastEnded; + CPLString m_osLastOccasion; + +public: + NASHandler( NASReader *poReader ); + virtual ~NASHandler(); + + void startElement( + const XMLCh* const uri, + const XMLCh* const localname, + const XMLCh* const qname, + const Attributes& attrs + ); + void endElement( + const XMLCh* const uri, + const XMLCh* const localname, + const XMLCh* const qname + ); +#if XERCES_VERSION_MAJOR >= 3 + void characters( const XMLCh *const chars, + const XMLSize_t length ); +#else + void characters( const XMLCh *const chars, + const unsigned int length ); +#endif + + void fatalError(const SAXParseException&); + + CPLString GetAttributes( const Attributes* attr ); +}; + +/************************************************************************/ +/* GMLReadState */ +/************************************************************************/ + +// for now, use existing gmlreadstate. +#ifdef notdef +class GMLReadState +{ + void RebuildPath(); + +public: + GMLReadState(); + ~GMLReadState(); + + void PushPath( const char *pszElement ); + void PopPath(); + + int MatchPath( const char *pszPathInput ); + const char *GetPath() const { return m_pszPath; } + const char *GetLastComponent() const; + + GMLFeature *m_poFeature; + GMLReadState *m_poParentState; + + char *m_pszPath; // element path ... | as separator. + + int m_nPathLength; + char **m_papszPathComponents; +}; +#endif + +/************************************************************************/ +/* NASReader */ +/************************************************************************/ + +class NASReader : public IGMLReader +{ +private: + int m_bClassListLocked; + + int m_nClassCount; + GMLFeatureClass **m_papoClass; + + char *m_pszFilename; + + NASHandler *m_poNASHandler; + SAX2XMLReader *m_poSAXReader; + int m_bReadStarted; + XMLPScanToken m_oToFill; + + GMLReadState *m_poState; + + GMLFeature *m_poCompleteFeature; + + int SetupParser(); + void CleanupParser(); + + char *m_pszFilteredClassName; + +public: + NASReader(); + virtual ~NASReader(); + + int IsClassListLocked() const { return m_bClassListLocked; } + void SetClassListLocked( int bFlag ) + { m_bClassListLocked = bFlag; } + + void SetSourceFile( const char *pszFilename ); + const char *GetSourceFileName(); + + int GetClassCount() const { return m_nClassCount; } + GMLFeatureClass *GetClass( int i ) const; + GMLFeatureClass *GetClass( const char *pszName ) const; + + int AddClass( GMLFeatureClass *poClass ); + void ClearClasses(); + + GMLFeature *NextFeature(); + + int LoadClasses( const char *pszFile = NULL ); + int SaveClasses( const char *pszFile = NULL ); + + int PrescanForSchema(int bGetExtents = TRUE, + int bAnalyzeSRSPerFeature = TRUE, + int bOnlyDetectSRS = FALSE); + int PrescanForTemplate( void ); + void ResetReading(); + + int ParseXSD( CPL_UNUSED const char *pszFile ) { return FALSE; } + + int ResolveXlinks( const char *pszFile, + int* pbOutIsTempFile, + char **papszSkip = NULL, + const int bStrict = FALSE ); + + int HugeFileResolver( const char *pszFile, + int bSqliteIsTempFile, + int iSqliteCacheMB ); + +// --- + + GMLReadState *GetState() const { return m_poState; } + void PopState(); + void PushState( GMLReadState * ); + + int IsFeatureElement( const char *pszElement ); + int IsAttributeElement( const char *pszElement ); + + void PushFeature( const char *pszElement, + const Attributes &attrs ); + + void SetFeaturePropertyDirectly( const char *pszElement, + char *pszValue ); + + int HasStoppedParsing() { return FALSE; } + + void CheckForFID( const Attributes &attrs, char **ppszCurField ); + void CheckForRelations( const char *pszElement, + const Attributes &attrs, + char **ppszCurField ); + + virtual const char* GetGlobalSRSName() { return NULL; } + + virtual int CanUseGlobalSRSName() { return FALSE; } + + int SetFilteredClassName(const char* pszClassName); + const char* GetFilteredClassName() { return m_pszFilteredClassName; } + + static CPLMutex* hMutex; + + static OGRGeometry* ConvertGeometry(OGRGeometry*); +}; + +#endif /* _CPL_NASREADERP_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogr_nas.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogr_nas.h new file mode 100644 index 000000000..8762ad1d6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogr_nas.h @@ -0,0 +1,147 @@ +/****************************************************************************** + * $Id: ogr_nas.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: NAS Reader + * Purpose: Declarations for OGR wrapper classes for NAS, and NAS<->OGR + * translation of geometry. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_NAS_H_INCLUDED +#define _OGR_NAS_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "nasreaderp.h" +#include "ogr_api.h" +#include + +class OGRNASDataSource; + +/************************************************************************/ +/* OGRNASLayer */ +/************************************************************************/ + +class OGRNASLayer : public OGRLayer +{ + OGRSpatialReference *poSRS; + OGRFeatureDefn *poFeatureDefn; + + int iNextNASId; + int nTotalNASCount; + + OGRNASDataSource *poDS; + + GMLFeatureClass *poFClass; + + public: + OGRNASLayer( const char * pszName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + OGRNASDataSource *poDS ); + + ~OGRNASLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + GIntBig GetFeatureCount( int bForce = TRUE ); + OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRNASRelationLayer */ +/************************************************************************/ + +class OGRNASRelationLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + OGRNASDataSource *poDS; + + int bPopulated; + int iNextFeature; + std::vector aoRelationCollection; + + public: + OGRNASRelationLayer( OGRNASDataSource *poDS ); + ~OGRNASRelationLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + GIntBig GetFeatureCount( int bForce = TRUE ); + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + int TestCapability( const char * ); + + // For use populating. + void AddRelation( const char *pszFromID, + const char *pszType, + const char *pszToID ); + void MarkRelationsPopulated() { bPopulated = TRUE; } +}; + +/************************************************************************/ +/* OGRNASDataSource */ +/************************************************************************/ + +class OGRNASDataSource : public OGRDataSource +{ + OGRLayer **papoLayers; + int nLayers; + + OGRNASRelationLayer *poRelationLayer; + + char *pszName; + + OGRNASLayer *TranslateNASSchema( GMLFeatureClass * ); + + // input related parameters. + IGMLReader *poReader; + + void InsertHeader(); + + public: + OGRNASDataSource(); + ~OGRNASDataSource(); + + int Open( const char * ); + int Create( const char *pszFile, char **papszOptions ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); + + IGMLReader *GetReader() { return poReader; } + + void GrowExtents( OGREnvelope *psGeomBounds ); + + void PopulateRelations(); +}; + +#endif /* _OGR_NAS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogrnasdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogrnasdatasource.cpp new file mode 100644 index 000000000..a90658015 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogrnasdatasource.cpp @@ -0,0 +1,356 @@ +/****************************************************************************** + * $Id: ogrnasdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OGR + * Purpose: Implements OGRNASDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_nas.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrnasdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +static const char *apszURNNames[] = +{ + "DE_DHDN_3GK2_*", "EPSG:31466", + "DE_DHDN_3GK3_*", "EPSG:31467", + "ETRS89_UTM32", "EPSG:25832", + "ETRS89_UTM33", "EPSG:25833", + NULL, NULL +}; + +/************************************************************************/ +/* OGRNASDataSource() */ +/************************************************************************/ + +OGRNASDataSource::OGRNASDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + + poReader = NULL; +} + +/************************************************************************/ +/* ~OGRNASDataSource() */ +/************************************************************************/ + +OGRNASDataSource::~OGRNASDataSource() + +{ + CPLFree( pszName ); + + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + if( poReader ) + delete poReader; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRNASDataSource::Open( const char * pszNewName ) + +{ + poReader = CreateNASReader(); + if( poReader == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "File %s appears to be NAS but the NAS reader can't\n" + "be instantiated, likely because Xerces support wasn't\n" + "configured in.", + pszNewName ); + return FALSE; + } + + poReader->SetSourceFile( pszNewName ); + + pszName = CPLStrdup( pszNewName ); + +/* -------------------------------------------------------------------- */ +/* Can we find a NAS Feature Schema (.gfs) for the input file? */ +/* -------------------------------------------------------------------- */ + const char *pszGFSFilename; + VSIStatBuf sGFSStatBuf, sNASStatBuf; + int bHaveSchema = FALSE; + + pszGFSFilename = CPLResetExtension( pszNewName, "gfs" ); + if( CPLStat( pszGFSFilename, &sGFSStatBuf ) == 0 ) + { + CPLStat( pszNewName, &sNASStatBuf ); + + if( sNASStatBuf.st_mtime > sGFSStatBuf.st_mtime ) + { + CPLDebug( "NAS", + "Found %s but ignoring because it appears\n" + "be older than the associated NAS file.", + pszGFSFilename ); + } + else + { + bHaveSchema = poReader->LoadClasses( pszGFSFilename ); + } + } + +/* -------------------------------------------------------------------- */ +/* Force a first pass to establish the schema. Eventually we */ +/* will have mechanisms for remembering the schema and related */ +/* information. */ +/* -------------------------------------------------------------------- */ + CPLErrorReset(); + if( !bHaveSchema + && !poReader->PrescanForSchema( TRUE ) + && CPLGetLastErrorType() == CE_Failure ) + { + // we assume an errors have been reported. + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Save the schema file if possible. Don't make a fuss if we */ +/* can't ... could be read-only directory or something. */ +/* -------------------------------------------------------------------- */ + if( !bHaveSchema && poReader->GetClassCount() > 0 ) + { + FILE *fp = NULL; + + pszGFSFilename = CPLResetExtension( pszNewName, "gfs" ); + if( CPLStat( pszGFSFilename, &sGFSStatBuf ) != 0 + && (fp = VSIFOpen( pszGFSFilename, "wt" )) != NULL ) + { + VSIFClose( fp ); + poReader->SaveClasses( pszGFSFilename ); + } + else + { + CPLDebug("NAS", + "Not saving %s files already exists or can't be created.", + pszGFSFilename ); + } + } + +/* -------------------------------------------------------------------- */ +/* Translate the NASFeatureClasses into layers. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRLayer **) + CPLCalloc( sizeof(OGRNASLayer *), poReader->GetClassCount()+1 ); + nLayers = 0; + + while( nLayers < poReader->GetClassCount() ) + { + papoLayers[nLayers] = TranslateNASSchema(poReader->GetClass(nLayers)); + nLayers++; + } + + poRelationLayer = new OGRNASRelationLayer( this ); + + // keep delete the last layer + if( nLayers>0 && EQUAL( papoLayers[nLayers-1]->GetName(), "Delete" ) ) + { + papoLayers[nLayers] = papoLayers[nLayers-1]; + papoLayers[nLayers-1] = poRelationLayer; + } + else + { + papoLayers[nLayers] = poRelationLayer; + } + + nLayers++; + + return TRUE; +} + +/************************************************************************/ +/* TranslateNASSchema() */ +/************************************************************************/ + +OGRNASLayer *OGRNASDataSource::TranslateNASSchema( GMLFeatureClass *poClass ) + +{ + OGRNASLayer *poLayer; + OGRwkbGeometryType eGType = wkbNone; + + if( poClass->GetGeometryPropertyCount() != 0 ) + { + eGType = (OGRwkbGeometryType) poClass->GetGeometryProperty(0)->GetType(); + + if( poClass->GetFeatureCount() == 0 ) + eGType = wkbUnknown; + } + +/* -------------------------------------------------------------------- */ +/* Translate SRS. */ +/* -------------------------------------------------------------------- */ + const char* pszSRSName = poClass->GetSRSName(); + OGRSpatialReference* poSRS = NULL; + if (pszSRSName) + { + int i; + + poSRS = new OGRSpatialReference(); + + const char *pszHandle = strrchr( pszSRSName, ':' ); + if( pszHandle != NULL ) + pszHandle += 1; + + for( i = 0; apszURNNames[i*2+0] != NULL; i++ ) + { + const char *pszTarget = apszURNNames[i*2+0]; + int nTLen = strlen(pszTarget); + + // Are we just looking for a prefix match? + if( pszTarget[nTLen-1] == '*' ) + { + if( EQUALN(pszTarget,pszHandle,nTLen-1) ) + pszSRSName = apszURNNames[i*2+1]; + } + else + { + if( EQUAL(pszTarget,pszHandle) ) + pszSRSName = apszURNNames[i*2+1]; + } + } + + if (poSRS->SetFromUserInput(pszSRSName) != OGRERR_NONE) + { + CPLDebug( "NAS", "Failed to translate srsName='%s'", + pszSRSName ); + delete poSRS; + poSRS = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Create an empty layer. */ +/* -------------------------------------------------------------------- */ + poLayer = new OGRNASLayer( poClass->GetName(), poSRS, eGType, this ); + delete poSRS; + +/* -------------------------------------------------------------------- */ +/* Added attributes (properties). */ +/* -------------------------------------------------------------------- */ + for( int iField = 0; iField < poClass->GetPropertyCount(); iField++ ) + { + GMLPropertyDefn *poProperty = poClass->GetProperty( iField ); + OGRFieldType eFType; + + if( poProperty->GetType() == GMLPT_Untyped ) + eFType = OFTString; + else if( poProperty->GetType() == GMLPT_String ) + eFType = OFTString; + else if( poProperty->GetType() == GMLPT_Integer ) + eFType = OFTInteger; + else if( poProperty->GetType() == GMLPT_Real ) + eFType = OFTReal; + else if( poProperty->GetType() == GMLPT_StringList ) + eFType = OFTStringList; + else if( poProperty->GetType() == GMLPT_IntegerList ) + eFType = OFTIntegerList; + else if( poProperty->GetType() == GMLPT_RealList ) + eFType = OFTRealList; + else + eFType = OFTString; + + OGRFieldDefn oField( poProperty->GetName(), eFType ); + if ( EQUALN(oField.GetNameRef(), "ogr:", 4) ) + oField.SetName(poProperty->GetName()+4); + if( poProperty->GetWidth() > 0 ) + oField.SetWidth( poProperty->GetWidth() ); + + poLayer->GetLayerDefn()->AddFieldDefn( &oField ); + } + + return poLayer; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRNASDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRNASDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* PopulateRelations() */ +/************************************************************************/ + +void OGRNASDataSource::PopulateRelations() + +{ + GMLFeature *poFeature; + + poReader->ResetReading(); + while( (poFeature = poReader->NextFeature()) != NULL ) + { + char **papszOBProperties = poFeature->GetOBProperties(); + int i; + + for( i = 0; papszOBProperties != NULL && papszOBProperties[i] != NULL; + i++ ) + { + int nGMLIdIndex = poFeature->GetClass()->GetPropertyIndex( "gml_id" ); + const GMLProperty *psGMLId = (nGMLIdIndex >= 0) ? poFeature->GetProperty(nGMLIdIndex ) : NULL; + char *pszName = NULL; + const char *pszValue = CPLParseNameValue( papszOBProperties[i], + &pszName ); + + if( EQUALN(pszValue,"urn:adv:oid:",12) + && psGMLId != NULL && psGMLId->nSubProperties == 1 ) + { + poRelationLayer->AddRelation( psGMLId->papszSubProperties[0], + pszName, + pszValue + 12 ); + } + CPLFree( pszName ); + } + + delete poFeature; + } + + poRelationLayer->MarkRelationsPopulated(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogrnasdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogrnasdriver.cpp new file mode 100644 index 000000000..7f1f15eae --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogrnasdriver.cpp @@ -0,0 +1,157 @@ +/****************************************************************************** + * $Id: ogrnasdriver.cpp 28131 2014-12-11 22:30:16Z jef $ + * + * Project: OGR + * Purpose: OGRNASDriver implementation + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_nas.h" +#include "cpl_conv.h" +#include "nasreaderp.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: ogrnasdriver.cpp 28131 2014-12-11 22:30:16Z jef $"); + + +/************************************************************************/ +/* OGRNASDriverUnload() */ +/************************************************************************/ + +static void OGRNASDriverUnload(CPL_UNUSED GDALDriver* poDriver) +{ + if( NASReader::hMutex != NULL ) + CPLDestroyMutex( NASReader::hMutex ); + NASReader::hMutex = NULL; +} + +/************************************************************************/ +/* OGRNASDriverIdentify() */ +/************************************************************************/ + +static int OGRNASDriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->fpL == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Check for a UTF-8 BOM and skip if found */ +/* */ +/* TODO: BOM is variable-length parameter and depends on encoding. */ +/* Add BOM detection for other encodings. */ +/* -------------------------------------------------------------------- */ + + // Used to skip to actual beginning of XML data + const char* szPtr = (const char*)poOpenInfo->pabyHeader; + + if( ( (unsigned char)szPtr[0] == 0xEF ) + && ( (unsigned char)szPtr[1] == 0xBB ) + && ( (unsigned char)szPtr[2] == 0xBF) ) + { + szPtr += 3; + } + +/* -------------------------------------------------------------------- */ +/* Here, we expect the opening chevrons of NAS tree root element */ +/* -------------------------------------------------------------------- */ + if( szPtr[0] != '<' ) + return FALSE; + + if( !poOpenInfo->TryToIngest(8192) ) + return FALSE; + szPtr = (const char*)poOpenInfo->pabyHeader; + + if( strstr(szPtr,"opengis.net/gml") == NULL ) + return FALSE; + + char **papszIndicators = CSLTokenizeStringComplex( CPLGetConfigOption( "NAS_INDICATOR", "NAS-Operationen.xsd;NAS-Operationen_optional.xsd;AAA-Fachschema.xsd" ), ";", 0, 0 ); + + bool bFound = FALSE; + for( int i = 0; papszIndicators[i] && !bFound; i++ ) + { + bFound = strstr( szPtr, papszIndicators[i] ) != NULL; + } + + CSLDestroy( papszIndicators ); + + return bFound; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRNASDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + OGRNASDataSource *poDS; + + if( poOpenInfo->eAccess == GA_Update || + !OGRNASDriverIdentify(poOpenInfo) ) + return NULL; + + VSIFCloseL(poOpenInfo->fpL); + poOpenInfo->fpL = NULL; + + poDS = new OGRNASDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename ) + || poDS->GetLayerCount() == 0 ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* RegisterOGRNAS() */ +/************************************************************************/ + +void RegisterOGRNAS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "NAS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "NAS" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "NAS - ALKIS" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "xml" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_nas.html" ); + + poDriver->pfnOpen = OGRNASDriverOpen; + poDriver->pfnIdentify = OGRNASDriverIdentify; + poDriver->pfnUnloadDriver = OGRNASDriverUnload; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogrnaslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogrnaslayer.cpp new file mode 100644 index 000000000..d268cef3a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogrnaslayer.cpp @@ -0,0 +1,327 @@ +/****************************************************************************** + * $Id: ogrnaslayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OGR + * Purpose: Implements OGRNASLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_nas.h" +#include "cpl_conv.h" +#include "cpl_port.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrnaslayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRNASLayer() */ +/************************************************************************/ + +OGRNASLayer::OGRNASLayer( const char * pszName, + OGRSpatialReference *poSRSIn, + OGRwkbGeometryType eReqType, + OGRNASDataSource *poDSIn ) + +{ + if( poSRSIn == NULL ) + poSRS = NULL; + else + poSRS = poSRSIn->Clone(); + + iNextNASId = 0; + nTotalNASCount = -1; + + poDS = poDSIn; + + if ( EQUALN(pszName, "ogr:", 4) ) + poFeatureDefn = new OGRFeatureDefn( pszName+4 ); + else + poFeatureDefn = new OGRFeatureDefn( pszName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + poFeatureDefn->SetGeomType( eReqType ); + +/* -------------------------------------------------------------------- */ +/* Reader's should get the corresponding NASFeatureClass and */ +/* cache it. */ +/* -------------------------------------------------------------------- */ + poFClass = poDS->GetReader()->GetClass( pszName ); +} + +/************************************************************************/ +/* ~OGRNASLayer() */ +/************************************************************************/ + +OGRNASLayer::~OGRNASLayer() + +{ + if( poFeatureDefn ) + poFeatureDefn->Release(); + + if( poSRS != NULL ) + poSRS->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRNASLayer::ResetReading() + +{ + iNextNASId = 0; + poDS->GetReader()->ResetReading(); + if (poFClass) + poDS->GetReader()->SetFilteredClassName(poFClass->GetName()); +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRNASLayer::GetNextFeature() + +{ + GMLFeature *poNASFeature = NULL; + OGRGeometry *poGeom = NULL; + + if( iNextNASId == 0 ) + ResetReading(); + +/* ==================================================================== */ +/* Loop till we find and translate a feature meeting all our */ +/* requirements. */ +/* ==================================================================== */ + while( TRUE ) + { +/* -------------------------------------------------------------------- */ +/* Cleanup last feature, and get a new raw nas feature. */ +/* -------------------------------------------------------------------- */ + delete poNASFeature; + delete poGeom; + + poNASFeature = NULL; + poGeom = NULL; + + poNASFeature = poDS->GetReader()->NextFeature(); + if( poNASFeature == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Is it of the proper feature class? */ +/* -------------------------------------------------------------------- */ + + // We count reading low level NAS features as a feature read for + // work checking purposes, though at least we didn't necessary + // have to turn it into an OGRFeature. + m_nFeaturesRead++; + + if( poNASFeature->GetClass() != poFClass ) + continue; + + iNextNASId++; + +/* -------------------------------------------------------------------- */ +/* Does it satisfy the spatial query, if there is one? */ +/* -------------------------------------------------------------------- */ + const CPLXMLNode* const * papsGeometry = poNASFeature->GetGeometryList(); + if (papsGeometry[0] != NULL) + { + poGeom = (OGRGeometry*) OGR_G_CreateFromGMLTree(papsGeometry[0]); + poGeom = NASReader::ConvertGeometry(poGeom); + poGeom = OGRGeometryFactory::forceTo(poGeom, GetGeomType()); + // poGeom->dumpReadable( 0, "NAS: " ); + + // We assume the OGR_G_CreateFromGMLTree() function would have already + // reported an error. + if( poGeom == NULL ) + { + delete poNASFeature; + return NULL; + } + + if( m_poFilterGeom != NULL && !FilterGeometry( poGeom ) ) + continue; + } + +/* -------------------------------------------------------------------- */ +/* Convert the whole feature into an OGRFeature. */ +/* -------------------------------------------------------------------- */ + int iField; + OGRFeature *poOGRFeature = new OGRFeature( GetLayerDefn() ); + + poOGRFeature->SetFID( iNextNASId ); + + for( iField = 0; iField < poFClass->GetPropertyCount(); iField++ ) + { + const GMLProperty *psGMLProperty = poNASFeature->GetProperty( iField ); + if( psGMLProperty == NULL || psGMLProperty->nSubProperties == 0 ) + continue; + + switch( poFClass->GetProperty(iField)->GetType() ) + { + case GMLPT_Real: + { + poOGRFeature->SetField( iField, CPLAtof(psGMLProperty->papszSubProperties[0]) ); + } + break; + + case GMLPT_IntegerList: + { + int nCount = psGMLProperty->nSubProperties; + int *panIntList = (int *) CPLMalloc(sizeof(int) * nCount ); + int i; + + for( i = 0; i < nCount; i++ ) + panIntList[i] = atoi(psGMLProperty->papszSubProperties[i]); + + poOGRFeature->SetField( iField, nCount, panIntList ); + CPLFree( panIntList ); + } + break; + + case GMLPT_RealList: + { + int nCount = psGMLProperty->nSubProperties; + double *padfList = (double *)CPLMalloc(sizeof(double)*nCount); + int i; + + for( i = 0; i < nCount; i++ ) + padfList[i] = CPLAtof(psGMLProperty->papszSubProperties[i]); + + poOGRFeature->SetField( iField, nCount, padfList ); + CPLFree( padfList ); + } + break; + + case GMLPT_StringList: + { + poOGRFeature->SetField( iField, psGMLProperty->papszSubProperties ); + } + break; + + default: + poOGRFeature->SetField( iField, psGMLProperty->papszSubProperties[0] ); + break; + } + } + + poOGRFeature->SetGeometryDirectly( poGeom ); + poGeom = NULL; + +/* -------------------------------------------------------------------- */ +/* Test against the attribute query. */ +/* -------------------------------------------------------------------- */ + if( m_poAttrQuery != NULL + && !m_poAttrQuery->Evaluate( poOGRFeature ) ) + { + delete poOGRFeature; + continue; + } + +/* -------------------------------------------------------------------- */ +/* Wow, we got our desired feature. Return it. */ +/* -------------------------------------------------------------------- */ + delete poNASFeature; + + return poOGRFeature; + } + + return NULL; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRNASLayer::GetFeatureCount( int bForce ) + +{ + if( poFClass == NULL ) + return 0; + + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL ) + return OGRLayer::GetFeatureCount( bForce ); + else + return poFClass->GetFeatureCount(); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRNASLayer::GetExtent(OGREnvelope *psExtent, int bForce ) + +{ + double dfXMin, dfXMax, dfYMin, dfYMax; + + if( poFClass != NULL && + poFClass->GetExtents( &dfXMin, &dfXMax, &dfYMin, &dfYMax ) ) + { + psExtent->MinX = dfXMin; + psExtent->MaxX = dfXMax; + psExtent->MinY = dfYMin; + psExtent->MaxY = dfYMax; + + return OGRERR_NONE; + } + else + return OGRLayer::GetExtent( psExtent, bForce ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRNASLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCFastGetExtent) ) + { + double dfXMin, dfXMax, dfYMin, dfYMax; + + if( poFClass == NULL ) + return FALSE; + + return poFClass->GetExtents( &dfXMin, &dfXMax, &dfYMin, &dfYMax ); + } + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + { + if( poFClass == NULL + || m_poFilterGeom != NULL + || m_poAttrQuery != NULL ) + return FALSE; + + return poFClass->GetFeatureCount() != -1; + } + + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + + else + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogrnasrelationlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogrnasrelationlayer.cpp new file mode 100644 index 000000000..6230c7e88 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/nas/ogrnasrelationlayer.cpp @@ -0,0 +1,206 @@ +/****************************************************************************** + * $Id: ogrnasrelationlayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OGR + * Purpose: Implements OGRNASRelationLayer class, a special layer holding all + * the relations from the NAS file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_nas.h" +#include "cpl_conv.h" +#include "cpl_port.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrnasrelationlayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRNASRelationLayer() */ +/************************************************************************/ + +OGRNASRelationLayer::OGRNASRelationLayer( OGRNASDataSource *poDSIn ) + +{ + poDS = poDSIn; + + iNextFeature = 0; + bPopulated = FALSE; + +/* -------------------------------------------------------------------- */ +/* Establish the layer fields. */ +/* -------------------------------------------------------------------- */ + poFeatureDefn = new OGRFeatureDefn( "ALKIS_beziehungen" ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + OGRFieldDefn oFD( "", OFTString ); + + oFD.SetName( "beziehung_von" ); + poFeatureDefn->AddFieldDefn( &oFD ); + + oFD.SetName( "beziehungsart" ); + poFeatureDefn->AddFieldDefn( &oFD ); + + oFD.SetName( "beziehung_zu" ); + poFeatureDefn->AddFieldDefn( &oFD ); +} + +/************************************************************************/ +/* ~OGRNASRelationLayer() */ +/************************************************************************/ + +OGRNASRelationLayer::~OGRNASRelationLayer() + +{ + if( poFeatureDefn ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRNASRelationLayer::ResetReading() + +{ + iNextFeature = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRNASRelationLayer::GetNextFeature() + +{ + if( !bPopulated ) + poDS->PopulateRelations(); + +/* ==================================================================== */ +/* Loop till we find and translate a feature meeting all our */ +/* requirements. */ +/* ==================================================================== */ + while( TRUE ) + { + // out of features? + if( iNextFeature >= (int) aoRelationCollection.size() ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* The from/type/to values are stored in a packed string with */ +/* \0 separators for compactness. Split out components. */ +/* -------------------------------------------------------------------- */ + const char *pszFromID, *pszType, *pszToID; + + pszFromID = aoRelationCollection[iNextFeature].c_str(); + pszType = pszFromID + strlen(pszFromID) + 1; + pszToID = pszType + strlen(pszType) + 1; + + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Translate values into an OGRFeature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + poFeature->SetField( 0, pszFromID ); + poFeature->SetField( 1, pszType ); + poFeature->SetField( 2, pszToID ); + + poFeature->SetFID( iNextFeature++ ); + +/* -------------------------------------------------------------------- */ +/* Test against the attribute query. */ +/* -------------------------------------------------------------------- */ + if( m_poAttrQuery != NULL + && !m_poAttrQuery->Evaluate( poFeature ) ) + delete poFeature; + else + return poFeature; + } + + return NULL; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRNASRelationLayer::GetFeatureCount( int bForce ) + +{ + if( !bPopulated ) + poDS->PopulateRelations(); + + if( m_poAttrQuery == NULL ) + return aoRelationCollection.size(); + else + return OGRLayer::GetFeatureCount( bForce ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRNASRelationLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCFastGetExtent) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return bPopulated && m_poAttrQuery == NULL; + + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* AddRelation() */ +/************************************************************************/ + +void OGRNASRelationLayer::AddRelation( const char *pszFromID, + const char *pszType, + const char *pszToID ) + +{ + int nMergedLen = strlen(pszFromID) + strlen(pszType) + strlen(pszToID) + 3; + char *pszMerged = (char *) CPLMalloc(nMergedLen); + + strcpy( pszMerged, pszFromID ); + strcpy( pszMerged + strlen(pszFromID) + 1, pszType ); + strcpy( pszMerged + strlen(pszFromID) + strlen(pszType) + 2, pszToID ); + + CPLString osRelation; + osRelation.assign( pszMerged, nMergedLen ); + + CPLFree( pszMerged ); + + aoRelationCollection.push_back( osRelation ); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/GNUmakefile new file mode 100644 index 000000000..8314359fd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/GNUmakefile @@ -0,0 +1,20 @@ + + +include ../../../GDALmake.opt + +OBJ = ntffilereader.o ntfrecord.o ogrntfdatasource.o \ + ogrntfdriver.o ogrntflayer.o ntf_estlayers.o \ + ogrntffeatureclasslayer.o ntf_generic.o ntf_raster.o \ + ntf_codelist.o ntfstroke.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +ntfdump$(EXE): ntfdump.$(OBJ_EXT) + $(LD) $(LDFLAGS) ntfdump.$(OBJ_EXT) $(CONFIG_LIBS) -o ntfdump$(EXE) + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/README.txt b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/README.txt new file mode 100644 index 000000000..a532ab5c6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/README.txt @@ -0,0 +1,213 @@ + + NTF OGR Implementation Notes + ============================ + + +Products (and Layers) Supported +------------------------------- + +Landline (and Landline Plus): + LANDLINE_POINT + LANDLINE_LINE + LANDLINE_NAME + +Panorama Contours: + PANORAMA_POINT + PANORAMA_CONTOUR + + HEIGHT attribute holds elevation. + +Strategi: + STRATEGI_POINT + STRATEGI_LINE + STRATEGI_TEXT + STRATEGI_NODE + +Meridian: + MERIDIAN_POINT + MERIDIAN_LINE + MERIDIAN_TEXT + MERIDIAN_NODE + +Boundaryline: + BOUNDARYLINE_LINK + BOUNDARYLINE_POLY + BOUNDARYLINE_COLLECTIONS + + The _POLY layer has links to links allowing true polygons to + be formed (otherwise the _POLY's only have a seed point for geometry. + The collections are collections of polygons (also without geometry + as read). This is the only product from which polygons can be + constructed. + +Boundary Line 2000: + BL2000_LINK + BL2000_POLY + BL2000_COLLECTIONS + + Similar to Boundaryline, but with different attributes, feature code + is largely unused, and the _POLY layer doesn't have a seed point. + +BaseData.GB: + BASEDATA_POINT + BASEDATA_LINE + BASEDATA_TEXT + BASEDATA_NODE + +OSCAR Asset/Traffic: + OSCAR_POINT + OSCAR_LINE + OSCAR_NODE + +OSCAR Network: + OSCAR_NETWORK_POINT + OSCAR_NETWORK_LINE + OSCAR_NETWORK_NODE + +Address Point: + ADDRESS_POINT + +Code Point: + CODE_POINT + +Code Point Plus: + CODE_POINT_PLUS + +The dataset as a whole will also have a FEATURE_CLASSES layer containing a +pure table relating FEAT_CODE numbers with feature class names (FC_NAME). +This applies to all products in the dataset. A few layer types (such as +the Code Point, and Address Point products) don't include feature classes. +Some products use features classes that are not defined in the file, and +so they will not appear in the FEATURE_CLASSES layer. + + +Product Schemas +--------------- + +The approach taken in this reader is to treat one file, or a directory +of files as a single dataset. All files in the dataset are scanned on +open. For each particular product (listed above) a set of layers are +created; however, these layers may be extracted from several files of the +same product. + +The layers are based on a low level feature type in the NTF file, but will +generally contain features of many different feature codes (FEAT_CODE +attribute). Different features within a given layer may have a variety of +attributes in the file; however, the schema is established based on the +union of all attributes possible within features of a particular type +(ie. POINT) of that product family (ie. OSCAR Network). + +If an NTF product is read that doesn't match one of the known schema's +it will go through a different generic handler which has only +layers of type GENERIC_POINT and GENERIC_LINE. The features only have +a FEAT_CODE attribute. + +Details of what layers of what products have what attributes can be found +in the NTFFileReader::EstablishLayers() method at the end of ntf_estlayers.cpp. +This file also contains all the product specific translation code. + + +Special Attributes +------------------ + +FEAT_CODE: General feature code integer, can be used to lookup a name in the + FEATURE_CLASSES layer/table. + +TEXT_ID/POINT_ID/LINE_ID/NAME_ID/COLL_ID/POLY_ID/GEOM_ID: + Unique identifier for a feature of the appropriate type. + +TILE_REF: All layers (except FEATURE_CLASSES) contain a TILE_REF attribute + which indicates which tile (file) the features came from. Generally + speaking the id numbers are only unique within the tile and so + the TILE_REF can be used restrict id links within features from + the same file. + +FONT/TEXT_HT/DIG_POSTN/ORIENT: + Detailed information on the font, text height, digitizing position, + and orientation of text or name objects. Review the OS product + manuals to understand the units, and meaning of these codes. + +GEOM_ID_OF_POINT: + For _NODE features this defines the POINT_ID of the point layer object + to which this node corresponds. Generally speaking the nodes don't + carry a geometry of their own. The node must be related to a point + to establish it's position. + +GEOM_ID_OF_LINK: + A _list_ of _LINK or _LINE features to end/start at a node. Nodes, + and this field are generally only of value when establishing + connectivity of line features for network analysis. Note that this + should be related to the target features GEOM_ID, not it's LINE_ID. + + On the BOUNDARYLINE_POLY layer this attribute contains the GEOM_IDs + of the lines which form the edge of the polygon. + +POLY_ID: + A list of POLY_ID's from the BOUNDARYLINE_POLY layer associated with + a given collection in the BOUNDARYLINE_COLLECTIONS layer. + + +Adding a New Product +-------------------- + +It is anticipated that over time the UK Ordnance Survey will define new +product formats, and to get decent milage out of them this library should +be updated to support them. While I will endevour to do this myself, it +seems prudent to define how it is done in case I am not available to do it, +or am unwilling to do it on a timely basis. To add a new product type the +following steps are required: + + o Add an NPC_ code for the product in ntf.h + o Add a case in NTFFileReader::Open() to translate the GetProduct() result + into the NPC_ code. + o Add a case in NTFFileReader::EstablishLayers() defining the layers found + on this product. + o Add translate functions for layers of this product. Generally they can + be cloned from an existing translate function, and the attribute mapping + in the NTFReader::ApplyAttributeValues() call can be modified. + +Occationally existing products will change slightly. This may result in a +slight change to the detection logic in NTFFileReader::Open() and changes +in the list of user attributes associated with the layer. If the differences +are signifiant it may be necessary to define a whole new product family +type (as is done for Code Point Plus vs. Code Point). + +Generic Products +---------------- + +In situations where a file is not identified as being part of an existing +known product it will be treated generically. In this case the entire dataset +is scanned to establish what features have what attributes. Because of this, +opening a generic dataset can be much slower than opening a recognised dataset. +Based on this scan a list of generic features (layers) are defined from the +following set: + + GENERIC_POINT + GENERIC_LINE + GENERIC_NAME + GENERIC_TEXT + GENERIC_POLY + GENERIC_NODE + GENERIC_COLLECTION + +Generic products are primarily handled by the ntf_generic.cpp module whereas +specific products are handled in ntf_estlayers.cpp. + +Because some data products (OSNI datasets) not from the Ordnance Survey +were found to have record groups in unusual orders compared to what the +UK Ordnance Survey does, it was found necessary to cache all the records of +level 3 and higher generic products, and construct record groups by id +reference from within this cache rather than depending on convenient record +orderings. This is accomplished by the NTFFileReader "indexing" capability +near the bottom of ntffilereader.cpp. Because of this in memory indexing +accessing generic datasets can be much more memory intensive than accessing +known data products, though it isn't necessary for generic level 1 and 2 +products. + +It is possible to force a known product to be treated as generic by setting +the FORCE_GENERIC option to "ON" using OGRNTFDataSource::SetOptionsList() as +is demonstrated in ntfdump.cpp. + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/drv_ntf.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/drv_ntf.html new file mode 100644 index 000000000..8131909a2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/drv_ntf.html @@ -0,0 +1,210 @@ + + +UK .NTF + + + + +

    UK .NTF

    + +The National Transfer Format, mostly used by the UK Ordnance Survey, +is supported for read access.

    + +This driver treats a directory as a dataset and attempts to merge all the .NTF +files in the directory, producing a layer for each type of feature (but +generally not for each source file). Thus a directory containing several +Landline files will have three layers (LANDLINE_POINT, LANDLINE_LINE and +LANDLINE_NAME) regardless of the number of landline files.

    + +NTF features are always returned with the British National Grid coordinate +system. This may be inappropriate for NTF files written by organizations other +than the UK Ordnance Survey.

    + +

    See Also

    + + + +
    + +

    Implementation Notes

    + +

    Products (and Layers) Supported

    +
    +Landline (and Landline Plus):
    +	LANDLINE_POINT
    +	LANDLINE_LINE
    +	LANDLINE_NAME
    +
    +Panorama Contours:
    +	PANORAMA_POINT
    +	PANORAMA_CONTOUR
    +
    +	HEIGHT attribute holds elevation.
    +
    +Strategi:
    +	STRATEGI_POINT
    +	STRATEGI_LINE
    +	STRATEGI_TEXT
    +	STRATEGI_NODE
    +
    +Meridian:
    +	MERIDIAN_POINT
    +	MERIDIAN_LINE
    +	MERIDIAN_TEXT
    +	MERIDIAN_NODE
    +
    +Boundaryline:
    +	BOUNDARYLINE_LINK
    +	BOUNDARYLINE_POLY
    +	BOUNDARYLINE_COLLECTIONS
    +
    +	The _POLY layer has links to links allowing true polygons to  
    +        be formed (otherwise the _POLY's only have a seed point for geometry.
    +	The collections are collections of polygons (also without geometry
    +	as read).  This is the only product from which polygons can be
    +	constructed.
    +	
    +BaseData.GB:
    +	BASEDATA_POINT
    +	BASEDATA_LINE
    +	BASEDATA_TEXT
    +	BASEDATA_NODE
    +
    +OSCAR Asset/Traffic:
    +	OSCAR_POINT
    +	OSCAR_LINE
    +	OSCAR_NODE
    +
    +OSCAR Network:
    +	OSCAR_NETWORK_POINT
    +	OSCAR_NETWORK_LINE
    +	OSCAR_NETWORK_NODE
    +
    +Address Point:
    +	ADDRESS_POINT
    +
    +Code Point:
    +	CODE_POINT
    +
    +Code Point Plus:
    +	CODE_POINT_PLUS
    +
    + +The dataset as a whole will also have a FEATURE_CLASSES layer containing a +pure table relating FEAT_CODE numbers with feature class names (FC_NAME). +This applies to all products in the dataset. A few layer types (such as +the Code Point, and Address Point products) don't include feature classes. +Some products use features classes that are not defined in the file, and +so they will not appear in the FEATURE_CLASSES layer.

    + + +

    Product Schemas

    + +The approach taken in this reader is to treat one file, or a directory +of files as a single dataset. All files in the dataset are scanned on +open. For each particular product (listed above) a set of layers are +created; however, these layers may be extracted from several files of the +same product.

    + +The layers are based on a low level feature type in the NTF file, but will +generally contain features of many different feature codes (FEAT_CODE +attribute). Different features within a given layer may have a variety of +attributes in the file; however, the schema is established based on the +union of all attributes possible within features of a particular type +(ie. POINT) of that product family (ie. OSCAR Network).

    + +If an NTF product is read that doesn't match one of the known schema's +it will go through a different generic handler which has only +layers of type GENERIC_POINT and GENERIC_LINE. The features only have +a FEAT_CODE attribute.

    + +Details of what layers of what products have what attributes can be found +in the NTFFileReader::EstablishLayers() method at the end of ntf_estlayers.cpp. +This file also contains all the product specific translation code.

    + + +

    Special Attributes

    + +
    +FEAT_CODE: General feature code integer, can be used to lookup a name in the
    +           FEATURE_CLASSES layer/table. 
    +
    +TEXT_ID/POINT_ID/LINE_ID/NAME_ID/COLL_ID/POLY_ID/GEOM_ID:
    +          Unique identifier for a feature of the appropriate type.  
    +
    +TILE_REF: All layers (except FEATURE_CLASSES) contain a TILE_REF attribute
    +          which indicates which tile (file) the features came from.  Generally
    +          speaking the id numbers are only unique within the tile and so
    +          the TILE_REF can be used restrict id links within features from
    +          the same file. 
    +
    +FONT/TEXT_HT/DIG_POSTN/ORIENT:
    +	Detailed information on the font, text height, digitizing position, 
    +        and orientation of text or name objects.  Review the OS product
    +        manuals to understand the units, and meaning of these codes. 
    +
    +GEOM_ID_OF_POINT:
    +	For _NODE features this defines the POINT_ID of the point layer object
    +        to which this node corresponds.  Generally speaking the nodes don't
    +        carry a geometry of their own.  The node must be related to a point
    +        to establish it's position. 
    +
    +GEOM_ID_OF_LINK:
    +	A _list_ of _LINK or _LINE features to end/start at a node.  Nodes,
    +        and this field are generally only of value when establishing 
    +        connectivity of line features for network analysis.   Note that this
    +        should be related to the target features GEOM_ID, not it's LINE_ID.
    +
    +        On the BOUNDARYLINE_POLY layer this attribute contains the GEOM_IDs
    +        of the lines which form the edge of the polygon. 
    +
    +POLY_ID:
    +	A list of POLY_ID's from the BOUNDARYLINE_POLY layer associated with
    +        a given collection in the BOUNDARYLINE_COLLECTIONS layer. 
    +
    + + +

    Generic Products

    + +In situations where a file is not identified as being part of an existing +known product it will be treated generically. In this case the entire dataset +is scanned to establish what features have what attributes. Because of this, +opening a generic dataset can be much slower than opening a recognised dataset. +Based on this scan a list of generic features (layers) are defined from the +following set:

    + +

    + GENERIC_POINT
    + GENERIC_LINE
    + GENERIC_NAME
    + GENERIC_TEXT
    + GENERIC_POLY
    + GENERIC_NODE
    + GENERIC_COLLECTION
    +
    + +Generic products are primarily handled by the ntf_generic.cpp module whereas +specific products are handled in ntf_estlayers.cpp.

    + +Because some data products (OSNI datasets) not from the Ordnance Survey +were found to have record groups in unusual orders compared to what the +UK Ordnance Survey does, it was found necessary to cache all the records of +level 3 and higher generic products, and construct record groups by id +reference from within this cache rather than depending on convenient record +orderings. This is accomplished by the NTFFileReader "indexing" capability +near the bottom of ntffilereader.cpp. Because of this in memory indexing +accessing generic datasets can be much more memory intensive than accessing +known data products, though it isn't necessary for generic level 1 and 2 +products.

    + +It is possible to force a known product to be treated as generic by setting +the FORCE_GENERIC option to "ON" using OGRNTFDataSource::SetOptionsList() as +is demonstrated in ntfdump.cpp. This may also be accomplished from +outside OGR applications by setting the OGR_NTF_OPTIONS environment +variable to "FORCE_GENERIC=ON".

    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/makefile.vc new file mode 100644 index 000000000..83d1d1c16 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/makefile.vc @@ -0,0 +1,19 @@ + +OBJ = ntf_estlayers.obj ntffilereader.obj ntfrecord.obj \ + ogrntfdatasource.obj ogrntfdriver.obj ogrntflayer.obj \ + ogrntffeatureclasslayer.obj ntf_generic.obj ntf_raster.obj \ + ntf_codelist.obj ntfstroke.obj + +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf.h new file mode 100644 index 000000000..7bcb1d3fe --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf.h @@ -0,0 +1,579 @@ +/****************************************************************************** + * $Id: ntf.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: NTF Translator + * Purpose: Main declarations for NTF translator. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _NTF_H_INCLUDED +#define _NTF_H_INCLUDED + +#include "cpl_conv.h" +#include "ogrsf_frmts.h" + +/* -------------------------------------------------------------------- */ +/* Record types. */ +/* -------------------------------------------------------------------- */ +#define NRT_VHR 1 /* Volume Header Record */ +#define NRT_DHR 2 /* Database Header Record */ +#define NRT_FCR 5 /* Feature Classification Record */ +#define NRT_SHR 7 /* Section Header Record */ +#define NRT_NAMEREC 11 /* Name Record */ +#define NRT_NAMEPOSTN 12 /* Name Position */ +#define NRT_ATTREC 14 /* Attribute Record */ +#define NRT_POINTREC 15 /* Point Record */ +#define NRT_NODEREC 16 /* Node Record */ +#define NRT_GEOMETRY 21 /* Geometry Record */ +#define NRT_GEOMETRY3D 22 /* 3D Geometry Record */ +#define NRT_LINEREC 23 /* Line Record */ +#define NRT_CHAIN 24 /* Chain */ +#define NRT_POLYGON 31 /* Polygon */ +#define NRT_CPOLY 33 /* Complex Polygon */ +#define NRT_COLLECT 34 /* Collection of featues */ +#define NRT_ADR 40 /* Attribute Description Record */ +#define NRT_CODELIST 42 /* Codelist Record (ie. BL2000) */ +#define NRT_TEXTREC 43 /* Text */ +#define NRT_TEXTPOS 44 /* Text position */ +#define NRT_TEXTREP 45 /* Text representation */ +#define NRT_GRIDHREC 50 /* Grid Header Record */ +#define NRT_GRIDREC 51 /* Grid Data Record */ +#define NRT_COMMENT 90 /* Comment record */ +#define NRT_VTR 99 /* Volume Termination Record */ + +/* -------------------------------------------------------------------- */ +/* Product names (DBNAME) and codes. */ +/* -------------------------------------------------------------------- */ + +#define NPC_UNKNOWN 0 + +#define NPC_LANDLINE 1 +#define NPC_LANDLINE99 2 +#define NTF_LANDLINE "LAND-LINE.93" +#define NTF_LANDLINE_PLUS "LAND-LINE.93+" + +#define NPC_STRATEGI 3 +#define NTF_STRATEGI "Strategi_02.96" + +#define NPC_MERIDIAN 4 +#define NTF_MERIDIAN "Meridian_01.95" + +#define NPC_BOUNDARYLINE 5 +#define NTF_BOUNDARYLINE "Boundary-Line" + +#define NPC_BASEDATA 6 +#define NTF_BASEDATA "BaseData.GB_01.96" + +#define NPC_OSCAR_ASSET 7 +#define NPC_OSCAR_TRAFFIC 8 +#define NPC_OSCAR_ROUTE 9 +#define NPC_OSCAR_NETWORK 10 + +#define NPC_ADDRESS_POINT 11 + +#define NPC_CODE_POINT 12 +#define NPC_CODE_POINT_PLUS 13 + +#define NPC_LANDFORM_PROFILE_CONT 14 + +#define NPC_LANDRANGER_CONT 15 +#define NTF_LANDRANGER_CONT "OS_LANDRANGER_CONT" + +#define NPC_LANDRANGER_DTM 16 +#define NPC_LANDFORM_PROFILE_DTM 17 + +#define NPC_BL2000 18 + +#define NPC_MERIDIAN2 19 +#define NTF_MERIDIAN2 "Meridian_02.01" + +/************************************************************************/ +/* NTFRecord */ +/************************************************************************/ + +class NTFRecord +{ + int nType; + int nLength; + char *pszData; + + int ReadPhysicalLine( FILE *fp, char *pszLine ); + + public: + NTFRecord( FILE * ); + ~NTFRecord(); + + int GetType() { return nType; } + int GetLength() { return nLength; } + const char *GetData() { return pszData; } + + const char *GetField( int, int ); +}; + +/************************************************************************/ +/* NTFGenericClass */ +/************************************************************************/ + +class NTFGenericClass +{ +public: + int nFeatureCount; + + int b3D; + int nAttrCount; + char **papszAttrNames; + char **papszAttrFormats; + int *panAttrMaxWidth; + int *pabAttrMultiple; + + NTFGenericClass(); + ~NTFGenericClass(); + + void CheckAddAttr( const char *, const char *, int ); + void SetMultiple( const char * ); +}; + +/************************************************************************/ +/* NTFCodeList */ +/************************************************************************/ + +class NTFCodeList +{ +public: + NTFCodeList( NTFRecord * ); + ~NTFCodeList(); + + const char *Lookup( const char * ); + + char szValType[3]; /* attribute code for list, ie. AC */ + char szFInter[6]; /* format of code values */ + + int nNumCode; + char **papszCodeVal; /* Short code value */ + char **papszCodeDes; /* Long description of code */ + +}; + +/************************************************************************/ +/* NTFAttDesc */ +/************************************************************************/ +typedef struct +{ + char val_type [ 2 +1]; + char fwidth [ 3 +1]; + char finter [ 5 +1]; + char att_name [ 100 ]; + + NTFCodeList *poCodeList; + +} NTFAttDesc; + + +class OGRNTFLayer; +class OGRNTFRasterLayer; +class OGRNTFDataSource; +class NTFFileReader; + +#define MAX_REC_GROUP 100 +typedef OGRFeature *(*NTFFeatureTranslator)(NTFFileReader *, + OGRNTFLayer *, + NTFRecord **); +typedef int (*NTFRecordGrouper)(NTFFileReader *, NTFRecord **, NTFRecord *); + +/************************************************************************/ +/* NTFFileReader */ +/************************************************************************/ + +class NTFFileReader +{ + char *pszFilename; + OGRNTFDataSource *poDS; + + FILE *fp; + + // feature class list. + int nFCCount; + char **papszFCNum; + char **papszFCName; + + // attribute definitions + int nAttCount; + NTFAttDesc *pasAttDesc; + + char *pszTileName; + int nCoordWidth; + int nZWidth; + int nNTFLevel; + + double dfXYMult; + double dfZMult; + + double dfXOrigin; + double dfYOrigin; + + double dfTileXSize; + double dfTileYSize; + + double dfScale; + double dfPaperToGround; + + long nStartPos; + long nPreSavedPos; + long nPostSavedPos; + NTFRecord *poSavedRecord; + + long nSavedFeatureId; + long nBaseFeatureId; + long nFeatureCount; + + NTFRecord *apoCGroup[MAX_REC_GROUP+1]; + + char *pszProduct; + char *pszPVName; + int nProduct; + + void EstablishLayers(); + + void ClearCGroup(); + void ClearDefs(); + + OGRNTFLayer *apoTypeTranslation[100]; + + NTFRecordGrouper pfnRecordGrouper; + + int anIndexSize[100]; + NTFRecord **apapoRecordIndex[100]; + int bIndexBuilt; + int bIndexNeeded; + + void EstablishRasterAccess(); + int nRasterXSize; + int nRasterYSize; + int nRasterDataType; + double adfGeoTransform[6]; + + OGRNTFRasterLayer *poRasterLayer; + + long *panColumnOffset; + + int bCacheLines; + int nLineCacheSize; + OGRGeometry **papoLineCache; + + public: + NTFFileReader( OGRNTFDataSource * ); + ~NTFFileReader(); + + int Open( const char * pszFilename = NULL ); + void Close(); + FILE *GetFP() { return fp; } + void GetFPPos( long *pnPos, long * pnFeatureId); + int SetFPPos( long nPos, long nFeatureId ); + void Reset(); + void SetBaseFID( long nFeatureId ); + + + OGRGeometry *ProcessGeometry( NTFRecord *, int * = NULL ); + OGRGeometry *ProcessGeometry3D( NTFRecord *, int * = NULL ); + int ProcessAttDesc( NTFRecord *, NTFAttDesc * ); + int ProcessAttRec( NTFRecord *, int *, char ***, char ***); + int ProcessAttRecGroup( NTFRecord **, char ***, char ***); + + NTFAttDesc *GetAttDesc( const char * ); + + void ApplyAttributeValues( OGRFeature *, NTFRecord **, ... ); + + int ApplyAttributeValue( OGRFeature *, int, const char *, + char **, char ** ); + + int ProcessAttValue( const char *pszValType, + const char *pszRawValue, + char **ppszAttName, + char **ppszAttValue, + char **ppszCodeDesc ); + + int TestForLayer( OGRNTFLayer * ); + OGRFeature *ReadOGRFeature( OGRNTFLayer * = NULL ); + NTFRecord **ReadRecordGroup(); + NTFRecord *ReadRecord(); + void SaveRecord( NTFRecord * ); + + void DumpReadable( FILE * ); + + int GetXYLen() { return nCoordWidth; } + double GetXYMult() { return dfXYMult; } + double GetXOrigin() { return dfXOrigin; } + double GetYOrigin() { return dfYOrigin; } + double GetZMult() { return dfZMult; } + const char *GetTileName() { return pszTileName; } + const char *GetFilename() { return pszFilename; } + int GetNTFLevel() { return nNTFLevel; } + const char *GetProduct() { return pszProduct; } + const char *GetPVName() { return pszPVName; } + int GetProductId() { return nProduct; } + double GetScale() { return dfScale; } + double GetPaperToGround() { return dfPaperToGround; } + + int GetFCCount() { return nFCCount; } + int GetFeatureClass( int, char **, char ** ); + + void OverrideTileName( const char * ); + + // Generic file index + void IndexFile(); + void FreshenIndex(); + void DestroyIndex(); + NTFRecord *GetIndexedRecord( int, int ); + NTFRecord **GetNextIndexedRecordGroup( NTFRecord ** ); + + // Line geometry cache + OGRGeometry *CacheGetByGeomId( int ); + void CacheAddByGeomId( int, OGRGeometry * ); + void CacheClean(); + void CacheLineGeometryInGroup( NTFRecord ** ); + + int FormPolygonFromCache( OGRFeature * ); + + // just for use of OGRNTFDatasource + void EstablishLayer( const char *, OGRwkbGeometryType, + NTFFeatureTranslator, int, + NTFGenericClass *, ... ); + + // Raster related + int IsRasterProduct(); + int GetRasterXSize() { return nRasterXSize; } + int GetRasterYSize() { return nRasterYSize; } + int GetRasterDataType() { return nRasterDataType; } + double *GetGeoTransform() { return adfGeoTransform; } + CPLErr ReadRasterColumn( int, float * ); + +}; + +/************************************************************************/ +/* OGRNTFLayer */ +/************************************************************************/ + +class OGRNTFLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + NTFFeatureTranslator pfnTranslator; + + OGRNTFDataSource *poDS; + + int iCurrentReader; + long nCurrentPos; + long nCurrentFID; + + public: + OGRNTFLayer( OGRNTFDataSource * poDS, + OGRFeatureDefn * poFeatureDefine, + NTFFeatureTranslator pfnTranslator ); + + ~OGRNTFLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + +#ifdef notdef + OGRFeature *GetFeature( GIntBig nFeatureId ); + OGRErr ISetFeature( OGRFeature *poFeature ); + OGRErr ICreateFeature( OGRFeature *poFeature ); +#endif + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + +#ifdef notdef + GIntBig GetFeatureCount( int ); +#endif + + int TestCapability( const char * ); + + // special to NTF + OGRFeature *FeatureTranslate( NTFFileReader *, NTFRecord ** ); +}; + +/************************************************************************/ +/* OGRNTFFeatureClassLayer */ +/************************************************************************/ + +class OGRNTFFeatureClassLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + OGRGeometry *poFilterGeom; + + OGRNTFDataSource *poDS; + + int iCurrentFC; + + public: + OGRNTFFeatureClassLayer( OGRNTFDataSource * poDS ); + ~OGRNTFFeatureClassLayer(); + + OGRGeometry * GetSpatialFilter() { return poFilterGeom; } + void SetSpatialFilter( OGRGeometry * ); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRFeature *GetFeature( GIntBig nFeatureId ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + GIntBig GetFeatureCount( int = TRUE ); + + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRNTFRasterLayer */ +/************************************************************************/ + +class OGRNTFRasterLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + OGRGeometry *poFilterGeom; + + OGRNTFDataSource *poDS; + + NTFFileReader *poReader; + + float *pafColumn; + int iColumnOffset; + + int iCurrentFC; + + int nDEMSample; + int nFeatureCount; + + public: + OGRNTFRasterLayer( OGRNTFDataSource * poDS, + NTFFileReader * poReaderIn ); + ~OGRNTFRasterLayer(); + + OGRGeometry * GetSpatialFilter() { return poFilterGeom; } + void SetSpatialFilter( OGRGeometry * ); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRFeature *GetFeature( GIntBig nFeatureId ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + GIntBig GetFeatureCount( int = TRUE ); + + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRNTFDataSource */ +/************************************************************************/ + +class OGRNTFDataSource : public OGRDataSource +{ + char *pszName; + + int nLayers; + OGRLayer **papoLayers; + + OGRNTFFeatureClassLayer *poFCLayer; + + int iCurrentFC; + int iCurrentReader; + long nCurrentPos; + long nCurrentFID; + + int nNTFFileCount; + NTFFileReader **papoNTFFileReader; + + int nFCCount; + char **papszFCNum; + char **papszFCName; + + OGRSpatialReference *poSpatialRef; + + NTFGenericClass aoGenericClass[100]; + + char **papszOptions; + + void EnsureTileNameUnique( NTFFileReader * ); + + public: + OGRNTFDataSource(); + ~OGRNTFDataSource(); + + void SetOptionList( char ** ); + const char *GetOption( const char * ); + + int Open( const char * pszName, int bTestOpen = FALSE, + char ** papszFileList = NULL ); + + const char *GetName() { return pszName; } + int GetLayerCount(); + OGRLayer *GetLayer( int ); + int TestCapability( const char * ); + + // Note: these are specific to NTF for now, but eventually might + // might be available as part of a more object oriented approach to + // features like that in FME or SFCORBA. + void ResetReading(); + OGRFeature * GetNextFeature(); + + // these are only for the use of the NTFFileReader class. + OGRNTFLayer *GetNamedLayer( const char * ); + void AddLayer( OGRLayer * ); + + // Mainly for OGRNTFLayer class + int GetFileCount() { return nNTFFileCount; } + NTFFileReader *GetFileReader(int i) { return papoNTFFileReader[i]; } + + int GetFCCount() { return nFCCount; } + int GetFeatureClass( int, char **, char ** ); + + OGRSpatialReference *GetSpatialRef() { return poSpatialRef; } + + NTFGenericClass *GetGClass( int i ) { return aoGenericClass + i; } + void WorkupGeneric( NTFFileReader * ); + void EstablishGenericLayers(); +}; + +/************************************************************************/ +/* Support functions. */ +/************************************************************************/ +int NTFArcCenterFromEdgePoints( double x_c0, double y_c0, + double x_c1, double y_c1, + double x_c2, double y_c2, + double *x_center, double *y_center ); +OGRGeometry * +NTFStrokeArcToOGRGeometry_Points( double dfStartX, double dfStartY, + double dfAlongX, double dfAlongY, + double dfEndX, double dfEndY, + int nVertexCount ); +OGRGeometry * +NTFStrokeArcToOGRGeometry_Angles( double dfCenterX, double dfCenterY, + double dfRadius, + double dfStartAngle, double dfEndAngle, + int nVertexCount ); + +#endif /* ndef _NTF_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf_codelist.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf_codelist.cpp new file mode 100644 index 000000000..e5fe9a0cd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf_codelist.cpp @@ -0,0 +1,126 @@ +/****************************************************************************** + * $Id: ntf_codelist.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: NTF Translator + * Purpose: NTFCodeList class implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ntf.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ntf_codelist.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* NTFCodeList */ +/************************************************************************/ + +NTFCodeList::NTFCodeList( NTFRecord * poRecord ) + +{ + int iThisField; + const char *pszText; + + CPLAssert( EQUAL(poRecord->GetField(1,2),"42") ); + + strcpy( szValType, poRecord->GetField(13,14) ); + strcpy( szFInter, poRecord->GetField(15,19) ); + + nNumCode = atoi(poRecord->GetField(20,22)); + + papszCodeVal = (char **) CPLMalloc(sizeof(char*) * nNumCode ); + papszCodeDes = (char **) CPLMalloc(sizeof(char*) * nNumCode ); + + pszText = poRecord->GetData() + 22; + for( iThisField=0; + *pszText != '\0' && iThisField < nNumCode; + iThisField++ ) + { + char szVal[128], szDes[128]; + int iLen; + + iLen = 0; + while( *pszText != '\\' && *pszText != '\0' ) + szVal[iLen++] = *(pszText++); + szVal[iLen] = '\0'; + + if( *pszText == '\\' ) + pszText++; + + iLen = 0; + while( *pszText != '\\' && *pszText != '\0' ) + szDes[iLen++] = *(pszText++); + szDes[iLen] = '\0'; + + if( *pszText == '\\' ) + pszText++; + + papszCodeVal[iThisField] = CPLStrdup(szVal); + papszCodeDes[iThisField] = CPLStrdup(szDes); + } + + if( iThisField < nNumCode ) + { + nNumCode = iThisField; + CPLDebug( "NTF", + "Didn't get all the expected fields from a CODELIST." ); + } +} + +/************************************************************************/ +/* ~NTFCodeList() */ +/************************************************************************/ + +NTFCodeList::~NTFCodeList() + +{ + for( int i = 0; i < nNumCode; i++ ) + { + CPLFree( papszCodeVal[i] ); + CPLFree( papszCodeDes[i] ); + } + + CPLFree( papszCodeVal ); + CPLFree( papszCodeDes ); +} + +/************************************************************************/ +/* Lookup() */ +/************************************************************************/ + +const char *NTFCodeList::Lookup( const char * pszCode ) + +{ + for( int i = 0; i < nNumCode; i++ ) + { + if( EQUAL(pszCode,papszCodeVal[i]) ) + return papszCodeDes[i]; + } + + return NULL; +} + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf_estlayers.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf_estlayers.cpp new file mode 100644 index 000000000..a104bf396 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf_estlayers.cpp @@ -0,0 +1,2447 @@ +/****************************************************************************** + * $Id: ntf_estlayers.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: NTF Translator + * Purpose: NTFFileReader methods related to establishing the schemas + * of features that could occur in this product and the functions + * for actually performing the NTFRecord to OGRFeature conversion. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ntf.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ntf_estlayers.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +#define MAX_LINK 5000 + +/************************************************************************/ +/* TranslateCodePoint() */ +/* */ +/* Used for code point, and code point plus. */ +/************************************************************************/ + +static OGRFeature *TranslateCodePoint( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1])); + + // Attributes + if( EQUAL(poLayer->GetLayerDefn()->GetName(),"CODE_POINT") ) + poReader->ApplyAttributeValues( poFeature, papoGroup, + "PC", 1, "PQ", 2, "PR", 3, "TP", 4, + "DQ", 5, "RP", 6, "BP", 7, "PD", 8, + "MP", 9, "UM", 10, "RV", 11, + NULL ); + else + poReader->ApplyAttributeValues( poFeature, papoGroup, + "PC", 1, "PQ", 2, "PR", 3, "TP", 4, + "DQ", 5, "RP", 6, "BP", 7, "PD", 8, + "MP", 9, "UM", 10, "RV", 11, + "RH", 12, "LH", 13, "CC", 14, + "DC", 15, "WC", 16, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateAddressPoint() */ +/************************************************************************/ + +static OGRFeature *TranslateAddressPoint( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // CHG_TYPE + poFeature->SetField( 17, papoGroup[0]->GetField( 22, 22 ) ); + + // CHG_DATE + poFeature->SetField( 18, papoGroup[0]->GetField( 23, 28 ) ); + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1])); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "OA", 1, "ON", 2, "DP", 3, "PB", 4, + "SB", 5, "BD", 6, "BN", 7, "DR", 8, + "TN", 9, "DD", 10, "DL", 11, "PT", 12, + "CN", 13, "PC", 14, "SF", 15, "RV", 16, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateOscarPoint() */ +/* */ +/* Used for OSCAR Traffic and Asset datasets. */ +/************************************************************************/ + +static OGRFeature *TranslateOscarPoint( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + poFeature->SetField( 1, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 2, "OD", 3, "JN", 4, "SN", 5, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateOscarLine() */ +/************************************************************************/ + +static OGRFeature *TranslateOscarLine( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_LINEREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // LINE_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + poFeature->SetField( 1, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 2, "OD", 3, "PN", 4, "LL", 5, + "SC", 6, "FW", 7, "RN", 8, "TR", 9, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateOscarRoutePoint() */ +/************************************************************************/ + +static OGRFeature *TranslateOscarRoutePoint( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + poFeature->SetField( 1, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 2, "OD", 3, "JN", 4, "SN", 5, + "NP", 6, "RT", 8, + NULL ); + + // PARENT_OSODR + char **papszTypes, **papszValues; + + if( poReader->ProcessAttRecGroup( papoGroup, &papszTypes, &papszValues ) ) + { + char **papszOSODRList = NULL; + + for( int i = 0; papszTypes != NULL && papszTypes[i] != NULL; i++ ) + { + if( EQUAL(papszTypes[i],"PO") ) + papszOSODRList = CSLAddString(papszOSODRList,papszValues[i]); + } + + poFeature->SetField( 7, papszOSODRList ); + CPLAssert( CSLCount(papszOSODRList) == + poFeature->GetFieldAsInteger( 6 ) ); + + CSLDestroy( papszOSODRList ); + CSLDestroy( papszTypes ); + CSLDestroy( papszValues ); + } + + return poFeature; +} + +/************************************************************************/ +/* TranslateOscarRouteLine() */ +/************************************************************************/ + +static OGRFeature *TranslateOscarRouteLine( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_LINEREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // LINE_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + poFeature->SetField( 1, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 2, "OD", 3, "PN", 4, "LL", 5, + "RN", 6, "TR", 7, "NP", 8, + NULL ); + + // PARENT_OSODR + char **papszTypes, **papszValues; + + if( poReader->ProcessAttRecGroup( papoGroup, &papszTypes, &papszValues ) ) + { + char **papszOSODRList = NULL; + + for( int i = 0; papszTypes != NULL && papszTypes[i] != NULL; i++ ) + { + if( EQUAL(papszTypes[i],"PO") ) + papszOSODRList = CSLAddString(papszOSODRList,papszValues[i]); + } + + poFeature->SetField( 9, papszOSODRList ); + CPLAssert( CSLCount(papszOSODRList) == + poFeature->GetFieldAsInteger( 8 ) ); + + CSLDestroy( papszOSODRList ); + CSLDestroy( papszTypes ); + CSLDestroy( papszValues ); + } + + return poFeature; +} + +/************************************************************************/ +/* TranslateOscarComment() */ +/************************************************************************/ + +static OGRFeature *TranslateOscarComment( CPL_UNUSED NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) != 1 + || papoGroup[0]->GetType() != NRT_COMMENT ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // RECORD_TYPE + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 4 )) ); + + // RECORD_ID + poFeature->SetField( 1, papoGroup[0]->GetField( 5, 17 ) ); + + // CHANGE_TYPE + poFeature->SetField( 2, papoGroup[0]->GetField( 18, 18 ) ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateOscarNetworkPoint() */ +/************************************************************************/ + +static OGRFeature *TranslateOscarNetworkPoint( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + poFeature->SetField( 1, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 2, "OD", 3, "JN", 4, "SN", 5, + "RT", 6, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateOscarNetworkLine() */ +/************************************************************************/ + +static OGRFeature *TranslateOscarNetworkLine( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_LINEREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // LINE_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + poFeature->SetField( 1, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 2, "OD", 3, "PN", 4, "LL", 5, + "RN", 6, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateBasedataPoint() */ +/************************************************************************/ + +static OGRFeature *TranslateBasedataPoint( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + // GEOM_ID + poFeature->SetField( 1, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 2, "PN", 3, "NU", 4, "CM", 5, + "UN", 6, "OR", 7, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateBasedataLine() */ +/************************************************************************/ + +static OGRFeature *TranslateBasedataLine( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_LINEREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // LINE_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + // GEOM_ID + poFeature->SetField( 2, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 1, "PN", 3, "NU", 4, "RB", 5, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateBoundarylineCollection() */ +/************************************************************************/ + +static OGRFeature *TranslateBoundarylineCollection( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) != 2 + || papoGroup[0]->GetType() != NRT_COLLECT + || papoGroup[1]->GetType() != NRT_ATTREC ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // COLL_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // NUM_PARTS + int nNumLinks = atoi(papoGroup[0]->GetField( 9, 12 )); + + if( nNumLinks > MAX_LINK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MAX_LINK exceeded in ntf_estlayers.cpp." ); + return poFeature; + } + + poFeature->SetField( 1, nNumLinks ); + + // POLY_ID + int i, anList[MAX_LINK]; + + for( i = 0; i < nNumLinks; i++ ) + anList[i] = atoi(papoGroup[0]->GetField( 15+i*8, 20+i*8 )); + + poFeature->SetField( 2, nNumLinks, anList ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "AI", 3, "OP", 4, "NM", 5, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateBoundarylinePoly() */ +/************************************************************************/ + +static OGRFeature *TranslateBoundarylinePoly( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ +/* ==================================================================== */ +/* Traditional POLYGON record groups. */ +/* ==================================================================== */ + if( CSLCount((char **) papoGroup) == 4 + && papoGroup[0]->GetType() == NRT_POLYGON + && papoGroup[1]->GetType() == NRT_ATTREC + && papoGroup[2]->GetType() == NRT_CHAIN + && papoGroup[3]->GetType() == NRT_GEOMETRY ) + { + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POLY_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // NUM_PARTS + int nNumLinks = atoi(papoGroup[2]->GetField( 9, 12 )); + + if( nNumLinks > MAX_LINK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MAX_LINK exceeded in ntf_estlayers.cpp." ); + return poFeature; + } + + poFeature->SetField( 4, nNumLinks ); + + // DIR + int i, anList[MAX_LINK]; + + for( i = 0; i < nNumLinks; i++ ) + anList[i] = atoi(papoGroup[2]->GetField( 19+i*7, 19+i*7 )); + + poFeature->SetField( 5, nNumLinks, anList ); + + // GEOM_ID_OF_LINK + for( i = 0; i < nNumLinks; i++ ) + anList[i] = atoi(papoGroup[2]->GetField( 13+i*7, 18+i*7 )); + + poFeature->SetField( 6, nNumLinks, anList ); + + // RingStart + int nRingList = 0; + poFeature->SetField( 7, 1, &nRingList ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 1, "PI", 2, "HA", 3, + NULL ); + + // Read point geometry + poFeature->SetGeometryDirectly( + poReader->ProcessGeometry(papoGroup[3])); + + // Try to assemble polygon geometry. + poReader->FormPolygonFromCache( poFeature ); + + return poFeature; + } + +/* ==================================================================== */ +/* CPOLYGON Group */ +/* ==================================================================== */ + +/* -------------------------------------------------------------------- */ +/* First we do validation of the grouping. */ +/* -------------------------------------------------------------------- */ + int iRec; + + for( iRec = 0; + papoGroup[iRec] != NULL && papoGroup[iRec+1] != NULL + && papoGroup[iRec]->GetType() == NRT_POLYGON + && papoGroup[iRec+1]->GetType() == NRT_CHAIN; + iRec += 2 ) {} + + if( CSLCount((char **) papoGroup) != iRec + 3 ) + return NULL; + + if( papoGroup[iRec]->GetType() != NRT_CPOLY + || papoGroup[iRec+1]->GetType() != NRT_ATTREC + || papoGroup[iRec+2]->GetType() != NRT_GEOMETRY ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Collect the chains for each of the rings, and just aggregate */ +/* these into the master list without any concept of where the */ +/* boundaries are. The boundary information will be emmitted */ +/* in the RingStart field. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + int nNumLink = 0; + int anDirList[MAX_LINK*2], anGeomList[MAX_LINK*2]; + int anRingStart[MAX_LINK], nRings = 0; + + for( iRec = 0; + papoGroup[iRec] != NULL && papoGroup[iRec+1] != NULL + && papoGroup[iRec]->GetType() == NRT_POLYGON + && papoGroup[iRec+1]->GetType() == NRT_CHAIN; + iRec += 2 ) + { + int i, nLineCount; + + nLineCount = atoi(papoGroup[iRec+1]->GetField(9,12)); + + anRingStart[nRings++] = nNumLink; + + for( i = 0; i < nLineCount && nNumLink < MAX_LINK*2; i++ ) + { + anDirList[nNumLink] = + atoi(papoGroup[iRec+1]->GetField( 19+i*7, 19+i*7 )); + anGeomList[nNumLink] = + atoi(papoGroup[iRec+1]->GetField( 13+i*7, 18+i*7 )); + nNumLink++; + } + + if( nNumLink == MAX_LINK*2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MAX_LINK exceeded in ntf_estlayers.cpp." ); + + delete poFeature; + return NULL; + } + } + + // NUM_PART + poFeature->SetField( 4, nNumLink ); + + // DIR + poFeature->SetField( 5, nNumLink, anDirList ); + + // GEOM_ID_OF_LINK + poFeature->SetField( 6, nNumLink, anGeomList ); + + // RingStart + poFeature->SetField( 7, nRings, anRingStart ); + + +/* -------------------------------------------------------------------- */ +/* collect information for whole complex polygon. */ +/* -------------------------------------------------------------------- */ + // POLY_ID + poFeature->SetField( 0, atoi(papoGroup[iRec]->GetField( 3, 8 )) ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 1, "PI", 2, "HA", 3, + NULL ); + + // point geometry for seed. + poFeature->SetGeometryDirectly( + poReader->ProcessGeometry(papoGroup[iRec+2])); + + // Try to assemble polygon geometry. + poReader->FormPolygonFromCache( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateBoundarylineLink() */ +/************************************************************************/ + +static OGRFeature *TranslateBoundarylineLink( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) != 2 + || papoGroup[0]->GetType() != NRT_GEOMETRY + || papoGroup[1]->GetType() != NRT_ATTREC ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[0], + &nGeomId)); + + // GEOM_ID + poFeature->SetField( 0, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 1, "LK", 2, "HW", 3, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateBL2000Poly() */ +/************************************************************************/ + +static OGRFeature *TranslateBL2000Poly( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ +/* ==================================================================== */ +/* Traditional POLYGON record groups. */ +/* ==================================================================== */ + if( CSLCount((char **) papoGroup) == 3 + && papoGroup[0]->GetType() == NRT_POLYGON + && papoGroup[1]->GetType() == NRT_ATTREC + && papoGroup[2]->GetType() == NRT_CHAIN ) + { + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POLY_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // NUM_PARTS + int nNumLinks = atoi(papoGroup[2]->GetField( 9, 12 )); + + if( nNumLinks > MAX_LINK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MAX_LINK exceeded in ntf_estlayers.cpp." ); + + return poFeature; + } + + poFeature->SetField( 3, nNumLinks ); + + // DIR + int i, anList[MAX_LINK]; + + for( i = 0; i < nNumLinks; i++ ) + anList[i] = atoi(papoGroup[2]->GetField( 19+i*7, 19+i*7 )); + + poFeature->SetField( 4, nNumLinks, anList ); + + // GEOM_ID_OF_LINK + for( i = 0; i < nNumLinks; i++ ) + anList[i] = atoi(papoGroup[2]->GetField( 13+i*7, 18+i*7 )); + + poFeature->SetField( 5, nNumLinks, anList ); + + // RingStart + int nRingList = 0; + poFeature->SetField( 6, 1, &nRingList ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "PI", 1, "HA", 2, + NULL ); + + // Try to assemble polygon geometry. + poReader->FormPolygonFromCache( poFeature ); + + return poFeature; + } + +/* ==================================================================== */ +/* CPOLYGON Group */ +/* ==================================================================== */ + +/* -------------------------------------------------------------------- */ +/* First we do validation of the grouping. */ +/* -------------------------------------------------------------------- */ + int iRec; + + for( iRec = 0; + papoGroup[iRec] != NULL && papoGroup[iRec+1] != NULL + && papoGroup[iRec]->GetType() == NRT_POLYGON + && papoGroup[iRec+1]->GetType() == NRT_CHAIN; + iRec += 2 ) {} + + if( CSLCount((char **) papoGroup) != iRec + 2 ) + return NULL; + + if( papoGroup[iRec]->GetType() != NRT_CPOLY + || papoGroup[iRec+1]->GetType() != NRT_ATTREC ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Collect the chains for each of the rings, and just aggregate */ +/* these into the master list without any concept of where the */ +/* boundaries are. The boundary information will be emmitted */ +/* in the RingStart field. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + int nNumLink = 0; + int anDirList[MAX_LINK*2], anGeomList[MAX_LINK*2]; + int anRingStart[MAX_LINK], nRings = 0; + + for( iRec = 0; + papoGroup[iRec] != NULL && papoGroup[iRec+1] != NULL + && papoGroup[iRec]->GetType() == NRT_POLYGON + && papoGroup[iRec+1]->GetType() == NRT_CHAIN; + iRec += 2 ) + { + int i, nLineCount; + + nLineCount = atoi(papoGroup[iRec+1]->GetField(9,12)); + + anRingStart[nRings++] = nNumLink; + + for( i = 0; i < nLineCount && nNumLink < MAX_LINK*2; i++ ) + { + anDirList[nNumLink] = + atoi(papoGroup[iRec+1]->GetField( 19+i*7, 19+i*7 )); + anGeomList[nNumLink] = + atoi(papoGroup[iRec+1]->GetField( 13+i*7, 18+i*7 )); + nNumLink++; + } + + if( nNumLink == MAX_LINK*2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MAX_LINK exceeded in ntf_estlayers.cpp." ); + + delete poFeature; + return NULL; + } + } + + // NUM_PART + poFeature->SetField( 3, nNumLink ); + + // DIR + poFeature->SetField( 4, nNumLink, anDirList ); + + // GEOM_ID_OF_LINK + poFeature->SetField( 5, nNumLink, anGeomList ); + + // RingStart + poFeature->SetField( 6, nRings, anRingStart ); + + +/* -------------------------------------------------------------------- */ +/* collect information for whole complex polygon. */ +/* -------------------------------------------------------------------- */ + // POLY_ID + poFeature->SetField( 0, atoi(papoGroup[iRec]->GetField( 3, 8 )) ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "PI", 1, "HA", 2, + NULL ); + + // Try to assemble polygon geometry. + poReader->FormPolygonFromCache( poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateBL2000Link() */ +/************************************************************************/ + +static OGRFeature *TranslateBL2000Link( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) != 3 + || papoGroup[0]->GetType() != NRT_LINEREC + || papoGroup[1]->GetType() != NRT_GEOMETRY + || papoGroup[2]->GetType() != NRT_ATTREC ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // LINE_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + // GEOM_ID + poFeature->SetField( 1, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 2, "LK", 3, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateBL2000Collection() */ +/************************************************************************/ + +static OGRFeature *TranslateBL2000Collection( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_COLLECT + || papoGroup[1]->GetType() != NRT_ATTREC ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // COLL_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // NUM_PARTS + int nNumLinks = atoi(papoGroup[0]->GetField( 9, 12 )); + + if( nNumLinks > MAX_LINK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MAX_LINK exceeded in ntf_estlayers.cpp." ); + + return poFeature; + } + + poFeature->SetField( 1, nNumLinks ); + + // POLY_ID / COLL_ID_REFS + int i, anList[MAX_LINK], anCollList[MAX_LINK]; + int nPolys=0, nCollections=0; + + for( i = 0; i < nNumLinks; i++ ) + { + if( atoi(papoGroup[0]->GetField( 13+i*8, 14+i*8 )) == 34 ) + anCollList[nCollections++] = + atoi(papoGroup[0]->GetField( 15+i*8, 20+i*8 )); + else + anList[nPolys++] = + atoi(papoGroup[0]->GetField( 15+i*8, 20+i*8 )); + } + + poFeature->SetField( 2, nPolys, anList ); + poFeature->SetField( 10, nCollections, anCollList ); + + // Attributes + // Node that _CODE_DESC values are automatically applied if + // the target fields exist. + poReader->ApplyAttributeValues( poFeature, papoGroup, + "AI", 3, "OP", 4, "NM", 5, "TY", 6, + "AC", 7, "NB", 8, "NA", 9, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateMeridianPoint() */ +/************************************************************************/ + +static OGRFeature *TranslateMeridianPoint( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + // GEOM_ID + poFeature->SetField( 1, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 2, "PN", 3, "OS", 4, "JN", 5, + "RT", 6, "SI", 7, "PI", 8, "NM", 9, + "DA", 10, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateMeridianLine() */ +/************************************************************************/ + +static OGRFeature *TranslateMeridianLine( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_LINEREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // LINE_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + // GEOM_ID + poFeature->SetField( 2, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 1, "OM", 3, "RN", 4, "TR", 5, + "RI", 6, "LC", 7, "RC", 8, "LD", 9, + "RD", 10, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateMeridian2Point() */ +/************************************************************************/ + +static OGRFeature *TranslateMeridian2Point( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + // GEOM_ID + poFeature->SetField( 1, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 2, "PN", 3, "OD", 4, "PO", 5, + "JN", 6, "RT", 7, "SN", 8, "SI", 9, + "PI", 10, "NM", 11, "DA", 12, + "WA", 13, "HT", 14, "FA", 15, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateMeridian2Line() */ +/************************************************************************/ + +static OGRFeature *TranslateMeridian2Line( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_LINEREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // LINE_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + // GEOM_ID + poFeature->SetField( 2, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 1, "OD", 3, "PO", 4, "RN", 5, + "TR", 6, "PN", 7, "RI", 8, "LC", 9, + "RC", 10, "LD", 11, "RD", 12, "WI", 14, + NULL ); + + + return poFeature; +} + +/************************************************************************/ +/* TranslateStrategiNode() */ +/* */ +/* Also used for Meridian, Oscar and BaseData.GB nodes. */ +/************************************************************************/ + +static OGRFeature *TranslateStrategiNode( CPL_UNUSED NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) != 1 + || papoGroup[0]->GetType() != NRT_NODEREC ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // NODE_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // GEOM_ID_OF_POINT + poFeature->SetField( 1, atoi(papoGroup[0]->GetField( 9, 14 )) ); + + // NUM_LINKS + int nNumLinks = atoi(papoGroup[0]->GetField( 15, 18 )); + + if( nNumLinks > MAX_LINK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MAX_LINK exceeded in ntf_estlayers.cpp." ); + + return poFeature; + } + + poFeature->SetField( 2, nNumLinks ); + + // DIR + int i, anList[MAX_LINK]; + + for( i = 0; i < nNumLinks; i++ ) + anList[i] = atoi(papoGroup[0]->GetField( 19+i*12, 19+i*12 )); + + poFeature->SetField( 3, nNumLinks, anList ); + + // GEOM_ID_OF_POINT + for( i = 0; i < nNumLinks; i++ ) + anList[i] = atoi(papoGroup[0]->GetField( 19+i*12+1, 19+i*12+6 )); + + poFeature->SetField( 4, nNumLinks, anList ); + + // LEVEL + for( i = 0; i < nNumLinks; i++ ) + anList[i] = atoi(papoGroup[0]->GetField( 19+i*12+11, 19+i*12+11 )); + + poFeature->SetField( 5, nNumLinks, anList ); + + // ORIENT (optional) + if( EQUAL(poFeature->GetDefnRef()->GetFieldDefn(6)->GetNameRef(), + "ORIENT") ) + { + double adfList[MAX_LINK]; + + for( i = 0; i < nNumLinks; i++ ) + adfList[i] = + atoi(papoGroup[0]->GetField( 19+i*12+7, 19+i*12+10 )) * 0.1; + + poFeature->SetField( 6, nNumLinks, adfList ); + } + + return poFeature; +} + +/************************************************************************/ +/* TranslateStrategiText() */ +/* */ +/* Also used for Meridian, BaseData and Generic text. */ +/************************************************************************/ + +static OGRFeature *TranslateStrategiText( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 4 + || papoGroup[0]->GetType() != NRT_TEXTREC + || papoGroup[1]->GetType() != NRT_TEXTPOS + || papoGroup[2]->GetType() != NRT_TEXTREP + || papoGroup[3]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // FONT + poFeature->SetField( 2, atoi(papoGroup[2]->GetField( 9, 12 )) ); + + // TEXT_HT + poFeature->SetField( 3, atoi(papoGroup[2]->GetField( 13, 15 )) * 0.1 ); + + // DIG_POSTN + poFeature->SetField( 4, atoi(papoGroup[2]->GetField( 16, 16 )) ); + + // ORIENT + poFeature->SetField( 5, atoi(papoGroup[2]->GetField( 17, 20 )) * 0.1 ); + + // TEXT_HT_GROUND + poFeature->SetField( 7, poFeature->GetFieldAsDouble(3) + * poReader->GetPaperToGround() ); + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[3])); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 1, "TX", 6, "DE", 8, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateStrategiPoint() */ +/************************************************************************/ + +static OGRFeature *TranslateStrategiPoint( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + // GEOM_ID + poFeature->SetField( 10, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 1, "PN", 2, "NU", 3, "RB", 4, + "RU", 5, "AN", 6, "AO", 7, "CM", 8, + "UN", 9, "DE", 11, "DN", 12, "FM", 13, + "GS", 14, "HI", 15, "HM", 16, "LO", 17, + "OR", 18, "OW", 19, "RJ", 20, "RL", 21, + "RM", 22, "RQ", 23, "RW", 24, "RZ", 25, + "UE", 26, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateStrategiLine() */ +/************************************************************************/ + +static OGRFeature *TranslateStrategiLine( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_LINEREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // LINE_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + int nGeomId; + + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1], + &nGeomId)); + + // GEOM_ID + poFeature->SetField( 3, nGeomId ); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "FC", 1, "PN", 2, "DE", 4, "FE", 5, + "FF", 6, "FI", 7, "FM", 8, "FP", 9, + "FR", 10, "FT", 11, "GS", 12, "NU", 13, + "TX", 14, + NULL ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateLandrangerPoint() */ +/************************************************************************/ + +static OGRFeature *TranslateLandrangerPoint( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) != 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // FEAT_CODE + poFeature->SetField( 1, papoGroup[0]->GetField( 17, 20 ) ); + + // HEIGHT + poFeature->SetField( 2, atoi(papoGroup[0]->GetField( 11, 16 )) ); + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1])); + + return poFeature; +} + +/************************************************************************/ +/* TranslateLandrangerLine() */ +/************************************************************************/ + +static OGRFeature *TranslateLandrangerLine( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) != 2 + || papoGroup[0]->GetType() != NRT_LINEREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // LINE_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // FEAT_CODE + poFeature->SetField( 1, papoGroup[0]->GetField( 17, 20 ) ); + + // HEIGHT + poFeature->SetField( 2, atoi(papoGroup[0]->GetField( 11, 16 )) ); + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1])); + + return poFeature; +} + +/************************************************************************/ +/* TranslateProfilePoint() */ +/************************************************************************/ + +static OGRFeature *TranslateProfilePoint( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || (papoGroup[1]->GetType() != NRT_GEOMETRY + && papoGroup[1]->GetType() != NRT_GEOMETRY3D) ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // FEAT_CODE + poFeature->SetField( 1, papoGroup[0]->GetField( 17, 20 ) ); + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1])); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "HT", 2, + NULL ); + + // Set HEIGHT/elevation + OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef(); + + if( poPoint != NULL && poPoint->getCoordinateDimension() == 3 ) + { + poFeature->SetField( 2, poPoint->getZ() ); + } + else if( poPoint != NULL ) + { + poFeature->SetField( 2, poFeature->GetFieldAsDouble(2) * 0.01 ); + poPoint->setZ( poFeature->GetFieldAsDouble(2) ); + } + + return poFeature; +} + +/************************************************************************/ +/* TranslateProfileLine() */ +/************************************************************************/ + +static OGRFeature *TranslateProfileLine( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_LINEREC + || (papoGroup[1]->GetType() != NRT_GEOMETRY + && papoGroup[1]->GetType() != NRT_GEOMETRY3D) ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // LINE_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // FEAT_CODE + poFeature->SetField( 1, papoGroup[0]->GetField( 17, 20 ) ); + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1])); + + // Attributes + poReader->ApplyAttributeValues( poFeature, papoGroup, + "HT", 2, + NULL ); + + // Set HEIGHT/elevation + OGRLineString *poLine = (OGRLineString *) poFeature->GetGeometryRef(); + + poFeature->SetField( 2, poFeature->GetFieldAsDouble(2) * 0.01 ); + if( poLine != NULL && poLine->getCoordinateDimension() == 2 ) + { + for( int i = 0; i < poLine->getNumPoints(); i++ ) + { + poLine->setPoint( i, poLine->getX(i), poLine->getY(i), + poFeature->GetFieldAsDouble(2) ); + } + } + else if( poLine != NULL ) + { + double dfAccum = 0.0; + + for( int i = 0; i < poLine->getNumPoints(); i++ ) + { + dfAccum += poLine->getZ(i); + } + poFeature->SetField( 2, dfAccum / poLine->getNumPoints() ); + } + + return poFeature; +} + +/************************************************************************/ +/* TranslateLandlinePoint() */ +/************************************************************************/ + +static OGRFeature *TranslateLandlinePoint( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // FEAT_CODE + poFeature->SetField( 1, papoGroup[0]->GetField( 17, 20 ) ); + + // ORIENT + poFeature->SetField( 2, atoi(papoGroup[0]->GetField( 11, 16 )) * 0.1 ); + + // DISTANCE + poReader->ApplyAttributeValues( poFeature, papoGroup, + "DT", 3, + NULL ); + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1])); + + // CHG_DATE (optional) + if( poFeature->GetFieldIndex("CHG_DATE") == 4 ) + { + poFeature->SetField( 4, papoGroup[0]->GetField( 23, 28 ) ); + } + + // CHG_TYPE (optional) + if( poFeature->GetFieldIndex("CHG_TYPE") == 5 ) + { + poFeature->SetField( 5, papoGroup[0]->GetField( 22, 22 ) ); + } + + return poFeature; +} + +/************************************************************************/ +/* TranslateLandlineLine() */ +/************************************************************************/ + +static OGRFeature *TranslateLandlineLine( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) != 2 + || papoGroup[0]->GetType() != NRT_LINEREC + || papoGroup[1]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // LINE_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // FEAT_CODE + poFeature->SetField( 1, papoGroup[0]->GetField( 17, 20 ) ); + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1])); + + // CHG_DATE (optional) + if( poFeature->GetFieldIndex("CHG_DATE") == 2 ) + { + poFeature->SetField( 2, papoGroup[0]->GetField( 23, 28 ) ); + } + + // CHG_TYPE (optional) + if( poFeature->GetFieldIndex("CHG_TYPE") == 3 ) + { + poFeature->SetField( 3, papoGroup[0]->GetField( 22, 22 ) ); + } + return poFeature; +} + +/************************************************************************/ +/* TranslateLandlineName() */ +/************************************************************************/ + +static OGRFeature *TranslateLandlineName( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) != 3 + || papoGroup[0]->GetType() != NRT_NAMEREC + || papoGroup[1]->GetType() != NRT_NAMEPOSTN + || papoGroup[2]->GetType() != NRT_GEOMETRY ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // NAME_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // TEXT_CODE + poFeature->SetField( 1, papoGroup[0]->GetField( 9, 12 ) ); + + // TEXT + int nNumChar = atoi(papoGroup[0]->GetField(13,14)); + poFeature->SetField( 2, papoGroup[0]->GetField( 15, 15+nNumChar-1) ); + + // FONT + poFeature->SetField( 3, atoi(papoGroup[1]->GetField( 3, 6 )) ); + + // TEXT_HT + poFeature->SetField( 4, atoi(papoGroup[1]->GetField(7,9)) * 0.1 ); + + // DIG_POSTN + poFeature->SetField( 5, atoi(papoGroup[1]->GetField(10,10)) ); + + // ORIENT + poFeature->SetField( 6, CPLAtof(papoGroup[1]->GetField( 11, 14 )) * 0.1 ); + + // TEXT_HT_GROUND + poFeature->SetField( 7, poFeature->GetFieldAsDouble(4) + * poReader->GetPaperToGround() ); + + // CHG_DATE (optional) + if( poFeature->GetFieldIndex("CHG_DATE") == 7 ) + { + poFeature->SetField( 8, papoGroup[0]->GetField( 15+nNumChar+2, + 15+nNumChar+2+5) ); + } + + // CHG_TYPE (optional) + if( poFeature->GetFieldIndex("CHG_TYPE") == 9 ) + { + poFeature->SetField( 9, papoGroup[0]->GetField( 15+nNumChar+1, + 15+nNumChar+1 ) ); + } + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[2])); + + return poFeature; +} + +/************************************************************************/ +/* EstablishLayer() */ +/* */ +/* Establish one layer based on a simplified description of the */ +/* fields to be present. */ +/************************************************************************/ + +void NTFFileReader::EstablishLayer( const char * pszLayerName, + OGRwkbGeometryType eGeomType, + NTFFeatureTranslator pfnTranslator, + int nLeadRecordType, + NTFGenericClass *poClass, + ... ) + +{ + va_list hVaArgs; + OGRFeatureDefn *poDefn; + OGRNTFLayer *poLayer; + +/* -------------------------------------------------------------------- */ +/* Does this layer already exist? If so, we do nothing */ +/* ... note that we don't check the definition. */ +/* -------------------------------------------------------------------- */ + poLayer = poDS->GetNamedLayer(pszLayerName); + +/* ==================================================================== */ +/* Create a new layer matching the request if we don't aleady */ +/* have one. */ +/* ==================================================================== */ + if( poLayer == NULL ) + { +/* -------------------------------------------------------------------- */ +/* Create a new feature definition. */ +/* -------------------------------------------------------------------- */ + poDefn = new OGRFeatureDefn( pszLayerName ); + poDefn->GetGeomFieldDefn(0)->SetSpatialRef(poDS->GetSpatialRef()); + poDefn->SetGeomType( eGeomType ); + poDefn->Reference(); + +/* -------------------------------------------------------------------- */ +/* Fetch definitions of each field in turn. */ +/* -------------------------------------------------------------------- */ + va_start(hVaArgs, poClass); + while( TRUE ) + { + const char *pszFieldName = va_arg(hVaArgs, const char *); + OGRFieldType eType; + int nWidth, nPrecision; + + if( pszFieldName == NULL ) + break; + + eType = (OGRFieldType) va_arg(hVaArgs, int); + nWidth = va_arg(hVaArgs, int); + nPrecision = va_arg(hVaArgs, int); + + OGRFieldDefn oFieldDefn( pszFieldName, eType ); + oFieldDefn.SetWidth( nWidth ); + oFieldDefn.SetPrecision( nPrecision ); + + poDefn->AddFieldDefn( &oFieldDefn ); + } + + va_end(hVaArgs); + +/* -------------------------------------------------------------------- */ +/* Add attributes collected in the generic class survey. */ +/* -------------------------------------------------------------------- */ + if( poClass != NULL ) + { + for( int iGAtt = 0; iGAtt < poClass->nAttrCount; iGAtt++ ) + { + const char *pszFormat = poClass->papszAttrFormats[iGAtt]; + OGRFieldDefn oFieldDefn( poClass->papszAttrNames[iGAtt], + OFTInteger ); + + if( EQUALN(pszFormat,"I",1) ) + { + oFieldDefn.SetType( OFTInteger ); + oFieldDefn.SetWidth( poClass->panAttrMaxWidth[iGAtt] ); + } + else if( EQUALN(pszFormat,"D",1) + || EQUALN(pszFormat,"A",1) ) + { + oFieldDefn.SetType( OFTString ); + oFieldDefn.SetWidth( poClass->panAttrMaxWidth[iGAtt] ); + } + else if( EQUALN(pszFormat,"R",1) ) + { + oFieldDefn.SetType( OFTReal ); + oFieldDefn.SetWidth( poClass->panAttrMaxWidth[iGAtt]+1 ); + if( pszFormat[2] == ',' ) + oFieldDefn.SetPrecision(atoi(pszFormat+3)); + else if( pszFormat[3] == ',' ) + oFieldDefn.SetPrecision(atoi(pszFormat+4)); + } + + poDefn->AddFieldDefn( &oFieldDefn ); + + /* + ** If this field can appear multiple times, create an + ** additional attribute to hold lists of values. This + ** is always created as a variable length string field. + */ + if( poClass->pabAttrMultiple[iGAtt] ) + { + char szName[128]; + + sprintf( szName, "%s_LIST", + poClass->papszAttrNames[iGAtt] ); + + OGRFieldDefn oFieldDefnL( szName, OFTString ); + + poDefn->AddFieldDefn( &oFieldDefnL ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Add the TILE_REF attribute. */ +/* -------------------------------------------------------------------- */ + OGRFieldDefn oTileID( "TILE_REF", OFTString ); + + oTileID.SetWidth( 10 ); + + poDefn->AddFieldDefn( &oTileID ); + +/* -------------------------------------------------------------------- */ +/* Create the layer, and give over to the data source object to */ +/* maintain. */ +/* -------------------------------------------------------------------- */ + poLayer = new OGRNTFLayer( poDS, poDefn, pfnTranslator ); + + poDS->AddLayer( poLayer ); + } + +/* -------------------------------------------------------------------- */ +/* Register this translator with this file reader for handling */ +/* the indicate record type. */ +/* -------------------------------------------------------------------- */ + apoTypeTranslation[nLeadRecordType] = poLayer; +} + +/************************************************************************/ +/* EstablishLayers() */ +/* */ +/* This method is responsible for creating any missing */ +/* OGRNTFLayers needed for the current product based on the */ +/* product name. */ +/* */ +/* NOTE: Any changes to the order of attribute fields in the */ +/* following EstablishLayer() calls must also result in updates */ +/* to the translate functions. Changes of names, widths and to */ +/* some extent types can be done without side effects. */ +/************************************************************************/ + +void NTFFileReader::EstablishLayers() + +{ + if( poDS == NULL || fp == NULL ) + return; + + if( GetProductId() == NPC_LANDLINE ) + { + EstablishLayer( "LANDLINE_POINT", wkbPoint, + TranslateLandlinePoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "ORIENT", OFTReal, 5, 1, + "DISTANCE", OFTReal, 6, 3, + NULL ); + + EstablishLayer( "LANDLINE_LINE", wkbLineString, + TranslateLandlineLine, NRT_LINEREC, NULL, + "LINE_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + NULL ); + + EstablishLayer( "LANDLINE_NAME", wkbPoint, + TranslateLandlineName, NRT_NAMEREC, NULL, + "NAME_ID", OFTInteger, 6, 0, + "TEXT_CODE", OFTString, 4, 0, + "TEXT", OFTString, 0, 0, + "FONT", OFTInteger, 4, 0, + "TEXT_HT", OFTReal, 4, 1, + "DIG_POSTN", OFTInteger, 1, 0, + "ORIENT", OFTReal, 5, 1, + "TEXT_HT_GROUND", OFTReal, 10, 3, + NULL ); + } + else if( GetProductId() == NPC_LANDLINE99 ) + { + EstablishLayer( "LANDLINE99_POINT", wkbPoint, + TranslateLandlinePoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "ORIENT", OFTReal, 5, 1, + "DISTANCE", OFTReal, 6, 3, + "CHG_DATE", OFTString, 6, 0, + "CHG_TYPE", OFTString, 1, 0, + NULL ); + + EstablishLayer( "LANDLINE99_LINE", wkbLineString, + TranslateLandlineLine, NRT_LINEREC, NULL, + "LINE_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "CHG_DATE", OFTString, 6, 0, + "CHG_TYPE", OFTString, 1, 0, + NULL ); + + EstablishLayer( "LANDLINE99_NAME", wkbPoint, + TranslateLandlineName, NRT_NAMEREC, NULL, + "NAME_ID", OFTInteger, 6, 0, + "TEXT_CODE", OFTString, 4, 0, + "TEXT", OFTString, 0, 0, + "FONT", OFTInteger, 4, 0, + "TEXT_HT", OFTReal, 4, 1, + "DIG_POSTN", OFTInteger, 1, 0, + "ORIENT", OFTReal, 5, 1, + "TEXT_HT_GROUND", OFTReal, 10, 3, + "CHG_DATE", OFTString, 6, 0, + "CHG_TYPE", OFTString, 1, 0, + NULL ); + } + else if( GetProductId() == NPC_LANDRANGER_CONT ) + { + EstablishLayer( "PANORAMA_POINT", wkbPoint, + TranslateLandrangerPoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "HEIGHT", OFTReal, 7, 2, + NULL ); + + EstablishLayer( "PANORAMA_CONTOUR", wkbLineString, + TranslateLandrangerLine, NRT_LINEREC, NULL, + "LINE_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "HEIGHT", OFTReal, 7, 2, + NULL ); + } + else if( GetProductId() == NPC_LANDFORM_PROFILE_CONT ) + { + EstablishLayer( "PROFILE_POINT", wkbPoint25D, + TranslateProfilePoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "HEIGHT", OFTReal, 7, 2, + NULL ); + + EstablishLayer( "PROFILE_LINE", wkbLineString25D, + TranslateProfileLine, NRT_LINEREC, NULL, + "LINE_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "HEIGHT", OFTReal, 7, 2, + NULL ); + } + else if( GetProductId() == NPC_STRATEGI ) + { + EstablishLayer( "STRATEGI_POINT", wkbPoint, + TranslateStrategiPoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "PROPER_NAME", OFTString, 0, 0, + "FEATURE_NUMBER", OFTString, 0, 0, + "RB", OFTString, 1, 0, + "RU", OFTString, 1, 0, + "AN", OFTString, 0, 0, + "AO", OFTString, 0, 0, + "COUNTY_NAME", OFTString, 0, 0, + "UNITARY_NAME", OFTString, 0, 0, + "GEOM_ID", OFTInteger, 6, 0, + "DATE", OFTInteger, 8, 0, + "DISTRICT_NAME", OFTString, 0, 0, + "FEATURE_NAME", OFTString, 0, 0, + "GIS", OFTString, 0, 0, + "HEIGHT_IMPERIAL", OFTInteger, 4, 0, + "HEIGHT_METRIC", OFTInteger, 4, 0, + "LOCATION", OFTInteger, 1, 0, + "ORIENTATION", OFTReal, 4, 1, + "OWNER", OFTString, 0, 0, + "RESTRICTION_NORTH", OFTString, 0, 0, + "RESTRICTION_SOUTH", OFTString, 0, 0, + "RESTRICTION_EAST", OFTString, 0, 0, + "RESTRICTION_WEST", OFTString, 0, 0, + "RESTRICTION_CLOCKWISE", OFTString, 0, 0, + "RESTRICTION_ANTICLOCKWISE", OFTString, 0, 0, + "USAGE", OFTInteger, 1, 0, + NULL ); + + EstablishLayer( "STRATEGI_LINE", wkbLineString, + TranslateStrategiLine, NRT_LINEREC, NULL, + "LINE_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "PROPER_NAME", OFTString, 0, 0, + "GEOM_ID", OFTInteger, 6, 0, + "DATE", OFTInteger, 8, 0, + "FERRY_ACCESS", OFTString, 0, 0, + "FERRY_FROM", OFTString, 0, 0, + "FERRY_TIME", OFTString, 0, 0, + "FEATURE_NAME", OFTString, 0, 0, + "FERRY_TYPE", OFTString, 0, 0, + "FERRY_RESTRICTIONS", OFTString, 0, 0, + "FERRY_TO", OFTString, 0, 0, + "GIS", OFTString, 0, 0, + "FEATURE_NUMBER", OFTString, 0, 0, + NULL ); + + EstablishLayer( "STRATEGI_TEXT", wkbPoint, + TranslateStrategiText, NRT_TEXTREC, NULL, + "TEXT_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "FONT", OFTInteger, 4, 0, + "TEXT_HT", OFTReal, 5, 1, + "DIG_POSTN", OFTInteger, 1, 0, + "ORIENT", OFTReal, 5, 1, + "TEXT", OFTString, 0, 0, + "TEXT_HT_GROUND", OFTReal, 10, 3, + "DATE", OFTInteger, 8, 0, + NULL ); + + EstablishLayer( "STRATEGI_NODE", wkbNone, + TranslateStrategiNode, NRT_NODEREC, NULL, + "NODE_ID", OFTInteger, 6, 0, + "GEOM_ID_OF_POINT", OFTInteger, 6, 0, + "NUM_LINKS", OFTInteger, 4, 0, + "DIR", OFTIntegerList, 1, 0, + "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, + "LEVEL", OFTIntegerList, 1, 0, + "ORIENT", OFTRealList, 5, 1, + NULL ); + } + else if( GetProductId() == NPC_MERIDIAN ) + { + EstablishLayer( "MERIDIAN_POINT", wkbPoint, + TranslateMeridianPoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "GEOM_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "PROPER_NAME", OFTString, 0, 0, + "OSMDR", OFTString, 13, 0, + "JUNCTION_NAME", OFTString, 0, 0, + "ROUNDABOUT", OFTString, 1, 0, + "STATION_ID", OFTString, 13, 0, + "GLOBAL_ID", OFTInteger, 6, 0, + "ADMIN_NAME", OFTString, 0, 0, + "DA_DLUA_ID", OFTString, 13, 0, + NULL ); + + EstablishLayer( "MERIDIAN_LINE", wkbLineString, + TranslateMeridianLine, NRT_LINEREC, NULL, + "LINE_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "GEOM_ID", OFTInteger, 6, 0, + "OSMDR", OFTString, 13, 0, + "ROAD_NUM", OFTString, 0, 0, + "TRUNK_ROAD", OFTString, 1, 0, + "RAIL_ID", OFTString, 13, 0, + "LEFT_COUNTY", OFTInteger, 6, 0, + "RIGHT_COUNTY", OFTInteger, 6, 0, + "LEFT_DISTRICT", OFTInteger, 6, 0, + "RIGHT_DISTRICT", OFTInteger, 6, 0, + NULL ); + + EstablishLayer( "MERIDIAN_TEXT", wkbPoint, + TranslateStrategiText, NRT_TEXTREC, NULL, + "TEXT_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "FONT", OFTInteger, 4, 0, + "TEXT_HT", OFTReal, 5, 1, + "DIG_POSTN", OFTInteger, 1, 0, + "ORIENT", OFTReal, 5, 1, + "TEXT", OFTString, 0, 0, + "TEXT_HT_GROUND", OFTReal, 10, 3, + NULL ); + + EstablishLayer( "MERIDIAN_NODE", wkbNone, + TranslateStrategiNode, NRT_NODEREC, NULL, + "NODE_ID", OFTInteger, 6, 0, + "GEOM_ID_OF_POINT", OFTInteger, 6, 0, + "NUM_LINKS", OFTInteger, 4, 0, + "DIR", OFTIntegerList, 1, 0, + "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, + "LEVEL", OFTIntegerList, 1, 0, + "ORIENT", OFTRealList, 5, 1, + NULL ); + } + else if( GetProductId() == NPC_MERIDIAN2 ) + { + EstablishLayer( "MERIDIAN2_POINT", wkbPoint, + TranslateMeridian2Point, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "GEOM_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "PROPER_NAME", OFTString, 0, 0, + "OSODR", OFTString, 13, 0, + "PARENT_OSODR", OFTString, 13, 0, + "JUNCTION_NAME", OFTString, 0, 0, + "ROUNDABOUT", OFTString, 1, 0, + "SETTLEMENT_NAME", OFTString, 0, 0, + "STATION_ID", OFTString, 13, 0, + "GLOBAL_ID", OFTInteger, 6, 0, + "ADMIN_NAME", OFTString, 0, 0, + "DA_DLUA_ID", OFTString, 13, 0, + "WATER_AREA", OFTString, 13, 0, + "HEIGHT", OFTInteger, 8, 0, + "FOREST_ID", OFTString, 13, 0, + NULL ); + + EstablishLayer( "MERIDIAN2_LINE", wkbLineString, + TranslateMeridian2Line, NRT_LINEREC, NULL, + "LINE_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "GEOM_ID", OFTInteger, 6, 0, + "OSODR", OFTString, 13, 0, + "PARENT_OSODR", OFTString, 13, 0, + "ROAD_NUM", OFTString, 0, 0, + "TRUNK_ROAD", OFTString, 1, 0, + "PROPER_NAME", OFTString, 0, 0, + "RAIL_ID", OFTString, 13, 0, + "LEFT_COUNTY", OFTInteger, 6, 0, + "RIGHT_COUNTY", OFTInteger, 6, 0, + "LEFT_DISTRICT", OFTInteger, 6, 0, + "RIGHT_DISTRICT", OFTInteger, 6, 0, + "WATER_LINK_ID", OFTString, 13, 0, + NULL ); + + EstablishLayer( "MERIDIAN2_TEXT", wkbPoint, + TranslateStrategiText, NRT_TEXTREC, NULL, + "TEXT_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "FONT", OFTInteger, 4, 0, + "TEXT_HT", OFTReal, 5, 1, + "DIG_POSTN", OFTInteger, 1, 0, + "ORIENT", OFTReal, 5, 1, + "TEXT", OFTString, 0, 0, + "TEXT_HT_GROUND", OFTReal, 10, 3, + NULL ); + + EstablishLayer( "MERIDIAN2_NODE", wkbNone, + TranslateStrategiNode, NRT_NODEREC, NULL, + "NODE_ID", OFTInteger, 6, 0, + "GEOM_ID_OF_POINT", OFTInteger, 6, 0, + "NUM_LINKS", OFTInteger, 4, 0, + "DIR", OFTIntegerList, 1, 0, + "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, + "LEVEL", OFTIntegerList, 1, 0, + "ORIENT", OFTRealList, 5, 1, + NULL ); + } + else if( GetProductId() == NPC_BOUNDARYLINE ) + { + EstablishLayer( "BOUNDARYLINE_LINK", wkbLineString, + TranslateBoundarylineLink, NRT_GEOMETRY, NULL, + "GEOM_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "GLOBAL_LINK_ID", OFTInteger, 10, 0, + "HWM_FLAG", OFTInteger, 1, 0, + NULL ); + + EstablishLayer( "BOUNDARYLINE_POLY", + bCacheLines ? wkbPolygon : wkbPoint, + TranslateBoundarylinePoly, NRT_POLYGON, NULL, + "POLY_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "GLOBAL_SEED_ID", OFTInteger, 6, 0, + "HECTARES", OFTReal, 9, 3, + "NUM_PARTS", OFTInteger, 4, 0, + "DIR", OFTIntegerList, 1, 0, + "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, + "RingStart", OFTIntegerList, 6, 0, + NULL ); + + EstablishLayer( "BOUNDARYLINE_COLLECTIONS", wkbNone, + TranslateBoundarylineCollection, NRT_COLLECT, NULL, + "COLL_ID", OFTInteger, 6, 0, + "NUM_PARTS", OFTInteger, 4, 0, + "POLY_ID", OFTIntegerList, 6, 0, + "ADMIN_AREA_ID", OFTInteger, 6, 0, + "OPCS_CODE", OFTString, 6, 0, + "ADMIN_NAME", OFTString, 0, 0, + NULL ); + } + else if( GetProductId() == NPC_BL2000 ) + { + EstablishLayer( "BL2000_LINK", wkbLineString, + TranslateBL2000Link, NRT_LINEREC, NULL, + "LINE_ID", OFTInteger, 6, 0, + "GEOM_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "GLOBAL_LINK_ID", OFTInteger, 10, 0, + NULL ); + EstablishLayer( "BL2000_POLY", + bCacheLines ? wkbPolygon : wkbNone, + TranslateBL2000Poly, NRT_POLYGON, NULL, + "POLY_ID", OFTInteger, 6, 0, + "GLOBAL_SEED_ID", OFTInteger, 6, 0, + "HECTARES", OFTReal, 12, 3, + "NUM_PARTS", OFTInteger, 4, 0, + "DIR", OFTIntegerList, 1, 0, + "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, + "RingStart", OFTIntegerList, 6, 0, + NULL ); + if( poDS->GetOption("CODELIST") != NULL + && EQUAL(poDS->GetOption("CODELIST"),"ON") ) + EstablishLayer( "BL2000_COLLECTIONS", wkbNone, + TranslateBL2000Collection, NRT_COLLECT, NULL, + "COLL_ID", OFTInteger, 6, 0, + "NUM_PARTS", OFTInteger, 4, 0, + "POLY_ID", OFTIntegerList, 6, 0, + "ADMIN_AREA_ID", OFTInteger, 6, 0, + "CENSUS_CODE", OFTString, 7, 0, + "ADMIN_NAME", OFTString, 0, 0, + "AREA_TYPE", OFTString, 2, 0, + "AREA_CODE", OFTString, 3, 0, + "NON_TYPE_CODE", OFTString, 3, 0, + "NON_INLAND_AREA", OFTReal, 12, 3, + "COLL_ID_REFS", OFTIntegerList, 6, 0, + "AREA_TYPE_DESC", OFTString, 0, 0, + "AREA_CODE_DESC", OFTString, 0, 0, + "NON_TYPE_CODE_DESC", OFTString, 0, 0, + NULL ); + else + EstablishLayer( "BL2000_COLLECTIONS", wkbNone, + TranslateBL2000Collection, NRT_COLLECT, NULL, + "COLL_ID", OFTInteger, 6, 0, + "NUM_PARTS", OFTInteger, 4, 0, + "POLY_ID", OFTIntegerList, 6, 0, + "ADMIN_AREA_ID", OFTInteger, 6, 0, + "CENSUS_CODE", OFTString, 7, 0, + "ADMIN_NAME", OFTString, 0, 0, + "AREA_TYPE", OFTString, 2, 0, + "AREA_CODE", OFTString, 3, 0, + "NON_TYPE_CODE", OFTString, 3, 0, + "NON_INLAND_AREA", OFTReal, 12, 3, + "COLL_ID_REFS", OFTIntegerList, 6, 0, + NULL ); + } + else if( GetProductId() == NPC_BASEDATA ) + { + EstablishLayer( "BASEDATA_POINT", wkbPoint, + TranslateBasedataPoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "GEOM_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "PROPER_NAME", OFTString, 0, 0, + "FEATURE_NUMBER", OFTString, 0, 0, + "COUNTY_NAME", OFTString, 0, 0, + "UNITARY_NAME", OFTString, 0, 0, + "ORIENT", OFTRealList, 5, 1, + NULL ); + + EstablishLayer( "BASEDATA_LINE", wkbLineString, + TranslateBasedataLine, NRT_LINEREC, NULL, + "LINE_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "GEOM_ID", OFTInteger, 6, 0, + "PROPER_NAME", OFTString, 0, 0, + "FEATURE_NUMBER", OFTString, 0, 0, + "RB", OFTString, 1, 0, + NULL ); + + EstablishLayer( "BASEDATA_TEXT", wkbPoint, + TranslateStrategiText, NRT_TEXTREC, NULL, + "TEXT_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "FONT", OFTInteger, 4, 0, + "TEXT_HT", OFTReal, 5, 1, + "DIG_POSTN", OFTInteger, 1, 0, + "ORIENT", OFTReal, 5, 1, + "TEXT", OFTString, 0, 0, + "TEXT_HT_GROUND", OFTReal, 10, 3, + NULL ); + + EstablishLayer( "BASEDATA_NODE", wkbNone, + TranslateStrategiNode, NRT_NODEREC, NULL, + "NODE_ID", OFTInteger, 6, 0, + "GEOM_ID_OF_POINT", OFTInteger, 6, 0, + "NUM_LINKS", OFTInteger, 4, 0, + "DIR", OFTIntegerList, 1, 0, + "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, + "LEVEL", OFTIntegerList, 1, 0, + "ORIENT", OFTRealList, 5, 1, + NULL ); + } + else if( GetProductId() == NPC_OSCAR_ASSET + || GetProductId() == NPC_OSCAR_TRAFFIC ) + { + EstablishLayer( "OSCAR_POINT", wkbPoint, + TranslateOscarPoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "GEOM_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "OSODR", OFTString, 13, 0, + "JUNCTION_NAME", OFTString, 0, 0, + "SETTLE_NAME", OFTString, 0, 0, + NULL ); + + EstablishLayer( "OSCAR_LINE", wkbLineString, + TranslateOscarLine, NRT_LINEREC, NULL, + "LINE_ID", OFTInteger, 6, 0, + "GEOM_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "OSODR", OFTString, 13, 0, + "PROPER_NAME", OFTString, 0, 0, + "LINE_LENGTH", OFTInteger, 5, 0, + "SOURCE", OFTString, 1, 0, + "FORM_OF_WAY", OFTString, 1, 0, + "ROAD_NUM", OFTString, 0, 0, + "TRUNK_ROAD", OFTString, 1, 0, + NULL ); + + EstablishLayer( "OSCAR_NODE", wkbNone, + TranslateStrategiNode, NRT_NODEREC, NULL, + "NODE_ID", OFTInteger, 6, 0, + "GEOM_ID_OF_POINT", OFTInteger, 6, 0, + "NUM_LINKS", OFTInteger, 4, 0, + "DIR", OFTIntegerList, 1, 0, + "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, + "LEVEL", OFTIntegerList, 1, 0, + NULL ); + + EstablishLayer( "OSCAR_COMMENT", wkbNone, + TranslateOscarComment, NRT_COMMENT, NULL, + "RECORD_TYPE", OFTInteger, 2, 0, + "RECORD_ID", OFTString, 13, 0, + "CHANGE_TYPE", OFTString, 1, 0, + NULL ); + } + else if( GetProductId() == NPC_OSCAR_ROUTE ) + { + EstablishLayer( "OSCAR_ROUTE_POINT", wkbPoint, + TranslateOscarRoutePoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "GEOM_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "OSODR", OFTString, 13, 0, + "JUNCTION_NAME", OFTString, 0, 0, + "SETTLE_NAME", OFTString, 0, 0, + "NUM_PARENTS", OFTInteger, 2, 0, + "PARENT_OSODR", OFTStringList, 13, 0, + "ROUNDABOUT", OFTString, 1, 0, + NULL ); + + EstablishLayer( "OSCAR_ROUTE_LINE", wkbLineString, + TranslateOscarRouteLine, NRT_LINEREC, NULL, + "LINE_ID", OFTInteger, 6, 0, + "GEOM_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "OSODR", OFTString, 13, 0, + "PROPER_NAME", OFTString, 0, 0, + "LINE_LENGTH", OFTInteger, 5, 0, + "ROAD_NUM", OFTString, 0, 0, + "TRUNK_ROAD", OFTString, 1, 0, + "NUM_PARENTS", OFTInteger, 2, 0, + "PARENT_OSODR", OFTStringList, 13, 0, + NULL ); + + EstablishLayer( "OSCAR_ROUTE_NODE", wkbNone, + TranslateStrategiNode, NRT_NODEREC, NULL, + "NODE_ID", OFTInteger, 6, 0, + "GEOM_ID_OF_POINT", OFTInteger, 6, 0, + "NUM_LINKS", OFTInteger, 4, 0, + "DIR", OFTIntegerList, 1, 0, + "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, + "LEVEL", OFTIntegerList, 1, 0, + NULL ); + + EstablishLayer( "OSCAR_COMMENT", wkbNone, + TranslateOscarComment, NRT_COMMENT, NULL, + "RECORD_TYPE", OFTInteger, 2, 0, + "RECORD_ID", OFTString, 13, 0, + "CHANGE_TYPE", OFTString, 1, 0, + NULL ); + } + else if( GetProductId() == NPC_OSCAR_NETWORK ) + { + EstablishLayer( "OSCAR_NETWORK_POINT", wkbPoint, + TranslateOscarNetworkPoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "GEOM_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "OSODR", OFTString, 13, 0, + "JUNCTION_NAME", OFTString, 0, 0, + "SETTLE_NAME", OFTString, 0, 0, + "ROUNDABOUT", OFTString, 1, 0, + NULL ); + + EstablishLayer( "OSCAR_NETWORK_LINE", wkbLineString, + TranslateOscarNetworkLine, NRT_LINEREC, NULL, + "LINE_ID", OFTInteger, 6, 0, + "GEOM_ID", OFTInteger, 6, 0, + "FEAT_CODE", OFTString, 4, 0, + "OSODR", OFTString, 13, 0, + "PROPER_NAME", OFTString, 0, 0, + "LINE_LENGTH", OFTInteger, 5, 0, + "ROAD_NUM", OFTString, 0, 0, + NULL ); + + EstablishLayer( "OSCAR_NETWORK_NODE", wkbNone, + TranslateStrategiNode, NRT_NODEREC, NULL, + "NODE_ID", OFTInteger, 6, 0, + "GEOM_ID_OF_POINT", OFTInteger, 6, 0, + "NUM_LINKS", OFTInteger, 4, 0, + "DIR", OFTIntegerList, 1, 0, + "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, + "LEVEL", OFTIntegerList, 1, 0, + NULL ); + + EstablishLayer( "OSCAR_COMMENT", wkbNone, + TranslateOscarComment, NRT_COMMENT, NULL, + "RECORD_TYPE", OFTInteger, 2, 0, + "RECORD_ID", OFTString, 13, 0, + "CHANGE_TYPE", OFTString, 1, 0, + NULL ); + } + else if( GetProductId() == NPC_ADDRESS_POINT ) + { + EstablishLayer( "ADDRESS_POINT", wkbPoint, + TranslateAddressPoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "OSAPR", OFTString, 18, 0, + "ORGANISATION_NAME", OFTString, 0, 0, + "DEPARTMENT_NAME", OFTString, 0, 0, + "PO_BOX", OFTString, 6, 0, + "SUBBUILDING_NAME", OFTString, 0, 0, + "BUILDING_NAME", OFTString, 0, 0, + "BUILDING_NUMBER", OFTInteger, 4, 0, + "DEPENDENT_THOROUGHFARE_NAME", OFTString, 0, 0, + "THOROUGHFARE_NAME", OFTString, 0, 0, + "DOUBLE_DEPENDENT_LOCALITY_NAME", OFTString, 0, 0, + "DEPENDENT_LOCALITY_NAME", OFTString, 0, 0, + "POST_TOWN_NAME", OFTString, 0, 0, + "COUNTY_NAME", OFTString, 0, 0, + "POSTCODE", OFTString, 7, 0, + "STATUS_FLAG", OFTString, 4, 0, + "RM_VERSION_DATE", OFTString, 8, 0, + "CHG_TYPE", OFTString, 1, 0, + "CHG_DATE", OFTString, 6, 0, + NULL ); + } + else if( GetProductId() == NPC_CODE_POINT ) + { + EstablishLayer( "CODE_POINT", wkbPoint, + TranslateCodePoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "UNIT_POSTCODE", OFTString, 7, 0, + "POSITIONAL_QUALITY", OFTInteger, 1, 0, + "PO_BOX_INDICATOR", OFTString, 1, 0, + "TOTAL_DELIVERY_POINTS", OFTInteger, 3, 0, + "DELIVERY_POINTS", OFTInteger, 3, 0, + "DOMESTIC_DELIVERY_POINTS", OFTInteger, 3, 0, + "NONDOMESTIC_DELIVERY_POINTS", OFTInteger, 3, 0, + "POBOX_DELIVERY_POINTS", OFTInteger, 3, 0, + "MATCHED_ADDRESS_PREMISES", OFTInteger, 3, 0, + "UNMATCHED_DELIVERY_POINTS", OFTInteger, 3, 0, + "RM_VERSION_DATA", OFTString, 8, 0, + NULL ); + } + else if( GetProductId() == NPC_CODE_POINT_PLUS ) + { + EstablishLayer( "CODE_POINT_PLUS", wkbPoint, + TranslateCodePoint, NRT_POINTREC, NULL, + "POINT_ID", OFTInteger, 6, 0, + "UNIT_POSTCODE", OFTString, 7, 0, + "POSITIONAL_QUALITY", OFTInteger, 1, 0, + "PO_BOX_INDICATOR", OFTString, 1, 0, + "TOTAL_DELIVERY_POINTS", OFTInteger, 3, 0, + "DELIVERY_POINTS", OFTInteger, 3, 0, + "DOMESTIC_DELIVERY_POINTS", OFTInteger, 3, 0, + "NONDOMESTIC_DELIVERY_POINTS", OFTInteger, 3, 0, + "POBOX_DELIVERY_POINTS", OFTInteger, 3, 0, + "MATCHED_ADDRESS_PREMISES", OFTInteger, 3, 0, + "UNMATCHED_DELIVERY_POINTS", OFTInteger, 3, 0, + "RM_VERSION_DATA", OFTString, 8, 0, + "NHS_REGIONAL_HEALTH_AUTHORITY", OFTString, 3, 0, + "NHS_HEALTH_AUTHORITY", OFTString, 3, 0, + "ADMIN_COUNTY", OFTString, 2, 0, + "ADMIN_DISTRICT", OFTString, 2, 0, + "ADMIN_WARD", OFTString, 2, 0, + NULL ); + } + else // generic case + { + CPLAssert( GetProductId() == NPC_UNKNOWN ); + + poDS->WorkupGeneric( this ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf_generic.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf_generic.cpp new file mode 100644 index 000000000..45d599831 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf_generic.cpp @@ -0,0 +1,986 @@ +/****************************************************************************** + * $Id: ntf_generic.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: NTF Translator + * Purpose: Handle NTF products that aren't recognised generically. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ntf.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ntf_generic.cpp 27959 2014-11-14 18:29:21Z rouault $"); + +#define MAX_LINK 5000 + +/************************************************************************/ +/* ==================================================================== */ +/* NTFGenericClass */ +/* */ +/* The NTFGenericClass class exists to hold aggregated */ +/* information for each type of record encountered in a set of */ +/* NTF files, primarily the list of attributes actually */ +/* encountered. */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* NTFGenericClass */ +/************************************************************************/ + +NTFGenericClass::NTFGenericClass() +{ + nFeatureCount = 0; + + b3D = FALSE; + nAttrCount = 0; + papszAttrNames = NULL; + papszAttrFormats = NULL; + panAttrMaxWidth = NULL; + pabAttrMultiple = NULL; +} + +/************************************************************************/ +/* ~NTFGenericClass */ +/************************************************************************/ + +NTFGenericClass::~NTFGenericClass() + +{ + CSLDestroy( papszAttrNames ); + CSLDestroy( papszAttrFormats ); + CPLFree( panAttrMaxWidth ); + CPLFree( pabAttrMultiple ); +} + +/************************************************************************/ +/* CheckAddAttr() */ +/* */ +/* Check if an attribute already exists. If not add it with */ +/* it's format. Note we don't check for format conflicts at */ +/* this time. */ +/************************************************************************/ + +void NTFGenericClass::CheckAddAttr( const char * pszName, + const char * pszFormat, + int nWidth ) + +{ + int iAttrOffset; + + if( EQUAL(pszName,"TX") ) + pszName = "TEXT"; + if( EQUAL(pszName,"FC") ) + pszName = "FEAT_CODE"; + + iAttrOffset = CSLFindString( papszAttrNames, pszName ); + + if( iAttrOffset == -1 ) + { + nAttrCount++; + + papszAttrNames = CSLAddString( papszAttrNames, pszName ); + papszAttrFormats = CSLAddString( papszAttrFormats, pszFormat ); + + panAttrMaxWidth = (int *) + CPLRealloc( panAttrMaxWidth, sizeof(int) * nAttrCount ); + + panAttrMaxWidth[nAttrCount-1] = nWidth; + + pabAttrMultiple = (int *) + CPLRealloc( pabAttrMultiple, sizeof(int) * nAttrCount ); + + pabAttrMultiple[nAttrCount-1] = FALSE; + } + else + { + if( panAttrMaxWidth[iAttrOffset] < nWidth ) + panAttrMaxWidth[iAttrOffset] = nWidth; + } +} + +/************************************************************************/ +/* SetMultiple() */ +/* */ +/* Mark this attribute as appearing multiple times on some */ +/* features. */ +/************************************************************************/ + +void NTFGenericClass::SetMultiple( const char *pszName ) + +{ + int iAttrOffset; + + if( EQUAL(pszName,"TX") ) + pszName = "TEXT"; + if( EQUAL(pszName,"FC") ) + pszName = "FEAT_CODE"; + + iAttrOffset = CSLFindString( papszAttrNames, pszName ); + if( iAttrOffset == -1 ) + return; + + pabAttrMultiple[iAttrOffset] = TRUE; +} + +/************************************************************************/ +/* WorkupGeneric() */ +/* */ +/* Scan a whole file, in order to build up a list of attributes */ +/* for the generic types. */ +/************************************************************************/ + +void OGRNTFDataSource::WorkupGeneric( NTFFileReader * poReader ) + +{ + NTFRecord **papoGroup = NULL; + + if( poReader->GetNTFLevel() > 2 ) + { + poReader->IndexFile(); + if( CPLGetLastErrorType() == CE_Failure ) + return; + } + else + poReader->Reset(); + +/* ==================================================================== */ +/* Read all record groups in the file. */ +/* ==================================================================== */ + while( TRUE ) + { +/* -------------------------------------------------------------------- */ +/* Read a record group */ +/* -------------------------------------------------------------------- */ + if( poReader->GetNTFLevel() > 2 ) + papoGroup = poReader->GetNextIndexedRecordGroup(papoGroup); + else + papoGroup = poReader->ReadRecordGroup(); + + if( papoGroup == NULL || papoGroup[0]->GetType() == 99 ) + break; + +/* -------------------------------------------------------------------- */ +/* Get the class corresponding to the anchor record. */ +/* -------------------------------------------------------------------- */ + NTFGenericClass *poClass = GetGClass( papoGroup[0]->GetType() ); + char **papszFullAttList = NULL; + + poClass->nFeatureCount++; + +/* -------------------------------------------------------------------- */ +/* Loop over constituent records collecting attributes. */ +/* -------------------------------------------------------------------- */ + for( int iRec = 0; papoGroup[iRec] != NULL; iRec++ ) + { + NTFRecord *poRecord = papoGroup[iRec]; + + switch( poRecord->GetType() ) + { + case NRT_ATTREC: + { + char **papszTypes, **papszValues; + + poReader->ProcessAttRec( poRecord, NULL, + &papszTypes, &papszValues ); + + for( int iAtt = 0; papszTypes[iAtt] != NULL; iAtt++ ) + { + NTFAttDesc *poAttDesc; + + poAttDesc = poReader->GetAttDesc( papszTypes[iAtt] ); + if( poAttDesc != NULL ) + { + poClass->CheckAddAttr( poAttDesc->val_type, + poAttDesc->finter, + strlen(papszValues[iAtt]) ); + } + + if( CSLFindString( papszFullAttList, + papszTypes[iAtt] ) == -1 ) + papszFullAttList = + CSLAddString( papszFullAttList, + papszTypes[iAtt] ); + else + poClass->SetMultiple( poAttDesc->val_type ); + } + + CSLDestroy( papszTypes ); + CSLDestroy( papszValues ); + } + break; + + case NRT_TEXTREP: + case NRT_NAMEPOSTN: + poClass->CheckAddAttr( "FONT", "I4", 4 ); + poClass->CheckAddAttr( "TEXT_HT", "R3,1", 3 ); + poClass->CheckAddAttr( "TEXT_HT_GROUND", "R9,3", 9 ); + poClass->CheckAddAttr( "TEXT_HT", "R3,1", 3 ); + poClass->CheckAddAttr( "DIG_POSTN", "I1", 1 ); + poClass->CheckAddAttr( "ORIENT", "R4,1", 4 ); + break; + + case NRT_NAMEREC: + poClass->CheckAddAttr( "TEXT", "A*", + atoi(poRecord->GetField(13,14)) ); + break; + + case NRT_GEOMETRY: + case NRT_GEOMETRY3D: + if( atoi(poRecord->GetField(3,8)) != 0 ) + poClass->CheckAddAttr( "GEOM_ID", "I6", 6 ); + if( poRecord->GetType() == NRT_GEOMETRY3D ) + poClass->b3D = TRUE; + break; + + case NRT_POINTREC: + case NRT_LINEREC: + if( poReader->GetNTFLevel() < 3 ) + { + NTFAttDesc *poAttDesc; + + poAttDesc = poReader->GetAttDesc(poRecord->GetField(9,10)); + if( poAttDesc != NULL ) + poClass->CheckAddAttr( poAttDesc->val_type, + poAttDesc->finter, 6 ); + + if( !EQUAL(poRecord->GetField(17,20)," ") ) + poClass->CheckAddAttr( "FEAT_CODE", "A4", 4 ); + } + break; + + default: + break; + } + } + + CSLDestroy( papszFullAttList ); + } + + if( GetOption("CACHING") != NULL + && EQUAL(GetOption("CACHING"),"OFF") ) + poReader->DestroyIndex(); + + poReader->Reset(); +} + +/************************************************************************/ +/* AddGenericAttributes() */ +/************************************************************************/ + +static void AddGenericAttributes( NTFFileReader * poReader, + NTFRecord **papoGroup, + OGRFeature * poFeature ) + +{ + char **papszTypes, **papszValues; + + if( !poReader->ProcessAttRecGroup( papoGroup, &papszTypes, &papszValues ) ) + return; + + for( int iAtt = 0; papszTypes != NULL && papszTypes[iAtt] != NULL; iAtt++ ) + { + int iField; + + if( EQUAL(papszTypes[iAtt],"TX") ) + iField = poFeature->GetFieldIndex("TEXT"); + else if( EQUAL(papszTypes[iAtt],"FC") ) + iField = poFeature->GetFieldIndex("FEAT_CODE"); + else + iField = poFeature->GetFieldIndex(papszTypes[iAtt]); + + if( iField == -1 ) + continue; + + poReader->ApplyAttributeValue( poFeature, iField, papszTypes[iAtt], + papszTypes, papszValues ); + +/* -------------------------------------------------------------------- */ +/* Do we have a corresponding list field we should be */ +/* accumulating this into? */ +/* -------------------------------------------------------------------- */ + char szListName[128]; + int iListField; + + sprintf( szListName, "%s_LIST", + poFeature->GetFieldDefnRef(iField)->GetNameRef() ); + iListField = poFeature->GetFieldIndex( szListName ); + +/* -------------------------------------------------------------------- */ +/* Yes, so perform processing similar to ApplyAttributeValue(), */ +/* and append to list value. */ +/* -------------------------------------------------------------------- */ + if( iListField != -1 ) + { + char *pszAttLongName, *pszAttValue, *pszCodeDesc; + + poReader->ProcessAttValue( papszTypes[iAtt], papszValues[iAtt], + &pszAttLongName, &pszAttValue, + &pszCodeDesc ); + + if( poFeature->IsFieldSet( iListField ) ) + { + poFeature->SetField( iListField, + CPLSPrintf( "%s,%s", + poFeature->GetFieldAsString( iListField ), + pszAttValue ) ); + } + else + { + poFeature->SetField( iListField, pszAttValue ); + } + } + } + + CSLDestroy( papszTypes ); + CSLDestroy( papszValues ); +} + +/************************************************************************/ +/* TranslateGenericNode() */ +/************************************************************************/ + +static OGRFeature *TranslateGenericNode( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_NODEREC + || (papoGroup[1]->GetType() != NRT_GEOMETRY + && papoGroup[1]->GetType() != NRT_GEOMETRY3D) ) + { + return NULL; + } + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // NODE_ID + poFeature->SetField( "NODE_ID", atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1])); + poFeature->SetField( "GEOM_ID", papoGroup[1]->GetField(3,8) ); + + // NUM_LINKS + int nLinkCount=0; + int *panLinks = NULL; + + if( papoGroup[0]->GetLength() > 18 ) + { + nLinkCount = atoi(papoGroup[0]->GetField(15,18)); + panLinks = (int *) CPLCalloc(sizeof(int),nLinkCount); + } + + poFeature->SetField( "NUM_LINKS", nLinkCount ); + + // GEOM_ID_OF_LINK + int iLink; + for( iLink = 0; iLink < nLinkCount; iLink++ ) + panLinks[iLink] = atoi(papoGroup[0]->GetField(20+iLink*12, + 25+iLink*12)); + + poFeature->SetField( "GEOM_ID_OF_LINK", nLinkCount, panLinks ); + + // DIR + for( iLink = 0; iLink < nLinkCount; iLink++ ) + panLinks[iLink] = atoi(papoGroup[0]->GetField(19+iLink*12, + 19+iLink*12)); + + poFeature->SetField( "DIR", nLinkCount, panLinks ); + + // should we add LEVEL and/or ORIENT? + + CPLFree( panLinks ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateGenericCollection() */ +/************************************************************************/ + +static OGRFeature *TranslateGenericCollection( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 1 + || papoGroup[0]->GetType() != NRT_COLLECT ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // COLL_ID + poFeature->SetField( "COLL_ID", atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // NUM_PARTS + int nPartCount=0; + int *panParts = NULL; + + if( papoGroup[0]->GetLength() > 18 ) + { + nPartCount = atoi(papoGroup[0]->GetField(9,12)); + panParts = (int *) CPLCalloc(sizeof(int),nPartCount); + } + + poFeature->SetField( "NUM_PARTS", nPartCount ); + + // TYPE + int iPart; + for( iPart = 0; iPart < nPartCount; iPart++ ) + panParts[iPart] = atoi(papoGroup[0]->GetField(13+iPart*8, + 14+iPart*8)); + + poFeature->SetField( "TYPE", nPartCount, panParts ); + + // ID + for( iPart = 0; iPart < nPartCount; iPart++ ) + panParts[iPart] = atoi(papoGroup[0]->GetField(15+iPart*8, + 20+iPart*8)); + + poFeature->SetField( "ID", nPartCount, panParts ); + + CPLFree( panParts ); + + // ATTREC Attributes + AddGenericAttributes( poReader, papoGroup, poFeature ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateGenericText() */ +/************************************************************************/ + +static OGRFeature *TranslateGenericText( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + int iRec; + + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_TEXTREC ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // TEXT_ID + poFeature->SetField( "TEXT_ID", atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + for( iRec = 0; papoGroup[iRec] != NULL; iRec++ ) + { + if( papoGroup[iRec]->GetType() == NRT_GEOMETRY + || papoGroup[iRec]->GetType() == NRT_GEOMETRY3D ) + { + poFeature->SetGeometryDirectly( + poReader->ProcessGeometry(papoGroup[iRec])); + poFeature->SetField( "GEOM_ID", papoGroup[iRec]->GetField(3,8) ); + break; + } + } + + // ATTREC Attributes + AddGenericAttributes( poReader, papoGroup, poFeature ); + + // TEXTREP information + for( iRec = 0; papoGroup[iRec] != NULL; iRec++ ) + { + NTFRecord *poRecord = papoGroup[iRec]; + + if( poRecord->GetType() == NRT_TEXTREP ) + { + poFeature->SetField( "FONT", atoi(poRecord->GetField(9,12)) ); + poFeature->SetField( "TEXT_HT", + atoi(poRecord->GetField(13,15)) * 0.1 ); + poFeature->SetField( "TEXT_HT_GROUND", + atoi(poRecord->GetField(13,15)) + * 0.1 * poReader->GetPaperToGround() ); + poFeature->SetField( "DIG_POSTN", + atoi(poRecord->GetField(16,16)) ); + poFeature->SetField( "ORIENT", + atoi(poRecord->GetField(17,20)) * 0.1 ); + break; + } + } + + return poFeature; +} + +/************************************************************************/ +/* TranslateGenericName() */ +/************************************************************************/ + +static OGRFeature *TranslateGenericName( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + int iRec; + + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_NAMEREC ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // NAME_ID + poFeature->SetField( "NAME_ID", atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // TEXT_CODE + poFeature->SetField( "TEXT_CODE", papoGroup[0]->GetField( 8, 12 ) ); + + // TEXT + int nNumChar = atoi(papoGroup[0]->GetField(13,14)); + + poFeature->SetField( "TEXT", papoGroup[0]->GetField( 15, 15+nNumChar-1)); + + // Geometry + for( iRec = 0; papoGroup[iRec] != NULL; iRec++ ) + { + if( papoGroup[iRec]->GetType() == NRT_GEOMETRY + || papoGroup[iRec]->GetType() == NRT_GEOMETRY3D ) + { + poFeature->SetGeometryDirectly( + poReader->ProcessGeometry(papoGroup[iRec])); + poFeature->SetField( "GEOM_ID", papoGroup[iRec]->GetField(3,8) ); + break; + } + } + + // ATTREC Attributes + AddGenericAttributes( poReader, papoGroup, poFeature ); + + // NAMEPOSTN information + for( iRec = 0; papoGroup[iRec] != NULL; iRec++ ) + { + NTFRecord *poRecord = papoGroup[iRec]; + + if( poRecord->GetType() == NRT_NAMEPOSTN ) + { + poFeature->SetField( "FONT", atoi(poRecord->GetField(3,6)) ); + poFeature->SetField( "TEXT_HT", + atoi(poRecord->GetField(7,9)) * 0.1 ); + poFeature->SetField( "TEXT_HT_GROUND", + atoi(poRecord->GetField(7,9)) + * 0.1 * poReader->GetPaperToGround() ); + poFeature->SetField( "DIG_POSTN", + atoi(poRecord->GetField(10,10)) ); + poFeature->SetField( "ORIENT", + atoi(poRecord->GetField(11,14)) * 0.1 ); + break; + } + } + + return poFeature; +} + +/************************************************************************/ +/* TranslateGenericPoint() */ +/************************************************************************/ + +static OGRFeature *TranslateGenericPoint( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_POINTREC + || (papoGroup[1]->GetType() != NRT_GEOMETRY + && papoGroup[1]->GetType() != NRT_GEOMETRY3D) ) + { + return NULL; + } + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POINT_ID + poFeature->SetField( "POINT_ID", atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1])); + poFeature->SetField( "GEOM_ID", papoGroup[1]->GetField(3,8) ); + + // ATTREC Attributes + AddGenericAttributes( poReader, papoGroup, poFeature ); + + // Handle singular attribute in pre-level 3 POINTREC. + if( poReader->GetNTFLevel() < 3 ) + { + char szValType[3]; + + strcpy( szValType, papoGroup[0]->GetField(9,10) ); + if( !EQUAL(szValType," ") ) + { + char *pszProcessedValue; + + if( poReader->ProcessAttValue(szValType, + papoGroup[0]->GetField(11,16), + NULL, &pszProcessedValue, NULL ) ) + poFeature->SetField(szValType, pszProcessedValue); + } + + if( !EQUAL(papoGroup[0]->GetField(17,20)," ") ) + { + poFeature->SetField("FEAT_CODE",papoGroup[0]->GetField(17,20)); + } + } + + return poFeature; +} + +/************************************************************************/ +/* TranslateGenericLine() */ +/************************************************************************/ + +static OGRFeature *TranslateGenericLine( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ + if( CSLCount((char **) papoGroup) < 2 + || papoGroup[0]->GetType() != NRT_LINEREC + || (papoGroup[1]->GetType() != NRT_GEOMETRY + && papoGroup[1]->GetType() != NRT_GEOMETRY3D) ) + return NULL; + + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // LINE_ID + poFeature->SetField( "LINE_ID", atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // Geometry + poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1])); + poFeature->SetField( "GEOM_ID", papoGroup[1]->GetField(3,8) ); + + // ATTREC Attributes + AddGenericAttributes( poReader, papoGroup, poFeature ); + + // Handle singular attribute in pre-level 3 LINEREC. + if( poReader->GetNTFLevel() < 3 ) + { + char szValType[3]; + + strcpy( szValType, papoGroup[0]->GetField(9,10) ); + if( !EQUAL(szValType," ") ) + { + char *pszProcessedValue; + + if( poReader->ProcessAttValue(szValType, + papoGroup[0]->GetField(11,16), + NULL, &pszProcessedValue, NULL ) ) + poFeature->SetField(szValType, pszProcessedValue); + } + + if( !EQUAL(papoGroup[0]->GetField(17,20)," ") ) + { + poFeature->SetField("FEAT_CODE",papoGroup[0]->GetField(17,20)); + } + } + + return poFeature; +} + +/************************************************************************/ +/* TranslateGenericPoly() */ +/************************************************************************/ + +static OGRFeature *TranslateGenericPoly( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ +/* ==================================================================== */ +/* Traditional POLYGON record groups. */ +/* ==================================================================== */ + if( CSLCount((char **) papoGroup) >= 2 + && papoGroup[0]->GetType() == NRT_POLYGON + && papoGroup[1]->GetType() == NRT_CHAIN ) + { + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // POLY_ID + poFeature->SetField( 0, atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // NUM_PARTS + int nNumLinks = atoi(papoGroup[1]->GetField( 9, 12 )); + + if( nNumLinks > MAX_LINK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "MAX_LINK exceeded in ntf_generic.cpp." ); + return poFeature; + } + + poFeature->SetField( "NUM_PARTS", nNumLinks ); + + // DIR + int i, anList[MAX_LINK]; + + for( i = 0; i < nNumLinks; i++ ) + anList[i] = atoi(papoGroup[1]->GetField( 19+i*7, 19+i*7 )); + + poFeature->SetField( "DIR", nNumLinks, anList ); + + // GEOM_ID_OF_LINK + for( i = 0; i < nNumLinks; i++ ) + anList[i] = atoi(papoGroup[1]->GetField( 13+i*7, 18+i*7 )); + + poFeature->SetField( "GEOM_ID_OF_LINK", nNumLinks, anList ); + + // RingStart + int nRingList = 0; + poFeature->SetField( "RingStart", 1, &nRingList ); + + // ATTREC Attributes + AddGenericAttributes( poReader, papoGroup, poFeature ); + + // Read point geometry + if( papoGroup[2] != NULL + && (papoGroup[2]->GetType() == NRT_GEOMETRY + || papoGroup[2]->GetType() == NRT_GEOMETRY3D) ) + { + poFeature->SetGeometryDirectly( + poReader->ProcessGeometry(papoGroup[2])); + poFeature->SetField( "GEOM_ID", papoGroup[2]->GetField(3,8) ); + } + + return poFeature; + } + + return NULL; +} + +/************************************************************************/ +/* TranslateGenericCPoly() */ +/************************************************************************/ + +static OGRFeature *TranslateGenericCPoly( NTFFileReader *poReader, + OGRNTFLayer *poLayer, + NTFRecord **papoGroup ) + +{ +/* -------------------------------------------------------------------- */ +/* First we do validation of the grouping. */ +/* -------------------------------------------------------------------- */ + if( papoGroup[0]->GetType() != NRT_CPOLY ) + return NULL; + + if( papoGroup[1] == NULL || + (papoGroup[1]->GetType() != NRT_GEOMETRY + && papoGroup[1]->GetType() != NRT_GEOMETRY3D) ) + return NULL; + + if( papoGroup[1] != NULL + && papoGroup[2]->GetType() != NRT_ATTREC ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* collect information for whole complex polygon. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = new OGRFeature( poLayer->GetLayerDefn() ); + + // CPOLY_ID + poFeature->SetField( "CPOLY_ID", atoi(papoGroup[0]->GetField( 3, 8 )) ); + + // ATTREC Attributes + AddGenericAttributes( poReader, papoGroup, poFeature ); + + // Read point geometry + if( papoGroup[1] != NULL + && (papoGroup[1]->GetType() == NRT_GEOMETRY + || papoGroup[1]->GetType() == NRT_GEOMETRY3D) ) + { + poFeature->SetGeometryDirectly( + poReader->ProcessGeometry(papoGroup[1])); + poFeature->SetField( "GEOM_ID", + atoi(papoGroup[1]->GetField(3,8)) ); + } + +/* -------------------------------------------------------------------- */ +/* Collect the chains for each of the rings, and just aggregate */ +/* these into the master list without any concept of where the */ +/* boundaries are. The boundary information will be emmitted */ +/* in the RingStart field. */ +/* -------------------------------------------------------------------- */ + int nNumLink = 0, iLink; + int anPolyId[MAX_LINK*2]; + + nNumLink = atoi(papoGroup[0]->GetField(9,12)); + for( iLink = 0; iLink < nNumLink; iLink++ ) + { + anPolyId[iLink] = atoi(papoGroup[0]->GetField(13 + iLink*7, + 18 + iLink*7)); + } + + // NUM_PARTS + poFeature->SetField( "NUM_PARTS", nNumLink ); + + // POLY_ID + poFeature->SetField( "POLY_ID", nNumLink, anPolyId ); + + return poFeature; +} + +/************************************************************************/ +/* EstablishGenericLayers() */ +/************************************************************************/ + +void OGRNTFDataSource::EstablishGenericLayers() + +{ + int iType; + +/* -------------------------------------------------------------------- */ +/* Pick an initial NTFFileReader to build the layers against. */ +/* -------------------------------------------------------------------- */ + for( int iFile = 0; iFile < nNTFFileCount; iFile++ ) + { + NTFFileReader *poPReader = NULL; + int bHasZ = FALSE; + + poPReader = papoNTFFileReader[iFile]; + if( poPReader->GetProductId() != NPC_UNKNOWN ) + continue; + +/* -------------------------------------------------------------------- */ +/* If any of the generic classes are 3D, then assume all our */ +/* geometry should be marked as 3D. */ +/* -------------------------------------------------------------------- */ + for( iType = 0; iType < 99; iType++ ) + { + NTFGenericClass *poClass = aoGenericClass + iType; + + if( poClass->nFeatureCount > 0 && poClass->b3D ) + bHasZ = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Create layers for all recognised layer types with features. */ +/* -------------------------------------------------------------------- */ + for( iType = 0; iType < 99; iType++ ) + { + NTFGenericClass *poClass = aoGenericClass + iType; + + if( poClass->nFeatureCount == 0 ) + continue; + + if( iType == NRT_POINTREC ) + { + poPReader-> + EstablishLayer( "GENERIC_POINT", + OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE), + TranslateGenericPoint, + NRT_POINTREC, poClass, + "POINT_ID", OFTInteger, 6, 0, + NULL ); + } + else if( iType == NRT_LINEREC ) + { + poPReader-> + EstablishLayer( "GENERIC_LINE", + OGR_GT_SetModifier(wkbLineString, bHasZ, FALSE), + TranslateGenericLine, + NRT_LINEREC, poClass, + "LINE_ID", OFTInteger, 6, 0, + NULL ); + } + else if( iType == NRT_TEXTREC ) + { + poPReader-> + EstablishLayer( "GENERIC_TEXT", + OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE), + TranslateGenericText, + NRT_TEXTREC, poClass, + "TEXT_ID", OFTInteger, 6, 0, + NULL ); + } + else if( iType == NRT_NAMEREC ) + { + poPReader-> + EstablishLayer( "GENERIC_NAME", + OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE), + TranslateGenericName, + NRT_NAMEREC, poClass, + "NAME_ID", OFTInteger, 6, 0, + NULL ); + } + else if( iType == NRT_NODEREC ) + { + poPReader-> + EstablishLayer( "GENERIC_NODE", + OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE), + TranslateGenericNode, + NRT_NODEREC, poClass, + "NODE_ID", OFTInteger, 6, 0, + "NUM_LINKS", OFTInteger, 4, 0, + "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, + "DIR", OFTIntegerList, 1, 0, + NULL ); + } + else if( iType == NRT_COLLECT ) + { + poPReader-> + EstablishLayer( "GENERIC_COLLECTION", wkbNone, + TranslateGenericCollection, + NRT_COLLECT, poClass, + "COLL_ID", OFTInteger, 6, 0, + "NUM_PARTS", OFTInteger, 4, 0, + "TYPE", OFTIntegerList, 2, 0, + "ID", OFTIntegerList, 6, 0, + NULL ); + } + else if( iType == NRT_POLYGON ) + { + poPReader-> + EstablishLayer( "GENERIC_POLY", + OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE), + TranslateGenericPoly, + NRT_POLYGON, poClass, + "POLY_ID", OFTInteger, 6, 0, + "NUM_PARTS", OFTInteger, 4, 0, + "DIR", OFTIntegerList, 1, 0, + "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, + "RingStart", OFTIntegerList, 6, 0, + NULL ); + } + else if( iType == NRT_CPOLY ) + { + poPReader-> + EstablishLayer( "GENERIC_CPOLY", + OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE), + TranslateGenericCPoly, + NRT_CPOLY, poClass, + "CPOLY_ID", OFTInteger, 6, 0, + "NUM_PARTS", OFTInteger, 4, 0, + "POLY_ID", OFTIntegerList, 1, 0, + NULL ); + } + } + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf_raster.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf_raster.cpp new file mode 100644 index 000000000..f77ea9408 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntf_raster.cpp @@ -0,0 +1,431 @@ +/****************************************************************************** + * $Id: ntf_raster.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: NTF Translator + * Purpose: Handle UK Ordnance Survey Raster DTM products. Includes some + * raster related methods from NTFFileReader and the implementation + * of OGRNTFRasterLayer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ntf.h" + +CPL_CVSID("$Id: ntf_raster.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* NTFFileReader Raster Methods */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* IsRasterProduct() */ +/************************************************************************/ + +int NTFFileReader::IsRasterProduct() + +{ + return GetProductId() == NPC_LANDRANGER_DTM + || GetProductId() == NPC_LANDFORM_PROFILE_DTM; +} + +/************************************************************************/ +/* EstablishRasterAccess() */ +/************************************************************************/ + +void NTFFileReader::EstablishRasterAccess() + +{ +/* -------------------------------------------------------------------- */ +/* Read the type 50 record. */ +/* -------------------------------------------------------------------- */ + NTFRecord *poRecord; + + while( (poRecord = ReadRecord()) != NULL + && poRecord->GetType() != NRT_GRIDHREC + && poRecord->GetType() != NRT_VTR ) + { + delete poRecord; + } + + if( poRecord->GetType() != NRT_GRIDHREC ) + { + delete poRecord; + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find GRIDHREC (type 50) record in what appears\n" + "to be an NTF Raster DTM product." ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Parse if LANDRANGER_DTM */ +/* -------------------------------------------------------------------- */ + if( GetProductId() == NPC_LANDRANGER_DTM ) + { + nRasterXSize = atoi(poRecord->GetField(13,16)); + nRasterYSize = atoi(poRecord->GetField(17,20)); + + // NOTE: unusual use of GeoTransform - the pixel origin is the + // bottom left corner! + adfGeoTransform[0] = atoi(poRecord->GetField(25,34)); + adfGeoTransform[1] = 50; + adfGeoTransform[2] = 0; + adfGeoTransform[3] = atoi(poRecord->GetField(35,44)); + adfGeoTransform[4] = 0; + adfGeoTransform[5] = 50; + + nRasterDataType = 3; /* GDT_Int16 */ + } + +/* -------------------------------------------------------------------- */ +/* Parse if LANDFORM_PROFILE_DTM */ +/* -------------------------------------------------------------------- */ + else if( GetProductId() == NPC_LANDFORM_PROFILE_DTM ) + { + nRasterXSize = atoi(poRecord->GetField(23,30)); + nRasterYSize = atoi(poRecord->GetField(31,38)); + + // NOTE: unusual use of GeoTransform - the pixel origin is the + // bottom left corner! + adfGeoTransform[0] = atoi(poRecord->GetField(13,17)) + + GetXOrigin(); + adfGeoTransform[1] = atoi(poRecord->GetField(39,42)); + adfGeoTransform[2] = 0; + adfGeoTransform[3] = atoi(poRecord->GetField(18,22)) + + GetYOrigin(); + adfGeoTransform[4] = 0; + adfGeoTransform[5] = atoi(poRecord->GetField(43,46)); + + nRasterDataType = 3; /* GDT_Int16 */ + } + +/* -------------------------------------------------------------------- */ +/* Initialize column offsets table. */ +/* -------------------------------------------------------------------- */ + delete poRecord; + + panColumnOffset = (long *) CPLCalloc(sizeof(long),nRasterXSize); + + GetFPPos( panColumnOffset+0, NULL ); + +/* -------------------------------------------------------------------- */ +/* Create an OGRSFLayer for this file readers raster points. */ +/* -------------------------------------------------------------------- */ + if( poDS != NULL ) + { + poRasterLayer = new OGRNTFRasterLayer( poDS, this ); + poDS->AddLayer( poRasterLayer ); + } +} + +/************************************************************************/ +/* ReadRasterColumn() */ +/************************************************************************/ + +CPLErr NTFFileReader::ReadRasterColumn( int iColumn, float *pafElev ) + +{ +/* -------------------------------------------------------------------- */ +/* If we don't already have the scanline offset of the previous */ +/* line, force reading of previous records to establish it. */ +/* -------------------------------------------------------------------- */ + if( panColumnOffset[iColumn] == 0 ) + { + int iPrev; + + for( iPrev = 0; iPrev < iColumn-1; iPrev++ ) + { + if( panColumnOffset[iPrev+1] == 0 ) + { + CPLErr eErr; + + eErr = ReadRasterColumn( iPrev, NULL ); + if( eErr != CE_None ) + return eErr; + } + } + } + +/* -------------------------------------------------------------------- */ +/* If the dataset isn't open, open it now. */ +/* -------------------------------------------------------------------- */ + if( GetFP() == NULL ) + Open(); + +/* -------------------------------------------------------------------- */ +/* Read requested record. */ +/* -------------------------------------------------------------------- */ + NTFRecord *poRecord; + + SetFPPos( panColumnOffset[iColumn], iColumn ); + poRecord = ReadRecord(); + + if( iColumn < nRasterXSize-1 ) + { + GetFPPos( panColumnOffset+iColumn+1, NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Handle LANDRANGER DTM columns. */ +/* -------------------------------------------------------------------- */ + if( pafElev != NULL && GetProductId() == NPC_LANDRANGER_DTM ) + { + double dfVScale, dfVOffset; + + dfVOffset = atoi(poRecord->GetField(56,65)); + dfVScale = atoi(poRecord->GetField(66,75)) * 0.001; + + for( int iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafElev[iPixel] = (float) (dfVOffset + dfVScale * + atoi(poRecord->GetField(84+iPixel*4,87+iPixel*4))); + } + } + +/* -------------------------------------------------------------------- */ +/* Handle PROFILE */ +/* -------------------------------------------------------------------- */ + else if( pafElev != NULL && GetProductId() == NPC_LANDFORM_PROFILE_DTM ) + { + for( int iPixel = 0; iPixel < nRasterXSize; iPixel++ ) + { + pafElev[iPixel] = (float) + (atoi(poRecord->GetField(19+iPixel*5,23+iPixel*5)) * GetZMult()); + } + } + + delete poRecord; + + return CE_None; +} + +/************************************************************************/ +/* ==================================================================== */ +/* OGRNTFRasterLayer */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* OGRNTFRasterLayer */ +/************************************************************************/ + +OGRNTFRasterLayer::OGRNTFRasterLayer( OGRNTFDataSource *poDSIn, + NTFFileReader * poReaderIn ) + +{ + char szLayerName[128]; + + sprintf( szLayerName, "DTM_%s", poReaderIn->GetTileName() ); + poFeatureDefn = new OGRFeatureDefn( szLayerName ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint25D ); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poDSIn->GetSpatialRef()); + + OGRFieldDefn oHeight( "HEIGHT", OFTReal ); + poFeatureDefn->AddFieldDefn( &oHeight ); + + poReader = poReaderIn; + poDS = poDSIn; + poFilterGeom = NULL; + + pafColumn = (float *) CPLCalloc(sizeof(float), + poReader->GetRasterYSize()); + iColumnOffset = -1; + iCurrentFC = 0; + +/* -------------------------------------------------------------------- */ +/* Check for DEM subsampling, and compute total feature count */ +/* accordingly. */ +/* -------------------------------------------------------------------- */ + if( poDS->GetOption( "DEM_SAMPLE" ) == NULL ) + nDEMSample = 1; + else + nDEMSample = MAX(1,atoi(poDS->GetOption("DEM_SAMPLE"))); + + nFeatureCount = (poReader->GetRasterXSize() / nDEMSample) + * (poReader->GetRasterYSize() / nDEMSample); +} + +/************************************************************************/ +/* ~OGRNTFRasterLayer() */ +/************************************************************************/ + +OGRNTFRasterLayer::~OGRNTFRasterLayer() + +{ + CPLFree( pafColumn ); + if( poFeatureDefn ) + poFeatureDefn->Release(); + + if( poFilterGeom != NULL ) + delete poFilterGeom; +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRNTFRasterLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) + +{ + if( poFilterGeom != NULL ) + { + delete poFilterGeom; + poFilterGeom = NULL; + } + + if( poGeomIn != NULL ) + poFilterGeom = poGeomIn->clone(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRNTFRasterLayer::ResetReading() + +{ + iCurrentFC = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRNTFRasterLayer::GetNextFeature() + +{ + if( iCurrentFC == 0 ) + iCurrentFC = 1; + else + { + int iReqColumn, iReqRow; + + iReqColumn = (iCurrentFC - 1) / poReader->GetRasterYSize(); + iReqRow = iCurrentFC - iReqColumn * poReader->GetRasterXSize() - 1; + + if( iReqRow + nDEMSample > poReader->GetRasterYSize() ) + { + iReqRow = 0; + iReqColumn += nDEMSample; + } + else + { + iReqRow += nDEMSample; + } + + iCurrentFC = iReqColumn * poReader->GetRasterYSize() + + iReqRow + 1; + } + + return GetFeature( (long) iCurrentFC ); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRNTFRasterLayer::GetFeature( GIntBig nFeatureId ) + +{ + int iReqColumn, iReqRow; + +/* -------------------------------------------------------------------- */ +/* Is this in the range of legal feature ids (pixels)? */ +/* -------------------------------------------------------------------- */ + if( nFeatureId < 1 + || nFeatureId > poReader->GetRasterXSize()*poReader->GetRasterYSize() ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we need to load a different column. */ +/* -------------------------------------------------------------------- */ + iReqColumn = ((int)nFeatureId - 1) / poReader->GetRasterYSize(); + iReqRow = (int)nFeatureId - iReqColumn * poReader->GetRasterXSize() - 1; + + if( iReqColumn != iColumnOffset ) + { + iColumnOffset = iReqColumn; + if( poReader->ReadRasterColumn( iReqColumn, pafColumn ) != CE_None ) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a corresponding feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + double *padfGeoTransform = poReader->GetGeoTransform(); + + poFeature->SetFID( nFeatureId ); + + // NOTE: unusual use of GeoTransform - the pixel origin is the + // bottom left corner! + poFeature->SetGeometryDirectly( + new OGRPoint( padfGeoTransform[0] + padfGeoTransform[1] * iReqColumn, + padfGeoTransform[3] + padfGeoTransform[5] * iReqRow, + pafColumn[iReqRow] ) ); + poFeature->SetField( 0, pafColumn[iReqRow] ); + + return poFeature; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRNTFRasterLayer::GetFeatureCount( CPL_UNUSED int bForce ) +{ + return nFeatureCount; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRNTFRasterLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntfdump.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntfdump.cpp new file mode 100644 index 000000000..0fa9335b0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntfdump.cpp @@ -0,0 +1,136 @@ +/****************************************************************************** + * $Id: ntfdump.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: NTF Translator + * Purpose: Simple test harnass. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ntf.h" +#include "cpl_vsi.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ntfdump.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +static void NTFDump( const char * pszFile, char **papszOptions ); +static void NTFCount( const char * pszFile ); + +/************************************************************************/ +/* main() */ +/************************************************************************/ + +int main( int argc, char ** argv ) + +{ + const char *pszMode = "-d"; + char **papszOptions = NULL; + + if( argc == 1 ) + printf( "Usage: ntfdump [-s n] [-g] [-d] [-c] [-codelist] files\n" ); + + for( int i = 1; i < argc; i++ ) + { + if( EQUAL(argv[i],"-g") ) + papszOptions = CSLSetNameValue( papszOptions, + "FORCE_GENERIC", "ON" ); + else if( EQUAL(argv[i],"-s") ) + { + papszOptions = CSLSetNameValue( papszOptions, + "DEM_SAMPLE", argv[++i] ); + } + else if( EQUAL(argv[i],"-codelist") ) + { + papszOptions = CSLSetNameValue( papszOptions, + "CODELIST", "ON" ); + } + else if( argv[i][0] == '-' ) + pszMode = argv[i]; + else if( EQUAL(pszMode,"-d") ) + NTFDump( argv[i], papszOptions ); + else if( EQUAL(pszMode,"-c") ) + NTFCount( argv[i] ); + } + + return 0; +} + +/************************************************************************/ +/* NTFCount() */ +/************************************************************************/ + +static void NTFCount( const char * pszFile ) + +{ + FILE *fp; + NTFRecord *poRecord = NULL; + int anCount[100], i; + + for( i = 0; i < 100; i++ ) + anCount[i] = 0; + + fp = VSIFOpen( pszFile, "r" ); + if( fp == NULL ) + return; + + do { + if( poRecord != NULL ) + delete poRecord; + + poRecord = new NTFRecord( fp ); + anCount[poRecord->GetType()]++; + + } while( poRecord->GetType() != 99 ); + + VSIFClose( fp ); + + printf( "\nReporting on: %s\n", pszFile ); + for( i = 0; i < 100; i++ ) + { + if( anCount[i] > 0 ) + printf( "Found %d records of type %d\n", anCount[i], i ); + } +} + +/************************************************************************/ +/* NTFDump() */ +/************************************************************************/ + +static void NTFDump( const char * pszFile, char **papszOptions ) + +{ + OGRFeature *poFeature; + OGRNTFDataSource oDS; + + oDS.SetOptionList( papszOptions ); + + if( !oDS.Open( pszFile ) ) + return; + + while( (poFeature = oDS.GetNextFeature()) != NULL ) + { + printf( "-------------------------------------\n" ); + poFeature->DumpReadable( stdout ); + delete poFeature; + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntffilereader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntffilereader.cpp new file mode 100644 index 000000000..f9fbce0ca --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntffilereader.cpp @@ -0,0 +1,2127 @@ +/****************************************************************************** + * $Id: ntffilereader.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: NTF Translator + * Purpose: NTFFileReader class implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ntf.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_api.h" + +CPL_CVSID("$Id: ntffilereader.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +static int DefaultNTFRecordGrouper( NTFFileReader *, NTFRecord **, + NTFRecord * ); + +#ifndef PI +# define PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* NTFFileReader */ +/************************************************************************/ + +NTFFileReader::NTFFileReader( OGRNTFDataSource * poDataSource ) + +{ + fp = NULL; + + nFCCount = 0; + papszFCNum = NULL; + papszFCName = NULL; + + nPreSavedPos = nPostSavedPos = 0; + nSavedFeatureId = nBaseFeatureId = 1; + nFeatureCount = -1; + poSavedRecord = NULL; + + nAttCount = 0; + pasAttDesc = NULL; + + pszTileName = NULL; + pszProduct = NULL; + pszPVName = NULL; + pszFilename = NULL; + + apoCGroup[0] = NULL; + + poDS = poDataSource; + + memset( apoTypeTranslation, 0, sizeof(apoTypeTranslation) ); + + nProduct = NPC_UNKNOWN; + + pfnRecordGrouper = DefaultNTFRecordGrouper; + + dfXYMult = 1.0; + dfZMult = 1.0; + dfXOrigin = 0; + dfYOrigin = 0; + nNTFLevel = 0; + dfTileXSize = 0; + dfTileYSize = 0; + + dfScale = 0.0; + dfPaperToGround = 0.0; + + nCoordWidth = 6; + nZWidth = 6; + + for( int i = 0; i < 100; i++ ) + { + anIndexSize[i] = 0; + apapoRecordIndex[i] = NULL; + } + + panColumnOffset = NULL; + poRasterLayer = NULL; + nRasterXSize = nRasterYSize = nRasterDataType = 1; + + bIndexBuilt = FALSE; + bIndexNeeded = FALSE; + + if( poDS->GetOption("CACHE_LINES") != NULL + && EQUAL(poDS->GetOption("CACHE_LINES"),"OFF") ) + bCacheLines = FALSE; + else + bCacheLines = TRUE; + + nLineCacheSize = 0; + papoLineCache = NULL; +} + +/************************************************************************/ +/* ~NTFFileReader() */ +/************************************************************************/ + +NTFFileReader::~NTFFileReader() + +{ + CacheClean(); + DestroyIndex(); + ClearDefs(); + CPLFree( pszFilename ); + CPLFree( panColumnOffset ); +} + +/************************************************************************/ +/* SetBaseFID() */ +/************************************************************************/ + +void NTFFileReader::SetBaseFID( long nNewBase ) + +{ + CPLAssert( nSavedFeatureId == 1 ); + + nBaseFeatureId = nNewBase; + nSavedFeatureId = nBaseFeatureId; +} + +/************************************************************************/ +/* ClearDefs() */ +/* */ +/* Clear attribute definitions and feature classes. All the */ +/* stuff that would have to be cleaned up by Open(), and the */ +/* destructor. */ +/************************************************************************/ + +void NTFFileReader::ClearDefs() + +{ + int i; + + Close(); + + ClearCGroup(); + + CSLDestroy( papszFCNum ); + papszFCNum = NULL; + CSLDestroy( papszFCName ); + papszFCName = NULL; + nFCCount = 0; + + for( i = 0; i < nAttCount; i++ ) + { + if( pasAttDesc[i].poCodeList != NULL ) + delete pasAttDesc[i].poCodeList; + } + + CPLFree( pasAttDesc ); + nAttCount = 0; + pasAttDesc = NULL; + + CPLFree( pszProduct ); + pszProduct = NULL; + + CPLFree( pszPVName ); + pszPVName = NULL; + + CPLFree( pszTileName ); + pszTileName = NULL; +} + +/************************************************************************/ +/* Close() */ +/* */ +/* Close the file, but don't wipe out our knowledge about this */ +/* file. */ +/************************************************************************/ + +void NTFFileReader::Close() + +{ + if( poSavedRecord != NULL ) + delete poSavedRecord; + poSavedRecord = NULL; + + nPreSavedPos = nPostSavedPos = 0; + nSavedFeatureId = nBaseFeatureId; + if( fp != NULL ) + { + VSIFClose( fp ); + fp = NULL; + } + + CacheClean(); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int NTFFileReader::Open( const char * pszFilenameIn ) + +{ + if( pszFilenameIn != NULL ) + { + ClearDefs(); + + CPLFree( pszFilename ); + pszFilename = CPLStrdup( pszFilenameIn ); + } + else + Close(); + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpen( pszFilename, "rb" ); + + // notdef: we should likely issue a proper CPL error message based + // based on errno here. + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open file `%s' for read access.\n", + pszFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If we are just reopening an existing file we will just scan */ +/* past the section header ... no need to reform all the definitions.*/ +/* -------------------------------------------------------------------- */ + if( pszFilenameIn == NULL ) + { + NTFRecord *poRecord; + + for( poRecord = new NTFRecord( fp ); + poRecord->GetType() != NRT_VTR && poRecord->GetType() != NRT_SHR; + poRecord = new NTFRecord( fp ) ) + { + delete poRecord; + } + + delete poRecord; + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Read the first record, and verify it is a proper volume header. */ +/* -------------------------------------------------------------------- */ + NTFRecord oVHR( fp ); + + if( oVHR.GetType() != NRT_VHR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "File `%s' appears to not be a UK NTF file.\n", + pszFilename ); + return FALSE; + } + + nNTFLevel = atoi(oVHR.GetField( 57, 57 )); + if( !( nNTFLevel >= 1 && nNTFLevel <= 5 ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid value : nNTFLevel = %d", nNTFLevel ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Read records till we get the section header. */ +/* -------------------------------------------------------------------- */ + NTFRecord *poRecord; + + for( poRecord = new NTFRecord( fp ); + poRecord->GetType() != NRT_VTR && poRecord->GetType() != NRT_SHR; + poRecord = new NTFRecord( fp ) ) + { +/* -------------------------------------------------------------------- */ +/* Handle feature class name records. */ +/* -------------------------------------------------------------------- */ + if( poRecord->GetType() == NRT_FCR ) + { + const char *pszData; + int iChar; + char szFCName[100]; + + nFCCount++; + + papszFCNum = CSLAddString( papszFCNum, poRecord->GetField(3,6) ); + + szFCName[0] = '\0'; + pszData = poRecord->GetData(); + + // CODE_COM + for( iChar = 15; pszData[iChar] == ' ' && iChar > 5; iChar-- ) {} + + if( iChar > 6 ) + strcat( szFCName, poRecord->GetField(7,iChar+1) ); + + // STCLASS + for( iChar = 35; pszData[iChar] == ' ' && iChar > 15; iChar-- ) {} + + if( iChar > 15 ) + { + if( strlen(szFCName) > 0 ) + strcat( szFCName, " : " ); + strcat( szFCName, poRecord->GetField(17,iChar+1) ); + } + + // FEATDES + for( iChar = 36; + pszData[iChar] != '\0' && pszData[iChar] != '\\'; + iChar++ ) {} + + if( iChar > 37 ) + { + if( strlen(szFCName) > 0 ) + strcat( szFCName, " : " ); + strcat( szFCName, poRecord->GetField(37,iChar) ); + } + + papszFCName = CSLAddString(papszFCName, szFCName ); + } + +/* -------------------------------------------------------------------- */ +/* Handle attribute description records. */ +/* -------------------------------------------------------------------- */ + else if( poRecord->GetType() == NRT_ADR ) + { + nAttCount++; + + pasAttDesc = (NTFAttDesc *) + CPLRealloc( pasAttDesc, sizeof(NTFAttDesc) * nAttCount ); + + ProcessAttDesc( poRecord, pasAttDesc + nAttCount - 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Handle attribute description records. */ +/* -------------------------------------------------------------------- */ + else if( poRecord->GetType() == NRT_CODELIST ) + { + NTFCodeList *poCodeList; + NTFAttDesc *psAttDesc; + + poCodeList = new NTFCodeList( poRecord ); + psAttDesc = GetAttDesc( poCodeList->szValType ); + if( psAttDesc == NULL ) + { + CPLDebug( "NTF", "Got CODELIST for %s without ATTDESC.", + poCodeList->szValType ); + delete poCodeList; + } + else + { + psAttDesc->poCodeList = poCodeList; + } + } + +/* -------------------------------------------------------------------- */ +/* Handle database header record. */ +/* -------------------------------------------------------------------- */ + else if( poRecord->GetType() == NRT_DHR ) + { + int iChar; + pszProduct = CPLStrdup(poRecord->GetField(3,22)); + for( iChar = strlen(pszProduct)-1; + iChar > 0 && pszProduct[iChar] == ' '; + pszProduct[iChar--] = '\0' ) {} + + pszPVName = CPLStrdup(poRecord->GetField(76+3,76+22)); + for( iChar = strlen(pszPVName)-1; + iChar > 0 && pszPVName[iChar] == ' '; + pszPVName[iChar--] = '\0' ) {} + + } + + delete poRecord; + } + +/* -------------------------------------------------------------------- */ +/* Did we fall off the end without finding what we were looking */ +/* for? */ +/* -------------------------------------------------------------------- */ + if( poRecord->GetType() == NRT_VTR ) + { + delete poRecord; + CPLError( CE_Failure, CPLE_AppDefined, + "Cound not find section header record in %s.\n", + pszFilename ); + return FALSE; + } + + if( pszProduct == NULL ) + { + delete poRecord; + CPLError( CE_Failure, CPLE_AppDefined, + "Cound not find product type in %s.\n", + pszFilename ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Classify the product type. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszProduct,"LAND-LINE",9) && CPLAtof(pszPVName+5) < 1.3 ) + nProduct = NPC_LANDLINE; + else if( EQUALN(pszProduct,"LAND-LINE",9) ) + nProduct = NPC_LANDLINE99; + else if( EQUAL(pszProduct,"OS_LANDRANGER_CONT") ) // Panorama + nProduct = NPC_LANDRANGER_CONT; + else if( EQUAL(pszProduct,"L-F_PROFILE_CON") ) // Panorama + nProduct = NPC_LANDFORM_PROFILE_CONT; + else if( EQUALN(pszProduct,"Strategi",8) ) + nProduct = NPC_STRATEGI; + else if( EQUALN(pszProduct,"Meridian_02",11) ) + nProduct = NPC_MERIDIAN2; + else if( EQUALN(pszProduct,"Meridian_01",11) ) + nProduct = NPC_MERIDIAN; + else if( EQUAL(pszProduct,NTF_BOUNDARYLINE) + && EQUALN(pszPVName,"A10N_FC",7) ) + nProduct = NPC_BOUNDARYLINE; + else if( EQUAL(pszProduct,NTF_BOUNDARYLINE) + && EQUALN(pszPVName,"A20N_FC",7) ) + nProduct = NPC_BL2000; + else if( EQUALN(pszProduct,"BaseData.GB",11) ) + nProduct = NPC_BASEDATA; + else if( EQUALN(pszProduct,"OSCAR_ASSET",11) ) + nProduct = NPC_OSCAR_ASSET; + else if( EQUALN(pszProduct,"OSCAR_TRAFF",11) ) + nProduct = NPC_OSCAR_TRAFFIC; + else if( EQUALN(pszProduct,"OSCAR_ROUTE",11) ) + nProduct = NPC_OSCAR_ROUTE; + else if( EQUALN(pszProduct,"OSCAR_NETWO",11) ) + nProduct = NPC_OSCAR_NETWORK; + else if( EQUALN(pszProduct,"ADDRESS_POI",11) ) + nProduct = NPC_ADDRESS_POINT; + else if( EQUALN(pszProduct,"CODE_POINT",10) ) + { + if( GetAttDesc( "RH" ) == NULL ) + nProduct = NPC_CODE_POINT; + else + nProduct = NPC_CODE_POINT_PLUS; + } + else if( EQUALN(pszProduct,"OS_LANDRANGER_DTM",17) ) + nProduct = NPC_LANDRANGER_DTM; + else if( EQUALN(pszProduct,"L-F_PROFILE_DTM",15) ) + nProduct = NPC_LANDFORM_PROFILE_DTM; + else if( EQUALN(pszProduct,"NEXTMap Britain DTM",19) ) + nProduct = NPC_LANDFORM_PROFILE_DTM; // Treat as landform + + if( poDS->GetOption("FORCE_GENERIC") != NULL + && !EQUAL(poDS->GetOption("FORCE_GENERIC"),"OFF") ) + nProduct = NPC_UNKNOWN; + + // No point in caching lines if there are no polygons. + if( nProduct != NPC_BOUNDARYLINE && nProduct != NPC_BL2000 ) + bCacheLines = FALSE; + +/* -------------------------------------------------------------------- */ +/* Handle the section header record. */ +/* -------------------------------------------------------------------- */ + nSavedFeatureId = nBaseFeatureId; + nStartPos = VSIFTell(fp); + + pszTileName = CPLStrdup(poRecord->GetField(3,12)); // SECT_REF + while( pszTileName[strlen(pszTileName)-1] == ' ' ) + pszTileName[strlen(pszTileName)-1] = '\0'; + + nCoordWidth = atoi(poRecord->GetField(15,19)); // XYLEN + if( nCoordWidth == 0 ) + nCoordWidth = 10; + + nZWidth = atoi(poRecord->GetField(31,35)); // ZLEN + if( nZWidth == 0 ) + nZWidth = 10; + + dfXYMult = atoi(poRecord->GetField(21,30)) / 1000.0; // XY_MULT + dfXOrigin = atoi(poRecord->GetField(47,56)); + dfYOrigin = atoi(poRecord->GetField(57,66)); + dfTileXSize = atoi(poRecord->GetField(23+74,32+74)); + dfTileYSize = atoi(poRecord->GetField(33+74,42+74)); + dfZMult = atoi(poRecord->GetField(37,46)) / 1000.0; + +/* -------------------------------------------------------------------- */ +/* Setup scale and transformation factor for text height. */ +/* -------------------------------------------------------------------- */ + if( poRecord->GetLength() >= 187 ) + dfScale = atoi(poRecord->GetField(148+31,148+39)); + else if( nProduct == NPC_STRATEGI ) + dfScale = 250000; + else if( nProduct == NPC_MERIDIAN || nProduct == NPC_MERIDIAN2 ) + dfScale = 100000; + else if( nProduct == NPC_LANDFORM_PROFILE_CONT ) + dfScale = 10000; + else if( nProduct == NPC_LANDRANGER_CONT ) + dfScale = 50000; + else if( nProduct == NPC_OSCAR_ASSET + || nProduct == NPC_OSCAR_TRAFFIC + || nProduct == NPC_OSCAR_NETWORK + || nProduct == NPC_OSCAR_ROUTE ) + dfScale = 10000; + else if( nProduct == NPC_BASEDATA ) + dfScale = 625000; + else if( nProduct == NPC_BOUNDARYLINE ) + dfScale = 10000; + else + dfScale = 10000; + + if( dfScale != 0.0 ) + dfPaperToGround = dfScale / 1000.0; + else + dfPaperToGround = 0.0; + + delete poRecord; + +/* -------------------------------------------------------------------- */ +/* Ensure we have appropriate layers defined. */ +/* -------------------------------------------------------------------- */ + CPLErrorReset(); + + if( !IsRasterProduct() ) + EstablishLayers(); + else + EstablishRasterAccess(); + + return CPLGetLastErrorType() != CE_Failure; +} + +/************************************************************************/ +/* DumpReadable() */ +/************************************************************************/ + +void NTFFileReader::DumpReadable( FILE *fpLog ) + +{ + fprintf( fpLog, "Tile Name = %s\n", pszTileName ); + fprintf( fpLog, "Product = %s\n", pszProduct ); + fprintf( fpLog, "NTFLevel = %d\n", nNTFLevel ); + fprintf( fpLog, "XYLEN = %d\n", nCoordWidth ); + fprintf( fpLog, "XY_MULT = %g\n", dfXYMult ); + fprintf( fpLog, "X_ORIG = %g\n", dfXOrigin ); + fprintf( fpLog, "Y_ORIG = %g\n", dfYOrigin ); + fprintf( fpLog, "XMAX = %g\n", dfTileXSize ); + fprintf( fpLog, "YMAX = %g\n", dfTileYSize ); +} + +/************************************************************************/ +/* ProcessGeometry() */ +/* */ +/* Drop duplicate vertices from line strings ... they mess up */ +/* FME's polygon handling sometimes. */ +/************************************************************************/ + +OGRGeometry *NTFFileReader::ProcessGeometry( NTFRecord * poRecord, + int * pnGeomId ) + +{ + int nGType, nNumCoord; + OGRGeometry *poGeometry = NULL; + + if( poRecord->GetType() == NRT_GEOMETRY3D ) + return ProcessGeometry3D( poRecord, pnGeomId ); + + else if( poRecord->GetType() != NRT_GEOMETRY ) + return NULL; + + nGType = atoi(poRecord->GetField(9,9)); // GTYPE + nNumCoord = atoi(poRecord->GetField(10,13)); // NUM_COORD + if( pnGeomId != NULL ) + *pnGeomId = atoi(poRecord->GetField(3,8)); // GEOM_ID + +/* -------------------------------------------------------------------- */ +/* Point */ +/* -------------------------------------------------------------------- */ + if( nGType == 1 ) + { + double dfX, dfY; + + dfX = atoi(poRecord->GetField(14,14+GetXYLen()-1)) * GetXYMult() + + GetXOrigin(); + dfY = atoi(poRecord->GetField(14+GetXYLen(),14+GetXYLen()*2-1)) + * GetXYMult() + GetYOrigin(); + + poGeometry = new OGRPoint( dfX, dfY ); + } + +/* -------------------------------------------------------------------- */ +/* Line (or arc) */ +/* -------------------------------------------------------------------- */ + else if( nGType == 2 || nGType == 3 || nGType == 4 ) + { + OGRLineString *poLine = new OGRLineString; + double dfX, dfY, dfXLast=0.0, dfYLast=0.0; + int iCoord, nOutCount = 0; + + poGeometry = poLine; + poLine->setNumPoints( nNumCoord ); + for( iCoord = 0; iCoord < nNumCoord; iCoord++ ) + { + int iStart = 14 + iCoord * (GetXYLen()*2+1); + + dfX = atoi(poRecord->GetField(iStart+0, + iStart+GetXYLen()-1)) + * GetXYMult() + GetXOrigin(); + dfY = atoi(poRecord->GetField(iStart+GetXYLen(), + iStart+GetXYLen()*2-1)) + * GetXYMult() + GetYOrigin(); + + if( iCoord == 0 ) + { + dfXLast = dfX; + dfYLast = dfY; + poLine->setPoint( nOutCount++, dfX, dfY ); + } + else if( dfXLast != dfX || dfYLast != dfY ) + { + dfXLast = dfX; + dfYLast = dfY; + poLine->setPoint( nOutCount++, dfX, dfY ); + } + } + poLine->setNumPoints( nOutCount ); + + CacheAddByGeomId( atoi(poRecord->GetField(3,8)), poLine ); + } + +/* -------------------------------------------------------------------- */ +/* Arc defined by three points on the arc. */ +/* -------------------------------------------------------------------- */ + else if( nGType == 5 && nNumCoord == 3 ) + { + double adfX[3], adfY[3]; + int iCoord; + + for( iCoord = 0; iCoord < nNumCoord; iCoord++ ) + { + int iStart = 14 + iCoord * (GetXYLen()*2+1); + + adfX[iCoord] = atoi(poRecord->GetField(iStart+0, + iStart+GetXYLen()-1)) + * GetXYMult() + GetXOrigin(); + adfY[iCoord] = atoi(poRecord->GetField(iStart+GetXYLen(), + iStart+GetXYLen()*2-1)) + * GetXYMult() + GetYOrigin(); + } + + poGeometry = NTFStrokeArcToOGRGeometry_Points( adfX[0], adfY[0], + adfX[1], adfY[1], + adfX[2], adfY[2], 72 ); + } + +/* -------------------------------------------------------------------- */ +/* Circle */ +/* -------------------------------------------------------------------- */ + else if( nGType == 7 ) + { + double dfCenterX, dfCenterY, dfArcX, dfArcY, dfRadius; + int iCenterStart = 14; + int iArcStart = 14 + 2 * GetXYLen() + 1; + + dfCenterX = atoi(poRecord->GetField(iCenterStart, + iCenterStart+GetXYLen()-1)) + * GetXYMult() + GetXOrigin(); + dfCenterY = atoi(poRecord->GetField(iCenterStart+GetXYLen(), + iCenterStart+GetXYLen()*2-1)) + * GetXYMult() + GetYOrigin(); + + dfArcX = atoi(poRecord->GetField(iArcStart, + iArcStart+GetXYLen()-1)) + * GetXYMult() + GetXOrigin(); + dfArcY = atoi(poRecord->GetField(iArcStart+GetXYLen(), + iArcStart+GetXYLen()*2-1)) + * GetXYMult() + GetYOrigin(); + + dfRadius = sqrt( (dfCenterX - dfArcX) * (dfCenterX - dfArcX) + + (dfCenterY - dfArcY) * (dfCenterY - dfArcY) ); + + poGeometry = NTFStrokeArcToOGRGeometry_Angles( dfCenterX, dfCenterY, + dfRadius, + 0.0, 360.0, + 72 ); + } + + else + { + fprintf( stderr, "GType = %d\n", nGType ); + CPLAssert( FALSE ); + } + + if( poGeometry != NULL ) + poGeometry->assignSpatialReference( poDS->GetSpatialRef() ); + + return poGeometry; +} + +/************************************************************************/ +/* ProcessGeometry3D() */ +/************************************************************************/ + +OGRGeometry *NTFFileReader::ProcessGeometry3D( NTFRecord * poRecord, + int * pnGeomId ) + +{ + int nGType, nNumCoord; + OGRGeometry *poGeometry = NULL; + + if( poRecord->GetType() != NRT_GEOMETRY3D ) + return NULL; + + nGType = atoi(poRecord->GetField(9,9)); // GTYPE + nNumCoord = atoi(poRecord->GetField(10,13)); // NUM_COORD + if( pnGeomId != NULL ) + *pnGeomId = atoi(poRecord->GetField(3,8)); // GEOM_ID + + if( nGType == 1 ) + { + double dfX, dfY, dfZ; + + dfX = atoi(poRecord->GetField(14,14+GetXYLen()-1)) * GetXYMult() + + GetXOrigin(); + dfY = atoi(poRecord->GetField(14+GetXYLen(),14+GetXYLen()*2-1)) + * GetXYMult() + GetYOrigin(); + dfZ = atoi(poRecord->GetField(14+1+2*GetXYLen(), + 14+1+2*GetXYLen()+nZWidth-1)) * dfZMult; + + + poGeometry = new OGRPoint( dfX, dfY, dfZ ); + } + + else if( nGType == 2 ) + { + OGRLineString *poLine = new OGRLineString; + double dfX, dfY, dfZ, dfXLast=0.0, dfYLast=0.0; + int iCoord, nOutCount = 0; + + poGeometry = poLine; + poLine->setNumPoints( nNumCoord ); + for( iCoord = 0; iCoord < nNumCoord; iCoord++ ) + { + int iStart = 14 + iCoord * (GetXYLen()*2+nZWidth+2); + + dfX = atoi(poRecord->GetField(iStart+0, + iStart+GetXYLen()-1)) + * GetXYMult() + GetXOrigin(); + dfY = atoi(poRecord->GetField(iStart+GetXYLen(), + iStart+GetXYLen()*2-1)) + * GetXYMult() + GetYOrigin(); + + dfZ = atoi(poRecord->GetField(iStart+1+2*GetXYLen(), + iStart+1+2*GetXYLen()+nZWidth-1)) + * dfZMult; + + if( iCoord == 0 ) + { + dfXLast = dfX; + dfYLast = dfY; + poLine->setPoint( nOutCount++, dfX, dfY, dfZ ); + } + else if( dfXLast != dfX || dfYLast != dfY ) + { + dfXLast = dfX; + dfYLast = dfY; + poLine->setPoint( nOutCount++, dfX, dfY, dfZ ); + } + } + poLine->setNumPoints( nOutCount ); + + CacheAddByGeomId( atoi(poRecord->GetField(3,8)), poLine ); + } + + if( poGeometry != NULL ) + poGeometry->assignSpatialReference( poDS->GetSpatialRef() ); + + return poGeometry; +} + +/************************************************************************/ +/* ProcessAttDesc() */ +/************************************************************************/ + +int NTFFileReader::ProcessAttDesc( NTFRecord * poRecord, NTFAttDesc* psAD ) + +{ + int iChar; + const char *pszData; + + if( poRecord->GetType() != NRT_ADR ) + return FALSE; + + psAD->poCodeList = NULL; + strcpy( psAD->val_type, poRecord->GetField( 3, 4 )); + strcpy( psAD->fwidth, poRecord->GetField( 5, 7 )); + strcpy( psAD->finter, poRecord->GetField( 8, 12 )); + + pszData = poRecord->GetData(); + for( iChar = 12; + pszData[iChar] != '\0' && pszData[iChar] != '\\'; + iChar++ ) {} + + strcpy( psAD->att_name, poRecord->GetField( 13, iChar )); + + return TRUE; +} + +/************************************************************************/ +/* ProcessAttRecGroup() */ +/* */ +/* Extract attribute values from all attribute records in a */ +/* record set. */ +/************************************************************************/ + +int NTFFileReader::ProcessAttRecGroup( NTFRecord **papoRecords, + char ***ppapszTypes, + char ***ppapszValues ) + +{ + *ppapszTypes = NULL; + *ppapszValues = NULL; + + for( int iRec = 0; papoRecords[iRec] != NULL; iRec++ ) + { + char **papszTypes1 = NULL, **papszValues1 = NULL; + + if( papoRecords[iRec]->GetType() != NRT_ATTREC ) + continue; + + if( !ProcessAttRec( papoRecords[iRec], NULL, + &papszTypes1, &papszValues1 ) ) + return FALSE; + + if( *ppapszTypes == NULL ) + { + *ppapszTypes = papszTypes1; + *ppapszValues = papszValues1; + } + else + { + for( int i=0; papszTypes1[i] != NULL; i++ ) + { + *ppapszTypes = CSLAddString( *ppapszTypes, papszTypes1[i] ); + *ppapszValues = CSLAddString( *ppapszValues, papszValues1[i] ); + } + CSLDestroy( papszTypes1 ); + CSLDestroy( papszValues1 ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* ProcessAttRec() */ +/************************************************************************/ + +int NTFFileReader::ProcessAttRec( NTFRecord * poRecord, + int *pnAttId, + char *** ppapszTypes, + char *** ppapszValues ) + +{ + int iOffset; + const char *pszData; + + if( poRecord->GetType() != NRT_ATTREC ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Extract the attribute id. */ +/* -------------------------------------------------------------------- */ + if( pnAttId != NULL ) + *pnAttId = atoi(poRecord->GetField(3,8)); + +/* ==================================================================== */ +/* Loop handling attribute till we get a '0' indicating the end */ +/* of the record. */ +/* ==================================================================== */ + *ppapszTypes = NULL; + *ppapszValues = NULL; + + iOffset = 8; + pszData = poRecord->GetData(); + + while( pszData[iOffset] != '0' && pszData[iOffset] != '\0' ) + { + NTFAttDesc *psAttDesc; + int nEnd; + int nFWidth; + +/* -------------------------------------------------------------------- */ +/* Extract the two letter code name for the attribute, and use */ +/* it to find the correct ATTDESC info. */ +/* -------------------------------------------------------------------- */ + psAttDesc = GetAttDesc(pszData + iOffset ); + if( psAttDesc == NULL ) + { + CPLDebug( "NTF", "Couldn't translate attrec type `%2.2s'.", + pszData + iOffset ); + return FALSE; + } + + *ppapszTypes = + CSLAddString( *ppapszTypes, + poRecord->GetField(iOffset+1,iOffset+2) ); + +/* -------------------------------------------------------------------- */ +/* Establish the width of the value. Zero width fields are */ +/* terminated by a backslash. */ +/* -------------------------------------------------------------------- */ + nFWidth = atoi(psAttDesc->fwidth); + if( nFWidth == 0 ) + { + const char * pszData = poRecord->GetData(); + + for( nEnd = iOffset + 2; + pszData[nEnd] != '\\' && pszData[nEnd] != '\0'; + nEnd++ ) {} + } + else + { + nEnd = iOffset + 3 + nFWidth - 1; + } + +/* -------------------------------------------------------------------- */ +/* Extract the value. If it is formatted as fixed point real */ +/* we reprocess it to insert the decimal point. */ +/* -------------------------------------------------------------------- */ + const char * pszRawValue = poRecord->GetField(iOffset+3,nEnd); + *ppapszValues = CSLAddString( *ppapszValues, pszRawValue ); + +/* -------------------------------------------------------------------- */ +/* Establish new offset position. */ +/* -------------------------------------------------------------------- */ + if( nFWidth == 0 ) + { + iOffset = nEnd; + if( pszData[iOffset] == '\\' ) + iOffset++; + } + else + iOffset += 2 + atoi(psAttDesc->fwidth); + } + + return TRUE; +} + +/************************************************************************/ +/* GetAttDesc() */ +/************************************************************************/ + +NTFAttDesc * NTFFileReader::GetAttDesc( const char * pszType ) + +{ + for( int i = 0; i < nAttCount; i++ ) + { + if( EQUALN(pszType, pasAttDesc[i].val_type, 2) ) + return pasAttDesc + i; + } + + return NULL; +} + +/************************************************************************/ +/* ProcessAttValue() */ +/* */ +/* Take an attribute type/value pair and transform into a */ +/* meaningful attribute name, and value. The source can be an */ +/* ATTREC or the VAL_TYPE/VALUE pair of a POINTREC or LINEREC. */ +/* The name is transformed from the two character short form to */ +/* the long user name. The value will be transformed from */ +/* fixed point (with the decimal implicit) to fixed point with */ +/* an explicit decimal point if it has a "R" format. */ +/************************************************************************/ + +int NTFFileReader::ProcessAttValue( const char *pszValType, + const char *pszRawValue, + char **ppszAttName, + char **ppszAttValue, + char **ppszCodeDesc ) + +{ +/* -------------------------------------------------------------------- */ +/* Find the ATTDESC for this attribute, and assign return name value.*/ +/* -------------------------------------------------------------------- */ + NTFAttDesc *psAttDesc = GetAttDesc(pszValType); + + if( psAttDesc == NULL ) + return FALSE; + + if( ppszAttName != NULL ) + *ppszAttName = psAttDesc->att_name; + +/* -------------------------------------------------------------------- */ +/* Extract the value. If it is formatted as fixed point real */ +/* we reprocess it to insert the decimal point. */ +/* -------------------------------------------------------------------- */ + if( psAttDesc->finter[0] == 'R' ) + { + static char szRealString[30]; + const char *pszDecimalPortion; + int nWidth, nPrecision; + + for( pszDecimalPortion = psAttDesc->finter; + *pszDecimalPortion != ',' && *pszDecimalPortion != '\0'; + pszDecimalPortion++ ) {} + + nWidth = strlen(pszRawValue); + nPrecision = atoi(pszDecimalPortion+1); + + strncpy( szRealString, pszRawValue, nWidth - nPrecision ); + szRealString[nWidth-nPrecision] = '.'; + strcpy( szRealString+nWidth-nPrecision+1, + pszRawValue+nWidth-nPrecision ); + + *ppszAttValue = szRealString; + } + +/* -------------------------------------------------------------------- */ +/* If it is an integer, we just reformat to get rid of leading */ +/* zeros. */ +/* -------------------------------------------------------------------- */ + else if( psAttDesc->finter[0] == 'I' ) + { + static char szIntString[30]; + + sprintf( szIntString, "%d", atoi(pszRawValue) ); + + *ppszAttValue = szIntString; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise we take the value directly. */ +/* -------------------------------------------------------------------- */ + else + { + *ppszAttValue = (char *) pszRawValue; + } + +/* -------------------------------------------------------------------- */ +/* Handle processing code values into code descriptions, if */ +/* applicable. */ +/* -------------------------------------------------------------------- */ + if( ppszCodeDesc == NULL ) + { + } + else if( psAttDesc->poCodeList != NULL ) + { + *ppszCodeDesc = (char *)psAttDesc->poCodeList->Lookup( *ppszAttValue ); + } + else + { + *ppszCodeDesc = NULL; + } + + return TRUE; +} + +/************************************************************************/ +/* ApplyAttributeValues() */ +/* */ +/* Apply a series of attribute values to a feature from generic */ +/* attribute records. */ +/************************************************************************/ + +void NTFFileReader::ApplyAttributeValues( OGRFeature * poFeature, + NTFRecord ** papoGroup, ... ) + +{ + char **papszTypes = NULL, **papszValues = NULL; + +/* -------------------------------------------------------------------- */ +/* Extract attribute values from record group. */ +/* -------------------------------------------------------------------- */ + if( !ProcessAttRecGroup( papoGroup, &papszTypes, &papszValues ) ) + return; + +/* -------------------------------------------------------------------- */ +/* Handle attribute pairs */ +/* -------------------------------------------------------------------- */ + va_list hVaArgs; + const char *pszAttName; + + va_start(hVaArgs, papoGroup); + + while( (pszAttName = va_arg(hVaArgs, const char *)) != NULL ) + { + int iField = va_arg(hVaArgs, int); + + ApplyAttributeValue( poFeature, iField, pszAttName, + papszTypes, papszValues ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + CSLDestroy( papszTypes ); + CSLDestroy( papszValues ); +} + + +/************************************************************************/ +/* ApplyAttributeValue() */ +/* */ +/* Apply the indicated attribute value to an OGRFeature field */ +/* if it exists in the attribute value list given. */ +/************************************************************************/ + +int NTFFileReader::ApplyAttributeValue( OGRFeature * poFeature, int iField, + const char * pszAttName, + char ** papszTypes, + char ** papszValues ) + +{ +/* -------------------------------------------------------------------- */ +/* Find the requested attribute in the name/value pair */ +/* provided. If not found that's fine, just return with */ +/* notification. */ +/* -------------------------------------------------------------------- */ + int iValue; + + iValue = CSLFindString( papszTypes, pszAttName ); + if( iValue < 0 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Process the attribute value ... this really only has a */ +/* useful effect for real numbers. */ +/* -------------------------------------------------------------------- */ + char *pszAttLongName, *pszAttValue, *pszCodeDesc; + + ProcessAttValue( pszAttName, papszValues[iValue], + &pszAttLongName, &pszAttValue, &pszCodeDesc ); + +/* -------------------------------------------------------------------- */ +/* Apply the value to the field using the simple set string */ +/* method. Leave it to the OGRFeature::SetField() method to */ +/* take care of translation to other types. */ +/* -------------------------------------------------------------------- */ + poFeature->SetField( iField, pszAttValue ); + +/* -------------------------------------------------------------------- */ +/* Apply the code description if we found one. */ +/* -------------------------------------------------------------------- */ + if( pszCodeDesc != NULL ) + { + char szDescFieldName[256]; + + sprintf( szDescFieldName, "%s_DESC", + poFeature->GetDefnRef()->GetFieldDefn(iField)->GetNameRef() ); + poFeature->SetField( szDescFieldName, pszCodeDesc ); + } + + return TRUE; +} + +/************************************************************************/ +/* SaveRecord() */ +/************************************************************************/ + +void NTFFileReader::SaveRecord( NTFRecord * poRecord ) + +{ + CPLAssert( poSavedRecord == NULL ); + poSavedRecord = poRecord; +} + +/************************************************************************/ +/* ReadRecord() */ +/************************************************************************/ + +NTFRecord *NTFFileReader::ReadRecord() + +{ + if( poSavedRecord != NULL ) + { + NTFRecord *poReturn; + + poReturn = poSavedRecord; + + poSavedRecord = NULL; + + return poReturn; + } + else + { + NTFRecord *poRecord; + + CPLErrorReset(); + if( fp != NULL ) + nPreSavedPos = VSIFTell( fp ); + poRecord = new NTFRecord( fp ); + if( fp != NULL ) + nPostSavedPos = VSIFTell( fp ); + + /* ensure termination if we fail to read a record */ + if( CPLGetLastErrorType() == CE_Failure ) + { + delete poRecord; + poRecord = NULL; + } + + return poRecord; + } +} + +/************************************************************************/ +/* GetFPPos() */ +/* */ +/* Return the current file pointer position. */ +/************************************************************************/ + +void NTFFileReader::GetFPPos( long *pnPos, long *pnFID ) + +{ + if( poSavedRecord != NULL ) + *pnPos = nPreSavedPos; + else + *pnPos = nPostSavedPos; + + if( pnFID != NULL ) + *pnFID = nSavedFeatureId; +} + +/************************************************************************/ +/* SetFPPos() */ +/************************************************************************/ + +int NTFFileReader::SetFPPos( long nNewPos, long nNewFID ) + +{ + if( nNewFID == nSavedFeatureId ) + return TRUE; + + if( poSavedRecord != NULL ) + { + delete poSavedRecord; + poSavedRecord = NULL; + } + + if( fp != NULL && VSIFSeek( fp, nNewPos, SEEK_SET ) == 0 ) + { + nPreSavedPos = nPostSavedPos = nNewPos; + nSavedFeatureId = nNewFID; + return TRUE; + } + else + return FALSE; +} + +/************************************************************************/ +/* Reset() */ +/* */ +/* Reset reading to the first feature record. */ +/************************************************************************/ + +void NTFFileReader::Reset() + +{ + SetFPPos( nStartPos, nBaseFeatureId ); + ClearCGroup(); +} + +/************************************************************************/ +/* ClearCGroup() */ +/* */ +/* Clear the currently loaded record group. */ +/************************************************************************/ + +void NTFFileReader::ClearCGroup() + +{ + for( int i = 0; apoCGroup[i] != NULL; i++ ) + delete apoCGroup[i]; + + apoCGroup[0] = NULL; + apoCGroup[1] = NULL; +} + +/************************************************************************/ +/* DefaultNTFRecordGrouper() */ +/* */ +/* Default rules for figuring out if a new candidate record */ +/* belongs to a group of records that together form a feature */ +/* (a record group). */ +/************************************************************************/ + +int DefaultNTFRecordGrouper( NTFFileReader *, NTFRecord ** papoGroup, + NTFRecord * poCandidate ) + +{ +/* -------------------------------------------------------------------- */ +/* Is this group going to be a CPOLY set? We can recognise */ +/* this because we get repeating POLY/CHAIN sets without an */ +/* intermediate attribute record. This is a rather special case! */ +/* -------------------------------------------------------------------- */ + if( papoGroup[0] != NULL && papoGroup[1] != NULL + && papoGroup[0]->GetType() == NRT_POLYGON + && papoGroup[1]->GetType() == NRT_CHAIN ) + { + // We keep going till we get the seed geometry. + int iRec, bGotCPOLY=FALSE; + + for( iRec = 0; papoGroup[iRec] != NULL; iRec++ ) + { + if( papoGroup[iRec]->GetType() == NRT_CPOLY ) + bGotCPOLY = TRUE; + } + + if( bGotCPOLY + && poCandidate->GetType() != NRT_GEOMETRY + && poCandidate->GetType() != NRT_ATTREC ) + return FALSE; + + /* + * this logic assumes we always get a point geometry with a CPOLY + * but that isn't always true, for instance with BL2000 data. The + * preceed check will handle this case. + */ + if( papoGroup[iRec-1]->GetType() != NRT_GEOMETRY ) + return TRUE; + else + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Is this a "feature" defining record? If so break out if it */ +/* isn't the first record in the group. */ +/* -------------------------------------------------------------------- */ + if( papoGroup[0] != NULL + && (poCandidate->GetType() == NRT_NAMEREC + || poCandidate->GetType() == NRT_NODEREC + || poCandidate->GetType() == NRT_LINEREC + || poCandidate->GetType() == NRT_POINTREC + || poCandidate->GetType() == NRT_POLYGON + || poCandidate->GetType() == NRT_CPOLY + || poCandidate->GetType() == NRT_COLLECT + || poCandidate->GetType() == NRT_TEXTREC + || poCandidate->GetType() == NRT_COMMENT) ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Do we already have a record of this type? If so, it likely */ +/* doesn't belong in the group. Attribute records do repeat in */ +/* some products. */ +/* -------------------------------------------------------------------- */ + if (poCandidate->GetType() != NRT_ATTREC ) + { + int iRec; + for( iRec = 0; papoGroup[iRec] != NULL; iRec++ ) + { + if( poCandidate->GetType() == papoGroup[iRec]->GetType() ) + break; + } + + if( papoGroup[iRec] != NULL ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* ReadRecordGroup() */ +/* */ +/* Read a group of records that form a single feature. */ +/************************************************************************/ + +NTFRecord **NTFFileReader::ReadRecordGroup() + +{ + NTFRecord *poRecord; + int nRecordCount = 0; + + ClearCGroup(); + +/* -------------------------------------------------------------------- */ +/* Loop, reading records till we think we have a grouping. */ +/* -------------------------------------------------------------------- */ + while( (poRecord = ReadRecord()) != NULL && poRecord->GetType() != NRT_VTR ) + { + CPLAssert( nRecordCount < MAX_REC_GROUP); + if( nRecordCount >= MAX_REC_GROUP ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Maximum record group size (%d) exceeded.\n", + MAX_REC_GROUP ); + break; + } + + if( !pfnRecordGrouper( this, apoCGroup, poRecord ) ) + break; + + apoCGroup[nRecordCount++] = poRecord; + apoCGroup[nRecordCount] = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Push the last record back on the input queue. */ +/* -------------------------------------------------------------------- */ + if( poRecord != NULL ) + SaveRecord( poRecord ); + +/* -------------------------------------------------------------------- */ +/* Return the list, or NULL if we didn't get any records. */ +/* -------------------------------------------------------------------- */ + if( nRecordCount == 0 ) + return NULL; + else + return apoCGroup; +} + +/************************************************************************/ +/* GetFeatureClass() */ +/************************************************************************/ + +int NTFFileReader::GetFeatureClass( int iFCIndex, + char ** ppszFCId, + char ** ppszFCName ) + +{ + if( iFCIndex < 0 || iFCIndex >= nFCCount ) + { + *ppszFCId = NULL; + *ppszFCName = NULL; + return FALSE; + } + else + { + *ppszFCId = papszFCNum[iFCIndex]; + *ppszFCName = papszFCName[iFCIndex]; + return TRUE; + } +} + +/************************************************************************/ +/* ReadOGRFeature() */ +/************************************************************************/ + +OGRFeature * NTFFileReader::ReadOGRFeature( OGRNTFLayer * poTargetLayer ) + +{ + OGRNTFLayer *poLayer = NULL; + NTFRecord **papoGroup; + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* If this is a raster file, use a custom method to read the */ +/* feature. */ +/* -------------------------------------------------------------------- */ + if( IsRasterProduct() ) + return poRasterLayer->GetNextFeature(); + +/* -------------------------------------------------------------------- */ +/* Loop looking for a group we can translate, and that if */ +/* needed matches our layer request. */ +/* -------------------------------------------------------------------- */ + while( TRUE ) + { + if( GetProductId() == NPC_UNKNOWN && nNTFLevel > 2 ) + papoGroup = GetNextIndexedRecordGroup( apoCGroup + 1 ); + else + papoGroup = ReadRecordGroup(); + + if( papoGroup == NULL ) + break; + + poLayer = apoTypeTranslation[papoGroup[0]->GetType()]; + if( poLayer == NULL ) + continue; + + if( poTargetLayer != NULL && poTargetLayer != poLayer ) + { + CacheLineGeometryInGroup( papoGroup ); + nSavedFeatureId++; + continue; + } + + poFeature = poLayer->FeatureTranslate( this, papoGroup ); + if( poFeature == NULL ) + { + // should this be a real error? + CPLDebug( "NTF", + "FeatureTranslate() failed for a type %d record group\n" + "in a %s type file.\n", + papoGroup[0]->GetType(), + GetProduct() ); + } + else + break; + } + +/* -------------------------------------------------------------------- */ +/* If we got a feature, set the TILE_REF on it. */ +/* -------------------------------------------------------------------- */ + if( poFeature != NULL ) + { + int iTileRefField; + + iTileRefField = poLayer->GetLayerDefn()->GetFieldCount()-1; + + CPLAssert( EQUAL(poLayer->GetLayerDefn()->GetFieldDefn(iTileRefField)-> + GetNameRef(), "TILE_REF") ); + + poFeature->SetField( iTileRefField, GetTileName() ); + poFeature->SetFID( nSavedFeatureId ); + + nSavedFeatureId++; + } + +/* -------------------------------------------------------------------- */ +/* If we got to the end we can establish our feature count for */ +/* the file. */ +/* -------------------------------------------------------------------- */ + else + { + CPLAssert( nFeatureCount == -1 + || nFeatureCount == nSavedFeatureId - nBaseFeatureId ); + nFeatureCount = nSavedFeatureId - nBaseFeatureId; + } + + return( poFeature ); +} + +/************************************************************************/ +/* TestForLayer() */ +/* */ +/* Return indicator of whether this file contains any features */ +/* of the indicated layer type. */ +/************************************************************************/ + +int NTFFileReader::TestForLayer( OGRNTFLayer * poLayer ) + +{ + for( int i = 0; i < 100; i++ ) + { + if( apoTypeTranslation[i] == poLayer ) + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* FreshenIndex() */ +/* */ +/* Rebuild the index if it is needed, and currently missing. */ +/************************************************************************/ + +void NTFFileReader::FreshenIndex() + +{ + if( !bIndexBuilt && bIndexNeeded ) + IndexFile(); +} + +/************************************************************************/ +/* IndexFile() */ +/* */ +/* Read all records beyond the section header and build an */ +/* internal index of them. */ +/************************************************************************/ + +void NTFFileReader::IndexFile() + +{ + NTFRecord *poRecord; + + Reset(); + + DestroyIndex(); + + bIndexNeeded = TRUE; + bIndexBuilt = TRUE; + bCacheLines = FALSE; + +/* -------------------------------------------------------------------- */ +/* Process all records after the section header, and before 99 */ +/* to put them in the index. */ +/* -------------------------------------------------------------------- */ + while( (poRecord = ReadRecord()) != NULL && poRecord->GetType() != 99 ) + { + int iType = poRecord->GetType(); + int iId = atoi(poRecord->GetField( 3, 8 )); + + if( iType < 0 || iType >= 100 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Illegal type %d record, skipping.", + iType ); + delete poRecord; + continue; + } + +/* -------------------------------------------------------------------- */ +/* Grow type specific subindex if needed. */ +/* -------------------------------------------------------------------- */ + if( anIndexSize[iType] <= iId ) + { + int nNewSize = MAX(iId+1,anIndexSize[iType] * 2 + 10); + + apapoRecordIndex[iType] = (NTFRecord **) + CPLRealloc(apapoRecordIndex[iType], + sizeof(void *) * nNewSize); + + for( int i = anIndexSize[iType]; i < nNewSize; i++ ) + (apapoRecordIndex[iType])[i] = NULL; + + anIndexSize[iType] = nNewSize; + } + +/* -------------------------------------------------------------------- */ +/* Put record into type specific subindex based on it's id as */ +/* the key. */ +/* -------------------------------------------------------------------- */ + if( apapoRecordIndex[iType][iId] != NULL ) + { + CPLDebug( "OGR_NTF", + "Duplicate record with index %d and type %d\n" + "in NTFFileReader::IndexFile().", + iId, iType ); + delete apapoRecordIndex[iType][iId]; + } + (apapoRecordIndex[iType])[iId] = poRecord; + } + + if( poRecord != NULL ) + delete poRecord; +} + +/************************************************************************/ +/* DestroyIndex() */ +/************************************************************************/ + +void NTFFileReader::DestroyIndex() + +{ + for( int i = 0; i < 100; i++ ) + { + for( int iId = 0; iId < anIndexSize[i]; iId++ ) + { + if( (apapoRecordIndex[i])[iId] != NULL ) + delete (apapoRecordIndex[i])[iId]; + } + + CPLFree( apapoRecordIndex[i] ); + apapoRecordIndex[i] = NULL; + anIndexSize[i] = 0; + } + + bIndexBuilt = FALSE; +} + +/************************************************************************/ +/* GetIndexedRecord() */ +/************************************************************************/ + +NTFRecord * NTFFileReader::GetIndexedRecord( int iType, int iId ) + +{ + if( (iType < 0 || iType > 99) + || (iId < 0 || iId >= anIndexSize[iType]) + || (apapoRecordIndex[iType])[iId] == NULL ) + { + /* If NRT_GEOMETRY3D is an acceptable alternative to 2D */ + if( iType == NRT_GEOMETRY ) + return GetIndexedRecord( NRT_GEOMETRY3D, iId ); + else + return NULL; + } + + return (apapoRecordIndex[iType])[iId]; +} + +/************************************************************************/ +/* AddToIndexGroup() */ +/************************************************************************/ + +static void AddToIndexGroup( NTFRecord **papoGroup, NTFRecord * poRecord ) + +{ + int i; + + for( i = 1; papoGroup[i] != NULL; i++ ) {} + + papoGroup[i] = poRecord; + papoGroup[i+1] = NULL; +} + + +/************************************************************************/ +/* GetNextIndexedRecordGroup() */ +/************************************************************************/ + +NTFRecord **NTFFileReader::GetNextIndexedRecordGroup( NTFRecord ** + papoPrevGroup ) + +{ + int nPrevType, nPrevId; + +/* -------------------------------------------------------------------- */ +/* What was the identify of our previous anchor record? */ +/* -------------------------------------------------------------------- */ + if( papoPrevGroup == NULL || papoPrevGroup[0] == NULL ) + { + nPrevType = NRT_POINTREC; + nPrevId = 0; + FreshenIndex(); + } + else + { + nPrevType = papoPrevGroup[0]->GetType(); + nPrevId = atoi(papoPrevGroup[0]->GetField(3,8)); + } + +/* -------------------------------------------------------------------- */ +/* Find the next anchor record. */ +/* -------------------------------------------------------------------- */ + NTFRecord *poAnchor = NULL; + + while( nPrevType != 99 && poAnchor == NULL ) + { + nPrevId++; + if( nPrevId >= anIndexSize[nPrevType] ) + { + do + { + nPrevType++; + } + while( nPrevType != NRT_VTR + && nPrevType != NRT_NODEREC + && nPrevType != NRT_TEXTREC + && nPrevType != NRT_NAMEREC + && nPrevType != NRT_COLLECT + && nPrevType != NRT_POLYGON + && nPrevType != NRT_CPOLY + && nPrevType != NRT_POINTREC + && nPrevType != NRT_LINEREC ); + + nPrevId = 0; + } + else + { + poAnchor = (apapoRecordIndex[nPrevType])[nPrevId]; + } + } + + if( poAnchor == NULL ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Build record group depending on type of anchor and what it */ +/* refers to. */ +/* -------------------------------------------------------------------- */ + apoCGroup[0] = NULL; + apoCGroup[1] = poAnchor; + apoCGroup[2] = NULL; + +/* -------------------------------------------------------------------- */ +/* Handle POINTREC/LINEREC */ +/* -------------------------------------------------------------------- */ + if( poAnchor->GetType() == NRT_POINTREC + || poAnchor->GetType() == NRT_LINEREC ) + { + int nAttCount = 0; + + AddToIndexGroup( apoCGroup, + GetIndexedRecord( NRT_GEOMETRY, + atoi(poAnchor->GetField(9,14)) ) ); + + if( poAnchor->GetLength() >= 16 ) + nAttCount = atoi(poAnchor->GetField(15,16)); + + for( int iAtt = 0; iAtt < nAttCount; iAtt++ ) + { + AddToIndexGroup( + apoCGroup, + GetIndexedRecord( NRT_ATTREC, + atoi(poAnchor->GetField(17+6*iAtt, + 22+6*iAtt)) ) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Handle TEXTREC */ +/* -------------------------------------------------------------------- */ + else if( poAnchor->GetType() == NRT_TEXTREC ) + { + int nAttCount = 0; + int nSelCount = 0; + + // Add all the text position records. + nSelCount = atoi(poAnchor->GetField(9,10)); + + for( int iSel = 0; iSel < nSelCount; iSel++ ) + { + int iStart = 11 + 12*iSel + 6; + + AddToIndexGroup( + apoCGroup, + GetIndexedRecord( NRT_TEXTPOS, + atoi(poAnchor->GetField(iStart,iStart+5)) )); + } + + // Add all geometry and TEXR records pointed to by text position + // records. + for( int iRec = 1; apoCGroup[iRec] != NULL; iRec++ ) + { + int nNumTEXR; + NTFRecord *poRecord = apoCGroup[iRec]; + + if( poRecord->GetType() != NRT_TEXTPOS ) + continue; + + nNumTEXR = atoi(poRecord->GetField(9,10)); + for( int iTEXR = 0; iTEXR < nNumTEXR; iTEXR++ ) + { + AddToIndexGroup( + apoCGroup, + GetIndexedRecord( NRT_TEXTREP, + atoi(poRecord->GetField(11+iTEXR*12, + 16+iTEXR*12)))); + AddToIndexGroup( + apoCGroup, + GetIndexedRecord( NRT_GEOMETRY, + atoi(poRecord->GetField(17+iTEXR*12, + 22+iTEXR*12)))); + } + } + + // Add all the attribute records. + if( poAnchor->GetLength() >= 10 + nSelCount*12 + 2 ) + nAttCount = atoi(poAnchor->GetField(11+nSelCount*12, + 12+nSelCount*12)); + + for( int iAtt = 0; iAtt < nAttCount; iAtt++ ) + { + int iStart = 13 + nSelCount*12 + 6 * iAtt; + + AddToIndexGroup( + apoCGroup, + GetIndexedRecord( NRT_ATTREC, + atoi(poAnchor->GetField(iStart,iStart+5)) )); + } + + } + +/* -------------------------------------------------------------------- */ +/* Handle NODEREC. */ +/* -------------------------------------------------------------------- */ + else if( poAnchor->GetType() == NRT_NODEREC ) + { + AddToIndexGroup( apoCGroup, + GetIndexedRecord( NRT_GEOMETRY, + atoi(poAnchor->GetField(9,14)) ) ); + } + +/* -------------------------------------------------------------------- */ +/* Handle COLLECT. */ +/* -------------------------------------------------------------------- */ + else if( poAnchor->GetType() == NRT_COLLECT ) + { + int nParts = atoi(poAnchor->GetField(9,12)); + int nAttOffset = 13 + nParts * 8; + int nAttCount = 0; + + if( poAnchor->GetLength() > nAttOffset + 2 ) + nAttCount = atoi(poAnchor->GetField(nAttOffset,nAttOffset+1)); + + for( int iAtt = 0; iAtt < nAttCount; iAtt++ ) + { + int iStart = nAttOffset + 2 + iAtt * 6; + + AddToIndexGroup( + apoCGroup, + GetIndexedRecord( NRT_ATTREC, + atoi(poAnchor->GetField(iStart,iStart+5)) )); + } + } + +/* -------------------------------------------------------------------- */ +/* Handle POLYGON */ +/* -------------------------------------------------------------------- */ + else if( poAnchor->GetType() == NRT_POLYGON ) + { + AddToIndexGroup( apoCGroup, + GetIndexedRecord( NRT_CHAIN, + atoi(poAnchor->GetField(9,14)) ) ); + + if( poAnchor->GetLength() >= 20 ) + AddToIndexGroup( apoCGroup, + GetIndexedRecord( NRT_GEOMETRY, + atoi(poAnchor->GetField(15,20)) ) ); + + // Attributes + + int nAttCount = 0; + + if( poAnchor->GetLength() >= 22 ) + nAttCount = atoi(poAnchor->GetField(21,22)); + + for( int iAtt = 0; iAtt < nAttCount; iAtt++ ) + { + AddToIndexGroup( + apoCGroup, + GetIndexedRecord( NRT_ATTREC, + atoi(poAnchor->GetField(23+6*iAtt, + 28+6*iAtt)) ) ); + } + } +/* -------------------------------------------------------------------- */ +/* Handle CPOLY */ +/* -------------------------------------------------------------------- */ + else if( poAnchor->GetType() == NRT_CPOLY ) + { + int nPolyCount = atoi(poAnchor->GetField(9,12)); + int nPostPoly = nPolyCount*7 + 12; + + if( poAnchor->GetLength() >= nPostPoly + 6 ) + { + int nGeomId = atoi(poAnchor->GetField(nPostPoly+1,nPostPoly+6)); + + AddToIndexGroup( apoCGroup, + GetIndexedRecord( NRT_GEOMETRY, nGeomId) ); + } + + if( poAnchor->GetLength() >= nPostPoly + 8 ) + { + int nAttCount = atoi(poAnchor->GetField(nPostPoly+7,nPostPoly+8)); + + for( int iAtt = 0; iAtt < nAttCount; iAtt++ ) + { + int nAttId = atoi(poAnchor->GetField(nPostPoly+9+iAtt*6, + nPostPoly+14+iAtt*6)); + AddToIndexGroup( apoCGroup, + GetIndexedRecord( NRT_ATTREC, nAttId) ); + } + } + } + + return apoCGroup + 1; +} + +/************************************************************************/ +/* OverrideTileName() */ +/************************************************************************/ + +void NTFFileReader::OverrideTileName( const char *pszNewName ) + +{ + CPLFree( pszTileName ); + pszTileName = CPLStrdup( pszNewName ); +} + +/************************************************************************/ +/* CacheAddByGeomId() */ +/* */ +/* Add a geometry to the geometry cache given it's GEOMID as */ +/* the index. */ +/************************************************************************/ + +void NTFFileReader::CacheAddByGeomId( int nGeomId, OGRGeometry *poGeometry ) + +{ + if( !bCacheLines ) + return; + + CPLAssert( nGeomId >= 0 ); + +/* -------------------------------------------------------------------- */ +/* Grow the cache if it isn't large enough to hold the newly */ +/* requested geometry id. */ +/* -------------------------------------------------------------------- */ + if( nGeomId >= nLineCacheSize ) + { + int nNewSize = nGeomId + 100; + + papoLineCache = (OGRGeometry **) + CPLRealloc( papoLineCache, sizeof(void*) * nNewSize ); + memset( papoLineCache + nLineCacheSize, 0, + sizeof(void*) * (nNewSize - nLineCacheSize) ); + nLineCacheSize = nNewSize; + } + +/* -------------------------------------------------------------------- */ +/* Make a cloned copy of the geometry for the cache. */ +/* -------------------------------------------------------------------- */ + if( papoLineCache[nGeomId] != NULL ) + return; + + papoLineCache[nGeomId] = poGeometry->clone(); +} + +/************************************************************************/ +/* CacheGetByGeomId() */ +/************************************************************************/ + +OGRGeometry *NTFFileReader::CacheGetByGeomId( int nGeomId ) + +{ + if( nGeomId < 0 || nGeomId >= nLineCacheSize ) + return NULL; + else + return papoLineCache[nGeomId]; +} + +/************************************************************************/ +/* CacheClean() */ +/************************************************************************/ + +void NTFFileReader::CacheClean() + +{ + for( int i = 0; i < nLineCacheSize; i++ ) + { + if( papoLineCache[i] != NULL ) + delete papoLineCache[i]; + } + if( papoLineCache != NULL ) + CPLFree( papoLineCache ); + + nLineCacheSize = 0; + papoLineCache = NULL; +} + +/************************************************************************/ +/* CacheLineGeometryInGroup() */ +/* */ +/* Run any line geometries in this group through the */ +/* ProcessGeometry() call just to ensure the line geometry will */ +/* be cached. */ +/************************************************************************/ + +void NTFFileReader::CacheLineGeometryInGroup( NTFRecord **papoGroup ) + +{ + if( !bCacheLines ) + return; + + for( int iRec = 0; papoGroup[iRec] != NULL; iRec++ ) + { + if( papoGroup[iRec]->GetType() == NRT_GEOMETRY + || papoGroup[iRec]->GetType() == NRT_GEOMETRY3D ) + { + OGRGeometry *poGeom = ProcessGeometry( papoGroup[iRec], NULL ); + if( poGeom != NULL ) + delete poGeom; + } + } +} + +/************************************************************************/ +/* FormPolygonFromCache() */ +/* */ +/* This method will attempt to find the line geometries */ +/* referenced by the GEOM_ID_OF_LINK ids of a feature in the */ +/* line cache (if available), and if so, assemble them into a */ +/* polygon. */ +/************************************************************************/ + +int NTFFileReader::FormPolygonFromCache( OGRFeature * poFeature ) + +{ + if( !bCacheLines ) + return FALSE; + + OGRGeometryCollection oLines; + const int *panLinks; + int nLinkCount, i; + +/* -------------------------------------------------------------------- */ +/* Collect all the linked lines. */ +/* -------------------------------------------------------------------- */ + panLinks = poFeature->GetFieldAsIntegerList( "GEOM_ID_OF_LINK", + &nLinkCount ); + + if( panLinks == NULL ) + return FALSE; + + for( i = 0; i < nLinkCount; i++ ) + { + OGRGeometry *poLine = CacheGetByGeomId( panLinks[i] ); + if( poLine == NULL ) + { + oLines.removeGeometry( -1, FALSE ); + return FALSE; + } + + oLines.addGeometryDirectly( poLine ); + } + +/* -------------------------------------------------------------------- */ +/* Assemble into a polygon geometry. */ +/* -------------------------------------------------------------------- */ + OGRPolygon *poPoly; + + poPoly = (OGRPolygon *) + OGRBuildPolygonFromEdges( (OGRGeometryH) &oLines, FALSE, FALSE, 0.1, + NULL ); + + poFeature->SetGeometryDirectly( poPoly ); + + oLines.removeGeometry( -1, FALSE ); + + return poPoly != NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntfrecord.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntfrecord.cpp new file mode 100644 index 000000000..90d761cfd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntfrecord.cpp @@ -0,0 +1,264 @@ +/****************************************************************************** + * $Id: ntfrecord.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: NTF Translator + * Purpose: NTFRecord class implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ntf.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ntfrecord.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +static int nFieldBufSize = 0; +static char *pszFieldBuf = NULL; + +#define MAX_RECORD_LEN 160 + +/************************************************************************/ +/* NTFRecord() */ +/* */ +/* The constructor is where the record is read. This includes */ +/* transparent merging of continuation lines. */ +/************************************************************************/ + +NTFRecord::NTFRecord( FILE * fp ) + +{ + nType = 99; + nLength = 0; + pszData = NULL; + + if( fp == NULL ) + return; + +/* ==================================================================== */ +/* Read lines untill we get to one without a continuation mark. */ +/* ==================================================================== */ + char szLine[MAX_RECORD_LEN+3]; + int nNewLength; + + do { + nNewLength = ReadPhysicalLine( fp, szLine ); + if( nNewLength == -1 || nNewLength == -2 ) + break; + + while( nNewLength > 0 && szLine[nNewLength-1] == ' ' ) + szLine[--nNewLength] = '\0'; + + if( szLine[nNewLength-1] != '%' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Corrupt NTF record, missing end '%%'." ); + CPLFree( pszData ); + pszData = NULL; + break; + } + + if( pszData == NULL ) + { + nLength = nNewLength - 2; + pszData = (char *) VSIMalloc(nLength+1); + if (pszData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + return; + } + memcpy( pszData, szLine, nLength ); + pszData[nLength] = '\0'; + } + else + { + if( !EQUALN(szLine,"00",2) ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Invalid line"); + VSIFree(pszData); + pszData = NULL; + return; + } + + char* pszNewData = (char *) VSIRealloc(pszData,nLength+(nNewLength-4)+1); + if (pszNewData == NULL) + { + CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory"); + VSIFree(pszData); + pszData = NULL; + return; + } + + pszData = pszNewData; + memcpy( pszData+nLength, szLine+2, nNewLength-4 ); + nLength += nNewLength-4; + pszData[nLength] = '\0'; + } + } while( szLine[nNewLength-2] == '1' ); + +/* -------------------------------------------------------------------- */ +/* Figure out the record type. */ +/* -------------------------------------------------------------------- */ + if( pszData != NULL ) + { + char szType[3]; + + strncpy( szType, pszData, 2 ); + szType[2] = '\0'; + + nType = atoi(szType); + } +} + +/************************************************************************/ +/* ~NTFRecord() */ +/************************************************************************/ + +NTFRecord::~NTFRecord() + +{ + CPLFree( pszData ); + + if( pszFieldBuf != NULL ) + { + CPLFree( pszFieldBuf ); + pszFieldBuf = NULL; + nFieldBufSize = 0; + } +} + +/************************************************************************/ +/* ReadPhysicalLine() */ +/************************************************************************/ + +int NTFRecord::ReadPhysicalLine( FILE *fp, char *pszLine ) + +{ + int nBytesRead = 0; + int nRecordStart, nRecordEnd, i, nLength = 0; + +/* -------------------------------------------------------------------- */ +/* Read enough data that we are sure we have a whole record. */ +/* -------------------------------------------------------------------- */ + nRecordStart = VSIFTell( fp ); + nBytesRead = VSIFRead( pszLine, 1, MAX_RECORD_LEN+2, fp ); + + if( nBytesRead == 0 ) + { + if( VSIFEof( fp ) ) + return -1; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Low level read error occured while reading NTF file." ); + return -2; + } + } + +/* -------------------------------------------------------------------- */ +/* Search for CR or LF. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nBytesRead; i++ ) + { + if( pszLine[i] == 10 || pszLine[i] == 13 ) + break; + } + +/* -------------------------------------------------------------------- */ +/* If we don't find EOL within 80 characters something has gone */ +/* badly wrong! */ +/* -------------------------------------------------------------------- */ + if( i == MAX_RECORD_LEN+2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%d byte record too long for NTF format.\n" + "No line may be longer than 80 characters though up to %d tolerated.\n", + nBytesRead, MAX_RECORD_LEN ); + return -2; + } + +/* -------------------------------------------------------------------- */ +/* Trim CR/LF. */ +/* -------------------------------------------------------------------- */ + nLength = i; + if( pszLine[i+1] == 10 || pszLine[i+1] == 13 ) + nRecordEnd = nRecordStart + i + 2; + else + nRecordEnd = nRecordStart + i + 1; + + pszLine[nLength] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Restore read pointer to beginning of next record. */ +/* -------------------------------------------------------------------- */ + VSIFSeek( fp, nRecordEnd, SEEK_SET ); + + return nLength; +} + +/************************************************************************/ +/* GetField() */ +/* */ +/* Note that the start position is 1 based, to match the */ +/* notation in the NTF document. The returned pointer is to an */ +/* internal buffer, but is zero terminated. */ +/************************************************************************/ + +const char * NTFRecord::GetField( int nStart, int nEnd ) + +{ + int nSize = nEnd - nStart + 1; + +/* -------------------------------------------------------------------- */ +/* Reallocate working buffer larger if needed. */ +/* -------------------------------------------------------------------- */ + if( nFieldBufSize < nSize + 1 ) + { + CPLFree( pszFieldBuf ); + nFieldBufSize = nSize + 1; + pszFieldBuf = (char *) CPLMalloc(nFieldBufSize); + } + +/* -------------------------------------------------------------------- */ +/* Copy out desired data. */ +/* -------------------------------------------------------------------- */ + if( nStart + nSize > nLength+1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to read %d to %d, beyond the end of %d byte long\n" + "type `%2.2s' record.\n", + nStart, nEnd, nLength, pszData ); + memset( pszFieldBuf, ' ', nSize ); + pszFieldBuf[nSize] = '\0'; + } + else + { + strncpy( pszFieldBuf, pszData + nStart - 1, nSize ); + pszFieldBuf[nSize] = '\0'; + } + + return pszFieldBuf; +} + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntfstroke.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntfstroke.cpp new file mode 100644 index 000000000..0a8f47eb0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ntfstroke.cpp @@ -0,0 +1,244 @@ +/****************************************************************************** + * $Id: ntfstroke.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: NTF Translator + * Purpose: NTF Arc to polyline stroking code. This code is really generic, + * and might be moved into an OGR module at some point in the + * future. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ntf.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ntfstroke.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* NTFArcCenterFromEdgePoints() */ +/* */ +/* Compute the center of an arc/circle from three edge points. */ +/************************************************************************/ + +int NTFArcCenterFromEdgePoints( double x_c0, double y_c0, + double x_c1, double y_c1, + double x_c2, double y_c2, + double *x_center, double *y_center ) + +{ + +/* -------------------------------------------------------------------- */ +/* Handle a degenerate case that occurs in OSNI products by */ +/* making some assumptions. If the first and third points are */ +/* the same assume they are intended to define a full circle, */ +/* and that the second point is on the opposite side of the */ +/* circle. */ +/* -------------------------------------------------------------------- */ + if( x_c0 == x_c2 && y_c0 == y_c2 ) + { + *x_center = (x_c0 + x_c1) * 0.5; + *y_center = (y_c0 + y_c1) * 0.5; + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Compute the inverse of the slopes connecting the first and */ +/* second points. Also compute the center point of the two */ +/* lines ... the point our crossing line will go through. */ +/* -------------------------------------------------------------------- */ + double m1, x1, y1; + + if( (y_c1 - y_c0) != 0.0 ) + m1 = (x_c0 - x_c1) / (y_c1 - y_c0); + else + m1 = 1e+10; + + x1 = (x_c0 + x_c1) * 0.5; + y1 = (y_c0 + y_c1) * 0.5; + +/* -------------------------------------------------------------------- */ +/* Compute the same for the second point compared to the third */ +/* point. */ +/* -------------------------------------------------------------------- */ + double m2, x2, y2; + + if( (y_c2 - y_c1) != 0.0 ) + m2 = (x_c1 - x_c2) / (y_c2 - y_c1); + else + m2 = 1e+10; + + x2 = (x_c1 + x_c2) * 0.5; + y2 = (y_c1 + y_c2) * 0.5; + +/* -------------------------------------------------------------------- */ +/* Turn these into the Ax+By+C = 0 form of the lines. */ +/* -------------------------------------------------------------------- */ + double a1, a2, b1, b2, c1, c2; + + a1 = m1; + a2 = m2; + + b1 = -1.0; + b2 = -1.0; + + c1 = (y1 - m1*x1); + c2 = (y2 - m2*x2); + +/* -------------------------------------------------------------------- */ +/* Compute the intersection of the two lines through the center */ +/* of the circle, using Kramers rule. */ +/* -------------------------------------------------------------------- */ + double det_inv; + + if( a1*b2 - a2*b1 == 0.0 ) + return FALSE; + + det_inv = 1 / (a1*b2 - a2*b1); + + *x_center = (b1*c2 - b2*c1) * det_inv; + *y_center = (a2*c1 - a1*c2) * det_inv; + + return TRUE; +} + +/************************************************************************/ +/* NTFStrokeArcToOGRGeometry_Points() */ +/************************************************************************/ + +OGRGeometry * +NTFStrokeArcToOGRGeometry_Points( double dfStartX, double dfStartY, + double dfAlongX, double dfAlongY, + double dfEndX, double dfEndY, + int nVertexCount ) + +{ + double dfStartAngle, dfEndAngle, dfAlongAngle; + double dfCenterX, dfCenterY, dfRadius; + + if( !NTFArcCenterFromEdgePoints( dfStartX, dfStartY, dfAlongX, dfAlongY, + dfEndX, dfEndY, &dfCenterX, &dfCenterY ) ) + return NULL; + + if( dfStartX == dfEndX && dfStartY == dfEndY ) + { + dfStartAngle = 0.0; + dfEndAngle = 360.0; + } + else + { + double dfDeltaX, dfDeltaY; + + dfDeltaX = dfStartX - dfCenterX; + dfDeltaY = dfStartY - dfCenterY; + dfStartAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / PI; + + dfDeltaX = dfAlongX - dfCenterX; + dfDeltaY = dfAlongY - dfCenterY; + dfAlongAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / PI; + + dfDeltaX = dfEndX - dfCenterX; + dfDeltaY = dfEndY - dfCenterY; + dfEndAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / PI; + +#ifdef notdef + if( dfStartAngle > dfAlongAngle && dfAlongAngle > dfEndAngle ) + { + double dfTempAngle; + + dfTempAngle = dfStartAngle; + dfStartAngle = dfEndAngle; + dfEndAngle = dfTempAngle; + } +#endif + + while( dfAlongAngle < dfStartAngle ) + dfAlongAngle += 360.0; + + while( dfEndAngle < dfAlongAngle ) + dfEndAngle += 360.0; + + if( dfEndAngle - dfStartAngle > 360.0 ) + { + double dfTempAngle; + + dfTempAngle = dfStartAngle; + dfStartAngle = dfEndAngle; + dfEndAngle = dfTempAngle; + + while( dfEndAngle < dfStartAngle ) + dfStartAngle -= 360.0; + } + } + + dfRadius = sqrt( (dfCenterX - dfStartX) * (dfCenterX - dfStartX) + + (dfCenterY - dfStartY) * (dfCenterY - dfStartY) ); + + return NTFStrokeArcToOGRGeometry_Angles( dfCenterX, dfCenterY, + dfRadius, + dfStartAngle, dfEndAngle, + nVertexCount ); +} + +/************************************************************************/ +/* NTFStrokeArcToOGRGeometry_Angles() */ +/************************************************************************/ + +OGRGeometry * +NTFStrokeArcToOGRGeometry_Angles( double dfCenterX, double dfCenterY, + double dfRadius, + double dfStartAngle, double dfEndAngle, + int nVertexCount ) + +{ + OGRLineString *poLine = new OGRLineString; + double dfArcX, dfArcY, dfSlice; + int iPoint; + + nVertexCount = MAX(2,nVertexCount); + dfSlice = (dfEndAngle-dfStartAngle)/(nVertexCount-1); + + poLine->setNumPoints( nVertexCount ); + + for( iPoint=0; iPoint < nVertexCount; iPoint++ ) + { + double dfAngle; + + dfAngle = (dfStartAngle + iPoint * dfSlice) * PI / 180.0; + + dfArcX = dfCenterX + cos(dfAngle) * dfRadius; + dfArcY = dfCenterY + sin(dfAngle) * dfRadius; + + poLine->setPoint( iPoint, dfArcX, dfArcY ); + } + + return poLine; +} + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ogrntfdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ogrntfdatasource.cpp new file mode 100644 index 000000000..d3cee33ef --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ogrntfdatasource.cpp @@ -0,0 +1,554 @@ +/****************************************************************************** + * $Id: ogrntfdatasource.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: UK NTF Reader + * Purpose: Implements OGRNTFDataSource class + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ntf.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrntfdatasource.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* OGRNTFDataSource() */ +/************************************************************************/ + +OGRNTFDataSource::OGRNTFDataSource() + +{ + nLayers = 0; + papoLayers = NULL; + + nNTFFileCount = 0; + papoNTFFileReader = NULL; + + pszName = NULL; + + iCurrentReader = -1; + iCurrentFC = 0; + + nFCCount = 0; + papszFCNum = NULL; + papszFCName = NULL; + + poFCLayer = NULL; + + papszOptions = NULL; + + poSpatialRef = new OGRSpatialReference( "PROJCS[\"OSGB 1936 / British National Grid\",GEOGCS[\"OSGB 1936\",DATUM[\"OSGB_1936\",SPHEROID[\"Airy 1830\",6377563.396,299.3249646,AUTHORITY[\"EPSG\",\"7001\"]],AUTHORITY[\"EPSG\",\"6277\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4277\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",49],PARAMETER[\"central_meridian\",-2],PARAMETER[\"scale_factor\",0.999601272],PARAMETER[\"false_easting\",400000],PARAMETER[\"false_northing\",-100000],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"27700\"]]" ); + + +/* -------------------------------------------------------------------- */ +/* Allow initialization of options from the environment. */ +/* -------------------------------------------------------------------- */ + if( getenv("OGR_NTF_OPTIONS") != NULL ) + { + papszOptions = + CSLTokenizeStringComplex( getenv("OGR_NTF_OPTIONS"), ",", + FALSE, FALSE ); + } +} + +/************************************************************************/ +/* ~OGRNTFDataSource() */ +/************************************************************************/ + +OGRNTFDataSource::~OGRNTFDataSource() + +{ + int i; + + for( i = 0; i < nNTFFileCount; i++ ) + delete papoNTFFileReader[i]; + + CPLFree( papoNTFFileReader ); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + if( poFCLayer != NULL ) + delete poFCLayer; + + CPLFree( papoLayers ); + + CPLFree( pszName ); + + CSLDestroy( papszOptions ); + + CSLDestroy( papszFCNum ); + CSLDestroy( papszFCName ); + + if( poSpatialRef ) + poSpatialRef->Release(); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRNTFDataSource::TestCapability( const char * ) + +{ + return FALSE; +} + +/************************************************************************/ +/* GetNamedLayer() */ +/************************************************************************/ + +OGRNTFLayer * OGRNTFDataSource::GetNamedLayer( const char * pszName ) + +{ + for( int i = 0; i < nLayers; i++ ) + { + if( EQUAL(papoLayers[i]->GetLayerDefn()->GetName(),pszName) ) + return (OGRNTFLayer *) papoLayers[i]; + } + + return NULL; +} + +/************************************************************************/ +/* AddLayer() */ +/************************************************************************/ + +void OGRNTFDataSource::AddLayer( OGRLayer * poNewLayer ) + +{ + papoLayers = (OGRLayer **) + CPLRealloc( papoLayers, sizeof(void*) * ++nLayers ); + + papoLayers[nLayers-1] = poNewLayer; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRNTFDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer > nLayers ) + return NULL; + else if( iLayer == nLayers ) + return poFCLayer; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int OGRNTFDataSource::GetLayerCount() + +{ + if( poFCLayer == NULL ) + return nLayers; + else + return nLayers + 1; +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRNTFDataSource::Open( const char * pszFilename, int bTestOpen, + char ** papszLimitedFileList ) + +{ + VSIStatBuf stat; + char **papszFileList = NULL; + + pszName = CPLStrdup( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Is the given path a directory or a regular file? */ +/* -------------------------------------------------------------------- */ + if( CPLStat( pszFilename, &stat ) != 0 + || (!VSI_ISDIR(stat.st_mode) && !VSI_ISREG(stat.st_mode)) ) + { + if( !bTestOpen ) + CPLError( CE_Failure, CPLE_AppDefined, + "%s is neither a file or directory, NTF access failed.\n", + pszFilename ); + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Build a list of filenames we figure are NTF files. */ +/* -------------------------------------------------------------------- */ + if( VSI_ISREG(stat.st_mode) ) + { + papszFileList = CSLAddString( NULL, pszFilename ); + } + else + { + char **candidateFileList = CPLReadDir( pszFilename ); + int i; + + for( i = 0; + candidateFileList != NULL && candidateFileList[i] != NULL; + i++ ) + { + if( papszLimitedFileList != NULL + && CSLFindString(papszLimitedFileList, + candidateFileList[i]) == -1 ) + { + continue; + } + + if( strlen(candidateFileList[i]) > 4 + && EQUALN(candidateFileList[i] + strlen(candidateFileList[i])-4, + ".ntf",4) ) + { + char fullFilename[2048]; + + sprintf( fullFilename, "%s%c%s", + pszFilename, +#ifdef WIN32 + '\\', +#else + '/', +#endif + candidateFileList[i] ); + + papszFileList = CSLAddString( papszFileList, fullFilename ); + } + } + + CSLDestroy( candidateFileList ); + + if( CSLCount(papszFileList) == 0 ) + { + if( !bTestOpen ) + CPLError( CE_Failure, CPLE_OpenFailed, + "No candidate NTF files (.ntf) found in\n" + "directory: %s", + pszFilename ); + + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Loop over all these files trying to open them. In testopen */ +/* mode we first read the first 80 characters, to verify that */ +/* it looks like an NTF file. Note that we don't keep the file */ +/* open ... we don't want to occupy alot of file handles when */ +/* handling a whole directory. */ +/* -------------------------------------------------------------------- */ + int i; + + papoNTFFileReader = (NTFFileReader **) + CPLCalloc(sizeof(void*), CSLCount(papszFileList)); + + for( i = 0; papszFileList[i] != NULL; i++ ) + { + if( bTestOpen ) + { + char szHeader[80]; + FILE *fp; + int j; + + fp = VSIFOpen( papszFileList[i], "rb" ); + if( fp == NULL ) + continue; + + if( VSIFRead( szHeader, 80, 1, fp ) < 1 ) + { + VSIFClose( fp ); + continue; + } + + VSIFClose( fp ); + + if( !EQUALN(szHeader,"01",2) ) + continue; + + for( j = 0; j < 80; j++ ) + { + if( szHeader[j] == 10 || szHeader[j] == 13 ) + break; + } + + if( j == 80 || szHeader[j-1] != '%' ) + continue; + + } + + NTFFileReader *poFR; + + poFR = new NTFFileReader( this ); + + if( !poFR->Open( papszFileList[i] ) ) + { + delete poFR; + CSLDestroy( papszFileList ); + + return FALSE; + } + + poFR->SetBaseFID( nNTFFileCount * 1000000 + 1 ); + poFR->Close(); + + EnsureTileNameUnique( poFR ); + + papoNTFFileReader[nNTFFileCount++] = poFR; + } + + CSLDestroy( papszFileList ); + + if( nNTFFileCount == 0 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Establish generic layers. */ +/* -------------------------------------------------------------------- */ + EstablishGenericLayers(); + +/* -------------------------------------------------------------------- */ +/* Loop over all the files, collecting a unique feature class */ +/* listing. */ +/* -------------------------------------------------------------------- */ + for( int iSrcFile = 0; iSrcFile < nNTFFileCount; iSrcFile++ ) + { + NTFFileReader *poSrcReader = papoNTFFileReader[iSrcFile]; + + for( int iSrcFC = 0; iSrcFC < poSrcReader->GetFCCount(); iSrcFC++ ) + { + int iDstFC; + char *pszSrcFCName, *pszSrcFCNum; + + poSrcReader->GetFeatureClass( iSrcFC, &pszSrcFCNum, &pszSrcFCName); + + for( iDstFC = 0; iDstFC < nFCCount; iDstFC++ ) + { + if( EQUAL(pszSrcFCNum,papszFCNum[iDstFC]) ) + break; + } + + if( iDstFC >= nFCCount ) + { + nFCCount++; + papszFCNum = CSLAddString(papszFCNum,pszSrcFCNum); + papszFCName = CSLAddString(papszFCName,pszSrcFCName); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create a new layer specifically for feature classes. */ +/* -------------------------------------------------------------------- */ + if( nFCCount > 0 ) + poFCLayer = new OGRNTFFeatureClassLayer( this ); + else + poFCLayer = NULL; + + return TRUE; +} + +/************************************************************************/ +/* ResetReading() */ +/* */ +/* Cleanup, and start over. */ +/************************************************************************/ + +void OGRNTFDataSource::ResetReading() + +{ + for( int i = 0; i < nNTFFileCount; i++ ) + papoNTFFileReader[i]->Close(); + + iCurrentReader = -1; + nCurrentPos = -1; + nCurrentFID = 1; + iCurrentFC = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRNTFDataSource::GetNextFeature() + +{ + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* If we have already read all the conventional features, we */ +/* should try and return feature class features. */ +/* -------------------------------------------------------------------- */ + if( iCurrentReader == nNTFFileCount ) + { + if( iCurrentFC < nFCCount ) + return poFCLayer->GetFeature( iCurrentFC++ ); + else + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we need to open a file? */ +/* -------------------------------------------------------------------- */ + if( iCurrentReader == -1 ) + { + iCurrentReader++; + nCurrentPos = -1; + } + + if( papoNTFFileReader[iCurrentReader]->GetFP() == NULL ) + { + papoNTFFileReader[iCurrentReader]->Open(); + } + +/* -------------------------------------------------------------------- */ +/* Ensure we are reading on from the same point we were reading */ +/* from for the last feature, even if some other access */ +/* mechanism has moved the file pointer. */ +/* -------------------------------------------------------------------- */ + if( nCurrentPos != -1 ) + papoNTFFileReader[iCurrentReader]->SetFPPos( nCurrentPos, + nCurrentFID ); + +/* -------------------------------------------------------------------- */ +/* Read a feature. If we get NULL the file must be all */ +/* consumed, advance to the next file. */ +/* -------------------------------------------------------------------- */ + poFeature = papoNTFFileReader[iCurrentReader]->ReadOGRFeature(); + if( poFeature == NULL ) + { + papoNTFFileReader[iCurrentReader]->Close(); + if( GetOption("CACHING") != NULL + && EQUAL(GetOption("CACHING"),"OFF") ) + papoNTFFileReader[iCurrentReader]->DestroyIndex(); + + iCurrentReader++; + nCurrentPos = -1; + nCurrentFID = 1; + + poFeature = GetNextFeature(); + } + else + { + papoNTFFileReader[iCurrentReader]->GetFPPos(&nCurrentPos, + &nCurrentFID); + } + + return poFeature; +} + +/************************************************************************/ +/* GetFeatureClass() */ +/************************************************************************/ + +int OGRNTFDataSource::GetFeatureClass( int iFCIndex, + char ** ppszFCId, + char ** ppszFCName ) + +{ + if( iFCIndex < 0 || iFCIndex >= nFCCount ) + { + *ppszFCId = NULL; + *ppszFCName = NULL; + return FALSE; + } + else + { + *ppszFCId = papszFCNum[iFCIndex]; + *ppszFCName = papszFCName[iFCIndex]; + return TRUE; + } +} + +/************************************************************************/ +/* SetOptions() */ +/************************************************************************/ + +void OGRNTFDataSource::SetOptionList( char ** papszNewOptions ) + +{ + CSLDestroy( papszOptions ); + papszOptions = CSLDuplicate( papszNewOptions ); +} + +/************************************************************************/ +/* GetOption() */ +/************************************************************************/ + +const char *OGRNTFDataSource::GetOption( const char * pszOption ) + +{ + return CSLFetchNameValue( papszOptions, pszOption ); +} + +/************************************************************************/ +/* EnsureTileNameUnique() */ +/* */ +/* This method is called with an NTFFileReader to ensure that */ +/* it's tilename is unique relative to all the readers already */ +/* assigned to this data source. If not, a unique name is */ +/* selected for it and assigned. This method should not be */ +/* called with readers that are allready attached to the data */ +/* source. */ +/************************************************************************/ + +void OGRNTFDataSource::EnsureTileNameUnique( NTFFileReader *poNewReader ) + +{ + int iSequenceNumber = -1; + int bIsUnique; + char szCandidateName[11]; + + szCandidateName[10] = '\0'; + do + { + bIsUnique = TRUE; + if( iSequenceNumber++ == -1 ) + strncpy( szCandidateName, poNewReader->GetTileName(), 10 ); + else + sprintf( szCandidateName, "%010d", iSequenceNumber ); + + for( int iReader = 0; iReader < nNTFFileCount && bIsUnique; iReader++ ) + { + if( strcmp( szCandidateName, + GetFileReader( iReader )->GetTileName() ) == 0 ) + bIsUnique = FALSE; + } + } while( !bIsUnique ); + + if( iSequenceNumber > 0 ) + { + poNewReader->OverrideTileName( szCandidateName ); + CPLError( CE_Warning, CPLE_AppDefined, + "Forcing TILE_REF to `%s' on file %s\n" + "to avoid conflict with other tiles in this data source.", + szCandidateName, poNewReader->GetFilename() ); + } + +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ogrntfdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ogrntfdriver.cpp new file mode 100644 index 000000000..c90c546f4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ogrntfdriver.cpp @@ -0,0 +1,112 @@ +/****************************************************************************** + * $Id: ogrntfdriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: UK NTF Reader + * Purpose: Implements OGRNTFDriver + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ntf.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrntfdriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* ==================================================================== */ +/* OGRNTFDriver */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRNTFDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( !poOpenInfo->bStatOK ) + return NULL; + if( poOpenInfo->fpL != NULL ) + { + if( poOpenInfo->nHeaderBytes < 80 ) + return NULL; + const char* pszHeader = (const char*)poOpenInfo->pabyHeader; + if( !EQUALN(pszHeader,"01",2) ) + return NULL; + + int j; + for( j = 0; j < 80; j++ ) + { + if( pszHeader[j] == 10 || pszHeader[j] == 13 ) + break; + } + + if( j == 80 || pszHeader[j-1] != '%' ) + return FALSE; + } + + OGRNTFDataSource *poDS = new OGRNTFDataSource; + if( !poDS->Open( poOpenInfo->pszFilename, TRUE ) ) + { + delete poDS; + poDS = NULL; + } + + if( poDS != NULL && poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "NTF Driver doesn't support update." ); + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRNTF() */ +/************************************************************************/ + +void RegisterOGRNTF() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "UK .NTF" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "UK .NTF" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "UK .NTF" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_ntf.html" ); + + poDriver->pfnOpen = OGRNTFDriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ogrntffeatureclasslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ogrntffeatureclasslayer.cpp new file mode 100644 index 000000000..b1b561883 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ogrntffeatureclasslayer.cpp @@ -0,0 +1,186 @@ +/****************************************************************************** + * $Id: ogrntffeatureclasslayer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: UK NTF Reader + * Purpose: Implements OGRNTFFeatureClassLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ntf.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrntffeatureclasslayer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGRNTFFeatureClassLayer() */ +/* */ +/* Note that the OGRNTFLayer assumes ownership of the passed */ +/* OGRFeatureDefn object. */ +/************************************************************************/ + +OGRNTFFeatureClassLayer::OGRNTFFeatureClassLayer( OGRNTFDataSource *poDSIn ) + +{ + poFilterGeom = NULL; + + poDS = poDSIn; + + iCurrentFC = 0; + +/* -------------------------------------------------------------------- */ +/* Establish the schema. */ +/* -------------------------------------------------------------------- */ + poFeatureDefn = new OGRFeatureDefn( "FEATURE_CLASSES" ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->SetGeomType( wkbNone ); + poFeatureDefn->Reference(); + + OGRFieldDefn oFCNum( "FEAT_CODE", OFTString ); + + oFCNum.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFCNum ); + + OGRFieldDefn oFCName( "FC_NAME", OFTString ); + + oFCNum.SetWidth( 80 ); + poFeatureDefn->AddFieldDefn( &oFCName ); +} + +/************************************************************************/ +/* ~OGRNTFFeatureClassLayer() */ +/************************************************************************/ + +OGRNTFFeatureClassLayer::~OGRNTFFeatureClassLayer() + +{ + if( poFeatureDefn ) + poFeatureDefn->Release(); + + if( poFilterGeom != NULL ) + delete poFilterGeom; +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRNTFFeatureClassLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) + +{ + if( poFilterGeom != NULL ) + { + delete poFilterGeom; + poFilterGeom = NULL; + } + + if( poGeomIn != NULL ) + poFilterGeom = poGeomIn->clone(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRNTFFeatureClassLayer::ResetReading() + +{ + iCurrentFC = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRNTFFeatureClassLayer::GetNextFeature() + +{ + if( iCurrentFC >= GetFeatureCount() ) + return NULL; + + return GetFeature( (long) iCurrentFC++ ); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRNTFFeatureClassLayer::GetFeature( GIntBig nFeatureId ) + +{ + char *pszFCName, *pszFCId; + + if( nFeatureId < 0 || nFeatureId >= poDS->GetFCCount() ) + return NULL; + + poDS->GetFeatureClass( (int)nFeatureId, &pszFCId, &pszFCName ); + +/* -------------------------------------------------------------------- */ +/* Create a corresponding feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + poFeature->SetField( 0, pszFCId ); + poFeature->SetField( 1, pszFCName ); + poFeature->SetFID( nFeatureId ); + + return poFeature; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRNTFFeatureClassLayer::GetFeatureCount( CPL_UNUSED int bForce ) +{ + return poDS->GetFCCount(); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRNTFFeatureClassLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return TRUE; + + else + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ogrntflayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ogrntflayer.cpp new file mode 100644 index 000000000..9d16f420c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ntf/ogrntflayer.cpp @@ -0,0 +1,217 @@ +/****************************************************************************** + * $Id: ogrntflayer.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: UK NTF Reader + * Purpose: Implements OGRNTFLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ntf.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrntflayer.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* OGRNTFLayer() */ +/* */ +/* Note that the OGRNTFLayer assumes ownership of the passed */ +/* OGRFeatureDefn object. */ +/************************************************************************/ + +OGRNTFLayer::OGRNTFLayer( OGRNTFDataSource *poDSIn, + OGRFeatureDefn * poFeatureDefine, + NTFFeatureTranslator pfnTranslatorIn ) + +{ + poDS = poDSIn; + poFeatureDefn = poFeatureDefine; + SetDescription( poFeatureDefn->GetName() ); + pfnTranslator = pfnTranslatorIn; + + iCurrentReader = -1; + nCurrentPos = -1; + nCurrentFID = 1; +} + +/************************************************************************/ +/* ~OGRNTFLayer() */ +/************************************************************************/ + +OGRNTFLayer::~OGRNTFLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "Mem", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poFeatureDefn ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRNTFLayer::ResetReading() + +{ + iCurrentReader = -1; + nCurrentPos = -1; + nCurrentFID = 1; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRNTFLayer::GetNextFeature() + +{ + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* Have we processed all features already? */ +/* -------------------------------------------------------------------- */ + if( iCurrentReader == poDS->GetFileCount() ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Do we need to open a file? */ +/* -------------------------------------------------------------------- */ + if( iCurrentReader == -1 ) + { + iCurrentReader++; + nCurrentPos = -1; + } + + NTFFileReader *poCurrentReader = poDS->GetFileReader(iCurrentReader); + if( poCurrentReader->GetFP() == NULL ) + { + poCurrentReader->Open(); + } + +/* -------------------------------------------------------------------- */ +/* Ensure we are reading on from the same point we were reading */ +/* from for the last feature, even if some other access */ +/* mechanism has moved the file pointer. */ +/* -------------------------------------------------------------------- */ + if( nCurrentPos != -1 ) + poCurrentReader->SetFPPos( nCurrentPos, nCurrentFID ); + else + poCurrentReader->Reset(); + +/* -------------------------------------------------------------------- */ +/* Read features till we find one that satisfies our current */ +/* spatial criteria. */ +/* -------------------------------------------------------------------- */ + while( TRUE ) + { + poFeature = poCurrentReader->ReadOGRFeature( this ); + if( poFeature == NULL ) + break; + + m_nFeaturesRead++; + + if( (m_poFilterGeom == NULL + || poFeature->GetGeometryRef() == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + break; + + delete poFeature; + } + +/* -------------------------------------------------------------------- */ +/* If we get NULL the file must be all consumed, advance to the */ +/* next file that contains features for this layer. */ +/* -------------------------------------------------------------------- */ + if( poFeature == NULL ) + { + poCurrentReader->Close(); + + if( poDS->GetOption("CACHING") != NULL + && EQUAL(poDS->GetOption("CACHING"),"OFF") ) + { + poCurrentReader->DestroyIndex(); + } + + do { + iCurrentReader++; + } while( iCurrentReader < poDS->GetFileCount() + && !poDS->GetFileReader(iCurrentReader)->TestForLayer(this) ); + + nCurrentPos = -1; + nCurrentFID = 1; + + poFeature = GetNextFeature(); + } + else + { + poCurrentReader->GetFPPos(&nCurrentPos, &nCurrentFID); + } + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRNTFLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return FALSE; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else + return FALSE; +} + +/************************************************************************/ +/* FeatureTranslate() */ +/************************************************************************/ + +OGRFeature * OGRNTFLayer::FeatureTranslate( NTFFileReader *poReader, + NTFRecord ** papoGroup ) + +{ + if( pfnTranslator == NULL ) + return NULL; + + return pfnTranslator( poReader, this, papoGroup ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/null/ogrnulldriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/null/ogrnulldriver.cpp new file mode 100644 index 000000000..8a82dc809 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/null/ogrnulldriver.cpp @@ -0,0 +1,266 @@ +/****************************************************************************** + * $Id: ogrnulldriver.cpp 28039 2014-11-30 18:24:59Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: NULL output driver. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* NOTE: this driver is only useful for debugging and is not included in the build process */ +/* To compile it as a pluing under Linux : + g++ -Wall -DDEBUG -fPIC -g ogr/ogrsf_frmts/null/ogrnulldriver.cpp -shared -o ogr_NULL.so -L. -lgdal -Iport -Igcore -Iogr -Iogr/ogrsf_frmts +*/ + +#include "ogrsf_frmts.h" + +CPL_CVSID("$Id: ogrnulldriver.cpp 28039 2014-11-30 18:24:59Z rouault $"); + +extern "C" void CPL_DLL RegisterOGRNULL(); + +/************************************************************************/ +/* OGRNULLLayer */ +/************************************************************************/ + +class OGRNULLLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + OGRSpatialReference *poSRS; + + public: + OGRNULLLayer( const char *pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType ); + virtual ~OGRNULLLayer(); + + virtual OGRFeatureDefn *GetLayerDefn() {return poFeatureDefn;} + virtual OGRSpatialReference * GetSpatialRef() { return poSRS; } + + virtual void ResetReading() {} + virtual int TestCapability( const char * ); + + virtual OGRFeature *GetNextFeature() { return NULL; } + + virtual OGRErr ICreateFeature( OGRFeature *poFeature ) { return OGRERR_NONE; } + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); +}; + +/************************************************************************/ +/* OGRNULLDataSource */ +/************************************************************************/ + +class OGRNULLDataSource : public OGRDataSource +{ + int nLayers; + OGRLayer** papoLayers; + char* pszName; + + public: + OGRNULLDataSource(const char* pszNameIn); + ~OGRNULLDataSource(); + + virtual const char *GetName() { return pszName; } + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer *GetLayer( int ); + + virtual OGRLayer *ICreateLayer( const char *pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char **papszOptions ); + + virtual int TestCapability( const char * ); + +}; + +/************************************************************************/ +/* OGRNULLDriver */ +/************************************************************************/ + +class OGRNULLDriver : public OGRSFDriver +{ + public: + ~OGRNULLDriver() {}; + + virtual const char *GetName() { return "NULL"; } + virtual OGRDataSource *Open( const char *, int ) { return NULL; } + virtual OGRDataSource *CreateDataSource( const char * pszName, + char **papszOptions ); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRNULLLayer() */ +/************************************************************************/ + +OGRNULLLayer::OGRNULLLayer( const char *pszLayerName, + OGRSpatialReference *poSRSIn, + OGRwkbGeometryType eType ) +{ + poFeatureDefn = new OGRFeatureDefn(pszLayerName); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->SetGeomType(eType); + poFeatureDefn->Reference(); + + poSRS = poSRSIn ? poSRSIn : NULL; + if (poSRS) + poSRS->Reference(); +} + +/************************************************************************/ +/* ~OGRNULLLayer() */ +/************************************************************************/ + +OGRNULLLayer::~OGRNULLLayer() +{ + poFeatureDefn->Release(); + + if (poSRS) + poSRS->Release(); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRNULLLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap, OLCSequentialWrite) ) + return TRUE; + if( EQUAL(pszCap, OLCCreateField) ) + return TRUE; + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRNULLLayer::CreateField( OGRFieldDefn *poField, + int bApproxOK ) +{ + poFeatureDefn->AddFieldDefn(poField); + return OGRERR_NONE; +} + +/************************************************************************/ +/* OGRNULLDataSource() */ +/************************************************************************/ + +OGRNULLDataSource::OGRNULLDataSource(const char* pszNameIn) +{ + pszName = CPLStrdup(pszNameIn); + nLayers = 0; + papoLayers = NULL; +} + +/************************************************************************/ +/* ~OGRNULLDataSource() */ +/************************************************************************/ + +OGRNULLDataSource::~OGRNULLDataSource() +{ + int i; + for(i=0;i= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRNULLDriver::CreateDataSource( const char * pszName, + char **papszOptions ) +{ + return new OGRNULLDataSource(pszName); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRNULLDriver::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap, ODrCCreateDataSource) ) + return TRUE; + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRNULL() */ +/************************************************************************/ + +void RegisterOGRNULL() +{ + if (! GDAL_CHECK_VERSION("OGR/NULL driver")) + return; + + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( new OGRNULLDriver ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/o/README.TXT b/bazaar/plugin/gdal/ogr/ogrsf_frmts/o/README.TXT new file mode 100644 index 000000000..71f041c64 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/o/README.TXT @@ -0,0 +1,7 @@ +gdal/ogr/ogrsf_frmts/o +------------ + +This directory is where object files are put from the various format +drivers so they can be easily linked into GDAL. This file is here to +ensure that the "o" directory isn't lost when people do a cvs checkout +with pruning. diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/GNUmakefile new file mode 100644 index 000000000..5cd9dc551 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/GNUmakefile @@ -0,0 +1,29 @@ + + +include ../../../GDALmake.opt + +OBJ = oci_utils.o ogrocisession.o ogrocistatement.o \ + ogrocidriver.o ogrocidatasource.o ogrocilayer.o \ + ogrocitablelayer.o ogrociselectlayer.o ogrocistringbuf.o \ + ogrociwritablelayer.o ogrociloaderlayer.o ogrocistroke.o + +CPPFLAGS := $(GDAL_INCLUDE) $(OCI_INCLUDE) $(CPPFLAGS) -fPIC + +PLUGIN_SO = ogr_OCI.so + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_oci.h + +fastload: fastload.cpp + $(CXX) $(CPPFLAGS) $(CFLAGS) fastload.cpp -L../../.. -lgdal.1.1 \ + -o fastload + +plugin: $(PLUGIN_SO) + +$(PLUGIN_SO): $(OBJ) + $(LD_SHARED) $(LNK_FLAGS) $(OBJ) $(CONFIG_LIBS_INS) $(LIBS) \ + -o $(PLUGIN_SO) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/drv_oci.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/drv_oci.html new file mode 100644 index 000000000..35c42fb37 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/drv_oci.html @@ -0,0 +1,213 @@ + + +Oracle Spatial + + + + +

    Oracle Spatial

    + +This driver supports reading and writing data in Oracle Spatial (8.1.7 or +later) Object-Relational format. The Oracle Spatial driver is not +normally built into OGR, but may be built in on platforms where the +Oracle client libraries are available.

    + +When opening a database, it's name should be specified in the form +"OCI:userid/password@database_instance:table,table". The list of tables +is optional. The database_instance portion may be omitted +when accessing the default local database instance.

    + +If the list of tables is not provided, then all tables appearing in +ALL_SDO_GEOM_METADATA will be treated by OGR as layers with the table +names as the layer names. Non-spatial tables or spatial tables not listed +in the ALL_SDO_GEOM_METADATA table are not accessable unless explicitly +listed in the datasource name. Even in databases where all desired layers +are in the ALL_SDO_GEOM_METADATA table, it may be desirable to list only +the tables to be used as this can substantially reduce initialization time +in databases with many tables.

    + +If the table has an integer column called OGR_FID it will be used as the +feature id by OGR (and it will not appear as a regular attribute). When +loading data into Oracle Spatial OGR will always create the OGR_FID field. +

    + +

    SQL Issues

    + +

    By default, the Oracle driver passes SQL statements directly to Oracle rather +than evaluating them internally when using the ExecuteSQL() call on the +OGRDataSource, or the -sql command option to ogr2ogr. Attribute query +expressions are also passed through to Oracle.

    + +

    As well two special commands are supported via the ExecuteSQL() interface. +These are "DELLAYER:<table_name>" to delete a layer, +and "VALLAYER:<table_name>" to apply the +SDO_GEOM.VALIDATE_GEOMETRY() check to a layer. Internally these +pseudo-commands are translated into more complex SQL commands for Oracle.

    + +

    It's also possible to request the driver to handle SQL commands with OGR SQL engine, +by passing "OGRSQL" string to the ExecuteSQL() method, as name of the SQL dialect.

    + +

    Caveats

    + +
      + +
    • The type recognition logic is currently somewhat impoverished. No +effort is made to preserve real width information for integer and real +fields.
    • + +
    • Various types such as objects, and BLOBs in Oracle will be completely +ignored by OGR.
    • + +
    • Currently the OGR transaction semantics are not properly mapped onto +transaction semantics in Oracle.
    • + +
    • If an attribute called OGR_FID exists in the schema for tables being +read, it will be used as the FID. Random (FID based) reads on tables without +an identified (and indexed) FID field can be very slow. To force use of a +particular field name the OCI_FID configuration variable (ie. environment +variable) can be set to the target field name.
    • + +
    • Curved geometry types are converted to linestrings or linear rings in six degree segments when reading. The driver has no support for writing curved geometries.
    • + +
    • There is no support for point cloud (SDO_PC), TIN (SDO_TIN) and annotation text data types in Oracle Spatial.
    • + +
    • It might be necessary to define the environment variable NLS_LANG to +"American_America.UTF8" to avoid issues with floating point numbers being +truncated to integer on non-English environments.
    • + +
    • For developers: when running the driver under the memory error detection +tool Valgrind, specifying the database_instance, typically to localhost, or with +the TWO_TASK environment variable seems to be +compulsory, otherwise "TNS:permission denied" errors will be reported)
    • + +
    + +

    Creation Issues

    + +The Oracle Spatial driver does not support creation of new datasets (database +instances), but it does allow creation of new layers within an +existing database.

    + +Upon closing the OGRDataSource newly created layers will have a spatial +index automatically built. At this point the USER_SDO_GEOM_METADATA table +will also be updated with bounds for the table based on the features that +have actually been written. One concequence of this is that once a layer +has been loaded it is generally not possible to load additional features +outside the original extents without manually modifying the DIMINFO information +in USER_SDO_GEOM_METADATA and rebuilding the spatial index.

    + +

    Layer Creation Options

    + +
      + +
    • OVERWRITE: This may be "YES" to force an existing layer of the +desired name to be destroyed before creating the requested layer.

      + +

    • TRUNCATE: This may be "YES" to force the existing table to +be reused, but to first truncate all records in the table, preserving +indexes or dependencies.

      + +

    • LAUNDER: This may be "YES" to force new fields created on this +layer to have their field names "laundered" into a form more compatible with +Oracle. This converts to upper case and converts some special characters +like "-" and "#" to "_". The default value is "NO".

      + +

    • PRECISION: This may be "YES" to force new fields created on this +layer to try and represent the width and precision information, if available +using NUMBER(width,precision) or VARCHAR2(width) types. If "NO" then the types +NUMBER, INTEGER and VARCHAR2 will be used instead. The default is "YES".

      + +

    • DIM: This may be set to 2 or 3 to force the dimension of the +created layer. If not set 3 is used by default.

      + +

    • SPATIAL_INDEX: This may be set to FALSE to disable creation of a spatial +index when a layer load is complete. By default an index is created if +any of the layer features have valid geometries. The default is "YES". +Note: option was called INDEX in releases before GDAL 2

      + +

    • INDEX_PARAMETERS: This may be set to pass creation parameters +when the spatial index is created. For instance setting INDEX_PARAMETERS to +SDO_LEVEL=5 would cause a 5 level tile index to be used. By default no +parameters are passed causing a default R-Tree spatial index to be created.

      + +

    • ADD_LAYER_GTYPE=YES/NO: (starting with GDAL 2.0) This may be +set to NO to disable the constraints on the geometry type in the spatial index, +through the layer_gtype keyword in the PARAMETERS clause of the CREATE INDEX. +Layers of type MultiPoint, MultiLineString or MultiPolygon will also accept +single geometry type (Point, LineString, Polygon). Defaults to YES.

      + +

    • DIMINFO_X: This may be set to xmin,xmax,xres values to +control the X dimension info written into the USER_SDO_GEOM_METADATA table. +By default extents are collected from the actual data written.

      + +

    • DIMINFO_Y: This may be set to ymin,ymax,yres values to +control the Y dimension info written into the USER_SDO_GEOM_METADATA table. +By default extents are collected from the actual data written.

      + +

    • DIMINFO_Z: This may be set to zmin,zmax,zres values to +control the Z dimension info written into the USER_SDO_GEOM_METADATA table. +By default fixed values of -100000,100000,0.002 are used for layers with +a third dimension.

      + +

    • SRID: By default this driver will attempt to find an existing +row in the MDSYS.CS_SRS table with a well known text coordinate system +exactly matching the one for this dataset. If one is not found, a new +row will be added to this table. The SRID creation option allows the user +to force use of an existing Oracle SRID item even it if does not exactly +match the WKT the driver expects.

      + +

    • MULTI_LOAD: If enabled new features will be created in +groups of 100 per SQL INSERT command, instead of each feature being a separate +INSERT command. Having this enabled is the fastest way to load data quickly. +Multi-load mode is enabled by default, and may be forced off for existing +layers or for new layers by setting to NO.

      + +

    • LOADER_FILE: If this option is set, all feature information will +be written to a file suitable for use with SQL*Loader instead of inserted +directly in the database. The layer itself is still created in the database +immediately. The SQL*Loader support is experimental, and generally +MULTI_LOAD enabled mode should be used instead when trying for optimal +load performance.

      + +

    • GEOMETRY_NAME: By default OGR creates new tables with the +geometry column named ORA_GEOMETRY. If you wish to use a different name, +it can be supplied with the GEOMETRY_NAME layer creation option.

      + +

    + +

    Example

    + +Simple translation of a shapefile into Oracle. The table 'ABC' will +be created with the features from abc.shp and attributes from abc.dbf.

    + +

    +% ogr2ogr -f OCI OCI:warmerda/password@gdal800.dreadfest.com abc.shp
    +
    + +This second example loads a political boundaries layer from VPF (via the +OGDI driver), and renames the layer from the +cryptic OGDI layer name to something more sensible. If an existing table +of the desired name exists it is overwritten.

    + +

    +% ogr2ogr  -f OCI OCI:warmerda/password \
    +        gltp:/vrf/usr4/mpp1/v0eur/vmaplv0/eurnasia \
    +        -lco OVERWRITE=yes -nln polbndl_bnd 'polbndl@bnd(*)_line'
    +
    + +This example shows using ogrinfo to evaluate an SQL query statement +within Oracle. More sophisticated Oracle Spatial specific queries may also be +used via the -sql commandline switch to ogrinfo.

    + +

    +ogrinfo -ro OCI:warmerda/password -sql "SELECT pop_1994 from canada where province_name = 'Alberta'"
    +
    + + +

    Credits

    + +I would like to thank SRC, LLC +for it's financial support of the development of this driver.

    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/fastload.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/fastload.cpp new file mode 100644 index 000000000..d73478257 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/fastload.cpp @@ -0,0 +1,241 @@ +/****************************************************************************** + * $Id: fastload.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: Oracle Spatial Driver + * Purpose: Test mainline for fast loading. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ogr_oci.h" + +int main() + +{ + OGROCISession oSession; + + if( !oSession.EstablishSession( "warmerda", "LetoKing", + "gdal800.dreadfest.com" ) ) + { + exit( 1 ); + } + + printf( "Session established.\n" ); + + OGROCIStatement oStatement( &oSession ); + + oStatement.Execute( "DROP TABLE fasttest" ); + oStatement.Execute( "CREATE TABLE fasttest (ifld INTEGER, cfld VARCHAR(4000), shape mdsys.sdo_geometry)" ); +// oStatement.Execute( "CREATE TABLE fasttest (ifld INTEGER, cfld VARCHAR(4000))" ); + +/* -------------------------------------------------------------------- */ +/* Prepare insert statement. */ +/* -------------------------------------------------------------------- */ + + oStatement.Prepare( "INSERT INTO fasttest VALUES " + "(:field_1, :field_2, :field_3)" ); +// oStatement.Prepare( "INSERT INTO fasttest VALUES " +// "(:field_1, :field_2)" ); + +/* -------------------------------------------------------------------- */ +/* Do a conventional bind. */ +/* -------------------------------------------------------------------- */ + int anField1[100]; + char szField2[100*4]; + int anGType[100]; + int anSRID[100]; + OCIArray *aphElemInfos[100]; + OCIArray *aphOrdinates[100]; + SDO_GEOMETRY_TYPE aoGeometries[100]; + SDO_GEOMETRY_ind aoGeometryIndicators[100]; + SDO_GEOMETRY_TYPE *apoGeomMap[100]; + SDO_GEOMETRY_ind *apoGeomIndMap[100]; + double adfX[100], adfY[100]; + + memset( aphElemInfos, 0, sizeof(OCIArray*) * 100 ); + memset( aphOrdinates, 0, sizeof(OCIArray*) * 100 ); + memset( aoGeometries, 0, sizeof(SDO_GEOMETRY) * 100 ); + memset( aoGeometryIndicators, 0, sizeof(SDO_GEOMETRY_ind) * 100 ); + + if( oStatement.BindScalar( ":field_1", anField1, + sizeof(int), SQLT_INT ) != CE_None ) + exit( 1 ); + + if( oStatement.BindScalar( ":field_2", szField2, 4, SQLT_STR ) != CE_None ) + exit( 1 ); + + if( oStatement.BindObject( ":field_3", apoGeomMap, oSession.hGeometryTDO, + (void**)apoGeomIndMap ) != CE_None ) + exit( 1 ); + +/* -------------------------------------------------------------------- */ +/* Create array of arrays for elem_info and ordinates. */ +/* -------------------------------------------------------------------- */ + int iBindRow; + for( iBindRow = 0; iBindRow < 100; iBindRow++ ) + { + if( oSession.Failed( + OCIObjectNew( oSession.hEnv, oSession.hError, + oSession.hSvcCtx, OCI_TYPECODE_VARRAY, + oSession.hElemInfoTDO, (dvoid *)NULL, + OCI_DURATION_SESSION, + FALSE, (dvoid **) (aphElemInfos + iBindRow)), + "OCIObjectNew()") ) + exit( 1 ); + + if( oSession.Failed( + OCIObjectNew( oSession.hEnv, oSession.hError, + oSession.hSvcCtx, OCI_TYPECODE_VARRAY, + oSession.hOrdinatesTDO, (dvoid *)NULL, + OCI_DURATION_SESSION, + FALSE, (dvoid **) (aphOrdinates + iBindRow)), + "OCIObjectNew()") ) + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Populate VARRAYs */ +/* -------------------------------------------------------------------- */ + int iRow; + + for( iRow = 0; iRow < 100; iRow++ ) + { + anField1[iRow] = iRow; + sprintf( szField2 + iRow*4, "%3d", iRow ); + anGType[iRow] = 3001; + anSRID[iRow] = -1; + adfX[iRow] = 100.0 + iRow; + adfY[iRow] = 100.0 - iRow; + + //--------------------------------------------------------------- + int anElemInfo[3], nElemInfoCount; + OCINumber oci_number; + int i; + + nElemInfoCount = 3; + anElemInfo[0] = 1; + anElemInfo[1] = 1; + anElemInfo[2] = 1; + + // Prepare the VARRAY of ordinate values. + for (i = 0; i < nElemInfoCount; i++) + { + if( oSession.Failed( + OCINumberFromInt( oSession.hError, + (dvoid *) (anElemInfo + i), + (uword)sizeof(int), + OCI_NUMBER_SIGNED, + &oci_number), + "OCINumberFromInt") ) + exit( 1 ); + + if( oSession.Failed( + OCICollAppend( oSession.hEnv, oSession.hError, + (dvoid *) &oci_number, + (dvoid *)0, aphElemInfos[iRow]), + "OCICollAppend") ) + exit( 1 ); + } + + //--------------------------------------------------------------- + double adfOrdinates[6]; + int nOrdCount; + + nOrdCount = 3; + adfOrdinates[0] = iRow + 100; + adfOrdinates[1] = iRow - 100; + adfOrdinates[2] = 0.0; + adfOrdinates[3] = iRow + 100; + adfOrdinates[4] = iRow - 100; + adfOrdinates[5] = 0.0; + + // Prepare the VARRAY of ordinate values. + for (i = 0; i < nOrdCount; i++) + { + if( oSession.Failed( + OCINumberFromReal( oSession.hError, + (dvoid *) (adfOrdinates + i), + (uword)sizeof(double), + &oci_number), + "OCINumberFromReal") ) + exit( 1 ); + + if( oSession.Failed( + OCICollAppend( oSession.hEnv, oSession.hError, + (dvoid *) &oci_number, + (dvoid *)0, aphOrdinates[iRow]), + "OCICollAppend") ) + exit( 1 ); + } + + // ------------------------------------------------------------- + SDO_GEOMETRY_TYPE *poGeom = aoGeometries + iRow; + SDO_GEOMETRY_ind *poInd = aoGeometryIndicators + iRow; + + poInd->sdo_point._atomic = OCI_IND_NULL; + + if( oSession.Failed( + OCINumberFromInt( oSession.hError, + (dvoid *) (anGType + iRow), + (uword)sizeof(int), + OCI_NUMBER_SIGNED, + &(poGeom->sdo_gtype)), + "OCINumberFromInt" ) ) + exit( 1 ); + + if( oSession.Failed( + OCINumberFromInt( oSession.hError, + (dvoid *) (anSRID + iRow), + (uword)sizeof(int), + OCI_NUMBER_SIGNED, + &(poGeom->sdo_srid)), + "OCINumberFromInt" ) ) + exit( 1 ); + + poGeom->sdo_elem_info = aphElemInfos[iRow]; + poGeom->sdo_ordinates = aphOrdinates[iRow]; + + apoGeomMap[iRow] = poGeom; + apoGeomIndMap[iRow] = poInd; + } + +/* -------------------------------------------------------------------- */ +/* Execute the statement. */ +/* -------------------------------------------------------------------- */ + int iGroup; + + for( iGroup = 0; iGroup < 2; iGroup++ ) + { + if( oSession.Failed( + OCIStmtExecute( oSession.hSvcCtx, oStatement.GetStatement(), + oSession.hError, (ub4) 100, (ub4)0, + (OCISnapshot *)NULL, (OCISnapshot *)NULL, + (ub4) OCI_COMMIT_ON_SUCCESS ), + "OCIStmtExecute" ) ) + exit( 1 ); + } + + printf( "Successful completion\n" ); + exit( 0 ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/makefile.vc new file mode 100644 index 000000000..07df39a85 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/makefile.vc @@ -0,0 +1,47 @@ + +LL_OBJ = ogrocisession.obj ogrocistatement.obj +OGR_OBJ = ogrocidriver.obj ogrocidatasource.obj ogrocilayer.obj \ + ogrocitablelayer.obj ogrociselectlayer.obj ogrocistringbuf.obj\ + ogrociwritablelayer.obj ogrociloaderlayer.obj \ + ogrocistroke.obj + +PLUGIN_DLL = ogr_OCI.dll + +OBJ = $(LL_OBJ) $(OGR_OBJ) + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + + +EXTRAFLAGS = -I.. -I..\.. $(OCI_INCLUDE) + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + -del *.exe + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) \ + $(GDAL_ROOT)/gdal_i.lib $(OCI_LIB) + if exist ogr_OCI.dll.manifest mt -manifest ogr_OCI.dll.manifest \ + -outputresource:ogr_OCI.dll;2 + +ocitest.exe: $(LL_OBJ) ocitest.obj + cl /Zi ocitest.obj $(LL_OBJ) \ + ../../ogr.lib ../ogrsf_frmts.lib ../ogrsf_frmts_sup.lib \ + $(GDAL_ROOT)/port/cpl.lib $(OCI_LIB) $(LIBS) + +fastload.exe: $(LL_OBJ) fastload.obj + cl /Zi fastload.obj $(LL_OBJ) \ + ../../ogr.lib ../ogrsf_frmts.lib ../ogrsf_frmts_sup.lib \ + $(GDAL_ROOT)/port/cpl.lib $(OCI_LIB) $(LIBS) + + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/oci_utils.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/oci_utils.cpp new file mode 100644 index 000000000..8ea4a2794 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/oci_utils.cpp @@ -0,0 +1,33 @@ +/****************************************************************************** + * $Id: oci_utils.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: Oracle Spatial Driver + * Purpose: Various low level utility functions for OCI connections. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_oci.h" + +CPL_CVSID("$Id: oci_utils.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ocitest.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ocitest.cpp new file mode 100644 index 000000000..c9a9c9c66 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ocitest.cpp @@ -0,0 +1,77 @@ +/****************************************************************************** + * $Id: ocitest.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: Oracle Spatial Driver + * Purpose: Test mainline for Oracle Spatial Driver low level functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_oci.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ocitest.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* main() */ +/************************************************************************/ + +int main( int nArgc, char ** papszArgv ) + +{ + OGROCISession *poSession = NULL; + const char *pszStatement = "SELECT * FROM NEPSITE"; + int nColCount; + char **papszResult; + + if( nArgc > 1 ) + pszStatement = papszArgv[1]; + + poSession = OGRGetOCISession( "system", "LetoKing", "" ); + if( poSession == NULL ) + exit( 1 ); + + OGROCIStatement oStatement( poSession ); + + if( oStatement.Execute( pszStatement ) == CE_Failure ) + exit( 2 ); + + while( (papszResult = oStatement.SimpleFetchRow()) != NULL ) + { + OGRFeatureDefn *poDefn = oStatement.GetResultDefn(); + int nColCount = poDefn->GetFieldCount(); + int i; + + printf( "\n" ); + for( i = 0; i < nColCount; i++ ) + { + printf( " %s = %s\n", + poDefn->GetFieldDefn(i)->GetNameRef(), + papszResult[i] ); + } + } +} + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogr_oci.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogr_oci.h new file mode 100644 index 000000000..6e8a1343b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogr_oci.h @@ -0,0 +1,562 @@ +/****************************************************************************** + * $Id: ogr_oci.h 29019 2015-04-25 20:34:19Z rouault $ + * + * Project: Oracle Spatial Driver + * Purpose: Oracle Spatial OGR Driver Declarations. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_OCI_H_INCLUDED +#define _OGR_OCI_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "oci.h" +#include "cpl_error.h" + +/* -------------------------------------------------------------------- */ +/* Low level Oracle spatial declarations. */ +/* -------------------------------------------------------------------- */ +#define TYPE_OWNER "MDSYS" +#define SDO_GEOMETRY "MDSYS.SDO_GEOMETRY" + +typedef struct +{ + OCINumber x; + OCINumber y; + OCINumber z; +} sdo_point_type; + +typedef OCIArray sdo_elem_info_array; +typedef OCIArray sdo_ordinate_array; + +typedef struct +{ + OCINumber sdo_gtype; + OCINumber sdo_srid; + sdo_point_type sdo_point; + OCIArray *sdo_elem_info; + OCIArray *sdo_ordinates; +} SDO_GEOMETRY_TYPE; + +typedef struct +{ + OCIInd _atomic; + OCIInd x; + OCIInd y; + OCIInd z; +} sdo_point_type_ind; + +typedef struct +{ + OCIInd _atomic; + OCIInd sdo_gtype; + OCIInd sdo_srid; + sdo_point_type_ind sdo_point; + OCIInd sdo_elem_info; + OCIInd sdo_ordinates; +} SDO_GEOMETRY_ind; + +#define ORA_GTYPE_MATCH(a,b) ( ((a) % 100) == ((b) % 100)) +#define ORA_GTYPE_UNKNOWN 0 +#define ORA_GTYPE_POINT 1 +#define ORA_GTYPE_LINESTRING 2 // or curve +#define ORA_GTYPE_POLYGON 3 // or surface +#define ORA_GTYPE_COLLECTION 4 +#define ORA_GTYPE_MULTIPOINT 5 +#define ORA_GTYPE_MULTILINESTRING 6 // or multicurve +#define ORA_GTYPE_MULTIPOLYGON 7 // or multisurface +#define ORA_GTYPE_SOLID 8 +#define ORA_GTYPE_MULTISOLID 9 + + +/************************************************************************/ +/* OGROCISession */ +/************************************************************************/ +class CPL_DLL OGROCISession { + public: + OCIEnv *hEnv; + OCIError *hError; + OCISvcCtx *hSvcCtx; + OCIServer *hServer; + OCISession *hSession; + OCIDescribe*hDescribe; + OCIType *hGeometryTDO; + OCIType *hOrdinatesTDO; + OCIType *hElemInfoTDO; + + char *pszUserid; + char *pszPassword; + char *pszDatabase; + + public: + OGROCISession(); + virtual ~OGROCISession(); + + int EstablishSession( const char *pszUserid, + const char *pszPassword, + const char *pszDatabase ); + + int Failed( sword nStatus, const char *pszFunction = NULL ); + + CPLErr GetParmInfo( OCIParam *hParmDesc, OGRFieldDefn *poOGRDefn, + ub2 *pnOCIType, ub4 *pnOCILen ); + + void CleanName( char * ); + + OCIType *PinTDO( const char * ); + + private: + +}; + +OGROCISession CPL_DLL* +OGRGetOCISession( const char *pszUserid, + const char *pszPassword, + const char *pszDatabase ); + +/************************************************************************/ +/* OGROCIStatement */ +/************************************************************************/ +class CPL_DLL OGROCIStatement { + public: + OGROCIStatement( OGROCISession * ); + virtual ~OGROCIStatement(); + + OCIStmt *GetStatement() { return hStatement; } + CPLErr BindScalar( const char *pszPlaceName, + void *pData, int nDataLen, int nSQLType, + sb2 *paeInd = NULL ); + CPLErr BindObject( const char *pszPlaceName, void *pahObject, + OCIType *hTDO, void **papIndicators ); + + char *pszCommandText; + + CPLErr Prepare( const char * pszStatement ); + CPLErr Execute( const char * pszStatement, + int nMode = -1 ); + void Clean(); + + OGRFeatureDefn *GetResultDefn() { return poDefn; } + + char **SimpleFetchRow(); + + int GetAffectedRows() const { return nAffectedRows; } + + private: + OGROCISession *poSession; + OCIStmt *hStatement; + + OGRFeatureDefn*poDefn; + + char **papszCurColumn; + char **papszCurImage; + sb2 *panCurColumnInd; + + int nRawColumnCount; + int *panFieldMap; + int nAffectedRows; +}; + +/************************************************************************/ +/* OGROCIStringBuf */ +/************************************************************************/ +class OGROCIStringBuf +{ + char *pszString; + int nLen; + int nBufSize; + + void UpdateEnd(); + +public: + + OGROCIStringBuf(); + ~OGROCIStringBuf(); + + void MakeRoomFor( int ); + void Append( const char * ); + void Appendf( int nMax, const char *pszFormat, ... ) CPL_PRINT_FUNC_FORMAT (3, 4); + char *StealString(); + + char GetLast(); + char *GetEnd() { UpdateEnd(); return pszString + nLen; } + char *GetString() { return pszString; } + + void Clear(); +}; + +/************************************************************************/ +/* OGROCILayer */ +/************************************************************************/ + +class OGROCIDataSource; + +class OGROCILayer : public OGRLayer +{ + protected: + OGRFeatureDefn *poFeatureDefn; + + int iNextShapeId; + + OGROCIDataSource *poDS; + + char *pszQueryStatement; + + int nResultOffset; + + OGROCIStatement *poStatement; + + int ExecuteQuery( const char * ); + + SDO_GEOMETRY_TYPE *hLastGeom; + SDO_GEOMETRY_ind *hLastGeomInd; + + char *pszGeomName; + int iGeomColumn; + + char *pszFIDName; + int iFIDColumn; + + OGRGeometry *TranslateGeometry(); + OGRGeometry *TranslateGeometryElement( int *piElement, + int nGType, int nDimension, + int nEType, + int nInterpretation, + int nStartOrdinal, + int nOrdCount); + int LoadElementInfo( int iElement, int nElemCount, int nTotalOrdCount, + int *pnEType, int *pnInterpretation, + int *pnStartOrdinal, int *pnElemOrdCount ); + int GetOrdinalPoint( int iOrdinal, int nDimension, + double *pdfX, double *pdfY, + double *pdfZ ); + + public: + OGROCILayer(); + virtual ~OGROCILayer(); + virtual int FindFieldIndex( const char *pszFieldName, int bExactMatch ) { return OGRLayer::FindFieldIndex( pszFieldName, bExactMatch ); } + + virtual void ResetReading(); + virtual OGRFeature *GetNextRawFeature(); + virtual OGRFeature *GetNextFeature(); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); + + virtual const char *GetFIDColumn(); + virtual const char *GetGeometryColumn(); + + int LookupTableSRID(); +}; + +/************************************************************************/ +/* OGROCIWritableLayer */ +/************************************************************************/ + +class OGROCIWritableLayer : public OGROCILayer +{ +protected: + int nDimension; + int nSRID; + + int nOrdinalCount; + int nOrdinalMax; + double *padfOrdinals; + + int nElemInfoCount; + int nElemInfoMax; + int *panElemInfo; + + void PushOrdinal( double ); + void PushElemInfo( int, int, int ); + + OGRErr TranslateToSDOGeometry( OGRGeometry *, + int *pnGType ); + OGRErr TranslateElementGroup( OGRGeometry *poGeometry ); + + int bLaunderColumnNames; + int bPreservePrecision; + + OGRSpatialReference *poSRS; + + char **papszOptions; + + int bTruncationReported; + void ReportTruncation( OGRFieldDefn * ); + + void ParseDIMINFO( const char *, double *, double *, + double * ); + + OGROCIWritableLayer(); + virtual ~OGROCIWritableLayer(); +public: + + virtual OGRSpatialReference *GetSpatialRef() { return poSRS; } + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual int FindFieldIndex( const char *pszFieldName, int bExactMatch ); + + // following methods are not base class overrides + void SetOptions( char ** ); + + void SetDimension( int ); + void SetLaunderFlag( int bFlag ) + { bLaunderColumnNames = bFlag; } + void SetPrecisionFlag( int bFlag ) + { bPreservePrecision = bFlag; } +}; + +/************************************************************************/ +/* OGROCILoaderLayer */ +/************************************************************************/ + +#define LDRM_UNKNOWN 0 +#define LDRM_STREAM 1 +#define LDRM_VARIABLE 2 +#define LDRM_BINARY 3 + +class OGROCILoaderLayer : public OGROCIWritableLayer +{ + OGREnvelope sExtent; + int iNextFIDToWrite; + + char *pszLoaderFilename; + + FILE *fpLoader; + int bHeaderWritten; + + FILE *fpData; + + int nLDRMode; + + void WriteLoaderHeader(); + void FinalizeNewLayer(); + + OGRErr WriteFeatureStreamMode( OGRFeature * ); + OGRErr WriteFeatureVariableMode( OGRFeature * ); + OGRErr WriteFeatureBinaryMode( OGRFeature * ); + + public: + OGROCILoaderLayer( OGROCIDataSource *, + const char * pszName, + const char *pszGeomCol, + int nSRID, + const char *pszLoaderFile ); + ~OGROCILoaderLayer(); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual void SetSpatialFilter( OGRGeometry * ) {} + + virtual OGRErr SetAttributeFilter( const char * ) + { return OGRERR_UNSUPPORTED_OPERATION; } + + virtual OGRFeature *GetNextFeature(); + + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + + virtual OGRSpatialReference *GetSpatialRef() { return poSRS; } + + virtual int TestCapability( const char * ); + +}; + +/************************************************************************/ +/* OGROCITableLayer */ +/************************************************************************/ + +class OGROCITableLayer : public OGROCIWritableLayer +{ + int bUpdateAccess; + int bNewLayer; + OGREnvelope sExtent; + bool bExtentUpdated; + + int iNextFIDToWrite; + int bHaveSpatialIndex; + + OGRFeatureDefn *ReadTableDefinition(const char *); + + void BuildWhere(void); + char *BuildFields(void); + void BuildFullQueryStatement(void); + + char *pszQuery; + char *pszWHERE; + + int bValidTable; + + CPLString osTableName; + CPLString osOwner; + + OCIArray *hOrdVARRAY; + OCIArray *hElemInfoVARRAY; + + void UpdateLayerExtents(); + void CreateSpatialIndex(); + + void TestForSpatialIndex( const char * ); + + OGROCIStatement *poBoundStatement; + + int nWriteCacheMax; + int nWriteCacheUsed; + + SDO_GEOMETRY_TYPE *pasWriteGeoms; + SDO_GEOMETRY_TYPE **papsWriteGeomMap; + SDO_GEOMETRY_ind *pasWriteGeomInd; + SDO_GEOMETRY_ind **papsWriteGeomIndMap; + + void **papWriteFields; + OCIInd **papaeWriteFieldInd; + int *panWriteFIDs; + + int AllocAndBindForWrite(); + OGRErr FlushPendingFeatures(); + + OGRErr UnboundCreateFeature( OGRFeature *poFeature ); + OGRErr BoundCreateFeature( OGRFeature *poFeature ); + + public: + OGROCITableLayer( OGROCIDataSource *, + const char * pszName, OGRwkbGeometryType eGType, + int nSRID, int bUpdate, int bNew ); + ~OGROCITableLayer(); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual void SetSpatialFilter( OGRGeometry * ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual OGRFeature *GetNextFeature(); + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); + + virtual OGRErr SyncToDisk(); + + // following methods are not base class overrides + int IsValid() { return bValidTable; } + + int GetMaxFID(); +}; + +/************************************************************************/ +/* OGROCISelectLayer */ +/************************************************************************/ + +class OGROCISelectLayer : public OGROCILayer +{ + OGRFeatureDefn *ReadTableDefinition( OGROCIStatement * poStatement ); + + public: + OGROCISelectLayer( OGROCIDataSource *, + const char * pszName, + OGROCIStatement *poStatement ); + ~OGROCISelectLayer(); +}; + +/************************************************************************/ +/* OGROCIDataSource */ +/************************************************************************/ + +class OGROCIDataSource : public OGRDataSource +{ + OGROCILayer **papoLayers; + int nLayers; + + char *pszName; + char *pszDBName; + + int bDSUpdate; + + OGROCISession *poSession; + + // We maintain a list of known SRID to reduce the number of trips to + // the database to get SRSes. + int nKnownSRID; + int *panSRID; + OGRSpatialReference **papoSRS; + + public: + OGROCIDataSource(); + ~OGROCIDataSource(); + + OGROCISession *GetSession() { return poSession; } + + int Open( const char *, char** papszOpenOptions, + int bUpdate, int bTestOpen ); + int OpenTable( const char *pszTableName, + int nSRID, int bUpdate, int bTestOpen ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + OGRLayer *GetLayerByName(const char * pszName); + + virtual OGRErr DeleteLayer(int); + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + int TestCapability( const char * ); + + void DeleteLayer( const char * ); + + void TruncateLayer( const char * ); + void ValidateLayer( const char * ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + int FetchSRSId( OGRSpatialReference * poSRS ); + OGRSpatialReference *FetchSRS( int nSRID ); +}; + +/* -------------------------------------------------------------------- */ +/* Helper functions. */ +/* -------------------------------------------------------------------- */ +int +OGROCIStrokeArcToOGRGeometry_Points( double dfStartX, double dfStartY, + double dfAlongX, double dfAlongY, + double dfEndX, double dfEndY, + double dfMaxAngleStepSizeDegrees, + int bForceWholeCircle, + OGRLineString *poLine ); + + +#endif /* ndef _OGR_OCI_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocidatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocidatasource.cpp new file mode 100644 index 000000000..2e4159c8e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocidatasource.cpp @@ -0,0 +1,1034 @@ +/****************************************************************************** + * $Id: ogrocidatasource.cpp 29019 2015-04-25 20:34:19Z rouault $ + * + * Project: Oracle Spatial Driver + * Purpose: Implementation of the OGROCIDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_oci.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrocidatasource.cpp 29019 2015-04-25 20:34:19Z rouault $"); + +static int anEPSGOracleMapping[] = +{ + /* Oracle SRID, EPSG GCS/PCS Code */ + + 8192, 4326, // WGS84 + 8306, 4322, // WGS72 + 8267, 4269, // NAD83 + 8274, 4277, // OSGB 36 + // NAD27 isn't easily mapped since there are many Oracle NAD27 codes. + + 81989, 27700, // UK National Grid + + 0, 0 // end marker +}; + +/************************************************************************/ +/* OGROCIDataSource() */ +/************************************************************************/ + +OGROCIDataSource::OGROCIDataSource() + +{ + pszName = NULL; + pszDBName = NULL; + papoLayers = NULL; + nLayers = 0; + poSession = NULL; + papoSRS = NULL; + panSRID = NULL; + nKnownSRID = 0; +} + +/************************************************************************/ +/* ~OGROCIDataSource() */ +/************************************************************************/ + +OGROCIDataSource::~OGROCIDataSource() + +{ + int i; + + CPLFree( pszName ); + CPLFree( pszDBName ); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + for( i = 0; i < nKnownSRID; i++ ) + { + papoSRS[i]->Release(); + } + CPLFree( papoSRS ); + CPLFree( panSRID ); + + if( poSession != NULL ) + delete poSession; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGROCIDataSource::Open( const char * pszNewName, + char** papszOpenOptions, + int bUpdate, + int bTestOpen ) + +{ + CPLAssert( nLayers == 0 && poSession == NULL ); + +/* -------------------------------------------------------------------- */ +/* Verify Oracle prefix. */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(pszNewName,"OCI:",3) ) + { + if( !bTestOpen ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s does not conform to Oracle OCI driver naming convention," + " OCI:*\n", pszNewName ); + } + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try to parse out name, password and database name. */ +/* -------------------------------------------------------------------- */ + char *pszUserid; + const char *pszPassword = ""; + const char *pszDatabase = ""; + char **papszTableList = NULL; + int i; + + if( pszNewName[4] == '\0' ) + { + pszUserid = CPLStrdup(CSLFetchNameValueDef(papszOpenOptions, "USER", "")); + pszPassword = CSLFetchNameValueDef(papszOpenOptions, "PASSWORD", ""); + pszDatabase = CSLFetchNameValueDef(papszOpenOptions, "DBNAME", ""); + const char* pszTables = CSLFetchNameValue(papszOpenOptions, "TABLES"); + if( pszTables ) + papszTableList = CSLTokenizeStringComplex(pszTables, ",", TRUE, FALSE ); + } + else + { + pszUserid = CPLStrdup( pszNewName + 4 ); + + // Is there a table list? + for( i = strlen(pszUserid)-1; i > 1; i-- ) + { + if( pszUserid[i] == ':' ) + { + papszTableList = CSLTokenizeStringComplex( pszUserid+i+1, ",", + TRUE, FALSE ); + pszUserid[i] = '\0'; + break; + } + + if( pszUserid[i] == '/' || pszUserid[i] == '@' ) + break; + } + + for( i = 0; + pszUserid[i] != '\0' && pszUserid[i] != '/' && pszUserid[i] != '@'; + i++ ) {} + + if( pszUserid[i] == '/' ) + { + pszUserid[i++] = '\0'; + pszPassword = pszUserid + i; + for( ; pszUserid[i] != '\0' && pszUserid[i] != '@'; i++ ) {} + } + + if( pszUserid[i] == '@' ) + { + pszUserid[i++] = '\0'; + pszDatabase = pszUserid + i; + } + } + +/* -------------------------------------------------------------------- */ +/* Try to establish connection. */ +/* -------------------------------------------------------------------- */ + CPLDebug( "OCI", "Userid=%s, Password=%s, Database=%s", + pszUserid, pszPassword, pszDatabase ); + + if( EQUAL(pszDatabase, "") && + EQUAL(pszPassword, "") && + EQUAL(pszUserid, "") ) + { + /* Use username/password OS Authentication and ORACLE_SID database */ + + poSession = OGRGetOCISession( "/", "", "" ); + } + else + { + poSession = OGRGetOCISession( pszUserid, pszPassword, pszDatabase ); + } + + if( poSession == NULL ) + { + CPLFree(pszUserid); + CSLDestroy(papszTableList); + return FALSE; + } + + pszName = CPLStrdup( pszNewName ); + + bDSUpdate = bUpdate; + +/* -------------------------------------------------------------------- */ +/* If no list of target tables was provided, collect a list of */ +/* spatial tables now. */ +/* -------------------------------------------------------------------- */ + if( papszTableList == NULL ) + { + OGROCIStatement oGetTables( poSession ); + + if( oGetTables.Execute( + "SELECT TABLE_NAME, OWNER FROM ALL_SDO_GEOM_METADATA" ) + == CE_None ) + { + char **papszRow; + + while( (papszRow = oGetTables.SimpleFetchRow()) != NULL ) + { + char szFullTableName[100]; + + if( EQUAL(papszRow[1],pszUserid) ) + strcpy( szFullTableName, papszRow[0] ); + else + sprintf( szFullTableName, "%s.%s", + papszRow[1], papszRow[0] ); + + if( CSLFindString( papszTableList, szFullTableName ) == -1 ) + papszTableList = CSLAddString( papszTableList, + szFullTableName ); + } + } + } + CPLFree( pszUserid ); + +/* -------------------------------------------------------------------- */ +/* Open all the selected tables or views. */ +/* -------------------------------------------------------------------- */ + for( i = 0; papszTableList != NULL && papszTableList[i] != NULL; i++ ) + { + OpenTable( papszTableList[i], -1, bUpdate, FALSE ); + } + + CSLDestroy( papszTableList ); + + return TRUE; +} + +/************************************************************************/ +/* OpenTable() */ +/************************************************************************/ + +int OGROCIDataSource::OpenTable( const char *pszNewName, + int nSRID, int bUpdate, int bTestOpen ) + +{ +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGROCITableLayer *poLayer; + + poLayer = new OGROCITableLayer( this, pszNewName, wkbUnknown, nSRID, + bUpdate, FALSE ); + + if( !poLayer->IsValid() ) + { + delete poLayer; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGROCILayer **) + CPLRealloc( papoLayers, sizeof(OGROCILayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + + return TRUE; +} + +/************************************************************************/ +/* ValidateLayer() */ +/************************************************************************/ + +void OGROCIDataSource::ValidateLayer( const char *pszLayerName ) + +{ + int iLayer; + +/* -------------------------------------------------------------------- */ +/* Try to find layer. */ +/* -------------------------------------------------------------------- */ + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName,papoLayers[iLayer]->GetLayerDefn()->GetName()) ) + break; + } + + if( iLayer == nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "ValidateLayer(): %s is not a recognised layer.", + pszLayerName ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Verify we have an FID and geometry column for this table. */ +/* -------------------------------------------------------------------- */ + OGROCITableLayer *poLayer = (OGROCITableLayer *) papoLayers[iLayer]; + + if( strlen(poLayer->GetFIDColumn()) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "ValidateLayer(): %s lacks a fid column.", + pszLayerName ); + + return; + } + +/* -------------------------------------------------------------------- */ +/* Prepare and execute the geometry validation. */ +/* -------------------------------------------------------------------- */ + + if( !strlen(poLayer->GetGeometryColumn()) == 0 ) + { + OGROCIStringBuf oValidateCmd; + OGROCIStatement oValidateStmt( GetSession() ); + + oValidateCmd.Append( "SELECT c." ); + oValidateCmd.Append( poLayer->GetFIDColumn() ); + oValidateCmd.Append( ", SDO_GEOM.VALIDATE_GEOMETRY(c." ); + oValidateCmd.Append( poLayer->GetGeometryColumn() ); + oValidateCmd.Append( ", m.diminfo) from " ); + oValidateCmd.Append( poLayer->GetLayerDefn()->GetName() ); + oValidateCmd.Append( " c, user_sdo_geom_metadata m WHERE m.table_name= '"); + oValidateCmd.Append( poLayer->GetLayerDefn()->GetName() ); + oValidateCmd.Append( "' AND m.column_name = '" ); + oValidateCmd.Append( poLayer->GetGeometryColumn() ); + oValidateCmd.Append( "' AND SDO_GEOM.VALIDATE_GEOMETRY(c." ); + oValidateCmd.Append( poLayer->GetGeometryColumn() ); + oValidateCmd.Append( ", m.diminfo ) <> 'TRUE'" ); + + oValidateStmt.Execute( oValidateCmd.GetString() ); + +/* -------------------------------------------------------------------- */ +/* Report results to debug stream. */ +/* -------------------------------------------------------------------- */ + char **papszRow; + + while( (papszRow = oValidateStmt.SimpleFetchRow()) != NULL ) + { + const char *pszReason = papszRow[1]; + + if( EQUAL(pszReason,"13011") ) + pszReason = "13011: value is out of range"; + else if( EQUAL(pszReason,"13050") ) + pszReason = "13050: unable to construct spatial object"; + else if( EQUAL(pszReason,"13349") ) + pszReason = "13349: polygon boundary crosses itself"; + + CPLDebug( "OCI", "Validation failure for FID=%s: %s", + papszRow[0], pszReason ); + } + } +} + +/************************************************************************/ +/* DeleteLayer(int) */ +/************************************************************************/ + +OGRErr OGROCIDataSource::DeleteLayer( int iLayer ) + +{ +/* -------------------------------------------------------------------- */ +/* Blow away our OGR structures related to the layer. This is */ +/* pretty dangerous if anything has a reference to this layer! */ +/* -------------------------------------------------------------------- */ + CPLString osLayerName = + papoLayers[iLayer]->GetLayerDefn()->GetName(); + + CPLDebug( "OCI", "DeleteLayer(%s)", osLayerName.c_str() ); + + delete papoLayers[iLayer]; + memmove( papoLayers + iLayer, papoLayers + iLayer + 1, + sizeof(void *) * (nLayers - iLayer - 1) ); + nLayers--; + +/* -------------------------------------------------------------------- */ +/* Remove from the database. */ +/* -------------------------------------------------------------------- */ + OGROCIStatement oCommand( poSession ); + CPLString osCommand; + int nFailures = 0; + + osCommand.Printf( "DROP TABLE \"%s\"", osLayerName.c_str() ); + if( oCommand.Execute( osCommand ) != CE_None ) + nFailures++; + + osCommand.Printf( + "DELETE FROM USER_SDO_GEOM_METADATA WHERE TABLE_NAME = UPPER('%s')", + osLayerName.c_str() ); + + if( oCommand.Execute( osCommand ) != CE_None ) + nFailures++; + + if( nFailures == 0 ) + return OGRERR_NONE; + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* DeleteLayer(const char *) */ +/************************************************************************/ + +void OGROCIDataSource::DeleteLayer( const char *pszLayerName ) + +{ + int iLayer; + +/* -------------------------------------------------------------------- */ +/* Try to find layer. */ +/* -------------------------------------------------------------------- */ + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName,papoLayers[iLayer]->GetLayerDefn()->GetName()) ) + { + break; + } + } + + if( iLayer == nLayers ) + { + CPLDebug( "OCI", "DeleteLayer: %s not found in layer list." \ + " Layer *not* deleted.", pszLayerName ); + return; + } + + DeleteLayer( iLayer ); +} + +/************************************************************************/ +/* TruncateLayer() */ +/************************************************************************/ + +void OGROCIDataSource::TruncateLayer( const char *pszLayerName ) + +{ + +/* -------------------------------------------------------------------- */ +/* Set OGR Debug statement explaining what is happening */ +/* -------------------------------------------------------------------- */ + CPLDebug( "OCI", "Truncate TABLE %s", pszLayerName ); + +/* -------------------------------------------------------------------- */ +/* Truncate the layer in the database. */ +/* -------------------------------------------------------------------- */ + OGROCIStatement oCommand( poSession ); + CPLString osCommand; + + osCommand.Printf( "TRUNCATE TABLE \"%s\"", pszLayerName ); + oCommand.Execute( osCommand ); +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGROCIDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ) + +{ + char szCommand[1024]; + char *pszSafeLayerName = CPLStrdup(pszLayerName); + + poSession->CleanName( pszSafeLayerName ); + CPLDebug( "OCI", "In Create Layer ..." ); + + +/* -------------------------------------------------------------------- */ +/* Do we already have this layer? If so, should we blow it */ +/* away? */ +/* -------------------------------------------------------------------- */ + int iLayer; + + if( CSLFetchBoolean( papszOptions, "TRUNCATE", FALSE ) ) + { + CPLDebug( "OCI", "Calling TruncateLayer for %s", pszLayerName ); + TruncateLayer( pszSafeLayerName ); + } + else + { + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszSafeLayerName, + papoLayers[iLayer]->GetLayerDefn()->GetName()) ) + { + if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL + && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") ) + { + DeleteLayer( pszSafeLayerName ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + pszSafeLayerName ); + CPLFree( pszSafeLayerName ); + return NULL; + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Try to get the SRS Id of this spatial reference system, */ +/* adding tot the srs table if needed. */ +/* -------------------------------------------------------------------- */ + char szSRSId[100]; + + if( CSLFetchNameValue( papszOptions, "SRID" ) != NULL ) + strcpy( szSRSId, CSLFetchNameValue( papszOptions, "SRID" ) ); + else if( poSRS != NULL ) + sprintf( szSRSId, "%d", FetchSRSId( poSRS ) ); + else + strcpy( szSRSId, "NULL" ); + +/* -------------------------------------------------------------------- */ +/* Determine name of geometry column to use. */ +/* -------------------------------------------------------------------- */ + const char *pszGeometryName = + CSLFetchNameValue( papszOptions, "GEOMETRY_NAME" ); + if( pszGeometryName == NULL ) + pszGeometryName = "ORA_GEOMETRY"; + int bGeomNullable = CSLFetchBoolean(papszOptions, "GEOMETRY_NULLABLE", TRUE); + +/* -------------------------------------------------------------------- */ +/* Create a basic table with the FID. Also include the */ +/* geometry if this is not a PostGIS enabled table. */ +/* -------------------------------------------------------------------- */ + const char *pszExpectedFIDName = + CPLGetConfigOption( "OCI_FID", "OGR_FID" ); + + OGROCIStatement oStatement( poSession ); + +/* -------------------------------------------------------------------- */ +/* If geometry type is wkbNone, do not create a geoemtry column */ +/* -------------------------------------------------------------------- */ + + if ( CSLFetchNameValue( papszOptions, "TRUNCATE" ) == NULL ) + { + if (eType == wkbNone) + { + sprintf( szCommand, + "CREATE TABLE \"%s\" ( " + "%s INTEGER PRIMARY KEY)", + pszSafeLayerName, pszExpectedFIDName); + } + else + { + sprintf( szCommand, + "CREATE TABLE \"%s\" ( " + "%s INTEGER PRIMARY KEY, " + "%s %s%s )", + pszSafeLayerName, pszExpectedFIDName, + pszGeometryName, SDO_GEOMETRY, + (!bGeomNullable) ? " NOT NULL":""); + } + + if( oStatement.Execute( szCommand ) != CE_None ) + { + CPLFree( pszSafeLayerName ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + const char *pszLoaderFile = CSLFetchNameValue(papszOptions,"LOADER_FILE"); + OGROCIWritableLayer *poLayer; + + if( pszLoaderFile == NULL ) + poLayer = new OGROCITableLayer( this, pszSafeLayerName, eType, + EQUAL(szSRSId,"NULL") ? -1 : atoi(szSRSId), + TRUE, TRUE ); + else + poLayer = + new OGROCILoaderLayer( this, pszSafeLayerName, + pszGeometryName, + EQUAL(szSRSId,"NULL") ? -1 : atoi(szSRSId), + pszLoaderFile ); + +/* -------------------------------------------------------------------- */ +/* Set various options on the layer. */ +/* -------------------------------------------------------------------- */ + poLayer->SetLaunderFlag( CSLFetchBoolean(papszOptions,"LAUNDER",FALSE) ); + poLayer->SetPrecisionFlag( CSLFetchBoolean(papszOptions,"PRECISION",TRUE)); + + if( CSLFetchNameValue(papszOptions,"DIM") != NULL ) + poLayer->SetDimension( atoi(CSLFetchNameValue(papszOptions,"DIM")) ); + + poLayer->SetOptions( papszOptions ); + if( eType != wkbNone && !bGeomNullable ) + poLayer->GetLayerDefn()->GetGeomFieldDefn(0)->SetNullable(FALSE); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGROCILayer **) + CPLRealloc( papoLayers, sizeof(OGROCILayer *) * (nLayers+1) ); + + papoLayers[nLayers++] = poLayer; + + CPLFree( pszSafeLayerName ); + + return poLayer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGROCIDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) && bDSUpdate ) + return TRUE; + else if( EQUAL(pszCap,ODsCDeleteLayer) && bDSUpdate ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGROCIDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + + + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGROCIDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Ensure any pending stuff is flushed to the database. */ +/* -------------------------------------------------------------------- */ + FlushCache(); + + CPLDebug( "OCI", "ExecuteSQL(%s)", pszSQLCommand ); + +/* -------------------------------------------------------------------- */ +/* Special case DELLAYER: command. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand,"DELLAYER:",9) ) + { + const char *pszLayerName = pszSQLCommand + 9; + + while( *pszLayerName == ' ' ) + pszLayerName++; + + DeleteLayer( pszLayerName ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Special case VALLAYER: command. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand,"VALLAYER:",9) ) + { + const char *pszLayerName = pszSQLCommand + 9; + + while( *pszLayerName == ' ' ) + pszLayerName++; + + ValidateLayer( pszLayerName ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Just execute simple command. */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(pszSQLCommand,"SELECT",6) ) + { + OGROCIStatement oCommand( poSession ); + + oCommand.Execute( pszSQLCommand, OCI_COMMIT_ON_SUCCESS ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise instantiate a layer. */ +/* -------------------------------------------------------------------- */ + else + { + OGROCIStatement oCommand( poSession ); + + if( oCommand.Execute( pszSQLCommand, OCI_DESCRIBE_ONLY ) == CE_None ) + return new OGROCISelectLayer( this, pszSQLCommand, &oCommand ); + else + return NULL; + } +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGROCIDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} + +/************************************************************************/ +/* FetchSRS() */ +/* */ +/* Return a SRS corresponding to a particular id. Note that */ +/* reference counting should be honoured on the returned */ +/* OGRSpatialReference, as handles may be cached. */ +/************************************************************************/ + +OGRSpatialReference *OGROCIDataSource::FetchSRS( int nId ) + +{ + if( nId < 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* First, we look through our SRID cache, is it there? */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nKnownSRID; i++ ) + { + if( panSRID[i] == nId ) + return papoSRS[i]; + } + +/* -------------------------------------------------------------------- */ +/* Try looking up in MDSYS.CS_SRS table. */ +/* -------------------------------------------------------------------- */ + OGROCIStatement oStatement( GetSession() ); + char szSelect[200], **papszResult; + + sprintf( szSelect, + "SELECT WKTEXT, AUTH_SRID, AUTH_NAME FROM MDSYS.CS_SRS " + "WHERE SRID = %d AND WKTEXT IS NOT NULL", nId ); + + if( oStatement.Execute( szSelect ) != CE_None ) + return NULL; + + papszResult = oStatement.SimpleFetchRow(); + if( CSLCount(papszResult) < 1 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Turn into a spatial reference. */ +/* -------------------------------------------------------------------- */ + char *pszWKT = papszResult[0]; + OGRSpatialReference *poSRS = NULL; + + poSRS = new OGRSpatialReference(); + if( poSRS->importFromWkt( &pszWKT ) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + +/* -------------------------------------------------------------------- */ +/* If we have a corresponding EPSG code for this SRID, use that */ +/* authority. */ +/* -------------------------------------------------------------------- */ + int bGotEPSGMapping = FALSE; + for( i = 0; anEPSGOracleMapping[i] != 0; i += 2 ) + { + if( anEPSGOracleMapping[i] == nId ) + { + poSRS->SetAuthority( poSRS->GetRoot()->GetValue(), "EPSG", + anEPSGOracleMapping[i+1] ); + bGotEPSGMapping = TRUE; + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Insert authority information, if it is available. */ +/* -------------------------------------------------------------------- */ + if( papszResult[1] != NULL && atoi(papszResult[1]) != 0 + && papszResult[2] != NULL && strlen(papszResult[1]) != 0 + && poSRS->GetRoot() != NULL + && !bGotEPSGMapping ) + { + poSRS->SetAuthority( poSRS->GetRoot()->GetValue(), + papszResult[2], atoi(papszResult[1]) ); + } + +/* -------------------------------------------------------------------- */ +/* Add to the cache. */ +/* -------------------------------------------------------------------- */ + panSRID = (int *) CPLRealloc(panSRID,sizeof(int) * (nKnownSRID+1) ); + papoSRS = (OGRSpatialReference **) + CPLRealloc(papoSRS, sizeof(void*) * (nKnownSRID + 1) ); + panSRID[nKnownSRID] = nId; + papoSRS[nKnownSRID] = poSRS; + + nKnownSRID++; + + return poSRS; +} + +/************************************************************************/ +/* FetchSRSId() */ +/* */ +/* Fetch the id corresponding to an SRS, and if not found, add */ +/* it to the table. */ +/************************************************************************/ + +int OGROCIDataSource::FetchSRSId( OGRSpatialReference * poSRS ) + +{ + char *pszWKT = NULL; + int nSRSId; + + if( poSRS == NULL ) + return -1; + + if( !poSRS->IsProjected() && !poSRS->IsGeographic() ) + return -1; + +/* ==================================================================== */ +/* The first strategy is to see if we can identify it by */ +/* authority information within the SRS. Either using ORACLE */ +/* authority values directly, or check if there is a known */ +/* translation for an EPSG authority code. */ +/* ==================================================================== */ + const char *pszAuthName = NULL, *pszAuthCode = NULL; + + if( poSRS->IsGeographic() ) + { + pszAuthName = poSRS->GetAuthorityName( "GEOGCS" ); + pszAuthCode = poSRS->GetAuthorityCode( "GEOGCS" ); + } + else if( poSRS->IsProjected() ) + { + pszAuthName = poSRS->GetAuthorityName( "PROJCS" ); + pszAuthCode = poSRS->GetAuthorityCode( "PROJCS" ); + } + + if( pszAuthName != NULL && pszAuthCode != NULL ) + { + if( EQUAL(pszAuthName,"Oracle") + && atoi(pszAuthCode) != 0 ) + return atoi(pszAuthCode); + + if( EQUAL(pszAuthName,"EPSG") ) + { + int i, nEPSGCode = atoi(pszAuthCode); + + for( i = 0; anEPSGOracleMapping[i] != 0; i += 2 ) + { + if( nEPSGCode == anEPSGOracleMapping[i+1] ) + return anEPSGOracleMapping[i]; + } + } + } + +/* ==================================================================== */ +/* We need to lookup the SRS in the existing Oracle CS_SRS */ +/* table. */ +/* ==================================================================== */ + +/* -------------------------------------------------------------------- */ +/* Convert SRS into old style format (SF-SQL 1.0). */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference *poSRS2 = poSRS->Clone(); + + poSRS2->StripCTParms(); + +/* -------------------------------------------------------------------- */ +/* Convert any degree type unit names to "Decimal Degree". */ +/* -------------------------------------------------------------------- */ + double dfAngularUnits = poSRS2->GetAngularUnits( NULL ); + if( fabs(dfAngularUnits - 0.0174532925199433) < 0.0000000000000010 ) + poSRS2->SetAngularUnits( "Decimal Degree", 0.0174532925199433 ); + +/* -------------------------------------------------------------------- */ +/* Translate SRS to WKT. */ +/* -------------------------------------------------------------------- */ + if( poSRS2->exportToWkt( &pszWKT ) != OGRERR_NONE ) + { + delete poSRS2; + return -1; + } + + delete poSRS2; + +/* -------------------------------------------------------------------- */ +/* Try to find in the existing table. */ +/* -------------------------------------------------------------------- */ + OGROCIStringBuf oCmdText; + OGROCIStatement oCmdStatement( GetSession() ); + char **papszResult = NULL; + + oCmdText.Append( "SELECT SRID FROM MDSYS.CS_SRS WHERE WKTEXT = '" ); + oCmdText.Append( pszWKT ); + oCmdText.Append( "'" ); + + if( oCmdStatement.Execute( oCmdText.GetString() ) == CE_None ) + papszResult = oCmdStatement.SimpleFetchRow() ; + +/* -------------------------------------------------------------------- */ +/* We got it! Return it. */ +/* -------------------------------------------------------------------- */ + if( CSLCount(papszResult) == 1 ) + { + CPLFree( pszWKT ); + return atoi( papszResult[0] ); + } + +/* ==================================================================== */ +/* We didn't find it, so we need to define it as a new SRID at */ +/* the end of the list of known values. */ +/* ==================================================================== */ + +/* -------------------------------------------------------------------- */ +/* Get the current maximum srid in the srs table. */ +/* -------------------------------------------------------------------- */ + if( oCmdStatement.Execute("SELECT MAX(SRID) FROM MDSYS.CS_SRS") == CE_None ) + papszResult = oCmdStatement.SimpleFetchRow(); + else + papszResult = NULL; + + if( CSLCount(papszResult) == 1 ) + nSRSId = atoi(papszResult[0]) + 1; + else + nSRSId = 1; + +/* -------------------------------------------------------------------- */ +/* Try adding the SRS to the SRS table. */ +/* -------------------------------------------------------------------- */ + oCmdText.Clear(); + oCmdText.Append( "INSERT INTO MDSYS.CS_SRS (SRID, WKTEXT, CS_NAME) " ); + oCmdText.Appendf( 100, " VALUES (%d,'", nSRSId ); + oCmdText.Append( pszWKT ); + oCmdText.Append( "', '" ); + oCmdText.Append( poSRS->GetRoot()->GetChild(0)->GetValue() ); + oCmdText.Append( "' )" ); + + CPLFree( pszWKT ); + + if( oCmdStatement.Execute( oCmdText.GetString() ) != CE_None ) + return -1; + else + return nSRSId; +} + + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer *OGROCIDataSource::GetLayerByName( const char *pszName ) + +{ + OGROCILayer *poLayer; + int i, count; + + if ( !pszName ) + return NULL; + + count = GetLayerCount(); + + /* first a case sensitive check */ + for( i = 0; i < count; i++ ) + { + poLayer = papoLayers[i]; + + if( strcmp( pszName, poLayer->GetName() ) == 0 ) + { + return poLayer; + } + } + + char *pszSafeLayerName = CPLStrdup( pszName ); + poSession->CleanName( pszSafeLayerName ); + + /* then case insensitive and laundered */ + for( i = 0; i < count; i++ ) + { + poLayer = papoLayers[i]; + + if( EQUAL( pszSafeLayerName, poLayer->GetName() ) ) + { + break; + } + } + + CPLFree( pszSafeLayerName ); + + return i < count ? poLayer : NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocidriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocidriver.cpp new file mode 100644 index 000000000..cde888277 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocidriver.cpp @@ -0,0 +1,160 @@ +/****************************************************************************** + * $Id: ogrocidriver.cpp 29019 2015-04-25 20:34:19Z rouault $ + * + * Project: Oracle Spatial Driver + * Purpose: Implementation of the OGROCIDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_oci.h" + +CPL_CVSID("$Id: ogrocidriver.cpp 29019 2015-04-25 20:34:19Z rouault $"); + + +/************************************************************************/ +/* OGROCIDriverIdentify() */ +/************************************************************************/ + +static int OGROCIDriverIdentify( GDALOpenInfo* poOpenInfo ) +{ + return EQUALN(poOpenInfo->pszFilename,"OCI:",4); +} + +/************************************************************************/ +/* OGROCIDriverOpen() */ +/************************************************************************/ + +static GDALDataset *OGROCIDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( !OGROCIDriverIdentify(poOpenInfo) ) + return NULL; + + OGROCIDataSource *poDS; + + poDS = new OGROCIDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, poOpenInfo->papszOpenOptions, + poOpenInfo->eAccess == GA_Update, TRUE ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* OGROCIDriverCreate() */ +/************************************************************************/ + +static GDALDataset *OGROCIDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + CPL_UNUSED char **papszOptions ) + +{ + OGROCIDataSource *poDS; + + poDS = new OGROCIDataSource(); + + + if( !poDS->Open( pszName, NULL, TRUE, TRUE ) ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "Oracle driver doesn't currently support database creation.\n" + "Please create database with Oracle tools before loading tables." ); + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGROCI() */ +/************************************************************************/ + +void RegisterOGROCI() + +{ + if (! GDAL_CHECK_VERSION("OCI driver")) + return; + + if( GDALGetDriverByName( "OCI" ) == NULL ) + { + GDALDriver* poDriver = new GDALDriver(); + + poDriver->SetDescription( "OCI" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Oracle Spatial" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_oci.html" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_CONNECTION_PREFIX, "OCI:" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, + "" + " "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_DEFAULT_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES" ); + + poDriver->pfnOpen = OGROCIDriverOpen; + poDriver->pfnIdentify = OGROCIDriverIdentify; + poDriver->pfnCreate = OGROCIDriverCreate; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocilayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocilayer.cpp new file mode 100644 index 000000000..0cfc2c89d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocilayer.cpp @@ -0,0 +1,970 @@ +/****************************************************************************** + * $Id: ogrocilayer.cpp 27855 2014-10-13 14:50:21Z rouault $ + * + * Project: Oracle Spatial Driver + * Purpose: Implementation of the OGROCILayer class. This is layer semantics + * shared between table accessors and ExecuteSQL() result + * pseudo-layers. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_oci.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrocilayer.cpp 27855 2014-10-13 14:50:21Z rouault $"); + +/************************************************************************/ +/* OGROCILayer() */ +/************************************************************************/ + +OGROCILayer::OGROCILayer() + +{ + poDS = NULL; + poStatement = NULL; + + pszQueryStatement = NULL; + pszGeomName = NULL; + iGeomColumn = -1; + pszFIDName = NULL; + iFIDColumn = -1; + + hLastGeom = NULL; + hLastGeomInd = NULL; + + iNextShapeId = 0; +} + +/************************************************************************/ +/* ~OGROCILayer() */ +/************************************************************************/ + +OGROCILayer::~OGROCILayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "OCI", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + ResetReading(); + + CPLFree( pszGeomName ); + pszGeomName = NULL; + + CPLFree( pszFIDName ); + pszFIDName = NULL; + + CPLFree( pszQueryStatement ); + pszQueryStatement = NULL; + + if( poFeatureDefn != NULL ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGROCILayer::ResetReading() + +{ + if( poStatement != NULL ) + delete poStatement; + poStatement = NULL; + + iNextShapeId = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/* */ +/* By default we implement the full spatial and attribute query */ +/* semantics manually here. The table query class will */ +/* override this method and implement these inline, but the */ +/* simple SELECT statement evaluator (OGROCISelectLayer) will */ +/* depend us this code implementing additional spatial or */ +/* attribute query semantics. */ +/************************************************************************/ + +OGRFeature *OGROCILayer::GetNextFeature() + +{ + while( TRUE ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGROCILayer::GetNextRawFeature() + +{ +/* -------------------------------------------------------------------- */ +/* Do we need to establish an initial query? */ +/* -------------------------------------------------------------------- */ + if( iNextShapeId == 0 && poStatement == NULL ) + { + if( !ExecuteQuery(pszQueryStatement) ) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Have we run out of query results, such that we have no */ +/* statement left? */ +/* -------------------------------------------------------------------- */ + if( poStatement == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Are we in some sort of error condition? */ +/* -------------------------------------------------------------------- */ + hLastGeom = NULL; + + char **papszResult = poStatement->SimpleFetchRow(); + + if( papszResult == NULL ) + { + iNextShapeId = MAX(1,iNextShapeId); + delete poStatement; + poStatement = NULL; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a feature from the current result. */ +/* -------------------------------------------------------------------- */ + int iField; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + poFeature->SetFID( iNextShapeId ); + iNextShapeId++; + m_nFeaturesRead++; + + if( iFIDColumn != -1 && papszResult[iFIDColumn] != NULL ) + poFeature->SetFID( atoi(papszResult[iFIDColumn]) ); + + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + if( papszResult[iField] != NULL ) + poFeature->SetField( iField, papszResult[iField] ); + } + +/* -------------------------------------------------------------------- */ +/* Translate geometry if we have it. */ +/* -------------------------------------------------------------------- */ + if( iGeomColumn != -1 ) + { + poFeature->SetGeometryDirectly( TranslateGeometry() ); + + OGROCISession *poSession = poDS->GetSession(); + + if( poFeature->GetGeometryRef() != NULL && hLastGeom != NULL ) + poSession->Failed( + OCIObjectFree(poSession->hEnv, poSession->hError, + (dvoid *) hLastGeom, + (ub2)OCI_OBJECTFREE_FORCE) ); + + hLastGeom = NULL; + hLastGeomInd = NULL; + } + + nResultOffset++; + + return poFeature; +} + +/************************************************************************/ +/* ExecuteQuery() */ +/* */ +/* This is invoke when the first request for a feature is */ +/* made. It executes the query, and binds columns as needed. */ +/* The OGROCIStatement is used for most of the work. */ +/************************************************************************/ + +int OGROCILayer::ExecuteQuery( const char *pszReqQuery ) + +{ + OGROCISession *poSession = poDS->GetSession(); + + CPLAssert( pszReqQuery != NULL ); + CPLAssert( poStatement == NULL ); + +/* -------------------------------------------------------------------- */ +/* Execute the query. */ +/* -------------------------------------------------------------------- */ + poStatement = new OGROCIStatement( poSession ); + if( poStatement->Execute( pszReqQuery ) != CE_None ) + { + delete poStatement; + poStatement = NULL; + return FALSE; + } + nResultOffset = 0; + +/* -------------------------------------------------------------------- */ +/* Do additional work binding the geometry column. */ +/* -------------------------------------------------------------------- */ + if( iGeomColumn != -1 ) + { + OCIDefine *hGDefine = NULL; + + if( poSession->Failed( + OCIDefineByPos(poStatement->GetStatement(), &hGDefine, + poSession->hError, + (ub4) iGeomColumn+1, (dvoid *)0, (sb4)0, SQLT_NTY, + (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)OCI_DEFAULT), + "OCIDefineByPos(geometry)") ) + return FALSE; + + if( poSession->Failed( + OCIDefineObject(hGDefine, poSession->hError, + poSession->hGeometryTDO, + (dvoid **) &hLastGeom, (ub4 *)0, + (dvoid **) &hLastGeomInd, (ub4 *)0 ), + "OCIDefineObject") ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* TranslateGeometry() */ +/************************************************************************/ + +OGRGeometry *OGROCILayer::TranslateGeometry() + +{ + OGROCISession *poSession = poDS->GetSession(); + +/* -------------------------------------------------------------------- */ +/* Is the geometry NULL? */ +/* -------------------------------------------------------------------- */ + if( hLastGeom == NULL || hLastGeomInd == NULL + || hLastGeomInd->_atomic == OCI_IND_NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get the size of the sdo_elem_info and sdo_ordinates arrays. */ +/* -------------------------------------------------------------------- */ + int nElemCount, nOrdCount; + + if( poSession->Failed( + OCICollSize( poSession->hEnv, poSession->hError, + (OCIColl *)(hLastGeom->sdo_elem_info), &nElemCount), + "OCICollSize(sdo_elem_info)" ) ) + return NULL; + + if( poSession->Failed( + OCICollSize( poSession->hEnv, poSession->hError, + (OCIColl *)(hLastGeom->sdo_ordinates), &nOrdCount), + "OCICollSize(sdo_ordinates)" ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get the GType. */ +/* -------------------------------------------------------------------- */ + int nGType; + + if( poSession->Failed( + OCINumberToInt(poSession->hError, &(hLastGeom->sdo_gtype), + (uword)sizeof(int), OCI_NUMBER_SIGNED, + (dvoid *)&nGType), + "OCINumberToInt(GType)" ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Establish the dimension. */ +/* -------------------------------------------------------------------- */ + int nDimension = MAX(2,(nGType / 1000)); + +/* -------------------------------------------------------------------- */ +/* Handle point data directly from built-in point info. */ +/* -------------------------------------------------------------------- */ + if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_POINT) + && hLastGeomInd->sdo_point._atomic == OCI_IND_NOTNULL + && hLastGeomInd->sdo_point.x == OCI_IND_NOTNULL + && hLastGeomInd->sdo_point.y == OCI_IND_NOTNULL ) + { + double dfX, dfY, dfZ = 0.0; + + OCINumberToReal(poSession->hError, &(hLastGeom->sdo_point.x), + (uword)sizeof(double), (dvoid *)&dfX); + OCINumberToReal(poSession->hError, &(hLastGeom->sdo_point.y), + (uword)sizeof(double), (dvoid *)&dfY); + if( hLastGeomInd->sdo_point.z == OCI_IND_NOTNULL ) + OCINumberToReal(poSession->hError, &(hLastGeom->sdo_point.z), + (uword)sizeof(double), (dvoid *)&dfZ); + + if( nDimension == 3 ) + return new OGRPoint( dfX, dfY, dfZ ); + else + return new OGRPoint( dfX, dfY ); + } + +/* -------------------------------------------------------------------- */ +/* If this is a sort of container geometry, create the */ +/* container now. */ +/* -------------------------------------------------------------------- */ + OGRGeometryCollection *poCollection = NULL; + OGRPolygon *poPolygon = NULL; + OGRGeometry *poParent = NULL; + + if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_POLYGON) ) + poParent = poPolygon = new OGRPolygon(); + else if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_COLLECTION) ) + poParent = poCollection = new OGRGeometryCollection(); + else if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_MULTIPOINT) ) + poParent = poCollection = new OGRMultiPoint(); + else if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_MULTILINESTRING) ) + poParent = poCollection = new OGRMultiLineString(); + else if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_MULTIPOLYGON) ) + poParent = poCollection = new OGRMultiPolygon(); + +/* ==================================================================== */ +/* Loop over the component elements. */ +/* ==================================================================== */ + for( int iElement = 0; iElement < nElemCount; iElement += 3 ) + { + int nInterpretation, nEType; + int nStartOrdinal, nElemOrdCount; + + LoadElementInfo( iElement, nElemCount, nOrdCount, + &nEType, &nInterpretation, + &nStartOrdinal, &nElemOrdCount ); + +/* -------------------------------------------------------------------- */ +/* Translate this element. */ +/* -------------------------------------------------------------------- */ + OGRGeometry *poGeom; + + poGeom = TranslateGeometryElement( &iElement, nGType, nDimension, + nEType, nInterpretation, + nStartOrdinal - 1, nElemOrdCount ); + + if( poGeom == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Based on GType do what is appropriate. */ +/* -------------------------------------------------------------------- */ + if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_LINESTRING) ) + { + CPLAssert(wkbFlatten(poGeom->getGeometryType()) == wkbLineString); + return poGeom; + } + + else if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_POINT) ) + { + CPLAssert(wkbFlatten(poGeom->getGeometryType()) == wkbPoint); + return poGeom; + } + + else if( ORA_GTYPE_MATCH(nGType,ORA_GTYPE_POLYGON) ) + { + CPLAssert(wkbFlatten(poGeom->getGeometryType()) == wkbLineString ); + poPolygon->addRingDirectly( (OGRLinearRing *) poGeom ); + } + else + { + CPLAssert( poCollection != NULL ); + if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint ) + { + int i; + OGRMultiPoint *poMP = (OGRMultiPoint *) poGeom; + + for( i = 0; i < poMP->getNumGeometries(); i++ ) + poCollection->addGeometry( poMP->getGeometryRef(i) ); + delete poMP; + } + else if( nEType % 1000 == 3 ) + { + /* its one poly ring, create new poly or add to existing */ + if( nEType == 1003 ) + { + if( poPolygon != NULL + && poPolygon->getExteriorRing() != NULL ) + { + poCollection->addGeometryDirectly( poPolygon ); + poPolygon = NULL; + } + + poPolygon = new OGRPolygon(); + } + + if( poPolygon != NULL ) + poPolygon->addRingDirectly( (OGRLinearRing *) poGeom ); + else + { + CPLAssert( poPolygon != NULL ); + } + } + else + poCollection->addGeometryDirectly( poGeom ); + } + } + + if( poCollection != NULL + && poPolygon != NULL ) + poCollection->addGeometryDirectly( poPolygon ); + +/* -------------------------------------------------------------------- */ +/* Return resulting collection geometry. */ +/* -------------------------------------------------------------------- */ + if( poCollection == NULL ) + return poPolygon; + else + return poCollection; +} + +/************************************************************************/ +/* LoadElementInfo() */ +/* */ +/* Fetch the start ordinal, count, EType and interpretation */ +/* values for a particular element. */ +/************************************************************************/ + +int +OGROCILayer::LoadElementInfo( int iElement, int nElemCount, int nTotalOrdCount, + int *pnEType, int *pnInterpretation, + int *pnStartOrdinal, int *pnElemOrdCount ) + +{ + OGROCISession *poSession = poDS->GetSession(); + boolean bExists; + OCINumber *hNumber; +/* -------------------------------------------------------------------- */ +/* Get the details about element from the elem_info array. */ +/* -------------------------------------------------------------------- */ + OCICollGetElem(poSession->hEnv, poSession->hError, + (OCIColl *)(hLastGeom->sdo_elem_info), + (sb4)(iElement+0), (boolean *)&bExists, + (dvoid **)&hNumber, NULL ); + OCINumberToInt(poSession->hError, hNumber, (uword)sizeof(ub4), + OCI_NUMBER_UNSIGNED, (dvoid *) pnStartOrdinal ); + + OCICollGetElem(poSession->hEnv, poSession->hError, + (OCIColl *)(hLastGeom->sdo_elem_info), + (sb4)(iElement+1), (boolean *)&bExists, + (dvoid **)&hNumber, NULL ); + OCINumberToInt(poSession->hError, hNumber, (uword)sizeof(ub4), + OCI_NUMBER_UNSIGNED, (dvoid *) pnEType ); + + OCICollGetElem(poSession->hEnv, poSession->hError, + (OCIColl *)(hLastGeom->sdo_elem_info), + (sb4)(iElement+2), (boolean *)&bExists, + (dvoid **)&hNumber, NULL ); + OCINumberToInt(poSession->hError, hNumber, (uword)sizeof(ub4), + OCI_NUMBER_UNSIGNED, (dvoid *) pnInterpretation ); + + if( iElement < nElemCount-3 ) + { + ub4 nNextStartOrdinal; + + OCICollGetElem(poSession->hEnv, poSession->hError, + (OCIColl *)(hLastGeom->sdo_elem_info), + (sb4)(iElement+3), (boolean *)&bExists, + (dvoid **)&hNumber,NULL); + OCINumberToInt(poSession->hError, hNumber, (uword)sizeof(ub4), + OCI_NUMBER_UNSIGNED, (dvoid *) &nNextStartOrdinal ); + + *pnElemOrdCount = nNextStartOrdinal - *pnStartOrdinal; + } + else + *pnElemOrdCount = nTotalOrdCount - *pnStartOrdinal + 1; + + return TRUE; +} + + +/************************************************************************/ +/* TranslateGeometryElement() */ +/************************************************************************/ + +OGRGeometry * +OGROCILayer::TranslateGeometryElement( int *piElement, + int nGType, int nDimension, + int nEType, int nInterpretation, + int nStartOrdinal, int nElemOrdCount ) + +{ +/* -------------------------------------------------------------------- */ +/* Handle simple point. */ +/* -------------------------------------------------------------------- */ + if( nEType == 1 && nInterpretation == 1 ) + { + OGRPoint *poPoint = new OGRPoint(); + double dfX, dfY, dfZ = 0.0; + + GetOrdinalPoint( nStartOrdinal, nDimension, &dfX, &dfY, &dfZ ); + + poPoint->setX( dfX ); + poPoint->setY( dfY ); + if( nDimension == 3 ) + poPoint->setZ( dfZ ); + + return poPoint; + } + +/* -------------------------------------------------------------------- */ +/* Handle multipoint. */ +/* -------------------------------------------------------------------- */ + else if( nEType == 1 && nInterpretation > 1 ) + { + OGRMultiPoint *poMP = new OGRMultiPoint(); + double dfX, dfY, dfZ = 0.0; + int i; + + CPLAssert( nInterpretation == nElemOrdCount / nDimension ); + + for( i = 0; i < nInterpretation; i++ ) + { + GetOrdinalPoint( nStartOrdinal + i*nDimension, nDimension, + &dfX, &dfY, &dfZ ); + + OGRPoint *poPoint = (nDimension == 3) ? new OGRPoint( dfX, dfY, dfZ ): new OGRPoint( dfX, dfY ); + poMP->addGeometryDirectly( poPoint ); + } + return poMP; + } + +/* -------------------------------------------------------------------- */ +/* Discard orientations for oriented points. */ +/* -------------------------------------------------------------------- */ + else if( nEType == 1 && nInterpretation == 0 ) + { + CPLDebug( "OCI", "Ignoring orientations for oriented points." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Handle line strings consisting of straight segments. */ +/* -------------------------------------------------------------------- */ + else if( nEType == 2 && nInterpretation == 1 ) + { + OGRLineString *poLS = new OGRLineString(); + int nPointCount = nElemOrdCount / nDimension, i; + + poLS->setNumPoints( nPointCount ); + + for( i = 0; i < nPointCount; i++ ) + { + double dfX, dfY, dfZ = 0.0; + + GetOrdinalPoint( i*nDimension + nStartOrdinal, nDimension, + &dfX, &dfY, &dfZ ); + if (nDimension == 3) + poLS->setPoint( i, dfX, dfY, dfZ ); + else + poLS->setPoint( i, dfX, dfY ); + } + + return poLS; + } + +/* -------------------------------------------------------------------- */ +/* Handle line strings consisting of circular arcs. */ +/* -------------------------------------------------------------------- */ + else if( nEType == 2 && nInterpretation == 2 ) + { + OGRLineString *poLS = new OGRLineString(); + int nPointCount = nElemOrdCount / nDimension, i; + + for( i = 0; i < nPointCount-2; i += 2 ) + { + double dfStartX, dfStartY, dfStartZ = 0.0; + double dfMidX, dfMidY, dfMidZ = 0.0; + double dfEndX, dfEndY, dfEndZ = 0.0; + + GetOrdinalPoint( i*nDimension + nStartOrdinal, nDimension, + &dfStartX, &dfStartY, &dfStartZ ); + GetOrdinalPoint( (i+1)*nDimension + nStartOrdinal, nDimension, + &dfMidX, &dfMidY, &dfMidZ ); + GetOrdinalPoint( (i+2)*nDimension + nStartOrdinal, nDimension, + &dfEndX, &dfEndY, &dfEndZ ); + + OGROCIStrokeArcToOGRGeometry_Points( dfStartX, dfStartY, + dfMidX, dfMidY, + dfEndX, dfEndY, + 6.0, FALSE, poLS ); + } + + return poLS; + } + +/* -------------------------------------------------------------------- */ +/* Handle polygon rings. Treat curves as if they were */ +/* linestrings. */ +/* -------------------------------------------------------------------- */ + else if( nEType % 1000 == 3 && nInterpretation == 1 ) + { + OGRLinearRing *poLS = new OGRLinearRing(); + int nPointCount = nElemOrdCount / nDimension, i; + + poLS->setNumPoints( nPointCount ); + + for( i = 0; i < nPointCount; i++ ) + { + double dfX, dfY, dfZ = 0.0; + + GetOrdinalPoint( i*nDimension + nStartOrdinal, nDimension, + &dfX, &dfY, &dfZ ); + if (nDimension == 3) + poLS->setPoint( i, dfX, dfY, dfZ ); + else + poLS->setPoint( i, dfX, dfY ); + } + + return poLS; + } + +/* -------------------------------------------------------------------- */ +/* Handle polygon rings made of circular arcs. */ +/* -------------------------------------------------------------------- */ + else if( nEType % 1000 == 3 && nInterpretation == 2 ) + { + OGRLineString *poLS = new OGRLinearRing(); + int nPointCount = nElemOrdCount / nDimension, i; + + for( i = 0; i < nPointCount-2; i += 2 ) + { + double dfStartX, dfStartY, dfStartZ = 0.0; + double dfMidX, dfMidY, dfMidZ = 0.0; + double dfEndX, dfEndY, dfEndZ = 0.0; + + GetOrdinalPoint( i*nDimension + nStartOrdinal, nDimension, + &dfStartX, &dfStartY, &dfStartZ ); + GetOrdinalPoint( (i+1)*nDimension + nStartOrdinal, nDimension, + &dfMidX, &dfMidY, &dfMidZ ); + GetOrdinalPoint( (i+2)*nDimension + nStartOrdinal, nDimension, + &dfEndX, &dfEndY, &dfEndZ ); + + OGROCIStrokeArcToOGRGeometry_Points( dfStartX, dfStartY, + dfMidX, dfMidY, + dfEndX, dfEndY, + 6.0, FALSE, poLS ); + } + + return poLS; + } + +/* -------------------------------------------------------------------- */ +/* Handle rectangle definitions ... translate into a linear ring. */ +/* -------------------------------------------------------------------- */ + else if( nEType % 1000 == 3 && nInterpretation == 3 ) + { + OGRLinearRing *poLS = new OGRLinearRing(); + double dfX1, dfY1, dfZ1 = 0.0; + double dfX2, dfY2, dfZ2 = 0.0; + + GetOrdinalPoint( nStartOrdinal, nDimension, + &dfX1, &dfY1, &dfZ1 ); + GetOrdinalPoint( nStartOrdinal + nDimension, nDimension, + &dfX2, &dfY2, &dfZ2 ); + + poLS->setNumPoints( 5 ); + + poLS->setPoint( 0, dfX1, dfY1, dfZ1 ); + poLS->setPoint( 1, dfX2, dfY1, dfZ1 ); + poLS->setPoint( 2, dfX2, dfY2, dfZ2 ); + poLS->setPoint( 3, dfX1, dfY2, dfZ2 ); + poLS->setPoint( 4, dfX1, dfY1, dfZ1 ); + + return poLS; + } + +/* -------------------------------------------------------------------- */ +/* Handle circle definitions ... translate into a linear ring. */ +/* -------------------------------------------------------------------- */ + else if( nEType % 100 == 3 && nInterpretation == 4 ) + { + OGRLinearRing *poLS = new OGRLinearRing(); + double dfX1, dfY1, dfZ1 = 0.0; + double dfX2, dfY2, dfZ2 = 0.0; + double dfX3, dfY3, dfZ3 = 0.0; + + GetOrdinalPoint( nStartOrdinal, nDimension, + &dfX1, &dfY1, &dfZ1 ); + GetOrdinalPoint( nStartOrdinal + nDimension, nDimension, + &dfX2, &dfY2, &dfZ2 ); + GetOrdinalPoint( nStartOrdinal + nDimension*2, nDimension, + &dfX3, &dfY3, &dfZ3 ); + + OGROCIStrokeArcToOGRGeometry_Points( dfX1, dfY1, + dfX2, dfY2, + dfX3, dfY3, + 6.0, TRUE, poLS ); + + return poLS; + } + +/* -------------------------------------------------------------------- */ +/* Handle compound line strings and polygon rings. */ +/* */ +/* This is quite complicated since we need to consume several */ +/* following elements, and merge the resulting geometries. */ +/* -------------------------------------------------------------------- */ + else if( nEType == 4 || nEType % 100 == 5 ) + { + int nSubElementCount = nInterpretation; + OGRLineString *poLS, *poElemLS; + int nElemCount, nTotalOrdCount; + OGROCISession *poSession = poDS->GetSession(); + + if( nEType == 4 ) + poLS = new OGRLineString(); + else + poLS = new OGRLinearRing(); + + if( poSession->Failed( + OCICollSize( poSession->hEnv, poSession->hError, + (OCIColl *)(hLastGeom->sdo_elem_info), &nElemCount), + "OCICollSize(sdo_elem_info)" ) ) + return NULL; + + if( poSession->Failed( + OCICollSize( poSession->hEnv, poSession->hError, + (OCIColl*)(hLastGeom->sdo_ordinates),&nTotalOrdCount), + "OCICollSize(sdo_ordinates)" ) ) + return NULL; + + for( *piElement += 3; nSubElementCount-- > 0; *piElement += 3 ) + { + LoadElementInfo( *piElement, nElemCount, nTotalOrdCount, + &nEType, &nInterpretation, + &nStartOrdinal, &nElemOrdCount ); + + // Adjust for repeated end point except for last element. + if( nSubElementCount > 0 ) + nElemOrdCount += nDimension; + + // translate element. + poElemLS = (OGRLineString *) + TranslateGeometryElement( piElement, nGType, nDimension, + nEType, nInterpretation, + nStartOrdinal - 1, nElemOrdCount ); + + // Try to append to our aggregate linestring/ring + if( poElemLS ) + { + if( poLS->getNumPoints() > 0 ) + { + CPLAssert( + poElemLS->getX(0) == poLS->getX(poLS->getNumPoints()-1) + && poElemLS->getY(0) ==poLS->getY(poLS->getNumPoints()-1)); + + poLS->addSubLineString( poElemLS, 1 ); + } + else + poLS->addSubLineString( poElemLS, 0 ); + + delete poElemLS; + } + + } + + *piElement -= 3; + return poLS; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise it is apparently unsupported. */ +/* -------------------------------------------------------------------- */ + else + { + + CPLDebug( "OCI", "Geometry with EType=%d, Interp=%d ignored.", + nEType, nInterpretation ); + } + + return NULL; +} + +/************************************************************************/ +/* GetOrdinalPoint() */ +/************************************************************************/ + +int OGROCILayer::GetOrdinalPoint( int iOrdinal, int nDimension, + double *pdfX, double *pdfY, double *pdfZ ) + +{ + OGROCISession *poSession = poDS->GetSession(); + boolean bExists; + OCINumber *hNumber; + + OCICollGetElem( poSession->hEnv, poSession->hError, + (OCIColl *)(hLastGeom->sdo_ordinates), + (sb4)iOrdinal+0, (boolean *)&bExists, + (dvoid **)&hNumber, NULL ); + OCINumberToReal(poSession->hError, hNumber, + (uword)sizeof(double), (dvoid *)pdfX); + OCICollGetElem( poSession->hEnv, poSession->hError, + (OCIColl *)(hLastGeom->sdo_ordinates), + (sb4)iOrdinal + 1, (boolean *)&bExists, + (dvoid **)&hNumber, NULL ); + OCINumberToReal(poSession->hError, hNumber, + (uword)sizeof(double), (dvoid *)pdfY); + if( nDimension == 3 ) + { + OCICollGetElem( poSession->hEnv, poSession->hError, + (OCIColl *)(hLastGeom->sdo_ordinates), + (sb4)iOrdinal + 2, (boolean *)&bExists, + (dvoid **)&hNumber, NULL ); + OCINumberToReal(poSession->hError, hNumber, + (uword)sizeof(double), (dvoid *)pdfZ); + } + + return TRUE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGROCILayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return m_poFilterGeom == NULL; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return TRUE; + + else if( EQUAL(pszCap,OLCTransactions) ) + return TRUE; + + else + return FALSE; +} + + +/************************************************************************/ +/* LookupTableSRID() */ +/* */ +/* Note that the table name may also be prefixed by the owner */ +/* with a dot separator. */ +/************************************************************************/ + +int OGROCILayer::LookupTableSRID() + +{ +/* -------------------------------------------------------------------- */ +/* If we don't have a geometry column, there isn't much point */ +/* in trying. */ +/* -------------------------------------------------------------------- */ + if( pszGeomName == NULL ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Split out the owner if available. */ +/* -------------------------------------------------------------------- */ + const char *pszTableName = GetLayerDefn()->GetName(); + char *pszOwner = NULL; + + if( strstr(pszTableName,".") != NULL ) + { + pszOwner = CPLStrdup(pszTableName); + pszTableName = strstr(pszTableName,".") + 1; + + *(strstr(pszOwner,".")) = '\0'; + } + +/* -------------------------------------------------------------------- */ +/* Build our query command. */ +/* -------------------------------------------------------------------- */ + OGROCIStringBuf oCommand; + + oCommand.Appendf( 1000, "SELECT SRID FROM ALL_SDO_GEOM_METADATA " + "WHERE TABLE_NAME = UPPER('%s') AND COLUMN_NAME = UPPER('%s')", + pszTableName, pszGeomName ); + + if( pszOwner != NULL ) + { + oCommand.Appendf( 500, " AND OWNER = '%s'", pszOwner ); + CPLFree( pszOwner ); + } + +/* -------------------------------------------------------------------- */ +/* Execute query command. */ +/* -------------------------------------------------------------------- */ + OGROCIStatement oGetTables( poDS->GetSession() ); + int nSRID = -1; + + if( oGetTables.Execute( oCommand.GetString() ) == CE_None ) + { + char **papszRow = oGetTables.SimpleFetchRow(); + + if( papszRow != NULL && papszRow[0] != NULL ) + nSRID = atoi( papszRow[0] ); + } + + return nSRID; +} + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char *OGROCILayer::GetFIDColumn() + +{ + if( pszFIDName != NULL ) + return pszFIDName; + else + return ""; +} + +/************************************************************************/ +/* GetGeometryColumn() */ +/************************************************************************/ + +const char *OGROCILayer::GetGeometryColumn() + +{ + if( pszGeomName != NULL ) + return pszGeomName; + else + return ""; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrociloaderlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrociloaderlayer.cpp new file mode 100644 index 000000000..d30e2d391 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrociloaderlayer.cpp @@ -0,0 +1,691 @@ +/****************************************************************************** + * $Id: ogrociloaderlayer.cpp 28430 2015-02-06 20:57:41Z rouault $ + * + * Project: Oracle Spatial Driver + * Purpose: Implementation of the OGROCILoaderLayer class. This implements + * an output only OGRLayer for writing an SQL*Loader file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_oci.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrociloaderlayer.cpp 28430 2015-02-06 20:57:41Z rouault $"); + +/************************************************************************/ +/* OGROCILoaderLayer() */ +/************************************************************************/ + +OGROCILoaderLayer::OGROCILoaderLayer( OGROCIDataSource *poDSIn, + const char * pszTableName, + const char * pszGeomColIn, + int nSRIDIn, + const char *pszLoaderFilenameIn ) + +{ + poDS = poDSIn; + + iNextFIDToWrite = 1; + + bTruncationReported = FALSE; + bHeaderWritten = FALSE; + nLDRMode = LDRM_UNKNOWN; + + poFeatureDefn = new OGRFeatureDefn( pszTableName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + pszGeomName = CPLStrdup( pszGeomColIn ); + pszFIDName = (char*)CPLGetConfigOption( "OCI_FID", "OGR_FID" ); + + + nSRID = nSRIDIn; + poSRS = poDSIn->FetchSRS( nSRID ); + + if( poSRS != NULL ) + poSRS->Reference(); + +/* -------------------------------------------------------------------- */ +/* Open the loader file. */ +/* -------------------------------------------------------------------- */ + pszLoaderFilename = CPLStrdup( pszLoaderFilenameIn ); + + fpData = NULL; + fpLoader = VSIFOpen( pszLoaderFilename, "wt" ); + if( fpLoader == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open SQL*Loader control file:%s", + pszLoaderFilename ); + return; + } + +} + +/************************************************************************/ +/* ~OGROCILoaderLayer() */ +/************************************************************************/ + +OGROCILoaderLayer::~OGROCILoaderLayer() + +{ + if( fpData != NULL ) + VSIFClose( fpData ); + + if( fpLoader != NULL ) + { + VSIFClose( fpLoader ); + FinalizeNewLayer(); + } + + CPLFree( pszLoaderFilename ); + + if( poSRS != NULL && poSRS->Dereference() == 0 ) + delete poSRS; +} + +/************************************************************************/ +/* WriteLoaderHeader() */ +/************************************************************************/ + +void OGROCILoaderLayer::WriteLoaderHeader() + +{ + if( bHeaderWritten ) + return; + +/* -------------------------------------------------------------------- */ +/* Determine name of geometry column to use. */ +/* -------------------------------------------------------------------- */ + const char *pszGeometryName = + CSLFetchNameValue( papszOptions, "GEOMETRY_NAME" ); + if( pszGeometryName == NULL ) + pszGeometryName = "ORA_GEOMETRY"; + +/* -------------------------------------------------------------------- */ +/* Dermine our operation mode. */ +/* -------------------------------------------------------------------- */ + const char *pszLDRMode = CSLFetchNameValue( papszOptions, "LOADER_MODE" ); + + if( pszLDRMode != NULL && EQUAL(pszLDRMode,"VARIABLE") ) + nLDRMode = LDRM_VARIABLE; + else if( pszLDRMode != NULL && EQUAL(pszLDRMode,"BINARY") ) + nLDRMode = LDRM_BINARY; + else + nLDRMode = LDRM_STREAM; + +/* -------------------------------------------------------------------- */ +/* Write loader header info. */ +/* -------------------------------------------------------------------- */ + VSIFPrintf( fpLoader, "LOAD DATA\n" ); + if( nLDRMode == LDRM_STREAM ) + { + VSIFPrintf( fpLoader, "INFILE *\n" ); + VSIFPrintf( fpLoader, "CONTINUEIF NEXT(1:1) = '#'\n" ); + } + else if( nLDRMode == LDRM_VARIABLE ) + { + const char *pszDataFilename = CPLResetExtension( pszLoaderFilename, + "dat" ); + fpData = VSIFOpen( pszDataFilename, "wb" ); + if( fpData == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open data output file `%s'.", + pszDataFilename ); + return; + } + + VSIFPrintf( fpLoader, "INFILE %s \"var 8\"\n", pszDataFilename ); + } + const char *pszExpectedFIDName = + CPLGetConfigOption( "OCI_FID", "OGR_FID" ); + + VSIFPrintf( fpLoader, "INTO TABLE \"%s\" REPLACE\n", + poFeatureDefn->GetName() ); + VSIFPrintf( fpLoader, "FIELDS TERMINATED BY '|'\n" ); + VSIFPrintf( fpLoader, "TRAILING NULLCOLS (\n" ); + VSIFPrintf( fpLoader, " %s INTEGER EXTERNAL,\n", pszExpectedFIDName ); + VSIFPrintf( fpLoader, " %s COLUMN OBJECT (\n", + pszGeometryName ); + VSIFPrintf( fpLoader, " SDO_GTYPE INTEGER EXTERNAL,\n" ); + VSIFPrintf( fpLoader, " SDO_ELEM_INFO VARRAY TERMINATED BY '|/'\n" ); + VSIFPrintf( fpLoader, " (elements INTEGER EXTERNAL),\n" ); + VSIFPrintf( fpLoader, " SDO_ORDINATES VARRAY TERMINATED BY '|/'\n" ); + VSIFPrintf( fpLoader, " (ordinates FLOAT EXTERNAL)\n" ); + VSIFPrintf( fpLoader, " ),\n" ); + +/* -------------------------------------------------------------------- */ +/* Write user field schema. */ +/* -------------------------------------------------------------------- */ + int iField; + + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(iField); + + if( poFldDefn->GetType() == OFTInteger ) + { + VSIFPrintf( fpLoader, " \"%s\" INTEGER EXTERNAL", + poFldDefn->GetNameRef() ); + } + else if( poFldDefn->GetType() == OFTInteger ) + { + VSIFPrintf( fpLoader, " \"%s\" LONGINTEGER EXTERNAL", + poFldDefn->GetNameRef() ); + } + else if( poFldDefn->GetType() == OFTReal ) + { + VSIFPrintf( fpLoader, " \"%s\" FLOAT EXTERNAL", + poFldDefn->GetNameRef() ); + } + else if( poFldDefn->GetType() == OFTString ) + { + VSIFPrintf( fpLoader, " \"%s\" VARCHARC(4)", + poFldDefn->GetNameRef() ); + } + else + { + VSIFPrintf( fpLoader, " \"%s\" VARCHARC(4)", + poFldDefn->GetNameRef() ); + } + + if( iField < poFeatureDefn->GetFieldCount() - 1 ) + VSIFPrintf( fpLoader, "," ); + VSIFPrintf( fpLoader, "\n" ); + } + + VSIFPrintf( fpLoader, ")\n" ); + + if( nLDRMode == LDRM_STREAM ) + VSIFPrintf( fpLoader, "begindata\n" ); + + bHeaderWritten = TRUE; +} + +/************************************************************************/ +/* GetNextFeature() */ +/* */ +/* We override the next feature method because we know that we */ +/* implement the attribute query within the statement and so we */ +/* don't have to test here. Eventually the spatial query will */ +/* be fully tested within the statement as well. */ +/************************************************************************/ + +OGRFeature *OGROCILoaderLayer::GetNextFeature() + +{ + CPLError( CE_Failure, CPLE_NotSupported, + "GetNextFeature() not supported for an OGROCILoaderLayer." ); + return NULL; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGROCILoaderLayer::ResetReading() + +{ + OGROCILayer::ResetReading(); +} + +/************************************************************************/ +/* WriteFeatureStreamMode() */ +/************************************************************************/ + +OGRErr OGROCILoaderLayer::WriteFeatureStreamMode( OGRFeature *poFeature ) + +{ +/* -------------------------------------------------------------------- */ +/* Write the FID. */ +/* -------------------------------------------------------------------- */ + VSIFPrintf( fpLoader, " " CPL_FRMT_GIB "|", poFeature->GetFID() ); + +/* -------------------------------------------------------------------- */ +/* Set the geometry */ +/* -------------------------------------------------------------------- */ + int nLineLen = 0; + if( poFeature->GetGeometryRef() != NULL) + { + char szSRID[128]; + int nGType; + int i; + + if( nSRID == -1 ) + strcpy( szSRID, "NULL" ); + else + sprintf( szSRID, "%d", nSRID ); + + if( TranslateToSDOGeometry( poFeature->GetGeometryRef(), &nGType ) + == OGRERR_NONE ) + { + VSIFPrintf( fpLoader, "%d|", nGType ); + for( i = 0; i < nElemInfoCount; i++ ) + { + VSIFPrintf( fpLoader, "%d|", panElemInfo[i] ); + if( ++nLineLen > 18 && i < nElemInfoCount-1 ) + { + VSIFPrintf( fpLoader, "\n#" ); + nLineLen = 0; + } + } + VSIFPrintf( fpLoader, "/" ); + + for( i = 0; i < nOrdinalCount; i++ ) + { + VSIFPrintf( fpLoader, "%.16g|", padfOrdinals[i] ); + if( ++nLineLen > 6 && i < nOrdinalCount-1 ) + { + VSIFPrintf( fpLoader, "\n#" ); + nLineLen = 0; + } + } + VSIFPrintf( fpLoader, "/" ); + } + else + { + VSIFPrintf( fpLoader, "0|/|/" ); + } + } + else + { + VSIFPrintf( fpLoader, "0|/|/" ); + } + +/* -------------------------------------------------------------------- */ +/* Set the other fields. */ +/* -------------------------------------------------------------------- */ + int i; + + nLineLen = 0; + VSIFPrintf( fpLoader, "\n#" ); + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(i); + + if( !poFeature->IsFieldSet( i ) ) + { + if( poFldDefn->GetType() != OFTInteger + && poFldDefn->GetType() != OFTInteger64 + && poFldDefn->GetType() != OFTReal ) + VSIFPrintf( fpLoader, "%04d", 0 ); + continue; + } + + const char *pszStrValue = poFeature->GetFieldAsString(i); + + if( nLineLen > 70 ) + { + VSIFPrintf( fpLoader, "\n#" ); + nLineLen = 0; + } + + nLineLen += strlen(pszStrValue); + + if( poFldDefn->GetType() == OFTInteger + || poFldDefn->GetType() == OFTInteger64 + || poFldDefn->GetType() == OFTReal ) + { + if( poFldDefn->GetWidth() > 0 && bPreservePrecision + && (int) strlen(pszStrValue) > poFldDefn->GetWidth() ) + { + ReportTruncation( poFldDefn ); + VSIFPrintf( fpLoader, "|" ); + } + else + VSIFPrintf( fpLoader, "%s|", pszStrValue ); + } + else + { + int nLength = strlen(pszStrValue); + + if( poFldDefn->GetWidth() > 0 && nLength > poFldDefn->GetWidth() ) + { + ReportTruncation( poFldDefn ); + nLength = poFldDefn->GetWidth(); + } + + VSIFPrintf( fpLoader, "%04d", nLength ); + VSIFWrite( (void *) pszStrValue, 1, nLength, fpLoader ); + } + } + + if( VSIFPrintf( fpLoader, "\n" ) == 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Write to loader file failed, likely out of disk space." ); + return OGRERR_FAILURE; + } + else + return OGRERR_NONE; +} + +/************************************************************************/ +/* WriteFeatureVariableMode() */ +/************************************************************************/ + +OGRErr OGROCILoaderLayer::WriteFeatureVariableMode( OGRFeature *poFeature ) + +{ + OGROCIStringBuf oLine; + + if( fpData == NULL ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Write the FID. */ +/* -------------------------------------------------------------------- */ + oLine.Append( "00000000" ); + oLine.Appendf( 32, " " CPL_FRMT_GIB "|", poFeature->GetFID() ); + +/* -------------------------------------------------------------------- */ +/* Set the geometry */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetGeometryRef() != NULL) + { + char szSRID[128]; + int nGType; + int i; + + if( nSRID == -1 ) + strcpy( szSRID, "NULL" ); + else + sprintf( szSRID, "%d", nSRID ); + + if( TranslateToSDOGeometry( poFeature->GetGeometryRef(), &nGType ) + == OGRERR_NONE ) + { + oLine.Appendf( 32, "%d|", nGType ); + for( i = 0; i < nElemInfoCount; i++ ) + { + oLine.Appendf( 32, "%d|", panElemInfo[i] ); + } + oLine.Append( "/" ); + + for( i = 0; i < nOrdinalCount; i++ ) + { + oLine.Appendf( 32, "%.16g|", padfOrdinals[i] ); + } + oLine.Append( "/" ); + } + else + { + oLine.Append( "0|/|/" ); + } + } + else + { + oLine.Append( "0|/|/" ); + } + +/* -------------------------------------------------------------------- */ +/* Set the other fields. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(i); + + if( !poFeature->IsFieldSet( i ) ) + { + if( poFldDefn->GetType() != OFTInteger + && poFldDefn->GetType() != OFTInteger64 + && poFldDefn->GetType() != OFTReal ) + oLine.Append( "0000" ); + else + oLine.Append( "|" ); + continue; + } + + const char *pszStrValue = poFeature->GetFieldAsString(i); + + if( poFldDefn->GetType() == OFTInteger + || poFldDefn->GetType() == OFTInteger64 + || poFldDefn->GetType() == OFTReal ) + { + if( poFldDefn->GetWidth() > 0 && bPreservePrecision + && (int) strlen(pszStrValue) > poFldDefn->GetWidth() ) + { + ReportTruncation( poFldDefn ); + oLine.Append( "|" ); + } + else + { + oLine.Append( pszStrValue ); + oLine.Append( "|" ); + } + } + else + { + int nLength = strlen(pszStrValue); + + if( poFldDefn->GetWidth() > 0 && nLength > poFldDefn->GetWidth() ) + { + ReportTruncation( poFldDefn ); + nLength = poFldDefn->GetWidth(); + ((char *) pszStrValue)[nLength] = '\0'; + } + + oLine.Appendf( 5, "%04d", nLength ); + oLine.Append( pszStrValue ); + } + } + + oLine.Appendf( 3, "\n" ); + +/* -------------------------------------------------------------------- */ +/* Update the line's length, and write to disk. */ +/* -------------------------------------------------------------------- */ + char szLength[9]; + size_t nStringLen = strlen(oLine.GetString()); + + sprintf( szLength, "%08d", (int) (nStringLen-8) ); + strncpy( oLine.GetString(), szLength, 8 ); + + if( VSIFWrite( oLine.GetString(), 1, nStringLen, fpData ) != nStringLen ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Write to loader file failed, likely out of disk space." ); + return OGRERR_FAILURE; + } + else + return OGRERR_NONE; +} + +/************************************************************************/ +/* WriteFeatureBinaryMode() */ +/************************************************************************/ + +OGRErr OGROCILoaderLayer::WriteFeatureBinaryMode( OGRFeature *poFeature ) + +{ + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGROCILoaderLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + WriteLoaderHeader(); + +/* -------------------------------------------------------------------- */ +/* Set the FID. */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetFID() == OGRNullFID ) + poFeature->SetFID( iNextFIDToWrite++ ); + +/* -------------------------------------------------------------------- */ +/* Add extents of this geometry to the existing layer extents. */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetGeometryRef() != NULL ) + { + OGREnvelope sThisExtent; + + poFeature->GetGeometryRef()->getEnvelope( &sThisExtent ); + sExtent.Merge( sThisExtent ); + } + +/* -------------------------------------------------------------------- */ +/* Call the mode specific write function. */ +/* -------------------------------------------------------------------- */ + if( nLDRMode == LDRM_STREAM ) + return WriteFeatureStreamMode( poFeature ); + else if( nLDRMode == LDRM_VARIABLE ) + return WriteFeatureVariableMode( poFeature ); + else if( nLDRMode == LDRM_BINARY ) + return WriteFeatureBinaryMode( poFeature ); + else + return OGRERR_UNSUPPORTED_OPERATION; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGROCILoaderLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCSequentialWrite) ) + return TRUE; + + else if( EQUAL(pszCap,OLCCreateField) ) + return TRUE; + + else + return OGROCILayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGROCILoaderLayer::GetFeatureCount( int bForce ) + +{ + return iNextFIDToWrite - 1; +} + +/************************************************************************/ +/* FinalizeNewLayer() */ +/* */ +/* Our main job here is to update the USER_SDO_GEOM_METADATA */ +/* table to include the correct array of dimension object with */ +/* the appropriate extents for this layer. We may also do */ +/* spatial indexing at this point. */ +/************************************************************************/ + +void OGROCILoaderLayer::FinalizeNewLayer() + +{ + OGROCIStringBuf sDimUpdate; + +/* -------------------------------------------------------------------- */ +/* If the dimensions are degenerate (all zeros) then we assume */ +/* there were no geometries, and we don't bother setting the */ +/* dimensions. */ +/* -------------------------------------------------------------------- */ + if( sExtent.MaxX == 0 && sExtent.MinX == 0 + && sExtent.MaxY == 0 && sExtent.MinY == 0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Layer %s appears to have no geometry ... not setting SDO DIMINFO metadata.", + poFeatureDefn->GetName() ); + return; + + } + +/* -------------------------------------------------------------------- */ +/* Establish the extents and resolution to use. */ +/* -------------------------------------------------------------------- */ + double dfResSize; + double dfXMin, dfXMax, dfXRes; + double dfYMin, dfYMax, dfYRes; + double dfZMin, dfZMax, dfZRes; + + if( sExtent.MaxX - sExtent.MinX > 400 ) + dfResSize = 0.001; + else + dfResSize = 0.0000001; + + dfXMin = sExtent.MinX - dfResSize * 3; + dfXMax = sExtent.MaxX + dfResSize * 3; + dfXRes = dfResSize; + ParseDIMINFO( "DIMINFO_X", &dfXMin, &dfXMax, &dfXRes ); + + dfYMin = sExtent.MinY - dfResSize * 3; + dfYMax = sExtent.MaxY + dfResSize * 3; + dfYRes = dfResSize; + ParseDIMINFO( "DIMINFO_Y", &dfYMin, &dfYMax, &dfYRes ); + + dfZMin = -100000.0; + dfZMax = 100000.0; + dfZRes = 0.002; + ParseDIMINFO( "DIMINFO_Z", &dfZMin, &dfZMax, &dfZRes ); + +/* -------------------------------------------------------------------- */ +/* Prepare dimension update statement. */ +/* -------------------------------------------------------------------- */ + sDimUpdate.Append( "UPDATE USER_SDO_GEOM_METADATA SET DIMINFO = " ); + sDimUpdate.Append( "MDSYS.SDO_DIM_ARRAY(" ); + + sDimUpdate.Appendf(200, + "MDSYS.SDO_DIM_ELEMENT('X',%.16g,%.16g,%.12g)", + dfXMin, dfXMax, dfXRes ); + sDimUpdate.Appendf(200, + ",MDSYS.SDO_DIM_ELEMENT('Y',%.16g,%.16g,%.12g)", + dfYMin, dfYMax, dfYRes ); + + if( nDimension == 3 ) + { + sDimUpdate.Appendf(200, + ",MDSYS.SDO_DIM_ELEMENT('Z',%.16g,%.16g,%.12g)", + dfZMin, dfZMax, dfZRes ); + } + + sDimUpdate.Append( ")" ); + + sDimUpdate.Appendf( strlen(poFeatureDefn->GetName()) + 100, + " WHERE table_name = UPPER('%s')", + poFeatureDefn->GetName() ); + +/* -------------------------------------------------------------------- */ +/* Execute the metadata update. */ +/* -------------------------------------------------------------------- */ + OGROCIStatement oExecStatement( poDS->GetSession() ); + + if( oExecStatement.Execute( sDimUpdate.GetString() ) != CE_None ) + return; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrociselectlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrociselectlayer.cpp new file mode 100644 index 000000000..ee9496f5e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrociselectlayer.cpp @@ -0,0 +1,148 @@ +/****************************************************************************** + * $Id: ogrociselectlayer.cpp 28407 2015-02-03 10:47:59Z rouault $ + * + * Project: Oracle Spatial Driver + * Purpose: Implementation of the OGROCISelectLayer class. This class + * provides read semantics on the result of a SELECT statement. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_oci.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrociselectlayer.cpp 28407 2015-02-03 10:47:59Z rouault $"); + +/************************************************************************/ +/* OGROCISelectLayer() */ +/************************************************************************/ + +OGROCISelectLayer::OGROCISelectLayer( OGROCIDataSource *poDSIn, + const char * pszQuery, + OGROCIStatement *poDescribedCommand ) + +{ + poDS = poDSIn; + + iNextShapeId = 0; + + poFeatureDefn = ReadTableDefinition( poDescribedCommand ); + SetDescription( poFeatureDefn->GetName() ); + + pszQueryStatement = CPLStrdup(pszQuery); + + ResetReading(); +} + +/************************************************************************/ +/* ~OGROCISelectLayer() */ +/************************************************************************/ + +OGROCISelectLayer::~OGROCISelectLayer() + +{ +} + +/************************************************************************/ +/* ReadTableDefinition() */ +/* */ +/* Build layer definition from the described information about */ +/* the command. */ +/************************************************************************/ + +OGRFeatureDefn * +OGROCISelectLayer::ReadTableDefinition( OGROCIStatement *poCommand ) + +{ + OGROCISession *poSession = poDS->GetSession(); + +/* -------------------------------------------------------------------- */ +/* Parse the returned table information. */ +/* -------------------------------------------------------------------- */ + for( int iParm = 0; TRUE; iParm++ ) + { + OGRFieldDefn oField( "", OFTString ); + int nStatus; + OCIParam *hParmDesc; + ub2 nOCIType; + ub4 nOCILen; + + nStatus = + OCIParamGet( poCommand->GetStatement(), OCI_HTYPE_STMT, + poSession->hError, (dvoid**)&hParmDesc, + (ub4) iParm+1 ); + + if( nStatus == OCI_ERROR ) + break; + + if( poSession->GetParmInfo( hParmDesc, &oField, &nOCIType, &nOCILen ) + != CE_None ) + break; + + if( oField.GetType() == OFTBinary && nOCIType == 108 ) + { + CPLFree( pszGeomName ); + pszGeomName = CPLStrdup( oField.GetNameRef() ); + iGeomColumn = iParm; + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Use the schema off the statement. */ +/* -------------------------------------------------------------------- */ + OGRFeatureDefn *poDefn; + + poDefn = poCommand->GetResultDefn(); + if( iGeomColumn >= 0 ) + poDefn->SetGeomType(wkbUnknown); + poDefn->Reference(); + +/* -------------------------------------------------------------------- */ +/* Do we have an FID? */ +/* -------------------------------------------------------------------- */ + const char *pszExpectedFIDName = + CPLGetConfigOption( "OCI_FID", "OGR_FID" ); + if( poDefn->GetFieldIndex(pszExpectedFIDName) > -1 ) + { + iFIDColumn = poDefn->GetFieldIndex(pszExpectedFIDName); + pszFIDName = CPLStrdup(poDefn->GetFieldDefn(iFIDColumn)->GetNameRef()); + } + + if( EQUAL(pszExpectedFIDName, "OGR_FID") && pszFIDName ) + { + for(int i=0;iGetFieldCount();i++) + { + // This is presumably a Integer since we always create Integer64 with a + // defined precision + if( poDefn->GetFieldDefn(i)->GetType() == OFTInteger64 && + poDefn->GetFieldDefn(i)->GetWidth() == 0 ) + { + poDefn->GetFieldDefn(i)->SetType(OFTInteger); + } + } + } + + return poDefn; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocisession.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocisession.cpp new file mode 100644 index 000000000..7d20a08b1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocisession.cpp @@ -0,0 +1,569 @@ +/****************************************************************************** + * $Id: ogrocisession.cpp 28481 2015-02-13 17:11:15Z rouault $ + * + * Project: Oracle Spatial Driver + * Purpose: Implementation of OGROCISession, which encapsulates much of the + * direct access to OCI. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_oci.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrocisession.cpp 28481 2015-02-13 17:11:15Z rouault $"); + +/************************************************************************/ +/* OGRGetOCISession() */ +/************************************************************************/ + +OGROCISession * OGRGetOCISession( const char *pszUserid, + const char *pszPassword, + const char *pszDatabase ) + +{ + OGROCISession *poSession; + + poSession = new OGROCISession(); + if( poSession->EstablishSession( pszUserid, pszPassword, pszDatabase ) ) + return poSession; + else + { + delete poSession; + return NULL; + } +} + +/************************************************************************/ +/* OGROCISession() */ +/************************************************************************/ + +OGROCISession::OGROCISession() + +{ + hEnv = NULL; + hError = NULL; + hSvcCtx = NULL; + hServer = NULL; + hSession = NULL; + hDescribe = NULL; + hGeometryTDO = NULL; + hOrdinatesTDO = NULL; + hElemInfoTDO = NULL; + pszUserid = NULL; + pszPassword = NULL; + pszDatabase = NULL; +} + +/************************************************************************/ +/* ~OGROCISession() */ +/************************************************************************/ + +OGROCISession::~OGROCISession() + +{ + if( hDescribe != NULL ) + OCIHandleFree((dvoid *)hDescribe, (ub4)OCI_HTYPE_DESCRIBE); + + if( hSvcCtx != NULL ) + { + OCISessionEnd(hSvcCtx, hError, hSession, (ub4) 0); + + if( hSvcCtx && hError) + OCIServerDetach(hServer, hError, (ub4) OCI_DEFAULT); + + if( hServer ) + OCIHandleFree((dvoid *) hServer, (ub4) OCI_HTYPE_SERVER); + + if( hSvcCtx ) + OCIHandleFree((dvoid *) hSvcCtx, (ub4) OCI_HTYPE_SVCCTX); + + if( hError ) + OCIHandleFree((dvoid *) hError, (ub4) OCI_HTYPE_ERROR); + + if( hSession ) + OCIHandleFree((dvoid *) hSession, (ub4) OCI_HTYPE_SESSION); + + if( hEnv ) + OCIHandleFree((dvoid *) hEnv, (ub4) OCI_HTYPE_ENV); + } + + CPLFree( pszUserid ); + CPLFree( pszPassword ); + CPLFree( pszDatabase ); +} + +/************************************************************************/ +/* EstablishSession() */ +/************************************************************************/ + +int OGROCISession::EstablishSession( const char *pszUserid, + const char *pszPassword, + const char *pszDatabase ) + +{ +/* -------------------------------------------------------------------- */ +/* Operational Systems's authentication option */ +/* -------------------------------------------------------------------- */ + + ub4 eCred = OCI_CRED_RDBMS; + + if( EQUAL(pszDatabase, "") && + EQUAL(pszPassword, "") && + EQUAL(pszUserid, "/") ) + { + eCred = OCI_CRED_EXT; + } + +/* -------------------------------------------------------------------- */ +/* Initialize Environment handler */ +/* -------------------------------------------------------------------- */ + + if( Failed( OCIInitialize((ub4) (OCI_DEFAULT | OCI_OBJECT), (dvoid *)0, + (dvoid * (*)(dvoid *, size_t)) 0, + (dvoid * (*)(dvoid *, dvoid *, size_t))0, + (void (*)(dvoid *, dvoid *)) 0 ) ) ) + { + return FALSE; + } + + if( Failed( OCIEnvInit( (OCIEnv **) &hEnv, OCI_DEFAULT, (size_t) 0, + (dvoid **) 0 ) ) ) + { + return FALSE; + } + + if( Failed( OCIHandleAlloc( (dvoid *) hEnv, (dvoid **) &hError, + OCI_HTYPE_ERROR, (size_t) 0, (dvoid **) 0) ) ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Initialize Server Context */ +/* -------------------------------------------------------------------- */ + + if( Failed( OCIHandleAlloc( (dvoid *) hEnv, (dvoid **) &hServer, + OCI_HTYPE_SERVER, (size_t) 0, (dvoid **) 0) ) ) + { + return FALSE; + } + + if( Failed( OCIHandleAlloc( (dvoid *) hEnv, (dvoid **) &hSvcCtx, + OCI_HTYPE_SVCCTX, (size_t) 0, (dvoid **) 0) ) ) + { + return FALSE; + } + + if( Failed( OCIServerAttach( hServer, hError, (text*) pszDatabase, + strlen((char*) pszDatabase), 0) ) ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Initialize Service Context */ +/* -------------------------------------------------------------------- */ + + if( Failed( OCIAttrSet( (dvoid *) hSvcCtx, OCI_HTYPE_SVCCTX, (dvoid *)hServer, + (ub4) 0, OCI_ATTR_SERVER, (OCIError *) hError) ) ) + { + return FALSE; + } + + if( Failed( OCIHandleAlloc((dvoid *) hEnv, (dvoid **)&hSession, + (ub4) OCI_HTYPE_SESSION, (size_t) 0, (dvoid **) 0) ) ) + { + return FALSE; + } + + if( Failed( OCIAttrSet((dvoid *) hSession, (ub4) OCI_HTYPE_SESSION, + (dvoid *) pszUserid, (ub4) strlen((char *) pszUserid), + (ub4) OCI_ATTR_USERNAME, hError) ) ) + { + return FALSE; + } + + if( Failed( OCIAttrSet((dvoid *) hSession, (ub4) OCI_HTYPE_SESSION, + (dvoid *) pszPassword, (ub4) strlen((char *) pszPassword), + (ub4) OCI_ATTR_PASSWORD, hError) ) ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Initialize Session */ +/* -------------------------------------------------------------------- */ + + if( Failed( OCISessionBegin(hSvcCtx, hError, hSession, eCred, + (ub4) OCI_DEFAULT) ) ) + { + CPLDebug("OCI", "OCISessionBegin() failed to intialize session"); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Initialize Service */ +/* -------------------------------------------------------------------- */ + + if( Failed( OCIAttrSet((dvoid *) hSvcCtx, (ub4) OCI_HTYPE_SVCCTX, + (dvoid *) hSession, (ub4) 0, + (ub4) OCI_ATTR_SESSION, hError) ) ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Create a describe handle. */ +/* -------------------------------------------------------------------- */ + + if( Failed( + OCIHandleAlloc( hEnv, (dvoid **) &hDescribe, (ub4)OCI_HTYPE_DESCRIBE, + (size_t)0, (dvoid **)0 ), + "OCIHandleAlloc(Describe)" ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Try to get the MDSYS.SDO_GEOMETRY type object. */ +/* -------------------------------------------------------------------- */ + /* If we have no MDSYS.SDO_GEOMETRY then we consider we are + working along with the VRT driver and access non spatial tables. + See #2202 for more details (Tamas Szekeres)*/ + if (OCIDescribeAny(hSvcCtx, hError, + (text *) SDO_GEOMETRY, (ub4) strlen(SDO_GEOMETRY), + OCI_OTYPE_NAME, (ub1) OCI_DEFAULT, (ub1)OCI_PTYPE_TYPE, + hDescribe ) != OCI_ERROR) + { + hGeometryTDO = PinTDO( SDO_GEOMETRY ); + if( hGeometryTDO == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Try to get the MDSYS.SDO_ORDINATE_ARRAY type object. */ +/* -------------------------------------------------------------------- */ + hOrdinatesTDO = PinTDO( "MDSYS.SDO_ORDINATE_ARRAY" ); + if( hOrdinatesTDO == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Try to get the MDSYS.SDO_ELEM_INFO_ARRAY type object. */ +/* -------------------------------------------------------------------- */ + hElemInfoTDO = PinTDO( "MDSYS.SDO_ELEM_INFO_ARRAY" ); + if( hElemInfoTDO == NULL ) + return FALSE; + } +/* -------------------------------------------------------------------- */ +/* Record information about the session. */ +/* -------------------------------------------------------------------- */ + this->pszUserid = CPLStrdup(pszUserid); + this->pszPassword = CPLStrdup(pszPassword); + this->pszDatabase = CPLStrdup(pszDatabase); + +/* -------------------------------------------------------------------- */ +/* Setting upt the OGR compatible time formating rules. */ +/* -------------------------------------------------------------------- */ + OGROCIStatement oSetNLSTimeFormat( this ); + if( oSetNLSTimeFormat.Execute( "ALTER SESSION SET NLS_DATE_FORMAT='YYYY/MM/DD' \ + NLS_TIME_FORMAT='HH24:MI:SS' NLS_TIME_TZ_FORMAT='HH24:MI:SS TZHTZM' \ + NLS_TIMESTAMP_FORMAT='YYYY/MM/DD HH24:MI:SS' \ + NLS_TIMESTAMP_TZ_FORMAT='YYYY/MM/DD HH24:MI:SS TZHTZM' \ + NLS_NUMERIC_CHARACTERS = '. '" ) != CE_None ) + return OGRERR_FAILURE; + + return TRUE; +} + +/************************************************************************/ +/* Failed() */ +/************************************************************************/ + +int OGROCISession::Failed( sword nStatus, const char *pszFunction ) + +{ + if( pszFunction == NULL ) + pszFunction = ""; + if( nStatus == OCI_ERROR ) + { + sb4 nErrCode = 0; + char szErrorMsg[10000]; + + szErrorMsg[0] = '\0'; + if( hError != NULL ) + { + OCIErrorGet( (dvoid *) hError, (ub4) 1, NULL, &nErrCode, + (text *) szErrorMsg, (ub4) sizeof(szErrorMsg), + OCI_HTYPE_ERROR ); + } + szErrorMsg[sizeof(szErrorMsg)-1] = '\0'; + + CPLError( CE_Failure, CPLE_AppDefined, + "%s in %s", szErrorMsg, pszFunction ); + return TRUE; + } + else if( nStatus == OCI_NEED_DATA ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OCI_NEED_DATA" ); + return TRUE; + } + else if( nStatus == OCI_INVALID_HANDLE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OCI_INVALID_HANDLE in %s", pszFunction ); + return TRUE; + } + else if( nStatus == OCI_STILL_EXECUTING ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OCI_STILL_EXECUTING in %s", pszFunction ); + return TRUE; + } + else if( nStatus == OCI_CONTINUE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OCI_CONTINUE in %s", pszFunction ); + return TRUE; + } + else + return FALSE; +} + +/************************************************************************/ +/* GetParmInfo() */ +/************************************************************************/ + +CPLErr +OGROCISession::GetParmInfo( OCIParam *hParmDesc, OGRFieldDefn *poOGRDefn, + ub2 *pnOCIType, ub4 *pnOCILen ) + +{ + ub2 nOCIType, nOCILen; + ub4 nColLen; + ub1 bOCINull; + char *pszColName; + char szTermColName[128]; + +/* -------------------------------------------------------------------- */ +/* Get basic parameter details. */ +/* -------------------------------------------------------------------- */ + if( Failed( + OCIAttrGet( hParmDesc, OCI_DTYPE_PARAM, + (dvoid **)&nOCIType, 0, OCI_ATTR_DATA_TYPE, hError ), + "OCIAttrGet(Type)" ) ) + return CE_Failure; + + if( Failed( + OCIAttrGet( hParmDesc, OCI_DTYPE_PARAM, + (dvoid **)&nOCILen, 0, OCI_ATTR_DATA_SIZE, hError ), + "OCIAttrGet(Size)" ) ) + return CE_Failure; + + if( Failed( + OCIAttrGet( hParmDesc, OCI_DTYPE_PARAM, (dvoid **)&pszColName, + &nColLen, OCI_ATTR_NAME, hError ), + "OCIAttrGet(Name)") ) + return CE_Failure; + + if( Failed( + OCIAttrGet( hParmDesc, OCI_DTYPE_PARAM, (dvoid **)&bOCINull, + 0, OCI_ATTR_IS_NULL, hError ), + "OCIAttrGet(Null)") ) + return CE_Failure; + + if( nColLen >= sizeof(szTermColName) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Column length (%d) longer than column name buffer (%d) in\n" + "OGROCISession::GetParmInfo()", + nColLen, (int) sizeof(szTermColName) ); + return CE_Failure; + } + + strncpy( szTermColName, pszColName, nColLen ); + szTermColName[nColLen] = '\0'; + + poOGRDefn->SetName( szTermColName ); + poOGRDefn->SetNullable( bOCINull ); + +/* -------------------------------------------------------------------- */ +/* Attempt to classify as an OGRType. */ +/* -------------------------------------------------------------------- */ + switch( nOCIType ) + { + case SQLT_CHR: + case SQLT_AFC: /* CHAR(), NCHAR() */ + poOGRDefn->SetType( OFTString ); + if( nOCILen <= 4000 ) + poOGRDefn->SetWidth( nOCILen ); + break; + + case SQLT_NUM: + { + // NOTE: OCI docs say this should be ub1 type, but we have + // determined that oracle is actually returning a short so we + // use that type and try to compensate for possible problems by + // initializing, and dividing by 256 if it is large. + unsigned short byPrecision = 0; + sb1 nScale; + + if( Failed( + OCIAttrGet( hParmDesc, OCI_DTYPE_PARAM, (dvoid **)&byPrecision, + 0, OCI_ATTR_PRECISION, hError ), + "OCIAttrGet(Precision)" ) ) + return CE_Failure; + if( Failed( + OCIAttrGet( hParmDesc, OCI_DTYPE_PARAM, (dvoid **)&nScale, + 0, OCI_ATTR_SCALE, hError ), + "OCIAttrGet(Scale)") ) + return CE_Failure; +#ifdef notdef + CPLDebug( "OCI", "%s: Scale=%d, Precision=%d", + szTermColName, nScale, byPrecision ); +#endif + if( byPrecision > 255 ) + byPrecision = byPrecision / 256; + + if( nScale < 0 ) + poOGRDefn->SetType( OFTReal ); + else if( nScale > 0 ) + { + poOGRDefn->SetType( OFTReal ); + poOGRDefn->SetWidth( byPrecision ); + poOGRDefn->SetPrecision( nScale ); + } + else if( byPrecision < 38 ) + { + poOGRDefn->SetType( (byPrecision < 10) ? OFTInteger : OFTInteger64 ); + poOGRDefn->SetWidth( byPrecision ); + } + else + { + poOGRDefn->SetType( OFTInteger64 ); + } + } + break; + + case SQLT_DAT: + case SQLT_DATE: + poOGRDefn->SetType( OFTDate ); + break; + case SQLT_TIMESTAMP: + case SQLT_TIMESTAMP_TZ: + case SQLT_TIMESTAMP_LTZ: + case SQLT_TIME: + case SQLT_TIME_TZ: + poOGRDefn->SetType( OFTDateTime ); + break; + + case SQLT_RID: + case SQLT_BIN: + case SQLT_LBI: + case 111: /* REF */ + case SQLT_CLOB: + case SQLT_BLOB: + case SQLT_FILE: + case 208: /* UROWID */ + poOGRDefn->SetType( OFTBinary ); + break; + + default: + poOGRDefn->SetType( OFTBinary ); + break; + } + + if( pnOCIType != NULL ) + *pnOCIType = nOCIType; + + if( pnOCILen != NULL ) + *pnOCILen = nOCILen; + + return CE_None; +} + +/************************************************************************/ +/* CleanName() */ +/* */ +/* Modify a name in-place to be a well formed Oracle name. */ +/************************************************************************/ + +void OGROCISession::CleanName( char * pszName ) + +{ + int i; + + if( strlen(pszName) > 30 ) + pszName[30] = '\0'; + + for( i = 0; pszName[i] != '\0'; i++ ) + { + pszName[i] = toupper(pszName[i]); + + if( (pszName[i] < '0' || pszName[i] > '9') + && (pszName[i] < 'A' || pszName[i] > 'Z') + && pszName[i] != '_' ) + pszName[i] = '_'; + } +} + +/************************************************************************/ +/* PinTDO() */ +/* */ +/* Fetch a Type Description Object for the named type. */ +/************************************************************************/ + +OCIType *OGROCISession::PinTDO( const char *pszType ) + +{ + OCIParam *hGeomParam = NULL; + OCIRef *hGeomTypeRef = NULL; + OCIType *hPinnedTDO = NULL; + + if( Failed( + OCIDescribeAny(hSvcCtx, hError, + (text *) pszType, (ub4) strlen(pszType), + OCI_OTYPE_NAME, (ub1)1, (ub1)OCI_PTYPE_TYPE, + hDescribe ), + "GetTDO()->OCIDescribeAny()" ) ) + return NULL; + + if( Failed( + OCIAttrGet((dvoid *)hDescribe, (ub4)OCI_HTYPE_DESCRIBE, + (dvoid *)&hGeomParam, (ub4 *)0, (ub4)OCI_ATTR_PARAM, + hError), "GetTDO()->OCIGetAttr(ATTR_PARAM)") ) + return NULL; + + if( Failed( + OCIAttrGet((dvoid *)hGeomParam, (ub4)OCI_DTYPE_PARAM, + (dvoid *)&hGeomTypeRef, (ub4 *)0, (ub4)OCI_ATTR_REF_TDO, + hError), "GetTDO()->OCIAttrGet(ATTR_REF_TDO)" ) ) + return NULL; + + if( Failed( + OCIObjectPin(hEnv, hError, hGeomTypeRef, (OCIComplexObject *)0, + OCI_PIN_ANY, OCI_DURATION_SESSION, + OCI_LOCK_NONE, (dvoid **)&hPinnedTDO ), + "GetTDO()->OCIObjectPin()" ) ) + return NULL; + + return hPinnedTDO; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocistatement.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocistatement.cpp new file mode 100644 index 000000000..aea733c0d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocistatement.cpp @@ -0,0 +1,409 @@ +/****************************************************************************** + * $Id: ogrocistatement.cpp 28809 2015-03-28 17:10:07Z rouault $ + * + * Project: Oracle Spatial Driver + * Purpose: Implementation of OGROCIStatement, which encapsulates the + * preparation, executation and fetching from an SQL statement. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_oci.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrocistatement.cpp 28809 2015-03-28 17:10:07Z rouault $"); + +/************************************************************************/ +/* OGROCIStatement() */ +/************************************************************************/ + +OGROCIStatement::OGROCIStatement( OGROCISession *poSessionIn ) + +{ + poSession = poSessionIn; + hStatement = NULL; + poDefn = NULL; + + nRawColumnCount = 0; + papszCurColumn = NULL; + papszCurImage = NULL; + panCurColumnInd = NULL; + panFieldMap = NULL; + + pszCommandText = NULL; + nAffectedRows = 0; +} + +/************************************************************************/ +/* ~OGROCIStatement() */ +/************************************************************************/ + +OGROCIStatement::~OGROCIStatement() + +{ + Clean(); +} + +/************************************************************************/ +/* Clean() */ +/************************************************************************/ + +void OGROCIStatement::Clean() + +{ + int i; + + CPLFree( pszCommandText ); + pszCommandText = NULL; + + if( papszCurColumn != NULL ) + { + for( i = 0; papszCurColumn[i] != NULL; i++ ) + CPLFree( papszCurColumn[i] ); + } + CPLFree( papszCurColumn ); + papszCurColumn = NULL; + + CPLFree( papszCurImage ); + papszCurImage = NULL; + + CPLFree( panCurColumnInd ); + panCurColumnInd = NULL; + + CPLFree( panFieldMap ); + panFieldMap = NULL; + + if( poDefn != NULL && poDefn->Dereference() <= 0 ) + { + delete poDefn; + poDefn = NULL; + } + + if( hStatement != NULL ) + { + OCIHandleFree((dvoid *)hStatement, (ub4)OCI_HTYPE_STMT); + hStatement = NULL; + } +} + +/************************************************************************/ +/* Prepare() */ +/************************************************************************/ + +CPLErr OGROCIStatement::Prepare( const char *pszSQLStatement ) + +{ + Clean(); + + CPLDebug( "OCI", "Prepare(%s)", pszSQLStatement ); + + pszCommandText = CPLStrdup(pszSQLStatement); + + if( hStatement != NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Statement already executed once on this OGROCIStatement." ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Allocate a statement handle. */ +/* -------------------------------------------------------------------- */ + if( poSession->Failed( + OCIHandleAlloc( poSession->hEnv, (dvoid **) &hStatement, + (ub4)OCI_HTYPE_STMT,(size_t)0, (dvoid **)0 ), + "OCIHandleAlloc(Statement)" ) ) + return CE_Failure; + +/* -------------------------------------------------------------------- */ +/* Prepare the statement. */ +/* -------------------------------------------------------------------- */ + if( poSession->Failed( + OCIStmtPrepare( hStatement, poSession->hError, + (text *) pszSQLStatement, strlen(pszSQLStatement), + (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT ), + "OCIStmtPrepare" ) ) + return CE_Failure; + + return CE_None; +} + +/************************************************************************/ +/* BindObject() */ +/************************************************************************/ + +CPLErr OGROCIStatement::BindObject( const char *pszPlaceName, + void *pahObjects, OCIType *hTDO, + void **papIndicators ) + +{ + OCIBind *hBindOrd = NULL; + + if( poSession->Failed( + OCIBindByName( hStatement, &hBindOrd, poSession->hError, + (text *) pszPlaceName, (sb4) strlen(pszPlaceName), + (dvoid *) 0, (sb4) 0, SQLT_NTY, (dvoid *)0, + (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, + (ub4)OCI_DEFAULT), + "OCIBindByName()") ) + return CE_Failure; + + if( poSession->Failed( + OCIBindObject( hBindOrd, poSession->hError, hTDO, + (dvoid **) pahObjects, (ub4 *)0, + (dvoid **)papIndicators, (ub4 *)0), + "OCIBindObject()" ) ) + return CE_Failure; + + return CE_None; +} + +/************************************************************************/ +/* BindScalar() */ +/************************************************************************/ + +CPLErr OGROCIStatement::BindScalar( const char *pszPlaceName, + void *pData, int nDataLen, + int nSQLType, sb2 *paeInd ) + +{ + OCIBind *hBindOrd = NULL; + + if( poSession->Failed( + OCIBindByName( hStatement, &hBindOrd, poSession->hError, + (text *) pszPlaceName, (sb4) strlen(pszPlaceName), + (dvoid *) pData, (sb4) nDataLen, + (ub2) nSQLType, (dvoid *)paeInd, (ub2 *)0, + (ub2 *)0, (ub4)0, (ub4 *)0, + (ub4)OCI_DEFAULT), + "OCIBindByName()") ) + return CE_Failure; + else + return CE_None; +} + +/************************************************************************/ +/* Execute() */ +/************************************************************************/ + +CPLErr OGROCIStatement::Execute( const char *pszSQLStatement, + int nMode ) + +{ +/* -------------------------------------------------------------------- */ +/* Prepare the statement if it is being passed in. */ +/* -------------------------------------------------------------------- */ + if( pszSQLStatement != NULL ) + { + CPLErr eErr = Prepare( pszSQLStatement ); + if( eErr != CE_None ) + return eErr; + } + + if( hStatement == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No prepared statement in call to OGROCIStatement::Execute(NULL)" ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Determine if this is a SELECT statement. */ +/* -------------------------------------------------------------------- */ + ub2 nStmtType; + + if( poSession->Failed( + OCIAttrGet( hStatement, OCI_HTYPE_STMT, + &nStmtType, 0, OCI_ATTR_STMT_TYPE, poSession->hError ), + "OCIAttrGet(ATTR_STMT_TYPE)") ) + return CE_Failure; + + int bSelect = (nStmtType == OCI_STMT_SELECT); + +/* -------------------------------------------------------------------- */ +/* Work out some details about execution mode. */ +/* -------------------------------------------------------------------- */ + if( nMode == -1 ) + { + if( bSelect ) + nMode = OCI_DEFAULT; + else + nMode = OCI_COMMIT_ON_SUCCESS; + } + +/* -------------------------------------------------------------------- */ +/* Execute the statement. */ +/* -------------------------------------------------------------------- */ + if( poSession->Failed( + OCIStmtExecute( poSession->hSvcCtx, hStatement, + poSession->hError, (ub4)bSelect ? 0 : 1, (ub4)0, + (OCISnapshot *)NULL, (OCISnapshot *)NULL, nMode ), + pszCommandText ) ) + return CE_Failure; + + if( !bSelect ) + { + ub4 row_count; + if( poSession->Failed( + OCIAttrGet( hStatement, OCI_HTYPE_STMT, + &row_count, 0, OCI_ATTR_ROW_COUNT, poSession->hError ), + "OCIAttrGet(OCI_ATTR_ROW_COUNT)") ) + return CE_Failure; + nAffectedRows = row_count; + + return CE_None; + } + +/* -------------------------------------------------------------------- */ +/* Count the columns. */ +/* -------------------------------------------------------------------- */ + for( nRawColumnCount = 0; TRUE; nRawColumnCount++ ) + { + OCIParam *hParmDesc; + + if( OCIParamGet( hStatement, OCI_HTYPE_STMT, poSession->hError, + (dvoid**)&hParmDesc, + (ub4) nRawColumnCount+1 ) != OCI_SUCCESS ) + break; + } + + panFieldMap = (int *) CPLCalloc(sizeof(int),nRawColumnCount); + + papszCurColumn = (char **) CPLCalloc(sizeof(char*),nRawColumnCount+1); + panCurColumnInd = (sb2 *) CPLCalloc(sizeof(sb2),nRawColumnCount+1); + +/* ==================================================================== */ +/* Establish result column definitions, and setup parameter */ +/* defines. */ +/* ==================================================================== */ + poDefn = new OGRFeatureDefn( pszCommandText ); + poDefn->SetGeomType(wkbNone); + poDefn->Reference(); + + for( int iParm = 0; iParm < nRawColumnCount; iParm++ ) + { + OGRFieldDefn oField( "", OFTString ); + OCIParam *hParmDesc; + ub2 nOCIType; + ub4 nOCILen; + +/* -------------------------------------------------------------------- */ +/* Get parameter definition. */ +/* -------------------------------------------------------------------- */ + if( poSession->Failed( + OCIParamGet( hStatement, OCI_HTYPE_STMT, poSession->hError, + (dvoid**)&hParmDesc, (ub4) iParm+1 ), + "OCIParamGet") ) + return CE_Failure; + + if( poSession->GetParmInfo( hParmDesc, &oField, &nOCIType, &nOCILen ) + != CE_None ) + return CE_Failure; + + if( oField.GetType() == OFTBinary ) + { + /* We could probably generalize that, but at least it works in that */ + /* use case */ + if( EQUAL(oField.GetNameRef(), "DATA_DEFAULT") && nOCIType == SQLT_LNG ) + { + oField.SetType(OFTString); + } + else + { + panFieldMap[iParm] = -1; + continue; + } + } + + poDefn->AddFieldDefn( &oField ); + panFieldMap[iParm] = poDefn->GetFieldCount() - 1; + +/* -------------------------------------------------------------------- */ +/* Prepare a binding. */ +/* -------------------------------------------------------------------- */ + int nBufWidth = 256, nOGRField = panFieldMap[iParm]; + OCIDefine *hDefn = NULL; + + if( oField.GetWidth() > 0 ) + /* extra space needed for the decimal separator the string + terminator and the negative sign (Tamas Szekeres)*/ + nBufWidth = oField.GetWidth() + 3; + else if( oField.GetType() == OFTInteger ) + nBufWidth = 22; + else if( oField.GetType() == OFTReal ) + nBufWidth = 36; + else if ( oField.GetType() == OFTDateTime ) + nBufWidth = 40; + else if ( oField.GetType() == OFTDate ) + nBufWidth = 20; + + papszCurColumn[nOGRField] = (char *) CPLMalloc(nBufWidth+2); + CPLAssert( ((long) papszCurColumn[nOGRField]) % 2 == 0 ); + + if( poSession->Failed( + OCIDefineByPos( hStatement, &hDefn, poSession->hError, + iParm+1, + (ub1 *) papszCurColumn[nOGRField], nBufWidth, + SQLT_STR, panCurColumnInd + nOGRField, + NULL, NULL, OCI_DEFAULT ), + "OCIDefineByPos" ) ) + return CE_Failure; + } + + return CE_None; +} + +/************************************************************************/ +/* SimpleFetchRow() */ +/************************************************************************/ + +char **OGROCIStatement::SimpleFetchRow() + +{ + int nStatus, i; + + if( papszCurImage == NULL ) + { + papszCurImage = (char **) + CPLCalloc(sizeof(char *), nRawColumnCount+1 ); + } + + nStatus = OCIStmtFetch( hStatement, poSession->hError, 1, + OCI_FETCH_NEXT, OCI_DEFAULT ); + + if( nStatus == OCI_NO_DATA ) + return NULL; + else if( poSession->Failed( nStatus, "OCIStmtFetch" ) ) + return NULL; + + for( i = 0; papszCurColumn[i] != NULL; i++ ) + { + if( panCurColumnInd[i] == OCI_IND_NULL ) + papszCurImage[i] = NULL; + else + papszCurImage[i] = papszCurColumn[i]; + } + + return papszCurImage; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocistringbuf.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocistringbuf.cpp new file mode 100644 index 000000000..2f091aa55 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocistringbuf.cpp @@ -0,0 +1,161 @@ +/****************************************************************************** + * $Id: ogrocistringbuf.cpp 28429 2015-02-06 20:56:30Z rouault $ + * + * Project: Oracle Spatial Driver + * Purpose: Simple string buffer used to accumulate text of commands + * efficiently. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_oci.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrocistringbuf.cpp 28429 2015-02-06 20:56:30Z rouault $"); + +/************************************************************************/ +/* OGROCIStringBuf() */ +/************************************************************************/ + +OGROCIStringBuf::OGROCIStringBuf() +{ + nBufSize = 25; + pszString = (char *) CPLMalloc(nBufSize); + nLen = 0; + pszString[0] = '\0'; +} + +/************************************************************************/ +/* OGROCIStringBuf() */ +/************************************************************************/ + +OGROCIStringBuf::~OGROCIStringBuf() + +{ + CPLFree( pszString ); +} + +/************************************************************************/ +/* MakeRoomFor() */ +/************************************************************************/ + +void OGROCIStringBuf::MakeRoomFor( int nCharacters ) + +{ + UpdateEnd(); + + if( nLen + nCharacters > nBufSize - 2 ) + { + nBufSize = (int) ((nLen + nCharacters) * 1.3); + pszString = (char *) CPLRealloc(pszString,nBufSize); + } +} + +/************************************************************************/ +/* Append() */ +/************************************************************************/ + +void OGROCIStringBuf::Append( const char *pszNewText ) + +{ + int nNewLen = strlen(pszNewText); + + MakeRoomFor( nNewLen ); + strcat( pszString+nLen, pszNewText ); + nLen += nNewLen; +} + +/************************************************************************/ +/* Appendf() */ +/************************************************************************/ + +void OGROCIStringBuf::Appendf( int nMax, const char *pszFormat, ... ) + +{ + va_list args; + char szSimpleBuf[100]; + char *pszBuffer; + + if( nMax > (int) sizeof(szSimpleBuf-1) ) + pszBuffer = (char *) CPLMalloc(nMax+1); + else + pszBuffer = szSimpleBuf; + + va_start(args, pszFormat); + CPLvsnprintf(pszBuffer, nMax, pszFormat, args); + va_end(args); + + Append( pszBuffer ); + if( pszBuffer != szSimpleBuf ) + CPLFree( pszBuffer ); +} + +/************************************************************************/ +/* UpdateEnd() */ +/************************************************************************/ + +void OGROCIStringBuf::UpdateEnd() + +{ + nLen += strlen(pszString+nLen); +} + +/************************************************************************/ +/* StealString() */ +/************************************************************************/ + +char *OGROCIStringBuf::StealString() + +{ + char *pszStolenString = pszString; + + nBufSize = 100; + pszString = (char *) CPLMalloc(nBufSize); + nLen = 0; + + return pszStolenString; +} + +/************************************************************************/ +/* GetLast() */ +/************************************************************************/ + +char OGROCIStringBuf::GetLast() + +{ + if( nLen != 0 ) + return pszString[nLen-1]; + else + return '\0'; +} + +/************************************************************************/ +/* Clear() */ +/************************************************************************/ + +void OGROCIStringBuf::Clear() + +{ + pszString[0] = '\0'; + nLen = 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocistroke.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocistroke.cpp new file mode 100644 index 000000000..5d0e21d86 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocistroke.cpp @@ -0,0 +1,271 @@ +/****************************************************************************** + * + * Purpose: Oracle curve to linestring stroking (approximation). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ogr_oci.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ntfstroke.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* OGROCIArcCenterFromEdgePoints() */ +/* */ +/* Compute the center of an arc/circle from three edge points. */ +/************************************************************************/ + +static int +OGROCIArcCenterFromEdgePoints( double x_c0, double y_c0, + double x_c1, double y_c1, + double x_c2, double y_c2, + double *x_center, double *y_center ) + +{ + +/* -------------------------------------------------------------------- */ +/* Handle a degenerate case that occurs in OSNI products by */ +/* making some assumptions. If the first and third points are */ +/* the same assume they are intended to define a full circle, */ +/* and that the second point is on the opposite side of the */ +/* circle. */ +/* -------------------------------------------------------------------- */ + if( x_c0 == x_c2 && y_c0 == y_c2 ) + { + *x_center = (x_c0 + x_c1) * 0.5; + *y_center = (y_c0 + y_c1) * 0.5; + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Compute the inverse of the slopes connecting the first and */ +/* second points. Also compute the center point of the two */ +/* lines ... the point our crossing line will go through. */ +/* -------------------------------------------------------------------- */ + double m1, x1, y1; + + if( (y_c1 - y_c0) != 0.0 ) + m1 = (x_c0 - x_c1) / (y_c1 - y_c0); + else + m1 = 1e+10; + + x1 = (x_c0 + x_c1) * 0.5; + y1 = (y_c0 + y_c1) * 0.5; + +/* -------------------------------------------------------------------- */ +/* Compute the same for the second point compared to the third */ +/* point. */ +/* -------------------------------------------------------------------- */ + double m2, x2, y2; + + if( (y_c2 - y_c1) != 0.0 ) + m2 = (x_c1 - x_c2) / (y_c2 - y_c1); + else + m2 = 1e+10; + + x2 = (x_c1 + x_c2) * 0.5; + y2 = (y_c1 + y_c2) * 0.5; + +/* -------------------------------------------------------------------- */ +/* Turn these into the Ax+By+C = 0 form of the lines. */ +/* -------------------------------------------------------------------- */ + double a1, a2, b1, b2, c1, c2; + + a1 = m1; + a2 = m2; + + b1 = -1.0; + b2 = -1.0; + + c1 = (y1 - m1*x1); + c2 = (y2 - m2*x2); + +/* -------------------------------------------------------------------- */ +/* Compute the intersection of the two lines through the center */ +/* of the circle, using Kramers rule. */ +/* -------------------------------------------------------------------- */ + double det_inv; + + if( a1*b2 - a2*b1 == 0.0 ) + return FALSE; + + det_inv = 1 / (a1*b2 - a2*b1); + + *x_center = (b1*c2 - b2*c1) * det_inv; + *y_center = (a2*c1 - a1*c2) * det_inv; + + return TRUE; +} + +/************************************************************************/ +/* OGROCIStrokeArcToOGRGeometry_Angles() */ +/************************************************************************/ + +static int +OGROCIStrokeArcToOGRGeometry_Angles( double dfCenterX, double dfCenterY, + double dfRadius, + double dfStartAngle, double dfEndAngle, + double dfMaxAngleStepSizeDegrees, + OGRLineString *poLine ) + +{ + double dfArcX, dfArcY, dfSlice; + int iPoint, iAppendLocation, nVertexCount; + double dfEps = dfRadius / 100000.0; + + nVertexCount = (int) + ceil(fabs(dfEndAngle - dfStartAngle)/dfMaxAngleStepSizeDegrees) + 1; + nVertexCount = MAX(2,nVertexCount); + dfSlice = (dfEndAngle-dfStartAngle)/(nVertexCount-1); + + for( iPoint=0; iPoint < nVertexCount; iPoint++ ) + { + double dfAngle; + + dfAngle = (dfStartAngle + iPoint * dfSlice) * PI / 180.0; + + dfArcX = dfCenterX + cos(dfAngle) * dfRadius; + dfArcY = dfCenterY + sin(dfAngle) * dfRadius; + + if( iPoint == 0 ) + { + iAppendLocation = poLine->getNumPoints(); + + if( poLine->getNumPoints() > 0 + && fabs(poLine->getX(poLine->getNumPoints()-1)-dfArcX) < dfEps + && fabs(poLine->getY(poLine->getNumPoints()-1)-dfArcY) < dfEps) + { + poLine->setNumPoints( + poLine->getNumPoints() + nVertexCount - 1 ); + } + else + { + poLine->setNumPoints( + poLine->getNumPoints() + nVertexCount - 1 ); + poLine->setPoint( iAppendLocation++, dfArcX, dfArcY ); + } + } + else + poLine->setPoint( iAppendLocation++, dfArcX, dfArcY ); + } + + return TRUE; +} + + +/************************************************************************/ +/* OGROCIStrokeArcToOGRGeometry_Points() */ +/************************************************************************/ + +int +OGROCIStrokeArcToOGRGeometry_Points( double dfStartX, double dfStartY, + double dfAlongX, double dfAlongY, + double dfEndX, double dfEndY, + double dfMaxAngleStepSizeDegrees, + int bForceWholeCircle, + OGRLineString *poLine ) + +{ + double dfStartAngle, dfEndAngle, dfAlongAngle; + double dfCenterX, dfCenterY, dfRadius; + + if( !OGROCIArcCenterFromEdgePoints( dfStartX, dfStartY, + dfAlongX, dfAlongY, + dfEndX, dfEndY, + &dfCenterX, &dfCenterY ) ) + return FALSE; + + if( bForceWholeCircle || (dfStartX == dfEndX && dfStartY == dfEndY) ) + { + dfStartAngle = 0.0; + dfEndAngle = 360.0; + } + else + { + double dfDeltaX, dfDeltaY; + + dfDeltaX = dfStartX - dfCenterX; + dfDeltaY = dfStartY - dfCenterY; + dfStartAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / PI; + + dfDeltaX = dfAlongX - dfCenterX; + dfDeltaY = dfAlongY - dfCenterY; + dfAlongAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / PI; + + dfDeltaX = dfEndX - dfCenterX; + dfDeltaY = dfEndY - dfCenterY; + dfEndAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / PI; + + // Try positive (clockwise?) winding. + while( dfAlongAngle < dfStartAngle ) + dfAlongAngle += 360.0; + + while( dfEndAngle < dfAlongAngle ) + dfEndAngle += 360.0; + + // If that doesn't work out, then go anticlockwise. + if( dfEndAngle - dfStartAngle > 360.0 ) + { + while( dfAlongAngle > dfStartAngle ) + dfAlongAngle -= 360.0; + + while( dfEndAngle > dfAlongAngle ) + dfEndAngle -= 360.0; + } + } + + dfRadius = sqrt( (dfCenterX - dfStartX) * (dfCenterX - dfStartX) + + (dfCenterY - dfStartY) * (dfCenterY - dfStartY) ); + + int bResult; + + bResult = + OGROCIStrokeArcToOGRGeometry_Angles( dfCenterX, dfCenterY, + dfRadius, + dfStartAngle, dfEndAngle, + dfMaxAngleStepSizeDegrees, + poLine ); + +/* -------------------------------------------------------------------- */ +/* Force the points for arcs, to avoid odd rounding/math */ +/* issues. Perhaps we should do this for the start too, but */ +/* this is a bit tricky since it isn't obvious which point is */ +/* the start. */ +/* -------------------------------------------------------------------- */ + if( bResult && !bForceWholeCircle ) + { + poLine->setPoint( poLine->getNumPoints() - 1, + dfEndX, dfEndY ); + } + + return bResult; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocitablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocitablelayer.cpp new file mode 100644 index 000000000..241840fb2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrocitablelayer.cpp @@ -0,0 +1,2222 @@ +/****************************************************************************** + * $Id: ogrocitablelayer.cpp 28809 2015-03-28 17:10:07Z rouault $ + * + * Project: Oracle Spatial Driver + * Purpose: Implementation of the OGROCITableLayer class. This class provides + * layer semantics on a table, but utilizing alot of machinery from + * the OGROCILayer base class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_oci.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrocitablelayer.cpp 28809 2015-03-28 17:10:07Z rouault $"); + +static int nDiscarded = 0; +static int nHits = 0; + +#define HSI_UNKNOWN -2 + +/************************************************************************/ +/* OGROCITableLayer() */ +/************************************************************************/ + +OGROCITableLayer::OGROCITableLayer( OGROCIDataSource *poDSIn, + const char * pszTableName, OGRwkbGeometryType eGType, + int nSRIDIn, int bUpdate, int bNewLayerIn ) + +{ + poDS = poDSIn; + bExtentUpdated = false; + + pszQuery = NULL; + pszWHERE = CPLStrdup( "" ); + pszQueryStatement = NULL; + + bUpdateAccess = bUpdate; + bNewLayer = bNewLayerIn; + + iNextShapeId = 0; + iNextFIDToWrite = -1; + + bValidTable = FALSE; + if( bNewLayerIn ) + bHaveSpatialIndex = FALSE; + else + bHaveSpatialIndex = HSI_UNKNOWN; + + poFeatureDefn = ReadTableDefinition( pszTableName ); + if( eGType != wkbUnknown && poFeatureDefn->GetGeomFieldCount() > 0 ) + poFeatureDefn->GetGeomFieldDefn(0)->SetType(eGType); + SetDescription( poFeatureDefn->GetName() ); + + nSRID = nSRIDIn; + if( nSRID == -1 ) + nSRID = LookupTableSRID(); + + poSRS = poDSIn->FetchSRS( nSRID ); + if( poSRS != NULL ) + poSRS->Reference(); + + hOrdVARRAY = NULL; + hElemInfoVARRAY = NULL; + + poBoundStatement = NULL; + + nWriteCacheMax = 0; + nWriteCacheUsed = 0; + pasWriteGeoms = NULL; + papsWriteGeomMap = NULL; + pasWriteGeomInd = NULL; + papsWriteGeomIndMap = NULL; + + papWriteFields = NULL; + papaeWriteFieldInd = NULL; + + panWriteFIDs = NULL; + + ResetReading(); +} + +/************************************************************************/ +/* ~OGROCITableLayer() */ +/************************************************************************/ + +OGROCITableLayer::~OGROCITableLayer() + +{ + int i; + + SyncToDisk(); + + CPLFree( panWriteFIDs ); + if( papWriteFields != NULL ) + { + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + CPLFree( papWriteFields[i] ); + CPLFree( papaeWriteFieldInd[i] ); + } + } + + CPLFree( papWriteFields ); + CPLFree( papaeWriteFieldInd ); + + if( poBoundStatement != NULL ) + delete poBoundStatement; + + CPLFree( pasWriteGeomInd ); + CPLFree( papsWriteGeomIndMap ); + + CPLFree( papsWriteGeomMap ); + CPLFree( pasWriteGeoms ); + + + CPLFree( pszQuery ); + CPLFree( pszWHERE ); + + if( poSRS != NULL && poSRS->Dereference() == 0 ) + delete poSRS; +} + +/************************************************************************/ +/* ReadTableDefinition() */ +/* */ +/* Build a schema from the named table. Done by querying the */ +/* catalog. */ +/************************************************************************/ + +OGRFeatureDefn *OGROCITableLayer::ReadTableDefinition( const char * pszTable ) + +{ + OGROCISession *poSession = poDS->GetSession(); + sword nStatus; + + CPLString osUnquotedTableName; + CPLString osQuotedTableName; + +/* -------------------------------------------------------------------- */ +/* Split out the owner if available. */ +/* -------------------------------------------------------------------- */ + if( strstr(pszTable,".") != NULL ) + { + osTableName = strstr(pszTable,".") + 1; + osOwner.assign( pszTable, strlen(pszTable)-osTableName.size() - 1 ); + osUnquotedTableName.Printf( "%s.%s", osOwner.c_str(), osTableName.c_str() ); + osQuotedTableName.Printf( "\"%s\".\"%s\"", osOwner.c_str(), osTableName.c_str() ); + } + else + { + osTableName = pszTable; + osOwner = ""; + osUnquotedTableName.Printf( "%s", pszTable ); + osQuotedTableName.Printf( "\"%s\"", pszTable ); + } + + OGRFeatureDefn *poDefn = new OGRFeatureDefn( osUnquotedTableName.c_str() ); + + poDefn->Reference(); + +/* -------------------------------------------------------------------- */ +/* Do a DescribeAll on the table. */ +/* -------------------------------------------------------------------- */ + OCIParam *hAttrParam = NULL; + OCIParam *hAttrList = NULL; + + // Table name unquoted + + nStatus = + OCIDescribeAny( poSession->hSvcCtx, poSession->hError, + (dvoid *) osUnquotedTableName.c_str(), + osUnquotedTableName.length(), OCI_OTYPE_NAME, + OCI_DEFAULT, OCI_PTYPE_TABLE, poSession->hDescribe ); + + if( poSession->Failed( nStatus, "OCIDescribeAny" ) ) + { + CPLErrorReset(); + + // View name unquoted + + nStatus = + OCIDescribeAny(poSession->hSvcCtx, poSession->hError, + (dvoid *) osQuotedTableName.c_str(), + osQuotedTableName.length(), OCI_OTYPE_NAME, + OCI_DEFAULT, OCI_PTYPE_VIEW, poSession->hDescribe ); + + if( poSession->Failed( nStatus, "OCIDescribeAny" ) ) + { + CPLErrorReset(); + + // Table name quoted + + nStatus = + OCIDescribeAny( poSession->hSvcCtx, poSession->hError, + (dvoid *) osQuotedTableName.c_str(), + osQuotedTableName.length(), OCI_OTYPE_NAME, + OCI_DEFAULT, OCI_PTYPE_TABLE, poSession->hDescribe ); + + if( poSession->Failed( nStatus, "OCIDescribeAny" ) ) + { + CPLErrorReset(); + + // View name quoted + + nStatus = + OCIDescribeAny(poSession->hSvcCtx, poSession->hError, + (dvoid *) osQuotedTableName.c_str(), + osQuotedTableName.length(), OCI_OTYPE_NAME, + OCI_DEFAULT, OCI_PTYPE_VIEW, poSession->hDescribe ); + + if( poSession->Failed( nStatus, "OCIDescribeAny" ) ) + return poDefn; + } + } + } + + if( poSession->Failed( + OCIAttrGet( poSession->hDescribe, OCI_HTYPE_DESCRIBE, + &hAttrParam, 0, OCI_ATTR_PARAM, poSession->hError ), + "OCIAttrGet(ATTR_PARAM)") ) + return poDefn; + + if( poSession->Failed( + OCIAttrGet( hAttrParam, OCI_DTYPE_PARAM, &hAttrList, 0, + OCI_ATTR_LIST_COLUMNS, poSession->hError ), + "OCIAttrGet(ATTR_LIST_COLUMNS)" ) ) + return poDefn; + +/* -------------------------------------------------------------------- */ +/* What is the name of the column to use as FID? This defaults */ +/* to OGR_FID but we allow it to be overridden by a config */ +/* variable. Ideally we would identify a column that is a */ +/* primary key and use that, but I'm not yet sure how to */ +/* accomplish that. */ +/* -------------------------------------------------------------------- */ + const char *pszExpectedFIDName = + CPLGetConfigOption( "OCI_FID", "OGR_FID" ); + int bGeomFieldNullable = FALSE; + +/* -------------------------------------------------------------------- */ +/* Parse the returned table information. */ +/* -------------------------------------------------------------------- */ + for( int iRawFld = 0; TRUE; iRawFld++ ) + { + OGRFieldDefn oField( "", OFTString); + OCIParam *hParmDesc; + ub2 nOCIType; + ub4 nOCILen; + sword nStatus; + + nStatus = OCIParamGet( hAttrList, OCI_DTYPE_PARAM, + poSession->hError, (dvoid**)&hParmDesc, + (ub4) iRawFld+1 ); + if( nStatus != OCI_SUCCESS ) + break; + + if( poSession->GetParmInfo( hParmDesc, &oField, &nOCIType, &nOCILen ) + != CE_None ) + return poDefn; + + if( oField.GetType() == OFTBinary ) + { + if( nOCIType == 108 && pszGeomName == NULL ) + { + CPLFree( pszGeomName ); + pszGeomName = CPLStrdup( oField.GetNameRef() ); + iGeomColumn = iRawFld; + bGeomFieldNullable = oField.IsNullable(); + } + continue; + } + + if( EQUAL(oField.GetNameRef(),pszExpectedFIDName) + && (oField.GetType() == OFTInteger || + oField.GetType() == OFTInteger64) ) + { + pszFIDName = CPLStrdup(oField.GetNameRef()); + continue; + } + + poDefn->AddFieldDefn( &oField ); + } + + CPLString osSQL; + osSQL.Printf("SELECT COLUMN_NAME, DATA_DEFAULT FROM user_tab_columns WHERE DATA_DEFAULT IS NOT NULL AND TABLE_NAME = '%s'", + CPLString(pszTable).toupper().c_str()); + OGRLayer* poSQLLyr = poDS->ExecuteSQL(osSQL, NULL, NULL); + if( poSQLLyr != NULL ) + { + OGRFeature* poFeature; + while( (poFeature = poSQLLyr->GetNextFeature()) != NULL ) + { + const char* pszColName = poFeature->GetFieldAsString(0); + const char* pszDefault = poFeature->GetFieldAsString(1); + int nIdx = poDefn->GetFieldIndex(pszColName); + if( nIdx >= 0 ) + poDefn->GetFieldDefn(nIdx)->SetDefault(pszDefault); + delete poFeature; + } + poDS->ReleaseResultSet(poSQLLyr); + } + + if( EQUAL(pszExpectedFIDName, "OGR_FID") && pszFIDName ) + { + for(int i=0;iGetFieldCount();i++) + { + // This is presumably a Integer since we always create Integer64 with a + // defined precision + if( poDefn->GetFieldDefn(i)->GetType() == OFTInteger64 && + poDefn->GetFieldDefn(i)->GetWidth() == 0 ) + { + poDefn->GetFieldDefn(i)->SetType(OFTInteger); + } + } + } + + /* -------------------------------------------------------------------- */ + /* Identify Geometry dimension */ + /* -------------------------------------------------------------------- */ + + if( pszGeomName != NULL && strlen(pszGeomName) > 0 ) + { + OGROCIStringBuf oDimCmd; + OGROCIStatement oDimStatement( poSession ); + char **papszResult; + int iDim = -1; + + oDimCmd.Append( "SELECT COUNT(*) FROM ALL_SDO_GEOM_METADATA u," ); + oDimCmd.Append( " TABLE(u.diminfo) t" ); + oDimCmd.Append( " WHERE u.table_name = '" ); + oDimCmd.Append( osTableName ); + oDimCmd.Append( "' AND u.column_name = '" ); + oDimCmd.Append( pszGeomName ); + oDimCmd.Append( "'" ); + + oDimStatement.Execute( oDimCmd.GetString() ); + + papszResult = oDimStatement.SimpleFetchRow(); + + if( CSLCount(papszResult) < 1 ) + { + OGROCIStringBuf oDimCmd2; + OGROCIStatement oDimStatement2( poSession ); + char **papszResult2; + + CPLErrorReset(); + + oDimCmd2.Appendf( 1024, + "select m.sdo_index_dims\n" + "from all_sdo_index_metadata m, all_sdo_index_info i\n" + "where i.index_name = m.sdo_index_name\n" + " and i.sdo_index_owner = m.sdo_index_owner\n" + " and i.table_name = upper('%s')", + osTableName.c_str() ); + + oDimStatement2.Execute( oDimCmd2.GetString() ); + + papszResult2 = oDimStatement2.SimpleFetchRow(); + + if( CSLCount( papszResult2 ) > 0 ) + { + iDim = atoi( papszResult2[0] ); + } + else + { + // we want to clear any errors to avoid confusing the application. + CPLErrorReset(); + } + } + else + { + iDim = atoi( papszResult[0] ); + } + + if( iDim > 0 ) + { + SetDimension( iDim ); + } + else + { + CPLDebug( "OCI", "get dim based of existing data or index failed." ); + } + + { + OGROCIStringBuf oDimCmd2; + OGROCIStatement oDimStatement2( poSession ); + char **papszResult2; + + CPLErrorReset(); + oDimCmd2.Appendf( 1024, + "select m.SDO_LAYER_GTYPE " + "from all_sdo_index_metadata m, all_sdo_index_info i " + "where i.index_name = m.sdo_index_name " + "and i.sdo_index_owner = m.sdo_index_owner " + "and i.table_name = upper('%s')", + osTableName.c_str() ); + + oDimStatement2.Execute( oDimCmd2.GetString() ); + + papszResult2 = oDimStatement2.SimpleFetchRow(); + + if( CSLCount( papszResult2 ) > 0 ) + { + const char* pszLayerGType = papszResult2[0]; + OGRwkbGeometryType eGeomType = wkbUnknown; + if( EQUAL(pszLayerGType, "POINT") ) + eGeomType = wkbPoint; + else if( EQUAL(pszLayerGType, "LINE") ) + eGeomType = wkbLineString; + else if( EQUAL(pszLayerGType, "POLYGON") ) + eGeomType = wkbPolygon; + else if( EQUAL(pszLayerGType, "MULTIPOINT") ) + eGeomType = wkbMultiPoint; + else if( EQUAL(pszLayerGType, "MULTILINE") ) + eGeomType = wkbMultiLineString; + else if( EQUAL(pszLayerGType, "MULTIPOLYGON") ) + eGeomType = wkbMultiPolygon; + else if( !EQUAL(pszLayerGType, "COLLECTION") ) + CPLDebug("OCI", "LAYER_GTYPE = %s", pszLayerGType ); + if( iDim == 3 ) + eGeomType = wkbSetZ(eGeomType); + poDefn->GetGeomFieldDefn(0)->SetType( eGeomType ); + poDefn->GetGeomFieldDefn(0)->SetNullable( bGeomFieldNullable ); + } + else + { + // we want to clear any errors to avoid confusing the application. + CPLErrorReset(); + } + } + } + else + { + poDefn->SetGeomType(wkbNone); + } + + bValidTable = TRUE; + + return poDefn; +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGROCITableLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) + +{ + if( !InstallFilter( poGeomIn ) ) + return; + + BuildWhere(); + + ResetReading(); +} + +/************************************************************************/ +/* TestForSpatialIndex() */ +/************************************************************************/ + +void OGROCITableLayer::TestForSpatialIndex( const char *pszSpatWHERE ) + +{ + OGROCIStringBuf oTestCmd; + OGROCIStatement oTestStatement( poDS->GetSession() ); + + oTestCmd.Append( "SELECT COUNT(*) FROM " ); + oTestCmd.Append( poFeatureDefn->GetName() ); + oTestCmd.Append( pszSpatWHERE ); + + if( oTestStatement.Execute( oTestCmd.GetString() ) != CE_None ) + bHaveSpatialIndex = FALSE; + else + bHaveSpatialIndex = TRUE; +} + +/************************************************************************/ +/* BuildWhere() */ +/* */ +/* Build the WHERE statement appropriate to the current set of */ +/* criteria (spatial and attribute queries). */ +/************************************************************************/ + +void OGROCITableLayer::BuildWhere() + +{ + OGROCIStringBuf oWHERE; + + CPLFree( pszWHERE ); + pszWHERE = NULL; + + if( m_poFilterGeom != NULL && bHaveSpatialIndex ) + { + OGREnvelope sEnvelope; + + m_poFilterGeom->getEnvelope( &sEnvelope ); + + oWHERE.Append( " WHERE sdo_filter(" ); + oWHERE.Append( pszGeomName ); + oWHERE.Append( ", MDSYS.SDO_GEOMETRY(2003," ); + if( nSRID == -1 ) + oWHERE.Append( "NULL" ); + else + oWHERE.Appendf( 15, "%d", nSRID ); + oWHERE.Append( ",NULL," ); + oWHERE.Append( "MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1)," ); + oWHERE.Append( "MDSYS.SDO_ORDINATE_ARRAY(" ); + oWHERE.Appendf( 600, + "%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g,%.16g", + sEnvelope.MinX, sEnvelope.MinY, + sEnvelope.MaxX, sEnvelope.MinY, + sEnvelope.MaxX, sEnvelope.MaxY, + sEnvelope.MinX, sEnvelope.MaxY, + sEnvelope.MinX, sEnvelope.MinY); + oWHERE.Append( ")), 'querytype=window') = 'TRUE' " ); + } + + if( bHaveSpatialIndex == HSI_UNKNOWN ) + { + TestForSpatialIndex( oWHERE.GetString() ); + if( !bHaveSpatialIndex ) + oWHERE.Clear(); + } + + if( pszQuery != NULL ) + { + if( oWHERE.GetLast() == '\0' ) + oWHERE.Append( "WHERE " ); + else + oWHERE.Append( "AND " ); + + oWHERE.Append( pszQuery ); + } + + pszWHERE = oWHERE.StealString(); +} + +/************************************************************************/ +/* BuildFullQueryStatement() */ +/************************************************************************/ + +void OGROCITableLayer::BuildFullQueryStatement() + +{ + if( pszQueryStatement != NULL ) + { + CPLFree( pszQueryStatement ); + pszQueryStatement = NULL; + } + + OGROCIStringBuf oCmd; + char *pszFields = BuildFields(); + + oCmd.Append( "SELECT " ); + oCmd.Append( pszFields ); + oCmd.Append( " FROM " ); + oCmd.Append( poFeatureDefn->GetName() ); + oCmd.Append( " " ); + oCmd.Append( pszWHERE ); + + pszQueryStatement = oCmd.StealString(); + + CPLFree( pszFields ); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGROCITableLayer::GetFeature( GIntBig nFeatureId ) + +{ + +/* -------------------------------------------------------------------- */ +/* If we don't have an FID column scan for the desired feature. */ +/* -------------------------------------------------------------------- */ + if( pszFIDName == NULL ) + return OGROCILayer::GetFeature( nFeatureId ); + +/* -------------------------------------------------------------------- */ +/* Clear any existing query. */ +/* -------------------------------------------------------------------- */ + ResetReading(); + +/* -------------------------------------------------------------------- */ +/* Build query for this specific feature. */ +/* -------------------------------------------------------------------- */ + OGROCIStringBuf oCmd; + char *pszFields = BuildFields(); + + oCmd.Append( "SELECT " ); + oCmd.Append( pszFields ); + oCmd.Append( " FROM " ); + oCmd.Append( poFeatureDefn->GetName() ); + oCmd.Append( " " ); + oCmd.Appendf( 50+strlen(pszFIDName), + " WHERE \"%s\" = " CPL_FRMT_GIB " ", + pszFIDName, nFeatureId ); + +/* -------------------------------------------------------------------- */ +/* Execute the statement. */ +/* -------------------------------------------------------------------- */ + if( !ExecuteQuery( oCmd.GetString() ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get the feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + + if( poFeature != NULL && poFeature->GetGeometryRef() != NULL ) + poFeature->GetGeometryRef()->assignSpatialReference( poSRS ); + +/* -------------------------------------------------------------------- */ +/* Cleanup the statement. */ +/* -------------------------------------------------------------------- */ + ResetReading(); + +/* -------------------------------------------------------------------- */ +/* verify the FID. */ +/* -------------------------------------------------------------------- */ + if( poFeature != NULL && poFeature->GetFID() != nFeatureId ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OGROCITableLayer::GetFeature(" CPL_FRMT_GIB ") ... query returned feature " CPL_FRMT_GIB " instead!", + nFeatureId, poFeature->GetFID() ); + delete poFeature; + return NULL; + } + else + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/* */ +/* We override the next feature method because we know that we */ +/* implement the attribute query within the statement and so we */ +/* don't have to test here. Eventually the spatial query will */ +/* be fully tested within the statement as well. */ +/************************************************************************/ + +OGRFeature *OGROCITableLayer::GetNextFeature() + +{ + + for( ; TRUE; ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + { + CPLDebug( "OCI", "Query complete, got %d hits, and %d discards.", + nHits, nDiscarded ); + nHits = 0; + nDiscarded = 0; + return NULL; + } + + if( m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + { + nHits++; + if( poFeature->GetGeometryRef() != NULL ) + poFeature->GetGeometryRef()->assignSpatialReference( poSRS ); + return poFeature; + } + + if( m_poFilterGeom != NULL ) + nDiscarded++; + + delete poFeature; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGROCITableLayer::ResetReading() + +{ + nHits = 0; + nDiscarded = 0; + + FlushPendingFeatures(); + + BuildFullQueryStatement(); + + OGROCILayer::ResetReading(); +} + +/************************************************************************/ +/* BuildFields() */ +/* */ +/* Build list of fields to fetch, performing any required */ +/* transformations (such as on geometry). */ +/************************************************************************/ + +char *OGROCITableLayer::BuildFields() + +{ + int i; + OGROCIStringBuf oFldList; + + if( pszGeomName ) + { + oFldList.Append( "\"" ); + oFldList.Append( pszGeomName ); + oFldList.Append( "\"" ); + iGeomColumn = 0; + } + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + + if( oFldList.GetLast() != '\0' ) + oFldList.Append( "," ); + + oFldList.Append( "\"" ); + oFldList.Append( pszName ); + oFldList.Append( "\"" ); + } + + if( pszFIDName != NULL ) + { + iFIDColumn = poFeatureDefn->GetFieldCount(); + oFldList.Append( ",\"" ); + oFldList.Append( pszFIDName ); + oFldList.Append( "\"" ); + } + + return oFldList.StealString(); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGROCITableLayer::SetAttributeFilter( const char *pszQuery ) + +{ + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + + if( (pszQuery == NULL && this->pszQuery == NULL) + || (pszQuery != NULL && this->pszQuery != NULL + && strcmp(pszQuery,this->pszQuery) == 0) ) + return OGRERR_NONE; + + CPLFree( this->pszQuery ); + + if( pszQuery == NULL ) + this->pszQuery = NULL; + else + this->pszQuery = CPLStrdup( pszQuery ); + + BuildWhere(); + + ResetReading(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ISetFeature() */ +/* */ +/* We implement SetFeature() by deleting the existing row (if */ +/* it exists), and then using CreateFeature() to write it out */ +/* tot he table normally. CreateFeature() will preserve the */ +/* existing FID if possible. */ +/************************************************************************/ + +OGRErr OGROCITableLayer::ISetFeature( OGRFeature *poFeature ) + +{ +/* -------------------------------------------------------------------- */ +/* Do some validation. */ +/* -------------------------------------------------------------------- */ + if( pszFIDName == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OGROCITableLayer::ISetFeature(" CPL_FRMT_GIB ") failed because there is " + "no apparent FID column on table %s.", + poFeature->GetFID(), + poFeatureDefn->GetName() ); + + return OGRERR_FAILURE; + } + + if( poFeature->GetFID() == OGRNullFID ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OGROCITableLayer::ISetFeature(" CPL_FRMT_GIB ") failed because the feature " + "has no FID!", poFeature->GetFID() ); + + return OGRERR_FAILURE; + } + + OGRErr eErr = DeleteFeature(poFeature->GetFID()); + if( eErr != OGRERR_NONE ) + return eErr; + + return CreateFeature( poFeature ); +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGROCITableLayer::DeleteFeature( GIntBig nFID ) + +{ +/* -------------------------------------------------------------------- */ +/* Do some validation. */ +/* -------------------------------------------------------------------- */ + if( pszFIDName == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OGROCITableLayer::DeleteFeature(" CPL_FRMT_GIB ") failed because there is " + "no apparent FID column on table %s.", + nFID, + poFeatureDefn->GetName() ); + + return OGRERR_FAILURE; + } + + if( nFID == OGRNullFID ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OGROCITableLayer::DeleteFeature(" CPL_FRMT_GIB ") failed for Null FID", + nFID ); + + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Prepare the delete command, and execute. We don't check the */ +/* error result of the execute, since attempting to Set a */ +/* non-existing feature may be OK. */ +/* -------------------------------------------------------------------- */ + OGROCIStringBuf oCmdText; + OGROCIStatement oCmdStatement( poDS->GetSession() ); + + oCmdText.Appendf( strlen(poFeatureDefn->GetName())+strlen(pszFIDName)+100, + "DELETE FROM %s WHERE \"%s\" = " CPL_FRMT_GIB, + poFeatureDefn->GetName(), + pszFIDName, + nFID ); + + if( oCmdStatement.Execute( oCmdText.GetString() ) == CE_None ) + return (oCmdStatement.GetAffectedRows() > 0) ? OGRERR_NONE : OGRERR_NON_EXISTING_FEATURE; + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGROCITableLayer::ICreateFeature( OGRFeature *poFeature ) + +{ +/* -------------------------------------------------------------------- */ +/* Add extents of this geometry to the existing layer extents. */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetGeometryRef() != NULL ) + { + OGREnvelope sThisExtent; + + poFeature->GetGeometryRef()->getEnvelope( &sThisExtent ); + + if( !sExtent.Contains( sThisExtent ) ) + { + sExtent.Merge( sThisExtent ); + bExtentUpdated = true; + } + } + +/* -------------------------------------------------------------------- */ +/* Do the actual creation. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchBoolean( papszOptions, "MULTI_LOAD", true ) ) + return BoundCreateFeature( poFeature ); + else + return UnboundCreateFeature( poFeature ); +} + +/************************************************************************/ +/* UnboundCreateFeature() */ +/************************************************************************/ + +OGRErr OGROCITableLayer::UnboundCreateFeature( OGRFeature *poFeature ) + +{ + OGROCISession *poSession = poDS->GetSession(); + char *pszCommand; + int i, bNeedComma = FALSE; + unsigned int nCommandBufSize;; + +/* -------------------------------------------------------------------- */ +/* Prepare SQL statement buffer. */ +/* -------------------------------------------------------------------- */ + nCommandBufSize = 2000; + pszCommand = (char *) CPLMalloc(nCommandBufSize); + +/* -------------------------------------------------------------------- */ +/* Form the INSERT command. */ +/* -------------------------------------------------------------------- */ + sprintf( pszCommand, "INSERT INTO \"%s\"(\"", poFeatureDefn->GetName() ); + + if( poFeature->GetGeometryRef() != NULL ) + { + bNeedComma = TRUE; + strcat( pszCommand, pszGeomName ); + } + + if( pszFIDName != NULL ) + { + if( bNeedComma ) + strcat( pszCommand, "\",\"" ); + + strcat( pszCommand, pszFIDName ); + bNeedComma = TRUE; + } + + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if( !poFeature->IsFieldSet( i ) ) + continue; + + if( !bNeedComma ) + bNeedComma = TRUE; + else + strcat( pszCommand, "\",\"" ); + + sprintf( pszCommand + strlen(pszCommand), "%s", + poFeatureDefn->GetFieldDefn(i)->GetNameRef() ); + } + + strcat( pszCommand, "\") VALUES (" ); + + CPLAssert( strlen(pszCommand) < nCommandBufSize ); + +/* -------------------------------------------------------------------- */ +/* Set the geometry */ +/* -------------------------------------------------------------------- */ + bNeedComma = poFeature->GetGeometryRef() != NULL; + if( poFeature->GetGeometryRef() != NULL) + { + OGRGeometry *poGeometry = poFeature->GetGeometryRef(); + char szSDO_GEOMETRY[512]; + char szSRID[128]; + + if( nSRID == -1 ) + strcpy( szSRID, "NULL" ); + else + sprintf( szSRID, "%d", nSRID ); + + if( wkbFlatten(poGeometry->getGeometryType()) == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + if( nDimension == 2 ) + CPLsprintf( szSDO_GEOMETRY, + "%s(%d,%s,MDSYS.SDO_POINT_TYPE(%.16g,%.16g,0),NULL,NULL)", + SDO_GEOMETRY, 2001, szSRID, + poPoint->getX(), poPoint->getY() ); + else + CPLsprintf( szSDO_GEOMETRY, + "%s(%d,%s,MDSYS.SDO_POINT_TYPE(%.16g,%.16g,%.16g),NULL,NULL)", + SDO_GEOMETRY, 3001, szSRID, + poPoint->getX(), poPoint->getY(), poPoint->getZ() ); + } + else + { + int nGType; + + if( TranslateToSDOGeometry( poFeature->GetGeometryRef(), &nGType ) + == OGRERR_NONE ) + sprintf( szSDO_GEOMETRY, + "%s(%d,%s,NULL,:elem_info,:ordinates)", + SDO_GEOMETRY, nGType, szSRID ); + else + sprintf( szSDO_GEOMETRY, "NULL" ); + } + + if( strlen(pszCommand) + strlen(szSDO_GEOMETRY) + > nCommandBufSize - 50 ) + { + nCommandBufSize = + strlen(pszCommand) + strlen(szSDO_GEOMETRY) + 10000; + pszCommand = (char *) CPLRealloc(pszCommand, nCommandBufSize ); + } + + strcat( pszCommand, szSDO_GEOMETRY ); + } + +/* -------------------------------------------------------------------- */ +/* Set the FID. */ +/* -------------------------------------------------------------------- */ + int nOffset = strlen(pszCommand); + + if( pszFIDName != NULL ) + { + GIntBig nFID; + + if( bNeedComma ) + strcat( pszCommand+nOffset, ", " ); + bNeedComma = TRUE; + + nOffset += strlen(pszCommand+nOffset); + + nFID = poFeature->GetFID(); + if( nFID == OGRNullFID ) + { + if( iNextFIDToWrite < 0 ) + { + iNextFIDToWrite = GetMaxFID() + 1; + } + nFID = iNextFIDToWrite++; + poFeature->SetFID( nFID ); + } + sprintf( pszCommand+nOffset, CPL_FRMT_GIB, nFID ); + } + +/* -------------------------------------------------------------------- */ +/* Set the other fields. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if( !poFeature->IsFieldSet( i ) ) + continue; + + OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(i); + const char *pszStrValue = poFeature->GetFieldAsString(i); + + if( bNeedComma ) + strcat( pszCommand+nOffset, ", " ); + else + bNeedComma = TRUE; + + if( strlen(pszStrValue) + strlen(pszCommand+nOffset) + nOffset + > nCommandBufSize-50 ) + { + nCommandBufSize = strlen(pszCommand) + strlen(pszStrValue) + 10000; + pszCommand = (char *) CPLRealloc(pszCommand, nCommandBufSize ); + } + + if( poFldDefn->GetType() == OFTInteger + || poFldDefn->GetType() == OFTInteger64 + || poFldDefn->GetType() == OFTReal ) + { + if( poFldDefn->GetWidth() > 0 && bPreservePrecision + && (int) strlen(pszStrValue) > poFldDefn->GetWidth() ) + { + strcat( pszCommand+nOffset, "NULL" ); + ReportTruncation( poFldDefn ); + } + else + strcat( pszCommand+nOffset, pszStrValue ); + } + else + { + int iChar; + + /* We need to quote and escape string fields. */ + strcat( pszCommand+nOffset, "'" ); + + nOffset += strlen(pszCommand+nOffset); + + for( iChar = 0; pszStrValue[iChar] != '\0'; iChar++ ) + { + if( poFldDefn->GetWidth() != 0 && bPreservePrecision + && iChar >= poFldDefn->GetWidth() ) + { + ReportTruncation( poFldDefn ); + break; + } + + if( pszStrValue[iChar] == '\'' ) + { + pszCommand[nOffset++] = '\''; + pszCommand[nOffset++] = pszStrValue[iChar]; + } + else + pszCommand[nOffset++] = pszStrValue[iChar]; + } + pszCommand[nOffset] = '\0'; + + strcat( pszCommand+nOffset, "'" ); + } + nOffset += strlen(pszCommand+nOffset); + } + + strcat( pszCommand+nOffset, ")" ); + +/* -------------------------------------------------------------------- */ +/* Prepare statement. */ +/* -------------------------------------------------------------------- */ + OGROCIStatement oInsert( poSession ); + int bHaveOrdinates = strstr(pszCommand,":ordinates") != NULL; + int bHaveElemInfo = strstr(pszCommand,":elem_info") != NULL; + + if( oInsert.Prepare( pszCommand ) != CE_None ) + { + CPLFree( pszCommand ); + return OGRERR_FAILURE; + } + + CPLFree( pszCommand ); + +/* -------------------------------------------------------------------- */ +/* Bind and translate the elem_info if we have some. */ +/* -------------------------------------------------------------------- */ + if( bHaveElemInfo ) + { + OCIBind *hBindOrd = NULL; + int i; + OCINumber oci_number; + + // Create or clear VARRAY + if( hElemInfoVARRAY == NULL ) + { + if( poSession->Failed( + OCIObjectNew( poSession->hEnv, poSession->hError, + poSession->hSvcCtx, OCI_TYPECODE_VARRAY, + poSession->hElemInfoTDO, (dvoid *)NULL, + OCI_DURATION_SESSION, + FALSE, (dvoid **)&hElemInfoVARRAY), + "OCIObjectNew(hElemInfoVARRAY)") ) + return OGRERR_FAILURE; + } + else + { + sb4 nOldCount; + + OCICollSize( poSession->hEnv, poSession->hError, + hElemInfoVARRAY, &nOldCount ); + OCICollTrim( poSession->hEnv, poSession->hError, + nOldCount, hElemInfoVARRAY ); + } + + // Prepare the VARRAY of ordinate values. + for (i = 0; i < nElemInfoCount; i++) + { + if( poSession->Failed( + OCINumberFromInt( poSession->hError, + (dvoid *) (panElemInfo + i), + (uword)sizeof(int), + OCI_NUMBER_SIGNED, + &oci_number), + "OCINumberFromInt") ) + return OGRERR_FAILURE; + + if( poSession->Failed( + OCICollAppend( poSession->hEnv, poSession->hError, + (dvoid *) &oci_number, + (dvoid *)0, hElemInfoVARRAY), + "OCICollAppend") ) + return OGRERR_FAILURE; + } + + // Do the binding. + if( poSession->Failed( + OCIBindByName( oInsert.GetStatement(), &hBindOrd, + poSession->hError, + (text *) ":elem_info", (sb4) -1, (dvoid *) 0, + (sb4) 0, SQLT_NTY, (dvoid *)0, (ub2 *)0, + (ub2 *)0, (ub4)0, (ub4 *)0, + (ub4)OCI_DEFAULT), + "OCIBindByName(:elem_info)") ) + return OGRERR_FAILURE; + + if( poSession->Failed( + OCIBindObject( hBindOrd, poSession->hError, + poSession->hElemInfoTDO, + (dvoid **)&hElemInfoVARRAY, (ub4 *)0, + (dvoid **)0, (ub4 *)0), + "OCIBindObject(:elem_info)" ) ) + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Bind and translate the ordinates if we have some. */ +/* -------------------------------------------------------------------- */ + if( bHaveOrdinates ) + { + OCIBind *hBindOrd = NULL; + int i; + OCINumber oci_number; + + // Create or clear VARRAY + if( hOrdVARRAY == NULL ) + { + if( poSession->Failed( + OCIObjectNew( poSession->hEnv, poSession->hError, + poSession->hSvcCtx, OCI_TYPECODE_VARRAY, + poSession->hOrdinatesTDO, (dvoid *)NULL, + OCI_DURATION_SESSION, + FALSE, (dvoid **)&hOrdVARRAY), + "OCIObjectNew(hOrdVARRAY)") ) + return OGRERR_FAILURE; + } + else + { + sb4 nOldCount; + + OCICollSize( poSession->hEnv, poSession->hError, + hOrdVARRAY, &nOldCount ); + OCICollTrim( poSession->hEnv, poSession->hError, + nOldCount, hOrdVARRAY ); + } + + // Prepare the VARRAY of ordinate values. + for (i = 0; i < nOrdinalCount; i++) + { + if( poSession->Failed( + OCINumberFromReal( poSession->hError, + (dvoid *) (padfOrdinals + i), + (uword)sizeof(double), + &oci_number), + "OCINumberFromReal") ) + return OGRERR_FAILURE; + + if( poSession->Failed( + OCICollAppend( poSession->hEnv, poSession->hError, + (dvoid *) &oci_number, + (dvoid *)0, hOrdVARRAY), + "OCICollAppend") ) + return OGRERR_FAILURE; + } + + // Do the binding. + if( poSession->Failed( + OCIBindByName( oInsert.GetStatement(), &hBindOrd, + poSession->hError, + (text *) ":ordinates", (sb4) -1, (dvoid *) 0, + (sb4) 0, SQLT_NTY, (dvoid *)0, (ub2 *)0, + (ub2 *)0, (ub4)0, (ub4 *)0, + (ub4)OCI_DEFAULT), + "OCIBindByName(:ordinates)") ) + return OGRERR_FAILURE; + + if( poSession->Failed( + OCIBindObject( hBindOrd, poSession->hError, + poSession->hOrdinatesTDO, + (dvoid **)&hOrdVARRAY, (ub4 *)0, + (dvoid **)0, (ub4 *)0), + "OCIBindObject(:ordinates)" ) ) + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Execute the insert. */ +/* -------------------------------------------------------------------- */ + if( oInsert.Execute( NULL ) != CE_None ) + return OGRERR_FAILURE; + else + return OGRERR_NONE; +} + + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGROCITableLayer::GetExtent(OGREnvelope *psExtent, int bForce) + +{ + CPLAssert( NULL != psExtent ); + + OGRErr err = OGRERR_FAILURE; + + if( EQUAL(GetGeometryColumn(),"") ) + { + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* Build query command. */ +/* -------------------------------------------------------------------- */ + CPLAssert( NULL != pszGeomName ); + + OGROCIStringBuf oCommand; + oCommand.Appendf( 1000, "SELECT " + "MIN(SDO_GEOM.SDO_MIN_MBR_ORDINATE(t.%s,m.DIMINFO,1)) AS MINX," + "MIN(SDO_GEOM.SDO_MIN_MBR_ORDINATE(t.%s,m.DIMINFO,2)) AS MINY," + "MAX(SDO_GEOM.SDO_MAX_MBR_ORDINATE(t.%s,m.DIMINFO,1)) AS MAXX," + "MAX(SDO_GEOM.SDO_MAX_MBR_ORDINATE(t.%s,m.DIMINFO,2)) AS MAXY " + "FROM ALL_SDO_GEOM_METADATA m, ", + pszGeomName, pszGeomName, pszGeomName, pszGeomName ); + + if( osOwner != "" ) + { + oCommand.Appendf( 500, " %s.%s t ", + osOwner.c_str(), osTableName.c_str() ); + } + else + { + oCommand.Appendf( 500, " %s t ", + osTableName.c_str() ); + } + + oCommand.Appendf( 500, "WHERE m.TABLE_NAME = UPPER('%s') AND m.COLUMN_NAME = UPPER('%s')", + osTableName.c_str(), pszGeomName ); + + if( osOwner != "" ) + { + oCommand.Appendf( 500, " AND OWNER = UPPER('%s')", osOwner.c_str() ); + } + +/* -------------------------------------------------------------------- */ +/* Execute query command. */ +/* -------------------------------------------------------------------- */ + OGROCISession *poSession = poDS->GetSession(); + CPLAssert( NULL != poSession ); + + OGROCIStatement oGetExtent( poSession ); + + if( oGetExtent.Execute( oCommand.GetString() ) == CE_None ) + { + char **papszRow = oGetExtent.SimpleFetchRow(); + + if( papszRow != NULL + && papszRow[0] != NULL && papszRow[1] != NULL + && papszRow[2] != NULL && papszRow[3] != NULL ) + { + psExtent->MinX = CPLAtof(papszRow[0]); + psExtent->MinY = CPLAtof(papszRow[1]); + psExtent->MaxX = CPLAtof(papszRow[2]); + psExtent->MaxY = CPLAtof(papszRow[3]); + + err = OGRERR_NONE; + } + } + +/* -------------------------------------------------------------------- */ +/* Query spatial extent of layer using default, */ +/* but not optimized implementation. */ +/* -------------------------------------------------------------------- */ + if( err != OGRERR_NONE ) + { + err = OGRLayer::GetExtent( psExtent, bForce ); + CPLDebug( "OCI", + "Failing to query extent of %s using default GetExtent", + osTableName.c_str() ); + } + + return err; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGROCITableLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCCreateField) ) + return bUpdateAccess; + + else + return OGROCILayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGROCITableLayer::GetFeatureCount( int bForce ) + +{ +/* -------------------------------------------------------------------- */ +/* Use a more brute force mechanism if we have a spatial query */ +/* in play. */ +/* -------------------------------------------------------------------- */ + if( m_poFilterGeom != NULL ) + return OGROCILayer::GetFeatureCount( bForce ); + +/* -------------------------------------------------------------------- */ +/* In theory it might be wise to cache this result, but it */ +/* won't be trivial to work out the lifetime of the value. */ +/* After all someone else could be adding records from another */ +/* application when working against a database. */ +/* -------------------------------------------------------------------- */ + OGROCISession *poSession = poDS->GetSession(); + OGROCIStatement oGetCount( poSession ); + char szCommand[1024]; + char **papszResult; + + sprintf( szCommand, "SELECT COUNT(*) FROM %s %s", + poFeatureDefn->GetName(), pszWHERE ); + + oGetCount.Execute( szCommand ); + + papszResult = oGetCount.SimpleFetchRow(); + + if( CSLCount(papszResult) < 1 ) + { + CPLDebug( "OCI", "Fast get count failed, doing hard way." ); + return OGROCILayer::GetFeatureCount( bForce ); + } + + return CPLAtoGIntBig(papszResult[0]); +} + +/************************************************************************/ +/* UpdateLayerExtents() */ +/************************************************************************/ + +void OGROCITableLayer::UpdateLayerExtents() + +{ + if( !bExtentUpdated ) + return; + + bExtentUpdated = false; + +/* -------------------------------------------------------------------- */ +/* Do we have existing layer extents we need to merge in to the */ +/* ones we collected as we created features? */ +/* -------------------------------------------------------------------- */ + bool bHaveOldExtent = false; + + if( !bNewLayer && pszGeomName ) + { + OGROCIStringBuf oCommand; + + oCommand.Appendf(1000, + "select min(case when r=1 then sdo_lb else null end) minx, min(case when r=2 then sdo_lb else null end) miny, " + "min(case when r=1 then sdo_ub else null end) maxx, min(case when r=2 then sdo_ub else null end) maxy" + " from (SELECT d.sdo_dimname, d.sdo_lb, sdo_ub, sdo_tolerance, rownum r" + " FROM ALL_SDO_GEOM_METADATA m, table(m.diminfo) d" + " where m.table_name = UPPER('%s') and m.COLUMN_NAME = UPPER('%s')", + osTableName.c_str(), pszGeomName ); + + if( osOwner != "" ) + { + oCommand.Appendf(500, " AND OWNER = UPPER('%s')", osOwner.c_str() ); + } + + oCommand.Append(" ) "); + + OGROCISession *poSession = poDS->GetSession(); + CPLAssert( NULL != poSession ); + + OGROCIStatement oGetExtent( poSession ); + + if( oGetExtent.Execute( oCommand.GetString() ) == CE_None ) + { + char **papszRow = oGetExtent.SimpleFetchRow(); + + if( papszRow != NULL + && papszRow[0] != NULL && papszRow[1] != NULL + && papszRow[2] != NULL && papszRow[3] != NULL ) + { + OGREnvelope sOldExtent; + + bHaveOldExtent = true; + + sOldExtent.MinX = CPLAtof(papszRow[0]); + sOldExtent.MinY = CPLAtof(papszRow[1]); + sOldExtent.MaxX = CPLAtof(papszRow[2]); + sOldExtent.MaxY = CPLAtof(papszRow[3]); + + if( sOldExtent.Contains( sExtent ) ) + { + // nothing to do! + sExtent = sOldExtent; + bExtentUpdated = false; + return; + } + else + { + sExtent.Merge( sOldExtent ); + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Establish the extents and resolution to use. */ +/* -------------------------------------------------------------------- */ + double dfResSize; + double dfXMin, dfXMax, dfXRes; + double dfYMin, dfYMax, dfYRes; + double dfZMin, dfZMax, dfZRes; + + if( sExtent.MaxX - sExtent.MinX > 400 ) + dfResSize = 0.001; + else + dfResSize = 0.0000001; + + dfXMin = sExtent.MinX - dfResSize * 3; + dfXMax = sExtent.MaxX + dfResSize * 3; + dfXRes = dfResSize; + ParseDIMINFO( "DIMINFO_X", &dfXMin, &dfXMax, &dfXRes ); + + dfYMin = sExtent.MinY - dfResSize * 3; + dfYMax = sExtent.MaxY + dfResSize * 3; + dfYRes = dfResSize; + ParseDIMINFO( "DIMINFO_Y", &dfYMin, &dfYMax, &dfYRes ); + + dfZMin = -100000.0; + dfZMax = 100000.0; + dfZRes = 0.002; + ParseDIMINFO( "DIMINFO_Z", &dfZMin, &dfZMax, &dfZRes ); + +/* -------------------------------------------------------------------- */ +/* If we already have an extent in the table, we will need to */ +/* update it in place. */ +/* -------------------------------------------------------------------- */ + OGROCIStringBuf sDimUpdate; + + if( bHaveOldExtent ) + { + sDimUpdate.Append( "UPDATE USER_SDO_GEOM_METADATA " ); + sDimUpdate.Append( "SET DIMINFO =" ); + sDimUpdate.Append( "MDSYS.SDO_DIM_ARRAY(" ); + sDimUpdate.Appendf(200, + "MDSYS.SDO_DIM_ELEMENT('X',%.16g,%.16g,%.12g)", + dfXMin, dfXMax, dfXRes ); + sDimUpdate.Appendf(200, + ",MDSYS.SDO_DIM_ELEMENT('Y',%.16g,%.16g,%.12g)", + dfYMin, dfYMax, dfYRes ); + + if( nDimension == 3 ) + { + sDimUpdate.Appendf(200, + ",MDSYS.SDO_DIM_ELEMENT('Z',%.16g,%.16g,%.12g)", + dfZMin, dfZMax, dfZRes ); + } + + sDimUpdate.Appendf(strlen(poFeatureDefn->GetName()) + 100,") WHERE TABLE_NAME = '%s'", poFeatureDefn->GetName()); + + } + else + { +/* -------------------------------------------------------------------- */ +/* Prepare dimension update statement. */ +/* -------------------------------------------------------------------- */ + sDimUpdate.Append( "INSERT INTO USER_SDO_GEOM_METADATA VALUES " ); + sDimUpdate.Appendf( strlen(poFeatureDefn->GetName()) + 100, + "('%s', '%s', ", + poFeatureDefn->GetName(), + pszGeomName ); + + sDimUpdate.Append( "MDSYS.SDO_DIM_ARRAY(" ); + sDimUpdate.Appendf(200, + "MDSYS.SDO_DIM_ELEMENT('X',%.16g,%.16g,%.12g)", + dfXMin, dfXMax, dfXRes ); + sDimUpdate.Appendf(200, + ",MDSYS.SDO_DIM_ELEMENT('Y',%.16g,%.16g,%.12g)", + dfYMin, dfYMax, dfYRes ); + + if( nDimension == 3 ) + { + sDimUpdate.Appendf(200, + ",MDSYS.SDO_DIM_ELEMENT('Z',%.16g,%.16g,%.12g)", + dfZMin, dfZMax, dfZRes ); + } + + if( nSRID == -1 ) + sDimUpdate.Append( "), NULL)" ); + else + sDimUpdate.Appendf( 100, "), %d)", nSRID ); + } + +/* -------------------------------------------------------------------- */ +/* Run the update/insert command. */ +/* -------------------------------------------------------------------- */ + OGROCIStatement oExecStatement( poDS->GetSession() ); + + oExecStatement.Execute( sDimUpdate.GetString() ); +} + +/************************************************************************/ +/* AllocAndBindForWrite() */ +/************************************************************************/ + +int OGROCITableLayer::AllocAndBindForWrite() + +{ + OGROCISession *poSession = poDS->GetSession(); + int i; + + CPLAssert( nWriteCacheMax == 0 ); + +/* -------------------------------------------------------------------- */ +/* Decide on the number of rows we want to be able to cache at */ +/* a time. */ +/* -------------------------------------------------------------------- */ + nWriteCacheMax = 100; + +/* -------------------------------------------------------------------- */ +/* Collect the INSERT statement. */ +/* -------------------------------------------------------------------- */ + OGROCIStringBuf oCmdBuf; + + oCmdBuf.Append( "INSERT INTO \"" ); + oCmdBuf.Append( poFeatureDefn->GetName() ); + oCmdBuf.Append( "\"(\"" ); + oCmdBuf.Append( pszFIDName ); + + if (GetGeomType() != wkbNone) + { + oCmdBuf.Append( "\",\"" ); + oCmdBuf.Append( pszGeomName ); + } + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + oCmdBuf.Append( "\",\"" ); + oCmdBuf.Append( poFeatureDefn->GetFieldDefn(i)->GetNameRef() ); + } + + oCmdBuf.Append( "\") VALUES ( :fid " ); + + if (GetGeomType() != wkbNone) + oCmdBuf.Append( ", :geometry" ); + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + oCmdBuf.Append( ", " ); + oCmdBuf.Appendf( 20, " :field_%d", i ); + } + + oCmdBuf.Append( ") " ); + +/* -------------------------------------------------------------------- */ +/* Bind and Prepare it. */ +/* -------------------------------------------------------------------- */ + poBoundStatement = new OGROCIStatement( poSession ); + poBoundStatement->Prepare( oCmdBuf.GetString() ); + +/* -------------------------------------------------------------------- */ +/* Setup geometry indicator information. */ +/* -------------------------------------------------------------------- */ + if (GetGeomType() != wkbNone) + { + pasWriteGeomInd = (SDO_GEOMETRY_ind *) + CPLCalloc(sizeof(SDO_GEOMETRY_ind),nWriteCacheMax); + + papsWriteGeomIndMap = (SDO_GEOMETRY_ind **) + CPLCalloc(sizeof(SDO_GEOMETRY_ind *),nWriteCacheMax); + + for( i = 0; i < nWriteCacheMax; i++ ) + papsWriteGeomIndMap[i] = pasWriteGeomInd + i; + +/* -------------------------------------------------------------------- */ +/* Setup all the required geometry objects, and the */ +/* corresponding indicator map. */ +/* -------------------------------------------------------------------- */ + pasWriteGeoms = (SDO_GEOMETRY_TYPE *) + CPLCalloc( sizeof(SDO_GEOMETRY_TYPE), nWriteCacheMax); + papsWriteGeomMap = (SDO_GEOMETRY_TYPE **) + CPLCalloc( sizeof(SDO_GEOMETRY_TYPE *), nWriteCacheMax ); + + for( i = 0; i < nWriteCacheMax; i++ ) + papsWriteGeomMap[i] = pasWriteGeoms + i; + +/* -------------------------------------------------------------------- */ +/* Allocate VARRAYs for the elem_info and ordinates. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nWriteCacheMax; i++ ) + { + if( poSession->Failed( + OCIObjectNew( poSession->hEnv, poSession->hError, + poSession->hSvcCtx, OCI_TYPECODE_VARRAY, + poSession->hElemInfoTDO, (dvoid *)NULL, + OCI_DURATION_SESSION, + FALSE, + (dvoid **) &(pasWriteGeoms[i].sdo_elem_info)), + "OCIObjectNew(elem_info)") ) + return FALSE; + + if( poSession->Failed( + OCIObjectNew( poSession->hEnv, poSession->hError, + poSession->hSvcCtx, OCI_TYPECODE_VARRAY, + poSession->hOrdinatesTDO, (dvoid *)NULL, + OCI_DURATION_SESSION, + FALSE, + (dvoid **) &(pasWriteGeoms[i].sdo_ordinates)), + "OCIObjectNew(ordinates)") ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Bind the geometry column. */ +/* -------------------------------------------------------------------- */ + if( poBoundStatement->BindObject( + ":geometry", papsWriteGeomMap, poSession->hGeometryTDO, + (void**) papsWriteGeomIndMap) != CE_None ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Bind the FID column. */ +/* -------------------------------------------------------------------- */ + panWriteFIDs = (int *) CPLMalloc(sizeof(int) * nWriteCacheMax ); + + if( poBoundStatement->BindScalar( ":fid", panWriteFIDs, sizeof(int), + SQLT_INT ) != CE_None ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Allocate each of the column data bind arrays. */ +/* -------------------------------------------------------------------- */ + + papWriteFields = (void **) + CPLMalloc(sizeof(void*) * poFeatureDefn->GetFieldCount() ); + papaeWriteFieldInd = (OCIInd **) + CPLCalloc(sizeof(OCIInd*),poFeatureDefn->GetFieldCount() ); + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(i); + char szFieldPlaceholderName[80]; + + sprintf( szFieldPlaceholderName, ":field_%d", i ); + + papaeWriteFieldInd[i] = (OCIInd *) + CPLCalloc(sizeof(OCIInd), nWriteCacheMax ); + + if( poFldDefn->GetType() == OFTInteger ) + { + papWriteFields[i] = + (void *) CPLCalloc( sizeof(int), nWriteCacheMax ); + + if( poBoundStatement->BindScalar( + szFieldPlaceholderName, papWriteFields[i], + sizeof(int), SQLT_INT, papaeWriteFieldInd[i] ) != CE_None ) + return FALSE; + } + else if( poFldDefn->GetType() == OFTInteger64 ) + { + papWriteFields[i] = + (void *) CPLCalloc( sizeof(GIntBig), nWriteCacheMax ); + + if( poBoundStatement->BindScalar( + szFieldPlaceholderName, papWriteFields[i], + sizeof(GIntBig), SQLT_INT, papaeWriteFieldInd[i] ) != CE_None ) + return FALSE; + } + else if( poFldDefn->GetType() == OFTReal ) + { + papWriteFields[i] = (void *) CPLCalloc( sizeof(double), + nWriteCacheMax ); + + if( poBoundStatement->BindScalar( + szFieldPlaceholderName, papWriteFields[i], + sizeof(double), SQLT_FLT, papaeWriteFieldInd[i] ) != CE_None ) + return FALSE; + } + else + { + int nEachBufSize = 4001; + + if( poFldDefn->GetType() == OFTString + && poFldDefn->GetWidth() != 0 ) + nEachBufSize = poFldDefn->GetWidth() + 1; + + papWriteFields[i] = + (void *) CPLCalloc( nEachBufSize, nWriteCacheMax ); + + if( poBoundStatement->BindScalar( + szFieldPlaceholderName, papWriteFields[i], + nEachBufSize, SQLT_STR, papaeWriteFieldInd[i]) != CE_None ) + return FALSE; + } + } + + return TRUE; +} + +/************************************************************************/ +/* BoundCreateFeature() */ +/************************************************************************/ + +OGRErr OGROCITableLayer::BoundCreateFeature( OGRFeature *poFeature ) + +{ + OGROCISession *poSession = poDS->GetSession(); + int iCache, i; + OGRErr eErr; + OCINumber oci_number; + + /* If an unset field has a default value, the current implementation */ + /* of BoundCreateFeature() doesn't work. */ + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if( !poFeature->IsFieldSet( i ) && + poFeature->GetFieldDefnRef(i)->GetDefault() != NULL ) + { + FlushPendingFeatures(); + return UnboundCreateFeature(poFeature); + } + } + + if( !poFeature->Validate( OGR_F_VAL_NULL | OGR_F_VAL_ALLOW_NULL_WHEN_DEFAULT, TRUE ) ) + return OGRERR_FAILURE; + + iCache = nWriteCacheUsed; + +/* -------------------------------------------------------------------- */ +/* Initiate the Insert */ +/* -------------------------------------------------------------------- */ + if( nWriteCacheMax == 0 ) + { + if( !AllocAndBindForWrite() ) + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Set the geometry */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetGeometryRef() != NULL ) + { + SDO_GEOMETRY_TYPE *psGeom = pasWriteGeoms + iCache; + SDO_GEOMETRY_ind *psInd = pasWriteGeomInd + iCache; + OGRGeometry *poGeometry = poFeature->GetGeometryRef(); + int nGType; + + psInd->_atomic = OCI_IND_NOTNULL; + + if( nSRID == -1 ) + psInd->sdo_srid = OCI_IND_NULL; + else + { + psInd->sdo_srid = OCI_IND_NOTNULL; + OCINumberFromInt( poSession->hError, &nSRID, + (uword)sizeof(int), OCI_NUMBER_SIGNED, + &(psGeom->sdo_srid) ); + } + + /* special more efficient case for simple points */ + if( wkbFlatten(poGeometry->getGeometryType()) == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poGeometry; + double dfValue; + + psInd->sdo_point._atomic = OCI_IND_NOTNULL; + psInd->sdo_elem_info = OCI_IND_NULL; + psInd->sdo_ordinates = OCI_IND_NULL; + + dfValue = poPoint->getX(); + OCINumberFromReal( poSession->hError, &dfValue, + (uword)sizeof(double), + &(psGeom->sdo_point.x) ); + + dfValue = poPoint->getY(); + OCINumberFromReal( poSession->hError, &dfValue, + (uword)sizeof(double), + &(psGeom->sdo_point.y) ); + + if( nDimension == 2 ) + { + nGType = 2001; + psInd->sdo_point.z = OCI_IND_NULL; + } + else + { + nGType = 3001; + psInd->sdo_point.z = OCI_IND_NOTNULL; + + dfValue = poPoint->getZ(); + OCINumberFromReal( poSession->hError, &dfValue, + (uword)sizeof(double), + &(psGeom->sdo_point.z) ); + } + } + else + { + psInd->sdo_point._atomic = OCI_IND_NULL; + psInd->sdo_elem_info = OCI_IND_NOTNULL; + psInd->sdo_ordinates = OCI_IND_NOTNULL; + + eErr = TranslateToSDOGeometry( poFeature->GetGeometryRef(), + &nGType ); + + if( eErr != OGRERR_NONE ) + return eErr; + + /* Clear the existing eleminfo and ordinates arrays */ + sb4 nOldCount; + + OCICollSize( poSession->hEnv, poSession->hError, + psGeom->sdo_elem_info, &nOldCount ); + OCICollTrim( poSession->hEnv, poSession->hError, + nOldCount, psGeom->sdo_elem_info ); + + OCICollSize( poSession->hEnv, poSession->hError, + psGeom->sdo_ordinates, &nOldCount ); + OCICollTrim( poSession->hEnv, poSession->hError, + nOldCount, psGeom->sdo_ordinates ); + + // Prepare the VARRAY of element values. + for (i = 0; i < nElemInfoCount; i++) + { + OCINumberFromInt( poSession->hError, + (dvoid *) (panElemInfo + i), + (uword)sizeof(int), OCI_NUMBER_SIGNED, + &oci_number ); + + OCICollAppend( poSession->hEnv, poSession->hError, + (dvoid *) &oci_number, + (dvoid *)0, psGeom->sdo_elem_info ); + } + + // Prepare the VARRAY of ordinate values. + for (i = 0; i < nOrdinalCount; i++) + { + OCINumberFromReal( poSession->hError, + (dvoid *) (padfOrdinals + i), + (uword)sizeof(double), &oci_number ); + OCICollAppend( poSession->hEnv, poSession->hError, + (dvoid *) &oci_number, + (dvoid *)0, psGeom->sdo_ordinates ); + } + } + + psInd->sdo_gtype = OCI_IND_NOTNULL; + OCINumberFromInt( poSession->hError, &nGType, + (uword)sizeof(int), OCI_NUMBER_SIGNED, + &(psGeom->sdo_gtype) ); + } + else if( pasWriteGeomInd != NULL ) + { + SDO_GEOMETRY_ind *psInd = pasWriteGeomInd + iCache; + psInd->_atomic = OCI_IND_NULL; + psInd->sdo_srid = OCI_IND_NULL; + psInd->sdo_point._atomic = OCI_IND_NULL; + psInd->sdo_elem_info = OCI_IND_NULL; + psInd->sdo_ordinates = OCI_IND_NULL; + psInd->sdo_gtype = OCI_IND_NULL; + } + +/* -------------------------------------------------------------------- */ +/* Set the FID. */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetFID() == OGRNullFID ) + { + if( iNextFIDToWrite < 0 ) + { + iNextFIDToWrite = GetMaxFID() + 1; + } + + poFeature->SetFID( iNextFIDToWrite++ ); + } + + panWriteFIDs[iCache] = poFeature->GetFID(); + +/* -------------------------------------------------------------------- */ +/* Set the other fields. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if( !poFeature->IsFieldSet( i ) ) + { + papaeWriteFieldInd[i][iCache] = OCI_IND_NULL; + continue; + } + + papaeWriteFieldInd[i][iCache] = OCI_IND_NOTNULL; + + OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(i); + + if( poFldDefn->GetType() == OFTInteger ) + ((int *) (papWriteFields[i]))[iCache] = + poFeature->GetFieldAsInteger( i ); + + else if( poFldDefn->GetType() == OFTInteger64 ) + ((GIntBig *) (papWriteFields[i]))[iCache] = + poFeature->GetFieldAsInteger64( i ); + + else if( poFldDefn->GetType() == OFTReal ) + ((double *) (papWriteFields[i]))[iCache] = + poFeature->GetFieldAsDouble( i ); + + else + { + int nEachBufSize = 4001, nLen; + const char *pszStrValue = poFeature->GetFieldAsString(i); + + if( poFldDefn->GetType() == OFTString + && poFldDefn->GetWidth() != 0 ) + nEachBufSize = poFldDefn->GetWidth() + 1; + + nLen = strlen(pszStrValue); + if( nLen > nEachBufSize-1 ) + nLen = nEachBufSize-1; + + char *pszTarget = ((char*)papWriteFields[i]) + iCache*nEachBufSize; + strncpy( pszTarget, pszStrValue, nLen ); + pszTarget[nLen] = '\0'; + } + } + +/* -------------------------------------------------------------------- */ +/* Do we need to flush out a full set of rows? */ +/* -------------------------------------------------------------------- */ + nWriteCacheUsed++; + + if( nWriteCacheUsed == nWriteCacheMax ) + return FlushPendingFeatures(); + else + return OGRERR_NONE; +} + +/************************************************************************/ +/* FlushPendingFeatures() */ +/************************************************************************/ + +OGRErr OGROCITableLayer::FlushPendingFeatures() + +{ + OGROCISession *poSession = poDS->GetSession(); + + if( nWriteCacheUsed > 0 ) + { + CPLDebug( "OCI", "Flushing %d features on layer %s", + nWriteCacheUsed, poFeatureDefn->GetName() ); + + if( poSession->Failed( + OCIStmtExecute( poSession->hSvcCtx, + poBoundStatement->GetStatement(), + poSession->hError, (ub4) nWriteCacheUsed, + (ub4) 0, + (OCISnapshot *)NULL, (OCISnapshot *)NULL, + (ub4) OCI_COMMIT_ON_SUCCESS ), + "OCIStmtExecute" ) ) + { + nWriteCacheUsed = 0; + return OGRERR_FAILURE; + } + else + { + nWriteCacheUsed = 0; + return OGRERR_NONE; + } + } + else + return OGRERR_NONE; +} + +/************************************************************************/ +/* SyncToDisk() */ +/* */ +/* Perhaps we should also be putting the metadata into a */ +/* useable state? */ +/************************************************************************/ + +OGRErr OGROCITableLayer::SyncToDisk() + +{ + OGRErr eErr = FlushPendingFeatures(); + + UpdateLayerExtents(); + + CreateSpatialIndex(); + + bNewLayer = FALSE; + + return eErr; +} + +/*************************************************************************/ +/* CreateSpatialIndex() */ +/*************************************************************************/ + +void OGROCITableLayer::CreateSpatialIndex() + +{ +/* -------------------------------------------------------------------- */ +/* For new layers we try to create a spatial index. */ +/* -------------------------------------------------------------------- */ + if( bNewLayer && sExtent.IsInit() ) + { +/* -------------------------------------------------------------------- */ +/* If the user has disabled INDEX support then don't create the */ +/* index. */ +/* -------------------------------------------------------------------- */ + if( !CSLFetchBoolean( papszOptions, "SPATIAL_INDEX", TRUE ) || + !CSLFetchBoolean( papszOptions, "INDEX", TRUE ) ) + return; + +/* -------------------------------------------------------------------- */ +/* Establish an index name. For some reason Oracle 8.1.7 does */ +/* not support spatial index names longer than 18 characters so */ +/* we magic up an index name if it would be too long. */ +/* -------------------------------------------------------------------- */ + char szIndexName[20]; + + if( strlen(poFeatureDefn->GetName()) < 15 ) + sprintf( szIndexName, "%s_idx", poFeatureDefn->GetName() ); + else if( strlen(poFeatureDefn->GetName()) < 17 ) + sprintf( szIndexName, "%si", poFeatureDefn->GetName() ); + else + { + int i, nHash = 0; + const char *pszSrcName = poFeatureDefn->GetName(); + + for( i = 0; pszSrcName[i] != '\0'; i++ ) + nHash = (nHash + i * pszSrcName[i]) % 987651; + + sprintf( szIndexName, "OSI_%d", nHash ); + } + + poDS->GetSession()->CleanName( szIndexName ); + +/* -------------------------------------------------------------------- */ +/* Try creating an index on the table now. Use a simple 5 */ +/* level quadtree based index. Would R-tree be a better default? */ +/* -------------------------------------------------------------------- */ + OGROCIStringBuf sIndexCmd; + OGROCIStatement oExecStatement( poDS->GetSession() ); + + + sIndexCmd.Appendf( 10000, "CREATE INDEX \"%s\" ON %s(\"%s\") " + "INDEXTYPE IS MDSYS.SPATIAL_INDEX ", + szIndexName, + poFeatureDefn->GetName(), + pszGeomName ); + + int bAddLayerGType = CSLTestBoolean( + CSLFetchNameValueDef( papszOptions, "ADD_LAYER_GTYPE", "YES") ) && + GetGeomType() != wkbUnknown; + + CPLString osParams(CSLFetchNameValueDef(papszOptions,"INDEX_PARAMETERS", "")); + if( bAddLayerGType || osParams.size() != 0 ) + { + sIndexCmd.Append( " PARAMETERS( '" ); + if( osParams.size() != 0 ) + sIndexCmd.Append( osParams.c_str() ); + if( bAddLayerGType && + osParams.ifind("LAYER_GTYPE") == std::string::npos ) + { + if( osParams.size() != 0 ) + sIndexCmd.Append( ", " ); + sIndexCmd.Append( "LAYER_GTYPE=" ); + if( wkbFlatten(GetGeomType()) == wkbPoint ) + sIndexCmd.Append( "POINT" ); + else if( wkbFlatten(GetGeomType()) == wkbLineString ) + sIndexCmd.Append( "LINE" ); + else if( wkbFlatten(GetGeomType()) == wkbPolygon ) + sIndexCmd.Append( "POLYGON" ); + else if( wkbFlatten(GetGeomType()) == wkbMultiPoint ) + sIndexCmd.Append( "MULTIPOINT" ); + else if( wkbFlatten(GetGeomType()) == wkbMultiLineString ) + sIndexCmd.Append( "MULTILINE" ); + else if( wkbFlatten(GetGeomType()) == wkbMultiPolygon ) + sIndexCmd.Append( "MULTIPOLYGON" ); + else + sIndexCmd.Append( "COLLECTION" ); + } + sIndexCmd.Append( "' )" ); + } + + if( oExecStatement.Execute( sIndexCmd.GetString() ) != CE_None ) + { + CPLString osDropCommand; + osDropCommand.Printf( "DROP INDEX \"%s\"", szIndexName ); + oExecStatement.Execute( osDropCommand ); + } + } +} + +int OGROCITableLayer::GetMaxFID() +{ + if( pszFIDName == NULL ) + return 0; + + OGROCIStringBuf sCmd; + OGROCIStatement oSelect( poDS->GetSession() ); + + sCmd.Appendf( 10000, "SELECT MAX(\"%s\") FROM \"%s\"", + pszFIDName, + poFeatureDefn->GetName() + ); + + oSelect.Execute( sCmd.GetString() ); + + char **papszResult = oSelect.SimpleFetchRow(); + return CSLCount(papszResult) == 1 ? atoi( papszResult[0] ) : 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrociwritablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrociwritablelayer.cpp new file mode 100644 index 000000000..02c2e89c7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/oci/ogrociwritablelayer.cpp @@ -0,0 +1,550 @@ +/****************************************************************************** + * $Id: ogrociwritablelayer.cpp 28481 2015-02-13 17:11:15Z rouault $ + * + * Project: Oracle Spatial Driver + * Purpose: Implementation of the OGROCIWritableLayer class. This provides + * some services for converting OGRGeometries into Oracle structures + * that is shared between OGROCITableLayer and OGROCILoaderLayer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_oci.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrociwritablelayer.cpp 28481 2015-02-13 17:11:15Z rouault $"); + +/************************************************************************/ +/* OGROCIWritableLayer() */ +/************************************************************************/ + +OGROCIWritableLayer::OGROCIWritableLayer() + +{ + nDimension = MAX(2,MIN(3,atoi(CPLGetConfigOption("OCI_DEFAULT_DIM","3")))); + nSRID = -1; + + nOrdinalCount = 0; + nOrdinalMax = 0; + padfOrdinals = NULL; + + nElemInfoCount = 0; + nElemInfoMax = 0; + panElemInfo = NULL; + + bLaunderColumnNames = TRUE; + bTruncationReported = FALSE; + nSRID = -1; + poSRS = NULL; + + papszOptions = NULL; +} + +/************************************************************************/ +/* ~OGROCIWritableLayer() */ +/************************************************************************/ + +OGROCIWritableLayer::~OGROCIWritableLayer() + +{ + CPLFree( padfOrdinals ); + CPLFree( panElemInfo ); + + CSLDestroy( papszOptions ); +} + +/************************************************************************/ +/* PushOrdinal() */ +/************************************************************************/ + +void OGROCIWritableLayer::PushOrdinal( double dfOrd ) + +{ + if( nOrdinalCount == nOrdinalMax ) + { + nOrdinalMax = nOrdinalMax * 2 + 100; + padfOrdinals = (double *) CPLRealloc(padfOrdinals, + sizeof(double) * nOrdinalMax); + } + + padfOrdinals[nOrdinalCount++] = dfOrd; +} + +/************************************************************************/ +/* PushElemInfo() */ +/************************************************************************/ + +void OGROCIWritableLayer::PushElemInfo( int nOffset, int nEType, int nInterp ) + +{ + if( nElemInfoCount+3 >= nElemInfoMax ) + { + nElemInfoMax = nElemInfoMax * 2 + 100; + panElemInfo = (int *) CPLRealloc(panElemInfo,sizeof(int)*nElemInfoMax); + } + + panElemInfo[nElemInfoCount++] = nOffset; + panElemInfo[nElemInfoCount++] = nEType; + panElemInfo[nElemInfoCount++] = nInterp; +} + +/************************************************************************/ +/* TranslateElementGroup() */ +/* */ +/* Append one or more element groups to the existing element */ +/* info and ordinates lists for the passed geometry. */ +/************************************************************************/ + +OGRErr +OGROCIWritableLayer::TranslateElementGroup( OGRGeometry *poGeometry ) + +{ + switch( wkbFlatten(poGeometry->getGeometryType()) ) + { + case wkbPoint: + { + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + PushElemInfo( nOrdinalCount+1, 1, 1 ); + + PushOrdinal( poPoint->getX() ); + PushOrdinal( poPoint->getY() ); + if( nDimension == 3 ) + PushOrdinal( poPoint->getZ() ); + + return OGRERR_NONE; + } + + case wkbLineString: + { + OGRLineString *poLine = (OGRLineString *) poGeometry; + int iVert; + + PushElemInfo( nOrdinalCount+1, 2, 1 ); + + for( iVert = 0; iVert < poLine->getNumPoints(); iVert++ ) + { + PushOrdinal( poLine->getX(iVert) ); + PushOrdinal( poLine->getY(iVert) ); + if( nDimension == 3 ) + PushOrdinal( poLine->getZ(iVert) ); + } + return OGRERR_NONE; + } + + case wkbPolygon: + { + OGRPolygon *poPoly = (OGRPolygon *) poGeometry; + int iRing; + + for( iRing = -1; iRing < poPoly->getNumInteriorRings(); iRing++ ) + { + OGRLinearRing *poRing; + int iVert; + + if( iRing == -1 ) + poRing = poPoly->getExteriorRing(); + else + poRing = poPoly->getInteriorRing(iRing); + + if( iRing == -1 ) + PushElemInfo( nOrdinalCount+1, 1003, 1 ); + else + PushElemInfo( nOrdinalCount+1, 2003, 1 ); + + if( (iRing == -1 && poRing->isClockwise()) + || (iRing != -1 && !poRing->isClockwise()) ) + { + for( iVert = poRing->getNumPoints()-1; iVert >= 0; iVert-- ) + { + PushOrdinal( poRing->getX(iVert) ); + PushOrdinal( poRing->getY(iVert) ); + if( nDimension == 3 ) + PushOrdinal( poRing->getZ(iVert) ); + } + } + else + { + for( iVert = 0; iVert < poRing->getNumPoints(); iVert++ ) + { + PushOrdinal( poRing->getX(iVert) ); + PushOrdinal( poRing->getY(iVert) ); + if( nDimension == 3 ) + PushOrdinal( poRing->getZ(iVert) ); + } + } + } + + return OGRERR_NONE; + } + + default: + { + return OGRERR_FAILURE; + } + } +} + +/************************************************************************/ +/* ReportTruncation() */ +/************************************************************************/ + +void OGROCIWritableLayer::ReportTruncation( OGRFieldDefn * psFldDefn ) + +{ + if( bTruncationReported ) + return; + + CPLError( CE_Warning, CPLE_AppDefined, + "The value for the field %s is being truncated to fit the\n" + "declared width/precision of the field. No more truncations\n" + "for table %s will be reported.", + psFldDefn->GetNameRef(), poFeatureDefn->GetName() ); + + bTruncationReported = TRUE; +} + +/************************************************************************/ +/* SetOptions() */ +/* */ +/* Set layer creation or other options. */ +/************************************************************************/ + +void OGROCIWritableLayer::SetOptions( char **papszOptionsIn ) + +{ + CSLDestroy( papszOptions ); + papszOptions = CSLDuplicate( papszOptionsIn ); +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGROCIWritableLayer::CreateField( OGRFieldDefn *poFieldIn, int bApproxOK ) + +{ + OGROCISession *poSession = poDS->GetSession(); + char szFieldType[256]; + char szFieldName[30]; // specify at most 30 characters, see ORA-00972 + OGRFieldDefn oField( poFieldIn ); + +/* -------------------------------------------------------------------- */ +/* Do we want to "launder" the column names into Oracle */ +/* friendly format? */ +/* -------------------------------------------------------------------- */ + if( bLaunderColumnNames ) + { + char *pszSafeName = CPLStrdup( oField.GetNameRef() ); + + poSession->CleanName( pszSafeName ); + oField.SetName( pszSafeName ); + CPLFree( pszSafeName ); + } + +/* -------------------------------------------------------------------- */ +/* Work out the Oracle type. */ +/* -------------------------------------------------------------------- */ + if( oField.GetType() == OFTInteger ) + { + if( bPreservePrecision && oField.GetWidth() != 0 ) + sprintf( szFieldType, "NUMBER(%d)", oField.GetWidth() ); + else + strcpy( szFieldType, "INTEGER" ); + } + else if( oField.GetType() == OFTInteger64 ) + { + if( bPreservePrecision && oField.GetWidth() != 0 ) + sprintf( szFieldType, "NUMBER(%d)", oField.GetWidth() ); + else + strcpy( szFieldType, "NUMBER(20)" ); + } + else if( oField.GetType() == OFTReal ) + { + if( bPreservePrecision && oField.GetWidth() != 0 ) + sprintf( szFieldType, "NUMBER(%d,%d)", + oField.GetWidth(), oField.GetPrecision() ); + else + strcpy( szFieldType, "FLOAT(126)" ); + } + else if( oField.GetType() == OFTString ) + { + if( oField.GetWidth() == 0 || !bPreservePrecision ) + strcpy( szFieldType, "VARCHAR2(2047)" ); + else + sprintf( szFieldType, "VARCHAR2(%d)", oField.GetWidth() ); + } + else if ( oField.GetType() == OFTDate ) + { + sprintf( szFieldType, "DATE" ); + } + else if ( oField.GetType() == OFTDateTime ) + { + sprintf( szFieldType, "TIMESTAMP" ); + } + else if( bApproxOK ) + { + oField.SetDefault(NULL); + CPLError( CE_Warning, CPLE_NotSupported, + "Can't create field %s with type %s on Oracle layers. Creating as VARCHAR.", + oField.GetNameRef(), + OGRFieldDefn::GetFieldTypeName(oField.GetType()) ); + strcpy( szFieldType, "VARCHAR2(2047)" ); + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can't create field %s with type %s on Oracle layers.", + oField.GetNameRef(), + OGRFieldDefn::GetFieldTypeName(oField.GetType()) ); + + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Create the new field. */ +/* -------------------------------------------------------------------- */ + OGROCIStringBuf oCommand; + OGROCIStatement oAddField( poSession ); + + oCommand.MakeRoomFor( 70 + strlen(poFeatureDefn->GetName()) + + strlen(oField.GetNameRef()) + + strlen(szFieldType) + + (oField.GetDefault() ? strlen(oField.GetDefault()) : 0) ); + + snprintf( szFieldName, sizeof( szFieldName ), "%s", oField.GetNameRef()); + szFieldName[sizeof( szFieldName )-1] = '\0'; + if ( strlen(oField.GetNameRef()) > sizeof ( szFieldName ) ) + { + szFieldName[sizeof( szFieldName ) - 1] = '_'; + CPLError( CE_Warning, CPLE_AppDefined, + "Column %s is too long (at most 30 characters). Using %s.", + oField.GetNameRef(), szFieldName ); + oField.SetName(szFieldName); + } + sprintf( oCommand.GetString(), "ALTER TABLE %s ADD \"%s\" %s", + poFeatureDefn->GetName(), szFieldName, szFieldType); + if( oField.GetDefault() != NULL && !oField.IsDefaultDriverSpecific() ) + { + sprintf( oCommand.GetString() + strlen(oCommand.GetString()), + " DEFAULT %s", oField.GetDefault() ); + } + if( !oField.IsNullable() ) + strcat( oCommand.GetString(), " NOT NULL"); + + if( oAddField.Execute( oCommand.GetString() ) != CE_None ) + return OGRERR_FAILURE; + + poFeatureDefn->AddFieldDefn( &oField ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetDimension() */ +/************************************************************************/ + +void OGROCIWritableLayer::SetDimension( int nNewDim ) + +{ + nDimension = nNewDim; +} + +/************************************************************************/ +/* ParseDIMINFO() */ +/************************************************************************/ + +void OGROCIWritableLayer::ParseDIMINFO( const char *pszOptionName, + double *pdfMin, + double *pdfMax, + double *pdfRes ) + +{ + const char *pszUserDIMINFO; + char **papszTokens; + + pszUserDIMINFO = CSLFetchNameValue( papszOptions, pszOptionName ); + if( pszUserDIMINFO == NULL ) + return; + + papszTokens = + CSLTokenizeStringComplex( pszUserDIMINFO, ",", FALSE, FALSE ); + if( CSLCount(papszTokens) != 3 ) + { + CSLDestroy( papszTokens ); + CPLError( CE_Warning, CPLE_AppDefined, + "Ignoring %s, it does not contain three comma separated values.", + pszOptionName ); + return; + } + + *pdfMin = CPLAtof(papszTokens[0]); + *pdfMax = CPLAtof(papszTokens[1]); + *pdfRes = CPLAtof(papszTokens[2]); + + CSLDestroy( papszTokens ); +} + +/************************************************************************/ +/* TranslateToSDOGeometry() */ +/************************************************************************/ + +OGRErr OGROCIWritableLayer::TranslateToSDOGeometry( OGRGeometry * poGeometry, + int *pnGType ) + +{ + nOrdinalCount = 0; + nElemInfoCount = 0; + + if( poGeometry == NULL ) + return OGRERR_FAILURE; + +/* ==================================================================== */ +/* Handle a point geometry. */ +/* ==================================================================== */ + if( wkbFlatten(poGeometry->getGeometryType()) == wkbPoint ) + { +#ifdef notdef + char szResult[1024]; + OGRPoint *poPoint = (OGRPoint *) poGeometry; + + if( nDimension == 2 ) + CPLsprintf( szResult, + "%s(%d,%s,MDSYS.SDO_POINT_TYPE(%.16g,%.16g,0.0),NULL,NULL)", + SDO_GEOMETRY, 2001, szSRID, + poPoint->getX(), poPoint->getY() ); + else + CPLsprintf( szResult, + "%s(%d,%s,MDSYS.SDO_POINT_TYPE(%.16g,%.16g,%.16g),NULL,NULL)", + SDO_GEOMETRY, 3001, szSRID, + poPoint->getX(), poPoint->getY(), poPoint->getZ() ); + + return CPLStrdup(szResult ); +#endif + } + +/* ==================================================================== */ +/* Handle a line string geometry. */ +/* ==================================================================== */ + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbLineString ) + { + *pnGType = nDimension * 1000 + 2; + TranslateElementGroup( poGeometry ); + return OGRERR_NONE; + } + +/* ==================================================================== */ +/* Handle a polygon geometry. */ +/* ==================================================================== */ + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbPolygon ) + { + *pnGType = nDimension == 2 ? 2003 : 3003; + TranslateElementGroup( poGeometry ); + return OGRERR_NONE; + } + +/* ==================================================================== */ +/* Handle a multi point geometry. */ +/* ==================================================================== */ + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPoint ) + { + OGRMultiPoint *poMP = (OGRMultiPoint *) poGeometry; + int iVert; + + *pnGType = nDimension*1000 + 5; + PushElemInfo( 1, 1, poMP->getNumGeometries() ); + + for( iVert = 0; iVert < poMP->getNumGeometries(); iVert++ ) + { + OGRPoint *poPoint = (OGRPoint *)poMP->getGeometryRef( iVert ); + + PushOrdinal( poPoint->getX() ); + PushOrdinal( poPoint->getY() ); + if( nDimension == 3 ) + PushOrdinal( poPoint->getZ() ); + } + + return OGRERR_NONE; + } + +/* ==================================================================== */ +/* Handle other geometry collections. */ +/* ==================================================================== */ + else + { +/* -------------------------------------------------------------------- */ +/* Identify the GType. */ +/* -------------------------------------------------------------------- */ + if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiLineString ) + *pnGType = nDimension * 1000 + 6; + else if( wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon ) + *pnGType = nDimension * 1000 + 7; + else if( wkbFlatten(poGeometry->getGeometryType()) + == wkbGeometryCollection ) + *pnGType = nDimension * 1000 + 4; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unexpected geometry type (%d/%s) in " + "OGROCIWritableLayer::TranslateToSDOGeometry()", + poGeometry->getGeometryType(), + poGeometry->getGeometryName() ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Translate each child in turn. */ +/* -------------------------------------------------------------------- */ + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeometry; + int iChild; + + for( iChild = 0; iChild < poGC->getNumGeometries(); iChild++ ) + TranslateElementGroup( poGC->getGeometryRef(iChild) ); + + return OGRERR_NONE; + } + + return OGRERR_FAILURE; +} + +int OGROCIWritableLayer::FindFieldIndex( const char *pszFieldName, int bExactMatch ) +{ + int iField = GetLayerDefn()->GetFieldIndex( pszFieldName ); + + if( !bExactMatch && iField < 0 ) + { + // try laundered version + OGROCISession *poSession = poDS->GetSession(); + char *pszSafeName = CPLStrdup( pszFieldName ); + + poSession->CleanName( pszSafeName ); + + iField = GetLayerDefn()->GetFieldIndex( pszSafeName ); + + CPLFree( pszSafeName ); + } + + return iField; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/GNUmakefile new file mode 100644 index 000000000..68d724e61 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/GNUmakefile @@ -0,0 +1,13 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrodbcdatasource.o ogrodbclayer.o ogrodbcdriver.o \ + ogrodbctablelayer.o ogrodbcselectlayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/drv_odbc.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/drv_odbc.html new file mode 100644 index 000000000..4f7c27cdc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/drv_odbc.html @@ -0,0 +1,82 @@ + + +ODBC RDBMS + + + + +

    ODBC RDBMS

    + +OGR optionally supports spatial and non-spatial tables accessed via ODBC. +ODBC is a generic access layer for access to many database systems, and +data that can be represented as a database (collection of tables). ODBC +support is potentially available on Unix and Windows platforms, but is +only included in unix builds by special configuration options.

    + +ODBC datasources are accessed using a datasource name of the form +ODBC:userid/password@dsn,schema.tablename(geometrycolname),...:srs_tablename(sridcolumn,srtextcolumn). +With optional items dropped the following are also acceptable: + +

      +
    • ODBC:userid/password@dsn +
    • ODBC:userid@dsn,table_list +
    • ODBC:dsn,table_list +
    • ODBC:dsn +
    • ODBC:dsn,table_list:srs_tablename +
    + +The dsn is the ODBC Data Source Name. Normally ODBC datasources +are setup using an ODBC Administration tool, and assigned a DSN. That DSN +is what is used to access the datasource.

    + +By default the ODBC searches for GEOMETRY_COLUMNS table. If found it is +used to identify the set of spatial tables that should be treated as layers +by OGR. If not found, then all tables in the datasource are returned as +non-spatial layers. However, if a table list (a list of comma seperated table +names) is provided, then only those tables will be represented as layers +(non-spatial). Fetching the full definition of all tables in a complicated +database can be quite timeconsuming, so the ability to restrict the set of +tables accessed is primarily a performance issue.

    + +If the GEOMETRY_COLUMNS table is found, it is used to select a column to +be the geometry source. If the tables are passed in the datasource name, +then the geometry column associated with a table can be included in +round brackets after the tablename. It is currently a hardcoded assumption +that the geometry is in Well Known Binary (WKB) format if the field +is binary, or Well Known Text (WKT) otherwise. The GEOMETRY_COLUMNS +table should have at least the columns F_TABLE_NAME, F_GEOMETRY_COLUMN +and GEOMETRY_TYPE.

    + +If the table has a geometry column, and has fields called XMIN, YMIN, XMAX +and YMAX then direct table queries with a spatial filter accelerate the +spatial query. The XMIN, YMIN, XMAX and YMAX fields should represent the +extent of the geometry in the row in the tables coordinate system.

    + +

    By default, SQL statements are passed directly to the underlying database +engine. It's also possible to request the driver to handle SQL commands +with the OGR SQL engine, by passing +"OGRSQL" string to the ExecuteSQL() method, as name of +the SQL dialect.

    + +

    Access Databases (.MDB) support

    + +Starting with GDAL 1.10, and on Windows provided that the "Microsoft Access Driver (*.mdb)" +ODBC driver is installed, non-spatial MS Access Databases (not Personnal +Geodabases or Geomedia databases) can be opened directly by their filenames. + +

    Creation Issues

    + +Currently the ODBC OGR driver is read-only, so new features, tables and +datasources cannot normally be created by OGR applications. This limitation +may be removed in the future.

    +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/makefile.vc new file mode 100644 index 000000000..5157ff50f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/makefile.vc @@ -0,0 +1,14 @@ + +OBJ = ogrodbcdriver.obj ogrodbcdatasource.obj ogrodbclayer.obj \ + ogrodbctablelayer.obj ogrodbcselectlayer.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. + +default: $(OBJ) + +clean: + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogr_odbc.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogr_odbc.h new file mode 100644 index 000000000..4b71851bb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogr_odbc.h @@ -0,0 +1,235 @@ +/****************************************************************************** + * $Id: ogr_odbc.h 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for OGR/ODBC driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_ODBC_H_INCLUDED +#define _OGR_ODBC_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_odbc.h" +#include "cpl_error.h" + +/************************************************************************/ +/* OGRODBCLayer */ +/************************************************************************/ + +class OGRODBCDataSource; + +class OGRODBCLayer : public OGRLayer +{ + protected: + OGRFeatureDefn *poFeatureDefn; + + CPLODBCStatement *poStmt; + + // Layer spatial reference system, and srid. + OGRSpatialReference *poSRS; + int nSRSId; + + GIntBig iNextShapeId; + + OGRODBCDataSource *poDS; + + int bGeomColumnWKB; + char *pszGeomColumn; + char *pszFIDColumn; + + int *panFieldOrdinals; + + CPLErr BuildFeatureDefn( const char *pszLayerName, + CPLODBCStatement *poStmt ); + + virtual CPLODBCStatement * GetStatement() { return poStmt; } + + public: + OGRODBCLayer(); + virtual ~OGRODBCLayer(); + + virtual void ResetReading(); + virtual OGRFeature *GetNextRawFeature(); + virtual OGRFeature *GetNextFeature(); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRODBCTableLayer */ +/************************************************************************/ + +class OGRODBCTableLayer : public OGRODBCLayer +{ + int bUpdateAccess; + + char *pszQuery; + + int bHaveSpatialExtents; + + void ClearStatement(); + OGRErr ResetStatement(); + + virtual CPLODBCStatement * GetStatement(); + + char *pszTableName; + char *pszSchemaName; + + public: + OGRODBCTableLayer( OGRODBCDataSource * ); + ~OGRODBCTableLayer(); + + CPLErr Initialize( const char *pszTableName, + const char *pszGeomCol ); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual OGRErr SetAttributeFilter( const char * ); +#ifdef notdef + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); +#endif + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int TestCapability( const char * ); + +#ifdef notdef + // follow methods are not base class overrides + void SetLaunderFlag( int bFlag ) + { bLaunderColumnNames = bFlag; } + void SetPrecisionFlag( int bFlag ) + { bPreservePrecision = bFlag; } +#endif +}; + +/************************************************************************/ +/* OGRODBCSelectLayer */ +/************************************************************************/ + +class OGRODBCSelectLayer : public OGRODBCLayer +{ + char *pszBaseStatement; + + void ClearStatement(); + OGRErr ResetStatement(); + + virtual CPLODBCStatement * GetStatement(); + + public: + OGRODBCSelectLayer( OGRODBCDataSource *, + CPLODBCStatement * ); + ~OGRODBCSelectLayer(); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRODBCDataSource */ +/************************************************************************/ + +class OGRODBCDataSource : public OGRDataSource +{ + OGRODBCLayer **papoLayers; + int nLayers; + + char *pszName; + + int bDSUpdate; + CPLODBCSession oSession; + + // We maintain a list of known SRID to reduce the number of trips to + // the database to get SRSes. + int nKnownSRID; + int *panSRID; + OGRSpatialReference **papoSRS; + + int OpenMDB( const char *, int bUpdate ); + + public: + OGRODBCDataSource(); + ~OGRODBCDataSource(); + + int Open( const char *, int bUpdate, int bTestOpen ); + int OpenTable( const char *pszTableName, + const char *pszGeomCol, + int bUpdate ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + // Internal use + CPLODBCSession *GetSession() { return &oSession; } + +}; + +/************************************************************************/ +/* OGRODBCDriver */ +/************************************************************************/ + +class OGRODBCDriver : public OGRSFDriver +{ + public: + ~OGRODBCDriver(); + + const char *GetName(); + OGRDataSource *Open( const char *, int ); + + virtual OGRDataSource *CreateDataSource( const char *pszName, + char ** = NULL ); + + int TestCapability( const char * ); +}; + + +#endif /* ndef _OGR_ODBC_H_INCLUDED */ + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbcdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbcdatasource.cpp new file mode 100644 index 000000000..a74653476 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbcdatasource.cpp @@ -0,0 +1,659 @@ +/****************************************************************************** + * $Id: ogrodbcdatasource.cpp 28368 2015-01-27 14:25:17Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRODBCDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_odbc.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrodbcdatasource.cpp 28368 2015-01-27 14:25:17Z rouault $"); +/************************************************************************/ +/* OGRODBCDataSource() */ +/************************************************************************/ + +OGRODBCDataSource::OGRODBCDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + + nKnownSRID = 0; + panSRID = NULL; + papoSRS = NULL; +} + +/************************************************************************/ +/* ~OGRODBCDataSource() */ +/************************************************************************/ + +OGRODBCDataSource::~OGRODBCDataSource() + +{ + int i; + + CPLFree( pszName ); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + for( i = 0; i < nKnownSRID; i++ ) + { + if( papoSRS[i] != NULL ) + papoSRS[i]->Release(); + } + CPLFree( panSRID ); + CPLFree( papoSRS ); +} + +/************************************************************************/ +/* CheckDSNStringTemplate() */ +/* The string will be used as the formatting argument of sprintf with */ +/* a string in vararg. So let's check there's only one '%s', and nothing*/ +/* else */ +/************************************************************************/ + +static int CheckDSNStringTemplate(const char* pszStr) +{ + int nPercentSFound = FALSE; + while(*pszStr) + { + if (*pszStr == '%') + { + if (pszStr[1] != 's') + { + return FALSE; + } + else + { + if (nPercentSFound) + return FALSE; + nPercentSFound = TRUE; + } + } + pszStr ++; + } + return TRUE; +} + +/************************************************************************/ +/* OpenMDB() */ +/************************************************************************/ + +int OGRODBCDataSource::OpenMDB( const char * pszNewName, int bUpdate ) +{ + const char* pszOptionName = ""; + pszOptionName = "PGEO_DRIVER_TEMPLATE"; + const char* pszDSNStringTemplate = CPLGetConfigOption( pszOptionName, NULL ); + if( pszDSNStringTemplate == NULL ) + { + pszOptionName = "MDB_DRIVER_TEMPLATE"; + pszDSNStringTemplate = CPLGetConfigOption( pszOptionName, NULL ); + if( pszDSNStringTemplate == NULL ) + { + pszOptionName = ""; + pszDSNStringTemplate = "DRIVER=Microsoft Access Driver (*.mdb);DBQ=%s"; + } + } + if (!CheckDSNStringTemplate(pszDSNStringTemplate)) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Illegal value for %s option", pszOptionName ); + return FALSE; + } + char* pszDSN = (char *) CPLMalloc(strlen(pszNewName)+strlen(pszDSNStringTemplate)+100); + sprintf( pszDSN, pszDSNStringTemplate, pszNewName ); + +/* -------------------------------------------------------------------- */ +/* Initialize based on the DSN. */ +/* -------------------------------------------------------------------- */ + CPLDebug( "ODBC", "EstablishSession(%s)", pszDSN ); + + if( !oSession.EstablishSession( pszDSN, NULL, NULL ) ) + { + int bError = TRUE; + if( EQUAL(pszDSN, "") ) + { + // Trying with another template (#5594) + pszDSNStringTemplate = "DRIVER=Microsoft Access Driver (*.mdb, *.accdb);DBQ=%s"; + CPLFree( pszDSN ); + pszDSN = (char *) CPLMalloc(strlen(pszNewName)+strlen(pszDSNStringTemplate)+100); + sprintf( pszDSN, pszDSNStringTemplate, pszNewName ); + CPLDebug( "ODBC", "EstablishSession(%s)", pszDSN ); + if( oSession.EstablishSession( pszDSN, NULL, NULL ) ) + { + bError = FALSE; + } + } + if( bError ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to initialize ODBC connection to DSN for %s,\n" + "%s", pszDSN, oSession.GetLastError() ); + CPLFree( pszDSN ); + return FALSE; + } + } + + CPLFree( pszDSN ); + + pszName = CPLStrdup( pszNewName ); + + bDSUpdate = bUpdate; + +/* -------------------------------------------------------------------- */ +/* Check if it is a PGeo MDB. */ +/* -------------------------------------------------------------------- */ + { + CPLODBCStatement oStmt( &oSession ); + + oStmt.Append( "SELECT TableName, FieldName, ShapeType, ExtentLeft, ExtentRight, ExtentBottom, ExtentTop, SRID, HasZ FROM GDB_GeomColumns" ); + + if( oStmt.ExecuteSQL() ) + { + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Check if it is a Geomedia MDB. */ +/* -------------------------------------------------------------------- */ + { + CPLODBCStatement oStmt( &oSession ); + + oStmt.Append( "SELECT TableName FROM GAliasTable WHERE TableType = 'INGRFeatures'" ); + + if( oStmt.ExecuteSQL() ) + { + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Check if it is a Walk MDB. */ +/* -------------------------------------------------------------------- */ + { + CPLODBCStatement oStmt( &oSession ); + + oStmt.Append( "SELECT LayerID, LayerName, minE, maxE, minN, maxN, Memo FROM WalkLayers" ); + + if( oStmt.ExecuteSQL() ) + { + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Return all tables as non-spatial tables. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oTableList( &oSession ); + + if( oTableList.GetTables() ) + { + while( oTableList.Fetch() ) + { + const char *pszSchema = oTableList.GetColData(1); + CPLString osLayerName; + + if( pszSchema != NULL && strlen(pszSchema) > 0 ) + { + osLayerName = pszSchema; + osLayerName += "."; + } + + osLayerName += oTableList.GetColData(2); + + OpenTable( osLayerName, NULL, bUpdate ); + } + + return TRUE; + } + else + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRODBCDataSource::Open( const char * pszNewName, int bUpdate, + CPL_UNUSED int bTestOpen ) +{ + CPLAssert( nLayers == 0 ); + + if( !EQUALN(pszNewName, "ODBC:",5) && EQUAL(CPLGetExtension(pszNewName), "MDB") ) + return OpenMDB(pszNewName, bUpdate); + +/* -------------------------------------------------------------------- */ +/* Start parsing dataset name from the end of string, fetching */ +/* the name of spatial reference table and names for SRID and */ +/* SRTEXT columns first. */ +/* -------------------------------------------------------------------- */ + char *pszWrkName = CPLStrdup( pszNewName + 5 ); // Skip the 'ODBC:' part + char **papszTables = NULL; + char **papszGeomCol = NULL; + char *pszSRSTableName = NULL; + char *pszSRIDCol = NULL, *pszSRTextCol = NULL; + char *pszDelimiter; + + if ( (pszDelimiter = strrchr( pszWrkName, ':' )) != NULL ) + { + char *pszOBracket = strchr( pszDelimiter + 1, '(' ); + + if( strchr(pszDelimiter,'\\') != NULL + || strchr(pszDelimiter,'/') != NULL ) + { + /* + ** if there are special tokens then this isn't really + ** the srs table name, so avoid further processing. + */ + } + else if( pszOBracket == NULL ) + { + pszSRSTableName = CPLStrdup( pszDelimiter + 1 ); + *pszDelimiter = '\0'; + } + else + { + char *pszCBracket = strchr( pszOBracket, ')' ); + if( pszCBracket != NULL ) + *pszCBracket = '\0'; + + char *pszComma = strchr( pszOBracket, ',' ); + if( pszComma != NULL ) + { + *pszComma = '\0'; + pszSRIDCol = CPLStrdup( pszComma + 1 ); + } + + *pszOBracket = '\0'; + pszSRSTableName = CPLStrdup( pszDelimiter + 1 ); + pszSRTextCol = CPLStrdup( pszOBracket + 1 ); + + *pszDelimiter = '\0'; + } + } + +/* -------------------------------------------------------------------- */ +/* Strip off any comma delimeted set of tables names to access */ +/* from the end of the string first. Also allow an optional */ +/* bracketed geometry column name after the table name. */ +/* -------------------------------------------------------------------- */ + while( (pszDelimiter = strrchr( pszWrkName, ',' )) != NULL ) + { + char *pszOBracket = strstr( pszDelimiter + 1, "(" ); + if( pszOBracket == NULL ) + { + papszTables = CSLAddString( papszTables, pszDelimiter + 1 ); + papszGeomCol = CSLAddString( papszGeomCol, "" ); + } + else + { + char *pszCBracket = strstr(pszOBracket,")"); + + if( pszCBracket != NULL ) + *pszCBracket = '\0'; + + *pszOBracket = '\0'; + papszTables = CSLAddString( papszTables, pszDelimiter + 1 ); + papszGeomCol = CSLAddString( papszGeomCol, pszOBracket+1 ); + } + *pszDelimiter = '\0'; + } + +/* -------------------------------------------------------------------- */ +/* Split out userid, password and DSN. The general form is */ +/* user/password@dsn. But if there are no @ characters the */ +/* whole thing is assumed to be a DSN. */ +/* -------------------------------------------------------------------- */ + char *pszUserid = NULL; + char *pszPassword = NULL; + char *pszDSN = NULL; + + if( strstr(pszWrkName,"@") == NULL ) + { + pszDSN = CPLStrdup( pszWrkName ); + } + else + { + char *pszTarget; + + pszDSN = CPLStrdup(strstr(pszWrkName, "@") + 1); + if( *pszWrkName == '/' ) + { + pszPassword = CPLStrdup(pszWrkName + 1); + pszTarget = strstr(pszPassword,"@"); + *pszTarget = '\0'; + } + else + { + pszUserid = CPLStrdup(pszWrkName); + pszTarget = strstr(pszUserid,"@"); + *pszTarget = '\0'; + + pszTarget = strstr(pszUserid,"/"); + if( pszTarget != NULL ) + { + *pszTarget = '\0'; + pszPassword = CPLStrdup(pszTarget+1); + } + } + } + + CPLFree( pszWrkName ); + +/* -------------------------------------------------------------------- */ +/* Initialize based on the DSN. */ +/* -------------------------------------------------------------------- */ + CPLDebug( "OGR_ODBC", + "EstablishSession(DSN:\"%s\", userid:\"%s\", password:\"%s\")", + pszDSN, pszUserid ? pszUserid : "", + pszPassword ? pszPassword : "" ); + + if( !oSession.EstablishSession( pszDSN, pszUserid, pszPassword ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to initialize ODBC connection to DSN for %s,\n" + "%s", + pszNewName+5, oSession.GetLastError() ); + CSLDestroy( papszTables ); + CSLDestroy( papszGeomCol ); + CPLFree( pszDSN ); + CPLFree( pszUserid ); + CPLFree( pszPassword ); + return FALSE; + } + + CPLFree( pszDSN ); + CPLFree( pszUserid ); + CPLFree( pszPassword ); + + pszName = CPLStrdup( pszNewName ); + + bDSUpdate = bUpdate; + +/* -------------------------------------------------------------------- */ +/* If no explicit list of tables was given, check for a list in */ +/* a geometry_columns table. */ +/* -------------------------------------------------------------------- */ + if( papszTables == NULL ) + { + CPLODBCStatement oStmt( &oSession ); + + oStmt.Append( "SELECT f_table_name, f_geometry_column, geometry_type" + " FROM geometry_columns" ); + if( oStmt.ExecuteSQL() ) + { + while( oStmt.Fetch() ) + { + papszTables = + CSLAddString( papszTables, oStmt.GetColData(0) ); + papszGeomCol = + CSLAddString( papszGeomCol, oStmt.GetColData(1) ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise our final resort is to return all tables as */ +/* non-spatial tables. */ +/* -------------------------------------------------------------------- */ + if( papszTables == NULL ) + { + CPLODBCStatement oTableList( &oSession ); + + if( oTableList.GetTables() ) + { + while( oTableList.Fetch() ) + { + const char *pszSchema = oTableList.GetColData(1); + CPLString osLayerName; + + if( pszSchema != NULL && strlen(pszSchema) > 0 ) + { + osLayerName = pszSchema; + osLayerName += "."; + } + + osLayerName += oTableList.GetColData(2); + + papszTables = CSLAddString( papszTables, osLayerName ); + + papszGeomCol = CSLAddString(papszGeomCol,""); + } + } + } + +/* -------------------------------------------------------------------- */ +/* If we have an explicit list of requested tables, use them */ +/* (non-spatial). */ +/* -------------------------------------------------------------------- */ + for( int iTable = 0; + papszTables != NULL && papszTables[iTable] != NULL; + iTable++ ) + { + if( strlen(papszGeomCol[iTable]) > 0 ) + OpenTable( papszTables[iTable], papszGeomCol[iTable], bUpdate ); + else + OpenTable( papszTables[iTable], NULL, bUpdate ); + } + + CSLDestroy( papszTables ); + CSLDestroy( papszGeomCol ); + +/* -------------------------------------------------------------------- */ +/* If no explicit list of tables was given, check for a list in */ +/* a geometry_columns table. */ +/* -------------------------------------------------------------------- */ + if ( pszSRSTableName ) + { + CPLODBCStatement oSRSList( &oSession ); + + if ( !pszSRTextCol ) + pszSRTextCol = CPLStrdup( "srtext" ); + if ( !pszSRIDCol ) + pszSRIDCol = CPLStrdup( "srid" ); + + oSRSList.Append( "SELECT " ); + oSRSList.Append( pszSRIDCol ); + oSRSList.Append( "," ); + oSRSList.Append( pszSRTextCol ); + oSRSList.Append( " FROM " ); + oSRSList.Append( pszSRSTableName ); + + CPLDebug( "OGR_ODBC", "ExecuteSQL(%s) to read SRS table", + oSRSList.GetCommand() ); + if ( oSRSList.ExecuteSQL() ) + { + int nRows = 256; // A reasonable number of SRIDs to start from + panSRID = (int *)CPLMalloc( nRows * sizeof(int) ); + papoSRS = (OGRSpatialReference **) + CPLMalloc( nRows * sizeof(OGRSpatialReference*) ); + + while ( oSRSList.Fetch() ) + { + char *pszSRID = (char *) oSRSList.GetColData( pszSRIDCol ); + if ( !pszSRID ) + continue; + + char *pszSRText = (char *) oSRSList.GetColData( pszSRTextCol ); + + if ( pszSRText ) + { + if ( nKnownSRID > nRows ) + { + nRows *= 2; + panSRID = (int *)CPLRealloc( panSRID, + nRows * sizeof(int) ); + papoSRS = (OGRSpatialReference **) + CPLRealloc( papoSRS, + nRows * sizeof(OGRSpatialReference*) ); + } + panSRID[nKnownSRID] = atoi( pszSRID ); + papoSRS[nKnownSRID] = new OGRSpatialReference(); + if ( papoSRS[nKnownSRID]->importFromWkt( &pszSRText ) + != OGRERR_NONE ) + { + delete papoSRS[nKnownSRID]; + continue; + } + nKnownSRID++; + } + } + } + } + + if ( pszSRIDCol ) + CPLFree( pszSRIDCol ); + if ( pszSRTextCol ) + CPLFree( pszSRTextCol ); + if ( pszSRSTableName ) + CPLFree( pszSRSTableName ); + + return TRUE; +} + +/************************************************************************/ +/* OpenTable() */ +/************************************************************************/ + +int OGRODBCDataSource::OpenTable( const char *pszNewName, + const char *pszGeomCol, + CPL_UNUSED int bUpdate ) +{ +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRODBCTableLayer *poLayer; + + poLayer = new OGRODBCTableLayer( this ); + + if( poLayer->Initialize( pszNewName, pszGeomCol ) ) + { + delete poLayer; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRODBCLayer **) + CPLRealloc( papoLayers, sizeof(OGRODBCLayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + + return TRUE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRODBCDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRODBCDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRODBCDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Execute statement. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement *poStmt = new CPLODBCStatement( &oSession ); + + CPLDebug( "ODBC", "ExecuteSQL(%s) called.", pszSQLCommand ); + poStmt->Append( pszSQLCommand ); + if( !poStmt->ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", oSession.GetLastError() ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Are there result columns for this statement? */ +/* -------------------------------------------------------------------- */ + if( poStmt->GetColCount() == 0 ) + { + delete poStmt; + CPLErrorReset(); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a results layer. It will take ownership of the */ +/* statement. */ +/* -------------------------------------------------------------------- */ + OGRODBCSelectLayer *poLayer = NULL; + + poLayer = new OGRODBCSelectLayer( this, poStmt ); + + if( poSpatialFilter != NULL ) + poLayer->SetSpatialFilter( poSpatialFilter ); + + return poLayer; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRODBCDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbcdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbcdriver.cpp new file mode 100644 index 000000000..dbce23272 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbcdriver.cpp @@ -0,0 +1,132 @@ +/****************************************************************************** + * $Id: ogrodbcdriver.cpp 24957 2012-09-23 17:03:30Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRODBCDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_odbc.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrodbcdriver.cpp 24957 2012-09-23 17:03:30Z rouault $"); + +/************************************************************************/ +/* ~OGRODBCDriver() */ +/************************************************************************/ + +OGRODBCDriver::~OGRODBCDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRODBCDriver::GetName() + +{ + return "ODBC"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRODBCDriver::Open( const char * pszFilename, + int bUpdate ) + +{ + OGRODBCDataSource *poDS; + + if( !EQUALN(pszFilename,"ODBC:",5) +#ifdef WIN32 + && !EQUAL(CPLGetExtension(pszFilename), "MDB") +#endif + ) + return NULL; + + poDS = new OGRODBCDataSource(); + + if( !poDS->Open( pszFilename, bUpdate, TRUE ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRODBCDriver::CreateDataSource( const char * pszName, + char ** /* papszOptions */ ) + +{ + OGRODBCDataSource *poDS; + + if( !EQUALN(pszName,"ODBC:",5) ) + return NULL; + + poDS = new OGRODBCDataSource(); + + + if( !poDS->Open( pszName, TRUE, TRUE ) ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "ODBC driver doesn't currently support database creation.\n" + "Please create database with the `createdb' command." ); + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRODBCDriver::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODrCCreateDataSource) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRODBC() */ +/************************************************************************/ + +void RegisterOGRODBC() + +{ + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( new OGRODBCDriver ); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbclayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbclayer.cpp new file mode 100644 index 000000000..9651982e7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbclayer.cpp @@ -0,0 +1,366 @@ +/****************************************************************************** + * $Id: ogrodbclayer.cpp 29014 2015-04-25 18:14:49Z tamas $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRODBCLayer class, code shared between + * the direct table access, and the generic SQL results. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_odbc.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrodbclayer.cpp 29014 2015-04-25 18:14:49Z tamas $"); + +/************************************************************************/ +/* OGRODBCLayer() */ +/************************************************************************/ + +OGRODBCLayer::OGRODBCLayer() + +{ + poDS = NULL; + + bGeomColumnWKB = FALSE; + pszGeomColumn = NULL; + pszFIDColumn = NULL; + panFieldOrdinals = NULL; + + poStmt = NULL; + + iNextShapeId = 0; + + poSRS = NULL; + nSRSId = -2; // we haven't even queried the database for it yet. +} + +/************************************************************************/ +/* ~OGRODBCLayer() */ +/************************************************************************/ + +OGRODBCLayer::~OGRODBCLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "OGR_ODBC", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poStmt ) + { + delete poStmt; + poStmt = NULL; + } + + if( pszGeomColumn ) + CPLFree( pszGeomColumn ); + + if ( panFieldOrdinals ) + CPLFree( panFieldOrdinals ); + + if( poFeatureDefn ) + { + poFeatureDefn->Release(); + poFeatureDefn = NULL; + } + + if( poSRS ) + poSRS->Release(); +} + +/************************************************************************/ +/* BuildFeatureDefn() */ +/* */ +/* Build feature definition from a set of column definitions */ +/* set on a statement. Sift out geometry and FID fields. */ +/************************************************************************/ + +CPLErr OGRODBCLayer::BuildFeatureDefn( const char *pszLayerName, + CPLODBCStatement *poStmt ) + +{ + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + int nRawColumns = poStmt->GetColCount(); + + poFeatureDefn->Reference(); + + panFieldOrdinals = (int *) CPLMalloc( sizeof(int) * nRawColumns ); + + for( int iCol = 0; iCol < nRawColumns; iCol++ ) + { + OGRFieldDefn oField( poStmt->GetColName(iCol), OFTString ); + + oField.SetWidth( MAX(0,poStmt->GetColSize( iCol )) ); + + if( pszGeomColumn != NULL + && EQUAL(poStmt->GetColName(iCol),pszGeomColumn) ) + continue; + + switch( CPLODBCStatement::GetTypeMapping(poStmt->GetColType(iCol)) ) + { + case SQL_C_SSHORT: + case SQL_C_USHORT: + case SQL_C_SLONG: + case SQL_C_ULONG: + oField.SetType( OFTInteger ); + break; + + case SQL_C_SBIGINT: + case SQL_C_UBIGINT: + oField.SetType( OFTInteger64 ); + break; + + case SQL_C_BINARY: + oField.SetType( OFTBinary ); + break; + + case SQL_C_NUMERIC: + oField.SetType( OFTReal ); + oField.SetPrecision( poStmt->GetColPrecision(iCol) ); + break; + + case SQL_C_FLOAT: + case SQL_C_DOUBLE: + oField.SetType( OFTReal ); + oField.SetWidth( 0 ); + break; + + case SQL_C_DATE: + oField.SetType( OFTDate ); + break; + + case SQL_C_TIME: + oField.SetType( OFTTime ); + break; + + case SQL_C_TIMESTAMP: + oField.SetType( OFTDateTime ); + break; + + default: + /* leave it as OFTString */; + } + + poFeatureDefn->AddFieldDefn( &oField ); + panFieldOrdinals[poFeatureDefn->GetFieldCount() - 1] = iCol+1; + } + +/* -------------------------------------------------------------------- */ +/* If we don't already have an FID, check if there is a special */ +/* FID named column available. */ +/* -------------------------------------------------------------------- */ + if( pszFIDColumn == NULL ) + { + const char *pszOGR_FID = CPLGetConfigOption("ODBC_OGR_FID","OGR_FID"); + if( poFeatureDefn->GetFieldIndex( pszOGR_FID ) != -1 ) + pszFIDColumn = CPLStrdup(pszOGR_FID); + } + + if( pszFIDColumn != NULL ) + CPLDebug( "OGR_ODBC", "Using column %s as FID for table %s.", + pszFIDColumn, poFeatureDefn->GetName() ); + else + CPLDebug( "OGR_ODBC", "Table %s has no identified FID column.", + poFeatureDefn->GetName() ); + + return CE_None; +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRODBCLayer::ResetReading() + +{ + iNextShapeId = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRODBCLayer::GetNextFeature() + +{ + for( ; TRUE; ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRODBCLayer::GetNextRawFeature() + +{ + if( GetStatement() == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we are marked to restart then do so, and fetch a record. */ +/* -------------------------------------------------------------------- */ + if( !poStmt->Fetch() ) + { + delete poStmt; + poStmt = NULL; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a feature from the current result. */ +/* -------------------------------------------------------------------- */ + int iField; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + if( pszFIDColumn != NULL && poStmt->GetColId(pszFIDColumn) > -1 ) + poFeature->SetFID( + atoi(poStmt->GetColData(poStmt->GetColId(pszFIDColumn))) ); + else + poFeature->SetFID( iNextShapeId ); + + iNextShapeId++; + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Set the fields. */ +/* -------------------------------------------------------------------- */ + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + int iSrcField = panFieldOrdinals[iField]-1; + const char *pszValue = poStmt->GetColData( iSrcField ); + + if( pszValue == NULL ) + /* no value */; + else if( poFeature->GetFieldDefnRef(iField)->GetType() == OFTBinary ) + poFeature->SetField( iField, + poStmt->GetColDataLength(iSrcField), + (GByte *) pszValue ); + else + poFeature->SetField( iField, pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Try to extract a geometry. */ +/* -------------------------------------------------------------------- */ + if( pszGeomColumn != NULL ) + { + int iField = poStmt->GetColId( pszGeomColumn ); + const char *pszGeomText = poStmt->GetColData( iField ); + OGRGeometry *poGeom = NULL; + OGRErr eErr = OGRERR_NONE; + + if( pszGeomText != NULL && !bGeomColumnWKB ) + { + eErr = + OGRGeometryFactory::createFromWkt((char **) &pszGeomText, + NULL, &poGeom); + } + else if( pszGeomText != NULL && bGeomColumnWKB ) + { + int nLength = poStmt->GetColDataLength( iField ); + + eErr = + OGRGeometryFactory::createFromWkb((unsigned char *) pszGeomText, + NULL, &poGeom, nLength); + } + + if ( eErr != OGRERR_NONE ) + { + const char *pszMessage; + + switch ( eErr ) + { + case OGRERR_NOT_ENOUGH_DATA: + pszMessage = "Not enough data to deserialize"; + break; + case OGRERR_UNSUPPORTED_GEOMETRY_TYPE: + pszMessage = "Unsupported geometry type"; + break; + case OGRERR_CORRUPT_DATA: + pszMessage = "Corrupt data"; + break; + default: + pszMessage = "Unrecognized error"; + } + CPLError(CE_Failure, CPLE_AppDefined, + "GetNextRawFeature(): %s", pszMessage); + } + + if( poGeom != NULL ) + poFeature->SetGeometryDirectly( poGeom ); + } + + return poFeature; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRODBCLayer::GetFeature( GIntBig nFeatureId ) + +{ + /* This should be implemented directly! */ + + return OGRLayer::GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRODBCLayer::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRODBCLayer::GetSpatialRef() + +{ + return poSRS; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbcselectlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbcselectlayer.cpp new file mode 100644 index 000000000..7e99a25e0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbcselectlayer.cpp @@ -0,0 +1,176 @@ +/****************************************************************************** + * $Id: ogrodbcselectlayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRODBCSelectLayer class, layer access to the results + * of a SELECT statement executed via ExecuteSQL(). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_odbc.h" + +CPL_CVSID("$Id: ogrodbcselectlayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); +/************************************************************************/ +/* OGRODBCSelectLayer() */ +/************************************************************************/ + +OGRODBCSelectLayer::OGRODBCSelectLayer( OGRODBCDataSource *poDSIn, + CPLODBCStatement * poStmtIn ) + +{ + poDS = poDSIn; + + iNextShapeId = 0; + nSRSId = -1; + poFeatureDefn = NULL; + + poStmt = poStmtIn; + pszBaseStatement = CPLStrdup( poStmtIn->GetCommand() ); + + BuildFeatureDefn( "SELECT", poStmt ); +} + +/************************************************************************/ +/* ~OGRODBCSelectLayer() */ +/************************************************************************/ + +OGRODBCSelectLayer::~OGRODBCSelectLayer() + +{ + ClearStatement(); +} + +/************************************************************************/ +/* ClearStatement() */ +/************************************************************************/ + +void OGRODBCSelectLayer::ClearStatement() + +{ + if( poStmt != NULL ) + { + delete poStmt; + poStmt = NULL; + } +} + +/************************************************************************/ +/* GetStatement() */ +/************************************************************************/ + +CPLODBCStatement *OGRODBCSelectLayer::GetStatement() + +{ + if( poStmt == NULL ) + ResetStatement(); + + return poStmt; +} + +/************************************************************************/ +/* ResetStatement() */ +/************************************************************************/ + +OGRErr OGRODBCSelectLayer::ResetStatement() + +{ + ClearStatement(); + + iNextShapeId = 0; + + CPLDebug( "OGR_ODBC", "Recreating statement." ); + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( pszBaseStatement ); + + if( poStmt->ExecuteSQL() ) + return OGRERR_NONE; + else + { + delete poStmt; + poStmt = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRODBCSelectLayer::ResetReading() + +{ + if( iNextShapeId != 0 ) + ClearStatement(); + + OGRODBCLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRODBCSelectLayer::GetFeature( GIntBig nFeatureId ) + +{ + return OGRODBCLayer::GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRODBCSelectLayer::TestCapability( const char * pszCap ) + +{ + return OGRODBCLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetExtent() */ +/* */ +/* Since SELECT layers currently cannot ever have geometry, we */ +/* can optimize the GetExtent() method! */ +/************************************************************************/ + +OGRErr OGRODBCSelectLayer::GetExtent(OGREnvelope *, int ) + +{ + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRODBCSelectLayer::GetFeatureCount( int bForce ) + +{ + return OGRODBCLayer::GetFeatureCount( bForce ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbctablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbctablelayer.cpp new file mode 100644 index 000000000..f74c6bd4c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/odbc/ogrodbctablelayer.cpp @@ -0,0 +1,417 @@ +/****************************************************************************** + * $Id: ogrodbctablelayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRODBCTableLayer class, access to an existing table. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_odbc.h" + +CPL_CVSID("$Id: ogrodbctablelayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); +/************************************************************************/ +/* OGRODBCTableLayer() */ +/************************************************************************/ + +OGRODBCTableLayer::OGRODBCTableLayer( OGRODBCDataSource *poDSIn ) + +{ + poDS = poDSIn; + + pszQuery = NULL; + + bUpdateAccess = TRUE; + bHaveSpatialExtents = FALSE; + + iNextShapeId = 0; + + nSRSId = -1; + + poFeatureDefn = NULL; + + pszTableName = NULL; + pszSchemaName = NULL; +} + +/************************************************************************/ +/* ~OGRODBCTableLayer() */ +/************************************************************************/ + +OGRODBCTableLayer::~OGRODBCTableLayer() + +{ + CPLFree( pszTableName ); + CPLFree( pszSchemaName ); + + CPLFree( pszQuery ); + ClearStatement(); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +CPLErr OGRODBCTableLayer::Initialize( const char *pszLayerName, + const char *pszGeomCol ) + +{ + CPLODBCSession *poSession = poDS->GetSession(); + + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + + SetDescription( pszLayerName ); + +/* -------------------------------------------------------------------- */ +/* Parse out schema name if present in layer. We assume a */ +/* schema is provided if there is a dot in the name, and that */ +/* it is in the form . */ +/* -------------------------------------------------------------------- */ + const char *pszDot = strstr(pszLayerName,"."); + if( pszDot != NULL ) + { + pszTableName = CPLStrdup(pszDot + 1); + pszSchemaName = CPLStrdup(pszLayerName); + pszSchemaName[pszDot - pszLayerName] = '\0'; + } + else + { + pszTableName = CPLStrdup(pszLayerName); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a simple primary key? */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oGetKey( poSession ); + + if( oGetKey.GetPrimaryKeys( pszTableName, NULL, pszSchemaName ) + && oGetKey.Fetch() ) + { + pszFIDColumn = CPLStrdup(oGetKey.GetColData( 3 )); + + if( oGetKey.Fetch() ) // more than one field in key! + { + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + + CPLDebug( "OGR_ODBC", "Table %s has multiple primary key fields, " + "ignoring them all.", pszTableName ); + } + } + +/* -------------------------------------------------------------------- */ +/* Have we been provided a geometry column? */ +/* -------------------------------------------------------------------- */ + CPLFree( pszGeomColumn ); + if( pszGeomCol == NULL ) + pszGeomColumn = NULL; + else + pszGeomColumn = CPLStrdup( pszGeomCol ); + +/* -------------------------------------------------------------------- */ +/* Get the column definitions for this table. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oGetCol( poSession ); + CPLErr eErr; + + if( !oGetCol.GetColumns( pszTableName, NULL, pszSchemaName ) ) + return CE_Failure; + + eErr = BuildFeatureDefn( pszLayerName, &oGetCol ); + if( eErr != CE_None ) + return eErr; + + if( poFeatureDefn->GetFieldCount() == 0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "No column definitions found for table '%s', layer not usable.", + pszLayerName ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Do we have XMIN, YMIN, XMAX, YMAX extent fields? */ +/* -------------------------------------------------------------------- */ + if( poFeatureDefn->GetFieldIndex( "XMIN" ) != -1 + && poFeatureDefn->GetFieldIndex( "XMAX" ) != -1 + && poFeatureDefn->GetFieldIndex( "YMIN" ) != -1 + && poFeatureDefn->GetFieldIndex( "YMAX" ) != -1 ) + { + bHaveSpatialExtents = TRUE; + CPLDebug( "OGR_ODBC", "Table %s has geometry extent fields.", + pszLayerName ); + } + +/* -------------------------------------------------------------------- */ +/* If we got a geometry column, does it exist? Is it binary? */ +/* -------------------------------------------------------------------- */ + if( pszGeomColumn != NULL ) + { + int iColumn = oGetCol.GetColId( pszGeomColumn ); + if( iColumn < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Column %s requested for geometry, but it does not exist.", + pszGeomColumn ); + CPLFree( pszGeomColumn ); + pszGeomColumn = NULL; + } + else + { + if( CPLODBCStatement::GetTypeMapping( + oGetCol.GetColType( iColumn )) == SQL_C_BINARY ) + bGeomColumnWKB = TRUE; + } + } + + + return CE_None; +} + +/************************************************************************/ +/* ClearStatement() */ +/************************************************************************/ + +void OGRODBCTableLayer::ClearStatement() + +{ + if( poStmt != NULL ) + { + delete poStmt; + poStmt = NULL; + } +} + +/************************************************************************/ +/* GetStatement() */ +/************************************************************************/ + +CPLODBCStatement *OGRODBCTableLayer::GetStatement() + +{ + if( poStmt == NULL ) + ResetStatement(); + + return poStmt; +} + +/************************************************************************/ +/* ResetStatement() */ +/************************************************************************/ + +OGRErr OGRODBCTableLayer::ResetStatement() + +{ + ClearStatement(); + + iNextShapeId = 0; + + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( "SELECT * FROM " ); + poStmt->Append( poFeatureDefn->GetName() ); + + /* Append attribute query if we have it */ + if( pszQuery != NULL ) + poStmt->Appendf( " WHERE %s", pszQuery ); + + /* If we have a spatial filter, and per record extents, query on it */ + if( m_poFilterGeom != NULL && bHaveSpatialExtents ) + { + if( pszQuery == NULL ) + poStmt->Append( " WHERE" ); + else + poStmt->Append( " AND" ); + + poStmt->Appendf( " XMAX > %.8f AND XMIN < %.8f" + " AND YMAX > %.8f AND YMIN < %.8f", + m_sFilterEnvelope.MinX, m_sFilterEnvelope.MaxX, + m_sFilterEnvelope.MinY, m_sFilterEnvelope.MaxY ); + } + + CPLDebug( "OGR_ODBC", "ExecuteSQL(%s)", poStmt->GetCommand() ); + if( poStmt->ExecuteSQL() ) + return OGRERR_NONE; + else + { + delete poStmt; + poStmt = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRODBCTableLayer::ResetReading() + +{ + ClearStatement(); + OGRODBCLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRODBCTableLayer::GetFeature( GIntBig nFeatureId ) + +{ + if( pszFIDColumn == NULL ) + return OGRODBCLayer::GetFeature( nFeatureId ); + + ClearStatement(); + + iNextShapeId = nFeatureId; + + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( "SELECT * FROM " ); + poStmt->Append( poFeatureDefn->GetName() ); + poStmt->Appendf( " WHERE %s = " CPL_FRMT_GIB, pszFIDColumn, nFeatureId ); + + if( !poStmt->ExecuteSQL() ) + { + delete poStmt; + poStmt = NULL; + return NULL; + } + + return GetNextRawFeature(); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRODBCTableLayer::SetAttributeFilter( const char *pszQuery ) + +{ + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + + if( (pszQuery == NULL && this->pszQuery == NULL) + || (pszQuery != NULL && this->pszQuery != NULL + && EQUAL(pszQuery,this->pszQuery)) ) + return OGRERR_NONE; + + CPLFree( this->pszQuery ); + this->pszQuery = (pszQuery != NULL ) ? CPLStrdup( pszQuery ) : NULL; + + ClearStatement(); + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRODBCTableLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else + return OGRODBCLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRODBCTableLayer::GetFeatureCount( int bForce ) + +{ + if( m_poFilterGeom != NULL ) + return OGRODBCLayer::GetFeatureCount( bForce ); + + CPLODBCStatement oStmt( poDS->GetSession() ); + oStmt.Append( "SELECT COUNT(*) FROM " ); + oStmt.Append( poFeatureDefn->GetName() ); + + if( pszQuery != NULL ) + oStmt.Appendf( " WHERE %s", pszQuery ); + + if( !oStmt.ExecuteSQL() || !oStmt.Fetch() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GetFeatureCount() failed on query %s.\n%s", + oStmt.GetCommand(), poDS->GetSession()->GetLastError() ); + return OGRODBCLayer::GetFeatureCount(bForce); + } + + return CPLAtoGIntBig(oStmt.GetColData(0)); +} + +/************************************************************************/ +/* GetSpatialRef() */ +/* */ +/* We override this to try and fetch the table SRID from the */ +/* geometry_columns table if the srsid is -2 (meaning we */ +/* haven't yet even looked for it). */ +/************************************************************************/ + +OGRSpatialReference *OGRODBCTableLayer::GetSpatialRef() + +{ +#ifdef notdef + if( nSRSId == -2 ) + { + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult; + char szCommand[1024]; + + nSRSId = -1; + + poDS->SoftStartTransaction(); + + sprintf( szCommand, + "SELECT srid FROM geometry_columns " + "WHERE f_table_name = '%s'", + poFeatureDefn->GetName() ); + hResult = PQexec(hPGConn, szCommand ); + + if( hResult + && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) == 1 ) + { + nSRSId = atoi(PQgetvalue(hResult,0,0)); + } + + poDS->SoftCommit(); + } +#endif + + return OGRODBCLayer::GetSpatialRef(); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/GNUmakefile new file mode 100644 index 000000000..a0884e7f4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/GNUmakefile @@ -0,0 +1,32 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrodsdriver.o ogrodsdatasource.o ods_formula.o ods_formula_parser.o ods_formula_node.o + +ifeq ($(HAVE_EXPAT),yes) +CPPFLAGS += -DHAVE_EXPAT +endif + +CPPFLAGS := -I.. -I../.. -I../mem $(EXPAT_INCLUDE) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) testparser$(EXE) + +$(O_OBJ): ogr_ods.h ods_formula.h ods_formula_parser.hpp ../mem/ogr_mem.h + +# The sed substition below workarounds a bug with gcc 4.1 -O2 (checked on 64bit platforms) +# that produces buggy compiled code. +# Seen on gcc 4.1.2-27ubuntu1 (Ubuntu 10.04) (not the default compiler) and gcc-4.1.2-48.el5 (CentOS 5.5) +# (default compiler...) +# The memset isn't necessary at all with a non-buggy compiler, but I've found +# that it helps gcc 4.1 generating correct code here... +parser: + bison -p ods_formula -d -oods_formula_parser.cpp ods_formula_parser.y + sed "s/yytype_int16 yyssa\[YYINITDEPTH\];/yytype_int16 yyssa[YYINITDEPTH]; \/\* workaround bug with gcc 4.1 -O2 \*\/ memset(yyssa, 0, sizeof(yyssa));/" < ods_formula_parser.cpp > ods_formula_parser.cpp.tmp + mv ods_formula_parser.cpp.tmp ods_formula_parser.cpp + +testparser$(EXE): testparser.$(OBJ_EXT) + $(LD) $(LDFLAGS) testparser.$(OBJ_EXT) $(CONFIG_LIBS) -o testparser$(EXE) \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/drv_ods.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/drv_ods.html new file mode 100644 index 000000000..82adc4019 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/drv_ods.html @@ -0,0 +1,34 @@ + + +ODS - Open Document Spreadsheet + + + + +

    ODS - Open Document Spreadsheet

    + +(GDAL/OGR >= 1.10.0)

    + +This driver can read, write and update spreadsheets in Open Document Spreadsheet format, used by applications like OpenOffice / LibreOffice / KSpread / etc...

    + +The driver is only available if GDAL/OGR is compiled against the Expat library.

    + +Each sheet is presented as a OGR layer. No geometry support is available directly (but you may use the OGR VRT capabilities for that).

    + +Note 1 : spreadsheets with passwords are not supported.

    + +Note 2 : when updating an existing document, all existing styles, formatting, formulas and other concepts (charts, drawings, macros, ...) +not understood by OGR will be lost : the document is re-written from scratch from the OGR data model.

    + +

    Configuration options

    + +
      +
    • OGR_ODS_HEADERS = FORCE / DISABLE / AUTO : By default, the driver will read the first lines of each sheet to detect if the +first line might be the name of columns. If set to FORCE, the driver will consider the first line will be taken as the header line. +If set to DISABLE, it will be considered as the first feature. Otherwise auto-dection will occur.
    • +
    • OGR_ODS_FIELD_TYPES = STRING / AUTO : By default, the driver will try to detect the data type of fields. If set to STRING, +all fields will be of String type.
    • +
    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/makefile.vc new file mode 100644 index 000000000..3f27728a3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/makefile.vc @@ -0,0 +1,20 @@ + +OBJ = ogrodsdriver.obj ogrodsdatasource.obj ods_formula.obj ods_formula_parser.obj ods_formula_node.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF EXPAT_DIR +EXTRAFLAGS = -I.. -I..\.. -I..\mem $(EXPAT_INCLUDE) -DHAVE_EXPAT=1 +!ELSE +EXTRAFLAGS = -I.. -I..\.. -I..\mem +!ENDIF + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula.cpp new file mode 100644 index 000000000..9a9d2d35a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula.cpp @@ -0,0 +1,368 @@ +/****************************************************************************** + * $Id: ods_formula.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Component: ODS formula Engine + * Purpose: + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include + +#include "cpl_conv.h" +#include "ods_formula.h" +#include "ods_formula_parser.hpp" + +#define YYSTYPE ods_formula_node* + +static const SingleOpStruct apsSingleOp[] = +{ + { "ABS", ODS_ABS, fabs }, + { "SQRT", ODS_SQRT, sqrt }, + { "COS", ODS_COS, cos }, + { "SIN", ODS_SIN, sin }, + { "TAN", ODS_TAN, tan }, + { "ACOS", ODS_ACOS, acos }, + { "ASIN", ODS_ASIN, asin }, + { "ATAN", ODS_ATAN, atan }, + { "EXP", ODS_EXP, exp }, + { "LN", ODS_LN, log }, + { "LOG", ODS_LOG, log10 }, + { "LOG10", ODS_LOG, log10 }, +}; + +const SingleOpStruct* ODSGetSingleOpEntry(const char* pszName) +{ + for(size_t i = 0; i < sizeof(apsSingleOp) / sizeof(apsSingleOp[0]); i++) + { + if (EQUAL(pszName, apsSingleOp[i].pszName)) + return &apsSingleOp[i]; + } + return NULL; +} + +const SingleOpStruct* ODSGetSingleOpEntry(ods_formula_op eOp) +{ + for(size_t i = 0; i < sizeof(apsSingleOp) / sizeof(apsSingleOp[0]); i++) + { + if (eOp == apsSingleOp[i].eOp) + return &apsSingleOp[i]; + } + return NULL; +} + +/************************************************************************/ +/* swqlex() */ +/* */ +/* Read back a token from the input. */ +/************************************************************************/ + +int ods_formulalex( YYSTYPE *ppNode, ods_formula_parse_context *context ) +{ + const char *pszInput = context->pszNext; + + *ppNode = NULL; + +/* -------------------------------------------------------------------- */ +/* Do we have a start symbol to return? */ +/* -------------------------------------------------------------------- */ + if( context->nStartToken != 0 ) + { + int nRet = context->nStartToken; + context->nStartToken = 0; + return nRet; + } + +/* -------------------------------------------------------------------- */ +/* Skip white space. */ +/* -------------------------------------------------------------------- */ + while( *pszInput == ' ' || *pszInput == '\t' + || *pszInput == 10 || *pszInput == 13 ) + pszInput++; + + if( *pszInput == '\0' ) + { + context->pszNext = pszInput; + return EOF; + } + +/* -------------------------------------------------------------------- */ +/* Handle string constants. */ +/* -------------------------------------------------------------------- */ + if( *pszInput == '"' ) + { + char *token; + int i_token; + + pszInput++; + + token = (char *) CPLMalloc(strlen(pszInput)+1); + i_token = 0; + + while( *pszInput != '\0' ) + { + if( *pszInput == '\\' && pszInput[1] == '"' ) + pszInput++; + else if( *pszInput == '\\' && pszInput[1] == '\'' ) + pszInput++; + else if( *pszInput == '\'' && pszInput[1] == '\'' ) + pszInput++; + else if( *pszInput == '"' ) + { + pszInput++; + break; + } + else if( *pszInput == '\'' ) + { + pszInput++; + break; + } + + token[i_token++] = *(pszInput++); + } + token[i_token] = '\0'; + + *ppNode = new ods_formula_node( token ); + CPLFree( token ); + + context->pszNext = pszInput; + + return ODST_STRING; + } + +/* -------------------------------------------------------------------- */ +/* Handle numbers. */ +/* -------------------------------------------------------------------- */ + else if( *pszInput >= '0' && *pszInput <= '9' ) + { + CPLString osToken; + const char *pszNext = pszInput + 1; + + osToken += *pszInput; + + // collect non-decimal part of number + while( *pszNext >= '0' && *pszNext <= '9' ) + osToken += *(pszNext++); + + // collect decimal places. + if( *pszNext == '.' ) + { + osToken += *(pszNext++); + while( *pszNext >= '0' && *pszNext <= '9' ) + osToken += *(pszNext++); + } + + // collect exponent + if( *pszNext == 'e' || *pszNext == 'E' ) + { + osToken += *(pszNext++); + if( *pszNext == '-' || *pszNext == '+' ) + osToken += *(pszNext++); + while( *pszNext >= '0' && *pszNext <= '9' ) + osToken += *(pszNext++); + } + + context->pszNext = pszNext; + + if( strstr(osToken,".") + || strstr(osToken,"e") + || strstr(osToken,"E") ) + { + *ppNode = new ods_formula_node( CPLAtof(osToken) ); + } + else + { + *ppNode = new ods_formula_node( atoi(osToken) ); + } + + return ODST_NUMBER; + } + +/* -------------------------------------------------------------------- */ +/* Handle alpha-numerics. */ +/* -------------------------------------------------------------------- */ + else if( *pszInput == '.' || isalnum( *pszInput ) ) + { + int nReturn = ODST_IDENTIFIER; + CPLString osToken; + const char *pszNext = pszInput + 1; + + osToken += *pszInput; + + // collect text characters + while( isalnum( *pszNext ) || *pszNext == '_' + || ((unsigned char) *pszNext) > 127 ) + osToken += *(pszNext++); + + context->pszNext = pszNext; + + /* Constants */ + if( EQUAL(osToken,"TRUE") ) + { + *ppNode = new ods_formula_node( 1 ); + return ODST_NUMBER; + } + else if( EQUAL(osToken,"FALSE") ) + { + *ppNode = new ods_formula_node( 0 ); + return ODST_NUMBER; + } + + else if( EQUAL(osToken,"NOT") ) + nReturn = ODST_NOT; + else if( EQUAL(osToken,"AND") ) + nReturn = ODST_AND; + else if( EQUAL(osToken,"OR") ) + nReturn = ODST_OR; + else if( EQUAL(osToken,"IF") ) + nReturn = ODST_IF; + + /* No-arg functions */ + else if( EQUAL(osToken,"PI") ) + { + *ppNode = new ods_formula_node( ODS_PI ); + return ODST_FUNCTION_NO_ARG; + } + + /* Single-arg functions */ + else if( EQUAL(osToken,"LEN") ) + { + *ppNode = new ods_formula_node( ODS_LEN ); + return ODST_FUNCTION_SINGLE_ARG; + } + /* + else if( EQUAL(osToken,"T") ) + { + *ppNode = new ods_formula_node( ODS_T ); + return ODST_FUNCTION_SINGLE_ARG; + }*/ + + /* Tow-arg functions */ + else if( EQUAL(osToken,"MOD") ) + { + *ppNode = new ods_formula_node( ODS_MODULUS ); + return ODST_FUNCTION_TWO_ARG; + } + else if( EQUAL(osToken,"LEFT") ) + { + *ppNode = new ods_formula_node( ODS_LEFT ); + return ODST_FUNCTION_TWO_ARG; + } + else if( EQUAL(osToken,"RIGHT") ) + { + *ppNode = new ods_formula_node( ODS_RIGHT ); + return ODST_FUNCTION_TWO_ARG; + } + + /* Three-arg functions */ + else if( EQUAL(osToken,"MID") ) + { + *ppNode = new ods_formula_node( ODS_MID ); + return ODST_FUNCTION_THREE_ARG; + } + + /* Multiple-arg functions */ + else if( EQUAL(osToken,"SUM") ) + { + *ppNode = new ods_formula_node( ODS_SUM ); + nReturn = ODST_FUNCTION_ARG_LIST; + } + else if( EQUAL(osToken,"AVERAGE") ) + { + *ppNode = new ods_formula_node( ODS_AVERAGE ); + nReturn = ODST_FUNCTION_ARG_LIST; + } + else if( EQUAL(osToken,"MIN") ) + { + *ppNode = new ods_formula_node( ODS_MIN ); + nReturn = ODST_FUNCTION_ARG_LIST; + } + else if( EQUAL(osToken,"MAX") ) + { + *ppNode = new ods_formula_node( ODS_MAX ); + nReturn = ODST_FUNCTION_ARG_LIST; + } + else if( EQUAL(osToken,"COUNT") ) + { + *ppNode = new ods_formula_node( ODS_COUNT ); + nReturn = ODST_FUNCTION_ARG_LIST; + } + else if( EQUAL(osToken,"COUNTA") ) + { + *ppNode = new ods_formula_node( ODS_COUNTA ); + nReturn = ODST_FUNCTION_ARG_LIST; + } + + else + { + const SingleOpStruct* psSingleOp = ODSGetSingleOpEntry(osToken); + if (psSingleOp != NULL) + { + *ppNode = new ods_formula_node( psSingleOp->eOp ); + nReturn = ODST_FUNCTION_SINGLE_ARG; + } + else + { + *ppNode = new ods_formula_node( osToken ); + nReturn = ODST_IDENTIFIER; + } + } + + return nReturn; + } + +/* -------------------------------------------------------------------- */ +/* Handle special tokens. */ +/* -------------------------------------------------------------------- */ + else + { + context->pszNext = pszInput+1; + return *pszInput; + } +} + +/************************************************************************/ +/* ods_formula_compile() */ +/************************************************************************/ + +ods_formula_node* ods_formula_compile( const char *expr ) + +{ + ods_formula_parse_context context; + + context.pszInput = expr; + context.pszNext = expr; + context.nStartToken = ODST_START; + + if( ods_formulaparse( &context ) == 0 ) + { + return context.poRoot; + } + else + { + delete context.poRoot; + return NULL; + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula.h new file mode 100644 index 000000000..e55f4db22 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula.h @@ -0,0 +1,212 @@ +/****************************************************************************** + * $Id: ods_formula.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Component: ODS formula Engine + * Purpose: Implementation of the ods_formula_node class used to represent a + * node in a ODS expression. + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _ODS_FORMULA_H_INCLUDED_ +#define _ODS_FORMULA_H_INCLUDED_ + +#include "cpl_conv.h" +#include "cpl_string.h" + +#include + +#if defined(_WIN32) && !defined(_WIN32_WCE) +# define strcasecmp stricmp +#elif defined(_WIN32_WCE) +# define strcasecmp _stricmp +#endif + +typedef enum { + ODS_OR, + ODS_AND, + ODS_NOT, + ODS_IF, + + ODS_PI, + + ODS_SUM, + ODS_AVERAGE, + ODS_MIN, + ODS_MAX, + ODS_COUNT, + ODS_COUNTA, + + //ODS_T, + ODS_LEN, + ODS_LEFT, + ODS_RIGHT, + ODS_MID, + + ODS_ABS, + ODS_SQRT, + ODS_COS, + ODS_SIN, + ODS_TAN, + ODS_ACOS, + ODS_ASIN, + ODS_ATAN, + ODS_EXP, + ODS_LN, + ODS_LOG, + + ODS_EQ, + ODS_NE, + ODS_LE, + ODS_GE, + ODS_LT, + ODS_GT, + + ODS_ADD, + ODS_SUBTRACT, + ODS_MULTIPLY, + ODS_DIVIDE, + ODS_MODULUS, + + ODS_CONCAT, + + ODS_LIST, + + ODS_CELL, + ODS_CELL_RANGE, +} ods_formula_op; + +typedef enum { + ODS_FIELD_TYPE_INTEGER, + ODS_FIELD_TYPE_FLOAT, + ODS_FIELD_TYPE_STRING, + ODS_FIELD_TYPE_EMPTY +} ods_formula_field_type; + +typedef enum { + SNT_CONSTANT, + SNT_OPERATION +} ods_formula_node_type; + +class IODSCellEvaluator; + +class ods_formula_node { +private: + void FreeSubExpr(); + std::string TransformToString() const; + + int EvaluateOR(IODSCellEvaluator* poEvaluator); + int EvaluateAND(IODSCellEvaluator* poEvaluator); + int EvaluateNOT(IODSCellEvaluator* poEvaluator); + int EvaluateIF(IODSCellEvaluator* poEvaluator); + + int EvaluateLEN(IODSCellEvaluator* poEvaluator); + int EvaluateLEFT(IODSCellEvaluator* poEvaluator); + int EvaluateRIGHT(IODSCellEvaluator* poEvaluator); + int EvaluateMID(IODSCellEvaluator* poEvaluator); + + int EvaluateListArgOp(IODSCellEvaluator* poEvaluator); + + int EvaluateSingleArgOp(IODSCellEvaluator* poEvaluator); + + int EvaluateEQ(IODSCellEvaluator* poEvaluator); + int EvaluateNE(IODSCellEvaluator* poEvaluator); + int EvaluateLE(IODSCellEvaluator* poEvaluator); + int EvaluateGE(IODSCellEvaluator* poEvaluator); + int EvaluateLT(IODSCellEvaluator* poEvaluator); + int EvaluateGT(IODSCellEvaluator* poEvaluator); + + int EvaluateBinaryArithmetic(IODSCellEvaluator* poEvaluator); + + int EvaluateCONCAT(IODSCellEvaluator* poEvaluator); + + int EvaluateCELL(IODSCellEvaluator* poEvaluator); + +public: + ods_formula_node(); + + ods_formula_node( const char *, ods_formula_field_type field_type_in = ODS_FIELD_TYPE_STRING ); + ods_formula_node( int ); + ods_formula_node( double ); + ods_formula_node( ods_formula_op ); + + ods_formula_node( const ods_formula_node& other ); + + ~ods_formula_node(); + + void Initialize(); + void Dump( FILE *fp, int depth ); + + int Evaluate(IODSCellEvaluator* poEvaluator); + + ods_formula_node_type eNodeType; + ods_formula_field_type field_type; + + /* only for SNT_OPERATION */ + void PushSubExpression( ods_formula_node * ); + void ReverseSubExpressions(); + ods_formula_op eOp; + int nSubExprCount; + ods_formula_node **papoSubExpr; + + /* only for SNT_CONSTANT */ + char *string_value; + int int_value; + double float_value; +}; + +class ods_formula_parse_context { +public: + ods_formula_parse_context() : nStartToken(0), poRoot(NULL) {} + + int nStartToken; + const char *pszInput; + const char *pszNext; + + ods_formula_node *poRoot; +}; + +class IODSCellEvaluator +{ +public: + virtual int EvaluateRange(int nRow1, int nCol1, int nRow2, int nCol2, + std::vector& aoOutValues) = 0; + virtual ~IODSCellEvaluator() {} +}; + +int ods_formulaparse( ods_formula_parse_context *context ); +int ods_formulalex( ods_formula_node **ppNode, ods_formula_parse_context *context ); +ods_formula_node* ods_formula_compile( const char *expr ); + +typedef struct +{ + const char *pszName; + ods_formula_op eOp; + double (*pfnEval)(double); +} SingleOpStruct; + +const SingleOpStruct* ODSGetSingleOpEntry(const char* pszName); +const SingleOpStruct* ODSGetSingleOpEntry(ods_formula_op eOp); + +#endif /* def _ODS_FORMULA_H_INCLUDED_ */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula_node.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula_node.cpp new file mode 100644 index 000000000..bfdb479f0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula_node.cpp @@ -0,0 +1,1609 @@ +/****************************************************************************** + * $Id: ods_formula_node.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Component: ODS formula Engine + * Purpose: Implementation of the ods_formula_node class used to represent a + * node in a ODS expression. + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ods_formula.h" + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* ods_formula_node() */ +/************************************************************************/ + +ods_formula_node::ods_formula_node() + +{ + Initialize(); +} + +/************************************************************************/ +/* ods_formula_node(int) */ +/************************************************************************/ + +ods_formula_node::ods_formula_node( int nValueIn ) + +{ + Initialize(); + + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = nValueIn; +} + +/************************************************************************/ +/* ods_formula_node(double) */ +/************************************************************************/ + +ods_formula_node::ods_formula_node( double dfValueIn ) + +{ + Initialize(); + + field_type = ODS_FIELD_TYPE_FLOAT; + float_value = dfValueIn; +} + +/************************************************************************/ +/* ods_formula_node(const char*) */ +/************************************************************************/ + +ods_formula_node::ods_formula_node( const char *pszValueIn, + ods_formula_field_type field_type_in ) + +{ + Initialize(); + + field_type = field_type_in; + string_value = CPLStrdup( pszValueIn ? pszValueIn : "" ); +} + +/************************************************************************/ +/* ods_formula_node(ods_formula_op) */ +/************************************************************************/ + +ods_formula_node::ods_formula_node( ods_formula_op eOpIn ) + +{ + Initialize(); + + eNodeType = SNT_OPERATION; + + eOp = eOpIn; +} + +/************************************************************************/ +/* ods_formula_node(const ods_formula_node&) */ +/************************************************************************/ + +ods_formula_node::ods_formula_node( const ods_formula_node& other ) +{ + eNodeType = other.eNodeType; + eOp = other.eOp; + field_type = other.field_type; + int_value = other.int_value; + float_value = other.float_value; + string_value = other.string_value ? CPLStrdup(other.string_value) : NULL; + nSubExprCount = other.nSubExprCount; + if (nSubExprCount) + { + papoSubExpr = (ods_formula_node **) + CPLMalloc( sizeof(void*) * nSubExprCount ); + for(int i=0;i"; + case ODS_GE : return ">="; + case ODS_LE : return "<="; + case ODS_LT : return "<"; + case ODS_GT : return ">"; + + case ODS_ADD : return "+"; + case ODS_SUBTRACT : return "-"; + case ODS_MULTIPLY : return "*"; + case ODS_DIVIDE : return "/"; + case ODS_MODULUS : return "MOD"; + case ODS_CONCAT : return "&"; + + case ODS_LIST : return "*list*"; + case ODS_CELL : return "*cell*"; + case ODS_CELL_RANGE : return "*cell_range*"; + default: + { + const SingleOpStruct* psSingleOp = ODSGetSingleOpEntry(eOp); + if (psSingleOp != NULL) + return psSingleOp->pszName; + return "*unknown*"; + } + } +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +void ods_formula_node::Dump( FILE * fp, int depth ) + +{ + char spaces[60]; + int i; + + for( i = 0; i < depth*2 && i < (int) sizeof(spaces) - 1; i++ ) + spaces[i] = ' '; + spaces[i] = '\0'; + + if( eNodeType == SNT_CONSTANT ) + { + if( field_type == ODS_FIELD_TYPE_INTEGER ) + fprintf( fp, "%s %d\n", spaces, int_value ); + else if( field_type == ODS_FIELD_TYPE_FLOAT ) + fprintf( fp, "%s %.15g\n", spaces, float_value ); + else + fprintf( fp, "%s \"%s\"\n", spaces, string_value ); + return; + } + + CPLAssert( eNodeType == SNT_OPERATION ); + + fprintf( fp, "%s%s\n", spaces, ODSGetOperatorName(eOp) ); + + for( i = 0; i < nSubExprCount; i++ ) + papoSubExpr[i]->Dump( fp, depth+1 ); +} + +/************************************************************************/ +/* FreeSubExpr() */ +/************************************************************************/ + +void ods_formula_node::FreeSubExpr() +{ + int i; + for( i = 0; i < nSubExprCount; i++ ) + delete papoSubExpr[i]; + CPLFree( papoSubExpr ); + + nSubExprCount = 0; + papoSubExpr = NULL; +} + +/************************************************************************/ +/* Evaluate() */ +/************************************************************************/ + +int ods_formula_node::Evaluate(IODSCellEvaluator* poEvaluator) +{ + if (eNodeType == SNT_CONSTANT) + return TRUE; + + CPLAssert( eNodeType == SNT_OPERATION ); + + switch (eOp) + { + case ODS_OR: return EvaluateOR(poEvaluator); + case ODS_AND: return EvaluateAND(poEvaluator); + case ODS_NOT: return EvaluateNOT(poEvaluator); + case ODS_IF: return EvaluateIF(poEvaluator); + + case ODS_PI: + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_FLOAT; + float_value = PI; + return TRUE; + + case ODS_LEN : return EvaluateLEN(poEvaluator); + case ODS_LEFT : return EvaluateLEFT(poEvaluator); + case ODS_RIGHT : return EvaluateRIGHT(poEvaluator); + case ODS_MID : return EvaluateMID(poEvaluator); + + case ODS_SUM: + case ODS_AVERAGE: + case ODS_MIN: + case ODS_MAX: + case ODS_COUNT: + case ODS_COUNTA: + return EvaluateListArgOp(poEvaluator); + + case ODS_ABS: + case ODS_SQRT: + case ODS_COS: + case ODS_SIN: + case ODS_TAN: + case ODS_ACOS: + case ODS_ASIN: + case ODS_ATAN: + case ODS_EXP: + case ODS_LN: + case ODS_LOG: + return EvaluateSingleArgOp(poEvaluator); + + + case ODS_EQ: return EvaluateEQ(poEvaluator); + case ODS_NE: return EvaluateNE(poEvaluator); + case ODS_LE: return EvaluateLE(poEvaluator); + case ODS_GE: return EvaluateGE(poEvaluator); + case ODS_LT: return EvaluateLT(poEvaluator); + case ODS_GT: return EvaluateGT(poEvaluator); + + case ODS_ADD: + case ODS_SUBTRACT: + case ODS_MULTIPLY: + case ODS_DIVIDE: + case ODS_MODULUS: + return EvaluateBinaryArithmetic(poEvaluator); + + case ODS_CONCAT: return EvaluateCONCAT(poEvaluator); + + case ODS_CELL: return EvaluateCELL(poEvaluator); + + default: + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unhandled case in Evaluate() for %s", + ODSGetOperatorName(eOp)); + return FALSE; + } + } +} + +/************************************************************************/ +/* EvaluateOR() */ +/************************************************************************/ + +int ods_formula_node::EvaluateOR(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp == ODS_OR ); + + CPLAssert(nSubExprCount == 1); + CPLAssert(papoSubExpr[0]->eNodeType == SNT_OPERATION ); + CPLAssert(papoSubExpr[0]->eOp == ODS_LIST ); + int bVal = FALSE; + for(int i = 0; i < papoSubExpr[0]->nSubExprCount; i++) + { + if (!(papoSubExpr[0]->papoSubExpr[i]->Evaluate(poEvaluator))) + return FALSE; + CPLAssert(papoSubExpr[0]->papoSubExpr[i]->eNodeType == SNT_CONSTANT ); + if (papoSubExpr[0]->papoSubExpr[i]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal |= (papoSubExpr[0]->papoSubExpr[i]->int_value != 0); + } + else if (papoSubExpr[0]->papoSubExpr[i]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal |= (papoSubExpr[0]->papoSubExpr[i]->float_value != 0); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } + } + + FreeSubExpr(); + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = bVal; + + return TRUE; +} + +/************************************************************************/ +/* EvaluateAND() */ +/************************************************************************/ + +int ods_formula_node::EvaluateAND(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp == ODS_AND ); + + CPLAssert(nSubExprCount == 1); + CPLAssert(papoSubExpr[0]->eNodeType == SNT_OPERATION ); + CPLAssert(papoSubExpr[0]->eOp == ODS_LIST ); + int bVal = TRUE; + for(int i = 0; i < papoSubExpr[0]->nSubExprCount; i++) + { + if (!(papoSubExpr[0]->papoSubExpr[i]->Evaluate(poEvaluator))) + return FALSE; + CPLAssert(papoSubExpr[0]->papoSubExpr[i]->eNodeType == SNT_CONSTANT ); + if (papoSubExpr[0]->papoSubExpr[i]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal &= (papoSubExpr[0]->papoSubExpr[i]->int_value != 0); + } + else if (papoSubExpr[0]->papoSubExpr[i]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal &= (papoSubExpr[0]->papoSubExpr[i]->float_value != 0); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } + } + + FreeSubExpr(); + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = bVal; + + return TRUE; +} + +/************************************************************************/ +/* EvaluateNOT() */ +/************************************************************************/ + +int ods_formula_node::EvaluateNOT(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp == ODS_NOT ); + + CPLAssert(nSubExprCount == 1); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + + int bVal = FALSE; + if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal = !(papoSubExpr[0]->int_value != 0); + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal = !(papoSubExpr[0]->float_value != 0); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } + + FreeSubExpr(); + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = bVal; + + return TRUE; +} + +/************************************************************************/ +/* EvaluateIF() */ +/************************************************************************/ + +int ods_formula_node::EvaluateIF(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp == ODS_IF ); + + CPLAssert(nSubExprCount == 2 || nSubExprCount == 3); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + if (!(papoSubExpr[1]->Evaluate(poEvaluator))) + return FALSE; + if (nSubExprCount == 3 && !(papoSubExpr[2]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT ); + if (nSubExprCount == 3) + { + CPLAssert(papoSubExpr[2]->eNodeType == SNT_CONSTANT ); + } + int bCond = FALSE; + if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bCond = (papoSubExpr[0]->int_value != 0); + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bCond = (papoSubExpr[0]->float_value != 0); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } + + if (bCond) + { + eNodeType = SNT_CONSTANT; + field_type = papoSubExpr[1]->field_type; + if (field_type == ODS_FIELD_TYPE_INTEGER) + int_value = papoSubExpr[1]->int_value; + else if (field_type == ODS_FIELD_TYPE_FLOAT) + float_value = papoSubExpr[1]->float_value; + else if (field_type == ODS_FIELD_TYPE_STRING) + { + string_value = papoSubExpr[1]->string_value; + papoSubExpr[1]->string_value = NULL; + } + } + else if (nSubExprCount == 3) + { + eNodeType = SNT_CONSTANT; + field_type = papoSubExpr[2]->field_type; + if (field_type == ODS_FIELD_TYPE_INTEGER) + int_value = papoSubExpr[2]->int_value; + else if (field_type == ODS_FIELD_TYPE_FLOAT) + float_value = papoSubExpr[2]->float_value; + else if (field_type == ODS_FIELD_TYPE_STRING) + { + string_value = papoSubExpr[2]->string_value; + papoSubExpr[2]->string_value = NULL; + } + } + else + { + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = FALSE; + } + + FreeSubExpr(); + + return TRUE; +} + +/************************************************************************/ +/* EvaluateEQ() */ +/************************************************************************/ + +int ods_formula_node::EvaluateEQ(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp == ODS_EQ ); + + CPLAssert(nSubExprCount == 2); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + if (!(papoSubExpr[1]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT ); + + int bVal = FALSE; + if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal = (papoSubExpr[0]->int_value == papoSubExpr[1]->int_value); + } + else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal = (papoSubExpr[0]->int_value == papoSubExpr[1]->float_value); + } + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal = (papoSubExpr[0]->float_value == papoSubExpr[1]->int_value); + } + else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal = (papoSubExpr[0]->float_value == papoSubExpr[1]->float_value); + } + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_STRING) + { + bVal = (strcmp(papoSubExpr[0]->string_value, + papoSubExpr[1]->string_value) == 0); + } + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = bVal; + + FreeSubExpr(); + + return TRUE; +} + +/************************************************************************/ +/* EvaluateNE() */ +/************************************************************************/ + +int ods_formula_node::EvaluateNE(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp == ODS_NE ); + + eOp = ODS_EQ; + if (!EvaluateEQ(poEvaluator)) + return FALSE; + + int_value = !int_value; + return TRUE; +} + +/************************************************************************/ +/* GetCase() */ +/************************************************************************/ + +typedef enum +{ + CASE_LOWER, + CASE_UPPER, + CASE_UNKNOWN, +} CaseType; + +static CaseType GetCase(const char* pszStr) +{ + int bInit = TRUE; + char ch; + CaseType eCase = CASE_UNKNOWN; + while((ch = *(pszStr++)) != '\0') + { + if (bInit) + { + if (ch >= 'a' && ch <= 'z') + eCase = CASE_LOWER; + else if (ch >= 'A' && ch <= 'Z') + eCase = CASE_UPPER; + else + return CASE_UNKNOWN; + } + else if (ch >= 'a' && ch <= 'z' && eCase == CASE_LOWER) + ; + else if (ch >= 'A' && ch <= 'Z' && eCase == CASE_UPPER) + ; + else + return CASE_UNKNOWN; + } + return eCase; +} + +/************************************************************************/ +/* EvaluateLE() */ +/************************************************************************/ + +int ods_formula_node::EvaluateLE(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp == ODS_LE ); + + CPLAssert(nSubExprCount == 2); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + if (!(papoSubExpr[1]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT ); + + int bVal = FALSE; + if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal = (papoSubExpr[0]->int_value <= papoSubExpr[1]->int_value); + } + else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal = (papoSubExpr[0]->int_value <= papoSubExpr[1]->float_value); + } + else + bVal = TRUE; + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal = (papoSubExpr[0]->float_value <= papoSubExpr[1]->int_value); + } + else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal = (papoSubExpr[0]->float_value <= papoSubExpr[1]->float_value); + } + else + bVal = TRUE; + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_STRING) + { + if (GetCase(papoSubExpr[0]->string_value) == + GetCase(papoSubExpr[1]->string_value)) + bVal = (strcmp(papoSubExpr[0]->string_value, + papoSubExpr[1]->string_value) <= 0); + else + bVal = (strcasecmp(papoSubExpr[0]->string_value, + papoSubExpr[1]->string_value) <= 0); + } + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = bVal; + + FreeSubExpr(); + + return TRUE; +} + +/************************************************************************/ +/* EvaluateGE() */ +/************************************************************************/ + +int ods_formula_node::EvaluateGE(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp == ODS_GE ); + + CPLAssert(nSubExprCount == 2); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + if (!(papoSubExpr[1]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT ); + + int bVal = FALSE; + if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal = (papoSubExpr[0]->int_value >= papoSubExpr[1]->int_value); + } + else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal = (papoSubExpr[0]->int_value >= papoSubExpr[1]->float_value); + } + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal = (papoSubExpr[0]->float_value >= papoSubExpr[1]->int_value); + } + else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal = (papoSubExpr[0]->float_value >= papoSubExpr[1]->float_value); + } + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_STRING) + { + if (GetCase(papoSubExpr[0]->string_value) == + GetCase(papoSubExpr[1]->string_value)) + bVal = (strcmp(papoSubExpr[0]->string_value, + papoSubExpr[1]->string_value) >= 0); + else + bVal = (strcasecmp(papoSubExpr[0]->string_value, + papoSubExpr[1]->string_value) >= 0); + } + else + bVal = TRUE; + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = bVal; + + FreeSubExpr(); + + return TRUE; +} + +/************************************************************************/ +/* EvaluateLT() */ +/************************************************************************/ + +int ods_formula_node::EvaluateLT(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp == ODS_LT ); + + CPLAssert(nSubExprCount == 2); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + if (!(papoSubExpr[1]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT ); + + int bVal = FALSE; + if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal = (papoSubExpr[0]->int_value < papoSubExpr[1]->int_value); + } + else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal = (papoSubExpr[0]->int_value < papoSubExpr[1]->float_value); + } + else + bVal = TRUE; + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal = (papoSubExpr[0]->float_value < papoSubExpr[1]->int_value); + } + else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal = (papoSubExpr[0]->float_value < papoSubExpr[1]->float_value); + } + else + bVal = TRUE; + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_STRING) + { + if (GetCase(papoSubExpr[0]->string_value) == + GetCase(papoSubExpr[1]->string_value)) + bVal = (strcmp(papoSubExpr[0]->string_value, + papoSubExpr[1]->string_value) < 0); + else + bVal = (strcasecmp(papoSubExpr[0]->string_value, + papoSubExpr[1]->string_value) < 0); + } + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = bVal; + + FreeSubExpr(); + + return TRUE; +} + +/************************************************************************/ +/* EvaluateGT() */ +/************************************************************************/ + +int ods_formula_node::EvaluateGT(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp == ODS_GT ); + + CPLAssert(nSubExprCount == 2); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + if (!(papoSubExpr[1]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT ); + + int bVal = FALSE; + if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal = (papoSubExpr[0]->int_value > papoSubExpr[1]->int_value); + } + else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal = (papoSubExpr[0]->int_value > papoSubExpr[1]->float_value); + } + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER) + { + bVal = (papoSubExpr[0]->float_value > papoSubExpr[1]->int_value); + } + else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT) + { + bVal = (papoSubExpr[0]->float_value > papoSubExpr[1]->float_value); + } + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_STRING) + { + if (GetCase(papoSubExpr[0]->string_value) == + GetCase(papoSubExpr[1]->string_value)) + bVal = (strcmp(papoSubExpr[0]->string_value, + papoSubExpr[1]->string_value) > 0); + else + bVal = (strcasecmp(papoSubExpr[0]->string_value, + papoSubExpr[1]->string_value) > 0); + } + else + bVal = TRUE; + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = bVal; + + FreeSubExpr(); + + return TRUE; +} + +/************************************************************************/ +/* EvaluateSingleArgOp() */ +/************************************************************************/ + +int ods_formula_node::EvaluateSingleArgOp(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + + const SingleOpStruct* psSingleOp = ODSGetSingleOpEntry(eOp); + CPLAssert(psSingleOp); + + CPLAssert(nSubExprCount == 1); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + double dfVal = 0; + + if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER) + { + dfVal = psSingleOp->pfnEval(papoSubExpr[0]->int_value); + } + else if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT) + { + dfVal = psSingleOp->pfnEval(papoSubExpr[0]->float_value); + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, "Bad argument type for %s", + psSingleOp->pszName); + return FALSE; + } + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_FLOAT; + float_value = dfVal; + + FreeSubExpr(); + + return TRUE; +} + +/************************************************************************/ +/* EvaluateBinaryArithmetic() */ +/************************************************************************/ + +int ods_formula_node::EvaluateBinaryArithmetic(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp >= ODS_ADD && eOp<= ODS_MODULUS ); + + CPLAssert(nSubExprCount == 2); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + if (!(papoSubExpr[1]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT ); + + if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_INTEGER) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER) + { + int nVal; + switch (eOp) + { + case ODS_ADD : nVal = (papoSubExpr[0]->int_value + papoSubExpr[1]->int_value); break; + case ODS_SUBTRACT : nVal = (papoSubExpr[0]->int_value - papoSubExpr[1]->int_value); break; + case ODS_MULTIPLY : nVal = (papoSubExpr[0]->int_value * papoSubExpr[1]->int_value); break; + case ODS_DIVIDE : + if (papoSubExpr[1]->int_value != 0) + nVal = (papoSubExpr[0]->int_value / papoSubExpr[1]->int_value); + else + return FALSE; + break; + case ODS_MODULUS : + if (papoSubExpr[1]->int_value != 0) + nVal = (papoSubExpr[0]->int_value % papoSubExpr[1]->int_value); + else + return FALSE; + break; + default: nVal = 0; CPLAssert(0); + } + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = nVal; + + FreeSubExpr(); + + return TRUE; + } + else if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT) + { + papoSubExpr[0]->field_type = ODS_FIELD_TYPE_FLOAT; + papoSubExpr[0]->float_value = papoSubExpr[0]->int_value; + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } + } + + if (papoSubExpr[0]->field_type == ODS_FIELD_TYPE_FLOAT) + { + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_INTEGER) + { + papoSubExpr[1]->field_type = ODS_FIELD_TYPE_FLOAT; + papoSubExpr[1]->float_value = papoSubExpr[1]->int_value; + } + + if (papoSubExpr[1]->field_type == ODS_FIELD_TYPE_FLOAT) + { + float dfVal; + switch (eOp) + { + case ODS_ADD : dfVal = (papoSubExpr[0]->float_value + papoSubExpr[1]->float_value); break; + case ODS_SUBTRACT : dfVal = (papoSubExpr[0]->float_value - papoSubExpr[1]->float_value); break; + case ODS_MULTIPLY : dfVal = (papoSubExpr[0]->float_value * papoSubExpr[1]->float_value); break; + case ODS_DIVIDE : + if (papoSubExpr[1]->float_value != 0) + dfVal = (papoSubExpr[0]->float_value / papoSubExpr[1]->float_value); + else + return FALSE; + break; + case ODS_MODULUS : + if (papoSubExpr[1]->float_value != 0) + dfVal = fmod(papoSubExpr[0]->float_value, papoSubExpr[1]->float_value); + else + return FALSE; + break; + default: dfVal = 0.0; CPLAssert(0); + } + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_FLOAT; + float_value = dfVal; + + FreeSubExpr(); + + return TRUE; + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } +} + + +/************************************************************************/ +/* TransformToString() */ +/************************************************************************/ + +std::string ods_formula_node::TransformToString() const +{ + char szTmp[128]; + if (field_type == ODS_FIELD_TYPE_INTEGER) + { + snprintf(szTmp, sizeof(szTmp), "%d", int_value); + return szTmp; + } + else if (field_type == ODS_FIELD_TYPE_FLOAT) + { + CPLsnprintf(szTmp, sizeof(szTmp), "%.16g", float_value); + return szTmp; + } + else if (field_type == ODS_FIELD_TYPE_STRING) + { + return string_value; + } + else + { + return ""; + } +} + +/************************************************************************/ +/* EvaluateCONCAT() */ +/************************************************************************/ + +int ods_formula_node::EvaluateCONCAT(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp == ODS_CONCAT ); + + CPLAssert(nSubExprCount == 2); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + if (!(papoSubExpr[1]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT ); + + std::string osLeft(papoSubExpr[0]->TransformToString()); + std::string osRight(papoSubExpr[1]->TransformToString()); + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_STRING; + string_value = CPLStrdup((osLeft + osRight).c_str()); + + FreeSubExpr(); + + return TRUE; +} + +/************************************************************************/ +/* GetRowCol() */ +/************************************************************************/ + +static int GetRowCol(const char* pszCell, int& nRow, int& nCol) +{ + int i; + + if (pszCell[0] != '.') + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid cell %s", pszCell); + return FALSE; + } + + nCol = 0; + for(i=1; pszCell[i]>='A' && pszCell[i]<='Z';i++) + { + nCol = nCol * 26 + (pszCell[i] - 'A'); + } + nRow = atoi(pszCell + i) - 1; + + return TRUE; +} + +/************************************************************************/ +/* EvaluateListArgOp() */ +/************************************************************************/ + +int ods_formula_node::EvaluateListArgOp(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp >= ODS_SUM && eOp <= ODS_COUNTA ); + + CPLAssert(nSubExprCount == 1); + CPLAssert(papoSubExpr[0]->eNodeType == SNT_OPERATION ); + CPLAssert(papoSubExpr[0]->eOp == ODS_LIST ); + + std::vector adfVal; + int i; + + int nCount = 0, nCountA = 0; + + for(i=0;inSubExprCount;i++) + { + if (papoSubExpr[0]->papoSubExpr[i]->eNodeType == SNT_OPERATION && + papoSubExpr[0]->papoSubExpr[i]->eOp == ODS_CELL_RANGE) + { + CPLAssert (papoSubExpr[0]->papoSubExpr[i]->nSubExprCount == 2); + CPLAssert (papoSubExpr[0]->papoSubExpr[i]->papoSubExpr[0]->eNodeType == SNT_CONSTANT); + CPLAssert (papoSubExpr[0]->papoSubExpr[i]->papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING); + CPLAssert (papoSubExpr[0]->papoSubExpr[i]->papoSubExpr[1]->eNodeType == SNT_CONSTANT); + CPLAssert (papoSubExpr[0]->papoSubExpr[i]->papoSubExpr[1]->field_type == ODS_FIELD_TYPE_STRING); + + if (poEvaluator == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "No cell evaluator provided"); + return FALSE; + } + + const char* psz1 = papoSubExpr[0]->papoSubExpr[i]->papoSubExpr[0]->string_value; + const char* psz2 = papoSubExpr[0]->papoSubExpr[i]->papoSubExpr[1]->string_value; + int nRow1 = 0,nCol1 = 0; + if (!GetRowCol(psz1, nRow1, nCol1)) + return FALSE; + int nRow2 = 0,nCol2 = 0; + if (!GetRowCol(psz2, nRow2, nCol2)) + return FALSE; + + std::vector aoOutValues; + if (poEvaluator->EvaluateRange(nRow1, nCol1, nRow2, nCol2, aoOutValues)) + { + for(size_t j = 0; j < aoOutValues.size(); j++) + { + if (aoOutValues[j].eNodeType == SNT_CONSTANT) + { + if (aoOutValues[j].field_type == ODS_FIELD_TYPE_INTEGER) + { + adfVal.push_back(aoOutValues[j].int_value); + nCount ++; + nCountA ++; + } + else if (aoOutValues[j].field_type == ODS_FIELD_TYPE_FLOAT) + { + adfVal.push_back(aoOutValues[j].float_value); + nCount ++; + nCountA ++; + } + else if (aoOutValues[j].field_type == ODS_FIELD_TYPE_STRING) + { + nCountA ++; + } + } + } + } + } + else + { + if (!(papoSubExpr[0]->papoSubExpr[i]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert (papoSubExpr[0]->papoSubExpr[i]->eNodeType == SNT_CONSTANT ); + if (papoSubExpr[0]->papoSubExpr[i]->field_type == ODS_FIELD_TYPE_INTEGER) + { + adfVal.push_back(papoSubExpr[0]->papoSubExpr[i]->int_value); + nCount ++; + nCountA ++; + } + else if (papoSubExpr[0]->papoSubExpr[i]->field_type == ODS_FIELD_TYPE_FLOAT) + { + adfVal.push_back(papoSubExpr[0]->papoSubExpr[i]->float_value); + nCount ++; + nCountA ++; + } + else if (eOp == ODS_COUNT || eOp == ODS_COUNTA) + { + if (papoSubExpr[0]->papoSubExpr[i]->field_type == ODS_FIELD_TYPE_STRING) + nCountA ++; + } + else + { + + CPLError(CE_Failure, CPLE_NotSupported, + "Bad argument type for %s", ODSGetOperatorName(eOp)); + return FALSE; + } + } + } + + if (eOp == ODS_COUNT) + { + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = nCount; + + FreeSubExpr(); + return TRUE; + } + + if (eOp == ODS_COUNTA) + { + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = nCountA; + + FreeSubExpr(); + return TRUE; + } + + double dfVal = 0; + + switch(eOp) + { + case ODS_SUM: + { + for(i=0;i<(int)adfVal.size();i++) + { + dfVal += adfVal[i]; + } + break; + } + + case ODS_AVERAGE: + { + for(i=0;i<(int)adfVal.size();i++) + { + dfVal += adfVal[i]; + } + dfVal /= adfVal.size(); + break; + } + + case ODS_MIN: + { + dfVal = (adfVal.size() == 0) ? 0 :adfVal[0]; + for(i=1;i<(int)adfVal.size();i++) + { + if (adfVal[i] < dfVal) dfVal = adfVal[i]; + } + break; + } + + case ODS_MAX: + { + dfVal = (adfVal.size() == 0) ? 0 :adfVal[0]; + for(i=1;i<(int)adfVal.size();i++) + { + if (adfVal[i] > dfVal) dfVal = adfVal[i]; + } + break; + } + + default: + break; + } + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_FLOAT; + float_value = dfVal; + + FreeSubExpr(); + + return TRUE; +} + +/************************************************************************/ +/* EvaluateCELL() */ +/************************************************************************/ + +int ods_formula_node::EvaluateCELL(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + CPLAssert( eOp == ODS_CELL ); + + CPLAssert(nSubExprCount == 1); + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[0]->field_type == ODS_FIELD_TYPE_STRING ); + + if (poEvaluator == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "No cell evaluator provided"); + return FALSE; + } + + int nRow = 0,nCol = 0; + if (!GetRowCol(papoSubExpr[0]->string_value, nRow, nCol)) + return FALSE; + + std::vector aoOutValues; + if (poEvaluator->EvaluateRange(nRow, nCol, nRow, nCol, aoOutValues) && + aoOutValues.size() == 1) + { + if (aoOutValues[0].eNodeType == SNT_CONSTANT) + { + FreeSubExpr(); + + eNodeType = aoOutValues[0].eNodeType; + field_type = aoOutValues[0].field_type; + int_value = aoOutValues[0].int_value; + float_value = aoOutValues[0].float_value; + string_value = aoOutValues[0].string_value ? CPLStrdup(aoOutValues[0].string_value) : NULL; + + return TRUE; + } + } + + return FALSE; +} + +/************************************************************************/ +/* EvaluateLEN() */ +/************************************************************************/ + +int ods_formula_node::EvaluateLEN(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + + CPLAssert(nSubExprCount == 1); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + + std::string osVal = papoSubExpr[0]->TransformToString(); + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_INTEGER; + int_value = strlen(osVal.c_str()); // FIXME : UTF8 support + + FreeSubExpr(); + + return TRUE; +} + +/************************************************************************/ +/* EvaluateLEFT() */ +/************************************************************************/ + +int ods_formula_node::EvaluateLEFT(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + + CPLAssert(nSubExprCount == 2); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + if (!(papoSubExpr[1]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT ); + + std::string osVal = papoSubExpr[0]->TransformToString(); + + if (papoSubExpr[1]->field_type != ODS_FIELD_TYPE_INTEGER) + return FALSE; + + // FIXME : UTF8 support + int nVal = papoSubExpr[1]->int_value; + if (nVal < 0) + return FALSE; + + osVal = osVal.substr(0,nVal); + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_STRING; + string_value = CPLStrdup(osVal.c_str()); + + FreeSubExpr(); + + return TRUE; +} + +/************************************************************************/ +/* EvaluateRIGHT() */ +/************************************************************************/ + +int ods_formula_node::EvaluateRIGHT(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + + CPLAssert(nSubExprCount == 2); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + if (!(papoSubExpr[1]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT ); + + std::string osVal = papoSubExpr[0]->TransformToString(); + + if (papoSubExpr[1]->field_type != ODS_FIELD_TYPE_INTEGER) + return FALSE; + + // FIXME : UTF8 support + size_t nLen = osVal.size(); + int nVal = papoSubExpr[1]->int_value; + if (nVal < 0) + return FALSE; + + if (nLen > (size_t) nVal) + osVal = osVal.substr(nLen-nVal); + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_STRING; + string_value = CPLStrdup(osVal.c_str()); + + FreeSubExpr(); + + return TRUE; +} + +/************************************************************************/ +/* EvaluateMID() */ +/************************************************************************/ + +int ods_formula_node::EvaluateMID(IODSCellEvaluator* poEvaluator) +{ + CPLAssert( eNodeType == SNT_OPERATION ); + + CPLAssert(nSubExprCount == 3); + if (!(papoSubExpr[0]->Evaluate(poEvaluator))) + return FALSE; + if (!(papoSubExpr[1]->Evaluate(poEvaluator))) + return FALSE; + if (!(papoSubExpr[2]->Evaluate(poEvaluator))) + return FALSE; + + CPLAssert(papoSubExpr[0]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[1]->eNodeType == SNT_CONSTANT ); + CPLAssert(papoSubExpr[2]->eNodeType == SNT_CONSTANT ); + + std::string osVal = papoSubExpr[0]->TransformToString(); + + if (papoSubExpr[1]->field_type != ODS_FIELD_TYPE_INTEGER) + return FALSE; + + if (papoSubExpr[2]->field_type != ODS_FIELD_TYPE_INTEGER) + return FALSE; + + // FIXME : UTF8 support + size_t nLen = osVal.size(); + int nStart = papoSubExpr[1]->int_value; + int nExtractLen = papoSubExpr[2]->int_value; + if (nStart <= 0) + return FALSE; + if (nExtractLen < 0) + return FALSE; + + if ((size_t)nStart <= nLen) + { + if (nStart-1 + nExtractLen >= (int)nLen) + osVal = osVal.substr(nStart - 1); + else + osVal = osVal.substr(nStart - 1, nExtractLen); + } + else + osVal = ""; + + eNodeType = SNT_CONSTANT; + field_type = ODS_FIELD_TYPE_STRING; + string_value = CPLStrdup(osVal.c_str()); + + FreeSubExpr(); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula_parser.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula_parser.cpp new file mode 100644 index 000000000..38c218293 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula_parser.cpp @@ -0,0 +1,2240 @@ + +/* A Bison parser, made by GNU Bison 2.4.1. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + * Copyright (c) 2012, Even Rouault + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.4.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* Substitute the variable and function names. */ +#define yyparse ods_formulaparse +#define yylex ods_formulalex +#define yyerror ods_formulaerror +#define yylval ods_formulalval +#define yychar ods_formulachar +#define yydebug ods_formuladebug +#define yynerrs ods_formulanerrs + + +/* Copy the first part of user declarations. */ + +/* Line 189 of yacc.c */ +#line 1 "ods_formula_parser.y" + +/****************************************************************************** + * $Id: ods_formula_parser.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Component: OGR ODS Formula Engine + * Purpose: expression and select parser grammar. + * Requires Bison 2.4.0 or newer to process. Use "make parser" target. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ods_formula.h" + +#define YYSTYPE ods_formula_node* + +/* Defining YYSTYPE_IS_TRIVIAL is needed because the parser is generated as a C++ file. */ +/* See http://www.gnu.org/s/bison/manual/html_node/Memory-Management.html that suggests */ +/* increase YYINITDEPTH instead, but this will consume memory. */ +/* Setting YYSTYPE_IS_TRIVIAL overcomes this limitation, but might be fragile because */ +/* it appears to be a non documented feature of Bison */ +#define YYSTYPE_IS_TRIVIAL 1 + +static void ods_formulaerror( CPL_UNUSED ods_formula_parse_context *context, const char *msg ) +{ + CPLError( CE_Failure, CPLE_AppDefined, + "Formula Parsing Error: %s", msg ); +} + + + +/* Line 189 of yacc.c */ +#line 135 "ods_formula_parser.cpp" + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + ODST_NUMBER = 258, + ODST_STRING = 259, + ODST_IDENTIFIER = 260, + ODST_FUNCTION_NO_ARG = 261, + ODST_FUNCTION_SINGLE_ARG = 262, + ODST_FUNCTION_TWO_ARG = 263, + ODST_FUNCTION_THREE_ARG = 264, + ODST_FUNCTION_ARG_LIST = 265, + ODST_START = 266, + ODST_NOT = 267, + ODST_OR = 268, + ODST_AND = 269, + ODST_IF = 270, + ODST_UMINUS = 271 + }; +#endif + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 264 of yacc.c */ +#line 193 "ods_formula_parser.cpp" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 18 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 333 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 34 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 7 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 41 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 108 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 271 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 30, 2, 2, 2, 21, 18, 2, + 25, 26, 19, 16, 23, 17, 2, 20, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 33, 24, + 28, 27, 29, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 31, 2, 32, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 22 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint8 yyprhs[] = +{ + 0, 0, 3, 6, 8, 10, 12, 14, 18, 23, + 30, 39, 44, 49, 54, 61, 70, 75, 79, 83, + 88, 93, 97, 101, 106, 111, 116, 121, 124, 128, + 132, 136, 140, 144, 148, 152, 156, 158, 162, 164, + 168, 170 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int8 yyrhs[] = +{ + 35, 0, -1, 11, 37, -1, 23, -1, 24, -1, + 3, -1, 4, -1, 6, 25, 26, -1, 7, 25, + 37, 26, -1, 8, 25, 37, 36, 37, 26, -1, + 9, 25, 37, 36, 37, 36, 37, 26, -1, 14, + 25, 38, 26, -1, 13, 25, 38, 26, -1, 12, + 25, 37, 26, -1, 15, 25, 37, 36, 37, 26, + -1, 15, 25, 37, 36, 37, 36, 37, 26, -1, + 10, 25, 39, 26, -1, 25, 37, 26, -1, 37, + 27, 37, -1, 37, 28, 29, 37, -1, 37, 30, + 27, 37, -1, 37, 28, 37, -1, 37, 29, 37, + -1, 37, 28, 27, 37, -1, 37, 27, 28, 37, + -1, 37, 27, 29, 37, -1, 37, 29, 27, 37, + -1, 17, 37, -1, 37, 16, 37, -1, 37, 17, + 37, -1, 37, 18, 37, -1, 37, 19, 37, -1, + 37, 20, 37, -1, 37, 21, 37, -1, 31, 5, + 32, -1, 37, 36, 38, -1, 37, -1, 37, 36, + 39, -1, 37, -1, 40, 36, 39, -1, 40, -1, + 31, 5, 33, 5, 32, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 88, 88, 93, 93, 97, 102, 107, 112, 118, + 125, 133, 140, 147, 153, 160, 168, 175, 180, 187, + 194, 201, 208, 215, 222, 229, 236, 243, 259, 266, + 273, 280, 287, 294, 301, 308, 314, 321, 327, 332, + 338, 345 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "ODST_NUMBER", "ODST_STRING", + "ODST_IDENTIFIER", "ODST_FUNCTION_NO_ARG", "ODST_FUNCTION_SINGLE_ARG", + "ODST_FUNCTION_TWO_ARG", "ODST_FUNCTION_THREE_ARG", + "ODST_FUNCTION_ARG_LIST", "ODST_START", "ODST_NOT", "ODST_OR", + "ODST_AND", "ODST_IF", "'+'", "'-'", "'&'", "'*'", "'/'", "'%'", + "ODST_UMINUS", "','", "';'", "'('", "')'", "'='", "'<'", "'>'", "'!'", + "'['", "']'", "':'", "$accept", "input", "comma", "value_expr", + "value_expr_list", "value_expr_and_cell_range_list", "cell_range", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 43, 45, 38, 42, + 47, 37, 271, 44, 59, 40, 41, 61, 60, 62, + 33, 91, 93, 58 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 34, 35, 36, 36, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 38, 38, 39, 39, 39, + 39, 40 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 2, 1, 1, 1, 1, 3, 4, 6, + 8, 4, 4, 4, 6, 8, 4, 3, 3, 4, + 4, 3, 3, 4, 4, 4, 4, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 1, 3, 1, 3, + 1, 5 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 0, 0, 5, 6, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 7, 0, 0, 0, 0, 38, 0, 40, 0, + 36, 0, 0, 0, 17, 34, 28, 29, 30, 31, + 32, 33, 0, 0, 18, 0, 0, 21, 0, 22, + 0, 8, 3, 4, 0, 0, 0, 0, 16, 0, + 13, 0, 12, 11, 0, 24, 25, 23, 19, 26, + 20, 0, 0, 0, 37, 39, 35, 0, 9, 0, + 0, 14, 0, 0, 41, 0, 10, 15 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + -1, 2, 74, 46, 51, 47, 48 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -75 +static const yytype_int16 yypact[] = +{ + -4, 162, 12, -75, -75, -3, 4, 14, 21, 32, + 33, 34, 35, 38, 162, 162, 44, 303, -75, 40, + 162, 162, 162, 182, 162, 162, 162, 162, -12, 213, + 36, 162, 162, 162, 162, 162, 162, 78, 107, 136, + 43, -75, 228, 24, 24, 66, 24, 46, -14, 243, + 24, 49, 50, 24, -75, -75, 181, 181, 181, -12, + -12, -12, 162, 162, 303, 162, 162, 303, 162, 303, + 162, -75, -75, -75, 162, 162, -5, 182, -75, 182, + -75, 162, -75, -75, 162, 303, 303, 303, 303, 303, + 303, 258, 24, 72, -75, -75, -75, 198, -75, 162, + 47, -75, 162, 273, -75, 288, -75, -75 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -75, -75, -42, -1, -25, -74, -75 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -1 +static const yytype_uint8 yytable[] = +{ + 17, 52, 75, 94, 77, 95, 79, 1, 81, 72, + 73, 84, 18, 28, 29, 37, 38, 39, 40, 42, + 43, 44, 19, 49, 50, 50, 53, 55, 93, 20, + 56, 57, 58, 59, 60, 61, 64, 67, 69, 21, + 31, 32, 33, 34, 35, 36, 22, 72, 73, 30, + 99, 37, 38, 39, 40, 102, 96, 23, 24, 25, + 26, 85, 86, 27, 87, 88, 41, 89, 55, 90, + 70, 76, 78, 91, 92, 82, 83, 100, 0, 104, + 50, 3, 4, 97, 5, 6, 7, 8, 9, 0, + 10, 11, 12, 13, 0, 14, 0, 0, 103, 0, + 0, 105, 0, 15, 0, 0, 62, 63, 0, 16, + 3, 4, 0, 5, 6, 7, 8, 9, 0, 10, + 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 65, 0, 66, 0, 16, 3, + 4, 0, 5, 6, 7, 8, 9, 0, 10, 11, + 12, 13, 0, 14, 0, 0, 0, 0, 0, 0, + 0, 15, 0, 68, 0, 3, 4, 16, 5, 6, + 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, + 0, 0, 0, 0, 0, 3, 4, 15, 5, 6, + 7, 8, 9, 16, 10, 11, 12, 13, 0, 14, + 34, 35, 36, 0, 0, 0, 0, 15, 37, 38, + 39, 40, 0, 45, 31, 32, 33, 34, 35, 36, + 0, 72, 73, 0, 101, 37, 38, 39, 40, 31, + 32, 33, 34, 35, 36, 0, 0, 0, 0, 54, + 37, 38, 39, 40, 31, 32, 33, 34, 35, 36, + 0, 0, 0, 0, 71, 37, 38, 39, 40, 31, + 32, 33, 34, 35, 36, 0, 0, 0, 0, 80, + 37, 38, 39, 40, 31, 32, 33, 34, 35, 36, + 0, 0, 0, 0, 98, 37, 38, 39, 40, 31, + 32, 33, 34, 35, 36, 0, 0, 0, 0, 106, + 37, 38, 39, 40, 31, 32, 33, 34, 35, 36, + 0, 0, 0, 0, 107, 37, 38, 39, 40, 31, + 32, 33, 34, 35, 36, 0, 0, 0, 0, 0, + 37, 38, 39, 40 +}; + +static const yytype_int8 yycheck[] = +{ + 1, 26, 44, 77, 46, 79, 48, 11, 50, 23, + 24, 53, 0, 14, 15, 27, 28, 29, 30, 20, + 21, 22, 25, 24, 25, 26, 27, 32, 33, 25, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 25, + 16, 17, 18, 19, 20, 21, 25, 23, 24, 5, + 92, 27, 28, 29, 30, 97, 81, 25, 25, 25, + 25, 62, 63, 25, 65, 66, 26, 68, 32, 70, + 27, 5, 26, 74, 75, 26, 26, 5, -1, 32, + 81, 3, 4, 84, 6, 7, 8, 9, 10, -1, + 12, 13, 14, 15, -1, 17, -1, -1, 99, -1, + -1, 102, -1, 25, -1, -1, 28, 29, -1, 31, + 3, 4, -1, 6, 7, 8, 9, 10, -1, 12, + 13, 14, 15, -1, 17, -1, -1, -1, -1, -1, + -1, -1, 25, -1, 27, -1, 29, -1, 31, 3, + 4, -1, 6, 7, 8, 9, 10, -1, 12, 13, + 14, 15, -1, 17, -1, -1, -1, -1, -1, -1, + -1, 25, -1, 27, -1, 3, 4, 31, 6, 7, + 8, 9, 10, -1, 12, 13, 14, 15, -1, 17, + -1, -1, -1, -1, -1, 3, 4, 25, 6, 7, + 8, 9, 10, 31, 12, 13, 14, 15, -1, 17, + 19, 20, 21, -1, -1, -1, -1, 25, 27, 28, + 29, 30, -1, 31, 16, 17, 18, 19, 20, 21, + -1, 23, 24, -1, 26, 27, 28, 29, 30, 16, + 17, 18, 19, 20, 21, -1, -1, -1, -1, 26, + 27, 28, 29, 30, 16, 17, 18, 19, 20, 21, + -1, -1, -1, -1, 26, 27, 28, 29, 30, 16, + 17, 18, 19, 20, 21, -1, -1, -1, -1, 26, + 27, 28, 29, 30, 16, 17, 18, 19, 20, 21, + -1, -1, -1, -1, 26, 27, 28, 29, 30, 16, + 17, 18, 19, 20, 21, -1, -1, -1, -1, 26, + 27, 28, 29, 30, 16, 17, 18, 19, 20, 21, + -1, -1, -1, -1, 26, 27, 28, 29, 30, 16, + 17, 18, 19, 20, 21, -1, -1, -1, -1, -1, + 27, 28, 29, 30 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 11, 35, 3, 4, 6, 7, 8, 9, 10, + 12, 13, 14, 15, 17, 25, 31, 37, 0, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 37, 37, + 5, 16, 17, 18, 19, 20, 21, 27, 28, 29, + 30, 26, 37, 37, 37, 31, 37, 39, 40, 37, + 37, 38, 38, 37, 26, 32, 37, 37, 37, 37, + 37, 37, 28, 29, 37, 27, 29, 37, 27, 37, + 27, 26, 23, 24, 36, 36, 5, 36, 26, 36, + 26, 36, 26, 26, 36, 37, 37, 37, 37, 37, + 37, 37, 37, 33, 39, 39, 38, 37, 26, 36, + 5, 26, 36, 37, 32, 37, 26, 26 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval, context) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, ods_formula_parse_context *context) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + ods_formula_parse_context *context; +#endif +{ + if (!yyvaluep) + return; + YYUSE (context); +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, ods_formula_parse_context *context) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep, context) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; + ods_formula_parse_context *context; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep, context); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule, ods_formula_parse_context *context) +#else +static void +yy_reduce_print (yyvsp, yyrule, context) + YYSTYPE *yyvsp; + int yyrule; + ods_formula_parse_context *context; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule, context); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, ods_formula_parse_context *context) +#else +static void +yydestruct (yymsg, yytype, yyvaluep, context) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; + ods_formula_parse_context *context; +#endif +{ + YYUSE (yyvaluep); + YYUSE (context); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + case 3: /* "ODST_NUMBER" */ + +/* Line 1000 of yacc.c */ +#line 82 "ods_formula_parser.y" + { delete (*yyvaluep); }; + +/* Line 1000 of yacc.c */ +#line 1220 "ods_formula_parser.cpp" + break; + case 4: /* "ODST_STRING" */ + +/* Line 1000 of yacc.c */ +#line 82 "ods_formula_parser.y" + { delete (*yyvaluep); }; + +/* Line 1000 of yacc.c */ +#line 1229 "ods_formula_parser.cpp" + break; + case 5: /* "ODST_IDENTIFIER" */ + +/* Line 1000 of yacc.c */ +#line 82 "ods_formula_parser.y" + { delete (*yyvaluep); }; + +/* Line 1000 of yacc.c */ +#line 1238 "ods_formula_parser.cpp" + break; + case 6: /* "ODST_FUNCTION_NO_ARG" */ + +/* Line 1000 of yacc.c */ +#line 82 "ods_formula_parser.y" + { delete (*yyvaluep); }; + +/* Line 1000 of yacc.c */ +#line 1247 "ods_formula_parser.cpp" + break; + case 7: /* "ODST_FUNCTION_SINGLE_ARG" */ + +/* Line 1000 of yacc.c */ +#line 82 "ods_formula_parser.y" + { delete (*yyvaluep); }; + +/* Line 1000 of yacc.c */ +#line 1256 "ods_formula_parser.cpp" + break; + case 8: /* "ODST_FUNCTION_TWO_ARG" */ + +/* Line 1000 of yacc.c */ +#line 82 "ods_formula_parser.y" + { delete (*yyvaluep); }; + +/* Line 1000 of yacc.c */ +#line 1265 "ods_formula_parser.cpp" + break; + case 9: /* "ODST_FUNCTION_THREE_ARG" */ + +/* Line 1000 of yacc.c */ +#line 82 "ods_formula_parser.y" + { delete (*yyvaluep); }; + +/* Line 1000 of yacc.c */ +#line 1274 "ods_formula_parser.cpp" + break; + case 10: /* "ODST_FUNCTION_ARG_LIST" */ + +/* Line 1000 of yacc.c */ +#line 82 "ods_formula_parser.y" + { delete (*yyvaluep); }; + +/* Line 1000 of yacc.c */ +#line 1283 "ods_formula_parser.cpp" + break; + case 37: /* "value_expr" */ + +/* Line 1000 of yacc.c */ +#line 83 "ods_formula_parser.y" + { delete (*yyvaluep); }; + +/* Line 1000 of yacc.c */ +#line 1292 "ods_formula_parser.cpp" + break; + case 38: /* "value_expr_list" */ + +/* Line 1000 of yacc.c */ +#line 83 "ods_formula_parser.y" + { delete (*yyvaluep); }; + +/* Line 1000 of yacc.c */ +#line 1301 "ods_formula_parser.cpp" + break; + case 39: /* "value_expr_and_cell_range_list" */ + +/* Line 1000 of yacc.c */ +#line 83 "ods_formula_parser.y" + { delete (*yyvaluep); }; + +/* Line 1000 of yacc.c */ +#line 1310 "ods_formula_parser.cpp" + break; + case 40: /* "cell_range" */ + +/* Line 1000 of yacc.c */ +#line 83 "ods_formula_parser.y" + { delete (*yyvaluep); }; + +/* Line 1000 of yacc.c */ +#line 1319 "ods_formula_parser.cpp" + break; + + default: + break; + } +} + +/* Prevent warnings from -Wmissing-prototypes. */ +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (ods_formula_parse_context *context); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + +/*-------------------------. +| yyparse or yypush_parse. | +`-------------------------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (ods_formula_parse_context *context) +#else +int +yyparse (context) + ods_formula_parse_context *context; +#endif +#endif +{ +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + + /* Number of syntax errors so far. */ + int yynerrs; + + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; /* workaround bug with gcc 4.1 -O2 */ memset(yyssa, 0, sizeof(yyssa)); + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yytoken = 0; + yyss = yyssa; + yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: + +/* Line 1455 of yacc.c */ +#line 89 "ods_formula_parser.y" + { + context->poRoot = (yyvsp[(2) - (2)]); + ;} + break; + + case 5: + +/* Line 1455 of yacc.c */ +#line 98 "ods_formula_parser.y" + { + (yyval) = (yyvsp[(1) - (1)]); + ;} + break; + + case 6: + +/* Line 1455 of yacc.c */ +#line 103 "ods_formula_parser.y" + { + (yyval) = (yyvsp[(1) - (1)]); + ;} + break; + + case 7: + +/* Line 1455 of yacc.c */ +#line 108 "ods_formula_parser.y" + { + (yyval) = (yyvsp[(1) - (3)]); + ;} + break; + + case 8: + +/* Line 1455 of yacc.c */ +#line 113 "ods_formula_parser.y" + { + (yyval) = (yyvsp[(1) - (4)]); + (yyval)->PushSubExpression( (yyvsp[(3) - (4)]) ); + ;} + break; + + case 9: + +/* Line 1455 of yacc.c */ +#line 119 "ods_formula_parser.y" + { + (yyval) = (yyvsp[(1) - (6)]); + (yyval)->PushSubExpression( (yyvsp[(3) - (6)]) ); + (yyval)->PushSubExpression( (yyvsp[(5) - (6)]) ); + ;} + break; + + case 10: + +/* Line 1455 of yacc.c */ +#line 126 "ods_formula_parser.y" + { + (yyval) = (yyvsp[(1) - (8)]); + (yyval)->PushSubExpression( (yyvsp[(3) - (8)]) ); + (yyval)->PushSubExpression( (yyvsp[(5) - (8)]) ); + (yyval)->PushSubExpression( (yyvsp[(7) - (8)]) ); + ;} + break; + + case 11: + +/* Line 1455 of yacc.c */ +#line 134 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_AND ); + (yyvsp[(3) - (4)])->ReverseSubExpressions(); + (yyval)->PushSubExpression( (yyvsp[(3) - (4)]) ); + ;} + break; + + case 12: + +/* Line 1455 of yacc.c */ +#line 141 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_OR ); + (yyvsp[(3) - (4)])->ReverseSubExpressions(); + (yyval)->PushSubExpression( (yyvsp[(3) - (4)]) ); + ;} + break; + + case 13: + +/* Line 1455 of yacc.c */ +#line 148 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_NOT ); + (yyval)->PushSubExpression( (yyvsp[(3) - (4)]) ); + ;} + break; + + case 14: + +/* Line 1455 of yacc.c */ +#line 154 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_IF ); + (yyval)->PushSubExpression( (yyvsp[(3) - (6)]) ); + (yyval)->PushSubExpression( (yyvsp[(5) - (6)]) ); + ;} + break; + + case 15: + +/* Line 1455 of yacc.c */ +#line 161 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_IF ); + (yyval)->PushSubExpression( (yyvsp[(3) - (8)]) ); + (yyval)->PushSubExpression( (yyvsp[(5) - (8)]) ); + (yyval)->PushSubExpression( (yyvsp[(7) - (8)]) ); + ;} + break; + + case 16: + +/* Line 1455 of yacc.c */ +#line 169 "ods_formula_parser.y" + { + (yyval) = (yyvsp[(1) - (4)]); + (yyvsp[(3) - (4)])->ReverseSubExpressions(); + (yyval)->PushSubExpression( (yyvsp[(3) - (4)]) ); + ;} + break; + + case 17: + +/* Line 1455 of yacc.c */ +#line 176 "ods_formula_parser.y" + { + (yyval) = (yyvsp[(2) - (3)]); + ;} + break; + + case 18: + +/* Line 1455 of yacc.c */ +#line 181 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_EQ ); + (yyval)->PushSubExpression( (yyvsp[(1) - (3)]) ); + (yyval)->PushSubExpression( (yyvsp[(3) - (3)]) ); + ;} + break; + + case 19: + +/* Line 1455 of yacc.c */ +#line 188 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_NE ); + (yyval)->PushSubExpression( (yyvsp[(1) - (4)]) ); + (yyval)->PushSubExpression( (yyvsp[(4) - (4)]) ); + ;} + break; + + case 20: + +/* Line 1455 of yacc.c */ +#line 195 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_NE ); + (yyval)->PushSubExpression( (yyvsp[(1) - (4)]) ); + (yyval)->PushSubExpression( (yyvsp[(4) - (4)]) ); + ;} + break; + + case 21: + +/* Line 1455 of yacc.c */ +#line 202 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_LT ); + (yyval)->PushSubExpression( (yyvsp[(1) - (3)]) ); + (yyval)->PushSubExpression( (yyvsp[(3) - (3)]) ); + ;} + break; + + case 22: + +/* Line 1455 of yacc.c */ +#line 209 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_GT ); + (yyval)->PushSubExpression( (yyvsp[(1) - (3)]) ); + (yyval)->PushSubExpression( (yyvsp[(3) - (3)]) ); + ;} + break; + + case 23: + +/* Line 1455 of yacc.c */ +#line 216 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_LE ); + (yyval)->PushSubExpression( (yyvsp[(1) - (4)]) ); + (yyval)->PushSubExpression( (yyvsp[(4) - (4)]) ); + ;} + break; + + case 24: + +/* Line 1455 of yacc.c */ +#line 223 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_LE ); + (yyval)->PushSubExpression( (yyvsp[(1) - (4)]) ); + (yyval)->PushSubExpression( (yyvsp[(4) - (4)]) ); + ;} + break; + + case 25: + +/* Line 1455 of yacc.c */ +#line 230 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_LE ); + (yyval)->PushSubExpression( (yyvsp[(1) - (4)]) ); + (yyval)->PushSubExpression( (yyvsp[(4) - (4)]) ); + ;} + break; + + case 26: + +/* Line 1455 of yacc.c */ +#line 237 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_GE ); + (yyval)->PushSubExpression( (yyvsp[(1) - (4)]) ); + (yyval)->PushSubExpression( (yyvsp[(4) - (4)]) ); + ;} + break; + + case 27: + +/* Line 1455 of yacc.c */ +#line 244 "ods_formula_parser.y" + { + if ((yyvsp[(2) - (2)])->eNodeType == SNT_CONSTANT) + { + (yyval) = (yyvsp[(2) - (2)]); + (yyval)->int_value *= -1; + (yyval)->float_value *= -1; + } + else + { + (yyval) = new ods_formula_node( ODS_MULTIPLY ); + (yyval)->PushSubExpression( new ods_formula_node(-1) ); + (yyval)->PushSubExpression( (yyvsp[(2) - (2)]) ); + } + ;} + break; + + case 28: + +/* Line 1455 of yacc.c */ +#line 260 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_ADD ); + (yyval)->PushSubExpression( (yyvsp[(1) - (3)]) ); + (yyval)->PushSubExpression( (yyvsp[(3) - (3)]) ); + ;} + break; + + case 29: + +/* Line 1455 of yacc.c */ +#line 267 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_SUBTRACT ); + (yyval)->PushSubExpression( (yyvsp[(1) - (3)]) ); + (yyval)->PushSubExpression( (yyvsp[(3) - (3)]) ); + ;} + break; + + case 30: + +/* Line 1455 of yacc.c */ +#line 274 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_CONCAT ); + (yyval)->PushSubExpression( (yyvsp[(1) - (3)]) ); + (yyval)->PushSubExpression( (yyvsp[(3) - (3)]) ); + ;} + break; + + case 31: + +/* Line 1455 of yacc.c */ +#line 281 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_MULTIPLY ); + (yyval)->PushSubExpression( (yyvsp[(1) - (3)]) ); + (yyval)->PushSubExpression( (yyvsp[(3) - (3)]) ); + ;} + break; + + case 32: + +/* Line 1455 of yacc.c */ +#line 288 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_DIVIDE ); + (yyval)->PushSubExpression( (yyvsp[(1) - (3)]) ); + (yyval)->PushSubExpression( (yyvsp[(3) - (3)]) ); + ;} + break; + + case 33: + +/* Line 1455 of yacc.c */ +#line 295 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_MODULUS ); + (yyval)->PushSubExpression( (yyvsp[(1) - (3)]) ); + (yyval)->PushSubExpression( (yyvsp[(3) - (3)]) ); + ;} + break; + + case 34: + +/* Line 1455 of yacc.c */ +#line 302 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_CELL ); + (yyval)->PushSubExpression( (yyvsp[(2) - (3)]) ); + ;} + break; + + case 35: + +/* Line 1455 of yacc.c */ +#line 309 "ods_formula_parser.y" + { + (yyval) = (yyvsp[(3) - (3)]); + (yyvsp[(3) - (3)])->PushSubExpression( (yyvsp[(1) - (3)]) ); + ;} + break; + + case 36: + +/* Line 1455 of yacc.c */ +#line 315 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_LIST ); + (yyval)->PushSubExpression( (yyvsp[(1) - (1)]) ); + ;} + break; + + case 37: + +/* Line 1455 of yacc.c */ +#line 322 "ods_formula_parser.y" + { + (yyval) = (yyvsp[(3) - (3)]); + (yyvsp[(3) - (3)])->PushSubExpression( (yyvsp[(1) - (3)]) ); + ;} + break; + + case 38: + +/* Line 1455 of yacc.c */ +#line 328 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_LIST ); + (yyval)->PushSubExpression( (yyvsp[(1) - (1)]) ); + ;} + break; + + case 39: + +/* Line 1455 of yacc.c */ +#line 333 "ods_formula_parser.y" + { + (yyval) = (yyvsp[(3) - (3)]); + (yyvsp[(3) - (3)])->PushSubExpression( (yyvsp[(1) - (3)]) ); + ;} + break; + + case 40: + +/* Line 1455 of yacc.c */ +#line 339 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_LIST ); + (yyval)->PushSubExpression( (yyvsp[(1) - (1)]) ); + ;} + break; + + case 41: + +/* Line 1455 of yacc.c */ +#line 346 "ods_formula_parser.y" + { + (yyval) = new ods_formula_node( ODS_CELL_RANGE ); + (yyval)->PushSubExpression( (yyvsp[(2) - (5)]) ); + (yyval)->PushSubExpression( (yyvsp[(4) - (5)]) ); + ;} + break; + + + +/* Line 1455 of yacc.c */ +#line 2033 "ods_formula_parser.cpp" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (context, YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (context, yymsg); + } + else + { + yyerror (context, YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined(yyoverflow) || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, context); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula_parser.hpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula_parser.hpp new file mode 100644 index 000000000..1657c602d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula_parser.hpp @@ -0,0 +1,71 @@ + +/* A Bison parser, made by GNU Bison 2.4.1. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + * Copyright (c) 2012, Even Rouault + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + ODST_NUMBER = 258, + ODST_STRING = 259, + ODST_IDENTIFIER = 260, + ODST_FUNCTION_NO_ARG = 261, + ODST_FUNCTION_SINGLE_ARG = 262, + ODST_FUNCTION_TWO_ARG = 263, + ODST_FUNCTION_THREE_ARG = 264, + ODST_FUNCTION_ARG_LIST = 265, + ODST_START = 266, + ODST_NOT = 267, + ODST_OR = 268, + ODST_AND = 269, + ODST_IF = 270, + ODST_UMINUS = 271 + }; +#endif + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula_parser.y b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula_parser.y new file mode 100644 index 000000000..23a59a8b7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ods_formula_parser.y @@ -0,0 +1,351 @@ +%{ +/****************************************************************************** + * $Id: ods_formula_parser.y 27745 2014-09-27 16:38:57Z goatbar $ + * + * Component: OGR ODS Formula Engine + * Purpose: expression and select parser grammar. + * Requires Bison 2.4.0 or newer to process. Use "make parser" target. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ods_formula.h" + +#define YYSTYPE ods_formula_node* + +/* Defining YYSTYPE_IS_TRIVIAL is needed because the parser is generated as a C++ file. */ +/* See http://www.gnu.org/s/bison/manual/html_node/Memory-Management.html that suggests */ +/* increase YYINITDEPTH instead, but this will consume memory. */ +/* Setting YYSTYPE_IS_TRIVIAL overcomes this limitation, but might be fragile because */ +/* it appears to be a non documented feature of Bison */ +#define YYSTYPE_IS_TRIVIAL 1 + +static void ods_formulaerror( CPL_UNUSED ods_formula_parse_context *context, + const char *msg ) +{ + CPLError( CE_Failure, CPLE_AppDefined, + "Formula Parsing Error: %s", msg ); +} + +%} + +%define api.pure +%require "2.4.0" + +%parse-param {ods_formula_parse_context *context} +%lex-param {ods_formula_parse_context *context} + +%token ODST_NUMBER +%token ODST_STRING +%token ODST_IDENTIFIER +%token ODST_FUNCTION_NO_ARG +%token ODST_FUNCTION_SINGLE_ARG +%token ODST_FUNCTION_TWO_ARG +%token ODST_FUNCTION_THREE_ARG +%token ODST_FUNCTION_ARG_LIST + +%token ODST_START + +%left ODST_NOT +%left ODST_OR +%left ODST_AND +%left ODST_IF + +%left '+' '-' '&' +%left '*' '/' '%' +%left ODST_UMINUS + +/* Any grammar rule that does $$ = must be listed afterwards */ +/* as well as ODST_NUMBER ODST_STRING ODST_IDENTIFIER that are allocated by ods_formulalex() */ +%destructor { delete $$; } ODST_NUMBER ODST_STRING ODST_IDENTIFIER ODST_FUNCTION_NO_ARG ODST_FUNCTION_SINGLE_ARG ODST_FUNCTION_TWO_ARG ODST_FUNCTION_THREE_ARG ODST_FUNCTION_ARG_LIST +%destructor { delete $$; } value_expr value_expr_list cell_range value_expr_and_cell_range_list + +%% + +input: + ODST_START value_expr + { + context->poRoot = $2; + } + +comma: ',' | ';' + +value_expr: + + ODST_NUMBER + { + $$ = $1; + } + + | ODST_STRING + { + $$ = $1; + } + + | ODST_FUNCTION_NO_ARG '(' ')' + { + $$ = $1; + } + + | ODST_FUNCTION_SINGLE_ARG '(' value_expr ')' + { + $$ = $1; + $$->PushSubExpression( $3 ); + } + + | ODST_FUNCTION_TWO_ARG '(' value_expr comma value_expr ')' + { + $$ = $1; + $$->PushSubExpression( $3 ); + $$->PushSubExpression( $5 ); + } + + | ODST_FUNCTION_THREE_ARG '(' value_expr comma value_expr comma value_expr ')' + { + $$ = $1; + $$->PushSubExpression( $3 ); + $$->PushSubExpression( $5 ); + $$->PushSubExpression( $7 ); + } + + | ODST_AND '(' value_expr_list ')' + { + $$ = new ods_formula_node( ODS_AND ); + $3->ReverseSubExpressions(); + $$->PushSubExpression( $3 ); + } + + | ODST_OR '(' value_expr_list ')' + { + $$ = new ods_formula_node( ODS_OR ); + $3->ReverseSubExpressions(); + $$->PushSubExpression( $3 ); + } + + | ODST_NOT '(' value_expr ')' + { + $$ = new ods_formula_node( ODS_NOT ); + $$->PushSubExpression( $3 ); + } + + | ODST_IF '(' value_expr comma value_expr ')' + { + $$ = new ods_formula_node( ODS_IF ); + $$->PushSubExpression( $3 ); + $$->PushSubExpression( $5 ); + } + + | ODST_IF '(' value_expr comma value_expr comma value_expr ')' + { + $$ = new ods_formula_node( ODS_IF ); + $$->PushSubExpression( $3 ); + $$->PushSubExpression( $5 ); + $$->PushSubExpression( $7 ); + } + + | ODST_FUNCTION_ARG_LIST '(' value_expr_and_cell_range_list ')' + { + $$ = $1; + $3->ReverseSubExpressions(); + $$->PushSubExpression( $3 ); + } + + | '(' value_expr ')' + { + $$ = $2; + } + + | value_expr '=' value_expr + { + $$ = new ods_formula_node( ODS_EQ ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr '<' '>' value_expr + { + $$ = new ods_formula_node( ODS_NE ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $4 ); + } + + | value_expr '!' '=' value_expr + { + $$ = new ods_formula_node( ODS_NE ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $4 ); + } + + | value_expr '<' value_expr + { + $$ = new ods_formula_node( ODS_LT ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr '>' value_expr + { + $$ = new ods_formula_node( ODS_GT ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr '<' '=' value_expr + { + $$ = new ods_formula_node( ODS_LE ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $4 ); + } + + | value_expr '=' '<' value_expr + { + $$ = new ods_formula_node( ODS_LE ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $4 ); + } + + | value_expr '=' '>' value_expr + { + $$ = new ods_formula_node( ODS_LE ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $4 ); + } + + | value_expr '>' '=' value_expr + { + $$ = new ods_formula_node( ODS_GE ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $4 ); + } + + | '-' value_expr %prec ODST_UMINUS + { + if ($2->eNodeType == SNT_CONSTANT) + { + $$ = $2; + $$->int_value *= -1; + $$->float_value *= -1; + } + else + { + $$ = new ods_formula_node( ODS_MULTIPLY ); + $$->PushSubExpression( new ods_formula_node(-1) ); + $$->PushSubExpression( $2 ); + } + } + + | value_expr '+' value_expr + { + $$ = new ods_formula_node( ODS_ADD ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr '-' value_expr + { + $$ = new ods_formula_node( ODS_SUBTRACT ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr '&' value_expr + { + $$ = new ods_formula_node( ODS_CONCAT ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr '*' value_expr + { + $$ = new ods_formula_node( ODS_MULTIPLY ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr '/' value_expr + { + $$ = new ods_formula_node( ODS_DIVIDE ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr '%' value_expr + { + $$ = new ods_formula_node( ODS_MODULUS ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | '[' ODST_IDENTIFIER ']' + { + $$ = new ods_formula_node( ODS_CELL ); + $$->PushSubExpression( $2 ); + } + +value_expr_list: + value_expr comma value_expr_list + { + $$ = $3; + $3->PushSubExpression( $1 ); + } + + | value_expr + { + $$ = new ods_formula_node( ODS_LIST ); + $$->PushSubExpression( $1 ); + } + +value_expr_and_cell_range_list: + value_expr comma value_expr_and_cell_range_list + { + $$ = $3; + $3->PushSubExpression( $1 ); + } + + | value_expr + { + $$ = new ods_formula_node( ODS_LIST ); + $$->PushSubExpression( $1 ); + } + | cell_range comma value_expr_and_cell_range_list + { + $$ = $3; + $3->PushSubExpression( $1 ); + } + + | cell_range + { + $$ = new ods_formula_node( ODS_LIST ); + $$->PushSubExpression( $1 ); + } + +cell_range: + '[' ODST_IDENTIFIER ':' ODST_IDENTIFIER ']' + { + $$ = new ods_formula_node( ODS_CELL_RANGE ); + $$->PushSubExpression( $2 ); + $$->PushSubExpression( $4 ); + } diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ogr_ods.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ogr_ods.h new file mode 100644 index 000000000..86550dccb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ogr_ods.h @@ -0,0 +1,239 @@ +/****************************************************************************** + * $Id: ogr_ods.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: ODS Translator + * Purpose: Definition of classes for OGR OpenOfficeSpreadsheet .ods driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_ODS_H_INCLUDED +#define _OGR_ODS_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "ogr_mem.h" + +#include "ogr_expat.h" + +#include +#include +#include + +/************************************************************************/ +/* OGRODSLayer */ +/************************************************************************/ + +class OGRODSDataSource; + +class OGRODSLayer : public OGRMemLayer +{ + OGRODSDataSource* poDS; + int bUpdated; + int bHasHeaderLine; + + public: + OGRODSLayer( OGRODSDataSource* poDSIn, + const char * pszName, + int bUpdateIn = FALSE); + + void SetUpdated(int bUpdatedIn = TRUE); + + int GetHasHeaderLine() { return bHasHeaderLine; } + void SetHasHeaderLine(int bIn) { bHasHeaderLine = bIn; } + + const char *GetName() { return OGRMemLayer::GetLayerDefn()->GetName(); }; + OGRwkbGeometryType GetGeomType() { return wkbNone; } + virtual OGRSpatialReference *GetSpatialRef() { return NULL; } + + /* For external usage. Mess with FID */ + virtual OGRFeature * GetNextFeature(); + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + /* For internal usage, for cell resolver */ + OGRFeature * GetNextFeatureWithoutFIDHack() { return OGRMemLayer::GetNextFeature(); } + OGRErr SetFeatureWithoutFIDHack( OGRFeature *poFeature ) { SetUpdated(); return OGRMemLayer::ISetFeature(poFeature); } + + OGRErr ICreateFeature( OGRFeature *poFeature ) + { SetUpdated(); return OGRMemLayer::ICreateFeature(poFeature); } + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ) + { SetUpdated(); return OGRMemLayer::CreateField(poField, bApproxOK); } + + virtual OGRErr DeleteField( int iField ) + { SetUpdated(); return OGRMemLayer::DeleteField(iField); } + + virtual OGRErr ReorderFields( int* panMap ) + { SetUpdated(); return OGRMemLayer::ReorderFields(panMap); } + + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) + { SetUpdated(); return OGRMemLayer::AlterFieldDefn(iField, poNewFieldDefn, nFlags); } + + virtual OGRErr SyncToDisk(); +}; + +/************************************************************************/ +/* OGRODSDataSource */ +/************************************************************************/ +#define STACK_SIZE 5 + +typedef enum +{ + STATE_DEFAULT, + STATE_TABLE, + STATE_ROW, + STATE_CELL, + STATE_TEXTP, +} HandlerStateEnum; + +typedef struct +{ + HandlerStateEnum eVal; + int nBeginDepth; +} HandlerState; + +class OGRODSDataSource : public OGRDataSource +{ + char* pszName; + int bUpdatable; + int bUpdated; + int bAnalysedFile; + + int nLayers; + OGRLayer **papoLayers; + + VSILFILE* fpSettings; + std::string osCurrentConfigTableName; + std::string osConfigName; + int nFlags; + std::set osSetLayerHasSplitter; + void AnalyseSettings(); + + VSILFILE* fpContent; + void AnalyseFile(); + + int bFirstLineIsHeaders; + int bAutodetectTypes; + + XML_Parser oParser; + int bStopParsing; + int nWithoutEventCounter; + int nDataHandlerCounter; + int nCurLine; + int nEmptyRowsAccumulated; + int nRowsRepeated; + int nCurCol; + int nCellsRepeated; + int bEndTableParsing; + + OGRODSLayer *poCurLayer; + + int nStackDepth; + int nDepth; + HandlerState stateStack[STACK_SIZE]; + + CPLString osValueType; + CPLString osValue; + std::string osFormula; + + std::vector apoFirstLineValues; + std::vector apoFirstLineTypes; + std::vector apoCurLineValues; + std::vector apoCurLineTypes; + + void PushState(HandlerStateEnum eVal); + void startElementDefault(const char *pszName, const char **ppszAttr); + void startElementTable(const char *pszName, const char **ppszAttr); + void endElementTable(const char *pszName); + void startElementRow(const char *pszName, const char **ppszAttr); + void endElementRow(const char *pszName); + void startElementCell(const char *pszName, const char **ppszAttr); + void endElementCell(const char *pszName); + void dataHandlerTextP(const char *data, int nLen); + + void DetectHeaderLine(); + + OGRFieldType GetOGRFieldType(const char* pszValue, + const char* pszValueType); + + void DeleteLayer( const char *pszLayerName ); + + public: + OGRODSDataSource(); + ~OGRODSDataSource(); + + int Open( const char * pszFilename, + VSILFILE* fpContentIn, + VSILFILE* fpSettingsIn, + int bUpdatableIn ); + int Create( const char * pszName, char **papszOptions ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount(); + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); + + virtual OGRLayer* ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ); + virtual OGRErr DeleteLayer(int iLayer); + + virtual void FlushCache(); + + void startElementCbk(const char *pszName, const char **ppszAttr); + void endElementCbk(const char *pszName); + void dataHandlerCbk(const char *data, int nLen); + + void startElementStylesCbk(const char *pszName, const char **ppszAttr); + void endElementStylesCbk(const char *pszName); + void dataHandlerStylesCbk(const char *data, int nLen); + + int GetUpdatable() { return bUpdatable; } + void SetUpdated() { bUpdated = TRUE; } +}; + +/************************************************************************/ +/* OGRODSDriver */ +/************************************************************************/ + +class OGRODSDriver : public OGRSFDriver +{ + public: + ~OGRODSDriver(); + + virtual const char* GetName(); + virtual OGRDataSource* Open( const char *, int ); + virtual int TestCapability( const char * ); + + virtual OGRDataSource *CreateDataSource( const char *pszName, + char ** = NULL ); + virtual OGRErr DeleteDataSource( const char *pszName ); +}; + + +#endif /* ndef _OGR_ODS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ogrodsdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ogrodsdatasource.cpp new file mode 100644 index 000000000..74815ee46 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ogrodsdatasource.cpp @@ -0,0 +1,1964 @@ +/****************************************************************************** + * $Id: ogrodsdatasource.cpp 28900 2015-04-14 09:40:34Z rouault $ + * + * Project: ODS Translator + * Purpose: Implements OGRODSDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ods.h" +#include "ogr_mem.h" +#include "ogr_p.h" +#include "cpl_conv.h" +#include "ods_formula.h" +#include + +CPL_CVSID("$Id: ogrodsdatasource.cpp 28900 2015-04-14 09:40:34Z rouault $"); + + +/************************************************************************/ +/* ODSCellEvaluator */ +/************************************************************************/ + +class ODSCellEvaluator : public IODSCellEvaluator +{ +private: + OGRODSLayer* poLayer; + std::set > oVisisitedCells; + +public: + ODSCellEvaluator(OGRODSLayer* poLayerIn) : poLayer(poLayerIn) {} + + int EvaluateRange(int nRow1, int nCol1, int nRow2, int nCol2, + std::vector& aoOutValues); + + int Evaluate(int nRow, int nCol); +}; + +/************************************************************************/ +/* OGRODSLayer() */ +/************************************************************************/ + +OGRODSLayer::OGRODSLayer( OGRODSDataSource* poDSIn, + const char * pszName, + int bUpdatedIn) : + OGRMemLayer(pszName, NULL, wkbNone) +{ + poDS = poDSIn; + bUpdated = bUpdatedIn; + bHasHeaderLine = FALSE; +} + +/************************************************************************/ +/* Updated() */ +/************************************************************************/ + +void OGRODSLayer::SetUpdated(int bUpdatedIn) +{ + if (bUpdatedIn && !bUpdated && poDS->GetUpdatable()) + { + bUpdated = TRUE; + poDS->SetUpdated(); + } + else if (bUpdated && !bUpdatedIn) + { + bUpdated = FALSE; + } +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr OGRODSLayer::SyncToDisk() +{ + poDS->FlushCache(); + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature* OGRODSLayer::GetNextFeature() +{ + OGRFeature* poFeature = OGRMemLayer::GetNextFeature(); + if (poFeature) + poFeature->SetFID(poFeature->GetFID() + 1 + bHasHeaderLine); + return poFeature; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature* OGRODSLayer::GetFeature( GIntBig nFeatureId ) +{ + OGRFeature* poFeature = OGRMemLayer::GetFeature(nFeatureId - (1 + bHasHeaderLine)); + if (poFeature) + poFeature->SetFID(nFeatureId); + return poFeature; +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRODSLayer::ISetFeature( OGRFeature *poFeature ) +{ + if (poFeature == NULL) + return OGRMemLayer::ISetFeature(poFeature); + + GIntBig nFID = poFeature->GetFID(); + if (nFID != OGRNullFID) + poFeature->SetFID(nFID - (1 + bHasHeaderLine)); + SetUpdated(); + OGRErr eErr = OGRMemLayer::ISetFeature(poFeature); + poFeature->SetFID(nFID); + return eErr; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRODSLayer::DeleteFeature( GIntBig nFID ) +{ + SetUpdated(); + return OGRMemLayer::DeleteFeature(nFID - (1 + bHasHeaderLine)); +} + +/************************************************************************/ +/* OGRODSDataSource() */ +/************************************************************************/ + +OGRODSDataSource::OGRODSDataSource() + +{ + pszName = NULL; + fpContent = NULL; + fpSettings = NULL; + bUpdatable = FALSE; + bUpdated = FALSE; + bAnalysedFile = FALSE; + + nLayers = 0; + papoLayers = NULL; + + bFirstLineIsHeaders = FALSE; + + oParser = NULL; + bStopParsing = FALSE; + nWithoutEventCounter = 0; + nDataHandlerCounter = 0; + nStackDepth = 0; + nDepth = 0; + nCurLine = 0; + nEmptyRowsAccumulated = 0; + nCurCol = 0; + nRowsRepeated = 0; + nCellsRepeated = 0; + stateStack[0].eVal = STATE_DEFAULT; + stateStack[0].nBeginDepth = 0; + bEndTableParsing = FALSE; + + poCurLayer = NULL; + + const char* pszODSFieldTypes = + CPLGetConfigOption("OGR_ODS_FIELD_TYPES", ""); + bAutodetectTypes = !EQUAL(pszODSFieldTypes, "STRING"); +} + +/************************************************************************/ +/* ~OGRODSDataSource() */ +/************************************************************************/ + +OGRODSDataSource::~OGRODSDataSource() + +{ + FlushCache(); + + CPLFree( pszName ); + + if (fpContent) + VSIFCloseL(fpContent); + if (fpSettings) + VSIFCloseL(fpSettings); + + for(int i=0;i= nLayers) + return NULL; + + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int OGRODSDataSource::GetLayerCount() +{ + AnalyseFile(); + return nLayers; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRODSDataSource::Open( const char * pszFilename, + VSILFILE* fpContentIn, + VSILFILE* fpSettingsIn, + int bUpdatableIn) + +{ + bUpdatable = bUpdatableIn; + + pszName = CPLStrdup( pszFilename ); + fpContent = fpContentIn; + fpSettings = fpSettingsIn; + + return TRUE; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRODSDataSource::Create( const char * pszFilename, + CPL_UNUSED char **papszOptions ) +{ + bUpdated = TRUE; + bUpdatable = TRUE; + bAnalysedFile = TRUE; + + pszName = CPLStrdup( pszFilename ); + + return TRUE; +} + +/************************************************************************/ +/* startElementCbk() */ +/************************************************************************/ + +static void XMLCALL startElementCbk(void *pUserData, const char *pszName, + const char **ppszAttr) +{ + ((OGRODSDataSource*)pUserData)->startElementCbk(pszName, ppszAttr); +} + +void OGRODSDataSource::startElementCbk(const char *pszName, + const char **ppszAttr) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + switch(stateStack[nStackDepth].eVal) + { + case STATE_DEFAULT: startElementDefault(pszName, ppszAttr); break; + case STATE_TABLE: startElementTable(pszName, ppszAttr); break; + case STATE_ROW: startElementRow(pszName, ppszAttr); break; + case STATE_CELL: startElementCell(pszName, ppszAttr); break; + case STATE_TEXTP: break; + default: break; + } + nDepth++; +} + +/************************************************************************/ +/* endElementCbk() */ +/************************************************************************/ + +static void XMLCALL endElementCbk(void *pUserData, const char *pszName) +{ + ((OGRODSDataSource*)pUserData)->endElementCbk(pszName); +} + +void OGRODSDataSource::endElementCbk(const char *pszName) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + nDepth--; + switch(stateStack[nStackDepth].eVal) + { + case STATE_DEFAULT: break; + case STATE_TABLE: endElementTable(pszName); break; + case STATE_ROW: endElementRow(pszName); break; + case STATE_CELL: endElementCell(pszName); break; + case STATE_TEXTP: break; + default: break; + } + + if (stateStack[nStackDepth].nBeginDepth == nDepth) + nStackDepth --; +} + +/************************************************************************/ +/* dataHandlerCbk() */ +/************************************************************************/ + +static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen) +{ + ((OGRODSDataSource*)pUserData)->dataHandlerCbk(data, nLen); +} + +void OGRODSDataSource::dataHandlerCbk(const char *data, int nLen) +{ + if (bStopParsing) return; + + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "File probably corrupted (million laugh pattern)"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + + nWithoutEventCounter = 0; + + switch(stateStack[nStackDepth].eVal) + { + case STATE_DEFAULT: break; + case STATE_TABLE: break; + case STATE_ROW: break; + case STATE_CELL: break; + case STATE_TEXTP: dataHandlerTextP(data, nLen); + default: break; + } +} + +/************************************************************************/ +/* PushState() */ +/************************************************************************/ + +void OGRODSDataSource::PushState(HandlerStateEnum eVal) +{ + if (nStackDepth + 1 == STACK_SIZE) + { + bStopParsing = TRUE; + return; + } + nStackDepth ++; + stateStack[nStackDepth].eVal = eVal; + stateStack[nStackDepth].nBeginDepth = nDepth; +} + +/************************************************************************/ +/* GetAttributeValue() */ +/************************************************************************/ + +static const char* GetAttributeValue(const char **ppszAttr, + const char* pszKey, + const char* pszDefaultVal) +{ + while(*ppszAttr) + { + if (strcmp(ppszAttr[0], pszKey) == 0) + return ppszAttr[1]; + ppszAttr += 2; + } + return pszDefaultVal; +} + +/************************************************************************/ +/* GetOGRFieldType() */ +/************************************************************************/ + +OGRFieldType OGRODSDataSource::GetOGRFieldType(const char* pszValue, + const char* pszValueType) +{ + if (!bAutodetectTypes || pszValueType == NULL) + return OFTString; + else if (strcmp(pszValueType, "string") == 0) + return OFTString; + else if (strcmp(pszValueType, "float") == 0 || + strcmp(pszValueType, "currency") == 0) + { + if (CPLGetValueType(pszValue) == CPL_VALUE_INTEGER) + { + GIntBig nVal = CPLAtoGIntBig(pszValue); + if( (GIntBig)(int)nVal != nVal ) + return OFTInteger64; + else + return OFTInteger; + } + else + return OFTReal; + } + else if (strcmp(pszValueType, "percentage") == 0) + return OFTReal; + else if (strcmp(pszValueType, "date") == 0) + { + if (strlen(pszValue) == 4 + 1 + 2 + 1 + 2) + return OFTDate; + else + return OFTDateTime; + } + else if (strcmp(pszValueType, "time") == 0) + { + return OFTTime; + } + else + return OFTString; +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +static void SetField(OGRFeature* poFeature, + int i, + const char* pszValue) +{ + if (pszValue[0] == '\0') + return; + + OGRFieldType eType = poFeature->GetFieldDefnRef(i)->GetType(); + if (eType == OFTTime) + { + int nHour, nHourRepeated, nMinute, nSecond; + char c; + if (strncmp(pszValue, "PT", 2) == 0 && + sscanf(pszValue + 2, "%02d%c%02d%c%02d%c", + &nHour, &c, &nMinute, &c, &nSecond, &c) == 6) + { + poFeature->SetField(i, 0, 0, 0, nHour, nMinute, nSecond, 0); + } + /* bug with kspread 2.1.2 ? */ + /* ex PT121234M56S */ + else if (strncmp(pszValue, "PT", 2) == 0 && + sscanf(pszValue + 2, "%02d%02d%02d%c%02d%c", + &nHour, &nHourRepeated, &nMinute, &c, &nSecond, &c) == 6 && + nHour == nHourRepeated) + { + poFeature->SetField(i, 0, 0, 0, nHour, nMinute, nSecond, 0); + } + } + else if (eType == OFTDate || eType == OFTDateTime) + { + OGRField sField; + if (OGRParseXMLDateTime( pszValue, &sField )) + { + poFeature->SetField(i, &sField); + } + } + else + poFeature->SetField(i, pszValue); +} + +/************************************************************************/ +/* DetectHeaderLine() */ +/************************************************************************/ + +void OGRODSDataSource::DetectHeaderLine() + +{ + int bHeaderLineCandidate = TRUE; + size_t i; + for(i = 0; i < apoFirstLineTypes.size(); i++) + { + if (apoFirstLineTypes[i] != "string") + { + /* If the values in the first line are not text, then it is */ + /* not a header line */ + bHeaderLineCandidate = FALSE; + break; + } + } + + size_t nCountTextOnCurLine = 0; + size_t nCountNonEmptyOnCurLine = 0; + for(i = 0; bHeaderLineCandidate && i < apoCurLineTypes.size(); i++) + { + if (apoCurLineTypes[i] == "string") + { + /* If there are only text values on the second line, then we cannot */ + /* know if it is a header line or just a regular line */ + nCountTextOnCurLine ++; + } + else if (apoCurLineTypes[i] != "") + { + nCountNonEmptyOnCurLine ++; + } + } + + const char* pszODSHeaders = CPLGetConfigOption("OGR_ODS_HEADERS", ""); + bFirstLineIsHeaders = FALSE; + if (EQUAL(pszODSHeaders, "FORCE")) + bFirstLineIsHeaders = TRUE; + else if (EQUAL(pszODSHeaders, "DISABLE")) + bFirstLineIsHeaders = FALSE; + else if (osSetLayerHasSplitter.find(poCurLayer->GetName()) != + osSetLayerHasSplitter.end()) + { + bFirstLineIsHeaders = TRUE; + } + else if (bHeaderLineCandidate && + apoFirstLineTypes.size() != 0 && + apoFirstLineTypes.size() == apoCurLineTypes.size() && + nCountTextOnCurLine != apoFirstLineTypes.size() && + nCountNonEmptyOnCurLine != 0) + { + bFirstLineIsHeaders = TRUE; + } + CPLDebug("ODS", "%s %s", + poCurLayer->GetName(), + bFirstLineIsHeaders ? "has header line" : "has no header line"); +} + +/************************************************************************/ +/* startElementDefault() */ +/************************************************************************/ + +void OGRODSDataSource::startElementDefault(const char *pszName, + const char **ppszAttr) +{ + if (strcmp(pszName, "table:table") == 0) + { + const char* pszTableName = + GetAttributeValue(ppszAttr, "table:name", "unnamed"); + + poCurLayer = new OGRODSLayer(this, pszTableName); + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers++] = poCurLayer; + + nCurLine = 0; + nEmptyRowsAccumulated = 0; + apoFirstLineValues.resize(0); + apoFirstLineTypes.resize(0); + PushState(STATE_TABLE); + bEndTableParsing = FALSE; + } +} + +/************************************************************************/ +/* startElementTable() */ +/************************************************************************/ + +void OGRODSDataSource::startElementTable(const char *pszName, + const char **ppszAttr) +{ + if (strcmp(pszName, "table:table-row") == 0 && !bEndTableParsing) + { + nRowsRepeated = atoi( + GetAttributeValue(ppszAttr, "table:number-rows-repeated", "1")); + if (nRowsRepeated > 65536) + { + bEndTableParsing = TRUE; + return; + } + + nCurCol = 0; + + apoCurLineValues.resize(0); + apoCurLineTypes.resize(0); + + PushState(STATE_ROW); + } +} + +/************************************************************************/ +/* endElementTable() */ +/************************************************************************/ + +void OGRODSDataSource::endElementTable(CPL_UNUSED const char *pszName) +{ + if (stateStack[nStackDepth].nBeginDepth == nDepth) + { + CPLAssert(strcmp(pszName, "table:table") == 0); + + if (nCurLine == 0 || + (nCurLine == 1 && apoFirstLineValues.size() == 0)) + { + /* Remove empty sheet */ + delete poCurLayer; + nLayers --; + poCurLayer = NULL; + } + else if (nCurLine == 1) + { + /* If we have only one single line in the sheet */ + size_t i; + for(i = 0; i < apoFirstLineValues.size(); i++) + { + const char* pszFieldName = CPLSPrintf("Field%d", (int)i + 1); + OGRFieldType eType = GetOGRFieldType(apoFirstLineValues[i].c_str(), + apoFirstLineTypes[i].c_str()); + OGRFieldDefn oFieldDefn(pszFieldName, eType); + poCurLayer->CreateField(&oFieldDefn); + } + + OGRFeature* poFeature = new OGRFeature(poCurLayer->GetLayerDefn()); + for(i = 0; i < apoFirstLineValues.size(); i++) + { + SetField(poFeature, i, apoFirstLineValues[i].c_str()); + } + poCurLayer->CreateFeature(poFeature); + delete poFeature; + } + + if (poCurLayer) + { + OGRFeature* poFeature; + + if (CSLTestBoolean(CPLGetConfigOption("ODS_RESOLVE_FORMULAS", "YES"))) + { + poCurLayer->ResetReading(); + + int nRow = 0; + poFeature = poCurLayer->GetNextFeature(); + while (poFeature) + { + for(int i=0;iGetFieldCount();i++) + { + if (poFeature->IsFieldSet(i) && + poFeature->GetFieldDefnRef(i)->GetType() == OFTString) + { + const char* pszVal = poFeature->GetFieldAsString(i); + if (strncmp(pszVal, "of:=", 4) == 0) + { + ODSCellEvaluator oCellEvaluator(poCurLayer); + oCellEvaluator.Evaluate(nRow, i); + } + } + } + delete poFeature; + + poFeature = poCurLayer->GetNextFeature(); + nRow ++; + } + } + + poCurLayer->ResetReading(); + + ((OGRMemLayer*)poCurLayer)->SetUpdatable(bUpdatable); + ((OGRMemLayer*)poCurLayer)->SetAdvertizeUTF8(TRUE); + ((OGRODSLayer*)poCurLayer)->SetUpdated(FALSE); + } + + poCurLayer = NULL; + } +} + +/************************************************************************/ +/* startElementRow() */ +/************************************************************************/ + +void OGRODSDataSource::startElementRow(const char *pszName, + const char **ppszAttr) +{ + if (strcmp(pszName, "table:table-cell") == 0) + { + PushState(STATE_CELL); + + osValueType = GetAttributeValue(ppszAttr, "office:value-type", ""); + const char* pszValue = + GetAttributeValue(ppszAttr, "office:value", NULL); + if (pszValue) + osValue = pszValue; + else + { + const char* pszDateValue = + GetAttributeValue(ppszAttr, "office:date-value", NULL); + if (pszDateValue) + osValue = pszDateValue; + else + osValue = GetAttributeValue(ppszAttr, "office:time-value", ""); + } + + const char* pszFormula = GetAttributeValue(ppszAttr, "table:formula", NULL); + if (pszFormula && strncmp(pszFormula, "of:=", 4) == 0) + { + osFormula = pszFormula; + if (osValueType.size() == 0) + osValueType = "formula"; + } + else + osFormula = ""; + + nCellsRepeated = atoi( + GetAttributeValue(ppszAttr, "table:number-columns-repeated", "1")); + } + else if (strcmp(pszName, "table:covered-table-cell") == 0) + { + /* Merged cell */ + apoCurLineValues.push_back(""); + apoCurLineTypes.push_back(""); + + nCurCol += 1; + } +} + +/************************************************************************/ +/* endElementRow() */ +/************************************************************************/ + +void OGRODSDataSource::endElementRow(CPL_UNUSED const char *pszName) +{ + if (stateStack[nStackDepth].nBeginDepth == nDepth) + { + CPLAssert(strcmp(pszName, "table:table-row") == 0); + + OGRFeature* poFeature; + size_t i; + + /* Remove blank columns at the right to defer type evaluation */ + /* until necessary */ + i = apoCurLineTypes.size(); + while(i > 0) + { + i --; + if (apoCurLineTypes[i] == "") + { + apoCurLineValues.resize(i); + apoCurLineTypes.resize(i); + } + else + break; + } + + /* Do not add immediately empty rows. Wait until there is another non */ + /* empty row */ + if (nCurLine >= 2 && apoCurLineTypes.size() == 0) + { + nEmptyRowsAccumulated += nRowsRepeated; + return; + } + else if (nEmptyRowsAccumulated > 0) + { + for(i = 0; i < (size_t)nEmptyRowsAccumulated; i++) + { + poFeature = new OGRFeature(poCurLayer->GetLayerDefn()); + poCurLayer->CreateFeature(poFeature); + delete poFeature; + } + nCurLine += nEmptyRowsAccumulated; + nEmptyRowsAccumulated = 0; + } + + /* Backup first line values and types in special arrays */ + if (nCurLine == 0) + { + apoFirstLineTypes = apoCurLineTypes; + apoFirstLineValues = apoCurLineValues; + + #if skip_leading_empty_rows + if (apoFirstLineTypes.size() == 0) + { + /* Skip leading empty rows */ + apoFirstLineTypes.resize(0); + apoFirstLineValues.resize(0); + return; + } + #endif + } + + if (nCurLine == 1) + { + DetectHeaderLine(); + + poCurLayer->SetHasHeaderLine(bFirstLineIsHeaders); + + if (bFirstLineIsHeaders) + { + for(i = 0; i < apoFirstLineValues.size(); i++) + { + const char* pszFieldName = apoFirstLineValues[i].c_str(); + if (pszFieldName[0] == '\0') + pszFieldName = CPLSPrintf("Field%d", (int)i + 1); + OGRFieldType eType = OFTString; + if (i < apoCurLineValues.size()) + { + eType = GetOGRFieldType(apoCurLineValues[i].c_str(), + apoCurLineTypes[i].c_str()); + } + OGRFieldDefn oFieldDefn(pszFieldName, eType); + poCurLayer->CreateField(&oFieldDefn); + } + } + else + { + for(i = 0; i < apoFirstLineValues.size(); i++) + { + const char* pszFieldName = + CPLSPrintf("Field%d", (int)i + 1); + OGRFieldType eType = GetOGRFieldType( + apoFirstLineValues[i].c_str(), + apoFirstLineTypes[i].c_str()); + OGRFieldDefn oFieldDefn(pszFieldName, eType); + poCurLayer->CreateField(&oFieldDefn); + } + + poFeature = new OGRFeature(poCurLayer->GetLayerDefn()); + for(i = 0; i < apoFirstLineValues.size(); i++) + { + SetField(poFeature, i, apoFirstLineValues[i].c_str()); + } + poCurLayer->CreateFeature(poFeature); + delete poFeature; + } + } + + if (nCurLine >= 1 || (nCurLine == 0 && nRowsRepeated > 1)) + { + /* Add new fields found on following lines. */ + if (apoCurLineValues.size() > + (size_t)poCurLayer->GetLayerDefn()->GetFieldCount()) + { + for(i = (size_t)poCurLayer->GetLayerDefn()->GetFieldCount(); + i < apoCurLineValues.size(); + i++) + { + const char* pszFieldName = + CPLSPrintf("Field%d", (int)i + 1); + OGRFieldType eType = GetOGRFieldType( + apoCurLineValues[i].c_str(), + apoCurLineTypes[i].c_str()); + OGRFieldDefn oFieldDefn(pszFieldName, eType); + poCurLayer->CreateField(&oFieldDefn); + } + } + + /* Update field type if necessary */ + if (bAutodetectTypes) + { + for(i = 0; i < apoCurLineValues.size(); i++) + { + if (apoCurLineValues[i].size()) + { + OGRFieldType eValType = GetOGRFieldType( + apoCurLineValues[i].c_str(), + apoCurLineTypes[i].c_str()); + OGRFieldType eFieldType = + poCurLayer->GetLayerDefn()->GetFieldDefn(i)->GetType(); + if (eFieldType == OFTDateTime && + (eValType == OFTDate || eValType == OFTTime) ) + { + /* ok */ + } + else if (eFieldType == OFTReal && (eValType == OFTInteger || eValType == OFTInteger64)) + { + /* ok */; + } + else if (eFieldType == OFTInteger64 && eValType == OFTInteger ) + { + /* ok */; + } + else if (eFieldType != OFTString && eValType != eFieldType) + { + OGRFieldDefn oNewFieldDefn( + poCurLayer->GetLayerDefn()->GetFieldDefn(i)); + if ((eFieldType == OFTDate || eFieldType == OFTTime) && + eValType == OFTDateTime) + oNewFieldDefn.SetType(OFTDateTime); + else if ((eFieldType == OFTInteger || eFieldType == OFTInteger64) && + eValType == OFTReal) + oNewFieldDefn.SetType(OFTReal); + else if( eFieldType == OFTInteger && eValType == OFTInteger64 ) + oNewFieldDefn.SetType(OFTInteger64); + else + oNewFieldDefn.SetType(OFTString); + poCurLayer->AlterFieldDefn(i, &oNewFieldDefn, + ALTER_TYPE_FLAG); + } + } + } + } + + /* Add feature for current line */ + for(int j=0;jGetLayerDefn()); + for(i = 0; i < apoCurLineValues.size(); i++) + { + SetField(poFeature, i, apoCurLineValues[i].c_str()); + } + poCurLayer->CreateFeature(poFeature); + delete poFeature; + } + } + + nCurLine += nRowsRepeated; + } +} + +/************************************************************************/ +/* startElementCell() */ +/************************************************************************/ + +void OGRODSDataSource::startElementCell(const char *pszName, + CPL_UNUSED const char **ppszAttr) +{ + if (osValue.size() == 0 && strcmp(pszName, "text:p") == 0) + { + PushState(STATE_TEXTP); + } +} + +/************************************************************************/ +/* endElementCell() */ +/************************************************************************/ + +void OGRODSDataSource::endElementCell(CPL_UNUSED const char *pszName) +{ + if (stateStack[nStackDepth].nBeginDepth == nDepth) + { + CPLAssert(strcmp(pszName, "table:table-cell") == 0); + + for(int i = 0; i < nCellsRepeated; i++) + { + if( osValue.size() ) + apoCurLineValues.push_back(osValue); + else + apoCurLineValues.push_back(osFormula); + apoCurLineTypes.push_back(osValueType); + } + + nCurCol += nCellsRepeated; + } +} + +/************************************************************************/ +/* dataHandlerTextP() */ +/************************************************************************/ + +void OGRODSDataSource::dataHandlerTextP(const char *data, int nLen) +{ + osValue.append(data, nLen); +} + +/************************************************************************/ +/* AnalyseFile() */ +/************************************************************************/ + +void OGRODSDataSource::AnalyseFile() +{ + if (bAnalysedFile) + return; + + bAnalysedFile = TRUE; + + AnalyseSettings(); + + oParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk); + XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk); + XML_SetUserData(oParser, this); + + nDepth = 0; + nStackDepth = 0; + stateStack[0].nBeginDepth = 0; + bStopParsing = FALSE; + nWithoutEventCounter = 0; + + VSIFSeekL( fpContent, 0, SEEK_SET ); + + char aBuf[BUFSIZ]; + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = + (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpContent ); + nDone = VSIFEofL(fpContent); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of ODS file failed : %s at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + bStopParsing = TRUE; + } + nWithoutEventCounter ++; + } while (!nDone && !bStopParsing && nWithoutEventCounter < 10); + + XML_ParserFree(oParser); + oParser = NULL; + + if (nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + VSIFCloseL(fpContent); + fpContent = NULL; + + bUpdated = FALSE; +} + +/************************************************************************/ +/* startElementStylesCbk() */ +/************************************************************************/ + +static void XMLCALL startElementStylesCbk(void *pUserData, const char *pszName, + const char **ppszAttr) +{ + ((OGRODSDataSource*)pUserData)->startElementStylesCbk(pszName, ppszAttr); +} + +void OGRODSDataSource::startElementStylesCbk(const char *pszName, + const char **ppszAttr) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + if (nStackDepth == 0 && + strcmp(pszName, "config:config-item-map-named") == 0 && + strcmp(GetAttributeValue(ppszAttr, "config:name", ""), "Tables") == 0) + { + stateStack[++nStackDepth].nBeginDepth = nDepth; + } + else if (nStackDepth == 1 && strcmp(pszName, "config:config-item-map-entry") == 0) + { + const char* pszTableName = GetAttributeValue(ppszAttr, "config:name", NULL); + if (pszTableName) + { + osCurrentConfigTableName = pszTableName; + nFlags = 0; + stateStack[++nStackDepth].nBeginDepth = nDepth; + } + } + else if (nStackDepth == 2 && strcmp(pszName, "config:config-item") == 0) + { + const char* pszConfigName = GetAttributeValue(ppszAttr, "config:name", NULL); + if (pszConfigName) + { + osConfigName = pszConfigName; + osValue = ""; + stateStack[++nStackDepth].nBeginDepth = nDepth; + } + } + + nDepth++; +} + +/************************************************************************/ +/* endElementStylesCbk() */ +/************************************************************************/ + +static void XMLCALL endElementStylesCbk(void *pUserData, const char *pszName) +{ + ((OGRODSDataSource*)pUserData)->endElementStylesCbk(pszName); +} + +void OGRODSDataSource::endElementStylesCbk(CPL_UNUSED const char *pszName) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + nDepth--; + + if (nStackDepth > 0 && stateStack[nStackDepth].nBeginDepth == nDepth) + { + if (nStackDepth == 2) + { + if (nFlags == (1 | 2)) + osSetLayerHasSplitter.insert(osCurrentConfigTableName); + } + if (nStackDepth == 3) + { + if (osConfigName == "VerticalSplitMode" && osValue == "2") + nFlags |= 1; + else if (osConfigName == "VerticalSplitPosition" && osValue == "1") + nFlags |= 2; + } + nStackDepth --; + } +} + +/************************************************************************/ +/* dataHandlerStylesCbk() */ +/************************************************************************/ + +static void XMLCALL dataHandlerStylesCbk(void *pUserData, const char *data, int nLen) +{ + ((OGRODSDataSource*)pUserData)->dataHandlerStylesCbk(data, nLen); +} + +void OGRODSDataSource::dataHandlerStylesCbk(const char *data, int nLen) +{ + if (bStopParsing) return; + + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "File probably corrupted (million laugh pattern)"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + + nWithoutEventCounter = 0; + + if (nStackDepth == 3) + { + osValue.append(data, nLen); + } +} + +/************************************************************************/ +/* AnalyseSettings() */ +/* */ +/* We parse settings.xml to see which layers have a vertical splitter */ +/* on the first line, so as to use it as the header line. */ +/************************************************************************/ + +void OGRODSDataSource::AnalyseSettings() +{ + if (fpSettings == NULL) + return; + + oParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oParser, ::startElementStylesCbk, ::endElementStylesCbk); + XML_SetCharacterDataHandler(oParser, ::dataHandlerStylesCbk); + XML_SetUserData(oParser, this); + + nDepth = 0; + nStackDepth = 0; + bStopParsing = FALSE; + nWithoutEventCounter = 0; + + VSIFSeekL( fpSettings, 0, SEEK_SET ); + + char aBuf[BUFSIZ]; + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = + (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpSettings ); + nDone = VSIFEofL(fpSettings); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of styles.xml file failed : %s at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + bStopParsing = TRUE; + } + nWithoutEventCounter ++; + } while (!nDone && !bStopParsing && nWithoutEventCounter < 10); + + XML_ParserFree(oParser); + oParser = NULL; + + if (nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + VSIFCloseL(fpSettings); + fpSettings = NULL; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRODSDataSource::ICreateLayer( const char * pszLayerName, + CPL_UNUSED OGRSpatialReference *poSRS, + CPL_UNUSED OGRwkbGeometryType eType, + char ** papszOptions ) +{ +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( !bUpdatable ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened read-only.\n" + "New layer %s cannot be created.\n", + pszName, pszLayerName ); + + return NULL; + } + + AnalyseFile(); + +/* -------------------------------------------------------------------- */ +/* Do we already have this layer? If so, should we blow it */ +/* away? */ +/* -------------------------------------------------------------------- */ + int iLayer; + + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) ) + { + if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL + && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") ) + { + DeleteLayer( pszLayerName ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + pszLayerName ); + return NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRLayer* poLayer = new OGRODSLayer(this, pszLayerName, TRUE); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers] = poLayer; + nLayers ++; + + bUpdated = TRUE; + + return poLayer; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +void OGRODSDataSource::DeleteLayer( const char *pszLayerName ) + +{ + int iLayer; + +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( !bUpdatable ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened read-only.\n" + "Layer %s cannot be deleted.\n", + pszName, pszLayerName ); + + return; + } + +/* -------------------------------------------------------------------- */ +/* Try to find layer. */ +/* -------------------------------------------------------------------- */ + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) ) + break; + } + + if( iLayer == nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to delete layer '%s', but this layer is not known to OGR.", + pszLayerName ); + return; + } + + DeleteLayer(iLayer); +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +OGRErr OGRODSDataSource::DeleteLayer(int iLayer) +{ + AnalyseFile(); + + if( iLayer < 0 || iLayer >= nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %d not in legal range of 0 to %d.", + iLayer, nLayers-1 ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Blow away our OGR structures related to the layer. This is */ +/* pretty dangerous if anything has a reference to this layer! */ +/* -------------------------------------------------------------------- */ + + delete papoLayers[iLayer]; + memmove( papoLayers + iLayer, papoLayers + iLayer + 1, + sizeof(void *) * (nLayers - iLayer - 1) ); + nLayers--; + + bUpdated = TRUE; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* HasHeaderLine() */ +/************************************************************************/ + +static int HasHeaderLine(OGRLayer* poLayer) +{ + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + int bHasHeaders = FALSE; + + for(int j=0;jGetFieldCount();j++) + { + if (strcmp(poFDefn->GetFieldDefn(j)->GetNameRef(), + CPLSPrintf("Field%d", j+1)) != 0) + bHasHeaders = TRUE; + } + + return bHasHeaders; +} + +/************************************************************************/ +/* WriteLayer() */ +/************************************************************************/ + +static void WriteLayer(VSILFILE* fp, OGRLayer* poLayer) +{ + int j; + const char* pszLayerName = poLayer->GetName(); + char* pszXML = OGRGetXML_UTF8_EscapedString(pszLayerName); + VSIFPrintfL(fp, "\n", pszXML); + CPLFree(pszXML); + + poLayer->ResetReading(); + + OGRFeature* poFeature = poLayer->GetNextFeature(); + + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + int bHasHeaders = HasHeaderLine(poLayer); + + for(j=0;jGetFieldCount();j++) + { + int nStyleNumber = 1; + if (poFDefn->GetFieldDefn(j)->GetType() == OFTDateTime) + nStyleNumber = 2; + VSIFPrintfL(fp, "\n", + nStyleNumber); + } + + if (bHasHeaders && poFeature != NULL) + { + VSIFPrintfL(fp, "\n"); + for(j=0;jGetFieldCount();j++) + { + const char* pszVal = poFDefn->GetFieldDefn(j)->GetNameRef(); + + VSIFPrintfL(fp, "\n"); + pszXML = OGRGetXML_UTF8_EscapedString(pszVal); + VSIFPrintfL(fp, "%s\n", pszXML); + CPLFree(pszXML); + VSIFPrintfL(fp, "\n"); + } + VSIFPrintfL(fp, "\n"); + } + + while(poFeature != NULL) + { + VSIFPrintfL(fp, "\n"); + for(j=0;jGetFieldCount();j++) + { + if (poFeature->IsFieldSet(j)) + { + OGRFieldType eType = poFDefn->GetFieldDefn(j)->GetType(); + + if (eType == OFTReal) + { + VSIFPrintfL(fp, "\n", + poFeature->GetFieldAsDouble(j)); + } + else if (eType == OFTInteger) + { + VSIFPrintfL(fp, "\n", + poFeature->GetFieldAsInteger(j)); + } + else if (eType == OFTInteger64) + { + VSIFPrintfL(fp, "\n", + poFeature->GetFieldAsInteger64(j)); + } + else if (eType == OFTDateTime) + { + int nYear, nMonth, nDay, nHour, nMinute, nTZFlag; + float fSecond; + poFeature->GetFieldAsDateTime(j, &nYear, &nMonth, &nDay, + &nHour, &nMinute, &fSecond, &nTZFlag ); + if( OGR_GET_MS(fSecond) ) + { + VSIFPrintfL(fp, "\n", + nYear, nMonth, nDay, nHour, nMinute, fSecond); + VSIFPrintfL(fp, "%02d/%02d/%04d %02d:%02d:%06.3f\n", + nDay, nMonth, nYear, nHour, nMinute, fSecond); + } + else + { + VSIFPrintfL(fp, "\n", + nYear, nMonth, nDay, nHour, nMinute, (int)fSecond); + VSIFPrintfL(fp, "%02d/%02d/%04d %02d:%02d:%02d\n", + nDay, nMonth, nYear, nHour, nMinute, (int)fSecond); + } + VSIFPrintfL(fp, "\n"); + } + else if (eType == OFTDate) + { + int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZFlag; + poFeature->GetFieldAsDateTime(j, &nYear, &nMonth, &nDay, + &nHour, &nMinute, &nSecond, &nTZFlag); + VSIFPrintfL(fp, "\n", + nYear, nMonth, nDay); + VSIFPrintfL(fp, "%02d/%02d/%04d\n", + nDay, nMonth, nYear); + VSIFPrintfL(fp, "\n"); + } + else if (eType == OFTTime) + { + int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZFlag; + poFeature->GetFieldAsDateTime(j, &nYear, &nMonth, &nDay, + &nHour, &nMinute, &nSecond, &nTZFlag); + VSIFPrintfL(fp, "\n", + nHour, nMinute, nSecond); + VSIFPrintfL(fp, "%02d:%02d:%02d\n", + nHour, nMinute, nSecond); + VSIFPrintfL(fp, "\n"); + } + else + { + const char* pszVal = poFeature->GetFieldAsString(j); + pszXML = OGRGetXML_UTF8_EscapedString(pszVal); + if (strncmp(pszVal, "of:=", 4) == 0) + { + VSIFPrintfL(fp, "\n", pszXML); + } + else + { + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "%s\n", pszXML); + VSIFPrintfL(fp, "\n"); + } + CPLFree(pszXML); + } + } + else + { + VSIFPrintfL(fp, "\n"); + } + } + VSIFPrintfL(fp, "\n"); + + delete poFeature; + poFeature = poLayer->GetNextFeature(); + } + + VSIFPrintfL(fp, "\n"); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void OGRODSDataSource::FlushCache() +{ + if (!bUpdated) + return; + + CPLAssert(fpSettings == NULL); + CPLAssert(fpContent == NULL); + + VSIStatBufL sStat; + if (VSIStatL(pszName, &sStat) == 0) + { + if (VSIUnlink( pszName ) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Cannot delete %s", pszName); + return; + } + } + + /* Maintain new ZIP files opened */ + void *hZIP = CPLCreateZip(pszName, NULL); + if (hZIP == NULL) + { + CPLError(CE_Failure, CPLE_FileIO, + "Cannot create %s", pszName); + return; + } + + /* Write uncopressed mimetype */ + char** papszOptions = CSLAddString(NULL, "COMPRESSED=NO"); + if( CPLCreateFileInZip(hZIP, "mimetype", papszOptions ) != CE_None ) + { + CSLDestroy(papszOptions); + CPLCloseZip(hZIP); + return; + } + CSLDestroy(papszOptions); + if( CPLWriteFileInZip(hZIP, "application/vnd.oasis.opendocument.spreadsheet", + strlen("application/vnd.oasis.opendocument.spreadsheet")) != CE_None ) + { + CPLCloseZip(hZIP); + return; + } + CPLCloseFileInZip(hZIP); + + /* Now close ZIP file */ + CPLCloseZip(hZIP); + hZIP = NULL; + + /* Re-open with VSILFILE */ + VSILFILE* fpZIP = VSIFOpenL(CPLSPrintf("/vsizip/%s", pszName), "ab"); + if (fpZIP == NULL) + return; + + VSILFILE* fp; + int i; + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/META-INF/manifest.xml", pszName), "wb"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/meta.xml", pszName), "wb"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/settings.xml", pszName), "wb"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + for(i=0;iGetName()); + VSIFPrintfL(fp, "\n", pszXML); + CPLFree(pszXML); + VSIFPrintfL(fp, "2\n"); + VSIFPrintfL(fp, "1\n"); + VSIFPrintfL(fp, "2\n"); + VSIFPrintfL(fp, "0\n"); + VSIFPrintfL(fp, "1\n"); + VSIFPrintfL(fp, "\n"); + } + } + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/styles.xml", pszName), "wb"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/content.xml", pszName), "wb"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "/\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "/\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, ":\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, ":\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "/\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "/\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, " \n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, ":\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, ":\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "/\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "/\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, " \n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, ":\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, ":\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + for(i=0;i\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); + + /* Now close ZIP file */ + VSIFCloseL(fpZIP); + + /* Reset updated flag at datasource and layer level */ + bUpdated = FALSE; + for(int i = 0; iSetUpdated(FALSE); + } + + return; +} + +/************************************************************************/ +/* EvaluateRange() */ +/************************************************************************/ + +int ODSCellEvaluator::EvaluateRange(int nRow1, int nCol1, int nRow2, int nCol2, + std::vector& aoOutValues) +{ + if (nRow1 < 0 || nRow1 >= poLayer->GetFeatureCount(FALSE) || + nCol1 < 0 || nCol1 >= poLayer->GetLayerDefn()->GetFieldCount()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid cell (row=%d, col=%d)", nRow1 + 1, nCol1 + 1); + return FALSE; + } + + if (nRow2 < 0 || nRow2 >= poLayer->GetFeatureCount(FALSE) || + nCol2 < 0 || nCol2 >= poLayer->GetLayerDefn()->GetFieldCount()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid cell (row=%d, col=%d)", nRow2 + 1, nCol2 + 1); + return FALSE; + } + + int nIndexBackup = poLayer->GetNextReadFID(); + + if (poLayer->SetNextByIndex(nRow1) != OGRERR_NONE) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot fetch feature for row = %d", nRow1); + return FALSE; + } + + for(int nRow = nRow1; nRow <= nRow2; nRow ++) + { + OGRFeature* poFeature = poLayer->GetNextFeatureWithoutFIDHack(); + + if (poFeature == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot fetch feature for for row = %d", nRow); + poLayer->SetNextByIndex(nIndexBackup); + return FALSE; + } + + for(int nCol = nCol1; nCol <= nCol2; nCol++) + { + if (!poFeature->IsFieldSet(nCol)) + { + aoOutValues.push_back(ods_formula_node()); + } + else if (poFeature->GetFieldDefnRef(nCol)->GetType() == OFTInteger) + { + aoOutValues.push_back(ods_formula_node(poFeature->GetFieldAsInteger(nCol))); + } + else if (poFeature->GetFieldDefnRef(nCol)->GetType() == OFTReal) + { + aoOutValues.push_back(ods_formula_node(poFeature->GetFieldAsDouble(nCol))); + } + else + { + std::string osVal(poFeature->GetFieldAsString(nCol)); + if (strncmp(osVal.c_str(), "of:=", 4) == 0) + { + delete poFeature; + poFeature = NULL; + + if (!Evaluate(nRow, nCol)) + { + /*CPLError(CE_Warning, CPLE_AppDefined, + "Formula at cell (%d, %d) has not yet been resolved", + nRow + 1, nCol + 1);*/ + poLayer->SetNextByIndex(nIndexBackup); + return FALSE; + } + + poLayer->SetNextByIndex(nRow); + poFeature = poLayer->GetNextFeatureWithoutFIDHack(); + + if (!poFeature->IsFieldSet(nCol)) + { + aoOutValues.push_back(ods_formula_node()); + } + else if (poFeature->GetFieldDefnRef(nCol)->GetType() == OFTInteger) + { + aoOutValues.push_back(ods_formula_node(poFeature->GetFieldAsInteger(nCol))); + } + else if (poFeature->GetFieldDefnRef(nCol)->GetType() == OFTReal) + { + aoOutValues.push_back(ods_formula_node(poFeature->GetFieldAsDouble(nCol))); + } + else + { + osVal = poFeature->GetFieldAsString(nCol); + if (strncmp(osVal.c_str(), "of:=", 4) != 0) + { + CPLValueType eType = CPLGetValueType(osVal.c_str()); + /* Try to convert into numeric value if possible */ + if (eType != CPL_VALUE_STRING) + aoOutValues.push_back(ods_formula_node(CPLAtofM(osVal.c_str()))); + else + aoOutValues.push_back(ods_formula_node(osVal.c_str())); + } + } + } + else + { + CPLValueType eType = CPLGetValueType(osVal.c_str()); + /* Try to convert into numeric value if possible */ + if (eType != CPL_VALUE_STRING) + aoOutValues.push_back(ods_formula_node(CPLAtofM(osVal.c_str()))); + else + aoOutValues.push_back(ods_formula_node(osVal.c_str())); + } + } + } + + delete poFeature; + } + + poLayer->SetNextByIndex(nIndexBackup); + + return TRUE; +} + +/************************************************************************/ +/* Evaluate() */ +/************************************************************************/ + +int ODSCellEvaluator::Evaluate(int nRow, int nCol) +{ + if (oVisisitedCells.find(std::pair(nRow, nCol)) != oVisisitedCells.end()) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Circular dependency with (row=%d, col=%d)", nRow + 1, nCol + 1); + return FALSE; + } + + oVisisitedCells.insert(std::pair(nRow, nCol)); + + if (poLayer->SetNextByIndex(nRow) != OGRERR_NONE) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot fetch feature for row = %d", nRow); + return FALSE; + } + + OGRFeature* poFeature = poLayer->GetNextFeatureWithoutFIDHack(); + if (poFeature->IsFieldSet(nCol) && + poFeature->GetFieldDefnRef(nCol)->GetType() == OFTString) + { + const char* pszVal = poFeature->GetFieldAsString(nCol); + if (strncmp(pszVal, "of:=", 4) == 0) + { + ods_formula_node* expr_out = ods_formula_compile( pszVal + 4 ); + if (expr_out && + expr_out->Evaluate(this) && + expr_out->eNodeType == SNT_CONSTANT) + { + /* Refetch feature in case Evaluate() modified another cell in this row */ + delete poFeature; + poLayer->SetNextByIndex(nRow); + poFeature = poLayer->GetNextFeatureWithoutFIDHack(); + + if (expr_out->field_type == ODS_FIELD_TYPE_EMPTY) + { + poFeature->UnsetField(nCol); + poLayer->SetFeatureWithoutFIDHack(poFeature); + } + else if (expr_out->field_type == ODS_FIELD_TYPE_INTEGER) + { + poFeature->SetField(nCol, expr_out->int_value); + poLayer->SetFeatureWithoutFIDHack(poFeature); + } + else if (expr_out->field_type == ODS_FIELD_TYPE_FLOAT) + { + poFeature->SetField(nCol, expr_out->float_value); + poLayer->SetFeatureWithoutFIDHack(poFeature); + } + else if (expr_out->field_type == ODS_FIELD_TYPE_STRING) + { + poFeature->SetField(nCol, expr_out->string_value); + poLayer->SetFeatureWithoutFIDHack(poFeature); + } + } + delete expr_out; + } + } + + delete poFeature; + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ogrodsdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ogrodsdriver.cpp new file mode 100644 index 000000000..3cd887040 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/ogrodsdriver.cpp @@ -0,0 +1,230 @@ +/****************************************************************************** + * $Id: ogrodsdriver.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: ODS Translator + * Purpose: Implements OGRODSDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_ods.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrodsdriver.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +extern "C" void RegisterOGRODS(); + +// g++ -DHAVE_EXPAT -g -Wall -fPIC ogr/ogrsf_frmts/ods/*.cpp -shared -o ogr_ODS.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/mem -Iogr/ogrsf_frmts/ods -L. -lgdal + +/************************************************************************/ +/* ~OGRODSDriver() */ +/************************************************************************/ + +OGRODSDriver::~OGRODSDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRODSDriver::GetName() + +{ + return "ODS"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRODSDriver::Open( const char * pszFilename, int bUpdate ) + +{ + CPLString osContentFilename; + const char* pszContentFilename = pszFilename; + + VSILFILE* fpContent = NULL; + VSILFILE* fpSettings = NULL; + + if (EQUAL(CPLGetExtension(pszFilename), "ODS")) + { + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return NULL; + + int bOK = FALSE; + char szBuffer[1024]; + if (VSIFReadL(szBuffer, sizeof(szBuffer), 1, fp) == 1 && + memcmp(szBuffer, "PK", 2) == 0) + { + bOK = TRUE; + } + + VSIFCloseL(fp); + + if (!bOK) + return NULL; + + osContentFilename.Printf("/vsizip/%s/content.xml", pszFilename); + pszContentFilename = osContentFilename.c_str(); + } + else if (bUpdate) /* We cannot update the xml file, only the .ods */ + { + return NULL; + } + + if (EQUALN(pszContentFilename, "ODS:", 4) || + EQUAL(CPLGetFilename(pszContentFilename), "content.xml")) + { + if (EQUALN(pszContentFilename, "ODS:", 4)) + pszContentFilename += 4; + + fpContent = VSIFOpenL(pszContentFilename, "rb"); + if (fpContent == NULL) + return NULL; + + char szBuffer[1024]; + int nRead = (int)VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fpContent); + szBuffer[nRead] = 0; + + if (strstr(szBuffer, ", but it might be further */ + /* in the XML due to styles, etc... */ + } + else + { + return NULL; + } + + if (EQUAL(CPLGetExtension(pszFilename), "ODS")) + { + fpSettings = VSIFOpenL(CPLSPrintf("/vsizip/%s/settings.xml", pszFilename), "rb"); + } + + OGRODSDataSource *poDS = new OGRODSDataSource(); + + if( !poDS->Open( pszFilename, fpContent, fpSettings, bUpdate ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRODSDriver::CreateDataSource( const char * pszName, + char **papszOptions ) + +{ + if (!EQUAL(CPLGetExtension(pszName), "ODS")) + { + CPLError( CE_Failure, CPLE_AppDefined, "File extension should be ODS" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* First, ensure there isn't any such file yet. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + + if( VSIStatL( pszName, &sStatBuf ) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "It seems a file system object called '%s' already exists.", + pszName ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to create datasource. */ +/* -------------------------------------------------------------------- */ + OGRODSDataSource *poDS; + + poDS = new OGRODSDataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* DeleteDataSource() */ +/************************************************************************/ + +OGRErr OGRODSDriver::DeleteDataSource( const char *pszName ) +{ + if (VSIUnlink( pszName ) == 0) + return OGRERR_NONE; + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRODSDriver::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODrCCreateDataSource) ) + return TRUE; + else if( EQUAL(pszCap,ODrCDeleteDataSource) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRODS() */ +/************************************************************************/ + +void RegisterOGRODS() + +{ + OGRSFDriver* poDriver = new OGRODSDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Open Document/ LibreOffice / OpenOffice Spreadsheet " ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "ods" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_ods.html" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime Time Binary" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/testparser.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/testparser.cpp new file mode 100644 index 000000000..ae91a37e6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ods/testparser.cpp @@ -0,0 +1,59 @@ +/****************************************************************************** + * $Id: testparser.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Component: Test ODS formula Engine + * Purpose: + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ods_formula.h" + +int main(int argc, char* argv[]) +{ + if (argc != 2) + { + printf("Usage: testparser \"expression\"\n"); + return 1; + } + + ods_formula_node* expr_out = ods_formula_compile( argv[1] ); + if (expr_out) + { + printf("Raw expression dump :\n"); + expr_out->Dump(stderr, 0); + if (expr_out->Evaluate(NULL)) + { + printf("After evaluation :\n"); + expr_out->Dump(stderr, 0); + } + else + { + printf("Error during evaluation\n"); + } + } + else + printf("Invalid expression\n"); + delete expr_out; + return 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/GNUmakefile new file mode 100644 index 000000000..209bfa754 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrogdidriver.o ogrogdidatasource.o ogrogdilayer.o + +CPPFLAGS := $(OGDI_INCLUDE) $(PROJ_INCLUDE) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +$(O_OBJ): ogrogdi.h + +clean: + rm -f *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/drv_ogdi.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/drv_ogdi.html new file mode 100644 index 000000000..77e05d434 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/drv_ogdi.html @@ -0,0 +1,96 @@ + + +OGDI Vectors + + + + +

    OGDI Vectors

    + +OGDI vector support is optional in OGR, and is normally only configured if +OGDI is installed on the build system. If available OGDI vectors are +supported for read access for the following family types: +
      +
    • Point +
    • Line +
    • Area +
    • Text (Currently returned as points with the text in the "text" attribute) +
    +

    + +OGDI can (among other formats) read VPF products, such as DCW and VMAP.

    + +If an OGDI gltp url is opened directly the OGDI 3.1 capabilities for the +driver/server are queried to get a list of layers. One OGR layer is created +for each OGDI family available for each layer in the datastore. For drivers +such as VRF this can result in alot of layers. Each of the layers has an +OGR name based on the OGDI name plus an underscore and the family name. For +instance a layer might be called watrcrsl@hydro(*)_line if coming +out of the VRF driver.

    + +From GDAL/OGR 1.8.0, setting the OGR_OGDI_LAUNDER_LAYER_NAMES configuration +option (or environment variable) to YES causes the layer names to be simplified. For example : +watrcrsl_hydro instead of 'watrcrsl@hydro(*)_line'

    + +Alternatively to accessing all the layers in a datastore, it is possible to +open a particular layer using a customized filename consisting +of the regular GLTP URL to +which you append the layer name and family type (separated by colons). This +mechanism must be used to access layers of pre OGDI 3.1 drivers as before +OGDI 3.1 there was no regular way to discover available layers in OGDI.

    + +

    +   gltp:[//<hostname>]/<driver_name>/<dataset_name>:<layer_name>:<family>
    +
    + +Where <layer_name> is the OGDI Layer name, and <family> is one of: +"line", "area", "point", or "text".

    + +OGDI coordinate system information is supported for most coordinate systems. +A warning will be produced when a layer is opened if the coordinate +system cannot be translated.

    + +There is no update or creation support in the OGDI driver.

    + +Raster layers cannot be accessed with this driver but can be accessed +using the GDAL OGDI Raster driver.

    + +

    Examples

    + +Usage example 'ogrinfo':
    + +
    +   ogrinfo gltp:/vrf/usr4/mpp1/v0eur/vmaplv0/eurnasia 'watrcrsl@hydro(*)_line'
    +
    +

    +In the dataset name 'gltp:/vrf/usr4/mpp1/v0eur/vmaplv0/eurnasia' the gltp:/vrf +part is not really in the filesystem, but has to be added. The VPF data was at +/usr4/mpp1/v0eur/. The 'eurnasia' directory should be at the same level as +the dht. and lat. files. The 'hydro' reference is a subdirectory of 'eurnasia/' +where watrcrsl.* is found. +

    + +Usage examples VMAP0 to SHAPE conversion with 'ogr2ogr':
    + +

    +   ogr2ogr watrcrsl.shp gltp:/vrf/usr4/mpp1/v0eur/vmaplv0/eurnasia 'watrcrsl@hydro(*)_line'
    +   ogr2ogr polbnda.shp  gltp:/vrf/usr4/mpp1/v0eur/vmaplv0/eurnasia 'polbnda@bnd(*)_area'
    +
    + +An OGR SQL query against a VMAP dataset. Again, note the careful quoting of +the layer name.

    + +

    +   ogrinfo -ro gltp:/vrf/usr4/mpp1/v0noa/vmaplv0/noamer \
    +           -sql 'select * from "polbndl@bnd(*)_line" where use=26'
    +
    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/makefile.vc new file mode 100644 index 000000000..3d67a3a12 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/makefile.vc @@ -0,0 +1,18 @@ + +OBJ = ogrogdidriver.obj ogrogdidatasource.obj ogrogdilayer.obj + +EXTRAFLAGS = -I.. -I..\.. \ + -I$(OGDI_INCLUDE) -I$(OGDIDIR)/include/win32 \ + -I$(OGDIDIR)/proj -DWIN32 -D_WINDOWS + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/ogrogdi.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/ogrogdi.h new file mode 100644 index 000000000..40597bece --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/ogrogdi.h @@ -0,0 +1,148 @@ +/****************************************************************************** + * $Id: ogrogdi.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OGDI Bridge + * Purpose: Private definitions within the OGDI driver to implement + * integration with OGR. + * Author: Daniel Morissette, danmo@videotron.ca + * (Based on some code contributed by Frank Warmerdam :) + * + ****************************************************************************** + * Copyright (c) 2000, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGDOGDI_H_INCLUDED +#define _OGDOGDI_H_INCLUDED + +#include +extern "C" { +#include "ecs.h" +} +#include "ogrsf_frmts.h" + + +/************************************************************************/ +/* OGROGDILayer */ +/************************************************************************/ +class OGROGDIDataSource; + +class OGROGDILayer : public OGRLayer +{ + OGROGDIDataSource *m_poODS; + int m_nClientID; + char *m_pszOGDILayerName; + ecs_Family m_eFamily; + + OGRFeatureDefn *m_poFeatureDefn; + OGRSpatialReference *m_poSpatialRef; + ecs_Region m_sFilterBounds; + + int m_iNextShapeId; + int m_nTotalShapeCount; + int m_nFilteredOutShapes; + + OGRFeature * GetNextRawFeature(); + + public: + OGROGDILayer(OGROGDIDataSource *, const char *, + ecs_Family); + ~OGROGDILayer(); + + virtual void SetSpatialFilter( OGRGeometry * ); + virtual OGRErr SetAttributeFilter( const char *pszQuery ); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRFeature *GetFeature( GIntBig nFeatureId ); + + OGRFeatureDefn * GetLayerDefn() { return m_poFeatureDefn; } + + GIntBig GetFeatureCount( int ); + + int TestCapability( const char * ); + + private: + void BuildFeatureDefn(); +}; + +/************************************************************************/ +/* OGROGDIDataSource */ +/************************************************************************/ + +class OGROGDIDataSource : public OGRDataSource +{ + OGROGDILayer **m_papoLayers; + int m_nLayers; + + int m_nClientID; + + ecs_Region m_sGlobalBounds; + OGRSpatialReference *m_poSpatialRef; + + OGROGDILayer *m_poCurrentLayer; + + char *m_pszFullName; + + int m_bLaunderLayerNames; + + void IAddLayer( const char *pszLayerName, + ecs_Family eFamily ); + + public: + OGROGDIDataSource(); + ~OGROGDIDataSource(); + + int Open( const char *, int bTestOpen ); + + const char *GetName() { return m_pszFullName; } + int GetLayerCount() { return m_nLayers; } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); + + ecs_Region *GetGlobalBounds() { return &m_sGlobalBounds; } + OGRSpatialReference*GetSpatialRef() { return m_poSpatialRef; } + int GetClientID() { return m_nClientID; } + + OGROGDILayer *GetCurrentLayer() { return m_poCurrentLayer; } + void SetCurrentLayer(OGROGDILayer* poLayer) { m_poCurrentLayer = poLayer ; } + + int LaunderLayerNames() { return m_bLaunderLayerNames; } +}; + +/************************************************************************/ +/* OGROGDIDriver */ +/************************************************************************/ + +class OGROGDIDriver : public OGRSFDriver +{ + public: + ~OGROGDIDriver(); + + const char *GetName(); + OGRDataSource *Open( const char *, int ); + + int TestCapability( const char * ); +}; + + +#endif /* _OGDOGDI_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/ogrogdidatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/ogrogdidatasource.cpp new file mode 100644 index 000000000..e42041d44 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/ogrogdidatasource.cpp @@ -0,0 +1,287 @@ +/****************************************************************************** + * $Id: ogrogdidatasource.cpp 27794 2014-10-04 10:13:46Z rouault $ + * + * Project: OGDI Bridge + * Purpose: Implements OGROGDIDataSource class. + * Author: Daniel Morissette, danmo@videotron.ca + * (Based on some code contributed by Frank Warmerdam :) + * + ****************************************************************************** + * Copyright (c) 2000, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrogdi.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrogdidatasource.cpp 27794 2014-10-04 10:13:46Z rouault $"); + +/************************************************************************/ +/* OGROGDIDataSource() */ +/************************************************************************/ + +OGROGDIDataSource::OGROGDIDataSource() + +{ + m_pszFullName = NULL; + m_papoLayers = NULL; + m_nLayers = 0; + m_nClientID = -1; + m_poSpatialRef = NULL; + m_poCurrentLayer = NULL; + m_bLaunderLayerNames = + CSLTestBoolean(CPLGetConfigOption("OGR_OGDI_LAUNDER_LAYER_NAMES", "NO")); +} + +/************************************************************************/ +/* ~OGROGDIDataSource() */ +/************************************************************************/ + +OGROGDIDataSource::~OGROGDIDataSource() + +{ + CPLFree(m_pszFullName ); + + for( int i = 0; i < m_nLayers; i++ ) + delete m_papoLayers[i]; + CPLFree( m_papoLayers ); + + if (m_nClientID != -1) + { + ecs_Result *psResult; + + psResult = cln_DestroyClient( m_nClientID ); + ecs_CleanUp( psResult ); + } + + if (m_poSpatialRef) + m_poSpatialRef->Release(); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGROGDIDataSource::Open( const char * pszNewName, int bTestOpen ) + +{ + ecs_Result *psResult; + char *pszFamily=NULL, *pszLyrName=NULL; + char *pszWorkingName; + + CPLAssert( m_nLayers == 0 ); + +/* -------------------------------------------------------------------- */ +/* Parse the dataset name. */ +/* i.e. */ +/* gltp:////[::] */ +/* */ +/* Where is one of: Line, Area, Point, and Text */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(pszNewName,"gltp:",5) ) + return FALSE; + + pszWorkingName = CPLStrdup( pszNewName ); + + pszFamily = strrchr(pszWorkingName, ':'); + + // Don't treat drive name colon as family separator. It is assumed + // that drive names are on character long, and preceeded by a + // forward or backward slash. + if( pszFamily < pszWorkingName+2 + || pszFamily[-2] == '/' + || pszFamily[-2] == '\\' ) + pszFamily = NULL; + + if (pszFamily && pszFamily != pszWorkingName + 4) + { + *pszFamily = '\0'; + pszFamily++; + + pszLyrName = strrchr(pszWorkingName, ':'); + if (pszLyrName == pszWorkingName + 4) + pszLyrName = NULL; + + if( pszLyrName != NULL ) + { + *pszLyrName = '\0'; + pszLyrName++; + } + } + +/* -------------------------------------------------------------------- */ +/* Open the client interface. */ +/* -------------------------------------------------------------------- */ + psResult = cln_CreateClient(&m_nClientID, pszWorkingName); + CPLFree( pszWorkingName ); + + if( ECSERROR( psResult ) ) + { + if (!bTestOpen) + { + CPLError( CE_Failure, CPLE_AppDefined, + "OGDI DataSource Open Failed: %s\n", + psResult->message ); + } + return FALSE; + } + + m_pszFullName = CPLStrdup(pszNewName); + +/* -------------------------------------------------------------------- */ +/* Capture some information from the file. */ +/* -------------------------------------------------------------------- */ + psResult = cln_GetGlobalBound( m_nClientID ); + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + return FALSE; + } + + m_sGlobalBounds = ECSREGION(psResult); + + psResult = cln_GetServerProjection(m_nClientID); + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + return FALSE; + } + + m_poSpatialRef = new OGRSpatialReference; + + if( m_poSpatialRef->importFromProj4( ECSTEXT(psResult) ) != OGRERR_NONE ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "untranslatable PROJ.4 projection: %s\n", + ECSTEXT(psResult) ); + delete m_poSpatialRef; + m_poSpatialRef = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Select the global region. */ +/* -------------------------------------------------------------------- */ + psResult = cln_SelectRegion( m_nClientID, &m_sGlobalBounds ); + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If an explicit layer was selected, just create that layer. */ +/* -------------------------------------------------------------------- */ + m_poCurrentLayer = NULL; + + if( pszLyrName != NULL ) + { + ecs_Family eFamily; + + if (EQUAL(pszFamily, "Line")) + eFamily = Line; + else if (EQUAL(pszFamily, "Area")) + eFamily = Area; + else if (EQUAL(pszFamily, "Point")) + eFamily = Point; + else if (EQUAL(pszFamily, "Text")) + eFamily = Text; + else + { + if (!bTestOpen) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid or unsupported family name (%s) in URL %s\n", + pszFamily, m_pszFullName); + } + return FALSE; + } + + IAddLayer( pszLyrName, eFamily ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise create a layer for every layer in the capabilities. */ +/* -------------------------------------------------------------------- */ + else + { + int i; + const ecs_LayerCapabilities *psLayerCap; + + for( i = 0; + (psLayerCap = cln_GetLayerCapabilities(m_nClientID,i)) != NULL; + i++ ) + { + if( psLayerCap->families[Point] ) + IAddLayer( psLayerCap->name, Point ); + if( psLayerCap->families[Line] ) + IAddLayer( psLayerCap->name, Line ); + if( psLayerCap->families[Area] ) + IAddLayer( psLayerCap->name, Area ); + if( psLayerCap->families[Text] ) + IAddLayer( psLayerCap->name, Text ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* IAddLayer() */ +/* */ +/* Internal helper function for adding one existing layer to */ +/* the datasource. */ +/************************************************************************/ + +void OGROGDIDataSource::IAddLayer( const char *pszLayerName, + ecs_Family eFamily ) + +{ + m_papoLayers = (OGROGDILayer**) + CPLRealloc( m_papoLayers, (m_nLayers+1) * sizeof(OGROGDILayer*)); + + m_papoLayers[m_nLayers++] = new OGROGDILayer(this, pszLayerName, eFamily); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGROGDIDataSource::TestCapability( CPL_UNUSED const char * pszCap ) + +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGROGDIDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= m_nLayers ) + return NULL; + else + return m_papoLayers[iLayer]; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/ogrogdidriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/ogrogdidriver.cpp new file mode 100644 index 000000000..343ccc370 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/ogrogdidriver.cpp @@ -0,0 +1,114 @@ +/****************************************************************************** + * $Id: ogrogdidriver.cpp 27794 2014-10-04 10:13:46Z rouault $ + * + * Project: OGDI Bridge + * Purpose: Implements OGROGDIDriver class. + * Author: Daniel Morissette, danmo@videotron.ca + * (Based on some code contributed by Frank Warmerdam :) + * + ****************************************************************************** + * Copyright (c) 2000, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrogdi.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrogdidriver.cpp 27794 2014-10-04 10:13:46Z rouault $"); + +/************************************************************************/ +/* ~OGROGDIDriver() */ +/************************************************************************/ + +OGROGDIDriver::~OGROGDIDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGROGDIDriver::GetName() + +{ + return "OGR_OGDI"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGROGDIDriver::Open( const char * pszFilename, + int bUpdate ) + +{ + OGROGDIDataSource *poDS; + + if( !EQUALN(pszFilename,"gltp:",5) ) + return FALSE; + + poDS = new OGROGDIDataSource(); + + if( !poDS->Open( pszFilename, TRUE ) ) + { + delete poDS; + poDS = NULL; + } + + if ( poDS != NULL && bUpdate ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "OGDI Driver doesn't support update." ); + delete poDS; + poDS = NULL; + } + + return poDS; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGROGDIDriver::TestCapability( CPL_UNUSED const char * pszCap ) + +{ + return FALSE; +} + +/************************************************************************/ +/* RegisterOGROGDI() */ +/************************************************************************/ + +void RegisterOGROGDI() + +{ + if (! GDAL_CHECK_VERSION("OGR/OGDI driver")) + return; + OGRSFDriver* poDriver = new OGROGDIDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "OGDI Vectors (VPF, VMAP, DCW)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_ogdi.html" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/ogrogdilayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/ogrogdilayer.cpp new file mode 100644 index 000000000..adad5f7f6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogdi/ogrogdilayer.cpp @@ -0,0 +1,643 @@ +/****************************************************************************** + * $Id: ogrogdilayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OGDI Bridge + * Purpose: Implements OGROGDILayer class. + * Author: Daniel Morissette, danmo@videotron.ca + * (Based on some code contributed by Frank Warmerdam :) + * + ****************************************************************************** + * Copyright (c) 2000, Daniel Morissette + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * http://bugzilla.remotesensing.org/show_bug.cgi?id=372 + * + * Revision 1.6 2003/05/21 03:58:49 warmerda + * expand tabs + * + * Revision 1.5 2001/07/18 04:55:16 warmerda + * added CPL_CSVID + * + * Revision 1.4 2001/06/19 15:50:23 warmerda + * added feature attribute query support + * + * Revision 1.3 2001/04/17 21:41:02 warmerda + * Added use of cln_GetLayerCapabilities() to query list of available layers. + * Restructured OGROGDIDataSource and OGROGDILayer classes somewhat to + * avoid passing so much information in the layer creation call. Added support + * for preserving text on OGDI text features. + * + * Revision 1.2 2000/08/30 01:36:57 danmo + * Added GetSpatialRef() support + * + * Revision 1.1 2000/08/24 04:16:19 danmo + * Initial revision + * + */ + +#include "ogrogdi.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrogdilayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGROGDILayer() */ +/************************************************************************/ + +OGROGDILayer::OGROGDILayer( OGROGDIDataSource *poODS, + const char * pszName, ecs_Family eFamily ) + +{ + m_poODS = poODS; + m_nClientID = m_poODS->GetClientID(); + m_eFamily = eFamily; + + m_pszOGDILayerName = CPLStrdup(pszName); + + m_sFilterBounds = *(m_poODS->GetGlobalBounds()); + + m_iNextShapeId = 0; + m_nTotalShapeCount = -1; + m_poFeatureDefn = NULL; + + // Keep a reference on the SpatialRef (owned by the dataset). + m_poSpatialRef = m_poODS->GetSpatialRef(); + + // Select layer and feature family. + ResetReading(); + + BuildFeatureDefn(); +} + +/************************************************************************/ +/* ~OGROGDILayer() */ +/************************************************************************/ + +OGROGDILayer::~OGROGDILayer() + +{ + if( m_nFeaturesRead > 0 && m_poFeatureDefn != NULL ) + { + CPLDebug( "OGDI", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + m_poFeatureDefn->GetName() ); + } + + if (m_poFeatureDefn) + m_poFeatureDefn->Release(); + + CPLFree(m_pszOGDILayerName); + + // Note: we do not delete m_poSpatialRef since it is owned by the dataset +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGROGDILayer::SetSpatialFilter( OGRGeometry * poGeomIn ) + +{ + if( !InstallFilter( poGeomIn ) ) + return; + + ResetReading(); + + m_nTotalShapeCount = -1; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGROGDILayer::SetAttributeFilter( const char *pszQuery ) +{ + OGRErr eErr = OGRLayer::SetAttributeFilter(pszQuery); + + ResetReading(); + + m_nTotalShapeCount = -1; + + return eErr; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGROGDILayer::ResetReading() + +{ + ecs_Result *psResult; + ecs_LayerSelection sSelectionLayer; + + sSelectionLayer.Select = m_pszOGDILayerName; + sSelectionLayer.F = m_eFamily; + + psResult = cln_SelectLayer(m_nClientID, &sSelectionLayer); + if( ECSERROR( psResult ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Access to layer '%s' Failed: %s\n", + m_pszOGDILayerName, psResult->message ); + return; + } + + /* Reset spatial filter */ + if( m_poFilterGeom != NULL ) + { + OGREnvelope oEnv; + + m_poFilterGeom->getEnvelope(&oEnv); + + m_sFilterBounds.north = oEnv.MaxY; + m_sFilterBounds.south = oEnv.MinY; + m_sFilterBounds.east = oEnv.MinX; + m_sFilterBounds.west = oEnv.MaxX; + + psResult = cln_SelectRegion( m_nClientID, &m_sFilterBounds); + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + return; + } + } + else + { + /* Reset to global bounds */ + psResult = cln_SelectRegion( m_nClientID, m_poODS->GetGlobalBounds() ); + if( ECSERROR(psResult) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", psResult->message ); + return; + } + } + + m_iNextShapeId = 0; + m_nFilteredOutShapes = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGROGDILayer::GetNextFeature() + +{ + OGRFeature *poFeature; + + /* Reset reading if we are not the current layer */ + /* WARNING : this does not allow interleaved reading of layers */ + if( m_poODS->GetCurrentLayer() != this ) + { + m_poODS->SetCurrentLayer(this); + ResetReading(); + } + + while( TRUE ) + { + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + /* -------------------------------------------------------------------- */ + /* Do we need to apply an attribute test? */ + /* -------------------------------------------------------------------- */ + if( (m_poAttrQuery != NULL + && !m_poAttrQuery->Evaluate( poFeature ) ) + || (m_poFilterGeom != NULL + && !FilterGeometry( poFeature->GetGeometryRef() ) ) ) + { + m_nFilteredOutShapes ++; + delete poFeature; + } + else + return poFeature; + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGROGDILayer::GetNextRawFeature() +{ + ecs_Result *psResult; + int i; + OGRFeature *poFeature; + +/* -------------------------------------------------------------------- */ +/* Retrieve object from OGDI server and create new feature */ +/* -------------------------------------------------------------------- */ + + psResult = cln_GetNextObject(m_nClientID); + if (! ECSSUCCESS(psResult)) + { + // We probably reached EOF... keep track of shape count. + m_nTotalShapeCount = m_iNextShapeId - m_nFilteredOutShapes; + return NULL; + } + + poFeature = new OGRFeature(m_poFeatureDefn); + + poFeature->SetFID( m_iNextShapeId++ ); + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Process geometry */ +/* -------------------------------------------------------------------- */ + if (m_eFamily == Point) + { + ecs_Point *psPoint = &(ECSGEOM(psResult).point); + OGRPoint *poOGRPoint = new OGRPoint(psPoint->c.x, psPoint->c.y); + + poOGRPoint->assignSpatialReference(m_poSpatialRef); + poFeature->SetGeometryDirectly(poOGRPoint); + } + else if (m_eFamily == Line) + { + ecs_Line *psLine = &(ECSGEOM(psResult).line); + OGRLineString *poOGRLine = new OGRLineString(); + + poOGRLine->setNumPoints( psLine->c.c_len ); + + for( i=0; i < (int) psLine->c.c_len; i++ ) + { + poOGRLine->setPoint(i, psLine->c.c_val[i].x, psLine->c.c_val[i].y); + } + + poOGRLine->assignSpatialReference(m_poSpatialRef); + poFeature->SetGeometryDirectly(poOGRLine); + } + else if (m_eFamily == Area) + { + ecs_Area *psArea = &(ECSGEOM(psResult).area); + OGRPolygon *poOGRPolygon = new OGRPolygon(); + + for(int iRing=0; iRing < (int) psArea->ring.ring_len; iRing++) + { + ecs_FeatureRing *psRing = &(psArea->ring.ring_val[iRing]); + OGRLinearRing *poOGRRing = new OGRLinearRing(); + + poOGRRing->setNumPoints( psRing->c.c_len ); + + for( i=0; i < (int) psRing->c.c_len; i++ ) + { + poOGRRing->setPoint(i, psRing->c.c_val[i].x, + psRing->c.c_val[i].y); + } + poOGRPolygon->addRingDirectly(poOGRRing); + } + + // __TODO__ + // When OGR supports polygon centroids then we should carry them here + + poOGRPolygon->assignSpatialReference(m_poSpatialRef); + poFeature->SetGeometryDirectly(poOGRPolygon); + } + else if (m_eFamily == Text) + { + // __TODO__ + // For now text is treated as a point and string is lost + // + ecs_Text *psText = &(ECSGEOM(psResult).text); + OGRPoint *poOGRPoint = new OGRPoint(psText->c.x, psText->c.y); + + poOGRPoint->assignSpatialReference(m_poSpatialRef); + poFeature->SetGeometryDirectly(poOGRPoint); + } + else + { + CPLAssert(FALSE); + } + +/* -------------------------------------------------------------------- */ +/* Set attributes */ +/* -------------------------------------------------------------------- */ + char *pszAttrList = ECSOBJECTATTR(psResult); + + for( int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++ ) + { + char *pszFieldStart; + int nNameLen; + char chSavedChar; + + /* parse out the next attribute value */ + if( !ecs_FindElement( pszAttrList, &pszFieldStart, &pszAttrList, + &nNameLen, NULL ) ) + { + nNameLen = 0; + pszFieldStart = pszAttrList; + } + + /* Skip any trailing white space (for string constants). */ + + if( nNameLen > 0 && pszFieldStart[nNameLen-1] == ' ' ) + nNameLen--; + + /* skip leading white space */ + while( pszFieldStart[0] == ' ' && nNameLen > 0 ) + { + pszFieldStart++; + nNameLen--; + } + + /* zero terminate the single field value, but save the */ + /* character we overwrote, so we can restore it when done. */ + + chSavedChar = pszFieldStart[nNameLen]; + pszFieldStart[nNameLen] = '\0'; + + /* OGR takes care of all field type conversions for us! */ + + poFeature->SetField(iField, pszFieldStart); + + pszFieldStart[nNameLen] = chSavedChar; + } + +/* -------------------------------------------------------------------- */ +/* Apply the text associated with text features if appropriate. */ +/* -------------------------------------------------------------------- */ + if( m_eFamily == Text ) + { + poFeature->SetField( "text", ECSGEOM(psResult).text.desc ); + } + + return poFeature; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGROGDILayer::GetFeature( GIntBig nFeatureId ) + +{ + ecs_Result *psResult; + + if (m_nTotalShapeCount != -1 && nFeatureId > m_nTotalShapeCount) + return NULL; + + /* Unset spatial filter */ + OGRGeometry* poOldFilterGeom = ( m_poFilterGeom != NULL ) ? m_poFilterGeom->clone() : NULL; + if( poOldFilterGeom != NULL ) + SetSpatialFilter(NULL); + + /* Reset reading if we are not the current layer */ + /* WARNING : this does not allow interleaved reading of layers */ + if( m_poODS->GetCurrentLayer() != this ) + { + m_poODS->SetCurrentLayer(this); + ResetReading(); + } + else if ( nFeatureId < m_iNextShapeId ) + ResetReading(); + + while(m_iNextShapeId != nFeatureId) + { + psResult = cln_GetNextObject(m_nClientID); + if (ECSSUCCESS(psResult)) + m_iNextShapeId++; + else + { + // We probably reached EOF... keep track of shape count. + m_nTotalShapeCount = m_iNextShapeId; + if( poOldFilterGeom != NULL ) + { + SetSpatialFilter(poOldFilterGeom); + delete poOldFilterGeom; + } + return NULL; + } + } + + // OK, we're ready to read the requested feature... + OGRFeature* poFeature = GetNextRawFeature(); + if( poOldFilterGeom != NULL ) + { + SetSpatialFilter(poOldFilterGeom); + delete poOldFilterGeom; + } + return poFeature; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGROGDILayer::GetFeatureCount( int bForce ) + +{ + if( m_nTotalShapeCount == -1) + { + m_nTotalShapeCount = OGRLayer::GetFeatureCount( bForce ); + } + + return m_nTotalShapeCount; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGROGDILayer::TestCapability( const char * pszCap ) + +{ +/* -------------------------------------------------------------------- */ +/* Hummm... what are the proper capabilities... */ +/* Does OGDI have any idea of capabilities??? */ +/* For now just return FALSE for everything. */ +/* -------------------------------------------------------------------- */ +#ifdef __TODO__ + if( EQUAL(pszCap,OLCFastFeatureCount) ) + return m_poFilterGeom == NULL && m_poAttrQuery == NULL; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else + return FALSE; +#endif + + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else + return FALSE; +} + + + +/************************************************************************/ +/* BuildFeatureDefn() */ +/* */ +/* (private) Initializes the schema in m_poFeatureDefn */ +/************************************************************************/ + +void OGROGDILayer::BuildFeatureDefn() +{ + ecs_Result *psResult; + ecs_ObjAttributeFormat *oaf; + int i, numFields; + const char *pszGeomName; + OGRwkbGeometryType eLayerGeomType; + +/* -------------------------------------------------------------------- */ +/* Feature Defn name will be "_" */ +/* -------------------------------------------------------------------- */ + + switch(m_eFamily) + { + case Point: + pszGeomName = "point"; + eLayerGeomType = wkbPoint; + break; + case Line: + pszGeomName = "line"; + eLayerGeomType = wkbLineString; + break; + case Area: + pszGeomName = "area"; + eLayerGeomType = wkbPolygon; + break; + case Text: + pszGeomName = "text"; + eLayerGeomType = wkbPoint; + break; + default: + pszGeomName = "unknown"; + eLayerGeomType = wkbUnknown; + break; + } + + char* pszFeatureDefnName; + if (m_poODS->LaunderLayerNames()) + { + pszFeatureDefnName = CPLStrdup(m_pszOGDILayerName); + char* pszAt = strchr(pszFeatureDefnName, '@'); + if (pszAt) + *pszAt = '_'; + char* pszLeftParenthesis = strchr(pszFeatureDefnName, '('); + if (pszLeftParenthesis) + *pszLeftParenthesis = '\0'; + } + else + pszFeatureDefnName = CPLStrdup(CPLSPrintf("%s_%s", + m_pszOGDILayerName, + pszGeomName )); + + m_poFeatureDefn = new OGRFeatureDefn(pszFeatureDefnName); + SetDescription( m_poFeatureDefn->GetName() ); + CPLFree(pszFeatureDefnName); + pszFeatureDefnName = NULL; + + m_poFeatureDefn->SetGeomType(eLayerGeomType); + m_poFeatureDefn->Reference(); + m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSpatialRef); + +/* -------------------------------------------------------------------- */ +/* Fetch schema from OGDI server and map to OGR types */ +/* -------------------------------------------------------------------- */ + psResult = cln_GetAttributesFormat( m_nClientID ); + if( ECSERROR( psResult ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "ECSERROR: %s\n", psResult->message); + return; + } + + oaf = &(ECSRESULT(psResult).oaf); + numFields = oaf->oa.oa_len; + for( i = 0; i < numFields; i++ ) + { + OGRFieldDefn oField("", OFTInteger); + + oField.SetName( oaf->oa.oa_val[i].name ); + oField.SetPrecision( 0 ); + + switch( oaf->oa.oa_val[i].type ) + { + case Decimal: + case Smallint: + case Integer: + oField.SetType( OFTInteger ); + if( oaf->oa.oa_val[i].lenght > 0 ) + oField.SetWidth( oaf->oa.oa_val[i].lenght ); + else + oField.SetWidth( 11 ); + break; + + case Numeric: + case Real: + case Float: + case Double: + oField.SetType( OFTReal ); + if( oaf->oa.oa_val[i].lenght > 0 ) + { + oField.SetWidth( oaf->oa.oa_val[i].lenght ); + oField.SetPrecision( oaf->oa.oa_val[i].precision ); + } + else + { + oField.SetWidth( 18 ); + oField.SetPrecision( 7 ); + } + break; + + case Char: + case Varchar: + case Longvarchar: + default: + oField.SetType( OFTString ); + if( oaf->oa.oa_val[i].lenght > 0 ) + oField.SetWidth( oaf->oa.oa_val[i].lenght ); + else + oField.SetWidth( 64 ); + break; + + } + + m_poFeatureDefn->AddFieldDefn( &oField ); + } + +/* -------------------------------------------------------------------- */ +/* Add a text attribute for text objects. */ +/* -------------------------------------------------------------------- */ + if( m_eFamily == Text ) + { + OGRFieldDefn oField("text", OFTString); + + m_poFeatureDefn->AddFieldDefn( &oField ); + } + +} + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogr_attrind.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogr_attrind.h new file mode 100644 index 000000000..b2d8f2d1c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogr_attrind.h @@ -0,0 +1,93 @@ +/****************************************************************************** + * $Id: ogr_attrind.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Classes related to generic implementation of attribute indexing. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_ATTRIND_H_INCLUDED +#define _OGR_ATTRIND_H_INCLUDED + +#include "ogrsf_frmts.h" + +/************************************************************************/ +/* OGRAttrIndex */ +/* */ +/* Base class for accessing the indexing info about one field. */ +/************************************************************************/ + +class CPL_DLL OGRAttrIndex +{ +protected: + OGRAttrIndex(); + +public: + virtual ~OGRAttrIndex(); + + virtual GIntBig GetFirstMatch( OGRField *psKey ) = 0; + virtual GIntBig *GetAllMatches( OGRField *psKey ) = 0; + virtual GIntBig *GetAllMatches( OGRField *psKey, GIntBig* panFIDList, int* nFIDCount, int* nLength ) = 0; + + virtual OGRErr AddEntry( OGRField *psKey, GIntBig nFID ) = 0; + virtual OGRErr RemoveEntry( OGRField *psKey, GIntBig nFID ) = 0; + + virtual OGRErr Clear() = 0; +}; + +/************************************************************************/ +/* OGRLayerAttrIndex */ +/* */ +/* Base class representing attribute indexes for all indexed */ +/* fields in a layer. */ +/************************************************************************/ + +class CPL_DLL OGRLayerAttrIndex +{ +protected: + OGRLayer *poLayer; + char *pszIndexPath; + + OGRLayerAttrIndex(); + +public: + virtual ~OGRLayerAttrIndex(); + + virtual OGRErr Initialize( const char *pszIndexPath, OGRLayer * ) = 0; + + virtual OGRErr CreateIndex( int iField ) = 0; + virtual OGRErr DropIndex( int iField ) = 0; + virtual OGRErr IndexAllFeatures( int iField = -1 ) = 0; + + virtual OGRErr AddToIndex( OGRFeature *poFeature, int iField = -1 ) = 0; + virtual OGRErr RemoveFromIndex( OGRFeature *poFeature ) = 0; + + virtual OGRAttrIndex *GetFieldIndex( int iField ) = 0; +}; + +OGRLayerAttrIndex CPL_DLL *OGRCreateDefaultLayerIndex(); + + +#endif /* ndef _OGR_ATTRIND_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogr_formats.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogr_formats.html new file mode 100644 index 000000000..6abd9bc2c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogr_formats.html @@ -0,0 +1,606 @@ + + +OGR Vector Formats + + + + +

    OGR Vector Formats

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Format NameCodeCreationGeoreferencingCompiled by default
    Aeronav FAA files + AeronavFAA + No + Yes + Yes +
    ESRI ArcObjects + ArcObjects + No + Yes + No, needs ESRI ArcObjects +
    Arc/Info Binary Coverage + AVCBin + No + Yes + Yes +
    Arc/Info .E00 (ASCII) Coverage + AVCE00 + No + Yes + Yes +
    Arc/Info Generate + ARCGEN + No + No + Yes +
    Atlas BNA + BNA + Yes + No + Yes +
    AutoCAD DWG + DWG + No + No + No +
    AutoCAD DXF + DXF + Yes + No + Yes +
    CartoDB + CartoDB + Yes + Yes + No, needs libcurl +
    Cloudant / CouchDB + Cloudant + Yes + Yes + No, needs libcurl +
    CouchDB / GeoCouch + CouchDB + Yes + Yes + No, needs libcurl +
    Comma Separated Value (.csv) + CSV + Yes + No + Yes +
    OGC CSW (Catalog Service for the Web) + CSW + No + Yes + No, needs libcurl +
    Czech Cadastral Exchange Data Format + VFK + No + Yes + No, needs libsqlite3 +
    DODS/OPeNDAP + DODS + No + Yes + No, needs libdap +
    EDIGEO + EDIGEO + No + Yes + Yes +
    ElasticSearch + ElasticSearch + Yes (write-only) + - + No, needs libcurl +
    ESRI FileGDB + FileGDB + Yes + Yes + No, needs FileGDB API library +
    ESRI Personal GeoDatabase + PGeo + No + Yes + No, needs ODBC library +
    ESRI ArcSDE + SDE + No + Yes + No, needs ESRI SDE +
    ESRI Shapefile + ESRI Shapefile + Yes + Yes + Yes +
    FMEObjects Gateway + FMEObjects Gateway + No + Yes + No, needs FME +
    GeoJSON + GeoJSON + Yes + Yes + Yes +
    Géoconcept Export + Geoconcept + Yes + Yes + Yes +
    Geomedia .mdb + Geomedia + No + No + No, needs ODBC library +
    GeoPackage + GPKG + Yes + Yes + No, needs libsqlite3 +
    GeoRSS + GeoRSS + Yes + Yes + Yes (read support needs libexpat) +
    Google Fusion Tables + GFT + Yes + Yes + No, needs libcurl +
    Google Maps Engine + GME + Yes + Yes + No, needs libcurl +
    GML + GML + Yes + Yes + Yes (read support needs Xerces or libexpat) +
    GMT + GMT + Yes + Yes + Yes +
    GPSBabel + GPSBabel + Yes + Yes + Yes (needs GPSBabel and GPX driver) +
    GPX + GPX + Yes + Yes + Yes (read support needs libexpat) +
    GRASS Vector Format + GRASS + No + Yes + No, needs libgrass +
    GPSTrackMaker (.gtm, .gtz) + GPSTrackMaker + Yes + Yes + Yes +
    Hydrographic Transfer Format + HTF + No + Yes + Yes +
    Idrisi Vector (.VCT) + Idrisi + No + Yes + Yes +
    Informix DataBlade + IDB + Yes + Yes + No, needs Informix DataBlade +
    INTERLIS + "Interlis 1" and "Interlis 2" + Yes + Yes + No, needs Xerces +
    INGRES + INGRES + Yes + No + No, needs INGRESS +
    JML + OpenJUMP .jml + Yes + No + Yes (read support needs libexpat) +
    KML + KML + Yes + Yes + Yes (read support needs libexpat) +
    LIBKML + LIBKML + Yes + Yes + No, needs libkml +
    Mapinfo File + MapInfo File + Yes + Yes + Yes +
    Microstation DGN + DGN + Yes + No + Yes +
    Access MDB (PGeo and Geomedia capable) + MDB + No + Yes + No, needs JDK/JRE +
    Memory + Memory + Yes + Yes + Yes +
    MySQL + MySQL + No + Yes + No, needs MySQL library +
    NAS - ALKIS + NAS + No + Yes + No, needs Xerces +
    Oracle Spatial + OCI + Yes + Yes + No, needs OCI library +
    ODBC + ODBC + No + Yes + No, needs ODBC library +
    MS SQL Spatial + MSSQLSpatial + Yes + Yes + No, needs ODBC library +
    Open Document Spreadsheet + ODS + Yes + No + No, needs libexpat +
    OGDI Vectors (VPF, VMAP, DCW) + OGDI + No + Yes + No, needs OGDI library +
    OpenAir + OpenAir + No + Yes + Yes +
    ESRI FileGDB + OpenFileGDB + No + Yes + Yes +
    OpenStreetMap XML and PBF + OSM + No + Yes + No, needs libsqlite3 (and libexpat for OSM XML) +
    PCI Geomatics Database File + PCIDSK + Yes + Yes + Yes, using internal PCIDSK SDK (from GDAL 1.7.0) +
    Geospatial PDF + PDF + Yes + Yes + Yes (read supports need libpoppler or libpodofo support) +
    PDS + PDS + No + Yes + Yes +
    Planet Labs Scenes API + PLScenes + No + Yes + No, needs libcurl +
    PostgreSQL SQL dump + PGDump + Yes + Yes + Yes +
    PostgreSQL/PostGIS + PostgreSQL/PostGIS + Yes + Yes + No, needs PostgreSQL client library (libpq) +
    EPIInfo .REC + REC + No + No + Yes +
    S-57 (ENC) + S57 + No + Yes + Yes +
    SDTS + SDTS + No + Yes + Yes +
    SEG-P1 / UKOOA P1/90 + SEGUKOOA + No + Yes + Yes +
    SEG-Y + SEGY + No + No + Yes +
    Selafin/Seraphin format + Selafin + Yes + Partial (only EPSG code) + Yes +
    Norwegian SOSI Standard + SOSI + No + Yes + No, needs FYBA library +
    SQLite/SpatiaLite + SQLite + Yes + Yes + No, needs libsqlite3 or libspatialite +
    SUA + SUA + No + Yes + Yes +
    SVG + SVG + No + Yes + No, needs libexpat +
    Storage and eXchange Format + SXF + No + Yes + Yes +
    UK .NTF + UK. NTF + No + Yes + Yes +
    U.S. Census TIGER/Line + TIGER + No + Yes + Yes +
    VRT - Virtual Datasource + VRT + No + Yes + Yes +
    OGC WFS (Web Feature Service) + WFS + Yes + Yes + No, needs libcurl +
    MS Excel format + XLS + No + No + No, needs libfreexl +
    MS Office Open XML spreadsheet + XLSX + Yes + No + No, needs libexpat +
    X-Plane/Flightgear aeronautical data + XPLANE + No + Yes + Yes +
    Walk + Walk + No + Yes + No, needs ODBC library +
    WAsP .map format + WAsP + Yes + Yes + Yes +
    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogrsf_frmts.dox b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogrsf_frmts.dox new file mode 100644 index 000000000..1d22ff135 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogrsf_frmts.dox @@ -0,0 +1,2592 @@ +/****************************************************************************** + * $Id: ogrsf_frmts.dox 29035 2015-04-27 12:38:54Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Documentation for ogrsf_frmts.h classes. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +/************************************************************************/ +/* OGRSFDriverRegistrar */ +/************************************************************************/ + +/** + + \fn OGRDataSourceH OGROpen( const char *pszName, int bUpdate, + OGRSFDriverH *pahDriverList ); + + \brief Open a file / data source with one of the registered drivers. + + This function loops through all the drivers registered with the driver + manager trying each until one succeeds with the given data source. + + If this function fails, CPLGetLastErrorMsg() can be used to check if there + is an error message explaining why. + + For drivers supporting the VSI virtual file API, it is possible to open + a file in a .zip archive (see VSIInstallZipFileHandler()), in a .tar/.tar.gz/.tgz archive + (see VSIInstallTarFileHandler()) or on a HTTP / FTP server (see VSIInstallCurlFileHandler()) + + NOTE: Starting with GDAL 2.0, it is *NOT* safe to cast the returned handle to + OGRDataSource*. If a C++ object is needed, the handle should be cast to GDALDataset*. + Similarly, the returned OGRSFDriverH handle should be cast to GDALDriver*, and + *NOT* OGRSFDriver*. + + @deprecated Use GDALOpenEx() in GDAL 2.0 + + @param pszName the name of the file, or data source to open. + @param bUpdate FALSE for read-only access (the default) or TRUE for + read-write access. + @param pahDriverList if non-NULL, this argument will be updated with a + pointer to the driver which was used to open the data source. + + @return NULL on error or if the pass name is not supported by this driver, + otherwise an handle to a GDALDataset. This GDALDataset should be + closed by deleting the object when it is no longer needed. + + Example: + +
    +    OGRDataSourceH	hDS;
    +    OGRSFDriverH        *pahDriver;
    +
    +    hDS = OGROpen( "polygon.shp", 0, pahDriver );
    +    if( hDS == NULL )
    +    {
    +        return;
    +    }
    +
    +    ... use the data source ...
    +
    +    OGRReleaseDataSource( hDS );
    +  
    + +*/ + +/** + + \fn int OGRGetDriverCount(); + + \brief Fetch the number of registered drivers. + + @deprecated Use GDALGetDriverCount() in GDAL 2.0 + + @return the drivers count. + +*/ + +/** + + \fn OGRSFDriverH OGRGetDriver( int iDriver ); + + \brief Fetch the indicated driver. + + NOTE: Starting with GDAL 2.0, it is *NOT* safe to cast the returned handle to + OGRSFDriver*. If a C++ object is needed, the handle should be cast to GDALDriver*. + + @deprecated Use GDALGetDriver() in GDAL 2.0 + + @param iDriver the driver index, from 0 to GetDriverCount()-1. + + @return handle to the driver, or NULL if iDriver is out of range. + +*/ + +/** + \fn OGRSFDriverH OGRGetDriverByName( const char *pszName ); + + \brief Fetch the indicated driver. + + NOTE: Starting with GDAL 2.0, it is *NOT* safe to cast the returned handle to + OGRSFDriver*. If a C++ object is needed, the handle should be cast to GDALDriver*. + + @deprecated Use GDALGetDriverByName() in GDAL 2.0 + + @param pszName the driver name + + @return the driver, or NULL if no driver with that name is found +*/ + +/** + + \fn int OGRRegisterAll(); + + \brief Register all drivers. + + @deprecated Use GDALAllRegister() in GDAL 2.0 +*/ + +/************************************************************************/ +/* OGRSFDriver */ +/************************************************************************/ + +/** + + \fn const char *OGR_Dr_GetName( OGRSFDriverH hDriver ); + + \brief Fetch name of driver (file format). + This name should be relatively short + (10-40 characters), and should reflect the underlying file format. For + instance "ESRI Shapefile". + + This function is the same as the C++ method OGRSFDriver::GetName(). + + @param hDriver handle to the the driver to get the name from. + @return driver name. This is an internal string and should not be modified + or freed. +*/ + +/** + + \fn OGRDataSourceH OGR_Dr_Open( OGRSFDriverH hDriver, const char *pszName, + int bUpdate ); + + \brief Attempt to open file with this driver. + + NOTE: Starting with GDAL 2.0, it is *NOT* safe to cast the returned handle to + OGRDataSource*. If a C++ object is needed, the handle should be cast to GDALDataset*. + Similarly, the returned OGRSFDriverH handle should be cast to GDALDriver*, and + *NOT* OGRSFDriver*. + + @deprecated Use GDALOpenEx() in GDAL 2.0 + + @param hDriver handle to the driver that is used to open file. + @param pszName the name of the file, or data source to try and open. + @param bUpdate TRUE if update access is required, otherwise FALSE (the + default). + + @return NULL on error or if the pass name is not supported by this driver, + otherwise an handle to a GDALDataset. This GDALDataset should be + closed by deleting the object when it is no longer needed. + +*/ + +/** + \fn int OGR_Dr_TestCapability( OGRSFDriverH hDriver, const char *pszCap ); + + \brief Test if capability is available. + + One of the following data source capability names can be passed into this + function, and a TRUE or FALSE value will be returned indicating whether + or not the capability is available for this object. + +
      +
    • ODrCCreateDataSource: True if this driver can support creating data sources.

      +

    • ODrCDeleteDataSource: True if this driver supports deleting data sources.

      +

    + + The \#define macro forms of the capability names should be used in preference + to the strings themselves to avoid mispelling. + + @deprecated Use GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE) in GDAL 2.0 + + @param hDriver handle to the driver to test the capability against. + @param pszCap the capability to test. + + @return TRUE if capability available otherwise FALSE. + +*/ + +/** + \fn OGRErr OGR_Dr_DeleteDataSource( OGRSFDriverH hDriver, + const char *pszDataSource ) + + \brief Delete a datasource. + + Delete (from the disk, in the database, ...) the named datasource. + Normally it would be safest if the datasource was not open at the time. + + Whether this is a supported operation on this driver case be tested + using TestCapability() on ODrCDeleteDataSource. + + @deprecated Use GDALDeleteDataset() in GDAL 2 + + @param hDriver handle to the driver on which data source deletion is +based. + + @param pszDataSource the name of the datasource to delete. + + @return OGRERR_NONE on success, and OGRERR_UNSUPPORTED_OPERATION if this + is not supported by this driver. + +*/ + +/** + + \fn OGRDataSourceH OGR_Dr_CreateDataSource( OGRSFDriverH hDriver, + const char *pszName, + char ** papszOptions ) + + \brief This function attempts to create a new data source based on the passed driver. + + The papszOptions argument can be used to control driver specific + creation options. These options are normally documented in the format + specific documentation. + + It is important to call OGR_DS_Destroy() when the datasource is no longer + used to ensure that all data has been properly flushed to disk. + + @deprecated Use GDALCreate() in GDAL 2.0 + + @param hDriver handle to the driver on which data source creation is +based. + @param pszName the name for the new data source. UTF-8 encoded. + @param papszOptions a StringList of name=value options. Options are driver +specific, and driver information can be found at the following url: +http://www.gdal.org/ogr/ogr_formats.html + + @return NULL is returned on failure, or a new OGRDataSource handle on +success. +*/ + +/** + + \fn OGRDataSourceH OGR_Dr_CopyDataSource( OGRSFDriverH hDriver, + OGRDataSourceH hSrcDS, + const char *pszNewName, + char **papszOptions ) + + \brief This function creates a new datasource by copying all the layers from the source datasource. + + It is important to call OGR_DS_Destroy() when the datasource is no longer + used to ensure that all data has been properly flushed to disk. + + @deprecated Use GDALCreateCopy() in GDAL 2.0 + + @param hDriver handle to the driver on which data source creation is +based. + @param hSrcDS source datasource + @param pszNewName the name for the new data source. + @param papszOptions a StringList of name=value options. Options are driver +specific, and driver information can be found at the following url: +http://www.gdal.org/ogr/ogr_formats.html + + @return NULL is returned on failure, or a new OGRDataSource handle on +success. +*/ + +/************************************************************************/ +/* OGRDataSource */ +/************************************************************************/ + +/** + \fn void OGR_DS_Destroy( OGRDataSourceH hDataSource ) + + \brief Closes opened datasource and releases allocated resources. + + This method is the same as the C++ method OGRDataSource::DestroyDataSource(). + + @deprecated Use GDALClose() in GDAL 2.0 + + @param hDataSource handle to allocated datasource object. +*/ + +/** + \fn const char *OGR_DS_GetName( OGRDataSourceH hDS ); + + \brief Returns the name of the data source. + + This string should be sufficient to + open the data source if passed to the same OGRSFDriver that this data + source was opened with, but it need not be exactly the same string that + was used to open the data source. Normally this is a filename. + + @deprecated Use GDALGetDescription() in GDAL 2.0 + + @param hDS handle to the data source to get the name from. + @return pointer to an internal name string which should not be modified + or freed by the caller. + +*/ + + +/** + \fn int OGR_DS_GetLayerCount( OGRDataSourceH hDS ); + + \brief Get the number of layers in this data source. + + @deprecated Use GDALDatasetGetLayerCount() in GDAL 2.0 + + @param hDS handle to the data source from which to get the number of layers. + @return layer count. + +*/ + +/** + \fn OGRLayerH OGR_DS_GetLayer( OGRDataSourceH hDS, int iLayer ); + + \brief Fetch a layer by index. + + The returned layer remains owned by the + OGRDataSource and should not be deleted by the application. + + @deprecated Use GDALDatasetGetLayer() in GDAL 2.0 + + @param hDS handle to the data source from which to get the layer. + @param iLayer a layer number between 0 and OGR_DS_GetLayerCount()-1. + + @return an handle to the layer, or NULL if iLayer is out of range + or an error occurs. + +*/ + +/** + \fn OGRLayerH OGR_DS_GetLayerByName(OGRDataSourceH hDS, + const char *pszLayerName ); + + \brief Fetch a layer by name. + + The returned layer remains owned by the + OGRDataSource and should not be deleted by the application. + + @deprecated Use GDALDatasetGetLayerByName() in GDAL 2.0 + + @param hDS handle to the data source from which to get the layer. + @param pszLayerName Layer the layer name of the layer to fetch. + + @return an handle to the layer, or NULL if the layer is not found + or an error occurs. + +*/ + +/** + \fn OGRLayerH OGR_DS_CopyLayer( OGRDataSourceH hDS, + OGRLayerH hSrcLayer, const char *pszNewName, + char **papszOptions ) + + \brief Duplicate an existing layer. + + This function creates a new layer, duplicate the field definitions of the + source layer and then duplicate each features of the source layer. + The papszOptions argument + can be used to control driver specific creation options. These options are + normally documented in the format specific documentation. + The source layer may come from another dataset. + + @deprecated Use GDALDatasetCopyLayer() in GDAL 2.0 + + @param hDS handle to the data source where to create the new layer + @param hSrcLayer handle to the source layer. + @param pszNewName the name of the layer to create. + @param papszOptions a StringList of name=value options. Options are driver + specific. + + @return an handle to the layer, or NULL if an error occurs. +*/ + +/** + \fn OGRErr OGR_DS_DeleteLayer(OGRDataSourceH hDS, int iLayer); + + \brief Delete the indicated layer from the datasource. + + If this method is supported + the ODsCDeleteLayer capability will test TRUE on the OGRDataSource. + + @deprecated Use GDALDatasetDeleteLayer() in GDAL 2.0 + + @param hDS handle to the datasource + @param iLayer the index of the layer to delete. + + @return OGRERR_NONE on success, or OGRERR_UNSUPPORTED_OPERATION if deleting + layers is not supported for this datasource. + +*/ + +/** + \fn OGRLayerH OGR_DS_ExecuteSQL( OGRDataSourceH hDS, + const char *pszSQLCommand, + OGRGeometryH hSpatialFilter, + const char *pszDialect ); + + \brief Execute an SQL statement against the data store. + + The result of an SQL query is either NULL for statements that are in error, + or that have no results set, or an OGRLayer handle representing a results + set from the query. Note that this OGRLayer is in addition to the layers + in the data store and must be destroyed with + OGR_DS_ReleaseResultSet() before the data source is closed + (destroyed). + + For more information on the SQL dialect supported internally by OGR + review the OGR SQL document. Some drivers (ie. + Oracle and PostGIS) pass the SQL directly through to the underlying RDBMS. + + Starting with OGR 1.10, the SQLITE dialect + can also be used. + + @deprecated Use GDALDatasetExecuteSQL() in GDAL 2.0 + + @param hDS handle to the data source on which the SQL query is executed. + @param pszSQLCommand the SQL statement to execute. + @param hSpatialFilter handle to a geometry which represents a spatial filter. Can be NULL. + @param pszDialect allows control of the statement dialect. If set to NULL, the +OGR SQL engine will be used, except for RDBMS drivers that will use their dedicated SQL engine, +unless OGRSQL is explicitly passed as the dialect. Starting with OGR 1.10, the SQLITE dialect +can also be used. + + @return an handle to a OGRLayer containing the results of the query. + Deallocate with OGR_DS_ReleaseResultSet(). + +*/ + + +/** + \fn void OGR_DS_ReleaseResultSet( OGRDataSourceH hDS, OGRLayerH hLayer ); + + \brief Release results of OGR_DS_ExecuteSQL(). + + This function should only be used to deallocate OGRLayers resulting from + an OGR_DS_ExecuteSQL() call on the same OGRDataSource. + Failure to deallocate a results set before destroying the OGRDataSource + may cause errors. + + @deprecated Use GDALDatasetReleaseResultSet() in GDAL 2.0 + + @param hDS an handle to the data source on which was executed an + SQL query. + @param hLayer handle to the result of a previous OGR_DS_ExecuteSQL() call. + +*/ + +/** + \fn int OGR_DS_TestCapability( OGRDataSourceH hDS, const char *pszCapability ); + + \brief Test if capability is available. + + One of the following data source capability names can be passed into this + function, and a TRUE or FALSE value will be returned indicating whether + or not the capability is available for this object. + +
      +
    • ODsCCreateLayer: True if this datasource can create new layers. +
    • ODsCDeleteLayer: True if this datasource can delete existing layers.

      +

    • ODsCCreateGeomFieldAfterCreateLayer: True if the layers of this + datasource support CreateGeomField() just after layer creation.

      +

    • ODsCCurveGeometries: True if this datasource supports writing curve geometries. (GDAL 2.0). + In that case, OLCCurveGeometries must also be declared in layers of that dataset.

      +

      +

    + + The \#define macro forms of the capability names should be used in preference + to the strings themselves to avoid mispelling. + + @deprecated Use GDALDatasetTestCapability() in GDAL 2.0 + + @param hDS handle to the data source against which to test the capability. + @param pszCapability the capability to test. + + @return TRUE if capability available otherwise FALSE. + +*/ + +/** + \fn OGRLayerH OGR_DS_CreateLayer( OGRDataSourceH hDS, + const char * pszName, + OGRSpatialReferenceH hSpatialRef, + OGRwkbGeometryType eType, + char ** papszOptions ); + +\brief This function attempts to create a new layer on the data source with the indicated name, coordinate system, geometry type. + +The papszOptions argument +can be used to control driver specific creation options. These options are +normally documented in the format specific documentation. + +@deprecated Use GDALDatasetCreateLayer() in GDAL 2.0 + + @param hDS The dataset handle. + @param pszName the name for the new layer. This should ideally not +match any existing layer on the datasource. + @param hSpatialRef handle to the coordinate system to use for the new layer, +or NULL if no coordinate system is available. + @param eType the geometry type for the layer. Use wkbUnknown if there +are no constraints on the types geometry to be written. + @param papszOptions a StringList of name=value options. Options are driver +specific, and driver information can be found at the following url: +http://www.gdal.org/ogr/ogr_formats.html + + @return NULL is returned on failure, or a new OGRLayer handle on success. + +Example: + +\code +#include "ogrsf_frmts.h" +#include "cpl_string.h" + +... + + OGRLayerH *hLayer; + char **papszOptions; + + if( OGR_DS_TestCapability( hDS, ODsCCreateLayer ) ) + { + ... + } + + papszOptions = CSLSetNameValue( papszOptions, "DIM", "2" ); + hLayer = OGR_DS_CreateLayer( hDS, "NewLayer", NULL, wkbUnknown, + papszOptions ); + CSLDestroy( papszOptions ); + + if( hLayer == NULL ) + { + ... + } +\endcode +*/ + +/** + \fn OGRErr OGRReleaseDataSource( OGRDataSourceH hDS ) + +\brief Drop a reference to this datasource, and if the reference count drops to zero close (destroy) the datasource. + +Internally this actually calls +the OGRSFDriverRegistrar::ReleaseDataSource() method. This method is +essentially a convenient alias. + +@deprecated Use GDALClose() in GDAL 2.0 + +@param hDS handle to the data source to release + +@return OGRERR_NONE on success or an error code. +*/ + +/** + \fn OGRSFDriverH OGR_DS_GetDriver( OGRDataSourceH hDS ); + +\brief Returns the driver that the dataset was opened with. + +NOTE: Starting with GDAL 2.0, it is *NOT* safe to cast the returned handle to +OGRSFDriver*. If a C++ object is needed, the handle should be cast to GDALDriver*. + +@deprecated Use GDALGetDatasetDriver() in GDAL 2.0 + +@param hDS handle to the datasource +@return NULL if driver info is not available, or pointer to a driver owned +by the OGRSFDriverManager. +*/ + +/************************************************************************/ +/* OGRLayer */ +/************************************************************************/ + +/** + \fn const char* OGRLayer::GetName(); + + \brief Return the layer name. + + This returns the same content as GetLayerDefn()->OGRFeatureDefn::GetName(), but for a + few drivers, calling GetName() directly can avoid lengthy layer + definition initialization. + + This method is the same as the C function OGR_L_GetName(). + + If this method is derived in a driver, it must be done such that it + returns the same content as GetLayerDefn()->OGRFeatureDefn::GetName(). + + @return the layer name (must not been freed) + @since OGR 1.8.0 + +*/ + +/** + \fn const char* OGR_L_GetName( OGRLayerH hLayer ); + + \brief Return the layer name. + + This returns the same content as OGR_FD_GetName(OGR_L_GetLayerDefn(hLayer)), + but for a few drivers, calling OGR_L_GetName() directly can avoid lengthy + layer definition initialization. + + This function is the same as the C++ method OGRLayer::GetName(). + + @param hLayer handle to the layer. + @return the layer name (must not been freed) + @since OGR 1.8.0 +*/ + + +/** + \fn OGRwkbGeometryType OGRLayer::GetGeomType(); + + \brief Return the layer geometry type. + + This returns the same result as GetLayerDefn()->OGRFeatureDefn::GetGeomType(), but for a + few drivers, calling GetGeomType() directly can avoid lengthy layer + definition initialization. + + For layers with multiple geometry fields, this method only returns the geometry + type of the first geometry column. For other columns, use + GetLayerDefn()->OGRFeatureDefn::GetGeomFieldDefn(i)->GetType(). + For layers without any geometry field, this method returns wkbNone. + + This method is the same as the C function OGR_L_GetGeomType(). + + If this method is derived in a driver, it must be done such that it + returns the same content as GetLayerDefn()->OGRFeatureDefn::GetGeomType(). + + @return the geometry type + @since OGR 1.8.0 + +*/ + +/** + \fn OGRwkbGeometryType OGR_L_GetGeomType( OGRLayerH hLayer ); + + \brief Return the layer geometry type. + + This returns the same result as OGR_FD_GetGeomType(OGR_L_GetLayerDefn(hLayer)), + but for a few drivers, calling OGR_L_GetGeomType() directly can avoid lengthy + layer definition initialization. + + For layers with multiple geometry fields, this method only returns the geometry + type of the first geometry column. For other columns, use + OGR_GFld_GetType(OGR_FD_GetGeomFieldDefn(OGR_L_GetLayerDefn(hLayer), i)). + For layers without any geometry field, this method returns wkbNone. + + This function is the same as the C++ method OGRLayer::GetGeomType(). + + @param hLayer handle to the layer. + @return the geometry type + @since OGR 1.8.0 +*/ + + +/** + \fn void OGRLayer::ResetReading(); + + \brief Reset feature reading to start on the first feature. + + This affects GetNextFeature(). + + This method is the same as the C function OGR_L_ResetReading(). + +*/ + +/** + \fn void OGR_L_ResetReading( OGRLayerH hLayer ); + + \brief Reset feature reading to start on the first feature. + + This affects GetNextFeature(). + + This function is the same as the C++ method OGRLayer::ResetReading(). + + @param hLayer handle to the layer on which features are read. + +*/ + +/** + \fn OGRFeature *OGRLayer::GetNextFeature(); + + \brief Fetch the next available feature from this layer. + + The returned feature + becomes the responsiblity of the caller to delete with OGRFeature::DestroyFeature(). It is critical that + all features associated with an OGRLayer (more specifically an + OGRFeatureDefn) be deleted before that layer/datasource is deleted. + + Only features matching the current spatial filter (set with + SetSpatialFilter()) will be returned. + + This method implements sequential access to the features of a layer. The + ResetReading() method can be used to start at the beginning again. + + Features returned by GetNextFeature() may or may not be affected by concurrent + modifications depending on drivers. A guaranteed way of seing modifications in + effect is to call ResetReading() on layers where GetNextFeature() has been called, + before reading again. + Structural changes in layers (field addition, deletion, ...) when a read is in + progress may or may not be possible depending on drivers. + If a transaction is committed/aborted, the current sequential reading may or may not be + valid after that operation and a call to ResetReading() might be needed. + + This method is the same as the C function OGR_L_GetNextFeature(). + + @return a feature, or NULL if no more features are available. + +*/ + + +/** + \fn OGRFeatureH OGR_L_GetNextFeature( OGRLayerH hLayer ); + + \brief Fetch the next available feature from this layer. + + The returned feature + becomes the responsiblity of the caller to delete with OGR_F_Destroy(). It is critical that + all features associated with an OGRLayer (more specifically an + OGRFeatureDefn) be deleted before that layer/datasource is deleted. + + Only features matching the current spatial filter (set with + SetSpatialFilter()) will be returned. + + This function implements sequential access to the features of a layer. + The OGR_L_ResetReading() function can be used to start at the beginning + again. + + Features returned by OGR_GetNextFeature() may or may not be affected by concurrent + modifications depending on drivers. A guaranteed way of seing modifications in + effect is to call OGR_L_ResetReading() on layers where OGR_GetNextFeature() has been called, + before reading again. + Structural changes in layers (field addition, deletion, ...) when a read is in + progress may or may not be possible depending on drivers. + If a transaction is committed/aborted, the current sequential reading may or may not be + valid after that operation and a call to OGR_L_ResetReading() might be needed. + + This function is the same as the C++ method OGRLayer::GetNextFeature(). + + @param hLayer handle to the layer from which feature are read. + @return an handle to a feature, or NULL if no more features are available. + +*/ + +/** + + \fn GIntBig OGRLayer::GetFeatureCount( int bForce = TRUE ); + + \brief Fetch the feature count in this layer. + + Returns the number of features in the layer. For dynamic databases the + count may not be exact. If bForce is FALSE, and it would be expensive + to establish the feature count a value of -1 may be returned indicating + that the count isn't know. If bForce is TRUE some implementations will + actually scan the entire layer once to count objects. + + The returned count takes the spatial filter into account. + + Note that some implementations of this method may alter the read cursor + of the layer. + + This method is the same as the C function OGR_L_GetFeatureCount(). + + Note: since GDAL 2.0, this method returns a GIntBig (previously a int) + + @param bForce Flag indicating whether the count should be computed even + if it is expensive. + + @return feature count, -1 if count not known. + +*/ + +/** + \fn GIntBig OGR_L_GetFeatureCount( OGRLayerH hLayer, int bForce ); + + \brief Fetch the feature count in this layer. + + Returns the number of features in the layer. For dynamic databases the + count may not be exact. If bForce is FALSE, and it would be expensive + to establish the feature count a value of -1 may be returned indicating + that the count isn't know. If bForce is TRUE some implementations will + actually scan the entire layer once to count objects. + + The returned count takes the spatial filter into account. + + Note that some implementations of this method may alter the read cursor + of the layer. + + This function is the same as the CPP OGRLayer::GetFeatureCount(). + + Note: since GDAL 2.0, this method returns a GIntBig (previously a int) + + @param hLayer handle to the layer that owned the features. + @param bForce Flag indicating whether the count should be computed even + if it is expensive. + + @return feature count, -1 if count not known. + +*/ + +/** + + \fn OGRErr OGRLayer::GetExtent( OGREnvelope *psExtent, int bForce = TRUE ); + + \brief Fetch the extent of this layer. + + Returns the extent (MBR) of the data in the layer. If bForce is FALSE, + and it would be expensive to establish the extent then OGRERR_FAILURE + will be returned indicating that the extent isn't know. If bForce is + TRUE then some implementations will actually scan the entire layer once + to compute the MBR of all the features in the layer. + + Depending on the drivers, the returned extent may or may not take the + spatial filter into account. So it is safer to call GetExtent() without + setting a spatial filter. + + Layers without any geometry may return OGRERR_FAILURE just indicating that + no meaningful extents could be collected. + + Note that some implementations of this method may alter the read cursor + of the layer. + + This method is the same as the C function OGR_L_GetExtent(). + + @param psExtent the structure in which the extent value will be returned. + @param bForce Flag indicating whether the extent should be computed even + if it is expensive. + + @return OGRERR_NONE on success, OGRERR_FAILURE if extent not known. + +*/ + + +/** + + \fn OGRErr OGR_L_GetExtent( OGRLayerH hLayer, OGREnvelope *psExtent, int bForce); + + \brief Fetch the extent of this layer. + + Returns the extent (MBR) of the data in the layer. If bForce is FALSE, + and it would be expensive to establish the extent then OGRERR_FAILURE + will be returned indicating that the extent isn't know. If bForce is + TRUE then some implementations will actually scan the entire layer once + to compute the MBR of all the features in the layer. + + Depending on the drivers, the returned extent may or may not take the + spatial filter into account. So it is safer to call OGR_L_GetExtent() without + setting a spatial filter. + + Layers without any geometry may return OGRERR_FAILURE just indicating that + no meaningful extents could be collected. + + Note that some implementations of this method may alter the read cursor + of the layer. + + This function is the same as the C++ method OGRLayer::GetExtent(). + + @param hLayer handle to the layer from which to get extent. + @param psExtent the structure in which the extent value will be returned. + @param bForce Flag indicating whether the extent should be computed even + if it is expensive. + + @return OGRERR_NONE on success, OGRERR_FAILURE if extent not known. + +*/ + +/** + + \fn OGRErr OGRLayer::GetExtent( int iGeomField,OGREnvelope *psExtent, int bForce = TRUE ); + + \brief Fetch the extent of this layer, on the specified geometry field. + + Returns the extent (MBR) of the data in the layer. If bForce is FALSE, + and it would be expensive to establish the extent then OGRERR_FAILURE + will be returned indicating that the extent isn't know. If bForce is + TRUE then some implementations will actually scan the entire layer once + to compute the MBR of all the features in the layer. + + Depending on the drivers, the returned extent may or may not take the + spatial filter into account. So it is safer to call GetExtent() without + setting a spatial filter. + + Layers without any geometry may return OGRERR_FAILURE just indicating that + no meaningful extents could be collected. + + Note that some implementations of this method may alter the read cursor + of the layer. + + Note to driver implementators: if you implement GetExtent(int,OGREnvelope*,int), + you must also implement GetExtent(OGREnvelope*, int) to make it call + GetExtent(0,OGREnvelope*,int). + + This method is the same as the C function OGR_L_GetExtentEx(). + + @param iGeomField the index of the geometry field on which to compute the extent. + @param psExtent the structure in which the extent value will be returned. + @param bForce Flag indicating whether the extent should be computed even + if it is expensive. + + @return OGRERR_NONE on success, OGRERR_FAILURE if extent not known. + +*/ + + +/** + + \fn OGRErr OGR_L_GetExtentEx( OGRLayerH hLayer, int iGeomField, OGREnvelope *psExtent, int bForce); + + \brief Fetch the extent of this layer, on the specified geometry field. + + Returns the extent (MBR) of the data in the layer. If bForce is FALSE, + and it would be expensive to establish the extent then OGRERR_FAILURE + will be returned indicating that the extent isn't know. If bForce is + TRUE then some implementations will actually scan the entire layer once + to compute the MBR of all the features in the layer. + + Depending on the drivers, the returned extent may or may not take the + spatial filter into account. So it is safer to call OGR_L_GetExtent() without + setting a spatial filter. + + Layers without any geometry may return OGRERR_FAILURE just indicating that + no meaningful extents could be collected. + + Note that some implementations of this method may alter the read cursor + of the layer. + + This function is the same as the C++ method OGRLayer::GetExtent(). + + @param hLayer handle to the layer from which to get extent. + @param iGeomField the index of the geometry field on which to compute the extent. + @param psExtent the structure in which the extent value will be returned. + @param bForce Flag indicating whether the extent should be computed even + if it is expensive. + + @return OGRERR_NONE on success, OGRERR_FAILURE if extent not known. + +*/ + +/** + \fn void OGRLayer::SetSpatialFilter( OGRGeometry * poFilter ); + + \brief Set a new spatial filter. + + This method set the geometry to be used as a spatial filter when + fetching features via the GetNextFeature() method. Only features that + geometrically intersect the filter geometry will be returned. + + Currently this test is may be inaccurately implemented, but it is + guaranteed that all features who's envelope (as returned by + OGRGeometry::getEnvelope()) overlaps the envelope of the spatial filter + will be returned. This can result in more shapes being returned that + should strictly be the case. + + This method makes an internal copy of the passed geometry. The + passed geometry remains the responsibility of the caller, and may + be safely destroyed. + + For the time being the passed filter geometry should be in the same + SRS as the layer (as returned by OGRLayer::GetSpatialRef()). In the + future this may be generalized. + + This method is the same as the C function OGR_L_SetSpatialFilter(). + + @param poFilter the geometry to use as a filtering region. NULL may + be passed indicating that the current spatial filter should be cleared, + but no new one instituted. + + */ + +/** + \fn void OGR_L_SetSpatialFilter( OGRLayerH hLayer, OGRGeometryH hGeom ); + + \brief Set a new spatial filter. + + This function set the geometry to be used as a spatial filter when + fetching features via the OGR_L_GetNextFeature() function. Only + features that geometrically intersect the filter geometry will be + returned. + + Currently this test is may be inaccurately implemented, but it is + guaranteed that all features who's envelope (as returned by + OGR_G_GetEnvelope()) overlaps the envelope of the spatial filter + will be returned. This can result in more shapes being returned that + should strictly be the case. + + This function makes an internal copy of the passed geometry. The + passed geometry remains the responsibility of the caller, and may + be safely destroyed. + + For the time being the passed filter geometry should be in the same + SRS as the layer (as returned by OGR_L_GetSpatialRef()). In the + future this may be generalized. + + This function is the same as the C++ method OGRLayer::SetSpatialFilter. + + @param hLayer handle to the layer on which to set the spatial filter. + @param hGeom handle to the geometry to use as a filtering region. NULL may + be passed indicating that the current spatial filter should be cleared, + but no new one instituted. + + */ + +/** + \fn void OGRLayer::SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + + \brief Set a new rectangular spatial filter. + + This method set rectangle to be used as a spatial filter when + fetching features via the GetNextFeature() method. Only features that + geometrically intersect the given rectangle will be returned. + + The x/y values should be in the same coordinate system as the layer as + a whole (as returned by OGRLayer::GetSpatialRef()). Internally this + method is normally implemented as creating a 5 vertex closed rectangular + polygon and passing it to OGRLayer::SetSpatialFilter(). It exists as + a convenience. + + The only way to clear a spatial filter set with this method is to + call OGRLayer::SetSpatialFilter(NULL). + + This method is the same as the C function OGR_L_SetSpatialFilterRect(). + + @param dfMinX the minimum X coordinate for the rectangular region. + @param dfMinY the minimum Y coordinate for the rectangular region. + @param dfMaxX the maximum X coordinate for the rectangular region. + @param dfMaxY the maximum Y coordinate for the rectangular region. + + */ + +/** + \fn void OGR_L_SetSpatialFilterRect( OGRLayerH hLayer, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + + \brief Set a new rectangular spatial filter. + + This method set rectangle to be used as a spatial filter when + fetching features via the OGR_L_GetNextFeature() method. Only features that + geometrically intersect the given rectangle will be returned. + + The x/y values should be in the same coordinate system as the layer as + a whole (as returned by OGRLayer::GetSpatialRef()). Internally this + method is normally implemented as creating a 5 vertex closed rectangular + polygon and passing it to OGRLayer::SetSpatialFilter(). It exists as + a convenience. + + The only way to clear a spatial filter set with this method is to + call OGRLayer::SetSpatialFilter(NULL). + + This method is the same as the C++ method OGRLayer::SetSpatialFilterRect(). + + @param hLayer handle to the layer on which to set the spatial filter. + @param dfMinX the minimum X coordinate for the rectangular region. + @param dfMinY the minimum Y coordinate for the rectangular region. + @param dfMaxX the maximum X coordinate for the rectangular region. + @param dfMaxY the maximum Y coordinate for the rectangular region. + + */ + + +/** + \fn void OGRLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poFilter ); + + \brief Set a new spatial filter. + + This method set the geometry to be used as a spatial filter when + fetching features via the GetNextFeature() method. Only features that + geometrically intersect the filter geometry will be returned. + + Currently this test is may be inaccurately implemented, but it is + guaranteed that all features who's envelope (as returned by + OGRGeometry::getEnvelope()) overlaps the envelope of the spatial filter + will be returned. This can result in more shapes being returned that + should strictly be the case. + + This method makes an internal copy of the passed geometry. The + passed geometry remains the responsibility of the caller, and may + be safely destroyed. + + For the time being the passed filter geometry should be in the same + SRS as the geometry field definition it corresponds to (as returned by + GetLayerDefn()->OGRFeatureDefn::GetGeomFieldDefn(iGeomField)->GetSpatialRef()). In the + future this may be generalized. + + Note that only the last spatial filter set is applied, even if several + successive calls are done with different iGeomField values. + + Note to driver implementators: if you implement SetSpatialFilter(int,OGRGeometry*), + you must also implement SetSpatialFilter(OGRGeometry*) to make it call + SetSpatialFilter(0,OGRGeometry*). + + This method is the same as the C function OGR_L_SetSpatialFilterEx(). + + @param iGeomField index of the geometry field on which the spatial filter + operates. + @param poFilter the geometry to use as a filtering region. NULL may + be passed indicating that the current spatial filter should be cleared, + but no new one instituted. + + @since GDAL 1.11 + + */ + +/** + \fn void OGR_L_SetSpatialFilterEx( OGRLayerH hLayer, int iGeomField, OGRGeometryH hGeom ); + + \brief Set a new spatial filter. + + This function set the geometry to be used as a spatial filter when + fetching features via the OGR_L_GetNextFeature() function. Only + features that geometrically intersect the filter geometry will be + returned. + + Currently this test is may be inaccurately implemented, but it is + guaranteed that all features who's envelope (as returned by + OGR_G_GetEnvelope()) overlaps the envelope of the spatial filter + will be returned. This can result in more shapes being returned that + should strictly be the case. + + This function makes an internal copy of the passed geometry. The + passed geometry remains the responsibility of the caller, and may + be safely destroyed. + + For the time being the passed filter geometry should be in the same + SRS as the geometry field definition it corresponds to (as returned by + GetLayerDefn()->OGRFeatureDefn::GetGeomFieldDefn(iGeomField)->GetSpatialRef()). In the + future this may be generalized. + + Note that only the last spatial filter set is applied, even if several + successive calls are done with different iGeomField values. + + This function is the same as the C++ method OGRLayer::SetSpatialFilter. + + @param hLayer handle to the layer on which to set the spatial filter. + @param iGeomField index of the geometry field on which the spatial filter + operates. + @param hGeom handle to the geometry to use as a filtering region. NULL may + be passed indicating that the current spatial filter should be cleared, + but no new one instituted. + + @since GDAL 1.11 + + */ + +/** + \fn void OGRLayer::SetSpatialFilterRect( int iGeomField, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + + \brief Set a new rectangular spatial filter. + + This method set rectangle to be used as a spatial filter when + fetching features via the GetNextFeature() method. Only features that + geometrically intersect the given rectangle will be returned. + + The x/y values should be in the same coordinate system as as the geometry + field definition it corresponds to (as returned by + GetLayerDefn()->OGRFeatureDefn::GetGeomFieldDefn(iGeomField)->GetSpatialRef()). Internally this + method is normally implemented as creating a 5 vertex closed rectangular + polygon and passing it to OGRLayer::SetSpatialFilter(). It exists as + a convenience. + + The only way to clear a spatial filter set with this method is to + call OGRLayer::SetSpatialFilter(NULL). + + This method is the same as the C function OGR_L_SetSpatialFilterRectEx(). + + @param iGeomField index of the geometry field on which the spatial filter + operates. + @param dfMinX the minimum X coordinate for the rectangular region. + @param dfMinY the minimum Y coordinate for the rectangular region. + @param dfMaxX the maximum X coordinate for the rectangular region. + @param dfMaxY the maximum Y coordinate for the rectangular region. + + @since GDAL 1.11 + */ + +/** + \fn void OGR_L_SetSpatialFilterRectEx( OGRLayerH hLayer, + int iGeomField, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + + \brief Set a new rectangular spatial filter. + + This method set rectangle to be used as a spatial filter when + fetching features via the OGR_L_GetNextFeature() method. Only features that + geometrically intersect the given rectangle will be returned. + + The x/y values should be in the same coordinate system as as the geometry + field definition it corresponds to (as returned by + GetLayerDefn()->OGRFeatureDefn::GetGeomFieldDefn(iGeomField)->GetSpatialRef()). Internally this + method is normally implemented as creating a 5 vertex closed rectangular + polygon and passing it to OGRLayer::SetSpatialFilter(). It exists as + a convenience. + + The only way to clear a spatial filter set with this method is to + call OGRLayer::SetSpatialFilter(NULL). + + This method is the same as the C++ method OGRLayer::SetSpatialFilterRect(). + + @param hLayer handle to the layer on which to set the spatial filter. + @param iGeomField index of the geometry field on which the spatial filter + operates. + @param dfMinX the minimum X coordinate for the rectangular region. + @param dfMinY the minimum Y coordinate for the rectangular region. + @param dfMaxX the maximum X coordinate for the rectangular region. + @param dfMaxY the maximum Y coordinate for the rectangular region. + + @since GDAL 1.11 + */ + + +/** + \fn OGRGeometry *OGRLayer::GetSpatialFilter(); + + \brief This method returns the current spatial filter for this layer. + + The returned pointer is to an internally owned object, and should not + be altered or deleted by the caller. + + This method is the same as the C function OGR_L_GetSpatialFilter(). + + @return spatial filter geometry. + + */ + + +/** + \fn OGRGeometryH OGR_L_GetSpatialFilter( OGRLayerH hLayer ); + + \brief This function returns the current spatial filter for this layer. + + The returned pointer is to an internally owned object, and should not + be altered or deleted by the caller. + + This function is the same as the C++ method OGRLayer::GetSpatialFilter(). + + @param hLayer handle to the layer to get the spatial filter from. + @return an handle to the spatial filter geometry. + + */ + +/** + \fn void OGRLayer::SetAttributeFilter( const char *pszQuery ); + + \brief Set a new attribute query. + + This method sets the attribute query string to be used when + fetching features via the GetNextFeature() method. Only features for which + the query evaluates as true will be returned. + + The query string should be in the format of an SQL WHERE clause. For + instance "population > 1000000 and population < 5000000" where population + is an attribute in the layer. The query format is normally a restricted + form of SQL WHERE clause as described in the "WHERE" section of the + OGR SQL tutorial. In some cases (RDBMS + backed drivers) the native capabilities of the database may be used to + interprete the WHERE clause in which case the capabilities will be broader + than those of OGR SQL. + + Note that installing a query string will generally result in resetting + the current reading position (ala ResetReading()). + + This method is the same as the C function OGR_L_SetAttributeFilter(). + + @param pszQuery query in restricted SQL WHERE format, or NULL to clear the + current query. + + @return OGRERR_NONE if successfully installed, or an error code if the + query expression is in error, or some other failure occurs. + + */ + +/** + \fn OGRErr OGR_L_SetAttributeFilter(OGRLayerH hLayer, const char *pszQuery); + + \brief Set a new attribute query. + + This function sets the attribute query string to be used when + fetching features via the OGR_L_GetNextFeature() function. + Only features for which the query evaluates as true will be returned. + + The query string should be in the format of an SQL WHERE clause. For + instance "population > 1000000 and population < 5000000" where population + is an attribute in the layer. The query format is a restricted form of SQL + WHERE clause as defined "eq_format=restricted_where" about half way through + this document: + + http://ogdi.sourceforge.net/prop/6.2.CapabilitiesMetadata.html + + Note that installing a query string will generally result in resetting + the current reading position (ala OGR_L_ResetReading()). + + This function is the same as the C++ method OGRLayer::SetAttributeFilter(). + + @param hLayer handle to the layer on which attribute query will be executed. + @param pszQuery query in restricted SQL WHERE format, or NULL to clear the + current query. + + @return OGRERR_NONE if successfully installed, or an error code if the + query expression is in error, or some other failure occurs. + + */ + +/** + \fn OGRFeatureDefn *OGRLayer::GetLayerDefn(); + + \brief Fetch the schema information for this layer. + + The returned OGRFeatureDefn is owned by the OGRLayer, and should not be + modified or freed by the application. It encapsulates the attribute schema + of the features of the layer. + + This method is the same as the C function OGR_L_GetLayerDefn(). + + @return feature definition. + +*/ + +/** + \fn OGRFeatureDefnH OGR_L_GetLayerDefn( OGRLayerH hLayer ); + + \brief Fetch the schema information for this layer. + + The returned handle to the OGRFeatureDefn is owned by the OGRLayer, + and should not be modified or freed by the application. It encapsulates + the attribute schema of the features of the layer. + + This function is the same as the C++ method OGRLayer::GetLayerDefn(). + + @param hLayer handle to the layer to get the schema information. + @return an handle to the feature definition. + +*/ + +/** + + \fn int OGR_L_FindFieldIndex( OGRLayerH hLayer, const char *, int bExactMatch ); + + \brief Find the index of field in a layer. + + The returned number is the index of the field in the layers, or -1 if the + field doesn't exist. + + If bExactMatch is set to FALSE and the field doesn't exists in the given form + the driver might apply some changes to make it match, like those it might do + if the layer was created (eg. like LAUNDER in the OCI driver). + + This method is the same as the C++ method OGRLayer::FindFieldIndex(). + + @return field index, or -1 if the field doesn't exist + +*/ + +/** + + \fn int OGRLayer::FindFieldIndex( const char *, int bExactMatch ); + + \brief Find the index of field in the layer. + + The returned number is the index of the field in the layers, or -1 if the + field doesn't exist. + + If bExactMatch is set to FALSE and the field doesn't exists in the given form + the driver might apply some changes to make it match, like those it might do + if the layer was created (eg. like LAUNDER in the OCI driver). + + This method is the same as the C function OGR_L_FindFieldIndex(). + + @return field index, or -1 if the field doesn't exist + +*/ + +/** + + \fn OGRSpatialReference *OGRLayer::GetSpatialRef(); + + \brief Fetch the spatial reference system for this layer. + + The returned object is owned by the OGRLayer and should not be modified + or freed by the application. + + Starting with OGR 1.11, several geometry fields can be associated to a + feature definition. Each geometry field can have its own spatial reference + system, which is returned by OGRGeomFieldDefn::GetSpatialRef(). + OGRLayer::GetSpatialRef() is equivalent to + GetLayerDefn()->OGRFeatureDefn::GetGeomFieldDefn(0)->GetSpatialRef() + + This method is the same as the C function OGR_L_GetSpatialRef(). + + @return spatial reference, or NULL if there isn't one. + +*/ + + +/** + + \fn OGRSpatialReferenceH OGR_L_GetSpatialRef( OGRLayerH hLayer ); + + \brief Fetch the spatial reference system for this layer. + + The returned object is owned by the OGRLayer and should not be modified + or freed by the application. + + This function is the same as the C++ method OGRLayer::GetSpatialRef(). + + @param hLayer handle to the layer to get the spatial reference from. + @return spatial reference, or NULL if there isn't one. + +*/ + +/** + + \fn OGRFeature *OGRLayer::GetFeature( GIntBig nFID ); + + \brief Fetch a feature by its identifier. + + This function will attempt to read the identified feature. The nFID + value cannot be OGRNullFID. Success or failure of this operation is + unaffected by the spatial or attribute filters (and specialized implementations + in drivers should make sure that they do not take into account spatial or + attribute filters). + + If this method returns a non-NULL feature, it is guaranteed that its + feature id (OGRFeature::GetFID()) will be the same as nFID. + + Use OGRLayer::TestCapability(OLCRandomRead) to establish if this layer + supports efficient random access reading via GetFeature(); however, the + call should always work if the feature exists as a fallback implementation + just scans all the features in the layer looking for the desired feature. + + Sequential reads (with GetNextFeature()) are generally considered interrupted + by a GetFeature() call. + + The returned feature should be free with OGRFeature::DestroyFeature(). + + This method is the same as the C function OGR_L_GetFeature(). + + @param nFID the feature id of the feature to read. + + @return a feature now owned by the caller, or NULL on failure. + +*/ + + +/** + + \fn OGRFeatureH OGR_L_GetFeature( OGRLayerH hLayer, GIntBig nFeatureId ); + + \brief Fetch a feature by its identifier. + + This function will attempt to read the identified feature. The nFID + value cannot be OGRNullFID. Success or failure of this operation is + unaffected by the spatial or attribute filters (and specialized implementations + in drivers should make sure that they do not take into account spatial or + attribute filters). + + If this function returns a non-NULL feature, it is guaranteed that its + feature id (OGR_F_GetFID()) will be the same as nFID. + + Use OGR_L_TestCapability(OLCRandomRead) to establish if this layer + supports efficient random access reading via OGR_L_GetFeature(); however, + the call should always work if the feature exists as a fallback + implementation just scans all the features in the layer looking for the + desired feature. + + Sequential reads (with OGR_L_GetNextFeature()) are generally considered interrupted by a + OGR_L_GetFeature() call. + + The returned feature should be free with OGR_F_Destroy(). + + This function is the same as the C++ method OGRLayer::GetFeature( ). + + @param hLayer handle to the layer that owned the feature. + @param nFeatureId the feature id of the feature to read. + + @return an handle to a feature now owned by the caller, or NULL on failure. + +*/ + +/** + + \fn OGRErr OGRLayer::SetFeature( OGRFeature * poFeature ); + + \brief Rewrite an existing feature. + + This method will write a feature to the layer, based on the feature id + within the OGRFeature. + + Use OGRLayer::TestCapability(OLCRandomWrite) to establish if this layer + supports random access writing via SetFeature(). + + Starting with GDAL 2.0, drivers should specialize the ISetFeature() method, + since SetFeature() is no longer virtual. + + This method is the same as the C function OGR_L_SetFeature(). + + @param poFeature the feature to write. + + @return OGRERR_NONE if the operation works, otherwise an appropriate error + code (e.g OGRERR_NON_EXISTING_FEATURE if the feature does not exist). + +*/ + +/** + + \fn OGRErr OGRLayer::ISetFeature( OGRFeature * poFeature ); + + \brief Rewrite an existing feature. + + This method is implemented by drivers and not called directly. User code should + use SetFeature() instead. + + This method will write a feature to the layer, based on the feature id + within the OGRFeature. + + @param poFeature the feature to write. + + @return OGRERR_NONE if the operation works, otherwise an appropriate error + code (e.g OGRERR_NON_EXISTING_FEATURE if the feature does not exist). + @since GDAL 2.0 + +*/ + +/** + + \fn OGRErr OGR_L_SetFeature( OGRLayerH hLayer, OGRFeatureH hFeat ); + + \brief Rewrite an existing feature. + + This function will write a feature to the layer, based on the feature id + within the OGRFeature. + + Use OGR_L_TestCapability(OLCRandomWrite) to establish if this layer + supports random access writing via OGR_L_SetFeature(). + + This function is the same as the C++ method OGRLayer::SetFeature(). + + @param hLayer handle to the layer to write the feature. + @param hFeat the feature to write. + + @return OGRERR_NONE if the operation works, otherwise an appropriate error + code (e.g OGRERR_NON_EXISTING_FEATURE if the feature does not exist). + +*/ + +/** + + \fn OGRErr OGRLayer::CreateFeature( OGRFeature * poFeature ); + + \brief Create and write a new feature within a layer. + + The passed feature is written to the layer as a new feature, rather than + overwriting an existing one. If the feature has a feature id other than + OGRNullFID, then the native implementation may use that as the feature id + of the new feature, but not necessarily. Upon successful return the + passed feature will have been updated with the new feature id. + + Starting with GDAL 2.0, drivers should specialize the ICreateFeature() method, + since CreateFeature() is no longer virtual. + + This method is the same as the C function OGR_L_CreateFeature(). + + @param poFeature the feature to write to disk. + + @return OGRERR_NONE on success. + +*/ + +/** + + \fn OGRErr OGRLayer::ICreateFeature( OGRFeature * poFeature ); + + \brief Create and write a new feature within a layer. + + This method is implemented by drivers and not called directly. User code should + use CreateFeature() instead. + + The passed feature is written to the layer as a new feature, rather than + overwriting an existing one. If the feature has a feature id other than + OGRNullFID, then the native implementation may use that as the feature id + of the new feature, but not necessarily. Upon successful return the + passed feature will have been updated with the new feature id. + + @param poFeature the feature to write to disk. + + @return OGRERR_NONE on success. + @since GDAL 2.0 + +*/ + +/** + + \fn OGRErr OGR_L_CreateFeature( OGRLayerH hLayer, OGRFeatureH hFeat ); + + \brief Create and write a new feature within a layer. + + The passed feature is written to the layer as a new feature, rather than + overwriting an existing one. If the feature has a feature id other than + OGRNullFID, then the native implementation may use that as the feature id + of the new feature, but not necessarily. Upon successful return the + passed feature will have been updated with the new feature id. + + This function is the same as the C++ method OGRLayer::CreateFeature(). + + @param hLayer handle to the layer to write the feature to. + @param hFeat the handle of the feature to write to disk. + + @return OGRERR_NONE on success. + +*/ + +/** + + \fn OGRErr OGRLayer::DeleteFeature( GIntBig nFID ); + + \brief Delete feature from layer. + + The feature with the indicated feature id is deleted from the layer if + supported by the driver. Most drivers do not support feature deletion, + and will return OGRERR_UNSUPPORTED_OPERATION. The TestCapability() + layer method may be called with OLCDeleteFeature to check if the driver + supports feature deletion. + + This method is the same as the C function OGR_L_DeleteFeature(). + + @param nFID the feature id to be deleted from the layer + + @return OGRERR_NONE if the operation works, otherwise an appropriate error + code (e.g OGRERR_NON_EXISTING_FEATURE if the feature does not exist). + +*/ + +/** + + \fn OGRErr OGR_L_DeleteFeature( OGRLayerH hLayer, GIntBig nFID ); + + \brief Delete feature from layer. + + The feature with the indicated feature id is deleted from the layer if + supported by the driver. Most drivers do not support feature deletion, + and will return OGRERR_UNSUPPORTED_OPERATION. The OGR_L_TestCapability() + function may be called with OLCDeleteFeature to check if the driver + supports feature deletion. + + This method is the same as the C++ method OGRLayer::DeleteFeature(). + + @param hLayer handle to the layer + @param nFID the feature id to be deleted from the layer + + @return OGRERR_NONE if the operation works, otherwise an appropriate error + code (e.g OGRERR_NON_EXISTING_FEATURE if the feature does not exist). + +*/ + + +/** + + \fn int OGRLayer::TestCapability( const char * pszCap ); + + \brief Test if this layer supported the named capability. + + The capability codes that can be tested are represented as strings, but + \#defined constants exists to ensure correct spelling. Specific layer + types may implement class specific capabilities, but this can't generally + be discovered by the caller.

    + +

      + +
    • OLCRandomRead / "RandomRead": TRUE if the GetFeature() method +is implemented in an optimized way for this layer, as opposed to the default +implementation using ResetReading() and GetNextFeature() to find the requested +feature id.

      + +

    • OLCSequentialWrite / "SequentialWrite": TRUE if the +CreateFeature() method works for this layer. Note this means that this +particular layer is writable. The same OGRLayer class may returned FALSE +for other layer instances that are effectively read-only.

      + +

    • OLCRandomWrite / "RandomWrite": TRUE if the SetFeature() method +is operational on this layer. Note this means that this +particular layer is writable. The same OGRLayer class may returned FALSE +for other layer instances that are effectively read-only.

      + +

    • OLCFastSpatialFilter / "FastSpatialFilter": TRUE if this layer +implements spatial filtering efficiently. Layers that effectively read all +features, and test them with the OGRFeature intersection methods should +return FALSE. This can be used as a clue by the application whether it +should build and maintain its own spatial index for features in this layer.

      + +

    • OLCFastFeatureCount / "FastFeatureCount": +TRUE if this layer can return a feature +count (via GetFeatureCount()) efficiently ... ie. without counting +the features. In some cases this will return TRUE until a spatial filter is +installed after which it will return FALSE.

      + +

    • OLCFastGetExtent / "FastGetExtent": +TRUE if this layer can return its data extent (via GetExtent()) +efficiently ... ie. without scanning all the features. In some cases this +will return TRUE until a spatial filter is installed after which it will +return FALSE.

      + +

    • OLCFastSetNextByIndex / "FastSetNextByIndex": +TRUE if this layer can perform the SetNextByIndex() call efficiently, otherwise +FALSE.

      + +

    • OLCCreateField / "CreateField": TRUE if this layer can create +new fields on the current layer using CreateField(), otherwise FALSE.

      + +

    • OLCCreateGeomField / "CreateGeomField": (GDAL >= 1.11) TRUE if this layer can create +new geometry fields on the current layer using CreateGeomField(), otherwise FALSE.

      + +

    • OLCDeleteField / "DeleteField": TRUE if this layer can delete +existing fields on the current layer using DeleteField(), otherwise FALSE.

      + +

    • OLCReorderFields / "ReorderFields": TRUE if this layer can reorder +existing fields on the current layer using ReorderField() or ReorderFields(), otherwise FALSE.

      + +

    • OLCAlterFieldDefn / "AlterFieldDefn": TRUE if this layer can alter +the definition of an existing field on the current layer using AlterFieldDefn(), otherwise FALSE.

      + +

    • OLCDeleteFeature / "DeleteFeature": TRUE if the DeleteFeature() +method is supported on this layer, otherwise FALSE.

      + +

    • OLCStringsAsUTF8 / "StringsAsUTF8": TRUE if values of OFTString +fields are assured to be in UTF-8 format. If FALSE the encoding of fields +is uncertain, though it might still be UTF-8.

      + +

    • OLCTransactions / "Transactions": TRUE if the StartTransaction(), +CommitTransaction() and RollbackTransaction() methods work in a meaningful way, +otherwise FALSE.

      + +

    • OLCIgnoreFields / "IgnoreFields": TRUE if fields, geometry and style +will be omitted when fetching features as set by SetIgnoredFields() method. + +
    • OLCCurveGeometries / "CurveGeometries": TRUE if this layer supports +writing curve geometries or may return such geometries. (GDAL 2.0). + +

      + +

    + + This method is the same as the C function OGR_L_TestCapability(). + + @param pszCap the name of the capability to test. + + @return TRUE if the layer has the requested capability, or FALSE otherwise. +OGRLayers will return FALSE for any unrecognised capabilities.

    + +*/ + + +/** + + \fn int OGR_L_TestCapability( OGRLayerH hLayer, const char *pszCap ); + + \brief Test if this layer supported the named capability. + + The capability codes that can be tested are represented as strings, but + \#defined constants exists to ensure correct spelling. Specific layer + types may implement class specific capabilities, but this can't generally + be discovered by the caller.

    + +

      + +
    • OLCRandomRead / "RandomRead": TRUE if the GetFeature() method +is implemented in an optimized way for this layer, as opposed to the default +implementation using ResetReading() and GetNextFeature() to find the requested +feature id.

      + +

    • OLCSequentialWrite / "SequentialWrite": TRUE if the +CreateFeature() method works for this layer. Note this means that this +particular layer is writable. The same OGRLayer class may returned FALSE +for other layer instances that are effectively read-only.

      + +

    • OLCRandomWrite / "RandomWrite": TRUE if the SetFeature() method +is operational on this layer. Note this means that this +particular layer is writable. The same OGRLayer class may returned FALSE +for other layer instances that are effectively read-only.

      + +

    • OLCFastSpatialFilter / "FastSpatialFilter": TRUE if this layer +implements spatial filtering efficiently. Layers that effectively read all +features, and test them with the OGRFeature intersection methods should +return FALSE. This can be used as a clue by the application whether it +should build and maintain its own spatial index for features in this +layer.

      + +

    • OLCFastFeatureCount / "FastFeatureCount": +TRUE if this layer can return a feature +count (via OGR_L_GetFeatureCount()) efficiently ... ie. without counting +the features. In some cases this will return TRUE until a spatial filter is +installed after which it will return FALSE.

      + +

    • OLCFastGetExtent / "FastGetExtent": +TRUE if this layer can return its data extent (via OGR_L_GetExtent()) +efficiently ... ie. without scanning all the features. In some cases this +will return TRUE until a spatial filter is installed after which it will +return FALSE.

      + +

    • OLCFastSetNextByIndex / "FastSetNextByIndex": +TRUE if this layer can perform the SetNextByIndex() call efficiently, otherwise +FALSE.

      + +

    • OLCCreateField / "CreateField": TRUE if this layer can create +new fields on the current layer using CreateField(), otherwise FALSE.

      + +

    • OLCCreateGeomField / "CreateGeomField": (GDAL >= 1.11) TRUE if this layer can create +new geometry fields on the current layer using CreateGeomField(), otherwise FALSE.

      + +

    • OLCDeleteField / "DeleteField": TRUE if this layer can delete +existing fields on the current layer using DeleteField(), otherwise FALSE.

      + +

    • OLCReorderFields / "ReorderFields": TRUE if this layer can reorder +existing fields on the current layer using ReorderField() or ReorderFields(), otherwise FALSE.

      + +

    • OLCAlterFieldDefn / "AlterFieldDefn": TRUE if this layer can alter +the definition of an existing field on the current layer using AlterFieldDefn(), otherwise FALSE.

      + +

    • OLCDeleteFeature / "DeleteFeature": TRUE if the DeleteFeature() +method is supported on this layer, otherwise FALSE.

      + +

    • OLCStringsAsUTF8 / "StringsAsUTF8": TRUE if values of OFTString +fields are assured to be in UTF-8 format. If FALSE the encoding of fields +is uncertain, though it might still be UTF-8.

      + +

    • OLCTransactions / "Transactions": TRUE if the StartTransaction(), +CommitTransaction() and RollbackTransaction() methods work in a meaningful way, +otherwise FALSE.

      + +

    • OLCCurveGeometries / "CurveGeometries": TRUE if this layer supports +writing curve geometries or may return such geometries. (GDAL 2.0). + +

      + +

    + + This function is the same as the C++ method OGRLayer::TestCapability(). + + @param hLayer handle to the layer to get the capability from. + @param pszCap the name of the capability to test. + + @return TRUE if the layer has the requested capability, or FALSE otherwise. +OGRLayers will return FALSE for any unrecognised capabilities.

    + +*/ + +/** + \fn OGRErr OGRLayer::SyncToDisk(); + +\brief Flush pending changes to disk. + +This call is intended to force the layer to flush any pending writes to +disk, and leave the disk file in a consistent state. It would not normally +have any effect on read-only datasources. + +Some layers do not implement this method, and will still return +OGRERR_NONE. The default implementation just returns OGRERR_NONE. An error +is only returned if an error occurs while attempting to flush to disk. + +In any event, you should always close any opened datasource with +OGRDataSource::DestroyDataSource() that will ensure all data is correctly flushed. + +This method is the same as the C function OGR_L_SyncToDisk(). + +@return OGRERR_NONE if no error occurs (even if nothing is done) or an +error code. +*/ + +/** + \fn OGRErr OGR_L_SyncToDisk(OGRLayerH hLayer); + +\brief Flush pending changes to disk. + +This call is intended to force the layer to flush any pending writes to +disk, and leave the disk file in a consistent state. It would not normally +have any effect on read-only datasources. + +Some layers do not implement this method, and will still return +OGRERR_NONE. The default implementation just returns OGRERR_NONE. An error +is only returned if an error occurs while attempting to flush to disk. + +In any event, you should always close any opened datasource with +OGR_DS_Destroy() that will ensure all data is correctly flushed. + +This method is the same as the C++ method OGRLayer::SyncToDisk() + +@param hLayer handle to the layer + +@return OGRERR_NONE if no error occurs (even if nothing is done) or an +error code. +*/ + +/** + \fn OGRErr OGRLayer::SetNextByIndex( GIntBig nIndex ); + + \brief Move read cursor to the nIndex'th feature in the current resultset. + + This method allows positioning of a layer such that the GetNextFeature() + call will read the requested feature, where nIndex is an absolute index + into the current result set. So, setting it to 3 would mean the next + feature read with GetNextFeature() would have been the 4th feature to have + been read if sequential reading took place from the beginning of the layer, + including accounting for spatial and attribute filters. + + Only in rare circumstances is SetNextByIndex() efficiently implemented. + In all other cases the default implementation which calls ResetReading() + and then calls GetNextFeature() nIndex times is used. To determine if + fast seeking is available on the current layer use the TestCapability() + method with a value of OLCFastSetNextByIndex. + + This method is the same as the C function OGR_L_SetNextByIndex(). + + @param nIndex the index indicating how many steps into the result set + to seek. + + @return OGRERR_NONE on success or an error code. + +*/ + + +/** + \fn OGRErr OGR_L_SetNextByIndex( OGRLayerH hLayer, GIntBig nIndex ); + + \brief Move read cursor to the nIndex'th feature in the current resultset. + + This method allows positioning of a layer such that the GetNextFeature() + call will read the requested feature, where nIndex is an absolute index + into the current result set. So, setting it to 3 would mean the next + feature read with GetNextFeature() would have been the 4th feature to have + been read if sequential reading took place from the beginning of the layer, + including accounting for spatial and attribute filters. + + Only in rare circumstances is SetNextByIndex() efficiently implemented. + In all other cases the default implementation which calls ResetReading() + and then calls GetNextFeature() nIndex times is used. To determine if + fast seeking is available on the current layer use the TestCapability() + method with a value of OLCFastSetNextByIndex. + + This method is the same as the C++ method OGRLayer::SetNextByIndex() + + @param hLayer handle to the layer + @param nIndex the index indicating how many steps into the result set + to seek. + + @return OGRERR_NONE on success or an error code. + +*/ + +/** + \fn int OGRLayer::Reference(); + +\brief Increment layer reference count. + +This method is the same as the C function OGR_L_Reference(). + +@return the reference count after incrementing. +*/ + +/** + \fn int OGRLayer::Dereference(); + +\brief Decrement layer reference count. + +This method is the same as the C function OGR_L_Dereference(). + +@return the reference count after decrementing. +*/ + +/** + \fn int OGRLayer::GetRefCount() const; + +\brief Fetch reference count. + +This method is the same as the C function OGR_L_GetRefCount(). + +@return the current reference count for the layer object itself. +*/ + +/** +\fn OGRErr OGRLayer::CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + +\brief Create a new field on a layer. + +You must use this to create new fields +on a real layer. Internally the OGRFeatureDefn for the layer will be updated +to reflect the new field. Applications should never modify the OGRFeatureDefn +used by a layer directly. + +This method should not be called while there are feature objects in existance that +were obtained or created with the previous layer definition. + +Not all drivers support this method. You can query a layer to check if it supports it +with the OLCCreateField capability. Some drivers may only support this method while +there are still no features in the layer. When it is supported, the existings features of the +backing file/database should be updated accordingly. + +Drivers may or may not support not-null constraints. If they support creating +fields with not-null constraints, this is generally before creating any feature to the layer. + +This function is the same as the C function OGR_L_CreateField(). + +@param poField field definition to write to disk. +@param bApproxOK If TRUE, the field may be created in a slightly different +form depending on the limitations of the format driver. + +@return OGRERR_NONE on success. +*/ + +/** + + \fn OGRErr OGR_L_CreateField( OGRLayerH hLayer, OGRFieldDefnH hField, + int bApproxOK ); + +\brief Create a new field on a layer. + +You must use this to create new fields +on a real layer. Internally the OGRFeatureDefn for the layer will be updated +to reflect the new field. Applications should never modify the OGRFeatureDefn +used by a layer directly. + +This function should not be called while there are feature objects in existance that +were obtained or created with the previous layer definition. + +Not all drivers support this function. You can query a layer to check if it supports it +with the OLCCreateField capability. Some drivers may only support this method while +there are still no features in the layer. When it is supported, the existings features of the +backing file/database should be updated accordingly. + +Drivers may or may not support not-null constraints. If they support creating +fields with not-null constraints, this is generally before creating any feature to the layer. + + This function is the same as the C++ method OGRLayer::CreateField(). + + @param hLayer handle to the layer to write the field definition. + @param hField handle of the field definition to write to disk. + @param bApproxOK If TRUE, the field may be created in a slightly different +form depending on the limitations of the format driver. + + @return OGRERR_NONE on success. + +*/ + +/** +\fn OGRErr OGRLayer::DeleteField( int iField ); + +\brief Delete an existing field on a layer. + +You must use this to delete existing fields +on a real layer. Internally the OGRFeatureDefn for the layer will be updated +to reflect the deleted field. Applications should never modify the OGRFeatureDefn +used by a layer directly. + +This method should not be called while there are feature objects in existance that +were obtained or created with the previous layer definition. + +Not all drivers support this method. You can query a layer to check if it supports it +with the OLCDeleteField capability. Some drivers may only support this method while +there are still no features in the layer. When it is supported, the existings features of the +backing file/database should be updated accordingly. + +This function is the same as the C function OGR_L_DeleteField(). + +@param iField index of the field to delete. + +@return OGRERR_NONE on success. + +@since OGR 1.9.0 +*/ + +/** + +\fn OGRErr OGR_L_DeleteField( OGRLayerH hLayer, int iField); + +\brief Create a new field on a layer. + +You must use this to delete existing fields +on a real layer. Internally the OGRFeatureDefn for the layer will be updated +to reflect the deleted field. Applications should never modify the OGRFeatureDefn +used by a layer directly. + +This function should not be called while there are feature objects in existance that +were obtained or created with the previous layer definition. + +Not all drivers support this function. You can query a layer to check if it supports it +with the OLCDeleteField capability. Some drivers may only support this method while +there are still no features in the layer. When it is supported, the existings features of the +backing file/database should be updated accordingly. + +This function is the same as the C++ method OGRLayer::DeleteField(). + +@param hLayer handle to the layer. +@param iField index of the field to delete. + +@return OGRERR_NONE on success. + +@since OGR 1.9.0 +*/ + +/** +\fn OGRErr OGRLayer::ReorderFields( int* panMap ); + +\brief Reorder all the fields of a layer. + +You must use this to reorder existing fields +on a real layer. Internally the OGRFeatureDefn for the layer will be updated +to reflect the reordering of the fields. Applications should never modify the OGRFeatureDefn +used by a layer directly. + +This method should not be called while there are feature objects in existance that +were obtained or created with the previous layer definition. + +panMap is such that,for each field definition at position i after reordering, +its position before reordering was panMap[i]. + +For example, let suppose the fields were "0","1","2","3","4" initially. +ReorderFields([0,2,3,1,4]) will reorder them as "0","2","3","1","4". + +Not all drivers support this method. You can query a layer to check if it supports it +with the OLCReorderFields capability. Some drivers may only support this method while +there are still no features in the layer. When it is supported, the existings features of the +backing file/database should be updated accordingly. + +This function is the same as the C function OGR_L_ReorderFields(). + +@param panMap an array of GetLayerDefn()->OGRFeatureDefn::GetFieldCount() elements which +is a permutation of [0, GetLayerDefn()->OGRFeatureDefn::GetFieldCount()-1]. + +@return OGRERR_NONE on success. + +@since OGR 1.9.0 +*/ + +/** + +\fn OGRErr OGR_L_ReorderFields( OGRLayerH hLayer, int* panMap ); + +\brief Reorder all the fields of a layer. + +You must use this to reorder existing fields +on a real layer. Internally the OGRFeatureDefn for the layer will be updated +to reflect the reordering of the fields. Applications should never modify the OGRFeatureDefn +used by a layer directly. + +This function should not be called while there are feature objects in existance that +were obtained or created with the previous layer definition. + +panMap is such that,for each field definition at position i after reordering, +its position before reordering was panMap[i]. + +For example, let suppose the fields were "0","1","2","3","4" initially. +ReorderFields([0,2,3,1,4]) will reorder them as "0","2","3","1","4". + +Not all drivers support this function. You can query a layer to check if it supports it +with the OLCReorderFields capability. Some drivers may only support this method while +there are still no features in the layer. When it is supported, the existings features of the +backing file/database should be updated accordingly. + +This function is the same as the C++ method OGRLayer::ReorderFields(). + +@param hLayer handle to the layer. +@param panMap an array of GetLayerDefn()->OGRFeatureDefn::GetFieldCount() elements which +is a permutation of [0, GetLayerDefn()->OGRFeatureDefn::GetFieldCount()-1]. + +@return OGRERR_NONE on success. + +@since OGR 1.9.0 +*/ + +/** +\fn OGRErr OGRLayer::ReorderField( int iOldFieldPos, int iNewFieldPos ); + +\brief Reorder an existing field on a layer. + +This method is a conveniency wrapper of ReorderFields() dedicated to move a single field. +It is a non-virtual method, so drivers should implement ReorderFields() instead. + +You must use this to reorder existing fields +on a real layer. Internally the OGRFeatureDefn for the layer will be updated +to reflect the reordering of the fields. Applications should never modify the OGRFeatureDefn +used by a layer directly. + +This method should not be called while there are feature objects in existance that +were obtained or created with the previous layer definition. + +The field definition that was at initial position iOldFieldPos will be moved at +position iNewFieldPos, and elements between will be shuffled accordingly. + +For example, let suppose the fields were "0","1","2","3","4" initially. +ReorderField(1, 3) will reorder them as "0","2","3","1","4". + +Not all drivers support this method. You can query a layer to check if it supports it +with the OLCReorderFields capability. Some drivers may only support this method while +there are still no features in the layer. When it is supported, the existings features of the +backing file/database should be updated accordingly. + +This function is the same as the C function OGR_L_ReorderField(). + +@param iOldFieldPos previous position of the field to move. Must be in the range [0,GetFieldCount()-1]. +@param iNewFieldPos new position of the field to move. Must be in the range [0,GetFieldCount()-1]. + +@return OGRERR_NONE on success. + +@since OGR 1.9.0 +*/ + +/** + +\fn OGRErr OGR_L_ReorderField( OGRLayerH hLayer, int iOldFieldPos, int iNewFieldPos ); + +\brief Reorder an existing field on a layer. + +This function is a conveniency wrapper of OGR_L_ReorderFields() dedicated to move a single field. + +You must use this to reorder existing fields +on a real layer. Internally the OGRFeatureDefn for the layer will be updated +to reflect the reordering of the fields. Applications should never modify the OGRFeatureDefn +used by a layer directly. + +This function should not be called while there are feature objects in existance that +were obtained or created with the previous layer definition. + +The field definition that was at initial position iOldFieldPos will be moved at +position iNewFieldPos, and elements between will be shuffled accordingly. + +For example, let suppose the fields were "0","1","2","3","4" initially. +ReorderField(1, 3) will reorder them as "0","2","3","1","4". + +Not all drivers support this function. You can query a layer to check if it supports it +with the OLCReorderFields capability. Some drivers may only support this method while +there are still no features in the layer. When it is supported, the existings features of the +backing file/database should be updated accordingly. + +This function is the same as the C++ method OGRLayer::ReorderField(). + +@param hLayer handle to the layer. +@param iOldFieldPos previous position of the field to move. Must be in the range [0,GetFieldCount()-1]. +@param iNewFieldPos new position of the field to move. Must be in the range [0,GetFieldCount()-1]. + +@return OGRERR_NONE on success. + +@since OGR 1.9.0 +*/ + +/** +\fn OGRErr OGRLayer::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + +\brief Alter the definition of an existing field on a layer. + +You must use this to alter the definition of an existing field of a real layer. +Internally the OGRFeatureDefn for the layer will be updated +to reflect the altered field. Applications should never modify the OGRFeatureDefn +used by a layer directly. + +This method should not be called while there are feature objects in existance that +were obtained or created with the previous layer definition. + +Not all drivers support this method. You can query a layer to check if it supports it +with the OLCAlterFieldDefn capability. Some drivers may only support this method while +there are still no features in the layer. When it is supported, the existings features of the +backing file/database should be updated accordingly. Some drivers might also not support +all update flags. + +This function is the same as the C function OGR_L_AlterFieldDefn(). + +@param iField index of the field whose definition must be altered. +@param poNewFieldDefn new field definition +@param nFlags combination of ALTER_NAME_FLAG, ALTER_TYPE_FLAG, ALTER_WIDTH_PRECISION_FLAG, +ALTER_NULLABLE_FLAG and ALTER_DEFAULT_FLAG +to indicate which of the name and/or type and/or width and precision fields and/or nullability from the new field +definition must be taken into account. + +@return OGRERR_NONE on success. + +@since OGR 1.9.0 +*/ + +/** +\fn OGRErr OGR_L_AlterFieldDefn( OGRLayerH hLayer, int iField, OGRFieldDefnH hNewFieldDefn, int nFlags ) + +\brief Alter the definition of an existing field on a layer. + +You must use this to alter the definition of an existing field of a real layer. +Internally the OGRFeatureDefn for the layer will be updated +to reflect the altered field. Applications should never modify the OGRFeatureDefn +used by a layer directly. + +This function should not be called while there are feature objects in existance that +were obtained or created with the previous layer definition. + +Not all drivers support this function. You can query a layer to check if it supports it +with the OLCAlterFieldDefn capability. Some drivers may only support this method while +there are still no features in the layer. When it is supported, the existings features of the +backing file/database should be updated accordingly. Some drivers might also not support +all update flags. + +This function is the same as the C++ method OGRLayer::AlterFieldDefn(). + +@param hLayer handle to the layer. +@param iField index of the field whose definition must be altered. +@param hNewFieldDefn new field definition +@param nFlags combination of ALTER_NAME_FLAG, ALTER_TYPE_FLAG, ALTER_WIDTH_PRECISION_FLAG, +ALTER_NULLABLE_FLAG and ALTER_DEFAULT_FLAG +to indicate which of the name and/or type and/or width and precision fields and/or nullability from the new field +definition must be taken into account. + +@return OGRERR_NONE on success. + +@since OGR 1.9.0 +*/ + + +/** +\fn OGRErr OGRLayer::CreateGeomField( OGRGeomFieldDefn *poField, + int bApproxOK = TRUE ); + +\brief Create a new geometry field on a layer. + +You must use this to create new geometry fields +on a real layer. Internally the OGRFeatureDefn for the layer will be updated +to reflect the new field. Applications should never modify the OGRFeatureDefn +used by a layer directly. + +This method should not be called while there are feature objects in existance that +were obtained or created with the previous layer definition. + +Not all drivers support this method. You can query a layer to check if it supports it +with the OLCCreateGeomField capability. Some drivers may only support this method while +there are still no features in the layer. When it is supported, the existings features of the +backing file/database should be updated accordingly. + +Drivers may or may not support not-null constraints. If they support creating +fields with not-null constraints, this is generally before creating any feature to the layer. + +This function is the same as the C function OGR_L_CreateGeomField(). + +@param poField geometry field definition to write to disk. +@param bApproxOK If TRUE, the field may be created in a slightly different +form depending on the limitations of the format driver. + +@return OGRERR_NONE on success. + +@since OGR 1.11 +*/ + +/** + + \fn OGRErr OGR_L_CreateGeomField( OGRLayerH hLayer, OGRGeomFieldDefnH hField, + int bApproxOK ); + +\brief Create a new geometry field on a layer. + +You must use this to create new geometry fields +on a real layer. Internally the OGRFeatureDefn for the layer will be updated +to reflect the new field. Applications should never modify the OGRFeatureDefn +used by a layer directly. + +This function should not be called while there are feature objects in existance that +were obtained or created with the previous layer definition. + +Not all drivers support this function. You can query a layer to check if it supports it +with the OLCCreateField capability. Some drivers may only support this method while +there are still no features in the layer. When it is supported, the existings features of the +backing file/database should be updated accordingly. + +Drivers may or may not support not-null constraints. If they support creating +fields with not-null constraints, this is generally before creating any feature to the layer. + + This function is the same as the C++ method OGRLayer::CreateField(). + + @param hLayer handle to the layer to write the field definition. + @param hField handle of the geometry field definition to write to disk. + @param bApproxOK If TRUE, the field may be created in a slightly different +form depending on the limitations of the format driver. + + @return OGRERR_NONE on success. + + @since OGR 1.11 +*/ + +/** + \fn void OGRLayer::GetStyleTable(); + + \brief Returns layer style table. + + This method is the same as the C function OGR_L_GetStyleTable(). + + @return pointer to a style table which should not be modified or freed by the + caller. +*/ + +/** + \fn void OGRLayer::SetStyleTable(OGRStyleTable *poStyleTable); + + \brief Set layer style table. + + This method operate exactly as OGRLayer::SetStyleTableDirectly() except + that it does not assume ownership of the passed table. + + This method is the same as the C function OGR_L_SetStyleTable(). + + @param poStyleTable pointer to style table to set + +*/ + +/** + \fn void OGRLayer::SetStyleTableDirectly(OGRStyleTable *poStyleTable); + + \brief Set layer style table. + + This method operate exactly as OGRLayer::SetStyleTable() except that it + assumes ownership of the passed table. + + This method is the same as the C function OGR_L_SetStyleTableDirectly(). + + @param poStyleTable pointer to style table to set + +*/ + +/** + + \fn OGRErr OGR_L_StartTransaction( OGRLayerH hLayer ); + + \brief For datasources which support transactions, StartTransaction creates a transaction. + + If starting the transaction fails, will return + OGRERR_FAILURE. Datasources which do not support transactions will + always return OGRERR_NONE. + + Note: as of GDAL 2.0, use of this API is discouraged when the dataset offers + dataset level transaction with GDALDataset::StartTransaction(). The reason is + that most drivers can only offer transactions at dataset level, and not layer level. + Very few drivers really support transactions at layer scope. + + This function is the same as the C++ method OGRLayer::StartTransaction(). + + @param hLayer handle to the layer + + @return OGRERR_NONE on success. + +*/ + +/** + + \fn OGRErr OGR_L_CommitTransaction( OGRLayerH hLayer ); + + \brief For datasources which support transactions, CommitTransaction commits a transaction. + + If no transaction is active, or the commit fails, will return + OGRERR_FAILURE. Datasources which do not support transactions will + always return OGRERR_NONE. + + This function is the same as the C++ method OGRLayer::CommitTransaction(). + + @param hLayer handle to the layer + + @return OGRERR_NONE on success. + +*/ + +/** + + \fn OGRErr OGR_L_RollbackTransaction( OGRLayerH hLayer ); + + \brief For datasources which support transactions, RollbackTransaction will roll back a datasource to its state before the start of the current transaction. + If no transaction is active, or the rollback fails, will return + OGRERR_FAILURE. Datasources which do not support transactions will + always return OGRERR_NONE. + + This function is the same as the C++ method OGRLayer::RollbackTransaction(). + + @param hLayer handle to the layer + + @return OGRERR_NONE on success. + +*/ + +/** + \fn const char *OGRLayer::GetFIDColumn(); + + \brief This method returns the name of the underlying database column being used as the FID column, or "" if not supported. + + This method is the same as the C function OGR_L_GetFIDColumn(). + + @return fid column name. + + */ + +/** + \fn const char* OGR_L_GetFIDColumn(OGRLayerH hLayer); + + \brief This method returns the name of the underlying database column being used as the FID column, or "" if not supported. + + This method is the same as the C++ method OGRLayer::GetFIDColumn() + + @param hLayer handle to the layer + @return fid column name. + + */ + +/** + \fn const char *OGRLayer::GetGeometryColumn(); + + \brief This method returns the name of the underlying database column being used as the geometry column, or "" if not supported. + + For layers with multiple geometry fields, this method only returns the name + of the first geometry column. For other columns, use + GetLayerDefn()->OGRFeatureDefn::GetGeomFieldDefn(i)->GetNameRef(). + + This method is the same as the C function OGR_L_GetGeometryColumn(). + + @return geometry column name. + + */ + +/** + \fn const char* OGR_L_GetGeometryColumn(OGRLayerH hLayer); + + \brief This method returns the name of the underlying database column being used as the geometry column, or "" if not supported. + + For layers with multiple geometry fields, this method only returns the geometry + type of the first geometry column. For other columns, use + OGR_GFld_GetNameRef(OGR_FD_GetGeomFieldDefn(OGR_L_GetLayerDefn(hLayer), i)). + + This method is the same as the C++ method OGRLayer::GetGeometryColumn() + + @param hLayer handle to the layer + @return geometry column name. + + */ + +/** + \fn OGRErr OGRLayer::SetIgnoredFields( const char **papszFields ); + + \brief Set which fields can be omitted when retrieving features from the layer. + + If the driver supports this functionality (testable using OLCIgnoreFields capability), it will not fetch the specified fields + in subsequent calls to GetFeature() / GetNextFeature() and thus save some processing time and/or bandwidth. + + Besides field names of the layers, the following special fields can be passed: "OGR_GEOMETRY" to ignore geometry and + "OGR_STYLE" to ignore layer style. + + By default, no fields are ignored. + + This method is the same as the C function OGR_L_SetIgnoredFields() + + @param papszFields an array of field names terminated by NULL item. If NULL is passed, the ignored list is cleared. + @return OGRERR_NONE if all field names have been resolved (even if the driver does not support this method) + + */ + +/** + \fn OGRErr OGR_L_SetIgnoredFields( OGRLayerH, const char** papszFields); + + \brief Set which fields can be omitted when retrieving features from the layer. + + If the driver supports this functionality (testable using OLCIgnoreFields capability), it will not fetch the specified fields + in subsequent calls to GetFeature() / GetNextFeature() and thus save some processing time and/or bandwidth. + + Besides field names of the layers, the following special fields can be passed: "OGR_GEOMETRY" to ignore geometry and + "OGR_STYLE" to ignore layer style. + + By default, no fields are ignored. + + This method is the same as the C++ method OGRLayer::SetIgnoredFields() + + @param papszFields an array of field names terminated by NULL item. If NULL is passed, the ignored list is cleared. + @return OGRERR_NONE if all field names have been resolved (even if the driver does not support this method) + + */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogrsf_frmts.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogrsf_frmts.h new file mode 100644 index 000000000..8a00307e6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/ogrsf_frmts.h @@ -0,0 +1,425 @@ +/****************************************************************************** + * $Id: ogrsf_frmts.h 29035 2015-04-27 12:38:54Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Classes related to format registration, and file opening. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRSF_FRMTS_H_INCLUDED +#define _OGRSF_FRMTS_H_INCLUDED + +#include "cpl_progress.h" +#include "ogr_feature.h" +#include "ogr_featurestyle.h" +#include "gdal_priv.h" + +/** + * \file ogrsf_frmts.h + * + * Classes related to registration of format support, and opening datasets. + */ + +#if !defined(GDAL_COMPILATION) && !defined(SUPPRESS_DEPRECATION_WARNINGS) +#define OGR_DEPRECATED(x) CPL_WARN_DEPRECATED(x) +#else +#define OGR_DEPRECATED(x) +#endif + +class OGRLayerAttrIndex; +class OGRSFDriver; + +/************************************************************************/ +/* OGRLayer */ +/************************************************************************/ + +/** + * This class represents a layer of simple features, with access methods. + * + */ + +/* Note: any virtual method added to this class must also be added in the */ +/* OGRLayerDecorator and OGRMutexedLayer classes. */ + +class CPL_DLL OGRLayer : public GDALMajorObject +{ + private: + void ConvertNonLinearGeomsIfNecessary( OGRFeature *poFeature ); + + protected: + int m_bFilterIsEnvelope; + OGRGeometry *m_poFilterGeom; + OGRPreparedGeometry *m_pPreparedFilterGeom; /* m_poFilterGeom compiled as a prepared geometry */ + OGREnvelope m_sFilterEnvelope; + int m_iGeomFieldFilter; // specify the index on which the spatial + // filter is active. + + int FilterGeometry( OGRGeometry * ); + //int FilterGeometry( OGRGeometry *, OGREnvelope* psGeometryEnvelope); + int InstallFilter( OGRGeometry * ); + + OGRErr GetExtentInternal(int iGeomField, OGREnvelope *psExtent, int bForce ); + + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + + public: + OGRLayer(); + virtual ~OGRLayer(); + + virtual OGRGeometry *GetSpatialFilter(); + virtual void SetSpatialFilter( OGRGeometry * ); + virtual void SetSpatialFilterRect( double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + virtual void SetSpatialFilterRect( int iGeomField, + double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual void ResetReading() = 0; + virtual OGRFeature *GetNextFeature() = 0; + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + virtual OGRFeature *GetFeature( GIntBig nFID ); + + OGRErr SetFeature( OGRFeature *poFeature ); + OGRErr CreateFeature( OGRFeature *poFeature ); + + virtual OGRErr DeleteFeature( GIntBig nFID ); + + virtual const char *GetName(); + virtual OGRwkbGeometryType GetGeomType(); + virtual OGRFeatureDefn *GetLayerDefn() = 0; + virtual int FindFieldIndex( const char *pszFieldName, int bExactMatch ); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, + int bForce = TRUE); + + virtual int TestCapability( const char * ) = 0; + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr DeleteField( int iField ); + virtual OGRErr ReorderFields( int* panMap ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlagsIn ); + + virtual OGRErr CreateGeomField( OGRGeomFieldDefn *poField, + int bApproxOK = TRUE ); + + virtual OGRErr SyncToDisk(); + + virtual OGRStyleTable *GetStyleTable(); + virtual void SetStyleTableDirectly( OGRStyleTable *poStyleTable ); + + virtual void SetStyleTable(OGRStyleTable *poStyleTable); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual const char *GetFIDColumn(); + virtual const char *GetGeometryColumn(); + + virtual OGRErr SetIgnoredFields( const char **papszFields ); + + OGRErr Intersection( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions = NULL, + GDALProgressFunc pfnProgress = NULL, + void * pProgressArg = NULL ); + OGRErr Union( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions = NULL, + GDALProgressFunc pfnProgress = NULL, + void * pProgressArg = NULL ); + OGRErr SymDifference( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions, + GDALProgressFunc pfnProgress, + void * pProgressArg ); + OGRErr Identity( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions = NULL, + GDALProgressFunc pfnProgress = NULL, + void * pProgressArg = NULL ); + OGRErr Update( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions = NULL, + GDALProgressFunc pfnProgress = NULL, + void * pProgressArg = NULL ); + OGRErr Clip( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions = NULL, + GDALProgressFunc pfnProgress = NULL, + void * pProgressArg = NULL ); + OGRErr Erase( OGRLayer *pLayerMethod, + OGRLayer *pLayerResult, + char** papszOptions = NULL, + GDALProgressFunc pfnProgress = NULL, + void * pProgressArg = NULL ); + + int Reference(); + int Dereference(); + int GetRefCount() const; + + GIntBig GetFeaturesRead(); + + /* non virtual : conveniency wrapper for ReorderFields() */ + OGRErr ReorderField( int iOldFieldPos, int iNewFieldPos ); + + int AttributeFilterEvaluationNeedsGeometry(); + + /* consider these private */ + OGRErr InitializeIndexSupport( const char * ); + OGRLayerAttrIndex *GetIndex() { return m_poAttrIndex; } + + protected: + OGRStyleTable *m_poStyleTable; + OGRFeatureQuery *m_poAttrQuery; + char *m_pszAttrQueryString; + OGRLayerAttrIndex *m_poAttrIndex; + + int m_nRefCount; + + GIntBig m_nFeaturesRead; +}; + +/************************************************************************/ +/* OGRDataSource */ +/************************************************************************/ + +/** + * LEGACY class. Use GDALDataset in your new code ! This class may be + * removed in a later release. + * + * This class represents a data source. A data source potentially + * consists of many layers (OGRLayer). A data source normally consists + * of one, or a related set of files, though the name doesn't have to be + * a real item in the file system. + * + * When an OGRDataSource is destroyed, all it's associated OGRLayers objects + * are also destroyed. + * + * NOTE: Starting with GDAL 2.0, it is *NOT* safe to cast the handle of + * a C function that returns a OGRDataSourceH to a OGRDataSource*. If a C++ object + * is needed, the handle should be cast to GDALDataset*. + * + * @deprecated + */ + +class CPL_DLL OGRDataSource : public GDALDataset +{ +public: + OGRDataSource(); + + virtual const char *GetName() OGR_DEPRECATED("Use GDALDataset class instead") = 0; + + static void DestroyDataSource( OGRDataSource * ) OGR_DEPRECATED("Use GDALDataset class instead"); +}; + +/************************************************************************/ +/* OGRSFDriver */ +/************************************************************************/ + +/** + * LEGACY class. Use GDALDriver in your new code ! This class may be + * removed in a later release. + * + * Represents an operational format driver. + * + * One OGRSFDriver derived class will normally exist for each file format + * registered for use, regardless of whether a file has or will be opened. + * The list of available drivers is normally managed by the + * OGRSFDriverRegistrar. + * + * NOTE: Starting with GDAL 2.0, it is *NOT* safe to cast the handle of + * a C function that returns a OGRSFDriverH to a OGRSFDriver*. If a C++ object + * is needed, the handle should be cast to GDALDriver*. + * + * @deprecated + */ + +class CPL_DLL OGRSFDriver : public GDALDriver +{ + public: + virtual ~OGRSFDriver(); + + virtual const char *GetName() OGR_DEPRECATED("Use GDALDriver class instead") = 0; + + virtual OGRDataSource *Open( const char *pszName, int bUpdate=FALSE ) OGR_DEPRECATED("Use GDALDriver class instead") = 0; + + virtual int TestCapability( const char *pszCap ) OGR_DEPRECATED("Use GDALDriver class instead") = 0; + + virtual OGRDataSource *CreateDataSource( const char *pszName, + char ** = NULL ) OGR_DEPRECATED("Use GDALDriver class instead"); + virtual OGRErr DeleteDataSource( const char *pszName ) OGR_DEPRECATED("Use GDALDriver class instead"); +}; + + +/************************************************************************/ +/* OGRSFDriverRegistrar */ +/************************************************************************/ + +/** + * LEGACY class. Use GDALDriverManager in your new code ! This class may be + * removed in a later release. + * + * Singleton manager for OGRSFDriver instances that will be used to try + * and open datasources. Normally the registrar is populated with + * standard drivers using the OGRRegisterAll() function and does not need + * to be directly accessed. The driver registrar and all registered drivers + * may be cleaned up on shutdown using OGRCleanupAll(). + * + * @deprecated + */ + +class CPL_DLL OGRSFDriverRegistrar +{ + + OGRSFDriverRegistrar(); + ~OGRSFDriverRegistrar(); + + static GDALDataset* OpenWithDriverArg(GDALDriver* poDriver, + GDALOpenInfo* poOpenInfo); + static GDALDataset* CreateVectorOnly( GDALDriver* poDriver, + const char * pszName, + char ** papszOptions ); + static CPLErr DeleteDataSource( GDALDriver* poDriver, + const char * pszName ); + + public: + + static OGRSFDriverRegistrar *GetRegistrar() OGR_DEPRECATED("Use GDALDriverManager class instead"); + + void RegisterDriver( OGRSFDriver * poDriver ) OGR_DEPRECATED("Use GDALDriverManager class instead"); + + int GetDriverCount( void ) OGR_DEPRECATED("Use GDALDriverManager class instead"); + GDALDriver *GetDriver( int iDriver ) OGR_DEPRECATED("Use GDALDriverManager class instead"); + GDALDriver *GetDriverByName( const char * ) OGR_DEPRECATED("Use GDALDriverManager class instead"); + + int GetOpenDSCount() OGR_DEPRECATED("Use GDALDriverManager class instead"); + OGRDataSource *GetOpenDS( int ) OGR_DEPRECATED("Use GDALDriverManager class instead"); +}; + +/* -------------------------------------------------------------------- */ +/* Various available registration methods. */ +/* -------------------------------------------------------------------- */ +CPL_C_START +void CPL_DLL OGRRegisterAll(); +void OGRRegisterAllInternal(); + +void CPL_DLL RegisterOGRFileGDB(); +void CPL_DLL RegisterOGRShape(); +void CPL_DLL RegisterOGRNTF(); +void CPL_DLL RegisterOGRFME(); +void CPL_DLL RegisterOGRSDTS(); +void CPL_DLL RegisterOGRTiger(); +void CPL_DLL RegisterOGRS57(); +void CPL_DLL RegisterOGRTAB(); +void CPL_DLL RegisterOGRMIF(); +void CPL_DLL RegisterOGROGDI(); +void CPL_DLL RegisterOGRODBC(); +void CPL_DLL RegisterOGRWAsP(); +void CPL_DLL RegisterOGRPG(); +void CPL_DLL RegisterOGRMSSQLSpatial(); +void CPL_DLL RegisterOGRMySQL(); +void CPL_DLL RegisterOGROCI(); +void CPL_DLL RegisterOGRDGN(); +void CPL_DLL RegisterOGRGML(); +void CPL_DLL RegisterOGRLIBKML(); +void CPL_DLL RegisterOGRKML(); +void CPL_DLL RegisterOGRGeoJSON(); +void CPL_DLL RegisterOGRAVCBin(); +void CPL_DLL RegisterOGRAVCE00(); +void CPL_DLL RegisterOGRREC(); +void CPL_DLL RegisterOGRMEM(); +void CPL_DLL RegisterOGRVRT(); +void CPL_DLL RegisterOGRDODS(); +void CPL_DLL RegisterOGRSQLite(); +void CPL_DLL RegisterOGRCSV(); +void CPL_DLL RegisterOGRILI1(); +void CPL_DLL RegisterOGRILI2(); +void CPL_DLL RegisterOGRGRASS(); +void CPL_DLL RegisterOGRPGeo(); +void CPL_DLL RegisterOGRDXFDWG(); +void CPL_DLL RegisterOGRDXF(); +void CPL_DLL RegisterOGRDWG(); +void CPL_DLL RegisterOGRSDE(); +void CPL_DLL RegisterOGRIDB(); +void CPL_DLL RegisterOGRGMT(); +void CPL_DLL RegisterOGRBNA(); +void CPL_DLL RegisterOGRGPX(); +void CPL_DLL RegisterOGRGeoconcept(); +void CPL_DLL RegisterOGRIngres(); +void CPL_DLL RegisterOGRXPlane(); +void CPL_DLL RegisterOGRNAS(); +void CPL_DLL RegisterOGRGeoRSS(); +void CPL_DLL RegisterOGRGTM(); +void CPL_DLL RegisterOGRVFK(); +void CPL_DLL RegisterOGRPGDump(); +void CPL_DLL RegisterOGROSM(); +void CPL_DLL RegisterOGRGPSBabel(); +void CPL_DLL RegisterOGRSUA(); +void CPL_DLL RegisterOGROpenAir(); +void CPL_DLL RegisterOGRPDS(); +void CPL_DLL RegisterOGRWFS(); +void CPL_DLL RegisterOGRSOSI(); +void CPL_DLL RegisterOGRHTF(); +void CPL_DLL RegisterOGRAeronavFAA(); +void CPL_DLL RegisterOGRGeomedia(); +void CPL_DLL RegisterOGRMDB(); +void CPL_DLL RegisterOGREDIGEO(); +void CPL_DLL RegisterOGRGFT(); +void CPL_DLL RegisterOGRGME(); +void CPL_DLL RegisterOGRSVG(); +void CPL_DLL RegisterOGRCouchDB(); +void CPL_DLL RegisterOGRCloudant(); +void CPL_DLL RegisterOGRIdrisi(); +void CPL_DLL RegisterOGRARCGEN(); +void CPL_DLL RegisterOGRSEGUKOOA(); +void CPL_DLL RegisterOGRSEGY(); +void CPL_DLL RegisterOGRXLS(); +void CPL_DLL RegisterOGRODS(); +void CPL_DLL RegisterOGRXLSX(); +void CPL_DLL RegisterOGRElastic(); +void CPL_DLL RegisterOGRGeoPackage(); +void CPL_DLL RegisterOGRWalk(); +void CPL_DLL RegisterOGRCartoDB(); +void CPL_DLL RegisterOGRSXF(); +void CPL_DLL RegisterOGROpenFileGDB(); +void CPL_DLL RegisterOGRSelafin(); +void CPL_DLL RegisterOGRJML(); +void CPL_DLL RegisterOGRPLSCENES(); +void CPL_DLL RegisterOGRCSW(); +CPL_C_END + + +#endif /* ndef _OGRSF_FRMTS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/GNUmakefile new file mode 100644 index 000000000..58773f93c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogropenairdriver.o ogropenairdatasource.o ogropenairlayer.o ogropenairlabellayer.o + +CPPFLAGS := -I.. -I../.. -I../xplane $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_openair.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/drv_openair.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/drv_openair.html new file mode 100644 index 000000000..720d78d65 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/drv_openair.html @@ -0,0 +1,28 @@ + + +OpenAir - OpenAir Special Use Airspace Format + + + + +

    OpenAir - OpenAir Special Use Airspace Format

    + +(GDAL/OGR >= 1.8.0)

    + +This driver reads files describing Special Use Airspaces in the OpenAir format

    + +Airspace are returned as features of a single layer called 'airspaces', with a geometry of type Polygon and the following fields : +CLASS, NAME, FLOOR, CEILING.

    + +Airspace geometries made of arcs will be tesselated. Styling information when present is returned at the feature level.

    + +An extra layer called 'labels' will contain a feature for each label (AT element). There can be multiple AT records for a single airspace segment. The fields are the same as the 'airspaces' layer.

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/makefile.vc new file mode 100644 index 000000000..9acd4498e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogropenairdriver.obj ogropenairdatasource.obj ogropenairlayer.obj ogropenairlabellayer.obj +EXTRAFLAGS = -I.. -I..\.. -I..\xplane + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogr_openair.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogr_openair.h new file mode 100644 index 000000000..fa7d96390 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogr_openair.h @@ -0,0 +1,135 @@ +/****************************************************************************** + * $Id: ogr_openair.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenAir Translator + * Purpose: Definition of classes for OGR .sua driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_OPENAIR_H_INCLUDED +#define _OGR_OPENAIR_H_INCLUDED + +#include "ogrsf_frmts.h" +#include + +/************************************************************************/ +/* OGROpenAirLayer */ +/************************************************************************/ + +typedef struct +{ + int penStyle; + int penWidth; + int penR, penG, penB; + int fillR, fillG, fillB; +} OpenAirStyle; + +class OGROpenAirLayer : public OGRLayer +{ + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference *poSRS; + + VSILFILE* fpOpenAir; + int bEOF; + int bHasLastLine; + CPLString osLastLine; + + int nNextFID; + + std::map oStyleMap; + + OGRFeature * GetNextRawFeature(); + + public: + OGROpenAirLayer(VSILFILE* fp); + ~OGROpenAirLayer(); + + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGROpenAirLabelLayer */ +/************************************************************************/ + +class OGROpenAirLabelLayer : public OGRLayer +{ + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference *poSRS; + + VSILFILE* fpOpenAir; + CPLString osLastLine; + + int nNextFID; + + OGRFeature * GetNextRawFeature(); + + CPLString osCLASS, osNAME, osFLOOR, osCEILING; + + public: + OGROpenAirLabelLayer(VSILFILE* fp); + ~OGROpenAirLabelLayer(); + + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGROpenAirDataSource */ +/************************************************************************/ + +class OGROpenAirDataSource : public OGRDataSource +{ + char* pszName; + + OGRLayer** papoLayers; + int nLayers; + + public: + OGROpenAirDataSource(); + ~OGROpenAirDataSource(); + + int Open( const char * pszFilename ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); +}; + +int OGROpenAirGetLatLon(const char* pszStr, double& dfLat, double& dfLon); + +#endif /* ndef _OGR_OPENAIR_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogropenairdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogropenairdatasource.cpp new file mode 100644 index 000000000..1af84d034 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogropenairdatasource.cpp @@ -0,0 +1,204 @@ +/****************************************************************************** + * $Id: ogropenairdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OpenAir Translator + * Purpose: Implements OGROpenAirDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_openair.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogropenairdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGROpenAirDataSource() */ +/************************************************************************/ + +OGROpenAirDataSource::OGROpenAirDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; +} + +/************************************************************************/ +/* ~OGROpenAirDataSource() */ +/************************************************************************/ + +OGROpenAirDataSource::~OGROpenAirDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGROpenAirDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGROpenAirDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGROpenAirDataSource::Open( const char * pszFilename ) + +{ + pszName = CPLStrdup( pszFilename ); + + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return FALSE; + + VSILFILE* fp2 = VSIFOpenL(pszFilename, "rb"); + if (fp2) + { + nLayers = 2; + papoLayers = (OGRLayer**) CPLMalloc(2 * sizeof(OGRLayer*)); + papoLayers[0] = new OGROpenAirLayer(fp); + papoLayers[1] = new OGROpenAirLabelLayer(fp2); + } + else + { + VSIFCloseL(fp); + return FALSE; + } + + return TRUE; +} + + +/************************************************************************/ +/* GetLatLon() */ +/************************************************************************/ + +enum { DEGREE, MINUTE, SECOND }; + +int OGROpenAirGetLatLon(const char* pszStr, double& dfLat, double& dfLon) +{ + dfLat = 0; + dfLon = 0; + + int nCurInt = 0; + double dfExp = 1.; + int bHasExp = FALSE; + int nCurPart = DEGREE; + double dfDegree = 0, dfMinute = 0, dfSecond = 0; + char c; + int bHasLat = FALSE, bHasLon = FALSE; + while((c = *pszStr) != 0) + { + if (c >= '0' && c <= '9') + { + nCurInt = nCurInt * 10 + c - '0'; + if (bHasExp) + dfExp *= 10; + } + else if (c == '.') + { + bHasExp = TRUE; + } + else if (c == ':') + { + double dfVal = nCurInt / dfExp; + if (nCurPart == DEGREE) + dfDegree = dfVal; + else if (nCurPart == MINUTE) + dfMinute = dfVal; + else if (nCurPart == SECOND) + dfSecond = dfVal; + nCurPart ++; + nCurInt = 0; + dfExp = 1.; + bHasExp = FALSE; + } + else if (c == ' ') + { + + } + else if (c == 'N' || c == 'S') + { + double dfVal = nCurInt / dfExp; + if (nCurPart == DEGREE) + dfDegree = dfVal; + else if (nCurPart == MINUTE) + dfMinute = dfVal; + else if (nCurPart == SECOND) + dfSecond = dfVal; + + dfLat = dfDegree + dfMinute / 60 + dfSecond / 3600; + if (c == 'S') + dfLat = -dfLat; + nCurInt = 0; + dfExp = 1.; + bHasExp = FALSE; + nCurPart = DEGREE; + bHasLat = TRUE; + } + else if (c == 'E' || c == 'W') + { + double dfVal = nCurInt / dfExp; + if (nCurPart == DEGREE) + dfDegree = dfVal; + else if (nCurPart == MINUTE) + dfMinute = dfVal; + else if (nCurPart == SECOND) + dfSecond = dfVal; + + dfLon = dfDegree + dfMinute / 60 + dfSecond / 3600; + if (c == 'W') + dfLon = -dfLon; + bHasLon = TRUE; + break; + } + + pszStr++; + } + + return bHasLat && bHasLon; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogropenairdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogropenairdriver.cpp new file mode 100644 index 000000000..61ef2efe1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogropenairdriver.cpp @@ -0,0 +1,129 @@ +/****************************************************************************** + * $Id: ogropenairdriver.cpp 29253 2015-05-27 08:49:16Z rouault $ + * + * Project: OpenAir Translator + * Purpose: Implements OGROpenAirDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_openair.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogropenairdriver.cpp 29253 2015-05-27 08:49:16Z rouault $"); + +extern "C" void RegisterOGROpenAir(); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGROpenAirDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->eAccess == GA_Update || + poOpenInfo->fpL == NULL || + !poOpenInfo->TryToIngest(10000) ) + return NULL; + + int bIsOpenAir = (strstr((const char*)poOpenInfo->pabyHeader, "\nAC ") != NULL && + strstr((const char*)poOpenInfo->pabyHeader, "\nAN ") != NULL && + strstr((const char*)poOpenInfo->pabyHeader, "\nAL ") != NULL && + strstr((const char*)poOpenInfo->pabyHeader, "\nAH") != NULL); + if( !bIsOpenAir ) + { + /* Some files such http://soaringweb.org/Airspace/CZ/CZ_combined_2014_05_01.txt */ + /* have very long comments in the header, so we will have to check */ + /* further, but only do this is we have a hint that the file might be */ + /* a candidate */ + int nLen = poOpenInfo->nHeaderBytes; + if( nLen < 10000 ) + return NULL; + /* Check the 'Airspace' word in the header */ + if( strstr((const char*)poOpenInfo->pabyHeader, "Airspace") == NULL ) + return NULL; + // Check that the header is at least UTF-8 + // but do not take into account partial UTF-8 characters at the end + int nTruncated = 0; + while(nLen > 0) + { + if( (poOpenInfo->pabyHeader[nLen-1] & 0xc0) != 0x80 ) + { + break; + } + nLen --; + nTruncated ++; + if( nTruncated == 7 ) + return NULL; + } + if( !CPLIsUTF8((const char*)poOpenInfo->pabyHeader, nLen) ) + return NULL; + if( !poOpenInfo->TryToIngest(30000) ) + return NULL; + bIsOpenAir = (strstr((const char*)poOpenInfo->pabyHeader, "\nAC ") != NULL && + strstr((const char*)poOpenInfo->pabyHeader, "\nAN ") != NULL && + strstr((const char*)poOpenInfo->pabyHeader, "\nAL ") != NULL && + strstr((const char*)poOpenInfo->pabyHeader, "\nAH") != NULL); + if( !bIsOpenAir ) + return NULL; + } + + OGROpenAirDataSource *poDS = new OGROpenAirDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGROpenAir() */ +/************************************************************************/ + +void RegisterOGROpenAir() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "OpenAir" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "OpenAir" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "OpenAir" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_openair.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGROpenAirDriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogropenairlabellayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogropenairlabellayer.cpp new file mode 100644 index 000000000..495966c99 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogropenairlabellayer.cpp @@ -0,0 +1,191 @@ +/****************************************************************************** + * $Id: ogropenairlabellayer.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OpenAir Translator + * Purpose: Implements OGROpenAirLabelLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_openair.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: ogropenairlabellayer.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGROpenAirLabelLayer() */ +/************************************************************************/ + +OGROpenAirLabelLayer::OGROpenAirLabelLayer( VSILFILE* fp ) + +{ + fpOpenAir = fp; + nNextFID = 0; + + poSRS = new OGRSpatialReference(SRS_WKT_WGS84); + + poFeatureDefn = new OGRFeatureDefn( "labels" ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint ); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + OGRFieldDefn oField1( "CLASS", OFTString); + poFeatureDefn->AddFieldDefn( &oField1 ); + OGRFieldDefn oField2( "NAME", OFTString); + poFeatureDefn->AddFieldDefn( &oField2 ); + OGRFieldDefn oField3( "FLOOR", OFTString); + poFeatureDefn->AddFieldDefn( &oField3 ); + OGRFieldDefn oField4( "CEILING", OFTString); + poFeatureDefn->AddFieldDefn( &oField4 ); +} + +/************************************************************************/ +/* ~OGROpenAirLabelLayer() */ +/************************************************************************/ + +OGROpenAirLabelLayer::~OGROpenAirLabelLayer() + +{ + if( poSRS != NULL ) + poSRS->Release(); + + poFeatureDefn->Release(); + + VSIFCloseL( fpOpenAir ); +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGROpenAirLabelLayer::ResetReading() + +{ + nNextFID = 0; + VSIFSeekL( fpOpenAir, 0, SEEK_SET ); +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGROpenAirLabelLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGROpenAirLabelLayer::GetNextRawFeature() +{ + const char* pszLine; + double dfLat = 0, dfLon = 0; + int bHasCoord = FALSE; + + while(TRUE) + { + pszLine = CPLReadLine2L(fpOpenAir, 1024, NULL); + if (pszLine == NULL) + return NULL; + + if (pszLine[0] == '*' || pszLine[0] == '\0') + continue; + + if (EQUALN(pszLine, "AC ", 3)) + { + if (osCLASS.size() != 0) + { + osNAME = ""; + osCEILING = ""; + osFLOOR = ""; + } + osCLASS = pszLine + 3; + } + else if (EQUALN(pszLine, "AN ", 3)) + osNAME = pszLine + 3; + else if (EQUALN(pszLine, "AH ", 3)) + osCEILING = pszLine + 3; + else if (EQUALN(pszLine, "AL ", 3)) + osFLOOR = pszLine + 3; + else if (EQUALN(pszLine, "AT ", 3)) + { + bHasCoord = OGROpenAirGetLatLon(pszLine + 3, dfLat, dfLon); + break; + } + } + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField(0, osCLASS.c_str()); + poFeature->SetField(1, osNAME.c_str()); + poFeature->SetField(2, osFLOOR.c_str()); + poFeature->SetField(3, osCEILING.c_str()); + + CPLString osStyle; + osStyle.Printf("LABEL(t:\"%s\")", osNAME.c_str()); + poFeature->SetStyleString(osStyle.c_str()); + + if (bHasCoord) + { + OGRPoint* poPoint = new OGRPoint(dfLon, dfLat); + poPoint->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poPoint); + } + + poFeature->SetFID(nNextFID++); + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGROpenAirLabelLayer::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogropenairlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogropenairlayer.cpp new file mode 100644 index 000000000..005b6e3b5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openair/ogropenairlayer.cpp @@ -0,0 +1,472 @@ +/****************************************************************************** + * $Id: ogropenairlayer.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: OpenAir Translator + * Purpose: Implements OGROpenAirLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_openair.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "ogr_xplane_geo_utils.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: ogropenairlayer.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* OGROpenAirLayer() */ +/************************************************************************/ + +OGROpenAirLayer::OGROpenAirLayer( VSILFILE* fp ) + +{ + fpOpenAir = fp; + nNextFID = 0; + bEOF = FALSE; + bHasLastLine = FALSE; + + poSRS = new OGRSpatialReference(SRS_WKT_WGS84); + + poFeatureDefn = new OGRFeatureDefn( "airspaces" ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPolygon ); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + OGRFieldDefn oField1( "CLASS", OFTString); + poFeatureDefn->AddFieldDefn( &oField1 ); + OGRFieldDefn oField2( "NAME", OFTString); + poFeatureDefn->AddFieldDefn( &oField2 ); + OGRFieldDefn oField3( "FLOOR", OFTString); + poFeatureDefn->AddFieldDefn( &oField3 ); + OGRFieldDefn oField4( "CEILING", OFTString); + poFeatureDefn->AddFieldDefn( &oField4 ); +} + +/************************************************************************/ +/* ~OGROpenAirLayer() */ +/************************************************************************/ + +OGROpenAirLayer::~OGROpenAirLayer() + +{ + if( poSRS != NULL ) + poSRS->Release(); + + poFeatureDefn->Release(); + + std::map::const_iterator iter; + + for( iter = oStyleMap.begin(); iter != oStyleMap.end(); ++iter ) + { + CPLFree(iter->second); + } + + VSIFCloseL( fpOpenAir ); +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGROpenAirLayer::ResetReading() + +{ + nNextFID = 0; + bEOF = FALSE; + bHasLastLine = FALSE; + VSIFSeekL( fpOpenAir, 0, SEEK_SET ); +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGROpenAirLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGROpenAirLayer::GetNextRawFeature() +{ + const char* pszLine; + CPLString osCLASS, osNAME, osFLOOR, osCEILING; + OGRLinearRing oLR; + /* double dfLastLat = 0, dfLastLon = 0; */ + int bFirst = TRUE; + int bClockWise = TRUE; + double dfCenterLat = 0, dfCenterLon = 0; + int bHasCenter = FALSE; + OpenAirStyle sStyle; + sStyle.penStyle = -1; + sStyle.penWidth = -1; + sStyle.penR = sStyle.penG = sStyle.penB = -1; + sStyle.fillR = sStyle.fillG = sStyle.fillB = -1; + + if (bEOF) + return NULL; + + while(TRUE) + { + if (bFirst && bHasLastLine) + { + pszLine = osLastLine.c_str(); + bFirst = FALSE; + } + else + { + pszLine = CPLReadLine2L(fpOpenAir, 1024, NULL); + if (pszLine == NULL) + { + bEOF = TRUE; + if (oLR.getNumPoints() == 0) + return NULL; + + if (osCLASS.size() != 0 && + oStyleMap.find(osCLASS) != oStyleMap.end()) + { + memcpy(&sStyle, oStyleMap[osCLASS], sizeof(sStyle)); + } + break; + } + osLastLine = pszLine; + bHasLastLine = TRUE; + } + + if (pszLine[0] == '*' || pszLine[0] == '\0') + continue; + + if (EQUALN(pszLine, "AC ", 3) || EQUALN(pszLine, "AC,", 3)) + { + if (osCLASS.size() != 0) + { + if (sStyle.penStyle != -1 || sStyle.fillR != -1) + { + if (oLR.getNumPoints() == 0) + { + OpenAirStyle* psStyle; + if (oStyleMap.find(osCLASS) == oStyleMap.end()) + { + psStyle = (OpenAirStyle*)CPLMalloc( + sizeof(OpenAirStyle)); + oStyleMap[osCLASS] = psStyle; + } + else + psStyle = oStyleMap[osCLASS]; + memcpy(psStyle, &sStyle, sizeof(sStyle)); + } + else + break; + } + else if (oStyleMap.find(osCLASS) != oStyleMap.end()) + { + memcpy(&sStyle, oStyleMap[osCLASS], sizeof(sStyle)); + break; + } + else + break; + } + sStyle.penStyle = -1; + sStyle.penWidth = -1; + sStyle.penR = sStyle.penG = sStyle.penB = -1; + sStyle.fillR = sStyle.fillG = sStyle.fillB = -1; + osCLASS = pszLine + 3; + bClockWise = TRUE; + bHasCenter = FALSE; + } + else if (EQUALN(pszLine, "AN ", 3)) + { + if (osNAME.size() != 0) + break; + osNAME = pszLine + 3; + } + else if (EQUALN(pszLine, "AH ", 3)) + osCEILING = pszLine + 3; + else if (EQUALN(pszLine, "AL ", 3)) + osFLOOR = pszLine + 3; + else if (EQUALN(pszLine, "AT ", 3)) + { + /* Ignored for that layer*/ + } + else if (EQUALN(pszLine, "SP ", 3)) + { + if (osCLASS.size() != 0) + { + char** papszTokens = CSLTokenizeString2(pszLine+3, ", ", 0); + if (CSLCount(papszTokens) == 5) + { + sStyle.penStyle = atoi(papszTokens[0]); + sStyle.penWidth = atoi(papszTokens[1]); + sStyle.penR = atoi(papszTokens[2]); + sStyle.penG = atoi(papszTokens[3]); + sStyle.penB = atoi(papszTokens[4]); + } + CSLDestroy(papszTokens); + } + } + else if (EQUALN(pszLine, "SB ", 3)) + { + if (osCLASS.size() != 0) + { + char** papszTokens = CSLTokenizeString2(pszLine+3, ", ", 0); + if (CSLCount(papszTokens) == 3) + { + sStyle.fillR = atoi(papszTokens[0]); + sStyle.fillG = atoi(papszTokens[1]); + sStyle.fillB = atoi(papszTokens[2]); + } + CSLDestroy(papszTokens); + } + } + else if (EQUALN(pszLine, "DP ", 3)) + { + pszLine += 3; + + double dfLat, dfLon; + if (!OGROpenAirGetLatLon(pszLine, dfLat, dfLon)) + continue; + + oLR.addPoint(dfLon, dfLat); + /* dfLastLat = dfLat; */ + /* dfLastLon = dfLon; */ + } + else if (EQUALN(pszLine, "DA ", 3)) + { + pszLine += 3; + + char* pszStar = strchr((char*)pszLine, '*'); + if (pszStar) *pszStar = 0; + char** papszTokens = CSLTokenizeString2(pszLine, ",", 0); + if (bHasCenter && CSLCount(papszTokens) == 3) + { + double dfRadius = CPLAtof(papszTokens[0]) * 1852; + double dfStartAngle = CPLAtof(papszTokens[1]); + double dfEndAngle = CPLAtof(papszTokens[2]); + + if (bClockWise && dfEndAngle < dfStartAngle) + dfEndAngle += 360; + else if (!bClockWise && dfStartAngle < dfEndAngle) + dfEndAngle -= 360; + + double dfStartDistance = dfRadius; + double dfEndDistance = dfRadius; + int nSign = (bClockWise) ? 1 : -1; + double dfAngle; + double dfLat, dfLon; + for(dfAngle = dfStartAngle; + (dfAngle - dfEndAngle) * nSign < 0; + dfAngle += nSign) + { + double pct = (dfAngle - dfStartAngle) / + (dfEndAngle - dfStartAngle); + double dfDist = dfStartDistance * (1-pct) + + dfEndDistance * pct; + OGRXPlane_ExtendPosition(dfCenterLat, dfCenterLon, + dfDist, dfAngle, &dfLat, &dfLon); + oLR.addPoint(dfLon, dfLat); + } + OGRXPlane_ExtendPosition(dfCenterLat, dfCenterLon, + dfEndDistance, dfEndAngle, &dfLat, &dfLon); + oLR.addPoint(dfLon, dfLat); + + /* dfLastLat = oLR.getY(oLR.getNumPoints() - 1); */ + /* dfLastLon = oLR.getX(oLR.getNumPoints() - 1); */ + } + CSLDestroy(papszTokens); + } + else if (EQUALN(pszLine, "DB ", 3)) + { + pszLine += 3; + + char* pszStar = strchr((char*)pszLine, '*'); + if (pszStar) *pszStar = 0; + char** papszTokens = CSLTokenizeString2(pszLine, ",", 0); + double dfFirstLat, dfFirstLon; + double dfSecondLat, dfSecondLon; + if (bHasCenter && CSLCount(papszTokens) == 2 && + OGROpenAirGetLatLon(papszTokens[0], dfFirstLat, dfFirstLon) && + OGROpenAirGetLatLon(papszTokens[1], dfSecondLat, dfSecondLon)) + { + double dfStartDistance =OGRXPlane_Distance(dfCenterLat, + dfCenterLon, dfFirstLat, dfFirstLon); + double dfEndDistance = OGRXPlane_Distance(dfCenterLat, + dfCenterLon, dfSecondLat, dfSecondLon); + double dfStartAngle = OGRXPlane_Track(dfCenterLat, + dfCenterLon, dfFirstLat, dfFirstLon); + double dfEndAngle = OGRXPlane_Track(dfCenterLat, + dfCenterLon, dfSecondLat, dfSecondLon); + + if (bClockWise && dfEndAngle < dfStartAngle) + dfEndAngle += 360; + else if (!bClockWise && dfStartAngle < dfEndAngle) + dfEndAngle -= 360; + + int nSign = (bClockWise) ? 1 : -1; + double dfAngle; + for(dfAngle = dfStartAngle; + (dfAngle - dfEndAngle) * nSign < 0; + dfAngle += nSign) + { + double dfLat, dfLon; + double pct = (dfAngle - dfStartAngle) / + (dfEndAngle - dfStartAngle); + double dfDist = dfStartDistance * (1-pct) + + dfEndDistance * pct; + OGRXPlane_ExtendPosition(dfCenterLat, dfCenterLon, + dfDist, dfAngle, &dfLat, &dfLon); + oLR.addPoint(dfLon, dfLat); + } + oLR.addPoint(dfSecondLon, dfSecondLat); + + /* dfLastLat = oLR.getY(oLR.getNumPoints() - 1); */ + /* dfLastLon = oLR.getX(oLR.getNumPoints() - 1); */ + } + CSLDestroy(papszTokens); + } + else if ((EQUALN(pszLine, "DC ", 3) || EQUALN(pszLine, "DC=", 3)) && + (bHasCenter || strstr(pszLine, "V X=") != NULL)) + { + if (!bHasCenter) + { + const char* pszVX = strstr(pszLine, "V X="); + bHasCenter = OGROpenAirGetLatLon(pszVX, dfCenterLat, dfCenterLon); + } + if (bHasCenter) + { + pszLine += 3; + + double dfRADIUS = CPLAtof(pszLine) * 1852; + + double dfAngle; + double dfLat, dfLon; + for(dfAngle = 0; dfAngle < 360; dfAngle += 1) + { + OGRXPlane_ExtendPosition(dfCenterLat, dfCenterLon, + dfRADIUS, dfAngle, &dfLat, &dfLon); + oLR.addPoint(dfLon, dfLat); + } + OGRXPlane_ExtendPosition(dfCenterLat, dfCenterLon, + dfRADIUS, 0, &dfLat, &dfLon); + oLR.addPoint(dfLon, dfLat); + + /* dfLastLat = oLR.getY(oLR.getNumPoints() - 1); */ + /* dfLastLon = oLR.getX(oLR.getNumPoints() - 1); */ + } + } + else if (EQUALN(pszLine, "V X=", 4)) + { + bHasCenter = + OGROpenAirGetLatLon(pszLine + 4, dfCenterLat, dfCenterLon); + } + else if (EQUALN(pszLine, "V D=-", 5)) + { + bClockWise = FALSE; + } + else if (EQUALN(pszLine, "V D=+", 5)) + { + bClockWise = TRUE; + } + else + { + //CPLDebug("OpenAir", "Unexpected content : %s", pszLine); + } + } + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField(0, osCLASS.c_str()); + poFeature->SetField(1, osNAME.c_str()); + poFeature->SetField(2, osFLOOR.c_str()); + poFeature->SetField(3, osCEILING.c_str()); + + if (sStyle.penStyle != -1 || sStyle.fillR != -1) + { + CPLString osStyle; + if (sStyle.penStyle != -1) + { + osStyle += CPLString().Printf("PEN(c:#%02X%02X%02X,w:%dpt", + sStyle.penR, sStyle.penG, sStyle.penB, + sStyle.penWidth); + if (sStyle.penStyle == 1) + osStyle += ",p:\"5px 5px\""; + osStyle += ")"; + } + if (sStyle.fillR != -1) + { + if (osStyle.size() != 0) + osStyle += ";"; + osStyle += CPLString().Printf("BRUSH(fc:#%02X%02X%02X)", + sStyle.fillR, sStyle.fillG, sStyle.fillB); + } + else + { + if (osStyle.size() != 0) + osStyle += ";"; + osStyle += "BRUSH(fc:#00000000,id:\"ogr-brush-1\")"; + } + if (osStyle.size() != 0) + poFeature->SetStyleString(osStyle); + } + + OGRPolygon* poPoly = new OGRPolygon(); + oLR.closeRings(); + poPoly->addRing(&oLR); + poPoly->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poPoly); + poFeature->SetFID(nNextFID++); + + return poFeature; +} +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGROpenAirLayer::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/GNUmakefile new file mode 100644 index 000000000..ddfa4e552 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogropenfilegdbdriver.o ogropenfilegdbdatasource.o ogropenfilegdblayer.o filegdbtable.o filegdbindex.o + +CPPFLAGS := -I.. -I../.. -I../mem $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_openfilegdb.h filegdbtable.h filegdbtable_priv.h ../../swq.h ../mem/ogr_mem.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/drv_openfilegdb.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/drv_openfilegdb.html new file mode 100644 index 000000000..e0c0ce476 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/drv_openfilegdb.html @@ -0,0 +1,101 @@ + + +ESRI File Geodatabase (OpenFileGDB) + + + + +

    ESRI File Geodatabase (OpenFileGDB)

    + +

    (Available in GDAL >= 1.11)

    + +

    +The OpenFileGDB driver provides read access to File Geodatabases +(.gdb directories) created by ArcGIS 9 and above. +

    +

    +It can also read directly zipped .gdb directories (.gdb.zip) +

    +

    +A specific .gdbtable file (including "system" tables) can also be opened +directly. +

    + +

    Spatial filtering

    + +The driver cannot use the .spx files (when they are present) for spatial +filtering. However, it will use the minimum bounding rectangle included at the +beginning of the geometry blobs to speed up spatial filtering. By default, it +will also build on the fly a in-memory spatial index during the first sequential +read of a layer. Following spatial filtering operations on that layer will then +benefit from that spatial index. The building of this in-memory spatial index +can be disabled by setting the OPENFILEGDB_IN_MEMORY_SPI configuration option to +NO. + +

    SQL support

    + +SQL statements are run through the OGR SQL engine. When attribute indexes (.atx +files) exist, the driver will use them to speed up WHERE clauses or SetAttributeFilter() +calls. + +

    Special SQL requests

    + +"GetLayerDefinition a_layer_name" and "GetLayerMetadata a_layer_name" can be +used as special SQL requests to get respectively the definition and metadata of +a FileGDB table as XML content (only available in Geodatabases created with +ArcGIS 10 or above) + +

    Comparison with the FileGDB driver

    + +

    +(Comparison done with a FileGDB driver using FileGDB API SDK 1.3) +

    + +

    +Advantages of the OpenFileGDB driver: +

    + +
      +
    • Can read ArcGIS 9.X Geodatabases, and not only 10 or above.
    • +
    • Can open layers with any spatial reference system.
    • +
    • Thread-safe (i.e. datasources can be processed in parallel).
    • +
    • Uses the VSI Virtual File API, enabling the user + to read a Geodatabase in a ZIP file or stored on a HTTP server.
    • +
    • Faster on databases with a big number of fields.
    • +
    • Does not depend on a third-party library.
    • +
    • Robust against corrupted Geodatabase files.
    • +
    + +

    +Drawbacks of the OpenFileGDB driver: +

    + +
      +
    • Read-only.
    • +
    • Cannot use spatial indexes.
    • +
    • Cannot read data from compressed feature classes (SDC, Smart Data Compression). Nor can can FileGDB driver because the ESRI FileGDB SDK lacks also this feature.
    • +
    + +

    Examples

    + +
      +
    • Read layer from FileGDB and load into PostGIS:

      + ogr2ogr -overwrite -f "PostgreSQL" PG:"host=myhost user=myuser dbname=mydb password=mypass" "C:\somefolder\BigFileGDB.gdb" "MyFeatureClass" +

    • +
    • Get detailed info for FileGDB:

      + ogrinfo -al "C:\somefolder\MyGDB.gdb" +

    • +
    • Get detailed info for a zipped FileGDB:

      + ogrinfo -al "C:\somefolder\MyGDB.gdb.zip" +

    • +
    + +

    Links

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/filegdbindex.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/filegdbindex.cpp new file mode 100644 index 000000000..ec02e522a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/filegdbindex.cpp @@ -0,0 +1,1805 @@ +/****************************************************************************** + * $Id: filegdbindex.cpp 29195 2015-05-14 11:27:35Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements reading of FileGDB indexes + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "filegdbtable_priv.h" +#include "cpl_string.h" +#include "cpl_time.h" +#include + +CPL_CVSID("$Id"); + +namespace OpenFileGDB +{ + +/************************************************************************/ +/* FileGDBOGRDateToDoubleDate() */ +/************************************************************************/ + +static int FileGDBOGRDateToDoubleDate( const OGRField* psField, double *pdfVal ) +{ + struct tm brokendowntime; + + brokendowntime.tm_year = psField->Date.Year - 1900; + brokendowntime.tm_mon = psField->Date.Month - 1; + brokendowntime.tm_mday = psField->Date.Day; + brokendowntime.tm_hour = psField->Date.Hour; + brokendowntime.tm_min = psField->Date.Minute; + brokendowntime.tm_sec = psField->Date.Second; + + GIntBig nTime = CPLYMDHMSToUnixTime(&brokendowntime); + + *pdfVal = nTime / 3600. / 24 + 25569; + + return TRUE; +} + +/************************************************************************/ +/* FileGDBTrivialIterator */ +/************************************************************************/ + +class FileGDBTrivialIterator : public FileGDBIterator +{ + FileGDBIterator *poParentIter; + FileGDBTable *poTable; + int iRow; + + public: + FileGDBTrivialIterator(FileGDBIterator *poParentIter); + virtual ~FileGDBTrivialIterator() { delete poParentIter; } + + virtual FileGDBTable *GetTable() { return poTable; } + virtual void Reset() { iRow = 0; poParentIter->Reset(); } + virtual int GetNextRowSortedByFID(); + virtual int GetRowCount() + { return poTable->GetTotalRecordCount(); } + + virtual int GetNextRowSortedByValue() + { return poParentIter->GetNextRowSortedByValue(); } + + virtual const OGRField* GetMinValue(int& eOutType) + { return poParentIter->GetMinValue(eOutType); } + virtual const OGRField* GetMaxValue(int& eOutType) + { return poParentIter->GetMaxValue(eOutType); } + virtual int GetMinMaxSumCount(double& dfMin, double& dfMax, + double& dfSum, int& nCount) + { return poParentIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount); } +}; + +/************************************************************************/ +/* FileGDBNotIterator */ +/************************************************************************/ + +class FileGDBNotIterator : public FileGDBIterator +{ + FileGDBIterator *poIterBase; + FileGDBTable *poTable; + int iRow; + int iNextRowBase; + int bNoHoles; + + public: + FileGDBNotIterator(FileGDBIterator* poIterBase); + virtual ~FileGDBNotIterator(); + + virtual FileGDBTable *GetTable() { return poTable; } + virtual void Reset(); + virtual int GetNextRowSortedByFID(); + virtual int GetRowCount(); +}; + +/************************************************************************/ +/* FileGDBAndIterator */ +/************************************************************************/ + +class FileGDBAndIterator : public FileGDBIterator +{ + FileGDBIterator *poIter1; + FileGDBIterator *poIter2; + int iNextRow1; + int iNextRow2; + + public: + FileGDBAndIterator(FileGDBIterator* poIter1, + FileGDBIterator* poIter2); + virtual ~FileGDBAndIterator(); + + virtual FileGDBTable *GetTable() { return poIter1->GetTable(); } + virtual void Reset(); + virtual int GetNextRowSortedByFID(); +}; + +/************************************************************************/ +/* FileGDBOrIterator */ +/************************************************************************/ + +class FileGDBOrIterator : public FileGDBIterator +{ + FileGDBIterator *poIter1; + FileGDBIterator *poIter2; + int bIteratorAreExclusive; + int iNextRow1; + int iNextRow2; + int bHasJustReset; + + public: + FileGDBOrIterator(FileGDBIterator* poIter1, + FileGDBIterator* poIter2, + int bIteratorAreExclusive = FALSE); + virtual ~FileGDBOrIterator(); + + virtual FileGDBTable *GetTable() { return poIter1->GetTable(); } + virtual void Reset(); + virtual int GetNextRowSortedByFID(); + virtual int GetRowCount(); +}; + +/************************************************************************/ +/* FileGDBIndexIterator */ +/************************************************************************/ + +#define MAX_DEPTH 3 +#define UUID_LEN_AS_STRING 38 +#define MAX_CAR_COUNT_STR 80 +#define MAX_UTF8_LEN_STR (4 * MAX_CAR_COUNT_STR) +#define FGDB_PAGE_SIZE 4096 + +class FileGDBIndexIterator : public FileGDBIterator +{ + FileGDBTable *poParent; + int bAscending; + VSILFILE *fpCurIdx; + FileGDBFieldType eFieldType; + GUInt32 nMaxPerPages; + GUInt32 nOffsetFirstValInPage; + GUInt32 nValueCountInIdx; + GUInt32 nIndexDepth; + FileGDBSQLOp eOp; + OGRField sValue; + + int iFirstPageIdx[MAX_DEPTH], + iLastPageIdx[MAX_DEPTH], + iCurPageIdx[MAX_DEPTH]; + GUInt32 nSubPagesCount[MAX_DEPTH]; + GUInt32 nLastPageAccessed[MAX_DEPTH]; + + int iCurFeatureInPage, nFeaturesInPage; + + int bEvaluateToFALSE; + int bEOF; + + int iSorted; + int nSortedCount; + int *panSortedRows; + int SortRows(); + + GUInt16 asUTF16Str[MAX_CAR_COUNT_STR]; + int nStrLen; + char szUUID[UUID_LEN_AS_STRING + 1]; + GByte abyPage[MAX_DEPTH][FGDB_PAGE_SIZE]; + GByte abyPageFeature[FGDB_PAGE_SIZE]; + + OGRField sMin, sMax; + char szMin[MAX_UTF8_LEN_STR+1]; + char szMax[MAX_UTF8_LEN_STR+1]; + const OGRField* GetMinMaxValue(OGRField* psField, + int& eOutType, + int bIsMin); + + int ReadPageNumber(int iLevel); + int LoadNextPage(int iLevel); + int FindPages(int iLevel, int nPage); + int LoadNextFeaturePage(); + + int GetNextRow(); + + FileGDBIndexIterator(FileGDBTable* poParent, + int bAscending); + int SetConstraint(int nFieldIdx, FileGDBSQLOp op, + OGRFieldType eOGRFieldType, + const OGRField* psValue); + + template void GetMinMaxSumCount( + double& dfMin, double& dfMax, + double& dfSum, int& nCount); + public: + virtual ~FileGDBIndexIterator(); + + static FileGDBIterator* Build(FileGDBTable* poParent, + int nFieldIdx, + int bAscending, + FileGDBSQLOp op, + OGRFieldType eOGRFieldType, + const OGRField* psValue); + + virtual FileGDBTable *GetTable() { return poParent; } + virtual void Reset(); + virtual int GetNextRowSortedByFID(); + virtual int GetRowCount(); + + virtual int GetNextRowSortedByValue() { return GetNextRow(); } + + virtual const OGRField* GetMinValue(int& eOutType); + virtual const OGRField* GetMaxValue(int& eOutType); + virtual int GetMinMaxSumCount(double& dfMin, double& dfMax, + double& dfSum, int& nCount); +}; + +/************************************************************************/ +/* GetMinValue() */ +/************************************************************************/ + +const OGRField* FileGDBIterator::GetMinValue(int& eOutType) +{ + PrintError(); + eOutType = -1; + return NULL; +} + +/************************************************************************/ +/* GetMaxValue() */ +/************************************************************************/ + +const OGRField* FileGDBIterator::GetMaxValue(int& eOutType) +{ + PrintError(); + eOutType = -1; + return NULL; +} + +/************************************************************************/ +/* GetNextRowSortedByValue() */ +/************************************************************************/ + +int FileGDBIterator::GetNextRowSortedByValue() +{ + PrintError(); + return -1; +} + +/************************************************************************/ +/* GetMinMaxSumCount() */ +/************************************************************************/ + +int FileGDBIterator::GetMinMaxSumCount(double& dfMin, double& dfMax, + double& dfSum, int& nCount) +{ + PrintError(); + dfMin = 0.0; + dfMax = 0.0; + dfSum = 0.0; + nCount = 0; + return FALSE; +} + +/************************************************************************/ +/* Build() */ +/************************************************************************/ + +FileGDBIterator* FileGDBIterator::Build(FileGDBTable* poParent, + int nFieldIdx, + int bAscending, + FileGDBSQLOp op, + OGRFieldType eOGRFieldType, + const OGRField* psValue) +{ + return FileGDBIndexIterator::Build(poParent, nFieldIdx, bAscending, + op, eOGRFieldType, psValue); +} + +/************************************************************************/ +/* BuildIsNotNull() */ +/************************************************************************/ + +FileGDBIterator* FileGDBIterator::BuildIsNotNull(FileGDBTable* poParent, + int nFieldIdx, + int bAscending) +{ + FileGDBIterator* poIter = Build(poParent, nFieldIdx, bAscending, + FGSO_ISNOTNULL, OFTMaxType, NULL); + if( poIter != NULL ) + { + /* Optimization */ + if( poIter->GetRowCount() == poParent->GetTotalRecordCount() ) + { + CPLAssert(poParent->GetValidRecordCount() == poParent->GetTotalRecordCount()); + poIter = new FileGDBTrivialIterator(poIter); + } + } + return poIter; +} + +/************************************************************************/ +/* BuildNot() */ +/************************************************************************/ + +FileGDBIterator* FileGDBIterator::BuildNot(FileGDBIterator* poIterBase) +{ + return new FileGDBNotIterator(poIterBase); +} + +/************************************************************************/ +/* BuildAnd() */ +/************************************************************************/ + +FileGDBIterator* FileGDBIterator::BuildAnd(FileGDBIterator* poIter1, + FileGDBIterator* poIter2) +{ + return new FileGDBAndIterator(poIter1, poIter2); +} + +/************************************************************************/ +/* BuildOr() */ +/************************************************************************/ + +FileGDBIterator* FileGDBIterator::BuildOr(FileGDBIterator* poIter1, + FileGDBIterator* poIter2, + int bIteratorAreExclusive) +{ + return new FileGDBOrIterator(poIter1, poIter2, bIteratorAreExclusive); +} + +/************************************************************************/ +/* GetRowCount() */ +/************************************************************************/ + +int FileGDBIterator::GetRowCount() +{ + Reset(); + int nCount = 0; + while( GetNextRowSortedByFID() >= 0 ) + nCount ++; + Reset(); + return nCount; +} + +/************************************************************************/ +/* FileGDBTrivialIterator() */ +/************************************************************************/ + +FileGDBTrivialIterator::FileGDBTrivialIterator(FileGDBIterator* poParentIter) : + poParentIter(poParentIter), poTable(poParentIter->GetTable()), iRow(0) +{ +} + +/************************************************************************/ +/* GetNextRowSortedByFID() */ +/************************************************************************/ + +int FileGDBTrivialIterator::GetNextRowSortedByFID() +{ + if( iRow < poTable->GetTotalRecordCount() ) + return iRow ++; + else + return -1; +} + +/************************************************************************/ +/* FileGDBNotIterator() */ +/************************************************************************/ + +FileGDBNotIterator::FileGDBNotIterator(FileGDBIterator* poIterBase) : + poIterBase(poIterBase), poTable(poIterBase->GetTable()), iRow(0), iNextRowBase(-1) +{ + bNoHoles = (poTable->GetValidRecordCount() == poTable->GetTotalRecordCount()); +} + +/************************************************************************/ +/* ~FileGDBNotIterator() */ +/************************************************************************/ + +FileGDBNotIterator::~FileGDBNotIterator() +{ + delete poIterBase; +} + +/************************************************************************/ +/* Reset() */ +/************************************************************************/ + +void FileGDBNotIterator::Reset() +{ + poIterBase->Reset(); + iRow = 0; + iNextRowBase = -1; +} + +/************************************************************************/ +/* GetNextRowSortedByFID() */ +/************************************************************************/ + +int FileGDBNotIterator::GetNextRowSortedByFID() +{ + if( iNextRowBase < 0 ) + { + iNextRowBase = poIterBase->GetNextRowSortedByFID(); + if( iNextRowBase < 0 ) + iNextRowBase = poTable->GetTotalRecordCount(); + } + + while( TRUE ) + { + if( iRow < iNextRowBase ) + { + if( bNoHoles ) + return iRow ++; + else if( poTable->GetOffsetInTableForRow(iRow) ) + return iRow ++; + else if( !poTable->HasGotError() ) + iRow ++; + else + return -1; + } + else if( iRow == poTable->GetTotalRecordCount() ) + return -1; + else + { + iRow = iNextRowBase + 1; + iNextRowBase = poIterBase->GetNextRowSortedByFID(); + if( iNextRowBase < 0 ) + iNextRowBase = poTable->GetTotalRecordCount(); + } + } +} + +/************************************************************************/ +/* GetRowCount() */ +/************************************************************************/ + +int FileGDBNotIterator::GetRowCount() +{ + return poTable->GetValidRecordCount() - poIterBase->GetRowCount(); +} + + +/************************************************************************/ +/* FileGDBAndIterator() */ +/************************************************************************/ + +FileGDBAndIterator::FileGDBAndIterator(FileGDBIterator* poIter1, + FileGDBIterator* poIter2) : + poIter1(poIter1), poIter2(poIter2), + iNextRow1(-1), iNextRow2(-1) +{ + CPLAssert(poIter1->GetTable() == poIter2->GetTable()); +} + +/************************************************************************/ +/* ~FileGDBAndIterator() */ +/************************************************************************/ + +FileGDBAndIterator::~FileGDBAndIterator() +{ + delete poIter1; + delete poIter2; +} + +/************************************************************************/ +/* Reset() */ +/************************************************************************/ + +void FileGDBAndIterator::Reset() +{ + poIter1->Reset(); + poIter2->Reset(); + iNextRow1 = -1; + iNextRow2 = -1; +} + +/************************************************************************/ +/* GetNextRowSortedByFID() */ +/************************************************************************/ + +int FileGDBAndIterator::GetNextRowSortedByFID() +{ + if( iNextRow1 == iNextRow2 ) + { + iNextRow1 = poIter1->GetNextRowSortedByFID(); + iNextRow2 = poIter2->GetNextRowSortedByFID(); + if( iNextRow1 < 0 || iNextRow2 < 0 ) + { + return -1; + } + } + + while( TRUE ) + { + if( iNextRow1 < iNextRow2 ) + { + iNextRow1 = poIter1->GetNextRowSortedByFID(); + if( iNextRow1 < 0 ) + return -1; + } + else if( iNextRow2 < iNextRow1 ) + { + iNextRow2 = poIter2->GetNextRowSortedByFID(); + if( iNextRow2 < 0 ) + return -1; + } + else + return iNextRow1; + } +} + +/************************************************************************/ +/* FileGDBOrIterator() */ +/************************************************************************/ + + +FileGDBOrIterator::FileGDBOrIterator(FileGDBIterator* poIter1, + FileGDBIterator* poIter2, + int bIteratorAreExclusive) : + poIter1(poIter1), poIter2(poIter2), + bIteratorAreExclusive(bIteratorAreExclusive), + iNextRow1(-1), iNextRow2(-1), bHasJustReset(TRUE) +{ + CPLAssert(poIter1->GetTable() == poIter2->GetTable()); +} + +/************************************************************************/ +/* ~FileGDBOrIterator() */ +/************************************************************************/ + +FileGDBOrIterator::~FileGDBOrIterator() +{ + delete poIter1; + delete poIter2; +} + +/************************************************************************/ +/* Reset() */ +/************************************************************************/ + +void FileGDBOrIterator::Reset() +{ + poIter1->Reset(); + poIter2->Reset(); + iNextRow1 = -1; + iNextRow2 = -1; + bHasJustReset = TRUE; +} + +/************************************************************************/ +/* GetNextRowSortedByFID() */ +/************************************************************************/ + +int FileGDBOrIterator::GetNextRowSortedByFID() +{ + if( bHasJustReset ) + { + bHasJustReset = FALSE; + iNextRow1 = poIter1->GetNextRowSortedByFID(); + iNextRow2 = poIter2->GetNextRowSortedByFID(); + } + + if( iNextRow1 < 0 ) + { + int iVal = iNextRow2; + iNextRow2 = poIter2->GetNextRowSortedByFID(); + return iVal; + } + if( iNextRow2 < 0 || iNextRow1 < iNextRow2 ) + { + int iVal = iNextRow1; + iNextRow1 = poIter1->GetNextRowSortedByFID(); + return iVal; + } + if( iNextRow2 < iNextRow1 ) + { + int iVal = iNextRow2; + iNextRow2 = poIter2->GetNextRowSortedByFID(); + return iVal; + } + + if( bIteratorAreExclusive ) + PrintError(); + + int iVal = iNextRow1; + iNextRow1 = poIter1->GetNextRowSortedByFID(); + iNextRow2 = poIter2->GetNextRowSortedByFID(); + return iVal; +} + +/************************************************************************/ +/* GetRowCount() */ +/************************************************************************/ + +int FileGDBOrIterator::GetRowCount() +{ + if( bIteratorAreExclusive ) + return poIter1->GetRowCount() + poIter2->GetRowCount(); + else + return FileGDBIterator::GetRowCount(); +} + +/************************************************************************/ +/* FileGDBIndexIterator() */ +/************************************************************************/ + +FileGDBIndexIterator::FileGDBIndexIterator(FileGDBTable* poParent, int bAscending) : + poParent(poParent), bAscending(bAscending), + fpCurIdx(NULL), eFieldType(FGFT_UNDEFINED), + nMaxPerPages(0), nOffsetFirstValInPage(0), + nValueCountInIdx(0), nIndexDepth(0), eOp(FGSO_ISNOTNULL), + iCurFeatureInPage(-1), nFeaturesInPage(0), + bEvaluateToFALSE(FALSE), bEOF(FALSE), + iSorted(0), nSortedCount(-1), panSortedRows(NULL), + nStrLen(0) +{ + memset(iFirstPageIdx, 0xFF, MAX_DEPTH * sizeof(int)); + memset(iLastPageIdx, 0xFF, MAX_DEPTH * sizeof(int)); + memset(iCurPageIdx, 0xFF, MAX_DEPTH * sizeof(int)); + memset(nSubPagesCount, 0, MAX_DEPTH * sizeof(int)); + memset(nLastPageAccessed, 0, MAX_DEPTH * sizeof(int)); + memset(&sValue, 0, sizeof(sValue)); +} + +/************************************************************************/ +/* ~FileGDBIndexIterator() */ +/************************************************************************/ + +FileGDBIndexIterator::~FileGDBIndexIterator() +{ + if( fpCurIdx ) + VSIFCloseL(fpCurIdx); + fpCurIdx = NULL; + VSIFree(panSortedRows); +} + +/************************************************************************/ +/* Build() */ +/************************************************************************/ + +FileGDBIterator* FileGDBIndexIterator::Build( FileGDBTable* poParent, + int nFieldIdx, + int bAscending, + FileGDBSQLOp op, + OGRFieldType eOGRFieldType, + const OGRField* psValue ) +{ + FileGDBIndexIterator* poIndexIterator = + new FileGDBIndexIterator(poParent, bAscending); + if( poIndexIterator->SetConstraint(nFieldIdx, op, eOGRFieldType, psValue) ) + { + return poIndexIterator; + } + delete poIndexIterator; + return NULL; +} + +/************************************************************************/ +/* FileGDBSQLOpToStr() */ +/************************************************************************/ + +static const char* FileGDBSQLOpToStr(FileGDBSQLOp op) +{ + switch( op ) + { + case FGSO_ISNOTNULL : return "IS NOT NULL"; + case FGSO_LT: return "<";; + case FGSO_LE: return "<="; + case FGSO_EQ: return "="; + case FGSO_GE: return ">="; + case FGSO_GT: return ">"; + } + return "unknown_op"; +} + +/************************************************************************/ +/* FileGDBValueToStr() */ +/************************************************************************/ + +static const char* FileGDBValueToStr(OGRFieldType eOGRFieldType, + const OGRField* psValue) +{ + if( psValue == NULL ) + return ""; + + switch( eOGRFieldType ) + { + case OFTInteger: return CPLSPrintf("%d", psValue->Integer); + case OFTReal: return CPLSPrintf("%.18g", psValue->Real); + case OFTString: return psValue->String; + case OFTDateTime: return CPLSPrintf("%04d/%02d/%02d %02d:%02d:%02d", + psValue->Date.Year, + psValue->Date.Month, + psValue->Date.Day, + psValue->Date.Hour, + psValue->Date.Minute, + (int)psValue->Date.Second); + case OFTDate: return CPLSPrintf("%04d/%02d/%02d", + psValue->Date.Year, + psValue->Date.Month, + psValue->Date.Day); + case OFTTime: return CPLSPrintf("%02d:%02d:%02d", + psValue->Date.Hour, + psValue->Date.Minute, + (int)psValue->Date.Second); + default: + break; + } + return ""; +} + +/************************************************************************/ +/* SetConstraint() */ +/************************************************************************/ + +int FileGDBIndexIterator::SetConstraint(int nFieldIdx, + FileGDBSQLOp op, + OGRFieldType eOGRFieldType, + const OGRField* psValue) +{ + const int errorRetValue = FALSE; + CPLAssert(fpCurIdx == NULL); + + returnErrorIf(nFieldIdx < 0 || nFieldIdx >= poParent->GetFieldCount() ); + FileGDBField* poField = poParent->GetField(nFieldIdx); + returnErrorIf(!(poField->HasIndex()) ); + + eFieldType = poField->GetType(); + eOp = op; + + returnErrorIf(eFieldType != FGFT_INT16 && eFieldType != FGFT_INT32 && + eFieldType != FGFT_FLOAT32 && eFieldType != FGFT_FLOAT64 && + eFieldType != FGFT_STRING && eFieldType != FGFT_DATETIME && + eFieldType != FGFT_UUID_1 && eFieldType != FGFT_UUID_2 ); + + const char* pszAtxName = CPLFormFilename(CPLGetPath(poParent->GetFilename().c_str()), + CPLGetBasename(poParent->GetFilename().c_str()), CPLSPrintf("%s.atx", + poField->GetIndex()->GetIndexName().c_str())); + fpCurIdx = VSIFOpenL( pszAtxName, "rb" ); + returnErrorIf(fpCurIdx == NULL ); + + VSIFSeekL(fpCurIdx, 0, SEEK_END); + vsi_l_offset nFileSize = VSIFTellL(fpCurIdx); + returnErrorIf(nFileSize < FGDB_PAGE_SIZE + 22 ); + + VSIFSeekL(fpCurIdx, nFileSize - 22, SEEK_SET); + GByte abyTrailer[22]; + returnErrorIf(VSIFReadL( abyTrailer, 22, 1, fpCurIdx ) != 1 ); + + nMaxPerPages = (FGDB_PAGE_SIZE - 12) / (4 + abyTrailer[0]); + nOffsetFirstValInPage = 12 + nMaxPerPages * 4; + + GUInt32 nMagic1 = GetUInt32(abyTrailer + 2, 0); + returnErrorIf(nMagic1 != 1 ); + + nIndexDepth = GetUInt32(abyTrailer + 6, 0); + /* CPLDebug("OpenFileGDB", "nIndexDepth = %u", nIndexDepth); */ + returnErrorIf(!(nIndexDepth >= 1 && nIndexDepth <= MAX_DEPTH + 1) ); + + nValueCountInIdx = GetUInt32(abyTrailer + 10, 0); + /* CPLDebug("OpenFileGDB", "nValueCountInIdx = %u", nValueCountInIdx); */ + /* negative like in sample_clcV15_esri_v10.gdb/a00000005.FDO_UUID.atx */ + if( (int)nValueCountInIdx < 0 ) + return FALSE; + /* QGIS_TEST_101.gdb/a00000006.FDO_UUID.atx */ + if( nValueCountInIdx == 0 ) + { + VSIFSeekL(fpCurIdx, 4, SEEK_SET); + GByte abyBuffer[4]; + returnErrorIf(VSIFReadL( abyBuffer, 4, 1, fpCurIdx ) != 1 ); + nValueCountInIdx = GetUInt32(abyBuffer, 0); + } + /* PreNIS.gdb/a00000006.FDO_UUID.atx has depth 2 and the value of */ + /* nValueCountInIdx is 11 which is not the number of non-null values */ + else if( nValueCountInIdx < nMaxPerPages && nIndexDepth > 1 ) + return FALSE; + returnErrorIf(nValueCountInIdx > (GUInt32)poParent->GetValidRecordCount() ); + + switch( eFieldType ) + { + case FGFT_INT16: + returnErrorIf(abyTrailer[0] != sizeof(GUInt16)); + if( eOp != FGSO_ISNOTNULL ) + { + returnErrorIf(eOGRFieldType != OFTInteger); + sValue.Integer = psValue->Integer; + } + break; + case FGFT_INT32: + returnErrorIf(abyTrailer[0] != sizeof(GUInt32)); + if( eOp != FGSO_ISNOTNULL ) + { + returnErrorIf(eOGRFieldType != OFTInteger); + sValue.Integer = psValue->Integer; + } + break; + case FGFT_FLOAT32: + returnErrorIf(abyTrailer[0] != sizeof(float)); + if( eOp != FGSO_ISNOTNULL ) + { + returnErrorIf(eOGRFieldType != OFTReal); + sValue.Real = psValue->Real; + } + break; + case FGFT_FLOAT64: + returnErrorIf(abyTrailer[0] != sizeof(double)); + if( eOp != FGSO_ISNOTNULL ) + { + returnErrorIf(eOGRFieldType != OFTReal); + sValue.Real = psValue->Real; + } + break; + case FGFT_STRING: + { + returnErrorIf((abyTrailer[0] % 2) != 0); + returnErrorIf(abyTrailer[0] == 0); + returnErrorIf(abyTrailer[0] > 2 * MAX_CAR_COUNT_STR); + nStrLen = abyTrailer[0] / 2; + if( eOp != FGSO_ISNOTNULL ) + { + returnErrorIf(eOGRFieldType != OFTString); + wchar_t *pWide = CPLRecodeToWChar( psValue->String, + CPL_ENC_UTF8, + CPL_ENC_UCS2 ); + returnErrorIf(pWide == NULL); + int nCount = 0; + while( pWide[nCount] != 0 ) + { + returnErrorAndCleanupIf(nCount == nStrLen, CPLFree(pWide)); + asUTF16Str[nCount] = pWide[nCount]; + nCount ++; + } + while( nCount < nStrLen ) + { + asUTF16Str[nCount] = 32; /* space character */ + nCount ++; + } + CPLFree(pWide); + } + break; + } + + case FGFT_DATETIME: + { + returnErrorIf( abyTrailer[0] != sizeof(double)); + if( eOp != FGSO_ISNOTNULL ) + { + returnErrorIf(eOGRFieldType != OFTReal && + eOGRFieldType != OFTDateTime && + eOGRFieldType != OFTDate && + eOGRFieldType != OFTTime); + if( eOGRFieldType == OFTReal ) + sValue.Real = psValue->Real; + else + FileGDBOGRDateToDoubleDate(psValue, &(sValue.Real)); + } + break; + } + + case FGFT_UUID_1: + case FGFT_UUID_2: + { + returnErrorIf(abyTrailer[0] != UUID_LEN_AS_STRING); + if( eOp != FGSO_ISNOTNULL ) + { + returnErrorIf(eOGRFieldType != OFTString); + memset(szUUID, 0, UUID_LEN_AS_STRING + 1); + strncpy(szUUID, psValue->String, UUID_LEN_AS_STRING); + bEvaluateToFALSE = (eOp == FGSO_EQ && + strlen(psValue->String) != UUID_LEN_AS_STRING); + } + break; + } + + default: + CPLAssert(FALSE); + break; + } + + if( nValueCountInIdx > 0 ) + { + if( nIndexDepth == 1 ) + { + iFirstPageIdx[0] = iLastPageIdx[0] = 0; + } + else + { + returnErrorIf(!FindPages(0, 1) ); + } + } + + CPLDebug("OpenFileGDB", "Using index on field %s (%s %s)", + poField->GetName().c_str(), + FileGDBSQLOpToStr(eOp), + FileGDBValueToStr(eOGRFieldType, psValue)); + + Reset(); + + return TRUE; +} + +/************************************************************************/ +/* FileGDBUTF16StrCompare() */ +/************************************************************************/ + +static int FileGDBUTF16StrCompare(const GUInt16* pasFirst, + const GUInt16* pasSecond, + int nStrLen) +{ + for(int i=0;i pasSecond[i] ) + return 1; + } + return 0; +} + +/************************************************************************/ +/* COMPARE() */ +/************************************************************************/ + +#define COMPARE(a,b) (((a)<(b)) ? -1 : ((a)==(b)) ? 0 : 1) + +/************************************************************************/ +/* FindPages() */ +/************************************************************************/ + +int FileGDBIndexIterator::FindPages(int iLevel, int nPage) +{ + const int errorRetValue = FALSE; + VSIFSeekL(fpCurIdx, (nPage - 1) * FGDB_PAGE_SIZE, SEEK_SET); + returnErrorIf(VSIFReadL( abyPage[iLevel], FGDB_PAGE_SIZE, 1, fpCurIdx ) != 1 ); + + nSubPagesCount[iLevel] = GetUInt32(abyPage[iLevel] + 4, 0); + returnErrorIf(nSubPagesCount[iLevel] == 0 || + nSubPagesCount[iLevel] > nMaxPerPages); + if( nIndexDepth == 2 ) + returnErrorIf(nValueCountInIdx > nMaxPerPages * (nSubPagesCount[0] + 1)); + + if( eOp == FGSO_ISNOTNULL ) + { + iFirstPageIdx[iLevel] = 0; + iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; + return TRUE; + } + + GUInt32 i; +#ifdef DEBUG_INDEX_CONSISTENCY + double dfLastMax = 0.0; + int nLastMax = 0; + GUInt16 asLastMax[MAX_CAR_COUNT_STR] = { 0 }; + char szLastMaxUUID[UUID_LEN_AS_STRING + 1] = { 0 }; +#endif + iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = -1; + + for( i = 0; i < nSubPagesCount[iLevel]; i ++ ) + { + int nComp; + + switch( eFieldType ) + { + case FGFT_INT16: + { + GInt16 nVal = GetInt16(abyPage[iLevel] + nOffsetFirstValInPage, i); +#ifdef DEBUG_INDEX_CONSISTENCY + returnErrorIf(i > 0 && nVal < nLastMax); + nLastMax = nVal; +#endif + nComp = COMPARE(sValue.Integer, nVal); + break; + } + + case FGFT_INT32: + { + GInt32 nVal = GetInt32(abyPage[iLevel] + nOffsetFirstValInPage, i); +#ifdef DEBUG_INDEX_CONSISTENCY + returnErrorIf(i > 0 && nVal < nLastMax); + nLastMax = nVal; +#endif + nComp = COMPARE(sValue.Integer, nVal); + break; + } + + case FGFT_FLOAT32: + { + float fVal = GetFloat32(abyPage[iLevel] + nOffsetFirstValInPage, i); +#ifdef DEBUG_INDEX_CONSISTENCY + returnErrorIf(i > 0 && fVal < dfLastMax); + dfLastMax = fVal; +#endif + nComp = COMPARE(sValue.Real, fVal); + break; + } + + case FGFT_FLOAT64: + case FGFT_DATETIME: + { + double dfVal = GetFloat64(abyPage[iLevel] + nOffsetFirstValInPage, i); +#ifdef DEBUG_INDEX_CONSISTENCY + returnErrorIf(i > 0 && dfVal < dfLastMax); + dfLastMax = dfVal; +#endif + nComp = COMPARE(sValue.Real, dfVal); + break; + } + + case FGFT_STRING: + { + GUInt16* pasMax; +#ifdef CPL_MSB + GUInt16 asMax[MAX_CAR_COUNT_STR]; + pasMax = asMax; + memcpy(asMax, abyPage[iLevel] + nOffsetFirstValInPage + + nStrLen * sizeof(GUInt16) * i, nStrLen * sizeof(GUInt16)); + for(int j=0;j 0 && + FileGDBUTF16StrCompare(pasMax, asLastMax, nStrLen) < 0); + memcpy(asLastMax, pasMax, nStrLen * 2); +#endif + nComp = FileGDBUTF16StrCompare(asUTF16Str, pasMax, nStrLen); + break; + } + + case FGFT_UUID_1: + case FGFT_UUID_2: + { + const char* psNonzMaxUUID = (char*)(abyPage[iLevel] + + nOffsetFirstValInPage + UUID_LEN_AS_STRING * i); +#ifdef DEBUG_INDEX_CONSISTENCY + returnErrorIf(i > 0 && + memcmp(psNonzMaxUUID, szLastMaxUUID, UUID_LEN_AS_STRING) < 0); + memcpy(szLastMaxUUID, psNonzMaxUUID, UUID_LEN_AS_STRING); +#endif + nComp = memcmp(szUUID, psNonzMaxUUID, UUID_LEN_AS_STRING); + break; + } + + default: + CPLAssert(FALSE); + nComp = 0; + break; + } + + int bStop = FALSE; + switch( eOp ) + { + /* dfVal = 1 2 2 3 3 4 */ + /* sValue.Real = 3 */ + /* nComp = (sValue.Real < dfVal) ? -1 : (sValue.Real == dfVal) ? 0 : 1; */ + case FGSO_LT: + case FGSO_LE: + if( iFirstPageIdx[iLevel] < 0 ) + { + iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = (int)i; + } + else + { + iLastPageIdx[iLevel] = (int)i; + if( nComp < 0 ) + { + bStop = TRUE; + } + } + break; + + case FGSO_EQ: + if( iFirstPageIdx[iLevel] < 0 ) + { + if( nComp <= 0 ) + iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = (int)i; + } + else + { + if( nComp == 0 ) + iLastPageIdx[iLevel] = (int)i; + else + bStop = TRUE; + } + break; + + case FGSO_GE: + if( iFirstPageIdx[iLevel] < 0 ) + { + if( nComp <= 0 ) + { + iFirstPageIdx[iLevel] = (int)i; + iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; + bStop = TRUE; + } + } + break; + + case FGSO_GT: + if( iFirstPageIdx[iLevel] < 0 ) + { + if( nComp < 0 ) + { + iFirstPageIdx[iLevel] = (int)i; + iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; + bStop = TRUE; + } + } + break; + + default: + CPLAssert(FALSE); + break; + } + if( bStop ) + break; + } + + if( iFirstPageIdx[iLevel] < 0 ) + { + iFirstPageIdx[iLevel] = iLastPageIdx[iLevel] = nSubPagesCount[iLevel]; + } + else if( iLastPageIdx[iLevel] < (int)nSubPagesCount[iLevel] ) + { + iLastPageIdx[iLevel] ++; + } + + return TRUE; +} + +/************************************************************************/ +/* Reset() */ +/************************************************************************/ + +void FileGDBIndexIterator::Reset() +{ + iCurPageIdx[0] = (bAscending) ? iFirstPageIdx[0] -1 : iLastPageIdx[0] + 1; + memset(iFirstPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(int)); + memset(iLastPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(int)); + memset(iCurPageIdx + 1, 0xFF, (MAX_DEPTH - 1) * sizeof(int)); + memset(nLastPageAccessed, 0, MAX_DEPTH * sizeof(int)); + iCurFeatureInPage = 0; + nFeaturesInPage = 0; + iSorted = 0; + + bEOF = ( nValueCountInIdx == 0 || bEvaluateToFALSE ); +} + +/************************************************************************/ +/* ReadPageNumber() */ +/************************************************************************/ + +int FileGDBIndexIterator::ReadPageNumber(int iLevel) +{ + const int errorRetValue = 0; + GUInt32 nPage = GetUInt32(abyPage[iLevel] + 8, iCurPageIdx[iLevel]); + if( nPage == nLastPageAccessed[iLevel] ) + { + if( !LoadNextPage(iLevel) ) + return 0; + nPage = GetUInt32(abyPage[iLevel] + 8, iCurPageIdx[iLevel]); + } + nLastPageAccessed[iLevel] = nPage; + returnErrorIf(nPage < 2); + return nPage; +} + +/************************************************************************/ +/* LoadNextPage() */ +/************************************************************************/ + +int FileGDBIndexIterator::LoadNextPage(int iLevel) +{ + const int errorRetValue = FALSE; + if( (bAscending && iCurPageIdx[iLevel] == iLastPageIdx[iLevel]) || + (!bAscending && iCurPageIdx[iLevel] == iFirstPageIdx[iLevel]) ) + { + if( iLevel == 0 || !LoadNextPage(iLevel - 1) ) + return FALSE; + + GUInt32 nPage = ReadPageNumber(iLevel-1); + returnErrorIf(!FindPages(iLevel, nPage) ); + + iCurPageIdx[iLevel] = (bAscending) ? iFirstPageIdx[iLevel] : + iLastPageIdx[iLevel]; + } + else + { + if( bAscending ) + iCurPageIdx[iLevel] ++; + else + iCurPageIdx[iLevel] --; + } + + return TRUE; +} + +/************************************************************************/ +/* LoadNextFeaturePage() */ +/************************************************************************/ + +int FileGDBIndexIterator::LoadNextFeaturePage() +{ + const int errorRetValue = FALSE; + GUInt32 nPage; + + if( nIndexDepth == 1 ) + { + if( iCurPageIdx[0] == iLastPageIdx[0] ) + { + return FALSE; + } + if( bAscending ) + iCurPageIdx[0] ++; + else + iCurPageIdx[0] --; + nPage = 1; + } + else + { + if( !LoadNextPage( nIndexDepth - 2 ) ) + { + return FALSE; + } + nPage = ReadPageNumber(nIndexDepth - 2); + returnErrorIf(nPage < 2); + } + + VSIFSeekL(fpCurIdx, (nPage - 1) * FGDB_PAGE_SIZE, SEEK_SET); + returnErrorIf(VSIFReadL( abyPageFeature, FGDB_PAGE_SIZE, 1, fpCurIdx ) != 1); + + GUInt32 nFeatures = GetUInt32(abyPageFeature + 4, 0); + returnErrorIf(nFeatures > nMaxPerPages); + + nFeaturesInPage = (int)nFeatures; + iCurFeatureInPage = (bAscending) ? 0 : nFeaturesInPage - 1; + if( nFeatures == 0 ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* GetNextRow() */ +/************************************************************************/ + +int FileGDBIndexIterator::GetNextRow() +{ + const int errorRetValue = -1; + if( bEOF ) + return -1; + + while( TRUE ) + { + if( iCurFeatureInPage >= nFeaturesInPage || iCurFeatureInPage < 0 ) + { + if( !LoadNextFeaturePage() ) + { + bEOF = TRUE; + return -1; + } + } + + int bMatch; + if( eOp == FGSO_ISNOTNULL ) + { + bMatch = TRUE; + } + else + { + int nComp; + bMatch = FALSE; + switch( eFieldType ) + { + case FGFT_INT16: + { + GInt16 nVal = GetInt16(abyPageFeature + nOffsetFirstValInPage, + iCurFeatureInPage); + nComp = COMPARE(sValue.Integer, nVal); + break; + } + + case FGFT_INT32: + { + GInt32 nVal = GetInt32(abyPageFeature + nOffsetFirstValInPage, + iCurFeatureInPage); + nComp = COMPARE(sValue.Integer, nVal); + break; + } + + case FGFT_FLOAT32: + { + float fVal = GetFloat32(abyPageFeature + nOffsetFirstValInPage, + iCurFeatureInPage); + nComp = COMPARE(sValue.Real, fVal); + break; + } + + case FGFT_FLOAT64: + case FGFT_DATETIME: + { + double dfVal = GetFloat64(abyPageFeature + nOffsetFirstValInPage, + iCurFeatureInPage); + nComp = COMPARE(sValue.Real, dfVal); + break; + } + + case FGFT_STRING: + { +#ifdef CPL_MSB + GUInt16 asVal[MAX_CAR_COUNT_STR]; + memcpy(asVal, abyPageFeature + nOffsetFirstValInPage + + nStrLen * 2 * iCurFeatureInPage, nStrLen * 2); + for(int j=0;j (GUInt32)poParent->GetTotalRecordCount(), bEOF = TRUE); + return (int) (nFID - 1); + } + else + { + if( bAscending ) + iCurFeatureInPage ++; + else + iCurFeatureInPage --; + } + } +} + +/************************************************************************/ +/* SortRows() */ +/************************************************************************/ + +int FileGDBIndexIterator::SortRows() +{ + nSortedCount = 0; + iSorted = 0; + int nSortedAlloc = 0; + Reset(); + while( TRUE ) + { + int nRow = GetNextRow(); + if( nRow < 0 ) + break; + if( nSortedCount == nSortedAlloc ) + { + int nNewSortedAlloc = 4 * nSortedAlloc / 3 + 16; + int* panNewSortedRows = (int*)VSIRealloc(panSortedRows, + sizeof(int) * nNewSortedAlloc); + if( panNewSortedRows == NULL ) + { + nSortedCount = 0; + return FALSE; + } + nSortedAlloc = nNewSortedAlloc; + panSortedRows = panNewSortedRows; + } + panSortedRows[nSortedCount ++] = nRow; + } + if( nSortedCount == 0 ) + return FALSE; + std::sort(panSortedRows, panSortedRows + nSortedCount); +#ifdef nValueCountInIdx_reliable + if( eOp == FGSO_ISNOTNULL && (int)nValueCountInIdx != nSortedCount ) + PrintError(); +#endif + return TRUE; +} + +/************************************************************************/ +/* GetNextRowSortedByFID() */ +/************************************************************************/ + +int FileGDBIndexIterator::GetNextRowSortedByFID() +{ + if( eOp == FGSO_EQ ) + return GetNextRow(); + + if( iSorted < nSortedCount ) + return panSortedRows[iSorted ++]; + + if( nSortedCount < 0 ) + { + if( !SortRows() ) + return -1; + return panSortedRows[iSorted ++]; + } + else + { + return -1; + } +} + +/************************************************************************/ +/* GetRowCount() */ +/************************************************************************/ + +int FileGDBIndexIterator::GetRowCount() +{ + // The nValueCountInIdx value has been found to be unreliable when the index is built + // as features are inserted (and when they are not in increasing order) + // (with FileGDB SDK 1.3) + // So disable this optimization as there's no fast way to know + // if the value is reliable or not. +#ifdef nValueCountInIdx_reliable + if( eOp == FGSO_ISNOTNULL ) + return (int)nValueCountInIdx; +#endif + + if( nSortedCount >= 0 ) + return nSortedCount; + + int nRowCount = 0; + int bSaveAscending = bAscending; + bAscending = TRUE; /* for a tiny bit of more efficiency */ + Reset(); + while( GetNextRow() >= 0 ) + nRowCount ++; + bAscending = bSaveAscending; + Reset(); + return nRowCount; +} + +/************************************************************************/ +/* GetMinMaxValue() */ +/************************************************************************/ + +const OGRField* FileGDBIndexIterator::GetMinMaxValue(OGRField* psField, + int& eOutType, + int bIsMin) +{ + const OGRField* errorRetValue = NULL; + eOutType = -1; + if( nValueCountInIdx == 0 ) + return NULL; + + GByte abyPage[FGDB_PAGE_SIZE]; + GUInt32 nPage = 1; + for( GUInt32 iLevel = 0; iLevel < nIndexDepth - 1; iLevel ++ ) + { + VSIFSeekL(fpCurIdx, (nPage - 1) * FGDB_PAGE_SIZE, SEEK_SET); + returnErrorIf(VSIFReadL( abyPage, FGDB_PAGE_SIZE, 1, fpCurIdx ) != 1 ); + GUInt32 nSubPagesCount = GetUInt32(abyPage + 4, 0); + returnErrorIf(nSubPagesCount == 0 || nSubPagesCount > nMaxPerPages); + + if( bIsMin ) + nPage = GetUInt32(abyPage + 8, 0); + else + nPage = GetUInt32(abyPage + 8, nSubPagesCount); + returnErrorIf(nPage < 2 ); + } + + VSIFSeekL(fpCurIdx, (nPage - 1) * FGDB_PAGE_SIZE, SEEK_SET); + returnErrorIf(VSIFReadL( abyPage, FGDB_PAGE_SIZE, 1, fpCurIdx ) != 1); + + GUInt32 nFeatures = GetUInt32(abyPage + 4, 0); + returnErrorIf(nFeatures < 1 || nFeatures > nMaxPerPages); + + int iFeature = (bIsMin) ? 0 : nFeatures-1; + + switch( eFieldType ) + { + case FGFT_INT16: + { + GInt16 nVal = GetInt16(abyPage + nOffsetFirstValInPage, iFeature); + psField->Integer = nVal; + eOutType = OFTInteger; + return psField; + } + + case FGFT_INT32: + { + GInt32 nVal = GetInt32(abyPage + nOffsetFirstValInPage, iFeature); + psField->Integer = nVal; + eOutType = OFTInteger; + return psField; + } + + case FGFT_FLOAT32: + { + float fVal = GetFloat32(abyPage + nOffsetFirstValInPage, iFeature); + psField->Real = fVal; + eOutType = OFTReal; + return psField; + } + + case FGFT_FLOAT64: + { + double dfVal = GetFloat64(abyPage + nOffsetFirstValInPage, iFeature); + psField->Real = dfVal; + eOutType = OFTReal; + return psField; + } + + case FGFT_DATETIME: + { + double dfVal = GetFloat64(abyPage + nOffsetFirstValInPage, iFeature); + FileGDBDoubleDateToOGRDate(dfVal, psField); + eOutType = OFTDateTime; + return psField; + } + + case FGFT_STRING: + { + wchar_t awsVal[MAX_CAR_COUNT_STR+1]; + for(int j=0;j + MAX_UTF8_LEN_STR, VSIFree(pszOut) ); + strcpy(psField->String, pszOut); + CPLFree(pszOut); + eOutType = OFTString; + return psField; + } + + case FGFT_UUID_1: + case FGFT_UUID_2: + { + memcpy(psField->String, abyPage + nOffsetFirstValInPage + + UUID_LEN_AS_STRING *iFeature, UUID_LEN_AS_STRING); + psField->String[UUID_LEN_AS_STRING] = 0; + eOutType = OFTString; + return psField; + } + + default: + CPLAssert(FALSE); + break; + } + return NULL; +} + +/************************************************************************/ +/* GetMinValue() */ +/************************************************************************/ + +const OGRField* FileGDBIndexIterator::GetMinValue(int& eOutType) +{ + if( eOp != FGSO_ISNOTNULL ) + return FileGDBIterator::GetMinValue(eOutType); + if( eFieldType == FGFT_STRING || eFieldType == FGFT_UUID_1 || + eFieldType == FGFT_UUID_2 ) + sMin.String = szMin; + return GetMinMaxValue(&sMin, eOutType, TRUE); +} + +/************************************************************************/ +/* GetMaxValue() */ +/************************************************************************/ + +const OGRField* FileGDBIndexIterator::GetMaxValue(int& eOutType) +{ + if( eOp != FGSO_ISNOTNULL ) + return FileGDBIterator::GetMinValue(eOutType); + if( eFieldType == FGFT_STRING || eFieldType == FGFT_UUID_1 || + eFieldType == FGFT_UUID_2 ) + sMax.String = szMax; + return GetMinMaxValue(&sMax, eOutType, FALSE); +} + +/************************************************************************/ +/* GetMinMaxSumCount() */ +/************************************************************************/ + +struct Int16Getter +{ + public: + static double GetAsDouble(const GByte* pBaseAddr, int iOffset) + { + return GetInt16(pBaseAddr, iOffset); + } +}; + +struct Int32Getter +{ + public: + static double GetAsDouble(const GByte* pBaseAddr, int iOffset) + { + return GetInt32(pBaseAddr, iOffset); + } +}; + +struct Float32Getter +{ + public: + static double GetAsDouble(const GByte* pBaseAddr, int iOffset) + { + return GetFloat32(pBaseAddr, iOffset); + } +}; + +struct Float64Getter +{ + public: + static double GetAsDouble(const GByte* pBaseAddr, int iOffset) + { + return GetFloat64(pBaseAddr, iOffset); + } +}; + +template void FileGDBIndexIterator::GetMinMaxSumCount( + double& dfMin, double& dfMax, double& dfSum, int& nCount) +{ + int nLocalCount = 0; + double dfLocalSum = 0.0; + double dfVal = 0.0; + + while( TRUE ) + { + if( iCurFeatureInPage >= nFeaturesInPage ) + { + if( !LoadNextFeaturePage() ) + { + break; + } + } + + dfVal = Getter::GetAsDouble(abyPageFeature + nOffsetFirstValInPage, + iCurFeatureInPage); + + dfLocalSum += dfVal; + if( nLocalCount == 0 ) + dfMin = dfVal; + nLocalCount ++; + iCurFeatureInPage ++; + } + + dfSum = dfLocalSum; + nCount = nLocalCount; + dfMax = dfVal; +} + +int FileGDBIndexIterator::GetMinMaxSumCount(double& dfMin, double& dfMax, + double& dfSum, int& nCount) +{ + const int errorRetValue = FALSE; + dfMin = 0.0; + dfMax = 0.0; + dfSum = 0.0; + nCount = 0; + returnErrorIf(eOp != FGSO_ISNOTNULL ); + returnErrorIf(eFieldType != FGFT_INT16 && eFieldType != FGFT_INT32 && + eFieldType != FGFT_FLOAT32 && eFieldType != FGFT_FLOAT64 && + eFieldType != FGFT_DATETIME ); + + int bSaveAscending = bAscending; + bAscending = TRUE; + Reset(); + + switch( eFieldType ) + { + case FGFT_INT16: + { + GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount); + break; + } + case FGFT_INT32: + { + GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount); + break; + } + case FGFT_FLOAT32: + { + GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount); + break; + } + case FGFT_FLOAT64: + case FGFT_DATETIME: + { + GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount); + break; + } + default: + CPLAssert(FALSE); + break; + } + + bAscending = bSaveAscending; + Reset(); + + return TRUE; +} + +}; /* namespace OpenFileGDB */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/filegdbtable.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/filegdbtable.cpp new file mode 100644 index 000000000..bd9fb5fa0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/filegdbtable.cpp @@ -0,0 +1,2763 @@ +/****************************************************************************** + * $Id: filegdbtable.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements reading of FileGDB tables + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "filegdbtable_priv.h" +#include "cpl_string.h" +#include "cpl_time.h" +#include "ogrpgeogeometry.h" /* SHPT_ constants and OGRCreateFromMultiPatchPart() */ + +CPL_CVSID("$Id"); + +#define TEST_BIT(ar, bit) (ar[(bit) / 8] & (1 << ((bit) % 8))) +#define BIT_ARRAY_SIZE_IN_BYTES(bitsize) (((bitsize)+7)/8) + +#define UUID_SIZE_IN_BYTES 16 + +#define IS_VALID_LAYER_GEOM_TYPE(byVal) ((byVal) <= FGTGT_POLYGON || (byVal) == FGTGT_MULTIPATCH) + +/* Reserve one extra byte in case the last field is a string */ +/* or 2 for 2 ReadVarIntAndAddNoCheck() in a row */ +/* or 4 for SkipVarUInt() with nIter = 4 */ +/* or for 4 ReadVarUInt64NoCheck */ +#define ZEROES_AFTER_END_OF_BUFFER 4 + +namespace OpenFileGDB +{ + +/************************************************************************/ +/* FileGDBTablePrintError() */ +/************************************************************************/ + +void FileGDBTablePrintError(const char* pszFile, int nLineNumber) +{ + CPLError(CE_Failure, CPLE_AppDefined, "Error occured in %s at line %d", + pszFile, nLineNumber); +} + +/************************************************************************/ +/* FileGDBTable() */ +/************************************************************************/ + +FileGDBTable::FileGDBTable() +{ + Init(); +} + +/************************************************************************/ +/* ~FileGDBTable() */ +/************************************************************************/ + +FileGDBTable::~FileGDBTable() +{ + Close(); +} + +/************************************************************************/ +/* Init() */ +/************************************************************************/ + +void FileGDBTable::Init() +{ + osFilename = ""; + fpTable = NULL; + fpTableX = NULL; + nFileSize = 0; + memset(&sCurField, 0, sizeof(sCurField)); + bError = FALSE; + nCurRow = -1; + nLastCol = -1; + pabyIterVals = NULL; + iAccNullable = 0; + nRowBlobLength = 0; + /* eCurFieldType = OFTInteger; */ + eTableGeomType = FGTGT_NONE; + nValidRecordCount = 0; + nTotalRecordCount = 0; + iGeomField = -1; + nCountNullableFields = 0; + nNullableFieldsSizeInBytes = 0; + nBufferMaxSize = 0; + pabyBuffer = NULL; + nFilterXMin = 0; + nFilterXMax = 0; + nFilterYMin = 0; + nFilterYMax = 0; + osObjectIdColName = ""; + nChSaved = -1; + pabyTablXBlockMap = NULL; + nCountBlocksBeforeIBlockIdx = 0; + nCountBlocksBeforeIBlockValue = 0; + bHasReadGDBIndexes = FALSE; + nOffsetFieldDesc = 0; + nFieldDescLength = 0; + nTablxOffsetSize = 0; + anFeatureOffsets.resize(0); + nOffsetHeaderEnd = 0; + bHasDeletedFeaturesListed = FALSE; + bIsDeleted = FALSE; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +void FileGDBTable::Close() +{ + if( fpTable ) + VSIFCloseL(fpTable); + fpTable = NULL; + + if( fpTableX ) + VSIFCloseL(fpTableX); + fpTableX = NULL; + + CPLFree(pabyBuffer); + pabyBuffer = NULL; + + for(size_t i=0;iGetName() == osName ) + return (int)i; + } + return -1; +} + +/************************************************************************/ +/* ReadVarUInt() */ +/************************************************************************/ + +template < class OutType, class ControleType > +static int ReadVarUInt(GByte*& pabyIter, GByte* pabyEnd, OutType& nOutVal) +{ + const int errorRetValue = FALSE; + if( !(ControleType::check_bounds) ) + { + /* nothing */ + } + else if( ControleType::verbose_error ) + { + returnErrorIf(pabyIter >= pabyEnd); + } + else + { + if( pabyIter >= pabyEnd ) + return FALSE; + } + OutType b = *pabyIter; + if( (b & 0x80) == 0 ) + { + pabyIter ++; + nOutVal = b; + return TRUE; + } + GByte* pabyLocalIter = pabyIter + 1; + int nShift = 7; + OutType nVal = ( b & 0x7F ); + while(TRUE) + { + if( !(ControleType::check_bounds) ) + { + /* nothing */ + } + else if( ControleType::verbose_error ) + { + returnErrorIf(pabyLocalIter >= pabyEnd); + } + else + { + if( pabyLocalIter >= pabyEnd ) + return FALSE; + } + b = *pabyLocalIter; + pabyLocalIter ++; + nVal |= ( b & 0x7F ) << nShift; + if( (b & 0x80) == 0 ) + { + pabyIter = pabyLocalIter; + nOutVal = nVal; + return TRUE; + } + nShift += 7; + } +} + +struct ControleTypeVerboseErrorTrue +{ + static const bool check_bounds = true; + static const bool verbose_error = true; +}; + +struct ControleTypeVerboseErrorFalse +{ + static const bool check_bounds = true; + static const bool verbose_error = false; +}; + +struct ControleTypeNone +{ + static const bool check_bounds = false; + static const bool verbose_error = false; +}; + +static int ReadVarUInt32(GByte*& pabyIter, GByte* pabyEnd, GUInt32& nOutVal) +{ + return ReadVarUInt(pabyIter, pabyEnd, nOutVal); +} + +static void ReadVarUInt32NoCheck(GByte*& pabyIter, GUInt32& nOutVal) +{ + GByte* pabyEnd = NULL; + ReadVarUInt(pabyIter, pabyEnd, nOutVal); +} + +static int ReadVarUInt32Silent(GByte*& pabyIter, GByte* pabyEnd, GUInt32& nOutVal) +{ + return ReadVarUInt(pabyIter, pabyEnd, nOutVal); +} + +static void ReadVarUInt64NoCheck(GByte*& pabyIter, GUIntBig& nOutVal) +{ + GByte* pabyEnd = NULL; + ReadVarUInt(pabyIter, pabyEnd, nOutVal); +} + +/************************************************************************/ +/* IsLikelyFeatureAtOffset() */ +/************************************************************************/ + +int FileGDBTable::IsLikelyFeatureAtOffset(vsi_l_offset nOffset, + GUInt32* pnSize, + int* pbDeletedRecord) +{ + VSIFSeekL(fpTable, nOffset, SEEK_SET); + GByte abyBuffer[4]; + if( VSIFReadL(abyBuffer, 4, 1, fpTable) != 1 ) + return FALSE; + + nRowBlobLength = GetUInt32(abyBuffer, 0); + if( nRowBlobLength < (GUInt32)nNullableFieldsSizeInBytes || + nRowBlobLength > nFileSize - nOffset || + nRowBlobLength > INT_MAX - ZEROES_AFTER_END_OF_BUFFER || + nRowBlobLength > 10 * (nFileSize / nValidRecordCount) ) + { + /* Is it a deleted record ? */ + if( (int)nRowBlobLength < 0 && nRowBlobLength != 0x80000000U ) + { + nRowBlobLength = (GUInt32) (-(int)nRowBlobLength); + if( nRowBlobLength < (GUInt32)nNullableFieldsSizeInBytes || + nRowBlobLength > nFileSize - nOffset || + nRowBlobLength > INT_MAX - ZEROES_AFTER_END_OF_BUFFER || + nRowBlobLength > 10 * (nFileSize / nValidRecordCount) ) + return FALSE; + else + *pbDeletedRecord = TRUE; + } + else + return FALSE; + } + else + *pbDeletedRecord = FALSE; + + if( nRowBlobLength > nBufferMaxSize ) + { + GByte* pabyNewBuffer = (GByte*) VSIRealloc( pabyBuffer, + nRowBlobLength + ZEROES_AFTER_END_OF_BUFFER ); + if( pabyNewBuffer == NULL ) + return FALSE; + + pabyBuffer = pabyNewBuffer; + nBufferMaxSize = nRowBlobLength; + } + if( pabyBuffer == NULL ) return FALSE; /* to please Coverity. Not needed */ + if( nCountNullableFields > 0 ) + { + if( VSIFReadL(pabyBuffer, nNullableFieldsSizeInBytes, 1, fpTable) != 1 ) + return FALSE; + } + size_t i; + iAccNullable = 0; + int bExactSizeKnown = TRUE; + GUInt32 nRequiredLength = nNullableFieldsSizeInBytes; + for(i=0;ibNullable ) + { + int bIsNull = TEST_BIT(pabyBuffer, iAccNullable); + iAccNullable ++; + if( bIsNull ) + continue; + } + + switch( apoFields[i]->eType ) + { + case FGFT_STRING: + case FGFT_XML: + case FGFT_GEOMETRY: + case FGFT_BINARY: + { + nRequiredLength += 1; /* varuint32 so at least one byte */ + bExactSizeKnown = FALSE; + break; + } + + /* Only 4 bytes ? */ + case FGFT_RASTER: nRequiredLength += sizeof(GInt32); break; + + case FGFT_INT16: nRequiredLength += sizeof(GInt16); break; + case FGFT_INT32: nRequiredLength += sizeof(GInt32); break; + case FGFT_FLOAT32: nRequiredLength += sizeof(float); break; + case FGFT_FLOAT64: nRequiredLength += sizeof(double); break; + case FGFT_DATETIME: nRequiredLength += sizeof(double); break; + case FGFT_UUID_1: + case FGFT_UUID_2: nRequiredLength += UUID_SIZE_IN_BYTES; break; + + default: + CPLAssert(FALSE); + break; + } + } + if( !bExactSizeKnown ) + { + if( nRowBlobLength < nRequiredLength ) + return FALSE; + if( VSIFReadL(pabyBuffer + nNullableFieldsSizeInBytes, + nRowBlobLength - nNullableFieldsSizeInBytes, 1, fpTable) != 1 ) + return FALSE; + + iAccNullable = 0; + nRequiredLength = nNullableFieldsSizeInBytes; + for(i=0;ibNullable ) + { + int bIsNull = TEST_BIT(pabyBuffer, iAccNullable); + iAccNullable ++; + if( bIsNull ) + continue; + } + + switch( apoFields[i]->eType ) + { + case FGFT_STRING: + case FGFT_XML: + { + GByte* pabyIter = pabyBuffer + nRequiredLength; + GUInt32 nLength; + if( !ReadVarUInt32Silent(pabyIter, pabyBuffer + nRowBlobLength, nLength) || + pabyIter - (pabyBuffer + nRequiredLength) > 5 ) + return FALSE; + nRequiredLength = pabyIter - pabyBuffer; + if( nLength > nRowBlobLength - nRequiredLength ) + return FALSE; + for( GUInt32 j=0;j 5 ) + return FALSE; + nRequiredLength = pabyIter - pabyBuffer; + if( nLength > nRowBlobLength - nRequiredLength ) + return FALSE; + nRequiredLength += nLength; + break; + } + + /* Only 4 bytes ? */ + case FGFT_RASTER: nRequiredLength += sizeof(GInt32); break; + + case FGFT_INT16: nRequiredLength += sizeof(GInt16); break; + case FGFT_INT32: nRequiredLength += sizeof(GInt32); break; + case FGFT_FLOAT32: nRequiredLength += sizeof(float); break; + case FGFT_FLOAT64: nRequiredLength += sizeof(double); break; + case FGFT_DATETIME: nRequiredLength += sizeof(double); break; + case FGFT_UUID_1: + case FGFT_UUID_2: nRequiredLength += UUID_SIZE_IN_BYTES; break; + + default: + CPLAssert(FALSE); + break; + } + if( nRequiredLength > nRowBlobLength ) + return FALSE; + } + } + + *pnSize = 4 + nRequiredLength; + return nRequiredLength == nRowBlobLength; +} + +/************************************************************************/ +/* GuessFeatureLocations() */ +/************************************************************************/ + +#define MARK_DELETED(x) ((x) | (((GIntBig)1) << 63)) +#define IS_DELETED(x) (((x) & (((GIntBig)1) << 63)) != 0) +#define GET_OFFSET(x) ((x) & ~(((GIntBig)1) << 63)) + +int FileGDBTable::GuessFeatureLocations() +{ + VSIFSeekL(fpTable, 0, SEEK_END); + nFileSize = VSIFTellL(fpTable); + + int bReportDeletedFeatures = + CSLTestBoolean(CPLGetConfigOption("OPENFILEGDB_REPORT_DELETED_FEATURES", "NO")); + + vsi_l_offset nOffset = 40 + nFieldDescLength; + + if( nOffsetFieldDesc != 40 ) + { + /* Check if there is a deleted field description at offset 40 */ + GByte abyBuffer[14]; + VSIFSeekL(fpTable, 40, SEEK_SET); + if( VSIFReadL(abyBuffer, 14, 1, fpTable) != 1 ) + return FALSE; + int nSize = GetInt32(abyBuffer, 0); + int nVersion = GetInt32(abyBuffer + 4, 0); + if( nSize < 0 && -nSize < 1024 * 1024 && + (nVersion == 3 || nVersion == 4) && + IS_VALID_LAYER_GEOM_TYPE(abyBuffer[8]) && + abyBuffer[9] == 3 && abyBuffer[10] == 0 && abyBuffer[11] == 0 ) + { + nOffset = 40 + (-nSize); + } + else + { + nOffset = 40; + } + } + + int nInvalidRecords = 0; + while(nOffset < nFileSize) + { + GUInt32 nSize; + int bDeletedRecord; + if( !IsLikelyFeatureAtOffset(nOffset, &nSize, &bDeletedRecord) ) + { + nOffset ++; + } + else + { + /*CPLDebug("OpenFileGDB", "Feature found at offset %d (size = %d)", + nOffset, nSize);*/ + if( bDeletedRecord ) + { + if( bReportDeletedFeatures ) + { + bHasDeletedFeaturesListed = TRUE; + anFeatureOffsets.push_back(MARK_DELETED(nOffset)); + } + else + { + nInvalidRecords ++; + anFeatureOffsets.push_back(0); + } + } + else + anFeatureOffsets.push_back(nOffset); + nOffset += nSize; + } + } + nTotalRecordCount = (int) anFeatureOffsets.size(); + if( nTotalRecordCount - nInvalidRecords > nValidRecordCount ) + { + if( !bHasDeletedFeaturesListed ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "More features found (%d) than declared number of valid features (%d). " + "So deleted features will likely be reported.", + nTotalRecordCount - nInvalidRecords, nValidRecordCount); + } + nValidRecordCount = nTotalRecordCount - nInvalidRecords; + } + + return nTotalRecordCount > 0; +} + +/************************************************************************/ +/* ReadTableXHeader() */ +/************************************************************************/ + +int FileGDBTable::ReadTableXHeader() +{ + const int errorRetValue = FALSE; + GByte abyHeader[16]; + + // Read .gdbtablx file header + returnErrorIf(VSIFReadL( abyHeader, 16, 1, fpTableX ) != 1 ); + GUInt32 n1024Blocks = GetUInt32(abyHeader + 4, 0); + + nTotalRecordCount = GetInt32(abyHeader + 8, 0); + if( n1024Blocks == 0 ) + returnErrorIf(nTotalRecordCount != 0 ); + else + returnErrorIf(nTotalRecordCount < 0 ); + + nTablxOffsetSize = GetUInt32(abyHeader + 12, 0); + returnErrorIf(nTablxOffsetSize < 4 || nTablxOffsetSize > 6); + + if( n1024Blocks != 0 ) + { + GByte abyTrailer[16]; + + VSIFSeekL( fpTableX, nTablxOffsetSize * 1024 * (vsi_l_offset)n1024Blocks + 16, SEEK_SET ); + returnErrorIf(VSIFReadL( abyTrailer, 16, 1, fpTableX ) != 1 ); + + GUInt32 nMagic = GetUInt32(abyTrailer, 0); + + GUInt32 nBitsForBlockMap = GetUInt32(abyTrailer + 4, 0); + returnErrorIf(nBitsForBlockMap > INT_MAX / 1024); + + GUInt32 n1024BlocksBis = GetUInt32(abyTrailer + 8, 0); + returnErrorIf(n1024BlocksBis != n1024Blocks ); + + /* GUInt32 nMagic2 = GetUInt32(abyTrailer + 12, 0); */ + + if( nMagic == 0 ) + { + returnErrorIf(nBitsForBlockMap != n1024Blocks ); + /* returnErrorIf(nMagic2 != 0 ); */ + } + else + { + returnErrorIf((GUInt32)nTotalRecordCount > nBitsForBlockMap * 1024 ); +#ifdef DEBUG_VERBOSE + CPLDebug("OpenFileGDB", "%s .gdbtablx has block map array", + osFilename.c_str()); +#endif + + // Allocate a bit mask array for blocks of 1024 features. + int nSizeInBytes = BIT_ARRAY_SIZE_IN_BYTES(nBitsForBlockMap); + pabyTablXBlockMap = (GByte*) VSIMalloc( nSizeInBytes ); + returnErrorIf(pabyTablXBlockMap == NULL ); + returnErrorIf(VSIFReadL( pabyTablXBlockMap, nSizeInBytes, 1, fpTableX ) != 1 ); + /* returnErrorIf(nMagic2 == 0 ); */ + + // Check that the map is consistent with n1024Blocks + GUInt32 nCountBlocks = 0; + for(GUInt32 i=0;i 0 && + !CSLTestBoolean(CPLGetConfigOption("OPENFILEGDB_IGNORE_GDBTABLX", "FALSE")) ) + { + osTableXName = CPLFormFilename(CPLGetPath(pszFilename), + CPLGetBasename(pszFilename), "gdbtablx"); + fpTableX = VSIFOpenL( osTableXName, "rb" ); + if( fpTableX == NULL ) + { + const char* pszIgnoreGDBTablXAbsence = + CPLGetConfigOption("OPENFILEGDB_IGNORE_GDBTABLX_ABSENCE", NULL); + if( pszIgnoreGDBTablXAbsence == NULL ) + { + CPLError(CE_Warning, CPLE_AppDefined, "%s could not be found. " + "Trying to guess feature locations, but this might fail or " + "return incorrect results", osTableXName.c_str()); + } + else if( !CSLTestBoolean(pszIgnoreGDBTablXAbsence) ) + { + returnErrorIf(fpTableX == NULL ); + } + } + else if( !ReadTableXHeader() ) + return FALSE; + } + + if( fpTableX != NULL ) + { + if(nValidRecordCount > nTotalRecordCount ) + { + if( CSLTestBoolean(CPLGetConfigOption("OPENFILEGDB_USE_GDBTABLE_RECORD_COUNT", "FALSE")) ) + { + /* Potentially unsafe. See #5842 */ + CPLDebug("OpenFileGDB", "%s: nTotalRecordCount (was %d) forced to nValidRecordCount=%d", + osFilenameWithLayerName.c_str(), + nTotalRecordCount, nValidRecordCount); + nTotalRecordCount = nValidRecordCount; + } + else + { + /* By default err on the safe side */ + CPLError(CE_Warning, CPLE_AppDefined, + "File %s declares %d valid records, but %s declares " + "only %d total records. Using that later value for safety " + "(this possibly ignoring features). " + "You can also try setting OPENFILEGDB_IGNORE_GDBTABLX=YES to " + "completely ignore the .gdbtablx file (but possibly retrieving " + "deleted features), or set OPENFILEGDB_USE_GDBTABLE_RECORD_COUNT=YES " + "(but that setting can potentially cause crashes)", + osFilenameWithLayerName.c_str(), nValidRecordCount, + osTableXName.c_str(), nTotalRecordCount); + nValidRecordCount = nTotalRecordCount; + } + } + +#ifdef DEBUG_VERBOSE + else if( nTotalRecordCount != nValidRecordCount ) + { + CPLDebug("OpenFileGDB", "%s: nTotalRecordCount=%d nValidRecordCount=%d", + pszFilename, + nTotalRecordCount, nValidRecordCount); + } +#endif + } + + nOffsetFieldDesc = GetUInt32(abyHeader + 32, 0); + +#ifdef DEBUG_VERBOSE + if( nOffsetFieldDesc != 40 ) + { + CPLDebug("OpenFileGDB", "%s: nOffsetFieldDesc=%d", + pszFilename, nOffsetFieldDesc); + } +#endif + + // Skip to field description section + VSIFSeekL( fpTable, nOffsetFieldDesc, SEEK_SET ); + returnErrorIf(VSIFReadL( abyHeader, 14, 1, fpTable ) != 1 ); + nFieldDescLength = GetUInt32(abyHeader, 0); + + nOffsetHeaderEnd = nOffsetFieldDesc + nFieldDescLength; + + returnErrorIf(nFieldDescLength > 10 * 1024 * 1024 || nFieldDescLength < 10 ); + GByte byTableGeomType = abyHeader[8]; + if( IS_VALID_LAYER_GEOM_TYPE(byTableGeomType) ) + eTableGeomType = (FileGDBTableGeometryType) byTableGeomType; + else + CPLDebug("OpenFileGDB", "Unknown table geometry type: %d", byTableGeomType); + GUInt16 iField, nFields; + nFields = GetUInt16(abyHeader + 12, 0); + + /* No interest in guessing a trivial file */ + returnErrorIf( fpTableX == NULL && nFields == 0) ; + + GUInt32 nRemaining = nFieldDescLength - 10; + nBufferMaxSize = nRemaining; + pabyBuffer = (GByte*)VSIMalloc(nBufferMaxSize + ZEROES_AFTER_END_OF_BUFFER); + returnErrorIf(pabyBuffer == NULL ); + returnErrorIf(VSIFReadL(pabyBuffer, nRemaining, 1, fpTable) != 1 ); + + GByte* pabyIter = pabyBuffer; + for(iField = 0; iField < nFields; iField ++) + { + returnErrorIf(nRemaining < 1 ); + GByte nCarCount = pabyIter[0]; + + pabyIter ++; + nRemaining --; + returnErrorIf(nRemaining < (GUInt32)(2 * nCarCount + 1) ); + std::string osName; + for(int j=0;j FGFT_XML) + { + CPLDebug("OpenFileGDB", "Unhandled field type : %d", byFieldType); + returnError(); + } + + FileGDBFieldType eType = (FileGDBFieldType) byFieldType; + if( eType != FGFT_GEOMETRY && eType != FGFT_RASTER ) + { + GByte flags = 0; + int nMaxWidth = 0; + GUInt32 defaultValueLength = 0; + + switch( eType ) + { + case FGFT_STRING: + { + returnErrorIf(nRemaining < 6 ); + nMaxWidth = GetInt32(pabyIter, 0); + returnErrorIf(nMaxWidth < 0); + flags = pabyIter[4]; + pabyIter += 5; + nRemaining -= 5; + GByte* pabyIterBefore = pabyIter; + returnErrorIf(!ReadVarUInt32(pabyIter, pabyIter + nRemaining, defaultValueLength)); + nRemaining -= (pabyIter - pabyIterBefore); + break; + } + + case FGFT_OBJECTID: + case FGFT_BINARY: + case FGFT_UUID_1: + case FGFT_UUID_2: + case FGFT_XML: + returnErrorIf(nRemaining < 2 ); + flags = pabyIter[1]; + pabyIter += 2; + nRemaining -= 2; + break; + + default: + returnErrorIf(nRemaining < 3 ); + flags = pabyIter[1]; + defaultValueLength = pabyIter[2]; + pabyIter += 3; + nRemaining -= 3; + break; + } + + OGRField sDefault; + sDefault.Set.nMarker1 = OGRUnsetMarker; + sDefault.Set.nMarker2 = OGRUnsetMarker; + if( (flags & 4) != 0 ) + { + /* Default value */ + /* Found on PreNIS.gdb/a0000000d.gdbtable */ + returnErrorIf(nRemaining < defaultValueLength ); + if( defaultValueLength ) + { + if( eType == FGFT_STRING ) + { + sDefault.String = (char*)CPLMalloc(defaultValueLength+1); + memcpy(sDefault.String, pabyIter, defaultValueLength); + sDefault.String[defaultValueLength] = 0; + } + else if( eType == FGFT_INT16 && defaultValueLength == 2 ) + { + sDefault.Integer = GetInt16(pabyIter, 0); + sDefault.Set.nMarker2 = 0; + } + else if( eType == FGFT_INT32 && defaultValueLength == 4 ) + { + sDefault.Integer = GetInt32(pabyIter, 0); + sDefault.Set.nMarker2 = 0; + } + else if( eType == FGFT_FLOAT32 && defaultValueLength == 4 ) + { + sDefault.Real = GetFloat32(pabyIter, 0); + } + else if( eType == FGFT_FLOAT64 && defaultValueLength == 8 ) + { + sDefault.Real = GetFloat64(pabyIter, 0); + } + else if( eType == FGFT_DATETIME && defaultValueLength == 8 ) + { + double dfVal = GetFloat64(pabyIter, 0); + FileGDBDoubleDateToOGRDate(dfVal, &sDefault); + } + } + + pabyIter += defaultValueLength; + nRemaining -= defaultValueLength; + } + + if( eType == FGFT_OBJECTID ) + { + returnErrorIf(osObjectIdColName.size() > 0 ); + osObjectIdColName = osName; + continue; + } + + FileGDBField* poField = new FileGDBField(this); + poField->osName = osName; + poField->osAlias = osAlias; + poField->eType = eType; + poField->bNullable = (flags & 1); + poField->nMaxWidth = nMaxWidth; + poField->sDefault = sDefault; + apoFields.push_back(poField); + } + else + { + + FileGDBRasterField* poRasterField = NULL; + FileGDBGeomField* poField; + if( eType == FGFT_GEOMETRY ) + { + returnErrorIf(iGeomField >= 0 ); + poField = new FileGDBGeomField(this); + } + else + { + poRasterField = new FileGDBRasterField(this); + poField = poRasterField; + } + + poField->osName = osName; + poField->osAlias = osAlias; + poField->eType = eType; + if( eType == FGFT_GEOMETRY ) + iGeomField = (int)apoFields.size(); + apoFields.push_back(poField); + + returnErrorIf(nRemaining < 2 ); + GByte flags = pabyIter[1]; + poField->bNullable = (flags & 1); + pabyIter += 2; + nRemaining -= 2; + + if( eType == FGFT_RASTER ) + { + returnErrorIf(nRemaining < 1 ); + nCarCount = pabyIter[0]; + pabyIter ++; + nRemaining --; + returnErrorIf(nRemaining < (GUInt32)(2 * nCarCount + 1) ); + std::string osRasterColumn; + for(int j=0;josRasterColumnName = osRasterColumn; + } + + returnErrorIf(nRemaining < 2 ); + GUInt16 nLengthWKT = GetUInt16(pabyIter, 0); + pabyIter += sizeof(nLengthWKT); + nRemaining -= sizeof(nLengthWKT); + + returnErrorIf(nRemaining < (GUInt32)(1 + nLengthWKT) ); + for(int j=0;josWKT += pabyIter[2 * j]; + pabyIter += nLengthWKT; + nRemaining -= nLengthWKT; + + GByte abyGeomFlags = pabyIter[0]; + pabyIter ++; + nRemaining --; + poField->bHasM = (abyGeomFlags & 2) != 0; + poField->bHasZ = (abyGeomFlags & 4) != 0; + + if( eType == FGFT_GEOMETRY || abyGeomFlags > 0 ) + { + returnErrorIf( + nRemaining < (GUInt32)(sizeof(double) * ( 4 + (( eType == FGFT_GEOMETRY ) ? 4 : 0) + (poField->bHasM + poField->bHasZ) * 3 )) ); + + #define READ_DOUBLE(field) do { \ + field = GetFloat64(pabyIter, 0); \ + pabyIter += sizeof(double); \ + nRemaining -= sizeof(double); } while(0) + + READ_DOUBLE(poField->dfXOrigin); + READ_DOUBLE(poField->dfYOrigin); + READ_DOUBLE(poField->dfXYScale); + + if( poField->bHasM ) + { + READ_DOUBLE(poField->dfMOrigin); + READ_DOUBLE(poField->dfMScale); + } + + if( poField->bHasZ ) + { + READ_DOUBLE(poField->dfZOrigin); + READ_DOUBLE(poField->dfZScale); + } + + READ_DOUBLE(poField->dfXYTolerance); + + if( poField->bHasM ) + { + READ_DOUBLE(poField->dfMTolerance); + } + + if( poField->bHasZ ) + { + READ_DOUBLE(poField->dfZTolerance); + } + } + + if( eType == FGFT_RASTER ) + { + /* Always one byte at end ? */ + returnErrorIf(nRemaining < 1 ); + pabyIter += 1; + nRemaining -= 1; + } + else + { + READ_DOUBLE(poField->dfXMin); + READ_DOUBLE(poField->dfYMin); + READ_DOUBLE(poField->dfXMax); + READ_DOUBLE(poField->dfYMax); + + /* Purely empiric logic ! */ + /* Well, it seems that in practice there are 1 or 3 doubles */ + /* here. When there are 3, the first one is zmin and the second */ + /* one is zmax */ + int nCountDoubles = 0; + while( TRUE ) + { + returnErrorIf(nRemaining < 5 ); + + if( pabyIter[0] == 0x00 && pabyIter[1] >= 1 && pabyIter[1] <= 3 && + pabyIter[2] == 0x00 && pabyIter[3] == 0x00 && pabyIter[4] == 0x00 ) + { + GByte nToSkip = pabyIter[1]; + pabyIter += 5; + nRemaining -= 5; + returnErrorIf(nRemaining < (GUInt32)(nToSkip * 8) ); + nCountDoubles += nToSkip; + pabyIter += nToSkip * 8; + nRemaining -= nToSkip * 8; + break; + } + else + { + returnErrorIf(nRemaining < 8 ); + pabyIter += 8; + nRemaining -= 8; + nCountDoubles ++; + } + } + if( nCountDoubles == 3 ) + poField->bHas3D = TRUE; + } + } + + nCountNullableFields += apoFields[apoFields.size()-1]->bNullable; + } + nNullableFieldsSizeInBytes = BIT_ARRAY_SIZE_IN_BYTES(nCountNullableFields); + +#ifdef DEBUG_VERBOSE + if( nRemaining > 0 ) + { + CPLDebug("OpenFileGDB", "%u remaining (ignored) bytes in field header section", + nRemaining); + } +#endif + + if( nValidRecordCount > 0 && fpTableX == NULL ) + return GuessFeatureLocations(); + + return TRUE; +} + +/************************************************************************/ +/* SkipVarUInt() */ +/************************************************************************/ + +/* Bound check only valid if nIter <= 4 */ +static int SkipVarUInt(GByte*& pabyIter, GByte* pabyEnd, int nIter = 1) +{ + const int errorRetValue = FALSE; + GByte* pabyLocalIter = pabyIter; + returnErrorIf(pabyLocalIter /*+ nIter - 1*/ >= pabyEnd); + while( nIter -- > 0 ) + { + while(TRUE) + { + GByte b = *pabyLocalIter; + pabyLocalIter ++; + if( (b & 0x80) == 0 ) + break; + } + } + pabyIter = pabyLocalIter; + return TRUE; +} + +/************************************************************************/ +/* ReadVarIntAndAddNoCheck() */ +/************************************************************************/ + +static void ReadVarIntAndAddNoCheck(GByte*& pabyIter, GIntBig& nOutVal) +{ + GUInt32 b; + + b = *pabyIter; + GUIntBig nVal = (b & 0x3F); + int nSign = 1; + if( (b & 0x40) != 0 ) + nSign = -1; + if( (b & 0x80) == 0 ) + { + pabyIter ++; + nOutVal += nVal * nSign; + return; + } + + GByte* pabyLocalIter = pabyIter + 1; + int nShift = 6; + while(TRUE) + { + GUIntBig b = *pabyLocalIter; + pabyLocalIter ++; + nVal |= ( b & 0x7F ) << nShift; + if( (b & 0x80) == 0 ) + { + pabyIter = pabyLocalIter; + nOutVal += nVal * nSign; + return; + } + nShift += 7; + } +} + +/************************************************************************/ +/* GetOffsetInTableForRow() */ +/************************************************************************/ + +vsi_l_offset FileGDBTable::GetOffsetInTableForRow(int iRow) +{ + const int errorRetValue = 0; + returnErrorIf(iRow < 0 || iRow >= nTotalRecordCount ); + + bIsDeleted = FALSE; + if( fpTableX == NULL ) + { + bIsDeleted = IS_DELETED(anFeatureOffsets[iRow]); + return GET_OFFSET(anFeatureOffsets[iRow]); + } + + if( pabyTablXBlockMap != NULL ) + { + GUInt32 nCountBlocksBefore = 0; + int iBlock = iRow / 1024; + + // Check if the block is not empty + if( TEST_BIT(pabyTablXBlockMap, iBlock) == 0 ) + return 0; + + // In case of sequential reading, optimization to avoid recomputing + // the number of blocks since the beginning of the map + if( iBlock >= nCountBlocksBeforeIBlockIdx ) + { + nCountBlocksBefore = nCountBlocksBeforeIBlockValue; + for(int i=nCountBlocksBeforeIBlockIdx;i= nTotalRecordCount, nCurRow = -1 ); + + while( iRow < nTotalRecordCount ) + { + if( pabyTablXBlockMap != NULL && (iRow % 1024) == 0 ) + { + int iBlock = iRow / 1024; + if( TEST_BIT(pabyTablXBlockMap, iBlock) == 0 ) + { + int nBlocks = (nTotalRecordCount+1023)/1024; + do + { + iBlock ++; + } + while( iBlock < nBlocks && + TEST_BIT(pabyTablXBlockMap, iBlock) == 0 ); + + iRow = iBlock * 1024; + if( iRow >= nTotalRecordCount ) + return -1; + } + } + + if( SelectRow(iRow) ) + return iRow; + if( HasGotError() ) + return -1; + iRow ++; + } + + return -1; +} + +/************************************************************************/ +/* SelectRow() */ +/************************************************************************/ + +int FileGDBTable::SelectRow(int iRow) +{ + const int errorRetValue = FALSE; + returnErrorAndCleanupIf(iRow < 0 || iRow >= nTotalRecordCount, nCurRow = -1 ); + + if( nCurRow != iRow ) + { + vsi_l_offset nOffsetTable = GetOffsetInTableForRow(iRow); + if( nOffsetTable == 0 ) + { + nCurRow = -1; + return FALSE; + } + + VSIFSeekL(fpTable, nOffsetTable, SEEK_SET); + GByte abyBuffer[4]; + returnErrorAndCleanupIf( + VSIFReadL(abyBuffer, 4, 1, fpTable) != 1, nCurRow = -1 ); + + nRowBlobLength = GetUInt32(abyBuffer, 0); + if( bIsDeleted ) + { + nRowBlobLength = (GUInt32)(-(int)nRowBlobLength); + } + + if( !(apoFields.size() == 0 && nRowBlobLength == 0) ) + { + /* CPLDebug("OpenFileGDB", "nRowBlobLength = %u", nRowBlobLength); */ + returnErrorAndCleanupIf( + nRowBlobLength < (GUInt32)nNullableFieldsSizeInBytes || + nRowBlobLength > INT_MAX - ZEROES_AFTER_END_OF_BUFFER, nCurRow = -1 ); + + if( nRowBlobLength > nBufferMaxSize ) + { + /* For suspicious row blob length, check if we don't go beyond file size */ + if( nRowBlobLength > 100 * 1024 * 1024 ) + { + if( nFileSize == 0 ) + { + VSIFSeekL(fpTable, 0, SEEK_END); + nFileSize = VSIFTellL(fpTable); + VSIFSeekL(fpTable, nOffsetTable + 4, SEEK_SET); + } + returnErrorAndCleanupIf( nOffsetTable + 4 + nRowBlobLength > nFileSize, nCurRow = -1 ); + } + + GByte* pabyNewBuffer = (GByte*) VSIRealloc( pabyBuffer, + nRowBlobLength + ZEROES_AFTER_END_OF_BUFFER ); + returnErrorAndCleanupIf(pabyNewBuffer == NULL, nCurRow = -1 ); + + pabyBuffer = pabyNewBuffer; + nBufferMaxSize = nRowBlobLength; + } + returnErrorAndCleanupIf( + VSIFReadL(pabyBuffer, nRowBlobLength, 1, fpTable) != 1, nCurRow = -1 ); + /* Protection for 4 ReadVarUInt64NoCheck */ + CPLAssert(ZEROES_AFTER_END_OF_BUFFER == 4); + pabyBuffer[nRowBlobLength] = 0; + pabyBuffer[nRowBlobLength+1] = 0; + pabyBuffer[nRowBlobLength+2] = 0; + pabyBuffer[nRowBlobLength+3] = 0; + } + + nCurRow = iRow; + nLastCol = -1; + pabyIterVals = pabyBuffer + nNullableFieldsSizeInBytes; + iAccNullable = 0; + bError = FALSE; + nChSaved = -1; + } + + return TRUE; +} + +/************************************************************************/ +/* FileGDBDoubleDateToOGRDate() */ +/************************************************************************/ + +int FileGDBDoubleDateToOGRDate(double dfVal, OGRField* psField) +{ + struct tm brokendowntime; + + /* 25569 = Number of days between 1899/12/30 00:00:00 and 1970/01/01 00:00:00 */ + CPLUnixTimeToYMDHMS((GIntBig)((dfVal - 25569) * 3600 * 24), &brokendowntime); + + psField->Date.Year = (GInt16)(brokendowntime.tm_year + 1900); + psField->Date.Month = (GByte)brokendowntime.tm_mon + 1; + psField->Date.Day = (GByte)brokendowntime.tm_mday; + psField->Date.Hour = (GByte)brokendowntime.tm_hour; + psField->Date.Minute = (GByte)brokendowntime.tm_min; + psField->Date.Second = (float)brokendowntime.tm_sec; + psField->Date.TZFlag = 0; + psField->Date.Reserved = 0; + + return TRUE; +} + +/************************************************************************/ +/* GetFieldValue() */ +/************************************************************************/ + +const OGRField* FileGDBTable::GetFieldValue(int iCol) +{ + const OGRField* errorRetValue = NULL; + + returnErrorIf(nCurRow < 0 ); + returnErrorIf((GUInt32)iCol >= apoFields.size() ); + returnErrorIf(bError ); + + GByte* pabyEnd = pabyBuffer + nRowBlobLength; + + /* In case a string was previously read */ + if( nChSaved >= 0 ) + { + *pabyIterVals = (GByte)nChSaved; + nChSaved = -1; + } + + if( iCol <= nLastCol ) + { + nLastCol = -1; + pabyIterVals = pabyBuffer + nNullableFieldsSizeInBytes; + iAccNullable = 0; + } + + // Skip previous fields + for( int j = nLastCol + 1; j < iCol; j++ ) + { + if( apoFields[j]->bNullable ) + { + int bIsNull = TEST_BIT(pabyBuffer, iAccNullable); + iAccNullable ++; + if( bIsNull ) + continue; + } + + GUInt32 nLength; + switch( apoFields[j]->eType ) + { + case FGFT_STRING: + case FGFT_XML: + case FGFT_GEOMETRY: + case FGFT_BINARY: + { + if( !ReadVarUInt32(pabyIterVals, pabyEnd, nLength) ) + { + bError = TRUE; + returnError(); + } + break; + } + + /* Only 4 bytes ? */ + case FGFT_RASTER: nLength = sizeof(GInt32); break; + + case FGFT_INT16: nLength = sizeof(GInt16); break; + case FGFT_INT32: nLength = sizeof(GInt32); break; + case FGFT_FLOAT32: nLength = sizeof(float); break; + case FGFT_FLOAT64: nLength = sizeof(double); break; + case FGFT_DATETIME: nLength = sizeof(double); break; + case FGFT_UUID_1: + case FGFT_UUID_2: nLength = UUID_SIZE_IN_BYTES; break; + + default: + nLength = 0; + CPLAssert(FALSE); + break; + } + + if( nLength > (GUInt32)(pabyEnd - pabyIterVals) ) + { + bError = TRUE; + returnError(); + } + pabyIterVals += nLength; + } + + nLastCol = iCol; + + if( apoFields[iCol]->bNullable ) + { + int bIsNull = TEST_BIT(pabyBuffer, iAccNullable); + iAccNullable ++; + if( bIsNull ) + { + return NULL; + } + } + + switch( apoFields[iCol]->eType ) + { + case FGFT_STRING: + case FGFT_XML: + { + GUInt32 nLength; + if( !ReadVarUInt32(pabyIterVals, pabyEnd, nLength) ) + { + bError = TRUE; + returnError(); + } + if( nLength > (GUInt32)(pabyEnd - pabyIterVals) ) + { + bError = TRUE; + returnError(); + } + + /* eCurFieldType = OFTString; */ + sCurField.String = (char*) pabyIterVals; + pabyIterVals += nLength; + + /* This is a trick to avoid a alloc()+copy(). We null-terminate */ + /* after the string, and save the pointer and value to restore */ + nChSaved = *pabyIterVals; + *pabyIterVals = '\0'; + + /* CPLDebug("OpenFileGDB", "Field %d, row %d: %s", iCol, nCurRow, sCurField.String); */ + + break; + } + + case FGFT_INT16: + { + if( pabyIterVals + sizeof(GInt16) > pabyEnd ) + { + bError = TRUE; + returnError(); + } + + /* eCurFieldType = OFTInteger; */ + sCurField.Integer = GetInt16(pabyIterVals, 0); + + pabyIterVals += sizeof(GInt16); + /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d", iCol, nCurRow, sCurField.Integer); */ + + break; + } + + case FGFT_INT32: + { + if( pabyIterVals + sizeof(GInt32) > pabyEnd ) + { + bError = TRUE; + returnError(); + } + + /* eCurFieldType = OFTInteger; */ + sCurField.Integer = GetInt32(pabyIterVals, 0); + + pabyIterVals += sizeof(GInt32); + /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d", iCol, nCurRow, sCurField.Integer); */ + + break; + } + + case FGFT_FLOAT32: + { + if( pabyIterVals + sizeof(float) > pabyEnd ) + { + bError = TRUE; + returnError(); + } + + /* eCurFieldType = OFTReal; */ + sCurField.Real = GetFloat32(pabyIterVals, 0); + + pabyIterVals += sizeof(float); + /* CPLDebug("OpenFileGDB", "Field %d, row %d: %f", iCol, nCurRow, sCurField.Real); */ + + break; + } + + case FGFT_FLOAT64: + { + if( pabyIterVals + sizeof(double) > pabyEnd ) + { + bError = TRUE; + returnError(); + } + + /* eCurFieldType = OFTReal; */ + sCurField.Real = GetFloat64(pabyIterVals, 0); + + pabyIterVals += sizeof(double); + /* CPLDebug("OpenFileGDB", "Field %d, row %d: %f", iCol, nCurRow, sCurField.Real); */ + + break; + } + + case FGFT_DATETIME: + { + if( pabyIterVals + sizeof(double) > pabyEnd ) + { + bError = TRUE; + returnError(); + } + + /* Number of days since 1899/12/30 00:00:00 */ + double dfVal = GetFloat64(pabyIterVals, 0); + + FileGDBDoubleDateToOGRDate(dfVal, &sCurField); + /* eCurFieldType = OFTDateTime; */ + + pabyIterVals += sizeof(double); + + break; + } + + case FGFT_GEOMETRY: + case FGFT_BINARY: + { + GUInt32 nLength; + if( !ReadVarUInt32(pabyIterVals, pabyEnd, nLength) ) + { + bError = TRUE; + returnError(); + } + if( nLength > (GUInt32)(pabyEnd - pabyIterVals) ) + { + bError = TRUE; + returnError(); + } + + /* eCurFieldType = OFTBinary; */ + sCurField.Binary.nCount = nLength; + sCurField.Binary.paData = (GByte*) pabyIterVals; + + pabyIterVals += nLength; + + /* Null terminate binary in case it is used as a string */ + nChSaved = *pabyIterVals; + *pabyIterVals = '\0'; + + /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d bytes", iCol, nCurRow, snLength); */ + + break; + } + + /* Only 4 bytes ? */ + case FGFT_RASTER: + { + if( pabyIterVals + sizeof(GInt32) > pabyEnd ) + { + bError = TRUE; + returnError(); + } + + /* GInt32 nVal = GetInt32(pabyIterVals, 0); */ + + /* eCurFieldType = OFTBinary; */ + sCurField.Set.nMarker1 = OGRUnsetMarker; + sCurField.Set.nMarker2 = OGRUnsetMarker; + + pabyIterVals += sizeof(GInt32); + /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d", iCol, nCurRow, sCurField.Integer); */ + break; + } + + case FGFT_UUID_1: + case FGFT_UUID_2: + { + if( pabyIterVals + UUID_SIZE_IN_BYTES > pabyEnd ) + { + bError = TRUE; + returnError(); + } + + /* eCurFieldType = OFTString; */ + sCurField.String = achGUIDBuffer; + /*78563412BC9AF0DE1234567890ABCDEF --> {12345678-9ABC-DEF0-1234-567890ABCDEF} */ + sprintf(achGUIDBuffer, + "{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + pabyIterVals[3], pabyIterVals[2], pabyIterVals[1], pabyIterVals[0], + pabyIterVals[5], pabyIterVals[4], + pabyIterVals[7], pabyIterVals[6], + pabyIterVals[8], pabyIterVals[9], + pabyIterVals[10], pabyIterVals[11], pabyIterVals[12], + pabyIterVals[13], pabyIterVals[14], pabyIterVals[15]); + + pabyIterVals += UUID_SIZE_IN_BYTES; + /* CPLDebug("OpenFileGDB", "Field %d, row %d: %s", iCol, nCurRow, sCurField.String); */ + + break; + } + + default: + CPLAssert(FALSE); + break; + } + + if( iCol == (int)apoFields.size() - 1 && pabyIterVals < pabyEnd ) + { + CPLDebug("OpenFileGDB", "%d bytes remaining at end of record %d", + (int)(pabyEnd - pabyIterVals), nCurRow); + } + + return &sCurField; +} + +/************************************************************************/ +/* GetIndexCount() */ +/************************************************************************/ + +int FileGDBTable::GetIndexCount() +{ + const int errorRetValue = 0; + if( bHasReadGDBIndexes ) + return (int) apoIndexes.size(); + + bHasReadGDBIndexes = TRUE; + + const char* pszIndexesName = CPLFormFilename(CPLGetPath(osFilename.c_str()), + CPLGetBasename(osFilename.c_str()), "gdbindexes"); + VSILFILE* fpIndexes = VSIFOpenL( pszIndexesName, "rb" ); + VSIStatBufL sStat; + if( fpIndexes == NULL ) + { + if ( VSIStatExL( pszIndexesName, &sStat, VSI_STAT_EXISTS_FLAG) == 0 ) + returnError(); + else + return 0; + } + + VSIFSeekL(fpIndexes, 0, SEEK_END); + vsi_l_offset nFileSize = VSIFTellL(fpIndexes); + returnErrorAndCleanupIf(nFileSize > 1024 * 1024, VSIFCloseL(fpIndexes) ); + + GByte* pabyIdx = (GByte*)VSIMalloc((size_t)nFileSize); + returnErrorAndCleanupIf(pabyIdx == NULL, VSIFCloseL(fpIndexes) ); + + VSIFSeekL(fpIndexes, 0, SEEK_SET); + int nRead = (int)VSIFReadL( pabyIdx, (size_t)nFileSize, 1, fpIndexes ); + VSIFCloseL(fpIndexes); + returnErrorAndCleanupIf(nRead != 1, VSIFree(pabyIdx) ); + + GByte* pabyCur = pabyIdx; + GByte* pabyEnd = pabyIdx + nFileSize; + returnErrorAndCleanupIf(pabyEnd - pabyCur < 4, VSIFree(pabyIdx) ); + GUInt32 nIndexCount = GetUInt32(pabyCur, 0); + pabyCur += 4; + + // FileGDB v9 indexes structure not handled yet. Start with 13 98 85 03 + if( nIndexCount == 0x03859813 ) + { + CPLDebug("OpenFileGDB", ".gdbindexes v9 not handled yet"); + VSIFree(pabyIdx); + return 0; + } + returnErrorAndCleanupIf(nIndexCount >= (size_t)(GetFieldCount() + 1) * 10, VSIFree(pabyIdx) ); + + GUInt32 i; + for(i=0;i 1024, VSIFree(pabyIdx) ); + returnErrorAndCleanupIf((GUInt32)(pabyEnd - pabyCur) < 2 * nIdxNameCarCount, VSIFree(pabyIdx) ); + std::string osIndexName; + GUInt32 j; + for(j=0;j 1024, VSIFree(pabyIdx) ); + returnErrorAndCleanupIf((GUInt32)(pabyEnd - pabyCur) < 2 * nColNameCarCount, VSIFree(pabyIdx) ); + std::string osFieldName; + for(j=0;josIndexName = osIndexName; + poIndex->osFieldName = osFieldName; + apoIndexes.push_back(poIndex); + + if( osFieldName != osObjectIdColName ) + { + int nFieldIdx = GetFieldIdx(osFieldName); + if( nFieldIdx < 0 ) + { + CPLDebug("OpenFileGDB", + "Index defined for field %s that does not exist", + osFieldName.c_str()); + } + else + { + if( apoFields[nFieldIdx]->poIndex != NULL ) + { + CPLDebug("OpenFileGDB", + "There is already one index defined for field %s", + osFieldName.c_str()); + } + else + { + apoFields[nFieldIdx]->poIndex = poIndex; + } + } + } + } + + VSIFree(pabyIdx); + + return (int) apoIndexes.size(); +} + +/************************************************************************/ +/* InstallFilterEnvelope() */ +/************************************************************************/ + +#define MAX_GUINTBIG (~((GUIntBig)0)) + +void FileGDBTable::InstallFilterEnvelope(const OGREnvelope* psFilterEnvelope) +{ + if( psFilterEnvelope != NULL ) + { + CPLAssert( iGeomField >= 0 ); + FileGDBGeomField* poGeomField = (FileGDBGeomField*) GetField(iGeomField); + + /* We store the bounding box as unscaled coordinates, so that BBOX */ + /* intersection is done with integer comparisons */ + if( psFilterEnvelope->MinX >= poGeomField->dfXOrigin ) + nFilterXMin = (GUIntBig)(0.5 + (psFilterEnvelope->MinX - + poGeomField->dfXOrigin) * poGeomField->dfXYScale); + else + nFilterXMin = 0; + if( psFilterEnvelope->MaxX - poGeomField->dfXOrigin < + MAX_GUINTBIG / poGeomField->dfXYScale ) + nFilterXMax = (GUIntBig)(0.5 + (psFilterEnvelope->MaxX - + poGeomField->dfXOrigin) * poGeomField->dfXYScale); + else + nFilterXMax = MAX_GUINTBIG; + if( psFilterEnvelope->MinY >= poGeomField->dfYOrigin ) + nFilterYMin = (GUIntBig)(0.5 + (psFilterEnvelope->MinY - + poGeomField->dfYOrigin) * poGeomField->dfXYScale); + else + nFilterYMin = 0; + if( psFilterEnvelope->MaxY - poGeomField->dfYOrigin < + MAX_GUINTBIG / poGeomField->dfXYScale ) + nFilterYMax = (GUIntBig)(0.5 + (psFilterEnvelope->MaxY - + poGeomField->dfYOrigin) * poGeomField->dfXYScale); + else + nFilterYMax = MAX_GUINTBIG; + } + else + { + nFilterXMin = 0; + nFilterXMax = 0; + nFilterYMin = 0; + nFilterYMax = 0; + } +} + +/************************************************************************/ +/* GetFeatureExtent() */ +/************************************************************************/ + +int FileGDBTable::GetFeatureExtent(const OGRField* psField, + OGREnvelope* psOutFeatureEnvelope) +{ + const int errorRetValue = FALSE; + GByte* pabyCur = psField->Binary.paData; + GByte* pabyEnd = pabyCur + psField->Binary.nCount; + GUInt32 nGeomType; + int nToSkip = 0; + + CPLAssert( iGeomField >= 0 ); + FileGDBGeomField* poGeomField = (FileGDBGeomField*) GetField(iGeomField); + + ReadVarUInt32NoCheck(pabyCur, nGeomType); + + switch( (nGeomType & 0xff) ) + { + case SHPT_NULL: + return FALSE; + + case SHPT_POINTZ: + case SHPT_POINTZM: + case SHPT_POINT: + case SHPT_POINTM: + { + GUIntBig x, y; + ReadVarUInt64NoCheck(pabyCur, x); + x --; + ReadVarUInt64NoCheck(pabyCur, y); + y --; + psOutFeatureEnvelope->MinX = x / poGeomField->dfXYScale + poGeomField->dfXOrigin; + psOutFeatureEnvelope->MinY = y / poGeomField->dfXYScale + poGeomField->dfYOrigin; + psOutFeatureEnvelope->MaxX = psOutFeatureEnvelope->MinX; + psOutFeatureEnvelope->MaxY = psOutFeatureEnvelope->MinY; + return TRUE; + } + + case SHPT_MULTIPOINTZM: + case SHPT_MULTIPOINTZ: + case SHPT_MULTIPOINT: + case SHPT_MULTIPOINTM: + { + break; + } + + case SHPT_ARC: + case SHPT_ARCZ: + case SHPT_ARCZM: + case SHPT_ARCM: + case SHPT_POLYGON: + case SHPT_POLYGONZ: + case SHPT_POLYGONZM: + case SHPT_POLYGONM: + { + nToSkip = 1; + break; + } + case SHPT_GENERALPOLYLINE: + case SHPT_GENERALPOLYGON: + { + nToSkip = 1 + ((nGeomType & 0x20000000) ? 1 : 0); + break; + } + + case SHPT_GENERALMULTIPATCH: + case SHPT_MULTIPATCHM: + case SHPT_MULTIPATCH: + { + nToSkip = 2; + break; + } + + default: + return FALSE; + } + + GUInt32 nPoints; + ReadVarUInt32NoCheck(pabyCur, nPoints); + if( nPoints == 0 ) + return TRUE; + returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, nToSkip) ); + + GUIntBig vxmin, vymin, vdx, vdy; + + returnErrorIf(pabyCur >= pabyEnd); + ReadVarUInt64NoCheck(pabyCur, vxmin); + ReadVarUInt64NoCheck(pabyCur, vymin); + ReadVarUInt64NoCheck(pabyCur, vdx); + ReadVarUInt64NoCheck(pabyCur, vdy); + + psOutFeatureEnvelope->MinX = vxmin / poGeomField->dfXYScale + poGeomField->dfXOrigin; + psOutFeatureEnvelope->MinY = vymin / poGeomField->dfXYScale + poGeomField->dfYOrigin; + psOutFeatureEnvelope->MaxX = (vxmin + vdx) / poGeomField->dfXYScale + poGeomField->dfXOrigin; + psOutFeatureEnvelope->MaxY = (vymin + vdy) / poGeomField->dfXYScale + poGeomField->dfYOrigin; + + return TRUE; +} + +/************************************************************************/ +/* DoesGeometryIntersectsFilterEnvelope() */ +/************************************************************************/ + +int FileGDBTable::DoesGeometryIntersectsFilterEnvelope(const OGRField* psField) +{ + const int errorRetValue = TRUE; + GByte* pabyCur = psField->Binary.paData; + GByte* pabyEnd = pabyCur + psField->Binary.nCount; + GUInt32 nGeomType; + int nToSkip = 0; + + ReadVarUInt32NoCheck(pabyCur, nGeomType); + + switch( (nGeomType & 0xff) ) + { + case SHPT_NULL: + return TRUE; + + case SHPT_POINTZ: + case SHPT_POINTZM: + case SHPT_POINT: + case SHPT_POINTM: + { + GUIntBig x, y; + ReadVarUInt64NoCheck(pabyCur, x); + x --; + if( x < nFilterXMin || x > nFilterXMax ) + return FALSE; + ReadVarUInt64NoCheck(pabyCur, y); + y --; + return( y >= nFilterYMin && y <= nFilterYMax ); + } + + case SHPT_MULTIPOINTZM: + case SHPT_MULTIPOINTZ: + case SHPT_MULTIPOINT: + case SHPT_MULTIPOINTM: + { + break; + } + + case SHPT_ARC: + case SHPT_ARCZ: + case SHPT_ARCZM: + case SHPT_ARCM: + case SHPT_POLYGON: + case SHPT_POLYGONZ: + case SHPT_POLYGONZM: + case SHPT_POLYGONM: + { + nToSkip = 1; + break; + } + + case SHPT_GENERALPOLYLINE: + case SHPT_GENERALPOLYGON: + { + nToSkip = 1 + ((nGeomType & 0x20000000) ? 1 : 0); + break; + } + + case SHPT_GENERALMULTIPATCH: + case SHPT_MULTIPATCHM: + case SHPT_MULTIPATCH: + { + nToSkip = 2; + break; + } + + default: + return TRUE; + } + + GUInt32 nPoints; + ReadVarUInt32NoCheck(pabyCur, nPoints); + if( nPoints == 0 ) + return TRUE; + returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, nToSkip) ); + + GUIntBig vxmin, vymin, vdx, vdy; + + returnErrorIf(pabyCur >= pabyEnd); + ReadVarUInt64NoCheck(pabyCur, vxmin); + if( vxmin > nFilterXMax ) + return FALSE; + ReadVarUInt64NoCheck(pabyCur, vymin); + if( vymin > nFilterYMax ) + return FALSE; + ReadVarUInt64NoCheck(pabyCur, vdx); + if( vxmin + vdx < nFilterXMin ) + return FALSE; + ReadVarUInt64NoCheck(pabyCur, vdy); + return vymin + vdy >= nFilterYMin; +} + +/************************************************************************/ +/* FileGDBField() */ +/************************************************************************/ + +FileGDBField::FileGDBField(FileGDBTable* poParent) : + poParent(poParent), eType(FGFT_UNDEFINED), bNullable(FALSE), + nMaxWidth(0), poIndex(NULL) +{ + sDefault.Set.nMarker1 = OGRUnsetMarker; + sDefault.Set.nMarker2 = OGRUnsetMarker; +} + +/************************************************************************/ +/* ~FileGDBField() */ +/************************************************************************/ + +FileGDBField::~FileGDBField() +{ + if( eType == FGFT_STRING && + !(sDefault.Set.nMarker1 == OGRUnsetMarker && + sDefault.Set.nMarker2 == OGRUnsetMarker) ) + CPLFree(sDefault.String); +} + + +/************************************************************************/ +/* HasIndex() */ +/************************************************************************/ + +int FileGDBField::HasIndex() +{ + poParent->GetIndexCount(); + return poIndex != NULL; +} + +/************************************************************************/ +/* GetIndex() */ +/************************************************************************/ + +FileGDBIndex *FileGDBField::GetIndex() +{ + poParent->GetIndexCount(); + return poIndex; +} + +/************************************************************************/ +/* FileGDBGeomField() */ +/************************************************************************/ + +FileGDBGeomField::FileGDBGeomField(FileGDBTable* poParent) : + FileGDBField(poParent), bHasZ(FALSE), bHasM(FALSE), + dfXOrigin(0.0), dfYOrigin(0.0), dfXYScale(0.0), dfMOrigin(0.0), + dfMScale(0.0), dfZOrigin(0.0), dfZScale(0.0), dfXYTolerance(0.0), + dfMTolerance(0.0), dfZTolerance(0.0), dfXMin(0.0), dfYMin(0.0), + dfXMax(0.0), dfYMax(0.0), bHas3D(FALSE) +{ +} + +/************************************************************************/ +/* FileGDBOGRGeometryConverterImpl */ +/************************************************************************/ + +class FileGDBOGRGeometryConverterImpl : public FileGDBOGRGeometryConverter +{ + const FileGDBGeomField *poGeomField; + GUInt32 *panPointCount; + GUInt32 nPointCountMax; +#ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING + int bUseOrganize; +#endif + + int ReadPartDefs( GByte*& pabyCur, + GByte* pabyEnd, + GUInt32& nPoints, + GUInt32& nParts, + int bHasCurveDesc, + int bIsMultiPatch ); + template int ReadXYArray(XYSetter& setter, + GByte*& pabyCur, + GByte* pabyEnd, + GUInt32 nPoints, + GIntBig& dx, + GIntBig& dy); + template int ReadZArray(ZSetter& setter, + GByte*& pabyCur, + GByte* pabyEnd, + GUInt32 nPoints, + GIntBig& dz); + + public: + FileGDBOGRGeometryConverterImpl( + const FileGDBGeomField* poGeomField); + virtual ~FileGDBOGRGeometryConverterImpl(); + + virtual OGRGeometry* GetAsGeometry(const OGRField* psField); +}; + +/************************************************************************/ +/* FileGDBOGRGeometryConverterImpl() */ +/************************************************************************/ + +FileGDBOGRGeometryConverterImpl::FileGDBOGRGeometryConverterImpl( + const FileGDBGeomField* poGeomField) : + poGeomField(poGeomField) +{ + panPointCount = NULL; + nPointCountMax = 0; +#ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING + bUseOrganize = CPLGetConfigOption("OGR_ORGANIZE_POLYGONS", NULL) != NULL; +#endif +} + +/************************************************************************/ +/* ~FileGDBOGRGeometryConverter() */ +/************************************************************************/ + +FileGDBOGRGeometryConverterImpl::~FileGDBOGRGeometryConverterImpl() +{ + CPLFree(panPointCount); +} + +/************************************************************************/ +/* ReadPartDefs() */ +/************************************************************************/ + +int FileGDBOGRGeometryConverterImpl::ReadPartDefs( GByte*& pabyCur, + GByte* pabyEnd, + GUInt32& nPoints, + GUInt32& nParts, + int bHasCurveDesc, + int bIsMultiPatch ) +{ + const int errorRetValue = FALSE; + returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nPoints)); + if( nPoints == 0 ) + { + nParts = 0; + return TRUE; + } + returnErrorIf(nPoints > (GUInt32)(pabyEnd - pabyCur) ); + if( bIsMultiPatch ) + returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd) ); + returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nParts)); + returnErrorIf(nParts > (GUInt32)(pabyEnd - pabyCur)); + if( bHasCurveDesc ) + returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd) ); + if( nParts == 0 ) + return TRUE; + GUInt32 i; + returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, 4) ); + if( nParts > nPointCountMax ) + { + GUInt32* panPointCountNew = + (GUInt32*) VSIRealloc( panPointCount, nParts * sizeof(GUInt32) ); + returnErrorIf(panPointCountNew == NULL ); + panPointCount = panPointCountNew; + nPointCountMax = nParts; + } + GUIntBig nSumNPartsM1 = 0; + for(i=0;i (GUInt32)(pabyEnd - pabyCur) ); + panPointCount[i] = nTmp; + nSumNPartsM1 += nTmp; + } + returnErrorIf(nSumNPartsM1 > nPoints ); + panPointCount[nParts-1] = (GUInt32)(nPoints - nSumNPartsM1); + + return TRUE; +} + +/************************************************************************/ +/* XYLineStringSetter */ +/************************************************************************/ + +class FileGDBOGRLineString: public OGRLineString +{ + public: + FileGDBOGRLineString() {} + + OGRRawPoint * GetPoints() const { return paoPoints; } +}; + +class FileGDBOGRLinearRing: public OGRLinearRing +{ + public: + FileGDBOGRLinearRing() {} + + OGRRawPoint * GetPoints() const { return paoPoints; } +}; + +class XYLineStringSetter +{ + OGRRawPoint* paoPoints; + public: + XYLineStringSetter(OGRRawPoint* paoPoints) : paoPoints(paoPoints) {} + + void set(int i, double dfX, double dfY) + { + paoPoints[i].x = dfX; + paoPoints[i].y = dfY; + } +}; + +/************************************************************************/ +/* XYMultiPointSetter */ +/************************************************************************/ + +class XYMultiPointSetter +{ + OGRMultiPoint* poMPoint; + public: + XYMultiPointSetter(OGRMultiPoint* poMPoint) : poMPoint(poMPoint) {} + + void set(int i, double dfX, double dfY) + { + (void)i; + poMPoint->addGeometryDirectly(new OGRPoint(dfX, dfY)); + } +}; + +/************************************************************************/ +/* XYArraySetter */ +/************************************************************************/ + +class XYArraySetter +{ + double* padfX; + double* padfY; + public: + XYArraySetter(double* padfX, double* padfY) : padfX(padfX), padfY(padfY) {} + + void set(int i, double dfX, double dfY) + { + padfX[i] = dfX; + padfY[i] = dfY; + } +}; + +/************************************************************************/ +/* ReadXYArray() */ +/************************************************************************/ + +template int FileGDBOGRGeometryConverterImpl::ReadXYArray(XYSetter& setter, + GByte*& pabyCur, + GByte* pabyEnd, + GUInt32 nPoints, + GIntBig& dx, + GIntBig& dy) +{ + const int errorRetValue = FALSE; + GIntBig dxLocal = dx; + GIntBig dyLocal = dy; + + for(GUInt32 i = 0; i < nPoints; i++ ) + { + returnErrorIf(pabyCur /*+ 1*/ >= pabyEnd); + + ReadVarIntAndAddNoCheck(pabyCur, dxLocal); + ReadVarIntAndAddNoCheck(pabyCur, dyLocal); + + double dfX = dxLocal / poGeomField->GetXYScale() + poGeomField->GetXOrigin(); + double dfY = dyLocal / poGeomField->GetXYScale() + poGeomField->GetYOrigin(); + setter.set(i, dfX, dfY); + } + + dx = dxLocal; + dy = dyLocal; + return TRUE; +} + +/************************************************************************/ +/* ZLineStringSetter */ +/************************************************************************/ + +class ZLineStringSetter +{ + OGRLineString* poLS; + public: + ZLineStringSetter(OGRLineString* poLS) : poLS(poLS) {} + + void set(int i, double dfZ) + { + poLS->setZ(i, dfZ); + } +}; + +/************************************************************************/ +/* ZMultiPointSetter */ +/************************************************************************/ + +class ZMultiPointSetter +{ + OGRMultiPoint* poMPoint; + public: + ZMultiPointSetter(OGRMultiPoint* poMPoint) : poMPoint(poMPoint) {} + + void set(int i, double dfZ) + { + ((OGRPoint*)poMPoint->getGeometryRef(i))->setZ(dfZ); + } +}; + +/************************************************************************/ +/* ZArraySetter */ +/************************************************************************/ + +class ZArraySetter +{ + double* padfZ; + public: + ZArraySetter(double* padfZ) : padfZ(padfZ) {} + + void set(int i, double dfZ) + { + padfZ[i] = dfZ; + } +}; + +/************************************************************************/ +/* ReadZArray() */ +/************************************************************************/ + +template int FileGDBOGRGeometryConverterImpl::ReadZArray(ZSetter& setter, + GByte*& pabyCur, + GByte* pabyEnd, + GUInt32 nPoints, + GIntBig& dz) +{ + const int errorRetValue = FALSE; + for(GUInt32 i = 0; i < nPoints; i++ ) + { + returnErrorIf(pabyCur >= pabyEnd); + ReadVarIntAndAddNoCheck(pabyCur, dz); + + double dfZ = dz / poGeomField->GetZScale() + poGeomField->GetZOrigin(); + setter.set(i, dfZ); + } + return TRUE; +} + +/************************************************************************/ +/* GetAsGeometry() */ +/************************************************************************/ + +OGRGeometry* FileGDBOGRGeometryConverterImpl::GetAsGeometry(const OGRField* psField) +{ + OGRGeometry* errorRetValue = NULL; + GByte* pabyCur = psField->Binary.paData; + GByte* pabyEnd = pabyCur + psField->Binary.nCount; + GUInt32 nGeomType, i, nPoints, nParts; + GUIntBig x, y, z; + GIntBig dx, dy, dz; + + ReadVarUInt32NoCheck(pabyCur, nGeomType); + + int bHasZ = (nGeomType & 0x80000000) != 0; + switch( (nGeomType & 0xff) ) + { + case SHPT_NULL: + return NULL; + + case SHPT_POINTZ: + case SHPT_POINTZM: + bHasZ = TRUE; /* go on */ + case SHPT_POINT: + case SHPT_POINTM: + { + double dfX, dfY, dfZ; + ReadVarUInt64NoCheck(pabyCur, x); + ReadVarUInt64NoCheck(pabyCur, y); + + dfX = (x - 1) / poGeomField->GetXYScale() + poGeomField->GetXOrigin(); + dfY = (y - 1) / poGeomField->GetXYScale() + poGeomField->GetYOrigin(); + if( bHasZ ) + { + ReadVarUInt64NoCheck(pabyCur, z); + dfZ = (z - 1) / poGeomField->GetZScale() + poGeomField->GetZOrigin(); + return new OGRPoint(dfX, dfY, dfZ); + } + else + { + return new OGRPoint(dfX, dfY); + } + break; + } + + case SHPT_MULTIPOINTZM: + case SHPT_MULTIPOINTZ: + bHasZ = TRUE; /* go on */ + case SHPT_MULTIPOINT: + case SHPT_MULTIPOINTM: + { + returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nPoints) ); + if( nPoints == 0 ) + { + OGRMultiPoint* poMP = new OGRMultiPoint(); + if( bHasZ ) + poMP->setCoordinateDimension(3); + return poMP; + } + + returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, 4) ); + + dx = dy = dz = 0; + + OGRMultiPoint* poMP = new OGRMultiPoint(); + XYMultiPointSetter mpSetter(poMP); + if( !ReadXYArray(mpSetter, + pabyCur, pabyEnd, nPoints, dx, dy) ) + { + delete poMP; + returnError(); + } + + if( bHasZ ) + { + poMP->setCoordinateDimension(3); + ZMultiPointSetter mpzSetter(poMP); + if( !ReadZArray(mpzSetter, + pabyCur, pabyEnd, nPoints, dz) ) + { + delete poMP; + returnError(); + } + } + + return poMP; + break; + } + + case SHPT_ARCZ: + case SHPT_ARCZM: + bHasZ = TRUE; /* go on */ + case SHPT_ARC: + case SHPT_ARCM: + case SHPT_GENERALPOLYLINE: + { + returnErrorIf(!ReadPartDefs(pabyCur, pabyEnd, nPoints, nParts, + (nGeomType & 0x20000000) != 0, + FALSE) ); + + if( nPoints == 0 || nParts == 0 ) + { + OGRLineString* poLS = new OGRLineString(); + if( bHasZ ) + poLS->setCoordinateDimension(3); + return poLS; + } + + OGRMultiLineString* poMLS = NULL; + FileGDBOGRLineString* poLS = NULL; + if( nParts > 1 ) + poMLS = new OGRMultiLineString(); + + dx = dy = dz = 0; + for(i=0;isetNumPoints(panPointCount[i], FALSE); + if( nParts > 1 ) + poMLS->addGeometryDirectly(poLS); + + XYLineStringSetter lsSetter(poLS->GetPoints()); + if( !ReadXYArray(lsSetter, + pabyCur, pabyEnd, + panPointCount[i], + dx, dy) ) + { + if( nParts > 1 ) + delete poMLS; + else + delete poLS; + returnError(); + } + } + + if( bHasZ ) + { + for(i=0;i 1 ) + poLS = (FileGDBOGRLineString*) poMLS->getGeometryRef(i); + + ZLineStringSetter lszSetter(poLS); + if( !ReadZArray(lszSetter, + pabyCur, pabyEnd, + panPointCount[i], dz) ) + { + if( nParts > 1 ) + delete poMLS; + else + delete poLS; + returnError(); + } + } + } + + if( poMLS ) + return poMLS; + else + return poLS; + + break; + } + + case SHPT_POLYGONZ: + case SHPT_POLYGONZM: + bHasZ = TRUE; /* go on */ + case SHPT_POLYGON: + case SHPT_POLYGONM: + case SHPT_GENERALPOLYGON: + { + returnErrorIf(!ReadPartDefs(pabyCur, pabyEnd, nPoints, nParts, + (nGeomType & 0x20000000) != 0, + FALSE) ); + + if( nPoints == 0 || nParts == 0 ) + { + OGRPolygon* poPoly = new OGRPolygon(); + if( bHasZ ) + poPoly->setCoordinateDimension(3); + return poPoly; + } + + OGRLinearRing** papoRings = new OGRLinearRing*[nParts]; + + dx = dy = dz = 0; + for(i=0;isetNumPoints(panPointCount[i], FALSE); + + XYLineStringSetter lsSetter(poRing->GetPoints()); + if( !ReadXYArray(lsSetter, + pabyCur, pabyEnd, + panPointCount[i], + dx, dy) ) + { + while( (int)i >= 0 ) + delete papoRings[i--]; + delete[] papoRings; + returnError(); + } + } + + if( bHasZ ) + { + for(i=0;isetCoordinateDimension(3); + + ZLineStringSetter lszSetter(papoRings[i]); + if( !ReadZArray(lszSetter, + pabyCur, pabyEnd, + panPointCount[i], dz) ) + { + while( (int)i >= 0 ) + delete papoRings[i--]; + delete[] papoRings; + returnError(); + } + } + } + + OGRGeometry* poRet; + if( nParts == 1 ) + { + OGRPolygon* poPoly = new OGRPolygon(); + poRet = poPoly; + poPoly->addRingDirectly(papoRings[0]); + } + else +#ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING + if( bUseOrganize || !(papoRings[0]->isClockwise()) ) +#endif + { + /* Slow method : not used by default */ + OGRPolygon** papoPolygons = new OGRPolygon*[nParts]; + for(i=0;iaddRingDirectly(papoRings[i]); + } + delete[] papoRings; + papoRings = NULL; + const char* papszOptions[] = { "METHOD=ONLY_CCW", NULL }; + poRet = OGRGeometryFactory::organizePolygons( + (OGRGeometry**) papoPolygons, nParts, NULL, papszOptions ); + delete[] papoPolygons; + } +#ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING + else + { + /* Inner rings are CCW oriented and follow immediately the outer */ + /* ring (that is CW oriented) in which they are included */ + OGRMultiPolygon* poMulti = NULL; + OGRPolygon* poPoly = new OGRPolygon(); + OGRPolygon* poCur = poPoly; + poRet = poCur; + /* We have already checked that the first ring is CW */ + poPoly->addRingDirectly(papoRings[0]); + OGREnvelope sEnvelope; + papoRings[0]->getEnvelope(&sEnvelope); + for(i=1;iisClockwise(); + if( bIsCW ) + { + if( poMulti == NULL ) + { + poMulti = new OGRMultiPolygon(); + poRet = poMulti; + poMulti->addGeometryDirectly(poCur); + } + OGRPolygon* poPoly = new OGRPolygon(); + poCur = poPoly; + poMulti->addGeometryDirectly(poCur); + poPoly->addRingDirectly(papoRings[i]); + papoRings[i]->getEnvelope(&sEnvelope); + } + else + { + poCur->addRingDirectly(papoRings[i]); + OGRPoint oPoint; + papoRings[i]->getPoint(0, &oPoint); + CPLAssert(oPoint.getX() >= sEnvelope.MinX && + oPoint.getX() <= sEnvelope.MaxX && + oPoint.getY() >= sEnvelope.MinY && + oPoint.getY() <= sEnvelope.MaxY); + } + } + } +#endif + + delete[] papoRings; + return poRet; + + break; + } + + case SHPT_MULTIPATCHM: + case SHPT_MULTIPATCH: + bHasZ = TRUE; /* go on */ + case SHPT_GENERALMULTIPATCH: + { + returnErrorIf(!ReadPartDefs(pabyCur, pabyEnd, nPoints, nParts, FALSE, TRUE ) ); + + if( nPoints == 0 || nParts == 0 ) + { + OGRPolygon* poPoly = new OGRPolygon(); + if( bHasZ ) + poPoly->setCoordinateDimension(3); + return poPoly; + } + int* panPartType = (int*) VSIMalloc(sizeof(int) * nParts); + double* padfXYZ = (double*) VSIMalloc(3 * sizeof(double) * nPoints); + double* padfX = padfXYZ; + double* padfY = padfXYZ + nPoints; + double* padfZ = padfXYZ + 2 * nPoints; + if( panPartType == NULL || padfXYZ == NULL ) + { + VSIFree(panPartType); + VSIFree(padfXYZ); + returnError(); + } + for(i=0;i(arraySetter, + pabyCur, pabyEnd, nPoints, dx, dy) ) + { + VSIFree(panPartType); + VSIFree(padfXYZ); + returnError(); + } + + if( bHasZ ) + { + ZArraySetter arrayzSetter(padfZ); + if( !ReadZArray(arrayzSetter, + pabyCur, pabyEnd, nPoints, dz) ) + { + VSIFree(panPartType); + VSIFree(padfXYZ); + returnError(); + } + } + else + { + memset(padfZ, 0, nPoints * sizeof(double)); + } + + OGRMultiPolygon* poMP = new OGRMultiPolygon(); + OGRPolygon* poLastPoly = NULL; + int iAccPoints = 0; + for(i=0;iaddGeometryDirectly( poLastPoly ); + poLastPoly = NULL; + } + + VSIFree(panPartType); + VSIFree(padfXYZ); + + return poMP; + + break; + } + + default: + CPLDebug("OpenFileGDB", "Unhandled geometry type = %d", (int)nGeomType); + break; +/* +#define SHPT_GENERALPOINT 52 +#define SHPT_GENERALMULTIPOINT 53 +*/ + } + return NULL; +} + + +/************************************************************************/ +/* BuildConverter() */ +/************************************************************************/ + +FileGDBOGRGeometryConverter* FileGDBOGRGeometryConverter::BuildConverter( + const FileGDBGeomField* poGeomField) +{ + return new FileGDBOGRGeometryConverterImpl(poGeomField); +} + +/************************************************************************/ +/* GetGeometryTypeFromESRI() */ +/************************************************************************/ + +static const struct +{ + const char *pszStr; + OGRwkbGeometryType eType; +} AssocESRIGeomTypeToOGRGeomType[] = +{ + { "esriGeometryPoint", wkbPoint }, + { "esriGeometryMultipoint", wkbMultiPoint }, + { "esriGeometryLine", wkbMultiLineString }, + { "esriGeometryPolyline", wkbMultiLineString }, + { "esriGeometryPolygon", wkbMultiPolygon }, + { "esriGeometryMultiPatch", wkbMultiPolygon } +}; + +OGRwkbGeometryType FileGDBOGRGeometryConverter::GetGeometryTypeFromESRI( + const char* pszESRIType) +{ + for(size_t i=0;i + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _FILEGDBTABLE_H_INCLUDED +#define _FILEGDBTABLE_H_INCLUDED + +#include "ogr_core.h" +#include "cpl_vsi.h" +#include "ogr_geometry.h" + +#include +#include + +namespace OpenFileGDB +{ + +/************************************************************************/ +/* FileGDBTableGeometryType */ +/************************************************************************/ + +/* FGTGT = (F)ile(G)DB(T)able(G)eometry(T)ype */ +typedef enum +{ + FGTGT_NONE = 0, + FGTGT_POINT = 1, + FGTGT_MULTIPOINT = 2, + FGTGT_LINE = 3, + FGTGT_POLYGON = 4, + FGTGT_MULTIPATCH = 9 +} FileGDBTableGeometryType; + +/************************************************************************/ +/* FileGDBFieldType */ +/************************************************************************/ + +/* FGFT = (F)ile(G)DB(F)ield(T)ype */ +typedef enum +{ + FGFT_UNDEFINED = -1, + FGFT_INT16 = 0, + FGFT_INT32 = 1, + FGFT_FLOAT32 = 2, + FGFT_FLOAT64 = 3, + FGFT_STRING = 4, + FGFT_DATETIME = 5, + FGFT_OBJECTID = 6, + FGFT_GEOMETRY = 7, + FGFT_BINARY = 8, + FGFT_RASTER = 9, + FGFT_UUID_1 = 10, + FGFT_UUID_2 = 11, + FGFT_XML = 12 +} FileGDBFieldType; + +/************************************************************************/ +/* FileGDBField */ +/************************************************************************/ + +class FileGDBTable; +class FileGDBIndex; + +class FileGDBField +{ + friend class FileGDBTable; + + FileGDBTable *poParent; + + std::string osName; + std::string osAlias; + FileGDBFieldType eType; + + int bNullable; + int nMaxWidth; /* for string */ + + OGRField sDefault; + + FileGDBIndex* poIndex; + + public: + + FileGDBField(FileGDBTable* poParent); + virtual ~FileGDBField(); + + const std::string& GetName() const { return osName; } + const std::string& GetAlias() const { return osAlias; } + FileGDBFieldType GetType() const { return eType; } + int IsNullable() const { return bNullable; } + int GetMaxWidth() const { return nMaxWidth; } + const OGRField *GetDefault() const { return &sDefault; } + + int HasIndex(); + FileGDBIndex *GetIndex(); +}; + +/************************************************************************/ +/* FileGDBGeomField */ +/************************************************************************/ + +class FileGDBGeomField: public FileGDBField +{ + friend class FileGDBTable; + + std::string osWKT; + int bHasZ; + int bHasM; + double dfXOrigin; + double dfYOrigin; + double dfXYScale; + double dfMOrigin; + double dfMScale; + double dfZOrigin; + double dfZScale; + double dfXYTolerance; + double dfMTolerance; + double dfZTolerance; + double dfXMin; + double dfYMin; + double dfXMax; + double dfYMax; + int bHas3D; + + public: + FileGDBGeomField(FileGDBTable* poParent); + virtual ~FileGDBGeomField() {} + + const std::string& GetWKT() const { return osWKT; } + + double GetXMin() const { return dfXMin; } + double GetYMin() const { return dfYMin; } + double GetXMax() const { return dfXMax; } + double GetYMax() const { return dfYMax; } + + int HasZ() const { return bHasZ; } + int HasM() const { return bHasM; } + + double GetXOrigin() const { return dfXOrigin; } + double GetYOrigin() const { return dfYOrigin; } + double GetXYScale() const { return dfXYScale; } + double GetXYTolerance() const { return dfXYTolerance; } + + double GetZOrigin() const { return dfZOrigin; } + double GetZScale() const { return dfZScale; } + double GetZTolerance() const { return dfZTolerance; } + + double GetMOrigin() const { return dfMOrigin; } + double GetMScale() const { return dfMScale; } + double GetMTolerance() const { return dfMTolerance; } + + int Has3D() const { return bHas3D; } +}; + +/************************************************************************/ +/* FileGDBRasterField */ +/************************************************************************/ + +class FileGDBRasterField: public FileGDBGeomField +{ + friend class FileGDBTable; + + std::string osRasterColumnName; + + public: + FileGDBRasterField(FileGDBTable* poParent) : FileGDBGeomField(poParent) {} + virtual ~FileGDBRasterField() {} + + const std::string& GetRasterColumnName() const { return osRasterColumnName; } + +}; + +/************************************************************************/ +/* FileGDBIndex */ +/************************************************************************/ + +class FileGDBIndex +{ + friend class FileGDBTable; + std::string osIndexName; + std::string osFieldName; + + public: + FileGDBIndex() {} + virtual ~FileGDBIndex() {} + + const std::string& GetIndexName() const { return osIndexName; } + const std::string& GetFieldName() const { return osFieldName; } +}; + +/************************************************************************/ +/* FileGDBTable */ +/************************************************************************/ + +class FileGDBTable +{ + VSILFILE *fpTable; + VSILFILE *fpTableX; + vsi_l_offset nFileSize; /* only read when needed */ + + std::string osFilename; + std::vector apoFields; + std::string osObjectIdColName; + + int bHasReadGDBIndexes; + std::vector apoIndexes; + + GUInt32 nOffsetFieldDesc; + GUInt32 nFieldDescLength; + + GUInt32 nTablxOffsetSize; + std::vector anFeatureOffsets; /* MSb set marks deleted feature */ + + GByte* pabyTablXBlockMap; + int nCountBlocksBeforeIBlockIdx; /* optimization */ + int nCountBlocksBeforeIBlockValue; /* optimization */ + + char achGUIDBuffer[32 + 6 + 1]; + int nChSaved; + + int bError; + int nCurRow; + int bHasDeletedFeaturesListed; + int bIsDeleted; + int nLastCol; + GByte* pabyIterVals; + int iAccNullable; + GUInt32 nRowBlobLength; + OGRField sCurField; + /* OGRFieldType eCurFieldType; */ + + FileGDBTableGeometryType eTableGeomType; + int nValidRecordCount; + int nTotalRecordCount; + int iGeomField; + int nCountNullableFields; + int nNullableFieldsSizeInBytes; + + GUInt32 nBufferMaxSize; + GByte* pabyBuffer; + + void Init(); + + GUIntBig nFilterXMin, nFilterXMax, nFilterYMin, nFilterYMax; + + GUInt32 nOffsetHeaderEnd; + + int ReadTableXHeader(); + int IsLikelyFeatureAtOffset( + vsi_l_offset nOffset, GUInt32* pnSize, + int* pbDeletedRecord); + int GuessFeatureLocations(); + + public: + + FileGDBTable(); + ~FileGDBTable(); + + int Open(const char* pszFilename, + const char* pszLayerName = NULL); + void Close(); + + const std::string& GetFilename() const { return osFilename; } + FileGDBTableGeometryType GetGeometryType() const { return eTableGeomType; } + int GetValidRecordCount() const { return nValidRecordCount; } + int GetTotalRecordCount() const { return nTotalRecordCount; } + int GetFieldCount() const { return (int)apoFields.size(); } + FileGDBField* GetField(int i) const { return apoFields[i]; } + int GetGeomFieldIdx() const { return iGeomField; } + const FileGDBGeomField* GetGeomField() const { return (iGeomField >= 0) ? (FileGDBGeomField*)apoFields[iGeomField] : NULL; } + const std::string& GetObjectIdColName() const { return osObjectIdColName; } + + int GetFieldIdx(const std::string& osName) const; + + int GetIndexCount(); + const FileGDBIndex* GetIndex(int i) const { return apoIndexes[i]; } + + vsi_l_offset GetOffsetInTableForRow(int iRow); + + int HasDeletedFeaturesListed() const { return bHasDeletedFeaturesListed; } + + /* Next call to SelectRow() or GetFieldValue() invalidates previously returned values */ + int SelectRow(int iRow); + int GetAndSelectNextNonEmptyRow(int iRow); + int HasGotError() const { return bError; } + int GetCurRow() const { return nCurRow; } + int IsCurRowDeleted() const { return bIsDeleted; } + const OGRField* GetFieldValue(int iCol); + + int GetFeatureExtent(const OGRField* psGeomField, + OGREnvelope* psOutFeatureEnvelope); + + void InstallFilterEnvelope(const OGREnvelope* psFilterEnvelope); + int DoesGeometryIntersectsFilterEnvelope(const OGRField* psGeomField); +}; + +/************************************************************************/ +/* FileGDBSQLOp */ +/************************************************************************/ + +typedef enum +{ + FGSO_ISNOTNULL, + FGSO_LT, + FGSO_LE, + FGSO_EQ, + FGSO_GE, + FGSO_GT +} FileGDBSQLOp; + +/************************************************************************/ +/* FileGDBIterator */ +/************************************************************************/ + +class FileGDBIterator +{ + public: + virtual ~FileGDBIterator() {} + + virtual FileGDBTable *GetTable() = 0; + virtual void Reset() = 0; + virtual int GetNextRowSortedByFID() = 0; + virtual int GetRowCount(); + + /* Only available on a BuildIsNotNull() iterator */ + virtual const OGRField* GetMinValue(int& eOutOGRFieldType); + virtual const OGRField* GetMaxValue(int& eOutOGRFieldType); + /* will reset the iterator */ + virtual int GetMinMaxSumCount(double& dfMin, double& dfMax, + double& dfSum, int& nCount); + + /* Only available on a BuildIsNotNull() or Build() iterator */ + virtual int GetNextRowSortedByValue(); + + static FileGDBIterator* Build(FileGDBTable* poParent, + int nFieldIdx, + int bAscending, + FileGDBSQLOp op, + OGRFieldType eOGRFieldType, + const OGRField* psValue); + static FileGDBIterator* BuildIsNotNull(FileGDBTable* poParent, + int nFieldIdx, + int bAscending); + static FileGDBIterator* BuildNot(FileGDBIterator* poIterBase); + static FileGDBIterator* BuildAnd(FileGDBIterator* poIter1, + FileGDBIterator* poIter2); + static FileGDBIterator* BuildOr(FileGDBIterator* poIter1, + FileGDBIterator* poIter2, + int bIteratorAreExclusive = FALSE); +}; + +/************************************************************************/ +/* FileGDBOGRGeometryConverter */ +/************************************************************************/ + +class FileGDBOGRGeometryConverter +{ + public: + virtual ~FileGDBOGRGeometryConverter() {} + + virtual OGRGeometry* GetAsGeometry(const OGRField* psField) = 0; + + static FileGDBOGRGeometryConverter* BuildConverter(const FileGDBGeomField* poGeomField); + static OGRwkbGeometryType GetGeometryTypeFromESRI(const char* pszESRIGeometyrType); +}; + +int FileGDBDoubleDateToOGRDate(double dfVal, OGRField* psField); + +}; /* namespace OpenFileGDB */ + +#endif /* ndef _FILEGDBTABLE_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/filegdbtable_priv.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/filegdbtable_priv.h new file mode 100644 index 000000000..b9cd381f1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/filegdbtable_priv.h @@ -0,0 +1,138 @@ +/****************************************************************************** + * $Id: filegdbtable_priv.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements reading of FileGDB tables + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _FILEGDBTABLE_PRIV_H_INCLUDED +#define _FILEGDBTABLE_PRIV_H_INCLUDED + +#include "filegdbtable.h" +#include "cpl_error.h" + +namespace OpenFileGDB +{ + +/************************************************************************/ +/* GetInt16() */ +/************************************************************************/ + +static GInt16 GetInt16(const GByte* pBaseAddr, int iOffset) +{ + GInt16 nVal; + memcpy(&nVal, pBaseAddr + sizeof(nVal) * iOffset, sizeof(nVal)); + CPL_LSBPTR16(&nVal); + return nVal; +} + +/************************************************************************/ +/* GetUInt16() */ +/************************************************************************/ + +static GUInt16 GetUInt16(const GByte* pBaseAddr, int iOffset) +{ + GUInt16 nVal; + memcpy(&nVal, pBaseAddr + sizeof(nVal) * iOffset, sizeof(nVal)); + CPL_LSBPTR16(&nVal); + return nVal; +} + +/************************************************************************/ +/* GetInt32() */ +/************************************************************************/ + +static GInt32 GetInt32(const GByte* pBaseAddr, int iOffset) +{ + GInt32 nVal; + memcpy(&nVal, pBaseAddr + sizeof(nVal) * iOffset, sizeof(nVal)); + CPL_LSBPTR32(&nVal); + return nVal; +} + +/************************************************************************/ +/* GetUInt32() */ +/************************************************************************/ + +static GUInt32 GetUInt32(const GByte* pBaseAddr, int iOffset) +{ + GUInt32 nVal; + memcpy(&nVal, pBaseAddr + sizeof(nVal) * iOffset, sizeof(nVal)); + CPL_LSBPTR32(&nVal); + return nVal; +} + +/************************************************************************/ +/* GetFloat32() */ +/************************************************************************/ + +static float GetFloat32(const GByte* pBaseAddr, int iOffset) +{ + float fVal; + memcpy(&fVal, pBaseAddr + sizeof(fVal) * iOffset, sizeof(fVal)); + CPL_LSBPTR32(&fVal); + return fVal; +} + +/************************************************************************/ +/* GetFloat64() */ +/************************************************************************/ + +static double GetFloat64(const GByte* pBaseAddr, int iOffset) +{ + double dfVal; + memcpy(&dfVal, pBaseAddr + sizeof(dfVal) * iOffset, sizeof(dfVal)); + CPL_LSBPTR64(&dfVal); + return dfVal; +} + +void FileGDBTablePrintError(const char* pszFile, int nLineNumber); + +#define PrintError() FileGDBTablePrintError(__FILE__, __LINE__) + +/************************************************************************/ +/* returnError() */ +/************************************************************************/ + +#define returnError() \ + do { PrintError(); return (errorRetValue); } while(0) + +/************************************************************************/ +/* returnErrorIf() */ +/************************************************************************/ + +#define returnErrorIf(expr) \ + do { if( (expr) ) returnError(); } while(0) + +/************************************************************************/ +/* returnErrorAndCleanupIf() */ +/************************************************************************/ + +#define returnErrorAndCleanupIf(expr, cleanup) \ + do { if( (expr) ) { cleanup; returnError(); } } while(0) + +}; /* namespace OpenFileGDB */ + +#endif /* _FILEGDBTABLE_PRIV_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/makefile.vc new file mode 100644 index 000000000..dd30436da --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/makefile.vc @@ -0,0 +1,13 @@ + +OBJ = ogropenfilegdbdriver.obj ogropenfilegdbdatasource.obj ogropenfilegdblayer.obj filegdbtable.obj filegdbindex.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. -I..\mem + +default: $(OBJ) + +clean: + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/ogr_openfilegdb.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/ogr_openfilegdb.h new file mode 100644 index 000000000..fac7ec5a8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/ogr_openfilegdb.h @@ -0,0 +1,201 @@ +/****************************************************************************** +* $Id: ogr_openfilegdb.h 28967 2015-04-21 11:01:15Z rouault $ +* +* Project: OpenGIS Simple Features Reference Implementation +* Purpose: Implements Open FileGDB OGR driver. +* Author: Even Rouault, +* +****************************************************************************** + * Copyright (c) 2014, Even Rouault +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +****************************************************************************/ + +#ifndef _OGR_OPENFILEGDB_H_INCLUDED +#define _OGR_OPENFILEGDB_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "filegdbtable.h" +#include "swq.h" +#include "cpl_quad_tree.h" + +#include +#include + +using namespace OpenFileGDB; + +/************************************************************************/ +/* OGROpenFileGDBLayer */ +/************************************************************************/ + +class OGROpenFileGDBDataSource; +class OGROpenFileGDBGeomFieldDefn; +class OGROpenFileGDBFeatureDefn; + +typedef enum +{ + SPI_IN_BUILDING, + SPI_COMPLETED, + SPI_INVALID, +} SPIState; + +class OGROpenFileGDBLayer : public OGRLayer +{ + friend class OGROpenFileGDBGeomFieldDefn; + friend class OGROpenFileGDBFeatureDefn; + + CPLString m_osGDBFilename; + CPLString m_osName; + FileGDBTable *m_poLyrTable; + OGROpenFileGDBFeatureDefn *m_poFeatureDefn; + int m_iGeomFieldIdx; + int m_iCurFeat; + std::string m_osDefinition; + std::string m_osDocumentation; + OGRwkbGeometryType m_eGeomType; + int m_bValidLayerDefn; + int m_bEOF; + + int BuildLayerDefinition(); + int BuildGeometryColumnGDBv10(); + OGRFeature *GetCurrentFeature(); + + FileGDBOGRGeometryConverter* m_poGeomConverter; + + int m_iFieldToReadAsBinary; + + FileGDBIterator *m_poIterator; + int m_bIteratorSufficientToEvaluateFilter; + FileGDBIterator* BuildIteratorFromExprNode(swq_expr_node* poNode); + + FileGDBIterator* m_poIterMinMax; + + SPIState m_eSpatialIndexState; + CPLQuadTree *m_pQuadTree; + void **m_pahFilteredFeatures; + int m_nFilteredFeatureCount; + static void GetBoundsFuncEx(const void* hFeature, + CPLRectObj* pBounds, + void* pQTUserData); + +public: + + OGROpenFileGDBLayer(const char* pszGDBFilename, + const char* pszName, + const std::string& osDefinition, + const std::string& osDocumentation, + const char* pszGeomName = NULL, + OGRwkbGeometryType eGeomType = wkbUnknown); + virtual ~OGROpenFileGDBLayer(); + + const std::string& GetXMLDefinition() { return m_osDefinition; } + const std::string& GetXMLDocumentation() { return m_osDocumentation; } + int GetAttrIndexUse() { return (m_poIterator == NULL) ? 0 : (m_bIteratorSufficientToEvaluateFilter) ? 2 : 1; } + const OGRField* GetMinMaxValue(OGRFieldDefn* poFieldDefn, int bIsMin, + int& eOutType); + int GetMinMaxSumCount(OGRFieldDefn* poFieldDefn, + double& dfMin, double& dfMax, + double& dfSum, int& nCount); + int HasIndexForField(const char* pszFieldName); + FileGDBIterator* BuildIndex(const char* pszFieldName, + int bAscending, + int op, + swq_expr_node* poValue); + SPIState GetSpatialIndexState() const { return m_eSpatialIndexState; } + int IsValidLayerDefn() { return BuildLayerDefinition(); } + + virtual const char* GetName() { return m_osName.c_str(); } + virtual OGRwkbGeometryType GetGeomType(); + + virtual const char* GetFIDColumn(); + + virtual void ResetReading(); + virtual OGRFeature* GetNextFeature(); + virtual OGRFeature* GetFeature( GIntBig nFeatureId ); + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual OGRFeatureDefn* GetLayerDefn(); + + virtual void SetSpatialFilter( OGRGeometry * ); + + virtual OGRErr SetAttributeFilter( const char* pszFilter ); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGROpenFileGDBDataSource */ +/************************************************************************/ + +class OGROpenFileGDBDataSource : public OGRDataSource +{ + char *m_pszName; + CPLString m_osDirName; + std::vector m_apoLayers; + std::vector m_apoHiddenLayers; + char **m_papszFiles; + std::map m_osMapNameToIdx; + + /* For debugging/testing */ + int bLastSQLUsedOptimizedImplementation; + + int OpenFileGDBv10(int iGDBItems, + int nInterestTable); + int OpenFileGDBv9 (int iGDBFeatureClasses, + int iGDBObjectClasses, + int nInterestTable); + + int FileExists(const char* pszFilename); + void AddLayer( const CPLString& osName, + int nInterestTable, + int& nCandidateLayers, + int& nLayersSDC, + const CPLString& osDefinition, + const CPLString& osDocumentation, + const char* pszGeomName, + OGRwkbGeometryType eGeomType ); + +public: + OGROpenFileGDBDataSource(); + virtual ~OGROpenFileGDBDataSource(); + + int Open(const char * ); + + virtual const char* GetName() { return m_pszName; } + virtual int GetLayerCount() { return static_cast(m_apoLayers.size()); } + + virtual OGRLayer* GetLayer( int ); + virtual OGRLayer* GetLayerByName( const char* pszName ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poResultsSet ); + + virtual int TestCapability( const char * ); + + virtual char **GetFileList(); +}; + +int OGROpenFileGDBIsComparisonOp(int op); + +#endif /* ndef _OGR_OPENFILEGDB_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdatasource.cpp new file mode 100644 index 000000000..a06a2b7a1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdatasource.cpp @@ -0,0 +1,1192 @@ +/****************************************************************************** + * $Id: ogropenfilegdbdatasource.cpp 29024 2015-04-26 10:42:08Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements Open FileGDB OGR driver. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_openfilegdb.h" +#include "ogr_mem.h" +#include + +CPL_CVSID("$Id"); + +/************************************************************************/ +/* OGROpenFileGDBDataSource() */ +/************************************************************************/ +OGROpenFileGDBDataSource::OGROpenFileGDBDataSource() +{ + m_pszName = NULL; + m_papszFiles = NULL; + bLastSQLUsedOptimizedImplementation = FALSE; +} + +/************************************************************************/ +/* ~OGROpenFileGDBDataSource() */ +/************************************************************************/ +OGROpenFileGDBDataSource::~OGROpenFileGDBDataSource() + +{ + size_t i; + for( i = 0; i < m_apoLayers.size(); i++ ) + delete m_apoLayers[i]; + for( i = 0; i < m_apoHiddenLayers.size(); i++ ) + delete m_apoHiddenLayers[i]; + CPLFree(m_pszName); + CSLDestroy(m_papszFiles); +} + +/************************************************************************/ +/* FileExists() */ +/************************************************************************/ + +int OGROpenFileGDBDataSource::FileExists(const char* pszFilename) +{ + if( m_papszFiles ) + return CSLFindString(m_papszFiles, CPLGetFilename(pszFilename)) >= 0; + else + { + VSIStatBufL sStat; + return VSIStatExL(pszFilename, &sStat, VSI_STAT_EXISTS_FLAG) == 0; + } +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGROpenFileGDBDataSource::Open( const char* pszFilename ) + +{ + FileGDBTable oTable; + + m_pszName = CPLStrdup(pszFilename); + + m_osDirName = pszFilename; + int nInterestTable = -1; + const char* pszFilenameWithoutPath = CPLGetFilename(pszFilename); + if( strlen(pszFilenameWithoutPath) == strlen("a00000000.gdbtable") && + pszFilenameWithoutPath[0] == 'a' && + sscanf(pszFilenameWithoutPath, "a%08x.gdbtable", &nInterestTable) == 1 ) + { + m_osDirName = CPLGetPath(m_osDirName); + } + else + { + nInterestTable = -1; + } + + if( EQUAL(CPLGetExtension(m_osDirName), "zip") && + strncmp(m_osDirName, "/vsizip/", strlen("/vsizip/")) != 0 ) + { + m_osDirName = "/vsizip/" + m_osDirName; + } + else if( EQUAL(CPLGetExtension(m_osDirName), "tar") && + strncmp(m_osDirName, "/vsitar/", strlen("/vsitar/")) != 0 ) + { + m_osDirName = "/vsitar/" + m_osDirName; + } + + if( strncmp(m_osDirName, "/vsizip/", strlen("/vsizip/")) == 0 || + strncmp(m_osDirName, "/vsitar/", strlen("/vsitar/")) == 0) + { + /* Look for one subdirectory ending with .gdb extension */ + char** papszDir = CPLReadDir(m_osDirName); + int iCandidate = -1; + for( int i=0; papszDir && papszDir[i] != NULL; i++ ) + { + VSIStatBufL sStat; + if( EQUAL(CPLGetExtension(papszDir[i]), "gdb") && + VSIStatL( CPLSPrintf("%s/%s", m_osDirName.c_str(), papszDir[i]), &sStat ) == 0 && + VSI_ISDIR(sStat.st_mode) ) + { + if( iCandidate < 0 ) + iCandidate = i; + else + { + iCandidate = -1; + break; + } + } + } + if( iCandidate >= 0 ) + { + m_osDirName += "/"; + m_osDirName += papszDir[iCandidate]; + } + CSLDestroy(papszDir); + } + + m_papszFiles = VSIReadDir(m_osDirName); + + /* Explore catalog table */ + const char* psza00000001 = CPLFormFilename(m_osDirName, "a00000001", "gdbtable"); + if( !FileExists(psza00000001) || !oTable.Open(psza00000001) ) + { + if( nInterestTable >= 0 && FileExists(m_pszName) ) + { + const char* pszLyrName = CPLSPrintf("a%08x", nInterestTable); + OGROpenFileGDBLayer* poLayer = new OGROpenFileGDBLayer( + m_pszName, pszLyrName, "", ""); + const char* pszTablX = CPLResetExtension(m_pszName, "gdbtablx"); + if( (!FileExists(pszTablX) && + poLayer->GetLayerDefn()->GetFieldCount() == 0 && + poLayer->GetFeatureCount() == 0) || + !poLayer->IsValidLayerDefn() ) + { + delete poLayer; + return FALSE; + } + m_apoLayers.push_back(poLayer); + return TRUE; + } + return FALSE; + } + + if( !(oTable.GetFieldCount() >= 2 && + oTable.GetField(0)->GetName() == "Name" && + oTable.GetField(0)->GetType() == FGFT_STRING && + oTable.GetField(1)->GetName() == "FileFormat" && + (oTable.GetField(1)->GetType() == FGFT_INT16 || + oTable.GetField(1)->GetType() == FGFT_INT32) ) ) + { + return FALSE; + } + + int iGDBItems = -1; /* V10 */ + int iGDBFeatureClasses = -1; /* V9.X */ + int iGDBObjectClasses = -1; /* V9.X */ + int i; + + std::vector aosTableNames; + for(i=0;iString); + + if( strcmp(psField->String, "GDB_Items") == 0 ) + { + iGDBItems = i; + } + else if( strcmp(psField->String, "GDB_FeatureClasses") == 0 ) + { + iGDBFeatureClasses = i; + } + else if( strcmp(psField->String, "GDB_ObjectClasses") == 0 ) + { + iGDBObjectClasses = i; + } + m_osMapNameToIdx[psField->String] = 1 + i; + } + else + { + aosTableNames.push_back(""); + } + } + + oTable.Close(); + + if( iGDBItems >= 0 ) + { + int bRet = OpenFileGDBv10(iGDBItems, + nInterestTable); + if( !bRet ) + return FALSE; + } + else if( iGDBFeatureClasses >= 0 && iGDBObjectClasses >= 0 ) + { + int bRet = OpenFileGDBv9(iGDBFeatureClasses, + iGDBObjectClasses, + nInterestTable); + if( !bRet ) + return FALSE; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "No GDB_Items nor GDB_FeatureClasses table"); + return FALSE; + } + + if( m_apoLayers.size() == 0 && nInterestTable >= 0 ) + { + if( FileExists(m_pszName) ) + { + const char* pszLyrName; + if( nInterestTable <= (int)aosTableNames.size() && + aosTableNames[nInterestTable-1].size() != 0 ) + pszLyrName = aosTableNames[nInterestTable-1].c_str(); + else + pszLyrName = CPLSPrintf("a%08x", nInterestTable); + m_apoLayers.push_back(new OGROpenFileGDBLayer( + m_pszName, pszLyrName, "", "")); + } + else + { + return FALSE; + } + } + + return TRUE; +} + +/***********************************************************************/ +/* AddLayer() */ +/***********************************************************************/ + +void OGROpenFileGDBDataSource::AddLayer( const CPLString& osName, + int nInterestTable, + int& nCandidateLayers, + int& nLayersSDC, + const CPLString& osDefinition, + const CPLString& osDocumentation, + const char* pszGeomName, + OGRwkbGeometryType eGeomType ) +{ + std::map::const_iterator oIter = + m_osMapNameToIdx.find(osName); + int idx = 0; + if( oIter != m_osMapNameToIdx.end() ) + idx = oIter->second; + if( idx > 0 && (nInterestTable < 0 || nInterestTable == idx) ) + { + const char* pszFilename = CPLFormFilename( + m_osDirName, CPLSPrintf("a%08x", idx), "gdbtable"); + if( FileExists(pszFilename) ) + { + nCandidateLayers ++; + + if( m_papszFiles != NULL ) + { + const char* pszSDC = CPLResetExtension(pszFilename, "gdbtable.sdc"); + if( FileExists(pszSDC) ) + { + nLayersSDC ++; + CPLError(CE_Warning, CPLE_AppDefined, + "%s layer has a %s file whose format is unhandled", + osName.c_str(), pszSDC); + return; + } + } + + m_apoLayers.push_back( + new OGROpenFileGDBLayer(pszFilename, + osName, + osDefinition, + osDocumentation, + pszGeomName, eGeomType)); + } + } +} + +/***********************************************************************/ +/* OpenFileGDBv10() */ +/***********************************************************************/ + +int OGROpenFileGDBDataSource::OpenFileGDBv10(int iGDBItems, + int nInterestTable) +{ + FileGDBTable oTable; + int i; + + CPLDebug("OpenFileGDB", "FileGDB v10 or later"); + + if( !oTable.Open(CPLFormFilename(m_osDirName, + CPLSPrintf("a%08x.gdbtable", iGDBItems + 1), NULL)) ) + return FALSE; + + int iName = oTable.GetFieldIdx("Name"); + int iDefinition = oTable.GetFieldIdx("Definition"); + int iDocumentation = oTable.GetFieldIdx("Documentation"); + if( iName < 0 || iDefinition < 0 || iDocumentation < 0 || + oTable.GetField(iName)->GetType() != FGFT_STRING || + oTable.GetField(iDefinition)->GetType() != FGFT_XML || + oTable.GetField(iDocumentation)->GetType() != FGFT_XML ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Wrong structure for GDB_Items table"); + return FALSE; + } + + int nCandidateLayers = 0, nLayersSDC = 0; + for(i=0;iString, "DEFeatureClassInfo") != NULL || + strstr(psField->String, "DETableInfo") != NULL) ) + { + CPLString osDefinition(psField->String); + + psField = oTable.GetFieldValue(iDocumentation); + CPLString osDocumentation( psField != NULL ? psField->String : "" ); + + psField = oTable.GetFieldValue(iName); + if( psField != NULL ) + { + AddLayer( psField->String, nInterestTable, nCandidateLayers, nLayersSDC, + osDefinition, osDocumentation, + NULL, wkbUnknown ); + } + } + } + + if( m_apoLayers.size() == 0 && nCandidateLayers > 0 && + nCandidateLayers == nLayersSDC ) + return FALSE; + + return TRUE; +} + +/***********************************************************************/ +/* OpenFileGDBv9() */ +/***********************************************************************/ + +int OGROpenFileGDBDataSource::OpenFileGDBv9(int iGDBFeatureClasses, + int iGDBObjectClasses, + int nInterestTable) +{ + FileGDBTable oTable; + int i; + + CPLDebug("OpenFileGDB", "FileGDB v9"); + + /* Fetch names of layers */ + if( !oTable.Open(CPLFormFilename(m_osDirName, + CPLSPrintf("a%08x", iGDBObjectClasses + 1), "gdbtable")) ) + return FALSE; + + int iName = oTable.GetFieldIdx("Name"); + int iCLSID = oTable.GetFieldIdx("CLSID"); + if( iName < 0 || oTable.GetField(iName)->GetType() != FGFT_STRING || + iCLSID < 0 || oTable.GetField(iCLSID)->GetType() != FGFT_STRING ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Wrong structure for GDB_ObjectClasses table"); + return FALSE; + } + + std::vector< std::string > aosName; + int nCandidateLayers = 0, nLayersSDC = 0; + for(i=0;iString); + psField = oTable.GetFieldValue(iCLSID); + if( psField != NULL ) + { + /* Is it a non-spatial table ? */ + if( strcmp(psField->String, "{7A566981-C114-11D2-8A28-006097AFF44E}") == 0 ) + { + aosName.push_back( "" ); + AddLayer( osName, nInterestTable, nCandidateLayers, nLayersSDC, + "", "", NULL, wkbNone ); + } + else + { + /* We should perhaps also check that the CLSID is the one of a spatial table */ + aosName.push_back( osName ); + } + } + } + } + oTable.Close(); + + /* Find tables that are spatial layers */ + if( !oTable.Open(CPLFormFilename(m_osDirName, + CPLSPrintf("a%08x", iGDBFeatureClasses + 1), "gdbtable")) ) + return FALSE; + + int iObjectClassID = oTable.GetFieldIdx("ObjectClassID"); + int iGeometryType = oTable.GetFieldIdx("GeometryType"); + int iShapeField = oTable.GetFieldIdx("ShapeField"); + if( iObjectClassID < 0 || iGeometryType < 0 || iShapeField < 0 || + oTable.GetField(iObjectClassID)->GetType() != FGFT_INT32 || + oTable.GetField(iGeometryType)->GetType() != FGFT_INT32 || + oTable.GetField(iShapeField)->GetType() != FGFT_STRING ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Wrong structure for GDB_FeatureClasses table"); + return FALSE; + } + + for(i=0;iInteger; + OGRwkbGeometryType eGeomType = wkbUnknown; + switch( nGeomType ) + { + case FGTGT_NONE: /* doesn't make sense ! */ break; + case FGTGT_POINT: eGeomType = wkbPoint; break; + case FGTGT_MULTIPOINT: eGeomType = wkbMultiPoint; break; + case FGTGT_LINE: eGeomType = wkbMultiLineString; break; + case FGTGT_POLYGON: eGeomType = wkbMultiPolygon; break; + case FGTGT_MULTIPATCH: eGeomType = wkbMultiPolygon; break; + } + + psField = oTable.GetFieldValue(iShapeField); + if( psField == NULL ) + continue; + CPLString osGeomFieldName(psField->String); + + psField = oTable.GetFieldValue(iObjectClassID); + if( psField == NULL ) + continue; + + int idx = psField->Integer; + if( psField != NULL && idx > 0 && idx <= (int)aosName.size() && + aosName[idx-1].size() > 0 ) + { + const std::string osName(aosName[idx-1]); + AddLayer( osName, nInterestTable, nCandidateLayers, nLayersSDC, + "", "", osGeomFieldName.c_str(), eGeomType); + } + } + + if( m_apoLayers.size() == 0 && nCandidateLayers > 0 && + nCandidateLayers == nLayersSDC ) + return FALSE; + + return TRUE; +} + +/***********************************************************************/ +/* TestCapability() */ +/***********************************************************************/ + +int OGROpenFileGDBDataSource::TestCapability( const char * pszCap ) +{ + (void)pszCap; + return FALSE; +} + +/***********************************************************************/ +/* GetLayer() */ +/***********************************************************************/ + +OGRLayer* OGROpenFileGDBDataSource::GetLayer( int iIndex ) +{ + if( iIndex < 0 || iIndex >= (int) m_apoLayers.size() ) + return NULL; + return m_apoLayers[iIndex]; +} + +/***********************************************************************/ +/* GetLayerByName() */ +/***********************************************************************/ + +OGRLayer* OGROpenFileGDBDataSource::GetLayerByName( const char* pszName ) +{ + OGRLayer* poLayer; + + poLayer = OGRDataSource::GetLayerByName(pszName); + if( poLayer != NULL ) + return poLayer; + + for(size_t i=0;iGetName(), pszName) ) + return m_apoHiddenLayers[i]; + } + + std::map::const_iterator oIter = m_osMapNameToIdx.find(pszName); + if( oIter != m_osMapNameToIdx.end() ) + { + int idx = oIter->second; + const char* pszFilename = CPLFormFilename( + m_osDirName, CPLSPrintf("a%08x", idx), "gdbtable"); + if( FileExists(pszFilename) ) + { + poLayer = new OGROpenFileGDBLayer( + pszFilename, pszName, "", ""); + m_apoHiddenLayers.push_back(poLayer); + return poLayer; + } + } + return NULL; +} + + +/************************************************************************/ +/* OGROpenFileGDBSingleFeatureLayer */ +/************************************************************************/ + +class OGROpenFileGDBSingleFeatureLayer : public OGRLayer +{ + private: + char *pszVal; + OGRFeatureDefn *poFeatureDefn; + int iNextShapeId; + + public: + OGROpenFileGDBSingleFeatureLayer( const char* pszLayerName, + const char *pszVal ); + ~OGROpenFileGDBSingleFeatureLayer(); + + virtual void ResetReading() { iNextShapeId = 0; } + virtual OGRFeature *GetNextFeature(); + virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; } + virtual int TestCapability( const char * ) { return FALSE; } +}; + +/************************************************************************/ +/* OGROpenFileGDBSingleFeatureLayer() */ +/************************************************************************/ + +OGROpenFileGDBSingleFeatureLayer::OGROpenFileGDBSingleFeatureLayer(const char* pszLayerName, + const char *pszVal ) +{ + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + OGRFieldDefn oField( "FIELD_1", OFTString ); + poFeatureDefn->AddFieldDefn( &oField ); + + iNextShapeId = 0; + this->pszVal = pszVal ? CPLStrdup(pszVal) : NULL; +} + +/************************************************************************/ +/* ~OGROpenFileGDBSingleFeatureLayer() */ +/************************************************************************/ + +OGROpenFileGDBSingleFeatureLayer::~OGROpenFileGDBSingleFeatureLayer() +{ + if( poFeatureDefn != NULL ) + poFeatureDefn->Release(); + CPLFree(pszVal); +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature * OGROpenFileGDBSingleFeatureLayer::GetNextFeature() +{ + if (iNextShapeId != 0) + return NULL; + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + if (pszVal) + poFeature->SetField(0, pszVal); + poFeature->SetFID(iNextShapeId ++); + return poFeature; +} + +/***********************************************************************/ +/* OGROpenFileGDBSimpleSQLLayer */ +/***********************************************************************/ + +class OGROpenFileGDBSimpleSQLLayer: public OGRLayer +{ + OGRLayer *poBaseLayer; + FileGDBIterator *poIter; + OGRFeatureDefn *poFeatureDefn; + + public: + OGROpenFileGDBSimpleSQLLayer(OGRLayer* poBaseLayer, + FileGDBIterator* poIter, + int nColumns, + swq_col_def* pasColDefs); + ~OGROpenFileGDBSimpleSQLLayer(); + + virtual void ResetReading(); + virtual OGRFeature* GetNextFeature(); + virtual OGRFeature* GetFeature( GIntBig nFeatureId ); + virtual OGRFeatureDefn* GetLayerDefn() { return poFeatureDefn; } + virtual int TestCapability( const char * ); + virtual const char* GetFIDColumn() { return poBaseLayer->GetFIDColumn(); } + virtual OGRErr GetExtent( OGREnvelope *psExtent, int bForce ) + { return poBaseLayer->GetExtent(psExtent, bForce); } + virtual GIntBig GetFeatureCount(int bForce); +}; + +/***********************************************************************/ +/* OGROpenFileGDBSimpleSQLLayer() */ +/***********************************************************************/ + +OGROpenFileGDBSimpleSQLLayer::OGROpenFileGDBSimpleSQLLayer( + OGRLayer* poBaseLayer, + FileGDBIterator* poIter, + int nColumns, + swq_col_def* pasColDefs) : + poBaseLayer(poBaseLayer), poIter(poIter) +{ + if( nColumns == 1 && strcmp(pasColDefs[0].field_name, "*") == 0 ) + { + poFeatureDefn = poBaseLayer->GetLayerDefn(); + poFeatureDefn->Reference(); + } + else + { + poFeatureDefn = new OGRFeatureDefn(poBaseLayer->GetName()); + poFeatureDefn->SetGeomType(poBaseLayer->GetGeomType()); + poFeatureDefn->Reference(); + if( poBaseLayer->GetGeomType() != wkbNone ) + { + poFeatureDefn->GetGeomFieldDefn(0)->SetName(poBaseLayer->GetGeometryColumn()); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poBaseLayer->GetSpatialRef()); + } + for(int i=0;iGetLayerDefn()->GetFieldCount();j++) + poFeatureDefn->AddFieldDefn(poBaseLayer->GetLayerDefn()->GetFieldDefn(j)); + } + else + { + OGRFieldDefn* poFieldDefn = poBaseLayer->GetLayerDefn()->GetFieldDefn( + poBaseLayer->GetLayerDefn()->GetFieldIndex(pasColDefs[i].field_name)); + CPLAssert(poFieldDefn != NULL ); /* already checked before */ + poFeatureDefn->AddFieldDefn(poFieldDefn); + } + } + } + SetDescription( poFeatureDefn->GetName() ); + ResetReading(); +} + +/***********************************************************************/ +/* ~OGROpenFileGDBSimpleSQLLayer() */ +/***********************************************************************/ + +OGROpenFileGDBSimpleSQLLayer::~OGROpenFileGDBSimpleSQLLayer() +{ + if( poFeatureDefn ) + { + poFeatureDefn->Release(); + } + delete poIter; +} + +/***********************************************************************/ +/* ResetReading() */ +/***********************************************************************/ + +void OGROpenFileGDBSimpleSQLLayer::ResetReading() +{ + poIter->Reset(); +} + +/***********************************************************************/ +/* GetFeature() */ +/***********************************************************************/ + +OGRFeature* OGROpenFileGDBSimpleSQLLayer::GetFeature( GIntBig nFeatureId ) +{ + OGRFeature* poSrcFeature = poBaseLayer->GetFeature(nFeatureId); + if( poSrcFeature == NULL ) + return NULL; + + if( poFeatureDefn == poBaseLayer->GetLayerDefn() ) + return poSrcFeature; + else + { + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFrom(poSrcFeature); + poFeature->SetFID(poSrcFeature->GetFID()); + delete poSrcFeature; + return poFeature; + } +} + +/***********************************************************************/ +/* GetNextFeature() */ +/***********************************************************************/ + +OGRFeature* OGROpenFileGDBSimpleSQLLayer::GetNextFeature() +{ + while(TRUE) + { + int nRow = poIter->GetNextRowSortedByValue(); + if( nRow < 0 ) + return NULL; + OGRFeature* poFeature = GetFeature(nRow + 1); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL || + m_poAttrQuery->Evaluate( poFeature ) ) ) + { + return poFeature; + } + + delete poFeature; + } +} + +/***********************************************************************/ +/* GetFeatureCount() */ +/***********************************************************************/ + +GIntBig OGROpenFileGDBSimpleSQLLayer::GetFeatureCount( int bForce ) +{ + + /* No filter */ + if( m_poFilterGeom == NULL && m_poAttrQuery == NULL ) + { + return poIter->GetRowCount(); + } + + return OGRLayer::GetFeatureCount(bForce); +} + +/***********************************************************************/ +/* TestCapability() */ +/***********************************************************************/ + +int OGROpenFileGDBSimpleSQLLayer::TestCapability( const char * pszCap ) +{ + + if( EQUAL(pszCap,OLCFastFeatureCount) ) + { + return( m_poFilterGeom == NULL && m_poAttrQuery == NULL ); + } + else if( EQUAL(pszCap,OLCFastGetExtent) ) + { + return TRUE; + } + else if( EQUAL(pszCap,OLCRandomRead) ) + { + return TRUE; + } + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + { + return TRUE; /* ? */ + } + + return FALSE; +} + +/***********************************************************************/ +/* ExecuteSQL() */ +/***********************************************************************/ + +OGRLayer* OGROpenFileGDBDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) +{ + +/* -------------------------------------------------------------------- */ +/* Special case GetLayerDefinition */ +/* -------------------------------------------------------------------- */ + if (EQUALN(pszSQLCommand, "GetLayerDefinition ", strlen("GetLayerDefinition "))) + { + OGROpenFileGDBLayer* poLayer = (OGROpenFileGDBLayer*) + GetLayerByName(pszSQLCommand + strlen("GetLayerDefinition ")); + if (poLayer) + { + OGRLayer* poRet = new OGROpenFileGDBSingleFeatureLayer( + "LayerDefinition", poLayer->GetXMLDefinition().c_str() ); + return poRet; + } + else + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Special case GetLayerMetadata */ +/* -------------------------------------------------------------------- */ + if (EQUALN(pszSQLCommand, "GetLayerMetadata ", strlen("GetLayerMetadata "))) + { + OGROpenFileGDBLayer* poLayer = (OGROpenFileGDBLayer*) + GetLayerByName(pszSQLCommand + strlen("GetLayerMetadata ")); + if (poLayer) + { + OGRLayer* poRet = new OGROpenFileGDBSingleFeatureLayer( + "LayerMetadata", poLayer->GetXMLDocumentation().c_str() ); + return poRet; + } + else + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Special case GetLayerAttrIndexUse (only for debugging purposes) */ +/* -------------------------------------------------------------------- */ + if (EQUALN(pszSQLCommand, "GetLayerAttrIndexUse ", strlen("GetLayerAttrIndexUse "))) + { + OGROpenFileGDBLayer* poLayer = (OGROpenFileGDBLayer*) + GetLayerByName(pszSQLCommand + strlen("GetLayerAttrIndexUse ")); + if (poLayer) + { + OGRLayer* poRet = new OGROpenFileGDBSingleFeatureLayer( + "LayerAttrIndexUse", CPLSPrintf("%d", poLayer->GetAttrIndexUse()) ); + return poRet; + } + else + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Special case GetLayerSpatialIndexState (only for debugging purposes) */ +/* -------------------------------------------------------------------- */ + if (EQUALN(pszSQLCommand, "GetLayerSpatialIndexState ", strlen("GetLayerSpatialIndexState "))) + { + OGROpenFileGDBLayer* poLayer = (OGROpenFileGDBLayer*) + GetLayerByName(pszSQLCommand + strlen("GetLayerSpatialIndexState ")); + if (poLayer) + { + OGRLayer* poRet = new OGROpenFileGDBSingleFeatureLayer( + "LayerSpatialIndexState", CPLSPrintf("%d", poLayer->GetSpatialIndexState()) ); + return poRet; + } + else + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Special case GetLastSQLUsedOptimizedImplementation (only for debugging purposes) */ +/* -------------------------------------------------------------------- */ + if (EQUAL(pszSQLCommand, "GetLastSQLUsedOptimizedImplementation")) + { + OGRLayer* poRet = new OGROpenFileGDBSingleFeatureLayer( + "GetLastSQLUsedOptimizedImplementation", + CPLSPrintf("%d", bLastSQLUsedOptimizedImplementation) ); + return poRet; + } + + bLastSQLUsedOptimizedImplementation = FALSE; + +/* -------------------------------------------------------------------- */ +/* Special cases for SQL optimizations */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand, "SELECT ", strlen("SELECT ")) && + (pszDialect == NULL || EQUAL(pszDialect, "") || EQUAL(pszDialect, "OGRSQL")) && + CSLTestBoolean(CPLGetConfigOption("OPENFILEGDB_USE_INDEX", "YES")) ) + { + swq_select oSelect; + if( oSelect.preparse(pszSQLCommand) != OGRERR_NONE ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* MIN/MAX/SUM/AVG/COUNT optimization */ +/* -------------------------------------------------------------------- */ + if( oSelect.join_count == 0 && oSelect.poOtherSelect == NULL && + oSelect.table_count == 1 && oSelect.order_specs == 0 && + oSelect.query_mode != SWQM_DISTINCT_LIST ) + { + OGROpenFileGDBLayer* poLayer = + (OGROpenFileGDBLayer*)GetLayerByName( oSelect.table_defs[0].table_name); + if( poLayer ) + { + OGRMemLayer* poMemLayer = NULL; + + int i; + for(i = 0; i < oSelect.result_columns; i ++ ) + { + swq_col_func col_func = oSelect.column_defs[i].col_func; + if( !(col_func == SWQCF_MIN || col_func == SWQCF_MAX || + col_func == SWQCF_COUNT || col_func == SWQCF_AVG || + col_func == SWQCF_SUM) ) + break; + + if( oSelect.column_defs[i].field_name == NULL ) + break; + if( oSelect.column_defs[i].distinct_flag ) + break; + if( oSelect.column_defs[i].target_type != SWQ_OTHER ) + break; + + int idx = poLayer->GetLayerDefn()->GetFieldIndex( + oSelect.column_defs[i].field_name); + if( idx < 0 ) + break; + + OGRFieldDefn* poFieldDefn = poLayer->GetLayerDefn()->GetFieldDefn(idx); + + if( col_func == SWQCF_SUM && poFieldDefn->GetType() == OFTDateTime ) + break; + + int eOutOGRType = -1; + int nCount = 0; + double dfSum = 0.0; + const OGRField* psField = NULL; + OGRField sField; + if( col_func == SWQCF_MIN || col_func == SWQCF_MAX ) + { + psField = poLayer->GetMinMaxValue( + poFieldDefn, col_func == SWQCF_MIN, eOutOGRType); + if( eOutOGRType < 0 ) + break; + } + else + { + double dfMin = 0.0, dfMax = 0.0; + if( !poLayer->GetMinMaxSumCount(poFieldDefn, dfMin, dfMax, + dfSum, nCount) ) + break; + psField = &sField; + if( col_func == SWQCF_AVG ) + { + if( nCount == 0 ) + { + eOutOGRType = OFTReal; + psField = NULL; + } + else + { + if( poFieldDefn->GetType() == OFTDateTime ) + { + eOutOGRType = OFTDateTime; + FileGDBDoubleDateToOGRDate(dfSum / nCount, &sField); + } + else + { + eOutOGRType = OFTReal; + sField.Real = dfSum / nCount; + } + } + } + else if( col_func == SWQCF_COUNT ) + { + sField.Integer = nCount; + eOutOGRType = OFTInteger; + } + else + { + sField.Real = dfSum; + eOutOGRType = OFTReal; + } + } + + if( poMemLayer == NULL ) + { + poMemLayer = new OGRMemLayer("SELECT", NULL, wkbNone); + OGRFeature* poFeature = new OGRFeature(poMemLayer->GetLayerDefn()); + poMemLayer->CreateFeature(poFeature); + delete poFeature; + } + + const char* pszMinMaxFieldName = + CPLSPrintf( "%s_%s", (col_func == SWQCF_MIN) ? "MIN" : + (col_func == SWQCF_MAX) ? "MAX" : + (col_func == SWQCF_AVG) ? "AVG" : + (col_func == SWQCF_SUM) ? "SUM" : + "COUNT", + oSelect.column_defs[i].field_name); + OGRFieldDefn oFieldDefn(pszMinMaxFieldName, + (OGRFieldType) eOutOGRType); + poMemLayer->CreateField(&oFieldDefn); + if( psField != NULL ) + { + OGRFeature* poFeature = poMemLayer->GetFeature(0); + poFeature->SetField(oFieldDefn.GetNameRef(), (OGRField*) psField); + poMemLayer->SetFeature(poFeature); + delete poFeature; + } + } + if( i != oSelect.result_columns ) + { + delete poMemLayer; + } + else + { + CPLDebug("OpenFileGDB", + "Using optimized MIN/MAX/SUM/AVG/COUNT implementation"); + bLastSQLUsedOptimizedImplementation = TRUE; + return poMemLayer; + } + } + } + +/* -------------------------------------------------------------------- */ +/* ORDER BY optimization */ +/* -------------------------------------------------------------------- */ + if( oSelect.join_count == 0 && oSelect.poOtherSelect == NULL && + oSelect.table_count == 1 && oSelect.order_specs == 1 && + oSelect.query_mode != SWQM_DISTINCT_LIST ) + { + OGROpenFileGDBLayer* poLayer = + (OGROpenFileGDBLayer*)GetLayerByName( oSelect.table_defs[0].table_name); + if( poLayer != NULL && + poLayer->HasIndexForField(oSelect.order_defs[0].field_name) ) + { + OGRErr eErr = OGRERR_NONE; + if( oSelect.where_expr != NULL ) + { + /* The where must be a simple comparison on the column */ + /* that is used for ordering */ + if( oSelect.where_expr->eNodeType == SNT_OPERATION && + OGROpenFileGDBIsComparisonOp(oSelect.where_expr->nOperation) && + oSelect.where_expr->nOperation != SWQ_NE && + oSelect.where_expr->nSubExprCount == 2 && + (oSelect.where_expr->papoSubExpr[0]->eNodeType == SNT_COLUMN || + oSelect.where_expr->papoSubExpr[0]->eNodeType == SNT_CONSTANT) && + oSelect.where_expr->papoSubExpr[0]->field_type == SWQ_STRING && + EQUAL(oSelect.where_expr->papoSubExpr[0]->string_value, + oSelect.order_defs[0].field_name) && + oSelect.where_expr->papoSubExpr[1]->eNodeType == SNT_CONSTANT ) + { + /* ok */ + } + else + eErr = OGRERR_FAILURE; + } + if( eErr == OGRERR_NONE ) + { + int i; + for(i = 0; i < oSelect.result_columns; i ++ ) + { + if( oSelect.column_defs[i].col_func != SWQCF_NONE ) + break; + if( oSelect.column_defs[i].field_name == NULL ) + break; + if( oSelect.column_defs[i].distinct_flag ) + break; + if( oSelect.column_defs[i].target_type != SWQ_OTHER ) + break; + if( strcmp(oSelect.column_defs[i].field_name, "*") != 0 && + poLayer->GetLayerDefn()->GetFieldIndex( + oSelect.column_defs[i].field_name) < 0 ) + break; + } + if( i != oSelect.result_columns ) + eErr = OGRERR_FAILURE; + } + if( eErr == OGRERR_NONE ) + { + int op = -1; + swq_expr_node* poValue = NULL; + if( oSelect.where_expr != NULL ) + { + op = oSelect.where_expr->nOperation; + poValue = oSelect.where_expr->papoSubExpr[1]; + } + + FileGDBIterator *poIter = poLayer->BuildIndex( + oSelect.order_defs[0].field_name, + oSelect.order_defs[0].ascending_flag, + op, poValue); + + /* Check that they are no NULL values */ + if( oSelect.where_expr == NULL && + poIter->GetRowCount() != poLayer->GetFeatureCount(FALSE) ) + { + delete poIter; + poIter = NULL; + } + + if( poIter != NULL ) + { + CPLDebug("OpenFileGDB", "Using OGROpenFileGDBSimpleSQLLayer"); + bLastSQLUsedOptimizedImplementation = TRUE; + return new OGROpenFileGDBSimpleSQLLayer(poLayer, + poIter, + oSelect.result_columns, + oSelect.column_defs); + } + } + } + } + } + + return OGRDataSource::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect); +} + +/***********************************************************************/ +/* ReleaseResultSet() */ +/***********************************************************************/ + +void OGROpenFileGDBDataSource::ReleaseResultSet( OGRLayer * poResultsSet ) +{ + delete poResultsSet; +} + +/***********************************************************************/ +/* GetFileList() */ +/***********************************************************************/ + +char** OGROpenFileGDBDataSource::GetFileList() +{ + int nInterestTable = -1; + const char* pszFilenameWithoutPath = CPLGetFilename(m_pszName); + CPLString osFilenameRadix; + if( strlen(pszFilenameWithoutPath) == strlen("a00000000.gdbtable") && + pszFilenameWithoutPath[0] == 'a' && + sscanf(pszFilenameWithoutPath, "a%08x.gdbtable", &nInterestTable) == 1 ) + { + osFilenameRadix = CPLSPrintf("a%08x.", nInterestTable); + } + + char** papszFiles = VSIReadDir(m_osDirName); + CPLStringList osStringList; + char** papszIter = papszFiles; + for( ; papszIter != NULL && *papszIter != NULL ; papszIter ++ ) + { + if( strcmp(*papszIter, ".") == 0 || strcmp(*papszIter, "..") == 0 ) + continue; + if( osFilenameRadix.size() == 0 || + strncmp(*papszIter, osFilenameRadix, osFilenameRadix.size()) == 0 ) + { + osStringList.AddString(CPLFormFilename(m_osDirName, *papszIter, NULL)); + } + } + CSLDestroy(papszFiles); + return osStringList.StealList(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdriver.cpp new file mode 100644 index 000000000..ea4a23c67 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdbdriver.cpp @@ -0,0 +1,203 @@ +/****************************************************************************** + * $Id: ogropenfilegdbdriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements Open FileGDB OGR driver. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_openfilegdb.h" + +CPL_CVSID("$Id"); + +// g++ -O2 -Wall -Wextra -g -shared -fPIC ogr/ogrsf_frmts/openfilegdb/*.cpp -o ogr_OpenFileGDB.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/mem -Iogr/ogrsf_frmts/openfilegdb -L. -lgdal + +extern "C" void RegisterOGROpenFileGDB(); + + +#define ENDS_WITH(str, strLen, end) \ + (strLen >= strlen(end) && EQUAL(str + strLen - strlen(end), end)) + +/************************************************************************/ +/* OGROpenFileGDBDriverIdentify() */ +/************************************************************************/ + +static int OGROpenFileGDBDriverIdentifyInternal( GDALOpenInfo* poOpenInfo, + const char*& pszFilename ) +{ +#ifdef FOR_FUSIL + CPLString osOrigFilename(pszFilename); +#endif + + // First check if we have to do any work. + size_t nLen = strlen(pszFilename); + if( ENDS_WITH(pszFilename, nLen, ".gdb") || + ENDS_WITH(pszFilename, nLen, ".gdb/") ) + { + /* Check that the filename is really a directory, to avoid confusion with */ + /* Garmin MapSource - gdb format which can be a problem when the */ + /* driver is loaded as a plugin, and loaded before the GPSBabel driver */ + /* (http://trac.osgeo.org/osgeo4w/ticket/245) */ + if( strncmp(pszFilename, "/vsicurl/https://github.com/", + strlen("/vsicurl/https://github.com/")) == 0 || + !poOpenInfo->bStatOK || + !poOpenInfo->bIsDirectory ) + { + /* In case we don't manage to list the directory, try to stat one file */ + VSIStatBufL stat; + if( !(strncmp(pszFilename, "/vsicurl/", strlen("/vsicurl/")) == 0 && + VSIStatL( CPLFormFilename(pszFilename, "a00000001", "gdbtable"), &stat ) == 0) ) + { + return FALSE; + } + } + return TRUE; + } + /* We also accept zipped GDB */ + else if( ENDS_WITH(pszFilename, nLen, ".gdb.zip") || + ENDS_WITH(pszFilename, nLen, ".gdb.tar") || + /* Canvec GBs */ + (ENDS_WITH(pszFilename, nLen, ".zip") && + (strstr(pszFilename, "_gdb") != NULL || + strstr(pszFilename, "_GDB") != NULL)) ) + { + return TRUE; + } + /* We also accept tables themselves */ + else if( ENDS_WITH(pszFilename, nLen, ".gdbtable") ) + { + return TRUE; + } +#ifdef FOR_FUSIL + /* To be able to test fuzzer on any auxiliary files used (indexes, etc.) */ + else if( strlen(CPLGetBasename(pszFilename)) == 9 && + CPLGetBasename(pszFilename)[0] == 'a' ) + { + pszFilename = CPLFormFilename(CPLGetPath(pszFilename), + CPLGetBasename(pszFilename), + "gdbtable"); + return TRUE; + } + else if( strlen(CPLGetBasename(CPLGetBasename(pszFilename))) == 9 && + CPLGetBasename(CPLGetBasename(pszFilename))[0] == 'a' ) + { + pszFilename = CPLFormFilename(CPLGetPath(pszFilename), + CPLGetBasename(CPLGetBasename(pszFilename)), + "gdbtable"); + return TRUE; + } +#endif + else + { + return FALSE; + } +} + +static int OGROpenFileGDBDriverIdentify( GDALOpenInfo* poOpenInfo ) +{ + const char* pszFilename = poOpenInfo->pszFilename; + return OGROpenFileGDBDriverIdentifyInternal( poOpenInfo, pszFilename ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset* OGROpenFileGDBDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->eAccess == GA_Update ) + return NULL; + const char* pszFilename = poOpenInfo->pszFilename; +#ifdef FOR_FUSIL + CPLString osOrigFilename(pszFilename); +#endif + if( OGROpenFileGDBDriverIdentifyInternal( poOpenInfo, pszFilename ) == FALSE ) + return NULL; + +#ifdef FOR_FUSIL + const char* pszSrcDir = CPLGetConfigOption("FUSIL_SRC_DIR", NULL); + if( pszSrcDir != NULL && VSIStatL( osOrigFilename, &stat ) == 0 && + VSI_ISREG(stat.st_mode) ) + { + /* Copy all files from FUSIL_SRC_DIR to directory of pszFilename */ + /* except pszFilename itself */ + CPLString osSave(pszFilename); + char** papszFiles = VSIReadDir(pszSrcDir); + for(int i=0; papszFiles[i] != NULL; i++) + { + if( strcmp(papszFiles[i], CPLGetFilename(osOrigFilename)) != 0 ) + { + CPLCopyFile(CPLFormFilename(CPLGetPath(osOrigFilename), papszFiles[i], NULL), + CPLFormFilename(pszSrcDir, papszFiles[i], NULL)); + } + } + CSLDestroy(papszFiles); + pszFilename = CPLFormFilename("", osSave.c_str(), NULL); + } +#endif + + OGROpenFileGDBDataSource* poDS = new OGROpenFileGDBDataSource(); + if( poDS->Open( pszFilename ) ) + { + return poDS; + } + else + { + delete poDS; + return NULL; + } +} + +/***********************************************************************/ +/* RegisterOGROpenFileGDB() */ +/***********************************************************************/ + +void RegisterOGROpenFileGDB() + +{ + if (! GDAL_CHECK_VERSION("OGR OpenFileGDB")) + return; + GDALDriver *poDriver; + + if( GDALGetDriverByName( "OpenFileGDB" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "OpenFileGDB" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ESRI FileGDB" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "gdb" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_openfilegdb.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGROpenFileGDBDriverOpen; + poDriver->pfnIdentify = OGROpenFileGDBDriverIdentify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer.cpp new file mode 100644 index 000000000..3762201ca --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/openfilegdb/ogropenfilegdblayer.cpp @@ -0,0 +1,1794 @@ +/****************************************************************************** + * $Id: ogropenfilegdblayer.cpp 29158 2015-05-05 21:19:37Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements Open FileGDB OGR driver. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_openfilegdb.h" +#include "cpl_minixml.h" +#include + +CPL_CVSID("$Id"); + +/************************************************************************/ +/* OGROpenFileGDBGeomFieldDefn */ +/************************************************************************/ +class OGROpenFileGDBGeomFieldDefn: public OGRGeomFieldDefn +{ + OGROpenFileGDBLayer* m_poLayer; + + public: + OGROpenFileGDBGeomFieldDefn(OGROpenFileGDBLayer* poLayer, + const char *pszNameIn, + OGRwkbGeometryType eGeomTypeIn) : + OGRGeomFieldDefn(pszNameIn, eGeomTypeIn), m_poLayer(poLayer) + { + }; + + ~OGROpenFileGDBGeomFieldDefn() {} + + void UnsetLayer() { m_poLayer = NULL; } + + virtual OGRSpatialReference* GetSpatialRef() + { + if( poSRS ) + return poSRS; + if( m_poLayer != NULL ) + (void) m_poLayer->BuildLayerDefinition(); + return poSRS; + } +}; + +/************************************************************************/ +/* OGROpenFileGDBFeatureDefn */ +/************************************************************************/ +class OGROpenFileGDBFeatureDefn: public OGRFeatureDefn +{ + OGROpenFileGDBLayer* m_poLayer; + int m_bHasBuildFieldDefn; + + public: + OGROpenFileGDBFeatureDefn( OGROpenFileGDBLayer* poLayer, + const char * pszName ) : + OGRFeatureDefn(pszName), m_poLayer(poLayer) + { + m_bHasBuildFieldDefn = FALSE; + } + + ~OGROpenFileGDBFeatureDefn() {} + + void UnsetLayer() + { + if( nGeomFieldCount ) + ((OGROpenFileGDBGeomFieldDefn*)papoGeomFieldDefn[0])->UnsetLayer(); + m_poLayer = NULL; + } + + virtual int GetFieldCount() + { + if( nFieldCount ) + return nFieldCount; + if( !m_bHasBuildFieldDefn && m_poLayer != NULL ) + { + m_bHasBuildFieldDefn = TRUE; + (void) m_poLayer->BuildLayerDefinition(); + } + return nFieldCount; + } + + virtual int GetGeomFieldCount() + { + /* FileGDB v9 case */ + if( !m_bHasBuildFieldDefn && + m_poLayer != NULL && m_poLayer->m_eGeomType != wkbNone && + m_poLayer->m_osDefinition.size() == 0 ) + { + m_bHasBuildFieldDefn = TRUE; + (void) m_poLayer->BuildLayerDefinition(); + } + return nGeomFieldCount; + } + + virtual OGRGeomFieldDefn* GetGeomFieldDefn( int i ) + { + /* FileGDB v9 case */ + if( !m_bHasBuildFieldDefn && + m_poLayer != NULL && m_poLayer->m_eGeomType != wkbNone && + m_poLayer->m_osDefinition.size() == 0 ) + { + m_bHasBuildFieldDefn = TRUE; + (void) m_poLayer->BuildLayerDefinition(); + } + return OGRFeatureDefn::GetGeomFieldDefn(i); + } +}; + +/************************************************************************/ +/* OGROpenFileGDBLayer() */ +/************************************************************************/ + +OGROpenFileGDBLayer::OGROpenFileGDBLayer(const char* pszGDBFilename, + const char* pszName, + const std::string& osDefinition, + const std::string& osDocumentation, + CPL_UNUSED const char* pszGeomName, + OGRwkbGeometryType eGeomType) : + m_osGDBFilename(pszGDBFilename), + m_osName(pszName), + m_poLyrTable(NULL), + m_poFeatureDefn(NULL), + m_iGeomFieldIdx(-1), + m_iCurFeat(0), + m_osDefinition(osDefinition), + m_osDocumentation(osDocumentation), + m_eGeomType(wkbNone), + m_bValidLayerDefn(-1), + m_bEOF(FALSE), + m_poGeomConverter(NULL), + m_iFieldToReadAsBinary(-1), + m_poIterator(NULL), + m_bIteratorSufficientToEvaluateFilter(FALSE), + m_poIterMinMax(NULL), + m_eSpatialIndexState(SPI_IN_BUILDING), + m_pQuadTree(NULL), + m_pahFilteredFeatures(NULL), + m_nFilteredFeatureCount(-1) +{ + m_poFeatureDefn = new OGROpenFileGDBFeatureDefn(this, pszName); + SetDescription( m_poFeatureDefn->GetName() ); + m_poFeatureDefn->SetGeomType(wkbNone); + m_poFeatureDefn->Reference(); + + m_eGeomType = eGeomType; + + if( m_osDefinition.size() ) + { + (void) BuildGeometryColumnGDBv10(); + } +} + +/***********************************************************************/ +/* ~OGROpenFileGDBLayer() */ +/***********************************************************************/ + +OGROpenFileGDBLayer::~OGROpenFileGDBLayer() +{ + delete m_poLyrTable; + if( m_poFeatureDefn ) + { + m_poFeatureDefn->UnsetLayer(); + m_poFeatureDefn->Release(); + } + delete m_poIterator; + delete m_poIterMinMax; + delete m_poGeomConverter; + if( m_pQuadTree != NULL ) + CPLQuadTreeDestroy(m_pQuadTree); + CPLFree(m_pahFilteredFeatures); +} + +/************************************************************************/ +/* BuildGeometryColumnGDBv10() */ +/************************************************************************/ + +int OGROpenFileGDBLayer::BuildGeometryColumnGDBv10() +{ + CPLXMLNode* psTree = CPLParseXMLString(m_osDefinition.c_str()); + if( psTree == NULL ) + { + return FALSE; + } + + CPLStripXMLNamespace( psTree, NULL, TRUE ); + /* CPLSerializeXMLTreeToFile( psTree, "/dev/stderr" ); */ + CPLXMLNode* psInfo = CPLSearchXMLNode( psTree, "=DEFeatureClassInfo" ); + if( psInfo == NULL ) + psInfo = CPLSearchXMLNode( psTree, "=DETableInfo" ); + if( psInfo == NULL ) + { + CPLDestroyXMLNode(psTree); + return FALSE; + } + + /* We cannot trust the XML definition to build the field definitions. */ + /* It sometimes misses a few fields ! */ + + int bHasZ = CSLTestBoolean(CPLGetXMLValue( psInfo, "HasZ", "NO" )); + const char* pszShapeType = CPLGetXMLValue(psInfo, "ShapeType", NULL); + const char* pszShapeFieldName = CPLGetXMLValue(psInfo, "ShapeFieldName", NULL); + if( pszShapeType != NULL && pszShapeFieldName != NULL ) + { + m_eGeomType = + FileGDBOGRGeometryConverter::GetGeometryTypeFromESRI(pszShapeType); + if( bHasZ ) + m_eGeomType = wkbSetZ( m_eGeomType ); + + const char* pszWKT = CPLGetXMLValue( psInfo, "SpatialReference.WKT", NULL ); + int nWKID = atoi(CPLGetXMLValue( psInfo, "SpatialReference.WKID", "0" )); + /* The concept of LatestWKID is explained in http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r3000000n1000000 */ + int nLatestWKID = atoi(CPLGetXMLValue( psInfo, "SpatialReference.LatestWKID", "0" )); + + OGROpenFileGDBGeomFieldDefn* poGeomFieldDefn = + new OGROpenFileGDBGeomFieldDefn(NULL, pszShapeFieldName, m_eGeomType); + + CPLXMLNode* psGPFieldInfoExs = CPLGetXMLNode(psInfo, "GPFieldInfoExs"); + if( psGPFieldInfoExs ) + { + for(CPLXMLNode* psChild = psGPFieldInfoExs->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType != CXT_Element ) + continue; + if( EQUAL(psChild->pszValue, "GPFieldInfoEx") && + EQUAL(CPLGetXMLValue(psChild, "Name", ""), pszShapeFieldName) ) + { + poGeomFieldDefn->SetNullable( CSLTestBoolean(CPLGetXMLValue( psChild, "IsNullable", "TRUE" )) ); + break; + } + } + } + + OGRSpatialReference* poSRS = NULL; + if( nWKID > 0 || nLatestWKID > 0 ) + { + int bSuccess = FALSE; + poSRS = new OGRSpatialReference(); + CPLPushErrorHandler(CPLQuietErrorHandler); + /* Try first with nLatestWKID as there's a higher chance it is a EPSG code and not an ESRI one */ + if( nLatestWKID > 0 ) + { + if( poSRS->importFromEPSG(nLatestWKID) == OGRERR_NONE ) + { + bSuccess = TRUE; + } + else + { + CPLDebug("OpenFileGDB", "Cannot import SRID %d", nLatestWKID); + } + } + if( !bSuccess && nWKID > 0 ) + { + if( poSRS->importFromEPSG(nWKID) == OGRERR_NONE ) + { + bSuccess = TRUE; + } + else + { + CPLDebug("OpenFileGDB", "Cannot import SRID %d", nWKID); + } + } + if( !bSuccess ) + { + delete poSRS; + poSRS = NULL; + } + CPLPopErrorHandler(); + CPLErrorReset(); + } + if( poSRS == NULL && pszWKT != NULL && pszWKT[0] != '{' ) + { + poSRS = new OGRSpatialReference( pszWKT ); + if( poSRS->morphFromESRI() != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + if( poSRS != NULL ) + { + poGeomFieldDefn->SetSpatialRef(poSRS); + poSRS->Dereference(); + } + m_poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + } + else + { + m_eGeomType = wkbNone; + } + CPLDestroyXMLNode(psTree); + return TRUE; +} + +/************************************************************************/ +/* BuildLayerDefinition() */ +/************************************************************************/ + +int OGROpenFileGDBLayer::BuildLayerDefinition() +{ + if( m_bValidLayerDefn >= 0 ) + return m_bValidLayerDefn; + + m_poLyrTable = new FileGDBTable(); + if( !(m_poLyrTable->Open(m_osGDBFilename, GetDescription())) ) + { + delete m_poLyrTable; + m_poLyrTable = NULL; + m_bValidLayerDefn = FALSE; + return FALSE; + } + + m_bValidLayerDefn = TRUE; + + m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx(); + if( m_iGeomFieldIdx >= 0 ) + { + FileGDBGeomField* poGDBGeomField = + (FileGDBGeomField* )m_poLyrTable->GetField(m_iGeomFieldIdx); + m_poGeomConverter = FileGDBOGRGeometryConverter::BuildConverter(poGDBGeomField); + + if( CSLTestBoolean(CPLGetConfigOption("OPENFILEGDB_IN_MEMORY_SPI", "YES")) ) + { + CPLRectObj sGlobalBounds; + sGlobalBounds.minx = poGDBGeomField->GetXMin(); + sGlobalBounds.miny = poGDBGeomField->GetYMin(); + sGlobalBounds.maxx = poGDBGeomField->GetXMax(); + sGlobalBounds.maxy = poGDBGeomField->GetYMax(); + m_pQuadTree = CPLQuadTreeCreate(&sGlobalBounds, + NULL); + CPLQuadTreeSetMaxDepth(m_pQuadTree, + CPLQuadTreeGetAdvisedMaxDepth(m_poLyrTable->GetValidRecordCount())); + } + else + { + m_eSpatialIndexState = SPI_INVALID; + } + } + + if( m_osDefinition.size() == 0 && m_iGeomFieldIdx >= 0 ) + { + /* FileGDB v9 case */ + FileGDBGeomField* poGDBGeomField = + (FileGDBGeomField* )m_poLyrTable->GetField(m_iGeomFieldIdx); + const char* pszName = poGDBGeomField->GetName().c_str(); + FileGDBTableGeometryType eGDBGeomType = m_poLyrTable->GetGeometryType(); + + OGRwkbGeometryType eGeomType = wkbUnknown; + switch( eGDBGeomType ) + { + case FGTGT_NONE: /* doesn't make sense ! */ break; + case FGTGT_POINT: eGeomType = wkbPoint; break; + case FGTGT_MULTIPOINT: eGeomType = wkbMultiPoint; break; + case FGTGT_LINE: eGeomType = wkbMultiLineString; break; + case FGTGT_POLYGON: eGeomType = wkbMultiPolygon; break; + case FGTGT_MULTIPATCH: eGeomType = wkbMultiPolygon; break; + } + + if( m_eGeomType != wkbUnknown && wkbFlatten(eGeomType) != wkbFlatten(m_eGeomType) ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Inconsistency for layer geometry type"); + } + + m_eGeomType = eGeomType; + if( poGDBGeomField->Has3D() ) + m_eGeomType = wkbSetZ(m_eGeomType); + + OGROpenFileGDBGeomFieldDefn* poGeomFieldDefn; + + poGeomFieldDefn = + new OGROpenFileGDBGeomFieldDefn(NULL, pszName, m_eGeomType); + poGeomFieldDefn->SetNullable(poGDBGeomField->IsNullable()); + + m_poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + + OGRSpatialReference* poSRS = NULL; + if( poGDBGeomField->GetWKT().size() && + poGDBGeomField->GetWKT()[0] != '{' ) + { + poSRS = new OGRSpatialReference( poGDBGeomField->GetWKT().c_str() ); + if( poSRS->morphFromESRI() != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + if( poSRS != NULL ) + { + poGeomFieldDefn->SetSpatialRef(poSRS); + poSRS->Dereference(); + } + } + else if( m_osDefinition.size() == 0 && m_iGeomFieldIdx < 0 ) + { + m_eGeomType = wkbNone; + } + + CPLXMLNode* psTree = NULL; + CPLXMLNode* psGPFieldInfoExs = NULL; + + for(int i=0;iGetFieldCount();i++) + { + if( i == m_iGeomFieldIdx ) + continue; + + const FileGDBField* poGDBField = m_poLyrTable->GetField(i); + OGRFieldType eType = OFTString; + OGRFieldSubType eSubType = OFSTNone; + int nWidth = poGDBField->GetMaxWidth(); + switch( poGDBField->GetType() ) + { + case FGFT_INT16: + eType = OFTInteger; + eSubType = OFSTInt16; + break; + case FGFT_INT32: + eType = OFTInteger; + break; + case FGFT_FLOAT32: + eType = OFTReal; + eSubType = OFSTFloat32; + break; + case FGFT_FLOAT64: + eType = OFTReal; + break; + case FGFT_STRING: + /* nWidth = poGDBField->GetMaxWidth(); */ + eType = OFTString; + break; + case FGFT_UUID_1: + case FGFT_UUID_2: + case FGFT_XML: + eType = OFTString; + break; + case FGFT_DATETIME: + eType = OFTDateTime; + break; + case FGFT_UNDEFINED: + case FGFT_OBJECTID: + case FGFT_GEOMETRY: + CPLAssert(FALSE); + break; + case FGFT_BINARY: + case FGFT_RASTER: + { + /* Special case for v9 GDB_UserMetadata table */ + if( m_iFieldToReadAsBinary < 0 && + poGDBField->GetName() == "Xml" && + poGDBField->GetType() == FGFT_BINARY ) + { + m_iFieldToReadAsBinary = i; + eType = OFTString; + } + else + eType = OFTBinary; + break; + } + } + OGRFieldDefn oFieldDefn(poGDBField->GetName().c_str(), eType); + oFieldDefn.SetSubType(eSubType); + /* On creation in the FileGDB driver (GDBFieldTypeToWidthPrecision) if string width is 0, we pick up */ + /* 65535 by default to mean unlimited string length, but we don't want */ + /* to advertize such a big number */ + if( eType == OFTString && nWidth < 65535 ) + oFieldDefn.SetWidth(nWidth); + oFieldDefn.SetNullable(poGDBField->IsNullable()); + const OGRField* psDefault = poGDBField->GetDefault(); + if( !(psDefault->Set.nMarker1 == OGRUnsetMarker && + psDefault->Set.nMarker2 == OGRUnsetMarker) ) + { + if( eType == OFTString ) + { + CPLString osDefault("'"); + char* pszTmp = CPLEscapeString(psDefault->String, -1, CPLES_SQL); + osDefault += pszTmp; + CPLFree(pszTmp); + osDefault += "'"; + oFieldDefn.SetDefault(osDefault); + } + else if( eType == OFTInteger || eType == OFTReal ) + { + /* GDBs and the FileGDB SDK aren't always reliable for numeric values */ + /* It often occurs that the XML definition in a00000004.gdbtable doesn't */ + /* match the default values (in binary) found in the field definition */ + /* section of the .gdbtable of the layers themselves */ + /* So check consistency */ + if( m_osDefinition.size() && psTree == NULL ) + { + psTree = CPLParseXMLString(m_osDefinition.c_str()); + if( psTree != NULL ) + { + CPLStripXMLNamespace( psTree, NULL, TRUE ); + CPLXMLNode* psInfo = CPLSearchXMLNode( psTree, "=DEFeatureClassInfo" ); + if( psInfo == NULL ) + psInfo = CPLSearchXMLNode( psTree, "=DETableInfo" ); + if( psInfo != NULL ) + psGPFieldInfoExs = CPLGetXMLNode(psInfo, "GPFieldInfoExs"); + } + } + const char* pszDefaultValue = NULL; + if( psGPFieldInfoExs != NULL ) + { + for(CPLXMLNode* psChild = psGPFieldInfoExs->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType != CXT_Element ) + continue; + if( EQUAL(psChild->pszValue, "GPFieldInfoEx") && + EQUAL(CPLGetXMLValue(psChild, "Name", ""), poGDBField->GetName().c_str()) ) + { + /* From ArcGIS this is called DefaultValueNumeric for integer and real */ + /* From FileGDB API this is called DefaultValue xsi:type=xs:int for integer and DefaultValueNumeric for real ... */ + pszDefaultValue = CPLGetXMLValue( psChild, "DefaultValueNumeric", NULL ); + if( pszDefaultValue == NULL ) + pszDefaultValue = CPLGetXMLValue( psChild, "DefaultValue", NULL ); + break; + } + } + } + if( pszDefaultValue != NULL ) + { + if( eType == OFTInteger ) + { + if ( atoi(pszDefaultValue) != psDefault->Integer) + { + CPLDebug("OpenFileGDB", "For field %s, XML definition mentions %s " + "as default value whereas .gdbtable header mentions %d. Using %s", + poGDBField->GetName().c_str(), + pszDefaultValue, + psDefault->Integer, + pszDefaultValue); + } + oFieldDefn.SetDefault(pszDefaultValue); + } + else if( eType == OFTReal ) + { + if( fabs(CPLAtof(pszDefaultValue) - psDefault->Real) > 1e-15 ) + { + CPLDebug("OpenFileGDB", "For field %s, XML definition mentions %s " + "as default value whereas .gdbtable header mentions %.18g. Using %s", + poGDBField->GetName().c_str(), + pszDefaultValue, + psDefault->Real, + pszDefaultValue); + } + oFieldDefn.SetDefault(pszDefaultValue); + } + } + } + else if( eType == OFTDateTime ) + oFieldDefn.SetDefault(CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%02d'", + psDefault->Date.Year, + psDefault->Date.Month, + psDefault->Date.Day, + psDefault->Date.Hour, + psDefault->Date.Minute, + (int)psDefault->Date.Second)); + } + m_poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + + if( m_poLyrTable->HasDeletedFeaturesListed() ) + { + OGRFieldDefn oFieldDefn("_deleted_", OFTInteger); + m_poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + + if( psTree != NULL ) + CPLDestroyXMLNode(psTree); + + return TRUE; +} + +/************************************************************************/ +/* GetGeomType() */ +/************************************************************************/ + +OGRwkbGeometryType OGROpenFileGDBLayer::GetGeomType() +{ + if( m_eGeomType == wkbUnknown || + m_osDefinition.size() == 0 /* FileGDB v9 case */ ) + { + (void) BuildLayerDefinition(); + } + + return m_eGeomType; +} + +/***********************************************************************/ +/* GetLayerDefn() */ +/***********************************************************************/ + +OGRFeatureDefn* OGROpenFileGDBLayer::GetLayerDefn() +{ + return m_poFeatureDefn; +} + +/***********************************************************************/ +/* GetFIDColumn() */ +/***********************************************************************/ + +const char* OGROpenFileGDBLayer::GetFIDColumn() +{ + if( !BuildLayerDefinition() ) + return ""; + return m_poLyrTable->GetObjectIdColName().c_str(); +} + +/***********************************************************************/ +/* ResetReading() */ +/***********************************************************************/ + +void OGROpenFileGDBLayer::ResetReading() +{ + if( m_iCurFeat != 0 ) + { + if( m_eSpatialIndexState == SPI_IN_BUILDING ) + m_eSpatialIndexState = SPI_INVALID; + } + m_bEOF = FALSE; + m_iCurFeat = 0; + if( m_poIterator ) + m_poIterator->Reset(); +} + +/***********************************************************************/ +/* SetSpatialFilter() */ +/***********************************************************************/ + +void OGROpenFileGDBLayer::SetSpatialFilter( OGRGeometry *poGeom ) +{ + if( !BuildLayerDefinition() ) + return; + + OGRLayer::SetSpatialFilter(poGeom); + + if( m_bFilterIsEnvelope ) + { + OGREnvelope sLayerEnvelope; + if( GetExtent(&sLayerEnvelope, FALSE) == OGRERR_NONE ) + { + if( m_sFilterEnvelope.MinX <= sLayerEnvelope.MinX && + m_sFilterEnvelope.MinY <= sLayerEnvelope.MinY && + m_sFilterEnvelope.MaxX >= sLayerEnvelope.MaxX && + m_sFilterEnvelope.MaxY >= sLayerEnvelope.MaxY ) + { + CPLDebug("OpenFileGDB", "Disabling spatial filter since it " + "contains the layer spatial extent"); + poGeom = NULL; + OGRLayer::SetSpatialFilter(poGeom); + } + } + } + + if( poGeom != NULL ) + { + if( m_eSpatialIndexState == SPI_COMPLETED ) + { + CPLRectObj aoi; + aoi.minx = m_sFilterEnvelope.MinX; + aoi.miny = m_sFilterEnvelope.MinY; + aoi.maxx = m_sFilterEnvelope.MaxX; + aoi.maxy = m_sFilterEnvelope.MaxY; + CPLFree(m_pahFilteredFeatures); + m_nFilteredFeatureCount = -1; + m_pahFilteredFeatures = CPLQuadTreeSearch(m_pQuadTree, + &aoi, + &m_nFilteredFeatureCount); + if( m_nFilteredFeatureCount >= 0 ) + { + size_t* panStart = (size_t*)m_pahFilteredFeatures; + std::sort(panStart, panStart + m_nFilteredFeatureCount); + } + } + m_poLyrTable->InstallFilterEnvelope(&m_sFilterEnvelope); + } + else + { + CPLFree(m_pahFilteredFeatures); + m_pahFilteredFeatures = NULL; + m_nFilteredFeatureCount = -1; + m_poLyrTable->InstallFilterEnvelope(NULL); + } +} + +/***********************************************************************/ +/* CompValues() */ +/***********************************************************************/ + +static int CompValues(OGRFieldDefn* poFieldDefn, + const swq_expr_node* poValue1, + const swq_expr_node* poValue2) +{ + switch( poFieldDefn->GetType() ) + { + case OFTInteger: + { + int n1, n2; + if (poValue1->field_type == SWQ_FLOAT) + n1 = (int) poValue1->float_value; + else + n1 = (int) poValue1->int_value; + if (poValue2->field_type == SWQ_FLOAT) + n2 = (int) poValue2->float_value; + else + n2 = (int) poValue2->int_value; + if( n1 < n2 ) + return -1; + if( n1 == n2 ) + return 0; + else + return 1; + break; + } + + case OFTReal: + if( poValue1->float_value < poValue2->float_value ) + return -1; + if( poValue1->float_value == poValue2->float_value ) + return 0; + else + return 1; + break; + + case OFTString: + return strcmp(poValue1->string_value, poValue2->string_value); + break; + + case OFTDate: + case OFTTime: + case OFTDateTime: + { + if ((poValue1->field_type == SWQ_TIMESTAMP || + poValue1->field_type == SWQ_DATE || + poValue1->field_type == SWQ_TIME) && + (poValue2->field_type == SWQ_TIMESTAMP || + poValue2->field_type == SWQ_DATE || + poValue2->field_type == SWQ_TIME)) + { + return strcmp(poValue1->string_value, poValue2->string_value); + } + return 0; + break; + } + + default: + return 0; + break; + } +} + +/***********************************************************************/ +/* OGROpenFileGDBIsComparisonOp() */ +/***********************************************************************/ + +int OGROpenFileGDBIsComparisonOp(int op) +{ + return (op == SWQ_EQ || op == SWQ_NE || op == SWQ_LT || + op == SWQ_LE || op == SWQ_GT || op == SWQ_GE); +} + +/***********************************************************************/ +/* AreExprExclusive() */ +/***********************************************************************/ + +static const struct +{ + swq_op op1; + swq_op op2; + int expected_comp_1; + int expected_comp_2; +} +asPairsOfComparisons[] = +{ + { SWQ_EQ, SWQ_EQ, -1, 1 }, + { SWQ_LT, SWQ_GT, -1, 0 }, + { SWQ_GT, SWQ_LT, 0, 1 }, + { SWQ_LT, SWQ_GE, -1, 999 }, + { SWQ_LE, SWQ_GE, -1, 999 }, + { SWQ_LE, SWQ_GT, -1, 999 }, + { SWQ_GE, SWQ_LE, 1, 999 }, + { SWQ_GE, SWQ_LT, 1, 999 }, + { SWQ_GT, SWQ_LE, 1, 999 } +}; + +static int AreExprExclusive(OGRFeatureDefn* poFeatureDefn, + const swq_expr_node* poNode1, + const swq_expr_node* poNode2) +{ + if( poNode1->eNodeType != SNT_OPERATION ) + return FALSE; + if( poNode2->eNodeType != SNT_OPERATION ) + return FALSE; + + const size_t nParis = sizeof(asPairsOfComparisons) / sizeof(asPairsOfComparisons[0]); + for(size_t i = 0; i < nParis; i++ ) + { + if( poNode1->nOperation == asPairsOfComparisons[i].op1 && + poNode2->nOperation == asPairsOfComparisons[i].op2 && + poNode1->nSubExprCount == 2 && + poNode2->nSubExprCount == 2 ) + { + swq_expr_node *poColumn1 = poNode1->papoSubExpr[0]; + swq_expr_node *poValue1 = poNode1->papoSubExpr[1]; + swq_expr_node *poColumn2 = poNode2->papoSubExpr[0]; + swq_expr_node *poValue2 = poNode2->papoSubExpr[1]; + if( poColumn1->eNodeType == SNT_COLUMN && + poValue1->eNodeType == SNT_CONSTANT && + poColumn2->eNodeType == SNT_COLUMN && + poValue2->eNodeType == SNT_CONSTANT && + poColumn1->field_index == poColumn2->field_index && + poColumn1->field_index < poFeatureDefn->GetFieldCount() ) + { + OGRFieldDefn *poFieldDefn; + poFieldDefn = poFeatureDefn->GetFieldDefn(poColumn1->field_index); + + int nComp = CompValues(poFieldDefn, poValue1, poValue2); + return nComp == asPairsOfComparisons[i].expected_comp_1 || + nComp == asPairsOfComparisons[i].expected_comp_2; + } + return FALSE; + } + } + + if( (poNode2->nOperation == SWQ_ISNULL && + OGROpenFileGDBIsComparisonOp(poNode1->nOperation) && + poNode1->nSubExprCount == 2 && + poNode2->nSubExprCount == 1) || + (poNode1->nOperation == SWQ_ISNULL && + OGROpenFileGDBIsComparisonOp(poNode2->nOperation) && + poNode2->nSubExprCount == 2 && + poNode1->nSubExprCount == 1)) + { + swq_expr_node *poColumn1 = poNode1->papoSubExpr[0]; + swq_expr_node *poColumn2 = poNode2->papoSubExpr[0]; + if( poColumn1->eNodeType == SNT_COLUMN && + poColumn2->eNodeType == SNT_COLUMN && + poColumn1->field_index == poColumn2->field_index && + poColumn1->field_index < poFeatureDefn->GetFieldCount() ) + { + return TRUE; + } + } + + /* In doubt: return FALSE */ + return FALSE; +} + + +/***********************************************************************/ +/* FillTargetValueFromSrcExpr() */ +/***********************************************************************/ + +static +int FillTargetValueFromSrcExpr( OGRFieldDefn* poFieldDefn, + OGRField* poTargetValue, + const swq_expr_node* poSrcValue ) +{ + switch( poFieldDefn->GetType() ) + { + case OFTInteger: + if (poSrcValue->field_type == SWQ_FLOAT) + poTargetValue->Integer = (int) poSrcValue->float_value; + else + poTargetValue->Integer = (int) poSrcValue->int_value; + break; + + case OFTReal: + poTargetValue->Real = poSrcValue->float_value; + break; + + case OFTString: + poTargetValue->String = poSrcValue->string_value; + break; + + case OFTDate: + case OFTTime: + case OFTDateTime: + if (poSrcValue->field_type == SWQ_TIMESTAMP || + poSrcValue->field_type == SWQ_DATE || + poSrcValue->field_type == SWQ_TIME) + { + int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMin = 0, nSec = 0; + if( sscanf(poSrcValue->string_value, "%04d/%02d/%02d %02d:%02d:%02d", + &nYear, &nMonth, &nDay, &nHour, &nMin, &nSec) == 6 || + sscanf(poSrcValue->string_value, "%04d/%02d/%02d", + &nYear, &nMonth, &nDay) == 3 || + sscanf(poSrcValue->string_value, "%02d:%02d:%02d", + &nHour, &nMin, &nSec) == 3 ) + { + poTargetValue->Date.Year = (GInt16)nYear; + poTargetValue->Date.Month = (GByte)nMonth; + poTargetValue->Date.Day = (GByte)nDay; + poTargetValue->Date.Hour = (GByte)nHour; + poTargetValue->Date.Minute = (GByte)nMin; + poTargetValue->Date.Second = (GByte)nSec; + poTargetValue->Date.TZFlag = 0; + poTargetValue->Date.Reserved = 0; + } + else + return FALSE; + } + else + return FALSE; + break; + + default: + return FALSE; + } + return TRUE; +} + +/***********************************************************************/ +/* GetColumnSubNode() */ +/***********************************************************************/ + +static swq_expr_node* GetColumnSubNode(swq_expr_node* poNode) +{ + if( poNode->eNodeType == SNT_OPERATION && + poNode->nSubExprCount == 2 ) + { + if( poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN ) + return poNode->papoSubExpr[0]; + if( poNode->papoSubExpr[1]->eNodeType == SNT_COLUMN ) + return poNode->papoSubExpr[1]; + } + return NULL; +} + +/***********************************************************************/ +/* GetConstantSubNode() */ +/***********************************************************************/ + +static swq_expr_node* GetConstantSubNode(swq_expr_node* poNode) +{ + if( poNode->eNodeType == SNT_OPERATION && + poNode->nSubExprCount == 2 ) + { + if( poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT ) + return poNode->papoSubExpr[1]; + if( poNode->papoSubExpr[0]->eNodeType == SNT_CONSTANT ) + return poNode->papoSubExpr[0]; + } + return NULL; +} +/***********************************************************************/ +/* BuildIteratorFromExprNode() */ +/***********************************************************************/ + +FileGDBIterator* OGROpenFileGDBLayer::BuildIteratorFromExprNode(swq_expr_node* poNode) +{ + if( m_bIteratorSufficientToEvaluateFilter == FALSE ) + return NULL; + + if( poNode->eNodeType == SNT_OPERATION && + poNode->nOperation == SWQ_AND && poNode->nSubExprCount == 2 ) + { + /* Even if there's only one branch of the 2 that results to an iterator, */ + /* it is useful. Of course, the iterator will not be sufficient to evaluate */ + /* the filter, but it will be a super-set of the features */ + FileGDBIterator* poIter1 = BuildIteratorFromExprNode(poNode->papoSubExpr[0]); + + /* In case the first branch didn't result to an iterator, temporarily */ + /* restore the flag */ + int bSaveIteratorSufficientToEvaluateFilter = m_bIteratorSufficientToEvaluateFilter; + m_bIteratorSufficientToEvaluateFilter = -1; + FileGDBIterator* poIter2 = BuildIteratorFromExprNode(poNode->papoSubExpr[1]); + m_bIteratorSufficientToEvaluateFilter = bSaveIteratorSufficientToEvaluateFilter; + + if( poIter1 != NULL && poIter2 != NULL ) + return FileGDBIterator::BuildAnd(poIter1, poIter2); + m_bIteratorSufficientToEvaluateFilter = FALSE; + if( poIter1 != NULL ) + return poIter1; + if( poIter2 != NULL ) + return poIter2; + } + + else if( poNode->eNodeType == SNT_OPERATION && + poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2 ) + { + /* For a OR, we need an iterator for the 2 branches */ + FileGDBIterator* poIter1 = BuildIteratorFromExprNode(poNode->papoSubExpr[0]); + if( poIter1 != NULL ) + { + FileGDBIterator* poIter2 = BuildIteratorFromExprNode(poNode->papoSubExpr[1]); + if( poIter2 == NULL ) + { + delete poIter1; + } + else + { + return FileGDBIterator::BuildOr(poIter1, poIter2, + AreExprExclusive(GetLayerDefn(), + poNode->papoSubExpr[0], + poNode->papoSubExpr[1])); + } + } + } + + else if( poNode->eNodeType == SNT_OPERATION && + OGROpenFileGDBIsComparisonOp(poNode->nOperation) && + poNode->nSubExprCount == 2 ) + { + swq_expr_node *poColumn = GetColumnSubNode(poNode); + swq_expr_node *poValue = GetConstantSubNode(poNode); + if( poColumn != NULL && poValue != NULL && + poColumn->field_index < GetLayerDefn()->GetFieldCount()) + { + OGRFieldDefn *poFieldDefn; + poFieldDefn = GetLayerDefn()->GetFieldDefn(poColumn->field_index); + + int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); + if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() ) + { + OGRField sValue; + + if( FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue) ) + { + FileGDBSQLOp eOp; + if( poColumn == poNode->papoSubExpr[0] ) + { + switch( poNode->nOperation ) + { + case SWQ_LE: eOp = FGSO_LE; break; + case SWQ_LT: eOp = FGSO_LT; break; + case SWQ_NE: eOp = FGSO_EQ; /* yes : EQ */ break; + case SWQ_EQ: eOp = FGSO_EQ; break; + case SWQ_GE: eOp = FGSO_GE; break; + case SWQ_GT: eOp = FGSO_GT; break; + default: eOp = FGSO_EQ; CPLAssert(FALSE); break; + } + } + else + { + /* If "constant op column", then we must reverse */ + /* the operator */ + switch( poNode->nOperation ) + { + case SWQ_LE: eOp = FGSO_GE; break; + case SWQ_LT: eOp = FGSO_GT; break; + case SWQ_NE: eOp = FGSO_EQ; /* yes : EQ */ break; + case SWQ_EQ: eOp = FGSO_EQ; break; + case SWQ_GE: eOp = FGSO_LE; break; + case SWQ_GT: eOp = FGSO_LT; break; + default: eOp = FGSO_EQ; CPLAssert(FALSE); break; + } + } + + FileGDBIterator* poIter = FileGDBIterator::Build( + m_poLyrTable, nTableColIdx, TRUE, + eOp, poFieldDefn->GetType(), &sValue); + if( poIter != NULL ) + m_bIteratorSufficientToEvaluateFilter = TRUE; + if( poIter && poNode->nOperation == SWQ_NE ) + return FileGDBIterator::BuildNot(poIter); + else + return poIter; + } + } + } + } + else if( poNode->eNodeType == SNT_OPERATION && + poNode->nOperation == SWQ_ISNULL && poNode->nSubExprCount == 1 ) + { + swq_expr_node *poColumn = poNode->papoSubExpr[0]; + if( poColumn->eNodeType == SNT_COLUMN && + poColumn->field_index < GetLayerDefn()->GetFieldCount() ) + { + OGRFieldDefn *poFieldDefn; + poFieldDefn = GetLayerDefn()->GetFieldDefn(poColumn->field_index); + + int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); + if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() ) + { + FileGDBIterator* poIter = FileGDBIterator::BuildIsNotNull( + m_poLyrTable, nTableColIdx, TRUE); + if( poIter ) + { + m_bIteratorSufficientToEvaluateFilter = TRUE; + poIter = FileGDBIterator::BuildNot(poIter); + } + return poIter; + } + } + } + else if( poNode->eNodeType == SNT_OPERATION && + poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 && + poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION && + poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL && + poNode->papoSubExpr[0]->nSubExprCount == 1 ) + { + swq_expr_node *poColumn = poNode->papoSubExpr[0]->papoSubExpr[0]; + if( poColumn->eNodeType == SNT_COLUMN && + poColumn->field_index < GetLayerDefn()->GetFieldCount() ) + { + OGRFieldDefn *poFieldDefn; + poFieldDefn = GetLayerDefn()->GetFieldDefn(poColumn->field_index); + + int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); + if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() ) + { + FileGDBIterator* poIter = FileGDBIterator::BuildIsNotNull( + m_poLyrTable, nTableColIdx, TRUE); + if( poIter ) + m_bIteratorSufficientToEvaluateFilter = TRUE; + return poIter; + } + } + } + else if( poNode->eNodeType == SNT_OPERATION && + poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2 ) + { + swq_expr_node *poColumn = poNode->papoSubExpr[0]; + if( poColumn->eNodeType == SNT_COLUMN && + poColumn->field_index < GetLayerDefn()->GetFieldCount() ) + { + int bAllConstants = TRUE; + int i; + for(i=1;inSubExprCount;i++) + { + if( poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT ) + bAllConstants = FALSE; + } + OGRFieldDefn *poFieldDefn; + poFieldDefn = GetLayerDefn()->GetFieldDefn(poColumn->field_index); + + int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); + if( bAllConstants && nTableColIdx >= 0 && + m_poLyrTable->GetField(nTableColIdx)->HasIndex() ) + { + FileGDBIterator* poRet = NULL; + for(i=1;inSubExprCount;i++) + { + OGRField sValue; + if( !FillTargetValueFromSrcExpr(poFieldDefn, &sValue, + poNode->papoSubExpr[i]) ) + { + delete poRet; + poRet = NULL; + break; + } + FileGDBIterator* poIter = FileGDBIterator::Build( + m_poLyrTable, nTableColIdx, TRUE, FGSO_EQ, + poFieldDefn->GetType(), &sValue); + if( poIter == NULL ) + { + delete poRet; + poRet = NULL; + break; + } + if( poRet == NULL ) + poRet = poIter; + else + poRet = FileGDBIterator::BuildOr(poRet, poIter); + } + if( poRet != NULL ) + { + m_bIteratorSufficientToEvaluateFilter = TRUE; + return poRet; + } + } + } + } + else if( poNode->eNodeType == SNT_OPERATION && + poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 ) + { + FileGDBIterator* poIter = BuildIteratorFromExprNode(poNode->papoSubExpr[0]); + /* If we have an iterator that is only partial w.r.t the full clause */ + /* then we cannot do anything with it unfortunately */ + if( m_bIteratorSufficientToEvaluateFilter == FALSE ) + { + if( poIter != NULL ) + CPLDebug("OpenFileGDB", "Disabling use of indexes"); + delete poIter; + } + else if( poIter != NULL ) + { + return FileGDBIterator::BuildNot(poIter); + } + } + + + if( m_bIteratorSufficientToEvaluateFilter == TRUE ) + CPLDebug("OpenFileGDB", "Disabling use of indexes"); + m_bIteratorSufficientToEvaluateFilter = FALSE; + return NULL; +} + +/***********************************************************************/ +/* SetAttributeFilter() */ +/***********************************************************************/ + +OGRErr OGROpenFileGDBLayer::SetAttributeFilter( const char* pszFilter ) +{ + if( !BuildLayerDefinition() ) + return OGRERR_FAILURE; + + delete m_poIterator; + m_poIterator = NULL; + m_bIteratorSufficientToEvaluateFilter = FALSE; + + OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter); + if( eErr != OGRERR_NONE || + !CSLTestBoolean(CPLGetConfigOption("OPENFILEGDB_USE_INDEX", "YES")) ) + return eErr; + + if( m_poAttrQuery != NULL && m_nFilteredFeatureCount < 0 ) + { + swq_expr_node* poNode = (swq_expr_node*) m_poAttrQuery->GetSWQExpr(); + poNode->ReplaceBetweenByGEAndLERecurse(); + m_bIteratorSufficientToEvaluateFilter = -1; + m_poIterator = BuildIteratorFromExprNode(poNode); + if( m_poIterator != NULL && m_eSpatialIndexState == SPI_IN_BUILDING ) + m_eSpatialIndexState = SPI_INVALID; + if( m_bIteratorSufficientToEvaluateFilter < 0 ) + m_bIteratorSufficientToEvaluateFilter = FALSE; + } + return eErr; +} + +/***********************************************************************/ +/* GetCurrentFeature() */ +/***********************************************************************/ + +OGRFeature* OGROpenFileGDBLayer::GetCurrentFeature() +{ + OGRFeature *poFeature = NULL; + int iOGRIdx = 0; + int iRow = m_poLyrTable->GetCurRow(); + for(int iGDBIdx=0;iGDBIdxGetFieldCount();iGDBIdx++) + { + if( iGDBIdx == m_iGeomFieldIdx ) + { + if( m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored() ) + { + if( m_eSpatialIndexState == SPI_IN_BUILDING ) + m_eSpatialIndexState = SPI_INVALID; + continue; + } + + const OGRField* psField = m_poLyrTable->GetFieldValue(iGDBIdx); + if( psField != NULL ) + { + if( m_eSpatialIndexState == SPI_IN_BUILDING ) + { + OGREnvelope sFeatureEnvelope; + if( m_poLyrTable->GetFeatureExtent(psField, + &sFeatureEnvelope) ) + { + CPLRectObj sBounds; + sBounds.minx = sFeatureEnvelope.MinX; + sBounds.miny = sFeatureEnvelope.MinY; + sBounds.maxx = sFeatureEnvelope.MaxX; + sBounds.maxy = sFeatureEnvelope.MaxY; + CPLQuadTreeInsertWithBounds(m_pQuadTree, + (void*)(size_t)iRow, + &sBounds); + } + } + + if( m_poFilterGeom != NULL && + m_eSpatialIndexState != SPI_COMPLETED && + !m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(psField) ) + { + delete poFeature; + return NULL; + } + + OGRGeometry* poGeom = m_poGeomConverter->GetAsGeometry(psField); + if( poGeom != NULL ) + { + OGRwkbGeometryType eFlattenType = wkbFlatten(poGeom->getGeometryType()); + if( eFlattenType == wkbPolygon ) + poGeom = OGRGeometryFactory::forceToMultiPolygon(poGeom); + else if( eFlattenType == wkbLineString ) + poGeom = OGRGeometryFactory::forceToMultiLineString(poGeom); + poGeom->assignSpatialReference( + m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef() ); + + if( poFeature == NULL ) + poFeature = new OGRFeature(m_poFeatureDefn); + poFeature->SetGeometryDirectly( poGeom ); + } + } + } + else + { + if( !m_poFeatureDefn->GetFieldDefn(iOGRIdx)->IsIgnored() ) + { + const OGRField* psField = m_poLyrTable->GetFieldValue(iGDBIdx); + if( psField != NULL ) + { + if( poFeature == NULL ) + poFeature = new OGRFeature(m_poFeatureDefn); + + if( iGDBIdx == m_iFieldToReadAsBinary ) + poFeature->SetField(iOGRIdx, (const char*) psField->Binary.paData); + else + poFeature->SetField(iOGRIdx, (OGRField*) psField); + } + } + iOGRIdx ++; + } + } + + if( poFeature == NULL ) + poFeature = new OGRFeature(m_poFeatureDefn); + + if( m_poLyrTable->HasDeletedFeaturesListed() ) + { + poFeature->SetField(poFeature->GetFieldCount() - 1, + m_poLyrTable->IsCurRowDeleted()); + } + + poFeature->SetFID(iRow + 1); + return poFeature; +} + +/***********************************************************************/ +/* GetNextFeature() */ +/***********************************************************************/ + +OGRFeature* OGROpenFileGDBLayer::GetNextFeature() +{ + if( !BuildLayerDefinition() || m_bEOF ) + return NULL; + + while( TRUE ) + { + OGRFeature *poFeature = NULL; + + if( m_nFilteredFeatureCount >= 0 ) + { + while( TRUE ) + { + if( m_iCurFeat >= m_nFilteredFeatureCount ) + { + return NULL; + } + int iRow = (int)(size_t)m_pahFilteredFeatures[m_iCurFeat++]; + if( m_poLyrTable->SelectRow(iRow) ) + { + poFeature = GetCurrentFeature(); + if( poFeature ) + break; + } + else if( m_poLyrTable->HasGotError() ) + { + m_bEOF = TRUE; + return NULL; + } + } + } + else if( m_poIterator != NULL ) + { + while( TRUE ) + { + int iRow = m_poIterator->GetNextRowSortedByFID(); + if( iRow < 0 ) + return NULL; + if( m_poLyrTable->SelectRow(iRow) ) + { + poFeature = GetCurrentFeature(); + if( poFeature ) + break; + } + else if( m_poLyrTable->HasGotError() ) + { + m_bEOF = TRUE; + return NULL; + } + } + } + else + { + while( TRUE ) + { + if( m_iCurFeat == m_poLyrTable->GetTotalRecordCount() ) + { + return NULL; + } + m_iCurFeat = m_poLyrTable->GetAndSelectNextNonEmptyRow(m_iCurFeat); + if( m_iCurFeat < 0 ) + { + m_bEOF = TRUE; + return NULL; + } + else + { + m_iCurFeat ++; + poFeature = GetCurrentFeature(); + if( m_eSpatialIndexState == SPI_IN_BUILDING && + m_iCurFeat == m_poLyrTable->GetTotalRecordCount() ) + { + CPLDebug("OpenFileGDB", "SPI_COMPLETED"); + m_eSpatialIndexState = SPI_COMPLETED; + } + if( poFeature ) + break; + } + } + } + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL || + (m_poIterator != NULL && m_bIteratorSufficientToEvaluateFilter) || + m_poAttrQuery->Evaluate( poFeature ) ) ) + { + return poFeature; + } + + delete poFeature; + } +} + +/***********************************************************************/ +/* GetFeature() */ +/***********************************************************************/ + +OGRFeature* OGROpenFileGDBLayer::GetFeature( GIntBig nFeatureId ) +{ + if( !BuildLayerDefinition() ) + return NULL; + + if( nFeatureId < 1 || nFeatureId > m_poLyrTable->GetTotalRecordCount() ) + return NULL; + if( !m_poLyrTable->SelectRow((int)nFeatureId - 1) ) + return NULL; + + /* Temporarily disable spatial filter */ + OGRGeometry* poOldSpatialFilter = m_poFilterGeom; + m_poFilterGeom = NULL; + /* and also spatial index state to avoid features to be inserted */ + /* multiple times in spatial index */ + SPIState eOldState = m_eSpatialIndexState; + m_eSpatialIndexState = SPI_INVALID; + + OGRFeature* poFeature = GetCurrentFeature(); + + /* Set it back */ + m_poFilterGeom = poOldSpatialFilter; + m_eSpatialIndexState = eOldState; + + return poFeature; +} + +/***********************************************************************/ +/* SetNextByIndex() */ +/***********************************************************************/ + +OGRErr OGROpenFileGDBLayer::SetNextByIndex( GIntBig nIndex ) +{ + if( m_poIterator != NULL ) + return OGRLayer::SetNextByIndex(nIndex); + + if( !BuildLayerDefinition() ) + return OGRERR_FAILURE; + + if( m_eSpatialIndexState == SPI_IN_BUILDING ) + m_eSpatialIndexState = SPI_INVALID; + + if( m_nFilteredFeatureCount >= 0 ) + { + if( nIndex < 0 || nIndex >= m_nFilteredFeatureCount ) + return OGRERR_FAILURE; + m_iCurFeat = (int) nIndex; + return OGRERR_NONE; + } + else if( m_poLyrTable->GetValidRecordCount() == m_poLyrTable->GetTotalRecordCount() ) + { + if( nIndex < 0 || nIndex >= m_poLyrTable->GetValidRecordCount() ) + return OGRERR_FAILURE; + m_iCurFeat = (int) nIndex; + return OGRERR_NONE; + } + else + return OGRLayer::SetNextByIndex(nIndex); +} + +/***********************************************************************/ +/* GetExtent() */ +/***********************************************************************/ + +OGRErr OGROpenFileGDBLayer::GetExtent( OGREnvelope *psExtent, int bForce ) +{ + (void)bForce; + + if( !BuildLayerDefinition() ) + return OGRERR_FAILURE; + + if( m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0 ) + { + FileGDBGeomField* poGDBGeomField = + (FileGDBGeomField* )m_poLyrTable->GetField(m_iGeomFieldIdx); + psExtent->MinX = poGDBGeomField->GetXMin(); + psExtent->MinY = poGDBGeomField->GetYMin(); + psExtent->MaxX = poGDBGeomField->GetXMax(); + psExtent->MaxY = poGDBGeomField->GetYMax(); + return OGRERR_NONE; + } + else + return OGRERR_FAILURE; +} + +/***********************************************************************/ +/* GetFeatureCount() */ +/***********************************************************************/ + +GIntBig OGROpenFileGDBLayer::GetFeatureCount( int bForce ) +{ + if( !BuildLayerDefinition() ) + return 0; + + /* No filter */ + if( (m_poFilterGeom == NULL || m_iGeomFieldIdx < 0 ) && + m_poAttrQuery == NULL ) + { + return m_poLyrTable->GetValidRecordCount(); + } + else if( m_nFilteredFeatureCount >= 0 && m_poAttrQuery == NULL ) + { + return m_nFilteredFeatureCount; + } + + /* Only geometry filter ? */ + if( m_poAttrQuery == NULL && m_bFilterIsEnvelope ) + { + int nCount = 0; + if( m_eSpatialIndexState == SPI_IN_BUILDING && m_iCurFeat != 0 ) + m_eSpatialIndexState = SPI_INVALID; + + int nFilteredFeatureCountAlloc = 0; + if( m_eSpatialIndexState == SPI_IN_BUILDING ) + { + CPLFree(m_pahFilteredFeatures); + m_pahFilteredFeatures = NULL; + m_nFilteredFeatureCount = 0; + } + + for(int i=0;iGetTotalRecordCount();i++) + { + if( !m_poLyrTable->SelectRow(i) ) + { + if( m_poLyrTable->HasGotError() ) + break; + else + continue; + } + + const OGRField* psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx); + if( psField != NULL ) + { + if( m_eSpatialIndexState == SPI_IN_BUILDING ) + { + OGREnvelope sFeatureEnvelope; + if( m_poLyrTable->GetFeatureExtent(psField, + &sFeatureEnvelope) ) + { + CPLRectObj sBounds; + sBounds.minx = sFeatureEnvelope.MinX; + sBounds.miny = sFeatureEnvelope.MinY; + sBounds.maxx = sFeatureEnvelope.MaxX; + sBounds.maxy = sFeatureEnvelope.MaxY; + CPLQuadTreeInsertWithBounds(m_pQuadTree, + (void*)(size_t)i, + &sBounds); + } + } + + if( m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(psField) ) + { + OGRGeometry* poGeom = m_poGeomConverter->GetAsGeometry(psField); + if( poGeom != NULL && FilterGeometry( poGeom )) + { + if( m_eSpatialIndexState == SPI_IN_BUILDING ) + { + if( nCount == nFilteredFeatureCountAlloc ) + { + nFilteredFeatureCountAlloc = 4 * nFilteredFeatureCountAlloc / 3 + 1024; + m_pahFilteredFeatures = (void**)CPLRealloc( + m_pahFilteredFeatures, sizeof(void*) * nFilteredFeatureCountAlloc); + } + m_pahFilteredFeatures[nCount] = (void*)(size_t)i; + } + nCount ++; + } + delete poGeom; + } + } + } + if( m_eSpatialIndexState == SPI_IN_BUILDING ) + { + m_nFilteredFeatureCount = nCount; + m_eSpatialIndexState = SPI_COMPLETED; + } + + return nCount; + } + /* Only simple attribute filter ? */ + else if( m_poFilterGeom == NULL && + m_poIterator != NULL && m_bIteratorSufficientToEvaluateFilter ) + { + return m_poIterator->GetRowCount(); + } + + return OGRLayer::GetFeatureCount(bForce); +} + +/***********************************************************************/ +/* TestCapability() */ +/***********************************************************************/ + +int OGROpenFileGDBLayer::TestCapability( const char * pszCap ) +{ + if( !BuildLayerDefinition() ) + return FALSE; + + if( EQUAL(pszCap,OLCFastFeatureCount) ) + { + return( (m_poFilterGeom == NULL || m_iGeomFieldIdx < 0 ) && + m_poAttrQuery == NULL ); + } + else if( EQUAL(pszCap,OLCFastSetNextByIndex) ) + { + return ( m_poLyrTable->GetValidRecordCount() == + m_poLyrTable->GetTotalRecordCount() && + m_poIterator == NULL ); + } + else if( EQUAL(pszCap,OLCRandomRead) ) + { + return TRUE; + } + else if( EQUAL(pszCap,OLCFastGetExtent) ) + { + return TRUE; + } + else if( EQUAL(pszCap,OLCIgnoreFields) ) + { + return TRUE; + } + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + { + return TRUE; /* ? */ + } + + return FALSE; +} + +/***********************************************************************/ +/* HasIndexForField() */ +/***********************************************************************/ + +int OGROpenFileGDBLayer::HasIndexForField(const char* pszFieldName) +{ + if( !BuildLayerDefinition() ) + return FALSE; + + int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName); + return ( nTableColIdx >= 0 && + m_poLyrTable->GetField(nTableColIdx)->HasIndex() ); +} + +/***********************************************************************/ +/* BuildIndex() */ +/***********************************************************************/ + +FileGDBIterator* OGROpenFileGDBLayer::BuildIndex(const char* pszFieldName, + int bAscending, + int op, + swq_expr_node* poValue) +{ + if( !BuildLayerDefinition() ) + return NULL; + + int idx = GetLayerDefn()->GetFieldIndex(pszFieldName); + if( idx < 0 ) + return NULL; + OGRFieldDefn* poFieldDefn = GetLayerDefn()->GetFieldDefn(idx); + + int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName); + if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() ) + { + if( op < 0 ) + return FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, bAscending); + else + { + OGRField sValue; + if( FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue) ) + { + FileGDBSQLOp eOp; + switch( op ) + { + case SWQ_LE: eOp = FGSO_LE; break; + case SWQ_LT: eOp = FGSO_LT; break; + case SWQ_EQ: eOp = FGSO_EQ; break; + case SWQ_GE: eOp = FGSO_GE; break; + case SWQ_GT: eOp = FGSO_GT; break; + default: return NULL; + } + + return FileGDBIterator::Build( + m_poLyrTable, nTableColIdx, bAscending, + eOp, poFieldDefn->GetType(), &sValue); + } + } + } + return NULL; +} + +/***********************************************************************/ +/* GetMinMaxValue() */ +/***********************************************************************/ + +const OGRField* OGROpenFileGDBLayer::GetMinMaxValue(OGRFieldDefn* poFieldDefn, + int bIsMin, + int& eOutType) +{ + eOutType = OFTMaxType; + if( !BuildLayerDefinition() ) + return NULL; + + int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); + if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() ) + { + delete m_poIterMinMax; + m_poIterMinMax = FileGDBIterator::BuildIsNotNull( + m_poLyrTable, nTableColIdx, TRUE); + if( m_poIterMinMax != NULL ) + { + const OGRField* poRet = (bIsMin ) ? + m_poIterMinMax->GetMinValue(eOutType) : + m_poIterMinMax->GetMaxValue(eOutType); + if( poRet == NULL ) + eOutType = poFieldDefn->GetType(); + return poRet; + } + } + return NULL; +} + +/***********************************************************************/ +/* GetMinMaxSumCount() */ +/***********************************************************************/ + +int OGROpenFileGDBLayer::GetMinMaxSumCount(OGRFieldDefn* poFieldDefn, + double& dfMin, double& dfMax, + double& dfSum, int& nCount) +{ + dfMin = 0.0; + dfMax = 0.0; + dfSum = 0.0; + nCount = 0; + if( !BuildLayerDefinition() ) + return FALSE; + + int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef()); + if( nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex() ) + { + FileGDBIterator* poIter = FileGDBIterator::BuildIsNotNull( + m_poLyrTable, nTableColIdx, TRUE); + if( poIter != NULL ) + { + int nRet = poIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount); + delete poIter; + return nRet; + } + } + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/GNUmakefile new file mode 100644 index 000000000..622b8b757 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/GNUmakefile @@ -0,0 +1,23 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrosmdriver.o ogrosmdatasource.o ogrosmlayer.o osm_parser.o + +CPPFLAGS := -I.. -I../.. -I../sqlite -I../generic $(EXPAT_INCLUDE) $(SQLITE_INC) $(CPPFLAGS) + +ifeq ($(HAVE_EXPAT),yes) +CPPFLAGS += -DHAVE_EXPAT +endif + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +all: default osm2osm$(EXE) + +clean: + rm -f *.o $(O_OBJ) osm2osm$(EXE) + +$(O_OBJ): ogr_osm.h gpb.h osm_parser.h + +osm2osm$(EXE): osm2osm.$(OBJ_EXT) + $(LD) $(LDFLAGS) osm2osm.$(OBJ_EXT) $(CONFIG_LIBS) -o osm2osm$(EXE) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/drv_osm.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/drv_osm.html new file mode 100644 index 000000000..3061f3070 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/drv_osm.html @@ -0,0 +1,157 @@ + + +OSM - OpenStreetMap XML and PBF + + + + +

    OSM - OpenStreetMap XML and PBF

    + +(GDAL/OGR >= 1.10.0)

    + +This driver reads OpenStreetMap files, in .osm (XML based) and .pbf (optimized binary) formats.

    + +The driver is available if GDAL is built with SQLite support and, for .osm XML files, with Expat support.

    + +The filenames must end with .osm or .pbf extension.

    + +The driver will categorize features into 5 layers : +

      +
    • points : "node" features that have significant tags attached.
    • +
    • lines : "way" features that are recognized as non-area.
    • +
    • multilinestrings : "relation" features that form a multilinestring(type = 'multilinestring' or type = 'route').
    • +
    • multipolygons : "relation" features that form a multipolygon (type = 'multipolygon' or type = 'boundary'), and "way" features that are recognized as area.
    • +
    • other_relations : "relation" features that do not belong to the above 2 layers.
    • +
    +

    + +

    Configuration

    + +In the data folder of the GDAL distribution, you can find a +osmconf.ini file that can be +customized to fit your needs. You can also define an alternate path with the OSM_CONFIG_FILE configuration +option.

    + +The customization is essentially which OSM attributes and keys should be translated into OGR layer fields.

    + +Starting with GDAL 2.0, fields can be computed with SQL expressions (evaluated by SQLite engine) +from other fields/tags. For example to compute the z_order attribute.

    + +

    "other_tags" field

    + +When keys are not strictly identified in the osmconf.ini file, the key/value pair is appended +in a "other_tags" field, with a syntax compatible with the PostgreSQL HSTORE type. See the +COLUMN_TYPES layer creation option of the PG driver.

    + +For example : +

    +ogr2ogr -f PostgreSQL "PG:dbname=osm" test.pbf -lco COLUMN_TYPES=other_tags=hstore
    +
    + +

    "all_tags" field

    + +(OGR >= 1.11)

    + +Similar to "other_tags", except that it contains both keys specifically identified to be reported as +dedicated fields, as well as other keys.

    +"all_tags" is disabled by default, and when enabled, it is exclusive with "other_tags". + +

    Internal working and performance tweaking

    + +The driver will use an internal SQLite database to resolve geometries. If that database remains under 100 MB +it will reside in RAM. If it grows above, it will be written in a temporary file on disk. By default, this +file will be written in the current directory, unless you define the CPL_TMPDIR configuration option. The +100 MB default threshold can be adjusted with the OSM_MAX_TMPFILE_SIZE configuration option (value in MB).

    + +For indexation of nodes, a custom mechanism not relying on SQLite is used by default (indexation of ways +to solve relations is still relying on SQLite). It can speed up operations significantly. However, in some +situations (non increasing node ids, or node ids not in expected range), it might not work and the driver will +output an error message suggesting to relaunch by defining the OSM_USE_CUSTOM_INDEXING configuration option to NO.

    + +When custom indexing is used (default case), the OSM_COMPRESS_NODES configuration option can be set to YES (the +default is NO). This option might be turned on to improve performances when I/O access is the limiting factor (typically +the case of rotational disk), and will be mostly efficient for country-sized OSM extracts where compression rate can +go up to a factor of 3 or 4, and help keep the node DB to a size that fit in the OS I/O caches. For whole planet file, the +effect of this option will be less efficient. This option consumes addionnal 60 MB of RAM.

    + +

    Interleaved reading

    + +Due to the nature of OSM files and how the driver works internally, the default reading mode might not work +correctly, because too many features will accumulate in the layers before being consummed by the user application. +For large files, applications should set the OGR_INTERLEAVED_READING=YES configuration option to turn on a +special reading mode where the following reading pattern must be used : + +
    +    int bHasLayersNonEmpty;
    +    do
    +    {
    +        bHasLayersNonEmpty = FALSE;
    +
    +        for( int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++ )
    +        {
    +            OGRLayer *poLayer = poDS->GetLayer(iLayer);
    +
    +            OGRFeature* poFeature;
    +            while( (poFeature = poLayer->GetNextFeature()) != NULL )
    +            {
    +                bHasLayersNonEmpty = TRUE;
    +                OGRFeature::DestroyFeature(poFeature);
    +            }
    +        }
    +    }
    +    while( bHasLayersNonEmpty );
    +
    + +

    + +Note : the ogr2ogr application has been modified to use that OGR_INTERLEAVED_READING mode without any +particular user action.

    + +

    Reading .osm.bz2 files and/or online files

    + +.osm.bz2 are not natively recognized, however you can process them (on Unix), with the following command : +
    +bzcat my.osm.bz2 | ogr2ogr -f SQLite my.sqlite /vsistdin/
    +
    + +You can convert a .osm or .pbf file without downloading it : +
    +wget -O - http://www.example.com/some.pbf | ogr2ogr -f SQLite my.sqlite /vsistdin/
    +
    +or
    +
    +ogr2ogr -f SQLite my.sqlite /vsicurl_streaming/http://www.example.com/some.pbf -progress
    +
    + +And to combine the above steps : + +
    +wget -O - http://www.example.com/some.osm.bz2 | bzcat | ogr2ogr -f SQLite my.sqlite /vsistdin/
    +
    + +

    Open options

    + +
      +
    • CONFIG_FILE=filename: (GDAL >=2.0) Configuration filename. +Defaults to {GDAL_DATA}/osmconf.ini.
    • +
    • USE_CUSTOM_INDEXING=YES/NO: (GDAL >=2.0) +Whether to enable custom indexing. Defaults to YES.
    • +
    • COMPRESS_NODES=YES/NO: (GDAL >=2.0) +Whether to compress nodes in temporary DB. Defaults to NO.
    • +
    • MAX_TMPFILE_SIZE=int_val: (GDAL >=2.0) Maximum size in MB +of in-memory temporary file. If it exceeds that value, it will go to disk. +Defaults to 100.
    • +
    • INTERLEAVED_READING=YES/NO: (GDAL >=2.0) Whether to +enable interleveaved reading. Defaults to NO.
    • +
    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/gpb.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/gpb.h new file mode 100644 index 000000000..5f9dd91e8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/gpb.h @@ -0,0 +1,289 @@ +/****************************************************************************** + * $Id: gpb.h 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Author: Even Rouault, + * Purpose: Google Protocol Buffer generic handling functions + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _GPB_H_INCLUDED +#define _GPB_H_INCLUDED + +#include "cpl_port.h" +#include "cpl_error.h" + +#ifndef CHECK_OOB +#define CHECK_OOB 1 +#endif + +//#define DEBUG_GPB_ERRORS +#ifdef DEBUG_GPB_ERRORS +static void error_occured(int nLine) +{ + CPLError(CE_Failure, CPLE_AppDefined, "Parsing error occured at line %d", nLine); +} + +#define GOTO_END_ERROR do { error_occured(__LINE__); goto end_error; } while(0) +#else +#define GOTO_END_ERROR goto end_error +#endif + +#if defined(__GNUC__) +#define CPL_NO_INLINE __attribute__ ((noinline)) +#else +#define CPL_NO_INLINE +#endif + +/************************************************************************/ +/* Google Protocol Buffer definitions */ +/************************************************************************/ + +#define WT_VARINT 0 +#define WT_64BIT 1 +#define WT_DATA 2 +#define WT_STARTGROUP 3 +#define WT_ENDGROUP 4 +#define WT_32BIT 5 + +#define MAKE_KEY(nFieldNumber, nWireType) ((nFieldNumber << 3) | nWireType) +#define GET_WIRETYPE(nKey) (nKey & 0x7) +#define GET_FIELDNUMBER(nKey) (nKey >> 3) + +/************************************************************************/ +/* ReadVarInt32() */ +/************************************************************************/ + +static int ReadVarInt32(GByte** ppabyData) +{ + int nVal = 0; + int nShift = 0; + GByte* pabyData = *ppabyData; + + while(TRUE) + { + int nByte = *pabyData; + if (!(nByte & 0x80)) + { + *ppabyData = pabyData + 1; + return nVal | (nByte << nShift); + } + nVal |= (nByte & 0x7f) << nShift; + pabyData ++; + nShift += 7; + } +} + +#define READ_VARINT32(pabyData, pabyDataLimit, nVal) \ + { \ + nVal = ReadVarInt32(&pabyData); \ + if (CHECK_OOB && pabyData > pabyDataLimit) GOTO_END_ERROR; \ + } + +#define READ_VARSINT32(pabyData, pabyDataLimit, nVal) \ + { \ + nVal = ReadVarInt32(&pabyData); \ + nVal = ((nVal & 1) == 0) ? (((unsigned int)nVal) >> 1) : -(((unsigned int)nVal) >> 1)-1; \ + if (CHECK_OOB && pabyData > pabyDataLimit) GOTO_END_ERROR; \ + } + +/************************************************************************/ +/* ReadVarUInt32() */ +/************************************************************************/ + +static unsigned int ReadVarUInt32(GByte** ppabyData) +{ + unsigned int nVal = 0; + int nShift = 0; + GByte* pabyData = *ppabyData; + + while(TRUE) + { + int nByte = *pabyData; + if (!(nByte & 0x80)) + { + *ppabyData = pabyData + 1; + return nVal | (nByte << nShift); + } + nVal |= (nByte & 0x7f) << nShift; + pabyData ++; + nShift += 7; + } +} + +#define READ_VARUINT32(pabyData, pabyDataLimit, nVal) \ + { \ + nVal = ReadVarUInt32(&pabyData); \ + if (CHECK_OOB && pabyData > pabyDataLimit) GOTO_END_ERROR; \ + } + +#define READ_SIZE(pabyData, pabyDataLimit, nSize) \ + { \ + READ_VARUINT32(pabyData, pabyDataLimit, nSize); \ + if (CHECK_OOB && nSize > (unsigned int)(pabyDataLimit - pabyData)) GOTO_END_ERROR; \ + } + +/************************************************************************/ +/* ReadVarInt64() */ +/************************************************************************/ + +static GIntBig ReadVarInt64(GByte** ppabyData) +{ + GIntBig nVal = 0; + int nShift = 0; + GByte* pabyData = *ppabyData; + + while(TRUE) + { + int nByte = *pabyData; + if (!(nByte & 0x80)) + { + *ppabyData = pabyData + 1; + return nVal | ((GIntBig)nByte << nShift); + } + nVal |= ((GIntBig)(nByte & 0x7f)) << nShift; + pabyData ++; + nShift += 7; + } +} + +#define READ_VARINT64(pabyData, pabyDataLimit, nVal) \ + { \ + nVal = ReadVarInt64(&pabyData); \ + if (CHECK_OOB && pabyData > pabyDataLimit) GOTO_END_ERROR; \ + } + +#define READ_VARSINT64(pabyData, pabyDataLimit, nVal) \ + { \ + nVal = ReadVarInt64(&pabyData); \ + nVal = ((nVal & 1) == 0) ? (((GUIntBig)nVal) >> 1) : -(((GUIntBig)nVal) >> 1)-1; \ + if (CHECK_OOB && pabyData > pabyDataLimit) GOTO_END_ERROR; \ + } + +#define READ_VARSINT64_NOCHECK(pabyData, pabyDataLimit, nVal) \ + { \ + nVal = ReadVarInt64(&pabyData); \ + nVal = ((nVal & 1) == 0) ? (((GUIntBig)nVal) >> 1) : -(((GUIntBig)nVal) >> 1)-1; \ + } + +/************************************************************************/ +/* SkipVarInt() */ +/************************************************************************/ + +static void SkipVarInt(GByte** ppabyData) +{ + GByte* pabyData = *ppabyData; + while(TRUE) + { + int nByte = *pabyData; + if (!(nByte & 0x80)) + { + *ppabyData = pabyData + 1; + return; + } + pabyData ++; + } +} + +#define SKIP_VARINT(pabyData, pabyDataLimit) \ + { \ + SkipVarInt(&pabyData); \ + if (CHECK_OOB && pabyData > pabyDataLimit) GOTO_END_ERROR; \ + } + +#define READ_FIELD_KEY(nKey) READ_VARINT32(pabyData, pabyDataLimit, nKey) + +#define READ_TEXT(pabyData, pabyDataLimit, pszTxt) \ + unsigned int nDataLength; \ + READ_SIZE(pabyData, pabyDataLimit, nDataLength); \ + pszTxt = (char*)VSIMalloc(nDataLength + 1); \ + if( pszTxt == NULL ) GOTO_END_ERROR; \ + memcpy(pszTxt, pabyData, nDataLength); \ + pszTxt[nDataLength] = 0; \ + pabyData += nDataLength; + +/************************************************************************/ +/* SkipUnknownField() */ +/************************************************************************/ + +#define SKIP_UNKNOWN_FIELD_INLINE(pabyData, pabyDataLimit, verbose) \ + int nWireType = GET_WIRETYPE(nKey); \ + if (verbose) \ + { \ + int nFieldNumber = GET_FIELDNUMBER(nKey); \ + CPLDebug("PBF", "Unhandled case: nFieldNumber = %d, nWireType = %d", nFieldNumber, nWireType); \ + } \ + switch (nWireType) \ + { \ + case WT_VARINT: \ + { \ + SKIP_VARINT(pabyData, pabyDataLimit); \ + break; \ + } \ + case WT_64BIT: \ + { \ + if (CHECK_OOB && pabyDataLimit - pabyData < 8) GOTO_END_ERROR; \ + pabyData += 8; \ + break; \ + } \ + case WT_DATA: \ + { \ + unsigned int nDataLength; \ + READ_SIZE(pabyData, pabyDataLimit, nDataLength); \ + pabyData += nDataLength; \ + break; \ + } \ + case WT_32BIT: \ + { \ + if (CHECK_OOB && pabyDataLimit - pabyData < 4) GOTO_END_ERROR; \ + pabyData += 4; \ + break; \ + } \ + default: \ + GOTO_END_ERROR; \ + } + +static +int SkipUnknownField(int nKey, GByte* pabyData, GByte* pabyDataLimit, int verbose) CPL_NO_INLINE; + +/* Putting statics in headers is trouble. */ +static +CPL_UNUSED +int SkipUnknownField(int nKey, GByte* pabyData, GByte* pabyDataLimit, int verbose) +{ + GByte* pabyDataBefore = pabyData; + SKIP_UNKNOWN_FIELD_INLINE(pabyData, pabyDataLimit, verbose); + return pabyData - pabyDataBefore; +end_error: + return -1; +} + +#define SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, verbose) \ + { \ + int _nOffset = SkipUnknownField(nKey, pabyData, pabyDataLimit, verbose); \ + if (_nOffset < 0) \ + GOTO_END_ERROR; \ + pabyData += _nOffset; \ + } + +#endif /* _GPB_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/makefile.vc new file mode 100644 index 000000000..c844f0629 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/makefile.vc @@ -0,0 +1,18 @@ + +OBJ = ogrosmdriver.obj ogrosmdatasource.obj ogrosmlayer.obj osm_parser.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF EXPAT_DIR +EXTRAFLAGS = -I.. -I..\.. -I..\sqlite -I..\generic $(SQLITE_INC) -DHAVE_SQLITE $(EXPAT_INCLUDE) -DHAVE_EXPAT=1 +!ELSE +EXTRAFLAGS = -I.. -I..\.. -I..\sqlite -I..\generic $(SQLITE_INC) -DHAVE_SQLITE +!ENDIF + +default: $(OBJ) + +clean: + -del *.obj *.pdb + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/ogr_osm.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/ogr_osm.h new file mode 100644 index 000000000..ea86a074b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/ogr_osm.h @@ -0,0 +1,490 @@ +/****************************************************************************** + * $Id: ogr_osm.h 29242 2015-05-24 10:59:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for OGR/OpenStreeMap driver. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_OSM_H_INCLUDED +#define _OGR_OSM_H_INCLUDED + +// replace O(log2(N)) complexity of FindNode() by O(1) +#define ENABLE_NODE_LOOKUP_BY_HASHING 1 + +#include "ogrsf_frmts.h" +#include "cpl_string.h" + +#include +#include +#include + +#include "osm_parser.h" + +#include "ogr_sqlite.h" + +class ConstCharComp +{ + public: + bool operator()(const char* a, const char* b) const + { + return strcmp(a, b) < 0; + } +}; + +class OGROSMComputedAttribute +{ + public: + CPLString osName; + int nIndex; + OGRFieldType eType; + CPLString osSQL; + sqlite3_stmt *hStmt; + std::vector aosAttrToBind; + std::vector anIndexToBind; + + OGROSMComputedAttribute() : nIndex(-1), eType(OFTString), hStmt(NULL) {} + OGROSMComputedAttribute(const char* pszName) : osName(pszName), nIndex(-1), eType(OFTString), hStmt(NULL) {} +}; + +/************************************************************************/ +/* OGROSMLayer */ +/************************************************************************/ + +class OGROSMDataSource; + +class OGROSMLayer : public OGRLayer +{ + friend class OGROSMDataSource; + + OGROSMDataSource *poDS; + int nIdxLayer; + OGRFeatureDefn *poFeatureDefn; + OGRSpatialReference *poSRS; + long nFeatureCount; + + std::vector apszNames; + std::map oMapFieldNameToIndex; + + std::vector oComputedAttributes; + + int bResetReadingAllowed; + + int nFeatureArraySize; + int nFeatureArrayMaxSize; + int nFeatureArrayIndex; + OGRFeature** papoFeatures; + + int bHasOSMId; + int nIndexOSMId; + int nIndexOSMWayId; + int bHasVersion; + int bHasTimestamp; + int bHasUID; + int bHasUser; + int bHasChangeset; + int bHasOtherTags; + int nIndexOtherTags; + int bHasAllTags; + int nIndexAllTags; + + int bHasWarnedTooManyFeatures; + + char *pszAllTags; + int bHasWarnedAllTagsTruncated; + + int bUserInterested; + + int AddToArray(OGRFeature* poFeature, int bCheckFeatureThreshold); + + int AddInOtherOrAllTags(const char* pszK); + + char szLaunderedFieldName[256]; + const char* GetLaunderedFieldName(const char* pszName); + + std::vector apszUnsignificantKeys; + std::map aoSetUnsignificantKeys; + + std::vector apszIgnoreKeys; + std::map aoSetIgnoreKeys; + + std::set aoSetWarnKeys; + + public: + OGROSMLayer( OGROSMDataSource* poDS, + int nIdxLayer, + const char* pszName ); + virtual ~OGROSMLayer(); + + virtual OGRFeatureDefn *GetLayerDefn() {return poFeatureDefn;} + + virtual void ResetReading(); + virtual int TestCapability( const char * ); + + virtual OGRFeature *GetNextFeature(); + virtual GIntBig GetFeatureCount( int bForce ); + + virtual OGRErr SetAttributeFilter( const char* pszAttrQuery ); + + virtual OGRErr GetExtent( OGREnvelope *psExtent, int bForce ); + + const OGREnvelope* GetSpatialFilterEnvelope(); + + int AddFeature(OGRFeature* poFeature, + int bAttrFilterAlreadyEvaluated, + int* pbFilteredOut = NULL, + int bCheckFeatureThreshold = TRUE); + void ForceResetReading(); + + void AddField(const char* pszName, OGRFieldType eFieldType); + int GetFieldIndex(const char* pszName); + + int HasOSMId() const { return bHasOSMId; } + void SetHasOSMId(int bIn) { bHasOSMId = bIn; } + + int HasVersion() const { return bHasVersion; } + void SetHasVersion(int bIn) { bHasVersion = bIn; } + + int HasTimestamp() const { return bHasTimestamp; } + void SetHasTimestamp(int bIn) { bHasTimestamp = bIn; } + + int HasUID() const { return bHasUID; } + void SetHasUID(int bIn) { bHasUID = bIn; } + + int HasUser() const { return bHasUser; } + void SetHasUser(int bIn) { bHasUser = bIn; } + + int HasChangeset() const { return bHasChangeset; } + void SetHasChangeset(int bIn) { bHasChangeset = bIn; } + + void SetHasOtherTags(int bIn) { bHasOtherTags = bIn; } + int HasOtherTags() const { return bHasOtherTags; } + + void SetHasAllTags(int bIn) { bHasAllTags = bIn; } + int HasAllTags() const { return bHasAllTags; } + + void SetFieldsFromTags(OGRFeature* poFeature, + GIntBig nID, + int bIsWayID, + unsigned int nTags, OSMTag* pasTags, + OSMInfo* psInfo); + + void SetDeclareInterest(int bIn) { bUserInterested = bIn; } + int IsUserInterested() const { return bUserInterested; } + + int HasAttributeFilter() const { return m_poAttrQuery != NULL; } + int EvaluateAttributeFilter(OGRFeature* poFeature); + + void AddUnsignificantKey(const char* pszK); + int IsSignificantKey(const char* pszK) const + { return aoSetUnsignificantKeys.find(pszK) == aoSetUnsignificantKeys.end(); } + + void AddIgnoreKey(const char* pszK); + void AddWarnKey(const char* pszK); + + void AddComputedAttribute(const char* pszName, + OGRFieldType eType, + const char* pszSQL); +}; + +/************************************************************************/ +/* OGROSMDataSource */ +/************************************************************************/ + +typedef struct +{ + char* pszK; + int nKeyIndex; + int nOccurences; + std::vector asValues; + std::map anMapV; /* map that is the reverse of asValues */ +} KeyDesc; + +typedef struct +{ + short nKeyIndex; /* index of OGROSMDataSource.asKeys */ + short bVIsIndex; /* whether we should use nValueIndex or nOffsetInpabyNonRedundantValues */ + union + { + int nValueIndex; /* index of KeyDesc.asValues */ + int nOffsetInpabyNonRedundantValues; /* offset in OGROSMDataSource.pabyNonRedundantValues */ + } u; +} IndexedKVP; + +typedef struct +{ + GIntBig nOff; + /* Note: only one of nth bucket pabyBitmap or panSectorSize must be free'd */ + union + { + GByte *pabyBitmap; /* array of BUCKET_BITMAP_SIZE bytes */ + GByte *panSectorSize; /* array of BUCKET_SECTOR_SIZE_ARRAY_SIZE bytes. Each values means (size in bytes - 8 ) / 2, minus 8. 252 means uncompressed */ + } u; +} Bucket; + +typedef struct +{ + int nLon; + int nLat; +} LonLat; + +typedef struct +{ + GIntBig nWayID; + GIntBig* panNodeRefs; /* point to a sub-array of OGROSMDataSource.anReqIds */ + unsigned int nRefs; + unsigned int nTags; + IndexedKVP* pasTags; /* point to a sub-array of OGROSMDataSource.pasAccumulatedTags */ + OSMInfo sInfo; + OGRFeature *poFeature; + int bIsArea : 1; + int bAttrFilterAlreadyEvaluated : 1; +} WayFeaturePair; + +#ifdef ENABLE_NODE_LOOKUP_BY_HASHING +typedef struct +{ + int nInd; /* values are indexes of panReqIds */ + int nNext; /* values are indexes of psCollisionBuckets, or -1 to stop the chain */ +} CollisionBucket; +#endif + +class OGROSMDataSource : public OGRDataSource +{ + friend class OGROSMLayer; + + int nLayers; + OGROSMLayer** papoLayers; + char* pszName; + + OGREnvelope sExtent; + int bExtentValid; + + int bInterleavedReading; + OGROSMLayer *poCurrentLayer; + + OSMContext *psParser; + int bHasParsedFirstChunk; + int bStopParsing; + +#ifdef HAVE_SQLITE_VFS + sqlite3_vfs* pMyVFS; +#endif + + sqlite3 *hDB; + sqlite3_stmt *hInsertNodeStmt; + sqlite3_stmt *hInsertWayStmt; + sqlite3_stmt *hSelectNodeBetweenStmt; + sqlite3_stmt **pahSelectNodeStmt; + sqlite3_stmt **pahSelectWayStmt; + sqlite3_stmt *hInsertPolygonsStandaloneStmt; + sqlite3_stmt *hDeletePolygonsStandaloneStmt; + sqlite3_stmt *hSelectPolygonsStandaloneStmt; + int bHasRowInPolygonsStandalone; + + sqlite3 *hDBForComputedAttributes; + + int nMaxSizeForInMemoryDBInMB; + int bInMemoryTmpDB; + int bMustUnlink; + CPLString osTmpDBName; + + int nNodesInTransaction; + + std::set aoSetClosedWaysArePolygons; + + LonLat *pasLonLatCache; + + int bReportAllNodes; + int bReportAllWays; + + int bFeatureAdded; + + int bInTransaction; + + int bIndexPoints; + int bUsePointsIndex; + int bIndexWays; + int bUseWaysIndex; + + std::vector abSavedDeclaredInterest; + OGRLayer* poResultSetLayer; + int bIndexPointsBackup; + int bUsePointsIndexBackup; + int bIndexWaysBackup; + int bUseWaysIndexBackup; + + int bIsFeatureCountEnabled; + + int bAttributeNameLaundering; + + GByte *pabyWayBuffer; + + int nWaysProcessed; + int nRelationsProcessed; + + int bCustomIndexing; + int bCompressNodes; + + unsigned int nUnsortedReqIds; + GIntBig *panUnsortedReqIds; + + unsigned int nReqIds; + GIntBig *panReqIds; + +#ifdef ENABLE_NODE_LOOKUP_BY_HASHING + int bEnableHashedIndex; + /* values >= 0 are indexes of panReqIds. */ + /* == -1 for unoccupied */ + /* < -1 are expressed as -nIndexToCollisonBuckets-2 where nIndexToCollisonBuckets point to psCollisionBuckets */ + int *panHashedIndexes; + CollisionBucket *psCollisionBuckets; + int bHashedIndexValid; +#endif + + LonLat *pasLonLatArray; + + IndexedKVP *pasAccumulatedTags; /* points to content of pabyNonRedundantValues or aoMapIndexedKeys */ + int nAccumulatedTags; + GByte *pabyNonRedundantValues; + int nNonRedundantValuesLen; + WayFeaturePair *pasWayFeaturePairs; + int nWayFeaturePairs; + + int nNextKeyIndex; + std::vector asKeys; + std::map aoMapIndexedKeys; /* map that is the reverse of asKeys */ + + CPLString osNodesFilename; + int bInMemoryNodesFile; + int bMustUnlinkNodesFile; + GIntBig nNodesFileSize; + VSILFILE *fpNodes; + + GIntBig nPrevNodeId; + int nBucketOld; + int nOffInBucketReducedOld; + GByte *pabySector; + Bucket *papsBuckets; + int nBuckets; + + int bNeedsToSaveWayInfo; + + int CompressWay (unsigned int nTags, IndexedKVP* pasTags, + int nPoints, LonLat* pasLonLatPairs, + OSMInfo* psInfo, + GByte* pabyCompressedWay); + int UncompressWay( int nBytes, GByte* pabyCompressedWay, + LonLat* pasCoords, + unsigned int* pnTags, OSMTag* pasTags, + OSMInfo* psInfo ); + + int ParseConf(char** papszOpenOptions); + int CreateTempDB(); + int SetDBOptions(); + int SetCacheSize(); + int CreatePreparedStatements(); + void CloseDB(); + + int IndexPoint(OSMNode* psNode); + int IndexPointSQLite(OSMNode* psNode); + int FlushCurrentSector(); + int FlushCurrentSectorCompressedCase(); + int FlushCurrentSectorNonCompressedCase(); + int IndexPointCustom(OSMNode* psNode); + + void IndexWay(GIntBig nWayID, + unsigned int nTags, IndexedKVP* pasTags, + LonLat* pasLonLatPairs, int nPairs, + OSMInfo* psInfo); + + int StartTransactionCacheDB(); + int CommitTransactionCacheDB(); + + int FindNode(GIntBig nID); + void ProcessWaysBatch(); + + void ProcessPolygonsStandalone(); + + void LookupNodes(); + void LookupNodesSQLite(); + void LookupNodesCustom(); + void LookupNodesCustomCompressedCase(); + void LookupNodesCustomNonCompressedCase(); + + unsigned int LookupWays( std::map< GIntBig, std::pair >& aoMapWays, + OSMRelation* psRelation ); + + OGRGeometry* BuildMultiPolygon(OSMRelation* psRelation, + unsigned int* pnTags, + OSMTag* pasTags); + OGRGeometry* BuildGeometryCollection(OSMRelation* psRelation, int bMultiLineString); + + int TransferToDiskIfNecesserary(); + + int AllocBucket(int iBucket); + int AllocMoreBuckets(int nNewBucketIdx, int bAllocBucket = FALSE); + + void AddComputedAttributes(int iCurLayer, + const std::vector& oAttributes); + + public: + OGROSMDataSource(); + ~OGROSMDataSource(); + + virtual const char *GetName() { return pszName; } + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer *GetLayer( int ); + + virtual int TestCapability( const char * ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + + int Open ( const char* pszFilename, char** papszOpenOptions ); + + int ResetReading(); + int ParseNextChunk(int nIdxLayer); + OGRErr GetExtent( OGREnvelope *psExtent ); + int IsInterleavedReading(); + + void NotifyNodes(unsigned int nNodes, OSMNode* pasNodes); + void NotifyWay (OSMWay* psWay); + void NotifyRelation (OSMRelation* psRelation); + void NotifyBounds (double dfXMin, double dfYMin, + double dfXMax, double dfYMax); + + OGROSMLayer* GetCurrentLayer() { return poCurrentLayer; } + void SetCurrentLayer(OGROSMLayer* poLyr) { poCurrentLayer = poLyr; } + + int IsFeatureCountEnabled() const { return bIsFeatureCountEnabled; } + + int DoesAttributeNameLaundering() const { return bAttributeNameLaundering; } +}; + +#endif /* ndef _OGR_OSM_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/ogrosmdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/ogrosmdatasource.cpp new file mode 100644 index 000000000..9dbaeb130 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/ogrosmdatasource.cpp @@ -0,0 +1,4301 @@ +/****************************************************************************** + * $Id: ogrosmdatasource.cpp 29242 2015-05-24 10:59:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGROSMDataSource class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_osm.h" +#include "cpl_conv.h" +#include "cpl_time.h" +#include "ogr_p.h" +#include "ogr_api.h" +#include "swq.h" +#include "gpb.h" +#include "ogrlayerdecorator.h" +#include "ogrsqliteexecutesql.h" +#include "cpl_multiproc.h" + +#include + +#define LIMIT_IDS_PER_REQUEST 200 + +#define MAX_NODES_PER_WAY 2000 + +#define IDX_LYR_POINTS 0 +#define IDX_LYR_LINES 1 +#define IDX_LYR_MULTILINESTRINGS 2 +#define IDX_LYR_MULTIPOLYGONS 3 +#define IDX_LYR_OTHER_RELATIONS 4 + +#define DBL_TO_INT(x) (int)floor((x) * 1e7 + 0.5) +#define INT_TO_DBL(x) ((x) / 1e7) + +#define MAX_COUNT_FOR_TAGS_IN_WAY 255 /* must fit on 1 byte */ +#define MAX_SIZE_FOR_TAGS_IN_WAY 1024 + +/* 5 bytes for encoding a int : really the worst case scenario ! */ +#define WAY_BUFFER_SIZE (1 + MAX_NODES_PER_WAY * 2 * 5 + MAX_SIZE_FOR_TAGS_IN_WAY) + +#define NODE_PER_BUCKET 65536 + + /* Initial Maximum count of buckets */ +#define INIT_BUCKET_COUNT 65536 + +#define VALID_ID_FOR_CUSTOM_INDEXING(_id) ((_id) >= 0 && (_id / NODE_PER_BUCKET) < INT_MAX) + +/* Minimum size of data written on disk, in *uncompressed* case */ +#define SECTOR_SIZE 512 +/* Which represents, 64 nodes */ +/* #define NODE_PER_SECTOR SECTOR_SIZE / (2 * 4) */ +#define NODE_PER_SECTOR 64 +#define NODE_PER_SECTOR_SHIFT 6 + +/* Per bucket, we keep track of the absence/presence of sectors */ +/* only, to reduce memory usage */ +/* #define BUCKET_BITMAP_SIZE NODE_PER_BUCKET / (8 * NODE_PER_SECTOR) */ +#define BUCKET_BITMAP_SIZE 128 + +/* #define BUCKET_SECTOR_SIZE_ARRAY_SIZE NODE_PER_BUCKET / NODE_PER_SECTOR */ +/* Per bucket, we keep track of the real size of the sector. Each sector */ +/* size is encoded in a single byte, whose value is : */ +/* (sector_size in bytes - 8 ) / 2, minus 8. 252 means uncompressed */ +#define BUCKET_SECTOR_SIZE_ARRAY_SIZE 1024 + +/* Must be a multiple of both BUCKET_BITMAP_SIZE and BUCKET_SECTOR_SIZE_ARRAY_SIZE */ +#define PAGE_SIZE 4096 + +/* compressSize should not be greater than 512, so COMPRESS_SIZE_TO_BYTE() fits on a byte */ +#define COMPRESS_SIZE_TO_BYTE(nCompressSize) (GByte)(((nCompressSize) - 8) / 2) +#define ROUND_COMPRESS_SIZE(nCompressSize) (((nCompressSize) + 1) / 2) * 2; +#define COMPRESS_SIZE_FROM_BYTE(byte_on_size) ((byte_on_size) * 2 + 8) + +/* Max number of features that are accumulated in pasWayFeaturePairs */ +#define MAX_DELAYED_FEATURES 75000 +/* Max number of tags that are accumulated in pasAccumulatedTags */ +#define MAX_ACCUMULATED_TAGS MAX_DELAYED_FEATURES * 5 +/* Max size of the string with tag values that are accumulated in pabyNonRedundantValues */ +#define MAX_NON_REDUNDANT_VALUES MAX_DELAYED_FEATURES * 10 +/* Max number of features that are accumulated in panUnsortedReqIds */ +#define MAX_ACCUMULATED_NODES 1000000 + +#ifdef ENABLE_NODE_LOOKUP_BY_HASHING +/* Size of panHashedIndexes array. Must be in the list at */ +/* http://planetmath.org/goodhashtableprimes , and greater than MAX_ACCUMULATED_NODES */ +#define HASHED_INDEXES_ARRAY_SIZE 3145739 +//#define HASHED_INDEXES_ARRAY_SIZE 1572869 +#define COLLISION_BUCKET_ARRAY_SIZE ((MAX_ACCUMULATED_NODES / 100) * 40) +/* hash function = identity ! */ +#define HASH_ID_FUNC(x) ((GUIntBig)(x)) +#endif // ENABLE_NODE_LOOKUP_BY_HASHING + +//#define FAKE_LOOKUP_NODES + +//#define DEBUG_MEM_USAGE +#ifdef DEBUG_MEM_USAGE +size_t GetMaxTotalAllocs(); +#endif + +static void WriteVarInt64(GUIntBig nSVal, GByte** ppabyData); +static void WriteVarSInt64(GIntBig nSVal, GByte** ppabyData); + +CPL_CVSID("$Id: ogrosmdatasource.cpp 29242 2015-05-24 10:59:41Z rouault $"); + +class DSToBeOpened +{ + public: + GIntBig nPID; + CPLString osDSName; + CPLString osInterestLayers; +}; + +static CPLMutex *hMutex = NULL; +static std::vector oListDSToBeOpened; + +/************************************************************************/ +/* AddInterestLayersForDSName() */ +/************************************************************************/ + +static void AddInterestLayersForDSName(const CPLString& osDSName, + const CPLString& osInterestLayers) +{ + CPLMutexHolder oMutexHolder(&hMutex); + DSToBeOpened oDSToBeOpened; + oDSToBeOpened.nPID = CPLGetPID(); + oDSToBeOpened.osDSName = osDSName; + oDSToBeOpened.osInterestLayers = osInterestLayers; + oListDSToBeOpened.push_back( oDSToBeOpened ); +} + +/************************************************************************/ +/* GetInterestLayersForDSName() */ +/************************************************************************/ + +static CPLString GetInterestLayersForDSName(const CPLString& osDSName) +{ + CPLMutexHolder oMutexHolder(&hMutex); + GIntBig nPID = CPLGetPID(); + for(int i = 0; i < (int)oListDSToBeOpened.size(); i++) + { + if( oListDSToBeOpened[i].nPID == nPID && + oListDSToBeOpened[i].osDSName == osDSName ) + { + CPLString osInterestLayers = oListDSToBeOpened[i].osInterestLayers; + oListDSToBeOpened.erase(oListDSToBeOpened.begin()+i); + return osInterestLayers; + } + } + return ""; +} + +/************************************************************************/ +/* OGROSMDataSource() */ +/************************************************************************/ + +OGROSMDataSource::OGROSMDataSource() + +{ + nLayers = 0; + papoLayers = NULL; + pszName = NULL; + bExtentValid = FALSE; + bInterleavedReading = -1; + poCurrentLayer = NULL; + psParser = NULL; + bHasParsedFirstChunk = FALSE; + bStopParsing = FALSE; +#ifdef HAVE_SQLITE_VFS + pMyVFS = NULL; +#endif + hDB = NULL; + hInsertNodeStmt = NULL; + hInsertWayStmt = NULL; + hInsertPolygonsStandaloneStmt = NULL; + hDeletePolygonsStandaloneStmt = NULL; + hSelectPolygonsStandaloneStmt = NULL; + bHasRowInPolygonsStandalone = FALSE; + + hDBForComputedAttributes = NULL; + + nNodesInTransaction = 0; + bInTransaction = FALSE; + pahSelectNodeStmt = NULL; + pahSelectWayStmt = NULL; + pasLonLatCache = NULL; + bInMemoryTmpDB = FALSE; + bMustUnlink = TRUE; + nMaxSizeForInMemoryDBInMB = 0; + bReportAllNodes = FALSE; + bReportAllWays = FALSE; + bFeatureAdded = FALSE; + + bIndexPoints = TRUE; + bUsePointsIndex = TRUE; + bIndexWays = TRUE; + bUseWaysIndex = TRUE; + + poResultSetLayer = NULL; + bIndexPointsBackup = FALSE; + bUsePointsIndexBackup = FALSE; + bIndexWaysBackup = FALSE; + bUseWaysIndexBackup = FALSE; + + bIsFeatureCountEnabled = FALSE; + + bAttributeNameLaundering = TRUE; + + pabyWayBuffer = NULL; + + nWaysProcessed = 0; + nRelationsProcessed = 0; + + bCustomIndexing = TRUE; + bCompressNodes = FALSE; + + bInMemoryNodesFile = FALSE; + bMustUnlinkNodesFile = TRUE; + fpNodes = NULL; + nNodesFileSize = 0; + + nPrevNodeId = -INT_MAX; + nBucketOld = -1; + nOffInBucketReducedOld = -1; + pabySector = NULL; + papsBuckets = NULL; + nBuckets = 0; + + nReqIds = 0; + panReqIds = NULL; +#ifdef ENABLE_NODE_LOOKUP_BY_HASHING + bEnableHashedIndex = TRUE; + panHashedIndexes = NULL; + psCollisionBuckets = NULL; + bHashedIndexValid = FALSE; +#endif + pasLonLatArray = NULL; + nUnsortedReqIds = 0; + panUnsortedReqIds = NULL; + nWayFeaturePairs = 0; + pasWayFeaturePairs = NULL; + nAccumulatedTags = 0; + pasAccumulatedTags = NULL; + nNonRedundantValuesLen = 0; + pabyNonRedundantValues = NULL; + nNextKeyIndex = 0; + + bNeedsToSaveWayInfo = FALSE; +} + +/************************************************************************/ +/* ~OGROSMDataSource() */ +/************************************************************************/ + +OGROSMDataSource::~OGROSMDataSource() + +{ + int i; + for(i=0;ipAppData); + CPLFree(pMyVFS); + } +#endif + + if( osTmpDBName.size() && bMustUnlink ) + { + const char* pszVal = CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES"); + if( !EQUAL(pszVal, "NOT_EVEN_AT_END") ) + VSIUnlink(osTmpDBName); + } + + CPLFree(panReqIds); +#ifdef ENABLE_NODE_LOOKUP_BY_HASHING + CPLFree(panHashedIndexes); + CPLFree(psCollisionBuckets); +#endif + CPLFree(pasLonLatArray); + CPLFree(panUnsortedReqIds); + + for( i = 0; i < nWayFeaturePairs; i++) + { + delete pasWayFeaturePairs[i].poFeature; + } + CPLFree(pasWayFeaturePairs); + CPLFree(pasAccumulatedTags); + CPLFree(pabyNonRedundantValues); + +#ifdef OSM_DEBUG + FILE* f; + f = fopen("keys.txt", "wt"); + for(i=0;i<(int)asKeys.size();i++) + { + KeyDesc* psKD = asKeys[i]; + fprintf(f, "%08d idx=%d %s\n", + psKD->nOccurences, + psKD->nKeyIndex, + psKD->pszK); + } + fclose(f); +#endif + + for(i=0;i<(int)asKeys.size();i++) + { + KeyDesc* psKD = asKeys[i]; + CPLFree(psKD->pszK); + for(int j=0;j<(int)psKD->asValues.size();j++) + CPLFree(psKD->asValues[j]); + delete psKD; + } + + if( fpNodes ) + VSIFCloseL(fpNodes); + if( osNodesFilename.size() && bMustUnlinkNodesFile ) + { + const char* pszVal = CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES"); + if( !EQUAL(pszVal, "NOT_EVEN_AT_END") ) + VSIUnlink(osNodesFilename); + } + + CPLFree(pabySector); + if( papsBuckets ) + { + for( i = 0; i < nBuckets; i++) + { + if( bCompressNodes ) + { + int nRem = i % (PAGE_SIZE / BUCKET_SECTOR_SIZE_ARRAY_SIZE); + if( nRem == 0 ) + CPLFree(papsBuckets[i].u.panSectorSize); + } + else + { + int nRem = i % (PAGE_SIZE / BUCKET_BITMAP_SIZE); + if( nRem == 0 ) + CPLFree(papsBuckets[i].u.pabyBitmap); + } + } + CPLFree(papsBuckets); + } +} + +/************************************************************************/ +/* CloseDB() */ +/************************************************************************/ + +void OGROSMDataSource::CloseDB() +{ + int i; + + if( hInsertNodeStmt != NULL ) + sqlite3_finalize( hInsertNodeStmt ); + hInsertNodeStmt = NULL; + + if( hInsertWayStmt != NULL ) + sqlite3_finalize( hInsertWayStmt ); + hInsertWayStmt = NULL; + + if( hInsertPolygonsStandaloneStmt != NULL ) + sqlite3_finalize( hInsertPolygonsStandaloneStmt ); + hInsertPolygonsStandaloneStmt = NULL; + + if( hDeletePolygonsStandaloneStmt != NULL ) + sqlite3_finalize( hDeletePolygonsStandaloneStmt ); + hDeletePolygonsStandaloneStmt = NULL; + + if( hSelectPolygonsStandaloneStmt != NULL ) + sqlite3_finalize( hSelectPolygonsStandaloneStmt ); + hSelectPolygonsStandaloneStmt = NULL; + + if( pahSelectNodeStmt != NULL ) + { + for(i = 0; i < LIMIT_IDS_PER_REQUEST; i++) + { + if( pahSelectNodeStmt[i] != NULL ) + sqlite3_finalize( pahSelectNodeStmt[i] ); + } + CPLFree(pahSelectNodeStmt); + pahSelectNodeStmt = NULL; + } + + if( pahSelectWayStmt != NULL ) + { + for(i = 0; i < LIMIT_IDS_PER_REQUEST; i++) + { + if( pahSelectWayStmt[i] != NULL ) + sqlite3_finalize( pahSelectWayStmt[i] ); + } + CPLFree(pahSelectWayStmt); + pahSelectWayStmt = NULL; + } + + if( bInTransaction ) + CommitTransactionCacheDB(); + + sqlite3_close(hDB); + hDB = NULL; +} + +/************************************************************************/ +/* IndexPoint() */ +/************************************************************************/ + +static const GByte abyBitsCount[] = { +0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4, +1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, +1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, +2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, +1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, +2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, +2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, +3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, +1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, +2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, +2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, +3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, +2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, +3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, +3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, +4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8 +}; + +int OGROSMDataSource::IndexPoint(OSMNode* psNode) +{ + if( !bIndexPoints ) + return TRUE; + + if( bCustomIndexing) + return IndexPointCustom(psNode); + else + return IndexPointSQLite(psNode); +} + +/************************************************************************/ +/* IndexPointSQLite() */ +/************************************************************************/ + +int OGROSMDataSource::IndexPointSQLite(OSMNode* psNode) +{ + sqlite3_bind_int64( hInsertNodeStmt, 1, psNode->nID ); + + LonLat sLonLat; + sLonLat.nLon = DBL_TO_INT(psNode->dfLon); + sLonLat.nLat = DBL_TO_INT(psNode->dfLat); + + sqlite3_bind_blob( hInsertNodeStmt, 2, &sLonLat, sizeof(sLonLat), SQLITE_STATIC ); + + int rc = sqlite3_step( hInsertNodeStmt ); + sqlite3_reset( hInsertNodeStmt ); + if( !(rc == SQLITE_OK || rc == SQLITE_DONE) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Failed inserting node " CPL_FRMT_GIB ": %s", + psNode->nID, sqlite3_errmsg(hDB)); + } + + return TRUE; +} + +/************************************************************************/ +/* FlushCurrentSector() */ +/************************************************************************/ + +int OGROSMDataSource::FlushCurrentSector() +{ +#ifndef FAKE_LOOKUP_NODES + if( bCompressNodes ) + return FlushCurrentSectorCompressedCase(); + else + return FlushCurrentSectorNonCompressedCase(); +#else + return TRUE; +#endif +} + +/************************************************************************/ +/* AllocBucket() */ +/************************************************************************/ + +int OGROSMDataSource::AllocBucket(int iBucket) +{ + int bOOM = FALSE; + if( bCompressNodes ) + { + int nRem = iBucket % (PAGE_SIZE / BUCKET_SECTOR_SIZE_ARRAY_SIZE); + if( papsBuckets[iBucket - nRem].u.panSectorSize == NULL ) + papsBuckets[iBucket - nRem].u.panSectorSize = (GByte*)VSICalloc(1, PAGE_SIZE); + if( papsBuckets[iBucket - nRem].u.panSectorSize == NULL ) + { + papsBuckets[iBucket].u.panSectorSize = NULL; + bOOM = TRUE; + } + else + papsBuckets[iBucket].u.panSectorSize = papsBuckets[iBucket - nRem].u.panSectorSize + nRem * BUCKET_SECTOR_SIZE_ARRAY_SIZE; + } + else + { + int nRem = iBucket % (PAGE_SIZE / BUCKET_BITMAP_SIZE); + if( papsBuckets[iBucket - nRem].u.pabyBitmap == NULL ) + papsBuckets[iBucket - nRem].u.pabyBitmap = (GByte*)VSICalloc(1, PAGE_SIZE); + if( papsBuckets[iBucket - nRem].u.pabyBitmap == NULL ) + { + papsBuckets[iBucket].u.pabyBitmap = NULL; + bOOM = TRUE; + } + else + papsBuckets[iBucket].u.pabyBitmap = papsBuckets[iBucket - nRem].u.pabyBitmap + nRem * BUCKET_BITMAP_SIZE; + } + + if( bOOM ) + { + CPLError(CE_Failure, CPLE_AppDefined, "AllocBucket() failed. Use OSM_USE_CUSTOM_INDEXING=NO"); + bStopParsing = TRUE; + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* AllocMoreBuckets() */ +/************************************************************************/ + +int OGROSMDataSource::AllocMoreBuckets(int nNewBucketIdx, int bAllocBucket) +{ + CPLAssert(nNewBucketIdx >= nBuckets); + + int nNewBuckets = MAX(nBuckets + nBuckets / 2, nNewBucketIdx); + + size_t nNewSize = sizeof(Bucket) * nNewBuckets; + if( (GUIntBig)nNewSize != sizeof(Bucket) * (GUIntBig)nNewBuckets ) + { + CPLError(CE_Failure, CPLE_AppDefined, "AllocMoreBuckets() failed. Use OSM_USE_CUSTOM_INDEXING=NO"); + bStopParsing = TRUE; + return FALSE; + } + + Bucket* papsNewBuckets = (Bucket*) VSIRealloc(papsBuckets, nNewSize); + if( papsNewBuckets == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "AllocMoreBuckets() failed. Use OSM_USE_CUSTOM_INDEXING=NO"); + bStopParsing = TRUE; + return FALSE; + } + papsBuckets = papsNewBuckets; + + int bOOM = FALSE; + int i; + for(i = nBuckets; i < nNewBuckets && !bOOM; i++) + { + papsBuckets[i].nOff = -1; + if( bAllocBucket ) + { + if( !AllocBucket(i) ) + bOOM = TRUE; + } + else + { + if( bCompressNodes ) + papsBuckets[i].u.panSectorSize = NULL; + else + papsBuckets[i].u.pabyBitmap = NULL; + } + } + nBuckets = i; + + if( bOOM ) + { + CPLError(CE_Failure, CPLE_AppDefined, "AllocMoreBuckets() failed. Use OSM_USE_CUSTOM_INDEXING=NO"); + bStopParsing = TRUE; + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* FlushCurrentSectorCompressedCase() */ +/************************************************************************/ + +int OGROSMDataSource::FlushCurrentSectorCompressedCase() +{ + GByte abyOutBuffer[2 * SECTOR_SIZE]; + GByte* pabyOut = abyOutBuffer; + LonLat* pasLonLatIn = (LonLat*)pabySector; + int nLastLon = 0, nLastLat = 0; + int bLastValid = FALSE; + int i; + + CPLAssert((NODE_PER_SECTOR % 8) == 0); + memset(abyOutBuffer, 0, NODE_PER_SECTOR / 8); + pabyOut += NODE_PER_SECTOR / 8; + for(i = 0; i < NODE_PER_SECTOR; i++) + { + if( pasLonLatIn[i].nLon || pasLonLatIn[i].nLat ) + { + abyOutBuffer[i >> 3] |= (1 << (i % 8)); + if( bLastValid ) + { + GIntBig nDiff64Lon = (GIntBig)pasLonLatIn[i].nLon - (GIntBig)nLastLon; + GIntBig nDiff64Lat = pasLonLatIn[i].nLat - nLastLat; + WriteVarSInt64(nDiff64Lon, &pabyOut); + WriteVarSInt64(nDiff64Lat, &pabyOut); + } + else + { + memcpy(pabyOut, &pasLonLatIn[i], sizeof(LonLat)); + pabyOut += sizeof(LonLat); + } + bLastValid = TRUE; + + nLastLon = pasLonLatIn[i].nLon; + nLastLat = pasLonLatIn[i].nLat; + } + } + + size_t nCompressSize = (size_t)(pabyOut - abyOutBuffer); + CPLAssert(nCompressSize < sizeof(abyOutBuffer) - 1); + abyOutBuffer[nCompressSize] = 0; + + nCompressSize = ROUND_COMPRESS_SIZE(nCompressSize); + GByte* pabyToWrite; + if(nCompressSize >= SECTOR_SIZE) + { + nCompressSize = SECTOR_SIZE; + pabyToWrite = pabySector; + } + else + pabyToWrite = abyOutBuffer; + + if( VSIFWriteL(pabyToWrite, 1, nCompressSize, fpNodes) == nCompressSize ) + { + memset(pabySector, 0, SECTOR_SIZE); + nNodesFileSize += nCompressSize; + + if( nBucketOld >= nBuckets ) + { + if( !AllocMoreBuckets(nBucketOld + 1) ) + return FALSE; + } + Bucket* psBucket = &papsBuckets[nBucketOld]; + if( psBucket->u.panSectorSize == NULL && !AllocBucket(nBucketOld) ) + return FALSE; + psBucket->u.panSectorSize[nOffInBucketReducedOld] = + COMPRESS_SIZE_TO_BYTE(nCompressSize); + + return TRUE; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot write in temporary node file %s : %s", + osNodesFilename.c_str(), VSIStrerror(errno)); + } + + return FALSE; +} + +/************************************************************************/ +/* FlushCurrentSectorNonCompressedCase() */ +/************************************************************************/ + +int OGROSMDataSource::FlushCurrentSectorNonCompressedCase() +{ + if( VSIFWriteL(pabySector, 1, SECTOR_SIZE, fpNodes) == SECTOR_SIZE ) + { + memset(pabySector, 0, SECTOR_SIZE); + nNodesFileSize += SECTOR_SIZE; + return TRUE; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot write in temporary node file %s : %s", + osNodesFilename.c_str(), VSIStrerror(errno)); + } + + return FALSE; +} + +/************************************************************************/ +/* IndexPointCustom() */ +/************************************************************************/ + +int OGROSMDataSource::IndexPointCustom(OSMNode* psNode) +{ + if( psNode->nID <= nPrevNodeId) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Non increasing node id. Use OSM_USE_CUSTOM_INDEXING=NO"); + bStopParsing = TRUE; + return FALSE; + } + if( !VALID_ID_FOR_CUSTOM_INDEXING(psNode->nID) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unsupported node id value (" CPL_FRMT_GIB "). Use OSM_USE_CUSTOM_INDEXING=NO", + psNode->nID); + bStopParsing = TRUE; + return FALSE; + } + + int nBucket = (int)(psNode->nID / NODE_PER_BUCKET); + int nOffInBucket = psNode->nID % NODE_PER_BUCKET; + int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT; + int nOffInBucketReducedRemainer = nOffInBucket & ((1 << NODE_PER_SECTOR_SHIFT) - 1); + + if( nBucket >= nBuckets ) + { + if( !AllocMoreBuckets(nBucket + 1) ) + return FALSE; + } + Bucket* psBucket = &papsBuckets[nBucket]; + + if( !bCompressNodes ) + { + int nBitmapIndex = nOffInBucketReduced / 8; + int nBitmapRemainer = nOffInBucketReduced % 8; + if( psBucket->u.pabyBitmap == NULL && !AllocBucket(nBucket) ) + return FALSE; + psBucket->u.pabyBitmap[nBitmapIndex] |= (1 << nBitmapRemainer); + } + + if( nBucket != nBucketOld ) + { + CPLAssert(nBucket > nBucketOld); + if( nBucketOld >= 0 ) + { + if( !FlushCurrentSector() ) + { + bStopParsing = TRUE; + return FALSE; + } + } + nBucketOld = nBucket; + nOffInBucketReducedOld = nOffInBucketReduced; + CPLAssert(psBucket->nOff == -1); + psBucket->nOff = VSIFTellL(fpNodes); + } + else if( nOffInBucketReduced != nOffInBucketReducedOld ) + { + CPLAssert(nOffInBucketReduced > nOffInBucketReducedOld); + if( !FlushCurrentSector() ) + { + bStopParsing = TRUE; + return FALSE; + } + nOffInBucketReducedOld = nOffInBucketReduced; + } + + LonLat* psLonLat = (LonLat*)(pabySector + sizeof(LonLat) * nOffInBucketReducedRemainer); + psLonLat->nLon = DBL_TO_INT(psNode->dfLon); + psLonLat->nLat = DBL_TO_INT(psNode->dfLat); + + nPrevNodeId = psNode->nID; + + return TRUE; +} + +/************************************************************************/ +/* NotifyNodes() */ +/************************************************************************/ + +void OGROSMDataSource::NotifyNodes(unsigned int nNodes, OSMNode* pasNodes) +{ + unsigned int i; + + const OGREnvelope* psEnvelope = + papoLayers[IDX_LYR_POINTS]->GetSpatialFilterEnvelope(); + + for(i = 0; i < nNodes; i++) + { + /* If the point doesn't fit into the envelope of the spatial filter */ + /* then skip it */ + if( psEnvelope != NULL && + !(pasNodes[i].dfLon >= psEnvelope->MinX && + pasNodes[i].dfLon <= psEnvelope->MaxX && + pasNodes[i].dfLat >= psEnvelope->MinY && + pasNodes[i].dfLat <= psEnvelope->MaxY) ) + continue; + + if( !IndexPoint(&pasNodes[i]) ) + break; + + if( !papoLayers[IDX_LYR_POINTS]->IsUserInterested() ) + continue; + + unsigned int j; + int bInterestingTag = bReportAllNodes; + OSMTag* pasTags = pasNodes[i].pasTags; + + if( !bReportAllNodes ) + { + for(j = 0; j < pasNodes[i].nTags; j++) + { + const char* pszK = pasTags[j].pszK; + if( papoLayers[IDX_LYR_POINTS]->IsSignificantKey(pszK) ) + { + bInterestingTag = TRUE; + break; + } + } + } + + if( bInterestingTag ) + { + OGRFeature* poFeature = new OGRFeature( + papoLayers[IDX_LYR_POINTS]->GetLayerDefn()); + + poFeature->SetGeometryDirectly( + new OGRPoint(pasNodes[i].dfLon, pasNodes[i].dfLat)); + + papoLayers[IDX_LYR_POINTS]->SetFieldsFromTags( + poFeature, pasNodes[i].nID, FALSE, pasNodes[i].nTags, pasTags, &pasNodes[i].sInfo ); + + int bFilteredOut = FALSE; + if( !papoLayers[IDX_LYR_POINTS]->AddFeature(poFeature, FALSE, + &bFilteredOut, + !bFeatureAdded) ) + { + bStopParsing = TRUE; + break; + } + else if (!bFilteredOut) + bFeatureAdded = TRUE; + } + } +} + +static void OGROSMNotifyNodes (unsigned int nNodes, + OSMNode* pasNodes, + CPL_UNUSED OSMContext* psOSMContext, + void* user_data) +{ + ((OGROSMDataSource*) user_data)->NotifyNodes(nNodes, pasNodes); +} + +/************************************************************************/ +/* LookupNodes() */ +/************************************************************************/ + +//#define DEBUG_COLLISIONS 1 + +void OGROSMDataSource::LookupNodes( ) +{ + if( bCustomIndexing ) + LookupNodesCustom(); + else + LookupNodesSQLite(); + +#ifdef ENABLE_NODE_LOOKUP_BY_HASHING + if( nReqIds > 1 && bEnableHashedIndex ) + { + memset(panHashedIndexes, 0xFF, HASHED_INDEXES_ARRAY_SIZE * sizeof(int)); + bHashedIndexValid = TRUE; +#ifdef DEBUG_COLLISIONS + int nCollisions = 0; +#endif + int iNextFreeBucket = 0; + for(unsigned int i = 0; i < nReqIds; i++) + { + int nIndInHashArray = HASH_ID_FUNC(panReqIds[i]) % HASHED_INDEXES_ARRAY_SIZE; + int nIdx = panHashedIndexes[nIndInHashArray]; + if( nIdx == -1 ) + { + panHashedIndexes[nIndInHashArray] = i; + } + else + { +#ifdef DEBUG_COLLISIONS + nCollisions ++; +#endif + int iBucket; + if( nIdx >= 0 ) + { + if(iNextFreeBucket == COLLISION_BUCKET_ARRAY_SIZE) + { + CPLDebug("OSM", "Too many collisions. Disabling hashed indexing"); + bHashedIndexValid = FALSE; + bEnableHashedIndex = FALSE; + break; + } + iBucket = iNextFreeBucket; + psCollisionBuckets[iNextFreeBucket].nInd = nIdx; + psCollisionBuckets[iNextFreeBucket].nNext = -1; + panHashedIndexes[nIndInHashArray] = -iNextFreeBucket - 2; + iNextFreeBucket ++; + } + else + iBucket = -nIdx - 2; + if(iNextFreeBucket == COLLISION_BUCKET_ARRAY_SIZE) + { + CPLDebug("OSM", "Too many collisions. Disabling hashed indexing"); + bHashedIndexValid = FALSE; + bEnableHashedIndex = FALSE; + break; + } + while( TRUE ) + { + int iNext = psCollisionBuckets[iBucket].nNext; + if( iNext < 0 ) + { + psCollisionBuckets[iBucket].nNext = iNextFreeBucket; + psCollisionBuckets[iNextFreeBucket].nInd = i; + psCollisionBuckets[iNextFreeBucket].nNext = -1; + iNextFreeBucket ++; + break; + } + iBucket = iNext; + } + } + } +#ifdef DEBUG_COLLISIONS + /* Collision rate in practice is around 12% on France, Germany, ... */ + /* Maximum seen ~ 15.9% on a planet file but often much smaller. */ + CPLDebug("OSM", "nCollisions = %d/%d (%.1f %%), iNextFreeBucket = %d/%d", + nCollisions, nReqIds, nCollisions * 100.0 / nReqIds, + iNextFreeBucket, COLLISION_BUCKET_ARRAY_SIZE); +#endif + } + else + bHashedIndexValid = FALSE; +#endif // ENABLE_NODE_LOOKUP_BY_HASHING +} + +/************************************************************************/ +/* LookupNodesSQLite() */ +/************************************************************************/ + +void OGROSMDataSource::LookupNodesSQLite( ) +{ + unsigned int iCur; + unsigned int i; + + CPLAssert(nUnsortedReqIds <= MAX_ACCUMULATED_NODES); + + nReqIds = 0; + for(i = 0; i < nUnsortedReqIds; i++) + { + GIntBig id = panUnsortedReqIds[i]; + panReqIds[nReqIds++] = id; + } + + std::sort(panReqIds, panReqIds + nReqIds); + + /* Remove duplicates */ + unsigned int j = 0; + for(i = 0; i < nReqIds; i++) + { + if (!(i > 0 && panReqIds[i] == panReqIds[i-1])) + panReqIds[j++] = panReqIds[i]; + } + nReqIds = j; + + iCur = 0; + j = 0; + while( iCur < nReqIds ) + { + unsigned int nToQuery = nReqIds - iCur; + if( nToQuery > LIMIT_IDS_PER_REQUEST ) + nToQuery = LIMIT_IDS_PER_REQUEST; + + sqlite3_stmt* hStmt = pahSelectNodeStmt[nToQuery-1]; + for(i=iCur;inLon; + pasLonLatArray[j].nLat = psLonLat->nLat; + j++; + } + + sqlite3_reset(hStmt); + } + nReqIds = j; +} + +/************************************************************************/ +/* ReadVarSInt64() */ +/************************************************************************/ + +static GIntBig ReadVarSInt64(GByte** ppabyPtr) +{ + GIntBig nSVal64 = ReadVarInt64(ppabyPtr); + GIntBig nDiff64 = ((nSVal64 & 1) == 0) ? (((GUIntBig)nSVal64) >> 1) : -(((GUIntBig)nSVal64) >> 1)-1; + return nDiff64; +} + +/************************************************************************/ +/* DecompressSector() */ +/************************************************************************/ + +static int DecompressSector(GByte* pabyIn, int nSectorSize, GByte* pabyOut) +{ + GByte* pabyPtr = pabyIn; + LonLat* pasLonLatOut = (LonLat*) pabyOut; + int nLastLon = 0, nLastLat = 0; + int bLastValid = FALSE; + int i; + + pabyPtr += NODE_PER_SECTOR / 8; + for(i = 0; i < NODE_PER_SECTOR; i++) + { + if( pabyIn[i >> 3] & (1 << (i % 8)) ) + { + if( bLastValid ) + { + pasLonLatOut[i].nLon = (int)(nLastLon + ReadVarSInt64(&pabyPtr)); + pasLonLatOut[i].nLat = (int)(nLastLat + ReadVarSInt64(&pabyPtr)); + } + else + { + bLastValid = TRUE; + memcpy(&(pasLonLatOut[i]), pabyPtr, sizeof(LonLat)); + pabyPtr += sizeof(LonLat); + } + + nLastLon = pasLonLatOut[i].nLon; + nLastLat = pasLonLatOut[i].nLat; + } + else + { + pasLonLatOut[i].nLon = 0; + pasLonLatOut[i].nLat = 0; + } + } + + int nRead = (int)(pabyPtr - pabyIn); + nRead = ROUND_COMPRESS_SIZE(nRead); + return( nRead == nSectorSize ); +} + +/************************************************************************/ +/* LookupNodesCustom() */ +/************************************************************************/ + +void OGROSMDataSource::LookupNodesCustom( ) +{ + nReqIds = 0; + + if( nBucketOld >= 0 ) + { + if( !FlushCurrentSector() ) + { + bStopParsing = TRUE; + return; + } + + nBucketOld = -1; + } + + unsigned int i; + + CPLAssert(nUnsortedReqIds <= MAX_ACCUMULATED_NODES); + + for(i = 0; i < nUnsortedReqIds; i++) + { + GIntBig id = panUnsortedReqIds[i]; + + if( !VALID_ID_FOR_CUSTOM_INDEXING(id) ) + continue; + + int nBucket = (int)(id / NODE_PER_BUCKET); + int nOffInBucket = id % NODE_PER_BUCKET; + int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT; + + if( nBucket >= nBuckets ) + continue; + Bucket* psBucket = &papsBuckets[nBucket]; + + if( bCompressNodes ) + { + if( psBucket->u.panSectorSize == NULL || + !(psBucket->u.panSectorSize[nOffInBucketReduced]) ) + continue; + } + else + { + int nBitmapIndex = nOffInBucketReduced / 8; + int nBitmapRemainer = nOffInBucketReduced % 8; + if( psBucket->u.pabyBitmap == NULL || + !(psBucket->u.pabyBitmap[nBitmapIndex] & (1 << nBitmapRemainer)) ) + continue; + } + + panReqIds[nReqIds++] = id; + } + + std::sort(panReqIds, panReqIds + nReqIds); + + /* Remove duplicates */ + unsigned int j = 0; + for(i = 0; i < nReqIds; i++) + { + if (!(i > 0 && panReqIds[i] == panReqIds[i-1])) + panReqIds[j++] = panReqIds[i]; + } + nReqIds = j; + +#ifdef FAKE_LOOKUP_NODES + for(i = 0; i < nReqIds; i++) + { + pasLonLatArray[i].nLon = 0; + pasLonLatArray[i].nLat = 0; + } +#else + if( bCompressNodes ) + LookupNodesCustomCompressedCase(); + else + LookupNodesCustomNonCompressedCase(); +#endif +} + +/************************************************************************/ +/* LookupNodesCustomCompressedCase() */ +/************************************************************************/ + +void OGROSMDataSource::LookupNodesCustomCompressedCase() +{ + unsigned int i; + unsigned int j = 0; +#define SECURITY_MARGIN (8 + 8 + 2 * NODE_PER_SECTOR) + GByte abyRawSector[SECTOR_SIZE + SECURITY_MARGIN]; + memset(abyRawSector + SECTOR_SIZE, 0, SECURITY_MARGIN); + + int nBucketOld = -1; + int nOffInBucketReducedOld = -1; + int k = 0; + int nOffFromBucketStart = 0; + + for(i = 0; i < nReqIds; i++) + { + GIntBig id = panReqIds[i]; + + int nBucket = (int)(id / NODE_PER_BUCKET); + int nOffInBucket = id % NODE_PER_BUCKET; + int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT; + int nOffInBucketReducedRemainer = nOffInBucket & ((1 << NODE_PER_SECTOR_SHIFT) - 1); + + if( nBucket != nBucketOld ) + { + nOffInBucketReducedOld = -1; + k = 0; + nOffFromBucketStart = 0; + } + + if ( nOffInBucketReduced != nOffInBucketReducedOld ) + { + if( nBucket >= nBuckets ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read node " CPL_FRMT_GIB, id); + continue; + // FIXME ? + } + Bucket* psBucket = &papsBuckets[nBucket]; + if( psBucket->u.panSectorSize == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read node " CPL_FRMT_GIB, id); + continue; + // FIXME ? + } + int nSectorSize = COMPRESS_SIZE_FROM_BYTE(psBucket->u.panSectorSize[nOffInBucketReduced]); + + /* If we stay in the same bucket, we can reuse the previously */ + /* computed offset, instead of starting from bucket start */ + for(; k < nOffInBucketReduced; k++) + { + if( psBucket->u.panSectorSize[k] ) + nOffFromBucketStart += COMPRESS_SIZE_FROM_BYTE(psBucket->u.panSectorSize[k]); + } + + VSIFSeekL(fpNodes, psBucket->nOff + nOffFromBucketStart, SEEK_SET); + if( nSectorSize == SECTOR_SIZE ) + { + if( VSIFReadL(pabySector, 1, SECTOR_SIZE, fpNodes) != SECTOR_SIZE ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read node " CPL_FRMT_GIB, id); + continue; + // FIXME ? + } + } + else + { + if( (int)VSIFReadL(abyRawSector, 1, nSectorSize, fpNodes) != nSectorSize ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read sector for node " CPL_FRMT_GIB, id); + continue; + // FIXME ? + } + abyRawSector[nSectorSize] = 0; + + if( !DecompressSector(abyRawSector, nSectorSize, pabySector) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error while uncompressing sector for node " CPL_FRMT_GIB, id); + continue; + // FIXME ? + } + } + + nBucketOld = nBucket; + nOffInBucketReducedOld = nOffInBucketReduced; + } + + panReqIds[j] = id; + memcpy(pasLonLatArray + j, + pabySector + nOffInBucketReducedRemainer * sizeof(LonLat), + sizeof(LonLat)); + + if( pasLonLatArray[j].nLon || pasLonLatArray[j].nLat ) + j++; + } + nReqIds = j; +} + +/************************************************************************/ +/* LookupNodesCustomNonCompressedCase() */ +/************************************************************************/ + +void OGROSMDataSource::LookupNodesCustomNonCompressedCase() +{ + unsigned int i; + unsigned int j = 0; + + for(i = 0; i < nReqIds; i++) + { + GIntBig id = panReqIds[i]; + + int nBucket = (int)(id / NODE_PER_BUCKET); + int nOffInBucket = id % NODE_PER_BUCKET; + int nOffInBucketReduced = nOffInBucket >> NODE_PER_SECTOR_SHIFT; + int nOffInBucketReducedRemainer = nOffInBucket & ((1 << NODE_PER_SECTOR_SHIFT) - 1); + + int nBitmapIndex = nOffInBucketReduced / 8; + int nBitmapRemainer = nOffInBucketReduced % 8; + + if( nBucket >= nBuckets ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read node " CPL_FRMT_GIB, id); + continue; + // FIXME ? + } + Bucket* psBucket = &papsBuckets[nBucket]; + if( psBucket->u.pabyBitmap == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read node " CPL_FRMT_GIB, id); + continue; + // FIXME ? + } + + int k; + int nSector = 0; + for(k = 0; k < nBitmapIndex; k++) + nSector += abyBitsCount[psBucket->u.pabyBitmap[k]]; + if (nBitmapRemainer) + nSector += abyBitsCount[psBucket->u.pabyBitmap[nBitmapIndex] & ((1 << nBitmapRemainer) - 1)]; + + VSIFSeekL(fpNodes, psBucket->nOff + nSector * SECTOR_SIZE + nOffInBucketReducedRemainer * sizeof(LonLat), SEEK_SET); + if( VSIFReadL(pasLonLatArray + j, 1, sizeof(LonLat), fpNodes) != sizeof(LonLat) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read node " CPL_FRMT_GIB, id); + // FIXME ? + } + else + { + panReqIds[j] = id; + if( pasLonLatArray[j].nLon || pasLonLatArray[j].nLat ) + j++; + } + } + nReqIds = j; +} + +/************************************************************************/ +/* WriteVarInt() */ +/************************************************************************/ + +static void WriteVarInt(unsigned int nVal, GByte** ppabyData) +{ + GByte* pabyData = *ppabyData; + while(TRUE) + { + if( (nVal & (~0x7f)) == 0 ) + { + *pabyData = (GByte)nVal; + *ppabyData = pabyData + 1; + return; + } + + *pabyData = 0x80 | (GByte)(nVal & 0x7f); + nVal >>= 7; + pabyData ++; + } +} + +/************************************************************************/ +/* WriteVarInt64() */ +/************************************************************************/ + +static void WriteVarInt64(GUIntBig nVal, GByte** ppabyData) +{ + GByte* pabyData = *ppabyData; + while(TRUE) + { + if( (nVal & (~0x7f)) == 0 ) + { + *pabyData = (GByte)nVal; + *ppabyData = pabyData + 1; + return; + } + + *pabyData = 0x80 | (GByte)(nVal & 0x7f); + nVal >>= 7; + pabyData ++; + } +} + +/************************************************************************/ +/* WriteVarSInt64() */ +/************************************************************************/ + +static void WriteVarSInt64(GIntBig nSVal, GByte** ppabyData) +{ + GIntBig nVal; + if( nSVal >= 0 ) + nVal = nSVal << 1; + else + nVal = ((-1-nSVal) << 1) + 1; + + GByte* pabyData = *ppabyData; + while(TRUE) + { + if( (nVal & (~0x7f)) == 0 ) + { + *pabyData = (GByte)nVal; + *ppabyData = pabyData + 1; + return; + } + + *pabyData = 0x80 | (GByte)(nVal & 0x7f); + nVal >>= 7; + pabyData ++; + } +} + +/************************************************************************/ +/* CompressWay() */ +/************************************************************************/ + +int OGROSMDataSource::CompressWay ( unsigned int nTags, IndexedKVP* pasTags, + int nPoints, LonLat* pasLonLatPairs, + OSMInfo* psInfo, + GByte* pabyCompressedWay ) +{ + GByte* pabyPtr = pabyCompressedWay; + pabyPtr ++; + + int nTagCount = 0; + CPLAssert(nTags < MAX_COUNT_FOR_TAGS_IN_WAY); + for(unsigned int iTag = 0; iTag < nTags; iTag++) + { + if ((int)(pabyPtr - pabyCompressedWay) + 2 >= MAX_SIZE_FOR_TAGS_IN_WAY) + { + break; + } + + WriteVarInt(pasTags[iTag].nKeyIndex, &pabyPtr); + + /* to fit in 2 bytes, the theoretical limit would be 127 * 128 + 127 */ + if( pasTags[iTag].bVIsIndex ) + { + if ((int)(pabyPtr - pabyCompressedWay) + 2 >= MAX_SIZE_FOR_TAGS_IN_WAY) + { + break; + } + + WriteVarInt(pasTags[iTag].u.nValueIndex, &pabyPtr); + } + else + { + const char* pszV = (const char*)pabyNonRedundantValues + + pasTags[iTag].u.nOffsetInpabyNonRedundantValues; + + int nLenV = strlen(pszV) + 1; + if ((int)(pabyPtr - pabyCompressedWay) + 2 + nLenV >= MAX_SIZE_FOR_TAGS_IN_WAY) + { + break; + } + + WriteVarInt(0, &pabyPtr); + + memcpy(pabyPtr, pszV, nLenV); + pabyPtr += nLenV; + } + + nTagCount ++; + } + + pabyCompressedWay[0] = (GByte) nTagCount; + + if( bNeedsToSaveWayInfo ) + { + if( psInfo != NULL ) + { + *pabyPtr = 1; + pabyPtr ++; + + WriteVarInt64(psInfo->ts.nTimeStamp, &pabyPtr); + WriteVarInt64(psInfo->nChangeset, &pabyPtr); + WriteVarInt(psInfo->nVersion, &pabyPtr); + WriteVarInt(psInfo->nUID, &pabyPtr); + // FIXME : do something with pszUserSID + } + else + { + *pabyPtr = 0; + pabyPtr ++; + } + } + + memcpy(pabyPtr, &(pasLonLatPairs[0]), sizeof(LonLat)); + pabyPtr += sizeof(LonLat); + for(int i=1;i= 0 && nK < (int)asKeys.size()); + pasTags[iTag].pszK = asKeys[nK]->pszK; + CPLAssert(nV == 0 || (nV > 0 && nV < (int)asKeys[nK]->asValues.size())); + pasTags[iTag].pszV = nV ? asKeys[nK]->asValues[nV] : (const char*) pszV; + } + } + + if( bNeedsToSaveWayInfo ) + { + if( *pabyPtr ) + { + pabyPtr ++; + + OSMInfo sInfo; + if( psInfo == NULL ) + psInfo = &sInfo; + + psInfo->ts.nTimeStamp = ReadVarInt64(&pabyPtr); + psInfo->nChangeset = ReadVarInt64(&pabyPtr); + psInfo->nVersion = ReadVarInt32(&pabyPtr); + psInfo->nUID = ReadVarInt32(&pabyPtr); + + psInfo->bTimeStampIsStr = FALSE; + psInfo->pszUserSID = ""; // FIXME + } + else + pabyPtr ++; + } + + memcpy(&pasCoords[0].nLon, pabyPtr, sizeof(int)); + memcpy(&pasCoords[0].nLat, pabyPtr + sizeof(int), sizeof(int)); + pabyPtr += 2 * sizeof(int); + int nPoints = 1; + do + { + pasCoords[nPoints].nLon = (int)(pasCoords[nPoints-1].nLon + ReadVarSInt64(&pabyPtr)); + pasCoords[nPoints].nLat = (int)(pasCoords[nPoints-1].nLat + ReadVarSInt64(&pabyPtr)); + + nPoints ++; + } while (pabyPtr < pabyCompressedWay + nBytes); + + return nPoints; +} + +/************************************************************************/ +/* IndexWay() */ +/************************************************************************/ + +void OGROSMDataSource::IndexWay(GIntBig nWayID, + unsigned int nTags, IndexedKVP* pasTags, + LonLat* pasLonLatPairs, int nPairs, + OSMInfo* psInfo) +{ + if( !bIndexWays ) + return; + + sqlite3_bind_int64( hInsertWayStmt, 1, nWayID ); + + int nBufferSize = CompressWay (nTags, pasTags, nPairs, pasLonLatPairs, psInfo, pabyWayBuffer); + CPLAssert(nBufferSize <= WAY_BUFFER_SIZE); + sqlite3_bind_blob( hInsertWayStmt, 2, pabyWayBuffer, + nBufferSize, SQLITE_STATIC ); + + int rc = sqlite3_step( hInsertWayStmt ); + sqlite3_reset( hInsertWayStmt ); + if( !(rc == SQLITE_OK || rc == SQLITE_DONE) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Failed inserting way " CPL_FRMT_GIB ": %s", + nWayID, sqlite3_errmsg(hDB)); + } +} + +/************************************************************************/ +/* FindNode() */ +/************************************************************************/ + +int OGROSMDataSource::FindNode(GIntBig nID) +{ + int iFirst = 0; + int iLast = nReqIds - 1; + while(iFirst < iLast) + { + int iMid = (iFirst + iLast) / 2; + if( nID > panReqIds[iMid]) + iFirst = iMid + 1; + else + iLast = iMid; + } + if( iFirst == iLast && nID == panReqIds[iFirst] ) + return iFirst; + return -1; +} + +/************************************************************************/ +/* ProcessWaysBatch() */ +/************************************************************************/ + +void OGROSMDataSource::ProcessWaysBatch() +{ + if( nWayFeaturePairs == 0 ) return; + + //printf("nodes = %d, features = %d\n", nUnsortedReqIds, nWayFeaturePairs); + LookupNodes(); + + int iPair; + for(iPair = 0; iPair < nWayFeaturePairs; iPair ++) + { + WayFeaturePair* psWayFeaturePairs = &pasWayFeaturePairs[iPair]; + + int bIsArea = psWayFeaturePairs->bIsArea; + + unsigned int nFound = 0; + unsigned int i; + +#ifdef ENABLE_NODE_LOOKUP_BY_HASHING + if( bHashedIndexValid ) + { + for(i=0;inRefs;i++) + { + int nIndInHashArray = + HASH_ID_FUNC(psWayFeaturePairs->panNodeRefs[i]) % + HASHED_INDEXES_ARRAY_SIZE; + int nIdx = panHashedIndexes[nIndInHashArray]; + if( nIdx < -1 ) + { + int iBucket = -nIdx - 2; + while( TRUE ) + { + nIdx = psCollisionBuckets[iBucket].nInd; + if( panReqIds[nIdx] == psWayFeaturePairs->panNodeRefs[i] ) + break; + iBucket = psCollisionBuckets[iBucket].nNext; + if( iBucket < 0 ) + { + nIdx = -1; + break; + } + } + } + else if( nIdx >= 0 && + panReqIds[nIdx] != psWayFeaturePairs->panNodeRefs[i] ) + nIdx = -1; + + if (nIdx >= 0) + { + pasLonLatCache[nFound].nLon = pasLonLatArray[nIdx].nLon; + pasLonLatCache[nFound].nLat = pasLonLatArray[nIdx].nLat; + nFound ++; + } + } + } + else +#endif // ENABLE_NODE_LOOKUP_BY_HASHING + { + int nIdx = -1; + for(i=0;inRefs;i++) + { + if( nIdx >= 0 && psWayFeaturePairs->panNodeRefs[i] == + psWayFeaturePairs->panNodeRefs[i-1] + 1 ) + { + if( nIdx+1 < (int)nReqIds && panReqIds[nIdx+1] == + psWayFeaturePairs->panNodeRefs[i] ) + nIdx ++; + else + nIdx = -1; + } + else + nIdx = FindNode( psWayFeaturePairs->panNodeRefs[i] ); + if (nIdx >= 0) + { + pasLonLatCache[nFound].nLon = pasLonLatArray[nIdx].nLon; + pasLonLatCache[nFound].nLat = pasLonLatArray[nIdx].nLat; + nFound ++; + } + } + } + + if( nFound > 0 && bIsArea ) + { + pasLonLatCache[nFound].nLon = pasLonLatCache[0].nLon; + pasLonLatCache[nFound].nLat = pasLonLatCache[0].nLat; + nFound ++; + } + + if( nFound < 2 ) + { + CPLDebug("OSM", "Way " CPL_FRMT_GIB " with %d nodes that could be found. Discarding it", + psWayFeaturePairs->nWayID, nFound); + delete psWayFeaturePairs->poFeature; + psWayFeaturePairs->poFeature = NULL; + psWayFeaturePairs->bIsArea = FALSE; + continue; + } + + if( bIsArea && papoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested() ) + { + IndexWay(psWayFeaturePairs->nWayID, + psWayFeaturePairs->nTags, + psWayFeaturePairs->pasTags, + pasLonLatCache, (int)nFound, + &psWayFeaturePairs->sInfo); + } + else + IndexWay(psWayFeaturePairs->nWayID, 0, NULL, + pasLonLatCache, (int)nFound, NULL); + + if( psWayFeaturePairs->poFeature == NULL ) + { + continue; + } + + OGRLineString* poLS; + OGRGeometry* poGeom; + + poLS = new OGRLineString(); + poGeom = poLS; + + poLS->setNumPoints((int)nFound); + for(i=0;isetPoint(i, + INT_TO_DBL(pasLonLatCache[i].nLon), + INT_TO_DBL(pasLonLatCache[i].nLat)); + } + + psWayFeaturePairs->poFeature->SetGeometryDirectly(poGeom); + + if( nFound != psWayFeaturePairs->nRefs ) + CPLDebug("OSM", "For way " CPL_FRMT_GIB ", got only %d nodes instead of %d", + psWayFeaturePairs->nWayID, nFound, + psWayFeaturePairs->nRefs); + + int bFilteredOut = FALSE; + if( !papoLayers[IDX_LYR_LINES]->AddFeature(psWayFeaturePairs->poFeature, + psWayFeaturePairs->bAttrFilterAlreadyEvaluated, + &bFilteredOut, + !bFeatureAdded) ) + bStopParsing = TRUE; + else if (!bFilteredOut) + bFeatureAdded = TRUE; + } + + if( papoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested() ) + { + for(iPair = 0; iPair < nWayFeaturePairs; iPair ++) + { + WayFeaturePair* psWayFeaturePairs = &pasWayFeaturePairs[iPair]; + + if( psWayFeaturePairs->bIsArea && + (psWayFeaturePairs->nTags || bReportAllWays) ) + { + sqlite3_bind_int64( hInsertPolygonsStandaloneStmt , 1, psWayFeaturePairs->nWayID ); + + int rc = sqlite3_step( hInsertPolygonsStandaloneStmt ); + sqlite3_reset( hInsertPolygonsStandaloneStmt ); + if( !(rc == SQLITE_OK || rc == SQLITE_DONE) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Failed inserting into polygons_standalone " CPL_FRMT_GIB ": %s", + psWayFeaturePairs->nWayID, sqlite3_errmsg(hDB)); + } + } + } + } + + nWayFeaturePairs = 0; + nUnsortedReqIds = 0; + + nAccumulatedTags = 0; + nNonRedundantValuesLen = 0; +} + +/************************************************************************/ +/* NotifyWay() */ +/************************************************************************/ + +void OGROSMDataSource::NotifyWay (OSMWay* psWay) +{ + unsigned int i; + + nWaysProcessed++; + if( (nWaysProcessed % 10000) == 0 ) + { + CPLDebug("OSM", "Ways processed : %d", nWaysProcessed); +#ifdef DEBUG_MEM_USAGE + CPLDebug("OSM", "GetMaxTotalAllocs() = " CPL_FRMT_GUIB, (GUIntBig)GetMaxTotalAllocs()); +#endif + } + + if( !bUsePointsIndex ) + return; + + //printf("way %d : %d nodes\n", (int)psWay->nID, (int)psWay->nRefs); + if( psWay->nRefs > MAX_NODES_PER_WAY ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Ways with more than %d nodes are not supported", + MAX_NODES_PER_WAY); + return; + } + + if( psWay->nRefs < 2 ) + { + CPLDebug("OSM", "Way " CPL_FRMT_GIB " with %d nodes. Discarding it", + psWay->nID, psWay->nRefs); + return; + } + + /* Is a closed way a polygon ? */ + int bIsArea = FALSE; + if( psWay->panNodeRefs[0] == psWay->panNodeRefs[psWay->nRefs - 1] ) + { + for(i=0;inTags;i++) + { + const char* pszK = psWay->pasTags[i].pszK; + if( strcmp(pszK, "area") == 0 ) + { + if( strcmp(psWay->pasTags[i].pszV, "yes") == 0 ) + { + bIsArea = TRUE; + } + else if( strcmp(psWay->pasTags[i].pszV, "no") == 0 ) + { + bIsArea = FALSE; + break; + } + } + else if( aoSetClosedWaysArePolygons.find(pszK) != + aoSetClosedWaysArePolygons.end() ) + { + bIsArea = TRUE; + } + } + } + + OGRFeature* poFeature = NULL; + + int bInterestingTag = bReportAllWays; + if( !bIsArea && !bReportAllWays ) + { + for(i=0;inTags;i++) + { + const char* pszK = psWay->pasTags[i].pszK; + if( papoLayers[IDX_LYR_LINES]->IsSignificantKey(pszK) ) + { + bInterestingTag = TRUE; + break; + } + } + } + + int bAttrFilterAlreadyEvaluated = FALSE; + if( !bIsArea && papoLayers[IDX_LYR_LINES]->IsUserInterested() && bInterestingTag ) + { + poFeature = new OGRFeature(papoLayers[IDX_LYR_LINES]->GetLayerDefn()); + + papoLayers[IDX_LYR_LINES]->SetFieldsFromTags( + poFeature, psWay->nID, FALSE, psWay->nTags, psWay->pasTags, &psWay->sInfo ); + + /* Optimization : if we have an attribute filter, that does not require geometry, */ + /* and if we don't need to index ways, then we can just evaluate the attribute */ + /* filter without the geometry */ + if( papoLayers[IDX_LYR_LINES]->HasAttributeFilter() && + !papoLayers[IDX_LYR_LINES]->AttributeFilterEvaluationNeedsGeometry() && + !bIndexWays ) + { + if( !papoLayers[IDX_LYR_LINES]->EvaluateAttributeFilter(poFeature) ) + { + delete poFeature; + return; + } + bAttrFilterAlreadyEvaluated = TRUE; + } + } + else if( !bIndexWays ) + { + return; + } + + if( nUnsortedReqIds + psWay->nRefs > MAX_ACCUMULATED_NODES || + nWayFeaturePairs == MAX_DELAYED_FEATURES || + nAccumulatedTags + psWay->nTags > MAX_ACCUMULATED_TAGS || + nNonRedundantValuesLen + 1024 > MAX_NON_REDUNDANT_VALUES ) + { + ProcessWaysBatch(); + } + + WayFeaturePair* psWayFeaturePairs = &pasWayFeaturePairs[nWayFeaturePairs]; + + psWayFeaturePairs->nWayID = psWay->nID; + psWayFeaturePairs->nRefs = psWay->nRefs - bIsArea; + psWayFeaturePairs->panNodeRefs = panUnsortedReqIds + nUnsortedReqIds; + psWayFeaturePairs->poFeature = poFeature; + psWayFeaturePairs->bIsArea = bIsArea; + psWayFeaturePairs->bAttrFilterAlreadyEvaluated = bAttrFilterAlreadyEvaluated; + + if( bIsArea && papoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested() ) + { + int nTagCount = 0; + + if( bNeedsToSaveWayInfo ) + { + if( !psWay->sInfo.bTimeStampIsStr ) + psWayFeaturePairs->sInfo.ts.nTimeStamp = + psWay->sInfo.ts.nTimeStamp; + else + { + OGRField sField; + if (OGRParseXMLDateTime(psWay->sInfo.ts.pszTimeStamp, &sField)) + { + struct tm brokendown; + brokendown.tm_year = sField.Date.Year - 1900; + brokendown.tm_mon = sField.Date.Month - 1; + brokendown.tm_mday = sField.Date.Day; + brokendown.tm_hour = sField.Date.Hour; + brokendown.tm_min = sField.Date.Minute; + brokendown.tm_sec = (int)(sField.Date.Second + .5); + psWayFeaturePairs->sInfo.ts.nTimeStamp = + CPLYMDHMSToUnixTime(&brokendown); + } + else + psWayFeaturePairs->sInfo.ts.nTimeStamp = 0; + } + psWayFeaturePairs->sInfo.nChangeset = psWay->sInfo.nChangeset; + psWayFeaturePairs->sInfo.nVersion = psWay->sInfo.nVersion; + psWayFeaturePairs->sInfo.nUID = psWay->sInfo.nUID; + psWayFeaturePairs->sInfo.bTimeStampIsStr = FALSE; + psWayFeaturePairs->sInfo.pszUserSID = ""; // FIXME + } + else + { + psWayFeaturePairs->sInfo.ts.nTimeStamp = 0; + psWayFeaturePairs->sInfo.nChangeset = 0; + psWayFeaturePairs->sInfo.nVersion = 0; + psWayFeaturePairs->sInfo.nUID = 0; + psWayFeaturePairs->sInfo.bTimeStampIsStr = FALSE; + psWayFeaturePairs->sInfo.pszUserSID = ""; + } + + psWayFeaturePairs->pasTags = pasAccumulatedTags + nAccumulatedTags; + + for(unsigned int iTag = 0; iTag < psWay->nTags; iTag++) + { + const char* pszK = psWay->pasTags[iTag].pszK; + const char* pszV = psWay->pasTags[iTag].pszV; + + if (strcmp(pszK, "area") == 0) + continue; + if (strcmp(pszK, "created_by") == 0) + continue; + if (strcmp(pszK, "converted_by") == 0) + continue; + if (strcmp(pszK, "note") == 0) + continue; + if (strcmp(pszK, "todo") == 0) + continue; + if (strcmp(pszK, "fixme") == 0) + continue; + if (strcmp(pszK, "FIXME") == 0) + continue; + + std::map::iterator oIterK = + aoMapIndexedKeys.find(pszK); + KeyDesc* psKD; + if (oIterK == aoMapIndexedKeys.end()) + { + if( nNextKeyIndex >= 32768 ) /* somewhat arbitrary */ + { + if( nNextKeyIndex == 32768 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too many different keys in file"); + nNextKeyIndex ++; /* to avoid next warnings */ + } + continue; + } + psKD = new KeyDesc(); + psKD->pszK = CPLStrdup(pszK); + psKD->nKeyIndex = nNextKeyIndex ++; + //CPLDebug("OSM", "nNextKeyIndex=%d", nNextKeyIndex); + psKD->nOccurences = 0; + psKD->asValues.push_back(CPLStrdup("")); + aoMapIndexedKeys[psKD->pszK] = psKD; + asKeys.push_back(psKD); + } + else + psKD = oIterK->second; + psKD->nOccurences ++; + + pasAccumulatedTags[nAccumulatedTags].nKeyIndex = (short)psKD->nKeyIndex; + + /* to fit in 2 bytes, the theoretical limit would be 127 * 128 + 127 */ + if( psKD->asValues.size() < 1024 ) + { + std::map::iterator oIterV; + oIterV = psKD->anMapV.find(pszV); + int nValueIndex; + if (oIterV == psKD->anMapV.end()) + { + char* pszVDup = CPLStrdup(pszV); + nValueIndex = (int)psKD->asValues.size(); + psKD->anMapV[pszVDup] = nValueIndex; + psKD->asValues.push_back(pszVDup); + } + else + nValueIndex = oIterV->second; + + pasAccumulatedTags[nAccumulatedTags].bVIsIndex = TRUE; + pasAccumulatedTags[nAccumulatedTags].u.nValueIndex = nValueIndex; + } + else + { + int nLenV = strlen(pszV) + 1; + + if( psKD->asValues.size() == 1024 ) + { + CPLDebug("OSM", "More than %d different values for tag %s", + 1024, pszK); + psKD->asValues.push_back(CPLStrdup("")); /* to avoid next warnings */ + } + + CPLAssert( nNonRedundantValuesLen + nLenV <= MAX_NON_REDUNDANT_VALUES ); + memcpy(pabyNonRedundantValues + nNonRedundantValuesLen, pszV, nLenV); + pasAccumulatedTags[nAccumulatedTags].bVIsIndex = FALSE; + pasAccumulatedTags[nAccumulatedTags].u.nOffsetInpabyNonRedundantValues = nNonRedundantValuesLen; + nNonRedundantValuesLen += nLenV; + } + nAccumulatedTags ++; + + nTagCount ++; + if( nTagCount == MAX_COUNT_FOR_TAGS_IN_WAY ) + break; + } + + psWayFeaturePairs->nTags = nTagCount; + } + else + { + psWayFeaturePairs->sInfo.ts.nTimeStamp = 0; + psWayFeaturePairs->sInfo.nChangeset = 0; + psWayFeaturePairs->sInfo.nVersion = 0; + psWayFeaturePairs->sInfo.nUID = 0; + psWayFeaturePairs->sInfo.bTimeStampIsStr = FALSE; + psWayFeaturePairs->sInfo.pszUserSID = ""; + + psWayFeaturePairs->nTags = 0; + psWayFeaturePairs->pasTags = NULL; + } + + nWayFeaturePairs++; + + memcpy(panUnsortedReqIds + nUnsortedReqIds, psWay->panNodeRefs, sizeof(GIntBig) * (psWay->nRefs - bIsArea)); + nUnsortedReqIds += (psWay->nRefs - bIsArea); +} + +static void OGROSMNotifyWay (OSMWay* psWay, + CPL_UNUSED OSMContext* psOSMContext, + void* user_data) +{ + ((OGROSMDataSource*) user_data)->NotifyWay(psWay); +} + +/************************************************************************/ +/* LookupWays() */ +/************************************************************************/ + +unsigned int OGROSMDataSource::LookupWays( std::map< GIntBig, std::pair >& aoMapWays, + OSMRelation* psRelation ) +{ + unsigned int nFound = 0; + unsigned int iCur = 0; + unsigned int i; + + while( iCur < psRelation->nMembers ) + { + unsigned int nToQuery = 0; + for(i=iCur;inMembers;i++) + { + if( psRelation->pasMembers[i].eType == MEMBER_WAY && + strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0 ) + { + nToQuery ++; + if( nToQuery == LIMIT_IDS_PER_REQUEST ) + break; + } + } + + if( nToQuery == 0) + break; + + unsigned int iLastI = (i == psRelation->nMembers) ? i : i + 1; + + sqlite3_stmt* hStmt = pahSelectWayStmt[nToQuery-1]; + unsigned int nBindIndex = 1; + for(i=iCur;ipasMembers[i].eType == MEMBER_WAY && + strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0 ) + { + sqlite3_bind_int64( hStmt, nBindIndex, + psRelation->pasMembers[i].nID ); + nBindIndex ++; + } + } + iCur = iLastI; + + while( sqlite3_step(hStmt) == SQLITE_ROW ) + { + GIntBig id = sqlite3_column_int64(hStmt, 0); + if( aoMapWays.find(id) == aoMapWays.end() ) + { + int nBlobSize = sqlite3_column_bytes(hStmt, 1); + const void* blob = sqlite3_column_blob(hStmt, 1); + void* blob_dup = CPLMalloc(nBlobSize); + memcpy(blob_dup, blob, nBlobSize); + aoMapWays[id] = std::pair(nBlobSize, blob_dup); + } + nFound++; + } + + sqlite3_reset(hStmt); + } + + return nFound; +} + +/************************************************************************/ +/* BuildMultiPolygon() */ +/************************************************************************/ + +OGRGeometry* OGROSMDataSource::BuildMultiPolygon(OSMRelation* psRelation, + unsigned int* pnTags, + OSMTag* pasTags) +{ + + std::map< GIntBig, std::pair > aoMapWays; + LookupWays( aoMapWays, psRelation ); + + int bMissing = FALSE; + unsigned int i; + for(i = 0; i < psRelation->nMembers; i ++ ) + { + if( psRelation->pasMembers[i].eType == MEMBER_WAY && + strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0 ) + { + if( aoMapWays.find( psRelation->pasMembers[i].nID ) == aoMapWays.end() ) + { + CPLDebug("OSM", "Relation " CPL_FRMT_GIB " has missing ways. Ignoring it", + psRelation->nID); + bMissing = TRUE; + break; + } + } + } + + OGRGeometry* poRet = NULL; + OGRMultiLineString* poMLS = NULL; + OGRGeometry** papoPolygons = NULL; + int nPolys = 0; + + if( bMissing ) + goto cleanup; + + poMLS = new OGRMultiLineString(); + papoPolygons = (OGRGeometry**) CPLMalloc( + sizeof(OGRGeometry*) * psRelation->nMembers); + nPolys = 0; + + if( pnTags != NULL ) + *pnTags = 0; + + for(i = 0; i < psRelation->nMembers; i ++ ) + { + if( psRelation->pasMembers[i].eType == MEMBER_WAY && + strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0 ) + { + const std::pair& oGeom = aoMapWays[ psRelation->pasMembers[i].nID ]; + + LonLat* pasCoords = (LonLat*) pasLonLatCache; + int nPoints; + + if( pnTags != NULL && *pnTags == 0 && + strcmp(psRelation->pasMembers[i].pszRole, "outer") == 0 ) + { + int nCompressedWaySize = oGeom.first; + GByte* pabyCompressedWay = (GByte*) oGeom.second; + + memcpy(pabyWayBuffer, pabyCompressedWay, nCompressedWaySize); + + nPoints = UncompressWay (nCompressedWaySize, pabyWayBuffer, + pasCoords, + pnTags, pasTags, NULL ); + } + else + { + nPoints = UncompressWay (oGeom.first, (GByte*) oGeom.second, pasCoords, + NULL, NULL, NULL); + } + + OGRLineString* poLS; + + if ( pasCoords[0].nLon == pasCoords[nPoints - 1].nLon && + pasCoords[0].nLat == pasCoords[nPoints - 1].nLat ) + { + OGRPolygon* poPoly = new OGRPolygon(); + OGRLinearRing* poRing = new OGRLinearRing(); + poPoly->addRingDirectly(poRing); + papoPolygons[nPolys ++] = poPoly; + poLS = poRing; + + if( strcmp(psRelation->pasMembers[i].pszRole, "outer") == 0 ) + { + sqlite3_bind_int64( hDeletePolygonsStandaloneStmt, 1, psRelation->pasMembers[i].nID ); + sqlite3_step( hDeletePolygonsStandaloneStmt ); + sqlite3_reset( hDeletePolygonsStandaloneStmt ); + } + } + else + { + poLS = new OGRLineString(); + poMLS->addGeometryDirectly(poLS); + } + + poLS->setNumPoints(nPoints); + for(int j=0;jsetPoint( j, + INT_TO_DBL(pasCoords[j].nLon), + INT_TO_DBL(pasCoords[j].nLat) ); + } + + } + } + + if( poMLS->getNumGeometries() > 0 ) + { + OGRGeometryH hPoly = OGRBuildPolygonFromEdges( (OGRGeometryH) poMLS, + TRUE, + FALSE, + 0, + NULL ); + if( hPoly != NULL && OGR_G_GetGeometryType(hPoly) == wkbPolygon ) + { + OGRPolygon* poSuperPoly = (OGRPolygon* ) hPoly; + for(i = 0; i < 1 + (unsigned int)poSuperPoly->getNumInteriorRings(); i++) + { + OGRPolygon* poPoly = new OGRPolygon(); + OGRLinearRing* poRing = (i == 0) ? poSuperPoly->getExteriorRing() : + poSuperPoly->getInteriorRing(i - 1); + if( poRing != NULL && poRing->getNumPoints() >= 4 && + poRing->getX(0) == poRing->getX(poRing->getNumPoints() -1) && + poRing->getY(0) == poRing->getY(poRing->getNumPoints() -1) ) + { + poPoly->addRing( poRing ); + papoPolygons[nPolys ++] = poPoly; + } + } + } + + OGR_G_DestroyGeometry(hPoly); + } + delete poMLS; + + if( nPolys > 0 ) + { + int bIsValidGeometry = FALSE; + const char* apszOptions[2] = { "METHOD=DEFAULT", NULL }; + OGRGeometry* poGeom = OGRGeometryFactory::organizePolygons( + papoPolygons, nPolys, &bIsValidGeometry, apszOptions ); + + if( poGeom != NULL && poGeom->getGeometryType() == wkbPolygon ) + { + OGRMultiPolygon* poMulti = new OGRMultiPolygon(); + poMulti->addGeometryDirectly(poGeom); + poGeom = poMulti; + } + + if( poGeom != NULL && poGeom->getGeometryType() == wkbMultiPolygon ) + { + poRet = poGeom; + } + else + { + CPLDebug("OSM", "Relation " CPL_FRMT_GIB ": Geometry has incompatible type : %s", + psRelation->nID, + poGeom != NULL ? OGR_G_GetGeometryName((OGRGeometryH)poGeom) : "null" ); + delete poGeom; + } + } + + CPLFree(papoPolygons); + +cleanup: + /* Cleanup */ + std::map< GIntBig, std::pair >::iterator oIter; + for( oIter = aoMapWays.begin(); oIter != aoMapWays.end(); ++oIter ) + CPLFree(oIter->second.second); + + return poRet; +} + +/************************************************************************/ +/* BuildGeometryCollection() */ +/************************************************************************/ + +OGRGeometry* OGROSMDataSource::BuildGeometryCollection(OSMRelation* psRelation, int bMultiLineString) +{ + std::map< GIntBig, std::pair > aoMapWays; + LookupWays( aoMapWays, psRelation ); + + unsigned int i; + + OGRGeometryCollection* poColl; + if( bMultiLineString ) + poColl = new OGRMultiLineString(); + else + poColl = new OGRGeometryCollection(); + + for(i = 0; i < psRelation->nMembers; i ++ ) + { + if( psRelation->pasMembers[i].eType == MEMBER_NODE && !bMultiLineString ) + { + nUnsortedReqIds = 1; + panUnsortedReqIds[0] = psRelation->pasMembers[i].nID; + LookupNodes(); + if( nReqIds == 1 ) + { + poColl->addGeometryDirectly(new OGRPoint( + INT_TO_DBL(pasLonLatArray[0].nLon), + INT_TO_DBL(pasLonLatArray[0].nLat))); + } + } + else if( psRelation->pasMembers[i].eType == MEMBER_WAY && + strcmp(psRelation->pasMembers[i].pszRole, "subarea") != 0 && + aoMapWays.find( psRelation->pasMembers[i].nID ) != aoMapWays.end() ) + { + const std::pair& oGeom = aoMapWays[ psRelation->pasMembers[i].nID ]; + + LonLat* pasCoords = (LonLat*) pasLonLatCache; + int nPoints = UncompressWay (oGeom.first, (GByte*) oGeom.second, pasCoords, NULL, NULL, NULL); + + OGRLineString* poLS; + + poLS = new OGRLineString(); + poColl->addGeometryDirectly(poLS); + + poLS->setNumPoints(nPoints); + for(int j=0;jsetPoint( j, + INT_TO_DBL(pasCoords[j].nLon), + INT_TO_DBL(pasCoords[j].nLat) ); + } + + } + } + + if( poColl->getNumGeometries() == 0 ) + { + delete poColl; + poColl = NULL; + } + + /* Cleanup */ + std::map< GIntBig, std::pair >::iterator oIter; + for( oIter = aoMapWays.begin(); oIter != aoMapWays.end(); ++oIter ) + CPLFree(oIter->second.second); + + return poColl; +} + +/************************************************************************/ +/* NotifyRelation() */ +/************************************************************************/ + +void OGROSMDataSource::NotifyRelation (OSMRelation* psRelation) +{ + unsigned int i; + + if( nWayFeaturePairs != 0 ) + ProcessWaysBatch(); + + nRelationsProcessed++; + if( (nRelationsProcessed % 10000) == 0 ) + { + CPLDebug("OSM", "Relations processed : %d", nRelationsProcessed); +#ifdef DEBUG_MEM_USAGE + CPLDebug("OSM", "GetMaxTotalAllocs() = " CPL_FRMT_GUIB, (GUIntBig)GetMaxTotalAllocs()); +#endif + } + + if( !bUseWaysIndex ) + return; + + int bMultiPolygon = FALSE; + int bMultiLineString = FALSE; + int bInterestingTagFound = FALSE; + const char* pszTypeV = NULL; + for(i = 0; i < psRelation->nTags; i ++ ) + { + const char* pszK = psRelation->pasTags[i].pszK; + if( strcmp(pszK, "type") == 0 ) + { + const char* pszV = psRelation->pasTags[i].pszV; + pszTypeV = pszV; + if( strcmp(pszV, "multipolygon") == 0 || + strcmp(pszV, "boundary") == 0) + { + bMultiPolygon = TRUE; + } + else if( strcmp(pszV, "multilinestring") == 0 || + strcmp(pszV, "route") == 0 ) + { + bMultiLineString = TRUE; + } + } + else if ( strcmp(pszK, "created_by") != 0 ) + bInterestingTagFound = TRUE; + } + + /* Optimization : if we have an attribute filter, that does not require geometry, */ + /* then we can just evaluate the attribute filter without the geometry */ + int iCurLayer = (bMultiPolygon) ? IDX_LYR_MULTIPOLYGONS : + (bMultiLineString) ? IDX_LYR_MULTILINESTRINGS : + IDX_LYR_OTHER_RELATIONS; + if( !papoLayers[iCurLayer]->IsUserInterested() ) + return; + + OGRFeature* poFeature = NULL; + + if( !(bMultiPolygon && !bInterestingTagFound) && /* we cannot do early filtering for multipolygon that has no interesting tag, since we may fetch attributes from ways */ + papoLayers[iCurLayer]->HasAttributeFilter() && + !papoLayers[iCurLayer]->AttributeFilterEvaluationNeedsGeometry() ) + { + poFeature = new OGRFeature(papoLayers[iCurLayer]->GetLayerDefn()); + + papoLayers[iCurLayer]->SetFieldsFromTags( poFeature, + psRelation->nID, + FALSE, + psRelation->nTags, + psRelation->pasTags, + &psRelation->sInfo); + + if( !papoLayers[iCurLayer]->EvaluateAttributeFilter(poFeature) ) + { + delete poFeature; + return; + } + } + + OGRGeometry* poGeom; + + unsigned int nExtraTags = 0; + OSMTag pasExtraTags[1 + MAX_COUNT_FOR_TAGS_IN_WAY]; + + if( bMultiPolygon ) + { + if( !bInterestingTagFound ) + { + poGeom = BuildMultiPolygon(psRelation, &nExtraTags, pasExtraTags); + CPLAssert(nExtraTags <= MAX_COUNT_FOR_TAGS_IN_WAY); + pasExtraTags[nExtraTags].pszK = "type"; + pasExtraTags[nExtraTags].pszV = pszTypeV; + nExtraTags ++; + } + else + poGeom = BuildMultiPolygon(psRelation, NULL, NULL); + } + else + poGeom = BuildGeometryCollection(psRelation, bMultiLineString); + + if( poGeom != NULL ) + { + int bAttrFilterAlreadyEvaluated; + if( poFeature == NULL ) + { + poFeature = new OGRFeature(papoLayers[iCurLayer]->GetLayerDefn()); + + papoLayers[iCurLayer]->SetFieldsFromTags( poFeature, + psRelation->nID, + FALSE, + nExtraTags ? nExtraTags : psRelation->nTags, + nExtraTags ? pasExtraTags : psRelation->pasTags, + &psRelation->sInfo); + + bAttrFilterAlreadyEvaluated = FALSE; + } + else + bAttrFilterAlreadyEvaluated = TRUE; + + poFeature->SetGeometryDirectly(poGeom); + + int bFilteredOut = FALSE; + if( !papoLayers[iCurLayer]->AddFeature( poFeature, + bAttrFilterAlreadyEvaluated, + &bFilteredOut, + !bFeatureAdded ) ) + bStopParsing = TRUE; + else if (!bFilteredOut) + bFeatureAdded = TRUE; + } + else + delete poFeature; +} + +static void OGROSMNotifyRelation (OSMRelation* psRelation, + CPL_UNUSED OSMContext* psOSMContext, + void* user_data) +{ + ((OGROSMDataSource*) user_data)->NotifyRelation(psRelation); +} + + +/************************************************************************/ +/* ProcessPolygonsStandalone() */ +/************************************************************************/ + +void OGROSMDataSource::ProcessPolygonsStandalone() +{ + unsigned int nTags = 0; + OSMTag pasTags[MAX_COUNT_FOR_TAGS_IN_WAY]; + OSMInfo sInfo; + int bFirst = TRUE; + + sInfo.ts.nTimeStamp = 0; + sInfo.nChangeset = 0; + sInfo.nVersion = 0; + sInfo.nUID = 0; + sInfo.bTimeStampIsStr = FALSE; + sInfo.pszUserSID = ""; + + if( !bHasRowInPolygonsStandalone ) + bHasRowInPolygonsStandalone = (sqlite3_step(hSelectPolygonsStandaloneStmt) == SQLITE_ROW); + + while( bHasRowInPolygonsStandalone && + papoLayers[IDX_LYR_MULTIPOLYGONS]->nFeatureArraySize < 10000 ) + { + if( bFirst ) + { + CPLDebug("OSM", "Remaining standalone polygons"); + bFirst = FALSE; + } + + GIntBig id = sqlite3_column_int64(hSelectPolygonsStandaloneStmt, 0); + + sqlite3_bind_int64( pahSelectWayStmt[0], 1, id ); + if( sqlite3_step(pahSelectWayStmt[0]) == SQLITE_ROW ) + { + int nBlobSize = sqlite3_column_bytes(pahSelectWayStmt[0], 1); + const void* blob = sqlite3_column_blob(pahSelectWayStmt[0], 1); + + LonLat* pasCoords = (LonLat*) pasLonLatCache; + + int nPoints = UncompressWay (nBlobSize, (GByte*) blob, + pasCoords, + &nTags, pasTags, &sInfo ); + CPLAssert(nTags <= MAX_COUNT_FOR_TAGS_IN_WAY); + + OGRLineString* poLS; + + OGRMultiPolygon* poMulti = new OGRMultiPolygon(); + OGRPolygon* poPoly = new OGRPolygon(); + OGRLinearRing* poRing = new OGRLinearRing(); + poMulti->addGeometryDirectly(poPoly); + poPoly->addRingDirectly(poRing); + poLS = poRing; + + poLS->setNumPoints(nPoints); + for(int j=0;jsetPoint( j, + INT_TO_DBL(pasCoords[j].nLon), + INT_TO_DBL(pasCoords[j].nLat) ); + } + + OGRFeature* poFeature = new OGRFeature(papoLayers[IDX_LYR_MULTIPOLYGONS]->GetLayerDefn()); + + papoLayers[IDX_LYR_MULTIPOLYGONS]->SetFieldsFromTags( poFeature, + id, + TRUE, + nTags, + pasTags, + &sInfo); + + poFeature->SetGeometryDirectly(poMulti); + + int bFilteredOut = FALSE; + if( !papoLayers[IDX_LYR_MULTIPOLYGONS]->AddFeature( poFeature, + FALSE, + &bFilteredOut, + !bFeatureAdded ) ) + { + bStopParsing = TRUE; + break; + } + else if (!bFilteredOut) + bFeatureAdded = TRUE; + + } + else { + CPLAssert(FALSE); + } + + sqlite3_reset(pahSelectWayStmt[0]); + + bHasRowInPolygonsStandalone = (sqlite3_step(hSelectPolygonsStandaloneStmt) == SQLITE_ROW); + } +} + +/************************************************************************/ +/* NotifyBounds() */ +/************************************************************************/ + +void OGROSMDataSource::NotifyBounds (double dfXMin, double dfYMin, + double dfXMax, double dfYMax) +{ + sExtent.MinX = dfXMin; + sExtent.MinY = dfYMin; + sExtent.MaxX = dfXMax; + sExtent.MaxY = dfYMax; + bExtentValid = TRUE; + + CPLDebug("OSM", "Got bounds : minx=%f, miny=%f, maxx=%f, maxy=%f", + dfXMin, dfYMin, dfXMax, dfYMax); +} + +static void OGROSMNotifyBounds( double dfXMin, double dfYMin, + double dfXMax, double dfYMax, + CPL_UNUSED OSMContext* psCtxt, + void* user_data ) +{ + ((OGROSMDataSource*) user_data)->NotifyBounds(dfXMin, dfYMin, + dfXMax, dfYMax); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGROSMDataSource::Open( const char * pszFilename, char** papszOpenOptions ) + +{ + pszName = CPLStrdup( pszFilename ); + + psParser = OSM_Open( pszName, + OGROSMNotifyNodes, + OGROSMNotifyWay, + OGROSMNotifyRelation, + OGROSMNotifyBounds, + this ); + if( psParser == NULL ) + return FALSE; + + if( CSLFetchBoolean(papszOpenOptions, "INTERLEAVED_READING", FALSE) ) + bInterleavedReading = TRUE; + + /* The following 4 config options are only useful for debugging */ + bIndexPoints = CSLTestBoolean(CPLGetConfigOption("OSM_INDEX_POINTS", "YES")); + bUsePointsIndex = CSLTestBoolean(CPLGetConfigOption("OSM_USE_POINTS_INDEX", "YES")); + bIndexWays = CSLTestBoolean(CPLGetConfigOption("OSM_INDEX_WAYS", "YES")); + bUseWaysIndex = CSLTestBoolean(CPLGetConfigOption("OSM_USE_WAYS_INDEX", "YES")); + + bCustomIndexing = CSLTestBoolean(CSLFetchNameValueDef( + papszOpenOptions, "USE_CUSTOM_INDEXING", + CPLGetConfigOption("OSM_USE_CUSTOM_INDEXING", "YES"))); + if( !bCustomIndexing ) + CPLDebug("OSM", "Using SQLite indexing for points"); + bCompressNodes = CSLTestBoolean(CSLFetchNameValueDef( + papszOpenOptions, "COMPRESS_NODES", + CPLGetConfigOption("OSM_COMPRESS_NODES", "NO"))); + if( bCompressNodes ) + CPLDebug("OSM", "Using compression for nodes DB"); + + nLayers = 5; + papoLayers = (OGROSMLayer**) CPLMalloc(nLayers * sizeof(OGROSMLayer*)); + + papoLayers[IDX_LYR_POINTS] = new OGROSMLayer(this, IDX_LYR_POINTS, "points"); + papoLayers[IDX_LYR_POINTS]->GetLayerDefn()->SetGeomType(wkbPoint); + + papoLayers[IDX_LYR_LINES] = new OGROSMLayer(this, IDX_LYR_LINES, "lines"); + papoLayers[IDX_LYR_LINES]->GetLayerDefn()->SetGeomType(wkbLineString); + + papoLayers[IDX_LYR_MULTILINESTRINGS] = new OGROSMLayer(this, IDX_LYR_MULTILINESTRINGS, "multilinestrings"); + papoLayers[IDX_LYR_MULTILINESTRINGS]->GetLayerDefn()->SetGeomType(wkbMultiLineString); + + papoLayers[IDX_LYR_MULTIPOLYGONS] = new OGROSMLayer(this, IDX_LYR_MULTIPOLYGONS, "multipolygons"); + papoLayers[IDX_LYR_MULTIPOLYGONS]->GetLayerDefn()->SetGeomType(wkbMultiPolygon); + + papoLayers[IDX_LYR_OTHER_RELATIONS] = new OGROSMLayer(this, IDX_LYR_OTHER_RELATIONS, "other_relations"); + papoLayers[IDX_LYR_OTHER_RELATIONS]->GetLayerDefn()->SetGeomType(wkbGeometryCollection); + + if( !ParseConf(papszOpenOptions) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Could not parse configuration file for OSM import"); + return FALSE; + } + + bNeedsToSaveWayInfo = + ( papoLayers[IDX_LYR_MULTIPOLYGONS]->HasTimestamp() || + papoLayers[IDX_LYR_MULTIPOLYGONS]->HasChangeset() || + papoLayers[IDX_LYR_MULTIPOLYGONS]->HasVersion() || + papoLayers[IDX_LYR_MULTIPOLYGONS]->HasUID() || + papoLayers[IDX_LYR_MULTIPOLYGONS]->HasUser() ); + + pasLonLatCache = (LonLat*)VSIMalloc(MAX_NODES_PER_WAY * sizeof(LonLat)); + pabyWayBuffer = (GByte*)VSIMalloc(WAY_BUFFER_SIZE); + + panReqIds = (GIntBig*)VSIMalloc(MAX_ACCUMULATED_NODES * sizeof(GIntBig)); +#ifdef ENABLE_NODE_LOOKUP_BY_HASHING + panHashedIndexes = (int*)VSIMalloc(HASHED_INDEXES_ARRAY_SIZE * sizeof(int)); + psCollisionBuckets = (CollisionBucket*)VSIMalloc(COLLISION_BUCKET_ARRAY_SIZE * sizeof(CollisionBucket)); +#endif + pasLonLatArray = (LonLat*)VSIMalloc(MAX_ACCUMULATED_NODES * sizeof(LonLat)); + panUnsortedReqIds = (GIntBig*)VSIMalloc(MAX_ACCUMULATED_NODES * sizeof(GIntBig)); + pasWayFeaturePairs = (WayFeaturePair*)VSIMalloc(MAX_DELAYED_FEATURES * sizeof(WayFeaturePair)); + pasAccumulatedTags = (IndexedKVP*) VSIMalloc(MAX_ACCUMULATED_TAGS * sizeof(IndexedKVP)); + pabyNonRedundantValues = (GByte*) VSIMalloc(MAX_NON_REDUNDANT_VALUES); + + if( pasLonLatCache == NULL || + pabyWayBuffer == NULL || + panReqIds == NULL || + pasLonLatArray == NULL || + panUnsortedReqIds == NULL || + pasWayFeaturePairs == NULL || + pasAccumulatedTags == NULL || + pabyNonRedundantValues == NULL ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Out-of-memory when allocating one of the buffer used for the processing."); + return FALSE; + } + + nMaxSizeForInMemoryDBInMB = atoi(CSLFetchNameValueDef(papszOpenOptions, + "MAX_TMPFILE_SIZE", CPLGetConfigOption("OSM_MAX_TMPFILE_SIZE", "100"))); + GIntBig nSize = (GIntBig)nMaxSizeForInMemoryDBInMB * 1024 * 1024; + if (nSize < 0 || (GIntBig)(size_t)nSize != nSize) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for OSM_MAX_TMPFILE_SIZE. Using 100 instead."); + nMaxSizeForInMemoryDBInMB = 100; + nSize = (GIntBig)nMaxSizeForInMemoryDBInMB * 1024 * 1024; + } + + if( bCustomIndexing ) + { + pabySector = (GByte*) VSICalloc(1, SECTOR_SIZE); + + if( pabySector == NULL || !AllocMoreBuckets(INIT_BUCKET_COUNT) ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Out-of-memory when allocating one of the buffer used for the processing."); + return FALSE; + } + + bInMemoryNodesFile = TRUE; + osNodesFilename.Printf("/vsimem/osm_importer/osm_temp_nodes_%p", this); + fpNodes = VSIFOpenL(osNodesFilename, "wb+"); + if( fpNodes == NULL ) + { + return FALSE; + } + + CPLPushErrorHandler(CPLQuietErrorHandler); + int bSuccess = VSIFSeekL(fpNodes, (vsi_l_offset) (nSize * 3 / 4), SEEK_SET) == 0; + CPLPopErrorHandler(); + + if( bSuccess ) + { + VSIFSeekL(fpNodes, 0, SEEK_SET); + VSIFTruncateL(fpNodes, 0); + } + else + { + CPLDebug("OSM", "Not enough memory for in-memory file. Using disk temporary file instead."); + + VSIFCloseL(fpNodes); + fpNodes = NULL; + VSIUnlink(osNodesFilename); + + bInMemoryNodesFile = FALSE; + osNodesFilename = CPLGenerateTempFilename("osm_tmp_nodes"); + + fpNodes = VSIFOpenL(osNodesFilename, "wb+"); + if( fpNodes == NULL ) + { + return FALSE; + } + + /* On Unix filesystems, you can remove a file even if it */ + /* opened */ + const char* pszVal = CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES"); + if( EQUAL(pszVal, "YES") ) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + bMustUnlinkNodesFile = VSIUnlink( osNodesFilename ) != 0; + CPLPopErrorHandler(); + } + + return FALSE; + } + } + + int bRet = CreateTempDB(); + if( bRet ) + { + CPLString osInterestLayers = GetInterestLayersForDSName(GetName()); + if( osInterestLayers.size() ) + { + ExecuteSQL( osInterestLayers, NULL, NULL ); + } + } + return bRet; +} + +/************************************************************************/ +/* CreateTempDB() */ +/************************************************************************/ + +int OGROSMDataSource::CreateTempDB() +{ + char* pszErrMsg = NULL; + + int rc = 0; + int bIsExisting = FALSE; + int bSuccess = FALSE; + +#ifdef HAVE_SQLITE_VFS + const char* pszExistingTmpFile = CPLGetConfigOption("OSM_EXISTING_TMPFILE", NULL); + if ( pszExistingTmpFile != NULL ) + { + bSuccess = TRUE; + bIsExisting = TRUE; + rc = sqlite3_open_v2( pszExistingTmpFile, &hDB, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX, + NULL ); + } + else + { + osTmpDBName.Printf("/vsimem/osm_importer/osm_temp_%p.sqlite", this); + + /* On 32 bit, the virtual memory space is scarce, so we need to reserve it right now */ + /* Won't hurt on 64 bit either. */ + VSILFILE* fp = VSIFOpenL(osTmpDBName, "wb"); + if( fp ) + { + GIntBig nSize = (GIntBig)nMaxSizeForInMemoryDBInMB * 1024 * 1024; + if( bCustomIndexing && bInMemoryNodesFile ) + nSize = nSize * 1 / 4; + + CPLPushErrorHandler(CPLQuietErrorHandler); + bSuccess = VSIFSeekL(fp, (vsi_l_offset) nSize, SEEK_SET) == 0; + CPLPopErrorHandler(); + + if( bSuccess ) + VSIFTruncateL(fp, 0); + + VSIFCloseL(fp); + + if( !bSuccess ) + { + CPLDebug("OSM", "Not enough memory for in-memory file. Using disk temporary file instead."); + VSIUnlink(osTmpDBName); + } + } + + if( bSuccess ) + { + bInMemoryTmpDB = TRUE; + pMyVFS = OGRSQLiteCreateVFS(NULL, this); + sqlite3_vfs_register(pMyVFS, 0); + rc = sqlite3_open_v2( osTmpDBName.c_str(), &hDB, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX, + pMyVFS->zName ); + } + } +#endif + + if( !bSuccess ) + { + osTmpDBName = CPLGenerateTempFilename("osm_tmp"); + rc = sqlite3_open( osTmpDBName.c_str(), &hDB ); + + /* On Unix filesystems, you can remove a file even if it */ + /* opened */ + if( rc == SQLITE_OK ) + { + const char* pszVal = CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES"); + if( EQUAL(pszVal, "YES") ) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + bMustUnlink = VSIUnlink( osTmpDBName ) != 0; + CPLPopErrorHandler(); + } + } + } + + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "sqlite3_open(%s) failed: %s", + osTmpDBName.c_str(), sqlite3_errmsg( hDB ) ); + return FALSE; + } + + if( !SetDBOptions() ) + { + return FALSE; + } + + if( !bIsExisting ) + { + rc = sqlite3_exec( hDB, + "CREATE TABLE nodes (id INTEGER PRIMARY KEY, coords BLOB)", + NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create table nodes : %s", pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + + rc = sqlite3_exec( hDB, + "CREATE TABLE ways (id INTEGER PRIMARY KEY, data BLOB)", + NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create table ways : %s", pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + + rc = sqlite3_exec( hDB, + "CREATE TABLE polygons_standalone (id INTEGER PRIMARY KEY)", + NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create table polygons_standalone : %s", pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + } + + return CreatePreparedStatements(); +} +/************************************************************************/ +/* SetDBOptions() */ +/************************************************************************/ + +int OGROSMDataSource::SetDBOptions() +{ + char* pszErrMsg = NULL; + int rc; + + rc = sqlite3_exec( hDB, "PRAGMA synchronous = OFF", NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to run PRAGMA synchronous : %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + + rc = sqlite3_exec( hDB, "PRAGMA journal_mode = OFF", NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to run PRAGMA journal_mode : %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + + rc = sqlite3_exec( hDB, "PRAGMA temp_store = MEMORY", NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to run PRAGMA temp_store : %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + + if( !SetCacheSize() ) + return FALSE; + + if( !StartTransactionCacheDB() ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* SetCacheSize() */ +/************************************************************************/ + +int OGROSMDataSource::SetCacheSize() +{ + int rc; + const char* pszSqliteCacheMB = CPLGetConfigOption("OSM_SQLITE_CACHE", NULL); + if (pszSqliteCacheMB != NULL) + { + char* pszErrMsg = NULL; + char **papszResult; + int nRowCount, nColCount; + int iSqliteCachePages; + int iSqlitePageSize = -1; + int iSqliteCacheBytes = atoi( pszSqliteCacheMB ) * 1024 * 1024; + + /* querying the current PageSize */ + rc = sqlite3_get_table( hDB, "PRAGMA page_size", + &papszResult, &nRowCount, &nColCount, + &pszErrMsg ); + if( rc == SQLITE_OK ) + { + int iRow; + for (iRow = 1; iRow <= nRowCount; iRow++) + { + iSqlitePageSize = atoi( papszResult[(iRow * nColCount) + 0] ); + } + sqlite3_free_table(papszResult); + } + if( iSqlitePageSize < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to run PRAGMA page_size : %s", + pszErrMsg ? pszErrMsg : sqlite3_errmsg(hDB) ); + sqlite3_free( pszErrMsg ); + return TRUE; + } + + /* computing the CacheSize as #Pages */ + iSqliteCachePages = iSqliteCacheBytes / iSqlitePageSize; + if( iSqliteCachePages <= 0) + return TRUE; + + rc = sqlite3_exec( hDB, CPLSPrintf( "PRAGMA cache_size = %d", + iSqliteCachePages ), + NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unrecognized value for PRAGMA cache_size : %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + rc = SQLITE_OK; + } + } + return TRUE; +} + +/************************************************************************/ +/* CreatePreparedStatements() */ +/************************************************************************/ + +int OGROSMDataSource::CreatePreparedStatements() +{ + int rc; + + rc = sqlite3_prepare( hDB, "INSERT INTO nodes (id, coords) VALUES (?,?)", -1, + &hInsertNodeStmt, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "sqlite3_prepare() failed : %s", sqlite3_errmsg(hDB) ); + return FALSE; + } + + pahSelectNodeStmt = (sqlite3_stmt**) CPLCalloc(sizeof(sqlite3_stmt*), LIMIT_IDS_PER_REQUEST); + + char szTmp[LIMIT_IDS_PER_REQUEST*2 + 128]; + strcpy(szTmp, "SELECT id, coords FROM nodes WHERE id IN ("); + int nLen = strlen(szTmp); + for(int i=0;i& oAttributes) +{ + for(size_t i=0; iAddComputedAttribute(oAttributes[i].osName, + oAttributes[i].eType, + oAttributes[i].osSQL); + } + } +} + +/************************************************************************/ +/* ParseConf() */ +/************************************************************************/ + +int OGROSMDataSource::ParseConf(char** papszOpenOptions) +{ + const char *pszFilename = + CSLFetchNameValueDef(papszOpenOptions, "CONFIG_FILE", + CPLGetConfigOption("OSM_CONFIG_FILE", NULL)); + if( pszFilename == NULL ) + pszFilename = CPLFindFile( "gdal", "osmconf.ini" ); + if( pszFilename == NULL ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Cannot find osmconf.ini configuration file"); + return FALSE; + } + + VSILFILE* fpConf = VSIFOpenL(pszFilename, "rb"); + if( fpConf == NULL ) + return FALSE; + + const char* pszLine; + int iCurLayer = -1; + std::vector oAttributes; + + int i; + + while((pszLine = CPLReadLine2L(fpConf, -1, NULL)) != NULL) + { + if(pszLine[0] == '#') + continue; + if(pszLine[0] == '[' && pszLine[strlen(pszLine)-1] == ']' ) + { + if( iCurLayer >= 0 ) + AddComputedAttributes(iCurLayer, oAttributes); + oAttributes.resize(0); + + iCurLayer = -1; + pszLine ++; + ((char*)pszLine)[strlen(pszLine)-1] = '\0'; /* Evil but OK */ + for(i = 0; i < nLayers; i++) + { + if( strcmp(pszLine, papoLayers[i]->GetName()) == 0 ) + { + iCurLayer = i; + break; + } + } + if( iCurLayer < 0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Layer '%s' mentionned in %s is unknown to the driver", + pszLine, pszFilename); + } + continue; + } + + if( strncmp(pszLine, "closed_ways_are_polygons=", + strlen("closed_ways_are_polygons=")) == 0) + { + char** papszTokens = CSLTokenizeString2(pszLine, "=", 0); + if( CSLCount(papszTokens) == 2) + { + char** papszTokens2 = CSLTokenizeString2(papszTokens[1], ",", 0); + for(int i=0;papszTokens2[i] != NULL;i++) + { + aoSetClosedWaysArePolygons.insert(papszTokens2[i]); + } + CSLDestroy(papszTokens2); + } + CSLDestroy(papszTokens); + } + + else if(strncmp(pszLine, "report_all_nodes=", strlen("report_all_nodes=")) == 0) + { + if( strcmp(pszLine + strlen("report_all_nodes="), "no") == 0 ) + { + bReportAllNodes = FALSE; + } + else if( strcmp(pszLine + strlen("report_all_nodes="), "yes") == 0 ) + { + bReportAllNodes = TRUE; + } + } + + else if(strncmp(pszLine, "report_all_ways=", strlen("report_all_ways=")) == 0) + { + if( strcmp(pszLine + strlen("report_all_ways="), "no") == 0 ) + { + bReportAllWays = FALSE; + } + else if( strcmp(pszLine + strlen("report_all_ways="), "yes") == 0 ) + { + bReportAllWays = TRUE; + } + } + + else if(strncmp(pszLine, "attribute_name_laundering=", strlen("attribute_name_laundering=")) == 0) + { + if( strcmp(pszLine + strlen("attribute_name_laundering="), "no") == 0 ) + { + bAttributeNameLaundering = FALSE; + } + else if( strcmp(pszLine + strlen("attribute_name_laundering="), "yes") == 0 ) + { + bAttributeNameLaundering = TRUE; + } + } + + else if( iCurLayer >= 0 ) + { + char** papszTokens = CSLTokenizeString2(pszLine, "=", 0); + if( CSLCount(papszTokens) == 2 && strcmp(papszTokens[0], "other_tags") == 0 ) + { + if( strcmp(papszTokens[1], "no") == 0 ) + papoLayers[iCurLayer]->SetHasOtherTags(FALSE); + else if( strcmp(papszTokens[1], "yes") == 0 ) + papoLayers[iCurLayer]->SetHasOtherTags(TRUE); + } + else if( CSLCount(papszTokens) == 2 && strcmp(papszTokens[0], "all_tags") == 0 ) + { + if( strcmp(papszTokens[1], "no") == 0 ) + papoLayers[iCurLayer]->SetHasAllTags(FALSE); + else if( strcmp(papszTokens[1], "yes") == 0 ) + papoLayers[iCurLayer]->SetHasAllTags(TRUE); + } + else if( CSLCount(papszTokens) == 2 && strcmp(papszTokens[0], "osm_id") == 0 ) + { + if( strcmp(papszTokens[1], "no") == 0 ) + papoLayers[iCurLayer]->SetHasOSMId(FALSE); + else if( strcmp(papszTokens[1], "yes") == 0 ) + { + papoLayers[iCurLayer]->SetHasOSMId(TRUE); + papoLayers[iCurLayer]->AddField("osm_id", OFTString); + + if( iCurLayer == IDX_LYR_MULTIPOLYGONS ) + papoLayers[iCurLayer]->AddField("osm_way_id", OFTString); + } + } + else if( CSLCount(papszTokens) == 2 && strcmp(papszTokens[0], "osm_version") == 0 ) + { + if( strcmp(papszTokens[1], "no") == 0 ) + papoLayers[iCurLayer]->SetHasVersion(FALSE); + else if( strcmp(papszTokens[1], "yes") == 0 ) + { + papoLayers[iCurLayer]->SetHasVersion(TRUE); + papoLayers[iCurLayer]->AddField("osm_version", OFTInteger); + } + } + else if( CSLCount(papszTokens) == 2 && strcmp(papszTokens[0], "osm_timestamp") == 0 ) + { + if( strcmp(papszTokens[1], "no") == 0 ) + papoLayers[iCurLayer]->SetHasTimestamp(FALSE); + else if( strcmp(papszTokens[1], "yes") == 0 ) + { + papoLayers[iCurLayer]->SetHasTimestamp(TRUE); + papoLayers[iCurLayer]->AddField("osm_timestamp", OFTDateTime); + } + } + else if( CSLCount(papszTokens) == 2 && strcmp(papszTokens[0], "osm_uid") == 0 ) + { + if( strcmp(papszTokens[1], "no") == 0 ) + papoLayers[iCurLayer]->SetHasUID(FALSE); + else if( strcmp(papszTokens[1], "yes") == 0 ) + { + papoLayers[iCurLayer]->SetHasUID(TRUE); + papoLayers[iCurLayer]->AddField("osm_uid", OFTInteger); + } + } + else if( CSLCount(papszTokens) == 2 && strcmp(papszTokens[0], "osm_user") == 0 ) + { + if( strcmp(papszTokens[1], "no") == 0 ) + papoLayers[iCurLayer]->SetHasUser(FALSE); + else if( strcmp(papszTokens[1], "yes") == 0 ) + { + papoLayers[iCurLayer]->SetHasUser(TRUE); + papoLayers[iCurLayer]->AddField("osm_user", OFTString); + } + } + else if( CSLCount(papszTokens) == 2 && strcmp(papszTokens[0], "osm_changeset") == 0 ) + { + if( strcmp(papszTokens[1], "no") == 0 ) + papoLayers[iCurLayer]->SetHasChangeset(FALSE); + else if( strcmp(papszTokens[1], "yes") == 0 ) + { + papoLayers[iCurLayer]->SetHasChangeset(TRUE); + papoLayers[iCurLayer]->AddField("osm_changeset", OFTInteger); + } + } + else if( CSLCount(papszTokens) == 2 && strcmp(papszTokens[0], "attributes") == 0 ) + { + char** papszTokens2 = CSLTokenizeString2(papszTokens[1], ",", 0); + for(int i=0;papszTokens2[i] != NULL;i++) + { + papoLayers[iCurLayer]->AddField(papszTokens2[i], OFTString); + } + CSLDestroy(papszTokens2); + } + else if ( CSLCount(papszTokens) == 2 && strcmp(papszTokens[0], "unsignificant") == 0 ) + { + char** papszTokens2 = CSLTokenizeString2(papszTokens[1], ",", 0); + for(int i=0;papszTokens2[i] != NULL;i++) + { + papoLayers[iCurLayer]->AddUnsignificantKey(papszTokens2[i]); + } + CSLDestroy(papszTokens2); + } + else if ( CSLCount(papszTokens) == 2 && strcmp(papszTokens[0], "ignore") == 0 ) + { + char** papszTokens2 = CSLTokenizeString2(papszTokens[1], ",", 0); + for(int i=0;papszTokens2[i] != NULL;i++) + { + papoLayers[iCurLayer]->AddIgnoreKey(papszTokens2[i]); + papoLayers[iCurLayer]->AddWarnKey(papszTokens2[i]); + } + CSLDestroy(papszTokens2); + } + else if ( CSLCount(papszTokens) == 2 && strcmp(papszTokens[0], "computed_attributes") == 0 ) + { + char** papszTokens2 = CSLTokenizeString2(papszTokens[1], ",", 0); + oAttributes.resize(0); + for(int i=0;papszTokens2[i] != NULL;i++) + { + oAttributes.push_back(OGROSMComputedAttribute(papszTokens2[i])); + } + CSLDestroy(papszTokens2); + } + else if ( CSLCount(papszTokens) == 2 && strlen(papszTokens[0]) >= 5 && + strcmp(papszTokens[0] + strlen(papszTokens[0]) - 5, "_type") == 0 ) + { + CPLString osName(papszTokens[0]); + osName.resize(strlen(papszTokens[0]) - 5); + const char* pszType = papszTokens[1]; + int bFound = FALSE; + OGRFieldType eType = OFTString; + if( EQUAL(pszType, "Integer") ) + eType = OFTInteger; + else if( EQUAL(pszType, "Integer64") ) + eType = OFTInteger64; + else if( EQUAL(pszType, "Real") ) + eType = OFTReal; + else if( EQUAL(pszType, "String") ) + eType = OFTString; + else if( EQUAL(pszType, "DateTime") ) + eType = OFTDateTime; + else + CPLError(CE_Warning, CPLE_AppDefined, + "Unhandled type (%s) for attribute %s", + pszType, osName.c_str()); + for(size_t i = 0; i < oAttributes.size(); i++ ) + { + if( oAttributes[i].osName == osName ) + { + bFound = TRUE; + oAttributes[i].eType = eType; + break; + } + } + if( !bFound ) + { + int idx = papoLayers[iCurLayer]->GetLayerDefn()->GetFieldIndex(osName); + if( idx >= 0 ) + { + papoLayers[iCurLayer]->GetLayerDefn()->GetFieldDefn(idx)->SetType(eType); + bFound = TRUE; + } + } + if( !bFound ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Undeclared attribute : %s", + osName.c_str()); + } + } + else if ( CSLCount(papszTokens) >= 2 && strlen(papszTokens[0]) >= 4 && + strcmp(papszTokens[0] + strlen(papszTokens[0]) - 4, "_sql") == 0 ) + { + CPLString osName(papszTokens[0]); + osName.resize(strlen(papszTokens[0]) - 4); + size_t i; + for(i = 0; i < oAttributes.size(); i++ ) + { + if( oAttributes[i].osName == osName ) + { + const char* pszSQL = strchr(pszLine, '=') + 1; + while( *pszSQL == ' ' ) + pszSQL ++; + int bInQuotes = FALSE; + if( *pszSQL == '"' ) + { + bInQuotes = TRUE; + pszSQL ++; + } + oAttributes[i].osSQL = pszSQL; + if( bInQuotes && oAttributes[i].osSQL.size() > 1 && + oAttributes[i].osSQL[oAttributes[i].osSQL.size()-1] == '"' ) + oAttributes[i].osSQL.resize(oAttributes[i].osSQL.size()-1); + break; + } + } + if( i == oAttributes.size() ) + { + CPLError(CE_Warning, CPLE_AppDefined, "Undeclared attribute : %s", + osName.c_str()); + } + } + CSLDestroy(papszTokens); + } + } + + if( iCurLayer >= 0 ) + AddComputedAttributes(iCurLayer, oAttributes); + + for(i=0;iHasAllTags() ) + { + papoLayers[i]->AddField("all_tags", OFTString); + if( papoLayers[i]->HasOtherTags() ) + { + papoLayers[i]->SetHasOtherTags(FALSE); + } + } + else if( papoLayers[i]->HasOtherTags() ) + papoLayers[i]->AddField("other_tags", OFTString); + } + + VSIFCloseL(fpConf); + + return TRUE; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +int OGROSMDataSource::ResetReading() +{ + if( hDB == NULL ) + return FALSE; + if( bCustomIndexing && fpNodes == NULL ) + return FALSE; + + OSM_ResetReading(psParser); + + char* pszErrMsg = NULL; + int rc = sqlite3_exec( hDB, "DELETE FROM nodes", NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to DELETE FROM nodes : %s", pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + + rc = sqlite3_exec( hDB, "DELETE FROM ways", NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to DELETE FROM ways : %s", pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + + rc = sqlite3_exec( hDB, "DELETE FROM polygons_standalone", NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to DELETE FROM polygons_standalone : %s", pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + bHasRowInPolygonsStandalone = FALSE; + + { + int i; + for( i = 0; i < nWayFeaturePairs; i++) + { + delete pasWayFeaturePairs[i].poFeature; + } + nWayFeaturePairs = 0; + nUnsortedReqIds = 0; + nReqIds = 0; + nAccumulatedTags = 0; + nNonRedundantValuesLen = 0; + + for(i=0;i<(int)asKeys.size();i++) + { + KeyDesc* psKD = asKeys[i]; + CPLFree(psKD->pszK); + for(int j=0;j<(int)psKD->asValues.size();j++) + CPLFree(psKD->asValues[j]); + delete psKD; + } + asKeys.resize(0); + aoMapIndexedKeys.clear(); + nNextKeyIndex = 0; + } + + if( bCustomIndexing ) + { + nPrevNodeId = -1; + nBucketOld = -1; + nOffInBucketReducedOld = -1; + + VSIFSeekL(fpNodes, 0, SEEK_SET); + VSIFTruncateL(fpNodes, 0); + nNodesFileSize = 0; + + memset(pabySector, 0, SECTOR_SIZE); + for(int i = 0; i < nBuckets; i++) + { + papsBuckets[i].nOff = -1; + if( bCompressNodes ) + { + if( papsBuckets[i].u.panSectorSize ) + memset(papsBuckets[i].u.panSectorSize, 0, BUCKET_SECTOR_SIZE_ARRAY_SIZE); + } + else + { + if( papsBuckets[i].u.pabyBitmap ) + memset(papsBuckets[i].u.pabyBitmap, 0, BUCKET_BITMAP_SIZE); + } + } + } + + for(int i=0;iForceResetReading(); + } + + bStopParsing = FALSE; + + return TRUE; +} + +/************************************************************************/ +/* ParseNextChunk() */ +/************************************************************************/ + +int OGROSMDataSource::ParseNextChunk(int nIdxLayer) +{ + if( bStopParsing ) + return FALSE; + + bHasParsedFirstChunk = TRUE; + bFeatureAdded = FALSE; + while( TRUE ) + { +#ifdef DEBUG_MEM_USAGE + static int counter = 0; + counter ++; + if ((counter % 1000) == 0) + CPLDebug("OSM", "GetMaxTotalAllocs() = " CPL_FRMT_GUIB, (GUIntBig)GetMaxTotalAllocs()); +#endif + + OSMRetCode eRet = OSM_ProcessBlock(psParser); + if( eRet == OSM_EOF || eRet == OSM_ERROR ) + { + if( eRet == OSM_EOF ) + { + if( nWayFeaturePairs != 0 ) + ProcessWaysBatch(); + + ProcessPolygonsStandalone(); + + if( !bHasRowInPolygonsStandalone ) + bStopParsing = TRUE; + + if( !bInterleavedReading && !bFeatureAdded && + bHasRowInPolygonsStandalone && + nIdxLayer != IDX_LYR_MULTIPOLYGONS ) + { + return FALSE; + } + + return bFeatureAdded || bHasRowInPolygonsStandalone; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "An error occured during the parsing of data around byte " CPL_FRMT_GUIB, + OSM_GetBytesRead(psParser)); + + bStopParsing = TRUE; + return FALSE; + } + } + else + { + if( bInMemoryTmpDB ) + { + if( !TransferToDiskIfNecesserary() ) + return FALSE; + } + + if( bFeatureAdded ) + break; + } + } + + return TRUE; +} + +/************************************************************************/ +/* TransferToDiskIfNecesserary() */ +/************************************************************************/ + +int OGROSMDataSource::TransferToDiskIfNecesserary() +{ + if( bInMemoryNodesFile ) + { + if( nNodesFileSize / 1024 / 1024 > 3 * nMaxSizeForInMemoryDBInMB / 4 ) + { + bInMemoryNodesFile = FALSE; + + VSIFCloseL(fpNodes); + fpNodes = NULL; + + CPLString osNewTmpDBName; + osNewTmpDBName = CPLGenerateTempFilename("osm_tmp_nodes"); + + CPLDebug("OSM", "%s too big for RAM. Transferring it onto disk in %s", + osNodesFilename.c_str(), osNewTmpDBName.c_str()); + + if( CPLCopyFile( osNewTmpDBName, osNodesFilename ) != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot copy %s to %s", + osNodesFilename.c_str(), osNewTmpDBName.c_str() ); + VSIUnlink(osNewTmpDBName); + bStopParsing = TRUE; + return FALSE; + } + + VSIUnlink(osNodesFilename); + + if( bInMemoryTmpDB ) + { + /* Try to grow the sqlite in memory-db to the full space now */ + /* it has been freed. */ + VSILFILE* fp = VSIFOpenL(osTmpDBName, "rb+"); + if( fp ) + { + VSIFSeekL(fp, 0, SEEK_END); + vsi_l_offset nCurSize = VSIFTellL(fp); + GIntBig nNewSize = ((GIntBig)nMaxSizeForInMemoryDBInMB) * 1024 * 1024; + CPLPushErrorHandler(CPLQuietErrorHandler); + int bSuccess = VSIFSeekL(fp, (vsi_l_offset) nNewSize, SEEK_SET) == 0; + CPLPopErrorHandler(); + + if( bSuccess ) + VSIFTruncateL(fp, nCurSize); + + VSIFCloseL(fp); + } + } + + osNodesFilename = osNewTmpDBName; + + fpNodes = VSIFOpenL(osNodesFilename, "rb+"); + if( fpNodes == NULL ) + { + bStopParsing = TRUE; + return FALSE; + } + + VSIFSeekL(fpNodes, 0, SEEK_END); + + /* On Unix filesystems, you can remove a file even if it */ + /* opened */ + const char* pszVal = CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES"); + if( EQUAL(pszVal, "YES") ) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + bMustUnlinkNodesFile = VSIUnlink( osNodesFilename ) != 0; + CPLPopErrorHandler(); + } + } + } + + if( bInMemoryTmpDB ) + { + VSIStatBufL sStat; + + int nLimitMB = nMaxSizeForInMemoryDBInMB; + if( bCustomIndexing && bInMemoryNodesFile ) + nLimitMB = nLimitMB * 1 / 4; + + if( VSIStatL( osTmpDBName, &sStat ) == 0 && + sStat.st_size / 1024 / 1024 > nLimitMB ) + { + bInMemoryTmpDB = FALSE; + + CloseDB(); + + CPLString osNewTmpDBName; + int rc; + + osNewTmpDBName = CPLGenerateTempFilename("osm_tmp"); + + CPLDebug("OSM", "%s too big for RAM. Transferring it onto disk in %s", + osTmpDBName.c_str(), osNewTmpDBName.c_str()); + + if( CPLCopyFile( osNewTmpDBName, osTmpDBName ) != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot copy %s to %s", + osTmpDBName.c_str(), osNewTmpDBName.c_str() ); + VSIUnlink(osNewTmpDBName); + bStopParsing = TRUE; + return FALSE; + } + + VSIUnlink(osTmpDBName); + + osTmpDBName = osNewTmpDBName; + +#ifdef HAVE_SQLITE_VFS + rc = sqlite3_open_v2( osTmpDBName.c_str(), &hDB, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX, + NULL ); +#else + rc = sqlite3_open( osTmpDBName.c_str(), &hDB ); +#endif + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "sqlite3_open(%s) failed: %s", + osTmpDBName.c_str(), sqlite3_errmsg( hDB ) ); + bStopParsing = TRUE; + CloseDB(); + return FALSE; + } + + /* On Unix filesystems, you can remove a file even if it */ + /* opened */ + const char* pszVal = CPLGetConfigOption("OSM_UNLINK_TMPFILE", "YES"); + if( EQUAL(pszVal, "YES") ) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + bMustUnlink = VSIUnlink( osTmpDBName ) != 0; + CPLPopErrorHandler(); + } + + if( !SetDBOptions() || !CreatePreparedStatements() ) + { + bStopParsing = TRUE; + CloseDB(); + return FALSE; + } + } + } + + return TRUE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGROSMDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGROSMDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGROSMDataSource::GetExtent( OGREnvelope *psExtent ) +{ + if (!bHasParsedFirstChunk) + { + bHasParsedFirstChunk = TRUE; + OSM_ProcessBlock(psParser); + } + + if (bExtentValid) + { + memcpy(psExtent, &sExtent, sizeof(sExtent)); + return OGRERR_NONE; + } + + return OGRERR_FAILURE; +} + + +/************************************************************************/ +/* OGROSMSingleFeatureLayer */ +/************************************************************************/ + +class OGROSMSingleFeatureLayer : public OGRLayer +{ + private: + int nVal; + char *pszVal; + OGRFeatureDefn *poFeatureDefn; + int iNextShapeId; + + public: + OGROSMSingleFeatureLayer( const char* pszLayerName, + int nVal ); + OGROSMSingleFeatureLayer( const char* pszLayerName, + const char *pszVal ); + ~OGROSMSingleFeatureLayer(); + + virtual void ResetReading() { iNextShapeId = 0; } + virtual OGRFeature *GetNextFeature(); + virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; } + virtual int TestCapability( const char * ) { return FALSE; } +}; + + +/************************************************************************/ +/* OGROSMSingleFeatureLayer() */ +/************************************************************************/ + +OGROSMSingleFeatureLayer::OGROSMSingleFeatureLayer( const char* pszLayerName, + int nVal ) +{ + poFeatureDefn = new OGRFeatureDefn( "SELECT" ); + poFeatureDefn->Reference(); + OGRFieldDefn oField( pszLayerName, OFTInteger ); + poFeatureDefn->AddFieldDefn( &oField ); + + iNextShapeId = 0; + this->nVal = nVal; + pszVal = NULL; +} + +/************************************************************************/ +/* OGROSMSingleFeatureLayer() */ +/************************************************************************/ + +OGROSMSingleFeatureLayer::OGROSMSingleFeatureLayer( const char* pszLayerName, + const char *pszVal ) +{ + poFeatureDefn = new OGRFeatureDefn( "SELECT" ); + poFeatureDefn->Reference(); + OGRFieldDefn oField( pszLayerName, OFTString ); + poFeatureDefn->AddFieldDefn( &oField ); + + iNextShapeId = 0; + nVal = 0; + this->pszVal = CPLStrdup(pszVal); +} + +/************************************************************************/ +/* ~OGROSMSingleFeatureLayer() */ +/************************************************************************/ + +OGROSMSingleFeatureLayer::~OGROSMSingleFeatureLayer() +{ + poFeatureDefn->Release(); + CPLFree(pszVal); +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature * OGROSMSingleFeatureLayer::GetNextFeature() +{ + if (iNextShapeId != 0) + return NULL; + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + if (pszVal) + poFeature->SetField(0, pszVal); + else + poFeature->SetField(0, nVal); + poFeature->SetFID(iNextShapeId ++); + return poFeature; +} + +/************************************************************************/ +/* OGROSMResultLayerDecorator */ +/************************************************************************/ + +class OGROSMResultLayerDecorator : public OGRLayerDecorator +{ + CPLString osDSName; + CPLString osInterestLayers; + + public: + OGROSMResultLayerDecorator(OGRLayer* poLayer, + CPLString osDSName, + CPLString osInterestLayers) : + OGRLayerDecorator(poLayer, TRUE), + osDSName(osDSName), + osInterestLayers(osInterestLayers) {} + + virtual GIntBig GetFeatureCount( int bForce = TRUE ) + { + /* When we run GetFeatureCount() with SQLite SQL dialect, */ + /* the OSM dataset will be re-opened. Make sure that it is */ + /* re-opened with the same interest layers */ + AddInterestLayersForDSName(osDSName, osInterestLayers); + return OGRLayerDecorator::GetFeatureCount(bForce); + } +}; + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGROSMDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ +/* -------------------------------------------------------------------- */ +/* Special GetBytesRead() command */ +/* -------------------------------------------------------------------- */ + if (strcmp(pszSQLCommand, "GetBytesRead()") == 0) + { + char szVal[64]; + sprintf(szVal, CPL_FRMT_GUIB, OSM_GetBytesRead(psParser)); + return new OGROSMSingleFeatureLayer( "GetBytesRead", szVal ); + } + + if( poResultSetLayer != NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "A SQL result layer is still in use. Please delete it first"); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Special SET interest_layers = command */ +/* -------------------------------------------------------------------- */ + if (strncmp(pszSQLCommand, "SET interest_layers =", 21) == 0) + { + char** papszTokens = CSLTokenizeString2(pszSQLCommand + 21, ",", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES); + int i; + + for(i=0; i < nLayers; i++) + { + papoLayers[i]->SetDeclareInterest(FALSE); + } + + for(i=0; papszTokens[i] != NULL; i++) + { + OGROSMLayer* poLayer = (OGROSMLayer*) GetLayerByName(papszTokens[i]); + if( poLayer != NULL ) + { + poLayer->SetDeclareInterest(TRUE); + } + } + + if( papoLayers[IDX_LYR_POINTS]->IsUserInterested() && + !papoLayers[IDX_LYR_LINES]->IsUserInterested() && + !papoLayers[IDX_LYR_MULTILINESTRINGS]->IsUserInterested() && + !papoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested() && + !papoLayers[IDX_LYR_OTHER_RELATIONS]->IsUserInterested()) + { + if( CPLGetConfigOption("OSM_INDEX_POINTS", NULL) == NULL ) + { + CPLDebug("OSM", "Disabling indexing of nodes"); + bIndexPoints = FALSE; + } + if( CPLGetConfigOption("OSM_USE_POINTS_INDEX", NULL) == NULL ) + { + bUsePointsIndex = FALSE; + } + if( CPLGetConfigOption("OSM_INDEX_WAYS", NULL) == NULL ) + { + CPLDebug("OSM", "Disabling indexing of ways"); + bIndexWays = FALSE; + } + if( CPLGetConfigOption("OSM_USE_WAYS_INDEX", NULL) == NULL ) + { + bUseWaysIndex = FALSE; + } + } + else if( papoLayers[IDX_LYR_LINES]->IsUserInterested() && + !papoLayers[IDX_LYR_MULTILINESTRINGS]->IsUserInterested() && + !papoLayers[IDX_LYR_MULTIPOLYGONS]->IsUserInterested() && + !papoLayers[IDX_LYR_OTHER_RELATIONS]->IsUserInterested() ) + { + if( CPLGetConfigOption("OSM_INDEX_WAYS", NULL) == NULL ) + { + CPLDebug("OSM", "Disabling indexing of ways"); + bIndexWays = FALSE; + } + if( CPLGetConfigOption("OSM_USE_WAYS_INDEX", NULL) == NULL ) + { + bUseWaysIndex = FALSE; + } + } + + CSLDestroy(papszTokens); + + return NULL; + } + + while(*pszSQLCommand == ' ') + pszSQLCommand ++; + + /* Try to analyse the SQL command to get the interest table */ + if( EQUALN(pszSQLCommand, "SELECT", 5) ) + { + int bLayerAlreadyAdded = FALSE; + CPLString osInterestLayers = "SET interest_layers ="; + + if( pszDialect != NULL && EQUAL(pszDialect, "SQLITE") ) + { + std::set oSetLayers = OGRSQLiteGetReferencedLayers(pszSQLCommand); + std::set::iterator oIter = oSetLayers.begin(); + for(; oIter != oSetLayers.end(); ++oIter) + { + const LayerDesc& oLayerDesc = *oIter; + if( oLayerDesc.osDSName.size() == 0 ) + { + if( bLayerAlreadyAdded ) osInterestLayers += ","; + bLayerAlreadyAdded = TRUE; + osInterestLayers += oLayerDesc.osLayerName; + } + } + } + else + { + swq_select sSelectInfo; + + CPLPushErrorHandler(CPLQuietErrorHandler); + CPLErr eErr = sSelectInfo.preparse( pszSQLCommand ); + CPLPopErrorHandler(); + + if( eErr == CPLE_None ) + { + swq_select* pCurSelect = &sSelectInfo; + while(pCurSelect != NULL) + { + for( int iTable = 0; iTable < pCurSelect->table_count; iTable++ ) + { + swq_table_def *psTableDef = pCurSelect->table_defs + iTable; + if( psTableDef->data_source == NULL ) + { + if( bLayerAlreadyAdded ) osInterestLayers += ","; + bLayerAlreadyAdded = TRUE; + osInterestLayers += psTableDef->table_name; + } + } + pCurSelect = pCurSelect->poOtherSelect; + } + } + } + + if( bLayerAlreadyAdded ) + { + /* Backup current optimization parameters */ + abSavedDeclaredInterest.resize(0); + for(int i=0; i < nLayers; i++) + { + abSavedDeclaredInterest.push_back(papoLayers[i]->IsUserInterested()); + } + bIndexPointsBackup = bIndexPoints; + bUsePointsIndexBackup = bUsePointsIndex; + bIndexWaysBackup = bIndexWays; + bUseWaysIndexBackup = bUseWaysIndex; + + /* Update optimization parameters */ + ExecuteSQL(osInterestLayers, NULL, NULL); + + ResetReading(); + + /* Run the request */ + poResultSetLayer = OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + + /* If the user explicitly run a COUNT() request, then do it ! */ + if( poResultSetLayer ) + { + if( pszDialect != NULL && EQUAL(pszDialect, "SQLITE") ) + { + poResultSetLayer = new OGROSMResultLayerDecorator( + poResultSetLayer, GetName(), osInterestLayers); + } + bIsFeatureCountEnabled = TRUE; + } + + return poResultSetLayer; + } + } + + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGROSMDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + if( poLayer != NULL && poLayer == poResultSetLayer ) + { + poResultSetLayer = NULL; + + bIsFeatureCountEnabled = FALSE; + + /* Restore backup'ed optimization parameters */ + for(int i=0; i < nLayers; i++) + { + papoLayers[i]->SetDeclareInterest(abSavedDeclaredInterest[i]); + } + if( bIndexPointsBackup && !bIndexPoints ) + CPLDebug("OSM", "Re-enabling indexing of nodes"); + bIndexPoints = bIndexPointsBackup; + bUsePointsIndex = bUsePointsIndexBackup; + if( bIndexWaysBackup && !bIndexWays ) + CPLDebug("OSM", "Re-enabling indexing of ways"); + bIndexWays = bIndexWaysBackup; + bUseWaysIndex = bUseWaysIndexBackup; + abSavedDeclaredInterest.resize(0); + } + + delete poLayer; +} + +/************************************************************************/ +/* IsInterleavedReading() */ +/************************************************************************/ + +int OGROSMDataSource::IsInterleavedReading() +{ + if( bInterleavedReading < 0 ) + { + bInterleavedReading = CSLTestBoolean( + CPLGetConfigOption("OGR_INTERLEAVED_READING", "NO")); + CPLDebug("OSM", "OGR_INTERLEAVED_READING = %d", bInterleavedReading); + } + return bInterleavedReading; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/ogrosmdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/ogrosmdriver.cpp new file mode 100644 index 000000000..f9ab442b6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/ogrosmdriver.cpp @@ -0,0 +1,121 @@ +/****************************************************************************** + * $Id: ogrosmdriver.cpp 29242 2015-05-24 10:59:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGROSMDriver class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_osm.h" +#include "cpl_conv.h" + +/* g++ -DHAVE_EXPAT -fPIC -g -Wall ogr/ogrsf_frmts/osm/ogrosmdriver.cpp ogr/ogrsf_frmts/osm/ogrosmdatasource.cpp ogr/ogrsf_frmts/osm/ogrosmlayer.cpp -Iport -Igcore -Iogr -Iogr/ogrsf_frmts/osm -Iogr/ogrsf_frmts/mitab -Iogr/ogrsf_frmts -shared -o ogr_OSM.so -L. -lgdal */ + +extern "C" void CPL_DLL RegisterOGROSM(); + +CPL_CVSID("$Id: ogrosmdriver.cpp 29242 2015-05-24 10:59:41Z rouault $"); + +/************************************************************************/ +/* OGROSMDriverIdentify() */ +/************************************************************************/ + +static int OGROSMDriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + if (poOpenInfo->fpL == NULL ) + return FALSE; + const char* pszExt = CPLGetExtension(poOpenInfo->pszFilename); + if( EQUAL(pszExt, "pbf") || + EQUAL(pszExt, "osm") ) + return TRUE; + if( EQUALN(poOpenInfo->pszFilename, "/vsicurl_streaming/", strlen("/vsicurl_streaming/")) || + strcmp(poOpenInfo->pszFilename, "/vsistdin/") == 0 || + strcmp(poOpenInfo->pszFilename, "/dev/stdin/") == 0 ) + return -1; + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGROSMDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if (poOpenInfo->eAccess == GA_Update ) + return NULL; + if( OGROSMDriverIdentify(poOpenInfo) == FALSE ) + return NULL; + + OGROSMDataSource *poDS = new OGROSMDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, poOpenInfo->papszOpenOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGROSM() */ +/************************************************************************/ + +void RegisterOGROSM() +{ + if (! GDAL_CHECK_VERSION("OGR/OSM driver")) + return; + GDALDriver *poDriver; + + if( GDALGetDriverByName( "OSM" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "OSM" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "OpenStreetMap XML and PBF" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "osm pbf" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_osm.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " ); + + poDriver->pfnOpen = OGROSMDriverOpen; + poDriver->pfnIdentify = OGROSMDriverIdentify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/ogrosmlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/ogrosmlayer.cpp new file mode 100644 index 000000000..d598c9de6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/ogrosmlayer.cpp @@ -0,0 +1,877 @@ +/****************************************************************************** + * $Id: ogrosmlayer.cpp 28900 2015-04-14 09:40:34Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGROSMLayer class + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_osm.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_time.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrosmlayer.cpp 28900 2015-04-14 09:40:34Z rouault $"); + +#define SWITCH_THRESHOLD 10000 +#define MAX_THRESHOLD 100000 + +#define ALLTAGS_LENGTH 8192 + +/************************************************************************/ +/* OGROSMLayer() */ +/************************************************************************/ + + +OGROSMLayer::OGROSMLayer(OGROSMDataSource* poDS, int nIdxLayer, const char* pszName ) +{ + this->poDS = poDS; + this->nIdxLayer = nIdxLayer; + + poFeatureDefn = new OGRFeatureDefn( pszName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + poSRS = new OGRSpatialReference(); + poSRS->SetWellKnownGeogCS("WGS84"); + if( poFeatureDefn->GetGeomFieldCount() != 0 ) + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + nFeatureArraySize = 0; + nFeatureArrayMaxSize = 0; + nFeatureArrayIndex = 0; + papoFeatures = NULL; + + nFeatureCount = 0; + + bHasOSMId = FALSE; + nIndexOSMId = -1; + nIndexOSMWayId = -1; + bHasVersion = FALSE; + bHasTimestamp = FALSE; + bHasUID = FALSE; + bHasUser = FALSE; + bHasChangeset = FALSE; + bHasOtherTags = TRUE; + nIndexOtherTags = -1; + bHasAllTags = FALSE; + nIndexAllTags = -1; + + bResetReadingAllowed = FALSE; + bHasWarnedTooManyFeatures = FALSE; + + pszAllTags = (char*)CPLMalloc(ALLTAGS_LENGTH); + bHasWarnedAllTagsTruncated = FALSE; + + bUserInterested = TRUE; +} + +/************************************************************************/ +/* ~OGROSMLayer() */ +/************************************************************************/ + +OGROSMLayer::~OGROSMLayer() +{ + int i; + + poFeatureDefn->Release(); + + if (poSRS) + poSRS->Release(); + + for(i=0;iIsInterleavedReading() ) + return; + + poDS->ResetReading(); +} + +/************************************************************************/ +/* ForceResetReading() */ +/************************************************************************/ + +void OGROSMLayer::ForceResetReading() +{ + for(int i=0;iIsInterleavedReading() ) + { + poDS->ResetReading(); + } + } + else + { + CPLError(CE_Warning, CPLE_AppDefined, "The new attribute filter will " + "not be taken into account immediately. It is advised to " + "set attribute filters for all needed layers, before reading *any* layer"); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGROSMLayer::GetFeatureCount( int bForce ) +{ + if( poDS->IsFeatureCountEnabled() ) + return OGRLayer::GetFeatureCount(bForce); + + return -1; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGROSMLayer::GetNextFeature() +{ + bResetReadingAllowed = TRUE; + + if ( nFeatureArraySize == 0) + { + if ( poDS->IsInterleavedReading() ) + { + int i; + + OGRLayer* poCurrentLayer = poDS->GetCurrentLayer(); + if ( poCurrentLayer == NULL ) + { + poDS->SetCurrentLayer(this); + } + else if( poCurrentLayer != this ) + { + return NULL; + } + + /* If too many features have been accumulated in */ + /* another layer, we force */ + /* a switch to that layer, so that it gets emptied */ + for(i=0;iGetLayerCount();i++) + { + if (poDS->papoLayers[i] != this && + poDS->papoLayers[i]->nFeatureArraySize > SWITCH_THRESHOLD) + { + poDS->SetCurrentLayer(poDS->papoLayers[i]); + CPLDebug("OSM", "Switching to '%s' as they are too many " + "features in '%s'", + poDS->papoLayers[i]->GetName(), + GetName()); + return NULL; + } + } + + /* Read some more data and accumulate features */ + poDS->ParseNextChunk(nIdxLayer); + + if ( nFeatureArraySize == 0 ) + { + /* If there are really no more features to read in the */ + /* current layer, force a switch to another non-empty layer */ + + for(i=0;iGetLayerCount();i++) + { + if (poDS->papoLayers[i] != this && + poDS->papoLayers[i]->nFeatureArraySize > 0) + { + poDS->SetCurrentLayer(poDS->papoLayers[i]); + CPLDebug("OSM", + "Switching to '%s' as they are no more feature in '%s'", + poDS->papoLayers[i]->GetName(), + GetName()); + return NULL; + } + } + + /* Game over : no more data to read from the stream */ + poDS->SetCurrentLayer(NULL); + return NULL; + } + } + else + { + while(TRUE) + { + int bRet = poDS->ParseNextChunk(nIdxLayer); + if (nFeatureArraySize != 0) + break; + if (bRet == FALSE) + return NULL; + } + } + } + + OGRFeature* poFeature = papoFeatures[nFeatureArrayIndex]; + + papoFeatures[nFeatureArrayIndex] = NULL; + nFeatureArrayIndex++; + + if ( nFeatureArrayIndex == nFeatureArraySize) + nFeatureArrayIndex = nFeatureArraySize = 0; + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGROSMLayer::TestCapability( const char * pszCap ) +{ + if( EQUAL(pszCap, OLCFastGetExtent) ) + { + OGREnvelope sExtent; + if (poDS->GetExtent(&sExtent) == OGRERR_NONE) + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* AddToArray() */ +/************************************************************************/ + +int OGROSMLayer::AddToArray(OGRFeature* poFeature, int bCheckFeatureThreshold) +{ + if( bCheckFeatureThreshold && nFeatureArraySize > MAX_THRESHOLD) + { + if( !bHasWarnedTooManyFeatures ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too many features have accumulated in %s layer. " + "Use OGR_INTERLEAVED_READING=YES mode", + GetName()); + } + bHasWarnedTooManyFeatures = TRUE; + return FALSE; + } + + if (nFeatureArraySize == nFeatureArrayMaxSize) + { + nFeatureArrayMaxSize = nFeatureArrayMaxSize + nFeatureArrayMaxSize / 2 + 128; + CPLDebug("OSM", "For layer %s, new max size is %d", GetName(), nFeatureArrayMaxSize); + OGRFeature** papoNewFeatures = (OGRFeature**)VSIRealloc(papoFeatures, + nFeatureArrayMaxSize * sizeof(OGRFeature*)); + if (papoNewFeatures == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "For layer %s, cannot resize feature array to %d features", + GetName(), nFeatureArrayMaxSize); + return FALSE; + } + papoFeatures = papoNewFeatures; + } + papoFeatures[nFeatureArraySize ++] = poFeature; + + return TRUE; +} + +/************************************************************************/ +/* EvaluateAttributeFilter() */ +/************************************************************************/ + +int OGROSMLayer::EvaluateAttributeFilter(OGRFeature* poFeature) +{ + return (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +int OGROSMLayer::AddFeature(OGRFeature* poFeature, + int bAttrFilterAlreadyEvaluated, + int* pbFilteredOut, + int bCheckFeatureThreshold) +{ + if( !bUserInterested ) + { + if (pbFilteredOut) + *pbFilteredOut = TRUE; + delete poFeature; + return TRUE; + } + + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if (poGeom) + poGeom->assignSpatialReference( poSRS ); + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL || bAttrFilterAlreadyEvaluated + || m_poAttrQuery->Evaluate( poFeature )) ) + { + if (!AddToArray(poFeature, bCheckFeatureThreshold)) + { + delete poFeature; + return FALSE; + } + } + else + { + if (pbFilteredOut) + *pbFilteredOut = TRUE; + delete poFeature; + return TRUE; + } + + if (pbFilteredOut) + *pbFilteredOut = FALSE; + return TRUE; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGROSMLayer::GetExtent( OGREnvelope *psExtent, + CPL_UNUSED int bForce ) +{ + if (poDS->GetExtent(psExtent) == OGRERR_NONE) + return OGRERR_NONE; + + /* return OGRLayer::GetExtent(psExtent, bForce);*/ + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* GetLaunderedFieldName() */ +/************************************************************************/ + +const char* OGROSMLayer::GetLaunderedFieldName(const char* pszName) +{ + if( poDS->DoesAttributeNameLaundering() && + strchr(pszName, ':') != NULL ) + { + size_t i; + for( i = 0; + pszName[i] != '\0' && i < sizeof(szLaunderedFieldName) - 1; i++ ) + { + if( pszName[i] == ':' ) + szLaunderedFieldName[i] = '_'; + else + szLaunderedFieldName[i] = pszName[i]; + } + szLaunderedFieldName[i] = '\0'; + return szLaunderedFieldName; + } + else + return pszName; +} + +/************************************************************************/ +/* AddField() */ +/************************************************************************/ + +void OGROSMLayer::AddField(const char* pszName, OGRFieldType eFieldType) +{ + const char* pszLaunderedName = GetLaunderedFieldName(pszName); + OGRFieldDefn oField(pszLaunderedName, eFieldType); + poFeatureDefn->AddFieldDefn(&oField); + + int nIndex = poFeatureDefn->GetFieldCount() - 1; + char* pszDupName = CPLStrdup(pszName); + apszNames.push_back(pszDupName); + oMapFieldNameToIndex[pszDupName] = nIndex; + + if( strcmp(pszName, "osm_id") == 0 ) + nIndexOSMId = nIndex; + + else if( strcmp(pszName, "osm_way_id") == 0 ) + nIndexOSMWayId = nIndex; + + else if( strcmp(pszName, "other_tags") == 0 ) + nIndexOtherTags = nIndex; + + else if( strcmp(pszName, "all_tags") == 0 ) + nIndexAllTags = nIndex; +} + +/************************************************************************/ +/* GetFieldIndex() */ +/************************************************************************/ + +int OGROSMLayer::GetFieldIndex(const char* pszName) +{ + std::map::iterator oIter = + oMapFieldNameToIndex.find(pszName); + if( oIter != oMapFieldNameToIndex.end() ) + return oIter->second; + else + return -1; +} + +/************************************************************************/ +/* AddInOtherOrAllTags() */ +/************************************************************************/ + +int OGROSMLayer::AddInOtherOrAllTags(const char* pszK) +{ + int bAddToOtherTags = FALSE; + + if ( aoSetIgnoreKeys.find(pszK) == aoSetIgnoreKeys.end() ) + { + char* pszColon = strchr((char*) pszK, ':'); + if( pszColon ) + { + char chBackup = pszColon[1]; + pszColon[1] = '\0'; /* Evil but OK */ + bAddToOtherTags = ( aoSetIgnoreKeys.find(pszK) == + aoSetIgnoreKeys.end() ); + pszColon[1] = chBackup; + } + else + bAddToOtherTags = TRUE; + } + + return bAddToOtherTags; +} + +/************************************************************************/ +/* OGROSMFormatForHSTORE() */ +/************************************************************************/ + +static int OGROSMFormatForHSTORE(const char* pszV, char* pszAllTags) +{ + int k; + + int nAllTagsOff = 0; + + pszAllTags[nAllTagsOff++] = '"'; + + for(k=0;pszV[k] != '\0'; k++) + { + if( pszV[k] == '"' || pszV[k] == '\\' ) + pszAllTags[nAllTagsOff++] = '\\'; + pszAllTags[nAllTagsOff++] = pszV[k]; + } + + pszAllTags[nAllTagsOff++] = '"'; + + return nAllTagsOff; +} + +/************************************************************************/ +/* SetFieldsFromTags() */ +/************************************************************************/ + +void OGROSMLayer::SetFieldsFromTags(OGRFeature* poFeature, + GIntBig nID, + int bIsWayID, + unsigned int nTags, OSMTag* pasTags, + OSMInfo* psInfo) +{ + if( !bIsWayID ) + { + poFeature->SetFID( nID ); + + if( bHasOSMId ) + { + char szID[32]; + sprintf(szID, CPL_FRMT_GIB, nID ); + poFeature->SetField(nIndexOSMId, szID); + } + } + else + { + poFeature->SetFID( nID ); + + if( nIndexOSMWayId >= 0 ) + { + char szID[32]; + sprintf(szID, CPL_FRMT_GIB, nID ); + poFeature->SetField(nIndexOSMWayId, szID ); + } + } + + if( bHasVersion ) + { + poFeature->SetField("osm_version", psInfo->nVersion); + } + if( bHasTimestamp ) + { + if( psInfo->bTimeStampIsStr ) + { + OGRField sField; + if (OGRParseXMLDateTime(psInfo->ts.pszTimeStamp, &sField)) + { + poFeature->SetField("osm_timestamp", &sField); + } + } + else + { + struct tm brokendown; + CPLUnixTimeToYMDHMS(psInfo->ts.nTimeStamp, &brokendown); + poFeature->SetField("osm_timestamp", + brokendown.tm_year + 1900, + brokendown.tm_mon + 1, + brokendown.tm_mday, + brokendown.tm_hour, + brokendown.tm_min, + brokendown.tm_sec, + 0); + } + + } + if( bHasUID ) + { + poFeature->SetField("osm_uid", psInfo->nUID); + } + if( bHasUser ) + { + poFeature->SetField("osm_user", psInfo->pszUserSID); + } + if( bHasChangeset ) + { + poFeature->SetField("osm_changeset", (int) psInfo->nChangeset); + } + + int nAllTagsOff = 0; + for(unsigned int j = 0; j < nTags; j++) + { + const char* pszK = pasTags[j].pszK; + const char* pszV = pasTags[j].pszV; + int nIndex = GetFieldIndex(pszK); + if( nIndex >= 0 ) + { + poFeature->SetField(nIndex, pszV); + if( nIndexAllTags < 0 ) + continue; + } + if ( nIndexAllTags >= 0 || nIndexOtherTags >= 0 ) + { + if ( AddInOtherOrAllTags(pszK) ) + { + int nLenK = (int)strlen(pszK); + int nLenV = (int)strlen(pszV); + if( nAllTagsOff + + 1 + 2 * nLenK + 1 + + 2 + + 1 + 2 * nLenV + 1 + + 1 >= ALLTAGS_LENGTH - 1 ) + { + if( !bHasWarnedAllTagsTruncated ) + CPLDebug("OSM", "all_tags/other_tags field truncated for feature " CPL_FRMT_GIB, nID); + bHasWarnedAllTagsTruncated = TRUE; + continue; + } + + if( nAllTagsOff ) + pszAllTags[nAllTagsOff++] = ','; + + nAllTagsOff += OGROSMFormatForHSTORE(pszK, + pszAllTags + nAllTagsOff); + + pszAllTags[nAllTagsOff++] = '='; + pszAllTags[nAllTagsOff++] = '>'; + + nAllTagsOff += OGROSMFormatForHSTORE(pszV, + pszAllTags + nAllTagsOff); + } + +#ifdef notdef + if ( aoSetWarnKeys.find(pszK) == + aoSetWarnKeys.end() ) + { + aoSetWarnKeys.insert(pszK); + CPLDebug("OSM_KEY", "Ignored key : %s", pszK); + } +#endif + } + } + + if( nAllTagsOff ) + { + pszAllTags[nAllTagsOff] = '\0'; + if( nIndexAllTags >= 0 ) + poFeature->SetField(nIndexAllTags, pszAllTags); + else + poFeature->SetField(nIndexOtherTags, pszAllTags); + } + + for(size_t i=0; i= 0 ) + { + if( !poFeature->IsFieldSet(oAttr.anIndexToBind[j]) ) + { + sqlite3_bind_null( oAttr.hStmt, j + 1 ); + } + else + { + OGRFieldType eType = poFeatureDefn->GetFieldDefn(oAttr.anIndexToBind[j])->GetType(); + if( eType == OFTInteger ) + sqlite3_bind_int( oAttr.hStmt, j + 1, + poFeature->GetFieldAsInteger(oAttr.anIndexToBind[j]) ); + else if( eType == OFTInteger64 ) + sqlite3_bind_int64( oAttr.hStmt, j + 1, + poFeature->GetFieldAsInteger64(oAttr.anIndexToBind[j]) ); + else if( eType == OFTReal ) + sqlite3_bind_double( oAttr.hStmt, j + 1, + poFeature->GetFieldAsDouble(oAttr.anIndexToBind[j]) ); + else + sqlite3_bind_text( oAttr.hStmt, j + 1, + poFeature->GetFieldAsString(oAttr.anIndexToBind[j]), + -1, SQLITE_TRANSIENT); + } + } + else + { + int bTagFound = FALSE; + for(unsigned int k = 0; k < nTags; k++) + { + const char* pszK = pasTags[k].pszK; + const char* pszV = pasTags[k].pszV; + if( strcmp(pszK, oAttr.aosAttrToBind[j]) == 0 ) + { + sqlite3_bind_text( oAttr.hStmt, j + 1, pszV, -1, SQLITE_TRANSIENT); + bTagFound = TRUE; + break; + } + } + if( !bTagFound ) + sqlite3_bind_null( oAttr.hStmt, j + 1 ); + } + } + + if( sqlite3_step( oAttr.hStmt ) == SQLITE_ROW && + sqlite3_column_count( oAttr.hStmt ) == 1 ) + { + switch( sqlite3_column_type( oAttr.hStmt, 0 ) ) + { + case SQLITE_INTEGER: + poFeature->SetField( oAttr.nIndex, + (GIntBig)sqlite3_column_int64(oAttr.hStmt, 0) ); + break; + case SQLITE_FLOAT: + poFeature->SetField( oAttr.nIndex, + sqlite3_column_double(oAttr.hStmt, 0) ); + break; + case SQLITE_TEXT: + poFeature->SetField( oAttr.nIndex, + (const char*)sqlite3_column_text(oAttr.hStmt, 0) ); + break; + default: + break; + } + } + + sqlite3_reset( oAttr.hStmt ); + } +} + +/************************************************************************/ +/* GetSpatialFilterEnvelope() */ +/************************************************************************/ + +const OGREnvelope* OGROSMLayer::GetSpatialFilterEnvelope() +{ + if( m_poFilterGeom != NULL ) + return &m_sFilterEnvelope; + else + return NULL; +} + +/************************************************************************/ +/* AddUnsignificantKey() */ +/************************************************************************/ + +void OGROSMLayer::AddUnsignificantKey(const char* pszK) +{ + char* pszKDup = CPLStrdup(pszK); + apszUnsignificantKeys.push_back(pszKDup); + aoSetUnsignificantKeys[pszKDup] = 1; +} + +/************************************************************************/ +/* AddIgnoreKey() */ +/************************************************************************/ + +void OGROSMLayer::AddIgnoreKey(const char* pszK) +{ + char* pszKDup = CPLStrdup(pszK); + apszIgnoreKeys.push_back(pszKDup); + aoSetIgnoreKeys[pszKDup] = 1; +} + +/************************************************************************/ +/* AddWarnKey() */ +/************************************************************************/ + +void OGROSMLayer::AddWarnKey(const char* pszK) +{ + aoSetWarnKeys.insert(pszK); +} + +/************************************************************************/ +/* AddWarnKey() */ +/************************************************************************/ + +void OGROSMLayer::AddComputedAttribute(const char* pszName, + OGRFieldType eType, + const char* pszSQL) +{ + if( poDS->hDBForComputedAttributes == NULL ) + { + int rc; +#ifdef HAVE_SQLITE_VFS + rc = sqlite3_open_v2( ":memory:", &(poDS->hDBForComputedAttributes), + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX, NULL ); +#else + rc = sqlite3_open( ":memory:", &(poDS->hDBForComputedAttributes) ); +#endif + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot open temporary sqlite DB" ); + return; + } + } + + if( poFeatureDefn->GetFieldIndex(pszName) >= 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "A field with same name %s already exists", pszName ); + return; + } + + CPLString osSQL(pszSQL); + std::vector aosAttrToBind; + std::vector anIndexToBind; + size_t nStartSearch = 0; + while(TRUE) + { + size_t nPos = osSQL.find("[", nStartSearch); + if( nPos == std::string::npos ) + break; + nStartSearch = nPos + 1; + if( nPos > 0 && osSQL[nPos-1] != '\\' ) + { + CPLString osAttr = osSQL.substr(nPos + 1); + size_t nPos2 = osAttr.find("]"); + if( nPos2 == std::string::npos ) + break; + osAttr.resize(nPos2); + + osSQL = osSQL.substr(0, nPos) + "?" + osSQL.substr(nPos + 1 + nPos2+1); + + aosAttrToBind.push_back(osAttr); + anIndexToBind.push_back(poFeatureDefn->GetFieldIndex(osAttr)); + } + } + while(TRUE) + { + size_t nPos = osSQL.find("\\"); + if( nPos == std::string::npos || nPos == osSQL.size() - 1 ) + break; + osSQL = osSQL.substr(0, nPos) + osSQL.substr(nPos + 1); + } + + CPLDebug("OSM", "SQL : \"%s\"", osSQL.c_str()); + + sqlite3_stmt *hStmt; + int rc = sqlite3_prepare( poDS->hDBForComputedAttributes, osSQL, -1, + &hStmt, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "sqlite3_prepare() failed : %s", + sqlite3_errmsg(poDS->hDBForComputedAttributes) ); + return; + } + + OGRFieldDefn oField(pszName, eType); + poFeatureDefn->AddFieldDefn(&oField); + oComputedAttributes.push_back(OGROSMComputedAttribute(pszName)); + oComputedAttributes[oComputedAttributes.size()-1].eType = eType; + oComputedAttributes[oComputedAttributes.size()-1].nIndex = poFeatureDefn->GetFieldCount() - 1; + oComputedAttributes[oComputedAttributes.size()-1].osSQL = pszSQL; + oComputedAttributes[oComputedAttributes.size()-1].hStmt = hStmt; + oComputedAttributes[oComputedAttributes.size()-1].aosAttrToBind = aosAttrToBind; + oComputedAttributes[oComputedAttributes.size()-1].anIndexToBind = anIndexToBind; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/osm2osm.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/osm2osm.c new file mode 100644 index 000000000..ced803265 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/osm2osm.c @@ -0,0 +1,400 @@ +/****************************************************************************** + * $Id: osm2osm.c 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Author: Even Rouault, + * Purpose: osm2osm + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi.h" + +#include "osm_parser.h" + +#include + +#define SECSPERMIN 60L +#define MINSPERHOUR 60L +#define HOURSPERDAY 24L +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY) +#define DAYSPERWEEK 7 +#define MONSPERYEAR 12 + +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY 4 +#define TM_YEAR_BASE 1900 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 + +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) +#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) + +static const int mon_lengths[2][MONSPERYEAR] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +} ; + + +static const int year_lengths[2] = { + DAYSPERNYEAR, DAYSPERLYEAR +}; + +/************************************************************************/ +/* CPLUnixTimeToYMDHMS() */ +/************************************************************************/ + +/** Converts a time value since the Epoch (aka "unix" time) to a broken-down UTC time. + * + * This function is similar to gmtime_r(). + * This function will always set tm_isdst to 0. + * + * @param unixTime number of seconds since the Epoch. + * @param pRet address of the return structure. + * + * @return the structure pointed by pRet filled with a broken-down UTC time. + */ + +struct tm * myCPLUnixTimeToYMDHMS(GIntBig unixTime, struct tm* pRet) +{ + GIntBig days = unixTime / SECSPERDAY; + GIntBig rem = unixTime % SECSPERDAY; + GIntBig y = EPOCH_YEAR; + int yleap; + const int* ip; + + while (rem < 0) { + rem += SECSPERDAY; + --days; + } + + pRet->tm_hour = (int) (rem / SECSPERHOUR); + rem = rem % SECSPERHOUR; + pRet->tm_min = (int) (rem / SECSPERMIN); + /* + ** A positive leap second requires a special + ** representation. This uses "... ??:59:60" et seq. + */ + pRet->tm_sec = (int) (rem % SECSPERMIN); + pRet->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); + if (pRet->tm_wday < 0) + pRet->tm_wday += DAYSPERWEEK; + while (days < 0 || days >= (GIntBig) year_lengths[yleap = isleap(y)]) + { + GIntBig newy; + + newy = y + days / DAYSPERNYEAR; + if (days < 0) + --newy; + days -= (newy - y) * DAYSPERNYEAR + + LEAPS_THRU_END_OF(newy - 1) - + LEAPS_THRU_END_OF(y - 1); + y = newy; + } + pRet->tm_year = (int) (y - TM_YEAR_BASE); + pRet->tm_yday = (int) days; + ip = mon_lengths[yleap]; + for (pRet->tm_mon = 0; days >= (GIntBig) ip[pRet->tm_mon]; ++(pRet->tm_mon)) + days = days - (GIntBig) ip[pRet->tm_mon]; + pRet->tm_mday = (int) (days + 1); + pRet->tm_isdst = 0; + + return pRet; +} + +static void WriteEscaped(const char* pszStr, VSILFILE* fp) +{ + GByte ch; + while((ch = *(pszStr ++)) != 0) + { + if( ch == '<' ) + { + /* printf("<"); */ + VSIFPrintfL(fp, "<"); + } + else if( ch == '>' ) + { + /* printf(">"); */ + VSIFPrintfL(fp, ">"); + } + else if( ch == '&' ) + { + /* printf("&"); */ + VSIFPrintfL(fp, "&"); + } + else if( ch == '"' ) + { + /* printf("""); */ + VSIFPrintfL(fp, """); + } + else if( ch == '\'' ) + { + VSIFPrintfL(fp, "'"); + } + else if( ch < 0x20 && ch != 0x9 && ch != 0xA && ch != 0xD ) + { + /* These control characters are unrepresentable in XML format, */ + /* so we just drop them. #4117 */ + } + else + VSIFWriteL(&ch, 1, 1, fp); + } +} + +#define WRITE_STR(str) VSIFWriteL(str, 1, strlen(str), fp) +#define WRITE_ESCAPED(str) WriteEscaped(str, fp) + +void myNotifyNodesFunc (unsigned int nNodes, OSMNode* pasNodes, OSMContext* psOSMContext, void* user_data) +{ + VSILFILE* fp = (VSILFILE*) user_data; + int k; + + for(k=0;kpasTags; + struct tm mytm; + + WRITE_STR(" nID); + WRITE_STR("\" lat=\""); + VSIFPrintfL(fp, "%.7f", psNode->dfLat); + WRITE_STR("\" lon=\""); + VSIFPrintfL(fp, "%.7f", psNode->dfLon); + WRITE_STR("\" version=\""); + VSIFPrintfL(fp, "%d", psNode->sInfo.nVersion); + WRITE_STR("\" changeset=\""); + VSIFPrintfL(fp, "%d", (int) psNode->sInfo.nChangeset); + if (psNode->sInfo.nUID >= 0) + { + WRITE_STR("\" user=\""); + WRITE_ESCAPED(psNode->sInfo.pszUserSID); + WRITE_STR("\" uid=\""); + VSIFPrintfL(fp, "%d", psNode->sInfo.nUID); + } + + if( !(psNode->sInfo.bTimeStampIsStr) ) + { + WRITE_STR("\" timestamp=\""); + myCPLUnixTimeToYMDHMS(psNode->sInfo.ts.nTimeStamp, &mytm); + VSIFPrintfL(fp, "%04d-%02d-%02dT%02d:%02d:%02dZ", + 1900 + mytm.tm_year, mytm.tm_mon + 1, mytm.tm_mday, + mytm.tm_hour, mytm.tm_min, mytm.tm_sec); + } + else if (psNode->sInfo.ts.pszTimeStamp != NULL && + psNode->sInfo.ts.pszTimeStamp[0] != '\0') + { + WRITE_STR("\" timestamp=\""); + WRITE_STR(psNode->sInfo.ts.pszTimeStamp); + } + + if (psNode->nTags) + { + WRITE_STR("\">\n"); + for(l=0;lnTags;l++) + { + WRITE_STR(" \n"); + } + WRITE_STR(" \n"); + } + else + { + WRITE_STR("\"/>\n"); + } + } +} + +void myNotifyWayFunc (OSMWay* psWay, OSMContext* psOSMContext, void* user_data) +{ + VSILFILE* fp = (VSILFILE*) user_data; + + int l; + struct tm mytm; + + WRITE_STR(" nID); + WRITE_STR("\" version=\""); + VSIFPrintfL(fp, "%d", psWay->sInfo.nVersion); + WRITE_STR("\" changeset=\""); + VSIFPrintfL(fp, "%d", (int) psWay->sInfo.nChangeset); + if (psWay->sInfo.nUID >= 0) + { + WRITE_STR("\" uid=\""); + VSIFPrintfL(fp, "%d", psWay->sInfo.nUID); + WRITE_STR("\" user=\""); + WRITE_ESCAPED(psWay->sInfo.pszUserSID); + } + + if( !(psWay->sInfo.bTimeStampIsStr) ) + { + WRITE_STR("\" timestamp=\""); + myCPLUnixTimeToYMDHMS(psWay->sInfo.ts.nTimeStamp, &mytm); + VSIFPrintfL(fp, "%04d-%02d-%02dT%02d:%02d:%02dZ", + 1900 + mytm.tm_year, mytm.tm_mon + 1, mytm.tm_mday, + mytm.tm_hour, mytm.tm_min, mytm.tm_sec); + } + else if (psWay->sInfo.ts.pszTimeStamp != NULL && + psWay->sInfo.ts.pszTimeStamp[0] != '\0') + { + WRITE_STR("\" timestamp=\""); + WRITE_STR(psWay->sInfo.ts.pszTimeStamp); + } + + WRITE_STR("\">\n"); + + for(l=0;lnRefs;l++) + VSIFPrintfL(fp, " \n", (int)psWay->panNodeRefs[l]); + + for(l=0;lnTags;l++) + { + WRITE_STR(" pasTags[l].pszK); + WRITE_STR("\" v=\""); + WRITE_ESCAPED(psWay->pasTags[l].pszV); + WRITE_STR("\" />\n"); + } + VSIFPrintfL(fp, " \n"); +} + +void myNotifyRelationFunc (OSMRelation* psRelation, OSMContext* psOSMContext, void* user_data) +{ + VSILFILE* fp = (VSILFILE*) user_data; + + int l; + const OSMTag* pasTags = psRelation->pasTags; + const OSMMember* pasMembers = psRelation->pasMembers; + struct tm mytm; + + WRITE_STR(" nID); + WRITE_STR("\" version=\""); + VSIFPrintfL(fp, "%d", psRelation->sInfo.nVersion); + WRITE_STR("\" changeset=\""); + VSIFPrintfL(fp, "%d", (int) psRelation->sInfo.nChangeset); + if (psRelation->sInfo.nUID >= 0) + { + WRITE_STR("\" uid=\""); + VSIFPrintfL(fp, "%d", psRelation->sInfo.nUID); + WRITE_STR("\" user=\""); + WRITE_ESCAPED(psRelation->sInfo.pszUserSID); + } + + if( !(psRelation->sInfo.bTimeStampIsStr) ) + { + myCPLUnixTimeToYMDHMS(psRelation->sInfo.ts.nTimeStamp, &mytm); + WRITE_STR("\" timestamp=\""); + VSIFPrintfL(fp, "%04d-%02d-%02dT%02d:%02d:%02dZ", + 1900 + mytm.tm_year, mytm.tm_mon + 1, mytm.tm_mday, + mytm.tm_hour, mytm.tm_min, mytm.tm_sec); + } + else if (psRelation->sInfo.ts.pszTimeStamp != NULL && + psRelation->sInfo.ts.pszTimeStamp[0] != '\0') + { + WRITE_STR("\" timestamp=\""); + WRITE_STR(psRelation->sInfo.ts.pszTimeStamp); + } + + WRITE_STR("\">\n"); + + for(l=0;lnMembers;l++) + { + WRITE_STR(" \n"); + } + + for(l=0;lnTags;l++) + { + WRITE_STR(" \n"); + } + VSIFPrintfL(fp, " \n"); +} + +void myNotifyBoundsFunc (double dfXMin, double dfYMin, double dfXMax, double dfYMax, OSMContext* psOSMContext, void* user_data) +{ + VSILFILE* fp = (VSILFILE*) user_data; + VSIFPrintfL(fp, " \n", + dfYMin, dfXMin, dfYMax, dfXMax); +} + +int main(int argc, char* argv[]) +{ + OSMContext* psContext; + const char* pszSrcFilename; + const char* pszDstFilename; + VSILFILE* fp; + + if( argc != 3 ) + { + fprintf(stderr, "Usage: osm2osm input.pbf output.osm\n"); + exit(1); + } + + pszSrcFilename = argv[1]; + pszDstFilename = argv[2]; + + fp = VSIFOpenL(pszDstFilename, "wt"); + if( fp == NULL ) + { + fprintf(stderr, "Cannot create %s.\n", pszDstFilename); + exit(1); + } + + psContext = OSM_Open( pszSrcFilename, + myNotifyNodesFunc, + myNotifyWayFunc, + myNotifyRelationFunc, + myNotifyBoundsFunc, + fp ); + if( psContext == NULL ) + { + fprintf(stderr, "Cannot process %s.\n", pszSrcFilename); + exit(1); + } + + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + + while( OSM_ProcessBlock(psContext) == OSM_OK ); + + VSIFPrintfL(fp, "\n"); + + OSM_Close( psContext ); + + VSIFCloseL(fp); + + return 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/osm_parser.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/osm_parser.cpp new file mode 100644 index 000000000..afb5a63b7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/osm_parser.cpp @@ -0,0 +1,2443 @@ +/****************************************************************************** + * $Id: osm_parser.cpp 28435 2015-02-07 14:35:34Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Author: Even Rouault, + * Purpose: OSM XML and OSM PBF parser + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "osm_parser.h" +#include "gpb.h" + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_vsi.h" + +#ifdef HAVE_EXPAT +#include "ogr_expat.h" +#endif + +/* The buffer that are passed to GPB decoding are extended with 0's */ +/* to be sure that we will be able to read a single 64bit value without */ +/* doing checks for each byte */ +#define EXTRA_BYTES 1 + +#define XML_BUFSIZE 64*1024 + +CPL_CVSID("$Id: osm_parser.cpp 28435 2015-02-07 14:35:34Z rouault $"); + +/************************************************************************/ +/* INIT_INFO() */ +/************************************************************************/ + +#define INIT_INFO(sInfo) \ + sInfo.ts.nTimeStamp = 0; \ + sInfo.nChangeset = 0; \ + sInfo.nVersion = 0; \ + sInfo.nUID = 0; \ + sInfo.bTimeStampIsStr = 0; \ + sInfo.pszUserSID = NULL; +/* \ sInfo.nVisible = 1; */ + + +/************************************************************************/ +/* _OSMContext */ +/************************************************************************/ + +struct _OSMContext +{ + char *pszStrBuf; + int *panStrOff; + unsigned int nStrCount; + unsigned int nStrAllocated; + + OSMNode *pasNodes; + unsigned int nNodesAllocated; + + OSMTag *pasTags; + unsigned int nTagsAllocated; + + OSMMember *pasMembers; + unsigned int nMembersAllocated; + + GIntBig *panNodeRefs; + unsigned int nNodeRefsAllocated; + + int nGranularity; + int nDateGranularity; + GIntBig nLatOffset; + GIntBig nLonOffset; + + unsigned int nBlobSizeAllocated; + GByte *pabyBlob; + + GByte *pabyUncompressed; + unsigned int nUncompressedAllocated; + +#ifdef HAVE_EXPAT + XML_Parser hXMLParser; + int bEOF; + int bStopParsing; + int bHasFoundFeature; + int nWithoutEventCounter; + int nDataHandlerCounter; + + unsigned int nStrLength; + unsigned int nTags; + + int bInNode; + int bInWay; + int bInRelation; + + OSMWay sWay; + OSMRelation sRelation; + + int bTryToFetchBounds; +#endif + + VSILFILE *fp; + + int bPBF; + + double dfLeft; + double dfRight; + double dfTop; + double dfBottom; + + GUIntBig nBytesRead; + + NotifyNodesFunc pfnNotifyNodes; + NotifyWayFunc pfnNotifyWay; + NotifyRelationFunc pfnNotifyRelation; + NotifyBoundsFunc pfnNotifyBounds; + void *user_data; +}; + +/************************************************************************/ +/* ReadBlobHeader() */ +/************************************************************************/ + +#define BLOBHEADER_IDX_TYPE 1 +#define BLOBHEADER_IDX_INDEXDATA 2 +#define BLOBHEADER_IDX_DATASIZE 3 + +typedef enum +{ + BLOB_UNKNOW, + BLOB_OSMHEADER, + BLOB_OSMDATA +} BlobType; + +static +int ReadBlobHeader(GByte* pabyData, GByte* pabyDataLimit, + unsigned int* pnBlobSize, BlobType* peBlobType) +{ + *pnBlobSize = 0; + *peBlobType = BLOB_UNKNOW; + + while(pabyData < pabyDataLimit) + { + int nKey; + READ_FIELD_KEY(nKey); + + if (nKey == MAKE_KEY(BLOBHEADER_IDX_TYPE, WT_DATA)) + { + unsigned int nDataLength; + READ_SIZE(pabyData, pabyDataLimit, nDataLength); + + if (nDataLength == 7 && memcmp(pabyData, "OSMData", 7) == 0) + { + *peBlobType = BLOB_OSMDATA; + } + else if (nDataLength == 9 && memcmp(pabyData, "OSMHeader", 9) == 0) + { + *peBlobType = BLOB_OSMHEADER; + } + + pabyData += nDataLength; + } + else if (nKey == MAKE_KEY(BLOBHEADER_IDX_INDEXDATA, WT_DATA)) + { + /* Ignored if found */ + unsigned int nDataLength; + READ_SIZE(pabyData, pabyDataLimit, nDataLength); + pabyData += nDataLength; + } + else if (nKey == MAKE_KEY(BLOBHEADER_IDX_DATASIZE, WT_VARINT)) + { + unsigned int nBlobSize; + READ_VARUINT32(pabyData, pabyDataLimit, nBlobSize); + /* printf("nBlobSize = %d\n", nBlobSize); */ + *pnBlobSize = nBlobSize; + } + else + { + SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE); + } + } + + return pabyData == pabyDataLimit; + +end_error: + return FALSE; +} + +/************************************************************************/ +/* ReadHeaderBBox() */ +/************************************************************************/ + +#define HEADERBBOX_IDX_LEFT 1 +#define HEADERBBOX_IDX_RIGHT 2 +#define HEADERBBOX_IDX_TOP 3 +#define HEADERBBOX_IDX_BOTTOM 4 + +static +int ReadHeaderBBox(GByte* pabyData, GByte* pabyDataLimit, + OSMContext* psCtxt) +{ + psCtxt->dfLeft = 0.0; + psCtxt->dfRight = 0.0; + psCtxt->dfTop = 0.0; + psCtxt->dfBottom = 0.0; + + /* printf(">ReadHeaderBBox\n"); */ + + while(pabyData < pabyDataLimit) + { + int nKey; + READ_FIELD_KEY(nKey); + + if (nKey == MAKE_KEY(HEADERBBOX_IDX_LEFT, WT_VARINT)) + { + GIntBig nLeft; + READ_VARSINT64(pabyData, pabyDataLimit, nLeft); + psCtxt->dfLeft = nLeft * 1e-9; + } + else if (nKey == MAKE_KEY(HEADERBBOX_IDX_RIGHT, WT_VARINT)) + { + GIntBig nRight; + READ_VARSINT64(pabyData, pabyDataLimit, nRight); + psCtxt->dfRight = nRight * 1e-9; + } + else if (nKey == MAKE_KEY(HEADERBBOX_IDX_TOP, WT_VARINT)) + { + GIntBig nTop; + READ_VARSINT64(pabyData, pabyDataLimit, nTop); + psCtxt->dfTop = nTop * 1e-9; + } + else if (nKey == MAKE_KEY(HEADERBBOX_IDX_BOTTOM, WT_VARINT)) + { + GIntBig nBottom; + READ_VARSINT64(pabyData, pabyDataLimit, nBottom); + psCtxt->dfBottom = nBottom * 1e-9; + } + else + { + SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE); + } + } + + psCtxt->pfnNotifyBounds(psCtxt->dfLeft, psCtxt->dfBottom, + psCtxt->dfRight, psCtxt->dfTop, + psCtxt, psCtxt->user_data); + + /* printf("panStrOff; + + psCtxt->pszStrBuf = pszStrBuf; + + if ((unsigned int)(pabyDataLimit - pabyData) > psCtxt->nStrAllocated) + { + int* panStrOffNew; + psCtxt->nStrAllocated = MAX(psCtxt->nStrAllocated * 2, + (unsigned int)(pabyDataLimit - pabyData)); + panStrOffNew = (int*) VSIRealloc( + panStrOff, psCtxt->nStrAllocated * sizeof(int)); + if( panStrOffNew == NULL ) + GOTO_END_ERROR; + panStrOff = panStrOffNew; + } + + while(pabyData < pabyDataLimit) + { + int nKey; + READ_FIELD_KEY(nKey); + + while (nKey == MAKE_KEY(READSTRINGTABLE_IDX_STRING, WT_DATA)) + { + GByte* pbSaved; + unsigned int nDataLength; + READ_SIZE(pabyData, pabyDataLimit, nDataLength); + + panStrOff[nStrCount ++] = pabyData - (GByte*)pszStrBuf; + pbSaved = &pabyData[nDataLength]; + + pabyData += nDataLength; + + if (pabyData < pabyDataLimit) + { + READ_FIELD_KEY(nKey); + *pbSaved = 0; + /* printf("string[%d] = %s\n", nStrCount-1, pbSaved - nDataLength); */ + } + else + { + *pbSaved = 0; + /* printf("string[%d] = %s\n", nStrCount-1, pbSaved - nDataLength); */ + break; + } + } + + if (pabyData < pabyDataLimit) + { + SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE); + } + } + + psCtxt->panStrOff = panStrOff; + psCtxt->nStrCount = nStrCount; + + return pabyData == pabyDataLimit; + +end_error: + + psCtxt->panStrOff = panStrOff; + psCtxt->nStrCount = nStrCount; + + return FALSE; +} + +/************************************************************************/ +/* ReadDenseNodes() */ +/************************************************************************/ + +#define DENSEINFO_IDX_VERSION 1 +#define DENSEINFO_IDX_TIMESTAMP 2 +#define DENSEINFO_IDX_CHANGESET 3 +#define DENSEINFO_IDX_UID 4 +#define DENSEINFO_IDX_USER_SID 5 +#define DENSEINFO_IDX_VISIBLE 6 + +#define DENSENODES_IDX_ID 1 +#define DENSENODES_IDX_DENSEINFO 5 +#define DENSENODES_IDX_LAT 8 +#define DENSENODES_IDX_LON 9 +#define DENSENODES_IDX_KEYVALS 10 + +static +int ReadDenseNodes(GByte* pabyData, GByte* pabyDataLimit, + OSMContext* psCtxt) +{ + GByte* pabyDataIDs = NULL; + GByte* pabyDataIDsLimit = NULL; + GByte* pabyDataLat = NULL; + GByte* pabyDataLon = NULL; + GByte* apabyData[DENSEINFO_IDX_VISIBLE] = {NULL, NULL, NULL, NULL, NULL, NULL}; + GByte* pabyDataKeyVal = NULL; + + /* printf(">ReadDenseNodes\n"); */ + while(pabyData < pabyDataLimit) + { + int nKey; + READ_FIELD_KEY(nKey); + + if( nKey == MAKE_KEY(DENSENODES_IDX_ID, WT_DATA) ) + { + unsigned int nSize; + + if (pabyDataIDs != NULL) + GOTO_END_ERROR; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + if (nSize > psCtxt->nNodesAllocated) + { + OSMNode* pasNodesNew; + psCtxt->nNodesAllocated = MAX(psCtxt->nNodesAllocated * 2, + nSize); + pasNodesNew = (OSMNode*) VSIRealloc( + psCtxt->pasNodes, psCtxt->nNodesAllocated * sizeof(OSMNode)); + if( pasNodesNew == NULL ) + GOTO_END_ERROR; + psCtxt->pasNodes = pasNodesNew; + } + + pabyDataIDs = pabyData; + pabyDataIDsLimit = pabyData + nSize; + pabyData += nSize; + } + else if( nKey == MAKE_KEY(DENSENODES_IDX_DENSEINFO, WT_DATA) ) + { + unsigned int nSize; + GByte* pabyDataNewLimit; + + READ_SIZE(pabyData, pabyDataLimit, nSize); + + /* Inline reading of DenseInfo structure */ + + pabyDataNewLimit = pabyData + nSize; + while(pabyData < pabyDataNewLimit) + { + int nFieldNumber; + READ_FIELD_KEY(nKey); + + nFieldNumber = GET_FIELDNUMBER(nKey); + if (GET_WIRETYPE(nKey) == WT_DATA && + nFieldNumber >= DENSEINFO_IDX_VERSION && nFieldNumber <= DENSEINFO_IDX_VISIBLE) + { + if( apabyData[nFieldNumber - 1] != NULL) GOTO_END_ERROR; + READ_SIZE(pabyData, pabyDataNewLimit, nSize); + + apabyData[nFieldNumber - 1] = pabyData; + pabyData += nSize; + } + else + { + SKIP_UNKNOWN_FIELD(pabyData, pabyDataNewLimit, TRUE); + } + } + + if( pabyData != pabyDataNewLimit ) + GOTO_END_ERROR; + } + else if( nKey == MAKE_KEY(DENSENODES_IDX_LAT, WT_DATA) ) + { + unsigned int nSize; + if (pabyDataLat != NULL) + GOTO_END_ERROR; + READ_SIZE(pabyData, pabyDataLimit, nSize); + pabyDataLat = pabyData; + pabyData += nSize; + } + else if( nKey == MAKE_KEY(DENSENODES_IDX_LON, WT_DATA) ) + { + unsigned int nSize; + if (pabyDataLon != NULL) + GOTO_END_ERROR; + READ_SIZE(pabyData, pabyDataLimit, nSize); + pabyDataLon = pabyData; + pabyData += nSize; + } + else if( nKey == MAKE_KEY(DENSENODES_IDX_KEYVALS, WT_DATA) ) + { + unsigned int nSize; + if( pabyDataKeyVal != NULL ) + GOTO_END_ERROR; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + pabyDataKeyVal = pabyData; + + if (nSize > psCtxt->nTagsAllocated) + { + OSMTag* pasTagsNew; + + psCtxt->nTagsAllocated = MAX( + psCtxt->nTagsAllocated * 2, nSize); + pasTagsNew = (OSMTag*) VSIRealloc( + psCtxt->pasTags, + psCtxt->nTagsAllocated * sizeof(OSMTag)); + if( pasTagsNew == NULL ) + GOTO_END_ERROR; + psCtxt->pasTags = pasTagsNew; + } + + pabyData += nSize; + } + else + { + SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE); + } + } + + if( pabyData != pabyDataLimit ) + GOTO_END_ERROR; + + if( pabyDataIDs != NULL && pabyDataLat != NULL && pabyDataLon != NULL ) + { + GByte* pabyDataVersion = apabyData[DENSEINFO_IDX_VERSION - 1]; + GByte* pabyDataTimeStamp = apabyData[DENSEINFO_IDX_TIMESTAMP - 1]; + GByte* pabyDataChangeset = apabyData[DENSEINFO_IDX_CHANGESET - 1]; + GByte* pabyDataUID = apabyData[DENSEINFO_IDX_UID - 1]; + GByte* pabyDataUserSID = apabyData[DENSEINFO_IDX_USER_SID - 1]; + /* GByte* pabyDataVisible = apabyData[DENSEINFO_IDX_VISIBLE - 1]; */ + + GIntBig nID = 0; + GIntBig nLat = 0; + GIntBig nLon = 0; + GIntBig nTimeStamp = 0; + GIntBig nChangeset = 0; + int nUID = 0; + unsigned int nUserSID = 0; + int nTags = 0; + int nNodes = 0; + + const char* pszStrBuf = psCtxt->pszStrBuf; + int* panStrOff = psCtxt->panStrOff; + const unsigned int nStrCount = psCtxt->nStrCount; + OSMTag* pasTags = psCtxt->pasTags; + OSMNode* pasNodes = psCtxt->pasNodes; + + int nVersion = 0; + /* int nVisible = 1; */ + + while(pabyDataIDs < pabyDataIDsLimit) + { + GIntBig nDelta1, nDelta2; + int nKVIndexStart = nTags; + + READ_VARSINT64_NOCHECK(pabyDataIDs, pabyDataIDsLimit, nDelta1); + READ_VARSINT64(pabyDataLat, pabyDataLimit, nDelta2); + nID += nDelta1; + nLat += nDelta2; + + READ_VARSINT64(pabyDataLon, pabyDataLimit, nDelta1); + nLon += nDelta1; + + if( pabyDataTimeStamp ) + { + READ_VARSINT64(pabyDataTimeStamp, pabyDataLimit, nDelta2); + nTimeStamp += nDelta2; + } + if( pabyDataChangeset ) + { + READ_VARSINT64(pabyDataChangeset, pabyDataLimit, nDelta1); + nChangeset += nDelta1; + } + if( pabyDataVersion ) + { + READ_VARINT32(pabyDataVersion, pabyDataLimit, nVersion); + } + if( pabyDataUID ) + { + int nDeltaUID; + READ_VARSINT32(pabyDataUID, pabyDataLimit, nDeltaUID); + nUID += nDeltaUID; + } + if( pabyDataUserSID ) + { + int nDeltaUserSID; + READ_VARSINT32(pabyDataUserSID, pabyDataLimit, nDeltaUserSID); + nUserSID += nDeltaUserSID; + if (nUserSID >= nStrCount) + GOTO_END_ERROR; + } + /* if( pabyDataVisible ) + READ_VARINT32(pabyDataVisible, pabyDataLimit, nVisible); */ + + if( pabyDataKeyVal ) + { + while (TRUE) + { + unsigned int nKey, nVal; + READ_VARUINT32(pabyDataKeyVal, pabyDataLimit, nKey); + if (nKey == 0) + break; + if (nKey >= nStrCount) + GOTO_END_ERROR; + + READ_VARUINT32(pabyDataKeyVal, pabyDataLimit, nVal); + if (nVal >= nStrCount) + GOTO_END_ERROR; + + pasTags[nTags].pszK = pszStrBuf + panStrOff[nKey]; + pasTags[nTags].pszV = pszStrBuf + panStrOff[nVal]; + nTags ++; + + /* printf("nKey = %d, nVal = %d\n", nKey, nVal); */ + } + } + + if( nTags > nKVIndexStart ) + pasNodes[nNodes].pasTags = pasTags + nKVIndexStart; + else + pasNodes[nNodes].pasTags = NULL; + pasNodes[nNodes].nTags = nTags - nKVIndexStart; + + pasNodes[nNodes].nID = nID; + pasNodes[nNodes].dfLat = .000000001 * (psCtxt->nLatOffset + (psCtxt->nGranularity * nLat)); + pasNodes[nNodes].dfLon = .000000001 * (psCtxt->nLonOffset + (psCtxt->nGranularity * nLon)); + pasNodes[nNodes].sInfo.bTimeStampIsStr = FALSE; + pasNodes[nNodes].sInfo.ts.nTimeStamp = nTimeStamp; + pasNodes[nNodes].sInfo.nChangeset = nChangeset; + pasNodes[nNodes].sInfo.nVersion = nVersion; + pasNodes[nNodes].sInfo.nUID = nUID; + pasNodes[nNodes].sInfo.pszUserSID = pszStrBuf + panStrOff[nUserSID]; + /* pasNodes[nNodes].sInfo.nVisible = nVisible; */ + nNodes ++; + /* printf("nLat = " CPL_FRMT_GIB "\n", nLat); printf("nLon = " CPL_FRMT_GIB "\n", nLon); */ + } + + psCtxt->pfnNotifyNodes(nNodes, pasNodes, psCtxt, psCtxt->user_data); + + if(pabyDataIDs != pabyDataIDsLimit) + GOTO_END_ERROR; + } + + /* printf("ReadOSMInfo\n"); */ + while(pabyData < pabyDataLimit) + { + int nKey; + READ_FIELD_KEY(nKey); + + if (nKey == MAKE_KEY(INFO_IDX_VERSION, WT_VARINT)) + { + READ_VARINT32(pabyData, pabyDataLimit, psInfo->nVersion); + } + else if (nKey == MAKE_KEY(INFO_IDX_TIMESTAMP, WT_VARINT)) + { + READ_VARINT64(pabyData, pabyDataLimit, psInfo->ts.nTimeStamp); + } + else if (nKey == MAKE_KEY(INFO_IDX_CHANGESET, WT_VARINT)) + { + READ_VARINT64(pabyData, pabyDataLimit, psInfo->nChangeset); + } + else if (nKey == MAKE_KEY(INFO_IDX_UID, WT_VARINT)) + { + READ_VARINT32(pabyData, pabyDataLimit, psInfo->nUID); + } + else if (nKey == MAKE_KEY(INFO_IDX_USER_SID, WT_VARINT)) + { + unsigned int nUserSID; + READ_VARUINT32(pabyData, pabyDataLimit, nUserSID); + if( nUserSID < psContext->nStrCount) + psInfo->pszUserSID = psContext->pszStrBuf + + psContext->panStrOff[nUserSID]; + } + else if (nKey == MAKE_KEY(INFO_IDX_VISIBLE, WT_VARINT)) + { + SKIP_VARINT(pabyData, pabyDataLimit); + //int nVisible; + //READ_VARINT32(pabyData, pabyDataLimit, /*psInfo->*/nVisible); + } + else + { + SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE); + } + } + /* printf("ReadNode\n"); */ + while(pabyData < pabyDataLimit) + { + int nKey; + READ_FIELD_KEY(nKey); + + if (nKey == MAKE_KEY(NODE_IDX_ID, WT_VARINT)) + { + READ_VARSINT64_NOCHECK(pabyData, pabyDataLimit, sNode.nID); + } + else if (nKey == MAKE_KEY(NODE_IDX_LAT, WT_VARINT)) + { + GIntBig nLat; + READ_VARSINT64_NOCHECK(pabyData, pabyDataLimit, nLat); + sNode.dfLat = .000000001 * (psCtxt->nLatOffset + (psCtxt->nGranularity * nLat)); + } + else if (nKey == MAKE_KEY(NODE_IDX_LON, WT_VARINT)) + { + GIntBig nLon; + READ_VARSINT64_NOCHECK(pabyData, pabyDataLimit, nLon); + sNode.dfLon = .000000001 * (psCtxt->nLonOffset + (psCtxt->nGranularity * nLon)); + } + else if (nKey == MAKE_KEY(NODE_IDX_KEYS, WT_DATA)) + { + unsigned int nSize; + GByte* pabyDataNewLimit; + if (sNode.nTags != 0) + GOTO_END_ERROR; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + if (nSize > psCtxt->nTagsAllocated) + { + OSMTag* pasTagsNew; + + psCtxt->nTagsAllocated = MAX( + psCtxt->nTagsAllocated * 2, nSize); + pasTagsNew = (OSMTag*) VSIRealloc( + psCtxt->pasTags, + psCtxt->nTagsAllocated * sizeof(OSMTag)); + if( pasTagsNew == NULL ) + GOTO_END_ERROR; + psCtxt->pasTags = pasTagsNew; + } + + pabyDataNewLimit = pabyData + nSize; + while (pabyData < pabyDataNewLimit) + { + unsigned int nKey; + READ_VARUINT32(pabyData, pabyDataNewLimit, nKey); + + if (nKey >= psCtxt->nStrCount) + GOTO_END_ERROR; + + psCtxt->pasTags[sNode.nTags].pszK = psCtxt->pszStrBuf + + psCtxt->panStrOff[nKey]; + psCtxt->pasTags[sNode.nTags].pszV = NULL; + sNode.nTags ++; + } + if (pabyData != pabyDataNewLimit) + GOTO_END_ERROR; + } + else if (nKey == MAKE_KEY(NODE_IDX_VALS, WT_DATA)) + { + //unsigned int nSize; + unsigned int nIter = 0; + if (sNode.nTags == 0) + GOTO_END_ERROR; + //READ_VARUINT32(pabyData, pabyDataLimit, nSize); + SKIP_VARINT(pabyData, pabyDataLimit); + + for(; nIter < sNode.nTags; nIter ++) + { + unsigned int nVal; + READ_VARUINT32(pabyData, pabyDataLimit, nVal); + + if (nVal >= psCtxt->nStrCount) + GOTO_END_ERROR; + + psCtxt->pasTags[nIter].pszV = psCtxt->pszStrBuf + + psCtxt->panStrOff[nVal]; + } + } + else if (nKey == MAKE_KEY(NODE_IDX_INFO, WT_DATA)) + { + unsigned int nSize; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + if (!ReadOSMInfo(pabyData, pabyDataLimit + nSize, &sNode.sInfo, psCtxt)) + GOTO_END_ERROR; + + pabyData += nSize; + } + else + { + SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE); + } + } + + if( pabyData != pabyDataLimit ) + GOTO_END_ERROR; + + if (sNode.nTags) + sNode.pasTags = psCtxt->pasTags; + else + sNode.pasTags = NULL; + psCtxt->pfnNotifyNodes(1, &sNode, psCtxt, psCtxt->user_data); + + /* printf("ReadWay\n"); */ + while(pabyData < pabyDataLimit) + { + int nKey; + READ_FIELD_KEY(nKey); + + if (nKey == MAKE_KEY(WAY_IDX_ID, WT_VARINT)) + { + READ_VARINT64(pabyData, pabyDataLimit, sWay.nID); + } + else if (nKey == MAKE_KEY(WAY_IDX_KEYS, WT_DATA)) + { + unsigned int nSize; + GByte* pabyDataNewLimit; + if (sWay.nTags != 0) + GOTO_END_ERROR; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + if (nSize > psCtxt->nTagsAllocated) + { + OSMTag* pasTagsNew; + + psCtxt->nTagsAllocated = MAX( + psCtxt->nTagsAllocated * 2, nSize); + pasTagsNew = (OSMTag*) VSIRealloc( + psCtxt->pasTags, + psCtxt->nTagsAllocated * sizeof(OSMTag)); + if( pasTagsNew == NULL ) + GOTO_END_ERROR; + psCtxt->pasTags = pasTagsNew; + } + + pabyDataNewLimit = pabyData + nSize; + while (pabyData < pabyDataNewLimit) + { + unsigned int nKey; + READ_VARUINT32(pabyData, pabyDataNewLimit, nKey); + + if (nKey >= psCtxt->nStrCount) + GOTO_END_ERROR; + + psCtxt->pasTags[sWay.nTags].pszK = psCtxt->pszStrBuf + + psCtxt->panStrOff[nKey]; + psCtxt->pasTags[sWay.nTags].pszV = NULL; + sWay.nTags ++; + } + if (pabyData != pabyDataNewLimit) + GOTO_END_ERROR; + } + else if (nKey == MAKE_KEY(WAY_IDX_VALS, WT_DATA)) + { + //unsigned int nSize; + unsigned int nIter = 0; + if (sWay.nTags == 0) + GOTO_END_ERROR; + //READ_VARUINT32(pabyData, pabyDataLimit, nSize); + SKIP_VARINT(pabyData, pabyDataLimit); + + for(; nIter < sWay.nTags; nIter ++) + { + unsigned int nVal; + READ_VARUINT32(pabyData, pabyDataLimit, nVal); + + if (nVal >= psCtxt->nStrCount) + GOTO_END_ERROR; + + psCtxt->pasTags[nIter].pszV = psCtxt->pszStrBuf + + psCtxt->panStrOff[nVal]; + } + } + else if (nKey == MAKE_KEY(WAY_IDX_INFO, WT_DATA)) + { + unsigned int nSize; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + if (!ReadOSMInfo(pabyData, pabyData + nSize, &sWay.sInfo, psCtxt)) + GOTO_END_ERROR; + + pabyData += nSize; + } + else if (nKey == MAKE_KEY(WAY_IDX_REFS, WT_DATA)) + { + GIntBig nRefVal = 0; + unsigned int nSize; + GByte* pabyDataNewLimit; + if (sWay.nRefs != 0) + GOTO_END_ERROR; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + if (nSize > psCtxt->nNodeRefsAllocated) + { + GIntBig* panNodeRefsNew; + psCtxt->nNodeRefsAllocated = + MAX(psCtxt->nNodeRefsAllocated * 2, nSize); + panNodeRefsNew = (GIntBig*) VSIRealloc( + psCtxt->panNodeRefs, + psCtxt->nNodeRefsAllocated * sizeof(GIntBig)); + if( panNodeRefsNew == NULL ) + GOTO_END_ERROR; + psCtxt->panNodeRefs = panNodeRefsNew; + } + + pabyDataNewLimit = pabyData + nSize; + while (pabyData < pabyDataNewLimit) + { + GIntBig nDeltaRef; + READ_VARSINT64_NOCHECK(pabyData, pabyDataNewLimit, nDeltaRef); + nRefVal += nDeltaRef; + + psCtxt->panNodeRefs[sWay.nRefs ++] = nRefVal; + } + + if (pabyData != pabyDataNewLimit) + GOTO_END_ERROR; + } + else + { + SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE); + } + } + + if( pabyData != pabyDataLimit ) + GOTO_END_ERROR; + + /* printf("pasTags; + else + sWay.pasTags = NULL; + sWay.panNodeRefs = psCtxt->panNodeRefs; + + psCtxt->pfnNotifyWay(&sWay, psCtxt, psCtxt->user_data); + + return TRUE; + +end_error: + /* printf("ReadRelation\n"); */ + while(pabyData < pabyDataLimit) + { + int nKey; + READ_FIELD_KEY(nKey); + + if (nKey == MAKE_KEY(RELATION_IDX_ID, WT_VARINT)) + { + READ_VARINT64(pabyData, pabyDataLimit, sRelation.nID); + } + else if (nKey == MAKE_KEY(RELATION_IDX_KEYS, WT_DATA)) + { + unsigned int nSize; + GByte* pabyDataNewLimit; + if (sRelation.nTags != 0) + GOTO_END_ERROR; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + if (nSize > psCtxt->nTagsAllocated) + { + OSMTag* pasTagsNew; + + psCtxt->nTagsAllocated = MAX( + psCtxt->nTagsAllocated * 2, nSize); + pasTagsNew = (OSMTag*) VSIRealloc( + psCtxt->pasTags, + psCtxt->nTagsAllocated * sizeof(OSMTag)); + if( pasTagsNew == NULL ) + GOTO_END_ERROR; + psCtxt->pasTags = pasTagsNew; + } + + pabyDataNewLimit = pabyData + nSize; + while (pabyData < pabyDataNewLimit) + { + unsigned int nKey; + READ_VARUINT32(pabyData, pabyDataNewLimit, nKey); + + if (nKey >= psCtxt->nStrCount) + GOTO_END_ERROR; + + psCtxt->pasTags[sRelation.nTags].pszK = psCtxt->pszStrBuf + + psCtxt->panStrOff[nKey]; + psCtxt->pasTags[sRelation.nTags].pszV = NULL; + sRelation.nTags ++; + } + if (pabyData != pabyDataNewLimit) + GOTO_END_ERROR; + } + else if (nKey == MAKE_KEY(RELATION_IDX_VALS, WT_DATA)) + { + //unsigned int nSize; + unsigned int nIter = 0; + if (sRelation.nTags == 0) + GOTO_END_ERROR; + //READ_VARUINT32(pabyData, pabyDataLimit, nSize); + SKIP_VARINT(pabyData, pabyDataLimit); + + for(; nIter < sRelation.nTags; nIter ++) + { + unsigned int nVal; + READ_VARUINT32(pabyData, pabyDataLimit, nVal); + + if (nVal >= psCtxt->nStrCount) + GOTO_END_ERROR; + + psCtxt->pasTags[nIter].pszV = psCtxt->pszStrBuf + + psCtxt->panStrOff[nVal]; + } + } + else if (nKey == MAKE_KEY(RELATION_IDX_INFO, WT_DATA)) + { + unsigned int nSize; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + if (!ReadOSMInfo(pabyData, pabyData + nSize, &sRelation.sInfo, psCtxt)) + GOTO_END_ERROR; + + pabyData += nSize; + } + else if (nKey == MAKE_KEY(RELATION_IDX_ROLES_SID, WT_DATA)) + { + unsigned int nSize; + GByte* pabyDataNewLimit; + if (sRelation.nMembers != 0) + GOTO_END_ERROR; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + if (nSize > psCtxt->nMembersAllocated) + { + OSMMember* pasMembersNew; + psCtxt->nMembersAllocated = + MAX(psCtxt->nMembersAllocated * 2, nSize); + pasMembersNew = (OSMMember*) VSIRealloc( + psCtxt->pasMembers, + psCtxt->nMembersAllocated * sizeof(OSMMember)); + if( pasMembersNew == NULL ) + GOTO_END_ERROR; + psCtxt->pasMembers = pasMembersNew; + } + + pabyDataNewLimit = pabyData + nSize; + while (pabyData < pabyDataNewLimit) + { + unsigned int nRoleSID; + READ_VARUINT32(pabyData, pabyDataNewLimit, nRoleSID); + if (nRoleSID >= psCtxt->nStrCount) + GOTO_END_ERROR; + + psCtxt->pasMembers[sRelation.nMembers].pszRole = + psCtxt->pszStrBuf + psCtxt->panStrOff[nRoleSID]; + psCtxt->pasMembers[sRelation.nMembers].nID = 0; + psCtxt->pasMembers[sRelation.nMembers].eType = MEMBER_NODE; + sRelation.nMembers ++; + } + + if (pabyData != pabyDataNewLimit) + GOTO_END_ERROR; + } + else if (nKey == MAKE_KEY(RELATION_IDX_MEMIDS, WT_DATA)) + { + unsigned int nIter = 0; + GIntBig nMemID = 0; + //unsigned int nSize; + if (sRelation.nMembers == 0) + GOTO_END_ERROR; + //READ_VARUINT32(pabyData, pabyDataLimit, nSize); + SKIP_VARINT(pabyData, pabyDataLimit); + + for(; nIter < sRelation.nMembers; nIter++) + { + GIntBig nDeltaMemID; + READ_VARSINT64(pabyData, pabyDataLimit, nDeltaMemID); + nMemID += nDeltaMemID; + + psCtxt->pasMembers[nIter].nID = nMemID; + } + } + else if (nKey == MAKE_KEY(RELATION_IDX_TYPES, WT_DATA)) + { + unsigned int nIter = 0; + unsigned int nSize; + if (sRelation.nMembers == 0) + GOTO_END_ERROR; + READ_SIZE(pabyData, pabyDataLimit, nSize); + if (nSize != sRelation.nMembers) + GOTO_END_ERROR; + + for(; nIter < sRelation.nMembers; nIter++) + { + unsigned int nType = pabyData[nIter]; + if (nType > MEMBER_RELATION) + GOTO_END_ERROR; + + psCtxt->pasMembers[nIter].eType = (OSMMemberType) nType; + } + pabyData += nSize; + } + else + { + SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE); + } + } + /* printf("pasTags; + else + sRelation.pasTags = NULL; + + sRelation.pasMembers = psCtxt->pasMembers; + + psCtxt->pfnNotifyRelation(&sRelation, psCtxt, psCtxt->user_data); + + return TRUE; + +end_error: + /* printf("ReadPrimitiveGroup\n"); */ + while(pabyData < pabyDataLimit) + { + int nKey; + int nFieldNumber; + READ_FIELD_KEY(nKey); + + nFieldNumber = GET_FIELDNUMBER(nKey) - 1; + if( GET_WIRETYPE(nKey) == WT_DATA && + nFieldNumber >= PRIMITIVEGROUP_IDX_NODES - 1 && + nFieldNumber <= PRIMITIVEGROUP_IDX_RELATIONS - 1 ) + { + unsigned int nSize; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + if (!apfnPrimitives[nFieldNumber](pabyData, pabyData + nSize, psCtxt)) + GOTO_END_ERROR; + + pabyData += nSize; + } + else + { + SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE); + } + } + /* printf("pszStrBuf = NULL; + psCtxt->nStrCount = 0; + psCtxt->nGranularity = 100; + psCtxt->nDateGranularity = 1000; + psCtxt->nLatOffset = 0; + psCtxt->nLonOffset = 0; + + while(pabyData < pabyDataLimit) + { + int nKey; + READ_FIELD_KEY(nKey); + + if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_GRANULARITY, WT_VARINT)) + { + READ_VARINT32(pabyData, pabyDataLimit, psCtxt->nGranularity); + } + else if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_DATE_GRANULARITY, WT_VARINT)) + { + READ_VARINT32(pabyData, pabyDataLimit, psCtxt->nDateGranularity); + } + else if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_LAT_OFFSET, WT_VARINT)) + { + READ_VARINT64(pabyData, pabyDataLimit, psCtxt->nLatOffset); + } + else if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_LON_OFFSET, WT_VARINT)) + { + READ_VARINT64(pabyData, pabyDataLimit, psCtxt->nLonOffset); + } + else + { + SKIP_UNKNOWN_FIELD_INLINE(pabyData, pabyDataLimit, FALSE); + } + } + + if (pabyData != pabyDataLimit) + GOTO_END_ERROR; + + pabyData = pabyDataSave; + while(pabyData < pabyDataLimit) + { + int nKey; + READ_FIELD_KEY(nKey); + + if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_STRINGTABLE, WT_DATA)) + { + GByte bSaveAfterByte; + GByte* pbSaveAfterByte; + unsigned int nSize; + if (psCtxt->nStrCount != 0) + GOTO_END_ERROR; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + /* Dirty little trick */ + /* ReadStringTable() will over-write the byte after the */ + /* StringTable message with a NUL charachter, so we backup */ + /* it to be able to restore it just before issuing the next */ + /* READ_FIELD_KEY. Then we will re-NUL it to have valid */ + /* NUL terminated strings */ + /* This trick enable us to keep the strings where there are */ + /* in RAM */ + pbSaveAfterByte = pabyData + nSize; + bSaveAfterByte = *pbSaveAfterByte; + + if (!ReadStringTable(pabyData, pabyData + nSize, + psCtxt)) + GOTO_END_ERROR; + + pabyData += nSize; + + *pbSaveAfterByte = bSaveAfterByte; + if (pabyData == pabyDataLimit) + break; + + READ_FIELD_KEY(nKey); + *pbSaveAfterByte = 0; + + if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_STRINGTABLE, WT_DATA)) + GOTO_END_ERROR; + + /* Yes we go on ! */ + } + + if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_PRIMITIVEGROUP, WT_DATA)) + { + unsigned int nSize; + READ_SIZE(pabyData, pabyDataLimit, nSize); + + if (!ReadPrimitiveGroup(pabyData, pabyData + nSize, + psCtxt)) + GOTO_END_ERROR; + + pabyData += nSize; + } + else + { + SKIP_UNKNOWN_FIELD_INLINE(pabyData, pabyDataLimit, FALSE); + } + } + + return pabyData == pabyDataLimit; + +end_error: + + return FALSE; +} + +/************************************************************************/ +/* ReadBlob() */ +/************************************************************************/ + +#define BLOB_IDX_RAW 1 +#define BLOB_IDX_RAW_SIZE 2 +#define BLOB_IDX_ZLIB_DATA 3 + +static +int ReadBlob(GByte* pabyData, unsigned int nDataSize, BlobType eType, + OSMContext* psCtxt) +{ + unsigned int nUncompressedSize = 0; + int bRet = TRUE; + GByte* pabyDataLimit = pabyData + nDataSize; + + while(pabyData < pabyDataLimit) + { + int nKey; + READ_FIELD_KEY(nKey); + + if (nKey == MAKE_KEY(BLOB_IDX_RAW, WT_DATA)) + { + unsigned int nDataLength; + READ_SIZE(pabyData, pabyDataLimit, nDataLength); + if (nDataLength > 64 * 1024 * 1024) GOTO_END_ERROR; + + /* printf("raw data size = %d\n", nDataLength); */ + + if (eType == BLOB_OSMHEADER) + { + bRet = ReadOSMHeader(pabyData, pabyData + nDataLength, psCtxt); + } + else if (eType == BLOB_OSMDATA) + { + bRet = ReadPrimitiveBlock(pabyData, pabyData + nDataLength, + psCtxt); + } + + pabyData += nDataLength; + } + else if (nKey == MAKE_KEY(BLOB_IDX_RAW_SIZE, WT_VARINT)) + { + READ_VARUINT32(pabyData, pabyDataLimit, nUncompressedSize); + /* printf("nUncompressedSize = %d\n", nUncompressedSize); */ + } + else if (nKey == MAKE_KEY(BLOB_IDX_ZLIB_DATA, WT_DATA)) + { + unsigned int nZlibCompressedSize; + READ_VARUINT32(pabyData, pabyDataLimit, nZlibCompressedSize); + if (CHECK_OOB && nZlibCompressedSize > nDataSize) GOTO_END_ERROR; + + /* printf("nZlibCompressedSize = %d\n", nZlibCompressedSize); */ + + if (nUncompressedSize != 0) + { + void* pOut; + + if (nUncompressedSize > psCtxt->nUncompressedAllocated) + { + GByte* pabyUncompressedNew; + psCtxt->nUncompressedAllocated = + MAX(psCtxt->nUncompressedAllocated * 2, nUncompressedSize); + pabyUncompressedNew = (GByte*)VSIRealloc(psCtxt->pabyUncompressed, + psCtxt->nUncompressedAllocated + EXTRA_BYTES); + if( pabyUncompressedNew == NULL ) + GOTO_END_ERROR; + psCtxt->pabyUncompressed = pabyUncompressedNew; + } + memset(psCtxt->pabyUncompressed + nUncompressedSize, 0, EXTRA_BYTES); + + /* printf("inflate %d -> %d\n", nZlibCompressedSize, nUncompressedSize); */ + + pOut = CPLZLibInflate( pabyData, nZlibCompressedSize, + psCtxt->pabyUncompressed, nUncompressedSize, + NULL ); + if( pOut == NULL ) + GOTO_END_ERROR; + + if (eType == BLOB_OSMHEADER) + { + bRet = ReadOSMHeader(psCtxt->pabyUncompressed, + psCtxt->pabyUncompressed + nUncompressedSize, + psCtxt); + } + else if (eType == BLOB_OSMDATA) + { + bRet = ReadPrimitiveBlock(psCtxt->pabyUncompressed, + psCtxt->pabyUncompressed + nUncompressedSize, + psCtxt); + } + } + + pabyData += nZlibCompressedSize; + } + else + { + SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE); + } + } + + return bRet; + +end_error: + return FALSE; +} + +/************************************************************************/ +/* EmptyNotifyNodesFunc() */ +/************************************************************************/ + +static void EmptyNotifyNodesFunc(CPL_UNUSED unsigned int nNodes, + CPL_UNUSED OSMNode* pasNodes, + CPL_UNUSED OSMContext* psCtxt, + CPL_UNUSED void* user_data) +{ +} + + +/************************************************************************/ +/* EmptyNotifyWayFunc() */ +/************************************************************************/ + +static void EmptyNotifyWayFunc(CPL_UNUSED OSMWay* psWay, + CPL_UNUSED OSMContext* psCtxt, + CPL_UNUSED void* user_data) +{ +} + +/************************************************************************/ +/* EmptyNotifyRelationFunc() */ +/************************************************************************/ + +static void EmptyNotifyRelationFunc(CPL_UNUSED OSMRelation* psRelation, + CPL_UNUSED OSMContext* psCtxt, + CPL_UNUSED void* user_data) +{ +} + +/************************************************************************/ +/* EmptyNotifyBoundsFunc() */ +/************************************************************************/ + +static void EmptyNotifyBoundsFunc( CPL_UNUSED double dfXMin, + CPL_UNUSED double dfYMin, + CPL_UNUSED double dfXMax, + CPL_UNUSED double dfYMax, + CPL_UNUSED OSMContext* psCtxt, + CPL_UNUSED void* user_data ) +{ +} + +#ifdef HAVE_EXPAT + +/************************************************************************/ +/* OSM_AddString() */ +/************************************************************************/ + +static const char* OSM_AddString(OSMContext* psCtxt, const char* pszStr) +{ + char* pszRet; + int nLen = (int)strlen(pszStr); + if( psCtxt->nStrLength + nLen + 1 > psCtxt->nStrAllocated ) + { + CPLError(CE_Failure, CPLE_AppDefined, "String buffer too small"); + return ""; + } + pszRet = psCtxt->pszStrBuf + psCtxt->nStrLength; + memcpy(pszRet, pszStr, nLen); + pszRet[nLen] = '\0'; + psCtxt->nStrLength += nLen + 1; + return pszRet; +} + + +/************************************************************************/ +/* OSM_Atoi64() */ +/************************************************************************/ + +static GIntBig OSM_Atoi64( const char *pszString ) +{ + GIntBig iValue; + +#if defined(__MSVCRT__) || (defined(WIN32) && defined(_MSC_VER)) + iValue = (GIntBig)_atoi64( pszString ); +# elif HAVE_ATOLL + iValue = atoll( pszString ); +#else + iValue = atol( pszString ); +#endif + + return iValue; +} + +/************************************************************************/ +/* OSM_XML_startElementCbk() */ +/************************************************************************/ + +static void XMLCALL OSM_XML_startElementCbk(void *pUserData, const char *pszName, + const char **ppszAttr) +{ + OSMContext* psCtxt = (OSMContext*) pUserData; + const char** ppszIter = ppszAttr; + + if (psCtxt->bStopParsing) return; + + psCtxt->nWithoutEventCounter = 0; + + if( psCtxt->bTryToFetchBounds ) + { + if( strcmp(pszName, "bounds") == 0 || + strcmp(pszName, "bound") == 0 /* osmosis uses bound */ ) + { + int nCountCoords = 0; + + psCtxt->bTryToFetchBounds = FALSE; + + if( ppszIter ) + { + while( ppszIter[0] != NULL ) + { + if( strcmp(ppszIter[0], "minlon") == 0 ) + { + psCtxt->dfLeft = CPLAtof( ppszIter[1] ); + nCountCoords ++; + } + else if( strcmp(ppszIter[0], "minlat") == 0 ) + { + psCtxt->dfBottom = CPLAtof( ppszIter[1] ); + nCountCoords ++; + } + else if( strcmp(ppszIter[0], "maxlon") == 0 ) + { + psCtxt->dfRight = CPLAtof( ppszIter[1] ); + nCountCoords ++; + } + else if( strcmp(ppszIter[0], "maxlat") == 0 ) + { + psCtxt->dfTop = CPLAtof( ppszIter[1] ); + nCountCoords ++; + } + else if( strcmp(ppszIter[0], "box") == 0 /* osmosis uses box */ ) + { + char** papszTokens = CSLTokenizeString2( ppszIter[1], ",", 0 ); + if( CSLCount(papszTokens) == 4 ) + { + psCtxt->dfBottom = CPLAtof( papszTokens[0] ); + psCtxt->dfLeft = CPLAtof( papszTokens[1] ); + psCtxt->dfTop = CPLAtof( papszTokens[2] ); + psCtxt->dfRight = CPLAtof( papszTokens[3] ); + nCountCoords = 4; + } + CSLDestroy(papszTokens); + } + ppszIter += 2; + } + } + + if( nCountCoords == 4 ) + { + psCtxt->pfnNotifyBounds(psCtxt->dfLeft, psCtxt->dfBottom, + psCtxt->dfRight, psCtxt->dfTop, + psCtxt, psCtxt->user_data); + } + } + } + + if( !psCtxt->bInNode && !psCtxt->bInWay && !psCtxt->bInRelation && + strcmp(pszName, "node") == 0 ) + { + psCtxt->bInNode = TRUE; + psCtxt->bTryToFetchBounds = FALSE; + + psCtxt->nStrLength = 0; + psCtxt->pszStrBuf[0] = '\0'; + psCtxt->nTags = 0; + + memset( &(psCtxt->pasNodes[0]), 0, sizeof(OSMNode) ); + psCtxt->pasNodes[0].sInfo.pszUserSID = ""; + + if( ppszIter ) + { + while( ppszIter[0] != NULL ) + { + if( strcmp(ppszIter[0], "id") == 0 ) + { + psCtxt->pasNodes[0].nID = OSM_Atoi64( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "lat") == 0 ) + { + psCtxt->pasNodes[0].dfLat = CPLAtof( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "lon") == 0 ) + { + psCtxt->pasNodes[0].dfLon = CPLAtof( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "version") == 0 ) + { + psCtxt->pasNodes[0].sInfo.nVersion = atoi( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "changeset") == 0 ) + { + psCtxt->pasNodes[0].sInfo.nChangeset = OSM_Atoi64( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "user") == 0 ) + { + psCtxt->pasNodes[0].sInfo.pszUserSID = OSM_AddString(psCtxt, ppszIter[1]); + } + else if( strcmp(ppszIter[0], "uid") == 0 ) + { + psCtxt->pasNodes[0].sInfo.nUID = atoi( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "timestamp") == 0 ) + { + psCtxt->pasNodes[0].sInfo.ts.pszTimeStamp = OSM_AddString(psCtxt, ppszIter[1]); + psCtxt->pasNodes[0].sInfo.bTimeStampIsStr = 1; + } + ppszIter += 2; + } + } + } + + else if( !psCtxt->bInNode && !psCtxt->bInWay && !psCtxt->bInRelation && + strcmp(pszName, "way") == 0 ) + { + psCtxt->bInWay = TRUE; + + psCtxt->nStrLength = 0; + psCtxt->pszStrBuf[0] = '\0'; + psCtxt->nTags = 0; + + memset( &(psCtxt->sWay), 0, sizeof(OSMWay) ); + psCtxt->sWay.sInfo.pszUserSID = ""; + + if( ppszIter ) + { + while( ppszIter[0] != NULL ) + { + if( strcmp(ppszIter[0], "id") == 0 ) + { + psCtxt->sWay.nID = OSM_Atoi64( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "version") == 0 ) + { + psCtxt->sWay.sInfo.nVersion = atoi( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "changeset") == 0 ) + { + psCtxt->sWay.sInfo.nChangeset = OSM_Atoi64( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "user") == 0 ) + { + psCtxt->sWay.sInfo.pszUserSID = OSM_AddString(psCtxt, ppszIter[1]); + } + else if( strcmp(ppszIter[0], "uid") == 0 ) + { + psCtxt->sWay.sInfo.nUID = atoi( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "timestamp") == 0 ) + { + psCtxt->sWay.sInfo.ts.pszTimeStamp = OSM_AddString(psCtxt, ppszIter[1]); + psCtxt->sWay.sInfo.bTimeStampIsStr = 1; + } + ppszIter += 2; + } + } + } + + else if( !psCtxt->bInNode && !psCtxt->bInWay && !psCtxt->bInRelation && + strcmp(pszName, "relation") == 0 ) + { + psCtxt->bInRelation = TRUE; + + psCtxt->nStrLength = 0; + psCtxt->pszStrBuf[0] = '\0'; + psCtxt->nTags = 0; + + memset( &(psCtxt->sRelation), 0, sizeof(OSMRelation) ); + psCtxt->sRelation.sInfo.pszUserSID = ""; + + if( ppszIter ) + { + while( ppszIter[0] != NULL ) + { + if( strcmp(ppszIter[0], "id") == 0 ) + { + psCtxt->sRelation.nID = OSM_Atoi64( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "version") == 0 ) + { + psCtxt->sRelation.sInfo.nVersion = atoi( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "changeset") == 0 ) + { + psCtxt->sRelation.sInfo.nChangeset = OSM_Atoi64( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "user") == 0 ) + { + psCtxt->sRelation.sInfo.pszUserSID = OSM_AddString(psCtxt, ppszIter[1]); + } + else if( strcmp(ppszIter[0], "uid") == 0 ) + { + psCtxt->sRelation.sInfo.nUID = atoi( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "timestamp") == 0 ) + { + psCtxt->sRelation.sInfo.ts.pszTimeStamp = OSM_AddString(psCtxt, ppszIter[1]); + psCtxt->sRelation.sInfo.bTimeStampIsStr = 1; + + } + ppszIter += 2; + } + } + } + + else if( psCtxt->bInWay && + strcmp(pszName, "nd") == 0 ) + { + if( ppszAttr != NULL && ppszAttr[0] != NULL && + strcmp(ppszAttr[0], "ref") == 0 ) + { + if( psCtxt->sWay.nRefs < psCtxt->nNodeRefsAllocated ) + { + psCtxt->panNodeRefs[psCtxt->sWay.nRefs] = OSM_Atoi64( ppszAttr[1] ); + psCtxt->sWay.nRefs ++; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too many nodes referenced in way " CPL_FRMT_GIB, + psCtxt->sWay.nID); + } + } + } + + else if( psCtxt->bInRelation && + strcmp(pszName, "member") == 0 ) + { + /* 300 is the recommended value, but there are files with more than 2000 so we should be able */ + /* to realloc over that value */ + if (psCtxt->sRelation.nMembers >= psCtxt->nMembersAllocated) + { + OSMMember* pasMembersNew; + int nMembersAllocated = + MAX(psCtxt->nMembersAllocated * 2, psCtxt->sRelation.nMembers + 1); + pasMembersNew = (OSMMember*) VSIRealloc( + psCtxt->pasMembers, + nMembersAllocated * sizeof(OSMMember)); + if( pasMembersNew == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot allocate enough memory to store members of relation " CPL_FRMT_GIB, + psCtxt->sRelation.nID); + return; + } + psCtxt->nMembersAllocated = nMembersAllocated; + psCtxt->pasMembers = pasMembersNew; + } + + OSMMember* psMember = &(psCtxt->pasMembers[psCtxt->sRelation.nMembers]); + psCtxt->sRelation.nMembers ++; + + psMember->nID = 0; + psMember->pszRole = ""; + psMember->eType = MEMBER_NODE; + + if( ppszIter ) + { + while( ppszIter[0] != NULL ) + { + if( strcmp(ppszIter[0], "ref") == 0 ) + { + psMember->nID = OSM_Atoi64( ppszIter[1] ); + } + else if( strcmp(ppszIter[0], "type") == 0 ) + { + if( strcmp( ppszIter[1], "node") == 0 ) + psMember->eType = MEMBER_NODE; + else if( strcmp( ppszIter[1], "way") == 0 ) + psMember->eType = MEMBER_WAY; + else if( strcmp( ppszIter[1], "relation") == 0 ) + psMember->eType = MEMBER_RELATION; + } + else if( strcmp(ppszIter[0], "role") == 0 ) + { + psMember->pszRole = OSM_AddString(psCtxt, ppszIter[1]); + } + ppszIter += 2; + } + } + } + else if( (psCtxt->bInNode || psCtxt->bInWay || psCtxt->bInRelation) && + strcmp(pszName, "tag") == 0 ) + { + if( psCtxt->nTags < psCtxt->nTagsAllocated ) + { + OSMTag* psTag = &(psCtxt->pasTags[psCtxt->nTags]); + psCtxt->nTags ++; + + psTag->pszK = ""; + psTag->pszV = ""; + + if( ppszIter ) + { + while( ppszIter[0] != NULL ) + { + if( ppszIter[0][0] == 'k' ) + { + psTag->pszK = OSM_AddString(psCtxt, ppszIter[1]); + } + else if( ppszIter[0][0] == 'v' ) + { + psTag->pszV = OSM_AddString(psCtxt, ppszIter[1]); + } + ppszIter += 2; + } + } + } + else + { + if (psCtxt->bInNode) + CPLError(CE_Failure, CPLE_AppDefined, + "Too many tags in node " CPL_FRMT_GIB, + psCtxt->pasNodes[0].nID); + else if (psCtxt->bInWay) + CPLError(CE_Failure, CPLE_AppDefined, + "Too many tags in way " CPL_FRMT_GIB, + psCtxt->sWay.nID); + else if (psCtxt->bInRelation) + CPLError(CE_Failure, CPLE_AppDefined, + "Too many tags in relation " CPL_FRMT_GIB, + psCtxt->sRelation.nID); + } + } +} + +/************************************************************************/ +/* OSM_XML_endElementCbk() */ +/************************************************************************/ + +static void XMLCALL OSM_XML_endElementCbk(void *pUserData, const char *pszName) +{ + OSMContext* psCtxt = (OSMContext*) pUserData; + + if (psCtxt->bStopParsing) return; + + psCtxt->nWithoutEventCounter = 0; + + if( psCtxt->bInNode && strcmp(pszName, "node") == 0 ) + { + psCtxt->pasNodes[0].nTags = psCtxt->nTags; + psCtxt->pasNodes[0].pasTags = psCtxt->pasTags; + + psCtxt->pfnNotifyNodes(1, psCtxt->pasNodes, psCtxt, psCtxt->user_data); + + psCtxt->bHasFoundFeature = TRUE; + + psCtxt->bInNode = FALSE; + } + + else + if( psCtxt->bInWay && strcmp(pszName, "way") == 0 ) + { + psCtxt->sWay.nTags = psCtxt->nTags; + psCtxt->sWay.pasTags = psCtxt->pasTags; + + psCtxt->sWay.panNodeRefs = psCtxt->panNodeRefs; + + psCtxt->pfnNotifyWay(&(psCtxt->sWay), psCtxt, psCtxt->user_data); + + psCtxt->bHasFoundFeature = TRUE; + + psCtxt->bInWay = FALSE; + } + + else + if( psCtxt->bInRelation && strcmp(pszName, "relation") == 0 ) + { + psCtxt->sRelation.nTags = psCtxt->nTags; + psCtxt->sRelation.pasTags = psCtxt->pasTags; + + psCtxt->sRelation.pasMembers = psCtxt->pasMembers; + + psCtxt->pfnNotifyRelation(&(psCtxt->sRelation), psCtxt, psCtxt->user_data); + + psCtxt->bHasFoundFeature = TRUE; + + psCtxt->bInRelation = FALSE; + } +} +/************************************************************************/ +/* dataHandlerCbk() */ +/************************************************************************/ + +static void XMLCALL OSM_XML_dataHandlerCbk(void *pUserData, + CPL_UNUSED const char *data, + CPL_UNUSED int nLen) +{ + OSMContext* psCtxt = (OSMContext*) pUserData; + + if (psCtxt->bStopParsing) return; + + psCtxt->nWithoutEventCounter = 0; + + psCtxt->nDataHandlerCounter ++; + if (psCtxt->nDataHandlerCounter >= XML_BUFSIZE) + { + CPLError(CE_Failure, CPLE_AppDefined, + "File probably corrupted (million laugh pattern)"); + XML_StopParser(psCtxt->hXMLParser, XML_FALSE); + psCtxt->bStopParsing = TRUE; + return; + } +} + +/************************************************************************/ +/* XML_ProcessBlock() */ +/************************************************************************/ + +static OSMRetCode XML_ProcessBlock(OSMContext* psCtxt) +{ + if( psCtxt->bEOF ) + return OSM_EOF; + if( psCtxt->bStopParsing ) + return OSM_ERROR; + + psCtxt->bHasFoundFeature = FALSE; + psCtxt->nWithoutEventCounter = 0; + + do + { + int eErr; + unsigned int nLen; + + psCtxt->nDataHandlerCounter = 0; + + nLen = (unsigned int)VSIFReadL( psCtxt->pabyBlob, 1, + XML_BUFSIZE, psCtxt->fp ); + + psCtxt->nBytesRead += nLen; + + psCtxt->bEOF = VSIFEofL(psCtxt->fp); + eErr = XML_Parse(psCtxt->hXMLParser, (const char*) psCtxt->pabyBlob, + nLen, psCtxt->bEOF ); + + if (eErr == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of OSM file failed : %s " + "at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(psCtxt->hXMLParser)), + (int)XML_GetCurrentLineNumber(psCtxt->hXMLParser), + (int)XML_GetCurrentColumnNumber(psCtxt->hXMLParser)); + psCtxt->bStopParsing = TRUE; + } + psCtxt->nWithoutEventCounter ++; + } while (!psCtxt->bEOF && !psCtxt->bStopParsing && + psCtxt->bHasFoundFeature == FALSE && + psCtxt->nWithoutEventCounter < 10); + + if (psCtxt->nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + psCtxt->bStopParsing = TRUE; + } + + return psCtxt->bStopParsing ? OSM_ERROR : psCtxt->bEOF ? OSM_EOF : OSM_OK; +} + +#endif + +/************************************************************************/ +/* OSM_Open() */ +/************************************************************************/ + +OSMContext* OSM_Open( const char* pszFilename, + NotifyNodesFunc pfnNotifyNodes, + NotifyWayFunc pfnNotifyWay, + NotifyRelationFunc pfnNotifyRelation, + NotifyBoundsFunc pfnNotifyBounds, + void* user_data ) +{ + OSMContext* psCtxt; + GByte abyHeader[1024]; + int nRead; + VSILFILE* fp; + int i; + int bPBF = FALSE; + + fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return NULL; + + nRead = (int)VSIFReadL(abyHeader, 1, sizeof(abyHeader)-1, fp); + abyHeader[nRead] = '\0'; + + if( strstr((const char*)abyHeader, "bPBF = bPBF; + psCtxt->fp = fp; + psCtxt->pfnNotifyNodes = pfnNotifyNodes; + if( pfnNotifyNodes == NULL ) + psCtxt->pfnNotifyNodes = EmptyNotifyNodesFunc; + psCtxt->pfnNotifyWay = pfnNotifyWay; + if( pfnNotifyWay == NULL ) + psCtxt->pfnNotifyWay = EmptyNotifyWayFunc; + psCtxt->pfnNotifyRelation = pfnNotifyRelation; + if( pfnNotifyRelation == NULL ) + psCtxt->pfnNotifyRelation = EmptyNotifyRelationFunc; + psCtxt->pfnNotifyBounds = pfnNotifyBounds; + if( pfnNotifyBounds == NULL ) + psCtxt->pfnNotifyBounds = EmptyNotifyBoundsFunc; + psCtxt->user_data = user_data; + + if( bPBF ) + { + psCtxt->nBlobSizeAllocated = 64 * 1024 + EXTRA_BYTES; + } +#ifdef HAVE_EXPAT + else + { + psCtxt->nBlobSizeAllocated = XML_BUFSIZE; + + psCtxt->nStrAllocated = 65536; + psCtxt->pszStrBuf = (char*) VSIMalloc(psCtxt->nStrAllocated); + if( psCtxt->pszStrBuf ) + psCtxt->pszStrBuf[0] = '\0'; + + psCtxt->hXMLParser = OGRCreateExpatXMLParser(); + XML_SetUserData(psCtxt->hXMLParser, psCtxt); + XML_SetElementHandler(psCtxt->hXMLParser, + OSM_XML_startElementCbk, + OSM_XML_endElementCbk); + XML_SetCharacterDataHandler(psCtxt->hXMLParser, OSM_XML_dataHandlerCbk); + + psCtxt->bTryToFetchBounds = TRUE; + + psCtxt->nNodesAllocated = 1; + psCtxt->pasNodes = (OSMNode*) VSIMalloc(sizeof(OSMNode) * psCtxt->nNodesAllocated); + + psCtxt->nTagsAllocated = 256; + psCtxt->pasTags = (OSMTag*) VSIMalloc(sizeof(OSMTag) * psCtxt->nTagsAllocated); + + /* 300 is the recommended value, but there are files with more than 2000 so we should be able */ + /* to realloc over that value */ + psCtxt->nMembersAllocated = 2000; + psCtxt->pasMembers = (OSMMember*) VSIMalloc(sizeof(OSMMember) * psCtxt->nMembersAllocated); + + psCtxt->nNodeRefsAllocated = 2000; + psCtxt->panNodeRefs = (GIntBig*) VSIMalloc(sizeof(GIntBig) * psCtxt->nNodeRefsAllocated); + + if( psCtxt->pszStrBuf == NULL || + psCtxt->pasNodes == NULL || + psCtxt->pasTags == NULL || + psCtxt->pasMembers == NULL || + psCtxt->panNodeRefs == NULL ) + { + OSM_Close(psCtxt); + return NULL; + } + + } +#endif + + psCtxt->pabyBlob = (GByte*)VSIMalloc(psCtxt->nBlobSizeAllocated); + if( psCtxt->pabyBlob == NULL ) + { + OSM_Close(psCtxt); + return NULL; + } + + return psCtxt; +} + +/************************************************************************/ +/* OSM_Close() */ +/************************************************************************/ + +void OSM_Close(OSMContext* psCtxt) +{ + if( psCtxt == NULL ) + return; + +#ifdef HAVE_EXPAT + if( !psCtxt->bPBF ) + { + if (psCtxt->hXMLParser) + XML_ParserFree(psCtxt->hXMLParser); + + CPLFree(psCtxt->pszStrBuf); /* only for XML case ! */ + } +#endif + + VSIFree(psCtxt->pabyBlob); + VSIFree(psCtxt->pabyUncompressed); + VSIFree(psCtxt->panStrOff); + VSIFree(psCtxt->pasNodes); + VSIFree(psCtxt->pasTags); + VSIFree(psCtxt->pasMembers); + VSIFree(psCtxt->panNodeRefs); + + VSIFCloseL(psCtxt->fp); + VSIFree(psCtxt); +} +/************************************************************************/ +/* OSM_ResetReading() */ +/************************************************************************/ + +void OSM_ResetReading( OSMContext* psCtxt ) +{ + VSIFSeekL(psCtxt->fp, 0, SEEK_SET); + + psCtxt->nBytesRead = 0; + +#ifdef HAVE_EXPAT + if( !psCtxt->bPBF ) + { + XML_ParserFree(psCtxt->hXMLParser); + psCtxt->hXMLParser = OGRCreateExpatXMLParser(); + XML_SetUserData(psCtxt->hXMLParser, psCtxt); + XML_SetElementHandler(psCtxt->hXMLParser, + OSM_XML_startElementCbk, + OSM_XML_endElementCbk); + XML_SetCharacterDataHandler(psCtxt->hXMLParser, OSM_XML_dataHandlerCbk); + psCtxt->bEOF = FALSE; + psCtxt->bStopParsing = FALSE; + psCtxt->nStrLength = 0; + psCtxt->pszStrBuf[0] = '\0'; + psCtxt->nTags = 0; + + psCtxt->bTryToFetchBounds = TRUE; + psCtxt->bInNode = FALSE; + psCtxt->bInWay = FALSE; + psCtxt->bInRelation = FALSE; + } +#endif +} + +/************************************************************************/ +/* OSM_ProcessBlock() */ +/************************************************************************/ + +static OSMRetCode PBF_ProcessBlock(OSMContext* psCtxt) +{ + int nRet = FALSE; + GByte abyHeaderSize[4]; + unsigned int nHeaderSize; + unsigned int nBlobSize = 0; + BlobType eType; + + if (VSIFReadL(abyHeaderSize, 4, 1, psCtxt->fp) != 1) + { + return OSM_EOF; + } + nHeaderSize = (abyHeaderSize[0] << 24) | (abyHeaderSize[1] << 16) | + (abyHeaderSize[2] << 8) | abyHeaderSize[3]; + + psCtxt->nBytesRead += 4; + + /* printf("nHeaderSize = %d\n", nHeaderSize); */ + if (nHeaderSize > 64 * 1024) + GOTO_END_ERROR; + if (VSIFReadL(psCtxt->pabyBlob, 1, nHeaderSize, psCtxt->fp) != nHeaderSize) + GOTO_END_ERROR; + + psCtxt->nBytesRead += nHeaderSize; + + memset(psCtxt->pabyBlob + nHeaderSize, 0, EXTRA_BYTES); + nRet = ReadBlobHeader(psCtxt->pabyBlob, psCtxt->pabyBlob + nHeaderSize, &nBlobSize, &eType); + if (!nRet || eType == BLOB_UNKNOW) + GOTO_END_ERROR; + + if (nBlobSize > 64*1024*1024) + GOTO_END_ERROR; + if (nBlobSize > psCtxt->nBlobSizeAllocated) + { + GByte* pabyBlobNew; + psCtxt->nBlobSizeAllocated = MAX(psCtxt->nBlobSizeAllocated * 2, nBlobSize); + pabyBlobNew = (GByte*)VSIRealloc(psCtxt->pabyBlob, + psCtxt->nBlobSizeAllocated + EXTRA_BYTES); + if( pabyBlobNew == NULL ) + GOTO_END_ERROR; + psCtxt->pabyBlob = pabyBlobNew; + } + if (VSIFReadL(psCtxt->pabyBlob, 1, nBlobSize, psCtxt->fp) != nBlobSize) + GOTO_END_ERROR; + + psCtxt->nBytesRead += nBlobSize; + + memset(psCtxt->pabyBlob + nBlobSize, 0, EXTRA_BYTES); + nRet = ReadBlob(psCtxt->pabyBlob, nBlobSize, eType, + psCtxt); + if (!nRet) + GOTO_END_ERROR; + + return OSM_OK; + +end_error: + + return OSM_ERROR; +} + +/************************************************************************/ +/* OSM_ProcessBlock() */ +/************************************************************************/ + +OSMRetCode OSM_ProcessBlock(OSMContext* psCtxt) +{ +#ifdef HAVE_EXPAT + if( psCtxt->bPBF ) + return PBF_ProcessBlock(psCtxt); + else + return XML_ProcessBlock(psCtxt); +#else + return PBF_ProcessBlock(psCtxt); +#endif +} + +/************************************************************************/ +/* OSM_GetBytesRead() */ +/************************************************************************/ + +GUIntBig OSM_GetBytesRead( OSMContext* psCtxt ) +{ + return psCtxt->nBytesRead; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/osm_parser.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/osm_parser.h new file mode 100644 index 000000000..bd9fea560 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/osm/osm_parser.h @@ -0,0 +1,133 @@ +/****************************************************************************** + * $Id: osm_parser.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Author: Even Rouault, + * Purpose: OSM XML and OSM PBF parser + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OSM_PARSER_H_INCLUDED +#define _OSM_PARSER_H_INCLUDED + +#include "cpl_port.h" +/* typedef long long GIntBig; */ + +CPL_C_START + +typedef struct +{ + const char *pszK; + const char *pszV; +} OSMTag; + +typedef struct +{ + union + { + GIntBig nTimeStamp; + const char *pszTimeStamp; + } ts; + GIntBig nChangeset; + int nVersion; + int nUID; + int bTimeStampIsStr; + const char *pszUserSID; +} OSMInfo; + +typedef struct +{ + GIntBig nID; + double dfLat; + double dfLon; + OSMInfo sInfo; + unsigned int nTags; + OSMTag *pasTags; +} OSMNode; + +typedef struct +{ + GIntBig nID; + OSMInfo sInfo; + unsigned int nTags; + unsigned int nRefs; + OSMTag *pasTags; + GIntBig *panNodeRefs; +} OSMWay; + +typedef enum +{ + MEMBER_NODE = 0, + MEMBER_WAY = 1, + MEMBER_RELATION = 2 +} OSMMemberType; + +typedef struct +{ + GIntBig nID; + const char *pszRole; + OSMMemberType eType; +} OSMMember; + +typedef struct +{ + GIntBig nID; + OSMInfo sInfo; + unsigned int nTags; + unsigned int nMembers; + OSMTag *pasTags; + OSMMember *pasMembers; +} OSMRelation; + +typedef enum +{ + OSM_OK, + OSM_EOF, + OSM_ERROR +} OSMRetCode; + +typedef struct _OSMContext OSMContext; + +typedef void (*NotifyNodesFunc) (unsigned int nNodes, OSMNode* pasNodes, OSMContext* psOSMContext, void* user_data); +typedef void (*NotifyWayFunc) (OSMWay* psWay, OSMContext* psOSMContext, void* user_data); +typedef void (*NotifyRelationFunc) (OSMRelation* psRelation, OSMContext* psOSMContext, void* user_data); +typedef void (*NotifyBoundsFunc) (double dfXMin, double dfYMin, double dfXMax, double dfYMax, OSMContext* psOSMContext, void* user_data); + +OSMContext* OSM_Open( const char* pszFilename, + NotifyNodesFunc pfnNotifyNodes, + NotifyWayFunc pfnNotifyWay, + NotifyRelationFunc pfnNotifyRelation, + NotifyBoundsFunc pfnNotifyBounds, + void* user_data ); + +GUIntBig OSM_GetBytesRead( OSMContext* psOSMContext ); + +void OSM_ResetReading( OSMContext* psOSMContext ); + +OSMRetCode OSM_ProcessBlock( OSMContext* psOSMContext ); + +void OSM_Close( OSMContext* psOSMContext ); + +CPL_C_END + +#endif /* _OSM_PARSER_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/GNUmakefile new file mode 100644 index 000000000..f43bd1377 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrpdsdriver.o ogrpdsdatasource.o ogrpdslayer.o + +CPPFLAGS := -I.. -I../.. -I../../../frmts/pds $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_pds.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/drv_pds.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/drv_pds.html new file mode 100644 index 000000000..9f8bd3f8b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/drv_pds.html @@ -0,0 +1,34 @@ + + +PDS - Planetary Data Systems TABLE + + + + +

    PDS - Planetary Data Systems TABLE

    + +(GDAL/OGR >= 1.8.0)

    + +This driver reads TABLE objects from PDS datasets. +Note there is a GDAL PDS driver to read the raster IMAGE objects from PDS datasets.

    + +The driver must be provided with the product label file (even when the actual data is placed in a separate file).

    + +If the label file contains a TABLE object, it will be read as the only layer of the dataset. +If no TABLE object is found, the driver will look for all objects containing the TABLE string and +read each one in a layer.

    + +ASCII and BINARY tables are supported. The driver can retrieve the field descriptions from inline COLUMN objects +or from a separate file pointed by ^STRUCTURE.

    + +If the table has a LONGITUDE and LATITUDE columns of type REAL and with UNIT=DEGREE, they will be used to return +POINT geometries.

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/makefile.vc new file mode 100644 index 000000000..2080d6f03 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrpdsdriver.obj ogrpdsdatasource.obj ogrpdslayer.obj +EXTRAFLAGS = -I.. -I..\.. -I$(GDAL_ROOT)/frmts/pds + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/ogr_pds.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/ogr_pds.h new file mode 100644 index 000000000..f56692e76 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/ogr_pds.h @@ -0,0 +1,141 @@ +/****************************************************************************** + * $Id: ogr_pds.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: PDS Translator + * Purpose: Definition of classes for OGR .pdstable driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_PDS_H_INCLUDED +#define _OGR_PDS_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "nasakeywordhandler.h" + +/************************************************************************/ +/* OGRPDSLayer */ +/************************************************************************/ + +typedef enum +{ + ASCII_REAL, + ASCII_INTEGER, + CHARACTER, + MSB_INTEGER, + MSB_UNSIGNED_INTEGER, + IEEE_REAL, +} FieldFormat; + +typedef struct +{ + int nStartByte; + int nByteCount; + FieldFormat eFormat; + int nItemBytes; + int nItems; +} FieldDesc; + +class OGRPDSLayer : public OGRLayer +{ + OGRFeatureDefn* poFeatureDefn; + + CPLString osTableID; + VSILFILE* fpPDS; + int nRecords; + int nStartBytes; + int nRecordSize; + GByte *pabyRecord; + int nNextFID; + int nLongitudeIndex; + int nLatitudeIndex; + + FieldDesc* pasFieldDesc; + + void ReadStructure(CPLString osStructureFilename); + OGRFeature *GetNextRawFeature(); + + public: + OGRPDSLayer(CPLString osTableID, + const char* pszLayerName, VSILFILE* fp, + CPLString osLabelFilename, + CPLString osStructureFilename, + int nRecords, + int nStartBytes, int nRecordSize, + GByte* pabyRecord, int bIsASCII); + ~OGRPDSLayer(); + + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); + + virtual GIntBig GetFeatureCount(int bForce = TRUE ); + + virtual OGRFeature *GetFeature( GIntBig nFID ); + + virtual OGRErr SetNextByIndex( GIntBig nIndex ); +}; + +/************************************************************************/ +/* OGRPDSDataSource */ +/************************************************************************/ + +class OGRPDSDataSource : public OGRDataSource +{ + char* pszName; + + OGRLayer** papoLayers; + int nLayers; + + NASAKeywordHandler oKeywords; + + CPLString osTempResult; + const char *GetKeywordSub( const char *pszPath, + int iSubscript, + const char *pszDefault ); + + int LoadTable(const char* pszFilename, + int nRecordSize, + CPLString osTableID); + + public: + OGRPDSDataSource(); + ~OGRPDSDataSource(); + + int Open( const char * pszFilename ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); + + static void CleanString( CPLString &osInput ); +}; + +#endif /* ndef _OGR_PDS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/ogrpdsdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/ogrpdsdatasource.cpp new file mode 100644 index 000000000..0f999b555 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/ogrpdsdatasource.cpp @@ -0,0 +1,361 @@ +/****************************************************************************** + * $Id: ogrpdsdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: PDS Translator + * Purpose: Implements OGRPDSDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_pds.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrpdsdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRPDSDataSource() */ +/************************************************************************/ + +OGRPDSDataSource::OGRPDSDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; +} + +/************************************************************************/ +/* ~OGRPDSDataSource() */ +/************************************************************************/ + +OGRPDSDataSource::~OGRPDSDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPDSDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRPDSDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + + +/************************************************************************/ +/* GetKeywordSub() */ +/************************************************************************/ + +const char * OGRPDSDataSource::GetKeywordSub( const char *pszPath, + int iSubscript, + const char *pszDefault ) + +{ + const char *pszResult = oKeywords.GetKeyword( pszPath, NULL ); + + if( pszResult == NULL ) + return pszDefault; + + if( pszResult[0] != '(' ) + return pszDefault; + + char **papszTokens = CSLTokenizeString2( pszResult, "(,)", + CSLT_HONOURSTRINGS ); + + if( iSubscript <= CSLCount(papszTokens) ) + { + osTempResult = papszTokens[iSubscript-1]; + CSLDestroy( papszTokens ); + return osTempResult.c_str(); + } + else + { + CSLDestroy( papszTokens ); + return pszDefault; + } +} + +/************************************************************************/ +/* CleanString() */ +/* */ +/* Removes single or double quotes, and converts spaces to underscores. */ +/* The change is made in-place to CPLString. */ +/************************************************************************/ + +void OGRPDSDataSource::CleanString( CPLString &osInput ) + +{ + if( ( osInput.size() < 2 ) || + ((osInput.at(0) != '"' || osInput.at(osInput.size()-1) != '"' ) && + ( osInput.at(0) != '\'' || osInput.at(osInput.size()-1) != '\'')) ) + return; + + char *pszWrk = CPLStrdup(osInput.c_str() + 1); + int i; + + pszWrk[strlen(pszWrk)-1] = '\0'; + + for( i = 0; pszWrk[i] != '\0'; i++ ) + { + if( pszWrk[i] == ' ' ) + pszWrk[i] = '_'; + } + + osInput = pszWrk; + CPLFree( pszWrk ); +} + +/************************************************************************/ +/* LoadTable() */ +/************************************************************************/ + +static CPLString MakeAttr(CPLString os1, CPLString os2) +{ + return os1 + "." + os2; +} + +int OGRPDSDataSource::LoadTable(const char* pszFilename, + int nRecordSize, + CPLString osTableID ) +{ + + CPLString osTableFilename; + int nStartBytes; + + CPLString osTableLink = "^"; + osTableLink += osTableID; + + CPLString osTable = oKeywords.GetKeyword( osTableLink, "" ); + if( osTable[0] == '(' ) + { + osTableFilename = GetKeywordSub(osTableLink, 1, ""); + CPLString osStartRecord = GetKeywordSub(osTableLink, 2, ""); + nStartBytes = (atoi(osStartRecord.c_str()) - 1) * nRecordSize; + if (osTableFilename.size() == 0 || osStartRecord.size() == 0 || + nStartBytes < 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot parse %s line", osTableLink.c_str()); + return FALSE; + } + CPLString osTPath = CPLGetPath(pszFilename); + CleanString( osTableFilename ); + osTableFilename = CPLFormCIFilename( osTPath, osTableFilename, NULL ); + } + else + { + osTableFilename = oKeywords.GetKeyword( osTableLink, "" ); + if (osTableFilename.size() != 0 && osTableFilename[0] >= '0' && + osTableFilename[0] <= '9') + { + nStartBytes = atoi(osTableFilename.c_str()) - 1; + if (strstr(osTableFilename.c_str(), "") == NULL) + nStartBytes *= nRecordSize; + osTableFilename = pszFilename; + } + else + { + CPLString osTPath = CPLGetPath(pszFilename); + CleanString( osTableFilename ); + osTableFilename = CPLFormCIFilename( osTPath, osTableFilename, NULL ); + nStartBytes = 0; + } + } + + CPLString osTableName = oKeywords.GetKeyword( MakeAttr(osTableID, "NAME"), "" ); + if (osTableName.size() == 0) + { + if (GetLayerByName(osTableID.c_str()) == NULL) + osTableName = osTableID; + else + osTableName = CPLSPrintf("Layer_%d", nLayers+1); + } + CleanString(osTableName); + CPLString osTableInterchangeFormat = + oKeywords.GetKeyword( MakeAttr(osTableID, "INTERCHANGE_FORMAT"), "" ); + CPLString osTableRows = oKeywords.GetKeyword( MakeAttr(osTableID, "ROWS"), "" ); + int nRecords = atoi(osTableRows); + if (osTableInterchangeFormat.size() == 0 || + osTableRows.size() == 0 || nRecords < 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "One of TABLE.INTERCHANGE_FORMAT or TABLE.ROWS is missing"); + return FALSE; + } + + CleanString(osTableInterchangeFormat); + if (osTableInterchangeFormat.compare("ASCII") != 0 && + osTableInterchangeFormat.compare("BINARY") != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Only INTERCHANGE_FORMAT=ASCII or BINARY is supported"); + return FALSE; + } + + VSILFILE* fp = VSIFOpenL(osTableFilename, "rb"); + if (fp == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", + osTableFilename.c_str()); + return FALSE; + } + + CPLString osTableStructure = oKeywords.GetKeyword( MakeAttr(osTableID, "^STRUCTURE"), "" ); + if (osTableStructure.size() != 0) + { + CPLString osTPath = CPLGetPath(pszFilename); + CleanString( osTableStructure ); + osTableStructure = CPLFormCIFilename( osTPath, osTableStructure, NULL ); + } + + GByte* pabyRecord = (GByte*) VSIMalloc(nRecordSize + 1); + if (pabyRecord == NULL) + { + VSIFCloseL(fp); + return FALSE; + } + pabyRecord[nRecordSize] = 0; + + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers] = new OGRPDSLayer(osTableID, osTableName, fp, + pszFilename, + osTableStructure, + nRecords, nStartBytes, + nRecordSize, pabyRecord, + osTableInterchangeFormat.compare("ASCII") == 0); + nLayers++; + + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRPDSDataSource::Open( const char * pszFilename ) + +{ + pszName = CPLStrdup( pszFilename ); + +// -------------------------------------------------------------------- +// Does this appear to be a .PDS table file? +// -------------------------------------------------------------------- + + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return FALSE; + + char szBuffer[512]; + int nbRead = (int)VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fp); + szBuffer[nbRead] = '\0'; + + const char* pszPos = strstr(szBuffer, "PDS_VERSION_ID"); + int bIsPDS = (pszPos != NULL); + + if (!bIsPDS) + { + VSIFCloseL(fp); + return FALSE; + } + + if (!oKeywords.Ingest(fp, pszPos - szBuffer)) + { + VSIFCloseL(fp); + return FALSE; + } + + VSIFCloseL(fp); + CPLString osRecordType = oKeywords.GetKeyword( "RECORD_TYPE", "" ); + CPLString osFileRecords = oKeywords.GetKeyword( "FILE_RECORDS", "" ); + CPLString osRecordBytes = oKeywords.GetKeyword( "RECORD_BYTES", "" ); + int nRecordSize = atoi(osRecordBytes); + if (osRecordType.size() == 0 || osFileRecords.size() == 0 || + osRecordBytes.size() == 0 || nRecordSize <= 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "One of RECORD_TYPE, FILE_RECORDS or RECORD_BYTES is missing"); + return FALSE; + } + CleanString(osRecordType); + if (osRecordType.compare("FIXED_LENGTH") != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Only RECORD_TYPE=FIXED_LENGTH is supported"); + return FALSE; + } + + CPLString osTable = oKeywords.GetKeyword( "^TABLE", "" ); + if (osTable.size() != 0) + LoadTable(pszFilename, nRecordSize, "TABLE"); + else + { + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return FALSE; + + while(TRUE) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + const char* pszLine = CPLReadLine2L(fp, 256, NULL); + CPLPopErrorHandler(); + CPLErrorReset(); + if (pszLine == NULL) + break; + char** papszTokens = + CSLTokenizeString2( pszLine, " =", CSLT_HONOURSTRINGS ); + int nTokens = CSLCount(papszTokens); + if (nTokens == 2 && + papszTokens[0][0] == '^' && + strstr(papszTokens[0], "TABLE") != NULL) + { + LoadTable(pszFilename, nRecordSize, papszTokens[0] + 1); + } + CSLDestroy(papszTokens); + papszTokens = NULL; + } + VSIFCloseL(fp); + } + + return nLayers != 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/ogrpdsdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/ogrpdsdriver.cpp new file mode 100644 index 000000000..ae4d03297 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/ogrpdsdriver.cpp @@ -0,0 +1,90 @@ +/****************************************************************************** + * $Id: ogrpdsdriver.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: PDS Translator + * Purpose: Implements OGRPDSDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_pds.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrpdsdriver.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +extern "C" void RegisterOGRPDS(); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRPDSDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->eAccess == GA_Update || + poOpenInfo->fpL == NULL ) + return NULL; + + if( strstr((const char*)poOpenInfo->pabyHeader, "PDS_VERSION_ID") == NULL ) + return NULL; + + OGRPDSDataSource *poDS = new OGRPDSDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + + +/************************************************************************/ +/* RegisterOGRPDS() */ +/************************************************************************/ + +void RegisterOGRPDS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "OGR_PDS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "OGR_PDS" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Planetary Data Systems TABLE" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_pds.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRPDSDriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/ogrpdslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/ogrpdslayer.cpp new file mode 100644 index 000000000..1f0fab6a7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pds/ogrpdslayer.cpp @@ -0,0 +1,742 @@ +/****************************************************************************** + * $Id: ogrpdslayer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: PDS Translator + * Purpose: Implements OGRPDSLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_pds.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrpdslayer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGRPDSLayer() */ +/************************************************************************/ + +OGRPDSLayer::OGRPDSLayer( CPLString osTableID, + const char* pszLayerName, VSILFILE* fp, + CPLString osLabelFilename, + CPLString osStructureFilename, + int nRecords, + int nStartBytes, int nRecordSize, + GByte* pabyRecordIn, int bIsASCII) + +{ + fpPDS = fp; + this->osTableID = osTableID; + this->nRecords = nRecords; + this->nStartBytes = nStartBytes; + this->nRecordSize = nRecordSize; + nLongitudeIndex = -1; + nLatitudeIndex = -1; + + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + pasFieldDesc = NULL; + + pabyRecord = pabyRecordIn; + + if (osStructureFilename.size() != 0) + { + ReadStructure(osStructureFilename); + } + else + { + ReadStructure(osLabelFilename); + } + + if (bIsASCII && + poFeatureDefn->GetFieldCount() == 0) + { + VSIFSeekL( fpPDS, nStartBytes, SEEK_SET ); + VSIFReadL( pabyRecord, nRecordSize, 1, fpPDS); + + char **papszTokens = CSLTokenizeString2( + (const char*)pabyRecord, " ", CSLT_HONOURSTRINGS ); + int nTokens = CSLCount(papszTokens); + int i; + for(i=0;i= '0' && ch <= '9') || ch == '+' || ch == '-') + { + } + else if (ch == '.') + { + eFieldType = OFTReal; + } + else + { + eFieldType = OFTString; + break; + } + pszStr ++; + } + char szFieldName[32]; + sprintf(szFieldName, "field_%d", + poFeatureDefn->GetFieldCount() + 1); + OGRFieldDefn oFieldDefn(szFieldName, eFieldType); + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + CSLDestroy(papszTokens); + } + + if (nLongitudeIndex >= 0 && nLatitudeIndex >= 0) + { + poFeatureDefn->SetGeomType( wkbPoint ); + } + + ResetReading(); +} + +/************************************************************************/ +/* ~OGRPDSLayer() */ +/************************************************************************/ + +OGRPDSLayer::~OGRPDSLayer() + +{ + CPLFree(pasFieldDesc); + poFeatureDefn->Release(); + VSIFree(pabyRecord); + + VSIFCloseL( fpPDS ); +} + + +/************************************************************************/ +/* ReadStructure() */ +/************************************************************************/ + +void OGRPDSLayer::ReadStructure(CPLString osStructureFilename) + +{ + int nFields = 0; + VSILFILE* fpStructure = VSIFOpenL(osStructureFilename, "rb"); + if (fpStructure == NULL) + return; + + const char* pszLine; + int bInObjectColumn = FALSE; + int nExpectedColumnNumber = 0; + CPLString osColumnName, osColumnDataType, osColumnStartByte, + osColumnBytes, osColumnFormat, osColumnUnit, + osColumnItems, osColumnItemBytes; + int nRowBytes = nRecordSize; + while(TRUE) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + pszLine = CPLReadLine2L(fpStructure, 256, NULL); + CPLPopErrorHandler(); + CPLErrorReset(); + if (pszLine == NULL) + break; + + char **papszTokens = + CSLTokenizeString2( pszLine, " =", CSLT_HONOURSTRINGS ); + int nTokens = CSLCount(papszTokens); + + if (bInObjectColumn && nTokens >= 1 && + EQUAL(papszTokens[0], "END_OBJECT")) + { + if (osColumnName.size() != 0 && + osColumnDataType.size() != 0 && + osColumnStartByte.size() != 0 && + osColumnBytes.size() != 0) + { + pasFieldDesc = + (FieldDesc*) CPLRealloc(pasFieldDesc, + (nFields + 1) * sizeof(FieldDesc)); + pasFieldDesc[nFields].nStartByte = atoi(osColumnStartByte) - 1; + pasFieldDesc[nFields].nByteCount = atoi(osColumnBytes); + if (pasFieldDesc[nFields].nStartByte >= 0 && + pasFieldDesc[nFields].nByteCount > 0 && + pasFieldDesc[nFields].nStartByte + + pasFieldDesc[nFields].nByteCount <= nRecordSize) + { + OGRFieldType eFieldType = OFTString; + pasFieldDesc[nFields].eFormat = CHARACTER; + pasFieldDesc[nFields].nItemBytes = atoi(osColumnItemBytes); + pasFieldDesc[nFields].nItems = atoi(osColumnItems); + if (pasFieldDesc[nFields].nItems == 0) + pasFieldDesc[nFields].nItems = 1; + if (pasFieldDesc[nFields].nItemBytes == 0 && + pasFieldDesc[nFields].nItems == 1) + pasFieldDesc[nFields].nItemBytes = pasFieldDesc[nFields].nByteCount; + + if (osColumnDataType.compare("ASCII_REAL") == 0) + { + eFieldType = OFTReal; + pasFieldDesc[nFields].eFormat = ASCII_REAL; + } + else if (osColumnDataType.compare("ASCII_INTEGER") == 0) + { + eFieldType = OFTInteger; + pasFieldDesc[nFields].eFormat = ASCII_INTEGER; + } + else if (osColumnDataType.compare("MSB_UNSIGNED_INTEGER") == 0) + { + if (pasFieldDesc[nFields].nItemBytes == 1 || + pasFieldDesc[nFields].nItemBytes == 2) + { + if (pasFieldDesc[nFields].nItems > 1) + eFieldType = OFTIntegerList; + else + eFieldType = OFTInteger; + } + else + { + pasFieldDesc[nFields].nItemBytes = 4; + if (pasFieldDesc[nFields].nItems > 1) + eFieldType = OFTRealList; + else + eFieldType = OFTReal; + } + pasFieldDesc[nFields].eFormat = MSB_UNSIGNED_INTEGER; + } + else if (osColumnDataType.compare("MSB_INTEGER") == 0) + { + if (pasFieldDesc[nFields].nItemBytes != 1 && + pasFieldDesc[nFields].nItemBytes != 2) + pasFieldDesc[nFields].nItemBytes = 4; + if (pasFieldDesc[nFields].nItems > 1) + eFieldType = OFTIntegerList; + else + eFieldType = OFTInteger; + pasFieldDesc[nFields].eFormat = MSB_INTEGER; + } + else if (osColumnDataType.compare("IEEE_REAL") == 0) + { + if (pasFieldDesc[nFields].nItemBytes != 4) + pasFieldDesc[nFields].nItemBytes = 4; + if (pasFieldDesc[nFields].nItems > 1) + eFieldType = OFTRealList; + else + eFieldType = OFTReal; + pasFieldDesc[nFields].eFormat = IEEE_REAL; + } + + OGRFieldDefn oFieldDefn(osColumnName, eFieldType); + if ((pasFieldDesc[nFields].eFormat == ASCII_REAL && + osColumnFormat.size() != 0 && + osColumnFormat[0] == 'F') || + (pasFieldDesc[nFields].eFormat == ASCII_INTEGER && + osColumnFormat.size() != 0 && + osColumnFormat[0] == 'I')) + { + const char* pszFormat = osColumnFormat.c_str(); + int nWidth = atoi(pszFormat + 1); + oFieldDefn.SetWidth(nWidth); + const char* pszPoint = strchr(pszFormat, '.'); + if (pszPoint) + { + int nPrecision = atoi(pszPoint + 1); + oFieldDefn.SetPrecision(nPrecision); + } + } + else if (oFieldDefn.GetType() == OFTString && + osColumnFormat.size() != 0 && + osColumnFormat[0] == 'A') + { + const char* pszFormat = osColumnFormat.c_str(); + int nWidth = atoi(pszFormat + 1); + oFieldDefn.SetWidth(nWidth); + } + poFeatureDefn->AddFieldDefn(&oFieldDefn); + + if (oFieldDefn.GetType() == OFTReal && + osColumnUnit.compare("DEGREE") == 0) + { + if (osColumnName.compare("LONGITUDE") == 0) + nLongitudeIndex = nFields; + else if (osColumnName.compare("LATITUDE") == 0) + nLatitudeIndex = nFields; + } + + nFields ++; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Field %d out of record extents", nFields); + CSLDestroy(papszTokens); + break; + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Did not get expected records for field %d", nFields); + CSLDestroy(papszTokens); + break; + } + bInObjectColumn = FALSE; + } + else if (nTokens == 2) + { + if (EQUAL(papszTokens[0], "PDS_VERSION_ID")) + { + CSLDestroy(papszTokens); + papszTokens = NULL; + while(TRUE) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + pszLine = CPLReadLine2L(fpStructure, 256, NULL); + CPLPopErrorHandler(); + CPLErrorReset(); + if (pszLine == NULL) + break; + papszTokens = + CSLTokenizeString2( pszLine, " =", CSLT_HONOURSTRINGS ); + int nTokens = CSLCount(papszTokens); + if (nTokens == 2 && + EQUAL(papszTokens[0], "OBJECT") && + EQUAL(papszTokens[1], osTableID.c_str())) + { + break; + } + CSLDestroy(papszTokens); + papszTokens = NULL; + } + CSLDestroy(papszTokens); + papszTokens = NULL; + if (pszLine == NULL) + break; + } + else if (EQUAL(papszTokens[0], "ROW_BYTES")) + { + nRowBytes = atoi(papszTokens[1]); + } + else if (EQUAL(papszTokens[0], "ROW_SUFFIX_BYTES")) + { + nRowBytes += atoi(papszTokens[1]); + } + else if (EQUAL(papszTokens[0], "OBJECT") && + EQUAL(papszTokens[1], "COLUMN")) + { + if (nRowBytes > nRecordSize) + { + nRecordSize = nRowBytes; + VSIFree(pabyRecord); + pabyRecord = (GByte*) CPLMalloc(nRecordSize + 1); + pabyRecord[nRecordSize] = 0; + } + else + nRecordSize = nRowBytes; + + nExpectedColumnNumber ++; + bInObjectColumn = TRUE; + osColumnName = ""; + osColumnDataType = ""; + osColumnStartByte = ""; + osColumnBytes = ""; + osColumnItems = ""; + osColumnItemBytes = ""; + osColumnFormat = ""; + osColumnUnit = ""; + } + else if (bInObjectColumn && EQUAL(papszTokens[0], "COLUMN_NUMBER")) + { + int nColumnNumber = atoi(papszTokens[1]); + if (nColumnNumber != nExpectedColumnNumber) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Did not get expected column number"); + CSLDestroy(papszTokens); + break; + } + } + else if (bInObjectColumn && EQUAL(papszTokens[0], "NAME")) + { + osColumnName = "\""; + osColumnName += papszTokens[1]; + osColumnName += "\""; + OGRPDSDataSource::CleanString(osColumnName); + } + else if (bInObjectColumn && EQUAL(papszTokens[0], "DATA_TYPE")) + { + osColumnDataType = papszTokens[1]; + OGRPDSDataSource::CleanString(osColumnDataType); + } + else if (bInObjectColumn && EQUAL(papszTokens[0], "START_BYTE")) + { + osColumnStartByte = papszTokens[1]; + } + else if (bInObjectColumn && EQUAL(papszTokens[0], "BYTES")) + { + osColumnBytes = papszTokens[1]; + } + else if (bInObjectColumn && EQUAL(papszTokens[0], "ITEMS")) + { + osColumnItems = papszTokens[1]; + } + else if (bInObjectColumn && EQUAL(papszTokens[0], "ITEM_BYTES")) + { + osColumnItemBytes = papszTokens[1]; + } + else if (bInObjectColumn && EQUAL(papszTokens[0], "FORMAT")) + { + osColumnFormat = papszTokens[1]; + } + else if (bInObjectColumn && EQUAL(papszTokens[0], "UNIT")) + { + osColumnUnit = papszTokens[1]; + } + } + CSLDestroy(papszTokens); + } + VSIFCloseL(fpStructure); +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRPDSLayer::ResetReading() + +{ + nNextFID = 0; + VSIFSeekL( fpPDS, nStartBytes, SEEK_SET ); +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRPDSLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRPDSLayer::GetNextRawFeature() +{ + if (nNextFID == nRecords) + return NULL; + int nRead = (int)VSIFReadL( pabyRecord, 1, nRecordSize, fpPDS); + if (nRead != nRecordSize) + return NULL; + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + int nFieldCount = poFeatureDefn->GetFieldCount(); + if (pasFieldDesc != NULL) + { + int i, j; + for(i=0;iSetField(i, (const char*)(pabyRecord + + pasFieldDesc[i].nStartByte)); + *pchEnd = chSaved; + } + else if (pasFieldDesc[i].eFormat == MSB_UNSIGNED_INTEGER && + pasFieldDesc[i].nStartByte + + pasFieldDesc[i].nItemBytes * pasFieldDesc[i].nItems <= nRecordSize) + { + if (pasFieldDesc[i].nItemBytes == 1) + { + if (pasFieldDesc[i].nItems > 1) + { + int* panValues = (int*)CPLMalloc(sizeof(int) * pasFieldDesc[i].nItems); + for(j=0;jSetField(i, pasFieldDesc[i].nItems, panValues); + CPLFree(panValues); + } + else + { + poFeature->SetField(i, pabyRecord[pasFieldDesc[i].nStartByte]); + } + } + else if (pasFieldDesc[i].nItemBytes == 2) + { + if (pasFieldDesc[i].nItems > 1) + { + int* panValues = (int*)CPLMalloc(sizeof(int) * pasFieldDesc[i].nItems); + for(j=0;jSetField(i, pasFieldDesc[i].nItems, panValues); + CPLFree(panValues); + } + else + { + unsigned short sVal; + memcpy(&sVal, pabyRecord + pasFieldDesc[i].nStartByte, 2); + CPL_MSBPTR16(&sVal); + poFeature->SetField(i, (int)sVal); + } + } + else if (pasFieldDesc[i].nItemBytes == 4) + { + if (pasFieldDesc[i].nItems > 1) + { + double* padfValues = (double*)CPLMalloc(sizeof(double) * pasFieldDesc[i].nItems); + for(j=0;jSetField(i, pasFieldDesc[i].nItems, padfValues); + CPLFree(padfValues); + } + else + { + unsigned int nVal; + memcpy(&nVal, pabyRecord + pasFieldDesc[i].nStartByte, 4); + CPL_MSBPTR32(&nVal); + poFeature->SetField(i, (double)nVal); + } + } + } + else if (pasFieldDesc[i].eFormat == MSB_INTEGER && + pasFieldDesc[i].nStartByte + + pasFieldDesc[i].nItemBytes * pasFieldDesc[i].nItems <= nRecordSize) + { + if (pasFieldDesc[i].nItemBytes == 1) + { + if (pasFieldDesc[i].nItems > 1) + { + int* panValues = (int*)CPLMalloc(sizeof(int) * pasFieldDesc[i].nItems); + for(j=0;jSetField(i, pasFieldDesc[i].nItems, panValues); + CPLFree(panValues); + } + else + { + poFeature->SetField(i, ((char*)pabyRecord)[pasFieldDesc[i].nStartByte]); + } + } + else if (pasFieldDesc[i].nItemBytes == 2) + { + if (pasFieldDesc[i].nItems > 1) + { + int* panValues = (int*)CPLMalloc(sizeof(int) * pasFieldDesc[i].nItems); + for(j=0;jSetField(i, pasFieldDesc[i].nItems, panValues); + CPLFree(panValues); + } + else + { + short sVal; + memcpy(&sVal, pabyRecord + pasFieldDesc[i].nStartByte, 2); + CPL_MSBPTR16(&sVal); + poFeature->SetField(i, (int)sVal); + } + } + else if (pasFieldDesc[i].nItemBytes == 4) + { + if (pasFieldDesc[i].nItems > 1) + { + int* panValues = (int*)CPLMalloc(sizeof(int) * pasFieldDesc[i].nItems); + for(j=0;jSetField(i, pasFieldDesc[i].nItems, panValues); + CPLFree(panValues); + } + else + { + int nVal; + memcpy(&nVal, pabyRecord + pasFieldDesc[i].nStartByte, 4); + CPL_MSBPTR32(&nVal); + poFeature->SetField(i, nVal); + } + } + } + else if (pasFieldDesc[i].eFormat == IEEE_REAL && + pasFieldDesc[i].nStartByte + + pasFieldDesc[i].nItemBytes * pasFieldDesc[i].nItems <= nRecordSize && + pasFieldDesc[i].nItemBytes == 4) + { + if (pasFieldDesc[i].nItems > 1) + { + double* padfValues = (double*)CPLMalloc(sizeof(double) * pasFieldDesc[i].nItems); + for(j=0;jSetField(i, pasFieldDesc[i].nItems, padfValues); + CPLFree(padfValues); + } + else + { + float fVal; + memcpy(&fVal, pabyRecord + pasFieldDesc[i].nStartByte, 4); + CPL_MSBPTR32(&fVal); + poFeature->SetField(i, (double)fVal); + } + } + } + } + else + { + char **papszTokens = CSLTokenizeString2( + (const char*)pabyRecord, " ", CSLT_HONOURSTRINGS ); + int nTokens = CSLCount(papszTokens); + nTokens = MIN(nTokens, nFieldCount); + int i; + for(i=0;iSetField(i, papszTokens[i]); + } + CSLDestroy(papszTokens); + } + + if (nLongitudeIndex >= 0 && nLatitudeIndex >= 0) + poFeature->SetGeometryDirectly(new OGRPoint( + poFeature->GetFieldAsDouble(nLongitudeIndex), + poFeature->GetFieldAsDouble(nLatitudeIndex))); + + poFeature->SetFID(nNextFID++); + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPDSLayer::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap,OLCFastFeatureCount) && + m_poFilterGeom == NULL && m_poAttrQuery == NULL) + return TRUE; + else if (EQUAL(pszCap,OLCRandomRead)) + return TRUE; + else if (EQUAL(pszCap,OLCFastSetNextByIndex) && + m_poFilterGeom == NULL && m_poAttrQuery == NULL) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRPDSLayer::GetFeatureCount(int bForce ) +{ + if (TestCapability(OLCFastFeatureCount)) + return nRecords; + + return OGRLayer::GetFeatureCount(bForce); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRPDSLayer::GetFeature( GIntBig nFID ) +{ + if (nFID < 0 || nFID >= nRecords) + return NULL; + + nNextFID = (int)nFID; + VSIFSeekL( fpPDS, nStartBytes + nNextFID * nRecordSize, SEEK_SET ); + return GetNextRawFeature(); +} + +/************************************************************************/ +/* SetNextByIndex() */ +/************************************************************************/ + +OGRErr OGRPDSLayer::SetNextByIndex( GIntBig nIndex ) +{ + if (!TestCapability(OLCFastSetNextByIndex)) + return OGRLayer::SetNextByIndex( nIndex ); + + if (nIndex < 0 || nIndex >= nRecords) + return OGRERR_FAILURE; + + nNextFID = (int)nIndex; + VSIFSeekL( fpPDS, nStartBytes + nNextFID * nRecordSize, SEEK_SET ); + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/GNUmakefile new file mode 100644 index 000000000..4d333ab6c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/GNUmakefile @@ -0,0 +1,15 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrpgdriver.o ogrpgdatasource.o ogrpglayer.o ogrpgtablelayer.o\ + ogrpgresultlayer.o ogrpgutility.o + +CPPFLAGS := $(PG_INC) -I../pgdump $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_pg.h ogrpgutility.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/drv_pg.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/drv_pg.html new file mode 100644 index 000000000..05bcb157c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/drv_pg.html @@ -0,0 +1,252 @@ + + +PostgreSQL / PostGIS + + + + +

    PostgreSQL / PostGIS

    + +This driver implements support for access to spatial tables in +PostgreSQL extended with the +PostGIS spatial data support. +Some support exists in the driver for use with PostgreSQL without PostGIS +but with less functionalities.

    + +This driver requires a connection to a Postgres database. If you want to +prepare a SQL dump to inject it later into a Postgres database, you can +instead use the PostgreSQL SQL Dump driver (GDAL/OGR >= 1.8.0)

    + +You can find additionnal information on the driver in the Advanced OGR PostgreSQL driver Information page. + +

    Connecting to a database

    + +To connect to a Postgres datasource, use a connection string specifying the database name, +with additional parameters as necessary
    +
    PG:dbname=databasename
    or
    PG:"dbname='databasename' host='addr' port='5432' user='x' password='y'"
    +It's also possible to omit the database name and connect +to a default database, with the same name as the user name.
    +Note: We use PQconnectdb() to make the connection, so any other options and defaults +that would apply to it, apply to the name here (refer to the documentation of the PostgreSQL server. +Here for PostgreSQL 8.4). +The PG: prefix is used to mark the name as a postgres connection string.

    + +

    Geometry columns

    + +If the geometry_columns table exists (i.e. PostGIS is enabled for the accessed +database), then all tables and named views listed in the geometry_columns table +will be treated as OGR layers. Otherwise (PostGIS disabled for the accessed database), +all regular user tables and named views will be treated as layers.

    + +Starting with GDAL 1.7.0, the driver also supports the +geography +column type introduced in PostGIS 1.5.

    + +Starting with GDAL 2.0, the driver also supports reading and writing the +following non-linear geometry types :CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE and MULTISURFACE

    + +

    SQL statements

    + +

    The PostgreSQL driver passes SQL statements directly to PostgreSQL by default, +rather than evaluating them internally when using the ExecuteSQL() call on the +OGRDataSource, or the -sql command option to ogr2ogr. Attribute query +expressions are also passed directly through to PostgreSQL. +It's also possible to request the ogr Pg driver to handle SQL commands +with the OGR SQL engine, by passing "OGRSQL" +string to the ExecuteSQL() method, as the name of the SQL dialect.

    + +

    The PostgreSQL driver in OGR supports the OGRDataSource::StartTrasaction(), +OGRDataSource::CommitTransaction() and OGRDataSource::RollbackTransaction() +calls in the normal SQL sense.

    + +

    Creation Issues

    + +The PostgreSQL driver does not support creation of new datasets (a database +within PostgreSQL), but it does allow creation of new layers within an +existing database.

    + +As mentioned above the type system is impoverished, and many OGR types +are not appropriately mapped into PostgreSQL.

    + +If the database has PostGIS types loaded (ie. the geometry type) newly +created layers will be created with the PostGIS Geometry type. Otherwise +they will use OID.

    + +By default it is assumed that text being sent to Postgres is in the UTF-8 +encoding. This is fine for plain ASCII, but can result in errors for +extended characters (ASCII 155+, LATIN1, etc). While OGR provides no direct +control over this, you can set the PGCLIENTENCODING environment variable +to indicate the format being provided. For instance, if your text is +LATIN1 you could set the environment variable to LATIN1 before using OGR +and input would be assumed to be LATIN1 instead of UTF-8. +An alternate way of setting the client encoding is to issue the following SQL command +with ExecuteSQL() : "SET client_encoding TO encoding_name" where encoding_name is LATIN1, etc. +Errors can be catched by enclosing this command with a CPLPushErrorHandler()/CPLPopErrorHandler() pair.

    + +

    Dataset open options

    + +(GDAL >= 2.0) + +
      +
    • LIST_ALL_TABLES=YES/NO: This may be "YES" to force all tables, +including non-spatial ones, to be listed.

      +

    + +

    Dataset Creation Options

    + +None

    + +

    Layer Creation Options

    + +
      +
    • +GEOM_TYPE: The GEOM_TYPE layer creation option can be set to +one of "geometry", "geography" (PostGIS >= 1.5), "BYTEA" or "OID" to force the type of geometry used for +a table. For a PostGIS database, "geometry" is the default value.

      +

    • OVERWRITE: This may be "YES" to force an existing layer of the +desired name to be destroyed before creating the requested layer.

      +

    • LAUNDER: This may be "YES" to force new fields created on this +layer to have their field names "laundered" into a form more compatible with +PostgreSQL. This converts to lower case and converts some special characters +like "-" and "#" to "_". If "NO" exact names are preserved. +The default value is "YES". If enabled the table (layer) name will also be laundered.

      +

    • PRECISION: This may be "YES" to force new fields created on this +layer to try and represent the width and precision information, if available +using NUMERIC(width,precision) or CHAR(width) types. If "NO" then the types +FLOAT8, INTEGER and VARCHAR will be used instead. The default is "YES".

      +

    • DIM={2,3}: Control the dimension of the layer. Defaults to 3. +Important to set to 2 for 2D layers with PostGIS 1.0+ as it has constraints +on the geometry dimension during loading.

      +

    • GEOMETRY_NAME: Set name of geometry column in new table. If +omitted it defaults to wkb_geometry for GEOM_TYPE=geometry, or the_geog for GEOM_TYPE=geography.

      +

    • SCHEMA: Set name of schema for new table. +Using the same layer name in different schemas is supported, but not in the public schema and others. Note that +using the -overwrite option of ogr2ogr and -lco SCHEMA= option at the same time will not work, as the ogr2ogr utility +will not understand that the existing layer must be destroyed in the specified schema. Use the -nln option of ogr2ogr instead, +or better the active_schema connection string. See below example.

      +

    • SPATIAL_INDEX: (From GDAL 1.6.0) Set to YES by default. Creates a spatial index (GiST) on the geometry column +to speed up queries. Set to FALSE to disable. (Has effect only when PostGIS is available).

      +

    • TEMPORARY: (From GDAL 1.8.0) Set to OFF by default. Creates a temporary table instead of a permanent one.

      +

    • UNLOGGED: (From GDAL 2.0) Set to OFF by default. Whether to create the table as a unlogged one. +Unlogged tables are only supported since PostgreSQL 9.1, and GiST indexes used for spatial indexing since PostgreSQL 9.3.

      +

    • NONE_AS_UNKNOWN: (From GDAL 1.8.1) Can bet set to TRUE to force non-spatial layers (wkbNone) to be created as +spatial tables of type GEOMETRY (wkbUnknown), which was the behaviour prior to GDAL 1.8.0. Defaults to NO, in which case +a regular table is created and not recorded in the PostGIS geometry_columns table.

      +

    • FID: (From GDAL 1.9.0) Name of the FID column to create. Defaults to 'ogc_fid'.

      +

    • FID64: (From GDAL 2.0) This may be "TRUE" to create a FID column that can support +64 bit identifiers. The default value is "FALSE".
    • +
    • EXTRACT_SCHEMA_FROM_LAYER_NAME: (From GDAL 1.9.0) Can be set to NO to avoid considering the dot character +as the separator between the schema and the table name. Defaults to YES.

      +

    • COLUMN_TYPES: (From GDAL 1.10) A list of strings of format field_name=pg_field_type (separated by comma) +that should be use when CreateField() is invoked on them. This will override the default choice that OGR would have made. +This can for example be used to create a column of type HSTORE.

      +

    + +

    Configuration Options

    + +There are a variety of +Configuration +Options which help control the behavior of this driver.

    + +

      +
    • PG_USE_COPY: This may be "YES" for using COPY for inserting data to Postgresql. +COPY is significantly faster than INSERT. Starting with GDAL 2.0, COPY is used by +default when inserting from a table that has just been created.
    • +

    • PGSQL_OGR_FID: Set name of primary key instead of 'ogc_fid'. Only used when opening a layer whose primary key cannot be autodetected. +Ignored by CreateLayer() that uses the FID creation option.
    • + + +

    • PG_USE_BASE64: (GDAL >= 1.8.0) If set to "YES", geometries will be fetched as BASE64 encoded EWKB instead of canonical HEX encoded EWKB. +This reduces the amount of data to be transferred from 2 N to 1.333 N, where N is the size of EWKB data. However, it might be a +bit slower than fetching in canonical form when the client and the server are on the same machine, so the default is NO.
    • +

    • OGR_TRUNCATE: (GDAL >= 1.11) If set to "YES", the content of the table will be first erased with the SQL TRUNCATE command before +inserting the first feature. This is an alternative to using the -overwrite flag of ogr2ogr, +that avoids views based on the table to be destroyed. +Typical use case: "ogr2ogr -append PG:dbname=foo abc.shp --config OGR_TRUNCATE YES". +
    + +

    Examples

    + +
      +
    • +Simple translation of a shapefile into PostgreSQL. The table 'abc' will +be created with the features from abc.shp and attributes from abc.dbf. +The database instance (warmerda) must already exist, and the table abc must +not already exist.

      + +

      +% ogr2ogr -f PostgreSQL PG:dbname=warmerda abc.shp
      +
      + +
    • +

      +This second example loads a political boundaries layer from VPF (via the +OGDI driver), and renames the layer from the +cryptic OGDI layer name to something more sensible. If an existing table +of the desired name exists it is overwritten.

      + +

      +% ogr2ogr -f PostgreSQL PG:dbname=warmerda \
      +        gltp:/vrf/usr4/mpp1/v0eur/vmaplv0/eurnasia \
      +        -lco OVERWRITE=yes -nln polbndl_bnd 'polbndl@bnd(*)_line'
      +
      +
    • + +
    • +

      +In this example we merge tiger line data from two different directories of +tiger files into one table. Note that the second invocation uses -append +and no OVERWRITE=yes.

      + +

      +% ogr2ogr -f PostgreSQL PG:dbname=warmerda tiger_michigan \
      +     -lco OVERWRITE=yes CompleteChain
      +% ogr2ogr -update -append -f PostgreSQL PG:dbname=warmerda tiger_ohio \
      +     CompleteChain
      +
      +
    • + +
    • +

      +This example shows using ogrinfo to evaluate an SQL query statement +within PostgreSQL. More sophisticated PostGIS specific queries may also be +used via the -sql commandline switch to ogrinfo.

      + +

      +ogrinfo -ro PG:dbname=warmerda -sql "SELECT pop_1994 from canada where province_name = 'Alberta'"
      +
      +
    • + +
    • +

      +This example shows using ogrinfo to list PostgreSQL/PostGIS layers on a different host.

      + +

      +ogrinfo -ro PG:'host=myserver.velocet.ca user=postgres dbname=warmerda'
      +
      +
    • + +
    + +

    FAQs

    + +
      +
    • Why can't I see my tables? PostGIS is installed and I have data
      +You must have permissions on all tables you want to read and geometry_columns and spatial_ref_sys.
      +Misleading behavior may follow without an error message if you do not have permissions to these tables. Permission +issues on geometry_columns and/or spatial_ref_sys tables can be generally confirmed if you can see the tables +by setting the configuration option PG_LIST_ALL_TABLES to YES. (e.g. ogrinfo --config PG_LIST_ALL_TABLES YES PG:xxxxx) +
    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/drv_pg_advanced.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/drv_pg_advanced.html new file mode 100644 index 000000000..6ac912723 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/drv_pg_advanced.html @@ -0,0 +1,363 @@ + + +PostgreSQL / PostGIS - Advanced Driver Information + + + + +

    PostgreSQL / PostGIS - Advanced Driver Information

    + +The information collected in that page deal with advanced topics, not found in the +OGR PostgreSQL driver Information page. + +

    Connection options related to schemas and tables

    + +Starting with GDAL 1.8.0, the database opening should be significantly faster than in previous versions, so using tables= or +schemas= options will not bring further noticeable speed-ups.

    + +Starting with GDAL 1.6.0, the set of tables to be scanned can be overridden by specifying +tables=[schema.]table[(geom_column_name)][,[schema2.]table2[(geom_column_name2)],...] within the connection string. +If the parameter is found, the driver skips enumeration of the tables as +described in the next paragraph.

    + +Starting with GDAL 1.7.0, it is possible to restrict the schemas that will be scanned while establishing +the list of tables. This can be done by specifying schemas=schema_name[,schema_name2] within the connection string. +This can also be a way of speeding up the connection to a PostgreSQL database if there are a lot of schemas. +Note that if only one schema is listed, it will also be made automatically the active schema +(and the schema name will not prefix the layer name). Otherwise, the active schema is still 'public', +unless otherwise specified by the active_schema= option.

    + +Starting with GDAL 1.7.0, the active schema ('public' being the default) can be overridden by specifying +active_schema=schema_name within the connection string. The active schema is the schema where tables +are created or looked for when their name is not explicitly prefixed by a schema name. +Note that this does not restrict the tables that will be listed (see schemas= option above). +When getting the list of tables, the name of the tables within that active schema will +not be prefixed by the schema name. For example, if you have a table 'foo' within the public +schema, and a table 'foo' within the 'bar_schema' schema, and that you specify +active_schema=bar_schema, 2 layers will be listed : 'foo' (implicetly within 'bar_schema') and 'public.foo'.

    + +

    Multiple geometry columns

    + +Starting with GDAL 1.6.0, the PostgreSQL driver supports accessing tables with multiple PostGIS geometry columns. + +

    OGR >= 1.11

    + +OGR 1.11 supports reading, updating, creating tables with multiple PostGIS +geometry columns (following +RFC 41) + +For such a table, a single OGR layer will be reported with as many geometry +fields as there are geometry columns in the table.

    + +For backward compatibility, it is also possible to query a layer with GetLayerByName() +with a name formatted like 'foo(bar)' where 'foo' is a table and 'bar' a geometry column.

    + +

    OGR < 1.11

    + +For such a table, there will be as many layers reported as the number of geometry columns listed for that +table in the geometry_columns table. +For example, if a table 'foo' has 2 geometry columns 'bar' and 'baz', 2 layers will be reported : +'foo(bar)' and 'foo(baz)'. For backward compatibility, if a table has only one geometry column, +the layer name is the table name. Also if a table 'foo' has several geometry columns, +with one being called 'wkb_geometry', the layer corresponding to this geometry column will +be simply reported as 'foo'. Be careful - the behaviour in creation, update or deletion of layers +that are based on tables with multiple PostGIS geometry column is known to have (not well-defined) +side-effects on the other layers as they are closely tied. Thus, that capability should currently +be thought as mostly read-only.

    + +

    Layers

    +Starting with GDAL 1.6.0, even when PostGIS is enabled, if the user defines the environment variable +
    PG_LIST_ALL_TABLES=YES
    (and does not specify tables=), all regular user tables and named views +will be treated as layers. However, tables with multiple geometry column will only be reported +once in that mode. So this variable is mainly useful when PostGIS is enabled to find out tables +with no spatial data, or views without an entry in geometry_columns table.

    + +In any case, all user tables can be queried explicitly with GetLayerByName()

    + +Regular (non-spatial) tables can be accessed, and will return features with +attributes, but not geometry. If the table has a "wkb_geometry" field, it will +be treated as a spatial table. The type of the field is inspected to +determine how to read it. It can be a PostGIS geometry field, which +is assumed to come back in OGC WKT, or type BYTEA or OID in which case it +is used as a source of OGC WKB geometry.

    + +Starting with GDAL 1.6.0, tables inherited from spatial tables are supported.

    + +If there is an "ogc_fid" field, it will be used to set the feature id of +the features, and not treated as a regular field.

    + +The layer name may be of the form "schema.table". The schema must exist, and the +user needs to have write permissions for the target and the public schema.

    + +

    Starting with GDAL 1.7.0, if the user defines the environment variable +

    PG_SKIP_VIEWS=YES
    (and does not specify tables=), only the regular +user tables will be treated as layers. The default action is to include the +views. This variable is particularly useful when you want to copy the data into +another format while avoiding the redundant data from the views. + +

    Named views

    + +When PostGIS is enabled for the accessed database, named views are supported, provided that +there is an entry in the geometry_columns tables. But, note that the AddGeometryColumn() SQL +function doesn't accept adding an entry for a view (only for regular tables). So, that must usually +be done by hand with a SQL statement like : +
    "INSERT INTO geometry_columns VALUES ( '', 'public', 'name_of_my_view', 'name_of_geometry_column', 2, 4326, 'POINT');"
    + +Starting with GDAL 1.6.0, it is also possible to use named views without inserting a row in the geometry_columns +table. For that, you need to explicitly specify the name of the view in the "tables=" option of the +connection string. See above. The drawback is that OGR will not be able to report a valid SRS and figure out +the right geometry type. + +

    Retrieving FID of newly inserted feature

    + +Starting with OGR 1.8.0, and for PostgreSQL >= 8.2 databases, the FID of a feature (i.e. usually the +value of the OGC_FID column for the feature) inserted into a table with CreateFeature(), in non-copy +mode, will be retrieved from the database and can be obtained with GetFID(). One side-effect of this +new behaviour is that you must be careful if you re-use the same feature object in a loop that makes +insertions. After the first iteration, the FID will be set to a non-null value, so at the second iteration, +CreateFeature() will try to insert the new feature with the FID of the previous feature, which will fail as +you cannot insert 2 features with same FID. So in that case you must explicitly reset the FID before calling CreateFeature(), +or use a fresh feature object.

    + +Snippet example in Python : +

    +    feat = ogr.Feature(lyr.GetLayerDefn())
    +    for i in range(100):
    +        feat.SetFID(-1)  # Reset FID to null value
    +        lyr.CreateFeature(feat)
    +        print('The feature has been assigned FID %d' % feat.GetFID())
    +
    + +or : +
    +    for i in range(100):
    +        feat = ogr.Feature(lyr.GetLayerDefn())
    +        lyr.CreateFeature(feat)
    +        print('The feature has been assigned FID %d' % feat.GetFID())
    +
    + +OGR < 1.8.0 behaviour can be obtained by setting the configuration option OGR_PG_RETRIEVE_FID to FALSE.

    + + +

    Issues with transactions

    + +

    Note: this section mostly applies to GDAL 2.0, that implements +RFC 54 - Dataset transactions +Previous versions had different behaviour which made it impractical to handle +both reading and writing with the same OGR datasource. Reading several layers +in a interleaved way was also not working properly. The new below behaviour should +enable more powerful uses, but might cause subtle problems for existing code +that relied on implicit transactions being regularly flushed by the PG driver in GDAL 1.X

    + +

    Efficient sequential reading in PostgreSQL requires to be done within a transaction +(technically this is a CURSOR WITHOUT HOLD). +So the PG driver will implicitely open such a transaction if none is currently +opened as soon as a feature is retrieved. This transaction will be released if +ResetReading() is called (provided that no other layer is still being read).

    + +

    If within such an implicit transaction, an explicit dataset level StartTransaction() +is issued, the PG driver will use a SAVEPOINT to emulate properly the transaction +behaviour while making the active cursor on the read layer still opened.

    + +

    If an explicit transaction is opened with dataset level StartTransaction() +before reading a layer, this transaction will be used for the cursor that iterates +over the layer. When explicitly committing or rolling back the transaction, the +cursor will become invalid, and ResetReading() should be issued again to restart +reading from the beginning.

    + +

    As calling SetAttributeFilter() or SetSpatialFilter() implies an implicit +ResetReading(), they have the same effect as ResetReading(). That is to say, +while an implict transaction is in progress, the transaction will be committed +(if no other layer is being read), and a new one will be started again at the next +GetNextFeature() call. On the contrary, if they are called within an explicit +transaction, the transaction is maintained.

    + +

    With the above rules, the below examples show the SQL instructions that are +run when using the OGR API in different scenarios.

    +
    +
    +lyr1->GetNextFeature()             BEGIN (implict)
    +                                   DECLARE cur1 CURSOR FOR SELECT * FROM lyr1
    +                                   FETCH 1 IN cur1
    +
    +lyr1->SetAttributeFilter('xxx')
    +     --> lyr1->ResetReading()      CLOSE cur1
    +                                   COMMIT (implicit)
    +
    +lyr1->GetNextFeature()             BEGIN (implict)
    +                                   DECLARE cur1 CURSOR  FOR SELECT * FROM lyr1 WHERE xxx
    +                                   FETCH 1 IN cur1
    +
    +lyr2->GetNextFeature()             DECLARE cur2 CURSOR  FOR SELECT * FROM lyr2
    +                                   FETCH 1 IN cur2
    +
    +lyr1->GetNextFeature()             FETCH 1 IN cur1
    +
    +lyr2->GetNextFeature()             FETCH 1 IN cur2
    +
    +lyr1->CreateFeature(f)             INSERT INTO cur1 ...
    +
    +lyr1->SetAttributeFilter('xxx')
    +     --> lyr1->ResetReading()      CLOSE cur1
    +                                   COMMIT (implicit)
    +
    +lyr1->GetNextFeature()             DECLARE cur1 CURSOR  FOR SELECT * FROM lyr1 WHERE xxx
    +                                   FETCH 1 IN cur1
    +
    +lyr1->ResetReading()               CLOSE cur1
    +
    +lyr2->ResetReading()               CLOSE cur2
    +                                   COMMIT (implicit)
    +
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +
    +ds->StartTransaction()             BEGIN
    +
    +lyr1->GetNextFeature()             DECLARE cur1 CURSOR FOR SELECT * FROM lyr1
    +                                   FETCH 1 IN cur1
    +
    +lyr2->GetNextFeature()             DECLARE cur2 CURSOR FOR SELECT * FROM lyr2
    +                                   FETCH 1 IN cur2
    +
    +lyr1->CreateFeature(f)             INSERT INTO cur1 ...
    +
    +lyr1->SetAttributeFilter('xxx')
    +     --> lyr1->ResetReading()      CLOSE cur1
    +                                   COMMIT (implicit)
    +
    +lyr1->GetNextFeature()             DECLARE cur1 CURSOR  FOR SELECT * FROM lyr1 WHERE xxx
    +                                   FETCH 1 IN cur1
    +
    +lyr1->ResetReading()               CLOSE cur1
    +
    +lyr2->ResetReading()               CLOSE cur2
    +
    +ds->CommitTransaction()            COMMIT
    +
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +
    +ds->StartTransaction()             BEGIN
    +
    +lyr1->GetNextFeature()             DECLARE cur1 CURSOR FOR SELECT * FROM lyr1
    +                                   FETCH 1 IN cur1
    +
    +lyr1->CreateFeature(f)             INSERT INTO cur1 ...
    +
    +ds->CommitTransaction()            CLOSE cur1 (implicit)
    +                                   COMMIT
    +
    +lyr1->GetNextFeature()             FETCH 1 IN cur1      ==> Error since the cursor was closed with the commit. Explicit ResetReading() required before
    +
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    +
    +lyr1->GetNextFeature()             BEGIN (implicit)
    +                                   DECLARE cur1 CURSOR FOR SELECT * FROM lyr1
    +                                   FETCH 1 IN cur1
    +
    +ds->StartTransaction()             SAVEPOINT savepoint
    +
    +lyr1->CreateFeature(f)             INSERT INTO cur1 ...
    +
    +ds->CommitTransaction()            RELEASE SAVEPOINT savepoint
    +
    +lyr1->ResetReading()               CLOSE cur1
    +                                   COMMIT (implicit)
    +
    + +

    Note: in reality, the PG drivers fetches 500 features at once. The FETCH 1 +is for clarity of the explanation.

    + + +

    Advanced Examples

    + +
      +
    • +

      +This example shows using ogrinfo to list only the layers specified by the tables= options. (Starting with GDAL 1.6.0)

      + +

      +ogrinfo -ro PG:'dbname=warmerda tables=table1,table2'
      +
      +
    • + +
    • +

      +This example shows using ogrinfo to query a table 'foo' with multiple geometry columns ('geom1' and 'geom2'). (Starting with GDAL 1.6.0)

      + +

      +ogrinfo -ro -al PG:dbname=warmerda 'foo(geom2)'
      +
      +
    • + +
    • +

      +This example show how to list only the layers inside the schema apt200810 and apt200812. +The layer names will be prefixed by the name of the schema they belong to. (Starting with GDAL 1.7.0)

      + +

      +ogrinfo -ro PG:'dbname=warmerda schemas=apt200810,apt200812'
      +
      +
    • + +
    • +

      +This example shows using ogrinfo to list only the layers inside the schema named apt200810. +Note that the layer names will not be prefixed by apt200810 as only one schema is listed. (Starting with GDAL 1.7.0)

      + +

      +ogrinfo -ro PG:'dbname=warmerda schemas=apt200810'
      +
      +
    • + +
    • +

      +This example shows how to convert a set of shapefiles inside the apt200810 directory into an +existing Postgres schema apt200810. In that example, we could have use the schemas= option instead. (Starting with GDAL 1.7.0)

      + +

      +ogr2ogr -f PostgreSQL "PG:dbname=warmerda active_schema=apt200810" apt200810
      +
      +
    • + +
    • +

      +This example shows how to convert all the tables inside the schema apt200810 as a set of shapefiles inside the apt200810 directory. +Note that the layer names will not be prefixed by apt200810 as only one schema is listed (Starting with GDAL 1.7.0)

      + +

      +ogr2ogr apt200810 PG:'dbname=warmerda schemas=apt200810'
      +
      +
    • + +
    • +

      +This example shows how to overwrite an existing table in an existing schema. Note the use of -nln to specify the qualified layer name.

      + +

      +ogr2ogr -overwrite -f PostgreSQL "PG:dbname=warmerda" mytable.shp mytable -nln myschema.mytable
      +
      + +Note that using -lco SCHEMA=mytable instead of -nln wouldn't have worked in that case +(see #2821 for more details).

      + +If you need to overwrite many tables located in a schema at once, the -nln option is not +the more appropriate, so it might be more convenient to use the active_schema connection +string (Starting with GDAL 1.7.0). The following example will overwrite, if necessary, +all the PostgreSQL tables corresponding to a set of shapefiles inside the apt200810 directory : + +

      +ogr2ogr -overwrite -f PostgreSQL "PG:dbname=warmerda active_schema=apt200810" apt200810
      +
      + +
    • + +
    + + +

    See Also

    + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/makefile.vc new file mode 100644 index 000000000..446b6f091 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/makefile.vc @@ -0,0 +1,30 @@ + +OBJ = ogrpgdriver.obj ogrpgdatasource.obj ogrpglayer.obj \ + ogrpgtablelayer.obj ogrpgresultlayer.obj ogrpgutility.obj + +PLUGIN_DLL = ogr_PG.dll + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. -I..\pgdump -I$(PG_INC_DIR) + +default: $(OBJ) + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll /out:$(PLUGIN_DLL) $(OBJ) $(GDALLIB) $(PG_LIB) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +clean: + -del *.lib + -del *.obj *.pdb *.exp + -del *.exe + -del *.dll + -del *.manifest + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogr_pg.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogr_pg.h new file mode 100644 index 000000000..a35d14ec8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogr_pg.h @@ -0,0 +1,550 @@ +/****************************************************************************** + * $Id: ogr_pg.h 28988 2015-04-24 11:58:49Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for OGR/PostgreSQL driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_PG_H_INCLUDED +#define _OGR_PG_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "libpq-fe.h" +#include "cpl_string.h" + +#include "ogrpgutility.h" +#include "ogr_pgdump.h" + +/* These are the OIDs for some builtin types, as returned by PQftype(). */ +/* They were copied from pg_type.h in src/include/catalog/pg_type.h */ + +#define BOOLOID 16 +#define BYTEAOID 17 +#define CHAROID 18 +#define NAMEOID 19 +#define INT8OID 20 +#define INT2OID 21 +#define INT2VECTOROID 22 +#define INT4OID 23 +#define REGPROCOID 24 +#define TEXTOID 25 +#define OIDOID 26 +#define TIDOID 27 +#define XIDOID 28 +#define CIDOID 29 +#define OIDVECTOROID 30 +#define FLOAT4OID 700 +#define FLOAT8OID 701 +#define BOOLARRAYOID 1000 +#define INT4ARRAYOID 1007 +#define TEXTARRAYOID 1009 +#define BPCHARARRAYOID 1014 +#define VARCHARARRAYOID 1015 +#define INT8ARRAYOID 1016 +#define FLOAT4ARRAYOID 1021 +#define FLOAT8ARRAYOID 1022 +#define BPCHAROID 1042 +#define VARCHAROID 1043 +#define DATEOID 1082 +#define TIMEOID 1083 +#define TIMESTAMPOID 1114 +#define TIMESTAMPTZOID 1184 +#define NUMERICOID 1700 + +CPLString OGRPGEscapeString(PGconn *hPGConn, + const char* pszStrValue, int nMaxLength = -1, + const char* pszTableName = "", + const char* pszFieldName = ""); +CPLString OGRPGEscapeColumnName(const char* pszColumnName); + +#define UNDETERMINED_SRID -2 /* Special value when we haven't yet looked for SRID */ + +class OGRPGDataSource; +class OGRPGLayer; + +typedef enum +{ + GEOM_TYPE_UNKNOWN = 0, + GEOM_TYPE_GEOMETRY = 1, + GEOM_TYPE_GEOGRAPHY = 2, + GEOM_TYPE_WKB = 3 +} PostgisType; + +typedef struct +{ + char* pszName; + char* pszGeomType; + int nCoordDimension; + int nSRID; + PostgisType ePostgisType; + int bNullable; +} PGGeomColumnDesc; + +/************************************************************************/ +/* OGRPGGeomFieldDefn */ +/************************************************************************/ + +class OGRPGGeomFieldDefn : public OGRGeomFieldDefn +{ + protected: + OGRPGLayer* poLayer; + + public: + OGRPGGeomFieldDefn( OGRPGLayer* poLayerIn, + const char* pszFieldName ) : + OGRGeomFieldDefn(pszFieldName, wkbUnknown), poLayer(poLayerIn), + nSRSId(UNDETERMINED_SRID), nCoordDimension(2), ePostgisType(GEOM_TYPE_UNKNOWN) + { + } + + virtual OGRSpatialReference* GetSpatialRef(); + + void UnsetLayer() { poLayer = NULL; } + + int nSRSId; + int nCoordDimension; + PostgisType ePostgisType; +}; + +/************************************************************************/ +/* OGRPGFeatureDefn */ +/************************************************************************/ + +class OGRPGFeatureDefn : public OGRFeatureDefn +{ + public: + OGRPGFeatureDefn( const char * pszName = NULL ) : + OGRFeatureDefn(pszName) + { + SetGeomType(wkbNone); + } + + virtual void UnsetLayer() + { + for(int i=0;iUnsetLayer(); + } + + OGRPGGeomFieldDefn* myGetGeomFieldDefn(int i) + { + return (OGRPGGeomFieldDefn*) GetGeomFieldDefn(i); + } +}; + +/************************************************************************/ +/* OGRPGLayer */ +/************************************************************************/ + +class OGRPGLayer : public OGRLayer +{ + protected: + OGRPGFeatureDefn *poFeatureDefn; + + int nCursorPage; + GIntBig iNextShapeId; + + static char *GByteArrayToBYTEA( const GByte* pabyData, int nLen); + static char *GeometryToBYTEA( OGRGeometry *, int bIsPostGIS1 ); + static GByte *BYTEAToGByteArray( const char *pszBytea, int* pnLength ); + static OGRGeometry *BYTEAToGeometry( const char *, int bIsPostGIS1 ); + Oid GeometryToOID( OGRGeometry * ); + OGRGeometry *OIDToGeometry( Oid ); + + OGRPGDataSource *poDS; + + char *pszQueryStatement; + + char *pszCursorName; + PGresult *hCursorResult; + int bInvalidated; + + int nResultOffset; + + int bWkbAsOid; + + char *pszFIDColumn; + + int bCanUseBinaryCursor; + int *m_panMapFieldNameToIndex; + int *m_panMapFieldNameToGeomIndex; + + int ParsePGDate( const char *, OGRField * ); + + void SetInitialQueryCursor(); + void CloseCursor(); + + virtual CPLString GetFromClauseForGetExtent() = 0; + OGRErr RunGetExtentRequest( OGREnvelope *psExtent, int bForce, + CPLString osCommand, int bErrorAsDebug ); + static void CreateMapFromFieldNameToIndex(PGresult* hResult, + OGRFeatureDefn* poFeatureDefn, + int*& panMapFieldNameToIndex, + int*& panMapFieldNameToGeomIndex); + + int ReadResultDefinition(PGresult *hInitialResultIn); + + OGRFeature *RecordToFeature( PGresult* hResult, + const int* panMapFieldNameToIndex, + const int* panMapFieldNameToGeomIndex, + int iRecord ); + OGRFeature *GetNextRawFeature(); + + public: + OGRPGLayer(); + virtual ~OGRPGLayer(); + + virtual void ResetReading(); + + virtual OGRFeatureDefn * GetLayerDefn(); + virtual OGRPGFeatureDefn * myGetLayerDefn() { return (OGRPGFeatureDefn*) GetLayerDefn(); } + + virtual OGRErr GetExtent( OGREnvelope *psExtent, int bForce ) { return GetExtent(0, psExtent, bForce); } + virtual OGRErr GetExtent( int iGeomField, OGREnvelope *psExtent, int bForce ); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + void InvalidateCursor(); + + virtual const char *GetFIDColumn(); + + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + + OGRPGDataSource *GetDS() { return poDS; } + + virtual void ResolveSRID(OGRPGGeomFieldDefn* poGFldDefn) = 0; +}; + +/************************************************************************/ +/* OGRPGTableLayer */ +/************************************************************************/ + +class OGRPGTableLayer : public OGRPGLayer +{ + int bUpdateAccess; + + void BuildWhere(void); + CPLString BuildFields(void); + void BuildFullQueryStatement(void); + + char *pszTableName; + char *pszSchemaName; + char *pszSqlTableName; + int bTableDefinitionValid; + + CPLString osPrimaryKey; + + int bGeometryInformationSet; + + /* Name of the parent table with the geometry definition if it is a derived table or NULL */ + char *pszSqlGeomParentTableName; + + char *pszGeomColForced; + + CPLString osQuery; + CPLString osWHERE; + + int bLaunderColumnNames; + int bPreservePrecision; + int bUseCopy; + int bCopyActive; + int bFIDColumnInCopyFields; + int bFirstInsertion; + + OGRErr CreateFeatureViaCopy( OGRFeature *poFeature ); + OGRErr CreateFeatureViaInsert( OGRFeature *poFeature ); + CPLString BuildCopyFields(); + + int bHasWarnedIncompatibleGeom; + void CheckGeomTypeCompatibility(int iGeomField, OGRGeometry* poGeom); + + int bRetrieveFID; + int bHasWarnedAlreadySetFID; + + char **papszOverrideColumnTypes; + int nForcedSRSId; + int nForcedDimension; + int bCreateSpatialIndexFlag; + int bInResetReading; + + int bAutoFIDOnCreateViaCopy; + int bUseCopyByDefault; + + int bDifferedCreation; + CPLString osCreateTable; + + int iFIDAsRegularColumnIndex; + + virtual CPLString GetFromClauseForGetExtent() { return pszSqlTableName; } + + OGRErr RunAddGeometryColumn( OGRPGGeomFieldDefn *poGeomField ); + OGRErr RunCreateSpatialIndex( OGRPGGeomFieldDefn *poGeomField ); + +public: + OGRPGTableLayer( OGRPGDataSource *, + CPLString& osCurrentSchema, + const char * pszTableName, + const char * pszSchemaName, + const char * pszGeomColForced, + int bUpdate ); + ~OGRPGTableLayer(); + + void SetGeometryInformation(PGGeomColumnDesc* pasDesc, + int nGeomFieldCount); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + virtual GIntBig GetFeatureCount( int ); + + virtual void SetSpatialFilter( OGRGeometry *poGeom ) { SetSpatialFilter(0, poGeom); } + virtual void SetSpatialFilter( int iGeomField, OGRGeometry *poGeom ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr CreateGeomField( OGRGeomFieldDefn *poGeomField, + int bApproxOK = TRUE ); + virtual OGRErr DeleteField( int iField ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + + virtual int TestCapability( const char * ); + + virtual OGRErr GetExtent( OGREnvelope *psExtent, int bForce ) { return GetExtent(0, psExtent, bForce); } + virtual OGRErr GetExtent( int iGeomField, OGREnvelope *psExtent, int bForce ); + + const char* GetTableName() { return pszTableName; } + const char* GetSchemaName() { return pszSchemaName; } + + virtual const char *GetFIDColumn(); + + // follow methods are not base class overrides + void SetLaunderFlag( int bFlag ) + { bLaunderColumnNames = bFlag; } + void SetPrecisionFlag( int bFlag ) + { bPreservePrecision = bFlag; } + + void SetOverrideColumnTypes( const char* pszOverrideColumnTypes ); + + OGRErr StartCopy(); + OGRErr EndCopy(); + + int ReadTableDefinition(); + int HasGeometryInformation() { return bGeometryInformationSet; } + void SetTableDefinition(const char* pszFIDColumnName, + const char* pszGFldName, + OGRwkbGeometryType eType, + const char* pszGeomType, + int nSRSId, + int nCoordDimension); + + void SetForcedSRSId( int nForcedSRSIdIn ) + { nForcedSRSId = nForcedSRSIdIn; } + void SetForcedDimension( int nForcedDimensionIn ) + { nForcedDimension = nForcedDimensionIn; } + void SetCreateSpatialIndexFlag( int bFlag ) + { bCreateSpatialIndexFlag = bFlag; } + void AllowAutoFIDOnCreateViaCopy() { bAutoFIDOnCreateViaCopy = TRUE; } + void SetUseCopy() { bUseCopy = TRUE; bUseCopyByDefault = TRUE; } + + void SetDifferedCreation(int bDifferedCreationIn, CPLString osCreateTable); + OGRErr RunDifferedCreationIfNecessary(); + + virtual void ResolveSRID(OGRPGGeomFieldDefn* poGFldDefn); +}; + +/************************************************************************/ +/* OGRPGResultLayer */ +/************************************************************************/ + +class OGRPGResultLayer : public OGRPGLayer +{ + void BuildFullQueryStatement(void); + + char *pszRawStatement; + + char *pszGeomTableName; + char *pszGeomTableSchemaName; + + CPLString osWHERE; + + virtual CPLString GetFromClauseForGetExtent() + { CPLString osStr("("); + osStr += pszRawStatement; osStr += ")"; return osStr; } + + public: + OGRPGResultLayer( OGRPGDataSource *, + const char * pszRawStatement, + PGresult *hInitialResult ); + virtual ~OGRPGResultLayer(); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual void SetSpatialFilter( OGRGeometry *poGeom ) { SetSpatialFilter(0, poGeom); } + virtual void SetSpatialFilter( int iGeomField, OGRGeometry *poGeom ); + + virtual int TestCapability( const char * ); + + virtual OGRFeature *GetNextFeature(); + + virtual void ResolveSRID(OGRPGGeomFieldDefn* poGFldDefn); +}; + +/************************************************************************/ +/* OGRPGDataSource */ +/************************************************************************/ +class OGRPGDataSource : public OGRDataSource +{ + typedef struct + { + int nMajor; + int nMinor; + int nRelease; + } PGver; + + OGRPGTableLayer **papoLayers; + int nLayers; + + char *pszName; + char *pszDBName; + + int bDSUpdate; + int bHavePostGIS; + int bHaveGeography; + + int bUserTransactionActive; + int bSavePointActive; + int nSoftTransactionLevel; + + PGconn *hPGConn; + + int DeleteLayer( int iLayer ); + + Oid nGeometryOID; + Oid nGeographyOID; + + // We maintain a list of known SRID to reduce the number of trips to + // the database to get SRSes. + int nKnownSRID; + int *panSRID; + OGRSpatialReference **papoSRS; + + OGRPGTableLayer *poLayerInCopyMode; + + void OGRPGDecodeVersionString(PGver* psVersion, const char* pszVer); + + CPLString osCurrentSchema; + CPLString GetCurrentSchema(); + + int nUndefinedSRID; + + char *pszForcedTables; + char **papszSchemaList; + int bHasLoadTables; + CPLString osActiveSchema; + int bListAllTables; + void LoadTables(); + + CPLString osDebugLastTransactionCommand; + OGRErr DoTransactionCommand(const char* pszCommand); + + OGRErr FlushSoftTransaction(); + + public: + PGver sPostgreSQLVersion; + PGver sPostGISVersion; + + int bUseBinaryCursor; + int bBinaryTimeFormatIsInt8; + int bUseEscapeStringSyntax; + + int GetUndefinedSRID() const { return nUndefinedSRID; } + + public: + OGRPGDataSource(); + ~OGRPGDataSource(); + + PGconn *GetPGConn() { return hPGConn; } + + int FetchSRSId( OGRSpatialReference * poSRS ); + OGRSpatialReference *FetchSRS( int nSRSId ); + OGRErr InitializeMetadataTables(); + + int Open( const char *, int bUpdate, int bTestOpen, + char** papszOpenOptions ); + OGRPGTableLayer* OpenTable( CPLString& osCurrentSchema, + const char * pszTableName, + const char * pszSchemaName, + const char * pszGeomColForced, + int bUpdate, int bTestOpen ); + + const char *GetName() { return pszName; } + int GetLayerCount(); + OGRLayer *GetLayer( int ); + OGRLayer *GetLayerByName(const char * pszName); + + virtual void FlushCache(void); + + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + int TestCapability( const char * ); + + virtual OGRErr StartTransaction(int bForce = FALSE); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + OGRErr SoftStartTransaction(); + OGRErr SoftCommitTransaction(); + OGRErr SoftRollbackTransaction(); + + Oid GetGeometryOID() { return nGeometryOID; } + Oid GetGeographyOID() { return nGeographyOID; } + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + virtual const char* GetMetadataItem(const char* pszKey, + const char* pszDomain); + + int UseCopy(); + void StartCopy( OGRPGTableLayer *poPGLayer ); + OGRErr EndCopy( ); +}; + +#endif /* ndef _OGR_PG_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgdatasource.cpp new file mode 100644 index 000000000..deccd707c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgdatasource.cpp @@ -0,0 +1,2845 @@ +/****************************************************************************** + * $Id: ogrpgdatasource.cpp 29019 2015-04-25 20:34:19Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ogr_pg.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_hash_set.h" +#include + +#define PQexec this_is_an_error + +CPL_CVSID("$Id: ogrpgdatasource.cpp 29019 2015-04-25 20:34:19Z rouault $"); + +static void OGRPGNoticeProcessor( void *arg, const char * pszMessage ); + +/************************************************************************/ +/* OGRPGDataSource() */ +/************************************************************************/ + +OGRPGDataSource::OGRPGDataSource() + +{ + pszName = NULL; + pszDBName = NULL; + papoLayers = NULL; + nLayers = 0; + hPGConn = NULL; + bHavePostGIS = FALSE; + bHaveGeography = FALSE; + bUseBinaryCursor = FALSE; + bUserTransactionActive = FALSE; + bSavePointActive = FALSE; + nSoftTransactionLevel = 0; + bBinaryTimeFormatIsInt8 = FALSE; + bUseEscapeStringSyntax = FALSE; + + nGeometryOID = (Oid) 0; + nGeographyOID = (Oid) 0; + + nKnownSRID = 0; + panSRID = NULL; + papoSRS = NULL; + + poLayerInCopyMode = NULL; + nUndefinedSRID = -1; /* actual value will be autotected if PostGIS >= 2.0 detected */ + + pszForcedTables = NULL; + papszSchemaList = NULL; + bListAllTables = FALSE; + bHasLoadTables = FALSE; +} + +/************************************************************************/ +/* ~OGRPGDataSource() */ +/************************************************************************/ + +OGRPGDataSource::~OGRPGDataSource() + +{ + int i; + + FlushSoftTransaction(); + + CPLFree( pszName ); + CPLFree( pszDBName ); + CPLFree( pszForcedTables ); + CSLDestroy( papszSchemaList ); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + if( hPGConn != NULL ) + { + /* XXX - mloskot: After the connection is closed, valgrind still + * reports 36 bytes definitely lost, somewhere in the libpq. + */ + PQfinish( hPGConn ); + hPGConn = NULL; + } + + for( i = 0; i < nKnownSRID; i++ ) + { + if( papoSRS[i] != NULL ) + papoSRS[i]->Release(); + } + CPLFree( panSRID ); + CPLFree( papoSRS ); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void OGRPGDataSource::FlushCache(void) +{ + EndCopy(); + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + papoLayers[iLayer]->RunDifferedCreationIfNecessary(); + } +} + +/************************************************************************/ +/* GetCurrentSchema() */ +/************************************************************************/ + +CPLString OGRPGDataSource::GetCurrentSchema() +{ + /* -------------------------------------------- */ + /* Get the current schema */ + /* -------------------------------------------- */ + PGresult *hResult = OGRPG_PQexec(hPGConn,"SELECT current_schema()"); + if ( hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult,0,0) ) + { + osCurrentSchema = PQgetvalue(hResult,0,0); + } + OGRPGClearResult( hResult ); + + return osCurrentSchema; +} + +/************************************************************************/ +/* OGRPGDecodeVersionString() */ +/************************************************************************/ + +void OGRPGDataSource::OGRPGDecodeVersionString(PGver* psVersion, const char* pszVer) +{ + GUInt32 iLen; + const char* ptr; + char szNum[25]; + char szVer[10]; + + while ( *pszVer == ' ' ) pszVer++; + + ptr = pszVer; + // get Version string + while (*ptr && *ptr != ' ') ptr++; + iLen = ptr-pszVer; + if ( iLen > sizeof(szVer) - 1 ) iLen = sizeof(szVer) - 1; + strncpy(szVer,pszVer,iLen); + szVer[iLen] = '\0'; + + ptr = pszVer = szVer; + + // get Major number + while (*ptr && *ptr != '.') ptr++; + iLen = ptr-pszVer; + if ( iLen > sizeof(szNum) - 1) iLen = sizeof(szNum) - 1; + strncpy(szNum,pszVer,iLen); + szNum[iLen] = '\0'; + psVersion->nMajor = atoi(szNum); + + if (*ptr == 0) + return; + pszVer = ++ptr; + + // get Minor number + while (*ptr && *ptr != '.') ptr++; + iLen = ptr-pszVer; + if ( iLen > sizeof(szNum) - 1) iLen = sizeof(szNum) - 1; + strncpy(szNum,pszVer,iLen); + szNum[iLen] = '\0'; + psVersion->nMinor = atoi(szNum); + + + if ( *ptr ) + { + pszVer = ++ptr; + + // get Release number + while (*ptr && *ptr != '.') ptr++; + iLen = ptr-pszVer; + if ( iLen > sizeof(szNum) - 1) iLen = sizeof(szNum) - 1; + strncpy(szNum,pszVer,iLen); + szNum[iLen] = '\0'; + psVersion->nRelease = atoi(szNum); + } + +} + + +/************************************************************************/ +/* One entry for each PG table */ +/************************************************************************/ + + +typedef struct +{ + char* pszTableName; + char* pszSchemaName; + int nGeomColumnCount; + PGGeomColumnDesc* pasGeomColumns; /* list of geometry columns */ + int bDerivedInfoAdded; /* set to TRUE if it derives from another table */ +} PGTableEntry; + +static unsigned long OGRPGHashTableEntry(const void * _psTableEntry) +{ + const PGTableEntry* psTableEntry = (PGTableEntry*)_psTableEntry; + return CPLHashSetHashStr(CPLString().Printf("%s.%s", + psTableEntry->pszSchemaName, psTableEntry->pszTableName)); +} + +static int OGRPGEqualTableEntry(const void* _psTableEntry1, const void* _psTableEntry2) +{ + const PGTableEntry* psTableEntry1 = (PGTableEntry*)_psTableEntry1; + const PGTableEntry* psTableEntry2 = (PGTableEntry*)_psTableEntry2; + return strcmp(psTableEntry1->pszTableName, psTableEntry2->pszTableName) == 0 && + strcmp(psTableEntry1->pszSchemaName, psTableEntry2->pszSchemaName) == 0; +} + +static void OGRPGTableEntryAddGeomColumn(PGTableEntry* psTableEntry, + const char* pszName, + const char* pszGeomType = NULL, + int nCoordDimension = 0, + int nSRID = UNDETERMINED_SRID, + PostgisType ePostgisType = GEOM_TYPE_UNKNOWN, + int bNullable = TRUE) +{ + psTableEntry->pasGeomColumns = (PGGeomColumnDesc*) + CPLRealloc(psTableEntry->pasGeomColumns, + sizeof(PGGeomColumnDesc) * (psTableEntry->nGeomColumnCount + 1)); + psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].pszName = CPLStrdup(pszName); + psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].pszGeomType = (pszGeomType) ? CPLStrdup(pszGeomType) : NULL; + psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].nCoordDimension = nCoordDimension; + /* With PostGIS 2.0, querying geometry_columns can return 0, not only when */ + /* the SRID is truly set to 0, but also when there's no constraint */ + psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].nSRID = nSRID > 0 ? nSRID : UNDETERMINED_SRID; + psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].ePostgisType = ePostgisType; + psTableEntry->pasGeomColumns[psTableEntry->nGeomColumnCount].bNullable = bNullable; + psTableEntry->nGeomColumnCount ++; +} + +static void OGRPGTableEntryAddGeomColumn(PGTableEntry* psTableEntry, + const PGGeomColumnDesc* psGeomColumnDesc) +{ + OGRPGTableEntryAddGeomColumn(psTableEntry, + psGeomColumnDesc->pszName, + psGeomColumnDesc->pszGeomType, + psGeomColumnDesc->nCoordDimension, + psGeomColumnDesc->nSRID, + psGeomColumnDesc->ePostgisType, + psGeomColumnDesc->bNullable); +} + +static void OGRPGFreeTableEntry(void * _psTableEntry) +{ + PGTableEntry* psTableEntry = (PGTableEntry*)_psTableEntry; + CPLFree(psTableEntry->pszTableName); + CPLFree(psTableEntry->pszSchemaName); + int i; + for(i=0;inGeomColumnCount;i++) + { + CPLFree(psTableEntry->pasGeomColumns[i].pszName); + CPLFree(psTableEntry->pasGeomColumns[i].pszGeomType); + } + CPLFree(psTableEntry->pasGeomColumns); + CPLFree(psTableEntry); +} + +static PGTableEntry* OGRPGFindTableEntry(CPLHashSet* hSetTables, + const char* pszTableName, + const char* pszSchemaName) +{ + PGTableEntry sEntry; + sEntry.pszTableName = (char*) pszTableName; + sEntry.pszSchemaName = (char*) pszSchemaName; + return (PGTableEntry*) CPLHashSetLookup(hSetTables, &sEntry); +} + +static PGTableEntry* OGRPGAddTableEntry(CPLHashSet* hSetTables, + const char* pszTableName, + const char* pszSchemaName) +{ + PGTableEntry* psEntry = (PGTableEntry*) CPLCalloc(1, sizeof(PGTableEntry)); + psEntry->pszTableName = CPLStrdup(pszTableName); + psEntry->pszSchemaName = CPLStrdup(pszSchemaName); + + CPLHashSetInsert(hSetTables, psEntry); + + return psEntry; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRPGDataSource::Open( const char * pszNewName, int bUpdate, + int bTestOpen, char** papszOpenOptions ) + +{ + CPLAssert( nLayers == 0 ); + +/* -------------------------------------------------------------------- */ +/* Verify postgresql prefix. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszNewName,"PGB:",4) ) + { + bUseBinaryCursor = TRUE; + CPLDebug("PG","BINARY cursor is used for geometry fetching"); + } + else + if( !EQUALN(pszNewName,"PG:",3) ) + { + if( !bTestOpen ) + CPLError( CE_Failure, CPLE_AppDefined, + "%s does not conform to PostgreSQL naming convention," + " PG:*\n", pszNewName ); + return FALSE; + } + + pszName = CPLStrdup( pszNewName ); + + CPLString osConnectionName(pszName); + const char* apszOpenOptions[] = { "dbname", "port", "user", "password", + "host", "active_schema", "schemas", "tables" }; + for(int i=0; i <(int)(sizeof(apszOpenOptions)/sizeof(char*));i++) + { + const char* pszVal = CSLFetchNameValue(papszOpenOptions, apszOpenOptions[i]); + if( pszVal ) + { + if( osConnectionName[osConnectionName.size()-1] != ':' ) + osConnectionName += " "; + osConnectionName += apszOpenOptions[i]; + osConnectionName += "="; + osConnectionName += pszVal; + } + } + + char* pszConnectionName = CPLStrdup(osConnectionName); + + +/* -------------------------------------------------------------------- */ +/* Determine if the connection string contains an optional */ +/* ACTIVE_SCHEMA portion. If so, parse it out. */ +/* -------------------------------------------------------------------- */ + char *pszActiveSchemaStart; + pszActiveSchemaStart = strstr(pszConnectionName, "active_schema="); + if (pszActiveSchemaStart == NULL) + pszActiveSchemaStart = strstr(pszConnectionName, "ACTIVE_SCHEMA="); + if (pszActiveSchemaStart != NULL) + { + char *pszActiveSchema; + const char *pszEnd = NULL; + + pszActiveSchema = CPLStrdup( pszActiveSchemaStart + strlen("active_schema=") ); + + pszEnd = strchr(pszActiveSchemaStart, ' '); + if( pszEnd == NULL ) + pszEnd = pszConnectionName + strlen(pszConnectionName); + + // Remove ACTIVE_SCHEMA=xxxxx from pszConnectionName string + memmove( pszActiveSchemaStart, pszEnd, strlen(pszEnd) + 1 ); + + pszActiveSchema[pszEnd - pszActiveSchemaStart - strlen("active_schema=")] = '\0'; + + osActiveSchema = pszActiveSchema; + CPLFree(pszActiveSchema); + } + else + { + osActiveSchema = "public"; + } + +/* -------------------------------------------------------------------- */ +/* Determine if the connection string contains an optional */ +/* SCHEMAS portion. If so, parse it out. */ +/* -------------------------------------------------------------------- */ + char *pszSchemasStart; + pszSchemasStart = strstr(pszConnectionName, "schemas="); + if (pszSchemasStart == NULL) + pszSchemasStart = strstr(pszConnectionName, "SCHEMAS="); + if (pszSchemasStart != NULL) + { + char *pszSchemas; + const char *pszEnd = NULL; + + pszSchemas = CPLStrdup( pszSchemasStart + strlen("schemas=") ); + + pszEnd = strchr(pszSchemasStart, ' '); + if( pszEnd == NULL ) + pszEnd = pszConnectionName + strlen(pszConnectionName); + + // Remove SCHEMAS=xxxxx from pszConnectionName string + memmove( pszSchemasStart, pszEnd, strlen(pszEnd) + 1 ); + + pszSchemas[pszEnd - pszSchemasStart - strlen("schemas=")] = '\0'; + + papszSchemaList = CSLTokenizeString2( pszSchemas, ",", 0 ); + + CPLFree(pszSchemas); + + /* If there is only one schema specified, make it the active schema */ + if (CSLCount(papszSchemaList) == 1) + { + osActiveSchema = papszSchemaList[0]; + } + } + +/* -------------------------------------------------------------------- */ +/* Determine if the connection string contains an optional */ +/* TABLES portion. If so, parse it out. The expected */ +/* connection string in this case will be, e.g.: */ +/* */ +/* 'PG:dbname=warmerda user=warmerda tables=s1.t1,[s2.t2,...] */ +/* - where sN is schema and tN is table name */ +/* We must also strip this information from the connection */ +/* string; PQconnectdb() does not like unknown directives */ +/* -------------------------------------------------------------------- */ + + char *pszTableStart; + pszTableStart = strstr(pszConnectionName, "tables="); + if (pszTableStart == NULL) + pszTableStart = strstr(pszConnectionName, "TABLES="); + + if( pszTableStart != NULL ) + { + const char *pszEnd = NULL; + + pszForcedTables = CPLStrdup( pszTableStart + 7 ); + + pszEnd = strchr(pszTableStart, ' '); + if( pszEnd == NULL ) + pszEnd = pszConnectionName + strlen(pszConnectionName); + + // Remove TABLES=xxxxx from pszConnectionName string + memmove( pszTableStart, pszEnd, strlen(pszEnd) + 1 ); + + pszForcedTables[pszEnd - pszTableStart - 7] = '\0'; + } + + + CPLString osCurrentSchema; + PGresult *hResult = NULL; + +/* -------------------------------------------------------------------- */ +/* Try to establish connection. */ +/* -------------------------------------------------------------------- */ + hPGConn = PQconnectdb( pszConnectionName + (bUseBinaryCursor ? 4 : 3) ); + CPLFree(pszConnectionName); + pszConnectionName = NULL; + + if( hPGConn == NULL || PQstatus(hPGConn) == CONNECTION_BAD ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "PQconnectdb failed.\n%s", + PQerrorMessage(hPGConn) ); + + PQfinish(hPGConn); + hPGConn = NULL; + + return FALSE; + } + + bDSUpdate = bUpdate; + +/* -------------------------------------------------------------------- */ +/* Set the encoding to UTF8 as the driver advertizes UTF8 */ +/* unless PGCLIENTENCODING is defined */ +/* -------------------------------------------------------------------- */ + if (CPLGetConfigOption("PGCLIENTENCODING", NULL) == NULL) + { + const char* encoding = "UNICODE"; + if (PQsetClientEncoding(hPGConn, encoding) == -1) + { + CPLError( CE_Warning, CPLE_AppDefined, + "PQsetClientEncoding(%s) failed.\n%s", + encoding, PQerrorMessage( hPGConn ) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Install a notice processor. */ +/* -------------------------------------------------------------------- */ + PQsetNoticeProcessor( hPGConn, OGRPGNoticeProcessor, this ); + +/* -------------------------------------------------------------------- */ +/* Try to establish the database name from the connection */ +/* string passed. */ +/* -------------------------------------------------------------------- */ + if( strstr(pszNewName, "dbname=") != NULL ) + { + pszDBName = CPLStrdup( strstr(pszNewName, "dbname=") + 7 ); + + for( int i = 0; pszDBName[i] != '\0'; i++ ) + { + if( pszDBName[i] == ' ' ) + { + pszDBName[i] = '\0'; + break; + } + } + } + else if( getenv( "USER" ) != NULL ) + pszDBName = CPLStrdup( getenv("USER") ); + else + pszDBName = CPLStrdup( "unknown_dbname" ); + + CPLDebug( "PG", "DBName=\"%s\"", pszDBName ); + +/* -------------------------------------------------------------------- */ +/* Set active schema if different from 'public' */ +/* -------------------------------------------------------------------- */ + if (strcmp(osActiveSchema, "public") != 0) + { + CPLString osCommand; + osCommand.Printf("SET search_path='%s',public", osActiveSchema.c_str()); + PGresult *hResult = OGRPG_PQexec(hPGConn, osCommand ); + + if( !hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + OGRPGClearResult( hResult ); + CPLDebug("PG","Command \"%s\" failed. Trying without 'public'.",osCommand.c_str()); + osCommand.Printf("SET search_path='%s'", osActiveSchema.c_str()); + PGresult *hResult2 = OGRPG_PQexec(hPGConn, osCommand ); + + if( !hResult2 || PQresultStatus(hResult2) != PGRES_COMMAND_OK ) + { + OGRPGClearResult( hResult2 ); + + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage(hPGConn) ); + + return FALSE; + } + } + + OGRPGClearResult(hResult); + } + +/* -------------------------------------------------------------------- */ +/* Find out PostgreSQL version */ +/* -------------------------------------------------------------------- */ + sPostgreSQLVersion.nMajor = -1; + sPostgreSQLVersion.nMinor = -1; + sPostgreSQLVersion.nRelease = -1; + + hResult = OGRPG_PQexec(hPGConn, "SELECT version()" ); + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) > 0 ) + { + const char* pszSpace; + char * pszVer = PQgetvalue(hResult,0,0); + + CPLDebug("PG","PostgreSQL version string : '%s'", pszVer); + + /* Should work with "PostgreSQL X.Y.Z ..." or "EnterpriseDB X.Y.Z ..." */ + pszSpace = strchr(pszVer, ' '); + if( pszSpace != NULL && isdigit(pszSpace[1]) ) + { + OGRPGDecodeVersionString(&sPostgreSQLVersion, pszSpace + 1); + if (sPostgreSQLVersion.nMajor == 7 && sPostgreSQLVersion.nMinor < 4) + { + /* We don't support BINARY CURSOR for PostgreSQL < 7.4. */ + /* The binary protocol for arrays seems to be different from later versions */ + CPLDebug("PG","BINARY cursor will finally NOT be used because version < 7.4"); + bUseBinaryCursor = FALSE; + } + } + } + OGRPGClearResult(hResult); + CPLAssert(NULL == hResult); /* Test if safe PQclear has not been broken */ + +/* -------------------------------------------------------------------- */ +/* Test if standard_conforming_strings is recognized */ +/* -------------------------------------------------------------------- */ + + hResult = OGRPG_PQexec(hPGConn, "SHOW standard_conforming_strings" ); + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) == 1 ) + { + /* Whatever the value is, it means that we can use the E'' */ + /* syntax */ + bUseEscapeStringSyntax = TRUE; + } + OGRPGClearResult(hResult); + +/* -------------------------------------------------------------------- */ +/* Test if time binary format is int8 or float8 */ +/* -------------------------------------------------------------------- */ +#if !defined(PG_PRE74) + if (bUseBinaryCursor) + { + SoftStartTransaction(); + + hResult = OGRPG_PQexec(hPGConn, "DECLARE gettimebinaryformat BINARY CURSOR FOR SELECT CAST ('00:00:01' AS time)"); + + if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK ) + { + OGRPGClearResult( hResult ); + + hResult = OGRPG_PQexec(hPGConn, "FETCH ALL IN gettimebinaryformat" ); + + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK && PQntuples(hResult) == 1 ) + { + if ( PQfformat( hResult, 0 ) == 1 ) // Binary data representation + { + CPLAssert(PQgetlength(hResult, 0, 0) == 8); + double dVal; + unsigned int nVal[2]; + memcpy( nVal, PQgetvalue( hResult, 0, 0 ), 8 ); + CPL_MSBPTR32(&nVal[0]); + CPL_MSBPTR32(&nVal[1]); + memcpy( &dVal, PQgetvalue( hResult, 0, 0 ), 8 ); + CPL_MSBPTR64(&dVal); + if (nVal[0] == 0 && nVal[1] == 1000000) + { + bBinaryTimeFormatIsInt8 = TRUE; + CPLDebug( "PG", "Time binary format is int8"); + } + else if (dVal == 1.) + { + bBinaryTimeFormatIsInt8 = FALSE; + CPLDebug( "PG", "Time binary format is float8"); + } + else + { + bBinaryTimeFormatIsInt8 = FALSE; + CPLDebug( "PG", "Time binary format is unknown"); + } + } + } + } + + OGRPGClearResult( hResult ); + + hResult = OGRPG_PQexec(hPGConn, "CLOSE gettimebinaryformat"); + OGRPGClearResult( hResult ); + + SoftCommitTransaction(); + } +#endif + +#ifdef notdef + /* This would be the quickest fix... instead, ogrpglayer has been updated to support */ + /* bytea hex format */ + if (sPostgreSQLVersion.nMajor >= 9) + { + /* Starting with PostgreSQL 9.0, the default output format for values of type bytea */ + /* is hex, whereas we traditionnaly expect escape */ + hResult = OGRPG_PQexec(hPGConn, "SET bytea_output TO escape"); + OGRPGClearResult( hResult ); + } +#endif + +/* -------------------------------------------------------------------- */ +/* Test to see if this database instance has support for the */ +/* PostGIS Geometry type. If so, disable sequential scanning */ +/* so we will get the value of the gist indexes. */ +/* -------------------------------------------------------------------- */ + hResult = OGRPG_PQexec(hPGConn, + "SELECT oid, typname FROM pg_type WHERE typname IN ('geometry', 'geography')" ); + + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) > 0 && CSLTestBoolean(CPLGetConfigOption("PG_USE_POSTGIS", "YES"))) + { + for( int iRecord = 0; iRecord < PQntuples(hResult); iRecord++ ) + { + const char *pszOid = PQgetvalue(hResult, iRecord, 0); + const char *pszTypname = PQgetvalue(hResult, iRecord, 1); + if( EQUAL(pszTypname, "geometry") ) + { + bHavePostGIS = TRUE; + nGeometryOID = atoi(pszOid); + } + else if( CSLTestBoolean(CPLGetConfigOption("PG_USE_GEOGRAPHY", "YES")) ) + { + bHaveGeography = TRUE; + nGeographyOID = atoi(pszOid); + } + } + } + + OGRPGClearResult( hResult ); + +/* -------------------------------------------------------------------- */ +/* Find out PostGIS version */ +/* -------------------------------------------------------------------- */ + + sPostGISVersion.nMajor = -1; + sPostGISVersion.nMinor = -1; + sPostGISVersion.nRelease = -1; + + if( bHavePostGIS ) + { + hResult = OGRPG_PQexec(hPGConn, "SELECT postgis_version()" ); + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) > 0 ) + { + char * pszVer = PQgetvalue(hResult,0,0); + + CPLDebug("PG","PostGIS version string : '%s'", pszVer); + + OGRPGDecodeVersionString(&sPostGISVersion, pszVer); + + } + OGRPGClearResult(hResult); + + + if (sPostGISVersion.nMajor == 0 && sPostGISVersion.nMinor < 8) + { + // Turning off sequential scans for PostGIS < 0.8 + hResult = OGRPG_PQexec(hPGConn, "SET ENABLE_SEQSCAN = OFF"); + + CPLDebug( "PG", "SET ENABLE_SEQSCAN=OFF" ); + } + else + { + // PostGIS >=0.8 is correctly integrated with query planner, + // thus PostgreSQL will use indexes whenever appropriate. + hResult = OGRPG_PQexec(hPGConn, "SET ENABLE_SEQSCAN = ON"); + } + OGRPGClearResult( hResult ); + } + +/* -------------------------------------------------------------------- */ +/* Find out "unknown SRID" value */ +/* -------------------------------------------------------------------- */ + + if (sPostGISVersion.nMajor >= 2) + { + hResult = OGRPG_PQexec(hPGConn, + "SELECT ST_Srid('POINT EMPTY'::GEOMETRY)" ); + + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) > 0) + { + nUndefinedSRID = atoi(PQgetvalue(hResult,0,0)); + } + + OGRPGClearResult( hResult ); + } + else + nUndefinedSRID = -1; + + osCurrentSchema = GetCurrentSchema(); + + bListAllTables = CSLTestBoolean(CSLFetchNameValueDef( + papszOpenOptions, "LIST_ALL_TABLES", + CPLGetConfigOption("PG_LIST_ALL_TABLES", "NO"))); + + return TRUE; +} + +/************************************************************************/ +/* LoadTables() */ +/************************************************************************/ + +void OGRPGDataSource::LoadTables() +{ + if( bHasLoadTables ) + return; + bHasLoadTables = TRUE; + + PGresult *hResult; + PGTableEntry **papsTables = NULL; + int nTableCount = 0; + CPLHashSet *hSetTables = NULL; + std::set osRegisteredLayers; + + int i; + for( i = 0; i < nLayers; i++) + { + osRegisteredLayers.insert(papoLayers[i]->GetName()); + } + + if( pszForcedTables ) + { + char **papszTableList; + + papszTableList = CSLTokenizeString2( pszForcedTables, ",", 0 ); + + for( i = 0; i < CSLCount(papszTableList); i++ ) + { + char **papszQualifiedParts; + + // Get schema and table name + papszQualifiedParts = CSLTokenizeString2( papszTableList[i], + ".", 0 ); + int nParts = CSLCount( papszQualifiedParts ); + + if( nParts == 1 || nParts == 2 ) + { + /* Find the geometry column name if specified */ + char* pszGeomColumnName = NULL; + char* pos = strchr(papszQualifiedParts[CSLCount( papszQualifiedParts ) - 1], '('); + if (pos != NULL) + { + *pos = '\0'; + pszGeomColumnName = pos+1; + int len = strlen(pszGeomColumnName); + if (len > 0) + pszGeomColumnName[len - 1] = '\0'; + } + + papsTables = (PGTableEntry**)CPLRealloc(papsTables, sizeof(PGTableEntry*) * (nTableCount + 1)); + papsTables[nTableCount] = (PGTableEntry*) CPLCalloc(1, sizeof(PGTableEntry)); + if (pszGeomColumnName) + OGRPGTableEntryAddGeomColumn(papsTables[nTableCount], pszGeomColumnName); + + if( nParts == 2 ) + { + papsTables[nTableCount]->pszSchemaName = CPLStrdup( papszQualifiedParts[0] ); + papsTables[nTableCount]->pszTableName = CPLStrdup( papszQualifiedParts[1] ); + } + else + { + papsTables[nTableCount]->pszSchemaName = CPLStrdup( osActiveSchema.c_str()); + papsTables[nTableCount]->pszTableName = CPLStrdup( papszQualifiedParts[0] ); + } + nTableCount ++; + } + + CSLDestroy(papszQualifiedParts); + } + + CSLDestroy(papszTableList); + } + +/* -------------------------------------------------------------------- */ +/* Get a list of available tables if they have not been */ +/* specified through the TABLES connection string param */ +/* -------------------------------------------------------------------- */ + const char* pszAllowedRelations; + if( CSLTestBoolean(CPLGetConfigOption("PG_SKIP_VIEWS", "NO")) ) + pszAllowedRelations = "'r'"; + else + pszAllowedRelations = "'r','v','m','f'"; + + hSetTables = CPLHashSetNew(OGRPGHashTableEntry, OGRPGEqualTableEntry, OGRPGFreeTableEntry); + + if( nTableCount == 0 && bHavePostGIS && sPostGISVersion.nMajor >= 2 && + !bListAllTables && + /* Config option mostly for comparison/debugging/etc... */ + CSLTestBoolean(CPLGetConfigOption("PG_USE_POSTGIS2_OPTIM", "YES")) ) + { +/* -------------------------------------------------------------------- */ +/* With PostGIS 2.0, the geometry_columns and geography_columns */ +/* are views, based on the catalog system, that can be slow to */ +/* query, so query directly the catalog system. */ +/* See http://trac.osgeo.org/postgis/ticket/3092 */ +/* -------------------------------------------------------------------- */ + CPLString osCommand; + osCommand.Printf( + "SELECT c.relname, n.nspname, c.relkind, a.attname, t.typname, " + "postgis_typmod_dims(a.atttypmod) dim, " + "postgis_typmod_srid(a.atttypmod) srid, " + "postgis_typmod_type(a.atttypmod)::text geomtyp, " + "array_agg(s.consrc)::text att_constraints, a.attnotnull " + "FROM pg_class c JOIN pg_attribute a ON a.attrelid=c.oid " + "JOIN pg_namespace n ON c.relnamespace = n.oid " + "AND c.relkind in (%s) AND NOT ( n.nspname = 'public' AND c.relname = 'raster_columns' ) " + "JOIN pg_type t ON a.atttypid = t.oid AND (t.typname = 'geometry'::name OR t.typname = 'geography'::name) " + "LEFT JOIN pg_constraint s ON s.connamespace = n.oid AND s.conrelid = c.oid " + "AND a.attnum = ANY (s.conkey) " + "AND (s.consrc LIKE '%%geometrytype(%% = %%' OR s.consrc LIKE '%%ndims(%% = %%' OR s.consrc LIKE '%%srid(%% = %%') " + "GROUP BY c.relname, n.nspname, c.relkind, a.attname, t.typname, dim, srid, geomtyp, a.attnotnull, c.oid, a.attnum " + "ORDER BY c.oid, a.attnum", + pszAllowedRelations); + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str()); + + if( !hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK ) + { + OGRPGClearResult( hResult ); + + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage(hPGConn) ); + goto end; + } + /* -------------------------------------------------------------------- */ + /* Parse the returned table list */ + /* -------------------------------------------------------------------- */ + for( int iRecord = 0; iRecord < PQntuples(hResult); iRecord++ ) + { + const char *pszTable = PQgetvalue(hResult, iRecord, 0); + const char *pszSchemaName = PQgetvalue(hResult, iRecord, 1); + const char *pszGeomColumnName = PQgetvalue(hResult, iRecord, 3); + const char *pszGeomOrGeography = PQgetvalue(hResult, iRecord, 4); + const char *pszDim = PQgetvalue(hResult, iRecord, 5); + const char *pszSRID = PQgetvalue(hResult, iRecord, 6); + const char *pszGeomType = PQgetvalue(hResult, iRecord, 7); + const char *pszConstraint = PQgetvalue(hResult, iRecord, 8); + const char *pszNotNull = PQgetvalue(hResult, iRecord, 9); + /*const char *pszRelkind = PQgetvalue(hResult, iRecord, 2); + CPLDebug("PG", "%s %s %s %s %s %s %s %s %s %s", + pszTable, pszSchemaName, pszRelkind, + pszGeomColumnName, pszGeomOrGeography, pszDim, + pszSRID, pszGeomType, pszConstraint, pszNotNull);*/ + + int bNullable = EQUAL(pszNotNull, "f"); + + PostgisType ePostgisType = GEOM_TYPE_UNKNOWN; + if( EQUAL(pszGeomOrGeography, "geometry") ) + ePostgisType = GEOM_TYPE_GEOMETRY; + else if( EQUAL(pszGeomOrGeography, "geography") ) + ePostgisType = GEOM_TYPE_GEOGRAPHY; + + int nGeomCoordDimension = atoi(pszDim); + int nSRID = atoi(pszSRID); + + /* Analyze constraints that might override geometrytype, */ + /* coordinate dimension and SRID */ + CPLString osConstraint(pszConstraint); + osConstraint = osConstraint.tolower(); + pszConstraint = osConstraint.c_str(); + const char* pszNeedle = strstr(pszConstraint, "geometrytype("); + CPLString osGeometryType; + if( pszNeedle ) + { + pszNeedle = strchr(pszNeedle, '\''); + if( pszNeedle ) + { + pszNeedle ++; + const char* pszEnd = strchr(pszNeedle, '\''); + if( pszEnd ) + { + osGeometryType = pszNeedle; + osGeometryType.resize(pszEnd - pszNeedle); + pszGeomType = osGeometryType.c_str(); + } + } + } + + pszNeedle = strstr(pszConstraint, "srid("); + if( pszNeedle ) + { + pszNeedle = strchr(pszNeedle, '='); + if( pszNeedle ) + { + pszNeedle ++; + nSRID = atoi(pszNeedle); + } + } + + pszNeedle = strstr(pszConstraint, "ndims("); + if( pszNeedle ) + { + pszNeedle = strchr(pszNeedle, '='); + if( pszNeedle ) + { + pszNeedle ++; + nGeomCoordDimension = atoi(pszNeedle); + } + } + + papsTables = (PGTableEntry**)CPLRealloc(papsTables, sizeof(PGTableEntry*) * (nTableCount + 1)); + papsTables[nTableCount] = (PGTableEntry*) CPLCalloc(1, sizeof(PGTableEntry)); + papsTables[nTableCount]->pszTableName = CPLStrdup( pszTable ); + papsTables[nTableCount]->pszSchemaName = CPLStrdup( pszSchemaName ); + + OGRPGTableEntryAddGeomColumn(papsTables[nTableCount], + pszGeomColumnName, + pszGeomType, nGeomCoordDimension, + nSRID, ePostgisType, bNullable); + nTableCount ++; + + PGTableEntry* psEntry = OGRPGFindTableEntry(hSetTables, pszTable, pszSchemaName); + if (psEntry == NULL) + psEntry = OGRPGAddTableEntry(hSetTables, pszTable, pszSchemaName); + OGRPGTableEntryAddGeomColumn(psEntry, + pszGeomColumnName, + pszGeomType, + nGeomCoordDimension, + nSRID, ePostgisType, bNullable); + } + + OGRPGClearResult( hResult ); + } + else if (nTableCount == 0) + { + CPLString osCommand; + + /* Caution : in PostGIS case, the result has 11 columns, whereas in the */ + /* non-PostGIS case it has only 3 columns */ + if ( bHavePostGIS && !bListAllTables ) + { + osCommand.Printf( "SELECT c.relname, n.nspname, c.relkind, g.f_geometry_column, g.type, g.coord_dimension, g.srid, %d, a.attnotnull, c.oid as oid, a.attnum as attnum FROM pg_class c, pg_namespace n, geometry_columns g, pg_attribute a " + "WHERE (c.relkind in (%s) AND c.relname !~ '^pg_' AND c.relnamespace=n.oid " + "AND c.relname::TEXT = g.f_table_name::TEXT AND n.nspname = g.f_table_schema AND a.attname = g.f_geometry_column AND a.attrelid = c.oid) ", + GEOM_TYPE_GEOMETRY, pszAllowedRelations); + + if (bHaveGeography) + osCommand += CPLString().Printf( + "UNION SELECT c.relname, n.nspname, c.relkind, g.f_geography_column, g.type, g.coord_dimension, g.srid, %d, a.attnotnull, c.oid as oid, a.attnum as attnum FROM pg_class c, pg_namespace n, geography_columns g, pg_attribute a " + "WHERE (c.relkind in (%s) AND c.relname !~ '^pg_' AND c.relnamespace=n.oid " + "AND c.relname::TEXT = g.f_table_name::TEXT AND n.nspname = g.f_table_schema AND a.attname = g.f_geography_column AND a.attrelid = c.oid)", + GEOM_TYPE_GEOGRAPHY, pszAllowedRelations); + osCommand += " ORDER BY oid, attnum"; + } + else + osCommand.Printf( + "SELECT c.relname, n.nspname, c.relkind FROM pg_class c, pg_namespace n " + "WHERE (c.relkind in (%s) AND c.relname !~ '^pg_' AND c.relnamespace=n.oid)", + pszAllowedRelations); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str()); + + if( !hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK ) + { + OGRPGClearResult( hResult ); + + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage(hPGConn) ); + goto end; + } + + /* -------------------------------------------------------------------- */ + /* Parse the returned table list */ + /* -------------------------------------------------------------------- */ + for( int iRecord = 0; iRecord < PQntuples(hResult); iRecord++ ) + { + const char *pszTable = PQgetvalue(hResult, iRecord, 0); + const char *pszSchemaName = PQgetvalue(hResult, iRecord, 1); + const char *pszRelkind = PQgetvalue(hResult, iRecord, 2); + const char *pszGeomColumnName = NULL; + const char *pszGeomType = NULL; + int nGeomCoordDimension = 0; + int nSRID = 0; + int bNullable = TRUE; + PostgisType ePostgisType = GEOM_TYPE_UNKNOWN; + if (bHavePostGIS && !bListAllTables) + { + pszGeomColumnName = PQgetvalue(hResult, iRecord, 3); + pszGeomType = PQgetvalue(hResult, iRecord, 4); + nGeomCoordDimension = atoi(PQgetvalue(hResult, iRecord, 5)); + nSRID = atoi(PQgetvalue(hResult, iRecord, 6)); + ePostgisType = (PostgisType) atoi(PQgetvalue(hResult, iRecord, 7)); + bNullable = EQUAL(PQgetvalue(hResult, iRecord, 8), "f"); + + /* We cannot reliably find geometry columns of a view that is */ + /* based on a table that inherits from another one, wit that */ + /* method, so give up, and let OGRPGTableLayer::ReadTableDefinition() */ + /* do the job */ + if( pszRelkind[0] == 'v' && sPostGISVersion.nMajor < 2 ) + pszGeomColumnName = NULL; + } + + if( EQUAL(pszTable,"spatial_ref_sys") + || EQUAL(pszTable,"geometry_columns") + || EQUAL(pszTable,"geography_columns") ) + continue; + + if( EQUAL(pszSchemaName,"information_schema") ) + continue; + + papsTables = (PGTableEntry**)CPLRealloc(papsTables, sizeof(PGTableEntry*) * (nTableCount + 1)); + papsTables[nTableCount] = (PGTableEntry*) CPLCalloc(1, sizeof(PGTableEntry)); + papsTables[nTableCount]->pszTableName = CPLStrdup( pszTable ); + papsTables[nTableCount]->pszSchemaName = CPLStrdup( pszSchemaName ); + if (pszGeomColumnName) + OGRPGTableEntryAddGeomColumn(papsTables[nTableCount], + pszGeomColumnName, + pszGeomType, nGeomCoordDimension, + nSRID, ePostgisType, bNullable); + nTableCount ++; + + PGTableEntry* psEntry = OGRPGFindTableEntry(hSetTables, pszTable, pszSchemaName); + if (psEntry == NULL) + psEntry = OGRPGAddTableEntry(hSetTables, pszTable, pszSchemaName); + if (pszGeomColumnName) + OGRPGTableEntryAddGeomColumn(psEntry, + pszGeomColumnName, + pszGeomType, + nGeomCoordDimension, + nSRID, ePostgisType, bNullable); + } + + /* -------------------------------------------------------------------- */ + /* Cleanup */ + /* -------------------------------------------------------------------- */ + OGRPGClearResult( hResult ); + + /* With PostGIS 2.0, we don't need to query base tables of inherited */ + /* tables */ + if ( bHavePostGIS && !bListAllTables && sPostGISVersion.nMajor < 2 ) + { + /* -------------------------------------------------------------------- */ + /* Fetch inherited tables */ + /* -------------------------------------------------------------------- */ + hResult = OGRPG_PQexec(hPGConn, + "SELECT c1.relname AS derived, c2.relname AS parent, n.nspname " + "FROM pg_class c1, pg_class c2, pg_namespace n, pg_inherits i " + "WHERE i.inhparent = c2.oid AND i.inhrelid = c1.oid AND c1.relnamespace=n.oid " + "AND c1.relkind in ('r', 'v') AND c1.relnamespace=n.oid AND c2.relkind in ('r','v') " + "AND c2.relname !~ '^pg_' AND c2.relnamespace=n.oid"); + + if( !hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK ) + { + OGRPGClearResult( hResult ); + + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage(hPGConn) ); + goto end; + } + + /* -------------------------------------------------------------------- */ + /* Parse the returned table list */ + /* -------------------------------------------------------------------- */ + int bHasDoneSomething; + do + { + /* Iterate over the tuples while we have managed to resolved at least one */ + /* table to its table parent with a geometry */ + /* For example if we have C inherits B and B inherits A, where A is a base table with a geometry */ + /* The first pass will add B to the set of tables */ + /* The second pass will add C to the set of tables */ + + bHasDoneSomething = FALSE; + + for( int iRecord = 0; iRecord < PQntuples(hResult); iRecord++ ) + { + const char *pszTable = PQgetvalue(hResult, iRecord, 0); + const char *pszParentTable = PQgetvalue(hResult, iRecord, 1); + const char *pszSchemaName = PQgetvalue(hResult, iRecord, 2); + + PGTableEntry* psEntry = OGRPGFindTableEntry(hSetTables, pszTable, pszSchemaName); + /* We must be careful that a derived table can have its own geometry column(s) */ + /* and some inherited from another table */ + if (psEntry == NULL || psEntry->bDerivedInfoAdded == FALSE) + { + PGTableEntry* psParentEntry = + OGRPGFindTableEntry(hSetTables, pszParentTable, pszSchemaName); + if (psParentEntry != NULL) + { + /* The parent table of this table is already in the set, so we */ + /* can now add the table in the set if it was not in it already */ + + bHasDoneSomething = TRUE; + + if (psEntry == NULL) + psEntry = OGRPGAddTableEntry(hSetTables, pszTable, pszSchemaName); + + int iGeomColumn; + for(iGeomColumn = 0; iGeomColumn < psParentEntry->nGeomColumnCount; iGeomColumn++) + { + papsTables = (PGTableEntry**)CPLRealloc(papsTables, sizeof(PGTableEntry*) * (nTableCount + 1)); + papsTables[nTableCount] = (PGTableEntry*) CPLCalloc(1, sizeof(PGTableEntry)); + papsTables[nTableCount]->pszTableName = CPLStrdup( pszTable ); + papsTables[nTableCount]->pszSchemaName = CPLStrdup( pszSchemaName ); + OGRPGTableEntryAddGeomColumn(papsTables[nTableCount], + &psParentEntry->pasGeomColumns[iGeomColumn]); + nTableCount ++; + + OGRPGTableEntryAddGeomColumn(psEntry, + &psParentEntry->pasGeomColumns[iGeomColumn]); + } + + psEntry->bDerivedInfoAdded = TRUE; + } + } + } + } while(bHasDoneSomething); + + /* -------------------------------------------------------------------- */ + /* Cleanup */ + /* -------------------------------------------------------------------- */ + OGRPGClearResult( hResult ); + } + } + +/* -------------------------------------------------------------------- */ +/* Register the available tables. */ +/* -------------------------------------------------------------------- */ + for( int iRecord = 0; iRecord < nTableCount; iRecord++ ) + { + PGTableEntry* psEntry; + CPLString osDefnName; + psEntry = (PGTableEntry* )CPLHashSetLookup(hSetTables, papsTables[iRecord]); + + /* If SCHEMAS= is specified, only take into account tables inside */ + /* one of the specified schemas */ + if (papszSchemaList != NULL && + CSLFindString(papszSchemaList, papsTables[iRecord]->pszSchemaName) == -1) + { + continue; + } + + if ( papsTables[iRecord]->pszSchemaName && + osCurrentSchema != papsTables[iRecord]->pszSchemaName ) + { + osDefnName.Printf("%s.%s", papsTables[iRecord]->pszSchemaName, + papsTables[iRecord]->pszTableName ); + } + else + { + //no prefix for current_schema in layer name, for backwards compatibility + osDefnName = papsTables[iRecord]->pszTableName; + } + if( osRegisteredLayers.find( osDefnName ) != osRegisteredLayers.end() ) + continue; + osRegisteredLayers.insert( osDefnName ); + + OGRPGTableLayer* poLayer; + poLayer = OpenTable( osCurrentSchema, papsTables[iRecord]->pszTableName, + papsTables[iRecord]->pszSchemaName, + NULL, bDSUpdate, FALSE ); + if( psEntry != NULL ) + { + if( psEntry->nGeomColumnCount > 0 ) + { + poLayer->SetGeometryInformation(psEntry->pasGeomColumns, + psEntry->nGeomColumnCount); + } + } + else + { + if( papsTables[iRecord]->nGeomColumnCount > 0 ) + { + poLayer->SetGeometryInformation(papsTables[iRecord]->pasGeomColumns, + papsTables[iRecord]->nGeomColumnCount); + } + } + } + +end: + if (hSetTables) + CPLHashSetDestroy(hSetTables); + + for(int i=0;iReadTableDefinition()) ) + { + delete poLayer; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRPGTableLayer **) + CPLRealloc( papoLayers, sizeof(OGRPGTableLayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + + return poLayer; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +int OGRPGDataSource::DeleteLayer( int iLayer ) + +{ + /* Force loading of all registered tables */ + GetLayerCount(); + if( iLayer < 0 || iLayer >= nLayers ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Blow away our OGR structures related to the layer. This is */ +/* pretty dangerous if anything has a reference to this layer! */ +/* -------------------------------------------------------------------- */ + CPLString osLayerName = papoLayers[iLayer]->GetLayerDefn()->GetName(); + CPLString osTableName = papoLayers[iLayer]->GetTableName(); + CPLString osSchemaName = papoLayers[iLayer]->GetSchemaName(); + + CPLDebug( "PG", "DeleteLayer(%s)", osLayerName.c_str() ); + + delete papoLayers[iLayer]; + memmove( papoLayers + iLayer, papoLayers + iLayer + 1, + sizeof(void *) * (nLayers - iLayer - 1) ); + nLayers--; + + if (osLayerName.size() == 0) + return OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* Remove from the database. */ +/* -------------------------------------------------------------------- */ + PGresult *hResult; + CPLString osCommand; + + SoftStartTransaction(); + + if( bHavePostGIS && sPostGISVersion.nMajor < 2) + { + /* This is unnecessary if the layer is not a geometry table, or an inherited geometry table */ + /* but it shouldn't hurt */ + osCommand.Printf( + "DELETE FROM geometry_columns WHERE f_table_name='%s' and f_table_schema='%s'", + osTableName.c_str(), osSchemaName.c_str() ); + + hResult = OGRPG_PQexec( hPGConn, osCommand.c_str() ); + OGRPGClearResult( hResult ); + } + + osCommand.Printf("DROP TABLE %s.%s CASCADE", + OGRPGEscapeColumnName(osSchemaName).c_str(), + OGRPGEscapeColumnName(osTableName).c_str() ); + hResult = OGRPG_PQexec( hPGConn, osCommand.c_str() ); + OGRPGClearResult( hResult ); + + SoftCommitTransaction(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRPGDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ) + +{ + PGresult *hResult = NULL; + CPLString osCommand; + const char *pszGeomType = NULL; + char *pszTableName = NULL; + char *pszSchemaName = NULL; + int nDimension = 3; + + if (pszLayerName == NULL) + return NULL; + + EndCopy(); + + const char* pszFIDColumnName = CSLFetchNameValue(papszOptions, "FID"); + CPLString osFIDColumnName; + if (pszFIDColumnName == NULL) + osFIDColumnName = "ogc_fid"; + else + { + if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) ) + { + char* pszLaunderedFid = OGRPGCommonLaunderName(pszFIDColumnName, "PG"); + osFIDColumnName += OGRPGEscapeColumnName(pszLaunderedFid); + CPLFree(pszLaunderedFid); + } + else + osFIDColumnName += OGRPGEscapeColumnName(pszFIDColumnName); + } + pszFIDColumnName = osFIDColumnName.c_str(); + + if (strncmp(pszLayerName, "pg", 2) == 0) + { + CPLError(CE_Warning, CPLE_AppDefined, + "The layer name should not begin by 'pg' as it is a reserved prefix"); + } + + if( wkbFlatten(eType) == eType ) + nDimension = 2; + + int nForcedDimension = -1; + if( CSLFetchNameValue( papszOptions, "DIM") != NULL ) + { + nDimension = atoi(CSLFetchNameValue( papszOptions, "DIM")); + nForcedDimension = nDimension; + } + + /* Should we turn layers with None geometry type as Unknown/GEOMETRY */ + /* so they are still recorded in geometry_columns table ? (#4012) */ + int bNoneAsUnknown = CSLTestBoolean(CSLFetchNameValueDef( + papszOptions, "NONE_AS_UNKNOWN", "NO")); + if (bNoneAsUnknown && eType == wkbNone) + eType = wkbUnknown; + + + int bExtractSchemaFromLayerName = CSLTestBoolean(CSLFetchNameValueDef( + papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES")); + + /* Postgres Schema handling: + Extract schema name from input layer name or passed with -lco SCHEMA. + Set layer name to "schema.table" or to "table" if schema == current_schema() + Usage without schema name is backwards compatible + */ + const char* pszDotPos = strstr(pszLayerName,"."); + if ( pszDotPos != NULL && bExtractSchemaFromLayerName ) + { + int length = pszDotPos - pszLayerName; + pszSchemaName = (char*)CPLMalloc(length+1); + strncpy(pszSchemaName, pszLayerName, length); + pszSchemaName[length] = '\0'; + + if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) ) + pszTableName = OGRPGCommonLaunderName( pszDotPos + 1, "PG" ); //skip "." + else + pszTableName = CPLStrdup( pszDotPos + 1 ); //skip "." + } + else + { + pszSchemaName = NULL; + if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) ) + pszTableName = OGRPGCommonLaunderName( pszLayerName, "PG" ); //skip "." + else + pszTableName = CPLStrdup( pszLayerName ); //skip "." + } + +/* -------------------------------------------------------------------- */ +/* Set the default schema for the layers. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue( papszOptions, "SCHEMA" ) != NULL ) + { + CPLFree(pszSchemaName); + pszSchemaName = CPLStrdup(CSLFetchNameValue( papszOptions, "SCHEMA" )); + } + + if ( pszSchemaName == NULL ) + { + pszSchemaName = CPLStrdup(osCurrentSchema); + } + +/* -------------------------------------------------------------------- */ +/* Do we already have this layer? If so, should we blow it */ +/* away? */ +/* -------------------------------------------------------------------- */ + int iLayer; + + CPLString osSQLLayerName; + if (pszSchemaName == NULL || (strlen(osCurrentSchema) > 0 && EQUAL(pszSchemaName, osCurrentSchema.c_str()))) + osSQLLayerName = pszTableName; + else + { + osSQLLayerName = pszSchemaName; + osSQLLayerName += "."; + osSQLLayerName += pszTableName; + } + + /* GetLayerByName() can instanciate layers that would have been */ + /* 'hidden' otherwise, for example, non-spatial tables in a */ + /* Postgis-enabled database, so this apparently useless command is */ + /* not useless... (#4012) */ + CPLPushErrorHandler(CPLQuietErrorHandler); + GetLayerByName(osSQLLayerName); + CPLPopErrorHandler(); + CPLErrorReset(); + + /* Force loading of all registered tables */ + GetLayerCount(); + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(osSQLLayerName.c_str(),papoLayers[iLayer]->GetName()) ) + { + if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL + && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") ) + { + DeleteLayer( iLayer ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + osSQLLayerName.c_str() ); + CPLFree( pszTableName ); + CPLFree( pszSchemaName ); + return NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Handle the GEOM_TYPE option. */ +/* -------------------------------------------------------------------- */ + pszGeomType = CSLFetchNameValue( papszOptions, "GEOM_TYPE" ); + if( pszGeomType == NULL ) + { + if( bHavePostGIS ) + pszGeomType = "geometry"; + else + pszGeomType = "bytea"; + } + + const char *pszGFldName = NULL; + if( eType != wkbNone && EQUAL(pszGeomType, "geography") ) + { + if( !bHaveGeography ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GEOM_TYPE=geography is only supported in PostGIS >= 1.5.\n" + "Creation of layer %s has failed.", + pszLayerName ); + CPLFree( pszTableName ); + CPLFree( pszSchemaName ); + return NULL; + } + + if( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL ) + pszGFldName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME"); + else + pszGFldName = "the_geog"; + } + else if ( eType != wkbNone && bHavePostGIS && !EQUAL(pszGeomType, "geography") ) + { + if( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL ) + pszGFldName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME"); + else + pszGFldName = "wkb_geometry"; + } + + if( eType != wkbNone && bHavePostGIS && !EQUAL(pszGeomType,"geometry") && + !EQUAL(pszGeomType, "geography") ) + { + if( bHaveGeography ) + CPLError( CE_Failure, CPLE_AppDefined, + "GEOM_TYPE in PostGIS enabled databases must be 'geometry' or 'geography'.\n" + "Creation of layer %s with GEOM_TYPE %s has failed.", + pszLayerName, pszGeomType ); + else + CPLError( CE_Failure, CPLE_AppDefined, + "GEOM_TYPE in PostGIS enabled databases must be 'geometry'.\n" + "Creation of layer %s with GEOM_TYPE %s has failed.", + pszLayerName, pszGeomType ); + + CPLFree( pszTableName ); + CPLFree( pszSchemaName ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to get the SRS Id of this spatial reference system, */ +/* adding tot the srs table if needed. */ +/* -------------------------------------------------------------------- */ + int nSRSId = nUndefinedSRID; + + if( poSRS != NULL ) + nSRSId = FetchSRSId( poSRS ); + + const char *pszGeometryType = OGRToOGCGeomType(eType); + + int bDifferedCreation = CSLTestBoolean(CPLGetConfigOption( "OGR_PG_DIFFERED_CREATION", "YES" )); + if( !bHavePostGIS ) + bDifferedCreation = FALSE; /* to avoid unnecessary implementation and testing burden */ + +/* -------------------------------------------------------------------- */ +/* Create a basic table with the FID. Also include the */ +/* geometry if this is not a PostGIS enabled table. */ +/* -------------------------------------------------------------------- */ + int bFID64 = CSLFetchBoolean(papszOptions, "FID64", FALSE); + const char* pszSerialType = bFID64 ? "BIGSERIAL": "SERIAL"; + + CPLString osCreateTable; + int bTemporary = CSLFetchBoolean( papszOptions, "TEMPORARY", FALSE ); + if (bTemporary) + { + CPLFree(pszSchemaName); + pszSchemaName = CPLStrdup("pg_temp_1"); + osCreateTable.Printf("CREATE TEMPORARY TABLE %s", + OGRPGEscapeColumnName(pszTableName).c_str()); + } + else + osCreateTable.Printf("CREATE%s TABLE %s.%s", + CSLFetchBoolean( papszOptions, "UNLOGGED", FALSE ) ? " UNLOGGED": "", + OGRPGEscapeColumnName(pszSchemaName).c_str(), + OGRPGEscapeColumnName(pszTableName).c_str()); + + if( eType != wkbNone && !bHavePostGIS ) + { + pszGFldName = "wkb_geometry"; + osCommand.Printf( + "%s ( " + " %s %s, " + " %s %s, " + " PRIMARY KEY (%s)", + osCreateTable.c_str(), + pszFIDColumnName, + pszSerialType, + pszGFldName, + pszGeomType, + pszFIDColumnName); + } + else if ( !bDifferedCreation && eType != wkbNone && EQUAL(pszGeomType, "geography") ) + { + osCommand.Printf( + "%s ( %s %s, %s geography(%s%s%s), PRIMARY KEY (%s)", + osCreateTable.c_str(), + pszFIDColumnName, + pszSerialType, + OGRPGEscapeColumnName(pszGFldName).c_str(), pszGeometryType, + nDimension == 2 ? "" : "Z", + nSRSId ? CPLSPrintf(",%d", nSRSId) : "", + pszFIDColumnName); + } + else if ( !bDifferedCreation && eType != wkbNone && !EQUAL(pszGeomType, "geography") && + sPostGISVersion.nMajor >= 2 ) + { + osCommand.Printf( + "%s ( %s %s, %s geometry(%s%s%s), PRIMARY KEY (%s)", + osCreateTable.c_str(), + pszFIDColumnName, + pszSerialType, + OGRPGEscapeColumnName(pszGFldName).c_str(), pszGeometryType, + nDimension == 2 ? "" : "Z", + nSRSId ? CPLSPrintf(",%d", nSRSId) : "", + pszFIDColumnName); + } + else + { + osCommand.Printf( + "%s ( %s %s, PRIMARY KEY (%s)", + osCreateTable.c_str(), + pszFIDColumnName, + pszSerialType, + pszFIDColumnName ); + } + osCreateTable = osCommand; + + const char *pszSI = CSLFetchNameValue( papszOptions, "SPATIAL_INDEX" ); + int bCreateSpatialIndex = ( pszSI == NULL || CSLTestBoolean(pszSI) ); + if( eType != wkbNone && + pszSI == NULL && + CSLFetchBoolean( papszOptions, "UNLOGGED", FALSE ) && + !(sPostgreSQLVersion.nMajor > 9 || + (sPostgreSQLVersion.nMajor == 9 && sPostgreSQLVersion.nMinor >= 3)) ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "GiST index only supported since Postgres 9.3 on unlogged table"); + bCreateSpatialIndex = FALSE; + } + + CPLString osEscapedTableNameSingleQuote = OGRPGEscapeString(hPGConn, pszTableName); + const char* pszEscapedTableNameSingleQuote = osEscapedTableNameSingleQuote.c_str(); + CPLString osEscapedSchemaNameSingleQuote = OGRPGEscapeString(hPGConn, pszSchemaName); + const char* pszEscapedSchemaNameSingleQuote = osEscapedSchemaNameSingleQuote.c_str(); + + if( eType != wkbNone && bHavePostGIS && sPostGISVersion.nMajor <= 1 ) + { + /* Sometimes there is an old cruft entry in the geometry_columns + * table if things were not properly cleaned up before. We make + * an effort to clean out such cruft. + * Note: PostGIS 2.0 defines geometry_columns as a view (no clean up is needed) + */ + osCommand.Printf( + "DELETE FROM geometry_columns WHERE f_table_name = %s AND f_table_schema = %s", + pszEscapedTableNameSingleQuote, pszEscapedSchemaNameSingleQuote ); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str()); + OGRPGClearResult( hResult ); + } + + if( !bDifferedCreation ) + { + SoftStartTransaction(); + + osCommand = osCreateTable; + osCommand += " )"; + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str()); + if( PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s\n%s", osCommand.c_str(), PQerrorMessage(hPGConn) ); + CPLFree( pszTableName ); + CPLFree( pszSchemaName ); + + OGRPGClearResult( hResult ); + + SoftRollbackTransaction(); + return NULL; + } + + OGRPGClearResult( hResult ); + + /* -------------------------------------------------------------------- */ + /* Eventually we should be adding this table to a table of */ + /* "geometric layers", capturing the WKT projection, and */ + /* perhaps some other housekeeping. */ + /* -------------------------------------------------------------------- */ + if( eType != wkbNone && bHavePostGIS && !EQUAL(pszGeomType, "geography") && + sPostGISVersion.nMajor <= 1 ) + { + osCommand.Printf( + "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s',%d)", + pszEscapedSchemaNameSingleQuote, pszEscapedTableNameSingleQuote, + OGRPGEscapeString(hPGConn, pszGFldName).c_str(), + nSRSId, pszGeometryType, nDimension ); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str()); + + if( !hResult + || PQresultStatus(hResult) != PGRES_TUPLES_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "AddGeometryColumn failed for layer %s, layer creation has failed.", + pszLayerName ); + + CPLFree( pszTableName ); + CPLFree( pszSchemaName ); + + OGRPGClearResult( hResult ); + + SoftRollbackTransaction(); + + return NULL; + } + + OGRPGClearResult( hResult ); + } + + if( eType != wkbNone && bHavePostGIS && bCreateSpatialIndex ) + { + /* -------------------------------------------------------------------- */ + /* Create the spatial index. */ + /* */ + /* We're doing this before we add geometry and record to the table */ + /* so this may not be exactly the best way to do it. */ + /* -------------------------------------------------------------------- */ + + osCommand.Printf("CREATE INDEX %s ON %s.%s USING GIST (%s)", + OGRPGEscapeColumnName( + CPLSPrintf("%s_%s_geom_idx", pszTableName, pszGFldName)).c_str(), + OGRPGEscapeColumnName(pszSchemaName).c_str(), + OGRPGEscapeColumnName(pszTableName).c_str(), + OGRPGEscapeColumnName(pszGFldName).c_str()); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str()); + + if( !hResult + || PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "'%s' failed for layer %s, index creation has failed.", + osCommand.c_str(), pszLayerName ); + + CPLFree( pszTableName ); + CPLFree( pszSchemaName ); + + OGRPGClearResult( hResult ); + + SoftRollbackTransaction(); + + return NULL; + } + OGRPGClearResult( hResult ); + } + + /* -------------------------------------------------------------------- */ + /* Complete, and commit the transaction. */ + /* -------------------------------------------------------------------- */ + SoftCommitTransaction(); + } + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRPGTableLayer *poLayer; + + poLayer = new OGRPGTableLayer( this, osCurrentSchema, pszTableName, + pszSchemaName, NULL, TRUE ); + poLayer->SetTableDefinition(pszFIDColumnName, pszGFldName, eType, + pszGeomType, nSRSId, nDimension); + poLayer->SetLaunderFlag( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) ); + poLayer->SetPrecisionFlag( CSLFetchBoolean(papszOptions,"PRECISION",TRUE)); + //poLayer->SetForcedSRSId(nForcedSRSId); + poLayer->SetForcedDimension(nForcedDimension); + poLayer->SetCreateSpatialIndexFlag(bCreateSpatialIndex); + poLayer->SetDifferedCreation(bDifferedCreation, osCreateTable); + + + /* HSTORE_COLUMNS existed at a time during GDAL 1.10dev */ + const char* pszHSTOREColumns = CSLFetchNameValue( papszOptions, "HSTORE_COLUMNS" ); + if( pszHSTOREColumns != NULL ) + CPLError(CE_Warning, CPLE_AppDefined, "HSTORE_COLUMNS not recognized. Use COLUMN_TYPES instead."); + + const char* pszOverrideColumnTypes = CSLFetchNameValue( papszOptions, "COLUMN_TYPES" ); + poLayer->SetOverrideColumnTypes(pszOverrideColumnTypes); + + poLayer->AllowAutoFIDOnCreateViaCopy(); + if( CSLTestBoolean(CPLGetConfigOption("PG_USE_COPY", "YES")) ) + poLayer->SetUseCopy(); + + if( bFID64 ) + poLayer->SetMetadataItem(OLMD_FID64, "YES"); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRPGTableLayer **) + CPLRealloc( papoLayers, sizeof(OGRPGTableLayer *) * (nLayers+1) ); + + papoLayers[nLayers++] = poLayer; + + CPLFree( pszTableName ); + CPLFree( pszSchemaName ); + + return poLayer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPGDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) + || EQUAL(pszCap,ODsCDeleteLayer) + || EQUAL(pszCap,ODsCCreateGeomFieldAfterCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCCurveGeometries) ) + return TRUE; + else if( EQUAL(pszCap,ODsCTransactions) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int OGRPGDataSource::GetLayerCount() +{ + LoadTables(); + return nLayers; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRPGDataSource::GetLayer( int iLayer ) + +{ + /* Force loading of all registered tables */ + if( iLayer < 0 || iLayer >= GetLayerCount() ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer *OGRPGDataSource::GetLayerByName( const char *pszName ) + +{ + char* pszTableName = NULL; + char *pszGeomColumnName = NULL; + char *pszSchemaName = NULL; + + if ( ! pszName ) + return NULL; + + int i; + + /* first a case sensitive check */ + /* do NOT force loading of all registered tables */ + for( i = 0; i < nLayers; i++ ) + { + OGRPGTableLayer *poLayer = papoLayers[i]; + + if( strcmp( pszName, poLayer->GetName() ) == 0 ) + { + return poLayer; + } + } + + /* then case insensitive */ + for( i = 0; i < nLayers; i++ ) + { + OGRPGTableLayer *poLayer = papoLayers[i]; + + if( EQUAL( pszName, poLayer->GetName() ) ) + { + return poLayer; + } + } + + char* pszNameWithoutBracket = CPLStrdup(pszName); + char *pos = strchr(pszNameWithoutBracket, '('); + if (pos != NULL) + { + *pos = '\0'; + pszGeomColumnName = CPLStrdup(pos+1); + int len = strlen(pszGeomColumnName); + if (len > 0) + pszGeomColumnName[len - 1] = '\0'; + } + + pos = strchr(pszNameWithoutBracket, '.'); + if (pos != NULL) + { + *pos = '\0'; + pszSchemaName = CPLStrdup(pszNameWithoutBracket); + pszTableName = CPLStrdup(pos + 1); + } + else + { + pszTableName = CPLStrdup(pszNameWithoutBracket); + } + CPLFree(pszNameWithoutBracket); + pszNameWithoutBracket = NULL; + + OGRPGTableLayer* poLayer = NULL; + + if (pszSchemaName != NULL && osCurrentSchema == pszSchemaName && + pszGeomColumnName == NULL ) + { + poLayer = (OGRPGTableLayer*) GetLayerByName(pszTableName); + } + else + { + EndCopy(); + + CPLString osTableName(pszTableName); + CPLString osTableNameLower(pszTableName); + osTableNameLower.tolower(); + if( osTableName != osTableNameLower ) + CPLPushErrorHandler(CPLQuietErrorHandler); + poLayer = OpenTable( osCurrentSchema, pszTableName, + pszSchemaName, + pszGeomColumnName, + bDSUpdate, TRUE ); + if( osTableName != osTableNameLower ) + CPLPopErrorHandler(); + if( poLayer == NULL && osTableName != osTableNameLower ) + { + poLayer = OpenTable( osCurrentSchema, osTableNameLower, + pszSchemaName, + pszGeomColumnName, + bDSUpdate, TRUE ); + } + } + + CPLFree(pszTableName); + CPLFree(pszSchemaName); + CPLFree(pszGeomColumnName); + + return poLayer; +} + + +/************************************************************************/ +/* OGRPGNoticeProcessor() */ +/************************************************************************/ + +static void OGRPGNoticeProcessor( CPL_UNUSED void *arg, const char * pszMessage ) +{ + CPLDebug( "OGR_PG_NOTICE", "%s", pszMessage ); +} + +/************************************************************************/ +/* InitializeMetadataTables() */ +/* */ +/* Create the metadata tables (SPATIAL_REF_SYS and */ +/* GEOMETRY_COLUMNS). */ +/************************************************************************/ + +OGRErr OGRPGDataSource::InitializeMetadataTables() + +{ + // implement later. + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* FetchSRS() */ +/* */ +/* Return a SRS corresponding to a particular id. Note that */ +/* reference counting should be honoured on the returned */ +/* OGRSpatialReference, as handles may be cached. */ +/************************************************************************/ + +OGRSpatialReference *OGRPGDataSource::FetchSRS( int nId ) + +{ + if( nId < 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* First, we look through our SRID cache, is it there? */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nKnownSRID; i++ ) + { + if( panSRID[i] == nId ) + return papoSRS[i]; + } + + EndCopy(); + +/* -------------------------------------------------------------------- */ +/* Try looking up in spatial_ref_sys table. */ +/* -------------------------------------------------------------------- */ + PGresult *hResult = NULL; + CPLString osCommand; + OGRSpatialReference *poSRS = NULL; + + osCommand.Printf( + "SELECT srtext FROM spatial_ref_sys " + "WHERE srid = %d", + nId ); + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() ); + + if( hResult + && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) == 1 ) + { + char *pszWKT; + + pszWKT = PQgetvalue(hResult,0,0); + poSRS = new OGRSpatialReference(); + if( poSRS->importFromWkt( &pszWKT ) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Could not fetch SRS: %s", PQerrorMessage( hPGConn ) ); + } + + OGRPGClearResult( hResult ); + +/* -------------------------------------------------------------------- */ +/* Add to the cache. */ +/* -------------------------------------------------------------------- */ + panSRID = (int *) CPLRealloc(panSRID,sizeof(int) * (nKnownSRID+1) ); + papoSRS = (OGRSpatialReference **) + CPLRealloc(papoSRS, sizeof(void*) * (nKnownSRID + 1) ); + panSRID[nKnownSRID] = nId; + papoSRS[nKnownSRID] = poSRS; + nKnownSRID++; + + return poSRS; +} + +/************************************************************************/ +/* FetchSRSId() */ +/* */ +/* Fetch the id corresponding to an SRS, and if not found, add */ +/* it to the table. */ +/************************************************************************/ + +int OGRPGDataSource::FetchSRSId( OGRSpatialReference * poSRS ) + +{ + PGresult *hResult = NULL; + CPLString osCommand; + char *pszWKT = NULL; + int nSRSId = nUndefinedSRID; + const char* pszAuthorityName; + + if( poSRS == NULL ) + return nUndefinedSRID; + + OGRSpatialReference oSRS(*poSRS); + poSRS = NULL; + + pszAuthorityName = oSRS.GetAuthorityName(NULL); + + if( pszAuthorityName == NULL || strlen(pszAuthorityName) == 0 ) + { +/* -------------------------------------------------------------------- */ +/* Try to identify an EPSG code */ +/* -------------------------------------------------------------------- */ + oSRS.AutoIdentifyEPSG(); + + pszAuthorityName = oSRS.GetAuthorityName(NULL); + if (pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG")) + { + const char* pszAuthorityCode = oSRS.GetAuthorityCode(NULL); + if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 ) + { + /* Import 'clean' SRS */ + oSRS.importFromEPSG( atoi(pszAuthorityCode) ); + + pszAuthorityName = oSRS.GetAuthorityName(NULL); + } + } + } +/* -------------------------------------------------------------------- */ +/* Check whether the authority name/code is already mapped to a */ +/* SRS ID. */ +/* -------------------------------------------------------------------- */ + int nAuthorityCode = 0; + if( pszAuthorityName != NULL ) + { + /* Check that the authority code is integral */ + nAuthorityCode = atoi( oSRS.GetAuthorityCode(NULL) ); + if( nAuthorityCode > 0 ) + { + osCommand.Printf("SELECT srid FROM spatial_ref_sys WHERE " + "auth_name = '%s' AND auth_srid = %d", + pszAuthorityName, + nAuthorityCode ); + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str()); + + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) > 0 ) + { + nSRSId = atoi(PQgetvalue( hResult, 0, 0 )); + + OGRPGClearResult( hResult ); + + return nSRSId; + } + + OGRPGClearResult( hResult ); + } + } + +/* -------------------------------------------------------------------- */ +/* Translate SRS to WKT. */ +/* -------------------------------------------------------------------- */ + if( oSRS.exportToWkt( &pszWKT ) != OGRERR_NONE ) + { + CPLFree(pszWKT); + return nUndefinedSRID; + } + +/* -------------------------------------------------------------------- */ +/* Try to find in the existing table. */ +/* -------------------------------------------------------------------- */ + CPLString osWKT = OGRPGEscapeString(hPGConn, pszWKT, -1, "spatial_ref_sys", "srtext"); + osCommand.Printf( + "SELECT srid FROM spatial_ref_sys WHERE srtext = %s", + osWKT.c_str() ); + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() ); + CPLFree( pszWKT ); // CM: Added to prevent mem leaks + pszWKT = NULL; // CM: Added + +/* -------------------------------------------------------------------- */ +/* We got it! Return it. */ +/* -------------------------------------------------------------------- */ + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) > 0 ) + { + nSRSId = atoi(PQgetvalue( hResult, 0, 0 )); + + OGRPGClearResult( hResult ); + + return nSRSId; + } + +/* -------------------------------------------------------------------- */ +/* If the command actually failed, then the metadata table is */ +/* likely missing. Try defining it. */ +/* -------------------------------------------------------------------- */ + int bTableMissing; + + bTableMissing = + hResult == NULL || PQresultStatus(hResult) == PGRES_NONFATAL_ERROR; + + OGRPGClearResult( hResult ); + + if( bTableMissing ) + { + if( InitializeMetadataTables() != OGRERR_NONE ) + return nUndefinedSRID; + } + +/* -------------------------------------------------------------------- */ +/* Get the current maximum srid in the srs table. */ +/* -------------------------------------------------------------------- */ + hResult = OGRPG_PQexec(hPGConn, "SELECT MAX(srid) FROM spatial_ref_sys" ); + + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK ) + { + nSRSId = atoi(PQgetvalue(hResult,0,0)) + 1; + OGRPGClearResult( hResult ); + } + else + { + nSRSId = 1; + } + +/* -------------------------------------------------------------------- */ +/* Try adding the SRS to the SRS table. */ +/* -------------------------------------------------------------------- */ + char *pszProj4 = NULL; + if( oSRS.exportToProj4( &pszProj4 ) != OGRERR_NONE ) + { + CPLFree( pszProj4 ); + return nUndefinedSRID; + } + + CPLString osProj4 = OGRPGEscapeString(hPGConn, pszProj4, -1, "spatial_ref_sys", "proj4text"); + + if( pszAuthorityName != NULL && nAuthorityCode > 0) + { + nAuthorityCode = atoi( oSRS.GetAuthorityCode(NULL) ); + + osCommand.Printf( + "INSERT INTO spatial_ref_sys (srid,srtext,proj4text,auth_name,auth_srid) " + "VALUES (%d, %s, %s, '%s', %d)", + nSRSId, osWKT.c_str(), osProj4.c_str(), + pszAuthorityName, nAuthorityCode ); + } + else + { + osCommand.Printf( + "INSERT INTO spatial_ref_sys (srid,srtext,proj4text) VALUES (%d,%s,%s)", + nSRSId, osWKT.c_str(), osProj4.c_str() ); + } + + // Free everything that was allocated. + CPLFree( pszProj4 ); + CPLFree( pszWKT); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() ); + OGRPGClearResult( hResult ); + + return nSRSId; +} + +/************************************************************************/ +/* StartTransaction() */ +/* */ +/* Should only be called by user code. Not driver internals. */ +/************************************************************************/ + +OGRErr OGRPGDataSource::StartTransaction(CPL_UNUSED int bForce) +{ + if( bUserTransactionActive ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Transaction already established"); + return OGRERR_FAILURE; + } + + CPLAssert(!bSavePointActive); + EndCopy(); + + if( nSoftTransactionLevel == 0 ) + { + OGRErr eErr = DoTransactionCommand("BEGIN"); + if( eErr != OGRERR_NONE ) + return eErr; + } + else + { + OGRErr eErr = DoTransactionCommand("SAVEPOINT ogr_savepoint"); + if( eErr != OGRERR_NONE ) + return eErr; + + bSavePointActive = TRUE; + } + + nSoftTransactionLevel++; + bUserTransactionActive = TRUE; + + /*CPLDebug("PG", "poDS=%p StartTransaction() nSoftTransactionLevel=%d", + this, nSoftTransactionLevel);*/ + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CommitTransaction() */ +/* */ +/* Should only be called by user code. Not driver internals. */ +/************************************************************************/ + +OGRErr OGRPGDataSource::CommitTransaction() +{ + if( !bUserTransactionActive ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established"); + return OGRERR_FAILURE; + } + + /*CPLDebug("PG", "poDS=%p CommitTransaction() nSoftTransactionLevel=%d", + this, nSoftTransactionLevel);*/ + + FlushCache(); + + nSoftTransactionLevel--; + bUserTransactionActive = FALSE; + + OGRErr eErr; + if( bSavePointActive ) + { + CPLAssert(nSoftTransactionLevel > 0); + bSavePointActive = FALSE; + + eErr = DoTransactionCommand("RELEASE SAVEPOINT ogr_savepoint"); + } + else + { + if( nSoftTransactionLevel > 0 ) + { + // This means we have cursors still in progress + for(int i=0;iInvalidateCursor(); + CPLAssert( nSoftTransactionLevel == 0 ); + } + + eErr = DoTransactionCommand("COMMIT"); + } + + return eErr; +} + +/************************************************************************/ +/* RollbackTransaction() */ +/* */ +/* Should only be called by user code. Not driver internals. */ +/************************************************************************/ + +OGRErr OGRPGDataSource::RollbackTransaction() +{ + if( !bUserTransactionActive ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established"); + return OGRERR_FAILURE; + } + + /*CPLDebug("PG", "poDS=%p RollbackTransaction() nSoftTransactionLevel=%d", + this, nSoftTransactionLevel);*/ + + FlushCache(); + + nSoftTransactionLevel--; + bUserTransactionActive = FALSE; + + OGRErr eErr; + if( bSavePointActive ) + { + CPLAssert(nSoftTransactionLevel > 0); + bSavePointActive = FALSE; + + eErr = DoTransactionCommand("ROLLBACK TO SAVEPOINT ogr_savepoint"); + } + else + { + if( nSoftTransactionLevel > 0 ) + { + // This means we have cursors still in progress + for(int i=0;iInvalidateCursor(); + CPLAssert( nSoftTransactionLevel == 0 ); + } + + eErr = DoTransactionCommand("ROLLBACK"); + } + + return eErr; +} + +/************************************************************************/ +/* SoftStartTransaction() */ +/* */ +/* Create a transaction scope. If we already have a */ +/* transaction active this isn't a real transaction, but just */ +/* an increment to the scope count. */ +/************************************************************************/ + +OGRErr OGRPGDataSource::SoftStartTransaction() + +{ + nSoftTransactionLevel++; + /*CPLDebug("PG", "poDS=%p SoftStartTransaction() nSoftTransactionLevel=%d", + this, nSoftTransactionLevel);*/ + + OGRErr eErr = OGRERR_NONE; + if( nSoftTransactionLevel == 1 ) + { + eErr = DoTransactionCommand("BEGIN"); + } + + return eErr; +} + +/************************************************************************/ +/* SoftCommitTransaction() */ +/* */ +/* Commit the current transaction if we are at the outer */ +/* scope. */ +/************************************************************************/ + +OGRErr OGRPGDataSource::SoftCommitTransaction() + +{ + EndCopy(); + + /*CPLDebug("PG", "poDS=%p SoftCommitTransaction() nSoftTransactionLevel=%d", + this, nSoftTransactionLevel);*/ + + if( nSoftTransactionLevel <= 0 ) + { + CPLAssert(FALSE); + return OGRERR_FAILURE; + } + + OGRErr eErr = OGRERR_NONE; + nSoftTransactionLevel--; + if( nSoftTransactionLevel == 0 ) + { + CPLAssert( !bSavePointActive ); + + eErr = DoTransactionCommand("COMMIT"); + } + + return eErr; +} + +/************************************************************************/ +/* SoftRollbackTransaction() */ +/* */ +/* Do a rollback of the current transaction if we are at the 1st */ +/* level */ +/************************************************************************/ + +OGRErr OGRPGDataSource::SoftRollbackTransaction() + +{ + EndCopy(); + + /*CPLDebug("PG", "poDS=%p SoftRollbackTransaction() nSoftTransactionLevel=%d", + this, nSoftTransactionLevel);*/ + + if( nSoftTransactionLevel <= 0 ) + { + CPLAssert(FALSE); + return OGRERR_FAILURE; + } + + OGRErr eErr = OGRERR_NONE; + nSoftTransactionLevel--; + if( nSoftTransactionLevel == 0 ) + { + CPLAssert( !bSavePointActive ); + + eErr = DoTransactionCommand("ROLLBACK"); + } + + return eErr; +} + +/************************************************************************/ +/* FlushSoftTransaction() */ +/* */ +/* Force the unwinding of any active transaction, and its */ +/* commit. Should only be used by datasource destructor */ +/************************************************************************/ + +OGRErr OGRPGDataSource::FlushSoftTransaction() + +{ + FlushCache(); + + /*CPLDebug("PG", "poDS=%p FlushSoftTransaction() nSoftTransactionLevel=%d", + this, nSoftTransactionLevel);*/ + + if( nSoftTransactionLevel <= 0 ) + return OGRERR_NONE; + + for(int i=0;iInvalidateCursor(); + bSavePointActive = FALSE; + + OGRErr eErr = OGRERR_NONE; + if( nSoftTransactionLevel > 0 ) + { + CPLAssert(nSoftTransactionLevel == 1 ); + nSoftTransactionLevel = 0; + eErr = DoTransactionCommand("COMMIT"); + } + return eErr; +} + +/************************************************************************/ +/* DoTransactionCommand() */ +/************************************************************************/ + +OGRErr OGRPGDataSource::DoTransactionCommand(const char* pszCommand) + +{ + OGRErr eErr = OGRERR_NONE; + PGresult *hResult = NULL; + PGconn *hPGConn = GetPGConn(); + + hResult = OGRPG_PQexec(hPGConn, pszCommand); + osDebugLastTransactionCommand = pszCommand; + + if( !hResult || PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + eErr = OGRERR_FAILURE; + } + + OGRPGClearResult( hResult ); + + return eErr; +} + +/************************************************************************/ +/* OGRPGNoResetResultLayer */ +/************************************************************************/ + +class OGRPGNoResetResultLayer : public OGRPGLayer +{ + public: + OGRPGNoResetResultLayer(OGRPGDataSource *poDSIn, + PGresult *hResultIn); + + virtual ~OGRPGNoResetResultLayer(); + + virtual void ResetReading(); + + virtual int TestCapability( const char * ) { return FALSE; } + + virtual OGRFeature *GetNextFeature(); + + virtual CPLString GetFromClauseForGetExtent() { CPLAssert(FALSE); return ""; } + virtual void ResolveSRID(OGRPGGeomFieldDefn* poGFldDefn) { poGFldDefn->nSRSId = -1; } +}; + + +/************************************************************************/ +/* OGRPGNoResetResultLayer() */ +/************************************************************************/ + +OGRPGNoResetResultLayer::OGRPGNoResetResultLayer( OGRPGDataSource *poDSIn, + PGresult *hResultIn ) +{ + poDS = poDSIn; + ReadResultDefinition(hResultIn); + hCursorResult = hResultIn; + CreateMapFromFieldNameToIndex(hCursorResult, + poFeatureDefn, + m_panMapFieldNameToIndex, + m_panMapFieldNameToGeomIndex); +} + +/************************************************************************/ +/* ~OGRPGNoResetResultLayer() */ +/************************************************************************/ + +OGRPGNoResetResultLayer::~OGRPGNoResetResultLayer() + +{ + OGRPGClearResult( hCursorResult ); + hCursorResult = NULL; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRPGNoResetResultLayer::ResetReading() +{ + iNextShapeId = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRPGNoResetResultLayer::GetNextFeature() + +{ + if (iNextShapeId == PQntuples(hCursorResult)) + { + return NULL; + } + return RecordToFeature(hCursorResult, + m_panMapFieldNameToIndex, + m_panMapFieldNameToGeomIndex, + iNextShapeId ++); +} + +/************************************************************************/ +/* OGRPGMemLayerWrapper */ +/************************************************************************/ + +class OGRPGMemLayerWrapper : public OGRLayer +{ + private: + GDALDataset *poMemDS; + OGRLayer *poMemLayer; + + public: + OGRPGMemLayerWrapper( GDALDataset *poMemDSIn ) + { + poMemDS = poMemDSIn; + poMemLayer = poMemDS->GetLayer(0); + } + + ~OGRPGMemLayerWrapper() { delete poMemDS; } + + virtual void ResetReading() { poMemLayer->ResetReading(); } + virtual OGRFeature *GetNextFeature() { return poMemLayer->GetNextFeature(); } + virtual OGRFeatureDefn *GetLayerDefn() { return poMemLayer->GetLayerDefn(); } + virtual int TestCapability( const char * ) { return FALSE; } +}; + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char* OGRPGDataSource::GetMetadataItem(const char* pszKey, + const char* pszDomain) +{ + /* Only used by ogr_pg.py to check inner working */ + if( pszDomain != NULL && EQUAL(pszDomain, "_debug_") && + pszKey != NULL ) + { + if( EQUAL(pszKey, "bHasLoadTables") ) + return CPLSPrintf("%d", bHasLoadTables); + if( EQUAL(pszKey, "nSoftTransactionLevel") ) + return CPLSPrintf("%d", nSoftTransactionLevel); + if( EQUAL(pszKey, "bSavePointActive") ) + return CPLSPrintf("%d", bSavePointActive); + if( EQUAL(pszKey, "bUserTransactionActive") ) + return CPLSPrintf("%d", bUserTransactionActive); + if( EQUAL(pszKey, "osDebugLastTransactionCommand") ) + { + const char* pszRet = CPLSPrintf("%s", osDebugLastTransactionCommand.c_str()); + osDebugLastTransactionCommand = ""; + return pszRet; + } + + } + return OGRDataSource::GetMetadataItem(pszKey, pszDomain); +} + + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRPGDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ + /* Skip leading spaces */ + while(*pszSQLCommand == ' ') + pszSQLCommand ++; + + FlushCache(); + +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Special case DELLAYER: command. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand,"DELLAYER:",9) ) + { + const char *pszLayerName = pszSQLCommand + 9; + + while( *pszLayerName == ' ' ) + pszLayerName++; + + GetLayerCount(); + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(papoLayers[iLayer]->GetName(), + pszLayerName )) + { + DeleteLayer( iLayer ); + break; + } + } + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Execute the statement. */ +/* -------------------------------------------------------------------- */ + PGresult *hResult = NULL; + + if (EQUALN(pszSQLCommand, "SELECT", 6) == FALSE || + (strstr(pszSQLCommand, "from") == NULL && strstr(pszSQLCommand, "FROM") == NULL)) + { + /* For something that is not a select or a select without table, do not */ + /* run under transaction (CREATE DATABASE, VACCUUM don't like transactions) */ + + hResult = OGRPG_PQexec(hPGConn, pszSQLCommand, TRUE /* multiple allowed */ ); + if (hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK) + { + CPLDebug( "PG", "Command Results Tuples = %d", PQntuples(hResult) ); + + GDALDriver* poMemDriver = OGRSFDriverRegistrar::GetRegistrar()-> + GetDriverByName("Memory"); + if (poMemDriver) + { + OGRPGLayer* poResultLayer = new OGRPGNoResetResultLayer( this, hResult ); + GDALDataset* poMemDS = poMemDriver->Create("", 0, 0, 0, GDT_Unknown, NULL); + poMemDS->CopyLayer(poResultLayer, "sql_statement"); + OGRPGMemLayerWrapper* poResLayer = new OGRPGMemLayerWrapper(poMemDS); + delete poResultLayer; + return poResLayer; + } + else + return NULL; + } + } + else + { + SoftStartTransaction(); + + CPLString osCommand; + osCommand.Printf( "DECLARE %s CURSOR for %s", + "executeSQLCursor", pszSQLCommand ); + + hResult = OGRPG_PQexec(hPGConn, osCommand ); + +/* -------------------------------------------------------------------- */ +/* Do we have a tuple result? If so, instantiate a results */ +/* layer for it. */ +/* -------------------------------------------------------------------- */ + if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK ) + { + OGRPGResultLayer *poLayer = NULL; + + OGRPGClearResult( hResult ); + + osCommand.Printf( "FETCH 0 in %s", "executeSQLCursor" ); + hResult = OGRPG_PQexec(hPGConn, osCommand ); + + poLayer = new OGRPGResultLayer( this, pszSQLCommand, hResult ); + + OGRPGClearResult( hResult ); + + osCommand.Printf( "CLOSE %s", "executeSQLCursor" ); + hResult = OGRPG_PQexec(hPGConn, osCommand ); + + SoftCommitTransaction(); + + if( poSpatialFilter != NULL ) + poLayer->SetSpatialFilter( poSpatialFilter ); + + return poLayer; + } + else + { + SoftRollbackTransaction(); + } + } + +/* -------------------------------------------------------------------- */ +/* Generate an error report if an error occured. */ +/* -------------------------------------------------------------------- */ + if( !hResult || + (PQresultStatus(hResult) == PGRES_NONFATAL_ERROR + || PQresultStatus(hResult) == PGRES_FATAL_ERROR ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage( hPGConn ) ); + } + + OGRPGClearResult( hResult ); + + return NULL; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRPGDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} + +/************************************************************************/ +/* StartCopy() */ +/************************************************************************/ + +void OGRPGDataSource::StartCopy( OGRPGTableLayer *poPGLayer ) +{ + if( poLayerInCopyMode == poPGLayer ) + return; + EndCopy(); + poLayerInCopyMode = poPGLayer; + poLayerInCopyMode->StartCopy(); +} + +/************************************************************************/ +/* EndCopy() */ +/************************************************************************/ + +OGRErr OGRPGDataSource::EndCopy( ) +{ + if( poLayerInCopyMode != NULL ) + { + OGRErr result = poLayerInCopyMode->EndCopy(); + poLayerInCopyMode = NULL; + + return result; + } + else + return OGRERR_NONE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgdriver.cpp new file mode 100644 index 000000000..7d04ee8bf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgdriver.cpp @@ -0,0 +1,176 @@ +/****************************************************************************** + * $Id: ogrpgdriver.cpp 29019 2015-04-25 20:34:19Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_pg.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrpgdriver.cpp 29019 2015-04-25 20:34:19Z rouault $"); + + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +static int OGRPGDriverIdentify( GDALOpenInfo* poOpenInfo ) +{ + if( !EQUALN(poOpenInfo->pszFilename,"PGB:",4) && + !EQUALN(poOpenInfo->pszFilename,"PG:",3) ) + return FALSE; + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRPGDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + OGRPGDataSource *poDS; + + if( !OGRPGDriverIdentify(poOpenInfo) ) + return NULL; + + poDS = new OGRPGDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, + poOpenInfo->eAccess == GA_Update, TRUE, + poOpenInfo->papszOpenOptions ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +static GDALDataset *OGRPGDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + CPL_UNUSED char **papszOptions ) + +{ + OGRPGDataSource *poDS; + + poDS = new OGRPGDataSource(); + + if( !poDS->Open( pszName, TRUE, TRUE, NULL ) ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "PostgreSQL driver doesn't currently support database creation.\n" + "Please create database with the `createdb' command." ); + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRPG() */ +/************************************************************************/ + +void RegisterOGRPG() + +{ + if (! GDAL_CHECK_VERSION("PG driver")) + return; + + if( GDALGetDriverByName( "PostgreSQL" ) == NULL ) + { + GDALDriver* poDriver = new GDALDriver(); + + poDriver->SetDescription( "PostgreSQL" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "PostgreSQL/PostGIS" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_pg.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CONNECTION_PREFIX, "PG:" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, ""); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" " +" "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime Time IntegerList Integer64List RealList StringList Binary" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_DEFAULT_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES" ); + + poDriver->pfnOpen = OGRPGDriverOpen; + poDriver->pfnIdentify = OGRPGDriverIdentify; + poDriver->pfnCreate = OGRPGDriverCreate; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpglayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpglayer.cpp new file mode 100644 index 000000000..22d1f6761 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpglayer.cpp @@ -0,0 +1,2259 @@ +/****************************************************************************** + * $Id: ogrpglayer.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGLayer class which implements shared handling + * of feature geometry and so forth needed by OGRPGResultLayer and + * OGRPGTableLayer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* Some functions have been extracted from PostgreSQL code base */ +/* The applicable copyright & licence notice is the following one : */ +/* +PostgreSQL Database Management System +(formerly known as Postgres, then as Postgres95) + +Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group + +Portions Copyright (c) 1994, The Regents of the University of California + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose, without fee, and without a written agreement +is hereby granted, provided that the above copyright notice and this +paragraph and the following two paragraphs appear in all copies. + +IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING +LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS +DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +*/ + +#include "ogr_pg.h" +#include "ogr_p.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +#define PQexec this_is_an_error + +CPL_CVSID("$Id: ogrpglayer.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +// These originally are defined in libpq-fs.h. + +#ifndef INV_WRITE +#define INV_WRITE 0x00020000 +#define INV_READ 0x00040000 +#endif + +/************************************************************************/ +/* OGRPGLayer() */ +/************************************************************************/ + +OGRPGLayer::OGRPGLayer() + +{ + poDS = NULL; + + bWkbAsOid = FALSE; + pszQueryStatement = NULL; + + pszFIDColumn = NULL; + + nCursorPage = atoi(CPLGetConfigOption("OGR_PG_CURSOR_PAGE", "500")); + iNextShapeId = 0; + nResultOffset = 0; + + pszCursorName = CPLStrdup(CPLSPrintf("OGRPGLayerReader%p", this)); + + hCursorResult = NULL; + bInvalidated = FALSE; + + bCanUseBinaryCursor = TRUE; + + poFeatureDefn = NULL; + m_panMapFieldNameToIndex = NULL; + m_panMapFieldNameToGeomIndex = NULL; +} + +/************************************************************************/ +/* ~OGRPGLayer() */ +/************************************************************************/ + +OGRPGLayer::~OGRPGLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "PG", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + ResetReading(); + + CPLFree( pszFIDColumn ); + CPLFree( pszQueryStatement ); + CPLFree( m_panMapFieldNameToIndex ); + CPLFree( m_panMapFieldNameToGeomIndex ); + CPLFree( pszCursorName ); + + if( poFeatureDefn ) + { + poFeatureDefn->UnsetLayer(); + poFeatureDefn->Release(); + } +} + +/************************************************************************/ +/* CloseCursor() */ +/************************************************************************/ + +void OGRPGLayer::CloseCursor() +{ + PGconn *hPGConn = poDS->GetPGConn(); + + if( hCursorResult != NULL ) + { + OGRPGClearResult( hCursorResult ); + + CPLString osCommand; + osCommand.Printf("CLOSE %s", pszCursorName ); + + /* In case of interleaving read in different layers we might have */ + /* close the transaction, and thus implicitely the cursor, so be */ + /* quiet about errors. This is potentially an issue by the way */ + hCursorResult = OGRPG_PQexec(hPGConn, osCommand.c_str(), FALSE, TRUE); + OGRPGClearResult( hCursorResult ); + + poDS->SoftCommitTransaction(); + + hCursorResult = NULL; + } +} + +/************************************************************************/ +/* InvalidateCursor() */ +/************************************************************************/ + +void OGRPGLayer::InvalidateCursor() +{ + CloseCursor(); + bInvalidated = TRUE; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRPGLayer::ResetReading() + +{ + GetLayerDefn(); + + iNextShapeId = 0; + + CloseCursor(); + bInvalidated = FALSE; +} + +/************************************************************************/ +/* OGRPGGetStrFromBinaryNumeric() */ +/************************************************************************/ + +/* Adaptation of get_str_from_var() from pgsql/src/backend/utils/adt/numeric.c */ + +typedef short NumericDigit; + +typedef struct NumericVar +{ + int ndigits; /* # of digits in digits[] - can be 0! */ + int weight; /* weight of first digit */ + int sign; /* NUMERIC_POS, NUMERIC_NEG, or NUMERIC_NAN */ + int dscale; /* display scale */ + NumericDigit *digits; /* base-NBASE digits */ +} NumericVar; + +#define NUMERIC_POS 0x0000 +#define NUMERIC_NEG 0x4000 +#define NUMERIC_NAN 0xC000 + +#define DEC_DIGITS 4 +/* +* get_str_from_var() - +* +* Convert a var to text representation (guts of numeric_out). +* CAUTION: var's contents may be modified by rounding! +* Returns a palloc'd string. +*/ +static char * +OGRPGGetStrFromBinaryNumeric(NumericVar *var) +{ + char *str; + char *cp; + char *endcp; + int i; + int d; + NumericDigit dig; + NumericDigit d1; + + int dscale = var->dscale; + + /* + * Allocate space for the result. + * + * i is set to to # of decimal digits before decimal point. dscale is the + * # of decimal digits we will print after decimal point. We may generate + * as many as DEC_DIGITS-1 excess digits at the end, and in addition we + * need room for sign, decimal point, null terminator. + */ + i = (var->weight + 1) * DEC_DIGITS; + if (i <= 0) + i = 1; + + str = (char*)CPLMalloc(i + dscale + DEC_DIGITS + 2); + cp = str; + + /* + * Output a dash for negative values + */ + if (var->sign == NUMERIC_NEG) + *cp++ = '-'; + + /* + * Output all digits before the decimal point + */ + if (var->weight < 0) + { + d = var->weight + 1; + *cp++ = '0'; + } + else + { + for (d = 0; d <= var->weight; d++) + { + dig = (d < var->ndigits) ? var->digits[d] : 0; + CPL_MSBPTR16(&dig); + /* In the first digit, suppress extra leading decimal zeroes */ + { + bool putit = (d > 0); + + d1 = dig / 1000; + dig -= d1 * 1000; + putit |= (d1 > 0); + if (putit) + *cp++ = (char)(d1 + '0'); + d1 = dig / 100; + dig -= d1 * 100; + putit |= (d1 > 0); + if (putit) + *cp++ = (char)(d1 + '0'); + d1 = dig / 10; + dig -= d1 * 10; + putit |= (d1 > 0); + if (putit) + *cp++ = (char)(d1 + '0'); + *cp++ = (char)(dig + '0'); + } + } + } + + /* + * If requested, output a decimal point and all the digits that follow it. + * We initially put out a multiple of DEC_DIGITS digits, then truncate if + * needed. + */ + if (dscale > 0) + { + *cp++ = '.'; + endcp = cp + dscale; + for (i = 0; i < dscale; d++, i += DEC_DIGITS) + { + dig = (d >= 0 && d < var->ndigits) ? var->digits[d] : 0; + CPL_MSBPTR16(&dig); + d1 = dig / 1000; + dig -= d1 * 1000; + *cp++ = (char)(d1 + '0'); + d1 = dig / 100; + dig -= d1 * 100; + *cp++ = (char)(d1 + '0'); + d1 = dig / 10; + dig -= d1 * 10; + *cp++ = (char)(d1 + '0'); + *cp++ = (char)(dig + '0'); + } + cp = endcp; + } + + /* + * terminate the string and return it + */ + *cp = '\0'; + return str; +} + +/************************************************************************/ +/* OGRPGj2date() */ +/************************************************************************/ + +/* Coming from j2date() in pgsql/src/backend/utils/adt/datetime.c */ + +#define POSTGRES_EPOCH_JDATE 2451545 /* == date2j(2000, 1, 1) */ + +static +void OGRPGj2date(int jd, int *year, int *month, int *day) +{ + unsigned int julian; + unsigned int quad; + unsigned int extra; + int y; + + julian = jd; + julian += 32044; + quad = julian / 146097; + extra = (julian - quad * 146097) * 4 + 3; + julian += 60 + quad * 3 + extra / 146097; + quad = julian / 1461; + julian -= quad * 1461; + y = julian * 4 / 1461; + julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366)) + + 123; + y += quad * 4; + *year = y - 4800; + quad = julian * 2141 / 65536; + *day = julian - 7834 * quad / 256; + *month = (quad + 10) % 12 + 1; + + return; +} /* j2date() */ + + +/************************************************************************/ +/* OGRPGdt2time() */ +/************************************************************************/ + +#define USECS_PER_SEC 1000000 +#define USECS_PER_MIN ((GIntBig) 60 * USECS_PER_SEC) +#define USECS_PER_HOUR ((GIntBig) 3600 * USECS_PER_SEC) +#define USECS_PER_DAY ((GIntBig) 3600 * 24 * USECS_PER_SEC) + +/* Coming from dt2time() in pgsql/src/backend/utils/adt/timestamp.c */ + +static +void +OGRPGdt2timeInt8(GIntBig jd, int *hour, int *min, int *sec, double *fsec) +{ + GIntBig time; + + time = jd; + + *hour = (int) (time / USECS_PER_HOUR); + time -= (GIntBig) (*hour) * USECS_PER_HOUR; + *min = (int) (time / USECS_PER_MIN); + time -= (GIntBig) (*min) * USECS_PER_MIN; + *sec = (int)time / USECS_PER_SEC; + *fsec = (double)(time - *sec * USECS_PER_SEC); +} /* dt2time() */ + +static +void +OGRPGdt2timeFloat8(double jd, int *hour, int *min, int *sec, double *fsec) +{ + double time; + + time = jd; + + *hour = (int) (time / 3600.); + time -= (*hour) * 3600.; + *min = (int) (time / 60.); + time -= (*min) * 60.; + *sec = (int)time; + *fsec = time - *sec; +} + +/************************************************************************/ +/* OGRPGTimeStamp2DMYHMS() */ +/************************************************************************/ + +#define TMODULO(t,q,u) \ +do { \ + (q) = ((t) / (u)); \ + if ((q) != 0) (t) -= ((q) * (u)); \ +} while(0) + +/* Coming from timestamp2tm() in pgsql/src/backend/utils/adt/timestamp.c */ + +static +int OGRPGTimeStamp2DMYHMS(GIntBig dt, int *year, int *month, int *day, + int* hour, int* min, double* pdfSec) +{ + GIntBig date; + GIntBig time; + int nSec; + double dfSec; + + time = dt; + TMODULO(time, date, USECS_PER_DAY); + + if (time < 0) + { + time += USECS_PER_DAY; + date -= 1; + } + + /* add offset to go from J2000 back to standard Julian date */ + date += POSTGRES_EPOCH_JDATE; + + /* Julian day routine does not work for negative Julian days */ + if (date < 0 || date > (double) INT_MAX) + return -1; + + OGRPGj2date((int) date, year, month, day); + OGRPGdt2timeInt8(time, hour, min, &nSec, &dfSec); + *pdfSec += nSec + dfSec; + + return 0; +} + + +/************************************************************************/ +/* TokenizeStringListFromText() */ +/* */ +/* Tokenize a varchar[] returned as a text */ +/************************************************************************/ + +static void OGRPGTokenizeStringListUnescapeToken(char* pszToken) +{ + if (EQUAL(pszToken, "NULL")) + { + pszToken[0] = '\0'; + return; + } + + int iSrc = 0, iDst = 0; + for(iSrc = 0; pszToken[iSrc] != '\0'; iSrc++) + { + pszToken[iDst] = pszToken[iSrc]; + if (pszToken[iSrc] != '\\') + iDst ++; + } + pszToken[iDst] = '\0'; +} + +/* {"a\",b",d,NULL,e} should be tokenized into 3 pieces : a",b d empty_string e */ +static char ** OGRPGTokenizeStringListFromText(const char* pszText) +{ + char** papszTokens = NULL; + const char* pszCur = strchr(pszText, '{'); + if (pszCur == NULL) + { + CPLError(CE_Warning, CPLE_AppDefined, "Incorrect string list : %s", pszText); + return papszTokens; + } + + const char* pszNewTokenStart = NULL; + int bInDoubleQuotes = FALSE; + pszCur ++; + while(*pszCur) + { + if (*pszCur == '\\') + { + pszCur ++; + if (*pszCur == 0) + break; + pszCur ++; + continue; + } + + if (*pszCur == '"') + { + bInDoubleQuotes = !bInDoubleQuotes; + if (bInDoubleQuotes) + pszNewTokenStart = pszCur + 1; + else + { + if (pszCur[1] == ',' || pszCur[1] == '}') + { + if (pszNewTokenStart != NULL && pszCur > pszNewTokenStart) + { + char* pszNewToken = (char*) CPLMalloc(pszCur - pszNewTokenStart + 1); + memcpy(pszNewToken, pszNewTokenStart, pszCur - pszNewTokenStart); + pszNewToken[pszCur - pszNewTokenStart] = 0; + OGRPGTokenizeStringListUnescapeToken(pszNewToken); + papszTokens = CSLAddString(papszTokens, pszNewToken); + CPLFree(pszNewToken); + } + pszNewTokenStart = NULL; + if (pszCur[1] == ',') + pszCur ++; + else + return papszTokens; + } + else + { + /* error */ + break; + } + } + } + if (!bInDoubleQuotes) + { + if (*pszCur == '{') + { + /* error */ + break; + } + else if (*pszCur == '}') + { + if (pszNewTokenStart != NULL && pszCur > pszNewTokenStart) + { + char* pszNewToken = (char*) CPLMalloc(pszCur - pszNewTokenStart + 1); + memcpy(pszNewToken, pszNewTokenStart, pszCur - pszNewTokenStart); + pszNewToken[pszCur - pszNewTokenStart] = 0; + OGRPGTokenizeStringListUnescapeToken(pszNewToken); + papszTokens = CSLAddString(papszTokens, pszNewToken); + CPLFree(pszNewToken); + } + return papszTokens; + } + else if (*pszCur == ',') + { + if (pszNewTokenStart != NULL && pszCur > pszNewTokenStart) + { + char* pszNewToken = (char*) CPLMalloc(pszCur - pszNewTokenStart + 1); + memcpy(pszNewToken, pszNewTokenStart, pszCur - pszNewTokenStart); + pszNewToken[pszCur - pszNewTokenStart] = 0; + OGRPGTokenizeStringListUnescapeToken(pszNewToken); + papszTokens = CSLAddString(papszTokens, pszNewToken); + CPLFree(pszNewToken); + } + pszNewTokenStart = pszCur + 1; + } + else if (pszNewTokenStart == NULL) + pszNewTokenStart = pszCur; + } + pszCur++; + } + + CPLError(CE_Warning, CPLE_AppDefined, "Incorrect string list : %s", pszText); + return papszTokens; +} + +/************************************************************************/ +/* RecordToFeature() */ +/* */ +/* Convert the indicated record of the current result set into */ +/* a feature. */ +/************************************************************************/ + +OGRFeature *OGRPGLayer::RecordToFeature( PGresult* hResult, + const int* panMapFieldNameToIndex, + const int* panMapFieldNameToGeomIndex, + int iRecord ) + +{ +/* -------------------------------------------------------------------- */ +/* Create a feature from the current result. */ +/* -------------------------------------------------------------------- */ + int iField; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + poFeature->SetFID( iNextShapeId ); + m_nFeaturesRead++; + +/* ==================================================================== */ +/* Transfer all result fields we can. */ +/* ==================================================================== */ + for( iField = 0; + iField < PQnfields(hResult); + iField++ ) + { + int iOGRField; + +#if !defined(PG_PRE74) + int nTypeOID = PQftype(hResult, iField); +#endif + const char* pszFieldName = PQfname(hResult,iField); + +/* -------------------------------------------------------------------- */ +/* Handle FID. */ +/* -------------------------------------------------------------------- */ + if( pszFIDColumn != NULL && EQUAL(pszFieldName,pszFIDColumn) ) + { +#if !defined(PG_PRE74) + if ( PQfformat( hResult, iField ) == 1 ) // Binary data representation + { + if ( nTypeOID == INT4OID) + { + int nVal; + CPLAssert(PQgetlength(hResult, iRecord, iField) == sizeof(int)); + memcpy( &nVal, PQgetvalue( hResult, iRecord, iField ), sizeof(int) ); + CPL_MSBPTR32(&nVal); + poFeature->SetFID( nVal ); + } + else if ( nTypeOID == INT8OID) + { + GIntBig nVal; + CPLAssert(PQgetlength(hResult, iRecord, iField) == sizeof(GIntBig)); + memcpy( &nVal, PQgetvalue( hResult, iRecord, iField ), sizeof(GIntBig) ); + CPL_MSBPTR64(&nVal); + poFeature->SetFID( nVal ); + } + else + { + CPLDebug("PG", "FID. Unhandled OID %d.", nTypeOID ); + continue; + } + } + else +#endif /* notdef PG_PRE74 */ + { + char* pabyData = PQgetvalue(hResult,iRecord,iField); + /* ogr_pg_20 may crash if PostGIS is unavailable and we don't test pabyData */ + if (pabyData) + poFeature->SetFID( CPLAtoGIntBig(pabyData) ); + else + continue; + } + } + +/* -------------------------------------------------------------------- */ +/* Handle PostGIS geometry */ +/* -------------------------------------------------------------------- */ + int iOGRGeomField = panMapFieldNameToGeomIndex[iField]; + OGRPGGeomFieldDefn* poGeomFieldDefn = NULL; + if( iOGRGeomField >= 0 ) + poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(iOGRGeomField); + if( iOGRGeomField >= 0 && ( + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY || + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY) ) + { + if ( !poDS->bUseBinaryCursor && + EQUALN(pszFieldName,"BinaryBase64", strlen("BinaryBase64")) ) + { + GByte* pabyData = (GByte*)PQgetvalue( hResult, + iRecord, iField); + + int nLength = PQgetlength(hResult, iRecord, iField); + + /* No geometry */ + if (nLength == 0) + continue; + + nLength = CPLBase64DecodeInPlace(pabyData); + OGRGeometry * poGeom = NULL; + OGRGeometryFactory::createFromWkb( pabyData, NULL, &poGeom, nLength, + (poDS->sPostGISVersion.nMajor < 2) ? wkbVariantPostGIS1 : wkbVariantOldOgc ); + + if( poGeom != NULL ) + { + poGeom->assignSpatialReference( poGeomFieldDefn->GetSpatialRef() ); + poFeature->SetGeomFieldDirectly(iOGRGeomField, poGeom ); + } + + continue; + } + else if ( EQUALN(pszFieldName,"ST_AsBinary", strlen("ST_AsBinary")) || + EQUALN(pszFieldName,"AsBinary", strlen("AsBinary")) ) + { + GByte* pabyVal = (GByte*) PQgetvalue( hResult, + iRecord, iField); + const char* pszVal = (const char*) pabyVal; + + int nLength = PQgetlength(hResult, iRecord, iField); + + /* No geometry */ + if (nLength == 0) + continue; + + OGRGeometry * poGeom = NULL; + if( !poDS->bUseBinaryCursor && nLength >= 4 && + /* escaped byea data */ + (strncmp(pszVal, "\\000",4) == 0 || strncmp(pszVal, "\\001",4) == 0 || + /* hex bytea data (PostgreSQL >= 9.0) */ + strncmp(pszVal, "\\x00",4) == 0 || strncmp(pszVal, "\\x01",4) == 0) ) + { + poGeom = BYTEAToGeometry(pszVal, (poDS->sPostGISVersion.nMajor < 2)); + } + else + OGRGeometryFactory::createFromWkb( pabyVal, NULL, &poGeom, nLength, + (poDS->sPostGISVersion.nMajor < 2) ? wkbVariantPostGIS1 : wkbVariantOldOgc ); + + if( poGeom != NULL ) + { + poGeom->assignSpatialReference( poGeomFieldDefn->GetSpatialRef() ); + poFeature->SetGeomFieldDirectly(iOGRGeomField, poGeom ); + } + + continue; + } + else if ( !poDS->bUseBinaryCursor && + EQUALN(pszFieldName,"EWKBBase64",strlen("EWKBBase64")) ) + { + GByte* pabyData = (GByte*)PQgetvalue( hResult, + iRecord, iField); + + int nLength = PQgetlength(hResult, iRecord, iField); + + /* No geometry */ + if (nLength == 0) + continue; + + nLength = CPLBase64DecodeInPlace(pabyData); + OGRGeometry * poGeom = OGRGeometryFromEWKB(pabyData, nLength, NULL, + poDS->sPostGISVersion.nMajor < 2); + + if( poGeom != NULL ) + { + poGeom->assignSpatialReference( poGeomFieldDefn->GetSpatialRef() ); + poFeature->SetGeomFieldDirectly(iOGRGeomField, poGeom ); + } + + continue; + } + else if ( poDS->bUseBinaryCursor || + EQUAL(pszFieldName,"ST_AsEWKB") || + EQUAL(pszFieldName,"AsEWKB") ) + { + /* Handle HEX result or EWKB binary cursor result */ + char * pabyData = PQgetvalue( hResult, + iRecord, iField); + + int nLength = PQgetlength(hResult, iRecord, iField); + + /* No geometry */ + if (nLength == 0) + continue; + + OGRGeometry * poGeom; + + if( !poDS->bUseBinaryCursor && + (strncmp(pabyData, "\\x00",4) == 0 || strncmp(pabyData, "\\x01",4) == 0 || + strncmp(pabyData, "\\000",4) == 0 || strncmp(pabyData, "\\001",4) == 0) ) + { + GByte* pabyEWKB = BYTEAToGByteArray(pabyData, &nLength); + poGeom = OGRGeometryFromEWKB(pabyEWKB, nLength, NULL, + poDS->sPostGISVersion.nMajor < 2); + CPLFree(pabyEWKB); + } + else if( nLength >= 2 && (EQUALN(pabyData,"00",2) || EQUALN(pabyData,"01",2)) ) + { + poGeom = OGRGeometryFromHexEWKB(pabyData, NULL, + poDS->sPostGISVersion.nMajor < 2); + } + else + { + poGeom = OGRGeometryFromEWKB((GByte*)pabyData, nLength, NULL, + poDS->sPostGISVersion.nMajor < 2); + } + + if( poGeom != NULL ) + { + poGeom->assignSpatialReference( poGeomFieldDefn->GetSpatialRef() ); + poFeature->SetGeomFieldDirectly(iOGRGeomField, poGeom ); + } + + continue; + } + else /*if (EQUAL(pszFieldName,"asEWKT") || + EQUAL(pszFieldName,"asText") || + EQUAL(pszFieldName,"ST_AsEWKT") || + EQUAL(pszFieldName,"ST_AsText") )*/ + { + /* Handle WKT */ + char *pszWKT; + char *pszPostSRID; + OGRGeometry *poGeometry = NULL; + + pszWKT = PQgetvalue( hResult, iRecord, iField ); + pszPostSRID = pszWKT; + + // optionally strip off PostGIS SRID identifier. This + // happens if we got a raw geometry field. + if( EQUALN(pszPostSRID,"SRID=",5) ) + { + while( *pszPostSRID != '\0' && *pszPostSRID != ';' ) + pszPostSRID++; + if( *pszPostSRID == ';' ) + pszPostSRID++; + } + + if( EQUALN(pszPostSRID,"00",2) || EQUALN(pszPostSRID,"01",2) ) + { + poGeometry = OGRGeometryFromHexEWKB( pszWKT, NULL, + poDS->sPostGISVersion.nMajor < 2 ); + } + else + OGRGeometryFactory::createFromWkt( &pszPostSRID, NULL, + &poGeometry ); + if( poGeometry != NULL ) + { + poGeometry->assignSpatialReference( poGeomFieldDefn->GetSpatialRef() ); + poFeature->SetGeomFieldDirectly(iOGRGeomField, poGeometry ); + } + + continue; + } + } +/* -------------------------------------------------------------------- */ +/* Handle raw binary geometry ... this hasn't been tested in a */ +/* while. */ +/* -------------------------------------------------------------------- */ + else if( iOGRGeomField >= 0 && + poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB ) + { + OGRGeometry *poGeometry = NULL; + GByte* pabyData = (GByte*) PQgetvalue( hResult, iRecord, iField); + + if( bWkbAsOid ) + { + poGeometry = + OIDToGeometry( (Oid) atoi((const char*)pabyData) ); + } + else + { + if (poDS->bUseBinaryCursor +#if !defined(PG_PRE74) + && PQfformat( hResult, iField ) == 1 +#endif + ) + { + int nLength = PQgetlength(hResult, iRecord, iField); + poGeometry = OGRGeometryFromEWKB(pabyData, nLength, NULL, + poDS->sPostGISVersion.nMajor < 2 ); + } + if (poGeometry == NULL) + { + poGeometry = BYTEAToGeometry( (const char*)pabyData, + (poDS->sPostGISVersion.nMajor < 2) ); + } + } + + if( poGeometry != NULL ) + { + poGeometry->assignSpatialReference( poGeomFieldDefn->GetSpatialRef() ); + poFeature->SetGeomFieldDirectly(iOGRGeomField, poGeometry ); + } + + continue; + } + +/* -------------------------------------------------------------------- */ +/* Transfer regular data fields. */ +/* -------------------------------------------------------------------- */ + iOGRField = panMapFieldNameToIndex[iField]; + + if( iOGRField < 0 ) + continue; + + if( PQgetisnull( hResult, iRecord, iField ) ) + continue; + + OGRFieldType eOGRType = + poFeatureDefn->GetFieldDefn(iOGRField)->GetType(); + + if( eOGRType == OFTIntegerList) + { + int *panList, nCount, i; + +#if !defined(PG_PRE74) + if ( PQfformat( hResult, iField ) == 1 ) // Binary data representation + { + if (nTypeOID == INT4ARRAYOID) + { + char * pData = PQgetvalue( hResult, iRecord, iField ); + + // goto number of array elements + pData += 3 * sizeof(int); + memcpy( &nCount, pData, sizeof(int) ); + CPL_MSBPTR32( &nCount ); + + panList = (int *) CPLCalloc(sizeof(int),nCount); + + // goto first array element + pData += 2 * sizeof(int); + + for( i = 0; i < nCount; i++ ) + { + // get element size + int nSize = *(int *)(pData); + CPL_MSBPTR32( &nSize ); + + CPLAssert( nSize == sizeof(int) ); + + pData += sizeof(int); + + memcpy( &panList[i], pData, nSize ); + CPL_MSBPTR32(&panList[i]); + + pData += nSize; + } + } + else + { + CPLDebug("PG", "Field %d: Incompatible OID (%d) with OFTIntegerList.", iOGRField, nTypeOID ); + continue; + } + } + else +#endif /* notdef PG_PRE74 */ + { + char **papszTokens; + papszTokens = CSLTokenizeStringComplex( + PQgetvalue( hResult, iRecord, iField ), + "{,}", FALSE, FALSE ); + + nCount = CSLCount(papszTokens); + panList = (int *) CPLCalloc(sizeof(int),nCount); + + if( poFeatureDefn->GetFieldDefn(iOGRField)->GetSubType() == OFSTBoolean ) + { + for( i = 0; i < nCount; i++ ) + panList[i] = EQUAL(papszTokens[i], "t"); + } + else + { + for( i = 0; i < nCount; i++ ) + panList[i] = atoi(papszTokens[i]); + } + CSLDestroy( papszTokens ); + } + poFeature->SetField( iOGRField, nCount, panList ); + CPLFree( panList ); + } + + else if( eOGRType == OFTInteger64List) + { + GIntBig *panList; + int nCount, i; + +#if !defined(PG_PRE74) + if ( PQfformat( hResult, iField ) == 1 ) // Binary data representation + { + if (nTypeOID == INT8ARRAYOID) + { + char * pData = PQgetvalue( hResult, iRecord, iField ); + + // goto number of array elements + pData += 3 * sizeof(int); + memcpy( &nCount, pData, sizeof(int) ); + CPL_MSBPTR32( &nCount ); + + panList = (GIntBig *) CPLCalloc(sizeof(GIntBig),nCount); + + // goto first array element + pData += 2 * sizeof(int); + + for( i = 0; i < nCount; i++ ) + { + // get element size + int nSize = *(int *)(pData); + CPL_MSBPTR32( &nSize ); + + CPLAssert( nSize == sizeof(GIntBig) ); + + pData += sizeof(int); + + memcpy( &panList[i], pData, nSize ); + CPL_MSBPTR64(&panList[i]); + + pData += nSize; + } + } + else + { + CPLDebug("PG", "Field %d: Incompatible OID (%d) with OFTInteger64List.", iOGRField, nTypeOID ); + continue; + } + } + else +#endif /* notdef PG_PRE74 */ + { + char **papszTokens; + papszTokens = CSLTokenizeStringComplex( + PQgetvalue( hResult, iRecord, iField ), + "{,}", FALSE, FALSE ); + + nCount = CSLCount(papszTokens); + panList = (GIntBig *) CPLCalloc(sizeof(GIntBig),nCount); + + if( poFeatureDefn->GetFieldDefn(iOGRField)->GetSubType() == OFSTBoolean ) + { + for( i = 0; i < nCount; i++ ) + panList[i] = EQUAL(papszTokens[i], "t"); + } + else + { + for( i = 0; i < nCount; i++ ) + panList[i] = CPLAtoGIntBig(papszTokens[i]); + } + CSLDestroy( papszTokens ); + } + poFeature->SetField( iOGRField, nCount, panList ); + CPLFree( panList ); + } + + else if( eOGRType == OFTRealList ) + { + int nCount, i; + double *padfList; + +#if !defined(PG_PRE74) + if ( PQfformat( hResult, iField ) == 1 ) // Binary data representation + { + if (nTypeOID == FLOAT8ARRAYOID || nTypeOID == FLOAT4ARRAYOID) + { + char * pData = PQgetvalue( hResult, iRecord, iField ); + + // goto number of array elements + pData += 3 * sizeof(int); + memcpy( &nCount, pData, sizeof(int) ); + CPL_MSBPTR32( &nCount ); + + padfList = (double *) CPLCalloc(sizeof(double),nCount); + + // goto first array element + pData += 2 * sizeof(int); + + for( i = 0; i < nCount; i++ ) + { + // get element size + int nSize = *(int *)(pData); + CPL_MSBPTR32( &nSize ); + + pData += sizeof(int); + + if (nTypeOID == FLOAT8ARRAYOID) + { + CPLAssert( nSize == sizeof(double) ); + + memcpy( &padfList[i], pData, nSize ); + CPL_MSBPTR64(&padfList[i]); + } + else + { + float fVal; + CPLAssert( nSize == sizeof(float) ); + + memcpy( &fVal, pData, nSize ); + CPL_MSBPTR32(&fVal); + + padfList[i] = fVal; + } + + pData += nSize; + } + } + else + { + CPLDebug("PG", "Field %d: Incompatible OID (%d) with OFTRealList.", iOGRField, nTypeOID ); + continue; + } + } + else +#endif /* notdef PG_PRE74 */ + { + char **papszTokens; + papszTokens = CSLTokenizeStringComplex( + PQgetvalue( hResult, iRecord, iField ), + "{,}", FALSE, FALSE ); + + nCount = CSLCount(papszTokens); + padfList = (double *) CPLCalloc(sizeof(double),nCount); + + for( i = 0; i < nCount; i++ ) + padfList[i] = CPLAtof(papszTokens[i]); + CSLDestroy( papszTokens ); + } + + poFeature->SetField( iOGRField, nCount, padfList ); + CPLFree( padfList ); + } + + else if( eOGRType == OFTStringList ) + { + char **papszTokens = 0; + +#if !defined(PG_PRE74) + if ( PQfformat( hResult, iField ) == 1 ) // Binary data representation + { + char * pData = PQgetvalue( hResult, iRecord, iField ); + int nCount, i; + + // goto number of array elements + pData += 3 * sizeof(int); + memcpy( &nCount, pData, sizeof(int) ); + CPL_MSBPTR32( &nCount ); + + // goto first array element + pData += 2 * sizeof(int); + + for( i = 0; i < nCount; i++ ) + { + // get element size + int nSize = *(int *)(pData); + CPL_MSBPTR32( &nSize ); + + pData += sizeof(int); + + if (nSize <= 0) + papszTokens = CSLAddString(papszTokens, ""); + else + { + if (pData[nSize] == '\0') + papszTokens = CSLAddString(papszTokens, pData); + else + { + char* pszToken = (char*) CPLMalloc(nSize + 1); + memcpy(pszToken, pData, nSize); + pszToken[nSize] = '\0'; + papszTokens = CSLAddString(papszTokens, pszToken); + CPLFree(pszToken); + } + + pData += nSize; + } + } + } + else +#endif /* notdef PG_PRE74 */ + { + papszTokens = + OGRPGTokenizeStringListFromText(PQgetvalue(hResult, iRecord, iField )); + } + + if ( papszTokens ) + { + poFeature->SetField( iOGRField, papszTokens ); + CSLDestroy( papszTokens ); + } + } + + else if( eOGRType == OFTDate + || eOGRType == OFTTime + || eOGRType == OFTDateTime ) + { +#if !defined(PG_PRE74) + if ( PQfformat( hResult, iField ) == 1 ) // Binary data + { + if ( nTypeOID == DATEOID ) + { + int nVal, nYear, nMonth, nDay; + CPLAssert(PQgetlength(hResult, iRecord, iField) == sizeof(int)); + memcpy( &nVal, PQgetvalue( hResult, iRecord, iField ), sizeof(int) ); + CPL_MSBPTR32(&nVal); + OGRPGj2date(nVal + POSTGRES_EPOCH_JDATE, &nYear, &nMonth, &nDay); + poFeature->SetField( iOGRField, nYear, nMonth, nDay); + } + else if ( nTypeOID == TIMEOID ) + { + int nHour, nMinute, nSecond; + char szTime[32]; + double dfsec; + CPLAssert(PQgetlength(hResult, iRecord, iField) == 8); + if (poDS->bBinaryTimeFormatIsInt8) + { + unsigned int nVal[2]; + GIntBig llVal; + memcpy( nVal, PQgetvalue( hResult, iRecord, iField ), 8 ); + CPL_MSBPTR32(&nVal[0]); + CPL_MSBPTR32(&nVal[1]); + llVal = (GIntBig) ((((GUIntBig)nVal[0]) << 32) | nVal[1]); + OGRPGdt2timeInt8(llVal, &nHour, &nMinute, &nSecond, &dfsec); + } + else + { + double dfVal; + memcpy( &dfVal, PQgetvalue( hResult, iRecord, iField ), 8 ); + CPL_MSBPTR64(&dfVal); + OGRPGdt2timeFloat8(dfVal, &nHour, &nMinute, &nSecond, &dfsec); + } + sprintf(szTime, "%02d:%02d:%02d", nHour, nMinute, nSecond); + poFeature->SetField( iOGRField, szTime); + } + else if ( nTypeOID == TIMESTAMPOID || nTypeOID == TIMESTAMPTZOID ) + { + unsigned int nVal[2]; + GIntBig llVal; + int nYear, nMonth, nDay, nHour, nMinute; + double dfSecond; + CPLAssert(PQgetlength(hResult, iRecord, iField) == 8); + memcpy( nVal, PQgetvalue( hResult, iRecord, iField ), 8 ); + CPL_MSBPTR32(&nVal[0]); + CPL_MSBPTR32(&nVal[1]); + llVal = (GIntBig) ((((GUIntBig)nVal[0]) << 32) | nVal[1]); + if (OGRPGTimeStamp2DMYHMS(llVal, &nYear, &nMonth, &nDay, &nHour, &nMinute, &dfSecond) == 0) + poFeature->SetField( iOGRField, nYear, nMonth, nDay, nHour, nMinute, (float)dfSecond, 100); + } + else if ( nTypeOID == TEXTOID ) + { + OGRField sFieldValue; + + if( OGRParseDate( PQgetvalue( hResult, iRecord, iField ), + &sFieldValue, 0 ) ) + { + poFeature->SetField( iOGRField, &sFieldValue ); + } + } + else + { + CPLDebug( "PG", "Binary DATE format not yet implemented. OID = %d", nTypeOID ); + } + } + else +#endif /* notdef PG_PRE74 */ + { + OGRField sFieldValue; + + if( OGRParseDate( PQgetvalue( hResult, iRecord, iField ), + &sFieldValue, 0 ) ) + { + poFeature->SetField( iOGRField, &sFieldValue ); + } + } + } + else if( eOGRType == OFTBinary ) + { +#if !defined(PG_PRE74) + if ( PQfformat( hResult, iField ) == 1) + { + int nLength = PQgetlength(hResult, iRecord, iField); + GByte* pabyData = (GByte*) PQgetvalue( hResult, iRecord, iField ); + poFeature->SetField( iOGRField, nLength, pabyData ); + } + else +#endif /* notdef PG_PRE74 */ + { + int nLength = PQgetlength(hResult, iRecord, iField); + const char* pszBytea = (const char*) PQgetvalue( hResult, iRecord, iField ); + GByte* pabyData = BYTEAToGByteArray( pszBytea, &nLength ); + poFeature->SetField( iOGRField, nLength, pabyData ); + CPLFree(pabyData); + } + } + else + { +#if !defined(PG_PRE74) + if ( PQfformat( hResult, iField ) == 1 && + eOGRType != OFTString ) // Binary data + { + if ( nTypeOID == BOOLOID ) + { + char cVal; + CPLAssert(PQgetlength(hResult, iRecord, iField) == sizeof(char)); + cVal = *PQgetvalue( hResult, iRecord, iField ); + poFeature->SetField( iOGRField, cVal ); + } + else if ( nTypeOID == NUMERICOID ) + { + unsigned short sLen, sSign, sDscale; + short sWeight; + char* pabyData = PQgetvalue( hResult, iRecord, iField ); + memcpy( &sLen, pabyData, sizeof(short)); + pabyData += sizeof(short); + CPL_MSBPTR16(&sLen); + memcpy( &sWeight, pabyData, sizeof(short)); + pabyData += sizeof(short); + CPL_MSBPTR16(&sWeight); + memcpy( &sSign, pabyData, sizeof(short)); + pabyData += sizeof(short); + CPL_MSBPTR16(&sSign); + memcpy( &sDscale, pabyData, sizeof(short)); + pabyData += sizeof(short); + CPL_MSBPTR16(&sDscale); + CPLAssert(PQgetlength(hResult, iRecord, iField) == (int)((4 + sLen) * sizeof(short))); + + NumericVar var; + var.ndigits = sLen; + var.weight = sWeight; + var.sign = sSign; + var.dscale = sDscale; + var.digits = (NumericDigit*)pabyData; + char* str = OGRPGGetStrFromBinaryNumeric(&var); + poFeature->SetField( iOGRField, CPLAtof(str)); + CPLFree(str); + } + else if ( nTypeOID == INT2OID ) + { + short sVal; + CPLAssert(PQgetlength(hResult, iRecord, iField) == sizeof(short)); + memcpy( &sVal, PQgetvalue( hResult, iRecord, iField ), sizeof(short) ); + CPL_MSBPTR16(&sVal); + poFeature->SetField( iOGRField, sVal ); + } + else if ( nTypeOID == INT4OID ) + { + int nVal; + CPLAssert(PQgetlength(hResult, iRecord, iField) == sizeof(int)); + memcpy( &nVal, PQgetvalue( hResult, iRecord, iField ), sizeof(int) ); + CPL_MSBPTR32(&nVal); + poFeature->SetField( iOGRField, nVal ); + } + else if ( nTypeOID == INT8OID ) + { + unsigned int nVal[2]; + GIntBig llVal; + CPLAssert(PQgetlength(hResult, iRecord, iField) == 8); + memcpy( nVal, PQgetvalue( hResult, iRecord, iField ), 8 ); + CPL_MSBPTR32(&nVal[0]); + CPL_MSBPTR32(&nVal[1]); + llVal = (GIntBig) ((((GUIntBig)nVal[0]) << 32) | nVal[1]); + poFeature->SetField( iOGRField, llVal ); + } + else if ( nTypeOID == FLOAT4OID ) + { + float fVal; + CPLAssert(PQgetlength(hResult, iRecord, iField) == sizeof(float)); + memcpy( &fVal, PQgetvalue( hResult, iRecord, iField ), sizeof(float) ); + CPL_MSBPTR32(&fVal); + poFeature->SetField( iOGRField, fVal ); + } + else if ( nTypeOID == FLOAT8OID ) + { + double dfVal; + CPLAssert(PQgetlength(hResult, iRecord, iField) == sizeof(double)); + memcpy( &dfVal, PQgetvalue( hResult, iRecord, iField ), sizeof(double) ); + CPL_MSBPTR64(&dfVal); + poFeature->SetField( iOGRField, dfVal ); + } + else + { + CPLDebug("PG", "Field %d(%s): Incompatible OID (%d) with %s.", + iOGRField, poFeatureDefn->GetFieldDefn(iOGRField)->GetNameRef(), + nTypeOID, + OGRFieldDefn::GetFieldTypeName( eOGRType )); + continue; + } + } + else +#endif /* notdef PG_PRE74 */ + { + if ( eOGRType == OFTInteger && + poFeatureDefn->GetFieldDefn(iOGRField)->GetWidth() == 1) + { + char* pabyData = PQgetvalue( hResult, iRecord, iField ); + if (EQUALN(pabyData, "T", 1)) + poFeature->SetField( iOGRField, 1); + else if (EQUALN(pabyData, "F", 1)) + poFeature->SetField( iOGRField, 0); + else + poFeature->SetField( iOGRField, pabyData); + } + else if ( eOGRType == OFTReal ) + { + poFeature->SetField( iOGRField, + CPLAtof(PQgetvalue( hResult, iRecord, iField )) ); + } + else + { + poFeature->SetField( iOGRField, + PQgetvalue( hResult, iRecord, iField ) ); + } + } + } + } + + return poFeature; +} + +/************************************************************************/ +/* OGRPGIsKnownGeomFuncPrefix() */ +/************************************************************************/ + +static const char* papszKnownGeomFuncPrefixes[] = { + "ST_AsBinary", "BinaryBase64", "ST_AsEWKT", "ST_AsEWKB", "EWKBBase64", + "ST_AsText", "AsBinary", "asEWKT", "asEWKB", "asText" }; +static int OGRPGIsKnownGeomFuncPrefix(const char* pszFieldName) +{ + for(size_t i=0; iGetFieldIndex(pszName); + if( panMapFieldNameToIndex[iField] < 0 ) + { + panMapFieldNameToGeomIndex[iField] = + poFeatureDefn->GetGeomFieldIndex(pszName); + if( panMapFieldNameToGeomIndex[iField] < 0 ) + { + int iKnownPrefix = OGRPGIsKnownGeomFuncPrefix(pszName); + if( iKnownPrefix >= 0 && + pszName[ strlen(papszKnownGeomFuncPrefixes[iKnownPrefix]) ] == '_' ) + { + panMapFieldNameToGeomIndex[iField] = + poFeatureDefn->GetGeomFieldIndex(pszName + + strlen(papszKnownGeomFuncPrefixes[iKnownPrefix]) + 1); + } + } + } + else + panMapFieldNameToGeomIndex[iField] = -1; + } + } +} + + +/************************************************************************/ +/* SetInitialQueryCursor() */ +/************************************************************************/ + +void OGRPGLayer::SetInitialQueryCursor() +{ + PGconn *hPGConn = poDS->GetPGConn(); + CPLString osCommand; + + CPLAssert( pszQueryStatement != NULL ); + + poDS->SoftStartTransaction(); + + if ( poDS->bUseBinaryCursor && bCanUseBinaryCursor ) + osCommand.Printf( "DECLARE %s BINARY CURSOR for %s", + pszCursorName, pszQueryStatement ); + else + osCommand.Printf( "DECLARE %s CURSOR for %s", + pszCursorName, pszQueryStatement ); + + hCursorResult = OGRPG_PQexec(hPGConn, osCommand ); + if ( !hCursorResult || PQresultStatus(hCursorResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage( hPGConn ) ); + poDS->SoftRollbackTransaction(); + } + OGRPGClearResult( hCursorResult ); + + osCommand.Printf( "FETCH %d in %s", nCursorPage, pszCursorName ); + hCursorResult = OGRPG_PQexec(hPGConn, osCommand ); + + CreateMapFromFieldNameToIndex(hCursorResult, + poFeatureDefn, + m_panMapFieldNameToIndex, + m_panMapFieldNameToGeomIndex); + + nResultOffset = 0; +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRPGLayer::GetNextRawFeature() + +{ + PGconn *hPGConn = poDS->GetPGConn(); + CPLString osCommand; + + if( bInvalidated ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cursor used to read layer has been closed due to a COMMIT. " + "ResetReading() must be explicitly called to restart reading"); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we need to establish an initial query? */ +/* -------------------------------------------------------------------- */ + if( iNextShapeId == 0 && hCursorResult == NULL ) + { + SetInitialQueryCursor(); + } + +/* -------------------------------------------------------------------- */ +/* Are we in some sort of error condition? */ +/* -------------------------------------------------------------------- */ + if( hCursorResult == NULL + || PQresultStatus(hCursorResult) != PGRES_TUPLES_OK ) + { + CPLDebug( "PG", "PQclear() on an error condition"); + + OGRPGClearResult( hCursorResult ); + + iNextShapeId = MAX(1,iNextShapeId); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we need to fetch more records? */ +/* -------------------------------------------------------------------- */ + + /* We test for PQntuples(hCursorResult) == 1 in the case the previous */ + /* request was a SetNextByIndex() */ + if( (PQntuples(hCursorResult) == 1 || PQntuples(hCursorResult) == nCursorPage) && + nResultOffset == PQntuples(hCursorResult) ) + { + OGRPGClearResult( hCursorResult ); + + osCommand.Printf( "FETCH %d in %s", nCursorPage, pszCursorName ); + hCursorResult = OGRPG_PQexec(hPGConn, osCommand ); + + nResultOffset = 0; + } + +/* -------------------------------------------------------------------- */ +/* Are we out of results? If so complete the transaction, and */ +/* cleanup, but don't reset the next shapeid. */ +/* -------------------------------------------------------------------- */ + if( nResultOffset == PQntuples(hCursorResult) ) + { + CloseCursor(); + + iNextShapeId = MAX(1,iNextShapeId); + + return NULL; + } + + +/* -------------------------------------------------------------------- */ +/* Create a feature from the current result. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = RecordToFeature( hCursorResult, + m_panMapFieldNameToIndex, + m_panMapFieldNameToGeomIndex, + nResultOffset ); + + nResultOffset++; + iNextShapeId++; + + return poFeature; +} + +/************************************************************************/ +/* SetNextByIndex() */ +/************************************************************************/ + +OGRErr OGRPGLayer::SetNextByIndex( GIntBig nIndex ) + +{ + GetLayerDefn(); + + if( !TestCapability(OLCFastSetNextByIndex) ) + return OGRLayer::SetNextByIndex(nIndex); + + if( nIndex == iNextShapeId) + { + return OGRERR_NONE; + } + + if( nIndex < 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid index"); + return OGRERR_FAILURE; + } + + if( nIndex == 0 ) + { + ResetReading(); + return OGRERR_NONE; + } + + PGconn *hPGConn = poDS->GetPGConn(); + CPLString osCommand; + + if (hCursorResult == NULL ) + { + SetInitialQueryCursor(); + } + + OGRPGClearResult( hCursorResult ); + + osCommand.Printf( "FETCH ABSOLUTE " CPL_FRMT_GIB " in %s", nIndex+1, pszCursorName ); + hCursorResult = OGRPG_PQexec(hPGConn, osCommand ); + + if (PQresultStatus(hCursorResult) != PGRES_TUPLES_OK || + PQntuples(hCursorResult) != 1) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to read feature at invalid index (" CPL_FRMT_GIB ").", nIndex ); + + CloseCursor(); + + iNextShapeId = 0; + + return OGRERR_FAILURE; + } + + nResultOffset = 0; + iNextShapeId = nIndex; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* BYTEAToGByteArray() */ +/************************************************************************/ + +GByte* OGRPGLayer::BYTEAToGByteArray( const char *pszBytea, int* pnLength ) +{ + GByte* pabyData; + int iSrc=0, iDst=0; + + if( pszBytea == NULL ) + { + if (pnLength) *pnLength = 0; + return NULL; + } + + /* hex bytea data (PostgreSQL >= 9.0) */ + if (pszBytea[0] == '\\' && pszBytea[1] == 'x') + return CPLHexToBinary(pszBytea + 2, pnLength); + + pabyData = (GByte *) CPLMalloc(strlen(pszBytea)); + + while( pszBytea[iSrc] != '\0' ) + { + if( pszBytea[iSrc] == '\\' ) + { + if( pszBytea[iSrc+1] >= '0' && pszBytea[iSrc+1] <= '9' ) + { + if (pszBytea[iSrc+2] == '\0' || + pszBytea[iSrc+3] == '\0') + break; + + pabyData[iDst++] = + (pszBytea[iSrc+1] - 48) * 64 + + (pszBytea[iSrc+2] - 48) * 8 + + (pszBytea[iSrc+3] - 48) * 1; + iSrc += 4; + } + else + { + if (pszBytea[iSrc+1] == '\0') + break; + + pabyData[iDst++] = pszBytea[iSrc+1]; + iSrc += 2; + } + } + else + { + pabyData[iDst++] = pszBytea[iSrc++]; + } + } + if (pnLength) *pnLength = iDst; + + return pabyData; +} + + +/************************************************************************/ +/* BYTEAToGeometry() */ +/************************************************************************/ + +OGRGeometry *OGRPGLayer::BYTEAToGeometry( const char *pszBytea, int bIsPostGIS1 ) + +{ + GByte *pabyWKB; + int nLen=0; + OGRGeometry *poGeometry; + + if( pszBytea == NULL ) + return NULL; + + pabyWKB = BYTEAToGByteArray(pszBytea, &nLen); + + poGeometry = NULL; + OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeometry, nLen, + (bIsPostGIS1) ? wkbVariantPostGIS1 : wkbVariantOldOgc ); + + CPLFree( pabyWKB ); + return poGeometry; +} + + +/************************************************************************/ +/* GByteArrayToBYTEA() */ +/************************************************************************/ + +char* OGRPGLayer::GByteArrayToBYTEA( const GByte* pabyData, int nLen) +{ + char* pszTextBuf; + + pszTextBuf = (char *) CPLMalloc(nLen*5+1); + + int iSrc, iDst=0; + + for( iSrc = 0; iSrc < nLen; iSrc++ ) + { + if( pabyData[iSrc] < 40 || pabyData[iSrc] > 126 + || pabyData[iSrc] == '\\' ) + { + sprintf( pszTextBuf+iDst, "\\\\%03o", pabyData[iSrc] ); + iDst += 5; + } + else + pszTextBuf[iDst++] = pabyData[iSrc]; + } + pszTextBuf[iDst] = '\0'; + + return pszTextBuf; +} + +/************************************************************************/ +/* GeometryToBYTEA() */ +/************************************************************************/ + +char *OGRPGLayer::GeometryToBYTEA( OGRGeometry * poGeometry, int bIsPostGIS1 ) + +{ + int nWkbSize = poGeometry->WkbSize(); + GByte *pabyWKB; + char *pszTextBuf; + + pabyWKB = (GByte *) CPLMalloc(nWkbSize); + if( poGeometry->exportToWkb( wkbNDR, pabyWKB, + (bIsPostGIS1) ? wkbVariantPostGIS1 : wkbVariantOldOgc ) != OGRERR_NONE ) + { + CPLFree(pabyWKB); + return CPLStrdup(""); + } + + pszTextBuf = GByteArrayToBYTEA( pabyWKB, nWkbSize ); + CPLFree(pabyWKB); + + return pszTextBuf; +} + +/************************************************************************/ +/* OIDToGeometry() */ +/************************************************************************/ + +OGRGeometry *OGRPGLayer::OIDToGeometry( Oid oid ) + +{ + PGconn *hPGConn = poDS->GetPGConn(); + GByte *pabyWKB; + int fd, nBytes; + OGRGeometry *poGeometry; + +#define MAX_WKB 500000 + + if( oid == 0 ) + return NULL; + + fd = lo_open( hPGConn, oid, INV_READ ); + if( fd < 0 ) + return NULL; + + pabyWKB = (GByte *) CPLMalloc(MAX_WKB); + nBytes = lo_read( hPGConn, fd, (char *) pabyWKB, MAX_WKB ); + lo_close( hPGConn, fd ); + + poGeometry = NULL; + OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeometry, nBytes, + (poDS->sPostGISVersion.nMajor < 2) ? wkbVariantPostGIS1 : wkbVariantOldOgc ); + + CPLFree( pabyWKB ); + + return poGeometry; +} + +/************************************************************************/ +/* GeometryToOID() */ +/************************************************************************/ + +Oid OGRPGLayer::GeometryToOID( OGRGeometry * poGeometry ) + +{ + PGconn *hPGConn = poDS->GetPGConn(); + int nWkbSize = poGeometry->WkbSize(); + GByte *pabyWKB; + Oid oid; + int fd, nBytesWritten; + + pabyWKB = (GByte *) CPLMalloc(nWkbSize); + if( poGeometry->exportToWkb( wkbNDR, pabyWKB, + (poDS->sPostGISVersion.nMajor < 2) ? wkbVariantPostGIS1 : wkbVariantOldOgc ) != OGRERR_NONE ) + return 0; + + oid = lo_creat( hPGConn, INV_READ|INV_WRITE ); + + fd = lo_open( hPGConn, oid, INV_WRITE ); + nBytesWritten = lo_write( hPGConn, fd, (char *) pabyWKB, nWkbSize ); + lo_close( hPGConn, fd ); + + if( nBytesWritten != nWkbSize ) + { + CPLDebug( "PG", + "Only wrote %d bytes of %d intended for (fd=%d,oid=%d).\n", + nBytesWritten, nWkbSize, fd, oid ); + } + + CPLFree( pabyWKB ); + + return oid; +} + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +OGRErr OGRPGLayer::StartTransaction() + +{ + return poDS->StartTransaction(); +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr OGRPGLayer::CommitTransaction() + +{ + return poDS->CommitTransaction(); +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +OGRErr OGRPGLayer::RollbackTransaction() + +{ + return poDS->RollbackTransaction(); +} + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char *OGRPGLayer::GetFIDColumn() + +{ + GetLayerDefn(); + + if( pszFIDColumn != NULL ) + return pszFIDColumn; + else + return ""; +} + +/************************************************************************/ +/* GetExtent() */ +/* */ +/* For PostGIS use internal Extend(geometry) function */ +/* in other cases we use standard OGRLayer::GetExtent() */ +/************************************************************************/ + +OGRErr OGRPGLayer::GetExtent( int iGeomField, OGREnvelope *psExtent, int bForce ) +{ + CPLString osCommand; + + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() || + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone ) + { + if( iGeomField != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } + return OGRERR_FAILURE; + } + + OGRPGGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(iGeomField); + + const char* pszExtentFct; + if (poDS->sPostGISVersion.nMajor >= 2) + pszExtentFct = "ST_Extent"; + else + pszExtentFct = "Extent"; + + if ( TestCapability(OLCFastGetExtent) ) + { + /* Do not take the spatial filter into account */ + osCommand.Printf( "SELECT %s(%s) FROM %s AS ogrpgextent", + pszExtentFct, + OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(), + GetFromClauseForGetExtent().c_str() ); + } + else if ( poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ) + { + /* Probably not very efficient, but more efficient than client-side implementation */ + osCommand.Printf( "SELECT %s(ST_GeomFromWKB(ST_AsBinary(%s))) FROM %s AS ogrpgextent", + pszExtentFct, + OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(), + GetFromClauseForGetExtent().c_str() ); + } + + if( osCommand.size() != 0 ) + { + if( RunGetExtentRequest(psExtent, bForce, osCommand, FALSE) == OGRERR_NONE ) + return OGRERR_NONE; + } + if( iGeomField == 0 ) + return OGRLayer::GetExtent( psExtent, bForce ); + else + return OGRLayer::GetExtent( iGeomField, psExtent, bForce ); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRPGLayer::RunGetExtentRequest( OGREnvelope *psExtent, + CPL_UNUSED int bForce, + CPLString osCommand, + int bErrorAsDebug ) +{ + if ( psExtent == NULL ) + return OGRERR_FAILURE; + + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult = NULL; + + hResult = OGRPG_PQexec( hPGConn, osCommand, FALSE, bErrorAsDebug ); + if( ! hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK || PQgetisnull(hResult,0,0) ) + { + OGRPGClearResult( hResult ); + CPLDebug("PG","Unable to get extent by PostGIS."); + return OGRERR_FAILURE; + } + + char * pszBox = PQgetvalue(hResult,0,0); + char * ptr, *ptrEndParenthesis; + char szVals[64*6+6]; + + ptr = strchr(pszBox, '('); + if (ptr) + ptr ++; + if (ptr == NULL || + (ptrEndParenthesis = strchr(ptr, ')')) == NULL || + ptrEndParenthesis - ptr > (int)(sizeof(szVals) - 1)) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Bad extent representation: '%s'", pszBox); + + OGRPGClearResult( hResult ); + return OGRERR_FAILURE; + } + + strncpy(szVals,ptr,ptrEndParenthesis - ptr); + szVals[ptrEndParenthesis - ptr] = '\0'; + + char ** papszTokens = CSLTokenizeString2(szVals," ,",CSLT_HONOURSTRINGS); + int nTokenCnt = poDS->sPostGISVersion.nMajor >= 1 ? 4 : 6; + + if ( CSLCount(papszTokens) != nTokenCnt ) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Bad extent representation: '%s'", pszBox); + CSLDestroy(papszTokens); + + OGRPGClearResult( hResult ); + return OGRERR_FAILURE; + } + + // Take X,Y coords + // For PostGis ver >= 1.0.0 -> Tokens: X1 Y1 X2 Y2 (nTokenCnt = 4) + // For PostGIS ver < 1.0.0 -> Tokens: X1 Y1 Z1 X2 Y2 Z2 (nTokenCnt = 6) + // => X2 index calculated as nTokenCnt/2 + // Y2 index caluclated as nTokenCnt/2+1 + + psExtent->MinX = CPLAtof( papszTokens[0] ); + psExtent->MinY = CPLAtof( papszTokens[1] ); + psExtent->MaxX = CPLAtof( papszTokens[nTokenCnt/2] ); + psExtent->MaxY = CPLAtof( papszTokens[nTokenCnt/2+1] ); + + CSLDestroy(papszTokens); + OGRPGClearResult( hResult ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRPGLayer::GetLayerDefn() +{ + return poFeatureDefn; +} + +/************************************************************************/ +/* ReadResultDefinition() */ +/* */ +/* Build a schema from the current resultset. */ +/************************************************************************/ + +int OGRPGLayer::ReadResultDefinition(PGresult *hInitialResultIn) + +{ + PGresult *hResult = hInitialResultIn; + +/* -------------------------------------------------------------------- */ +/* Parse the returned table information. */ +/* -------------------------------------------------------------------- */ + poFeatureDefn = new OGRPGFeatureDefn( "sql_statement" ); + SetDescription( poFeatureDefn->GetName() ); + int iRawField; + + poFeatureDefn->Reference(); + + for( iRawField = 0; iRawField < PQnfields(hResult); iRawField++ ) + { + OGRFieldDefn oField( PQfname(hResult,iRawField), OFTString); + Oid nTypeOID; + + nTypeOID = PQftype(hResult,iRawField); + + int iGeomFuncPrefix; + if( EQUAL(oField.GetNameRef(),"ogc_fid") ) + { + if (pszFIDColumn) + { + CPLError(CE_Warning, CPLE_AppDefined, + "More than one ogc_fid column was found in the result of the SQL request. Only last one will be used"); + } + CPLFree(pszFIDColumn); + pszFIDColumn = CPLStrdup(oField.GetNameRef()); + continue; + } + else if( (iGeomFuncPrefix = + OGRPGIsKnownGeomFuncPrefix(oField.GetNameRef())) >= 0 || + nTypeOID == poDS->GetGeometryOID() || + nTypeOID == poDS->GetGeographyOID() ) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = + new OGRPGGeomFieldDefn(this, oField.GetNameRef()); + if( iGeomFuncPrefix >= 0 && + oField.GetNameRef()[strlen( + papszKnownGeomFuncPrefixes[iGeomFuncPrefix])] == '_' ) + { + poGeomFieldDefn->SetName( oField.GetNameRef() + + strlen(papszKnownGeomFuncPrefixes[iGeomFuncPrefix]) + 1 ); + } + if (nTypeOID == poDS->GetGeographyOID()) + { + poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOGRAPHY; + poGeomFieldDefn->nSRSId = 4326; + } + else + poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOMETRY; + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + continue; + } + else if( EQUAL(oField.GetNameRef(),"WKB_GEOMETRY") ) + { + if( nTypeOID == OIDOID ) + bWkbAsOid = TRUE; + OGRPGGeomFieldDefn* poGeomFieldDefn = + new OGRPGGeomFieldDefn(this, oField.GetNameRef()); + poGeomFieldDefn->ePostgisType = GEOM_TYPE_WKB; + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + continue; + } + + //CPLDebug("PG", "Field %s, oid %d", oField.GetNameRef(), nTypeOID); + + if( nTypeOID == BYTEAOID ) + { + oField.SetType( OFTBinary ); + } + else if( nTypeOID == CHAROID || + nTypeOID == TEXTOID || + nTypeOID == BPCHAROID || + nTypeOID == VARCHAROID ) + { + oField.SetType( OFTString ); + + /* See http://www.mail-archive.com/pgsql-hackers@postgresql.org/msg57726.html */ + /* nTypmod = width + 4 */ + int nTypmod = PQfmod(hResult, iRawField); + if (nTypmod >= 4 && (nTypeOID == BPCHAROID || + nTypeOID == VARCHAROID ) ) + { + oField.SetWidth( nTypmod - 4); + } + } + else if( nTypeOID == BOOLOID ) + { + oField.SetType( OFTInteger ); + oField.SetSubType( OFSTBoolean ); + oField.SetWidth( 1 ); + } + else if (nTypeOID == INT2OID ) + { + oField.SetType( OFTInteger ); + oField.SetSubType( OFSTInt16 ); + oField.SetWidth( 5 ); + } + else if (nTypeOID == INT4OID ) + { + oField.SetType( OFTInteger ); + } + else if ( nTypeOID == INT8OID ) + { + oField.SetType( OFTInteger64 ); + } + else if( nTypeOID == FLOAT4OID ) + { + oField.SetType( OFTReal ); + oField.SetSubType( OFSTFloat32 ); + } + else if( nTypeOID == FLOAT8OID ) + { + oField.SetType( OFTReal ); + } + else if( nTypeOID == NUMERICOID ) + { + /* See http://www.mail-archive.com/pgsql-hackers@postgresql.org/msg57726.html */ + /* typmod = (width << 16) + precision + 4 */ + int nTypmod = PQfmod(hResult, iRawField); + if (nTypmod >= 4) + { + int nWidth = (nTypmod - 4) >> 16; + int nPrecision = (nTypmod - 4) & 0xFFFF; + if (nWidth <= 10 && nPrecision == 0) + { + oField.SetType( OFTInteger ); + oField.SetWidth( nWidth ); + } + else + { + oField.SetType( OFTReal ); + oField.SetWidth( nWidth ); + oField.SetPrecision( nPrecision ); + } + } + else + oField.SetType( OFTReal ); + } + else if ( nTypeOID == BOOLARRAYOID ) + { + oField.SetType ( OFTIntegerList ); + oField.SetSubType( OFSTBoolean ); + oField.SetWidth( 1 ); + } + else if ( nTypeOID == INT4ARRAYOID ) + { + oField.SetType ( OFTIntegerList ); + } + else if ( nTypeOID == INT8ARRAYOID ) + { + oField.SetType ( OFTInteger64List ); + } + else if ( nTypeOID == FLOAT4ARRAYOID || + nTypeOID == FLOAT8ARRAYOID ) + { + oField.SetType ( OFTRealList ); + } + else if ( nTypeOID == TEXTARRAYOID || + nTypeOID == BPCHARARRAYOID || + nTypeOID == VARCHARARRAYOID ) + { + oField.SetType ( OFTStringList ); + } + else if ( nTypeOID == DATEOID ) + { + oField.SetType( OFTDate ); + } + else if ( nTypeOID == TIMEOID ) + { + oField.SetType( OFTTime ); + } + else if ( nTypeOID == TIMESTAMPOID || + nTypeOID == TIMESTAMPTZOID ) + { + /* We can't deserialize properly timestamp with time zone */ + /* with binary cursors */ + if (nTypeOID == TIMESTAMPTZOID) + bCanUseBinaryCursor = FALSE; + + oField.SetType( OFTDateTime ); + } + else /* unknown type */ + { + CPLDebug("PG", "Unhandled OID (%d) for column %s. Defaulting to String.", + nTypeOID, oField.GetNameRef()); + oField.SetType( OFTString ); + } + + poFeatureDefn->AddFieldDefn( &oField ); + } + + return TRUE; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference* OGRPGGeomFieldDefn::GetSpatialRef() +{ + if( poLayer == NULL ) + return NULL; + if (nSRSId == UNDETERMINED_SRID) + poLayer->ResolveSRID(this); + + if( poSRS == NULL && nSRSId > 0 ) + { + poSRS = poLayer->GetDS()->FetchSRS( nSRSId ); + if( poSRS != NULL ) + poSRS->Reference(); + } + return poSRS; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgresultlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgresultlayer.cpp new file mode 100644 index 000000000..88e50f99f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgresultlayer.cpp @@ -0,0 +1,437 @@ +/****************************************************************************** + * $Id: ogrpgresultlayer.cpp 28481 2015-02-13 17:11:15Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGResultLayer class, access the resultset from + * a particular select query done via ExecuteSQL(). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_pg.h" + +CPL_CVSID("$Id: ogrpgresultlayer.cpp 28481 2015-02-13 17:11:15Z rouault $"); + +#define PQexec this_is_an_error + +/************************************************************************/ +/* OGRPGResultLayer() */ +/************************************************************************/ + +OGRPGResultLayer::OGRPGResultLayer( OGRPGDataSource *poDSIn, + const char * pszRawQueryIn, + PGresult *hInitialResultIn ) +{ + poDS = poDSIn; + + iNextShapeId = 0; + + pszRawStatement = CPLStrdup(pszRawQueryIn); + + osWHERE = ""; + + BuildFullQueryStatement(); + + ReadResultDefinition(hInitialResultIn); + + pszGeomTableName = NULL; + pszGeomTableSchemaName = NULL; + + /* Find at which index the geometry column is */ + /* and prepare a request to identify not-nullable fields */ + int iGeomCol = -1; + CPLString osRequest; + std::map< std::pair, int> oMapAttributeToFieldIndex; + + int iRawField; + for( iRawField = 0; iRawField < PQnfields(hInitialResultIn); iRawField++ ) + { + if( poFeatureDefn->GetGeomFieldCount() == 1 && + strcmp(PQfname(hInitialResultIn,iRawField), + poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef()) == 0 ) + { + iGeomCol = iRawField; + } + + Oid tableOID = PQftable(hInitialResultIn, iRawField); + int tableCol = PQftablecol(hInitialResultIn, iRawField); + if( tableOID != InvalidOid && tableCol > 0 ) + { + if( osRequest.size() ) + osRequest += " OR "; + osRequest += "(attrelid = "; + osRequest += CPLSPrintf("%d", tableOID); + osRequest += " AND attnum = "; + osRequest += CPLSPrintf("%d)", tableCol); + oMapAttributeToFieldIndex[std::pair(tableOID,tableCol)] = iRawField; + } + } + + if( osRequest.size() ) + { + osRequest = "SELECT attnum, attrelid FROM pg_attribute WHERE attnotnull = 't' AND (" + osRequest + ")"; + PGresult* hResult = OGRPG_PQexec(poDS->GetPGConn(), osRequest ); + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK) + { + int iCol; + for( iCol = 0; iCol < PQntuples(hResult); iCol++ ) + { + const char* pszAttNum = PQgetvalue(hResult,iCol,0); + const char* pszAttRelid = PQgetvalue(hResult,iCol,1); + int iRawField = oMapAttributeToFieldIndex[std::pair(atoi(pszAttRelid),atoi(pszAttNum))]; + const char* pszFieldname = PQfname(hInitialResultIn,iRawField); + int iFieldIdx = poFeatureDefn->GetFieldIndex(pszFieldname); + if( iFieldIdx >= 0 ) + poFeatureDefn->GetFieldDefn(iFieldIdx)->SetNullable(FALSE); + else + { + iFieldIdx = poFeatureDefn->GetGeomFieldIndex(pszFieldname); + if( iFieldIdx >= 0 ) + poFeatureDefn->GetGeomFieldDefn(iFieldIdx)->SetNullable(FALSE); + } + } + } + OGRPGClearResult( hResult ); + } + +#ifndef PG_PRE74 + /* Determine the table from which the geometry column is extracted */ + if (iGeomCol != -1) + { + Oid tableOID = PQftable(hInitialResultIn, iGeomCol); + if (tableOID != InvalidOid) + { + CPLString osGetTableName; + osGetTableName.Printf("SELECT c.relname, n.nspname FROM pg_class c " + "JOIN pg_namespace n ON c.relnamespace=n.oid WHERE c.oid = %d ", tableOID); + PGresult* hTableNameResult = OGRPG_PQexec(poDS->GetPGConn(), osGetTableName ); + if( hTableNameResult && PQresultStatus(hTableNameResult) == PGRES_TUPLES_OK) + { + if ( PQntuples(hTableNameResult) > 0 ) + { + pszGeomTableName = CPLStrdup(PQgetvalue(hTableNameResult,0,0)); + pszGeomTableSchemaName = CPLStrdup(PQgetvalue(hTableNameResult,0,1)); + } + } + OGRPGClearResult( hTableNameResult ); + } + } +#endif +} + +/************************************************************************/ +/* ~OGRPGResultLayer() */ +/************************************************************************/ + +OGRPGResultLayer::~OGRPGResultLayer() + +{ + CPLFree( pszRawStatement ); + CPLFree( pszGeomTableName ); + CPLFree( pszGeomTableSchemaName ); +} + + +/************************************************************************/ +/* BuildFullQueryStatement() */ +/************************************************************************/ + +void OGRPGResultLayer::BuildFullQueryStatement() + +{ + if( pszQueryStatement != NULL ) + { + CPLFree( pszQueryStatement ); + pszQueryStatement = NULL; + } + + pszQueryStatement = (char*) CPLMalloc(strlen(pszRawStatement) + strlen(osWHERE) + 40); + + if (strlen(osWHERE) == 0) + strcpy(pszQueryStatement, pszRawStatement); + else + sprintf(pszQueryStatement, "SELECT * FROM (%s) AS ogrpgsubquery %s", + pszRawStatement, osWHERE.c_str()); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRPGResultLayer::ResetReading() + +{ + OGRPGLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRPGResultLayer::GetFeatureCount( int bForce ) + +{ + if( TestCapability(OLCFastFeatureCount) == FALSE ) + return OGRPGLayer::GetFeatureCount( bForce ); + + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult = NULL; + CPLString osCommand; + int nCount = 0; + + osCommand.Printf( + "SELECT count(*) FROM (%s) AS ogrpgcount", + pszQueryStatement ); + + hResult = OGRPG_PQexec(hPGConn, osCommand); + if( hResult != NULL && PQresultStatus(hResult) == PGRES_TUPLES_OK ) + nCount = atoi(PQgetvalue(hResult,0,0)); + else + CPLDebug( "PG", "%s; failed.", osCommand.c_str() ); + OGRPGClearResult( hResult ); + + return nCount; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPGResultLayer::TestCapability( const char * pszCap ) + +{ + GetLayerDefn(); + + if( EQUAL(pszCap,OLCFastFeatureCount) || + EQUAL(pszCap,OLCFastSetNextByIndex) ) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = NULL; + if( poFeatureDefn->GetGeomFieldCount() > 0 ) + poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(m_iGeomFieldFilter); + return (m_poFilterGeom == NULL || + poGeomFieldDefn == NULL || + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY || + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ) + && m_poAttrQuery == NULL; + } + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = NULL; + if( poFeatureDefn->GetGeomFieldCount() > 0 ) + poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(m_iGeomFieldFilter); + return (poGeomFieldDefn == NULL || + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY || + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ) + && m_poAttrQuery == NULL; + } + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = NULL; + if( poFeatureDefn->GetGeomFieldCount() > 0 ) + poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(0); + return (poGeomFieldDefn == NULL || + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ) + && m_poAttrQuery == NULL; + } + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + + else + return FALSE; +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRPGResultLayer::GetNextFeature() + +{ + OGRPGGeomFieldDefn* poGeomFieldDefn = NULL; + if( poFeatureDefn->GetGeomFieldCount() != 0 ) + poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(m_iGeomFieldFilter); + + for( ; TRUE; ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || poGeomFieldDefn == NULL + || poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY + || poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY + || FilterGeometry( poFeature->GetGeomFieldRef(m_iGeomFieldFilter) ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRPGResultLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeomIn ) + +{ + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() || + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone ) + { + if( iGeomField != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } + return; + } + m_iGeomFieldFilter = iGeomField; + + OGRPGGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(m_iGeomFieldFilter); + if( InstallFilter( poGeomIn ) ) + { + if ( poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY || + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ) + { + if( m_poFilterGeom != NULL) + { + char szBox3D_1[128]; + char szBox3D_2[128]; + OGREnvelope sEnvelope; + + m_poFilterGeom->getEnvelope( &sEnvelope ); + if( poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ) + { + if( sEnvelope.MinX < -180.0 ) + sEnvelope.MinX = -180.0; + if( sEnvelope.MinY < -90.0 ) + sEnvelope.MinY = -90.0; + if( sEnvelope.MaxX > 180.0 ) + sEnvelope.MaxX = 180.0; + if( sEnvelope.MaxY > 90.0 ) + sEnvelope.MaxY = 90.0; + } + CPLsnprintf(szBox3D_1, sizeof(szBox3D_1), "%.18g %.18g", sEnvelope.MinX, sEnvelope.MinY); + CPLsnprintf(szBox3D_2, sizeof(szBox3D_2), "%.18g %.18g", sEnvelope.MaxX, sEnvelope.MaxY); + osWHERE.Printf("WHERE %s && %s('BOX3D(%s, %s)'::box3d,%d) ", + OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(), + (poDS->sPostGISVersion.nMajor >= 2) ? "ST_SetSRID" : "SetSRID", + szBox3D_1, szBox3D_2, poGeomFieldDefn->nSRSId ); + } + else + { + osWHERE = ""; + } + + BuildFullQueryStatement(); + } + + ResetReading(); + } + +} + +/************************************************************************/ +/* ResolveSRID() */ +/************************************************************************/ + +void OGRPGResultLayer::ResolveSRID(OGRPGGeomFieldDefn* poGFldDefn) + +{ + /* We have to get the SRID of the geometry column, so to be able */ + /* to do spatial filtering */ + int nSRSId = UNDETERMINED_SRID; + if( poGFldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ) + { + if (pszGeomTableName != NULL) + { + CPLString osName(pszGeomTableSchemaName); + osName += "."; + osName += pszGeomTableName; + OGRPGLayer* poBaseLayer = (OGRPGLayer*) poDS->GetLayerByName(osName); + if (poBaseLayer) + { + int iBaseIdx = poBaseLayer->GetLayerDefn()-> + GetGeomFieldIndex( poGFldDefn->GetNameRef() ); + if( iBaseIdx >= 0 ) + { + OGRPGGeomFieldDefn* poBaseGFldDefn = + poBaseLayer->myGetLayerDefn()->myGetGeomFieldDefn(iBaseIdx); + poBaseGFldDefn->GetSpatialRef(); /* To make sure nSRSId is resolved */ + nSRSId = poBaseGFldDefn->nSRSId; + } + } + } + + if( nSRSId == UNDETERMINED_SRID ) + { + CPLString osGetSRID; + + const char* psGetSRIDFct; + if (poDS->sPostGISVersion.nMajor >= 2) + psGetSRIDFct = "ST_SRID"; + else + psGetSRIDFct = "getsrid"; + + osGetSRID += "SELECT "; + osGetSRID += psGetSRIDFct; + osGetSRID += "("; + osGetSRID += OGRPGEscapeColumnName(poGFldDefn->GetNameRef()); + osGetSRID += ") FROM("; + osGetSRID += pszRawStatement; + osGetSRID += ") AS ogrpggetsrid LIMIT 1"; + + PGresult* hSRSIdResult = OGRPG_PQexec(poDS->GetPGConn(), osGetSRID ); + + nSRSId = poDS->GetUndefinedSRID(); + + if( hSRSIdResult && PQresultStatus(hSRSIdResult) == PGRES_TUPLES_OK) + { + if ( PQntuples(hSRSIdResult) > 0 ) + nSRSId = atoi(PQgetvalue(hSRSIdResult, 0, 0)); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage(poDS->GetPGConn()) ); + } + + OGRPGClearResult(hSRSIdResult); + } + } + else if( poGFldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ) + { + nSRSId = 4326; + } + poGFldDefn->nSRSId = nSRSId; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgtablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgtablelayer.cpp new file mode 100644 index 000000000..558f39a73 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgtablelayer.cpp @@ -0,0 +1,3086 @@ +/****************************************************************************** + * $Id: ogrpgtablelayer.cpp 29330 2015-06-14 12:11:11Z rouault $ + + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGTableLayer class, access to an existing table. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_pg.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_error.h" +#include "ogr_p.h" + +#define PQexec this_is_an_error + +CPL_CVSID("$Id: ogrpgtablelayer.cpp 29330 2015-06-14 12:11:11Z rouault $"); + + +#define USE_COPY_UNSET -10 + +#define UNSUPPORTED_OP_READ_ONLY "%s : unsupported operation on a read-only datasource." + +/************************************************************************/ +/* OGRPGTableFeatureDefn */ +/************************************************************************/ + +class OGRPGTableFeatureDefn : public OGRPGFeatureDefn +{ + private: + OGRPGTableLayer *poLayer; + + void SolveFields(); + + public: + OGRPGTableFeatureDefn( OGRPGTableLayer* poLayerIn, + const char * pszName = NULL ) : + OGRPGFeatureDefn(pszName), poLayer(poLayerIn) + { + } + + virtual void UnsetLayer() + { + poLayer = NULL; + OGRPGFeatureDefn::UnsetLayer(); + } + + virtual int GetFieldCount() + { SolveFields(); return OGRPGFeatureDefn::GetFieldCount(); } + virtual OGRFieldDefn *GetFieldDefn( int i ) + { SolveFields(); return OGRPGFeatureDefn::GetFieldDefn(i); } + virtual int GetFieldIndex( const char * pszName ) + { SolveFields(); return OGRPGFeatureDefn::GetFieldIndex(pszName); } + + virtual int GetGeomFieldCount() + { if (poLayer != NULL && !poLayer->HasGeometryInformation()) + SolveFields(); + return OGRPGFeatureDefn::GetGeomFieldCount(); } + virtual OGRGeomFieldDefn *GetGeomFieldDefn( int i ) + { if (poLayer != NULL && !poLayer->HasGeometryInformation()) + SolveFields(); + return OGRPGFeatureDefn::GetGeomFieldDefn(i); } + virtual int GetGeomFieldIndex( const char * pszName) + { if (poLayer != NULL && !poLayer->HasGeometryInformation()) + SolveFields(); + return OGRPGFeatureDefn::GetGeomFieldIndex(pszName); } + +}; + +/************************************************************************/ +/* SolveFields() */ +/************************************************************************/ + +void OGRPGTableFeatureDefn::SolveFields() +{ + if( poLayer == NULL ) + return; + + poLayer->ReadTableDefinition(); +} + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char *OGRPGTableLayer::GetFIDColumn() + +{ + ReadTableDefinition(); + + if( pszFIDColumn != NULL ) + return pszFIDColumn; + else + return ""; +} + +/************************************************************************/ +/* OGRPGTableLayer() */ +/************************************************************************/ + +OGRPGTableLayer::OGRPGTableLayer( OGRPGDataSource *poDSIn, + CPLString& osCurrentSchema, + const char * pszTableNameIn, + const char * pszSchemaNameIn, + const char * pszGeomColForced, + int bUpdate ) + +{ + poDS = poDSIn; + + pszQueryStatement = NULL; + + bUpdateAccess = bUpdate; + + bGeometryInformationSet = FALSE; + + bLaunderColumnNames = TRUE; + bPreservePrecision = TRUE; + bCopyActive = FALSE; + bUseCopy = USE_COPY_UNSET; // unknown + bUseCopyByDefault = FALSE; + bFIDColumnInCopyFields = FALSE; + bFirstInsertion = TRUE; + + pszTableName = CPLStrdup( pszTableNameIn ); + if (pszSchemaNameIn) + pszSchemaName = CPLStrdup( pszSchemaNameIn ); + else + pszSchemaName = CPLStrdup( osCurrentSchema ); + this->pszGeomColForced = + pszGeomColForced ? CPLStrdup(pszGeomColForced) : NULL; + + pszSqlGeomParentTableName = NULL; + bTableDefinitionValid = -1; + + bHasWarnedIncompatibleGeom = FALSE; + bHasWarnedAlreadySetFID = FALSE; + + /* Just in provision for people yelling about broken backward compatibility ... */ + bRetrieveFID = CSLTestBoolean(CPLGetConfigOption("OGR_PG_RETRIEVE_FID", "TRUE")); + +/* -------------------------------------------------------------------- */ +/* Build the layer defn name. */ +/* -------------------------------------------------------------------- */ + CPLString osDefnName; + if ( pszSchemaNameIn && osCurrentSchema != pszSchemaNameIn ) + { + osDefnName.Printf("%s.%s", pszSchemaNameIn, pszTableName ); + pszSqlTableName = CPLStrdup(CPLString().Printf("%s.%s", + OGRPGEscapeColumnName(pszSchemaNameIn).c_str(), + OGRPGEscapeColumnName(pszTableName).c_str() )); + } + else + { + //no prefix for current_schema in layer name, for backwards compatibility + osDefnName = pszTableName; + pszSqlTableName = CPLStrdup(OGRPGEscapeColumnName(pszTableName)); + } + if( pszGeomColForced != NULL ) + { + osDefnName += "("; + osDefnName += pszGeomColForced; + osDefnName += ")"; + } + + osPrimaryKey = CPLGetConfigOption( "PGSQL_OGR_FID", "ogc_fid" ); + + papszOverrideColumnTypes = NULL; + nForcedSRSId = UNDETERMINED_SRID; + nForcedDimension = -1; + bCreateSpatialIndexFlag = TRUE; + bInResetReading = FALSE; + + poFeatureDefn = new OGRPGTableFeatureDefn( this, osDefnName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + bAutoFIDOnCreateViaCopy = FALSE; + + bDifferedCreation = FALSE; + iFIDAsRegularColumnIndex = -1; +} + +//************************************************************************/ +/* ~OGRPGTableLayer() */ +/************************************************************************/ + +OGRPGTableLayer::~OGRPGTableLayer() + +{ + if( bDifferedCreation ) RunDifferedCreationIfNecessary(); + if ( bCopyActive ) EndCopy(); + CPLFree( pszSqlTableName ); + CPLFree( pszTableName ); + CPLFree( pszSqlGeomParentTableName ); + CPLFree( pszSchemaName ); + CPLFree( pszGeomColForced ); + CSLDestroy( papszOverrideColumnTypes ); +} + +/************************************************************************/ +/* SetGeometryInformation() */ +/************************************************************************/ + +void OGRPGTableLayer::SetGeometryInformation(PGGeomColumnDesc* pasDesc, + int nGeomFieldCount) +{ + /* flag must be set before instanciating geometry fields */ + bGeometryInformationSet = TRUE; + + for(int i=0; iSetNullable(pasDesc[i].bNullable); + poGeomFieldDefn->nSRSId = pasDesc[i].nSRID; + poGeomFieldDefn->nCoordDimension = pasDesc[i].nCoordDimension; + poGeomFieldDefn->ePostgisType = pasDesc[i].ePostgisType; + if( pasDesc[i].pszGeomType != NULL ) + { + OGRwkbGeometryType eGeomType = OGRFromOGCGeomType(pasDesc[i].pszGeomType); + if( poGeomFieldDefn->nCoordDimension == 3 && eGeomType != wkbUnknown ) + eGeomType = wkbSetZ(eGeomType); + poGeomFieldDefn->SetType(eGeomType); + } + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + } +} + + +/************************************************************************/ +/* ReadTableDefinition() */ +/* */ +/* Build a schema from the named table. Done by querying the */ +/* catalog. */ +/************************************************************************/ + +int OGRPGTableLayer::ReadTableDefinition() + +{ + PGresult *hResult; + CPLString osCommand; + PGconn *hPGConn = poDS->GetPGConn(); + + if( bTableDefinitionValid >= 0 ) + return bTableDefinitionValid; + bTableDefinitionValid = FALSE; + + CPLString osSchemaClause; + osSchemaClause.Printf("AND n.nspname=%s", + OGRPGEscapeString(hPGConn, pszSchemaName).c_str()); + + const char* pszTypnameEqualsAnyClause; + if (poDS->sPostgreSQLVersion.nMajor == 7 && poDS->sPostgreSQLVersion.nMinor <= 3) + pszTypnameEqualsAnyClause = "ANY(SELECT '{int2, int4, int8, serial, bigserial}')"; + else + pszTypnameEqualsAnyClause = "ANY(ARRAY['int2','int4','int8','serial','bigserial'])"; + + const char* pszAttnumEqualAnyIndkey; + if( poDS->sPostgreSQLVersion.nMajor > 8 || ( + poDS->sPostgreSQLVersion.nMajor == 8 && poDS->sPostgreSQLVersion.nMinor >= 2) ) + pszAttnumEqualAnyIndkey = "a.attnum = ANY(i.indkey)"; + else + pszAttnumEqualAnyIndkey = "(i.indkey[0]=a.attnum OR i.indkey[1]=a.attnum OR i.indkey[2]=a.attnum " + "OR i.indkey[3]=a.attnum OR i.indkey[4]=a.attnum OR i.indkey[5]=a.attnum " + "OR i.indkey[6]=a.attnum OR i.indkey[7]=a.attnum OR i.indkey[8]=a.attnum " + "OR i.indkey[9]=a.attnum)"; + + CPLString osEscapedTableNameSingleQuote = OGRPGEscapeString(hPGConn, pszTableName); + const char* pszEscapedTableNameSingleQuote = osEscapedTableNameSingleQuote.c_str(); + + /* See #1889 for why we don't use 'AND a.attnum = ANY(i.indkey)' */ + osCommand.Printf("SELECT a.attname, a.attnum, t.typname, " + "t.typname = %s AS isfid " + "FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n, pg_index i " + "WHERE a.attnum > 0 AND a.attrelid = c.oid " + "AND a.atttypid = t.oid AND c.relnamespace = n.oid " + "AND c.oid = i.indrelid AND i.indisprimary = 't' " + "AND t.typname !~ '^geom' AND c.relname = %s " + "AND %s %s ORDER BY a.attnum", + pszTypnameEqualsAnyClause, pszEscapedTableNameSingleQuote, + pszAttnumEqualAnyIndkey, osSchemaClause.c_str() ); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() ); + + if ( hResult && PGRES_TUPLES_OK == PQresultStatus(hResult) ) + { + if ( PQntuples( hResult ) == 1 && PQgetisnull( hResult,0,0 ) == false ) + { + /* Check if single-field PK can be represented as integer. */ + CPLString osValue(PQgetvalue(hResult, 0, 3)); + if( osValue == "t" ) + { + osPrimaryKey.Printf( "%s", PQgetvalue(hResult,0,0) ); + const char* pszFIDType = PQgetvalue(hResult, 0, 2); + CPLDebug( "PG", "Primary key name (FID): %s, type : %s", + osPrimaryKey.c_str(), pszFIDType ); + if( EQUAL(pszFIDType, "int8") ) + SetMetadataItem(OLMD_FID64, "YES"); + } + } + else if ( PQntuples( hResult ) > 1 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Multi-column primary key in \'%s\' detected but not supported.", + pszTableName ); + } + + OGRPGClearResult( hResult ); + /* Zero tuples means no PK is defined, perfectly valid case. */ + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage(hPGConn) ); + } + +/* -------------------------------------------------------------------- */ +/* Fire off commands to get back the columns of the table. */ +/* -------------------------------------------------------------------- */ + osCommand.Printf( + "SELECT DISTINCT a.attname, t.typname, a.attlen," + " format_type(a.atttypid,a.atttypmod), a.attnum, a.attnotnull, a.atthasdef " + "FROM pg_class c, pg_attribute a, pg_type t, pg_namespace n " + "WHERE c.relname = %s " + "AND a.attnum > 0 AND a.attrelid = c.oid " + "AND a.atttypid = t.oid " + "AND c.relnamespace=n.oid " + "%s " + "ORDER BY a.attnum", + pszEscapedTableNameSingleQuote, osSchemaClause.c_str()); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() ); + + if( !hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK ) + { + OGRPGClearResult( hResult ); + + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage(hPGConn) ); + return bTableDefinitionValid; + } + + if( PQntuples(hResult) == 0 ) + { + OGRPGClearResult( hResult ); + + CPLDebug( "PG", + "No field definitions found for '%s', is it a table?", + pszTableName ); + return bTableDefinitionValid; + } + +/* -------------------------------------------------------------------- */ +/* Parse the returned table information. */ +/* -------------------------------------------------------------------- */ + int iRecord; + int bHasDefault = FALSE; + for( iRecord = 0; iRecord < PQntuples(hResult); iRecord++ ) + { + const char *pszType = NULL; + const char *pszFormatType = NULL; + const char *pszNotNull = NULL; + const char *pszHasDef = NULL; + OGRFieldDefn oField( PQgetvalue( hResult, iRecord, 0 ), OFTString); + + pszType = PQgetvalue(hResult, iRecord, 1 ); + int nWidth = atoi(PQgetvalue(hResult,iRecord,2)); + pszFormatType = PQgetvalue(hResult,iRecord,3); + pszNotNull = PQgetvalue(hResult,iRecord,5); + pszHasDef = PQgetvalue(hResult,iRecord,6); + + if( pszNotNull && EQUAL(pszNotNull, "t") ) + oField.SetNullable(FALSE); + if( pszHasDef && EQUAL(pszHasDef, "t") ) + bHasDefault = TRUE; + + if( EQUAL(oField.GetNameRef(),osPrimaryKey) ) + { + pszFIDColumn = CPLStrdup(oField.GetNameRef()); + CPLDebug("PG","Using column '%s' as FID for table '%s'", pszFIDColumn, pszTableName ); + continue; + } + else if( EQUAL(pszType,"geometry") || + EQUAL(pszType,"geography") || + EQUAL(oField.GetNameRef(),"WKB_GEOMETRY") ) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = NULL; + if( !bGeometryInformationSet ) + { + if( pszGeomColForced == NULL || + EQUAL(pszGeomColForced, oField.GetNameRef()) ) + poGeomFieldDefn = new OGRPGGeomFieldDefn(this, oField.GetNameRef()); + } + else + { + int idx = poFeatureDefn->GetGeomFieldIndex(oField.GetNameRef()); + if( idx >= 0 ) + poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(idx); + } + if( poGeomFieldDefn != NULL ) + { + if( EQUAL(pszType,"geometry") ) + poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOMETRY; + else if( EQUAL(pszType,"geography") ) + { + poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOGRAPHY; + poGeomFieldDefn->nSRSId = 4326; + } + else + { + poGeomFieldDefn->ePostgisType = GEOM_TYPE_WKB; + if( EQUAL(pszType,"OID") ) + bWkbAsOid = TRUE; + } + poGeomFieldDefn->SetNullable(oField.IsNullable()); + if( !bGeometryInformationSet ) + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + } + continue; + } + + OGRPGCommonLayerSetType(oField, pszType, pszFormatType, nWidth); + + //CPLDebug("PG", "name=%s, type=%s", oField.GetNameRef(), pszType); + poFeatureDefn->AddFieldDefn( &oField ); + } + + OGRPGClearResult( hResult ); + + if( bHasDefault ) + { + osCommand.Printf( + "SELECT a.attname, pg_get_expr(def.adbin, c.oid) " + "FROM pg_attrdef def, pg_class c, pg_attribute a, pg_type t, pg_namespace n " + "WHERE c.relname = %s AND a.attnum > 0 AND a.attrelid = c.oid " + "AND a.atttypid = t.oid AND c.relnamespace=n.oid AND " + "def.adrelid = c.oid AND def.adnum = a.attnum " + "%s " + "ORDER BY a.attnum", + pszEscapedTableNameSingleQuote, osSchemaClause.c_str()); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() ); + if( !hResult || PQresultStatus(hResult) != PGRES_TUPLES_OK ) + { + OGRPGClearResult( hResult ); + + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage(hPGConn) ); + return bTableDefinitionValid; + } + + for( iRecord = 0; iRecord < PQntuples(hResult); iRecord++ ) + { + const char *pszName = PQgetvalue( hResult, iRecord, 0 ); + const char *pszDefault = PQgetvalue( hResult, iRecord, 1 ); + int nIdx = poFeatureDefn->GetFieldIndex(pszName); + if( nIdx >= 0 ) + { + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(nIdx); + OGRPGCommonLayerNormalizeDefault(poFieldDefn, pszDefault); + } + } + + OGRPGClearResult( hResult ); + } + + bTableDefinitionValid = TRUE; + + ResetReading(); + + /* If geometry type, SRID, etc... have always been set by SetGeometryInformation() */ + /* no need to issue a new SQL query. Just record the geom type in the layer definition */ + if (bGeometryInformationSet) + { + return TRUE; + } + bGeometryInformationSet = TRUE; + + // get layer geometry type (for PostGIS dataset) + for(int iField = 0; iField < poFeatureDefn->GetGeomFieldCount(); iField++) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(iField); + + /* Get the geometry type and dimensions from the table, or */ + /* from its parents if it is a derived table, or from the parent of the parent, etc.. */ + int bGoOn = TRUE; + int bHasPostGISGeometry = + (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY); + + while(bGoOn) + { + CPLString osEscapedTableNameSingleQuote = OGRPGEscapeString(hPGConn, + (pszSqlGeomParentTableName) ? pszSqlGeomParentTableName : pszTableName); + const char* pszEscapedTableNameSingleQuote = osEscapedTableNameSingleQuote.c_str(); + + osCommand.Printf( + "SELECT type, coord_dimension, srid FROM %s WHERE f_table_name = %s", + (bHasPostGISGeometry) ? "geometry_columns" : "geography_columns", + pszEscapedTableNameSingleQuote); + + osCommand += CPLString().Printf(" AND %s=%s", + (bHasPostGISGeometry) ? "f_geometry_column" : "f_geography_column", + OGRPGEscapeString(hPGConn,poGeomFieldDefn->GetNameRef()).c_str()); + + osCommand += CPLString().Printf(" AND f_table_schema = %s", + OGRPGEscapeString(hPGConn,pszSchemaName).c_str()); + + hResult = OGRPG_PQexec(hPGConn,osCommand); + + if ( hResult && PQntuples(hResult) == 1 && !PQgetisnull(hResult,0,0) ) + { + const char* pszType = PQgetvalue(hResult,0,0); + + int nCoordDimension = atoi(PQgetvalue(hResult,0,1)); + + int nSRSId = atoi(PQgetvalue(hResult,0,2)); + + poGeomFieldDefn->nCoordDimension = nCoordDimension; + if( nSRSId > 0 ) + poGeomFieldDefn->nSRSId = nSRSId; + OGRwkbGeometryType eGeomType = OGRFromOGCGeomType(pszType); + if( poGeomFieldDefn->nCoordDimension == 3 && eGeomType != wkbUnknown ) + eGeomType = wkbSetZ(eGeomType); + poGeomFieldDefn->SetType(eGeomType); + + bGoOn = FALSE; + } + else + { + /* Fetch the name of the parent table */ + osCommand.Printf("SELECT pg_class.relname FROM pg_class WHERE oid = " + "(SELECT pg_inherits.inhparent FROM pg_inherits WHERE inhrelid = " + "(SELECT c.oid FROM pg_class c, pg_namespace n WHERE c.relname = %s AND c.relnamespace=n.oid AND n.nspname = %s))", + pszEscapedTableNameSingleQuote, + OGRPGEscapeString(hPGConn, pszSchemaName).c_str() ); + + OGRPGClearResult( hResult ); + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() ); + + if ( hResult && PQntuples( hResult ) == 1 && !PQgetisnull( hResult,0,0 ) ) + { + CPLFree(pszSqlGeomParentTableName); + pszSqlGeomParentTableName = CPLStrdup( PQgetvalue(hResult,0,0) ); + } + else + { + /* No more parent : stop recursion */ + bGoOn = FALSE; + } + } + + OGRPGClearResult( hResult ); + } + } + + return bTableDefinitionValid; +} + +/************************************************************************/ +/* SetTableDefinition() */ +/************************************************************************/ + +void OGRPGTableLayer::SetTableDefinition(const char* pszFIDColumnName, + const char* pszGFldName, + OGRwkbGeometryType eType, + const char* pszGeomType, + int nSRSId, + int nCoordDimension) +{ + bTableDefinitionValid = TRUE; + bGeometryInformationSet = TRUE; + if( pszFIDColumnName[0] == '"' && + pszFIDColumnName[strlen(pszFIDColumnName)-1] == '"') + { + pszFIDColumn = CPLStrdup(pszFIDColumnName + 1); + pszFIDColumn[strlen(pszFIDColumn)-1] = '\0'; + } + else + pszFIDColumn = CPLStrdup(pszFIDColumnName); + poFeatureDefn->SetGeomType(wkbNone); + if( eType != wkbNone ) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = new OGRPGGeomFieldDefn(this, pszGFldName); + poGeomFieldDefn->SetType(eType); + poGeomFieldDefn->nCoordDimension = nCoordDimension; + + if( EQUAL(pszGeomType,"geometry") ) + { + poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOMETRY; + poGeomFieldDefn->nSRSId = nSRSId; + } + else if( EQUAL(pszGeomType,"geography") ) + { + poGeomFieldDefn->ePostgisType = GEOM_TYPE_GEOGRAPHY; + poGeomFieldDefn->nSRSId = 4326; + } + else + { + poGeomFieldDefn->ePostgisType = GEOM_TYPE_WKB; + if( EQUAL(pszGeomType,"OID") ) + bWkbAsOid = TRUE; + } + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + } +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRPGTableLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeomIn ) + +{ + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() || + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone ) + { + if( iGeomField != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } + return; + } + m_iGeomFieldFilter = iGeomField; + + if( InstallFilter( poGeomIn ) ) + { + BuildWhere(); + + ResetReading(); + } +} + +/************************************************************************/ +/* BuildWhere() */ +/* */ +/* Build the WHERE statement appropriate to the current set of */ +/* criteria (spatial and attribute queries). */ +/************************************************************************/ + +void OGRPGTableLayer::BuildWhere() + +{ + osWHERE = ""; + OGRPGGeomFieldDefn* poGeomFieldDefn = NULL; + if( poFeatureDefn->GetGeomFieldCount() != 0 ) + poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(m_iGeomFieldFilter); + + if( m_poFilterGeom != NULL && poGeomFieldDefn != NULL && + poDS->sPostGISVersion.nMajor >= 0 && ( + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY || + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY) ) + { + char szBox3D_1[128]; + char szBox3D_2[128]; + OGREnvelope sEnvelope; + + m_poFilterGeom->getEnvelope( &sEnvelope ); + if( poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ) + { + if( sEnvelope.MinX < -180.0 ) + sEnvelope.MinX = -180.0; + if( sEnvelope.MinY < -90.0 ) + sEnvelope.MinY = -90.0; + if( sEnvelope.MaxX > 180.0 ) + sEnvelope.MaxX = 180.0; + if( sEnvelope.MaxY > 90.0 ) + sEnvelope.MaxY = 90.0; + } + CPLsnprintf(szBox3D_1, sizeof(szBox3D_1), "%.18g %.18g", sEnvelope.MinX, sEnvelope.MinY); + CPLsnprintf(szBox3D_2, sizeof(szBox3D_2), "%.18g %.18g", sEnvelope.MaxX, sEnvelope.MaxY); + osWHERE.Printf("WHERE %s && %s('BOX3D(%s, %s)'::box3d,%d) ", + OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()).c_str(), + (poDS->sPostGISVersion.nMajor >= 2) ? "ST_SetSRID" : "SetSRID", + szBox3D_1, szBox3D_2, poGeomFieldDefn->nSRSId ); + } + + if( strlen(osQuery) > 0 ) + { + if( strlen(osWHERE) == 0 ) + { + osWHERE.Printf( "WHERE %s ", osQuery.c_str() ); + } + else + { + osWHERE += "AND ("; + osWHERE += osQuery; + osWHERE += ")"; + } + } +} + +/************************************************************************/ +/* BuildFullQueryStatement() */ +/************************************************************************/ + +void OGRPGTableLayer::BuildFullQueryStatement() + +{ + CPLString osFields = BuildFields(); + if( pszQueryStatement != NULL ) + { + CPLFree( pszQueryStatement ); + pszQueryStatement = NULL; + } + pszQueryStatement = (char *) + CPLMalloc(strlen(osFields)+strlen(osWHERE) + +strlen(pszSqlTableName) + 40); + sprintf( pszQueryStatement, + "SELECT %s FROM %s %s", + osFields.c_str(), pszSqlTableName, osWHERE.c_str() ); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRPGTableLayer::ResetReading() + +{ + if( bInResetReading ) + return; + bInResetReading = TRUE; + + if( bDifferedCreation ) RunDifferedCreationIfNecessary(); + poDS->EndCopy(); + bUseCopyByDefault = FALSE; + + BuildFullQueryStatement(); + + OGRPGLayer::ResetReading(); + + bInResetReading = FALSE; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRPGTableLayer::GetNextFeature() + +{ + if( bDifferedCreation && RunDifferedCreationIfNecessary() != OGRERR_NONE ) + return NULL; + poDS->EndCopy(); + + if( pszQueryStatement == NULL ) + ResetReading(); + + if( pszQueryStatement == NULL ) + ResetReading(); + + OGRPGGeomFieldDefn* poGeomFieldDefn = NULL; + if( poFeatureDefn->GetGeomFieldCount() != 0 ) + poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(m_iGeomFieldFilter); + poFeatureDefn->GetFieldCount(); + + for( ; TRUE; ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + /* We just have to look if there is a geometry filter */ + /* If there's a PostGIS geometry column, the spatial filter */ + /* is already taken into account in the select request */ + /* The attribute filter is always taken into account by the select request */ + if( m_poFilterGeom == NULL + || poGeomFieldDefn == NULL + || poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY + || poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY + || FilterGeometry( poFeature->GetGeomFieldRef(m_iGeomFieldFilter) ) ) + { + if( poFeature && iFIDAsRegularColumnIndex >= 0 ) + { + poFeature->SetField(iFIDAsRegularColumnIndex, poFeature->GetFID()); + } + return poFeature; + } + + delete poFeature; + } +} + +/************************************************************************/ +/* BuildFields() */ +/* */ +/* Build list of fields to fetch, performing any required */ +/* transformations (such as on geometry). */ +/************************************************************************/ + +CPLString OGRPGTableLayer::BuildFields() + +{ + int i = 0; + CPLString osFieldList; + + poFeatureDefn->GetFieldCount(); + + if( pszFIDColumn != NULL && poFeatureDefn->GetFieldIndex( pszFIDColumn ) == -1 ) + { + osFieldList += OGRPGEscapeColumnName(pszFIDColumn); + } + + for( i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(i); + CPLString osEscapedGeom = + OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()); + + if( osFieldList.size() > 0 ) + osFieldList += ", "; + + if( poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ) + { + if ( poDS->sPostGISVersion.nMajor < 0 || poDS->bUseBinaryCursor ) + { + osFieldList += osEscapedGeom; + } + else if (CSLTestBoolean(CPLGetConfigOption("PG_USE_BASE64", "NO")) && + poGeomFieldDefn->nCoordDimension != 4 /* we don't know how to decode 4-dim EWKB for now */) + { + if (poDS->sPostGISVersion.nMajor >= 2) + osFieldList += "encode(ST_AsEWKB("; + else + osFieldList += "encode(AsEWKB("; + osFieldList += osEscapedGeom; + osFieldList += "), 'base64') AS "; + osFieldList += OGRPGEscapeColumnName( + CPLSPrintf("EWKBBase64_%s", poGeomFieldDefn->GetNameRef())); + } + else if ( !CSLTestBoolean(CPLGetConfigOption("PG_USE_TEXT", "NO")) && + poGeomFieldDefn->nCoordDimension != 4 && /* we don't know how to decode 4-dim EWKB for now */ + /* perhaps works also for older version, but I didn't check */ + (poDS->sPostGISVersion.nMajor > 1 || + (poDS->sPostGISVersion.nMajor == 1 && poDS->sPostGISVersion.nMinor >= 1)) ) + { + /* This will return EWKB in an hex encoded form */ + osFieldList += osEscapedGeom; + } + else if ( poDS->sPostGISVersion.nMajor >= 1 ) + { + if (poDS->sPostGISVersion.nMajor >= 2) + osFieldList += "ST_AsEWKT("; + else + osFieldList += "AsEWKT("; + osFieldList += osEscapedGeom; + osFieldList += ") AS "; + osFieldList += OGRPGEscapeColumnName( + CPLSPrintf("AsEWKT_%s", poGeomFieldDefn->GetNameRef())); + + } + else + { + osFieldList += "AsText("; + osFieldList += osEscapedGeom; + osFieldList += ") AS "; + osFieldList += OGRPGEscapeColumnName( + CPLSPrintf("AsText_%s", poGeomFieldDefn->GetNameRef())); + } + } + else if ( poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ) + { + if ( poDS->bUseBinaryCursor ) + { + osFieldList += "ST_AsBinary("; + osFieldList += osEscapedGeom; + osFieldList += ") AS"; + osFieldList += OGRPGEscapeColumnName( + CPLSPrintf("AsBinary_%s", poGeomFieldDefn->GetNameRef())); + } + else if (CSLTestBoolean(CPLGetConfigOption("PG_USE_BASE64", "NO"))) + { + osFieldList += "encode(ST_AsBinary("; + osFieldList += osEscapedGeom; + osFieldList += "), 'base64') AS "; + osFieldList += OGRPGEscapeColumnName( + CPLSPrintf("BinaryBase64_%s", poGeomFieldDefn->GetNameRef())); + } + else if ( !CSLTestBoolean(CPLGetConfigOption("PG_USE_TEXT", "NO")) ) + { + osFieldList += osEscapedGeom; + } + else + { + osFieldList += "ST_AsText("; + osFieldList += osEscapedGeom; + osFieldList += ") AS "; + osFieldList += OGRPGEscapeColumnName( + CPLSPrintf("AsText_%s", poGeomFieldDefn->GetNameRef())); + } + } + else + { + osFieldList += osEscapedGeom; + } + } + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + + if( osFieldList.size() > 0 ) + osFieldList += ", "; + + /* With a binary cursor, it is not possible to get the time zone */ + /* of a timestamptz column. So we fallback to asking it in text mode */ + if ( poDS->bUseBinaryCursor && + poFeatureDefn->GetFieldDefn(i)->GetType() == OFTDateTime) + { + osFieldList += "CAST ("; + osFieldList += OGRPGEscapeColumnName(pszName); + osFieldList += " AS text)"; + } + else + { + osFieldList += OGRPGEscapeColumnName(pszName); + } + } + + return osFieldList; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::SetAttributeFilter( const char *pszQuery ) + +{ + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + + if( pszQuery == NULL ) + osQuery = ""; + else + osQuery = pszQuery; + + BuildWhere(); + + ResetReading(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::DeleteFeature( GIntBig nFID ) + +{ + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult = NULL; + CPLString osCommand; + + GetLayerDefn()->GetFieldCount(); + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "DeleteFeature"); + return OGRERR_FAILURE; + } + + if( bDifferedCreation && RunDifferedCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + poDS->EndCopy(); + bAutoFIDOnCreateViaCopy = FALSE; + +/* -------------------------------------------------------------------- */ +/* We can only delete features if we have a well defined FID */ +/* column to target. */ +/* -------------------------------------------------------------------- */ + if( pszFIDColumn == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DeleteFeature(" CPL_FRMT_GIB ") failed. Unable to delete features in tables without\n" + "a recognised FID column.", + nFID ); + return OGRERR_FAILURE; + + } + +/* -------------------------------------------------------------------- */ +/* Form the statement to drop the record. */ +/* -------------------------------------------------------------------- */ + osCommand.Printf( "DELETE FROM %s WHERE %s = " CPL_FRMT_GIB, + pszSqlTableName, OGRPGEscapeColumnName(pszFIDColumn).c_str(), nFID ); + +/* -------------------------------------------------------------------- */ +/* Execute the delete. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr; + + hResult = OGRPG_PQexec(hPGConn, osCommand); + + if( PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DeleteFeature() DELETE statement failed.\n%s", + PQerrorMessage(hPGConn) ); + + eErr = OGRERR_FAILURE; + } + else + { + if( EQUAL(PQcmdStatus(hResult), "DELETE 0") ) + eErr = OGRERR_NON_EXISTING_FEATURE; + else + eErr = OGRERR_NONE; + } + + OGRPGClearResult( hResult ); + + return eErr; +} + +/************************************************************************/ +/* ISetFeature() */ +/* */ +/* SetFeature() is implemented by an UPDATE SQL command */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::ISetFeature( OGRFeature *poFeature ) + +{ + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult = NULL; + CPLString osCommand; + int i = 0; + int bNeedComma = FALSE; + OGRErr eErr = OGRERR_FAILURE; + + GetLayerDefn()->GetFieldCount(); + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "SetFeature"); + return OGRERR_FAILURE; + } + + if( bDifferedCreation && RunDifferedCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + poDS->EndCopy(); + + if( NULL == poFeature ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "NULL pointer to OGRFeature passed to SetFeature()." ); + return eErr; + } + + if( poFeature->GetFID() == OGRNullFID ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "FID required on features given to SetFeature()." ); + return eErr; + } + + if( pszFIDColumn == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to update features in tables without\n" + "a recognised FID column."); + return eErr; + + } + + /* In case the FID column has also been created as a regular field */ + if( iFIDAsRegularColumnIndex >= 0 ) + { + if( !poFeature->IsFieldSet( iFIDAsRegularColumnIndex ) || + poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) != poFeature->GetFID() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Inconsistent values of FID and field of same name"); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Form the UPDATE command. */ +/* -------------------------------------------------------------------- */ + osCommand.Printf( "UPDATE %s SET ", pszSqlTableName ); + + /* Set the geometry */ + for( i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(i); + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(i); + if( poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB ) + { + if( bNeedComma ) + osCommand += ", "; + else + bNeedComma = TRUE; + + osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()); + osCommand += " = "; + if ( poGeom != NULL ) + { + if( !bWkbAsOid ) + { + char *pszBytea = GeometryToBYTEA( poGeom, poDS->sPostGISVersion.nMajor < 2 ); + + if( pszBytea != NULL ) + { + if (poDS->bUseEscapeStringSyntax) + osCommand += "E"; + osCommand = osCommand + "'" + pszBytea + "'"; + CPLFree( pszBytea ); + } + else + osCommand += "NULL"; + } + else + { + Oid oid = GeometryToOID( poGeom ); + + if( oid != 0 ) + { + osCommand += CPLString().Printf( "'%d' ", oid ); + } + else + osCommand += "NULL"; + } + } + else + osCommand += "NULL"; + } + else if( poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY || + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ) + { + if( bNeedComma ) + osCommand += ", "; + else + bNeedComma = TRUE; + + osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()); + osCommand += " = "; + if( poGeom != NULL ) + { + poGeom->closeRings(); + poGeom->setCoordinateDimension( poGeomFieldDefn->nCoordDimension ); + } + + if ( !CSLTestBoolean(CPLGetConfigOption("PG_USE_TEXT", "NO")) ) + { + if ( poGeom != NULL ) + { + char* pszHexEWKB = OGRGeometryToHexEWKB( poGeom, poGeomFieldDefn->nSRSId, + poDS->sPostGISVersion.nMajor < 2 ); + if ( poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ) + osCommand += CPLString().Printf("'%s'::GEOGRAPHY", pszHexEWKB); + else + osCommand += CPLString().Printf("'%s'::GEOMETRY", pszHexEWKB); + OGRFree( pszHexEWKB ); + } + else + osCommand += "NULL"; + } + else + { + char *pszWKT = NULL; + + if (poGeom != NULL) + poGeom->exportToWkt( &pszWKT ); + + int nSRSId = poGeomFieldDefn->nSRSId; + if( pszWKT != NULL ) + { + if( poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ) + osCommand += + CPLString().Printf( + "ST_GeographyFromText('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT ); + else if( poDS->sPostGISVersion.nMajor >= 1 ) + osCommand += + CPLString().Printf( + "GeomFromEWKT('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT ); + else + osCommand += + CPLString().Printf( + "GeometryFromText('%s'::TEXT,%d) ", pszWKT, nSRSId ); + OGRFree( pszWKT ); + } + else + osCommand += "NULL"; + + } + } + } + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if( iFIDAsRegularColumnIndex == i ) + continue; + if( bNeedComma ) + osCommand += ", "; + else + bNeedComma = TRUE; + + osCommand = osCommand + + OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(i)->GetNameRef()) + " = "; + + if( !poFeature->IsFieldSet( i ) ) + { + osCommand += "NULL"; + } + else + { + OGRPGCommonAppendFieldValue(osCommand, poFeature, i, + (OGRPGCommonEscapeStringCbk)OGRPGEscapeString, hPGConn); + } + } + + /* Add the WHERE clause */ + osCommand += " WHERE "; + osCommand = osCommand + OGRPGEscapeColumnName(pszFIDColumn) + " = "; + osCommand += CPLString().Printf( CPL_FRMT_GIB, poFeature->GetFID() ); + +/* -------------------------------------------------------------------- */ +/* Execute the update. */ +/* -------------------------------------------------------------------- */ + hResult = OGRPG_PQexec(hPGConn, osCommand); + if( PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "UPDATE command for feature " CPL_FRMT_GIB " failed.\n%s\nCommand: %s", + poFeature->GetFID(), PQerrorMessage(hPGConn), osCommand.c_str() ); + + OGRPGClearResult( hResult ); + + return OGRERR_FAILURE; + } + + if( EQUAL(PQcmdStatus(hResult), "UPDATE 0") ) + eErr = OGRERR_NON_EXISTING_FEATURE; + else + eErr = OGRERR_NONE; + + OGRPGClearResult( hResult ); + + return eErr; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::ICreateFeature( OGRFeature *poFeature ) +{ + GetLayerDefn()->GetFieldCount(); + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "CreateFeature"); + return OGRERR_FAILURE; + } + + if( NULL == poFeature ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "NULL pointer to OGRFeature passed to CreateFeature()." ); + return OGRERR_FAILURE; + } + + if( bDifferedCreation && RunDifferedCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + + /* In case the FID column has also been created as a regular field */ + if( iFIDAsRegularColumnIndex >= 0 ) + { + if( poFeature->GetFID() == OGRNullFID ) + { + if( poFeature->IsFieldSet( iFIDAsRegularColumnIndex ) ) + { + poFeature->SetFID( + poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex)); + } + } + else + { + if( !poFeature->IsFieldSet( iFIDAsRegularColumnIndex ) || + poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) != poFeature->GetFID() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Inconsistent values of FID and field of same name"); + return CE_Failure; + } + } + } + + /* Auto-promote FID column to 64bit if necessary */ + if( pszFIDColumn != NULL && + (GIntBig)(int)poFeature->GetFID() != poFeature->GetFID() && + GetMetadataItem(OLMD_FID64) == NULL ) + { + poDS->EndCopy(); + + CPLString osCommand; + osCommand.Printf( "ALTER TABLE %s ALTER COLUMN %s TYPE INT8", + pszSqlTableName, + OGRPGEscapeColumnName(pszFIDColumn).c_str() ); + PGconn *hPGConn = poDS->GetPGConn(); + PGresult* hResult = OGRPG_PQexec(hPGConn, osCommand); + if( PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s\n%s", + osCommand.c_str(), + PQerrorMessage(hPGConn) ); + + OGRPGClearResult( hResult ); + + return OGRERR_FAILURE; + } + OGRPGClearResult( hResult ); + + SetMetadataItem(OLMD_FID64, "YES"); + } + + if( bFirstInsertion ) + { + bFirstInsertion = FALSE; + if( CSLTestBoolean(CPLGetConfigOption("OGR_TRUNCATE", "NO")) ) + { + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult; + CPLString osCommand; + + osCommand.Printf("TRUNCATE TABLE %s", pszSqlTableName ); + hResult = OGRPG_PQexec( hPGConn, osCommand.c_str() ); + OGRPGClearResult( hResult ); + } + } + + // We avoid testing the config option too often. + if( bUseCopy == USE_COPY_UNSET ) + bUseCopy = CSLTestBoolean( CPLGetConfigOption( "PG_USE_COPY", "NO") ); + + OGRErr eErr; + if( !bUseCopy ) + { + eErr = CreateFeatureViaInsert( poFeature ); + } + else + { + /* If there's a unset field with a default value, then we must use */ + /* a specific INSERT statement to avoid unset fields to be bound to NULL */ + int bHasDefaultValue = FALSE; + int iField; + int nFieldCount = poFeatureDefn->GetFieldCount(); + for( iField = 0; iField < nFieldCount; iField++ ) + { + if( !poFeature->IsFieldSet( iField ) && + poFeature->GetFieldDefnRef(iField)->GetDefault() != NULL ) + { + bHasDefaultValue = TRUE; + break; + } + } + if( bHasDefaultValue ) + { + eErr = CreateFeatureViaInsert( poFeature ); + } + else + { + int bFIDSet = (pszFIDColumn != NULL && poFeature->GetFID() != OGRNullFID); + if( bCopyActive && bFIDSet != bFIDColumnInCopyFields ) + { + eErr = CreateFeatureViaInsert( poFeature ); + } + else if( !bCopyActive && poFeatureDefn->GetFieldCount() == 0 && + poFeatureDefn->GetGeomFieldCount() == 0 && !bFIDSet ) + { + eErr = CreateFeatureViaInsert( poFeature ); + } + else + { + if ( !bCopyActive ) + { + /* This is a heuristics. If the first feature to be copied has a */ + /* FID set (and that a FID column has been identified), then we will */ + /* try to copy FID values from features. Otherwise, we will not */ + /* do and assume that the FID column is an autoincremented column. */ + bFIDColumnInCopyFields = bFIDSet; + } + + eErr = CreateFeatureViaCopy( poFeature ); + if( bFIDSet ) + bAutoFIDOnCreateViaCopy = FALSE; + if( eErr == CE_None && bAutoFIDOnCreateViaCopy ) + { + poFeature->SetFID( ++iNextShapeId ); + } + } + } + } + + if( eErr == CE_None && iFIDAsRegularColumnIndex >= 0 ) + { + poFeature->SetField(iFIDAsRegularColumnIndex, poFeature->GetFID()); + } + + return eErr; +} + +/************************************************************************/ +/* OGRPGEscapeColumnName( ) */ +/************************************************************************/ + +CPLString OGRPGEscapeColumnName(const char* pszColumnName) +{ + CPLString osStr; + + osStr += "\""; + + char ch; + for(int i=0; (ch = pszColumnName[i]) != '\0'; i++) + { + if (ch == '"') + osStr.append(1, ch); + osStr.append(1, ch); + } + + osStr += "\""; + + return osStr; +} + +/************************************************************************/ +/* OGRPGEscapeString( ) */ +/************************************************************************/ + +CPLString OGRPGEscapeString(PGconn *hPGConn, + const char* pszStrValue, int nMaxLength, + const char* pszTableName, + const char* pszFieldName ) +{ + CPLString osCommand; + + /* We need to quote and escape string fields. */ + osCommand += "'"; + + + int nSrcLen = strlen(pszStrValue); + int nSrcLenUTF = CPLStrlenUTF8(pszStrValue); + + if (nMaxLength > 0 && nSrcLenUTF > nMaxLength) + { + CPLDebug( "PG", + "Truncated %s.%s field value '%s' to %d characters.", + pszTableName, pszFieldName, pszStrValue, nMaxLength ); + + int iUTF8Char = 0; + for(int iChar = 0; iChar < nSrcLen; iChar++ ) + { + if( (((unsigned char *) pszStrValue)[iChar] & 0xc0) != 0x80 ) + { + if( iUTF8Char == nMaxLength ) + { + nSrcLen = iChar; + break; + } + iUTF8Char ++; + } + } + } + + char* pszDestStr = (char*)CPLMalloc(2 * nSrcLen + 1); + + /* -------------------------------------------------------------------- */ + /* PQescapeStringConn was introduced in PostgreSQL security releases */ + /* 8.1.4, 8.0.8, 7.4.13, 7.3.15 */ + /* PG_HAS_PQESCAPESTRINGCONN is added by a test in 'configure' */ + /* so it is not set by default when building OGR for Win32 */ + /* -------------------------------------------------------------------- */ +#if defined(PG_HAS_PQESCAPESTRINGCONN) + int nError; + PQescapeStringConn (hPGConn, pszDestStr, pszStrValue, nSrcLen, &nError); + if (nError == 0) + osCommand += pszDestStr; + else + CPLError(CE_Warning, CPLE_AppDefined, + "PQescapeString(): %s\n" + " input: '%s'\n" + " got: '%s'\n", + PQerrorMessage( hPGConn ), + pszStrValue, pszDestStr ); +#else + PQescapeString(pszDestStr, pszStrValue, nSrcLen); + osCommand += pszDestStr; +#endif + CPLFree(pszDestStr); + + osCommand += "'"; + + return osCommand; +} + +/************************************************************************/ +/* CreateFeatureViaInsert() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::CreateFeatureViaInsert( OGRFeature *poFeature ) + +{ + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult; + CPLString osCommand; + int i; + int bNeedComma = FALSE; + + int bEmptyInsert = FALSE; + + poDS->EndCopy(); + +/* -------------------------------------------------------------------- */ +/* Form the INSERT command. */ +/* -------------------------------------------------------------------- */ + osCommand.Printf( "INSERT INTO %s (", pszSqlTableName ); + + for( i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(i); + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(i); + if( poGeom == NULL ) + continue; + if( !bNeedComma ) + bNeedComma = TRUE; + else + osCommand += ", "; + osCommand += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()) + " "; + } + + /* Use case of ogr_pg_60 test */ + if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL ) + { + if( bNeedComma ) + osCommand += ", "; + + osCommand = osCommand + OGRPGEscapeColumnName(pszFIDColumn) + " "; + bNeedComma = TRUE; + } + + int nFieldCount = poFeatureDefn->GetFieldCount(); + for( i = 0; i < nFieldCount; i++ ) + { + if( iFIDAsRegularColumnIndex == i ) + continue; + if( !poFeature->IsFieldSet( i ) ) + continue; + + if( !bNeedComma ) + bNeedComma = TRUE; + else + osCommand += ", "; + + osCommand = osCommand + + OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(i)->GetNameRef()); + } + + if (!bNeedComma) + bEmptyInsert = TRUE; + + osCommand += ") VALUES ("; + + /* Set the geometry */ + bNeedComma = FALSE; + for( i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(i); + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(i); + if( poGeom == NULL ) + continue; + if( bNeedComma ) + osCommand += ", "; + else + bNeedComma = TRUE; + + if( poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY || + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY ) + { + CheckGeomTypeCompatibility(i, poGeom); + + poGeom->closeRings(); + poGeom->setCoordinateDimension( poGeomFieldDefn->nCoordDimension ); + + int nSRSId = poGeomFieldDefn->nSRSId; + + if ( !CSLTestBoolean(CPLGetConfigOption("PG_USE_TEXT", "NO")) ) + { + char *pszHexEWKB = OGRGeometryToHexEWKB( poGeom, nSRSId, + poDS->sPostGISVersion.nMajor < 2 ); + if ( poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ) + osCommand += CPLString().Printf("'%s'::GEOGRAPHY", pszHexEWKB); + else + osCommand += CPLString().Printf("'%s'::GEOMETRY", pszHexEWKB); + OGRFree( pszHexEWKB ); + } + else + { + char *pszWKT = NULL; + poGeom->exportToWkt( &pszWKT ); + + if( pszWKT != NULL ) + { + if( poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY ) + osCommand += + CPLString().Printf( + "ST_GeographyFromText('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT ); + else if( poDS->sPostGISVersion.nMajor >= 1 ) + osCommand += + CPLString().Printf( + "GeomFromEWKT('SRID=%d;%s'::TEXT) ", nSRSId, pszWKT ); + else + osCommand += + CPLString().Printf( + "GeometryFromText('%s'::TEXT,%d) ", pszWKT, nSRSId ); + OGRFree( pszWKT ); + } + else + osCommand += "''"; + + } + } + else if( !bWkbAsOid ) + { + char *pszBytea = GeometryToBYTEA( poGeom, poDS->sPostGISVersion.nMajor < 2 ); + + if( pszBytea != NULL ) + { + if (poDS->bUseEscapeStringSyntax) + osCommand += "E"; + osCommand = osCommand + "'" + pszBytea + "'"; + CPLFree( pszBytea ); + } + else + osCommand += "''"; + } + else if( poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB && + bWkbAsOid && poGeom != NULL ) + { + Oid oid = GeometryToOID( poGeom ); + + if( oid != 0 ) + { + osCommand += CPLString().Printf( "'%d' ", oid ); + } + else + osCommand += "''"; + } + } + + if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL ) + { + if( bNeedComma ) + osCommand += ", "; + osCommand += CPLString().Printf( CPL_FRMT_GIB " ", poFeature->GetFID() ); + bNeedComma = TRUE; + } + + + for( i = 0; i < nFieldCount; i++ ) + { + if( iFIDAsRegularColumnIndex == i ) + continue; + if( !poFeature->IsFieldSet( i ) ) + continue; + + if( bNeedComma ) + osCommand += ", "; + else + bNeedComma = TRUE; + + OGRPGCommonAppendFieldValue(osCommand, poFeature, i, + (OGRPGCommonEscapeStringCbk)OGRPGEscapeString, hPGConn); + } + + osCommand += ")"; + + if (bEmptyInsert) + osCommand.Printf( "INSERT INTO %s DEFAULT VALUES", pszSqlTableName ); + + int bReturnRequested = FALSE; + /* RETURNING is only available since Postgres 8.2 */ + /* We only get the FID, but we also could add the unset fields to get */ + /* the default values */ + if (bRetrieveFID && pszFIDColumn != NULL && poFeature->GetFID() == OGRNullFID && + (poDS->sPostgreSQLVersion.nMajor >= 9 || + (poDS->sPostgreSQLVersion.nMajor == 8 && poDS->sPostgreSQLVersion.nMinor >= 2))) + { + bReturnRequested = TRUE; + osCommand += " RETURNING "; + osCommand += OGRPGEscapeColumnName(pszFIDColumn); + } + +/* -------------------------------------------------------------------- */ +/* Execute the insert. */ +/* -------------------------------------------------------------------- */ + hResult = OGRPG_PQexec(hPGConn, osCommand); + if (bReturnRequested && PQresultStatus(hResult) == PGRES_TUPLES_OK && + PQntuples(hResult) == 1 && PQnfields(hResult) == 1 ) + { + const char* pszFID = PQgetvalue(hResult, 0, 0 ); + poFeature->SetFID(CPLAtoGIntBig(pszFID)); + } + else if( bReturnRequested || PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "INSERT command for new feature failed.\n%s\nCommand: %s", + PQerrorMessage(hPGConn), osCommand.c_str() ); + + if( !bHasWarnedAlreadySetFID && poFeature->GetFID() != OGRNullFID && + pszFIDColumn != NULL ) + { + bHasWarnedAlreadySetFID = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "You've inserted feature with an already set FID and that's perhaps the reason for the failure. " + "If so, this can happen if you reuse the same feature object for sequential insertions. " + "Indeed, since GDAL 1.8.0, the FID of an inserted feature is got from the server, so it is not a good idea" + "to reuse it afterwards... All in all, try unsetting the FID with SetFID(-1) before calling CreateFeature()"); + } + + OGRPGClearResult( hResult ); + + return OGRERR_FAILURE; + } + + OGRPGClearResult( hResult ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateFeatureViaCopy() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::CreateFeatureViaCopy( OGRFeature *poFeature ) +{ + PGconn *hPGConn = poDS->GetPGConn(); + CPLString osCommand; + int i; + + /* Tell the datasource we are now planning to copy data */ + poDS->StartCopy( this ); + + /* First process geometry */ + for( i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(i); + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(i); + + char *pszGeom = NULL; + if ( NULL != poGeom ) + { + CheckGeomTypeCompatibility(i, poGeom); + + poGeom->closeRings(); + poGeom->setCoordinateDimension( poGeomFieldDefn->nCoordDimension ); + + if( poGeomFieldDefn->ePostgisType == GEOM_TYPE_WKB ) + pszGeom = GeometryToBYTEA( poGeom, poDS->sPostGISVersion.nMajor < 2 ); + else + pszGeom = OGRGeometryToHexEWKB( poGeom, poGeomFieldDefn->nSRSId, + poDS->sPostGISVersion.nMajor < 2 ); + } + + if (osCommand.size() > 0) + osCommand += "\t"; + + if ( pszGeom ) + { + osCommand += pszGeom; + CPLFree( pszGeom ); + } + else + { + osCommand += "\\N"; + } + } + + OGRPGCommonAppendCopyFieldsExceptGeom(osCommand, + poFeature, + pszFIDColumn, + bFIDColumnInCopyFields, + (OGRPGCommonEscapeStringCbk)OGRPGEscapeString, + hPGConn); + + /* Add end of line marker */ + osCommand += "\n"; + + + /* ------------------------------------------------------------ */ + /* Execute the copy. */ + /* ------------------------------------------------------------ */ + + OGRErr result = OGRERR_NONE; + + /* This is for postgresql 7.4 and higher */ +#if !defined(PG_PRE74) + int copyResult = PQputCopyData(hPGConn, osCommand.c_str(), strlen(osCommand.c_str())); +#ifdef DEBUG_VERBOSE + CPLDebug("PG", "PQputCopyData(%s)", osCommand.c_str()); +#endif + + switch (copyResult) + { + case 0: + CPLError( CE_Failure, CPLE_AppDefined, "Writing COPY data blocked."); + result = OGRERR_FAILURE; + break; + case -1: + CPLError( CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn) ); + result = OGRERR_FAILURE; + break; + } +#else /* else defined(PG_PRE74) */ + int copyResult = PQputline(hPGConn, osCommand.c_str()); + + if (copyResult == EOF) + { + CPLError( CE_Failure, CPLE_AppDefined, "Writing COPY data blocked."); + result = OGRERR_FAILURE; + } +#endif /* end of defined(PG_PRE74) */ + + return result; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPGTableLayer::TestCapability( const char * pszCap ) + +{ + if ( bUpdateAccess ) + { + if( EQUAL(pszCap,OLCSequentialWrite) || + EQUAL(pszCap,OLCCreateField) || + EQUAL(pszCap,OLCCreateGeomField) || + EQUAL(pszCap,OLCDeleteField) || + EQUAL(pszCap,OLCAlterFieldDefn) ) + return TRUE; + + else if( EQUAL(pszCap,OLCRandomWrite) || + EQUAL(pszCap,OLCDeleteFeature) ) + { + GetLayerDefn()->GetFieldCount(); + return pszFIDColumn != NULL; + } + } + + if( EQUAL(pszCap,OLCRandomRead) ) + { + GetLayerDefn()->GetFieldCount(); + return pszFIDColumn != NULL; + } + + else if( EQUAL(pszCap,OLCFastFeatureCount) || + EQUAL(pszCap,OLCFastSetNextByIndex) ) + { + if( m_poFilterGeom == NULL ) + return TRUE; + OGRPGGeomFieldDefn* poGeomFieldDefn = NULL; + if( poFeatureDefn->GetGeomFieldCount() > 0 ) + poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(m_iGeomFieldFilter); + return poGeomFieldDefn == NULL || + (poDS->sPostGISVersion.nMajor >= 0 && + (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY || + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)); + } + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = NULL; + if( poFeatureDefn->GetGeomFieldCount() > 0 ) + poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(m_iGeomFieldFilter); + return poGeomFieldDefn == NULL || + (poDS->sPostGISVersion.nMajor >= 0 && + (poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY || + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOGRAPHY)); + } + + else if( EQUAL(pszCap,OLCTransactions) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + { + OGRPGGeomFieldDefn* poGeomFieldDefn = NULL; + if( poFeatureDefn->GetGeomFieldCount() > 0 ) + poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(0); + return poGeomFieldDefn != NULL && + poDS->sPostGISVersion.nMajor >= 0 && + poGeomFieldDefn->ePostgisType == GEOM_TYPE_GEOMETRY; + } + + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + + else if( EQUAL(pszCap,OLCCurveGeometries) ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::CreateField( OGRFieldDefn *poFieldIn, int bApproxOK ) + +{ + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult = NULL; + CPLString osCommand; + CPLString osFieldType; + OGRFieldDefn oField( poFieldIn ); + + GetLayerDefn()->GetFieldCount(); + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "CreateField"); + return OGRERR_FAILURE; + } + + if( pszFIDColumn != NULL && + EQUAL( oField.GetNameRef(), pszFIDColumn ) && + oField.GetType() != OFTInteger && + oField.GetType() != OFTInteger64 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s", + oField.GetNameRef()); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Do we want to "launder" the column names into Postgres */ +/* friendly format? */ +/* -------------------------------------------------------------------- */ + if( bLaunderColumnNames ) + { + char *pszSafeName = OGRPGCommonLaunderName( oField.GetNameRef(), "PG" ); + + oField.SetName( pszSafeName ); + CPLFree( pszSafeName ); + + if( EQUAL(oField.GetNameRef(),"oid") ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Renaming field 'oid' to 'oid_' to avoid conflict with internal oid field." ); + oField.SetName( "oid_" ); + } + } + + + const char* pszOverrideType = CSLFetchNameValue(papszOverrideColumnTypes, oField.GetNameRef()); + if( pszOverrideType != NULL ) + osFieldType = pszOverrideType; + else + { + osFieldType = OGRPGCommonLayerGetType(oField, bPreservePrecision, bApproxOK); + if (osFieldType.size() == 0) + return OGRERR_FAILURE; + } + + CPLString osNotNullDefault; + if( !oField.IsNullable() ) + osNotNullDefault += " NOT NULL"; + if( oField.GetDefault() != NULL && !oField.IsDefaultDriverSpecific() ) + { + osNotNullDefault += " DEFAULT "; + osNotNullDefault += OGRPGCommonLayerGetPGDefault(&oField); + } + +/* -------------------------------------------------------------------- */ +/* Create the new field. */ +/* -------------------------------------------------------------------- */ + if( bDifferedCreation ) + { + if( !(pszFIDColumn != NULL && EQUAL(pszFIDColumn,oField.GetNameRef())) ) + { + osCreateTable += ", "; + osCreateTable += OGRPGEscapeColumnName(oField.GetNameRef()); + osCreateTable += " "; + osCreateTable += osFieldType; + osCreateTable += osNotNullDefault; + } + } + else + { + poDS->EndCopy(); + + osCommand.Printf( "ALTER TABLE %s ADD COLUMN %s %s", + pszSqlTableName, OGRPGEscapeColumnName(oField.GetNameRef()).c_str(), + osFieldType.c_str() ); + osCommand += osNotNullDefault; + + hResult = OGRPG_PQexec(hPGConn, osCommand); + if( PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s\n%s", + osCommand.c_str(), + PQerrorMessage(hPGConn) ); + + OGRPGClearResult( hResult ); + + return OGRERR_FAILURE; + } + + OGRPGClearResult( hResult ); + } + + poFeatureDefn->AddFieldDefn( &oField ); + + if( pszFIDColumn != NULL && + EQUAL( oField.GetNameRef(), pszFIDColumn ) ) + { + iFIDAsRegularColumnIndex = poFeatureDefn->GetFieldCount() - 1; + } + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* RunAddGeometryColumn() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::RunAddGeometryColumn( OGRPGGeomFieldDefn *poGeomField ) +{ + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult; + CPLString osCommand; + + const char *pszGeometryType = OGRToOGCGeomType(poGeomField->GetType()); + osCommand.Printf( + "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s',%d)", + OGRPGEscapeString(hPGConn, pszSchemaName).c_str(), + OGRPGEscapeString(hPGConn, pszTableName).c_str(), + OGRPGEscapeString(hPGConn, poGeomField->GetNameRef()).c_str(), + poGeomField->nSRSId, pszGeometryType, poGeomField->nCoordDimension ); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str()); + + if( !hResult + || PQresultStatus(hResult) != PGRES_TUPLES_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "AddGeometryColumn failed for layer %s.", + GetName()); + + OGRPGClearResult( hResult ); + + return OGRERR_FAILURE; + } + + OGRPGClearResult( hResult ); + + if( !poGeomField->IsNullable() ) + { + osCommand.Printf( "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", + pszSqlTableName, + OGRPGEscapeColumnName(poGeomField->GetNameRef()).c_str() ); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str()); + OGRPGClearResult( hResult ); + } + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* RunCreateSpatialIndex() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::RunCreateSpatialIndex( OGRPGGeomFieldDefn *poGeomField ) +{ + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult; + CPLString osCommand; + + osCommand.Printf("CREATE INDEX %s ON %s USING GIST (%s)", + OGRPGEscapeColumnName( + CPLSPrintf("%s_%s_geom_idx", pszTableName, poGeomField->GetNameRef())).c_str(), + pszSqlTableName, + OGRPGEscapeColumnName(poGeomField->GetNameRef()).c_str()); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str()); + + if( !hResult + || PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "CREATE INDEX failed for layer %s.", GetName()); + + OGRPGClearResult( hResult ); + + return OGRERR_FAILURE; + } + + OGRPGClearResult( hResult ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateGeomField() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::CreateGeomField( OGRGeomFieldDefn *poGeomFieldIn, + CPL_UNUSED int bApproxOK ) +{ + OGRwkbGeometryType eType = poGeomFieldIn->GetType(); + if( eType == wkbNone ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot create geometry field of type wkbNone"); + return OGRERR_FAILURE; + } + + OGRPGGeomFieldDefn *poGeomField = + new OGRPGGeomFieldDefn( this, poGeomFieldIn->GetNameRef() ); + if( EQUAL(poGeomField->GetNameRef(), "") ) + { + if( poFeatureDefn->GetGeomFieldCount() == 0 ) + poGeomField->SetName( "wkb_geometry" ); + else + poGeomField->SetName( + CPLSPrintf("wkb_geometry%d", poFeatureDefn->GetGeomFieldCount()+1) ); + } + poGeomField->SetSpatialRef(poGeomFieldIn->GetSpatialRef()); + +/* -------------------------------------------------------------------- */ +/* Do we want to "launder" the column names into Postgres */ +/* friendly format? */ +/* -------------------------------------------------------------------- */ + if( bLaunderColumnNames ) + { + char *pszSafeName = OGRPGCommonLaunderName( poGeomField->GetNameRef(), "PG" ); + + poGeomField->SetName( pszSafeName ); + CPLFree( pszSafeName ); + } + + OGRSpatialReference* poSRS = poGeomField->GetSpatialRef(); + int nSRSId = poDS->GetUndefinedSRID(); + if( nForcedSRSId != UNDETERMINED_SRID ) + nSRSId = nForcedSRSId; + else if( poSRS != NULL ) + nSRSId = poDS->FetchSRSId( poSRS ); + + int nDimension = 3; + if( wkbFlatten(eType) == eType ) + nDimension = 2; + if( nForcedDimension > 0 ) + { + nDimension = nForcedDimension; + eType = OGR_GT_SetModifier(eType, nDimension == 3, FALSE); + } + poGeomField->SetType(eType); + poGeomField->SetNullable( poGeomFieldIn->IsNullable() ); + poGeomField->nSRSId = nSRSId; + poGeomField->nCoordDimension = nDimension; + poGeomField->ePostgisType = GEOM_TYPE_GEOMETRY; + +/* -------------------------------------------------------------------- */ +/* Create the new field. */ +/* -------------------------------------------------------------------- */ + if( !bDifferedCreation ) + { + poDS->EndCopy(); + + if( RunAddGeometryColumn(poGeomField) != OGRERR_NONE ) + { + delete poGeomField; + + return OGRERR_FAILURE; + } + + if( bCreateSpatialIndexFlag ) + { + if( RunCreateSpatialIndex(poGeomField) != OGRERR_NONE ) + { + delete poGeomField; + + return OGRERR_FAILURE; + } + } + } + + poFeatureDefn->AddGeomFieldDefn( poGeomField, FALSE ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteField() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::DeleteField( int iField ) +{ + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult = NULL; + CPLString osCommand; + + GetLayerDefn()->GetFieldCount(); + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "DeleteField"); + return OGRERR_FAILURE; + } + + if (iField < 0 || iField >= poFeatureDefn->GetFieldCount()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + + if( bDifferedCreation && RunDifferedCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + poDS->EndCopy(); + + osCommand.Printf( "ALTER TABLE %s DROP COLUMN %s", + pszSqlTableName, + OGRPGEscapeColumnName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef()).c_str() ); + hResult = OGRPG_PQexec(hPGConn, osCommand); + if( PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s\n%s", + osCommand.c_str(), + PQerrorMessage(hPGConn) ); + + OGRPGClearResult( hResult ); + + return OGRERR_FAILURE; + } + + OGRPGClearResult( hResult ); + + return poFeatureDefn->DeleteFieldDefn( iField ); +} + +/************************************************************************/ +/* AlterFieldDefn() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) +{ + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult = NULL; + CPLString osCommand; + + GetLayerDefn()->GetFieldCount(); + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "AlterFieldDefn"); + return OGRERR_FAILURE; + } + + if (iField < 0 || iField >= poFeatureDefn->GetFieldCount()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + + if( bDifferedCreation && RunDifferedCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + poDS->EndCopy(); + + OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField); + OGRFieldDefn oField( poNewFieldDefn ); + + poDS->SoftStartTransaction(); + + if (!(nFlags & ALTER_TYPE_FLAG)) + oField.SetType(poFieldDefn->GetType()); + + if (!(nFlags & ALTER_WIDTH_PRECISION_FLAG)) + { + oField.SetWidth(poFieldDefn->GetWidth()); + oField.SetPrecision(poFieldDefn->GetPrecision()); + } + + if ((nFlags & ALTER_TYPE_FLAG) || + (nFlags & ALTER_WIDTH_PRECISION_FLAG)) + { + CPLString osFieldType = OGRPGCommonLayerGetType(oField, + bPreservePrecision, + TRUE); + if (osFieldType.size() == 0) + { + poDS->SoftRollbackTransaction(); + + return OGRERR_FAILURE; + } + + osCommand.Printf( "ALTER TABLE %s ALTER COLUMN %s TYPE %s", + pszSqlTableName, + OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(), + osFieldType.c_str() ); + + hResult = OGRPG_PQexec(hPGConn, osCommand); + if( PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s\n%s", + osCommand.c_str(), + PQerrorMessage(hPGConn) ); + + OGRPGClearResult( hResult ); + + poDS->SoftRollbackTransaction(); + + return OGRERR_FAILURE; + } + OGRPGClearResult( hResult ); + } + + if( (nFlags & ALTER_NULLABLE_FLAG) && + poFieldDefn->IsNullable() != poNewFieldDefn->IsNullable() ) + { + oField.SetNullable(poNewFieldDefn->IsNullable()); + + if( poNewFieldDefn->IsNullable() ) + osCommand.Printf( "ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL", + pszSqlTableName, + OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str() ); + else + osCommand.Printf( "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", + pszSqlTableName, + OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str() ); + + hResult = OGRPG_PQexec(hPGConn, osCommand); + if( PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s\n%s", + osCommand.c_str(), + PQerrorMessage(hPGConn) ); + + OGRPGClearResult( hResult ); + + poDS->SoftRollbackTransaction(); + + return OGRERR_FAILURE; + } + OGRPGClearResult( hResult ); + } + + if( (nFlags & ALTER_DEFAULT_FLAG) && + ((poFieldDefn->GetDefault() == NULL && poNewFieldDefn->GetDefault() != NULL) || + (poFieldDefn->GetDefault() != NULL && poNewFieldDefn->GetDefault() == NULL) || + (poFieldDefn->GetDefault() != NULL && poNewFieldDefn->GetDefault() != NULL && + strcmp(poFieldDefn->GetDefault(), poNewFieldDefn->GetDefault()) != 0)) ) + { + oField.SetDefault(poNewFieldDefn->GetDefault()); + + if( poNewFieldDefn->GetDefault() == NULL ) + osCommand.Printf( "ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT", + pszSqlTableName, + OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str() ); + else + osCommand.Printf( "ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s", + pszSqlTableName, + OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(), + OGRPGCommonLayerGetPGDefault(poNewFieldDefn).c_str()); + + hResult = OGRPG_PQexec(hPGConn, osCommand); + if( PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s\n%s", + osCommand.c_str(), + PQerrorMessage(hPGConn) ); + + OGRPGClearResult( hResult ); + + poDS->SoftRollbackTransaction(); + + return OGRERR_FAILURE; + } + OGRPGClearResult( hResult ); + } + + if( (nFlags & ALTER_NAME_FLAG) ) + { + if (bLaunderColumnNames) + { + char *pszSafeName = OGRPGCommonLaunderName( oField.GetNameRef(), "PG" ); + oField.SetName( pszSafeName ); + CPLFree( pszSafeName ); + } + + if( EQUAL(oField.GetNameRef(),"oid") ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Renaming field 'oid' to 'oid_' to avoid conflict with internal oid field." ); + oField.SetName( "oid_" ); + } + + if ( strcmp(poFieldDefn->GetNameRef(), oField.GetNameRef()) != 0 ) + { + osCommand.Printf( "ALTER TABLE %s RENAME COLUMN %s TO %s", + pszSqlTableName, + OGRPGEscapeColumnName(poFieldDefn->GetNameRef()).c_str(), + OGRPGEscapeColumnName(oField.GetNameRef()).c_str() ); + hResult = OGRPG_PQexec(hPGConn, osCommand); + if( PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s\n%s", + osCommand.c_str(), + PQerrorMessage(hPGConn) ); + + OGRPGClearResult( hResult ); + + poDS->SoftRollbackTransaction(); + + return OGRERR_FAILURE; + } + OGRPGClearResult( hResult ); + } + } + + poDS->SoftCommitTransaction(); + + if (nFlags & ALTER_NAME_FLAG) + poFieldDefn->SetName(oField.GetNameRef()); + if (nFlags & ALTER_TYPE_FLAG) + poFieldDefn->SetType(oField.GetType()); + if (nFlags & ALTER_WIDTH_PRECISION_FLAG) + { + poFieldDefn->SetWidth(oField.GetWidth()); + poFieldDefn->SetPrecision(oField.GetPrecision()); + } + if (nFlags & ALTER_NULLABLE_FLAG) + poFieldDefn->SetNullable(oField.IsNullable()); + if (nFlags & ALTER_DEFAULT_FLAG) + poFieldDefn->SetDefault(oField.GetDefault()); + + return OGRERR_NONE; + +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRPGTableLayer::GetFeature( GIntBig nFeatureId ) + +{ + GetLayerDefn()->GetFieldCount(); + + if( pszFIDColumn == NULL ) + return OGRLayer::GetFeature( nFeatureId ); + +/* -------------------------------------------------------------------- */ +/* Issue query for a single record. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = NULL; + PGresult *hResult = NULL; + PGconn *hPGConn = poDS->GetPGConn(); + CPLString osFieldList = BuildFields(); + CPLString osCommand; + + poDS->EndCopy(); + poDS->SoftStartTransaction(); + + osCommand.Printf( + "DECLARE getfeaturecursor %s for " + "SELECT %s FROM %s WHERE %s = " CPL_FRMT_GIB, + ( poDS->bUseBinaryCursor ) ? "BINARY CURSOR" : "CURSOR", + osFieldList.c_str(), pszSqlTableName, OGRPGEscapeColumnName(pszFIDColumn).c_str(), + nFeatureId ); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() ); + + if( hResult && PQresultStatus(hResult) == PGRES_COMMAND_OK ) + { + OGRPGClearResult( hResult ); + + hResult = OGRPG_PQexec(hPGConn, "FETCH ALL in getfeaturecursor" ); + + if( hResult && PQresultStatus(hResult) == PGRES_TUPLES_OK ) + { + int nRows = PQntuples(hResult); + if (nRows > 0) + { + int* panTempMapFieldNameToIndex = NULL; + int* panTempMapFieldNameToGeomIndex = NULL; + CreateMapFromFieldNameToIndex(hResult, + poFeatureDefn, + panTempMapFieldNameToIndex, + panTempMapFieldNameToGeomIndex); + poFeature = RecordToFeature(hResult, + panTempMapFieldNameToIndex, + panTempMapFieldNameToGeomIndex, + 0 ); + CPLFree(panTempMapFieldNameToIndex); + CPLFree(panTempMapFieldNameToGeomIndex); + if( poFeature && iFIDAsRegularColumnIndex >= 0 ) + { + poFeature->SetField(iFIDAsRegularColumnIndex, poFeature->GetFID()); + } + + if (nRows > 1) + { + CPLError(CE_Warning, CPLE_AppDefined, + "%d rows in response to the WHERE %s = " CPL_FRMT_GIB " clause !", + nRows, pszFIDColumn, nFeatureId ); + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to read feature with unknown feature id (" CPL_FRMT_GIB ").", nFeatureId ); + } + } + } + else if ( hResult && PQresultStatus(hResult) == PGRES_FATAL_ERROR ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQresultErrorMessage( hResult ) ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + OGRPGClearResult( hResult ); + + hResult = OGRPG_PQexec(hPGConn, "CLOSE getfeaturecursor"); + OGRPGClearResult( hResult ); + + poDS->SoftCommitTransaction(); + + return poFeature; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRPGTableLayer::GetFeatureCount( int bForce ) + +{ + if( bDifferedCreation && RunDifferedCreationIfNecessary() != OGRERR_NONE ) + return 0; + poDS->EndCopy(); + + if( TestCapability(OLCFastFeatureCount) == FALSE ) + return OGRPGLayer::GetFeatureCount( bForce ); + +/* -------------------------------------------------------------------- */ +/* In theory it might be wise to cache this result, but it */ +/* won't be trivial to work out the lifetime of the value. */ +/* After all someone else could be adding records from another */ +/* application when working against a database. */ +/* -------------------------------------------------------------------- */ + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult = NULL; + CPLString osCommand; + GIntBig nCount = 0; + + osCommand.Printf( + "SELECT count(*) FROM %s %s", + pszSqlTableName, osWHERE.c_str() ); + + hResult = OGRPG_PQexec(hPGConn, osCommand); + if( hResult != NULL && PQresultStatus(hResult) == PGRES_TUPLES_OK ) + nCount = CPLAtoGIntBig(PQgetvalue(hResult,0,0)); + else + CPLDebug( "PG", "%s; failed.", osCommand.c_str() ); + OGRPGClearResult( hResult ); + + return nCount; +} + +/************************************************************************/ +/* ResolveSRID() */ +/************************************************************************/ + +void OGRPGTableLayer::ResolveSRID(OGRPGGeomFieldDefn* poGFldDefn) + +{ + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult = NULL; + CPLString osCommand; + + int nSRSId = poDS->GetUndefinedSRID(); + + osCommand.Printf( + "SELECT srid FROM geometry_columns " + "WHERE f_table_name = %s AND " + "f_geometry_column = %s", + OGRPGEscapeString(hPGConn, pszTableName).c_str(), + OGRPGEscapeString(hPGConn, poGFldDefn->GetNameRef()).c_str()); + + osCommand += CPLString().Printf(" AND f_table_schema = %s", + OGRPGEscapeString(hPGConn, pszSchemaName).c_str()); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str() ); + + if( hResult + && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) == 1 ) + { + nSRSId = atoi(PQgetvalue(hResult,0,0)); + } + + OGRPGClearResult( hResult ); + + /* With PostGIS 2.0, SRID = 0 can also mean that there's no constraint */ + /* so we need to fetch from values */ + /* We assume that all geometry of this column have identical SRID */ + if( nSRSId <= 0 && poGFldDefn->ePostgisType == GEOM_TYPE_GEOMETRY && + poDS->sPostGISVersion.nMajor >= 0 ) + { + CPLString osGetSRID; + + const char* psGetSRIDFct; + if (poDS->sPostGISVersion.nMajor >= 2) + psGetSRIDFct = "ST_SRID"; + else + psGetSRIDFct = "getsrid"; + + osGetSRID += "SELECT "; + osGetSRID += psGetSRIDFct; + osGetSRID += "("; + osGetSRID += OGRPGEscapeColumnName(poGFldDefn->GetNameRef()); + osGetSRID += ") FROM "; + osGetSRID += pszSqlTableName; + osGetSRID += " LIMIT 1"; + + hResult = OGRPG_PQexec(poDS->GetPGConn(), osGetSRID ); + if( hResult + && PQresultStatus(hResult) == PGRES_TUPLES_OK + && PQntuples(hResult) == 1 ) + { + nSRSId = atoi(PQgetvalue(hResult,0,0)); + } + + OGRPGClearResult( hResult ); + } + + poGFldDefn->nSRSId = nSRSId; +} + +/************************************************************************/ +/* StartCopy() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::StartCopy() + +{ + /*CPLDebug("PG", "OGRPGDataSource(%p)::StartCopy(%p)", poDS, this);*/ + + CPLString osFields = BuildCopyFields(); + + int size = strlen(osFields) + strlen(pszSqlTableName) + 100; + char *pszCommand = (char *) CPLMalloc(size); + + sprintf( pszCommand, + "COPY %s (%s) FROM STDIN;", + pszSqlTableName, osFields.c_str() ); + + PGconn *hPGConn = poDS->GetPGConn(); + PGresult *hResult = OGRPG_PQexec(hPGConn, pszCommand); + + if ( !hResult || (PQresultStatus(hResult) != PGRES_COPY_IN)) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", PQerrorMessage(hPGConn) ); + } + else + bCopyActive = TRUE; + + OGRPGClearResult( hResult ); + CPLFree( pszCommand ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* EndCopy() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::EndCopy() + +{ + if( !bCopyActive ) + return OGRERR_NONE; + /*CPLDebug("PG", "OGRPGDataSource(%p)::EndCopy(%p)", poDS, this);*/ + + /* This method is called from the datasource when + a COPY operation is ended */ + OGRErr result = OGRERR_NONE; + + PGconn *hPGConn = poDS->GetPGConn(); + CPLDebug( "PG", "PQputCopyEnd()" ); + + bCopyActive = FALSE; + + /* This is for postgresql 7.4 and higher */ +#if !defined(PG_PRE74) + int copyResult = PQputCopyEnd(hPGConn, NULL); + + switch (copyResult) + { + case 0: + CPLError( CE_Failure, CPLE_AppDefined, "Writing COPY data blocked."); + result = OGRERR_FAILURE; + break; + case -1: + CPLError( CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn) ); + result = OGRERR_FAILURE; + break; + } + +#else /* defined(PG_PRE74) */ + PQputline(hPGConn, "\\.\n"); + int copyResult = PQendcopy(hPGConn); + + if (copyResult != 0) + { + CPLError( CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage(hPGConn) ); + result = OGRERR_FAILURE; + } +#endif /* defined(PG_PRE74) */ + + /* Now check the results of the copy */ + PGresult * hResult = PQgetResult( hPGConn ); + + if( hResult && PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "COPY statement failed.\n%s", + PQerrorMessage(hPGConn) ); + + result = OGRERR_FAILURE; + } + + OGRPGClearResult( hResult ); + + if( !bUseCopyByDefault ) + bUseCopy = USE_COPY_UNSET; + + return result; +} + +/************************************************************************/ +/* BuildCopyFields() */ +/************************************************************************/ + +CPLString OGRPGTableLayer::BuildCopyFields() +{ + int i = 0; + int nFIDIndex = -1; + CPLString osFieldList; + + for( i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(i); + if( osFieldList.size() > 0 ) + osFieldList += ", "; + osFieldList += OGRPGEscapeColumnName(poGeomFieldDefn->GetNameRef()); + } + + if( bFIDColumnInCopyFields ) + { + if( osFieldList.size() > 0 ) + osFieldList += ", "; + + nFIDIndex = poFeatureDefn->GetFieldIndex( pszFIDColumn ); + + osFieldList += OGRPGEscapeColumnName(pszFIDColumn); + } + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if (i == nFIDIndex) + continue; + + const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + + if( osFieldList.size() > 0 ) + osFieldList += ", "; + + osFieldList += OGRPGEscapeColumnName(pszName); + } + + return osFieldList; +} + +/************************************************************************/ +/* CheckGeomTypeCompatibility() */ +/************************************************************************/ + +void OGRPGTableLayer::CheckGeomTypeCompatibility(int iGeomField, + OGRGeometry* poGeom) +{ + if (bHasWarnedIncompatibleGeom) + return; + + OGRwkbGeometryType eExpectedGeomType = + poFeatureDefn->GetGeomFieldDefn(iGeomField)->GetType(); + OGRwkbGeometryType eFlatLayerGeomType = wkbFlatten(eExpectedGeomType); + OGRwkbGeometryType eFlatGeomType = wkbFlatten(poGeom->getGeometryType()); + if (eFlatLayerGeomType == wkbUnknown) + return; + + if (eFlatLayerGeomType == wkbGeometryCollection) + bHasWarnedIncompatibleGeom = eFlatGeomType != wkbMultiPoint && + eFlatGeomType != wkbMultiLineString && + eFlatGeomType != wkbMultiPolygon && + eFlatGeomType != wkbGeometryCollection; + else + bHasWarnedIncompatibleGeom = (eFlatGeomType != eFlatLayerGeomType); + + if (bHasWarnedIncompatibleGeom) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Geometry to be inserted is of type %s, whereas the layer geometry type is %s.\n" + "Insertion is likely to fail", + OGRGeometryTypeToName(poGeom->getGeometryType()), + OGRGeometryTypeToName(eExpectedGeomType)); + } +} + +/************************************************************************/ +/* SetOverrideColumnTypes() */ +/************************************************************************/ + +void OGRPGTableLayer::SetOverrideColumnTypes( const char* pszOverrideColumnTypes ) +{ + if( pszOverrideColumnTypes == NULL ) + return; + + const char* pszIter = pszOverrideColumnTypes; + CPLString osCur; + while(*pszIter != '\0') + { + if( *pszIter == '(' ) + { + /* Ignore commas inside ( ) pair */ + while(*pszIter != '\0') + { + if( *pszIter == ')' ) + { + osCur += *pszIter; + pszIter ++; + break; + } + osCur += *pszIter; + pszIter ++; + } + if( *pszIter == '\0') + break; + } + + if( *pszIter == ',' ) + { + papszOverrideColumnTypes = CSLAddString(papszOverrideColumnTypes, osCur); + osCur = ""; + } + else + osCur += *pszIter; + pszIter ++; + } + if( osCur.size() ) + papszOverrideColumnTypes = CSLAddString(papszOverrideColumnTypes, osCur); +} + +/************************************************************************/ +/* GetExtent() */ +/* */ +/* For PostGIS use internal ST_EstimatedExtent(geometry) function */ +/* if bForce == 0 */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::GetExtent( int iGeomField, OGREnvelope *psExtent, int bForce ) +{ + CPLString osCommand; + + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() || + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone ) + { + if( iGeomField != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } + return OGRERR_FAILURE; + } + + if( bDifferedCreation && RunDifferedCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + poDS->EndCopy(); + + OGRPGGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(iGeomField); + + const char* pszExtentFct; + // if bForce is 0 and ePostgisType is not GEOM_TYPE_GEOGRAPHY we can use + // the ST_EstimatedExtent function which is quicker + // ST_EstimatedExtent was called ST_Estimated_Extent up to PostGIS 2.0.x + // ST_EstimatedExtent returns NULL in absence of statistics (an exception before + // PostGIS 1.5.4) + if ( bForce == 0 && TestCapability(OLCFastGetExtent) ) + { + PGconn *hPGConn = poDS->GetPGConn(); + + if ( poDS->sPostGISVersion.nMajor > 2 || + ( poDS->sPostGISVersion.nMajor == 2 && poDS->sPostGISVersion.nMinor >= 1 ) ) + pszExtentFct = "ST_EstimatedExtent"; + else + pszExtentFct = "ST_Estimated_Extent"; + + osCommand.Printf( "SELECT %s(%s, %s, %s)", + pszExtentFct, + OGRPGEscapeString(hPGConn, pszSchemaName).c_str(), + OGRPGEscapeString(hPGConn, pszTableName).c_str(), + OGRPGEscapeString(hPGConn, poGeomFieldDefn->GetNameRef()).c_str() ); + + /* Quiet error: ST_Estimated_Extent may return an error if statistics */ + /* have not been computed */ + if( RunGetExtentRequest(psExtent, bForce, osCommand, TRUE) == OGRERR_NONE ) + return OGRERR_NONE; + + CPLDebug("PG","Unable to get extimated extent by PostGIS. Trying real extent."); + } + + return OGRPGLayer::GetExtent( iGeomField, psExtent, bForce ); +} + +/************************************************************************/ +/* SetDifferedCreation() */ +/************************************************************************/ + +void OGRPGTableLayer::SetDifferedCreation(int bDifferedCreationIn, CPLString osCreateTableIn) +{ + bDifferedCreation = bDifferedCreationIn; + osCreateTable = osCreateTableIn; +} + +/************************************************************************/ +/* RunDifferedCreationIfNecessary() */ +/************************************************************************/ + +OGRErr OGRPGTableLayer::RunDifferedCreationIfNecessary() +{ + if( !bDifferedCreation ) + return OGRERR_NONE; + bDifferedCreation = FALSE; + + poDS->EndCopy(); + + int i; + + for( i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRPGGeomFieldDefn *poGeomField = (OGRPGGeomFieldDefn*) poFeatureDefn->GetGeomFieldDefn(i); + + if (poDS->sPostGISVersion.nMajor >= 2 || + poGeomField->ePostgisType == GEOM_TYPE_GEOGRAPHY) + { + const char *pszGeometryType = OGRToOGCGeomType(poGeomField->GetType()); + + osCreateTable += ", "; + osCreateTable += OGRPGEscapeColumnName(poGeomField->GetNameRef()); + osCreateTable += " "; + if( poGeomField->ePostgisType == GEOM_TYPE_GEOMETRY ) + osCreateTable += "geometry("; + else + osCreateTable += "geography("; + osCreateTable += pszGeometryType; + if( poGeomField->nCoordDimension == 3 ) + osCreateTable += "Z"; + if( poGeomField->nSRSId > 0 ) + osCreateTable += CPLSPrintf(",%d", poGeomField->nSRSId); + osCreateTable += ")"; + if( !poGeomField->IsNullable() ) + osCreateTable += " NOT NULL"; + } + } + + osCreateTable += " )"; + CPLString osCommand(osCreateTable); + + PGresult *hResult; + PGconn *hPGConn = poDS->GetPGConn(); + + hResult = OGRPG_PQexec(hPGConn, osCommand.c_str()); + if( PQresultStatus(hResult) != PGRES_COMMAND_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s\n%s", osCommand.c_str(), PQerrorMessage(hPGConn) ); + + OGRPGClearResult( hResult ); + return OGRERR_FAILURE; + } + + OGRPGClearResult( hResult ); + + // For PostGIS 1.X, use AddGeometryColumn() to create geometry columns + if (poDS->sPostGISVersion.nMajor < 2) + { + for( i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRPGGeomFieldDefn *poGeomField = (OGRPGGeomFieldDefn*) poFeatureDefn->GetGeomFieldDefn(i); + if( poGeomField->ePostgisType == GEOM_TYPE_GEOMETRY && + RunAddGeometryColumn(poGeomField) != OGRERR_NONE ) + { + return OGRERR_FAILURE; + } + } + } + + if( bCreateSpatialIndexFlag ) + { + for( i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRPGGeomFieldDefn *poGeomField = (OGRPGGeomFieldDefn*) poFeatureDefn->GetGeomFieldDefn(i); + if( RunCreateSpatialIndex(poGeomField) != OGRERR_NONE ) + { + return OGRERR_FAILURE; + } + } + } + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgutility.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgutility.cpp new file mode 100644 index 000000000..a982e1e16 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgutility.cpp @@ -0,0 +1,96 @@ +/****************************************************************************** + * $Id: ogrpgutility.cpp 27784 2014-10-02 15:43:18Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Utility methods + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2009-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_pg.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrpgutility.cpp 27784 2014-10-02 15:43:18Z rouault $"); + +/************************************************************************/ +/* OGRPG_PQexec() */ +/************************************************************************/ + +PGresult *OGRPG_PQexec(PGconn *conn, const char *query, int bMultipleCommandAllowed, + int bErrorAsDebug) +{ + PGresult* hResult; +#if defined(PG_PRE74) + /* PQexecParams introduced in PG >= 7.4 */ + hResult = PQexec(conn, query); +#else + if (bMultipleCommandAllowed) + hResult = PQexec(conn, query); + else + hResult = PQexecParams(conn, query, 0, NULL, NULL, NULL, NULL, 0); +#endif + +#ifdef DEBUG + const char* pszRetCode = "UNKNOWN"; + char szNTuples[32]; + szNTuples[0] = '\0'; + if (hResult) + { + switch(PQresultStatus(hResult)) + { + case PGRES_TUPLES_OK: + pszRetCode = "PGRES_TUPLES_OK"; + sprintf(szNTuples, ", ntuples = %d", PQntuples(hResult)); + break; + case PGRES_COMMAND_OK: + pszRetCode = "PGRES_COMMAND_OK"; + break; + case PGRES_NONFATAL_ERROR: + pszRetCode = "PGRES_NONFATAL_ERROR"; + break; + case PGRES_FATAL_ERROR: + pszRetCode = "PGRES_FATAL_ERROR"; + break; + default: break; + } + } + if (bMultipleCommandAllowed) + CPLDebug("PG", "PQexec(%s) = %s%s", query, pszRetCode, szNTuples); + else + CPLDebug("PG", "PQexecParams(%s) = %s%s", query, pszRetCode, szNTuples); +#endif + +/* -------------------------------------------------------------------- */ +/* Generate an error report if an error occured. */ +/* -------------------------------------------------------------------- */ + if ( !hResult || (PQresultStatus(hResult) == PGRES_NONFATAL_ERROR || + PQresultStatus(hResult) == PGRES_FATAL_ERROR ) ) + { + if( bErrorAsDebug ) + CPLDebug("PG", "%s", PQerrorMessage( conn ) ); + else + CPLError( CE_Failure, CPLE_AppDefined, "%s", PQerrorMessage( conn ) ); + } + + return hResult; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgutility.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgutility.h new file mode 100644 index 000000000..2371fe1a1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pg/ogrpgutility.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * $Id: ogrpgutility.h 27784 2014-10-02 15:43:18Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private utilities for OGR/PostgreSQL driver. + * Author: Mateusz Loskot, mateusz@loskot.net + * + ****************************************************************************** + * Copyright (c) 2007, Mateusz Loskot + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef OGRPGUTILITY_H_INCLUDED +#define OGRPGUTILITY_H_INCLUDED + +#include "libpq-fe.h" + +PGresult *OGRPG_PQexec(PGconn *conn, const char *query, + int bMultipleCommandAllowed = FALSE, + int bErrorAsDebug = FALSE); + +/************************************************************************/ +/* OGRPGClearResult */ +/* */ +/* Safe wrapper for PQclear() function. */ +/* Releases given result and resets handle to NULL. */ +/* Parameter hResult is input/output - a reference to pointer */ +/************************************************************************/ + +inline void OGRPGClearResult( PGresult*& hResult ) +{ + if( NULL != hResult ) + { + PQclear( hResult ); + hResult = NULL; + } +} + +#endif /* ndef OGRPGUTILITY_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/GNUmakefile new file mode 100644 index 000000000..cce818e95 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrpgdumpdriver.o ogrpgdumpdatasource.o ogrpgdumplayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_pgdump.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/drv_pgdump.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/drv_pgdump.html new file mode 100644 index 000000000..4b39a7a35 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/drv_pgdump.html @@ -0,0 +1,136 @@ + + +PGDump (PostgreSQL SQL Dump) + + + + +

    PostgreSQL SQL Dump

    + +(GDAL/OGR >= 1.8.0)

    + +This write-only driver implements support for generating a SQL dump file that +can later be injected into a live PostgreSQL instance. It supports +PostgreSQL extended with the +PostGIS geometries.

    + +This driver is very similar to the PostGIS shp2pgsql utility.

    + +Most creation options are shared with the regular PostgreSQL driver.

    + +Starting with OGR 1.11, the PGDump driver supports creating tables with multiple PostGIS +geometry columns (following +RFC 41)

    + +

    Creation options

    + +

    Dataset Creation Options

    + +
      +
    • LINEFORMAT: +By default files are created with the line +termination conventions of the local platform (CR/LF on win32 or +LF on all other systems). This may be overridden through use of the +LINEFORMAT layer creation option which may have a value of CRLF +(DOS format) or LF (Unix format).

      +

    + +

    Layer Creation Options

    + +
      +
    • +GEOM_TYPE: The GEOM_TYPE layer creation option can be set to +one of "geometry" or "geography" (PostGIS >= 1.5) to force the type of geometry used for +a table. "geometry" is the default value.

      +

    • LAUNDER: This may be "YES" to force new fields created on this +layer to have their field names "laundered" into a form more compatible with +PostgreSQL. This converts to lower case and converts some special characters +like "-" and "#" to "_". If "NO" exact names are preserved. +The default value is "YES". If enabled the table (layer) name will also be laundered.

      +

    • PRECISION: This may be "YES" to force new fields created on this +layer to try and represent the width and precision information, if available +using NUMERIC(width,precision) or CHAR(width) types. If "NO" then the types +FLOAT8, INTEGER and VARCHAR will be used instead. The default is "YES".

      +

    • DIM={2,3}: Control the dimension of the layer. Defaults to 3. +Important to set to 2 for 2D layers with PostGIS 1.0+ as it has constraints +on the geometry dimension during loading.

      +

    • GEOMETRY_NAME: Set name of geometry column in new table. If +omitted it defaults to wkb_geometry for GEOM_TYPE=geometry, or the_geog for GEOM_TYPE=geography.

      +

    • SCHEMA: Set name of schema for new table. +Using the same layer name in different schemas is supported, but not in the public schema and others.

      +

    • CREATE_SCHEMA: (OGR >= 1.8.1) To be used in combination with SCHEMA. Set to ON by default so that +the CREATE SCHEMA instruction is emitted. Turn to OFF to prevent CREATE SCHEMA from being emitted.

      +

    • SPATIAL_INDEX: Set to ON by default. Creates a spatial index (GiST) on the geometry column +to speed up queries. Set to OFF to disable. (Has effect only when PostGIS is available).

      +

    • TEMPORARY: Set to OFF by default. Creates a temporary table instead of a permanent one.

      +

    • UNLOGGED: (From GDAL 2.0) Set to OFF by default. Whether to create the table as a unlogged one. +Unlogged tables are only supported since PostgreSQL 9.1, and GiST indexes used for spatial indexing since PostgreSQL 9.3.

      +

    • WRITE_EWKT_GEOM: Set to OFF by default. Turn to ON to write EWKT geometries instead of HEX geometries. +This option will have no effect if PG_USE_COPY environment variable is to YES.

      +

    • CREATE_TABLE: Set to ON by default so that tables are recreated if necessary. Turn to OFF to disable this and use existing table structure.

      +

    • DROP_TABLE=ON/OFF/IF_EXISTS: (OGR >= 1.8.1) Set to ON so that tables are destroyed before being recreated. +Set to OFF to prevent DROP TABLE from being emitted. Set to IF_EXISTS (default in GDAL 2.0) in order DROP TABLE IF EXISTS to be emitted (needs PostgreSQL >= 8.2)

      +

    • SRID: Set the SRID of the geometry. Defaults to -1, unless a SRS is associated with the layer. In the case, if the EPSG code is mentionned, it will be used as the SRID. (Note: the spatial_ref_sys table must be correctly populated with the specified SRID)

      +

    • NONE_AS_UNKNOWN: (From GDAL 1.9.0) Can bet set to TRUE to force non-spatial layers (wkbNone) to be created as +spatial tables of type GEOMETRY (wkbUnknown), which was the behaviour prior to GDAL 1.8.0. Defaults to NO, in which case +a regular table is created and not recorded in the PostGIS geometry_columns table.

      +

    • FID: (From GDAL 1.9.0) Name of the FID column to create. Defaults to 'ogc_fid'.

      +

    • FID64: (From GDAL 2.0) This may be "TRUE" to create a FID column that can support +64 bit identifiers. The default value is "FALSE".
    • +
    • EXTRACT_SCHEMA_FROM_LAYER_NAME: (From GDAL 1.9.0) Can be set to NO to avoid considering the dot character +as the separator between the schema and the table name. Defaults to YES.

      +

    • COLUMN_TYPES: (From GDAL 1.10) A list of strings of format field_name=pg_field_type (separated by comma) +that should be use when CreateField() is invoked on them. This will override the default choice that OGR would have made. +This can for example be used to create a column of type HSTORE.

      +

    • POSTGIS_VERSION: (From GDAL 1.9.0) Can be set to 2.0 for PostGIS 2.0 compatibility. Starting with GDAL 2.0, +it is important to set it correctly when dealing with non-linear geometry types.

      +

    + +

    Environment variables

    + +
      +
    • PG_USE_COPY: This may be "YES" for using COPY for inserting data to Postgresql. +COPY is significantly faster than INSERT.
    • +

    + +

    VSI Virtual File System API support

    + +(Some features below might require OGR >= 1.9.0)

    + +The driver supports rwriting to files managed by VSI Virtual File System API, which include +"regular" files, as well as files in the /vsizip/, /vsigzip/ domains.

    + +Writing to /dev/stdout or /vsistdout/ is also supported.

    + +

    Example

    + +
      +
    • +Simple translation of a shapefile into PostgreSQL into a file abc.sql. The table 'abc' will +be created with the features from abc.shp and attributes from abc.dbf. The SRID is specified. +PG_USE_COPY is set to YES to improve the peformance.

      + +

      +% ogr2ogr --config PG_USE_COPY YES -f PGDump abc.sql abc.shp -lco SRID=32631
      +
      + +
    • +Pipe the output of the PGDump driver into the psql utility. + +
      +% ogr2ogr --config PG_USE_COPY YES -f PGDump /vsistdout/ abc.shp | psql -d my_dbname -f -
      +
      + +
    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/makefile.vc new file mode 100644 index 000000000..3c9f06b6c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrpgdumpdriver.obj ogrpgdumpdatasource.obj ogrpgdumplayer.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/ogr_pgdump.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/ogr_pgdump.h new file mode 100644 index 000000000..226cf9239 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/ogr_pgdump.h @@ -0,0 +1,207 @@ +/****************************************************************************** + * $Id: ogr_pgdump.h 28988 2015-04-24 11:58:49Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for OGR/PostgreSQL dump driver. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_PGDUMP_H_INCLUDED +#define _OGR_PGDUMP_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_string.h" + +CPLString OGRPGDumpEscapeColumnName(const char* pszColumnName); +CPLString OGRPGDumpEscapeString( const char* pszStrValue, int nMaxLength = -1, + const char* pszFieldName = ""); +CPLString CPL_DLL OGRPGCommonLayerGetType(OGRFieldDefn& oField, + int bPreservePrecision, + int bApproxOK); +int CPL_DLL OGRPGCommonLayerSetType(OGRFieldDefn& oField, + const char* pszType, + const char* pszFormatType, + int nWidth); +void CPL_DLL OGRPGCommonLayerNormalizeDefault(OGRFieldDefn* poFieldDefn, + const char* pszDefault); +CPLString CPL_DLL OGRPGCommonLayerGetPGDefault(OGRFieldDefn* poFieldDefn); + +typedef CPLString (*OGRPGCommonEscapeStringCbk)(void* userdata, + const char* pszValue, + int nWidth, + const char* pszLayerName, + const char* pszFieldRef); +void CPL_DLL OGRPGCommonAppendCopyFieldsExceptGeom(CPLString& osCommand, + OGRFeature* poFeature, + const char* pszFIDColumn, + int bFIDColumnInCopyFields, + OGRPGCommonEscapeStringCbk pfnEscapeString, + void* userdata); +void CPL_DLL OGRPGCommonAppendFieldValue(CPLString& osCommand, + OGRFeature* poFeature, int i, + OGRPGCommonEscapeStringCbk pfnEscapeString, + void* userdata); + +char CPL_DLL *OGRPGCommonLaunderName( const char *pszSrcName, + const char* pszDebugPrefix = "OGR" ); + +/************************************************************************/ +/* OGRPGDumpGeomFieldDefn */ +/************************************************************************/ + +class OGRPGDumpGeomFieldDefn : public OGRGeomFieldDefn +{ + public: + OGRPGDumpGeomFieldDefn( OGRGeomFieldDefn *poGeomField ) : + OGRGeomFieldDefn(poGeomField), nSRSId(-1), nCoordDimension(2) + { + } + + int nSRSId; + int nCoordDimension; +}; + +/************************************************************************/ +/* OGRPGDumpLayer */ +/************************************************************************/ + + +class OGRPGDumpDataSource; + +class OGRPGDumpLayer : public OGRLayer +{ + char *pszSchemaName; + char *pszSqlTableName; + char *pszFIDColumn; + OGRFeatureDefn *poFeatureDefn; + OGRPGDumpDataSource *poDS; + int bLaunderColumnNames; + int bPreservePrecision; + int bUseCopy; + int bWriteAsHex; + int bCopyActive; + int bFIDColumnInCopyFields; + int bCreateTable; + int nUnknownSRSId; + int nForcedSRSId; + int bCreateSpatialIndexFlag; + int bPostGIS2; + + int iNextShapeId; + int iFIDAsRegularColumnIndex; + int bAutoFIDOnCreateViaCopy; + int bCopyStatementWithFID; + + char **papszOverrideColumnTypes; + + OGRErr StartCopy(int bSetFID); + CPLString BuildCopyFields(int bSetFID); + + public: + OGRPGDumpLayer(OGRPGDumpDataSource* poDS, + const char* pszSchemaName, + const char* pszLayerName, + const char *pszFIDColumn, + int bWriteAsHexIn, + int bCreateTable); + virtual ~OGRPGDumpLayer(); + + virtual OGRFeatureDefn *GetLayerDefn() {return poFeatureDefn;} + virtual const char* GetFIDColumn() { return pszFIDColumn; } + + virtual void ResetReading() { } + virtual int TestCapability( const char * ); + + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr CreateFeatureViaInsert( OGRFeature *poFeature ); + virtual OGRErr CreateFeatureViaCopy( OGRFeature *poFeature ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr CreateGeomField( OGRGeomFieldDefn *poGeomField, + int bApproxOK = TRUE ); + + virtual OGRFeature *GetNextFeature(); + + // follow methods are not base class overrides + void SetLaunderFlag( int bFlag ) + { bLaunderColumnNames = bFlag; } + void SetPrecisionFlag( int bFlag ) + { bPreservePrecision = bFlag; } + + void SetOverrideColumnTypes( const char* pszOverrideColumnTypes ); + void SetUnknownSRSId( int nUnknownSRSIdIn ) + { nUnknownSRSId = nUnknownSRSIdIn; } + void SetForcedSRSId( int nForcedSRSIdIn ) + { nForcedSRSId = nForcedSRSIdIn; } + void SetCreateSpatialIndexFlag( int bFlag ) + { bCreateSpatialIndexFlag = bFlag; } + void SetPostGIS2( int bFlag ) + { bPostGIS2 = bFlag; } + OGRErr EndCopy(); + + static char* GByteArrayToBYTEA( const GByte* pabyData, int nLen); +}; + +/************************************************************************/ +/* OGRPGDumpDataSource */ +/************************************************************************/ +class OGRPGDumpDataSource : public OGRDataSource +{ + int nLayers; + OGRPGDumpLayer** papoLayers; + char* pszName; + int bTriedOpen; + VSILFILE* fp; + int bInTransaction; + OGRPGDumpLayer* poLayerInCopyMode; + const char* pszEOL; + + public: + OGRPGDumpDataSource(const char* pszName, + char** papszOptions); + ~OGRPGDumpDataSource(); + + int Log(const char* pszStr, int bAddSemiColumn = TRUE); + + virtual const char *GetName() { return pszName; } + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer *GetLayer( int ); + + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + virtual int TestCapability( const char * ); + + void LogStartTransaction(); + void LogCommit(); + + void StartCopy( OGRPGDumpLayer *poPGLayer ); + OGRErr EndCopy( ); +}; + +#endif /* ndef _OGR_PGDUMP_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/ogrpgdumpdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/ogrpgdumpdatasource.cpp new file mode 100644 index 000000000..7df03bd03 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/ogrpgdumpdatasource.cpp @@ -0,0 +1,606 @@ +/****************************************************************************** + * $Id: ogrpgdumpdatasource.cpp 28988 2015-04-24 11:58:49Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGDumpDataSource class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "ogr_pgdump.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrpgdumpdatasource.cpp 28988 2015-04-24 11:58:49Z rouault $"); + +/************************************************************************/ +/* OGRPGDumpDataSource() */ +/************************************************************************/ + +OGRPGDumpDataSource::OGRPGDumpDataSource(const char* pszName, + char** papszOptions) + +{ + nLayers = 0; + papoLayers = NULL; + this->pszName = CPLStrdup(pszName); + bTriedOpen = FALSE; + fp = NULL; + bInTransaction = FALSE; + poLayerInCopyMode = NULL; + + const char *pszCRLFFormat = CSLFetchNameValue( papszOptions, "LINEFORMAT"); + + int bUseCRLF; + if( pszCRLFFormat == NULL ) + { +#ifdef WIN32 + bUseCRLF = TRUE; +#else + bUseCRLF = FALSE; +#endif + } + else if( EQUAL(pszCRLFFormat,"CRLF") ) + bUseCRLF = TRUE; + else if( EQUAL(pszCRLFFormat,"LF") ) + bUseCRLF = FALSE; + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "LINEFORMAT=%s not understood, use one of CRLF or LF.", + pszCRLFFormat ); +#ifdef WIN32 + bUseCRLF = TRUE; +#else + bUseCRLF = FALSE; +#endif + } + pszEOL = (bUseCRLF) ? "\r\n" : "\n"; +} + +/************************************************************************/ +/* ~OGRPGDumpDataSource() */ +/************************************************************************/ + +OGRPGDumpDataSource::~OGRPGDumpDataSource() + +{ + int i; + + if (fp) + { + LogCommit(); + VSIFCloseL(fp); + fp = NULL; + } + + for(i=0;i '%s'", + pszSrcName, pszSafeName); + + return pszSafeName; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRPGDumpDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ) + +{ + CPLString osCommand; + const char *pszGeomType = NULL; + char *pszTableName = NULL; + char *pszSchemaName = NULL; + int nDimension = 3; + int bHavePostGIS = TRUE; + + const char* pszFIDColumnNameIn = CSLFetchNameValue(papszOptions, "FID"); + CPLString osFIDColumnName, osFIDColumnNameEscaped; + if (pszFIDColumnNameIn == NULL) + osFIDColumnNameEscaped = osFIDColumnName = "OGC_FID"; + else + { + if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) ) + { + char* pszLaunderedFid = OGRPGCommonLaunderName(pszFIDColumnNameIn, "PGDump"); + osFIDColumnName = pszLaunderedFid; + osFIDColumnNameEscaped = OGRPGDumpEscapeColumnName(osFIDColumnName); + CPLFree(pszLaunderedFid); + } + else + { + osFIDColumnName = pszFIDColumnNameIn; + osFIDColumnNameEscaped = OGRPGDumpEscapeColumnName(osFIDColumnName); + } + } + + if (strncmp(pszLayerName, "pg", 2) == 0) + { + CPLError(CE_Warning, CPLE_AppDefined, + "The layer name should not begin by 'pg' as it is a reserved prefix"); + } + + //bHavePostGIS = CSLFetchBoolean(papszOptions,"POSTGIS", TRUE); + + int bCreateTable = CSLFetchBoolean(papszOptions,"CREATE_TABLE", TRUE); + int bCreateSchema = CSLFetchBoolean(papszOptions,"CREATE_SCHEMA", TRUE); + const char* pszDropTable = CSLFetchNameValueDef(papszOptions,"DROP_TABLE", "IF_EXISTS"); + + if( wkbFlatten(eType) == eType ) + nDimension = 2; + + if( CSLFetchNameValue( papszOptions, "DIM") != NULL ) + nDimension = atoi(CSLFetchNameValue( papszOptions, "DIM")); + + /* Should we turn layers with None geometry type as Unknown/GEOMETRY */ + /* so they are still recorded in geometry_columns table ? (#4012) */ + int bNoneAsUnknown = CSLTestBoolean(CSLFetchNameValueDef( + papszOptions, "NONE_AS_UNKNOWN", "NO")); + if (bNoneAsUnknown && eType == wkbNone) + eType = wkbUnknown; + else if (eType == wkbNone) + bHavePostGIS = FALSE; + + int bExtractSchemaFromLayerName = CSLTestBoolean(CSLFetchNameValueDef( + papszOptions, "EXTRACT_SCHEMA_FROM_LAYER_NAME", "YES")); + + /* Postgres Schema handling: + Extract schema name from input layer name or passed with -lco SCHEMA. + Set layer name to "schema.table" or to "table" if schema == current_schema() + Usage without schema name is backwards compatible + */ + const char* pszDotPos = strstr(pszLayerName,"."); + if ( pszDotPos != NULL && bExtractSchemaFromLayerName ) + { + int length = pszDotPos - pszLayerName; + pszSchemaName = (char*)CPLMalloc(length+1); + strncpy(pszSchemaName, pszLayerName, length); + pszSchemaName[length] = '\0'; + + if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) ) + pszTableName = OGRPGCommonLaunderName( pszDotPos + 1, "PGDump" ); //skip "." + else + pszTableName = CPLStrdup( pszDotPos + 1 ); //skip "." + } + else + { + pszSchemaName = NULL; + if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) ) + pszTableName = OGRPGCommonLaunderName( pszLayerName, "PGDump" ); //skip "." + else + pszTableName = CPLStrdup( pszLayerName ); //skip "." + } + + LogCommit(); + +/* -------------------------------------------------------------------- */ +/* Set the default schema for the layers. */ +/* -------------------------------------------------------------------- */ + if( CSLFetchNameValue( papszOptions, "SCHEMA" ) != NULL ) + { + CPLFree(pszSchemaName); + pszSchemaName = CPLStrdup(CSLFetchNameValue( papszOptions, "SCHEMA" )); + if (bCreateSchema) + { + osCommand.Printf("CREATE SCHEMA \"%s\"", pszSchemaName); + Log(osCommand); + } + } + + if ( pszSchemaName == NULL) + { + pszSchemaName = CPLStrdup("public"); + } + +/* -------------------------------------------------------------------- */ +/* Do we already have this layer? */ +/* -------------------------------------------------------------------- */ + int iLayer; + + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName,papoLayers[iLayer]->GetLayerDefn()->GetName()) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n", + pszLayerName ); + CPLFree( pszTableName ); + CPLFree( pszSchemaName ); + return NULL; + } + } + + + if (bCreateTable && (EQUAL(pszDropTable, "YES") || + EQUAL(pszDropTable, "ON") || + EQUAL(pszDropTable, "TRUE") || + EQUAL(pszDropTable, "IF_EXISTS"))) + { + if (EQUAL(pszDropTable, "IF_EXISTS")) + osCommand.Printf("DROP TABLE IF EXISTS \"%s\".\"%s\" CASCADE", pszSchemaName, pszTableName ); + else + osCommand.Printf("DROP TABLE \"%s\".\"%s\" CASCADE", pszSchemaName, pszTableName ); + Log(osCommand); + } + +/* -------------------------------------------------------------------- */ +/* Handle the GEOM_TYPE option. */ +/* -------------------------------------------------------------------- */ + pszGeomType = CSLFetchNameValue( papszOptions, "GEOM_TYPE" ); + if( pszGeomType == NULL ) + { + pszGeomType = "geometry"; + } + + if( !EQUAL(pszGeomType,"geometry") && !EQUAL(pszGeomType, "geography")) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GEOM_TYPE in PostGIS enabled databases must be 'geometry' or 'geography'.\n" + "Creation of layer %s with GEOM_TYPE %s has failed.", + pszLayerName, pszGeomType ); + CPLFree( pszTableName ); + CPLFree( pszSchemaName ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to get the SRS Id of this spatial reference system, */ +/* adding tot the srs table if needed. */ +/* -------------------------------------------------------------------- */ + int nUnknownSRSId = -1; + const char* pszPostgisVersion = CSLFetchNameValue( papszOptions, "POSTGIS_VERSION" ); + int bPostGIS2 = FALSE; + if( pszPostgisVersion != NULL && atoi(pszPostgisVersion) >= 2 ) + { + bPostGIS2 = TRUE; + nUnknownSRSId = 0; + } + + int nSRSId = nUnknownSRSId; + int nForcedSRSId = -2; + if( CSLFetchNameValue( papszOptions, "SRID") != NULL ) + { + nSRSId = atoi(CSLFetchNameValue( papszOptions, "SRID")); + nForcedSRSId = nSRSId; + } + else + { + if (poSRS) + { + const char* pszAuthorityName = poSRS->GetAuthorityName(NULL); + if( pszAuthorityName != NULL && EQUAL( pszAuthorityName, "EPSG" ) ) + { + /* Assume the EPSG Id is the SRS ID. Might be a wrong guess ! */ + nSRSId = atoi( poSRS->GetAuthorityCode(NULL) ); + } + else + { + const char* pszGeogCSName = poSRS->GetAttrValue("GEOGCS"); + if (pszGeogCSName != NULL && EQUAL(pszGeogCSName, "GCS_WGS_1984")) + nSRSId = 4326; + } + } + } + + CPLString osEscapedTableNameSingleQuote = OGRPGDumpEscapeString(pszTableName); + const char* pszEscapedTableNameSingleQuote = osEscapedTableNameSingleQuote.c_str(); + + const char *pszGeometryType = OGRToOGCGeomType(eType); + + const char *pszGFldName = NULL; + if( bHavePostGIS && !EQUAL(pszGeomType, "geography")) + { + if( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL ) + pszGFldName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME"); + else + pszGFldName = "wkb_geometry"; + + if( pszPostgisVersion == NULL || atoi(pszPostgisVersion) < 2 ) + { + /* Sometimes there is an old cruft entry in the geometry_columns + * table if things were not properly cleaned up before. We make + * an effort to clean out such cruft. + * Note: PostGIS 2.0 defines geometry_columns as a view (no clean up is needed) + */ + osCommand.Printf( + "DELETE FROM geometry_columns WHERE f_table_name = %s AND f_table_schema = '%s'", + pszEscapedTableNameSingleQuote, pszSchemaName ); + if (bCreateTable) + Log(osCommand); + } + } + + + LogStartTransaction(); + +/* -------------------------------------------------------------------- */ +/* Create a basic table with the FID. Also include the */ +/* geometry if this is not a PostGIS enabled table. */ +/* -------------------------------------------------------------------- */ + int bFID64 = CSLFetchBoolean(papszOptions, "FID64", FALSE); + const char* pszSerialType = bFID64 ? "BIGSERIAL": "SERIAL"; + + CPLString osCreateTable; + int bTemporary = CSLFetchBoolean( papszOptions, "TEMPORARY", FALSE ); + if (bTemporary) + { + CPLFree(pszSchemaName); + pszSchemaName = CPLStrdup("pg_temp_1"); + osCreateTable.Printf("CREATE TEMPORARY TABLE \"%s\"", pszTableName); + } + else + osCreateTable.Printf("CREATE TABLE%s \"%s\".\"%s\"", + CSLFetchBoolean( papszOptions, "UNLOGGED", FALSE ) ? " UNLOGGED": "", + pszSchemaName, pszTableName); + + if( !bHavePostGIS ) + { + if (eType == wkbNone) + osCommand.Printf( + "%s ( " + " %s %s, " + " CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )", + osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(), pszSerialType, pszTableName, osFIDColumnNameEscaped.c_str() ); + else + osCommand.Printf( + "%s ( " + " %s %s, " + " WKB_GEOMETRY %s, " + " CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )", + osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(), pszSerialType, pszGeomType, pszTableName, osFIDColumnNameEscaped.c_str() ); + } + else if ( EQUAL(pszGeomType, "geography") ) + { + if( CSLFetchNameValue( papszOptions, "GEOMETRY_NAME") != NULL ) + pszGFldName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME"); + else + pszGFldName = "the_geog"; + + if (nSRSId) + osCommand.Printf( + "%s ( %s %s, \"%s\" geography(%s%s,%d), CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )", + osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(), pszSerialType, pszGFldName, pszGeometryType, nDimension == 2 ? "" : "Z", nSRSId, pszTableName, osFIDColumnNameEscaped.c_str() ); + else + osCommand.Printf( + "%s ( %s %s, \"%s\" geography(%s%s), CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )", + osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(), pszSerialType, pszGFldName, pszGeometryType, nDimension == 2 ? "" : "Z", pszTableName, osFIDColumnNameEscaped.c_str() ); + } + else + { + osCommand.Printf( + "%s ( %s %s, CONSTRAINT \"%s_pk\" PRIMARY KEY (%s) )", + osCreateTable.c_str(), osFIDColumnNameEscaped.c_str(), pszSerialType, pszTableName, osFIDColumnNameEscaped.c_str() ); + } + + if (bCreateTable) + Log(osCommand); + +/* -------------------------------------------------------------------- */ +/* Eventually we should be adding this table to a table of */ +/* "geometric layers", capturing the WKT projection, and */ +/* perhaps some other housekeeping. */ +/* -------------------------------------------------------------------- */ + if( bCreateTable && bHavePostGIS && !EQUAL(pszGeomType, "geography")) + { + osCommand.Printf( + "SELECT AddGeometryColumn('%s',%s,'%s',%d,'%s',%d)", + pszSchemaName, pszEscapedTableNameSingleQuote, pszGFldName, + nSRSId, pszGeometryType, nDimension ); + Log(osCommand); + } + + const char *pszSI = CSLFetchNameValue( papszOptions, "SPATIAL_INDEX" ); + int bCreateSpatialIndex = ( pszSI == NULL || CSLTestBoolean(pszSI) ); + if( bCreateTable && bHavePostGIS && bCreateSpatialIndex ) + { +/* -------------------------------------------------------------------- */ +/* Create the spatial index. */ +/* */ +/* We're doing this before we add geometry and record to the table */ +/* so this may not be exactly the best way to do it. */ +/* -------------------------------------------------------------------- */ + osCommand.Printf("CREATE INDEX \"%s_%s_geom_idx\" " + "ON \"%s\".\"%s\" " + "USING GIST (\"%s\")", + pszTableName, pszGFldName, pszSchemaName, pszTableName, pszGFldName); + + Log(osCommand); + } + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRPGDumpLayer *poLayer; + + int bWriteAsHex = !CSLFetchBoolean(papszOptions,"WRITE_EWKT_GEOM",FALSE); + + poLayer = new OGRPGDumpLayer( this, pszSchemaName, pszTableName, + osFIDColumnName, bWriteAsHex, bCreateTable ); + poLayer->SetLaunderFlag( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) ); + poLayer->SetPrecisionFlag( CSLFetchBoolean(papszOptions,"PRECISION",TRUE)); + + const char* pszOverrideColumnTypes = CSLFetchNameValue( papszOptions, "COLUMN_TYPES" ); + poLayer->SetOverrideColumnTypes(pszOverrideColumnTypes); + poLayer->SetUnknownSRSId(nUnknownSRSId); + poLayer->SetForcedSRSId(nForcedSRSId); + poLayer->SetCreateSpatialIndexFlag(bCreateSpatialIndex); + poLayer->SetPostGIS2(bPostGIS2); + + if( bHavePostGIS ) + { + OGRGeomFieldDefn oTmp( pszGFldName, eType ); + OGRPGDumpGeomFieldDefn *poGeomField = + new OGRPGDumpGeomFieldDefn(&oTmp); + poGeomField->nSRSId = nSRSId; + poGeomField->nCoordDimension = nDimension; + poLayer->GetLayerDefn()->AddGeomFieldDefn(poGeomField, FALSE); + } + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRPGDumpLayer **) + CPLRealloc( papoLayers, sizeof(OGRPGDumpLayer *) * (nLayers+1) ); + + papoLayers[nLayers++] = poLayer; + + CPLFree( pszTableName ); + CPLFree( pszSchemaName ); + + return poLayer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPGDumpDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCCreateGeomFieldAfterCreateLayer) ) + return TRUE; + else if( EQUAL(pszCap,ODsCCurveGeometries) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRPGDumpDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* Log() */ +/************************************************************************/ + +int OGRPGDumpDataSource::Log(const char* pszStr, int bAddSemiColumn) +{ + if (fp == NULL) + { + if (bTriedOpen) + return FALSE; + bTriedOpen = TRUE; + fp = VSIFOpenL(pszName, "wb"); + if (fp == NULL) + { + CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszName); + return FALSE; + } + } + + if (bAddSemiColumn) + VSIFPrintfL(fp, "%s;%s", pszStr, pszEOL); + else + VSIFPrintfL(fp, "%s%s", pszStr, pszEOL); + return TRUE; +} + +/************************************************************************/ +/* StartCopy() */ +/************************************************************************/ +void OGRPGDumpDataSource::StartCopy( OGRPGDumpLayer *poPGLayer ) +{ + EndCopy(); + poLayerInCopyMode = poPGLayer; +} + +/************************************************************************/ +/* EndCopy() */ +/************************************************************************/ +OGRErr OGRPGDumpDataSource::EndCopy( ) +{ + if( poLayerInCopyMode != NULL ) + { + OGRErr result = poLayerInCopyMode->EndCopy(); + poLayerInCopyMode = NULL; + + return result; + } + else + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/ogrpgdumpdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/ogrpgdumpdriver.cpp new file mode 100644 index 000000000..dd8aaae45 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/ogrpgdumpdriver.cpp @@ -0,0 +1,140 @@ +/****************************************************************************** + * $Id: ogrpgdumpdriver.cpp 28908 2015-04-15 09:01:01Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGDumpDriver class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_pgdump.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrpgdumpdriver.cpp 28908 2015-04-15 09:01:01Z rouault $"); + +/************************************************************************/ +/* OGRPGDumpDriverCreate() */ +/************************************************************************/ + +static GDALDataset* OGRPGDumpDriverCreate( const char * pszName, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED int nBands, + CPL_UNUSED GDALDataType eDT, + char ** papszOptions ) +{ + OGRPGDumpDataSource *poDS; + + if (strcmp(pszName, "/dev/stdout") == 0) + pszName = "/vsistdout/"; + + poDS = new OGRPGDumpDataSource(pszName, papszOptions); + if( !poDS->Log("SET standard_conforming_strings = OFF") ) + { + delete poDS; + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRPGDump() */ +/************************************************************************/ + +void RegisterOGRPGDump() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "PGDUMP" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "PGDUMP" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "PostgreSQL SQL dump" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_pgdump.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "sql" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, + "" + #ifdef WIN32 + " " + ""); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, + "" + " " + " " + " "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime Time IntegerList Integer64List RealList StringList Binary" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_DEFAULT_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnCreate = OGRPGDumpDriverCreate; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/ogrpgdumplayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/ogrpgdumplayer.cpp new file mode 100644 index 000000000..fa07f1f4f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgdump/ogrpgdumplayer.cpp @@ -0,0 +1,1673 @@ +/****************************************************************************** + * $Id: ogrpgdumplayer.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGDumpLayer class + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_pgdump.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrpgdumplayer.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +#define USE_COPY_UNSET -1 + +static CPLString OGRPGDumpEscapeStringList( + char** papszItems, int bForInsertOrUpdate, + OGRPGCommonEscapeStringCbk pfnEscapeString, + void* userdata); + +static CPLString OGRPGDumpEscapeStringWithUserData( void* user_data, + const char* pszStrValue, int nMaxLength, + const char* pszLayerName, + const char* pszFieldName) +{ + return OGRPGDumpEscapeString(pszStrValue, nMaxLength, pszFieldName); +} + +/************************************************************************/ +/* OGRPGDumpLayer() */ +/************************************************************************/ + +OGRPGDumpLayer::OGRPGDumpLayer(OGRPGDumpDataSource* poDS, + const char* pszSchemaName, + const char* pszTableName, + const char *pszFIDColumn, + int bWriteAsHexIn, + int bCreateTable) +{ + this->poDS = poDS; + poFeatureDefn = new OGRFeatureDefn( pszTableName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->SetGeomType(wkbNone); + poFeatureDefn->Reference(); + pszSqlTableName = CPLStrdup(CPLString().Printf("%s.%s", + OGRPGDumpEscapeColumnName(pszSchemaName).c_str(), + OGRPGDumpEscapeColumnName(pszTableName).c_str() )); + this->pszSchemaName = CPLStrdup(pszSchemaName); + this->pszFIDColumn = CPLStrdup(pszFIDColumn); + this->bCreateTable = bCreateTable; + bLaunderColumnNames = TRUE; + bPreservePrecision = TRUE; + bUseCopy = USE_COPY_UNSET; + bFIDColumnInCopyFields = FALSE; + bWriteAsHex = bWriteAsHexIn; + bCopyActive = FALSE; + papszOverrideColumnTypes = NULL; + nUnknownSRSId = -1; + nForcedSRSId = -2; + bCreateSpatialIndexFlag = TRUE; + bPostGIS2 = FALSE; + iNextShapeId = 0; + iFIDAsRegularColumnIndex = -1; + bAutoFIDOnCreateViaCopy = TRUE; + bCopyStatementWithFID = FALSE; +} + +/************************************************************************/ +/* ~OGRPGDumpLayer() */ +/************************************************************************/ + +OGRPGDumpLayer::~OGRPGDumpLayer() +{ + EndCopy(); + + poFeatureDefn->Release(); + CPLFree(pszSchemaName); + CPLFree(pszSqlTableName); + CPLFree(pszFIDColumn); + CSLDestroy(papszOverrideColumnTypes); +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRPGDumpLayer::GetNextFeature() +{ + CPLError(CE_Failure, CPLE_NotSupported, "PGDump driver is write only"); + return NULL; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +int OGRPGDumpLayer::TestCapability( const char * pszCap ) +{ + if( EQUAL(pszCap,OLCSequentialWrite) || + EQUAL(pszCap,OLCCreateField) || + EQUAL(pszCap,OLCCreateGeomField) || + EQUAL(pszCap,OLCCurveGeometries) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRErr OGRPGDumpLayer::ICreateFeature( OGRFeature *poFeature ) +{ + if( NULL == poFeature ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "NULL pointer to OGRFeature passed to CreateFeature()." ); + return OGRERR_FAILURE; + } + + /* In case the FID column has also been created as a regular field */ + if( iFIDAsRegularColumnIndex >= 0 ) + { + if( poFeature->GetFID() == OGRNullFID ) + { + if( poFeature->IsFieldSet( iFIDAsRegularColumnIndex ) ) + { + poFeature->SetFID( + poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex)); + } + } + else + { + if( !poFeature->IsFieldSet( iFIDAsRegularColumnIndex ) || + poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) != poFeature->GetFID() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Inconsistent values of FID and field of same name"); + return CE_Failure; + } + } + } + + if( !poFeature->Validate(OGR_F_VAL_ALL & ~OGR_F_VAL_WIDTH, TRUE ) ) + return OGRERR_FAILURE; + + // We avoid testing the config option too often. + if( bUseCopy == USE_COPY_UNSET ) + bUseCopy = CSLTestBoolean( CPLGetConfigOption( "PG_USE_COPY", "NO") ); + + OGRErr eErr; + if( !bUseCopy ) + { + eErr = CreateFeatureViaInsert( poFeature ); + } + else + { + /* If there's a unset field with a default value, then we must use */ + /* a specific INSERT statement to avoid unset fields to be bound to NULL */ + int bHasDefaultValue = FALSE; + int iField; + int nFieldCount = poFeatureDefn->GetFieldCount(); + for( iField = 0; iField < nFieldCount; iField++ ) + { + if( !poFeature->IsFieldSet( iField ) && + poFeature->GetFieldDefnRef(iField)->GetDefault() != NULL ) + { + bHasDefaultValue = TRUE; + break; + } + } + if( bHasDefaultValue ) + { + EndCopy(); + eErr = CreateFeatureViaInsert( poFeature ); + } + else + { + int bFIDSet = (poFeature->GetFID() != OGRNullFID); + if( bCopyActive && bFIDSet != bCopyStatementWithFID ) + { + EndCopy(); + eErr = CreateFeatureViaInsert( poFeature ); + } + else + { + if ( !bCopyActive ) + { + /* This is a heuristics. If the first feature to be copied has a */ + /* FID set (and that a FID column has been identified), then we will */ + /* try to copy FID values from features. Otherwise, we will not */ + /* do and assume that the FID column is an autoincremented column. */ + StartCopy(bFIDSet); + bCopyStatementWithFID = bFIDSet; + } + + eErr = CreateFeatureViaCopy( poFeature ); + if( bFIDSet ) + bAutoFIDOnCreateViaCopy = FALSE; + if( eErr == CE_None && bAutoFIDOnCreateViaCopy ) + { + poFeature->SetFID( ++iNextShapeId ); + } + } + } + } + + if( eErr == CE_None && iFIDAsRegularColumnIndex >= 0 ) + { + poFeature->SetField(iFIDAsRegularColumnIndex, poFeature->GetFID()); + } + return eErr; +} + +/************************************************************************/ +/* CreateFeatureViaInsert() */ +/************************************************************************/ + +OGRErr OGRPGDumpLayer::CreateFeatureViaInsert( OGRFeature *poFeature ) + +{ + CPLString osCommand; + int i = 0; + int bNeedComma = FALSE; + OGRErr eErr = OGRERR_FAILURE; + int bEmptyInsert = FALSE; + + if( NULL == poFeature ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "NULL pointer to OGRFeature passed to CreateFeatureViaInsert()." ); + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Form the INSERT command. */ +/* -------------------------------------------------------------------- */ + osCommand.Printf( "INSERT INTO %s (", pszSqlTableName ); + + for(i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i); + if( poGeom != NULL ) + { + if( bNeedComma ) + osCommand += ", "; + + OGRGeomFieldDefn* poGFldDefn = poFeature->GetGeomFieldDefnRef(i); + osCommand = osCommand + OGRPGDumpEscapeColumnName(poGFldDefn->GetNameRef()) + " "; + bNeedComma = TRUE; + } + } + + if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL ) + { + if( bNeedComma ) + osCommand += ", "; + + osCommand = osCommand + OGRPGDumpEscapeColumnName(pszFIDColumn) + " "; + bNeedComma = TRUE; + } + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if( i == iFIDAsRegularColumnIndex ) + continue; + if( !poFeature->IsFieldSet( i ) ) + continue; + + if( !bNeedComma ) + bNeedComma = TRUE; + else + osCommand += ", "; + + osCommand = osCommand + + OGRPGDumpEscapeColumnName(poFeatureDefn->GetFieldDefn(i)->GetNameRef()); + } + + if (!bNeedComma) + bEmptyInsert = TRUE; + + osCommand += ") VALUES ("; + + /* Set the geometry */ + bNeedComma = FALSE; + for(i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRGeometry *poGeom = poFeature->GetGeomFieldRef(i); + if( poGeom != NULL ) + { + char *pszWKT = NULL; + + OGRPGDumpGeomFieldDefn* poGFldDefn = + (OGRPGDumpGeomFieldDefn*) poFeature->GetGeomFieldDefnRef(i); + + poGeom->closeRings(); + poGeom->setCoordinateDimension( poGFldDefn->nCoordDimension ); + + if( bNeedComma ) + osCommand += ", "; + + if( bWriteAsHex ) + { + char* pszHex = OGRGeometryToHexEWKB( poGeom, poGFldDefn->nSRSId, + bPostGIS2 ); + osCommand += "'"; + if (pszHex) + osCommand += pszHex; + osCommand += "'"; + CPLFree(pszHex); + } + else + { + poGeom->exportToWkt( &pszWKT ); + + if( pszWKT != NULL ) + { + osCommand += + CPLString().Printf( + "GeomFromEWKT('SRID=%d;%s'::TEXT) ", poGFldDefn->nSRSId, pszWKT ); + OGRFree( pszWKT ); + } + else + osCommand += "''"; + } + + bNeedComma = TRUE; + } + } + + /* Set the FID */ + if( poFeature->GetFID() != OGRNullFID && pszFIDColumn != NULL ) + { + if( bNeedComma ) + osCommand += ", "; + osCommand += CPLString().Printf( CPL_FRMT_GIB, poFeature->GetFID() ); + bNeedComma = TRUE; + } + + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if( i == iFIDAsRegularColumnIndex ) + continue; + if( !poFeature->IsFieldSet( i ) ) + continue; + + if( bNeedComma ) + osCommand += ", "; + else + bNeedComma = TRUE; + + OGRPGCommonAppendFieldValue(osCommand, poFeature, i, + OGRPGDumpEscapeStringWithUserData, NULL); + } + + osCommand += ")"; + + if (bEmptyInsert) + osCommand.Printf( "INSERT INTO %s DEFAULT VALUES", pszSqlTableName ); + +/* -------------------------------------------------------------------- */ +/* Execute the insert. */ +/* -------------------------------------------------------------------- */ + poDS->Log(osCommand); + + if( poFeature->GetFID() == OGRNullFID ) + poFeature->SetFID( ++iNextShapeId ); + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* CreateFeatureViaCopy() */ +/************************************************************************/ + +OGRErr OGRPGDumpLayer::CreateFeatureViaCopy( OGRFeature *poFeature ) +{ + int i; + CPLString osCommand; + + /* First process geometry */ + for( i = 0; i < poFeature->GetGeomFieldCount(); i++ ) + { + OGRGeometry *poGeometry = poFeature->GetGeomFieldRef(i); + char *pszGeom = NULL; + if ( NULL != poGeometry /* && (bHasWkb || bHasPostGISGeometry || bHasPostGISGeography) */) + { + OGRPGDumpGeomFieldDefn* poGFldDefn = + (OGRPGDumpGeomFieldDefn*) poFeature->GetGeomFieldDefnRef(i); + + poGeometry->closeRings(); + poGeometry->setCoordinateDimension( poGFldDefn->nCoordDimension ); + + //CheckGeomTypeCompatibility(poGeometry); + + /*if (bHasWkb) + pszGeom = GeometryToBYTEA( poGeometry ); + else*/ + pszGeom = OGRGeometryToHexEWKB( poGeometry, poGFldDefn->nSRSId, + bPostGIS2 ); + } + + if (osCommand.size() > 0) + osCommand += "\t"; + if ( pszGeom ) + { + osCommand += pszGeom, + CPLFree( pszGeom ); + } + else + { + osCommand += "\\N"; + } + } + + OGRPGCommonAppendCopyFieldsExceptGeom(osCommand, + poFeature, + pszFIDColumn, + bFIDColumnInCopyFields, + OGRPGDumpEscapeStringWithUserData, + NULL); + + /* Add end of line marker */ + //osCommand += "\n"; + + + /* ------------------------------------------------------------ */ + /* Execute the copy. */ + /* ------------------------------------------------------------ */ + + OGRErr result = OGRERR_NONE; + + poDS->Log(osCommand, FALSE); + + return result; +} + +/************************************************************************/ +/* OGRPGCommonAppendCopyFieldsExceptGeom() */ +/************************************************************************/ + +void OGRPGCommonAppendCopyFieldsExceptGeom(CPLString& osCommand, + OGRFeature* poFeature, + const char* pszFIDColumn, + int bFIDColumnInCopyFields, + OGRPGCommonEscapeStringCbk pfnEscapeString, + void* userdata) +{ + int i; + OGRFeatureDefn* poFeatureDefn = poFeature->GetDefnRef(); + + /* Next process the field id column */ + int nFIDIndex = -1; + if( bFIDColumnInCopyFields ) + { + if (osCommand.size() > 0) + osCommand += "\t"; + + nFIDIndex = poFeatureDefn->GetFieldIndex( pszFIDColumn ); + + /* Set the FID */ + if( poFeature->GetFID() != OGRNullFID ) + { + osCommand += CPLString().Printf( CPL_FRMT_GIB, poFeature->GetFID()); + } + else + { + osCommand += "\\N" ; + } + } + + + /* Now process the remaining fields */ + + int nFieldCount = poFeatureDefn->GetFieldCount(); + int bAddTab = osCommand.size() > 0; + + for( i = 0; i < nFieldCount; i++ ) + { + if (i == nFIDIndex) + continue; + + const char *pszStrValue = poFeature->GetFieldAsString(i); + char *pszNeedToFree = NULL; + + if (bAddTab) + osCommand += "\t"; + bAddTab = TRUE; + + if( !poFeature->IsFieldSet( i ) ) + { + osCommand += "\\N" ; + + continue; + } + + int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType(); + + // We need special formatting for integer list values. + if( nOGRFieldType == OFTIntegerList ) + { + int nCount, nOff = 0, j; + const int *panItems = poFeature->GetFieldAsIntegerList(i,&nCount); + + pszNeedToFree = (char *) CPLMalloc(nCount * 13 + 10); + strcpy( pszNeedToFree, "{" ); + for( j = 0; j < nCount; j++ ) + { + if( j != 0 ) + strcat( pszNeedToFree+nOff, "," ); + + nOff += strlen(pszNeedToFree+nOff); + sprintf( pszNeedToFree+nOff, "%d", panItems[j] ); + } + strcat( pszNeedToFree+nOff, "}" ); + pszStrValue = pszNeedToFree; + } + + else if( nOGRFieldType == OFTInteger64List ) + { + int nCount, nOff = 0, j; + const GIntBig *panItems = poFeature->GetFieldAsInteger64List(i,&nCount); + + pszNeedToFree = (char *) CPLMalloc(nCount * 26 + 10); + strcpy( pszNeedToFree, "{" ); + for( j = 0; j < nCount; j++ ) + { + if( j != 0 ) + strcat( pszNeedToFree+nOff, "," ); + + nOff += strlen(pszNeedToFree+nOff); + sprintf( pszNeedToFree+nOff, CPL_FRMT_GIB, panItems[j] ); + } + strcat( pszNeedToFree+nOff, "}" ); + pszStrValue = pszNeedToFree; + } + + // We need special formatting for real list values. + else if( nOGRFieldType == OFTRealList ) + { + int nCount, nOff = 0, j; + const double *padfItems =poFeature->GetFieldAsDoubleList(i,&nCount); + + pszNeedToFree = (char *) CPLMalloc(nCount * 40 + 10); + strcpy( pszNeedToFree, "{" ); + for( j = 0; j < nCount; j++ ) + { + if( j != 0 ) + strcat( pszNeedToFree+nOff, "," ); + + nOff += strlen(pszNeedToFree+nOff); + //Check for special values. They need to be quoted. + if( CPLIsNan(padfItems[j]) ) + sprintf( pszNeedToFree+nOff, "NaN" ); + else if( CPLIsInf(padfItems[j]) ) + sprintf( pszNeedToFree+nOff, (padfItems[j] > 0) ? "Infinity" : "-Infinity" ); + else + CPLsprintf( pszNeedToFree+nOff, "%.16g", padfItems[j] ); + + } + strcat( pszNeedToFree+nOff, "}" ); + pszStrValue = pszNeedToFree; + } + + + // We need special formatting for string list values. + else if( nOGRFieldType == OFTStringList ) + { + CPLString osStr; + char **papszItems = poFeature->GetFieldAsStringList(i); + + pszStrValue = pszNeedToFree = CPLStrdup( + OGRPGDumpEscapeStringList(papszItems, FALSE, + pfnEscapeString, userdata)); + } + + // Binary formatting + else if( nOGRFieldType == OFTBinary ) + { + int nLen = 0; + GByte* pabyData = poFeature->GetFieldAsBinary( i, &nLen ); + char* pszBytea = OGRPGDumpLayer::GByteArrayToBYTEA( pabyData, nLen); + + pszStrValue = pszNeedToFree = pszBytea; + } + + else if( nOGRFieldType == OFTReal ) + { + //Check for special values. They need to be quoted. + double dfVal = poFeature->GetFieldAsDouble(i); + if( CPLIsNan(dfVal) ) + pszStrValue = "NaN"; + else if( CPLIsInf(dfVal) ) + pszStrValue = (dfVal > 0) ? "Infinity" : "-Infinity"; + } + + if( nOGRFieldType != OFTIntegerList && + nOGRFieldType != OFTInteger64List && + nOGRFieldType != OFTRealList && + nOGRFieldType != OFTInteger && + nOGRFieldType != OFTInteger64 && + nOGRFieldType != OFTReal && + nOGRFieldType != OFTBinary ) + { + int iChar; + int iUTFChar = 0; + int nMaxWidth = poFeatureDefn->GetFieldDefn(i)->GetWidth(); + + for( iChar = 0; pszStrValue[iChar] != '\0'; iChar++ ) + { + //count of utf chars + if ((pszStrValue[iChar] & 0xc0) != 0x80) + { + if( nMaxWidth > 0 && iUTFChar == nMaxWidth ) + { + CPLDebug( "PG", + "Truncated %s field value, it was too long.", + poFeatureDefn->GetFieldDefn(i)->GetNameRef() ); + break; + } + iUTFChar++; + } + + /* Escape embedded \, \t, \n, \r since they will cause COPY + to misinterpret a line of text and thus abort */ + if( pszStrValue[iChar] == '\\' || + pszStrValue[iChar] == '\t' || + pszStrValue[iChar] == '\r' || + pszStrValue[iChar] == '\n' ) + { + osCommand += '\\'; + } + + osCommand += pszStrValue[iChar]; + } + } + else + { + osCommand += pszStrValue; + } + + if( pszNeedToFree ) + CPLFree( pszNeedToFree ); + } +} + +/************************************************************************/ +/* StartCopy() */ +/************************************************************************/ + +OGRErr OGRPGDumpLayer::StartCopy(int bSetFID) + +{ + /* Tell the datasource we are now planning to copy data */ + poDS->StartCopy( this ); + + CPLString osFields = BuildCopyFields(bSetFID); + + int size = strlen(osFields) + strlen(pszSqlTableName) + 100; + char *pszCommand = (char *) CPLMalloc(size); + + sprintf( pszCommand, + "COPY %s (%s) FROM STDIN", + pszSqlTableName, osFields.c_str() ); + + poDS->Log(pszCommand); + bCopyActive = TRUE; + + CPLFree( pszCommand ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* EndCopy() */ +/************************************************************************/ + +OGRErr OGRPGDumpLayer::EndCopy() + +{ + if( !bCopyActive ) + return OGRERR_NONE; + + bCopyActive = FALSE; + + poDS->Log("\\.", FALSE); + poDS->Log("END"); + + bUseCopy = USE_COPY_UNSET; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* BuildCopyFields() */ +/************************************************************************/ + +CPLString OGRPGDumpLayer::BuildCopyFields(int bSetFID) +{ + int i = 0; + int nFIDIndex = -1; + CPLString osFieldList; + + for( i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + if( osFieldList.size() > 0 ) + osFieldList += ", "; + + OGRGeomFieldDefn* poGFldDefn = poFeatureDefn->GetGeomFieldDefn(i); + + osFieldList += OGRPGDumpEscapeColumnName(poGFldDefn->GetNameRef()); + } + + bFIDColumnInCopyFields = (pszFIDColumn != NULL && bSetFID); + if( bFIDColumnInCopyFields ) + { + if( osFieldList.size() > 0 ) + osFieldList += ", "; + + nFIDIndex = poFeatureDefn->GetFieldIndex( pszFIDColumn ); + + osFieldList += OGRPGDumpEscapeColumnName(pszFIDColumn); + } + + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if (i == nFIDIndex) + continue; + + const char *pszName = poFeatureDefn->GetFieldDefn(i)->GetNameRef(); + + if( osFieldList.size() > 0 ) + osFieldList += ", "; + + osFieldList += OGRPGDumpEscapeColumnName(pszName); + } + + return osFieldList; +} + +/************************************************************************/ +/* OGRPGDumpEscapeColumnName( ) */ +/************************************************************************/ + +CPLString OGRPGDumpEscapeColumnName(const char* pszColumnName) +{ + CPLString osStr; + + osStr += "\""; + + char ch; + for(int i=0; (ch = pszColumnName[i]) != '\0'; i++) + { + if (ch == '"') + osStr.append(1, ch); + osStr.append(1, ch); + } + + osStr += "\""; + + return osStr; +} + +/************************************************************************/ +/* EscapeString( ) */ +/************************************************************************/ + +CPLString OGRPGDumpEscapeString( + const char* pszStrValue, int nMaxLength, + const char* pszFieldName) +{ + CPLString osCommand; + + /* We need to quote and escape string fields. */ + osCommand += "'"; + + int nSrcLen = strlen(pszStrValue); + int nSrcLenUTF = CPLStrlenUTF8(pszStrValue); + + if (nMaxLength > 0 && nSrcLenUTF > nMaxLength) + { + CPLDebug( "PG", + "Truncated %s field value, it was too long.", + pszFieldName ); + + int iUTF8Char = 0; + for(int iChar = 0; iChar < nSrcLen; iChar++ ) + { + if( (((unsigned char *) pszStrValue)[iChar] & 0xc0) != 0x80 ) + { + if( iUTF8Char == nMaxLength ) + { + nSrcLen = iChar; + break; + } + iUTF8Char ++; + } + } + } + + char* pszDestStr = (char*)CPLMalloc(2 * nSrcLen + 1); + + /* -------------------------------------------------------------------- */ + /* PQescapeStringConn was introduced in PostgreSQL security releases */ + /* 8.1.4, 8.0.8, 7.4.13, 7.3.15 */ + /* PG_HAS_PQESCAPESTRINGCONN is added by a test in 'configure' */ + /* so it is not set by default when building OGR for Win32 */ + /* -------------------------------------------------------------------- */ +#if defined(PG_HAS_PQESCAPESTRINGCONN) + int nError; + PQescapeStringConn (hPGConn, pszDestStr, pszStrValue, nSrcLen, &nError); + if (nError == 0) + osCommand += pszDestStr; + else + CPLError(CE_Warning, CPLE_AppDefined, + "PQescapeString(): %s\n" + " input: '%s'\n" + " got: '%s'\n", + PQerrorMessage( hPGConn ), + pszStrValue, pszDestStr ); +#else + //PQescapeString(pszDestStr, pszStrValue, nSrcLen); + + int i, j; + for(i=0,j=0; i < nSrcLen; i++) + { + if (pszStrValue[i] == '\'') + { + pszDestStr[j++] = '\''; + pszDestStr[j++] = '\''; + } + /* FIXME: at some point (when we drop PostgreSQL < 9.1 support, remove + the escaping of backslash and remove 'SET standard_conforming_strings = OFF' + inICreateLayer() */ + else if (pszStrValue[i] == '\\') + { + pszDestStr[j++] = '\\'; + pszDestStr[j++] = '\\'; + } + else + pszDestStr[j++] = pszStrValue[i]; + } + pszDestStr[j] = 0; + + osCommand += pszDestStr; +#endif + CPLFree(pszDestStr); + + osCommand += "'"; + + return osCommand; +} + + +/************************************************************************/ +/* OGRPGDumpEscapeStringList( ) */ +/************************************************************************/ + +static CPLString OGRPGDumpEscapeStringList( + char** papszItems, int bForInsertOrUpdate, + OGRPGCommonEscapeStringCbk pfnEscapeString, + void* userdata) +{ + int bFirstItem = TRUE; + CPLString osStr; + if (bForInsertOrUpdate) + osStr += "ARRAY["; + else + osStr += "{"; + while(papszItems && *papszItems) + { + if (!bFirstItem) + { + osStr += ','; + } + + char* pszStr = *papszItems; + if (*pszStr != '\0') + { + if (bForInsertOrUpdate) + osStr += pfnEscapeString(userdata, pszStr, 0, "", ""); + else + { + osStr += '"'; + + while(*pszStr) + { + if (*pszStr == '"' ) + osStr += "\\"; + osStr += *pszStr; + pszStr++; + } + + osStr += '"'; + } + } + else + osStr += "NULL"; + + bFirstItem = FALSE; + + papszItems++; + } + if (bForInsertOrUpdate) + { + osStr += "]"; + if( papszItems == NULL ) + osStr += "::varchar[]"; + } + else + osStr += "}"; + return osStr; +} + +/************************************************************************/ +/* AppendFieldValue() */ +/* */ +/* Used by CreateFeatureViaInsert() and SetFeature() to format a */ +/* non-empty field value */ +/************************************************************************/ + +void OGRPGCommonAppendFieldValue(CPLString& osCommand, + OGRFeature* poFeature, int i, + OGRPGCommonEscapeStringCbk pfnEscapeString, + void* userdata) +{ + OGRFeatureDefn* poFeatureDefn = poFeature->GetDefnRef(); + OGRFieldType nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType(); + OGRFieldSubType eSubType = poFeatureDefn->GetFieldDefn(i)->GetSubType(); + + // We need special formatting for integer list values. + if( nOGRFieldType == OFTIntegerList ) + { + int nCount, nOff = 0, j; + const int *panItems = poFeature->GetFieldAsIntegerList(i,&nCount); + char *pszNeedToFree = NULL; + + pszNeedToFree = (char *) CPLMalloc(nCount * 13 + 10); + strcpy( pszNeedToFree, "'{" ); + for( j = 0; j < nCount; j++ ) + { + if( j != 0 ) + strcat( pszNeedToFree+nOff, "," ); + + nOff += strlen(pszNeedToFree+nOff); + sprintf( pszNeedToFree+nOff, "%d", panItems[j] ); + } + strcat( pszNeedToFree+nOff, "}'" ); + + osCommand += pszNeedToFree; + CPLFree(pszNeedToFree); + + return; + } + + else if( nOGRFieldType == OFTInteger64List ) + { + int nCount, nOff = 0, j; + const GIntBig *panItems = poFeature->GetFieldAsInteger64List(i,&nCount); + char *pszNeedToFree = NULL; + + pszNeedToFree = (char *) CPLMalloc(nCount * 26 + 10); + strcpy( pszNeedToFree, "'{" ); + for( j = 0; j < nCount; j++ ) + { + if( j != 0 ) + strcat( pszNeedToFree+nOff, "," ); + + nOff += strlen(pszNeedToFree+nOff); + sprintf( pszNeedToFree+nOff, CPL_FRMT_GIB, panItems[j] ); + } + strcat( pszNeedToFree+nOff, "}'" ); + + osCommand += pszNeedToFree; + CPLFree(pszNeedToFree); + + return; + } + + // We need special formatting for real list values. + else if( nOGRFieldType == OFTRealList ) + { + int nCount, nOff = 0, j; + const double *padfItems =poFeature->GetFieldAsDoubleList(i,&nCount); + char *pszNeedToFree = NULL; + + pszNeedToFree = (char *) CPLMalloc(nCount * 40 + 10); + strcpy( pszNeedToFree, "'{" ); + for( j = 0; j < nCount; j++ ) + { + if( j != 0 ) + strcat( pszNeedToFree+nOff, "," ); + + nOff += strlen(pszNeedToFree+nOff); + //Check for special values. They need to be quoted. + if( CPLIsNan(padfItems[j]) ) + sprintf( pszNeedToFree+nOff, "NaN" ); + else if( CPLIsInf(padfItems[j]) ) + sprintf( pszNeedToFree+nOff, (padfItems[j] > 0) ? "Infinity" : "-Infinity" ); + else + CPLsprintf( pszNeedToFree+nOff, "%.16g", padfItems[j] ); + + } + strcat( pszNeedToFree+nOff, "}'" ); + + osCommand += pszNeedToFree; + CPLFree(pszNeedToFree); + + return; + } + + // We need special formatting for string list values. + else if( nOGRFieldType == OFTStringList ) + { + char **papszItems = poFeature->GetFieldAsStringList(i); + + osCommand += OGRPGDumpEscapeStringList(papszItems, TRUE, + pfnEscapeString, userdata); + + return; + } + + // Binary formatting + else if( nOGRFieldType == OFTBinary ) + { + osCommand += "'"; + + int nLen = 0; + GByte* pabyData = poFeature->GetFieldAsBinary( i, &nLen ); + char* pszBytea = OGRPGDumpLayer::GByteArrayToBYTEA( pabyData, nLen); + + osCommand += pszBytea; + + CPLFree(pszBytea); + osCommand += "'"; + + return; + } + + // Flag indicating NULL or not-a-date date value + // e.g. 0000-00-00 - there is no year 0 + OGRBoolean bIsDateNull = FALSE; + + const char *pszStrValue = poFeature->GetFieldAsString(i); + + // Check if date is NULL: 0000-00-00 + if( nOGRFieldType == OFTDate ) + { + if( EQUALN( pszStrValue, "0000", 4 ) ) + { + pszStrValue = "NULL"; + bIsDateNull = TRUE; + } + } + else if ( nOGRFieldType == OFTReal ) + { + //Check for special values. They need to be quoted. + double dfVal = poFeature->GetFieldAsDouble(i); + if( CPLIsNan(dfVal) ) + pszStrValue = "'NaN'"; + else if( CPLIsInf(dfVal) ) + pszStrValue = (dfVal > 0) ? "'Infinity'" : "'-Infinity'"; + } + else if ( (nOGRFieldType == OFTInteger || + nOGRFieldType == OFTInteger64) && eSubType == OFSTBoolean ) + pszStrValue = poFeature->GetFieldAsInteger(i) ? "'t'" : "'f'"; + + if( nOGRFieldType != OFTInteger && nOGRFieldType != OFTInteger64 && + nOGRFieldType != OFTReal + && !bIsDateNull ) + { + osCommand += pfnEscapeString( userdata, pszStrValue, + poFeatureDefn->GetFieldDefn(i)->GetWidth(), + poFeatureDefn->GetName(), + poFeatureDefn->GetFieldDefn(i)->GetNameRef() ); + } + else + { + osCommand += pszStrValue; + } +} + + +/************************************************************************/ +/* GByteArrayToBYTEA() */ +/************************************************************************/ + +char* OGRPGDumpLayer::GByteArrayToBYTEA( const GByte* pabyData, int nLen) +{ + char* pszTextBuf; + + pszTextBuf = (char *) CPLMalloc(nLen*5+1); + + int iSrc, iDst=0; + + for( iSrc = 0; iSrc < nLen; iSrc++ ) + { + if( pabyData[iSrc] < 40 || pabyData[iSrc] > 126 + || pabyData[iSrc] == '\\' ) + { + sprintf( pszTextBuf+iDst, "\\\\%03o", pabyData[iSrc] ); + iDst += 5; + } + else + pszTextBuf[iDst++] = pabyData[iSrc]; + } + pszTextBuf[iDst] = '\0'; + + return pszTextBuf; +} + +/************************************************************************/ +/* OGRPGCommonLayerGetType() */ +/************************************************************************/ + +CPLString OGRPGCommonLayerGetType(OGRFieldDefn& oField, + int bPreservePrecision, + int bApproxOK) +{ + char szFieldType[256]; + +/* -------------------------------------------------------------------- */ +/* Work out the PostgreSQL type. */ +/* -------------------------------------------------------------------- */ + if( oField.GetType() == OFTInteger ) + { + if( oField.GetSubType() == OFSTBoolean ) + strcpy( szFieldType, "BOOLEAN" ); + else if( oField.GetSubType() == OFSTInt16 ) + strcpy( szFieldType, "SMALLINT" ); + else if( oField.GetWidth() > 0 && bPreservePrecision ) + sprintf( szFieldType, "NUMERIC(%d,0)", oField.GetWidth() ); + else + strcpy( szFieldType, "INTEGER" ); + } + else if( oField.GetType() == OFTInteger64 ) + { + if( oField.GetWidth() > 0 && bPreservePrecision ) + sprintf( szFieldType, "NUMERIC(%d,0)", oField.GetWidth() ); + else + strcpy( szFieldType, "INT8" ); + } + else if( oField.GetType() == OFTReal ) + { + if( oField.GetSubType() == OFSTFloat32 ) + strcpy( szFieldType, "REAL" ); + else if( oField.GetWidth() > 0 && oField.GetPrecision() > 0 + && bPreservePrecision ) + sprintf( szFieldType, "NUMERIC(%d,%d)", + oField.GetWidth(), oField.GetPrecision() ); + else + strcpy( szFieldType, "FLOAT8" ); + } + else if( oField.GetType() == OFTString ) + { + if (oField.GetWidth() > 0 && bPreservePrecision ) + sprintf( szFieldType, "VARCHAR(%d)", oField.GetWidth() ); + else + strcpy( szFieldType, "VARCHAR"); + } + else if( oField.GetType() == OFTIntegerList ) + { + if( oField.GetSubType() == OFSTBoolean ) + strcpy( szFieldType, "BOOLEAN[]" ); + else + strcpy( szFieldType, "INTEGER[]" ); + } + else if( oField.GetType() == OFTInteger64List ) + { + strcpy( szFieldType, "INT8[]" ); + } + else if( oField.GetType() == OFTRealList ) + { + strcpy( szFieldType, "FLOAT8[]" ); + } + else if( oField.GetType() == OFTStringList ) + { + strcpy( szFieldType, "varchar[]" ); + } + else if( oField.GetType() == OFTDate ) + { + strcpy( szFieldType, "date" ); + } + else if( oField.GetType() == OFTTime ) + { + strcpy( szFieldType, "time" ); + } + else if( oField.GetType() == OFTDateTime ) + { + strcpy( szFieldType, "timestamp with time zone" ); + } + else if( oField.GetType() == OFTBinary ) + { + strcpy( szFieldType, "bytea" ); + } + else if( bApproxOK ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Can't create field %s with type %s on PostgreSQL layers. Creating as VARCHAR.", + oField.GetNameRef(), + OGRFieldDefn::GetFieldTypeName(oField.GetType()) ); + strcpy( szFieldType, "VARCHAR" ); + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can't create field %s with type %s on PostgreSQL layers.", + oField.GetNameRef(), + OGRFieldDefn::GetFieldTypeName(oField.GetType()) ); + strcpy( szFieldType, ""); + } + + return szFieldType; +} + +/************************************************************************/ +/* OGRPGCommonLayerSetType() */ +/************************************************************************/ + +int OGRPGCommonLayerSetType(OGRFieldDefn& oField, + const char* pszType, + const char* pszFormatType, + int nWidth) +{ + if( EQUAL(pszType,"text") ) + { + oField.SetType( OFTString ); + } + else if( EQUAL(pszType,"_bpchar") || + EQUAL(pszType,"_varchar") || + EQUAL(pszType,"_text")) + { + oField.SetType( OFTStringList ); + } + else if( EQUAL(pszType,"bpchar") || EQUAL(pszType,"varchar") ) + { + if( nWidth == -1 ) + { + if( EQUALN(pszFormatType,"character(",10) ) + nWidth = atoi(pszFormatType+10); + else if( EQUALN(pszFormatType,"character varying(",18) ) + nWidth = atoi(pszFormatType+18); + else + nWidth = 0; + } + oField.SetType( OFTString ); + oField.SetWidth( nWidth ); + } + else if( EQUAL(pszType,"bool") ) + { + oField.SetType( OFTInteger ); + oField.SetSubType( OFSTBoolean ); + oField.SetWidth( 1 ); + } + else if( EQUAL(pszType,"numeric") ) + { + if( EQUAL(pszFormatType, "numeric") ) + oField.SetType( OFTReal ); + else + { + const char *pszPrecision = strstr(pszFormatType,","); + int nWidth, nPrecision = 0; + + nWidth = atoi(pszFormatType + 8); + if( pszPrecision != NULL ) + nPrecision = atoi(pszPrecision+1); + + if( nPrecision == 0 ) + { + if( nWidth >= 10 ) + oField.SetType( OFTInteger64 ); + else + oField.SetType( OFTInteger ); + } + else + oField.SetType( OFTReal ); + + oField.SetWidth( nWidth ); + oField.SetPrecision( nPrecision ); + } + } + else if( EQUAL(pszFormatType,"integer[]") ) + { + oField.SetType( OFTIntegerList ); + } + else if( EQUAL(pszFormatType,"boolean[]") ) + { + oField.SetType( OFTIntegerList ); + oField.SetSubType( OFSTBoolean ); + } + else if( EQUAL(pszFormatType, "float[]") || + EQUAL(pszFormatType, "real[]") ) + { + oField.SetType( OFTRealList ); + oField.SetSubType( OFSTFloat32 ); + } + else if( EQUAL(pszFormatType, "double precision[]") ) + { + oField.SetType( OFTRealList ); + } + else if( EQUAL(pszType,"int2") ) + { + oField.SetType( OFTInteger ); + oField.SetSubType( OFSTInt16 ); + oField.SetWidth( 5 ); + } + else if( EQUAL(pszType,"int8") ) + { + oField.SetType( OFTInteger64 ); + } + else if( EQUAL(pszFormatType,"bigint[]") ) + { + oField.SetType( OFTInteger64List ); + } + else if( EQUALN(pszType,"int",3) ) + { + oField.SetType( OFTInteger ); + } + else if( EQUAL(pszType,"float4") ) + { + oField.SetType( OFTReal ); + oField.SetSubType( OFSTFloat32 ); + } + else if( EQUALN(pszType,"float",5) || + EQUALN(pszType,"double",6) || + EQUAL(pszType,"real") ) + { + oField.SetType( OFTReal ); + } + else if( EQUALN(pszType, "timestamp",9) ) + { + oField.SetType( OFTDateTime ); + } + else if( EQUALN(pszType, "date",4) ) + { + oField.SetType( OFTDate ); + } + else if( EQUALN(pszType, "time",4) ) + { + oField.SetType( OFTTime ); + } + else if( EQUAL(pszType,"bytea") ) + { + oField.SetType( OFTBinary ); + } + else + { + CPLDebug( "PGCommon", "Field %s is of unknown format type %s (type=%s).", + oField.GetNameRef(), pszFormatType, pszType ); + return FALSE; + } + return TRUE; +} + +/************************************************************************/ +/* OGRPGCommonLayerNormalizeDefault() */ +/************************************************************************/ + +void OGRPGCommonLayerNormalizeDefault(OGRFieldDefn* poFieldDefn, + const char* pszDefault) +{ + if(pszDefault==NULL) + return; + CPLString osDefault(pszDefault); + size_t nPos = osDefault.find("::character varying"); + if( nPos != std::string::npos ) + osDefault.resize(nPos); + else if( strcmp(osDefault, "now()") == 0 ) + osDefault = "CURRENT_TIMESTAMP"; + else if( strcmp(osDefault, "('now'::text)::date") == 0 ) + osDefault = "CURRENT_DATE"; + else if( strcmp(osDefault, "('now'::text)::time with time zone") == 0 ) + osDefault = "CURRENT_TIME"; + else + { + nPos = osDefault.find("::timestamp with time zone"); + if( poFieldDefn->GetType() == OFTDateTime && nPos != std::string::npos ) + { + osDefault.resize(nPos); + nPos = osDefault.find("'+"); + if( nPos != std::string::npos ) + { + osDefault.resize(nPos); + osDefault += "'"; + } + int nYear, nMonth, nDay, nHour, nMinute; + float fSecond; + if( sscanf(osDefault, "'%d-%d-%d %d:%d:%f'", &nYear, &nMonth, &nDay, + &nHour, &nMinute, &fSecond) == 6 || + sscanf(osDefault, "'%d-%d-%d %d:%d:%f+00'", &nYear, &nMonth, &nDay, + &nHour, &nMinute, &fSecond) == 6) + { + if( osDefault.find('.') == std::string::npos ) + osDefault = CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%02d'", + nYear, nMonth, nDay, nHour, nMinute, (int)(fSecond+0.5)); + else + osDefault = CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%06.3f'", + nYear, nMonth, nDay, nHour, nMinute, fSecond); + } + } + } + poFieldDefn->SetDefault(osDefault); +} + +/************************************************************************/ +/* OGRPGCommonLayerGetPGDefault() */ +/************************************************************************/ + +CPLString OGRPGCommonLayerGetPGDefault(OGRFieldDefn* poFieldDefn) +{ + CPLString osRet = poFieldDefn->GetDefault(); + int nYear, nMonth, nDay, nHour, nMinute; + float fSecond; + if( sscanf(osRet, "'%d/%d/%d %d:%d:%f'", + &nYear, &nMonth, &nDay, + &nHour, &nMinute, &fSecond) == 6 ) + { + osRet.resize(osRet.size()-1); + osRet += "+00'::timestamp with time zone"; + } + return osRet; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRErr OGRPGDumpLayer::CreateField( OGRFieldDefn *poFieldIn, + int bApproxOK ) +{ + CPLString osCommand; + CPLString osFieldType; + OGRFieldDefn oField( poFieldIn ); + + // Can be set to NO to test ogr2ogr default behaviour + int bAllowCreationOfFieldWithFIDName = + CSLTestBoolean(CPLGetConfigOption("PGDUMP_DEBUG_ALLOW_CREATION_FIELD_WITH_FID_NAME", "YES")); + + if( bAllowCreationOfFieldWithFIDName && pszFIDColumn != NULL && + EQUAL( oField.GetNameRef(), pszFIDColumn ) && + oField.GetType() != OFTInteger && + oField.GetType() != OFTInteger64 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s", + oField.GetNameRef()); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Do we want to "launder" the column names into Postgres */ +/* friendly format? */ +/* -------------------------------------------------------------------- */ + if( bLaunderColumnNames ) + { + char *pszSafeName = OGRPGCommonLaunderName( oField.GetNameRef(), "PGDump" ); + + oField.SetName( pszSafeName ); + CPLFree( pszSafeName ); + + if( EQUAL(oField.GetNameRef(),"oid") ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Renaming field 'oid' to 'oid_' to avoid conflict with internal oid field." ); + oField.SetName( "oid_" ); + } + } + + const char* pszOverrideType = CSLFetchNameValue(papszOverrideColumnTypes, oField.GetNameRef()); + if( pszOverrideType != NULL ) + osFieldType = pszOverrideType; + else + { + osFieldType = OGRPGCommonLayerGetType(oField, bPreservePrecision, bApproxOK); + if (osFieldType.size() == 0) + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Create the new field. */ +/* -------------------------------------------------------------------- */ + osCommand.Printf( "ALTER TABLE %s ADD COLUMN %s %s", + pszSqlTableName, OGRPGDumpEscapeColumnName(oField.GetNameRef()).c_str(), + osFieldType.c_str() ); + if( !oField.IsNullable() ) + osCommand += " NOT NULL"; + if( oField.GetDefault() != NULL && !oField.IsDefaultDriverSpecific() ) + { + osCommand += " DEFAULT "; + osCommand += OGRPGCommonLayerGetPGDefault(&oField); + } + + poFeatureDefn->AddFieldDefn( &oField ); + + if( bAllowCreationOfFieldWithFIDName && pszFIDColumn != NULL && + EQUAL( oField.GetNameRef(), pszFIDColumn ) ) + { + iFIDAsRegularColumnIndex = poFeatureDefn->GetFieldCount() - 1; + } + else + { + if( bCreateTable ) + poDS->Log(osCommand); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateGeomField() */ +/************************************************************************/ + +OGRErr OGRPGDumpLayer::CreateGeomField( OGRGeomFieldDefn *poGeomFieldIn, + CPL_UNUSED int bApproxOK ) +{ + OGRwkbGeometryType eType = poGeomFieldIn->GetType(); + if( eType == wkbNone ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot create geometry field of type wkbNone"); + return OGRERR_FAILURE; + } + + CPLString osCommand; + OGRPGDumpGeomFieldDefn *poGeomField = + new OGRPGDumpGeomFieldDefn( poGeomFieldIn ); + +/* -------------------------------------------------------------------- */ +/* Do we want to "launder" the column names into Postgres */ +/* friendly format? */ +/* -------------------------------------------------------------------- */ + if( bLaunderColumnNames ) + { + char *pszSafeName = OGRPGCommonLaunderName( poGeomField->GetNameRef(), "PGDump" ); + + poGeomField->SetName( pszSafeName ); + CPLFree( pszSafeName ); + } + + OGRSpatialReference* poSRS = poGeomField->GetSpatialRef(); + int nSRSId = nUnknownSRSId; + if( nForcedSRSId != -2 ) + nSRSId = nForcedSRSId; + else if( poSRS != NULL ) + { + const char* pszAuthorityName = poSRS->GetAuthorityName(NULL); + if( pszAuthorityName != NULL && EQUAL( pszAuthorityName, "EPSG" ) ) + { + /* Assume the EPSG Id is the SRS ID. Might be a wrong guess ! */ + nSRSId = atoi( poSRS->GetAuthorityCode(NULL) ); + } + else + { + const char* pszGeogCSName = poSRS->GetAttrValue("GEOGCS"); + if (pszGeogCSName != NULL && EQUAL(pszGeogCSName, "GCS_WGS_1984")) + nSRSId = 4326; + } + } + + int nDimension = 3; + if( wkbFlatten(eType) == eType ) + nDimension = 2; + poGeomField->nSRSId = nSRSId; + poGeomField->nCoordDimension = nDimension; + +/* -------------------------------------------------------------------- */ +/* Create the new field. */ +/* -------------------------------------------------------------------- */ + if (bCreateTable) + { + const char *pszGeometryType = OGRToOGCGeomType(poGeomField->GetType()); + osCommand.Printf( + "SELECT AddGeometryColumn(%s,%s,%s,%d,'%s',%d)", + OGRPGDumpEscapeString(pszSchemaName).c_str(), + OGRPGDumpEscapeString(poFeatureDefn->GetName()).c_str(), + OGRPGDumpEscapeString(poGeomField->GetNameRef()).c_str(), + nSRSId, pszGeometryType, nDimension ); + + poDS->Log(osCommand); + + if( !poGeomField->IsNullable() ) + { + osCommand.Printf( "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL", + OGRPGDumpEscapeColumnName(poFeatureDefn->GetName()).c_str(), + OGRPGDumpEscapeColumnName(poGeomField->GetNameRef()).c_str() ); + + poDS->Log(osCommand); + } + + if( bCreateSpatialIndexFlag ) + { + osCommand.Printf("CREATE INDEX %s ON %s USING GIST (%s)", + OGRPGDumpEscapeColumnName( + CPLSPrintf("%s_%s_geom_idx", GetName(), poGeomField->GetNameRef())).c_str(), + pszSqlTableName, + OGRPGDumpEscapeColumnName(poGeomField->GetNameRef()).c_str()); + + poDS->Log(osCommand); + } + } + + poFeatureDefn->AddGeomFieldDefn( poGeomField, FALSE ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetOverrideColumnTypes() */ +/************************************************************************/ + +void OGRPGDumpLayer::SetOverrideColumnTypes( const char* pszOverrideColumnTypes ) +{ + if( pszOverrideColumnTypes == NULL ) + return; + + const char* pszIter = pszOverrideColumnTypes; + CPLString osCur; + while(*pszIter != '\0') + { + if( *pszIter == '(' ) + { + /* Ignore commas inside ( ) pair */ + while(*pszIter != '\0') + { + if( *pszIter == ')' ) + { + osCur += *pszIter; + pszIter ++; + break; + } + osCur += *pszIter; + pszIter ++; + } + if( *pszIter == '\0') + break; + } + + if( *pszIter == ',' ) + { + papszOverrideColumnTypes = CSLAddString(papszOverrideColumnTypes, osCur); + osCur = ""; + } + else + osCur += *pszIter; + pszIter ++; + } + if( osCur.size() ) + papszOverrideColumnTypes = CSLAddString(papszOverrideColumnTypes, osCur); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/GNUmakefile new file mode 100644 index 000000000..13864a379 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/GNUmakefile @@ -0,0 +1,13 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrpgeodatasource.o ogrpgeolayer.o ogrpgeodriver.o \ + ogrpgeotablelayer.o ogrpgeoselectlayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/drv_pgeo.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/drv_pgeo.html new file mode 100644 index 000000000..3c853883a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/drv_pgeo.html @@ -0,0 +1,221 @@ + + + + + + + ESRI Personal GeoDatabase + + + +

    ESRI Personal GeoDatabase

    + +

    OGR optionally supports reading ESRI Personal GeoDatabase .mdb + files via ODBC. Personal GeoDatabase is a Microsoft Access + database with a set of tables defined by ESRI for holding + geodatabase metadata, and with geometry for features held in a + BLOB column in a custom format (essentially Shapefile geometry + fragments). This drivers accesses the personal geodatabase via + ODBC but does not depend on any ESRI middle-ware.

    + +

    Personal Geodatabases are accessed by passing the file name of + the .mdb file to be accessed as the data source name. On Windows, + no ODBC DSN is required. On Linux, there are problems with DSN-less + connection due to incomplete or buggy implementation of this feature + in the MDB Tools package, + So, it is required to configure Data Source Name (DSN) if the MDB + Tools driver is used (check instructions below).

    + +

    In order to facilitate compatibility with different configurations, + the PGEO_DRIVER_TEMPLATE Config Option was added to provide a way to + programmatically set the DSN programmatically with the filename as + an argument. In cases where the driver name is known, this allows for + the construction of the DSN based on that information in a manner similar + to the default (used for Windows access to the Microsoft Access Driver).

    + +

    OGR treats all feature tables as layers. Most geometry types + should be supported, including 3D data. Measures information will + be discarded. Coordinate system information should be properly + associated with layers.

    + +

    Currently the OGR Personal Geodatabase driver does not take + advantage of spatial indexes for fast spatial queries, though + that may be added in the future.

    + +

    By default, SQL statements are passed directly to the MDB database engine. +It's also possible to request the driver to handle SQL commands +with OGR SQL engine, +by passing "OGRSQL" string to the ExecuteSQL() +method, as name of the SQL dialect.

    + +

    How to use PGeo driver with unixODBC and MDB Tools (on Unix and Linux)

    + +

    Starting with GDAL/OGR 1.9.0, the MDB driver is an alternate + way of reading ESRI Personal GeoDatabase .mdb files without requiring unixODBC and MDB Tools

    + +

    This article gives step-by-step explanation of how to use OGR + with unixODBC package and how to access Personal Geodatabase with + PGeo driver. See also GDAL wiki for other details

    + +

    Prerequisites

    + +
      +
    1. Install unixODBC + >= 2.2.11
    2. + +
    3. Install MDB + Tools >= 0.6. I also tested with 0.5.99 (0.6 + pre-release).
    4. +
    + +

    (On Ubuntu 8.04 : sudo apt-get install unixodbc libmdbodbc)

    + +

    Configuration

    + +

    There are two configuration files for unixODBC:

    + +
      +
    • odbcinst.ini - this file contains definition of ODBC + drivers available to all users; this file can be found in /etc + directory or location given as --sysconfdir if you did build + unixODBC yourself.
    • + +
    • odbc.ini - this file contains definition of ODBC data + sources (DSN entries) available to all users.
    • + +
    • ~/.odbc.ini - this is the private file where users can put + their own ODBC data sources.
    • +
    + +

    Format of configuration files is very simple:

    +
    +[section_name]
    +entry1 = value
    +entry2 = value
    +
    + +

    For more details, refer to unixODBC manual.

    + +

    1. ODBC driver configuration

    + +

    First, you need to configure ODBC driver to access Microsoft + Access databases with MDB Tools. Add following definition to your + odbcinst.ini file.

    +
    +[Microsoft Access Driver (*.mdb)]
    +Description = MDB Tools ODBC drivers
    +Driver     = /usr/lib/libmdbodbc.so.0
    +Setup      =
    +FileUsage  = 1
    +CPTimeout  =
    +CPReuse    =
    +
    + +
      +
    • [Microsoft Access Driver (*.mdb)] - remember to use + "Microsoft Access Driver (*.mdb)" as the name of section + because PGeo driver composes ODBC connection string for + Personal Geodatabase using "DRIVER=Microsoft Access Driver + (*.mdb);" string.
    • + +
    • Description - put short description of this driver + definition.
    • + +
    • Driver - full path of ODBC driver for MDB Tools.
    • +
    + +

    2. ODBC data source configuration

    + +

    In this section, I use 'sample.mdb' as a name of Personal + Geodatabase, so replace this name with your own database.

    + +

    Create .odbc.ini file in your HOME directory:

    +
    +$ touch ~/.odbc.ini
    +
    + +

    Put following ODBC data source definition to your .odbc.ini + file:

    +
    +[sample_pgeo]
    +Description = Sample PGeo Database
    +Driver      = Microsoft Access Driver (*.mdb)
    +Database    = /home/mloskot/data/sample.mdb
    +Host        = localhost
    +Port        = 1360
    +User        = mloskot
    +Password    =
    +Trace       = Yes
    +TraceFile   = /home/mloskot/odbc.log
    +
    + +

    Step by step explanation of DSN entry:

    + +
      +
    • [sample_pgeo] - this is name of ODBC data source (DSN). You + will refer to your Personal Geodatabase using this name. You + can use your own name here.
    • + +
    • Description - short description of the DSN entry.
    • + +
    • Driver - full name of driver defined in step 1. above.
    • + +
    • Database - full path to .mdb file with your Personal + Geodatabase.
    • + +
    • Host, Port, User and Password entries are not used by MDB + Tools driver.
    • +
    + +

    Testing PGeo driver with ogrinfo

    + +

    Now, you can try to access PGeo data source with ogrinfo.

    + +

    First, check if you have PGeo driver built in OGR:

    +
    +$ ogrinfo --formats
    +Supported Formats:
    +  ESRI Shapefile
    +  ...
    +  PGeo
    +  ...
    +
    + +

    Now, you can access your Personal Geodatabase. As a data + source use PGeo:<DSN> where <DSN> is a name of DSN + entry you put to your .odbc.ini.

    +
    +ogrinfo PGeo:sample_pgeo
    +INFO: Open of `PGeo:sample_pgeo'
    +using driver `PGeo' successful.
    +1. ...
    +
    After you run the command above, you should get list of +layers stored in your geodatabase. + +

    Now, you can try to query details of particular layer:

    +
    +ogrinfo PGeo:sample_pgeo <layer name>
    +INFO: Open of `PGeo:sample_pgeo'
    +using driver `PGeo' successful.
    +
    +Layer name: ...
    +
    + +

    Resources

    + + + +

    See also

    + +
      +
    • MDB driver page
    • +
    + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/makefile.vc new file mode 100644 index 000000000..bb599d71e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/makefile.vc @@ -0,0 +1,14 @@ + +OBJ = ogrpgeodriver.obj ogrpgeodatasource.obj ogrpgeolayer.obj \ + ogrpgeotablelayer.obj ogrpgeoselectlayer.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. + +default: $(OBJ) + +clean: + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogr_pgeo.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogr_pgeo.h new file mode 100644 index 000000000..f60c6ba6f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogr_pgeo.h @@ -0,0 +1,229 @@ +/****************************************************************************** + * $Id: ogr_pgeo.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for Personal Geodatabase driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_ODBC_H_INCLUDED +#define _OGR_ODBC_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_odbc.h" +#include "cpl_error.h" + +/************************************************************************/ +/* OGRPGeoLayer */ +/************************************************************************/ + +class OGRPGeoDataSource; + +class OGRPGeoLayer : public OGRLayer +{ + protected: + OGRFeatureDefn *poFeatureDefn; + + CPLODBCStatement *poStmt; + + // Layer spatial reference system, and srid. + OGRSpatialReference *poSRS; + int nSRSId; + + GIntBig iNextShapeId; + + OGRPGeoDataSource *poDS; + + char *pszGeomColumn; + char *pszFIDColumn; + + int *panFieldOrdinals; + + CPLErr BuildFeatureDefn( const char *pszLayerName, + CPLODBCStatement *poStmt ); + + virtual CPLODBCStatement * GetStatement() { return poStmt; } + + void LookupSRID( int ); + + public: + OGRPGeoLayer(); + virtual ~OGRPGeoLayer(); + + virtual void ResetReading(); + virtual OGRFeature *GetNextRawFeature(); + virtual OGRFeature *GetNextFeature(); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); + + virtual const char *GetFIDColumn(); + virtual const char *GetGeometryColumn(); +}; + +/************************************************************************/ +/* OGRPGeoTableLayer */ +/************************************************************************/ + +class OGRPGeoTableLayer : public OGRPGeoLayer +{ + int bUpdateAccess; + + char *pszQuery; + + void ClearStatement(); + OGRErr ResetStatement(); + + virtual CPLODBCStatement * GetStatement(); + + OGREnvelope sExtent; + + public: + OGRPGeoTableLayer( OGRPGeoDataSource * ); + ~OGRPGeoTableLayer(); + + CPLErr Initialize( const char *pszTableName, + const char *pszGeomCol, + int nShapeType, + double dfExtentLeft, + double dfExtentRight, + double dfExtentBottom, + double dfExtentTop, + int nSRID, + int bHasZ ); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual OGRErr SetAttributeFilter( const char * ); + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual int TestCapability( const char * ); + + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); +}; + +/************************************************************************/ +/* OGRPGeoSelectLayer */ +/************************************************************************/ + +class OGRPGeoSelectLayer : public OGRPGeoLayer +{ + char *pszBaseStatement; + + void ClearStatement(); + OGRErr ResetStatement(); + + virtual CPLODBCStatement * GetStatement(); + + public: + OGRPGeoSelectLayer( OGRPGeoDataSource *, + CPLODBCStatement * ); + ~OGRPGeoSelectLayer(); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount( int ); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRPGeoDataSource */ +/************************************************************************/ + +class OGRPGeoDataSource : public OGRDataSource +{ + OGRPGeoLayer **papoLayers; + int nLayers; + + char *pszName; + + int bDSUpdate; + CPLODBCSession oSession; + + public: + OGRPGeoDataSource(); + ~OGRPGeoDataSource(); + + int Open( const char *, int bUpdate, int bTestOpen ); + int OpenTable( const char *pszTableName, + const char *pszGeomCol, + int bUpdate ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + // Internal use + CPLODBCSession *GetSession() { return &oSession; } +}; + +/************************************************************************/ +/* OGRODBCMDBDriver */ +/************************************************************************/ + +class OGRODBCMDBDriver : public OGRSFDriver +{ +#ifndef WIN32 + CPLString osDriverFile; + bool LibraryExists( const char* pszLibPath ); + bool FindDriverLib(); + CPLString FindDefaultLib(const char* pszLibName); +#endif + +protected: +#ifndef WIN32 + bool InstallMdbDriver(); +#endif +}; + +/************************************************************************/ +/* OGRPGeoDriver */ +/************************************************************************/ + +class OGRPGeoDriver : public OGRODBCMDBDriver +{ + public: + ~OGRPGeoDriver(); + + const char *GetName(); + OGRDataSource *Open( const char *, int ); + + int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_PGeo_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeodatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeodatasource.cpp new file mode 100644 index 000000000..5d38f0704 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeodatasource.cpp @@ -0,0 +1,323 @@ +/****************************************************************************** + * $Id: ogrpgeodatasource.cpp 28368 2015-01-27 14:25:17Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGeoDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_pgeo.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: ogrpgeodatasource.cpp 28368 2015-01-27 14:25:17Z rouault $"); + +/************************************************************************/ +/* OGRPGeoDataSource() */ +/************************************************************************/ + +OGRPGeoDataSource::OGRPGeoDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; +} + +/************************************************************************/ +/* ~OGRPGeoDataSource() */ +/************************************************************************/ + +OGRPGeoDataSource::~OGRPGeoDataSource() + +{ + int i; + + CPLFree( pszName ); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); +} + +/************************************************************************/ +/* CheckDSNStringTemplate() */ +/* The string will be used as the formatting argument of sprintf with */ +/* a string in vararg. So let's check there's only one '%s', and nothing*/ +/* else */ +/************************************************************************/ + +static int CheckDSNStringTemplate(const char* pszStr) +{ + int nPercentSFound = FALSE; + while(*pszStr) + { + if (*pszStr == '%') + { + if (pszStr[1] != 's') + { + return FALSE; + } + else + { + if (nPercentSFound) + return FALSE; + nPercentSFound = TRUE; + } + } + pszStr ++; + } + return TRUE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRPGeoDataSource::Open( const char * pszNewName, int bUpdate, + CPL_UNUSED int bTestOpen ) +{ + CPLAssert( nLayers == 0 ); + +/* -------------------------------------------------------------------- */ +/* If this is the name of an MDB file, then construct the */ +/* appropriate connection string. Otherwise clip of PGEO: to */ +/* get the DSN. */ +/* */ +/* -------------------------------------------------------------------- */ + char *pszDSN; + const char* pszOptionName = ""; + const char* pszDSNStringTemplate = NULL; + if( EQUALN(pszNewName,"PGEO:",5) ) + pszDSN = CPLStrdup( pszNewName + 5 ); + else + { + pszOptionName = "PGEO_DRIVER_TEMPLATE"; + pszDSNStringTemplate = CPLGetConfigOption( pszOptionName, NULL ); + if( pszDSNStringTemplate == NULL ) + { + pszOptionName = ""; + pszDSNStringTemplate = "DRIVER=Microsoft Access Driver (*.mdb);DBQ=%s"; + } + if (!CheckDSNStringTemplate(pszDSNStringTemplate)) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Illegal value for PGEO_DRIVER_TEMPLATE option"); + return FALSE; + } + pszDSN = (char *) CPLMalloc(strlen(pszNewName)+strlen(pszDSNStringTemplate)+100); + sprintf( pszDSN, pszDSNStringTemplate, pszNewName ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize based on the DSN. */ +/* -------------------------------------------------------------------- */ + CPLDebug( "PGeo", "EstablishSession(%s)", pszDSN ); + + if( !oSession.EstablishSession( pszDSN, NULL, NULL ) ) + { + int bError = TRUE; + if( !EQUALN(pszNewName,"PGEO:",5) ) + { + // Trying with another template (#5594) + pszDSNStringTemplate = "DRIVER=Microsoft Access Driver (*.mdb, *.accdb);DBQ=%s"; + CPLFree( pszDSN ); + pszDSN = (char *) CPLMalloc(strlen(pszNewName)+strlen(pszDSNStringTemplate)+100); + sprintf( pszDSN, pszDSNStringTemplate, pszNewName ); + CPLDebug( "PGeo", "EstablishSession(%s)", pszDSN ); + if( oSession.EstablishSession( pszDSN, NULL, NULL ) ) + { + bError = FALSE; + } + } + if( bError ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to initialize ODBC connection to DSN for %s,\n" + "%s", pszDSN, oSession.GetLastError() ); + CPLFree( pszDSN ); + return FALSE; + } + } + + CPLFree( pszDSN ); + + pszName = CPLStrdup( pszNewName ); + + bDSUpdate = bUpdate; + +/* -------------------------------------------------------------------- */ +/* Collect list of tables and their supporting info from */ +/* GDB_GeomColumns. */ +/* -------------------------------------------------------------------- */ + std::vector apapszGeomColumns; + CPLODBCStatement oStmt( &oSession ); + + oStmt.Append( "SELECT TableName, FieldName, ShapeType, ExtentLeft, ExtentRight, ExtentBottom, ExtentTop, SRID, HasZ FROM GDB_GeomColumns" ); + + if( !oStmt.ExecuteSQL() ) + { + CPLDebug( "PGEO", + "SELECT on GDB_GeomColumns fails, perhaps not a personal geodatabase?\n%s", + oSession.GetLastError() ); + return FALSE; + } + + while( oStmt.Fetch() ) + { + int i, iNew = apapszGeomColumns.size(); + char **papszRecord = NULL; + for( i = 0; i < 9; i++ ) + papszRecord = CSLAddString( papszRecord, + oStmt.GetColData(i) ); + apapszGeomColumns.resize(iNew+1); + apapszGeomColumns[iNew] = papszRecord; + } + +/* -------------------------------------------------------------------- */ +/* Create a layer for each spatial table. */ +/* -------------------------------------------------------------------- */ + unsigned int iTable; + + papoLayers = (OGRPGeoLayer **) CPLCalloc(apapszGeomColumns.size(), + sizeof(void*)); + + for( iTable = 0; iTable < apapszGeomColumns.size(); iTable++ ) + { + char **papszRecord = apapszGeomColumns[iTable]; + OGRPGeoTableLayer *poLayer; + + poLayer = new OGRPGeoTableLayer( this ); + + if( poLayer->Initialize( papszRecord[0], // TableName + papszRecord[1], // FieldName + atoi(papszRecord[2]), // ShapeType + CPLAtof(papszRecord[3]), // ExtentLeft + CPLAtof(papszRecord[4]), // ExtentRight + CPLAtof(papszRecord[5]), // ExtentBottom + CPLAtof(papszRecord[6]), // ExtentTop + atoi(papszRecord[7]), // SRID + atoi(papszRecord[8])) // HasZ + != CE_None ) + { + delete poLayer; + } + else + papoLayers[nLayers++] = poLayer; + + CSLDestroy( papszRecord ); + } + + return TRUE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPGeoDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRPGeoDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRPGeoDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Execute statement. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement *poStmt = new CPLODBCStatement( &oSession ); + + poStmt->Append( pszSQLCommand ); + if( !poStmt->ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", oSession.GetLastError() ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Are there result columns for this statement? */ +/* -------------------------------------------------------------------- */ + if( poStmt->GetColCount() == 0 ) + { + delete poStmt; + CPLErrorReset(); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a results layer. It will take ownership of the */ +/* statement. */ +/* -------------------------------------------------------------------- */ + OGRPGeoSelectLayer *poLayer = NULL; + + poLayer = new OGRPGeoSelectLayer( this, poStmt ); + + if( poSpatialFilter != NULL ) + poLayer->SetSpatialFilter( poSpatialFilter ); + + return poLayer; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRPGeoDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeodriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeodriver.cpp new file mode 100644 index 000000000..24ee6791d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeodriver.cpp @@ -0,0 +1,304 @@ +/****************************************************************************** + * $Id: ogrpgeodriver.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements Personal Geodatabase driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_pgeo.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrpgeodriver.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* ~OGRODBCDriver() */ +/************************************************************************/ + +OGRPGeoDriver::~OGRPGeoDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRPGeoDriver::GetName() + +{ + return "PGeo"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRPGeoDriver::Open( const char * pszFilename, + int bUpdate ) + +{ + OGRPGeoDataSource *poDS; + + if( EQUALN(pszFilename, "WALK:", strlen("WALK:")) ) + return NULL; + + if( EQUALN(pszFilename, "GEOMEDIA:", strlen("GEOMEDIA:")) ) + return NULL; + + if( !EQUALN(pszFilename,"PGEO:",5) + && !EQUAL(CPLGetExtension(pszFilename),"mdb") ) + return NULL; + + /* Disabling the attempt to guess if a MDB file is a PGeo database */ + /* or not. The mention to GDB_GeomColumns might be quite far in the */ + /* file, which can cause mis-detection. See http://trac.osgeo.org/gdal/ticket/4498 */ + /* This was initially meant to know if a MDB should be opened by the PGeo or the */ + /* Geomedia driver. */ +#if 0 + if( !EQUALN(pszFilename,"PGEO:",5) && + EQUAL(CPLGetExtension(pszFilename),"mdb") ) + { + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (!fp) + return NULL; + GByte* pabyHeader = (GByte*) CPLMalloc(100000); + VSIFReadL(pabyHeader, 100000, 1, fp); + VSIFCloseL(fp); + + /* Look for GDB_GeomColumns table */ + const GByte pabyNeedle[] = { 'G', 0, 'D', 0, 'B', 0, '_', 0, 'G', 0, 'e', 0, 'o', 0, 'm', 0, 'C', 0, 'o', 0, 'l', 0, 'u', 0, 'm', 0, 'n', 0, 's' }; + int bFound = FALSE; + for(int i=0;i<100000 - (int)sizeof(pabyNeedle);i++) + { + if (memcmp(pabyHeader + i, pabyNeedle, sizeof(pabyNeedle)) == 0) + { + bFound = TRUE; + break; + } + } + CPLFree(pabyHeader); + if (!bFound) + return NULL; + } +#endif + +#ifndef WIN32 + // Try to register MDB Tools driver + // + // ODBCINST.INI NOTE: + // This operation requires write access to odbcinst.ini file + // located in directory pointed by ODBCINISYS variable. + // Usually, it points to /etc, so non-root users can overwrite this + // setting ODBCINISYS with location they have write access to, e.g.: + // $ export ODBCINISYS=$HOME/etc + // $ touch $ODBCINISYS/odbcinst.ini + // + // See: http://www.unixodbc.org/internals.html + // + if ( !InstallMdbDriver() ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to install MDB driver for ODBC, MDB access may not supported.\n" ); + } + else + CPLDebug( "PGeo", "MDB Tools driver installed successfully!"); + +#endif /* ndef WIN32 */ + + // Open data source + poDS = new OGRPGeoDataSource(); + + if( !poDS->Open( pszFilename, bUpdate, TRUE ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPGeoDriver::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + + +/* + * START OF UNIX-only features. + */ +#ifndef WIN32 + +/************************************************************************/ +/* InstallMdbDriver() */ +/************************************************************************/ + +bool OGRODBCMDBDriver::InstallMdbDriver() +{ + if ( !FindDriverLib() ) + { + return false; + } + else + { + CPLAssert( !osDriverFile.empty() ); + CPLDebug( GetName(), "MDB Tools driver: %s", osDriverFile.c_str() ); + + CPLString driverName("Microsoft Access Driver (*.mdb)"); + CPLString driver(driverName); + driver += '\0'; + driver += "Driver="; + driver += osDriverFile; // Found by FindDriverLib() + driver += '\0'; + driver += "FileUsage=1"; + driver += '\0'; + driver += '\0'; + + // Create installer and register driver + CPLODBCDriverInstaller dri; + + if ( !dri.InstallDriver(driver.c_str(), 0, ODBC_INSTALL_COMPLETE) ) + { + // Report ODBC error + CPLError( CE_Failure, CPLE_AppDefined, "ODBC: %s", dri.GetLastError() ); + return false; + } + } + + return true; +} + +/************************************************************************/ +/* FindDriverLib() */ +/************************************************************************/ + +bool OGRODBCMDBDriver::FindDriverLib() +{ + // Default name and path of driver library + const char* aszDefaultLibName[] = { + "libmdbodbc.so", + "libmdbodbc.so.0" /* for Ubuntu 8.04 support */ + }; + const int nLibNames = sizeof(aszDefaultLibName) / sizeof(aszDefaultLibName[0]); + const char* libPath[] = { + "/usr/lib", + "/usr/local/lib" + }; + const int nLibPaths = sizeof(libPath) / sizeof(libPath[0]); + + CPLString strLibPath(""); + + const char* pszDrvCfg = CPLGetConfigOption("MDBDRIVER_PATH", NULL); + if ( NULL != pszDrvCfg ) + { + // Directory or file path + strLibPath = pszDrvCfg; + + VSIStatBuf sStatBuf; + if ( VSIStat( pszDrvCfg, &sStatBuf ) == 0 + && VSI_ISDIR( sStatBuf.st_mode ) ) + { + // Find default library in custom directory + const char* pszDriverFile = CPLFormFilename( pszDrvCfg, aszDefaultLibName[0], NULL ); + CPLAssert( 0 != pszDriverFile ); + + strLibPath = pszDriverFile; + } + + if ( LibraryExists( strLibPath.c_str() ) ) + { + // Save custom driver path + osDriverFile = strLibPath; + return true; + } + } + + // Try to find library in default path + for ( int i = 0; i < nLibPaths; i++ ) + { + for ( int j = 0; j < nLibNames; j++ ) + { + const char* pszDriverFile = CPLFormFilename( libPath[i], aszDefaultLibName[j], NULL ); + CPLAssert( 0 != pszDriverFile ); + + if ( LibraryExists( pszDriverFile ) ) + { + // Save default driver path + osDriverFile = pszDriverFile; + return true; + } + } + } + + CPLError(CE_Failure, CPLE_AppDefined, "%s: MDB Tools driver not found!\n", GetName()); + // Driver not found! + return false; +} + +/************************************************************************/ +/* LibraryExists() */ +/************************************************************************/ + +bool OGRODBCMDBDriver::LibraryExists(const char* pszLibPath) +{ + CPLAssert( 0 != pszLibPath ); + + VSIStatBuf stb; + + if ( 0 == VSIStat( pszLibPath, &stb ) ) + { + if (VSI_ISREG( stb.st_mode ) || VSI_ISLNK(stb.st_mode)) + { + return true; + } + } + + return false; +} + +#endif /* ndef WIN32 */ +/* + * END OF UNIX-only features + */ + +/************************************************************************/ +/* RegisterOGRODBC() */ +/************************************************************************/ + +void RegisterOGRPGeo() + +{ + OGRSFDriver* poDriver = new OGRPGeoDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ESRI Personal GeoDatabase" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "mdb" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_pgeo.html" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeolayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeolayer.cpp new file mode 100644 index 000000000..6baedf84c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeolayer.cpp @@ -0,0 +1,425 @@ +/****************************************************************************** + * $Id: ogrpgeolayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGeoLayer class, code shared between + * the direct table access, and the generic SQL results. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_pgeo.h" +#include "cpl_string.h" +#include "ogrpgeogeometry.h" + +CPL_CVSID("$Id: ogrpgeolayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRPGeoLayer() */ +/************************************************************************/ + +OGRPGeoLayer::OGRPGeoLayer() + +{ + poDS = NULL; + + pszGeomColumn = NULL; + pszFIDColumn = NULL; + + poStmt = NULL; + + iNextShapeId = 0; + + poSRS = NULL; + nSRSId = -2; // we haven't even queried the database for it yet. +} + +/************************************************************************/ +/* ~OGRPGeoLayer() */ +/************************************************************************/ + +OGRPGeoLayer::~OGRPGeoLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "PGeo", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poStmt != NULL ) + { + delete poStmt; + poStmt = NULL; + } + + if( poFeatureDefn != NULL ) + { + poFeatureDefn->Release(); + poFeatureDefn = NULL; + } + + CPLFree( pszGeomColumn ); + CPLFree( panFieldOrdinals ); + CPLFree( pszFIDColumn ); + + if( poSRS != NULL ) + { + poSRS->Release(); + poSRS = NULL; + } +} + +/************************************************************************/ +/* BuildFeatureDefn() */ +/* */ +/* Build feature definition from a set of column definitions */ +/* set on a statement. Sift out geometry and FID fields. */ +/************************************************************************/ + +CPLErr OGRPGeoLayer::BuildFeatureDefn( const char *pszLayerName, + CPLODBCStatement *poStmt ) + +{ + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + int nRawColumns = poStmt->GetColCount(); + + poFeatureDefn->Reference(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + panFieldOrdinals = (int *) CPLMalloc( sizeof(int) * nRawColumns ); + + for( int iCol = 0; iCol < nRawColumns; iCol++ ) + { + OGRFieldDefn oField( poStmt->GetColName(iCol), OFTString ); + + oField.SetWidth( MAX(0,poStmt->GetColSize( iCol )) ); + + if( pszGeomColumn != NULL + && EQUAL(poStmt->GetColName(iCol),pszGeomColumn) ) + continue; + + if( pszFIDColumn == NULL + && EQUAL(poStmt->GetColName(iCol),"OBJECTID") ) + { + pszFIDColumn = CPLStrdup(poStmt->GetColName(iCol)); + } + + if( pszGeomColumn == NULL + && EQUAL(poStmt->GetColName(iCol),"Shape") ) + { + pszGeomColumn = CPLStrdup(poStmt->GetColName(iCol)); + continue; + } + + switch( poStmt->GetColType(iCol) ) + { + case SQL_INTEGER: + case SQL_SMALLINT: + oField.SetType( OFTInteger ); + break; + + case SQL_BINARY: + case SQL_VARBINARY: + case SQL_LONGVARBINARY: + oField.SetType( OFTBinary ); + break; + + case SQL_DECIMAL: + oField.SetType( OFTReal ); + oField.SetPrecision( poStmt->GetColPrecision(iCol) ); + break; + + case SQL_FLOAT: + case SQL_REAL: + case SQL_DOUBLE: + oField.SetType( OFTReal ); + oField.SetWidth( 0 ); + break; + + case SQL_C_DATE: + oField.SetType( OFTDate ); + break; + + case SQL_C_TIME: + oField.SetType( OFTTime ); + break; + + case SQL_C_TIMESTAMP: + oField.SetType( OFTDateTime ); + break; + + default: + /* leave it as OFTString */; + } + + if( pszGeomColumn != NULL ) + poFeatureDefn->GetGeomFieldDefn(0)->SetName(pszGeomColumn); + + poFeatureDefn->AddFieldDefn( &oField ); + panFieldOrdinals[poFeatureDefn->GetFieldCount() - 1] = iCol+1; + } + + return CE_None; +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRPGeoLayer::ResetReading() + +{ + iNextShapeId = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRPGeoLayer::GetNextFeature() + +{ + for( ; TRUE; ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRPGeoLayer::GetNextRawFeature() + +{ + OGRErr err = OGRERR_NONE; + + if( GetStatement() == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we are marked to restart then do so, and fetch a record. */ +/* -------------------------------------------------------------------- */ + if( !poStmt->Fetch() ) + { + delete poStmt; + poStmt = NULL; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a feature from the current result. */ +/* -------------------------------------------------------------------- */ + int iField; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + if( pszFIDColumn != NULL && poStmt->GetColId(pszFIDColumn) > -1 ) + poFeature->SetFID( + atoi(poStmt->GetColData(poStmt->GetColId(pszFIDColumn))) ); + else + poFeature->SetFID( iNextShapeId ); + + iNextShapeId++; + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Set the fields. */ +/* -------------------------------------------------------------------- */ + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + int iSrcField = panFieldOrdinals[iField]-1; + const char *pszValue = poStmt->GetColData( iSrcField ); + + if( pszValue == NULL ) + /* no value */; + else if( poFeature->GetFieldDefnRef(iField)->GetType() == OFTBinary ) + poFeature->SetField( iField, + poStmt->GetColDataLength(iSrcField), + (GByte *) pszValue ); + else + poFeature->SetField( iField, pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Try to extract a geometry. */ +/* -------------------------------------------------------------------- */ + if( pszGeomColumn != NULL ) + { + int iField = poStmt->GetColId( pszGeomColumn ); + GByte *pabyShape = (GByte *) poStmt->GetColData( iField ); + int nBytes = poStmt->GetColDataLength(iField); + OGRGeometry *poGeom = NULL; + + if( pabyShape != NULL ) + { + err = OGRCreateFromShapeBin( pabyShape, &poGeom, nBytes ); + if( OGRERR_NONE != err ) + { + CPLDebug( "PGeo", + "Translation shape binary to OGR geometry failed (FID=%ld)", + (long)poFeature->GetFID() ); + } + } + + if( poGeom != NULL && OGRERR_NONE == err ) + { + poGeom->assignSpatialReference( poSRS ); + poFeature->SetGeometryDirectly( poGeom ); + } + } + + return poFeature; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRPGeoLayer::GetFeature( GIntBig nFeatureId ) + +{ + /* This should be implemented directly! */ + + return OGRLayer::GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPGeoLayer::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +void OGRPGeoLayer::LookupSRID( int nSRID ) + +{ +/* -------------------------------------------------------------------- */ +/* Fetch the corresponding WKT from the SpatialRef table. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oStmt( poDS->GetSession() ); + + oStmt.Appendf( "SELECT srtext FROM GDB_SpatialRefs WHERE srid = %d", + nSRID ); + + if( !oStmt.ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "'%s' failed.\n%s", + oStmt.GetCommand(), + poDS->GetSession()->GetLastError() ); + return; + } + + if( !oStmt.Fetch() ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "SRID %d lookup failed.\n%s", + nSRID, poDS->GetSession()->GetLastError() ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Check that it isn't just a GUID. We don't know how to */ +/* translate those. */ +/* -------------------------------------------------------------------- */ + char *pszSRText = (char *) oStmt.GetColData(0); + + if( pszSRText[0] == '{' ) + { + CPLDebug( "PGEO", "Ignoreing GUID SRTEXT: %s", pszSRText ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Turn it into an OGRSpatialReference. */ +/* -------------------------------------------------------------------- */ + poSRS = new OGRSpatialReference(); + + if( poSRS->importFromWkt( &pszSRText ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "importFromWKT() failed on SRS '%s'.", + pszSRText); + delete poSRS; + poSRS = NULL; + } + else if( poSRS->morphFromESRI() != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "morphFromESRI() failed on SRS." ); + delete poSRS; + poSRS = NULL; + } + else + nSRSId = nSRID; +} + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char *OGRPGeoLayer::GetFIDColumn() + +{ + if( pszFIDColumn != NULL ) + return pszFIDColumn; + else + return ""; +} + +/************************************************************************/ +/* GetGeometryColumn() */ +/************************************************************************/ + +const char *OGRPGeoLayer::GetGeometryColumn() + +{ + if( pszGeomColumn != NULL ) + return pszGeomColumn; + else + return ""; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeoselectlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeoselectlayer.cpp new file mode 100644 index 000000000..b104b18a6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeoselectlayer.cpp @@ -0,0 +1,181 @@ +/****************************************************************************** + * $Id: ogrpgeoselectlayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGeoSelectLayer class, layer access to the results + * of a SELECT statement executed via ExecuteSQL(). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_pgeo.h" + +CPL_CVSID("$Id: ogrpgeoselectlayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRPGeoSelectLayer() */ +/************************************************************************/ + +OGRPGeoSelectLayer::OGRPGeoSelectLayer( OGRPGeoDataSource *poDSIn, + CPLODBCStatement * poStmtIn ) + +{ + poDS = poDSIn; + + iNextShapeId = 0; + nSRSId = -1; + poFeatureDefn = NULL; + + poStmt = poStmtIn; + pszBaseStatement = CPLStrdup( poStmtIn->GetCommand() ); + + /* Just to make test_ogrsf happy, but would/could need be extended to */ + /* other cases */ + if( EQUALN(pszBaseStatement, "SELECT * FROM ", strlen("SELECT * FROM ")) ) + { + + OGRLayer* poBaseLayer = + poDSIn->GetLayerByName(pszBaseStatement + strlen("SELECT * FROM ")); + if( poBaseLayer != NULL ) + { + poSRS = poBaseLayer->GetSpatialRef(); + if( poSRS != NULL ) + poSRS->Reference(); + } + } + + BuildFeatureDefn( "SELECT", poStmt ); +} + +/************************************************************************/ +/* ~OGRPGeoSelectLayer() */ +/************************************************************************/ + +OGRPGeoSelectLayer::~OGRPGeoSelectLayer() + +{ + ClearStatement(); + CPLFree(pszBaseStatement); +} + +/************************************************************************/ +/* ClearStatement() */ +/************************************************************************/ + +void OGRPGeoSelectLayer::ClearStatement() + +{ + if( poStmt != NULL ) + { + delete poStmt; + poStmt = NULL; + } +} + +/************************************************************************/ +/* GetStatement() */ +/************************************************************************/ + +CPLODBCStatement *OGRPGeoSelectLayer::GetStatement() + +{ + if( poStmt == NULL ) + ResetStatement(); + + return poStmt; +} + +/************************************************************************/ +/* ResetStatement() */ +/************************************************************************/ + +OGRErr OGRPGeoSelectLayer::ResetStatement() + +{ + ClearStatement(); + + iNextShapeId = 0; + + CPLDebug( "ODBC", "Recreating statement." ); + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( pszBaseStatement ); + + if( poStmt->ExecuteSQL() ) + return OGRERR_NONE; + else + { + delete poStmt; + poStmt = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRPGeoSelectLayer::ResetReading() + +{ + if( iNextShapeId != 0 ) + ClearStatement(); + + OGRPGeoLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRPGeoSelectLayer::GetFeature( GIntBig nFeatureId ) + +{ + return OGRPGeoLayer::GetFeature( nFeatureId ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPGeoSelectLayer::TestCapability( const char * pszCap ) + +{ + return OGRPGeoLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRPGeoSelectLayer::GetFeatureCount( int bForce ) + +{ + return OGRPGeoLayer::GetFeatureCount( bForce ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeotablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeotablelayer.cpp new file mode 100644 index 000000000..200a2cd46 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/pgeo/ogrpgeotablelayer.cpp @@ -0,0 +1,383 @@ +/****************************************************************************** + * $Id: ogrpgeotablelayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRPGeoTableLayer class, access to an existing table. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_pgeo.h" +#include "ogrpgeogeometry.h" + +CPL_CVSID("$Id: ogrpgeotablelayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRPGeoTableLayer() */ +/************************************************************************/ + +OGRPGeoTableLayer::OGRPGeoTableLayer( OGRPGeoDataSource *poDSIn ) + +{ + poDS = poDSIn; + pszQuery = NULL; + bUpdateAccess = TRUE; + iNextShapeId = 0; + nSRSId = -1; + poFeatureDefn = NULL; + memset( &sExtent, 0, sizeof(sExtent) ); +} + +/************************************************************************/ +/* ~OGRPGeoTableLayer() */ +/************************************************************************/ + +OGRPGeoTableLayer::~OGRPGeoTableLayer() + +{ + CPLFree( pszQuery ); + ClearStatement(); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +CPLErr OGRPGeoTableLayer::Initialize( const char *pszTableName, + const char *pszGeomCol, + int nShapeType, + double dfExtentLeft, + double dfExtentRight, + double dfExtentBottom, + double dfExtentTop, + int nSRID, + int bHasZ ) + + +{ + CPLODBCSession *poSession = poDS->GetSession(); + + SetDescription( pszTableName ); + + CPLFree( pszGeomColumn ); + if( pszGeomCol == NULL ) + pszGeomColumn = NULL; + else + pszGeomColumn = CPLStrdup( pszGeomCol ); + + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + + sExtent.MinX = dfExtentLeft; + sExtent.MaxX = dfExtentRight; + sExtent.MinY = dfExtentBottom; + sExtent.MaxY = dfExtentTop; + + LookupSRID( nSRID ); + +/* -------------------------------------------------------------------- */ +/* Setup geometry type. */ +/* -------------------------------------------------------------------- */ + OGRwkbGeometryType eOGRType; + + switch( nShapeType ) + { + case ESRI_LAYERGEOMTYPE_NULL: + eOGRType = wkbNone; + break; + + case ESRI_LAYERGEOMTYPE_POINT: + eOGRType = wkbPoint; + break; + + case ESRI_LAYERGEOMTYPE_MULTIPOINT: + eOGRType = wkbMultiPoint; + break; + + case ESRI_LAYERGEOMTYPE_POLYLINE: + eOGRType = wkbLineString; + break; + + case ESRI_LAYERGEOMTYPE_POLYGON: + case ESRI_LAYERGEOMTYPE_MULTIPATCH: + eOGRType = wkbPolygon; + break; + + default: + CPLDebug("PGeo", "Unexpected value for shape type : %d", nShapeType); + eOGRType = wkbUnknown; + break; + } + + if( eOGRType != wkbUnknown && eOGRType != wkbNone && bHasZ ) + eOGRType = wkbSetZ(eOGRType); + +/* -------------------------------------------------------------------- */ +/* Do we have a simple primary key? */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oGetKey( poSession ); + + if( oGetKey.GetPrimaryKeys( pszTableName ) && oGetKey.Fetch() ) + { + pszFIDColumn = CPLStrdup(oGetKey.GetColData( 3 )); + + if( oGetKey.Fetch() ) // more than one field in key! + { + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + CPLDebug( "PGeo", "%s: Compound primary key, ignoring.", + pszTableName ); + } + else + CPLDebug( "PGeo", + "%s: Got primary key %s.", + pszTableName, pszFIDColumn ); + } + else + CPLDebug( "PGeo", "%s: no primary key", pszTableName ); + +/* -------------------------------------------------------------------- */ +/* Get the column definitions for this table. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oGetCol( poSession ); + CPLErr eErr; + + if( !oGetCol.GetColumns( pszTableName ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GetColumns() failed on %s.\n%s", + pszTableName, poSession->GetLastError() ); + return CE_Failure; + } + + eErr = BuildFeatureDefn( pszTableName, &oGetCol ); + if( eErr != CE_None ) + return eErr; + + if( poFeatureDefn->GetFieldCount() == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No column definitions found for table '%s', layer not usable.", + pszTableName ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Set geometry type. */ +/* */ +/* NOTE: per reports from Craig Miller, it seems we cannot really */ +/* trust the ShapeType value. At the very least "line" tables */ +/* sometimes have multilinestrings. So for now we just always */ +/* return wkbUnknown. */ +/* */ +/* TODO - mloskot: Similar issue has been reported in Ticket #1484 */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + poFeatureDefn->SetGeomType( eOGRType ); +#endif + + return CE_None; +} + +/************************************************************************/ +/* ClearStatement() */ +/************************************************************************/ + +void OGRPGeoTableLayer::ClearStatement() + +{ + if( poStmt != NULL ) + { + delete poStmt; + poStmt = NULL; + } +} + +/************************************************************************/ +/* GetStatement() */ +/************************************************************************/ + +CPLODBCStatement *OGRPGeoTableLayer::GetStatement() + +{ + if( poStmt == NULL ) + ResetStatement(); + + return poStmt; +} + +/************************************************************************/ +/* ResetStatement() */ +/************************************************************************/ + +OGRErr OGRPGeoTableLayer::ResetStatement() + +{ + ClearStatement(); + + iNextShapeId = 0; + + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( "SELECT * FROM " ); + poStmt->Append( poFeatureDefn->GetName() ); + if( pszQuery != NULL ) + poStmt->Appendf( " WHERE %s", pszQuery ); + + if( poStmt->ExecuteSQL() ) + return OGRERR_NONE; + else + { + delete poStmt; + poStmt = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRPGeoTableLayer::ResetReading() + +{ + ClearStatement(); + OGRPGeoLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRPGeoTableLayer::GetFeature( GIntBig nFeatureId ) + +{ + if( pszFIDColumn == NULL ) + return OGRPGeoLayer::GetFeature( nFeatureId ); + + ClearStatement(); + + iNextShapeId = nFeatureId; + + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( "SELECT * FROM " ); + poStmt->Append( poFeatureDefn->GetName() ); + poStmt->Appendf( " WHERE %s = " CPL_FRMT_GIB, pszFIDColumn, nFeatureId ); + + if( !poStmt->ExecuteSQL() ) + { + delete poStmt; + poStmt = NULL; + return NULL; + } + + return GetNextRawFeature(); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRPGeoTableLayer::SetAttributeFilter( const char *pszQuery ) + +{ + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + + if( (pszQuery == NULL && this->pszQuery == NULL) + || (pszQuery != NULL && this->pszQuery != NULL + && EQUAL(pszQuery,this->pszQuery)) ) + return OGRERR_NONE; + + CPLFree( this->pszQuery ); + this->pszQuery = pszQuery ? CPLStrdup( pszQuery ) : NULL; + + ClearStatement(); + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPGeoTableLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return m_poFilterGeom == NULL; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else + return OGRPGeoLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRPGeoTableLayer::GetFeatureCount( int bForce ) + +{ + if( m_poFilterGeom != NULL ) + return OGRPGeoLayer::GetFeatureCount( bForce ); + + CPLODBCStatement oStmt( poDS->GetSession() ); + oStmt.Append( "SELECT COUNT(*) FROM " ); + oStmt.Append( poFeatureDefn->GetName() ); + + if( pszQuery != NULL ) + oStmt.Appendf( " WHERE %s", pszQuery ); + + if( !oStmt.ExecuteSQL() || !oStmt.Fetch() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GetFeatureCount() failed on query %s.\n%s", + oStmt.GetCommand(), poDS->GetSession()->GetLastError() ); + return OGRPGeoLayer::GetFeatureCount(bForce); + } + + return CPLAtoGIntBig(oStmt.GetColData(0)); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRPGeoTableLayer::GetExtent( OGREnvelope *psExtent, CPL_UNUSED int bForce ) +{ + *psExtent = sExtent; + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/GNUmakefile new file mode 100644 index 000000000..9a8864993 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrplscenesdataset.o ogrplsceneslayer.o + +CPPFLAGS := $(JSON_INCLUDE) -I.. -I../.. -I../geojson $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_plscenes.h ../../swq.h ../geojson/ogrgeojsonreader.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/drv_plscenes.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/drv_plscenes.html new file mode 100644 index 000000000..813f9061d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/drv_plscenes.html @@ -0,0 +1,196 @@ + + +PLScenes (Planet Labs Scenes API) + + + + +

    PLScenes (Planet Labs Scenes API)

    + +(GDAL/OGR >= 2.0)

    + +This driver can connect to Planet Labs Scenes API. +GDAL/OGR must be built with Curl support in order for the +PLScenes driver to be compiled.

    + +The driver supports read-only operations to list scenes and their metadata +as a vector layer per scene type ("ortho" for example). It can also access raster scenes.

    + +

    Dataset name syntax

    + +The minimal syntax to open a datasource is :
    PLScenes:[options]

    + +Additionnal optional parameters can be specified after the ':' sign. +Currently the following one is supported :

    + +

      +
    • api_key=value: To specify the Planet API KEY. It is mandatory, unless +it is supplied through the open option API_KEY, or the configuration option PL_API_KEY.
    • +
    • scene=scene_id: To specify the scene ID, when accessing raster data. +Optional for vector layer access.
    • +
    • product_type=value: To specify the product type: 'visual', 'analytic' or 'thumb' +(for raster fetching). Default is "visual". Optional for vector layer access.
    • +
    + +If several parameters are specified, they must be separated by a comma.

    + +

    Open options

    + +The following open options are available : +
      +
    • API_KEY=value: To specify the Planet API KEY.
    • +
    • SCENE=scene_id: To specify the scene ID, when accessing raster data. +Optional for vector layer access.
    • +
    • PRODUCT_TYPE=value: To specify the product type: 'visual', 'analytic' or 'thumb +(for raster fetching). Default is "visual". Optional for vector layer access.
    • +
    • RANDOM_ACCESS=YES/NO: Whether raster should be accessed in random access mode +(but with potentially not optimal throughput). If NO, in-memory ingestion is done. +Default is YES.
    • +
    + +

    Configuration options

    + +The following configuration options are available : +
      +
    • PL_API_KEY=value: To specify the Planet API KEY.
    • +
    + +

    Attributes

    + +The scene metadata +are retrieved into the following feature fields for the "ortho" layer :

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDescription
    idStringScene unique identifier.
    acquiredDateTimeThe time that image was taken in UTC.
    camera.bit_depthIntegerBit depth with which the image was taken onboard the satellite. Currently 8 or 12.
    camera.color_modeStringThe color mode of the image as taken by the satellite. Currently "RGB" or "Monochromatic".
    camera.exposure_timeIntegerThe exposure time in microseconds.
    camera.gainIntegerThe analog gain with which the image was taken.
    camera.tdi_pulsesIntegerThe number of pulses used for time delay and integration on the CCD. Currently 0 (if TDI was not used), 4, 6, or 12.
    cloud_cover.estimatedRealThe estimated percentage of the image covered by clouds. Decimal 0-100.
    data.products.analytic.fullStringURL to download scene GeoTIFF of the "analytic" product.
    data.products.visual.fullStringURL to download scene GeoTIFF of the "visual" product.
    file_sizeIntegerThe size of the full image in bytes.
    image_statistics.gsdRealThe ground sample distance (distance between pixel centers measured on the ground) of the image in meters.
    image_statistics.image_qualityStringImage quality category for scene. One of 'test', 'standard', or 'target'.
    image_statistics.snrRealThe estimated signal to noise ratio. Decimal > 0. Values greater than or equal to 50 are considered excellent quality. Values less than 50 and greater than or equal to 20 are considered adequate quality. Values less than 20 are considered poor quality.
    links.fullStringURL to download scene GeoTIFF (same content as data.products.visual.full currently)
    links.selfStringURL to scene information
    links.square_thumbnailStringURL to image thumbnail
    links.thumbnailStringLink to image square thumbnail
    sat.altRealThe altitude of the satellite when the image was taken in kilometers.
    sat.idStringA unique identifier for the satellite that captured this image.
    sat.latRealThe latitude of the satellite when the image was taken in degrees.
    sat.lngRealThe longitude of the satellite when the image was taken in degrees.
    sat.off_nadirRealThe angle off nadir in degrees at which the image was taken.
    strip_idRealA unique float identifier for the set of images taken sequentially be the same satellite.
    sun.altitudeRealThe altitude (angle above horizon) of the sun from the imaged location at the time of capture in degrees.
    sun.azimuthRealThe azimuth (angle clockwise from north) of the sun from the imaged location at the time of capture in degrees.
    sun.local_time_of_dayRealThe local sun time at the imaged location at the time of capture (0-24).
    + +For other layers / scene types, additional attributes may be retrieved.

    + +

    Geometry

    + +The footprint of each scene is reported as a MultiPolygon with a longitude/latitude +WGS84 coordinate system (EPSG:4326). + +

    Filtering

    + +The driver will forward any spatial filter set with SetSpatialFilter() to +the server. It also makes the same for simple attribute filters set with +SetAttributeFilter(). Note that not all attributes support all comparison +operators. Refer to comparator column in Metadata properties

    + +

    Paging

    + +Features are retrieved from the server by chunks of 1000 by default (and this +is the maximum value accepted by the server). +This number can be altered with the PLSCENES_PAGE_SIZE +configuration option.

    + +

    Vector layer (scene metadata) examples

    + +
  • +Listing all scenes available (with the rights of the account) : +
    +ogrinfo -ro -al "PLScenes:" -oo API_KEY=some_value
    +
    +or +
    +ogrinfo -ro -al "PLScenes:api_key=some_value"
    +
    +or +
    +ogrinfo -ro -al "PLScenes:" --config PL_API_KEY some_value
    +
    +

    + +

  • +Listing all scenes available under a point of (lat,lon)=(40,-100) : +
    +ogrinfo -ro -al "PLScenes:" -oo API_KEY=some_value -spat -100,40,-100,40
    +
    +

    + +

  • +Listing all scenes available within a bounding box (lat,lon)=(40,-100) to (lat,lon)=(39,-99) +
    +ogrinfo -ro -al "PLScenes:" -oo API_KEY=some_value -spat -100,40,-99,39
    +
    +

    + +

  • +Listing all scenes available matching critera : +
    +ogrinfo -ro -al "PLScenes:" -oo API_KEY=some_value -where "acquired >= '2015/03/26 00:00:00' AND \"cloud_cover.estimated\" < 10"
    +
    +

    + +

    Raster access

    + +

    Scenes and their thumbnails can be accessed as raster datasets, provided +that the scene ID is specified with the 'scene' parameter / SCENE open option. +The product type (visual, analytic or thumb) can be specified with the +'product_type' parameter / PRODUCT_TYPE open option. The scene id is the +content of the value of the 'id' field of the features of the 'ortho' vector layer

    + +

    This functionality is a conveniency wrapper of the +API for fetching the scene GeoTIFF +

    + +

    Raster access examples

    + +
  • +Displaying raster metadata : + +
    +gdalinfo "PLScenes:scene=scene_id,product_type=analytic" -oo API_KEY=some_value
    +
    +or +
    +gdalinfo "PLScenes:" -oo API_KEY=some_value -oo SCENE=scene_id -oo PRODUCT_TYPE=analytic
    +
    + +
  • +Converting/downloading a whole file: + +
    +gdal_translate "PLScenes:" -oo API_KEY=some_value -oo SCENE=scene_id \
    +                -oo PRODUCT_TYPE=analytic -oo RANDOM_ACCESS=NO out.tif
    +
    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/makefile.vc new file mode 100644 index 000000000..284000876 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrplscenesdataset.obj ogrplsceneslayer.obj +EXTRAFLAGS = -I.. -I..\.. -I..\geojson -I..\geojson\libjson + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/ogr_plscenes.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/ogr_plscenes.h new file mode 100644 index 000000000..a5b6d7748 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/ogr_plscenes.h @@ -0,0 +1,135 @@ +/****************************************************************************** + * $Id: ogr_plscenes.h 29164 2015-05-06 15:01:37Z rouault $ + * + * Project: PlanetLabs scene driver + * Purpose: PLScenes driver interface + * Author: Even Rouault, even dot rouault at spatialys.com + * + ****************************************************************************** + * Copyright (c) 2015, Planet Labs + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_PLSCENES_H_INCLUDED +#define _OGR_PLSCENES_H_INCLUDED + +#include "gdal_priv.h" +#include "ogrsf_frmts.h" +#include "ogr_srs_api.h" +#include "cpl_http.h" +#include +#include "ogr_geojson.h" +#include "ogrgeojsonreader.h" +#include "swq.h" +#include + +class OGRPLScenesLayer; +class OGRPLScenesDataset: public GDALDataset +{ + int bMustCleanPersistant; + CPLString osBaseURL; + CPLString osAPIKey; + + int nLayers; + OGRPLScenesLayer **papoLayers; + std::map oMapResultSetToSourceLayer; + + char **GetBaseHTTPOptions(); + GDALDataset *OpenRasterScene(GDALOpenInfo* poOpenInfo, + CPLString osScene, + char** papszOptions); + + public: + OGRPLScenesDataset(); + ~OGRPLScenesDataset(); + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer *GetLayer(int idx); + virtual OGRLayer *GetLayerByName(const char* pszName); + virtual OGRLayer *ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + json_object *RunRequest(const char* pszURL, + int bQuiet404Error = FALSE); + + static int Identify(GDALOpenInfo* poOpenInfo); + static GDALDataset* Open(GDALOpenInfo* poOpenInfo); +}; + +class OGRPLScenesLayer: public OGRLayer +{ + friend class OGRPLScenesDataset; + + OGRPLScenesDataset* poDS; + CPLString osBaseURL; + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference* poSRS; + int bEOF; + GIntBig nNextFID; + GIntBig nFeatureCount; + CPLString osNextURL; + CPLString osRequestURL; + CPLString osQuery; + + OGRGeoJSONDataSource *poGeoJSONDS; + OGRLayer *poGeoJSONLayer; + + OGRGeometry *poMainFilter; + + int nPageSize; + int bStillInFirstPage; + int bAcquiredAscending; + + int bFilterMustBeClientSideEvaluated; + CPLString osFilterURLPart; + + OGRFeature *GetNextRawFeature(); + CPLString BuildURL(int nFeatures); + int GetNextPage(); + CPLString BuildFilter(swq_expr_node* poNode); + + public: + OGRPLScenesLayer(OGRPLScenesDataset* poDS, + const char* pszName, + const char* pszBaseURL, + json_object* poObjCount10 = NULL); + ~OGRPLScenesLayer(); + + virtual void ResetReading(); + virtual GIntBig GetFeatureCount(int bForce = FALSE); + virtual OGRFeature *GetNextFeature(); + virtual int TestCapability(const char*); + virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; } + + virtual void SetSpatialFilter( OGRGeometry *poGeom ); + virtual OGRErr SetAttributeFilter( const char * ); + + virtual OGRErr GetExtent( OGREnvelope *psExtent, int bForce ); + + void SetMainFilterRect(double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY); + void SetAcquiredOrderingFlag(int bAcquiredAscendingIn) + { bAcquiredAscending = bAcquiredAscendingIn; } +}; + +#endif /* ndef _OGR_PLSCENES_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/ogrplscenesdataset.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/ogrplscenesdataset.cpp new file mode 100644 index 000000000..930d35272 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/ogrplscenesdataset.cpp @@ -0,0 +1,614 @@ +/****************************************************************************** + * $Id: ogrplscenesdataset.cpp 29164 2015-05-06 15:01:37Z rouault $ + * + * Project: PlanetLabs scene driver + * Purpose: Implements OGRPLScenesDataset + * Author: Even Rouault, even dot rouault at spatialys.com + * + ****************************************************************************** + * Copyright (c) 2015, Planet Labs + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_plscenes.h" + +// g++ -g -Wall -fPIC -shared -o ogr_PLSCENES.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/plscene ogr/ogrsf_frmts/plscenes/*.c* -L. -lgdal -Iogr/ogrsf_frmts/geojson -Iogr/ogrsf_frmts/geojson/libjson + +CPL_CVSID("$Id: ogrplscenesdataset.cpp 29164 2015-05-06 15:01:37Z rouault $"); + +extern "C" void RegisterOGRPLSCENES(); + +/************************************************************************/ +/* OGRPLScenesDataset() */ +/************************************************************************/ + +OGRPLScenesDataset::OGRPLScenesDataset() +{ + bMustCleanPersistant = FALSE; + nLayers = 0; + papoLayers = NULL; +} + +/************************************************************************/ +/* ~OGRPLScenesDataset() */ +/************************************************************************/ + +OGRPLScenesDataset::~OGRPLScenesDataset() +{ + for(int i=0;i= nLayers ) + return NULL; + return papoLayers[idx]; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer *OGRPLScenesDataset::GetLayerByName(const char* pszName) +{ + OGRLayer* poLayer = GDALDataset::GetLayerByName(pszName); + if( poLayer != NULL ) + return poLayer; + + CPLString osURL; + osURL = osBaseURL; + osURL += pszName; + osURL += "/"; + json_object* poObj = RunRequest( (osURL + CPLString("?count=10")).c_str() ); + if( poObj == NULL ) + return NULL; + + OGRPLScenesLayer* poPLLayer = new OGRPLScenesLayer(this, pszName, osURL, poObj); + papoLayers = (OGRPLScenesLayer**) CPLRealloc(papoLayers, + sizeof(OGRPLScenesLayer*) * (nLayers + 1)); + papoLayers[nLayers ++] = poPLLayer; + + json_object_put(poObj); + + return poPLLayer; +} + +/***********************************************************************/ +/* ExecuteSQL() */ +/***********************************************************************/ + +OGRLayer* OGRPLScenesDataset::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) +{ + if( EQUALN(pszSQLCommand, "SELECT ", strlen("SELECT ")) ) + { + swq_select oSelect; + CPLString osSQLCommand(pszSQLCommand); + size_t nLimitPos = osSQLCommand.ifind(" limit "); + if( nLimitPos != std::string::npos ) + osSQLCommand.resize(nLimitPos); + + CPLPushErrorHandler(CPLQuietErrorHandler); + OGRErr eErr = oSelect.preparse(osSQLCommand); + CPLPopErrorHandler(); + if( eErr != OGRERR_NONE ) + return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect); + +/* -------------------------------------------------------------------- */ +/* ORDER BY optimization on acquired field */ +/* -------------------------------------------------------------------- */ + if( oSelect.join_count == 0 && oSelect.poOtherSelect == NULL && + oSelect.table_count == 1 && oSelect.order_specs == 1 && + strcmp(oSelect.order_defs[0].field_name, "acquired") == 0 ) + { + int idx; + OGRPLScenesLayer* poLayer = NULL; + for(idx = 0; idx < nLayers; idx ++ ) + { + if( strcmp( papoLayers[idx]->GetName(), + oSelect.table_defs[0].table_name) == 0 ) + { + poLayer = papoLayers[idx]; + break; + } + } + if( poLayer != NULL ) + { + poLayer->SetAcquiredOrderingFlag( + oSelect.order_defs[0].ascending_flag); + OGRLayer* poRet = GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect); + if( poRet ) + oMapResultSetToSourceLayer[poRet] = poLayer; + return poRet; + } + } + } + return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect); +} + +/***********************************************************************/ +/* ReleaseResultSet() */ +/***********************************************************************/ + +void OGRPLScenesDataset::ReleaseResultSet( OGRLayer * poResultsSet ) +{ + if( poResultsSet ) + { + OGRPLScenesLayer* poSrcLayer = oMapResultSetToSourceLayer[poResultsSet]; + // Reset the acquired ordering to its default + if( poSrcLayer ) + { + poSrcLayer->SetAcquiredOrderingFlag(-1); + oMapResultSetToSourceLayer.erase(poResultsSet); + } + delete poResultsSet; + } +} + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int OGRPLScenesDataset::Identify(GDALOpenInfo* poOpenInfo) +{ + return EQUALN(poOpenInfo->pszFilename, "PLSCENES:", strlen("PLSCENES:")); +} + +/************************************************************************/ +/* GetBaseHTTPOptions() */ +/************************************************************************/ + +char** OGRPLScenesDataset::GetBaseHTTPOptions() +{ + bMustCleanPersistant = TRUE; + + char** papszOptions = NULL; + papszOptions = CSLAddString(papszOptions, CPLSPrintf("PERSISTENT=PLSCENES:%p", this)); + papszOptions = CSLAddString(papszOptions, CPLSPrintf("HEADERS=Authorization: api-key %s", osAPIKey.c_str())); + return papszOptions; +} + +/************************************************************************/ +/* RunRequest() */ +/************************************************************************/ + +json_object* OGRPLScenesDataset::RunRequest(const char* pszURL, + int bQuiet404Error) +{ + char** papszOptions = CSLAddString(GetBaseHTTPOptions(), NULL); + CPLHTTPResult * psResult; + if( strncmp(osBaseURL, "/vsimem/", strlen("/vsimem/")) == 0 && + strncmp(pszURL, "/vsimem/", strlen("/vsimem/")) == 0 ) + { + CPLDebug("PLSCENES", "Fetching %s", pszURL); + psResult = (CPLHTTPResult*) CPLCalloc(1, sizeof(CPLHTTPResult)); + vsi_l_offset nDataLength = 0; + CPLString osURL(pszURL); + if( osURL[osURL.size()-1 ] == '/' ) + osURL.resize(osURL.size()-1); + GByte* pabyBuf = VSIGetMemFileBuffer(osURL, &nDataLength, FALSE); + if( pabyBuf ) + { + psResult->pabyData = (GByte*) VSIMalloc(1 + nDataLength); + if( psResult->pabyData ) + { + memcpy(psResult->pabyData, pabyBuf, nDataLength); + psResult->pabyData[nDataLength] = 0; + } + } + else + { + psResult->pszErrBuf = + CPLStrdup(CPLSPrintf("Error 404. Cannot find %s", pszURL)); + } + } + else + { + if( bQuiet404Error ) + CPLPushErrorHandler(CPLQuietErrorHandler); + psResult = CPLHTTPFetch( pszURL, papszOptions); + if( bQuiet404Error ) + CPLPopErrorHandler(); + } + CSLDestroy(papszOptions); + + if( psResult->pszErrBuf != NULL ) + { + if( !(bQuiet404Error && strstr(psResult->pszErrBuf, "404")) ) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s", + psResult->pabyData ? (const char*) psResult->pabyData : + psResult->pszErrBuf); + } + CPLHTTPDestroyResult(psResult); + return NULL; + } + + if( psResult->pabyData == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Empty content returned by server"); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + json_tokener* jstok = NULL; + json_object* poObj = NULL; + + jstok = json_tokener_new(); + poObj = json_tokener_parse_ex(jstok, (const char*) psResult->pabyData, -1); + if( jstok->err != json_tokener_success) + { + CPLError( CE_Failure, CPLE_AppDefined, + "JSON parsing error: %s (at offset %d)", + json_tokener_error_desc(jstok->err), jstok->char_offset); + json_tokener_free(jstok); + CPLHTTPDestroyResult(psResult); + return NULL; + } + json_tokener_free(jstok); + + CPLHTTPDestroyResult(psResult); + + if( json_object_get_type(poObj) != json_type_object ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Return is not a JSON dictionary"); + json_object_put(poObj); + poObj = NULL; + } + + return poObj; +} + +/************************************************************************/ +/* OpenRasterScene() */ +/************************************************************************/ + +GDALDataset* OGRPLScenesDataset::OpenRasterScene(GDALOpenInfo* poOpenInfo, + CPLString osScene, + char** papszOptions) +{ + if( !(poOpenInfo->nOpenFlags & GDAL_OF_RASTER) ) + { + return NULL; + } + + for( char** papszIter = papszOptions; papszIter && *papszIter; papszIter ++ ) + { + char* pszKey; + const char* pszValue = CPLParseNameValue(*papszIter, &pszKey); + if( pszValue != NULL ) + { + if( !EQUAL(pszKey, "api_key") && + !EQUAL(pszKey, "scene") && + !EQUAL(pszKey, "product_type") ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Unsupported option %s", pszKey); + CPLFree(pszKey); + return NULL; + } + CPLFree(pszKey); + } + } + + const char* pszProductType = CSLFetchNameValueDef(papszOptions, "product_type", + CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "PRODUCT_TYPE", "visual")); + + CPLString osRasterURL; + osRasterURL = osBaseURL; + osRasterURL += "ortho/"; + osRasterURL += osScene; + json_object* poObj = RunRequest( osRasterURL ); + if( poObj == NULL ) + return NULL; + json_object* poProperties = json_object_object_get(poObj, "properties"); + if( poProperties == NULL || json_object_get_type(poProperties) != json_type_object ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find properties object"); + json_object_put(poObj); + return NULL; + } + + const char* pszLink = NULL; + if( EQUAL(pszProductType, "thumb") ) + { + json_object* poLinks = json_object_object_get(poProperties, "links"); + if( poLinks != NULL && json_object_get_type(poLinks) == json_type_object ) + { + json_object* poThumbnail = json_object_object_get(poLinks, "thumbnail"); + if( poThumbnail && json_object_get_type(poThumbnail) == json_type_string ) + pszLink = json_object_get_string(poThumbnail); + } + } + else + { + json_object* poData = json_object_object_get(poProperties, "data"); + if( poData != NULL && json_object_get_type(poData) == json_type_object ) + { + json_object* poProducts = json_object_object_get(poData, "products"); + if( poProducts != NULL && json_object_get_type(poProducts) == json_type_object ) + { + json_object* poProduct = json_object_object_get(poProducts, pszProductType); + if( poProduct != NULL && json_object_get_type(poProduct) == json_type_object ) + { + json_object* poFull = json_object_object_get(poProduct, "full"); + if( poFull && json_object_get_type(poFull) == json_type_string ) + pszLink = json_object_get_string(poFull); + } + } + } + } + osRasterURL = pszLink ? pszLink : ""; + json_object_put(poObj); + if( osRasterURL.size() == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find link to scene %s", + osScene.c_str()); + return NULL; + } + + if( strncmp(osRasterURL, "http://", strlen("http://")) == 0 ) + { + osRasterURL = "http://" + osAPIKey + ":@" + osRasterURL.substr(strlen("http://")); + } + else if( strncmp(osRasterURL, "https://", strlen("https://")) == 0 ) + { + osRasterURL = "https://" + osAPIKey + ":@" + osRasterURL.substr(strlen("https://")); + } + + CPLString osOldHead(CPLGetConfigOption("CPL_VSIL_CURL_USE_HEAD", "")); + CPLString osOldExt(CPLGetConfigOption("CPL_VSIL_CURL_ALLOWED_EXTENSIONS", "")); + + int bUseVSICURL = CSLFetchBoolean(poOpenInfo->papszOpenOptions, "RANDOM_ACCESS", TRUE); + if( bUseVSICURL && !(strncmp(osBaseURL, "/vsimem/", strlen("/vsimem/")) == 0) ) + { + CPLSetThreadLocalConfigOption("CPL_VSIL_CURL_USE_HEAD", "NO"); + CPLSetThreadLocalConfigOption("CPL_VSIL_CURL_ALLOWED_EXTENSIONS", "{noext}"); + + VSIStatBufL sStat; + if( VSIStatL(("/vsicurl/" + osRasterURL).c_str(), &sStat) == 0 && + sStat.st_size > 0 ) + { + osRasterURL = "/vsicurl/" + osRasterURL; + } + else + { + CPLDebug("PLSCENES", "Cannot use random access for that file"); + } + } + + GDALDataset* poOutDS = (GDALDataset*) GDALOpen(osRasterURL, GA_ReadOnly); + if( poOutDS ) + { + poOutDS->SetDescription(poOpenInfo->pszFilename); + poOutDS->GetFileList(); /* so as to probe all auxiliary files before reseting the allowed extensions */ + + if( !EQUAL(pszProductType, "thumb") ) + { + OGRPLScenesLayer* poLayer = new OGRPLScenesLayer(this, "ortho", + (osBaseURL + "ortho/").c_str()); + papoLayers = (OGRPLScenesLayer**) CPLRealloc(papoLayers, + sizeof(OGRPLScenesLayer*) * (nLayers + 1)); + papoLayers[nLayers ++] = poLayer; + + /* Attach scene matadata */ + poLayer->SetAttributeFilter(CPLSPrintf("id = '%s'", osScene.c_str())); + OGRFeature* poFeat = poLayer->GetNextFeature(); + if( poFeat ) + { + for(int i=0;iGetFieldCount();i++) + { + if( poFeat->IsFieldSet(i) ) + { + const char* pszKey = poFeat->GetFieldDefnRef(i)->GetNameRef(); + const char* pszVal = poFeat->GetFieldAsString(i); + if( strstr(pszKey, "file_size") == NULL && + strstr(pszVal, "https://") == NULL ) + { + poOutDS->SetMetadataItem(pszKey, pszVal); + } + } + } + } + delete poFeat; + } + } + + if( bUseVSICURL ) + { + CPLSetThreadLocalConfigOption("CPL_VSIL_CURL_USE_HEAD", + osOldHead.size() ? osOldHead.c_str(): NULL); + CPLSetThreadLocalConfigOption("CPL_VSIL_CURL_ALLOWED_EXTENSIONS", + osOldExt.size() ? osOldExt.c_str(): NULL); + } + + return poOutDS; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset* OGRPLScenesDataset::Open(GDALOpenInfo* poOpenInfo) +{ + if( !Identify(poOpenInfo) || poOpenInfo->eAccess == GA_Update ) + return NULL; + + OGRPLScenesDataset* poDS = new OGRPLScenesDataset(); + + poDS->osBaseURL = CPLGetConfigOption("PL_URL", "https://api.planet.com/v0/scenes/"); + + char** papszOptions = CSLTokenizeStringComplex( + poOpenInfo->pszFilename+strlen("PLScenes:"), ",", TRUE, FALSE ); + + poDS->osAPIKey = CSLFetchNameValueDef(papszOptions, "api_key", + CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "API_KEY", + CPLGetConfigOption("PL_API_KEY","")) ); + if( poDS->osAPIKey.size() == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Missing PL_API_KEY configuration option or API_KEY open option"); + delete poDS; + CSLDestroy(papszOptions); + return NULL; + } + + const char* pszScene = CSLFetchNameValueDef(papszOptions, "scene", + CSLFetchNameValue(poOpenInfo->papszOpenOptions, "SCENE")); + if( pszScene ) + { + GDALDataset* poRasterDS = poDS->OpenRasterScene(poOpenInfo, pszScene, + papszOptions); + delete poDS; + CSLDestroy(papszOptions); + return poRasterDS; + } + + for( char** papszIter = papszOptions; papszIter && *papszIter; papszIter ++ ) + { + char* pszKey; + const char* pszValue = CPLParseNameValue(*papszIter, &pszKey); + if( pszValue != NULL ) + { + if( !EQUAL(pszKey, "api_key") && + !EQUAL(pszKey, "spat") ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Unsupported option %s", pszKey); + CPLFree(pszKey); + delete poDS; + CSLDestroy(papszOptions); + return NULL; + } + CPLFree(pszKey); + } + } + + json_object* poObj = poDS->RunRequest(poDS->osBaseURL); + if( poObj == NULL ) + { + delete poDS; + CSLDestroy(papszOptions); + return NULL; + } + + json_object_iter it; + it.key = NULL; + it.val = NULL; + it.entry = NULL; + json_object_object_foreachC( poObj, it ) + { + if( it.val != NULL && json_object_get_type(it.val) == json_type_string ) + { + const char* pszSceneType = it.key; + const char* pszSceneTypeURL = json_object_get_string(it.val); + json_object* poObj = NULL; + if( !EQUAL(pszSceneType, "ortho") ) + poObj = poDS->RunRequest( (CPLString(pszSceneTypeURL) + CPLString("?count=10")).c_str() ); + + OGRPLScenesLayer* poLayer = new OGRPLScenesLayer(poDS, pszSceneType, pszSceneTypeURL, poObj); + + if( poObj ) + json_object_put(poObj); + + poDS->papoLayers = (OGRPLScenesLayer**) CPLRealloc(poDS->papoLayers, + sizeof(OGRPLScenesLayer*) * (poDS->nLayers + 1)); + poDS->papoLayers[poDS->nLayers ++] = poLayer; + + const char* pszSpat = CSLFetchNameValue(papszOptions, "spat"); + if( pszSpat ) + { + char** papszTokens = CSLTokenizeString2(pszSpat, " ", 0); + if( CSLCount(papszTokens) >= 4 ) + { + poLayer->SetMainFilterRect(CPLAtof(papszTokens[0]), + CPLAtof(papszTokens[1]), + CPLAtof(papszTokens[2]), + CPLAtof(papszTokens[3])); + } + CSLDestroy(papszTokens); + } + } + } + + json_object_put(poObj); + + CSLDestroy(papszOptions); + + if( !(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) ) + { + delete poDS; + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRPLSCENES() */ +/************************************************************************/ + +void RegisterOGRPLSCENES() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "PLSCENES" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "PLSCENES" ); + poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Planet Labs Scenes API" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_plscenes.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CONNECTION_PREFIX, "PLSCENES:" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" "); + + poDriver->pfnOpen = OGRPLScenesDataset::Open; + poDriver->pfnIdentify = OGRPLScenesDataset::Identify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/ogrplsceneslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/ogrplsceneslayer.cpp new file mode 100644 index 000000000..72d92e6dc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/plscenes/ogrplsceneslayer.cpp @@ -0,0 +1,725 @@ +/****************************************************************************** + * $Id: ogrplsceneslayer.cpp 29164 2015-05-06 15:01:37Z rouault $ + * + * Project: PlanetLabs scene driver + * Purpose: Implements OGRPLScenesLayer + * Author: Even Rouault, even dot rouault at spatialys.com + * + ****************************************************************************** + * Copyright (c) 2015, Planet Labs + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_plscenes.h" +#include + +CPL_CVSID("$Id: ogrplsceneslayer.cpp 29164 2015-05-06 15:01:37Z rouault $"); + +typedef struct +{ + const char* pszName; + OGRFieldType eType; +} PLAttribute; + +static const PLAttribute apsAttrs[] = { + { "id", OFTString }, + { "acquired", OFTDateTime }, + { "camera.bit_depth", OFTInteger }, + { "camera.color_mode", OFTString }, + { "camera.exposure_time", OFTInteger }, + { "camera.gain", OFTInteger }, + { "camera.tdi_pulses", OFTInteger }, + { "cloud_cover.estimated", OFTReal }, + { "data.products.analytic.full", OFTString }, + { "data.products.visual.full", OFTString }, + { "file_size", OFTInteger }, + { "image_statistics.gsd", OFTReal }, + { "image_statistics.image_quality", OFTString }, + { "image_statistics.snr", OFTReal }, + { "links.full", OFTString }, + { "links.self", OFTString }, + { "links.square_thumbnail", OFTString }, + { "links.thumbnail", OFTString }, + { "sat.alt", OFTReal }, + { "sat.id", OFTString }, + { "sat.lat", OFTReal }, + { "sat.lng", OFTReal }, + { "sat.off_nadir", OFTReal }, + { "strip_id", OFTReal }, + { "sun.altitude", OFTReal }, + { "sun.azimuth", OFTReal }, + { "sun.local_time_of_day", OFTReal }, +}; + +static bool OGRPLScenesLayerFieldNameComparator(const CPLString& osFirst, + const CPLString& osSecond) +{ + const char* pszUnderscore1 = strrchr(osFirst.c_str(), '_'); + const char* pszUnderscore2 = strrchr(osSecond.c_str(), '_'); + int val1, val2; + if( pszUnderscore1 && pszUnderscore2 && + (CPLString(osFirst).substr(0, pszUnderscore1-osFirst.c_str()) == + CPLString(osSecond).substr(0, pszUnderscore2-osSecond.c_str())) && + sscanf(pszUnderscore1 + 1, "%d", &val1) == 1 && + sscanf(pszUnderscore2 + 1, "%d", &val2) == 1 ) + { + return val1 < val2; + } + return osFirst < osSecond; +} + +/************************************************************************/ +/* OGRPLScenesLayer() */ +/************************************************************************/ + +OGRPLScenesLayer::OGRPLScenesLayer(OGRPLScenesDataset* poDS, + const char* pszName, + const char* pszBaseURL, + json_object* poObjCount10) +{ + this->poDS = poDS; + osBaseURL = pszBaseURL; + SetDescription(pszName); + poFeatureDefn = new OGRFeatureDefn(pszName); + poFeatureDefn->SetGeomType(wkbMultiPolygon); + for(int i = 0; i < (int)sizeof(apsAttrs) / (int)sizeof(apsAttrs[0]); i++) + { + OGRFieldDefn oField(apsAttrs[i].pszName, apsAttrs[i].eType); + poFeatureDefn->AddFieldDefn(&oField); + } + poFeatureDefn->Reference(); + poSRS = new OGRSpatialReference(SRS_WKT_WGS84); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + bEOF = FALSE; + nFeatureCount = -1; + nNextFID = 1; + poGeoJSONDS = NULL; + poGeoJSONLayer = NULL; + poMainFilter = NULL; + nPageSize = atoi(CPLGetConfigOption("PLSCENES_PAGE_SIZE", "1000")); + bStillInFirstPage = FALSE; + bAcquiredAscending = -1; + bFilterMustBeClientSideEvaluated = FALSE; + ResetReading(); + + if( poObjCount10 != NULL ) + { + json_object* poCount = json_object_object_get(poObjCount10, "count"); + if( poCount != NULL ) + nFeatureCount = MAX(0, json_object_get_int64(poCount)); + + OGRGeoJSONDataSource* poTmpDS = new OGRGeoJSONDataSource(); + OGRGeoJSONReader oReader; + oReader.SetFlattenNestedAttributes(true, '.'); + oReader.ReadLayer( poTmpDS, "layer", poObjCount10 ); + OGRLayer* poTmpLayer = poTmpDS->GetLayer(0); + if( poTmpLayer ) + { + OGRFeatureDefn* poTmpFDefn = poTmpLayer->GetLayerDefn(); + std::vector aosNewFields; + for(int i=0;iGetFieldCount();i++) + { + if( poFeatureDefn->GetFieldIndex(poTmpFDefn->GetFieldDefn(i)->GetNameRef()) < 0 ) + aosNewFields.push_back(poTmpFDefn->GetFieldDefn(i)->GetNameRef()); + } + std::sort(aosNewFields.begin(), aosNewFields.end(), OGRPLScenesLayerFieldNameComparator); + for(int i=0;i<(int)aosNewFields.size();i++) + { + OGRFieldDefn oField(poTmpFDefn->GetFieldDefn(poTmpFDefn->GetFieldIndex(aosNewFields[i]))); + poFeatureDefn->AddFieldDefn(&oField); + } + } + delete poTmpDS; + } +} + +/************************************************************************/ +/* ~OGRPLScenesLayer() */ +/************************************************************************/ + +OGRPLScenesLayer::~OGRPLScenesLayer() +{ + poFeatureDefn->Release(); + poSRS->Release(); + delete poGeoJSONDS; + delete poMainFilter; +} + +/************************************************************************/ +/* BuildFilter() */ +/************************************************************************/ + +CPLString OGRPLScenesLayer::BuildFilter(swq_expr_node* poNode) +{ + if( poNode->eNodeType == SNT_OPERATION ) + { + if( poNode->nOperation == SWQ_AND && poNode->nSubExprCount == 2 ) + { + // For AND, we can deal with a failure in one of the branch + // since client-side will do that extra filtering + CPLString osFilter1 = BuildFilter(poNode->papoSubExpr[0]); + CPLString osFilter2 = BuildFilter(poNode->papoSubExpr[1]); + if( osFilter1.size() && osFilter2.size() ) + return osFilter1 + "&" + osFilter2; + else if( osFilter1.size() ) + return osFilter1; + else + return osFilter2; + } + else if( (poNode->nOperation == SWQ_EQ || + poNode->nOperation == SWQ_NE || + poNode->nOperation == SWQ_LT || + poNode->nOperation == SWQ_LE || + poNode->nOperation == SWQ_GT || + poNode->nOperation == SWQ_GE) && poNode->nSubExprCount == 2 && + poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN && + poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT && + poNode->papoSubExpr[0]->field_index != poFeatureDefn->GetFieldIndex("id") && + poNode->papoSubExpr[0]->field_index < poFeatureDefn->GetFieldCount() ) + { + OGRFieldDefn *poFieldDefn; + poFieldDefn = poFeatureDefn->GetFieldDefn(poNode->papoSubExpr[0]->field_index); + + CPLString osFilter(poFieldDefn->GetNameRef()); + + int bDateTimeParsed = FALSE; + int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMinute = 0, nSecond = 0; + if( poNode->papoSubExpr[1]->field_type == SWQ_TIMESTAMP ) + { + if( sscanf(poNode->papoSubExpr[1]->string_value,"%04d/%02d/%02d %02d:%02d:%02d", + &nYear, &nMonth, &nDay, &nHour, &nMinute, &nSecond) >= 3 || + sscanf(poNode->papoSubExpr[1]->string_value,"%04d-%02d-%02dT%02d:%02d:%02d", + &nYear, &nMonth, &nDay, &nHour, &nMinute, &nSecond) >= 3 ) + bDateTimeParsed = TRUE; + } + + osFilter += "."; + if( poNode->nOperation == SWQ_EQ ) + { + if( bDateTimeParsed ) + osFilter += "gte"; + else + osFilter += "eq"; + } + else if( poNode->nOperation == SWQ_NE ) + osFilter += "neq"; + else if( poNode->nOperation == SWQ_LT ) + osFilter += "lt"; + else if( poNode->nOperation == SWQ_LE ) + osFilter += "lte"; + else if( poNode->nOperation == SWQ_GT ) + osFilter += "gt"; + else if( poNode->nOperation == SWQ_GE ) + osFilter += "gte"; + osFilter += "="; + + if (poNode->papoSubExpr[1]->field_type == SWQ_FLOAT) + osFilter += CPLSPrintf("%.8f", poNode->papoSubExpr[1]->float_value); + else if (poNode->papoSubExpr[1]->field_type == SWQ_INTEGER) + osFilter += CPLSPrintf(CPL_FRMT_GIB, poNode->papoSubExpr[1]->int_value); + else if (poNode->papoSubExpr[1]->field_type == SWQ_STRING) + osFilter += poNode->papoSubExpr[1]->string_value; + else if (poNode->papoSubExpr[1]->field_type == SWQ_TIMESTAMP) + { + if( bDateTimeParsed ) + { + osFilter += CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d", + nYear, nMonth, nDay, nHour, nMinute, nSecond); + if( poNode->nOperation == SWQ_EQ ) + { + osFilter += "&"; + osFilter += poFieldDefn->GetNameRef(); + osFilter += ".lt="; + nSecond ++; + if( nSecond == 60 ) { nSecond = 0; nMinute ++; } + if( nMinute == 60 ) { nMinute = 0; nHour ++; } + if( nHour == 24 ) { nHour = 0; nDay ++; } + osFilter += CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d", + nYear, nMonth, nDay, nHour, nMinute, nSecond); + } + } + else + osFilter += poNode->papoSubExpr[1]->string_value; + } + return osFilter; + } + } + if( !bFilterMustBeClientSideEvaluated ) + { + bFilterMustBeClientSideEvaluated = TRUE; + CPLDebug("PLSCENES", + "Part or full filter will have to be evaluated on client side."); + } + return ""; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRPLScenesLayer::ResetReading() +{ + bEOF = FALSE; + if( poGeoJSONLayer && bStillInFirstPage ) + poGeoJSONLayer->ResetReading(); + else + poGeoJSONLayer = NULL; + nNextFID = 1; + bStillInFirstPage = TRUE; + osRequestURL = BuildURL(nPageSize); +} + +/************************************************************************/ +/* BuildURL() */ +/************************************************************************/ + +CPLString OGRPLScenesLayer::BuildURL(int nFeatures) +{ + CPLString osURL = osBaseURL + CPLSPrintf("?count=%d", nFeatures); + + if( bAcquiredAscending == 1 ) + osURL += "&order_by=acquired%20asc"; + else if( bAcquiredAscending == 0 ) + osURL += "&order_by=acquired%20desc"; + + if( m_poFilterGeom != NULL || poMainFilter != NULL ) + { + OGRGeometry* poIntersection = NULL; + OGRGeometry* poFilterGeom = m_poFilterGeom; + if( poFilterGeom ) + { + OGREnvelope sEnvelope; + poFilterGeom->getEnvelope(&sEnvelope); + if( sEnvelope.MinX <= -180 && sEnvelope.MinY <= -90 && + sEnvelope.MaxX >= 180 && sEnvelope.MaxY >= 90 ) + poFilterGeom = NULL; + } + + if( poFilterGeom && poMainFilter ) + poIntersection = poFilterGeom->Intersection(poMainFilter); + else if( poFilterGeom ) + poIntersection = poFilterGeom; + else if( poMainFilter ) + poIntersection = poMainFilter; + if( poIntersection ) + { + char* pszWKT = NULL; + OGREnvelope sEnvelope; + poIntersection->getEnvelope(&sEnvelope); + if( sEnvelope.MinX == sEnvelope.MaxX && sEnvelope.MinY == sEnvelope.MaxY ) + { + pszWKT = CPLStrdup(CPLSPrintf("POINT(%.18g %.18g)", + sEnvelope.MinX, sEnvelope.MinY)); + } + else + poIntersection->exportToWkt(&pszWKT); + + osURL += "&intersects="; + char* pszWKTEscaped = CPLEscapeString(pszWKT, -1, CPLES_URL); + osURL += pszWKTEscaped; + CPLFree(pszWKTEscaped); + CPLFree(pszWKT); + } + if( poIntersection != m_poFilterGeom && poIntersection != poMainFilter ) + delete poIntersection; + } + + if( osFilterURLPart.size() ) + { + if( osFilterURLPart[0] == '&' ) + osURL += osFilterURLPart; + else + osURL = osBaseURL + osFilterURLPart; + } + + return osURL; +} + +/************************************************************************/ +/* GetNextPage() */ +/************************************************************************/ + +int OGRPLScenesLayer::GetNextPage() +{ + delete poGeoJSONDS; + poGeoJSONLayer = NULL; + poGeoJSONDS = NULL; + + if( osRequestURL.size() == 0 ) + { + bEOF = TRUE; + if( !bFilterMustBeClientSideEvaluated && nFeatureCount < 0 ) + nFeatureCount = 0; + return FALSE; + } + // In the case of a "id = 'foo'" filter, a non existing resource + // will cause a 404 error, which we want to be silent + int bQuiet404Error = ( osRequestURL.find('?') == std::string::npos ); + json_object* poObj = poDS->RunRequest(osRequestURL, bQuiet404Error); + if( poObj == NULL ) + { + bEOF = TRUE; + if( !bFilterMustBeClientSideEvaluated && nFeatureCount < 0 ) + nFeatureCount = 0; + return FALSE; + } + + if( !bFilterMustBeClientSideEvaluated && nFeatureCount < 0 ) + { + json_object* poType = json_object_object_get(poObj, "type"); + if( poType && json_object_get_type(poType) == json_type_string && + strcmp(json_object_get_string(poType), "Feature") == 0 ) + { + nFeatureCount = 1; + } + else + { + json_object* poCount = json_object_object_get(poObj, "count"); + if( poCount == NULL ) + { + json_object_put(poObj); + bEOF = TRUE; + nFeatureCount = 0; + return FALSE; + } + nFeatureCount = MAX(0, json_object_get_int64(poCount)); + } + } + + // Parse the Feature/FeatureCollection with the GeoJSON reader + poGeoJSONDS = new OGRGeoJSONDataSource(); + OGRGeoJSONReader oReader; + oReader.SetFlattenNestedAttributes(true, '.'); + oReader.ReadLayer( poGeoJSONDS, "layer", poObj); + poGeoJSONLayer = poGeoJSONDS->GetLayer(0); + + // Get URL of next page + osNextURL = ""; + if( poGeoJSONLayer ) + { + json_object* poLinks = json_object_object_get(poObj, "links"); + if( poLinks && json_object_get_type(poLinks) == json_type_object ) + { + json_object* poNext = json_object_object_get(poLinks, "next"); + if( poNext && json_object_get_type(poNext) == json_type_string ) + { + osNextURL = json_object_get_string(poNext); + } + } + } + + json_object_put(poObj); + return poGeoJSONLayer != NULL; +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRPLScenesLayer::SetSpatialFilter( OGRGeometry *poGeomIn ) +{ + nFeatureCount = -1; + poGeoJSONLayer = NULL; + + if( poGeomIn ) + { + OGREnvelope sEnvelope; + poGeomIn->getEnvelope(&sEnvelope); + if( sEnvelope.MinX == sEnvelope.MaxX && sEnvelope.MinY == sEnvelope.MaxY ) + { + OGRPoint p(sEnvelope.MinX, sEnvelope.MinY); + InstallFilter(&p); + } + else + InstallFilter( poGeomIn ); + } + else + InstallFilter( poGeomIn ); + + ResetReading(); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRPLScenesLayer::SetAttributeFilter( const char *pszQuery ) + +{ + if( pszQuery == NULL ) + osQuery = ""; + else + osQuery = pszQuery; + + nFeatureCount = -1; + poGeoJSONLayer = NULL; + + OGRErr eErr = OGRLayer::SetAttributeFilter(pszQuery); + + osFilterURLPart = ""; + bFilterMustBeClientSideEvaluated = FALSE; + if( m_poAttrQuery != NULL ) + { + swq_expr_node* poNode = (swq_expr_node*) m_poAttrQuery->GetSWQExpr(); + + poNode->ReplaceBetweenByGEAndLERecurse(); + + if( poNode->eNodeType == SNT_OPERATION && + poNode->nOperation == SWQ_EQ && poNode->nSubExprCount == 2 && + poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN && + poNode->papoSubExpr[0]->field_index == poFeatureDefn->GetFieldIndex("id") && + poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT && + poNode->papoSubExpr[1]->field_type == SWQ_STRING ) + { + osFilterURLPart = poNode->papoSubExpr[1]->string_value; + } + else + { + CPLString osFilter = BuildFilter(poNode); + if( osFilter.size() ) + { + osFilterURLPart = "&"; + osFilterURLPart += osFilter; + } + } + } + + ResetReading(); + + return eErr; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRPLScenesLayer::GetNextFeature() +{ + if( !bFilterMustBeClientSideEvaluated ) + return GetNextRawFeature(); + + OGRFeature *poFeature; + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature* OGRPLScenesLayer::GetNextRawFeature() +{ + if( bEOF || + (!bFilterMustBeClientSideEvaluated && nFeatureCount >= 0 && nNextFID > nFeatureCount) ) + return NULL; + + if( poGeoJSONLayer == NULL ) + { + if( !GetNextPage() ) + return NULL; + } + +#ifdef notdef + if( CSLTestBoolean(CPLGetConfigOption("OGR_LIMIT_TOO_MANY_FEATURES", "FALSE")) && + nFeatureCount > nPageSize ) + { + bEOF = TRUE; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + OGRGeometry* poGeom; + const char* pszWKT = "MULTIPOLYGON(((-180 90,180 90,180 -90,-180 -90,-180 90)))"; + OGRGeometryFactory::createFromWkt((char**)&pszWKT, poSRS, &poGeom); + poFeature->SetGeometryDirectly(poGeom); + return poFeature; + } +#endif + + OGRFeature* poGeoJSONFeature = poGeoJSONLayer->GetNextFeature(); + if( poGeoJSONFeature == NULL ) + { + osRequestURL = osNextURL; + bStillInFirstPage = FALSE; + if( !GetNextPage() ) + return NULL; + poGeoJSONFeature = poGeoJSONLayer->GetNextFeature(); + if( poGeoJSONFeature == NULL ) + { + bEOF = TRUE; + return NULL; + } + } + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFID(nNextFID++); + + OGRGeometry* poGeom = poGeoJSONFeature->StealGeometry(); + if( poGeom != NULL ) + { + if( poGeom->getGeometryType() == wkbPolygon ) + { + OGRMultiPolygon* poMP = new OGRMultiPolygon(); + poMP->addGeometryDirectly(poGeom); + poGeom = poMP; + } + poGeom->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poGeom); + } + + for(int i=0;iGetFieldCount();i++) + { + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(i); + OGRFieldType eType = poFieldDefn->GetType(); + int iSrcField = poGeoJSONFeature->GetFieldIndex(poFieldDefn->GetNameRef()); + if( iSrcField >= 0 && poGeoJSONFeature->IsFieldSet(iSrcField) ) + { + if( eType == OFTInteger ) + poFeature->SetField(i, + poGeoJSONFeature->GetFieldAsInteger(iSrcField)); + else if( eType == OFTReal ) + poFeature->SetField(i, + poGeoJSONFeature->GetFieldAsDouble(iSrcField)); + else + poFeature->SetField(i, + poGeoJSONFeature->GetFieldAsString(iSrcField)); + } + } + + delete poGeoJSONFeature; + + return poFeature; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRPLScenesLayer::GetFeatureCount(int bForce) +{ + if( nFeatureCount < 0 ) + { + CPLString osURL(BuildURL(1)); + if( bFilterMustBeClientSideEvaluated ) + { + nFeatureCount = OGRLayer::GetFeatureCount(bForce); + } + else if( osURL.find('?') == std::string::npos ) + { + /* Case of a "id = XXXXX" filter: we get directly a Feature, */ + /* not a FeatureCollection */ + GetNextPage(); + } + else + { + nFeatureCount = 0; + json_object* poObj = poDS->RunRequest(osURL); + if( poObj != NULL ) + { + json_object* poCount = json_object_object_get(poObj, "count"); + if( poCount != NULL ) + nFeatureCount = MAX(0, json_object_get_int64(poCount)); + + // Small optimization, if the feature count is actually 1 + // then we can fetch it as the full layer + if( nFeatureCount == 1 ) + { + delete poGeoJSONDS; + // Parse the Feature/FeatureCollection with the GeoJSON reader + poGeoJSONDS = new OGRGeoJSONDataSource(); + OGRGeoJSONReader oReader; + oReader.SetFlattenNestedAttributes(true, '.'); + oReader.ReadLayer( poGeoJSONDS, "layer", poObj); + poGeoJSONLayer = poGeoJSONDS->GetLayer(0); + osNextURL = ""; + } + json_object_put(poObj); + } + } + } + + return nFeatureCount; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRPLScenesLayer::GetExtent( OGREnvelope *psExtent, int bForce ) +{ + GetFeatureCount(); + if( nFeatureCount > 0 && nFeatureCount < nPageSize ) + return OGRLayer::GetExtentInternal(0, psExtent, bForce); + + psExtent->MinX = -180; + psExtent->MinY = -90; + psExtent->MaxX = 180; + psExtent->MaxY = 90; + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetMainFilterRect() */ +/************************************************************************/ + +void OGRPLScenesLayer::SetMainFilterRect(double dfMinX, double dfMinY, + double dfMaxX, double dfMaxY) +{ + delete poMainFilter; + if( dfMinX == dfMaxX && dfMinY == dfMaxY ) + poMainFilter = new OGRPoint(dfMinX, dfMinY); + else + { + OGRPolygon* poPolygon = new OGRPolygon(); + poMainFilter = poPolygon; + OGRLinearRing* poLR = new OGRLinearRing; + poPolygon->addRingDirectly(poLR); + poLR->addPoint(dfMinX, dfMinY); + poLR->addPoint(dfMinX, dfMaxY); + poLR->addPoint(dfMaxX, dfMaxY); + poLR->addPoint(dfMaxX, dfMinY); + poLR->addPoint(dfMinX, dfMinY); + } + ResetReading(); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRPLScenesLayer::TestCapability(const char* pszCap) +{ + if( EQUAL(pszCap, OLCFastFeatureCount) ) + return !bFilterMustBeClientSideEvaluated; + if( EQUAL(pszCap, OLCStringsAsUTF8) ) + return TRUE; + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/GNUmakefile new file mode 100644 index 000000000..62765e3e7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/GNUmakefile @@ -0,0 +1,12 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrrecdriver.o ogrrecdatasource.o ogrreclayer.o ll_recio.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ll_recio.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ll_recio.cpp new file mode 100644 index 000000000..baca46cce --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ll_recio.cpp @@ -0,0 +1,189 @@ +/****************************************************************************** + * $Id: ll_recio.cpp 25185 2012-10-27 19:40:46Z rouault $ + * + * Project: EPIInfo .REC Reader + * Purpose: Implements low level REC reading API. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_rec.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ll_recio.cpp 25185 2012-10-27 19:40:46Z rouault $"); + +static int nNextRecLine = 0; + +/************************************************************************/ +/* RECGetFieldCount() */ +/************************************************************************/ + +int RECGetFieldCount( FILE * fp ) + +{ + const char *pszLine = CPLReadLine( fp ); + if( pszLine == NULL ) + return -1; + if( atoi(pszLine) < 1 ) + return -1; + + nNextRecLine = 1; + + return atoi(pszLine); +} + +/************************************************************************/ +/* RECGetFieldDefinition() */ +/************************************************************************/ + +int RECGetFieldDefinition( FILE *fp, char *pszFieldname, + int *pnType, int *pnWidth, int *pnPrecision ) + +{ + const char *pszLine = CPLReadLine( fp ); + int nTypeCode; + OGRFieldType eFType = OFTString; + + if( pszLine == NULL ) + return FALSE; + + if( strlen(pszLine) < 44 ) + return FALSE; + + // Extract field width. + *pnWidth = atoi( RECGetField( pszLine, 37, 4 ) ); + + // Is this an real, integer or string field? Default to string. + nTypeCode = atoi(RECGetField(pszLine,33,4)); + if( nTypeCode == 0 ) + eFType = OFTInteger; + else if( nTypeCode > 100 && nTypeCode < 120 ) + { + eFType = OFTReal; + } + else if( nTypeCode == 6 ) + { + if( *pnWidth < 3 ) + eFType = OFTInteger; + else + eFType = OFTReal; + } + else + eFType = OFTString; + + *pnType = (int) eFType; + + strcpy( pszFieldname, RECGetField( pszLine, 2, 10 ) ); + *pnPrecision = 0; + + if( nTypeCode > 100 && nTypeCode < 120 ) + *pnPrecision = nTypeCode - 100; + else if( eFType == OFTReal ) + { + *pnPrecision = *pnWidth - 1; + } + + nNextRecLine++; + + return TRUE; +} + +/************************************************************************/ +/* RECGetField() */ +/************************************************************************/ + +const char *RECGetField( const char *pszSrc, int nStart, int nWidth ) + +{ + static char szWorkField[128]; + int i; + + strncpy( szWorkField, pszSrc+nStart-1, nWidth ); + szWorkField[nWidth] = '\0'; + + i = strlen(szWorkField)-1; + + while( i >= 0 && szWorkField[i] == ' ' ) + szWorkField[i--] = '\0'; + + return szWorkField; +} + +/************************************************************************/ +/* RECReadRecord() */ +/************************************************************************/ + +int RECReadRecord( FILE *fp, char *pszRecord, int nRecordLength ) + +{ + int nDataLen = 0; + + while( nDataLen < nRecordLength ) + { + const char *pszLine = CPLReadLine( fp ); + int iSegLen; + + nNextRecLine++; + + if( pszLine == NULL ) + return FALSE; + + if( *pszLine == 0 || *pszLine == 26 /* Cntl-Z - DOS EOF */ ) + return FALSE; + + // If the end-of-line markers is '?' the record is deleted. + iSegLen = strlen(pszLine); + if( pszLine[iSegLen-1] == '?' ) + { + pszRecord[0] = '\0'; + nDataLen = 0; + continue; + } + + // Strip off end-of-line '!' marker. + if( pszLine[iSegLen-1] != '!' + && pszLine[iSegLen-1] != '^' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Apparent corrupt data line at line=%d", + nNextRecLine ); + return FALSE; + } + + iSegLen--; + if( nDataLen + iSegLen > nRecordLength ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too much data for line at line %d.", + nNextRecLine-1 ); + return FALSE; + } + + strncpy( pszRecord+nDataLen, pszLine, iSegLen ); + pszRecord[nDataLen+iSegLen] = '\0'; + nDataLen += iSegLen; + } + + return nDataLen; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/makefile.vc new file mode 100644 index 000000000..c60a5f966 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/makefile.vc @@ -0,0 +1,17 @@ + +OBJ = ogrrecdriver.obj ogrrecdatasource.obj ogrreclayer.obj \ + ll_recio.obj + +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ogr_rec.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ogr_rec.h new file mode 100644 index 000000000..b27ff06d4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ogr_rec.h @@ -0,0 +1,107 @@ +/****************************************************************************** + * $Id: ogr_rec.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: Epi .REC Translator + * Purpose: Definition of classes for OGR .REC support. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_REC_H_INCLUDED +#define _OGR_REC_H_INCLUDED + +#include "ogrsf_frmts.h" + +class OGRRECDataSource; + +CPL_C_START +int CPL_DLL RECGetFieldCount( FILE *fp); +int CPL_DLL RECGetFieldDefinition( FILE *fp, char *pszFieldName, int *pnType, + int *pnWidth, int *pnPrecision ); +int CPL_DLL RECReadRecord( FILE *fp, char *pszRecBuf, int nRecordLength ); +const char CPL_DLL *RECGetField( const char *pszSrc, int nStart, int nWidth ); +CPL_C_END + + +/************************************************************************/ +/* OGRRECLayer */ +/************************************************************************/ + +class OGRRECLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + + FILE *fpREC; + int nStartOfData; + int bIsValid; + + int nFieldCount; + int *panFieldOffset; + int *panFieldWidth; + int nRecordLength; + + int nNextFID; + + OGRFeature * GetNextUnfilteredFeature(); + + public: + OGRRECLayer( const char *pszName, FILE *fp, + int nFieldCount ); + ~OGRRECLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + void SetSpatialFilter( OGRGeometry * ) {} + + int TestCapability( const char * ); + + int IsValid() { return bIsValid; } + +}; + +/************************************************************************/ +/* OGRRECDataSource */ +/************************************************************************/ + +class OGRRECDataSource : public OGRDataSource +{ + char *pszName; + + OGRRECLayer *poLayer; + + public: + OGRRECDataSource(); + ~OGRRECDataSource(); + + int Open( const char * pszFilename ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return 1; } + OGRLayer *GetLayer( int ); + int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_REC_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ogrrecdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ogrrecdatasource.cpp new file mode 100644 index 000000000..3febb84fd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ogrrecdatasource.cpp @@ -0,0 +1,130 @@ +/****************************************************************************** + * $Id: ogrrecdatasource.cpp 26243 2013-07-29 20:45:59Z rouault $ + * + * Project: Epiinfo .REC Translator + * Purpose: Implements OGRRECDataSource class + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_rec.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrrecdatasource.cpp 26243 2013-07-29 20:45:59Z rouault $"); + +/************************************************************************/ +/* OGRRECDataSource() */ +/************************************************************************/ + +OGRRECDataSource::OGRRECDataSource() + +{ + poLayer = NULL; + + pszName = NULL; +} + +/************************************************************************/ +/* ~OGRRECDataSource() */ +/************************************************************************/ + +OGRRECDataSource::~OGRRECDataSource() + +{ + if( poLayer != NULL ) + delete poLayer; + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRRECDataSource::TestCapability( const char * ) + +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRRECDataSource::GetLayer( int iLayer ) + +{ + if( iLayer == 0 ) + return poLayer; + else + return NULL; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRRECDataSource::Open( const char * pszFilename ) + +{ + pszName = CPLStrdup( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Verify that the extension is REC. */ +/* -------------------------------------------------------------------- */ + if( !(strlen(pszFilename) > 4 && + EQUAL(pszFilename+strlen(pszFilename)-4,".rec") ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + const char * pszLine; + FILE * fp; + + fp = VSIFOpen( pszFilename, "rb" ); + if( fp == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read a line, and verify that it consists of at least one */ +/* field that is a number greater than zero. */ +/* -------------------------------------------------------------------- */ + int nFieldCount; + pszLine = CPLReadLine( fp ); + + nFieldCount = atoi(pszLine); + if( nFieldCount < 1 || nFieldCount > 1000 ) + { + VSIFClose( fp ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Create a layer. */ +/* -------------------------------------------------------------------- */ + poLayer = new OGRRECLayer( CPLGetBasename(pszFilename), fp, nFieldCount ); + + return poLayer->IsValid(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ogrrecdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ogrrecdriver.cpp new file mode 100644 index 000000000..a8cd38daf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ogrrecdriver.cpp @@ -0,0 +1,92 @@ +/****************************************************************************** + * $Id: ogrrecdriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: REC Translator + * Purpose: Implements EpiInfo .REC driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_rec.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrrecdriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRRECDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + OGRRECDataSource *poDS; + + if( poOpenInfo->fpL == NULL || + !EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "REC") ) + { + return NULL; + } + + poDS = new OGRRECDataSource(); + if( !poDS->Open( poOpenInfo->pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + if( poDS != NULL && poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "REC Driver doesn't support update." ); + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRREC() */ +/************************************************************************/ + +void RegisterOGRREC() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "REC" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "REC" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "rec" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "EPIInfo .REC " ); + + poDriver->pfnOpen = OGRRECDriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ogrreclayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ogrreclayer.cpp new file mode 100644 index 000000000..364826168 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/rec/ogrreclayer.cpp @@ -0,0 +1,308 @@ +/****************************************************************************** + * $Id: ogrreclayer.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: EPIInfo .REC Reader + * Purpose: Implements OGRRECLayer class. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_rec.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrreclayer.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRRECLayer() */ +/* */ +/* Note that the OGRRECLayer assumes ownership of the passed */ +/* file pointer. */ +/************************************************************************/ + +OGRRECLayer::OGRRECLayer( const char *pszLayerNameIn, + FILE * fp, int nFieldCountIn ) + +{ + fpREC = fp; + bIsValid = FALSE; + nStartOfData = 0; + + nNextFID = 1; + + poFeatureDefn = new OGRFeatureDefn( pszLayerNameIn ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + nFieldCount = 0; + panFieldOffset = (int *) CPLCalloc(sizeof(int),nFieldCountIn); + panFieldWidth = (int *) CPLCalloc(sizeof(int),nFieldCountIn); + +/* -------------------------------------------------------------------- */ +/* Read field definition lines. */ +/* -------------------------------------------------------------------- */ + int iField; + + for( nFieldCount=0, iField = 0; iField < nFieldCountIn; iField++ ) + { + const char *pszLine = CPLReadLine( fp ); + int nTypeCode; + OGRFieldType eFType = OFTString; + + if( pszLine == NULL ) + return; + + if( strlen(pszLine) < 44 ) + return; + + // Extract field width. + panFieldWidth[nFieldCount] = atoi( RECGetField( pszLine, 37, 4 ) ); + if( panFieldWidth[nFieldCount] < 0 ) + return; + + // Is this an real, integer or string field? Default to string. + nTypeCode = atoi(RECGetField(pszLine,33,4)); + if( nTypeCode == 12 ) + eFType = OFTInteger; + else if( nTypeCode > 100 && nTypeCode < 120 ) + { + eFType = OFTReal; + } + else if( nTypeCode == 0 || nTypeCode == 6 || nTypeCode == 102 ) + { + if( panFieldWidth[nFieldCount] < 3 ) + eFType = OFTInteger; + else + eFType = OFTReal; + } + else + eFType = OFTString; + + OGRFieldDefn oField( RECGetField( pszLine, 2, 10 ), eFType ); + + // Establish field offset. + if( nFieldCount > 0 ) + panFieldOffset[nFieldCount] + = panFieldOffset[nFieldCount-1] + panFieldWidth[nFieldCount-1]; + + if( nTypeCode > 100 && nTypeCode < 120 ) + { + oField.SetWidth( panFieldWidth[nFieldCount] ); + oField.SetPrecision( nTypeCode - 100 ); + } + else if( eFType == OFTReal ) + { + oField.SetWidth( panFieldWidth[nFieldCount]*2 ); + oField.SetPrecision( panFieldWidth[nFieldCount]-1 ); + } + else + oField.SetWidth( panFieldWidth[nFieldCount] ); + + // Skip fields that are only screen labels. + if( panFieldWidth[nFieldCount] == 0 ) + continue; + + poFeatureDefn->AddFieldDefn( &oField ); + nFieldCount++; + } + + if( nFieldCount == 0 ) + return; + + nRecordLength = panFieldOffset[nFieldCount-1]+panFieldWidth[nFieldCount-1]; + bIsValid = TRUE; + + nStartOfData = VSIFTell( fp ); +} + +/************************************************************************/ +/* ~OGRRECLayer() */ +/************************************************************************/ + +OGRRECLayer::~OGRRECLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "REC", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( fpREC != NULL ) + VSIFClose( fpREC ); + + if( poFeatureDefn ) + poFeatureDefn->Release(); + + CPLFree( panFieldOffset ); + CPLFree( panFieldWidth ); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRRECLayer::ResetReading() + +{ + VSIFSeek( fpREC, nStartOfData, SEEK_SET ); + nNextFID = 1; +} + +/************************************************************************/ +/* GetNextUnfilteredFeature() */ +/************************************************************************/ + +OGRFeature * OGRRECLayer::GetNextUnfilteredFeature() + +{ +/* -------------------------------------------------------------------- */ +/* Read and assemble the source data record. */ +/* -------------------------------------------------------------------- */ + int nDataLen = 0; + char *pszRecord = (char *) CPLMalloc(nRecordLength + 2 ); + + while( nDataLen < nRecordLength ) + { + const char *pszLine = CPLReadLine( fpREC ); + int iSegLen; + + if( pszLine == NULL ) + { + CPLFree( pszRecord ); + return NULL; + } + + if( *pszLine == 0 || *pszLine == 26 /* Cntl-Z - DOS EOF */ ) + { + CPLFree( pszRecord ); + return NULL; + } + + // If the end-of-line markers is '?' the record is deleted. + iSegLen = strlen(pszLine); + if( pszLine[iSegLen-1] == '?' ) + { + pszRecord[0] = '\0'; + nDataLen = 0; + continue; + } + + // Strip off end-of-line '!' marker. + if( pszLine[iSegLen-1] != '!' + && pszLine[iSegLen-1] != '^' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Apparent corrupt data line .. record FID=%d", + nNextFID ); + CPLFree( pszRecord ); + return NULL; + } + + iSegLen--; + if( nDataLen + iSegLen > nRecordLength ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too much data for record %d.", + nNextFID ); + CPLFree( pszRecord ); + return NULL; + } + + strncpy( pszRecord+nDataLen, pszLine, iSegLen ); + pszRecord[nDataLen+iSegLen] = '\0'; + nDataLen += iSegLen; + } + +/* -------------------------------------------------------------------- */ +/* Create the OGR feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poFeature = new OGRFeature( poFeatureDefn ); + +/* -------------------------------------------------------------------- */ +/* Set attributes for any indicated attribute records. */ +/* -------------------------------------------------------------------- */ + int iAttr; + + for( iAttr = 0; iAttr < nFieldCount; iAttr++) + { + const char *pszFieldText = + RECGetField( pszRecord, + panFieldOffset[iAttr] + 1, + panFieldWidth[iAttr] ); + + if( strlen(pszFieldText) != 0 ) + poFeature->SetField( iAttr, pszFieldText ); + } + +/* -------------------------------------------------------------------- */ +/* Translate the record id. */ +/* -------------------------------------------------------------------- */ + poFeature->SetFID( nNextFID++ ); + m_nFeaturesRead++; + + CPLFree( pszRecord ); + + return poFeature; +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRRECLayer::GetNextFeature() + +{ + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* Read features till we find one that satisfies our current */ +/* spatial criteria. */ +/* -------------------------------------------------------------------- */ + while( TRUE ) + { + poFeature = GetNextUnfilteredFeature(); + if( poFeature == NULL ) + break; + + if( m_poAttrQuery == NULL || m_poAttrQuery->Evaluate( poFeature ) ) + break; + + delete poFeature; + } + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRRECLayer::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/GNUmakefile new file mode 100644 index 000000000..c4faaccbf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/GNUmakefile @@ -0,0 +1,25 @@ + + +ISODIR = ../../../frmts/iso8211 +ISOLIB = $(ISODIR)/libiso8211.a + +include ../../../GDALmake.opt + +OBJ = ogrs57driver.o ogrs57datasource.o ogrs57layer.o \ + s57reader.o s57writer.o ddfrecordindex.o \ + s57classregistrar.o s57filecollector.o \ + s57featuredefns.o + +CPPFLAGS := -I.. -I../.. -I$(ISODIR) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o s57dump$(EXE) $(O_OBJ) + +all: default s57dump$(EXE) + +$(O_OBJ): s57.h ogr_s57.h + +s57dump$(EXE): s57dump.$(OBJ_EXT) + $(LD) $(LDFLAGS) s57dump.$(OBJ_EXT) $(CONFIG_LIBS) -o s57dump$(EXE) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ddfrecordindex.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ddfrecordindex.cpp new file mode 100644 index 000000000..2da9825c4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ddfrecordindex.cpp @@ -0,0 +1,327 @@ +/****************************************************************************** + * $Id: ddfrecordindex.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: S-57 Translator + * Purpose: Implements DDFRecordIndex class. This class is used to cache + * ISO8211 records for spatial objects so they can be efficiently + * assembled later as features. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "s57.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ddfrecordindex.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* DDFRecordIndex() */ +/************************************************************************/ + +DDFRecordIndex::DDFRecordIndex() + +{ + bSorted = FALSE; + + nRecordCount = 0; + nRecordMax = 0; + pasRecords = NULL; + + nLastObjlPos = 0; /* rjensen. added for FindRecordByObjl() */ + nLastObjl = 0; /* rjensen. added for FindRecordByObjl() */ +} + +/************************************************************************/ +/* ~DDFRecordIndex() */ +/************************************************************************/ + +DDFRecordIndex::~DDFRecordIndex() + +{ + Clear(); +} + +/************************************************************************/ +/* Clear() */ +/* */ +/* Clear all entries from the index and deallocate all index */ +/* resources. */ +/************************************************************************/ + +void DDFRecordIndex::Clear() + +{ + // It turns out that deleting these records here is very expensive + // due to the linear search in DDFModule::RemoveClone(). For now we + // just leave the clones depending on DDFModule::~DDFModule() to clean + // them up eventually. + + //for( int i = 0; i < nRecordCount; i++ ) + // delete pasRecords[i].poRecord; + + CPLFree( pasRecords ); + pasRecords = NULL; + + nRecordCount = 0; + nRecordMax = 0; + + nLastObjlPos = 0; /* rjensen. added for FindRecordByObjl() */ + nLastObjl = 0; /* rjensen. added for FindRecordByObjl() */ + + bSorted = FALSE; +} + +/************************************************************************/ +/* AddRecord() */ +/* */ +/* Add a record to the index. The index will assume ownership */ +/* of the record. If passing a record just read from a */ +/* DDFModule it is imperitive that the caller Clone()'s the */ +/* record first. */ +/************************************************************************/ + +void DDFRecordIndex::AddRecord( int nKey, DDFRecord * poRecord ) + +{ + if( nRecordCount == nRecordMax ) + { + nRecordMax = (int) (nRecordCount * 1.3 + 100); + pasRecords = (DDFIndexedRecord *) + CPLRealloc( pasRecords, sizeof(DDFIndexedRecord)*nRecordMax ); + } + + bSorted = FALSE; + + pasRecords[nRecordCount].nKey = nKey; + pasRecords[nRecordCount].poRecord = poRecord; + pasRecords[nRecordCount].pClientData = NULL; + + nRecordCount++; +} + +/************************************************************************/ +/* FindRecord() */ +/* */ +/* Though the returned pointer is not const, it should be */ +/* considered internal to the index and not modified or freed */ +/* by application code. */ +/************************************************************************/ + +DDFRecord * DDFRecordIndex::FindRecord( int nKey ) + +{ + if( !bSorted ) + Sort(); + +/* -------------------------------------------------------------------- */ +/* Do a binary search based on the key to find the desired record. */ +/* -------------------------------------------------------------------- */ + int nMinIndex = 0, nMaxIndex = nRecordCount-1; + + while( nMinIndex <= nMaxIndex ) + { + int nTestIndex = (nMaxIndex + nMinIndex) / 2; + + if( pasRecords[nTestIndex].nKey < nKey ) + nMinIndex = nTestIndex + 1; + else if( pasRecords[nTestIndex].nKey > nKey ) + nMaxIndex = nTestIndex - 1; + else + return pasRecords[nTestIndex].poRecord; + } + + return NULL; +} + +/************************************************************************/ +/* FindRecordByObjl() */ +/* Rodney Jensen */ +/* Though the returned pointer is not const, it should be */ +/* considered internal to the index and not modified or freed */ +/* by application code. */ +/************************************************************************/ + +DDFRecord * DDFRecordIndex::FindRecordByObjl( int nObjl ) +{ + if( !bSorted ) + Sort(); + +/* -------------------------------------------------------------------- */ +/* Do a linear search based on the nObjl to find the desired record. */ +/* -------------------------------------------------------------------- */ + int nMinIndex = 0; + if (nLastObjl != nObjl) nLastObjlPos=0; + + for (nMinIndex = nLastObjlPos; nMinIndex < nRecordCount; nMinIndex++) + { + if (nObjl == pasRecords[nMinIndex].poRecord->GetIntSubfield( "FRID", 0, "OBJL", 0 ) ) + { + nLastObjlPos=nMinIndex+1; /* add 1, don't want to look at same again */ + nLastObjl=nObjl; + return pasRecords[nMinIndex].poRecord; + } + } + + nLastObjlPos=0; + nLastObjl=0; + + return NULL; +} + +/************************************************************************/ +/* RemoveRecord() */ +/************************************************************************/ + +int DDFRecordIndex::RemoveRecord( int nKey ) + +{ + if( !bSorted ) + Sort(); + +/* -------------------------------------------------------------------- */ +/* Do a binary search based on the key to find the desired record. */ +/* -------------------------------------------------------------------- */ + int nMinIndex = 0, nMaxIndex = nRecordCount-1; + int nTestIndex = 0; + + while( nMinIndex <= nMaxIndex ) + { + nTestIndex = (nMaxIndex + nMinIndex) / 2; + + if( pasRecords[nTestIndex].nKey < nKey ) + nMinIndex = nTestIndex + 1; + else if( pasRecords[nTestIndex].nKey > nKey ) + nMaxIndex = nTestIndex - 1; + else + break; + } + + if( nMinIndex > nMaxIndex ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Delete this record. */ +/* -------------------------------------------------------------------- */ + delete pasRecords[nTestIndex].poRecord; + +/* -------------------------------------------------------------------- */ +/* Move all the list entries back one to fill the hole, and */ +/* update the total count. */ +/* -------------------------------------------------------------------- */ + memmove( pasRecords + nTestIndex, + pasRecords + nTestIndex + 1, + (nRecordCount - nTestIndex - 1) * sizeof(DDFIndexedRecord) ); + + nRecordCount--; + + return TRUE; +} + +/************************************************************************/ +/* DDFCompare() */ +/* */ +/* Compare two DDFIndexedRecord objects for qsort(). */ +/************************************************************************/ + +static int DDFCompare( const void *pRec1, const void *pRec2 ) + +{ + if( ((const DDFIndexedRecord *) pRec1)->nKey + == ((const DDFIndexedRecord *) pRec2)->nKey ) + return 0; + else if( ((const DDFIndexedRecord *) pRec1)->nKey + < ((const DDFIndexedRecord *) pRec2)->nKey ) + return -1; + else + return 1; +} + +/************************************************************************/ +/* Sort() */ +/* */ +/* Sort the records based on the key. This is currently */ +/* implemented as a bubble sort, and could gain in efficiency */ +/* by reimplementing as a quick sort; however, I believe that */ +/* the keys will always be in order so a bubble sort should */ +/* only require one pass to verify this. */ +/************************************************************************/ + +void DDFRecordIndex::Sort() + +{ + if( bSorted ) + return; + + qsort( pasRecords, nRecordCount, sizeof(DDFIndexedRecord), DDFCompare ); + + bSorted = TRUE; +} + +/************************************************************************/ +/* GetByIndex() */ +/************************************************************************/ + +DDFRecord * DDFRecordIndex::GetByIndex( int nIndex ) + +{ + if( !bSorted ) + Sort(); + + if( nIndex < 0 || nIndex >= nRecordCount ) + return NULL; + else + return pasRecords[nIndex].poRecord; +} + +/************************************************************************/ +/* GetClientInfoByIndex() */ +/************************************************************************/ + +void * DDFRecordIndex::GetClientInfoByIndex( int nIndex ) + +{ + if( !bSorted ) + Sort(); + + if( nIndex < 0 || nIndex >= nRecordCount ) + return NULL; + else + return pasRecords[nIndex].pClientData; +} + +/************************************************************************/ +/* SetClientInfoByIndex() */ +/************************************************************************/ + +void DDFRecordIndex::SetClientInfoByIndex( int nIndex, void *pClientData ) + +{ + if( !bSorted ) + Sort(); + + if( nIndex < 0 || nIndex >= nRecordCount ) + return; + else + pasRecords[nIndex].pClientData = pClientData; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/drv_s57.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/drv_s57.html new file mode 100644 index 000000000..a225f06bb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/drv_s57.html @@ -0,0 +1,226 @@ + + +IHO S-57 (ENC) + + + + +

    IHO S-57 (ENC)

    + +IHO S-57 datasets are supported for read access.

    + +The S-57 driver module produces features for all S-57 features in +S-57 file, and associated updates. S-57 (ENC) files normally have +the extension ".000".

    + +S-57 feature objects are translated into features. S-57 geometry +objects are automatically collected and formed into geometries on the +features.

    + +The S-57 reader depends on having two supporting files, s57objectclasses.csv, +and s57attributes.csv available at runtime in order to translate features +in an object class specific manner. These should be in the directory pointed +to by the environment variable S57_CSV, or in the current working directory. +

    + +S-57 update files contain information on how to update a distributed S-57 +base data file. The base files normally have the extension .000 while the +update files have extensions like .001, .002 and so on. The S-57 reader +will normally read and apply all updates files to the in memory version of +the base file on the fly. The feature data provided to the application +therefore includes all the updates.

    + +

    Feature Translation

    + +Normally all features read from S-57 are assigned to a layer based on +the name of the object class (OBJL) to which they belong. For instance, +with an OBJL value of 2, the feature is an "Airport / airfield" and has +a short name of "AIRARE" which is used as the layer name. A typical +S-57 transfer will have in excess of 100 layers.

    + +Each feature type has a predefined set of attributes as defined by the +S-57 standard. For instance, the airport (AIRARE) object class can +have the AIRARE, CATAIR, CONDTN, CONVIS, NOBJNM, OBJNAM, STATUS, INFORM, +NINFOM, NTXTDS, PICREP, SCAMAX, SCAMIN, TXTDSC, ,RECDAT, RECIND, SORDAT, +and SORIND attributes. These short names can be related to longer, more +meaningful names using an S-57 object/attribute catalog such as the +S-57 standard document itself, or the catalog files (s57attributes.csv, +and s57objectclasses.csv). +Such a catalog can also be used to establish all the available object classes, +and their attributes.

    + +The following are some common attributes, including generic attributes +which appear on all feature, regardless of object class. +is turned on.

    + +

    +  Attribute Name  Description                            Defined On
    +  --------------  -----------                            ----------
    +
    +  GRUP            Group number.			         All features
    +
    +  OBJL            Object label code.  This number	 All features
    +	          indicates the object class of the 
    +                  feature. 
    +
    +  RVER            Record version.
    +
    +  AGEN            Numeric agency code, such as 50 for    All features
    +                  the Canadian Hydrographic Service.
    +		  A potentially outdated list is 
    +		  available in agencode.txt.
    +
    +  FIDN            Feature identification number.         All features
    +
    +  FIDS            Feature identification subdivision.    All features
    +
    +  DSNM            Dataset name.  The name of the file    All features
    +                  the feature came from.  Used with
    +                  LNAM to form a unique dataset wide
    +		  identifier for a feature.
    +
    +  INFORM          Informational text.                    Some features
    +
    +  NINFOM	  Informational text in national         Some features
    +                  language. 
    +
    +  OBJNAM          Object name				 Some features
    +
    +  NOBJNM          Object name in national		 Some features
    +                  language.
    +                         
    +  SCAMAX          Maximum scale for display              Some features
    + 
    +  SCAMIN          Minimum scale for display              Some features
    +  
    +  SORDAT          Source date                            Some features
    +
    + +The following are present if LNAM_REFS is enabled: +
    +  LNAM            Long name.  An encoding of AGEN,       All features
    +                  FIDN and FIDS used to uniquely 
    +                  identify this features within an
    +                  S-57 file.  
    +
    +  LNAM_REFS       List of long names of related features All Features
    +
    +  FFPT_RIND       Relationship indicators for each of    All Features
    +                  the LNAM_REFS relationships. 
    +
    +
    + +

    Soundings

    + +Depth soundings are handled somewhat specially in S-57 format, in order +to efficiently represent the many available data points. In S-57 one +sounding feature can have many sounding points. The S-57 reader +splits each of these out into it's own feature type `SOUNDG' feature +with an s57_type of `s57_point3d'. All the soundings from a single +feature record will have the same AGEN, FIDN, FIDS and LNAM value.

    + +

    S57 Control Options

    + +There are several control options which can be used to alter the behavior of +the S-57 reader. Users can set these by appending them in the +OGR_S57_OPTIONS environment variable. +

    +Starting with GDAL 2.0, they can also be specified independantly as open options +to the driver. +

    + +

      + +
    • UPDATES=APPLY/IGNORE: Should update files be incorporated into the +base data on the fly. Default is APPLY.

      + +

    • SPLIT_MULTIPOINT=ON/OFF: Should multipoint soundings be split +into many single point sounding features. Multipoint geometries are not well +handle by many formats, so it can be convenient to split single sounding +features with many points into many single point features. Default is OFF.

      + +

    • ADD_SOUNDG_DEPTH=ON/OFF: Should a DEPTH attribute be added on +SOUNDG features and assign the depth of the sounding. This should only be +enabled with SPLIT_MULTIPOINT is also enabled. +Default is OFF.

      + +

    • RETURN_PRIMITIVES=ON/OFF: Should all the low level geometry +primitives be returned as special IsolatedNode, ConnectedNode, Edge and +Face layers. Default is OFF.

      + +

    • PRESERVE_EMPTY_NUMBERS=ON/OFF: If enabled, numeric attributes +assigned an empty string as a value will be preserved as a special numeric +value. This option should not generally be needed, but may be useful +when translated S-57 to S-57 losslessly. Default is OFF.

      + +

    • LNAM_REFS=ON/OFF: Should LNAM and LNAM_REFS fields be +attached to features capturing the feature to feature relationships in the +FFPT group of the S-57 file. Default is ON.

      + +

    • RETURN_LINKAGES=ON/OFF: Should additional attributes +relating features to their underlying geometric primtives be attached. These +are the values of the FSPT group, and are primarily needed when doing +S-57 to S-57 translations. Default is OFF.

      + +

    • RECODE_BY_DSSI=ON/OFF: (OGR >= 1.10) Should attribute values be recoded to UTF-8 +from the character encoding specified in the S57 DSSI record. Default is OFF.

      + +

    + +Example: + +
    +set OGR_S57_OPTIONS = "RETURN_PRIMITIVES=ON,RETURN_LINKAGES=ON,LNAM_REFS=ON"
    +
    + +

    S-57 Export

    + +Preliminary S-57 export capability has been added in GDAL/OGR 1.2.0 but +is intended only for specialized use, and is not properly documented at +this time. Setting the following options is a minimum required to +support S-57 to S-57 conversion via OGR.

    + +

    +set OGR_S57_OPTIONS = "RETURN_PRIMITIVES=ON,RETURN_LINKAGES=ON,LNAM_REFS=ON"
    +
    + +Since GDAL/OGR X.Y.Z, the following dataset creation options are supported to +supply basic information for the S-57 data set descriptive records (DSID and +DSPM, see the S-57 standard for a more detailed description): + +
      +
    • S57_EXPP: Exchange purpose. Default is 1.

      +

    • S57_INTU: Intended usage. Default is 4.

      +

    • S57_EDTN: Edition number. Default is 2.

      +

    • S57_UPDN: Update number. Default is 0.

      +

    • S57_UADT: Update application date. Default is 20030801.

      +

    • S57_ISDT: Issue date. Default is 20030801.

      +

    • S57_STED: Edition number of S-57. Default is 03.1.

      +

    • S57_AGEN: Producing agency. Default is 540.

      +

    • S57_COMT: Comment.

      +

    • S57_NOMR: Number of meta records (objects with acronym starting with "M_"). Default is 0.

      +

    • S57_NOGR: Number of geo records. Default is 0.

      +

    • S57_NOLR: Number of collection records. Default is 0.

      +

    • S57_NOIN: Number of isolated node records. Default is 0.

      +

    • S57_NOCN: Number of connected node records. Default is 0.

      +

    • S57_NOED: Number of edge records. Default is 0.

      +

    • S57_HDAT: Horizontal geodetic datum. Default is 2.

      +

    • S57_VDAT: Vertical datum. Default is 17.

      +

    • S57_SDAT: Sounding datum. Default is 23.

      +

    • S57_CSCL: Compilation scale of data (1:X). Default is 52000.

      +

    + + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/makefile.vc new file mode 100644 index 000000000..ceace882c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/makefile.vc @@ -0,0 +1,19 @@ + +OBJ = ogrs57driver.obj ogrs57datasource.obj ogrs57layer.obj \ + s57classregistrar.obj s57reader.obj ddfrecordindex.obj \ + s57featuredefns.obj s57filecollector.obj s57writer.obj + + +EXTRAFLAGS = -I.. -I..\.. -I$(GDAL_ROOT)/frmts/iso8211 + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ogr_s57.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ogr_s57.h new file mode 100644 index 000000000..9c0a479d2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ogr_s57.h @@ -0,0 +1,147 @@ +/****************************************************************************** + * $Id: ogr_s57.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: S-57 Translator + * Purpose: Declarations for classes binding S57 support onto OGRLayer, + * OGRDataSource and OGRDriver. See also s57.h. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_S57_H_INCLUDED +#define _OGR_S57_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "s57.h" + +class OGRS57DataSource; + +/************************************************************************/ +/* OGRS57Layer */ +/* */ +/* Represents all features of a particular S57 object class. */ +/************************************************************************/ + +class OGRS57Layer : public OGRLayer +{ + OGRS57DataSource *poDS; + + OGRFeatureDefn *poFeatureDefn; + + int nCurrentModule; + int nRCNM; + int nOBJL; + int nNextFEIndex; + int nFeatureCount; + + public: + OGRS57Layer( OGRS57DataSource * poDS, + OGRFeatureDefn *, int nFeatureCount = -1, + int nOBJL = -1 ); + virtual ~OGRS57Layer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + OGRFeature * GetNextUnfilteredFeature(); + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRS57DataSource */ +/************************************************************************/ + +class OGRS57DataSource : public OGRDataSource +{ + char *pszName; + + int nLayers; + OGRS57Layer **papoLayers; + + OGRSpatialReference *poSpatialRef; + + char **papszOptions; + + int nModules; + S57Reader **papoModules; + + S57Writer *poWriter; + + S57ClassContentExplorer* poClassContentExplorer; + + int bExtentsSet; + OGREnvelope oExtents; + + public: + OGRS57DataSource(char** papszOpenOptions = NULL); + ~OGRS57DataSource(); + + void SetOptionList( char ** ); + const char *GetOption( const char * ); + + int Open( const char * pszName ); + int Create( const char *pszName, char **papszOptions ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + void AddLayer( OGRS57Layer * ); + int TestCapability( const char * ); + + OGRSpatialReference *GetSpatialRef() { return poSpatialRef; } + + int GetModuleCount() { return nModules; } + S57Reader *GetModule( int ); + S57Writer *GetWriter() { return poWriter; } + + OGRErr GetDSExtent(OGREnvelope *psExtent, int bForce = TRUE); +}; + +/************************************************************************/ +/* OGRS57Driver */ +/************************************************************************/ + +class OGRS57Driver : public GDALDriver +{ + static S57ClassRegistrar *poRegistrar; + + public: + OGRS57Driver(); + ~OGRS57Driver(); + + static GDALDataset *Open( GDALOpenInfo* poOpenInfo ); + static GDALDataset *Create( const char * pszName, + int nBands, int nXSize, int nYSize, GDALDataType eDT, + char **papszOptions ); + + static S57ClassRegistrar *GetS57Registrar(); +}; + +#endif /* ndef _OGR_S57_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ogrs57datasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ogrs57datasource.cpp new file mode 100644 index 000000000..169a75676 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ogrs57datasource.cpp @@ -0,0 +1,582 @@ +/****************************************************************************** + * $Id: ogrs57datasource.cpp 28348 2015-01-23 15:27:13Z rouault $ + * + * Project: S-57 Translator + * Purpose: Implements OGRS57DataSource class + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_s57.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrs57datasource.cpp 28348 2015-01-23 15:27:13Z rouault $"); + +/************************************************************************/ +/* OGRS57DataSource() */ +/************************************************************************/ + +OGRS57DataSource::OGRS57DataSource(char** papszOpenOptions) + +{ + nLayers = 0; + papoLayers = NULL; + + nModules = 0; + papoModules = NULL; + poClassContentExplorer = NULL; + poWriter = NULL; + + pszName = NULL; + + poSpatialRef = new OGRSpatialReference(); + poSpatialRef->SetWellKnownGeogCS( "WGS84" ); + + bExtentsSet = FALSE; + + +/* -------------------------------------------------------------------- */ +/* Allow initialization of options from the environment. */ +/* -------------------------------------------------------------------- */ + if( papszOpenOptions != NULL ) + { + papszOptions = CSLDuplicate(papszOpenOptions); + } + else + { + const char *pszOptString = CPLGetConfigOption( "OGR_S57_OPTIONS", NULL ); + papszOptions = NULL; + + if ( pszOptString ) + { + char **papszCurOption; + + papszOptions = + CSLTokenizeStringComplex( pszOptString, ",", FALSE, FALSE ); + + if ( papszOptions && *papszOptions ) + { + CPLDebug( "S57", "The following S57 options are being set:" ); + papszCurOption = papszOptions; + while( *papszCurOption ) + CPLDebug( "S57", " %s", *papszCurOption++ ); + } + } + } +} + +/************************************************************************/ +/* ~OGRS57DataSource() */ +/************************************************************************/ + +OGRS57DataSource::~OGRS57DataSource() + +{ + int i; + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + for( i = 0; i < nModules; i++ ) + delete papoModules[i]; + CPLFree( papoModules ); + + CPLFree( pszName ); + + CSLDestroy( papszOptions ); + + poSpatialRef->Release(); + + if( poWriter != NULL ) + { + poWriter->Close(); + delete poWriter; + } + delete poClassContentExplorer; +} + +/************************************************************************/ +/* SetOptionList() */ +/************************************************************************/ + +void OGRS57DataSource::SetOptionList( char ** papszNewOptions ) + +{ + CSLDestroy( papszOptions ); + papszOptions = CSLDuplicate( papszNewOptions ); +} + +/************************************************************************/ +/* GetOption() */ +/************************************************************************/ + +const char *OGRS57DataSource::GetOption( const char * pszOption ) + +{ + return CSLFetchNameValue( papszOptions, pszOption ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRS57DataSource::TestCapability( const char * ) + +{ + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRS57DataSource::Open( const char * pszFilename ) + +{ + int iModule; + + pszName = CPLStrdup( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Setup reader options. */ +/* -------------------------------------------------------------------- */ + char **papszReaderOptions = NULL; + S57Reader *poModule; + + poModule = new S57Reader( pszFilename ); + + if( GetOption(S57O_LNAM_REFS) == NULL ) + papszReaderOptions = CSLSetNameValue(papszReaderOptions, + S57O_LNAM_REFS, "ON" ); + else + papszReaderOptions = + CSLSetNameValue( papszReaderOptions, S57O_LNAM_REFS, + GetOption(S57O_LNAM_REFS)); + + if( GetOption(S57O_UPDATES) != NULL ) + papszReaderOptions = + CSLSetNameValue( papszReaderOptions, S57O_UPDATES, + GetOption(S57O_UPDATES)); + + if( GetOption(S57O_SPLIT_MULTIPOINT) != NULL ) + papszReaderOptions = + CSLSetNameValue( papszReaderOptions, S57O_SPLIT_MULTIPOINT, + GetOption(S57O_SPLIT_MULTIPOINT) ); + + if( GetOption(S57O_ADD_SOUNDG_DEPTH) != NULL ) + papszReaderOptions = + CSLSetNameValue( papszReaderOptions, S57O_ADD_SOUNDG_DEPTH, + GetOption(S57O_ADD_SOUNDG_DEPTH)); + + if( GetOption(S57O_PRESERVE_EMPTY_NUMBERS) != NULL ) + papszReaderOptions = + CSLSetNameValue( papszReaderOptions, S57O_PRESERVE_EMPTY_NUMBERS, + GetOption(S57O_PRESERVE_EMPTY_NUMBERS) ); + + if( GetOption(S57O_RETURN_PRIMITIVES) != NULL ) + papszReaderOptions = + CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES, + GetOption(S57O_RETURN_PRIMITIVES) ); + + if( GetOption(S57O_RETURN_LINKAGES) != NULL ) + papszReaderOptions = + CSLSetNameValue( papszReaderOptions, S57O_RETURN_LINKAGES, + GetOption(S57O_RETURN_LINKAGES) ); + + if( GetOption(S57O_RETURN_DSID) != NULL ) + papszReaderOptions = + CSLSetNameValue( papszReaderOptions, S57O_RETURN_DSID, + GetOption(S57O_RETURN_DSID) ); + + if( GetOption(S57O_RECODE_BY_DSSI) != NULL ) + papszReaderOptions = + CSLSetNameValue( papszReaderOptions, S57O_RECODE_BY_DSSI, + GetOption(S57O_RECODE_BY_DSSI) ); + + int bRet = poModule->SetOptions( papszReaderOptions ); + CSLDestroy( papszReaderOptions ); + + if( !bRet ) + { + delete poModule; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try opening. */ +/* */ +/* Eventually this should check for catalogs, and if found */ +/* instantiate a whole series of modules. */ +/* -------------------------------------------------------------------- */ + if( !poModule->Open( TRUE ) ) + { + delete poModule; + + return FALSE; + } + + int bSuccess = TRUE; + + nModules = 1; + papoModules = (S57Reader **) CPLMalloc(sizeof(void*)); + papoModules[0] = poModule; + +/* -------------------------------------------------------------------- */ +/* Add the header layers if they are called for. */ +/* -------------------------------------------------------------------- */ + if( GetOption( S57O_RETURN_DSID ) == NULL + || CSLTestBoolean(GetOption( S57O_RETURN_DSID )) ) + { + OGRFeatureDefn *poDefn; + + poDefn = S57GenerateDSIDFeatureDefn(); + AddLayer( new OGRS57Layer( this, poDefn ) ); + } + +/* -------------------------------------------------------------------- */ +/* Add the primitive layers if they are called for. */ +/* -------------------------------------------------------------------- */ + if( GetOption( S57O_RETURN_PRIMITIVES ) != NULL ) + { + OGRFeatureDefn *poDefn; + + poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VI, poModule->GetOptionFlags()); + AddLayer( new OGRS57Layer( this, poDefn ) ); + + poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VC, poModule->GetOptionFlags()); + AddLayer( new OGRS57Layer( this, poDefn ) ); + + poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VE, poModule->GetOptionFlags()); + AddLayer( new OGRS57Layer( this, poDefn ) ); + + poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VF, poModule->GetOptionFlags()); + AddLayer( new OGRS57Layer( this, poDefn ) ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize a layer for each type of geometry. Eventually */ +/* we will do this by object class. */ +/* -------------------------------------------------------------------- */ + if( OGRS57Driver::GetS57Registrar() == NULL ) + { + OGRFeatureDefn *poDefn; + + poDefn = S57GenerateGeomFeatureDefn( wkbPoint, + poModule->GetOptionFlags() ); + AddLayer( new OGRS57Layer( this, poDefn ) ); + + poDefn = S57GenerateGeomFeatureDefn( wkbLineString, + poModule->GetOptionFlags() ); + AddLayer( new OGRS57Layer( this, poDefn ) ); + + poDefn = S57GenerateGeomFeatureDefn( wkbPolygon, + poModule->GetOptionFlags() ); + AddLayer( new OGRS57Layer( this, poDefn ) ); + + poDefn = S57GenerateGeomFeatureDefn( wkbNone, + poModule->GetOptionFlags() ); + AddLayer( new OGRS57Layer( this, poDefn ) ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize a feature definition for each class that actually */ +/* occurs in the dataset. */ +/* -------------------------------------------------------------------- */ + else + { + OGRFeatureDefn *poDefn; + std::vector anClassCount; + int bGeneric = FALSE; + unsigned int iClass; + + poClassContentExplorer = + new S57ClassContentExplorer( OGRS57Driver::GetS57Registrar() ); + + for( iModule = 0; iModule < nModules; iModule++ ) + papoModules[iModule]->SetClassBased( OGRS57Driver::GetS57Registrar(), + poClassContentExplorer ); + + for( iModule = 0; iModule < nModules; iModule++ ) + { + bSuccess &= + papoModules[iModule]->CollectClassList(anClassCount); + } + + for( iClass = 0; iClass < anClassCount.size(); iClass++ ) + { + if( anClassCount[iClass] > 0 ) + { + poDefn = + S57GenerateObjectClassDefn( OGRS57Driver::GetS57Registrar(), + poClassContentExplorer, + iClass, + poModule->GetOptionFlags() ); + + if( poDefn != NULL ) + AddLayer( new OGRS57Layer( this, poDefn, + anClassCount[iClass] ) ); + else + { + bGeneric = TRUE; + CPLDebug( "S57", + "Unable to find definition for OBJL=%d\n", + iClass ); + } + } + } + + if( bGeneric ) + { + poDefn = S57GenerateGeomFeatureDefn( wkbUnknown, + poModule->GetOptionFlags() ); + AddLayer( new OGRS57Layer( this, poDefn ) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Attach the layer definitions to each of the readers. */ +/* -------------------------------------------------------------------- */ + for( iModule = 0; iModule < nModules; iModule++ ) + { + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + papoModules[iModule]->AddFeatureDefn( + papoLayers[iLayer]->GetLayerDefn() ); + } + } + + return bSuccess; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRS57DataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* AddLayer() */ +/************************************************************************/ + +void OGRS57DataSource::AddLayer( OGRS57Layer * poNewLayer ) + +{ + papoLayers = (OGRS57Layer **) + CPLRealloc( papoLayers, sizeof(void*) * ++nLayers ); + + papoLayers[nLayers-1] = poNewLayer; +} + +/************************************************************************/ +/* GetModule() */ +/************************************************************************/ + +S57Reader * OGRS57DataSource::GetModule( int i ) + +{ + if( i < 0 || i >= nModules ) + return NULL; + else + return papoModules[i]; +} + +/************************************************************************/ +/* GetDSExtent() */ +/************************************************************************/ + +OGRErr OGRS57DataSource::GetDSExtent( OGREnvelope *psExtent, int bForce ) + +{ +/* -------------------------------------------------------------------- */ +/* If we have it, return it immediately. */ +/* -------------------------------------------------------------------- */ + if( bExtentsSet ) + { + *psExtent = oExtents; + return OGRERR_NONE; + } + + if( nModules == 0 ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Otherwise try asking each of the readers for it. */ +/* -------------------------------------------------------------------- */ + for( int iModule=0; iModule < nModules; iModule++ ) + { + OGREnvelope oModuleEnvelope; + OGRErr eErr; + + eErr = papoModules[iModule]->GetExtent( &oModuleEnvelope, bForce ); + if( eErr != OGRERR_NONE ) + return eErr; + + if( iModule == 0 ) + oExtents = oModuleEnvelope; + else + { + oExtents.MinX = MIN(oExtents.MinX,oModuleEnvelope.MinX); + oExtents.MaxX = MAX(oExtents.MaxX,oModuleEnvelope.MaxX); + oExtents.MinY = MIN(oExtents.MinY,oModuleEnvelope.MinY); + oExtents.MaxX = MAX(oExtents.MaxY,oModuleEnvelope.MaxY); + } + } + + *psExtent = oExtents; + bExtentsSet = TRUE; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* Create() */ +/* */ +/* Create a new S57 file, and represent it as a datasource. */ +/************************************************************************/ + +int OGRS57DataSource::Create( const char *pszFilename, + char **papszOptions ) +{ +/* -------------------------------------------------------------------- */ +/* Instantiate the class registrar if possible. */ +/* -------------------------------------------------------------------- */ + if( OGRS57Driver::GetS57Registrar() == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to load s57objectclasses.csv, unable to continue." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Create the S-57 file with definition record. */ +/* -------------------------------------------------------------------- */ + poWriter = new S57Writer(); + + if( !poWriter->CreateS57File( pszFilename ) ) + return FALSE; + + poClassContentExplorer = + new S57ClassContentExplorer( OGRS57Driver::GetS57Registrar() ); + + poWriter->SetClassBased( OGRS57Driver::GetS57Registrar(), + poClassContentExplorer ); + pszName = CPLStrdup( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Add the primitive layers if they are called for. */ +/* -------------------------------------------------------------------- */ + OGRFeatureDefn *poDefn; + int nOptionFlags = S57M_RETURN_LINKAGES | S57M_LNAM_REFS; + + poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VI, nOptionFlags ); + AddLayer( new OGRS57Layer( this, poDefn ) ); + + poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VC, nOptionFlags ); + AddLayer( new OGRS57Layer( this, poDefn ) ); + + poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VE, nOptionFlags ); + AddLayer( new OGRS57Layer( this, poDefn ) ); + + poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VF, nOptionFlags ); + AddLayer( new OGRS57Layer( this, poDefn ) ); + +/* -------------------------------------------------------------------- */ +/* Initialize a feature definition for each object class. */ +/* -------------------------------------------------------------------- */ + poClassContentExplorer->Rewind(); + while( poClassContentExplorer->NextClass() ) + { + poDefn = + S57GenerateObjectClassDefn( OGRS57Driver::GetS57Registrar(), + poClassContentExplorer, + poClassContentExplorer->GetOBJL(), + nOptionFlags ); + + AddLayer( new OGRS57Layer( this, poDefn, 0, + poClassContentExplorer->GetOBJL() ) ); + } + +/* -------------------------------------------------------------------- */ +/* Write out "header" records. */ +/* -------------------------------------------------------------------- */ + int nEXPP = 1, nINTU = 4, nAGEN = 540, nNOMR = 0, nNOGR = 0, + nNOLR = 0, nNOIN = 0, nNOCN = 0, nNOED = 0; + const char + *pszEXPP = CSLFetchNameValue(papszOptions, "S57_EXPP"), + *pszINTU = CSLFetchNameValue(papszOptions, "S57_INTU"), + *pszEDTN = CSLFetchNameValue(papszOptions, "S57_EDTN"), + *pszUPDN = CSLFetchNameValue(papszOptions, "S57_UPDN"), + *pszUADT = CSLFetchNameValue(papszOptions, "S57_UADT"), + *pszISDT = CSLFetchNameValue(papszOptions, "S57_ISDT"), + *pszSTED = CSLFetchNameValue(papszOptions, "S57_STED"), + *pszAGEN = CSLFetchNameValue(papszOptions, "S57_AGEN"), + *pszCOMT = CSLFetchNameValue(papszOptions, "S57_COMT"), + *pszNOMR = CSLFetchNameValue(papszOptions, "S57_NOMR"), + *pszNOGR = CSLFetchNameValue(papszOptions, "S57_NOGR"), + *pszNOLR = CSLFetchNameValue(papszOptions, "S57_NOLR"), + *pszNOIN = CSLFetchNameValue(papszOptions, "S57_NOIN"), + *pszNOCN = CSLFetchNameValue(papszOptions, "S57_NOCN"), + *pszNOED = CSLFetchNameValue(papszOptions, "S57_NOED"); + if (pszEXPP) nEXPP = atoi(pszEXPP); + if (pszINTU) nINTU = atoi(pszINTU); + if (pszAGEN) nAGEN = atoi(pszAGEN); + if (pszNOMR) nNOMR = atoi(pszNOMR); + if (pszNOGR) nNOGR = atoi(pszNOGR); + if (pszNOLR) nNOLR = atoi(pszNOLR); + if (pszNOIN) nNOIN = atoi(pszNOIN); + if (pszNOCN) nNOCN = atoi(pszNOCN); + if (pszNOED) nNOED = atoi(pszNOED); + poWriter->WriteDSID( nEXPP, nINTU, CPLGetFilename( pszFilename ), + pszEDTN, pszUPDN, pszUADT, pszISDT, pszSTED, nAGEN, + pszCOMT, nNOMR, nNOGR, nNOLR, nNOIN, nNOCN, nNOED ); + + int nHDAT = 2, nVDAT = 17, nSDAT = 23, nCSCL = 52000; + const char + *pszHDAT = CSLFetchNameValue(papszOptions, "S57_HDAT"), + *pszVDAT = CSLFetchNameValue(papszOptions, "S57_VDAT"), + *pszSDAT = CSLFetchNameValue(papszOptions, "S57_SDAT"), + *pszCSCL = CSLFetchNameValue(papszOptions, "S57_CSCL"); + if (pszHDAT) + nHDAT = atoi(pszHDAT); + if (pszVDAT) + nVDAT = atoi(pszVDAT); + if (pszSDAT) + nSDAT = atoi(pszSDAT); + if (pszCSCL) + nCSCL = atoi(pszCSCL); + poWriter->WriteDSPM(nHDAT, nVDAT, nSDAT, nCSCL); + + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ogrs57driver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ogrs57driver.cpp new file mode 100644 index 000000000..a407d8ef6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ogrs57driver.cpp @@ -0,0 +1,233 @@ +/****************************************************************************** + * $Id: ogrs57driver.cpp 28459 2015-02-12 13:48:21Z rouault $ + * + * Project: S-57 Translator + * Purpose: Implements OGRS57Driver + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_s57.h" +#include "cpl_conv.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: ogrs57driver.cpp 28459 2015-02-12 13:48:21Z rouault $"); + +S57ClassRegistrar *OGRS57Driver::poRegistrar = NULL; +static CPLMutex* hS57RegistrarMutex = NULL; + +/************************************************************************/ +/* OGRS57Driver() */ +/************************************************************************/ + +OGRS57Driver::OGRS57Driver() + +{ +} + +/************************************************************************/ +/* ~OGRS57Driver() */ +/************************************************************************/ + +OGRS57Driver::~OGRS57Driver() + +{ + if( poRegistrar != NULL ) + { + delete poRegistrar; + poRegistrar = NULL; + } + + if( hS57RegistrarMutex != NULL ) + { + CPLDestroyMutex(hS57RegistrarMutex); + hS57RegistrarMutex = NULL; + } +} + +/************************************************************************/ +/* OGRS57DriverIdentify() */ +/************************************************************************/ + +static int OGRS57DriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->nHeaderBytes < 10 ) + return FALSE; + const char* pachLeader = (const char* )poOpenInfo->pabyHeader; + if( (pachLeader[5] != '1' && pachLeader[5] != '2' + && pachLeader[5] != '3' ) + || pachLeader[6] != 'L' + || (pachLeader[8] != '1' && pachLeader[8] != ' ') ) + { + return FALSE; + } + return strstr( pachLeader, "DSID") != NULL; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *OGRS57Driver::Open( GDALOpenInfo* poOpenInfo ) + +{ + OGRS57DataSource *poDS; + + if( !OGRS57DriverIdentify(poOpenInfo) ) + return NULL; + + poDS = new OGRS57DataSource(poOpenInfo->papszOpenOptions); + if( !poDS->Open( poOpenInfo->pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + if( poDS && poOpenInfo->eAccess == GA_Update ) + { + delete poDS; + CPLError( CE_Failure, CPLE_OpenFailed, + "S57 Driver doesn't support update." ); + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +GDALDataset *OGRS57Driver::Create( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + OGRS57DataSource *poDS = new OGRS57DataSource(); + + if( poDS->Create( pszName, papszOptions ) ) + return poDS; + else + { + delete poDS; + return NULL; + } +} + +/************************************************************************/ +/* GetS57Registrar() */ +/************************************************************************/ + +S57ClassRegistrar *OGRS57Driver::GetS57Registrar() + +{ +/* -------------------------------------------------------------------- */ +/* Instantiate the class registrar if possible. */ +/* -------------------------------------------------------------------- */ + CPLMutexHolderD(&hS57RegistrarMutex); + + if( poRegistrar == NULL ) + { + poRegistrar = new S57ClassRegistrar(); + + if( !poRegistrar->LoadInfo( NULL, NULL, FALSE ) ) + { + delete poRegistrar; + poRegistrar = NULL; + } + } + + return poRegistrar; +} + +/************************************************************************/ +/* RegisterOGRS57() */ +/************************************************************************/ + +void RegisterOGRS57() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "S57" ) == NULL ) + { + poDriver = new OGRS57Driver(); + + poDriver->SetDescription( "S57" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "IHO S-57 (ENC)" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "000" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_s57.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " +" "); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" " ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRS57Driver::Open; + poDriver->pfnIdentify = OGRS57DriverIdentify; + poDriver->pfnCreate = OGRS57Driver::Create; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ogrs57layer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ogrs57layer.cpp new file mode 100644 index 000000000..c40dc2221 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/ogrs57layer.cpp @@ -0,0 +1,320 @@ +/****************************************************************************** + * $Id: ogrs57layer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: S-57 Translator + * Purpose: Implements OGRS57Layer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_s57.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrs57layer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGRS57Layer() */ +/* */ +/* Note that the OGRS57Layer assumes ownership of the passed */ +/* OGRFeatureDefn object. */ +/************************************************************************/ + +OGRS57Layer::OGRS57Layer( OGRS57DataSource *poDSIn, + OGRFeatureDefn * poDefnIn, + int nFeatureCountIn, + int nOBJLIn) + +{ + poDS = poDSIn; + + nFeatureCount = nFeatureCountIn; + + poFeatureDefn = poDefnIn; + SetDescription( poFeatureDefn->GetName() ); + if( poFeatureDefn->GetGeomFieldCount() > 0 ) + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poDS->GetSpatialRef()); + + nOBJL = nOBJLIn; + + nNextFEIndex = 0; + nCurrentModule = -1; + + if( EQUAL(poDefnIn->GetName(),OGRN_VI) ) + nRCNM = RCNM_VI; + else if( EQUAL(poDefnIn->GetName(),OGRN_VC) ) + nRCNM = RCNM_VC; + else if( EQUAL(poDefnIn->GetName(),OGRN_VE) ) + nRCNM = RCNM_VE; + else if( EQUAL(poDefnIn->GetName(),OGRN_VF) ) + nRCNM = RCNM_VF; + else if( EQUAL(poDefnIn->GetName(),"DSID") ) + nRCNM = RCNM_DSID; + else + nRCNM = 100; /* feature */ +} + +/************************************************************************/ +/* ~OGRS57Layer() */ +/************************************************************************/ + +OGRS57Layer::~OGRS57Layer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "S57", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRS57Layer::ResetReading() + +{ + nNextFEIndex = 0; + nCurrentModule = -1; +} + +/************************************************************************/ +/* GetNextUnfilteredFeature() */ +/************************************************************************/ + +OGRFeature *OGRS57Layer::GetNextUnfilteredFeature() + +{ + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* Are we out of modules to request features from? */ +/* -------------------------------------------------------------------- */ + if( nCurrentModule >= poDS->GetModuleCount() ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Set the current position on the current module and fetch a */ +/* feature. */ +/* -------------------------------------------------------------------- */ + S57Reader *poReader = poDS->GetModule(nCurrentModule); + + if( poReader != NULL ) + { + poReader->SetNextFEIndex( nNextFEIndex, nRCNM ); + poFeature = poReader->ReadNextFeature( poFeatureDefn ); + nNextFEIndex = poReader->GetNextFEIndex( nRCNM ); + } + +/* -------------------------------------------------------------------- */ +/* If we didn't get a feature we need to move onto the next file. */ +/* -------------------------------------------------------------------- */ + if( poFeature == NULL ) + { + nCurrentModule++; + poReader = poDS->GetModule(nCurrentModule); + + if( poReader != NULL && poReader->GetModule() == NULL ) + { + if( !poReader->Open( FALSE ) ) + return NULL; + } + + return GetNextUnfilteredFeature(); + } + else + { + m_nFeaturesRead++; + if( poFeature->GetGeometryRef() != NULL ) + poFeature->GetGeometryRef()->assignSpatialReference( + GetSpatialRef() ); + } + + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRS57Layer::GetNextFeature() + +{ + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* Read features till we find one that satisfies our current */ +/* spatial criteria. */ +/* -------------------------------------------------------------------- */ + while( TRUE ) + { + poFeature = GetNextUnfilteredFeature(); + if( poFeature == NULL ) + break; + + if( (m_poFilterGeom == NULL + || FilterGeometry(poFeature->GetGeometryRef()) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + break; + + delete poFeature; + } + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRS57Layer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return FALSE; + + else if( EQUAL(pszCap,OLCSequentialWrite) ) + return TRUE; + + else if( EQUAL(pszCap,OLCRandomWrite) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return !(m_poFilterGeom != NULL || m_poAttrQuery != NULL + || nFeatureCount == -1 || + ( EQUAL(poFeatureDefn->GetName(), "SOUNDG") && + poDS->GetModule(0) != NULL && + (poDS->GetModule(0)->GetOptionFlags() & S57M_SPLIT_MULTIPOINT))); + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + { + OGREnvelope oEnvelope; + + return GetExtent( &oEnvelope, FALSE ) == OGRERR_NONE; + } + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else + return FALSE; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRS57Layer::GetExtent( OGREnvelope *psExtent, int bForce ) + +{ + if( GetGeomType() == wkbNone ) + return OGRERR_FAILURE; + + return poDS->GetDSExtent( psExtent, bForce ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ +GIntBig OGRS57Layer::GetFeatureCount (int bForce) +{ + + if( !TestCapability(OLCFastFeatureCount) ) + return OGRLayer::GetFeatureCount( bForce ); + else + return nFeatureCount; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRS57Layer::GetFeature( GIntBig nFeatureId ) + +{ + S57Reader *poReader = poDS->GetModule(0); // not multi-reader aware + + if( poReader != NULL && nFeatureId <= INT_MAX ) + { + OGRFeature *poFeature; + + poFeature = poReader->ReadFeature( (int)nFeatureId, poFeatureDefn ); + if( poFeature != NULL && poFeature->GetGeometryRef() != NULL ) + poFeature->GetGeometryRef()->assignSpatialReference( + GetSpatialRef() ); + return poFeature; + } + else + return NULL; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRS57Layer::ICreateFeature( OGRFeature *poFeature ) + +{ +/* -------------------------------------------------------------------- */ +/* Set RCNM if not already set. */ +/* -------------------------------------------------------------------- */ + int iRCNMFld = poFeature->GetFieldIndex( "RCNM" ); + + if( iRCNMFld != -1 ) + { + if( !poFeature->IsFieldSet( iRCNMFld ) ) + poFeature->SetField( iRCNMFld, nRCNM ); + else + { + CPLAssert( poFeature->GetFieldAsInteger( iRCNMFld ) == nRCNM ); + } + } + +/* -------------------------------------------------------------------- */ +/* Set OBJL if not already set. */ +/* -------------------------------------------------------------------- */ + if( nOBJL != -1 ) + { + int iOBJLFld = poFeature->GetFieldIndex( "OBJL" ); + + if( !poFeature->IsFieldSet( iOBJLFld ) ) + poFeature->SetField( iOBJLFld, nOBJL ); + else + { + CPLAssert( poFeature->GetFieldAsInteger( iOBJLFld ) == nOBJL ); + } + } + +/* -------------------------------------------------------------------- */ +/* Create the isolated node feature. */ +/* -------------------------------------------------------------------- */ + if( poDS->GetWriter()->WriteCompleteFeature( poFeature ) ) + return OGRERR_NONE; + else + return OGRERR_FAILURE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57.h new file mode 100644 index 000000000..74f5d0766 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57.h @@ -0,0 +1,417 @@ +/****************************************************************************** + * $Id: s57.h 28348 2015-01-23 15:27:13Z rouault $ + * + * Project: S-57 Translator + * Purpose: Declarations for S-57 translator not including the + * binding onto OGRLayer/DataSource/Driver which are found in + * ogr_s57.h. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _S57_H_INCLUDED +#define _S57_H_INCLUDED + +#include +#include "ogr_feature.h" +#include "iso8211.h" + +class S57Reader; + +char **S57FileCollector( const char * pszDataset ); + +#define EMPTY_NUMBER_MARKER 2147483641 /* MAXINT-6 */ + +/* -------------------------------------------------------------------- */ +/* Various option strings. */ +/* -------------------------------------------------------------------- */ +#define S57O_UPDATES "UPDATES" +#define S57O_LNAM_REFS "LNAM_REFS" +#define S57O_SPLIT_MULTIPOINT "SPLIT_MULTIPOINT" +#define S57O_ADD_SOUNDG_DEPTH "ADD_SOUNDG_DEPTH" +#define S57O_PRESERVE_EMPTY_NUMBERS "PRESERVE_EMPTY_NUMBERS" +#define S57O_RETURN_PRIMITIVES "RETURN_PRIMITIVES" +#define S57O_RETURN_LINKAGES "RETURN_LINKAGES" +#define S57O_RETURN_DSID "RETURN_DSID" +#define S57O_RECODE_BY_DSSI "RECODE_BY_DSSI" + +#define S57M_UPDATES 0x01 +#define S57M_LNAM_REFS 0x02 +#define S57M_SPLIT_MULTIPOINT 0x04 +#define S57M_ADD_SOUNDG_DEPTH 0x08 +#define S57M_PRESERVE_EMPTY_NUMBERS 0x10 +#define S57M_RETURN_PRIMITIVES 0x20 +#define S57M_RETURN_LINKAGES 0x40 +#define S57M_RETURN_DSID 0x80 +#define S57M_RECODE_BY_DSSI 0x100 + +/* -------------------------------------------------------------------- */ +/* RCNM values. */ +/* -------------------------------------------------------------------- */ + +#define RCNM_FE 100 /* Feature record */ + +#define RCNM_VI 110 /* Isolated Node */ +#define RCNM_VC 120 /* Connected Node */ +#define RCNM_VE 130 /* Edge */ +#define RCNM_VF 140 /* Face */ + +#define RCNM_DSID 10 + +#define OGRN_VI "IsolatedNode" +#define OGRN_VC "ConnectedNode" +#define OGRN_VE "Edge" +#define OGRN_VF "Face" + +/* -------------------------------------------------------------------- */ +/* FRID PRIM values. */ +/* -------------------------------------------------------------------- */ +#define PRIM_P 1 /* point feature */ +#define PRIM_L 2 /* line feature */ +#define PRIM_A 3 /* area feature */ +#define PRIM_N 4 /* non-spatial feature */ + +/************************************************************************/ +/* S57ClassRegistrar */ +/************************************************************************/ + +class S57ClassContentExplorer; + +class CPL_DLL S57AttrInfo +{ + public: + CPLString osName; + CPLString osAcronym; + char chType; + char chClass; +}; + +class CPL_DLL S57ClassRegistrar +{ + friend class S57ClassContentExplorer; + + // Class information: + int nClasses; + CPLStringList apszClassesInfo; + + // Attribute Information: + int nAttrCount; + std::vector aoAttrInfos; + std::vector anAttrIndex; // sorted by acronym. + + int FindFile( const char *pszTarget, const char *pszDirectory, + int bReportErr, VSILFILE **fp ); + + const char *ReadLine( VSILFILE * fp ); + char **papszNextLine; + +public: + S57ClassRegistrar(); + ~S57ClassRegistrar(); + + int LoadInfo( const char *, const char *, int ); + + // attribute table methods. + //int GetMaxAttrIndex() { return nAttrMax; } + const S57AttrInfo *GetAttrInfo(int i); + const char *GetAttrName( int i ) + { return GetAttrInfo(i) == NULL ? NULL : aoAttrInfos[i]->osName.c_str(); } + const char *GetAttrAcronym( int i ) + { return GetAttrInfo(i) == NULL ? NULL : aoAttrInfos[i]->osAcronym.c_str(); } + char GetAttrType( int i ) + { return GetAttrInfo(i) == NULL ? '\0' : aoAttrInfos[i]->chType; } +#define SAT_ENUM 'E' +#define SAT_LIST 'L' +#define SAT_FLOAT 'F' +#define SAT_INT 'I' +#define SAT_CODE_STRING 'A' +#define SAT_FREE_TEXT 'S' + + char GetAttrClass( int i ) + { return GetAttrInfo(i) == NULL ? '\0' : aoAttrInfos[i]->chClass; } + int FindAttrByAcronym( const char * ); + +}; + +/************************************************************************/ +/* S57ClassContentExplorer */ +/************************************************************************/ + +class S57ClassContentExplorer +{ + S57ClassRegistrar* poRegistrar; + + char ***papapszClassesFields; + + int iCurrentClass; + + char **papszCurrentFields; + + char **papszTempResult; + + public: + S57ClassContentExplorer(S57ClassRegistrar* poRegistrar); + ~S57ClassContentExplorer(); + + int SelectClassByIndex( int ); + int SelectClass( int ); + int SelectClass( const char * ); + + int Rewind() { return SelectClassByIndex(0); } + int NextClass() { return SelectClassByIndex(iCurrentClass+1); } + + int GetOBJL(); + const char *GetDescription(); + const char *GetAcronym(); + + char **GetAttributeList( const char * = NULL ); + + char GetClassCode(); + char **GetPrimitives(); +}; + +/************************************************************************/ +/* DDFRecordIndex */ +/* */ +/* Maintain an index of DDF records based on an integer key. */ +/************************************************************************/ + +typedef struct +{ + int nKey; + DDFRecord *poRecord; + void *pClientData; +} DDFIndexedRecord; + +class CPL_DLL DDFRecordIndex +{ + int bSorted; + + int nRecordCount; + int nRecordMax; + + int nLastObjlPos; /* rjensen. added for FindRecordByObjl() */ + int nLastObjl; /* rjensen. added for FindRecordByObjl() */ + + DDFIndexedRecord *pasRecords; + + void Sort(); + +public: + DDFRecordIndex(); + ~DDFRecordIndex(); + + void AddRecord( int nKey, DDFRecord * ); + int RemoveRecord( int nKey ); + + DDFRecord *FindRecord( int nKey ); + + DDFRecord *FindRecordByObjl( int nObjl ); /* rjensen. added for FindRecordByObjl() */ + + void Clear(); + + int GetCount() { return nRecordCount; } + + DDFRecord *GetByIndex( int i ); + void *GetClientInfoByIndex( int i ); + void SetClientInfoByIndex( int i, void *pClientInfo ); +}; + +/************************************************************************/ +/* S57Reader */ +/************************************************************************/ + +class CPL_DLL S57Reader +{ + S57ClassRegistrar *poRegistrar; + S57ClassContentExplorer* poClassContentExplorer; + + int nFDefnCount; + OGRFeatureDefn **papoFDefnList; + + std::vector apoFDefnByOBJL; + + char *pszModuleName; + char *pszDSNM; + + DDFModule *poModule; + + int nCOMF; /* Coordinate multiplier */ + int nSOMF; /* Vertical (sounding) multiplier */ + + int bFileIngested; + DDFRecordIndex oVI_Index; + DDFRecordIndex oVC_Index; + DDFRecordIndex oVE_Index; + DDFRecordIndex oVF_Index; + + int nNextVIIndex; + int nNextVCIndex; + int nNextVEIndex; + int nNextVFIndex; + + int nNextFEIndex; + DDFRecordIndex oFE_Index; + + int nNextDSIDIndex; + DDFRecord *poDSIDRecord; + DDFRecord *poDSPMRecord; + char szUPDNUpdate[10]; + + char **papszOptions; + + int nOptionFlags; + + int iPointOffset; + OGRFeature *poMultiPoint; + + int Aall; // see RecodeByDSSI() function + int Nall; // see RecodeByDSSI() function + bool needAallNallSetup; // see RecodeByDSSI() function + + void ClearPendingMultiPoint(); + OGRFeature *NextPendingMultiPoint(); + + OGRFeature *AssembleFeature( DDFRecord *, OGRFeatureDefn * ); + + void ApplyObjectClassAttributes( DDFRecord *, OGRFeature *); + void GenerateLNAMAndRefs( DDFRecord *, OGRFeature * ); + void GenerateFSPTAttributes( DDFRecord *, OGRFeature * ); + + void AssembleSoundingGeometry( DDFRecord *, OGRFeature * ); + void AssemblePointGeometry( DDFRecord *, OGRFeature * ); + void AssembleLineGeometry( DDFRecord *, OGRFeature * ); + void AssembleAreaGeometry( DDFRecord *, OGRFeature * ); + + int FetchPoint( int, int, + double *, double *, double * = NULL ); + int FetchLine( DDFRecord *, int, int, OGRLineString * ); + + OGRFeatureDefn *FindFDefn( DDFRecord * ); + int ParseName( DDFField *, int = 0, int * = NULL ); + + int ApplyRecordUpdate( DDFRecord *, DDFRecord * ); + + int bMissingWarningIssued; + int bAttrWarningIssued; + + public: + S57Reader( const char * ); + ~S57Reader(); + + void SetClassBased( S57ClassRegistrar *, S57ClassContentExplorer* ); + int SetOptions( char ** ); + int GetOptionFlags() { return nOptionFlags; } + + int Open( int bTestOpen ); + void Close(); + DDFModule *GetModule() { return poModule; } + const char *GetDSNM() { return pszDSNM; } + + int Ingest(); + int ApplyUpdates( DDFModule * ); + int FindAndApplyUpdates( const char *pszPath=NULL ); + + void Rewind(); + OGRFeature *ReadNextFeature( OGRFeatureDefn * = NULL ); + OGRFeature *ReadFeature( int nFID, OGRFeatureDefn * = NULL ); + OGRFeature *ReadVector( int nFID, int nRCNM ); + OGRFeature *ReadDSID( void ); + + int GetNextFEIndex( int nRCNM = 100 ); + void SetNextFEIndex( int nNewIndex, int nRCNM = 100 ); + + void AddFeatureDefn( OGRFeatureDefn * ); + + int CollectClassList(std::vector &anClassCount); + + OGRErr GetExtent( OGREnvelope *psExtent, int bForce ); + + char *RecodeByDSSI(const char *SourceString, bool LookAtAALL_NALL); + + }; + +/************************************************************************/ +/* S57Writer */ +/************************************************************************/ + +class CPL_DLL S57Writer +{ +public: + S57Writer(); + ~S57Writer(); + + void SetClassBased( S57ClassRegistrar *, S57ClassContentExplorer* ); + int CreateS57File( const char *pszFilename ); + int Close(); + + int WriteGeometry( DDFRecord *, int, double *, double *, + double * ); + int WriteATTF( DDFRecord *, OGRFeature * ); + int WritePrimitive( OGRFeature *poFeature ); + int WriteCompleteFeature( OGRFeature *poFeature ); + int WriteDSID( int nEXPP = 1, + int nINTU = 4, + const char *pszDSNM = NULL, + const char *pszEDTN = NULL, + const char *pszUPDN = NULL, + const char *pszUADT = NULL, + const char *pszISDT = NULL, + const char *pszSTED = NULL, + int nAGEN = 0, + const char *pszCOMT = NULL, + int nNOMR = 0, int nNOGR = 0, + int nNOLR = 0, int nNOIN = 0, + int nNOCN = 0, int nNOED = 0 + ); + int WriteDSPM( int nHDAT = 0, + int nVDAT = 0, + int nSDAT = 0, + int nCSCL = 0 ); + +// semi-private - for sophisticated writers. + DDFRecord *MakeRecord(); + DDFModule *poModule; + +private: + int nNext0001Index; + S57ClassRegistrar *poRegistrar; + S57ClassContentExplorer* poClassContentExplorer; + + int nCOMF; /* Coordinate multiplier */ + int nSOMF; /* Vertical (sounding) multiplier */ +}; + +/* -------------------------------------------------------------------- */ +/* Functions to create OGRFeatureDefns. */ +/* -------------------------------------------------------------------- */ +void CPL_DLL S57GenerateStandardAttributes( OGRFeatureDefn *, int ); +OGRFeatureDefn CPL_DLL *S57GenerateGeomFeatureDefn( OGRwkbGeometryType, int ); +OGRFeatureDefn CPL_DLL *S57GenerateObjectClassDefn( S57ClassRegistrar *, + S57ClassContentExplorer* poClassContentExplorer, + int, int ); +OGRFeatureDefn CPL_DLL *S57GenerateVectorPrimitiveFeatureDefn( int, int ); +OGRFeatureDefn CPL_DLL *S57GenerateDSIDFeatureDefn( void ); + +#endif /* ndef _S57_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57classregistrar.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57classregistrar.cpp new file mode 100644 index 000000000..d2ab8ce6e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57classregistrar.cpp @@ -0,0 +1,588 @@ +/****************************************************************************** + * $Id: s57classregistrar.cpp 27885 2014-10-19 22:56:48Z rouault $ + * + * Project: S-57 Translator + * Purpose: Implements S57ClassRegistrar class for keeping track of + * information on S57 object classes. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "s57.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: s57classregistrar.cpp 27885 2014-10-19 22:56:48Z rouault $"); + + +#ifdef S57_BUILTIN_CLASSES +#include "s57tables.h" +#endif + +/************************************************************************/ +/* S57ClassRegistrar() */ +/************************************************************************/ + +S57ClassRegistrar::S57ClassRegistrar() + +{ + papszNextLine = NULL; +} + +/************************************************************************/ +/* ~S57ClassRegistrar() */ +/************************************************************************/ + +S57ClassRegistrar::~S57ClassRegistrar() + +{ + nClasses = 0; + for(size_t i=0;inClasses; i++ ) + CSLDestroy( papapszClassesFields[i] ); + CPLFree( papapszClassesFields ); + } +} + +/************************************************************************/ +/* FindFile() */ +/************************************************************************/ + +int S57ClassRegistrar::FindFile( const char *pszTarget, + const char *pszDirectory, + int bReportErr, + VSILFILE **pfp ) + +{ + const char *pszFilename; + + if( pszDirectory == NULL ) + { + pszFilename = CPLFindFile( "s57", pszTarget ); + if( pszFilename == NULL ) + pszFilename = pszTarget; + } + else + { + pszFilename = CPLFormFilename( pszDirectory, pszTarget, NULL ); + } + + *pfp = VSIFOpenL( pszFilename, "rb" ); + +#ifdef S57_BUILTIN_CLASSES + if( *pfp == NULL ) + { + if( EQUAL(pszTarget, "s57objectclasses.csv") ) + papszNextLine = gpapszS57Classes; + else + papszNextLine = gpapszS57attributes; + } +#else + if( *pfp == NULL ) + { + if( bReportErr ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %s.\n", + pszFilename ); + return FALSE; + } +#endif + + return TRUE; +} + +/************************************************************************/ +/* ReadLine() */ +/* */ +/* Read a line from the provided file, or from the "built-in" */ +/* configuration file line list if the file is NULL. */ +/************************************************************************/ + +const char *S57ClassRegistrar::ReadLine( VSILFILE * fp ) + +{ + if( fp != NULL ) + return CPLReadLineL( fp ); + + if( papszNextLine == NULL ) + return NULL; + + if( *papszNextLine == NULL ) + { + papszNextLine = NULL; + return NULL; + } + else + return *(papszNextLine++); +} + +/************************************************************************/ +/* LoadInfo() */ +/************************************************************************/ + +int S57ClassRegistrar::LoadInfo( const char * pszDirectory, + const char * pszProfile, + int bReportErr ) + +{ + VSILFILE *fp; + char szTargetFile[1024]; + + if( pszDirectory == NULL ) + pszDirectory = CPLGetConfigOption("S57_CSV",NULL); + +/* ==================================================================== */ +/* Read the s57objectclasses file. */ +/* ==================================================================== */ + if( pszProfile == NULL ) + pszProfile = CPLGetConfigOption( "S57_PROFILE", "" ); + + if( EQUAL(pszProfile, "Additional_Military_Layers") ) + { + sprintf( szTargetFile, "s57objectclasses_%s.csv", "aml" ); + } + else if ( EQUAL(pszProfile, "Inland_Waterways") ) + { + sprintf( szTargetFile, "s57objectclasses_%s.csv", "iw" ); + } + else if( strlen(pszProfile) > 0 ) + { + snprintf( szTargetFile, sizeof(szTargetFile), "s57objectclasses_%s.csv", pszProfile ); + } + else + { + strcpy( szTargetFile, "s57objectclasses.csv" ); + } + + if( !FindFile( szTargetFile, pszDirectory, bReportErr, &fp ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Skip the line defining the column titles. */ +/* -------------------------------------------------------------------- */ + const char * pszLine = ReadLine( fp ); + + if( !EQUAL(pszLine, + "\"Code\",\"ObjectClass\",\"Acronym\",\"Attribute_A\"," + "\"Attribute_B\",\"Attribute_C\",\"Class\",\"Primitives\"" ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "s57objectclasses columns don't match expected format!\n" ); + if( fp != NULL ) + VSIFCloseL( fp ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Read and form string list. */ +/* -------------------------------------------------------------------- */ + apszClassesInfo.Clear(); + while( (pszLine = ReadLine(fp)) != NULL ) + { + apszClassesInfo.AddString(pszLine); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup, and establish state. */ +/* -------------------------------------------------------------------- */ + if( fp != NULL ) + VSIFCloseL( fp ); + + nClasses = apszClassesInfo.size(); + if( nClasses == 0 ) + return FALSE; + +/* ==================================================================== */ +/* Read the attributes list. */ +/* ==================================================================== */ + + if( EQUAL(pszProfile, "Additional_Military_Layers") ) + { + sprintf( szTargetFile, "s57attributes_%s.csv", "aml" ); + } + else if ( EQUAL(pszProfile, "Inland_Waterways") ) + { + sprintf( szTargetFile, "s57attributes_%s.csv", "iw" ); + } + else if( strlen(pszProfile) > 0 ) + { + snprintf( szTargetFile, sizeof(szTargetFile), "s57attributes_%s.csv", pszProfile ); + } + else + { + strcpy( szTargetFile, "s57attributes.csv" ); + } + + if( !FindFile( szTargetFile, pszDirectory, bReportErr, &fp ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Skip the line defining the column titles. */ +/* -------------------------------------------------------------------- */ + pszLine = ReadLine( fp ); + + if( !EQUAL(pszLine, + "\"Code\",\"Attribute\",\"Acronym\",\"Attributetype\",\"Class\"") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "s57attributes columns don't match expected format!\n" ); + if( fp != NULL ) + VSIFCloseL( fp ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Read and form string list. */ +/* -------------------------------------------------------------------- */ + int iAttr; + + while( (pszLine = ReadLine(fp)) != NULL ) + { + char **papszTokens = CSLTokenizeStringComplex( pszLine, ",", + TRUE, TRUE ); + + if( CSLCount(papszTokens) < 5 ) + { + CPLAssert( FALSE ); + continue; + } + + iAttr = atoi(papszTokens[0]); + if( iAttr >= (int) aoAttrInfos.size() ) + aoAttrInfos.resize(iAttr+1); + + if( iAttr < 0 || aoAttrInfos[iAttr] != NULL ) + { + CPLDebug( "S57", + "Duplicate/corrupt definition for attribute %d:%s", + iAttr, papszTokens[2] ); + continue; + } + + aoAttrInfos[iAttr] = new S57AttrInfo(); + aoAttrInfos[iAttr]->osName = papszTokens[1]; + aoAttrInfos[iAttr]->osAcronym = papszTokens[2]; + aoAttrInfos[iAttr]->chType = papszTokens[3][0]; + aoAttrInfos[iAttr]->chClass = papszTokens[4][0]; + anAttrIndex.push_back(iAttr); + CSLDestroy( papszTokens ); + } + + if( fp != NULL ) + VSIFCloseL( fp ); + + nAttrCount = anAttrIndex.size(); + +/* -------------------------------------------------------------------- */ +/* Sort index by acronym. */ +/* -------------------------------------------------------------------- */ + int bModified; + do + { + bModified = FALSE; + for( iAttr = 0; iAttr < nAttrCount-1; iAttr++ ) + { + if( strcmp(aoAttrInfos[anAttrIndex[iAttr]]->osAcronym, + aoAttrInfos[anAttrIndex[iAttr+1]]->osAcronym) > 0 ) + { + int nTemp; + + nTemp = anAttrIndex[iAttr]; + anAttrIndex[iAttr] = anAttrIndex[iAttr+1]; + anAttrIndex[iAttr+1] = nTemp; + bModified = TRUE; + } + } + } while( bModified ); + + return TRUE; +} + +/************************************************************************/ +/* SelectClassByIndex() */ +/************************************************************************/ + +int S57ClassContentExplorer::SelectClassByIndex( int nNewIndex ) + +{ + if( nNewIndex < 0 || nNewIndex >= poRegistrar->nClasses ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Do we have our cache of class information field lists? */ +/* -------------------------------------------------------------------- */ + if( papapszClassesFields == NULL ) + { + papapszClassesFields = (char ***) CPLCalloc(sizeof(void*),poRegistrar->nClasses); + } + +/* -------------------------------------------------------------------- */ +/* Has this info been parsed yet? */ +/* -------------------------------------------------------------------- */ + if( papapszClassesFields[nNewIndex] == NULL ) + papapszClassesFields[nNewIndex] = + CSLTokenizeStringComplex( poRegistrar->apszClassesInfo[nNewIndex], + ",", TRUE, TRUE ); + + papszCurrentFields = papapszClassesFields[nNewIndex]; + + iCurrentClass = nNewIndex; + + return TRUE; +} + +/************************************************************************/ +/* SelectClass() */ +/************************************************************************/ + +int S57ClassContentExplorer::SelectClass( int nOBJL ) + +{ + for( int i = 0; i < poRegistrar->nClasses; i++ ) + { + if( atoi(poRegistrar->apszClassesInfo[i]) == nOBJL ) + return SelectClassByIndex( i ); + } + + return FALSE; +} + +/************************************************************************/ +/* SelectClass() */ +/************************************************************************/ + +int S57ClassContentExplorer::SelectClass( const char *pszAcronym ) + +{ + for( int i = 0; i < poRegistrar->nClasses; i++ ) + { + if( !SelectClassByIndex( i ) ) + continue; + + if( strcmp(GetAcronym(),pszAcronym) == 0 ) + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* GetOBJL() */ +/************************************************************************/ + +int S57ClassContentExplorer::GetOBJL() + +{ + if( iCurrentClass >= 0 ) + return atoi(poRegistrar->apszClassesInfo[iCurrentClass]); + else + return -1; +} + +/************************************************************************/ +/* GetDescription() */ +/************************************************************************/ + +const char * S57ClassContentExplorer::GetDescription() + +{ + if( iCurrentClass >= 0 && papszCurrentFields[0] != NULL ) + return papszCurrentFields[1]; + else + return NULL; +} + +/************************************************************************/ +/* GetAcronym() */ +/************************************************************************/ + +const char * S57ClassContentExplorer::GetAcronym() + +{ + if( iCurrentClass >= 0 + && papszCurrentFields[0] != NULL + && papszCurrentFields[1] != NULL ) + return papszCurrentFields[2]; + else + return NULL; +} + +/************************************************************************/ +/* GetAttributeList() */ +/* */ +/* The passed string can be "a", "b", "c" or NULL for all. The */ +/* returned list remained owned by this object, not the caller. */ +/************************************************************************/ + +char **S57ClassContentExplorer::GetAttributeList( const char * pszType ) + +{ + if( iCurrentClass < 0 ) + return NULL; + + CSLDestroy( papszTempResult ); + papszTempResult = NULL; + + for( int iColumn = 3; iColumn < 6; iColumn++ ) + { + if( pszType != NULL && iColumn == 3 && !EQUAL(pszType,"a") ) + continue; + + if( pszType != NULL && iColumn == 4 && !EQUAL(pszType,"b") ) + continue; + + if( pszType != NULL && iColumn == 5 && !EQUAL(pszType,"c") ) + continue; + + char **papszTokens; + + papszTokens = + CSLTokenizeStringComplex( papszCurrentFields[iColumn], ";", + TRUE, FALSE ); + + papszTempResult = CSLInsertStrings( papszTempResult, -1, + papszTokens ); + + CSLDestroy( papszTokens ); + } + + return papszTempResult; +} + +/************************************************************************/ +/* GetClassCode() */ +/************************************************************************/ + +char S57ClassContentExplorer::GetClassCode() + +{ + if( iCurrentClass >= 0 + && papszCurrentFields[0] != NULL + && papszCurrentFields[1] != NULL + && papszCurrentFields[2] != NULL + && papszCurrentFields[3] != NULL + && papszCurrentFields[4] != NULL + && papszCurrentFields[5] != NULL + && papszCurrentFields[6] != NULL ) + return papszCurrentFields[6][0]; + else + return '\0'; +} + +/************************************************************************/ +/* GetPrimitives() */ +/************************************************************************/ + +char **S57ClassContentExplorer::GetPrimitives() + +{ + if( iCurrentClass >= 0 + && CSLCount(papszCurrentFields) > 7 ) + { + CSLDestroy( papszTempResult ); + papszTempResult = + CSLTokenizeStringComplex( papszCurrentFields[7], ";", + TRUE, FALSE ); + return papszTempResult; + } + else + return NULL; +} + +/************************************************************************/ +/* GetAttrInfo() */ +/************************************************************************/ + +const S57AttrInfo *S57ClassRegistrar::GetAttrInfo(int iAttr) +{ + if( iAttr < 0 || iAttr >= (int) aoAttrInfos.size() ) + return NULL; + else + return aoAttrInfos[iAttr]; +} + +/************************************************************************/ +/* FindAttrByAcronym() */ +/************************************************************************/ + +int S57ClassRegistrar::FindAttrByAcronym( const char * pszName ) + +{ + int iStart, iEnd, iCandidate; + + iStart = 0; + iEnd = nAttrCount-1; + + while( iStart <= iEnd ) + { + int nCompareValue; + + iCandidate = (iStart + iEnd)/2; + nCompareValue = + strcmp(pszName, aoAttrInfos[anAttrIndex[iCandidate]]->osAcronym); + + if( nCompareValue < 0 ) + { + iEnd = iCandidate-1; + } + else if( nCompareValue > 0 ) + { + iStart = iCandidate+1; + } + else + return anAttrIndex[iCandidate]; + } + + return -1; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57dump.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57dump.cpp new file mode 100644 index 000000000..814a29160 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57dump.cpp @@ -0,0 +1,207 @@ +/****************************************************************************** + * $Id: s57dump.cpp 26388 2013-09-02 18:04:16Z warmerdam $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Simple client for viewing S57 driver data. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "s57.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: s57dump.cpp 26388 2013-09-02 18:04:16Z warmerdam $"); + +/************************************************************************/ +/* main() */ +/************************************************************************/ + +int main( int nArgc, char ** papszArgv ) + +{ + char **papszOptions = NULL; + int bReturnPrimitives = FALSE; + char *pszDataPath = NULL; + + if( nArgc < 2 ) + { + printf( "Usage: s57dump [-pen] [-split] [-lnam] [-return-prim] [-no-update]\n" + " [-return-link] [-data ] filename\n" ); + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Process commandline arguments. */ +/* -------------------------------------------------------------------- */ + for( int iArg = 1; iArg < nArgc-1; iArg++ ) + { + if( EQUAL(papszArgv[iArg],"-split") ) + papszOptions = + CSLSetNameValue( papszOptions, S57O_SPLIT_MULTIPOINT, "ON" ); + else if( EQUAL(papszArgv[iArg],"-data") ) + pszDataPath = papszArgv[++iArg]; + else if( EQUAL(papszArgv[iArg],"-no-update") ) + papszOptions = + CSLSetNameValue( papszOptions, S57O_UPDATES, "OFF" ); + else if( EQUAL(papszArgv[iArg],"-pen") ) + papszOptions = + CSLSetNameValue( papszOptions, S57O_PRESERVE_EMPTY_NUMBERS, + "ON" ); + else if( EQUALN(papszArgv[iArg],"-return-prim",12) ) + { + papszOptions = + CSLSetNameValue( papszOptions, S57O_RETURN_PRIMITIVES, + "ON" ); + bReturnPrimitives = TRUE; + } + else if( EQUALN(papszArgv[iArg],"-lnam",4) ) + papszOptions = + CSLSetNameValue( papszOptions, S57O_LNAM_REFS, "ON" ); + else if( EQUALN(papszArgv[iArg],"-return-link",12) ) + papszOptions = + CSLSetNameValue( papszOptions, S57O_RETURN_LINKAGES, "ON" ); + } + +/* -------------------------------------------------------------------- */ +/* Load the class definitions into the registrar. */ +/* -------------------------------------------------------------------- */ + S57ClassRegistrar oRegistrar; + int bRegistrarLoaded; + + bRegistrarLoaded = oRegistrar.LoadInfo( pszDataPath, NULL, TRUE ); + + S57ClassContentExplorer *poClassContentExplorer = NULL; + if (bRegistrarLoaded) + poClassContentExplorer = new S57ClassContentExplorer(&oRegistrar); + +/* -------------------------------------------------------------------- */ +/* Get a list of candidate files. */ +/* -------------------------------------------------------------------- */ + char **papszFiles; + int iFile; + + papszFiles = S57FileCollector( papszArgv[nArgc-1] ); + + for( iFile = 0; papszFiles != NULL && papszFiles[iFile] != NULL; iFile++ ) + { + printf( "Found: %s\n", papszFiles[iFile] ); + } + + for( iFile = 0; papszFiles != NULL && papszFiles[iFile] != NULL; iFile++ ) + { + printf( "<------------------------------------------------------------------------->\n" ); + printf( "\nFile: %s\n\n", papszFiles[iFile] ); + + S57Reader oReader( papszFiles[iFile] ); + + oReader.SetOptions( papszOptions ); + + int nOptionFlags = oReader.GetOptionFlags(); + + if( !oReader.Open( FALSE ) ) + continue; + + if( bRegistrarLoaded ) + { + int bGeneric = FALSE; + std::vector anClassList; + unsigned int i; + oReader.CollectClassList(anClassList); + + oReader.SetClassBased( &oRegistrar, poClassContentExplorer ); + + printf( "Classes found:\n" ); + for( i = 0; i < anClassList.size(); i++ ) + { + if( anClassList[i] == 0 ) + continue; + + if( poClassContentExplorer->SelectClass( i ) ) + { + printf( "%d: %s/%s\n", + i, + poClassContentExplorer->GetAcronym(), + poClassContentExplorer->GetDescription() ); + + oReader.AddFeatureDefn( + S57GenerateObjectClassDefn( &oRegistrar, + poClassContentExplorer, + i, nOptionFlags ) ); + } + else + { + printf( "%d: unrecognised ... treat as generic.\n", i ); + bGeneric = TRUE; + } + } + + if( bGeneric ) + { + oReader.AddFeatureDefn( + S57GenerateGeomFeatureDefn( wkbUnknown, nOptionFlags ) ); + } + } + else + { + oReader.AddFeatureDefn( + S57GenerateGeomFeatureDefn( wkbPoint, nOptionFlags ) ); + oReader.AddFeatureDefn( + S57GenerateGeomFeatureDefn( wkbLineString, nOptionFlags ) ); + oReader.AddFeatureDefn( + S57GenerateGeomFeatureDefn( wkbPolygon, nOptionFlags ) ); + oReader.AddFeatureDefn( + S57GenerateGeomFeatureDefn( wkbNone, nOptionFlags ) ); + } + + if( bReturnPrimitives ) + { + oReader.AddFeatureDefn( + S57GenerateVectorPrimitiveFeatureDefn( RCNM_VI, nOptionFlags)); + oReader.AddFeatureDefn( + S57GenerateVectorPrimitiveFeatureDefn( RCNM_VC, nOptionFlags)); + oReader.AddFeatureDefn( + S57GenerateVectorPrimitiveFeatureDefn( RCNM_VE, nOptionFlags)); + oReader.AddFeatureDefn( + S57GenerateVectorPrimitiveFeatureDefn( RCNM_VF, nOptionFlags)); + } + + oReader.AddFeatureDefn( S57GenerateDSIDFeatureDefn() ); + + OGRFeature *poFeature; + int nFeatures = 0; + DDFModule oUpdate; + + while( (poFeature = oReader.ReadNextFeature()) != NULL ) + { + poFeature->DumpReadable( stdout ); + nFeatures++; + delete poFeature; + } + + printf( "Feature Count: %d\n", nFeatures ); + } + + return 0; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57featuredefns.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57featuredefns.cpp new file mode 100644 index 000000000..a52934eee --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57featuredefns.cpp @@ -0,0 +1,534 @@ +/****************************************************************************** + * $Id: s57featuredefns.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: S-57 Translator + * Purpose: Implements methods to create OGRFeatureDefns for various + * object classes, and primitive features. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001, 2003 Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "s57.h" +#include "ogr_api.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: s57featuredefns.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + + +/************************************************************************/ +/* S57GenerateGeomFeatureDefn() */ +/************************************************************************/ + +OGRFeatureDefn *S57GenerateDSIDFeatureDefn() + +{ + OGRFeatureDefn *poFDefn = new OGRFeatureDefn( "DSID" ); + OGRFieldDefn oField( "", OFTInteger ); + + poFDefn->SetGeomType( wkbNone ); + poFDefn->Reference(); + +/* -------------------------------------------------------------------- */ +/* DSID fields. */ +/* -------------------------------------------------------------------- */ + oField.Set( "DSID_EXPP", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_INTU", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_DSNM", OFTString, 0, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_EDTN", OFTString, 0, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_UPDN", OFTString, 0, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_UADT", OFTString, 8, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_ISDT", OFTString, 8, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_STED", OFTReal, 11, 6 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_PRSP", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_PSDN", OFTString, 0, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_PRED", OFTString, 0, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_PROF", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_AGEN", OFTInteger, 5, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSID_COMT", OFTString, 0, 0 ); + poFDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* DSSI fields. */ +/* -------------------------------------------------------------------- */ + + oField.Set( "DSSI_DSTR", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSSI_AALL", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSSI_NALL", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSSI_NOMR", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSSI_NOCR", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSSI_NOGR", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSSI_NOLR", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSSI_NOIN", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSSI_NOCN", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSSI_NOED", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSSI_NOFA", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* DSPM fields. */ +/* -------------------------------------------------------------------- */ + + oField.Set( "DSPM_HDAT", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSPM_VDAT", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSPM_SDAT", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSPM_CSCL", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSPM_DUNI", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSPM_HUNI", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSPM_PUNI", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSPM_COUN", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSPM_COMF", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSPM_SOMF", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "DSPM_COMT", OFTString, 0, 0 ); + poFDefn->AddFieldDefn( &oField ); + + return poFDefn; +} + +/************************************************************************/ +/* S57GenerateGeomFeatureDefn() */ +/************************************************************************/ + +OGRFeatureDefn *S57GenerateGeomFeatureDefn( OGRwkbGeometryType eGType, + int nOptionFlags ) + +{ + OGRFeatureDefn *poFDefn = NULL; + + if( eGType == wkbPoint ) + { + poFDefn = new OGRFeatureDefn( "Point" ); + poFDefn->SetGeomType( eGType ); + } + else if( eGType == wkbLineString ) + { + poFDefn = new OGRFeatureDefn( "Line" ); + poFDefn->SetGeomType( eGType ); + } + else if( eGType == wkbPolygon ) + { + poFDefn = new OGRFeatureDefn( "Area" ); + poFDefn->SetGeomType( eGType ); + } + else if( eGType == wkbNone ) + { + poFDefn = new OGRFeatureDefn( "Meta" ); + poFDefn->SetGeomType( eGType ); + } + else if( eGType == wkbUnknown ) + { + poFDefn = new OGRFeatureDefn( "Generic" ); + poFDefn->SetGeomType( eGType ); + } + else + return NULL; + + poFDefn->Reference(); + S57GenerateStandardAttributes( poFDefn, nOptionFlags ); + + return poFDefn; +} + +/************************************************************************/ +/* S57GenerateVectorPrimitiveFeatureDefn() */ +/************************************************************************/ + +OGRFeatureDefn * +S57GenerateVectorPrimitiveFeatureDefn( int nRCNM, + CPL_UNUSED int nOptionFlags ) +{ + OGRFeatureDefn *poFDefn = NULL; + + if( nRCNM == RCNM_VI ) + { + poFDefn = new OGRFeatureDefn( OGRN_VI ); + poFDefn->SetGeomType( wkbPoint ); + } + else if( nRCNM == RCNM_VC ) + { + poFDefn = new OGRFeatureDefn( OGRN_VC ); + poFDefn->SetGeomType( wkbPoint ); + } + else if( nRCNM == RCNM_VE ) + { + poFDefn = new OGRFeatureDefn( OGRN_VE ); + poFDefn->SetGeomType( wkbUnknown ); + } + else if( nRCNM == RCNM_VF ) + { + poFDefn = new OGRFeatureDefn( OGRN_VF ); + poFDefn->SetGeomType( wkbPolygon ); + } + else + return NULL; + + poFDefn->Reference(); + +/* -------------------------------------------------------------------- */ +/* Core vector primitive attributes */ +/* -------------------------------------------------------------------- */ + OGRFieldDefn oField("",OFTInteger); + + oField.Set( "RCNM", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "RCID", OFTInteger, 8, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "RVER", OFTInteger, 2, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "RUIN", OFTInteger, 2, 0 ); + poFDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* For lines we want to capture the point links for the first */ +/* and last nodes. */ +/* -------------------------------------------------------------------- */ + if( nRCNM == RCNM_VE ) + { + oField.Set( "NAME_RCNM_0", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "NAME_RCID_0", OFTInteger, 8, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "ORNT_0", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "USAG_0", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "TOPI_0", OFTInteger, 1, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "MASK_0", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "NAME_RCNM_1", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "NAME_RCID_1", OFTInteger, 8, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "ORNT_1", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "USAG_1", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "TOPI_1", OFTInteger, 1, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "MASK_1", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + } + + return poFDefn; +} + +/************************************************************************/ +/* S57GenerateObjectClassDefn() */ +/************************************************************************/ + +OGRFeatureDefn *S57GenerateObjectClassDefn( S57ClassRegistrar *poCR, + S57ClassContentExplorer* poClassContentExplorer, + int nOBJL, int nOptionFlags ) + +{ + OGRFeatureDefn *poFDefn = NULL; + char **papszGeomPrim; + + if( !poClassContentExplorer->SelectClass( nOBJL ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the feature definition based on the object class */ +/* acronym. */ +/* -------------------------------------------------------------------- */ + poFDefn = new OGRFeatureDefn( poClassContentExplorer->GetAcronym() ); + poFDefn->Reference(); + +/* -------------------------------------------------------------------- */ +/* Try and establish the geometry type. If more than one */ +/* geometry type is allowed we just fall back to wkbUnknown. */ +/* -------------------------------------------------------------------- */ + papszGeomPrim = poClassContentExplorer->GetPrimitives(); + if( CSLCount(papszGeomPrim) == 0 ) + { + poFDefn->SetGeomType( wkbNone ); + } + else if( CSLCount(papszGeomPrim) > 1 ) + { + // leave as unknown geometry type. + } + else if( papszGeomPrim[0][0] == 'P' ) + { + if( EQUAL(poClassContentExplorer->GetAcronym(),"SOUNDG") ) + { + if( nOptionFlags & S57M_SPLIT_MULTIPOINT ) + poFDefn->SetGeomType( wkbPoint25D ); + else + poFDefn->SetGeomType( wkbMultiPoint25D ); + } + else + poFDefn->SetGeomType( wkbPoint ); + } + else if( papszGeomPrim[0][0] == 'A' ) + { + poFDefn->SetGeomType( wkbPolygon ); + } + else if( papszGeomPrim[0][0] == 'L' ) + { + // unfortunately this could be a multilinestring + poFDefn->SetGeomType( wkbUnknown ); + } + +/* -------------------------------------------------------------------- */ +/* Add the standard attributes. */ +/* -------------------------------------------------------------------- */ + S57GenerateStandardAttributes( poFDefn, nOptionFlags ); + +/* -------------------------------------------------------------------- */ +/* Add the attributes specific to this object class. */ +/* -------------------------------------------------------------------- */ + char **papszAttrList = poClassContentExplorer->GetAttributeList(); + + for( int iAttr = 0; + papszAttrList != NULL && papszAttrList[iAttr] != NULL; + iAttr++ ) + { + int iAttrIndex = poCR->FindAttrByAcronym( papszAttrList[iAttr] ); + + if( iAttrIndex == -1 ) + { + CPLDebug( "S57", "Can't find attribute %s from class %s:%s.", + papszAttrList[iAttr], + poClassContentExplorer->GetAcronym(), + poClassContentExplorer->GetDescription() ); + continue; + } + + OGRFieldDefn oField( papszAttrList[iAttr], OFTInteger ); + + switch( poCR->GetAttrType( iAttrIndex ) ) + { + case SAT_ENUM: + case SAT_INT: + oField.SetType( OFTInteger ); + break; + + case SAT_FLOAT: + oField.SetType( OFTReal ); + break; + + case SAT_CODE_STRING: + case SAT_FREE_TEXT: + oField.SetType( OFTString ); + break; + + case SAT_LIST: + oField.SetType( OFTString ); + break; + } + + poFDefn->AddFieldDefn( &oField ); + } + + +/* -------------------------------------------------------------------- */ +/* Do we need to add DEPTH attributes to soundings? */ +/* -------------------------------------------------------------------- */ + if( EQUAL(poClassContentExplorer->GetAcronym(),"SOUNDG") + && (nOptionFlags & S57M_ADD_SOUNDG_DEPTH) ) + { + OGRFieldDefn oField( "DEPTH", OFTReal ); + poFDefn->AddFieldDefn( &oField ); + } + + return poFDefn; +} + +/************************************************************************/ +/* S57GenerateStandardAttributes() */ +/* */ +/* Attach standard feature attributes to a feature definition. */ +/************************************************************************/ + +void S57GenerateStandardAttributes( OGRFeatureDefn *poFDefn, int nOptionFlags ) + +{ + OGRFieldDefn oField( "", OFTInteger ); + +/* -------------------------------------------------------------------- */ +/* RCID */ +/* -------------------------------------------------------------------- */ + oField.Set( "RCID", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* PRIM */ +/* -------------------------------------------------------------------- */ + oField.Set( "PRIM", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* GRUP */ +/* -------------------------------------------------------------------- */ + oField.Set( "GRUP", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* OBJL */ +/* -------------------------------------------------------------------- */ + oField.Set( "OBJL", OFTInteger, 5, 0 ); + poFDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* RVER */ +/* -------------------------------------------------------------------- */ + oField.Set( "RVER", OFTInteger, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* AGEN */ +/* -------------------------------------------------------------------- */ + oField.Set( "AGEN", OFTInteger, 5, 0 ); + poFDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* FIDN */ +/* -------------------------------------------------------------------- */ + oField.Set( "FIDN", OFTInteger, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* FIDS */ +/* -------------------------------------------------------------------- */ + oField.Set( "FIDS", OFTInteger, 5, 0 ); + poFDefn->AddFieldDefn( &oField ); + +/* -------------------------------------------------------------------- */ +/* LNAM - only generated when LNAM strings are being used. */ +/* -------------------------------------------------------------------- */ + if( nOptionFlags & S57M_LNAM_REFS ) + { + oField.Set( "LNAM", OFTString, 16, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "LNAM_REFS", OFTStringList, 16, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "FFPT_RIND", OFTIntegerList, 1, 0 ); + poFDefn->AddFieldDefn( &oField ); + + // We should likely include FFPT_COMT here. + } + +/* -------------------------------------------------------------------- */ +/* Values from FSPT field. */ +/* -------------------------------------------------------------------- */ + if( nOptionFlags & S57M_RETURN_LINKAGES ) + { + oField.Set( "NAME_RCNM", OFTIntegerList, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "NAME_RCID", OFTIntegerList, 10, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "ORNT", OFTIntegerList, 1, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "USAG", OFTIntegerList, 1, 0 ); + poFDefn->AddFieldDefn( &oField ); + + oField.Set( "MASK", OFTIntegerList, 3, 0 ); + poFDefn->AddFieldDefn( &oField ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57filecollector.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57filecollector.cpp new file mode 100644 index 000000000..b58b209ee --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57filecollector.cpp @@ -0,0 +1,188 @@ +/****************************************************************************** + * $Id: s57filecollector.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: S-57 Translator + * Purpose: Implements S57FileCollector() function. This function collects + * a list of S-57 data files based on the contents of a directory, + * catalog file, or direct reference to an S-57 file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "s57.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: s57filecollector.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +/************************************************************************/ +/* S57FileCollector() */ +/************************************************************************/ + +char **S57FileCollector( const char *pszDataset ) + +{ + VSIStatBuf sStatBuf; + char **papszRetList = NULL; + +/* -------------------------------------------------------------------- */ +/* Stat the dataset, and fail if it isn't a file or directory. */ +/* -------------------------------------------------------------------- */ + if( CPLStat( pszDataset, &sStatBuf ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No S-57 files found, %s\nisn't a directory or a file.\n", + pszDataset ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* We handle directories by scanning for all S-57 data files in */ +/* them, but not for catalogs. */ +/* -------------------------------------------------------------------- */ + if( VSI_ISDIR(sStatBuf.st_mode) ) + { + char **papszDirFiles = CPLReadDir( pszDataset ); + int iFile; + DDFModule oModule; + + for( iFile = 0; + papszDirFiles != NULL && papszDirFiles[iFile] != NULL; + iFile++ ) + { + char *pszFullFile; + + pszFullFile = CPLStrdup( + CPLFormFilename( pszDataset, papszDirFiles[iFile], NULL ) ); + + // Add to list if it is an S-57 _data_ file. + if( VSIStat( pszFullFile, &sStatBuf ) == 0 + && VSI_ISREG( sStatBuf.st_mode ) + && oModule.Open( pszFullFile, TRUE ) ) + { + if( oModule.FindFieldDefn("DSID") != NULL ) + papszRetList = CSLAddString( papszRetList, pszFullFile ); + } + + CPLFree( pszFullFile ); + } + + return papszRetList; + } + +/* -------------------------------------------------------------------- */ +/* If this is a regular file, but not a catalog just return it. */ +/* Note that the caller may still open it and fail. */ +/* -------------------------------------------------------------------- */ + DDFModule oModule; + DDFRecord *poRecord; + + if( !oModule.Open(pszDataset) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The file %s isn't an S-57 data file, or catalog.\n", + pszDataset ); + + return NULL; + } + + poRecord = oModule.ReadRecord(); + if( poRecord == NULL ) + return NULL; + + if( poRecord->FindField( "CATD" ) == NULL + || oModule.FindFieldDefn("CATD")->FindSubfieldDefn( "IMPL" ) == NULL ) + { + papszRetList = CSLAddString( papszRetList, pszDataset ); + return papszRetList; + } + + +/* -------------------------------------------------------------------- */ +/* We presumably have a catalog. It contains paths to files */ +/* that generally lack the ENC_ROOT component. Try to find the */ +/* correct name for the ENC_ROOT directory if available and */ +/* build a base path for our purposes. */ +/* -------------------------------------------------------------------- */ + char *pszCatDir = CPLStrdup( CPLGetPath( pszDataset ) ); + char *pszRootDir = NULL; + + if( CPLStat( CPLFormFilename(pszCatDir,"ENC_ROOT",NULL), &sStatBuf ) == 0 + && VSI_ISDIR(sStatBuf.st_mode) ) + { + pszRootDir = CPLStrdup(CPLFormFilename( pszCatDir, "ENC_ROOT", NULL )); + } + else if( CPLStat( CPLFormFilename( pszCatDir, "enc_root", NULL ), + &sStatBuf ) == 0 && VSI_ISDIR(sStatBuf.st_mode) ) + { + pszRootDir = CPLStrdup(CPLFormFilename( pszCatDir, "enc_root", NULL )); + } + + if( pszRootDir ) + CPLDebug( "S57", "Found root directory to be %s.", + pszRootDir ); + +/* -------------------------------------------------------------------- */ +/* We have a catalog. Scan it for data files, those with an */ +/* IMPL of BIN. Shouldn't there be a better way of testing */ +/* whether a file is a data file or another catalog file? */ +/* -------------------------------------------------------------------- */ + for( ; poRecord != NULL; poRecord = oModule.ReadRecord() ) + { + if( poRecord->FindField( "CATD" ) != NULL + && EQUAL(poRecord->GetStringSubfield("CATD",0,"IMPL",0),"BIN") ) + { + const char *pszFile, *pszWholePath; + + pszFile = poRecord->GetStringSubfield("CATD",0,"FILE",0); + + // Often there is an extra ENC_ROOT in the path, try finding + // this file. + + pszWholePath = CPLFormFilename( pszCatDir, pszFile, NULL ); + if( CPLStat( pszWholePath, &sStatBuf ) != 0 + && pszRootDir != NULL ) + { + pszWholePath = CPLFormFilename( pszRootDir, pszFile, NULL ); + } + + if( CPLStat( pszWholePath, &sStatBuf ) != 0 ) + { + CPLError( CE_Warning, CPLE_OpenFailed, + "Can't find file %s from catalog %s.", + pszFile, pszDataset ); + continue; + } + + papszRetList = CSLAddString( papszRetList, pszWholePath ); + CPLDebug( "S57", "Got path %s from CATALOG.", pszWholePath ); + } + } + + CPLFree( pszCatDir ); + CPLFree( pszRootDir ); + + return papszRetList; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57reader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57reader.cpp new file mode 100644 index 000000000..9142910b3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57reader.cpp @@ -0,0 +1,3460 @@ +/****************************************************************************** + * $Id: s57reader.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: S-57 Translator + * Purpose: Implements S57Reader class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "s57.h" +#include "ogr_api.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +#include +#include + +CPL_CVSID("$Id: s57reader.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/*************************************************************************************************************** +* Recode the given string from a source encoding to UTF-8 encoding. +* The source encoding is established by inspecting the AALL and NALL fields of the S57 DSSI record. If first +* time, the DSSI is read to setup appropriate variables. Main scope of this function is to have the strings +* of all attributes encoded/recoded to the same codepage in the final Shapefiles .DBF. +* +* @param[in] SourceString: source string to be recoded to UTF-8. +* LookAtAALL-NALL: flag indicating if the string becomes from an international attribute (e.g. +* INFORM, OBJNAM) or national attribute (e.g NINFOM, NOBJNM). The type of +* encoding is contained in two different fields of the S57 DSSI record: AALL for +* the international attributes, NAAL for the national ones, so depending on the +* type of encoding, different fields must be checked to fetch in which way the +* source string is encoded. +* 0: the type of endoding is for international attributes +* 1: the type of endoding is for national attributes +* +* @param[out] +* +* @return: - the output string recoded to UTF-8 or left unchanged if no valid recoding applicable. The +* recodinf relies on GDAL functions appropriately called, which allocate themselves the +* necessary memory to hold the recoded string. +* NOTE: Aall variable is currently not used. +***************************************************************************************************************/ +char *S57Reader::RecodeByDSSI(const char *SourceString, bool LookAtAALL_NALL) +{ + char *RecodedString = NULL; + + if(needAallNallSetup==true) + { + OGRFeature *dsidFeature=ReadDSID(); + if( dsidFeature == NULL ) + return CPLStrdup(SourceString); + Aall=dsidFeature->GetFieldAsInteger("DSSI_AALL"); + Nall=dsidFeature->GetFieldAsInteger("DSSI_NALL"); + CPLDebug("S57", "DSSI_AALL = %d, DSSI_NALL = %d", Aall, Nall); + needAallNallSetup=false; + delete dsidFeature; + } + + if(!LookAtAALL_NALL) + { + //in case of international attributes, only ISO8859-1 code page is used (standard ascii). The result + //is identical to the source string if it contains 0..127 ascii code (LL0), can sligthly differ if + //it contains diacritics 0..255 ascii codes (LL1) + RecodedString = CPLRecode(SourceString,CPL_ENC_ISO8859_1,CPL_ENC_UTF8); + } + else + { + if(Nall==2) //national string encoded in UCS-2 + { + GByte* pabyStr = (GByte*)SourceString; + + /* Count the number of characters */ + int i=0; + while( ! ((pabyStr[2 * i] == DDF_UNIT_TERMINATOR && pabyStr[2 * i + 1] == 0) || + (pabyStr[2 * i] == 0 && pabyStr[2 * i + 1] == 0)) ) + i++; + + wchar_t *wideString = (wchar_t*) CPLMalloc((i+1) * sizeof(wchar_t)); + i = 0; + int bLittleEndian = TRUE; + + /* Skip BOM */ + if( pabyStr[0] == 0xFF && pabyStr[1] == 0xFE ) + i ++; + else if( pabyStr[0] == 0xFE && pabyStr[1] == 0xFF ) + { + bLittleEndian = FALSE; + i ++; + } + + int j=0; + while( ! ((pabyStr[2 * i] == DDF_UNIT_TERMINATOR && pabyStr[2 * i + 1] == 0) || + (pabyStr[2 * i] == 0 && pabyStr[2 * i + 1] == 0)) ) + { + if( bLittleEndian ) + wideString[j++] = pabyStr[i * 2] | (pabyStr[i * 2 + 1] << 8); + else + wideString[j++] = pabyStr[i * 2 + 1] | (pabyStr[i * 2] << 8); + i++; + } + wideString[j] = 0; + RecodedString = CPLRecodeFromWChar(wideString,CPL_ENC_UCS2,CPL_ENC_UTF8); + CPLFree(wideString); + } + else //national string encoded as ISO8859-1 (see comment for above on LL0/LL1) + { + RecodedString = CPLRecode(SourceString,CPL_ENC_ISO8859_1,CPL_ENC_UTF8); + } + } + + if( RecodedString == NULL ) + RecodedString = CPLStrdup(SourceString); + + return(RecodedString); +} + +/************************************************************************/ +/* S57Reader() */ +/************************************************************************/ + +S57Reader::S57Reader( const char * pszFilename ) + +{ + pszModuleName = CPLStrdup( pszFilename ); + pszDSNM = NULL; + + poModule = NULL; + + nFDefnCount = 0; + papoFDefnList = NULL; + + nCOMF = 1000000; + nSOMF = 10; + + poRegistrar = NULL; + poClassContentExplorer = NULL; + bFileIngested = FALSE; + + nNextFEIndex = 0; + nNextVIIndex = 0; + nNextVCIndex = 0; + nNextVEIndex = 0; + nNextVFIndex = 0; + nNextDSIDIndex = 0; + + poDSIDRecord = NULL; + poDSPMRecord = NULL; + szUPDNUpdate[0] = '\0'; + + iPointOffset = 0; + poMultiPoint = NULL; + + papszOptions = NULL; + + nOptionFlags = S57M_UPDATES; + + bMissingWarningIssued = FALSE; + bAttrWarningIssued = FALSE; + + Aall=0; // see RecodeByDSSI() function + Nall=0; // see RecodeByDSSI() function + needAallNallSetup=true; // see RecodeByDSSI() function + +} + +/************************************************************************/ +/* ~S57Reader() */ +/************************************************************************/ + +S57Reader::~S57Reader() + +{ + Close(); + + CPLFree( pszModuleName ); + CSLDestroy( papszOptions ); + + CPLFree( papoFDefnList ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int S57Reader::Open( int bTestOpen ) + +{ + if( poModule != NULL ) + { + Rewind(); + return TRUE; + } + + poModule = new DDFModule(); + if( !poModule->Open( pszModuleName ) ) + { + // notdef: test bTestOpen. + delete poModule; + poModule = NULL; + return FALSE; + } + + // note that the following won't work for catalogs. + if( poModule->FindFieldDefn("DSID") == NULL ) + { + if( !bTestOpen ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s is an ISO8211 file, but not an S-57 data file.\n", + pszModuleName ); + } + delete poModule; + poModule = NULL; + return FALSE; + } + + // Make sure the FSPT field is marked as repeating. + DDFFieldDefn *poFSPT = poModule->FindFieldDefn( "FSPT" ); + if( poFSPT != NULL && !poFSPT->IsRepeating() ) + { + CPLDebug( "S57", "Forcing FSPT field to be repeating." ); + poFSPT->SetRepeatingFlag( TRUE ); + } + + nNextFEIndex = 0; + nNextVIIndex = 0; + nNextVCIndex = 0; + nNextVEIndex = 0; + nNextVFIndex = 0; + nNextDSIDIndex = 0; + + return TRUE; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +void S57Reader::Close() + +{ + if( poModule != NULL ) + { + oVI_Index.Clear(); + oVC_Index.Clear(); + oVE_Index.Clear(); + oVF_Index.Clear(); + oFE_Index.Clear(); + + if( poDSIDRecord != NULL ) + { + delete poDSIDRecord; + poDSIDRecord = NULL; + } + if( poDSPMRecord != NULL ) + { + delete poDSPMRecord; + poDSPMRecord = NULL; + } + + ClearPendingMultiPoint(); + + delete poModule; + poModule = NULL; + + bFileIngested = FALSE; + + CPLFree( pszDSNM ); + pszDSNM = NULL; + } +} + +/************************************************************************/ +/* ClearPendingMultiPoint() */ +/************************************************************************/ + +void S57Reader::ClearPendingMultiPoint() + +{ + if( poMultiPoint != NULL ) + { + delete poMultiPoint; + poMultiPoint = NULL; + } +} + +/************************************************************************/ +/* NextPendingMultiPoint() */ +/************************************************************************/ + +OGRFeature *S57Reader::NextPendingMultiPoint() + +{ + CPLAssert( poMultiPoint != NULL ); + CPLAssert( wkbFlatten(poMultiPoint->GetGeometryRef()->getGeometryType()) + == wkbMultiPoint ); + + OGRFeatureDefn *poDefn = poMultiPoint->GetDefnRef(); + OGRFeature *poPoint = new OGRFeature( poDefn ); + OGRMultiPoint *poMPGeom = (OGRMultiPoint *) poMultiPoint->GetGeometryRef(); + OGRPoint *poSrcPoint; + + poPoint->SetFID( poMultiPoint->GetFID() ); + + for( int i = 0; i < poDefn->GetFieldCount(); i++ ) + { + poPoint->SetField( i, poMultiPoint->GetRawFieldRef(i) ); + } + + poSrcPoint = (OGRPoint *) poMPGeom->getGeometryRef( iPointOffset++ ); + poPoint->SetGeometry( poSrcPoint ); + + if( poPoint != NULL && (nOptionFlags & S57M_ADD_SOUNDG_DEPTH) ) + poPoint->SetField( "DEPTH", poSrcPoint->getZ() ); + + if( iPointOffset >= poMPGeom->getNumGeometries() ) + ClearPendingMultiPoint(); + + return poPoint; +} + +/************************************************************************/ +/* SetOptions() */ +/************************************************************************/ + +int S57Reader::SetOptions( char ** papszOptionsIn ) + +{ + const char * pszOptionValue; + + CSLDestroy( papszOptions ); + papszOptions = CSLDuplicate( papszOptionsIn ); + + pszOptionValue = CSLFetchNameValue( papszOptions, S57O_SPLIT_MULTIPOINT ); + if( pszOptionValue != NULL && CSLTestBoolean(pszOptionValue) ) + nOptionFlags |= S57M_SPLIT_MULTIPOINT; + else + nOptionFlags &= ~S57M_SPLIT_MULTIPOINT; + + pszOptionValue = CSLFetchNameValue( papszOptions, S57O_ADD_SOUNDG_DEPTH ); + if( pszOptionValue != NULL && CSLTestBoolean(pszOptionValue) ) + nOptionFlags |= S57M_ADD_SOUNDG_DEPTH; + else + nOptionFlags &= ~S57M_ADD_SOUNDG_DEPTH; + + if( (nOptionFlags & S57M_ADD_SOUNDG_DEPTH) && + !(nOptionFlags & S57M_SPLIT_MULTIPOINT) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Inconsistent options : ADD_SOUNDG_DEPTH should only be enabled if SPLIT_MULTIPOINT is also enabled"); + return FALSE; + } + + pszOptionValue = CSLFetchNameValue( papszOptions, S57O_LNAM_REFS ); + if( pszOptionValue != NULL && CSLTestBoolean(pszOptionValue) ) + nOptionFlags |= S57M_LNAM_REFS; + else + nOptionFlags &= ~S57M_LNAM_REFS; + + pszOptionValue = CSLFetchNameValue( papszOptions, S57O_UPDATES ); + if( pszOptionValue == NULL ) + /* no change */; + else if( pszOptionValue != NULL && !EQUAL(pszOptionValue,"APPLY") ) + nOptionFlags &= ~S57M_UPDATES; + else + nOptionFlags |= S57M_UPDATES; + + pszOptionValue = CSLFetchNameValue(papszOptions, + S57O_PRESERVE_EMPTY_NUMBERS); + if( pszOptionValue != NULL && CSLTestBoolean(pszOptionValue) ) + nOptionFlags |= S57M_PRESERVE_EMPTY_NUMBERS; + else + nOptionFlags &= ~S57M_PRESERVE_EMPTY_NUMBERS; + + pszOptionValue = CSLFetchNameValue( papszOptions, S57O_RETURN_PRIMITIVES ); + if( pszOptionValue != NULL && CSLTestBoolean(pszOptionValue) ) + nOptionFlags |= S57M_RETURN_PRIMITIVES; + else + nOptionFlags &= ~S57M_RETURN_PRIMITIVES; + + pszOptionValue = CSLFetchNameValue( papszOptions, S57O_RETURN_LINKAGES ); + if( pszOptionValue != NULL && CSLTestBoolean(pszOptionValue) ) + nOptionFlags |= S57M_RETURN_LINKAGES; + else + nOptionFlags &= ~S57M_RETURN_LINKAGES; + + pszOptionValue = CSLFetchNameValue( papszOptions, S57O_RETURN_DSID ); + if( pszOptionValue == NULL || CSLTestBoolean(pszOptionValue) ) + nOptionFlags |= S57M_RETURN_DSID; + else + nOptionFlags &= ~S57M_RETURN_DSID; + + pszOptionValue = CSLFetchNameValue( papszOptions, S57O_RECODE_BY_DSSI ); + if( pszOptionValue != NULL && CSLTestBoolean(pszOptionValue) ) + nOptionFlags |= S57M_RECODE_BY_DSSI; + else + nOptionFlags &= ~S57M_RECODE_BY_DSSI; + + return TRUE; +} + +/************************************************************************/ +/* SetClassBased() */ +/************************************************************************/ + +void S57Reader::SetClassBased( S57ClassRegistrar * poReg, + S57ClassContentExplorer* poClassContentExplorerIn ) + +{ + poRegistrar = poReg; + poClassContentExplorer = poClassContentExplorerIn; +} + +/************************************************************************/ +/* Rewind() */ +/************************************************************************/ + +void S57Reader::Rewind() + +{ + ClearPendingMultiPoint(); + nNextFEIndex = 0; + nNextVIIndex = 0; + nNextVCIndex = 0; + nNextVEIndex = 0; + nNextVFIndex = 0; + nNextDSIDIndex = 0; +} + +/************************************************************************/ +/* Ingest() */ +/* */ +/* Read all the records into memory, adding to the appropriate */ +/* indexes. */ +/************************************************************************/ + +int S57Reader::Ingest() + +{ + DDFRecord *poRecord; + + if( poModule == NULL || bFileIngested ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Read all the records in the module, and place them in */ +/* appropriate indexes. */ +/* -------------------------------------------------------------------- */ + CPLErrorReset(); + while( (poRecord = poModule->ReadRecord()) != NULL ) + { + DDFField *poKeyField = poRecord->GetField(1); + if (poKeyField == NULL) + return FALSE; + + if( EQUAL(poKeyField->GetFieldDefn()->GetName(),"VRID") ) + { + int nRCNM = poRecord->GetIntSubfield( "VRID",0, "RCNM",0); + int nRCID = poRecord->GetIntSubfield( "VRID",0, "RCID",0); + + switch( nRCNM ) + { + case RCNM_VI: + oVI_Index.AddRecord( nRCID, poRecord->Clone() ); + break; + + case RCNM_VC: + oVC_Index.AddRecord( nRCID, poRecord->Clone() ); + break; + + case RCNM_VE: + oVE_Index.AddRecord( nRCID, poRecord->Clone() ); + break; + + case RCNM_VF: + oVF_Index.AddRecord( nRCID, poRecord->Clone() ); + break; + + default: + CPLError(CE_Failure, CPLE_AppDefined, + "Unhandled value for RCNM ; %d", nRCNM); + break; + } + } + + else if( EQUAL(poKeyField->GetFieldDefn()->GetName(),"FRID") ) + { + int nRCID = poRecord->GetIntSubfield( "FRID",0, "RCID",0); + + oFE_Index.AddRecord( nRCID, poRecord->Clone() ); + } + + else if( EQUAL(poKeyField->GetFieldDefn()->GetName(),"DSID") ) + { + CPLFree( pszDSNM ); + pszDSNM = + CPLStrdup(poRecord->GetStringSubfield( "DSID", 0, "DSNM", 0 )); + + if( nOptionFlags & S57M_RETURN_DSID ) + { + if( poDSIDRecord != NULL ) + delete poDSIDRecord; + + poDSIDRecord = poRecord->Clone(); + } + } + + else if( EQUAL(poKeyField->GetFieldDefn()->GetName(),"DSPM") ) + { + nCOMF = MAX(1,poRecord->GetIntSubfield( "DSPM",0, "COMF",0)); + nSOMF = MAX(1,poRecord->GetIntSubfield( "DSPM",0, "SOMF",0)); + + if( nOptionFlags & S57M_RETURN_DSID ) + { + if( poDSPMRecord != NULL ) + delete poDSPMRecord; + + poDSPMRecord = poRecord->Clone(); + } + } + + else + { + CPLDebug( "S57", + "Skipping %s record in S57Reader::Ingest().\n", + poKeyField->GetFieldDefn()->GetName() ); + } + } + + if( CPLGetLastErrorType() == CE_Failure ) + return FALSE; + + bFileIngested = TRUE; + +/* -------------------------------------------------------------------- */ +/* If update support is enabled, read and apply them. */ +/* -------------------------------------------------------------------- */ + if( nOptionFlags & S57M_UPDATES ) + return FindAndApplyUpdates(); + else + return TRUE; +} + +/************************************************************************/ +/* SetNextFEIndex() */ +/************************************************************************/ + +void S57Reader::SetNextFEIndex( int nNewIndex, int nRCNM ) + +{ + if( nRCNM == RCNM_VI ) + nNextVIIndex = nNewIndex; + else if( nRCNM == RCNM_VC ) + nNextVCIndex = nNewIndex; + else if( nRCNM == RCNM_VE ) + nNextVEIndex = nNewIndex; + else if( nRCNM == RCNM_VF ) + nNextVFIndex = nNewIndex; + else if( nRCNM == RCNM_DSID ) + nNextDSIDIndex = nNewIndex; + else + { + if( nNextFEIndex != nNewIndex ) + ClearPendingMultiPoint(); + + nNextFEIndex = nNewIndex; + } +} + +/************************************************************************/ +/* GetNextFEIndex() */ +/************************************************************************/ + +int S57Reader::GetNextFEIndex( int nRCNM ) + +{ + if( nRCNM == RCNM_VI ) + return nNextVIIndex; + else if( nRCNM == RCNM_VC ) + return nNextVCIndex; + else if( nRCNM == RCNM_VE ) + return nNextVEIndex; + else if( nRCNM == RCNM_VF ) + return nNextVFIndex; + else if( nRCNM == RCNM_DSID ) + return nNextDSIDIndex; + else + return nNextFEIndex; +} + +/************************************************************************/ +/* ReadNextFeature() */ +/************************************************************************/ + +OGRFeature * S57Reader::ReadNextFeature( OGRFeatureDefn * poTarget ) + +{ + if( !bFileIngested && !Ingest() ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Special case for "in progress" multipoints being split up. */ +/* -------------------------------------------------------------------- */ + if( poMultiPoint != NULL ) + { + if( poTarget == NULL || poTarget == poMultiPoint->GetDefnRef() ) + { + return NextPendingMultiPoint(); + } + else + { + ClearPendingMultiPoint(); + } + } + +/* -------------------------------------------------------------------- */ +/* Next vector feature? */ +/* -------------------------------------------------------------------- */ + if( (nOptionFlags & S57M_RETURN_DSID) + && nNextDSIDIndex == 0 + && (poTarget == NULL || EQUAL(poTarget->GetName(),"DSID")) ) + { + return ReadDSID(); + } + +/* -------------------------------------------------------------------- */ +/* Next vector feature? */ +/* -------------------------------------------------------------------- */ + if( nOptionFlags & S57M_RETURN_PRIMITIVES ) + { + int nRCNM = 0; + int *pnCounter = NULL; + + if( poTarget == NULL ) + { + if( nNextVIIndex < oVI_Index.GetCount() ) + { + nRCNM = RCNM_VI; + pnCounter = &nNextVIIndex; + } + else if( nNextVCIndex < oVC_Index.GetCount() ) + { + nRCNM = RCNM_VC; + pnCounter = &nNextVCIndex; + } + else if( nNextVEIndex < oVE_Index.GetCount() ) + { + nRCNM = RCNM_VE; + pnCounter = &nNextVEIndex; + } + else if( nNextVFIndex < oVF_Index.GetCount() ) + { + nRCNM = RCNM_VF; + pnCounter = &nNextVFIndex; + } + } + else + { + if( EQUAL(poTarget->GetName(),OGRN_VI) ) + { + nRCNM = RCNM_VI; + pnCounter = &nNextVIIndex; + } + else if( EQUAL(poTarget->GetName(),OGRN_VC) ) + { + nRCNM = RCNM_VC; + pnCounter = &nNextVCIndex; + } + else if( EQUAL(poTarget->GetName(),OGRN_VE) ) + { + nRCNM = RCNM_VE; + pnCounter = &nNextVEIndex; + } + else if( EQUAL(poTarget->GetName(),OGRN_VF) ) + { + nRCNM = RCNM_VF; + pnCounter = &nNextVFIndex; + } + } + + if( nRCNM != 0 ) + { + OGRFeature *poFeature = ReadVector( *pnCounter, nRCNM ); + if( poFeature != NULL ) + { + *pnCounter += 1; + return poFeature; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Next feature. */ +/* -------------------------------------------------------------------- */ + while( nNextFEIndex < oFE_Index.GetCount() ) + { + OGRFeature *poFeature; + OGRFeatureDefn *poFeatureDefn; + + poFeatureDefn = (OGRFeatureDefn *) + oFE_Index.GetClientInfoByIndex( nNextFEIndex ); + + if( poFeatureDefn == NULL ) + { + poFeatureDefn = FindFDefn( oFE_Index.GetByIndex( nNextFEIndex ) ); + oFE_Index.SetClientInfoByIndex( nNextFEIndex, poFeatureDefn ); + } + + if( poFeatureDefn != poTarget && poTarget != NULL ) + { + nNextFEIndex++; + continue; + } + + poFeature = ReadFeature( nNextFEIndex++, poTarget ); + if( poFeature != NULL ) + { + if( (nOptionFlags & S57M_SPLIT_MULTIPOINT) + && poFeature->GetGeometryRef() != NULL + && wkbFlatten(poFeature->GetGeometryRef()->getGeometryType()) + == wkbMultiPoint) + { + poMultiPoint = poFeature; + iPointOffset = 0; + return NextPendingMultiPoint(); + } + + return poFeature; + } + } + + return NULL; +} + +/************************************************************************/ +/* ReadFeature() */ +/* */ +/* Read the features who's id is provided. */ +/************************************************************************/ + +OGRFeature *S57Reader::ReadFeature( int nFeatureId, OGRFeatureDefn *poTarget ) + +{ + OGRFeature *poFeature; + + if( nFeatureId < 0 || nFeatureId >= oFE_Index.GetCount() ) + return NULL; + + if( (nOptionFlags & S57M_RETURN_DSID) + && nFeatureId == 0 + && (poTarget == NULL || EQUAL(poTarget->GetName(),"DSID")) ) + { + poFeature = ReadDSID(); + } + else + { + poFeature = AssembleFeature( oFE_Index.GetByIndex(nFeatureId), + poTarget ); + } + if( poFeature != NULL ) + poFeature->SetFID( nFeatureId ); + + return poFeature; +} + + +/************************************************************************/ +/* AssembleFeature() */ +/* */ +/* Assemble an OGR feature based on a feature record. */ +/************************************************************************/ + +OGRFeature *S57Reader::AssembleFeature( DDFRecord * poRecord, + OGRFeatureDefn * poTarget ) + +{ + int nPRIM, nOBJL; + OGRFeatureDefn *poFDefn; + +/* -------------------------------------------------------------------- */ +/* Find the feature definition to use. Currently this is based */ +/* on the primitive, but eventually this should be based on the */ +/* object class (FRID.OBJL) in some cases, and the primitive in */ +/* others. */ +/* -------------------------------------------------------------------- */ + poFDefn = FindFDefn( poRecord ); + if( poFDefn == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Does this match our target feature definition? If not skip */ +/* this feature. */ +/* -------------------------------------------------------------------- */ + if( poTarget != NULL && poFDefn != poTarget ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the new feature object. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poFeature = new OGRFeature( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Assign a few standard feature attribues. */ +/* -------------------------------------------------------------------- */ + nOBJL = poRecord->GetIntSubfield( "FRID", 0, "OBJL", 0 ); + poFeature->SetField( "OBJL", nOBJL ); + + poFeature->SetField( "RCID", + poRecord->GetIntSubfield( "FRID", 0, "RCID", 0 )); + poFeature->SetField( "PRIM", + poRecord->GetIntSubfield( "FRID", 0, "PRIM", 0 )); + poFeature->SetField( "GRUP", + poRecord->GetIntSubfield( "FRID", 0, "GRUP", 0 )); + poFeature->SetField( "RVER", + poRecord->GetIntSubfield( "FRID", 0, "RVER", 0 )); + poFeature->SetField( "AGEN", + poRecord->GetIntSubfield( "FOID", 0, "AGEN", 0 )); + poFeature->SetField( "FIDN", + poRecord->GetIntSubfield( "FOID", 0, "FIDN", 0 )); + poFeature->SetField( "FIDS", + poRecord->GetIntSubfield( "FOID", 0, "FIDS", 0 )); + +/* -------------------------------------------------------------------- */ +/* Generate long name, if requested. */ +/* -------------------------------------------------------------------- */ + if( nOptionFlags & S57M_LNAM_REFS ) + { + GenerateLNAMAndRefs( poRecord, poFeature ); + } + +/* -------------------------------------------------------------------- */ +/* Generate primitive references if requested. */ +/* -------------------------------------------------------------------- */ + if( nOptionFlags & S57M_RETURN_LINKAGES ) + GenerateFSPTAttributes( poRecord, poFeature ); + +/* -------------------------------------------------------------------- */ +/* Apply object class specific attributes, if supported. */ +/* -------------------------------------------------------------------- */ + if( poRegistrar != NULL ) + ApplyObjectClassAttributes( poRecord, poFeature ); + +/* -------------------------------------------------------------------- */ +/* Find and assign spatial component. */ +/* -------------------------------------------------------------------- */ + nPRIM = poRecord->GetIntSubfield( "FRID", 0, "PRIM", 0 ); + + if( nPRIM == PRIM_P ) + { + if( nOBJL == 129 ) /* SOUNDG */ + AssembleSoundingGeometry( poRecord, poFeature ); + else + AssemblePointGeometry( poRecord, poFeature ); + } + else if( nPRIM == PRIM_L ) + { + AssembleLineGeometry( poRecord, poFeature ); + } + else if( nPRIM == PRIM_A ) + { + AssembleAreaGeometry( poRecord, poFeature ); + } + + return poFeature; +} + +/************************************************************************/ +/* ApplyObjectClassAttributes() */ +/************************************************************************/ + +void S57Reader::ApplyObjectClassAttributes( DDFRecord * poRecord, + OGRFeature * poFeature ) + +{ +/* -------------------------------------------------------------------- */ +/* ATTF Attributes */ +/* -------------------------------------------------------------------- */ + DDFField *poATTF = poRecord->FindField( "ATTF" ); + int nAttrCount, iAttr; + + if( poATTF == NULL ) + return; + + nAttrCount = poATTF->GetRepeatCount(); + for( iAttr = 0; iAttr < nAttrCount; iAttr++ ) + { + int nAttrId = poRecord->GetIntSubfield("ATTF",0,"ATTL",iAttr); + + if( poRegistrar->GetAttrInfo(nAttrId) == NULL ) + { + if( !bAttrWarningIssued ) + { + bAttrWarningIssued = TRUE; + CPLError( CE_Warning, CPLE_AppDefined, + "Illegal feature attribute id (ATTF:ATTL[%d]) of %d\n" + "on feature FIDN=%d, FIDS=%d.\n" + "Skipping attribute, no more warnings will be issued.", + iAttr, nAttrId, + poFeature->GetFieldAsInteger( "FIDN" ), + poFeature->GetFieldAsInteger( "FIDS" ) ); + } + + continue; + } + + /* Fetch the attribute value */ + const char *pszValue; + pszValue = poRecord->GetStringSubfield("ATTF",0,"ATVL",iAttr); + if( pszValue == NULL ) + return; + + //If needed, recode the string in UTF-8. + char* pszValueToFree = NULL; + if(nOptionFlags & S57M_RECODE_BY_DSSI) + pszValue = pszValueToFree = RecodeByDSSI(pszValue,false); + + /* Apply to feature in an appropriate way */ + int iField; + OGRFieldDefn *poFldDefn; + + const char *pszAcronym = poRegistrar->GetAttrAcronym(nAttrId); + iField = poFeature->GetDefnRef()->GetFieldIndex(pszAcronym); + if( iField < 0 ) + { + if( !bMissingWarningIssued ) + { + bMissingWarningIssued = TRUE; + CPLError( CE_Warning, CPLE_AppDefined, + "Attributes %s ignored, not in expected schema.\n" + "No more warnings will be issued for this dataset.", + pszAcronym ); + } + CPLFree(pszValueToFree); + continue; + } + + poFldDefn = poFeature->GetDefnRef()->GetFieldDefn( iField ); + if( poFldDefn->GetType() == OFTInteger + || poFldDefn->GetType() == OFTReal ) + { + if( strlen(pszValue) == 0 ) + { + if( nOptionFlags & S57M_PRESERVE_EMPTY_NUMBERS ) + poFeature->SetField( iField, EMPTY_NUMBER_MARKER ); + else + { + /* leave as null if value was empty string */ + } + } + else + poFeature->SetField( iField, pszValue ); + } + else + poFeature->SetField( iField, pszValue ); + + CPLFree(pszValueToFree); + } + +/* -------------------------------------------------------------------- */ +/* NATF (national) attributes */ +/* -------------------------------------------------------------------- */ + DDFField *poNATF = poRecord->FindField( "NATF" ); + + if( poNATF == NULL ) + return; + + nAttrCount = poNATF->GetRepeatCount(); + for( iAttr = 0; iAttr < nAttrCount; iAttr++ ) + { + int nAttrId = poRecord->GetIntSubfield("NATF",0,"ATTL",iAttr); + const char *pszAcronym = poRegistrar->GetAttrAcronym(nAttrId); + + if( pszAcronym == NULL ) + { + static int bAttrWarningIssued = FALSE; + + if( !bAttrWarningIssued ) + { + bAttrWarningIssued = TRUE; + CPLError( CE_Warning, CPLE_AppDefined, + "Illegal feature attribute id (NATF:ATTL[%d]) of %d\n" + "on feature FIDN=%d, FIDS=%d.\n" + "Skipping attribute, no more warnings will be issued.", + iAttr, nAttrId, + poFeature->GetFieldAsInteger( "FIDN" ), + poFeature->GetFieldAsInteger( "FIDS" ) ); + } + + continue; + } + + //If needed, recode the string in UTF-8. + const char *pszValue = poRecord->GetStringSubfield("NATF",0,"ATVL",iAttr); + if( pszValue != NULL ) + { + if(nOptionFlags & S57M_RECODE_BY_DSSI) + { + char* pszValueRecoded = RecodeByDSSI(pszValue,true); + poFeature->SetField(pszAcronym,pszValueRecoded); + CPLFree(pszValueRecoded); + } + else + poFeature->SetField(pszAcronym,pszValue); + } + } +} + +/************************************************************************/ +/* GenerateLNAMAndRefs() */ +/************************************************************************/ + +void S57Reader::GenerateLNAMAndRefs( DDFRecord * poRecord, + OGRFeature * poFeature ) + +{ + char szLNAM[32]; + +/* -------------------------------------------------------------------- */ +/* Apply the LNAM to the object. */ +/* -------------------------------------------------------------------- */ + sprintf( szLNAM, "%04X%08X%04X", + poFeature->GetFieldAsInteger( "AGEN" ), + poFeature->GetFieldAsInteger( "FIDN" ), + poFeature->GetFieldAsInteger( "FIDS" ) ); + poFeature->SetField( "LNAM", szLNAM ); + +/* -------------------------------------------------------------------- */ +/* Do we have references to other features. */ +/* -------------------------------------------------------------------- */ + DDFField *poFFPT; + + poFFPT = poRecord->FindField( "FFPT" ); + + if( poFFPT == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Apply references. */ +/* -------------------------------------------------------------------- */ + int nRefCount = poFFPT->GetRepeatCount(); + DDFSubfieldDefn *poLNAM, *poRIND; + char **papszRefs = NULL; + int *panRIND = (int *) CPLMalloc(sizeof(int) * nRefCount); + + poLNAM = poFFPT->GetFieldDefn()->FindSubfieldDefn( "LNAM" ); + poRIND = poFFPT->GetFieldDefn()->FindSubfieldDefn( "RIND" ); + if( poLNAM == NULL || poRIND == NULL ) + return; + + for( int iRef = 0; iRef < nRefCount; iRef++ ) + { + unsigned char *pabyData; + int nMaxBytes; + + pabyData = (unsigned char *) + poFFPT->GetSubfieldData( poLNAM, &nMaxBytes, iRef ); + if( pabyData == NULL || nMaxBytes < 8 ) + { + CSLDestroy( papszRefs ); + CPLFree( panRIND ); + return; + } + + sprintf( szLNAM, "%02X%02X%02X%02X%02X%02X%02X%02X", + pabyData[1], pabyData[0], /* AGEN */ + pabyData[5], pabyData[4], pabyData[3], pabyData[2], /* FIDN */ + pabyData[7], pabyData[6] ); + + papszRefs = CSLAddString( papszRefs, szLNAM ); + + pabyData = (unsigned char *) + poFFPT->GetSubfieldData( poRIND, &nMaxBytes, iRef ); + if( pabyData == NULL || nMaxBytes < 1 ) + { + CSLDestroy( papszRefs ); + CPLFree( panRIND ); + return; + } + panRIND[iRef] = pabyData[0]; + } + + poFeature->SetField( "LNAM_REFS", papszRefs ); + CSLDestroy( papszRefs ); + + poFeature->SetField( "FFPT_RIND", nRefCount, panRIND ); + CPLFree( panRIND ); +} + +/************************************************************************/ +/* GenerateFSPTAttributes() */ +/************************************************************************/ + +void S57Reader::GenerateFSPTAttributes( DDFRecord * poRecord, + OGRFeature * poFeature ) + +{ +/* -------------------------------------------------------------------- */ +/* Feature the spatial record containing the point. */ +/* -------------------------------------------------------------------- */ + DDFField *poFSPT; + int nCount, i; + + poFSPT = poRecord->FindField( "FSPT" ); + if( poFSPT == NULL ) + return; + + nCount = poFSPT->GetRepeatCount(); + +/* -------------------------------------------------------------------- */ +/* Allocate working lists of the attributes. */ +/* -------------------------------------------------------------------- */ + int *panORNT, *panUSAG, *panMASK, *panRCNM, *panRCID; + + panORNT = (int *) CPLMalloc( sizeof(int) * nCount ); + panUSAG = (int *) CPLMalloc( sizeof(int) * nCount ); + panMASK = (int *) CPLMalloc( sizeof(int) * nCount ); + panRCNM = (int *) CPLMalloc( sizeof(int) * nCount ); + panRCID = (int *) CPLMalloc( sizeof(int) * nCount ); + +/* -------------------------------------------------------------------- */ +/* loop over all entries, decoding them. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nCount; i++ ) + { + panRCID[i] = ParseName( poFSPT, i, panRCNM + i ); + panORNT[i] = poRecord->GetIntSubfield( "FSPT", 0, "ORNT",i); + panUSAG[i] = poRecord->GetIntSubfield( "FSPT", 0, "USAG",i); + panMASK[i] = poRecord->GetIntSubfield( "FSPT", 0, "MASK",i); + } + +/* -------------------------------------------------------------------- */ +/* Assign to feature. */ +/* -------------------------------------------------------------------- */ + poFeature->SetField( "NAME_RCNM", nCount, panRCNM ); + poFeature->SetField( "NAME_RCID", nCount, panRCID ); + poFeature->SetField( "ORNT", nCount, panORNT ); + poFeature->SetField( "USAG", nCount, panUSAG ); + poFeature->SetField( "MASK", nCount, panMASK ); + +/* -------------------------------------------------------------------- */ +/* Cleanup. */ +/* -------------------------------------------------------------------- */ + CPLFree( panRCNM ); + CPLFree( panRCID ); + CPLFree( panORNT ); + CPLFree( panUSAG ); + CPLFree( panMASK ); +} + +/************************************************************************/ +/* ReadDSID() */ +/************************************************************************/ + +OGRFeature *S57Reader::ReadDSID() + +{ + if( poDSIDRecord == NULL && poDSPMRecord == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Find the feature definition to use. */ +/* -------------------------------------------------------------------- */ + OGRFeatureDefn *poFDefn = NULL; + + for( int i = 0; i < nFDefnCount; i++ ) + { + if( EQUAL(papoFDefnList[i]->GetName(),"DSID") ) + { + poFDefn = papoFDefnList[i]; + break; + } + } + + if( poFDefn == NULL ) + { + //CPLAssert( FALSE ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create feature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = new OGRFeature( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Apply DSID values. */ +/* -------------------------------------------------------------------- */ + if( poDSIDRecord != NULL ) + { + poFeature->SetField( "DSID_EXPP", + poDSIDRecord->GetIntSubfield( "DSID", 0, "EXPP", 0 )); + poFeature->SetField( "DSID_INTU", + poDSIDRecord->GetIntSubfield( "DSID", 0, "INTU", 0 )); + poFeature->SetField( "DSID_DSNM", + poDSIDRecord->GetStringSubfield( "DSID", 0, "DSNM", 0 )); + poFeature->SetField( "DSID_EDTN", + poDSIDRecord->GetStringSubfield( "DSID", 0, "EDTN", 0 )); + if( strlen(szUPDNUpdate) > 0 ) + poFeature->SetField( "DSID_UPDN", szUPDNUpdate ); + else + poFeature->SetField( "DSID_UPDN", + poDSIDRecord->GetStringSubfield( "DSID", 0, "UPDN", 0 )); + + poFeature->SetField( "DSID_UADT", + poDSIDRecord->GetStringSubfield( "DSID", 0, "UADT", 0 )); + poFeature->SetField( "DSID_ISDT", + poDSIDRecord->GetStringSubfield( "DSID", 0, "ISDT", 0 )); + poFeature->SetField( "DSID_STED", + poDSIDRecord->GetFloatSubfield( "DSID", 0, "STED", 0 )); + poFeature->SetField( "DSID_PRSP", + poDSIDRecord->GetIntSubfield( "DSID", 0, "PRSP", 0 )); + poFeature->SetField( "DSID_PSDN", + poDSIDRecord->GetStringSubfield( "DSID", 0, "PSDN", 0 )); + poFeature->SetField( "DSID_PRED", + poDSIDRecord->GetStringSubfield( "DSID", 0, "PRED", 0 )); + poFeature->SetField( "DSID_PROF", + poDSIDRecord->GetIntSubfield( "DSID", 0, "PROF", 0 )); + poFeature->SetField( "DSID_AGEN", + poDSIDRecord->GetIntSubfield( "DSID", 0, "AGEN", 0 )); + poFeature->SetField( "DSID_COMT", + poDSIDRecord->GetStringSubfield( "DSID", 0, "COMT", 0 )); + +/* -------------------------------------------------------------------- */ +/* Apply DSSI values. */ +/* -------------------------------------------------------------------- */ + poFeature->SetField( "DSSI_DSTR", + poDSIDRecord->GetIntSubfield( "DSSI", 0, "DSTR", 0 )); + poFeature->SetField( "DSSI_AALL", + poDSIDRecord->GetIntSubfield( "DSSI", 0, "AALL", 0 )); + poFeature->SetField( "DSSI_NALL", + poDSIDRecord->GetIntSubfield( "DSSI", 0, "NALL", 0 )); + poFeature->SetField( "DSSI_NOMR", + poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOMR", 0 )); + poFeature->SetField( "DSSI_NOCR", + poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOCR", 0 )); + poFeature->SetField( "DSSI_NOGR", + poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOGR", 0 )); + poFeature->SetField( "DSSI_NOLR", + poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOLR", 0 )); + poFeature->SetField( "DSSI_NOIN", + poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOIN", 0 )); + poFeature->SetField( "DSSI_NOCN", + poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOCN", 0 )); + poFeature->SetField( "DSSI_NOED", + poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOED", 0 )); + poFeature->SetField( "DSSI_NOFA", + poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOFA", 0 )); + } + +/* -------------------------------------------------------------------- */ +/* Apply DSPM record. */ +/* -------------------------------------------------------------------- */ + if( poDSPMRecord != NULL ) + { + poFeature->SetField( "DSPM_HDAT", + poDSPMRecord->GetIntSubfield( "DSPM", 0, "HDAT", 0 )); + poFeature->SetField( "DSPM_VDAT", + poDSPMRecord->GetIntSubfield( "DSPM", 0, "VDAT", 0 )); + poFeature->SetField( "DSPM_SDAT", + poDSPMRecord->GetIntSubfield( "DSPM", 0, "SDAT", 0 )); + poFeature->SetField( "DSPM_CSCL", + poDSPMRecord->GetIntSubfield( "DSPM", 0, "CSCL", 0 )); + poFeature->SetField( "DSPM_DUNI", + poDSPMRecord->GetIntSubfield( "DSPM", 0, "DUNI", 0 )); + poFeature->SetField( "DSPM_HUNI", + poDSPMRecord->GetIntSubfield( "DSPM", 0, "HUNI", 0 )); + poFeature->SetField( "DSPM_PUNI", + poDSPMRecord->GetIntSubfield( "DSPM", 0, "PUNI", 0 )); + poFeature->SetField( "DSPM_COUN", + poDSPMRecord->GetIntSubfield( "DSPM", 0, "COUN", 0 )); + poFeature->SetField( "DSPM_COMF", + poDSPMRecord->GetIntSubfield( "DSPM", 0, "COMF", 0 )); + poFeature->SetField( "DSPM_SOMF", + poDSPMRecord->GetIntSubfield( "DSPM", 0, "SOMF", 0 )); + poFeature->SetField( "DSPM_COMT", + poDSPMRecord->GetStringSubfield( "DSPM", 0, "COMT", 0 )); + } + + poFeature->SetFID( nNextDSIDIndex++ ); + + return poFeature; +} + + +/************************************************************************/ +/* ReadVector() */ +/* */ +/* Read a vector primitive objects based on the type (RCNM_) */ +/* and index within the related index. */ +/************************************************************************/ + +OGRFeature *S57Reader::ReadVector( int nFeatureId, int nRCNM ) + +{ + DDFRecordIndex *poIndex; + const char *pszFDName = NULL; + +/* -------------------------------------------------------------------- */ +/* What type of vector are we fetching. */ +/* -------------------------------------------------------------------- */ + switch( nRCNM ) + { + case RCNM_VI: + poIndex = &oVI_Index; + pszFDName = OGRN_VI; + break; + + case RCNM_VC: + poIndex = &oVC_Index; + pszFDName = OGRN_VC; + break; + + case RCNM_VE: + poIndex = &oVE_Index; + pszFDName = OGRN_VE; + break; + + case RCNM_VF: + poIndex = &oVF_Index; + pszFDName = OGRN_VF; + break; + + default: + CPLAssert( FALSE ); + return NULL; + } + + if( nFeatureId < 0 || nFeatureId >= poIndex->GetCount() ) + return NULL; + + DDFRecord *poRecord = poIndex->GetByIndex( nFeatureId ); + +/* -------------------------------------------------------------------- */ +/* Find the feature definition to use. */ +/* -------------------------------------------------------------------- */ + OGRFeatureDefn *poFDefn = NULL; + + for( int i = 0; i < nFDefnCount; i++ ) + { + if( EQUAL(papoFDefnList[i]->GetName(),pszFDName) ) + { + poFDefn = papoFDefnList[i]; + break; + } + } + + if( poFDefn == NULL ) + { + //CPLAssert( FALSE ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create feature, and assign standard fields. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = new OGRFeature( poFDefn ); + + poFeature->SetFID( nFeatureId ); + + poFeature->SetField( "RCNM", + poRecord->GetIntSubfield( "VRID", 0, "RCNM",0) ); + poFeature->SetField( "RCID", + poRecord->GetIntSubfield( "VRID", 0, "RCID",0) ); + poFeature->SetField( "RVER", + poRecord->GetIntSubfield( "VRID", 0, "RVER",0) ); + poFeature->SetField( "RUIN", + poRecord->GetIntSubfield( "VRID", 0, "RUIN",0) ); + +/* -------------------------------------------------------------------- */ +/* Collect point geometries. */ +/* -------------------------------------------------------------------- */ + if( nRCNM == RCNM_VI || nRCNM == RCNM_VC ) + { + double dfX=0.0, dfY=0.0, dfZ=0.0; + + if( poRecord->FindField( "SG2D" ) != NULL ) + { + dfX = poRecord->GetIntSubfield("SG2D",0,"XCOO",0) / (double)nCOMF; + dfY = poRecord->GetIntSubfield("SG2D",0,"YCOO",0) / (double)nCOMF; + poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY ) ); + } + + else if( poRecord->FindField( "SG3D" ) != NULL ) /* presume sounding*/ + { + int i, nVCount = poRecord->FindField("SG3D")->GetRepeatCount(); + if( nVCount == 1 ) + { + dfX =poRecord->GetIntSubfield("SG3D",0,"XCOO",0)/(double)nCOMF; + dfY =poRecord->GetIntSubfield("SG3D",0,"YCOO",0)/(double)nCOMF; + dfZ =poRecord->GetIntSubfield("SG3D",0,"VE3D",0)/(double)nSOMF; + poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY, dfZ )); + } + else + { + OGRMultiPoint *poMP = new OGRMultiPoint(); + + for( i = 0; i < nVCount; i++ ) + { + dfX = poRecord->GetIntSubfield("SG3D",0,"XCOO",i) + / (double)nCOMF; + dfY = poRecord->GetIntSubfield("SG3D",0,"YCOO",i) + / (double)nCOMF; + dfZ = poRecord->GetIntSubfield("SG3D",0,"VE3D",i) + / (double)nSOMF; + + poMP->addGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ) ); + } + + poFeature->SetGeometryDirectly( poMP ); + } + } + + } + +/* -------------------------------------------------------------------- */ +/* Collect an edge geometry. */ +/* -------------------------------------------------------------------- */ + else if( nRCNM == RCNM_VE ) + { + int nPoints = 0; + DDFField *poSG2D; + OGRLineString *poLine = new OGRLineString(); + + for( int iField = 0; iField < poRecord->GetFieldCount(); ++iField ) + { + poSG2D = poRecord->GetField( iField ); + + if( EQUAL(poSG2D->GetFieldDefn()->GetName(), "SG2D") ) + { + int nVCount = poSG2D->GetRepeatCount(); + + poLine->setNumPoints( nPoints + nVCount ); + + for( int i = 0; i < nVCount; ++i ) + { + poLine->setPoint + (nPoints++, + poRecord->GetIntSubfield("SG2D",0,"XCOO",i) / (double)nCOMF, + poRecord->GetIntSubfield("SG2D",0,"YCOO",i) / (double)nCOMF ); + } + } + } + + poFeature->SetGeometryDirectly( poLine ); + } + +/* -------------------------------------------------------------------- */ +/* Special edge fields. */ +/* Allow either 2 VRPT fields or one VRPT field with 2 rows */ +/* -------------------------------------------------------------------- */ + DDFField *poVRPT; + + if( nRCNM == RCNM_VE + && (poVRPT = poRecord->FindField( "VRPT" )) != NULL ) + { + int iField = 0, iSubField = 1; + + poFeature->SetField( "NAME_RCNM_0", RCNM_VC ); + poFeature->SetField( "NAME_RCID_0", ParseName( poVRPT ) ); + poFeature->SetField( "ORNT_0", + poRecord->GetIntSubfield("VRPT",0,"ORNT",0) ); + poFeature->SetField( "USAG_0", + poRecord->GetIntSubfield("VRPT",0,"USAG",0) ); + poFeature->SetField( "TOPI_0", + poRecord->GetIntSubfield("VRPT",0,"TOPI",0) ); + poFeature->SetField( "MASK_0", + poRecord->GetIntSubfield("VRPT",0,"MASK",0) ); + + if( poVRPT != NULL && poVRPT->GetRepeatCount() == 1 ) + { + // Only one row, need a second VRPT field + iField = 1; iSubField = 0; + + if( (poVRPT = poRecord->FindField( "VRPT", iField )) == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to fetch last edge node.\n" + "Feature OBJL=%s, RCID=%d may have corrupt or" + " missing geometry.", + poFeature->GetDefnRef()->GetName(), + poFeature->GetFieldAsInteger( "RCID" ) ); + + return poFeature; + } + } + + poFeature->SetField( "NAME_RCID_1", ParseName( poVRPT, iSubField ) ); + poFeature->SetField( "NAME_RCNM_1", RCNM_VC ); + poFeature->SetField( "ORNT_1", + poRecord->GetIntSubfield("VRPT",iField, + "ORNT",iSubField) ); + poFeature->SetField( "USAG_1", + poRecord->GetIntSubfield("VRPT",iField, + "USAG",iSubField) ); + poFeature->SetField( "TOPI_1", + poRecord->GetIntSubfield("VRPT",iField, + "TOPI",iSubField) ); + poFeature->SetField( "MASK_1", + poRecord->GetIntSubfield("VRPT",iField, + "MASK",iSubField) ); + } + + return poFeature; +} + +/************************************************************************/ +/* FetchPoint() */ +/* */ +/* Fetch the location of a spatial point object. */ +/************************************************************************/ + +int S57Reader::FetchPoint( int nRCNM, int nRCID, + double * pdfX, double * pdfY, double * pdfZ ) + +{ + DDFRecord *poSRecord; + + if( nRCNM == RCNM_VI ) + poSRecord = oVI_Index.FindRecord( nRCID ); + else + poSRecord = oVC_Index.FindRecord( nRCID ); + + if( poSRecord == NULL ) + return FALSE; + + double dfX = 0.0, dfY = 0.0, dfZ = 0.0; + + if( poSRecord->FindField( "SG2D" ) != NULL ) + { + dfX = poSRecord->GetIntSubfield("SG2D",0,"XCOO",0) / (double)nCOMF; + dfY = poSRecord->GetIntSubfield("SG2D",0,"YCOO",0) / (double)nCOMF; + } + else if( poSRecord->FindField( "SG3D" ) != NULL ) + { + dfX = poSRecord->GetIntSubfield("SG3D",0,"XCOO",0) / (double)nCOMF; + dfY = poSRecord->GetIntSubfield("SG3D",0,"YCOO",0) / (double)nCOMF; + dfZ = poSRecord->GetIntSubfield("SG3D",0,"VE3D",0) / (double)nSOMF; + } + else + return FALSE; + + if( pdfX != NULL ) + *pdfX = dfX; + if( pdfY != NULL ) + *pdfY = dfY; + if( pdfZ != NULL ) + *pdfZ = dfZ; + + return TRUE; +} + +/************************************************************************/ +/* S57StrokeArcToOGRGeometry_Angles() */ +/************************************************************************/ + +static OGRLineString * +S57StrokeArcToOGRGeometry_Angles( double dfCenterX, double dfCenterY, + double dfRadius, + double dfStartAngle, double dfEndAngle, + int nVertexCount ) + +{ + OGRLineString *poLine = new OGRLineString; + double dfArcX, dfArcY, dfSlice; + int iPoint; + + nVertexCount = MAX(2,nVertexCount); + dfSlice = (dfEndAngle-dfStartAngle)/(nVertexCount-1); + + poLine->setNumPoints( nVertexCount ); + + for( iPoint=0; iPoint < nVertexCount; iPoint++ ) + { + double dfAngle; + + dfAngle = (dfStartAngle + iPoint * dfSlice) * PI / 180.0; + + dfArcX = dfCenterX + cos(dfAngle) * dfRadius; + dfArcY = dfCenterY + sin(dfAngle) * dfRadius; + + poLine->setPoint( iPoint, dfArcX, dfArcY ); + } + + return poLine; +} + + +/************************************************************************/ +/* S57StrokeArcToOGRGeometry_Points() */ +/************************************************************************/ + +static OGRLineString * +S57StrokeArcToOGRGeometry_Points( double dfStartX, double dfStartY, + double dfCenterX, double dfCenterY, + double dfEndX, double dfEndY, + int nVertexCount ) + +{ + double dfStartAngle, dfEndAngle; + double dfRadius; + + if( dfStartX == dfEndX && dfStartY == dfEndY ) + { + dfStartAngle = 0.0; + dfEndAngle = 360.0; + } + else + { + double dfDeltaX, dfDeltaY; + + dfDeltaX = dfStartX - dfCenterX; + dfDeltaY = dfStartY - dfCenterY; + dfStartAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / PI; + + dfDeltaX = dfEndX - dfCenterX; + dfDeltaY = dfEndY - dfCenterY; + dfEndAngle = atan2(dfDeltaY,dfDeltaX) * 180.0 / PI; + +#ifdef notdef + if( dfStartAngle > dfAlongAngle && dfAlongAngle > dfEndAngle ) + { + double dfTempAngle; + + dfTempAngle = dfStartAngle; + dfStartAngle = dfEndAngle; + dfEndAngle = dfTempAngle; + } +#endif + + while( dfStartAngle < dfEndAngle ) + dfStartAngle += 360.0; + +// while( dfAlongAngle < dfStartAngle ) +// dfAlongAngle += 360.0; + +// while( dfEndAngle < dfAlongAngle ) +// dfEndAngle += 360.0; + + if( dfEndAngle - dfStartAngle > 360.0 ) + { + double dfTempAngle; + + dfTempAngle = dfStartAngle; + dfStartAngle = dfEndAngle; + dfEndAngle = dfTempAngle; + + while( dfEndAngle < dfStartAngle ) + dfStartAngle -= 360.0; + } + } + + dfRadius = sqrt( (dfCenterX - dfStartX) * (dfCenterX - dfStartX) + + (dfCenterY - dfStartY) * (dfCenterY - dfStartY) ); + + return S57StrokeArcToOGRGeometry_Angles( dfCenterX, dfCenterY, + dfRadius, + dfStartAngle, dfEndAngle, + nVertexCount ); +} + +/************************************************************************/ +/* FetchLine() */ +/************************************************************************/ + +int S57Reader::FetchLine( DDFRecord *poSRecord, + int iStartVertex, int iDirection, + OGRLineString *poLine ) + +{ + int nPoints = 0; + DDFField *poSG2D, *poAR2D; + DDFSubfieldDefn *poXCOO=NULL, *poYCOO=NULL; + int bStandardFormat = TRUE; + +/* -------------------------------------------------------------------- */ +/* Points may be multiple rows in one SG2D/AR2D field or */ +/* multiple SG2D/AR2D fields (or a combination of both) */ +/* Iterate over all the SG2D/AR2D fields in the record */ +/* -------------------------------------------------------------------- */ + + for( int iField = 0; iField < poSRecord->GetFieldCount(); ++iField ) + { + poSG2D = poSRecord->GetField( iField ); + + if( EQUAL(poSG2D->GetFieldDefn()->GetName(), "SG2D") ) + { + poAR2D = NULL; + } + else if( EQUAL(poSG2D->GetFieldDefn()->GetName(), "AR2D") ) + { + poAR2D = poSG2D; + } + else + { + /* Other types of fields are skipped */ + continue; + } + +/* -------------------------------------------------------------------- */ +/* Get some basic definitions. */ +/* -------------------------------------------------------------------- */ + + poXCOO = poSG2D->GetFieldDefn()->FindSubfieldDefn("XCOO"); + poYCOO = poSG2D->GetFieldDefn()->FindSubfieldDefn("YCOO"); + + if( poXCOO == NULL || poYCOO == NULL ) + { + CPLDebug( "S57", "XCOO or YCOO are NULL" ); + return FALSE; + } + + int nVCount = poSG2D->GetRepeatCount(); + +/* -------------------------------------------------------------------- */ +/* It is legitimate to have zero vertices for line segments */ +/* that just have the start and end node (bug 840). */ +/* */ +/* This is bogus! nVCount != 0, because poXCOO != 0 here */ +/* In case of zero vertices, there will not be any SG2D fields */ +/* -------------------------------------------------------------------- */ + if( nVCount == 0 ) + continue; + +/* -------------------------------------------------------------------- */ +/* Make sure out line is long enough to hold all the vertices */ +/* we will apply. */ +/* -------------------------------------------------------------------- */ + int nVBase; + + if( iDirection < 0 ) + nVBase = iStartVertex + nPoints + nVCount; + else + nVBase = iStartVertex + nPoints; + + if( poLine->getNumPoints() < iStartVertex + nPoints + nVCount ) + poLine->setNumPoints( iStartVertex + nPoints + nVCount ); + + nPoints += nVCount; +/* -------------------------------------------------------------------- */ +/* Are the SG2D and XCOO/YCOO definitions in the form we expect? */ +/* -------------------------------------------------------------------- */ + bStandardFormat = + (poSG2D->GetFieldDefn()->GetSubfieldCount() == 2) && + EQUAL(poXCOO->GetFormat(),"b24") && + EQUAL(poYCOO->GetFormat(),"b24"); + +/* -------------------------------------------------------------------- */ +/* Collect the vertices: */ +/* */ +/* This approach assumes that the data is LSB organized int32 */ +/* binary data as per the specification. We avoid lots of */ +/* extra calls to low level DDF methods as they are quite */ +/* expensive. */ +/* -------------------------------------------------------------------- */ + if( bStandardFormat ) + { + const char *pachData; + int nBytesRemaining; + + pachData = poSG2D->GetSubfieldData(poYCOO,&nBytesRemaining,0); + + for( int i = 0; i < nVCount; i++ ) + { + double dfX, dfY; + GInt32 nXCOO, nYCOO; + + memcpy( &nYCOO, pachData, 4 ); + pachData += 4; + memcpy( &nXCOO, pachData, 4 ); + pachData += 4; + +#ifdef CPL_MSB + CPL_SWAP32PTR( &nXCOO ); + CPL_SWAP32PTR( &nYCOO ); +#endif + dfX = nXCOO / (double) nCOMF; + dfY = nYCOO / (double) nCOMF; + + poLine->setPoint( nVBase, dfX, dfY ); + + nVBase += iDirection; + } + } + +/* -------------------------------------------------------------------- */ +/* Collect the vertices: */ +/* */ +/* The generic case where we use low level but expensive DDF */ +/* methods to get the data. This should work even if some */ +/* things are changed about the SG2D fields such as making them */ +/* floating point or a different byte order. */ +/* -------------------------------------------------------------------- */ + else + { + for( int i = 0; i < nVCount; i++ ) + { + double dfX, dfY; + const char *pachData; + int nBytesRemaining; + + pachData = poSG2D->GetSubfieldData(poXCOO,&nBytesRemaining,i); + + dfX = poXCOO->ExtractIntData(pachData,nBytesRemaining,NULL) + / (double) nCOMF; + + pachData = poSG2D->GetSubfieldData(poYCOO,&nBytesRemaining,i); + + dfY = poXCOO->ExtractIntData(pachData,nBytesRemaining,NULL) + / (double) nCOMF; + + poLine->setPoint( nVBase, dfX, dfY ); + + nVBase += iDirection; + } + } + +/* -------------------------------------------------------------------- */ +/* If this is actually an arc, turn the start, end and center */ +/* of rotation into a "stroked" arc linestring. */ +/* -------------------------------------------------------------------- */ + if( poAR2D != NULL && poLine->getNumPoints() >= 3 ) + { + OGRLineString *poArc; + int i, iLast = poLine->getNumPoints() - 1; + + poArc = S57StrokeArcToOGRGeometry_Points( + poLine->getX(iLast-0), poLine->getY(iLast-0), + poLine->getX(iLast-1), poLine->getY(iLast-1), + poLine->getX(iLast-2), poLine->getY(iLast-2), + 30 ); + + if( poArc != NULL ) + { + for( i = 0; i < poArc->getNumPoints(); i++ ) + poLine->setPoint( iLast-2+i, poArc->getX(i), poArc->getY(i) ); + + delete poArc; + } + } + } + + return TRUE; +} + +/************************************************************************/ +/* AssemblePointGeometry() */ +/************************************************************************/ + +void S57Reader::AssemblePointGeometry( DDFRecord * poFRecord, + OGRFeature * poFeature ) + +{ + DDFField *poFSPT; + int nRCNM, nRCID; + +/* -------------------------------------------------------------------- */ +/* Feature the spatial record containing the point. */ +/* -------------------------------------------------------------------- */ + poFSPT = poFRecord->FindField( "FSPT" ); + if( poFSPT == NULL ) + return; + + if( poFSPT->GetRepeatCount() != 1 ) + { +#ifdef DEBUG + fprintf( stderr, + "Point features with other than one spatial linkage.\n" ); + poFRecord->Dump( stderr ); +#endif + CPLDebug( "S57", + "Point feature encountered with other than one spatial linkage." ); + } + + nRCID = ParseName( poFSPT, 0, &nRCNM ); + + double dfX = 0.0, dfY = 0.0, dfZ = 0.0; + + if( nRCID == -1 || !FetchPoint( nRCNM, nRCID, &dfX, &dfY, &dfZ ) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Failed to fetch %d/%d point geometry for point feature.\n" + "Feature will have empty geometry.", + nRCNM, nRCID ); + return; + } + + if( dfZ == 0.0 ) + poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY ) ); + else + poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ) ); +} + +/************************************************************************/ +/* AssembleSoundingGeometry() */ +/************************************************************************/ + +void S57Reader::AssembleSoundingGeometry( DDFRecord * poFRecord, + OGRFeature * poFeature ) + +{ + DDFField *poFSPT; + int nRCNM, nRCID; + DDFRecord *poSRecord; + + +/* -------------------------------------------------------------------- */ +/* Feature the spatial record containing the point. */ +/* -------------------------------------------------------------------- */ + poFSPT = poFRecord->FindField( "FSPT" ); + if( poFSPT == NULL ) + return; + + if( poFSPT->GetRepeatCount() != 1 ) + return; + + nRCID = ParseName( poFSPT, 0, &nRCNM ); + + if( nRCNM == RCNM_VI ) + poSRecord = oVI_Index.FindRecord( nRCID ); + else + poSRecord = oVC_Index.FindRecord( nRCID ); + + if( poSRecord == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Extract vertices. */ +/* -------------------------------------------------------------------- */ + OGRMultiPoint *poMP = new OGRMultiPoint(); + DDFField *poField; + int nPointCount, i, nBytesLeft; + DDFSubfieldDefn *poXCOO, *poYCOO, *poVE3D; + const char *pachData; + + poField = poSRecord->FindField( "SG2D" ); + if( poField == NULL ) + poField = poSRecord->FindField( "SG3D" ); + if( poField == NULL ) + return; + + poXCOO = poField->GetFieldDefn()->FindSubfieldDefn( "XCOO" ); + poYCOO = poField->GetFieldDefn()->FindSubfieldDefn( "YCOO" ); + if( poXCOO == NULL || poYCOO == NULL ) + { + CPLDebug( "S57", "XCOO or YCOO are NULL" ); + return; + } + poVE3D = poField->GetFieldDefn()->FindSubfieldDefn( "VE3D" ); + + nPointCount = poField->GetRepeatCount(); + + pachData = poField->GetData(); + nBytesLeft = poField->GetDataSize(); + + for( i = 0; i < nPointCount; i++ ) + { + double dfX, dfY, dfZ = 0.0; + int nBytesConsumed; + + dfY = poYCOO->ExtractIntData( pachData, nBytesLeft, + &nBytesConsumed ) / (double) nCOMF; + nBytesLeft -= nBytesConsumed; + pachData += nBytesConsumed; + + dfX = poXCOO->ExtractIntData( pachData, nBytesLeft, + &nBytesConsumed ) / (double) nCOMF; + nBytesLeft -= nBytesConsumed; + pachData += nBytesConsumed; + + if( poVE3D != NULL ) + { + dfZ = poYCOO->ExtractIntData( pachData, nBytesLeft, + &nBytesConsumed ) / (double) nSOMF; + nBytesLeft -= nBytesConsumed; + pachData += nBytesConsumed; + } + + poMP->addGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ) ); + } + + poFeature->SetGeometryDirectly( poMP ); +} + +/************************************************************************/ +/* GetIntSubfield() */ +/************************************************************************/ + +static int +GetIntSubfield( DDFField *poField, + const char * pszSubfield, + int iSubfieldIndex) +{ + DDFSubfieldDefn *poSFDefn = + poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield ); + + if( poSFDefn == NULL ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Get a pointer to the data. */ +/* -------------------------------------------------------------------- */ + int nBytesRemaining; + + const char *pachData = poField->GetSubfieldData( poSFDefn, + &nBytesRemaining, + iSubfieldIndex ); + + return( poSFDefn->ExtractIntData( pachData, nBytesRemaining, NULL ) ); +} + +/************************************************************************/ +/* AssembleLineGeometry() */ +/************************************************************************/ + +void S57Reader::AssembleLineGeometry( DDFRecord * poFRecord, + OGRFeature * poFeature ) + +{ + DDFField *poFSPT; + OGRLineString *poLine = new OGRLineString(); + OGRMultiLineString *poMLS = new OGRMultiLineString(); + double dlastfX( 0.0 ), dlastfY( 0.0 ), dfX, dfY; + +/* -------------------------------------------------------------------- */ +/* Loop collecting edges. */ +/* Iterate over the FSPT fields. */ +/* -------------------------------------------------------------------- */ + const int nFieldCount = poFRecord->GetFieldCount(); + + for( int iField = 0; iField < nFieldCount; ++iField ) + { + poFSPT = poFRecord->GetField( iField ); + + if( !EQUAL(poFSPT->GetFieldDefn()->GetName(), "FSPT") ) + continue; + +/* -------------------------------------------------------------------- */ +/* Loop over the rows of each FSPT field */ +/* -------------------------------------------------------------------- */ + const int nEdgeCount = poFSPT->GetRepeatCount(); + + for( int iEdge = 0; iEdge < nEdgeCount; ++iEdge ) + { + int nVC_RCID_firstnode, nVC_RCID_lastnode; + bool bReverse = (GetIntSubfield( poFSPT, "ORNT", iEdge ) == 2); + +/* -------------------------------------------------------------------- */ +/* Find the spatial record for this edge. */ +/* -------------------------------------------------------------------- */ + int nRCID = ParseName( poFSPT, iEdge ); + + DDFRecord *poSRecord = oVE_Index.FindRecord( nRCID ); + if( poSRecord == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Couldn't find spatial record %d.\n" + "Feature OBJL=%s, RCID=%d may have corrupt or" + "missing geometry.", + nRCID, + poFeature->GetDefnRef()->GetName(), + GetIntSubfield( poFSPT, "RCID", 0 ) ); + continue; + } + +/* -------------------------------------------------------------------- */ +/* Get the first and last nodes */ +/* -------------------------------------------------------------------- */ + DDFField *poVRPT = poSRecord->FindField( "VRPT" ); + if( poVRPT == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to fetch start node for RCID %d.\n" + "Feature OBJL=%s, RCID=%d may have corrupt or" + "missing geometry.", + nRCID, + poFeature->GetDefnRef()->GetName(), + GetIntSubfield( poFSPT, "RCID", 0 ) ); + continue; + } + + // The "VRPT" field has only one row + // Get the next row from a second "VRPT" field + if( poVRPT != NULL && poVRPT->GetRepeatCount() == 1 ) + { + nVC_RCID_firstnode = ParseName( poVRPT ); + poVRPT = poSRecord->FindField( "VRPT", 1 ); + + if( poVRPT == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to fetch end node for RCID %d.\n" + "Feature OBJL=%s, RCID=%d may have corrupt or" + "missing geometry.", + nRCID, + poFeature->GetDefnRef()->GetName(), + GetIntSubfield( poFSPT, "RCID", 0 ) ); + continue; + } + + nVC_RCID_lastnode = ParseName( poVRPT ); + + if( bReverse ) + { + int tmp = nVC_RCID_lastnode; + nVC_RCID_lastnode = nVC_RCID_firstnode; + nVC_RCID_firstnode = tmp; + } + } + else if( bReverse ) + { + nVC_RCID_lastnode = ParseName( poVRPT ); + nVC_RCID_firstnode = ParseName( poVRPT, 1 ); + } + else + { + nVC_RCID_firstnode = ParseName( poVRPT ); + nVC_RCID_lastnode = ParseName( poVRPT, 1 ); + } + + if( nVC_RCID_firstnode == -1 || + ! FetchPoint( RCNM_VC, nVC_RCID_firstnode, &dfX, &dfY ) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to fetch start node RCID=%d.\n" + "Feature OBJL=%s, RCID=%d may have corrupt or" + " missing geometry.", + nVC_RCID_firstnode, + poFeature->GetDefnRef()->GetName(), + poFRecord->GetIntSubfield( "FRID", 0, + "RCID", 0 ) ); + + continue; + } + +/* -------------------------------------------------------------------- */ +/* Does the first node match the trailing node on the existing */ +/* line string? If so, skip it, otherwise if the existing */ +/* linestring is not empty we need to push it out and start a */ +/* new one as it means things are not connected. */ +/* -------------------------------------------------------------------- */ + if( poLine->getNumPoints() == 0 ) + { + poLine->addPoint( dfX, dfY ); + } + else if( ABS(dlastfX - dfX) > 0.00000001 || + ABS(dlastfY - dfY) > 0.00000001 ) + { + // we need to start a new linestring. + poMLS->addGeometryDirectly( poLine ); + poLine = new OGRLineString(); + poLine->addPoint( dfX, dfY ); + } + else + { + /* omit point, already present */ + } + + // remember the coordinates of the last point + dlastfX = dfX; dlastfY = dfY; + +/* -------------------------------------------------------------------- */ +/* Collect the vertices. */ +/* Iterate over all the SG2D fields in the Spatial record */ +/* -------------------------------------------------------------------- */ + int nVBase, nVCount; + int nStart, nEnd, nInc; + DDFField *poSG2D; + DDFSubfieldDefn *poXCOO=NULL, *poYCOO=NULL; + + for( int iSField = 0; iSField < poSRecord->GetFieldCount(); + ++iSField ) + { + poSG2D = poSRecord->GetField( iSField ); + + if( EQUAL(poSG2D->GetFieldDefn()->GetName(), "SG2D") || + EQUAL(poSG2D->GetFieldDefn()->GetName(), "AR2D") ) + { + poXCOO = poSG2D->GetFieldDefn()->FindSubfieldDefn("XCOO"); + poYCOO = poSG2D->GetFieldDefn()->FindSubfieldDefn("YCOO"); + + if( poXCOO == NULL || poYCOO == NULL ) + { + CPLDebug( "S57", "XCOO or YCOO are NULL" ); + return; + } + + nVCount = poSG2D->GetRepeatCount(); + + if( bReverse ) + { + nStart = nVCount-1; + nEnd = 0; + nInc = -1; + } + else + { + nStart = 0; + nEnd = nVCount-1; + nInc = 1; + } + + nVBase = poLine->getNumPoints(); + poLine->setNumPoints( nVBase + nVCount ); + + const char *pachData; + int nBytesRemaining; + + for( int i = nStart; i != nEnd+nInc; i += nInc ) + { + pachData = poSG2D->GetSubfieldData(poXCOO,&nBytesRemaining,i); + + dfX = poXCOO->ExtractIntData(pachData,nBytesRemaining,NULL) + / (double) nCOMF; + + pachData = poSG2D->GetSubfieldData(poYCOO,&nBytesRemaining,i); + + dfY = poXCOO->ExtractIntData(pachData,nBytesRemaining,NULL) + / (double) nCOMF; + + poLine->setPoint( nVBase++, dfX, dfY ); + } + } + } + dlastfX = dfX; dlastfY = dfY; + +/* -------------------------------------------------------------------- */ +/* Add the end node. */ +/* -------------------------------------------------------------------- */ + if( nVC_RCID_lastnode != -1 && + FetchPoint( RCNM_VC, nVC_RCID_lastnode, &dfX, &dfY ) ) + { + poLine->addPoint( dfX, dfY ); + dlastfX = dfX; dlastfY = dfY; + } + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to fetch end node RCID=%d.\n" + "Feature OBJL=%s, RCID=%d may have corrupt or" + " missing geometry.", + nVC_RCID_lastnode, + poFeature->GetDefnRef()->GetName(), + poFRecord->GetIntSubfield( "FRID", 0, "RCID", 0 ) ); + continue; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Set either the line or multilinestring as the geometry. We */ +/* are careful to just produce a linestring if there are no */ +/* disconnections. */ +/* -------------------------------------------------------------------- */ + if( poMLS->getNumGeometries() > 0 ) + { + poMLS->addGeometryDirectly( poLine ); + poFeature->SetGeometryDirectly( poMLS ); + } + else if( poLine->getNumPoints() >= 2 ) + { + poFeature->SetGeometryDirectly( poLine ); + delete poMLS; + } + else + { + delete poLine; + delete poMLS; + } +} + +/************************************************************************/ +/* AssembleAreaGeometry() */ +/************************************************************************/ + +void S57Reader::AssembleAreaGeometry( DDFRecord * poFRecord, + OGRFeature * poFeature ) + +{ + DDFField *poFSPT; + OGRGeometryCollection * poLines = new OGRGeometryCollection(); + +/* -------------------------------------------------------------------- */ +/* Find the FSPT fields. */ +/* -------------------------------------------------------------------- */ + const int nFieldCount = poFRecord->GetFieldCount(); + + for( int iFSPT = 0; iFSPT < nFieldCount; ++iFSPT ) + { + poFSPT = poFRecord->GetField(iFSPT); + + if ( !EQUAL(poFSPT->GetFieldDefn()->GetName(), "FSPT") ) + continue; + + int nEdgeCount; + + nEdgeCount = poFSPT->GetRepeatCount(); + +/* ==================================================================== */ +/* Loop collecting edges. */ +/* ==================================================================== */ + for( int iEdge = 0; iEdge < nEdgeCount; iEdge++ ) + { + DDFRecord *poSRecord; + int nRCID; + +/* -------------------------------------------------------------------- */ +/* Find the spatial record for this edge. */ +/* -------------------------------------------------------------------- */ + nRCID = ParseName( poFSPT, iEdge ); + + poSRecord = oVE_Index.FindRecord( nRCID ); + if( poSRecord == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Couldn't find spatial record %d.\n" + "Feature OBJL=%s, RCID=%d may have corrupt or" + "missing geometry.", + nRCID, + poFeature->GetDefnRef()->GetName(), + GetIntSubfield( poFSPT, "RCID", 0 ) ); + continue; + } + +/* -------------------------------------------------------------------- */ +/* Create the line string. */ +/* -------------------------------------------------------------------- */ + OGRLineString *poLine = new OGRLineString(); + +/* -------------------------------------------------------------------- */ +/* Add the start node. */ +/* -------------------------------------------------------------------- */ + DDFField *poVRPT = poSRecord->FindField( "VRPT" ); + if( poVRPT != NULL ) + { + int nVC_RCID = ParseName( poVRPT ); + double dfX, dfY; + + if( nVC_RCID != -1 && FetchPoint( RCNM_VC, nVC_RCID, &dfX, &dfY ) ) + poLine->addPoint( dfX, dfY ); + } + +/* -------------------------------------------------------------------- */ +/* Collect the vertices. */ +/* -------------------------------------------------------------------- */ + if( !FetchLine( poSRecord, poLine->getNumPoints(), 1, poLine ) ) + { + CPLDebug( "S57", "FetchLine() failed in AssembleAreaGeometry()!" ); + } + + +/* -------------------------------------------------------------------- */ +/* Add the end node. */ +/* -------------------------------------------------------------------- */ + if( poVRPT != NULL && poVRPT->GetRepeatCount() > 1 ) + { + int nVC_RCID = ParseName( poVRPT, 1 ); + double dfX, dfY; + + if( nVC_RCID != -1 && FetchPoint( RCNM_VC, nVC_RCID, &dfX, &dfY ) ) + poLine->addPoint( dfX, dfY ); + } + else if( (poVRPT = poSRecord->FindField( "VRPT", 1 )) != NULL ) + { + int nVC_RCID = ParseName( poVRPT ); + double dfX, dfY; + + if( nVC_RCID != -1 && FetchPoint( RCNM_VC, nVC_RCID, &dfX, &dfY ) ) + poLine->addPoint( dfX, dfY ); + } + + poLines->addGeometryDirectly( poLine ); + } + } + +/* -------------------------------------------------------------------- */ +/* Build lines into a polygon. */ +/* -------------------------------------------------------------------- */ + OGRPolygon *poPolygon; + OGRErr eErr; + + poPolygon = (OGRPolygon *) + OGRBuildPolygonFromEdges( (OGRGeometryH) poLines, + TRUE, FALSE, 0.0, &eErr ); + if( eErr != OGRERR_NONE ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Polygon assembly has failed for feature FIDN=%d,FIDS=%d.\n" + "Geometry may be missing or incomplete.", + poFeature->GetFieldAsInteger( "FIDN" ), + poFeature->GetFieldAsInteger( "FIDS" ) ); + } + + delete poLines; + + if( poPolygon != NULL ) + poFeature->SetGeometryDirectly( poPolygon ); +} + +/************************************************************************/ +/* FindFDefn() */ +/* */ +/* Find the OGRFeatureDefn corresponding to the passed feature */ +/* record. It will search based on geometry class, or object */ +/* class depending on the bClassBased setting. */ +/************************************************************************/ + +OGRFeatureDefn * S57Reader::FindFDefn( DDFRecord * poRecord ) + +{ + if( poRegistrar != NULL ) + { + int nOBJL = poRecord->GetIntSubfield( "FRID", 0, "OBJL", 0 ); + + if( nOBJL < (int)apoFDefnByOBJL.size() + && apoFDefnByOBJL[nOBJL] != NULL ) + return apoFDefnByOBJL[nOBJL]; + + if( !poClassContentExplorer->SelectClass( nOBJL ) ) + { + for( int i = 0; i < nFDefnCount; i++ ) + { + if( EQUAL(papoFDefnList[i]->GetName(),"Generic") ) + return papoFDefnList[i]; + } + return NULL; + } + + for( int i = 0; i < nFDefnCount; i++ ) + { + if( EQUAL(papoFDefnList[i]->GetName(), + poClassContentExplorer->GetAcronym()) ) + return papoFDefnList[i]; + } + + return NULL; + } + else + { + int nPRIM = poRecord->GetIntSubfield( "FRID", 0, "PRIM", 0 ); + OGRwkbGeometryType eGType; + + if( nPRIM == PRIM_P ) + eGType = wkbPoint; + else if( nPRIM == PRIM_L ) + eGType = wkbLineString; + else if( nPRIM == PRIM_A ) + eGType = wkbPolygon; + else + eGType = wkbNone; + + for( int i = 0; i < nFDefnCount; i++ ) + { + if( papoFDefnList[i]->GetGeomType() == eGType ) + return papoFDefnList[i]; + } + } + + return NULL; +} + +/************************************************************************/ +/* ParseName() */ +/* */ +/* Pull the RCNM and RCID values from a NAME field. The RCID */ +/* is returned and the RCNM can be gotten via the pnRCNM argument. */ +/* Note: nIndex is the index of the requested 'NAME' instance */ +/************************************************************************/ + +int S57Reader::ParseName( DDFField * poField, int nIndex, int * pnRCNM ) + +{ + unsigned char *pabyData; + + if( poField == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing field in ParseName()." ); + return -1; + } + + DDFSubfieldDefn* poName = poField->GetFieldDefn()->FindSubfieldDefn( "NAME" ); + if( poName == NULL ) + return -1; + + int nMaxBytes; + pabyData = (unsigned char *) + poField->GetSubfieldData( poName, &nMaxBytes, nIndex ); + if( pabyData == NULL || nMaxBytes < 5 ) + return -1; + + if( pnRCNM != NULL ) + *pnRCNM = pabyData[0]; + + return pabyData[1] + + pabyData[2] * 256 + + pabyData[3] * 256 * 256 + + pabyData[4] * 256 * 256 * 256; +} + +/************************************************************************/ +/* AddFeatureDefn() */ +/************************************************************************/ + +void S57Reader::AddFeatureDefn( OGRFeatureDefn * poFDefn ) + +{ + nFDefnCount++; + papoFDefnList = (OGRFeatureDefn **) + CPLRealloc(papoFDefnList, sizeof(OGRFeatureDefn*)*nFDefnCount ); + + papoFDefnList[nFDefnCount-1] = poFDefn; + + if( poRegistrar != NULL ) + { + if( poClassContentExplorer->SelectClass( poFDefn->GetName() ) ) + { + int nOBJL = poClassContentExplorer->GetOBJL(); + if( nOBJL >= (int) apoFDefnByOBJL.size() ) + apoFDefnByOBJL.resize(nOBJL+1); + apoFDefnByOBJL[nOBJL] = poFDefn; + } + } +} + +/************************************************************************/ +/* CollectClassList() */ +/* */ +/* Establish the list of classes (unique OBJL values) that */ +/* occur in this dataset. */ +/************************************************************************/ + +int S57Reader::CollectClassList(std::vector &anClassCount) + +{ + int bSuccess = TRUE; + + if( !bFileIngested && !Ingest() ) + return FALSE; + + for( int iFEIndex = 0; iFEIndex < oFE_Index.GetCount(); iFEIndex++ ) + { + DDFRecord *poRecord = oFE_Index.GetByIndex( iFEIndex ); + int nOBJL = poRecord->GetIntSubfield( "FRID", 0, "OBJL", 0 ); + + if( nOBJL < 0 ) + bSuccess = FALSE; + else + { + if( nOBJL >= (int) anClassCount.size() ) + anClassCount.resize(nOBJL+1); + anClassCount[nOBJL]++; + } + + } + + return bSuccess; +} + +/************************************************************************/ +/* ApplyRecordUpdate() */ +/* */ +/* Update one target record based on an S-57 update record */ +/* (RUIN=3). */ +/************************************************************************/ + +int S57Reader::ApplyRecordUpdate( DDFRecord *poTarget, DDFRecord *poUpdate ) + +{ + const char *pszKey = poUpdate->GetField(1)->GetFieldDefn()->GetName(); + +/* -------------------------------------------------------------------- */ +/* Validate versioning. */ +/* -------------------------------------------------------------------- */ + if( poTarget->GetIntSubfield( pszKey, 0, "RVER", 0 ) + 1 + != poUpdate->GetIntSubfield( pszKey, 0, "RVER", 0 ) ) + { + CPLDebug( "S57", + "Mismatched RVER value on RCNM=%d,RCID=%d.\n", + poTarget->GetIntSubfield( pszKey, 0, "RCNM", 0 ), + poTarget->GetIntSubfield( pszKey, 0, "RCID", 0 ) ); + + //CPLAssert( FALSE ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Update the target version. */ +/* -------------------------------------------------------------------- */ + unsigned char *pnRVER; + DDFField *poKey = poTarget->FindField( pszKey ); + DDFSubfieldDefn *poRVER_SFD; + + if( poKey == NULL ) + { + //CPLAssert( FALSE ); + return FALSE; + } + + poRVER_SFD = poKey->GetFieldDefn()->FindSubfieldDefn( "RVER" ); + if( poRVER_SFD == NULL ) + return FALSE; + + pnRVER = (unsigned char *) poKey->GetSubfieldData( poRVER_SFD, NULL, 0 ); + + *pnRVER += 1; + +/* -------------------------------------------------------------------- */ +/* Check for, and apply record record to spatial record pointer */ +/* updates. */ +/* -------------------------------------------------------------------- */ + if( poUpdate->FindField( "FSPC" ) != NULL ) + { + int nFSUI = poUpdate->GetIntSubfield( "FSPC", 0, "FSUI", 0 ); + int nFSIX = poUpdate->GetIntSubfield( "FSPC", 0, "FSIX", 0 ); + int nNSPT = poUpdate->GetIntSubfield( "FSPC", 0, "NSPT", 0 ); + DDFField *poSrcFSPT = poUpdate->FindField( "FSPT" ); + DDFField *poDstFSPT = poTarget->FindField( "FSPT" ); + int nPtrSize; + + if( (poSrcFSPT == NULL && nFSUI != 2) || poDstFSPT == NULL ) + { + //CPLAssert( FALSE ); + return FALSE; + } + + nPtrSize = poDstFSPT->GetFieldDefn()->GetFixedWidth(); + + if( nFSUI == 1 ) /* INSERT */ + { + char *pachInsertion; + int nInsertionBytes = nPtrSize * nNSPT; + + if( poSrcFSPT->GetDataSize() < nInsertionBytes ) + { + CPLDebug("S57", "Not enough bytes in source FSPT field. Has %d, requires %d", + poSrcFSPT->GetDataSize(), nInsertionBytes ); + return FALSE; + } + + pachInsertion = (char *) CPLMalloc(nInsertionBytes + nPtrSize); + memcpy( pachInsertion, poSrcFSPT->GetData(), nInsertionBytes ); + + /* + ** If we are inserting before an instance that already + ** exists, we must add it to the end of the data being + ** inserted. + */ + if( nFSIX <= poDstFSPT->GetRepeatCount() ) + { + if( poDstFSPT->GetDataSize() < nPtrSize * nFSIX ) + { + CPLDebug("S57", "Not enough bytes in dest FSPT field. Has %d, requires %d", + poDstFSPT->GetDataSize(), nPtrSize * nFSIX ); + CPLFree( pachInsertion ); + return FALSE; + } + + memcpy( pachInsertion + nInsertionBytes, + poDstFSPT->GetData() + nPtrSize * (nFSIX-1), + nPtrSize ); + nInsertionBytes += nPtrSize; + } + + poTarget->SetFieldRaw( poDstFSPT, nFSIX - 1, + pachInsertion, nInsertionBytes ); + CPLFree( pachInsertion ); + } + else if( nFSUI == 2 ) /* DELETE */ + { + /* Wipe each deleted coordinate */ + for( int i = nNSPT-1; i >= 0; i-- ) + { + poTarget->SetFieldRaw( poDstFSPT, i + nFSIX - 1, NULL, 0 ); + } + } + else if( nFSUI == 3 ) /* MODIFY */ + { + /* copy over each ptr */ + if( poSrcFSPT->GetDataSize() < nNSPT * nPtrSize ) + { + CPLDebug("S57", "Not enough bytes in source FSPT field. Has %d, requires %d", + poSrcFSPT->GetDataSize(), nNSPT * nPtrSize ); + return FALSE; + } + + for( int i = 0; i < nNSPT; i++ ) + { + const char *pachRawData; + + pachRawData = poSrcFSPT->GetData() + nPtrSize * i; + poTarget->SetFieldRaw( poDstFSPT, i + nFSIX - 1, + pachRawData, nPtrSize ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Check for, and apply vector record to vector record pointer */ +/* updates. */ +/* -------------------------------------------------------------------- */ + if( poUpdate->FindField( "VRPC" ) != NULL ) + { + int nVPUI = poUpdate->GetIntSubfield( "VRPC", 0, "VPUI", 0 ); + int nVPIX = poUpdate->GetIntSubfield( "VRPC", 0, "VPIX", 0 ); + int nNVPT = poUpdate->GetIntSubfield( "VRPC", 0, "NVPT", 0 ); + DDFField *poSrcVRPT = poUpdate->FindField( "VRPT" ); + DDFField *poDstVRPT = poTarget->FindField( "VRPT" ); + int nPtrSize; + + if( (poSrcVRPT == NULL && nVPUI != 2) || poDstVRPT == NULL ) + { + //CPLAssert( FALSE ); + return FALSE; + } + + nPtrSize = poDstVRPT->GetFieldDefn()->GetFixedWidth(); + + if( nVPUI == 1 ) /* INSERT */ + { + char *pachInsertion; + int nInsertionBytes = nPtrSize * nNVPT; + + if( poSrcVRPT->GetDataSize() < nInsertionBytes ) + { + CPLDebug("S57", "Not enough bytes in source VRPT field. Has %d, requires %d", + poSrcVRPT->GetDataSize(), nInsertionBytes ); + return FALSE; + } + + pachInsertion = (char *) CPLMalloc(nInsertionBytes + nPtrSize); + memcpy( pachInsertion, poSrcVRPT->GetData(), nInsertionBytes ); + + /* + ** If we are inserting before an instance that already + ** exists, we must add it to the end of the data being + ** inserted. + */ + if( nVPIX <= poDstVRPT->GetRepeatCount() ) + { + if( poDstVRPT->GetDataSize() < nPtrSize * nVPIX ) + { + CPLDebug("S57", "Not enough bytes in dest VRPT field. Has %d, requires %d", + poDstVRPT->GetDataSize(), nPtrSize * nVPIX ); + CPLFree( pachInsertion ); + return FALSE; + } + + memcpy( pachInsertion + nInsertionBytes, + poDstVRPT->GetData() + nPtrSize * (nVPIX-1), + nPtrSize ); + nInsertionBytes += nPtrSize; + } + + poTarget->SetFieldRaw( poDstVRPT, nVPIX - 1, + pachInsertion, nInsertionBytes ); + CPLFree( pachInsertion ); + } + else if( nVPUI == 2 ) /* DELETE */ + { + /* Wipe each deleted coordinate */ + for( int i = nNVPT-1; i >= 0; i-- ) + { + poTarget->SetFieldRaw( poDstVRPT, i + nVPIX - 1, NULL, 0 ); + } + } + else if( nVPUI == 3 ) /* MODIFY */ + { + if( poSrcVRPT->GetDataSize() < nNVPT * nPtrSize ) + { + CPLDebug("S57", "Not enough bytes in source VRPT field. Has %d, requires %d", + poSrcVRPT->GetDataSize(), nNVPT * nPtrSize ); + return FALSE; + } + + /* copy over each ptr */ + for( int i = 0; i < nNVPT; i++ ) + { + const char *pachRawData; + + pachRawData = poSrcVRPT->GetData() + nPtrSize * i; + + poTarget->SetFieldRaw( poDstVRPT, i + nVPIX - 1, + pachRawData, nPtrSize ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Check for, and apply record update to coordinates. */ +/* -------------------------------------------------------------------- */ + if( poUpdate->FindField( "SGCC" ) != NULL ) + { + int nCCUI = poUpdate->GetIntSubfield( "SGCC", 0, "CCUI", 0 ); + int nCCIX = poUpdate->GetIntSubfield( "SGCC", 0, "CCIX", 0 ); + int nCCNC = poUpdate->GetIntSubfield( "SGCC", 0, "CCNC", 0 ); + DDFField *poSrcSG2D = poUpdate->FindField( "SG2D" ); + DDFField *poDstSG2D = poTarget->FindField( "SG2D" ); + int nCoordSize; + + /* If we don't have SG2D, check for SG3D */ + if( poDstSG2D == NULL ) + { + poDstSG2D = poTarget->FindField( "SG3D" ); + if (poDstSG2D != NULL) + { + poSrcSG2D = poUpdate->FindField("SG3D"); + } + } + + if( (poSrcSG2D == NULL && nCCUI != 2) + || (poDstSG2D == NULL && nCCUI != 1) ) + { + //CPLAssert( FALSE ); + return FALSE; + } + + if (poDstSG2D == NULL) + { + poTarget->AddField(poTarget->GetModule()->FindFieldDefn("SG2D")); + poDstSG2D = poTarget->FindField("SG2D"); + if (poDstSG2D == NULL) { + //CPLAssert( FALSE ); + return FALSE; + } + + // Delete null default data that was created + poTarget->SetFieldRaw( poDstSG2D, 0, NULL, 0 ); + } + + nCoordSize = poDstSG2D->GetFieldDefn()->GetFixedWidth(); + + if( nCCUI == 1 ) /* INSERT */ + { + char *pachInsertion; + int nInsertionBytes = nCoordSize * nCCNC; + + if( poSrcSG2D->GetDataSize() < nInsertionBytes ) + { + CPLDebug("S57", "Not enough bytes in source SG2D field. Has %d, requires %d", + poSrcSG2D->GetDataSize(), nInsertionBytes ); + return FALSE; + } + + pachInsertion = (char *) CPLMalloc(nInsertionBytes + nCoordSize); + memcpy( pachInsertion, poSrcSG2D->GetData(), nInsertionBytes ); + + /* + ** If we are inserting before an instance that already + ** exists, we must add it to the end of the data being + ** inserted. + */ + if( nCCIX <= poDstSG2D->GetRepeatCount() ) + { + if( poDstSG2D->GetDataSize() < nCoordSize * nCCIX ) + { + CPLDebug("S57", "Not enough bytes in dest SG2D field. Has %d, requires %d", + poDstSG2D->GetDataSize(), nCoordSize * nCCIX ); + CPLFree( pachInsertion ); + return FALSE; + } + + memcpy( pachInsertion + nInsertionBytes, + poDstSG2D->GetData() + nCoordSize * (nCCIX-1), + nCoordSize ); + nInsertionBytes += nCoordSize; + } + + poTarget->SetFieldRaw( poDstSG2D, nCCIX - 1, + pachInsertion, nInsertionBytes ); + CPLFree( pachInsertion ); + } + else if( nCCUI == 2 ) /* DELETE */ + { + /* Wipe each deleted coordinate */ + for( int i = nCCNC-1; i >= 0; i-- ) + { + poTarget->SetFieldRaw( poDstSG2D, i + nCCIX - 1, NULL, 0 ); + } + } + else if( nCCUI == 3 ) /* MODIFY */ + { + if( poSrcSG2D->GetDataSize() < nCCNC * nCoordSize ) + { + CPLDebug("S57", "Not enough bytes in source SG2D field. Has %d, requires %d", + poSrcSG2D->GetDataSize(), nCCNC * nCoordSize ); + return FALSE; + } + + /* copy over each ptr */ + for( int i = 0; i < nCCNC; i++ ) + { + const char *pachRawData; + + pachRawData = poSrcSG2D->GetData() + nCoordSize * i; + + poTarget->SetFieldRaw( poDstSG2D, i + nCCIX - 1, + pachRawData, nCoordSize ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Apply updates to Feature to Feature pointer fields. Note */ +/* INSERT and DELETE are untested. UPDATE tested per bug #5028. */ +/* -------------------------------------------------------------------- */ + if( poUpdate->FindField( "FFPC" ) != NULL ) + { + int nFFUI = poUpdate->GetIntSubfield( "FFPC", 0, "FFUI", 0 ); + int nFFIX = poUpdate->GetIntSubfield( "FFPC", 0, "FFIX", 0 ); + int nNFPT = poUpdate->GetIntSubfield( "FFPC", 0, "NFPT", 0 ); + DDFField *poSrcFFPT = poUpdate->FindField( "FFPT" ); + DDFField *poDstFFPT = poTarget->FindField( "FFPT" ); + + if( (poSrcFFPT == NULL && nFFUI != 2) + || (poDstFFPT == NULL && nFFUI != 1) ) + { + CPLDebug( "S57", "Missing source or target FFPT applying update."); + //CPLAssert( FALSE ); + return FALSE; + } + + // Create FFPT field on target record, if it does not yet exist. + if (poDstFFPT == NULL) + { + // Untested! + poTarget->AddField(poTarget->GetModule()->FindFieldDefn("FFPT")); + poDstFFPT = poTarget->FindField("FFPT"); + if (poDstFFPT == NULL) { + //CPLAssert( FALSE ); + return FALSE; + } + + // Delete null default data that was created + poTarget->SetFieldRaw( poDstFFPT, 0, NULL, 0 ); + } + + // FFPT includes COMT which is variable length which would + // greatly complicate updates. But in practice COMT is always + // an empty string so we will take a chance and assume that so + // we have a fixed record length. We *could* actually verify that + // but I have not done so for now. + int nFFPTSize = 10; + + if (nFFUI == 1 ) /* INSERT */ + { + // Untested! + CPLDebug( "S57", "Using untested FFPT INSERT code!"); + + char *pachInsertion; + int nInsertionBytes = nFFPTSize * nNFPT; + + if( poSrcFFPT->GetDataSize() < nInsertionBytes ) + { + CPLDebug("S57", "Not enough bytes in source FFPT field. Has %d, requires %d", + poSrcFFPT->GetDataSize(), nInsertionBytes ); + return FALSE; + } + + pachInsertion = (char *) CPLMalloc(nInsertionBytes + nFFPTSize); + memcpy( pachInsertion, poSrcFFPT->GetData(), nInsertionBytes ); + + /* + ** If we are inserting before an instance that already + ** exists, we must add it to the end of the data being + ** inserted. + */ + if( nFFIX <= poDstFFPT->GetRepeatCount() ) + { + if( poDstFFPT->GetDataSize() < nFFPTSize * nFFIX ) + { + CPLDebug("S57", "Not enough bytes in dest FFPT field. Has %d, requires %d", + poDstFFPT->GetDataSize(), nFFPTSize * nFFIX ); + CPLFree( pachInsertion ); + return FALSE; + } + + memcpy( pachInsertion + nInsertionBytes, + poDstFFPT->GetData() + nFFPTSize * (nFFIX-1), + nFFPTSize ); + nInsertionBytes += nFFPTSize; + } + + poTarget->SetFieldRaw( poDstFFPT, nFFIX - 1, + pachInsertion, nInsertionBytes ); + CPLFree( pachInsertion ); + } + else if( nFFUI == 2 ) /* DELETE */ + { + // Untested! + CPLDebug( "S57", "Using untested FFPT DELETE code!"); + + /* Wipe each deleted record */ + for( int i = nNFPT-1; i >= 0; i-- ) + { + poTarget->SetFieldRaw( poDstFFPT, i + nFFIX - 1, NULL, 0 ); + } + } + else if( nFFUI == 3 ) /* UPDATE */ + { + if( poSrcFFPT->GetDataSize() < nNFPT * nFFPTSize ) + { + CPLDebug("S57", "Not enough bytes in source FFPT field. Has %d, requires %d", + poSrcFFPT->GetDataSize(), nNFPT * nFFPTSize ); + return FALSE; + } + + /* copy over each ptr */ + for( int i = 0; i < nNFPT; i++ ) + { + const char *pachRawData; + + pachRawData = poSrcFFPT->GetData() + nFFPTSize * i; + + poTarget->SetFieldRaw( poDstFFPT, i + nFFIX - 1, + pachRawData, nFFPTSize ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Check for and apply changes to attribute lists. */ +/* -------------------------------------------------------------------- */ + if( poUpdate->FindField( "ATTF" ) != NULL ) + { + DDFField *poSrcATTF = poUpdate->FindField( "ATTF" ); + DDFField *poDstATTF = poTarget->FindField( "ATTF" ); + int nRepeatCount = poSrcATTF->GetRepeatCount(); + + if( poDstATTF == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to apply ATTF change to target record without an ATTF field (see GDAL/OGR Bug #1648)" ); + return FALSE; + } + + for( int iAtt = 0; iAtt < nRepeatCount; iAtt++ ) + { + int nATTL = poUpdate->GetIntSubfield( "ATTF", 0, "ATTL", iAtt ); + int iTAtt, nDataBytes; + const char *pszRawData; + + for( iTAtt = poDstATTF->GetRepeatCount()-1; iTAtt >= 0; iTAtt-- ) + { + if( poTarget->GetIntSubfield( "ATTF", 0, "ATTL", iTAtt ) + == nATTL ) + break; + } + if( iTAtt == -1 ) + iTAtt = poDstATTF->GetRepeatCount(); + + pszRawData = poSrcATTF->GetInstanceData( iAtt, &nDataBytes ); + if( pszRawData[2] == 0x7f /* delete marker */ ) + { + poTarget->SetFieldRaw( poDstATTF, iTAtt, NULL, 0 ); + } + else + { + poTarget->SetFieldRaw( poDstATTF, iTAtt, pszRawData, + nDataBytes ); + } + } + } + + return TRUE; +} + + +/************************************************************************/ +/* ApplyUpdates() */ +/* */ +/* Read records from an update file, and apply them to the */ +/* currently loaded index of features. */ +/************************************************************************/ + +int S57Reader::ApplyUpdates( DDFModule *poUpdateModule ) + +{ + DDFRecord *poRecord; + +/* -------------------------------------------------------------------- */ +/* Ensure base file is loaded. */ +/* -------------------------------------------------------------------- */ + if( !bFileIngested && !Ingest() ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Read records, and apply as updates. */ +/* -------------------------------------------------------------------- */ + CPLErrorReset(); + while( (poRecord = poUpdateModule->ReadRecord()) != NULL ) + { + DDFField *poKeyField = poRecord->GetField(1); + if( poKeyField == NULL ) + return FALSE; + const char *pszKey = poKeyField->GetFieldDefn()->GetName(); + + if( EQUAL(pszKey,"VRID") || EQUAL(pszKey,"FRID")) + { + int nRCNM = poRecord->GetIntSubfield( pszKey,0, "RCNM",0 ); + int nRCID = poRecord->GetIntSubfield( pszKey,0, "RCID",0 ); + int nRVER = poRecord->GetIntSubfield( pszKey,0, "RVER",0 ); + int nRUIN = poRecord->GetIntSubfield( pszKey,0, "RUIN",0 ); + DDFRecordIndex *poIndex = NULL; + + if( EQUAL(poKeyField->GetFieldDefn()->GetName(),"VRID") ) + { + switch( nRCNM ) + { + case RCNM_VI: + poIndex = &oVI_Index; + break; + + case RCNM_VC: + poIndex = &oVC_Index; + break; + + case RCNM_VE: + poIndex = &oVE_Index; + break; + + case RCNM_VF: + poIndex = &oVF_Index; + break; + + default: + //CPLAssert( FALSE ); + return FALSE; + break; + } + } + else + { + poIndex = &oFE_Index; + } + + if( poIndex != NULL ) + { + if( nRUIN == 1 ) /* insert */ + { + poIndex->AddRecord( nRCID, poRecord->CloneOn(poModule) ); + } + else if( nRUIN == 2 ) /* delete */ + { + DDFRecord *poTarget; + + poTarget = poIndex->FindRecord( nRCID ); + if( poTarget == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Can't find RCNM=%d,RCID=%d for delete.\n", + nRCNM, nRCID ); + } + else if( poTarget->GetIntSubfield( pszKey, 0, "RVER", 0 ) + != nRVER - 1 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Mismatched RVER value on RCNM=%d,RCID=%d.\n", + nRCNM, nRCID ); + } + else + { + poIndex->RemoveRecord( nRCID ); + } + } + + else if( nRUIN == 3 ) /* modify in place */ + { + DDFRecord *poTarget; + + poTarget = poIndex->FindRecord( nRCID ); + if( poTarget == NULL ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Can't find RCNM=%d,RCID=%d for update.\n", + nRCNM, nRCID ); + } + else + { + if( !ApplyRecordUpdate( poTarget, poRecord ) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "An update to RCNM=%d,RCID=%d failed.\n", + nRCNM, nRCID ); + } + } + } + } + } + + else if( EQUAL(pszKey,"DSID") ) + { + if( poDSIDRecord != NULL ) + { + const char* pszUPDN = poRecord->GetStringSubfield( "DSID", 0, "UPDN", 0 ); + if( pszUPDN != NULL && strlen(pszUPDN) < sizeof(szUPDNUpdate) ) + strcpy( szUPDNUpdate, pszUPDN ); + } + } + + else + { + CPLDebug( "S57", + "Skipping %s record in S57Reader::ApplyUpdates().\n", + pszKey ); + } + } + + return CPLGetLastErrorType() != CE_Failure; +} + +/************************************************************************/ +/* FindAndApplyUpdates() */ +/* */ +/* Find all update files that would appear to apply to this */ +/* base file. */ +/************************************************************************/ + +int S57Reader::FindAndApplyUpdates( const char * pszPath ) + +{ + int iUpdate; + int bSuccess = TRUE; + + if( pszPath == NULL ) + pszPath = pszModuleName; + + if( !EQUAL(CPLGetExtension(pszPath),"000") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't apply updates to a base file with a different\n" + "extension than .000.\n" ); + return FALSE; + } + + for( iUpdate = 1; bSuccess; iUpdate++ ) + { + //Creaing file extension + CPLString extension; + CPLString dirname; + if( 1 <= iUpdate && iUpdate < 10 ) + { + char buf[2]; + sprintf( buf, "%i", iUpdate ); + extension.append("00"); + extension.append(buf); + dirname.append(buf); + } + else if( 10 <= iUpdate && iUpdate < 100 ) + { + char buf[3]; + sprintf( buf, "%i", iUpdate ); + extension.append("0"); + extension.append(buf); + dirname.append(buf); + } + else if( 100 <= iUpdate && iUpdate < 1000 ) + { + char buf[4]; + sprintf( buf, "%i", iUpdate ); + extension.append(buf); + dirname.append(buf); + } + + DDFModule oUpdateModule; + + //trying current dir first + char *pszUpdateFilename = + CPLStrdup(CPLResetExtension(pszPath,extension.c_str())); + + VSILFILE *file = VSIFOpenL( pszUpdateFilename, "r" ); + if( file ) + { + VSIFCloseL( file ); + bSuccess = oUpdateModule.Open( pszUpdateFilename, TRUE ); + if( bSuccess ) + CPLDebug( "S57", "Applying feature updates from %s.", + pszUpdateFilename ); + if( bSuccess ) + { + if( !ApplyUpdates( &oUpdateModule ) ) + return FALSE; + } + } + else // file is store on Primar generated cd + { + char* pszBaseFileDir = CPLStrdup(CPLGetDirname(pszPath)); + char* pszFileDir = CPLStrdup(CPLGetDirname(pszBaseFileDir)); + + CPLString remotefile(pszFileDir); + remotefile.append( "/" ); + remotefile.append( dirname ); + remotefile.append( "/" ); + remotefile.append( CPLGetBasename(pszPath) ); + remotefile.append( "." ); + remotefile.append( extension ); + bSuccess = oUpdateModule.Open( remotefile.c_str(), TRUE ); + + if( bSuccess ) + CPLDebug( "S57", "Applying feature updates from %s.", + remotefile.c_str() ); + CPLFree( pszBaseFileDir ); + CPLFree( pszFileDir ); + if( bSuccess ) + { + if( !ApplyUpdates( &oUpdateModule ) ) + return FALSE; + } + }//end for if-else + CPLFree( pszUpdateFilename ); + } + + return TRUE; +} + +/************************************************************************/ +/* GetExtent() */ +/* */ +/* Scan all the cached records collecting spatial bounds as */ +/* efficiently as possible for this transfer. */ +/************************************************************************/ + +OGRErr S57Reader::GetExtent( OGREnvelope *psExtent, int bForce ) + +{ +#define INDEX_COUNT 4 + + DDFRecordIndex *apoIndex[INDEX_COUNT]; + +/* -------------------------------------------------------------------- */ +/* If we aren't forced to get the extent say no if we haven't */ +/* already indexed the iso8211 records. */ +/* -------------------------------------------------------------------- */ + if( !bForce && !bFileIngested ) + return OGRERR_FAILURE; + + if( !Ingest() ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* We will scan all the low level vector elements for extents */ +/* coordinates. */ +/* -------------------------------------------------------------------- */ + int bGotExtents = FALSE; + int nXMin=0, nXMax=0, nYMin=0, nYMax=0; + + apoIndex[0] = &oVI_Index; + apoIndex[1] = &oVC_Index; + apoIndex[2] = &oVE_Index; + apoIndex[3] = &oVF_Index; + + for( int iIndex = 0; iIndex < INDEX_COUNT; iIndex++ ) + { + DDFRecordIndex *poIndex = apoIndex[iIndex]; + + for( int iVIndex = 0; iVIndex < poIndex->GetCount(); iVIndex++ ) + { + DDFRecord *poRecord = poIndex->GetByIndex( iVIndex ); + DDFField *poSG3D = poRecord->FindField( "SG3D" ); + DDFField *poSG2D = poRecord->FindField( "SG2D" ); + + if( poSG3D != NULL ) + { + int i, nVCount = poSG3D->GetRepeatCount(); + GInt32 *panData, nX, nY; + + panData = (GInt32 *) poSG3D->GetData(); + if( poSG3D->GetDataSize() < 3 * nVCount * (int)sizeof(int) ) + return OGRERR_FAILURE; + + for( i = 0; i < nVCount; i++ ) + { + nX = CPL_LSBWORD32(panData[i*3+1]); + nY = CPL_LSBWORD32(panData[i*3+0]); + + if( bGotExtents ) + { + nXMin = MIN(nXMin,nX); + nXMax = MAX(nXMax,nX); + nYMin = MIN(nYMin,nY); + nYMax = MAX(nYMax,nY); + } + else + { + nXMin = nXMax = nX; + nYMin = nYMax = nY; + bGotExtents = TRUE; + } + } + } + else if( poSG2D != NULL ) + { + int i, nVCount = poSG2D->GetRepeatCount(); + GInt32 *panData, nX, nY; + + panData = (GInt32 *) poSG2D->GetData(); + if( poSG2D->GetDataSize() < 2 * nVCount * (int)sizeof(int) ) + return OGRERR_FAILURE; + + for( i = 0; i < nVCount; i++ ) + { + nX = CPL_LSBWORD32(panData[i*2+1]); + nY = CPL_LSBWORD32(panData[i*2+0]); + + if( bGotExtents ) + { + nXMin = MIN(nXMin,nX); + nXMax = MAX(nXMax,nX); + nYMin = MIN(nYMin,nY); + nYMax = MAX(nYMax,nY); + } + else + { + nXMin = nXMax = nX; + nYMin = nYMax = nY; + bGotExtents = TRUE; + } + } + } + } + } + + if( !bGotExtents ) + return OGRERR_FAILURE; + else + { + psExtent->MinX = nXMin / (double) nCOMF; + psExtent->MaxX = nXMax / (double) nCOMF; + psExtent->MinY = nYMin / (double) nCOMF; + psExtent->MaxY = nYMax / (double) nCOMF; + + return OGRERR_NONE; + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57tables.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57tables.h new file mode 100644 index 000000000..fc809bf92 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57tables.h @@ -0,0 +1,413 @@ +/****************************************************************************** + * $Id: s57tables.h 9412 2006-03-28 23:13:12Z fwarmerdam $ + * + * Project: S-57 Translator + * Purpose: Inline tables. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +char *gpapszS57Classes[] = { +"\"Code\",\"ObjectClass\",\"Acronym\",\"Attribute_A\",\"Attribute_B\",\"Attribute_C\",\"Class\",\"Primitives\"", +"1,Administration area (Named),ADMARE,JRSDTN;NATION;NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"2,Airport / airfield,AIRARE,CATAIR;CONDTN;CONVIS;NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"3,Anchor berth,ACHBRT,CATACH;DATEND;DATSTA;NOBJNM;OBJNAM;PEREND;PERSTA;RADIUS;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"4,Anchorage area,ACHARE,CATACH;DATEND;DATSTA;NOBJNM;OBJNAM;PEREND;PERSTA;RESTRN;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"5,\"Beacon, cardinal\",BCNCAR,BCNSHP;CATCAM;COLOUR;COLPAT;CONDTN;CONVIS;CONRAD;DATEND;DATSTA;ELEVAT;HEIGHT;MARSYS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"6,\"Beacon, isolated danger\",BCNISD,BCNSHP;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;ELEVAT;HEIGHT;MARSYS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"7,\"Beacon, lateral\",BCNLAT,BCNSHP;CATLAM;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;ELEVAT;HEIGHT;MARSYS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"8,\"Beacon, safe water\",BCNSAW,BCNSHP;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;ELEVAT;HEIGHT;MARSYS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"9,\"Beacon, special purpose/general\",BCNSPP,BCNSHP;CATSPM;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;ELEVAT;HEIGHT;MARSYS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"10,Berth,BERTHS,DATEND;DATSTA;DRVAL1;NOBJNM;OBJNAM;PEREND;PERSTA;QUASOU;SOUACC;STATUS;VERDAT;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"11,Bridge,BRIDGE,CATBRG;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;HORACC;HORCLR;NATCON;NOBJNM;OBJNAM;VERACC;VERCCL;VERCLR;VERCOP;VERDAT;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"12,\"Building, single\",BUISGL,BUISHP;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;ELEVAT;FUNCTN;HEIGHT;NATCON;NOBJNM;OBJNAM;STATUS;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"13,Built-up area,BUAARE,CATBUA;CONDTN;CONRAD;CONVIS;HEIGHT;NOBJNM;OBJNAM;VERACC;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"14,\"Buoy, cardinal\",BOYCAR,BOYSHP;CATCAM;COLOUR;COLPAT;CONRAD;DATEND;DATSTA;MARSYS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"15,\"Buoy, installation\",BOYINB,BOYSHP;CATINB;COLOUR;COLPAT;CONRAD;DATEND;DATSTA;MARSYS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;PRODCT;STATUS;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"16,\"Buoy, isolated danger\",BOYISD,BOYSHP;COLOUR;COLPAT;CONRAD;DATEND;DATSTA;MARSYS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"17,\"Buoy, lateral\",BOYLAT,BOYSHP;CATLAM;COLOUR;COLPAT;CONRAD;DATEND;DATSTA;MARSYS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"18,\"Buoy, safe water\",BOYSAW,BOYSHP;COLOUR;COLPAT;CONRAD;DATEND;DATSTA;MARSYS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"19,\"Buoy, special purpose/general\",BOYSPP,BOYSHP;CATSPM;COLOUR;COLPAT;CONRAD;DATEND;DATSTA;MARSYS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"20,Cable area,CBLARE,CATCBL;DATEND;DATSTA;NOBJNM;OBJNAM;RESTRN;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"21,\"Cable, overhead\",CBLOHD,CATCBL;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;ICEFAC;NOBJNM;OBJNAM;STATUS;VERACC;VERCLR;VERCSA;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"22,\"Cable, submarine\",CBLSUB,BURDEP;CATCBL;CONDTN;DATEND;DATSTA;NOBJNM;OBJNAM;STATUS;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"23,Canal,CANALS,CATCAN;CONDTN;DATEND;DATSTA;HORACC;HORCLR;HORWID;NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"24,Canal bank,CANBNK,CONDTN;DATEND;DATSTA;NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"25,Cargo transshipment area,CTSARE,DATEND;DATSTA;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"26,Causeway,CAUSWY,CONDTN;NATCON;NOBJNM;OBJNAM;STATUS;WATLEV;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"27,Caution area,CTNARE,DATEND;DATSTA;PEREND;PERSTA;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"28,Checkpoint,CHKPNT,CATCHP;NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"29,Coastguard station,CGUSTA,DATEND;DATSTA;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"30,Coastline,COALNE,CATCOA;COLOUR;CONRAD;CONVIS;ELEVAT;NOBJNM;OBJNAM;VERACC;VERDAT;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"31,Contiguous zone,CONZNE,DATEND;DATSTA;NATION;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"32,Continental shelf area,COSARE,NATION;NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"33,Control point,CTRPNT,CATCTR;DATEND;DATSTA;ELEVAT;NOBJNM;OBJNAM;VERACC;VERDAT;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"34,Conveyor,CONVYR,CATCON;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;HEIGHT;LIFCAP;NOBJNM;OBJNAM;PRODCT;STATUS;VERACC;VERCLR;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"35,Crane,CRANES,CATCRN;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;HEIGHT;LIFCAP;NOBJNM;OBJNAM;ORIENT;RADIUS;STATUS;VERACC;VERCLR;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"36,Current - non - gravitational,CURENT,CURVEL;DATEND;DATSTA;NOBJNM;OBJNAM;ORIENT;PEREND;PERSTA;,INFORM;NINFOM;SCAMAX;SCAMIN;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"37,Custom zone,CUSZNE,NATION;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"38,Dam,DAMCON,CATDAM;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;HEIGHT;NATCON;NOBJNM;OBJNAM;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"39,Daymark,DAYMAR,CATSPM;COLOUR;COLPAT;DATEND;DATSTA;ELEVAT;HEIGHT;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;TOPSHP;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"40,Deep water route centerline,DWRTCL,CATTRK;DATEND;DATSTA;DRVAL1;DRVAL2;NOBJNM;OBJNAM;ORIENT;QUASOU;SOUACC;STATUS;TECSOU;TRAFIC;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"41,Deep water route part,DWRTPT,DATEND;DATSTA;DRVAL1;DRVAL2;NOBJNM;OBJNAM;ORIENT;QUASOU;SOUACC;STATUS;TECSOU;TRAFIC;VERDAT;RESTRN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"42,Depth area,DEPARE,DRVAL1;DRVAL2;QUASOU;SOUACC;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"43,Depth contour,DEPCNT,VALDCO;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"44,Distance mark,DISMAR,CATDIS;DATEND;DATSTA;NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"45,Dock area,DOCARE,CATDOC;CONDTN;DATEND;DATSTA;HORACC;HORCLR;NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"46,Dredged area,DRGARE,DRVAL1;DRVAL2;NOBJNM;OBJNAM;QUASOU;RESTRN;SOUACC;TECSOU;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"47,Dry dock,DRYDOC,CONDTN;HORACC;HORCLR;HORLEN;HORWID;NOBJNM;OBJNAM;STATUS;DRVAL1;QUASOU;SOUACC;VERDAT;,INFORM;NINFOM;SCAMAX;SCAMIN;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"48,Dumping ground,DMPGRD,CATDPG;NOBJNM;OBJNAM;RESTRN;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"49,Dyke,DYKCON,CONDTN;CONRAD;DATEND;DATSTA;HEIGHT;NATCON;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"50,Exclusive Economic Zone,EXEZNE,NATION;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"51,Fairway,FAIRWY,DATEND;DATSTA;DRVAL1;NOBJNM;OBJNAM;ORIENT;QUASOU;RESTRN;SOUACC;STATUS;TRAFIC;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"52,Fence/wall,FNCLNE,CATFNC;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;ELEVAT;HEIGHT;NATCON;NOBJNM;OBJNAM;STATUS;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"53,Ferry route,FERYRT,CATFRY;DATEND;DATSTA;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"54,Fishery zone,FSHZNE,NATION;NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"55,Fishing facility,FSHFAC,CATFIF;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"56,Fishing ground,FSHGRD,NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"57,Floating dock,FLODOC,COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;DRVAL1;HORACC;HORCLR;HORLEN;HORWID;LIFCAP;NOBJNM;OBJNAM;STATUS;VERACC;VERLEN;VERDAT;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"58,Fog signal,FOGSIG,CATFOG;DATEND;DATSTA;NOBJNM;OBJNAM;SIGFRQ;SIGGEN;SIGGRP;SIGPER;SIGSEQ;STATUS;VALMXR;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"59,Fortified structure,FORSTC,CATFOR;CONDTN;CONRAD;CONVIS;HEIGHT;NATCON;NOBJNM;OBJNAM;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"60,Free port area,FRPARE,NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"61,Gate,GATCON,CATGAT;CONDTN;DRVAL1;HORACC;HORCLR;NATCON;NOBJNM;OBJNAM;QUASOU;SOUACC;STATUS;VERACC;VERCLR;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"62,Gridiron,GRIDRN,HORACC;HORLEN;HORWID;NATCON;NOBJNM;OBJNAM;STATUS;VERACC;VERLEN;WATLEV;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"63,Harbour area (administrative),HRBARE,NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"64,Harbour facility,HRBFAC,CATHAF;CONDTN;DATEND;DATSTA;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"65,Hulk,HULKES,CATHLK;COLOUR;COLPAT;CONRAD;CONVIS;HORACC;HORLEN;HORWID;NOBJNM;OBJNAM;VERACC;VERLEN;CONDTN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"66,Ice area,ICEARE,CATICE;CONVIS;ELEVAT;HEIGHT;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"67,Incineration area,ICNARE,NOBJNM;OBJNAM;PEREND;PERSTA;RESTRN;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"68,Inshore traffic zone,ISTZNE,CATTSS;DATEND;DATSTA;RESTRN;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"69,Lake,LAKARE,ELEVAT;NOBJNM;OBJNAM;VERACC;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"70,Lake shore,LAKSHR,NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"71,Land area,LNDARE,CONDTN;NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"72,Land elevation,LNDELV,CONVIS;ELEVAT;NOBJNM;OBJNAM;VERACC;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;", +"73,Land region,LNDRGN,CATLND;NATQUA;NATSUR;NOBJNM;OBJNAM;WATLEV;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"74,Landmark,LNDMRK,CATLMK;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;ELEVAT;FUNCTN;HEIGHT;NATCON;NOBJNM;OBJNAM;STATUS;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"75,Light,LIGHTS,CATLIT;COLOUR;DATEND;DATSTA;EXCLIT;HEIGHT;LITCHR;LITVIS;MARSYS;MLTYLT;NOBJNM;OBJNAM;ORIENT;PEREND;PERSTA;SECTR1;SECTR2;SIGGRP;SIGPER;SIGSEQ;STATUS;VERACC;VALNMR;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"76,Light float,LITFLT,COLOUR;COLPAT;CONRAD;CONVIS;DATEND;DATSTA;HORACC;HORLEN;HORWID;MARSYS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"77,Light vessel,LITVES,COLOUR;COLPAT;CONRAD;CONVIS;DATEND;DATSTA;HORACC;HORLEN;HORWID;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"78,Local magnetic anomaly,LOCMAG,NOBJNM;OBJNAM;VALLMA;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"79,Lock basin,LOKBSN,DATEND;DATSTA;HORACC;HORCLR;HORLEN;HORWID;NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"80,Log pond,LOGPON,NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"81,Magnetic variation,MAGVAR,DATEND;DATSTA;RYRMGV;VALACM;VALMAG;,INFORM;NINFOM;SCAMAX;SCAMIN;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"82,Marine farm/culture,MARCUL,CATMFA;DATEND;DATSTA;EXPSOU;NOBJNM;OBJNAM;PEREND;PERSTA;QUASOU;RESTRN;SOUACC;STATUS;VALSOU;VERACC;VERDAT;VERLEN;WATLEV;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"83,Military practice area,MIPARE,CATMPA;DATEND;DATSTA;NOBJNM;OBJNAM;PEREND;PERSTA;RESTRN;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"84,Mooring/warping facility,MORFAC,BOYSHP;CATMOR;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;HEIGHT;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERDAT;VERLEN;WATLEV;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"85,Navigation line,NAVLNE,CATNAV;DATEND;DATSTA;ORIENT;PEREND;PERSTA;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"86,Obstruction,OBSTRN,CATOBS;CONDTN;EXPSOU;HEIGHT;NATCON;NATQUA;NOBJNM;OBJNAM;PRODCT;QUASOU;SOUACC;STATUS;TECSOU;VALSOU;VERACC;VERDAT;VERLEN;WATLEV;NATSUR;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"87,Offshore platform,OFSPLF,CATOFP;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;HEIGHT;NATCON;NOBJNM;OBJNAM;PRODCT;STATUS;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"88,Offshore production area,OSPARE,CATPRA;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;HEIGHT;NOBJNM;OBJNAM;PRODCT;RESTRN;STATUS;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"89,Oil barrier,OILBAR,CATOLB;CONDTN;DATEND;DATSTA;NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"90,Pile,PILPNT,CATPLE;COLOUR;COLPAT;CONDTN;CONVIS;DATEND;DATSTA;HEIGHT;NOBJNM;OBJNAM;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"91,Pilot boarding place,PILBOP,CATPIL;COMCHA;DATEND;DATSTA;NOBJNM;NPLDST;OBJNAM;PEREND;PERSTA;PILDST;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"92,Pipeline area,PIPARE,CONDTN;DATEND;DATSTA;NOBJNM;OBJNAM;PRODCT;RESTRN;STATUS;CATPIP;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"93,\"Pipeline, overhead\",PIPOHD,CATPIP;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;NOBJNM;OBJNAM;PRODCT;STATUS;VERACC;VERCLR;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"94,\"Pipeline, submarine/on land\",PIPSOL,BURDEP;CATPIP;CONDTN;DATEND;DATSTA;DRVAL1;DRVAL2;NOBJNM;OBJNAM;PRODCT;STATUS;VERACC;VERLEN;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;", +"95,Pontoon,PONTON,CONDTN;CONRAD;CONVIS;DATEND;DATSTA;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"96,Precautionary area,PRCARE,DATEND;DATSTA;RESTRN;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"97,Production / storage area,PRDARE,CATPRA;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;ELEVAT;HEIGHT;NOBJNM;OBJNAM;PRODCT;STATUS;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"98,Pylon/bridge support,PYLONS,CATPYL;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;HEIGHT;NATCON;NOBJNM;OBJNAM;VERACC;VERDAT;VERLEN;WATLEV;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"99,Radar line,RADLNE,NOBJNM;OBJNAM;ORIENT;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"100,Radar range,RADRNG,COMCHA;DATEND;DATSTA;NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"101,Radar reflector,RADRFL,HEIGHT;STATUS;VERACC;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"102,Radar station,RADSTA,CATRAS;DATEND;DATSTA;HEIGHT;NOBJNM;OBJNAM;STATUS;VERACC;VALMXR;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"103,Radar transponder beacon,RTPBCN,CATRTB;DATEND;DATSTA;NOBJNM;OBJNAM;RADWAL;SECTR1;SECTR2;SIGGRP;SIGSEQ;STATUS;VALMXR;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"104,Radio calling-in point,RDOCAL,COMCHA;DATEND;DATSTA;NOBJNM;OBJNAM;ORIENT;PEREND;PERSTA;STATUS;TRAFIC;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;", +"105,Radio station,RDOSTA,CALSGN;CATROS;COMCHA;DATEND;DATSTA;ESTRNG;NOBJNM;OBJNAM;ORIENT;PEREND;PERSTA;SIGFRQ;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"106,Railway,RAILWY,CONDTN;HEIGHT;NOBJNM;OBJNAM;STATUS;VERACC;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"107,Rapids,RAPIDS,NOBJNM;OBJNAM;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"108,Recommended route centerline,RCRTCL,CATTRK;DATEND;DATSTA;DRVAL1;DRVAL2;NOBJNM;OBJNAM;ORIENT;PEREND;PERSTA;QUASOU;SOUACC;STATUS;TECSOU;TRAFIC;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"109,Recommended track,RECTRC,CATTRK;DATEND;DATSTA;DRVAL1;DRVAL2;NOBJNM;OBJNAM;ORIENT;PEREND;PERSTA;QUASOU;SOUACC;STATUS;TECSOU;TRAFIC;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"110,Recommended Traffic Lane Part,RCTLPT,DATEND;DATSTA;ORIENT;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"111,Rescue station,RSCSTA,CATRSC;DATEND;DATSTA;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;,INFORM;NINFOM;SCAMAX;SCAMIN;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"112,Restricted area,RESARE,CATREA;DATEND;DATSTA;NOBJNM;OBJNAM;PEREND;PERSTA;RESTRN;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"113,Retro-reflector,RETRFL,COLOUR;COLPAT;HEIGHT;MARSYS;STATUS;VERACC;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"114,River,RIVERS,NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"115,River bank,RIVBNK,NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"116,Road,ROADWY,CATROD;CONDTN;NATCON;NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"117,Runway,RUNWAY,CATRUN;CONDTN;CONVIS;NATCON;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"118,Sand waves,SNDWAV,VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"119,Sea area / named water area,SEAARE,CATSEA;NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"120,Sea-plane landing area,SPLARE,NOBJNM;OBJNAM;PEREND;PERSTA;RESTRN;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;VALDCO;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"121,Seabed area,SBDARE,COLOUR;NATQUA;NATSUR;WATLEV;OBJNAM;NOBJNM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"122,Shoreline Construction,SLCONS,CATSLC;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;DATEND;DATSTA;HEIGHT;HORACC;HORCLR;HORLEN;HORWID;NATCON;NOBJNM;OBJNAM;STATUS;VERACC;VERDAT;VERLEN;WATLEV;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"123,\"Signal station, traffic\",SISTAT,CATSIT;COMCHA;DATEND;DATSTA;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"124,\"Signal station, warning\",SISTAW,CATSIW;COMCHA;DATEND;DATSTA;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"125,Silo / tank,SILTNK,BUISHP;CATSIL;COLOUR;COLPAT;CONDTN;CONRAD;CONVIS;ELEVAT;HEIGHT;NATCON;NOBJNM;OBJNAM;PRODCT;STATUS;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"126,Slope topline,SLOTOP,CATSLO;COLOUR;CONRAD;CONVIS;ELEVAT;NATCON;NATQUA;NATSUR;NOBJNM;OBJNAM;VERACC;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"127,Sloping ground,SLOGRD,CATSLO;COLOUR;CONRAD;CONVIS;NATCON;NATQUA;NATSUR;NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"128,Small craft facility,SMCFAC,CATSCF;NOBJNM;OBJNAM;PEREND;PERSTA;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"129,Sounding,SOUNDG,EXPSOU;NOBJNM;OBJNAM;QUASOU;SOUACC;TECSOU;VERDAT;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"130,Spring,SPRING,NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"131,Square,SQUARE,CONDTN;NATCON;NOBJNM;OBJNAM;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"132,Straight territorial sea baseline,STSLNE,NATION;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"133,Submarine transit lane,SUBTLN,NOBJNM;OBJNAM;RESTRN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"134,Swept Area,SWPARE,DRVAL1;QUASOU;SOUACC;TECSOU;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"135,Territorial sea area,TESARE,NATION;RESTRN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"136,Tidal stream - harmonic prediction,TS_PRH,NOBJNM;OBJNAM;T_MTOD;T_VAHC;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"137,Tidal stream - non-harmonic prediction,TS_PNH,NOBJNM;OBJNAM;T_MTOD;T_THDF;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"138,Tidal stream panel data,TS_PAD,NOBJNM;OBJNAM;TS_TSP;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"139,Tidal stream - time series,TS_TIS,NOBJNM;OBJNAM;STATUS;TIMEND;TIMSTA;T_TINT;TS_TSV;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"140,Tide - harmonic prediction,T_HMON,NOBJNM;OBJNAM;T_ACWL;T_MTOD;T_VAHC;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"141,Tide - non-harmonic prediction,T_NHMN,NOBJNM;OBJNAM;T_ACWL;T_MTOD;T_THDF;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"142,Tidal stream - time series,T_TIMS,NOBJNM;OBJNAM;T_HWLW;T_TINT;T_TSVL;TIMEND;TIMSTA;STATUS;T_ACWL;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"143,Tideway,TIDEWY,NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;Area;", +"144,Top mark,TOPMAR,COLOUR;COLPAT;HEIGHT;MARSYS;STATUS;TOPSHP;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"145,Traffic Separation Line,TSELNE,CATTSS;DATEND;DATSTA;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"146,Traffic Separation Scheme Boundary,TSSBND,CATTSS;DATEND;DATSTA;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Line;", +"147,Traffic Separation Scheme Crossing,TSSCRS,CATTSS;DATEND;DATSTA;RESTRN;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"148,Traffic Separation Scheme Lane part,TSSLPT,CATTSS;DATEND;DATSTA;ORIENT;RESTRN;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"149,Traffic Separation Scheme Roundabout,TSSRON,CATTSS;DATEND;DATSTA;RESTRN;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"150,Traffic Separation Zone,TSEZNE,CATTSS;DATEND;DATSTA;STATUS;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"151,Tunnel,TUNNEL,BURDEP;CONDTN;HORACC;HORCLR;NOBJNM;OBJNAM;STATUS;VERACC;VERCLR;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"152,Two-way route part,TWRTPT,CATTRK;DATEND;DATSTA;DRVAL1;DRVAL2;ORIENT;QUASOU;SOUACC;STATUS;TECSOU;TRAFIC;VERDAT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"153,Underwater rock / awash rock,UWTROC,EXPSOU;NATSUR;NATQUA;NOBJNM;OBJNAM;QUASOU;SOUACC;STATUS;TECSOU;VALSOU;VERDAT;WATLEV;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;", +"154,Unsurveyed area,UNSARE,,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Area;", +"155,Vegetation,VEGATN,CATVEG;CONVIS;ELEVAT;HEIGHT;NOBJNM;OBJNAM;VERACC;VERDAT;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"156,Water turbulence,WATTUR,CATWAT;NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;Area;", +"157,Waterfall,WATFAL,CONVIS;NOBJNM;OBJNAM;VERACC;VERLEN;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Line;", +"158,Weed/Kelp,WEDKLP,CATWED;NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"159,Wreck,WRECKS,CATWRK;CONRAD;CONVIS;EXPSOU;HEIGHT;NOBJNM;OBJNAM;QUASOU;SOUACC;STATUS;TECSOU;VALSOU;VERACC;VERDAT;VERLEN;WATLEV;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"160,Tidal stream - flood/ebb,TS_FEB,CAT_TS;CURVEL;DATEND;DATSTA;NOBJNM;OBJNAM;ORIENT;PEREND;PERSTA;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,G,Point;Area;", +"300,Accuracy of data,M_ACCY,HORACC;POSACC;SOUACC;VERACC;,INFORM;NINFOM;NTXTDS;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"301,Compilation scale of data,M_CSCL,CSCALE;,INFORM;NINFOM;NTXTDS;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"302,Coverage,M_COVR,CATCOV;,INFORM;NINFOM;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"303,Horizontal datum of data,M_HDAT,HORDAT;,INFORM;NINFOM;NTXTDS;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"304,Horizontal datum shift parameters,M_HOPA,HORDAT;SHIPAM;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"305,Nautical publication information,M_NPUB,,INFORM;NINFOM;NTXTDS;PICREP;PUBREF;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"306,Navigational system of marks,M_NSYS,MARSYS;ORIENT;,INFORM;NINFOM;NTXTDS;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"307,Production information,M_PROD,AGENCY;CPDATE;NATION;NMDATE;PRCTRY;,INFORM;NINFOM;NTXTDS;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"308,Quality of data,M_QUAL,CATQUA;CATZOC;DRVAL1;DRVAL2;POSACC;SOUACC;SUREND;SURSTA;TECSOU;VERDAT;,INFORM;NINFOM;NTXTDS;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"309,Sounding datum,M_SDAT,VERDAT;,INFORM;NINFOM;NTXTDS;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"310,Survey reliability,M_SREL,QUAPOS;QUASOU;SCVAL1;SCVAL2;SDISMN;SDISMX;SURATH;SUREND;SURSTA;SURTYP;TECSOU;,INFORM;NINFOM;NTXTDS;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"311,Units of measurement of data,M_UNIT,DUNITS;HUNITS;PUNITS;,INFORM;NINFOM;NTXTDS;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"312,Vertical datum of data,M_VDAT,VERDAT;,INFORM;NINFOM;NTXTDS;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,M,Area;", +"400,Aggregation,C_AGGR,NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,C,", +"401,Association,C_ASSO,NOBJNM;OBJNAM;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,C,", +"402,Stacked on/stacked under,C_STAC,,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,C,", +"500,Cartographic area,$AREAS,COLOUR;ORIENT;$SCODE;$TINTS;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,$,", +"501,Cartographic line,$LINES,$SCODE;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,$,", +"502,Cartographic symbol,$CSYMB,ORIENT;$SCALE;$SCODE;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,$,", +"503,Compass,$COMPS,$CSIZE;RYRMGV;VALACM;VALMAG;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,$,", +"504,Text,$TEXTS,$CHARS;COLOUR;$JUSTH;$JUSTV;$NTXST;$SPACE;$TXSTR;,INFORM;NINFOM;NTXTDS;PICREP;SCAMAX;SCAMIN;TXTDSC;,RECDAT;RECIND;SORDAT;SORIND;,$,", +NULL }; +char *gpapszS57attributes[] = { +"\"Code\",\"Attribute\",\"Acronym\",\"Attributetype\",\"Class\"", +"1,Agency responsible for production,AGENCY,A,F", +"2,Beacon shape,BCNSHP,E,F", +"3,Building shape,BUISHP,E,F", +"4,Buoy shape,BOYSHP,E,F", +"5,Buried depth,BURDEP,F,F", +"6,Call sign,CALSGN,S,F", +"7,Category of airport/airfield,CATAIR,L,F", +"8,Category of anchorage,CATACH,L,F", +"9,Category of bridge,CATBRG,L,F", +"10,Category of built-up area,CATBUA,E,F", +"11,Category of cable,CATCBL,E,F", +"12,Category of canal,CATCAN,E,F", +"13,Category of cardinal mark,CATCAM,E,F", +"14,Category of checkpoint,CATCHP,E,F", +"15,Category of coastline,CATCOA,E,F", +"16,Category of control point,CATCTR,E,F", +"17,Category of conveyor,CATCON,E,F", +"18,Category of coverage,CATCOV,E,F", +"19,Category of crane,CATCRN,E,F", +"20,Category of dam,CATDAM,E,F", +"21,Category of distance mark,CATDIS,E,F", +"22,Category of dock,CATDOC,E,F", +"23,Category of dumping ground,CATDPG,L,F", +"24,Category of fence/wall,CATFNC,E,F", +"25,Category of ferry,CATFRY,E,F", +"26,Category of fishing facility,CATFIF,E,F", +"27,Category of fog signal,CATFOG,E,F", +"28,Category of fortified structure,CATFOR,E,F", +"29,Category of gate,CATGAT,E,F", +"30,Category of harbour facility,CATHAF,L,F", +"31,Category of hulk,CATHLK,L,F", +"32,Category of ice,CATICE,E,F", +"33,Category of installation buoy,CATINB,E,F", +"34,Category of land region,CATLND,L,F", +"35,Category of landmark,CATLMK,L,F", +"36,Category of lateral mark,CATLAM,E,F", +"37,Category of light,CATLIT,L,F", +"38,Category of marine farm/culture,CATMFA,E,F", +"39,Category of military practice area,CATMPA,L,F", +"40,Category of mooring/warping facility,CATMOR,E,F", +"41,Category of navigation line,CATNAV,E,F", +"42,Category of obstruction,CATOBS,E,F", +"43,Category of offshore platform,CATOFP,L,F", +"44,Category of oil barrier,CATOLB,E,F", +"45,Category of pile,CATPLE,E,F", +"46,Category of pilot boarding place,CATPIL,E,F", +"47,Category of pipeline / pipe,CATPIP,L,F", +"48,Category of production area,CATPRA,E,F", +"49,Category of pylon,CATPYL,E,F", +"50,Category of quality of data,CATQUA,E,F", +"51,Category of radar station,CATRAS,E,F", +"52,Category of radar transponder beacon,CATRTB,E,F", +"53,Category of radio station,CATROS,L,F", +"54,Category of recommended track,CATTRK,E,F", +"55,Category of rescue station,CATRSC,L,F", +"56,Category of restricted area,CATREA,L,F", +"57,Category of road,CATROD,E,F", +"58,Category of runway,CATRUN,E,F", +"59,Category of sea area,CATSEA,E,F", +"60,Category of shoreline construction,CATSLC,E,F", +"61,\"Category of signal station, traffic\",CATSIT,L,F", +"62,\"Category of signal station, warning\",CATSIW,L,F", +"63,Category of silo/tank,CATSIL,E,F", +"64,Category of slope,CATSLO,E,F", +"65,Category of small craft facility,CATSCF,L,F", +"66,Category of special purpose mark,CATSPM,L,F", +"67,Category of Traffic Separation Scheme,CATTSS,E,F", +"68,Category of vegetation,CATVEG,L,F", +"69,Category of water turbulence,CATWAT,E,F", +"70,Category of weed/kelp,CATWED,E,F", +"71,Category of wreck,CATWRK,E,F", +"72,Category of zone of confidence data,CATZOC,E,F", +"73,Character spacing,$SPACE,E,$", +"74,Character specification,$CHARS,A,$", +"75,Colour,COLOUR,L,F", +"76,Colour pattern,COLPAT,L,F", +"77,Communication channel,COMCHA,A,F", +"78,Compass size,$CSIZE,F,$", +"79,Compilation date,CPDATE,A,F", +"80,Compilation scale,CSCALE,I,F", +"81,Condition,CONDTN,E,F", +"82,\"Conspicuous, Radar\",CONRAD,E,F", +"83,\"Conspicuous, visual\",CONVIS,E,F", +"84,Current velocity,CURVEL,F,F", +"85,Date end,DATEND,A,F", +"86,Date start,DATSTA,A,F", +"87,Depth range value 1,DRVAL1,F,F", +"88,Depth range value 2,DRVAL2,F,F", +"89,Depth units,DUNITS,E,F", +"90,Elevation,ELEVAT,F,F", +"91,Estimated range of transmission,ESTRNG,F,F", +"92,Exhibition condition of light,EXCLIT,E,F", +"93,Exposition of sounding,EXPSOU,E,F", +"94,Function,FUNCTN,L,F", +"95,Height,HEIGHT,F,F", +"96,Height/length units,HUNITS,E,F", +"97,Horizontal accuracy,HORACC,F,F", +"98,Horizontal clearance,HORCLR,F,F", +"99,Horizontal length,HORLEN,F,F", +"100,Horizontal width,HORWID,F,F", +"101,Ice factor,ICEFAC,F,F", +"102,Information,INFORM,S,F", +"103,Jurisdiction,JRSDTN,E,F", +"104,Justification - horizontal,$JUSTH,E,$", +"105,Justification - vertical,$JUSTV,E,$", +"106,Lifting capacity,LIFCAP,F,F", +"107,Light characteristic,LITCHR,E,F", +"108,Light visibility,LITVIS,L,F", +"109,Marks navigational - System of,MARSYS,E,F", +"110,Multiplicity of lights,MLTYLT,I,F", +"111,Nationality,NATION,A,F", +"112,Nature of construction,NATCON,L,F", +"113,Nature of surface,NATSUR,L,F", +"114,Nature of surface - qualifying terms,NATQUA,L,F", +"115,Notice to Mariners date,NMDATE,A,F", +"116,Object name,OBJNAM,S,F", +"117,Orientation,ORIENT,F,F", +"118,Periodic date end,PEREND,A,F", +"119,Periodic date start,PERSTA,A,F", +"120,Pictorial representation,PICREP,S,F", +"121,Pilot district,PILDST,S,F", +"122,Producing country,PRCTRY,A,F", +"123,Product,PRODCT,L,F", +"124,Publication reference,PUBREF,S,F", +"125,Quality of sounding measurement,QUASOU,L,F", +"126,Radar wave length,RADWAL,A,F", +"127,Radius,RADIUS,F,F", +"128,Recording date,RECDAT,A,F", +"129,Recording indication,RECIND,A,F", +"130,Reference year for magnetic variation,RYRMGV,A,F", +"131,Restriction,RESTRN,L,F", +"132,Scale maximum,SCAMAX,I,F", +"133,Scale minimum,SCAMIN,I,F", +"134,Scale value one,SCVAL1,I,F", +"135,Scale value two,SCVAL2,I,F", +"136,Sector limit one,SECTR1,F,F", +"137,Sector limit two,SECTR2,F,F", +"138,Shift parameters,SHIPAM,A,F", +"139,Signal frequency,SIGFRQ,I,F", +"140,Signal generation,SIGGEN,E,F", +"141,Signal group,SIGGRP,A,F", +"142,Signal period,SIGPER,F,F", +"143,Signal sequence,SIGSEQ,A,F", +"144,Sounding accuracy,SOUACC,F,F", +"145,Sounding distance - maximum,SDISMX,I,F", +"146,Sounding distance - minimum,SDISMN,I,F", +"147,Source date,SORDAT,A,F", +"148,Source indication,SORIND,A,F", +"149,Status,STATUS,L,F", +"150,Survey authority,SURATH,S,F", +"151,Survey date - end,SUREND,A,F", +"152,Survey date - start,SURSTA,A,F", +"153,Survey type,SURTYP,L,F", +"154,Symbol scaling factor,$SCALE,F,$", +"155,Symbolization code,$SCODE,A,$", +"156,Technique of sounding measurement,TECSOU,L,F", +"157,Text string,$TXSTR,S,$", +"158,Textual description,TXTDSC,S,F", +"159,Tidal stream - panel values,TS_TSP,A,F", +"160,\"Tidal stream, current - time series values\",TS_TSV,A,F", +"161,Tide - accuracy of water level,T_ACWL,E,F", +"162,Tide - high and low water values,T_HWLW,A,F", +"163,Tide - method of tidal prediction,T_MTOD,E,F", +"164,Tide - time and height differences,T_THDF,A,F", +"165,\"Tide, current - time interval of values\",T_TINT,I,F", +"166,Tide - time series values,T_TSVL,A,F", +"167,Tide - value of harmonic constituents,T_VAHC,A,F", +"168,Time end,TIMEND,A,F", +"169,Time start,TIMSTA,A,F", +"170,Tint,$TINTS,E,$", +"171,Topmark/daymark shape,TOPSHP,E,F", +"172,Traffic flow,TRAFIC,E,F", +"173,Value of annual change in magnetic variation,VALACM,F,F", +"174,Value of depth contour,VALDCO,F,F", +"175,Value of local magnetic anomaly,VALLMA,F,F", +"176,Value of magnetic variation,VALMAG,F,F", +"177,Value of maximum range,VALMXR,F,F", +"178,Value of nominal range,VALNMR,F,F", +"179,Value of sounding,VALSOU,F,F", +"180,Vertical accuracy,VERACC,F,F", +"181,Vertical clearance,VERCLR,F,F", +"182,\"Vertical clearance, closed\",VERCCL,F,F", +"183,\"Vertical clearance, open\",VERCOP,F,F", +"184,\"Vertical clearance, safe\",VERCSA,F,F", +"185,Vertical datum,VERDAT,E,F", +"186,Vertical length,VERLEN,F,F", +"187,Water level effect,WATLEV,E,F", +"188,Category of Tidal stream,CAT_TS,E,F", +"189,Positional accuracy units,PUNITS,E,F", +"300,Information in national language,NINFOM,S,N", +"301,Object name in national language,NOBJNM,S,N", +"302,Pilot district in national language,NPLDST,S,N", +"303,Text string in national language,$NTXST,S,N", +"304,Textual description in national language,NTXTDS,S,N", +"400,Horizontal datum,HORDAT,E,S", +"401,Positional Accuracy,POSACC,F,S", +"402,Quality of position,QUAPOS,E,S", +NULL }; diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57tables.py b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57tables.py new file mode 100644 index 000000000..79f1b23be --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57tables.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +#****************************************************************************** +# $Id: s57tables.py 2780 2001-12-17 22:33:06Z warmerda $ +# +# Project: S-57 OGR Translator +# Purpose: Script to translate s57 .csv files into C code "data" statements. +# Author: Frank Warmerdam, warmerdam@pobox.com +# +#****************************************************************************** +# Copyright (c) 2001, Frank Warmerdam +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +#****************************************************************************** +# +# $Log$ +# Revision 1.1 2001/12/17 22:33:06 warmerda +# New +# + +import sys +import os +import string + +# ----------------------------------------------------------------------------- +# EscapeLine - escape anything C-problematic in a line. +# ----------------------------------------------------------------------------- + +def EscapeLine( line ): + + line_out = '' + for lchar in line: + if lchar == '"': + line_out += '\\"' + else: + line_out += lchar + + return line_out + +# ----------------------------------------------------------------------------- +# + +if __name__ != '__main__': + print 'This module should only be used as a mainline.' + sys.exit( 1 ) + +if len(sys.argv) < 2: + directory = os.environ['S57_CSV'] +else: + directory = sys.argv[1] + + +print 'char *gpapszS57Classes[] = {' +classes = open( directory + '/s57objectclasses.csv' ).readlines() + +for line in classes: + print '"%s",' % EscapeLine(string.strip(line)) + +print 'NULL };' + +print 'char *gpapszS57attributes[] = {' +classes = open( directory + '/s57attributes.csv' ).readlines() + +for line in classes: + print '"%s",' % EscapeLine(string.strip(line)) + +print 'NULL };' + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57writer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57writer.cpp new file mode 100644 index 000000000..54750c550 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/s57/s57writer.cpp @@ -0,0 +1,1068 @@ +/****************************************************************************** + * $Id: s57writer.cpp 28348 2015-01-23 15:27:13Z rouault $ + * + * Project: S-57 Translator + * Purpose: Implements S57Writer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "s57.h" +#include "ogr_api.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: s57writer.cpp 28348 2015-01-23 15:27:13Z rouault $"); + +/************************************************************************/ +/* S57Writer() */ +/************************************************************************/ + +S57Writer::S57Writer() + +{ + poModule = NULL; + poRegistrar = NULL; + poClassContentExplorer = NULL; + + nCOMF = 10000000; + nSOMF = 10; +} + +/************************************************************************/ +/* ~S57Writer() */ +/************************************************************************/ + +S57Writer::~S57Writer() + +{ + Close(); +} + +/************************************************************************/ +/* Close() */ +/* */ +/* Close the current S-57 dataset. */ +/************************************************************************/ + +int S57Writer::Close() + +{ + if( poModule != NULL ) + { + poModule->Close(); + delete poModule; + poModule = NULL; + } + return TRUE; +} + +/************************************************************************/ +/* CreateS57File() */ +/* */ +/* Create a new output ISO8211 file with all the S-57 data */ +/* definitions. */ +/************************************************************************/ + +int S57Writer::CreateS57File( const char *pszFilename ) + +{ + DDFModule oModule; + DDFFieldDefn *poFDefn; + + Close(); + + nNext0001Index = 1; + +/* -------------------------------------------------------------------- */ +/* Create and initialize new module. */ +/* -------------------------------------------------------------------- */ + poModule = new DDFModule(); + poModule->Initialize(); + +/* -------------------------------------------------------------------- */ +/* Create the '0000' definition. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "0000", "", "0001DSIDDSIDDSSI0001DSPM0001VRIDVRIDATTVVRIDVRPCVRIDVRPTVRIDSGCCVRIDSG2DVRIDSG3D0001FRIDFRIDFOIDFRIDATTFFRIDNATFFRIDFFPCFRIDFFPTFRIDFSPCFRIDFSPT", + dsc_elementary, + dtc_char_string ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the '0001' definition. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "0001", "ISO 8211 Record Identifier", "", + dsc_elementary, dtc_bit_string, + "(b12)" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the DSID field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "DSID", "Data set identification field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "RCNM", "b11" ); + poFDefn->AddSubfield( "RCID", "b14" ); + poFDefn->AddSubfield( "EXPP", "b11" ); + poFDefn->AddSubfield( "INTU", "b11" ); + poFDefn->AddSubfield( "DSNM", "A" ); + poFDefn->AddSubfield( "EDTN", "A" ); + poFDefn->AddSubfield( "UPDN", "A" ); + poFDefn->AddSubfield( "UADT", "A(8)" ); + poFDefn->AddSubfield( "ISDT", "A(8)" ); + poFDefn->AddSubfield( "STED", "R(4)" ); + poFDefn->AddSubfield( "PRSP", "b11" ); + poFDefn->AddSubfield( "PSDN", "A" ); + poFDefn->AddSubfield( "PRED", "A" ); + poFDefn->AddSubfield( "PROF", "b11" ); + poFDefn->AddSubfield( "AGEN", "b12" ); + poFDefn->AddSubfield( "COMT", "A" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the DSSI field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "DSSI", "Data set structure information field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "DSTR", "b11" ); + poFDefn->AddSubfield( "AALL", "b11" ); + poFDefn->AddSubfield( "NALL", "b11" ); + poFDefn->AddSubfield( "NOMR", "b14" ); + poFDefn->AddSubfield( "NOCR", "b14" ); + poFDefn->AddSubfield( "NOGR", "b14" ); + poFDefn->AddSubfield( "NOLR", "b14" ); + poFDefn->AddSubfield( "NOIN", "b14" ); + poFDefn->AddSubfield( "NOCN", "b14" ); + poFDefn->AddSubfield( "NOED", "b14" ); + poFDefn->AddSubfield( "NOFA", "b14" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the DSPM field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "DSPM", "Data set parameter field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "RCNM", "b11" ); + poFDefn->AddSubfield( "RCID", "b14" ); + poFDefn->AddSubfield( "HDAT", "b11" ); + poFDefn->AddSubfield( "VDAT", "b11" ); + poFDefn->AddSubfield( "SDAT", "b11" ); + poFDefn->AddSubfield( "CSCL", "b14" ); + poFDefn->AddSubfield( "DUNI", "b11" ); + poFDefn->AddSubfield( "HUNI", "b11" ); + poFDefn->AddSubfield( "PUNI", "b11" ); + poFDefn->AddSubfield( "COUN", "b11" ); + poFDefn->AddSubfield( "COMF", "b14" ); + poFDefn->AddSubfield( "SOMF", "b14" ); + poFDefn->AddSubfield( "COMT", "A" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the VRID field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "VRID", "Vector record identifier field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "RCNM", "b11" ); + poFDefn->AddSubfield( "RCID", "b14" ); + poFDefn->AddSubfield( "RVER", "b12" ); + poFDefn->AddSubfield( "RUIN", "b11" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the VRPC field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "VRPC", "Vector Record Pointer Control field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "VPUI", "b11" ); + poFDefn->AddSubfield( "VPIX", "b12" ); + poFDefn->AddSubfield( "NVPT", "b12" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the VRPT field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "VRPT", "Vector record pointer field", "*", + dsc_array, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "NAME", "B(40)" ); + poFDefn->AddSubfield( "ORNT", "b11" ); + poFDefn->AddSubfield( "USAG", "b11" ); + poFDefn->AddSubfield( "TOPI", "b11" ); + poFDefn->AddSubfield( "MASK", "b11" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the ATTV field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "ATTV", "Vector record attribute field", "*", + dsc_array, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "ATTL", "b12" ); + poFDefn->AddSubfield( "ATVL", "A" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the SGCC field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "SGCC", "Coordinate Control Field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "CCUI", "b11" ); + poFDefn->AddSubfield( "CCIX", "b12" ); + poFDefn->AddSubfield( "CCNC", "b12" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the SG2D field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "SG2D", "2-D coordinate field", "*", + dsc_array, dtc_bit_string ); + + poFDefn->AddSubfield( "YCOO", "b24" ); + poFDefn->AddSubfield( "XCOO", "b24" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the SG3D field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "SG3D", "3-D coordinate (sounding array) field", "*", + dsc_array, dtc_bit_string ); + + poFDefn->AddSubfield( "YCOO", "b24" ); + poFDefn->AddSubfield( "XCOO", "b24" ); + poFDefn->AddSubfield( "VE3D", "b24" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the FRID field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "FRID", "Feature record identifier field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "RCNM", "b11" ); + poFDefn->AddSubfield( "RCID", "b14" ); + poFDefn->AddSubfield( "PRIM", "b11" ); + poFDefn->AddSubfield( "GRUP", "b11" ); + poFDefn->AddSubfield( "OBJL", "b12" ); + poFDefn->AddSubfield( "RVER", "b12" ); + poFDefn->AddSubfield( "RUIN", "b11" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the FOID field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "FOID", "Feature object identifier field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "AGEN", "b12" ); + poFDefn->AddSubfield( "FIDN", "b14" ); + poFDefn->AddSubfield( "FIDS", "b12" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the ATTF field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "ATTF", "Feature record attribute field", "*", + dsc_array, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "ATTL", "b12" ); + poFDefn->AddSubfield( "ATVL", "A" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the NATF field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "NATF", "Feature record national attribute field", "*", + dsc_array, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "ATTL", "b12" ); + poFDefn->AddSubfield( "ATVL", "A" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the FFPC field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "FFPC", "Feature record to feature object pointer control field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "FFUI", "b11" ); + poFDefn->AddSubfield( "FFIX", "b12" ); + poFDefn->AddSubfield( "NFPT", "b12" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the FFPT field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "FFPT", "Feature record to feature object pointer field", "*", + dsc_array, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "LNAM", "B(64)" ); + poFDefn->AddSubfield( "RIND", "b11" ); + poFDefn->AddSubfield( "COMT", "A" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the FSPC field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "FSPC", "Feature record to spatial record pointer control field", "", + dsc_vector, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "FSUI", "b11" ); + poFDefn->AddSubfield( "FSIX", "b12" ); + poFDefn->AddSubfield( "NSPT", "b12" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create the FSPT field. */ +/* -------------------------------------------------------------------- */ + poFDefn = new DDFFieldDefn(); + + poFDefn->Create( "FSPT", "Feature record to spatial record pointer field", + "*", dsc_array, dtc_mixed_data_type ); + + poFDefn->AddSubfield( "NAME", "B(40)" ); + poFDefn->AddSubfield( "ORNT", "b11" ); + poFDefn->AddSubfield( "USAG", "b11" ); + poFDefn->AddSubfield( "MASK", "b11" ); + + poModule->AddField( poFDefn ); + +/* -------------------------------------------------------------------- */ +/* Create file. */ +/* -------------------------------------------------------------------- */ + if( !poModule->Create( pszFilename ) ) + { + delete poModule; + poModule = NULL; + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* WriteDSID() */ +/************************************************************************/ + +int S57Writer::WriteDSID( int nEXPP /*1*/, int nINTU /*4*/, + const char *pszDSNM, const char *pszEDTN, + const char *pszUPDN, const char *pszUADT, + const char *pszISDT, const char *pszSTED, + int nAGEN, const char *pszCOMT, + int nNOMR, int nNOGR, int nNOLR, int nNOIN, + int nNOCN, int nNOED ) + +{ +/* -------------------------------------------------------------------- */ +/* Default values. */ +/* -------------------------------------------------------------------- */ + if( pszDSNM == NULL ) + pszDSNM = ""; + if( pszEDTN == NULL ) + pszEDTN = "2"; + if( pszUPDN == NULL ) + pszUPDN = "0"; + if( pszISDT == NULL ) + pszISDT = "20030801"; + if( pszUADT == NULL ) + pszUADT = pszISDT; + if( pszSTED == NULL ) + pszSTED = "03.1"; + if( pszCOMT == NULL ) + pszCOMT = ""; + +/* -------------------------------------------------------------------- */ +/* Add the DSID field. */ +/* -------------------------------------------------------------------- */ + DDFRecord *poRec = MakeRecord(); + /* DDFField *poField; */ + + /* poField = */ poRec->AddField( poModule->FindFieldDefn( "DSID" ) ); + + poRec->SetIntSubfield ( "DSID", 0, "RCNM", 0, 10 ); + poRec->SetIntSubfield ( "DSID", 0, "RCID", 0, 1 ); + poRec->SetIntSubfield ( "DSID", 0, "EXPP", 0, nEXPP ); + poRec->SetIntSubfield ( "DSID", 0, "INTU", 0, nINTU ); + poRec->SetStringSubfield( "DSID", 0, "DSNM", 0, pszDSNM ); + poRec->SetStringSubfield( "DSID", 0, "EDTN", 0, pszEDTN ); + poRec->SetStringSubfield( "DSID", 0, "UPDN", 0, pszUPDN ); + poRec->SetStringSubfield( "DSID", 0, "UADT", 0, pszUADT ); + poRec->SetStringSubfield( "DSID", 0, "ISDT", 0, pszISDT ); + poRec->SetStringSubfield( "DSID", 0, "STED", 0, pszSTED ); + poRec->SetIntSubfield ( "DSID", 0, "PRSP", 0, 1 ); + poRec->SetStringSubfield( "DSID", 0, "PSDN", 0, "" ); + poRec->SetStringSubfield( "DSID", 0, "PRED", 0, "2.0" ); + poRec->SetIntSubfield ( "DSID", 0, "PROF", 0, 1 ); + poRec->SetIntSubfield ( "DSID", 0, "AGEN", 0, nAGEN ); + poRec->SetStringSubfield( "DSID", 0, "COMT", 0, pszCOMT ); + +/* -------------------------------------------------------------------- */ +/* Add the DSSI record. Eventually we will need to return and */ +/* correct these when we are finished writing. */ +/* -------------------------------------------------------------------- */ + /* poField = */ poRec->AddField( poModule->FindFieldDefn( "DSSI" ) ); + + poRec->SetIntSubfield ( "DSSI", 0, "DSTR", 0, 2 ); // "Chain node" + poRec->SetIntSubfield ( "DSSI", 0, "AALL", 0, 0 ); + poRec->SetIntSubfield ( "DSSI", 0, "NALL", 0, 0 ); + poRec->SetIntSubfield ( "DSSI", 0, "NOMR", 0, nNOMR ); // Meta records + poRec->SetIntSubfield ( "DSSI", 0, "NOCR", 0, 0 ); // Cartographic records are not permitted in ENC + poRec->SetIntSubfield ( "DSSI", 0, "NOGR", 0, nNOGR ); // Geo records + poRec->SetIntSubfield ( "DSSI", 0, "NOLR", 0, nNOLR ); // Collection records + poRec->SetIntSubfield ( "DSSI", 0, "NOIN", 0, nNOIN ); // Isolated node records + poRec->SetIntSubfield ( "DSSI", 0, "NOCN", 0, nNOCN ); // Connected node records + poRec->SetIntSubfield ( "DSSI", 0, "NOED", 0, nNOED ); // Edge records + poRec->SetIntSubfield ( "DSSI", 0, "NOFA", 0, 0 ); // Face are not permitted in chain node structure + +/* -------------------------------------------------------------------- */ +/* Write out the record. */ +/* -------------------------------------------------------------------- */ + poRec->Write(); + delete poRec; + + return TRUE; +} + +/************************************************************************/ +/* WriteDSPM() */ +/************************************************************************/ + +int S57Writer::WriteDSPM( int nHDAT, int nVDAT, int nSDAT, int nCSCL ) + +{ + if( nHDAT == 0 ) + nHDAT = 2; + if( nVDAT == 0 ) + nVDAT = 17; + if( nSDAT == 0 ) + nSDAT = 23; + if( nCSCL == 0 ) + nCSCL = 52000; + +/* -------------------------------------------------------------------- */ +/* Add the DSID field. */ +/* -------------------------------------------------------------------- */ + DDFRecord *poRec = MakeRecord(); + /* DDFField *poField; */ + + /* poField = */ poRec->AddField( poModule->FindFieldDefn( "DSPM" ) ); + + poRec->SetIntSubfield ( "DSPM", 0, "RCNM", 0, 20 ); + poRec->SetIntSubfield ( "DSPM", 0, "RCID", 0, 1 ); + poRec->SetIntSubfield ( "DSPM", 0, "HDAT", 0, nHDAT ); // Must be 2 for ENC + poRec->SetIntSubfield ( "DSPM", 0, "VDAT", 0, nVDAT ); + poRec->SetIntSubfield ( "DSPM", 0, "SDAT", 0, nSDAT ); + poRec->SetIntSubfield ( "DSPM", 0, "CSCL", 0, nCSCL ); + poRec->SetIntSubfield ( "DSPM", 0, "DUNI", 0, 1 ); + poRec->SetIntSubfield ( "DSPM", 0, "HUNI", 0, 1 ); + poRec->SetIntSubfield ( "DSPM", 0, "PUNI", 0, 1 ); + poRec->SetIntSubfield ( "DSPM", 0, "COUN", 0, 1 ); + poRec->SetIntSubfield ( "DSPM", 0, "COMF", 0, nCOMF ); + poRec->SetIntSubfield ( "DSPM", 0, "SOMF", 0, nSOMF ); + +/* -------------------------------------------------------------------- */ +/* Write out the record. */ +/* -------------------------------------------------------------------- */ + poRec->Write(); + delete poRec; + + return TRUE; +} + +/************************************************************************/ +/* MakeRecord() */ +/* */ +/* Create a new empty record, and append a 0001 field with a */ +/* properly set record index in it. */ +/************************************************************************/ + +DDFRecord *S57Writer::MakeRecord() + +{ + DDFRecord *poRec = new DDFRecord( poModule ); + DDFField *poField; + unsigned char abyData[2]; + + abyData[0] = nNext0001Index % 256; + abyData[1] = (unsigned char) (nNext0001Index / 256); + + poField = poRec->AddField( poModule->FindFieldDefn( "0001" ) ); + poRec->SetFieldRaw( poField, 0, (const char *) abyData, 2 ); + + nNext0001Index++; + + return poRec; +} + +/************************************************************************/ +/* WriteGeometry() */ +/************************************************************************/ + +int S57Writer::WriteGeometry( DDFRecord *poRec, int nVertCount, + double *padfX, double *padfY, double *padfZ ) + +{ + const char *pszFieldName = "SG2D"; + DDFField *poField; + int nRawDataSize, i, nSuccess; + unsigned char *pabyRawData; + + if( padfZ != NULL ) + pszFieldName = "SG3D"; + + poField = poRec->AddField( poModule->FindFieldDefn( pszFieldName ) ); + + if( padfZ ) + nRawDataSize = 12 * nVertCount; + else + nRawDataSize = 8 * nVertCount; + + pabyRawData = (unsigned char *) CPLMalloc(nRawDataSize); + + for( i = 0; i < nVertCount; i++ ) + { + GInt32 nXCOO, nYCOO, nVE3D; + + nXCOO = CPL_LSBWORD32((GInt32) floor(padfX[i] * nCOMF + 0.5)); + nYCOO = CPL_LSBWORD32((GInt32) floor(padfY[i] * nCOMF + 0.5)); + + if( padfZ == NULL ) + { + memcpy( pabyRawData + i * 8, &nYCOO, 4 ); + memcpy( pabyRawData + i * 8 + 4, &nXCOO, 4 ); + } + else + { + nVE3D = CPL_LSBWORD32((GInt32) floor( padfZ[i] * nSOMF + 0.5 )); + memcpy( pabyRawData + i * 12, &nYCOO, 4 ); + memcpy( pabyRawData + i * 12 + 4, &nXCOO, 4 ); + memcpy( pabyRawData + i * 12 + 8, &nVE3D, 4 ); + } + } + + nSuccess = poRec->SetFieldRaw( poField, 0, + (const char *) pabyRawData, nRawDataSize ); + + CPLFree( pabyRawData ); + + return nSuccess; +} + +/************************************************************************/ +/* WritePrimitive() */ +/************************************************************************/ + +int S57Writer::WritePrimitive( OGRFeature *poFeature ) + +{ + DDFRecord *poRec = MakeRecord(); + /* DDFField *poField; */ + OGRGeometry *poGeom = poFeature->GetGeometryRef(); + +/* -------------------------------------------------------------------- */ +/* Add the VRID field. */ +/* -------------------------------------------------------------------- */ + + /* poField = */ poRec->AddField( poModule->FindFieldDefn( "VRID" ) ); + + poRec->SetIntSubfield ( "VRID", 0, "RCNM", 0, + poFeature->GetFieldAsInteger( "RCNM") ); + poRec->SetIntSubfield ( "VRID", 0, "RCID", 0, + poFeature->GetFieldAsInteger( "RCID") ); + poRec->SetIntSubfield ( "VRID", 0, "RVER", 0, 1 ); + poRec->SetIntSubfield ( "VRID", 0, "RUIN", 0, 1 ); + +/* -------------------------------------------------------------------- */ +/* Handle simple point. */ +/* -------------------------------------------------------------------- */ + if( poGeom != NULL && wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) + { + double dfX, dfY, dfZ; + OGRPoint *poPoint = (OGRPoint *) poGeom; + + CPLAssert( poFeature->GetFieldAsInteger( "RCNM") == RCNM_VI + || poFeature->GetFieldAsInteger( "RCNM") == RCNM_VC ); + + dfX = poPoint->getX(); + dfY = poPoint->getY(); + dfZ = poPoint->getZ(); + + if( dfZ == 0.0 ) + WriteGeometry( poRec, 1, &dfX, &dfY, NULL ); + else + WriteGeometry( poRec, 1, &dfX, &dfY, &dfZ ); + } + +/* -------------------------------------------------------------------- */ +/* For multipoints we assuming SOUNDG, and write out as SG3D. */ +/* -------------------------------------------------------------------- */ + else if( poGeom != NULL + && wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint ) + { + OGRMultiPoint *poMP = (OGRMultiPoint *) poGeom; + int i, nVCount = poMP->getNumGeometries(); + double *padfX, *padfY, *padfZ; + + CPLAssert( poFeature->GetFieldAsInteger( "RCNM") == RCNM_VI + || poFeature->GetFieldAsInteger( "RCNM") == RCNM_VC ); + + padfX = (double *) CPLMalloc(sizeof(double) * nVCount); + padfY = (double *) CPLMalloc(sizeof(double) * nVCount); + padfZ = (double *) CPLMalloc(sizeof(double) * nVCount); + + for( i = 0; i < nVCount; i++ ) + { + OGRPoint *poPoint = (OGRPoint *) poMP->getGeometryRef( i ); + padfX[i] = poPoint->getX(); + padfY[i] = poPoint->getY(); + padfZ[i] = poPoint->getZ(); + } + + WriteGeometry( poRec, nVCount, padfX, padfY, padfZ ); + + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + } + +/* -------------------------------------------------------------------- */ +/* Handle LINESTRINGs (edge) geometry. */ +/* -------------------------------------------------------------------- */ + else if( poGeom != NULL + && wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) + { + OGRLineString *poLS = (OGRLineString *) poGeom; + int i, nVCount = poLS->getNumPoints(); + double *padfX, *padfY; + + CPLAssert( poFeature->GetFieldAsInteger( "RCNM") == RCNM_VE ); + + padfX = (double *) CPLMalloc(sizeof(double) * nVCount); + padfY = (double *) CPLMalloc(sizeof(double) * nVCount); + + for( i = 0; i < nVCount; i++ ) + { + padfX[i] = poLS->getX(i); + padfY[i] = poLS->getY(i); + } + + if (nVCount) + WriteGeometry( poRec, nVCount, padfX, padfY, NULL ); + + CPLFree( padfX ); + CPLFree( padfY ); + + } + +/* -------------------------------------------------------------------- */ +/* edge node linkages. */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetDefnRef()->GetFieldIndex( "NAME_RCNM_0" ) >= 0 ) + { + /* DDFField *poField; */ + char szName[5]; + int nRCID; + + CPLAssert( poFeature->GetFieldAsInteger( "NAME_RCNM_0") == RCNM_VC ); + + /* poField = */ poRec->AddField( poModule->FindFieldDefn( "VRPT" ) ); + + nRCID = poFeature->GetFieldAsInteger( "NAME_RCID_0"); + szName[0] = RCNM_VC; + szName[1] = nRCID & 0xff; + szName[2] = (char) ((nRCID & 0xff00) >> 8); + szName[3] = (char) ((nRCID & 0xff0000) >> 16); + szName[4] = (char) ((nRCID & 0xff000000) >> 24); + + poRec->SetStringSubfield( "VRPT", 0, "NAME", 0, szName, 5 ); + poRec->SetIntSubfield ( "VRPT", 0, "ORNT", 0, + poFeature->GetFieldAsInteger( "ORNT_0") ); + poRec->SetIntSubfield ( "VRPT", 0, "USAG", 0, + poFeature->GetFieldAsInteger( "USAG_0") ); + poRec->SetIntSubfield ( "VRPT", 0, "TOPI", 0, + poFeature->GetFieldAsInteger( "TOPI_0") ); + poRec->SetIntSubfield ( "VRPT", 0, "MASK", 0, + poFeature->GetFieldAsInteger( "MASK_0") ); + + nRCID = poFeature->GetFieldAsInteger( "NAME_RCID_1"); + szName[0] = RCNM_VC; + szName[1] = nRCID & 0xff; + szName[2] = (char) ((nRCID & 0xff00) >> 8); + szName[3] = (char) ((nRCID & 0xff0000) >> 16); + szName[4] = (char) ((nRCID & 0xff000000) >> 24); + + poRec->SetStringSubfield( "VRPT", 0, "NAME", 1, szName, 5 ); + poRec->SetIntSubfield ( "VRPT", 0, "ORNT", 1, + poFeature->GetFieldAsInteger( "ORNT_1") ); + poRec->SetIntSubfield ( "VRPT", 0, "USAG", 1, + poFeature->GetFieldAsInteger( "USAG_1") ); + poRec->SetIntSubfield ( "VRPT", 0, "TOPI", 1, + poFeature->GetFieldAsInteger( "TOPI_1") ); + poRec->SetIntSubfield ( "VRPT", 0, "MASK", 1, + poFeature->GetFieldAsInteger( "MASK_1") ); + } + +/* -------------------------------------------------------------------- */ +/* Write out the record. */ +/* -------------------------------------------------------------------- */ + poRec->Write(); + delete poRec; + + return TRUE; +} + +/************************************************************************/ +/* GetHEXChar() */ +/************************************************************************/ + +static char GetHEXChar( const char *pszSrcHEXString ) + +{ + int nResult = 0; + + if( pszSrcHEXString[0] == '\0' || pszSrcHEXString[1] == '\0' ) + return (char) 0; + + if( pszSrcHEXString[0] >= '0' && pszSrcHEXString[0] <= '9' ) + nResult += (pszSrcHEXString[0] - '0') * 16; + else if( pszSrcHEXString[0] >= 'a' && pszSrcHEXString[0] <= 'f' ) + nResult += (pszSrcHEXString[0] - 'a' + 10) * 16; + else if( pszSrcHEXString[0] >= 'A' && pszSrcHEXString[0] <= 'F' ) + nResult += (pszSrcHEXString[0] - 'A' + 10) * 16; + + if( pszSrcHEXString[1] >= '0' && pszSrcHEXString[1] <= '9' ) + nResult += pszSrcHEXString[1] - '0'; + else if( pszSrcHEXString[1] >= 'a' && pszSrcHEXString[1] <= 'f' ) + nResult += pszSrcHEXString[1] - 'a' + 10; + else if( pszSrcHEXString[1] >= 'A' && pszSrcHEXString[1] <= 'F' ) + nResult += pszSrcHEXString[1] - 'A' + 10; + + return (char) nResult; +} + +/************************************************************************/ +/* WriteCompleteFeature() */ +/************************************************************************/ + +int S57Writer::WriteCompleteFeature( OGRFeature *poFeature ) + +{ + OGRFeatureDefn *poFDefn = poFeature->GetDefnRef(); + +/* -------------------------------------------------------------------- */ +/* We handle primitives in a seperate method. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(poFDefn->GetName(),OGRN_VI) + || EQUAL(poFDefn->GetName(),OGRN_VC) + || EQUAL(poFDefn->GetName(),OGRN_VE) ) + return WritePrimitive( poFeature ); + +/* -------------------------------------------------------------------- */ +/* Create the record. */ +/* -------------------------------------------------------------------- */ + DDFRecord *poRec = MakeRecord(); + +/* -------------------------------------------------------------------- */ +/* Add the FRID. */ +/* -------------------------------------------------------------------- */ + DDFField *poField; + + poField = poRec->AddField( poModule->FindFieldDefn( "FRID" ) ); + + poRec->SetIntSubfield ( "FRID", 0, "RCNM", 0, 100 ); + poRec->SetIntSubfield ( "FRID", 0, "RCID", 0, + poFeature->GetFieldAsInteger( "RCID" ) ); + poRec->SetIntSubfield ( "FRID", 0, "PRIM", 0, + poFeature->GetFieldAsInteger( "PRIM" ) ); + poRec->SetIntSubfield ( "FRID", 0, "GRUP", 0, + poFeature->GetFieldAsInteger( "GRUP") ); + poRec->SetIntSubfield ( "FRID", 0, "OBJL", 0, + poFeature->GetFieldAsInteger( "OBJL") ); + poRec->SetIntSubfield ( "FRID", 0, "RVER", 0, 1 ); /* always new insert*/ + poRec->SetIntSubfield ( "FRID", 0, "RUIN", 0, 1 ); + +/* -------------------------------------------------------------------- */ +/* Add the FOID */ +/* -------------------------------------------------------------------- */ + poField = poRec->AddField( poModule->FindFieldDefn( "FOID" ) ); + + poRec->SetIntSubfield ( "FOID", 0, "AGEN", 0, + poFeature->GetFieldAsInteger( "AGEN") ); + poRec->SetIntSubfield ( "FOID", 0, "FIDN", 0, + poFeature->GetFieldAsInteger( "FIDN") ); + poRec->SetIntSubfield ( "FOID", 0, "FIDS", 0, + poFeature->GetFieldAsInteger( "FIDS") ); + +/* -------------------------------------------------------------------- */ +/* ATTF support. */ +/* -------------------------------------------------------------------- */ + + if( poRegistrar != NULL + && poClassContentExplorer->SelectClass( poFeature->GetDefnRef()->GetName() ) + && !WriteATTF( poRec, poFeature ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Add the FSPT if needed. */ +/* -------------------------------------------------------------------- */ + if( poFeature->IsFieldSet( poFeature->GetFieldIndex("NAME_RCNM") ) ) + { + int nItemCount, i; + const int *panRCNM, *panRCID, *panORNT, *panUSAG, *panMASK; + unsigned char *pabyRawData; + int nRawDataSize; + + panRCNM = poFeature->GetFieldAsIntegerList( "NAME_RCNM", &nItemCount ); + panRCID = poFeature->GetFieldAsIntegerList( "NAME_RCID", &nItemCount ); + panORNT = poFeature->GetFieldAsIntegerList( "ORNT", &nItemCount ); + panUSAG = poFeature->GetFieldAsIntegerList( "USAG", &nItemCount ); + panMASK = poFeature->GetFieldAsIntegerList( "MASK", &nItemCount ); + + CPLAssert( sizeof(int) == sizeof(GInt32) ); + + nRawDataSize = nItemCount * 8; + pabyRawData = (unsigned char *) CPLMalloc(nRawDataSize); + + for( i = 0; i < nItemCount; i++ ) + { + GInt32 nRCID = CPL_LSBWORD32(panRCID[i]); + + pabyRawData[i*8 + 0] = (GByte) panRCNM[i]; + memcpy( pabyRawData + i*8 + 1, &nRCID, 4 ); + pabyRawData[i*8 + 5] = (GByte) panORNT[i]; + pabyRawData[i*8 + 6] = (GByte) panUSAG[i]; + pabyRawData[i*8 + 7] = (GByte) panMASK[i]; + } + + poField = poRec->AddField( poModule->FindFieldDefn( "FSPT" ) ); + poRec->SetFieldRaw( poField, 0, + (const char *) pabyRawData, nRawDataSize ); + CPLFree( pabyRawData ); + } + +/* -------------------------------------------------------------------- */ +/* Add the FFPT if needed. */ +/* -------------------------------------------------------------------- */ + char **papszLNAM_REFS = poFeature->GetFieldAsStringList( "LNAM_REFS" ); + + if( CSLCount(papszLNAM_REFS) > 0 ) + { + int i, nRefCount = CSLCount(papszLNAM_REFS); + const int *panRIND = + poFeature->GetFieldAsIntegerList( "FFPT_RIND", NULL ); + + poRec->AddField( poModule->FindFieldDefn( "FFPT" ) ); + + for( i = 0; i < nRefCount; i++ ) + { + char szLNAM[9]; + + if( strlen(papszLNAM_REFS[i]) < 16 ) + continue; + + // AGEN + szLNAM[1] = GetHEXChar( papszLNAM_REFS[i] + 0 ); + szLNAM[0] = GetHEXChar( papszLNAM_REFS[i] + 2 ); + + // FIDN + szLNAM[5] = GetHEXChar( papszLNAM_REFS[i] + 4 ); + szLNAM[4] = GetHEXChar( papszLNAM_REFS[i] + 6 ); + szLNAM[3] = GetHEXChar( papszLNAM_REFS[i] + 8 ); + szLNAM[2] = GetHEXChar( papszLNAM_REFS[i] + 10 ); + + // FIDS + szLNAM[7] = GetHEXChar( papszLNAM_REFS[i] + 12 ); + szLNAM[6] = GetHEXChar( papszLNAM_REFS[i] + 14 ); + + szLNAM[8] = '\0'; + + poRec->SetStringSubfield( "FFPT", 0, "LNAM", i, + (char *) szLNAM, 8 ); + poRec->SetIntSubfield( "FFPT", 0, "RIND", i, + panRIND[i] ); + } + } + +/* -------------------------------------------------------------------- */ +/* Write out the record. */ +/* -------------------------------------------------------------------- */ + poRec->Write(); + delete poRec; + + return TRUE; +} + +/************************************************************************/ +/* SetClassBased() */ +/************************************************************************/ + +void S57Writer::SetClassBased( S57ClassRegistrar * poReg, + S57ClassContentExplorer* poClassContentExplorerIn ) + +{ + poRegistrar = poReg; + poClassContentExplorer = poClassContentExplorerIn; +} + +/************************************************************************/ +/* WriteATTF() */ +/************************************************************************/ + +int S57Writer::WriteATTF( DDFRecord *poRec, OGRFeature *poFeature ) +{ + int nRawSize=0, nACount = 0; + char achRawData[5000]; + char **papszAttrList; + + CPLAssert( poRegistrar != NULL ); + +/* -------------------------------------------------------------------- */ +/* Loop over all attributes. */ +/* -------------------------------------------------------------------- */ + papszAttrList = poClassContentExplorer->GetAttributeList(NULL); + + for( int iAttr = 0; papszAttrList[iAttr] != NULL; iAttr++ ) + { + int iField = poFeature->GetFieldIndex( papszAttrList[iAttr] ); + OGRFieldType eFldType = + poFeature->GetDefnRef()->GetFieldDefn(iField)->GetType(); + int nATTLInt; + GUInt16 nATTL; + const char *pszATVL; + + if( iField < 0 ) + continue; + + if( !poFeature->IsFieldSet( iField ) ) + continue; + + nATTLInt = poRegistrar->FindAttrByAcronym( papszAttrList[iAttr] ); + if( nATTLInt == -1 ) + continue; + + nATTL = (GUInt16)nATTLInt; + nATTL = CPL_LSBWORD16( nATTL ); + memcpy( achRawData + nRawSize, &nATTL, 2 ); + nRawSize += 2; + + pszATVL = poFeature->GetFieldAsString( iField ); + + // Special hack to handle special "empty" marker in integer fields. + if( atoi(pszATVL) == EMPTY_NUMBER_MARKER + && (eFldType == OFTInteger || eFldType == OFTReal) ) + pszATVL = ""; + + // Watch for really long data. + if( strlen(pszATVL) + nRawSize + 10 > sizeof(achRawData) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too much ATTF data for fixed buffer size." ); + return FALSE; + } + + // copy data into record buffer. + memcpy( achRawData + nRawSize, pszATVL, strlen(pszATVL) ); + nRawSize += strlen(pszATVL); + achRawData[nRawSize++] = DDF_UNIT_TERMINATOR; + + nACount++; + } + +/* -------------------------------------------------------------------- */ +/* If we got no attributes, return without adding ATTF. */ +/* -------------------------------------------------------------------- */ + if( nACount == 0 ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Write the new field value. */ +/* -------------------------------------------------------------------- */ + DDFField *poField; + + poField = poRec->AddField( poModule->FindFieldDefn( "ATTF" ) ); + + return poRec->SetFieldRaw( poField, 0, achRawData, nRawSize ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/GNUmakefile new file mode 100644 index 000000000..30ba239dc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrsdedriver.o ogrsdedatasource.o ogrsdelayer.o + +CPPFLAGS := $(SDE_INC) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_sde.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/drv_sde.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/drv_sde.html new file mode 100644 index 000000000..b93c9dbed --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/drv_sde.html @@ -0,0 +1,136 @@ + + +ESRI ArcSDE + + + + +

    ESRI ArcSDE

    + +OGR optionally supports reading ESRI ArcSDE database instances. ArcSDE is +a middleware spatial solution for storing spatial data in a variety of backend +relational databases. The OGR ArcSDE driver depends on being built with the +ESRI provided ArcSDE client libraries.

    + +ArcSDE instances are accessed with a datasource name of the following +form. The server, instance, username and password fields are required. The instance +is the port number of the SDE server, which generally defaults to 5151. +If the layer parameter is specified then the SDE driver is able to skip reading +the summary metadata for each layer; skipping this step can be a significant time savings.

    + +Note: Only GDAL 1.6+ supports querying against versions and write +operations. Older versions only support querying against the base (SDE.DEFAULT) version +and no writing operations.

    + +

    +  SDE:server,instance,database,username,password[,layer]
    +
    + +To specify a version to query against, you *must* specify a layer as well. +The SDE.DEFAULT version will be used when no version name is specified.

    +

    +  SDE:server,instance,database,username,password,layer,[version]
    +
    + +You can also request to create a new version if it does not already exist. +If the child version already exists, it will be used unless the SDE_VERSIONOVERWRITE +environment variable is set to "TRUE". In that case, the version will be deleted +and recreated.

    +

    +  SDE:server,instance,database,username,password,layer,[parentversion],[childversion]
    +
    + +The OGR ArcSDE driver does not support reading CAD data (treated as BLOB +attribute), annotation properties, measure values at vertices, or raster data. +The ExecuteSQL() method does not get passed through to the underlying +database. For now it is interpreted by the limited OGR SQL handler. +Spatial indexes are used to accelerate spatial queries.

    + +The driver has been tested with ArcSDE 9.x, and should work with newer +versions, as well as ArcSDE 8.2 or 8.3. Both 2D and 3D geometries are +supported. Curve geometries are approximated as line strings (actually +still TODO).

    + +ArcSDE is generally sensitive to case-specific, fully-qualified tablenames. +While you may be able to use short names for some operations, others (notably deleting) +will require a fully-qualified name. Because of this fact, it is generally best +to always use fully-qualified table names.

    + +

    Layer Creation Options

    + +
      +
    • + OVERWRITE: This can be set to allow an existing layer to be + overwritten during the layer creation process. If set, and the value + is not "NO", the layer will first be deleted prior to creating a new + layer of the same name as an existing layer. Set to "NO" explicitly, + or do not include the option to treat attempts to create new layers + which collide with existing layers of the same name as an error. + Off by default. +
    • +
    • + GEOMETRY_NAME: By default OGR creates new layers with the + geometry (feature) column named `SHAPE'. If you wish to use a different + name, it can be supplied with the GEOMETRY_NAME layer creation option. +
    • +
    • + SDE_FID: Can be set to override the default name of the feature + ID column. The default is "OBJECTID". +
    • +
    • + SDE_KEYWORD: The DBTUNE keyword with which to create the layer. + Defaults to "DEFAULTS". +
    • +
    • + SDE_DESCRIPTION: The text description of the layer. Defaults to + "Created by GDAL/OGR 1.6" (Also used as the version description when + creating a new child version from a parent version.) +
    • +
    • + SDE_MULTIVERSION: If this creation option is set is set to + "FALSE", multi-versioning will be disabled for the layer at creation + time. By default, multiversion tables are created when layers are created + on an SDE datasource. +
    • +
    • + USE_NSTRING: If this option is set to "TRUE" then string fields + will be created as type NSTRING. This option was added for GDAL/OGR + 1.9.0. +
    • +
    + + +

    Environment variables

    + +
      +
    • OGR_SDE_GETLAYERTYPE: This may be "TRUE" to determine the + geometry type from the database. Otherwise, the SDE driver will always + return an Unknown geometry type. +
    • +
    • OGR_SDE_SEARCHORDER: This may be "ATTRIBUTE_FIRST" to tell ArcSDE + to filter based on attributes *before* using a spatial filter or + "SPATIAL_FIRST" to use the spatial filter. By default, it uses the + spatial filter first. +
    • +
    • + SDE_VERSIONOVERWRITE: If set to "TRUE", the specified child + version will be deleted before being recreated. Note that this action + does nothing to reconsile any edits that existed on that version before + doing so and essentially throws them away. +
    • +
    • + OGR_SDE_USE_NSTRING: If this option is set to "TRUE" then string + fields will be created as type NSTRING. This option was added for + GDAL/OGR 1.9.0. +
    • + +
    • + + +
    + +

    Examples

    + +See the
    ogr_sde.py test script for some example connection strings and usage of the driver.

    + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/makefile.vc new file mode 100644 index 000000000..a4f0796bc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/makefile.vc @@ -0,0 +1,23 @@ +OBJ = ogrsdedatasource.obj ogrsdedriver.obj ogrsdelayer.obj +EXTRAFLAGS = -I.. -I..\.. -I$(SDE_INC) + +PLUGIN_DLL = ogr_SDE.dll + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll $(LDEBUG) /out:$(PLUGIN_DLL) $(OBJ) \ + $(GDALLIB) $(SDE_LIB) + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/ogr_sde.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/ogr_sde.h new file mode 100644 index 000000000..3ed97463a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/ogr_sde.h @@ -0,0 +1,237 @@ +/****************************************************************************** + * $Id: ogr_sde.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for OGR SDE driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * Copyright (c) 2008, Shawn Gervais + * Copyright (c) 2008, Howard Butler + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SDE_H_INCLUDED +#define _OGR_SDE_H_INCLUDED + +#include "ogrsf_frmts.h" + +#include /* ESRI SDE Client Includes */ +#include +#include +#include "cpl_string.h" + +#define OGR_SDE_LAYER_CO_GRID1 1000 +#define OGR_SDE_LAYER_CO_GRID2 0 +#define OGR_SDE_LAYER_CO_GRID3 0 +#define OGR_SDE_LAYER_CO_INIT_FEATS 50 +#define OGR_SDE_LAYER_CO_AVG_PTS 5 + +/************************************************************************/ +/* OGRSDELayer */ +/************************************************************************/ + + +class OGRSDEDataSource; + +class OGRSDELayer : public OGRLayer +{ + protected: + OGRFeatureDefn *poFeatureDefn; + + // Layer spatial reference system, and srid. + OGRSpatialReference *poSRS; + + char *pszOwnerName; + char *pszDbTableName; + + int bUpdateAccess; + int bVersioned; + int bPreservePrecision; + + CPLString osAttributeFilter; + + int bQueryInstalled; + int bQueryActive; + + SE_STREAM hStream; + + int bHaveLayerInfo; + SE_LAYERINFO hLayerInfo; + SE_COORDREF hCoordRef; + + OGRSDEDataSource *poDS; + + int iFIDColumn; + LONG nFIDColumnType; + + int nNextFID; + int iNextFIDToWrite; + + int iShapeColumn; + + int bUseNSTRING; + + + char **papszAllColumns; + std::vector anFieldMap; // SDE index of OGR field. + std::vector anFieldTypeMap; // SDE type + + int InstallQuery( int ); + OGRFeature *TranslateSDERecord(); + OGRGeometry *TranslateSDEGeometry( SE_SHAPE ); + OGRErr TranslateOGRRecord( OGRFeature *, int ); + OGRErr TranslateOGRGeometry( OGRGeometry *, SE_SHAPE *, + SE_COORDREF ); + int NeedLayerInfo(); + + // This process can be fairly expensive depending on the configuration + // of the layer in SDE. Enable this feature with OGR_SDE_GETLAYERTYPE. + OGRwkbGeometryType DiscoverLayerType(); + + public: + + OGRSDELayer( OGRSDEDataSource *, + int ); + virtual ~OGRSDELayer(); + + CPLString osFIDColumnName; + CPLString osShapeColumnName; + + int Initialize( const char *, const char *, const char * ); + + virtual void ResetReading(); + OGRErr ResetStream(); + + virtual OGRFeature *GetNextFeature(); + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + virtual OGRErr GetExtent( OGREnvelope *psExtent, int bForce ); + virtual GIntBig GetFeatureCount( int bForce ); + + virtual OGRErr SetAttributeFilter( const char *pszQuery ); + + virtual OGRErr CreateField( OGRFieldDefn *poFieldIn, + int bApproxOK ); + + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual int TestCapability( const char * ); + + // The following methods are not base class overrides + //void SetOptions( char ** ); + + void SetFIDColType( LONG nType ) + { nFIDColumnType = nType; } + void SetPrecisionFlag( int bFlag ) + { bPreservePrecision = bFlag; } + void SetUseNSTRING( int bFlag ) + { bUseNSTRING = bFlag; } +}; + +/************************************************************************/ +/* OGRSDEDataSource */ +/************************************************************************/ +class OGRSDEDataSource : public OGRDataSource +{ + OGRSDELayer **papoLayers; + int nLayers; + + char *pszName; + + int bDSUpdate; + int bDSUseVersionEdits; + int bDSVersionLocked; + + SE_CONNECTION hConnection; + LONG nState; + LONG nNextState; + SE_VERSIONINFO hVersion; + + public: + OGRSDEDataSource(); + ~OGRSDEDataSource(); + + int Open( const char *, int ); + int OpenTable( const char *pszTableName, + const char *pszFIDColumn, + const char *pszShapeColumn, + LONG nFIDColumnType ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + virtual OGRErr DeleteLayer( int ); + + int TestCapability( const char * ); + + SE_CONNECTION GetConnection() { return hConnection; } + LONG GetState() {return nState; } + LONG GetNextState() {return nNextState; } + + void IssueSDEError( int, const char * ); + + OGRErr ConvertOSRtoSDESpatRef( OGRSpatialReference *, + SE_COORDREF * ); + int IsOpenForUpdate() { return bDSUpdate; } + int UseVersionEdits() { return bDSUseVersionEdits; } + + protected: + void EnumerateSpatialTables(); + void OpenSpatialTable( const char* pszTableName ); + void CreateLayerFromRegInfo(SE_REGINFO& reginfo); + void CleanupLayerCreation(const char* pszLayerName); + int SetVersionState( const char* pszVersionName ); + int CreateVersion( const char* pszParentVersion, const char* pszChildVersion); +}; + +/************************************************************************/ +/* OGRSDEDriver */ +/************************************************************************/ + +class OGRSDEDriver : public OGRSFDriver +{ + public: + ~OGRSDEDriver(); + + const char *GetName(); + OGRDataSource *Open( const char *, int ); + + int TestCapability( const char * ); + virtual OGRDataSource *CreateDataSource( const char *pszName, + char ** = NULL); +}; + + +#endif /* ndef _OGR_PG_H_INCLUDED */ + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/ogrsdedatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/ogrsdedatasource.cpp new file mode 100644 index 000000000..42f2f60ed --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/ogrsdedatasource.cpp @@ -0,0 +1,1509 @@ +/****************************************************************************** + * $Id: ogrsdedatasource.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRSDEDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008, Shawn Gervais + * Copyright (c) 2008, Howard Butler + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sde.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "gdal.h" + +CPL_CVSID("$Id: ogrsdedatasource.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* OGRSDEDataSource() */ +/************************************************************************/ + +OGRSDEDataSource::OGRSDEDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + + bDSVersionLocked = TRUE; + bDSUpdate = FALSE; + bDSUseVersionEdits = FALSE; + + nState = SE_DEFAULT_STATE_ID; + nNextState = -2; + hConnection = NULL; + hVersion = NULL; +} + +/************************************************************************/ +/* ~OGRSDEDataSource() */ +/************************************************************************/ + +OGRSDEDataSource::~OGRSDEDataSource() + +{ + int i; + LONG nSDEErr; + char pszVersionName[SE_MAX_VERSION_LEN]; + + + // Commit our transactions if we were opened for update + if (bDSUpdate && bDSUseVersionEdits && (nNextState != -2 && nState != SE_DEFAULT_STATE_ID ) ) { + CPLDebug("OGR_SDE", "Moving states from %ld to %ld", + (long) nState, (long) nNextState); + + SE_connection_commit_transaction(hConnection); + nSDEErr = SE_state_close(hConnection, nNextState); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_state_close" ); + } + nSDEErr = SE_versioninfo_get_name(hVersion, pszVersionName); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_versioninfo_get_name" ); + } + nSDEErr = SE_version_free_lock(hConnection, pszVersionName); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_version_free_lock" ); + } + nSDEErr = SE_version_change_state(hConnection, hVersion, nNextState); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_version_change_state" ); + } + nSDEErr = SE_state_trim_tree(hConnection, nState, nNextState); + if( nSDEErr != SE_SUCCESS && nSDEErr != SE_STATE_INUSE && nSDEErr != SE_STATE_USED_BY_VERSION) + { + + IssueSDEError( nSDEErr, "SE_state_trim_tree" ); + } + + bDSVersionLocked = TRUE; + + } + + CPLFree( pszName ); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + if (hVersion != NULL) + { + SE_versioninfo_free(hVersion); + } + + if( hConnection != NULL ) + { + SE_connection_free( hConnection ); + } + + + +} + +/************************************************************************/ +/* IssueSDEError() */ +/************************************************************************/ + +void OGRSDEDataSource::IssueSDEError( int nErrorCode, + const char *pszFunction ) + +{ + char szErrorMsg[SE_MAX_MESSAGE_LENGTH+1]; + LONG nSDEErr; + char pszVersionName[SE_MAX_VERSION_LEN]; + + if( pszFunction == NULL ) + pszFunction = "SDE"; + + if (bDSUpdate && bDSUseVersionEdits && !bDSVersionLocked) { + // try to clean up our state/transaction mess if we can + nSDEErr = SE_state_delete(hConnection, nNextState); + if (nSDEErr && nSDEErr != SE_STATE_INUSE) { + SE_error_get_string( nSDEErr, szErrorMsg ); + CPLError( CE_Failure, CPLE_AppDefined, + "SE_state_delete could not complete in IssueSDEError %d/%s", + nErrorCode, szErrorMsg ); + } + nSDEErr = SE_versioninfo_get_name(hVersion, pszVersionName); + if( nSDEErr != SE_SUCCESS ) + { + SE_error_get_string( nSDEErr, szErrorMsg ); + CPLError( CE_Failure, CPLE_AppDefined, + "SE_versioninfo_get_name could not complete in IssueSDEError %d/%s", + nErrorCode, szErrorMsg ); + } + nSDEErr = SE_version_free_lock(hConnection, pszVersionName); + if( nSDEErr != SE_SUCCESS ) + { + SE_error_get_string( nSDEErr, szErrorMsg ); + CPLError( CE_Failure, CPLE_AppDefined, + "SE_version_free_lock could not complete in IssueSDEError %d/%s", + nErrorCode, szErrorMsg ); + } + nSDEErr = SE_connection_rollback_transaction(hConnection); + if (nSDEErr) { + SE_error_get_string( nSDEErr, szErrorMsg ); + CPLError( CE_Failure, CPLE_AppDefined, + "SE_connection_rollback_transaction could not complete in IssueSDEError %d/%s", + nErrorCode, szErrorMsg ); + } + } + bDSVersionLocked = TRUE; + SE_error_get_string( nErrorCode, szErrorMsg ); + + CPLError( CE_Failure, CPLE_AppDefined, + "%s: %d/%s", + pszFunction, nErrorCode, szErrorMsg ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRSDEDataSource::Open( const char * pszNewName, int bUpdate ) + +{ + CPLAssert( nLayers == 0 ); + +/* -------------------------------------------------------------------- */ +/* If we aren't prefixed with SDE: then ignore this datasource. */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(pszNewName,"SDE:",4) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Parse arguments on comma. We expect (layer is optional): */ +/* SDE:server,instance,database,username,password,layer */ +/* -------------------------------------------------------------------- */ + char **papszTokens = CSLTokenizeStringComplex( pszNewName+4, ",", + TRUE, TRUE ); + + CPLDebug( "OGR_SDE", "Open(\"%s\") revealed %d tokens.", pszNewName, + CSLCount( papszTokens ) ); + + if( CSLCount( papszTokens ) < 5 || CSLCount( papszTokens ) > 8 ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "SDE connect string had wrong number of arguments.\n" + "Expected 'SDE:server,instance,database,username,password,layer'\n" + "The layer name value is optional.\n" + "Got '%s'", + pszNewName ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Try to establish connection. */ +/* -------------------------------------------------------------------- */ + int nSDEErr; + SE_ERROR sSDEErrorInfo; + + nSDEErr = SE_connection_create( papszTokens[0], + papszTokens[1], + papszTokens[2], + papszTokens[3], + papszTokens[4], + &sSDEErrorInfo, &hConnection ); + + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_connection_create" ); + CSLDestroy( papszTokens ); + return FALSE; + } + + pszName = CPLStrdup( pszNewName ); + + bDSUpdate = bUpdate; + + // Use SDE Versioned edits by default + const char* pszVersionEdits; + pszVersionEdits = CPLGetConfigOption( "SDE_VERSIONEDITS", "TRUE" ); + if (EQUAL(pszVersionEdits,"TRUE")) { + bDSUseVersionEdits = TRUE; + } else { + bDSUseVersionEdits = FALSE; + } + + +/* -------------------------------------------------------------------- */ +/* Set unprotected concurrency policy, suitable for single */ +/* threaded access. */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_connection_set_concurrency( hConnection, + SE_UNPROTECTED_POLICY); + + if( nSDEErr != SE_SUCCESS) { + IssueSDEError( nSDEErr, NULL ); + CSLDestroy( papszTokens ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Open a selected layer only, or else treat all known spatial */ +/* tables as layers. */ +/* -------------------------------------------------------------------- */ + if ( CSLCount( papszTokens ) == 6 && *papszTokens[5] != '\0' ) + { + OpenSpatialTable( papszTokens[5] ); + } + + +/* -------------------------------------------------------------------- */ +/* Create a new version from the parent version if we were given */ +/* both the child and parent version values */ +/* -------------------------------------------------------------------- */ + + if ( CSLCount( papszTokens ) == 8 && *papszTokens[7] != '\0' ) + { + CPLDebug("OGR_SDE", "Creating child version %s from parent version %s", papszTokens[7],papszTokens[6]); + CPLDebug("OGR_SDE", "Opening layer %s", papszTokens[5]); + OpenSpatialTable( papszTokens[5] ); + nSDEErr = CreateVersion(papszTokens[6],papszTokens[7]); + bDSVersionLocked = FALSE; + if (!nSDEErr) + { + // We've already set the error + CSLDestroy( papszTokens ); + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Fetch the specified version or use SDE.DEFAULT if none is */ +/* specified. */ +/* -------------------------------------------------------------------- */ + + if ( CSLCount( papszTokens ) == 7 && *papszTokens[6] != '\0' ) + { + CPLDebug("OGR_SDE", "Setting version to %s", papszTokens[6]); + CPLDebug("OGR_SDE", "Opening layer %s", papszTokens[5]); + OpenSpatialTable( papszTokens[5] ); + nSDEErr = SetVersionState(papszTokens[6]); + if (!nSDEErr) + { + // We've already set the error + CSLDestroy( papszTokens ); + return FALSE; + } + } + else if ( CSLCount( papszTokens ) == 8 && *papszTokens[7] != '\0' ) + { + // For user-specified version names, the input is not fully qualified + // We have to append the connection's username to the version name for + // SDE to be able to find it. + char username[SE_MAX_OWNER_LEN]; + nSDEErr = SE_connection_get_user_name(hConnection, username); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_connection_get_user_name" ); + CSLDestroy( papszTokens ); + return FALSE; + } + + const char* pszVersionName= CPLSPrintf( "%s.%s", username, papszTokens[7]); + + + CPLDebug("OGR_SDE", "Setting version to %s", pszVersionName); + CPLDebug("OGR_SDE", "Opening layer %s", papszTokens[5]); + OpenSpatialTable( papszTokens[5] ); + nSDEErr = SetVersionState(pszVersionName); + if (!nSDEErr) + { + // We've already set the error + CSLDestroy( papszTokens ); + return FALSE; + } + + } + + else + { + CPLDebug("OGR_SDE", "Setting version to SDE.DEFAULT"); + nSDEErr = SetVersionState("SDE.DEFAULT"); + EnumerateSpatialTables(); + if (!nSDEErr) + { + // We've already set the error + CSLDestroy( papszTokens ); + return FALSE; + } + + } + CSLDestroy( papszTokens ); + + return TRUE; +} + + +/************************************************************************/ +/* CreateVersion() */ +/************************************************************************/ +int OGRSDEDataSource::CreateVersion( const char* pszParentVersion, const char* pszChildVersion) { + SE_VERSIONINFO hParentVersion = NULL; + SE_VERSIONINFO hChildVersion = NULL; + SE_VERSIONINFO hDummyVersion = NULL; + + LONG nSDEErr; + nSDEErr = SE_versioninfo_create(&hParentVersion); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_versioninfo_create" ); + return FALSE; + } + + nSDEErr = SE_versioninfo_create(&hChildVersion); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_versioninfo_create" ); + return FALSE; + } + + const char* pszOverwriteVersion = CPLGetConfigOption( "SDE_VERSIONOVERWRITE", "FALSE" ); + if( EQUAL(pszOverwriteVersion, "TRUE") && bDSUpdate ) { + nSDEErr = SE_version_delete(hConnection, pszChildVersion); + + // if the version didn't exist in the first place, just continue on. + if( nSDEErr != SE_SUCCESS && nSDEErr != SE_VERSION_NOEXIST) + { + IssueSDEError( nSDEErr, "SE_version_delete" ); + return FALSE; + } + } + + // Attempt to use the child version if it is there. + nSDEErr = SE_version_get_info(hConnection, pszChildVersion, hChildVersion); + if( nSDEErr != SE_SUCCESS) + { + if (nSDEErr != SE_VERSION_NOEXIST) { + IssueSDEError( nSDEErr, "SE_version_get_info child" ); + return FALSE; + } + } else { + SE_versioninfo_free(hParentVersion); + SE_versioninfo_free(hChildVersion); + return TRUE; + } + + if (!bDSUpdate) { + CPLError( CE_Failure, CPLE_AppDefined, + "The version %s does not exist and cannot be created because the datasource is not in update mode", + pszChildVersion); + return FALSE; + } + + nSDEErr = SE_version_get_info(hConnection, pszParentVersion, hParentVersion); + if( nSDEErr != SE_SUCCESS ) + { + // this usually denotes incongruent versions of the client + // and server. If this is the case, we're going to attempt to + // not do versioned queries at all. + if ( nSDEErr == SE_INVALID_RELEASE ) { + CPLDebug("OGR_SDE", "nState was set to SE_INVALID_RELEASE\n\n\n"); + // leave nState set to SE_DEFAULT_STATE_ID + SE_versioninfo_free(hParentVersion); + hParentVersion = NULL; + IssueSDEError( nSDEErr, "SE_INVALID_RELEASE." + " Your client/server versions must not match or " + "you have some other major configuration problem"); + return FALSE; + + } else { + IssueSDEError( nSDEErr, "SE_version_get_info parent" ); + return FALSE; + } + } + + // Fill in details of our child version from our parent version + nSDEErr = SE_versioninfo_set_name(hChildVersion, pszChildVersion); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_versioninfo_set_name " + "Version names must be in the form \"MYVERSION\"" + "not \"SDE.MYVERSION\"" ); + return FALSE; + } + + nSDEErr = SE_versioninfo_set_access(hChildVersion, SE_VERSION_ACCESS_PUBLIC); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_versioninfo_set_access" ); + return FALSE; + } + + const char* pszDescription = CPLGetConfigOption( "SDE_DESCRIPTION", "Created by OGR" ); + nSDEErr = SE_versioninfo_set_description(hChildVersion, pszDescription); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_versioninfo_set_description" ); + return FALSE; + } + + nSDEErr = SE_versioninfo_set_parent_name(hChildVersion, pszParentVersion); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_versioninfo_set_parent_name" ); + return FALSE; + } + + LONG nStateID; + nSDEErr = SE_versioninfo_get_state_id(hParentVersion, &nStateID); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_versioninfo_get_state_id" ); + return FALSE; + } + nSDEErr = SE_versioninfo_set_state_id(hChildVersion, nStateID); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_versioninfo_set_parent_name" ); + return FALSE; + } + + nSDEErr = SE_versioninfo_create(&hDummyVersion); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_versioninfo_create" ); + return FALSE; + } + nSDEErr = SE_version_create(hConnection, hChildVersion, FALSE, hDummyVersion); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_version_create" ); + return FALSE; + } + + SE_versioninfo_free(hParentVersion); + SE_versioninfo_free(hChildVersion); + SE_versioninfo_free(hDummyVersion); + + return TRUE; +} + +/************************************************************************/ +/* SetVersionState() */ +/************************************************************************/ +int OGRSDEDataSource::SetVersionState( const char* pszVersionName ) { + + int nSDEErr; + SE_STATEINFO hCurrentStateInfo= NULL; + SE_STATEINFO hNextStateInfo= NULL; + SE_STATEINFO hDummyStateInfo = NULL; + + nSDEErr = SE_versioninfo_create(&hVersion); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_versioninfo_create" ); + return FALSE; + } + nSDEErr = SE_version_get_info(hConnection, pszVersionName, hVersion); + if( nSDEErr != SE_SUCCESS ) + { + // this usually denotes incongruent versions of the client + // and server. If this is the case, we're going to attempt to + // not do versioned queries at all. + if ( nSDEErr == SE_INVALID_RELEASE ) { + CPLDebug("OGR_SDE", "nState was set to SE_INVALID_RELEASE\n\n\n"); + // leave nState set to SE_DEFAULT_STATE_ID + SE_versioninfo_free(hVersion); + hVersion = NULL; + IssueSDEError( nSDEErr, "SE_INVALID_RELEASE." + " Your client/server versions must not match or " + "you have some other major configuration problem"); + return FALSE; + + } else { + IssueSDEError( nSDEErr, "SE_version_get_info" ); + return FALSE; + } + } + + nSDEErr = SE_versioninfo_get_state_id(hVersion, &nState); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_versioninfo_get_state_id" ); + return FALSE; + } + + + + if (bDSUpdate && bDSUseVersionEdits) { + LONG nLockCount = 0; + SE_VERSION_LOCK* pahLocks = NULL; + nSDEErr = SE_version_get_locks(hConnection, pszVersionName, &nLockCount, &pahLocks); + + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_version_get_locks" ); + return FALSE; + } + + if (nLockCount > 0) + { + // This version is already locked for edit. We can't edit this + // version right now until the lock is released. All we can do is issue + // an error. + + SE_version_free_locks(pahLocks, nLockCount); + bDSVersionLocked = TRUE; + CPLError( CE_Failure, CPLE_AppDefined, + "The %s version is already locked and open for edit", pszVersionName); + return FALSE; + } + + // So we're in update mode. We need to get the state id + // of the active version, create a child state of it to + // push our edits onto, and close the state and move the + // version to it when we're done. + + nSDEErr = SE_connection_start_transaction(hConnection); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_connection_start_transaction" ); + return FALSE; + } + + // Lock the version we're editing on so no one can change the state + // of it underneath us. SHARED_LOCK is the same lock mode that ArcGIS uses, + // and it means the state of the version cannot be moved until until we + // release the lock and move it. + nSDEErr = SE_version_lock(hConnection, pszVersionName, SE_VERSION_SHARED_LOCK); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_version_lock" ); + return FALSE; + } + nSDEErr = SE_stateinfo_create(&hCurrentStateInfo); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_stateinfo_create" ); + return FALSE; + } + nSDEErr = SE_state_get_info(hConnection, nState, hCurrentStateInfo); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_state_get_info" ); + return FALSE; + } + if (SE_stateinfo_is_open(hCurrentStateInfo)) { + CPLError( CE_Failure, CPLE_AppDefined, + "The editing state for this version is currently open. " + "It must be closed for edits before it can be opened by OGR for update. "); + return FALSE; + } + nSDEErr = SE_stateinfo_create(&hNextStateInfo); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_stateinfo_create" ); + return FALSE; + } + + nSDEErr = SE_stateinfo_create(&hDummyStateInfo); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_stateinfo_create" ); + return FALSE; + } + nSDEErr = SE_state_create(hConnection, hDummyStateInfo, nState, hNextStateInfo); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_state_create" ); + return FALSE; + } + + nSDEErr = SE_stateinfo_get_id(hNextStateInfo, &nNextState); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_stateinfo_get_id" ); + return FALSE; + } + nSDEErr = SE_state_open(hConnection, nNextState); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_state_open" ); + return FALSE; + } + SE_stateinfo_free(hDummyStateInfo); + SE_stateinfo_free(hCurrentStateInfo); + SE_stateinfo_free(hNextStateInfo); + + } + return TRUE; + + +} +/************************************************************************/ +/* OpenTable() */ +/************************************************************************/ + +int OGRSDEDataSource::OpenTable( const char *pszTableName, + const char *pszFIDColumn, + const char *pszShapeColumn, + LONG nFIDColType ) + +{ +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRSDELayer *poLayer; + + poLayer = new OGRSDELayer( this, bDSUpdate ); + + if( !poLayer->Initialize( pszTableName, pszFIDColumn, pszShapeColumn ) ) + { + delete poLayer; + return FALSE; + } + + poLayer->SetFIDColType( nFIDColType ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRSDELayer **) + CPLRealloc( papoLayers, sizeof(OGRSDELayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + + return TRUE; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +OGRErr OGRSDEDataSource::DeleteLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Blow away our OGR structures related to the layer. This is */ +/* pretty dangerous if anything has a reference to this layer! */ +/* -------------------------------------------------------------------- */ + + + OGRSDELayer* poLayer = (OGRSDELayer*) papoLayers[iLayer]; + + CPLString osGeometryName = poLayer->osShapeColumnName; + CPLString osLayerName = poLayer->GetLayerDefn()->GetName(); + + CPLDebug( "OGR_SDE", + "DeleteLayer(%s,%s)", + osLayerName.c_str(), + osGeometryName.c_str() ); + + delete papoLayers[iLayer]; + memmove( papoLayers + iLayer, papoLayers + iLayer + 1, + sizeof(void *) * (nLayers - iLayer - 1) ); + nLayers--; + +/* -------------------------------------------------------------------- */ +/* Remove from the database. */ +/* -------------------------------------------------------------------- */ + LONG nSDEErr; + char** paszTables; + LONG nCount; + char pszVersionName[SE_MAX_VERSION_LEN]; + + nSDEErr = SE_layer_delete( hConnection, osLayerName.c_str(), osGeometryName.c_str()); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_layer_delete" ); + return OGRERR_FAILURE; + } + + nSDEErr = SE_registration_get_dependent_tables(hConnection, osLayerName.c_str(), &paszTables, &nCount); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_registration_get_dependent_tables" ); + return OGRERR_FAILURE; + } + + for (int i=0; iGetLayerDefn()->GetName()) + || EQUAL(pszLayerName, + papoLayers[iLayer]->GetLayerDefn()->GetName()) ) + { + if( CSLFetchBoolean( papszOptions, "OVERWRITE", FALSE ) ) + { + DeleteLayer( iLayer ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + pszLayerName ); + return NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Sometimes there are residual layers left around and we need */ +/* to blow them away. */ +/* -------------------------------------------------------------------- */ + SE_REGINFO *ahTableList; + LONG nTableListCount; + int iTable; + + nSDEErr = SE_registration_get_info_list( hConnection, &ahTableList, + &nTableListCount ); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_registration_get_info_list" ); + return NULL; + } + + for( iTable = 0; iTable < nTableListCount; iTable++ ) + { + char szTableName[SE_QUALIFIED_TABLE_NAME+1]; + szTableName[0] = '\0'; + + SE_reginfo_get_table_name( ahTableList[iTable], szTableName ); + if( EQUAL(szTableName,pszLayerName) + || EQUAL(szTableName,osFullName) ) + { + if( !CSLFetchBoolean( papszOptions, "OVERWRITE", FALSE ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Registration informatin for %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to pre-clear it.", + pszLayerName ); + return NULL; + } + + CPLDebug( "SDE", "sde_layer_delete(%s) - hidden/residual layer.", + osFullName.c_str() ); + + SE_layer_delete( hConnection, szTableName, "SHAPE"); + SE_registration_delete( hConnection, szTableName ); + SE_table_delete( hConnection, szTableName ); + } + } + + SE_registration_free_info_list( nTableListCount, ahTableList ); + +/* -------------------------------------------------------------------- */ +/* Get various layer creation options. */ +/* -------------------------------------------------------------------- */ + const char *pszExpectedFIDName; + const char *pszGeometryName; + const char *pszDbtuneKeyword; + const char *pszLayerDescription; + + + pszGeometryName = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME" ); + if( pszGeometryName == NULL ) + pszGeometryName = "SHAPE"; + + + pszExpectedFIDName = CPLGetConfigOption( "SDE_FID", "OBJECTID" ); + + pszDbtuneKeyword = CSLFetchNameValue( papszOptions, "SDE_KEYWORD" ); + if( pszDbtuneKeyword == NULL ) + pszDbtuneKeyword = "DEFAULTS"; + + // Set layer description + pszLayerDescription = CSLFetchNameValue( papszOptions, "SDE_DESCRIPTION" ); + if( pszLayerDescription == NULL ) + pszLayerDescription = CPLSPrintf( "Created by GDAL/OGR %s", + GDALVersionInfo( "RELEASE_NAME" ) ); + +/* -------------------------------------------------------------------- */ +/* Create a basic table with the FID column */ +/* -------------------------------------------------------------------- */ + SE_COLUMN_DEF sColumnDef; + + /* + * Setting the size and decimal_digits to 0 instructs SDE to + * use default values for the SE_INTEGER_TYPE - these might be specific + * to the underlying RDBMS. + */ + strcpy( sColumnDef.column_name, pszExpectedFIDName ); + sColumnDef.sde_type = SE_INTEGER_TYPE; + sColumnDef.size = 0; + sColumnDef.decimal_digits = 0; + sColumnDef.nulls_allowed = FALSE; + + nSDEErr = SE_table_create( hConnection, pszLayerName, 1, &sColumnDef, + pszDbtuneKeyword ); + + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_table_create" ); + return NULL; + } + + +/* -------------------------------------------------------------------- */ +/* Convert the OGRSpatialReference to a SDE coordref object */ +/* -------------------------------------------------------------------- */ + SE_COORDREF hCoordRef; + + if( ConvertOSRtoSDESpatRef( poSRS, &hCoordRef ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot create layer %s: Unable to convert " + "OGRSpatialReference to SDE SE_COORDREF.", + pszLayerName ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + +/* -------------------------------------------------------------------- */ +/* Construct the layer info necessary to spatially enable */ +/* the table. */ +/* -------------------------------------------------------------------- */ + SE_LAYERINFO hLayerInfo; + SE_ENVELOPE sLayerEnvelope; + LONG nLayerShapeTypes = 0L; + + nSDEErr = SE_layerinfo_create( hCoordRef, &hLayerInfo ); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_layerinfo_create" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + // Determine the type of geometries that this layer will allow + nLayerShapeTypes |= SE_NIL_TYPE_MASK; + + if( wkbFlatten(eType) == wkbPoint || wkbFlatten(eType) == wkbMultiPoint ) + nLayerShapeTypes |= SE_POINT_TYPE_MASK; + + else if( wkbFlatten(eType) == wkbLineString + || wkbFlatten(eType) == wkbMultiLineString ) + nLayerShapeTypes |= ( SE_LINE_TYPE_MASK | SE_SIMPLE_LINE_TYPE_MASK ); + + else if( wkbFlatten(eType) == wkbPolygon ) + { + nLayerShapeTypes |= SE_AREA_TYPE_MASK; + } + else if( wkbFlatten(eType) == wkbMultiPolygon ) + { + nLayerShapeTypes |= SE_AREA_TYPE_MASK; + nLayerShapeTypes |= SE_MULTIPART_TYPE_MASK; + } + else if( eType == wkbUnknown ) + { + nLayerShapeTypes |= ( SE_POINT_TYPE_MASK + | SE_LINE_TYPE_MASK + | SE_SIMPLE_LINE_TYPE_MASK + | SE_AREA_TYPE_MASK ); + CPLError( CE_Warning, CPLE_AppDefined, + "Warning: Creation of a wkbUnknown layer in ArcSDE will " + "result in layers which are not displayable in Arc* " + "software" ); + } + + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot create SDE layer %s with geometry type %d.", + pszLayerName, eType ); + + SE_layerinfo_free( hLayerInfo ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + nSDEErr = SE_layerinfo_set_shape_types( hLayerInfo, nLayerShapeTypes ); + if( nSDEErr != SE_SUCCESS ) + { + SE_layerinfo_free( hLayerInfo ); + IssueSDEError( nSDEErr, "SE_layerinfo_set_shape_types" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + + // Set geometry column name + nSDEErr = SE_layerinfo_set_spatial_column( hLayerInfo, pszLayerName, + pszGeometryName ); + if( nSDEErr != SE_SUCCESS ) + { + SE_layerinfo_free( hLayerInfo ); + IssueSDEError( nSDEErr, "SE_layerinfo_set_spatial_column" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + // Set creation keyword + nSDEErr = SE_layerinfo_set_creation_keyword( hLayerInfo, pszDbtuneKeyword ); + if( nSDEErr != SE_SUCCESS ) + { + SE_layerinfo_free( hLayerInfo ); + IssueSDEError( nSDEErr, "SE_layerinfo_set_creation_keyword" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + // Set layer extent based on coordinate system envelope + if( poSRS != NULL && poSRS->IsGeographic() ) + { + sLayerEnvelope.minx = -180; + sLayerEnvelope.miny = -90; + sLayerEnvelope.maxx = 180; + sLayerEnvelope.maxy = 90; + } + else + { + nSDEErr = SE_coordref_get_xy_envelope( hCoordRef, &sLayerEnvelope ); + if( nSDEErr != SE_SUCCESS ) + { + SE_layerinfo_free( hLayerInfo ); + IssueSDEError( nSDEErr, "SE_coordref_get_xy_envelope" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + } + + CPLDebug( "SDE", "Creating layer with envelope (%g,%g) to (%g,%g)", + sLayerEnvelope.minx, + sLayerEnvelope.miny, + sLayerEnvelope.maxx, + sLayerEnvelope.maxy ); + nSDEErr = SE_layerinfo_set_envelope( hLayerInfo, &sLayerEnvelope ); + if( nSDEErr != SE_SUCCESS ) + { + SE_layerinfo_free( hLayerInfo ); + IssueSDEError( nSDEErr, "SE_layerinfo_set_envelope" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + + nSDEErr = SE_layerinfo_set_description( hLayerInfo, pszLayerDescription ); + if( nSDEErr != SE_SUCCESS ) + { + SE_layerinfo_free( hLayerInfo ); + IssueSDEError( nSDEErr, "SE_layerinfo_set_description" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + + // Set grid size + nSDEErr = SE_layerinfo_set_grid_sizes( hLayerInfo, + OGR_SDE_LAYER_CO_GRID1, + OGR_SDE_LAYER_CO_GRID2, + OGR_SDE_LAYER_CO_GRID3 ); + if( nSDEErr != SE_SUCCESS ) + { + SE_layerinfo_free( hLayerInfo ); + IssueSDEError( nSDEErr, "SE_layerinfo_set_grid_sizes" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + + // Set layer coordinate reference + nSDEErr = SE_layerinfo_set_coordref( hLayerInfo, hCoordRef ); + if( nSDEErr != SE_SUCCESS ) + { + SE_layerinfo_free( hLayerInfo ); + IssueSDEError( nSDEErr, "SE_layerinfo_set_coordref" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + +/* -------------------------------------------------------------------- */ +/* Spatially enable the newly created table */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_layer_create( hConnection, hLayerInfo, + OGR_SDE_LAYER_CO_INIT_FEATS, + OGR_SDE_LAYER_CO_AVG_PTS ); + + SE_layerinfo_free( hLayerInfo ); + + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_layer_create" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Register the newly created table */ +/* -------------------------------------------------------------------- */ + char szQualifiedTable[SE_QUALIFIED_TABLE_NAME]; + SE_REGINFO hRegInfo; + + nSDEErr = SE_reginfo_create( &hRegInfo ); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_reginfo_create" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + nSDEErr = SE_registration_get_info( hConnection, pszLayerName, + hRegInfo ); + if( nSDEErr != SE_SUCCESS ) + { + SE_reginfo_free( hRegInfo ); + IssueSDEError( nSDEErr, "SE_registration_get_info" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + nSDEErr = SE_reginfo_set_creation_keyword( hRegInfo, pszDbtuneKeyword ); + if( nSDEErr != SE_SUCCESS ) + { + SE_reginfo_free( hRegInfo ); + IssueSDEError( nSDEErr, "SE_reginfo_set_creation_keyword" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + nSDEErr = SE_reginfo_set_rowid_column( hRegInfo, pszExpectedFIDName, + SE_REGISTRATION_ROW_ID_COLUMN_TYPE_SDE ); + if( nSDEErr != SE_SUCCESS ) + { + SE_reginfo_free( hRegInfo ); + IssueSDEError( nSDEErr, "SE_reginfo_set_rowid_column" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + /* + * If the layer creation option 'MULTIVERSION' is set, enable + * multi-versioning for this layer + */ + if( CSLFetchBoolean( papszOptions, "SDE_MULTIVERSION", TRUE ) ) + { + CPLDebug("OGR_SDE","Setting multiversion to true"); + nSDEErr = SE_reginfo_set_multiversion( hRegInfo, TRUE ); + if( nSDEErr != SE_SUCCESS ) + { + SE_reginfo_free( hRegInfo ); + IssueSDEError( nSDEErr, "SE_reginfo_set_multiversion" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + } + + nSDEErr = SE_registration_alter( hConnection, hRegInfo ); + if( nSDEErr != SE_SUCCESS ) + { + SE_reginfo_free( hRegInfo ); + IssueSDEError( nSDEErr, "SE_registration_alter" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + nSDEErr = SE_reginfo_get_table_name( hRegInfo, szQualifiedTable ); + if( nSDEErr != SE_SUCCESS ) + { + SE_reginfo_free( hRegInfo ); + IssueSDEError( nSDEErr, "SE_reginfo_get_table_name" ); + CleanupLayerCreation(pszLayerName); + return NULL; + } + + SE_reginfo_free( hRegInfo ); + SE_coordref_free( hCoordRef ); + + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRSDELayer *poLayer; + + poLayer = new OGRSDELayer( this, bDSUpdate ); + + if( !poLayer->Initialize( szQualifiedTable, pszExpectedFIDName, + pszGeometryName) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot initialize newly created layer \"%s\"", + pszLayerName ); + CleanupLayerCreation(pszLayerName); + delete poLayer; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Set various options on the layer. */ +/* -------------------------------------------------------------------- */ + poLayer->SetFIDColType( SE_REGISTRATION_ROW_ID_COLUMN_TYPE_SDE ); + + poLayer->SetUseNSTRING( + CSLFetchBoolean( papszOptions, "USE_NSTRING", FALSE ) ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRSDELayer **) + CPLRealloc( papoLayers, sizeof(OGRSDELayer *) * (nLayers+1) ); + + papoLayers[nLayers++] = poLayer; + + return poLayer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSDEDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) && bDSUpdate ) + return TRUE; + else if( EQUAL(pszCap,ODsCDeleteLayer) && bDSUpdate ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRSDEDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* EnumerateSpatialTables() */ +/************************************************************************/ +void OGRSDEDataSource::EnumerateSpatialTables() +{ +/* -------------------------------------------------------------------- */ +/* Fetch list of spatial tables from SDE. */ +/* -------------------------------------------------------------------- */ + SE_REGINFO *ahTableList; + LONG nTableListCount; + LONG nSDEErr; + + nSDEErr = SE_registration_get_info_list( hConnection, &ahTableList, + &nTableListCount ); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_registration_get_info_list" ); + return; + } + + CPLDebug( "OGR_SDE", + "SDE::EnumerateSpatialTables() found %d tables.", + (int) nTableListCount ); + +/* -------------------------------------------------------------------- */ +/* Process the tables, turning any appropriate ones into layers. */ +/* -------------------------------------------------------------------- */ + int iTable; + + for( iTable = 0; iTable < nTableListCount; iTable++ ) + { + ICreateLayerFromRegInfo( ahTableList[iTable] ); + } + + SE_registration_free_info_list( nTableListCount, ahTableList ); +} + +/************************************************************************/ +/* OpenSpatialTable() */ +/************************************************************************/ + +void OGRSDEDataSource::OpenSpatialTable( const char* pszTableName ) +{ + SE_REGINFO tableinfo = NULL; + LONG nSDEErr; + + CPLDebug( "OGR_SDE", "SDE::OpenSpatialTable(\"%s\").", pszTableName ); + + nSDEErr = SE_reginfo_create( &tableinfo ); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_reginfo_create" ); + } + + nSDEErr = SE_registration_get_info( hConnection, pszTableName, tableinfo ); + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_registration_get_info_list" ); + } + else + { + ICreateLayerFromRegInfo( tableinfo ); + } + + SE_reginfo_free( tableinfo ); +} + +/************************************************************************/ +/* ICreateLayerFromRegInfo() */ +/************************************************************************/ + +void OGRSDEDataSource::CreateLayerFromRegInfo( SE_REGINFO& reginfo ) +{ + char szTableName[SE_QUALIFIED_TABLE_NAME+1]; + char szIDColName[SE_MAX_COLUMN_LEN+1]; + LONG nFIDColType; + LONG nSDEErr; + + nSDEErr = SE_reginfo_get_table_name( reginfo, szTableName ); + if( nSDEErr != SE_SUCCESS ) + { + CPLDebug( "SDE", + "Ignoring reginfo '%p', no table name.", + reginfo ); + return; + } + + // Ignore non-spatial, or hidden tables. + if( !SE_reginfo_has_layer( reginfo ) || SE_reginfo_is_hidden( reginfo ) ) + { + CPLDebug( "SDE", + "Ignoring layer '%s' as it is hidden or does not have a reginfo layer.", + szTableName ); + return; + } + + CPLDebug( "OGR_SDE", "CreateLayerFromRegInfo() asked to load table \"%s\".", szTableName ); + + nSDEErr = SE_reginfo_get_rowid_column( reginfo, szIDColName, &nFIDColType ); + + if( nFIDColType == SE_REGISTRATION_ROW_ID_COLUMN_TYPE_NONE + || strlen(szIDColName) == 0 ) + { + CPLDebug( "OGR_SDE", "Unable to determine FID column for %s.", + szTableName ); + OpenTable( szTableName, NULL, NULL, nFIDColType ); + } + else + OpenTable( szTableName, szIDColName, NULL, nFIDColType ); +} + +/************************************************************************/ +/* ConvertOSRtoSDESpatRef */ +/************************************************************************/ +OGRErr OGRSDEDataSource::ConvertOSRtoSDESpatRef( OGRSpatialReference *poSRS, + SE_COORDREF *psCoordRef ) + +{ + OGRSpatialReference *poESRISRS; + char *pszWkt; + + if( SE_coordref_create( psCoordRef ) != SE_SUCCESS ) + return OGRERR_FAILURE; + + // Construct a generic SE_COORDREF if poSRS is NULL + if( poSRS == NULL ) + { + SE_ENVELOPE sGenericEnvelope; + + sGenericEnvelope.minx = -1000000; + sGenericEnvelope.miny = -1000000; + sGenericEnvelope.maxx = 1000000; + sGenericEnvelope.maxy = 1000000; + + if( SE_coordref_set_xy_by_envelope( *psCoordRef, &sGenericEnvelope ) + != SE_SUCCESS ) + { + SE_coordref_free( *psCoordRef ); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; + } + + + poESRISRS = poSRS->Clone(); + + if (poSRS->IsLocal()) { + CPLDebug("OGR_SDE", "Coordinate reference was local, using UNKNOWN for ESRI SRS description"); + if( SE_coordref_set_by_description( *psCoordRef, "UNKNOWN") != SE_SUCCESS ) + return OGRERR_FAILURE; + CPLFree(pszWkt); + return OGRERR_NONE; + } + + if( poESRISRS->morphToESRI() != OGRERR_NONE ) + { + SE_coordref_free( *psCoordRef ); + delete poESRISRS; + return OGRERR_FAILURE; + } + + if( poESRISRS->exportToWkt( &pszWkt ) != OGRERR_NONE ) + { + SE_coordref_free( *psCoordRef ); + delete poESRISRS; + return OGRERR_FAILURE; + } + + delete poESRISRS; + + if( SE_coordref_set_by_description( *psCoordRef, pszWkt ) != SE_SUCCESS ) + return OGRERR_FAILURE; + + { + SE_ENVELOPE sGenericEnvelope; + SE_coordref_get_xy_envelope( *psCoordRef, &sGenericEnvelope ); + + CPLDebug( "SDE", + "Created coordref '%s' with envelope (%g,%g) to (%g,%g)", + pszWkt, + sGenericEnvelope.minx, + sGenericEnvelope.miny, + sGenericEnvelope.maxx, + sGenericEnvelope.maxy ); + } + + if( poSRS && poSRS->IsGeographic() ) + { + LONG nSDEErr; + + // Reset the offset and precision to match the ordinary values + // for SDE geographic coordinate systems. + nSDEErr = SE_coordref_set_xy( *psCoordRef, -400, -400, 1.11195e9 ); + + if( nSDEErr != SE_SUCCESS ) + { + IssueSDEError( nSDEErr, "SE_coordref_set_xy()" ); + } + } + + CPLFree( pszWkt ); + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/ogrsdedriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/ogrsdedriver.cpp new file mode 100644 index 000000000..b6795539a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/ogrsdedriver.cpp @@ -0,0 +1,128 @@ +/****************************************************************************** + * $Id: ogrsdedriver.cpp 14165 2008-03-31 17:50:47Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRSDEDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * Copyright (c) 2008, Shawn Gervais + * Copyright (c) 2008, Howard Butler + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sde.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrsdedriver.cpp 14165 2008-03-31 17:50:47Z rouault $"); + +/************************************************************************/ +/* ~OGRSDEDriver() */ +/************************************************************************/ + +OGRSDEDriver::~OGRSDEDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRSDEDriver::GetName() + +{ + return "SDE"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRSDEDriver::Open( const char * pszFilename, + int bUpdate ) + +{ + OGRSDEDataSource *poDS; + + poDS = new OGRSDEDataSource(); + + if( !poDS->Open( pszFilename, bUpdate ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRSDEDriver::CreateDataSource( const char * pszName, + char **papszOptions) + +{ + OGRSDEDataSource *poDS; + + poDS = new OGRSDEDataSource(); + + if( !poDS->Open( pszName, TRUE ) ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "ArcSDE driver doesn't currently support database or service " + "creation. Please create the service before using."); + return NULL; + } + else + return poDS; +} +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSDEDriver::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap, ODsCCreateLayer) ) + return TRUE; + if( EQUAL(pszCap, ODsCDeleteLayer) ) + return true; + if( EQUAL(pszCap, ODrCCreateDataSource) ) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRSDE() */ +/************************************************************************/ + +void RegisterOGRSDE() + +{ + if (! GDAL_CHECK_VERSION("OGR SDE")) + return; + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( new OGRSDEDriver ); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/ogrsdelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/ogrsdelayer.cpp new file mode 100644 index 000000000..1ae701651 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sde/ogrsdelayer.cpp @@ -0,0 +1,2603 @@ +/****************************************************************************** + * $Id: ogrsdelayer.cpp 28831 2015-04-01 16:46:05Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRSDELayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008, Shawn Gervais + * Copyright (c) 2008, Howard Butler + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sde.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrsdelayer.cpp 28831 2015-04-01 16:46:05Z rouault $"); + +/************************************************************************/ +/* OGRSDELayer() */ +/************************************************************************/ + +OGRSDELayer::OGRSDELayer( OGRSDEDataSource *poDSIn, int bUpdate ) + +{ + poDS = poDSIn; + + bUpdateAccess = bUpdate; + bPreservePrecision = TRUE; + + iFIDColumn = -1; + nNextFID = 0; + iNextFIDToWrite = 1; + + iShapeColumn = -1; + poSRS = NULL; + poFeatureDefn = NULL; + + bQueryInstalled = FALSE; + hStream = NULL; + hCoordRef = NULL; + papszAllColumns = NULL; + bHaveLayerInfo = FALSE; + bUseNSTRING = FALSE; +} + +/************************************************************************/ +/* ~OGRSDELayer() */ +/************************************************************************/ + +OGRSDELayer::~OGRSDELayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "OGR_SDE", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( bHaveLayerInfo ) + SE_layerinfo_free( hLayerInfo ); + + if( hStream ) + { + SE_stream_free( hStream ); + hStream = NULL; + } + + if( poFeatureDefn ) + poFeatureDefn->Release(); + + if( hCoordRef ) + SE_coordref_free( hCoordRef ); + + if( poSRS ) + poSRS->Release(); + + CSLDestroy( papszAllColumns ); + + CPLFree( pszOwnerName ); + CPLFree( pszDbTableName ); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +int OGRSDELayer::Initialize( const char *pszTableName, + const char *pszFIDColumn, + const char *pszShapeColumn ) + +{ + SE_COLUMN_DEF *asColumnDefs; + SHORT nColumnCount; + int nSDEErr, iCol; + +/* -------------------------------------------------------------------- */ +/* Determine DBMS table owner name and the table name part */ +/* from pszTableName which is a fully-qualified table name */ +/* -------------------------------------------------------------------- */ + char *pszTableNameCopy = CPLStrdup( pszTableName ); + char *pszPeriodPtr; + + if( (pszPeriodPtr = strstr( pszTableNameCopy,"." )) != NULL ) + { + *pszPeriodPtr = '\0'; + pszOwnerName = CPLStrdup(pszTableNameCopy); + pszDbTableName = CPLStrdup(pszPeriodPtr + 1); + } + else + { + pszOwnerName = NULL; + pszDbTableName = CPLStrdup(pszTableName); + } + + CPLFree( pszTableNameCopy ); + +/* -------------------------------------------------------------------- */ +/* Determine whether multi-versioning is enabled for this table. */ +/* -------------------------------------------------------------------- */ + SE_REGINFO hRegInfo; + + nSDEErr = SE_reginfo_create( &hRegInfo ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_reginfo_create" ); + return FALSE; + } + + // TODO: This is called from places that have RegInfo already - + // should we just pass that in? + nSDEErr = SE_registration_get_info( poDS->GetConnection(), + pszTableName, + hRegInfo ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_registration_get_info" ); + return FALSE; + } + + bVersioned = SE_reginfo_is_multiversion( hRegInfo ); + +/* -------------------------------------------------------------------- */ +/* Describe table */ +/* -------------------------------------------------------------------- */ + nSDEErr = + SE_table_describe( poDS->GetConnection(), pszTableName, + &nColumnCount, &asColumnDefs ); + + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_table_describe" ); + return FALSE; + } + + poFeatureDefn = new OGRFeatureDefn( pszTableName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + +/* -------------------------------------------------------------------- */ +/* If the OGR_SDE_GETLAYERTYPE option is set to TRUE, then the layer */ +/* is tested to see if it contains one, and only one, type of geometry */ +/* then that geometry type is placed into the LayerDefn. */ +/* -------------------------------------------------------------------- */ + const char *pszLayerType = CPLGetConfigOption( "OGR_SDE_GETLAYERTYPE", + "FALSE" ); + + if( CSLTestBoolean(pszLayerType) != FALSE ) + { + poFeatureDefn->SetGeomType(DiscoverLayerType()); + } + + for( iCol = 0; iCol < nColumnCount; iCol++ ) + { + OGRFieldType eOGRType = OFTIntegerList; // dummy + int nWidth = -1, nPrecision = -1; + + papszAllColumns = CSLAddString( papszAllColumns, + asColumnDefs[iCol].column_name ); + + switch( asColumnDefs[iCol].sde_type ) + { + case SE_SMALLINT_TYPE: + case SE_INTEGER_TYPE: + eOGRType = OFTInteger; + nWidth = asColumnDefs[iCol].size; + break; + + case SE_FLOAT_TYPE: + case SE_DOUBLE_TYPE: + eOGRType = OFTReal; + nWidth = asColumnDefs[iCol].size; + nPrecision = asColumnDefs[iCol].decimal_digits; + break; + + case SE_STRING_TYPE: +#ifdef SE_UUID_TYPE + case SE_UUID_TYPE: +#endif +#ifdef SE_NSTRING_TYPE + case SE_NSTRING_TYPE: +#endif +#ifdef SE_CLOB_TYPE + case SE_CLOB_TYPE: +#endif +#ifdef SE_NCLOB_TYPE + case SE_NCLOB_TYPE: +#endif + eOGRType = OFTString; + nWidth = asColumnDefs[iCol].size; + break; + + case SE_BLOB_TYPE: + eOGRType = OFTBinary; + break; + + case SE_DATE_TYPE: + eOGRType = OFTDateTime; + break; + + case SE_SHAPE_TYPE: + { + if( iShapeColumn == -1 ) + { + if( pszShapeColumn == NULL + || EQUAL(pszShapeColumn, + asColumnDefs[iCol].column_name) ) + { + iShapeColumn = iCol; + osShapeColumnName = asColumnDefs[iCol].column_name; + } + } + } + break; + + default: + break; + } + + if( eOGRType == OFTIntegerList ) + continue; + + OGRFieldDefn oField( asColumnDefs[iCol].column_name, eOGRType ); + + if( nWidth != -1 ) + oField.SetWidth( nWidth ); + if( nPrecision != -1 ) + oField.SetPrecision( nPrecision ); + + poFeatureDefn->AddFieldDefn( &oField ); + + anFieldMap.push_back( iCol ); + anFieldTypeMap.push_back( asColumnDefs[iCol].sde_type ); + + if( pszFIDColumn + && EQUAL(asColumnDefs[iCol].column_name,pszFIDColumn) ) + { + osFIDColumnName = asColumnDefs[iCol].column_name; + iFIDColumn = anFieldMap.size() - 1; + } + } + + SE_table_free_descriptions( asColumnDefs ); + SE_reginfo_free( hRegInfo ); + + return TRUE; +} + +/************************************************************************/ +/* NeedLayerInfo() */ +/* */ +/* Verify layerinfo is available, and load if not. Loading */ +/* layerinfo is relatively expensive so we try to put it off as */ +/* long as possible. */ +/************************************************************************/ + +int OGRSDELayer::NeedLayerInfo() + +{ + int nSDEErr; + + if( bHaveLayerInfo ) + return TRUE; + + nSDEErr = SE_layerinfo_create( NULL, &hLayerInfo ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_layerinfo_create" ); + return FALSE; + } + + CPLDebug( "OGR_SDE", "Loading %s layerinfo.", + poFeatureDefn->GetName() ); + + nSDEErr = SE_layer_get_info( poDS->GetConnection(), + poFeatureDefn->GetName(), + osShapeColumnName.c_str(), + hLayerInfo ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_layer_get_info" ); + return FALSE; + } + + bHaveLayerInfo = TRUE; + +/* -------------------------------------------------------------------- */ +/* Fetch coordinate reference system. */ +/* -------------------------------------------------------------------- */ + SE_coordref_create( &hCoordRef ); + + nSDEErr = SE_layerinfo_get_coordref( hLayerInfo, hCoordRef ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_layerinfo_get_coordref" ); + } + else + { + char szWKT[SE_MAX_SPATIALREF_SRTEXT_LEN]; + + nSDEErr = SE_coordref_get_description( hCoordRef, szWKT ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_coordref_get_description" ); + } + else + { + poSRS = new OGRSpatialReference(szWKT); + poSRS->morphFromESRI(); + } + + LFLOAT falsex, falsey, xyunits; + nSDEErr = SE_coordref_get_xy( hCoordRef, &falsex, &falsey, &xyunits ); + CPLDebug( "SDE", "SE_coordref_get_xy(%s) = %g/%g/%g", + pszDbTableName, falsex, falsey, xyunits ); + } + + return TRUE; +} + +/************************************************************************/ +/* DiscoverLayerType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRSDELayer::DiscoverLayerType() +{ + if( !NeedLayerInfo() ) + return wkbUnknown; + + int nSDEErr; + LONG nShapeTypeMask = 0; + +/* -------------------------------------------------------------------- */ +/* Check layerinfo flags to establish what geometry types may */ +/* occur. */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_layerinfo_get_shape_types( hLayerInfo, &nShapeTypeMask ); + if (nSDEErr != SE_SUCCESS) + { + CPLDebug( "OGR_SDE", + "Unable to read the layer type information, defaulting to wkbUnknown: error=%d.", + nSDEErr ); + + return wkbUnknown; + } + + int bIsMultipart = ( nShapeTypeMask & SE_MULTIPART_TYPE_MASK ? 1 : 0); + nShapeTypeMask &= ~SE_MULTIPART_TYPE_MASK; + + // Since we assume that all layers can bear a NULL geometry, + // throw the flag away. + nShapeTypeMask &= ~SE_NIL_TYPE_MASK; + + int nTypeCount = 0; + if ( nShapeTypeMask & SE_POINT_TYPE_MASK ) + nTypeCount++; + if ( nShapeTypeMask & SE_LINE_TYPE_MASK + || nShapeTypeMask & SE_SIMPLE_LINE_TYPE_MASK ) + nTypeCount++; + + if ( nShapeTypeMask & SE_AREA_TYPE_MASK ) + nTypeCount++; + +/* -------------------------------------------------------------------- */ +/* When the flags indicate multiple geometry types are */ +/* possible, we examine the layer statistics to see if in */ +/* reality only one geometry type does occur. This is somewhat */ +/* expensive, so we avoid it if we can. */ +/* -------------------------------------------------------------------- */ + if ( nTypeCount == 0 ) + { + CPLDebug( "OGR_SDE", "There is no layer type indicated for the current layer." ); + return wkbUnknown; + } + else if ( nTypeCount > 1 ) + { + CPLDebug( "OGR_SDE", "More than one layer type is indicated for this layer, gathering layer statistics are being gathered." ); + SE_LAYER_STATS layerstats = {0}; + char szTableName[SE_QUALIFIED_TABLE_NAME]; + char szShapeColumn[SE_MAX_COLUMN_LEN]; + + szTableName[0] = '\0'; + szShapeColumn[0] = '\0'; + + nSDEErr = SE_layerinfo_get_spatial_column( hLayerInfo, szTableName, + szShapeColumn ); + if ( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_layerinfo_get_spatial_column" ); + return wkbUnknown; + } + + nSDEErr = SE_layer_get_statistics( poDS->GetConnection(), szTableName, + szShapeColumn, + &layerstats); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_layer_get_statistics" ); + return wkbUnknown; + } + + if ( nShapeTypeMask & SE_POINT_TYPE_MASK && + ( layerstats.POINTs + layerstats.MultiPOINTs ) == 0 ) + nShapeTypeMask &= ~SE_POINT_TYPE_MASK; + + if ( nShapeTypeMask & SE_LINE_TYPE_MASK && + ( layerstats.LINEs + layerstats.MultiLINEs ) == 0 ) + nShapeTypeMask &= ~SE_LINE_TYPE_MASK; + + if ( nShapeTypeMask & SE_SIMPLE_LINE_TYPE_MASK && + ( layerstats.SIMPLE_LINEs + layerstats.MultiSIMPLE_LINEs ) == 0 ) + nShapeTypeMask &= ~SE_SIMPLE_LINE_TYPE_MASK; + + if ( nShapeTypeMask & SE_AREA_TYPE_MASK && + ( layerstats.AREAs + layerstats.MultiAREAs ) == 0 ) + nShapeTypeMask &= ~SE_AREA_TYPE_MASK; + } + +/* -------------------------------------------------------------------- */ +/* Select a geometry type based on the remaining flags. If */ +/* there is a mix we will fall through to the default (wkbUknown). */ +/* -------------------------------------------------------------------- */ + OGRwkbGeometryType eGeoType; + char *pszTypeName; + switch (nShapeTypeMask) + { + case SE_POINT_TYPE_MASK: + if (bIsMultipart) + eGeoType = wkbMultiPoint; + else + eGeoType = wkbPoint; + pszTypeName = "point"; + break; + + case (SE_SIMPLE_LINE_TYPE_MASK | SE_LINE_TYPE_MASK): + case SE_SIMPLE_LINE_TYPE_MASK: + case SE_LINE_TYPE_MASK: + if (bIsMultipart) + eGeoType = wkbMultiLineString; + else + eGeoType = wkbLineString; + pszTypeName = "line"; + break; + + case SE_AREA_TYPE_MASK: + if (bIsMultipart) + eGeoType = wkbMultiPolygon; + else + eGeoType = wkbPolygon; + pszTypeName = "polygon"; + break; + + default: + eGeoType = wkbUnknown; + pszTypeName = "unknown"; + break; + } + + CPLDebug( "OGR_SDE", + "DiscoverLayerType is returning type=%d (%s), multipart=%d.", + eGeoType, pszTypeName, bIsMultipart ); + + return eGeoType; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRSDELayer::ResetReading() + +{ + bQueryInstalled = FALSE; + nNextFID = 0; +} + +/************************************************************************/ +/* InstallQuery() */ +/* */ +/* Setup the stream with current query characteristics. */ +/************************************************************************/ + +int OGRSDELayer::InstallQuery( int bCountingOnly ) + +{ + int nSDEErr; + +/* -------------------------------------------------------------------- */ +/* Create stream, or reset it. */ +/* -------------------------------------------------------------------- */ + if( ResetStream() != OGRERR_NONE ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Create query info. */ +/* -------------------------------------------------------------------- */ + SE_QUERYINFO hQueryInfo; + const char *pszTableName = poFeatureDefn->GetName(); + + nSDEErr = SE_queryinfo_create (&hQueryInfo); + if( nSDEErr != SE_SUCCESS) { + poDS->IssueSDEError( nSDEErr, "SE_queryinfo_create" ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Select table. */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_queryinfo_set_tables( hQueryInfo, 1, + &pszTableName, NULL ); + if( nSDEErr != SE_SUCCESS) { + poDS->IssueSDEError( nSDEErr, "SE_queryinfo_set_tables" ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Set where clause. */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_queryinfo_set_where_clause( hQueryInfo, + osAttributeFilter.c_str() ); + if( nSDEErr != SE_SUCCESS) { + poDS->IssueSDEError( nSDEErr, "SE_queryinfo_set_where_clause" ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* We want to join the spatial and attribute tables. */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_queryinfo_set_query_type(hQueryInfo,SE_QUERYTYPE_JSF); + if( nSDEErr != SE_SUCCESS) { + poDS->IssueSDEError( nSDEErr, "SE_queryinfo_set_query_type" ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Establish the columns to query. If only counting features, */ +/* we will just use the FID column, otherwise we use all */ +/* columns. */ +/* -------------------------------------------------------------------- */ + if( bCountingOnly && iFIDColumn != -1 ) + { + const char *pszFIDColName = + poFeatureDefn->GetFieldDefn( iFIDColumn )->GetNameRef(); + + nSDEErr = SE_queryinfo_set_columns( hQueryInfo, 1, + (const char **) &pszFIDColName ); + if( nSDEErr != SE_SUCCESS) { + poDS->IssueSDEError( nSDEErr, "SE_queryinfo_set_columns" ); + return FALSE; + } + } + else + { + nSDEErr = SE_queryinfo_set_columns( hQueryInfo, + CSLCount(papszAllColumns), + (const char **) papszAllColumns ); + if( nSDEErr != SE_SUCCESS) { + poDS->IssueSDEError( nSDEErr, "SE_queryinfo_set_columns" ); + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Apply the query to the stream. */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_stream_query_with_info( hStream, hQueryInfo ); + if( nSDEErr != SE_SUCCESS) { + poDS->IssueSDEError( nSDEErr, "SE_stream_query_with_info" ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Free query resources. */ +/* -------------------------------------------------------------------- */ + SE_queryinfo_free( hQueryInfo ); + +/* -------------------------------------------------------------------- */ +/* Setup spatial filter on stream if one is installed. */ +/* -------------------------------------------------------------------- */ + if( m_poFilterGeom != NULL ) + { + SE_FILTER sConstraint; + SE_ENVELOPE sEnvelope; + SE_ENVELOPE sLayerEnvelope; + SE_SHAPE hRectShape; + SHORT nSearchOrder = SE_SPATIAL_FIRST; + + if( osAttributeFilter.size() > 0 ) + { + const char *pszOrder = CPLGetConfigOption( "OGR_SDE_SEARCHORDER", + "ATTRIBUTE_FIRST" ); + + if( EQUAL(pszOrder, "ATTRIBUTE_FIRST") ) + nSearchOrder = SE_ATTRIBUTE_FIRST; + else + { + if( !EQUAL(pszOrder, "SPATIAL_FIRST") ) + CPLError( CE_Warning, CPLE_AppDefined, + "Unrecognised OGR_SDE_SEARCHORDER value of %s.", + pszOrder ); + nSearchOrder = SE_SPATIAL_FIRST; + } + } + + NeedLayerInfo(); // need hCoordRef + + nSDEErr = SE_shape_create( hCoordRef, &hRectShape ); + if( nSDEErr != SE_SUCCESS) + { + poDS->IssueSDEError( nSDEErr, "SE_shape_create"); + return FALSE; + } + sEnvelope.minx = m_sFilterEnvelope.MinX; + sEnvelope.miny = m_sFilterEnvelope.MinY; + sEnvelope.maxx = m_sFilterEnvelope.MaxX; + sEnvelope.maxy = m_sFilterEnvelope.MaxY; + + nSDEErr = SE_layerinfo_get_envelope( hLayerInfo, &sLayerEnvelope ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_layerinfo_get_envelope" ); + return FALSE; + } + // ensure that the spatial filter overlaps area of the layer + if (sEnvelope.minx > sLayerEnvelope.maxx || sEnvelope.maxx < sLayerEnvelope.minx || + sEnvelope.miny > sLayerEnvelope.maxy || sEnvelope.maxy < sLayerEnvelope.miny ) + { + // using a small rectangle to filter out all the shapes + sEnvelope.minx = sLayerEnvelope.minx; + sEnvelope.miny = sLayerEnvelope.miny; + sEnvelope.maxx = sLayerEnvelope.minx + 0.00000001; + sEnvelope.maxy = sLayerEnvelope.miny + 0.00000001; + } + + nSDEErr = SE_shape_generate_rectangle( &sEnvelope, hRectShape ); + if( nSDEErr != SE_SUCCESS) + { + poDS->IssueSDEError( nSDEErr, "SE_shape_generate_rectangle"); + return FALSE; + } + + sConstraint.filter.shape = hRectShape; + strcpy( sConstraint.table, poFeatureDefn->GetName() ); + strcpy( sConstraint.column, osShapeColumnName.c_str() ); + sConstraint.method = SM_ENVP; + sConstraint.filter_type = SE_SHAPE_FILTER; + sConstraint.truth = TRUE; + + nSDEErr = SE_stream_set_spatial_constraints( hStream, nSearchOrder, + FALSE, 1, &sConstraint ); + if( nSDEErr != SE_SUCCESS) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_set_spatial_constraints"); + return FALSE; + } + + SE_shape_free( hRectShape ); + } + +/* -------------------------------------------------------------------- */ +/* Execute the query. */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_stream_execute( hStream ); + if( nSDEErr != SE_SUCCESS ) { + poDS->IssueSDEError( nSDEErr, "SE_stream_execute" ); + return FALSE; + } + + bQueryInstalled = TRUE; + + return TRUE; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRSDELayer::SetAttributeFilter( const char *pszQuery ) + +{ + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + + if( pszQuery == NULL ) + osAttributeFilter = ""; + else + osAttributeFilter = pszQuery; + + ResetReading(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TranslateOGRRecord() */ +/* */ +/* Translates OGR feature semantics to SDE ones and sets items */ +/* in the stream for an update or insert operation. The stream is */ +/* set in insert or update mode depending on the value of the */ +/* bIsInsert flag. The stream must have already been reset by */ +/* the caller. Actual execution of the stream operation is the */ +/* responsibility of the caller. */ +/************************************************************************/ +OGRErr OGRSDELayer::TranslateOGRRecord( OGRFeature *poFeature, + int bIsInsert ) + +{ + SE_SHAPE hShape; + LONG nSDEErr; + +/* -------------------------------------------------------------------- */ +/* Translate geometry to SDE geometry */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetGeometryRef() != NULL ) + { + if( TranslateOGRGeometry( poFeature->GetGeometryRef(), &hShape, + hCoordRef ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to convert geometry from OGR -> SDE"); + return OGRERR_FAILURE; + } + } + +/* -------------------------------------------------------------------- */ +/* Determine which fields to insert */ +/* -------------------------------------------------------------------- */ + int *paiColToDefMap; + char **papszInsertCols = NULL; + int nAttributeCols = 0; + int nSpecialCols = 0; + int i; + + paiColToDefMap = (int *) CPLMalloc( sizeof(int) + * poFeatureDefn->GetFieldCount() ); + + /* + * If the row id is managed by USER, and not SDE, then we need to take + * care to set the FID column ourselves. If the row id column is managed by + * SDE we are forbidden from setting it. + */ + if( nFIDColumnType == SE_REGISTRATION_ROW_ID_COLUMN_TYPE_USER + && iFIDColumn != -1 ) + { + papszInsertCols = CSLAddString( papszInsertCols, + osFIDColumnName.c_str() ); + + nSpecialCols++; + } + + if( poFeature->GetGeometryRef() != NULL ) + { + papszInsertCols = CSLAddString( papszInsertCols, + osShapeColumnName.c_str() ); + + nSpecialCols++; + } + + // Add attribute fields of this feature, build mapping of + // indexes to field definitions vs. the columns we will insert + for( i=0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(i); + + if( !poFeature->IsFieldSet(i) ) + continue; + + // Skip FID and Geometry columns + if( EQUAL(poFieldDefn->GetNameRef(), osFIDColumnName.c_str()) ) + { + // Skip the column if it's managed by SDE + if( nFIDColumnType == SE_REGISTRATION_ROW_ID_COLUMN_TYPE_SDE ) + continue; + } + + if( EQUAL(poFieldDefn->GetNameRef(), osShapeColumnName.c_str()) ) + continue; + + papszInsertCols = CSLAddString( papszInsertCols, + poFieldDefn->GetNameRef() ); + + paiColToDefMap[nAttributeCols] = i; + nAttributeCols++; + } + + +/* -------------------------------------------------------------------- */ +/* Prepare the insert or update stream mode */ +/* -------------------------------------------------------------------- */ + const char *pszMethod; + + if( bIsInsert ) + { + nSDEErr = SE_stream_insert_table( hStream, poFeatureDefn->GetName(), + nSpecialCols + nAttributeCols, + (const char **)papszInsertCols ); + pszMethod = "SE_stream_insert_table"; + } + else // It's an UPDATE + { + const char *pszWhere; + + // Check that there is a FID column detected on the table, and that + // this feature has a non-null FID + if( iFIDColumn == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot update feature: Layer \"%s\" has no FID column", + poFeatureDefn->GetName() ); + + CSLDestroy( papszInsertCols ); + CPLFree( paiColToDefMap ); + + return OGRERR_FAILURE; + } + else if( poFeature->GetFID() == OGRNullFID ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot update feature: Feature has a NULL Feature ID" ); + + CSLDestroy( papszInsertCols ); + CPLFree( paiColToDefMap ); + + return OGRERR_FAILURE; + } + + // Build WHERE clause + pszWhere = CPLSPrintf( "%s = %ld", osFIDColumnName.c_str(), + poFeature->GetFID() ); + + nSDEErr = SE_stream_update_table( hStream, poFeatureDefn->GetName(), + nSpecialCols + nAttributeCols, + (const char **)papszInsertCols, + pszWhere ); + + pszMethod = "SE_stream_update_table"; + } + + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, pszMethod ); + return OGRERR_FAILURE; + } + + +/* -------------------------------------------------------------------- */ +/* Set the feature attributes */ +/* -------------------------------------------------------------------- */ + short iCurColNum = 1; + + if( nFIDColumnType == SE_REGISTRATION_ROW_ID_COLUMN_TYPE_USER + && iFIDColumn != -1 ) + { + LONG nFID; + + nFID = poFeature->GetFID(); + if( nFID == OGRNullFID ) + { + nFID = iNextFIDToWrite++; + poFeature->SetFID( nFID ); + } + + nSDEErr = SE_stream_set_integer( hStream, iCurColNum++, + &nFID ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_set_integer" ); + + CSLDestroy( papszInsertCols ); + CPLFree( paiColToDefMap ); + + return OGRERR_FAILURE; + } + } + + // Set geometry (shape) column + if( poFeature->GetGeometryRef() != NULL ) + { + nSDEErr = SE_stream_set_shape( hStream, iCurColNum++, hShape ); + + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_set_shape" ); + + CSLDestroy( papszInsertCols ); + CPLFree( paiColToDefMap ); + SE_shape_free( hShape ); + return OGRERR_FAILURE; + } + } + + // Set attribute columns + for( i=0; i < nAttributeCols; i++ ) + { + int iFieldDefnIdx = paiColToDefMap[i]; + OGRFieldDefn *poFieldDefn; + OGRField *poField; + + poFieldDefn = poFeatureDefn->GetFieldDefn(iFieldDefnIdx); + poField = poFeature->GetRawFieldRef(iFieldDefnIdx); + + CPLAssert( poFieldDefn != NULL ); + CPLAssert( poField != NULL ); + + if( poFieldDefn->GetType() == OFTInteger ) + { + LONG nLong = poField->Integer; + + nSDEErr = SE_stream_set_integer( hStream, iCurColNum++, + &nLong ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_set_integer" ); + CSLDestroy( papszInsertCols ); + CPLFree( paiColToDefMap ); + return OGRERR_FAILURE; + } + } + + else if( poFieldDefn->GetType() == OFTReal ) + { + LFLOAT nDouble = poField->Real; + + nSDEErr = SE_stream_set_double( hStream, iCurColNum++, + &nDouble ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_set_float" ); + CSLDestroy( papszInsertCols ); + CPLFree( paiColToDefMap ); + return OGRERR_FAILURE; + } + } + + else if( poFieldDefn->GetType() == OFTString + && anFieldTypeMap[iFieldDefnIdx] == SE_NSTRING_TYPE ) + { + SE_WCHAR *pszUTF16 = (SE_WCHAR *) + CPLRecodeToWChar( poField->String, CPL_ENC_UTF8, + CPL_ENC_UTF16 ); + + nSDEErr = SE_stream_set_nstring( hStream, iCurColNum++, + pszUTF16 ); + CPLFree( pszUTF16 ); + + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_set_nstring" ); + CSLDestroy( papszInsertCols ); + CPLFree( paiColToDefMap ); + return OGRERR_FAILURE; + } + } + + else if( poFieldDefn->GetType() == OFTString ) + { + nSDEErr = SE_stream_set_string( hStream, iCurColNum++, + poField->String ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_set_string" ); + CSLDestroy( papszInsertCols ); + CPLFree( paiColToDefMap ); + return OGRERR_FAILURE; + } + } + + else if( poFieldDefn->GetType() == OFTDate + || poFieldDefn->GetType() == OFTDateTime ) + { + struct tm sDateVal; + + // TODO: hobu, please double-check this. + sDateVal.tm_year = poField->Date.Year - 1900; + sDateVal.tm_mon = poField->Date.Month - 1; + sDateVal.tm_mday = poField->Date.Day; + sDateVal.tm_hour = poField->Date.Hour; + sDateVal.tm_min = poField->Date.Minute; + sDateVal.tm_sec = poField->Date.Second; + sDateVal.tm_isdst = (poField->Date.TZFlag == 0 ? 0 : 1); + + nSDEErr = SE_stream_set_date( hStream, iCurColNum++, + &sDateVal ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_set_date" ); + CSLDestroy( papszInsertCols ); + CPLFree( paiColToDefMap ); + return OGRERR_FAILURE; + } + } + + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Cannot set attribute of type %s in SDE layer: " + "attempting to create as STRING", + OGRFieldDefn::GetFieldTypeName(poFieldDefn->GetType()) ); + + CSLDestroy( papszInsertCols ); + CPLFree( paiColToDefMap ); + + return OGRERR_FAILURE; + } + } + + CSLDestroy( papszInsertCols ); + CPLFree( paiColToDefMap ); + SE_shape_free(hShape); + return OGRERR_NONE; +} + +/************************************************************************/ +/* TranslateOGRGeometry() */ +/************************************************************************/ +OGRErr OGRSDELayer::TranslateOGRGeometry( OGRGeometry *poGeom, + SE_SHAPE *phShape, + SE_COORDREF hCoordRef ) + +{ + LONG nSDEErr; + + CPLAssert( poGeom != NULL ); + CPLAssert( phShape != NULL ); + +/* -------------------------------------------------------------------- */ +/* Initialize shape. */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_shape_create( hCoordRef, phShape ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_shape_create" ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Determine whether the geometry includes Z coordinates. */ +/* -------------------------------------------------------------------- */ + int b3D = FALSE; + + b3D = wkbHasZ(poGeom->getGeometryType()); + +/* -------------------------------------------------------------------- */ +/* Translate POINT/MULTIPOINT type. */ +/* -------------------------------------------------------------------- */ + if( wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) + { + OGRPoint *poPoint = (OGRPoint *) poGeom; + LONG nParts = 1; + SE_POINT *asPointParts; + LFLOAT *anfZcoords; + + asPointParts = (SE_POINT*) CPLMalloc(nParts * sizeof(SE_POINT)); + if (!asPointParts) { + CPLError( CE_Fatal, CPLE_AppDefined, + "Cannot allocate asPointParts" ); + return OGRERR_FAILURE; + } + + anfZcoords = (LFLOAT*) CPLMalloc(nParts * sizeof(LFLOAT)); + if (!anfZcoords) { + CPLError( CE_Fatal, CPLE_AppDefined, + "Cannot allocate anfZcoords" ); + return OGRERR_FAILURE; + } + asPointParts[0].x = poPoint->getX(); + asPointParts[0].y = poPoint->getY(); + + if( b3D ) + { + anfZcoords[0] = poPoint->getZ(); + nSDEErr = SE_shape_generate_point( nParts, asPointParts, anfZcoords, + NULL, *phShape ); + } + else + { + nSDEErr = SE_shape_generate_point( nParts, asPointParts, NULL, NULL, + *phShape ); + } + + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_shape_generate_point" ); + return OGRERR_FAILURE; + } + } + + else if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint ) + { + OGRMultiPoint *poMulPoint = (OGRMultiPoint *) poGeom; + LONG nParts; + SE_POINT *pasPointParts; + LFLOAT *panfZcoords = NULL; + int i; + + nParts = poMulPoint->getNumGeometries(); + + pasPointParts = (SE_POINT *) CPLMalloc( sizeof(SE_POINT) * nParts ); + + if( b3D ) + panfZcoords = (LFLOAT *) CPLMalloc( sizeof(LFLOAT) * nParts ); + + for( i=0; i < nParts; i++ ) + { + OGRPoint *poThisPoint; + + poThisPoint = (OGRPoint *) poMulPoint->getGeometryRef(i); + + pasPointParts[i].x = poThisPoint->getX(); + pasPointParts[i].y = poThisPoint->getY(); + + if( b3D ) + panfZcoords[i] = poThisPoint->getZ(); + } + + nSDEErr = SE_shape_generate_point( nParts, pasPointParts, panfZcoords, + NULL, *phShape ); + + CPLFree( pasPointParts ); + if( b3D ) + CPLFree( panfZcoords ); + + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_shape_generate_point" ); + return OGRERR_FAILURE; + } + } + +/* -------------------------------------------------------------------- */ +/* Translate POLYGON/MULTIPOLYGON type. */ +/* -------------------------------------------------------------------- */ + else if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) + { + OGRPolygon *poPoly = (OGRPolygon *) poGeom; + LONG nPoints=0, iCurPoint=0; + SE_POINT *pasPoints; + LFLOAT *panfZcoords = NULL; + int i, j; + + // Get exterior ring + OGRLinearRing *poExtRing = poPoly->getExteriorRing(); + + if( poExtRing == NULL ) + { + // The polygon is empty + // TODO: Does this mean that the shape is NULL, then? + nSDEErr = SE_shape_make_nil( *phShape ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_shape_make_nil" ); + return OGRERR_FAILURE; + } + } + + // Get total number of points in polygon + nPoints += poExtRing->getNumPoints(); + + for( i=0; i < poPoly->getNumInteriorRings(); i++ ) + nPoints += poPoly->getInteriorRing(i)->getNumPoints(); + + pasPoints = (SE_POINT *) CPLMalloc( sizeof(SE_POINT) * nPoints ); + + if( b3D ) + panfZcoords = (LFLOAT *) CPLMalloc( sizeof(LFLOAT) * nPoints ); + + for( i=0; i < poExtRing->getNumPoints(); i++ ) + { + OGRPoint oLRPnt; + + poExtRing->getPoint( i, &oLRPnt ); + + pasPoints[iCurPoint].x = oLRPnt.getX(); + pasPoints[iCurPoint].y = oLRPnt.getY(); + + if( b3D ) + panfZcoords[iCurPoint] = oLRPnt.getZ(); + + iCurPoint++; + } + + for( i=0; i < poPoly->getNumInteriorRings(); i++ ) + { + OGRLinearRing *poIntRing = poPoly->getInteriorRing(i); + + for( j=0; j < poIntRing->getNumPoints(); j++ ) + { + OGRPoint oLRPnt; + + poIntRing->getPoint( j, &oLRPnt ); + + pasPoints[iCurPoint].x = oLRPnt.getX(); + pasPoints[iCurPoint].y = oLRPnt.getY(); + + if( b3D ) + panfZcoords[iCurPoint] = oLRPnt.getZ(); + + iCurPoint++; + } + } + + nSDEErr = SE_shape_generate_polygon( nPoints, 1, NULL, pasPoints, + panfZcoords, NULL, *phShape ); + + CPLFree( pasPoints ); + if( b3D ) + CPLFree( panfZcoords ); + + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_shape_generate_polygon" ); + return OGRERR_FAILURE; + } + } + + else if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ) + { + OGRMultiPolygon *poMP = (OGRMultiPolygon *) poGeom; + LONG nPoints=0; + LONG nParts; + LONG iCurPoint=0; + LONG *panPartOffsets; + SE_POINT *pasPoints; + LFLOAT *panfZcoords = NULL; + int i, j, k; + + nParts = poMP->getNumGeometries(); + + // Find total number of points + for( i=0; i < nParts; i++ ) + { + OGRPolygon *poPoly = (OGRPolygon *) poMP->getGeometryRef(i); + OGRLinearRing *poExtRing = poPoly->getExteriorRing(); + + nPoints += poExtRing->getNumPoints(); + + for( j=0; j < poPoly->getNumInteriorRings(); j++ ) + nPoints += poPoly->getInteriorRing(j)->getNumPoints(); + } + + // Allocate points and part offset arrays + pasPoints = (SE_POINT *) CPLMalloc( sizeof(SE_POINT) * nPoints ); + panPartOffsets = (LONG *) CPLMalloc( sizeof(LONG) * nParts ); + if( b3D ) + panfZcoords = (LFLOAT *) CPLMalloc( sizeof(LFLOAT) * nPoints ); + + + // Build arrays of points and part offsets + for( i=0; i < nParts; i++ ) + { + OGRPolygon *poPoly = (OGRPolygon *) poMP->getGeometryRef(i); + OGRLinearRing *poExtRing = poPoly->getExteriorRing(); + + panPartOffsets[i] = iCurPoint; + + for( j=0; j < poExtRing->getNumPoints(); j++ ) + { + OGRPoint oLRPnt; + + poExtRing->getPoint( j, &oLRPnt ); + + pasPoints[iCurPoint].x = oLRPnt.getX(); + pasPoints[iCurPoint].y = oLRPnt.getY(); + + if( b3D ) + panfZcoords[iCurPoint] = oLRPnt.getZ(); + + iCurPoint++; + } + + for( j=0; j < poPoly->getNumInteriorRings(); j++ ) + { + OGRLinearRing *poIntRing = poPoly->getInteriorRing(j); + + for( k=0; k < poIntRing->getNumPoints(); k++ ) + { + OGRPoint oLRPnt; + + poIntRing->getPoint( k, &oLRPnt ); + + pasPoints[iCurPoint].x = oLRPnt.getX(); + pasPoints[iCurPoint].y = oLRPnt.getY(); + + if( b3D ) + panfZcoords[iCurPoint] = oLRPnt.getZ(); + + iCurPoint++; + } + } + } + + nSDEErr = SE_shape_generate_polygon( nPoints, nParts, panPartOffsets, + pasPoints, panfZcoords, NULL, + *phShape ); + + CPLFree( pasPoints ); + CPLFree( panPartOffsets ); + if( b3D ) + CPLFree( panfZcoords ); + + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_shape_generate_polygon" ); + return OGRERR_FAILURE; + } + } + + +/* -------------------------------------------------------------------- */ +/* Translate LINESTRING/MULTILINESTRING type. */ +/* -------------------------------------------------------------------- */ + else if( wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) + { + OGRLineString *poLineString = (OGRLineString *) poGeom; + LONG nParts = 1; + LONG nPoints; + SE_POINT *pasPoints; + LFLOAT *panfZcoords = NULL; + int i; + + nPoints = poLineString->getNumPoints(); + + pasPoints = (SE_POINT *) CPLMalloc( sizeof(SE_POINT) * nPoints ); + if( b3D ) + panfZcoords = (LFLOAT *) CPLMalloc( sizeof(LFLOAT) * nPoints ); + + for( i=0; i < nPoints; i++ ) + { + OGRPoint oPoint; + + poLineString->getPoint( i, &oPoint ); + + pasPoints[i].x = oPoint.getX(); + pasPoints[i].y = oPoint.getY(); + + if( b3D ) + panfZcoords[i] = oPoint.getZ(); + } + + nSDEErr = SE_shape_generate_line( nPoints, nParts, NULL, pasPoints, + panfZcoords, NULL, *phShape ); + + CPLFree( pasPoints ); + if( b3D ) + CPLFree( panfZcoords ); + + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_shape_generate_line" ); + return OGRERR_FAILURE; + } + } + + else if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ) + { + OGRMultiLineString *poMLS = (OGRMultiLineString *) poGeom; + LONG nParts; + LONG nPoints = 0; + LONG iCurPoint = 0; + SE_POINT *pasPoints; + LONG *panPartOffsets; + LFLOAT *panfZcoords = NULL; + int i, j; + + // Get number of parts and total number of points + nParts = poMLS->getNumGeometries(); + + for( i=0; i < nParts; i++ ) + { + OGRLineString *poLS = (OGRLineString *) poMLS->getGeometryRef(i); + + nPoints += poLS->getNumPoints(); + } + + // Allocate arrays for points and part offsets + pasPoints = (SE_POINT *) CPLMalloc( sizeof(SE_POINT) * nPoints ); + panPartOffsets = (LONG *) CPLMalloc( sizeof(LONG) * nParts ); + if( b3D ) + panfZcoords = (LFLOAT *) CPLMalloc( sizeof(LFLOAT) * nPoints ); + + for( i=0; i < nParts; i++ ) + { + OGRLineString *poLS = (OGRLineString *) poMLS->getGeometryRef(i); + + panPartOffsets[i] = iCurPoint; + + for( j=0; j < poLS->getNumPoints(); j++ ) + { + OGRPoint oPoint; + + poLS->getPoint( j, &oPoint ); + + pasPoints[iCurPoint].x = oPoint.getX(); + pasPoints[iCurPoint].y = oPoint.getY(); + + if( b3D ) + panfZcoords[iCurPoint] = oPoint.getZ(); + + iCurPoint++; + } + } + + nSDEErr = SE_shape_generate_line( nPoints, nParts, panPartOffsets, + pasPoints, panfZcoords, NULL, + *phShape ); + + CPLFree( pasPoints ); + CPLFree( panPartOffsets ); + if( b3D ) + CPLFree( panfZcoords ); + + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_shape_generate_line" ); + return OGRERR_FAILURE; + } + } + +/* -------------------------------------------------------------------- */ +/* Error on other geometry types. */ +/* -------------------------------------------------------------------- */ + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "OGR_SDE: TranslateOGRGeometry() cannot translate " + "geometries of type %s (%d)", poGeom->getGeometryName(), + poGeom->getGeometryType() ); + + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TranslateSDEGeometry() */ +/************************************************************************/ + +OGRGeometry *OGRSDELayer::TranslateSDEGeometry( SE_SHAPE hShape ) + +{ + LONG nSDEGeomType; + OGRGeometry *poGeom = NULL; + +/* -------------------------------------------------------------------- */ +/* Fetch geometry type. */ +/* -------------------------------------------------------------------- */ + SE_shape_get_type( hShape, &nSDEGeomType ); + + if( nSDEGeomType == SG_NIL_SHAPE ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Fetch points and parts. */ +/* -------------------------------------------------------------------- */ + LONG nPointCount, nPartCount, nSubPartCount, nSDEErr; + SE_POINT *pasPoints; + LONG *panSubParts; + LONG *panParts; + LFLOAT *padfZ = NULL; + + SE_shape_get_num_points( hShape, 0, 0, &nPointCount ); + SE_shape_get_num_parts( hShape, &nPartCount, &nSubPartCount ); + + pasPoints = (SE_POINT *) CPLMalloc(nPointCount * sizeof(SE_POINT)); + panParts = (LONG *) CPLMalloc(nPartCount * sizeof(LONG)); + panSubParts = (LONG *) CPLMalloc(nSubPartCount * sizeof(LONG)); + + if( SE_shape_is_3D( hShape ) ) + padfZ = (LFLOAT *) CPLMalloc(nPointCount * sizeof(LFLOAT)); + + nSDEErr = SE_shape_get_all_points( hShape, SE_DEFAULT_ROTATION, + panParts, panSubParts, + pasPoints, padfZ, NULL ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_shape_get_all_points" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Handle simple point. */ +/* -------------------------------------------------------------------- */ + switch( nSDEGeomType ) + { + case SG_POINT_SHAPE: + { + CPLAssert( nPointCount == 1 ); + CPLAssert( nSubPartCount == 1 ); + CPLAssert( nPartCount == 1 ); + if( padfZ ) + poGeom = new OGRPoint( pasPoints[0].x, pasPoints[0].y, padfZ[0] ); + else + poGeom = new OGRPoint( pasPoints[0].x, pasPoints[0].y ); + } + break; + +/* -------------------------------------------------------------------- */ +/* Handle simple point. */ +/* -------------------------------------------------------------------- */ + case SG_MULTI_POINT_SHAPE: + { + OGRMultiPoint *poMP = new OGRMultiPoint(); + int iPart; + + CPLAssert( nPartCount == nSubPartCount ); // one vertex per point. + CPLAssert( nPointCount == nPartCount ); + + for( iPart = 0; iPart < nPartCount; iPart++ ) + { + if( padfZ != NULL ) + poMP->addGeometryDirectly( new OGRPoint( pasPoints[iPart].x, + pasPoints[iPart].y, + padfZ[iPart] ) ); + else + poMP->addGeometryDirectly( new OGRPoint( pasPoints[iPart].x, + pasPoints[iPart].y ) ); + } + poGeom = poMP; + } + break; + +/* -------------------------------------------------------------------- */ +/* Handle line. */ +/* -------------------------------------------------------------------- */ + case SG_LINE_SHAPE: + case SG_SIMPLE_LINE_SHAPE: + { + OGRLineString *poLine = new OGRLineString(); + int i; + + CPLAssert( nPartCount == 1 && nSubPartCount == 1 ); + + poLine->setNumPoints( nPointCount ); + + for( i = 0; i < nPointCount; i++ ) + { + if( padfZ ) + poLine->setPoint( i, pasPoints[i].x, pasPoints[i].y, + padfZ[i] ); + else + poLine->setPoint( i, pasPoints[i].x, pasPoints[i].y ); + } + + poGeom = poLine; + } + break; + +/* -------------------------------------------------------------------- */ +/* Handle multi line. */ +/* -------------------------------------------------------------------- */ + case SG_MULTI_LINE_SHAPE: + case SG_MULTI_SIMPLE_LINE_SHAPE: + { + OGRMultiLineString *poMLS = new OGRMultiLineString(); + int iPart; + + CPLAssert( nPartCount == nSubPartCount ); + + for( iPart = 0; iPart < nPartCount; iPart++ ) + { + OGRLineString *poLine = new OGRLineString(); + int i, nLineVertCount; + + CPLAssert( panParts[iPart] == iPart ); // 1:1 correspondance + + if( iPart == nPartCount-1 ) + nLineVertCount = nPointCount - panSubParts[iPart]; + else + nLineVertCount = panSubParts[iPart+1] - panSubParts[iPart]; + + poLine->setNumPoints( nLineVertCount ); + + for( i = 0; i < nLineVertCount; i++ ) + { + int iVert = i + panSubParts[iPart]; + + if( padfZ ) + poLine->setPoint( i, + pasPoints[iVert].x, + pasPoints[iVert].y, + padfZ[iVert] ); + else + poLine->setPoint( i, + pasPoints[iVert].x, + pasPoints[iVert].y ); + } + + poMLS->addGeometryDirectly( poLine ); + } + + poGeom = poMLS; + } + break; + +/* -------------------------------------------------------------------- */ +/* Handle polygon and multipolygon. Each subpart is a ring. */ +/* -------------------------------------------------------------------- */ + case SG_AREA_SHAPE: + case SG_MULTI_AREA_SHAPE: + { + int iPart; + OGRMultiPolygon *poMP = NULL; + + if( nSDEGeomType == SG_MULTI_AREA_SHAPE ) + poMP = new OGRMultiPolygon(); + + for( iPart = 0; iPart < nPartCount; iPart++ ) + { + OGRPolygon *poPoly = new OGRPolygon(); + int iVert, iSubPart; + int nNextSubPart; + + if( iPart == nPartCount-1 ) + nNextSubPart = nSubPartCount; + else + nNextSubPart = panParts[iPart+1]; + + for( iSubPart = panParts[iPart]; iSubPart < nNextSubPart; iSubPart++ ) + { + OGRLinearRing *poRing = new OGRLinearRing(); + int nRingVertCount; + + if( iSubPart == nSubPartCount-1 ) + nRingVertCount = nPointCount - panSubParts[iSubPart]; + else + nRingVertCount = + panSubParts[iSubPart+1] - panSubParts[iSubPart]; + + poRing->setNumPoints( nRingVertCount ); + + for( iVert=0; iVert < nRingVertCount; iVert++ ) + { + if( padfZ ) + poRing->setPoint( + iVert, + pasPoints[iVert+panSubParts[iSubPart]].x, + pasPoints[iVert+panSubParts[iSubPart]].y, + padfZ[iVert+panSubParts[iSubPart]] ); + else + poRing->setPoint( + iVert, + pasPoints[iVert+panSubParts[iSubPart]].x, + pasPoints[iVert+panSubParts[iSubPart]].y ); + } + + poPoly->addRingDirectly( poRing ); + } + + if( poMP ) + poMP->addGeometryDirectly( poPoly ); + else + poGeom = poPoly; + } + + if( poMP ) + poGeom = poMP; + } + break; + +/* -------------------------------------------------------------------- */ +/* Report unsupported geometries. */ +/* -------------------------------------------------------------------- */ + default: + { + CPLError( CE_Warning, CPLE_NotSupported, + "Unsupported geometry type: %d", + (int) nSDEGeomType ); + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( pasPoints ); + CPLFree( panParts ); + CPLFree( panSubParts ); + CPLFree( padfZ ); + + return poGeom; +} + +/************************************************************************/ +/* TranslateSDERecord() */ +/************************************************************************/ + +OGRFeature *OGRSDELayer::TranslateSDERecord() + +{ + unsigned int i; + int nSDEErr; + OGRFeature *poFeat = new OGRFeature( poFeatureDefn ); + +/* -------------------------------------------------------------------- */ +/* Translate field values. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < anFieldMap.size(); i++ ) + { + OGRFieldDefn *poFieldDef = poFeatureDefn->GetFieldDefn( i ); + + switch( anFieldTypeMap[i] ) + { + case SE_SMALLINT_TYPE: + { + short nShort; + nSDEErr = SE_stream_get_smallint( hStream, anFieldMap[i]+1, + &nShort ); + if( nSDEErr == SE_SUCCESS ) + poFeat->SetField( i, (int) nShort ); + else if( nSDEErr != SE_NULL_VALUE ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_get_smallint" ); + return NULL; + } + } + break; + + case SE_INTEGER_TYPE: + { + LONG nValue; + nSDEErr = SE_stream_get_integer( hStream, anFieldMap[i]+1, + &nValue ); + if( nSDEErr == SE_SUCCESS ) + poFeat->SetField( i, (int) nValue ); + else if( nSDEErr != SE_NULL_VALUE ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_get_integer" ); + return NULL; + } + } + break; + + case SE_FLOAT_TYPE: + { + float fValue; + + nSDEErr = SE_stream_get_float( hStream, anFieldMap[i]+1, + &fValue ); + if( nSDEErr == SE_SUCCESS ) + poFeat->SetField( i, (double) fValue ); + else if( nSDEErr != SE_NULL_VALUE ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_get_float" ); + return NULL; + } + } + break; + + case SE_DOUBLE_TYPE: + { + double dfValue; + + nSDEErr = SE_stream_get_double( hStream, anFieldMap[i]+1, + &dfValue ); + if( nSDEErr == SE_SUCCESS ) + poFeat->SetField( i, dfValue ); + else if( nSDEErr != SE_NULL_VALUE ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_get_double" ); + return NULL; + } + } + break; + + case SE_STRING_TYPE: + { + char *pszTempString = (char *) + CPLMalloc(poFieldDef->GetWidth()+1); + + nSDEErr = SE_stream_get_string( hStream, anFieldMap[i]+1, + pszTempString ); + if( nSDEErr == SE_SUCCESS ) + poFeat->SetField( i, pszTempString ); + else if( nSDEErr != SE_NULL_VALUE ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_get_string" ); + return NULL; + } + CPLFree( pszTempString ); + } + break; + + case SE_NSTRING_TYPE: + { + SE_WCHAR * pszTempStringUTF16 = (SE_WCHAR *) + CPLMalloc ((poFieldDef->GetWidth()+1) * sizeof(SE_WCHAR )); + + nSDEErr = SE_stream_get_nstring( hStream, anFieldMap[i]+1, + pszTempStringUTF16 ); + + if( nSDEErr == SE_SUCCESS ) + { + char* pszUTF8 = CPLRecodeFromWChar((const wchar_t*)pszTempStringUTF16, CPL_ENC_UTF16, CPL_ENC_UTF8); + + poFeat->SetField( i, pszUTF8 ); + CPLFree( pszUTF8 ); + + } + else if( nSDEErr != SE_NULL_VALUE ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_get_nstring" ); + CPLFree( pszTempStringUTF16 ); + + return NULL; + } + CPLFree( pszTempStringUTF16 ); + } + break; + +#ifdef SE_UUID_TYPE + case SE_UUID_TYPE: + { + char *pszTempString = (char *) + CPLMalloc(poFieldDef->GetWidth()+1); + + nSDEErr = SE_stream_get_uuid( hStream, anFieldMap[i]+1, + pszTempString ); + if( nSDEErr == SE_SUCCESS ) + poFeat->SetField( i, pszTempString ); + else if( nSDEErr != SE_NULL_VALUE ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_get_string" ); + return NULL; + } + CPLFree( pszTempString ); + } + break; +#endif + case SE_BLOB_TYPE: + { + SE_BLOB_INFO sBlobVal; + + nSDEErr = SE_stream_get_blob( hStream, anFieldMap[i]+1, + &sBlobVal ); + if( nSDEErr == SE_SUCCESS ) + { + poFeat->SetField( i, + sBlobVal.blob_length, + (GByte *) sBlobVal.blob_buffer ); + SE_blob_free( &sBlobVal ); + } + else if( nSDEErr != SE_NULL_VALUE ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_get_blob" ); + return NULL; + } + } + break; + +#ifdef SE_CLOB_TYPE + case SE_CLOB_TYPE: + { + SE_CLOB_INFO sClobVal; + + memset(&sClobVal, 0, sizeof(sClobVal)); /* to prevent from the crash in SE_stream_get_clob */ + nSDEErr = SE_stream_get_clob( hStream, anFieldMap[i]+1, + &sClobVal ); + if( nSDEErr == SE_SUCCESS ) + { + /* the returned string is not null-terminated */ + char* sClobstring = (char*)CPLMalloc(sizeof(char)*(sClobVal.clob_length+1)); + memcpy(sClobstring, sClobVal.clob_buffer, sClobVal.clob_length); + sClobstring[sClobVal.clob_length] = '\0'; + + poFeat->SetField( i, sClobstring ); + SE_clob_free( &sClobVal ); + CPLFree(sClobstring); + } + else if( nSDEErr != SE_NULL_VALUE ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_get_clob" ); + return NULL; + } + } + break; + +#endif + +#ifdef SE_NCLOB_TYPE + case SE_NCLOB_TYPE: + { + SE_NCLOB_INFO sNclobVal; + + memset(&sNclobVal, 0, sizeof(sNclobVal)); /* to prevent from the crash in SE_stream_get_nclob */ + nSDEErr = SE_stream_get_nclob( hStream, anFieldMap[i]+1, + &sNclobVal ); + if( nSDEErr == SE_SUCCESS ) + { + /* the returned string is not null-terminated */ + SE_WCHAR* sNclobstring = (SE_WCHAR*)CPLMalloc(sizeof(char)*(sNclobVal.nclob_length+2)); + memcpy(sNclobstring, sNclobVal.nclob_buffer, sNclobVal.nclob_length); + sNclobstring[sNclobVal.nclob_length / 2] = '\0'; + + char* pszUTF8 = CPLRecodeFromWChar((const wchar_t*)sNclobstring, CPL_ENC_UTF16, CPL_ENC_UTF8); + + poFeat->SetField( i, pszUTF8 ); + CPLFree( pszUTF8 ); + + SE_nclob_free( &sNclobVal ); + CPLFree(sNclobstring); + } + else if( nSDEErr != SE_NULL_VALUE ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_get_nclob" ); + return NULL; + } + } + break; + +#endif + + case SE_DATE_TYPE: + { + struct tm sDateVal; + + nSDEErr = SE_stream_get_date( hStream, anFieldMap[i]+1, + &sDateVal ); + if( nSDEErr == SE_SUCCESS ) + { + poFeat->SetField( i, sDateVal.tm_year + 1900, sDateVal.tm_mon + 1, sDateVal.tm_mday, + sDateVal.tm_hour, sDateVal.tm_min, sDateVal.tm_sec, (sDateVal.tm_isdst > 0)); + } + else if( nSDEErr != SE_NULL_VALUE ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_get_date" ); + return NULL; + } + } + break; + + } + } + +/* -------------------------------------------------------------------- */ +/* Apply FID. */ +/* -------------------------------------------------------------------- */ + if( iFIDColumn != -1 ) + poFeat->SetFID( poFeat->GetFieldAsInteger( iFIDColumn ) ); + else + poFeat->SetFID( nNextFID++ ); + +/* -------------------------------------------------------------------- */ +/* Fetch geometry. */ +/* -------------------------------------------------------------------- */ + if( iShapeColumn != -1 ) + { + SE_SHAPE hShape = 0; + + nSDEErr = SE_shape_create( NULL, &hShape ); + + if( nSDEErr != SE_SUCCESS ) + poDS->IssueSDEError( nSDEErr, "SE_shape_create" ); + else + { + nSDEErr = SE_stream_get_shape( hStream, (short) (iShapeColumn+1), + hShape ); + if( nSDEErr != SE_SUCCESS ) + poDS->IssueSDEError( nSDEErr, "SE_stream_get_shape" ); + } + + if( nSDEErr == SE_SUCCESS ) + poFeat->SetGeometryDirectly( TranslateSDEGeometry( hShape ) ); + + SE_shape_free( hShape ); + } + + return poFeat; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRSDELayer::GetNextFeature() + +{ + int nSDEErr; + +/* -------------------------------------------------------------------- */ +/* Make sure we have an installed query executed. */ +/* -------------------------------------------------------------------- */ + if( !bQueryInstalled && !InstallQuery( FALSE ) ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Fetch the next record. */ +/* -------------------------------------------------------------------- */ + while( TRUE ) + { + nSDEErr = SE_stream_fetch( hStream ); + if( nSDEErr == SE_FINISHED ) + { + bQueryInstalled = FALSE; + return NULL; + } + else if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_fetch" ); + return NULL; + } + + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Translate into an OGRFeature. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poFeature = TranslateSDERecord(); + + if( poFeature != NULL ) + { + if( m_poFilterGeom == NULL + || m_bFilterIsEnvelope + || FilterGeometry( poFeature->GetGeometryRef() ) ) + return poFeature; + + delete poFeature; + } + } +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRSDELayer::GetFeature( GIntBig nFeatureId ) + +{ + int nSDEErr; + + if( iFIDColumn == -1 ) + return OGRLayer::GetFeature( nFeatureId ); + +/* -------------------------------------------------------------------- */ +/* Our direct row access will terminate any active queries. */ +/* -------------------------------------------------------------------- */ + ResetReading(); + +/* -------------------------------------------------------------------- */ +/* Create stream, or reset it. */ +/* -------------------------------------------------------------------- */ + if( ResetStream() != OGRERR_NONE ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* We want to fetch all the columns, just like we normally */ +/* would for GetNextFeature(). */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_stream_fetch_row( hStream, poFeatureDefn->GetName(), + nFeatureId, + CSLCount( papszAllColumns ), + (const char **) papszAllColumns ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_fetch_row" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* We got our row, now translate it. */ +/* -------------------------------------------------------------------- */ + return TranslateSDERecord(); +} + + +/************************************************************************/ +/* ResetStream() */ +/* */ +/* Create or reset stream environment */ +/************************************************************************/ + +OGRErr OGRSDELayer::ResetStream() + +{ + LONG nSDEErr; + + if( hStream == NULL ) + { + nSDEErr = SE_stream_create( poDS->GetConnection(), &hStream ); + if( nSDEErr != SE_SUCCESS) { + poDS->IssueSDEError( nSDEErr, "SE_stream_create" ); + return OGRERR_FAILURE; + } + if (poDS->IsOpenForUpdate() && poDS->UseVersionEdits()) { + nSDEErr = SE_stream_set_state( hStream, + poDS->GetNextState(), + SE_NULL_STATE_ID, + SE_STATE_DIFF_NOCHECK ); + if( nSDEErr != SE_SUCCESS) { + poDS->IssueSDEError( nSDEErr, "SE_stream_set_state" ); + return OGRERR_FAILURE; + } + } + else { + nSDEErr = SE_stream_set_state( hStream, + poDS->GetState(), + poDS->GetState(), + SE_STATE_DIFF_NOCHECK ); + if( nSDEErr != SE_SUCCESS) { + poDS->IssueSDEError( nSDEErr, "SE_stream_set_state" ); + return OGRERR_FAILURE; + } + } + } + else + { + nSDEErr = SE_stream_close( hStream, TRUE ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_close" ); + return OGRERR_FAILURE; + } + if (poDS->IsOpenForUpdate() && poDS->UseVersionEdits()) { + nSDEErr = SE_stream_set_state( hStream, + poDS->GetNextState(), + SE_NULL_STATE_ID, + SE_STATE_DIFF_NOCHECK ); + if( nSDEErr != SE_SUCCESS) { + poDS->IssueSDEError( nSDEErr, "SE_stream_set_state" ); + return OGRERR_FAILURE; + } + } + else { + nSDEErr = SE_stream_set_state( hStream, + poDS->GetState(), + poDS->GetState(), + SE_STATE_DIFF_NOCHECK ); + if( nSDEErr != SE_SUCCESS) { + poDS->IssueSDEError( nSDEErr, "SE_stream_set_state" ); + return OGRERR_FAILURE; + } + } + + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* Issue a special "counter only" query that will just fetch */ +/* objectids, and count the result set. This will inherently */ +/* include all the spatial and attribute filtering logic in the */ +/* database. It would be nice if we could also use a COUNT() */ +/* operator in the database. */ +/************************************************************************/ + +GIntBig OGRSDELayer::GetFeatureCount( int bForce ) + +{ +/* -------------------------------------------------------------------- */ +/* If there is no attribute or spatial filter in place, then */ +/* use the SDE function call to obtain the number of */ +/* features. The performance difference between this and manual */ +/* iteration is significant. */ +/* -------------------------------------------------------------------- */ + if( osAttributeFilter.empty() && m_poFilterGeom == NULL + && NeedLayerInfo() ) + { + SE_LAYER_STATS layerstats = {0}; + char szTableName[SE_QUALIFIED_TABLE_NAME]; + char szShapeColumn[SE_MAX_COLUMN_LEN]; + int nSDEErr; + + szTableName[0] = '\0'; + szShapeColumn[0] = '\0'; + + nSDEErr = SE_layerinfo_get_spatial_column( hLayerInfo, szTableName, szShapeColumn ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_layerinfo_get_spatial_column" ); + return -1; + } + + nSDEErr = SE_layer_get_statistics( poDS->GetConnection(), szTableName, szShapeColumn, + &layerstats); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_layer_get_statistics" ); + return -1; + } + + return layerstats.TotalFeatures; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise use direct reading of the result set, though we */ +/* skip translating into OGRFeatures at least. */ +/* -------------------------------------------------------------------- */ + int nSDEErr, nFeatureCount = 0; + + ResetReading(); + if( !InstallQuery( TRUE ) ) + return -1; + + for( nSDEErr = SE_stream_fetch( hStream ); + nSDEErr == SE_SUCCESS; + nSDEErr = SE_stream_fetch( hStream ) ) + { + nFeatureCount++; + } + + if( nSDEErr != SE_FINISHED ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_fetch" ); + return -1; + } + + ResetReading(); + + return nFeatureCount; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRSDELayer::GetExtent (OGREnvelope *psExtent, int bForce) + +{ + if( !NeedLayerInfo() ) + return OGRERR_FAILURE; + + if (bForce) { + return OGRLayer::GetExtent( psExtent, bForce ); + } + + SE_ENVELOPE sEnvelope; + int nSDEErr; + + nSDEErr = SE_layerinfo_get_envelope( hLayerInfo, &sEnvelope ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_layerinfo_get_envelope" ); + return OGRERR_FAILURE; + } + + psExtent->MinX = sEnvelope.minx; + psExtent->MinY = sEnvelope.miny; + psExtent->MaxX = sEnvelope.maxx; + psExtent->MaxY = sEnvelope.maxy; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ +OGRErr OGRSDELayer::CreateField( OGRFieldDefn *poFieldIn, int bApproxOK ) + +{ + SE_COLUMN_DEF sColumnDef; + OGRFieldDefn oField( poFieldIn ); + LONG nSDEErr; + + CPLAssert( poFieldIn != NULL ); + CPLAssert( poFeatureDefn != NULL ); + + /* TODO: Do we need to launder column names in the same way that OCI/PG + * do? If so, do we also need to launder table names? */ + strncpy( sColumnDef.column_name, oField.GetNameRef(), SE_MAX_COLUMN_LEN ); + + sColumnDef.nulls_allowed = TRUE; + sColumnDef.decimal_digits = 0; + +/* -------------------------------------------------------------------- */ +/* Set the new column's SDE type. We intentionally use deprecated */ +/* SDE field types for backwards compatibility with 8.x servers */ +/* -------------------------------------------------------------------- */ + if( oField.GetType() == OFTInteger ) + sColumnDef.sde_type = SE_INTEGER_TYPE; + + else if( oField.GetType() == OFTReal ) + sColumnDef.sde_type = SE_DOUBLE_TYPE; + + else if( oField.GetType() == OFTString ) + { + const char *pszUseNSTRING = + CPLGetConfigOption( "OGR_SDE_USE_NSTRING", "FALSE" ); + + if( bUseNSTRING || CSLTestBoolean( pszUseNSTRING ) ) + sColumnDef.sde_type = SE_NSTRING_TYPE; + else + sColumnDef.sde_type = SE_STRING_TYPE; + } + else if( oField.GetType() == OFTDate + || oField.GetType() == OFTTime + || oField.GetType() == OFTDateTime + ) + sColumnDef.sde_type = SE_DATE_TYPE; + + else if( bApproxOK ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Can't create field %s with type %s on SDE layers - creating " + "as SE_STRING_TYPE.", + oField.GetNameRef(), + OGRFieldDefn::GetFieldTypeName(oField.GetType()) ); + + sColumnDef.sde_type = SE_STRING_TYPE; + } + + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can't create field %s with type %s on SDE layers.", + oField.GetNameRef(), + OGRFieldDefn::GetFieldTypeName(oField.GetType()) ); + + return OGRERR_FAILURE; + } + + +/* -------------------------------------------------------------------- */ +/* Set field width and precision */ +/* -------------------------------------------------------------------- */ + if( bPreservePrecision && oField.GetWidth() != 0 ) + { + sColumnDef.size = oField.GetWidth(); + + if( oField.GetPrecision() != 0 && oField.GetType() == OFTReal ) + sColumnDef.decimal_digits = oField.GetPrecision(); + + else if( oField.GetType() == OFTReal ) + { + /* Float types require a >0 decimal_digits */ + sColumnDef.decimal_digits = 6; + } + } + else if( !bPreservePrecision || oField.GetWidth() == 0 ) + { + if( oField.GetType() == OFTReal ) + { + sColumnDef.size = 24; + sColumnDef.decimal_digits = 6; + } + else + { + /* Set the size and decimal digits to 0, which instructs SDE to use + * DBMS-sensible defaults for these columns + */ + sColumnDef.size = 0; + } + } + + +/* -------------------------------------------------------------------- */ +/* Create the new field */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_table_add_column( poDS->GetConnection(), + poFeatureDefn->GetName(), + &sColumnDef ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_table_add_column" ); + return OGRERR_FAILURE; + } + + poFeatureDefn->AddFieldDefn( &oField ); + anFieldTypeMap.push_back( sColumnDef.sde_type ); + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ +OGRErr OGRSDELayer::ISetFeature( OGRFeature *poFeature ) + +{ + LONG nSDEErr; + + CPLAssert( poFeature != NULL ); + CPLAssert( poFeatureDefn != NULL ); + + if( !NeedLayerInfo() ) // Need hCoordRef, layerinfo shape types + return OGRERR_FAILURE; + + ResetReading(); + + if( ResetStream() != OGRERR_NONE ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Delegate setup of update operation to TranslateOGRRecord() */ +/* -------------------------------------------------------------------- */ + if( TranslateOGRRecord( poFeature, FALSE ) != OGRERR_NONE ) + return OGRERR_FAILURE; // TranslateOGRRecord() will report the error + +/* -------------------------------------------------------------------- */ +/* Execute the update */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_stream_execute( hStream ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_execute" ); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ +OGRErr OGRSDELayer::ICreateFeature( OGRFeature *poFeature ) + +{ + LONG nSDEErr; + + CPLAssert( poFeature != NULL ); + CPLAssert( poFeatureDefn != NULL ); + + if( !NeedLayerInfo() ) // Need hCoordRef, layerinfo shape types + return OGRERR_FAILURE; + + ResetReading(); + + if( ResetStream() != OGRERR_NONE ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Delegate setup of insert operation to TranslateOGRRecord() */ +/* -------------------------------------------------------------------- */ + if( TranslateOGRRecord( poFeature, TRUE ) != OGRERR_NONE ) + return OGRERR_FAILURE; // TranslateOGRRecord() will report the error + +/* -------------------------------------------------------------------- */ +/* Execute the insert */ +/* -------------------------------------------------------------------- */ + nSDEErr = SE_stream_execute( hStream ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_execute" ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* If ROWIDs are managed by SDE, then get the last inserted ID */ +/* which is the feature ID for this new feature. If the ROWID */ +/* is USER-managed, then TranslateOGRRecord() will have set */ +/* the FID. */ +/* -------------------------------------------------------------------- */ + if( nFIDColumnType == SE_REGISTRATION_ROW_ID_COLUMN_TYPE_SDE ) + { + LONG nLastFID; + + nSDEErr = SE_stream_last_inserted_row_id( hStream, &nLastFID ); + if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_last_inserted_row_id" ); + return OGRERR_FAILURE; + } + + poFeature->SetFID( nLastFID ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ +OGRErr OGRSDELayer::DeleteFeature( GIntBig nFID ) + +{ + LONG nSDEErr; + const char *pszWhere; + + ResetReading(); + + if( ResetStream() != OGRERR_NONE ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Verify that this layer has a FID column */ +/* -------------------------------------------------------------------- */ + if( iFIDColumn == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer \"%s\": cannot DeleteFeature(%ld): the layer has no " + "FID column detected.", poFeatureDefn->GetName(), nFID ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Perform the deletion */ +/* -------------------------------------------------------------------- */ + pszWhere = CPLSPrintf( "%s = %ld", osFIDColumnName.c_str(), nFID ); + + nSDEErr = SE_stream_delete_from_table( hStream, poFeatureDefn->GetName(), + pszWhere ); + + if( nSDEErr == SE_NO_ROWS_DELETED ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Layer \"%s\": Tried to delete a feature by FID, but no " + "rows were deleted!", + poFeatureDefn->GetName() ); + } + else if( nSDEErr != SE_SUCCESS ) + { + poDS->IssueSDEError( nSDEErr, "SE_stream_delete_from_table" ); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSDELayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return iFIDColumn != -1; + + else if( EQUAL(pszCap,OLCFastFeatureCount) + && osAttributeFilter.empty() + && m_poFilterGeom == NULL ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return TRUE; + + else if( EQUAL(pszCap,OLCCreateField) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + { + // We always treat NSTRING fields by translating to UTF8, but + // we don't do anything to regular string fields so this is a + // bit hard to answer simply. Also, whether writes support UTF8 + // depend on whether the field(s) were created as NSTRING fields. + return TRUE; + } + else + return FALSE; +} +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRSDELayer::GetSpatialRef() + +{ + NeedLayerInfo(); + + return poSRS; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/GNUmakefile new file mode 100644 index 000000000..bd21ccc8a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/GNUmakefile @@ -0,0 +1,37 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrsdtsdriver.o ogrsdtsdatasource.o ogrsdtslayer.o + +FRMTSLIB = ../ogr.a + +ISODIR = ../../../frmts/iso8211 +ISOLIB = $(ISODIR)/libiso8211.a + +SDTSDIR = ../../../frmts/sdts +SDTSLIB = $(SDTSDIR)/libsdts_al.a + +CPPFLAGS := -I.. -I../.. -I$(SDTSDIR) -I$(ISODIR) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +$(O_OBJ): $(SDTSDIR)/sdts_al.h + +clean: + rm -f *.o *-marker $(O_OBJ) + +install-libs: isolib-marker sdtslib-marker + @echo Done adding ISO8211 and SDTS libraries into $(FRMTSLIB) + +isolib-marker: $(ISOLIB) + ./install-libs.sh $(ISOLIB) $(FRMTSLIB) + touch isolib-marker + +sdtslib-marker: $(SDTSLIB) + ./install-libs.sh $(SDTSLIB) $(FRMTSLIB) + touch sdtslib-marker + +libs: + (cd ../../../frmts/iso8211; $(MAKE)) + (cd ../../../frmts/sdts; $(MAKE)) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/drv_sdts.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/drv_sdts.html new file mode 100644 index 000000000..860023a12 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/drv_sdts.html @@ -0,0 +1,40 @@ + + +SDTS + + + + +

    SDTS

    + +SDTS TVP (Topological Vector Profile) and Point Profile datasets are +supported for read access. Each primary attribute, node (point), line +and polygon module is treated as a distinct layer.

    + +To select an SDTS transfer, the name of the catalog file should be used. +For instance TR01CATD.DDF where the first four characters are all +that typically varies.

    + +SDTS coordinate system information is properly supported for most coordinate +systems defined in SDTS.

    + +There is no update or creation support in the SDTS driver.

    + +Note that in TVP datasets the polygon geometry is formed from the geometry +in the line modules. Primary attribute module attributes should be +properly attached to their related node, line or polygon features, but can +be accessed separately as their own layers.

    + +This driver has no support for raster (DEM) SDTS datasets.

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/install-libs.sh b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/install-libs.sh new file mode 100644 index 000000000..783d8978a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/install-libs.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +SRCLIB=$1 +DSTLIB=$2 + +OBJ=`ar t $SRCLIB | grep -v SORTED | grep -v SYMDEF` + +ar x $SRCLIB $OBJ +ar r $DSTLIB $OBJ +rm $OBJ + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/makefile.vc new file mode 100644 index 000000000..723b4ff33 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/makefile.vc @@ -0,0 +1,19 @@ + +OBJ = ogrsdtsdatasource.obj \ + ogrsdtsdriver.obj \ + ogrsdtslayer.obj + +EXTRAFLAGS = -I.. -I..\.. -I$(GDAL_ROOT)/frmts/sdts \ + -I$(GDAL_ROOT)/frmts/iso8211 + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/ogr_sdts.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/ogr_sdts.h new file mode 100644 index 000000000..f14a7e953 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/ogr_sdts.h @@ -0,0 +1,102 @@ +/****************************************************************************** + * $Id: ogr_sdts.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: STS Translator + * Purpose: Definition of classes finding SDTS support into OGRDriver + * framework. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SDTS_H_INCLUDED +#define _OGR_SDTS_H_INCLUDED + +#include "sdts_al.h" +#include "ogrsf_frmts.h" + +class OGRSDTSDataSource; + +/************************************************************************/ +/* OGRSDTSLayer */ +/************************************************************************/ + +class OGRSDTSLayer : public OGRLayer +{ + OGRFeatureDefn *poFeatureDefn; + + SDTSTransfer *poTransfer; + int iLayer; + SDTSIndexedReader *poReader; + + OGRSDTSDataSource *poDS; + + OGRFeature *GetNextUnfilteredFeature(); + + void BuildPolygons(); + int bPolygonsBuilt; + + public: + OGRSDTSLayer( SDTSTransfer *, int, OGRSDTSDataSource*); + ~OGRSDTSLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + +// OGRFeature *GetFeature( GIntBig nFeatureId ); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + +// GIntBig GetFeatureCount( int ); + + int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRSDTSDataSource */ +/************************************************************************/ + +class OGRSDTSDataSource : public OGRDataSource +{ + SDTSTransfer *poTransfer; + char *pszName; + + int nLayers; + OGRSDTSLayer **papoLayers; + + OGRSpatialReference *poSRS; + + public: + OGRSDTSDataSource(); + ~OGRSDTSDataSource(); + + int Open( const char * pszFilename, int bTestOpen ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + int TestCapability( const char * ); + + OGRSpatialReference *GetSpatialRef() { return poSRS; } +}; + +#endif /* ndef _OGR_SDTS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/ogrsdtsdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/ogrsdtsdatasource.cpp new file mode 100644 index 000000000..5a777afdc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/ogrsdtsdatasource.cpp @@ -0,0 +1,207 @@ +/****************************************************************************** + * $Id: ogrsdtsdatasource.cpp 13025 2007-11-25 18:03:46Z rouault $ + * + * Project: SDTS Translator + * Purpose: Implements OGRSDTSDataSource class + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sdts.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrsdtsdatasource.cpp 13025 2007-11-25 18:03:46Z rouault $"); + +/************************************************************************/ +/* OGRSDTSDataSource() */ +/************************************************************************/ + +OGRSDTSDataSource::OGRSDTSDataSource() + +{ + nLayers = 0; + papoLayers = NULL; + + pszName = NULL; + poSRS = NULL; + + poTransfer = NULL; +} + +/************************************************************************/ +/* ~OGRSDTSDataSource() */ +/************************************************************************/ + +OGRSDTSDataSource::~OGRSDTSDataSource() + +{ + int i; + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + CPLFree( pszName ); + + if( poSRS ) + poSRS->Release(); + + if( poTransfer ) + delete poTransfer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSDTSDataSource::TestCapability( const char * ) + +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRSDTSDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRSDTSDataSource::Open( const char * pszFilename, int bTestOpen ) + +{ + pszName = CPLStrdup( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Verify that the extension is DDF if we are testopening. */ +/* -------------------------------------------------------------------- */ + if( bTestOpen && !(strlen(pszFilename) > 4 && + EQUAL(pszFilename+strlen(pszFilename)-4,".ddf")) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Check a few bits of the header to see if it looks like an */ +/* SDTS file (really, if it looks like an ISO8211 file). */ +/* -------------------------------------------------------------------- */ + if( bTestOpen ) + { + FILE *fp; + char pachLeader[10]; + + fp = VSIFOpen( pszFilename, "rb" ); + if( fp == NULL ) + return FALSE; + + if( VSIFRead( pachLeader, 1, 10, fp ) != 10 + || (pachLeader[5] != '1' && pachLeader[5] != '2' + && pachLeader[5] != '3' ) + || pachLeader[6] != 'L' + || (pachLeader[8] != '1' && pachLeader[8] != ' ') ) + { + VSIFClose( fp ); + return FALSE; + } + + VSIFClose( fp ); + } + +/* -------------------------------------------------------------------- */ +/* Create a transfer, and open it. */ +/* -------------------------------------------------------------------- */ + poTransfer = new SDTSTransfer(); + + if( !poTransfer->Open( pszFilename ) ) + { + delete poTransfer; + poTransfer = NULL; + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Initialize the projection. */ +/* -------------------------------------------------------------------- */ + SDTS_XREF *poXREF = poTransfer->GetXREF(); + + poSRS = new OGRSpatialReference(); + + if( EQUAL(poXREF->pszSystemName,"UTM") ) + { + poSRS->SetUTM( poXREF->nZone, TRUE ); + } + + if( EQUAL(poXREF->pszDatum,"NAS") ) + poSRS->SetGeogCS("NAD27", "North_American_Datum_1927", + "Clarke 1866", 6378206.4, 294.978698213901 ); + + else if( EQUAL(poXREF->pszDatum,"NAX") ) + poSRS->SetGeogCS("NAD83", "North_American_Datum_1983", + "GRS 1980", 6378137, 298.257222101 ); + + else if( EQUAL(poXREF->pszDatum,"WGC") ) + poSRS->SetGeogCS("WGS 72", "WGS_1972", "NWL 10D", 6378135, 298.26 ); + + else if( EQUAL(poXREF->pszDatum,"WGE") ) + poSRS->SetGeogCS("WGS 84", "WGS_1984", + "WGS 84", 6378137, 298.257223563 ); + + else + poSRS->SetGeogCS("WGS 84", "WGS_1984", + "WGS 84", 6378137, 298.257223563 ); + + poSRS->Fixup(); + +/* -------------------------------------------------------------------- */ +/* Initialize a layer for each source dataset layer. */ +/* -------------------------------------------------------------------- */ + for( int iLayer = 0; iLayer < poTransfer->GetLayerCount(); iLayer++ ) + { + SDTSIndexedReader *poReader; + + if( poTransfer->GetLayerType( iLayer ) == SLTRaster ) + continue; + + poReader = poTransfer->GetLayerIndexedReader( iLayer ); + if( poReader == NULL ) + continue; + + papoLayers = (OGRSDTSLayer **) + CPLRealloc( papoLayers, sizeof(void*) * ++nLayers ); + papoLayers[nLayers-1] = new OGRSDTSLayer( poTransfer, iLayer, this ); + } + + return TRUE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/ogrsdtsdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/ogrsdtsdriver.cpp new file mode 100644 index 000000000..098ddc559 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/ogrsdtsdriver.cpp @@ -0,0 +1,97 @@ +/****************************************************************************** + * $Id: ogrsdtsdriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: SDTS Translator + * Purpose: Implements OGRSDTSDriver + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sdts.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrsdtsdriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRSDTSDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( !EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "DDF") ) + return NULL; + if( poOpenInfo->nHeaderBytes < 10 ) + return NULL; + const char* pachLeader = (const char* )poOpenInfo->pabyHeader; + if( (pachLeader[5] != '1' && pachLeader[5] != '2' + && pachLeader[5] != '3' ) + || pachLeader[6] != 'L' + || (pachLeader[8] != '1' && pachLeader[8] != ' ') ) + { + return NULL; + } + + OGRSDTSDataSource *poDS = new OGRSDTSDataSource(); + if( !poDS->Open( poOpenInfo->pszFilename, TRUE ) ) + { + delete poDS; + poDS = NULL; + } + + if( poDS != NULL && poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "SDTS Driver doesn't support update." ); + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRSDTS() */ +/************************************************************************/ + +void RegisterOGRSDTS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "OGR_SDTS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "OGR_SDTS" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "SDTS" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_sdts.html" ); + + poDriver->pfnOpen = OGRSDTSDriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/ogrsdtslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/ogrsdtslayer.cpp new file mode 100644 index 000000000..cfac5af63 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sdts/ogrsdtslayer.cpp @@ -0,0 +1,477 @@ +/****************************************************************************** + * $Id: ogrsdtslayer.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: SDTSReader + * Purpose: Implements OGRSDTSLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sdts.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrsdtslayer.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRSDTSLayer() */ +/* */ +/* Note that the OGRSDTSLayer assumes ownership of the passed */ +/* OGRFeatureDefn object. */ +/************************************************************************/ + +OGRSDTSLayer::OGRSDTSLayer( SDTSTransfer * poTransferIn, int iLayerIn, + OGRSDTSDataSource * poDSIn ) + +{ + poDS = poDSIn; + + poTransfer = poTransferIn; + iLayer = iLayerIn; + + poReader = poTransfer->GetLayerIndexedReader( iLayer ); + +/* -------------------------------------------------------------------- */ +/* Define the feature. */ +/* -------------------------------------------------------------------- */ + int iCATDEntry = poTransfer->GetLayerCATDEntry( iLayer ); + + poFeatureDefn = + new OGRFeatureDefn(poTransfer->GetCATD()->GetEntryModule(iCATDEntry)); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poDS->GetSpatialRef()); + + OGRFieldDefn oRecId( "RCID", OFTInteger ); + poFeatureDefn->AddFieldDefn( &oRecId ); + + if( poTransfer->GetLayerType(iLayer) == SLTPoint ) + { + poFeatureDefn->SetGeomType( wkbPoint ); + } + else if( poTransfer->GetLayerType(iLayer) == SLTLine ) + { + poFeatureDefn->SetGeomType( wkbLineString ); + + oRecId.SetName( "SNID" ); + poFeatureDefn->AddFieldDefn( &oRecId ); + + oRecId.SetName( "ENID" ); + poFeatureDefn->AddFieldDefn( &oRecId ); + } + else if( poTransfer->GetLayerType(iLayer) == SLTPoly ) + { + poFeatureDefn->SetGeomType( wkbPolygon ); + } + else if( poTransfer->GetLayerType(iLayer) == SLTAttr ) + { + poFeatureDefn->SetGeomType( wkbNone ); + } + +/* -------------------------------------------------------------------- */ +/* Add schema from referenced attribute records. */ +/* -------------------------------------------------------------------- */ + char **papszATIDRefs = NULL; + + if( poTransfer->GetLayerType(iLayer) != SLTAttr ) + papszATIDRefs = poReader->ScanModuleReferences(); + else + papszATIDRefs = CSLAddString( papszATIDRefs, + poTransfer->GetCATD()->GetEntryModule(iCATDEntry) ); + + for( int iTable = 0; + papszATIDRefs != NULL && papszATIDRefs[iTable] != NULL; + iTable++ ) + { + SDTSAttrReader *poAttrReader; + DDFFieldDefn *poFDefn; + +/* -------------------------------------------------------------------- */ +/* Get the attribute table reader, and the associated user */ +/* attribute field. */ +/* -------------------------------------------------------------------- */ + poAttrReader = (SDTSAttrReader *) + poTransfer->GetLayerIndexedReader( + poTransfer->FindLayer( papszATIDRefs[iTable] ) ); + + if( poAttrReader == NULL ) + continue; + + poFDefn = poAttrReader->GetModule()->FindFieldDefn( "ATTP" ); + if( poFDefn == NULL ) + poFDefn = poAttrReader->GetModule()->FindFieldDefn( "ATTS" ); + if( poFDefn == NULL ) + continue; + +/* -------------------------------------------------------------------- */ +/* Process each user subfield on the attribute table into an */ +/* OGR field definition. */ +/* -------------------------------------------------------------------- */ + for( int iSF=0; iSF < poFDefn->GetSubfieldCount(); iSF++ ) + { + DDFSubfieldDefn *poSFDefn = poFDefn->GetSubfield( iSF ); + int nWidth = poSFDefn->GetWidth(); + char *pszFieldName; + + if( poFeatureDefn->GetFieldIndex( poSFDefn->GetName() ) != -1 ) + pszFieldName = CPLStrdup( CPLSPrintf( "%s_%s", + papszATIDRefs[iTable], + poSFDefn->GetName() ) ); + else + pszFieldName = CPLStrdup( poSFDefn->GetName() ); + + switch( poSFDefn->GetType() ) + { + case DDFString: + { + OGRFieldDefn oStrField( pszFieldName, OFTString ); + + if( nWidth != 0 ) + oStrField.SetWidth( nWidth ); + + poFeatureDefn->AddFieldDefn( &oStrField ); + } + break; + + case DDFInt: + { + OGRFieldDefn oIntField( pszFieldName, OFTInteger ); + + if( nWidth != 0 ) + oIntField.SetWidth( nWidth ); + + poFeatureDefn->AddFieldDefn( &oIntField ); + } + break; + + case DDFFloat: + { + OGRFieldDefn oRealField( pszFieldName, OFTReal ); + + // We don't have a precision in DDF files, so we never even + // use the width. Otherwise with a precision of zero the + // result would look like an integer. + + poFeatureDefn->AddFieldDefn( &oRealField ); + } + break; + + default: + break; + } + + CPLFree( pszFieldName ); + + } /* next iSF (subfield) */ + } /* next iTable */ + CSLDestroy( papszATIDRefs ); +} + +/************************************************************************/ +/* ~OGRSDTSLayer() */ +/************************************************************************/ + +OGRSDTSLayer::~OGRSDTSLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "SDTS", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poFeatureDefn ) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRSDTSLayer::ResetReading() + +{ + poReader->Rewind(); +} + +/************************************************************************/ +/* AssignAttrRecordToFeature() */ +/************************************************************************/ + +static void +AssignAttrRecordToFeature( OGRFeature * poFeature, + CPL_UNUSED SDTSTransfer * poTransfer, + DDFField * poSR ) +{ +/* -------------------------------------------------------------------- */ +/* Process each subfield in the record. */ +/* -------------------------------------------------------------------- */ + DDFFieldDefn *poFDefn = poSR->GetFieldDefn(); + + for( int iSF=0; iSF < poFDefn->GetSubfieldCount(); iSF++ ) + { + DDFSubfieldDefn *poSFDefn = poFDefn->GetSubfield( iSF ); + int iField; + int nMaxBytes; + const char * pachData = poSR->GetSubfieldData(poSFDefn, + &nMaxBytes); +/* -------------------------------------------------------------------- */ +/* Indentify this field on the feature. */ +/* -------------------------------------------------------------------- */ + iField = poFeature->GetFieldIndex( poSFDefn->GetName() ); + +/* -------------------------------------------------------------------- */ +/* Handle each of the types. */ +/* -------------------------------------------------------------------- */ + switch( poSFDefn->GetType() ) + { + case DDFString: + const char *pszValue; + + pszValue = poSFDefn->ExtractStringData(pachData, nMaxBytes, + NULL); + + if( iField != -1 ) + poFeature->SetField( iField, pszValue ); + break; + + case DDFFloat: + double dfValue; + + dfValue = poSFDefn->ExtractFloatData(pachData, nMaxBytes, + NULL); + + if( iField != -1 ) + poFeature->SetField( iField, dfValue ); + break; + + case DDFInt: + int nValue; + + nValue = poSFDefn->ExtractIntData(pachData, nMaxBytes, NULL); + + if( iField != -1 ) + poFeature->SetField( iField, nValue ); + break; + + default: + break; + } + } /* next subfield */ +} + +/************************************************************************/ +/* GetNextUnfilteredFeature() */ +/************************************************************************/ + +OGRFeature * OGRSDTSLayer::GetNextUnfilteredFeature() + +{ +/* -------------------------------------------------------------------- */ +/* If not done before we need to assemble the geometry for a */ +/* polygon layer. */ +/* -------------------------------------------------------------------- */ + if( poTransfer->GetLayerType(iLayer) == SLTPoly ) + { + ((SDTSPolygonReader *) poReader)->AssembleRings(poTransfer,iLayer); + } + +/* -------------------------------------------------------------------- */ +/* Fetch the next sdts style feature object from the reader. */ +/* -------------------------------------------------------------------- */ + SDTSFeature *poSDTSFeature = poReader->GetNextFeature(); + OGRFeature *poFeature; + + if( poSDTSFeature == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the OGR feature. */ +/* -------------------------------------------------------------------- */ + poFeature = new OGRFeature( poFeatureDefn ); + + m_nFeaturesRead++; + + switch( poTransfer->GetLayerType(iLayer) ) + { +/* -------------------------------------------------------------------- */ +/* Translate point feature specific information and geometry. */ +/* -------------------------------------------------------------------- */ + case SLTPoint: + { + SDTSRawPoint *poPoint = (SDTSRawPoint *) poSDTSFeature; + + poFeature->SetGeometryDirectly( new OGRPoint( poPoint->dfX, + poPoint->dfY, + poPoint->dfZ ) ); + } + break; + +/* -------------------------------------------------------------------- */ +/* Translate line feature specific information and geometry. */ +/* -------------------------------------------------------------------- */ + case SLTLine: + { + SDTSRawLine *poLine = (SDTSRawLine *) poSDTSFeature; + OGRLineString *poOGRLine = new OGRLineString(); + + poOGRLine->setPoints( poLine->nVertices, + poLine->padfX, poLine->padfY, poLine->padfZ ); + poFeature->SetGeometryDirectly( poOGRLine ); + poFeature->SetField( "SNID", (int) poLine->oStartNode.nRecord ); + poFeature->SetField( "ENID", (int) poLine->oEndNode.nRecord ); + } + break; + +/* -------------------------------------------------------------------- */ +/* Translate polygon feature specific information and geometry. */ +/* -------------------------------------------------------------------- */ + case SLTPoly: + { + SDTSRawPolygon *poPoly = (SDTSRawPolygon *) poSDTSFeature; + OGRPolygon *poOGRPoly = new OGRPolygon(); + + for( int iRing = 0; iRing < poPoly->nRings; iRing++ ) + { + OGRLinearRing *poRing = new OGRLinearRing(); + int nVertices; + + if( iRing == poPoly->nRings - 1 ) + nVertices = poPoly->nVertices - poPoly->panRingStart[iRing]; + else + nVertices = poPoly->panRingStart[iRing+1] + - poPoly->panRingStart[iRing]; + + poRing->setPoints( nVertices, + poPoly->padfX + poPoly->panRingStart[iRing], + poPoly->padfY + poPoly->panRingStart[iRing], + poPoly->padfZ + poPoly->panRingStart[iRing] ); + + poOGRPoly->addRingDirectly( poRing ); + } + + poFeature->SetGeometryDirectly( poOGRPoly ); + } + break; + + default: + break; + } + +/* -------------------------------------------------------------------- */ +/* Set attributes for any indicated attribute records. */ +/* -------------------------------------------------------------------- */ + int iAttrRecord; + + for( iAttrRecord = 0; + iAttrRecord < poSDTSFeature->nAttributes; + iAttrRecord++) + { + DDFField *poSR; + + poSR = poTransfer->GetAttr( poSDTSFeature->paoATID+iAttrRecord ); + if( poSR != NULL ) + AssignAttrRecordToFeature( poFeature, poTransfer, poSR ); + } + +/* -------------------------------------------------------------------- */ +/* If this record is an attribute record, attach the local */ +/* attributes. */ +/* -------------------------------------------------------------------- */ + if( poTransfer->GetLayerType(iLayer) == SLTAttr ) + { + AssignAttrRecordToFeature( poFeature, poTransfer, + ((SDTSAttrRecord *) poSDTSFeature)->poATTR); + } + +/* -------------------------------------------------------------------- */ +/* Translate the record id. */ +/* -------------------------------------------------------------------- */ + poFeature->SetFID( poSDTSFeature->oModId.nRecord ); + poFeature->SetField( 0, (int) poSDTSFeature->oModId.nRecord ); + if( poFeature->GetGeometryRef() != NULL ) + poFeature->GetGeometryRef()->assignSpatialReference( + poDS->GetSpatialRef() ); + + if( !poReader->IsIndexed() ) + delete poSDTSFeature; + + return poFeature; +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRSDTSLayer::GetNextFeature() + +{ + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* Read features till we find one that satisfies our current */ +/* spatial criteria. */ +/* -------------------------------------------------------------------- */ + while( TRUE ) + { + poFeature = GetNextUnfilteredFeature(); + if( poFeature == NULL ) + break; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + break; + + delete poFeature; + } + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSDTSLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return FALSE; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/GNUmakefile new file mode 100644 index 000000000..84115ede2 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrsegukooadriver.o ogrsegukooadatasource.o ogrsegukooalayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_segukooa.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/drv_segukooa.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/drv_segukooa.html new file mode 100644 index 000000000..f906b7a73 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/drv_segukooa.html @@ -0,0 +1,25 @@ + + +SEG-P1 / UKOOA P1/90 + + + + +

    SEG-P1 / UKOOA P1/90

    + +(GDAL/OGR >= 1.9.0)

    + +This driver reads files in SEG-P1 and UKOOA P1/90 formats. Those files are simple +ASCII files that contain seismic shotpoints. Two layers are reported : one with +the points, and another ones where sequential points with same line name are merged +together in a single feature with a line geometry.

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/makefile.vc new file mode 100644 index 000000000..8776738cd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrsegukooadriver.obj ogrsegukooadatasource.obj ogrsegukooalayer.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/ogr_segukooa.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/ogr_segukooa.h new file mode 100644 index 000000000..c4220188c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/ogr_segukooa.h @@ -0,0 +1,157 @@ +/****************************************************************************** + * $Id: ogr_segukooa.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: SEG-P1 / UKOOA P1-90 Translator + * Purpose: Definition of classes for OGR SEG-P1 / UKOOA P1-90 driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMSEGUKOOAS OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SEGUKOOA_H_INCLUDED +#define _OGR_SEGUKOOA_H_INCLUDED + +#include "ogrsf_frmts.h" + +/************************************************************************/ +/* OGRSEGUKOOABaseLayer */ +/************************************************************************/ + +class OGRSEGUKOOABaseLayer : public OGRLayer +{ + protected: + OGRFeatureDefn* poFeatureDefn; + int bEOF; + int nNextFID; + + virtual OGRFeature * GetNextRawFeature() = 0; + + public: + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ) { return FALSE; } +}; + +/************************************************************************/ +/* OGRUKOOAP190Layer */ +/************************************************************************/ + +class OGRUKOOAP190Layer : public OGRSEGUKOOABaseLayer +{ + OGRSpatialReference* poSRS; + + VSILFILE* fp; + + int bUseEastingNorthingAsGeometry; + int nYear; + void ParseHeaders(); + + protected: + OGRFeature * GetNextRawFeature(); + + public: + OGRUKOOAP190Layer(const char* pszFilename, + VSILFILE* fp); + ~OGRUKOOAP190Layer(); + + + virtual void ResetReading(); +}; + +/************************************************************************/ +/* OGRSEGUKOOALineLayer */ +/************************************************************************/ + +class OGRSEGUKOOALineLayer : public OGRSEGUKOOABaseLayer +{ + OGRLayer *poBaseLayer; + OGRFeature *poNextBaseFeature; + + protected: + OGRFeature * GetNextRawFeature(); + + public: + OGRSEGUKOOALineLayer(const char* pszFilename, + OGRLayer *poBaseLayer); + ~OGRSEGUKOOALineLayer(); + + virtual void ResetReading(); +}; + + +/************************************************************************/ +/* OGRSEGP1Layer */ +/************************************************************************/ + +class OGRSEGP1Layer: public OGRSEGUKOOABaseLayer +{ + OGRSpatialReference* poSRS; + + VSILFILE* fp; + int nLatitudeCol; + + int bUseEastingNorthingAsGeometry; + + protected: + OGRFeature * GetNextRawFeature(); + + public: + OGRSEGP1Layer(const char* pszFilename, + VSILFILE* fp, + int nLatitudeCol); + ~OGRSEGP1Layer(); + + virtual void ResetReading(); + +public: + static char* ExpandTabs(const char* pszLine); + static int DetectLatitudeColumn(const char* pzLine); +}; + +/************************************************************************/ +/* OGRSEGUKOOADataSource */ +/************************************************************************/ + +class OGRSEGUKOOADataSource : public OGRDataSource +{ + char* pszName; + + OGRLayer** papoLayers; + int nLayers; + + public: + OGRSEGUKOOADataSource(); + ~OGRSEGUKOOADataSource(); + + int Open( const char * pszFilename ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_SEGUKOOA_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/ogrsegukooadatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/ogrsegukooadatasource.cpp new file mode 100644 index 000000000..c8fbbcab4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/ogrsegukooadatasource.cpp @@ -0,0 +1,193 @@ +/****************************************************************************** + * $Id: ogrsegukooadatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: SEG-P1 / UKOOA P1-90 Translator + * Purpose: Implements OGRSEGUKOOADataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMSEGUKOOAS OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_segukooa.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrsegukooadatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRSEGUKOOADataSource() */ +/************************************************************************/ + +OGRSEGUKOOADataSource::OGRSEGUKOOADataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; +} + +/************************************************************************/ +/* ~OGRSEGUKOOADataSource() */ +/************************************************************************/ + +OGRSEGUKOOADataSource::~OGRSEGUKOOADataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSEGUKOOADataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRSEGUKOOADataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRSEGUKOOADataSource::Open( const char * pszFilename ) + +{ + pszName = CPLStrdup( pszFilename ); + + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return FALSE; + + const char* pszLine; + CPLPushErrorHandler(CPLQuietErrorHandler); + pszLine = CPLReadLine2L(fp,81,NULL); + CPLPopErrorHandler(); + CPLErrorReset(); + + /* Both UKOOA P1/90 and SEG-P1 begins by a H character */ + if (pszLine == NULL || pszLine[0] != 'H') + { + VSIFCloseL(fp); + return FALSE; + } + +// -------------------------------------------------------------------- +// Does this appear to be a UKOOA P1/90 file? +// -------------------------------------------------------------------- + + if (strncmp(pszLine, "H0100 ", 6) == 0) + { + VSIFSeekL( fp, 0, SEEK_SET ); + + VSILFILE* fp2 = VSIFOpenL(pszFilename, "rb"); + if (fp2 == NULL) + { + VSIFCloseL(fp); + return FALSE; + } + + nLayers = 2; + papoLayers = (OGRLayer**) CPLMalloc(2 * sizeof(OGRLayer*)); + papoLayers[0] = new OGRUKOOAP190Layer(pszName, fp); + papoLayers[1] = new OGRSEGUKOOALineLayer(pszName, + new OGRUKOOAP190Layer(pszName, fp2)); + + return TRUE; + } + +// -------------------------------------------------------------------- +// Does this appear to be a SEG-P1 file? +// -------------------------------------------------------------------- + + /* Check first 20 header lines, and fetch the first point */ + for(int iLine = 0; iLine < 21; iLine ++) + { + const char* szPtr = pszLine; + for(;*szPtr != '\0';szPtr++) + { + if (*szPtr != 9 && *szPtr < 32) + { + VSIFCloseL(fp); + return FALSE; + } + } + + if (iLine == 20) + break; + + CPLPushErrorHandler(CPLQuietErrorHandler); + pszLine = CPLReadLine2L(fp,81,NULL); + CPLPopErrorHandler(); + CPLErrorReset(); + if (pszLine == NULL) + { + VSIFCloseL(fp); + return FALSE; + } + } + + char* pszExpandedLine = OGRSEGP1Layer::ExpandTabs(pszLine); + int nLatitudeCol = OGRSEGP1Layer::DetectLatitudeColumn(pszExpandedLine); + CPLFree(pszExpandedLine); + + if (nLatitudeCol > 0) + { + VSIFSeekL( fp, 0, SEEK_SET ); + + VSILFILE* fp2 = VSIFOpenL(pszFilename, "rb"); + if (fp2 == NULL) + { + VSIFCloseL(fp); + return FALSE; + } + + nLayers = 2; + papoLayers = (OGRLayer**) CPLMalloc(2 * sizeof(OGRLayer*)); + papoLayers[0] = new OGRSEGP1Layer(pszName, fp, nLatitudeCol); + papoLayers[1] = new OGRSEGUKOOALineLayer(pszName, + new OGRSEGP1Layer(pszName, fp2, + nLatitudeCol)); + + return TRUE; + } + + VSIFCloseL(fp); + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/ogrsegukooadriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/ogrsegukooadriver.cpp new file mode 100644 index 000000000..266d75d0d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/ogrsegukooadriver.cpp @@ -0,0 +1,88 @@ +/****************************************************************************** + * $Id: ogrsegukooadriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: SEG-P1 / UKOOA P1-90 Translator + * Purpose: Implements OGRSEGUKOOADriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMSEGUKOOAS OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_segukooa.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrsegukooadriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +extern "C" void RegisterOGRSEGUKOOA(); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRSEGUKOOADriverOpen( GDALOpenInfo* poOpenInfo ) +{ + if( poOpenInfo->eAccess == GA_Update || + poOpenInfo->fpL == NULL || + poOpenInfo->pabyHeader[0] != 'H' ) + { + return NULL; + } + + OGRSEGUKOOADataSource *poDS = new OGRSEGUKOOADataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRSEGUKOOA() */ +/************************************************************************/ + +void RegisterOGRSEGUKOOA() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "SEGUKOOA" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SEGUKOOA" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "SEG-P1 / UKOOA P1/90" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_segukooa.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRSEGUKOOADriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/ogrsegukooalayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/ogrsegukooalayer.cpp new file mode 100644 index 000000000..4c4ebcd6f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/ogrsegukooalayer.cpp @@ -0,0 +1,823 @@ +/****************************************************************************** + * $Id: ogrsegukooalayer.cpp 28849 2015-04-05 14:05:18Z goatbar $ + * + * Project: SEG-P1 / UKOOA P1-90 Translator + * Purpose: Implements OGRUKOOAP190Layer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMSEGUKOOAS OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_segukooa.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: ogrsegukooalayer.cpp 28849 2015-04-05 14:05:18Z goatbar $"); + +/************************************************************************/ +/* ExtractField() */ +/************************************************************************/ + +static void ExtractField(char* szField, const char* pszLine, int nOffset, int nLen) +{ + memcpy(szField, pszLine + nOffset, nLen); + szField[nLen] = '\0'; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRSEGUKOOABaseLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* OGRUKOOAP190Layer() */ +/************************************************************************/ + +typedef struct +{ + const char* pszName; + OGRFieldType eType; +} FieldDesc; + +static const FieldDesc UKOOAP190Fields[] = +{ + { "LINENAME", OFTString }, + { "VESSEL_ID", OFTString }, + { "SOURCE_ID", OFTString }, + { "OTHER_ID", OFTString }, + { "POINTNUMBER", OFTInteger }, + { "LONGITUDE", OFTReal }, + { "LATITUDE", OFTReal }, + { "EASTING", OFTReal }, + { "NORTHING", OFTReal }, + { "DEPTH", OFTReal }, + { "DAYOFYEAR", OFTInteger }, + { "TIME", OFTTime }, + { "DATETIME", OFTDateTime } +}; + +#define FIELD_LINENAME 0 +#define FIELD_VESSEL_ID 1 +#define FIELD_SOURCE_ID 2 +#define FIELD_OTHER_ID 3 +#define FIELD_POINTNUMBER 4 +#define FIELD_LONGITUDE 5 +#define FIELD_LATITUDE 6 +#define FIELD_EASTING 7 +#define FIELD_NORTHING 8 +#define FIELD_DEPTH 9 +#define FIELD_DAYOFYEAR 10 +#define FIELD_TIME 11 +#define FIELD_DATETIME 12 + +OGRUKOOAP190Layer::OGRUKOOAP190Layer( const char* pszFilename, + VSILFILE* fp ) + +{ + this->fp = fp; + nNextFID = 0; + bEOF = FALSE; + poSRS = NULL; + nYear = 0; + + poFeatureDefn = new OGRFeatureDefn( CPLGetBasename(pszFilename) ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint ); + + for(int i=0;i<(int)(sizeof(UKOOAP190Fields)/sizeof(UKOOAP190Fields[0]));i++) + { + OGRFieldDefn oField( UKOOAP190Fields[i].pszName, + UKOOAP190Fields[i].eType ); + poFeatureDefn->AddFieldDefn( &oField ); + } + + bUseEastingNorthingAsGeometry = + CSLTestBoolean(CPLGetConfigOption("UKOOAP190_USE_EASTING_NORTHING", "NO")); + + ParseHeaders(); + + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); +} + +/************************************************************************/ +/* ~OGRUKOOAP190Layer() */ +/************************************************************************/ + +OGRUKOOAP190Layer::~OGRUKOOAP190Layer() + +{ + poFeatureDefn->Release(); + + VSIFCloseL( fp ); + + if (poSRS) + poSRS->Release(); +} + +/************************************************************************/ +/* ParseHeaders() */ +/************************************************************************/ + +void OGRUKOOAP190Layer::ParseHeaders() +{ + while(TRUE) + { + const char* pszLine = CPLReadLine2L(fp,81,NULL); + if (pszLine == NULL || EQUALN(pszLine, "EOF", 3)) + { + break; + } + + int nLineLen = strlen(pszLine); + while(nLineLen > 0 && pszLine[nLineLen-1] == ' ') + { + ((char*)pszLine)[nLineLen-1] = '\0'; + nLineLen --; + } + + if (pszLine[0] != 'H') + break; + + if (nLineLen < 33) + continue; + + if (!bUseEastingNorthingAsGeometry && + strncmp(pszLine, "H1500", 5) == 0 && poSRS == NULL) + { + if (strncmp(pszLine + 33 - 1, "WGS84", 5) == 0 || + strncmp(pszLine + 33 - 1, "WGS-84", 6) == 0) + { + poSRS = new OGRSpatialReference(SRS_WKT_WGS84); + } + else if (strncmp(pszLine + 33 - 1, "WGS72", 5) == 0) + { + poSRS = new OGRSpatialReference(); + poSRS->SetFromUserInput("WGS72"); + } + } + else if (!bUseEastingNorthingAsGeometry && + strncmp(pszLine, "H1501", 5) == 0 && poSRS != NULL && + nLineLen >= 32 + 6 * 6 + 10) + { + char aszParams[6][6+1]; + char szZ[10+1]; + int i; + for(i=0;i<6;i++) + { + ExtractField(aszParams[i], pszLine, 33 - 1 + i * 6, 6); + } + ExtractField(szZ, pszLine, 33 - 1 + 6 * 6, 10); + poSRS->SetTOWGS84(CPLAtof(aszParams[0]), + CPLAtof(aszParams[1]), + CPLAtof(aszParams[2]), + CPLAtof(aszParams[3]), + CPLAtof(aszParams[4]), + CPLAtof(aszParams[5]), + CPLAtof(szZ)); + } + else if (strncmp(pszLine, "H0200", 5) == 0) + { + char** papszTokens = CSLTokenizeString(pszLine + 33 - 1); + for(int i = 0; papszTokens[i] != NULL; i++) + { + if (strlen(papszTokens[i]) == 4) + { + int nVal = atoi(papszTokens[i]); + if (nVal >= 1900) + { + if (nYear != 0 && nYear != nVal) + { + CPLDebug("SEGUKOOA", + "Several years found in H0200. Ignoring them!"); + nYear = 0; + break; + } + nYear = nVal; + } + } + } + CSLDestroy(papszTokens); + } + } + VSIFSeekL( fp, 0, SEEK_SET ); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRUKOOAP190Layer::ResetReading() + +{ + nNextFID = 0; + bEOF = FALSE; + VSIFSeekL( fp, 0, SEEK_SET ); +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRUKOOAP190Layer::GetNextRawFeature() +{ + if (bEOF) + return NULL; + + const char* pszLine; + + while(TRUE) + { + pszLine = CPLReadLine2L(fp,81,NULL); + if (pszLine == NULL || EQUALN(pszLine, "EOF", 3)) + { + bEOF = TRUE; + return NULL; + } + + int nLineLen = strlen(pszLine); + while(nLineLen > 0 && pszLine[nLineLen-1] == ' ') + { + ((char*)pszLine)[nLineLen-1] = '\0'; + nLineLen --; + } + + if (pszLine[0] == 'H' || nLineLen < 46) + continue; + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFID(nNextFID ++); + + char szLineName[12 + 1]; + ExtractField(szLineName, pszLine, 2-1, 12); + int i = 11; + while (i >= 0) + { + if (szLineName[i] == ' ') + szLineName[i] = '\0'; + else + break; + i --; + } + poFeature->SetField(FIELD_LINENAME, szLineName); + + char szVesselId[1+1]; + szVesselId[0] = pszLine[17-1]; + if (szVesselId[0] != ' ') + { + szVesselId[1] = '\0'; + poFeature->SetField(FIELD_VESSEL_ID, szVesselId); + } + + char szSourceId[1+1]; + szSourceId[0] = pszLine[18-1]; + if (szSourceId[0] != ' ') + { + szSourceId[1] = '\0'; + poFeature->SetField(FIELD_SOURCE_ID, szSourceId); + } + + char szOtherId[1+1]; + szOtherId[0] = pszLine[19-1]; + if (szOtherId[0] != ' ') + { + szOtherId[1] = '\0'; + poFeature->SetField(FIELD_OTHER_ID, szOtherId); + } + + char szPointNumber[6+1]; + ExtractField(szPointNumber, pszLine, 20-1, 6); + poFeature->SetField(4, atoi(szPointNumber)); + + char szDeg[3+1]; + char szMin[2+1]; + char szSec[5+1]; + + ExtractField(szDeg, pszLine, 26-1, 2); + ExtractField(szMin, pszLine, 26+2-1, 2); + ExtractField(szSec, pszLine, 26+2+2-1, 5); + double dfLat = atoi(szDeg) + atoi(szMin) / 60.0 + CPLAtof(szSec) / 3600.0; + if (pszLine[26+2+2+5-1] == 'S') + dfLat = -dfLat; + poFeature->SetField(FIELD_LATITUDE, dfLat); + + ExtractField(szDeg, pszLine, 36-1, 3); + ExtractField(szMin, pszLine, 36+3-1, 2); + ExtractField(szSec, pszLine, 36+3+2-1, 5); + double dfLon = atoi(szDeg) + atoi(szMin) / 60.0 + CPLAtof(szSec) / 3600.0; + if (pszLine[36+3+2+5-1] == 'W') + dfLon = -dfLon; + poFeature->SetField(FIELD_LONGITUDE, dfLon); + + OGRGeometry* poGeom = NULL; + if (!bUseEastingNorthingAsGeometry) + poGeom = new OGRPoint(dfLon, dfLat); + + if (nLineLen >= 64) + { + char szEasting[9+1]; + ExtractField(szEasting, pszLine, 47-1, 9); + double dfEasting = CPLAtof(szEasting); + poFeature->SetField(FIELD_EASTING, dfEasting); + + char szNorthing[9+1]; + ExtractField(szNorthing, pszLine, 56-1, 9); + double dfNorthing = CPLAtof(szNorthing); + poFeature->SetField(FIELD_NORTHING, dfNorthing); + + if (bUseEastingNorthingAsGeometry) + poGeom = new OGRPoint(dfEasting, dfNorthing); + } + + if (poGeom) + { + if (poSRS) + poGeom->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poGeom); + } + + if (nLineLen >= 70) + { + char szDepth[6+1]; + ExtractField(szDepth, pszLine, 65-1, 6); + double dfDepth = CPLAtof(szDepth); + poFeature->SetField(FIELD_DEPTH, dfDepth); + } + + int nDayOfYear = 0; + if (nLineLen >= 73) + { + char szDayOfYear[3+1]; + ExtractField(szDayOfYear, pszLine, 71-1, 3); + nDayOfYear = atoi(szDayOfYear); + poFeature->SetField(FIELD_DAYOFYEAR, nDayOfYear); + } + + if (nLineLen >= 79) + { + char szH[2+1], szM[2+1], szS[2+1]; + ExtractField(szH, pszLine, 74-1, 2); + ExtractField(szM, pszLine, 74-1+2, 2); + ExtractField(szS, pszLine, 74-1+2+2, 2); + poFeature->SetField(FIELD_TIME, 0, 0, 0, atoi(szH), atoi(szM), atoi(szS) ); + + if (nYear != 0) + { + #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) + static const int mon_lengths[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} + } ; + int bIsLeap = isleap(nYear); + int nMonth = 0; + int nDays = 0; + if ((bIsLeap && nDayOfYear >= 1 && nDayOfYear <= 366) || + (!bIsLeap && nDayOfYear >= 1 && nDayOfYear <= 365)) + { + while(nDayOfYear > nDays + mon_lengths[bIsLeap][nMonth]) + { + nDays += mon_lengths[bIsLeap][nMonth]; + nMonth ++; + } + int nDayOfMonth = nDayOfYear - nDays; + nMonth ++; + + poFeature->SetField(FIELD_DATETIME, nYear, nMonth, nDayOfMonth, + atoi(szH), atoi(szM), atoi(szS) ); + } + + } + } + + return poFeature; + } +} + +/************************************************************************/ +/* OGRSEGP1Layer() */ +/************************************************************************/ + +static const FieldDesc SEGP1Fields[] = +{ + { "LINENAME", OFTString }, + { "POINTNUMBER", OFTInteger }, + { "RESHOOTCODE", OFTString }, + { "LONGITUDE", OFTReal }, + { "LATITUDE", OFTReal }, + { "EASTING", OFTReal }, + { "NORTHING", OFTReal }, + { "DEPTH", OFTReal }, +#if 0 + { "YEAR", OFTInteger }, + { "DAYOFYEAR", OFTInteger }, + { "TIME", OFTTime }, + { "DATETIME", OFTDateTime } +#endif +}; + +#define SEGP1_FIELD_LINENAME 0 +#define SEGP1_FIELD_POINTNUMBER 1 +#define SEGP1_FIELD_RESHOOTCODE 2 +#define SEGP1_FIELD_LONGITUDE 3 +#define SEGP1_FIELD_LATITUDE 4 +#define SEGP1_FIELD_EASTING 5 +#define SEGP1_FIELD_NORTHING 6 +#define SEGP1_FIELD_DEPTH 7 +#define SEGP1_FIELD_DAYOFYEAR 8 +#define SEGP1_FIELD_TIME 9 +#define SEGP1_FIELD_DATETIME 10 + +OGRSEGP1Layer::OGRSEGP1Layer( const char* pszFilename, + VSILFILE* fp, + int nLatitudeCol ) + +{ + this->fp = fp; + this->nLatitudeCol = nLatitudeCol; + nNextFID = 0; + bEOF = FALSE; + poSRS = NULL; + + poFeatureDefn = new OGRFeatureDefn( CPLGetBasename(pszFilename) ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint ); + + for(int i=0;i<(int)(sizeof(SEGP1Fields)/sizeof(SEGP1Fields[0]));i++) + { + OGRFieldDefn oField( SEGP1Fields[i].pszName, + SEGP1Fields[i].eType ); + poFeatureDefn->AddFieldDefn( &oField ); + } + + bUseEastingNorthingAsGeometry = + CSLTestBoolean(CPLGetConfigOption("SEGP1_USE_EASTING_NORTHING", "NO")); + + ResetReading(); +} + +/************************************************************************/ +/* ~OGRSEGP1Layer() */ +/************************************************************************/ + +OGRSEGP1Layer::~OGRSEGP1Layer() + +{ + poFeatureDefn->Release(); + + VSIFCloseL( fp ); + + if (poSRS) + poSRS->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRSEGP1Layer::ResetReading() + +{ + nNextFID = 0; + bEOF = FALSE; + VSIFSeekL( fp, 0, SEEK_SET ); + + /* Skip first 20 header lines */ + const char* pszLine = NULL; + for(int i=0; i<20;i++) + { + pszLine = CPLReadLine2L(fp,81,NULL); + if (pszLine == NULL) + { + bEOF = TRUE; + break; + } + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRSEGP1Layer::GetNextRawFeature() +{ + if (bEOF) + return NULL; + + const char* pszLine = NULL; + while(TRUE) + { + pszLine = CPLReadLine2L(fp,81,NULL); + if (pszLine == NULL || EQUALN(pszLine, "EOF", 3)) + { + bEOF = TRUE; + return NULL; + } + + int nLineLen = strlen(pszLine); + while(nLineLen > 0 && pszLine[nLineLen-1] == ' ') + { + ((char*)pszLine)[nLineLen-1] = '\0'; + nLineLen --; + } + + char* pszExpandedLine = ExpandTabs(pszLine); + pszLine = pszExpandedLine; + nLineLen = strlen(pszLine); + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFID(nNextFID ++); + + OGRGeometry* poGeom = NULL; + + if (nLatitudeCol-1 + 19 <= nLineLen) + { + char szDeg[3+1]; + char szMin[2+1]; + char szSec[4+1]; + + ExtractField(szDeg, pszLine, nLatitudeCol-1, 2); + ExtractField(szMin, pszLine, nLatitudeCol+2-1, 2); + ExtractField(szSec, pszLine, nLatitudeCol+2+2-1, 4); + double dfLat = atoi(szDeg) + atoi(szMin) / 60.0 + atoi(szSec) / 100.0 / 3600.0; + if (pszLine[nLatitudeCol+2+2+4-1] == 'S') + dfLat = -dfLat; + poFeature->SetField(SEGP1_FIELD_LATITUDE, dfLat); + + ExtractField(szDeg, pszLine, nLatitudeCol+9-1, 3); + ExtractField(szMin, pszLine, nLatitudeCol+9+3-1, 2); + ExtractField(szSec, pszLine, nLatitudeCol+9+3+2-1, 4); + double dfLon = atoi(szDeg) + atoi(szMin) / 60.0 + atoi(szSec) / 100.0 / 3600.0; + if (pszLine[nLatitudeCol+9+3+2+4-1] == 'W') + dfLon = -dfLon; + poFeature->SetField(SEGP1_FIELD_LONGITUDE, dfLon); + + if (!bUseEastingNorthingAsGeometry) + poGeom = new OGRPoint(dfLon, dfLat); + } + + /* Normal layout -> extract other fields */ + if (nLatitudeCol == 27) + { + char szLineName[16 + 1]; + ExtractField(szLineName, pszLine, 2-1, 16); + int i = 15; + while (i >= 0) + { + if (szLineName[i] == ' ') + szLineName[i] = '\0'; + else + break; + i --; + } + poFeature->SetField(SEGP1_FIELD_LINENAME, szLineName); + + char szPointNumber[8+1]; + ExtractField(szPointNumber, pszLine, 18-1, 8); + poFeature->SetField(SEGP1_FIELD_POINTNUMBER, atoi(szPointNumber)); + + char szReshootCode[1+1]; + ExtractField(szReshootCode, pszLine, 26-1, 1); + poFeature->SetField(SEGP1_FIELD_RESHOOTCODE, szReshootCode); + + if (nLineLen >= 61) + { + char szEasting[8+1]; + ExtractField(szEasting, pszLine, 46-1, 8); + double dfEasting = CPLAtof(szEasting); + poFeature->SetField(SEGP1_FIELD_EASTING, dfEasting); + + char szNorthing[8+1]; + ExtractField(szNorthing, pszLine, 54-1, 8); + double dfNorthing = CPLAtof(szNorthing); + poFeature->SetField(SEGP1_FIELD_NORTHING, dfNorthing); + + if (bUseEastingNorthingAsGeometry) + poGeom = new OGRPoint(dfEasting, dfNorthing); + } + + if (nLineLen >= 66) + { + char szDepth[5+1]; + ExtractField(szDepth, pszLine, 62-1, 5); + double dfDepth = CPLAtof(szDepth); + poFeature->SetField(SEGP1_FIELD_DEPTH, dfDepth); + } + } + + if (poGeom) + { + if (poSRS) + poGeom->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly(poGeom); + } + + CPLFree(pszExpandedLine); + + return poFeature; + } +} + +/************************************************************************/ +/* ExpandTabs() */ +/************************************************************************/ + +char* OGRSEGP1Layer::ExpandTabs(const char* pszLine) +{ + char* pszExpandedLine = (char*)CPLMalloc(strlen(pszLine) * 8 + 1); + int j = 0; + for(int i=0; pszLine[i] != '\0'; i++) + { + /* A tab must be space-expanded to reach the next column number */ + /* which is a multiple of 8 */ + if (pszLine[i] == 9) + { + do + { + pszExpandedLine[j ++] = ' '; + } while ((j % 8) != 0); + } + else + pszExpandedLine[j ++] = pszLine[i]; + } + pszExpandedLine[j] = '\0'; + + return pszExpandedLine; +} + +/************************************************************************/ +/* DetectLatitudeColumn() */ +/************************************************************************/ + +/* Some SEG-P1 files have unusual offsets for latitude/longitude, so */ +/* we try our best to identify it even in case of non-standard layout */ +/* Return non-0 if detection is successful (column number starts at 1) */ + +int OGRSEGP1Layer::DetectLatitudeColumn(const char* pszLine) +{ + int nLen = strlen(pszLine); + if (nLen >= 45 && pszLine[0] == ' ' && + (pszLine[35-1] == 'N' || pszLine[35-1] == 'S') && + (pszLine[45-1] == 'E' || pszLine[45-1] == 'W')) + return 27; + + for(int i=8;iReference(); + poFeatureDefn->SetGeomType( wkbLineString ); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poBaseLayer->GetSpatialRef()); + + OGRFieldDefn oField( "LINENAME", OFTString ); + poFeatureDefn->AddFieldDefn( &oField ); + + this->poBaseLayer = poBaseLayer; + poNextBaseFeature = NULL; +} + +/************************************************************************/ +/* ~OGRSEGUKOOALineLayer() */ +/************************************************************************/ + +OGRSEGUKOOALineLayer::~OGRSEGUKOOALineLayer() +{ + delete poNextBaseFeature; + delete poBaseLayer; + + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRSEGUKOOALineLayer::ResetReading() + +{ + nNextFID = 0; + bEOF = FALSE; + delete poNextBaseFeature; + poNextBaseFeature = NULL; + poBaseLayer->ResetReading(); +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRSEGUKOOALineLayer::GetNextRawFeature() +{ + if (bEOF) + return NULL; + + /* Merge points of base layer that have same value for attribute(0) */ + /* into a single linestring */ + + OGRFeature* poFeature = NULL; + OGRLineString* poLS = NULL; + + if (poNextBaseFeature == NULL) + poNextBaseFeature = poBaseLayer->GetNextFeature(); + + while(poNextBaseFeature != NULL) + { + if (poNextBaseFeature->IsFieldSet(0) && + poNextBaseFeature->GetFieldAsString(0)[0] != '\0') + { + if (poFeature != NULL && + strcmp(poFeature->GetFieldAsString(0), + poNextBaseFeature->GetFieldAsString(0)) != 0) + { + return poFeature; + } + + OGRPoint* poPoint = + (OGRPoint*) poNextBaseFeature->GetGeometryRef(); + if (poPoint != NULL) + { + if (poFeature == NULL) + { + poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFID(nNextFID ++); + poFeature->SetField(0, + poNextBaseFeature->GetFieldAsString(0)); + poLS = new OGRLineString(); + if (poBaseLayer->GetSpatialRef()) + poLS->assignSpatialReference( + poBaseLayer->GetSpatialRef()); + poFeature->SetGeometryDirectly(poLS); + } + + poLS->addPoint(poPoint); + } + } + + delete poNextBaseFeature; + poNextBaseFeature = poBaseLayer->GetNextFeature(); + } + + bEOF = TRUE; + return poFeature; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/segukooa.txt b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/segukooa.txt new file mode 100644 index 000000000..ad1fa7748 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segukooa/segukooa.txt @@ -0,0 +1,13 @@ +http://www.litho.ucalgary.ca/geometry/abitibi/seg401.tr +http://www.litho.ucalgary.ca/geometry/Alberta/l1.seg +http://www.litho.ucalgary.ca/geometry/Alberta/l11.tr +http://www.litho.ucalgary.ca/geometry/Alberta/SALT21.segp1 +http://quashnet.er.usgs.gov/data/pubs/of99-037/of99-37_cd1/nav/shotnav/dumpsite.seg +http://quashnet.er.usgs.gov/data/pubs/of99-037/of99-37_cd1/nav/shotnav/mosaic.seg +http://pubs.usgs.gov/of/2000/ofr-00-0286/NPRANAV.SEG + +http://www.polarhovercraft.no/uploads/Main/Yermak2010_1.UKOOA.dat +http://cotuit.er.usgs.gov/Data/1974-019-FA/LN/001/74019nav.txt + +http://www.seg.org/documents/10161/77915/seg_p1_p2_p3.pdf +http://www.polarhovercraft.no/uploads/Main/ukooa_p1_90.pdf \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/GNUmakefile new file mode 100644 index 000000000..71188324e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrsegydriver.o ogrsegydatasource.o ogrsegylayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_segy.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/drv_segy.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/drv_segy.html new file mode 100644 index 000000000..2b5a18fca --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/drv_segy.html @@ -0,0 +1,27 @@ + + +SEG-Y / SEGY + + + + +

    SEG-Y / SEGY

    + +(GDAL/OGR >= 1.9.0)

    + +This driver reads files in SEG-Y format. Those files are binary files that contain single-line seismic digital data. +The driver will report the attributes of the trace header (in their raw form, see the SEG-Y specification for more +information), and use the receiver group coordinates as geometry. The sample values are also reported.

    + +A layer "{basefilename}_header" is also created and contains a single feature with the content of the text and binary file +headers.

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/makefile.vc new file mode 100644 index 000000000..91622207d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrsegydriver.obj ogrsegydatasource.obj ogrsegylayer.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/ogr_segy.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/ogr_segy.h new file mode 100644 index 000000000..6e495eaef --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/ogr_segy.h @@ -0,0 +1,158 @@ +/****************************************************************************** + * $Id: ogr_segy.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: SEG-Y Translator + * Purpose: Definition of classes for OGR SEG-Y driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMSEGYS OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SEGY_H_INCLUDED +#define _OGR_SEGY_H_INCLUDED + +#include "ogrsf_frmts.h" + +GInt16 SEGYReadMSBInt16(const GByte* pabyVal); +GInt32 SEGYReadMSBInt32(const GByte* pabyVal); + +typedef struct +{ + int nJobIdNumber; + int nLineNumber; + int nReelNumber; + int nDataTracesPerEnsemble; + int nAuxTracesPerEnsemble; + int nSampleInterval; + int nSampleIntervalOriginal; + int nSamplesPerDataTrace; + int nSamplesPerDataTraceOriginal; + int nDataSampleType; + int nEnsembleFold; + int nTraceSortingCode; + int nVerticalSumCode; + int nSweepFrequencyAtStart; + int nSweepFrequencyAtEnd; + int nSweepLength; + int nSweepType; + int nTraceNumberOfSweepChannel; + int nSweepTraceTaperLengthAtStart; + int nSweepTraceTaperLengthAtEnd; + int nTaperType; + int nCorrelated; + int nBinaryGainRecovered; + int nAmplitudeRecoveryMethod; + int nMeasurementSystem; + int nImpulseSignalPolarity; + int nVibratoryPolaryCode; + int nSEGYRevisionNumber; + double dfSEGYRevisionNumber; + int nFixedLengthTraceFlag; + int nNumberOfExtendedTextualFileHeader; +} SEGYBinaryFileHeader; + +/************************************************************************/ +/* OGRSEGYLayer */ +/************************************************************************/ + +class OGRSEGYLayer: public OGRLayer +{ + OGRFeatureDefn* poFeatureDefn; + int bEOF; + int nNextFID; + VSILFILE* fp; + + SEGYBinaryFileHeader sBFH; + int nDataSize; + + OGRFeature * GetNextRawFeature(); + + public: + OGRSEGYLayer(const char* pszFilename, + VSILFILE* fp, + SEGYBinaryFileHeader* psBFH); + ~OGRSEGYLayer(); + + virtual OGRFeature * GetNextFeature(); + + virtual void ResetReading(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ) { return FALSE; } +}; + +/************************************************************************/ +/* OGRSEGYHeaderLayer */ +/************************************************************************/ + +class OGRSEGYHeaderLayer: public OGRLayer +{ + OGRFeatureDefn* poFeatureDefn; + int bEOF; + + SEGYBinaryFileHeader sBFH; + char* pszHeaderText; + + OGRFeature * GetNextRawFeature(); + + public: + OGRSEGYHeaderLayer(const char* pszLayerName, + SEGYBinaryFileHeader* psBFH, + const char* pszHeaderText); + ~OGRSEGYHeaderLayer(); + + virtual OGRFeature * GetNextFeature(); + + virtual void ResetReading(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ) { return FALSE; } +}; + +/************************************************************************/ +/* OGRSEGYDataSource */ +/************************************************************************/ + +class OGRSEGYDataSource : public OGRDataSource +{ + char* pszName; + + OGRLayer** papoLayers; + int nLayers; + + public: + OGRSEGYDataSource(); + ~OGRSEGYDataSource(); + + int Open( const char * pszFilename, const char* pszHeaderText ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_SEGY_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/ogrsegydatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/ogrsegydatasource.cpp new file mode 100644 index 000000000..2f759d1fd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/ogrsegydatasource.cpp @@ -0,0 +1,214 @@ +/****************************************************************************** + * $Id: ogrsegydatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: SEG-Y Translator + * Purpose: Implements OGRSEGYDataSource class. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMSEGYS OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_segy.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrsegydatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRSEGYDataSource() */ +/************************************************************************/ + +OGRSEGYDataSource::OGRSEGYDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; +} + +/************************************************************************/ +/* ~OGRSEGYDataSource() */ +/************************************************************************/ + +OGRSEGYDataSource::~OGRSEGYDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSEGYDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRSEGYDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* SEGYReadMSBInt16() */ +/************************************************************************/ + +GInt16 SEGYReadMSBInt16(const GByte* pabyVal) +{ + GInt16 nVal; + memcpy(&nVal, pabyVal, 2); + CPL_MSBPTR16(&nVal); + return nVal; +} + +/************************************************************************/ +/* SEGYReadMSBInt32() */ +/************************************************************************/ + +GInt32 SEGYReadMSBInt32(const GByte* pabyVal) +{ + GInt32 nVal; + memcpy(&nVal, pabyVal, 4); + CPL_MSBPTR32(&nVal); + return nVal; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRSEGYDataSource::Open( const char * pszFilename, const char* pszASCIITextHeader) + +{ + pszName = CPLStrdup( pszFilename ); + + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return FALSE; + + VSIFSeekL(fp, 3200, SEEK_SET); + +// -------------------------------------------------------------------- +// Read the next 400 bytes, where the Binary File Header is +// located +// -------------------------------------------------------------------- + + GByte abyFileHeader[400]; + if ((int)VSIFReadL(abyFileHeader, 1, 400, fp) != 400) + { + VSIFCloseL(fp); + return FALSE; + } + + SEGYBinaryFileHeader sBFH; + + sBFH.nJobIdNumber = SEGYReadMSBInt32(abyFileHeader + 0); + sBFH.nLineNumber = SEGYReadMSBInt32(abyFileHeader + 4); + sBFH.nReelNumber = SEGYReadMSBInt32(abyFileHeader + 8); + sBFH.nDataTracesPerEnsemble = SEGYReadMSBInt16(abyFileHeader + 12); + sBFH.nAuxTracesPerEnsemble = SEGYReadMSBInt16(abyFileHeader + 14); + sBFH.nSampleInterval = SEGYReadMSBInt16(abyFileHeader + 16); + sBFH.nSampleIntervalOriginal = SEGYReadMSBInt16(abyFileHeader + 18); + sBFH.nSamplesPerDataTrace = SEGYReadMSBInt16(abyFileHeader + 20); + sBFH.nSamplesPerDataTraceOriginal = SEGYReadMSBInt16(abyFileHeader + 22); + sBFH.nDataSampleType = SEGYReadMSBInt16(abyFileHeader + 24); + sBFH.nEnsembleFold = SEGYReadMSBInt16(abyFileHeader + 26); + sBFH.nTraceSortingCode = SEGYReadMSBInt16(abyFileHeader + 28); + sBFH.nVerticalSumCode = SEGYReadMSBInt16(abyFileHeader + 30); + sBFH.nSweepFrequencyAtStart = SEGYReadMSBInt16(abyFileHeader + 32); + sBFH.nSweepFrequencyAtEnd = SEGYReadMSBInt16(abyFileHeader + 34); + sBFH.nSweepLength = SEGYReadMSBInt16(abyFileHeader + 36); + sBFH.nSweepType = SEGYReadMSBInt16(abyFileHeader + 38); + sBFH.nTraceNumberOfSweepChannel = SEGYReadMSBInt16(abyFileHeader + 40); + sBFH.nSweepTraceTaperLengthAtStart = SEGYReadMSBInt16(abyFileHeader + 42); + sBFH.nSweepTraceTaperLengthAtEnd = SEGYReadMSBInt16(abyFileHeader + 44); + sBFH.nTaperType = SEGYReadMSBInt16(abyFileHeader + 46); + sBFH.nCorrelated = SEGYReadMSBInt16(abyFileHeader + 48); + sBFH.nBinaryGainRecovered = SEGYReadMSBInt16(abyFileHeader + 50); + sBFH.nAmplitudeRecoveryMethod = SEGYReadMSBInt16(abyFileHeader + 52); + sBFH.nMeasurementSystem = SEGYReadMSBInt16(abyFileHeader + 54); + sBFH.nImpulseSignalPolarity = SEGYReadMSBInt16(abyFileHeader + 56); + sBFH.nVibratoryPolaryCode = SEGYReadMSBInt16(abyFileHeader + 58); + sBFH.nSEGYRevisionNumber = SEGYReadMSBInt16(abyFileHeader + 300) & 0xffff; + sBFH.dfSEGYRevisionNumber = sBFH.nSEGYRevisionNumber / 256.0; + sBFH.nFixedLengthTraceFlag = SEGYReadMSBInt16(abyFileHeader + 302); + sBFH.nNumberOfExtendedTextualFileHeader = SEGYReadMSBInt16(abyFileHeader + 304); + +#if 0 + CPLDebug("SIGY", "nJobIdNumber = %d", sBFH.nJobIdNumber); + CPLDebug("SIGY", "nLineNumber = %d", sBFH.nLineNumber); + CPLDebug("SIGY", "nReelNumber = %d", sBFH.nReelNumber); + CPLDebug("SIGY", "nDataTracesPerEnsemble = %d", sBFH.nDataTracesPerEnsemble); + CPLDebug("SIGY", "nAuxTracesPerEnsemble = %d", sBFH.nAuxTracesPerEnsemble); + CPLDebug("SIGY", "nSampleInterval = %d", sBFH.nSampleInterval); + CPLDebug("SIGY", "nSampleIntervalOriginal = %d", sBFH.nSampleIntervalOriginal); + CPLDebug("SIGY", "nSamplesPerDataTrace = %d", sBFH.nSamplesPerDataTrace); + CPLDebug("SIGY", "nSamplesPerDataTraceOriginal = %d", sBFH.nSamplesPerDataTraceOriginal); + CPLDebug("SIGY", "nDataSampleType = %d", sBFH.nDataSampleType); + CPLDebug("SIGY", "nEnsembleFold = %d", sBFH.nEnsembleFold); + CPLDebug("SIGY", "nTraceSortingCode = %d", sBFH.nTraceSortingCode); + CPLDebug("SIGY", "nVerticalSumCode = %d", sBFH.nVerticalSumCode); + CPLDebug("SIGY", "nSweepFrequencyAtStart = %d", sBFH.nSweepFrequencyAtStart); + CPLDebug("SIGY", "nSweepFrequencyAtEnd = %d", sBFH.nSweepFrequencyAtEnd); + CPLDebug("SIGY", "nSweepLength = %d", sBFH.nSweepLength); + CPLDebug("SIGY", "nSweepType = %d", sBFH.nSweepType); + CPLDebug("SIGY", "nTraceNumberOfSweepChannel = %d", sBFH.nTraceNumberOfSweepChannel); + CPLDebug("SIGY", "nSweepTraceTaperLengthAtStart = %d", sBFH.nSweepTraceTaperLengthAtStart); + CPLDebug("SIGY", "nSweepTraceTaperLengthAtEnd = %d", sBFH.nSweepTraceTaperLengthAtEnd); + CPLDebug("SIGY", "nTaperType = %d", sBFH.nTaperType); + CPLDebug("SIGY", "nCorrelated = %d", sBFH.nCorrelated); + CPLDebug("SIGY", "nBinaryGainRecovered = %d", sBFH.nBinaryGainRecovered); + CPLDebug("SIGY", "nAmplitudeRecoveryMethod = %d", sBFH.nAmplitudeRecoveryMethod); + CPLDebug("SIGY", "nMeasurementSystem = %d", sBFH.nMeasurementSystem); + CPLDebug("SIGY", "nImpulseSignalPolarity = %d", sBFH.nImpulseSignalPolarity); + CPLDebug("SIGY", "nVibratoryPolaryCode = %d", sBFH.nVibratoryPolaryCode); + CPLDebug("SIGY", "nSEGYRevisionNumber = %d", sBFH.nSEGYRevisionNumber); + CPLDebug("SIGY", "dfSEGYRevisionNumber = %f", sBFH.dfSEGYRevisionNumber); + CPLDebug("SIGY", "nFixedLengthTraceFlag = %d", sBFH.nFixedLengthTraceFlag); + CPLDebug("SIGY", "nNumberOfExtendedTextualFileHeader = %d", sBFH.nNumberOfExtendedTextualFileHeader); +#endif + +// -------------------------------------------------------------------- +// Create layer +// -------------------------------------------------------------------- + + nLayers = 2; + papoLayers = (OGRLayer**) CPLMalloc(nLayers * sizeof(OGRLayer*)); + papoLayers[0] = new OGRSEGYLayer(pszName, fp, &sBFH); + papoLayers[1] = new OGRSEGYHeaderLayer(CPLSPrintf("%s_header", CPLGetBasename(pszName)), &sBFH, pszASCIITextHeader); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/ogrsegydriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/ogrsegydriver.cpp new file mode 100644 index 000000000..0a95620fc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/ogrsegydriver.cpp @@ -0,0 +1,185 @@ +/****************************************************************************** + * $Id: ogrsegydriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: SEG-Y Translator + * Purpose: Implements OGRSEGYDriver class. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMSEGYS OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_segy.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrsegydriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* EBCDICToASCII */ +/************************************************************************/ + +static const GByte EBCDICToASCII[] = +{ +0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +0x10, 0x11, 0x12, 0x13, 0x9D, 0x85, 0x08, 0x87, 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F, +0x80, 0x81, 0x82, 0x83, 0x84, 0x0A, 0x17, 0x1B, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07, +0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, 0x98, 0x99, 0x9A, 0x9B, 0x14, 0x15, 0x9E, 0x1A, +0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA2, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, +0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAC, +0x2D, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, +0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x5C, 0x00, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, +}; + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRSEGYDriverOpen( GDALOpenInfo* poOpenInfo ) +{ + if( poOpenInfo->eAccess == GA_Update || + poOpenInfo->fpL == NULL || + !poOpenInfo->TryToIngest(3200+400) || + poOpenInfo->nHeaderBytes < 3200+400 ) + { + return NULL; + } + if( EQUALN((const char*)poOpenInfo->pabyHeader, "%PDF", 4)) + { + return NULL; + } + +// -------------------------------------------------------------------- +// Try to decode the header encoded as EBCDIC and then ASCII +// -------------------------------------------------------------------- + + int i, j, k; + const GByte* pabyTextHeader = poOpenInfo->pabyHeader; + GByte* pabyASCIITextHeader = (GByte*) CPLMalloc(3200 + 40 + 1); + for( k=0; k<2; k++) + { + for( i=0, j=0;i<3200;i++) + { + GByte chASCII = (k == 0) ? EBCDICToASCII[pabyTextHeader[i]] : + pabyTextHeader[i]; + if (chASCII < 32 && chASCII != '\t' && + chASCII != '\n' && chASCII != '\r') + { + break; + } + pabyASCIITextHeader[j++] = chASCII; + if (chASCII != '\n' && ((i + 1) % 80) == 0) + pabyASCIITextHeader[j++] = '\n'; + } + pabyASCIITextHeader[j] = '\0'; + + if (i == 3200) + break; + if (k == 1) + { + CPLFree(pabyASCIITextHeader); + return FALSE; + } + } + + CPLDebug("SIGY", "Header = \n%s", pabyASCIITextHeader); + CPLFree(pabyASCIITextHeader); + pabyASCIITextHeader = NULL; + +// -------------------------------------------------------------------- +// Read the next 400 bytes, where the Binary File Header is +// located +// -------------------------------------------------------------------- + + const GByte* abyFileHeader = poOpenInfo->pabyHeader + 3200; + +// -------------------------------------------------------------------- +// First check that this binary header is not EBCDIC nor ASCII +// -------------------------------------------------------------------- + for( k=0;k<2;k++ ) + { + for( i=0;i<400;i++) + { + GByte chASCII = (k == 0) ? abyFileHeader[i] : + EBCDICToASCII[abyFileHeader[i]]; + /* A translated 0 value, when source value is not 0, means an invalid */ + /* EBCDIC value. Bail out also for control characters */ + if (chASCII < 32 && chASCII != '\t' && + chASCII != '\n' && chASCII != '\r') + { + break; + } + } + if (i == 400) + { + CPLFree(pabyASCIITextHeader); + return NULL; + } + } + + OGRSEGYDataSource *poDS = new OGRSEGYDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, (const char*)pabyASCIITextHeader ) ) + { + CPLFree(pabyASCIITextHeader); + delete poDS; + poDS = NULL; + } + + CPLFree(pabyASCIITextHeader); + return poDS; +} + +/************************************************************************/ +/* RegisterOGRSEGY() */ +/************************************************************************/ + +void RegisterOGRSEGY() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "SEGY" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SEGY" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "SEG-Y" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_segy.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRSEGYDriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/ogrsegylayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/ogrsegylayer.cpp new file mode 100644 index 000000000..0182446c4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/ogrsegylayer.cpp @@ -0,0 +1,926 @@ +/****************************************************************************** + * $Id: ogrsegylayer.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: SEG-Y Translator + * Purpose: Implements OGRSEGYLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMSEGYS OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_segy.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: ogrsegylayer.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +#define DT_IBM_4BYTES_FP 1 +#define DT_4BYTES_INT 2 +#define DT_2BYTES_INT 3 +#define DT_4BYTES_FP_WITH_GAIN 4 +#define DT_IEEE_4BYTES_FP 5 +#define DT_1BYTE_INT 8 + +typedef struct +{ + const char* pszName; + OGRFieldType eType; +} FieldDesc; + +static const FieldDesc SEGYFields[] = +{ + { "TRACE_NUMBER_WITHIN_LINE", OFTInteger }, + { "TRACE_NUMBER_WITHIN_FILE", OFTInteger }, + { "ORIGINAL_FIELD_RECORD_NUMBER", OFTInteger }, + { "TRACE_NUMBER_WITHIN_ORIGINAL_FIELD_RECORD", OFTInteger }, + { "TRACE_IDENTIFICATION_CODE", OFTInteger }, + { "ENSEMBLE_NUMBER", OFTInteger }, + { "TRACE_NUMBER_WITHIN_ENSEMBLE", OFTInteger }, + { "NUMBER_VERTICAL_SUMMED_TRACES", OFTInteger }, + { "NUMBER_HORIZONTAL_STACKED_TRACES", OFTInteger }, + { "DATA_USE", OFTInteger }, + { "DISTANCE_SOURCE_GROUP", OFTInteger }, + { "RECEIVER_GROUP_ELEVATION", OFTInteger }, + { "SURFACE_ELEVATION_AT_SOURCE", OFTInteger }, + { "SOURCE_DEPTH_BELOW_SURFACE", OFTInteger }, + { "DATUM_ELEVATION_AT_RECEIVER_GROUP", OFTInteger }, + { "DATUM_ELEVATION_AT_SOURCE", OFTInteger }, + { "WATER_DEPTH_AT_SOURCE", OFTInteger }, + { "WATER_DEPTH_AT_GROUP", OFTInteger }, + { "VERTICAL_SCALAR", OFTInteger }, + { "HORIZONTAL_SCALAR", OFTInteger }, + { "SOURCE_X", OFTInteger }, + { "SOURCE_Y", OFTInteger }, + { "GROUP_X", OFTInteger }, + { "GROUP_Y", OFTInteger }, + { "COORDINATE_UNITS", OFTInteger }, + { "WEATHERING_VELOCITY", OFTInteger }, + { "SUB_WEATHERING_VELOCITY", OFTInteger }, + { "UPHOLE_TIME_AT_SOURCE", OFTInteger }, + { "UPHOLE_TIME_AT_GROUP", OFTInteger }, + { "SOURCE_STATIC_CORRECTION", OFTInteger }, + { "GROUP_STATIC_CORRECTION", OFTInteger }, + { "TOTAL_STATIC_CORRECTION", OFTInteger }, + { "LAG_TIME_A", OFTInteger }, + { "LAG_TIME_B", OFTInteger }, + { "DELAY_RECORDING_TIME", OFTInteger }, + { "MUTE_TIME_START", OFTInteger }, + { "MUTE_TIME_END", OFTInteger }, + { "SAMPLES", OFTInteger }, + { "SAMPLE_INTERVAL", OFTInteger }, + { "GAIN_TYPE", OFTInteger }, + { "INSTRUMENT_GAIN_CONSTANT", OFTInteger }, + { "INSTRUMENT_INITIAL_GAIN", OFTInteger }, + { "CORRELATED", OFTInteger }, + { "SWEEP_FREQUENCY_AT_START", OFTInteger }, + { "SWEEP_FREQUENCY_AT_END", OFTInteger }, + { "SWEEP_LENGTH", OFTInteger }, + { "SWEEP_TYPE", OFTInteger }, + { "SWEEP_TRACE_TAPER_LENGTH_AT_START", OFTInteger }, + { "SWEEP_TRACE_TAPER_LENGTH_AT_END", OFTInteger }, + { "TAPER_TYPE", OFTInteger }, + { "ALIAS_FILTER_FREQUENCY", OFTInteger }, + { "ALIAS_FILTER_SLOPE", OFTInteger }, + { "NOTCH_FILTER_FREQUENCY", OFTInteger }, + { "NOTCH_FILTER_SLOPE", OFTInteger }, + { "LOW_CUT_FREQUENCY", OFTInteger }, + { "HIGH_CUT_FREQUENCY", OFTInteger }, + { "LOW_CUT_SLOPE", OFTInteger }, + { "HIGH_CUT_SLOPE", OFTInteger }, + { "YEAR", OFTInteger }, + { "DAY_OF_YEAR", OFTInteger }, + { "HOUR", OFTInteger }, + { "MINUTE", OFTInteger }, + { "SECOND", OFTInteger }, + { "TIME_BASIC_CODE", OFTInteger }, + { "TRACE_WEIGHTING_FACTOR", OFTInteger }, + { "GEOPHONE_GROUP_NUMBER_OF_ROLL_SWITH", OFTInteger }, + { "GEOPHONE_GROUP_NUMBER_OF_TRACE_NUMBER_ONE", OFTInteger }, + { "GEOPHONE_GROUP_NUMBER_OF_LAST_TRACE", OFTInteger }, + { "GAP_SIZE", OFTInteger }, + { "OVER_TRAVEL", OFTInteger }, +}; + +/* SEGY >= 1.0 */ +static const FieldDesc SEGYFields10[] = +{ + { "INLINE_NUMBER", OFTInteger }, + { "CROSSLINE_NUMBER", OFTInteger }, + { "SHOTPOINT_NUMBER", OFTInteger }, + { "SHOTPOINT_SCALAR", OFTInteger }, +}; + +#define TRACE_NUMBER_WITHIN_LINE 0 +#define TRACE_NUMBER_WITHIN_FILE 1 +#define ORIGINAL_FIELD_RECORD_NUMBER 2 +#define TRACE_NUMBER_WITHIN_ORIGINAL_FIELD_RECORD 3 +#define TRACE_IDENTIFICATION_CODE 4 +#define ENSEMBLE_NUMBER 5 +#define TRACE_NUMBER_WITHIN_ENSEMBLE 6 +#define NUMBER_VERTICAL_SUMMED_TRACES 7 +#define NUMBER_HORIZONTAL_STACKED_TRACES 8 +#define DATA_USE 9 +#define DISTANCE_SOURCE_GROUP 10 +#define RECEIVER_GROUP_ELEVATION 11 +#define SURFACE_ELEVATION_AT_SOURCE 12 +#define SOURCE_DEPTH_BELOW_SURFACE 13 +#define DATUM_ELEVATION_AT_RECEIVER_GROUP 14 +#define DATUM_ELEVATION_AT_SOURCE 15 +#define WATER_DEPTH_AT_SOURCE 16 +#define WATER_DEPTH_AT_GROUP 17 +#define VERTICAL_SCALAR 18 +#define HORIZONTAL_SCALAR 19 +#define SOURCE_X 20 +#define SOURCE_Y 21 +#define GROUP_X 22 +#define GROUP_Y 23 +#define COORDINATE_UNITS 24 +#define WEATHERING_VELOCITY 25 +#define SUB_WEATHERING_VELOCITY 26 +#define UPHOLE_TIME_AT_SOURCE 27 +#define UPHOLE_TIME_AT_GROUP 28 +#define SOURCE_STATIC_CORRECTION 29 +#define GROUP_STATIC_CORRECTION 30 +#define TOTAL_STATIC_CORRECTION 31 +#define LAG_TIME_A 32 +#define LAG_TIME_B 33 +#define DELAY_RECORDING_TIME 34 +#define MUTE_TIME_START 35 +#define MUTE_TIME_END 36 +#define SAMPLES 37 +#define SAMPLE_INTERVAL 38 +#define GAIN_TYPE 39 +#define INSTRUMENT_GAIN_CONSTANT 40 +#define INSTRUMENT_INITIAL_GAIN 41 +#define CORRELATED 42 +#define SWEEP_FREQUENCY_AT_START 43 +#define SWEEP_FREQUENCY_AT_END 44 +#define SWEEP_LENGTH 45 +#define SWEEP_TYPE 46 +#define SWEEP_TRACE_TAPER_LENGTH_AT_START 47 +#define SWEEP_TRACE_TAPER_LENGTH_AT_END 48 +#define TAPER_TYPE 49 +#define ALIAS_FILTER_FREQUENCY 50 +#define ALIAS_FILTER_SLOPE 51 +#define NOTCH_FILTER_FREQUENCY 52 +#define NOTCH_FILTER_SLOPE 53 +#define LOW_CUT_FREQUENCY 54 +#define HIGH_CUT_FREQUENCY 55 +#define LOW_CUT_SLOPE 56 +#define HIGH_CUT_SLOPE 57 +#define YEAR 58 +#define DAY_OF_YEAR 59 +#define HOUR 60 +#define MINUTE 61 +#define SECOND 62 +#define TIME_BASIC_CODE 63 +#define TRACE_WEIGHTING_FACTOR 64 +#define GEOPHONE_GROUP_NUMBER_OF_ROLL_SWITH 65 +#define GEOPHONE_GROUP_NUMBER_OF_TRACE_NUMBER_ONE 66 +#define GEOPHONE_GROUP_NUMBER_OF_LAST_TRACE 67 +#define GAP_SIZE 68 +#define OVER_TRAVEL 69 +#define INLINE_NUMBER 70 +#define CROSSLINE_NUMBER 71 +#define SHOTPOINT_NUMBER 72 +#define SHOTPOINT_SCALAR 73 + +#if 0 +/************************************************************************/ +/* SEGYReadMSBFloat32() */ +/************************************************************************/ + +static float SEGYReadMSBFloat32(const GByte* pabyVal) +{ + float fVal; + memcpy(&fVal, pabyVal, 4); + CPL_MSBPTR32(&fVal); + return fVal; +} +#endif + +/************************************************************************/ +/* OGRSEGYLayer() */ +/************************************************************************/ + + +OGRSEGYLayer::OGRSEGYLayer( const char* pszFilename, + VSILFILE* fp, + SEGYBinaryFileHeader* psBFH ) + +{ + this->fp = fp; + nNextFID = 0; + bEOF = FALSE; + memcpy(&sBFH, psBFH, sizeof(sBFH)); + + nDataSize = 0; + switch (sBFH.nDataSampleType) + { + case DT_IBM_4BYTES_FP: nDataSize = 4; break; + case DT_4BYTES_INT: nDataSize = 4; break; + case DT_2BYTES_INT: nDataSize = 2; break; + case DT_4BYTES_FP_WITH_GAIN: nDataSize = 4; break; + case DT_IEEE_4BYTES_FP: nDataSize = 4; break; + case DT_1BYTE_INT: nDataSize = 1; break; + default: break; + } + + poFeatureDefn = new OGRFeatureDefn( CPLGetBasename(pszFilename) ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint ); + + int i; + for(i=0;i<(int)(sizeof(SEGYFields)/sizeof(SEGYFields[0]));i++) + { + OGRFieldDefn oField( SEGYFields[i].pszName, + SEGYFields[i].eType ); + poFeatureDefn->AddFieldDefn( &oField ); + } + + if (sBFH.dfSEGYRevisionNumber >= 1.0) + { + for(i=0;i<(int)(sizeof(SEGYFields10)/sizeof(SEGYFields10[0]));i++) + { + OGRFieldDefn oField( SEGYFields10[i].pszName, + SEGYFields10[i].eType ); + poFeatureDefn->AddFieldDefn( &oField ); + } + } + + OGRFieldDefn oField( "SAMPLE_ARRAY", OFTRealList ); + poFeatureDefn->AddFieldDefn(&oField); + + ResetReading(); +} + +/************************************************************************/ +/* ~OGRSEGYLayer() */ +/************************************************************************/ + +OGRSEGYLayer::~OGRSEGYLayer() + +{ + poFeatureDefn->Release(); + + VSIFCloseL( fp ); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRSEGYLayer::ResetReading() + +{ + nNextFID = 0; + bEOF = FALSE; + + VSIFSeekL( fp, 3200 + 400 + 3200 * sBFH.nNumberOfExtendedTextualFileHeader, + SEEK_SET ); +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRSEGYLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* GetIBMFloat() */ +/************************************************************************/ + +static float GetIBMFloat(const GByte* pabyData) +{ + int nVal; + memcpy(&nVal, pabyData, 4); + CPL_MSBPTR32(&nVal); + int nSign = 1 - 2 * ((nVal >> 31) & 0x01); + int nExp = (nVal >> 24) & 0x7f; + int nMant = nVal & 0xffffff; + + if (nExp == 0x7f) + { + nVal = (nVal & 0x80000000) | (0xff << 23) | (nMant >> 1); + float fVal; + memcpy(&fVal, &nVal, 4); + return fVal; + } + + return (float)((double)nSign * nMant * pow(2.0, 4 * (nExp - 64) - 24)); +} +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRSEGYLayer::GetNextRawFeature() +{ + if (bEOF) + return NULL; + + GByte abyTraceHeader[240]; + + if ((int)VSIFReadL(abyTraceHeader, 1, 240, fp) != 240) + { + bEOF = TRUE; + return NULL; + } + + int nTraceNumberWithinLine = SEGYReadMSBInt32(abyTraceHeader + 0); + int nTraceNumberWithinFile = SEGYReadMSBInt32(abyTraceHeader + 4); + int nOriginalFieldRecordNumber = SEGYReadMSBInt32(abyTraceHeader + 8); + int nTraceNumberWithinOriginalFieldRecord = SEGYReadMSBInt32(abyTraceHeader + 12); + int nEnsembleNumber = SEGYReadMSBInt32(abyTraceHeader + 20); + int nTraceNumberWithinEnsemble = SEGYReadMSBInt32(abyTraceHeader + 24); + int nTraceIdentificationCode = SEGYReadMSBInt16(abyTraceHeader + 28); + int nNumberVerticalSummedTraces = SEGYReadMSBInt16(abyTraceHeader + 30); + int nNumberHorizontalStackedTraces = SEGYReadMSBInt16(abyTraceHeader + 32); + int nDataUse = SEGYReadMSBInt16(abyTraceHeader + 34); + int nDistanceSourceGroup = SEGYReadMSBInt32(abyTraceHeader + 36); + int nReceiverGroupElevation = SEGYReadMSBInt32(abyTraceHeader + 40); + int nSurfaceElevationAtSource = SEGYReadMSBInt32(abyTraceHeader + 44); + int nSourceDepthBelowSurface = SEGYReadMSBInt32(abyTraceHeader + 48); + int nDatumElevationAtReceiverGroup = SEGYReadMSBInt32(abyTraceHeader + 52); + int nDatumElevationAtSource = SEGYReadMSBInt32(abyTraceHeader + 56); + int nWaterDepthAtSource = SEGYReadMSBInt32(abyTraceHeader + 60); + int nWaterDepthAtGroup = SEGYReadMSBInt32(abyTraceHeader + 64); + int nVerticalScalar = SEGYReadMSBInt16(abyTraceHeader + 68); + int nHorizontalScalar = SEGYReadMSBInt16(abyTraceHeader + 70); + int nSourceX = SEGYReadMSBInt32(abyTraceHeader + 72); + int nSourceY = SEGYReadMSBInt32(abyTraceHeader + 76); + int nGroupX = SEGYReadMSBInt32(abyTraceHeader + 80); + int nGroupY = SEGYReadMSBInt32(abyTraceHeader + 84); + int nCoordinateUnits = SEGYReadMSBInt16(abyTraceHeader + 88); + int nWeatheringVelocity = SEGYReadMSBInt16(abyTraceHeader + 90); + int nSubWeatheringVelocity = SEGYReadMSBInt16(abyTraceHeader + 92); + + int nUpholeTimeAtSource = SEGYReadMSBInt16(abyTraceHeader + 94); + int nUpholeTimeAtGroup = SEGYReadMSBInt16(abyTraceHeader + 96); + int nSourceStaticCorrection = SEGYReadMSBInt16(abyTraceHeader + 98); + int nGroupStaticCorrection = SEGYReadMSBInt16(abyTraceHeader + 100); + int nTotalStaticCorrection = SEGYReadMSBInt16(abyTraceHeader + 102); + int nLagTimeA = SEGYReadMSBInt16(abyTraceHeader + 104); + int nLagTimeB = SEGYReadMSBInt16(abyTraceHeader + 106); + int nDelayRecordingTime = SEGYReadMSBInt16(abyTraceHeader + 108); + int nMuteTimeStart = SEGYReadMSBInt16(abyTraceHeader + 110); + int nMuteTimeEnd = SEGYReadMSBInt16(abyTraceHeader + 112); + + int nSamples = SEGYReadMSBInt16(abyTraceHeader + 114); + if (nSamples == 0) /* Happens with ftp://software.seg.org/pub/datasets/2D/Hess_VTI/timodel_c11.segy.gz */ + nSamples = sBFH.nSamplesPerDataTrace; + + if (nSamples < 0) + { + bEOF = TRUE; + return NULL; + } + int nSampleInterval = SEGYReadMSBInt16(abyTraceHeader + 116); + + int nGainType = SEGYReadMSBInt16(abyTraceHeader + 118); + int nInstrumentGainConstant = SEGYReadMSBInt16(abyTraceHeader + 120); + int nInstrumentInitialGain = SEGYReadMSBInt16(abyTraceHeader + 122); + int nCorrelated = SEGYReadMSBInt16(abyTraceHeader + 124); + int nSweepFrequencyAtStart = SEGYReadMSBInt16(abyTraceHeader + 126); + int nSweepFrequencyAtEnd = SEGYReadMSBInt16(abyTraceHeader + 128); + int nSweepLength = SEGYReadMSBInt16(abyTraceHeader + 130); + int nSweepType = SEGYReadMSBInt16(abyTraceHeader + 132); + int nSweepTraceTaperLengthAtStart = SEGYReadMSBInt16(abyTraceHeader + 134); + int nSweepTraceTaperLengthAtEnd = SEGYReadMSBInt16(abyTraceHeader + 136); + int nTaperType = SEGYReadMSBInt16(abyTraceHeader + 138); + int nAliasFilterFrequency = SEGYReadMSBInt16(abyTraceHeader + 140); + int nAliasFilterSlope = SEGYReadMSBInt16(abyTraceHeader + 142); + int nNotchFilterFrequency = SEGYReadMSBInt16(abyTraceHeader + 144); + int nNotchFilterSlope = SEGYReadMSBInt16(abyTraceHeader + 146); + int nLowCutFrequency = SEGYReadMSBInt16(abyTraceHeader + 148); + int nHighCutFrequency = SEGYReadMSBInt16(abyTraceHeader + 150); + int nLowCutSlope = SEGYReadMSBInt16(abyTraceHeader + 152); + int nHighCutSlope = SEGYReadMSBInt16(abyTraceHeader + 154); + + int nYear = SEGYReadMSBInt16(abyTraceHeader + 156); + int nDayOfYear = SEGYReadMSBInt16(abyTraceHeader + 158); + int nHour = SEGYReadMSBInt16(abyTraceHeader + 160); + int nMinute = SEGYReadMSBInt16(abyTraceHeader + 162); + int nSecond = SEGYReadMSBInt16(abyTraceHeader + 164); + int nTimeBasicCode = SEGYReadMSBInt16(abyTraceHeader + 166); + + int nTraceWeightingFactor = SEGYReadMSBInt16(abyTraceHeader + 168); + int nGeophoneGroupNumberOfRollSwith = SEGYReadMSBInt16(abyTraceHeader + 170); + int nGeophoneGroupNumberOfTraceNumberOne = SEGYReadMSBInt16(abyTraceHeader + 172); + int nGeophoneGroupNumberOfLastTrace = SEGYReadMSBInt16(abyTraceHeader + 174); + int nGapSize = SEGYReadMSBInt16(abyTraceHeader + 176); + int nOverTravel = SEGYReadMSBInt16(abyTraceHeader + 178); + + int nInlineNumber = SEGYReadMSBInt32(abyTraceHeader + 188); + int nCrosslineNumber = SEGYReadMSBInt32(abyTraceHeader + 192); + int nShotpointNumber = SEGYReadMSBInt32(abyTraceHeader + 196); + int nShotpointScalar = SEGYReadMSBInt16(abyTraceHeader + 200); + +#if 0 + /* Extensions of http://sioseis.ucsd.edu/segy.header.html */ + float fDeepWaterDelay = SEGYReadMSBFloat32(abyTraceHeader + 180); + float fStartMuteTime = SEGYReadMSBFloat32(abyTraceHeader + 184); + float fEndMuteTime = SEGYReadMSBFloat32(abyTraceHeader + 188); + float fSampleInterval = SEGYReadMSBFloat32(abyTraceHeader + 192); + float fWaterBottomTime = SEGYReadMSBFloat32(abyTraceHeader + 196); + int nEndOfRp = SEGYReadMSBInt16(abyTraceHeader + 200); + CPLDebug("SIGY", "fDeepWaterDelay = %f", fDeepWaterDelay); + CPLDebug("SIGY", "fStartMuteTime = %f", fStartMuteTime); + CPLDebug("SIGY", "fEndMuteTime = %f", fEndMuteTime); + CPLDebug("SIGY", "fSampleInterval = %f", fSampleInterval); + CPLDebug("SIGY", "fWaterBottomTime = %f", fWaterBottomTime); + CPLDebug("SIGY", "nEndOfRp = %d", nEndOfRp); +#endif + + double dfHorizontalScale = (nHorizontalScalar > 0) ? nHorizontalScalar : + (nHorizontalScalar < 0) ? 1.0 / -nHorizontalScalar : 1.0; + if (nCoordinateUnits == 2) + dfHorizontalScale /= 3600; + + double dfGroupX = nGroupX * dfHorizontalScale; + double dfGroupY = nGroupY * dfHorizontalScale; + +#if 0 + double dfSourceX = nSourceX * dfHorizontalScale; + double dfSourceY = nSourceY * dfHorizontalScale; +#endif + +#if 0 + CPLDebug("SIGY", "nTraceNumberWithinLine = %d", nTraceNumberWithinLine); + CPLDebug("SIGY", "nTraceNumberWithinFile = %d", nTraceNumberWithinFile); + CPLDebug("SIGY", "nOriginalFieldRecordNumber = %d", nOriginalFieldRecordNumber); + CPLDebug("SIGY", "nTraceNumberWithinOriginalFieldRecord = %d", nTraceNumberWithinOriginalFieldRecord); + CPLDebug("SIGY", "nTraceIdentificationCode = %d", nTraceIdentificationCode); + CPLDebug("SIGY", "nEnsembleNumber = %d", nEnsembleNumber); + CPLDebug("SIGY", "nTraceNumberWithinEnsemble = %d", nTraceNumberWithinEnsemble); + CPLDebug("SIGY", "nNumberVerticalSummedTraces = %d", nNumberVerticalSummedTraces); + CPLDebug("SIGY", "nNumberHorizontalStackedTraces = %d", nNumberHorizontalStackedTraces); + CPLDebug("SIGY", "nDataUse = %d", nDataUse); + CPLDebug("SIGY", "nDistanceSourceGroup = %d", nDistanceSourceGroup); + CPLDebug("SIGY", "nReceiverGroupElevation = %d", nReceiverGroupElevation); + CPLDebug("SIGY", "nSurfaceElevationAtSource = %d", nSurfaceElevationAtSource); + CPLDebug("SIGY", "nSourceDepthBelowSurface = %d", nSourceDepthBelowSurface); + CPLDebug("SIGY", "nDatumElevationAtReceiverGroup = %d", nDatumElevationAtReceiverGroup); + CPLDebug("SIGY", "nDatumElevationAtSource = %d", nDatumElevationAtSource); + CPLDebug("SIGY", "nWaterDepthAtSource = %d", nWaterDepthAtSource); + CPLDebug("SIGY", "nWaterDepthAtGroup = %d", nWaterDepthAtGroup); + CPLDebug("SIGY", "nVerticalScalar = %d", nVerticalScalar); + CPLDebug("SIGY", "nHorizontalScalar = %d", nHorizontalScalar); + CPLDebug("SIGY", "nSourceX = %d", nSourceX); + CPLDebug("SIGY", "nSourceY = %d", nSourceY); + CPLDebug("SIGY", "dfSourceX = %f", dfSourceX); + CPLDebug("SIGY", "dfSourceY = %f", dfSourceY); + CPLDebug("SIGY", "nGroupX = %d", nGroupX); + CPLDebug("SIGY", "nGroupY = %d", nGroupY); + CPLDebug("SIGY", "dfGroupX = %f", dfGroupX); + CPLDebug("SIGY", "dfGroupY = %f", dfGroupY); + CPLDebug("SIGY", "nCoordinateUnits = %d", nCoordinateUnits); + + CPLDebug("SIGY", "nWeatheringVelocity = %d", nWeatheringVelocity); + CPLDebug("SIGY", "nSubWeatheringVelocity = %d", nSubWeatheringVelocity); + CPLDebug("SIGY", "nUpholeTimeAtSource = %d", nUpholeTimeAtSource); + CPLDebug("SIGY", "nUpholeTimeAtGroup = %d", nUpholeTimeAtGroup); + CPLDebug("SIGY", "nSourceStaticCorrection = %d", nSourceStaticCorrection); + CPLDebug("SIGY", "nGroupStaticCorrection = %d", nGroupStaticCorrection); + CPLDebug("SIGY", "nTotalStaticCorrection = %d", nTotalStaticCorrection); + CPLDebug("SIGY", "nLagTimeA = %d", nLagTimeA); + CPLDebug("SIGY", "nLagTimeB = %d", nLagTimeB); + CPLDebug("SIGY", "nDelayRecordingTime = %d", nDelayRecordingTime); + CPLDebug("SIGY", "nMuteTimeStart = %d", nMuteTimeStart); + CPLDebug("SIGY", "nMuteTimeEnd = %d", nMuteTimeEnd); + + CPLDebug("SIGY", "nSamples = %d", nSamples); + CPLDebug("SIGY", "nSampleInterval = %d", nSampleInterval); + + CPLDebug("SIGY", "nGainType = %d", nGainType); + CPLDebug("SIGY", "nInstrumentGainConstant = %d", nInstrumentGainConstant); + CPLDebug("SIGY", "nInstrumentInitialGain = %d", nInstrumentInitialGain); + CPLDebug("SIGY", "nCorrelated = %d", nCorrelated); + CPLDebug("SIGY", "nSweepFrequencyAtStart = %d", nSweepFrequencyAtStart); + CPLDebug("SIGY", "nSweepFrequencyAtEnd = %d", nSweepFrequencyAtEnd); + CPLDebug("SIGY", "nSweepLength = %d", nSweepLength); + CPLDebug("SIGY", "nSweepType = %d", nSweepType); + CPLDebug("SIGY", "nSweepTraceTaperLengthAtStart = %d", nSweepTraceTaperLengthAtStart); + CPLDebug("SIGY", "nSweepTraceTaperLengthAtEnd = %d", nSweepTraceTaperLengthAtEnd); + CPLDebug("SIGY", "nTaperType = %d", nTaperType); + CPLDebug("SIGY", "nAliasFilterFrequency = %d", nAliasFilterFrequency); + CPLDebug("SIGY", "nAliasFilterSlope = %d", nAliasFilterSlope); + CPLDebug("SIGY", "nNotchFilterFrequency = %d", nNotchFilterFrequency); + CPLDebug("SIGY", "nNotchFilterSlope = %d", nNotchFilterSlope); + CPLDebug("SIGY", "nLowCutFrequency = %d", nLowCutFrequency); + CPLDebug("SIGY", "nHighCutFrequency = %d", nHighCutFrequency); + CPLDebug("SIGY", "nLowCutSlope = %d", nLowCutSlope); + CPLDebug("SIGY", "nHighCutSlope = %d", nHighCutSlope); + CPLDebug("SIGY", "nYear = %d", nYear); + CPLDebug("SIGY", "nDayOfYear = %d", nDayOfYear); + CPLDebug("SIGY", "nHour = %d", nHour); + CPLDebug("SIGY", "nMinute = %d", nMinute); + CPLDebug("SIGY", "nSecond = %d", nSecond); + CPLDebug("SIGY", "nTimeBasicCode = %d", nTimeBasicCode); + CPLDebug("SIGY", "nTraceWeightingFactor = %d", nTraceWeightingFactor); + CPLDebug("SIGY", "nGeophoneGroupNumberOfRollSwith = %d", nGeophoneGroupNumberOfRollSwith); + CPLDebug("SIGY", "nGeophoneGroupNumberOfTraceNumberOne = %d", nGeophoneGroupNumberOfTraceNumberOne); + CPLDebug("SIGY", "nGeophoneGroupNumberOfLastTrace = %d", nGeophoneGroupNumberOfLastTrace); + CPLDebug("SIGY", "nGapSize = %d", nGapSize); + CPLDebug("SIGY", "nOverTravel = %d", nOverTravel); + + if (sBFH.dfSEGYRevisionNumber >= 1.0) + { + CPLDebug("SIGY", "nInlineNumber = %d", nInlineNumber); + CPLDebug("SIGY", "nCrosslineNumber = %d", nCrosslineNumber); + CPLDebug("SIGY", "nShotpointNumber = %d", nShotpointNumber); + CPLDebug("SIGY", "nShotpointScalar = %d", nShotpointScalar); + } +#endif + + GByte* pabyData = (GByte*) VSIMalloc( nDataSize * nSamples ); + double* padfValues = (double*) VSICalloc( nSamples, sizeof(double) ); + if (pabyData == NULL || padfValues == NULL) + { + VSIFSeekL( fp, nDataSize * nSamples, SEEK_CUR ); + } + else + { + if ((int)VSIFReadL(pabyData, nDataSize, nSamples, fp) != nSamples) + { + bEOF = TRUE; + } + for(int i=0;iSetFID(nNextFID ++); + if (dfGroupX != 0.0 || dfGroupY != 0.0) + poFeature->SetGeometryDirectly(new OGRPoint(dfGroupX, dfGroupY)); + + poFeature->SetField(TRACE_NUMBER_WITHIN_LINE, nTraceNumberWithinLine); + poFeature->SetField(TRACE_NUMBER_WITHIN_FILE, nTraceNumberWithinFile); + poFeature->SetField(ORIGINAL_FIELD_RECORD_NUMBER, nOriginalFieldRecordNumber); + poFeature->SetField(TRACE_NUMBER_WITHIN_ORIGINAL_FIELD_RECORD, nTraceNumberWithinOriginalFieldRecord); + poFeature->SetField(TRACE_IDENTIFICATION_CODE, nTraceIdentificationCode); + poFeature->SetField(ENSEMBLE_NUMBER, nEnsembleNumber); + poFeature->SetField(TRACE_NUMBER_WITHIN_ENSEMBLE, nTraceNumberWithinEnsemble); + poFeature->SetField(NUMBER_VERTICAL_SUMMED_TRACES, nNumberVerticalSummedTraces); + poFeature->SetField(NUMBER_HORIZONTAL_STACKED_TRACES, nNumberHorizontalStackedTraces); + poFeature->SetField(DATA_USE, nDataUse); + poFeature->SetField(DISTANCE_SOURCE_GROUP, nDistanceSourceGroup); + poFeature->SetField(RECEIVER_GROUP_ELEVATION, nReceiverGroupElevation); + poFeature->SetField(SURFACE_ELEVATION_AT_SOURCE, nSurfaceElevationAtSource); + poFeature->SetField(SOURCE_DEPTH_BELOW_SURFACE, nSourceDepthBelowSurface); + poFeature->SetField(DATUM_ELEVATION_AT_RECEIVER_GROUP, nDatumElevationAtReceiverGroup); + poFeature->SetField(DATUM_ELEVATION_AT_SOURCE, nDatumElevationAtSource); + poFeature->SetField(WATER_DEPTH_AT_SOURCE, nWaterDepthAtSource); + poFeature->SetField(WATER_DEPTH_AT_GROUP, nWaterDepthAtGroup); + poFeature->SetField(VERTICAL_SCALAR, nVerticalScalar); + poFeature->SetField(HORIZONTAL_SCALAR, nHorizontalScalar); + poFeature->SetField(SOURCE_X, nSourceX); + poFeature->SetField(SOURCE_Y, nSourceY); + poFeature->SetField(GROUP_X, nGroupX); + poFeature->SetField(GROUP_Y, nGroupY); + poFeature->SetField(COORDINATE_UNITS, nCoordinateUnits); + poFeature->SetField(WEATHERING_VELOCITY, nWeatheringVelocity); + poFeature->SetField(SUB_WEATHERING_VELOCITY, nSubWeatheringVelocity); + poFeature->SetField(UPHOLE_TIME_AT_SOURCE, nUpholeTimeAtSource); + poFeature->SetField(UPHOLE_TIME_AT_GROUP, nUpholeTimeAtGroup); + poFeature->SetField(SOURCE_STATIC_CORRECTION, nSourceStaticCorrection); + poFeature->SetField(GROUP_STATIC_CORRECTION, nGroupStaticCorrection); + poFeature->SetField(TOTAL_STATIC_CORRECTION, nTotalStaticCorrection); + poFeature->SetField(LAG_TIME_A, nLagTimeA); + poFeature->SetField(LAG_TIME_B, nLagTimeB); + poFeature->SetField(DELAY_RECORDING_TIME, nDelayRecordingTime); + poFeature->SetField(MUTE_TIME_START, nMuteTimeStart); + poFeature->SetField(MUTE_TIME_END, nMuteTimeEnd); + poFeature->SetField(SAMPLES, nSamples); + poFeature->SetField(SAMPLE_INTERVAL, nSampleInterval); + poFeature->SetField(GAIN_TYPE, nGainType); + poFeature->SetField(INSTRUMENT_GAIN_CONSTANT, nInstrumentGainConstant); + poFeature->SetField(INSTRUMENT_INITIAL_GAIN, nInstrumentInitialGain); + poFeature->SetField(CORRELATED, nCorrelated); + poFeature->SetField(SWEEP_FREQUENCY_AT_START, nSweepFrequencyAtStart); + poFeature->SetField(SWEEP_FREQUENCY_AT_END, nSweepFrequencyAtEnd); + poFeature->SetField(SWEEP_LENGTH, nSweepLength); + poFeature->SetField(SWEEP_TYPE, nSweepType); + poFeature->SetField(SWEEP_TRACE_TAPER_LENGTH_AT_START, nSweepTraceTaperLengthAtStart); + poFeature->SetField(SWEEP_TRACE_TAPER_LENGTH_AT_END, nSweepTraceTaperLengthAtEnd); + poFeature->SetField(TAPER_TYPE, nTaperType); + poFeature->SetField(ALIAS_FILTER_FREQUENCY, nAliasFilterFrequency); + poFeature->SetField(ALIAS_FILTER_SLOPE, nAliasFilterSlope); + poFeature->SetField(NOTCH_FILTER_FREQUENCY, nNotchFilterFrequency); + poFeature->SetField(NOTCH_FILTER_SLOPE, nNotchFilterSlope); + poFeature->SetField(LOW_CUT_FREQUENCY, nLowCutFrequency); + poFeature->SetField(HIGH_CUT_FREQUENCY, nHighCutFrequency); + poFeature->SetField(LOW_CUT_SLOPE, nLowCutSlope); + poFeature->SetField(HIGH_CUT_SLOPE, nHighCutSlope); + poFeature->SetField(YEAR, nYear); + poFeature->SetField(DAY_OF_YEAR, nDayOfYear); + poFeature->SetField(HOUR, nHour); + poFeature->SetField(MINUTE, nMinute); + poFeature->SetField(SECOND, nSecond); + poFeature->SetField(TIME_BASIC_CODE, nTimeBasicCode); + poFeature->SetField(TRACE_WEIGHTING_FACTOR, nTraceWeightingFactor); + poFeature->SetField(GEOPHONE_GROUP_NUMBER_OF_ROLL_SWITH, nGeophoneGroupNumberOfRollSwith); + poFeature->SetField(GEOPHONE_GROUP_NUMBER_OF_TRACE_NUMBER_ONE, nGeophoneGroupNumberOfTraceNumberOne); + poFeature->SetField(GEOPHONE_GROUP_NUMBER_OF_LAST_TRACE, nGeophoneGroupNumberOfLastTrace); + poFeature->SetField(GAP_SIZE, nGapSize); + poFeature->SetField(OVER_TRAVEL, nOverTravel); + + if (sBFH.dfSEGYRevisionNumber >= 1.0) + { + poFeature->SetField(INLINE_NUMBER, nInlineNumber); + poFeature->SetField(CROSSLINE_NUMBER, nCrosslineNumber); + poFeature->SetField(SHOTPOINT_NUMBER, nShotpointNumber); + poFeature->SetField(SHOTPOINT_SCALAR, nShotpointScalar); + } + + if (nSamples > 0 && padfValues != NULL) + poFeature->SetField(poFeature->GetFieldCount() - 1, nSamples, padfValues); + + CPLFree(padfValues); + return poFeature; +} + + + +static const FieldDesc SEGYHeaderFields[] = +{ + { "TEXT_HEADER", OFTString }, + { "JOB_ID_NUMBER", OFTInteger }, + { "LINE_NUMBER", OFTInteger }, + { "REEL_NUMBER", OFTInteger }, + { "DATA_TRACES_PER_ENSEMBLE", OFTInteger }, + { "AUX_TRACES_PER_ENSEMBLE", OFTInteger }, + { "SAMPLE_INTERVAL", OFTInteger }, + { "SAMPLE_INTERVAL_ORIGINAL", OFTInteger }, + { "SAMPLES_PER_DATA_TRACE", OFTInteger }, + { "SAMPLES_PER_DATA_TRACE_ORIGINAL", OFTInteger }, + { "DATA_SAMPLE_TYPE", OFTInteger }, + { "ENSEMBLE_FOLD", OFTInteger }, + { "TRACE_SORTING_CODE", OFTInteger }, + { "VERTICAL_SUM_CODE", OFTInteger }, + { "SWEEP_FREQUENCY_AT_START", OFTInteger }, + { "SWEEP_FREQUENCY_AT_END", OFTInteger }, + { "SWEEP_LENGTH", OFTInteger }, + { "SWEEP_TYPE", OFTInteger }, + { "TRACE_NUMBER_OF_SWEEP_CHANNEL", OFTInteger }, + { "SWEEP_TRACE_TAPER_LENGTH_AT_START", OFTInteger }, + { "SWEEP_TRACE_TAPER_LENGTH_AT_END", OFTInteger }, + { "TAPER_TYPE", OFTInteger }, + { "CORRELATED", OFTInteger }, + { "BINARY_GAIN_RECOVERED", OFTInteger }, + { "AMPLITUDE_RECOVERY_METHOD", OFTInteger }, + { "MEASUREMENT_SYSTEM", OFTInteger }, + { "IMPULSE_SIGNAL_POLARITY", OFTInteger }, + { "VIBRATORY_POLARY_CODE", OFTInteger }, + { "SEGY_REVISION_NUMBER", OFTInteger }, + { "SEGY_FLOAT_REVISION_NUMBER", OFTReal }, + { "FIXED_LENGTH_TRACE_FLAG", OFTInteger }, + { "NUMBER_OF_EXTENDED_TEXTUAL_FILE_HEADER", OFTInteger }, +}; + +#define HEADER_TEXT_HEADER 0 +#define HEADER_JOB_ID_NUMBER 1 +#define HEADER_LINE_NUMBER 2 +#define HEADER_REEL_NUMBER 3 +#define HEADER_DATA_TRACES_PER_ENSEMBLE 4 +#define HEADER_AUX_TRACES_PER_ENSEMBLE 5 +#define HEADER_SAMPLE_INTERVAL 6 +#define HEADER_SAMPLE_INTERVAL_ORIGINAL 7 +#define HEADER_SAMPLES_PER_DATA_TRACE 8 +#define HEADER_SAMPLES_PER_DATA_TRACE_ORIGINAL 9 +#define HEADER_DATA_SAMPLE_TYPE 10 +#define HEADER_ENSEMBLE_FOLD 11 +#define HEADER_TRACE_SORTING_CODE 12 +#define HEADER_VERTICAL_SUM_CODE 13 +#define HEADER_SWEEP_FREQUENCY_AT_START 14 +#define HEADER_SWEEP_FREQUENCY_AT_END 15 +#define HEADER_SWEEP_LENGTH 16 +#define HEADER_SWEEP_TYPE 17 +#define HEADER_TRACE_NUMBER_OF_SWEEP_CHANNEL 18 +#define HEADER_SWEEP_TRACE_TAPER_LENGTH_AT_START 19 +#define HEADER_SWEEP_TRACE_TAPER_LENGTH_AT_END 20 +#define HEADER_TAPER_TYPE 21 +#define HEADER_CORRELATED 22 +#define HEADER_BINARY_GAIN_RECOVERED 23 +#define HEADER_AMPLITUDE_RECOVERY_METHOD 24 +#define HEADER_MEASUREMENT_SYSTEM 25 +#define HEADER_IMPULSE_SIGNAL_POLARITY 26 +#define HEADER_VIBRATORY_POLARY_CODE 27 +#define HEADER_SEGY_REVISION_NUMBER 28 +#define HEADER_FLOAT_SEGY_REVISION_NUMBER 29 +#define HEADER_FIXED_LENGTH_TRACE_FLAG 30 +#define HEADER_NUMBER_OF_EXTENDED_TEXTUAL_FILE_HEADER 31 + + +/************************************************************************/ +/* OGRSEGYHeaderLayer() */ +/************************************************************************/ + + +OGRSEGYHeaderLayer::OGRSEGYHeaderLayer( const char* pszLayerName, + SEGYBinaryFileHeader* psBFH, + const char* pszHeaderTextIn ) + +{ + bEOF = FALSE; + memcpy(&sBFH, psBFH, sizeof(sBFH)); + pszHeaderText = CPLStrdup(pszHeaderTextIn); + + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + int i; + for(i=0;i<(int)(sizeof(SEGYHeaderFields)/sizeof(SEGYHeaderFields[0]));i++) + { + OGRFieldDefn oField( SEGYHeaderFields[i].pszName, + SEGYHeaderFields[i].eType ); + poFeatureDefn->AddFieldDefn( &oField ); + } + + ResetReading(); +} + +/************************************************************************/ +/* ~OGRSEGYLayer() */ +/************************************************************************/ + +OGRSEGYHeaderLayer::~OGRSEGYHeaderLayer() + +{ + poFeatureDefn->Release(); + CPLFree(pszHeaderText); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRSEGYHeaderLayer::ResetReading() + +{ + bEOF = FALSE; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRSEGYHeaderLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRSEGYHeaderLayer::GetNextRawFeature() +{ + if (bEOF) + return NULL; + + bEOF = TRUE; + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetFID(0); + + poFeature->SetField(HEADER_TEXT_HEADER, pszHeaderText); + poFeature->SetField(HEADER_JOB_ID_NUMBER, sBFH.nJobIdNumber); + poFeature->SetField(HEADER_LINE_NUMBER, sBFH.nLineNumber); + poFeature->SetField(HEADER_REEL_NUMBER, sBFH.nReelNumber); + poFeature->SetField(HEADER_DATA_TRACES_PER_ENSEMBLE, sBFH.nDataTracesPerEnsemble); + poFeature->SetField(HEADER_AUX_TRACES_PER_ENSEMBLE, sBFH.nAuxTracesPerEnsemble); + poFeature->SetField(HEADER_SAMPLE_INTERVAL, sBFH.nSampleInterval); + poFeature->SetField(HEADER_SAMPLE_INTERVAL_ORIGINAL, sBFH.nSampleIntervalOriginal); + poFeature->SetField(HEADER_SAMPLES_PER_DATA_TRACE, sBFH.nSamplesPerDataTrace); + poFeature->SetField(HEADER_SAMPLES_PER_DATA_TRACE_ORIGINAL, sBFH.nSamplesPerDataTraceOriginal); + poFeature->SetField(HEADER_DATA_SAMPLE_TYPE, sBFH.nDataSampleType); + poFeature->SetField(HEADER_ENSEMBLE_FOLD, sBFH.nEnsembleFold); + poFeature->SetField(HEADER_TRACE_SORTING_CODE, sBFH.nTraceSortingCode); + poFeature->SetField(HEADER_VERTICAL_SUM_CODE, sBFH.nVerticalSumCode); + poFeature->SetField(HEADER_SWEEP_FREQUENCY_AT_START, sBFH.nSweepFrequencyAtStart); + poFeature->SetField(HEADER_SWEEP_FREQUENCY_AT_END, sBFH.nSweepFrequencyAtEnd); + poFeature->SetField(HEADER_SWEEP_LENGTH, sBFH.nSweepLength); + poFeature->SetField(HEADER_SWEEP_TYPE, sBFH.nSweepType); + poFeature->SetField(HEADER_TRACE_NUMBER_OF_SWEEP_CHANNEL, sBFH.nTraceNumberOfSweepChannel); + poFeature->SetField(HEADER_SWEEP_TRACE_TAPER_LENGTH_AT_START, sBFH.nSweepTraceTaperLengthAtStart); + poFeature->SetField(HEADER_SWEEP_TRACE_TAPER_LENGTH_AT_END, sBFH.nSweepTraceTaperLengthAtEnd); + poFeature->SetField(HEADER_TAPER_TYPE, sBFH.nTaperType); + poFeature->SetField(HEADER_CORRELATED, sBFH.nCorrelated); + poFeature->SetField(HEADER_BINARY_GAIN_RECOVERED, sBFH.nBinaryGainRecovered); + poFeature->SetField(HEADER_AMPLITUDE_RECOVERY_METHOD, sBFH.nAmplitudeRecoveryMethod); + poFeature->SetField(HEADER_MEASUREMENT_SYSTEM, sBFH.nMeasurementSystem); + poFeature->SetField(HEADER_IMPULSE_SIGNAL_POLARITY, sBFH.nImpulseSignalPolarity); + poFeature->SetField(HEADER_VIBRATORY_POLARY_CODE, sBFH.nVibratoryPolaryCode); + poFeature->SetField(HEADER_SEGY_REVISION_NUMBER, sBFH.nSEGYRevisionNumber); + poFeature->SetField(HEADER_FLOAT_SEGY_REVISION_NUMBER, sBFH.dfSEGYRevisionNumber); + poFeature->SetField(HEADER_FIXED_LENGTH_TRACE_FLAG, sBFH.nFixedLengthTraceFlag); + poFeature->SetField(HEADER_NUMBER_OF_EXTENDED_TEXTUAL_FILE_HEADER, sBFH.nNumberOfExtendedTextualFileHeader); + + return poFeature; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/segy.txt b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/segy.txt new file mode 100644 index 000000000..91d706d4b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/segy/segy.txt @@ -0,0 +1,30 @@ +http://sioseis.ucsd.edu/segy.header.html +http://www.seg.org/documents/10161/77915/seg_y_rev1.pdf + +http://gdr.nrcan.gc.ca/seismtlitho/archive/le/stacks_e.php +http://apps1.gdr.nrcan.gc.ca/lithoprobe/le/ld0077_file_080.sgy + +http://segymat.sourceforge.net/segypy/ +http://www.cg.nrcan.gc.ca/lith_arch/stacks/ksz/ld0057_file_00095.sgy --> ko +http://apps1.gdr.nrcan.gc.ca/lithoprobe/ksz/ld0057_file_00095.sgy + +http://utam.gg.utah.edu/SeismicData/SeismicData.html +http://utam.gg.utah.edu/SeismicData/NewMont/Acsg.segy +http://utam.gg.utah.edu/SeismicData/WFault3D/WD_3D.sgy + +http://www.syqwestinc.com/stratabox/stratabox%20data%20download/index.htm +http://www.syqwestinc.com/stratabox/stratabox%20data%20download/20060113100958-1.zip + +http://www.syqwestinc.com/B2010/data%20download/index.htm +http://www.syqwestinc.com/B2010/data%20download/20071121142735_CH1_35P.zip + +http://software.seg.org/datasets/2D/Hess_VTI/ +ftp://software.seg.org/pub/datasets/2D/Hess_VTI/timodel_c11.segy.gz + +http://www.segy.ca/segy/examples/ --> non standard formats. Do not work with OGR SEGY driver + +http://wush.net/trac/geocraft/browser/geoscience_data/trunk/saltmodel +http://wush.net/trac/geocraft/export/12564/geoscience_data/trunk/saltmodel/SeisSaltDepth20m5km.segy +http://wush.net/trac/geocraft/export/12564/geoscience_data/trunk/abavo/test_data3far.segy + +g++ -fPIC -g -Wall ogr/ogrsf_frmts/segy/*.cpp -shared -o ogr_SEGY.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/segy -L. -lgdal diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/GNUmakefile new file mode 100644 index 000000000..7344dbe71 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/GNUmakefile @@ -0,0 +1,15 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrselafindriver.o ogrselafindatasource.o ogrselafinlayer.o io_selafin.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_selafin.h io_selafin.h + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/drv_selafin.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/drv_selafin.html new file mode 100644 index 000000000..b6bf8bfa4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/drv_selafin.html @@ -0,0 +1,259 @@ + + +Selafin files + + + + + +

    Selafin files

    + +

    OGR supports reading from 2D Selafin/Seraphin files. Selafin/Seraphin is the generic output and input format of geographical files in the open-source Telemac hydraulic model. The file format is suited to the description of numerical attributes for a set of point features at different time steps. Those features usually correspond to the nodes in a finite-element model. The file also holds a connectivity table which describes the elements formed by those nodes and which can also be read by the driver.

    + +

    The driver supports the use of VSI virtual files as Selafin datasources.

    + +

    The driver offers full read-write support on Selafin files. However, due to the particular nature of Selafin files where element (polygon) features and node (point) features are closely related, writing on Selafin layers can lead to counter-intuitive results. In a general way, writing on any layer of a Selafin data-source will cause side effects on all the other layers. Also, it is very important not to open the same datasource more than once in update mode. Having two processes write at the same time on a single datasource can lead to irreversible data corruption. The driver issues a warning each time a datasource is opened in update mode.

    + +

    Magic bytes

    + +

    There is no generic extension to Selafin files. The adequate format is tested by looking at a dozen of magic bytes at the beginning of the file: +

      +
    • The first four bytes of the file should contain the values (in hexadecimal): 00 00 00 50. This actually indicates the start of a string of length 80 in the file.
    • +
    • At position 84 in the file, the eight next bytes should read (in hexadecimal): 00 00 00 50 00 00 00 04.
    • +
    +

    + +

    Files which match those two criterias are considered to be Selafin files and the driver will report it has opened them successfully.

    + +

    Format

    + +

    Selafin format is designed to hold data structures in a portable and compact way, and to allow efficient random access to the data. To this purpose, Selafin files are binary files with a generic structure.

    + +

    Elements

    +

    Selafin files are made of the juxtaposition of elements. Elements have one of the following types: +

      +
    • integer,
    • +
    • string,
    • +
    • floating point values,
    • +
    • arrays of integers,
    • +
    • arrays of floating point values.
    • +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ElementInternal representationComments
    Integer
    abcd
    Integers are stored on 4 bytes in big-endian format (most significant byte first). The value of the integer is 224.a+216.b+28.c+d.
    Floating point
    abcd
    Floating point values are stored on 4 bytes in IEEE 754 format and under big-endian convention (most significant byte first). Endianness is detected at run time only once when the first floating point value is read.
    String
    Length12345...Length
    Strings are stored in three parts: +
      +
    • an integer holding the length (in characters) of the string, over 4 bytes;
    • +
    • the sequence of characters of the string, each character on one byte;
    • +
    • the same integer with the length of the string repeated
    • +
    +
    Array of integers
    Length123...Length
    Arrays of integers are stored in three parts: +
      +
    • an integer holding the length (in bytes, thus 4 times the number of elements) of the array, over 4 bytes;
    • +
    • the sequence of integers in the array, each integer on 4 bytes as described earlier;
    • +
    • the same integer with the length of the array repeated
    • +
    +
    Array of floating point values
    Length123...Length
    Arrays of floating point values are stored in three parts: +
      +
    • an integer holding the length (in bytes, thus 4 times the number of elements) of the array, over 4 bytes;
    • +
    • the sequence of floating point values in the array, each one on 4 bytes as described earlier;
    • +
    • the same integer with the length of the array repeated
    • +
    +
    + +

    Full structure

    +

    The header of a Selafin file holds the following elements in that exact order: +

      +
    • a string of 80 characters with the title of the study; the last 8 characters shall be "SERAPHIN" or "SERAFIN" or "SERAFIND";
    • +
    • an array of integers of exactly 2 elements, the first one being the number of variables (attributes) nVar, and the second is ignored;
    • +
    • nVar strings with the names of the variables, each one with length 32;
    • +
    • an array of integers of exactly 10 elements: +
        +
      • the third element is the x-coordinate of the origin of the model;
      • +
      • the fourth element is the y-coordinate of the origin of the model;
      • +
      • the tenth element isDate indicates if the date of the model has to be read (see later);
      • +
      • in addition, the second element being unused by hydraulic software at the moment, it is used by the driver to store the spatial reference system of the datasource, in the form of a single integer with the EPSG number of the projection;
      • +
    • +
    • if isDate=1, an array of integers of exactly 6 elements, with the starting date of the model (year, month, day, hour, minute, second);
    • +
    • an array of integers of exactly 4 elements: +
        +
      • the first element is the number of elements nElements, +
      • the second element is the number of points nPoints, +
      • the third element is the number of points per elementnPointsPerElement, +
      • the fourth element must be 1;
      • +
    • +
    • an array of integers of exactly nElements*nPointsPerElement elements, with each successive set of nPointsPerElement being the list of the number of the points (number starting with 1) constituting an element;
    • +
    • an array of integers of exactly nPoints elements ignored by the driver (the elements shall be 0 for inner points and another value for the border points where a limit condition is applied);
    • +
    • an array of floating point values of exactly nPoints elements with the x-coordinates of the points;
    • +
    • an array of floating point values of exactly nPoints elements with the y-coordinates of the points;
    • +

    + +

    The rest of the file actually holds the data for each successive time step. A time step contains the following elements: +

      +
    • a array of floating point values of exactly 1 element, being the date of the time step relative to the starting date of the simulation (usually in seconds);
    • +
    • nVar array of floating point values, each with exactly nPoints elements, with the values of each attribute for each point at the current time step.
    • +

    + +

    Mapping between file and layers

    + +

    Layers in a Selafin datasource

    +

    The Selafin driver accepts only Selafin files as data sources.

    + +

    Each Selafin file can hold one or several time steps. All the time steps are read by the driver and two layers are generated for each time step: +

      +
    • one layer with the nodes (points) and their attributes: its name is the base name of the data source, followed by "_p" (for Points);
    • +
    • one layer with the elements (polygons) and their attributes calculated as the averages of the values of the attributes of their vertices: its name is the base name of the data source, followed by "_e" (for Elements).
    • +

    + +

    Finally, either the number of the time step, or the calculated date of the time step (based on the starting date and the number of seconds elapsed), is added to the name. A data source in a file called Results may therefore be read as several layers: +

      +
    • Results_p2014_05_01_20_00_00, meaning that the layers holds the attributes for the nodes and that the results hold for the time step at 8:00 PM, on May 1st, 2014;
    • +
    • Results_e2014_05_01_20_00_00, meaning that the layers holds the attributes for the elements and that the results hold for the time step at 8:00 PM, on May 1st, 2014;
    • +
    • Results_p2014_05_01_20_15_00, meaning that the layers holds the attributes for the elements and that the results hold for the time step at 8:15 PM, on May 1st, 2014;
    • +
    • ...
    • +
    +

    + +

    Constraints on layers

    +

    Because of the format of the Selafin file, the layers in a single Selafin datasource are not independent from each other. Changing one layer will most certainly have side effects on all other layers. The driver updates all the layers to match the constraints: +

      +
    • All the point layers have the same number of features. When a feature is added or removed in one layer, it is also added or removed in all other layers.
    • +
    • Features in different point layers share the same geometry. When the position of one point is changed, it is also changed in all other layers.
    • +
    • All the element layers have the same number of features. When a feature is added or removed in one layer, it is also added or removed in all other layers.
    • +
    • All the polygons in element layers have the same number of vertices. The number of vertices is fixed when the first feature is added to an element layer, and can not be changed afterwards without recreating the datasource from scratch.
    • +
    • Features in different element layers share the same geometry. When an element is added or removed in one layer, it is also added or removed in all other layers.
    • +
    • Every vertex of every feature in an element layer has a corresponding point feature in the point layers. When an element feature is added, if its vertices do not exist yet, they are created in the point layers.
    • +
    • Points and elements layers only support attributes of type "REAL". The format of real numbers (width and precision) can not be changed.
    • +

    + +

    Layer filtering specification

    +

    As a single Selafin files may hold millions of layers, and the user is generally interested in only a few of them, the driver supports syntactic sugar to filter the layers before they are read.

    + +

    When the datasource is specified, it may be followed immediatly by a layer filtering specification., as in Results[0:10]. The effects of the layer filtering specification is to indicate which time steps shall be loaded from all Selafin files.

    + +

    The layer filtering specification is a comma-separated sequence of range specifications, delimited by square brackets and maybe preceded by the character 'e' or 'p'. The effect of characters 'e' and 'p' is to select respectively either only elements or only nodes. If no character is added, both nodes and elements are selected. +Each range specification is: +

      +
    • either a single number, representing one single time step (whose numbers start with 0),
    • +
    • or a set of two numbers separated by a colon: in that case, all the time steps between and including those two numbers are selected; if the first number is missing, the range starts from the beginning of the file (first time step); if the second number is missing, the range goes to the end of the file (last time step);
    • +
    +Numbers can also be negative. In this case, they are counted from the end of the file, -1 being the last time step.

    + +

    Some examples of layer filtering specifications: + + + + + +
    [0]First time step only, but return both points and elements
    [e:9]First 10 time steps only, and only layers with elements
    [p-4:]Last 4 time steps only, and only layers with nodes
    [3,10,-2:-1]4th, 11th, and last two time steps, for both nodes and elements

    + +

    Datasource creation options

    +

    Datasource creation options can be specified with the "-dsco" flag in ogr2ogr.

    +
    +
    TITLE
    +
    Title of the datasource, stored in the Selafin file. The title must not hold more than 72 characters. If it is longer, it will be truncated to fit in the file.
    +
    DATE
    +
    Starting date of the simulation. Each layer in a Selafin file is characterized by a date, counted in seconds since a reference date. This option allows providing the reference date. The format of this field must be YYYY-MM-DD_hh:mm:ss. The format does not mention the time zone.
    +
    + +

    An example of datasource creation option is: -dsco TITLE="My simulation" -dsco DATE=2014-05-01_10:00:00. + +

    Layer creation options

    +

    Layer creation options can be specified with the "-lco" flag in ogr2ogr.

    +
    +
    DATE
    +
    Date of the time step relative to the starting date of the simulation (see Datasource creation options). This is a single floating-point value giving the number of seconds since the starting date.
    +
    + +

    An example of datasource creation option is: -lco DATE=24000. + +

    Notes about the creation and the update of a Selafin datasource

    +

    The driver supports creating and writing to Selafin datasources, but there are some caveats when doing so.

    + +

    When a new datasource is created, it does not contain any layer, feature or attribute.

    + +

    When a new layer is created, it automatically inherits the same number of features and attributes as the other layers of the same type (points or elements) already in the datasource. The features inherit the same geometry as their corresponding ones in other layers. The attributes are set to 0. If there was no layer in the datasource yet, the new layer is created with no feature and attribute.In any case, when a new layer is created, two layers are actually added: one for points and one for elements.

    + +

    New features and attributes can be added to the layers or removed. The behaviour depends on the type of layer (points or elements). The following table explains the behaviour of the driver in the different cases.

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    OperationPoints layersElement layers
    Change the geometry of a featureThe coordinates of the point are changed in the current layer and all other layers in the datasource.The coordinates of all the vertices of the element are changed in the current layer and all other layers in the datasource. It is not possible to change the number of vertices. The order of the vertices matters.
    Change the attributes of a featureThe attributes of the point are changed in the current layer only.No effect.
    Add a new featureA new point is added at the end of the list of features, for this layer and all other layers. Its attributes are set to the values of the new feature.The operation is only allowed if the new feature has the same number of vertices as the other features in the layer. The vertices are checked to see if they currently exist in the set of points. A vertex is considered equal to a point if its distance is less than some maximum distance, approximately equal to 1/1000th of the average distance between two points in the points layers. When a corresponding point is found, it is used as a vertex for the element. If no point is found, a new is created in all associated layers.
    Delete a featureThe point is removed from the current layer and all point layers in the datasource. All elements using this point as a vertex are also removed from all element layers in the datasource.The element is removed from the current layer and all element layers in the datasource.
    + +

    Typically, this implementation of operations is exactly what you'll expect. For example, ogr2ogr can be used to reproject the file without changing the inner link between points and elements.

    + +

    It should be noted that update operations on Selafin datasources are very slow. This is because the format does no allow for quick insertions or deletion of features and the file must be recreated for each operation.

    + +

    VSI Virtual File System API support

    + +

    The driver supports reading and writing to files managed by VSI Virtual File System API, which include "regular" files, as well as files in the /vsizip/ (read-write) , /vsigzip/ (read-only) , /vsicurl/ (read-only) domains.

    + +

    Other notes

    +

    There is no SRS specification in the Selafin standard. The implementation of SRS is an addition of the driver and stores the SRS in an unused data field in the file. Future software using the Selafin standard may use this field and break the SRS specification. In this case, Selafin files will still be readable by the driver, but their writing will overwrite a value which may have another purpose.

    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/io_selafin.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/io_selafin.cpp new file mode 100644 index 000000000..33bd1901b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/io_selafin.cpp @@ -0,0 +1,666 @@ +/****************************************************************************** + * Project: Selafin importer + * Purpose: Implementation of functions for reading records in Selafin files + * Author: François Hissel, francois.hissel@gmail.com + * + ****************************************************************************** + * Copyright (c) 2014, François Hissel + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "io_selafin.h" +#include "cpl_vsi.h" +#include "cpl_conv.h" +#include "cpl_port.h" +#include "cpl_error.h" +#include "cpl_quad_tree.h" + +namespace Selafin { + + const char SELAFIN_ERROR_MESSAGE[]="Error when reading Selafin file\n"; + + struct Point { + long nIndex; + const Header *poHeader; + }; + + void GetBoundsFunc(const void *hFeature,CPLRectObj *poBounds) { + const Point *poPoint=(const Point*)hFeature; + poBounds->minx=poPoint->poHeader->paadfCoords[0][poPoint->nIndex]; + poBounds->maxx=poPoint->poHeader->paadfCoords[0][poPoint->nIndex]; + poBounds->miny=poPoint->poHeader->paadfCoords[1][poPoint->nIndex]; + poBounds->maxy=poPoint->poHeader->paadfCoords[1][poPoint->nIndex]; + } + + int DumpFeatures(void *pElt, + CPL_UNUSED void *pUserData) { + Point *poPoint=(Point*)pElt; + delete poPoint; + return TRUE; + } + + + /****************************************************************/ + /* Header */ + /****************************************************************/ + Header::Header():nMinxIndex(-1),nMaxxIndex(-1),nMinyIndex(-1),nMaxyIndex(-1),bTreeUpdateNeeded(true),fp(0),pszFilename(0),pszTitle(0),papszVariables(0),nPointsPerElement(0),panConnectivity(0),poTree(0),panBorder(0),panStartDate(0),nEpsg(0) { + paadfCoords[0]=0; + paadfCoords[1]=0; + for (size_t i=0;i<7;++i) anUnused[i]=0; + } + + Header::~Header() { + if (pszFilename!=0) CPLFree(pszFilename); + if (pszTitle!=0) CPLFree(pszTitle); + if (papszVariables!=0) { + for (int i=0;iminx=paadfCoords[0][nMinxIndex]; + poBox->maxx=paadfCoords[0][nMaxxIndex]; + poBox->miny=paadfCoords[1][nMinyIndex]; + poBox->maxy=paadfCoords[1][nMaxyIndex]; + return poBox; + } + + void Header::updateBoundingBox() { + if (nPoints>0) { + nMinxIndex=0; + for (long i=1;ipaadfCoords[0][nMaxxIndex]) nMaxxIndex=i; + nMinyIndex=0; + for (long i=1;ipaadfCoords[1][nMaxyIndex]) nMaxyIndex=i; + } + } + + long Header::getClosestPoint(const double &dfx,const double &dfy,const double &dfMax) { + // If there is no quad-tree of the points, build it now + if (bTreeUpdateNeeded) { + if (poTree!=0) { + CPLQuadTreeForeach(poTree,DumpFeatures,0); + CPLQuadTreeDestroy(poTree); + } + } + if (bTreeUpdateNeeded || poTree==0) { + bTreeUpdateNeeded=false; + CPLRectObj *poBB=getBoundingBox(); + poTree=CPLQuadTreeCreate(poBB,GetBoundsFunc); + delete poBB; + CPLQuadTreeSetBucketCapacity(poTree,2); + for (long i=0;ipoHeader=this; + poPoint->nIndex=i; + CPLQuadTreeInsert(poTree,(void*)poPoint); + } + } + // Now we can look for the nearest neighbour using this tree + long nIndex=-1; + double dfMin; + CPLRectObj poObj; + poObj.minx=dfx-dfMax; + poObj.maxx=dfx+dfMax; + poObj.miny=dfy-dfMax; + poObj.maxy=dfy+dfMax; + int nFeatureCount; + void **phResults=CPLQuadTreeSearch(poTree,&poObj,&nFeatureCount); + if (nFeatureCount<=0) return -1; + double dfa,dfb,dfc; + dfMin=dfMax*dfMax; + for (long i=0;ipoHeader->paadfCoords[0][poPoint->nIndex]; + dfa*=dfa; + if (dfa>=dfMin) continue; + dfb=dfy-poPoint->poHeader->paadfCoords[1][poPoint->nIndex]; + dfc=dfa+dfb*dfb; + if (dfcnIndex; + } + } + CPLFree(phResults); + return nIndex; + } + + void Header::addPoint(const double &dfx,const double &dfy) { + // We add the point to all the tables + nPoints++; + for (size_t i=0;i<2;++i) paadfCoords[i]=(double*)CPLRealloc(paadfCoords[i],sizeof(double)*nPoints); + paadfCoords[0][nPoints-1]=dfx; + paadfCoords[1][nPoints-1]=dfy; + panBorder=(long*)CPLRealloc(panBorder,sizeof(long)*nPoints); + panBorder[nPoints-1]=0; + // We update the bounding box + if (nMinxIndex==-1 || dfxpaadfCoords[0][nMaxxIndex]) nMaxxIndex=nPoints-1; + if (nMinyIndex==-1 || dfypaadfCoords[1][nMaxyIndex]) nMaxyIndex=nPoints-1; + // We update some parameters of the header + bTreeUpdateNeeded=true; + setUpdated(); + } + + void Header::removePoint(long nIndex) { + // We remove the point from all the tables + nPoints--; + for (size_t i=0;i<2;++i) { + for (long j=nIndex;jpaadfCoords[0][nMaxxIndex]) nMaxxIndex=i; + } + if (nIndex==nMinyIndex) { + nMinyIndex=0; + for (long i=1;ipaadfCoords[1][nMaxyIndex]) nMaxyIndex=i; + } + } + + // We update some parameters of the header + bTreeUpdateNeeded=true; + setUpdated(); + } + +#ifdef notdef + /****************************************************************/ + /* TimeStep */ + /****************************************************************/ + TimeStep::TimeStep(long nRecordsP,long nFieldsP):nFields(nFieldsP) { + papadfData=(double**)VSIMalloc2(sizeof(double*),nFieldsP); + for (long i=0;ipoNext; + delete poFirst->poStep; + delete poFirst; + poFirst=poTmp; + } + } +#endif + + /****************************************************************/ + /* General functions */ + /****************************************************************/ + int read_integer(VSILFILE *fp,long &nData,bool bDiscard) { + unsigned char anb[4]; + if (VSIFReadL(anb,1,4,fp)<4) { + CPLError(CE_Failure,CPLE_FileIO,"%s",SELAFIN_ERROR_MESSAGE); + return 0; + }; + if (!bDiscard) { + nData=0; + for (size_t i=0;i<4;++i) nData=(nData*0x100)+anb[i]; + } + return 1; + } + + int write_integer(VSILFILE *fp,long nData) { + unsigned char anb[4]; + for (int i=3;i>=0;--i) { + anb[i]=nData%0x100; + nData/=0x100; + } + if (VSIFWriteL(anb,1,4,fp)<4) { + CPLError(CE_Failure,CPLE_FileIO,"%s",SELAFIN_ERROR_MESSAGE); + return 0; + }; + return 1; + } + + long read_string(VSILFILE *fp,char *&pszData,bool bDiscard) { + long nLength=0; + read_integer(fp,nLength); + if (nLength<=0 || nLength+1<=0) { + CPLError(CE_Failure,CPLE_FileIO,"%s",SELAFIN_ERROR_MESSAGE); + return 0; + } + if (bDiscard) { + if (VSIFSeekL(fp,nLength+4,SEEK_CUR)!=0) { + CPLError(CE_Failure,CPLE_FileIO,"%s",SELAFIN_ERROR_MESSAGE); + return 0; + } + } + else { + pszData=(char*)CPLMalloc(sizeof(char)*(nLength+1)); + if ((long)VSIFReadL(pszData,1,nLength,fp)<(long)nLength) { + CPLError(CE_Failure,CPLE_FileIO,"%s",SELAFIN_ERROR_MESSAGE); + return 0; + } + pszData[nLength]=0; + if (VSIFSeekL(fp,4,SEEK_CUR)!=0) { + CPLError(CE_Failure,CPLE_FileIO,"%s",SELAFIN_ERROR_MESSAGE); + return 0; + } + } + return nLength; + } + + int write_string(VSILFILE *fp,char *pszData,size_t nLength) { + if (nLength==0) nLength=strlen(pszData); + if (write_integer(fp,nLength)==0) return 0; + if (VSIFWriteL(pszData,1,nLength,fp)fp=fp; + poHeader->pszFilename=CPLStrdup(pszFilename); + long *panTemp; + // Read the title + nLength=read_string(fp,poHeader->pszTitle); + if (nLength==0) { + delete poHeader; + return 0; + } + // Read the array of 2 integers, with the number of variables at the first position + nLength=read_intarray(fp,panTemp); + if (nLength!=2) { + delete poHeader; + CPLFree(panTemp); + return 0; + } + poHeader->nVar=panTemp[0]; + poHeader->anUnused[0]=panTemp[1]; + CPLFree(panTemp); + if (poHeader->nVar<0) { + delete poHeader; + return 0; + } + // For each variable, read its name as a string of 32 characters + poHeader->papszVariables=(char**)VSIMalloc2(sizeof(char*),poHeader->nVar); + for (long i=0;inVar;++i) { + nLength=read_string(fp,poHeader->papszVariables[i]); + if (nLength==0) { + delete poHeader; + return 0; + } + // We eliminate quotes in the names of the variables because SQL requests don't seem to appreciate them + char *pszc=poHeader->papszVariables[i]; + while (*pszc!=0) { + if (*pszc=='\'') *pszc=' '; + pszc++; + } + } + // Read an array of 10 integers + nLength=read_intarray(fp,panTemp); + if (nLength<10) { + delete poHeader; + CPLFree(panTemp); + return 0; + } + poHeader->anUnused[1]=panTemp[0]; + poHeader->nEpsg=panTemp[1]; + poHeader->adfOrigin[0]=panTemp[2]; + poHeader->adfOrigin[1]=panTemp[3]; + for (size_t i=4;i<9;++i) poHeader->anUnused[i-2]=panTemp[i]; + // If the last integer was 1, read an array of 6 integers with the starting date + if (panTemp[9]==1) { + nLength=read_intarray(fp,poHeader->panStartDate); + if (nLength<6) { + delete poHeader; + CPLFree(panTemp); + return 0; + } + } + CPLFree(panTemp); + // Read an array of 4 integers with the number of elements, points and points per element + nLength=read_intarray(fp,panTemp); + if (nLength<4) { + delete poHeader; + CPLFree(panTemp); + return 0; + } + poHeader->nElements=panTemp[0]; + poHeader->nPoints=panTemp[1]; + poHeader->nPointsPerElement=panTemp[2]; + if (poHeader->nElements<0 || poHeader->nPoints<0 || poHeader->nPointsPerElement<0 || panTemp[3]!=1) { + delete poHeader; + CPLFree(panTemp); + return 0; + } + CPLFree(panTemp); + // Read the connectivity table as an array of nPointsPerElement*nElements integers, and check if all point numbers are valid + nLength=read_intarray(fp,poHeader->panConnectivity); + if (nLength!=poHeader->nElements*poHeader->nPointsPerElement) { + delete poHeader; + return 0; + } + for (long i=0;inElements*poHeader->nPointsPerElement;++i) { + if (poHeader->panConnectivity[i]<=0 || poHeader->panConnectivity[i]>poHeader->nPoints) { + delete poHeader; + return 0; + } + } + // Read the array of nPoints integers with the border points + nLength=read_intarray(fp,poHeader->panBorder); + if (nLength!=poHeader->nPoints) { + delete poHeader; + return 0; + } + // Read two arrays of nPoints floats with the coordinates of each point + for (size_t i=0;i<2;++i) { + read_floatarray(fp,poHeader->paadfCoords+i); + if (nLengthnPoints) { + delete poHeader; + return 0; + } + for (long j=0;jnPoints;++j) poHeader->paadfCoords[i][j]+=poHeader->adfOrigin[i]; + } + // Update the boundinx box + poHeader->updateBoundingBox(); + // Update the size of the header and calculate the number of time steps + poHeader->setUpdated(); + long nPos=poHeader->getPosition(0); + poHeader->nSteps=(nFileSize-nPos)/(poHeader->getPosition(1)-nPos); + return poHeader; + } + + int write_header(VSILFILE *fp,Header *poHeader) { + VSIRewindL(fp); + if (write_string(fp,poHeader->pszTitle,80)==0) return 0; + long anTemp[10]={0}; + anTemp[0]=poHeader->nVar; + anTemp[1]=poHeader->anUnused[0]; + if (write_intarray(fp,anTemp,2)==0) return 0; + for (long i=0;inVar;++i) if (write_string(fp,poHeader->papszVariables[i],32)==0) return 0; + anTemp[0]=poHeader->anUnused[1]; + anTemp[1]=poHeader->nEpsg; + anTemp[2]=(long)poHeader->adfOrigin[0]; + anTemp[3]=(long)poHeader->adfOrigin[1]; + for (size_t i=4;i<9;++i) anTemp[i]=poHeader->anUnused[i-2]; + anTemp[9]=(poHeader->panStartDate!=0)?1:0; + if (write_intarray(fp,anTemp,10)==0) return 0; + if (poHeader->panStartDate!=0 && write_intarray(fp,poHeader->panStartDate,6)==0) return 0; + anTemp[0]=poHeader->nElements; + anTemp[1]=poHeader->nPoints; + anTemp[2]=poHeader->nPointsPerElement; + anTemp[3]=1; + if (write_intarray(fp,anTemp,4)==0) return 0; + if (write_intarray(fp,poHeader->panConnectivity,poHeader->nElements*poHeader->nPointsPerElement)==0) return 0; + if (write_intarray(fp,poHeader->panBorder,poHeader->nPoints)==0) return 0; + double *dfVals; + dfVals=(double*)VSIMalloc2(sizeof(double),poHeader->nPoints); + if (poHeader->nPoints>0 && dfVals==0) return 0; + for (size_t i=0;i<2;++i) { + for (long j=0;jnPoints;++j) dfVals[j]=poHeader->paadfCoords[i][j]-poHeader->adfOrigin[i]; + if (write_floatarray(fp,dfVals,poHeader->nPoints)==0) { + CPLFree(dfVals); + return 0; + } + } + CPLFree(dfVals); + return 1; + } + +#ifdef notdef + int read_step(VSILFILE *fp,const Header *poHeader,TimeStep *&poStep) { + poStep=new TimeStep(poHeader->nPoints,poHeader->nVar); + long nLength; + if (read_integer(fp,nLength)==0 || nLength!=1) { + delete poStep; + return 0; + } + if (read_float(fp,poStep->dfDate)==0) { + delete poStep; + return 0; + } + if (read_integer(fp,nLength)==0 || nLength!=1) { + delete poStep; + return 0; + } + for (long i=0;inVar;++i) { + nLength=read_floatarray(fp,&(poStep->papadfData[i])); + if (nLength!=poHeader->nPoints) { + delete poStep; + return 0; + } + } + return 1; + } + + + int write_step(VSILFILE *fp,const Header *poHeader,const TimeStep *poStep) { + if (write_integer(fp,1)==0) return 0; + if (write_float(fp,poStep->dfDate)==0) return 0; + if (write_integer(fp,1)==0) return 0; + for (long i=0;inVar;++i) { + if (write_floatarray(fp,poStep->papadfData[i])==0) return 0; + } + return 1; + } + + int read_steps(VSILFILE *fp,const Header *poHeader,TimeStepList *&poSteps) { + poSteps=0; + TimeStepList *poCur,*poNew; + for (long i=0;inSteps;++i) { + poNew=new TimeStepList(0,0); + if (read_step(fp,poHeader,poNew->poStep)==0) { + delete poSteps; + return 0; + } + if (poSteps==0) poSteps=poNew; else poCur->poNext=poNew; + poCur=poNew; + } + return 1; + } + + int write_steps(VSILFILE *fp,const Header *poHeader,const TimeStepList *poSteps) { + const TimeStepList *poCur=poSteps; + while (poCur!=0) { + if (write_step(fp,poHeader,poCur->poStep)==0) return 0; + poCur=poCur->poNext; + } + return 1; + } +#endif + +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/io_selafin.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/io_selafin.h new file mode 100644 index 000000000..d7205edc6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/io_selafin.h @@ -0,0 +1,358 @@ +/****************************************************************************** + * Project: Selafin importer + * Purpose: Definition of functions for reading records in Selafin files + * Author: François Hissel, francois.hissel@gmail.com + * + ****************************************************************************** + * Copyright (c) 2014, François Hissel + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + +#ifndef IO_SELAFIN_H_INC +#define IO_SELAFIN_H_INC + +#include "cpl_vsi.h" +#include "cpl_quad_tree.h" + +namespace Selafin { + /** + * \brief Data structure holding general information about a Selafin file + */ + class Header { + private: + long nHeaderSize; //!< Size (in bytes) of the header of the file (seeking to this location brings to the first "feature") + long nStepSize; //!< Size (in bytes) of one feature in the file + long nMinxIndex; //!< Index of the point at the western border of the bounding box + long nMaxxIndex; //!< Index of the point at the eastern border of the bounding box + long nMinyIndex; //!< Index of the point at the southern border of the bounding box + long nMaxyIndex; //!< Index of the point at the northern border of the bounding box + bool bTreeUpdateNeeded; //!< Tell if the quad tree has to be updated + public: + //size_t nRefCount; //!< Number of references to this object + VSILFILE *fp; //!< Pointer to the file with the layers + char *pszFilename; //!< Name of the Selafin file + char *pszTitle; //!< Title of the simulation + long nVar; //!< Number of variables + char **papszVariables; //!< Name of the variables + long nPoints; //!< Total number of points + long nElements; //!< Total number of elements + long nPointsPerElement; //!< Number of points per element + long *panConnectivity; //!< Connectivity table of elements: first nPointsPerElement elements are the indices of the points making the first element, and so on. In the Selafin file, the first point has index 1. + double *paadfCoords[2]; //!< Table of coordinates of points: x then y + CPLQuadTree *poTree; //!< Quad-tree for spatially indexing points in the array paadfCoords. The tree will mostly contain a Null value, until a request is made to find a closest neighbour, in which case it will be built and maintained + double adfOrigin[2]; //!< Table of coordinates of the origin of the axis + long *panBorder; //!< Array of integers defining border points (0 for an inner point, and the index of the border point otherwise). This table is not used by the driver but stored to allow to rewrite the header if needed + long *panStartDate; //!< Table with the starting date of the simulation (may be 0 if date is not defined). Date is registered as a set of six elements: year, month, day, hour, minute, second. + long nSteps; //!< Number of steps in the Selafin file + long nEpsg; //!< EPSG of the file + long anUnused[7]; //!< Array of integers for storing the eight values read from the header but not actually used by the driver. These values are merely saved to rewrite them in the file if it is changed. + + Header(); //!< Standard constructor + ~Header(); //!< Destructor of structure + + /** + * \brief Return the position of a particular data in the Selafin file + * + * This function returns the position in the Selafin file of a particular element, characterized by the number of the time step, the index of the feature and the index of the attribute. If both nFeature and nAttribute are equal to -1 (default value), the function returns the position of the start of this time step. If nFeature is -1 but nAttribute is greater than 0, the function returns the position of the table for the given attribute (compatible with Selafin::read_floatarray) + * \param nStep Number of the time step, starting with 0 + * \param nFeature Index of the feature (point), starting with 0 + * \param nAttribute Index of the attribute, starting with 0 + * \return Position (in bytes) from the start of the file + */ + long getPosition(long nStep,long nFeature=-1,long nAttribute=-1) const; // {return nHeaderSize+nStep*nStepSize+(nFeature!=-1)?(12+nAttribute*(nPoints+2)*4+4+nFeature*4):0;} + + /** + * \brief Return the bounding box of the points + * + * This function returns the bounding box of the set of points. The bounding box is stored in memory for quicker access. + * \return Pointer to the bounding box. The calling program is responsible for destroying it + */ + CPLRectObj *getBoundingBox() const; + + /** + * \brief Update the bounding box of the points + * + * This function calculates the bounding box of the set of points and stores it in the class. + */ + void updateBoundingBox(); + + /** + * \brief Return the index of the point closest to the given coordinates + * + * This function searches the point in the array which is the closest to the one given by the user in arguments, but no farther than distance dfMax. + * \param dfx x-coordinate of the reference point + * \param dfy y-coordinate of the reference point + * \param dfMax Maximum distance allowed to the point + * \return Index of the closest point in the array, -1 if there was no point at all in the array closer than distance dfMax + */ + long getClosestPoint(const double &dfx,const double &dfy,const double &dfMax); + + /** + * \brief Tells that the header has been changed + * + * This function must be used whenever one of the member of the structure was changed. It forces the recalculation of some variables (header size and step size). + */ + void setUpdated(); + + /** + * \brief Add a new point at the end of point list + * + * This function add a new point at the end of the array. If the arrays of coordinates are too small, they may be resized. The bounding box is updated. + * \param dfx x-coordinate of the new point + * \param dfy y-coordinate of the new point + */ + void addPoint(const double &dfx,const double &dfy); + + /** + * \brief Remove a point from the point list + * + * This function removes a point at position nIndex from the array of points. The bounding box is updated. All the elements which referenced this point are also removed. + * \param nIndex Index of the point which has to be removed + */ + void removePoint(long nIndex); + + }; + +#ifdef notdef + /** + * \brief Data structure holding the attributes of all nodes for one time step + */ + class TimeStep { + private: + //long nRecords; //!< Number of records (first index) in the TimeStep::papadfData array, which should correspond to the number of features (either points or elements) in a Selafin layer + long nFields; //!< Number of fields for each record (second index) in the TimeStep::papadfData array, which should correspond to the number of attributes in a Selafin layer + public: + double dfDate; //!< Date of the time step (usually in seconds after the starting date) + double **papadfData; //!< Double-indexed array with values of all atributes for all features. The first index is the number of the attribute, the second is the number of the feature. + + /** + * \brief Constructor of the structure + * + * This function allocates the TimeStep::papadfData array based on the dimensions provided in argument. + * \param nRecords Number of records (first index) in the TimeStep::papadfData array, which should correspond to the number of features (either points or elements) in a Selafin layer + * \param nFields Number of fields for each record (second index) in the TimeStep::papadfData array, which should correspond to the number of attributes in a Selafin layer + */ + TimeStep(long nRecordsP,long nFieldsP); + + ~TimeStep(); //!< Standard destructor + }; + + /** + * \brief Structure holding a chained list of time steps (of class TimeStep) + */ + class TimeStepList { + public: + TimeStep *poStep; //!< Pointer to the time step structure + TimeStepList *poNext; //!< Pointer to the next element in the list + + TimeStepList(TimeStep *poStepP,TimeStepList *poNextP):poStep(poStepP),poNext(poNextP) {} //!< Standard constructor + ~TimeStepList(); //!< Standard destructor + }; +#endif + + /** + * \brief Read an integer from a Selafin file + * + * This function reads an integer from an opened file. In Selafin files, integers are stored on 4 bytes in big-endian format (most significant byte first). + * \param fp Pointer to an open file + * \param nData After the execution, contains the value read + * \param bDiscard If true, the function does not attempt to save the value read in the variable nData, but only advances in the file as it should. Default value is false. + * \return 1 if the integer was successfully read, 0 otherwise + */ + int read_integer(VSILFILE *fp,long &nData,bool bDiscard=false); + + /** + * \brief Write an integer to a Selafin file + * + * This function writes an integer to an opened file. See also Selafin::read_integer for additional information about how integers are stored in a Selafin file. + * \param fp Pointer to an open file + * \param nData Value to be written to the file + * \return 1 if the integer was successfully written, 0 otherwise + */ + int write_integer(VSILFILE *fp,long nData); + + /** + * \brief Read a string from a Selafin file + * + * This function reads a string from an opened file. In Selafin files, strings are stored in three parts: + * - an integer \a n (therefore on 4 bytes) with the length of the string + * - the \a n bytes of the string, preferably in ASCII format + * - once again the integer \a n (on 4 bytes) + * The function does not check if the last integer is the same as the first one. + * \param fp Pointer to an open file + * \param pszData After the execution, contains the value read. The structure is allocated by the function. + * \param bDiscard If true, the function does not attempt to save the value read in the variable nData, but only advances in the file as it should. Default value is false. + * \return Number of characters in string read + */ + long read_string(VSILFILE *fp,char *&pszData,bool bDiscard=false); + + /** + * \brief Write a string to a Selafin file + * + * This function writes a string to an opened file. See also Selafin::read_string for additional information about how strings are stored in a Selafin file. + * \param fp Pointer to an open file + * \param pszData String to be written to the file + * \param nLength Length of the string. If the value is 0, the length is automatically calculated. It is recommended to provide this value to avoid an additional strlen call. However, providing a value larger than the actual size of the string will result in an overflow read access and most likely in a segmentation fault. + * \return 1 if the string was successfully written, 0 otherwise + */ + int write_string(VSILFILE *fp,char *pszData,size_t nLength=0); + + /** + * \brief Read an array of integers from a Selafin file + * + * This function reads an array of integers from an opened file. In Selafin files, arrays of integers are stored in three parts: + * - an integer \a n with the \e size of the array (therefore 4 times the number of elements) + * - the \f$ \frac{n}{4} \f$ elements of the array, each on 4 bytes + * - once again the integer \a n (on 4 bytes) + * The function does not check if the last integer is the same as the first one. + * \param fp Pointer to an open file + * \param panData After the execution, contains the array read. The structure is allocated by the function. + * \param bDiscard If true, the function does not attempt to save the value read in the variable nData, but only advances in the file as it should. Default value is false. + * \return Number of elements in array read, -1 if an error occurred + */ + long read_intarray(VSILFILE *fp,long *&panData,bool bDiscard=false); + + /** + * \brief Write an array of integers to a Selafin file + * + * This function writes an array of integers to an opened file. See also Selafin::read_intarray for additional information about how arrays of integers are stored in a Selafin file. + * \param fp Pointer to an open file + * \param panData Array to be written to the file + * \param nLength Number of elements in the array + * \return 1 if the array was successfully written, 0 otherwise + */ + int write_intarray(VSILFILE *fp,long *panData,size_t nLength); + + /** + * \brief Read a floating point number from a Selafin file + * + * This function reads a floating point value from an opened file. In Selafin files, floats are stored on 4 bytes in big-endian format (most significant byte first). + * \param fp Pointer to an open file + * \param dfData After the execution, contains the value read + * \param bDiscard If true, the function does not attempt to save the value read in the variable nData, but only advances in the file as it should. Default value is false. + * \return 1 if the floating point number was successfully read, 0 otherwise + */ + int read_float(VSILFILE *fp,double &dfData,bool bDiscard=false); + + /** + * \brief Write a floating point number to a Selafin file + * + * This function writes a floating point value from an opened file. See also Selafin::read_float for additional information about how floating point numbers are stored in a Selafin file. + * \param fp Pointer to an open file + * \param dfData Floating point number to be written to the file + * \return 1 if the floating point number was successfully written, 0 otherwise + */ + int write_float(VSILFILE *fp,double dfData); + + /** + * \brief Read an array of floats from a Selafin file + * + * This function reads an array of floats from an opened file. In Selafin files, arrays of floats are stored in three parts: + * - an integer \a n with the \e size of the array (therefore 4 times the number of elements) + * - the \f$ \frac{n}{4} \f$ elements of the array, each on 4 bytes + * - once again the integer \a n (on 4 bytes) + * The function does not check if the last integer is the same as the first one. + * \param fp Pointer to an open file + * \param papadfData After the execution, contains the array read. The structure is allocated by the function. + * \param bDiscard If true, the function does not attempt to save the value read in the variable nData, but only advances in the file as it should. Default value is false. + * \return Number of elements in array read, -1 if an error occurred + */ + long read_floatarray(VSILFILE *fp,double **papadfData,bool bDiscard=false); + + /** + * \brief Write an array of floats to a Selafin file + * + * This function writes an array of floats from an opened file. See also Selafin::read_floatarray for additional information about how arrays of floating point numbers are stored in a Selafin file. + * \param fp Pointer to an open file + * \param padfData Pointer to the array of floating point numbers to be written to the file + * \param nLength Number of elements in the array + * \return 1 if the array was successfully written, 0 otherwise + */ + int write_floatarray(VSILFILE *fp,double *padfData,size_t nLength); + + /** + * \brief Read the header of a Selafin file + * + * This function reads the whole header of a Selafin file (that is everything before the first time step) and stores it in a Selafin::Header structure. The file pointer is moved at the beginning of the file before reading. + * \param fp Pointer to an open file + * \param pszFilename Name of the file + * \return Pointer to a newly-allocated header structure, 0 if the function failed to read the whole header + */ + Header *read_header(VSILFILE *fp,const char* pszFilename); + + /** + * \brief Write the header to a file + * + * This function writes the header to an opened file. The file pointer is moved at the beginning of the file and the content is overwritten. + * \param fp Pointer to an open file with write access + * \return 1 if the header was successfully written, 0 otherwise + */ + int write_header(VSILFILE *fp,Header *poHeader); + +#ifdef notdef + /** + * \brief Read one time step from a Selafin file + * + * This function reads a single time step, with all the field values, from an open Selafin file at the current position. + * \param fp Pointer to an open file + * \param poHeader Pointer to the header structure. This should be read before the call to the function because some of its members are used to determine the format of the time step. + * \param poStep After the execution, holds the data for the current step. The variable is allocated by the function + * \return 1 if the time step was successfully read, 0 otherwise + */ + int read_step(VSILFILE *fp,const Header *poHeader,TimeStep *&poStep); + + /** + * \brief Write one time step to a Selafin file + * + * This function writes a single time step, with all the field values, to an open Selafin file at the current position. + * \param fp Pointer to an open file with write access + * \param poHeader Pointer to the header structure. This should be read before the call to the function because some of its members are used to determine the format of the time step. + * \param poStep Data of current step + * \return 1 if the time step was successfully written, 0 otherwise + */ + int write_step(VSILFILE *fp,const Header *poHeader,const TimeStep *poStep); + + /** + * \brief Read all time steps from a Selafin file + * + * This function reads all time steps, with all the field values, from an open Selafin file at the current position. + * \param fp Pointer to an open file + * \param poHeader Pointer to the header structure. This should be read before the call to the function because some of its members are used to determine the format of the time step. + * \param poSteps After the execution, holds the list of time steps. The variable is allocated by the function + * \return 1 if the time steps were successfully read, 0 otherwise + */ + int read_steps(VSILFILE *fp,const Header *poHeader,TimeStepList *&poSteps); + + /** + * \brief Write one time step to a Selafin file + * + * This function writes a single time step, with all the field values, to an open Selafin file at the current position. + * \param fp Pointer to an open file with write access + * \param poHeader Pointer to the header structure. This should be read before the call to the function because some of its members are used to determine the format of the time step. + * \param poSteps List of all steps + * \return 1 if the time steps were successfully written, 0 otherwise + */ + int write_steps(VSILFILE *fp,const Header *poHeader,const TimeStepList *poSteps); +#endif + +} + +#endif /* ----- #ifndef IO_SELAFIN_H_INC ----- */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/makefile.vc new file mode 100644 index 000000000..d3eb50dc6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrselafindriver.obj ogrselafindatasource.obj ogrselafinlayer.obj io_selafin.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/ogr_selafin.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/ogr_selafin.h new file mode 100644 index 000000000..3ccf613ae --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/ogr_selafin.h @@ -0,0 +1,130 @@ +/****************************************************************************** + * Project: Selafin importer + * Purpose: Definition of classes for OGR driver for Selafin files. + * Author: François Hissel, francois.hissel@gmail.com + * + ****************************************************************************** + * Copyright (c) 2014, François Hissel + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SELAFIN_H_INCLUDED +#define _OGR_SELAFIN_H_INCLUDED + +#include "io_selafin.h" +#include "ogrsf_frmts.h" + +class OGRSelafinDataSource; + +typedef enum {POINTS,ELEMENTS,ALL} SelafinTypeDef; + +/************************************************************************/ +/* Range */ +/************************************************************************/ +class Range { + private: + typedef struct List { + SelafinTypeDef eType; + long nMin,nMax; + List *poNext; + List():poNext(0) {} + List(SelafinTypeDef eTypeP,long nMinP,long nMaxP,List *poNextP):eType(eTypeP),nMin(nMinP),nMax(nMaxP),poNext(poNextP) {} + } List; + List *poVals,*poActual; + long nMaxValue; + static void sortList(List *&poList,List *poEnd=0); + static void deleteList(List *poList); + public: + Range():poVals(0),poActual(0),nMaxValue(0) {} + void setRange(const char *pszStr); + ~Range(); + void setMaxValue(long nMaxValueP); + bool contains(SelafinTypeDef eType,long nValue) const; + size_t getSize() const; +}; + + +/************************************************************************/ +/* OGRSelafinLayer */ +/************************************************************************/ + +class OGRSelafinLayer : public OGRLayer { + private: + SelafinTypeDef eType; + int bUpdate; + long nStepNumber; + Selafin::Header *poHeader; + OGRFeatureDefn *poFeatureDefn; + OGRSpatialReference *poSpatialRef; + GIntBig nCurrentId; + public: + OGRSelafinLayer( const char *pszLayerNameP, int bUpdateP,OGRSpatialReference *poSpatialRefP,Selafin::Header *poHeaderP,int nStepNumberP,SelafinTypeDef eTypeP); + ~OGRSelafinLayer(); + OGRSpatialReference *GetSpatialRef() {return poSpatialRef;} + long GetStepNumber() {return nStepNumber;} + OGRFeature *GetNextFeature(); + OGRFeature *GetFeature(GIntBig nFID); + void ResetReading(); + OGRErr SetNextByIndex(GIntBig nIndex); + OGRFeatureDefn *GetLayerDefn() {return poFeatureDefn;} + int TestCapability(const char *pszCap); + GIntBig GetFeatureCount(int bForce=TRUE); + OGRErr GetExtent(OGREnvelope *psExtent,int bForce=TRUE); + OGRErr ISetFeature(OGRFeature *poFeature); + OGRErr ICreateFeature(OGRFeature *poFeature); + OGRErr CreateField(OGRFieldDefn *poField,int bApproxOK=TRUE); + OGRErr DeleteField(int iField); + OGRErr ReorderFields(int *panMap); + OGRErr AlterFieldDefn(int iField,OGRFieldDefn *poNewFieldDefn,int nFlags); + OGRErr DeleteFeature(GIntBig nFID); +}; + +/************************************************************************/ +/* OGRSelafinDataSource */ +/************************************************************************/ + +class OGRSelafinDataSource : public OGRDataSource { + private: + char *pszName; + char *pszLockName; + OGRSelafinLayer **papoLayers; + Range poRange; + int nLayers; + int bUpdate; + Selafin::Header *poHeader; + CPLString osDefaultSelafinName; + OGRSpatialReference *poSpatialRef; + int TakeLock(const char *pszFilename); + void ReleaseLock(); + public: + OGRSelafinDataSource(); + ~OGRSelafinDataSource(); + int Open(const char * pszFilename, int bUpdate, int bCreate); + int OpenTable(const char * pszFilename); + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + virtual OGRLayer *ICreateLayer( const char *pszName, OGRSpatialReference *poSpatialRefP = NULL, OGRwkbGeometryType eGType = wkbUnknown, char ** papszOptions = NULL ); + virtual OGRErr DeleteLayer(int); + int TestCapability( const char * ); + void SetDefaultSelafinName( const char *pszName ) { osDefaultSelafinName = pszName; } +}; + +#endif /* ndef _OGR_SELAFIN_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/ogrselafindatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/ogrselafindatasource.cpp new file mode 100644 index 000000000..dedbcbfaf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/ogrselafindatasource.cpp @@ -0,0 +1,592 @@ +/****************************************************************************** + * Project: Selafin importer + * Purpose: Implementation of OGRSelafinDataSource class. + * Author: François Hissel, francois.hissel@gmail.com + * + ****************************************************************************** + * Copyright (c) 2014, François Hissel + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_selafin.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_vsi_virtual.h" +#include "cpl_vsi.h" +#include "io_selafin.h" + +#include + +/************************************************************************/ +/* Range */ +/************************************************************************/ +Range::~Range() { + deleteList(poVals); + deleteList(poActual); +} + +void Range::deleteList(Range::List *poList) { + if (poList==0) return; + Range::List *pol=poList; + while (pol!=0) { + poList=poList->poNext; + delete pol; + pol=poList; + } +} + +void Range::setRange(const char *pszStr) { + deleteList(poVals); + deleteList(poActual); + poVals=0; + Range::List *poEnd=0; + if (pszStr==NULL || pszStr[0]!='[') { + CPLError( CE_Warning, CPLE_IllegalArg, "Invalid range specified\n"); + return; + } + const char *pszc=pszStr; + char *psze; + long nMin,nMax; + SelafinTypeDef eType; + while (*pszc!=0 && *pszc!=']') { + pszc++; + if (*pszc=='p' || *pszc=='P') { + eType=POINTS; + pszc++; + } else if (*pszc=='e' || *pszc=='E') { + eType=ELEMENTS; + pszc++; + } else eType=ALL; + if (*pszc==':') { + nMin=0; + } else { + nMin=strtol(pszc,&psze,10); + if (*psze!=':' && *psze!=',' && *psze!=']') { + CPLError( CE_Warning, CPLE_IllegalArg, "Invalid range specified\n"); + deleteList(poVals); + poVals=0; + return; + } + pszc=psze; + } + if (*pszc==':') { + ++pszc; + if (*pszc==',' || *pszc==']') { + nMax=-1; + } else { + nMax=strtol(pszc,&psze,10); + if (*psze!=',' && *psze!=']') { + CPLError( CE_Warning, CPLE_IllegalArg, "Invalid range specified\n"); + deleteList(poVals); + poVals=0; + return; + } + pszc=psze; + } + } else nMax=nMin; + Range::List *poNew; + if (eType!=ALL) poNew=new Range::List(eType,nMin,nMax,NULL); else poNew=new Range::List(POINTS,nMin,nMax,new Range::List(ELEMENTS,nMin,nMax,NULL)); + if (poVals==0) { + poVals=poNew; + poEnd=poNew; + } else { + poEnd->poNext=poNew; + poEnd=poNew; + } + if (poEnd->poNext!=NULL) poEnd=poEnd->poNext; + } + if (*pszc!=']') { + CPLError( CE_Warning, CPLE_IllegalArg, "Invalid range specified\n"); + deleteList(poVals); + poVals=0; + } +} + +bool Range::contains(SelafinTypeDef eType,long nValue) const { + if (poVals==0) return true; + Range::List *poCur=poActual; + while (poCur!=0) { + if (poCur->eType==eType && nValue>=poCur->nMin && nValue<=poCur->nMax) return true; + poCur=poCur->poNext; + } + return false; +} + +void Range::sortList(Range::List *&poList,Range::List *poEnd) { + if (poList==0 || poList==poEnd) return; + Range::List *pol=poList; + Range::List *poBefore=0; + Range::List *poBeforeEnd=0; + // poList plays the role of the pivot value. Values greater and smaller are sorted on each side of it. + // The order relation here is POINTS ranges first, then sorted by nMin value. + while (pol->poNext!=poEnd) { + if ((pol->eType==ELEMENTS && (pol->poNext->eType==POINTS || pol->poNext->nMinnMin)) || (pol->eType==POINTS && pol->poNext->eType==POINTS && pol->poNext->nMinnMin)) { + if (poBefore==0) { + poBefore=pol->poNext; + poBeforeEnd=poBefore; + } else { + poBeforeEnd->poNext=pol->poNext; + poBeforeEnd=poBeforeEnd->poNext; + } + pol->poNext=pol->poNext->poNext; + } else pol=pol->poNext; + } + if (poBefore!=0) poBeforeEnd->poNext=poList; + // Now, poList is well placed. We do the same for the sublists before and after poList + Range::sortList(poBefore,poList); + Range::sortList(poList->poNext,poEnd); + // Finally, we restore the right starting point of the list + if (poBefore!=0) poList=poBefore; +} + +void Range::setMaxValue(long nMaxValueP) { + nMaxValue=nMaxValueP; + if (poVals==0) return; + // We keep an internal private copy of the list where the range is "resolved", that is simplified to a union of disjoint intervals + deleteList(poActual); + poActual=0; + Range::List *pol=poVals; + Range::List *poActualEnd=0; + long nMinT,nMaxT; + while (pol!=0) { + if (pol->nMin<0) nMinT=pol->nMin+nMaxValue; else nMinT=pol->nMin; + if (pol->nMin<0) pol->nMin=0; + if (pol->nMin>=nMaxValue) pol->nMin=nMaxValue-1; + if (pol->nMax<0) nMaxT=pol->nMax+nMaxValue; else nMaxT=pol->nMax; + if (pol->nMax<0) pol->nMax=0; + if (pol->nMax>=nMaxValue) pol->nMax=nMaxValue-1; + if (nMaxTeType,nMinT,nMaxT,0); + poActualEnd=poActual; + } else { + poActualEnd->poNext=new Range::List(pol->eType,nMinT,nMaxT,0); + poActualEnd=poActualEnd->poNext; + } + pol=pol->poNext; + } + sortList(poActual); + // Now we merge successive ranges when they intersect or are consecutive + if (poActual!=0) { + pol=poActual; + while (pol->poNext!=0) { + if (pol->poNext->eType==pol->eType && pol->poNext->nMin<=pol->nMax+1) { + if (pol->poNext->nMax>pol->nMax) pol->nMax=pol->poNext->nMax; + poActualEnd=pol->poNext->poNext; + delete pol->poNext; + pol->poNext=poActualEnd; + } else pol=pol->poNext; + } + } +} + +size_t Range::getSize() const { + if (poVals==0) return nMaxValue*2; + Range::List *pol=poActual; + size_t nSize=0; + while (pol!=0) { + nSize+=(pol->nMax-pol->nMin+1); + pol=pol->poNext; + } + return nSize; +} + +/************************************************************************/ +/* OGRSelafinDataSource() */ +/************************************************************************/ + +OGRSelafinDataSource::OGRSelafinDataSource() { + papoLayers = NULL; + nLayers = 0; + pszName = NULL; + poHeader=0; + pszLockName=0; + poSpatialRef=0; +} + +/************************************************************************/ +/* ~OGRSelafinDataSource() */ +/************************************************************************/ + +OGRSelafinDataSource::~OGRSelafinDataSource() { + //CPLDebug("Selafin","~OGRSelafinDataSource(%s)",pszName); + for( int i = 0; i < nLayers; i++ ) delete papoLayers[i]; + CPLFree( papoLayers ); + CPLFree( pszName ); + ReleaseLock(); + delete poHeader; + if (poSpatialRef!=0) poSpatialRef->Release(); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSelafinDataSource::TestCapability( const char * pszCap ) { + if( EQUAL(pszCap,ODsCCreateLayer) ) return TRUE; + else if( EQUAL(pszCap,ODsCDeleteLayer) ) return TRUE; + else if( EQUAL(pszCap,ODsCCreateGeomFieldAfterCreateLayer) ) return FALSE; + else return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRSelafinDataSource::GetLayer( int iLayer ) { + if( iLayer < 0 || iLayer >= nLayers ) return NULL; + else return papoLayers[iLayer]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ +int OGRSelafinDataSource::Open(const char * pszFilename, int bUpdateIn, int bCreate) { + // Check if a range is set and extract it and the filename + const char *pszc=pszFilename; + if (*pszFilename==0) return FALSE; + while (*pszc) ++pszc; + if (*(pszc-1)==']') { + --pszc; + while (pszc!=pszFilename && *pszc!='[') pszc--; + if (pszc==pszFilename) return FALSE; + poRange.setRange(pszc); + } + pszName = CPLStrdup( pszFilename ); + pszName[pszc-pszFilename]=0; + bUpdate = bUpdateIn; + if (bCreate && EQUAL(pszName, "/vsistdout/")) return TRUE; + /* For writable /vsizip/, do nothing more */ + if (bCreate && strncmp(pszName, "/vsizip/", 8) == 0) return TRUE; + CPLString osFilename(pszName); + CPLString osBaseFilename = CPLGetFilename(pszName); + // Determine what sort of object this is. + VSIStatBufL sStatBuf; + if (VSIStatExL( osFilename, &sStatBuf, VSI_STAT_NATURE_FLAG ) != 0) return FALSE; + + // Is this a single Selafin file? + if (VSI_ISREG(sStatBuf.st_mode)) return OpenTable( pszName ); + + // Is this a single a ZIP file with only a Selafin file inside ? + if( strncmp(osFilename, "/vsizip/", 8) == 0 && VSI_ISREG(sStatBuf.st_mode) ) { + char** papszFiles = VSIReadDir(osFilename); + if (CSLCount(papszFiles) != 1) { + CSLDestroy(papszFiles); + return FALSE; + } + osFilename = CPLFormFilename(osFilename, papszFiles[0], NULL); + CSLDestroy(papszFiles); + return OpenTable( osFilename ); + } + +#ifdef notdef + // Otherwise it has to be a directory. + if( !VSI_ISDIR(sStatBuf.st_mode) ) return FALSE; + + // Scan through for entries which look like Selafin files + int nNotSelafinCount = 0, i; + char **papszNames = CPLReadDir( osFilename ); + for( i = 0; papszNames != NULL && papszNames[i] != NULL; i++ ) { + CPLString oSubFilename = CPLFormFilename( osFilename, papszNames[i], NULL ); + if( EQUAL(papszNames[i],".") || EQUAL(papszNames[i],"..") ) continue; + if( VSIStatL( oSubFilename, &sStatBuf ) != 0 || !VSI_ISREG(sStatBuf.st_mode) ) { + nNotSelafinCount++; + continue; + } + if( !OpenTable( oSubFilename ) ) { + CPLDebug("Selafin", "Cannot open %s", oSubFilename.c_str()); + nNotSelafinCount++; + continue; + } + } + CSLDestroy( papszNames ); + + // We presume that this is indeed intended to be a Selafin datasource if over half the files were Selafin files. + return nNotSelafinCount < nLayers; +#else + return FALSE; +#endif +} + +/************************************************************************/ +/* TakeLock() */ +/************************************************************************/ +int OGRSelafinDataSource::TakeLock(CPL_UNUSED const char *pszFilename) { +#ifdef notdef + // Ideally, we should implement a locking mechanism for Selafin layers because different layers share the same header and file on disk. If two layers are modified at the same time, + // this would most likely result in data corruption. However, locking the data source causes other problems in QGis which always tries to open the datasource twice, a first time + // in Update mode, and the second time for the layer inside in Read-only mode (because the lock is taken), and layers therefore can't be changed in QGis. + // For now, this procedure is deactivated and a warning message is issued when a datasource is opened in update mode. + //CPLDebug("Selafin","TakeLock(%s)",pszFilename); + if (pszLockName!=0) CPLFree(pszLockName); + VSILFILE *fpLock; + size_t nLen=strlen(pszFilename)+4; + pszLockName=(char*)CPLMalloc(sizeof(char)*nLen); + CPLStrlcpy(pszLockName,pszFilename,nLen-3); + CPLStrlcat(pszLockName,"~~~",nLen); + fpLock=VSIFOpenL(pszLockName,"rb+"); + // This is not thread-safe but I'm not quite sure how to open a file in exclusive mode and in a portable way + if (fpLock!=NULL) { + VSIFCloseL(fpLock); + return 0; + } + fpLock=VSIFOpenL(pszLockName,"wb"); + VSIFCloseL(fpLock); +#endif + return 1; +} + +/************************************************************************/ +/* ReleaseLock() */ +/************************************************************************/ +void OGRSelafinDataSource::ReleaseLock() { +#ifdef notdef + if (pszLockName==0) return; + VSIUnlink(pszLockName); + CPLFree(pszLockName); +#endif +} + +/************************************************************************/ +/* OpenTable() */ +/************************************************************************/ +int OGRSelafinDataSource::OpenTable(const char * pszFilename) { + //CPLDebug("Selafin","OpenTable(%s,%i)",pszFilename,bUpdate); + // Open the file + VSILFILE * fp; + if( bUpdate ) { + // We have to implement this locking feature for write access because the same file may hold several layers, and some programs (like QGIS) open each layer in a single datasource, + // so the same file might be opened several times for write access + if (TakeLock(pszFilename)==0) { + CPLError(CE_Failure,CPLE_OpenFailed,"Failed to open %s for write access, lock file found %s.",pszFilename,pszLockName); + return FALSE; + } + fp = VSIFOpenL( pszFilename, "rb+" ); + } else fp = VSIFOpenL( pszFilename, "rb" ); + if( fp == NULL ) { + CPLError( CE_Warning, CPLE_OpenFailed, "Failed to open %s, %s.", pszFilename, VSIStrerror( errno ) ); + return FALSE; + } + if( !bUpdate && strstr(pszFilename, "/vsigzip/") == NULL && strstr(pszFilename, "/vsizip/") == NULL ) fp = (VSILFILE*) VSICreateBufferedReaderHandle((VSIVirtualHandle*)fp); + + // Quickly check if the file is in Selafin format, before actually starting to read to make it faster + char szBuf[9]; + VSIFReadL(szBuf,1,4,fp); + if (szBuf[0]!=0 || szBuf[1]!=0 || szBuf[2]!=0 || szBuf[3]!=0x50) { + VSIFCloseL(fp); + return FALSE; + } + VSIFSeekL(fp,84,SEEK_SET); + VSIFReadL(szBuf,1,8,fp); + if (szBuf[0]!=0 || szBuf[1]!=0 || szBuf[2]!=0 || szBuf[3]!=0x50 || szBuf[4]!=0 || szBuf[5]!=0 || szBuf[6]!=0 || szBuf[7]!=8) { + VSIFCloseL(fp); + return FALSE; + } + /* VSIFSeekL(fp,76,SEEK_SET); + VSIFReadL(szBuf,1,8,fp); + if (STRNCASECMP(szBuf,"Seraphin",8)!=0 && STRNCASECMP(szBuf,"Serafin",7)!=0) { + VSIFCloseL(fp); + return FALSE; + } */ + + // Get layer base name + CPLString osBaseLayerName = CPLGetBasename(pszFilename); + CPLString osExt = CPLGetExtension(pszFilename); + if( strncmp(pszFilename, "/vsigzip/", 9) == 0 && EQUAL(osExt, "gz") ) { + size_t nPos=std::string::npos; + if (strlen(pszFilename)>3) nPos=osExt.find_last_of('.',strlen(pszFilename)-4); + if (nPos!=std::string::npos) { + osExt=osExt.substr(nPos+1,strlen(pszFilename)-4-nPos); + osBaseLayerName=osBaseLayerName.substr(0,nPos); + } else { + osExt=""; + } + } + + // Read header of file to get common information for all layers + poHeader=Selafin::read_header(fp,pszFilename); + if (poHeader==0) { + VSIFCloseL(fp); + CPLError( CE_Failure, CPLE_OpenFailed, "Failed to open %s, wrong format.\n", pszFilename); + return FALSE; + } + if (poHeader->nEpsg!=0) { + poSpatialRef=new OGRSpatialReference(); + if (poSpatialRef->importFromEPSG(poHeader->nEpsg)!=OGRERR_NONE) { + CPLError( CE_Warning, CPLE_AppDefined, "EPSG %li not found. Could not set datasource SRS.\n", poHeader->nEpsg); + delete poSpatialRef; + poSpatialRef=0; + } + } + + // Create two layers for each selected time step: one for points, the other for elements + int nNewLayers; + poRange.setMaxValue(poHeader->nSteps); + nNewLayers=poRange.getSize(); + if (EQUAL(pszFilename, "/vsistdin/")) osBaseLayerName = "layer"; + CPLString osLayerName; + papoLayers = (OGRSelafinLayer **) CPLRealloc(papoLayers, sizeof(void*) * (nLayers+nNewLayers)); + for (size_t j=0;j<2;++j) { + SelafinTypeDef eType=(j==0)?POINTS:ELEMENTS; + for (long i=0;inSteps;++i) { + if (poRange.contains(eType,i)) { + char szTemp[30]; + double dfTime; + if (VSIFSeekL(fp,poHeader->getPosition(i)+4,SEEK_SET)!=0 || Selafin::read_float(fp,dfTime)==0) { + VSIFCloseL(fp); + CPLError( CE_Failure, CPLE_OpenFailed, "Failed to open %s, wrong format.\n", pszFilename); + return FALSE; + } + if (poHeader->panStartDate==0) snprintf(szTemp,29,"%li",i); else { + struct tm sDate={0}; + sDate.tm_year=poHeader->panStartDate[0]-1900; + sDate.tm_mon=poHeader->panStartDate[1]-1; + sDate.tm_mday=poHeader->panStartDate[2]; + sDate.tm_hour=poHeader->panStartDate[3]; + sDate.tm_min=poHeader->panStartDate[4]; + sDate.tm_sec=poHeader->panStartDate[5]+(int)dfTime; + mktime(&sDate); + strftime(szTemp,29,"%Y_%m_%d_%H_%M_%S",&sDate); + } + if (eType==POINTS) osLayerName=osBaseLayerName+"_p"+szTemp; else osLayerName=osBaseLayerName+"_e"+szTemp; + papoLayers[nLayers++] = new OGRSelafinLayer( osLayerName, bUpdate, poSpatialRef, poHeader,i,eType); + //poHeader->nRefCount++; + } + } + } + + // Free allocated variables and exit + return TRUE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer *OGRSelafinDataSource::ICreateLayer( const char *pszLayerName, OGRSpatialReference *poSpatialRefP, OGRwkbGeometryType eGType, char ** papszOptions ) { + CPLDebug("Selafin","CreateLayer(%s,%s)",pszLayerName,(eGType==wkbPoint)?"wkbPoint":"wkbPolygon"); + // Verify we are in update mode. + if (!bUpdate) { + CPLError( CE_Failure, CPLE_NoWriteAccess, "Data source %s opened read-only.\n" "New layer %s cannot be created.\n", pszName, pszLayerName ); + return NULL; + } + // Check that new layer is a point or polygon layer + if (eGType!=wkbPoint) { + CPLError( CE_Failure, CPLE_NoWriteAccess, "Selafin format can only handle %s layers whereas input is %s\n.", OGRGeometryTypeToName(wkbPoint),OGRGeometryTypeToName(eGType)); + return NULL; + } + // Parse options + double dfDate; + const char *pszTemp=CSLFetchNameValue(papszOptions,"DATE"); + if (pszTemp!=0) dfDate=CPLAtof(pszTemp); else dfDate=0.0; + // Set the SRS of the datasource if this is the first layer + if (nLayers==0 && poSpatialRefP!=0) { + poSpatialRef=poSpatialRefP; + poSpatialRef->Reference(); + const char* szEpsg=poSpatialRef->GetAttrValue("GEOGCS|AUTHORITY",1); + long nEpsg=0; + if (szEpsg!=0) nEpsg=strtol(szEpsg,0,10); + if (nEpsg==0) { + CPLError(CE_Warning,CPLE_AppDefined,"Could not find EPSG code for SRS. The SRS won't be saved in the datasource."); + } else { + poHeader->nEpsg=nEpsg; + } + } + // Create the new layer in the Selafin file by adding a "time step" at the end + // Beware, as the new layer shares the same header, it automatically contains the same number of features and fields as the existing ones. This may not be intuitive for the user. + if (VSIFSeekL(poHeader->fp,0,SEEK_END)!=0) return NULL; + if (Selafin::write_integer(poHeader->fp,4)==0 || + Selafin::write_float(poHeader->fp,dfDate)==0 || + Selafin::write_integer(poHeader->fp,4)==0) { + CPLError( CE_Failure, CPLE_FileIO, "Could not write to Selafin file %s.\n",pszName); + return NULL; + } + double *pdfValues=0; + if (poHeader->nPoints>0) pdfValues=(double*)VSIMalloc2(sizeof(double),poHeader->nPoints); + for (long i=0;inVar;++i) { + if (Selafin::write_floatarray(poHeader->fp,pdfValues,poHeader->nPoints)==0) { + CPLError( CE_Failure, CPLE_FileIO, "Could not write to Selafin file %s.\n",pszName); + CPLFree(pdfValues); + return NULL; + } + } + CPLFree(pdfValues); + VSIFFlushL(poHeader->fp); + poHeader->nSteps++; + // Create two layers as usual, one for points and one for elements + nLayers+=2; + papoLayers = (OGRSelafinLayer **) CPLRealloc(papoLayers, sizeof(void*) * nLayers); + CPLString szName=pszLayerName; + CPLString szNewLayerName=szName+"_p"; + papoLayers[nLayers-2] = new OGRSelafinLayer( szNewLayerName, bUpdate, poSpatialRef, poHeader,poHeader->nSteps-1,POINTS); + szNewLayerName=szName+"_e"; + papoLayers[nLayers-1] = new OGRSelafinLayer( szNewLayerName, bUpdate, poSpatialRef, poHeader,poHeader->nSteps-1,ELEMENTS); + return papoLayers[nLayers-2]; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ +OGRErr OGRSelafinDataSource::DeleteLayer( int iLayer ) { + // Verify we are in update mode. + if( !bUpdate ) { + CPLError( CE_Failure, CPLE_NoWriteAccess, "Data source %s opened read-only.\n" "Layer %d cannot be deleted.\n", pszName, iLayer ); + return OGRERR_FAILURE; + } + if( iLayer < 0 || iLayer >= nLayers ) { + CPLError( CE_Failure, CPLE_AppDefined, "Layer %d not in legal range of 0 to %d.", iLayer, nLayers-1 ); + return OGRERR_FAILURE; + } + // Delete layer in file. Here we don't need to create a copy of the file because we only update values and it can't get corrupted even if the system crashes during the operation + long nNum=papoLayers[iLayer]->GetStepNumber(); + double dfTime; + double *dfValues=0; + long nTemp; + for (long i=nNum;inSteps-1;++i) { + if (VSIFSeekL(poHeader->fp,poHeader->getPosition(i+1)+4,SEEK_SET)!=0 || + Selafin::read_float(poHeader->fp,dfTime)==0 || + VSIFSeekL(poHeader->fp,poHeader->getPosition(i)+4,SEEK_SET)!=0 || + Selafin::write_float(poHeader->fp,dfTime)==0) { + CPLError( CE_Failure, CPLE_FileIO, "Could not update Selafin file %s.\n",pszName); + return OGRERR_FAILURE; + } + for (long j=0;jnVar;++j) { + if (VSIFSeekL(poHeader->fp,poHeader->getPosition(i+1)+12,SEEK_SET)!=0 || + (nTemp=Selafin::read_floatarray(poHeader->fp,&dfValues)) !=poHeader->nPoints || + VSIFSeekL(poHeader->fp,poHeader->getPosition(i)+12,SEEK_SET)!=0 || + Selafin::write_floatarray(poHeader->fp,dfValues,poHeader->nPoints)==0) { + CPLError( CE_Failure, CPLE_FileIO, "Could not update Selafin file %s.\n",pszName); + CPLFree(dfValues); + return OGRERR_FAILURE; + } + CPLFree(dfValues); + } + } + // Delete all layers with the same step number in layer list. Usually there are two of them: one for points and one for elements, but we can't rely on that because of possible layer filtering specifications + for (long i=0;iGetStepNumber()==nNum) { + delete papoLayers[i]; + nLayers--; + for (long j=i;j + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_selafin.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "io_selafin.h" + +/************************************************************************/ +/* OGRSelafinDriverIdentify() */ +/************************************************************************/ + +static int OGRSelafinDriverIdentify( GDALOpenInfo* poOpenInfo ) { + + if( poOpenInfo->fpL != NULL ) + { + if( poOpenInfo->nHeaderBytes < 84 + 8 ) + return FALSE; + if (poOpenInfo->pabyHeader[0]!=0 || poOpenInfo->pabyHeader[1]!=0 || + poOpenInfo->pabyHeader[2]!=0 || poOpenInfo->pabyHeader[3]!=0x50) + return FALSE; + + if (poOpenInfo->pabyHeader[84+0]!=0 || poOpenInfo->pabyHeader[84+1]!=0 || + poOpenInfo->pabyHeader[84+2]!=0 || poOpenInfo->pabyHeader[84+3]!=0x50 || + poOpenInfo->pabyHeader[84+4]!=0 || poOpenInfo->pabyHeader[84+5]!=0 || + poOpenInfo->pabyHeader[84+6]!=0 || poOpenInfo->pabyHeader[84+7]!=8) + return FALSE; + + return TRUE; + } + return -1; +} + +/************************************************************************/ +/* OGRSelafinDriverOpen() */ +/************************************************************************/ + +static GDALDataset *OGRSelafinDriverOpen( GDALOpenInfo* poOpenInfo ) { + + if( OGRSelafinDriverIdentify(poOpenInfo) == 0 ) + return NULL; + + OGRSelafinDataSource *poDS = new OGRSelafinDataSource(); + if( !poDS->Open(poOpenInfo->pszFilename, poOpenInfo->eAccess == GA_Update, FALSE) ) { + delete poDS; + poDS = NULL; + } + return poDS; +} + +/************************************************************************/ +/* OGRSelafinDriverCreate() */ +/************************************************************************/ + +static GDALDataset *OGRSelafinDriverCreate( const char * pszName, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED int nBands, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) { + // First, ensure there isn't any such file yet. + VSIStatBufL sStatBuf; + if (strcmp(pszName, "/dev/stdout") == 0) pszName = "/vsistdout/"; + if( VSIStatL( pszName, &sStatBuf ) == 0 ) { + CPLError(CE_Failure, CPLE_AppDefined,"It seems a file system object called '%s' already exists.",pszName); + return NULL; + } + // Parse options + const char *pszTemp=CSLFetchNameValue(papszOptions,"TITLE"); + char pszTitle[81]; + long pnDate[6]={-1,0}; + if (pszTemp!=0) strncpy(pszTitle,pszTemp,72); else memset(pszTitle,' ',72); + pszTemp=CSLFetchNameValue(papszOptions,"DATE"); + if (pszTemp!=0) { + const char* pszErrorMessage="Wrong format for date parameter: must be \"%%Y-%%m-%%d_%%H:%%M:%%S\", ignored"; + const char *pszc=pszTemp; + pnDate[0]=atoi(pszTemp); + if (pnDate[0]<=0) CPLError(CE_Warning, CPLE_AppDefined,"%s",pszErrorMessage); else { + if (pnDate[0]<100) pnDate[0]+=2000; + } + while (*pszc!=0 && *pszc!='-') ++pszc; + pnDate[1]=atoi(pszc); + if (pnDate[1]<0 || pnDate[1]>12) CPLError(CE_Warning, CPLE_AppDefined,"%s",pszErrorMessage); + while (*pszc!=0 && *pszc!='_') ++pszc; + pnDate[2]=atoi(pszc); + if (pnDate[2]<0 || pnDate[2]>59) CPLError(CE_Warning, CPLE_AppDefined,"%s",pszErrorMessage); + while (*pszc!=0 && *pszc!='_') ++pszc; + pnDate[3]=atoi(pszc); + if (pnDate[3]<0 || pnDate[3]>23) CPLError(CE_Warning, CPLE_AppDefined,"%s",pszErrorMessage); + while (*pszc!=0 && *pszc!=':') ++pszc; + pnDate[4]=atoi(pszc); + if (pnDate[4]<0 || pnDate[4]>59) CPLError(CE_Warning, CPLE_AppDefined,"%s",pszErrorMessage); + while (*pszc!=0 && *pszc!=':') ++pszc; + pnDate[5]=atoi(pszc); + if (pnDate[5]<0 || pnDate[5]>59) CPLError(CE_Warning, CPLE_AppDefined,"%s",pszErrorMessage); + } + // Create the skeleton of a Selafin file + VSILFILE *fp=VSIFOpenL(pszName,"wb"); + if (fp==NULL) { + CPLError(CE_Failure, CPLE_AppDefined,"Unable to open %s with write access.",pszName); + return NULL; + } + strncpy(pszTitle+72,"SERAPHIN",9); + bool bError=false; + if (Selafin::write_string(fp,pszTitle,80)==0) bError=true; + long pnTemp[10]={0}; + if (Selafin::write_intarray(fp,pnTemp,2)==0) bError=true; + if (pnDate[0]>=0) pnTemp[9]=1; + if (Selafin::write_intarray(fp,pnTemp,10)==0) bError=true; + if (pnDate[0]>=0) { + if (Selafin::write_intarray(fp,pnTemp,6)==0) bError=true; + } + pnTemp[3]=1; + if (Selafin::write_intarray(fp,pnTemp,4)==0) bError=true; + if (Selafin::write_intarray(fp,pnTemp,0)==0) bError=true; + if (Selafin::write_intarray(fp,pnTemp,0)==0) bError=true; + if (Selafin::write_floatarray(fp,0,0)==0) bError=true; + if (Selafin::write_floatarray(fp,0,0)==0) bError=true; + VSIFCloseL(fp); + if (bError) { + CPLError(CE_Failure, CPLE_AppDefined,"Error writing to file %s.",pszName); + return NULL; + } + // Force it to open as a datasource + OGRSelafinDataSource *poDS = new OGRSelafinDataSource(); + if( !poDS->Open( pszName, TRUE, TRUE ) ) { + delete poDS; + return NULL; + } + return poDS; +} + +/************************************************************************/ +/* OGRSelafinDriverDelete() */ +/************************************************************************/ +static CPLErr OGRSelafinDriverDelete( const char *pszFilename ) { + if( CPLUnlinkTree( pszFilename ) == 0 ) return CE_None; + else return CE_Failure; +} + +/************************************************************************/ +/* RegisterOGRSelafin() */ +/************************************************************************/ +void RegisterOGRSelafin() { + GDALDriver *poDriver; + + if( GDALGetDriverByName( "Selafin" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "Selafin" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "Selafin" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Selafin" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_selafin.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +" "); + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRSelafinDriverOpen; + poDriver->pfnIdentify = OGRSelafinDriverIdentify; + poDriver->pfnCreate = OGRSelafinDriverCreate; + poDriver->pfnDelete = OGRSelafinDriverDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/ogrselafinlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/ogrselafinlayer.cpp new file mode 100644 index 000000000..0423daa2c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/selafin/ogrselafinlayer.cpp @@ -0,0 +1,719 @@ +/****************************************************************************** + * Project: Selafin importer + * Purpose: Implementation of OGRSelafinLayer class. + * Author: François Hissel, francois.hissel@gmail.com + * + ****************************************************************************** + * Copyright (c) 2014, François Hissel + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "io_selafin.h" +#include "ogr_selafin.h" +#include "cpl_error.h" +#include "cpl_quad_tree.h" + +/************************************************************************/ +/* Utilities functions */ +/************************************************************************/ +void MoveOverwrite(VSILFILE *fpDest,VSILFILE *fpSource) { + VSIRewindL(fpSource); + VSIRewindL(fpDest); + VSIFTruncateL(fpDest,0); + char anBuf[0x10000]; + while (!VSIFEofL(fpSource)) { + size_t nSize=VSIFReadL(anBuf,1,0x10000,fpSource); + size_t nLeft=nSize; + while (nLeft>0) nLeft-=VSIFWriteL(anBuf+nSize-nLeft,1,nLeft,fpDest); + } + VSIFCloseL(fpSource); + VSIFFlushL(fpDest); +} + +/************************************************************************/ +/* OGRSelafinLayer() */ +/* Note that no operation on OGRSelafinLayer is thread-safe */ +/************************************************************************/ + +OGRSelafinLayer::OGRSelafinLayer( const char *pszLayerNameP, int bUpdateP,OGRSpatialReference *poSpatialRefP,Selafin::Header *poHeaderP,int nStepNumberP,SelafinTypeDef eTypeP):eType(eTypeP),bUpdate(bUpdateP),nStepNumber(nStepNumberP),poHeader(poHeaderP),poSpatialRef(poSpatialRefP),nCurrentId(-1) { + //CPLDebug("Selafin","Opening layer %s",pszLayerNameP); + poFeatureDefn = new OGRFeatureDefn( CPLGetBasename( pszLayerNameP ) ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + if (eType==POINTS) poFeatureDefn->SetGeomType( wkbPoint ); + else poFeatureDefn->SetGeomType(wkbPolygon); + for (int i=0;inVar;++i) { + OGRFieldDefn oFieldDefn(poHeader->papszVariables[i],OFTReal); + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } +} + + +/************************************************************************/ +/* ~OGRSelafinLayer() */ +/************************************************************************/ +OGRSelafinLayer::~OGRSelafinLayer() { + //CPLDebug("Selafin","Closing layer %s",GetName()); + poFeatureDefn->Release(); + //poHeader->nRefCount--; + //if (poHeader->nRefCount==0) delete poHeader; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ +OGRFeature *OGRSelafinLayer::GetNextFeature() { + //CPLDebug("Selafin","GetNextFeature(%li)",nCurrentId+1); + while (true) { + OGRFeature *poFeature=GetFeature(++nCurrentId); + if (poFeature==NULL) return NULL; + if( (m_poFilterGeom == NULL || FilterGeometry( poFeature->GetGeometryRef() ) ) && (m_poAttrQuery == NULL || m_poAttrQuery->Evaluate( poFeature )) ) return poFeature; + delete poFeature; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ +void OGRSelafinLayer::ResetReading() { + //CPLDebug("Selafin","ResetReading()"); + nCurrentId=-1; +} + +/************************************************************************/ +/* SetNextByIndex() */ +/************************************************************************/ +OGRErr OGRSelafinLayer::SetNextByIndex(GIntBig nIndex) { + //CPLDebug("Selafin","SetNexByIndex(%li)",nIndex); + if (nIndex<0 || nIndex>=poHeader->nPoints) return OGRERR_FAILURE; + nCurrentId=nIndex-1; + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ +int OGRSelafinLayer::TestCapability(const char *pszCap) { + //CPLDebug("Selafin","TestCapability(%s)",pszCap); + if (EQUAL(pszCap,OLCRandomRead)) return TRUE; + if (EQUAL(pszCap,OLCSequentialWrite)) return (bUpdate); + if (EQUAL(pszCap,OLCRandomWrite)) return (bUpdate); + if (EQUAL(pszCap,OLCFastSpatialFilter)) return FALSE; + if (EQUAL(pszCap,OLCFastFeatureCount)) return TRUE; + if (EQUAL(pszCap,OLCFastGetExtent)) return TRUE; + if (EQUAL(pszCap,OLCFastSetNextByIndex)) return TRUE; + if (EQUAL(pszCap,OLCCreateField)) return (bUpdate); + if (EQUAL(pszCap,OLCCreateGeomField)) return FALSE; + if (EQUAL(pszCap,OLCDeleteField)) return (bUpdate); + if (EQUAL(pszCap,OLCReorderFields)) return (bUpdate); + if (EQUAL(pszCap,OLCAlterFieldDefn)) return (bUpdate); + if (EQUAL(pszCap,OLCDeleteFeature)) return (bUpdate); + if (EQUAL(pszCap,OLCStringsAsUTF8)) return FALSE; + if (EQUAL(pszCap,OLCTransactions)) return FALSE; + if (EQUAL(pszCap,OLCIgnoreFields)) return FALSE; + return FALSE; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ +OGRFeature* OGRSelafinLayer::GetFeature(GIntBig nFID) { + CPLDebug("Selafin","GetFeature(" CPL_FRMT_GIB ")",nFID); + if (nFID<0) return NULL; + if (eType==POINTS) { + if (nFID>=poHeader->nPoints) return NULL; + double nData; + OGRFeature *poFeature=new OGRFeature(poFeatureDefn); + poFeature->SetGeometryDirectly(new OGRPoint(poHeader->paadfCoords[0][nFID],poHeader->paadfCoords[1][nFID])); + poFeature->SetFID(nFID); + for (int i=0;inVar;++i) { + VSIFSeekL(poHeader->fp,poHeader->getPosition(nStepNumber,(long)nFID,i),SEEK_SET); + if (Selafin::read_float(poHeader->fp,nData)==1) poFeature->SetField(i,nData); + } + return poFeature; + } else { + if (nFID>=poHeader->nElements) return NULL; + double *anData; + anData=(double*)VSIMalloc2(sizeof(double),poHeader->nVar); + if (poHeader->nVar>0 && anData==0) return NULL; + for (long i=0;inVar;++i) anData[i]=0; + double nData; + OGRFeature *poFeature=new OGRFeature(poFeatureDefn); + poFeature->SetFID(nFID); + OGRPolygon *poPolygon=new OGRPolygon(); + OGRLinearRing *poLinearRing=new OGRLinearRing(); + for (long j=0;jnPointsPerElement;++j) { + long nPointNum=poHeader->panConnectivity[nFID*poHeader->nPointsPerElement+j]-1; + poLinearRing->addPoint(poHeader->paadfCoords[0][nPointNum],poHeader->paadfCoords[1][nPointNum]); + for (long i=0;inVar;++i) { + VSIFSeekL(poHeader->fp,poHeader->getPosition(nStepNumber,nPointNum,i),SEEK_SET); + if (Selafin::read_float(poHeader->fp,nData)==1) anData[i]+=nData; + } + } + poPolygon->addRingDirectly(poLinearRing); + poPolygon->closeRings(); + poFeature->SetGeometryDirectly(poPolygon); + for (long i=0;inVar;++i) poFeature->SetField(i,anData[i]/poHeader->nPointsPerElement); + CPLFree(anData); + return poFeature; + } +} + + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ +GIntBig OGRSelafinLayer::GetFeatureCount(int bForce) { + //CPLDebug("Selafin","GetFeatureCount(%i)",bForce); + if (m_poFilterGeom==NULL && m_poAttrQuery==NULL) return (eType==POINTS)?poHeader->nPoints:poHeader->nElements; + if (bForce==FALSE) return -1; + long i=0; + int nFeatureCount=0; + long nMax=(eType==POINTS)?poHeader->nPoints:poHeader->nElements; + while (iGetGeometryRef() ) ) && (m_poAttrQuery == NULL || m_poAttrQuery->Evaluate( poFeature )) ) ++nFeatureCount; + delete poFeature; + } + return nFeatureCount; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ +OGRErr OGRSelafinLayer::GetExtent(OGREnvelope *psExtent, + CPL_UNUSED int bForce) { + //CPLDebug("Selafin","GetExtent(%i)",bForce); + if (poHeader->nPoints==0) return OGRERR_NONE; + CPLRectObj *poObj=poHeader->getBoundingBox(); + psExtent->MinX=poObj->minx; + psExtent->MaxX=poObj->maxx; + psExtent->MinY=poObj->miny; + psExtent->MaxY=poObj->maxy; + delete poObj; + return OGRERR_NONE; +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ +OGRErr OGRSelafinLayer::ISetFeature(OGRFeature *poFeature) { + OGRGeometry *poGeom=poFeature->GetGeometryRef(); + if (poGeom==0) return OGRERR_FAILURE; + if (eType==POINTS) { + // If it's a point layer, it's the "easy" case: we change the coordinates and attributes of the feature and update the file + if (poGeom->getGeometryType()!=wkbPoint) { + CPLError( CE_Failure, CPLE_AppDefined, "The new feature should be of the same Point geometry as the existing ones in the layer."); + return OGRERR_FAILURE; + } + OGRPoint *poPoint=(OGRPoint*)poGeom; + GIntBig nFID=poFeature->GetFID(); + poHeader->paadfCoords[0][nFID]=poPoint->getX(); + poHeader->paadfCoords[1][nFID]=poPoint->getY(); + CPLDebug("Selafin","SetFeature(" CPL_FRMT_GIB ",%f,%f)",nFID,poHeader->paadfCoords[0][nFID],poHeader->paadfCoords[1][nFID]); + if (VSIFSeekL(poHeader->fp,88+16+40*poHeader->nVar+48+((poHeader->panStartDate!=0)?32:0)+24+(poHeader->nElements*poHeader->nPointsPerElement+2)*4+(poHeader->nPoints+2)*4+4+nFID*4,SEEK_SET)!=0) return OGRERR_FAILURE; + CPLDebug("Selafin","Write_float(" CPL_FRMT_GUIB ",%f)",VSIFTellL(poHeader->fp),poHeader->paadfCoords[0][nFID]-poHeader->adfOrigin[0]); + if (Selafin::write_float(poHeader->fp,poHeader->paadfCoords[0][nFID]-poHeader->adfOrigin[0])==0) return OGRERR_FAILURE; + if (VSIFSeekL(poHeader->fp,88+16+40*poHeader->nVar+48+((poHeader->panStartDate!=0)?32:0)+24+(poHeader->nElements*poHeader->nPointsPerElement+2)*4+(poHeader->nPoints+2)*4+(poHeader->nPoints+2)*4+4+nFID*4,SEEK_SET)!=0) return OGRERR_FAILURE; + CPLDebug("Selafin","Write_float(" CPL_FRMT_GUIB ",%f)",VSIFTellL(poHeader->fp),poHeader->paadfCoords[1][nFID]-poHeader->adfOrigin[1]); + if (Selafin::write_float(poHeader->fp,poHeader->paadfCoords[1][nFID]-poHeader->adfOrigin[1])==0) return OGRERR_FAILURE; + for (long i=0;inVar;++i) { + double nData=poFeature->GetFieldAsDouble(i); + if (VSIFSeekL(poHeader->fp,poHeader->getPosition(nStepNumber,(long)nFID,i),SEEK_SET)!=0) return OGRERR_FAILURE; + if (Selafin::write_float(poHeader->fp,nData)==0) return OGRERR_FAILURE; + } + } else { + // Else, we have a layer of polygonal elements. Here we consider that the vertices are moved when we change the geometry (which will also lead to a modification in the corresponding point layer). The attributes table can't be changed, because attributes are calculated from those of the vertices. + // First we check that the new feature is a polygon with the right number of vertices + if (poGeom->getGeometryType()!=wkbPolygon) { + CPLError( CE_Failure, CPLE_AppDefined, "The new feature should be of the same Polygon geometry as the existing ones in the layer."); + return OGRERR_FAILURE; + } + OGRLinearRing *poLinearRing=((OGRPolygon*)poGeom)->getExteriorRing(); + if (poLinearRing->getNumPoints()!=poHeader->nPointsPerElement+1) { + CPLError( CE_Failure, CPLE_AppDefined, "The new feature should have the same number of vertices %li as the existing ones in the layer.",poHeader->nPointsPerElement); + return OGRERR_FAILURE; + } + CPLError(CE_Warning,CPLE_AppDefined,"The attributes of elements layer in Selafin files can't be updated."); + CPLDebug("Selafin","SetFeature(" CPL_FRMT_GIB ",%f,%f,%f,%f,%f,%f)",poFeature->GetFID(),poLinearRing->getX(0),poLinearRing->getY(0),poLinearRing->getX(1),poLinearRing->getY(1),poLinearRing->getX(2),poLinearRing->getY(2)); //!< This is not safe as we can't be sure there are at least three vertices in the linear ring, but we can assume that for a debug mode + long nFID=poFeature->GetFID(); + // Now we change the coordinates of points in the layer based on the vertices of the new polygon. We don't look at the order of points and we assume that it is the same as in the original layer. + for (long i=0;inPointsPerElement;++i) { + long nPointId=poHeader->panConnectivity[nFID*poHeader->nPointsPerElement+i]-1; + poHeader->paadfCoords[0][nPointId]=poLinearRing->getX(i); + poHeader->paadfCoords[1][nPointId]=poLinearRing->getY(i); + if (VSIFSeekL(poHeader->fp,88+16+40*poHeader->nVar+48+((poHeader->panStartDate!=0)?32:0)+24+(poHeader->nElements*poHeader->nPointsPerElement+2)*4+(poHeader->nPoints+2)*4+4+nPointId*4,SEEK_SET)!=0) return OGRERR_FAILURE; + CPLDebug("Selafin","Write_float(" CPL_FRMT_GUIB ",%f)",VSIFTellL(poHeader->fp),poHeader->paadfCoords[0][nPointId]-poHeader->adfOrigin[0]); + if (Selafin::write_float(poHeader->fp,poHeader->paadfCoords[0][nPointId]-poHeader->adfOrigin[0])==0) return OGRERR_FAILURE; + if (VSIFSeekL(poHeader->fp,88+16+40*poHeader->nVar+48+((poHeader->panStartDate!=0)?32:0)+24+(poHeader->nElements*poHeader->nPointsPerElement+2)*4+(poHeader->nPoints+2)*4+(poHeader->nPoints+2)*4+4+nPointId*4,SEEK_SET)!=0) return OGRERR_FAILURE; + CPLDebug("Selafin","Write_float(" CPL_FRMT_GUIB ",%f)",VSIFTellL(poHeader->fp),poHeader->paadfCoords[1][nPointId]-poHeader->adfOrigin[1]); + if (Selafin::write_float(poHeader->fp,poHeader->paadfCoords[1][nPointId]-poHeader->adfOrigin[1])==0) return OGRERR_FAILURE; + } + } + VSIFFlushL(poHeader->fp); + return OGRERR_NONE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ +OGRErr OGRSelafinLayer::ICreateFeature(OGRFeature *poFeature) { + OGRGeometry *poGeom=poFeature->GetGeometryRef(); + if (poGeom==0) return OGRERR_FAILURE; + if (VSIFSeekL(poHeader->fp,poHeader->getPosition(0),SEEK_SET)!=0) return OGRERR_FAILURE; + if (eType==POINTS) { + // If it's a point layer, it's the "easy" case: we add a new point feature and update the file + if (poGeom->getGeometryType()!=wkbPoint) { + CPLError( CE_Failure, CPLE_AppDefined, "The new feature should be of the same Point geometry as the existing ones in the layer."); + return OGRERR_FAILURE; + } + OGRPoint *poPoint=(OGRPoint*)poGeom; + poFeature->SetFID(poHeader->nPoints); + CPLDebug("Selafin","CreateFeature(%li,%f,%f)",poHeader->nPoints,poPoint->getX(),poPoint->getY()); + // Change the header to add the new feature + poHeader->addPoint(poPoint->getX(),poPoint->getY()); + } else { + // This is the most difficult case. The user wants to add a polygon element. First we check that it has the same number of vertices as the other polygon elements in the file. If there is no other element, then we define the number of vertices. + // Every vertex in the layer should have a corresponding point in the corresponding point layer. So if we add a polygon element, we also have to add points in the corresponding layer. + // The function tries to add as few new points as possible, reusing already existing points. This is generally what the user will expect. + + // First we check that we have the required geometry + if (poGeom->getGeometryType()!=wkbPolygon) { + CPLError( CE_Failure, CPLE_AppDefined, "The new feature should be of the same Polygon geometry as the existing ones in the layer."); + return OGRERR_FAILURE; + } + + // Now we check that we have the right number of vertices, or if this number was not defined yet (0), we define it at once + OGRLinearRing *poLinearRing=((OGRPolygon*)poGeom)->getExteriorRing(); + poFeature->SetFID(poHeader->nElements); + CPLDebug("Selafin","CreateFeature(" CPL_FRMT_GIB ",%f,%f,%f,%f,%f,%f)",poFeature->GetFID(),poLinearRing->getX(0),poLinearRing->getY(0),poLinearRing->getX(1),poLinearRing->getY(1),poLinearRing->getX(2),poLinearRing->getY(2)); //!< This is not safe as we can't be sure there are at least three vertices in the linear ring, but we can assume that for a debug mode + int nNum=poLinearRing->getNumPoints(); + if (poHeader->nPointsPerElement==0) { + if (nNum<4) { + CPLError( CE_Failure, CPLE_AppDefined, "The new feature should have at least 3 vertices."); + return OGRERR_FAILURE; + } + poHeader->nPointsPerElement=nNum-1; + if (poHeader->nElements>0) { + poHeader->panConnectivity=(long*)CPLRealloc(poHeader->panConnectivity,poHeader->nElements*poHeader->nPointsPerElement); + if (poHeader->panConnectivity==0) return OGRERR_FAILURE; + } + } else { + if (poLinearRing->getNumPoints()!=poHeader->nPointsPerElement+1) { + CPLError( CE_Failure, CPLE_AppDefined, "The new feature should have the same number of vertices %li as the existing ones in the layer.",poHeader->nPointsPerElement); + return OGRERR_FAILURE; + } + } + + // Now we look for vertices that are already referenced as points in the file + int *anMap; + anMap=(int*)VSIMalloc2(sizeof(int),poHeader->nPointsPerElement); + if (anMap==0) { + CPLError(CE_Failure,CPLE_AppDefined,"%s","Not enough memory for operation"); + return OGRERR_FAILURE; + } + for (long i=0;inPointsPerElement;++i) anMap[i]=-1; + if (poHeader->nPoints>0) { + CPLRectObj *poBB=poHeader->getBoundingBox(); + double dfMaxDist=(poBB->maxx-poBB->minx)/sqrt((double)(poHeader->nPoints))/1000.0; //!< Heuristic approach to estimate a maximum distance such that two points are considered equal if they are closer from each other + dfMaxDist*=dfMaxDist; + delete poBB; + for (long i=0;inPointsPerElement;++i) anMap[i]=poHeader->getClosestPoint(poLinearRing->getX(i),poLinearRing->getY(i),dfMaxDist); + } + + // We add new points if needed only + for (long i=0;inPointsPerElement;++i) if (anMap[i]==-1) { + poHeader->addPoint(poLinearRing->getX(i),poLinearRing->getY(i)); + anMap[i]=poHeader->nPoints-1; + } + + // And we update the connectivity table to add the new element + poHeader->nElements++; + poHeader->panConnectivity=(long*)CPLRealloc(poHeader->panConnectivity,sizeof(long)*poHeader->nPointsPerElement*poHeader->nElements); + for (long i=0;inPointsPerElement;++i) { + poHeader->panConnectivity[poHeader->nPointsPerElement*(poHeader->nElements-1)+i]=anMap[i]+1; + } + poHeader->setUpdated(); + CPLFree(anMap); + + } + + // Now comes the real insertion. Since values have to be inserted nearly everywhere in the file and we don't want to store everything in memory to overwrite it, we create a new copy of it where we write the new values + VSILFILE *fpNew; + const char *pszTempfile=CPLGenerateTempFilename(0); + fpNew=VSIFOpenL(pszTempfile,"wb+"); + if( fpNew == NULL ) { + CPLError( CE_Failure, CPLE_OpenFailed, "Failed to open temporary file %s with write access, %s.",pszTempfile, VSIStrerror( errno ) ); + return OGRERR_FAILURE; + } + if (Selafin::write_header(fpNew,poHeader)==0) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + long nLen; + double dfDate; + double *padfValues; + for (long i=0;inSteps;++i) { + if (Selafin::read_integer(poHeader->fp,nLen,true)==0 || + Selafin::read_float(poHeader->fp,dfDate)==0 || + Selafin::read_integer(poHeader->fp,nLen,true)==0 || + Selafin::write_integer(fpNew,4)==0 || + Selafin::write_float(fpNew,dfDate)==0 || + Selafin::write_integer(fpNew,4)==0) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + for (long j=0;jnVar;++j) { + if (Selafin::read_floatarray(poHeader->fp,&padfValues)==-1) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + padfValues=(double*)CPLRealloc(padfValues,sizeof(double)*poHeader->nPoints); + if (padfValues==NULL) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + if (eType==POINTS) padfValues[poHeader->nPoints-1]=poFeature->GetFieldAsDouble(j); + else padfValues[poHeader->nPoints-1]=0; + if (Selafin::write_floatarray(fpNew,padfValues,poHeader->nPoints)==0) { + CPLFree(padfValues); + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + CPLFree(padfValues); + } + } + + // If everything went fine, we overwrite the new file with the content of the old one. This way, even if something goes bad, we can still recover the layer. The copy process is format-agnostic. + MoveOverwrite(poHeader->fp,fpNew); + VSIUnlink(pszTempfile); + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ +OGRErr OGRSelafinLayer::CreateField(OGRFieldDefn *poField, + CPL_UNUSED int bApproxOK) { + CPLDebug("Selafin","CreateField(%s,%s)",poField->GetNameRef(),OGRFieldDefn::GetFieldTypeName(poField->GetType())); + // Test if the field does not exist yet + if (poFeatureDefn->GetFieldIndex(poField->GetNameRef())!=-1) { + // Those two lines are copied from CSV driver, but I am not quite sure what they actually do + if (poFeatureDefn->GetGeomFieldIndex(poField->GetNameRef())!=-1) return OGRERR_NONE; + if (poFeatureDefn->GetGeomFieldIndex(CPLSPrintf("geom_%s",poField->GetNameRef()))!=-1) return OGRERR_NONE; + CPLError(CE_Failure,CPLE_AppDefined,"Attempt to create field %s, but a field with this name already exists.",poField->GetNameRef()); + return OGRERR_FAILURE; + } + // Test if the field type is legal (only double precision values are allowed) + if (poField->GetType()!=OFTReal) { + CPLError(CE_Failure,CPLE_AppDefined,"Attempt to create field of type %s, but this is not supported for Selafin files (only double precision fields are allowed).",poField->GetFieldTypeName(poField->GetType())); + return OGRERR_FAILURE; + } + if (VSIFSeekL(poHeader->fp,poHeader->getPosition(0),SEEK_SET)!=0) return OGRERR_FAILURE; + // Change the header to add the new field + poHeader->nVar++; + poHeader->setUpdated(); + poHeader->papszVariables=(char**)CPLRealloc(poHeader->papszVariables,sizeof(char*)*poHeader->nVar); + poHeader->papszVariables[poHeader->nVar-1]=(char*)VSIMalloc2(sizeof(char),33); + strncpy(poHeader->papszVariables[poHeader->nVar-1],poField->GetNameRef(),32); + poHeader->papszVariables[poHeader->nVar-1][32]=0; + poFeatureDefn->AddFieldDefn(poField); + + // Now comes the real insertion. Since values have to be inserted nearly everywhere in the file and we don't want to store everything in memory to overwrite it, we create a new copy of it where we write the new values + VSILFILE *fpNew; + const char *pszTempfile=CPLGenerateTempFilename(0); + fpNew=VSIFOpenL(pszTempfile,"wb+"); + if( fpNew == NULL ) { + CPLError( CE_Failure, CPLE_OpenFailed, "Failed to open temporary file %s with write access, %s.",pszTempfile, VSIStrerror( errno ) ); + return OGRERR_FAILURE; + } + if (Selafin::write_header(fpNew,poHeader)==0) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + long nLen; + double dfDate; + double *padfValues; + for (long i=0;inSteps;++i) { + if (Selafin::read_integer(poHeader->fp,nLen,true)==0 || + Selafin::read_float(poHeader->fp,dfDate)==0 || + Selafin::read_integer(poHeader->fp,nLen,true)==0 || + Selafin::write_integer(fpNew,4)==0 || + Selafin::write_float(fpNew,dfDate)==0 || + Selafin::write_integer(fpNew,4)==0) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + for (long j=0;jnVar-1;++j) { + if (Selafin::read_floatarray(poHeader->fp,&padfValues)==-1) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + if (Selafin::write_floatarray(fpNew,padfValues,poHeader->nPoints)==0) { + CPLFree(padfValues); + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + CPLFree(padfValues); + } + padfValues=(double*)VSIMalloc2(sizeof(double),poHeader->nPoints); + for (long k=0;knPoints;++k) padfValues[k]=0; + if (Selafin::write_floatarray(fpNew,padfValues,poHeader->nPoints)==0) { + CPLFree(padfValues); + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + CPLFree(padfValues); + } + MoveOverwrite(poHeader->fp,fpNew); + VSIUnlink(pszTempfile); + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteField() */ +/************************************************************************/ +OGRErr OGRSelafinLayer::DeleteField(int iField) { + CPLDebug("Selafin","DeleteField(%i)",iField); + if (VSIFSeekL(poHeader->fp,poHeader->getPosition(0),SEEK_SET)!=0) return OGRERR_FAILURE; + // Change the header to remove the field + poHeader->nVar--; + poHeader->setUpdated(); + CPLFree(poHeader->papszVariables[iField]); + for (long i=iField;inVar;++i) poHeader->papszVariables[i]=poHeader->papszVariables[i+1]; + poHeader->papszVariables=(char**)CPLRealloc(poHeader->papszVariables,sizeof(char*)*poHeader->nVar); + poFeatureDefn->DeleteFieldDefn(iField); + + // Now comes the real deletion. Since values have to be deleted nearly everywhere in the file and we don't want to store everything in memory to overwrite it, we create a new copy of it where we write the new values + VSILFILE *fpNew; + const char *pszTempfile=CPLGenerateTempFilename(0); + fpNew=VSIFOpenL(pszTempfile,"wb+"); + if( fpNew == NULL ) { + CPLError( CE_Failure, CPLE_OpenFailed, "Failed to open temporary file %s with write access, %s.",pszTempfile, VSIStrerror( errno ) ); + return OGRERR_FAILURE; + } + if (Selafin::write_header(fpNew,poHeader)==0) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + long nLen; + double dfDate; + double *padfValues; + for (long i=0;inSteps;++i) { + if (Selafin::read_integer(poHeader->fp,nLen,true)==0 || + Selafin::read_float(poHeader->fp,dfDate)==0 || + Selafin::read_integer(poHeader->fp,nLen,true)==0 || + Selafin::write_integer(fpNew,4)==0 || + Selafin::write_float(fpNew,dfDate)==0 || + Selafin::write_integer(fpNew,4)==0) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + for (long j=0;jnVar;++j) { + if (Selafin::read_floatarray(poHeader->fp,&padfValues)==-1) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + if (j!=iField) { + if (Selafin::write_floatarray(fpNew,padfValues,poHeader->nPoints)==0) { + CPLFree(padfValues); + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + } + CPLFree(padfValues); + } + } + MoveOverwrite(poHeader->fp,fpNew); + VSIUnlink(pszTempfile); + return OGRERR_NONE; +} + +/************************************************************************/ +/* ReorderFields() */ +/************************************************************************/ +OGRErr OGRSelafinLayer::ReorderFields(int *panMap) { + CPLDebug("Selafin","ReorderFields()"); + if (VSIFSeekL(poHeader->fp,poHeader->getPosition(0),SEEK_SET)!=0) return OGRERR_FAILURE; + // Change the header according to the map + char **papszNew=(char**)VSIMalloc2(sizeof(char*),poHeader->nVar); + for (long i=0;inVar;++i) papszNew[i]=poHeader->papszVariables[panMap[i]]; + CPLFree(poHeader->papszVariables); + poHeader->papszVariables=papszNew; + poFeatureDefn->ReorderFieldDefns(panMap); + + // Now comes the real change. + VSILFILE *fpNew; + const char *pszTempfile=CPLGenerateTempFilename(0); + fpNew=VSIFOpenL(pszTempfile,"wb+"); + if( fpNew == NULL ) { + CPLError( CE_Failure, CPLE_OpenFailed, "Failed to open temporary file %s with write access, %s.",pszTempfile, VSIStrerror( errno ) ); + return OGRERR_FAILURE; + } + if (Selafin::write_header(fpNew,poHeader)==0) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + long nLen; + double dfDate; + double *padfValues=0; + for (long i=0;inSteps;++i) { + if (Selafin::read_integer(poHeader->fp,nLen,true)==0 || + Selafin::read_float(poHeader->fp,dfDate)==0 || + Selafin::read_integer(poHeader->fp,nLen,true)==0 || + Selafin::write_integer(fpNew,4)==0 || + Selafin::write_float(fpNew,dfDate)==0 || + Selafin::write_integer(fpNew,4)==0) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + for (long j=0;jnVar;++j) { + if (VSIFSeekL(poHeader->fp,poHeader->getPosition(i,-1,panMap[j]),SEEK_SET)!=0 || Selafin::read_floatarray(poHeader->fp,&padfValues)==-1) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + if (Selafin::write_floatarray(fpNew,padfValues,poHeader->nPoints)==0) { + CPLFree(padfValues); + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + CPLFree(padfValues); + } + } + MoveOverwrite(poHeader->fp,fpNew); + VSIUnlink(pszTempfile); + return OGRERR_NONE; +} + +/************************************************************************/ +/* AlterFieldDefn() */ +/************************************************************************/ +OGRErr OGRSelafinLayer::AlterFieldDefn(int iField, + OGRFieldDefn *poNewFieldDefn, + CPL_UNUSED int nFlags) { + CPLDebug("Selafin","AlterFieldDefn(%i,%s,%s)",iField,poNewFieldDefn->GetNameRef(),OGRFieldDefn::GetFieldTypeName(poNewFieldDefn->GetType())); + // Test if the field type is legal (only double precision values are allowed) + if (poNewFieldDefn->GetType()!=OFTReal) { + CPLError(CE_Failure,CPLE_AppDefined,"Attempt to update field with type %s, but this is not supported for Selafin files (only double precision fields are allowed).",poNewFieldDefn->GetFieldTypeName(poNewFieldDefn->GetType())); + return OGRERR_FAILURE; + } + // Since the field type can't change, only the field name is changed. We change it in the header + CPLFree(poHeader->papszVariables[iField]); + poHeader->papszVariables[iField]=(char*)VSIMalloc2(sizeof(char),33); + strncpy(poHeader->papszVariables[iField],poNewFieldDefn->GetNameRef(),32); + poHeader->papszVariables[iField][32]=0; + // And we update the file + if (VSIFSeekL(poHeader->fp,88+16+40*iField,SEEK_SET)!=0) return OGRERR_FAILURE; + if (Selafin::write_string(poHeader->fp,poHeader->papszVariables[iField],32)==0) return OGRERR_FAILURE; + VSIFFlushL(poHeader->fp); + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ +OGRErr OGRSelafinLayer::DeleteFeature(GIntBig nFID) { + CPLDebug("Selafin","DeleteFeature(" CPL_FRMT_GIB ")",nFID); + if (VSIFSeekL(poHeader->fp,poHeader->getPosition(0),SEEK_SET)!=0) return OGRERR_FAILURE; + // Change the header to delete the feature + if (eType==POINTS) poHeader->removePoint((long)nFID); else { + // For elements layer, we only delete the element and not the vertices + poHeader->nElements--; + for (long i=(long)nFID;inElements;++i) + for (long j=0;jnPointsPerElement;++j) + poHeader->panConnectivity[poHeader->nPointsPerElement*i+j]=poHeader->panConnectivity[poHeader->nPointsPerElement*(i+1)+j]; + poHeader->panConnectivity=(long*)CPLRealloc(poHeader->panConnectivity,sizeof(long)*poHeader->nPointsPerElement*poHeader->nElements); + poHeader->setUpdated(); + } + + // Now we perform the deletion by creating a new temporary layer + VSILFILE *fpNew; + const char *pszTempfile=CPLGenerateTempFilename(0); + fpNew=VSIFOpenL(pszTempfile,"wb+"); + if( fpNew == NULL ) { + CPLError( CE_Failure, CPLE_OpenFailed, "Failed to open temporary file %s with write access, %s.",pszTempfile, VSIStrerror( errno ) ); + return OGRERR_FAILURE; + } + if (Selafin::write_header(fpNew,poHeader)==0) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + long nLen; + double dfDate; + double *padfValues; + for (long i=0;inSteps;++i) { + if (Selafin::read_integer(poHeader->fp,nLen,true)==0 || + Selafin::read_float(poHeader->fp,dfDate)==0 || + Selafin::read_integer(poHeader->fp,nLen,true)==0 || + Selafin::write_integer(fpNew,4)==0 || + Selafin::write_float(fpNew,dfDate)==0 || + Selafin::write_integer(fpNew,4)==0) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + for (long j=0;jnVar;++j) { + if (Selafin::read_floatarray(poHeader->fp,&padfValues)==-1) { + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + if (eType==POINTS) { + for (long k=(long)nFID;k<=poHeader->nPoints;++k) padfValues[k-1]=padfValues[k]; + } + if (Selafin::write_floatarray(fpNew,padfValues,poHeader->nPoints)==0) { + CPLFree(padfValues); + VSIFCloseL(fpNew); + VSIUnlink(pszTempfile); + return OGRERR_FAILURE; + } + CPLFree(padfValues); + } + } + + // If everything went fine, we overwrite the new file with the content of the old one. This way, even if something goes bad, we can still recover the layer. The copy process is format-agnostic. + MoveOverwrite(poHeader->fp,fpNew); + VSIUnlink(pszTempfile); + return OGRERR_NONE; + +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/GNUmakefile new file mode 100644 index 000000000..b1a865091 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/GNUmakefile @@ -0,0 +1,30 @@ + + +include ../../../GDALmake.opt + +OBJ = shape2ogr.o shpopen.o dbfopen.o shptree.o sbnsearch.o shp_vsi.o \ + ogrshapedriver.o ogrshapedatasource.o ogrshapelayer.o + +CPPFLAGS := -DSAOffset=vsi_l_offset -DUSE_CPL \ + -I.. -I../.. -I../generic $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +$(OBJ) $(O_OBJ): ogrshape.h shapefil.h + +clean: + rm -f *.o $(O_OBJ) + +import: + @if test ! -d ~/shapelib ; then \ + echo reimport requires shapelib checked out as ~/shapelib ; \ + exit 1; \ + fi + + copymatch.sh ~/shapelib *.c *.h + + @echo + @echo 'Now do something like:' + @echo '% svn commit -m "updated to shapelib CVS"' + @echo + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/LICENSE.LGPL b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/LICENSE.LGPL new file mode 100644 index 000000000..0b643ac83 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/LICENSE.LGPL @@ -0,0 +1,483 @@ + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/dbfopen.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/dbfopen.c new file mode 100644 index 000000000..95aa57afd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/dbfopen.c @@ -0,0 +1,2244 @@ +/****************************************************************************** + * $Id: dbfopen.c,v 1.89 2011-07-24 05:59:25 fwarmerdam Exp $ + * + * Project: Shapelib + * Purpose: Implementation of .dbf access API documented in dbf_api.html. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2012-2013, Even Rouault + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log: dbfopen.c,v $ + * Revision 1.89 2011-07-24 05:59:25 fwarmerdam + * minimize use of CPLError in favor of SAHooks.Error() + * + * Revision 1.88 2011-05-13 17:35:17 fwarmerdam + * added DBFReorderFields() and DBFAlterFields() functions (from Even) + * + * Revision 1.87 2011-05-07 22:41:02 fwarmerdam + * ensure pending record is flushed when adding a native field (GDAL #4073) + * + * Revision 1.86 2011-04-17 15:15:29 fwarmerdam + * Removed unused variable. + * + * Revision 1.85 2010-12-06 16:09:34 fwarmerdam + * fix buffer read overrun fetching code page (bug 2276) + * + * Revision 1.84 2009-10-29 19:59:48 fwarmerdam + * avoid crash on truncated header (gdal #3093) + * + * Revision 1.83 2008/11/12 14:28:15 fwarmerdam + * DBFCreateField() now works on files with records + * + * Revision 1.82 2008/11/11 17:47:09 fwarmerdam + * added DBFDeleteField() function + * + * Revision 1.81 2008/01/03 17:48:13 bram + * in DBFCreate, use default code page LDID/87 (= 0x57, ANSI) + * instead of LDID/3. This seems to be the same as what ESRI + * would be doing by default. + * + * Revision 1.80 2007/12/30 14:36:39 fwarmerdam + * avoid syntax issue with last comment. + * + * Revision 1.79 2007/12/30 14:35:48 fwarmerdam + * Avoid char* / unsigned char* warnings. + * + * Revision 1.78 2007/12/18 18:28:07 bram + * - create hook for client specific atof (bugzilla ticket 1615) + * - check for NULL handle before closing cpCPG file, and close after reading. + * + * Revision 1.77 2007/12/15 20:25:21 bram + * dbfopen.c now reads the Code Page information from the DBF file, and exports + * this information as a string through the DBFGetCodePage function. This is + * either the number from the LDID header field ("LDID/") or as the + * content of an accompanying .CPG file. When creating a DBF file, the code can + * be set using DBFCreateEx. + * + * Revision 1.76 2007/12/12 22:21:32 bram + * DBFClose: check for NULL psDBF handle before trying to close it. + * + * Revision 1.75 2007/12/06 13:58:19 fwarmerdam + * make sure file offset calculations are done in as SAOffset + * + * Revision 1.74 2007/12/06 07:00:25 fwarmerdam + * dbfopen now using SAHooks for fileio + * + * Revision 1.73 2007/09/03 19:48:11 fwarmerdam + * move DBFReadAttribute() static dDoubleField into dbfinfo + * + * Revision 1.72 2007/09/03 19:34:06 fwarmerdam + * Avoid use of static tuple buffer in DBFReadTuple() + * + * Revision 1.71 2006/06/22 14:37:18 fwarmerdam + * avoid memory leak if dbfopen fread fails + * + * Revision 1.70 2006/06/17 17:47:05 fwarmerdam + * use calloc() for dbfinfo in DBFCreate + * + * Revision 1.69 2006/06/17 15:34:32 fwarmerdam + * disallow creating fields wider than 255 + * + * Revision 1.68 2006/06/17 15:12:40 fwarmerdam + * Fixed C++ style comments. + * + * Revision 1.67 2006/06/17 00:24:53 fwarmerdam + * Don't treat non-zero decimals values as high order byte for length + * for strings. It causes serious corruption for some files. + * http://bugzilla.remotesensing.org/show_bug.cgi?id=1202 + * + * Revision 1.66 2006/03/29 18:26:20 fwarmerdam + * fixed bug with size of pachfieldtype in dbfcloneempty + * + * Revision 1.65 2006/02/15 01:14:30 fwarmerdam + * added DBFAddNativeFieldType + * + * Revision 1.64 2006/02/09 00:29:04 fwarmerdam + * Changed to put spaces into string fields that are NULL as + * per http://bugzilla.maptools.org/show_bug.cgi?id=316. + * + * Revision 1.63 2006/01/25 15:35:43 fwarmerdam + * check success on DBFFlushRecord + * + * Revision 1.62 2006/01/10 16:28:03 fwarmerdam + * Fixed typo in CPLError. + * + * Revision 1.61 2006/01/10 16:26:29 fwarmerdam + * Push loading record buffer into DBFLoadRecord. + * Implement CPL error reporting if USE_CPL defined. + * + * Revision 1.60 2006/01/05 01:27:27 fwarmerdam + * added dbf deletion mark/fetch + * + * Revision 1.59 2005/03/14 15:20:28 fwarmerdam + * Fixed last change. + * + * Revision 1.58 2005/03/14 15:18:54 fwarmerdam + * Treat very wide fields with no decimals as double. This is + * more than 32bit integer fields. + * + * Revision 1.57 2005/02/10 20:16:54 fwarmerdam + * Make the pszStringField buffer for DBFReadAttribute() static char [256] + * as per bug 306. + * + * Revision 1.56 2005/02/10 20:07:56 fwarmerdam + * Fixed bug 305 in DBFCloneEmpty() - header length problem. + * + * Revision 1.55 2004/09/26 20:23:46 fwarmerdam + * avoid warnings with rcsid and signed/unsigned stuff + * + * Revision 1.54 2004/09/15 16:26:10 fwarmerdam + * Treat all blank numeric fields as null too. + */ + +#include "shapefil.h" + +#include +#include +#include +#include + +#ifdef USE_CPL +#include "cpl_string.h" +#else +#define CPLsprintf sprintf +#endif + +SHP_CVSID("$Id: dbfopen.c,v 1.89 2011-07-24 05:59:25 fwarmerdam Exp $") + +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif + +/************************************************************************/ +/* SfRealloc() */ +/* */ +/* A realloc cover function that will access a NULL pointer as */ +/* a valid input. */ +/************************************************************************/ + +static void * SfRealloc( void * pMem, int nNewSize ) + +{ + if( pMem == NULL ) + return( (void *) malloc(nNewSize) ); + else + return( (void *) realloc(pMem,nNewSize) ); +} + +/************************************************************************/ +/* DBFWriteHeader() */ +/* */ +/* This is called to write out the file header, and field */ +/* descriptions before writing any actual data records. This */ +/* also computes all the DBFDataSet field offset/size/decimals */ +/* and so forth values. */ +/************************************************************************/ + +static void DBFWriteHeader(DBFHandle psDBF) + +{ + unsigned char abyHeader[XBASE_FLDHDR_SZ]; + int i; + + if( !psDBF->bNoHeader ) + return; + + psDBF->bNoHeader = FALSE; + +/* -------------------------------------------------------------------- */ +/* Initialize the file header information. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < XBASE_FLDHDR_SZ; i++ ) + abyHeader[i] = 0; + + abyHeader[0] = 0x03; /* memo field? - just copying */ + + /* write out update date */ + abyHeader[1] = (unsigned char) psDBF->nUpdateYearSince1900; + abyHeader[2] = (unsigned char) psDBF->nUpdateMonth; + abyHeader[3] = (unsigned char) psDBF->nUpdateDay; + + /* record count preset at zero */ + + abyHeader[8] = (unsigned char) (psDBF->nHeaderLength % 256); + abyHeader[9] = (unsigned char) (psDBF->nHeaderLength / 256); + + abyHeader[10] = (unsigned char) (psDBF->nRecordLength % 256); + abyHeader[11] = (unsigned char) (psDBF->nRecordLength / 256); + + abyHeader[29] = (unsigned char) (psDBF->iLanguageDriver); + +/* -------------------------------------------------------------------- */ +/* Write the initial 32 byte file header, and all the field */ +/* descriptions. */ +/* -------------------------------------------------------------------- */ + psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); + psDBF->sHooks.FWrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp ); + psDBF->sHooks.FWrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields, + psDBF->fp ); + +/* -------------------------------------------------------------------- */ +/* Write out the newline character if there is room for it. */ +/* -------------------------------------------------------------------- */ + if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 ) + { + char cNewline; + + cNewline = 0x0d; + psDBF->sHooks.FWrite( &cNewline, 1, 1, psDBF->fp ); + } +} + +/************************************************************************/ +/* DBFFlushRecord() */ +/* */ +/* Write out the current record if there is one. */ +/************************************************************************/ + +static int DBFFlushRecord( DBFHandle psDBF ) + +{ + SAOffset nRecordOffset; + + if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 ) + { + psDBF->bCurrentRecordModified = FALSE; + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) psDBF->nCurrentRecord + + psDBF->nHeaderLength; + + if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ) != 0 + || psDBF->sHooks.FWrite( psDBF->pszCurrentRecord, + psDBF->nRecordLength, + 1, psDBF->fp ) != 1 ) + { + char szMessage[128]; + sprintf( szMessage, "Failure writing DBF record %d.", + psDBF->nCurrentRecord ); + psDBF->sHooks.Error( szMessage ); + return FALSE; + } + } + + return TRUE; +} + +/************************************************************************/ +/* DBFLoadRecord() */ +/************************************************************************/ + +static int DBFLoadRecord( DBFHandle psDBF, int iRecord ) + +{ + if( psDBF->nCurrentRecord != iRecord ) + { + SAOffset nRecordOffset; + + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, SEEK_SET ) != 0 ) + { + char szMessage[128]; + sprintf( szMessage, "fseek(%ld) failed on DBF file.\n", + (long) nRecordOffset ); + psDBF->sHooks.Error( szMessage ); + return FALSE; + } + + if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord, + psDBF->nRecordLength, 1, psDBF->fp ) != 1 ) + { + char szMessage[128]; + sprintf( szMessage, "fread(%d) failed on DBF file.\n", + psDBF->nRecordLength ); + psDBF->sHooks.Error( szMessage ); + return FALSE; + } + + psDBF->nCurrentRecord = iRecord; + } + + return TRUE; +} + +/************************************************************************/ +/* DBFUpdateHeader() */ +/************************************************************************/ + +void SHPAPI_CALL +DBFUpdateHeader( DBFHandle psDBF ) + +{ + unsigned char abyFileHeader[32]; + + if( psDBF->bNoHeader ) + DBFWriteHeader( psDBF ); + + DBFFlushRecord( psDBF ); + + psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); + psDBF->sHooks.FRead( abyFileHeader, 32, 1, psDBF->fp ); + + abyFileHeader[1] = (unsigned char) psDBF->nUpdateYearSince1900; + abyFileHeader[2] = (unsigned char) psDBF->nUpdateMonth; + abyFileHeader[3] = (unsigned char) psDBF->nUpdateDay; + abyFileHeader[4] = (unsigned char) (psDBF->nRecords % 256); + abyFileHeader[5] = (unsigned char) ((psDBF->nRecords/256) % 256); + abyFileHeader[6] = (unsigned char) ((psDBF->nRecords/(256*256)) % 256); + abyFileHeader[7] = (unsigned char) ((psDBF->nRecords/(256*256*256)) % 256); + + psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); + psDBF->sHooks.FWrite( abyFileHeader, 32, 1, psDBF->fp ); + + psDBF->sHooks.FFlush( psDBF->fp ); +} + +/************************************************************************/ +/* DBFSetLastModifiedDate() */ +/************************************************************************/ + +void SHPAPI_CALL +DBFSetLastModifiedDate( DBFHandle psDBF, int nYYSince1900, int nMM, int nDD ) +{ + psDBF->nUpdateYearSince1900 = nYYSince1900; + psDBF->nUpdateMonth = nMM; + psDBF->nUpdateDay = nDD; +} + +/************************************************************************/ +/* DBFOpen() */ +/* */ +/* Open a .dbf file. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFOpen( const char * pszFilename, const char * pszAccess ) + +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return DBFOpenLL( pszFilename, pszAccess, &sHooks ); +} + +/************************************************************************/ +/* DBFOpen() */ +/* */ +/* Open a .dbf file. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) + +{ + DBFHandle psDBF; + SAFile pfCPG; + unsigned char *pabyBuf; + int nFields, nHeadLen, iField, i; + char *pszBasename, *pszFullname; + int nBufSize = 500; + +/* -------------------------------------------------------------------- */ +/* We only allow the access strings "rb" and "r+". */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0 + && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0 + && strcmp(pszAccess,"r+b") != 0 ) + return( NULL ); + + if( strcmp(pszAccess,"r") == 0 ) + pszAccess = "rb"; + + if( strcmp(pszAccess,"r+") == 0 ) + pszAccess = "rb+"; + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszFilename)+5); + strcpy( pszBasename, pszFilename ); + for( i = strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + + pszFullname = (char *) malloc(strlen(pszBasename) + 5); + sprintf( pszFullname, "%s.dbf", pszBasename ); + + psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) ); + psDBF->fp = psHooks->FOpen( pszFullname, pszAccess ); + memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) ); + + if( psDBF->fp == NULL ) + { + sprintf( pszFullname, "%s.DBF", pszBasename ); + psDBF->fp = psDBF->sHooks.FOpen(pszFullname, pszAccess ); + } + + sprintf( pszFullname, "%s.cpg", pszBasename ); + pfCPG = psHooks->FOpen( pszFullname, "r" ); + if( pfCPG == NULL ) + { + sprintf( pszFullname, "%s.CPG", pszBasename ); + pfCPG = psHooks->FOpen( pszFullname, "r" ); + } + + free( pszBasename ); + free( pszFullname ); + + if( psDBF->fp == NULL ) + { + free( psDBF ); + if( pfCPG ) psHooks->FClose( pfCPG ); + return( NULL ); + } + + psDBF->bNoHeader = FALSE; + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + +/* -------------------------------------------------------------------- */ +/* Read Table Header info */ +/* -------------------------------------------------------------------- */ + pabyBuf = (unsigned char *) malloc(nBufSize); + if( psDBF->sHooks.FRead( pabyBuf, 32, 1, psDBF->fp ) != 1 ) + { + psDBF->sHooks.FClose( psDBF->fp ); + if( pfCPG ) psDBF->sHooks.FClose( pfCPG ); + free( pabyBuf ); + free( psDBF ); + return NULL; + } + + DBFSetLastModifiedDate(psDBF, pabyBuf[1], pabyBuf[2], pabyBuf[3]); + + psDBF->nRecords = + pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256; + + psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256; + psDBF->nRecordLength = pabyBuf[10] + pabyBuf[11]*256; + psDBF->iLanguageDriver = pabyBuf[29]; + + if (nHeadLen < 32) + { + psDBF->sHooks.FClose( psDBF->fp ); + if( pfCPG ) psDBF->sHooks.FClose( pfCPG ); + free( pabyBuf ); + free( psDBF ); + return NULL; + } + + psDBF->nFields = nFields = (nHeadLen - 32) / 32; + + psDBF->pszCurrentRecord = (char *) malloc(psDBF->nRecordLength); + +/* -------------------------------------------------------------------- */ +/* Figure out the code page from the LDID and CPG */ +/* -------------------------------------------------------------------- */ + + psDBF->pszCodePage = NULL; + if( pfCPG ) + { + size_t n; + memset( pabyBuf, 0, nBufSize); + psDBF->sHooks.FRead( pabyBuf, nBufSize - 1, 1, pfCPG ); + n = strcspn( (char *) pabyBuf, "\n\r" ); + if( n > 0 ) + { + pabyBuf[n] = '\0'; + psDBF->pszCodePage = (char *) malloc(n + 1); + memcpy( psDBF->pszCodePage, pabyBuf, n + 1 ); + } + psDBF->sHooks.FClose( pfCPG ); + } + if( psDBF->pszCodePage == NULL && pabyBuf[29] != 0 ) + { + sprintf( (char *) pabyBuf, "LDID/%d", psDBF->iLanguageDriver ); + psDBF->pszCodePage = (char *) malloc(strlen((char*)pabyBuf) + 1); + strcpy( psDBF->pszCodePage, (char *) pabyBuf ); + } + +/* -------------------------------------------------------------------- */ +/* Read in Field Definitions */ +/* -------------------------------------------------------------------- */ + + pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen); + psDBF->pszHeader = (char *) pabyBuf; + + psDBF->sHooks.FSeek( psDBF->fp, 32, 0 ); + if( psDBF->sHooks.FRead( pabyBuf, nHeadLen-32, 1, psDBF->fp ) != 1 ) + { + psDBF->sHooks.FClose( psDBF->fp ); + free( pabyBuf ); + free( psDBF->pszCurrentRecord ); + free( psDBF ); + return NULL; + } + + psDBF->panFieldOffset = (int *) malloc(sizeof(int) * nFields); + psDBF->panFieldSize = (int *) malloc(sizeof(int) * nFields); + psDBF->panFieldDecimals = (int *) malloc(sizeof(int) * nFields); + psDBF->pachFieldType = (char *) malloc(sizeof(char) * nFields); + + for( iField = 0; iField < nFields; iField++ ) + { + unsigned char *pabyFInfo; + + pabyFInfo = pabyBuf+iField*32; + + if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' ) + { + psDBF->panFieldSize[iField] = pabyFInfo[16]; + psDBF->panFieldDecimals[iField] = pabyFInfo[17]; + } + else + { + psDBF->panFieldSize[iField] = pabyFInfo[16]; + psDBF->panFieldDecimals[iField] = 0; + +/* +** The following seemed to be used sometimes to handle files with long +** string fields, but in other cases (such as bug 1202) the decimals field +** just seems to indicate some sort of preferred formatting, not very +** wide fields. So I have disabled this code. FrankW. + psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17]*256; + psDBF->panFieldDecimals[iField] = 0; +*/ + } + + psDBF->pachFieldType[iField] = (char) pabyFInfo[11]; + if( iField == 0 ) + psDBF->panFieldOffset[iField] = 1; + else + psDBF->panFieldOffset[iField] = + psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1]; + } + + return( psDBF ); +} + +/************************************************************************/ +/* DBFClose() */ +/************************************************************************/ + +void SHPAPI_CALL +DBFClose(DBFHandle psDBF) +{ + if( psDBF == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Write out header if not already written. */ +/* -------------------------------------------------------------------- */ + if( psDBF->bNoHeader ) + DBFWriteHeader( psDBF ); + + DBFFlushRecord( psDBF ); + +/* -------------------------------------------------------------------- */ +/* Update last access date, and number of records if we have */ +/* write access. */ +/* -------------------------------------------------------------------- */ + if( psDBF->bUpdated ) + DBFUpdateHeader( psDBF ); + +/* -------------------------------------------------------------------- */ +/* Close, and free resources. */ +/* -------------------------------------------------------------------- */ + psDBF->sHooks.FClose( psDBF->fp ); + + if( psDBF->panFieldOffset != NULL ) + { + free( psDBF->panFieldOffset ); + free( psDBF->panFieldSize ); + free( psDBF->panFieldDecimals ); + free( psDBF->pachFieldType ); + } + + if( psDBF->pszWorkField != NULL ) + free( psDBF->pszWorkField ); + + free( psDBF->pszHeader ); + free( psDBF->pszCurrentRecord ); + free( psDBF->pszCodePage ); + + free( psDBF ); +} + +/************************************************************************/ +/* DBFCreate() */ +/* */ +/* Create a new .dbf file with default code page LDID/87 (0x57) */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFCreate( const char * pszFilename ) + +{ + return DBFCreateEx( pszFilename, "LDID/87" ); // 0x57 +} + +/************************************************************************/ +/* DBFCreateEx() */ +/* */ +/* Create a new .dbf file. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFCreateEx( const char * pszFilename, const char* pszCodePage ) + +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return DBFCreateLL( pszFilename, pszCodePage , &sHooks ); +} + +/************************************************************************/ +/* DBFCreate() */ +/* */ +/* Create a new .dbf file. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHooks ) + +{ + DBFHandle psDBF; + SAFile fp; + char *pszFullname, *pszBasename; + int i, ldid = -1; + char chZero = '\0'; + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszFilename)+5); + strcpy( pszBasename, pszFilename ); + for( i = strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + + pszFullname = (char *) malloc(strlen(pszBasename) + 5); + sprintf( pszFullname, "%s.dbf", pszBasename ); + +/* -------------------------------------------------------------------- */ +/* Create the file. */ +/* -------------------------------------------------------------------- */ + fp = psHooks->FOpen( pszFullname, "wb" ); + if( fp == NULL ) + { + free( pszBasename ); + free( pszFullname ); + return( NULL ); + } + + psHooks->FWrite( &chZero, 1, 1, fp ); + psHooks->FClose( fp ); + + fp = psHooks->FOpen( pszFullname, "rb+" ); + if( fp == NULL ) + { + free( pszBasename ); + free( pszFullname ); + return( NULL ); + } + + sprintf( pszFullname, "%s.cpg", pszBasename ); + if( pszCodePage != NULL ) + { + if( strncmp( pszCodePage, "LDID/", 5 ) == 0 ) + { + ldid = atoi( pszCodePage + 5 ); + if( ldid > 255 ) + ldid = -1; // don't use 0 to indicate out of range as LDID/0 is a valid one + } + if( ldid < 0 ) + { + SAFile fpCPG = psHooks->FOpen( pszFullname, "w" ); + psHooks->FWrite( (char*) pszCodePage, strlen(pszCodePage), 1, fpCPG ); + psHooks->FClose( fpCPG ); + } + } + if( pszCodePage == NULL || ldid >= 0 ) + { + psHooks->Remove( pszFullname ); + } + + free( pszBasename ); + free( pszFullname ); + +/* -------------------------------------------------------------------- */ +/* Create the info structure. */ +/* -------------------------------------------------------------------- */ + psDBF = (DBFHandle) calloc(1,sizeof(DBFInfo)); + + memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) ); + psDBF->fp = fp; + psDBF->nRecords = 0; + psDBF->nFields = 0; + psDBF->nRecordLength = 1; + psDBF->nHeaderLength = 33; + + psDBF->panFieldOffset = NULL; + psDBF->panFieldSize = NULL; + psDBF->panFieldDecimals = NULL; + psDBF->pachFieldType = NULL; + psDBF->pszHeader = NULL; + + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + psDBF->pszCurrentRecord = NULL; + + psDBF->bNoHeader = TRUE; + + psDBF->iLanguageDriver = ldid > 0 ? ldid : 0; + psDBF->pszCodePage = NULL; + if( pszCodePage ) + { + psDBF->pszCodePage = (char * ) malloc( strlen(pszCodePage) + 1 ); + strcpy( psDBF->pszCodePage, pszCodePage ); + } + DBFSetLastModifiedDate(psDBF, 95, 7, 26); /* dummy date */ + + return( psDBF ); +} + +/************************************************************************/ +/* DBFAddField() */ +/* */ +/* Add a field to a newly created .dbf or to an existing one */ +/************************************************************************/ + +int SHPAPI_CALL +DBFAddField(DBFHandle psDBF, const char * pszFieldName, + DBFFieldType eType, int nWidth, int nDecimals ) + +{ + char chNativeType = 'C'; + + if( eType == FTLogical ) + chNativeType = 'L'; + else if( eType == FTString ) + chNativeType = 'C'; + else + chNativeType = 'N'; + + return DBFAddNativeFieldType( psDBF, pszFieldName, chNativeType, + nWidth, nDecimals ); +} + +/************************************************************************/ +/* DBFGetNullCharacter() */ +/************************************************************************/ + +static char DBFGetNullCharacter(char chType) +{ + switch (chType) + { + case 'N': + case 'F': + return '*'; + case 'D': + return '0'; + case 'L': + return '?'; + default: + return ' '; + } +} + +/************************************************************************/ +/* DBFAddField() */ +/* */ +/* Add a field to a newly created .dbf file before any records */ +/* are written. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, + char chType, int nWidth, int nDecimals ) + +{ + char *pszFInfo; + int i; + int nOldRecordLength, nOldHeaderLength; + char *pszRecord; + char chFieldFill; + SAOffset nRecordOffset; + + /* make sure that everything is written in .dbf */ + if( !DBFFlushRecord( psDBF ) ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Do some checking to ensure we can add records to this file. */ +/* -------------------------------------------------------------------- */ + if( nWidth < 1 ) + return -1; + + if( nWidth > 255 ) + nWidth = 255; + + nOldRecordLength = psDBF->nRecordLength; + nOldHeaderLength = psDBF->nHeaderLength; + +/* -------------------------------------------------------------------- */ +/* SfRealloc all the arrays larger to hold the additional field */ +/* information. */ +/* -------------------------------------------------------------------- */ + psDBF->nFields++; + + psDBF->panFieldOffset = (int *) + SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldSize = (int *) + SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldDecimals = (int *) + SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); + + psDBF->pachFieldType = (char *) + SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); + +/* -------------------------------------------------------------------- */ +/* Assign the new field information fields. */ +/* -------------------------------------------------------------------- */ + psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength; + psDBF->nRecordLength += nWidth; + psDBF->panFieldSize[psDBF->nFields-1] = nWidth; + psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals; + psDBF->pachFieldType[psDBF->nFields-1] = chType; + +/* -------------------------------------------------------------------- */ +/* Extend the required header information. */ +/* -------------------------------------------------------------------- */ + psDBF->nHeaderLength += 32; + psDBF->bUpdated = FALSE; + + psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); + + pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1); + + for( i = 0; i < 32; i++ ) + pszFInfo[i] = '\0'; + + if( (int) strlen(pszFieldName) < 10 ) + strncpy( pszFInfo, pszFieldName, strlen(pszFieldName)); + else + strncpy( pszFInfo, pszFieldName, 10); + + pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1]; + + if( chType == 'C' ) + { + pszFInfo[16] = (unsigned char) (nWidth % 256); + pszFInfo[17] = (unsigned char) (nWidth / 256); + } + else + { + pszFInfo[16] = (unsigned char) nWidth; + pszFInfo[17] = (unsigned char) nDecimals; + } + +/* -------------------------------------------------------------------- */ +/* Make the current record buffer appropriately larger. */ +/* -------------------------------------------------------------------- */ + psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, + psDBF->nRecordLength); + + /* we're done if dealing with new .dbf */ + if( psDBF->bNoHeader ) + return( psDBF->nFields - 1 ); + +/* -------------------------------------------------------------------- */ +/* For existing .dbf file, shift records */ +/* -------------------------------------------------------------------- */ + + /* alloc record */ + pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength); + + chFieldFill = DBFGetNullCharacter(chType); + + for (i = psDBF->nRecords-1; i >= 0; --i) + { + nRecordOffset = nOldRecordLength * (SAOffset) i + nOldHeaderLength; + + /* load record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp ); + + /* set new field's value to NULL */ + memset(pszRecord + nOldRecordLength, chFieldFill, nWidth); + + nRecordOffset = psDBF->nRecordLength * (SAOffset) i + psDBF->nHeaderLength; + + /* move record to the new place*/ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); + } + + /* free record */ + free(pszRecord); + + /* force update of header with new header, record length and new field */ + psDBF->bNoHeader = TRUE; + DBFUpdateHeader( psDBF ); + + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + psDBF->bUpdated = TRUE; + + return( psDBF->nFields-1 ); +} + +/************************************************************************/ +/* DBFReadAttribute() */ +/* */ +/* Read one of the attribute fields of a record. */ +/************************************************************************/ + +static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, + char chReqType ) + +{ + unsigned char *pabyRec; + void *pReturnField = NULL; + +/* -------------------------------------------------------------------- */ +/* Verify selection. */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity >= psDBF->nRecords ) + return( NULL ); + + if( iField < 0 || iField >= psDBF->nFields ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Have we read the record? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, hEntity ) ) + return NULL; + + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + +/* -------------------------------------------------------------------- */ +/* Ensure we have room to extract the target field. */ +/* -------------------------------------------------------------------- */ + if( psDBF->panFieldSize[iField] >= psDBF->nWorkFieldLength ) + { + psDBF->nWorkFieldLength = psDBF->panFieldSize[iField] + 100; + if( psDBF->pszWorkField == NULL ) + psDBF->pszWorkField = (char *) malloc(psDBF->nWorkFieldLength); + else + psDBF->pszWorkField = (char *) realloc(psDBF->pszWorkField, + psDBF->nWorkFieldLength); + } + +/* -------------------------------------------------------------------- */ +/* Extract the requested field. */ +/* -------------------------------------------------------------------- */ + memcpy( psDBF->pszWorkField, + ((const char *) pabyRec) + psDBF->panFieldOffset[iField], + psDBF->panFieldSize[iField] ); + psDBF->pszWorkField[psDBF->panFieldSize[iField]] = '\0'; + + pReturnField = psDBF->pszWorkField; + +/* -------------------------------------------------------------------- */ +/* Decode the field. */ +/* -------------------------------------------------------------------- */ + if( chReqType == 'I' ) + { + psDBF->fieldValue.nIntField = atoi(psDBF->pszWorkField); + + pReturnField = &(psDBF->fieldValue.nIntField); + } + else if( chReqType == 'N' ) + { + psDBF->fieldValue.dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField); + + pReturnField = &(psDBF->fieldValue.dfDoubleField); + } + +/* -------------------------------------------------------------------- */ +/* Should we trim white space off the string attribute value? */ +/* -------------------------------------------------------------------- */ +#ifdef TRIM_DBF_WHITESPACE + else + { + char *pchSrc, *pchDst; + + pchDst = pchSrc = psDBF->pszWorkField; + while( *pchSrc == ' ' ) + pchSrc++; + + while( *pchSrc != '\0' ) + *(pchDst++) = *(pchSrc++); + *pchDst = '\0'; + + while( pchDst != psDBF->pszWorkField && *(--pchDst) == ' ' ) + *pchDst = '\0'; + } +#endif + + return( pReturnField ); +} + +/************************************************************************/ +/* DBFReadIntAttribute() */ +/* */ +/* Read an integer attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + int *pnValue; + + pnValue = (int *) DBFReadAttribute( psDBF, iRecord, iField, 'I' ); + + if( pnValue == NULL ) + return 0; + else + return( *pnValue ); +} + +/************************************************************************/ +/* DBFReadDoubleAttribute() */ +/* */ +/* Read a double attribute. */ +/************************************************************************/ + +double SHPAPI_CALL +DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + double *pdValue; + + pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); + + if( pdValue == NULL ) + return 0.0; + else + return( *pdValue ); +} + +/************************************************************************/ +/* DBFReadStringAttribute() */ +/* */ +/* Read a string attribute. */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'C' ) ); +} + +/************************************************************************/ +/* DBFReadLogicalAttribute() */ +/* */ +/* Read a logical attribute. */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFReadLogicalAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'L' ) ); +} + + +/************************************************************************/ +/* DBFIsValueNULL() */ +/* */ +/* Return TRUE if the passed string is NULL. */ +/************************************************************************/ + +static int DBFIsValueNULL( char chType, const char* pszValue ) +{ + int i; + + if( pszValue == NULL ) + return TRUE; + + switch(chType) + { + case 'N': + case 'F': + /* + ** We accept all asterisks or all blanks as NULL + ** though according to the spec I think it should be all + ** asterisks. + */ + if( pszValue[0] == '*' ) + return TRUE; + + for( i = 0; pszValue[i] != '\0'; i++ ) + { + if( pszValue[i] != ' ' ) + return FALSE; + } + return TRUE; + + case 'D': + /* NULL date fields have value "00000000" */ + return strncmp(pszValue,"00000000",8) == 0; + + case 'L': + /* NULL boolean fields have value "?" */ + return pszValue[0] == '?'; + + default: + /* empty string fields are considered NULL */ + return strlen(pszValue) == 0; + } +} + +/************************************************************************/ +/* DBFIsAttributeNULL() */ +/* */ +/* Return TRUE if value for field is NULL. */ +/* */ +/* Contributed by Jim Matthews. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField ) + +{ + const char *pszValue; + + pszValue = DBFReadStringAttribute( psDBF, iRecord, iField ); + + if( pszValue == NULL ) + return TRUE; + + return DBFIsValueNULL( psDBF->pachFieldType[iField], pszValue ); +} + +/************************************************************************/ +/* DBFGetFieldCount() */ +/* */ +/* Return the number of fields in this table. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFGetFieldCount( DBFHandle psDBF ) + +{ + return( psDBF->nFields ); +} + +/************************************************************************/ +/* DBFGetRecordCount() */ +/* */ +/* Return the number of records in this table. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFGetRecordCount( DBFHandle psDBF ) + +{ + return( psDBF->nRecords ); +} + +/************************************************************************/ +/* DBFGetFieldInfo() */ +/* */ +/* Return any requested information about the field. */ +/************************************************************************/ + +DBFFieldType SHPAPI_CALL +DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, + int * pnWidth, int * pnDecimals ) + +{ + if( iField < 0 || iField >= psDBF->nFields ) + return( FTInvalid ); + + if( pnWidth != NULL ) + *pnWidth = psDBF->panFieldSize[iField]; + + if( pnDecimals != NULL ) + *pnDecimals = psDBF->panFieldDecimals[iField]; + + if( pszFieldName != NULL ) + { + int i; + + strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 ); + pszFieldName[11] = '\0'; + for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- ) + pszFieldName[i] = '\0'; + } + + if ( psDBF->pachFieldType[iField] == 'L' ) + return( FTLogical); + + else if( psDBF->pachFieldType[iField] == 'N' + || psDBF->pachFieldType[iField] == 'F' ) + { + if( psDBF->panFieldDecimals[iField] > 0 + || psDBF->panFieldSize[iField] >= 10 ) + return( FTDouble ); + else + return( FTInteger ); + } + else + { + return( FTString ); + } +} + +/************************************************************************/ +/* DBFWriteAttribute() */ +/* */ +/* Write an attribute record to the file. */ +/************************************************************************/ + +static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, + void * pValue ) + +{ + int i, j, nRetResult = TRUE; + unsigned char *pabyRec; + char szSField[400], szFormat[20]; + +/* -------------------------------------------------------------------- */ +/* Is this a valid record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity > psDBF->nRecords ) + return( FALSE ); + + if( psDBF->bNoHeader ) + DBFWriteHeader(psDBF); + +/* -------------------------------------------------------------------- */ +/* Is this a brand new record? */ +/* -------------------------------------------------------------------- */ + if( hEntity == psDBF->nRecords ) + { + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + psDBF->nRecords++; + for( i = 0; i < psDBF->nRecordLength; i++ ) + psDBF->pszCurrentRecord[i] = ' '; + + psDBF->nCurrentRecord = hEntity; + } + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, hEntity ) ) + return FALSE; + + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + +/* -------------------------------------------------------------------- */ +/* Translate NULL value to valid DBF file representation. */ +/* */ +/* Contributed by Jim Matthews. */ +/* -------------------------------------------------------------------- */ + if( pValue == NULL ) + { + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), + DBFGetNullCharacter(psDBF->pachFieldType[iField]), + psDBF->panFieldSize[iField] ); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Assign all the record fields. */ +/* -------------------------------------------------------------------- */ + switch( psDBF->pachFieldType[iField] ) + { + case 'D': + case 'N': + case 'F': + { + int nWidth = psDBF->panFieldSize[iField]; + + if( (int) sizeof(szSField)-2 < nWidth ) + nWidth = sizeof(szSField)-2; + + sprintf( szFormat, "%%%d.%df", + nWidth, psDBF->panFieldDecimals[iField] ); + CPLsprintf(szSField, szFormat, *((double *) pValue) ); + if( (int) strlen(szSField) > psDBF->panFieldSize[iField] ) + { + szSField[psDBF->panFieldSize[iField]] = '\0'; + nRetResult = FALSE; + } + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + szSField, strlen(szSField) ); + break; + } + + case 'L': + if (psDBF->panFieldSize[iField] >= 1 && + (*(char*)pValue == 'F' || *(char*)pValue == 'T')) + *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue; + break; + + default: + if( (int) strlen((char *) pValue) > psDBF->panFieldSize[iField] ) + { + j = psDBF->panFieldSize[iField]; + nRetResult = FALSE; + } + else + { + memset( pabyRec+psDBF->panFieldOffset[iField], ' ', + psDBF->panFieldSize[iField] ); + j = strlen((char *) pValue); + } + + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + (char *) pValue, j ); + break; + } + + return( nRetResult ); +} + +/************************************************************************/ +/* DBFWriteAttributeDirectly() */ +/* */ +/* Write an attribute record to the file, but without any */ +/* reformatting based on type. The provided buffer is written */ +/* as is to the field position in the record. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, + void * pValue ) + +{ + int i, j; + unsigned char *pabyRec; + +/* -------------------------------------------------------------------- */ +/* Is this a valid record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity > psDBF->nRecords ) + return( FALSE ); + + if( psDBF->bNoHeader ) + DBFWriteHeader(psDBF); + +/* -------------------------------------------------------------------- */ +/* Is this a brand new record? */ +/* -------------------------------------------------------------------- */ + if( hEntity == psDBF->nRecords ) + { + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + psDBF->nRecords++; + for( i = 0; i < psDBF->nRecordLength; i++ ) + psDBF->pszCurrentRecord[i] = ' '; + + psDBF->nCurrentRecord = hEntity; + } + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, hEntity ) ) + return FALSE; + + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + +/* -------------------------------------------------------------------- */ +/* Assign all the record fields. */ +/* -------------------------------------------------------------------- */ + if( (int)strlen((char *) pValue) > psDBF->panFieldSize[iField] ) + j = psDBF->panFieldSize[iField]; + else + { + memset( pabyRec+psDBF->panFieldOffset[iField], ' ', + psDBF->panFieldSize[iField] ); + j = strlen((char *) pValue); + } + + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + (char *) pValue, j ); + + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + + return( TRUE ); +} + +/************************************************************************/ +/* DBFWriteDoubleAttribute() */ +/* */ +/* Write a double attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField, + double dValue ) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); +} + +/************************************************************************/ +/* DBFWriteIntegerAttribute() */ +/* */ +/* Write a integer attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField, + int nValue ) + +{ + double dValue = nValue; + + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); +} + +/************************************************************************/ +/* DBFWriteStringAttribute() */ +/* */ +/* Write a string attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField, + const char * pszValue ) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) ); +} + +/************************************************************************/ +/* DBFWriteNULLAttribute() */ +/* */ +/* Write a string attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteNULLAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, NULL ) ); +} + +/************************************************************************/ +/* DBFWriteLogicalAttribute() */ +/* */ +/* Write a logical attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteLogicalAttribute( DBFHandle psDBF, int iRecord, int iField, + const char lValue) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) (&lValue) ) ); +} + +/************************************************************************/ +/* DBFWriteTuple() */ +/* */ +/* Write an attribute record to the file. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ) + +{ + int i; + unsigned char *pabyRec; + +/* -------------------------------------------------------------------- */ +/* Is this a valid record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity > psDBF->nRecords ) + return( FALSE ); + + if( psDBF->bNoHeader ) + DBFWriteHeader(psDBF); + +/* -------------------------------------------------------------------- */ +/* Is this a brand new record? */ +/* -------------------------------------------------------------------- */ + if( hEntity == psDBF->nRecords ) + { + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + psDBF->nRecords++; + for( i = 0; i < psDBF->nRecordLength; i++ ) + psDBF->pszCurrentRecord[i] = ' '; + + psDBF->nCurrentRecord = hEntity; + } + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, hEntity ) ) + return FALSE; + + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + + memcpy ( pabyRec, pRawTuple, psDBF->nRecordLength ); + + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + + return( TRUE ); +} + +/************************************************************************/ +/* DBFReadTuple() */ +/* */ +/* Read a complete record. Note that the result is only valid */ +/* till the next record read for any reason. */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFReadTuple(DBFHandle psDBF, int hEntity ) + +{ + if( hEntity < 0 || hEntity >= psDBF->nRecords ) + return( NULL ); + + if( !DBFLoadRecord( psDBF, hEntity ) ) + return NULL; + + return (const char *) psDBF->pszCurrentRecord; +} + +/************************************************************************/ +/* DBFCloneEmpty() */ +/* */ +/* Read one of the attribute fields of a record. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) +{ + DBFHandle newDBF; + + newDBF = DBFCreateEx ( pszFilename, psDBF->pszCodePage ); + if ( newDBF == NULL ) return ( NULL ); + + newDBF->nFields = psDBF->nFields; + newDBF->nRecordLength = psDBF->nRecordLength; + newDBF->nHeaderLength = psDBF->nHeaderLength; + + newDBF->pszHeader = (char *) malloc ( newDBF->nHeaderLength ); + memcpy ( newDBF->pszHeader, psDBF->pszHeader, newDBF->nHeaderLength ); + + newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields ); + memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); + newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields ); + memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); + newDBF->panFieldDecimals = (int *) malloc ( sizeof(int) * psDBF->nFields ); + memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); + newDBF->pachFieldType = (char *) malloc ( sizeof(char) * psDBF->nFields ); + memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(char)*psDBF->nFields ); + + newDBF->bNoHeader = TRUE; + newDBF->bUpdated = TRUE; + + DBFWriteHeader ( newDBF ); + DBFClose ( newDBF ); + + newDBF = DBFOpen ( pszFilename, "rb+" ); + + return ( newDBF ); +} + +/************************************************************************/ +/* DBFGetNativeFieldType() */ +/* */ +/* Return the DBase field type for the specified field. */ +/* */ +/* Value can be one of: 'C' (String), 'D' (Date), 'F' (Float), */ +/* 'N' (Numeric, with or without decimal), */ +/* 'L' (Logical), */ +/* 'M' (Memo: 10 digits .DBT block ptr) */ +/************************************************************************/ + +char SHPAPI_CALL +DBFGetNativeFieldType( DBFHandle psDBF, int iField ) + +{ + if( iField >=0 && iField < psDBF->nFields ) + return psDBF->pachFieldType[iField]; + + return ' '; +} + +/************************************************************************/ +/* str_to_upper() */ +/************************************************************************/ + +static void str_to_upper (char *string) +{ + int len; + short i = -1; + + len = strlen (string); + + while (++i < len) + if (isalpha(string[i]) && islower(string[i])) + string[i] = (char) toupper ((int)string[i]); +} + +/************************************************************************/ +/* DBFGetFieldIndex() */ +/* */ +/* Get the index number for a field in a .dbf file. */ +/* */ +/* Contributed by Jim Matthews. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName) + +{ + char name[12], name1[12], name2[12]; + int i; + + strncpy(name1, pszFieldName,11); + name1[11] = '\0'; + str_to_upper(name1); + + for( i = 0; i < DBFGetFieldCount(psDBF); i++ ) + { + DBFGetFieldInfo( psDBF, i, name, NULL, NULL ); + strncpy(name2,name,11); + str_to_upper(name2); + + if(!strncmp(name1,name2,10)) + return(i); + } + return(-1); +} + +/************************************************************************/ +/* DBFIsRecordDeleted() */ +/* */ +/* Returns TRUE if the indicated record is deleted, otherwise */ +/* it returns FALSE. */ +/************************************************************************/ + +int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify selection. */ +/* -------------------------------------------------------------------- */ + if( iShape < 0 || iShape >= psDBF->nRecords ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Have we read the record? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, iShape ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* '*' means deleted. */ +/* -------------------------------------------------------------------- */ + return psDBF->pszCurrentRecord[0] == '*'; +} + +/************************************************************************/ +/* DBFMarkRecordDeleted() */ +/************************************************************************/ + +int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, + int bIsDeleted ) + +{ + char chNewFlag; + +/* -------------------------------------------------------------------- */ +/* Verify selection. */ +/* -------------------------------------------------------------------- */ + if( iShape < 0 || iShape >= psDBF->nRecords ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, iShape ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Assign value, marking record as dirty if it changes. */ +/* -------------------------------------------------------------------- */ + if( bIsDeleted ) + chNewFlag = '*'; + else + chNewFlag = ' '; + + if( psDBF->pszCurrentRecord[0] != chNewFlag ) + { + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + psDBF->pszCurrentRecord[0] = chNewFlag; + } + + return TRUE; +} + +/************************************************************************/ +/* DBFGetCodePage */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFGetCodePage(DBFHandle psDBF ) +{ + if( psDBF == NULL ) + return NULL; + return psDBF->pszCodePage; +} + +/************************************************************************/ +/* DBFDeleteField() */ +/* */ +/* Remove a field from a .dbf file */ +/************************************************************************/ + +int SHPAPI_CALL +DBFDeleteField(DBFHandle psDBF, int iField) +{ + int nOldRecordLength, nOldHeaderLength; + int nDeletedFieldOffset, nDeletedFieldSize; + SAOffset nRecordOffset; + char* pszRecord; + int i, iRecord; + + if (iField < 0 || iField >= psDBF->nFields) + return FALSE; + + /* make sure that everything is written in .dbf */ + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + /* get information about field to be deleted */ + nOldRecordLength = psDBF->nRecordLength; + nOldHeaderLength = psDBF->nHeaderLength; + nDeletedFieldOffset = psDBF->panFieldOffset[iField]; + nDeletedFieldSize = psDBF->panFieldSize[iField]; + + /* update fields info */ + for (i = iField + 1; i < psDBF->nFields; i++) + { + psDBF->panFieldOffset[i-1] = psDBF->panFieldOffset[i] - nDeletedFieldSize; + psDBF->panFieldSize[i-1] = psDBF->panFieldSize[i]; + psDBF->panFieldDecimals[i-1] = psDBF->panFieldDecimals[i]; + psDBF->pachFieldType[i-1] = psDBF->pachFieldType[i]; + } + + /* resize fields arrays */ + psDBF->nFields--; + + psDBF->panFieldOffset = (int *) + SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldSize = (int *) + SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldDecimals = (int *) + SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); + + psDBF->pachFieldType = (char *) + SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); + + /* update header information */ + psDBF->nHeaderLength -= 32; + psDBF->nRecordLength -= nDeletedFieldSize; + + /* overwrite field information in header */ + memmove(psDBF->pszHeader + iField*32, + psDBF->pszHeader + (iField+1)*32, + sizeof(char) * (psDBF->nFields - iField)*32); + + psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); + + /* update size of current record appropriately */ + psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, + psDBF->nRecordLength); + + /* we're done if we're dealing with not yet created .dbf */ + if ( psDBF->bNoHeader && psDBF->nRecords == 0 ) + return TRUE; + + /* force update of header with new header and record length */ + psDBF->bNoHeader = TRUE; + DBFUpdateHeader( psDBF ); + + /* alloc record */ + pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength); + + /* shift records to their new positions */ + for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++) + { + nRecordOffset = + nOldRecordLength * (SAOffset) iRecord + nOldHeaderLength; + + /* load record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp ); + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* move record in two steps */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( pszRecord, nDeletedFieldOffset, 1, psDBF->fp ); + psDBF->sHooks.FWrite( pszRecord + nDeletedFieldOffset + nDeletedFieldSize, + nOldRecordLength - nDeletedFieldOffset - nDeletedFieldSize, + 1, psDBF->fp ); + + } + + /* TODO: truncate file */ + + /* free record */ + free(pszRecord); + + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + psDBF->bUpdated = TRUE; + + return TRUE; +} + +/************************************************************************/ +/* DBFReorderFields() */ +/* */ +/* Reorder the fields of a .dbf file */ +/* */ +/* panMap must be exactly psDBF->nFields long and be a permutation */ +/* of [0, psDBF->nFields-1]. This assumption will not be asserted in the*/ +/* code of DBFReorderFields. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFReorderFields( DBFHandle psDBF, int* panMap ) +{ + SAOffset nRecordOffset; + int i, iRecord; + int *panFieldOffsetNew; + int *panFieldSizeNew; + int *panFieldDecimalsNew; + char *pachFieldTypeNew; + char *pszHeaderNew; + char *pszRecord; + char *pszRecordNew; + + if ( psDBF->nFields == 0 ) + return TRUE; + + /* make sure that everything is written in .dbf */ + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + panFieldOffsetNew = (int *) malloc(sizeof(int) * psDBF->nFields); + panFieldSizeNew = (int *) malloc(sizeof(int) * psDBF->nFields); + panFieldDecimalsNew = (int *) malloc(sizeof(int) * psDBF->nFields); + pachFieldTypeNew = (char *) malloc(sizeof(char) * psDBF->nFields); + pszHeaderNew = (char*) malloc(sizeof(char) * 32 * psDBF->nFields); + + /* shuffle fields definitions */ + for(i=0; i < psDBF->nFields; i++) + { + panFieldSizeNew[i] = psDBF->panFieldSize[panMap[i]]; + panFieldDecimalsNew[i] = psDBF->panFieldDecimals[panMap[i]]; + pachFieldTypeNew[i] = psDBF->pachFieldType[panMap[i]]; + memcpy(pszHeaderNew + i * 32, + psDBF->pszHeader + panMap[i] * 32, 32); + } + panFieldOffsetNew[0] = 1; + for(i=1; i < psDBF->nFields; i++) + { + panFieldOffsetNew[i] = panFieldOffsetNew[i - 1] + panFieldSizeNew[i - 1]; + } + + free(psDBF->pszHeader); + psDBF->pszHeader = pszHeaderNew; + + /* we're done if we're dealing with not yet created .dbf */ + if ( !(psDBF->bNoHeader && psDBF->nRecords == 0) ) + { + /* force update of header with new header and record length */ + psDBF->bNoHeader = TRUE; + DBFUpdateHeader( psDBF ); + + /* alloc record */ + pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength); + pszRecordNew = (char *) malloc(sizeof(char) * psDBF->nRecordLength); + + /* shuffle fields in records */ + for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++) + { + nRecordOffset = + psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* load record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FRead( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); + + pszRecordNew[0] = pszRecord[0]; + + for(i=0; i < psDBF->nFields; i++) + { + memcpy(pszRecordNew + panFieldOffsetNew[i], + pszRecord + psDBF->panFieldOffset[panMap[i]], + psDBF->panFieldSize[panMap[i]]); + } + + /* write record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( pszRecordNew, psDBF->nRecordLength, 1, psDBF->fp ); + } + + /* free record */ + free(pszRecord); + free(pszRecordNew); + } + + free(psDBF->panFieldOffset); + free(psDBF->panFieldSize); + free(psDBF->panFieldDecimals); + free(psDBF->pachFieldType); + + psDBF->panFieldOffset = panFieldOffsetNew; + psDBF->panFieldSize = panFieldSizeNew; + psDBF->panFieldDecimals =panFieldDecimalsNew; + psDBF->pachFieldType = pachFieldTypeNew; + + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + psDBF->bUpdated = TRUE; + + return TRUE; +} + + +/************************************************************************/ +/* DBFAlterFieldDefn() */ +/* */ +/* Alter a field definition in a .dbf file */ +/************************************************************************/ + +int SHPAPI_CALL +DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName, + char chType, int nWidth, int nDecimals ) +{ + int i; + int iRecord; + int nOffset; + int nOldWidth; + int nOldRecordLength; + int nRecordOffset; + char* pszFInfo; + char chOldType; + int bIsNULL; + char chFieldFill; + + if (iField < 0 || iField >= psDBF->nFields) + return FALSE; + + /* make sure that everything is written in .dbf */ + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + chFieldFill = DBFGetNullCharacter(chType); + + chOldType = psDBF->pachFieldType[iField]; + nOffset = psDBF->panFieldOffset[iField]; + nOldWidth = psDBF->panFieldSize[iField]; + nOldRecordLength = psDBF->nRecordLength; + +/* -------------------------------------------------------------------- */ +/* Do some checking to ensure we can add records to this file. */ +/* -------------------------------------------------------------------- */ + if( nWidth < 1 ) + return -1; + + if( nWidth > 255 ) + nWidth = 255; + +/* -------------------------------------------------------------------- */ +/* Assign the new field information fields. */ +/* -------------------------------------------------------------------- */ + psDBF->panFieldSize[iField] = nWidth; + psDBF->panFieldDecimals[iField] = nDecimals; + psDBF->pachFieldType[iField] = chType; + +/* -------------------------------------------------------------------- */ +/* Update the header information. */ +/* -------------------------------------------------------------------- */ + pszFInfo = psDBF->pszHeader + 32 * iField; + + for( i = 0; i < 32; i++ ) + pszFInfo[i] = '\0'; + + if( (int) strlen(pszFieldName) < 10 ) + strncpy( pszFInfo, pszFieldName, strlen(pszFieldName)); + else + strncpy( pszFInfo, pszFieldName, 10); + + pszFInfo[11] = psDBF->pachFieldType[iField]; + + if( chType == 'C' ) + { + pszFInfo[16] = (unsigned char) (nWidth % 256); + pszFInfo[17] = (unsigned char) (nWidth / 256); + } + else + { + pszFInfo[16] = (unsigned char) nWidth; + pszFInfo[17] = (unsigned char) nDecimals; + } + +/* -------------------------------------------------------------------- */ +/* Update offsets */ +/* -------------------------------------------------------------------- */ + if (nWidth != nOldWidth) + { + for (i = iField + 1; i < psDBF->nFields; i++) + psDBF->panFieldOffset[i] += nWidth - nOldWidth; + psDBF->nRecordLength += nWidth - nOldWidth; + + psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, + psDBF->nRecordLength); + } + + /* we're done if we're dealing with not yet created .dbf */ + if ( psDBF->bNoHeader && psDBF->nRecords == 0 ) + return TRUE; + + /* force update of header with new header and record length */ + psDBF->bNoHeader = TRUE; + DBFUpdateHeader( psDBF ); + + if (nWidth < nOldWidth || (nWidth == nOldWidth && chType != chOldType)) + { + char* pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength); + char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1)); + + pszOldField[nOldWidth] = 0; + + /* move records to their new positions */ + for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++) + { + nRecordOffset = + nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* load record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp ); + + memcpy(pszOldField, pszRecord + nOffset, nOldWidth); + bIsNULL = DBFIsValueNULL( chOldType, pszOldField ); + + if (nWidth != nOldWidth) + { + if ((chOldType == 'N' || chOldType == 'F') && pszOldField[0] == ' ') + { + /* Strip leading spaces when truncating a numeric field */ + memmove( pszRecord + nOffset, + pszRecord + nOffset + nOldWidth - nWidth, + nWidth ); + } + if (nOffset + nOldWidth < nOldRecordLength) + { + memmove( pszRecord + nOffset + nWidth, + pszRecord + nOffset + nOldWidth, + nOldRecordLength - (nOffset + nOldWidth)); + } + } + + /* Convert null value to the appropriate value of the new type */ + if (bIsNULL) + { + memset( pszRecord + nOffset, chFieldFill, nWidth); + } + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* write record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); + } + + free(pszRecord); + free(pszOldField); + } + else if (nWidth > nOldWidth) + { + char* pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength); + char* pszOldField = (char *) malloc(sizeof(char) * (nOldWidth + 1)); + + pszOldField[nOldWidth] = 0; + + /* move records to their new positions */ + for (iRecord = psDBF->nRecords - 1; iRecord >= 0; iRecord--) + { + nRecordOffset = + nOldRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* load record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp ); + + memcpy(pszOldField, pszRecord + nOffset, nOldWidth); + bIsNULL = DBFIsValueNULL( chOldType, pszOldField ); + + if (nOffset + nOldWidth < nOldRecordLength) + { + memmove( pszRecord + nOffset + nWidth, + pszRecord + nOffset + nOldWidth, + nOldRecordLength - (nOffset + nOldWidth)); + } + + /* Convert null value to the appropriate value of the new type */ + if (bIsNULL) + { + memset( pszRecord + nOffset, chFieldFill, nWidth); + } + else + { + if ((chOldType == 'N' || chOldType == 'F')) + { + /* Add leading spaces when expanding a numeric field */ + memmove( pszRecord + nOffset + nWidth - nOldWidth, + pszRecord + nOffset, nOldWidth ); + memset( pszRecord + nOffset, ' ', nWidth - nOldWidth ); + } + else + { + /* Add trailing spaces */ + memset(pszRecord + nOffset + nOldWidth, ' ', nWidth - nOldWidth); + } + } + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* write record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); + } + + free(pszRecord); + free(pszOldField); + } + + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + psDBF->bUpdated = TRUE; + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/drv_shapefile.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/drv_shapefile.html new file mode 100644 index 000000000..40cfd6d19 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/drv_shapefile.html @@ -0,0 +1,311 @@ + + + +ESRI Shapefile + + + + +

    ESRI Shapefile

    + +

    All varieties of ESRI Shapefiles should be available for reading, and +simple 3D files can be created.

    + +

    Normally the OGR Shapefile driver treats a whole directory of shapefiles +as a dataset, and a single shapefile within that directory as a layer. In +this case the directory name should be used as the dataset name. However, +it is also possible to use one of the files (.shp, .shx or .dbf) in a +shapefile set as the dataset name, and then it will be treated as a dataset +with one layer.

    + +

    Note that when reading a Shapefile of type SHPT_ARC, the corresponding layer +will be reported as of type wkbLineString, but depending on the number of +parts of each geometry, the actual type of the geometry for each feature +can be either OGRLineString or OGRMultiLineString. +The same applies for SHPT_POLYGON shapefiles, reported as layers of type +wkbPolygon, but depending on the number of parts of each geometry, the +actual type can be either OGRPolygon or OGRMultiPolygon.

    + +

    ESRI measure values (XYM) are read as XYZ geometries. MultiPatch +files are read and each patch geometry is turned into a multi-polygon +representation with one polygon per triangle in triangle fans and meshes.

    + +

    If a .prj files in old Arc/Info style or new ESRI OGC WKT style is present, +it will be read and used to associate a projection with features.

    + +

    The read driver assumes that multipart polygons follow the specification, +that is to say the vertices of outer rings should be oriented clockwise on the +X/Y plane, and those of inner rings counterclockwise. +If a Shapefile is broken w.r.t. that rule, it is possible to define the +configuration option OGR_ORGANIZE_POLYGONS=DEFAULT to proceed to a full +analysis based on topological relationships of the parts of the polygons so +that the resulting polygons are correctly +defined in the OGC Simple Feature convention.

    + +

    An attempt is made to read the LDID/codepage setting from the .dbf file +and use it to translate string fields to UTF-8 on read, and back when writing. +LDID "87 / 0x57" is treated as ISO8859_1 which may not be appropriate. +The SHAPE_ENCODING +configuration option may be used to override the encoding interpretation +of the shapefile with any encoding supported by CPLRecode or to "" to avoid +any recoding. (Recoding support is new for GDAL/OGR 1.9.0)

    + +

    Open options

    + +

    Starting with GDAL 2.0, the following open options are available.

    +
      +
    • ENCODING=encoding_name: to override the encoding interpretation of the +sshapefile with any encoding supported by CPLRecode or to "" to avoid any +recoding
    • +
    • DBF_DATE_LAST_UPDATE=YYYY-MM-DD: Modification +date to write in DBF header with year-month-day format. If not specified, current date is used.
    • +
    • ADJUST_TYPE=YES/NO: Set to YES (default is NO) to read the whole .dbf to adjust +Real->Integer/Integer64 or Integer64->Integer field types when possible. This +can be used when field widths are ambiguous and that by default OGR would select +the larger data type. For example, a numeric column with 0 decimal figures and with +width of 10/11 character may hold Integer or Integer64, and with width 19/20 may +hold Integer64 or larger integer (hold as Real)
    • +
    + +

    Spatial and Attribute Indexing

    + +

    The OGR Shapefile driver supports spatial indexing and a limited form of +attribute indexing.

    + +

    The spatial indexing uses the same .qix quadtree spatial index files that +are used by UMN MapServer. Spatial indexing can accelerate spatially filtered +passes through large datasets to pick out a small area quite dramatically.

    + +

    Starting with OGR 1.10, it can also use the ESRI spatial index +files (.sbn / .sbx), but writing them is not supported currently.

    + +

    To create a spatial index (in .qix format), issue a SQL command of the form

    +
    CREATE SPATIAL INDEX ON tablename [DEPTH N]
    +

    where optional DEPTH specifier can be used to control number of index tree levels +generated. If DEPTH is omitted, tree depth is estimated on basis of number of features +in a shapefile and its value ranges from 1 to 12.

    + +

    To delete a spatial index issue a command of the form

    +
    DROP SPATIAL INDEX ON tablename
    + + +

    + Otherwise, the MapServer shptree utility can be used:

    +
    shptree <shpfile> [<depth>] [<index_format>]
    + +

    +More information is available about this utility at the +MapServer shptree page +

    + +

    Currently the OGR Shapefile driver only supports attribute indexes for +looking up specific values in a unique key column. To create an attribute +index for a column issue an SQL command of the form "CREATE INDEX ON tablename +USING fieldname". To drop the attribute indexes issue a command of the +form "DROP INDEX ON tablename". The attribute index will accelerate +WHERE clause searches of the form "fieldname = value". The attribute +index is actually stored as a mapinfo format index and is not compatible +with any other shapefile applications.

    + +

    Creation Issues

    + +

    The Shapefile driver treats a directory as a dataset, and each Shapefile +set (.shp, .shx, and .dbf) as a layer. The dataset name will be treated +as a directory name. If the directory already exists it is used and +existing files in the directory are ignored. If the directory does not +exist it will be created.

    + +

    As a special case attempts to create a new dataset with the extension .shp +will result in a single file set being created instead of a directory.

    + +

    ESRI shapefiles can only store one kind of geometry per layer (shapefile). +On creation this is may be set based on the source file (if a uniform geometry +type is known from the source driver), or it may be set directly by the +user with the layer creation option SHPT (shown below). If not set the +layer creation will fail. If geometries of incompatible types are written +to the layer, the output will be terminated with an error.

    + +

    Note that this can make it very difficult to translate a mixed geometry layer +from another format into Shapefile format using ogr2ogr, since ogr2ogr has +no support for separating out geometries from a source layer. See +the FAQ for a solution.

    + +

    Shapefile feature attributes are stored in an associated .dbf file, and so +attributes suffer a number of limitations:

    + +
      +
    • Attribute names can only be up to 10 characters long. Starting with version 1.7, +the OGR Shapefile driver tries to generate +unique field names. Successive duplicate field names, including those created +by truncation to 10 characters, will be truncated to 8 characters and appended +with a serial number from 1 to 99.

      For example:

      +
      • a → a, a → a_1, A → A_2;
      • +
      • abcdefghijk → abcdefghij, abcdefghijkl → abcdefgh_1
      +
    • + +
    • Only Integer, Integer64, Real, String and Date (not DateTime, just year/month/day) +field types are supported. The various list, and binary field types cannot +be created.

    • + +
    • The field width and precision are directly used to establish storage +size in the .dbf file. This means that strings longer than the field +width, or numbers that don't fit into the indicated field format will suffer +truncation.

    • + +
    • Integer fields without an explicit width are treated as width 9, and +extended to 10 or 11 if needed.

    • + +
    • Integer64 fields without an explicit width are treated as width 18, and +extended to 19 or 20 if needed.

    • + +
    • Real (floating point) fields without an explicit width are treated as +width 24 with 15 decimal places of precision.

    • + +
    • String fields without an assigned width are treated as 80 characters.

    • + +
    + +

    Also, .dbf files are required to have at least one field. If none are created +by the application an "FID" field will be automatically created and populated +with the record number.

    + +

    The OGR shapefile driver supports rewriting existing shapes in a shapefile +as well as deleting shapes. Deleted shapes are marked for deletion in +the .dbf file, and then ignored by OGR. To actually remove them permanently +(resulting in renumbering of FIDs) invoke the SQL 'REPACK <tablename>' via +the datasource ExecuteSQL() method.

    +

    Starting with GDAL 2.0, REPACK will also result in .shp being rewritten if +a feature geometry has been modified with SetFeature() and resulted in a change +of the size the binary encoding of the geometry in the .shp file.

    + +

    Field sizes

    + +

    Starting with GDAL/OGR 1.10, the driver knows to auto-extend string and integer fields +(up to the 255 bytes limit imposed by the DBF format) to dynamically accommodate for +the length of the data to be inserted.

    + +

    It is also possible to force a resize of the fields to the optimal width by issuing a +SQL 'RESIZE <tablename>' via the datasource ExecuteSQL() method. This is convenient +in situations where the default column width (80 characters for a string field) is bigger than +necessary.

    + +

    Spatial extent

    + +

    Shapefiles store the layer spatial extent in the .SHP file. The layer spatial extent +is automatically updated when inserting a new feature in a shapefile. However when +updating an existing feature, if its previous shape was touching the bounding box of the +layer extent but the updated shape does not touch the new extent, the computed extent +will not be correct. It is then necessary to force a recomputation by invoking the +SQL 'RECOMPUTE EXTENT ON <tablename>' via the datasource ExecuteSQL() method. The +same applies for the deletion of a shape.

    + +

    Note: RECOMPUTE EXTENT ON is available in OGR >= 1.9.0.

    + +

    Size Issues

    + +

    Geometry: The Shapefile format explicitly uses 32bit offsets and so cannot +go over 8GB (it actually uses 32bit offsets to 16bit words), but the OGR shapefile +implementation has a limitation to 4GB.

    + +

    Attributes: The dbf format does not have any offsets in it, so it can be +arbitrarily large.

    + +

    However, for compatibility with other software implementation, it is is not +recommended to use a file size over 2GB for both .SHP and .DBF files.

    + +

    Starting with OGR 1.11, the 2GB_LIMIT=YES layer creation option can be used +to strictly enforce that limit. For update mode, the SHAPE_2GB_LIMIT configuration option +can be set to YES for similar effect. If nothing is set, a warning will be +emitted when the 2GB limit is reached.

    + +

    Dataset Creation Options

    + +

    None

    + +

    Layer Creation Options

    + +
      +
    • +SHPT=type: Override the type of shapefile created. Can be one of +NULL for a simple .dbf file with no .shp file, + POINT, ARC, POLYGON or MULTIPOINT for 2D, or +POINTZ, ARCZ, POLYGONZ or MULTIPOINTZ for 3D. Shapefiles with measure +values are not supported, nor are MULTIPATCH files.
    • + +
    • ENCODING=value: set the encoding value in the DBF file. The +default value is "LDID/87". It is not clear what other values may be +appropriate.
    • + +
    • RESIZE=YES/NO: (OGR >= 1.10.0) set the YES to resize fields to their optimal +size. See above "Field sizes" section. Defaults to NO.
    • + +
    • 2GB_LIMIT=YES/NO: (OGR >= 1.11) set the YES to enforce the 2GB file size +for .SHP or .DBF files. Defaults to NO.
    • + +
    • SPATIAL_INDEX=YES/NO: (OGR >= 2.0) set the YES to create a spatial index (.qix). Defaults to NO.
    • + +
    • DBF_DATE_LAST_UPDATE=YYYY-MM-DD: (OGR >= 2.0) Modification +date to write in DBF header with year-month-day format. If not specified, current date is used. +Note: behaviour of past GDAL releases was to write 1995-07-26
    • + +
    + +

    VSI Virtual File System API support

    + +The driver supports reading from files managed by VSI Virtual File System API, which include +"regular" files, as well as files in the /vsizip/, /vsigzip/ , /vsicurl/ domains.

    + +

    Examples

    + +
      +
    • +

      A merge of two shapefiles 'file1.shp' and 'file2.shp' into a new file +'file_merged.shp' is performed like this:

      + +
      +% ogr2ogr file_merged.shp file1.shp
      +% ogr2ogr -update -append file_merged.shp file2.shp -nln file_merged
      +
      + +

      The second command is opening file_merged.shp in update mode, and trying to +find existing layers and append the features being copied.

      + +

      The -nln option sets the name of the layer to be copied to.

      +
    • + +
    • Building a spatial index : +
      +% ogrinfo file1.shp -sql "CREATE SPATIAL INDEX ON file1"
      +
      +
    • + +
    • Resizing columns of a DBF file to their optimal size (OGR >= 1.10.0) : +
      +% ogrinfo file1.dbf -sql "RESIZE file1"
      +
      +
    • + + +
    + +

    Advanced topics

    + +

    +(GDAL >= 2.0) The SHAPE_REWIND_ON_WRITE configuration option/environment +variable can be set to NO to prevent the shapefile writer to correct the +winding order of exterior/interior rings to be conformant with the one mandated +by the Shapefile specification. This can be useful in some situations where +a MultiPolygon passed to the shapefile writer is not really a compliant Single +Feature polygon, but originates from example from a MultiPatch object (from +a Shapefile/FileGDB/PGeo datasource). +

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/makefile.vc new file mode 100644 index 000000000..b330920fd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/makefile.vc @@ -0,0 +1,18 @@ + +OBJ = shape2ogr.obj shpopen.obj dbfopen.obj ogrshapedriver.obj \ + ogrshapedatasource.obj ogrshapelayer.obj shptree.obj sbnsearch.obj \ + shp_vsi.obj +EXTRAFLAGS = -I.. -I..\.. -I..\generic /DSHAPELIB_DLLEXPORT \ + -DUSE_CPL -DSAOffset=vsi_l_offset + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/ogrshape.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/ogrshape.h new file mode 100644 index 000000000..1238720a4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/ogrshape.h @@ -0,0 +1,282 @@ +/****************************************************************************** + * $Id: ogrshape.h 28775 2015-03-25 16:24:02Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions within the Shapefile driver to implement + * integration with OGR. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGRSHAPE_H_INCLUDED +#define _OGRSHAPE_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "shapefil.h" +#include "shp_vsi.h" +#include "ogrlayerpool.h" +#include + +/* Was limited to 255 until OGR 1.10, but 254 seems to be a more */ +/* conventionnal limit (http://en.wikipedia.org/wiki/Shapefile, */ +/* http://www.clicketyclick.dk/databases/xbase/format/data_types.html, */ +/* #5052 ) */ +#define OGR_DBF_MAX_FIELD_WIDTH 254 + +/* ==================================================================== */ +/* Functions from Shape2ogr.cpp. */ +/* ==================================================================== */ +OGRFeature *SHPReadOGRFeature( SHPHandle hSHP, DBFHandle hDBF, + OGRFeatureDefn * poDefn, int iShape, + SHPObject *psShape, const char *pszSHPEncoding ); +OGRGeometry *SHPReadOGRObject( SHPHandle hSHP, int iShape, SHPObject *psShape ); +OGRFeatureDefn *SHPReadOGRFeatureDefn( const char * pszName, + SHPHandle hSHP, DBFHandle hDBF, + const char *pszSHPEncoding, + int bAdjustType ); +OGRErr SHPWriteOGRFeature( SHPHandle hSHP, DBFHandle hDBF, + OGRFeatureDefn *poFeatureDefn, + OGRFeature *poFeature, const char *pszSHPEncoding, + int* pbTruncationWarningEmitted, + int bRewind ); + +/************************************************************************/ +/* OGRShapeGeomFieldDefn */ +/************************************************************************/ + +class OGRShapeGeomFieldDefn: public OGRGeomFieldDefn +{ + char* pszFullName; + int bSRSSet; + CPLString osPrjFile; + + public: + OGRShapeGeomFieldDefn(const char* pszFullNameIn, OGRwkbGeometryType eType, + int bSRSSetIn, OGRSpatialReference *poSRSIn) : + OGRGeomFieldDefn("", eType), + pszFullName(CPLStrdup(pszFullNameIn)), + bSRSSet(bSRSSetIn) + { + poSRS = poSRSIn; + } + + virtual ~OGRShapeGeomFieldDefn() { CPLFree(pszFullName); } + + virtual OGRSpatialReference* GetSpatialRef(); + + const CPLString& GetPrjFilename() { return osPrjFile; } +}; + +/************************************************************************/ +/* OGRShapeLayer */ +/************************************************************************/ + +class OGRShapeDataSource; + +class OGRShapeLayer : public OGRAbstractProxiedLayer +{ + OGRShapeDataSource *poDS; + + OGRFeatureDefn *poFeatureDefn; + int iNextShapeId; + int nTotalShapeCount; + + char *pszFullName; + + SHPHandle hSHP; + DBFHandle hDBF; + + int bUpdateAccess; + + OGRwkbGeometryType eRequestedGeomType; + int ResetGeomType( int nNewType ); + + int ScanIndices(); + + GIntBig *panMatchingFIDs; + int iMatchingFID; + void ClearMatchingFIDs(); + + OGRGeometry *m_poFilterGeomLastValid; + int nSpatialFIDCount; + int *panSpatialFIDs; + void ClearSpatialFIDs(); + + int bHeaderDirty; + int bSHPNeedsRepack; + + int bCheckedForQIX; + SHPTreeDiskHandle hQIX; + int CheckForQIX(); + + int bCheckedForSBN; + SBNSearchHandle hSBN; + int CheckForSBN(); + + int bSbnSbxDeleted; + + CPLString ConvertCodePage( const char * ); + CPLString osEncoding; + + int bTruncationWarningEmitted; + + int bHSHPWasNonNULL; /* to know if we must try to reopen a .shp */ + int bHDBFWasNonNULL; /* to know if we must try to reopen a .dbf */ + int eFileDescriptorsState; /* current state of opening of file descriptor to .shp and .dbf */ + int TouchLayer(); + int ReopenFileDescriptors(); + + int bResizeAtClose; + + void TruncateDBF(); + + int bCreateSpatialIndexAtClose; + int bRewindOnWrite; + + protected: + + virtual void CloseUnderlyingLayer(); + +/* WARNING: each of the below public methods should start with a call to */ +/* TouchLayer() and test its return value, so as to make sure that */ +/* the layer is properly re-opened if necessary */ + + public: + OGRErr CreateSpatialIndex( int nMaxDepth ); + OGRErr DropSpatialIndex(); + OGRErr Repack(); + OGRErr RecomputeExtent(); + OGRErr ResizeDBF(); + + void SetResizeAtClose( int bFlag ) { bResizeAtClose = bFlag; } + + const char *GetFullName() { return pszFullName; } + + OGRFeature * FetchShape(int iShapeId); + int GetFeatureCountWithSpatialFilterOnly(); + + public: + OGRShapeLayer( OGRShapeDataSource* poDSIn, + const char * pszName, + SHPHandle hSHP, DBFHandle hDBF, + OGRSpatialReference *poSRS, int bSRSSet, + int bUpdate, + OGRwkbGeometryType eReqType, + char ** papszCreateOptions = NULL); + ~OGRShapeLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + + OGRFeature *GetFeature( GIntBig nFeatureId ); + OGRErr ISetFeature( OGRFeature *poFeature ); + OGRErr DeleteFeature( GIntBig nFID ); + OGRErr ICreateFeature( OGRFeature *poFeature ); + OGRErr SyncToDisk(); + + OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + GIntBig GetFeatureCount( int ); + OGRErr GetExtent(OGREnvelope *psExtent, int bForce); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr DeleteField( int iField ); + virtual OGRErr ReorderFields( int* panMap ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + + virtual int TestCapability( const char * ); + virtual void SetSpatialFilter( OGRGeometry * ); + virtual OGRErr SetAttributeFilter( const char * ); + + void AddToFileList( CPLStringList& oFileList ); + void CreateSpatialIndexAtClose( int bFlag ) { bCreateSpatialIndexAtClose = bFlag; } + void SetModificationDate(const char* pszStr); +}; + +/************************************************************************/ +/* OGRShapeDataSource */ +/************************************************************************/ + +class OGRShapeDataSource : public OGRDataSource +{ + OGRShapeLayer **papoLayers; + int nLayers; + + char *pszName; + + int bDSUpdate; + + int bSingleFileDataSource; + + OGRLayerPool* poPool; + + void AddLayer(OGRShapeLayer* poLayer); + + std::vector oVectorLayerName; + + int b2GBLimit; + + char **papszOpenOptions; + + public: + OGRShapeDataSource(); + ~OGRShapeDataSource(); + + OGRLayerPool *GetPool() { return poPool; } + + int Open( GDALOpenInfo* poOpenInfo, int bTestOpen, + int bForceSingleFileDataSource = FALSE ); + int OpenFile( const char *, int bUpdate, int bTestOpen ); + + virtual const char *GetName() { return pszName; } + + virtual int GetLayerCount(); + virtual OGRLayer *GetLayer( int ); + virtual OGRLayer *GetLayerByName(const char *); + + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); + + virtual OGRLayer *ExecuteSQL( const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + + virtual int TestCapability( const char * ); + virtual OGRErr DeleteLayer( int iLayer ); + + virtual char **GetFileList(void); + + void SetLastUsedLayer( OGRShapeLayer* poLayer ); + void UnchainLayer( OGRShapeLayer* poLayer ); + + SHPHandle DS_SHPOpen( const char * pszShapeFile, const char * pszAccess ); + DBFHandle DS_DBFOpen( const char * pszDBFFile, const char * pszAccess ); + char **GetOpenOptions() { return papszOpenOptions; } +}; + +#endif /* ndef _OGRSHAPE_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/ogrshapedatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/ogrshapedatasource.cpp new file mode 100644 index 000000000..8dec7f7e8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/ogrshapedatasource.cpp @@ -0,0 +1,1109 @@ +/****************************************************************************** + * $Id: ogrshapedatasource.cpp 28585 2015-03-01 19:42:37Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRShapeDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrshape.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include + +//#define IMMEDIATE_OPENING 1 + +CPL_CVSID("$Id: ogrshapedatasource.cpp 28585 2015-03-01 19:42:37Z rouault $"); + + +/************************************************************************/ +/* DS_SHPOpen() */ +/************************************************************************/ + +SHPHandle OGRShapeDataSource::DS_SHPOpen( const char * pszShapeFile, const char * pszAccess ) +{ + /* Do lazy shx loading for /vsicurl/ */ + if( strncmp(pszShapeFile, "/vsicurl/", strlen("/vsicurl/")) == 0 && + strcmp(pszAccess, "r") == 0 ) + pszAccess = "rl"; + SHPHandle hSHP = SHPOpenLL( pszShapeFile, pszAccess, (SAHooks*) VSI_SHP_GetHook(b2GBLimit) ); + if( hSHP != NULL ) + SHPSetFastModeReadObject( hSHP, TRUE ); + return hSHP; +} + +/************************************************************************/ +/* DS_DBFOpen() */ +/************************************************************************/ + +DBFHandle OGRShapeDataSource::DS_DBFOpen( const char * pszDBFFile, const char * pszAccess ) +{ + DBFHandle hDBF = DBFOpenLL( pszDBFFile, pszAccess, (SAHooks*) VSI_SHP_GetHook(b2GBLimit) ); + return hDBF; +} + +/************************************************************************/ +/* OGRShapeDataSource() */ +/************************************************************************/ + +OGRShapeDataSource::OGRShapeDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + bSingleFileDataSource = FALSE; + poPool = new OGRLayerPool(); + b2GBLimit = CSLTestBoolean(CPLGetConfigOption("SHAPE_2GB_LIMIT", "FALSE")); + papszOpenOptions = NULL; +} + + +/************************************************************************/ +/* ~OGRShapeDataSource() */ +/************************************************************************/ + +OGRShapeDataSource::~OGRShapeDataSource() + +{ + CPLFree( pszName ); + + for( int i = 0; i < nLayers; i++ ) + { + CPLAssert( NULL != papoLayers[i] ); + + delete papoLayers[i]; + } + + delete poPool; + + CPLFree( papoLayers ); + CSLDestroy( papszOpenOptions ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRShapeDataSource::Open( GDALOpenInfo* poOpenInfo, + int bTestOpen, int bForceSingleFileDataSource ) + +{ + CPLAssert( nLayers == 0 ); + + const char * pszNewName = poOpenInfo->pszFilename; + int bUpdate = poOpenInfo->eAccess == GA_Update; + papszOpenOptions = CSLDuplicate( poOpenInfo->papszOpenOptions ); + + pszName = CPLStrdup( pszNewName ); + + bDSUpdate = bUpdate; + + bSingleFileDataSource = bForceSingleFileDataSource; + +/* -------------------------------------------------------------------- */ +/* If bSingleFileDataSource is TRUE we don't try to do anything else. */ +/* This is only utilized when the OGRShapeDriver::Create() */ +/* method wants to create a stub OGRShapeDataSource for a */ +/* single shapefile. The driver will take care of creating the */ +/* file by callingICreateLayer(). */ +/* -------------------------------------------------------------------- */ + if( bSingleFileDataSource ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Is the given path a directory or a regular file? */ +/* -------------------------------------------------------------------- */ + if( !poOpenInfo->bStatOK ) + { + if( !bTestOpen ) + CPLError( CE_Failure, CPLE_AppDefined, + "%s is neither a file or directory, Shape access failed.\n", + pszNewName ); + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Build a list of filenames we figure are Shape files. */ +/* -------------------------------------------------------------------- */ + if( !poOpenInfo->bIsDirectory ) + { + if( !OpenFile( pszNewName, bUpdate, bTestOpen ) ) + { + if( !bTestOpen ) + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open shapefile %s.\n" + "It may be corrupt or read-only file accessed in update mode.\n", + pszNewName ); + + return FALSE; + } + + bSingleFileDataSource = TRUE; + + return TRUE; + } + else + { + char **papszCandidates = CPLReadDir( pszNewName ); + int iCan, nCandidateCount = CSLCount( papszCandidates ); + int bMightBeOldCoverage = FALSE; + std::set osLayerNameSet; + + for( iCan = 0; iCan < nCandidateCount; iCan++ ) + { + char *pszFilename; + const char *pszCandidate = papszCandidates[iCan]; + const char *pszLayerName = CPLGetBasename(pszCandidate); + CPLString osLayerName(pszLayerName); +#ifdef WIN32 + /* On Windows, as filenames are case insensitive, a shapefile layer can be made of */ + /* foo.shp and FOO.DBF, so to detect unique layer names, put them */ + /* upper case in the unique set used for detection */ + osLayerName.toupper(); +#endif + + if( EQUAL(pszCandidate,"ARC") ) + bMightBeOldCoverage = TRUE; + + if( strlen(pszCandidate) < 4 + || !EQUAL(pszCandidate+strlen(pszCandidate)-4,".shp") ) + continue; + + pszFilename = + CPLStrdup(CPLFormFilename(pszNewName, pszCandidate, NULL)); + + osLayerNameSet.insert(osLayerName); +#ifdef IMMEDIATE_OPENING + if( !OpenFile( pszFilename, bUpdate, bTestOpen ) + && !bTestOpen ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open shapefile %s.\n" + "It may be corrupt or read-only file accessed in update mode.\n", + pszFilename ); + CPLFree( pszFilename ); + CSLDestroy( papszCandidates ); + return FALSE; + } +#else + oVectorLayerName.push_back(pszFilename); +#endif + CPLFree( pszFilename ); + } + + // Try and .dbf files without apparent associated shapefiles. + for( iCan = 0; iCan < nCandidateCount; iCan++ ) + { + char *pszFilename; + const char *pszCandidate = papszCandidates[iCan]; + const char *pszLayerName = CPLGetBasename(pszCandidate); + CPLString osLayerName(pszLayerName); +#ifdef WIN32 + osLayerName.toupper(); +#endif + + // We don't consume .dbf files in a directory that looks like + // an old style Arc/Info (for PC?) that unless we found at least + // some shapefiles. See Bug 493. + if( bMightBeOldCoverage && osLayerNameSet.size() == 0 ) + continue; + + if( strlen(pszCandidate) < 4 + || !EQUAL(pszCandidate+strlen(pszCandidate)-4,".dbf") ) + continue; + + if( osLayerNameSet.find(osLayerName) != osLayerNameSet.end() ) + continue; + + // We don't want to access .dbf files with an associated .tab + // file, or it will never get recognised as a mapinfo dataset. + int iCan2, bFoundTAB = FALSE; + for( iCan2 = 0; iCan2 < nCandidateCount; iCan2++ ) + { + const char *pszCandidate2 = papszCandidates[iCan2]; + + if( EQUALN(pszCandidate2,pszLayerName,strlen(pszLayerName)) + && EQUAL(pszCandidate2 + strlen(pszLayerName), ".tab") ) + bFoundTAB = TRUE; + } + + if( bFoundTAB ) + continue; + + pszFilename = + CPLStrdup(CPLFormFilename(pszNewName, pszCandidate, NULL)); + + osLayerNameSet.insert(osLayerName); + +#ifdef IMMEDIATE_OPENING + if( !OpenFile( pszFilename, bUpdate, bTestOpen ) + && !bTestOpen ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open dbf file %s.\n" + "It may be corrupt or read-only file accessed in update mode.\n", + pszFilename ); + CPLFree( pszFilename ); + CSLDestroy( papszCandidates ); + return FALSE; + } +#else + oVectorLayerName.push_back(pszFilename); +#endif + CPLFree( pszFilename ); + } + + CSLDestroy( papszCandidates ); + +#ifdef IMMEDIATE_OPENING + int nDirLayers = nLayers; +#else + int nDirLayers = oVectorLayerName.size(); +#endif + + CPLErrorReset(); + + return nDirLayers > 0 || !bTestOpen; + } +} + +/************************************************************************/ +/* OpenFile() */ +/************************************************************************/ + +int OGRShapeDataSource::OpenFile( const char *pszNewName, int bUpdate, + int bTestOpen ) + +{ + SHPHandle hSHP; + DBFHandle hDBF; + const char *pszExtension = CPLGetExtension( pszNewName ); + + (void) bTestOpen; + + if( !EQUAL(pszExtension,"shp") && !EQUAL(pszExtension,"shx") + && !EQUAL(pszExtension,"dbf") ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* SHPOpen() should include better (CPL based) error reporting, */ +/* and we should be trying to distinquish at this point whether */ +/* failure is a result of trying to open a non-shapefile, or */ +/* whether it was a shapefile and we want to report the error */ +/* up. */ +/* */ +/* Care is taken to suppress the error and only reissue it if */ +/* we think it is appropriate. */ +/* -------------------------------------------------------------------- */ + CPLPushErrorHandler( CPLQuietErrorHandler ); + if( bUpdate ) + hSHP = DS_SHPOpen( pszNewName, "r+" ); + else + hSHP = DS_SHPOpen( pszNewName, "r" ); + CPLPopErrorHandler(); + + if( hSHP == NULL + && (!EQUAL(CPLGetExtension(pszNewName),"dbf") + || strstr(CPLGetLastErrorMsg(),".shp") == NULL) ) + { + CPLString osMsg = CPLGetLastErrorMsg(); + + CPLError( CE_Failure, CPLE_OpenFailed, "%s", osMsg.c_str() ); + + return FALSE; + } + CPLErrorReset(); + +/* -------------------------------------------------------------------- */ +/* Open the .dbf file, if it exists. To open a dbf file, the */ +/* filename has to either refer to a successfully opened shp */ +/* file or has to refer to the actual .dbf file. */ +/* -------------------------------------------------------------------- */ + if( hSHP != NULL || EQUAL(CPLGetExtension(pszNewName),"dbf") ) + { + if( bUpdate ) + { + hDBF = DS_DBFOpen( pszNewName, "r+" ); + if( hSHP != NULL && hDBF == NULL ) + { + for(int i=0;i<2;i++) + { + VSIStatBufL sStat; + const char* pszDBFName = CPLResetExtension(pszNewName, + (i == 0 ) ? "dbf" : "DBF"); + VSILFILE* fp = NULL; + if( VSIStatExL( pszDBFName, &sStat, VSI_STAT_EXISTS_FLAG) == 0 ) + { + fp = VSIFOpenL(pszDBFName, "r+"); + if (fp == NULL) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "%s exists, but cannot be opened in update mode", + pszDBFName ); + SHPClose(hSHP); + return FALSE; + } + VSIFCloseL(fp); + break; + } + } + } + } + else + hDBF = DS_DBFOpen( pszNewName, "r" ); + } + else + hDBF = NULL; + + if( hDBF == NULL && hSHP == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRShapeLayer *poLayer; + + poLayer = new OGRShapeLayer( this, pszNewName, hSHP, hDBF, NULL, FALSE, bUpdate, + wkbNone ); + poLayer->SetModificationDate( + CSLFetchNameValue( papszOpenOptions, "DBF_DATE_LAST_UPDATE" ) ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + AddLayer(poLayer); + + return TRUE; +} + + +/************************************************************************/ +/* AddLayer() */ +/************************************************************************/ + +void OGRShapeDataSource::AddLayer(OGRShapeLayer* poLayer) +{ + papoLayers = (OGRShapeLayer **) + CPLRealloc( papoLayers, sizeof(OGRShapeLayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + + /* If we reach the limit, then register all the already opened layers */ + /* Technically this code would not be necessary if there was not the */ + /* following initial test in SetLastUsedLayer() : */ + /* if (nLayers < MAX_SIMULTANEOUSLY_OPENED_LAYERS) */ + /* return; */ + if (nLayers == poPool->GetMaxSimultaneouslyOpened() && poPool->GetSize() == 0) + { + for(int i=0;iSetLastUsedLayer(papoLayers[i]); + } +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRShapeDataSource::ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ) + +{ + SHPHandle hSHP; + DBFHandle hDBF; + int nShapeType; + + /* To ensure that existing layers are created */ + GetLayerCount(); + +/* -------------------------------------------------------------------- */ +/* Check that the layer doesn't already exist. */ +/* -------------------------------------------------------------------- */ + if (GetLayerByName(pszLayerName) != NULL) + { + CPLError( CE_Failure, CPLE_AppDefined, "Layer '%s' already exists", + pszLayerName); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( !bDSUpdate ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened read-only.\n" + "New layer %s cannot be created.\n", + pszName, pszLayerName ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Figure out what type of layer we need. */ +/* -------------------------------------------------------------------- */ + if( eType == wkbUnknown || eType == wkbLineString ) + nShapeType = SHPT_ARC; + else if( eType == wkbPoint ) + nShapeType = SHPT_POINT; + else if( eType == wkbPolygon ) + nShapeType = SHPT_POLYGON; + else if( eType == wkbMultiPoint ) + nShapeType = SHPT_MULTIPOINT; + else if( eType == wkbPoint25D ) + nShapeType = SHPT_POINTZ; + else if( eType == wkbLineString25D ) + nShapeType = SHPT_ARCZ; + else if( eType == wkbMultiLineString ) + nShapeType = SHPT_ARC; + else if( eType == wkbMultiLineString25D ) + nShapeType = SHPT_ARCZ; + else if( eType == wkbPolygon25D ) + nShapeType = SHPT_POLYGONZ; + else if( eType == wkbMultiPolygon ) + nShapeType = SHPT_POLYGON; + else if( eType == wkbMultiPolygon25D ) + nShapeType = SHPT_POLYGONZ; + else if( eType == wkbMultiPoint25D ) + nShapeType = SHPT_MULTIPOINTZ; + else if( eType == wkbNone ) + nShapeType = SHPT_NULL; + else + nShapeType = -1; + +/* -------------------------------------------------------------------- */ +/* Has the application overridden this with a special creation */ +/* option? */ +/* -------------------------------------------------------------------- */ + const char *pszOverride = CSLFetchNameValue( papszOptions, "SHPT" ); + + if( pszOverride == NULL ) + /* ignore */; + else if( EQUAL(pszOverride,"POINT") ) + { + nShapeType = SHPT_POINT; + eType = wkbPoint; + } + else if( EQUAL(pszOverride,"ARC") ) + { + nShapeType = SHPT_ARC; + eType = wkbLineString; + } + else if( EQUAL(pszOverride,"POLYGON") ) + { + nShapeType = SHPT_POLYGON; + eType = wkbPolygon; + } + else if( EQUAL(pszOverride,"MULTIPOINT") ) + { + nShapeType = SHPT_MULTIPOINT; + eType = wkbMultiPoint; + } + else if( EQUAL(pszOverride,"POINTZ") ) + { + nShapeType = SHPT_POINTZ; + eType = wkbPoint25D; + } + else if( EQUAL(pszOverride,"ARCZ") ) + { + nShapeType = SHPT_ARCZ; + eType = wkbLineString25D; + } + else if( EQUAL(pszOverride,"POLYGONZ") ) + { + nShapeType = SHPT_POLYGONZ; + eType = wkbPolygon25D; + } + else if( EQUAL(pszOverride,"MULTIPOINTZ") ) + { + nShapeType = SHPT_MULTIPOINTZ; + eType = wkbMultiPoint25D; + } + else if( EQUAL(pszOverride,"NONE") || EQUAL(pszOverride,"NULL") ) + { + nShapeType = SHPT_NULL; + eType = wkbNone; + } + else + { + CPLError( CE_Failure, CPLE_NotSupported, + "Unknown SHPT value of `%s' passed to Shapefile layer\n" + "creation. Creation aborted.\n", + pszOverride ); + + return NULL; + } + + if( nShapeType == -1 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Geometry type of `%s' not supported in shapefiles.\n" + "Type can be overridden with a layer creation option\n" + "of SHPT=POINT/ARC/POLYGON/MULTIPOINT/POINTZ/ARCZ/POLYGONZ/MULTIPOINTZ.\n", + OGRGeometryTypeToName(eType) ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* What filename do we use, excluding the extension? */ +/* -------------------------------------------------------------------- */ + char *pszFilenameWithoutExt; + + if( bSingleFileDataSource && nLayers == 0 ) + { + char *pszPath = CPLStrdup(CPLGetPath(pszName)); + char *pszFBasename = CPLStrdup(CPLGetBasename(pszName)); + + pszFilenameWithoutExt = CPLStrdup(CPLFormFilename(pszPath, pszFBasename, NULL)); + + CPLFree( pszFBasename ); + CPLFree( pszPath ); + } + else if( bSingleFileDataSource ) + { + /* This is a very weird use case : the user creates/open a datasource */ + /* made of a single shapefile 'foo.shp' and wants to add a new layer */ + /* to it, 'bar'. So we create a new shapefile 'bar.shp' in the same */ + /* directory as 'foo.shp' */ + /* So technically, we will not be any longer a single file */ + /* datasource ... Ahem ahem */ + char *pszPath = CPLStrdup(CPLGetPath(pszName)); + pszFilenameWithoutExt = CPLStrdup(CPLFormFilename(pszPath,pszLayerName,NULL)); + CPLFree( pszPath ); + } + else + pszFilenameWithoutExt = CPLStrdup(CPLFormFilename(pszName,pszLayerName,NULL)); + +/* -------------------------------------------------------------------- */ +/* Create the shapefile. */ +/* -------------------------------------------------------------------- */ + char *pszFilename; + + int b2GBLimit = CSLTestBoolean(CSLFetchNameValueDef( papszOptions, "2GB_LIMIT", "FALSE" )); + + if( nShapeType != SHPT_NULL ) + { + pszFilename = CPLStrdup(CPLFormFilename( NULL, pszFilenameWithoutExt, "shp" )); + + hSHP = SHPCreateLL( pszFilename, nShapeType, (SAHooks*) VSI_SHP_GetHook(b2GBLimit) ); + + if( hSHP == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open Shapefile `%s'.\n", + pszFilename ); + CPLFree( pszFilename ); + CPLFree( pszFilenameWithoutExt ); + return NULL; + } + + SHPSetFastModeReadObject( hSHP, TRUE ); + + CPLFree( pszFilename ); + } + else + hSHP = NULL; + +/* -------------------------------------------------------------------- */ +/* Has a specific LDID been specified by the caller? */ +/* -------------------------------------------------------------------- */ + const char *pszLDID = CSLFetchNameValue( papszOptions, "ENCODING" ); + +/* -------------------------------------------------------------------- */ +/* Create a DBF file. */ +/* -------------------------------------------------------------------- */ + pszFilename = CPLStrdup(CPLFormFilename( NULL, pszFilenameWithoutExt, "dbf" )); + + if( pszLDID != NULL ) + hDBF = DBFCreateLL( pszFilename, pszLDID, (SAHooks*) VSI_SHP_GetHook(b2GBLimit) ); + else + hDBF = DBFCreateLL( pszFilename, "LDID/87",(SAHooks*) VSI_SHP_GetHook(b2GBLimit) ); + + if( hDBF == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open Shape DBF file `%s'.\n", + pszFilename ); + CPLFree( pszFilename ); + CPLFree( pszFilenameWithoutExt ); + SHPClose(hSHP); + return NULL; + } + + CPLFree( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Create the .prj file, if required. */ +/* -------------------------------------------------------------------- */ + if( poSRS != NULL ) + { + char *pszWKT = NULL; + CPLString osPrjFile = CPLFormFilename( NULL, pszFilenameWithoutExt, "prj"); + VSILFILE *fp; + + /* the shape layer needs it's own copy */ + poSRS = poSRS->Clone(); + poSRS->morphToESRI(); + + if( poSRS->exportToWkt( &pszWKT ) == OGRERR_NONE + && (fp = VSIFOpenL( osPrjFile, "wt" )) != NULL ) + { + VSIFWriteL( pszWKT, strlen(pszWKT), 1, fp ); + VSIFCloseL( fp ); + } + + CPLFree( pszWKT ); + + poSRS->morphFromESRI(); + } + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRShapeLayer *poLayer; + + /* OGRShapeLayer constructor expects a filename with an extension (that could be */ + /* random actually), otherwise this is going to cause problems with layer */ + /* names that have a dot (not speaking about the one before the shp) */ + pszFilename = CPLStrdup(CPLFormFilename( NULL, pszFilenameWithoutExt, "shp" )); + + poLayer = new OGRShapeLayer( this, pszFilename, hSHP, hDBF, poSRS, TRUE, TRUE, + eType ); + + CPLFree( pszFilenameWithoutExt ); + CPLFree( pszFilename ); + + poLayer->SetResizeAtClose( CSLFetchBoolean( papszOptions, "RESIZE", FALSE ) ); + poLayer->CreateSpatialIndexAtClose( CSLFetchBoolean( papszOptions, "SPATIAL_INDEX", FALSE ) ); + poLayer->SetModificationDate( + CSLFetchNameValue( papszOptions, "DBF_DATE_LAST_UPDATE" ) ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + AddLayer(poLayer); + + return poLayer; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRShapeDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return bDSUpdate; + else if( EQUAL(pszCap,ODsCDeleteLayer) ) + return bDSUpdate; + else + return FALSE; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int OGRShapeDataSource::GetLayerCount() + +{ +#ifndef IMMEDIATE_OPENING + if (oVectorLayerName.size() != 0) + { + for(size_t i = 0; i < oVectorLayerName.size(); i++) + { + const char* pszFilename = oVectorLayerName[i].c_str(); + const char* pszLayerName = CPLGetBasename(pszFilename); + + int j; + for(j=0;jGetName(), pszLayerName) == 0) + break; + } + if (j < nLayers) + continue; + + if( !OpenFile( pszFilename, bDSUpdate, TRUE ) ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open file %s.\n" + "It may be corrupt or read-only file accessed in update mode.\n", + pszFilename ); + } + } + oVectorLayerName.resize(0); + } +#endif + + return nLayers; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRShapeDataSource::GetLayer( int iLayer ) + +{ + /* To ensure that existing layers are created */ + GetLayerCount(); + + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer *OGRShapeDataSource::GetLayerByName(const char * pszLayerNameIn) +{ +#ifndef IMMEDIATE_OPENING + if (oVectorLayerName.size() != 0) + { + int j; + for(j=0;jGetName(), pszLayerNameIn) == 0) + { + return papoLayers[j]; + } + } + + size_t i; + for(j = 0; j < 2; j++) + { + for(i = 0; i < oVectorLayerName.size(); i++) + { + const char* pszFilename = oVectorLayerName[i].c_str(); + const char* pszLayerName = CPLGetBasename(pszFilename); + + if (j == 0) + { + if (strcmp(pszLayerName, pszLayerNameIn) != 0) + continue; + } + else + { + if ( !EQUAL(pszLayerName, pszLayerNameIn) ) + continue; + } + + if( !OpenFile( pszFilename, bDSUpdate, TRUE ) ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open file %s.\n" + "It may be corrupt or read-only file accessed in update mode.\n", + pszFilename ); + return NULL; + } + else + { + return papoLayers[nLayers - 1]; + } + } + } + + return NULL; + } +#endif + + return OGRDataSource::GetLayerByName(pszLayerNameIn); +} + +/************************************************************************/ +/* ExecuteSQL() */ +/* */ +/* We override this to provide special handling of CREATE */ +/* SPATIAL INDEX commands. Support forms are: */ +/* */ +/* CREATE SPATIAL INDEX ON layer_name [DEPTH n] */ +/* DROP SPATIAL INDEX ON layer_name */ +/* REPACK layer_name */ +/* RECOMPUTE EXTENT ON layer_name */ +/************************************************************************/ + +OGRLayer * OGRShapeDataSource::ExecuteSQL( const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ +/* ==================================================================== */ +/* Handle command to drop a spatial index. */ +/* ==================================================================== */ + if( EQUALN(pszStatement, "REPACK ", 7) ) + { + OGRShapeLayer *poLayer = (OGRShapeLayer *) + GetLayerByName( pszStatement + 7 ); + + if( poLayer != NULL ) + { + if( poLayer->Repack() != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "REPACK of layer '%s' failed.", + pszStatement + 7 ); + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "No such layer as '%s' in REPACK.", + pszStatement + 7 ); + } + return NULL; + } + +/* ==================================================================== */ +/* Handle command to shrink columns to their minimum size. */ +/* ==================================================================== */ + if( EQUALN(pszStatement, "RESIZE ", 7) ) + { + OGRShapeLayer *poLayer = (OGRShapeLayer *) + GetLayerByName( pszStatement + 7 ); + + if( poLayer != NULL ) + poLayer->ResizeDBF(); + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "No such layer as '%s' in RESIZE.", + pszStatement + 7 ); + } + return NULL; + } + +/* ==================================================================== */ +/* Handle command to recompute extent */ +/* ==================================================================== */ + if( EQUALN(pszStatement, "RECOMPUTE EXTENT ON ", 20) ) + { + OGRShapeLayer *poLayer = (OGRShapeLayer *) + GetLayerByName( pszStatement + 20 ); + + if( poLayer != NULL ) + poLayer->RecomputeExtent(); + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "No such layer as '%s' in RECOMPUTE EXTENT.", + pszStatement + 20 ); + } + return NULL; + } + +/* ==================================================================== */ +/* Handle command to drop a spatial index. */ +/* ==================================================================== */ + if( EQUALN(pszStatement, "DROP SPATIAL INDEX ON ", 22) ) + { + OGRShapeLayer *poLayer = (OGRShapeLayer *) + GetLayerByName( pszStatement + 22 ); + + if( poLayer != NULL ) + poLayer->DropSpatialIndex(); + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "No such layer as '%s' in DROP SPATIAL INDEX.", + pszStatement + 22 ); + } + return NULL; + } + +/* ==================================================================== */ +/* Handle all comands except spatial index creation generically. */ +/* ==================================================================== */ + if( !EQUALN(pszStatement,"CREATE SPATIAL INDEX ON ",24) ) + { + char **papszTokens = CSLTokenizeString( pszStatement ); + if( CSLCount(papszTokens) >=4 + && (EQUAL(papszTokens[0],"CREATE") || EQUAL(papszTokens[0],"DROP")) + && EQUAL(papszTokens[1],"INDEX") + && EQUAL(papszTokens[2],"ON") ) + { + OGRShapeLayer *poLayer = (OGRShapeLayer *) GetLayerByName(papszTokens[3]); + if (poLayer != NULL) + poLayer->InitializeIndexSupport( poLayer->GetFullName() ); + } + CSLDestroy( papszTokens ); + + return OGRDataSource::ExecuteSQL( pszStatement, poSpatialFilter, + pszDialect ); + } + +/* -------------------------------------------------------------------- */ +/* Parse into keywords. */ +/* -------------------------------------------------------------------- */ + char **papszTokens = CSLTokenizeString( pszStatement ); + + if( CSLCount(papszTokens) < 5 + || !EQUAL(papszTokens[0],"CREATE") + || !EQUAL(papszTokens[1],"SPATIAL") + || !EQUAL(papszTokens[2],"INDEX") + || !EQUAL(papszTokens[3],"ON") + || CSLCount(papszTokens) > 7 + || (CSLCount(papszTokens) == 7 && !EQUAL(papszTokens[5],"DEPTH")) ) + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax error in CREATE SPATIAL INDEX command.\n" + "Was '%s'\n" + "Should be of form 'CREATE SPATIAL INDEX ON [DEPTH ]'", + pszStatement ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Get depth if provided. */ +/* -------------------------------------------------------------------- */ + int nDepth = 0; + if( CSLCount(papszTokens) == 7 ) + nDepth = atoi(papszTokens[6]); + +/* -------------------------------------------------------------------- */ +/* What layer are we operating on. */ +/* -------------------------------------------------------------------- */ + OGRShapeLayer *poLayer = (OGRShapeLayer *) GetLayerByName(papszTokens[4]); + + if( poLayer == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s not recognised.", + papszTokens[4] ); + CSLDestroy( papszTokens ); + return NULL; + } + + CSLDestroy( papszTokens ); + + poLayer->CreateSpatialIndex( nDepth ); + return NULL; +} + + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +OGRErr OGRShapeDataSource::DeleteLayer( int iLayer ) + +{ + char *pszFilename; + +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( !bDSUpdate ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened read-only.\n" + "Layer %d cannot be deleted.\n", + pszName, iLayer ); + + return OGRERR_FAILURE; + } + + if( iLayer < 0 || iLayer >= nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %d not in legal range of 0 to %d.", + iLayer, nLayers-1 ); + return OGRERR_FAILURE; + } + + OGRShapeLayer* poLayerToDelete = (OGRShapeLayer*) papoLayers[iLayer]; + + pszFilename = CPLStrdup(poLayerToDelete->GetFullName()); + + delete poLayerToDelete; + + while( iLayer < nLayers - 1 ) + { + papoLayers[iLayer] = papoLayers[iLayer+1]; + iLayer++; + } + + nLayers--; + + VSIUnlink( CPLResetExtension(pszFilename, "shp") ); + VSIUnlink( CPLResetExtension(pszFilename, "shx") ); + VSIUnlink( CPLResetExtension(pszFilename, "dbf") ); + VSIUnlink( CPLResetExtension(pszFilename, "prj") ); + VSIUnlink( CPLResetExtension(pszFilename, "qix") ); + + CPLFree( pszFilename ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetLastUsedLayer() */ +/************************************************************************/ + +void OGRShapeDataSource::SetLastUsedLayer( OGRShapeLayer* poLayer ) +{ + /* We could remove that check and things would still work in */ + /* 99.99% cases */ + /* The only rationale for that test is to avoid breaking applications */ + /* that would deal with layers of the same datasource in different */ + /* threads. In GDAL < 1.9.0, this would work in most cases I can */ + /* imagine as shapefile layers are pretty much independant from each */ + /* others (although it has never been guaranteed to be a valid use case, */ + /* and the shape driver is likely more the exception than the rule in */ + /* permitting accessing layers from different threads !) */ + /* Anyway the LRU list mechanism leaves the door open to concurrent accesses to it */ + /* so when the datasource has not many layers, we don't try to build the */ + /* LRU list to avoid concurrency issues. I haven't bothered making the analysis */ + /* of how a mutex could be used to protect that (my intuition is that it would */ + /* need to be placed at the beginning of OGRShapeLayer::TouchLayer() ) */ + if (nLayers < poPool->GetMaxSimultaneouslyOpened()) + return; + + poPool->SetLastUsedLayer(poLayer); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char** OGRShapeDataSource::GetFileList() +{ + CPLStringList oFileList; + GetLayerCount(); + for(int i=0;iAddToFileList(oFileList); + } + return oFileList.StealList(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/ogrshapedriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/ogrshapedriver.cpp new file mode 100644 index 000000000..21f460cfa --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/ogrshapedriver.cpp @@ -0,0 +1,291 @@ +/****************************************************************************** + * $Id: ogrshapedriver.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRShapeDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrshape.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrshapedriver.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +static int OGRShapeDriverIdentify( GDALOpenInfo* poOpenInfo ) +{ + /* Files not ending with .shp, .shx or .dbf are not handled by this driver */ + if( !poOpenInfo->bStatOK ) + return FALSE; + if( poOpenInfo->bIsDirectory ) + return -1; /* unsure */ + if( poOpenInfo->fpL != NULL && + (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "SHP") || + EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "SHX")) ) + { + return memcmp(poOpenInfo->pabyHeader, "\x00\x00\x27\x0A", 4) == 0 || + memcmp(poOpenInfo->pabyHeader, "\x00\x00\x27\x0D", 4) == 0; + } + if( poOpenInfo->fpL != NULL && EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "DBF") ) + { + if( poOpenInfo->nHeaderBytes < 32 ) + return FALSE; + const GByte* pabyBuf = poOpenInfo->pabyHeader; + unsigned int nHeadLen = pabyBuf[8] + pabyBuf[9]*256; + unsigned int nRecordLength = pabyBuf[10] + pabyBuf[11]*256; + if( nHeadLen < 32 ) + return FALSE; + if( (nHeadLen % 32) != 0 && (nHeadLen % 32) != 1 ) + return FALSE; + unsigned int nFields = (nHeadLen - 32) / 32; + if( nRecordLength < nFields ) + return FALSE; + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRShapeDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + OGRShapeDataSource *poDS; + + if( OGRShapeDriverIdentify(poOpenInfo) == FALSE ) + return NULL; + + poDS = new OGRShapeDataSource(); + + if( !poDS->Open( poOpenInfo, TRUE ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRShapeDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + CPL_UNUSED char **papszOptions ) +{ + VSIStatBuf stat; + int bSingleNewFile = FALSE; + +/* -------------------------------------------------------------------- */ +/* Is the target a valid existing directory? */ +/* -------------------------------------------------------------------- */ + if( CPLStat( pszName, &stat ) == 0 ) + { + if( !VSI_ISDIR(stat.st_mode) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s is not a directory.\n", + pszName ); + + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Does it end in the extension .shp indicating the user likely */ +/* wants to create a single file set? */ +/* -------------------------------------------------------------------- */ + else if( EQUAL(CPLGetExtension(pszName),"shp") + || EQUAL(CPLGetExtension(pszName),"dbf") ) + { + bSingleNewFile = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise try to create a new directory. */ +/* -------------------------------------------------------------------- */ + else + { + if( VSIMkdir( pszName, 0755 ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to create directory %s\n" + "for shapefile datastore.\n", + pszName ); + + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Return a new OGRDataSource() */ +/* -------------------------------------------------------------------- */ + OGRShapeDataSource *poDS = NULL; + + poDS = new OGRShapeDataSource(); + + GDALOpenInfo oOpenInfo( pszName, GA_Update ); + if( !poDS->Open( &oOpenInfo, FALSE, bSingleNewFile ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +static CPLErr OGRShapeDriverDelete( const char *pszDataSource ) + +{ + int iExt; + VSIStatBufL sStatBuf; + static const char *apszExtensions[] = + { "shp", "shx", "dbf", "sbn", "sbx", "prj", "idm", "ind", + "qix", "cpg", NULL }; + + if( VSIStatL( pszDataSource, &sStatBuf ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s does not appear to be a file or directory.", + pszDataSource ); + + return CE_Failure; + } + + if( VSI_ISREG(sStatBuf.st_mode) + && (EQUAL(CPLGetExtension(pszDataSource),"shp") + || EQUAL(CPLGetExtension(pszDataSource),"shx") + || EQUAL(CPLGetExtension(pszDataSource),"dbf")) ) + { + for( iExt=0; apszExtensions[iExt] != NULL; iExt++ ) + { + const char *pszFile = CPLResetExtension(pszDataSource, + apszExtensions[iExt] ); + if( VSIStatL( pszFile, &sStatBuf ) == 0 ) + VSIUnlink( pszFile ); + } + } + else if( VSI_ISDIR(sStatBuf.st_mode) ) + { + char **papszDirEntries = CPLReadDir( pszDataSource ); + int iFile; + + for( iFile = 0; + papszDirEntries != NULL && papszDirEntries[iFile] != NULL; + iFile++ ) + { + if( CSLFindString( (char **) apszExtensions, + CPLGetExtension(papszDirEntries[iFile])) != -1) + { + VSIUnlink( CPLFormFilename( pszDataSource, + papszDirEntries[iFile], + NULL ) ); + } + } + + CSLDestroy( papszDirEntries ); + + VSIRmdir( pszDataSource ); + } + + return CE_None; +} + +/************************************************************************/ +/* RegisterOGRShape() */ +/************************************************************************/ + +void RegisterOGRShape() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "ESRI Shapefile" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "ESRI Shapefile" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "ESRI Shapefile" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "shp" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "shp dbf" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_shape.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, "" ); + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" " +" "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRShapeDriverOpen; + poDriver->pfnIdentify = OGRShapeDriverIdentify; + poDriver->pfnCreate = OGRShapeDriverCreate; + poDriver->pfnDelete = OGRShapeDriverDelete; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp new file mode 100644 index 000000000..c172754e5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp @@ -0,0 +1,2914 @@ +/****************************************************************************** + * $Id: ogrshapelayer.cpp 29066 2015-04-30 08:50:05Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRShapeLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrshape.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "cpl_time.h" + +#if defined(_WIN32_WCE) +# include +#endif + +#define FD_OPENED 0 +#define FD_CLOSED 1 +#define FD_CANNOT_REOPEN 2 + +#define UNSUPPORTED_OP_READ_ONLY "%s : unsupported operation on a read-only datasource." + +CPL_CVSID("$Id: ogrshapelayer.cpp 29066 2015-04-30 08:50:05Z rouault $"); + +/************************************************************************/ +/* OGRShapeLayer() */ +/************************************************************************/ + +OGRShapeLayer::OGRShapeLayer( OGRShapeDataSource* poDSIn, + const char * pszFullNameIn, + SHPHandle hSHPIn, DBFHandle hDBFIn, + OGRSpatialReference *poSRSIn, int bSRSSetIn, + int bUpdate, + OGRwkbGeometryType eReqType, + char ** papszCreateOptions ) : + OGRAbstractProxiedLayer(poDSIn->GetPool()) + +{ + poDS = poDSIn; + + pszFullName = CPLStrdup(pszFullNameIn); + + hSHP = hSHPIn; + hDBF = hDBFIn; + bUpdateAccess = bUpdate; + + iNextShapeId = 0; + iMatchingFID = 0; + panMatchingFIDs = NULL; + + nSpatialFIDCount = 0; + panSpatialFIDs = NULL; + m_poFilterGeomLastValid = NULL; + + bCheckedForQIX = FALSE; + hQIX = NULL; + + bCheckedForSBN = FALSE; + hSBN = NULL; + + bSbnSbxDeleted = FALSE; + + bHeaderDirty = FALSE; + bSHPNeedsRepack = FALSE; + + if( hSHP != NULL ) + { + nTotalShapeCount = hSHP->nRecords; + if( hDBF != NULL && hDBF->nRecords != nTotalShapeCount ) + { + CPLDebug("Shape", "Inconsistent record number in .shp (%d) and in .dbf (%d)", + hSHP->nRecords, hDBF->nRecords); + } + } + else + nTotalShapeCount = hDBF->nRecords; + + eRequestedGeomType = eReqType; + + bTruncationWarningEmitted = FALSE; + + bHSHPWasNonNULL = hSHPIn != NULL; + bHDBFWasNonNULL = hDBFIn != NULL; + eFileDescriptorsState = FD_OPENED; + TouchLayer(); + + bResizeAtClose = FALSE; + bCreateSpatialIndexAtClose = FALSE; + + if( hDBF != NULL && hDBF->pszCodePage != NULL ) + { + CPLDebug( "Shape", "DBF Codepage = %s for %s", + hDBF->pszCodePage, pszFullName ); + + // Not too sure about this, but it seems like better than nothing. + osEncoding = ConvertCodePage( hDBF->pszCodePage ); + } + + if( hDBF != NULL ) + { + if( !(hDBF->nUpdateYearSince1900 == 95 && + hDBF->nUpdateMonth == 7 && + hDBF->nUpdateDay == 26) ) + { + SetMetadataItem( "DBF_DATE_LAST_UPDATE", CPLSPrintf("%04d-%02d-%02d", + hDBF->nUpdateYearSince1900 + 1900, + hDBF->nUpdateMonth, hDBF->nUpdateDay) ); + } + struct tm tm; + CPLUnixTimeToYMDHMS(time(NULL), &tm); + DBFSetLastModifiedDate( hDBF, tm.tm_year, + tm.tm_mon + 1, tm.tm_mday ); + } + + const char* pszShapeEncoding = NULL; + pszShapeEncoding = CSLFetchNameValue(poDS->GetOpenOptions(), "ENCODING"); + if( pszShapeEncoding == NULL && osEncoding == "") + pszShapeEncoding = CSLFetchNameValue( papszCreateOptions, "ENCODING" ); + if( pszShapeEncoding == NULL ) + pszShapeEncoding = CPLGetConfigOption( "SHAPE_ENCODING", NULL ); + if( pszShapeEncoding != NULL ) + osEncoding = pszShapeEncoding; + + if( osEncoding != "" ) + { + CPLDebug( "Shape", "Treating as encoding '%s'.", osEncoding.c_str() ); + + if (!TestCapability(OLCStringsAsUTF8)) + { + CPLDebug( "Shape", "Cannot recode from '%s'. Disabling recoding", osEncoding.c_str() ); + osEncoding = ""; + } + } + + poFeatureDefn = SHPReadOGRFeatureDefn( CPLGetBasename(pszFullName), + hSHP, hDBF, osEncoding, + CSLFetchBoolean(poDS->GetOpenOptions(), "ADJUST_TYPE", FALSE) ); + + /* To make sure that GetLayerDefn()->GetGeomFieldDefn(0)->GetSpatialRef() == GetSpatialRef() */ + OGRwkbGeometryType eGeomType = poFeatureDefn->GetGeomType(); + if( eGeomType != wkbNone ) + { + OGRShapeGeomFieldDefn* poGeomFieldDefn = + new OGRShapeGeomFieldDefn(pszFullName, eGeomType, bSRSSetIn, poSRSIn); + poFeatureDefn->SetGeomType(wkbNone); + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + } + else if( bSRSSetIn && poSRSIn != NULL ) + poSRSIn->Release(); + SetDescription( poFeatureDefn->GetName() ); + bRewindOnWrite = CSLTestBoolean(CPLGetConfigOption( "SHAPE_REWIND_ON_WRITE", "YES" )); +} + +/************************************************************************/ +/* ~OGRShapeLayer() */ +/************************************************************************/ + +OGRShapeLayer::~OGRShapeLayer() + +{ + if( bResizeAtClose && hDBF != NULL ) + { + ResizeDBF(); + } + if( bCreateSpatialIndexAtClose && hSHP != NULL ) + { + CreateSpatialIndex(0); + } + + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "Shape", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + ClearMatchingFIDs(); + ClearSpatialFIDs(); + + CPLFree( pszFullName ); + + if( poFeatureDefn != NULL ) + poFeatureDefn->Release(); + + if( hDBF != NULL ) + DBFClose( hDBF ); + + if( hSHP != NULL ) + SHPClose( hSHP ); + + if( hQIX != NULL ) + SHPCloseDiskTree( hQIX ); + + if( hSBN != NULL ) + SBNCloseDiskTree( hSBN ); +} + + +/************************************************************************/ +/* SetModificationDate() */ +/************************************************************************/ + +void OGRShapeLayer::SetModificationDate(const char* pszStr) +{ + if( hDBF && pszStr ) + { + int year, month, day; + if ((sscanf(pszStr, "%04d-%02d-%02d", &year, &month, &day) == 3 || + sscanf(pszStr, "%04d/%02d/%02d", &year, &month, &day) == 3) && + (year >= 1900 && year <= 1900 + 255 && month >= 1 && month <= 12 && + day >= 1 && day <= 31)) + { + DBFSetLastModifiedDate( hDBF, year - 1900, month, day ); + } + } +} + +/************************************************************************/ +/* ConvertCodePage() */ +/************************************************************************/ + +CPLString OGRShapeLayer::ConvertCodePage( const char *pszCodePage ) + +{ + CPLString osEncoding; + + if( pszCodePage == NULL ) + return osEncoding; + + if( EQUALN(pszCodePage,"LDID/",5) ) + { + int nCP = -1; // windows code page. + + //http://www.autopark.ru/ASBProgrammerGuide/DBFSTRUC.HTM + switch( atoi(pszCodePage+5) ) + { + case 1: nCP = 437; break; + case 2: nCP = 850; break; + case 3: nCP = 1252; break; + case 4: nCP = 10000; break; + case 8: nCP = 865; break; + case 10: nCP = 850; break; + case 11: nCP = 437; break; + case 13: nCP = 437; break; + case 14: nCP = 850; break; + case 15: nCP = 437; break; + case 16: nCP = 850; break; + case 17: nCP = 437; break; + case 18: nCP = 850; break; + case 19: nCP = 932; break; + case 20: nCP = 850; break; + case 21: nCP = 437; break; + case 22: nCP = 850; break; + case 23: nCP = 865; break; + case 24: nCP = 437; break; + case 25: nCP = 437; break; + case 26: nCP = 850; break; + case 27: nCP = 437; break; + case 28: nCP = 863; break; + case 29: nCP = 850; break; + case 31: nCP = 852; break; + case 34: nCP = 852; break; + case 35: nCP = 852; break; + case 36: nCP = 860; break; + case 37: nCP = 850; break; + case 38: nCP = 866; break; + case 55: nCP = 850; break; + case 64: nCP = 852; break; + case 77: nCP = 936; break; + case 78: nCP = 949; break; + case 79: nCP = 950; break; + case 80: nCP = 874; break; + case 87: return CPL_ENC_ISO8859_1; + case 88: nCP = 1252; break; + case 89: nCP = 1252; break; + case 100: nCP = 852; break; + case 101: nCP = 866; break; + case 102: nCP = 865; break; + case 103: nCP = 861; break; + case 104: nCP = 895; break; + case 105: nCP = 620; break; + case 106: nCP = 737; break; + case 107: nCP = 857; break; + case 108: nCP = 863; break; + case 120: nCP = 950; break; + case 121: nCP = 949; break; + case 122: nCP = 936; break; + case 123: nCP = 932; break; + case 124: nCP = 874; break; + case 134: nCP = 737; break; + case 135: nCP = 852; break; + case 136: nCP = 857; break; + case 150: nCP = 10007; break; + case 151: nCP = 10029; break; + case 200: nCP = 1250; break; + case 201: nCP = 1251; break; + case 202: nCP = 1254; break; + case 203: nCP = 1253; break; + case 204: nCP = 1257; break; + default: break; + } + + if( nCP != -1 ) + { + osEncoding.Printf( "CP%d", nCP ); + return osEncoding; + } + } + + // From the CPG file + // http://resources.arcgis.com/fr/content/kbase?fa=articleShow&d=21106 + + if( (atoi(pszCodePage) >= 437 && atoi(pszCodePage) <= 950) + || (atoi(pszCodePage) >= 1250 && atoi(pszCodePage) <= 1258) ) + { + osEncoding.Printf( "CP%d", atoi(pszCodePage) ); + return osEncoding; + } + if( EQUALN(pszCodePage,"8859",4) ) + { + if( pszCodePage[4] == '-' ) + osEncoding.Printf( "ISO-8859-%s", pszCodePage + 5 ); + else + osEncoding.Printf( "ISO-8859-%s", pszCodePage + 4 ); + return osEncoding; + } + if( EQUALN(pszCodePage,"UTF-8",5) ) + return CPL_ENC_UTF8; + + // try just using the CPG value directly. Works for stuff like Big5. + return pszCodePage; +} + +/************************************************************************/ +/* CheckForQIX() */ +/************************************************************************/ + +int OGRShapeLayer::CheckForQIX() + +{ + const char *pszQIXFilename; + + if( bCheckedForQIX ) + return hQIX != NULL; + + pszQIXFilename = CPLResetExtension( pszFullName, "qix" ); + + hQIX = SHPOpenDiskTree( pszQIXFilename, NULL ); + + bCheckedForQIX = TRUE; + + return hQIX != NULL; +} + +/************************************************************************/ +/* CheckForSBN() */ +/************************************************************************/ + +int OGRShapeLayer::CheckForSBN() + +{ + const char *pszSBNFilename; + + if( bCheckedForSBN ) + return hSBN != NULL; + + pszSBNFilename = CPLResetExtension( pszFullName, "sbn" ); + + hSBN = SBNOpenDiskTree( pszSBNFilename, NULL ); + + bCheckedForSBN = TRUE; + + return hSBN != NULL; +} + +/************************************************************************/ +/* ScanIndices() */ +/* */ +/* Utilize optional spatial and attribute indices if they are */ +/* available. */ +/************************************************************************/ + +int OGRShapeLayer::ScanIndices() + +{ + iMatchingFID = 0; + +/* -------------------------------------------------------------------- */ +/* Utilize attribute index if appropriate. */ +/* -------------------------------------------------------------------- */ + if( m_poAttrQuery != NULL ) + { + CPLAssert( panMatchingFIDs == NULL ); + + InitializeIndexSupport( pszFullName ); + + panMatchingFIDs = m_poAttrQuery->EvaluateAgainstIndices( this, + NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Check for spatial index if we have a spatial query. */ +/* -------------------------------------------------------------------- */ + + if( m_poFilterGeom == NULL || hSHP == NULL ) + return TRUE; + + OGREnvelope oSpatialFilterEnvelope; + int bTryQIXorSBN = TRUE; + + m_poFilterGeom->getEnvelope( &oSpatialFilterEnvelope ); + + OGREnvelope oLayerExtent; + if (GetExtent(&oLayerExtent, TRUE) == OGRERR_NONE) + { + if (oSpatialFilterEnvelope.Contains(oLayerExtent)) + { + // The spatial filter is larger than the layer extent. No use of .qix file for now + return TRUE; + } + else if (!oSpatialFilterEnvelope.Intersects(oLayerExtent)) + { + /* No intersection : no need to check for .qix or .sbn */ + bTryQIXorSBN = FALSE; + + /* Set an empty result for spatial FIDs */ + free(panSpatialFIDs); + panSpatialFIDs = (int*) calloc(1, sizeof(int)); + nSpatialFIDCount = 0; + + delete m_poFilterGeomLastValid; + m_poFilterGeomLastValid = m_poFilterGeom->clone(); + } + } + + if( bTryQIXorSBN ) + { + if( !bCheckedForQIX ) + CheckForQIX(); + if( hQIX == NULL && !bCheckedForSBN ) + CheckForSBN(); + } + +/* -------------------------------------------------------------------- */ +/* Compute spatial index if appropriate. */ +/* -------------------------------------------------------------------- */ + if( bTryQIXorSBN && (hQIX != NULL || hSBN != NULL) && panSpatialFIDs == NULL ) + { + double adfBoundsMin[4], adfBoundsMax[4]; + + adfBoundsMin[0] = oSpatialFilterEnvelope.MinX; + adfBoundsMin[1] = oSpatialFilterEnvelope.MinY; + adfBoundsMin[2] = 0.0; + adfBoundsMin[3] = 0.0; + adfBoundsMax[0] = oSpatialFilterEnvelope.MaxX; + adfBoundsMax[1] = oSpatialFilterEnvelope.MaxY; + adfBoundsMax[2] = 0.0; + adfBoundsMax[3] = 0.0; + + if( hQIX != NULL ) + panSpatialFIDs = SHPSearchDiskTreeEx( hQIX, + adfBoundsMin, adfBoundsMax, + &nSpatialFIDCount ); + else + panSpatialFIDs = SBNSearchDiskTree( hSBN, + adfBoundsMin, adfBoundsMax, + &nSpatialFIDCount ); + + CPLDebug( "SHAPE", "Used spatial index, got %d matches.", + nSpatialFIDCount ); + + delete m_poFilterGeomLastValid; + m_poFilterGeomLastValid = m_poFilterGeom->clone(); + } + +/* -------------------------------------------------------------------- */ +/* Use spatial index if appropriate. */ +/* -------------------------------------------------------------------- */ + if( panSpatialFIDs != NULL ) + { + // Use resulting list as matching FID list (but reallocate and + // terminate with OGRNullFID). + + if( panMatchingFIDs == NULL ) + { + int i; + + panMatchingFIDs = (GIntBig *) + CPLMalloc(sizeof(GIntBig) * (nSpatialFIDCount+1) ); + for( i = 0; i < nSpatialFIDCount; i++ ) + panMatchingFIDs[i] = (GIntBig) panSpatialFIDs[i]; + panMatchingFIDs[nSpatialFIDCount] = OGRNullFID; + } + + // Cull attribute index matches based on those in the spatial index + // result set. We assume that the attribute results are in sorted + // order. + else + { + int iRead, iWrite=0, iSpatial=0; + + for( iRead = 0; panMatchingFIDs[iRead] != OGRNullFID; iRead++ ) + { + while( iSpatial < nSpatialFIDCount + && panSpatialFIDs[iSpatial] < panMatchingFIDs[iRead] ) + iSpatial++; + + if( iSpatial == nSpatialFIDCount ) + continue; + + if( panSpatialFIDs[iSpatial] == panMatchingFIDs[iRead] ) + panMatchingFIDs[iWrite++] = panMatchingFIDs[iRead]; + } + panMatchingFIDs[iWrite] = OGRNullFID; + } + + if (nSpatialFIDCount > 100000) + { + ClearSpatialFIDs(); + } + } + + return TRUE; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRShapeLayer::ResetReading() + +{ + if (!TouchLayer()) + return; + + iMatchingFID = 0; + + iNextShapeId = 0; + + if( bHeaderDirty && bUpdateAccess ) + SyncToDisk(); +} + +/************************************************************************/ +/* ClearMatchingFIDs() */ +/************************************************************************/ + +void OGRShapeLayer::ClearMatchingFIDs() +{ +/* -------------------------------------------------------------------- */ +/* Clear previous index search result, if any. */ +/* -------------------------------------------------------------------- */ + CPLFree( panMatchingFIDs ); + panMatchingFIDs = NULL; +} + +/************************************************************************/ +/* ClearSpatialFIDs() */ +/************************************************************************/ + +void OGRShapeLayer::ClearSpatialFIDs() +{ + if ( panSpatialFIDs != NULL ) + { + CPLDebug("SHAPE", "Clear panSpatialFIDs"); + free( panSpatialFIDs ); + } + panSpatialFIDs = NULL; + nSpatialFIDCount = 0; + + delete m_poFilterGeomLastValid; + m_poFilterGeomLastValid = NULL; +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRShapeLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) +{ + ClearMatchingFIDs(); + + if( poGeomIn == NULL ) + { + /* Do nothing */ + } + else if ( m_poFilterGeomLastValid != NULL && + m_poFilterGeomLastValid->Equals(poGeomIn) ) + { + /* Do nothing */ + } + else if ( panSpatialFIDs != NULL ) + { + /* We clear the spatialFIDs only if we have a new non-NULL */ + /* spatial filter, otherwise we keep the previous result */ + /* cached. This can be useful when several SQL layers */ + /* rely on the same table layer, and use the same spatial */ + /* filters. But as there is in the destructor of OGRGenSQLResultsLayer */ + /* a clearing of the spatial filter of the table layer, we */ + /* need this trick. */ + ClearSpatialFIDs(); + } + + return OGRLayer::SetSpatialFilter(poGeomIn); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRShapeLayer::SetAttributeFilter( const char * pszAttributeFilter ) +{ + ClearMatchingFIDs(); + + return OGRLayer::SetAttributeFilter(pszAttributeFilter); +} + +/************************************************************************/ +/* SetNextByIndex() */ +/* */ +/* If we already have an FID list, we can easily resposition */ +/* ourselves in it. */ +/************************************************************************/ + +OGRErr OGRShapeLayer::SetNextByIndex( GIntBig nIndex ) + +{ + if (!TouchLayer()) + return OGRERR_FAILURE; + + if( nIndex < 0 || nIndex > INT_MAX ) + return OGRERR_FAILURE; + + // Eventually we should try to use panMatchingFIDs list + // if available and appropriate. + if( m_poFilterGeom != NULL || m_poAttrQuery != NULL ) + return OGRLayer::SetNextByIndex( nIndex ); + + iNextShapeId = (int)nIndex; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* FetchShape() */ +/* */ +/* Take a shape id, a geometry, and a feature, and set the feature */ +/* if the shapeid bbox intersects the geometry. */ +/************************************************************************/ + +OGRFeature *OGRShapeLayer::FetchShape(int iShapeId /*, OGREnvelope* psShapeExtent */) + +{ + OGRFeature *poFeature; + + if (m_poFilterGeom != NULL && hSHP != NULL ) + { + SHPObject *psShape; + + psShape = SHPReadObject( hSHP, iShapeId ); + + // do not trust degenerate bounds on non-point geometries + // or bounds on null shapes. + if( psShape == NULL + || (psShape->nSHPType != SHPT_POINT + && psShape->nSHPType != SHPT_POINTZ + && psShape->nSHPType != SHPT_POINTM + && (psShape->dfXMin == psShape->dfXMax + || psShape->dfYMin == psShape->dfYMax)) + || psShape->nSHPType == SHPT_NULL ) + { + poFeature = SHPReadOGRFeature( hSHP, hDBF, poFeatureDefn, + iShapeId, psShape, osEncoding ); + } + else if( m_sFilterEnvelope.MaxX < psShape->dfXMin + || m_sFilterEnvelope.MaxY < psShape->dfYMin + || psShape->dfXMax < m_sFilterEnvelope.MinX + || psShape->dfYMax < m_sFilterEnvelope.MinY ) + { + SHPDestroyObject(psShape); + poFeature = NULL; + } + else + { + /*psShapeExtent->MinX = psShape->dfXMin; + psShapeExtent->MinY = psShape->dfYMin; + psShapeExtent->MaxX = psShape->dfXMax; + psShapeExtent->MaxY = psShape->dfYMax;*/ + poFeature = SHPReadOGRFeature( hSHP, hDBF, poFeatureDefn, + iShapeId, psShape, osEncoding ); + } + } + else + { + poFeature = SHPReadOGRFeature( hSHP, hDBF, poFeatureDefn, + iShapeId, NULL, osEncoding ); + } + + return poFeature; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRShapeLayer::GetNextFeature() + +{ + if (!TouchLayer()) + return NULL; + + OGRFeature *poFeature = NULL; + +/* -------------------------------------------------------------------- */ +/* Collect a matching list if we have attribute or spatial */ +/* indices. Only do this on the first request for a given pass */ +/* of course. */ +/* -------------------------------------------------------------------- */ + if( (m_poAttrQuery != NULL || m_poFilterGeom != NULL) + && iNextShapeId == 0 && panMatchingFIDs == NULL ) + { + ScanIndices(); + } + +/* -------------------------------------------------------------------- */ +/* Loop till we find a feature matching our criteria. */ +/* -------------------------------------------------------------------- */ + while( TRUE ) + { + //OGREnvelope oShapeExtent; + + if( panMatchingFIDs != NULL ) + { + if( panMatchingFIDs[iMatchingFID] == OGRNullFID ) + { + return NULL; + } + + // Check the shape object's geometry, and if it matches + // any spatial filter, return it. + poFeature = FetchShape((int)panMatchingFIDs[iMatchingFID] /*, &oShapeExtent*/); + + iMatchingFID++; + + } + else + { + if( iNextShapeId >= nTotalShapeCount ) + { + return NULL; + } + + if( hDBF ) + { + if (DBFIsRecordDeleted( hDBF, iNextShapeId )) + poFeature = NULL; + else if( VSIFEofL(VSI_SHP_GetVSIL(hDBF->fp)) ) + return NULL; /* There's an I/O error */ + else + poFeature = FetchShape(iNextShapeId /*, &oShapeExtent */); + } + else + poFeature = FetchShape(iNextShapeId /*, &oShapeExtent */); + + iNextShapeId++; + } + + if( poFeature != NULL ) + { + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if( poGeom != NULL ) + { + poGeom->assignSpatialReference( GetSpatialRef() ); + } + + m_nFeaturesRead++; + + if( (m_poFilterGeom == NULL || FilterGeometry( poGeom /*, &oShapeExtent*/ ) ) + && (m_poAttrQuery == NULL || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + + delete poFeature; + } + } +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRShapeLayer::GetFeature( GIntBig nFeatureId ) + +{ + if (!TouchLayer() || nFeatureId > INT_MAX ) + return NULL; + + OGRFeature *poFeature = NULL; + poFeature = SHPReadOGRFeature( hSHP, hDBF, poFeatureDefn, (int)nFeatureId, NULL, + osEncoding ); + + if( poFeature != NULL ) + { + if( poFeature->GetGeometryRef() != NULL ) + { + poFeature->GetGeometryRef()->assignSpatialReference( GetSpatialRef() ); + } + + m_nFeaturesRead++; + + return poFeature; + } + + /* + * Reading shape feature failed. + */ + return NULL; +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRShapeLayer::ISetFeature( OGRFeature *poFeature ) + +{ + if (!TouchLayer()) + return OGRERR_FAILURE; + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "SetFeature"); + return OGRERR_FAILURE; + } + + GIntBig nFID = poFeature->GetFID(); + if( nFID < 0 + || (hSHP != NULL && nFID >= hSHP->nRecords) + || (hDBF != NULL && nFID >= hDBF->nRecords) ) + { + return OGRERR_NON_EXISTING_FEATURE; + } + + bHeaderDirty = TRUE; + if( CheckForQIX() || CheckForSBN() ) + DropSpatialIndex(); + + unsigned int nOffset = 0; + unsigned int nSize = 0; + if( hSHP != NULL ) + { + nOffset = hSHP->panRecOffset[nFID]; + nSize = hSHP->panRecSize[nFID]; + } + + OGRErr eErr = SHPWriteOGRFeature( hSHP, hDBF, poFeatureDefn, poFeature, + osEncoding, &bTruncationWarningEmitted, + bRewindOnWrite ); + + if( hSHP != NULL ) + { + if( nOffset != hSHP->panRecOffset[nFID] || + nSize != hSHP->panRecSize[nFID] ) + { + bSHPNeedsRepack = TRUE; + } + } + + return eErr; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRShapeLayer::DeleteFeature( GIntBig nFID ) + +{ + if (!TouchLayer() || nFID > INT_MAX ) + return OGRERR_FAILURE; + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "DeleteFeature"); + return OGRERR_FAILURE; + } + + if( nFID < 0 + || (hSHP != NULL && nFID >= hSHP->nRecords) + || (hDBF != NULL && nFID >= hDBF->nRecords) ) + { + return OGRERR_NON_EXISTING_FEATURE; + } + + if( !hDBF ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to delete shape in shapefile with no .dbf file.\n" + "Deletion is done by marking record deleted in dbf\n" + "and is not supported without a .dbf file." ); + return OGRERR_FAILURE; + } + + if( DBFIsRecordDeleted( hDBF, (int)nFID ) ) + { + return OGRERR_NON_EXISTING_FEATURE; + } + + if( !DBFMarkRecordDeleted( hDBF, (int)nFID, TRUE ) ) + return OGRERR_FAILURE; + + bHeaderDirty = TRUE; + if( CheckForQIX() || CheckForSBN() ) + DropSpatialIndex(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRShapeLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + OGRErr eErr; + + if (!TouchLayer()) + return OGRERR_FAILURE; + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "CreateFeature"); + return OGRERR_FAILURE; + } + + if( hDBF != NULL && + !VSI_SHP_WriteMoreDataOK(hDBF->fp, hDBF->nRecordLength) ) + { + return OGRERR_FAILURE; + } + + bHeaderDirty = TRUE; + if( CheckForQIX() || CheckForSBN() ) + DropSpatialIndex(); + + poFeature->SetFID( OGRNullFID ); + + if( nTotalShapeCount == 0 + && eRequestedGeomType == wkbUnknown + && poFeature->GetGeometryRef() != NULL ) + { + OGRGeometry *poGeom = poFeature->GetGeometryRef(); + int nShapeType; + + switch( poGeom->getGeometryType() ) + { + case wkbPoint: + nShapeType = SHPT_POINT; + eRequestedGeomType = wkbPoint; + break; + + case wkbPoint25D: + nShapeType = SHPT_POINTZ; + eRequestedGeomType = wkbPoint25D; + break; + + case wkbMultiPoint: + nShapeType = SHPT_MULTIPOINT; + eRequestedGeomType = wkbMultiPoint; + break; + + case wkbMultiPoint25D: + nShapeType = SHPT_MULTIPOINTZ; + eRequestedGeomType = wkbMultiPoint25D; + break; + + case wkbLineString: + case wkbMultiLineString: + nShapeType = SHPT_ARC; + eRequestedGeomType = wkbLineString; + break; + + case wkbLineString25D: + case wkbMultiLineString25D: + nShapeType = SHPT_ARCZ; + eRequestedGeomType = wkbLineString25D; + break; + + case wkbPolygon: + case wkbMultiPolygon: + nShapeType = SHPT_POLYGON; + eRequestedGeomType = wkbPolygon; + break; + + case wkbPolygon25D: + case wkbMultiPolygon25D: + nShapeType = SHPT_POLYGONZ; + eRequestedGeomType = wkbPolygon25D; + break; + + default: + nShapeType = -1; + break; + } + + if( nShapeType != -1 ) + { + ResetGeomType( nShapeType ); + } + } + + eErr = SHPWriteOGRFeature( hSHP, hDBF, poFeatureDefn, poFeature, + osEncoding, &bTruncationWarningEmitted, + bRewindOnWrite ); + + if( hSHP != NULL ) + nTotalShapeCount = hSHP->nRecords; + else + nTotalShapeCount = hDBF->nRecords; + + return eErr; +} + +/************************************************************************/ +/* GetFeatureCountWithSpatialFilterOnly() */ +/* */ +/* Specialized implementation of GetFeatureCount() when there is *only* */ +/* a spatial filter and no attribute filter. */ +/************************************************************************/ + +int OGRShapeLayer::GetFeatureCountWithSpatialFilterOnly() + +{ +/* -------------------------------------------------------------------- */ +/* Collect a matching list if we have attribute or spatial */ +/* indices. Only do this on the first request for a given pass */ +/* of course. */ +/* -------------------------------------------------------------------- */ + if( panMatchingFIDs == NULL ) + { + ScanIndices(); + } + + int nFeatureCount = 0; + int iLocalMatchingFID = 0; + int iLocalNextShapeId = 0; + int bExpectPoints = FALSE; + + if (wkbFlatten(poFeatureDefn->GetGeomType()) == wkbPoint) + bExpectPoints = TRUE; + +/* -------------------------------------------------------------------- */ +/* Loop till we find a feature matching our criteria. */ +/* -------------------------------------------------------------------- */ + + SHPObject sShape; + memset(&sShape, 0, sizeof(sShape)); + + while( TRUE ) + { + SHPObject* psShape = NULL; + int iShape = -1; + + if( panMatchingFIDs != NULL ) + { + iShape = (int)panMatchingFIDs[iLocalMatchingFID]; + if( iShape == OGRNullFID ) + break; + iLocalMatchingFID++; + } + else + { + if( iLocalNextShapeId >= nTotalShapeCount ) + break; + iShape = iLocalNextShapeId ++; + + if( hDBF ) + { + if (DBFIsRecordDeleted( hDBF, iShape )) + continue; + + if (VSIFEofL(VSI_SHP_GetVSIL(hDBF->fp))) + break; + } + } + + /* Read full shape for point layers */ + if (bExpectPoints || + hSHP->panRecOffset[iShape] == 0 /* lazy shx loading case */ ) + psShape = SHPReadObject( hSHP, iShape); + +/* -------------------------------------------------------------------- */ +/* Only read feature type and bounding box for now. In case of */ +/* inconclusive tests on bounding box only, we will read the full */ +/* shape later. */ +/* -------------------------------------------------------------------- */ + else if (iShape >= 0 && iShape < hSHP->nRecords && + hSHP->panRecSize[iShape] > 4 + 8 * 4 ) + { + GByte abyBuf[4 + 8 * 4]; + if( hSHP->sHooks.FSeek( hSHP->fpSHP, hSHP->panRecOffset[iShape] + 8, 0 ) == 0 && + hSHP->sHooks.FRead( abyBuf, sizeof(abyBuf), 1, hSHP->fpSHP ) == 1 ) + { + memcpy(&(sShape.nSHPType), abyBuf, 4); + CPL_LSBPTR32(&(sShape.nSHPType)); + if ( sShape.nSHPType != SHPT_NULL && + sShape.nSHPType != SHPT_POINT && + sShape.nSHPType != SHPT_POINTM && + sShape.nSHPType != SHPT_POINTZ) + { + psShape = &sShape; + memcpy(&(sShape.dfXMin), abyBuf + 4, 8); + memcpy(&(sShape.dfYMin), abyBuf + 12, 8); + memcpy(&(sShape.dfXMax), abyBuf + 20, 8); + memcpy(&(sShape.dfYMax), abyBuf + 28, 8); + CPL_LSBPTR64(&(sShape.dfXMin)); + CPL_LSBPTR64(&(sShape.dfYMin)); + CPL_LSBPTR64(&(sShape.dfXMax)); + CPL_LSBPTR64(&(sShape.dfYMax)); + } + } + else + { + break; + } + } + + if( psShape != NULL && psShape->nSHPType != SHPT_NULL ) + { + OGRGeometry* poGeometry = NULL; + OGREnvelope sGeomEnv; + /* Test if we have a degenerated bounding box */ + if (psShape->nSHPType != SHPT_POINT + && psShape->nSHPType != SHPT_POINTZ + && psShape->nSHPType != SHPT_POINTM + && (psShape->dfXMin == psShape->dfXMax + || psShape->dfYMin == psShape->dfYMax)) + { + /* We need to read the full geometry */ + /* to compute the envelope */ + if (psShape == &sShape) + psShape = SHPReadObject( hSHP, iShape); + if (psShape) + { + poGeometry = SHPReadOGRObject( hSHP, iShape, psShape ); + poGeometry->getEnvelope( &sGeomEnv ); + psShape = NULL; + } + } + else + { + /* Trust the shape bounding box as the shape envelope */ + sGeomEnv.MinX = psShape->dfXMin; + sGeomEnv.MinY = psShape->dfYMin; + sGeomEnv.MaxX = psShape->dfXMax; + sGeomEnv.MaxY = psShape->dfYMax; + } + +/* -------------------------------------------------------------------- */ +/* If there is no */ +/* intersection between the envelopes we are sure not to have */ +/* any intersection. */ +/* -------------------------------------------------------------------- */ + if( sGeomEnv.MaxX < m_sFilterEnvelope.MinX + || sGeomEnv.MaxY < m_sFilterEnvelope.MinY + || m_sFilterEnvelope.MaxX < sGeomEnv.MinX + || m_sFilterEnvelope.MaxY < sGeomEnv.MinY ) + { + } +/* -------------------------------------------------------------------- */ +/* If the filter geometry is its own envelope and if the */ +/* envelope of the geometry is inside the filter geometry, */ +/* the geometry itself is inside the filter geometry */ +/* -------------------------------------------------------------------- */ + else if( m_bFilterIsEnvelope && + sGeomEnv.MinX >= m_sFilterEnvelope.MinX && + sGeomEnv.MinY >= m_sFilterEnvelope.MinY && + sGeomEnv.MaxX <= m_sFilterEnvelope.MaxX && + sGeomEnv.MaxY <= m_sFilterEnvelope.MaxY) + { + nFeatureCount ++; + } + else + { +/* -------------------------------------------------------------------- */ +/* Fallback to full intersect test (using GEOS) if we still */ +/* don't know for sure. */ +/* -------------------------------------------------------------------- */ + if( OGRGeometryFactory::haveGEOS() ) + { + /* We need to read the full geometry */ + if (poGeometry == NULL) + { + if (psShape == &sShape) + psShape = SHPReadObject( hSHP, iShape); + if (psShape) + { + poGeometry = + SHPReadOGRObject( hSHP, iShape, psShape ); + psShape = NULL; + } + } + if( poGeometry == NULL ) + nFeatureCount ++; + else if ( m_pPreparedFilterGeom != NULL ) + { + if( OGRPreparedGeometryIntersects(m_pPreparedFilterGeom, + poGeometry) ) + { + nFeatureCount ++; + } + } + else if( m_poFilterGeom->Intersects( poGeometry ) ) + nFeatureCount ++; + } + else + nFeatureCount ++; + } + + delete poGeometry; + } + else + nFeatureCount ++; + + if (psShape && psShape != &sShape) + SHPDestroyObject( psShape ); + } + + return nFeatureCount; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRShapeLayer::GetFeatureCount( int bForce ) + +{ + /* Check if the spatial filter is non-trivial */ + int bHasTrivialSpatialFilter; + if (m_poFilterGeom != NULL) + { + OGREnvelope oSpatialFilterEnvelope; + m_poFilterGeom->getEnvelope( &oSpatialFilterEnvelope ); + + OGREnvelope oLayerExtent; + if (GetExtent(&oLayerExtent, TRUE) == OGRERR_NONE) + { + if (oSpatialFilterEnvelope.Contains(oLayerExtent)) + { + bHasTrivialSpatialFilter = TRUE; + } + else + bHasTrivialSpatialFilter = FALSE; + } + else + bHasTrivialSpatialFilter = FALSE; + } + else + bHasTrivialSpatialFilter = TRUE; + + + if( bHasTrivialSpatialFilter && m_poAttrQuery == NULL ) + return nTotalShapeCount; + + if (!TouchLayer()) + return 0; + + /* Spatial filter only */ + if( m_poAttrQuery == NULL && hSHP != NULL ) + { + return GetFeatureCountWithSpatialFilterOnly(); + } + + /* Attribute filter only */ + if( m_poAttrQuery != NULL ) + { + /* Let's see if we can ignore reading geometries */ + int bSaveGeometryIgnored = poFeatureDefn->IsGeometryIgnored(); + if (!AttributeFilterEvaluationNeedsGeometry()) + poFeatureDefn->SetGeometryIgnored(TRUE); + + GIntBig nRet = OGRLayer::GetFeatureCount( bForce ); + + poFeatureDefn->SetGeometryIgnored(bSaveGeometryIgnored); + return nRet; + } + + return OGRLayer::GetFeatureCount( bForce ); +} + +/************************************************************************/ +/* GetExtent() */ +/* */ +/* Fetch extent of the data currently stored in the dataset. */ +/* The bForce flag has no effect on SHP files since that value */ +/* is always in the header. */ +/* */ +/* Returns OGRERR_NONE/OGRRERR_FAILURE. */ +/************************************************************************/ + +OGRErr OGRShapeLayer::GetExtent (OGREnvelope *psExtent, int bForce) + +{ + UNREFERENCED_PARAM( bForce ); + + if (!TouchLayer()) + return OGRERR_FAILURE; + + double adMin[4], adMax[4]; + + if( hSHP == NULL ) + return OGRERR_FAILURE; + + SHPGetInfo(hSHP, NULL, NULL, adMin, adMax); + + psExtent->MinX = adMin[0]; + psExtent->MinY = adMin[1]; + psExtent->MaxX = adMax[0]; + psExtent->MaxY = adMax[1]; + + if( CPLIsNan(adMin[0]) || CPLIsNan(adMin[1]) || + CPLIsNan(adMax[0]) || CPLIsNan(adMax[1]) ) + { + CPLDebug("SHAPE", "Invalid extent in shape header"); + OGRErr eErr; + + /* Disable filters to avoid infinite recursion in GetNextFeature() */ + /* that calls ScanIndices() that call GetExtent... */ + OGRFeatureQuery* poAttrQuery = m_poAttrQuery; + m_poAttrQuery = NULL; + OGRGeometry* poFilterGeom = m_poFilterGeom; + m_poFilterGeom = NULL; + + eErr = OGRLayer::GetExtent(psExtent, bForce); + + m_poAttrQuery = poAttrQuery; + m_poFilterGeom = poFilterGeom; + return eErr; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRShapeLayer::TestCapability( const char * pszCap ) + +{ + if (!TouchLayer()) + return FALSE; + + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + { + if( !(m_poFilterGeom == NULL || CheckForQIX() || CheckForSBN()) ) + return FALSE; + + if( m_poAttrQuery != NULL ) + { + InitializeIndexSupport( pszFullName ); + return m_poAttrQuery->CanUseIndex(this); + } + return TRUE; + } + + else if( EQUAL(pszCap,OLCDeleteFeature) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return CheckForQIX() || CheckForSBN(); + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastSetNextByIndex) ) + return m_poFilterGeom == NULL && m_poAttrQuery == NULL; + + else if( EQUAL(pszCap,OLCCreateField) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCDeleteField) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCReorderFields) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCAlterFieldDefn) ) + return bUpdateAccess; + + else if( EQUAL(pszCap,OLCIgnoreFields) ) + return TRUE; + + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + { + /* No encoding defined : we don't know */ + if( osEncoding.size() == 0) + return FALSE; + + if( hDBF == NULL || DBFGetFieldCount( hDBF ) == 0 ) + return TRUE; + + CPLClearRecodeWarningFlags(); + + /* Otherwise test that we can re-encode field names to UTF-8 */ + int nFieldCount = DBFGetFieldCount( hDBF ); + for(int i=0;iGetFieldCount() == 255 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Creating a 256th field, but some DBF readers might only support 255 fields" ); + } + if ( hDBF->nHeaderLength + 32 > 65535 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Cannot add more fields in DBF file."); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Normalize field name */ +/* -------------------------------------------------------------------- */ + + char szNewFieldName[10 + 1]; + char * pszTmp = NULL; + int nRenameNum = 1; + + CPLString osFieldName; + if( osEncoding.size() ) + { + CPLClearRecodeWarningFlags(); + CPLPushErrorHandler(CPLQuietErrorHandler); + CPLErr eLastErr = CPLGetLastErrorType(); + char* pszRecoded = CPLRecode( poFieldDefn->GetNameRef(), CPL_ENC_UTF8, osEncoding); + CPLPopErrorHandler(); + osFieldName = pszRecoded; + CPLFree(pszRecoded); + if( CPLGetLastErrorType() != eLastErr ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Failed to create field name '%s' : cannot convert to %s", + poFieldDefn->GetNameRef(), osEncoding.c_str()); + return OGRERR_FAILURE; + } + } + else + osFieldName = poFieldDefn->GetNameRef(); + + size_t nNameSize = osFieldName.size(); + pszTmp = CPLScanString( osFieldName, + MIN( nNameSize, 10) , TRUE, TRUE); + strncpy(szNewFieldName, pszTmp, 10); + szNewFieldName[10] = '\0'; + + if( !bApproxOK && + ( DBFGetFieldIndex( hDBF, szNewFieldName ) >= 0 || + !EQUAL(osFieldName,szNewFieldName) ) ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Failed to add field named '%s'", + poFieldDefn->GetNameRef() ); + + CPLFree( pszTmp ); + return OGRERR_FAILURE; + } + + while( DBFGetFieldIndex( hDBF, szNewFieldName ) >= 0 && nRenameNum < 10 ) + sprintf( szNewFieldName, "%.8s_%.1d", pszTmp, nRenameNum++ ); + while( DBFGetFieldIndex( hDBF, szNewFieldName ) >= 0 && nRenameNum < 100 ) + sprintf( szNewFieldName, "%.8s%.2d", pszTmp, nRenameNum++ ); + + CPLFree( pszTmp ); + pszTmp = NULL; + + if( DBFGetFieldIndex( hDBF, szNewFieldName ) >= 0 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Too many field names like '%s' when truncated to 10 letters " + "for Shapefile format.", + poFieldDefn->GetNameRef() );//One hundred similar field names!!? + } + + OGRFieldDefn oModFieldDefn(poFieldDefn); + + if( !EQUAL(osFieldName,szNewFieldName) ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "Normalized/laundered field name: '%s' to '%s'", + poFieldDefn->GetNameRef(), + szNewFieldName ); + + // Set field name with normalized value + oModFieldDefn.SetName(szNewFieldName); + } + +/* -------------------------------------------------------------------- */ +/* Add field to layer */ +/* -------------------------------------------------------------------- */ + + char chType = 'C'; + int nWidth = 0; + int nDecimals = 0; + + switch( oModFieldDefn.GetType() ) + { + case OFTInteger: + chType = 'N'; + nWidth = oModFieldDefn.GetWidth(); + if (nWidth == 0) nWidth = 9; + break; + + case OFTInteger64: + chType = 'N'; + nWidth = oModFieldDefn.GetWidth(); + if (nWidth == 0) nWidth = 18; + break; + + case OFTReal: + chType = 'N'; + nWidth = oModFieldDefn.GetWidth(); + nDecimals = oModFieldDefn.GetPrecision(); + if (nWidth == 0) + { + nWidth = 24; + nDecimals = 15; + } + break; + + case OFTString: + chType = 'C'; + nWidth = oModFieldDefn.GetWidth(); + if (nWidth == 0) nWidth = 80; + else if (nWidth > OGR_DBF_MAX_FIELD_WIDTH) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Field %s of width %d truncated to %d.", + szNewFieldName, nWidth, OGR_DBF_MAX_FIELD_WIDTH ); + nWidth = OGR_DBF_MAX_FIELD_WIDTH; + } + break; + + case OFTDate: + chType = 'D'; + nWidth = 8; + break; + + case OFTDateTime: + CPLError( CE_Warning, CPLE_NotSupported, + "Field %s create as date field, though DateTime requested.", + szNewFieldName ); + chType = 'D'; + nWidth = 8; + oModFieldDefn.SetType( OFTDate ); + break; + + default: + CPLError( CE_Failure, CPLE_NotSupported, + "Can't create fields of type %s on shapefile layers.", + OGRFieldDefn::GetFieldTypeName(oModFieldDefn.GetType()) ); + + return OGRERR_FAILURE; + break; + } + + oModFieldDefn.SetWidth( nWidth ); + oModFieldDefn.SetPrecision( nDecimals ); + + if ( hDBF->nRecordLength + nWidth > 65535 ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can't create field %s in Shape DBF file. " + "Maximum record length reached.", + szNewFieldName ); + return OGRERR_FAILURE; + } + + /* Suppress the dummy FID field if we have created it just before */ + if( DBFGetFieldCount( hDBF ) == 1 && poFeatureDefn->GetFieldCount() == 0 ) + { + DBFDeleteField( hDBF, 0 ); + } + + iNewField = + DBFAddNativeFieldType( hDBF, szNewFieldName, + chType, nWidth, nDecimals ); + + if( iNewField != -1 ) + { + poFeatureDefn->AddFieldDefn( &oModFieldDefn ); + + if( bDBFJustCreated ) + { + for(int i=0;i= poFeatureDefn->GetFieldCount()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + + if ( DBFDeleteField( hDBF, iField ) ) + { + TruncateDBF(); + + return poFeatureDefn->DeleteFieldDefn( iField ); + } + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* ReorderFields() */ +/************************************************************************/ + +OGRErr OGRShapeLayer::ReorderFields( int* panMap ) +{ + if (!TouchLayer()) + return OGRERR_FAILURE; + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "ReorderFields"); + return OGRERR_FAILURE; + } + + if (poFeatureDefn->GetFieldCount() == 0) + return OGRERR_NONE; + + OGRErr eErr = OGRCheckPermutation(panMap, poFeatureDefn->GetFieldCount()); + if (eErr != OGRERR_NONE) + return eErr; + + if ( DBFReorderFields( hDBF, panMap ) ) + { + return poFeatureDefn->ReorderFieldDefns( panMap ); + } + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* AlterFieldDefn() */ +/************************************************************************/ + +OGRErr OGRShapeLayer::AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) +{ + if (!TouchLayer()) + return OGRERR_FAILURE; + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "AlterFieldDefn"); + return OGRERR_FAILURE; + } + + if (iField < 0 || iField >= poFeatureDefn->GetFieldCount()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(iField); + + char chNativeType; + char szFieldName[20]; + int nWidth, nPrecision; + OGRFieldType eType = poFieldDefn->GetType(); + /* DBFFieldType eDBFType; */ + + chNativeType = DBFGetNativeFieldType( hDBF, iField ); + /* eDBFType = */ DBFGetFieldInfo( hDBF, iField, szFieldName, + &nWidth, &nPrecision ); + + if ((nFlags & ALTER_TYPE_FLAG) && + poNewFieldDefn->GetType() != poFieldDefn->GetType()) + { + if (poNewFieldDefn->GetType() == OFTInteger64 && poFieldDefn->GetType() == OFTInteger ) + { + eType = poNewFieldDefn->GetType(); + } + else if (poNewFieldDefn->GetType() != OFTString) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can only convert to OFTString"); + return OGRERR_FAILURE; + } + else + { + chNativeType = 'C'; + eType = poNewFieldDefn->GetType(); + } + } + + if (nFlags & ALTER_NAME_FLAG) + { + CPLString osFieldName; + if( osEncoding.size() ) + { + CPLClearRecodeWarningFlags(); + CPLErrorReset(); + CPLPushErrorHandler(CPLQuietErrorHandler); + char* pszRecoded = CPLRecode( poNewFieldDefn->GetNameRef(), CPL_ENC_UTF8, osEncoding); + CPLPopErrorHandler(); + osFieldName = pszRecoded; + CPLFree(pszRecoded); + if( CPLGetLastErrorType() != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Failed to rename field name to '%s' : cannot convert to %s", + poNewFieldDefn->GetNameRef(), osEncoding.c_str()); + return OGRERR_FAILURE; + } + } + else + osFieldName = poNewFieldDefn->GetNameRef(); + + strncpy(szFieldName, osFieldName, 10); + szFieldName[10] = '\0'; + } + if (nFlags & ALTER_WIDTH_PRECISION_FLAG) + { + nWidth = poNewFieldDefn->GetWidth(); + nPrecision = poNewFieldDefn->GetPrecision(); + } + + if ( DBFAlterFieldDefn( hDBF, iField, szFieldName, + chNativeType, nWidth, nPrecision) ) + { + if (nFlags & ALTER_TYPE_FLAG) + poFieldDefn->SetType(eType); + if (nFlags & ALTER_NAME_FLAG) + poFieldDefn->SetName(poNewFieldDefn->GetNameRef()); + if (nFlags & ALTER_WIDTH_PRECISION_FLAG) + { + poFieldDefn->SetWidth(nWidth); + poFieldDefn->SetPrecision(nPrecision); + + TruncateDBF(); + } + return OGRERR_NONE; + } + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRShapeGeomFieldDefn::GetSpatialRef() + +{ + if (bSRSSet) + return poSRS; + + bSRSSet = TRUE; + +/* -------------------------------------------------------------------- */ +/* Is there an associated .prj file we can read? */ +/* -------------------------------------------------------------------- */ + const char *pszPrjFile = CPLResetExtension( pszFullName, "prj" ); + char **papszLines; + + char* apszOptions[] = { (char*)"EMIT_ERROR_IF_CANNOT_OPEN_FILE=FALSE", NULL }; + papszLines = CSLLoad2( pszPrjFile, -1, -1, apszOptions ); + if (papszLines == NULL) + { + pszPrjFile = CPLResetExtension( pszFullName, "PRJ" ); + papszLines = CSLLoad2( pszPrjFile, -1, -1, apszOptions ); + } + + if( papszLines != NULL ) + { + osPrjFile = pszPrjFile; + + poSRS = new OGRSpatialReference(); + /* Remove UTF-8 BOM if found */ + /* http://lists.osgeo.org/pipermail/gdal-dev/2014-July/039527.html */ + if( ((unsigned char)papszLines[0][0] == 0xEF) && + ((unsigned char)papszLines[0][1] == 0xBB) && + ((unsigned char)papszLines[0][2] == 0xBF) ) + { + memmove(papszLines[0], papszLines[0] + 3, strlen(papszLines[0] + 3) + 1); + } + if( poSRS->importFromESRI( papszLines ) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + CSLDestroy( papszLines ); + } + + return poSRS; +} + +/************************************************************************/ +/* ResetGeomType() */ +/* */ +/* Modify the geometry type for this file. Used to convert to */ +/* a different geometry type when a layer was created with a */ +/* type of unknown, and we get to the first feature to */ +/* establish the type. */ +/************************************************************************/ + +int OGRShapeLayer::ResetGeomType( int nNewGeomType ) + +{ + char abyHeader[100]; + int nStartPos; + + if( nTotalShapeCount > 0 ) + return FALSE; + + if( hSHP->fpSHX == NULL) + { + CPLError( CE_Failure, CPLE_NotSupported, + " OGRShapeLayer::ResetGeomType failed : SHX file is closed"); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Update .shp header. */ +/* -------------------------------------------------------------------- */ + nStartPos = (int)( hSHP->sHooks.FTell( hSHP->fpSHP ) ); + + if( hSHP->sHooks.FSeek( hSHP->fpSHP, 0, SEEK_SET ) != 0 + || hSHP->sHooks.FRead( abyHeader, 100, 1, hSHP->fpSHP ) != 1 ) + return FALSE; + + *((GInt32 *) (abyHeader + 32)) = CPL_LSBWORD32( nNewGeomType ); + + if( hSHP->sHooks.FSeek( hSHP->fpSHP, 0, SEEK_SET ) != 0 + || hSHP->sHooks.FWrite( abyHeader, 100, 1, hSHP->fpSHP ) != 1 ) + return FALSE; + + if( hSHP->sHooks.FSeek( hSHP->fpSHP, nStartPos, SEEK_SET ) != 0 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Update .shx header. */ +/* -------------------------------------------------------------------- */ + nStartPos = (int)( hSHP->sHooks.FTell( hSHP->fpSHX ) ); + + if( hSHP->sHooks.FSeek( hSHP->fpSHX, 0, SEEK_SET ) != 0 + || hSHP->sHooks.FRead( abyHeader, 100, 1, hSHP->fpSHX ) != 1 ) + return FALSE; + + *((GInt32 *) (abyHeader + 32)) = CPL_LSBWORD32( nNewGeomType ); + + if( hSHP->sHooks.FSeek( hSHP->fpSHX, 0, SEEK_SET ) != 0 + || hSHP->sHooks.FWrite( abyHeader, 100, 1, hSHP->fpSHX ) != 1 ) + return FALSE; + + if( hSHP->sHooks.FSeek( hSHP->fpSHX, nStartPos, SEEK_SET ) != 0 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Update other information. */ +/* -------------------------------------------------------------------- */ + hSHP->nShapeType = nNewGeomType; + + return TRUE; +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr OGRShapeLayer::SyncToDisk() + +{ + if (!TouchLayer()) + return OGRERR_FAILURE; + + if( bHeaderDirty ) + { + if( hSHP != NULL ) + SHPWriteHeader( hSHP ); + + if( hDBF != NULL ) + DBFUpdateHeader( hDBF ); + + bHeaderDirty = FALSE; + } + + if( hSHP != NULL ) + { + hSHP->sHooks.FFlush( hSHP->fpSHP ); + if( hSHP->fpSHX != NULL ) + hSHP->sHooks.FFlush( hSHP->fpSHX ); + } + + if( hDBF != NULL ) + { + hDBF->sHooks.FFlush( hDBF->fp ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DropSpatialIndex() */ +/************************************************************************/ + +OGRErr OGRShapeLayer::DropSpatialIndex() + +{ + if (!TouchLayer()) + return OGRERR_FAILURE; + + if( !CheckForQIX() && !CheckForSBN() ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Layer %s has no spatial index, DROP SPATIAL INDEX failed.", + poFeatureDefn->GetName() ); + return OGRERR_FAILURE; + } + + int bHadQIX = hQIX != NULL; + + SHPCloseDiskTree( hQIX ); + hQIX = NULL; + bCheckedForQIX = FALSE; + + SBNCloseDiskTree( hSBN ); + hSBN = NULL; + bCheckedForSBN = FALSE; + + if( bHadQIX ) + { + const char *pszQIXFilename; + + pszQIXFilename = CPLResetExtension( pszFullName, "qix" ); + CPLDebug( "SHAPE", "Unlinking index file %s", pszQIXFilename ); + + if( VSIUnlink( pszQIXFilename ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to delete file %s.\n%s", + pszQIXFilename, VSIStrerror( errno ) ); + return OGRERR_FAILURE; + } + } + + if( !bSbnSbxDeleted ) + { + const char *pszIndexFilename; + const char papszExt[2][4] = { "sbn", "sbx" }; + int i; + for( i = 0; i < 2; i++ ) + { + pszIndexFilename = CPLResetExtension( pszFullName, papszExt[i] ); + CPLDebug( "SHAPE", "Trying to unlink index file %s", pszIndexFilename ); + + if( VSIUnlink( pszIndexFilename ) != 0 ) + { + CPLDebug( "SHAPE", + "Failed to delete file %s.\n%s", + pszIndexFilename, VSIStrerror( errno ) ); + } + } + } + bSbnSbxDeleted = TRUE; + + ClearSpatialFIDs(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateSpatialIndex() */ +/************************************************************************/ + +OGRErr OGRShapeLayer::CreateSpatialIndex( int nMaxDepth ) + +{ + if (!TouchLayer()) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* If we have an existing spatial index, blow it away first. */ +/* -------------------------------------------------------------------- */ + if( CheckForQIX() ) + DropSpatialIndex(); + + bCheckedForQIX = FALSE; + +/* -------------------------------------------------------------------- */ +/* Build a quadtree structure for this file. */ +/* -------------------------------------------------------------------- */ + SHPTree *psTree; + + SyncToDisk(); + psTree = SHPCreateTree( hSHP, 2, nMaxDepth, NULL, NULL ); + + if( NULL == psTree ) + { + // TODO - mloskot: Is it better to return OGRERR_NOT_ENOUGH_MEMORY? + + CPLDebug( "SHAPE", + "Index creation failure. Likely, memory allocation error." ); + + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Trim unused nodes from the tree. */ +/* -------------------------------------------------------------------- */ + SHPTreeTrimExtraNodes( psTree ); + +/* -------------------------------------------------------------------- */ +/* Dump tree to .qix file. */ +/* -------------------------------------------------------------------- */ + char *pszQIXFilename; + + pszQIXFilename = CPLStrdup(CPLResetExtension( pszFullName, "qix" )); + + CPLDebug( "SHAPE", "Creating index file %s", pszQIXFilename ); + + SHPWriteTree( psTree, pszQIXFilename ); + CPLFree( pszQIXFilename ); + + +/* -------------------------------------------------------------------- */ +/* cleanup */ +/* -------------------------------------------------------------------- */ + SHPDestroyTree( psTree ); + + CheckForQIX(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* Repack() */ +/* */ +/* Repack the shape and dbf file, dropping deleted records. */ +/* FIDs may change. */ +/************************************************************************/ + +OGRErr OGRShapeLayer::Repack() + +{ + if (!TouchLayer()) + return OGRERR_FAILURE; + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "Repack"); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Build a list of records to be dropped. */ +/* -------------------------------------------------------------------- */ + int *panRecordsToDelete = (int*) CPLMalloc(sizeof(int)*128); + int nDeleteCount = 0, nDeleteCountAlloc = 128; + int iShape = 0; + OGRErr eErr = OGRERR_NONE; + + if( hDBF != NULL ) + { + for( iShape = 0; iShape < nTotalShapeCount; iShape++ ) + { + if( DBFIsRecordDeleted( hDBF, iShape ) ) + { + if( nDeleteCount == nDeleteCountAlloc ) + { + int nDeleteCountAllocNew = + nDeleteCountAlloc + nDeleteCountAlloc / 3 + 32; + if( nDeleteCountAlloc >= (INT_MAX - 32) / 4 * 3 || + nDeleteCountAllocNew > INT_MAX / (int)sizeof(int) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many features to delete : %d", nDeleteCount ); + CPLFree( panRecordsToDelete ); + return OGRERR_FAILURE; + } + nDeleteCountAlloc = nDeleteCountAllocNew; + int* panRecordsToDeleteNew = (int*) VSIRealloc( + panRecordsToDelete, nDeleteCountAlloc * sizeof(int) ); + if( panRecordsToDeleteNew == NULL ) + { + CPLFree( panRecordsToDelete ); + return OGRERR_FAILURE; + } + panRecordsToDelete = panRecordsToDeleteNew; + } + panRecordsToDelete[nDeleteCount++] = iShape; + } + if( VSIFEofL(VSI_SHP_GetVSIL(hDBF->fp)) ) + { + CPLFree( panRecordsToDelete ); + return OGRERR_FAILURE; /* There's an I/O error */ + } + } + } + +/* -------------------------------------------------------------------- */ +/* If there are no records marked for deletion, we take no */ +/* action. */ +/* -------------------------------------------------------------------- */ + if( nDeleteCount == 0 && !bSHPNeedsRepack ) + { + CPLFree( panRecordsToDelete ); + return OGRERR_NONE; + } + panRecordsToDelete[nDeleteCount] = -1; + +/* -------------------------------------------------------------------- */ +/* Find existing filenames with exact case (see #3293). */ +/* -------------------------------------------------------------------- */ + CPLString osDirname(CPLGetPath(pszFullName)); + CPLString osBasename(CPLGetBasename(pszFullName)); + + CPLString osDBFName, osSHPName, osSHXName, osCPGName; + char **papszCandidates = CPLReadDir( osDirname ); + int i = 0; + while(papszCandidates != NULL && papszCandidates[i] != NULL) + { + CPLString osCandidateBasename = CPLGetBasename(papszCandidates[i]); + CPLString osCandidateExtension = CPLGetExtension(papszCandidates[i]); +#ifdef WIN32 + /* On Windows, as filenames are case insensitive, a shapefile layer can be made of */ + /* foo.shp and FOO.DBF, so use case insensitive comparison */ + if (EQUAL(osCandidateBasename, osBasename)) +#else + if (osCandidateBasename.compare(osBasename) == 0) +#endif + { + if (EQUAL(osCandidateExtension, "dbf")) + osDBFName = CPLFormFilename(osDirname, papszCandidates[i], NULL); + else if (EQUAL(osCandidateExtension, "shp")) + osSHPName = CPLFormFilename(osDirname, papszCandidates[i], NULL); + else if (EQUAL(osCandidateExtension, "shx")) + osSHXName = CPLFormFilename(osDirname, papszCandidates[i], NULL); + else if (EQUAL(osCandidateExtension, "cpg")) + osCPGName = CPLFormFilename(osDirname, papszCandidates[i], NULL); + } + + i++; + } + CSLDestroy(papszCandidates); + papszCandidates = NULL; + + if( hDBF != NULL && osDBFName.size() == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find the filename of the DBF file, but we managed to open it before !"); + /* Should not happen, really */ + CPLFree( panRecordsToDelete ); + return OGRERR_FAILURE; + } + + if( hSHP != NULL && osSHPName.size() == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find the filename of the SHP file, but we managed to open it before !"); + /* Should not happen, really */ + CPLFree( panRecordsToDelete ); + return OGRERR_FAILURE; + } + + if( hSHP != NULL && osSHXName.size() == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find the filename of the SHX file, but we managed to open it before !"); + /* Should not happen, really */ + CPLFree( panRecordsToDelete ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup any existing spatial index. It will become */ +/* meaningless when the fids change. */ +/* -------------------------------------------------------------------- */ + if( CheckForQIX() || CheckForSBN() ) + DropSpatialIndex(); + +/* -------------------------------------------------------------------- */ +/* Create a new dbf file, matching the old. */ +/* -------------------------------------------------------------------- */ + int bMustReopenDBF = FALSE; + + if( hDBF != NULL && nDeleteCount > 0 ) + { + bMustReopenDBF = TRUE; + + CPLString oTempFile(CPLFormFilename(osDirname, osBasename, NULL)); + oTempFile += "_packed.dbf"; + + DBFHandle hNewDBF = DBFCloneEmpty( hDBF, oTempFile ); + if( hNewDBF == NULL ) + { + CPLFree( panRecordsToDelete ); + + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to create temp file %s.", + oTempFile.c_str() ); + return OGRERR_FAILURE; + } + + /* Delete temporary .cpg file if existing */ + if( osCPGName.size() ) + { + CPLString oCPGTempFile = CPLFormFilename(osDirname, osBasename, NULL); + oCPGTempFile += "_packed.cpg"; + if( VSIUnlink( oCPGTempFile ) != 0 ) + { + CPLDebug( "Shape", "Did not manage to remove temporary .cpg file: %s", + VSIStrerror( errno ) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Copy over all records that are not deleted. */ +/* -------------------------------------------------------------------- */ + int iDestShape = 0; + int iNextDeletedShape = 0; + + for( iShape = 0; + iShape < nTotalShapeCount && eErr == OGRERR_NONE; + iShape++ ) + { + if( panRecordsToDelete[iNextDeletedShape] == iShape ) + iNextDeletedShape++; + else + { + void *pTuple = (void *) DBFReadTuple( hDBF, iShape ); + if( pTuple == NULL ) + eErr = OGRERR_FAILURE; + else if( !DBFWriteTuple( hNewDBF, iDestShape++, pTuple ) ) + eErr = OGRERR_FAILURE; + } + } + + if( eErr != OGRERR_NONE ) + { + CPLFree( panRecordsToDelete ); + VSIUnlink( oTempFile ); + DBFClose( hNewDBF ); + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup the old .dbf and rename the new one. */ +/* -------------------------------------------------------------------- */ + DBFClose( hDBF ); + DBFClose( hNewDBF ); + hDBF = hNewDBF = NULL; + + if( VSIUnlink( osDBFName ) != 0 ) + { + CPLDebug( "Shape", "Failed to delete DBF file: %s", VSIStrerror( errno ) ); + CPLFree( panRecordsToDelete ); + + hDBF = poDS->DS_DBFOpen ( osDBFName, bUpdateAccess ? "r+" : "r" ); + + VSIUnlink( oTempFile ); + + return OGRERR_FAILURE; + } + + if( VSIRename( oTempFile, osDBFName ) != 0 ) + { + CPLDebug( "Shape", "Can not rename DBF file: %s", VSIStrerror( errno ) ); + CPLFree( panRecordsToDelete ); + return OGRERR_FAILURE; + } + } + +/* -------------------------------------------------------------------- */ +/* Now create a shapefile matching the old one. */ +/* -------------------------------------------------------------------- */ + int bMustReopenSHP = ( hSHP != NULL ); + + if( hSHP != NULL ) + { + SHPHandle hNewSHP = NULL; + + CPLString oTempFile = CPLFormFilename(osDirname, osBasename, NULL); + oTempFile += "_packed.shp"; + + hNewSHP = SHPCreate( oTempFile, hSHP->nShapeType ); + if( hNewSHP == NULL ) + { + CPLFree( panRecordsToDelete ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Copy over all records that are not deleted. */ +/* -------------------------------------------------------------------- */ + int iNextDeletedShape = 0; + + for( iShape = 0; + iShape < nTotalShapeCount && eErr == OGRERR_NONE; + iShape++ ) + { + if( panRecordsToDelete[iNextDeletedShape] == iShape ) + iNextDeletedShape++; + else + { + SHPObject *hObject; + + hObject = SHPReadObject( hSHP, iShape ); + if( hObject == NULL ) + eErr = OGRERR_FAILURE; + else if( SHPWriteObject( hNewSHP, -1, hObject ) == -1 ) + eErr = OGRERR_FAILURE; + + if( hObject ) + SHPDestroyObject( hObject ); + } + } + + if( eErr != OGRERR_NONE ) + { + CPLFree( panRecordsToDelete ); + VSIUnlink( CPLResetExtension( oTempFile, "shp" ) ); + VSIUnlink( CPLResetExtension( oTempFile, "shx" ) ); + SHPClose( hNewSHP ); + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup the old .shp/.shx and rename the new one. */ +/* -------------------------------------------------------------------- */ + SHPClose( hSHP ); + SHPClose( hNewSHP ); + hSHP = hNewSHP = NULL; + + VSIUnlink( osSHPName ); + VSIUnlink( osSHXName ); + + oTempFile = CPLResetExtension( oTempFile, "shp" ); + if( VSIRename( oTempFile, osSHPName ) != 0 ) + { + CPLDebug( "Shape", "Can not rename SHP file: %s", VSIStrerror( errno ) ); + CPLFree( panRecordsToDelete ); + return OGRERR_FAILURE; + } + + oTempFile = CPLResetExtension( oTempFile, "shx" ); + if( VSIRename( oTempFile, osSHXName ) != 0 ) + { + CPLDebug( "Shape", "Can not rename SHX file: %s", VSIStrerror( errno ) ); + CPLFree( panRecordsToDelete ); + return OGRERR_FAILURE; + } + } + + CPLFree( panRecordsToDelete ); + panRecordsToDelete = NULL; + +/* -------------------------------------------------------------------- */ +/* Reopen the shapefile */ +/* */ +/* We do not need to reimplement OGRShapeDataSource::OpenFile() here */ +/* with the fully featured error checking. */ +/* If all operations above succeeded, then all necessery files are */ +/* in the right place and accessible. */ +/* -------------------------------------------------------------------- */ + + const char* pszAccess = NULL; + if( bUpdateAccess ) + pszAccess = "r+"; + else + pszAccess = "r"; + + if( bMustReopenSHP ) + hSHP = poDS->DS_SHPOpen ( osSHPName , pszAccess ); + if( bMustReopenDBF ) + hDBF = poDS->DS_DBFOpen ( osDBFName , pszAccess ); + + if( (bMustReopenSHP && NULL == hSHP) || (bMustReopenDBF && NULL == hDBF) ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Update total shape count. */ +/* -------------------------------------------------------------------- */ + nTotalShapeCount = hDBF->nRecords; + bSHPNeedsRepack = FALSE; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ResizeDBF() */ +/* */ +/* Autoshrink columns of the DBF file to their minimum */ +/* size, according to the existing data. */ +/************************************************************************/ + +OGRErr OGRShapeLayer::ResizeDBF() + +{ + if (!TouchLayer()) + return OGRERR_FAILURE; + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "ResizeDBF"); + return OGRERR_FAILURE; + } + + if( hDBF == NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Attempt to RESIZE a shapefile with no .dbf file not supported."); + return OGRERR_FAILURE; + } + + int i, j; + + /* Look which columns must be examined */ + int* panColMap = (int*) CPLMalloc(poFeatureDefn->GetFieldCount() * sizeof(int)); + int* panBestWidth = (int*) CPLMalloc(poFeatureDefn->GetFieldCount() * sizeof(int)); + int nStringCols = 0; + for( i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + if( poFeatureDefn->GetFieldDefn(i)->GetType() == OFTString || + poFeatureDefn->GetFieldDefn(i)->GetType() == OFTInteger || + poFeatureDefn->GetFieldDefn(i)->GetType() == OFTInteger64 ) + { + panColMap[nStringCols] = i; + panBestWidth[nStringCols] = 1; + nStringCols ++; + } + } + + if (nStringCols == 0) + { + /* Nothing to do */ + CPLFree(panColMap); + CPLFree(panBestWidth); + return OGRERR_NONE; + } + + CPLDebug("SHAPE", "Computing optimal column size..."); + + int bAlreadyWarned = FALSE; + for( i = 0; i < hDBF->nRecords; i++ ) + { + if( !DBFIsRecordDeleted( hDBF, i ) ) + { + for( j = 0; j < nStringCols; j ++) + { + if (DBFIsAttributeNULL(hDBF, i, panColMap[j])) + continue; + + const char* pszVal = DBFReadStringAttribute(hDBF, i, panColMap[j]); + int nLen = strlen(pszVal); + if (nLen > panBestWidth[j]) + panBestWidth[j] = nLen; + } + } + else if (!bAlreadyWarned) + { + bAlreadyWarned = TRUE; + CPLDebug("SHAPE", + "DBF file would also need a REPACK due to deleted records"); + } + } + + for( j = 0; j < nStringCols; j ++) + { + int iField = panColMap[j]; + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(iField); + + char szFieldName[20]; + int nOriWidth, nPrecision; + char chNativeType; + /* DBFFieldType eDBFType; */ + + chNativeType = DBFGetNativeFieldType( hDBF, iField ); + /* eDBFType = */ DBFGetFieldInfo( hDBF, iField, szFieldName, + &nOriWidth, &nPrecision ); + + if (panBestWidth[j] < nOriWidth) + { + CPLDebug("SHAPE", "Shrinking field %d (%s) from %d to %d characters", + iField, poFieldDefn->GetNameRef(), nOriWidth, panBestWidth[j]); + + if (!DBFAlterFieldDefn( hDBF, iField, szFieldName, + chNativeType, panBestWidth[j], nPrecision )) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Shrinking field %d (%s) from %d to %d characters failed", + iField, poFieldDefn->GetNameRef(), nOriWidth, panBestWidth[j]); + + CPLFree(panColMap); + CPLFree(panBestWidth); + + return OGRERR_FAILURE; + } + else + { + poFieldDefn->SetWidth(panBestWidth[j]); + } + } + } + + TruncateDBF(); + + CPLFree(panColMap); + CPLFree(panBestWidth); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TruncateDBF() */ +/************************************************************************/ + +void OGRShapeLayer::TruncateDBF() +{ + if (hDBF == NULL) + return; + + hDBF->sHooks.FSeek(hDBF->fp, 0, SEEK_END); + vsi_l_offset nOldSize = hDBF->sHooks.FTell(hDBF->fp); + vsi_l_offset nNewSize = hDBF->nRecordLength * (SAOffset) hDBF->nRecords + + hDBF->nHeaderLength; + if (nNewSize < nOldSize) + { + CPLDebug("SHAPE", + "Truncating DBF file from " CPL_FRMT_GUIB " to " CPL_FRMT_GUIB " bytes", + nOldSize, nNewSize); + VSIFTruncateL(VSI_SHP_GetVSIL(hDBF->fp), nNewSize); + } + hDBF->sHooks.FSeek(hDBF->fp, 0, SEEK_SET); +} + +/************************************************************************/ +/* RecomputeExtent() */ +/* */ +/* Force recomputation of the extent of the .SHP file */ +/************************************************************************/ + +OGRErr OGRShapeLayer::RecomputeExtent() +{ + if (!TouchLayer()) + return OGRERR_FAILURE; + + if( !bUpdateAccess ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "RecomputeExtent"); + return OGRERR_FAILURE; + } + + if( hSHP == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The RECOMPUTE EXTENT operation is not permitted on a layer without .SHP file." ); + return OGRERR_FAILURE; + } + + double adBoundsMin[4] = { 0.0, 0.0, 0.0, 0.0 }; + double adBoundsMax[4] = { 0.0, 0.0, 0.0, 0.0 }; + + int bHasBeenInit = FALSE; + + for( int iShape = 0; + iShape < nTotalShapeCount; + iShape++ ) + { + if( hDBF == NULL || !DBFIsRecordDeleted( hDBF, iShape ) ) + { + SHPObject *psObject = SHPReadObject( hSHP, iShape ); + if ( psObject != NULL && + psObject->nSHPType != SHPT_NULL && + psObject->nVertices != 0 ) + { + if( !bHasBeenInit ) + { + bHasBeenInit = TRUE; + adBoundsMin[0] = adBoundsMax[0] = psObject->padfX[0]; + adBoundsMin[1] = adBoundsMax[1] = psObject->padfY[0]; + if( psObject->padfZ ) + adBoundsMin[2] = adBoundsMax[2] = psObject->padfZ[0]; + if( psObject->padfM ) + adBoundsMin[3] = adBoundsMax[3] = psObject->padfM[0]; + } + + for( int i = 0; i < psObject->nVertices; i++ ) + { + adBoundsMin[0] = MIN(adBoundsMin[0],psObject->padfX[i]); + adBoundsMin[1] = MIN(adBoundsMin[1],psObject->padfY[i]); + adBoundsMax[0] = MAX(adBoundsMax[0],psObject->padfX[i]); + adBoundsMax[1] = MAX(adBoundsMax[1],psObject->padfY[i]); + if( psObject->padfZ ) + { + adBoundsMin[2] = MIN(adBoundsMin[2],psObject->padfZ[i]); + adBoundsMax[2] = MAX(adBoundsMax[2],psObject->padfZ[i]); + } + if( psObject->padfM ) + { + adBoundsMax[3] = MAX(adBoundsMax[3],psObject->padfM[i]); + adBoundsMin[3] = MIN(adBoundsMin[3],psObject->padfM[i]); + } + } + } + SHPDestroyObject(psObject); + } + } + + if( memcmp(hSHP->adBoundsMin, adBoundsMin, 4*sizeof(double)) != 0 || + memcmp(hSHP->adBoundsMax, adBoundsMax, 4*sizeof(double)) != 0 ) + { + bHeaderDirty = TRUE; + hSHP->bUpdated = TRUE; + memcpy(hSHP->adBoundsMin, adBoundsMin, 4*sizeof(double)); + memcpy(hSHP->adBoundsMax, adBoundsMax, 4*sizeof(double)); + } + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* TouchLayer() */ +/************************************************************************/ + +int OGRShapeLayer::TouchLayer() +{ + poDS->SetLastUsedLayer(this); + + if (eFileDescriptorsState == FD_OPENED) + return TRUE; + else if (eFileDescriptorsState == FD_CANNOT_REOPEN) + return FALSE; + else + return ReopenFileDescriptors(); +} + +/************************************************************************/ +/* ReopenFileDescriptors() */ +/************************************************************************/ + +int OGRShapeLayer::ReopenFileDescriptors() +{ + CPLDebug("SHAPE", "ReopenFileDescriptors(%s)", pszFullName); + + if( bHSHPWasNonNULL ) + { + if( bUpdateAccess ) + hSHP = poDS->DS_SHPOpen( pszFullName, "r+" ); + else + hSHP = poDS->DS_SHPOpen( pszFullName, "r" ); + + if (hSHP == NULL) + { + eFileDescriptorsState = FD_CANNOT_REOPEN; + return FALSE; + } + } + + if( bHDBFWasNonNULL ) + { + if( bUpdateAccess ) + hDBF = poDS->DS_DBFOpen( pszFullName, "r+" ); + else + hDBF = poDS->DS_DBFOpen( pszFullName, "r" ); + + if (hDBF == NULL) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Cannot reopen %s", CPLResetExtension(pszFullName, "dbf")); + eFileDescriptorsState = FD_CANNOT_REOPEN; + return FALSE; + } + } + + eFileDescriptorsState = FD_OPENED; + + return TRUE; +} + +/************************************************************************/ +/* CloseUnderlyingLayer() */ +/************************************************************************/ + +void OGRShapeLayer::CloseUnderlyingLayer() +{ + CPLDebug("SHAPE", "CloseUnderlyingLayer(%s)", pszFullName); + + if( hDBF != NULL ) + DBFClose( hDBF ); + hDBF = NULL; + + if( hSHP != NULL ) + SHPClose( hSHP ); + hSHP = NULL; + + /* We close QIX and reset the check flag, so that CheckForQIX() */ + /* will retry opening it if necessary when the layer is active again */ + if( hQIX != NULL ) + SHPCloseDiskTree( hQIX ); + hQIX = NULL; + bCheckedForQIX = FALSE; + + if( hSBN != NULL ) + SBNCloseDiskTree( hSBN ); + hSBN = NULL; + bCheckedForSBN = FALSE; + + eFileDescriptorsState = FD_CLOSED; +} + +/************************************************************************/ +/* AddToFileList() */ +/************************************************************************/ + +void OGRShapeLayer::AddToFileList( CPLStringList& oFileList ) +{ + if (!TouchLayer()) + return; + if( hSHP ) + { + const char* pszSHPFilename = VSI_SHP_GetFilename( hSHP->fpSHP ); + oFileList.AddString(pszSHPFilename); + const char* pszSHPExt = CPLGetExtension(pszSHPFilename); + const char* pszSHXFilename = CPLResetExtension( pszSHPFilename, + (pszSHPExt[0] == 's') ? "shx" : "SHX" ); + oFileList.AddString(pszSHXFilename); + } + if( hDBF ) + { + const char* pszDBFFilename = VSI_SHP_GetFilename( hDBF->fp ); + oFileList.AddString(pszDBFFilename); + if( hDBF->pszCodePage != NULL && hDBF->iLanguageDriver == 0 ) + { + const char* pszDBFExt = CPLGetExtension(pszDBFFilename); + const char* pszCPGFilename = CPLResetExtension( pszDBFFilename, + (pszDBFExt[0] == 'd') ? "cpg" : "CPG" ); + oFileList.AddString(pszCPGFilename); + } + } + if( hSHP ) + { + if( GetSpatialRef() != NULL ) + { + OGRShapeGeomFieldDefn* poGeomFieldDefn = + (OGRShapeGeomFieldDefn*)GetLayerDefn()->GetGeomFieldDefn(0); + oFileList.AddString(poGeomFieldDefn->GetPrjFilename()); + } + if( CheckForQIX() ) + { + const char* pszQIXFilename = CPLResetExtension( pszFullName, "qix" ); + oFileList.AddString(pszQIXFilename); + } + else if( CheckForSBN() ) + { + const char* pszSBNFilename = CPLResetExtension( pszFullName, "sbn" ); + oFileList.AddString(pszSBNFilename); + const char* pszSBXFilename = CPLResetExtension( pszFullName, "sbx" ); + oFileList.AddString(pszSBXFilename); + } + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/sbnsearch.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/sbnsearch.c new file mode 100644 index 000000000..727f9a868 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/sbnsearch.c @@ -0,0 +1,975 @@ +/****************************************************************************** + * $Id: sbnsearch.c 28039 2014-11-30 18:24:59Z rouault $ + * + * Project: Shapelib + * Purpose: Implementation of search in ESRI SBN spatial index. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#include "shapefil.h" + +#include +#include +#include +#include + +SHP_CVSID("$Id: sbnsearch.c 28039 2014-11-30 18:24:59Z rouault $") + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +#define READ_MSB_INT(ptr) \ + (((ptr)[0] << 24) | ((ptr)[1] << 16) | ((ptr)[2] << 8) | (ptr)[3]) + +#define CACHED_DEPTH_LIMIT 8 + +typedef unsigned char uchar; + +typedef int coord; +/*typedef uchar coord;*/ + +typedef struct +{ + uchar *pabyShapeDesc; /* Cache of (nShapeCount * 8) bytes of the bins. May be NULL. */ + int nBinStart; /* Index of first bin for this node. */ + int nShapeCount; /* Number of shapes attached to this node. */ + int nBinCount; /* Number of bins for this node. May be 0 if node is empty. */ + int nBinOffset; /* Offset in file of the start of the first bin. May be 0 if node is empty. */ + + int bBBoxInit; /* TRUE if the following bounding box has been computed. */ + coord bMinX; /* Bounding box of the shapes directly attached to this node. */ + coord bMinY; /* This is *not* the theoretical footprint of the node. */ + coord bMaxX; + coord bMaxY; +} SBNNodeDescriptor; + +struct SBNSearchInfo +{ + SAHooks sHooks; + SAFile fpSBN; + SBNNodeDescriptor *pasNodeDescriptor; + int nShapeCount; /* Total number of shapes */ + int nMaxDepth; /* Tree depth */ + double dfMinX; /* Bounding box of all shapes */ + double dfMaxX; + double dfMinY; + double dfMaxY; + +#ifdef DEBUG_IO + int nTotalBytesRead; +#endif +}; + +typedef struct +{ + SBNSearchHandle hSBN; + + coord bMinX; /* Search bounding box */ + coord bMinY; + coord bMaxX; + coord bMaxY; + + int nShapeCount; + int nShapeAlloc; + int *panShapeId; /* 0 based */ + + uchar abyBinShape[8 * 100]; + +#ifdef DEBUG_IO + int nBytesRead; +#endif +} SearchStruct; + +/************************************************************************/ +/* SwapWord() */ +/* */ +/* Swap a 2, 4 or 8 byte word. */ +/************************************************************************/ + +static void SwapWord( int length, void * wordP ) + +{ + int i; + uchar temp; + + for( i=0; i < length/2; i++ ) + { + temp = ((uchar *) wordP)[i]; + ((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1]; + ((uchar *) wordP)[length-i-1] = temp; + } +} + +/************************************************************************/ +/* SBNOpenDiskTree() */ +/************************************************************************/ + +SBNSearchHandle SBNOpenDiskTree( const char* pszSBNFilename, + SAHooks *psHooks ) +{ + int i; + SBNSearchHandle hSBN; + uchar abyHeader[108]; + int nShapeCount; + int nMaxDepth; + int nMaxNodes; + int nNodeDescSize; + int nNodeDescCount; + uchar* pabyData = NULL; + SBNNodeDescriptor* pasNodeDescriptor = NULL; + uchar abyBinHeader[8]; + int nCurNode; + int nNextNonEmptyNode; + int nExpectedBinId; + int bBigEndian; + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this machine. */ +/* -------------------------------------------------------------------- */ + i = 1; + if( *((unsigned char *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; + +/* -------------------------------------------------------------------- */ +/* Initialize the handle structure. */ +/* -------------------------------------------------------------------- */ + hSBN = (SBNSearchHandle) + calloc(sizeof(struct SBNSearchInfo),1); + + if (psHooks == NULL) + SASetupDefaultHooks( &(hSBN->sHooks) ); + else + memcpy( &(hSBN->sHooks), psHooks, sizeof(SAHooks) ); + + hSBN->fpSBN = hSBN->sHooks.FOpen(pszSBNFilename, "rb"); + if (hSBN->fpSBN == NULL) + { + free(hSBN); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Check file header signature. */ +/* -------------------------------------------------------------------- */ + if (hSBN->sHooks.FRead(abyHeader, 108, 1, hSBN->fpSBN) != 1 || + abyHeader[0] != 0 || + abyHeader[1] != 0 || + abyHeader[2] != 0x27 || + (abyHeader[3] != 0x0A && abyHeader[3] != 0x0D) || + abyHeader[4] != 0xFF || + abyHeader[5] != 0xFF || + abyHeader[6] != 0xFE || + abyHeader[7] != 0x70) + { + hSBN->sHooks.Error( ".sbn file is unreadable, or corrupt." ); + SBNCloseDiskTree(hSBN); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read shapes bounding box. */ +/* -------------------------------------------------------------------- */ + + memcpy(&hSBN->dfMinX, abyHeader + 32, 8); + memcpy(&hSBN->dfMinY, abyHeader + 40, 8); + memcpy(&hSBN->dfMaxX, abyHeader + 48, 8); + memcpy(&hSBN->dfMaxY, abyHeader + 56, 8); + + if( !bBigEndian ) + { + SwapWord(8, &hSBN->dfMinX); + SwapWord(8, &hSBN->dfMinY); + SwapWord(8, &hSBN->dfMaxX); + SwapWord(8, &hSBN->dfMaxY); + } + + if( hSBN->dfMinX > hSBN->dfMaxX || + hSBN->dfMinY > hSBN->dfMaxY ) + { + hSBN->sHooks.Error( "Invalid extent in .sbn file." ); + SBNCloseDiskTree(hSBN); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read and check number of shapes. */ +/* -------------------------------------------------------------------- */ + nShapeCount = READ_MSB_INT(abyHeader + 28); + hSBN->nShapeCount = nShapeCount; + if (nShapeCount < 0 || nShapeCount > 256000000 ) + { + char szErrorMsg[64]; + sprintf(szErrorMsg, "Invalid shape count in .sbn : %d", nShapeCount ); + hSBN->sHooks.Error( szErrorMsg ); + SBNCloseDiskTree(hSBN); + return NULL; + } + + /* Empty spatial index */ + if( nShapeCount == 0 ) + { + return hSBN; + } + +/* -------------------------------------------------------------------- */ +/* Compute tree depth. */ +/* It is computed such as in average there are not more than 8 */ +/* shapes per node. With a minimum depth of 2, and a maximum of 24 */ +/* -------------------------------------------------------------------- */ + nMaxDepth = 2; + while( nMaxDepth < 24 && nShapeCount > ((1 << nMaxDepth) - 1) * 8 ) + nMaxDepth ++; + hSBN->nMaxDepth = nMaxDepth; + nMaxNodes = (1 << nMaxDepth) - 1; + +/* -------------------------------------------------------------------- */ +/* Check that the first bin id is 1. */ +/* -------------------------------------------------------------------- */ + + if( READ_MSB_INT(abyHeader + 100) != 1 ) + { + hSBN->sHooks.Error( "Unexpected bin id" ); + SBNCloseDiskTree(hSBN); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read and check number of node descriptors to be read. */ +/* There are at most (2^nMaxDepth) - 1, but all are not necessary */ +/* described. Non described nodes are empty. */ +/* -------------------------------------------------------------------- */ + nNodeDescSize = READ_MSB_INT(abyHeader + 104); + nNodeDescSize *= 2; /* 16-bit words */ + + /* each bin descriptor is made of 2 ints */ + nNodeDescCount = nNodeDescSize / 8; + + if ((nNodeDescSize % 8) != 0 || + nNodeDescCount < 0 || nNodeDescCount > nMaxNodes ) + { + char szErrorMsg[64]; + sprintf(szErrorMsg, + "Invalid node descriptor size in .sbn : %d", nNodeDescSize ); + hSBN->sHooks.Error( szErrorMsg ); + SBNCloseDiskTree(hSBN); + return NULL; + } + + pabyData = (uchar*) malloc( nNodeDescSize ); + pasNodeDescriptor = (SBNNodeDescriptor*) + calloc ( nMaxNodes, sizeof(SBNNodeDescriptor) ); + if (pabyData == NULL || pasNodeDescriptor == NULL) + { + free(pabyData); + free(pasNodeDescriptor); + hSBN->sHooks.Error( "Out of memory error" ); + SBNCloseDiskTree(hSBN); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read node descriptors. */ +/* -------------------------------------------------------------------- */ + if (hSBN->sHooks.FRead(pabyData, nNodeDescSize, 1, + hSBN->fpSBN) != 1) + { + free(pabyData); + free(pasNodeDescriptor); + hSBN->sHooks.Error( "Cannot read node descriptors" ); + SBNCloseDiskTree(hSBN); + return NULL; + } + + hSBN->pasNodeDescriptor = pasNodeDescriptor; + + for(i = 0; i < nNodeDescCount; i++) + { +/* -------------------------------------------------------------------- */ +/* Each node descriptor contains the index of the first bin that */ +/* described it, and the number of shapes in this first bin and */ +/* the following bins (in the relevant case). */ +/* -------------------------------------------------------------------- */ + int nBinStart = READ_MSB_INT(pabyData + 8 * i); + int nNodeShapeCount = READ_MSB_INT(pabyData + 8 * i + 4); + pasNodeDescriptor[i].nBinStart = nBinStart > 0 ? nBinStart : 0; + pasNodeDescriptor[i].nShapeCount = nNodeShapeCount; + + if ((nBinStart > 0 && nNodeShapeCount == 0) || + nNodeShapeCount < 0 || nNodeShapeCount > nShapeCount) + { + hSBN->sHooks.Error( "Inconsistent shape count in bin" ); + SBNCloseDiskTree(hSBN); + return NULL; + } + } + + free(pabyData); + pabyData = NULL; + + /* Locate first non-empty node */ + nCurNode = 0; + while(nCurNode < nMaxNodes && pasNodeDescriptor[nCurNode].nBinStart <= 0) + nCurNode ++; + + if( nCurNode >= nMaxNodes) + { + hSBN->sHooks.Error( "All nodes are empty" ); + SBNCloseDiskTree(hSBN); + return NULL; + } + + pasNodeDescriptor[nCurNode].nBinOffset = + hSBN->sHooks.FTell(hSBN->fpSBN); + + /* Compute the index of the next non empty node. */ + nNextNonEmptyNode = nCurNode + 1; + while(nNextNonEmptyNode < nMaxNodes && + pasNodeDescriptor[nNextNonEmptyNode].nBinStart <= 0) + nNextNonEmptyNode ++; + + nExpectedBinId = 1; + +/* -------------------------------------------------------------------- */ +/* Traverse bins to compute the offset of the first bin of each */ +/* node. */ +/* Note: we could use the .sbx file to compute the offsets instead.*/ +/* -------------------------------------------------------------------- */ + while( hSBN->sHooks.FRead(abyBinHeader, 8, 1, + hSBN->fpSBN) == 1 ) + { + int nBinId; + int nBinSize; + + nExpectedBinId ++; + + nBinId = READ_MSB_INT(abyBinHeader); + nBinSize = READ_MSB_INT(abyBinHeader + 4); + nBinSize *= 2; /* 16-bit words */ + + if( nBinId != nExpectedBinId ) + { + hSBN->sHooks.Error( "Unexpected bin id" ); + SBNCloseDiskTree(hSBN); + return NULL; + } + + /* Bins are always limited to 100 features */ + /* If there are more, then they are located in continuous bins */ + if( (nBinSize % 8) != 0 || nBinSize <= 0 || nBinSize > 100 * 8) + { + hSBN->sHooks.Error( "Unexpected bin size" ); + SBNCloseDiskTree(hSBN); + return NULL; + } + + if( nNextNonEmptyNode < nMaxNodes && + nBinId == pasNodeDescriptor[nNextNonEmptyNode].nBinStart ) + { + nCurNode = nNextNonEmptyNode; + pasNodeDescriptor[nCurNode].nBinOffset = + hSBN->sHooks.FTell(hSBN->fpSBN) - 8; + + /* Compute the index of the next non empty node. */ + nNextNonEmptyNode = nCurNode + 1; + while(nNextNonEmptyNode < nMaxNodes && + pasNodeDescriptor[nNextNonEmptyNode].nBinStart <= 0) + nNextNonEmptyNode ++; + } + + pasNodeDescriptor[nCurNode].nBinCount ++; + + /* Skip shape description */ + hSBN->sHooks.FSeek(hSBN->fpSBN, nBinSize, SEEK_CUR); + } + + return hSBN; +} + +/***********************************************************************/ +/* SBNCloseDiskTree() */ +/************************************************************************/ + +void SBNCloseDiskTree( SBNSearchHandle hSBN ) +{ + int i; + int nMaxNodes; + + if (hSBN == NULL) + return; + + if( hSBN->pasNodeDescriptor != NULL ) + { + nMaxNodes = (1 << hSBN->nMaxDepth) - 1; + for(i = 0; i < nMaxNodes; i++) + { + if( hSBN->pasNodeDescriptor[i].pabyShapeDesc != NULL ) + free(hSBN->pasNodeDescriptor[i].pabyShapeDesc); + } + } + + /* printf("hSBN->nTotalBytesRead = %d\n", hSBN->nTotalBytesRead); */ + + hSBN->sHooks.FClose(hSBN->fpSBN); + free(hSBN->pasNodeDescriptor); + free(hSBN); +} + + +/************************************************************************/ +/* SfRealloc() */ +/* */ +/* A realloc cover function that will access a NULL pointer as */ +/* a valid input. */ +/************************************************************************/ + +static void * SfRealloc( void * pMem, int nNewSize ) + +{ + if( pMem == NULL ) + return( (void *) malloc(nNewSize) ); + else + return( (void *) realloc(pMem,nNewSize) ); +} + +/************************************************************************/ +/* SBNAddShapeId() */ +/************************************************************************/ + +static int SBNAddShapeId( SearchStruct* psSearch, + int nShapeId ) +{ + if (psSearch->nShapeCount == psSearch->nShapeAlloc) + { + int* pNewPtr; + + psSearch->nShapeAlloc = + (int) (((psSearch->nShapeCount + 100) * 5) / 4); + pNewPtr = + (int *) SfRealloc( psSearch->panShapeId, + psSearch->nShapeAlloc * sizeof(int) ); + if( pNewPtr == NULL ) + { + psSearch->hSBN->sHooks.Error( "Out of memory error" ); + return FALSE; + } + psSearch->panShapeId = pNewPtr; + } + + psSearch->panShapeId[psSearch->nShapeCount] = nShapeId; + psSearch->nShapeCount ++; + return TRUE; +} + +/************************************************************************/ +/* SBNSearchDiskInternal() */ +/************************************************************************/ + +/* Due to the way integer coordinates are rounded, */ +/* we can use a strict intersection test, except when the node */ +/* bounding box or the search bounding box is degenerated. */ +#define SEARCH_BB_INTERSECTS(_bMinX, _bMinY, _bMaxX, _bMaxY) \ + (((bSearchMinX < _bMaxX && bSearchMaxX > _bMinX) || \ + ((_bMinX == _bMaxX || bSearchMinX == bSearchMaxX) && \ + bSearchMinX <= _bMaxX && bSearchMaxX >= _bMinX)) && \ + ((bSearchMinY < _bMaxY && bSearchMaxY > _bMinY) || \ + ((_bMinY == _bMaxY || bSearchMinY == bSearchMaxY ) && \ + bSearchMinY <= _bMaxY && bSearchMaxY >= _bMinY))) + + +static int SBNSearchDiskInternal( SearchStruct* psSearch, + int nDepth, + int nNodeId, + coord bNodeMinX, + coord bNodeMinY, + coord bNodeMaxX, + coord bNodeMaxY ) +{ + SBNSearchHandle hSBN; + SBNNodeDescriptor* psNode; + coord bSearchMinX = psSearch->bMinX; + coord bSearchMinY = psSearch->bMinY; + coord bSearchMaxX = psSearch->bMaxX; + coord bSearchMaxY = psSearch->bMaxY; + + hSBN = psSearch->hSBN; + + psNode = &(hSBN->pasNodeDescriptor[nNodeId]); + +/* -------------------------------------------------------------------- */ +/* Check if this node contains shapes that intersect the search */ +/* bounding box. */ +/* -------------------------------------------------------------------- */ + if ( psNode->bBBoxInit && + !(SEARCH_BB_INTERSECTS(psNode->bMinX, psNode->bMinY, + psNode->bMaxX, psNode->bMaxY)) ) + + { + /* No intersection, then don't try to read the shapes attached */ + /* to this node */ + } + +/* -------------------------------------------------------------------- */ +/* If this node contains shapes that are cached, then read them. */ +/* -------------------------------------------------------------------- */ + else if (psNode->pabyShapeDesc != NULL) + { + int j; + uchar* pabyShapeDesc = psNode->pabyShapeDesc; + + /* printf("nNodeId = %d, nDepth = %d\n", nNodeId, nDepth); */ + + for(j = 0; j < psNode->nShapeCount; j++) + { + coord bMinX = pabyShapeDesc[0]; + coord bMinY = pabyShapeDesc[1]; + coord bMaxX = pabyShapeDesc[2]; + coord bMaxY = pabyShapeDesc[3]; + + if( SEARCH_BB_INTERSECTS(bMinX, bMinY, bMaxX, bMaxY) ) + { + int nShapeId; + + nShapeId = READ_MSB_INT(pabyShapeDesc + 4); + + /* Caution : we count shape id starting from 0, and not 1 */ + nShapeId --; + + /*printf("shape=%d, minx=%d, miny=%d, maxx=%d, maxy=%d\n", + nShapeId, bMinX, bMinY, bMaxX, bMaxY);*/ + + if( !SBNAddShapeId( psSearch, nShapeId ) ) + return FALSE; + } + + pabyShapeDesc += 8; + } + } + +/* -------------------------------------------------------------------- */ +/* If the node has attached shapes (that are not (yet) cached), */ +/* then retrieve them from disk. */ +/* -------------------------------------------------------------------- */ + + else if (psNode->nBinCount > 0) + { + uchar abyBinHeader[8]; + int nBinSize, nShapes; + int nShapeCountAcc = 0; + int i, j; + + /* printf("nNodeId = %d, nDepth = %d\n", nNodeId, nDepth); */ + + hSBN->sHooks.FSeek(hSBN->fpSBN, psNode->nBinOffset, SEEK_SET); + + if (nDepth < CACHED_DEPTH_LIMIT) + psNode->pabyShapeDesc = (uchar*) malloc(psNode->nShapeCount * 8); + + for(i = 0; i < psNode->nBinCount; i++) + { + uchar* pabyBinShape; + +#ifdef DEBUG_IO + psSearch->nBytesRead += 8; +#endif + if( hSBN->sHooks.FRead(abyBinHeader, 8, 1, + hSBN->fpSBN) != 1) + { + hSBN->sHooks.Error( "I/O error" ); + free(psNode->pabyShapeDesc); + psNode->pabyShapeDesc = NULL; + return FALSE; + } + + if ( READ_MSB_INT(abyBinHeader + 0) != psNode->nBinStart + i ) + { + hSBN->sHooks.Error( "Unexpected bin id" ); + free(psNode->pabyShapeDesc); + psNode->pabyShapeDesc = NULL; + return FALSE; + } + + nBinSize = READ_MSB_INT(abyBinHeader + 4); + nBinSize *= 2; /* 16-bit words */ + + nShapes = nBinSize / 8; + + /* Bins are always limited to 100 features */ + if( (nBinSize % 8) != 0 || nShapes <= 0 || nShapes > 100) + { + hSBN->sHooks.Error( "Unexpected bin size" ); + free(psNode->pabyShapeDesc); + psNode->pabyShapeDesc = NULL; + return FALSE; + } + + if( nShapeCountAcc + nShapes > psNode->nShapeCount) + { + free(psNode->pabyShapeDesc); + psNode->pabyShapeDesc = NULL; + hSBN->sHooks.Error( "Inconsistent shape count for bin" ); + return FALSE; + } + + if (nDepth < CACHED_DEPTH_LIMIT && psNode->pabyShapeDesc != NULL) + { + pabyBinShape = psNode->pabyShapeDesc + nShapeCountAcc * 8; + } + else + { + pabyBinShape = psSearch->abyBinShape; + } + +#ifdef DEBUG_IO + psSearch->nBytesRead += nBinSize; +#endif + if (hSBN->sHooks.FRead(pabyBinShape, nBinSize, 1, + hSBN->fpSBN) != 1) + { + hSBN->sHooks.Error( "I/O error" ); + free(psNode->pabyShapeDesc); + psNode->pabyShapeDesc = NULL; + return FALSE; + } + + nShapeCountAcc += nShapes; + + if (i == 0 && !psNode->bBBoxInit) + { + psNode->bMinX = pabyBinShape[0]; + psNode->bMinY = pabyBinShape[1]; + psNode->bMaxX = pabyBinShape[2]; + psNode->bMaxY = pabyBinShape[3]; + } + + for(j = 0; j < nShapes; j++) + { + coord bMinX = pabyBinShape[0]; + coord bMinY = pabyBinShape[1]; + coord bMaxX = pabyBinShape[2]; + coord bMaxY = pabyBinShape[3]; + + if( !psNode->bBBoxInit ) + { +#ifdef sanity_checks +/* -------------------------------------------------------------------- */ +/* Those tests only check that the shape bounding box in the bin */ +/* are consistent (self-consistent and consistent with the node */ +/* they are attached to). They are optional however (as far as */ +/* the safety of runtime is concerned at least). */ +/* -------------------------------------------------------------------- */ + + if( !(((bMinX < bMaxX || + (bMinX == 0 && bMaxX == 0) || + (bMinX == 255 && bMaxX == 255))) && + ((bMinY < bMaxY || + (bMinY == 0 && bMaxY == 0) || + (bMinY == 255 && bMaxY == 255)))) || + bMaxX < bNodeMinX || bMaxY < bNodeMinY || + bMinX > bNodeMaxX || bMinY > bNodeMaxY ) + { + /*printf("shape %d %d %d %d\n", bMinX, bMinY, bMaxX, bMaxY); + printf("node %d %d %d %d\n", bNodeMinX, bNodeMinY, bNodeMaxX, bNodeMaxY);*/ + hSBN->sHooks.Error( + "Invalid shape bounding box in bin" ); + free(psNode->pabyShapeDesc); + psNode->pabyShapeDesc = NULL; + return FALSE; + } +#endif + if (bMinX < psNode->bMinX) psNode->bMinX = bMinX; + if (bMinY < psNode->bMinY) psNode->bMinY = bMinY; + if (bMaxX > psNode->bMaxX) psNode->bMaxX = bMaxX; + if (bMaxY > psNode->bMaxY) psNode->bMaxY = bMaxY; + } + + if( SEARCH_BB_INTERSECTS(bMinX, bMinY, bMaxX, bMaxY) ) + { + int nShapeId; + + nShapeId = READ_MSB_INT(pabyBinShape + 4); + + /* Caution : we count shape id starting from 0, and not 1 */ + nShapeId --; + + /*printf("shape=%d, minx=%d, miny=%d, maxx=%d, maxy=%d\n", + nShapeId, bMinX, bMinY, bMaxX, bMaxY);*/ + + if( !SBNAddShapeId( psSearch, nShapeId ) ) + return FALSE; + } + + pabyBinShape += 8; + } + } + + if( nShapeCountAcc != psNode->nShapeCount) + { + free(psNode->pabyShapeDesc); + psNode->pabyShapeDesc = NULL; + hSBN->sHooks.Error( "Inconsistent shape count for bin" ); + return FALSE; + } + + psNode->bBBoxInit = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Look up in child nodes. */ +/* -------------------------------------------------------------------- */ + if( nDepth + 1 < hSBN->nMaxDepth ) + { + nNodeId = nNodeId * 2 + 1; + + if( (nDepth % 2) == 0 ) /* x split */ + { + coord bMid = (coord) (1 + ((int)bNodeMinX + bNodeMaxX) / 2); + if( bSearchMinX <= bMid - 1 && + !SBNSearchDiskInternal( psSearch, nDepth + 1, nNodeId + 1, + bNodeMinX, bNodeMinY, + bMid - 1, bNodeMaxY ) ) + { + return FALSE; + } + if( bSearchMaxX >= bMid && + !SBNSearchDiskInternal( psSearch, nDepth + 1, nNodeId, + bMid, bNodeMinY, + bNodeMaxX, bNodeMaxY ) ) + { + return FALSE; + } + } + else /* y split */ + { + coord bMid = (coord) (1 + ((int)bNodeMinY + bNodeMaxY) / 2); + if( bSearchMinY <= bMid - 1 && + !SBNSearchDiskInternal( psSearch, nDepth + 1, nNodeId + 1, + bNodeMinX, bNodeMinY, + bNodeMaxX, bMid - 1 ) ) + { + return FALSE; + } + if( bSearchMaxY >= bMid && + !SBNSearchDiskInternal( psSearch, nDepth + 1, nNodeId, + bNodeMinX, bMid, + bNodeMaxX, bNodeMaxY ) ) + { + return FALSE; + } + } + } + + return TRUE; +} + +/************************************************************************/ +/* compare_ints() */ +/************************************************************************/ + +/* helper for qsort */ +static int +compare_ints( const void * a, const void * b) +{ + return (*(int*)a) - (*(int*)b); +} + +/************************************************************************/ +/* SBNSearchDiskTree() */ +/************************************************************************/ + +int* SBNSearchDiskTree( SBNSearchHandle hSBN, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount ) +{ + double dfMinX, dfMinY, dfMaxX, dfMaxY; + double dfDiskXExtent, dfDiskYExtent; + int bMinX, bMinY, bMaxX, bMaxY; + + *pnShapeCount = 0; + + dfMinX = padfBoundsMin[0]; + dfMinY = padfBoundsMin[1]; + dfMaxX = padfBoundsMax[0]; + dfMaxY = padfBoundsMax[1]; + + if( dfMinX > dfMaxX || dfMinY > dfMaxY ) + return NULL; + + if( dfMaxX < hSBN->dfMinX || dfMaxY < hSBN->dfMinY || + dfMinX > hSBN->dfMaxX || dfMinY > hSBN->dfMaxY ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Compute the search coordinates in [0,255]x[0,255] coord. space */ +/* -------------------------------------------------------------------- */ + dfDiskXExtent = hSBN->dfMaxX - hSBN->dfMinX; + dfDiskYExtent = hSBN->dfMaxY - hSBN->dfMinY; + + if ( dfDiskXExtent == 0.0 ) + { + bMinX = 0; + bMaxX = 255; + } + else + { + if( dfMinX < hSBN->dfMinX ) + bMinX = 0; + else + { + double dfMinX_255 = (dfMinX - hSBN->dfMinX) + / dfDiskXExtent * 255.0; + bMinX = (int)floor(dfMinX_255 - 0.005); + if( bMinX < 0 ) bMinX = 0; + } + + if( dfMaxX > hSBN->dfMaxX ) + bMaxX = 255; + else + { + double dfMaxX_255 = (dfMaxX - hSBN->dfMinX) + / dfDiskXExtent * 255.0; + bMaxX = (int)ceil(dfMaxX_255 + 0.005); + if( bMaxX > 255 ) bMaxX = 255; + } + } + + if ( dfDiskYExtent == 0.0 ) + { + bMinY = 0; + bMaxY = 255; + } + else + { + if( dfMinY < hSBN->dfMinY ) + bMinY = 0; + else + { + double dfMinY_255 = (dfMinY - hSBN->dfMinY) + / dfDiskYExtent * 255.0; + bMinY = (int)floor(dfMinY_255 - 0.005); + if( bMinY < 0 ) bMinY = 0; + } + + if( dfMaxY > hSBN->dfMaxY ) + bMaxY = 255; + else + { + double dfMaxY_255 = (dfMaxY - hSBN->dfMinY) + / dfDiskYExtent * 255.0; + bMaxY = (int)ceil(dfMaxY_255 + 0.005); + if( bMaxY > 255 ) bMaxY = 255; + } + } + +/* -------------------------------------------------------------------- */ +/* Run the search. */ +/* -------------------------------------------------------------------- */ + + return SBNSearchDiskTreeInteger(hSBN, + bMinX, bMinY, bMaxX, bMaxY, + pnShapeCount); +} + +/************************************************************************/ +/* SBNSearchDiskTreeInteger() */ +/************************************************************************/ + +int* SBNSearchDiskTreeInteger( SBNSearchHandle hSBN, + int bMinX, int bMinY, int bMaxX, int bMaxY, + int *pnShapeCount ) +{ + SearchStruct sSearch; + int bRet; + + *pnShapeCount = 0; + + if( bMinX > bMaxX || bMinY > bMaxY ) + return NULL; + + if( bMaxX < 0 || bMaxY < 0 || bMinX > 255 || bMinX > 255 ) + return NULL; + + if( hSBN->nShapeCount == 0 ) + return NULL; +/* -------------------------------------------------------------------- */ +/* Run the search. */ +/* -------------------------------------------------------------------- */ + sSearch.hSBN = hSBN; + sSearch.bMinX = (coord) (bMinX >= 0 ? bMinX : 0); + sSearch.bMinY = (coord) (bMinY >= 0 ? bMinY : 0); + sSearch.bMaxX = (coord) (bMaxX <= 255 ? bMaxX : 255); + sSearch.bMaxY = (coord) (bMaxY <= 255 ? bMaxY : 255); + sSearch.nShapeCount = 0; + sSearch.nShapeAlloc = 0; + sSearch.panShapeId = NULL; +#ifdef DEBUG_IO + sSearch.nBytesRead = 0; +#endif + + bRet = SBNSearchDiskInternal(&sSearch, 0, 0, 0, 0, 255, 255); + +#ifdef DEBUG_IO + hSBN->nTotalBytesRead += sSearch.nBytesRead; + /* printf("nBytesRead = %d\n", sSearch.nBytesRead); */ +#endif + + if( !bRet ) + { + if( sSearch.panShapeId != NULL ) + free( sSearch.panShapeId ); + *pnShapeCount = 0; + return NULL; + } + + *pnShapeCount = sSearch.nShapeCount; + +/* -------------------------------------------------------------------- */ +/* Sort the id array */ +/* -------------------------------------------------------------------- */ + qsort(sSearch.panShapeId, *pnShapeCount, sizeof(int), compare_ints); + + /* To distinguish between empty intersection from error case */ + if( sSearch.panShapeId == NULL ) + sSearch.panShapeId = (int*) calloc(1, sizeof(int)); + + return sSearch.panShapeId; +} + +/************************************************************************/ +/* SBNSearchFreeIds() */ +/************************************************************************/ + +void SBNSearchFreeIds( int* panShapeId ) +{ + free( panShapeId ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shape2ogr.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shape2ogr.cpp new file mode 100644 index 000000000..3a22036d7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shape2ogr.cpp @@ -0,0 +1,1474 @@ +/****************************************************************************** + * $Id: shape2ogr.cpp 29234 2015-05-22 19:17:03Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements translation of Shapefile shapes into OGR + * representation. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrshape.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: shape2ogr.cpp 29234 2015-05-22 19:17:03Z rouault $"); + +/************************************************************************/ +/* RingStartEnd */ +/* set first and last vertex for given ring */ +/************************************************************************/ +static void RingStartEnd ( SHPObject *psShape, int ring, int *start, int *end ) +{ + if( psShape->panPartStart == NULL ) + { + *start = 0; + *end = psShape->nVertices - 1; + } + else + { + if( ring == psShape->nParts - 1 ) + *end = psShape->nVertices - 1; + else + *end = psShape->panPartStart[ring+1] - 1; + + *start = psShape->panPartStart[ring]; + } +} + +/************************************************************************/ +/* CreateLinearRing */ +/* */ +/************************************************************************/ +static OGRLinearRing * CreateLinearRing ( SHPObject *psShape, int ring, int bHasZ ) +{ + OGRLinearRing *poRing; + int nRingStart, nRingEnd, nRingPoints; + + poRing = new OGRLinearRing(); + + RingStartEnd ( psShape, ring, &nRingStart, &nRingEnd ); + + nRingPoints = nRingEnd - nRingStart + 1; + + if (bHasZ) + poRing->setPoints( nRingPoints, psShape->padfX + nRingStart, + psShape->padfY + nRingStart, + psShape->padfZ + nRingStart ); + else + poRing->setPoints( nRingPoints, psShape->padfX + nRingStart, + psShape->padfY + nRingStart ); + + return ( poRing ); +} + + +/************************************************************************/ +/* SHPReadOGRObject() */ +/* */ +/* Read an item in a shapefile, and translate to OGR geometry */ +/* representation. */ +/************************************************************************/ + +OGRGeometry *SHPReadOGRObject( SHPHandle hSHP, int iShape, SHPObject *psShape ) +{ + // CPLDebug( "Shape", "SHPReadOGRObject( iShape=%d )\n", iShape ); + + OGRGeometry *poOGR = NULL; + + if( psShape == NULL ) + psShape = SHPReadObject( hSHP, iShape ); + + if( psShape == NULL ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Point. */ +/* -------------------------------------------------------------------- */ + else if( psShape->nSHPType == SHPT_POINT ) + { + poOGR = new OGRPoint( psShape->padfX[0], psShape->padfY[0] ); + } + else if(psShape->nSHPType == SHPT_POINTZ ) + { + poOGR = new OGRPoint( psShape->padfX[0], psShape->padfY[0], + psShape->padfZ[0] ); + } + else if(psShape->nSHPType == SHPT_POINTM ) + { + // Read XYM as XYZ + poOGR = new OGRPoint( psShape->padfX[0], psShape->padfY[0], + psShape->padfM[0] ); + } +/* -------------------------------------------------------------------- */ +/* Multipoint. */ +/* -------------------------------------------------------------------- */ + else if( psShape->nSHPType == SHPT_MULTIPOINT + || psShape->nSHPType == SHPT_MULTIPOINTM + || psShape->nSHPType == SHPT_MULTIPOINTZ ) + { + if (psShape->nVertices == 0) + { + poOGR = NULL; + } + else + { + OGRMultiPoint *poOGRMPoint = new OGRMultiPoint(); + int i; + + for( i = 0; i < psShape->nVertices; i++ ) + { + OGRPoint *poPoint; + + if( psShape->nSHPType == SHPT_MULTIPOINTZ ) + poPoint = new OGRPoint( psShape->padfX[i], psShape->padfY[i], + psShape->padfZ[i] ); + else + poPoint = new OGRPoint( psShape->padfX[i], psShape->padfY[i] ); + + poOGRMPoint->addGeometry( poPoint ); + + delete poPoint; + } + + poOGR = poOGRMPoint; + } + } + +/* -------------------------------------------------------------------- */ +/* Arc (LineString) */ +/* */ +/* I am ignoring parts though they can apply to arcs as well. */ +/* -------------------------------------------------------------------- */ + else if( psShape->nSHPType == SHPT_ARC + || psShape->nSHPType == SHPT_ARCM + || psShape->nSHPType == SHPT_ARCZ ) + { + if( psShape->nParts == 0 ) + { + poOGR = NULL; + } + else if( psShape->nParts == 1 ) + { + OGRLineString *poOGRLine = new OGRLineString(); + + if( psShape->nSHPType == SHPT_ARCZ ) + poOGRLine->setPoints( psShape->nVertices, + psShape->padfX, psShape->padfY, psShape->padfZ ); + else if( psShape->nSHPType == SHPT_ARCM ) + // Read XYM as XYZ + poOGRLine->setPoints( psShape->nVertices, + psShape->padfX, psShape->padfY, psShape->padfM ); + else + poOGRLine->setPoints( psShape->nVertices, + psShape->padfX, psShape->padfY ); + + poOGR = poOGRLine; + } + else + { + int iRing; + OGRMultiLineString *poOGRMulti; + + poOGR = poOGRMulti = new OGRMultiLineString(); + + for( iRing = 0; iRing < psShape->nParts; iRing++ ) + { + OGRLineString *poLine; + int nRingPoints; + int nRingStart; + + poLine = new OGRLineString(); + + if( psShape->panPartStart == NULL ) + { + nRingPoints = psShape->nVertices; + nRingStart = 0; + } + else + { + + if( iRing == psShape->nParts - 1 ) + nRingPoints = + psShape->nVertices - psShape->panPartStart[iRing]; + else + nRingPoints = psShape->panPartStart[iRing+1] + - psShape->panPartStart[iRing]; + nRingStart = psShape->panPartStart[iRing]; + } + + if( psShape->nSHPType == SHPT_ARCZ ) + poLine->setPoints( nRingPoints, + psShape->padfX + nRingStart, + psShape->padfY + nRingStart, + psShape->padfZ + nRingStart ); + else if( psShape->nSHPType == SHPT_ARCM ) + // Read XYM as XYZ + poLine->setPoints( nRingPoints, + psShape->padfX + nRingStart, + psShape->padfY + nRingStart, + psShape->padfM + nRingStart ); + else + poLine->setPoints( nRingPoints, + psShape->padfX + nRingStart, + psShape->padfY + nRingStart ); + + poOGRMulti->addGeometryDirectly( poLine ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Polygon */ +/* */ +/* As for now Z coordinate is not handled correctly */ +/* -------------------------------------------------------------------- */ + else if( psShape->nSHPType == SHPT_POLYGON + || psShape->nSHPType == SHPT_POLYGONM + || psShape->nSHPType == SHPT_POLYGONZ ) + { + int iRing; + int bHasZ = ( psShape->nSHPType == SHPT_POLYGONZ ); + + //CPLDebug( "Shape", "Shape type: polygon with nParts=%d \n", psShape->nParts ); + + if ( psShape->nParts == 0 ) + { + poOGR = NULL; + } + else if ( psShape->nParts == 1 ) + { + /* Surely outer ring */ + OGRPolygon *poOGRPoly = NULL; + OGRLinearRing *poRing = NULL; + + poOGR = poOGRPoly = new OGRPolygon(); + poRing = CreateLinearRing ( psShape, 0, bHasZ ); + poOGRPoly->addRingDirectly( poRing ); + } + + else + { + OGRPolygon** tabPolygons = new OGRPolygon*[psShape->nParts]; + for( iRing = 0; iRing < psShape->nParts; iRing++ ) + { + tabPolygons[iRing] = new OGRPolygon(); + tabPolygons[iRing]->addRingDirectly(CreateLinearRing ( psShape, iRing, bHasZ )); + } + + int isValidGeometry; + const char* papszOptions[] = { "METHOD=ONLY_CCW", NULL }; + poOGR = OGRGeometryFactory::organizePolygons( + (OGRGeometry**)tabPolygons, psShape->nParts, &isValidGeometry, papszOptions ); + + if (!isValidGeometry) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Geometry of polygon of fid %d cannot be translated to Simple Geometry. " + "All polygons will be contained in a multipolygon.\n", + iShape); + } + + delete[] tabPolygons; + } + } + +/* -------------------------------------------------------------------- */ +/* MultiPatch */ +/* -------------------------------------------------------------------- */ + else if( psShape->nSHPType == SHPT_MULTIPATCH ) + { + OGRMultiPolygon *poMP = new OGRMultiPolygon(); + int iPart; + OGRPolygon *poLastPoly = NULL; + + for( iPart = 0; iPart < psShape->nParts; iPart++ ) + { + int nPartPoints, nPartStart; + + // Figure out details about this part's vertex list. + if( psShape->panPartStart == NULL ) + { + nPartPoints = psShape->nVertices; + nPartStart = 0; + } + else + { + + if( iPart == psShape->nParts - 1 ) + nPartPoints = + psShape->nVertices - psShape->panPartStart[iPart]; + else + nPartPoints = psShape->panPartStart[iPart+1] + - psShape->panPartStart[iPart]; + nPartStart = psShape->panPartStart[iPart]; + } + + if( psShape->panPartType[iPart] == SHPP_TRISTRIP ) + { + int iBaseVert; + + if( poLastPoly != NULL ) + { + poMP->addGeometryDirectly( poLastPoly ); + poLastPoly = NULL; + } + + for( iBaseVert = 0; iBaseVert < nPartPoints-2; iBaseVert++ ) + { + OGRPolygon *poPoly = new OGRPolygon(); + OGRLinearRing *poRing = new OGRLinearRing(); + int iSrcVert = iBaseVert + nPartStart; + + poRing->setPoint( 0, + psShape->padfX[iSrcVert], + psShape->padfY[iSrcVert], + psShape->padfZ[iSrcVert] ); + poRing->setPoint( 1, + psShape->padfX[iSrcVert+1], + psShape->padfY[iSrcVert+1], + psShape->padfZ[iSrcVert+1] ); + + poRing->setPoint( 2, + psShape->padfX[iSrcVert+2], + psShape->padfY[iSrcVert+2], + psShape->padfZ[iSrcVert+2] ); + poRing->setPoint( 3, + psShape->padfX[iSrcVert], + psShape->padfY[iSrcVert], + psShape->padfZ[iSrcVert] ); + + poPoly->addRingDirectly( poRing ); + poMP->addGeometryDirectly( poPoly ); + } + } + else if( psShape->panPartType[iPart] == SHPP_TRIFAN ) + { + int iBaseVert; + + if( poLastPoly != NULL ) + { + poMP->addGeometryDirectly( poLastPoly ); + poLastPoly = NULL; + } + + for( iBaseVert = 0; iBaseVert < nPartPoints-2; iBaseVert++ ) + { + OGRPolygon *poPoly = new OGRPolygon(); + OGRLinearRing *poRing = new OGRLinearRing(); + int iSrcVert = iBaseVert + nPartStart; + + poRing->setPoint( 0, + psShape->padfX[nPartStart], + psShape->padfY[nPartStart], + psShape->padfZ[nPartStart] ); + poRing->setPoint( 1, + psShape->padfX[iSrcVert+1], + psShape->padfY[iSrcVert+1], + psShape->padfZ[iSrcVert+1] ); + + poRing->setPoint( 2, + psShape->padfX[iSrcVert+2], + psShape->padfY[iSrcVert+2], + psShape->padfZ[iSrcVert+2] ); + poRing->setPoint( 3, + psShape->padfX[nPartStart], + psShape->padfY[nPartStart], + psShape->padfZ[nPartStart] ); + + poPoly->addRingDirectly( poRing ); + poMP->addGeometryDirectly( poPoly ); + } + } + else if( psShape->panPartType[iPart] == SHPP_OUTERRING + || psShape->panPartType[iPart] == SHPP_INNERRING + || psShape->panPartType[iPart] == SHPP_FIRSTRING + || psShape->panPartType[iPart] == SHPP_RING ) + { + if( poLastPoly != NULL + && (psShape->panPartType[iPart] == SHPP_OUTERRING + || psShape->panPartType[iPart] == SHPP_FIRSTRING) ) + { + poMP->addGeometryDirectly( poLastPoly ); + poLastPoly = NULL; + } + + if( poLastPoly == NULL ) + poLastPoly = new OGRPolygon(); + + poLastPoly->addRingDirectly( + CreateLinearRing( psShape, iPart, TRUE ) ); + } + else + CPLDebug( "OGR", "Unrecognised parttype %d, ignored.", + psShape->panPartType[iPart] ); + } + + if( poLastPoly != NULL ) + { + poMP->addGeometryDirectly( poLastPoly ); + poLastPoly = NULL; + } + + poOGR = poMP; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise for now we just ignore the object. */ +/* -------------------------------------------------------------------- */ + else + { + if( psShape->nSHPType != SHPT_NULL ) + { + CPLDebug( "OGR", "Unsupported shape type in SHPReadOGRObject()" ); + } + + /* nothing returned */ + } + +/* -------------------------------------------------------------------- */ +/* Cleanup shape, and set feature id. */ +/* -------------------------------------------------------------------- */ + SHPDestroyObject( psShape ); + + return poOGR; +} + +/************************************************************************/ +/* SHPWriteOGRObject() */ +/************************************************************************/ + +OGRErr SHPWriteOGRObject( SHPHandle hSHP, int iShape, OGRGeometry *poGeom, + int bRewind) + +{ + int nReturnedShapeID; +/* ==================================================================== */ +/* Write "shape" with no geometry or with empty geometry */ +/* ==================================================================== */ + if( poGeom == NULL || poGeom->IsEmpty() ) + { + SHPObject *psShape; + + psShape = SHPCreateSimpleObject( SHPT_NULL, 0, NULL, NULL, NULL ); + nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape ); + SHPDestroyObject( psShape ); + if( nReturnedShapeID == -1 ) + { + //Assuming error is reported by SHPWriteObject() + return OGRERR_FAILURE; + } + } + +/* ==================================================================== */ +/* Write point geometry. */ +/* ==================================================================== */ + else if( hSHP->nShapeType == SHPT_POINT + || hSHP->nShapeType == SHPT_POINTM + || hSHP->nShapeType == SHPT_POINTZ ) + { + SHPObject *psShape; + OGRPoint *poPoint = (OGRPoint *) poGeom; + double dfX, dfY, dfZ = 0; + + if( poGeom->getGeometryType() != wkbPoint + && poGeom->getGeometryType() != wkbPoint25D ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to write non-point (%s) geometry to" + " point shapefile.", + poGeom->getGeometryName() ); + + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + + dfX = poPoint->getX(); + dfY = poPoint->getY(); + dfZ = poPoint->getZ(); + + psShape = SHPCreateSimpleObject( hSHP->nShapeType, 1, + &dfX, &dfY, &dfZ ); + nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape ); + SHPDestroyObject( psShape ); + if( nReturnedShapeID == -1 ) + return OGRERR_FAILURE; + } +/* ==================================================================== */ +/* MultiPoint. */ +/* ==================================================================== */ + else if( hSHP->nShapeType == SHPT_MULTIPOINT + || hSHP->nShapeType == SHPT_MULTIPOINTM + || hSHP->nShapeType == SHPT_MULTIPOINTZ ) + { + OGRMultiPoint *poMP = (OGRMultiPoint *) poGeom; + double *padfX, *padfY, *padfZ; + int iPoint; + SHPObject *psShape; + + if( wkbFlatten(poGeom->getGeometryType()) != wkbMultiPoint ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to write non-multipoint (%s) geometry to " + "multipoint shapefile.", + poGeom->getGeometryName() ); + + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + + padfX = (double *) CPLMalloc(sizeof(double)*poMP->getNumGeometries()); + padfY = (double *) CPLMalloc(sizeof(double)*poMP->getNumGeometries()); + padfZ = (double *) CPLCalloc(sizeof(double),poMP->getNumGeometries()); + + int iDstPoints = 0; + for( iPoint = 0; iPoint < poMP->getNumGeometries(); iPoint++ ) + { + OGRPoint *poPoint = (OGRPoint *) poMP->getGeometryRef(iPoint); + + /* Ignore POINT EMPTY */ + if (poPoint->IsEmpty() == FALSE) + { + padfX[iDstPoints] = poPoint->getX(); + padfY[iDstPoints] = poPoint->getY(); + padfZ[iDstPoints] = poPoint->getZ(); + iDstPoints ++; + } + else + CPLDebug( "OGR", + "Ignore POINT EMPTY inside MULTIPOINT in shapefile writer." ); + } + + psShape = SHPCreateSimpleObject( hSHP->nShapeType, + iDstPoints, + padfX, padfY, padfZ ); + nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape ); + SHPDestroyObject( psShape ); + + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + if( nReturnedShapeID == -1 ) + return OGRERR_FAILURE; + } + +/* ==================================================================== */ +/* Arcs from simple line strings. */ +/* ==================================================================== */ + else if( (hSHP->nShapeType == SHPT_ARC + || hSHP->nShapeType == SHPT_ARCM + || hSHP->nShapeType == SHPT_ARCZ) + && wkbFlatten(poGeom->getGeometryType()) == wkbLineString ) + { + OGRLineString *poArc = (OGRLineString *) poGeom; + double *padfX, *padfY, *padfZ; + int iPoint; + SHPObject *psShape; + + padfX = (double *) CPLMalloc(sizeof(double)*poArc->getNumPoints()); + padfY = (double *) CPLMalloc(sizeof(double)*poArc->getNumPoints()); + padfZ = (double *) CPLCalloc(sizeof(double),poArc->getNumPoints()); + + for( iPoint = 0; iPoint < poArc->getNumPoints(); iPoint++ ) + { + padfX[iPoint] = poArc->getX( iPoint ); + padfY[iPoint] = poArc->getY( iPoint ); + padfZ[iPoint] = poArc->getZ( iPoint ); + } + + psShape = SHPCreateSimpleObject( hSHP->nShapeType, + poArc->getNumPoints(), + padfX, padfY, padfZ ); + nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape ); + SHPDestroyObject( psShape ); + + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + if( nReturnedShapeID == -1 ) + return OGRERR_FAILURE; + } +/* ==================================================================== */ +/* Arcs - Try to treat as MultiLineString. */ +/* ==================================================================== */ + else if( hSHP->nShapeType == SHPT_ARC + || hSHP->nShapeType == SHPT_ARCM + || hSHP->nShapeType == SHPT_ARCZ ) + { + OGRMultiLineString *poML; + double *padfX=NULL, *padfY=NULL, *padfZ=NULL; + int iGeom, iPoint, nPointCount = 0; + SHPObject *psShape; + int *panRingStart; + int nParts = 0; + + poML = (OGRMultiLineString *) + OGRGeometryFactory::forceToMultiLineString( poGeom->clone() ); + + if( wkbFlatten(poML->getGeometryType()) != wkbMultiLineString ) + { + delete poML; + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to write non-linestring (%s) geometry to " + "ARC type shapefile.", + poGeom->getGeometryName() ); + + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + + panRingStart = (int *) + CPLMalloc(sizeof(int) * poML->getNumGeometries()); + + for( iGeom = 0; iGeom < poML->getNumGeometries(); iGeom++ ) + { + OGRLineString *poArc = (OGRLineString *) + poML->getGeometryRef(iGeom); + int nNewPoints = poArc->getNumPoints(); + + /* Ignore LINESTRING EMPTY */ + if (nNewPoints == 0) + { + CPLDebug( "OGR", + "Ignore LINESTRING EMPTY inside MULTILINESTRING in shapefile writer." ); + continue; + } + + panRingStart[nParts ++] = nPointCount; + + padfX = (double *) + CPLRealloc( padfX, sizeof(double)*(nNewPoints+nPointCount) ); + padfY = (double *) + CPLRealloc( padfY, sizeof(double)*(nNewPoints+nPointCount) ); + padfZ = (double *) + CPLRealloc( padfZ, sizeof(double)*(nNewPoints+nPointCount) ); + + for( iPoint = 0; iPoint < nNewPoints; iPoint++ ) + { + padfX[nPointCount] = poArc->getX( iPoint ); + padfY[nPointCount] = poArc->getY( iPoint ); + padfZ[nPointCount] = poArc->getZ( iPoint ); + nPointCount++; + } + } + + CPLAssert(nParts != 0); + + psShape = SHPCreateObject( hSHP->nShapeType, iShape, + nParts, + panRingStart, NULL, + nPointCount, padfX, padfY, padfZ, NULL); + nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape ); + SHPDestroyObject( psShape ); + + CPLFree( panRingStart ); + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + + delete poML; + if( nReturnedShapeID == -1 ) + return OGRERR_FAILURE; + } + +/* ==================================================================== */ +/* Polygons/MultiPolygons */ +/* ==================================================================== */ + else if( hSHP->nShapeType == SHPT_POLYGON + || hSHP->nShapeType == SHPT_POLYGONM + || hSHP->nShapeType == SHPT_POLYGONZ ) + { + OGRPolygon *poPoly; + OGRLinearRing *poRing, **papoRings=NULL; + double *padfX=NULL, *padfY=NULL, *padfZ=NULL; + int iPoint, iRing, nRings, nVertex=0, *panRingStart; + SHPObject *psShape; + + /* Collect list of rings */ + + if( wkbFlatten(poGeom->getGeometryType()) == wkbPolygon ) + { + poPoly = (OGRPolygon *) poGeom; + + if( poPoly->getExteriorRing() == NULL || + poPoly->getExteriorRing()->IsEmpty() ) + { + CPLDebug( "OGR", + "Ignore POLYGON EMPTY in shapefile writer." ); + nRings = 0; + } + else + { + int nSrcRings = poPoly->getNumInteriorRings()+1; + nRings = 0; + papoRings = (OGRLinearRing **) CPLMalloc(sizeof(void*)*nSrcRings); + for( iRing = 0; iRing < nSrcRings; iRing++ ) + { + if( iRing == 0 ) + papoRings[nRings] = poPoly->getExteriorRing(); + else + papoRings[nRings] = poPoly->getInteriorRing( iRing-1 ); + + /* Ignore LINEARRING EMPTY */ + if (papoRings[nRings]->getNumPoints() != 0) + nRings ++; + else + CPLDebug( "OGR", + "Ignore LINEARRING EMPTY inside POLYGON in shapefile writer." ); + } + } + } + else if( wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon + || wkbFlatten(poGeom->getGeometryType()) + == wkbGeometryCollection ) + { + OGRGeometryCollection *poGC = (OGRGeometryCollection *) poGeom; + int iGeom; + + nRings = 0; + for( iGeom=0; iGeom < poGC->getNumGeometries(); iGeom++ ) + { + poPoly = (OGRPolygon *) poGC->getGeometryRef( iGeom ); + + if( wkbFlatten(poPoly->getGeometryType()) != wkbPolygon ) + { + CPLFree( papoRings ); + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to write non-polygon (%s) geometry to " + "POLYGON type shapefile.", + poGeom->getGeometryName()); + + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + + /* Ignore POLYGON EMPTY */ + if( poPoly->getExteriorRing() == NULL || + poPoly->getExteriorRing()->IsEmpty() ) + { + CPLDebug( "OGR", + "Ignore POLYGON EMPTY inside MULTIPOLYGON in shapefile writer." ); + continue; + } + + papoRings = (OGRLinearRing **) CPLRealloc(papoRings, + sizeof(void*) * (nRings+poPoly->getNumInteriorRings()+1)); + for( iRing = 0; + iRing < poPoly->getNumInteriorRings()+1; + iRing++ ) + { + if( iRing == 0 ) + papoRings[nRings] = poPoly->getExteriorRing(); + else + papoRings[nRings] = + poPoly->getInteriorRing( iRing-1 ); + + /* Ignore LINEARRING EMPTY */ + if (papoRings[nRings]->getNumPoints() != 0) + nRings ++; + else + CPLDebug( "OGR", + "Ignore LINEARRING EMPTY inside POLYGON in shapefile writer." ); + } + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to write non-polygon (%s) geometry to " + "POLYGON type shapefile.", + poGeom->getGeometryName() ); + + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + +/* -------------------------------------------------------------------- */ +/* If we only had emptypolygons or unacceptable geometries */ +/* write NULL geometry object. */ +/* -------------------------------------------------------------------- */ + if( nRings == 0 ) + { + SHPObject *psShape; + + psShape = SHPCreateSimpleObject( SHPT_NULL, 0, NULL, NULL, NULL ); + nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape ); + SHPDestroyObject( psShape ); + + if( nReturnedShapeID == -1 ) + return OGRERR_FAILURE; + + return OGRERR_NONE; + } + + /* count vertices */ + nVertex = 0; + for( iRing = 0; iRing < nRings; iRing++ ) + nVertex += papoRings[iRing]->getNumPoints(); + + panRingStart = (int *) CPLMalloc(sizeof(int) * nRings); + padfX = (double *) CPLMalloc(sizeof(double)*nVertex); + padfY = (double *) CPLMalloc(sizeof(double)*nVertex); + padfZ = (double *) CPLMalloc(sizeof(double)*nVertex); + + /* collect vertices */ + nVertex = 0; + for( iRing = 0; iRing < nRings; iRing++ ) + { + poRing = papoRings[iRing]; + panRingStart[iRing] = nVertex; + + for( iPoint = 0; iPoint < poRing->getNumPoints(); iPoint++ ) + { + padfX[nVertex] = poRing->getX( iPoint ); + padfY[nVertex] = poRing->getY( iPoint ); + padfZ[nVertex] = poRing->getZ( iPoint ); + nVertex++; + } + } + + psShape = SHPCreateObject( hSHP->nShapeType, iShape, nRings, + panRingStart, NULL, + nVertex, padfX, padfY, padfZ, NULL ); + if( bRewind ) + SHPRewindObject( hSHP, psShape ); + nReturnedShapeID = SHPWriteObject( hSHP, iShape, psShape ); + SHPDestroyObject( psShape ); + + CPLFree( papoRings ); + CPLFree( panRingStart ); + CPLFree( padfX ); + CPLFree( padfY ); + CPLFree( padfZ ); + if( nReturnedShapeID == -1 ) + return OGRERR_FAILURE; + } + else + { + /* do nothing for multipatch */ + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SHPReadOGRFeatureDefn() */ +/************************************************************************/ + +OGRFeatureDefn *SHPReadOGRFeatureDefn( const char * pszName, + SHPHandle hSHP, DBFHandle hDBF, + const char* pszSHPEncoding, + int bAdjustType ) + +{ + OGRFeatureDefn *poDefn = new OGRFeatureDefn( pszName ); + int iField; + int nAdjustableFields = 0; + int nFieldCount = (hDBF) ? DBFGetFieldCount(hDBF) : 0; + + poDefn->Reference(); + + for( iField = 0; iField < nFieldCount; iField++ ) + { + char szFieldName[12] = {}; + int nWidth, nPrecision; + DBFFieldType eDBFType; + OGRFieldDefn oField("", OFTInteger); + char chNativeType; + + chNativeType = DBFGetNativeFieldType( hDBF, iField ); + eDBFType = DBFGetFieldInfo( hDBF, iField, szFieldName, + &nWidth, &nPrecision ); + + if( strlen(pszSHPEncoding) > 0 ) + { + char *pszUTF8Field = CPLRecode( szFieldName, + pszSHPEncoding, CPL_ENC_UTF8); + oField.SetName( pszUTF8Field ); + CPLFree( pszUTF8Field ); + } + else + oField.SetName( szFieldName ); + + oField.SetWidth( nWidth ); + oField.SetPrecision( nPrecision ); + + if( chNativeType == 'D' ) + { + /* XXX - mloskot: + * Shapefile date has following 8-chars long format: 20060101. + * OGR splits it as YYYY/MM/DD, so 2 additional characters are required. + * Is this correct assumtion? What about time part of date? + * Shouldn't this format look as datetime: YYYY/MM/DD HH:MM:SS + * with 4 additional characters? + */ + oField.SetWidth( nWidth + 2 ); + oField.SetType( OFTDate ); + } + else if( eDBFType == FTDouble ) + { + nAdjustableFields += (nPrecision == 0); + if( nPrecision == 0 && nWidth < 19 ) + oField.SetType( OFTInteger64 ); + else + oField.SetType( OFTReal ); + } + else if( eDBFType == FTInteger ) + oField.SetType( OFTInteger ); + else + oField.SetType( OFTString ); + + poDefn->AddFieldDefn( &oField ); + } + + /* Do an optional past if requested and needed to demote Integer64->Integer */ + /* or Real->Integer64/Integer */ + if( nAdjustableFields && bAdjustType ) + { + int* panAdjustableField = (int*)CPLCalloc(sizeof(int), nFieldCount); + for( iField = 0; iField < nFieldCount; iField++ ) + { + OGRFieldType eType = poDefn->GetFieldDefn(iField)->GetType(); + if( poDefn->GetFieldDefn(iField)->GetPrecision() == 0 && + (eType == OFTInteger64 || eType == OFTReal) ) + { + panAdjustableField[iField] = TRUE; + poDefn->GetFieldDefn(iField)->SetType(OFTInteger); + //poDefn->GetFieldDefn(iField)->SetWidth(0); + } + } + + int nRowCount = DBFGetRecordCount(hDBF); + for( int iRow = 0; iRow < nRowCount && nAdjustableFields; iRow ++ ) + { + for( iField = 0; iField < nFieldCount; iField++ ) + { + if( panAdjustableField[iField] ) + { + const char* pszValue = DBFReadStringAttribute( hDBF, iRow, iField ); + int nValueLength = (int)strlen(pszValue); + //if( nValueLength >= poDefn->GetFieldDefn(iField)->GetWidth()) + // poDefn->GetFieldDefn(iField)->SetWidth(nValueLength); + if( nValueLength >= 10 ) + { + int bOverflow; + GIntBig nVal = CPLAtoGIntBigEx(pszValue, FALSE, &bOverflow); + if( bOverflow ) + { + poDefn->GetFieldDefn(iField)->SetType(OFTReal); + panAdjustableField[iField] = FALSE; + nAdjustableFields --; + + /*char szFieldName[12] = {}; + int nWidth, nPrecision; + DBFGetFieldInfo( hDBF, iField, szFieldName, + &nWidth, &nPrecision ); + poDefn->GetFieldDefn(iField)->SetWidth(nWidth);*/ + } + else if( (GIntBig)(int)nVal != nVal ) + { + poDefn->GetFieldDefn(iField)->SetType(OFTInteger64); + if( poDefn->GetFieldDefn(iField)->GetWidth() <= 18 ) + { + panAdjustableField[iField] = FALSE; + nAdjustableFields --; + + /*char szFieldName[12] = {}; + int nWidth, nPrecision; + DBFGetFieldInfo( hDBF, iField, szFieldName, + &nWidth, &nPrecision ); + poDefn->GetFieldDefn(iField)->SetWidth(nWidth);*/ + } + } + } + } + } + } + + CPLFree(panAdjustableField); + } + + if( hSHP == NULL ) + poDefn->SetGeomType( wkbNone ); + else + { + switch( hSHP->nShapeType ) + { + case SHPT_POINT: + poDefn->SetGeomType( wkbPoint ); + break; + + case SHPT_POINTZ: + case SHPT_POINTM: + poDefn->SetGeomType( wkbPoint25D ); + break; + + case SHPT_ARC: + poDefn->SetGeomType( wkbLineString ); + break; + + case SHPT_ARCZ: + case SHPT_ARCM: + poDefn->SetGeomType( wkbLineString25D ); + break; + + case SHPT_MULTIPOINT: + poDefn->SetGeomType( wkbMultiPoint ); + break; + + case SHPT_MULTIPOINTZ: + case SHPT_MULTIPOINTM: + poDefn->SetGeomType( wkbMultiPoint25D ); + break; + + case SHPT_POLYGON: + poDefn->SetGeomType( wkbPolygon ); + break; + + case SHPT_POLYGONZ: + case SHPT_POLYGONM: + poDefn->SetGeomType( wkbPolygon25D ); + break; + + } + } + + return poDefn; +} + +/************************************************************************/ +/* SHPReadOGRFeature() */ +/************************************************************************/ + +OGRFeature *SHPReadOGRFeature( SHPHandle hSHP, DBFHandle hDBF, + OGRFeatureDefn * poDefn, int iShape, + SHPObject *psShape, const char *pszSHPEncoding ) + +{ + if( iShape < 0 + || (hSHP != NULL && iShape >= hSHP->nRecords) + || (hDBF != NULL && iShape >= hDBF->nRecords) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to read shape with feature id (%d) out of available" + " range.", iShape ); + return NULL; + } + + if( hDBF && DBFIsRecordDeleted( hDBF, iShape ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to read shape with feature id (%d), but it is marked deleted.", + iShape ); + if( psShape != NULL ) + SHPDestroyObject(psShape); + return NULL; + } + + OGRFeature *poFeature = new OGRFeature( poDefn ); + +/* -------------------------------------------------------------------- */ +/* Fetch geometry from Shapefile to OGRFeature. */ +/* -------------------------------------------------------------------- */ + if( hSHP != NULL ) + { + if( !poDefn->IsGeometryIgnored() ) + { + OGRGeometry* poGeometry = NULL; + poGeometry = SHPReadOGRObject( hSHP, iShape, psShape ); + + /* + * NOTE - mloskot: + * Two possibilities are expected here (bot hare tested by GDAL Autotests): + * 1. Read valid geometry and assign it directly. + * 2. Read and assign null geometry if it can not be read correctly from a shapefile + * + * It's NOT required here to test poGeometry == NULL. + */ + + poFeature->SetGeometryDirectly( poGeometry ); + } + else if( psShape != NULL ) + { + SHPDestroyObject( psShape ); + } + } + +/* -------------------------------------------------------------------- */ +/* Fetch feature attributes to OGRFeature fields. */ +/* -------------------------------------------------------------------- */ + + for( int iField = 0; iField < poDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn* poFieldDefn = poDefn->GetFieldDefn(iField); + if (poFieldDefn->IsIgnored() ) + continue; + + switch( poFieldDefn->GetType() ) + { + case OFTString: + { + const char *pszFieldVal = + DBFReadStringAttribute( hDBF, iShape, iField ); + if( pszFieldVal != NULL && pszFieldVal[0] != '\0' ) + { + if( pszSHPEncoding[0] != '\0' ) + { + char *pszUTF8Field = CPLRecode( pszFieldVal, + pszSHPEncoding, CPL_ENC_UTF8); + poFeature->SetField( iField, pszUTF8Field ); + CPLFree( pszUTF8Field ); + } + else + poFeature->SetField( iField, pszFieldVal ); + } + } + break; + + case OFTInteger: + case OFTInteger64: + case OFTReal: + if( !DBFIsAttributeNULL( hDBF, iShape, iField ) ) + poFeature->SetField( iField, + DBFReadStringAttribute( hDBF, iShape, + iField ) ); + break; + + case OFTDate: + { + OGRField sFld; + if( DBFIsAttributeNULL( hDBF, iShape, iField ) ) + continue; + + const char* pszDateValue = + DBFReadStringAttribute(hDBF,iShape,iField); + + /* Some DBF files have fields filled with spaces */ + /* (trimmed by DBFReadStringAttribute) to indicate null */ + /* values for dates (#4265) */ + if (pszDateValue[0] == '\0') + continue; + + memset( &sFld, 0, sizeof(sFld) ); + + if( strlen(pszDateValue) >= 10 && + pszDateValue[2] == '/' && pszDateValue[5] == '/' ) + { + sFld.Date.Month = (GByte)atoi(pszDateValue+0); + sFld.Date.Day = (GByte)atoi(pszDateValue+3); + sFld.Date.Year = (GInt16)atoi(pszDateValue+6); + } + else + { + int nFullDate = atoi(pszDateValue); + sFld.Date.Year = (GInt16)(nFullDate / 10000); + sFld.Date.Month = (GByte)((nFullDate / 100) % 100); + sFld.Date.Day = (GByte)(nFullDate % 100); + } + + poFeature->SetField( iField, &sFld ); + } + break; + + default: + CPLAssert( FALSE ); + } + } + + if( poFeature != NULL ) + poFeature->SetFID( iShape ); + + return( poFeature ); +} + +/************************************************************************/ +/* GrowField() */ +/************************************************************************/ + +static OGRErr GrowField(DBFHandle hDBF, int iField, OGRFieldDefn* poFieldDefn, + int nNewSize) +{ + char szFieldName[20] = {}; + int nOriWidth, nPrecision; + char chNativeType; + /* DBFFieldType eDBFType; */ + + chNativeType = DBFGetNativeFieldType( hDBF, iField ); + /* eDBFType = */ DBFGetFieldInfo( hDBF, iField, szFieldName, + &nOriWidth, &nPrecision ); + + CPLDebug("SHAPE", "Extending field %d (%s) from %d to %d characters", + iField, poFieldDefn->GetNameRef(), nOriWidth, nNewSize); + + if ( !DBFAlterFieldDefn( hDBF, iField, szFieldName, + chNativeType, nNewSize, nPrecision ) ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Extending field %d (%s) from %d to %d characters failed", + iField, poFieldDefn->GetNameRef(), nOriWidth, nNewSize); + return OGRERR_FAILURE; + } + else + { + poFieldDefn->SetWidth(nNewSize); + return OGRERR_NONE; + } +} + +/************************************************************************/ +/* SHPWriteOGRFeature() */ +/* */ +/* Write to an existing feature in a shapefile, or create a new */ +/* feature. */ +/************************************************************************/ + +OGRErr SHPWriteOGRFeature( SHPHandle hSHP, DBFHandle hDBF, + OGRFeatureDefn * poDefn, + OGRFeature * poFeature, + const char *pszSHPEncoding, + int* pbTruncationWarningEmitted, + int bRewind ) + +{ +#ifdef notdef +/* -------------------------------------------------------------------- */ +/* Don't write objects with missing geometry. */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetGeometryRef() == NULL && hSHP != NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to write feature without geometry not supported" + " for shapefile driver." ); + + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } +#endif + +/* -------------------------------------------------------------------- */ +/* Write the geometry. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr; + + if( hSHP != NULL ) + { + eErr = SHPWriteOGRObject( hSHP, (int)poFeature->GetFID(), + poFeature->GetGeometryRef(), bRewind ); + if( eErr != OGRERR_NONE ) + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* If there is no DBF, the job is done now. */ +/* -------------------------------------------------------------------- */ + if( hDBF == NULL ) + { +/* -------------------------------------------------------------------- */ +/* If this is a new feature, establish it's feature id. */ +/* -------------------------------------------------------------------- */ + if( hSHP != NULL && poFeature->GetFID() == OGRNullFID ) + poFeature->SetFID( hSHP->nRecords - 1 ); + + return OGRERR_NONE; + } + +/* -------------------------------------------------------------------- */ +/* If this is a new feature, establish it's feature id. */ +/* -------------------------------------------------------------------- */ + if( poFeature->GetFID() == OGRNullFID ) + poFeature->SetFID( DBFGetRecordCount( hDBF ) ); + +/* -------------------------------------------------------------------- */ +/* If this is the first feature to be written, verify that we */ +/* have at least one attribute in the DBF file. If not, create */ +/* a dummy FID attribute to satisfy the requirement that there */ +/* be at least one attribute. */ +/* -------------------------------------------------------------------- */ + if( DBFGetRecordCount( hDBF ) == 0 && DBFGetFieldCount( hDBF ) == 0 ) + { + CPLDebug( "OGR", + "Created dummy FID field for shapefile since schema is empty."); + DBFAddField( hDBF, "FID", FTInteger, 11, 0 ); + } + +/* -------------------------------------------------------------------- */ +/* Write out dummy field value if it exists. */ +/* -------------------------------------------------------------------- */ + if( DBFGetFieldCount( hDBF ) == 1 && poDefn->GetFieldCount() == 0 ) + { + DBFWriteIntegerAttribute( hDBF, (int)poFeature->GetFID(), 0, + (int)poFeature->GetFID() ); + } + +/* -------------------------------------------------------------------- */ +/* Write all the fields. */ +/* -------------------------------------------------------------------- */ + for( int iField = 0; iField < poDefn->GetFieldCount(); iField++ ) + { + if( !poFeature->IsFieldSet( iField ) ) + { + DBFWriteNULLAttribute( hDBF, (int)poFeature->GetFID(), iField ); + continue; + } + + OGRFieldDefn* poFieldDefn = poDefn->GetFieldDefn(iField); + + switch( poFieldDefn->GetType() ) + { + case OFTString: + { + const char *pszStr = poFeature->GetFieldAsString(iField); + char *pszEncoded = NULL; + if( strlen(pszSHPEncoding) > 0 ) + { + pszEncoded = + CPLRecode( pszStr, CPL_ENC_UTF8, pszSHPEncoding ); + pszStr = pszEncoded; + } + + int nStrLen = (int) strlen(pszStr); + if (nStrLen > OGR_DBF_MAX_FIELD_WIDTH) + { + if (!(*pbTruncationWarningEmitted)) + { + *pbTruncationWarningEmitted = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "Value '%s' of field %s has been truncated to %d characters.\n" + "This warning will not be emitted any more for that layer.", + poFeature->GetFieldAsString(iField), + poFieldDefn->GetNameRef(), + OGR_DBF_MAX_FIELD_WIDTH); + } + + nStrLen = OGR_DBF_MAX_FIELD_WIDTH; + + if(EQUAL(pszSHPEncoding, CPL_ENC_UTF8)) + { + const char *p = pszStr + nStrLen; + int byteCount = nStrLen; + while(byteCount > 0) + { + if( (*p & 0xc0) != 0x80 ) + { + nStrLen = byteCount; + break; + } + + byteCount--; + p--; + } + + pszEncoded[nStrLen] = 0; + } + } + + if ( nStrLen > poFieldDefn->GetWidth() ) + { + if (GrowField(hDBF, iField, poFieldDefn, nStrLen) != OGRERR_NONE) + { + CPLFree( pszEncoded ); + return OGRERR_FAILURE; + } + } + + DBFWriteStringAttribute( hDBF, (int)poFeature->GetFID(), iField, + pszStr ); + + CPLFree( pszEncoded ); + } + break; + + case OFTInteger: + case OFTInteger64: + { + char szFormat[20]; + char szValue[32]; + int nFieldWidth = poFieldDefn->GetWidth(); + sprintf(szFormat, "%%%d" CPL_FRMT_GB_WITHOUT_PREFIX "d", MIN(nFieldWidth, (int)sizeof(szValue)-1)); + sprintf(szValue, szFormat, poFeature->GetFieldAsInteger64(iField) ); + int nStrLen = strlen(szValue); + if( nStrLen > nFieldWidth ) + { + if (GrowField(hDBF, iField, poFieldDefn, nStrLen) != OGRERR_NONE) + { + return OGRERR_FAILURE; + } + } + + DBFWriteAttributeDirectly( hDBF, (int)poFeature->GetFID(), iField, + szValue ); + + break; + } + + case OFTReal: + { + double dfVal = poFeature->GetFieldAsDouble(iField); + /* IEEE754 doubles can store exact values of all integers below 2^53 */ + if( poFieldDefn->GetPrecision() == 0 && fabs(dfVal) > ((GIntBig)1 << 53) ) + { + static int nCounter = 0; + if( nCounter <= 10 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Value %.18g of field %s with 0 decimal of feature " CPL_FRMT_GIB " is bigger than 2^53. Precision loss likely occured or going to happen.%s", + dfVal, poFieldDefn->GetNameRef(), poFeature->GetFID(), + (nCounter == 10) ? " This warning will not be emitted anymore." : ""); + nCounter ++; + } + } + int ret = DBFWriteDoubleAttribute( hDBF, (int)poFeature->GetFID(), iField, + dfVal ); + if( !ret ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Value %.18g of field %s of feature " CPL_FRMT_GIB " not successfully written. " + "Possibly due to too larger number with respect to field width", + dfVal, poFieldDefn->GetNameRef(), poFeature->GetFID()); + } + break; + } + + case OFTDate: + { + const OGRField* psField = poFeature->GetRawFieldRef(iField); + + if( psField->Date.Year < 0 || psField->Date.Year > 9999 ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Year < 0 or > 9999 is not a valid date for shapefile"); + } + else + DBFWriteIntegerAttribute( hDBF, (int)poFeature->GetFID(), iField, + psField->Date.Year*10000 + psField->Date.Month*100 + psField->Date.Day ); + } + break; + + default: + { + /* Ignore fields of other types */ + break; + } + } + + } + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shapefil.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shapefil.h new file mode 100644 index 000000000..fc78b07fe --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shapefil.h @@ -0,0 +1,704 @@ +#ifndef SHAPEFILE_H_INCLUDED +#define SHAPEFILE_H_INCLUDED + +/****************************************************************************** + * $Id: shapefil.h 28337 2015-01-21 21:10:33Z rouault $ + * + * Project: Shapelib + * Purpose: Primary include file for Shapelib. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2012-2013, Even Rouault + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log: shapefil.h,v $ + * Revision 1.52 2011-12-11 22:26:46 fwarmerdam + * upgrade .qix access code to use SAHooks (gdal #3365) + * + * Revision 1.51 2011-07-24 05:59:25 fwarmerdam + * minimize use of CPLError in favor of SAHooks.Error() + * + * Revision 1.50 2011-05-13 17:35:17 fwarmerdam + * added DBFReorderFields() and DBFAlterFields() functions (from Even) + * + * Revision 1.49 2011-04-16 14:38:21 fwarmerdam + * avoid warnings with gcc on SHP_CVSID + * + * Revision 1.48 2010-08-27 23:42:52 fwarmerdam + * add SHPAPI_CALL attribute in code + * + * Revision 1.47 2010-01-28 11:34:34 fwarmerdam + * handle the shape file length limits more gracefully (#3236) + * + * Revision 1.46 2008-11-12 14:28:15 fwarmerdam + * DBFCreateField() now works on files with records + * + * Revision 1.45 2008/11/11 17:47:10 fwarmerdam + * added DBFDeleteField() function + * + * Revision 1.44 2008/01/16 20:05:19 bram + * Add file hooks that accept UTF-8 encoded filenames on some platforms. Use SASetupUtf8Hooks + * tosetup the hooks and check SHPAPI_UTF8_HOOKS for its availability. Currently, this + * is only available on the Windows platform that decodes the UTF-8 filenames to wide + * character strings and feeds them to _wfopen and _wremove. + * + * Revision 1.43 2008/01/10 16:35:30 fwarmerdam + * avoid _ prefix on #defined symbols (bug 1840) + * + * Revision 1.42 2007/12/18 18:28:14 bram + * - create hook for client specific atof (bugzilla ticket 1615) + * - check for NULL handle before closing cpCPG file, and close after reading. + * + * Revision 1.41 2007/12/15 20:25:32 bram + * dbfopen.c now reads the Code Page information from the DBF file, and exports + * this information as a string through the DBFGetCodePage function. This is + * either the number from the LDID header field ("LDID/") or as the + * content of an accompanying .CPG file. When creating a DBF file, the code can + * be set using DBFCreateEx. + * + * Revision 1.40 2007/12/06 07:00:25 fwarmerdam + * dbfopen now using SAHooks for fileio + * + * Revision 1.39 2007/12/04 20:37:56 fwarmerdam + * preliminary implementation of hooks api for io and errors + * + * Revision 1.38 2007/11/21 22:39:56 fwarmerdam + * close shx file in readonly mode (GDAL #1956) + * + * Revision 1.37 2007/10/27 03:31:14 fwarmerdam + * limit default depth of tree to 12 levels (gdal ticket #1594) + * + * Revision 1.36 2007/09/10 23:33:15 fwarmerdam + * Upstreamed support for visibility flag in SHPAPI_CALL for the needs + * of GDAL (gdal ticket #1810). + * + * Revision 1.35 2007/09/03 19:48:10 fwarmerdam + * move DBFReadAttribute() static dDoubleField into dbfinfo + * + * Revision 1.34 2006/06/17 15:33:32 fwarmerdam + * added pszWorkField - bug 1202 (rso) + * + * Revision 1.33 2006/02/15 01:14:30 fwarmerdam + * added DBFAddNativeFieldType + * + * Revision 1.32 2006/01/26 15:07:32 fwarmerdam + * add bMeasureIsUsed flag from Craig Bruce: Bug 1249 + * + * Revision 1.31 2006/01/05 01:27:27 fwarmerdam + * added dbf deletion mark/fetch + * + * Revision 1.30 2005/01/03 22:30:13 fwarmerdam + * added support for saved quadtrees + * + * Revision 1.29 2004/09/26 20:09:35 fwarmerdam + * avoid rcsid warnings + * + * Revision 1.28 2003/12/29 06:02:18 fwarmerdam + * added cpl_error.h option + * + * Revision 1.27 2003/04/21 18:30:37 warmerda + * added header write/update public methods + * + * Revision 1.26 2002/09/29 00:00:08 warmerda + * added FTLogical and logical attribute read/write calls + * + * Revision 1.25 2002/05/07 13:46:30 warmerda + * added DBFWriteAttributeDirectly(). + * + * Revision 1.24 2002/04/10 16:59:54 warmerda + * added SHPRewindObject + * + * Revision 1.23 2002/01/15 14:36:07 warmerda + * updated email address + * + * Revision 1.22 2002/01/15 14:32:00 warmerda + * try to improve SHPAPI_CALL docs + */ + +#include + +#ifdef USE_DBMALLOC +#include +#endif + +#ifdef USE_CPL +#include "cpl_conv.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************/ +/* Configuration options. */ +/************************************************************************/ + +/* -------------------------------------------------------------------- */ +/* Should the DBFReadStringAttribute() strip leading and */ +/* trailing white space? */ +/* -------------------------------------------------------------------- */ +#define TRIM_DBF_WHITESPACE + +/* -------------------------------------------------------------------- */ +/* Should we write measure values to the Multipatch object? */ +/* Reportedly ArcView crashes if we do write it, so for now it */ +/* is disabled. */ +/* -------------------------------------------------------------------- */ +#define DISABLE_MULTIPATCH_MEASURE + +/* -------------------------------------------------------------------- */ +/* SHPAPI_CALL */ +/* */ +/* The following two macros are present to allow forcing */ +/* various calling conventions on the Shapelib API. */ +/* */ +/* To force __stdcall conventions (needed to call Shapelib */ +/* from Visual Basic and/or Dephi I believe) the makefile could */ +/* be modified to define: */ +/* */ +/* /DSHPAPI_CALL=__stdcall */ +/* */ +/* If it is desired to force export of the Shapelib API without */ +/* using the shapelib.def file, use the following definition. */ +/* */ +/* /DSHAPELIB_DLLEXPORT */ +/* */ +/* To get both at once it will be necessary to hack this */ +/* include file to define: */ +/* */ +/* #define SHPAPI_CALL __declspec(dllexport) __stdcall */ +/* #define SHPAPI_CALL1 __declspec(dllexport) * __stdcall */ +/* */ +/* The complexity of the situtation is partly caused by the */ +/* peculiar requirement of Visual C++ that __stdcall appear */ +/* after any "*"'s in the return value of a function while the */ +/* __declspec(dllexport) must appear before them. */ +/* -------------------------------------------------------------------- */ + +#ifdef SHAPELIB_DLLEXPORT +# define SHPAPI_CALL __declspec(dllexport) +# define SHPAPI_CALL1(x) __declspec(dllexport) x +#endif + +#ifndef SHPAPI_CALL +# if defined(USE_GCC_VISIBILITY_FLAG) +# define SHPAPI_CALL __attribute__ ((visibility("default"))) +# define SHPAPI_CALL1(x) __attribute__ ((visibility("default"))) x +# else +# define SHPAPI_CALL +# endif +#endif + +#ifndef SHPAPI_CALL1 +# define SHPAPI_CALL1(x) x SHPAPI_CALL +#endif + +/* -------------------------------------------------------------------- */ +/* Macros for controlling CVSID and ensuring they don't appear */ +/* as unreferenced variables resulting in lots of warnings. */ +/* -------------------------------------------------------------------- */ +#ifndef DISABLE_CVSID +# if defined(__GNUC__) && __GNUC__ >= 4 +# define SHP_CVSID(string) static char cpl_cvsid[] __attribute__((used)) = string; +# else +# define SHP_CVSID(string) static char cpl_cvsid[] = string; \ +static char *cvsid_aw() { return( cvsid_aw() ? ((char *) NULL) : cpl_cvsid ); } +# endif +#else +# define SHP_CVSID(string) +#endif + +/* -------------------------------------------------------------------- */ +/* On some platforms, additional file IO hooks are defined that */ +/* UTF-8 encoded filenames Unicode filenames */ +/* -------------------------------------------------------------------- */ +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +# define SHPAPI_WINDOWS +# define SHPAPI_UTF8_HOOKS +#endif + +/* -------------------------------------------------------------------- */ +/* IO/Error hook functions. */ +/* -------------------------------------------------------------------- */ +typedef int *SAFile; + +#ifndef SAOffset +typedef unsigned long SAOffset; +#endif + +typedef struct { + SAFile (*FOpen) ( const char *filename, const char *access); + SAOffset (*FRead) ( void *p, SAOffset size, SAOffset nmemb, SAFile file); + SAOffset (*FWrite)( void *p, SAOffset size, SAOffset nmemb, SAFile file); + SAOffset (*FSeek) ( SAFile file, SAOffset offset, int whence ); + SAOffset (*FTell) ( SAFile file ); + int (*FFlush)( SAFile file ); + int (*FClose)( SAFile file ); + int (*Remove) ( const char *filename ); + + void (*Error) ( const char *message ); + double (*Atof) ( const char *str ); +} SAHooks; + +void SHPAPI_CALL SASetupDefaultHooks( SAHooks *psHooks ); +#ifdef SHPAPI_UTF8_HOOKS +void SHPAPI_CALL SASetupUtf8Hooks( SAHooks *psHooks ); +#endif + +/************************************************************************/ +/* SHP Support. */ +/************************************************************************/ +typedef struct tagSHPObject SHPObject; + +typedef struct +{ + SAHooks sHooks; + + SAFile fpSHP; + SAFile fpSHX; + + int nShapeType; /* SHPT_* */ + + unsigned int nFileSize; /* SHP file */ + + int nRecords; + int nMaxRecords; + unsigned int *panRecOffset; + unsigned int *panRecSize; + + double adBoundsMin[4]; + double adBoundsMax[4]; + + int bUpdated; + + unsigned char *pabyRec; + int nBufSize; + + int bFastModeReadObject; + unsigned char *pabyObjectBuf; + int nObjectBufSize; + SHPObject* psCachedObject; +} SHPInfo; + +typedef SHPInfo * SHPHandle; + +/* -------------------------------------------------------------------- */ +/* Shape types (nSHPType) */ +/* -------------------------------------------------------------------- */ +#define SHPT_NULL 0 +#define SHPT_POINT 1 +#define SHPT_ARC 3 +#define SHPT_POLYGON 5 +#define SHPT_MULTIPOINT 8 +#define SHPT_POINTZ 11 +#define SHPT_ARCZ 13 +#define SHPT_POLYGONZ 15 +#define SHPT_MULTIPOINTZ 18 +#define SHPT_POINTM 21 +#define SHPT_ARCM 23 +#define SHPT_POLYGONM 25 +#define SHPT_MULTIPOINTM 28 +#define SHPT_MULTIPATCH 31 + + +/* -------------------------------------------------------------------- */ +/* Part types - everything but SHPT_MULTIPATCH just uses */ +/* SHPP_RING. */ +/* -------------------------------------------------------------------- */ + +#define SHPP_TRISTRIP 0 +#define SHPP_TRIFAN 1 +#define SHPP_OUTERRING 2 +#define SHPP_INNERRING 3 +#define SHPP_FIRSTRING 4 +#define SHPP_RING 5 + +/* -------------------------------------------------------------------- */ +/* SHPObject - represents on shape (without attributes) read */ +/* from the .shp file. */ +/* -------------------------------------------------------------------- */ +struct tagSHPObject +{ + int nSHPType; + + int nShapeId; /* -1 is unknown/unassigned */ + + int nParts; + int *panPartStart; + int *panPartType; + + int nVertices; + double *padfX; + double *padfY; + double *padfZ; + double *padfM; + + double dfXMin; + double dfYMin; + double dfZMin; + double dfMMin; + + double dfXMax; + double dfYMax; + double dfZMax; + double dfMMax; + + int bMeasureIsUsed; + int bFastModeReadObject; +}; + +/* -------------------------------------------------------------------- */ +/* SHP API Prototypes */ +/* -------------------------------------------------------------------- */ + +/* If pszAccess is read-only, the fpSHX field of the returned structure */ +/* will be NULL as it is not necessary to keep the SHX file open */ +SHPHandle SHPAPI_CALL + SHPOpen( const char * pszShapeFile, const char * pszAccess ); +SHPHandle SHPAPI_CALL + SHPOpenLL( const char *pszShapeFile, const char *pszAccess, + SAHooks *psHooks ); + +/* If setting bFastMode = TRUE, the content of SHPReadObject() is owned by the SHPHandle. */ +/* So you cannot have 2 valid instances of SHPReadObject() simultaneously. */ +/* The SHPObject padfZ and padfM members may be NULL depending on the geometry */ +/* type. It is illegal to free at hand any of the pointer members of the SHPObject structure */ +void SHPAPI_CALL SHPSetFastModeReadObject( SHPHandle hSHP, int bFastMode ); + +SHPHandle SHPAPI_CALL + SHPCreate( const char * pszShapeFile, int nShapeType ); +SHPHandle SHPAPI_CALL + SHPCreateLL( const char * pszShapeFile, int nShapeType, + SAHooks *psHooks ); +void SHPAPI_CALL + SHPGetInfo( SHPHandle hSHP, int * pnEntities, int * pnShapeType, + double * padfMinBound, double * padfMaxBound ); + +SHPObject SHPAPI_CALL1(*) + SHPReadObject( SHPHandle hSHP, int iShape ); +int SHPAPI_CALL + SHPWriteObject( SHPHandle hSHP, int iShape, SHPObject * psObject ); + +void SHPAPI_CALL + SHPDestroyObject( SHPObject * psObject ); +void SHPAPI_CALL + SHPComputeExtents( SHPObject * psObject ); +SHPObject SHPAPI_CALL1(*) + SHPCreateObject( int nSHPType, int nShapeId, int nParts, + const int * panPartStart, const int * panPartType, + int nVertices, + const double * padfX, const double * padfY, + const double * padfZ, const double * padfM ); +SHPObject SHPAPI_CALL1(*) + SHPCreateSimpleObject( int nSHPType, int nVertices, + const double * padfX, + const double * padfY, + const double * padfZ ); + +int SHPAPI_CALL + SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ); + +void SHPAPI_CALL SHPClose( SHPHandle hSHP ); +void SHPAPI_CALL SHPWriteHeader( SHPHandle hSHP ); + +const char SHPAPI_CALL1(*) + SHPTypeName( int nSHPType ); +const char SHPAPI_CALL1(*) + SHPPartTypeName( int nPartType ); + +/* -------------------------------------------------------------------- */ +/* Shape quadtree indexing API. */ +/* -------------------------------------------------------------------- */ + +/* this can be two or four for binary or quad tree */ +#define MAX_SUBNODE 4 + +/* upper limit of tree levels for automatic estimation */ +#define MAX_DEFAULT_TREE_DEPTH 12 + +typedef struct shape_tree_node +{ + /* region covered by this node */ + double adfBoundsMin[4]; + double adfBoundsMax[4]; + + /* list of shapes stored at this node. The papsShapeObj pointers + or the whole list can be NULL */ + int nShapeCount; + int *panShapeIds; + SHPObject **papsShapeObj; + + int nSubNodes; + struct shape_tree_node *apsSubNode[MAX_SUBNODE]; + +} SHPTreeNode; + +typedef struct +{ + SHPHandle hSHP; + + int nMaxDepth; + int nDimension; + int nTotalCount; + + SHPTreeNode *psRoot; +} SHPTree; + +SHPTree SHPAPI_CALL1(*) + SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, + double *padfBoundsMin, double *padfBoundsMax ); +void SHPAPI_CALL + SHPDestroyTree( SHPTree * hTree ); + +int SHPAPI_CALL + SHPWriteTree( SHPTree *hTree, const char * pszFilename ); + +int SHPAPI_CALL + SHPTreeAddShapeId( SHPTree * hTree, SHPObject * psObject ); +int SHPAPI_CALL + SHPTreeRemoveShapeId( SHPTree * hTree, int nShapeId ); + +void SHPAPI_CALL + SHPTreeTrimExtraNodes( SHPTree * hTree ); + +int SHPAPI_CALL1(*) + SHPTreeFindLikelyShapes( SHPTree * hTree, + double * padfBoundsMin, + double * padfBoundsMax, + int * ); +int SHPAPI_CALL + SHPCheckBoundsOverlap( double *, double *, double *, double *, int ); + +int SHPAPI_CALL1(*) +SHPSearchDiskTree( FILE *fp, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount ); + + +typedef struct SHPDiskTreeInfo* SHPTreeDiskHandle; + +SHPTreeDiskHandle SHPAPI_CALL + SHPOpenDiskTree( const char* pszQIXFilename, + SAHooks *psHooks ); + +void SHPAPI_CALL + SHPCloseDiskTree( SHPTreeDiskHandle hDiskTree ); + +int SHPAPI_CALL1(*) +SHPSearchDiskTreeEx( SHPTreeDiskHandle hDiskTree, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount ); + +int SHPAPI_CALL + SHPWriteTreeLL(SHPTree *hTree, const char *pszFilename, SAHooks *psHooks ); + + +/* -------------------------------------------------------------------- */ +/* SBN Search API */ +/* -------------------------------------------------------------------- */ + +typedef struct SBNSearchInfo* SBNSearchHandle; + +SBNSearchHandle SHPAPI_CALL + SBNOpenDiskTree( const char* pszSBNFilename, + SAHooks *psHooks ); + +void SHPAPI_CALL + SBNCloseDiskTree( SBNSearchHandle hSBN ); + +int SHPAPI_CALL1(*) +SBNSearchDiskTree( SBNSearchHandle hSBN, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount ); + +int SHPAPI_CALL1(*) +SBNSearchDiskTreeInteger( SBNSearchHandle hSBN, + int bMinX, int bMinY, int bMaxX, int bMaxY, + int *pnShapeCount ); + +void SHPAPI_CALL SBNSearchFreeIds( int* panShapeId ); + +/************************************************************************/ +/* DBF Support. */ +/************************************************************************/ +typedef struct +{ + SAHooks sHooks; + + SAFile fp; + + int nRecords; + + int nRecordLength; + int nHeaderLength; + int nFields; + int *panFieldOffset; + int *panFieldSize; + int *panFieldDecimals; + char *pachFieldType; + + char *pszHeader; + + int nCurrentRecord; + int bCurrentRecordModified; + char *pszCurrentRecord; + + int nWorkFieldLength; + char *pszWorkField; + + int bNoHeader; + int bUpdated; + + union + { + double dfDoubleField; + int nIntField; + } fieldValue; + + int iLanguageDriver; + char *pszCodePage; + + int nUpdateYearSince1900; /* 0-255 */ + int nUpdateMonth; /* 1-12 */ + int nUpdateDay; /* 1-31 */ +} DBFInfo; + +typedef DBFInfo * DBFHandle; + +typedef enum { + FTString, + FTInteger, + FTDouble, + FTLogical, + FTInvalid +} DBFFieldType; + +#define XBASE_FLDHDR_SZ 32 + + +DBFHandle SHPAPI_CALL + DBFOpen( const char * pszDBFFile, const char * pszAccess ); +DBFHandle SHPAPI_CALL + DBFOpenLL( const char * pszDBFFile, const char * pszAccess, + SAHooks *psHooks ); +DBFHandle SHPAPI_CALL + DBFCreate( const char * pszDBFFile ); +DBFHandle SHPAPI_CALL + DBFCreateEx( const char * pszDBFFile, const char * pszCodePage ); +DBFHandle SHPAPI_CALL + DBFCreateLL( const char * pszDBFFile, const char * pszCodePage, SAHooks *psHooks ); + +int SHPAPI_CALL + DBFGetFieldCount( DBFHandle psDBF ); +int SHPAPI_CALL + DBFGetRecordCount( DBFHandle psDBF ); +int SHPAPI_CALL + DBFAddField( DBFHandle hDBF, const char * pszFieldName, + DBFFieldType eType, int nWidth, int nDecimals ); + +int SHPAPI_CALL + DBFAddNativeFieldType( DBFHandle hDBF, const char * pszFieldName, + char chType, int nWidth, int nDecimals ); + +int SHPAPI_CALL + DBFDeleteField( DBFHandle hDBF, int iField ); + +int SHPAPI_CALL + DBFReorderFields( DBFHandle psDBF, int* panMap ); + +int SHPAPI_CALL + DBFAlterFieldDefn( DBFHandle psDBF, int iField, const char * pszFieldName, + char chType, int nWidth, int nDecimals ); + +DBFFieldType SHPAPI_CALL + DBFGetFieldInfo( DBFHandle psDBF, int iField, + char * pszFieldName, int * pnWidth, int * pnDecimals ); + +int SHPAPI_CALL + DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName); + +int SHPAPI_CALL + DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField ); +double SHPAPI_CALL + DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField ); +const char SHPAPI_CALL1(*) + DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField ); +const char SHPAPI_CALL1(*) + DBFReadLogicalAttribute( DBFHandle hDBF, int iShape, int iField ); +int SHPAPI_CALL + DBFIsAttributeNULL( DBFHandle hDBF, int iShape, int iField ); + +int SHPAPI_CALL + DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, + int nFieldValue ); +int SHPAPI_CALL + DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField, + double dFieldValue ); +int SHPAPI_CALL + DBFWriteStringAttribute( DBFHandle hDBF, int iShape, int iField, + const char * pszFieldValue ); +int SHPAPI_CALL + DBFWriteNULLAttribute( DBFHandle hDBF, int iShape, int iField ); + +int SHPAPI_CALL + DBFWriteLogicalAttribute( DBFHandle hDBF, int iShape, int iField, + const char lFieldValue); +int SHPAPI_CALL + DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, + void * pValue ); +const char SHPAPI_CALL1(*) + DBFReadTuple(DBFHandle psDBF, int hEntity ); +int SHPAPI_CALL + DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ); + +int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape ); +int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, + int bIsDeleted ); + +DBFHandle SHPAPI_CALL + DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ); + +void SHPAPI_CALL + DBFClose( DBFHandle hDBF ); +void SHPAPI_CALL + DBFUpdateHeader( DBFHandle hDBF ); +char SHPAPI_CALL + DBFGetNativeFieldType( DBFHandle hDBF, int iField ); + +const char SHPAPI_CALL1(*) + DBFGetCodePage(DBFHandle psDBF ); + +void SHPAPI_CALL + DBFSetLastModifiedDate( DBFHandle psDBF, int nYYSince1900, int nMM, int nDD ); + +#ifdef __cplusplus +} +#endif + +#endif /* ndef SHAPEFILE_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shp_vsi.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shp_vsi.c new file mode 100644 index 000000000..4dda3f788 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shp_vsi.c @@ -0,0 +1,302 @@ +/****************************************************************************** + * $Id: shp_vsi.c 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: IO Redirection via VSI services for shp/dbf io. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "shp_vsi.h" +#include "cpl_error.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: shp_vsi.c 27384 2014-05-24 12:28:12Z rouault $"); + +typedef struct +{ + VSILFILE *fp; + char *pszFilename; + int bEnforce2GBLimit; + int bHasWarned2GB; + SAOffset nCurOffset; +} OGRSHPDBFFile; + +/************************************************************************/ +/* VSI_SHP_GetVSIL() */ +/************************************************************************/ + +VSILFILE* VSI_SHP_GetVSIL( SAFile file ) +{ + OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file; + return pFile->fp; +} + +/************************************************************************/ +/* VSI_SHP_GetFilename() */ +/************************************************************************/ + +const char* VSI_SHP_GetFilename( SAFile file ) +{ + OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file; + return pFile->pszFilename; +} + +/************************************************************************/ +/* VSI_SHP_OpenInternal() */ +/************************************************************************/ + +static +SAFile VSI_SHP_OpenInternal( const char *pszFilename, const char *pszAccess, + int bEnforce2GBLimit ) + +{ + OGRSHPDBFFile* pFile; + VSILFILE* fp = VSIFOpenL( pszFilename, pszAccess ); + if( fp == NULL ) + return NULL; + pFile = (OGRSHPDBFFile* )CPLCalloc(1,sizeof(OGRSHPDBFFile)); + pFile->fp = fp; + pFile->pszFilename = CPLStrdup(pszFilename); + pFile->bEnforce2GBLimit = bEnforce2GBLimit; + pFile->nCurOffset = 0; + return (SAFile) pFile; +} + +/************************************************************************/ +/* VSI_SHP_Open() */ +/************************************************************************/ + +static +SAFile VSI_SHP_Open( const char *pszFilename, const char *pszAccess ) + +{ + return VSI_SHP_OpenInternal(pszFilename, pszAccess, FALSE); +} + +/************************************************************************/ +/* VSI_SHP_Open2GBLimit() */ +/************************************************************************/ + +static +SAFile VSI_SHP_Open2GBLimit( const char *pszFilename, const char *pszAccess ) + +{ + return VSI_SHP_OpenInternal(pszFilename, pszAccess, TRUE); +} + +/************************************************************************/ +/* VSI_SHP_Read() */ +/************************************************************************/ + +static +SAOffset VSI_SHP_Read( void *p, SAOffset size, SAOffset nmemb, SAFile file ) + +{ + OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file; + SAOffset ret = (SAOffset) VSIFReadL( p, (size_t) size, (size_t) nmemb, + pFile->fp ); + pFile->nCurOffset += ret * size; + return ret; +} + +/************************************************************************/ +/* VSI_SHP_WriteMoreDataOK() */ +/************************************************************************/ + +int VSI_SHP_WriteMoreDataOK( SAFile file, SAOffset nExtraBytes ) +{ + OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file; + if( pFile->nCurOffset + nExtraBytes > 0x7FFFFFFF ) + { + if( pFile->bEnforce2GBLimit ) + { + CPLError( CE_Failure, CPLE_AppDefined, "2GB file size limit reached for %s", + pFile->pszFilename ); + return FALSE; + } + else if( !pFile->bHasWarned2GB ) + { + pFile->bHasWarned2GB = TRUE; + CPLError( CE_Warning, CPLE_AppDefined, "2GB file size limit reached for %s. " + "Going on, but might cause compatibility issues with third party software", + pFile->pszFilename ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* VSI_SHP_Write() */ +/************************************************************************/ + +static +SAOffset VSI_SHP_Write( void *p, SAOffset size, SAOffset nmemb, SAFile file ) + +{ + OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file; + SAOffset ret; + if( !VSI_SHP_WriteMoreDataOK( file, size * nmemb ) ) + return 0; + ret = (SAOffset) VSIFWriteL( p, (size_t) size, (size_t) nmemb, + pFile->fp ); + pFile->nCurOffset += ret * size; + return ret; +} + +/************************************************************************/ +/* VSI_SHP_Seek() */ +/************************************************************************/ + +static +SAOffset VSI_SHP_Seek( SAFile file, SAOffset offset, int whence ) + +{ + OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file; + SAOffset ret = (SAOffset) VSIFSeekL( pFile->fp, (vsi_l_offset) offset, whence ); + if( whence == 0 && ret == 0) + pFile->nCurOffset = offset; + else + pFile->nCurOffset = (SAOffset) VSIFTellL( pFile->fp ); + return ret; +} + +/************************************************************************/ +/* VSI_SHP_Tell() */ +/************************************************************************/ + +static +SAOffset VSI_SHP_Tell( SAFile file ) + +{ + OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file; + return (SAOffset) pFile->nCurOffset; +} + +/************************************************************************/ +/* VSI_SHP_Flush() */ +/************************************************************************/ + +static +int VSI_SHP_Flush( SAFile file ) + +{ + OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file; + return VSIFFlushL( pFile->fp ); +} + +/************************************************************************/ +/* VSI_SHP_Close() */ +/************************************************************************/ + +static +int VSI_SHP_Close( SAFile file ) + +{ + OGRSHPDBFFile* pFile = (OGRSHPDBFFile*) file; + int ret = VSIFCloseL( pFile->fp ); + CPLFree(pFile->pszFilename); + CPLFree(pFile); + return ret; +} + +/************************************************************************/ +/* SADError() */ +/************************************************************************/ + +static +void VSI_SHP_Error( const char *message ) + +{ + CPLError( CE_Failure, CPLE_AppDefined, "%s", message ); +} + +/************************************************************************/ +/* VSI_SHP_Remove() */ +/************************************************************************/ + +static +int VSI_SHP_Remove( const char *pszFilename ) + +{ + return VSIUnlink( pszFilename ); +} + +/************************************************************************/ +/* SASetupDefaultHooks() */ +/************************************************************************/ + +void SASetupDefaultHooks( SAHooks *psHooks ) + +{ + psHooks->FOpen = VSI_SHP_Open; + psHooks->FRead = VSI_SHP_Read; + psHooks->FWrite = VSI_SHP_Write; + psHooks->FSeek = VSI_SHP_Seek; + psHooks->FTell = VSI_SHP_Tell; + psHooks->FFlush = VSI_SHP_Flush; + psHooks->FClose = VSI_SHP_Close; + psHooks->Remove = VSI_SHP_Remove; + + psHooks->Error = VSI_SHP_Error; + psHooks->Atof = CPLAtof; +} + +/************************************************************************/ +/* VSI_SHP_GetHook() */ +/************************************************************************/ + +static const SAHooks sOGRHook = +{ + VSI_SHP_Open, + VSI_SHP_Read, + VSI_SHP_Write, + VSI_SHP_Seek, + VSI_SHP_Tell, + VSI_SHP_Flush, + VSI_SHP_Close, + VSI_SHP_Remove, + VSI_SHP_Error, + CPLAtof +}; + +static const SAHooks sOGRHook2GBLimit = +{ + VSI_SHP_Open2GBLimit, + VSI_SHP_Read, + VSI_SHP_Write, + VSI_SHP_Seek, + VSI_SHP_Tell, + VSI_SHP_Flush, + VSI_SHP_Close, + VSI_SHP_Remove, + VSI_SHP_Error, + CPLAtof +}; + +const SAHooks* VSI_SHP_GetHook(int b2GBLimit) +{ + return (b2GBLimit) ? &sOGRHook2GBLimit : &sOGRHook; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shp_vsi.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shp_vsi.h new file mode 100644 index 000000000..839e0d895 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shp_vsi.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * $Id: shp_vsi.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: IO Redirection via VSI services for shp/dbf io. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2007, Frank Warmerdam + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef SHP_VSI_H_INCLUDED +#define SHP_VSI_H_INCLUDED + +#include "cpl_vsi.h" +#include "shapefil.h" + +CPL_C_START + +const SAHooks* VSI_SHP_GetHook(int b2GBLimit); + +VSILFILE* VSI_SHP_GetVSIL( SAFile file ); +const char* VSI_SHP_GetFilename( SAFile file ); +int VSI_SHP_WriteMoreDataOK( SAFile file, SAOffset nExtraBytes ); + +CPL_C_END + +#endif /* SHP_VSI_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shpopen.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shpopen.c new file mode 100644 index 000000000..c0d1b08ee --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shpopen.c @@ -0,0 +1,2618 @@ +/****************************************************************************** + * $Id: shpopen.c,v 1.73 2012-01-24 22:33:01 fwarmerdam Exp $ + * + * Project: Shapelib + * Purpose: Implementation of core Shapefile read/write functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001, Frank Warmerdam + * Copyright (c) 2011-2013, Even Rouault + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log: shpopen.c,v $ + * Revision 1.73 2012-01-24 22:33:01 fwarmerdam + * fix memory leak on failure to open .shp (gdal #4410) + * + * Revision 1.72 2011-12-11 22:45:28 fwarmerdam + * fix failure return from SHPOpenLL. + * + * Revision 1.71 2011-09-15 03:33:58 fwarmerdam + * fix missing cast (#2344) + * + * Revision 1.70 2011-07-24 05:59:25 fwarmerdam + * minimize use of CPLError in favor of SAHooks.Error() + * + * Revision 1.69 2011-07-24 03:24:22 fwarmerdam + * fix memory leaks in error cases creating shapefiles (#2061) + * + * Revision 1.68 2010-08-27 23:42:52 fwarmerdam + * add SHPAPI_CALL attribute in code + * + * Revision 1.67 2010-07-01 08:15:48 fwarmerdam + * do not error out on an object with zero vertices + * + * Revision 1.66 2010-07-01 07:58:57 fwarmerdam + * minor cleanup of error handling + * + * Revision 1.65 2010-07-01 07:27:13 fwarmerdam + * white space formatting adjustments + * + * Revision 1.64 2010-01-28 11:34:34 fwarmerdam + * handle the shape file length limits more gracefully (#3236) + * + * Revision 1.63 2010-01-28 04:04:40 fwarmerdam + * improve numerical accuracy of SHPRewind() algs (gdal #3363) + * + * Revision 1.62 2010-01-17 05:34:13 fwarmerdam + * Remove asserts on x/y being null (#2148). + * + * Revision 1.61 2010-01-16 05:07:42 fwarmerdam + * allow 0/nulls in shpcreateobject (#2148) + * + * Revision 1.60 2009-09-17 20:50:02 bram + * on Win32, define snprintf as alias to _snprintf + * + * Revision 1.59 2008-03-14 05:25:31 fwarmerdam + * Correct crash on buggy geometries (gdal #2218) + * + * Revision 1.58 2008/01/08 23:28:26 bram + * on line 2095, use a float instead of a double to avoid a compiler warning + * + * Revision 1.57 2007/12/06 07:00:25 fwarmerdam + * dbfopen now using SAHooks for fileio + * + * Revision 1.56 2007/12/04 20:37:56 fwarmerdam + * preliminary implementation of hooks api for io and errors + * + * Revision 1.55 2007/11/21 22:39:56 fwarmerdam + * close shx file in readonly mode (GDAL #1956) + * + * Revision 1.54 2007/11/15 00:12:47 mloskot + * Backported recent changes from GDAL (Ticket #1415) to Shapelib. + * + * Revision 1.53 2007/11/14 22:31:08 fwarmerdam + * checks after mallocs to detect for corrupted/voluntary broken shapefiles. + * http://trac.osgeo.org/gdal/ticket/1991 + * + * Revision 1.52 2007/06/21 15:58:33 fwarmerdam + * fix for SHPRewindObject when rings touch at one vertex (gdal #976) + * + * Revision 1.51 2006/09/04 15:24:01 fwarmerdam + * Fixed up log message for 1.49. + * + * Revision 1.50 2006/09/04 15:21:39 fwarmerdam + * fix of last fix + * + * Revision 1.49 2006/09/04 15:21:00 fwarmerdam + * MLoskot: Added stronger test of Shapefile reading failures, e.g. truncated + * files. The problem was discovered by Tim Sutton and reported here + * https://svn.qgis.org/trac/ticket/200 + * + * Revision 1.48 2006/01/26 15:07:32 fwarmerdam + * add bMeasureIsUsed flag from Craig Bruce: Bug 1249 + * + * Revision 1.47 2006/01/04 20:07:23 fwarmerdam + * In SHPWriteObject() make sure that the record length is updated + * when rewriting an existing record. + * + * Revision 1.46 2005/02/11 17:17:46 fwarmerdam + * added panPartStart[0] validation + * + * Revision 1.45 2004/09/26 20:09:48 fwarmerdam + * const correctness changes + * + * Revision 1.44 2003/12/29 00:18:39 fwarmerdam + * added error checking for failed IO and optional CPL error reporting + * + * Revision 1.43 2003/12/01 16:20:08 warmerda + * be careful of zero vertex shapes + * + * Revision 1.42 2003/12/01 14:58:27 warmerda + * added degenerate object check in SHPRewindObject() + * + * Revision 1.41 2003/07/08 15:22:43 warmerda + * avoid warning + * + * Revision 1.40 2003/04/21 18:30:37 warmerda + * added header write/update public methods + * + * Revision 1.39 2002/08/26 06:46:56 warmerda + * avoid c++ comments + * + * Revision 1.38 2002/05/07 16:43:39 warmerda + * Removed debugging printf. + * + * Revision 1.37 2002/04/10 17:35:22 warmerda + * fixed bug in ring reversal code + * + * Revision 1.36 2002/04/10 16:59:54 warmerda + * added SHPRewindObject + * + * Revision 1.35 2001/12/07 15:10:44 warmerda + * fix if .shx fails to open + * + * Revision 1.34 2001/11/01 16:29:55 warmerda + * move pabyRec into SHPInfo for thread safety + * + * Revision 1.33 2001/07/03 12:18:15 warmerda + * Improved cleanup if SHX not found, provied by Riccardo Cohen. + * + * Revision 1.32 2001/06/22 01:58:07 warmerda + * be more careful about establishing initial bounds in face of NULL shapes + * + * Revision 1.31 2001/05/31 19:35:29 warmerda + * added support for writing null shapes + * + * Revision 1.30 2001/05/28 12:46:29 warmerda + * Add some checking on reasonableness of record count when opening. + * + * Revision 1.29 2001/05/23 13:36:52 warmerda + * added use of SHPAPI_CALL + * + * Revision 1.28 2001/02/06 22:25:06 warmerda + * fixed memory leaks when SHPOpen() fails + * + * Revision 1.27 2000/07/18 15:21:33 warmerda + * added better enforcement of -1 for append in SHPWriteObject + * + * Revision 1.26 2000/02/16 16:03:51 warmerda + * added null shape support + * + * Revision 1.25 1999/12/15 13:47:07 warmerda + * Fixed record size settings in .shp file (was 4 words too long) + * Added stdlib.h. + * + * Revision 1.24 1999/11/05 14:12:04 warmerda + * updated license terms + * + * Revision 1.23 1999/07/27 00:53:46 warmerda + * added support for rewriting shapes + * + * Revision 1.22 1999/06/11 19:19:11 warmerda + * Cleanup pabyRec static buffer on SHPClose(). + * + * Revision 1.21 1999/06/02 14:57:56 kshih + * Remove unused variables + * + * Revision 1.20 1999/04/19 21:04:17 warmerda + * Fixed syntax error. + * + * Revision 1.19 1999/04/19 21:01:57 warmerda + * Force access string to binary in SHPOpen(). + * + * Revision 1.18 1999/04/01 18:48:07 warmerda + * Try upper case extensions if lower case doesn't work. + * + * Revision 1.17 1998/12/31 15:29:39 warmerda + * Disable writing measure values to multipatch objects if + * DISABLE_MULTIPATCH_MEASURE is defined. + * + * Revision 1.16 1998/12/16 05:14:33 warmerda + * Added support to write MULTIPATCH. Fixed reading Z coordinate of + * MULTIPATCH. Fixed record size written for all feature types. + * + * Revision 1.15 1998/12/03 16:35:29 warmerda + * r+b is proper binary access string, not rb+. + * + * Revision 1.14 1998/12/03 15:47:56 warmerda + * Fixed setting of nVertices in SHPCreateObject(). + * + * Revision 1.13 1998/12/03 15:33:54 warmerda + * Made SHPCalculateExtents() separately callable. + * + * Revision 1.12 1998/11/11 20:01:50 warmerda + * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines. + * + * Revision 1.11 1998/11/09 20:56:44 warmerda + * Fixed up handling of file wide bounds. + * + * Revision 1.10 1998/11/09 20:18:51 warmerda + * Converted to support 3D shapefiles, and use of SHPObject. + * + * Revision 1.9 1998/02/24 15:09:05 warmerda + * Fixed memory leak. + * + * Revision 1.8 1997/12/04 15:40:29 warmerda + * Fixed byte swapping of record number, and record length fields in the + * .shp file. + * + * Revision 1.7 1995/10/21 03:15:58 warmerda + * Added support for binary file access, the magic cookie 9997 + * and tried to improve the int32 selection logic for 16bit systems. + * + * Revision 1.6 1995/09/04 04:19:41 warmerda + * Added fix for file bounds. + * + * Revision 1.5 1995/08/25 15:16:44 warmerda + * Fixed a couple of problems with big endian systems ... one with bounds + * and the other with multipart polygons. + * + * Revision 1.4 1995/08/24 18:10:17 warmerda + * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc() + * functions (such as on the Sun). + * + * Revision 1.3 1995/08/23 02:23:15 warmerda + * Added support for reading bounds, and fixed up problems in setting the + * file wide bounds. + * + * Revision 1.2 1995/08/04 03:16:57 warmerda + * Added header. + * + */ + +#include "shapefil.h" + +#include +#include +#include +#include +#include +#include + +SHP_CVSID("$Id: shpopen.c,v 1.73 2012-01-24 22:33:01 fwarmerdam Exp $") + +typedef unsigned char uchar; + +#if UINT_MAX == 65535 +typedef unsigned long int32; +#else +typedef unsigned int int32; +#endif + +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif + +#define ByteCopy( a, b, c ) memcpy( b, a, c ) +#ifndef MAX +# define MIN(a,b) ((ab) ? a : b) +#endif + +#if defined(WIN32) || defined(_WIN32) +# ifndef snprintf +# define snprintf _snprintf +# endif +#endif + +#if defined(CPL_LSB) +#define bBigEndian FALSE +#elif defined(CPL_MSB) +#define bBigEndian TRUE +#else +static int bBigEndian; +#endif + +/************************************************************************/ +/* SwapWord() */ +/* */ +/* Swap a 2, 4 or 8 byte word. */ +/************************************************************************/ + +static void SwapWord( int length, void * wordP ) + +{ + int i; + uchar temp; + + for( i=0; i < length/2; i++ ) + { + temp = ((uchar *) wordP)[i]; + ((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1]; + ((uchar *) wordP)[length-i-1] = temp; + } +} + +/************************************************************************/ +/* SfRealloc() */ +/* */ +/* A realloc cover function that will access a NULL pointer as */ +/* a valid input. */ +/************************************************************************/ + +static void * SfRealloc( void * pMem, int nNewSize ) + +{ + if( pMem == NULL ) + return( (void *) malloc(nNewSize) ); + else + return( (void *) realloc(pMem,nNewSize) ); +} + +/************************************************************************/ +/* SHPWriteHeader() */ +/* */ +/* Write out a header for the .shp and .shx files as well as the */ +/* contents of the index (.shx) file. */ +/************************************************************************/ + +void SHPAPI_CALL SHPWriteHeader( SHPHandle psSHP ) + +{ + uchar abyHeader[100]; + int i; + int32 i32; + double dValue; + int32 *panSHX; + + if (psSHP->fpSHX == NULL) + { + psSHP->sHooks.Error( "SHPWriteHeader failed : SHX file is closed"); + return; + } + +/* -------------------------------------------------------------------- */ +/* Prepare header block for .shp file. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < 100; i++ ) + abyHeader[i] = 0; + + abyHeader[2] = 0x27; /* magic cookie */ + abyHeader[3] = 0x0a; + + i32 = psSHP->nFileSize/2; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + i32 = 1000; /* version */ + ByteCopy( &i32, abyHeader+28, 4 ); + if( bBigEndian ) SwapWord( 4, abyHeader+28 ); + + i32 = psSHP->nShapeType; /* shape type */ + ByteCopy( &i32, abyHeader+32, 4 ); + if( bBigEndian ) SwapWord( 4, abyHeader+32 ); + + dValue = psSHP->adBoundsMin[0]; /* set bounds */ + ByteCopy( &dValue, abyHeader+36, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+36 ); + + dValue = psSHP->adBoundsMin[1]; + ByteCopy( &dValue, abyHeader+44, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+44 ); + + dValue = psSHP->adBoundsMax[0]; + ByteCopy( &dValue, abyHeader+52, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+52 ); + + dValue = psSHP->adBoundsMax[1]; + ByteCopy( &dValue, abyHeader+60, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+60 ); + + dValue = psSHP->adBoundsMin[2]; /* z */ + ByteCopy( &dValue, abyHeader+68, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+68 ); + + dValue = psSHP->adBoundsMax[2]; + ByteCopy( &dValue, abyHeader+76, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+76 ); + + dValue = psSHP->adBoundsMin[3]; /* m */ + ByteCopy( &dValue, abyHeader+84, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+84 ); + + dValue = psSHP->adBoundsMax[3]; + ByteCopy( &dValue, abyHeader+92, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+92 ); + +/* -------------------------------------------------------------------- */ +/* Write .shp file header. */ +/* -------------------------------------------------------------------- */ + if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0 + || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHP ) != 1 ) + { + psSHP->sHooks.Error( "Failure writing .shp header" ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Prepare, and write .shx file header. */ +/* -------------------------------------------------------------------- */ + i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0 + || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHX ) != 1 ) + { + psSHP->sHooks.Error( "Failure writing .shx header" ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Write out the .shx contents. */ +/* -------------------------------------------------------------------- */ + panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords); + + for( i = 0; i < psSHP->nRecords; i++ ) + { + panSHX[i*2 ] = psSHP->panRecOffset[i]/2; + panSHX[i*2+1] = psSHP->panRecSize[i]/2; + if( !bBigEndian ) SwapWord( 4, panSHX+i*2 ); + if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 ); + } + + if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX ) + != psSHP->nRecords ) + { + psSHP->sHooks.Error( "Failure writing .shx contents" ); + } + + free( panSHX ); + +/* -------------------------------------------------------------------- */ +/* Flush to disk. */ +/* -------------------------------------------------------------------- */ + psSHP->sHooks.FFlush( psSHP->fpSHP ); + psSHP->sHooks.FFlush( psSHP->fpSHX ); +} + +/************************************************************************/ +/* SHPOpen() */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPOpen( const char * pszLayer, const char * pszAccess ) + +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return SHPOpenLL( pszLayer, pszAccess, &sHooks ); +} + +/************************************************************************/ +/* SHPOpen() */ +/* */ +/* Open the .shp and .shx files based on the basename of the */ +/* files or either file name. */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) + +{ + char *pszFullname, *pszBasename; + SHPHandle psSHP; + + uchar *pabyBuf; + int i; + double dValue; + int bLazySHXLoading = FALSE; + +/* -------------------------------------------------------------------- */ +/* Ensure the access string is one of the legal ones. We */ +/* ensure the result string indicates binary to avoid common */ +/* problems on Windows. */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 + || strcmp(pszAccess,"r+") == 0 ) + pszAccess = "r+b"; + else + { + bLazySHXLoading = strchr(pszAccess, 'l') != NULL; + pszAccess = "rb"; + } + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this machine. */ +/* -------------------------------------------------------------------- */ +#if !defined(bBigEndian) + i = 1; + if( *((uchar *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; +#endif + +/* -------------------------------------------------------------------- */ +/* Initialize the info structure. */ +/* -------------------------------------------------------------------- */ + psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1); + + psSHP->bUpdated = FALSE; + memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) ); + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszLayer)+5); + strcpy( pszBasename, pszLayer ); + for( i = strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Open the .shp and .shx files. Note that files pulled from */ +/* a PC to Unix with upper case filenames won't work! */ +/* -------------------------------------------------------------------- */ + pszFullname = (char *) malloc(strlen(pszBasename) + 5); + sprintf( pszFullname, "%s.shp", pszBasename ) ; + psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess ); + if( psSHP->fpSHP == NULL ) + { + sprintf( pszFullname, "%s.SHP", pszBasename ); + psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess ); + } + + if( psSHP->fpSHP == NULL ) + { + char *pszMessage = (char *) malloc(strlen(pszBasename)*2+256); + sprintf( pszMessage, "Unable to open %s.shp or %s.SHP.", + pszBasename, pszBasename ); + psHooks->Error( pszMessage ); + free( pszMessage ); + + free( psSHP ); + free( pszBasename ); + free( pszFullname ); + + return NULL; + } + + sprintf( pszFullname, "%s.shx", pszBasename ); + psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess ); + if( psSHP->fpSHX == NULL ) + { + sprintf( pszFullname, "%s.SHX", pszBasename ); + psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess ); + } + + if( psSHP->fpSHX == NULL ) + { + char *pszMessage = (char *) malloc(strlen(pszBasename)*2+256); + sprintf( pszMessage, "Unable to open %s.shx or %s.SHX.", + pszBasename, pszBasename ); + psHooks->Error( pszMessage ); + free( pszMessage ); + + psSHP->sHooks.FClose( psSHP->fpSHP ); + free( psSHP ); + free( pszBasename ); + free( pszFullname ); + return( NULL ); + } + + free( pszFullname ); + free( pszBasename ); + +/* -------------------------------------------------------------------- */ +/* Read the file size from the SHP file. */ +/* -------------------------------------------------------------------- */ + pabyBuf = (uchar *) malloc(100); + psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHP ); + + psSHP->nFileSize = ((unsigned int)pabyBuf[24] * 256 * 256 * 256 + + (unsigned int)pabyBuf[25] * 256 * 256 + + (unsigned int)pabyBuf[26] * 256 + + (unsigned int)pabyBuf[27]) * 2; + +/* -------------------------------------------------------------------- */ +/* Read SHX file Header info */ +/* -------------------------------------------------------------------- */ + if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1 + || pabyBuf[0] != 0 + || pabyBuf[1] != 0 + || pabyBuf[2] != 0x27 + || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) ) + { + psSHP->sHooks.Error( ".shx file is unreadable, or corrupt." ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + free( psSHP ); + + return( NULL ); + } + + psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256 + + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256; + psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8; + + psSHP->nShapeType = pabyBuf[32]; + + if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 ) + { + char szError[200]; + + sprintf( szError, + "Record count in .shp header is %d, which seems\n" + "unreasonable. Assuming header is corrupt.", + psSHP->nRecords ); + psSHP->sHooks.Error( szError ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + free( psSHP ); + free(pabyBuf); + + return( NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Read the bounds. */ +/* -------------------------------------------------------------------- */ + if( bBigEndian ) SwapWord( 8, pabyBuf+36 ); + memcpy( &dValue, pabyBuf+36, 8 ); + psSHP->adBoundsMin[0] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+44 ); + memcpy( &dValue, pabyBuf+44, 8 ); + psSHP->adBoundsMin[1] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+52 ); + memcpy( &dValue, pabyBuf+52, 8 ); + psSHP->adBoundsMax[0] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+60 ); + memcpy( &dValue, pabyBuf+60, 8 ); + psSHP->adBoundsMax[1] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */ + memcpy( &dValue, pabyBuf+68, 8 ); + psSHP->adBoundsMin[2] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+76 ); + memcpy( &dValue, pabyBuf+76, 8 ); + psSHP->adBoundsMax[2] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */ + memcpy( &dValue, pabyBuf+84, 8 ); + psSHP->adBoundsMin[3] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+92 ); + memcpy( &dValue, pabyBuf+92, 8 ); + psSHP->adBoundsMax[3] = dValue; + + free( pabyBuf ); + +/* -------------------------------------------------------------------- */ +/* Read the .shx file to get the offsets to each record in */ +/* the .shp file. */ +/* -------------------------------------------------------------------- */ + psSHP->nMaxRecords = psSHP->nRecords; + + psSHP->panRecOffset = (unsigned int *) + malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ); + psSHP->panRecSize = (unsigned int *) + malloc(sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ); + if( bLazySHXLoading ) + pabyBuf = NULL; + else + pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) ); + + if (psSHP->panRecOffset == NULL || + psSHP->panRecSize == NULL || + (!bLazySHXLoading && pabyBuf == NULL)) + { + char szError[200]; + + sprintf(szError, + "Not enough memory to allocate requested memory (nRecords=%d).\n" + "Probably broken SHP file", + psSHP->nRecords ); + psSHP->sHooks.Error( szError ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + if (psSHP->panRecOffset) free( psSHP->panRecOffset ); + if (psSHP->panRecSize) free( psSHP->panRecSize ); + if (pabyBuf) free( pabyBuf ); + free( psSHP ); + return( NULL ); + } + + if( bLazySHXLoading ) + { + memset(psSHP->panRecOffset, 0, sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ); + memset(psSHP->panRecSize, 0, sizeof(unsigned int) * MAX(1,psSHP->nMaxRecords) ); + return( psSHP ); + } + + if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ) + != psSHP->nRecords ) + { + char szError[200]; + + sprintf( szError, + "Failed to read all values for %d records in .shx file.", + psSHP->nRecords ); + psSHP->sHooks.Error( szError ); + + /* SHX is short or unreadable for some reason. */ + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + free( psSHP->panRecOffset ); + free( psSHP->panRecSize ); + free( pabyBuf ); + free( psSHP ); + + return( NULL ); + } + + /* In read-only mode, we can close the SHX now */ + if (strcmp(pszAccess, "rb") == 0) + { + psSHP->sHooks.FClose( psSHP->fpSHX ); + psSHP->fpSHX = NULL; + } + + for( i = 0; i < psSHP->nRecords; i++ ) + { + int32 nOffset, nLength; + + memcpy( &nOffset, pabyBuf + i * 8, 4 ); + if( !bBigEndian ) SwapWord( 4, &nOffset ); + + memcpy( &nLength, pabyBuf + i * 8 + 4, 4 ); + if( !bBigEndian ) SwapWord( 4, &nLength ); + + psSHP->panRecOffset[i] = nOffset*2; + psSHP->panRecSize[i] = nLength*2; + } + free( pabyBuf ); + + return( psSHP ); +} + +/************************************************************************/ +/* SHPClose() */ +/* */ +/* Close the .shp and .shx files. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPClose(SHPHandle psSHP ) + +{ + if( psSHP == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Update the header if we have modified anything. */ +/* -------------------------------------------------------------------- */ + if( psSHP->bUpdated ) + SHPWriteHeader( psSHP ); + +/* -------------------------------------------------------------------- */ +/* Free all resources, and close files. */ +/* -------------------------------------------------------------------- */ + free( psSHP->panRecOffset ); + free( psSHP->panRecSize ); + + if ( psSHP->fpSHX != NULL) + psSHP->sHooks.FClose( psSHP->fpSHX ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + + if( psSHP->pabyRec != NULL ) + { + free( psSHP->pabyRec ); + } + + if( psSHP->pabyObjectBuf != NULL ) + { + free( psSHP->pabyObjectBuf ); + } + if( psSHP->psCachedObject != NULL ) + { + free( psSHP->psCachedObject ); + } + + free( psSHP ); +} + +/************************************************************************/ +/* SHPSetFastModeReadObject() */ +/************************************************************************/ + +/* If setting bFastMode = TRUE, the content of SHPReadObject() is owned by the SHPHandle. */ +/* So you cannot have 2 valid instances of SHPReadObject() simultaneously. */ +/* The SHPObject padfZ and padfM members may be NULL depending on the geometry */ +/* type. It is illegal to free at hand any of the pointer members of the SHPObject structure */ +void SHPAPI_CALL SHPSetFastModeReadObject( SHPHandle hSHP, int bFastMode ) +{ + if( bFastMode ) + { + if( hSHP->psCachedObject == NULL ) + { + hSHP->psCachedObject = (SHPObject*) calloc(1, sizeof(SHPObject)); + assert( hSHP->psCachedObject != NULL ); + } + } + + hSHP->bFastModeReadObject = bFastMode; +} + +/************************************************************************/ +/* SHPGetInfo() */ +/* */ +/* Fetch general information about the shape file. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, + double * padfMinBound, double * padfMaxBound ) + +{ + int i; + + if( psSHP == NULL ) + return; + + if( pnEntities != NULL ) + *pnEntities = psSHP->nRecords; + + if( pnShapeType != NULL ) + *pnShapeType = psSHP->nShapeType; + + for( i = 0; i < 4; i++ ) + { + if( padfMinBound != NULL ) + padfMinBound[i] = psSHP->adBoundsMin[i]; + if( padfMaxBound != NULL ) + padfMaxBound[i] = psSHP->adBoundsMax[i]; + } +} + +/************************************************************************/ +/* SHPCreate() */ +/* */ +/* Create a new shape file and return a handle to the open */ +/* shape file with read/write access. */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPCreate( const char * pszLayer, int nShapeType ) + +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return SHPCreateLL( pszLayer, nShapeType, &sHooks ); +} + +/************************************************************************/ +/* SHPCreate() */ +/* */ +/* Create a new shape file and return a handle to the open */ +/* shape file with read/write access. */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) + +{ + char *pszBasename = NULL, *pszFullname = NULL; + int i; + SAFile fpSHP = NULL, fpSHX = NULL; + uchar abyHeader[100]; + int32 i32; + double dValue; + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this system. */ +/* -------------------------------------------------------------------- */ +#if !defined(bBigEndian) + i = 1; + if( *((uchar *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; +#endif + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszLayer)+5); + strcpy( pszBasename, pszLayer ); + for( i = strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Open the two files so we can write their headers. */ +/* -------------------------------------------------------------------- */ + pszFullname = (char *) malloc(strlen(pszBasename) + 5); + sprintf( pszFullname, "%s.shp", pszBasename ); + fpSHP = psHooks->FOpen(pszFullname, "wb" ); + if( fpSHP == NULL ) + { + psHooks->Error( "Failed to create file .shp file." ); + goto error; + } + + sprintf( pszFullname, "%s.shx", pszBasename ); + fpSHX = psHooks->FOpen(pszFullname, "wb" ); + if( fpSHX == NULL ) + { + psHooks->Error( "Failed to create file .shx file." ); + goto error; + } + + free( pszFullname ); pszFullname = NULL; + free( pszBasename ); pszBasename = NULL; + +/* -------------------------------------------------------------------- */ +/* Prepare header block for .shp file. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < 100; i++ ) + abyHeader[i] = 0; + + abyHeader[2] = 0x27; /* magic cookie */ + abyHeader[3] = 0x0a; + + i32 = 50; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + i32 = 1000; /* version */ + ByteCopy( &i32, abyHeader+28, 4 ); + if( bBigEndian ) SwapWord( 4, abyHeader+28 ); + + i32 = nShapeType; /* shape type */ + ByteCopy( &i32, abyHeader+32, 4 ); + if( bBigEndian ) SwapWord( 4, abyHeader+32 ); + + dValue = 0.0; /* set bounds */ + ByteCopy( &dValue, abyHeader+36, 8 ); + ByteCopy( &dValue, abyHeader+44, 8 ); + ByteCopy( &dValue, abyHeader+52, 8 ); + ByteCopy( &dValue, abyHeader+60, 8 ); + +/* -------------------------------------------------------------------- */ +/* Write .shp file header. */ +/* -------------------------------------------------------------------- */ + if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 ) + { + psHooks->Error( "Failed to write .shp header." ); + goto error; + } + +/* -------------------------------------------------------------------- */ +/* Prepare, and write .shx file header. */ +/* -------------------------------------------------------------------- */ + i32 = 50; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + if( psHooks->FWrite( abyHeader, 100, 1, fpSHX ) != 1 ) + { + psHooks->Error( "Failed to write .shx header." ); + goto error; + } + +/* -------------------------------------------------------------------- */ +/* Close the files, and then open them as regular existing files. */ +/* -------------------------------------------------------------------- */ + psHooks->FClose( fpSHP ); + psHooks->FClose( fpSHX ); + + return( SHPOpenLL( pszLayer, "r+b", psHooks ) ); + +error: + if (pszFullname) free(pszFullname); + if (pszBasename) free(pszBasename); + if (fpSHP) psHooks->FClose( fpSHP ); + if (fpSHX) psHooks->FClose( fpSHX ); + return NULL; +} + +/************************************************************************/ +/* _SHPSetBounds() */ +/* */ +/* Compute a bounds rectangle for a shape, and set it into the */ +/* indicated location in the record. */ +/************************************************************************/ + +static void _SHPSetBounds( uchar * pabyRec, SHPObject * psShape ) + +{ + ByteCopy( &(psShape->dfXMin), pabyRec + 0, 8 ); + ByteCopy( &(psShape->dfYMin), pabyRec + 8, 8 ); + ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 ); + ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 ); + + if( bBigEndian ) + { + SwapWord( 8, pabyRec + 0 ); + SwapWord( 8, pabyRec + 8 ); + SwapWord( 8, pabyRec + 16 ); + SwapWord( 8, pabyRec + 24 ); + } +} + +/************************************************************************/ +/* SHPComputeExtents() */ +/* */ +/* Recompute the extents of a shape. Automatically done by */ +/* SHPCreateObject(). */ +/************************************************************************/ + +void SHPAPI_CALL +SHPComputeExtents( SHPObject * psObject ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Build extents for this object. */ +/* -------------------------------------------------------------------- */ + if( psObject->nVertices > 0 ) + { + psObject->dfXMin = psObject->dfXMax = psObject->padfX[0]; + psObject->dfYMin = psObject->dfYMax = psObject->padfY[0]; + psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0]; + psObject->dfMMin = psObject->dfMMax = psObject->padfM[0]; + } + + for( i = 0; i < psObject->nVertices; i++ ) + { + psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]); + psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]); + psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]); + psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]); + + psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]); + psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]); + psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]); + psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]); + } +} + +/************************************************************************/ +/* SHPCreateObject() */ +/* */ +/* Create a shape object. It should be freed with */ +/* SHPDestroyObject(). */ +/************************************************************************/ + +SHPObject SHPAPI_CALL1(*) +SHPCreateObject( int nSHPType, int nShapeId, int nParts, + const int * panPartStart, const int * panPartType, + int nVertices, const double *padfX, const double *padfY, + const double * padfZ, const double * padfM ) + +{ + SHPObject *psObject; + int i, bHasM, bHasZ; + + psObject = (SHPObject *) calloc(1,sizeof(SHPObject)); + psObject->nSHPType = nSHPType; + psObject->nShapeId = nShapeId; + psObject->bMeasureIsUsed = FALSE; + +/* -------------------------------------------------------------------- */ +/* Establish whether this shape type has M, and Z values. */ +/* -------------------------------------------------------------------- */ + if( nSHPType == SHPT_ARCM + || nSHPType == SHPT_POINTM + || nSHPType == SHPT_POLYGONM + || nSHPType == SHPT_MULTIPOINTM ) + { + bHasM = TRUE; + bHasZ = FALSE; + } + else if( nSHPType == SHPT_ARCZ + || nSHPType == SHPT_POINTZ + || nSHPType == SHPT_POLYGONZ + || nSHPType == SHPT_MULTIPOINTZ + || nSHPType == SHPT_MULTIPATCH ) + { + bHasM = TRUE; + bHasZ = TRUE; + } + else + { + bHasM = FALSE; + bHasZ = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Capture parts. Note that part type is optional, and */ +/* defaults to ring. */ +/* -------------------------------------------------------------------- */ + if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON + || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM + || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ + || nSHPType == SHPT_MULTIPATCH ) + { + psObject->nParts = MAX(1,nParts); + + psObject->panPartStart = (int *) + calloc(sizeof(int), psObject->nParts); + psObject->panPartType = (int *) + malloc(sizeof(int) * psObject->nParts); + + psObject->panPartStart[0] = 0; + psObject->panPartType[0] = SHPP_RING; + + for( i = 0; i < nParts; i++ ) + { + if( psObject->panPartStart != NULL ) + psObject->panPartStart[i] = panPartStart[i]; + + if( panPartType != NULL ) + psObject->panPartType[i] = panPartType[i]; + else + psObject->panPartType[i] = SHPP_RING; + } + + if( psObject->panPartStart[0] != 0 ) + psObject->panPartStart[0] = 0; + } + +/* -------------------------------------------------------------------- */ +/* Capture vertices. Note that X, Y, Z and M are optional. */ +/* -------------------------------------------------------------------- */ + if( nVertices > 0 ) + { + psObject->padfX = (double *) calloc(sizeof(double),nVertices); + psObject->padfY = (double *) calloc(sizeof(double),nVertices); + psObject->padfZ = (double *) calloc(sizeof(double),nVertices); + psObject->padfM = (double *) calloc(sizeof(double),nVertices); + + for( i = 0; i < nVertices; i++ ) + { + if( padfX != NULL ) + psObject->padfX[i] = padfX[i]; + if( padfY != NULL ) + psObject->padfY[i] = padfY[i]; + if( padfZ != NULL && bHasZ ) + psObject->padfZ[i] = padfZ[i]; + if( padfM != NULL && bHasM ) + psObject->padfM[i] = padfM[i]; + } + if( padfM != NULL && bHasM ) + psObject->bMeasureIsUsed = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Compute the extents. */ +/* -------------------------------------------------------------------- */ + psObject->nVertices = nVertices; + SHPComputeExtents( psObject ); + + return( psObject ); +} + +/************************************************************************/ +/* SHPCreateSimpleObject() */ +/* */ +/* Create a simple (common) shape object. Destroy with */ +/* SHPDestroyObject(). */ +/************************************************************************/ + +SHPObject SHPAPI_CALL1(*) +SHPCreateSimpleObject( int nSHPType, int nVertices, + const double * padfX, const double * padfY, + const double * padfZ ) + +{ + return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL, + nVertices, padfX, padfY, padfZ, NULL ) ); +} + +/************************************************************************/ +/* SHPWriteObject() */ +/* */ +/* Write out the vertices of a new structure. Note that it is */ +/* only possible to write vertices at the end of the file. */ +/************************************************************************/ + +int SHPAPI_CALL +SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) + +{ + unsigned int nRecordOffset, nRecordSize=0; + int i; + uchar *pabyRec; + int32 i32; + int bExtendFile = FALSE; + + psSHP->bUpdated = TRUE; + +/* -------------------------------------------------------------------- */ +/* Ensure that shape object matches the type of the file it is */ +/* being written to. */ +/* -------------------------------------------------------------------- */ + assert( psObject->nSHPType == psSHP->nShapeType + || psObject->nSHPType == SHPT_NULL ); + +/* -------------------------------------------------------------------- */ +/* Ensure that -1 is used for appends. Either blow an */ +/* assertion, or if they are disabled, set the shapeid to -1 */ +/* for appends. */ +/* -------------------------------------------------------------------- */ + assert( nShapeId == -1 + || (nShapeId >= 0 && nShapeId < psSHP->nRecords) ); + + if( nShapeId != -1 && nShapeId >= psSHP->nRecords ) + nShapeId = -1; + +/* -------------------------------------------------------------------- */ +/* Add the new entity to the in memory index. */ +/* -------------------------------------------------------------------- */ + if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords ) + { + psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100); + + psSHP->panRecOffset = (unsigned int *) + SfRealloc(psSHP->panRecOffset,sizeof(unsigned int) * psSHP->nMaxRecords ); + psSHP->panRecSize = (unsigned int *) + SfRealloc(psSHP->panRecSize,sizeof(unsigned int) * psSHP->nMaxRecords ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize record. */ +/* -------------------------------------------------------------------- */ + pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) + + psObject->nParts * 8 + 128); + +/* -------------------------------------------------------------------- */ +/* Extract vertices for a Polygon or Arc. */ +/* -------------------------------------------------------------------- */ + if( psObject->nSHPType == SHPT_POLYGON + || psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_POLYGONM + || psObject->nSHPType == SHPT_ARC + || psObject->nSHPType == SHPT_ARCZ + || psObject->nSHPType == SHPT_ARCM + || psObject->nSHPType == SHPT_MULTIPATCH ) + { + int32 nPoints, nParts; + int i; + + nPoints = psObject->nVertices; + nParts = psObject->nParts; + + _SHPSetBounds( pabyRec + 12, psObject ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + if( bBigEndian ) SwapWord( 4, &nParts ); + + ByteCopy( &nPoints, pabyRec + 40 + 8, 4 ); + ByteCopy( &nParts, pabyRec + 36 + 8, 4 ); + + nRecordSize = 52; + + /* + * Write part start positions. + */ + ByteCopy( psObject->panPartStart, pabyRec + 44 + 8, + 4 * psObject->nParts ); + for( i = 0; i < psObject->nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i ); + nRecordSize += 4; + } + + /* + * Write multipatch part types if needed. + */ + if( psObject->nSHPType == SHPT_MULTIPATCH ) + { + memcpy( pabyRec + nRecordSize, psObject->panPartType, + 4*psObject->nParts ); + for( i = 0; i < psObject->nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize ); + nRecordSize += 4; + } + } + + /* + * Write the (x,y) vertex values. + */ + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 ); + ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 ); + + if( bBigEndian ) + SwapWord( 8, pabyRec + nRecordSize ); + + if( bBigEndian ) + SwapWord( 8, pabyRec + nRecordSize + 8 ); + + nRecordSize += 2 * 8; + } + + /* + * Write the Z coordinates (if any). + */ + if( psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_ARCZ + || psObject->nSHPType == SHPT_MULTIPATCH ) + { + ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + + /* + * Write the M values, if any. + */ + if( psObject->bMeasureIsUsed + && (psObject->nSHPType == SHPT_POLYGONM + || psObject->nSHPType == SHPT_ARCM +#ifndef DISABLE_MULTIPATCH_MEASURE + || psObject->nSHPType == SHPT_MULTIPATCH +#endif + || psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_ARCZ) ) + { + ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Extract vertices for a MultiPoint. */ +/* -------------------------------------------------------------------- */ + else if( psObject->nSHPType == SHPT_MULTIPOINT + || psObject->nSHPType == SHPT_MULTIPOINTZ + || psObject->nSHPType == SHPT_MULTIPOINTM ) + { + int32 nPoints; + int i; + + nPoints = psObject->nVertices; + + _SHPSetBounds( pabyRec + 12, psObject ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + ByteCopy( &nPoints, pabyRec + 44, 4 ); + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 ); + ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 ); + if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 ); + } + + nRecordSize = 48 + 16 * psObject->nVertices; + + if( psObject->nSHPType == SHPT_MULTIPOINTZ ) + { + ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + + if( psObject->bMeasureIsUsed + && (psObject->nSHPType == SHPT_MULTIPOINTZ + || psObject->nSHPType == SHPT_MULTIPOINTM) ) + { + ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Write point. */ +/* -------------------------------------------------------------------- */ + else if( psObject->nSHPType == SHPT_POINT + || psObject->nSHPType == SHPT_POINTZ + || psObject->nSHPType == SHPT_POINTM ) + { + ByteCopy( psObject->padfX, pabyRec + 12, 8 ); + ByteCopy( psObject->padfY, pabyRec + 20, 8 ); + + if( bBigEndian ) SwapWord( 8, pabyRec + 12 ); + if( bBigEndian ) SwapWord( 8, pabyRec + 20 ); + + nRecordSize = 28; + + if( psObject->nSHPType == SHPT_POINTZ ) + { + ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + + if( psObject->bMeasureIsUsed + && (psObject->nSHPType == SHPT_POINTZ + || psObject->nSHPType == SHPT_POINTM) ) + { + ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + +/* -------------------------------------------------------------------- */ +/* Not much to do for null geometries. */ +/* -------------------------------------------------------------------- */ + else if( psObject->nSHPType == SHPT_NULL ) + { + nRecordSize = 12; + } + + else + { + /* unknown type */ + assert( FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Establish where we are going to put this record. If we are */ +/* rewriting and existing record, and it will fit, then put it */ +/* back where the original came from. Otherwise write at the end. */ +/* -------------------------------------------------------------------- */ + if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 ) + { + unsigned int nExpectedSize = psSHP->nFileSize + nRecordSize; + if( nExpectedSize < psSHP->nFileSize ) // due to unsigned int overflow + { + char str[128]; + sprintf( str, "Failed to write shape object. " + "File size cannot reach %u + %u.", + psSHP->nFileSize, nRecordSize ); + psSHP->sHooks.Error( str ); + free( pabyRec ); + return -1; + } + + bExtendFile = TRUE; + nRecordOffset = psSHP->nFileSize; + } + else + { + nRecordOffset = psSHP->panRecOffset[nShapeId]; + } + +/* -------------------------------------------------------------------- */ +/* Set the shape type, record number, and record size. */ +/* -------------------------------------------------------------------- */ + i32 = (nShapeId < 0) ? psSHP->nRecords+1 : nShapeId+1; /* record # */ + if( !bBigEndian ) SwapWord( 4, &i32 ); + ByteCopy( &i32, pabyRec, 4 ); + + i32 = (nRecordSize-8)/2; /* record size */ + if( !bBigEndian ) SwapWord( 4, &i32 ); + ByteCopy( &i32, pabyRec + 4, 4 ); + + i32 = psObject->nSHPType; /* shape type */ + if( bBigEndian ) SwapWord( 4, &i32 ); + ByteCopy( &i32, pabyRec + 8, 4 ); + +/* -------------------------------------------------------------------- */ +/* Write out record. */ +/* -------------------------------------------------------------------- */ + if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 ) + { + psSHP->sHooks.Error( "Error in psSHP->sHooks.FSeek() while writing object to .shp file." ); + free( pabyRec ); + return -1; + } + if( psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 ) + { + psSHP->sHooks.Error( "Error in psSHP->sHooks.Fwrite() while writing object to .shp file." ); + free( pabyRec ); + return -1; + } + + free( pabyRec ); + + if( bExtendFile ) + { + if( nShapeId == -1 ) + nShapeId = psSHP->nRecords++; + + psSHP->panRecOffset[nShapeId] = psSHP->nFileSize; + psSHP->nFileSize += nRecordSize; + } + psSHP->panRecSize[nShapeId] = nRecordSize-8; + +/* -------------------------------------------------------------------- */ +/* Expand file wide bounds based on this shape. */ +/* -------------------------------------------------------------------- */ + if( psSHP->adBoundsMin[0] == 0.0 + && psSHP->adBoundsMax[0] == 0.0 + && psSHP->adBoundsMin[1] == 0.0 + && psSHP->adBoundsMax[1] == 0.0 ) + { + if( psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0 ) + { + psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0; + psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0; + psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0; + psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0; + } + else + { + psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0]; + psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0]; + psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ ? psObject->padfZ[0] : 0.0; + psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM ? psObject->padfM[0] : 0.0; + } + } + + for( i = 0; i < psObject->nVertices; i++ ) + { + psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]); + psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]); + psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]); + psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]); + if( psObject->padfZ ) + { + psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]); + psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]); + } + if( psObject->padfM ) + { + psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]); + psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]); + } + } + + return( nShapeId ); +} + +/************************************************************************/ +/* SHPAllocBuffer() */ +/************************************************************************/ + +static void* SHPAllocBuffer(unsigned char** pBuffer, int nSize) +{ + unsigned char* pRet; + + if( pBuffer == NULL ) + return calloc(1, nSize); + + pRet = *pBuffer; + if( pRet == NULL ) + return NULL; + + (*pBuffer) += nSize; + return pRet; +} + +/************************************************************************/ +/* SHPReallocObjectBufIfNecessary() */ +/************************************************************************/ + +static unsigned char* SHPReallocObjectBufIfNecessary ( SHPHandle psSHP, + int nObjectBufSize ) +{ + unsigned char* pBuffer; + if( nObjectBufSize == 0 ) + { + nObjectBufSize = 4 * sizeof(double); + } + if( nObjectBufSize > psSHP->nObjectBufSize ) + { + pBuffer = (unsigned char*) realloc( psSHP->pabyObjectBuf, nObjectBufSize ); + if( pBuffer != NULL ) + { + psSHP->pabyObjectBuf = pBuffer; + psSHP->nObjectBufSize = nObjectBufSize; + } + } + else + pBuffer = psSHP->pabyObjectBuf; + return pBuffer; +} + +/************************************************************************/ +/* SHPReadObject() */ +/* */ +/* Read the vertices, parts, and other non-attribute information */ +/* for one shape. */ +/************************************************************************/ + +SHPObject SHPAPI_CALL1(*) +SHPReadObject( SHPHandle psSHP, int hEntity ) + +{ + int nEntitySize, nRequiredSize; + SHPObject *psShape; + char szErrorMsg[128]; + int nSHPType; + int nBytesRead; + +/* -------------------------------------------------------------------- */ +/* Validate the record/entity number. */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity >= psSHP->nRecords ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Read offset/length from SHX loading if necessary. */ +/* -------------------------------------------------------------------- */ + if( psSHP->panRecOffset[hEntity] == 0 && psSHP->fpSHX != NULL ) + { + int32 nOffset, nLength; + + if( psSHP->sHooks.FSeek( psSHP->fpSHX, 100 + 8 * hEntity, 0 ) != 0 || + psSHP->sHooks.FRead( &nOffset, 1, 4, psSHP->fpSHX ) != 4 || + psSHP->sHooks.FRead( &nLength, 1, 4, psSHP->fpSHX ) != 4 ) + { + char str[128]; + sprintf( str, + "Error in fseek()/fread() reading object from .shx file at offset %d", + 100 + 8 * hEntity); + + psSHP->sHooks.Error( str ); + return NULL; + } + if( !bBigEndian ) SwapWord( 4, &nOffset ); + if( !bBigEndian ) SwapWord( 4, &nLength ); + + psSHP->panRecOffset[hEntity] = nOffset*2; + psSHP->panRecSize[hEntity] = nLength*2; + } + +/* -------------------------------------------------------------------- */ +/* Ensure our record buffer is large enough. */ +/* -------------------------------------------------------------------- */ + nEntitySize = psSHP->panRecSize[hEntity]+8; + if( nEntitySize > psSHP->nBufSize ) + { + psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize); + if (psSHP->pabyRec == NULL) + { + char szError[200]; + + /* Reallocate previous successful size for following features */ + psSHP->pabyRec = (uchar *) malloc(psSHP->nBufSize); + + sprintf( szError, + "Not enough memory to allocate requested memory (nBufSize=%d). " + "Probably broken SHP file", psSHP->nBufSize ); + psSHP->sHooks.Error( szError ); + return NULL; + } + + /* Only set new buffer size after successful alloc */ + psSHP->nBufSize = nEntitySize; + } + + /* In case we were not able to reallocate the buffer on a previous step */ + if (psSHP->pabyRec == NULL) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the record. */ +/* -------------------------------------------------------------------- */ + if( psSHP->sHooks.FSeek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ) != 0 ) + { + /* + * TODO - mloskot: Consider detailed diagnostics of shape file, + * for example to detect if file is truncated. + */ + char str[128]; + sprintf( str, + "Error in fseek() reading object from .shp file at offset %u", + psSHP->panRecOffset[hEntity]); + + psSHP->sHooks.Error( str ); + return NULL; + } + + nBytesRead = psSHP->sHooks.FRead( psSHP->pabyRec, 1, nEntitySize, psSHP->fpSHP ); + + /* Special case for a shapefile whose .shx content length field is not equal */ + /* to the content length field of the .shp, which is a violation of "The */ + /* content length stored in the index record is the same as the value stored in the main */ + /* file record header." (http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf, page 24) */ + /* Actually in that case the .shx content length is equal to the .shp content length + */ + /* 4 (16 bit words), representing the 8 bytes of the record header... */ + if( nBytesRead == nEntitySize - 8 ) + { + /* Do a sanity check */ + int nSHPContentLength; + memcpy( &nSHPContentLength, psSHP->pabyRec + 4, 4 ); + if( !bBigEndian ) SwapWord( 4, &(nSHPContentLength) ); + if( 2 * nSHPContentLength + 8 != nBytesRead ) + { + char str[128]; + sprintf( str, + "Sanity check failed when trying to recover from inconsistent .shx/.shp with shape %d", + hEntity ); + + psSHP->sHooks.Error( str ); + return NULL; + } + } + else if( nBytesRead != nEntitySize ) + { + /* + * TODO - mloskot: Consider detailed diagnostics of shape file, + * for example to detect if file is truncated. + */ + char str[128]; + sprintf( str, + "Error in fread() reading object of size %u at offset %u from .shp file", + nEntitySize, psSHP->panRecOffset[hEntity] ); + + psSHP->sHooks.Error( str ); + return NULL; + } + + if ( 8 + 4 > nEntitySize ) + { + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( szErrorMsg ); + return NULL; + } + memcpy( &nSHPType, psSHP->pabyRec + 8, 4 ); + + if( bBigEndian ) SwapWord( 4, &(nSHPType) ); + +/* -------------------------------------------------------------------- */ +/* Allocate and minimally initialize the object. */ +/* -------------------------------------------------------------------- */ + if( psSHP->bFastModeReadObject ) + { + if( psSHP->psCachedObject->bFastModeReadObject ) + { + psSHP->sHooks.Error( "Invalid read pattern in fast read mode. " + "SHPDestroyObject() should be called." ); + return NULL; + } + + psShape = psSHP->psCachedObject; + memset(psShape, 0, sizeof(SHPObject)); + } + else + psShape = (SHPObject *) calloc(1,sizeof(SHPObject)); + psShape->nShapeId = hEntity; + psShape->nSHPType = nSHPType; + psShape->bMeasureIsUsed = FALSE; + psShape->bFastModeReadObject = psSHP->bFastModeReadObject; + +/* ==================================================================== */ +/* Extract vertices for a Polygon or Arc. */ +/* ==================================================================== */ + if( psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC + || psShape->nSHPType == SHPT_POLYGONZ + || psShape->nSHPType == SHPT_POLYGONM + || psShape->nSHPType == SHPT_ARCZ + || psShape->nSHPType == SHPT_ARCM + || psShape->nSHPType == SHPT_MULTIPATCH ) + { + int32 nPoints, nParts; + int i, nOffset; + unsigned char* pBuffer = NULL; + unsigned char** ppBuffer = NULL; + + if ( 40 + 8 + 4 > nEntitySize ) + { + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( szErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Get the X/Y bounds. */ +/* -------------------------------------------------------------------- */ + memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); + memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); + memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); + memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); + +/* -------------------------------------------------------------------- */ +/* Extract part/point count, and build vertex and part arrays */ +/* to proper size. */ +/* -------------------------------------------------------------------- */ + memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 ); + memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + if( bBigEndian ) SwapWord( 4, &nParts ); + + /* nPoints and nParts are unsigned */ + if (/* nPoints < 0 || nParts < 0 || */ + nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000) + { + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.", + hEntity, nPoints, nParts); + psSHP->sHooks.Error( szErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + /* With the previous checks on nPoints and nParts, */ + /* we should not overflow here and after */ + /* since 50 M * (16 + 8 + 8) = 1 600 MB */ + nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints; + if ( psShape->nSHPType == SHPT_POLYGONZ + || psShape->nSHPType == SHPT_ARCZ + || psShape->nSHPType == SHPT_MULTIPATCH ) + { + nRequiredSize += 16 + 8 * nPoints; + } + if( psShape->nSHPType == SHPT_MULTIPATCH ) + { + nRequiredSize += 4 * nParts; + } + if (nRequiredSize > nEntitySize) + { + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d, nEntitySize=%d.", + hEntity, nPoints, nParts, nEntitySize); + psSHP->sHooks.Error( szErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + if( psShape->bFastModeReadObject ) + { + int nObjectBufSize = 4 * sizeof(double) * nPoints + 2 * sizeof(int) * nParts; + pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize); + ppBuffer = &pBuffer; + } + + psShape->nVertices = nPoints; + psShape->padfX = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfY = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfZ = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfM = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + + psShape->nParts = nParts; + psShape->panPartStart = (int *) SHPAllocBuffer(ppBuffer, nParts * sizeof(int)); + psShape->panPartType = (int *) SHPAllocBuffer(ppBuffer, nParts * sizeof(int)); + + if (psShape->padfX == NULL || + psShape->padfY == NULL || + psShape->padfZ == NULL || + psShape->padfM == NULL || + psShape->panPartStart == NULL || + psShape->panPartType == NULL) + { + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Not enough memory to allocate requested memory (nPoints=%d, nParts=%d) for shape %d. " + "Probably broken SHP file", hEntity, nPoints, nParts ); + psSHP->sHooks.Error( szErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + for( i = 0; (int32)i < nParts; i++ ) + psShape->panPartType[i] = SHPP_RING; + +/* -------------------------------------------------------------------- */ +/* Copy out the part array from the record. */ +/* -------------------------------------------------------------------- */ + memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts ); + for( i = 0; (int32)i < nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i ); + + /* We check that the offset is inside the vertex array */ + if (psShape->panPartStart[i] < 0 + || (psShape->panPartStart[i] >= psShape->nVertices + && psShape->nVertices > 0) ) + { + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d", + hEntity, i, psShape->panPartStart[i], psShape->nVertices); + psSHP->sHooks.Error( szErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + if (i > 0 && psShape->panPartStart[i] <= psShape->panPartStart[i-1]) + { + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : panPartStart[%d] = %d, panPartStart[%d] = %d", + hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]); + psSHP->sHooks.Error( szErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + } + + nOffset = 44 + 8 + 4*nParts; + +/* -------------------------------------------------------------------- */ +/* If this is a multipatch, we will also have parts types. */ +/* -------------------------------------------------------------------- */ + if( psShape->nSHPType == SHPT_MULTIPATCH ) + { + memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts ); + for( i = 0; (int32)i < nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, psShape->panPartType+i ); + } + + nOffset += 4*nParts; + } + +/* -------------------------------------------------------------------- */ +/* Copy out the vertices from the record. */ +/* -------------------------------------------------------------------- */ + for( i = 0; (int32)i < nPoints; i++ ) + { + memcpy(psShape->padfX + i, + psSHP->pabyRec + nOffset + i * 16, + 8 ); + + memcpy(psShape->padfY + i, + psSHP->pabyRec + nOffset + i * 16 + 8, + 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); + if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); + } + + nOffset += 16*nPoints; + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( psShape->nSHPType == SHPT_POLYGONZ + || psShape->nSHPType == SHPT_ARCZ + || psShape->nSHPType == SHPT_MULTIPATCH ) + { + memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); + + for( i = 0; (int32)i < nPoints; i++ ) + { + memcpy( psShape->padfZ + i, + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); + } + + nOffset += 16 + 8*nPoints; + } + else if( psShape->bFastModeReadObject ) + { + psShape->padfZ = NULL; + } + +/* -------------------------------------------------------------------- */ +/* If we have a M measure value, then read it now. We assume */ +/* that the measure can be present for any shape if the size is */ +/* big enough, but really it will only occur for the Z shapes */ +/* (options), and the M shapes. */ +/* -------------------------------------------------------------------- */ + if( nEntitySize >= (int)(nOffset + 16 + 8*nPoints) ) + { + memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); + + for( i = 0; (int32)i < nPoints; i++ ) + { + memcpy( psShape->padfM + i, + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); + } + psShape->bMeasureIsUsed = TRUE; + } + else if( psShape->bFastModeReadObject ) + { + psShape->padfM = NULL; + } + } + +/* ==================================================================== */ +/* Extract vertices for a MultiPoint. */ +/* ==================================================================== */ + else if( psShape->nSHPType == SHPT_MULTIPOINT + || psShape->nSHPType == SHPT_MULTIPOINTM + || psShape->nSHPType == SHPT_MULTIPOINTZ ) + { + int32 nPoints; + int i, nOffset; + unsigned char* pBuffer = NULL; + unsigned char** ppBuffer = NULL; + + if ( 44 + 4 > nEntitySize ) + { + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( szErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + memcpy( &nPoints, psSHP->pabyRec + 44, 4 ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + + /* nPoints is unsigned */ + if (/* nPoints < 0 || */ nPoints > 50 * 1000 * 1000) + { + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : nPoints = %d", + hEntity, nPoints); + psSHP->sHooks.Error( szErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + nRequiredSize = 48 + nPoints * 16; + if( psShape->nSHPType == SHPT_MULTIPOINTZ ) + { + nRequiredSize += 16 + nPoints * 8; + } + if (nRequiredSize > nEntitySize) + { + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d", + hEntity, nPoints, nEntitySize); + psSHP->sHooks.Error( szErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + if( psShape->bFastModeReadObject ) + { + int nObjectBufSize = 4 * sizeof(double) * nPoints; + pBuffer = SHPReallocObjectBufIfNecessary(psSHP, nObjectBufSize); + ppBuffer = &pBuffer; + } + + psShape->nVertices = nPoints; + + psShape->padfX = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfY = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfZ = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + psShape->padfM = (double *) SHPAllocBuffer(ppBuffer, sizeof(double) * nPoints); + + if (psShape->padfX == NULL || + psShape->padfY == NULL || + psShape->padfZ == NULL || + psShape->padfM == NULL) + { + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Not enough memory to allocate requested memory (nPoints=%d) for shape %d. " + "Probably broken SHP file", hEntity, nPoints ); + psSHP->sHooks.Error( szErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + for( i = 0; (int32)i < nPoints; i++ ) + { + memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 ); + memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); + if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); + } + + nOffset = 48 + 16*nPoints; + +/* -------------------------------------------------------------------- */ +/* Get the X/Y bounds. */ +/* -------------------------------------------------------------------- */ + memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); + memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); + memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); + memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( psShape->nSHPType == SHPT_MULTIPOINTZ ) + { + memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); + + for( i = 0; (int32)i < nPoints; i++ ) + { + memcpy( psShape->padfZ + i, + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); + } + + nOffset += 16 + 8*nPoints; + } + else if( psShape->bFastModeReadObject ) + psShape->padfZ = NULL; + +/* -------------------------------------------------------------------- */ +/* If we have a M measure value, then read it now. We assume */ +/* that the measure can be present for any shape if the size is */ +/* big enough, but really it will only occur for the Z shapes */ +/* (options), and the M shapes. */ +/* -------------------------------------------------------------------- */ + if( nEntitySize >= (int)(nOffset + 16 + 8*nPoints) ) + { + memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); + + for( i = 0; (int32)i < nPoints; i++ ) + { + memcpy( psShape->padfM + i, + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); + } + psShape->bMeasureIsUsed = TRUE; + } + else if( psShape->bFastModeReadObject ) + psShape->padfM = NULL; + } + +/* ==================================================================== */ +/* Extract vertices for a point. */ +/* ==================================================================== */ + else if( psShape->nSHPType == SHPT_POINT + || psShape->nSHPType == SHPT_POINTM + || psShape->nSHPType == SHPT_POINTZ ) + { + int nOffset; + + psShape->nVertices = 1; + if( psShape->bFastModeReadObject ) + { + psShape->padfX = &(psShape->dfXMin); + psShape->padfY = &(psShape->dfYMin); + psShape->padfZ = &(psShape->dfZMin); + psShape->padfM = &(psShape->dfMMin); + psShape->padfZ[0] = 0.0; + psShape->padfM[0] = 0.0; + } + else + { + psShape->padfX = (double *) calloc(1,sizeof(double)); + psShape->padfY = (double *) calloc(1,sizeof(double)); + psShape->padfZ = (double *) calloc(1,sizeof(double)); + psShape->padfM = (double *) calloc(1,sizeof(double)); + } + + if (20 + 8 + (( psShape->nSHPType == SHPT_POINTZ ) ? 8 : 0)> nEntitySize) + { + snprintf(szErrorMsg, sizeof(szErrorMsg), + "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( szErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 ); + memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfX ); + if( bBigEndian ) SwapWord( 8, psShape->padfY ); + + nOffset = 20 + 8; + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( psShape->nSHPType == SHPT_POINTZ ) + { + memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfZ ); + + nOffset += 8; + } + +/* -------------------------------------------------------------------- */ +/* If we have a M measure value, then read it now. We assume */ +/* that the measure can be present for any shape if the size is */ +/* big enough, but really it will only occur for the Z shapes */ +/* (options), and the M shapes. */ +/* -------------------------------------------------------------------- */ + if( nEntitySize >= nOffset + 8 ) + { + memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfM ); + psShape->bMeasureIsUsed = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Since no extents are supplied in the record, we will apply */ +/* them from the single vertex. */ +/* -------------------------------------------------------------------- */ + psShape->dfXMin = psShape->dfXMax = psShape->padfX[0]; + psShape->dfYMin = psShape->dfYMax = psShape->padfY[0]; + psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0]; + psShape->dfMMin = psShape->dfMMax = psShape->padfM[0]; + } + + return( psShape ); +} + +/************************************************************************/ +/* SHPTypeName() */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +SHPTypeName( int nSHPType ) + +{ + switch( nSHPType ) + { + case SHPT_NULL: + return "NullShape"; + + case SHPT_POINT: + return "Point"; + + case SHPT_ARC: + return "Arc"; + + case SHPT_POLYGON: + return "Polygon"; + + case SHPT_MULTIPOINT: + return "MultiPoint"; + + case SHPT_POINTZ: + return "PointZ"; + + case SHPT_ARCZ: + return "ArcZ"; + + case SHPT_POLYGONZ: + return "PolygonZ"; + + case SHPT_MULTIPOINTZ: + return "MultiPointZ"; + + case SHPT_POINTM: + return "PointM"; + + case SHPT_ARCM: + return "ArcM"; + + case SHPT_POLYGONM: + return "PolygonM"; + + case SHPT_MULTIPOINTM: + return "MultiPointM"; + + case SHPT_MULTIPATCH: + return "MultiPatch"; + + default: + return "UnknownShapeType"; + } +} + +/************************************************************************/ +/* SHPPartTypeName() */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +SHPPartTypeName( int nPartType ) + +{ + switch( nPartType ) + { + case SHPP_TRISTRIP: + return "TriangleStrip"; + + case SHPP_TRIFAN: + return "TriangleFan"; + + case SHPP_OUTERRING: + return "OuterRing"; + + case SHPP_INNERRING: + return "InnerRing"; + + case SHPP_FIRSTRING: + return "FirstRing"; + + case SHPP_RING: + return "Ring"; + + default: + return "UnknownPartType"; + } +} + +/************************************************************************/ +/* SHPDestroyObject() */ +/************************************************************************/ + +void SHPAPI_CALL +SHPDestroyObject( SHPObject * psShape ) + +{ + if( psShape == NULL ) + return; + + if( psShape->bFastModeReadObject ) + { + psShape->bFastModeReadObject = FALSE; + return; + } + + if( psShape->padfX != NULL ) + free( psShape->padfX ); + if( psShape->padfY != NULL ) + free( psShape->padfY ); + if( psShape->padfZ != NULL ) + free( psShape->padfZ ); + if( psShape->padfM != NULL ) + free( psShape->padfM ); + + if( psShape->panPartStart != NULL ) + free( psShape->panPartStart ); + if( psShape->panPartType != NULL ) + free( psShape->panPartType ); + + free( psShape ); +} + +/************************************************************************/ +/* SHPRewindObject() */ +/* */ +/* Reset the winding of polygon objects to adhere to the */ +/* specification. */ +/************************************************************************/ + +int SHPAPI_CALL +SHPRewindObject( CPL_UNUSED SHPHandle hSHP, + SHPObject * psObject ) +{ + int iOpRing, bAltered = 0; + +/* -------------------------------------------------------------------- */ +/* Do nothing if this is not a polygon object. */ +/* -------------------------------------------------------------------- */ + if( psObject->nSHPType != SHPT_POLYGON + && psObject->nSHPType != SHPT_POLYGONZ + && psObject->nSHPType != SHPT_POLYGONM ) + return 0; + + if( psObject->nVertices == 0 || psObject->nParts == 0 ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Process each of the rings. */ +/* -------------------------------------------------------------------- */ + for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ ) + { + int bInner, iVert, nVertCount, nVertStart, iCheckRing; + double dfSum, dfTestX, dfTestY; + +/* -------------------------------------------------------------------- */ +/* Determine if this ring is an inner ring or an outer ring */ +/* relative to all the other rings. For now we assume the */ +/* first ring is outer and all others are inner, but eventually */ +/* we need to fix this to handle multiple island polygons and */ +/* unordered sets of rings. */ +/* */ +/* -------------------------------------------------------------------- */ + + /* Use point in the middle of segment to avoid testing + * common points of rings. + */ + dfTestX = ( psObject->padfX[psObject->panPartStart[iOpRing]] + + psObject->padfX[psObject->panPartStart[iOpRing] + 1] ) / 2; + dfTestY = ( psObject->padfY[psObject->panPartStart[iOpRing]] + + psObject->padfY[psObject->panPartStart[iOpRing] + 1] ) / 2; + + bInner = FALSE; + for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ ) + { + int iEdge; + + if( iCheckRing == iOpRing ) + continue; + + nVertStart = psObject->panPartStart[iCheckRing]; + + if( iCheckRing == psObject->nParts-1 ) + nVertCount = psObject->nVertices + - psObject->panPartStart[iCheckRing]; + else + nVertCount = psObject->panPartStart[iCheckRing+1] + - psObject->panPartStart[iCheckRing]; + + for( iEdge = 0; iEdge < nVertCount; iEdge++ ) + { + int iNext; + + if( iEdge < nVertCount-1 ) + iNext = iEdge+1; + else + iNext = 0; + + /* Rule #1: + * Test whether the edge 'straddles' the horizontal ray from the test point (dfTestY,dfTestY) + * The rule #1 also excludes edges collinear with the ray. + */ + if ( ( psObject->padfY[iEdge+nVertStart] < dfTestY + && dfTestY <= psObject->padfY[iNext+nVertStart] ) + || ( psObject->padfY[iNext+nVertStart] < dfTestY + && dfTestY <= psObject->padfY[iEdge+nVertStart] ) ) + { + /* Rule #2: + * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY) + */ + double const intersect = + ( psObject->padfX[iEdge+nVertStart] + + ( dfTestY - psObject->padfY[iEdge+nVertStart] ) + / ( psObject->padfY[iNext+nVertStart] - psObject->padfY[iEdge+nVertStart] ) + * ( psObject->padfX[iNext+nVertStart] - psObject->padfX[iEdge+nVertStart] ) ); + + if (intersect < dfTestX) + { + bInner = !bInner; + } + } + } + } /* for iCheckRing */ + +/* -------------------------------------------------------------------- */ +/* Determine the current order of this ring so we will know if */ +/* it has to be reversed. */ +/* -------------------------------------------------------------------- */ + nVertStart = psObject->panPartStart[iOpRing]; + + if( iOpRing == psObject->nParts-1 ) + nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing]; + else + nVertCount = psObject->panPartStart[iOpRing+1] + - psObject->panPartStart[iOpRing]; + + if (nVertCount < 2) + continue; + + dfSum = psObject->padfX[nVertStart] * (psObject->padfY[nVertStart+1] - psObject->padfY[nVertStart+nVertCount-1]); + for( iVert = nVertStart + 1; iVert < nVertStart+nVertCount-1; iVert++ ) + { + dfSum += psObject->padfX[iVert] * (psObject->padfY[iVert+1] - psObject->padfY[iVert-1]); + } + + dfSum += psObject->padfX[iVert] * (psObject->padfY[nVertStart] - psObject->padfY[iVert-1]); + +/* -------------------------------------------------------------------- */ +/* Reverse if necessary. */ +/* -------------------------------------------------------------------- */ + if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) ) + { + int i; + + bAltered++; + for( i = 0; i < nVertCount/2; i++ ) + { + double dfSaved; + + /* Swap X */ + dfSaved = psObject->padfX[nVertStart+i]; + psObject->padfX[nVertStart+i] = + psObject->padfX[nVertStart+nVertCount-i-1]; + psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved; + + /* Swap Y */ + dfSaved = psObject->padfY[nVertStart+i]; + psObject->padfY[nVertStart+i] = + psObject->padfY[nVertStart+nVertCount-i-1]; + psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved; + + /* Swap Z */ + if( psObject->padfZ ) + { + dfSaved = psObject->padfZ[nVertStart+i]; + psObject->padfZ[nVertStart+i] = + psObject->padfZ[nVertStart+nVertCount-i-1]; + psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved; + } + + /* Swap M */ + if( psObject->padfM ) + { + dfSaved = psObject->padfM[nVertStart+i]; + psObject->padfM[nVertStart+i] = + psObject->padfM[nVertStart+nVertCount-i-1]; + psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved; + } + } + } + } + + return bAltered; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shptree.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shptree.c new file mode 100644 index 000000000..ab98c6114 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/shape/shptree.c @@ -0,0 +1,1241 @@ +/****************************************************************************** + * $Id: shptree.c,v 1.17 2012-01-27 21:09:26 fwarmerdam Exp $ + * + * Project: Shapelib + * Purpose: Implementation of quadtree building and searching functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2012, Even Rouault + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log: shptree.c,v $ + * Revision 1.17 2012-01-27 21:09:26 fwarmerdam + * optimize .qix output (gdal #4472) + * + * Revision 1.16 2011-12-11 22:26:46 fwarmerdam + * upgrade .qix access code to use SAHooks (gdal #3365) + * + * Revision 1.15 2011-07-24 05:59:25 fwarmerdam + * minimize use of CPLError in favor of SAHooks.Error() + * + * Revision 1.14 2010-08-27 23:43:27 fwarmerdam + * add SHPAPI_CALL attribute in code + * + * Revision 1.13 2010-06-29 05:50:15 fwarmerdam + * fix sign of Z/M comparisons in SHPCheckObjectContained (#2223) + * + * Revision 1.12 2008-11-12 15:39:50 fwarmerdam + * improve safety in face of buggy .shp file. + * + * Revision 1.11 2007/10/27 03:31:14 fwarmerdam + * limit default depth of tree to 12 levels (gdal ticket #1594) + * + * Revision 1.10 2005/01/03 22:30:13 fwarmerdam + * added support for saved quadtrees + * + * Revision 1.9 2003/01/28 15:53:41 warmerda + * Avoid build warnings. + * + * Revision 1.8 2002/05/07 13:07:45 warmerda + * use qsort() - patch from Bernhard Herzog + * + * Revision 1.7 2002/01/15 14:36:07 warmerda + * updated email address + * + * Revision 1.6 2001/05/23 13:36:52 warmerda + * added use of SHPAPI_CALL + * + * Revision 1.5 1999/11/05 14:12:05 warmerda + * updated license terms + * + * Revision 1.4 1999/06/02 18:24:21 warmerda + * added trimming code + * + * Revision 1.3 1999/06/02 17:56:12 warmerda + * added quad'' subnode support for trees + * + * Revision 1.2 1999/05/18 19:11:11 warmerda + * Added example searching capability + * + * Revision 1.1 1999/05/18 17:49:20 warmerda + * New + * + */ + +#include "shapefil.h" + +#include +#include +#include +#include + +#ifdef USE_CPL +#include "cpl_error.h" +#endif + +SHP_CVSID("$Id: shptree.c,v 1.17 2012-01-27 21:09:26 fwarmerdam Exp $") + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +static int bBigEndian = 0; + + +/* -------------------------------------------------------------------- */ +/* If the following is 0.5, nodes will be split in half. If it */ +/* is 0.6 then each subnode will contain 60% of the parent */ +/* node, with 20% representing overlap. This can be help to */ +/* prevent small objects on a boundary from shifting too high */ +/* up the tree. */ +/* -------------------------------------------------------------------- */ + +#define SHP_SPLIT_RATIO 0.55 + +/************************************************************************/ +/* SfRealloc() */ +/* */ +/* A realloc cover function that will access a NULL pointer as */ +/* a valid input. */ +/************************************************************************/ + +static void * SfRealloc( void * pMem, int nNewSize ) + +{ + if( pMem == NULL ) + return( (void *) malloc(nNewSize) ); + else + return( (void *) realloc(pMem,nNewSize) ); +} + +/************************************************************************/ +/* SHPTreeNodeInit() */ +/* */ +/* Initialize a tree node. */ +/************************************************************************/ + +static SHPTreeNode *SHPTreeNodeCreate( double * padfBoundsMin, + double * padfBoundsMax ) + +{ + SHPTreeNode *psTreeNode; + + psTreeNode = (SHPTreeNode *) malloc(sizeof(SHPTreeNode)); + if( NULL == psTreeNode ) + return NULL; + + psTreeNode->nShapeCount = 0; + psTreeNode->panShapeIds = NULL; + psTreeNode->papsShapeObj = NULL; + + psTreeNode->nSubNodes = 0; + + if( padfBoundsMin != NULL ) + memcpy( psTreeNode->adfBoundsMin, padfBoundsMin, sizeof(double) * 4 ); + + if( padfBoundsMax != NULL ) + memcpy( psTreeNode->adfBoundsMax, padfBoundsMax, sizeof(double) * 4 ); + + return psTreeNode; +} + + +/************************************************************************/ +/* SHPCreateTree() */ +/************************************************************************/ + +SHPTree SHPAPI_CALL1(*) + SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, + double *padfBoundsMin, double *padfBoundsMax ) + +{ + SHPTree *psTree; + + if( padfBoundsMin == NULL && hSHP == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Allocate the tree object */ +/* -------------------------------------------------------------------- */ + psTree = (SHPTree *) malloc(sizeof(SHPTree)); + if( NULL == psTree ) + { + return NULL; + } + + psTree->hSHP = hSHP; + psTree->nMaxDepth = nMaxDepth; + psTree->nDimension = nDimension; + psTree->nTotalCount = 0; + +/* -------------------------------------------------------------------- */ +/* If no max depth was defined, try to select a reasonable one */ +/* that implies approximately 8 shapes per node. */ +/* -------------------------------------------------------------------- */ + if( psTree->nMaxDepth == 0 && hSHP != NULL ) + { + int nMaxNodeCount = 1; + int nShapeCount; + + SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL ); + while( nMaxNodeCount*4 < nShapeCount ) + { + psTree->nMaxDepth += 1; + nMaxNodeCount = nMaxNodeCount * 2; + } + +#ifdef USE_CPL + CPLDebug( "Shape", + "Estimated spatial index tree depth: %d", + psTree->nMaxDepth ); +#endif + + /* NOTE: Due to problems with memory allocation for deep trees, + * automatically estimated depth is limited up to 12 levels. + * See Ticket #1594 for detailed discussion. + */ + if( psTree->nMaxDepth > MAX_DEFAULT_TREE_DEPTH ) + { + psTree->nMaxDepth = MAX_DEFAULT_TREE_DEPTH; + +#ifdef USE_CPL + CPLDebug( "Shape", + "Falling back to max number of allowed index tree levels (%d).", + MAX_DEFAULT_TREE_DEPTH ); +#endif + } + } + +/* -------------------------------------------------------------------- */ +/* Allocate the root node. */ +/* -------------------------------------------------------------------- */ + psTree->psRoot = SHPTreeNodeCreate( padfBoundsMin, padfBoundsMax ); + if( NULL == psTree->psRoot ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Assign the bounds to the root node. If none are passed in, */ +/* use the bounds of the provided file otherwise the create */ +/* function will have already set the bounds. */ +/* -------------------------------------------------------------------- */ + assert( NULL != psTree ); + assert( NULL != psTree->psRoot ); + + if( padfBoundsMin == NULL ) + { + SHPGetInfo( hSHP, NULL, NULL, + psTree->psRoot->adfBoundsMin, + psTree->psRoot->adfBoundsMax ); + } + +/* -------------------------------------------------------------------- */ +/* If we have a file, insert all it's shapes into the tree. */ +/* -------------------------------------------------------------------- */ + if( hSHP != NULL ) + { + int iShape, nShapeCount; + + SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL ); + + for( iShape = 0; iShape < nShapeCount; iShape++ ) + { + SHPObject *psShape; + + psShape = SHPReadObject( hSHP, iShape ); + if( psShape != NULL ) + { + SHPTreeAddShapeId( psTree, psShape ); + SHPDestroyObject( psShape ); + } + } + } + + return psTree; +} + +/************************************************************************/ +/* SHPDestroyTreeNode() */ +/************************************************************************/ + +static void SHPDestroyTreeNode( SHPTreeNode * psTreeNode ) + +{ + int i; + + assert( NULL != psTreeNode ); + + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( psTreeNode->apsSubNode[i] != NULL ) + SHPDestroyTreeNode( psTreeNode->apsSubNode[i] ); + } + + if( psTreeNode->panShapeIds != NULL ) + free( psTreeNode->panShapeIds ); + + if( psTreeNode->papsShapeObj != NULL ) + { + for( i = 0; i < psTreeNode->nShapeCount; i++ ) + { + if( psTreeNode->papsShapeObj[i] != NULL ) + SHPDestroyObject( psTreeNode->papsShapeObj[i] ); + } + + free( psTreeNode->papsShapeObj ); + } + + free( psTreeNode ); +} + +/************************************************************************/ +/* SHPDestroyTree() */ +/************************************************************************/ + +void SHPAPI_CALL +SHPDestroyTree( SHPTree * psTree ) + +{ + SHPDestroyTreeNode( psTree->psRoot ); + free( psTree ); +} + +/************************************************************************/ +/* SHPCheckBoundsOverlap() */ +/* */ +/* Do the given boxes overlap at all? */ +/************************************************************************/ + +int SHPAPI_CALL +SHPCheckBoundsOverlap( double * padfBox1Min, double * padfBox1Max, + double * padfBox2Min, double * padfBox2Max, + int nDimension ) + +{ + int iDim; + + for( iDim = 0; iDim < nDimension; iDim++ ) + { + if( padfBox2Max[iDim] < padfBox1Min[iDim] ) + return FALSE; + + if( padfBox1Max[iDim] < padfBox2Min[iDim] ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* SHPCheckObjectContained() */ +/* */ +/* Does the given shape fit within the indicated extents? */ +/************************************************************************/ + +static int SHPCheckObjectContained( SHPObject * psObject, int nDimension, + double * padfBoundsMin, double * padfBoundsMax ) + +{ + if( psObject->dfXMin < padfBoundsMin[0] + || psObject->dfXMax > padfBoundsMax[0] ) + return FALSE; + + if( psObject->dfYMin < padfBoundsMin[1] + || psObject->dfYMax > padfBoundsMax[1] ) + return FALSE; + + if( nDimension == 2 ) + return TRUE; + + if( psObject->dfZMin < padfBoundsMin[2] + || psObject->dfZMax > padfBoundsMax[2] ) + return FALSE; + + if( nDimension == 3 ) + return TRUE; + + if( psObject->dfMMin < padfBoundsMin[3] + || psObject->dfMMax > padfBoundsMax[3] ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* SHPTreeSplitBounds() */ +/* */ +/* Split a region into two subregion evenly, cutting along the */ +/* longest dimension. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPTreeSplitBounds( double *padfBoundsMinIn, double *padfBoundsMaxIn, + double *padfBoundsMin1, double * padfBoundsMax1, + double *padfBoundsMin2, double * padfBoundsMax2 ) + +{ +/* -------------------------------------------------------------------- */ +/* The output bounds will be very similar to the input bounds, */ +/* so just copy over to start. */ +/* -------------------------------------------------------------------- */ + memcpy( padfBoundsMin1, padfBoundsMinIn, sizeof(double) * 4 ); + memcpy( padfBoundsMax1, padfBoundsMaxIn, sizeof(double) * 4 ); + memcpy( padfBoundsMin2, padfBoundsMinIn, sizeof(double) * 4 ); + memcpy( padfBoundsMax2, padfBoundsMaxIn, sizeof(double) * 4 ); + +/* -------------------------------------------------------------------- */ +/* Split in X direction. */ +/* -------------------------------------------------------------------- */ + if( (padfBoundsMaxIn[0] - padfBoundsMinIn[0]) + > (padfBoundsMaxIn[1] - padfBoundsMinIn[1]) ) + { + double dfRange = padfBoundsMaxIn[0] - padfBoundsMinIn[0]; + + padfBoundsMax1[0] = padfBoundsMinIn[0] + dfRange * SHP_SPLIT_RATIO; + padfBoundsMin2[0] = padfBoundsMaxIn[0] - dfRange * SHP_SPLIT_RATIO; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise split in Y direction. */ +/* -------------------------------------------------------------------- */ + else + { + double dfRange = padfBoundsMaxIn[1] - padfBoundsMinIn[1]; + + padfBoundsMax1[1] = padfBoundsMinIn[1] + dfRange * SHP_SPLIT_RATIO; + padfBoundsMin2[1] = padfBoundsMaxIn[1] - dfRange * SHP_SPLIT_RATIO; + } +} + +/************************************************************************/ +/* SHPTreeNodeAddShapeId() */ +/************************************************************************/ + +static int +SHPTreeNodeAddShapeId( SHPTreeNode * psTreeNode, SHPObject * psObject, + int nMaxDepth, int nDimension ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* If there are subnodes, then consider wiether this object */ +/* will fit in them. */ +/* -------------------------------------------------------------------- */ + if( nMaxDepth > 1 && psTreeNode->nSubNodes > 0 ) + { + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( SHPCheckObjectContained(psObject, nDimension, + psTreeNode->apsSubNode[i]->adfBoundsMin, + psTreeNode->apsSubNode[i]->adfBoundsMax)) + { + return SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[i], + psObject, nMaxDepth-1, + nDimension ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise, consider creating four subnodes if could fit into */ +/* them, and adding to the appropriate subnode. */ +/* -------------------------------------------------------------------- */ +#if MAX_SUBNODE == 4 + else if( nMaxDepth > 1 && psTreeNode->nSubNodes == 0 ) + { + double adfBoundsMinH1[4], adfBoundsMaxH1[4]; + double adfBoundsMinH2[4], adfBoundsMaxH2[4]; + double adfBoundsMin1[4], adfBoundsMax1[4]; + double adfBoundsMin2[4], adfBoundsMax2[4]; + double adfBoundsMin3[4], adfBoundsMax3[4]; + double adfBoundsMin4[4], adfBoundsMax4[4]; + + SHPTreeSplitBounds( psTreeNode->adfBoundsMin, + psTreeNode->adfBoundsMax, + adfBoundsMinH1, adfBoundsMaxH1, + adfBoundsMinH2, adfBoundsMaxH2 ); + + SHPTreeSplitBounds( adfBoundsMinH1, adfBoundsMaxH1, + adfBoundsMin1, adfBoundsMax1, + adfBoundsMin2, adfBoundsMax2 ); + + SHPTreeSplitBounds( adfBoundsMinH2, adfBoundsMaxH2, + adfBoundsMin3, adfBoundsMax3, + adfBoundsMin4, adfBoundsMax4 ); + + if( SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin1, adfBoundsMax1) + || SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin2, adfBoundsMax2) + || SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin3, adfBoundsMax3) + || SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin4, adfBoundsMax4) ) + { + psTreeNode->nSubNodes = 4; + psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1, + adfBoundsMax1 ); + psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2, + adfBoundsMax2 ); + psTreeNode->apsSubNode[2] = SHPTreeNodeCreate( adfBoundsMin3, + adfBoundsMax3 ); + psTreeNode->apsSubNode[3] = SHPTreeNodeCreate( adfBoundsMin4, + adfBoundsMax4 ); + + /* recurse back on this node now that it has subnodes */ + return( SHPTreeNodeAddShapeId( psTreeNode, psObject, + nMaxDepth, nDimension ) ); + } + } +#endif /* MAX_SUBNODE == 4 */ + +/* -------------------------------------------------------------------- */ +/* Otherwise, consider creating two subnodes if could fit into */ +/* them, and adding to the appropriate subnode. */ +/* -------------------------------------------------------------------- */ +#if MAX_SUBNODE == 2 + else if( nMaxDepth > 1 && psTreeNode->nSubNodes == 0 ) + { + double adfBoundsMin1[4], adfBoundsMax1[4]; + double adfBoundsMin2[4], adfBoundsMax2[4]; + + SHPTreeSplitBounds( psTreeNode->adfBoundsMin, psTreeNode->adfBoundsMax, + adfBoundsMin1, adfBoundsMax1, + adfBoundsMin2, adfBoundsMax2 ); + + if( SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin1, adfBoundsMax1)) + { + psTreeNode->nSubNodes = 2; + psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1, + adfBoundsMax1 ); + psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2, + adfBoundsMax2 ); + + return( SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[0], psObject, + nMaxDepth - 1, nDimension ) ); + } + else if( SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin2, adfBoundsMax2) ) + { + psTreeNode->nSubNodes = 2; + psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1, + adfBoundsMax1 ); + psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2, + adfBoundsMax2 ); + + return( SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[1], psObject, + nMaxDepth - 1, nDimension ) ); + } + } +#endif /* MAX_SUBNODE == 2 */ + +/* -------------------------------------------------------------------- */ +/* If none of that worked, just add it to this nodes list. */ +/* -------------------------------------------------------------------- */ + psTreeNode->nShapeCount++; + + psTreeNode->panShapeIds = (int *) + SfRealloc( psTreeNode->panShapeIds, + sizeof(int) * psTreeNode->nShapeCount ); + psTreeNode->panShapeIds[psTreeNode->nShapeCount-1] = psObject->nShapeId; + + if( psTreeNode->papsShapeObj != NULL ) + { + psTreeNode->papsShapeObj = (SHPObject **) + SfRealloc( psTreeNode->papsShapeObj, + sizeof(void *) * psTreeNode->nShapeCount ); + psTreeNode->papsShapeObj[psTreeNode->nShapeCount-1] = NULL; + } + + return TRUE; +} + +/************************************************************************/ +/* SHPTreeAddShapeId() */ +/* */ +/* Add a shape to the tree, but don't keep a pointer to the */ +/* object data, just keep the shapeid. */ +/************************************************************************/ + +int SHPAPI_CALL +SHPTreeAddShapeId( SHPTree * psTree, SHPObject * psObject ) + +{ + psTree->nTotalCount++; + + return( SHPTreeNodeAddShapeId( psTree->psRoot, psObject, + psTree->nMaxDepth, psTree->nDimension ) ); +} + +/************************************************************************/ +/* SHPTreeCollectShapesIds() */ +/* */ +/* Work function implementing SHPTreeFindLikelyShapes() on a */ +/* tree node by tree node basis. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPTreeCollectShapeIds( SHPTree *hTree, SHPTreeNode * psTreeNode, + double * padfBoundsMin, double * padfBoundsMax, + int * pnShapeCount, int * pnMaxShapes, + int ** ppanShapeList ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Does this node overlap the area of interest at all? If not, */ +/* return without adding to the list at all. */ +/* -------------------------------------------------------------------- */ + if( !SHPCheckBoundsOverlap( psTreeNode->adfBoundsMin, + psTreeNode->adfBoundsMax, + padfBoundsMin, + padfBoundsMax, + hTree->nDimension ) ) + return; + +/* -------------------------------------------------------------------- */ +/* Grow the list to hold the shapes on this node. */ +/* -------------------------------------------------------------------- */ + if( *pnShapeCount + psTreeNode->nShapeCount > *pnMaxShapes ) + { + *pnMaxShapes = (*pnShapeCount + psTreeNode->nShapeCount) * 2 + 20; + *ppanShapeList = (int *) + SfRealloc(*ppanShapeList,sizeof(int) * *pnMaxShapes); + } + +/* -------------------------------------------------------------------- */ +/* Add the local nodes shapeids to the list. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nShapeCount; i++ ) + { + (*ppanShapeList)[(*pnShapeCount)++] = psTreeNode->panShapeIds[i]; + } + +/* -------------------------------------------------------------------- */ +/* Recurse to subnodes if they exist. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( psTreeNode->apsSubNode[i] != NULL ) + SHPTreeCollectShapeIds( hTree, psTreeNode->apsSubNode[i], + padfBoundsMin, padfBoundsMax, + pnShapeCount, pnMaxShapes, + ppanShapeList ); + } +} + +/************************************************************************/ +/* SHPTreeFindLikelyShapes() */ +/* */ +/* Find all shapes within tree nodes for which the tree node */ +/* bounding box overlaps the search box. The return value is */ +/* an array of shapeids terminated by a -1. The shapeids will */ +/* be in order, as hopefully this will result in faster (more */ +/* sequential) reading from the file. */ +/************************************************************************/ + +/* helper for qsort */ +static int +compare_ints( const void * a, const void * b) +{ + return (*(int*)a) - (*(int*)b); +} + +int SHPAPI_CALL1(*) +SHPTreeFindLikelyShapes( SHPTree * hTree, + double * padfBoundsMin, double * padfBoundsMax, + int * pnShapeCount ) + +{ + int *panShapeList=NULL, nMaxShapes = 0; + +/* -------------------------------------------------------------------- */ +/* Perform the search by recursive descent. */ +/* -------------------------------------------------------------------- */ + *pnShapeCount = 0; + + SHPTreeCollectShapeIds( hTree, hTree->psRoot, + padfBoundsMin, padfBoundsMax, + pnShapeCount, &nMaxShapes, + &panShapeList ); + +/* -------------------------------------------------------------------- */ +/* Sort the id array */ +/* -------------------------------------------------------------------- */ + + qsort(panShapeList, *pnShapeCount, sizeof(int), compare_ints); + + return panShapeList; +} + +/************************************************************************/ +/* SHPTreeNodeTrim() */ +/* */ +/* This is the recurve version of SHPTreeTrimExtraNodes() that */ +/* walks the tree cleaning it up. */ +/************************************************************************/ + +static int SHPTreeNodeTrim( SHPTreeNode * psTreeNode ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Trim subtrees, and free subnodes that come back empty. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( SHPTreeNodeTrim( psTreeNode->apsSubNode[i] ) ) + { + SHPDestroyTreeNode( psTreeNode->apsSubNode[i] ); + + psTreeNode->apsSubNode[i] = + psTreeNode->apsSubNode[psTreeNode->nSubNodes-1]; + + psTreeNode->nSubNodes--; + + i--; /* process the new occupant of this subnode entry */ + } + } + +/* -------------------------------------------------------------------- */ +/* If the current node has 1 subnode and no shapes, promote that */ +/* subnode to the current node position. */ +/* -------------------------------------------------------------------- */ + if( psTreeNode->nSubNodes == 1 && psTreeNode->nShapeCount == 0) + { + SHPTreeNode* psSubNode = psTreeNode->apsSubNode[0]; + + memcpy(psTreeNode->adfBoundsMin, psSubNode->adfBoundsMin, + sizeof(psSubNode->adfBoundsMin)); + memcpy(psTreeNode->adfBoundsMax, psSubNode->adfBoundsMax, + sizeof(psSubNode->adfBoundsMax)); + psTreeNode->nShapeCount = psSubNode->nShapeCount; + assert(psTreeNode->panShapeIds == NULL); + psTreeNode->panShapeIds = psSubNode->panShapeIds; + assert(psTreeNode->papsShapeObj == NULL); + psTreeNode->papsShapeObj = psSubNode->papsShapeObj; + psTreeNode->nSubNodes = psSubNode->nSubNodes; + for( i = 0; i < psSubNode->nSubNodes; i++ ) + psTreeNode->apsSubNode[i] = psSubNode->apsSubNode[i]; + free(psSubNode); + } + +/* -------------------------------------------------------------------- */ +/* We should be trimmed if we have no subnodes, and no shapes. */ +/* -------------------------------------------------------------------- */ + return( psTreeNode->nSubNodes == 0 && psTreeNode->nShapeCount == 0 ); +} + +/************************************************************************/ +/* SHPTreeTrimExtraNodes() */ +/* */ +/* Trim empty nodes from the tree. Note that we never trim an */ +/* empty root node. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPTreeTrimExtraNodes( SHPTree * hTree ) + +{ + SHPTreeNodeTrim( hTree->psRoot ); +} + +/************************************************************************/ +/* SwapWord() */ +/* */ +/* Swap a 2, 4 or 8 byte word. */ +/************************************************************************/ + +static void SwapWord( int length, void * wordP ) + +{ + int i; + unsigned char temp; + + for( i=0; i < length/2; i++ ) + { + temp = ((unsigned char *) wordP)[i]; + ((unsigned char *)wordP)[i] = ((unsigned char *) wordP)[length-i-1]; + ((unsigned char *) wordP)[length-i-1] = temp; + } +} + + +struct SHPDiskTreeInfo +{ + SAHooks sHooks; + SAFile fpQIX; +}; + +/************************************************************************/ +/* SHPOpenDiskTree() */ +/************************************************************************/ + +SHPTreeDiskHandle SHPOpenDiskTree( const char* pszQIXFilename, + SAHooks *psHooks ) +{ + SHPTreeDiskHandle hDiskTree; + + hDiskTree = (SHPTreeDiskHandle) calloc(sizeof(struct SHPDiskTreeInfo),1); + + if (psHooks == NULL) + SASetupDefaultHooks( &(hDiskTree->sHooks) ); + else + memcpy( &(hDiskTree->sHooks), psHooks, sizeof(SAHooks) ); + + hDiskTree->fpQIX = hDiskTree->sHooks.FOpen(pszQIXFilename, "rb"); + if (hDiskTree->fpQIX == NULL) + { + free(hDiskTree); + return NULL; + } + + return hDiskTree; +} + +/***********************************************************************/ +/* SHPCloseDiskTree() */ +/************************************************************************/ + +void SHPCloseDiskTree( SHPTreeDiskHandle hDiskTree ) +{ + if (hDiskTree == NULL) + return; + + hDiskTree->sHooks.FClose(hDiskTree->fpQIX); + free(hDiskTree); +} + +/************************************************************************/ +/* SHPSearchDiskTreeNode() */ +/************************************************************************/ + +static int +SHPSearchDiskTreeNode( SHPTreeDiskHandle hDiskTree, double *padfBoundsMin, double *padfBoundsMax, + int **ppanResultBuffer, int *pnBufferMax, + int *pnResultCount, int bNeedSwap, int nRecLevel ) + +{ + unsigned int i; + unsigned int offset; + unsigned int numshapes, numsubnodes; + double adfNodeBoundsMin[2], adfNodeBoundsMax[2]; + int nFReadAcc; + +/* -------------------------------------------------------------------- */ +/* Read and unswap first part of node info. */ +/* -------------------------------------------------------------------- */ + nFReadAcc = (int)hDiskTree->sHooks.FRead( &offset, 4, 1, hDiskTree->fpQIX ); + if ( bNeedSwap ) SwapWord ( 4, &offset ); + + nFReadAcc += (int)hDiskTree->sHooks.FRead( adfNodeBoundsMin, sizeof(double), 2, hDiskTree->fpQIX ); + nFReadAcc += (int)hDiskTree->sHooks.FRead( adfNodeBoundsMax, sizeof(double), 2, hDiskTree->fpQIX ); + if ( bNeedSwap ) + { + SwapWord( 8, adfNodeBoundsMin + 0 ); + SwapWord( 8, adfNodeBoundsMin + 1 ); + SwapWord( 8, adfNodeBoundsMax + 0 ); + SwapWord( 8, adfNodeBoundsMax + 1 ); + } + + nFReadAcc += (int)hDiskTree->sHooks.FRead( &numshapes, 4, 1, hDiskTree->fpQIX ); + if ( bNeedSwap ) SwapWord ( 4, &numshapes ); + + /* Check that we could read all previous values */ + if( nFReadAcc != 1 + 2 + 2 + 1 ) + { + hDiskTree->sHooks.Error("I/O error"); + return FALSE; + } + + /* Sanity checks to avoid int overflows in later computation */ + if( offset > INT_MAX - sizeof(int) ) + { + hDiskTree->sHooks.Error("Invalid value for offset"); + return FALSE; + } + + if( numshapes > (INT_MAX - offset - sizeof(int)) / sizeof(int) || + numshapes > INT_MAX / sizeof(int) - *pnResultCount ) + { + hDiskTree->sHooks.Error("Invalid value for numshapes"); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* If we don't overlap this node at all, we can just fseek() */ +/* pass this node info and all subnodes. */ +/* -------------------------------------------------------------------- */ + if( !SHPCheckBoundsOverlap( adfNodeBoundsMin, adfNodeBoundsMax, + padfBoundsMin, padfBoundsMax, 2 ) ) + { + offset += numshapes*sizeof(int) + sizeof(int); + hDiskTree->sHooks.FSeek(hDiskTree->fpQIX, offset, SEEK_CUR); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Add all the shapeids at this node to our list. */ +/* -------------------------------------------------------------------- */ + if(numshapes > 0) + { + if( *pnResultCount + numshapes > (unsigned int)*pnBufferMax ) + { + int* pNewBuffer; + + *pnBufferMax = (*pnResultCount + numshapes + 100) * 5 / 4; + + if( (size_t)*pnBufferMax > INT_MAX / sizeof(int) ) + *pnBufferMax = *pnResultCount + numshapes; + + pNewBuffer = (int *) + SfRealloc( *ppanResultBuffer, *pnBufferMax * sizeof(int) ); + + if( pNewBuffer == NULL ) + { + hDiskTree->sHooks.Error("Out of memory error"); + return FALSE; + } + + *ppanResultBuffer = pNewBuffer; + } + + if( hDiskTree->sHooks.FRead( *ppanResultBuffer + *pnResultCount, + sizeof(int), numshapes, hDiskTree->fpQIX ) != numshapes ) + { + hDiskTree->sHooks.Error("I/O error"); + return FALSE; + } + + if (bNeedSwap ) + { + for( i=0; isHooks.FRead( &numsubnodes, 4, 1, hDiskTree->fpQIX ) != 1 ) + { + hDiskTree->sHooks.Error("I/O error"); + return FALSE; + } + if ( bNeedSwap ) SwapWord ( 4, &numsubnodes ); + if( numsubnodes > 0 && nRecLevel == 32 ) + { + hDiskTree->sHooks.Error("Shape tree is too deep"); + return FALSE; + } + + for(i=0; isHooks.FSeek( hDiskTree->fpQIX, 0, SEEK_SET ); + hDiskTree->sHooks.FRead( abyBuf, 16, 1, hDiskTree->fpQIX ); + + if( memcmp( abyBuf, "SQT", 3 ) != 0 ) + return NULL; + + if( (abyBuf[3] == 2 && bBigEndian) + || (abyBuf[3] == 1 && !bBigEndian) ) + bNeedSwap = FALSE; + else + bNeedSwap = TRUE; + +/* -------------------------------------------------------------------- */ +/* Search through root node and it's decendents. */ +/* -------------------------------------------------------------------- */ + if( !SHPSearchDiskTreeNode( hDiskTree, padfBoundsMin, padfBoundsMax, + &panResultBuffer, &nBufferMax, + pnShapeCount, bNeedSwap, 0 ) ) + { + if( panResultBuffer != NULL ) + free( panResultBuffer ); + *pnShapeCount = 0; + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Sort the id array */ +/* -------------------------------------------------------------------- */ + qsort(panResultBuffer, *pnShapeCount, sizeof(int), compare_ints); + + /* To distinguish between empty intersection from error case */ + if( panResultBuffer == NULL ) + panResultBuffer = (int*) calloc(1, sizeof(int)); + + return panResultBuffer; +} + +/************************************************************************/ +/* SHPGetSubNodeOffset() */ +/* */ +/* Determine how big all the subnodes of this node (and their */ +/* children) will be. This will allow disk based searchers to */ +/* seek past them all efficiently. */ +/************************************************************************/ + +static int SHPGetSubNodeOffset( SHPTreeNode *node) +{ + int i; + long offset=0; + + for(i=0; inSubNodes; i++ ) + { + if(node->apsSubNode[i]) + { + offset += 4*sizeof(double) + + (node->apsSubNode[i]->nShapeCount+3)*sizeof(int); + offset += SHPGetSubNodeOffset(node->apsSubNode[i]); + } + } + + return(offset); +} + +/************************************************************************/ +/* SHPWriteTreeNode() */ +/************************************************************************/ + +static void SHPWriteTreeNode( SAFile fp, SHPTreeNode *node, SAHooks* psHooks) +{ + int i,j; + int offset; + unsigned char *pabyRec = NULL; + assert( NULL != node ); + + offset = SHPGetSubNodeOffset(node); + + pabyRec = (unsigned char *) + malloc(sizeof(double) * 4 + + (3 * sizeof(int)) + (node->nShapeCount * sizeof(int)) ); + if( NULL == pabyRec ) + { +#ifdef USE_CPL + CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure"); +#endif + assert( 0 ); + return; + } + + memcpy( pabyRec, &offset, 4); + + /* minx, miny, maxx, maxy */ + memcpy( pabyRec+ 4, node->adfBoundsMin+0, sizeof(double) ); + memcpy( pabyRec+12, node->adfBoundsMin+1, sizeof(double) ); + memcpy( pabyRec+20, node->adfBoundsMax+0, sizeof(double) ); + memcpy( pabyRec+28, node->adfBoundsMax+1, sizeof(double) ); + + memcpy( pabyRec+36, &node->nShapeCount, 4); + j = node->nShapeCount * sizeof(int); + memcpy( pabyRec+40, node->panShapeIds, j); + memcpy( pabyRec+j+40, &node->nSubNodes, 4); + + psHooks->FWrite( pabyRec, 44+j, 1, fp ); + free (pabyRec); + + for(i=0; inSubNodes; i++ ) + { + if(node->apsSubNode[i]) + SHPWriteTreeNode( fp, node->apsSubNode[i], psHooks); + } +} + +/************************************************************************/ +/* SHPWriteTree() */ +/************************************************************************/ + +int SHPAPI_CALL SHPWriteTree(SHPTree *tree, const char *filename ) +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return SHPWriteTreeLL(tree, filename, &sHooks); +} + +/************************************************************************/ +/* SHPWriteTreeLL() */ +/************************************************************************/ + +int SHPWriteTreeLL(SHPTree *tree, const char *filename, SAHooks* psHooks ) +{ + char signature[4] = "SQT"; + int i; + char abyBuf[32]; + SAFile fp; + + SAHooks sHooks; + if (psHooks == NULL) + { + SASetupDefaultHooks( &sHooks ); + psHooks = &sHooks; + } + +/* -------------------------------------------------------------------- */ +/* Open the output file. */ +/* -------------------------------------------------------------------- */ + fp = psHooks->FOpen(filename, "wb"); + if( fp == NULL ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this machine. */ +/* -------------------------------------------------------------------- */ + i = 1; + if( *((unsigned char *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; + +/* -------------------------------------------------------------------- */ +/* Write the header. */ +/* -------------------------------------------------------------------- */ + memcpy( abyBuf+0, signature, 3 ); + + if( bBigEndian ) + abyBuf[3] = 2; /* New MSB */ + else + abyBuf[3] = 1; /* New LSB */ + + abyBuf[4] = 1; /* version */ + abyBuf[5] = 0; /* next 3 reserved */ + abyBuf[6] = 0; + abyBuf[7] = 0; + + psHooks->FWrite( abyBuf, 8, 1, fp ); + + psHooks->FWrite( &(tree->nTotalCount), 4, 1, fp ); + + /* write maxdepth */ + + psHooks->FWrite( &(tree->nMaxDepth), 4, 1, fp ); + +/* -------------------------------------------------------------------- */ +/* Write all the nodes "in order". */ +/* -------------------------------------------------------------------- */ + + SHPWriteTreeNode( fp, tree->psRoot, psHooks ); + + psHooks->FClose( fp ); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/GNUmakefile new file mode 100644 index 000000000..0b81095c6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/GNUmakefile @@ -0,0 +1,16 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrsosidriver.o ogrsosidatasource.o ogrsosilayer.o +# ogrsosidatatypes.o + +CPPFLAGS :=-DLINUX -DUNIX -I.. -I../.. $(SOSI_INC) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_sosi.h + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/fyba_melding.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/fyba_melding.cpp new file mode 100644 index 000000000..7edb97613 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/fyba_melding.cpp @@ -0,0 +1,71 @@ +/****************************************************************************** + * $Id: fyba_melding.cpp 27897 2014-10-23 12:20:01Z jef $ + * + * Project: FYBA Callbacks + * Purpose: Needed by FYBA - however we do not want to display most messages + * Author: Thomas Hirsch, + * + ****************************************************************************** + * Copyright (c) 2010, Thomas Hirsch + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "fyba.h" + +static short sProsent; + +void LC_Error(short feil_nr, const char *logtx, const char *vartx) +{ + char szErrMsg[260]; + short strategi; + char *pszFeilmelding; + + + // Egen enkel implementasjon av feilhandtering + /* Hent feilmeldingstekst og strategi */ + strategi = LC_StrError(feil_nr,&pszFeilmelding); + switch(strategi) { + case 2: sprintf(szErrMsg,"%s","Observer følgende! \n\n");break; + case 3: sprintf(szErrMsg,"%s","Det er oppstått en feil! \n\n");break; + case 4: sprintf(szErrMsg,"%s","Alvorlig feil avslutt programmet! \n\n");break; + default: szErrMsg[0]='\0'; + } +} + + +void LC_StartMessage(const char *pszFilnavn) +{ +} + +void LC_ShowMessage(double prosent) +{ +} + +void LC_EndMessage(void) +{ +} + +short LC_Cancel(void) +{ + /* Not supported */ + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/makefile.vc new file mode 100644 index 000000000..665d85d07 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/makefile.vc @@ -0,0 +1,30 @@ + +OBJ = ogrsosidriver.obj ogrsosidatasource.obj \ + ogrsosilayer.obj fyba_melding.obj + +PLUGIN_DLL = ogr_SOSI.dll + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. -I$(SOSI_INC_DIR) + +default: $(OBJ) + +plugin: $(PLUGIN_DLL) + +$(PLUGIN_DLL): $(OBJ) + link /dll /out:$(PLUGIN_DLL) $(OBJ) $(GDALLIB) $(SOSI_LIBS) + if exist $(PLUGIN_DLL).manifest mt -manifest $(PLUGIN_DLL).manifest -outputresource:$(PLUGIN_DLL);2 + +clean: + -del *.lib + -del *.obj *.pdb *.exp + -del *.dll + -del *.manifest + +plugin-install: + -mkdir $(PLUGINDIR) + $(INSTALL) $(PLUGIN_DLL) $(PLUGINDIR) + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/ogr_sosi.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/ogr_sosi.h new file mode 100644 index 000000000..d2ea0ca7c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/ogr_sosi.h @@ -0,0 +1,126 @@ +/****************************************************************************** + * $Id: ogr_sosi.h 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: SOSI Translator + * Purpose: Implements OGRSOSIDriver. + * Author: Thomas Hirsch, + * + ****************************************************************************** + * Copyright (c) 2010, Thomas Hirsch + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SOSI_H_INCLUDED +#define _OGR_SOSI_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "fyba.h" +#include + +typedef std::map S2S; +typedef std::map S2I; + +void RegisterOGRSOSI(); + +class ORGSOSILayer; /* defined below */ +class OGRSOSIDataSource; /* defined below */ + +/************************************************************************ + * OGRSOSILayer * + * OGRSOSILayer reports all the OGR Features generated by the data * + * source, in an orderly fashion. * + ************************************************************************/ + +class OGRSOSILayer : public OGRLayer { + int nNextFID; + + OGRSOSIDataSource *poParent; /* used to call methods from data source */ + LC_FILADM *poFileadm; /* ResetReading needs to refer to the file struct */ + OGRFeatureDefn *poFeatureDefn; /* the common definition of all features returned by this layer */ + S2I *poHeaderDefn; + + LC_SNR_ADM oSnradm; + LC_BGR oNextSerial; /* used by FYBA to iterate through features */ + LC_BGR *poNextSerial; + +public: + OGRSOSILayer( OGRSOSIDataSource *poPar, OGRFeatureDefn *poFeatDefn, LC_FILADM *poFil, S2I *poHeadDefn); + ~OGRSOSILayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + OGRFeatureDefn * GetLayerDefn(); + OGRErr CreateField(OGRFieldDefn *poField, int bApproxOK=TRUE); + OGRErr ICreateFeature(OGRFeature *poFeature); + int TestCapability( const char * ); +}; + +/************************************************************************ + * OGRSOSIDataSource * + * OGRSOSIDataSource reads a SOSI file, prebuilds the features, and * + * creates one OGRSOSILayer per geometry type * + ************************************************************************/ +class OGRSOSIDataSource : public OGRDataSource { + char *pszName; + OGRSOSILayer **papoLayers; + int nLayers; + +#define MODE_READING 0 +#define MODE_WRITING 1 + int nMode; + + void buildOGRPoint(long nSerial); + void buildOGRLineString(int nNumCoo, long nSerial); + void buildOGRMultiPoint(int nNumCoo, long nSerial); + +public: + + OGRSpatialReference *poSRS; + const char *pszEncoding; + unsigned int nNumFeatures; + OGRGeometry **papoBuiltGeometries; /* OGRSOSIDataSource prebuilds some features upon opening, te be used + * by the more complex geometries later. */ + //FYBA specific + LC_BASEADM *poBaseadm; + LC_FILADM *poFileadm; + + S2I *poPolyHeaders; /* Contain the header definitions of the four feature layers */ + S2I *poPointHeaders; + S2I *poCurveHeaders; + S2I *poTextHeaders; + + OGRSOSIDataSource(); + ~OGRSOSIDataSource(); + + int Open ( const char * pszFilename, int bUpdate); + int Create( const char * pszFilename ); + const char *GetName() { + return pszName; + } + int GetLayerCount() { + return nLayers; + } + OGRLayer *GetLayer( int ); + OGRLayer *ICreateLayer( const char *pszName, OGRSpatialReference *poSpatialRef=NULL, OGRwkbGeometryType eGType=wkbUnknown, char **papszOptions=NULL); + int TestCapability( const char * ); +}; + +#endif /* _OGR_SOSI_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/ogrsosidatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/ogrsosidatasource.cpp new file mode 100644 index 000000000..9f4b39fea --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/ogrsosidatasource.cpp @@ -0,0 +1,619 @@ +/****************************************************************************** + * $Id: ogrsosidatasource.cpp 27794 2014-10-04 10:13:46Z rouault $ + * + * Project: SOSI Data Source + * Purpose: Provide SOSI Data to OGR. + * Author: Thomas Hirsch, + * + ****************************************************************************** + * Copyright (c) 2010, Thomas Hirsch + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sosi.h" +#include + +/* This is the most common encoding for SOSI files. Let's at least try if + * it is supported, or generate a meaningful error message. */ +#ifndef CPL_ENC_ISO8859_10 +# define CPL_ENC_ISO8859_10 "ISO8859-10" +#endif + +/************************************************************************/ +/* utility methods */ +/************************************************************************/ + +int epsg2sosi (int nEPSG) { + int nSOSI = 23; + switch (nEPSG) { + case 27391: /* NGO 1984 Axis I-VIII */ + case 27392: + case 27393: + case 27394: + case 27395: + case 27396: + case 27397: + case 27398: { + nSOSI = nEPSG - 27390; + break; + } + case 3043: /* UTM ZONE 31-36 */ + case 3044: + case 3045: + case 3046: + case 3047: + case 3048: { + nSOSI = nEPSG - 3022; + break; + } + case 23031: /* UTM ZONE 31-36 / ED50 */ + case 23032: + case 23033: + case 23034: + case 23035: + case 23036: { + nSOSI = nEPSG - 23000; + break; + } + case 4326: { /* WSG84 */ + nSOSI = 84; + break; + } + default: { + CPLError( CE_Warning, CPLE_AppDefined, + "(Yet) unsupported coodinate system writing to SOSI file: %i. Defaulting to EPSG:4326/SOSI 84.", nEPSG); + } + } + return nSOSI; +} + +int sosi2epsg (int nSOSI) { + int nEPSG = 4326; + switch (nSOSI) { + case 1: /* NGO 1984 Axis I-VIII */ + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: { + nEPSG = 27390+nSOSI; + break; + } + case 21: /* UTM ZONE 31-36 */ + case 22: + case 23: + case 24: + case 25: + case 26: { + nEPSG = 3022+nSOSI; + break; + } + case 31: /* UTM ZONE 31-36 / ED50 */ + case 32: + case 33: + case 34: + case 35: + case 36: { + nEPSG = 23000+nSOSI; + break; + } + case 84: { /* WSG84 */ + nEPSG = 4326; + break; + } + default: { + CPLError( CE_Warning, CPLE_AppDefined, + "(Yet) unsupported coodinate system in SOSI-file: %i. Defaulting to EPSG:4326.", nSOSI); + } + } + return nEPSG; +} + +/************************************************************************/ +/* OGRSOSIDataSource() */ +/************************************************************************/ + +OGRSOSIDataSource::OGRSOSIDataSource() { + nLayers = 0; + + poFileadm = NULL; + poBaseadm = NULL; + papoBuiltGeometries = NULL; + papoLayers = NULL; + pszName = NULL; + poSRS = NULL; + + poPolyHeaders = NULL; + poTextHeaders = NULL; + poPointHeaders = NULL; + poCurveHeaders = NULL; + + pszEncoding = CPL_ENC_UTF8; + nMode = MODE_READING; +} + +/************************************************************************/ +/* ~OGRSOSIDataSource() */ +/************************************************************************/ + +OGRSOSIDataSource::~OGRSOSIDataSource() { + if (papoBuiltGeometries != NULL) { + for (unsigned int i=0; iRelease(); + if (pszName != NULL) CPLFree(pszName); +} + +static +OGRFeatureDefn *defineLayer(const char *szName, OGRwkbGeometryType szType, S2I *poHeaders) { + OGRFeatureDefn *poFeatureDefn = new OGRFeatureDefn( szName ); + poFeatureDefn->SetGeomType( szType ); + + for (unsigned int n=0; nsize(); n++) { /* adding headers in the correct order again */ + for (S2I::iterator i=poHeaders->begin(); i!=poHeaders->end(); i++) { + if (n==i->second) { + OGRFieldDefn oFieldTemplate( i->first.c_str(), OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldTemplate ); + } + } + } + return poFeatureDefn; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRSOSIDataSource::Open( const char *pszFilename, int bUpdate ) { + papoBuiltGeometries = NULL; + poFileadm = NULL; + poBaseadm = NULL; + char *pszPos; + + if ( bUpdate ) { + CPLError( CE_Failure, CPLE_OpenFailed, + "Update access not supported by the SOSI driver." ); + return FALSE; + } + + /* Check that the file exists otherwise HO_TestSOSI() emits an error */ + VSIStatBuf sStat; + if( VSIStat(pszFilename, &sStat) != 0 ) + return FALSE; + + pszName = CPLStrdup( pszFilename ); + /* We ignore any layer parameters for now. */ + pszPos = strchr(pszName, ','); + if (pszPos != NULL) { + pszPos[0] = '\0'; + } + + /* Confirm that we are dealing with a SOSI file. Used also by data + * format auto-detection in some ogr utilities. */ + UT_INT64 nEnd = 0; + int bIsSosi = HO_TestSOSI ( pszName, &nEnd ); + if ( bIsSosi == UT_FALSE ) { + return FALSE; /* No error message: This is used by file format auto-detection */ + } + + short nStatus = 0, nDetStatus = 0; /* immediate status, detailed status */ + + /* open index base and sosi file */ + poBaseadm = LC_OpenBase(LC_BASE); + nStatus = LC_OpenSos(pszName, LC_BASE_FRAMGR, LC_GML_IDX, LC_INGEN_STATUS, + &poFileadm, &nDetStatus); + if ( nStatus == UT_FALSE ) { + char *pszErrorMessage; + LC_StrError(nDetStatus, &pszErrorMessage); + CPLError( CE_Failure, CPLE_OpenFailed, + "File %s could not be opened by SOSI Driver: %s", pszName, pszErrorMessage ); + return FALSE; + } + + /* --------------------------------------------------------------------* + * Prefetch all the information needed to determine layers * + * and prebuild LineString features for later assembly. * + * --------------------------------------------------------------------*/ + + /* allocate room for one pointer per feature */ + nNumFeatures = poFileadm->lAntGr; + void* mem = VSIMalloc2(nNumFeatures, sizeof(void*)); + if (mem == NULL) { + CPLError( CE_Failure, CPLE_OpenFailed, + "Memory allocation for SOSI features failed." ); + return FALSE; + } else { + papoBuiltGeometries = (OGRGeometry**)mem; + } + for (unsigned int i=0; ifind(osKey) == poPolyHeaders->end()) { + iH = poPolyHeaders->size(); + (*poPolyHeaders)[osKey] = iH; + } + break; + } + case L_KURVE: { + if (poCurveHeaders->find(osKey) == poCurveHeaders->end()) { + iH = poCurveHeaders->size(); + (*poCurveHeaders)[osKey] = iH; + } + break; + } + case L_PUNKT: { + if (poPointHeaders->find(osKey) == poPointHeaders->end()) { + iH = poPointHeaders->size(); + (*poPointHeaders)[osKey] = iH; + } + break; + } + case L_TEKST: { + if (poTextHeaders->find(osKey) == poTextHeaders->end()) { + iH = poTextHeaders->size(); + (*poTextHeaders)[osKey] = iH; + } + break; + } + } + } + CPLFree(pszUTFLine); + } + + /* Feature-specific tasks */ + switch (nName) { + case L_PUNKT: { + /* Pre-build a point feature. Activate point layer. */ + bPointLayer = TRUE; + buildOGRPoint(oNextSerial.lNr); + break; + } + case L_FLATE: { + /* Activate polygon layer. */ + bPolyLayer = TRUE; + /* cannot build geometries that reference others yet */ + break; + } + case L_KURVE: { + /* Pre-build a line feature. Activate line/curve layer. */ + bCurveLayer = TRUE; + buildOGRLineString(nNumCoo, oNextSerial.lNr); + break; + } + case L_TEKST: { + /* Pre-build a text line contour feature. Activate text layer. */ + /* Todo: observe only points 2ff if more than one point is given for follow mode */ + bTextLayer = TRUE; + buildOGRMultiPoint(nNumCoo, oNextSerial.lNr); + break; + } + case L_HODE: { + /* Get SRS from SOSI header. */ + unsigned short nMask = LC_TR_ALLT; + LC_TRANSPAR oTrans; + if (LC_GetTransEx(&nMask,&oTrans) == UT_FALSE) { + CPLError( CE_Failure, CPLE_OpenFailed, + "TRANSPAR section not found - No reference system information available."); + return NULL; + } + poSRS = new OGRSpatialReference(); + + /* Get coordinate system from SOSI header. */ + int nEPSG = sosi2epsg(oTrans.sKoordsys); + if (poSRS->importFromEPSG(nEPSG) != OGRERR_NONE) { + CPLError( CE_Failure, CPLE_OpenFailed, + "OGR could not load coordinate system definition EPSG:%i.", nEPSG); + return NULL; + } + + /* Get character encoding from SOSI header. */ + iHeaders = oHeaders.find("TEGNSETT"); + if (iHeaders != oHeaders.end()) { + CPLString osLine = iHeaders->second; + if (osLine.compare("ISO8859-1")==0) { + pszEncoding = CPL_ENC_ISO8859_1; + } else if (osLine.compare("ISO8859-10")==0) { + pszEncoding = CPL_ENC_ISO8859_10; + } else if (osLine.compare("UTF-8")==0) { + pszEncoding = CPL_ENC_UTF8; + } + } + + break; + } + default: { + break; + } + } + } + + /* -------------------------------------------------------------------- * + * Create a corresponding layers. One per geometry type * + * -------------------------------------------------------------------- */ + int nLayers = 0; + if (bPolyLayer) nLayers++; + if (bCurveLayer) nLayers++; + if (bPointLayer) nLayers++; + if (bTextLayer) nLayers++; + this->nLayers = nLayers; + /* allocate some memory for up to three layers */ + papoLayers = (OGRSOSILayer **) VSIMalloc2(sizeof(void*), nLayers); + + /* Define each layer, using a proper feature definition, geometry type, + * and adding every SOSI header encountered in the file as field. */ + S2I::iterator i; + if (bPolyLayer) { + OGRFeatureDefn *poFeatureDefn = defineLayer("polygons", wkbPolygon, poPolyHeaders); + poFeatureDefn->Reference(); + papoLayers[--nLayers] = new OGRSOSILayer( this, poFeatureDefn, poFileadm, poPolyHeaders ); + } else { + delete poPolyHeaders; + poPolyHeaders = NULL; + } + if (bCurveLayer) { + OGRFeatureDefn *poFeatureDefn = defineLayer("lines", wkbLineString, poCurveHeaders); + poFeatureDefn->Reference(); + papoLayers[--nLayers] = new OGRSOSILayer( this, poFeatureDefn, poFileadm, poCurveHeaders ); + } else { + delete poCurveHeaders; + poCurveHeaders = NULL; + } + if (bPointLayer) { + OGRFeatureDefn *poFeatureDefn = defineLayer("points", wkbPoint, poPointHeaders); + poFeatureDefn->Reference(); + papoLayers[--nLayers] = new OGRSOSILayer( this, poFeatureDefn, poFileadm, poPointHeaders ); + } else { + delete poPointHeaders; + poPointHeaders = NULL; + } + if (bTextLayer) { + OGRFeatureDefn *poFeatureDefn = defineLayer("text", wkbMultiPoint, poTextHeaders); + poFeatureDefn->Reference(); + papoLayers[--nLayers] = new OGRSOSILayer( this, poFeatureDefn, poFileadm, poTextHeaders ); + } else { + delete poTextHeaders; + poTextHeaders = NULL; + } + return TRUE; +} + + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRSOSIDataSource::Create( const char *pszFilename ) { + short nStatus; + short nDetStatus; + + poBaseadm = LC_OpenBase(LC_KLADD); + nStatus = LC_OpenSos(pszFilename, LC_SEKV_SKRIV, LC_NY_IDX, LC_INGEN_STATUS, + &poFileadm, &nDetStatus); + if (nStatus == UT_FALSE) { + CPLError( CE_Failure, CPLE_OpenFailed, + "Could not open SOSI file for writing (Status %i).", nDetStatus ); + return FALSE; + } + + LC_NyttHode(); /* Create new file header, will be written to file when all + header information elements are set. */ + + return TRUE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer *OGRSOSIDataSource::ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef, + OGRwkbGeometryType eGType, + CPL_UNUSED char **papszOptions ) { + /* SOSI does not really support layers - so let's first see that the global settings are consistent */ + if (poSRS == NULL) { + if (poSpatialRef!=NULL) { + poSRS = poSpatialRef; + poSRS->Reference(); + + const char *pszKoosys = poSRS->GetAuthorityCode("PROJCS"); + if (pszKoosys == NULL) { + OGRErr err = poSRS->AutoIdentifyEPSG(); + if (err == OGRERR_UNSUPPORTED_SRS) { + CPLError( CE_Failure, CPLE_OpenFailed, + "Could not identify EPSG code for spatial reference system"); + return NULL; + } + pszKoosys = poSRS->GetAuthorityCode("PROJCS"); + } + + if (pszKoosys != NULL) { + int nKoosys = epsg2sosi(atoi(pszKoosys)); + CPLDebug( "[CreateLayer]","Projection set to SOSI %i", nKoosys); + LC_PutTrans(nKoosys,0,0,0.01,0.01,0.01); + } else { + pszKoosys = poSRS->GetAuthorityCode("GEOGCS"); + if (pszKoosys != NULL) { + int nKoosys = epsg2sosi(atoi(pszKoosys)); + LC_PutTrans(nKoosys,0,0,0.01,0.01,0.01); + } else { + CPLError( CE_Failure, CPLE_OpenFailed, + "Could not retrieve EPSG code for spatial reference system"); + return NULL; + } + } + } + LC_WsGr(poFileadm); /* Writing the header here! */ + + } else { + if (!poSRS->IsSame(poSpatialRef)) { + CPLError( CE_Failure, CPLE_AppDefined, + "SOSI driver does not support different spatial reference systems in one file."); + } + } + + OGRFeatureDefn *poFeatureDefn = new OGRFeatureDefn( pszName ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( eGType ); + OGRSOSILayer *poLayer = new OGRSOSILayer( this, poFeatureDefn, poFileadm, NULL /*poHeaderDefn*/); + /* todo: where do we delete poLayer and poFeatureDefn? */ + return poLayer; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRSOSIDataSource::GetLayer( int iLayer ) { + if ( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +void OGRSOSIDataSource::buildOGRMultiPoint(int nNumCoo, long iSerial) { + if (papoBuiltGeometries[iSerial] != NULL) { + return; + } + + OGRMultiPoint *poMP = new OGRMultiPoint(); + + long i; + double dfEast = 0, dfNorth = 0; + for (i=(nNumCoo>1)?2:1; i<=nNumCoo; i++) { + LC_GetTK(i, &dfEast, &dfNorth); + OGRPoint poP = OGRPoint(dfEast, dfNorth); + poMP->addGeometry(&poP); /*poP will be cloned before returning*/ + } + papoBuiltGeometries[iSerial] = poMP; +} + +void OGRSOSIDataSource::buildOGRLineString(int nNumCoo, long iSerial) { + if (papoBuiltGeometries[iSerial] != NULL) { + return; + } + + OGRLineString *poLS = new OGRLineString(); + poLS->setNumPoints(nNumCoo); + + long i; + double dfEast = 0, dfNorth = 0; + for (i=1; i<=nNumCoo; i++) { + LC_GetTK(i, &dfEast, &dfNorth); + poLS->setPoint(i-1, dfEast, dfNorth); + } + papoBuiltGeometries[iSerial] = poLS; +} +void OGRSOSIDataSource::buildOGRPoint(long iSerial) { + double dfEast = 0, dfNorth = 0; + LC_GetTK(1, &dfEast, &dfNorth); + papoBuiltGeometries[iSerial] = new OGRPoint(dfEast, dfNorth); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSOSIDataSource::TestCapability( const char * pszCap ) { + if (strcmp("CreateLayer",pszCap) == 0) { + return TRUE; + } else { + CPLDebug( "[TestCapability]","Capability %s not supported by SOSI data source", pszCap); + } + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/ogrsosidriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/ogrsosidriver.cpp new file mode 100644 index 000000000..c31510a39 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/ogrsosidriver.cpp @@ -0,0 +1,120 @@ +/****************************************************************************** + * $Id: ogrsosidriver.cpp 27794 2014-10-04 10:13:46Z rouault $ + * + * Project: SOSI Translator + * Purpose: Implements OGRSOSIDriver. + * Author: Thomas Hirsch, + * + ****************************************************************************** + * Copyright (c) 2010, Thomas Hirsch + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sosi.h" + +static int bFYBAInit = FALSE; + +/************************************************************************/ +/* OGRSOSIDriverUnload() */ +/************************************************************************/ + +static void OGRSOSIDriverUnload(CPL_UNUSED GDALDriver* poDriver) { + + if ( bFYBAInit ) + { + LC_Close(); /* Close FYBA */ + bFYBAInit = FALSE; + } +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRSOSIDriverOpen( GDALOpenInfo* poOpenInfo ) +{ + if( poOpenInfo->fpL == NULL || + strstr((const char*)poOpenInfo->pabyHeader, ".HODE") == NULL ) + return NULL; + + if ( !bFYBAInit ) + { + LC_Init(); /* Init FYBA */ + bFYBAInit = TRUE; + } + + OGRSOSIDataSource *poDS = new OGRSOSIDataSource(); + if ( !poDS->Open( poOpenInfo->pszFilename, 0 ) ) { + delete poDS; + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRSOSIDriverCreate( const char * pszName, + CPL_UNUSED int nBands, CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, CPL_UNUSED GDALDataType eDT, + CPL_UNUSED char **papszOptions ) +{ + + if ( !bFYBAInit ) + { + LC_Init(); /* Init FYBA */ + bFYBAInit = TRUE; + } + OGRSOSIDataSource *poDS = new OGRSOSIDataSource(); + if ( !poDS->Create( pszName ) ) { + delete poDS; + return NULL; + } + return poDS; +} + +/************************************************************************/ +/* RegisterOGRSOSI() */ +/************************************************************************/ + +void RegisterOGRSOSI() { + GDALDriver *poDriver; + + if( GDALGetDriverByName( "SOSI" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SOSI" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Norwegian SOSI Standard" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_sosi.html" ); + + poDriver->pfnOpen = OGRSOSIDriverOpen; + poDriver->pfnCreate = OGRSOSIDriverCreate; + poDriver->pfnUnloadDriver = OGRSOSIDriverUnload; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/ogrsosilayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/ogrsosilayer.cpp new file mode 100644 index 000000000..0f68c4b63 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sosi/ogrsosilayer.cpp @@ -0,0 +1,306 @@ +/****************************************************************************** + * $Id: ogrsosilayer.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: SOSI Translator + * Purpose: Implements OGRSOSILayer. + * Author: Thomas Hirsch, + * + ****************************************************************************** + * Copyright (c) 2010, Thomas Hirsch + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sosi.h" +#include + +/************************************************************************/ +/* OGRSOSILayer() */ +/************************************************************************/ + +OGRSOSILayer::OGRSOSILayer( OGRSOSIDataSource *poPar, OGRFeatureDefn *poFeatDefn, LC_FILADM *poFil, std::map *poHeadDefn) { + poParent = poPar; + poFileadm = poFil; + poFeatureDefn = poFeatDefn; + poHeaderDefn = poHeadDefn; + nNextFID = 0; + poNextSerial = NULL; + + SetDescription( poFeatureDefn->GetName() ); + if( poFeatureDefn->GetGeomFieldCount() > 0 ) + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poParent->poSRS); + + ResetReading(); +} + +/************************************************************************/ +/* ~OGRSOSILayer() */ +/************************************************************************/ +OGRSOSILayer::~OGRSOSILayer() { + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ +OGRFeatureDefn *OGRSOSILayer::GetLayerDefn() { + return poFeatureDefn; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ +OGRErr OGRSOSILayer::CreateField (OGRFieldDefn *poField, CPL_UNUSED int bApproxOK) { + poFeatureDefn->AddFieldDefn( poField ); + return OGRERR_NONE; /* We'll just gladly accept any "field" we find */ +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ +OGRErr OGRSOSILayer::ICreateFeature(OGRFeature *poFeature) { + short nNavn; + long nSerial; + + const char *pszSosi = NULL; + switch (poFeatureDefn->GetGeomType()) { + case wkbPoint: { + pszSosi = ".PUNKT"; + break; + } + case wkbLineString: { + pszSosi = ".KURVE"; + break; + } + case wkbPolygon: { + pszSosi = ".FLATE"; + break; + } + default: { + CPLError( CE_Warning, CPLE_AppDefined, "Unknown geometry type in CreateFeature."); + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + } + nNavn = LC_NyGr(poFileadm, (char *)pszSosi, &oNextSerial, &nSerial); + /* === WIP - Work in progress === */ + /* PutGI for all headers */ + char pszGi[255]; + for (int i=0;iGetFieldCount();i++) { + int n = snprintf (pszGi, 255, "%s", poFeature->GetFieldDefnRef(i)->GetNameRef()); + if (n<255) { + /*int m = */snprintf (pszGi + (n-1), 255-n, "%s", poFeature->GetFieldAsString(i)); + /* check overflow */ + } + LC_PutGi(i+2, pszGi); /* should add headers too */ + } + //LC_OppdaterEndret(0); + /* PutTK for all coords */ + /* ... */ + /* === /WIP - Work in progress === */ + LC_WsGr(poFileadm); /* Writing the header here! */ + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ +OGRFeature *OGRSOSILayer::GetNextFeature() { + short nName, nNumLines; + long nNumCoo; + unsigned short nInfo; + + typedef std::map S2S; + + /* iterate through the SOSI groups*/ + while (LC_NextBgr(poNextSerial,LC_FRAMGR)) { + nName = LC_RxGr(&oNextSerial, LES_OPTIMALT, &nNumLines, &nNumCoo, &nInfo); + + S2S oHeaders; + S2S::iterator iHeaders; + /* extract reference strings from group header */ + CPLString osKey, osValue; + for (short i=1; i<=nNumLines; i++) { + char *pszLine = LC_GetGi(i); + if (pszLine[0] == '!') continue; /* If we have a comment line, skip it. */ + if ((pszLine[0] == ':')||(pszLine[0] == '(')) { /* if we have a continued REF line... */ + osValue.append(CPLString(pszLine)); /* append to previous line. */ + oHeaders.insert(std::pair(osKey,osValue)); + continue; + } + while (pszLine[0] == '.') pszLine++; /* skipping the dots at the beginning of a SOSI line */ + char *pszUTFLine = CPLRecode(pszLine, poParent->pszEncoding, CPL_ENC_UTF8); /* switch to UTF encoding here */ + char *pszPos = strstr(pszUTFLine, " "); + if (pszPos != NULL) { + osKey = CPLString(std::string(pszUTFLine,pszPos)); + osValue = CPLString(pszPos+1); + oHeaders.insert(std::pair(osKey,osValue)); + } + CPLFree(pszUTFLine); + } + + /* get Feature from fyba, according to feature definition */ + OGRGeometry *poGeom = NULL; + OGRwkbGeometryType oGType = wkbUnknown; + + switch (nName) { + case INGEN_GRUPPE: { /* No group */ + CPLDebug( "[GetNextFeature]", "Could not load further groups - FYBA reported INGEN_GRUPPE."); + break; + } + case L_FLATE: { /* Area */ + oGType = wkbPolygon; + OGRLinearRing *poOuter = new OGRLinearRing(); /* Initialize a new closed polygon */ + long nRefNr; + unsigned char nRefStatus; + long nRefCount; + LC_GRF_STATUS oGrfStat; + + LC_InitGetRefFlate(&oGrfStat); /* Iterate through all objects that constitute this area */ + nRefCount = LC_GetRefFlate(&oGrfStat, GRF_YTRE, &nRefNr, &nRefStatus, 1); + while (nRefCount > 0) { + if (poParent->papoBuiltGeometries[nRefNr] == NULL) { /* this shouldn't happen under normal operation */ + CPLError( CE_Fatal, CPLE_AppDefined, "Feature %li referenced by %li, but it was not initialized.", nRefNr, oNextSerial.lNr); + return NULL; + } + OGRLineString *poCurve = (OGRLineString*)(poParent->papoBuiltGeometries[nRefNr]); + if (nRefStatus == LC_MED_DIG) { /* clockwise */ + poOuter->addSubLineString(poCurve); + } else if (nRefStatus == LC_MOT_DIG) { /* counter-clockwise */ + poOuter->addSubLineString(poCurve,poCurve->getNumPoints()-1,0); + } else { + CPLError( CE_Failure, CPLE_OpenFailed, "Island (OEY) encountered, but not yet supported."); + return NULL; + } + nRefCount = LC_GetRefFlate(&oGrfStat, GRF_YTRE, &nRefNr, &nRefStatus, 1); + } + + OGRPolygon *poLy = new OGRPolygon(); + poLy->addRingDirectly(poOuter); + poGeom = poLy; + break; + } + case L_KURVE: { /* curve */ + oGType = wkbLineString; + + OGRLineString *poCurve = (OGRLineString*)(poParent->papoBuiltGeometries[oNextSerial.lNr]); + if (poCurve == NULL) { + CPLError( CE_Fatal, CPLE_AppDefined, "Curve %li was not initialized.", oNextSerial.lNr); + return NULL; + } + poGeom = poCurve->clone(); + break; + } + case L_TEKST: { /* text */ + oGType = wkbMultiPoint; + + OGRMultiPoint *poMP = (OGRMultiPoint*)(poParent->papoBuiltGeometries[oNextSerial.lNr]); + if (poMP == NULL) { + CPLError( CE_Fatal, CPLE_AppDefined, "Tekst %li was not initialized.", oNextSerial.lNr); + return NULL; + } + poGeom = poMP->clone(); + break; + } + case L_PUNKT: { /* point */ + oGType = wkbPoint; + OGRPoint *poPoint = (OGRPoint*)(poParent->papoBuiltGeometries[oNextSerial.lNr]); + if (poPoint == NULL) { + CPLError( CE_Fatal, CPLE_AppDefined, "Point %li was not initialized.", oNextSerial.lNr); + return NULL; + } + poGeom = poPoint->clone(); + break; + } + case L_DEF: /* skip user definitions and headers here */ + case L_HODE: { + break; + } + default: { /* complain a bit about anything else that is not implemented */ + CPLError( CE_Failure, CPLE_OpenFailed, "Unrecognized geometry of type %i.", nName); + break; + } + } + + if (poGeom == NULL) continue; /* skipping L_HODE and unrecognized groups */ + if (oGType != poFeatureDefn->GetGeomType()) { + if (poGeom != NULL) delete poGeom; + continue; /* skipping features that are not the correct geometry */ + } + + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + /* set all headers found in this group - we export everything, just in case */ + for (iHeaders = oHeaders.begin(); iHeaders != oHeaders.end(); iHeaders++) { + const char *pszLine = iHeaders->second.c_str(); + int iHNr = poHeaderDefn->find(iHeaders->first)->second; + if (iHNr == -1) { + CPLError( CE_Warning, CPLE_AppDefined, "Could not find field definition for %s.", iHeaders->first.c_str()); + continue; + } + if ((pszLine[0] == '\'')||(pszLine[0] == '\"')) { /* If the value is quoted, ignore these */ + int nLen = strlen(pszLine); + char *pszNline = (char*)CPLMalloc(nLen-1); + strncpy(pszNline, pszLine+1, nLen-2); + pszNline[nLen-2] = '\0'; + poFeature->SetField( iHNr, pszNline); + CPLFree(pszNline); + } else { + poFeature->SetField( iHNr, pszLine); + } + } + + if( poGeom != NULL ) + poGeom->assignSpatialReference(poParent->poSRS); + + poFeature->SetGeometryDirectly( poGeom ); + poFeature->SetFID( nNextFID++ ); + + /* Loop until we have a feature that matches the definition */ + if ( (m_poFilterGeom == NULL || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + delete poFeature; + } + return NULL; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ +void OGRSOSILayer::ResetReading() { + LC_SBSn(&oSnradm, poFileadm, 0, poFileadm->lAntGr); /* set FYBA Search limits */ + poNextSerial = &oNextSerial; + LC_InitNextBgr(poNextSerial); + nNextFID = 0; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSOSILayer::TestCapability( const char * pszCap ) { + + if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + if( EQUAL(pszCap,OLCCreateField) ) + return TRUE; + else + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/GNUmakefile new file mode 100644 index 000000000..fb795c5d3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/GNUmakefile @@ -0,0 +1,41 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrsqlitedatasource.o ogrsqlitelayer.o ogrsqlitedriver.o \ + ogrsqlitetablelayer.o ogrsqliteviewlayer.o ogrsqliteselectlayer.o ogrsqlitesinglefeaturelayer.o \ + ogrsqlitevfs.o ogrsqlitevirtualogr.o ogrsqliteexecutesql.o ogrsqliteapiroutines.o + +ifeq ($(HAVE_SPATIALITE),yes) +CPPFLAGS += -DHAVE_SPATIALITE +endif + +ifeq ($(SPATIALITE_AMALGAMATION),yes) +CPPFLAGS += -DSPATIALITE_AMALGAMATION +endif + +ifeq ($(SPATIALITE_412_OR_LATER),yes) +CPPFLAGS += -DSPATIALITE_412_OR_LATER +endif + +ifeq ($(HAVE_PCRE),yes) +CPPFLAGS += -DHAVE_PCRE +endif + +ifeq ($(SQLITE_HAS_COLUMN_METADATA),yes) +CPPFLAGS += -DSQLITE_HAS_COLUMN_METADATA +endif + +CPPFLAGS := -I.. $(SQLITE_INC) $(SPATIALITE_INC) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +all: default test_load_virtual_ogr$(EXE) + +clean: + rm -f *.o $(O_OBJ) test_load_virtual_ogr$(EXE) + +$(O_OBJ): ogr_sqlite.h ogrsqlitevirtualogr.h ogrsqliteexecutesql.h ogrsqliteregexp.h ogrsqlitesqlfunctions.h ogrsqliteregexp.cpp ogrsqlitesqlfunctions.cpp + +test_load_virtual_ogr$(EXE): test_load_virtual_ogr.$(OBJ_EXT) + $(LD) $(LDFLAGS) test_load_virtual_ogr.$(OBJ_EXT) -lsqlite3 -o test_load_virtual_ogr$(EXE) \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/drv_sqlite.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/drv_sqlite.html new file mode 100644 index 000000000..55991cd50 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/drv_sqlite.html @@ -0,0 +1,368 @@ + + +SQLite / Spatialite RDBMS + + + + +

    SQLite / Spatialite RDBMS

    + +

    OGR optionally supports spatial and non-spatial tables stored in SQLite 3.x +database files. SQLite is a "light weight" single file based RDBMS engine +with fairly complete SQL semantics and respectible performance.

    + +

    The driver can handle "regular" SQLite databases, as well as Spatialite +databases (spatial enabled SQLite databases).

    + +

    The SQLite database is essentially typeless, but the SQLite driver will +attempt to classify attributes field as text, integer or floating point +based on the contents of the first record in a table. None of the list +attribute field types existing in SQLite. Starting with OGR 1.10, datetime +field types are also handled.

    + +

    SQLite databases often due not work well over NFS, or some other networked +file system protocols due to the poor support for locking. It is safest +to operate only on SQLite files on a physical disk of the local system.

    + +

    SQLite is an optionally compiled in driver. It is not compiled in by default.

    + +

    By default, SQL statements are passed directly to the SQLite database engine. +It's also possible to request the driver to handle SQL commands +with OGR SQL engine, +by passing "OGRSQL" string to the ExecuteSQL() +method, as name of the SQL dialect.

    + +

    Starting with OGR 1.8.0, the OGR_SQLITE_SYNCHRONOUS configuration option has been added. +When set to OFF, this issues a 'PRAGMA synchronous = OFF' command to the SQLite database. +This has the advantage of speeding-up some write operations (e.g. on EXT4 filesystems), but +at the expense of data safety w.r.t system/OS crashes. So use it carefully in +production environments and read the SQLite +related documentation.

    + +

    Starting with OGR 1.11, any SQLite pragma can be +specified with the OGR_SQLITE_PRAGMA configuration option. The syntax is +OGR_SQLITE_PRAGMA = "pragma_name=pragma_value[,pragma_name2=pragma_value2]*".

    + +

    "Regular" SQLite databases

    + +

    The driver looks for a geometry_columns table layed out as defined +loosely according to OGC Simple Features standards, particularly as defined +in FDO RFC 16. If +found it is used to map tables to layers.

    + +

    If geometry_columns is not found, each table is treated as a layer. Layers +with a WKT_GEOMETRY field will be treated as spatial tables, and the +WKT_GEOMETRY column will be read as Well Known Text geometry.

    + +

    If geometry_columns is found, it will be used to lookup spatial reference +systems in the spatial_ref_sys table.

    + +

    While the SQLite driver supports reading spatial data from records, there is +no support for spatial indexing, so spatial queries will tend to be slow (use Spatialite for that). +Attributes queries may be fast, especially if indexes are built for +appropriate attribute columns using the "CREATE INDEX +ON ( )" SQL command.

    + +

    Starting with GDAL 2.0, the driver also supports reading and writing the +following non-linear geometry types :CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE and MULTISURFACE. +Note: this is not true for Spatialite databases, since those geometry types +are not supported by current Spatialite versions.

    + +

    Tables with multiple geometry columns

    + +

    +Starting with GDAL 2.0, layers with multiple geometry columns can be created, +modified or read, following new API described in RFC 41 +

    + +

    +In GDAL 1.10, such support was read-only and there were as many OGR layers exposed +as there are geometry columns. They were named "table_name(geometry_column_name)". +Such syntax is still possible with GDAL 2.0 when explictely querying a layer +with GetLayerByName()

    + +

    REGEXP operator

    + +By default, the REGEXP operator has no implementation in SQLite. With OGR >= 1.10 built against +the PCRE library, the REGEXP operator is available in SQL statements run by OGR. + +

    VSI Virtual File System API support

    + +(Require OGR >= 1.9.0 and SQLite >= 3.6.0)

    + +The driver supports reading and writing to files managed by VSI Virtual File System API, which include +"regular" files, as well as files in the /vsimem/ (read-write), /vsizip/ (read-only), /vsigzip/ (read-only), /vsicurl/ (read-only) domains.

    + +Note: for regular files, the standard I/O operations provided by SQLite are used, in order to benefit +from its integrity guarantees.

    + +

    Using the SpatiaLite library (Spatial extension for SQLite)

    + +(Starting with GDAL 1.7.0)

    + +The SQLite driver can read and write SpatiaLite databases. Creating or updating a spatialite database requires +explicit linking against SpatiaLite library (version >= 2.3.1). Explicit linking against SpatiaLite library also +provides access to functions provided by this library, such as spatial indexes, spatial functions, etc...

    + +A few examples : +

    +# Duplicate the sample database provided with SpatiaLite
    +ogr2ogr -f SQLite testspatialite.sqlite test-2.3.sqlite  -dsco SPATIALITE=YES
    +
    +# Make a request with a spatial filter. Will work faster if spatial index has
    +# been created and explicit linking against SpatiaLite library.
    +ogrinfo testspatialite.sqlite Towns -spat 754000 4692000 770000 4924000
    +
    + +

    Opening with 'VirtualShape:'

    + +(Require OGR >= 1.9.0 and Spatialite support)

    + +It is possible to open on-the-fly a shapefile as a VirtualShape with Spatialite. The syntax to use for the +datasource is "VirtualShape:/path/to/shapefile.shp" (the shapefile must be a "real" file).

    + +This gives the capability to use the spatial operations of Spatialite (note that spatial indexes on virtual +tables are not available).

    + +

    The SQLite SQL dialect

    + +Starting with OGR 1.10, the SQLite SQL engine can be used to run SQL queries +on any OGR datasource if using the SQLite SQL dialect. + +

    The VirtualOGR SQLite extension

    + +Starting with OGR 1.10, the GDAL/OGR library can be loaded as a SQLite extension. + +The extension is loaded with the load_extension(gdal_library_name) SQL function, where gdal_library_name is +typically libgdal.so on Unix/Linux, gdal110.dll on Windows, etc..

    + +After the extension is loaded, a virtual table, corresponding to a OGR layer, can be created with one of +the following SQL statement : +

    +CREATE VIRTUAL TABLE table_name USING VirtualOGR(datasource_name);
    +CREATE VIRTUAL TABLE table_name USING VirtualOGR(datasource_name, update_mode);
    +CREATE VIRTUAL TABLE table_name USING VirtualOGR(datasource_name, update_mode, layer_name);
    +CREATE VIRTUAL TABLE table_name USING VirtualOGR(datasource_name, update_mode, layer_name, expose_ogr_style);
    +
    + +where : +
      +
    • datasource_name is the connexion string to any OGR datasource.
    • +
    • update_mode = 0 for read-only mode (default value) or 1 for update mode.
    • +
    • layer_name = the name of a layer of the opened datasource.
    • +
    • expose_ogr_style = 0 to prevent the OGR_STYLE special from being displayed (default value) or 1 to expose it.
    • +
    + +Note: layer_name does not need to be specified if the datasource has only one single layer. + +

    + +From the sqlite3 console, a typical use case is : +

    +sqlite> SELECT load_extension('libgdal.so');
    +
    +sqlite> SELECT load_extension('libspatialite.so');
    +
    +sqlite> CREATE VIRTUAL TABLE poly USING VirtualOGR('poly.shp');
    +
    +sqlite> SELECT *, ST_Area(GEOMETRY) FROM POLY;
    +215229.266|168.0|35043411||215229.265625
    +247328.172|179.0|35043423||247328.171875
    +261752.781|171.0|35043414||261752.78125
    +547597.188|173.0|35043416||547597.2109375
    +15775.758|172.0|35043415||15775.7578125
    +101429.977|169.0|35043412||101429.9765625
    +268597.625|166.0|35043409||268597.625
    +1634833.375|158.0|35043369||1634833.390625
    +596610.313|165.0|35043408||596610.3359375
    +5268.813|170.0|35043413||5268.8125
    +
    + +

    + +Alternatively, you can use the ogr_datasource_load_layers(datasource_name[, update_mode[, prefix]]) +function to automatically load all the layers of a datasource. + +

    +sqlite> SELECT load_extension('libgdal.so');
    +
    +sqlite> SELECT load_extension('libspatialite.so');
    +
    +sqlite> SELECT ogr_datasource_load_layers('poly.shp');
    +1
    +sqlite> SELECT * FROM sqlite_master;
    +table|poly|poly|0|CREATE VIRTUAL TABLE "poly" USING VirtualOGR('poly.shp', 0, 'poly')
    +
    + +Refer to the SQLite SQL dialect for an overview of the capabilities of VirtualOGR tables.

    + +

    Creation Issues

    + +

    The SQLite driver supports creating new SQLite database files, or adding +tables to existing ones. Note that a new database file cannot be +created over an existing file.

    + +

    Transaction support (GDAL >= 2.0)

    + +

    +The driver implements transactions at the database level, per +RFC 54 +

    + +

    Dataset open options

    + +(GDAL >= 2.0) + +
      +
    • LIST_ALL_TABLES=YES/NO: This may be "YES" to force all tables, +including non-spatial ones, to be listed.

      + +

    • LIST_VIRTUAL_OGR=YES/NO: This may be "YES" to force VirtualOGR virtual +tables to be listed. This should only be enabled on trusted datasources to avoid +potential safety issues.

      +

    + +

    Database Creation Options

    + +
      + +
    • METADATA=YES/NO: This can be used to avoid creating the +geometry_columns and spatial_ref_sys tables in a new database. By default +these metadata tables are created when a new database is created.

    • + +
    • SPATIALITE=YES/NO: (Starting with GDAL 1.7.0) Create the SpatiaLite flavour of the metadata +tables, which are a bit differ from the metadata used by this OGR driver and +from OGC specifications. Implies METADATA=YES.
      +Please note: (Starting with GDAL 1.9.0) OGR must be linked against libspatialite in order to support +insert/write on SpatiaLite; if not, read-only mode is enforced.
      +Attempting to perform any insert/write on SpatiaLite skipping the appropriate library support simply produces broken (corrupted) DB-files.
      +Important notice: when the underlaying libspatialite is v.2.3.1 (or any previous +version) any Geometry will be casted to 2D [XY], because earlier versions of this library +are simply able to support 2D [XY] dimensions. Version 2.4.0 (or any subsequent) is required in order to support 2.5D [XYZ].

    • + +
    • INIT_WITH_EPSG=YES/NO: (Starting with GDAL 1.8.0) Insert the content of the EPSG CSV files +into the spatial_ref_sys table. Defaults to NO for regular SQLite databases.
      +Please note: if SPATIALITE=YES and the underlaying libspatialite is v2.4 or v3.X, +INIT_WITH_EPSG is ignored; those library versions will unconditionally load the EPSG +dataset into the spatial_ref_sys table when creating a new DB (self-initialization). Starting +with libspatialite 4.0, INIT_WITH_EPSG defaults to YES, but can be set to NO.

    • + +
    + +

    Layer Creation Options

    + +
      + +
    • FORMAT=WKB/WKT/SPATIALITE: Controls the format used for the +geometry column. By default WKB (Well Known Binary) is used. This is +generally more space and processing efficient, but harder to inspect or use in +simple applications than WKT (Well Known Text). SpatiaLite extension uses its +own binary format to store geometries and you can choose it as well. It will +be selected automatically when SpatiaLite database is opened or created with +SPATIALITE=YES option. SPATIALITE value is available starting with GDAL 1.7.0.

    • + +
    • GEOMETRY_NAME: (Starting with GDAL 2.0) By default OGR creates new tables with the +geometry column named GEOMETRY (or WKT_GEOMETRY if FORMAT=WKT). If you wish to use a different name, +it can be supplied with the GEOMETRY_NAME layer creation option.

    • + +
    • LAUNDER=YES/NO: Controls whether layer and field names will be +laundered for easier use in SQLite. Laundered names will be convered to lower +case and some special characters(' - #) will be changed to underscores. Default +to YES.

    • + +
    • SPATIAL_INDEX=YES/NO: (Starting with GDAL 1.7.0) If the database is +of the SpatiaLite flavour, and if OGR is linked against libspatialite, this option +can be used to control if a spatial index must be created. Default to YES.

    • + +
    • COMPRESS_GEOM=YES/NO: (Starting with GDAL 1.9.0) If the format of the +geometry BLOB is of the SpatiaLite flavour, this option can be used to control +if the compressed format for geometries (LINESTRINGs, POLYGONs) must be used. This +format is understood by Spatialite v2.4 (or any subsequent version). Default to NO. +Note: when updating an existing Spatialite DB, the COMPRESS_GEOM configuration option +can be set to produce similar results for appended/overwritten features.

    • + +
    • SRID=srid: (Starting with GDAL 1.10) Used to force the SRID number of the +SRS associated with the layer. When this option isn't specified and that a SRS is +associated with the layer, a search is made in the spatial_ref_sys to find a match for the SRS, +and, if there is no match, a new entry is inserted for the SRS in the spatial_ref_sys table. +When the SRID option is specified, this search (and the eventual insertion of a new entry) will +not be done : the specified SRID is used as such.

    • + +
    • COMPRESS_COLUMNS=column_name1[,column_name2, ...]: (Starting with GDAL 1.10.0) +A list of (String) columns that must be compressed with ZLib DEFLATE algorithm. This might be beneficial +for databases that have big string blobs. However, use with care, since the value of such columns +will be seen as compressed binary content with other SQLite utilities (or previous OGR versions). +With OGR, when inserting, modifying or queryings compressed columns, compression/decompression is done transparently. +However, such columns cannot be (easily) queried with an attribute filter or WHERE clause. +Note: in table definition, such columns have the "VARCHAR_deflate" declaration type.

    • + +
    • FID=fid_name: (From GDAL 2.0) Name of the FID column to create. Defaults to OGC_FID.

    • + +
    + +

    Other Configuration Options

    +See other configure options here. +

    Performance hints

    +SQLite is a Transactional DBMS; while many INSERT statements are executed in close +sequence, BEGIN TRANSACTION and COMMIT TRANSACTION statements have to be invoked +appropriately in order to get optimal performance. +The default OGR behavior is to COMMIT a transaction every 200 inserted rows. This +value is surely too low for SQLite; and closing too much frequently the current +transaction causes severe performance degradation. +The -gt argument allows explicitly setting the number of rows for each transaction. +Explicitly defining -gt 1024 usually ensures a noticeable performance boost; +defining an even bigger -gt 65536 ensures optimal performance while +populating some table containing many hundredth thousand or million rows.

    + +SQLite usually has a very minimal memory foot-print; just about 20MB of RAM are +reserved to store the internal Page Cache [merely 2000 pages]. +This value too may well be inappropriate under many circumstances, most notably when +accessing some really huge DB-file containing many tables related to a corresponding +Spatial Index. +Explicitly setting a much more generously dimensioned internal Page Cache may often +help to get a noticeably better performance. +Starting since GDAL 1.9.0 you can explicitly set the internal Page Cache size using the +configuration option OGR_SQLITE_CACHE value [value being measured in MB]; +if your HW has enough available RAM, defining a Cache size as big as 512MB (or even 1024MB) +may sometimes help a lot in order to get better performance.

    + +Setting the OGR_SQLITE_SYNCHRONOUS configuration option to OFF might also +increase performance when creating SQLite databases (altough at the expense of integrity in case of +interruption/crash ).

    + +If many source files will be collected into the same Spatialite table, it +can be much faster to initialize the table without a spatial index by using +-lco SPATIAL_INDEX=NO and to create spatial index with a separate command +after all the data are appended. Spatial index can be created with ogrinfo +command +

    +ogr2ogr -f SQLite -dsco SPATIALITE=YES db.sqlite first.shp -nln the_table -lco SPATIAL_INDEX=NO
    +ogr2ogr -append db.sqlite second.shp -nln the_table
    +...
    +ogr2ogr -append db.sqlite last.shp -nln the_table
    +ogrinfo db.sqlite -sql "SELECT CreateSpatialIndex('the_table','GEOMETRY')"
    +
    +

    + +If a database has gone through editing operations, it might be useful to run a +VACUUM query to compact and optimize it. +

    ogrinfo db.sqlite -sql "VACUUM"
    +

    + +

    Credits

    + + +

    Links

    +
    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/makefile.vc new file mode 100644 index 000000000..4b5d5f27a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/makefile.vc @@ -0,0 +1,35 @@ + +OBJ = ogrsqlitedriver.obj ogrsqlitedatasource.obj \ + ogrsqlitelayer.obj ogrsqlitetablelayer.obj ogrsqliteviewlayer.obj \ + ogrsqliteselectlayer.obj ogrsqlitesinglefeaturelayer.obj \ + ogrsqlitevfs.obj ogrsqlitevirtualogr.obj ogrsqliteexecutesql.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. $(SQLITE_INC) $(PCRE_EXTRAFLAGS) $(SQLITE_HAS_COLUMN_METADATA_EXTRAFLAGS) $(SPATIALITE_412_OR_LATER_EXTRAFLAGS) + +!IFDEF PCRE_INC +PCRE_EXTRAFLAGS = $(PCRE_INC) +!ENDIF + +!IFDEF SQLITE_HAS_COLUMN_METADATA +SQLITE_HAS_COLUMN_METADATA_EXTRAFLAGS = -DSQLITE_HAS_COLUMN_METADATA +!ENDIF + +!IFDEF SPATIALITE_412_OR_LATER +SPATIALITE_412_OR_LATER_EXTRAFLAGS = -DSPATIALITE_412_OR_LATER +!ENDIF + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + -del *.exe + +test_load_virtual_ogr.exe: test_load_virtual_ogr.obj + $(CC) $(CFLAGS) test_load_virtual_ogr.obj $(SQLITE_LIB) /link $(LINKER_FLAGS) + if exist $@.manifest mt -manifest $@.manifest -outputresource:$@;1 + del test_load_virtual_ogr.obj diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogr_sqlite.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogr_sqlite.h new file mode 100644 index 000000000..8a2931aad --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogr_sqlite.h @@ -0,0 +1,849 @@ +/****************************************************************************** + * $Id: ogr_sqlite.h 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for OGR/SQLite driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2009-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SQLITE_H_INCLUDED +#define _OGR_SQLITE_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "gdal_pam.h" +#include "cpl_error.h" +#include +#include + +#ifdef HAVE_SPATIALITE + #ifdef SPATIALITE_AMALGAMATION + /* + / using an AMALGAMATED version of SpatiaLite + / a private internal copy of SQLite is included: + / so we are required including the SpatiaLite's + / own header + / + / IMPORTANT NOTICE: using AMALAGATION is only + / useful on Windows (to skip DLL hell related oddities) + */ + #include + #else + /* + / You MUST NOT use AMALGAMATION on Linux or any + / other "sane" operating system !!!! + */ + #include "sqlite3.h" + #endif +#else +#include "sqlite3.h" +#endif + +#if SQLITE_VERSION_NUMBER >= 3006000 +#define HAVE_SQLITE_VFS +#define HAVE_SQLITE3_PREPARE_V2 +#endif + +#define UNINITIALIZED_SRID -2 + +/************************************************************************/ +/* Format used to store geometry data in the database. */ +/************************************************************************/ + +enum OGRSQLiteGeomFormat +{ + OSGF_None = 0, + OSGF_WKT = 1, + OSGF_WKB = 2, + OSGF_FGF = 3, + OSGF_SpatiaLite = 4 +}; + +/************************************************************************/ +/* SpatiaLite's own Geometry type IDs. */ +/************************************************************************/ + +enum OGRSpatialiteGeomType +{ +// 2D [XY] + OGRSplitePointXY = 1, + OGRSpliteLineStringXY = 2, + OGRSplitePolygonXY = 3, + OGRSpliteMultiPointXY = 4, + OGRSpliteMultiLineStringXY = 5, + OGRSpliteMultiPolygonXY = 6, + OGRSpliteGeometryCollectionXY = 7, +// 3D [XYZ] + OGRSplitePointXYZ = 1001, + OGRSpliteLineStringXYZ = 1002, + OGRSplitePolygonXYZ = 1003, + OGRSpliteMultiPointXYZ = 1004, + OGRSpliteMultiLineStringXYZ = 1005, + OGRSpliteMultiPolygonXYZ = 1006, + OGRSpliteGeometryCollectionXYZ = 1007, +// 2D with Measure [XYM] + OGRSplitePointXYM = 2001, + OGRSpliteLineStringXYM = 2002, + OGRSplitePolygonXYM = 2003, + OGRSpliteMultiPointXYM = 2004, + OGRSpliteMultiLineStringXYM = 2005, + OGRSpliteMultiPolygonXYM = 2006, + OGRSpliteGeometryCollectionXYM = 2007, +// 3D with Measure [XYZM] + OGRSplitePointXYZM = 3001, + OGRSpliteLineStringXYZM = 3002, + OGRSplitePolygonXYZM = 3003, + OGRSpliteMultiPointXYZM = 3004, + OGRSpliteMultiLineStringXYZM = 3005, + OGRSpliteMultiPolygonXYZM = 3006, + OGRSpliteGeometryCollectionXYZM = 3007, +// COMPRESSED: 2D [XY] + OGRSpliteComprLineStringXY = 1000002, + OGRSpliteComprPolygonXY = 1000003, + OGRSpliteComprMultiPointXY = 1000004, + OGRSpliteComprMultiLineStringXY = 1000005, + OGRSpliteComprMultiPolygonXY = 1000006, + OGRSpliteComprGeometryCollectionXY = 1000007, +// COMPRESSED: 3D [XYZ] + OGRSpliteComprLineStringXYZ = 1001002, + OGRSpliteComprPolygonXYZ = 1001003, + OGRSpliteComprMultiPointXYZ = 1001004, + OGRSpliteComprMultiLineStringXYZ = 1001005, + OGRSpliteComprMultiPolygonXYZ = 1001006, + OGRSpliteComprGeometryCollectionXYZ = 1001007, +// COMPRESSED: 2D with Measure [XYM] + OGRSpliteComprLineStringXYM = 1002002, + OGRSpliteComprPolygonXYM = 1002003, + OGRSpliteComprMultiPointXYM = 1002004, + OGRSpliteComprMultiLineStringXYM = 1002005, + OGRSpliteComprMultiPolygonXYM = 1002006, + OGRSpliteComprGeometryCollectionXYM = 1002007, +// COMPRESSED: 3D with Measure [XYZM] + OGRSpliteComprLineStringXYZM = 1003002, + OGRSpliteComprPolygonXYZM = 1003003, + OGRSpliteComprMultiPointXYZM = 1003004, + OGRSpliteComprMultiLineStringXYZM = 1003005, + OGRSpliteComprMultiPolygonXYZM = 1003006, + OGRSpliteComprGeometryCollectionXYZM = 1003007 +}; + +/************************************************************************/ +/* OGRSQLiteGeomFieldDefn */ +/************************************************************************/ + +class OGRSQLiteGeomFieldDefn : public OGRGeomFieldDefn +{ + public: + OGRSQLiteGeomFieldDefn( const char* pszName, int iGeomColIn ) : + OGRGeomFieldDefn(pszName, wkbUnknown), nSRSId(-1), + iCol(iGeomColIn), bTriedAsSpatiaLite(FALSE), eGeomFormat(OSGF_None), + bHasM(FALSE), bCachedExtentIsValid(FALSE), bHasSpatialIndex(FALSE), + bHasCheckedSpatialIndexTable(FALSE) + { + } + + int nSRSId; + int iCol; /* ordinal of geometry field in SQL statement */ + int bTriedAsSpatiaLite; + OGRSQLiteGeomFormat eGeomFormat; + int bHasM; + OGREnvelope oCachedExtent; + int bCachedExtentIsValid; + int bHasSpatialIndex; + int bHasCheckedSpatialIndexTable; + std::vector< std::pair > aosDisabledTriggers; +}; + +/************************************************************************/ +/* OGRSQLiteFeatureDefn */ +/************************************************************************/ + +class OGRSQLiteFeatureDefn : public OGRFeatureDefn +{ + public: + OGRSQLiteFeatureDefn( const char * pszName = NULL ) : + OGRFeatureDefn(pszName) + { + SetGeomType(wkbNone); + } + + OGRSQLiteGeomFieldDefn* myGetGeomFieldDefn(int i) + { + return (OGRSQLiteGeomFieldDefn*) GetGeomFieldDefn(i); + } +}; + +/************************************************************************/ +/* OGRSQLiteLayer */ +/************************************************************************/ + +class OGRSQLiteDataSource; + +class IOGRSQLiteGetSpatialWhere +{ + public: + virtual int HasFastSpatialFilter(int iGeomCol) = 0; + virtual CPLString GetSpatialWhere(int iGeomCol, + OGRGeometry* poFilterGeom) = 0; +}; + +class OGRSQLiteLayer : public OGRLayer, public IOGRSQLiteGetSpatialWhere +{ + private: + static OGRErr createFromSpatialiteInternal(const GByte *pabyData, + OGRGeometry **ppoReturn, + int nBytes, + OGRwkbByteOrder eByteOrder, + int* pnBytesConsumed, + int nRecLevel); + + static int CanBeCompressedSpatialiteGeometry(const OGRGeometry *poGeometry); + + static int ComputeSpatiaLiteGeometrySize(const OGRGeometry *poGeometry, + int bHasM, int bSpatialite2D, + int bUseComprGeom ); + + static int GetSpatialiteGeometryCode(const OGRGeometry *poGeometry, + int bHasM, int bSpatialite2D, + int bUseComprGeom, + int bAcceptMultiGeom); + + static int ExportSpatiaLiteGeometryInternal(const OGRGeometry *poGeometry, + OGRwkbByteOrder eByteOrder, + int bHasM, int bSpatialite2D, + int bUseComprGeom, + GByte* pabyData ); + + protected: + OGRSQLiteFeatureDefn *poFeatureDefn; + + GIntBig iNextShapeId; + + sqlite3_stmt *hStmt; + int bDoStep; + + OGRSQLiteDataSource *poDS; + + char *pszFIDColumn; + + int *panFieldOrdinals; + int iFIDCol; + + int bIsVirtualShape; + + void BuildFeatureDefn( const char *pszLayerName, + sqlite3_stmt *hStmt, + const std::set& aosGeomCols, + const std::set& aosIgnoredCols); + + void ClearStatement(); + virtual OGRErr ResetStatement() = 0; + + int bUseComprGeom; + + char **papszCompressedColumns; + + int bAllowMultipleGeomFields; + + CPLString FormatSpatialFilterFromRTree(OGRGeometry* poFilterGeom, + const char* pszRowIDName, + const char* pszEscapedTable, + const char* pszEscapedGeomCol); + CPLString FormatSpatialFilterFromMBR(OGRGeometry* poFilterGeom, + const char* pszEscapedGeomColName); + + + public: + OGRSQLiteLayer(); + virtual ~OGRSQLiteLayer(); + + virtual void Finalize(); + + virtual void ResetReading(); + virtual OGRFeature *GetNextRawFeature(); + virtual OGRFeature *GetNextFeature(); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; } + virtual OGRSQLiteFeatureDefn *myGetLayerDefn() { return poFeatureDefn; } + + virtual const char *GetFIDColumn(); + + virtual int TestCapability( const char * ); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual void InvalidateCachedFeatureCountAndExtent() { } + + virtual int IsTableLayer() { return FALSE; } + + virtual int HasSpatialIndex(CPL_UNUSED int iGeomField) { return FALSE; } + + virtual int HasFastSpatialFilter(CPL_UNUSED int iGeomCol) { return FALSE; } + virtual CPLString GetSpatialWhere(CPL_UNUSED int iGeomCol, + CPL_UNUSED OGRGeometry* poFilterGeom) { return ""; } + + static OGRErr ImportSpatiaLiteGeometry( const GByte *, int, + OGRGeometry ** ); + static OGRErr ImportSpatiaLiteGeometry( const GByte *, int, + OGRGeometry **, int *pnSRID ); + static OGRErr ExportSpatiaLiteGeometry( const OGRGeometry *, + GInt32, OGRwkbByteOrder, + int, int, int bUseComprGeom, GByte **, int * ); + +}; + +/************************************************************************/ +/* OGRSQLiteTableLayer */ +/************************************************************************/ + +class OGRSQLiteTableLayer : public OGRSQLiteLayer +{ + int bLaunderColumnNames; + int bSpatialite2D; + + CPLString osWHERE; + CPLString osQuery; + int bDeferredSpatialIndexCreation; + + char *pszTableName; + char *pszEscapedTableName; + + int bLayerDefnError; + + sqlite3_stmt *hInsertStmt; + CPLString osLastInsertStmt; + + int bHasCheckedTriggers; + + void ClearInsertStmt(); + + void BuildWhere(void); + + virtual OGRErr ResetStatement(); + + OGRErr RecomputeOrdinals(); + + OGRErr AddColumnAncientMethod( OGRFieldDefn& oField); + void AddColumnDef(char* pszNewFieldList, + OGRFieldDefn* poFldDefn); + + void InitFieldListForRecrerate(char* & pszNewFieldList, + char* & pszFieldListForSelect, + int nExtraSpace = 0); + OGRErr RecreateTable(const char* pszFieldListForSelect, + const char* pszNewFieldList, + const char* pszGenericErrorMessage); + OGRErr BindValues( OGRFeature *poFeature, + sqlite3_stmt* hStmt, + int bBindNullValues ); + + int CheckSpatialIndexTable(int iGeomCol); + + CPLErr EstablishFeatureDefn(const char* pszGeomCol); + + int bStatisticsNeedsToBeFlushed; + GIntBig nFeatureCount; /* if -1, means not up-to-date */ + + void LoadStatistics(); + void LoadStatisticsSpatialite4DB(); + + CPLString FieldDefnToSQliteFieldDefn( OGRFieldDefn* poFieldDefn ); + + int bDeferredCreation; + OGRErr RunAddGeometryColumn( OGRSQLiteGeomFieldDefn *poGeomField, + int bAddColumnsForNonSpatialite ); + + char *pszCreationGeomFormat; + int iFIDAsRegularColumnIndex; + + public: + OGRSQLiteTableLayer( OGRSQLiteDataSource * ); + ~OGRSQLiteTableLayer(); + + CPLErr Initialize( const char *pszTableName, + int bIsVirtualShapeIn, + int bDeferredCreation); + void SetCreationParameters( const char *pszFIDColumnName, + OGRwkbGeometryType eGeomType, + const char *pszGeomFormat, + const char *pszGeometryName, + OGRSpatialReference *poSRS, + int nSRSId ); + virtual const char* GetName(); + + virtual GIntBig GetFeatureCount( int ); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce); + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce); + + virtual OGRFeatureDefn *GetLayerDefn(); + int HasLayerDefnError() { GetLayerDefn(); return bLayerDefnError; } + + virtual void SetSpatialFilter( OGRGeometry * ); + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + virtual OGRErr SetAttributeFilter( const char * ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr CreateGeomField( OGRGeomFieldDefn *poGeomFieldIn, + int bApproxOK = TRUE ); + virtual OGRErr DeleteField( int iField ); + virtual OGRErr ReorderFields( int* panMap ); + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ); + + virtual OGRFeature *GetNextFeature(); + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual int TestCapability( const char * ); + + virtual char **GetMetadata( const char * pszDomain = "" ); + virtual const char *GetMetadataItem( const char * pszName, + const char * pszDomain = "" ); + + // follow methods are not base class overrides + void SetLaunderFlag( int bFlag ) + { bLaunderColumnNames = bFlag; } + void SetUseCompressGeom( int bFlag ) + { bUseComprGeom = bFlag; } + void SetDeferredSpatialIndexCreation( int bFlag ) + { bDeferredSpatialIndexCreation = bFlag; } + void SetCompressedColumns( const char* pszCompressedColumns ); + + int CreateSpatialIndex(int iGeomCol); + + void CreateSpatialIndexIfNecessary(); + + void InitFeatureCount(); + int DoStatisticsNeedToBeFlushed(); + void ForceStatisticsToBeFlushed(); + int AreStatisticsValid(); + int SaveStatistics(); + + virtual void InvalidateCachedFeatureCountAndExtent(); + + virtual int IsTableLayer() { return TRUE; } + + virtual int HasSpatialIndex(int iGeomField); + virtual int HasFastSpatialFilter(int iGeomCol); + virtual CPLString GetSpatialWhere(int iGeomCol, + OGRGeometry* poFilterGeom); + + OGRErr RunDeferredCreationIfNecessary(); +}; + +/************************************************************************/ +/* OGRSQLiteViewLayer */ +/************************************************************************/ + +class OGRSQLiteViewLayer : public OGRSQLiteLayer +{ + CPLString osWHERE; + CPLString osQuery; + int bHasCheckedSpatialIndexTable; + + OGRSQLiteGeomFormat eGeomFormat; + CPLString osGeomColumn; + int bHasSpatialIndex; + + char *pszViewName; + char *pszEscapedTableName; + char *pszEscapedUnderlyingTableName; + + int bLayerDefnError; + + CPLString osUnderlyingTableName; + CPLString osUnderlyingGeometryColumn; + + OGRSQLiteLayer *poUnderlyingLayer; + OGRSQLiteLayer *GetUnderlyingLayer(); + + void BuildWhere(void); + + virtual OGRErr ResetStatement(); + + CPLErr EstablishFeatureDefn(); + + public: + OGRSQLiteViewLayer( OGRSQLiteDataSource * ); + ~OGRSQLiteViewLayer(); + + virtual const char* GetName() { return pszViewName; } + virtual OGRwkbGeometryType GetGeomType(); + + CPLErr Initialize( const char *pszViewName, + const char *pszViewGeometry, + const char *pszViewRowid, + const char *pszTableName, + const char *pszGeometryColumn); + + virtual OGRFeatureDefn *GetLayerDefn(); + int HasLayerDefnError() { GetLayerDefn(); return bLayerDefnError; } + + virtual OGRFeature *GetNextFeature(); + virtual GIntBig GetFeatureCount( int ); + + virtual void SetSpatialFilter( OGRGeometry * ); + virtual OGRErr SetAttributeFilter( const char * ); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual int TestCapability( const char * ); + + virtual int HasSpatialIndex(CPL_UNUSED int iGeomField) { return bHasSpatialIndex; } + virtual CPLString GetSpatialWhere(int iGeomCol, + OGRGeometry* poFilterGeom); +}; + +/************************************************************************/ +/* IOGRSQLiteSelectLayer */ +/************************************************************************/ + +class IOGRSQLiteSelectLayer +{ + public: + virtual char*& GetAttrQueryString() = 0; + virtual OGRFeatureQuery*& GetFeatureQuery() = 0; + virtual OGRGeometry*& GetFilterGeom() = 0; + virtual int& GetIGeomFieldFilter() = 0; + virtual OGRSpatialReference* GetSpatialRef() = 0; + virtual OGRFeatureDefn *GetLayerDefn() = 0; + virtual int InstallFilter( OGRGeometry * ) = 0; + virtual int HasReadFeature() = 0; + virtual void BaseResetReading() = 0; + virtual OGRFeature *BaseGetNextFeature() = 0; + virtual OGRErr BaseSetAttributeFilter(const char* pszQuery) = 0; + virtual GIntBig BaseGetFeatureCount(int bForce) = 0; + virtual int BaseTestCapability( const char * ) = 0; + virtual OGRErr BaseGetExtent(OGREnvelope *psExtent, int bForce) = 0; + virtual OGRErr BaseGetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) = 0; +}; + +/************************************************************************/ +/* OGRSQLiteSelectLayerCommonBehaviour */ +/************************************************************************/ + +class OGRSQLiteBaseDataSource; +class OGRSQLiteSelectLayerCommonBehaviour +{ + OGRSQLiteBaseDataSource *poDS; + IOGRSQLiteSelectLayer *poLayer; + + CPLString osSQLBase; + + int bEmptyLayer; + int bAllowResetReadingEvenIfIndexAtZero; + int bSpatialFilterInSQL; + + std::pair GetBaseLayer(size_t& i); + int BuildSQL(); + + public: + + CPLString osSQLCurrent; + + OGRSQLiteSelectLayerCommonBehaviour(OGRSQLiteBaseDataSource* poDS, + IOGRSQLiteSelectLayer* poBaseLayer, + CPLString osSQL, + int bEmptyLayer); + + void ResetReading(); + OGRFeature *GetNextFeature(); + GIntBig GetFeatureCount( int ); + void SetSpatialFilter( int iGeomField, OGRGeometry * ); + OGRErr SetAttributeFilter( const char * ); + int TestCapability( const char * ); + OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce); +}; + +/************************************************************************/ +/* OGRSQLiteSelectLayer */ +/************************************************************************/ + +class OGRSQLiteSelectLayer : public OGRSQLiteLayer, public IOGRSQLiteSelectLayer +{ + OGRSQLiteSelectLayerCommonBehaviour* poBehaviour; + + virtual OGRErr ResetStatement(); + + public: + OGRSQLiteSelectLayer( OGRSQLiteDataSource *, + CPLString osSQL, + sqlite3_stmt *, + int bUseStatementForGetNextFeature, + int bEmptyLayer, + int bAllowMultipleGeomFields ); + ~OGRSQLiteSelectLayer(); + + virtual void ResetReading(); + + virtual OGRFeature *GetNextFeature(); + virtual GIntBig GetFeatureCount( int ); + + virtual void SetSpatialFilter( OGRGeometry * poGeom ) { SetSpatialFilter(0, poGeom); } + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * ); + virtual OGRErr SetAttributeFilter( const char * ); + + virtual int TestCapability( const char * ); + + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE) { return GetExtent(0, psExtent, bForce); } + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce = TRUE); + + virtual OGRFeatureDefn * GetLayerDefn() { return OGRSQLiteLayer::GetLayerDefn(); } + virtual char*& GetAttrQueryString() { return m_pszAttrQueryString; } + virtual OGRFeatureQuery*& GetFeatureQuery() { return m_poAttrQuery; } + virtual OGRGeometry*& GetFilterGeom() { return m_poFilterGeom; } + virtual int& GetIGeomFieldFilter() { return m_iGeomFieldFilter; } + virtual OGRSpatialReference* GetSpatialRef() { return OGRSQLiteLayer::GetSpatialRef(); } + virtual int InstallFilter( OGRGeometry * poGeomIn ) { return OGRSQLiteLayer::InstallFilter(poGeomIn); } + virtual int HasReadFeature() { return iNextShapeId > 0; } + virtual void BaseResetReading() { OGRSQLiteLayer::ResetReading(); } + virtual OGRFeature *BaseGetNextFeature() { return OGRSQLiteLayer::GetNextFeature(); } + virtual OGRErr BaseSetAttributeFilter(const char* pszQuery) { return OGRSQLiteLayer::SetAttributeFilter(pszQuery); } + virtual GIntBig BaseGetFeatureCount(int bForce) { return OGRSQLiteLayer::GetFeatureCount(bForce); } + virtual int BaseTestCapability( const char *pszCap ) { return OGRSQLiteLayer::TestCapability(pszCap); } + virtual OGRErr BaseGetExtent(OGREnvelope *psExtent, int bForce) { return OGRSQLiteLayer::GetExtent(psExtent, bForce); } + virtual OGRErr BaseGetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) { return OGRSQLiteLayer::GetExtent(iGeomField, psExtent, bForce); } +}; + +/************************************************************************/ +/* OGRSQLiteSingleFeatureLayer */ +/************************************************************************/ + +class OGRSQLiteSingleFeatureLayer : public OGRLayer +{ + private: + int nVal; + char *pszVal; + OGRFeatureDefn *poFeatureDefn; + int iNextShapeId; + + public: + OGRSQLiteSingleFeatureLayer( const char* pszLayerName, + int nVal ); + OGRSQLiteSingleFeatureLayer( const char* pszLayerName, + const char *pszVal ); + ~OGRSQLiteSingleFeatureLayer(); + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + virtual OGRFeatureDefn *GetLayerDefn(); + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRSQLiteBaseDataSource */ +/************************************************************************/ + +/* Used by both OGRSQLiteDataSource and OGRGeoPackageDataSource */ +class OGRSQLiteBaseDataSource : public GDALPamDataset +{ + protected: + char *m_pszFilename; + + sqlite3 *hDB; + int bUpdate; + +#ifdef HAVE_SQLITE_VFS + sqlite3_vfs* pMyVFS; +#endif + + VSILFILE* fpMainFile; /* Set by the VFS layer when it opens the DB */ + /* Must *NOT* be closed by the datasource explicitly. */ + + int OpenOrCreateDB(int flags, int bRegisterOGR2SQLiteExtensions); + int SetSynchronous(); + int SetCacheSize(); + + void CloseDB(); + + std::map oMapSQLEnvelope; + +#ifdef SPATIALITE_412_OR_LATER + void *hSpatialiteCtxt; + int InitNewSpatialite(); + void FinishNewSpatialite(); +#endif + + int bUserTransactionActive; + int nSoftTransactionLevel; + + OGRErr DoTransactionCommand(const char* pszCommand); + + public: + OGRSQLiteBaseDataSource(); + ~OGRSQLiteBaseDataSource(); + + sqlite3 *GetDB() { return hDB; } + int GetUpdate() const { return bUpdate; } + + void NotifyFileOpened (const char* pszFilename, + VSILFILE* fp); + + const OGREnvelope* GetEnvelopeFromSQL(const CPLString& osSQL); + void SetEnvelopeForSQL(const CPLString& osSQL, const OGREnvelope& oEnvelope); + + virtual std::pair GetLayerWithGetSpatialWhereByName( const char* pszName ) = 0; + + virtual OGRErr StartTransaction(int bForce = FALSE); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual int TestCapability( const char * ); + + OGRErr SoftStartTransaction(); + OGRErr SoftCommitTransaction(); + OGRErr SoftRollbackTransaction(); +}; + +/************************************************************************/ +/* OGRSQLiteDataSource */ +/************************************************************************/ + +class OGRSQLiteDataSource : public OGRSQLiteBaseDataSource +{ + OGRSQLiteLayer **papoLayers; + int nLayers; + + // We maintain a list of known SRID to reduce the number of trips to + // the database to get SRSes. + int nKnownSRID; + int *panSRID; + OGRSpatialReference **papoSRS; + + char **papszOpenOptions; + + void AddSRIDToCache(int nId, OGRSpatialReference * poSRS ); + + int bHaveGeometryColumns; + int bIsSpatiaLiteDB; + int bSpatialite4Layout; + + int nUndefinedSRID; + + virtual void DeleteLayer( const char *pszLayer ); + + const char* GetSRTEXTColName(); + + int InitWithEPSG(); + + int OpenVirtualTable(const char* pszName, const char* pszSQL); + + GIntBig nFileTimestamp; + int bLastSQLCommandIsUpdateLayerStatistics; + + std::map< CPLString, std::set > aoMapTableToSetOfGeomCols; + + void SaveStatistics(); + + public: + OGRSQLiteDataSource(); + ~OGRSQLiteDataSource(); + + int Open( const char *, int bUpdateIn, char** papszOpenOptions ); + int Create( const char *, char **papszOptions ); + + int OpenTable( const char *pszTableName, + int bIsVirtualShapeIn = FALSE ); + int OpenView( const char *pszViewName, + const char *pszViewGeometry, + const char *pszViewRowid, + const char *pszTableName, + const char *pszGeometryColumn); + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer *GetLayer( int ); + virtual OGRLayer *GetLayerByName( const char* ); + virtual std::pair GetLayerWithGetSpatialWhereByName( const char* pszName ); + + virtual OGRLayer *ICreateLayer( const char *pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char **papszOptions ); + virtual OGRErr DeleteLayer(int); + + virtual int TestCapability( const char * ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poLayer ); + + virtual void FlushCache(); + + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + char *LaunderName( const char * ); + int FetchSRSId( OGRSpatialReference * poSRS ); + OGRSpatialReference*FetchSRS( int nSRID ); + + void SetUpdate(int bUpdateIn) { bUpdate = bUpdateIn; } + + void SetName(const char* pszNameIn); + + const std::set& GetGeomColsForTable(const char* pszTableName) + { return aoMapTableToSetOfGeomCols[pszTableName]; } + + GIntBig GetFileTimestamp() const { return nFileTimestamp; } + + int IsSpatialiteLoaded(); + int GetSpatialiteVersionNumber(); + + int IsSpatialiteDB() const { return bIsSpatiaLiteDB; } + int HasSpatialite4Layout() const { return bSpatialite4Layout; } + + int GetUndefinedSRID() const { return nUndefinedSRID; } + int HasGeometryColumns() const { return bHaveGeometryColumns; } + + void ReloadLayers(); +}; + +/* To escape literals. The returned string doesn't contain the surrounding single quotes */ +CPLString OGRSQLiteEscape( const char *pszLiteral ); + +/* To escape table or field names. The returned string doesn't contain the surrounding double quotes */ +CPLString OGRSQLiteEscapeName( const char* pszName ); + +CPLString OGRSQLiteParamsUnquote(const char* pszVal); + +CPLString OGRSQLiteFieldDefnToSQliteFieldDefn( OGRFieldDefn* poFieldDefn, + int bSQLiteDialectInternalUse ); + +int OGRSQLITEStringToDateTimeField( OGRFeature* poFeature, int iField, + const char* pszValue ); + +#ifdef HAVE_SQLITE_VFS +typedef void (*pfnNotifyFileOpenedType)(void* pfnUserData, const char* pszFilename, VSILFILE* fp); +sqlite3_vfs* OGRSQLiteCreateVFS(pfnNotifyFileOpenedType pfn, void* pfnUserData); +#endif + +void OGRSQLiteRegisterInflateDeflate(sqlite3* hDB); + +#endif /* ndef _OGR_SQLITE_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlite3ext.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlite3ext.h new file mode 100644 index 000000000..3e7a40caf --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlite3ext.h @@ -0,0 +1,676 @@ +/****************************************************************************** + * $Id: ogrsqlite3ext.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Custom version of sqlite3ext.h to workaround issues with Spatialite amalgamation + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* Customized version of sqlite3ext.h to fix issues with */ +/* Spatialite amalgamation sqlite3ext.h that defines */ +/* sqlite3_macros that contradict the ones that should be defined */ +/* by sqlite3ext.h !!! This is messy, but no other choice, */ +/* except not using the sqlite3ext.h from spatialite amalgamation */ + +#undef sqlite3_aggregate_context + +#undef sqlite3_aggregate_count + +#undef sqlite3_bind_blob +#undef sqlite3_bind_double +#undef sqlite3_bind_int +#undef sqlite3_bind_int64 +#undef sqlite3_bind_null +#undef sqlite3_bind_parameter_count +#undef sqlite3_bind_parameter_index +#undef sqlite3_bind_parameter_name +#undef sqlite3_bind_text +#undef sqlite3_bind_text16 +#undef sqlite3_bind_value +#undef sqlite3_busy_handler +#undef sqlite3_busy_timeout +#undef sqlite3_changes +#undef sqlite3_close +#undef sqlite3_collation_needed +#undef sqlite3_collation_needed16 +#undef sqlite3_column_blob +#undef sqlite3_column_bytes +#undef sqlite3_column_bytes16 +#undef sqlite3_column_count +#undef sqlite3_column_database_name +#undef sqlite3_column_database_name16 +#undef sqlite3_column_decltype +#undef sqlite3_column_decltype16 +#undef sqlite3_column_double +#undef sqlite3_column_int +#undef sqlite3_column_int64 +#undef sqlite3_column_name +#undef sqlite3_column_name16 +#undef sqlite3_column_origin_name +#undef sqlite3_column_origin_name16 +#undef sqlite3_column_table_name +#undef sqlite3_column_table_name16 +#undef sqlite3_column_text +#undef sqlite3_column_text16 +#undef sqlite3_column_type +#undef sqlite3_column_value +#undef sqlite3_commit_hook +#undef sqlite3_complete +#undef sqlite3_complete16 +#undef sqlite3_create_collation +#undef sqlite3_create_collation16 +#undef sqlite3_create_function +#undef sqlite3_create_function16 +#undef sqlite3_create_module +#undef sqlite3_create_module_v2 +#undef sqlite3_data_count +#undef sqlite3_db_handle +#undef sqlite3_declare_vtab +#undef sqlite3_enable_shared_cache +#undef sqlite3_errcode +#undef sqlite3_errmsg +#undef sqlite3_errmsg16 +#undef sqlite3_exec + +#undef sqlite3_expired + +#undef sqlite3_finalize +#undef sqlite3_free +#undef sqlite3_free_table +#undef sqlite3_get_autocommit +#undef sqlite3_get_auxdata +#undef sqlite3_get_table + +#undef sqlite3_global_recover + +#undef sqlite3_interrupt +#undef sqlite3_last_insert_rowid +#undef sqlite3_libversion +#undef sqlite3_libversion_number +#undef sqlite3_malloc +#undef sqlite3_mprintf +#undef sqlite3_open +#undef sqlite3_open16 +#undef sqlite3_prepare +#undef sqlite3_prepare16 +#undef sqlite3_prepare_v2 +#undef sqlite3_prepare16_v2 +#undef sqlite3_profile +#undef sqlite3_progress_handler +#undef sqlite3_realloc +#undef sqlite3_reset +#undef sqlite3_result_blob +#undef sqlite3_result_double +#undef sqlite3_result_error +#undef sqlite3_result_error16 +#undef sqlite3_result_int +#undef sqlite3_result_int64 +#undef sqlite3_result_null +#undef sqlite3_result_text +#undef sqlite3_result_text16 +#undef sqlite3_result_text16be +#undef sqlite3_result_text16le +#undef sqlite3_result_value +#undef sqlite3_rollback_hook +#undef sqlite3_set_authorizer +#undef sqlite3_set_auxdata +#undef sqlite3_snprintf +#undef sqlite3_step +#undef sqlite3_table_column_metadata +#undef sqlite3_thread_cleanup +#undef sqlite3_total_changes +#undef sqlite3_trace + +#undef sqlite3_transfer_bindings + +#undef sqlite3_update_hook +#undef sqlite3_user_data +#undef sqlite3_value_blob +#undef sqlite3_value_bytes +#undef sqlite3_value_bytes16 +#undef sqlite3_value_double +#undef sqlite3_value_int +#undef sqlite3_value_int64 +#undef sqlite3_value_numeric_type +#undef sqlite3_value_text +#undef sqlite3_value_text16 +#undef sqlite3_value_text16be +#undef sqlite3_value_text16le +#undef sqlite3_value_type +#undef sqlite3_vmprintf +#undef sqlite3_overload_function +#undef sqlite3_prepare_v2 +#undef sqlite3_prepare16_v2 +#undef sqlite3_clear_bindings +#undef sqlite3_bind_zeroblob +#undef sqlite3_blob_bytes +#undef sqlite3_blob_close +#undef sqlite3_blob_open +#undef sqlite3_blob_read +#undef sqlite3_blob_write +#undef sqlite3_create_collation_v2 +#undef sqlite3_file_control +#undef sqlite3_memory_highwater +#undef sqlite3_memory_used +#undef sqlite3_mutex_alloc +#undef sqlite3_mutex_enter +#undef sqlite3_mutex_free +#undef sqlite3_mutex_leave +#undef sqlite3_mutex_try +#undef sqlite3_open_v2 +#undef sqlite3_release_memory +#undef sqlite3_result_error_nomem +#undef sqlite3_result_error_toobig +#undef sqlite3_sleep +#undef sqlite3_soft_heap_limit +#undef sqlite3_vfs_find +#undef sqlite3_vfs_register +#undef sqlite3_vfs_unregister +#undef sqlite3_threadsafe +#undef sqlite3_result_zeroblob +#undef sqlite3_result_error_code +#undef sqlite3_test_control +#undef sqlite3_randomness +#undef sqlite3_context_db_handle +#undef sqlite3_extended_result_codes +#undef sqlite3_limit +#undef sqlite3_next_stmt +#undef sqlite3_sql +#undef sqlite3_status +#undef sqlite3_backup_finish +#undef sqlite3_backup_init +#undef sqlite3_backup_pagecount +#undef sqlite3_backup_remaining +#undef sqlite3_backup_step +#undef sqlite3_compileoption_get +#undef sqlite3_compileoption_used +#undef sqlite3_create_function_v2 +#undef sqlite3_db_config +#undef sqlite3_db_mutex +#undef sqlite3_db_status +#undef sqlite3_extended_errcode +#undef sqlite3_log +#undef sqlite3_soft_heap_limit64 +#undef sqlite3_sourceid +#undef sqlite3_stmt_status +#undef sqlite3_strnicmp +#undef sqlite3_unlock_notify +#undef sqlite3_wal_autocheckpoint +#undef sqlite3_wal_checkpoint +#undef sqlite3_wal_hook +#undef sqlite3_blob_reopen +#undef sqlite3_vtab_config +#undef sqlite3_vtab_on_conflict + +typedef struct sqlite3_backup ogr_sqlite3_backup; + +/* +** 2006 June 7 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This header file defines the SQLite interface for use by +** shared libraries that want to be imported as extensions into +** an SQLite instance. Shared libraries that intend to be loaded +** as extensions by SQLite should #include this file instead of +** sqlite3.h. +*/ +#ifndef _SQLITE3EXT_H_ +#define _SQLITE3EXT_H_ + +// Commented out to avoid reimporting the #define sqlite3_xxx macros +//#include "sqlite3.h" + +typedef struct sqlite3_api_routines sqlite3_api_routines; + +/* +** The following structure holds pointers to all of the SQLite API +** routines. +** +** WARNING: In order to maintain backwards compatibility, add new +** interfaces to the end of this structure only. If you insert new +** interfaces in the middle of this structure, then older different +** versions of SQLite will not be able to load each others' shared +** libraries! +*/ +struct sqlite3_api_routines { + void * (*aggregate_context)(sqlite3_context*,int nBytes); + int (*aggregate_count)(sqlite3_context*); + int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*)); + int (*bind_double)(sqlite3_stmt*,int,double); + int (*bind_int)(sqlite3_stmt*,int,int); + int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64); + int (*bind_null)(sqlite3_stmt*,int); + int (*bind_parameter_count)(sqlite3_stmt*); + int (*bind_parameter_index)(sqlite3_stmt*,const char*zName); + const char * (*bind_parameter_name)(sqlite3_stmt*,int); + int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*)); + int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*)); + int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*); + int (*busy_handler)(sqlite3*,int(*)(void*,int),void*); + int (*busy_timeout)(sqlite3*,int ms); + int (*changes)(sqlite3*); + int (*close)(sqlite3*); + int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*, + int eTextRep,const char*)); + int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*, + int eTextRep,const void*)); + const void * (*column_blob)(sqlite3_stmt*,int iCol); + int (*column_bytes)(sqlite3_stmt*,int iCol); + int (*column_bytes16)(sqlite3_stmt*,int iCol); + int (*column_count)(sqlite3_stmt*pStmt); + const char * (*column_database_name)(sqlite3_stmt*,int); + const void * (*column_database_name16)(sqlite3_stmt*,int); + const char * (*column_decltype)(sqlite3_stmt*,int i); + const void * (*column_decltype16)(sqlite3_stmt*,int); + double (*column_double)(sqlite3_stmt*,int iCol); + int (*column_int)(sqlite3_stmt*,int iCol); + sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol); + const char * (*column_name)(sqlite3_stmt*,int); + const void * (*column_name16)(sqlite3_stmt*,int); + const char * (*column_origin_name)(sqlite3_stmt*,int); + const void * (*column_origin_name16)(sqlite3_stmt*,int); + const char * (*column_table_name)(sqlite3_stmt*,int); + const void * (*column_table_name16)(sqlite3_stmt*,int); + const unsigned char * (*column_text)(sqlite3_stmt*,int iCol); + const void * (*column_text16)(sqlite3_stmt*,int iCol); + int (*column_type)(sqlite3_stmt*,int iCol); + sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol); + void * (*commit_hook)(sqlite3*,int(*)(void*),void*); + int (*complete)(const char*sql); + int (*complete16)(const void*sql); + int (*create_collation)(sqlite3*,const char*,int,void*, + int(*)(void*,int,const void*,int,const void*)); + int (*create_collation16)(sqlite3*,const void*,int,void*, + int(*)(void*,int,const void*,int,const void*)); + int (*create_function)(sqlite3*,const char*,int,int,void*, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*)); + int (*create_function16)(sqlite3*,const void*,int,int,void*, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*)); + int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*); + int (*data_count)(sqlite3_stmt*pStmt); + sqlite3 * (*db_handle)(sqlite3_stmt*); + int (*declare_vtab)(sqlite3*,const char*); + int (*enable_shared_cache)(int); + int (*errcode)(sqlite3*db); + const char * (*errmsg)(sqlite3*); + const void * (*errmsg16)(sqlite3*); + int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**); + int (*expired)(sqlite3_stmt*); + int (*finalize)(sqlite3_stmt*pStmt); + void (*free)(void*); + void (*free_table)(char**result); + int (*get_autocommit)(sqlite3*); + void * (*get_auxdata)(sqlite3_context*,int); + int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**); + int (*global_recover)(void); + void (*interruptx)(sqlite3*); + sqlite_int64 (*last_insert_rowid)(sqlite3*); + const char * (*libversion)(void); + int (*libversion_number)(void); + void *(*malloc)(int); + char * (*mprintf)(const char*,...); + int (*open)(const char*,sqlite3**); + int (*open16)(const void*,sqlite3**); + int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); + int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); + void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*); + void (*progress_handler)(sqlite3*,int,int(*)(void*),void*); + void *(*realloc)(void*,int); + int (*reset)(sqlite3_stmt*pStmt); + void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*)); + void (*result_double)(sqlite3_context*,double); + void (*result_error)(sqlite3_context*,const char*,int); + void (*result_error16)(sqlite3_context*,const void*,int); + void (*result_int)(sqlite3_context*,int); + void (*result_int64)(sqlite3_context*,sqlite_int64); + void (*result_null)(sqlite3_context*); + void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*)); + void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*)); + void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*)); + void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*)); + void (*result_value)(sqlite3_context*,sqlite3_value*); + void * (*rollback_hook)(sqlite3*,void(*)(void*),void*); + int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*, + const char*,const char*),void*); + void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*)); + char * (*snprintf)(int,char*,const char*,...); + int (*step)(sqlite3_stmt*); + int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*, + char const**,char const**,int*,int*,int*); + void (*thread_cleanup)(void); + int (*total_changes)(sqlite3*); + void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*); + int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*); + void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*, + sqlite_int64),void*); + void * (*user_data)(sqlite3_context*); + const void * (*value_blob)(sqlite3_value*); + int (*value_bytes)(sqlite3_value*); + int (*value_bytes16)(sqlite3_value*); + double (*value_double)(sqlite3_value*); + int (*value_int)(sqlite3_value*); + sqlite_int64 (*value_int64)(sqlite3_value*); + int (*value_numeric_type)(sqlite3_value*); + const unsigned char * (*value_text)(sqlite3_value*); + const void * (*value_text16)(sqlite3_value*); + const void * (*value_text16be)(sqlite3_value*); + const void * (*value_text16le)(sqlite3_value*); + int (*value_type)(sqlite3_value*); + char *(*vmprintf)(const char*,va_list); + /* Added ??? */ + int (*overload_function)(sqlite3*, const char *zFuncName, int nArg); + /* Added by 3.3.13 */ + int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**); + int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**); + int (*clear_bindings)(sqlite3_stmt*); + /* Added by 3.4.1 */ + int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*, + void (*xDestroy)(void *)); + /* Added by 3.5.0 */ + int (*bind_zeroblob)(sqlite3_stmt*,int,int); + int (*blob_bytes)(sqlite3_blob*); + int (*blob_close)(sqlite3_blob*); + int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64, + int,sqlite3_blob**); + int (*blob_read)(sqlite3_blob*,void*,int,int); + int (*blob_write)(sqlite3_blob*,const void*,int,int); + int (*create_collation_v2)(sqlite3*,const char*,int,void*, + int(*)(void*,int,const void*,int,const void*), + void(*)(void*)); + int (*file_control)(sqlite3*,const char*,int,void*); + sqlite3_int64 (*memory_highwater)(int); + sqlite3_int64 (*memory_used)(void); + sqlite3_mutex *(*mutex_alloc)(int); + void (*mutex_enter)(sqlite3_mutex*); + void (*mutex_free)(sqlite3_mutex*); + void (*mutex_leave)(sqlite3_mutex*); + int (*mutex_try)(sqlite3_mutex*); + int (*open_v2)(const char*,sqlite3**,int,const char*); + int (*release_memory)(int); + void (*result_error_nomem)(sqlite3_context*); + void (*result_error_toobig)(sqlite3_context*); + int (*sleep)(int); + void (*soft_heap_limit)(int); + sqlite3_vfs *(*vfs_find)(const char*); + int (*vfs_register)(sqlite3_vfs*,int); + int (*vfs_unregister)(sqlite3_vfs*); + int (*xthreadsafe)(void); + void (*result_zeroblob)(sqlite3_context*,int); + void (*result_error_code)(sqlite3_context*,int); + int (*test_control)(int, ...); + void (*randomness)(int,void*); + sqlite3 *(*context_db_handle)(sqlite3_context*); + int (*extended_result_codes)(sqlite3*,int); + int (*limit)(sqlite3*,int,int); + sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*); + const char *(*sql)(sqlite3_stmt*); + int (*status)(int,int*,int*,int); + int (*backup_finish)(ogr_sqlite3_backup*); + ogr_sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*); + int (*backup_pagecount)(ogr_sqlite3_backup*); + int (*backup_remaining)(ogr_sqlite3_backup*); + int (*backup_step)(ogr_sqlite3_backup*,int); + const char *(*compileoption_get)(int); + int (*compileoption_used)(const char*); + int (*create_function_v2)(sqlite3*,const char*,int,int,void*, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*), + void(*xDestroy)(void*)); + int (*db_config)(sqlite3*,int,...); + sqlite3_mutex *(*db_mutex)(sqlite3*); + int (*db_status)(sqlite3*,int,int*,int*,int); + int (*extended_errcode)(sqlite3*); + void (*log)(int,const char*,...); + sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64); + const char *(*sourceid)(void); + int (*stmt_status)(sqlite3_stmt*,int,int); + int (*strnicmp)(const char*,const char*,int); + int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*); + int (*wal_autocheckpoint)(sqlite3*,int); + int (*wal_checkpoint)(sqlite3*,const char*); + void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*); + int (*blob_reopen)(sqlite3_blob*,sqlite3_int64); + int (*vtab_config)(sqlite3*,int op,...); + int (*vtab_on_conflict)(sqlite3*); +}; + +/* +** The following macros redefine the API routines so that they are +** redirected throught the global sqlite3_api structure. +** +** This header file is also used by the loadext.c source file +** (part of the main SQLite library - not an extension) so that +** it can get access to the sqlite3_api_routines structure +** definition. But the main library does not want to redefine +** the API. So the redefinition macros are only valid if the +** SQLITE_CORE macros is undefined. +*/ +#ifndef SQLITE_CORE +#define sqlite3_aggregate_context sqlite3_api->aggregate_context +#ifndef SQLITE_OMIT_DEPRECATED +#define sqlite3_aggregate_count sqlite3_api->aggregate_count +#endif +#define sqlite3_bind_blob sqlite3_api->bind_blob +#define sqlite3_bind_double sqlite3_api->bind_double +#define sqlite3_bind_int sqlite3_api->bind_int +#define sqlite3_bind_int64 sqlite3_api->bind_int64 +#define sqlite3_bind_null sqlite3_api->bind_null +#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count +#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index +#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name +#define sqlite3_bind_text sqlite3_api->bind_text +#define sqlite3_bind_text16 sqlite3_api->bind_text16 +#define sqlite3_bind_value sqlite3_api->bind_value +#define sqlite3_busy_handler sqlite3_api->busy_handler +#define sqlite3_busy_timeout sqlite3_api->busy_timeout +#define sqlite3_changes sqlite3_api->changes +#define sqlite3_close sqlite3_api->close +#define sqlite3_collation_needed sqlite3_api->collation_needed +#define sqlite3_collation_needed16 sqlite3_api->collation_needed16 +#define sqlite3_column_blob sqlite3_api->column_blob +#define sqlite3_column_bytes sqlite3_api->column_bytes +#define sqlite3_column_bytes16 sqlite3_api->column_bytes16 +#define sqlite3_column_count sqlite3_api->column_count +#define sqlite3_column_database_name sqlite3_api->column_database_name +#define sqlite3_column_database_name16 sqlite3_api->column_database_name16 +#define sqlite3_column_decltype sqlite3_api->column_decltype +#define sqlite3_column_decltype16 sqlite3_api->column_decltype16 +#define sqlite3_column_double sqlite3_api->column_double +#define sqlite3_column_int sqlite3_api->column_int +#define sqlite3_column_int64 sqlite3_api->column_int64 +#define sqlite3_column_name sqlite3_api->column_name +#define sqlite3_column_name16 sqlite3_api->column_name16 +#define sqlite3_column_origin_name sqlite3_api->column_origin_name +#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16 +#define sqlite3_column_table_name sqlite3_api->column_table_name +#define sqlite3_column_table_name16 sqlite3_api->column_table_name16 +#define sqlite3_column_text sqlite3_api->column_text +#define sqlite3_column_text16 sqlite3_api->column_text16 +#define sqlite3_column_type sqlite3_api->column_type +#define sqlite3_column_value sqlite3_api->column_value +#define sqlite3_commit_hook sqlite3_api->commit_hook +#define sqlite3_complete sqlite3_api->complete +#define sqlite3_complete16 sqlite3_api->complete16 +#define sqlite3_create_collation sqlite3_api->create_collation +#define sqlite3_create_collation16 sqlite3_api->create_collation16 +#define sqlite3_create_function sqlite3_api->create_function +#define sqlite3_create_function16 sqlite3_api->create_function16 +#define sqlite3_create_module sqlite3_api->create_module +#define sqlite3_create_module_v2 sqlite3_api->create_module_v2 +#define sqlite3_data_count sqlite3_api->data_count +#define sqlite3_db_handle sqlite3_api->db_handle +#define sqlite3_declare_vtab sqlite3_api->declare_vtab +#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache +#define sqlite3_errcode sqlite3_api->errcode +#define sqlite3_errmsg sqlite3_api->errmsg +#define sqlite3_errmsg16 sqlite3_api->errmsg16 +#define sqlite3_exec sqlite3_api->exec +#ifndef SQLITE_OMIT_DEPRECATED +#define sqlite3_expired sqlite3_api->expired +#endif +#define sqlite3_finalize sqlite3_api->finalize +#define sqlite3_free sqlite3_api->free +#define sqlite3_free_table sqlite3_api->free_table +#define sqlite3_get_autocommit sqlite3_api->get_autocommit +#define sqlite3_get_auxdata sqlite3_api->get_auxdata +#define sqlite3_get_table sqlite3_api->get_table +#ifndef SQLITE_OMIT_DEPRECATED +#define sqlite3_global_recover sqlite3_api->global_recover +#endif +#define sqlite3_interrupt sqlite3_api->interruptx +#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid +#define sqlite3_libversion sqlite3_api->libversion +#define sqlite3_libversion_number sqlite3_api->libversion_number +#define sqlite3_malloc sqlite3_api->malloc +#define sqlite3_mprintf sqlite3_api->mprintf +#define sqlite3_open sqlite3_api->open +#define sqlite3_open16 sqlite3_api->open16 +#define sqlite3_prepare sqlite3_api->prepare +#define sqlite3_prepare16 sqlite3_api->prepare16 +#define sqlite3_prepare_v2 sqlite3_api->prepare_v2 +#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 +#define sqlite3_profile sqlite3_api->profile +#define sqlite3_progress_handler sqlite3_api->progress_handler +#define sqlite3_realloc sqlite3_api->realloc +#define sqlite3_reset sqlite3_api->reset +#define sqlite3_result_blob sqlite3_api->result_blob +#define sqlite3_result_double sqlite3_api->result_double +#define sqlite3_result_error sqlite3_api->result_error +#define sqlite3_result_error16 sqlite3_api->result_error16 +#define sqlite3_result_int sqlite3_api->result_int +#define sqlite3_result_int64 sqlite3_api->result_int64 +#define sqlite3_result_null sqlite3_api->result_null +#define sqlite3_result_text sqlite3_api->result_text +#define sqlite3_result_text16 sqlite3_api->result_text16 +#define sqlite3_result_text16be sqlite3_api->result_text16be +#define sqlite3_result_text16le sqlite3_api->result_text16le +#define sqlite3_result_value sqlite3_api->result_value +#define sqlite3_rollback_hook sqlite3_api->rollback_hook +#define sqlite3_set_authorizer sqlite3_api->set_authorizer +#define sqlite3_set_auxdata sqlite3_api->set_auxdata +#define sqlite3_snprintf sqlite3_api->snprintf +#define sqlite3_step sqlite3_api->step +#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata +#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup +#define sqlite3_total_changes sqlite3_api->total_changes +#define sqlite3_trace sqlite3_api->trace +#ifndef SQLITE_OMIT_DEPRECATED +#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings +#endif +#define sqlite3_update_hook sqlite3_api->update_hook +#define sqlite3_user_data sqlite3_api->user_data +#define sqlite3_value_blob sqlite3_api->value_blob +#define sqlite3_value_bytes sqlite3_api->value_bytes +#define sqlite3_value_bytes16 sqlite3_api->value_bytes16 +#define sqlite3_value_double sqlite3_api->value_double +#define sqlite3_value_int sqlite3_api->value_int +#define sqlite3_value_int64 sqlite3_api->value_int64 +#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type +#define sqlite3_value_text sqlite3_api->value_text +#define sqlite3_value_text16 sqlite3_api->value_text16 +#define sqlite3_value_text16be sqlite3_api->value_text16be +#define sqlite3_value_text16le sqlite3_api->value_text16le +#define sqlite3_value_type sqlite3_api->value_type +#define sqlite3_vmprintf sqlite3_api->vmprintf +#define sqlite3_overload_function sqlite3_api->overload_function +#define sqlite3_prepare_v2 sqlite3_api->prepare_v2 +#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2 +#define sqlite3_clear_bindings sqlite3_api->clear_bindings +#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob +#define sqlite3_blob_bytes sqlite3_api->blob_bytes +#define sqlite3_blob_close sqlite3_api->blob_close +#define sqlite3_blob_open sqlite3_api->blob_open +#define sqlite3_blob_read sqlite3_api->blob_read +#define sqlite3_blob_write sqlite3_api->blob_write +#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2 +#define sqlite3_file_control sqlite3_api->file_control +#define sqlite3_memory_highwater sqlite3_api->memory_highwater +#define sqlite3_memory_used sqlite3_api->memory_used +#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc +#define sqlite3_mutex_enter sqlite3_api->mutex_enter +#define sqlite3_mutex_free sqlite3_api->mutex_free +#define sqlite3_mutex_leave sqlite3_api->mutex_leave +#define sqlite3_mutex_try sqlite3_api->mutex_try +#define sqlite3_open_v2 sqlite3_api->open_v2 +#define sqlite3_release_memory sqlite3_api->release_memory +#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem +#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig +#define sqlite3_sleep sqlite3_api->sleep +#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit +#define sqlite3_vfs_find sqlite3_api->vfs_find +#define sqlite3_vfs_register sqlite3_api->vfs_register +#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister +#define sqlite3_threadsafe sqlite3_api->xthreadsafe +#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob +#define sqlite3_result_error_code sqlite3_api->result_error_code +#define sqlite3_test_control sqlite3_api->test_control +#define sqlite3_randomness sqlite3_api->randomness +#define sqlite3_context_db_handle sqlite3_api->context_db_handle +#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes +#define sqlite3_limit sqlite3_api->limit +#define sqlite3_next_stmt sqlite3_api->next_stmt +#define sqlite3_sql sqlite3_api->sql +#define sqlite3_status sqlite3_api->status +#define sqlite3_backup_finish sqlite3_api->backup_finish +#define sqlite3_backup_init sqlite3_api->backup_init +#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount +#define sqlite3_backup_remaining sqlite3_api->backup_remaining +#define sqlite3_backup_step sqlite3_api->backup_step +#define sqlite3_compileoption_get sqlite3_api->compileoption_get +#define sqlite3_compileoption_used sqlite3_api->compileoption_used +#define sqlite3_create_function_v2 sqlite3_api->create_function_v2 +#define sqlite3_db_config sqlite3_api->db_config +#define sqlite3_db_mutex sqlite3_api->db_mutex +#define sqlite3_db_status sqlite3_api->db_status +#define sqlite3_extended_errcode sqlite3_api->extended_errcode +#define sqlite3_log sqlite3_api->log +#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64 +#define sqlite3_sourceid sqlite3_api->sourceid +#define sqlite3_stmt_status sqlite3_api->stmt_status +#define sqlite3_strnicmp sqlite3_api->strnicmp +#define sqlite3_unlock_notify sqlite3_api->unlock_notify +#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint +#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint +#define sqlite3_wal_hook sqlite3_api->wal_hook +#define sqlite3_blob_reopen sqlite3_api->blob_reopen +#define sqlite3_vtab_config sqlite3_api->vtab_config +#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict +#endif /* SQLITE_CORE */ + +#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0; +#define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v; + +#endif /* _SQLITE3EXT_H_ */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteapiroutines.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteapiroutines.c new file mode 100644 index 000000000..c9ac26687 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteapiroutines.c @@ -0,0 +1,275 @@ +/****************************************************************************** + * $Id: ogrsqliteapiroutines.c 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Static registration of sqlite3 entry points + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_port.h" + +#ifndef WIN32 + +#ifdef HAVE_SPATIALITE + #ifdef SPATIALITE_AMALGAMATION + /* + / using an AMALGAMATED version of SpatiaLite + / a private internal copy of SQLite is included: + / so we are required including the SpatiaLite's + / own header + / + / IMPORTANT NOTICE: using AMALAGATION is only + / useful on Windows (to skip DLL hell related oddities) + */ + #include + #else + /* + / You MUST NOT use AMALGAMATION on Linux or any + / other "sane" operating system !!!! + */ + #include "sqlite3.h" + #endif +#else +#include "sqlite3.h" +#endif + +#if SQLITE_VERSION_NUMBER >= 3006000 +#define HAVE_SQLITE_VFS +#define HAVE_SQLITE3_PREPARE_V2 +#endif + +#define DONT_UNDEF_SQLITE3_MACROS +#ifndef SQLITE_CORE +#define SQLITE_CORE +#endif +#include "ogrsqlite3ext.h" + +const struct sqlite3_api_routines OGRSQLITE_static_routines = +{ + NULL, /*sqlite3_aggregate_context, */ + NULL, /*sqlite3_aggregate_count, */ + sqlite3_bind_blob, /* YES */ + sqlite3_bind_double, /* YES */ + sqlite3_bind_int, /* YES */ + sqlite3_bind_int64, /* YES */ + sqlite3_bind_null, /* YES */ + NULL, /*sqlite3_bind_parameter_count,*/ + NULL, /*sqlite3_bind_parameter_index,*/ + NULL, /*sqlite3_bind_parameter_name,*/ + sqlite3_bind_text, /* YES */ + NULL, /*sqlite3_bind_text16,*/ + NULL, /*sqlite3_bind_value,*/ + NULL, /*sqlite3_busy_handler,*/ + NULL, /*sqlite3_busy_timeout,*/ + sqlite3_changes, /* YES */ + sqlite3_close, /* YES */ + NULL, /*sqlite3_collation_needed,*/ + NULL, /*sqlite3_collation_needed16,*/ + sqlite3_column_blob, /* YES */ + sqlite3_column_bytes, /* YES */ + NULL, /*sqlite3_column_bytes16,*/ + sqlite3_column_count, /* YES */ + NULL, /*sqlite3_column_database_name,*/ + NULL, /*sqlite3_column_database_name16,*/ + sqlite3_column_decltype, /* YES */ + NULL, /*sqlite3_column_decltype16,*/ + sqlite3_column_double, /* YES */ + sqlite3_column_int, /* YES */ + sqlite3_column_int64, /* YES */ + sqlite3_column_name, /* YES */ + NULL, /*sqlite3_column_name16,*/ + NULL, /*sqlite3_column_origin_name,*/ + NULL, /*sqlite3_column_origin_name16,*/ +#ifdef SQLITE_HAS_COLUMN_METADATA + sqlite3_column_table_name, /* YES */ +#else + NULL, +#endif + NULL, /*sqlite3_column_table_name16,*/ + sqlite3_column_text, /* YES */ + NULL, /*sqlite3_column_text16,*/ + sqlite3_column_type, /* YES */ + NULL, /*sqlite3_column_value,*/ + NULL, /*sqlite3_commit_hook,*/ + NULL, /*sqlite3_complete,*/ + NULL, /*sqlite3_complete16,*/ + NULL, /*sqlite3_create_collation,*/ + NULL, /*sqlite3_create_collation16,*/ + sqlite3_create_function, + NULL, /*sqlite3_create_function16,*/ + sqlite3_create_module, + NULL, /*sqlite3_data_count,*/ + NULL, /*sqlite3_db_handle,*/ + sqlite3_declare_vtab, + NULL, /*sqlite3_enable_shared_cache,*/ + sqlite3_errcode, + sqlite3_errmsg, /* YES */ + NULL, /*sqlite3_errmsg16,*/ + sqlite3_exec, /* YES */ + NULL, /*sqlite3_expired,*/ + sqlite3_finalize, /* YES */ + sqlite3_free, /* YES */ + sqlite3_free_table, /* YES */ + NULL, /*sqlite3_get_autocommit,*/ + NULL, /*sqlite3_get_auxdata,*/ + sqlite3_get_table, /* YES */ + NULL, /*sqlite3_global_recover,*/ + NULL, /*sqlite3_interrupt,*/ + sqlite3_last_insert_rowid, /* YES */ + NULL, /*sqlite3_libversion,*/ + sqlite3_libversion_number, /* YES */ + sqlite3_malloc, + sqlite3_mprintf, + sqlite3_open, /* YES */ + NULL, /*sqlite3_open16,*/ + sqlite3_prepare, /* YES */ + NULL, /*sqlite3_prepare16,*/ + NULL, /*sqlite3_profile,*/ + NULL, /*sqlite3_progress_handler,*/ + sqlite3_realloc, + sqlite3_reset, /* YES */ + sqlite3_result_blob, + sqlite3_result_double, + sqlite3_result_error, + NULL, /*sqlite3_result_error16,*/ + sqlite3_result_int, + sqlite3_result_int64, + sqlite3_result_null, + sqlite3_result_text, + NULL, /*sqlite3_result_text16,*/ + NULL, /*sqlite3_result_text16be,*/ + NULL, /*sqlite3_result_text16le,*/ + sqlite3_result_value, + NULL, /*sqlite3_rollback_hook,*/ + NULL, /*sqlite3_set_authorizer,*/ + NULL, /*sqlite3_set_auxdata,*/ + sqlite3_snprintf, + sqlite3_step, /* YES */ + NULL, /*sqlite3_table_column_metadata,*/ + NULL, /*sqlite3_thread_cleanup,*/ + sqlite3_total_changes, + NULL, /*sqlite3_trace,*/ + NULL, /*sqlite3_transfer_bindings,*/ + NULL, /*sqlite3_update_hook,*/ + sqlite3_user_data, + sqlite3_value_blob, + sqlite3_value_bytes, + NULL, /*sqlite3_value_bytes16,*/ + sqlite3_value_double, + sqlite3_value_int, + sqlite3_value_int64, + sqlite3_value_numeric_type, + sqlite3_value_text, + NULL, /*sqlite3_value_text16,*/ + NULL, /*sqlite3_value_text16be,*/ + NULL, /*sqlite3_value_text16le,*/ + sqlite3_value_type, + sqlite3_vmprintf, + /* Added ??? */ + NULL, /*sqlite3_overload_function,*/ + /* Added by 3.3.13 */ +#ifdef HAVE_SQLITE3_PREPARE_V2 + sqlite3_prepare_v2, /* YES */ +#else + NULL, +#endif + NULL, /*sqlite3_prepare16_v2,*/ + NULL, /*sqlite3_clear_bindings,*/ + /* Added by 3.4.1 */ +#ifdef HAVE_SQLITE_VFS + sqlite3_create_module_v2, +#endif + /* Added by 3.5.0 */ + NULL, /*sqlite3_bind_zeroblob,*/ + NULL, /*sqlite3_blob_bytes,*/ + NULL, /*sqlite3_blob_close,*/ + NULL, /*sqlite3_blob_open,*/ + NULL, /*sqlite3_blob_read,*/ + NULL, /*sqlite3_blob_write,*/ + NULL, /*sqlite3_create_collation_v2,*/ + NULL, /*sqlite3_file_control,*/ + NULL, /*sqlite3_memory_highwater,*/ + NULL, /*sqlite3_memory_used,*/ + NULL, /*sqlite3_mutex_alloc,*/ + NULL, /*sqlite3_mutex_enter,*/ + NULL, /*sqlite3_mutex_free,*/ + NULL, /*sqlite3_mutex_leave,*/ + NULL, /*sqlite3_mutex_try,*/ +#ifdef HAVE_SQLITE_VFS + sqlite3_open_v2, /* YES */ +#else + NULL, +#endif + NULL, /*sqlite3_release_memory,*/ + NULL, /*sqlite3_result_error_nomem,*/ + NULL, /*sqlite3_result_error_toobig,*/ + NULL, /*sqlite3_sleep,*/ + NULL, /*sqlite3_soft_heap_limit,*/ +#ifdef HAVE_SQLITE_VFS + sqlite3_vfs_find, /* YES */ + sqlite3_vfs_register, /* YES */ + sqlite3_vfs_unregister, /* YES */ +#else + NULL, + NULL, + NULL, +#endif + NULL, /*sqlite3_threadsafe,*/ + NULL, /*sqlite3_result_zeroblob,*/ + NULL, /*sqlite3_result_error_code,*/ + NULL, /*sqlite3_test_control,*/ + NULL, /*sqlite3_randomness,*/ + NULL, /*sqlite3_context_db_handle,*/ + NULL, /*sqlite3_extended_result_codes,*/ + NULL, /*sqlite3_limit,*/ + NULL, /*sqlite3_next_stmt,*/ + NULL, /*sqlite3_sql,*/ + NULL, /*sqlite3_status,*/ + NULL, /*sqlite3_backup_finish,*/ + NULL, /*sqlite3_backup_init,*/ + NULL, /*sqlite3_backup_pagecount,*/ + NULL, /*sqlite3_backup_remaining,*/ + NULL, /*sqlite3_backup_step,*/ + NULL,/*sqlite3_compileoption_get,*/ + NULL,/*sqlite3_compileoption_used,*/ + NULL,/*sqlite3_create_function_v2,*/ + NULL, /*sqlite3_db_config,*/ + NULL, /*sqlite3_db_mutex,*/ + NULL, /*sqlite3_db_status,*/ + NULL, /*sqlite3_extended_errcode,*/ + NULL, /*sqlite3_log,*/ + NULL, /*sqlite3_soft_heap_limit64,*/ + NULL, /*sqlite3_sourceid,*/ + NULL, /*sqlite3_stmt_status,*/ + NULL, /*sqlite3_strnicmp,*/ + NULL, /*sqlite3_unlock_notify,*/ + NULL, /*sqlite3_wal_autocheckpoint,*/ + NULL, /*sqlite3_wal_checkpoint,*/ + NULL, /*sqlite3_wal_hook,*/ + NULL, /*sqlite3_blob_reopen,*/ + NULL, /*sqlite3_vtab_config,*/ + NULL, /*sqlite3_vtab_on_conflict,*/ +}; + +#endif // WIN32 diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitedatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitedatasource.cpp new file mode 100644 index 000000000..1d94574da --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitedatasource.cpp @@ -0,0 +1,3257 @@ +/****************************************************************************** + * $Id: ogrsqlitedatasource.cpp 29060 2015-04-29 21:49:46Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRSQLiteDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * + * Contributor: Alessandro Furieri, a.furieri@lqt.it + * Portions of this module properly supporting SpatiaLite Table/Geom creation + * Developed for Faunalia ( http://www.faunalia.it) with funding from + * Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sqlite.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_hash_set.h" +#include "cpl_csv.h" +#include "ogrsqlitevirtualogr.h" + +#ifdef HAVE_SPATIALITE +#include "spatialite.h" +#endif + +#ifndef SPATIALITE_412_OR_LATER +static int bSpatialiteGlobalLoaded = FALSE; +#endif + +CPL_CVSID("$Id: ogrsqlitedatasource.cpp 29060 2015-04-29 21:49:46Z rouault $"); + +/************************************************************************/ +/* OGRSQLiteInitOldSpatialite() */ +/************************************************************************/ + +#ifndef SPATIALITE_412_OR_LATER + +static int OGRSQLiteInitOldSpatialite() +{ +/* -------------------------------------------------------------------- */ +/* Try loading SpatiaLite. */ +/* -------------------------------------------------------------------- */ +#ifdef HAVE_SPATIALITE + if (!bSpatialiteGlobalLoaded && CSLTestBoolean(CPLGetConfigOption("SPATIALITE_LOAD", "TRUE"))) + { + bSpatialiteGlobalLoaded = TRUE; + spatialite_init(CSLTestBoolean(CPLGetConfigOption("SPATIALITE_INIT_VERBOSE", "FALSE"))); + } +#endif + return bSpatialiteGlobalLoaded; +} + +#else + +/************************************************************************/ +/* InitNewSpatialite() */ +/************************************************************************/ + +int OGRSQLiteBaseDataSource::InitNewSpatialite() +{ + if( CSLTestBoolean(CPLGetConfigOption("SPATIALITE_LOAD", "TRUE")) ) + { + CPLAssert(hSpatialiteCtxt == NULL); + hSpatialiteCtxt = spatialite_alloc_connection(); + if( hSpatialiteCtxt != NULL ) + { + spatialite_init_ex(hDB, hSpatialiteCtxt, + CSLTestBoolean(CPLGetConfigOption("SPATIALITE_INIT_VERBOSE", "FALSE"))); + } + } + return hSpatialiteCtxt != NULL; +} + +/************************************************************************/ +/* FinishNewSpatialite() */ +/************************************************************************/ + +void OGRSQLiteBaseDataSource::FinishNewSpatialite() +{ + if( hSpatialiteCtxt != NULL ) + { + spatialite_cleanup_ex(hSpatialiteCtxt); + hSpatialiteCtxt = NULL; + } +} + +#endif + +/************************************************************************/ +/* IsSpatialiteLoaded() */ +/************************************************************************/ + +int OGRSQLiteDataSource::IsSpatialiteLoaded() +{ +#ifdef SPATIALITE_412_OR_LATER + return hSpatialiteCtxt != NULL; +#else + return bSpatialiteGlobalLoaded; +#endif +} + +/************************************************************************/ +/* GetSpatialiteVersionNumber() */ +/************************************************************************/ + +int OGRSQLiteDataSource::GetSpatialiteVersionNumber() +{ + int v = 0; +#ifdef HAVE_SPATIALITE + if( IsSpatialiteLoaded() ) + { + v = (int)(( CPLAtof( spatialite_version() ) + 0.001 ) * 10.0); + } +#endif + return v; +} + +/************************************************************************/ +/* OGRSQLiteBaseDataSource() */ +/************************************************************************/ + +OGRSQLiteBaseDataSource::OGRSQLiteBaseDataSource() + +{ + m_pszFilename = NULL; + hDB = NULL; + bUpdate = FALSE; + +#ifdef HAVE_SQLITE_VFS + pMyVFS = NULL; +#endif + + fpMainFile = NULL; /* Do not close ! The VFS layer will do it for us */ + +#ifdef SPATIALITE_412_OR_LATER + hSpatialiteCtxt = NULL; +#endif + + bUserTransactionActive = FALSE; + nSoftTransactionLevel = 0; +} + +/************************************************************************/ +/* ~OGRSQLiteBaseDataSource() */ +/************************************************************************/ + +OGRSQLiteBaseDataSource::~OGRSQLiteBaseDataSource() + +{ +#ifdef SPATIALITE_412_OR_LATER + FinishNewSpatialite(); +#endif + + CloseDB(); + CPLFree(m_pszFilename); +} + +/************************************************************************/ +/* CloseDB() */ +/************************************************************************/ + +void OGRSQLiteBaseDataSource::CloseDB() +{ + if( hDB != NULL ) + { + sqlite3_close( hDB ); + hDB = NULL; + } + +#ifdef HAVE_SQLITE_VFS + if (pMyVFS) + { + sqlite3_vfs_unregister(pMyVFS); + CPLFree(pMyVFS->pAppData); + CPLFree(pMyVFS); + pMyVFS = NULL; + } +#endif +} + +/************************************************************************/ +/* OGRSQLiteDataSource() */ +/************************************************************************/ + +OGRSQLiteDataSource::OGRSQLiteDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + nKnownSRID = 0; + panSRID = NULL; + papoSRS = NULL; + + bHaveGeometryColumns = FALSE; + bIsSpatiaLiteDB = FALSE; + bSpatialite4Layout = FALSE; + + nUndefinedSRID = -1; /* will be changed to 0 if Spatialite >= 4.0 detected */ + + nFileTimestamp = 0; + bLastSQLCommandIsUpdateLayerStatistics = FALSE; + papszOpenOptions = NULL; +} + +/************************************************************************/ +/* ~OGRSQLiteDataSource() */ +/************************************************************************/ + +OGRSQLiteDataSource::~OGRSQLiteDataSource() + +{ + int i; + + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( papoLayers[iLayer]->IsTableLayer() ) + { + OGRSQLiteTableLayer* poLayer = (OGRSQLiteTableLayer*) papoLayers[iLayer]; + poLayer->RunDeferredCreationIfNecessary(); + poLayer->CreateSpatialIndexIfNecessary(); + } + } + + SaveStatistics(); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + for( i = 0; i < nKnownSRID; i++ ) + { + if( papoSRS[i] != NULL ) + papoSRS[i]->Release(); + } + CPLFree( panSRID ); + CPLFree( papoSRS ); + CSLDestroy( papszOpenOptions ); +} + +/************************************************************************/ +/* SaveStatistics() */ +/************************************************************************/ + +void OGRSQLiteDataSource::SaveStatistics() +{ + int i; + int nSavedAllLayersCacheData = -1; + + if( !bIsSpatiaLiteDB || !IsSpatialiteLoaded() || bLastSQLCommandIsUpdateLayerStatistics ) + return; + + for( i = 0; i < nLayers; i++ ) + { + if( papoLayers[i]->IsTableLayer() ) + { + OGRSQLiteTableLayer* poLayer = (OGRSQLiteTableLayer*) papoLayers[i]; + int nSaveRet = poLayer->SaveStatistics(); + if( nSaveRet >= 0) + { + if( nSavedAllLayersCacheData < 0 ) + nSavedAllLayersCacheData = nSaveRet; + else + nSavedAllLayersCacheData &= nSaveRet; + } + } + } + + if( hDB && nSavedAllLayersCacheData == TRUE ) + { + char* pszErrMsg = NULL; + + int nRowCount = 0, nColCount = 0; + char **papszResult = NULL; + int nReplaceEventId = -1; + + sqlite3_get_table( hDB, + "SELECT event_id, table_name, geometry_column, event " + "FROM spatialite_history ORDER BY event_id DESC LIMIT 1", + &papszResult, + &nRowCount, &nColCount, &pszErrMsg ); + + if( nRowCount == 1 ) + { + char **papszRow = papszResult + 4; + const char* pszEventId = papszRow[0]; + const char* pszTableName = papszRow[1]; + const char* pszGeomCol = papszRow[2]; + const char* pszEvent = papszRow[3]; + + if( pszEventId != NULL && pszTableName != NULL && + pszGeomCol != NULL && pszEvent != NULL && + strcmp(pszTableName, "ALL-TABLES") == 0 && + strcmp(pszGeomCol, "ALL-GEOMETRY-COLUMNS") == 0 && + strcmp(pszEvent, "UpdateLayerStatistics") == 0 ) + { + nReplaceEventId = atoi(pszEventId); + } + } + if( pszErrMsg ) + sqlite3_free( pszErrMsg ); + pszErrMsg = NULL; + + sqlite3_free_table( papszResult ); + + int rc; + + if( nReplaceEventId >= 0 ) + { + rc = sqlite3_exec( hDB, + CPLSPrintf("UPDATE spatialite_history SET " + "timestamp = DateTime('now') " + "WHERE event_id = %d", nReplaceEventId), + NULL, NULL, &pszErrMsg ); + } + else + { + rc = sqlite3_exec( hDB, + "INSERT INTO spatialite_history (table_name, geometry_column, " + "event, timestamp, ver_sqlite, ver_splite) VALUES (" + "'ALL-TABLES', 'ALL-GEOMETRY-COLUMNS', 'UpdateLayerStatistics', " + "DateTime('now'), sqlite_version(), spatialite_version())", + NULL, NULL, &pszErrMsg ); + } + + if( rc != SQLITE_OK ) + { + CPLDebug("SQLITE", "Error %s", pszErrMsg ? pszErrMsg : "unknown"); + sqlite3_free( pszErrMsg ); + } + } +} + +/************************************************************************/ +/* SetSynchronous() */ +/************************************************************************/ + +int OGRSQLiteBaseDataSource::SetSynchronous() +{ + int rc; + const char* pszSqliteSync = CPLGetConfigOption("OGR_SQLITE_SYNCHRONOUS", NULL); + if (pszSqliteSync != NULL) + { + char* pszErrMsg = NULL; + if (EQUAL(pszSqliteSync, "OFF") || EQUAL(pszSqliteSync, "0") || + EQUAL(pszSqliteSync, "FALSE")) + rc = sqlite3_exec( hDB, "PRAGMA synchronous = OFF", NULL, NULL, &pszErrMsg ); + else if (EQUAL(pszSqliteSync, "NORMAL") || EQUAL(pszSqliteSync, "1")) + rc = sqlite3_exec( hDB, "PRAGMA synchronous = NORMAL", NULL, NULL, &pszErrMsg ); + else if (EQUAL(pszSqliteSync, "ON") || EQUAL(pszSqliteSync, "FULL") || + EQUAL(pszSqliteSync, "2") || EQUAL(pszSqliteSync, "TRUE")) + rc = sqlite3_exec( hDB, "PRAGMA synchronous = FULL", NULL, NULL, &pszErrMsg ); + else + { + CPLError( CE_Warning, CPLE_AppDefined, "Unrecognized value for OGR_SQLITE_SYNCHRONOUS : %s", + pszSqliteSync); + rc = SQLITE_OK; + } + + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to run PRAGMA synchronous : %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + } + return TRUE; +} + +/************************************************************************/ +/* SetCacheSize() */ +/************************************************************************/ + +int OGRSQLiteBaseDataSource::SetCacheSize() +{ + int rc; + const char* pszSqliteCacheMB = CPLGetConfigOption("OGR_SQLITE_CACHE", NULL); + if (pszSqliteCacheMB != NULL) + { + char* pszErrMsg = NULL; + char **papszResult; + int nRowCount, nColCount; + int iSqliteCachePages; + int iSqlitePageSize = -1; + int iSqliteCacheBytes = atoi( pszSqliteCacheMB ) * 1024 * 1024; + + /* querying the current PageSize */ + rc = sqlite3_get_table( hDB, "PRAGMA page_size", + &papszResult, &nRowCount, &nColCount, + &pszErrMsg ); + if( rc == SQLITE_OK ) + { + int iRow; + for (iRow = 1; iRow <= nRowCount; iRow++) + { + iSqlitePageSize = atoi( papszResult[(iRow * nColCount) + 0] ); + } + sqlite3_free_table(papszResult); + } + if( iSqlitePageSize < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to run PRAGMA page_size : %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return TRUE; + } + + /* computing the CacheSize as #Pages */ + iSqliteCachePages = iSqliteCacheBytes / iSqlitePageSize; + if( iSqliteCachePages <= 0) + return TRUE; + + rc = sqlite3_exec( hDB, CPLSPrintf( "PRAGMA cache_size = %d", + iSqliteCachePages ), + NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unrecognized value for PRAGMA cache_size : %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + rc = SQLITE_OK; + } + } + return TRUE; +} + +/************************************************************************/ +/* OGRSQLiteBaseDataSourceNotifyFileOpened() */ +/************************************************************************/ + +static +void OGRSQLiteBaseDataSourceNotifyFileOpened (void* pfnUserData, + const char* pszFilename, + VSILFILE* fp) +{ + ((OGRSQLiteBaseDataSource*)pfnUserData)->NotifyFileOpened(pszFilename, fp); +} + + +/************************************************************************/ +/* NotifyFileOpened() */ +/************************************************************************/ + +void OGRSQLiteBaseDataSource::NotifyFileOpened(const char* pszFilename, + VSILFILE* fp) +{ + if (strcmp(pszFilename, m_pszFilename) == 0) + { + fpMainFile = fp; + } +} + +#ifdef USE_SQLITE_DEBUG_MEMALLOC + +/* DMA9 */ +#define DMA_SIGNATURE 0x444D4139 + +static void* OGRSQLiteDMA_Malloc(int size) +{ + int* ret = (int*) CPLMalloc(size + 8); + ret[0] = size; + ret[1] = DMA_SIGNATURE; + return ret + 2; +} + +static void* OGRSQLiteDMA_Realloc(void* old_ptr, int size) +{ + CPLAssert(((int*)old_ptr)[-1] == DMA_SIGNATURE); + int* ret = (int*) CPLRealloc(old_ptr ? (int*)old_ptr - 2 : NULL, size + 8); + ret[0] = size; + ret[1] = DMA_SIGNATURE; + return ret + 2; +} + +static void OGRSQLiteDMA_Free(void* ptr) +{ + if( ptr ) + { + CPLAssert(((int*)ptr)[-1] == DMA_SIGNATURE); + ((int*)ptr)[-1] = 0; + CPLFree((int*)ptr - 2); + } +} + +static int OGRSQLiteDMA_Size (void* ptr) +{ + if( ptr ) + { + CPLAssert(((int*)ptr)[-1] == DMA_SIGNATURE); + return ((int*)ptr)[-2]; + } + else + return 0; +} + +static int OGRSQLiteDMA_Roundup( int size ) +{ + return (size + 7) & (~7); +} + +static int OGRSQLiteDMA_Init(void *) +{ + return SQLITE_OK; +} + +static void OGRSQLiteDMA_Shutdown(void *) +{ +} + +const struct sqlite3_mem_methods sDebugMemAlloc = +{ + OGRSQLiteDMA_Malloc, + OGRSQLiteDMA_Free, + OGRSQLiteDMA_Realloc, + OGRSQLiteDMA_Size, + OGRSQLiteDMA_Roundup, + OGRSQLiteDMA_Init, + OGRSQLiteDMA_Shutdown, + NULL +}; + +#endif // USE_SQLITE_DEBUG_MEMALLOC + +/************************************************************************/ +/* OpenOrCreateDB() */ +/************************************************************************/ + +int OGRSQLiteBaseDataSource::OpenOrCreateDB(int flags, int bRegisterOGR2SQLiteExtensions) +{ + int rc; + +#ifdef USE_SQLITE_DEBUG_MEMALLOC + if( CSLTestBoolean(CPLGetConfigOption("USE_SQLITE_DEBUG_MEMALLOC", "NO")) ) + sqlite3_config(SQLITE_CONFIG_MALLOC, &sDebugMemAlloc); +#endif + +#ifdef HAVE_SQLITE_VFS + if( bRegisterOGR2SQLiteExtensions ) + OGR2SQLITE_Register(); + + int bUseOGRVFS = CSLTestBoolean(CPLGetConfigOption("SQLITE_USE_OGR_VFS", "NO")); + if (bUseOGRVFS || strncmp(m_pszFilename, "/vsi", 4) == 0) + { + pMyVFS = OGRSQLiteCreateVFS(OGRSQLiteBaseDataSourceNotifyFileOpened, this); + sqlite3_vfs_register(pMyVFS, 0); + rc = sqlite3_open_v2( m_pszFilename, &hDB, flags, pMyVFS->zName ); + } + else + rc = sqlite3_open_v2( m_pszFilename, &hDB, flags, NULL ); +#else + rc = sqlite3_open( m_pszFilename, &hDB ); +#endif + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "sqlite3_open(%s) failed: %s", + m_pszFilename, sqlite3_errmsg( hDB ) ); + return FALSE; + } + + int nRowCount = 0, nColCount = 0; + char** papszResult = NULL; + sqlite3_get_table( hDB, + "SELECT name, sql FROM sqlite_master " + "WHERE (type = 'trigger' OR type = 'view') AND (" + "sql LIKE '%%ogr_geocode%%' OR " + "sql LIKE '%%ogr_datasource_load_layers%%' OR " + "sql LIKE '%%ogr_GetConfigOption%%' OR " + "sql LIKE '%%ogr_SetConfigOption%%' )", + &papszResult, &nRowCount, &nColCount, + NULL ); + + sqlite3_free_table(papszResult); + papszResult = NULL; + + if( nRowCount > 0 ) + { + if( !CSLTestBoolean(CPLGetConfigOption("ALLOW_OGR_SQL_FUNCTIONS_FROM_TRIGGER_AND_VIEW", "NO")) ) + { + CPLError( CE_Failure, CPLE_OpenFailed, "%s", + "A trigger and/or view calls a OGR extension SQL function that could be used to " + "steal data, or use network bandwidth, without your consent.\n" + "The database will not be opened unless the ALLOW_OGR_SQL_FUNCTIONS_FROM_TRIGGER_AND_VIEW " + "configuration option to YES."); + return FALSE; + } + } + + const char* pszSqliteJournal = CPLGetConfigOption("OGR_SQLITE_JOURNAL", NULL); + if (pszSqliteJournal != NULL) + { + char* pszErrMsg = NULL; + char **papszResult; + int nRowCount, nColCount; + + const char* pszSQL = CPLSPrintf("PRAGMA journal_mode = %s", + pszSqliteJournal); + + rc = sqlite3_get_table( hDB, pszSQL, + &papszResult, &nRowCount, &nColCount, + &pszErrMsg ); + if( rc == SQLITE_OK ) + { + sqlite3_free_table(papszResult); + } + else + { + sqlite3_free( pszErrMsg ); + } + } + + const char* pszSqlitePragma = CPLGetConfigOption("OGR_SQLITE_PRAGMA", NULL); + if (pszSqlitePragma != NULL) + { + char** papszTokens = CSLTokenizeString2( pszSqlitePragma, ",", CSLT_HONOURSTRINGS ); + for(int i=0; papszTokens[i] != NULL; i++ ) + { + char* pszErrMsg = NULL; + char **papszResult; + int nRowCount, nColCount; + + const char* pszSQL = CPLSPrintf("PRAGMA %s", papszTokens[i]); + + rc = sqlite3_get_table( hDB, pszSQL, + &papszResult, &nRowCount, &nColCount, + &pszErrMsg ); + if( rc == SQLITE_OK ) + { + sqlite3_free_table(papszResult); + } + else + { + sqlite3_free( pszErrMsg ); + } + } + CSLDestroy(papszTokens); + } + + if (!SetCacheSize()) + return FALSE; + + if (!SetSynchronous()) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRSQLiteDataSource::Create( const char * pszNameIn, char **papszOptions ) +{ + int rc; + CPLString osCommand; + char *pszErrMsg = NULL; + + m_pszFilename = CPLStrdup( pszNameIn ); + +/* -------------------------------------------------------------------- */ +/* Check that spatialite extensions are loaded if required to */ +/* create a spatialite database */ +/* -------------------------------------------------------------------- */ + int bSpatialite = CSLFetchBoolean( papszOptions, "SPATIALITE", FALSE ); + int bMetadata = CSLFetchBoolean( papszOptions, "METADATA", TRUE ); + + if (bSpatialite == TRUE) + { +#ifdef HAVE_SPATIALITE +#ifndef SPATIALITE_412_OR_LATER + OGRSQLiteInitOldSpatialite(); + if (!IsSpatialiteLoaded()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Creating a Spatialite database, but Spatialite extensions are not loaded." ); + return FALSE; + } +#endif +#else + CPLError( CE_Failure, CPLE_NotSupported, + "OGR was built without libspatialite support\n" + "... sorry, creating/writing any SpatiaLite DB is unsupported\n" ); + + return FALSE; +#endif + } + + bIsSpatiaLiteDB = bSpatialite; + +/* -------------------------------------------------------------------- */ +/* Create the database file. */ +/* -------------------------------------------------------------------- */ + if (!OpenOrCreateDB(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, TRUE)) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Create the SpatiaLite metadata tables. */ +/* -------------------------------------------------------------------- */ + if ( bSpatialite ) + { +#ifdef SPATIALITE_412_OR_LATER + if (!InitNewSpatialite()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Creating a Spatialite database, but Spatialite extensions are not loaded." ); + return FALSE; + } +#endif + + /* + / SpatiaLite full support: calling InitSpatialMetadata() + / + / IMPORTANT NOTICE: on SpatiaLite any attempt aimed + / to directly CREATE "geometry_columns" and "spatial_ref_sys" + / [by-passing InitSpatialMetadata() as absolutely required] + / will severely [and irremediably] corrupt the DB !!! + */ + + const char* pszVal = CSLFetchNameValue( papszOptions, "INIT_WITH_EPSG" ); + if( pszVal != NULL && !CSLTestBoolean(pszVal) && + GetSpatialiteVersionNumber() >= 40 ) + osCommand = "SELECT InitSpatialMetadata('NONE')"; + else + { + /* Since spatialite 4.1, InitSpatialMetadata() is no longer run */ + /* into a transaction, which makes population of spatial_ref_sys */ + /* from EPSG awfully slow. We have to use InitSpatialMetadata(1) */ + /* to run within a transaction */ + if( GetSpatialiteVersionNumber() >= 41 ) + osCommand = "SELECT InitSpatialMetadata(1)"; + else + osCommand = "SELECT InitSpatialMetadata()"; + } + rc = sqlite3_exec( hDB, osCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to Initialize SpatiaLite Metadata: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Create the geometry_columns and spatial_ref_sys metadata tables. */ +/* -------------------------------------------------------------------- */ + else if( bMetadata ) + { + osCommand = + "CREATE TABLE geometry_columns (" + " f_table_name VARCHAR, " + " f_geometry_column VARCHAR, " + " geometry_type INTEGER, " + " coord_dimension INTEGER, " + " srid INTEGER," + " geometry_format VARCHAR )"; + rc = sqlite3_exec( hDB, osCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create table geometry_columns: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + + osCommand = + "CREATE TABLE spatial_ref_sys (" + " srid INTEGER UNIQUE," + " auth_name TEXT," + " auth_srid TEXT," + " srtext TEXT)"; + rc = sqlite3_exec( hDB, osCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create table spatial_ref_sys: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Optionnaly initialize the content of the spatial_ref_sys table */ +/* with the EPSG database */ +/* -------------------------------------------------------------------- */ + if ( (bSpatialite || bMetadata) && + CSLFetchBoolean( papszOptions, "INIT_WITH_EPSG", FALSE ) ) + { + if (!InitWithEPSG()) + return FALSE; + } + + return Open(m_pszFilename, TRUE, NULL); +} + +/************************************************************************/ +/* InitWithEPSG() */ +/************************************************************************/ + +int OGRSQLiteDataSource::InitWithEPSG() +{ + CPLString osCommand; + + if ( bIsSpatiaLiteDB ) + { + /* + / if v.2.4.0 (or any subsequent) InitWithEPSG make no sense at all + / because the EPSG dataset is already self-initialized at DB creation + */ + int iSpatialiteVersion = GetSpatialiteVersionNumber(); + if ( iSpatialiteVersion >= 24 ) + return TRUE; + } + + if( SoftStartTransaction() != OGRERR_NONE ) + return FALSE; + + FILE* fp; + int i; + int rc = SQLITE_OK; + for(i=0;i<2 && rc == SQLITE_OK;i++) + { + const char* pszFilename = (i == 0) ? "gcs.csv" : "pcs.csv"; + fp = VSIFOpen(CSVFilename(pszFilename), "rt"); + if (fp == NULL) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open EPSG support file %s.\n" + "Try setting the GDAL_DATA environment variable to point to the\n" + "directory containing EPSG csv files.", + pszFilename ); + + continue; + } + + OGRSpatialReference oSRS; + char** papszTokens; + CSLDestroy(CSVReadParseLine( fp )); + while ( (papszTokens = CSVReadParseLine( fp )) != NULL && rc == SQLITE_OK) + { + int nSRSId = atoi(papszTokens[0]); + CSLDestroy(papszTokens); + + CPLPushErrorHandler(CPLQuietErrorHandler); + oSRS.importFromEPSG(nSRSId); + CPLPopErrorHandler(); + + if (bIsSpatiaLiteDB) + { + char *pszProj4 = NULL; + + CPLPushErrorHandler(CPLQuietErrorHandler); + OGRErr eErr = oSRS.exportToProj4( &pszProj4 ); + CPLPopErrorHandler(); + + char *pszWKT = NULL; + if( oSRS.exportToWkt( &pszWKT ) != OGRERR_NONE ) + { + CPLFree(pszWKT); + pszWKT = NULL; + } + + if( eErr == OGRERR_NONE ) + { + const char *pszProjCS = oSRS.GetAttrValue("PROJCS"); + if (pszProjCS == NULL) + pszProjCS = oSRS.GetAttrValue("GEOGCS"); + + const char* pszSRTEXTColName = GetSRTEXTColName(); + if (pszSRTEXTColName != NULL) + { + /* the SPATIAL_REF_SYS table supports a SRS_WKT column */ + if ( pszProjCS ) + osCommand.Printf( + "INSERT INTO spatial_ref_sys " + "(srid, auth_name, auth_srid, ref_sys_name, proj4text, %s) " + "VALUES (%d, 'EPSG', '%d', ?, ?, ?)", + pszSRTEXTColName, nSRSId, nSRSId); + else + osCommand.Printf( + "INSERT INTO spatial_ref_sys " + "(srid, auth_name, auth_srid, proj4text, %s) " + "VALUES (%d, 'EPSG', '%d', ?, ?)", + pszSRTEXTColName, nSRSId, nSRSId); + } + else + { + /* the SPATIAL_REF_SYS table does not support a SRS_WKT column */ + if ( pszProjCS ) + osCommand.Printf( + "INSERT INTO spatial_ref_sys " + "(srid, auth_name, auth_srid, ref_sys_name, proj4text) " + "VALUES (%d, 'EPSG', '%d', ?, ?)", + nSRSId, nSRSId); + else + osCommand.Printf( + "INSERT INTO spatial_ref_sys " + "(srid, auth_name, auth_srid, proj4text) " + "VALUES (%d, 'EPSG', '%d', ?)", + nSRSId, nSRSId); + } + + sqlite3_stmt *hInsertStmt = NULL; + rc = sqlite3_prepare( hDB, osCommand, -1, &hInsertStmt, NULL ); + + if ( pszProjCS ) + { + if( rc == SQLITE_OK) + rc = sqlite3_bind_text( hInsertStmt, 1, pszProjCS, -1, SQLITE_STATIC ); + if( rc == SQLITE_OK) + rc = sqlite3_bind_text( hInsertStmt, 2, pszProj4, -1, SQLITE_STATIC ); + if (pszSRTEXTColName != NULL) + { + /* the SPATIAL_REF_SYS table supports a SRS_WKT column */ + if( rc == SQLITE_OK && pszWKT != NULL) + rc = sqlite3_bind_text( hInsertStmt, 3, pszWKT, -1, SQLITE_STATIC ); + } + } + else + { + if( rc == SQLITE_OK) + rc = sqlite3_bind_text( hInsertStmt, 1, pszProj4, -1, SQLITE_STATIC ); + if (pszSRTEXTColName != NULL) + { + /* the SPATIAL_REF_SYS table supports a SRS_WKT column */ + if( rc == SQLITE_OK && pszWKT != NULL) + rc = sqlite3_bind_text( hInsertStmt, 2, pszWKT, -1, SQLITE_STATIC ); + } + } + + if( rc == SQLITE_OK) + rc = sqlite3_step( hInsertStmt ); + + if( rc != SQLITE_OK && rc != SQLITE_DONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot insert %s into spatial_ref_sys : %s", + pszProj4, + sqlite3_errmsg(hDB) ); + + sqlite3_finalize( hInsertStmt ); + CPLFree(pszProj4); + CPLFree(pszWKT); + break; + } + rc = SQLITE_OK; + + sqlite3_finalize( hInsertStmt ); + } + + CPLFree(pszProj4); + CPLFree(pszWKT); + } + else + { + char *pszWKT = NULL; + if( oSRS.exportToWkt( &pszWKT ) == OGRERR_NONE ) + { + osCommand.Printf( + "INSERT INTO spatial_ref_sys " + "(srid, auth_name, auth_srid, srtext) " + "VALUES (%d, 'EPSG', '%d', ?)", + nSRSId, nSRSId ); + + sqlite3_stmt *hInsertStmt = NULL; + rc = sqlite3_prepare( hDB, osCommand, -1, &hInsertStmt, NULL ); + + if( rc == SQLITE_OK) + rc = sqlite3_bind_text( hInsertStmt, 1, pszWKT, -1, SQLITE_STATIC ); + + if( rc == SQLITE_OK) + rc = sqlite3_step( hInsertStmt ); + + if( rc != SQLITE_OK && rc != SQLITE_DONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot insert %s into spatial_ref_sys : %s", + pszWKT, + sqlite3_errmsg(hDB) ); + + sqlite3_finalize( hInsertStmt ); + CPLFree(pszWKT); + break; + } + rc = SQLITE_OK; + + sqlite3_finalize( hInsertStmt ); + } + + CPLFree(pszWKT); + } + } + VSIFClose(fp); + } + + if( rc == SQLITE_OK ) + { + if( SoftCommitTransaction() != OGRERR_NONE ) + return FALSE; + return TRUE; + } + else + { + SoftRollbackTransaction(); + return FALSE; + } +} + +/************************************************************************/ +/* ReloadLayers() */ +/************************************************************************/ + +void OGRSQLiteDataSource::ReloadLayers() +{ + for(int i=0;i 0 && iSpatialiteVersion < 40) + { + CPLDebug("SQLITE", "SpatiaLite v4 DB found, " + "but updating tables disabled because runtime spatialite library is v%.1f !", + iSpatialiteVersion / 10.0); + bUpdate = FALSE; + } + else + { + CPLDebug("SQLITE", "SpatiaLite%s DB found !", + (bSpatialite4Layout) ? " v4" : ""); + } + + for ( iRow = 0; iRow < nRowCount; iRow++ ) + { + char **papszRow = papszResult + iRow * 6 + 6; + const char* pszTableName = papszRow[0]; + const char* pszGeomCol = papszRow[1]; + + if( pszTableName == NULL || pszGeomCol == NULL ) + continue; + + aoMapTableToSetOfGeomCols[pszTableName].insert(CPLString(pszGeomCol).tolower()); + } + + for ( iRow = 0; iRow < nRowCount; iRow++ ) + { + char **papszRow = papszResult + iRow * 6 + 6; + const char* pszTableName = papszRow[0]; + + if (pszTableName == NULL ) + continue; + + if( GDALDataset::GetLayerByName(pszTableName) == NULL ) + OpenTable( pszTableName); + if (bListAllTables) + CPLHashSetInsert(hSet, CPLStrdup(pszTableName)); + } + + sqlite3_free_table(papszResult); + +/* -------------------------------------------------------------------- */ +/* Detect VirtualShape, VirtualXL and VirtualOGR layers */ +/* -------------------------------------------------------------------- */ + rc = sqlite3_get_table( hDB, + "SELECT name, sql FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE %'", + &papszResult, &nRowCount, + &nColCount, &pszErrMsg ); + + if ( rc == SQLITE_OK ) + { + for( iRow = 0; iRow < nRowCount; iRow++ ) + { + char **papszRow = papszResult + iRow * 2 + 2; + const char *pszName = papszRow[0]; + const char *pszSQL = papszRow[1]; + if( pszName == NULL || pszSQL == NULL ) + continue; + + if( (IsSpatialiteLoaded() && + (strstr(pszSQL, "VirtualShape") || strstr(pszSQL, "VirtualXL"))) || + (bListVirtualOGRLayers && strstr(pszSQL, "VirtualOGR")) ) + { + OpenVirtualTable(pszName, pszSQL); + + if (bListAllTables) + CPLHashSetInsert(hSet, CPLStrdup(pszName)); + } + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to fetch list of tables: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + } + + sqlite3_free_table(papszResult); + +/* -------------------------------------------------------------------- */ +/* Detect spatial views */ +/* -------------------------------------------------------------------- */ + rc = sqlite3_get_table( hDB, + "SELECT view_name, view_geometry, view_rowid, f_table_name, f_geometry_column FROM views_geometry_columns", + &papszResult, &nRowCount, + &nColCount, &pszErrMsg ); + if ( rc == SQLITE_OK ) + { + for( iRow = 0; iRow < nRowCount; iRow++ ) + { + char **papszRow = papszResult + iRow * 5 + 5; + const char* pszViewName = papszRow[0]; + const char* pszViewGeometry = papszRow[1]; + const char* pszViewRowid = papszRow[2]; + const char* pszTableName = papszRow[3]; + const char* pszGeometryColumn = papszRow[4]; + + if (pszViewName == NULL || + pszViewGeometry == NULL || + pszViewRowid == NULL || + pszTableName == NULL || + pszGeometryColumn == NULL) + continue; + + OpenView( pszViewName, pszViewGeometry, pszViewRowid, + pszTableName, pszGeometryColumn ); + + if (bListAllTables) + CPLHashSetInsert(hSet, CPLStrdup(pszViewName)); + } + sqlite3_free_table(papszResult); + } + + + if (bListAllTables) + goto all_tables; + + CPLHashSetDestroy(hSet); + + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise our final resort is to return all tables and views */ +/* as non-spatial tables. */ +/* -------------------------------------------------------------------- */ + sqlite3_free( pszErrMsg ); + +all_tables: + rc = sqlite3_get_table( hDB, + "SELECT name FROM sqlite_master " + "WHERE type IN ('table','view') " + "UNION ALL " + "SELECT name FROM sqlite_temp_master " + "WHERE type IN ('table','view') " + "ORDER BY 1", + &papszResult, &nRowCount, + &nColCount, &pszErrMsg ); + + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to fetch list of tables: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + CPLHashSetDestroy(hSet); + return FALSE; + } + + for( iRow = 0; iRow < nRowCount; iRow++ ) + { + const char* pszTableName = papszResult[iRow+1]; + if (CPLHashSetLookup(hSet, pszTableName) == NULL) + OpenTable( pszTableName ); + } + + sqlite3_free_table(papszResult); + CPLHashSetDestroy(hSet); + + return TRUE; +} + +/************************************************************************/ +/* OpenVirtualTable() */ +/************************************************************************/ + +int OGRSQLiteDataSource::OpenVirtualTable(const char* pszName, const char* pszSQL) +{ + int nSRID = nUndefinedSRID; + const char* pszVirtualShape = strstr(pszSQL, "VirtualShape"); + if (pszVirtualShape != NULL) + { + const char* pszParenthesis = strchr(pszVirtualShape, '('); + if (pszParenthesis) + { + /* CREATE VIRTUAL TABLE table_name VirtualShape(shapename, codepage, srid) */ + /* Extract 3rd parameter */ + char** papszTokens = CSLTokenizeString2( pszParenthesis + 1, ",", CSLT_HONOURSTRINGS ); + if (CSLCount(papszTokens) == 3) + { + nSRID = atoi(papszTokens[2]); + } + CSLDestroy(papszTokens); + } + } + + if (OpenTable(pszName, pszVirtualShape != NULL)) + { + OGRSQLiteLayer* poLayer = papoLayers[nLayers-1]; + if( poLayer->GetLayerDefn()->GetGeomFieldCount() == 1 ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poLayer->myGetLayerDefn()->myGetGeomFieldDefn(0); + poGeomFieldDefn->eGeomFormat = OSGF_SpatiaLite; + if( nSRID > 0 ) + { + poGeomFieldDefn->nSRSId = nSRID; + poGeomFieldDefn->SetSpatialRef( FetchSRS( nSRID ) ); + } + } + + OGRFeature* poFeature = poLayer->GetNextFeature(); + if (poFeature) + { + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if (poGeom) + poLayer->GetLayerDefn()->SetGeomType(poGeom->getGeometryType()); + delete poFeature; + } + poLayer->ResetReading(); + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* OpenTable() */ +/************************************************************************/ + +int OGRSQLiteDataSource::OpenTable( const char *pszTableName, + int bIsVirtualShapeIn ) + +{ +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRSQLiteTableLayer *poLayer; + + poLayer = new OGRSQLiteTableLayer( this ); + if( poLayer->Initialize( pszTableName, bIsVirtualShapeIn, FALSE) != CE_None ) + { + delete poLayer; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRSQLiteLayer **) + CPLRealloc( papoLayers, sizeof(OGRSQLiteLayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + + return TRUE; +} + +/************************************************************************/ +/* OpenView() */ +/************************************************************************/ + +int OGRSQLiteDataSource::OpenView( const char *pszViewName, + const char *pszViewGeometry, + const char *pszViewRowid, + const char *pszTableName, + const char *pszGeometryColumn) + +{ +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRSQLiteViewLayer *poLayer; + + poLayer = new OGRSQLiteViewLayer( this ); + + if( poLayer->Initialize( pszViewName, pszViewGeometry, + pszViewRowid, pszTableName, pszGeometryColumn ) != CE_None ) + { + delete poLayer; + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRSQLiteLayer **) + CPLRealloc( papoLayers, sizeof(OGRSQLiteLayer *) * (nLayers+1) ); + papoLayers[nLayers++] = poLayer; + + return TRUE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSQLiteDataSource::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return bUpdate; + else if( EQUAL(pszCap,ODsCDeleteLayer) ) + return bUpdate; + else if( EQUAL(pszCap,ODsCCurveGeometries) ) + return !bIsSpatiaLiteDB; + else if EQUAL(pszCap,ODsCCreateGeomFieldAfterCreateLayer) + return bUpdate; + else + return OGRSQLiteBaseDataSource::TestCapability(pszCap); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSQLiteBaseDataSource::TestCapability( const char * pszCap ) +{ + if EQUAL(pszCap,ODsCTransactions) + return TRUE; + else + return GDALPamDataset::TestCapability(pszCap); +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRSQLiteDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer *OGRSQLiteDataSource::GetLayerByName( const char* pszLayerName ) + +{ + OGRLayer* poLayer = GDALDataset::GetLayerByName(pszLayerName); + if( poLayer != NULL ) + return poLayer; + + if( !OpenTable(pszLayerName) ) + return NULL; + + poLayer = papoLayers[nLayers-1]; + CPLErrorReset(); + CPLPushErrorHandler(CPLQuietErrorHandler); + poLayer->GetLayerDefn(); + CPLPopErrorHandler(); + if( CPLGetLastErrorType() != 0 ) + { + CPLErrorReset(); + delete poLayer; + nLayers --; + return NULL; + } + + return poLayer; +} + +/************************************************************************/ +/* GetLayerWithGetSpatialWhereByName() */ +/************************************************************************/ + +std::pair + OGRSQLiteDataSource::GetLayerWithGetSpatialWhereByName( const char* pszName ) +{ + OGRSQLiteLayer* poRet = (OGRSQLiteLayer*) GetLayerByName(pszName); + return std::pair(poRet, poRet); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void OGRSQLiteDataSource::FlushCache() +{ + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( papoLayers[iLayer]->IsTableLayer() ) + { + OGRSQLiteTableLayer* poLayer = (OGRSQLiteTableLayer*) papoLayers[iLayer]; + poLayer->RunDeferredCreationIfNecessary(); + poLayer->CreateSpatialIndexIfNecessary(); + } + } + GDALDataset::FlushCache(); +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +static const char* apszFuncsWithSideEffects[] = +{ + "InitSpatialMetaData", + "AddGeometryColumn", + "RecoverGeometryColumn", + "DiscardGeometryColumn", + "CreateSpatialIndex", + "CreateMbrCache", + "DisableSpatialIndex", + "UpdateLayerStatistics", + + "ogr_datasource_load_layers" +}; + +OGRLayer * OGRSQLiteDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( papoLayers[iLayer]->IsTableLayer() ) + { + OGRSQLiteTableLayer* poLayer = (OGRSQLiteTableLayer*) papoLayers[iLayer]; + poLayer->RunDeferredCreationIfNecessary(); + poLayer->CreateSpatialIndexIfNecessary(); + } + } + + if( pszDialect != NULL && EQUAL(pszDialect,"OGRSQL") ) + return GDALDataset::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Special case DELLAYER: command. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand,"DELLAYER:",9) ) + { + const char *pszLayerName = pszSQLCommand + 9; + + while( *pszLayerName == ' ' ) + pszLayerName++; + + DeleteLayer( pszLayerName ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Special case GetVSILFILE() command (used by GDAL MBTiles driver)*/ +/* -------------------------------------------------------------------- */ + if (strcmp(pszSQLCommand, "GetVSILFILE()") == 0) + { + if (fpMainFile == NULL) + return NULL; + + char szVal[64]; + int nRet = CPLPrintPointer( szVal, fpMainFile, sizeof(szVal)-1 ); + szVal[nRet] = '\0'; + return new OGRSQLiteSingleFeatureLayer( "VSILFILE", szVal ); + } + +/* -------------------------------------------------------------------- */ +/* Special case for SQLITE_HAS_COLUMN_METADATA() */ +/* -------------------------------------------------------------------- */ + if (strcmp(pszSQLCommand, "SQLITE_HAS_COLUMN_METADATA()") == 0) + { +#ifdef SQLITE_HAS_COLUMN_METADATA + return new OGRSQLiteSingleFeatureLayer( "SQLITE_HAS_COLUMN_METADATA", TRUE ); +#else + return new OGRSQLiteSingleFeatureLayer( "SQLITE_HAS_COLUMN_METADATA", FALSE ); +#endif + } + +/* -------------------------------------------------------------------- */ +/* In case, this is not a SELECT, invalidate cached feature */ +/* count and extent to be on the safe side. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(pszSQLCommand, "VACUUM") ) + { + int bNeedRefresh = -1; + int i; + for( i = 0; i < nLayers; i++ ) + { + if( papoLayers[i]->IsTableLayer() ) + { + OGRSQLiteTableLayer* poLayer = (OGRSQLiteTableLayer*) papoLayers[i]; + if ( !(poLayer->AreStatisticsValid()) || + poLayer->DoStatisticsNeedToBeFlushed()) + { + bNeedRefresh = FALSE; + break; + } + else if( bNeedRefresh < 0 ) + bNeedRefresh = TRUE; + } + } + if( bNeedRefresh == TRUE ) + { + for( i = 0; i < nLayers; i++ ) + { + if( papoLayers[i]->IsTableLayer() ) + { + OGRSQLiteTableLayer* poLayer = (OGRSQLiteTableLayer*) papoLayers[i]; + poLayer->ForceStatisticsToBeFlushed(); + } + } + } + } + else if( !EQUALN(pszSQLCommand,"SELECT ",7) && !EQUAL(pszSQLCommand, "BEGIN") + && !EQUAL(pszSQLCommand, "COMMIT") + && !EQUALN(pszSQLCommand, "CREATE TABLE ", strlen("CREATE TABLE ")) ) + { + for(int i = 0; i < nLayers; i++) + papoLayers[i]->InvalidateCachedFeatureCountAndExtent(); + } + + bLastSQLCommandIsUpdateLayerStatistics = + EQUAL(pszSQLCommand, "SELECT UpdateLayerStatistics()"); + +/* -------------------------------------------------------------------- */ +/* Prepare statement. */ +/* -------------------------------------------------------------------- */ + int rc; + sqlite3_stmt *hSQLStmt = NULL; + + CPLString osSQLCommand = pszSQLCommand; + + /* This will speed-up layer creation */ + /* ORDER BY are costly to evaluate and are not necessary to establish */ + /* the layer definition. */ + int bUseStatementForGetNextFeature = TRUE; + int bEmptyLayer = FALSE; + + if( osSQLCommand.ifind("SELECT ") == 0 && + osSQLCommand.ifind(" UNION ") == std::string::npos && + osSQLCommand.ifind(" INTERSECT ") == std::string::npos && + osSQLCommand.ifind(" EXCEPT ") == std::string::npos ) + { + size_t nOrderByPos = osSQLCommand.ifind(" ORDER BY "); + if( nOrderByPos != std::string::npos ) + { + osSQLCommand.resize(nOrderByPos); + bUseStatementForGetNextFeature = FALSE; + } + } + + rc = sqlite3_prepare( GetDB(), osSQLCommand.c_str(), osSQLCommand.size(), + &hSQLStmt, NULL ); + + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In ExecuteSQL(): sqlite3_prepare(%s):\n %s", + pszSQLCommand, sqlite3_errmsg(GetDB()) ); + + if( hSQLStmt != NULL ) + { + sqlite3_finalize( hSQLStmt ); + } + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we get a resultset? */ +/* -------------------------------------------------------------------- */ + rc = sqlite3_step( hSQLStmt ); + if( rc != SQLITE_ROW ) + { + if ( rc != SQLITE_DONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In ExecuteSQL(): sqlite3_step(%s):\n %s", + pszSQLCommand, sqlite3_errmsg(GetDB()) ); + + sqlite3_finalize( hSQLStmt ); + return NULL; + } + + if( EQUALN(pszSQLCommand, "CREATE ", 7) ) + { + char **papszTokens = CSLTokenizeString( pszSQLCommand ); + if ( CSLCount(papszTokens) >= 4 && + EQUAL(papszTokens[1], "VIRTUAL") && + EQUAL(papszTokens[2], "TABLE") ) + { + OpenVirtualTable(papszTokens[3], pszSQLCommand); + } + CSLDestroy(papszTokens); + + sqlite3_finalize( hSQLStmt ); + return NULL; + } + + if( !EQUALN(pszSQLCommand, "SELECT ", 7) ) + { + sqlite3_finalize( hSQLStmt ); + return NULL; + } + + bUseStatementForGetNextFeature = FALSE; + bEmptyLayer = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Special case for some functions which must be run */ +/* only once */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand,"SELECT ",7) ) + { + unsigned int i; + for(i=0;iSetSpatialFilter( 0, poSpatialFilter ); + + return poLayer; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRSQLiteDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRSQLiteDataSource::ICreateLayer( const char * pszLayerNameIn, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ) + +{ + char *pszLayerName; + const char *pszGeomFormat; + int bImmediateSpatialIndexCreation = FALSE; + int bDeferredSpatialIndexCreation = FALSE; + +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( !bUpdate ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened read-only.\n" + "New layer %s cannot be created.\n", + m_pszFilename, pszLayerNameIn ); + + return NULL; + } + + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( papoLayers[iLayer]->IsTableLayer() ) + { + OGRSQLiteTableLayer* poLayer = (OGRSQLiteTableLayer*) papoLayers[iLayer]; + poLayer->RunDeferredCreationIfNecessary(); + } + } + + CPLString osFIDColumnName; + const char* pszFIDColumnNameIn = CSLFetchNameValueDef(papszOptions, "FID", "OGC_FID"); + if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) ) + { + char* pszFIDColumnName = LaunderName(pszFIDColumnNameIn); + osFIDColumnName = pszFIDColumnName; + CPLFree(pszFIDColumnName); + } + else + osFIDColumnName = pszFIDColumnNameIn; + + if( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) ) + pszLayerName = LaunderName( pszLayerNameIn ); + else + pszLayerName = CPLStrdup( pszLayerNameIn ); + + pszGeomFormat = CSLFetchNameValue( papszOptions, "FORMAT" ); + if( pszGeomFormat == NULL ) + { + if ( !bIsSpatiaLiteDB ) + pszGeomFormat = "WKB"; + else + pszGeomFormat = "SpatiaLite"; + } + + if( !EQUAL(pszGeomFormat,"WKT") + && !EQUAL(pszGeomFormat,"WKB") + && !EQUAL(pszGeomFormat, "SpatiaLite") ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "FORMAT=%s not recognised or supported.", + pszGeomFormat ); + CPLFree( pszLayerName ); + return NULL; + } + + CPLString osGeometryName; + const char* pszGeometryNameIn = CSLFetchNameValue( papszOptions, "GEOMETRY_NAME" ); + if( pszGeometryNameIn == NULL ) + { + osGeometryName = ( EQUAL(pszGeomFormat,"WKT") ) ? "WKT_GEOMETRY" : "GEOMETRY"; + } + else + { + if( CSLFetchBoolean(papszOptions,"LAUNDER", TRUE) ) + { + char* pszGeometryName = LaunderName(pszGeometryNameIn); + osGeometryName = pszGeometryName; + CPLFree(pszGeometryName); + } + else + osGeometryName = pszGeometryNameIn; + } + + if (bIsSpatiaLiteDB && !EQUAL(pszGeomFormat, "SpatiaLite") ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "FORMAT=%s not supported on a SpatiaLite enabled database.", + pszGeomFormat ); + CPLFree( pszLayerName ); + return NULL; + } + + /* Shouldn't happen since a spatialite DB should be opened in read-only mode */ + /* if libspatialite isn't loaded */ + if (bIsSpatiaLiteDB && !IsSpatialiteLoaded()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Creating layers on a SpatiaLite enabled database, " + "without Spatialite extensions loaded, is not supported." ); + CPLFree( pszLayerName ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we already have this layer? If so, should we blow it */ +/* away? */ +/* -------------------------------------------------------------------- */ + int iLayer; + + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName,papoLayers[iLayer]->GetLayerDefn()->GetName()) ) + { + if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL + && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") ) + { + DeleteLayer( pszLayerName ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + pszLayerName ); + CPLFree( pszLayerName ); + return NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Try to get the SRS Id of this spatial reference system, */ +/* adding to the srs table if needed. */ +/* -------------------------------------------------------------------- */ + int nSRSId = nUndefinedSRID; + const char* pszSRID = CSLFetchNameValue(papszOptions, "SRID"); + + if( pszSRID != NULL ) + { + nSRSId = atoi(pszSRID); + if( nSRSId > 0 ) + { + OGRSpatialReference* poSRSFetched = FetchSRS( nSRSId ); + if( poSRSFetched == NULL ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "SRID %d will be used, but no matching SRS is defined in spatial_ref_sys", + nSRSId); + } + } + } + else if( poSRS != NULL ) + nSRSId = FetchSRSId( poSRS ); + + const char* pszSI = CSLFetchNameValue( papszOptions, "SPATIAL_INDEX" ); + if( bHaveGeometryColumns && eType != wkbNone ) + { + if ( pszSI != NULL && CSLTestBoolean(pszSI) && + (bIsSpatiaLiteDB || EQUAL(pszGeomFormat, "SpatiaLite")) && !IsSpatialiteLoaded() ) + { + CPLError( CE_Warning, CPLE_OpenFailed, + "Cannot create a spatial index when Spatialite extensions are not loaded." ); + } + +#ifdef HAVE_SPATIALITE + /* Only if linked against SpatiaLite and the datasource was created as a SpatiaLite DB */ + if ( bIsSpatiaLiteDB && IsSpatialiteLoaded() ) +#else + if ( 0 ) +#endif + { + if( pszSI != NULL && EQUAL(pszSI, "IMMEDIATE") ) + { + bImmediateSpatialIndexCreation = TRUE; + } + else if( pszSI == NULL || CSLTestBoolean(pszSI) ) + { + bDeferredSpatialIndexCreation = TRUE; + } + } + } + else if( bHaveGeometryColumns ) + { +#ifdef HAVE_SPATIALITE + if( bIsSpatiaLiteDB && IsSpatialiteLoaded() && (pszSI == NULL || CSLTestBoolean(pszSI)) ) + bDeferredSpatialIndexCreation = TRUE; +#endif + } + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRSQLiteTableLayer *poLayer; + + poLayer = new OGRSQLiteTableLayer( this ); + + poLayer->Initialize( pszLayerName, FALSE, TRUE ) ; + poLayer->SetCreationParameters( osFIDColumnName, eType, pszGeomFormat, + osGeometryName, poSRS, nSRSId ); + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + papoLayers = (OGRSQLiteLayer **) + CPLRealloc( papoLayers, sizeof(OGRSQLiteLayer *) * (nLayers+1) ); + + papoLayers[nLayers++] = poLayer; + + poLayer->InitFeatureCount(); + poLayer->SetLaunderFlag( CSLFetchBoolean(papszOptions,"LAUNDER",TRUE) ); + if ( CSLFetchBoolean(papszOptions,"COMPRESS_GEOM",FALSE) ) + poLayer->SetUseCompressGeom( TRUE ); + if( bImmediateSpatialIndexCreation ) + poLayer->CreateSpatialIndex(0); + else if( bDeferredSpatialIndexCreation ) + poLayer->SetDeferredSpatialIndexCreation( TRUE ); + poLayer->SetCompressedColumns( CSLFetchNameValue(papszOptions,"COMPRESS_COLUMNS") ); + + CPLFree( pszLayerName ); + + return poLayer; +} + +/************************************************************************/ +/* LaunderName() */ +/************************************************************************/ + +char *OGRSQLiteDataSource::LaunderName( const char *pszSrcName ) + +{ + char *pszSafeName = CPLStrdup( pszSrcName ); + int i; + + for( i = 0; pszSafeName[i] != '\0'; i++ ) + { + pszSafeName[i] = (char) tolower( pszSafeName[i] ); + if( pszSafeName[i] == '\'' || pszSafeName[i] == '-' || pszSafeName[i] == '#' ) + pszSafeName[i] = '_'; + } + + return pszSafeName; +} + +/************************************************************************/ +/* OGRSQLiteParamsUnquote() */ +/************************************************************************/ + +CPLString OGRSQLiteParamsUnquote(const char* pszVal) +{ + char chQuoteChar = pszVal[0]; + if( chQuoteChar != '\'' && chQuoteChar != '"' ) + return pszVal; + + CPLString osRet; + pszVal ++; + while( *pszVal != '\0' ) + { + if( *pszVal == chQuoteChar ) + { + if( pszVal[1] == chQuoteChar ) + pszVal ++; + else + break; + } + osRet += *pszVal; + pszVal ++; + } + return osRet; +} + +/************************************************************************/ +/* OGRSQLiteEscape() */ +/************************************************************************/ + +CPLString OGRSQLiteEscape( const char *pszLiteral ) +{ + CPLString osVal; + for( int i = 0; pszLiteral[i] != '\0'; i++ ) + { + if ( pszLiteral[i] == '\'' ) + osVal += '\''; + osVal += pszLiteral[i]; + } + return osVal; +} + +/************************************************************************/ +/* OGRSQLiteEscapeName() */ +/************************************************************************/ + +CPLString OGRSQLiteEscapeName(const char* pszName) +{ + CPLString osRet; + while( *pszName != '\0' ) + { + if( *pszName == '"' ) + osRet += "\"\""; + else + osRet += *pszName; + pszName ++; + } + return osRet; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +void OGRSQLiteDataSource::DeleteLayer( const char *pszLayerName ) + +{ + int iLayer; + +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( !bUpdate ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened read-only.\n" + "Layer %s cannot be deleted.\n", + m_pszFilename, pszLayerName ); + + return; + } + +/* -------------------------------------------------------------------- */ +/* Try to find layer. */ +/* -------------------------------------------------------------------- */ + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName,papoLayers[iLayer]->GetLayerDefn()->GetName()) ) + break; + } + + if( iLayer == nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to delete layer '%s', but this layer is not known to OGR.", + pszLayerName ); + return; + } + + DeleteLayer(iLayer); +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +OGRErr OGRSQLiteDataSource::DeleteLayer(int iLayer) +{ + if( iLayer < 0 || iLayer >= nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %d not in legal range of 0 to %d.", + iLayer, nLayers-1 ); + return OGRERR_FAILURE; + } + + CPLString osLayerName = GetLayer(iLayer)->GetName(); + CPLString osGeometryColumn = GetLayer(iLayer)->GetGeometryColumn(); + +/* -------------------------------------------------------------------- */ +/* Blow away our OGR structures related to the layer. This is */ +/* pretty dangerous if anything has a reference to this layer! */ +/* -------------------------------------------------------------------- */ + CPLDebug( "OGR_SQLITE", "DeleteLayer(%s)", osLayerName.c_str() ); + + delete papoLayers[iLayer]; + memmove( papoLayers + iLayer, papoLayers + iLayer + 1, + sizeof(void *) * (nLayers - iLayer - 1) ); + nLayers--; + +/* -------------------------------------------------------------------- */ +/* Remove from the database. */ +/* -------------------------------------------------------------------- */ + int rc; + char *pszErrMsg; + + CPLString osEscapedLayerName = OGRSQLiteEscape(osLayerName); + const char* pszEscapedLayerName = osEscapedLayerName.c_str(); + const char* pszGeometryColumn = osGeometryColumn.size() ? osGeometryColumn.c_str() : NULL; + + rc = sqlite3_exec( hDB, CPLSPrintf( "DROP TABLE '%s'", pszEscapedLayerName ), + NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to drop table %s: %s", + osLayerName.c_str(), pszErrMsg ); + sqlite3_free( pszErrMsg ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Drop from geometry_columns table. */ +/* -------------------------------------------------------------------- */ + if( bHaveGeometryColumns ) + { + CPLString osCommand; + + osCommand.Printf( + "DELETE FROM geometry_columns WHERE f_table_name = '%s'", + pszEscapedLayerName ); + + rc = sqlite3_exec( hDB, osCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Removal from geometry_columns failed.\n%s: %s", + osCommand.c_str(), pszErrMsg ); + sqlite3_free( pszErrMsg ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Drop spatialite spatial index tables */ +/* -------------------------------------------------------------------- */ + if( bIsSpatiaLiteDB && pszGeometryColumn ) + { + osCommand.Printf( "DROP TABLE 'idx_%s_%s'", pszEscapedLayerName, + OGRSQLiteEscape(pszGeometryColumn).c_str()); + rc = sqlite3_exec( hDB, osCommand, NULL, NULL, NULL ); + + osCommand.Printf( "DROP TABLE 'idx_%s_%s_node'", pszEscapedLayerName, + OGRSQLiteEscape(pszGeometryColumn).c_str()); + rc = sqlite3_exec( hDB, osCommand, NULL, NULL, NULL ); + + osCommand.Printf( "DROP TABLE 'idx_%s_%s_parent'", pszEscapedLayerName, + OGRSQLiteEscape(pszGeometryColumn).c_str()); + rc = sqlite3_exec( hDB, osCommand, NULL, NULL, NULL ); + + osCommand.Printf( "DROP TABLE 'idx_%s_%s_rowid'", pszEscapedLayerName, + OGRSQLiteEscape(pszGeometryColumn).c_str()); + rc = sqlite3_exec( hDB, osCommand, NULL, NULL, NULL ); + } + } + return OGRERR_NONE; +} + + +/************************************************************************/ +/* StartTransaction() */ +/* */ +/* Should only be called by user code. Not driver internals. */ +/************************************************************************/ + +OGRErr OGRSQLiteBaseDataSource::StartTransaction(CPL_UNUSED int bForce) +{ + if( bUserTransactionActive || nSoftTransactionLevel != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Transaction already established"); + return OGRERR_FAILURE; + } + + OGRErr eErr = SoftStartTransaction(); + if( eErr != OGRERR_NONE ) + return eErr; + + bUserTransactionActive = TRUE; + return OGRERR_NONE; +} + +/************************************************************************/ +/* CommitTransaction() */ +/* */ +/* Should only be called by user code. Not driver internals. */ +/************************************************************************/ + +OGRErr OGRSQLiteBaseDataSource::CommitTransaction() +{ + if( !bUserTransactionActive ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established"); + return OGRERR_FAILURE; + } + + bUserTransactionActive = FALSE; + CPLAssert( nSoftTransactionLevel == 1 ); + return SoftCommitTransaction(); +} + +OGRErr OGRSQLiteDataSource::CommitTransaction() + +{ + if( nSoftTransactionLevel == 1 ) + { + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( papoLayers[iLayer]->IsTableLayer() ) + { + OGRSQLiteTableLayer* poLayer = (OGRSQLiteTableLayer*) papoLayers[iLayer]; + poLayer->RunDeferredCreationIfNecessary(); + //poLayer->CreateSpatialIndexIfNecessary(); + } + } + } + + return OGRSQLiteBaseDataSource::CommitTransaction(); +} + +/************************************************************************/ +/* RollbackTransaction() */ +/* */ +/* Should only be called by user code. Not driver internals. */ +/************************************************************************/ + +OGRErr OGRSQLiteBaseDataSource::RollbackTransaction() +{ + if( !bUserTransactionActive ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Transaction not established"); + return OGRERR_FAILURE; + } + + bUserTransactionActive = FALSE; + CPLAssert( nSoftTransactionLevel == 1 ); + return SoftRollbackTransaction(); +} + +OGRErr OGRSQLiteDataSource::RollbackTransaction() + +{ + if( nSoftTransactionLevel == 1 ) + { + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( papoLayers[iLayer]->IsTableLayer() ) + { + OGRSQLiteTableLayer* poLayer = (OGRSQLiteTableLayer*) papoLayers[iLayer]; + poLayer->RunDeferredCreationIfNecessary(); + poLayer->CreateSpatialIndexIfNecessary(); + } + } + + for(int i = 0; i < nLayers; i++) + { + papoLayers[i]->InvalidateCachedFeatureCountAndExtent(); + papoLayers[i]->ResetReading(); + } + } + + return OGRSQLiteBaseDataSource::RollbackTransaction(); +} + +/************************************************************************/ +/* SoftStartTransaction() */ +/* */ +/* Create a transaction scope. If we already have a */ +/* transaction active this isn't a real transaction, but just */ +/* an increment to the scope count. */ +/************************************************************************/ + +OGRErr OGRSQLiteBaseDataSource::SoftStartTransaction() + +{ + nSoftTransactionLevel++; + + OGRErr eErr = OGRERR_NONE; + if( nSoftTransactionLevel == 1 ) + { + eErr = DoTransactionCommand("BEGIN"); + } + + //CPLDebug("SQLite", "%p->SoftStartTransaction() : %d", + // this, nSoftTransactionLevel); + + return eErr; +} + +/************************************************************************/ +/* SoftCommitTransaction() */ +/* */ +/* Commit the current transaction if we are at the outer */ +/* scope. */ +/************************************************************************/ + +OGRErr OGRSQLiteBaseDataSource::SoftCommitTransaction() + +{ + //CPLDebug("SQLite", "%p->SoftCommitTransaction() : %d", + // this, nSoftTransactionLevel); + + if( nSoftTransactionLevel <= 0 ) + { + CPLAssert(FALSE); + return OGRERR_FAILURE; + } + + OGRErr eErr = OGRERR_NONE; + nSoftTransactionLevel--; + if( nSoftTransactionLevel == 0 ) + { + eErr = DoTransactionCommand("COMMIT"); + } + + return eErr; +} + +/************************************************************************/ +/* SoftRollbackTransaction() */ +/* */ +/* Do a rollback of the current transaction if we are at the 1st */ +/* level */ +/************************************************************************/ + +OGRErr OGRSQLiteBaseDataSource::SoftRollbackTransaction() + +{ + //CPLDebug("SQLite", "%p->SoftRollbackTransaction() : %d", + // this, nSoftTransactionLevel); + + if( nSoftTransactionLevel <= 0 ) + { + CPLAssert(FALSE); + return OGRERR_FAILURE; + } + + OGRErr eErr = OGRERR_NONE; + nSoftTransactionLevel--; + if( nSoftTransactionLevel == 0 ) + { + eErr = DoTransactionCommand("ROLLBACK"); + } + + return eErr; +} + +/************************************************************************/ +/* DoTransactionCommand() */ +/************************************************************************/ + +OGRErr OGRSQLiteBaseDataSource::DoTransactionCommand(const char* pszCommand) + +{ + int rc; + char *pszErrMsg; + +#ifdef DEBUG + CPLDebug( "OGR_SQLITE", "%s Transaction", pszCommand ); +#endif + + rc = sqlite3_exec( hDB, pszCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + nSoftTransactionLevel--; + CPLError( CE_Failure, CPLE_AppDefined, + "%s transaction failed: %s", + pszCommand, pszErrMsg ); + sqlite3_free( pszErrMsg ); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetSRTEXTColName() */ +/************************************************************************/ + +const char* OGRSQLiteDataSource::GetSRTEXTColName() +{ + if( !bIsSpatiaLiteDB || bSpatialite4Layout ) + return "srtext"; + +/* testing for SRS_WKT column presence */ + int bHasSrsWkt = FALSE; + char **papszResult; + int nRowCount, nColCount; + char *pszErrMsg = NULL; + int rc = sqlite3_get_table( hDB, "PRAGMA table_info(spatial_ref_sys)", + &papszResult, &nRowCount, &nColCount, + &pszErrMsg ); + + if( rc == SQLITE_OK ) + { + int iRow; + for (iRow = 1; iRow <= nRowCount; iRow++) + { + if (EQUAL("srs_wkt", + papszResult[(iRow * nColCount) + 1])) + bHasSrsWkt = TRUE; + } + sqlite3_free_table(papszResult); + } + else + { + sqlite3_free( pszErrMsg ); + } + + return bHasSrsWkt ? "srs_wkt" : NULL; +} + +/************************************************************************/ +/* AddSRIDToCache() */ +/* */ +/* Note: this will not add a reference on the poSRS object. Make */ +/* sure it is freshly created, or add a reference yourself if not. */ +/************************************************************************/ + +void OGRSQLiteDataSource::AddSRIDToCache(int nId, OGRSpatialReference * poSRS ) +{ +/* -------------------------------------------------------------------- */ +/* Add to the cache. */ +/* -------------------------------------------------------------------- */ + panSRID = (int *) CPLRealloc(panSRID,sizeof(int) * (nKnownSRID+1) ); + papoSRS = (OGRSpatialReference **) + CPLRealloc(papoSRS, sizeof(void*) * (nKnownSRID + 1) ); + panSRID[nKnownSRID] = nId; + papoSRS[nKnownSRID] = poSRS; + nKnownSRID++; +} + +/************************************************************************/ +/* FetchSRSId() */ +/* */ +/* Fetch the id corresponding to an SRS, and if not found, add */ +/* it to the table. */ +/************************************************************************/ + +int OGRSQLiteDataSource::FetchSRSId( OGRSpatialReference * poSRS ) + +{ + int nSRSId = nUndefinedSRID; + const char *pszAuthorityName, *pszAuthorityCode = NULL; + CPLString osCommand; + char *pszErrMsg; + int rc; + char **papszResult; + int nRowCount, nColCount; + + if( poSRS == NULL ) + return nSRSId; + +/* -------------------------------------------------------------------- */ +/* First, we look through our SRID cache, is it there? */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nKnownSRID; i++ ) + { + if( papoSRS[i] == poSRS ) + return panSRID[i]; + } + for( i = 0; i < nKnownSRID; i++ ) + { + if( papoSRS[i] != NULL && papoSRS[i]->IsSame(poSRS) ) + return panSRID[i]; + } + +/* -------------------------------------------------------------------- */ +/* Build a copy since we may call AutoIdentifyEPSG() */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference oSRS(*poSRS); + poSRS = NULL; + + pszAuthorityName = oSRS.GetAuthorityName(NULL); + + if( pszAuthorityName == NULL || strlen(pszAuthorityName) == 0 ) + { +/* -------------------------------------------------------------------- */ +/* Try to identify an EPSG code */ +/* -------------------------------------------------------------------- */ + oSRS.AutoIdentifyEPSG(); + + pszAuthorityName = oSRS.GetAuthorityName(NULL); + if (pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG")) + { + pszAuthorityCode = oSRS.GetAuthorityCode(NULL); + if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 ) + { + /* Import 'clean' SRS */ + oSRS.importFromEPSG( atoi(pszAuthorityCode) ); + + pszAuthorityName = oSRS.GetAuthorityName(NULL); + pszAuthorityCode = oSRS.GetAuthorityCode(NULL); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Check whether the EPSG authority code is already mapped to a */ +/* SRS ID. */ +/* -------------------------------------------------------------------- */ + if( pszAuthorityName != NULL && strlen(pszAuthorityName) > 0 ) + { + pszAuthorityCode = oSRS.GetAuthorityCode(NULL); + + if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 ) + { + // XXX: We are using case insensitive comparison for "auth_name" + // values, because there are variety of options exist. By default + // the driver uses 'EPSG' in upper case, but SpatiaLite extension + // uses 'epsg' in lower case. + osCommand.Printf( "SELECT srid FROM spatial_ref_sys WHERE " + "auth_name = '%s' COLLATE NOCASE AND auth_srid = '%s'", + pszAuthorityName, pszAuthorityCode ); + + rc = sqlite3_get_table( hDB, osCommand, &papszResult, + &nRowCount, &nColCount, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + /* Retry without COLLATE NOCASE which may not be understood by older sqlite3 */ + sqlite3_free( pszErrMsg ); + + osCommand.Printf( "SELECT srid FROM spatial_ref_sys WHERE " + "auth_name = '%s' AND auth_srid = '%s'", + pszAuthorityName, pszAuthorityCode ); + + rc = sqlite3_get_table( hDB, osCommand, &papszResult, + &nRowCount, &nColCount, &pszErrMsg ); + + /* Retry in lower case for SpatiaLite */ + if( rc != SQLITE_OK ) + { + sqlite3_free( pszErrMsg ); + } + else if ( nRowCount == 0 && + strcmp(pszAuthorityName, "EPSG") == 0) + { + /* If it's in upper case, look for lower case */ + sqlite3_free_table(papszResult); + + osCommand.Printf( "SELECT srid FROM spatial_ref_sys WHERE " + "auth_name = 'epsg' AND auth_srid = '%s'", + pszAuthorityCode ); + + rc = sqlite3_get_table( hDB, osCommand, &papszResult, + &nRowCount, &nColCount, &pszErrMsg ); + + if( rc != SQLITE_OK ) + { + sqlite3_free( pszErrMsg ); + } + } + } + + if( rc == SQLITE_OK && nRowCount == 1 ) + { + nSRSId = (papszResult[1] != NULL) ? atoi(papszResult[1]) : nUndefinedSRID; + sqlite3_free_table(papszResult); + + if( nSRSId != nUndefinedSRID) + AddSRIDToCache(nSRSId, new OGRSpatialReference(oSRS)); + + return nSRSId; + } + sqlite3_free_table(papszResult); + } + } + +/* -------------------------------------------------------------------- */ +/* Search for existing record using either WKT definition or */ +/* PROJ.4 string (SpatiaLite variant). */ +/* -------------------------------------------------------------------- */ + CPLString osWKT, osProj4; + +/* -------------------------------------------------------------------- */ +/* Translate SRS to WKT. */ +/* -------------------------------------------------------------------- */ + char *pszWKT = NULL; + + if( oSRS.exportToWkt( &pszWKT ) != OGRERR_NONE ) + { + CPLFree(pszWKT); + return nUndefinedSRID; + } + + osWKT = pszWKT; + CPLFree( pszWKT ); + pszWKT = NULL; + + const char* pszSRTEXTColName = GetSRTEXTColName(); + + if ( pszSRTEXTColName != NULL ) + { +/* -------------------------------------------------------------------- */ +/* Try to find based on the WKT match. */ +/* -------------------------------------------------------------------- */ + osCommand.Printf( "SELECT srid FROM spatial_ref_sys WHERE \"%s\" = ?", + OGRSQLiteEscapeName(pszSRTEXTColName).c_str()); + } + +/* -------------------------------------------------------------------- */ +/* Handle SpatiaLite (< 4) flavour of the spatial_ref_sys. */ +/* -------------------------------------------------------------------- */ + else + { +/* -------------------------------------------------------------------- */ +/* Translate SRS to PROJ.4 string. */ +/* -------------------------------------------------------------------- */ + char *pszProj4 = NULL; + + if( oSRS.exportToProj4( &pszProj4 ) != OGRERR_NONE ) + { + CPLFree(pszProj4); + return nUndefinedSRID; + } + + osProj4 = pszProj4; + CPLFree( pszProj4 ); + pszProj4 = NULL; + +/* -------------------------------------------------------------------- */ +/* Try to find based on the PROJ.4 match. */ +/* -------------------------------------------------------------------- */ + osCommand.Printf( + "SELECT srid FROM spatial_ref_sys WHERE proj4text = ?"); + } + + sqlite3_stmt *hSelectStmt = NULL; + rc = sqlite3_prepare( hDB, osCommand, -1, &hSelectStmt, NULL ); + + if( rc == SQLITE_OK) + rc = sqlite3_bind_text( hSelectStmt, 1, + ( pszSRTEXTColName != NULL ) ? osWKT.c_str() : osProj4.c_str(), + -1, SQLITE_STATIC ); + + if( rc == SQLITE_OK) + rc = sqlite3_step( hSelectStmt ); + + if (rc == SQLITE_ROW) + { + if (sqlite3_column_type( hSelectStmt, 0 ) == SQLITE_INTEGER) + nSRSId = sqlite3_column_int( hSelectStmt, 0 ); + else + nSRSId = nUndefinedSRID; + + sqlite3_finalize( hSelectStmt ); + + if( nSRSId != nUndefinedSRID) + AddSRIDToCache(nSRSId, new OGRSpatialReference(oSRS)); + + return nSRSId; + } + +/* -------------------------------------------------------------------- */ +/* If the command actually failed, then the metadata table is */ +/* likely missing, so we give up. */ +/* -------------------------------------------------------------------- */ + if (rc != SQLITE_DONE && rc != SQLITE_ROW) + { + sqlite3_finalize( hSelectStmt ); + return nUndefinedSRID; + } + + sqlite3_finalize( hSelectStmt ); + +/* -------------------------------------------------------------------- */ +/* Translate SRS to PROJ.4 string (if not already done) */ +/* -------------------------------------------------------------------- */ + if( osProj4.size() == 0 ) + { + char* pszProj4 = NULL; + if( oSRS.exportToProj4( &pszProj4 ) == OGRERR_NONE ) + { + osProj4 = pszProj4; + CPLFree( pszProj4 ); + pszProj4 = NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* If we have an authority code try to assign SRS ID the same */ +/* as that code. */ +/* -------------------------------------------------------------------- */ + if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 ) + { + osCommand.Printf( "SELECT * FROM spatial_ref_sys WHERE auth_srid='%s'", + OGRSQLiteEscape(pszAuthorityCode).c_str() ); + rc = sqlite3_get_table( hDB, osCommand, &papszResult, + &nRowCount, &nColCount, &pszErrMsg ); + + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "exec(SELECT '%s' FROM spatial_ref_sys) failed: %s", + pszAuthorityCode, pszErrMsg ); + sqlite3_free( pszErrMsg ); + } + +/* -------------------------------------------------------------------- */ +/* If there is no SRS ID with such auth_srid, use it as SRS ID. */ +/* -------------------------------------------------------------------- */ + if ( nRowCount < 1 ) + { + nSRSId = atoi(pszAuthorityCode); + /* The authority code might be non numeric, e.g. IGNF:LAMB93 */ + /* in which case we might fallback to the fake OGR authority */ + /* for spatialite, since its auth_srid is INTEGER */ + if( nSRSId == 0 ) + { + nSRSId = nUndefinedSRID; + if( bIsSpatiaLiteDB ) + pszAuthorityName = NULL; + } + } + sqlite3_free_table(papszResult); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise get the current maximum srid in the srs table. */ +/* -------------------------------------------------------------------- */ + if ( nSRSId == nUndefinedSRID ) + { + rc = sqlite3_get_table( hDB, "SELECT MAX(srid) FROM spatial_ref_sys", + &papszResult, &nRowCount, &nColCount, + &pszErrMsg ); + + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SELECT of the maximum SRS ID failed: %s", pszErrMsg ); + sqlite3_free( pszErrMsg ); + return nUndefinedSRID; + } + + if ( nRowCount < 1 || !papszResult[1] ) + nSRSId = 50000; + else + nSRSId = atoi(papszResult[1]) + 1; // Insert as the next SRS ID + sqlite3_free_table(papszResult); + } + +/* -------------------------------------------------------------------- */ +/* Try adding the SRS to the SRS table. */ +/* -------------------------------------------------------------------- */ + + const char* apszToInsert[] = { NULL, NULL, NULL, NULL, NULL, NULL }; + + if ( !bIsSpatiaLiteDB ) + { + if( pszAuthorityName != NULL ) + { + osCommand.Printf( + "INSERT INTO spatial_ref_sys (srid,srtext,auth_name,auth_srid) " + " VALUES (%d, ?, ?, ?)", + nSRSId ); + apszToInsert[0] = osWKT.c_str(); + apszToInsert[1] = pszAuthorityName; + apszToInsert[2] = pszAuthorityCode; + } + else + { + osCommand.Printf( + "INSERT INTO spatial_ref_sys (srid,srtext) " + " VALUES (%d, ?)", + nSRSId ); + apszToInsert[0] = osWKT.c_str(); + } + } + else + { + CPLString osSRTEXTColNameWithCommaBefore; + if( pszSRTEXTColName != NULL ) + osSRTEXTColNameWithCommaBefore.Printf(", %s", pszSRTEXTColName); + + const char *pszProjCS = oSRS.GetAttrValue("PROJCS"); + if (pszProjCS == NULL) + pszProjCS = oSRS.GetAttrValue("GEOGCS"); + + if( pszAuthorityName != NULL ) + { + if ( pszProjCS ) + { + osCommand.Printf( + "INSERT INTO spatial_ref_sys " + "(srid, auth_name, auth_srid, ref_sys_name, proj4text%s) " + "VALUES (%d, ?, ?, ?, ?%s)", + (pszSRTEXTColName != NULL) ? osSRTEXTColNameWithCommaBefore.c_str() : "", + nSRSId, + (pszSRTEXTColName != NULL) ? ", ?" : ""); + apszToInsert[0] = pszAuthorityName; + apszToInsert[1] = pszAuthorityCode; + apszToInsert[2] = pszProjCS; + apszToInsert[3] = osProj4.c_str(); + apszToInsert[4] = (pszSRTEXTColName != NULL) ? osWKT.c_str() : NULL; + } + else + { + osCommand.Printf( + "INSERT INTO spatial_ref_sys " + "(srid, auth_name, auth_srid, proj4text%s) " + "VALUES (%d, ?, ?, ?%s)", + (pszSRTEXTColName != NULL) ? osSRTEXTColNameWithCommaBefore.c_str() : "", + nSRSId, + (pszSRTEXTColName != NULL) ? ", ?" : ""); + apszToInsert[0] = pszAuthorityName; + apszToInsert[1] = pszAuthorityCode; + apszToInsert[2] = osProj4.c_str(); + apszToInsert[3] = (pszSRTEXTColName != NULL) ? osWKT.c_str() : NULL; + } + } + else + { + /* SpatiaLite spatial_ref_sys auth_name and auth_srid columns must be NOT NULL */ + /* so insert within a fake OGR "authority" */ + if ( pszProjCS ) + { + osCommand.Printf( + "INSERT INTO spatial_ref_sys " + "(srid, auth_name, auth_srid, ref_sys_name, proj4text%s) VALUES (%d, 'OGR', %d, ?, ?%s)", + (pszSRTEXTColName != NULL) ? osSRTEXTColNameWithCommaBefore.c_str() : "", + nSRSId, nSRSId, + (pszSRTEXTColName != NULL) ? ", ?" : ""); + apszToInsert[0] = pszProjCS; + apszToInsert[1] = osProj4.c_str(); + apszToInsert[2] = (pszSRTEXTColName != NULL) ? osWKT.c_str() : NULL; + } + else + { + osCommand.Printf( + "INSERT INTO spatial_ref_sys " + "(srid, auth_name, auth_srid, proj4text%s) VALUES (%d, 'OGR', %d, ?%s)", + (pszSRTEXTColName != NULL) ? osSRTEXTColNameWithCommaBefore.c_str() : "", + nSRSId, nSRSId, + (pszSRTEXTColName != NULL) ? ", ?" : ""); + apszToInsert[0] = osProj4.c_str(); + apszToInsert[1] = (pszSRTEXTColName != NULL) ? osWKT.c_str() : NULL; + } + } + } + + sqlite3_stmt *hInsertStmt = NULL; + rc = sqlite3_prepare( hDB, osCommand, -1, &hInsertStmt, NULL ); + + for(i=0;apszToInsert[i]!=NULL;i++) + { + if( rc == SQLITE_OK) + rc = sqlite3_bind_text( hInsertStmt, i+1, apszToInsert[i], -1, SQLITE_STATIC ); + } + + if( rc == SQLITE_OK) + rc = sqlite3_step( hInsertStmt ); + + if( rc != SQLITE_OK && rc != SQLITE_DONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to insert SRID (%s): %s", + osCommand.c_str(), sqlite3_errmsg(hDB) ); + + sqlite3_finalize( hInsertStmt ); + return FALSE; + } + + sqlite3_finalize( hInsertStmt ); + + if( nSRSId != nUndefinedSRID) + AddSRIDToCache(nSRSId, new OGRSpatialReference(oSRS)); + + return nSRSId; +} + +/************************************************************************/ +/* FetchSRS() */ +/* */ +/* Return a SRS corresponding to a particular id. Note that */ +/* reference counting should be honoured on the returned */ +/* OGRSpatialReference, as handles may be cached. */ +/************************************************************************/ + +OGRSpatialReference *OGRSQLiteDataSource::FetchSRS( int nId ) + +{ + if( nId <= 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* First, we look through our SRID cache, is it there? */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nKnownSRID; i++ ) + { + if( panSRID[i] == nId ) + return papoSRS[i]; + } + +/* -------------------------------------------------------------------- */ +/* Try looking up in spatial_ref_sys table. */ +/* -------------------------------------------------------------------- */ + char *pszErrMsg; + int rc; + char **papszResult; + int nRowCount, nColCount; + CPLString osCommand; + OGRSpatialReference *poSRS = NULL; + + osCommand.Printf( "SELECT srtext FROM spatial_ref_sys WHERE srid = %d", + nId ); + rc = sqlite3_get_table( hDB, osCommand, + &papszResult, &nRowCount, &nColCount, &pszErrMsg ); + + if ( rc == SQLITE_OK ) + { + if( nRowCount < 1 ) + { + sqlite3_free_table(papszResult); + return NULL; + } + + char** papszRow = papszResult + nColCount; + if (papszRow[0] != NULL) + { + CPLString osWKT = papszRow[0]; + +/* -------------------------------------------------------------------- */ +/* Translate into a spatial reference. */ +/* -------------------------------------------------------------------- */ + char *pszWKT = (char *) osWKT.c_str(); + + poSRS = new OGRSpatialReference(); + if( poSRS->importFromWkt( &pszWKT ) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + + sqlite3_free_table(papszResult); + } + +/* -------------------------------------------------------------------- */ +/* Next try SpatiaLite flavour. SpatiaLite uses PROJ.4 strings */ +/* in 'proj4text' column instead of WKT in 'srtext'. Note: recent */ +/* versions of spatialite have a srs_wkt column too */ +/* -------------------------------------------------------------------- */ + else + { + sqlite3_free( pszErrMsg ); + pszErrMsg = NULL; + + const char* pszSRTEXTColName = GetSRTEXTColName(); + CPLString osSRTEXTColNameWithCommaBefore; + if( pszSRTEXTColName != NULL ) + osSRTEXTColNameWithCommaBefore.Printf(", %s", pszSRTEXTColName); + + osCommand.Printf( + "SELECT proj4text, auth_name, auth_srid%s FROM spatial_ref_sys WHERE srid = %d", + (pszSRTEXTColName != NULL) ? osSRTEXTColNameWithCommaBefore.c_str() : "", nId ); + rc = sqlite3_get_table( hDB, osCommand, + &papszResult, &nRowCount, + &nColCount, &pszErrMsg ); + if ( rc == SQLITE_OK ) + { + if( nRowCount < 1 ) + { + sqlite3_free_table(papszResult); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Translate into a spatial reference. */ +/* -------------------------------------------------------------------- */ + char** papszRow = papszResult + nColCount; + + const char* pszProj4Text = papszRow[0]; + const char* pszAuthName = papszRow[1]; + int nAuthSRID = (papszRow[2] != NULL) ? atoi(papszRow[2]) : 0; + char* pszWKT = (pszSRTEXTColName != NULL) ? (char*) papszRow[3] : NULL; + + poSRS = new OGRSpatialReference(); + + /* Try first from EPSG code */ + if (pszAuthName != NULL && + EQUAL(pszAuthName, "EPSG") && + poSRS->importFromEPSG( nAuthSRID ) == OGRERR_NONE) + { + /* Do nothing */ + } + /* Then from WKT string */ + else if( pszWKT != NULL && + poSRS->importFromWkt( &pszWKT ) == OGRERR_NONE ) + { + /* Do nothing */ + } + /* Finally from Proj4 string */ + else if( pszProj4Text != NULL && + poSRS->importFromProj4( pszProj4Text ) == OGRERR_NONE ) + { + /* Do nothing */ + } + else + { + delete poSRS; + poSRS = NULL; + } + + sqlite3_free_table(papszResult); + } + +/* -------------------------------------------------------------------- */ +/* No success, report an error. */ +/* -------------------------------------------------------------------- */ + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s: %s", osCommand.c_str(), pszErrMsg ); + sqlite3_free( pszErrMsg ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Add to the cache. */ +/* -------------------------------------------------------------------- */ + AddSRIDToCache(nId, poSRS); + + return poSRS; +} + +/************************************************************************/ +/* SetName() */ +/************************************************************************/ + +void OGRSQLiteDataSource::SetName(const char* pszNameIn) +{ + CPLFree(m_pszFilename); + m_pszFilename = CPLStrdup(pszNameIn); +} + +/************************************************************************/ +/* GetEnvelopeFromSQL() */ +/************************************************************************/ + +const OGREnvelope* OGRSQLiteBaseDataSource::GetEnvelopeFromSQL(const CPLString& osSQL) +{ + std::map::iterator oIter = oMapSQLEnvelope.find(osSQL); + if (oIter != oMapSQLEnvelope.end()) + return &oIter->second; + else + return NULL; +} + +/************************************************************************/ +/* SetEnvelopeForSQL() */ +/************************************************************************/ + +void OGRSQLiteBaseDataSource::SetEnvelopeForSQL(const CPLString& osSQL, + const OGREnvelope& oEnvelope) +{ + oMapSQLEnvelope[osSQL] = oEnvelope; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitedriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitedriver.cpp new file mode 100644 index 000000000..b2ee95736 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitedriver.cpp @@ -0,0 +1,290 @@ +/****************************************************************************** + * $Id: ogrsqlitedriver.cpp 29265 2015-05-29 10:49:34Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRSQLiteDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * + * Contributor: Alessandro Furieri, a.furieri@lqt.it + * Portions of this module properly supporting SpatiaLite DB creation + * Developed for Faunalia ( http://www.faunalia.it) with funding from + * Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sqlite.h" +#include "cpl_conv.h" + +#ifdef HAVE_SPATIALITE +#include "spatialite.h" +#endif + +CPL_CVSID("$Id: ogrsqlitedriver.cpp 29265 2015-05-29 10:49:34Z rouault $"); + +/************************************************************************/ +/* OGRSQLiteDriverUnload() */ +/************************************************************************/ + +static void OGRSQLiteDriverUnload(CPL_UNUSED GDALDriver* poDriver) +{ +#ifdef SPATIALITE_412_OR_LATER + spatialite_shutdown(); +#endif +} + +/************************************************************************/ +/* OGRSQLiteDriverIdentify() */ +/************************************************************************/ + +static int OGRSQLiteDriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + int nLen = (int) strlen(poOpenInfo->pszFilename); + if (EQUALN(poOpenInfo->pszFilename, "VirtualShape:", strlen( "VirtualShape:" )) && + nLen > 4 && EQUAL(poOpenInfo->pszFilename + nLen - 4, ".SHP")) + { + return TRUE; + } + + if( EQUAL(poOpenInfo->pszFilename, ":memory:") ) + return TRUE; +/* -------------------------------------------------------------------- */ +/* Verify that the target is a real file, and has an */ +/* appropriate magic string at the beginning. */ +/* -------------------------------------------------------------------- */ + if( poOpenInfo->nHeaderBytes < 16 ) + return FALSE; + + if( strncmp( (const char*)poOpenInfo->pabyHeader, "SQLite format 3", 15 ) != 0 ) + return FALSE; + + // Could be a Rasterlite file as well + return -1; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRSQLiteDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( OGRSQLiteDriverIdentify(poOpenInfo) == FALSE ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Check VirtualShape:xxx.shp syntax */ +/* -------------------------------------------------------------------- */ + int nLen = (int) strlen(poOpenInfo->pszFilename); + if (EQUALN(poOpenInfo->pszFilename, "VirtualShape:", strlen( "VirtualShape:" )) && + nLen > 4 && EQUAL(poOpenInfo->pszFilename + nLen - 4, ".SHP")) + { + OGRSQLiteDataSource *poDS; + + poDS = new OGRSQLiteDataSource(); + + char** papszOptions = CSLAddString(NULL, "SPATIALITE=YES"); + int nRet = poDS->Create( ":memory:", papszOptions ); + poDS->SetDescription(poOpenInfo->pszFilename); + CSLDestroy(papszOptions); + if (!nRet) + { + delete poDS; + return NULL; + } + + char* pszSQLiteFilename = CPLStrdup(poOpenInfo->pszFilename + strlen( "VirtualShape:" )); + GDALDataset* poSQLiteDS = (GDALDataset*) GDALOpenEx(pszSQLiteFilename, + GDAL_OF_VECTOR, NULL, NULL, NULL); + if (poSQLiteDS == NULL) + { + CPLFree(pszSQLiteFilename); + delete poDS; + return NULL; + } + delete poSQLiteDS; + + char* pszLastDot = strrchr(pszSQLiteFilename, '.'); + if (pszLastDot) + *pszLastDot = '\0'; + + const char* pszTableName = CPLGetBasename(pszSQLiteFilename); + + char* pszSQL = CPLStrdup(CPLSPrintf("CREATE VIRTUAL TABLE %s USING VirtualShape(%s, CP1252, -1)", + pszTableName, pszSQLiteFilename)); + poDS->ExecuteSQL(pszSQL, NULL, NULL); + CPLFree(pszSQL); + CPLFree(pszSQLiteFilename); + poDS->SetUpdate(FALSE); + return poDS; + } + +/* -------------------------------------------------------------------- */ +/* We think this is really an SQLite database, go ahead and try */ +/* and open it. */ +/* -------------------------------------------------------------------- */ + OGRSQLiteDataSource *poDS; + + poDS = new OGRSQLiteDataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename, poOpenInfo->eAccess == GA_Update, + poOpenInfo->papszOpenOptions ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRSQLiteDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ +/* -------------------------------------------------------------------- */ +/* First, ensure there isn't any such file yet. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + + if( VSIStatL( pszName, &sStatBuf ) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "It seems a file system object called '%s' already exists.", + pszName ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to create datasource. */ +/* -------------------------------------------------------------------- */ + OGRSQLiteDataSource *poDS; + + poDS = new OGRSQLiteDataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* Delete() */ +/************************************************************************/ + +CPLErr OGRSQLiteDriverDelete( const char *pszName ) +{ + if (VSIUnlink( pszName ) == 0) + return CE_None; + else + return CE_Failure; +} + +/************************************************************************/ +/* RegisterOGRSQLite() */ +/************************************************************************/ + +void RegisterOGRSQLite() + +{ + if (! GDAL_CHECK_VERSION("SQLite driver")) + return; + GDALDriver *poDriver; + + if( GDALGetDriverByName( "SQLite" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SQLite" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "SQLite / Spatialite" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_sqlite.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "sqlite db" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, +"" +#ifdef HAVE_SPATIALITE +" "); + + poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST, +"" +" " +" "); + + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime Time Binary" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_DEFAULT_FIELDS, "YES" ); + poDriver->SetMetadataItem( GDAL_DCAP_NOTNULL_GEOMFIELDS, "YES" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRSQLiteDriverOpen; + poDriver->pfnIdentify = OGRSQLiteDriverIdentify; + poDriver->pfnCreate = OGRSQLiteDriverCreate; + poDriver->pfnDelete = OGRSQLiteDriverDelete; + poDriver->pfnUnloadDriver = OGRSQLiteDriverUnload; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteexecutesql.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteexecutesql.cpp new file mode 100644 index 000000000..2f001e120 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteexecutesql.cpp @@ -0,0 +1,1072 @@ +/****************************************************************************** + * $Id: ogrsqliteexecutesql.cpp 28612 2015-03-04 15:22:08Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Run SQL requests with SQLite SQL engine + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sqlite.h" +#include "ogr_api.h" +#include "ogrsqlitevirtualogr.h" +#include "ogrsqliteexecutesql.h" +#include "cpl_multiproc.h" + +#ifdef HAVE_SQLITE_VFS + +/************************************************************************/ +/* OGRSQLiteExecuteSQLLayer */ +/************************************************************************/ + +class OGRSQLiteExecuteSQLLayer: public OGRSQLiteSelectLayer +{ + char *pszTmpDBName; + + public: + OGRSQLiteExecuteSQLLayer(char* pszTmpDBName, + OGRSQLiteDataSource* poDS, + CPLString osSQL, + sqlite3_stmt * hStmt, + int bUseStatementForGetNextFeature, + int bEmptyLayer ); + virtual ~OGRSQLiteExecuteSQLLayer(); +}; + +/************************************************************************/ +/* OGRSQLiteExecuteSQLLayer() */ +/************************************************************************/ + +OGRSQLiteExecuteSQLLayer::OGRSQLiteExecuteSQLLayer(char* pszTmpDBName, + OGRSQLiteDataSource* poDS, + CPLString osSQL, + sqlite3_stmt * hStmt, + int bUseStatementForGetNextFeature, + int bEmptyLayer ) : + + OGRSQLiteSelectLayer(poDS, osSQL, hStmt, + bUseStatementForGetNextFeature, + bEmptyLayer, TRUE) +{ + this->pszTmpDBName = pszTmpDBName; +} + +/************************************************************************/ +/* ~OGRSQLiteExecuteSQLLayer() */ +/************************************************************************/ + +OGRSQLiteExecuteSQLLayer::~OGRSQLiteExecuteSQLLayer() +{ + /* This is a bit peculiar: we must "finalize" the OGRLayer, since */ + /* it has objects that depend on the datasource, that we are just */ + /* going to destroy afterwards. The issue here is that we destroy */ + /* our own datasource ! */ + Finalize(); + + delete poDS; + VSIUnlink(pszTmpDBName); + CPLFree(pszTmpDBName); +} + +/************************************************************************/ +/* OGR2SQLITEExtractUnquotedString() */ +/************************************************************************/ + +static CPLString OGR2SQLITEExtractUnquotedString(const char **ppszSQLCommand) +{ + CPLString osRet; + const char *pszSQLCommand = *ppszSQLCommand; + char chQuoteChar = 0; + + if( *pszSQLCommand == '"' || *pszSQLCommand == '\'' ) + { + chQuoteChar = *pszSQLCommand; + pszSQLCommand ++; + } + + while( *pszSQLCommand != '\0' ) + { + if( *pszSQLCommand == chQuoteChar && + pszSQLCommand[1] == chQuoteChar ) + { + pszSQLCommand ++; + osRet += chQuoteChar; + } + else if( *pszSQLCommand == chQuoteChar ) + { + pszSQLCommand ++; + break; + } + else if( chQuoteChar == '\0' && + (isspace((int)*pszSQLCommand) || + *pszSQLCommand == '.' || + *pszSQLCommand == ')' || + *pszSQLCommand == ',') ) + break; + else + osRet += *pszSQLCommand; + + pszSQLCommand ++; + } + + *ppszSQLCommand = pszSQLCommand; + + return osRet; +} + +/************************************************************************/ +/* OGR2SQLITEExtractLayerDesc() */ +/************************************************************************/ + +static +LayerDesc OGR2SQLITEExtractLayerDesc(const char **ppszSQLCommand) +{ + CPLString osStr; + const char *pszSQLCommand = *ppszSQLCommand; + LayerDesc oLayerDesc; + + while( isspace((int)*pszSQLCommand) ) + pszSQLCommand ++; + + const char* pszOriginalStrStart = pszSQLCommand; + oLayerDesc.osOriginalStr = pszSQLCommand; + + osStr = OGR2SQLITEExtractUnquotedString(&pszSQLCommand); + + if( *pszSQLCommand == '.' ) + { + oLayerDesc.osDSName = osStr; + pszSQLCommand ++; + oLayerDesc.osLayerName = + OGR2SQLITEExtractUnquotedString(&pszSQLCommand); + } + else + { + oLayerDesc.osLayerName = osStr; + } + + oLayerDesc.osOriginalStr.resize(pszSQLCommand - pszOriginalStrStart); + + *ppszSQLCommand = pszSQLCommand; + + return oLayerDesc; +} + +/************************************************************************/ +/* OGR2SQLITEAddLayer() */ +/************************************************************************/ + +static void OGR2SQLITEAddLayer( const char*& pszStart, int& nNum, + const char*& pszSQLCommand, + std::set& oSet, + CPLString& osModifiedSQL ) +{ + CPLString osTruncated(pszStart); + osTruncated.resize(pszSQLCommand - pszStart); + osModifiedSQL += osTruncated; + pszStart = pszSQLCommand; + LayerDesc oLayerDesc = OGR2SQLITEExtractLayerDesc(&pszSQLCommand); + int bInsert = TRUE; + if( oLayerDesc.osDSName.size() == 0 ) + { + osTruncated = pszStart; + osTruncated.resize(pszSQLCommand - pszStart); + osModifiedSQL += osTruncated; + } + else + { + std::set::iterator oIter = oSet.find(oLayerDesc); + if( oIter == oSet.end() ) + { + oLayerDesc.osSubstitutedName = CPLString().Printf("_OGR_%d", nNum ++); + osModifiedSQL += "\""; + osModifiedSQL += oLayerDesc.osSubstitutedName; + osModifiedSQL += "\""; + } + else + { + osModifiedSQL += (*oIter).osSubstitutedName; + bInsert = FALSE; + } + } + if( bInsert ) + { + oSet.insert(oLayerDesc); + } + pszStart = pszSQLCommand; +} + +/************************************************************************/ +/* StartsAsSQLITEKeyWord() */ +/************************************************************************/ + +static const char* apszKeywords[] = { + "WHERE", "GROUP", "ORDER", "JOIN", "UNION", "INTERSECT", "EXCEPT", "LIMIT" +}; + +static int StartsAsSQLITEKeyWord(const char* pszStr) +{ + int i; + for(i=0;i<(int)(sizeof(apszKeywords) / sizeof(char*));i++) + { + if( EQUALN(pszStr, apszKeywords[i], strlen(apszKeywords[i])) ) + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* OGR2SQLITEGetPotentialLayerNames() */ +/************************************************************************/ + +static void OGR2SQLITEGetPotentialLayerNamesInternal(const char **ppszSQLCommand, + std::set& oSetLayers, + std::set& oSetSpatialIndex, + CPLString& osModifiedSQL, + int& nNum) +{ + const char *pszSQLCommand = *ppszSQLCommand; + const char* pszStart = pszSQLCommand; + char ch; + int nParenthesisLevel = 0; + int bLookforFTableName = FALSE; + + while( (ch = *pszSQLCommand) != '\0' ) + { + if( ch == '(' ) + nParenthesisLevel ++; + else if( ch == ')' ) + { + nParenthesisLevel --; + if( nParenthesisLevel < 0 ) + { + pszSQLCommand ++; + break; + } + } + + /* Skip literals and strings */ + if( ch == '\'' || ch == '"' ) + { + char chEscapeChar = ch; + pszSQLCommand ++; + while( (ch = *pszSQLCommand) != '\0' ) + { + if( ch == chEscapeChar && pszSQLCommand[1] == chEscapeChar ) + pszSQLCommand ++; + else if( ch == chEscapeChar ) + { + pszSQLCommand ++; + break; + } + pszSQLCommand ++; + } + } + + else if( EQUALN(pszSQLCommand, "ogr_layer_", strlen("ogr_layer_")) ) + { + while( *pszSQLCommand != '\0' && *pszSQLCommand != '(' ) + pszSQLCommand ++; + + if( *pszSQLCommand != '(' ) + break; + + pszSQLCommand ++; + nParenthesisLevel ++; + + while( isspace((int)*pszSQLCommand) ) + pszSQLCommand ++; + + OGR2SQLITEAddLayer(pszStart, nNum, + pszSQLCommand, oSetLayers, osModifiedSQL); + } + + else if( bLookforFTableName && + EQUALN(pszSQLCommand, "f_table_name", strlen("f_table_name")) && + (pszSQLCommand[strlen("f_table_name")] == '=' || + isspace((int)pszSQLCommand[strlen("f_table_name")])) ) + { + pszSQLCommand += strlen("f_table_name"); + + while( isspace((int)*pszSQLCommand) ) + pszSQLCommand ++; + + if( *pszSQLCommand == '=' ) + { + pszSQLCommand ++; + + while( isspace((int)*pszSQLCommand) ) + pszSQLCommand ++; + + oSetSpatialIndex.insert(OGR2SQLITEExtractUnquotedString(&pszSQLCommand)); + } + + bLookforFTableName = FALSE; + } + + else if( EQUALN(pszSQLCommand, "FROM", strlen("FROM")) && + isspace(pszSQLCommand[strlen("FROM")]) ) + { + pszSQLCommand += strlen("FROM") + 1; + + while( isspace((int)*pszSQLCommand) ) + pszSQLCommand ++; + + if( EQUALN(pszSQLCommand, "SpatialIndex", strlen("SpatialIndex")) && + isspace((int)pszSQLCommand[strlen("SpatialIndex")]) ) + { + pszSQLCommand += strlen("SpatialIndex") + 1; + + bLookforFTableName = TRUE; + + continue; + } + + if( *pszSQLCommand == '(' ) + { + pszSQLCommand++; + + CPLString osTruncated(pszStart); + osTruncated.resize(pszSQLCommand - pszStart); + osModifiedSQL += osTruncated; + + OGR2SQLITEGetPotentialLayerNamesInternal( + &pszSQLCommand, oSetLayers, oSetSpatialIndex, + osModifiedSQL, nNum); + + pszStart = pszSQLCommand; + } + else + OGR2SQLITEAddLayer(pszStart, nNum, + pszSQLCommand, oSetLayers, osModifiedSQL); + + while( *pszSQLCommand != '\0' ) + { + if( isspace((int)*pszSQLCommand) ) + { + pszSQLCommand ++; + while( isspace((int)*pszSQLCommand) ) + pszSQLCommand ++; + + if( EQUALN(pszSQLCommand, "AS", 2) ) + { + pszSQLCommand += 2; + while( isspace((int)*pszSQLCommand) ) + pszSQLCommand ++; + } + + /* Skip alias */ + if( *pszSQLCommand != '\0' && + *pszSQLCommand != ',' ) + { + if ( StartsAsSQLITEKeyWord(pszSQLCommand) ) + break; + OGR2SQLITEExtractUnquotedString(&pszSQLCommand); + } + } + else if (*pszSQLCommand == ',' ) + { + pszSQLCommand ++; + while( isspace((int)*pszSQLCommand) ) + pszSQLCommand ++; + + if( *pszSQLCommand == '(' ) + { + pszSQLCommand++; + + CPLString osTruncated(pszStart); + osTruncated.resize(pszSQLCommand - pszStart); + osModifiedSQL += osTruncated; + + OGR2SQLITEGetPotentialLayerNamesInternal( + &pszSQLCommand, oSetLayers, + oSetSpatialIndex, + osModifiedSQL, nNum); + + pszStart = pszSQLCommand; + } + else + OGR2SQLITEAddLayer(pszStart, nNum, + pszSQLCommand, oSetLayers, + osModifiedSQL); + } + else + break; + } + } + else if ( EQUALN(pszSQLCommand, "JOIN", strlen("JOIN")) && + isspace(pszSQLCommand[strlen("JOIN")]) ) + { + pszSQLCommand += strlen("JOIN") + 1; + OGR2SQLITEAddLayer(pszStart, nNum, pszSQLCommand, + oSetLayers, osModifiedSQL); + } + else if( EQUALN(pszSQLCommand, "INTO", strlen("INTO")) && + isspace(pszSQLCommand[strlen("INTO")]) ) + { + pszSQLCommand += strlen("INTO") + 1; + OGR2SQLITEAddLayer(pszStart, nNum, pszSQLCommand, + oSetLayers, osModifiedSQL); + } + else if( EQUALN(pszSQLCommand, "UPDATE", strlen("UPDATE")) && + isspace(pszSQLCommand[strlen("UPDATE")]) ) + { + pszSQLCommand += strlen("UPDATE") + 1; + OGR2SQLITEAddLayer(pszStart, nNum, pszSQLCommand, + oSetLayers, osModifiedSQL); + } + else if ( EQUALN(pszSQLCommand, "DROP TABLE ", strlen("DROP TABLE ")) ) + { + pszSQLCommand += strlen("DROP TABLE") + 1; + OGR2SQLITEAddLayer(pszStart, nNum, pszSQLCommand, + oSetLayers, osModifiedSQL); + } + else + pszSQLCommand ++; + } + + CPLString osTruncated(pszStart); + osTruncated.resize(pszSQLCommand - pszStart); + osModifiedSQL += osTruncated; + + *ppszSQLCommand = pszSQLCommand; +} + +static void OGR2SQLITEGetPotentialLayerNames(const char *pszSQLCommand, + std::set& oSetLayers, + std::set& oSetSpatialIndex, + CPLString& osModifiedSQL) +{ + int nNum = 1; + OGR2SQLITEGetPotentialLayerNamesInternal(&pszSQLCommand, oSetLayers, + oSetSpatialIndex, + osModifiedSQL, nNum); +} + +/************************************************************************/ +/* OGR2SQLITE_IgnoreAllFieldsExceptGeometry() */ +/************************************************************************/ + +static +void OGR2SQLITE_IgnoreAllFieldsExceptGeometry(OGRLayer* poLayer) +{ + char** papszIgnored = NULL; + papszIgnored = CSLAddString(papszIgnored, "OGR_STYLE"); + OGRFeatureDefn* poFeatureDefn = poLayer->GetLayerDefn(); + for(int i=0; i < poFeatureDefn->GetFieldCount(); i++) + { + papszIgnored = CSLAddString(papszIgnored, + poFeatureDefn->GetFieldDefn(i)->GetNameRef()); + } + poLayer->SetIgnoredFields((const char**)papszIgnored); + CSLDestroy(papszIgnored); +} + + +/************************************************************************/ +/* OGR2SQLITEDealWithSpatialColumn() */ +/************************************************************************/ +static +int OGR2SQLITEDealWithSpatialColumn(OGRLayer* poLayer, + int iGeomCol, + const LayerDesc& oLayerDesc, + const CPLString& osTableName, + OGRSQLiteDataSource* poSQLiteDS, + sqlite3* hDB, + int bSpatialiteDB, + const std::set& oSetLayers, + const std::set& oSetSpatialIndex + ) +{ + int rc; + + OGRGeomFieldDefn* poGeomField = + poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeomCol); + CPLString osGeomColRaw; + if( iGeomCol == 0 ) + osGeomColRaw = OGR2SQLITE_GetNameForGeometryColumn(poLayer); + else + osGeomColRaw = poGeomField->GetNameRef(); + const char* pszGeomColRaw = osGeomColRaw.c_str(); + + CPLString osGeomColEscaped(OGRSQLiteEscape(pszGeomColRaw)); + const char* pszGeomColEscaped = osGeomColEscaped.c_str(); + + CPLString osLayerNameEscaped(OGRSQLiteEscape(osTableName)); + const char* pszLayerNameEscaped = osLayerNameEscaped.c_str(); + + CPLString osIdxNameRaw(CPLSPrintf("idx_%s_%s", + oLayerDesc.osLayerName.c_str(), pszGeomColRaw)); + CPLString osIdxNameEscaped(OGRSQLiteEscapeName(osIdxNameRaw)); + + /* Make sure that the SRS is injected in spatial_ref_sys */ + OGRSpatialReference* poSRS = poGeomField->GetSpatialRef(); + if( iGeomCol == 0 && poSRS == NULL ) + poSRS = poLayer->GetSpatialRef(); + int nSRSId = poSQLiteDS->GetUndefinedSRID(); + if( poSRS != NULL ) + nSRSId = poSQLiteDS->FetchSRSId(poSRS); + + CPLString osSQL; + int bCreateSpatialIndex = FALSE; + if( !bSpatialiteDB ) + { + osSQL.Printf("INSERT INTO geometry_columns (f_table_name, " + "f_geometry_column, geometry_format, geometry_type, " + "coord_dimension, srid) " + "VALUES ('%s','%s','SpatiaLite',%d,%d,%d)", + pszLayerNameEscaped, + pszGeomColEscaped, + (int) wkbFlatten(poLayer->GetGeomType()), + wkbHasZ( poLayer->GetGeomType() ) ? 3 : 2, + nSRSId); + } +#ifdef HAVE_SPATIALITE + else + { + /* We detect the need for creating a spatial index by 2 means : */ + + /* 1) if there's an explicit reference to a 'idx_layername_geometrycolumn' */ + /* table in the SQL --> old/traditionnal way of requesting spatial indices */ + /* with spatialite. */ + + std::set::const_iterator oIter2 = oSetLayers.begin(); + for(; oIter2 != oSetLayers.end(); ++oIter2) + { + const LayerDesc& oLayerDescIter = *oIter2; + if( EQUAL(oLayerDescIter.osLayerName, osIdxNameRaw) ) + { + bCreateSpatialIndex = TRUE; + break; + } + } + + /* 2) or if there's a SELECT FROM SpatialIndex WHERE f_table_name = 'layername' */ + if( !bCreateSpatialIndex ) + { + std::set::const_iterator oIter3 = oSetSpatialIndex.begin(); + for(; oIter3 != oSetSpatialIndex.end(); ++oIter3) + { + const CPLString& osNameIter = *oIter3; + if( EQUAL(osNameIter, oLayerDesc.osLayerName) ) + { + bCreateSpatialIndex = TRUE; + break; + } + } + } + + if( poSQLiteDS->HasSpatialite4Layout() ) + { + int nGeomType = poLayer->GetGeomType(); + int nCoordDimension = 2; + if( wkbHasZ((OGRwkbGeometryType)nGeomType) ) + { + nGeomType += 1000; + nCoordDimension = 3; + } + + osSQL.Printf("INSERT INTO geometry_columns (f_table_name, " + "f_geometry_column, geometry_type, coord_dimension, " + "srid, spatial_index_enabled) " + "VALUES ('%s',Lower('%s'),%d ,%d ,%d, %d)", + pszLayerNameEscaped, + pszGeomColEscaped, nGeomType, + nCoordDimension, + nSRSId, bCreateSpatialIndex ); + } + else + { + const char *pszGeometryType = OGRToOGCGeomType(poLayer->GetGeomType()); + if (pszGeometryType[0] == '\0') + pszGeometryType = "GEOMETRY"; + + osSQL.Printf("INSERT INTO geometry_columns (f_table_name, " + "f_geometry_column, type, coord_dimension, " + "srid, spatial_index_enabled) " + "VALUES ('%s','%s','%s','%s',%d, %d)", + pszLayerNameEscaped, + pszGeomColEscaped, pszGeometryType, + wkbHasZ( poLayer->GetGeomType() ) ? "XYZ" : "XY", + nSRSId, bCreateSpatialIndex ); + } + } +#endif // HAVE_SPATIALITE + rc = sqlite3_exec( hDB, osSQL.c_str(), NULL, NULL, NULL ); + +#ifdef HAVE_SPATIALITE +/* -------------------------------------------------------------------- */ +/* Should we create a spatial index ?. */ +/* -------------------------------------------------------------------- */ + if( !bSpatialiteDB || !bCreateSpatialIndex ) + return rc == SQLITE_OK; + + CPLDebug("SQLITE", "Create spatial index %s", osIdxNameRaw.c_str()); + + /* ENABLE_VIRTUAL_OGR_SPATIAL_INDEX is not defined */ +#ifdef ENABLE_VIRTUAL_OGR_SPATIAL_INDEX + osSQL.Printf("CREATE VIRTUAL TABLE \"%s\" USING " + "VirtualOGRSpatialIndex(%d, '%s', pkid, xmin, xmax, ymin, ymax)", + osIdxNameEscaped.c_str(), + nExtraDS, + OGRSQLiteEscape(oLayerDesc.osLayerName).c_str()); + + rc = sqlite3_exec( hDB, osSQL.c_str(), NULL, NULL, NULL ); + if( rc != SQLITE_OK ) + { + CPLDebug("SQLITE", + "Error occured during spatial index creation : %s", + sqlite3_errmsg(hDB)); + } +#else // ENABLE_VIRTUAL_OGR_SPATIAL_INDEX + rc = sqlite3_exec( hDB, "BEGIN", NULL, NULL, NULL ); + + osSQL.Printf("CREATE VIRTUAL TABLE \"%s\" " + "USING rtree(pkid, xmin, xmax, ymin, ymax)", + osIdxNameEscaped.c_str()); + + if( rc == SQLITE_OK ) + rc = sqlite3_exec( hDB, osSQL.c_str(), NULL, NULL, NULL ); + + sqlite3_stmt *hStmt = NULL; + if( rc == SQLITE_OK ) + { + const char* pszInsertInto = CPLSPrintf( + "INSERT INTO \"%s\" (pkid, xmin, xmax, ymin, ymax) " + "VALUES (?,?,?,?,?)", osIdxNameEscaped.c_str()); + rc = sqlite3_prepare(hDB, pszInsertInto, -1, &hStmt, NULL); + } + + OGRFeature* poFeature; + OGREnvelope sEnvelope; + OGR2SQLITE_IgnoreAllFieldsExceptGeometry(poLayer); + poLayer->ResetReading(); + + while( rc == SQLITE_OK && + (poFeature = poLayer->GetNextFeature()) != NULL ) + { + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if( poGeom != NULL && !poGeom->IsEmpty() ) + { + poGeom->getEnvelope(&sEnvelope); + sqlite3_bind_int64(hStmt, 1, + (sqlite3_int64) poFeature->GetFID() ); + sqlite3_bind_double(hStmt, 2, sEnvelope.MinX); + sqlite3_bind_double(hStmt, 3, sEnvelope.MaxX); + sqlite3_bind_double(hStmt, 4, sEnvelope.MinY); + sqlite3_bind_double(hStmt, 5, sEnvelope.MaxY); + rc = sqlite3_step(hStmt); + if( rc == SQLITE_OK || rc == SQLITE_DONE ) + rc = sqlite3_reset(hStmt); + } + delete poFeature; + } + + poLayer->SetIgnoredFields(NULL); + + sqlite3_finalize(hStmt); + + if( rc == SQLITE_OK ) + rc = sqlite3_exec( hDB, "COMMIT", NULL, NULL, NULL ); + else + { + CPLDebug("SQLITE", + "Error occured during spatial index creation : %s", + sqlite3_errmsg(hDB)); + rc = sqlite3_exec( hDB, "ROLLBACK", NULL, NULL, NULL ); + } +#endif // ENABLE_VIRTUAL_OGR_SPATIAL_INDEX + +#endif // HAVE_SPATIALITE + + return rc == SQLITE_OK; +} + +/************************************************************************/ +/* OGRSQLiteExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRSQLiteExecuteSQL( GDALDataset* poDS, + const char *pszStatement, + OGRGeometry *poSpatialFilter, + CPL_UNUSED const char *pszDialect ) +{ + char* pszTmpDBName = (char*) CPLMalloc(256); + sprintf(pszTmpDBName, "/vsimem/ogr2sqlite/temp_%p.db", pszTmpDBName); + + OGRSQLiteDataSource* poSQLiteDS = NULL; + int nRet; + int bSpatialiteDB = FALSE; + + CPLString osOldVal; + const char* pszOldVal = CPLGetConfigOption("OGR_SQLITE_STATIC_VIRTUAL_OGR", NULL); + if( pszOldVal != NULL ) + { + osOldVal = pszOldVal; + pszOldVal = osOldVal.c_str(); + } + +/* -------------------------------------------------------------------- */ +/* Create in-memory sqlite/spatialite DB */ +/* -------------------------------------------------------------------- */ + +#ifdef HAVE_SPATIALITE + +/* -------------------------------------------------------------------- */ +/* Creating an empty spatialite DB (with spatial_ref_sys populated */ +/* has a non-neglectable cost. So at the first attempt, let's make */ +/* one and cache it for later use. */ +/* -------------------------------------------------------------------- */ +#if 1 + static vsi_l_offset nEmptyDBSize = 0; + static GByte* pabyEmptyDB = NULL; + { + static CPLMutex* hMutex = NULL; + CPLMutexHolder oMutexHolder(&hMutex); + static int bTried = FALSE; + if( !bTried && + CSLTestBoolean(CPLGetConfigOption("OGR_SQLITE_DIALECT_USE_SPATIALITE", "YES")) ) + { + bTried = TRUE; + char* pszCachedFilename = (char*) CPLMalloc(256); + sprintf(pszCachedFilename, "/vsimem/ogr2sqlite/reference_%p.db",pszCachedFilename); + char** papszOptions = CSLAddString(NULL, "SPATIALITE=YES"); + OGRSQLiteDataSource* poCachedDS = new OGRSQLiteDataSource(); + nRet = poCachedDS->Create( pszCachedFilename, papszOptions ); + CSLDestroy(papszOptions); + papszOptions = NULL; + delete poCachedDS; + if( nRet ) + /* Note: the reference file keeps the ownership of the data, so that */ + /* it gets released with VSICleanupFileManager() */ + pabyEmptyDB = VSIGetMemFileBuffer( pszCachedFilename, &nEmptyDBSize, FALSE ); + CPLFree( pszCachedFilename ); + } + } + + /* The following configuration option is useful mostly for debugging/testing */ + if( pabyEmptyDB != NULL && CSLTestBoolean(CPLGetConfigOption("OGR_SQLITE_DIALECT_USE_SPATIALITE", "YES")) ) + { + GByte* pabyEmptyDBClone = (GByte*)VSIMalloc(nEmptyDBSize); + if( pabyEmptyDBClone == NULL ) + { + CPLFree(pszTmpDBName); + return NULL; + } + memcpy(pabyEmptyDBClone, pabyEmptyDB, nEmptyDBSize); + VSIFCloseL(VSIFileFromMemBuffer( pszTmpDBName, pabyEmptyDBClone, nEmptyDBSize, TRUE )); + + poSQLiteDS = new OGRSQLiteDataSource(); + CPLSetThreadLocalConfigOption("OGR_SQLITE_STATIC_VIRTUAL_OGR", "NO"); + nRet = poSQLiteDS->Open( pszTmpDBName, TRUE, NULL ); + CPLSetThreadLocalConfigOption("OGR_SQLITE_STATIC_VIRTUAL_OGR", pszOldVal); + if( !nRet ) + { + /* should not happen really ! */ + delete poSQLiteDS; + VSIUnlink(pszTmpDBName); + CPLFree(pszTmpDBName); + return NULL; + } + bSpatialiteDB = TRUE; + } +#else + /* No caching version */ + poSQLiteDS = new OGRSQLiteDataSource(); + char** papszOptions = CSLAddString(NULL, "SPATIALITE=YES"); + CPLSetThreadLocalConfigOption("OGR_SQLITE_STATIC_VIRTUAL_OGR", "NO"); + nRet = poSQLiteDS->Create( pszTmpDBName, papszOptions ); + CPLSetThreadLocalConfigOption("OGR_SQLITE_STATIC_VIRTUAL_OGR", pszOldVal); + CSLDestroy(papszOptions); + papszOptions = NULL; + if( nRet ) + { + bSpatialiteDB = TRUE; + } +#endif + + else + { + delete poSQLiteDS; + poSQLiteDS = NULL; +#else // HAVE_SPATIALITE + if( TRUE ) + { +#endif // HAVE_SPATIALITE + poSQLiteDS = new OGRSQLiteDataSource(); + CPLSetThreadLocalConfigOption("OGR_SQLITE_STATIC_VIRTUAL_OGR", "NO"); + nRet = poSQLiteDS->Create( pszTmpDBName, NULL ); + CPLSetThreadLocalConfigOption("OGR_SQLITE_STATIC_VIRTUAL_OGR", pszOldVal); + if( !nRet ) + { + delete poSQLiteDS; + VSIUnlink(pszTmpDBName); + CPLFree(pszTmpDBName); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Attach the Virtual Table OGR2SQLITE module to it. */ +/* -------------------------------------------------------------------- */ + OGR2SQLITEModule* poModule = OGR2SQLITE_Setup(poDS, poSQLiteDS); + sqlite3* hDB = poSQLiteDS->GetDB(); + +/* -------------------------------------------------------------------- */ +/* Analysze the statement to determine which tables will be used. */ +/* -------------------------------------------------------------------- */ + std::set oSetLayers; + std::set oSetSpatialIndex; + CPLString osModifiedSQL; + OGR2SQLITEGetPotentialLayerNames(pszStatement, oSetLayers, + oSetSpatialIndex, osModifiedSQL); + std::set::iterator oIter = oSetLayers.begin(); + + if( strcmp(pszStatement, osModifiedSQL.c_str()) != 0 ) + CPLDebug("OGR", "Modified SQL: %s", osModifiedSQL.c_str()); + pszStatement = osModifiedSQL.c_str(); /* do not use it anymore */ + + int bFoundOGRStyle = ( osModifiedSQL.ifind("OGR_STYLE") != std::string::npos ); + +/* -------------------------------------------------------------------- */ +/* For each of those tables, create a Virtual Table. */ +/* -------------------------------------------------------------------- */ + for(; oIter != oSetLayers.end(); ++oIter) + { + const LayerDesc& oLayerDesc = *oIter; + /*CPLDebug("OGR", "Layer desc : %s, %s, %s, %s", + oLayerDesc.osOriginalStr.c_str(), + oLayerDesc.osSubstitutedName.c_str(), + oLayerDesc.osDSName.c_str(), + oLayerDesc.osLayerName.c_str());*/ + + CPLString osSQL; + OGRLayer* poLayer = NULL; + CPLString osTableName; + int nExtraDS; + if( oLayerDesc.osDSName.size() == 0 ) + { + poLayer = poDS->GetLayerByName(oLayerDesc.osLayerName); + /* Might be a false positive (unlikely) */ + if( poLayer == NULL ) + continue; + + osTableName = oLayerDesc.osLayerName; + + nExtraDS = -1; + } + else + { + OGRDataSource* poOtherDS = (OGRDataSource* ) + OGROpen(oLayerDesc.osDSName, FALSE, NULL); + if( poOtherDS == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot open datasource '%s'", + oLayerDesc.osDSName.c_str() ); + delete poSQLiteDS; + VSIUnlink(pszTmpDBName); + CPLFree(pszTmpDBName); + return NULL; + } + + poLayer = poOtherDS->GetLayerByName(oLayerDesc.osLayerName); + if( poLayer == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find layer '%s' in '%s'", + oLayerDesc.osLayerName.c_str(), + oLayerDesc.osDSName.c_str() ); + delete poOtherDS; + delete poSQLiteDS; + VSIUnlink(pszTmpDBName); + CPLFree(pszTmpDBName); + return NULL; + } + + osTableName = oLayerDesc.osSubstitutedName; + + nExtraDS = OGR2SQLITE_AddExtraDS(poModule, poOtherDS); + } + + osSQL.Printf("CREATE VIRTUAL TABLE \"%s\" USING VirtualOGR(%d,'%s',%d)", + OGRSQLiteEscapeName(osTableName).c_str(), + nExtraDS, + OGRSQLiteEscape(oLayerDesc.osLayerName).c_str(), + bFoundOGRStyle); + + char* pszErrMsg = NULL; + int rc = sqlite3_exec( hDB, osSQL.c_str(), + NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot create virtual table for layer '%s' : %s", + osTableName.c_str(), pszErrMsg); + sqlite3_free(pszErrMsg); + continue; + } + + for(int i=0; iGetLayerDefn()->GetGeomFieldCount(); i++) + { + OGR2SQLITEDealWithSpatialColumn(poLayer, i, oLayerDesc, + osTableName, poSQLiteDS, hDB, + bSpatialiteDB, oSetLayers, + oSetSpatialIndex); + } + } + +/* -------------------------------------------------------------------- */ +/* Reload, so that virtual tables are recognized */ +/* -------------------------------------------------------------------- */ + poSQLiteDS->ReloadLayers(); + +/* -------------------------------------------------------------------- */ +/* Prepare the statement. */ +/* -------------------------------------------------------------------- */ + /* This will speed-up layer creation */ + /* ORDER BY are costly to evaluate and are not necessary to establish */ + /* the layer definition. */ + int bUseStatementForGetNextFeature = TRUE; + int bEmptyLayer = FALSE; + + sqlite3_stmt *hSQLStmt = NULL; + int rc = sqlite3_prepare( hDB, + pszStatement, strlen(pszStatement), + &hSQLStmt, NULL ); + + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In ExecuteSQL(): sqlite3_prepare(%s):\n %s", + pszStatement, sqlite3_errmsg(hDB) ); + + if( hSQLStmt != NULL ) + { + sqlite3_finalize( hSQLStmt ); + } + + delete poSQLiteDS; + VSIUnlink(pszTmpDBName); + CPLFree(pszTmpDBName); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we get a resultset? */ +/* -------------------------------------------------------------------- */ + rc = sqlite3_step( hSQLStmt ); + if( rc != SQLITE_ROW ) + { + if ( rc != SQLITE_DONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In ExecuteSQL(): sqlite3_step(%s):\n %s", + pszStatement, sqlite3_errmsg(hDB) ); + + sqlite3_finalize( hSQLStmt ); + + delete poSQLiteDS; + VSIUnlink(pszTmpDBName); + CPLFree(pszTmpDBName); + + return NULL; + } + + if( !EQUALN(pszStatement, "SELECT ", 7) ) + { + + sqlite3_finalize( hSQLStmt ); + + delete poSQLiteDS; + VSIUnlink(pszTmpDBName); + CPLFree(pszTmpDBName); + + return NULL; + } + + bUseStatementForGetNextFeature = FALSE; + bEmptyLayer = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Create layer. */ +/* -------------------------------------------------------------------- */ + OGRSQLiteSelectLayer *poLayer = NULL; + + poLayer = new OGRSQLiteExecuteSQLLayer( pszTmpDBName, + poSQLiteDS, pszStatement, hSQLStmt, + bUseStatementForGetNextFeature, bEmptyLayer ); + + if( poSpatialFilter != NULL ) + poLayer->SetSpatialFilter( 0, poSpatialFilter ); + + return poLayer; +} + +/************************************************************************/ +/* OGRSQLiteGetReferencedLayers() */ +/************************************************************************/ + +std::set OGRSQLiteGetReferencedLayers(const char* pszStatement) +{ +/* -------------------------------------------------------------------- */ +/* Analysze the statement to determine which tables will be used. */ +/* -------------------------------------------------------------------- */ + std::set oSetLayers; + std::set oSetSpatialIndex; + CPLString osModifiedSQL; + OGR2SQLITEGetPotentialLayerNames(pszStatement, oSetLayers, + oSetSpatialIndex, osModifiedSQL); + + return oSetLayers; +} + +#else // HAVE_SQLITE_VFS + +/************************************************************************/ +/* OGRSQLiteExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRSQLiteExecuteSQL( OGRDataSource* poDS, + const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) +{ + CPLError(CE_Failure, CPLE_NotSupported, + "The SQLite version is to old to support the SQLite SQL dialect"); + return NULL; +} + +/************************************************************************/ +/* OGRSQLiteGetReferencedLayers() */ +/************************************************************************/ + +std::set OGRSQLiteGetReferencedLayers(const char* pszStatement) +{ + std::set oSetLayers; + return oSetLayers; +} + +#endif // HAVE_SQLITE_VFS diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteexecutesql.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteexecutesql.h new file mode 100644 index 000000000..fa34bc881 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteexecutesql.h @@ -0,0 +1,67 @@ +/****************************************************************************** + * $Id: ogrsqliteexecutesql.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Run SQL requests with SQLite SQL engine + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SQLITE_EXECUTE_SQL_H_INCLUDED +#define _OGR_SQLITE_EXECUTE_SQL_H_INCLUDED + +#include "ogrsf_frmts.h" +#include + +OGRLayer * OGRSQLiteExecuteSQL( GDALDataset* poDS, + const char *pszStatement, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + + +/************************************************************************/ +/* LayerDesc */ +/************************************************************************/ + +class LayerDesc +{ + public: + LayerDesc() {}; + + bool operator < ( const LayerDesc& other ) const + { + return osOriginalStr < other.osOriginalStr; + } + + CPLString osOriginalStr; + CPLString osSubstitutedName; + CPLString osDSName; + CPLString osLayerName; +}; + +std::set OGRSQLiteGetReferencedLayers(const char* pszStatement); + +#endif /* ndef _OGR_SQLITE_EXECUTE_SQL_H_INCLUDED */ + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitelayer.cpp new file mode 100644 index 000000000..4eb5f85ac --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitelayer.cpp @@ -0,0 +1,3332 @@ +/****************************************************************************** + * $Id: ogrsqlitelayer.cpp 29080 2015-04-30 15:30:33Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRSQLiteLayer class, code shared between + * the direct table access, and the generic SQL results. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * + * Contributor: Alessandro Furieri, a.furieri@lqt.it + * Portions of this module supporting SpatiaLite's own 3D geometries + * [XY, XYM, XYZ and XYZM] available since v.2.4.0 + * Developed for Faunalia ( http://www.faunalia.it) with funding from + * Regione Toscana - Settore SISTEMA INFORMATIVO TERRITORIALE ED AMBIENTALE + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_sqlite.h" +#include + +CPL_CVSID("$Id: ogrsqlitelayer.cpp 29080 2015-04-30 15:30:33Z rouault $"); + +/************************************************************************/ +/* OGRSQLiteLayer() */ +/************************************************************************/ + +OGRSQLiteLayer::OGRSQLiteLayer() + +{ + poDS = NULL; + + pszFIDColumn = NULL; + + hStmt = NULL; + bDoStep = TRUE; + + poFeatureDefn = NULL; + iNextShapeId = 0; + + panFieldOrdinals = NULL; + iFIDCol = -1; + + bIsVirtualShape = FALSE; + + bUseComprGeom = CSLTestBoolean(CPLGetConfigOption("COMPRESS_GEOM", "FALSE")); + + papszCompressedColumns = NULL; + + bAllowMultipleGeomFields = FALSE; +} + +/************************************************************************/ +/* ~OGRSQLiteLayer() */ +/************************************************************************/ + +OGRSQLiteLayer::~OGRSQLiteLayer() + +{ + Finalize(); +} + +/************************************************************************/ +/* Finalize() */ +/************************************************************************/ + +void OGRSQLiteLayer::Finalize() +{ + /* Caution: this function can be called several times (see */ + /* OGRSQLiteExecuteSQLLayer::~OGRSQLiteExecuteSQLLayer()), so it must */ + /* be a no-op on second call */ + + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "SQLite", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( hStmt != NULL ) + { + sqlite3_finalize( hStmt ); + hStmt = NULL; + } + + if( poFeatureDefn != NULL ) + { + poFeatureDefn->Release(); + poFeatureDefn = NULL; + } + + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + CPLFree( panFieldOrdinals ); + panFieldOrdinals = NULL; + + CSLDestroy(papszCompressedColumns); + papszCompressedColumns = NULL; +} + +/************************************************************************/ +/* OGRIsBinaryGeomCol() */ +/************************************************************************/ + +static +int OGRIsBinaryGeomCol( sqlite3_stmt *hStmt, + int iCol, + CPL_UNUSED OGRFieldDefn& oField, + OGRSQLiteGeomFormat& eGeomFormat ) +{ + OGRGeometry* poGeometry = NULL; + const int nBytes = sqlite3_column_bytes( hStmt, iCol ); + CPLPushErrorHandler(CPLQuietErrorHandler); + /* Try as spatialite first since createFromWkb() can sometimes */ + /* interpret spatialite blobs as WKB for certain SRID values */ + if( OGRSQLiteLayer::ImportSpatiaLiteGeometry( + (GByte*)sqlite3_column_blob( hStmt, iCol ), nBytes, + &poGeometry ) == OGRERR_NONE ) + { + eGeomFormat = OSGF_SpatiaLite; + } + else if( OGRGeometryFactory::createFromWkb( + (GByte*)sqlite3_column_blob( hStmt, iCol ), + NULL, &poGeometry, nBytes ) == OGRERR_NONE ) + { + eGeomFormat = OSGF_WKB; + } + else if( OGRGeometryFactory::createFromFgf( + (GByte*)sqlite3_column_blob( hStmt, iCol ), + NULL, &poGeometry, nBytes, NULL ) == OGRERR_NONE ) + { + eGeomFormat = OSGF_FGF; + } + CPLPopErrorHandler(); + CPLErrorReset(); + delete poGeometry; + if( eGeomFormat != OSGF_None ) + { + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* BuildFeatureDefn() */ +/* */ +/* Build feature definition from a set of column definitions */ +/* set on a statement. Sift out geometry and FID fields. */ +/************************************************************************/ + +void OGRSQLiteLayer::BuildFeatureDefn( const char *pszLayerName, + sqlite3_stmt *hStmt, + const std::set& aosGeomCols, + const std::set& aosIgnoredCols ) + +{ + poFeatureDefn = new OGRSQLiteFeatureDefn( pszLayerName ); + poFeatureDefn->SetGeomType(wkbNone); + poFeatureDefn->Reference(); + + int nRawColumns = sqlite3_column_count( hStmt ); + + panFieldOrdinals = (int *) CPLMalloc( sizeof(int) * nRawColumns ); + + int iCol; + for( iCol = 0; iCol < nRawColumns; iCol++ ) + { + OGRFieldDefn oField( OGRSQLiteParamsUnquote(sqlite3_column_name( hStmt, iCol )), + OFTString ); + + // In some cases, particularly when there is a real name for + // the primary key/_rowid_ column we will end up getting the + // primary key column appearing twice. Ignore any repeated names. + if( poFeatureDefn->GetFieldIndex( oField.GetNameRef() ) != -1 ) + continue; + + /* In the case of Spatialite VirtualShape, the PKUID */ + /* should be considered as a primary key */ + if( bIsVirtualShape && EQUAL(oField.GetNameRef(), "PKUID") ) + { + CPLFree(pszFIDColumn); + pszFIDColumn = CPLStrdup(oField.GetNameRef()); + } + + if( pszFIDColumn != NULL && EQUAL(pszFIDColumn, oField.GetNameRef())) + continue; + + //oField.SetWidth( MAX(0,poStmt->GetColSize( iCol )) ); + + if( aosIgnoredCols.find( CPLString(oField.GetNameRef()).tolower() ) != aosIgnoredCols.end() ) + { + continue; + } + if( aosGeomCols.find( CPLString(oField.GetNameRef()).tolower() ) != aosGeomCols.end() ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + new OGRSQLiteGeomFieldDefn(oField.GetNameRef(), iCol); + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + continue; + } + + int nColType = sqlite3_column_type( hStmt, iCol ); + switch( nColType ) + { + case SQLITE_INTEGER: + if( CSLTestBoolean(CPLGetConfigOption("OGR_PROMOTE_TO_INTEGER64", "FALSE")) ) + oField.SetType( OFTInteger64 ); + else + { + GIntBig nVal = sqlite3_column_int64(hStmt, iCol); + if( (GIntBig)(int)nVal == nVal ) + oField.SetType( OFTInteger ); + else + oField.SetType( OFTInteger64 ); + } + break; + + case SQLITE_FLOAT: + oField.SetType( OFTReal ); + break; + + case SQLITE_BLOB: + oField.SetType( OFTBinary ); + break; + + default: + /* leave it as OFTString */; + } + + const char * pszDeclType = sqlite3_column_decltype(hStmt, iCol); + //CPLDebug("SQLITE", "decltype(%s) = %s", + // oField.GetNameRef(), pszDeclType ? pszDeclType : "null"); + OGRFieldType eFieldType = OFTString; + if (pszDeclType != NULL) + { + if (EQUAL(pszDeclType, "INTEGER_BOOLEAN")) + { + oField.SetType(OFTInteger); + oField.SetSubType(OFSTBoolean); + } + else if (EQUAL(pszDeclType, "INTEGER_INT16")) + { + oField.SetType(OFTInteger); + oField.SetSubType(OFSTInt16); + } + else if (EQUAL(pszDeclType, "INTEGERLIST")) + { + oField.SetType(OFTIntegerList); + } + else if (EQUAL(pszDeclType, "INTEGER64LIST")) + { + oField.SetType(OFTInteger64List); + } + else if (EQUAL(pszDeclType, "REALLIST")) + { + oField.SetType(OFTRealList); + } + else if (EQUAL(pszDeclType, "STRINGLIST")) + { + oField.SetType(OFTStringList); + } + else if (EQUAL(pszDeclType, "BIGINT") || EQUAL(pszDeclType, "INT8")) + { + oField.SetType(OFTInteger64); + } + else if (EQUALN(pszDeclType, "INTEGER", strlen("INTEGER"))) + { + oField.SetType(OFTInteger); + } + else if (EQUAL(pszDeclType, "FLOAT_FLOAT32")) + { + oField.SetType(OFTReal); + oField.SetSubType(OFSTFloat32); + } + else if (EQUAL(pszDeclType, "FLOAT") || + EQUAL(pszDeclType, "DECIMAL")) + { + oField.SetType(OFTReal); + } + else if (EQUALN(pszDeclType, "BLOB", 4)) + { + oField.SetType( OFTBinary ); + /* Parse format like BLOB_POINT_25D_4326 created by */ + /* OGRSQLiteExecuteSQL() */ + if( pszDeclType[4] == '_' ) + { + char* pszDeclTypeDup = CPLStrdup(pszDeclType); + char* pszNextUnderscore = strchr(pszDeclTypeDup + 5, '_'); + const char* pszGeomType = pszDeclTypeDup + 5; + if( pszNextUnderscore != NULL ) + { + *pszNextUnderscore = '\0'; + pszNextUnderscore ++; + int nSRID = -1; + const char* pszCoordDimension = pszNextUnderscore; + pszNextUnderscore = strchr(pszNextUnderscore, '_'); + if( pszNextUnderscore != NULL ) + { + *pszNextUnderscore = '\0'; + pszNextUnderscore ++; + const char* pszSRID = pszNextUnderscore; + nSRID = atoi(pszSRID); + } + + OGRwkbGeometryType eGeomType = + OGRFromOGCGeomType(pszGeomType); + if( EQUAL(pszCoordDimension, "25D") ) + eGeomType = wkbSetZ(eGeomType); + OGRSpatialReference* poSRS = poDS->FetchSRS(nSRID); + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + new OGRSQLiteGeomFieldDefn(oField.GetNameRef(), iCol); + poGeomFieldDefn->eGeomFormat = OSGF_SpatiaLite; + poGeomFieldDefn->SetSpatialRef(poSRS); + poGeomFieldDefn->SetType(eGeomType); + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + CPLFree(pszDeclTypeDup); + continue; + } + CPLFree(pszDeclTypeDup); + } + } + else if (EQUAL(pszDeclType, "TEXT") || + EQUALN(pszDeclType, "VARCHAR", 7)) + { + oField.SetType( OFTString ); + if( strstr(pszDeclType, "_deflate") != NULL ) + { + if( CSLFindString(papszCompressedColumns, + oField.GetNameRef()) < 0 ) + { + papszCompressedColumns = CSLAddString( + papszCompressedColumns, oField.GetNameRef()); + CPLDebug("SQLITE", "%s is compressed", oField.GetNameRef()); + } + } + } + else if ((EQUAL(pszDeclType, "TIMESTAMP") || + EQUAL(pszDeclType, "DATETIME")) && + (nColType == SQLITE_TEXT || nColType == SQLITE_FLOAT || nColType == SQLITE_NULL)) + eFieldType = OFTDateTime; + else if (EQUAL(pszDeclType, "DATE") && + (nColType == SQLITE_TEXT || nColType == SQLITE_FLOAT || nColType == SQLITE_NULL)) + eFieldType = OFTDate; + else if (EQUAL(pszDeclType, "TIME") && + (nColType == SQLITE_TEXT || nColType == SQLITE_FLOAT || nColType == SQLITE_NULL)) + eFieldType = OFTTime; + } + else if( nColType == SQLITE_TEXT && + (EQUALN(oField.GetNameRef(), "MIN(", 4) || + EQUALN(oField.GetNameRef(), "MAX(", 4)) && + sqlite3_column_text( hStmt, iCol ) != NULL ) + { + int nRet = OGRSQLITEStringToDateTimeField(NULL, 0, + (const char*)sqlite3_column_text( hStmt, iCol )); + if( nRet > 0 ) + eFieldType = (OGRFieldType) nRet; + } + + // Recognise some common geometry column names. + if( (EQUAL(oField.GetNameRef(),"wkt_geometry") + || EQUAL(oField.GetNameRef(),"geometry") + || EQUALN(oField.GetNameRef(), "asbinary(", 9) + || EQUALN(oField.GetNameRef(), "astext(", 7) + || (EQUALN(oField.GetNameRef(), "st_", 3) && nColType == SQLITE_BLOB ) ) + && (bAllowMultipleGeomFields || poFeatureDefn->GetGeomFieldCount() == 0) ) + { + if( nColType == SQLITE_BLOB ) + { + const int nBytes = sqlite3_column_bytes( hStmt, iCol ); + if( nBytes > 0 ) + { + OGRSQLiteGeomFormat eGeomFormat = OSGF_None; + if( OGRIsBinaryGeomCol( hStmt, iCol, oField, eGeomFormat ) ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + new OGRSQLiteGeomFieldDefn(oField.GetNameRef(), iCol); + poGeomFieldDefn->eGeomFormat = eGeomFormat; + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + continue; + } + } + else + { + /* This could also be a SpatialLite geometry, so */ + /* we'll also try to decode as SpatialLite if */ + /* bTriedAsSpatiaLite is not FALSE */ + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + new OGRSQLiteGeomFieldDefn(oField.GetNameRef(), iCol); + poGeomFieldDefn->eGeomFormat = OSGF_WKB; + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + continue; + } + } + else if( nColType == SQLITE_TEXT ) + { + char* pszText = (char*) sqlite3_column_text( hStmt, iCol ); + if( pszText != NULL ) + { + OGRSQLiteGeomFormat eGeomFormat = OSGF_None; + CPLPushErrorHandler(CPLQuietErrorHandler); + OGRGeometry* poGeometry = NULL; + if( OGRGeometryFactory::createFromWkt( + &pszText, NULL, &poGeometry ) == OGRERR_NONE ) + { + eGeomFormat = OSGF_WKT; + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + new OGRSQLiteGeomFieldDefn(oField.GetNameRef(), iCol); + poGeomFieldDefn->eGeomFormat = eGeomFormat; + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + } + CPLPopErrorHandler(); + CPLErrorReset(); + delete poGeometry; + if( eGeomFormat != OSGF_None ) + continue; + } + else + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + new OGRSQLiteGeomFieldDefn(oField.GetNameRef(), iCol); + poGeomFieldDefn->eGeomFormat = OSGF_WKT; + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + continue; + } + } + } + + // SpatialLite / Gaia + if( EQUAL(oField.GetNameRef(),"GaiaGeometry") + && (bAllowMultipleGeomFields || poFeatureDefn->GetGeomFieldCount() == 0) ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + new OGRSQLiteGeomFieldDefn(oField.GetNameRef(), iCol); + poGeomFieldDefn->eGeomFormat = OSGF_SpatiaLite; + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + continue; + } + + // Recognize a geometry column from trying to build the geometry + // Useful for OGRSQLiteSelectLayer + if( nColType == SQLITE_BLOB && + (bAllowMultipleGeomFields || poFeatureDefn->GetGeomFieldCount() == 0) ) + { + const int nBytes = sqlite3_column_bytes( hStmt, iCol ); + OGRSQLiteGeomFormat eGeomFormat = OSGF_None; + if( nBytes > 0 && OGRIsBinaryGeomCol( hStmt, iCol, oField, + eGeomFormat ) ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + new OGRSQLiteGeomFieldDefn(oField.GetNameRef(), iCol); + poGeomFieldDefn->eGeomFormat = eGeomFormat; + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + continue; + } + } + + // The rowid is for internal use, not a real column. + if( EQUAL(oField.GetNameRef(),"_rowid_") ) + continue; + + // The OGC_FID is for internal use, not a real user visible column. + if( EQUAL(oField.GetNameRef(),"OGC_FID") ) + continue; + + /* config option just in case we wouldn't want that in some cases */ + if( (eFieldType == OFTTime || eFieldType == OFTDate || + eFieldType == OFTDateTime) && + CSLTestBoolean( + CPLGetConfigOption("OGR_SQLITE_ENABLE_DATETIME", "YES")) ) + { + oField.SetType( eFieldType ); + } + + poFeatureDefn->AddFieldDefn( &oField ); + panFieldOrdinals[poFeatureDefn->GetFieldCount() - 1] = iCol; + } + + if( pszFIDColumn != NULL ) + { + for( iCol = 0; iCol < nRawColumns; iCol++ ) + { + if( EQUAL(OGRSQLiteParamsUnquote(sqlite3_column_name(hStmt,iCol)).c_str(), + pszFIDColumn) ) + { + iFIDCol = iCol; + break; + } + } + } +} + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char *OGRSQLiteLayer::GetFIDColumn() + +{ + GetLayerDefn(); + if( pszFIDColumn != NULL ) + return pszFIDColumn; + else + return ""; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRSQLiteLayer::ResetReading() + +{ + ClearStatement(); + iNextShapeId = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRSQLiteLayer::GetNextFeature() + +{ + for( ; TRUE; ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeomFieldRef(m_iGeomFieldFilter) ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRSQLiteLayer::GetNextRawFeature() + +{ + if( hStmt == NULL ) + { + ResetStatement(); + if (hStmt == NULL) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Fetch a record (unless otherwise instructed) */ +/* -------------------------------------------------------------------- */ + if( bDoStep ) + { + int rc; + + rc = sqlite3_step( hStmt ); + if( rc != SQLITE_ROW ) + { + if ( rc != SQLITE_DONE ) + { + sqlite3_reset(hStmt); + CPLError( CE_Failure, CPLE_AppDefined, + "In GetNextRawFeature(): sqlite3_step() : %s", + sqlite3_errmsg(poDS->GetDB()) ); + } + + ClearStatement(); + + return NULL; + } + } + else + bDoStep = TRUE; + +/* -------------------------------------------------------------------- */ +/* Create a feature from the current result. */ +/* -------------------------------------------------------------------- */ + int iField; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + +/* -------------------------------------------------------------------- */ +/* Set FID if we have a column to set it from. */ +/* -------------------------------------------------------------------- */ + if( iFIDCol >= 0 ) + poFeature->SetFID( sqlite3_column_int64( hStmt, iFIDCol ) ); + else + poFeature->SetFID( iNextShapeId ); + + iNextShapeId++; + + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Process Geometry if we have a column. */ +/* -------------------------------------------------------------------- */ + for( iField = 0; iField < poFeatureDefn->GetGeomFieldCount(); iField++ ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(iField); + if( !poGeomFieldDefn->IsIgnored() ) + { + OGRGeometry *poGeometry = NULL; + if ( poGeomFieldDefn->eGeomFormat == OSGF_WKT ) + { + char *pszWKTCopy, *pszWKT = NULL; + + pszWKT = (char *) sqlite3_column_text( hStmt, poGeomFieldDefn->iCol ); + pszWKTCopy = pszWKT; + if( OGRGeometryFactory::createFromWkt( + &pszWKTCopy, NULL, &poGeometry ) == OGRERR_NONE ) + { + poFeature->SetGeomFieldDirectly( iField, poGeometry ); + } + } + else if ( poGeomFieldDefn->eGeomFormat == OSGF_WKB ) + { + const int nBytes = sqlite3_column_bytes( hStmt, poGeomFieldDefn->iCol ); + + /* Try as spatialite first since createFromWkb() can sometimes */ + /* interpret spatialite blobs as WKB for certain SRID values */ + if (!poGeomFieldDefn->bTriedAsSpatiaLite) + { + /* If the layer is the result of a sql select, we cannot be sure if it is */ + /* WKB or SpatialLite format */ + if( ImportSpatiaLiteGeometry( + (GByte*)sqlite3_column_blob( hStmt, poGeomFieldDefn->iCol ), nBytes, + &poGeometry ) == OGRERR_NONE ) + { + poFeature->SetGeomFieldDirectly( iField, poGeometry ); + poGeomFieldDefn->eGeomFormat = OSGF_SpatiaLite; + } + poGeomFieldDefn->bTriedAsSpatiaLite = TRUE; + } + + if( poGeomFieldDefn->eGeomFormat == OSGF_WKB && + OGRGeometryFactory::createFromWkb( + (GByte*)sqlite3_column_blob( hStmt, poGeomFieldDefn->iCol ), + NULL, &poGeometry, nBytes ) == OGRERR_NONE ) + { + poFeature->SetGeomFieldDirectly( iField, poGeometry ); + } + } + else if ( poGeomFieldDefn->eGeomFormat == OSGF_FGF ) + { + const int nBytes = sqlite3_column_bytes( hStmt, poGeomFieldDefn->iCol ); + + if( OGRGeometryFactory::createFromFgf( + (GByte*)sqlite3_column_blob( hStmt, poGeomFieldDefn->iCol ), + NULL, &poGeometry, nBytes, NULL ) == OGRERR_NONE ) + { + poFeature->SetGeomFieldDirectly( iField, poGeometry ); + } + } + else if ( poGeomFieldDefn->eGeomFormat == OSGF_SpatiaLite ) + { + const int nBytes = sqlite3_column_bytes( hStmt, poGeomFieldDefn->iCol ); + + if( ImportSpatiaLiteGeometry( + (GByte*)sqlite3_column_blob( hStmt, poGeomFieldDefn->iCol ), nBytes, + &poGeometry ) == OGRERR_NONE ) + { + poFeature->SetGeomFieldDirectly( iField, poGeometry ); + } + } + + if (poGeometry != NULL && poGeomFieldDefn->GetSpatialRef() != NULL) + poGeometry->assignSpatialReference(poGeomFieldDefn->GetSpatialRef()); + } + } + +/* -------------------------------------------------------------------- */ +/* set the fields. */ +/* -------------------------------------------------------------------- */ + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn( iField ); + if ( poFieldDefn->IsIgnored() ) + continue; + + int iRawField = panFieldOrdinals[iField]; + + int nSQLite3Type = sqlite3_column_type( hStmt, iRawField ); + if( nSQLite3Type == SQLITE_NULL ) + continue; + + switch( poFieldDefn->GetType() ) + { + case OFTInteger: + case OFTInteger64: + { + /* Possible since SQLite3 has no strong typing */ + if( nSQLite3Type == SQLITE_TEXT ) + poFeature->SetField( iField, + (const char *)sqlite3_column_text( hStmt, iRawField ) ); + else + poFeature->SetField( iField, + sqlite3_column_int64( hStmt, iRawField ) ); + break; + } + + case OFTReal: + { + /* Possible since SQLite3 has no strong typing */ + if( nSQLite3Type == SQLITE_TEXT ) + poFeature->SetField( iField, + (const char *)sqlite3_column_text( hStmt, iRawField ) ); + else + poFeature->SetField( iField, + sqlite3_column_double( hStmt, iRawField ) ); + break; + } + + case OFTBinary: + { + const int nBytes = sqlite3_column_bytes( hStmt, iRawField ); + + poFeature->SetField( iField, nBytes, + (GByte*)sqlite3_column_blob( hStmt, iRawField ) ); + } + break; + + case OFTString: + case OFTIntegerList: + case OFTInteger64List: + case OFTRealList: + case OFTStringList: + { + if( CSLFindString(papszCompressedColumns, + poFeatureDefn->GetFieldDefn(iField)->GetNameRef()) >= 0 ) + { + const int nBytes = sqlite3_column_bytes( hStmt, iRawField ); + GByte* pabyBlob = (GByte*)sqlite3_column_blob( hStmt, iRawField ); + + void* pOut = CPLZLibInflate( pabyBlob, nBytes, NULL, 0, NULL ); + if( pOut != NULL ) + { + poFeature->SetField( iField, (const char*) pOut ); + CPLFree(pOut); + } + else + { + poFeature->SetField( iField, + (const char *) + sqlite3_column_text( hStmt, iRawField ) ); + } + } + else + { + poFeature->SetField( iField, + (const char *) + sqlite3_column_text( hStmt, iRawField ) ); + } + break; + } + + case OFTDate: + case OFTTime: + case OFTDateTime: + { + if( sqlite3_column_type( hStmt, iRawField ) == SQLITE_TEXT ) + { + const char* pszValue = (const char *) + sqlite3_column_text( hStmt, iRawField ); + OGRSQLITEStringToDateTimeField( poFeature, iField, pszValue ); + } + else if( sqlite3_column_type( hStmt, iRawField ) == SQLITE_FLOAT ) + { + // Try converting from Julian day + char** papszResult = NULL; + sqlite3_get_table( poDS->GetDB(), + CPLSPrintf("SELECT strftime('%%Y-%%m-%%d %%H:%%M:%%S', %.16g)", + sqlite3_column_double(hStmt, iRawField)), + &papszResult, NULL, NULL, NULL ); + if( papszResult && papszResult[0] && papszResult[1] ) + { + OGRSQLITEStringToDateTimeField( poFeature, iField, papszResult[1] ); + } + sqlite3_free_table(papszResult); + } + break; + } + + default: + break; + } + } + + return poFeature; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRSQLiteLayer::GetFeature( GIntBig nFeatureId ) + +{ + return OGRLayer::GetFeature( nFeatureId ); +} + + +/************************************************************************/ +/* createFromSpatialiteInternal() */ +/************************************************************************/ + +/* See http://www.gaia-gis.it/spatialite/spatialite-manual-2.3.0.html#t3.3 */ +/* for the specification of the spatialite BLOB geometry format */ +/* Derived from WKB, but unfortunately it is not practical to reuse existing */ +/* WKB encoding/decoding code */ + +#ifdef CPL_LSB +#define NEED_SWAP_SPATIALITE() (eByteOrder != wkbNDR) +#else +#define NEED_SWAP_SPATIALITE() (eByteOrder == wkbNDR) +#endif + + +OGRErr OGRSQLiteLayer::createFromSpatialiteInternal(const GByte *pabyData, + OGRGeometry **ppoReturn, + int nBytes, + OGRwkbByteOrder eByteOrder, + int* pnBytesConsumed, + int nRecLevel) +{ + OGRErr eErr = OGRERR_NONE; + OGRGeometry *poGeom = NULL; + GInt32 nGType; + GInt32 compressedSize; + + *ppoReturn = NULL; + + /* Arbitrary value, but certainly large enough for reasonable usages ! */ + if( nRecLevel == 32 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too many recursiong level (%d) while parsing Spatialite geometry.", + nRecLevel ); + return OGRERR_CORRUPT_DATA; + } + + if (nBytes < 4) + return OGRERR_NOT_ENOUGH_DATA; + +/* -------------------------------------------------------------------- */ +/* Decode the geometry type. */ +/* -------------------------------------------------------------------- */ + memcpy( &nGType, pabyData, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nGType ); + + if( ( nGType >= OGRSplitePointXY && + nGType <= OGRSpliteGeometryCollectionXY ) || // XY types + ( nGType >= OGRSplitePointXYZ && + nGType <= OGRSpliteGeometryCollectionXYZ ) || // XYZ types + ( nGType >= OGRSplitePointXYM && + nGType <= OGRSpliteGeometryCollectionXYM ) || // XYM types + ( nGType >= OGRSplitePointXYZM && + nGType <= OGRSpliteGeometryCollectionXYZM ) || // XYZM types + ( nGType >= OGRSpliteComprLineStringXY && + nGType <= OGRSpliteComprGeometryCollectionXY ) || // XY compressed + ( nGType >= OGRSpliteComprLineStringXYZ && + nGType <= OGRSpliteComprGeometryCollectionXYZ ) || // XYZ compressed + ( nGType >= OGRSpliteComprLineStringXYM && + nGType <= OGRSpliteComprGeometryCollectionXYM ) || // XYM compressed + ( nGType >= OGRSpliteComprLineStringXYZM && + nGType <= OGRSpliteComprGeometryCollectionXYZM ) ) // XYZM compressed + ; + else + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + +/* -------------------------------------------------------------------- */ +/* Point [XY] */ +/* -------------------------------------------------------------------- */ + if( nGType == OGRSplitePointXY ) + { + double adfTuple[2]; + + if( nBytes < 4 + 2 * 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( adfTuple, pabyData + 4, 2*8 ); + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + } + + poGeom = new OGRPoint( adfTuple[0], adfTuple[1] ); + + if( pnBytesConsumed ) + *pnBytesConsumed = 4 + 2 * 8; + } +/* -------------------------------------------------------------------- */ +/* Point [XYZ] */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSplitePointXYZ ) + { + double adfTuple[3]; + + if( nBytes < 4 + 3 * 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( adfTuple, pabyData + 4, 3*8 ); + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + } + + poGeom = new OGRPoint( adfTuple[0], adfTuple[1], adfTuple[2] ); + + if( pnBytesConsumed ) + *pnBytesConsumed = 4 + 3 * 8; + } + +/* -------------------------------------------------------------------- */ +/* Point [XYM] */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSplitePointXYM ) + { + double adfTuple[3]; + + if( nBytes < 4 + 3 * 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( adfTuple, pabyData + 4, 3*8 ); + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + } + + poGeom = new OGRPoint( adfTuple[0], adfTuple[1] ); + + if( pnBytesConsumed ) + *pnBytesConsumed = 4 + 3 * 8; + } + +/* -------------------------------------------------------------------- */ +/* Point [XYZM] */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSplitePointXYZM ) + { + double adfTuple[4]; + + if( nBytes < 4 + 4 * 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( adfTuple, pabyData + 4, 4*8 ); + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + CPL_SWAP64PTR( adfTuple + 3 ); + } + + poGeom = new OGRPoint( adfTuple[0], adfTuple[1], adfTuple[2] ); + + if( pnBytesConsumed ) + *pnBytesConsumed = 4 + 4 * 8; + } + +/* -------------------------------------------------------------------- */ +/* LineString [XY] */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSpliteLineStringXY ) + { + double adfTuple[2]; + GInt32 nPointCount; + int iPoint; + OGRLineString *poLS; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nPointCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount > INT_MAX / (2 * 8)) + return OGRERR_CORRUPT_DATA; + + if (nBytes - 8 < 2 * 8 * nPointCount ) + return OGRERR_NOT_ENOUGH_DATA; + + poGeom = poLS = new OGRLineString(); + if( !NEED_SWAP_SPATIALITE() ) + { + poLS->setPoints( nPointCount, (OGRRawPoint*)(pabyData + 8), NULL ); + } + else + { + poLS->setNumPoints( nPointCount, FALSE ); + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + memcpy( adfTuple, pabyData + 8 + 2*8*iPoint, 2*8 ); + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + poLS->setPoint( iPoint, adfTuple[0], adfTuple[1] ); + } + } + + if( pnBytesConsumed ) + *pnBytesConsumed = 8 + 2 * 8 * nPointCount; + } + +/* -------------------------------------------------------------------- */ +/* LineString [XYZ] */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSpliteLineStringXYZ ) + { + double adfTuple[3]; + GInt32 nPointCount; + int iPoint; + OGRLineString *poLS; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nPointCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount > INT_MAX / (3 * 8)) + return OGRERR_CORRUPT_DATA; + + if (nBytes - 8 < 3 * 8 * nPointCount ) + return OGRERR_NOT_ENOUGH_DATA; + + poGeom = poLS = new OGRLineString(); + poLS->setNumPoints( nPointCount ); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + memcpy( adfTuple, pabyData + 8 + 3*8*iPoint, 3*8 ); + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + } + + poLS->setPoint( iPoint, adfTuple[0], adfTuple[1], adfTuple[2] ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = 8 + 3 * 8 * nPointCount; + } + +/* -------------------------------------------------------------------- */ +/* LineString [XYM] */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSpliteLineStringXYM ) + { + double adfTuple[3]; + GInt32 nPointCount; + int iPoint; + OGRLineString *poLS; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nPointCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount > INT_MAX / (3 * 8)) + return OGRERR_CORRUPT_DATA; + + if (nBytes - 8 < 3 * 8 * nPointCount ) + return OGRERR_NOT_ENOUGH_DATA; + + poGeom = poLS = new OGRLineString(); + poLS->setNumPoints( nPointCount ); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + memcpy( adfTuple, pabyData + 8 + 3*8*iPoint, 3*8 ); + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + } + + poLS->setPoint( iPoint, adfTuple[0], adfTuple[1] ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = 8 + 3 * 8 * nPointCount; + } + +/* -------------------------------------------------------------------- */ +/* LineString [XYZM] */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSpliteLineStringXYZM ) + { + double adfTuple[4]; + GInt32 nPointCount; + int iPoint; + OGRLineString *poLS; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nPointCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount > INT_MAX / (4 * 8)) + return OGRERR_CORRUPT_DATA; + + if (nBytes - 8 < 4 * 8 * nPointCount ) + return OGRERR_NOT_ENOUGH_DATA; + + poGeom = poLS = new OGRLineString(); + poLS->setNumPoints( nPointCount ); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + memcpy( adfTuple, pabyData + 8 + 4*8*iPoint, 4*8 ); + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + CPL_SWAP64PTR( adfTuple + 3 ); + } + + poLS->setPoint( iPoint, adfTuple[0], adfTuple[1], adfTuple[2] ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = 8 + 4 * 8 * nPointCount; + } + +/* -------------------------------------------------------------------- */ +/* LineString [XY] Compressed */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSpliteComprLineStringXY ) + { + double adfTuple[2]; + double adfTupleBase[2]; + float asfTuple[2]; + GInt32 nPointCount; + int iPoint; + OGRLineString *poLS; + int nNextByte; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nPointCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount - 2 > (INT_MAX - 16 * 2) / 8) + return OGRERR_CORRUPT_DATA; + + compressedSize = 16 * 2; // first and last Points + compressedSize += 8 * (nPointCount - 2); // intermediate Points + + if (nBytes - 8 < compressedSize ) + return OGRERR_NOT_ENOUGH_DATA; + + poGeom = poLS = new OGRLineString(); + poLS->setNumPoints( nPointCount ); + + nNextByte = 8; + adfTupleBase[0] = 0.0; + adfTupleBase[1] = 0.0; + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + if ( iPoint == 0 || iPoint == (nPointCount - 1 ) ) + { + // first and last Points are uncompressed + memcpy( adfTuple, pabyData + nNextByte, 2*8 ); + nNextByte += 2 * 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + } + } + else + { + // any other intermediate Point is compressed + memcpy( asfTuple, pabyData + nNextByte, 2*4 ); + nNextByte += 2 * 4; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP32PTR( asfTuple ); + CPL_SWAP32PTR( asfTuple + 1 ); + } + adfTuple[0] = asfTuple[0] + adfTupleBase[0]; + adfTuple[1] = asfTuple[1] + adfTupleBase[1]; + } + + poLS->setPoint( iPoint, adfTuple[0], adfTuple[1] ); + adfTupleBase[0] = adfTuple[0]; + adfTupleBase[1] = adfTuple[1]; + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* LineString [XYZ] Compressed */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSpliteComprLineStringXYZ ) + { + double adfTuple[3]; + double adfTupleBase[3]; + float asfTuple[3]; + GInt32 nPointCount; + int iPoint; + OGRLineString *poLS; + int nNextByte; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nPointCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount - 2 > (INT_MAX - 24 * 2) / 12) + return OGRERR_CORRUPT_DATA; + + compressedSize = 24 * 2; // first and last Points + compressedSize += 12 * (nPointCount - 2); // intermediate Points + + if (nBytes - 8 < compressedSize ) + return OGRERR_NOT_ENOUGH_DATA; + + poGeom = poLS = new OGRLineString(); + poLS->setNumPoints( nPointCount ); + + nNextByte = 8; + adfTupleBase[0] = 0.0; + adfTupleBase[1] = 0.0; + adfTupleBase[2] = 0.0; + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + if ( iPoint == 0 || iPoint == (nPointCount - 1 ) ) + { + // first and last Points are uncompressed + memcpy( adfTuple, pabyData + nNextByte, 3*8 ); + nNextByte += 3 * 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + } + } + else + { + // any other intermediate Point is compressed + memcpy( asfTuple, pabyData + nNextByte, 3*4 ); + nNextByte += 3 * 4; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP32PTR( asfTuple ); + CPL_SWAP32PTR( asfTuple + 1 ); + CPL_SWAP32PTR( asfTuple + 2 ); + } + adfTuple[0] = asfTuple[0] + adfTupleBase[0]; + adfTuple[1] = asfTuple[1] + adfTupleBase[1]; + adfTuple[2] = asfTuple[2] + adfTupleBase[2]; + } + + poLS->setPoint( iPoint, adfTuple[0], adfTuple[1], adfTuple[2] ); + adfTupleBase[0] = adfTuple[0]; + adfTupleBase[1] = adfTuple[1]; + adfTupleBase[2] = adfTuple[2]; + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* LineString [XYM] Compressed */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSpliteComprLineStringXYM ) + { + double adfTuple[2]; + double adfTupleBase[2]; + float asfTuple[2]; + GInt32 nPointCount; + int iPoint; + OGRLineString *poLS; + int nNextByte; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nPointCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount - 2 > (INT_MAX - 24 * 2) / 16) + return OGRERR_CORRUPT_DATA; + + compressedSize = 24 * 2; // first and last Points + compressedSize += 16 * (nPointCount - 2); // intermediate Points + + if (nBytes - 8 < compressedSize ) + return OGRERR_NOT_ENOUGH_DATA; + + poGeom = poLS = new OGRLineString(); + poLS->setNumPoints( nPointCount ); + + nNextByte = 8; + adfTupleBase[0] = 0.0; + adfTupleBase[1] = 0.0; + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + if ( iPoint == 0 || iPoint == (nPointCount - 1 ) ) + { + // first and last Points are uncompressed + memcpy( adfTuple, pabyData + nNextByte, 2*8 ); + nNextByte += 3 * 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + } + } + else + { + // any other intermediate Point is compressed + memcpy( asfTuple, pabyData + nNextByte, 2*4 ); + nNextByte += 2 * 4 + 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP32PTR( asfTuple ); + CPL_SWAP32PTR( asfTuple + 1 ); + } + adfTuple[0] = asfTuple[0] + adfTupleBase[0]; + adfTuple[1] = asfTuple[1] + adfTupleBase[1]; + } + + poLS->setPoint( iPoint, adfTuple[0], adfTuple[1] ); + adfTupleBase[0] = adfTuple[0]; + adfTupleBase[1] = adfTuple[1]; + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* LineString [XYZM] Compressed */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSpliteComprLineStringXYZM ) + { + double adfTuple[3]; + double adfTupleBase[3]; + float asfTuple[3]; + GInt32 nPointCount; + int iPoint; + OGRLineString *poLS; + int nNextByte; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nPointCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount - 2 > (INT_MAX - 32 * 2) / 20) + return OGRERR_CORRUPT_DATA; + + compressedSize = 32 * 2; // first and last Points + /* Note 20 is not an error : x,y,z are float and the m is a double */ + compressedSize += 20 * (nPointCount - 2); // intermediate Points + + if (nBytes - 8 < compressedSize ) + return OGRERR_NOT_ENOUGH_DATA; + + poGeom = poLS = new OGRLineString(); + poLS->setNumPoints( nPointCount ); + + nNextByte = 8; + adfTupleBase[0] = 0.0; + adfTupleBase[1] = 0.0; + adfTupleBase[2] = 0.0; + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + if ( iPoint == 0 || iPoint == (nPointCount - 1 ) ) + { + // first and last Points are uncompressed + memcpy( adfTuple, pabyData + nNextByte, 3*8 ); + nNextByte += 4 * 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + } + } + else + { + // any other intermediate Point is compressed + memcpy( asfTuple, pabyData + nNextByte, 3*4 ); + nNextByte += 3 * 4 + 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP32PTR( asfTuple ); + CPL_SWAP32PTR( asfTuple + 1 ); + CPL_SWAP32PTR( asfTuple + 2 ); + } + adfTuple[0] = asfTuple[0] + adfTupleBase[0]; + adfTuple[1] = asfTuple[1] + adfTupleBase[1]; + adfTuple[2] = asfTuple[2] + adfTupleBase[2]; + } + + poLS->setPoint( iPoint, adfTuple[0], adfTuple[1], adfTuple[2] ); + adfTupleBase[0] = adfTuple[0]; + adfTupleBase[1] = adfTuple[1]; + adfTupleBase[2] = adfTuple[2]; + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* Polygon [XY] */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSplitePolygonXY ) + { + double adfTuple[2]; + GInt32 nPointCount; + GInt32 nRingCount; + int iPoint, iRing; + OGRLinearRing *poLR; + OGRPolygon *poPoly; + int nNextByte; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nRingCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nRingCount ); + + if (nRingCount < 0 || nRingCount > INT_MAX / 4) + return OGRERR_CORRUPT_DATA; + + // Each ring has a minimum of 4 bytes + if (nBytes - 8 < nRingCount * 4) + return OGRERR_NOT_ENOUGH_DATA; + + nNextByte = 8; + + poGeom = poPoly = new OGRPolygon(); + + for( iRing = 0; iRing < nRingCount; iRing++ ) + { + if( nBytes - nNextByte < 4 ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + memcpy( &nPointCount, pabyData + nNextByte, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount > INT_MAX / (2 * 8)) + { + delete poPoly; + return OGRERR_CORRUPT_DATA; + } + + nNextByte += 4; + + if( nBytes - nNextByte < 2 * 8 * nPointCount ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + poLR = new OGRLinearRing(); + if( !NEED_SWAP_SPATIALITE() ) + { + poLR->setPoints( nPointCount, (OGRRawPoint*)(pabyData + nNextByte), NULL ); + nNextByte += 2 * 8 * nPointCount; + } + else + { + poLR->setNumPoints( nPointCount, FALSE ); + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + memcpy( adfTuple, pabyData + nNextByte, 2*8 ); + nNextByte += 2 * 8; + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + poLR->setPoint( iPoint, adfTuple[0], adfTuple[1] ); + } + } + + poPoly->addRingDirectly( poLR ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* Polygon [XYZ] */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSplitePolygonXYZ ) + { + double adfTuple[3]; + GInt32 nPointCount; + GInt32 nRingCount; + int iPoint, iRing; + OGRLinearRing *poLR; + OGRPolygon *poPoly; + int nNextByte; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nRingCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nRingCount ); + + if (nRingCount < 0 || nRingCount > INT_MAX / 4) + return OGRERR_CORRUPT_DATA; + + // Each ring has a minimum of 4 bytes + if (nBytes - 8 < nRingCount * 4) + return OGRERR_NOT_ENOUGH_DATA; + + nNextByte = 8; + + poGeom = poPoly = new OGRPolygon(); + + for( iRing = 0; iRing < nRingCount; iRing++ ) + { + if( nBytes - nNextByte < 4 ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + memcpy( &nPointCount, pabyData + nNextByte, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount > INT_MAX / (3 * 8)) + { + delete poPoly; + return OGRERR_CORRUPT_DATA; + } + + nNextByte += 4; + + if( nBytes - nNextByte < 3 * 8 * nPointCount ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + poLR = new OGRLinearRing(); + poLR->setNumPoints( nPointCount, FALSE ); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + memcpy( adfTuple, pabyData + nNextByte, 3*8 ); + nNextByte += 3 * 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + } + + poLR->setPoint( iPoint, adfTuple[0], adfTuple[1], adfTuple[2] ); + } + + poPoly->addRingDirectly( poLR ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* Polygon [XYM] */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSplitePolygonXYM ) + { + double adfTuple[3]; + GInt32 nPointCount; + GInt32 nRingCount; + int iPoint, iRing; + OGRLinearRing *poLR; + OGRPolygon *poPoly; + int nNextByte; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nRingCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nRingCount ); + + if (nRingCount < 0 || nRingCount > INT_MAX / 4) + return OGRERR_CORRUPT_DATA; + + // Each ring has a minimum of 4 bytes + if (nBytes - 8 < nRingCount * 4) + return OGRERR_NOT_ENOUGH_DATA; + + nNextByte = 8; + + poGeom = poPoly = new OGRPolygon(); + + for( iRing = 0; iRing < nRingCount; iRing++ ) + { + if( nBytes - nNextByte < 4 ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + memcpy( &nPointCount, pabyData + nNextByte, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount > INT_MAX / (3 * 8)) + { + delete poPoly; + return OGRERR_CORRUPT_DATA; + } + + nNextByte += 4; + + if( nBytes - nNextByte < 3 * 8 * nPointCount ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + poLR = new OGRLinearRing(); + poLR->setNumPoints( nPointCount, FALSE ); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + memcpy( adfTuple, pabyData + nNextByte, 3*8 ); + nNextByte += 3 * 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + } + + poLR->setPoint( iPoint, adfTuple[0], adfTuple[1] ); + } + + poPoly->addRingDirectly( poLR ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* Polygon [XYZM] */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSplitePolygonXYZM ) + { + double adfTuple[4]; + GInt32 nPointCount; + GInt32 nRingCount; + int iPoint, iRing; + OGRLinearRing *poLR; + OGRPolygon *poPoly; + int nNextByte; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nRingCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nRingCount ); + + if (nRingCount < 0 || nRingCount > INT_MAX / 4) + return OGRERR_CORRUPT_DATA; + + // Each ring has a minimum of 4 bytes + if (nBytes - 8 < nRingCount * 4) + return OGRERR_NOT_ENOUGH_DATA; + + nNextByte = 8; + + poGeom = poPoly = new OGRPolygon(); + + for( iRing = 0; iRing < nRingCount; iRing++ ) + { + if( nBytes - nNextByte < 4 ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + memcpy( &nPointCount, pabyData + nNextByte, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount > INT_MAX / (4 * 8)) + { + delete poPoly; + return OGRERR_CORRUPT_DATA; + } + + nNextByte += 4; + + if( nBytes - nNextByte < 4 * 8 * nPointCount ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + poLR = new OGRLinearRing(); + poLR->setNumPoints( nPointCount, FALSE ); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + memcpy( adfTuple, pabyData + nNextByte, 4*8 ); + nNextByte += 4 * 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + CPL_SWAP64PTR( adfTuple + 3 ); + } + + poLR->setPoint( iPoint, adfTuple[0], adfTuple[1], adfTuple[2] ); + } + + poPoly->addRingDirectly( poLR ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* Polygon [XY] Compressed */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSpliteComprPolygonXY ) + { + double adfTuple[2]; + double adfTupleBase[2]; + float asfTuple[2]; + GInt32 nPointCount; + GInt32 nRingCount; + int iPoint, iRing; + OGRLinearRing *poLR; + OGRPolygon *poPoly; + int nNextByte; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nRingCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nRingCount ); + + if (nRingCount < 0 || nRingCount > INT_MAX / 4) + return OGRERR_CORRUPT_DATA; + + // Each ring has a minimum of 4 bytes + if (nBytes - 8 < nRingCount * 4) + return OGRERR_NOT_ENOUGH_DATA; + + nNextByte = 8; + + poGeom = poPoly = new OGRPolygon(); + + for( iRing = 0; iRing < nRingCount; iRing++ ) + { + if( nBytes - nNextByte < 4 ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + memcpy( &nPointCount, pabyData + nNextByte, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount - 2 > (INT_MAX - 16 * 2) / 8) + { + delete poPoly; + return OGRERR_CORRUPT_DATA; + } + + compressedSize = 16 * 2; // first and last Points + compressedSize += 8 * (nPointCount - 2); // intermediate Points + + nNextByte += 4; + adfTupleBase[0] = 0.0; + adfTupleBase[1] = 0.0; + + if (nBytes - nNextByte < compressedSize ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + poLR = new OGRLinearRing(); + poLR->setNumPoints( nPointCount, FALSE ); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + if ( iPoint == 0 || iPoint == (nPointCount - 1 ) ) + { + // first and last Points are uncompressed + memcpy( adfTuple, pabyData + nNextByte, 2*8 ); + nNextByte += 2 * 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + } + } + else + { + // any other intermediate Point is compressed + memcpy( asfTuple, pabyData + nNextByte, 2*4 ); + nNextByte += 2 * 4; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP32PTR( asfTuple ); + CPL_SWAP32PTR( asfTuple + 1 ); + } + adfTuple[0] = asfTuple[0] + adfTupleBase[0]; + adfTuple[1] = asfTuple[1] + adfTupleBase[1]; + } + + poLR->setPoint( iPoint, adfTuple[0], adfTuple[1] ); + adfTupleBase[0] = adfTuple[0]; + adfTupleBase[1] = adfTuple[1]; + } + + poPoly->addRingDirectly( poLR ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* Polygon [XYZ] Compressed */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSpliteComprPolygonXYZ ) + { + double adfTuple[3]; + double adfTupleBase[3]; + float asfTuple[3]; + GInt32 nPointCount; + GInt32 nRingCount; + int iPoint, iRing; + OGRLinearRing *poLR; + OGRPolygon *poPoly; + int nNextByte; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nRingCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nRingCount ); + + if (nRingCount < 0 || nRingCount > INT_MAX / 4) + return OGRERR_CORRUPT_DATA; + + // Each ring has a minimum of 4 bytes + if (nBytes - 8 < nRingCount * 4) + return OGRERR_NOT_ENOUGH_DATA; + + nNextByte = 8; + + poGeom = poPoly = new OGRPolygon(); + + for( iRing = 0; iRing < nRingCount; iRing++ ) + { + if( nBytes - nNextByte < 4 ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + memcpy( &nPointCount, pabyData + nNextByte, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount - 2 > (INT_MAX - 24 * 2) / 12) + { + delete poPoly; + return OGRERR_CORRUPT_DATA; + } + + compressedSize = 24 * 2; // first and last Points + compressedSize += 12 * (nPointCount - 2); // intermediate Points + + nNextByte += 4; + adfTupleBase[0] = 0.0; + adfTupleBase[1] = 0.0; + adfTupleBase[2] = 0.0; + + if (nBytes - nNextByte < compressedSize ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + poLR = new OGRLinearRing(); + poLR->setNumPoints( nPointCount, FALSE ); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + if ( iPoint == 0 || iPoint == (nPointCount - 1 ) ) + { + // first and last Points are uncompressed + memcpy( adfTuple, pabyData + nNextByte, 3*8 ); + nNextByte += 3 * 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + } + } + else + { + // any other intermediate Point is compressed + memcpy( asfTuple, pabyData + nNextByte, 3*4 ); + nNextByte += 3 * 4; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP32PTR( asfTuple ); + CPL_SWAP32PTR( asfTuple + 1 ); + CPL_SWAP32PTR( asfTuple + 2 ); + } + adfTuple[0] = asfTuple[0] + adfTupleBase[0]; + adfTuple[1] = asfTuple[1] + adfTupleBase[1]; + adfTuple[2] = asfTuple[2] + adfTupleBase[2]; + } + + poLR->setPoint( iPoint, adfTuple[0], adfTuple[1], adfTuple[2] ); + adfTupleBase[0] = adfTuple[0]; + adfTupleBase[1] = adfTuple[1]; + adfTupleBase[2] = adfTuple[2]; + } + + poPoly->addRingDirectly( poLR ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* Polygon [XYM] Compressed */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSpliteComprPolygonXYM ) + { + double adfTuple[2]; + double adfTupleBase[3]; + float asfTuple[2]; + GInt32 nPointCount; + GInt32 nRingCount; + int iPoint, iRing; + OGRLinearRing *poLR; + OGRPolygon *poPoly; + int nNextByte; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nRingCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nRingCount ); + + if (nRingCount < 0 || nRingCount > INT_MAX / 4) + return OGRERR_CORRUPT_DATA; + + // Each ring has a minimum of 4 bytes + if (nBytes - 8 < nRingCount * 4) + return OGRERR_NOT_ENOUGH_DATA; + + nNextByte = 8; + + poGeom = poPoly = new OGRPolygon(); + + for( iRing = 0; iRing < nRingCount; iRing++ ) + { + if( nBytes - nNextByte < 4 ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + memcpy( &nPointCount, pabyData + nNextByte, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + + if( nPointCount < 0 || nPointCount - 2 > (INT_MAX - 24 * 2) / 16) + { + delete poPoly; + return OGRERR_CORRUPT_DATA; + } + + compressedSize = 24 * 2; // first and last Points + compressedSize += 16 * (nPointCount - 2); // intermediate Points + + nNextByte += 4; + adfTupleBase[0] = 0.0; + adfTupleBase[1] = 0.0; + + if (nBytes - nNextByte < compressedSize ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + poLR = new OGRLinearRing(); + poLR->setNumPoints( nPointCount, FALSE ); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + if ( iPoint == 0 || iPoint == (nPointCount - 1 ) ) + { + // first and last Points are uncompressed + memcpy( adfTuple, pabyData + nNextByte, 2*8 ); + nNextByte += 2 * 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + } + } + else + { + // any other intermediate Point is compressed + memcpy( asfTuple, pabyData + nNextByte, 2*4 ); + nNextByte += 2 * 4 + 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP32PTR( asfTuple ); + CPL_SWAP32PTR( asfTuple + 1 ); + } + adfTuple[0] = asfTuple[0] + adfTupleBase[0]; + adfTuple[1] = asfTuple[1] + adfTupleBase[1]; + } + + poLR->setPoint( iPoint, adfTuple[0], adfTuple[1] ); + adfTupleBase[0] = adfTuple[0]; + adfTupleBase[1] = adfTuple[1]; + } + + poPoly->addRingDirectly( poLR ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* Polygon [XYZM] Compressed */ +/* -------------------------------------------------------------------- */ + else if( nGType == OGRSpliteComprPolygonXYZM ) + { + double adfTuple[3]; + double adfTupleBase[3]; + float asfTuple[3]; + GInt32 nPointCount; + GInt32 nRingCount; + int iPoint, iRing; + OGRLinearRing *poLR; + OGRPolygon *poPoly; + int nNextByte; + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nRingCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nRingCount ); + + if (nRingCount < 0 || nRingCount > INT_MAX / 4) + return OGRERR_CORRUPT_DATA; + + // Each ring has a minimum of 4 bytes + if (nBytes - 8 < nRingCount * 4) + return OGRERR_NOT_ENOUGH_DATA; + + nNextByte = 8; + + poGeom = poPoly = new OGRPolygon(); + + for( iRing = 0; iRing < nRingCount; iRing++ ) + { + if( nBytes - nNextByte < 4 ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + memcpy( &nPointCount, pabyData + nNextByte, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nPointCount ); + + if( nPointCount < 0 || nPointCount - 2 > (INT_MAX - 32 * 2) / 20) + { + delete poPoly; + return OGRERR_CORRUPT_DATA; + } + + compressedSize = 32 * 2; // first and last Points + /* Note 20 is not an error : x,y,z are float and the m is a double */ + compressedSize += 20 * (nPointCount - 2); // intermediate Points + + nNextByte += 4; + adfTupleBase[0] = 0.0; + adfTupleBase[1] = 0.0; + adfTupleBase[2] = 0.0; + + if (nBytes - nNextByte < compressedSize ) + { + delete poPoly; + return OGRERR_NOT_ENOUGH_DATA; + } + + poLR = new OGRLinearRing(); + poLR->setNumPoints( nPointCount, FALSE ); + + for( iPoint = 0; iPoint < nPointCount; iPoint++ ) + { + if ( iPoint == 0 || iPoint == (nPointCount - 1 ) ) + { + // first and last Points are uncompressed + memcpy( adfTuple, pabyData + nNextByte, 3*8 ); + nNextByte += 4 * 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( adfTuple ); + CPL_SWAP64PTR( adfTuple + 1 ); + CPL_SWAP64PTR( adfTuple + 2 ); + } + } + else + { + // any other intermediate Point is compressed + memcpy( asfTuple, pabyData + nNextByte, 3*4 ); + nNextByte += 3 * 4 + 8; + + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP32PTR( asfTuple ); + CPL_SWAP32PTR( asfTuple + 1 ); + CPL_SWAP32PTR( asfTuple + 2 ); + } + adfTuple[0] = asfTuple[0] + adfTupleBase[0]; + adfTuple[1] = asfTuple[1] + adfTupleBase[1]; + adfTuple[2] = asfTuple[2] + adfTupleBase[2]; + } + + poLR->setPoint( iPoint, adfTuple[0], adfTuple[1], adfTuple[2] ); + adfTupleBase[0] = adfTuple[0]; + adfTupleBase[1] = adfTuple[1]; + adfTupleBase[2] = adfTuple[2]; + } + + poPoly->addRingDirectly( poLR ); + } + + if( pnBytesConsumed ) + *pnBytesConsumed = nNextByte; + } + +/* -------------------------------------------------------------------- */ +/* GeometryCollections of various kinds. */ +/* -------------------------------------------------------------------- */ + else if( ( nGType >= OGRSpliteMultiPointXY && + nGType <= OGRSpliteGeometryCollectionXY ) || // XY types + ( nGType >= OGRSpliteMultiPointXYZ && + nGType <= OGRSpliteGeometryCollectionXYZ ) || // XYZ types + ( nGType >= OGRSpliteMultiPointXYM && + nGType <= OGRSpliteGeometryCollectionXYM ) || // XYM types + ( nGType >= OGRSpliteMultiPointXYZM && + nGType <= OGRSpliteGeometryCollectionXYZM ) || // XYZM types + ( nGType >= OGRSpliteComprMultiLineStringXY && + nGType <= OGRSpliteComprGeometryCollectionXY ) || // XY compressed + ( nGType >= OGRSpliteComprMultiLineStringXYZ && + nGType <= OGRSpliteComprGeometryCollectionXYZ ) || // XYZ compressed + ( nGType >= OGRSpliteComprMultiLineStringXYM && + nGType <= OGRSpliteComprGeometryCollectionXYM ) || // XYM compressed + ( nGType >= OGRSpliteComprMultiLineStringXYZM && + nGType <= OGRSpliteComprGeometryCollectionXYZM ) ) // XYZM compressed + { + OGRGeometryCollection *poGC = NULL; + GInt32 nGeomCount = 0; + int iGeom = 0; + int nBytesUsed = 0; + + switch ( nGType ) + { + case OGRSpliteMultiPointXY: + case OGRSpliteMultiPointXYZ: + case OGRSpliteMultiPointXYM: + case OGRSpliteMultiPointXYZM: + poGC = new OGRMultiPoint(); + break; + case OGRSpliteMultiLineStringXY: + case OGRSpliteMultiLineStringXYZ: + case OGRSpliteMultiLineStringXYM: + case OGRSpliteMultiLineStringXYZM: + case OGRSpliteComprMultiLineStringXY: + case OGRSpliteComprMultiLineStringXYZ: + case OGRSpliteComprMultiLineStringXYM: + case OGRSpliteComprMultiLineStringXYZM: + poGC = new OGRMultiLineString(); + break; + case OGRSpliteMultiPolygonXY: + case OGRSpliteMultiPolygonXYZ: + case OGRSpliteMultiPolygonXYM: + case OGRSpliteMultiPolygonXYZM: + case OGRSpliteComprMultiPolygonXY: + case OGRSpliteComprMultiPolygonXYZ: + case OGRSpliteComprMultiPolygonXYM: + case OGRSpliteComprMultiPolygonXYZM: + poGC = new OGRMultiPolygon(); + break; + case OGRSpliteGeometryCollectionXY: + case OGRSpliteGeometryCollectionXYZ: + case OGRSpliteGeometryCollectionXYM: + case OGRSpliteGeometryCollectionXYZM: + case OGRSpliteComprGeometryCollectionXY: + case OGRSpliteComprGeometryCollectionXYZ: + case OGRSpliteComprGeometryCollectionXYM: + case OGRSpliteComprGeometryCollectionXYZM: + poGC = new OGRGeometryCollection(); + break; + } + + assert(NULL != poGC); + + if( nBytes < 8 ) + return OGRERR_NOT_ENOUGH_DATA; + + memcpy( &nGeomCount, pabyData + 4, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nGeomCount ); + + if (nGeomCount < 0 || nGeomCount > INT_MAX / 9) + return OGRERR_CORRUPT_DATA; + + // Each sub geometry takes at least 9 bytes + if (nBytes - 8 < nGeomCount * 9) + return OGRERR_NOT_ENOUGH_DATA; + + nBytesUsed = 8; + + for( iGeom = 0; iGeom < nGeomCount; iGeom++ ) + { + int nThisGeomSize; + OGRGeometry *poThisGeom = NULL; + + if (nBytes - nBytesUsed < 5) + { + delete poGC; + return OGRERR_NOT_ENOUGH_DATA; + } + + if (pabyData[nBytesUsed] != 0x69) + { + delete poGC; + return OGRERR_CORRUPT_DATA; + } + + nBytesUsed ++; + + eErr = createFromSpatialiteInternal( pabyData + nBytesUsed, + &poThisGeom, nBytes - nBytesUsed, + eByteOrder, &nThisGeomSize, nRecLevel + 1); + if( eErr != OGRERR_NONE ) + { + delete poGC; + return eErr; + } + + nBytesUsed += nThisGeomSize; + eErr = poGC->addGeometryDirectly( poThisGeom ); + if( eErr != OGRERR_NONE ) + { + delete poGC; + return eErr; + } + } + + poGeom = poGC; + if( pnBytesConsumed ) + *pnBytesConsumed = nBytesUsed; + } + +/* -------------------------------------------------------------------- */ +/* Currently unsupported geometry. */ +/* -------------------------------------------------------------------- */ + else + { + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + +/* -------------------------------------------------------------------- */ +/* Assign spatial reference system. */ +/* -------------------------------------------------------------------- */ + if( eErr == OGRERR_NONE ) + { + *ppoReturn = poGeom; + } + else + { + delete poGeom; + } + + return eErr; +} + +/************************************************************************/ +/* ImportSpatiaLiteGeometry() */ +/************************************************************************/ + +OGRErr OGRSQLiteLayer::ImportSpatiaLiteGeometry( const GByte *pabyData, + int nBytes, + OGRGeometry **ppoGeometry ) + +{ + return ImportSpatiaLiteGeometry(pabyData, nBytes, ppoGeometry, NULL); +} + +/************************************************************************/ +/* ImportSpatiaLiteGeometry() */ +/************************************************************************/ + +OGRErr OGRSQLiteLayer::ImportSpatiaLiteGeometry( const GByte *pabyData, + int nBytes, + OGRGeometry **ppoGeometry, + int* pnSRID ) + +{ + OGRwkbByteOrder eByteOrder; + + *ppoGeometry = NULL; + + if( nBytes < 44 + || pabyData[0] != 0 + || pabyData[38] != 0x7C + || pabyData[nBytes-1] != 0xFE ) + return OGRERR_CORRUPT_DATA; + + eByteOrder = (OGRwkbByteOrder) pabyData[1]; + +/* -------------------------------------------------------------------- */ +/* Decode the geometry type. */ +/* -------------------------------------------------------------------- */ + if( pnSRID != NULL ) + { + int nSRID; + memcpy( &nSRID, pabyData + 2, 4 ); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( &nSRID ); + *pnSRID = nSRID; + } + + int nBytesConsumed = 0; + OGRErr eErr = createFromSpatialiteInternal(pabyData + 39, ppoGeometry, + nBytes - 39, eByteOrder, &nBytesConsumed, 0); + if( eErr == OGRERR_NONE ) + { + /* This is a hack: in OGR2SQLITE_ExportGeometry(), we may have added */ + /* the original curve geometry after the spatialite blob, so in case */ + /* we detect that there's still binary */ + /* content after the spatialite blob, this may be our original geometry */ + if( pabyData[39 + nBytesConsumed] == 0xFE && 39 + nBytesConsumed + 1 < nBytes ) + { + OGRGeometry* poOriginalGeometry = NULL; + eErr = OGRGeometryFactory::createFromWkb( + (unsigned char*)(pabyData + 39 + nBytesConsumed + 1), + NULL, &poOriginalGeometry, nBytes - (39 + nBytesConsumed + 1 + 1)); + if( eErr == OGRERR_NONE ) + { + delete *ppoGeometry; + *ppoGeometry = poOriginalGeometry; + } + } + } + return eErr; +} + +/************************************************************************/ +/* CanBeCompressedSpatialiteGeometry() */ +/************************************************************************/ + +int OGRSQLiteLayer::CanBeCompressedSpatialiteGeometry(const OGRGeometry *poGeometry) +{ + switch (wkbFlatten(poGeometry->getGeometryType())) + { + case wkbLineString: + case wkbLinearRing: + { + int nPoints = ((OGRLineString*)poGeometry)->getNumPoints(); + return nPoints >= 2; + } + + case wkbPolygon: + { + OGRPolygon* poPoly = (OGRPolygon*) poGeometry; + if (poPoly->getExteriorRing() != NULL) + { + if (!CanBeCompressedSpatialiteGeometry(poPoly->getExteriorRing())) + return FALSE; + + int nInteriorRingCount = poPoly->getNumInteriorRings(); + for(int i=0;igetInteriorRing(i))) + return FALSE; + } + } + return TRUE; + } + + case wkbMultiPoint: + case wkbMultiLineString: + case wkbMultiPolygon: + case wkbGeometryCollection: + { + OGRGeometryCollection* poGeomCollection = (OGRGeometryCollection*) poGeometry; + int nParts = poGeomCollection->getNumGeometries(); + for(int i=0;igetGeometryRef(i))) + return FALSE; + } + return TRUE; + } + + default: + return FALSE; + } +} + +/************************************************************************/ +/* ComputeSpatiaLiteGeometrySize() */ +/************************************************************************/ + +int OGRSQLiteLayer::ComputeSpatiaLiteGeometrySize(const OGRGeometry *poGeometry, + int bHasM, int bSpatialite2D, + int bUseComprGeom) +{ + switch (wkbFlatten(poGeometry->getGeometryType())) + { + case wkbPoint: + if ( bSpatialite2D == TRUE ) + return 16; + else if (poGeometry->getCoordinateDimension() == 3) + { + if (bHasM == TRUE) + return 32; + else + return 24; + } + else + { + if (bHasM == TRUE) + return 24; + else + return 16; + } + + case wkbLineString: + case wkbLinearRing: + { + int nPoints = ((OGRLineString*)poGeometry)->getNumPoints(); + int nDimension; + int nPointsDouble = nPoints; + int nPointsFloat = 0; + if ( bSpatialite2D == TRUE ) + { + nDimension = 2; + bHasM = FALSE; + } + else + { + if ( bUseComprGeom && nPoints >= 2 ) + { + nPointsDouble = 2; + nPointsFloat = nPoints - 2; + } + nDimension = poGeometry->getCoordinateDimension(); + } + return 4 + nDimension * (8 * nPointsDouble + 4 * nPointsFloat) + ((bHasM) ? nPoints * 8 : 0); + } + + case wkbPolygon: + { + int nSize = 4; + OGRPolygon* poPoly = (OGRPolygon*) poGeometry; + bUseComprGeom = bUseComprGeom && !bSpatialite2D && CanBeCompressedSpatialiteGeometry(poGeometry); + if (poPoly->getExteriorRing() != NULL) + { + nSize += ComputeSpatiaLiteGeometrySize(poPoly->getExteriorRing(), + bHasM, bSpatialite2D, bUseComprGeom); + + int nInteriorRingCount = poPoly->getNumInteriorRings(); + for(int i=0;igetInteriorRing(i), + bHasM, bSpatialite2D, bUseComprGeom ); + } + return nSize; + } + + case wkbMultiPoint: + case wkbMultiLineString: + case wkbMultiPolygon: + case wkbGeometryCollection: + { + int nSize = 4; + OGRGeometryCollection* poGeomCollection = (OGRGeometryCollection*) poGeometry; + int nParts = poGeomCollection->getNumGeometries(); + for(int i=0;igetGeometryRef(i), + bHasM, bSpatialite2D, bUseComprGeom ); + return nSize; + } + + default: + CPLError(CE_Failure, CPLE_AppDefined, "Unexpected geometry type"); + return 0; + } +} + +/************************************************************************/ +/* GetSpatialiteGeometryCode() */ +/************************************************************************/ + +int OGRSQLiteLayer::GetSpatialiteGeometryCode(const OGRGeometry *poGeometry, + int bHasM, int bSpatialite2D, + int bUseComprGeom, + int bAcceptMultiGeom) +{ + OGRwkbGeometryType eType = wkbFlatten(poGeometry->getGeometryType()); + switch (eType) + { + case wkbPoint: + if ( bSpatialite2D == TRUE ) + return OGRSplitePointXY; + else if (poGeometry->getCoordinateDimension() == 3) + { + if (bHasM == TRUE) + return OGRSplitePointXYZM; + else + return OGRSplitePointXYZ; + } + else + { + if (bHasM == TRUE) + return OGRSplitePointXYM; + else + return OGRSplitePointXY; + } + break; + + case wkbLineString: + case wkbLinearRing: + if ( bSpatialite2D == TRUE ) + return OGRSpliteLineStringXY; + else if (poGeometry->getCoordinateDimension() == 3) + { + if (bHasM == TRUE) + return (bUseComprGeom) ? OGRSpliteComprLineStringXYZM : OGRSpliteLineStringXYZM; + else + return (bUseComprGeom) ? OGRSpliteComprLineStringXYZ : OGRSpliteLineStringXYZ; + } + else + { + if (bHasM == TRUE) + return (bUseComprGeom) ? OGRSpliteComprLineStringXYM : OGRSpliteLineStringXYM; + else + return (bUseComprGeom) ? OGRSpliteComprLineStringXY : OGRSpliteLineStringXY; + } + break; + + case wkbPolygon: + if ( bSpatialite2D == TRUE ) + return OGRSplitePolygonXY; + else if (poGeometry->getCoordinateDimension() == 3) + { + if (bHasM == TRUE) + return (bUseComprGeom) ? OGRSpliteComprPolygonXYZM : OGRSplitePolygonXYZM; + else + return (bUseComprGeom) ? OGRSpliteComprPolygonXYZ : OGRSplitePolygonXYZ; + } + else + { + if (bHasM == TRUE) + return (bUseComprGeom) ? OGRSpliteComprPolygonXYM : OGRSplitePolygonXYM; + else + return (bUseComprGeom) ? OGRSpliteComprPolygonXY : OGRSplitePolygonXY; + } + break; + + default: + break; + } + + if (!bAcceptMultiGeom) + { + CPLError(CE_Failure, CPLE_AppDefined, "Unexpected geometry type"); + return 0; + } + + switch (eType) + { + case wkbMultiPoint: + if ( bSpatialite2D == TRUE ) + return OGRSpliteMultiPointXY; + else if (poGeometry->getCoordinateDimension() == 3) + { + if (bHasM == TRUE) + return OGRSpliteMultiPointXYZM; + else + return OGRSpliteMultiPointXYZ; + } + else + { + if (bHasM == TRUE) + return OGRSpliteMultiPointXYM; + else + return OGRSpliteMultiPointXY; + } + break; + + case wkbMultiLineString: + if ( bSpatialite2D == TRUE ) + return OGRSpliteMultiLineStringXY; + else if (poGeometry->getCoordinateDimension() == 3) + { + if (bHasM == TRUE) + return /*(bUseComprGeom) ? OGRSpliteComprMultiLineStringXYZM :*/ OGRSpliteMultiLineStringXYZM; + else + return /*(bUseComprGeom) ? OGRSpliteComprMultiLineStringXYZ :*/ OGRSpliteMultiLineStringXYZ; + } + else + { + if (bHasM == TRUE) + return /*(bUseComprGeom) ? OGRSpliteComprMultiLineStringXYM :*/ OGRSpliteMultiLineStringXYM; + else + return /*(bUseComprGeom) ? OGRSpliteComprMultiLineStringXY :*/ OGRSpliteMultiLineStringXY; + } + break; + + case wkbMultiPolygon: + if ( bSpatialite2D == TRUE ) + return OGRSpliteMultiPolygonXY; + else if (poGeometry->getCoordinateDimension() == 3) + { + if (bHasM == TRUE) + return /*(bUseComprGeom) ? OGRSpliteComprMultiPolygonXYZM :*/ OGRSpliteMultiPolygonXYZM; + else + return /*(bUseComprGeom) ? OGRSpliteComprMultiPolygonXYZ :*/ OGRSpliteMultiPolygonXYZ; + } + else + { + if (bHasM == TRUE) + return /*(bUseComprGeom) ? OGRSpliteComprMultiPolygonXYM :*/ OGRSpliteMultiPolygonXYM; + else + return /*(bUseComprGeom) ? OGRSpliteComprMultiPolygonXY :*/ OGRSpliteMultiPolygonXY; + } + break; + + + case wkbGeometryCollection: + if ( bSpatialite2D == TRUE ) + return OGRSpliteGeometryCollectionXY; + else if (poGeometry->getCoordinateDimension() == 3) + { + if (bHasM == TRUE) + return /*(bUseComprGeom) ? OGRSpliteComprGeometryCollectionXYZM :*/ OGRSpliteGeometryCollectionXYZM; + else + return /*(bUseComprGeom) ? OGRSpliteComprGeometryCollectionXYZ :*/ OGRSpliteGeometryCollectionXYZ; + } + else + { + if (bHasM == TRUE) + return /*(bUseComprGeom) ? OGRSpliteComprGeometryCollectionXYM :*/ OGRSpliteGeometryCollectionXYM; + else + return /*(bUseComprGeom) ? OGRSpliteComprGeometryCollectionXY :*/ OGRSpliteGeometryCollectionXY; + } + break; + + default: + CPLError(CE_Failure, CPLE_AppDefined, "Unexpected geometry type"); + return 0; + } +} + +/************************************************************************/ +/* ExportSpatiaLiteGeometry() */ +/************************************************************************/ + +int OGRSQLiteLayer::ExportSpatiaLiteGeometryInternal(const OGRGeometry *poGeometry, + OGRwkbByteOrder eByteOrder, + int bHasM, int bSpatialite2D, + int bUseComprGeom, + GByte* pabyData ) +{ + switch (wkbFlatten(poGeometry->getGeometryType())) + { + case wkbPoint: + { + OGRPoint* poPoint = (OGRPoint*) poGeometry; + double x = poPoint->getX(); + double y = poPoint->getY(); + memcpy(pabyData, &x, 8); + memcpy(pabyData + 8, &y, 8); + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( pabyData ); + CPL_SWAP64PTR( pabyData + 8 ); + } + if ( bSpatialite2D == TRUE ) + return 16; + else if (poGeometry->getCoordinateDimension() == 3) + { + double z = poPoint->getZ(); + memcpy(pabyData + 16, &z, 8); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP64PTR( pabyData + 16 ); + if (bHasM == TRUE) + { + double m = 0.0; + memcpy(pabyData + 24, &m, 8); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP64PTR( pabyData + 24 ); + return 32; + } + else + return 24; + } + else + { + if (bHasM == TRUE) + { + double m = 0.0; + memcpy(pabyData + 16, &m, 8); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP64PTR( pabyData + 16 ); + return 24; + } + else + return 16; + } + } + + case wkbLineString: + case wkbLinearRing: + { + OGRLineString* poLineString = (OGRLineString*) poGeometry; + int nTotalSize = 4; + int nPointCount = poLineString->getNumPoints(); + memcpy(pabyData, &nPointCount, 4); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( pabyData ); + + for(int i=0;igetX(i); + double y = poLineString->getY(i); + + if (!bUseComprGeom || i == 0 || i == nPointCount - 1) + { + memcpy(pabyData + nTotalSize, &x, 8); + memcpy(pabyData + nTotalSize + 8, &y, 8); + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP64PTR( pabyData + nTotalSize ); + CPL_SWAP64PTR( pabyData + nTotalSize + 8 ); + } + if (!bSpatialite2D && poGeometry->getCoordinateDimension() == 3) + { + double z = poLineString->getZ(i); + memcpy(pabyData + nTotalSize + 16, &z, 8); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP64PTR( pabyData + nTotalSize + 16 ); + if (bHasM == TRUE) + { + double m = 0.0; + memcpy(pabyData + nTotalSize + 24, &m, 8); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP64PTR( pabyData + nTotalSize + 24 ); + nTotalSize += 32; + } + else + nTotalSize += 24; + } + else + { + if (bHasM == TRUE) + { + double m = 0.0; + memcpy(pabyData + nTotalSize + 16, &m, 8); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP64PTR( pabyData + nTotalSize + 16 ); + nTotalSize += 24; + } + else + nTotalSize += 16; + } + } + else /* Compressed intermediate points */ + { + float deltax = (float)(x - poLineString->getX(i-1)); + float deltay = (float)(y - poLineString->getY(i-1)); + memcpy(pabyData + nTotalSize, &deltax, 4); + memcpy(pabyData + nTotalSize + 4, &deltay, 4); + if (NEED_SWAP_SPATIALITE()) + { + CPL_SWAP32PTR( pabyData + nTotalSize ); + CPL_SWAP32PTR( pabyData + nTotalSize + 4 ); + } + if (poGeometry->getCoordinateDimension() == 3) + { + double z = poLineString->getZ(i); + float deltaz = (float)(z - poLineString->getZ(i-1)); + memcpy(pabyData + nTotalSize + 8, &deltaz, 4); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( pabyData + nTotalSize + 8 ); + if (bHasM == TRUE) + { + double m = 0.0; + memcpy(pabyData + nTotalSize + 12, &m, 8); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP64PTR( pabyData + nTotalSize + 12 ); + nTotalSize += 20; + } + else + nTotalSize += 12; + } + else + { + if (bHasM == TRUE) + { + double m = 0.0; + memcpy(pabyData + nTotalSize + 8, &m, 8); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP64PTR( pabyData + nTotalSize + 8 ); + nTotalSize += 16; + } + else + nTotalSize += 8; + } + } + } + return nTotalSize; + } + + case wkbPolygon: + { + OGRPolygon* poPoly = (OGRPolygon*) poGeometry; + int nParts = 0; + int nTotalSize = 4; + if (poPoly->getExteriorRing() != NULL) + { + int nInteriorRingCount = poPoly->getNumInteriorRings(); + nParts = 1 + nInteriorRingCount; + memcpy(pabyData, &nParts, 4); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( pabyData ); + + nTotalSize += ExportSpatiaLiteGeometryInternal(poPoly->getExteriorRing(), + eByteOrder, + bHasM, bSpatialite2D, + bUseComprGeom, + pabyData + nTotalSize); + + for(int i=0;igetInteriorRing(i), + eByteOrder, + bHasM, bSpatialite2D, + bUseComprGeom, + pabyData + nTotalSize); + } + } + else + { + memset(pabyData, 0, 4); + } + return nTotalSize; + } + + case wkbMultiPoint: + case wkbMultiLineString: + case wkbMultiPolygon: + case wkbGeometryCollection: + { + OGRGeometryCollection* poGeomCollection = (OGRGeometryCollection*) poGeometry; + int nTotalSize = 4; + int nParts = poGeomCollection->getNumGeometries(); + memcpy(pabyData, &nParts, 4); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( pabyData ); + + for(int i=0;igetGeometryRef(i), + bHasM, bSpatialite2D, + bUseComprGeom, FALSE); + if (nCode == 0) + return 0; + memcpy(pabyData + nTotalSize, &nCode, 4); + if (NEED_SWAP_SPATIALITE()) + CPL_SWAP32PTR( pabyData + nTotalSize ); + nTotalSize += 4; + nTotalSize += ExportSpatiaLiteGeometryInternal(poGeomCollection->getGeometryRef(i), + eByteOrder, + bHasM, bSpatialite2D, + bUseComprGeom, + pabyData + nTotalSize); + } + return nTotalSize; + } + + default: + return 0; + } +} + + +OGRErr OGRSQLiteLayer::ExportSpatiaLiteGeometry( const OGRGeometry *poGeometry, + GInt32 nSRID, + OGRwkbByteOrder eByteOrder, + int bHasM, int bSpatialite2D, + int bUseComprGeom, + GByte **ppabyData, + int *pnDataLenght ) + +{ + /* Spatialite does not support curve geometries */ + const OGRGeometry* poWorkGeom; + if( poGeometry->hasCurveGeometry() ) + poWorkGeom = poGeometry->getLinearGeometry(); + else + poWorkGeom = poGeometry; + + bUseComprGeom = bUseComprGeom && !bSpatialite2D && CanBeCompressedSpatialiteGeometry(poWorkGeom); + + int nDataLen = 44 + ComputeSpatiaLiteGeometrySize( poWorkGeom, + bHasM, + bSpatialite2D, + bUseComprGeom ); + OGREnvelope sEnvelope; + + *ppabyData = (GByte *) CPLMalloc( nDataLen ); + + (*ppabyData)[0] = 0x00; + (*ppabyData)[1] = (GByte) eByteOrder; + + // Write out SRID + memcpy( *ppabyData + 2, &nSRID, 4 ); + + // Write out the geometry bounding rectangle + poGeometry->getEnvelope( &sEnvelope ); + memcpy( *ppabyData + 6, &sEnvelope.MinX, 8 ); + memcpy( *ppabyData + 14, &sEnvelope.MinY, 8 ); + memcpy( *ppabyData + 22, &sEnvelope.MaxX, 8 ); + memcpy( *ppabyData + 30, &sEnvelope.MaxY, 8 ); + + (*ppabyData)[38] = 0x7C; + + int nCode = GetSpatialiteGeometryCode(poWorkGeom, + bHasM, bSpatialite2D, + bUseComprGeom, TRUE); + if (nCode == 0) + { + CPLFree(*ppabyData); + *ppabyData = NULL; + *pnDataLenght = 0; + if( poWorkGeom != poGeometry ) delete poWorkGeom; + return CE_Failure; + } + memcpy( *ppabyData + 39, &nCode, 4 ); + + int nWritten = ExportSpatiaLiteGeometryInternal(poWorkGeom, + eByteOrder, + bHasM, bSpatialite2D, + bUseComprGeom, + *ppabyData + 43); + if( poWorkGeom != poGeometry ) delete poWorkGeom; + + if (nWritten == 0) + { + CPLFree(*ppabyData); + *ppabyData = NULL; + *pnDataLenght = 0; + return CE_Failure; + } + + (*ppabyData)[nDataLen - 1] = 0xFE; + + if( NEED_SWAP_SPATIALITE() ) + { + CPL_SWAP32PTR( *ppabyData + 2 ); + CPL_SWAP64PTR( *ppabyData + 6 ); + CPL_SWAP64PTR( *ppabyData + 14 ); + CPL_SWAP64PTR( *ppabyData + 22 ); + CPL_SWAP64PTR( *ppabyData + 30 ); + CPL_SWAP32PTR( *ppabyData + 39 ); + } + + *pnDataLenght = nDataLen; + + return CE_None; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSQLiteLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else if( EQUAL(pszCap,OLCIgnoreFields) ) + return TRUE; + + else if( EQUAL(pszCap,OLCTransactions) ) + return TRUE; + + else + return FALSE; +} + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +OGRErr OGRSQLiteLayer::StartTransaction() + +{ + return poDS->StartTransaction(); +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr OGRSQLiteLayer::CommitTransaction() + +{ + return poDS->CommitTransaction(); +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +OGRErr OGRSQLiteLayer::RollbackTransaction() + +{ + return poDS->RollbackTransaction(); +} + +/************************************************************************/ +/* ClearStatement() */ +/************************************************************************/ + +void OGRSQLiteLayer::ClearStatement() + +{ + if( hStmt != NULL ) + { + CPLDebug( "OGR_SQLITE", "finalize %p", hStmt ); + sqlite3_finalize( hStmt ); + hStmt = NULL; + } +} + +/************************************************************************/ +/* OGRSQLITEStringToDateTimeField() */ +/************************************************************************/ + +int OGRSQLITEStringToDateTimeField( OGRFeature* poFeature, int iField, + const char* pszValue ) +{ + int nYear = 0, nMonth = 0, nDay = 0, + nHour = 0, nMinute = 0; + float fSecond = 0; + + /* YYYY-MM-DD HH:MM:SS or YYYY-MM-DD HH:MM:SS.SSS */ + nYear = 0; nMonth = 0; nDay = 0; nHour = 0; + nMinute = 0; fSecond = 0; + if( sscanf(pszValue, "%04d-%02d-%02d %02d:%02d:%f", + &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) == 6 || + sscanf(pszValue, "%04d/%02d/%02d %02d:%02d:%f", + &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) == 6 ) + { + if( poFeature ) + poFeature->SetField( iField, nYear, nMonth, + nDay, nHour, nMinute, fSecond, 0); + return OFTDateTime; + } + + /* YYYY-MM-DD HH:MM */ + nYear = 0; nMonth = 0; nDay = 0; nHour = 0; + nMinute = 0; + if( sscanf(pszValue, "%04d-%02d-%02d %02d:%02d", + &nYear, &nMonth, &nDay, &nHour, &nMinute) == 5 || + sscanf(pszValue, "%04d/%02d/%02d %02d:%02d", + &nYear, &nMonth, &nDay, &nHour, &nMinute) == 5 ) + { + if( poFeature ) + poFeature->SetField( iField, nYear, nMonth, + nDay, nHour, nMinute, 0, 0); + return OFTDateTime; + } + + /* YYYY-MM-DDTHH:MM:SS or YYYY-MM-DDTHH:MM:SS.SSS */ + nYear = 0; nMonth = 0; nDay = 0; nHour = 0; + nMinute = 0; fSecond = 0; + if( sscanf(pszValue, "%04d-%02d-%02dT%02d:%02d:%f", + &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond) == 6 ) + { + if( poFeature ) + poFeature->SetField( iField, nYear, nMonth, nDay, + nHour, nMinute, fSecond, 0); + return OFTDateTime; + } + + /* YYYY-MM-DDTHH:MM */ + nYear = 0; nMonth = 0; nDay = 0; nHour = 0; + nMinute = 0; + if( sscanf(pszValue, "%04d-%02d-%02dT%02d:%02d", + &nYear, &nMonth, &nDay, &nHour, &nMinute) == 5 ) + { + if( poFeature ) + poFeature->SetField( iField, nYear, nMonth, nDay, + nHour, nMinute, 0, 0); + return OFTDateTime; + } + + /* YYYY-MM-DD */ + nYear = 0; nMonth = 0; nDay = 0; + if( sscanf(pszValue, "%04d-%02d-%02d", + &nYear, &nMonth, &nDay) == 3 || + sscanf(pszValue, "%04d/%02d/%02d", + &nYear, &nMonth, &nDay) == 3 ) + { + if( poFeature ) + poFeature->SetField( iField, nYear, nMonth, nDay, + 0, 0, 0, 0 ); + return OFTDate; + } + + /* HH:MM:SS or HH:MM:SS.SSS */ + nDay = 0; nHour = 0; fSecond = 0; + if( sscanf(pszValue, "%02d:%02d:%f", + &nHour, &nMinute, &fSecond) == 3 ) + { + if( poFeature ) + poFeature->SetField( iField, 0, 0, 0, + nHour, nMinute, fSecond, 0 ); + return OFTTime; + } + + /* HH:MM */ + nHour = 0; nMinute = 0; + if( sscanf(pszValue, "%02d:%02d", &nHour, &nMinute) == 2 ) + { + if( poFeature ) + poFeature->SetField( iField, 0, 0, 0, + nHour, nMinute, 0, 0 ); + return OFTTime; + } + + return FALSE; +} + +/************************************************************************/ +/* FormatSpatialFilterFromRTree() */ +/************************************************************************/ + +CPLString OGRSQLiteLayer::FormatSpatialFilterFromRTree(OGRGeometry* poFilterGeom, + const char* pszRowIDName, + const char* pszEscapedTable, + const char* pszEscapedGeomCol) +{ + CPLString osSpatialWHERE; + OGREnvelope sEnvelope; + + poFilterGeom->getEnvelope( &sEnvelope ); + + if( CPLIsInf(sEnvelope.MinX) && sEnvelope.MinX < 0 && + CPLIsInf(sEnvelope.MinY) && sEnvelope.MinY < 0 && + CPLIsInf(sEnvelope.MaxX) && sEnvelope.MaxX > 0 && + CPLIsInf(sEnvelope.MaxY) && sEnvelope.MaxY > 0 ) + return ""; + + osSpatialWHERE.Printf("%s IN ( SELECT pkid FROM 'idx_%s_%s' WHERE " + "xmax >= %s AND xmin <= %s AND ymax >= %s AND ymin <= %s)", + pszRowIDName, + pszEscapedTable, + pszEscapedGeomCol, + // Insure that only Decimal.Points are used, never local settings such as Decimal.Comma. + CPLString().FormatC(sEnvelope.MinX - 1e-11,"%.12f").c_str(), + CPLString().FormatC(sEnvelope.MaxX + 1e-11,"%.12f").c_str(), + CPLString().FormatC(sEnvelope.MinY - 1e-11,"%.12f").c_str(), + CPLString().FormatC(sEnvelope.MaxY + 1e-11,"%.12f").c_str()); + + return osSpatialWHERE; +} + +/************************************************************************/ +/* FormatSpatialFilterFromMBR() */ +/************************************************************************/ + +CPLString OGRSQLiteLayer::FormatSpatialFilterFromMBR(OGRGeometry* poFilterGeom, + const char* pszEscapedGeomColName) +{ + CPLString osSpatialWHERE; + OGREnvelope sEnvelope; + + poFilterGeom->getEnvelope( &sEnvelope ); + + if( CPLIsInf(sEnvelope.MinX) && sEnvelope.MinX < 0 && + CPLIsInf(sEnvelope.MinY) && sEnvelope.MinY < 0 && + CPLIsInf(sEnvelope.MaxX) && sEnvelope.MaxX > 0 && + CPLIsInf(sEnvelope.MaxY) && sEnvelope.MaxY > 0 ) + return ""; + + /* A bit inefficient but still faster than OGR filtering */ + osSpatialWHERE.Printf("MBRIntersects(\"%s\", BuildMBR(%s, %s, %s, %s))", + pszEscapedGeomColName, + // Insure that only Decimal.Points are used, never local settings such as Decimal.Comma. + CPLString().FormatC(sEnvelope.MinX - 1e-11,"%.12f").c_str(), + CPLString().FormatC(sEnvelope.MinY - 1e-11,"%.12f").c_str(), + CPLString().FormatC(sEnvelope.MaxX + 1e-11,"%.12f").c_str(), + CPLString().FormatC(sEnvelope.MaxY + 1e-11,"%.12f").c_str()); + + return osSpatialWHERE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteregexp.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteregexp.cpp new file mode 100644 index 000000000..19a936676 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteregexp.cpp @@ -0,0 +1,221 @@ +/****************************************************************************** + * $Id: ogrsqliteregexp.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: SQLite REGEXP function + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* WARNING: VERY IMPORTANT NOTE: This file MUST not be directly compiled as */ +/* a standalone object. It must be included from ogrsqlitevirtualogr.cpp */ +/* (actually from ogrsqlitesqlfunctions.cpp) */ +#ifndef COMPILATION_ALLOWED +#error See comment in file +#endif + +/* This code originates from pcre.c from the sqlite3-pcre extension */ +/* from http://laltromondo.dynalias.net/~iki/informatica/soft/sqlite3-pcre/ */ +/* whose header is : */ +/* + * Written by Alexey Tourbin . + * + * The author has dedicated the code to the public domain. Anyone is free + * to copy, modify, publish, use, compile, sell, or distribute the original + * code, either in source code form or as a compiled binary, for any purpose, + * commercial or non-commercial, and by any means. + */ + + +#include "ogrsqliteregexp.h" + +#ifdef HAVE_PCRE + +#include + +typedef struct { + char *s; + pcre *p; + pcre_extra *e; +} cache_entry; + +#ifndef CACHE_SIZE +#define CACHE_SIZE 16 +#endif + +/************************************************************************/ +/* OGRSQLiteREGEXPFunction() */ +/************************************************************************/ + +static +void OGRSQLiteREGEXPFunction(sqlite3_context *ctx, CPL_UNUSED int argc, sqlite3_value **argv) +{ + const char *re, *str; + pcre *p; + pcre_extra *e; + + CPLAssert(argc == 2); + + re = (const char *) sqlite3_value_text(argv[0]); + if (!re) { + sqlite3_result_error(ctx, "no regexp", -1); + return; + } + + if( sqlite3_value_type(argv[1]) == SQLITE_NULL ) + { + sqlite3_result_int(ctx, 0); + return; + } + + str = (const char *) sqlite3_value_text(argv[1]); + if (!str) { + sqlite3_result_error(ctx, "no string", -1); + return; + } + + /* simple LRU cache */ + int i; + int found = 0; + cache_entry *cache = (cache_entry*) sqlite3_user_data(ctx); + + CPLAssert(cache); + + for (i = 0; i < CACHE_SIZE && cache[i].s; i++) + { + if (strcmp(re, cache[i].s) == 0) { + found = 1; + break; + } + } + + if (found) + { + if (i > 0) + { + cache_entry c = cache[i]; + memmove(cache + 1, cache, i * sizeof(cache_entry)); + cache[0] = c; + } + } + else + { + cache_entry c; + const char *err; + int pos; + c.p = pcre_compile(re, 0, &err, &pos, NULL); + if (!c.p) + { + char *e2 = sqlite3_mprintf("%s: %s (offset %d)", re, err, pos); + sqlite3_result_error(ctx, e2, -1); + sqlite3_free(e2); + return; + } + c.e = pcre_study(c.p, 0, &err); + c.s = VSIStrdup(re); + if (!c.s) + { + sqlite3_result_error(ctx, "strdup: ENOMEM", -1); + pcre_free(c.p); + pcre_free(c.e); + return; + } + i = CACHE_SIZE - 1; + if (cache[i].s) + { + CPLFree(cache[i].s); + CPLAssert(cache[i].p); + pcre_free(cache[i].p); + pcre_free(cache[i].e); + } + memmove(cache + 1, cache, i * sizeof(cache_entry)); + cache[0] = c; + } + p = cache[0].p; + e = cache[0].e; + + int rc; + CPLAssert(p); + rc = pcre_exec(p, e, str, strlen(str), 0, 0, NULL, 0); + sqlite3_result_int(ctx, rc >= 0); +} + +#endif // HAVE_PCRE + +/************************************************************************/ +/* OGRSQLiteRegisterRegExpFunction() */ +/************************************************************************/ + +static +void* OGRSQLiteRegisterRegExpFunction(sqlite3* hDB) +{ +#ifdef HAVE_PCRE + + /* For debugging purposes mostly */ + if( !CSLTestBoolean(CPLGetConfigOption("OGR_SQLITE_REGEXP", "YES")) ) + return NULL; + + /* Check if we really need to define our own REGEXP function */ + int rc = sqlite3_exec(hDB, "SELECT 'a' REGEXP 'a'", NULL, NULL, NULL); + if( rc == SQLITE_OK ) + { + CPLDebug("SQLITE", "REGEXP already available"); + return NULL; + } + + cache_entry *cache = (cache_entry*) CPLCalloc(CACHE_SIZE, sizeof(cache_entry)); + sqlite3_create_function(hDB, "REGEXP", 2, SQLITE_UTF8, cache, + OGRSQLiteREGEXPFunction, NULL, NULL); + + /* To clear the error flag */ + sqlite3_exec(hDB, "SELECT 1", NULL, NULL, NULL); + + return cache; +#else // HAVE_PCRE + return NULL; +#endif // HAVE_PCRE +} + +/************************************************************************/ +/* OGRSQLiteFreeRegExpCache() */ +/************************************************************************/ + +static +void OGRSQLiteFreeRegExpCache(void* hRegExpCache) +{ +#ifdef HAVE_PCRE + if( hRegExpCache == NULL ) + return; + + cache_entry *cache = (cache_entry*) hRegExpCache; + int i; + for (i = 0; i < CACHE_SIZE && cache[i].s; i++) + { + CPLFree(cache[i].s); + CPLAssert(cache[i].p); + pcre_free(cache[i].p); + pcre_free(cache[i].e); + } + CPLFree(cache); +#endif // HAVE_PCRE +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteregexp.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteregexp.h new file mode 100644 index 000000000..585e10b4d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteregexp.h @@ -0,0 +1,38 @@ +/****************************************************************************** + * $Id: ogrsqliteregexp.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: SQLite REGEXP function + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SQLITE_REGEXP_INCLUDED +#define _OGR_SQLITE_REGEXP_INCLUDED + +#include "ogr_sqlite.h" + +static void* OGRSQLiteRegisterRegExpFunction(sqlite3* hDB); +static void OGRSQLiteFreeRegExpCache(void* hRegExpCache); + +#endif // _OGR_SQLITE_REGEXP_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteselectlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteselectlayer.cpp new file mode 100644 index 000000000..ca64c555d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteselectlayer.cpp @@ -0,0 +1,678 @@ +/****************************************************************************** + * $Id: ogrsqliteselectlayer.cpp 28928 2015-04-17 10:24:19Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRSQLiteSelectLayer class, layer access to the results + * of a SELECT statement executed via ExecuteSQL(). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_sqlite.h" +#include "swq.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrsqliteselectlayer.cpp 28928 2015-04-17 10:24:19Z rouault $"); + +/************************************************************************/ +/* OGRSQLiteSelectLayerCommonBehaviour() */ +/************************************************************************/ + +OGRSQLiteSelectLayerCommonBehaviour::OGRSQLiteSelectLayerCommonBehaviour(OGRSQLiteBaseDataSource* poDS, + IOGRSQLiteSelectLayer* poLayer, + CPLString osSQL, + int bEmptyLayer) : + poDS(poDS), poLayer(poLayer), osSQLBase(osSQL), + bEmptyLayer(bEmptyLayer), osSQLCurrent(osSQL) +{ + bAllowResetReadingEvenIfIndexAtZero = FALSE; + bSpatialFilterInSQL = TRUE; +} + +/************************************************************************/ +/* OGRSQLiteSelectLayer() */ +/************************************************************************/ + +OGRSQLiteSelectLayer::OGRSQLiteSelectLayer( OGRSQLiteDataSource *poDSIn, + CPLString osSQLIn, + sqlite3_stmt *hStmtIn, + int bUseStatementForGetNextFeature, + int bEmptyLayer, + int bAllowMultipleGeomFields ) + +{ + poBehaviour = new OGRSQLiteSelectLayerCommonBehaviour(poDSIn, this, osSQLIn, bEmptyLayer); + poDS = poDSIn; + + this->bAllowMultipleGeomFields = bAllowMultipleGeomFields; + + std::set aosEmpty; + BuildFeatureDefn( "SELECT", hStmtIn, aosEmpty, aosEmpty ); + SetDescription( "SELECT" ); + + if( bUseStatementForGetNextFeature ) + { + hStmt = hStmtIn; + bDoStep = FALSE; + + // Try to extract SRS from first geometry + for( int iField = 0; + !bEmptyLayer && iField < poFeatureDefn->GetGeomFieldCount(); + iField ++) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(iField); + if( wkbFlatten(poGeomFieldDefn->GetType()) != wkbUnknown ) + continue; + + int nBytes; + if( sqlite3_column_type( hStmt, poGeomFieldDefn->iCol ) == SQLITE_BLOB && + (nBytes = sqlite3_column_bytes( hStmt, poGeomFieldDefn->iCol )) > 39 ) + { + const GByte* pabyBlob = (const GByte*)sqlite3_column_blob( hStmt, poGeomFieldDefn->iCol ); + int eByteOrder = pabyBlob[1]; + if( pabyBlob[0] == 0x00 && + (eByteOrder == wkbNDR || eByteOrder == wkbXDR) && + pabyBlob[38] == 0x7C ) + { + int nSRSId; + memcpy(&nSRSId, pabyBlob + 2, 4); +#ifdef CPL_LSB + if( eByteOrder != wkbNDR) + CPL_SWAP32PTR(&nSRSId); +#else + if( eByteOrder == wkbNDR) + CPL_SWAP32PTR(&nSRSId); +#endif + CPLPushErrorHandler(CPLQuietErrorHandler); + OGRSpatialReference* poSRS = poDS->FetchSRS( nSRSId ); + CPLPopErrorHandler(); + if( poSRS != NULL ) + { + poGeomFieldDefn->nSRSId = nSRSId; + poGeomFieldDefn->SetSpatialRef(poSRS); + } + else + CPLErrorReset(); + } +#ifdef SQLITE_HAS_COLUMN_METADATA + else if( iField == 0 ) + { + const char* pszTableName = sqlite3_column_table_name( hStmt, poGeomFieldDefn->iCol ); + if( pszTableName != NULL ) + { + OGRSQLiteLayer* poLayer = (OGRSQLiteLayer*) + poDS->GetLayerByName(pszTableName); + if( poLayer != NULL && poLayer->GetLayerDefn()->GetGeomFieldCount() > 0) + { + OGRSQLiteGeomFieldDefn* poSrcGFldDefn = + poLayer->myGetLayerDefn()->myGetGeomFieldDefn(0); + poGeomFieldDefn->nSRSId = poSrcGFldDefn->nSRSId; + poGeomFieldDefn->SetSpatialRef(poSrcGFldDefn->GetSpatialRef()); + } + } + } +#endif + } + } + } + else + sqlite3_finalize( hStmtIn ); +} + +/************************************************************************/ +/* ~OGRSQLiteSelectLayer() */ +/************************************************************************/ + +OGRSQLiteSelectLayer::~OGRSQLiteSelectLayer() +{ + delete poBehaviour; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRSQLiteSelectLayer::ResetReading() +{ + return poBehaviour->ResetReading(); +} + +void OGRSQLiteSelectLayerCommonBehaviour::ResetReading() +{ + if( poLayer->HasReadFeature() || bAllowResetReadingEvenIfIndexAtZero ) + { + poLayer->BaseResetReading(); + bAllowResetReadingEvenIfIndexAtZero = FALSE; + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRSQLiteSelectLayer::GetNextFeature() +{ + return poBehaviour->GetNextFeature(); +} + +OGRFeature *OGRSQLiteSelectLayerCommonBehaviour::GetNextFeature() +{ + if( bEmptyLayer ) + return NULL; + + return poLayer->BaseGetNextFeature(); +} + +/************************************************************************/ +/* OGRGenSQLResultsLayerHasSpecialField() */ +/************************************************************************/ + +static +int HasSpecialFields(swq_expr_node* expr, int nMinIndexForSpecialField) +{ + if (expr->eNodeType == SNT_COLUMN) + { + if (expr->table_index == 0) + { + return expr->field_index >= nMinIndexForSpecialField && + expr->field_index < nMinIndexForSpecialField + SPECIAL_FIELD_COUNT; + } + } + else if (expr->eNodeType == SNT_OPERATION) + { + for( int i = 0; i < expr->nSubExprCount; i++ ) + { + if (HasSpecialFields(expr->papoSubExpr[i], nMinIndexForSpecialField)) + return TRUE; + } + } + return FALSE; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRSQLiteSelectLayer::SetAttributeFilter( const char *pszQuery ) +{ + return poBehaviour->SetAttributeFilter(pszQuery); +} + +OGRErr OGRSQLiteSelectLayerCommonBehaviour::SetAttributeFilter( const char *pszQuery ) + +{ + char*& m_pszAttrQuertyString = poLayer->GetAttrQueryString(); + if( m_pszAttrQuertyString == NULL && pszQuery == NULL ) + return OGRERR_NONE; + + CPLFree(m_pszAttrQuertyString); + m_pszAttrQuertyString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + + bAllowResetReadingEvenIfIndexAtZero = TRUE; + + OGRFeatureQuery oQuery; + + CPLPushErrorHandler(CPLQuietErrorHandler); + int bHasSpecialFields = (pszQuery != NULL && pszQuery[0] != '\0' && + oQuery.Compile( poLayer->GetLayerDefn(), pszQuery ) == OGRERR_NONE && + HasSpecialFields((swq_expr_node*)oQuery.GetSWQExpr(), poLayer->GetLayerDefn()->GetFieldCount()) ); + CPLPopErrorHandler(); + + if( bHasSpecialFields || !BuildSQL() ) + { + return poLayer->BaseSetAttributeFilter(pszQuery); + } + + ResetReading(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +GIntBig OGRSQLiteSelectLayer::GetFeatureCount( int bForce ) +{ + return poBehaviour->GetFeatureCount(bForce); +} + +GIntBig OGRSQLiteSelectLayerCommonBehaviour::GetFeatureCount( int bForce ) +{ + if( bEmptyLayer ) + return 0; + + if( poLayer->GetFeatureQuery() == NULL && + EQUALN(osSQLCurrent, "SELECT COUNT(*) FROM", strlen("SELECT COUNT(*) FROM")) && + osSQLCurrent.ifind(" GROUP BY ") == std::string::npos && + osSQLCurrent.ifind(" UNION ") == std::string::npos && + osSQLCurrent.ifind(" INTERSECT ") == std::string::npos && + osSQLCurrent.ifind(" EXCEPT ") == std::string::npos ) + return 1; + + if( poLayer->GetFeatureQuery() != NULL || (poLayer->GetFilterGeom() != NULL && !bSpatialFilterInSQL) ) + return poLayer->BaseGetFeatureCount(bForce); + + CPLString osFeatureCountSQL("SELECT COUNT(*) FROM ("); + osFeatureCountSQL += osSQLCurrent; + osFeatureCountSQL += ")"; + + CPLDebug("SQLITE", "Running %s", osFeatureCountSQL.c_str()); + +/* -------------------------------------------------------------------- */ +/* Execute. */ +/* -------------------------------------------------------------------- */ + char *pszErrMsg = NULL; + char **papszResult; + int nRowCount, nColCount; + int nResult = -1; + + if( sqlite3_get_table( poDS->GetDB(), osFeatureCountSQL, &papszResult, + &nRowCount, &nColCount, &pszErrMsg ) != SQLITE_OK ) + { + CPLDebug("SQLITE", "Error: %s", pszErrMsg); + sqlite3_free(pszErrMsg); + return poLayer->BaseGetFeatureCount(bForce); + } + + if( nRowCount == 1 && nColCount == 1 ) + { + nResult = atoi(papszResult[1]); + } + + sqlite3_free_table( papszResult ); + + return nResult; +} + +/************************************************************************/ +/* ResetStatement() */ +/************************************************************************/ + +OGRErr OGRSQLiteSelectLayer::ResetStatement() + +{ + int rc; + + ClearStatement(); + + iNextShapeId = 0; + bDoStep = TRUE; + +#ifdef DEBUG + CPLDebug( "OGR_SQLITE", "prepare(%s)", poBehaviour->osSQLCurrent.c_str() ); +#endif + + rc = sqlite3_prepare( poDS->GetDB(), poBehaviour->osSQLCurrent, poBehaviour->osSQLCurrent.size(), + &hStmt, NULL ); + + if( rc == SQLITE_OK ) + { + return OGRERR_NONE; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "In ResetStatement(): sqlite3_prepare(%s):\n %s", + poBehaviour->osSQLCurrent.c_str(), sqlite3_errmsg(poDS->GetDB()) ); + hStmt = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRSQLiteSelectLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeomIn ) + +{ + poBehaviour->SetSpatialFilter(iGeomField, poGeomIn); +} + +void OGRSQLiteSelectLayerCommonBehaviour::SetSpatialFilter( int iGeomField, OGRGeometry * poGeomIn ) + +{ + if( iGeomField == 0 && poGeomIn == NULL && poLayer->GetLayerDefn()->GetGeomFieldCount() == 0 ) + { + /* do nothing */ + } + else if( iGeomField < 0 || iGeomField >= poLayer->GetLayerDefn()->GetGeomFieldCount() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + return; + } + + bAllowResetReadingEvenIfIndexAtZero = TRUE; + + int& m_iGeomFieldFilter = poLayer->GetIGeomFieldFilter(); + m_iGeomFieldFilter = iGeomField; + if( poLayer->InstallFilter( poGeomIn ) ) + { + BuildSQL(); + + ResetReading(); + } +} + +/************************************************************************/ +/* GetBaseLayer() */ +/************************************************************************/ + +std::pair OGRSQLiteSelectLayerCommonBehaviour::GetBaseLayer(size_t& i) +{ + char** papszTokens = CSLTokenizeString(osSQLBase.c_str()); + int bCanInsertFilter = TRUE; + int nCountSelect = 0, nCountFrom = 0, nCountWhere = 0; + + for(int iToken = 0; papszTokens[iToken] != NULL; iToken++) + { + if (EQUAL(papszTokens[iToken], "SELECT")) + nCountSelect ++; + else if (EQUAL(papszTokens[iToken], "FROM")) + nCountFrom ++; + else if (EQUAL(papszTokens[iToken], "WHERE")) + nCountWhere ++; + else if (EQUAL(papszTokens[iToken], "UNION") || + EQUAL(papszTokens[iToken], "JOIN") || + EQUAL(papszTokens[iToken], "INTERSECT") || + EQUAL(papszTokens[iToken], "EXCEPT")) + { + bCanInsertFilter = FALSE; + } + } + CSLDestroy(papszTokens); + + if (!(bCanInsertFilter && nCountSelect == 1 && nCountFrom == 1 && nCountWhere <= 1)) + { + CPLDebug("SQLITE", "SQL expression too complex to analyse"); + return std::pair((OGRLayer*)NULL, (IOGRSQLiteGetSpatialWhere*)NULL); + } + + size_t nFromPos = osSQLBase.ifind(" from "); + if (nFromPos == std::string::npos) + { + return std::pair((OGRLayer*)NULL, (IOGRSQLiteGetSpatialWhere*)NULL); + } + + char chQuote = osSQLBase[nFromPos + 6]; + int bInQuotes = (chQuote == '\'' || chQuote == '"' ); + CPLString osBaseLayerName; + for( i = nFromPos + 6 + (bInQuotes ? 1 : 0); + i < osSQLBase.size(); i++ ) + { + if (osSQLBase[i] == chQuote && i + 1 < osSQLBase.size() && + osSQLBase[i + 1] == chQuote ) + { + osBaseLayerName += osSQLBase[i]; + i++; + } + else if (osSQLBase[i] == chQuote && bInQuotes) + { + i++; + break; + } + else if (osSQLBase[i] == ' ' && !bInQuotes) + break; + else + osBaseLayerName += osSQLBase[i]; + } + + std::pair oPair; + if( strchr(osBaseLayerName, '(') == NULL && + poLayer->GetLayerDefn()->GetGeomFieldCount() != 0 ) + { + CPLString osNewUnderlyingTableName; + osNewUnderlyingTableName.Printf("%s(%s)", + osBaseLayerName.c_str(), + poLayer->GetLayerDefn()->GetGeomFieldDefn(0)->GetNameRef()); + oPair = poDS->GetLayerWithGetSpatialWhereByName(osNewUnderlyingTableName); + } + if( oPair.first == NULL ) + oPair = poDS->GetLayerWithGetSpatialWhereByName(osBaseLayerName); + + if( oPair.first != NULL && poLayer->GetSpatialRef() != NULL && + oPair.first->GetSpatialRef() != NULL && + poLayer->GetSpatialRef() != oPair.first->GetSpatialRef() && + !poLayer->GetSpatialRef()->IsSame(oPair.first->GetSpatialRef()) ) + { + CPLDebug("SQLITE", "Result layer and base layer don't have the same SRS."); + return std::pair((OGRLayer*)NULL, (IOGRSQLiteGetSpatialWhere*)NULL); + } + + return oPair; +} + +/************************************************************************/ +/* BuildSQL() */ +/************************************************************************/ + +int OGRSQLiteSelectLayerCommonBehaviour::BuildSQL() + +{ + osSQLCurrent = osSQLBase; + bSpatialFilterInSQL = TRUE; + + size_t i = 0; + std::pair oPair = GetBaseLayer(i); + OGRLayer* poBaseLayer = oPair.first; + if (poBaseLayer == NULL) + { + CPLDebug("SQLITE", "Cannot find base layer"); + bSpatialFilterInSQL = FALSE; + return FALSE; + } + + CPLString osSpatialWhere; + if (poLayer->GetFilterGeom() != NULL) + { + const char* pszGeomCol = + poLayer->GetLayerDefn()->GetGeomFieldDefn(poLayer->GetIGeomFieldFilter())->GetNameRef(); + int nIdx = poBaseLayer->GetLayerDefn()->GetGeomFieldIndex(pszGeomCol); + if( nIdx < 0 ) + { + CPLDebug("SQLITE", "Cannot find field %s in base layer", pszGeomCol); + bSpatialFilterInSQL = FALSE; + } + else + { + osSpatialWhere = oPair.second->GetSpatialWhere(nIdx, poLayer->GetFilterGeom()); + if (osSpatialWhere.size() == 0) + { + CPLDebug("SQLITE", "Cannot get spatial where clause"); + bSpatialFilterInSQL = FALSE; + } + } + } + + CPLString osCustomWhere; + if( osSpatialWhere.size() != 0 ) + { + osCustomWhere = osSpatialWhere; + } + if( poLayer->GetAttrQueryString() != NULL && poLayer->GetAttrQueryString()[0] != '\0' ) + { + if( osSpatialWhere.size() != 0) + osCustomWhere += " AND ("; + osCustomWhere += poLayer->GetAttrQueryString(); + if( osSpatialWhere.size() != 0) + osCustomWhere += ")"; + } + + /* Nothing to do */ + if( osCustomWhere.size() == 0 ) + return TRUE; + + while (i < osSQLBase.size() && osSQLBase[i] == ' ') + i ++; + + if (i < osSQLBase.size() && EQUALN(osSQLBase.c_str() + i, "WHERE ", 6)) + { + osSQLCurrent = osSQLBase.substr(0, i + 6); + osSQLCurrent += osCustomWhere; + osSQLCurrent += " AND ("; + + size_t nEndOfWhere = osSQLBase.ifind(" GROUP "); + if (nEndOfWhere == std::string::npos) + nEndOfWhere = osSQLBase.ifind(" ORDER "); + if (nEndOfWhere == std::string::npos) + nEndOfWhere = osSQLBase.ifind(" LIMIT "); + + if (nEndOfWhere == std::string::npos) + { + osSQLCurrent += osSQLBase.substr(i + 6); + osSQLCurrent += ")"; + } + else + { + osSQLCurrent += osSQLBase.substr(i + 6, nEndOfWhere - (i + 6)); + osSQLCurrent += ")"; + osSQLCurrent += osSQLBase.substr(nEndOfWhere); + } + } + else if (i < osSQLBase.size() && + (EQUALN(osSQLBase.c_str() + i, "GROUP ", 6) || + EQUALN(osSQLBase.c_str() + i, "ORDER ", 6) || + EQUALN(osSQLBase.c_str() + i, "LIMIT ", 6))) + { + osSQLCurrent = osSQLBase.substr(0, i); + osSQLCurrent += " WHERE "; + osSQLCurrent += osCustomWhere; + osSQLCurrent += " "; + osSQLCurrent += osSQLBase.substr(i); + } + else if (i == osSQLBase.size()) + { + osSQLCurrent = osSQLBase.substr(0, i); + osSQLCurrent += " WHERE "; + osSQLCurrent += osCustomWhere; + } + else + { + CPLDebug("SQLITE", "SQL expression too complex for the driver to insert attribute and/or spatial filter in it"); + bSpatialFilterInSQL = FALSE; + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSQLiteSelectLayer::TestCapability( const char * pszCap ) +{ + return poBehaviour->TestCapability(pszCap); +} + +int OGRSQLiteSelectLayerCommonBehaviour::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap,OLCFastSpatialFilter)) + { + size_t i = 0; + std::pair oPair = GetBaseLayer(i); + if (oPair.first == NULL) + { + CPLDebug("SQLITE", "Cannot find base layer"); + return FALSE; + } + + return oPair.second->HasFastSpatialFilter(0); + } + else + return poLayer->BaseTestCapability( pszCap ); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRSQLiteSelectLayer::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) +{ + return poBehaviour->GetExtent(iGeomField, psExtent, bForce); +} + +OGRErr OGRSQLiteSelectLayerCommonBehaviour::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) +{ + if( iGeomField < 0 || iGeomField >= poLayer->GetLayerDefn()->GetGeomFieldCount() || + poLayer->GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone ) + { + if( iGeomField != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } + return OGRERR_FAILURE; + } + + /* Caching of extent by SQL string is interesting to speed-up the */ + /* establishment of the WFS GetCapabilities document for a MapServer mapfile */ + /* which has several layers, only differing by scale rules */ + if( iGeomField == 0 ) + { + const OGREnvelope* psCachedExtent = poDS->GetEnvelopeFromSQL(osSQLBase); + if (psCachedExtent) + { + memcpy(psExtent, psCachedExtent, sizeof(*psCachedExtent)); + return OGRERR_NONE; + } + } + + CPLString osSQLCommand = osSQLBase; + + /* ORDER BY are costly to evaluate and are not necessary to establish */ + /* the layer extent. */ + size_t nOrderByPos = osSQLCommand.ifind(" ORDER BY "); + if( osSQLCommand.ifind("SELECT ") == 0 && + nOrderByPos != std::string::npos && + osSQLCommand.ifind(" LIMIT ") == std::string::npos && + osSQLCommand.ifind(" UNION ") == std::string::npos && + osSQLCommand.ifind(" INTERSECT ") == std::string::npos && + osSQLCommand.ifind(" EXCEPT ") == std::string::npos) + { + osSQLCommand.resize(nOrderByPos); + + OGRLayer* poTmpLayer = poDS->ExecuteSQL(osSQLCommand.c_str(), NULL, NULL); + if (poTmpLayer) + { + OGRErr eErr = poTmpLayer->GetExtent(iGeomField, psExtent, bForce); + poDS->ReleaseResultSet(poTmpLayer); + return eErr; + } + } + + OGRErr eErr; + if( iGeomField == 0 ) + eErr = poLayer->BaseGetExtent(psExtent, bForce); + else + eErr = poLayer->BaseGetExtent(iGeomField, psExtent, bForce); + if (iGeomField == 0 && eErr == OGRERR_NONE && poDS->GetUpdate() == FALSE) + poDS->SetEnvelopeForSQL(osSQLBase, *psExtent); + return eErr; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitesinglefeaturelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitesinglefeaturelayer.cpp new file mode 100644 index 000000000..b20453617 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitesinglefeaturelayer.cpp @@ -0,0 +1,130 @@ +/****************************************************************************** + * $Id: ogrsqlitesinglefeaturelayer.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRSQLiteSingleFeatureLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_sqlite.h" + +CPL_CVSID("$Id: ogrsqlitesinglefeaturelayer.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +/************************************************************************/ +/* OGRSQLiteSingleFeatureLayer() */ +/************************************************************************/ + +OGRSQLiteSingleFeatureLayer::OGRSQLiteSingleFeatureLayer( + const char* pszLayerName, + int nVal ) +{ + poFeatureDefn = new OGRFeatureDefn( "SELECT" ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + OGRFieldDefn oField( pszLayerName, OFTInteger ); + poFeatureDefn->AddFieldDefn( &oField ); + + iNextShapeId = 0; + this->nVal = nVal; + pszVal = NULL; +} + +/************************************************************************/ +/* OGRSQLiteSingleFeatureLayer() */ +/************************************************************************/ + +OGRSQLiteSingleFeatureLayer::OGRSQLiteSingleFeatureLayer( + const char* pszLayerName, + const char *pszVal ) +{ + poFeatureDefn = new OGRFeatureDefn( "SELECT" ); + poFeatureDefn->Reference(); + OGRFieldDefn oField( pszLayerName, OFTString ); + poFeatureDefn->AddFieldDefn( &oField ); + + iNextShapeId = 0; + nVal = 0; + this->pszVal = CPLStrdup(pszVal); +} + +/************************************************************************/ +/* ~OGRSQLiteSingleFeatureLayer() */ +/************************************************************************/ + +OGRSQLiteSingleFeatureLayer::~OGRSQLiteSingleFeatureLayer() +{ + if( poFeatureDefn != NULL ) + { + poFeatureDefn->Release(); + poFeatureDefn = NULL; + } + CPLFree(pszVal); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRSQLiteSingleFeatureLayer::ResetReading() +{ + iNextShapeId = 0; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature * OGRSQLiteSingleFeatureLayer::GetNextFeature() +{ + if (iNextShapeId != 0) + return NULL; + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + if (pszVal) + poFeature->SetField(0, pszVal); + else + poFeature->SetField(0, nVal); + poFeature->SetFID(iNextShapeId ++); + return poFeature; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRSQLiteSingleFeatureLayer::GetLayerDefn() +{ + return poFeatureDefn; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSQLiteSingleFeatureLayer::TestCapability( const char * ) +{ + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitesqlfunctions.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitesqlfunctions.cpp new file mode 100644 index 000000000..d3bfab1d8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitesqlfunctions.cpp @@ -0,0 +1,1195 @@ +/****************************************************************************** + * $Id: ogrsqlitesqlfunctions.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Extension SQL functions + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* WARNING: VERY IMPORTANT NOTE: This file MUST not be directly compiled as */ +/* a standalone object. It must be included from ogrsqlitevirtualogr.cpp */ +#ifndef COMPILATION_ALLOWED +#error See comment in file +#endif + +#include "ogrsqlitesqlfunctions.h" +#include "ogr_geocoding.h" + +#include "ogrsqliteregexp.cpp" /* yes the .cpp file, to make it work on Windows with load_extension('gdalXX.dll') */ +#include "swq.h" + +#ifndef HAVE_SPATIALITE +#define MINIMAL_SPATIAL_FUNCTIONS +#endif + +class OGRSQLiteExtensionData +{ +#ifdef DEBUG + void* pDummy; /* to track memory leaks */ +#endif + std::map< std::pair, OGRCoordinateTransformation*> oCachedTransformsMap; + + void* hRegExpCache; + + OGRGeocodingSessionH hGeocodingSession; + + public: + OGRSQLiteExtensionData(sqlite3* hDB); + ~OGRSQLiteExtensionData(); + + OGRCoordinateTransformation* GetTransform(int nSrcSRSId, int nDstSRSId); + + OGRGeocodingSessionH GetGeocodingSession() { return hGeocodingSession; } + void SetGeocodingSession(OGRGeocodingSessionH hGeocodingSessionIn) { hGeocodingSession = hGeocodingSessionIn; } + + void SetRegExpCache(void* hRegExpCacheIn) { hRegExpCache = hRegExpCacheIn; } +}; + +/************************************************************************/ +/* OGRSQLiteExtensionData() */ +/************************************************************************/ + +OGRSQLiteExtensionData::OGRSQLiteExtensionData(CPL_UNUSED sqlite3* hDB) : + hRegExpCache(NULL), hGeocodingSession(NULL) +{ +#ifdef DEBUG + pDummy = CPLMalloc(1); +#endif +} + +/************************************************************************/ +/* ~OGRSQLiteExtensionData() */ +/************************************************************************/ + +OGRSQLiteExtensionData::~OGRSQLiteExtensionData() +{ +#ifdef DEBUG + CPLFree(pDummy); +#endif + + std::map< std::pair, OGRCoordinateTransformation*>::iterator oIter = + oCachedTransformsMap.begin(); + for(; oIter != oCachedTransformsMap.end(); ++oIter) + delete oIter->second; + + OGRSQLiteFreeRegExpCache(hRegExpCache); + + OGRGeocodeDestroySession(hGeocodingSession); +} + +/************************************************************************/ +/* GetTransform() */ +/************************************************************************/ + +OGRCoordinateTransformation* OGRSQLiteExtensionData::GetTransform(int nSrcSRSId, + int nDstSRSId) +{ + std::map< std::pair, OGRCoordinateTransformation*>::iterator oIter = + oCachedTransformsMap.find(std::pair(nSrcSRSId, nDstSRSId)); + if( oIter == oCachedTransformsMap.end() ) + { + OGRCoordinateTransformation* poCT = NULL; + OGRSpatialReference oSrcSRS, oDstSRS; + if (oSrcSRS.importFromEPSG(nSrcSRSId) == OGRERR_NONE && + oDstSRS.importFromEPSG(nDstSRSId) == OGRERR_NONE ) + { + poCT = OGRCreateCoordinateTransformation( &oSrcSRS, &oDstSRS ); + } + oCachedTransformsMap[std::pair(nSrcSRSId, nDstSRSId)] = poCT; + return poCT; + } + else + return oIter->second; +} + +/************************************************************************/ +/* OGR2SQLITE_ogr_version() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_version(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + if( argc == 0 || sqlite3_value_type (argv[0]) != SQLITE_TEXT ) + { + sqlite3_result_text( pContext, GDAL_RELEASE_NAME, -1, SQLITE_STATIC ); + } + else + { + sqlite3_result_text( pContext, + GDALVersionInfo((const char*)sqlite3_value_text(argv[0])), + -1, SQLITE_TRANSIENT ); + } +} + +/************************************************************************/ +/* OGR2SQLITE_Transform() */ +/************************************************************************/ + +static +void OGR2SQLITE_Transform(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + if( argc != 3 ) + { + sqlite3_result_null (pContext); + return; + } + + if( sqlite3_value_type (argv[0]) != SQLITE_BLOB ) + { + sqlite3_result_null (pContext); + return; + } + + if( sqlite3_value_type (argv[1]) != SQLITE_INTEGER ) + { + sqlite3_result_null (pContext); + return; + } + + if( sqlite3_value_type (argv[2]) != SQLITE_INTEGER ) + { + sqlite3_result_null (pContext); + return; + } + + int nSrcSRSId = sqlite3_value_int(argv[1]); + int nDstSRSId = sqlite3_value_int(argv[2]); + + OGRSQLiteExtensionData* poModule = + (OGRSQLiteExtensionData*) sqlite3_user_data(pContext); + OGRCoordinateTransformation* poCT = + poModule->GetTransform(nSrcSRSId, nDstSRSId); + if( poCT == NULL ) + { + sqlite3_result_null (pContext); + return; + } + + GByte* pabySLBLOB = (GByte *) sqlite3_value_blob (argv[0]); + int nBLOBLen = sqlite3_value_bytes (argv[0]); + OGRGeometry* poGeom = NULL; + if( OGRSQLiteLayer::ImportSpatiaLiteGeometry( + pabySLBLOB, nBLOBLen, &poGeom ) == CE_None && + poGeom->transform(poCT) == OGRERR_NONE && + OGRSQLiteLayer::ExportSpatiaLiteGeometry( + poGeom, nDstSRSId, wkbNDR, FALSE, + FALSE, FALSE, &pabySLBLOB, &nBLOBLen ) == CE_None ) + { + sqlite3_result_blob(pContext, pabySLBLOB, nBLOBLen, CPLFree); + } + else + { + sqlite3_result_null (pContext); + } + delete poGeom; +} + +/************************************************************************/ +/* OGR2SQLITE_ogr_deflate() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_deflate(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + int nLevel = -1; + if( !(argc == 1 || argc == 2) || + !(sqlite3_value_type (argv[0]) == SQLITE_TEXT || + sqlite3_value_type (argv[0]) == SQLITE_BLOB) ) + { + sqlite3_result_null (pContext); + return; + } + if( argc == 2 ) + { + if( sqlite3_value_type (argv[1]) != SQLITE_INTEGER ) + { + sqlite3_result_null (pContext); + return; + } + nLevel = sqlite3_value_int(argv[1]); + } + + size_t nOutBytes = 0; + void* pOut; + if( sqlite3_value_type (argv[0]) == SQLITE_TEXT ) + { + const char* pszVal = (const char*)sqlite3_value_text(argv[0]); + pOut = CPLZLibDeflate( pszVal, strlen(pszVal) + 1, nLevel, NULL, 0, &nOutBytes); + } + else + { + const void* pSrc = sqlite3_value_blob (argv[0]); + int nLen = sqlite3_value_bytes (argv[0]); + pOut = CPLZLibDeflate( pSrc, nLen, nLevel, NULL, 0, &nOutBytes); + } + if( pOut != NULL ) + { + sqlite3_result_blob (pContext, pOut, nOutBytes, VSIFree); + } + else + { + sqlite3_result_null (pContext); + } + + return; +} + +/************************************************************************/ +/* OGR2SQLITE_ogr_inflate() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_inflate(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + if( argc != 1 || + sqlite3_value_type (argv[0]) != SQLITE_BLOB ) + { + sqlite3_result_null (pContext); + return; + } + + size_t nOutBytes = 0; + void* pOut; + + const void* pSrc = sqlite3_value_blob (argv[0]); + int nLen = sqlite3_value_bytes (argv[0]); + pOut = CPLZLibInflate( pSrc, nLen, NULL, 0, &nOutBytes); + + if( pOut != NULL ) + { + sqlite3_result_blob (pContext, pOut, nOutBytes, VSIFree); + } + else + { + sqlite3_result_null (pContext); + } + + return; +} + +/************************************************************************/ +/* OGR2SQLITE_ogr_geocode_set_result() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_geocode_set_result(sqlite3_context* pContext, + OGRLayerH hLayer, + const char* pszField) +{ + if( hLayer == NULL ) + sqlite3_result_null (pContext); + else + { + OGRLayer* poLayer = (OGRLayer*)hLayer; + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + OGRFeature* poFeature = poLayer->GetNextFeature(); + int nIdx = -1; + if( poFeature == NULL ) + sqlite3_result_null (pContext); + else if( strcmp(pszField, "geometry") == 0 && + poFeature->GetGeometryRef() != NULL ) + { + GByte* pabyGeomBLOB = NULL; + int nGeomBLOBLen = 0; + if( OGRSQLiteLayer::ExportSpatiaLiteGeometry( + poFeature->GetGeometryRef(), 4326, wkbNDR, FALSE, FALSE, FALSE, + &pabyGeomBLOB, + &nGeomBLOBLen ) != CE_None ) + { + sqlite3_result_null (pContext); + } + else + { + sqlite3_result_blob (pContext, pabyGeomBLOB, nGeomBLOBLen, CPLFree); + } + } + else if( (nIdx = poFDefn->GetFieldIndex(pszField)) >= 0 && + poFeature->IsFieldSet(nIdx) ) + { + OGRFieldType eType = poFDefn->GetFieldDefn(nIdx)->GetType(); + if( eType == OFTInteger ) + sqlite3_result_int(pContext, + poFeature->GetFieldAsInteger(nIdx)); + else if( eType == OFTInteger64 ) + sqlite3_result_int64(pContext, + poFeature->GetFieldAsInteger64(nIdx)); + else if( eType == OFTReal ) + sqlite3_result_double(pContext, + poFeature->GetFieldAsDouble(nIdx)); + else + sqlite3_result_text(pContext, + poFeature->GetFieldAsString(nIdx), + -1, SQLITE_TRANSIENT); + } + else + sqlite3_result_null (pContext); + delete poFeature; + OGRGeocodeFreeResult(hLayer); + } +} + +/************************************************************************/ +/* OGR2SQLITE_ogr_geocode() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_geocode(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + OGRSQLiteExtensionData* poModule = + (OGRSQLiteExtensionData*) sqlite3_user_data(pContext); + + if( argc < 1 || sqlite3_value_type (argv[0]) != SQLITE_TEXT ) + { + sqlite3_result_null (pContext); + return; + } + const char* pszQuery = (const char*)sqlite3_value_text(argv[0]); + + CPLString osField = "geometry"; + if( argc >= 2 && sqlite3_value_type (argv[1]) == SQLITE_TEXT ) + { + osField = (const char*)sqlite3_value_text(argv[1]); + } + + int i; + char** papszOptions = NULL; + for(i = 2; i < argc; i++) + { + if( sqlite3_value_type (argv[i]) == SQLITE_TEXT ) + { + papszOptions = CSLAddString(papszOptions, + (const char*)sqlite3_value_text(argv[i])); + } + } + + OGRGeocodingSessionH hSession = poModule->GetGeocodingSession(); + if( hSession == NULL ) + { + hSession = OGRGeocodeCreateSession(papszOptions); + if( hSession == NULL ) + { + sqlite3_result_null (pContext); + CSLDestroy(papszOptions); + return; + } + poModule->SetGeocodingSession(hSession); + } + + if( osField == "raw" ) + papszOptions = CSLAddString(papszOptions, "RAW_FEATURE=YES"); + + if( CSLFindString(papszOptions, "LIMIT") == -1 ) + papszOptions = CSLAddString(papszOptions, "LIMIT=1"); + + OGRLayerH hLayer = OGRGeocode(hSession, pszQuery, NULL, papszOptions); + + OGR2SQLITE_ogr_geocode_set_result(pContext, hLayer, osField); + + CSLDestroy(papszOptions); + + return; +} + +/************************************************************************/ +/* OGR2SQLITE_GetValAsDouble() */ +/************************************************************************/ + +static double OGR2SQLITE_GetValAsDouble(sqlite3_value* val, int* pbGotVal) +{ + switch(sqlite3_value_type(val)) + { + case SQLITE_FLOAT: + if( pbGotVal ) *pbGotVal = TRUE; + return sqlite3_value_double(val); + + case SQLITE_INTEGER: + if( pbGotVal ) *pbGotVal = TRUE; + return (double) sqlite3_value_int64(val); + + default: + if( pbGotVal ) *pbGotVal = FALSE; + return 0.0; + } +} + +/************************************************************************/ +/* OGR2SQLITE_GetGeom() */ +/************************************************************************/ + +static OGRGeometry* OGR2SQLITE_GetGeom(CPL_UNUSED sqlite3_context* pContext, + CPL_UNUSED int argc, + sqlite3_value** argv, + int* pnSRSId) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_BLOB ) + { + return NULL; + } + + GByte* pabySLBLOB = (GByte *) sqlite3_value_blob (argv[0]); + int nBLOBLen = sqlite3_value_bytes (argv[0]); + OGRGeometry* poGeom = NULL; + if( OGRSQLiteLayer::ImportSpatiaLiteGeometry( + pabySLBLOB, nBLOBLen, &poGeom, pnSRSId) != CE_None ) + { + return NULL; + } + + return poGeom; +} + +/************************************************************************/ +/* OGR2SQLITE_ogr_geocode_reverse() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_geocode_reverse(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + OGRSQLiteExtensionData* poModule = + (OGRSQLiteExtensionData*) sqlite3_user_data(pContext); + + double dfLon = 0.0, dfLat = 0.0; + int iAfterGeomIdx = 0; + int bGotLon = FALSE, bGotLat = FALSE; + + if( argc >= 2 ) + { + dfLon = OGR2SQLITE_GetValAsDouble(argv[0], &bGotLon); + dfLat = OGR2SQLITE_GetValAsDouble(argv[1], &bGotLat); + } + + if( argc >= 3 && bGotLon && bGotLat && + sqlite3_value_type (argv[2]) == SQLITE_TEXT ) + { + iAfterGeomIdx = 2; + } + else if( argc >= 2 && + sqlite3_value_type (argv[0]) == SQLITE_BLOB && + sqlite3_value_type (argv[1]) == SQLITE_TEXT ) + { + OGRGeometry* poGeom = OGR2SQLITE_GetGeom(pContext, argc, argv, NULL); + if( poGeom != NULL && wkbFlatten(poGeom->getGeometryType()) == wkbPoint ) + { + OGRPoint* poPoint = (OGRPoint*) poGeom; + dfLon = poPoint->getX(); + dfLat = poPoint->getY(); + delete poGeom; + } + else + { + delete poGeom; + sqlite3_result_null (pContext); + return; + } + iAfterGeomIdx = 1; + } + else + { + sqlite3_result_null (pContext); + return; + } + + const char* pszField = (const char*)sqlite3_value_text(argv[iAfterGeomIdx]); + + int i; + char** papszOptions = NULL; + for(i = iAfterGeomIdx + 1; i < argc; i++) + { + if( sqlite3_value_type (argv[i]) == SQLITE_TEXT ) + { + papszOptions = CSLAddString(papszOptions, + (const char*)sqlite3_value_text(argv[i])); + } + } + + OGRGeocodingSessionH hSession = poModule->GetGeocodingSession(); + if( hSession == NULL ) + { + hSession = OGRGeocodeCreateSession(papszOptions); + if( hSession == NULL ) + { + sqlite3_result_null (pContext); + CSLDestroy(papszOptions); + return; + } + poModule->SetGeocodingSession(hSession); + } + + if( strcmp(pszField, "raw") == 0 ) + papszOptions = CSLAddString(papszOptions, "RAW_FEATURE=YES"); + + OGRLayerH hLayer = OGRGeocodeReverse(hSession, dfLon, dfLat, papszOptions); + + OGR2SQLITE_ogr_geocode_set_result(pContext, hLayer, pszField); + + CSLDestroy(papszOptions); + + return; +} + +/************************************************************************/ +/* OGR2SQLITE_ogr_datasource_load_layers() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_datasource_load_layers(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + sqlite3* hDB = (sqlite3*) sqlite3_user_data(pContext); + + if( (argc < 1 || argc > 3) || sqlite3_value_type (argv[0]) != SQLITE_TEXT ) + { + sqlite3_result_int (pContext, 0); + return; + } + const char* pszDataSource = (const char*) sqlite3_value_text(argv[0]); + + int bUpdate = FALSE; + if( argc >= 2 ) + { + if( sqlite3_value_type(argv[1]) != SQLITE_INTEGER ) + { + sqlite3_result_int (pContext, 0); + return; + } + bUpdate = sqlite3_value_int(argv[1]); + } + + const char* pszPrefix = NULL; + if( argc >= 3 ) + { + if( sqlite3_value_type(argv[2]) != SQLITE_TEXT ) + { + sqlite3_result_int (pContext, 0); + return; + } + pszPrefix = (const char*) sqlite3_value_text(argv[2]); + } + + OGRDataSource* poDS = (OGRDataSource*)OGROpenShared(pszDataSource, bUpdate, NULL); + if( poDS == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszDataSource); + sqlite3_result_int (pContext, 0); + return; + } + + CPLString osEscapedDataSource = OGRSQLiteEscape(pszDataSource); + for(int i=0;iGetLayerCount();i++) + { + const char* pszLayerName = poDS->GetLayer(i)->GetName(); + CPLString osEscapedLayerName = OGRSQLiteEscape(pszLayerName); + CPLString osTableName; + if( pszPrefix != NULL ) + { + osTableName = pszPrefix; + osTableName += "_"; + osTableName += OGRSQLiteEscapeName(pszLayerName); + } + else + { + osTableName = OGRSQLiteEscapeName(pszLayerName); + } + + char* pszErrMsg = NULL; + if( sqlite3_exec(hDB, CPLSPrintf( + "CREATE VIRTUAL TABLE \"%s\" USING VirtualOGR('%s', %d, '%s')", + osTableName.c_str(), + osEscapedDataSource.c_str(), + bUpdate, + osEscapedLayerName.c_str()), + NULL, NULL, &pszErrMsg) != SQLITE_OK ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot create table \"%s\" : %s", + osTableName.c_str(), pszErrMsg); + sqlite3_free(pszErrMsg); + } + } + + poDS->Release(); + sqlite3_result_int (pContext, 1); +} + +#ifdef notdef +/************************************************************************/ +/* OGR2SQLITE_ogr_GetConfigOption() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_GetConfigOption(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_TEXT ) + { + sqlite3_result_null (pContext); + return; + } + + const char* pszKey = (const char*)sqlite3_value_text(argv[0]); + const char* pszVal = CPLGetConfigOption(pszKey, NULL); + if( pszVal == NULL ) + sqlite3_result_null (pContext); + else + sqlite3_result_text( pContext, pszVal, -1, SQLITE_TRANSIENT ); +} + +/************************************************************************/ +/* OGR2SQLITE_ogr_SetConfigOption() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_SetConfigOption(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_TEXT ) + { + sqlite3_result_null (pContext); + return; + } + if( sqlite3_value_type (argv[1]) != SQLITE_TEXT && + sqlite3_value_type (argv[1]) != SQLITE_NULL ) + { + sqlite3_result_null (pContext); + return; + } + + const char* pszKey = (const char*)sqlite3_value_text(argv[0]); + const char* pszVal = (sqlite3_value_type (argv[1]) == SQLITE_TEXT) ? + (const char*)sqlite3_value_text(argv[1]) : NULL; + CPLSetConfigOption(pszKey, pszVal); + sqlite3_result_null (pContext); +} +#endif // notdef + +#ifdef MINIMAL_SPATIAL_FUNCTIONS + +/************************************************************************/ +/* OGR2SQLITE_ST_AsText() */ +/************************************************************************/ + +static +void OGR2SQLITE_ST_AsText(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + OGRGeometry* poGeom = OGR2SQLITE_GetGeom(pContext, argc, argv, NULL); + if( poGeom != NULL ) + { + char* pszWKT = NULL; + if( poGeom->exportToWkt(&pszWKT) == OGRERR_NONE ) + sqlite3_result_text( pContext, pszWKT, -1, CPLFree); + else + sqlite3_result_null (pContext); + delete poGeom; + } + else + sqlite3_result_null (pContext); +} + +/************************************************************************/ +/* OGR2SQLITE_ST_AsBinary() */ +/************************************************************************/ + +static +void OGR2SQLITE_ST_AsBinary(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + OGRGeometry* poGeom = OGR2SQLITE_GetGeom(pContext, argc, argv, NULL); + if( poGeom != NULL ) + { + int nBLOBLen = poGeom->WkbSize(); + GByte* pabyGeomBLOB = (GByte*) VSIMalloc(nBLOBLen); + if( pabyGeomBLOB != NULL ) + { + if( poGeom->exportToWkb(wkbNDR, pabyGeomBLOB) == OGRERR_NONE ) + sqlite3_result_blob( pContext, pabyGeomBLOB, nBLOBLen, CPLFree); + else + { + VSIFree(pabyGeomBLOB); + sqlite3_result_null (pContext); + } + } + else + sqlite3_result_null (pContext); + delete poGeom; + } + else + sqlite3_result_null (pContext); +} + +/************************************************************************/ +/* OGR2SQLITE_SetGeom_AndDestroy() */ +/************************************************************************/ + +static void OGR2SQLITE_SetGeom_AndDestroy(sqlite3_context* pContext, + OGRGeometry* poGeom, + int nSRSId) +{ + GByte* pabySLBLOB = NULL; + int nBLOBLen = 0; + if( poGeom != NULL && OGRSQLiteLayer::ExportSpatiaLiteGeometry( + poGeom, nSRSId, wkbNDR, FALSE, + FALSE, FALSE, &pabySLBLOB, &nBLOBLen ) == CE_None ) + { + sqlite3_result_blob(pContext, pabySLBLOB, nBLOBLen, CPLFree); + } + else + { + sqlite3_result_null(pContext); + } + delete poGeom; +} + +/************************************************************************/ +/* OGR2SQLITE_ST_GeomFromText() */ +/************************************************************************/ + +static +void OGR2SQLITE_ST_GeomFromText(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_TEXT ) + { + sqlite3_result_null (pContext); + return; + } + char* pszWKT = (char*) sqlite3_value_text( argv[0] ); + + int nSRID = -1; + if( argc == 2 && sqlite3_value_type (argv[1]) == SQLITE_INTEGER ) + nSRID = sqlite3_value_int( argv[1] ); + + OGRGeometry* poGeom = NULL; + if( OGRGeometryFactory::createFromWkt(&pszWKT, NULL, &poGeom) == OGRERR_NONE ) + { + OGR2SQLITE_SetGeom_AndDestroy(pContext, poGeom, nSRID); + } + else + sqlite3_result_null (pContext); +} + +/************************************************************************/ +/* OGR2SQLITE_ST_GeomFromWKB() */ +/************************************************************************/ + +static +void OGR2SQLITE_ST_GeomFromWKB(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_BLOB ) + { + sqlite3_result_null (pContext); + return; + } + + int nSRID = -1; + if( argc == 2 && sqlite3_value_type (argv[1]) == SQLITE_INTEGER ) + nSRID = sqlite3_value_int( argv[1] ); + + GByte* pabySLBLOB = (GByte *) sqlite3_value_blob (argv[0]); + int nBLOBLen = sqlite3_value_bytes (argv[0]); + OGRGeometry* poGeom = NULL; + + if( OGRGeometryFactory::createFromWkb(pabySLBLOB, NULL, &poGeom, nBLOBLen) + == OGRERR_NONE ) + { + OGR2SQLITE_SetGeom_AndDestroy(pContext, poGeom, nSRID); + } + else + sqlite3_result_null (pContext); +} + +/************************************************************************/ +/* CheckSTFunctions() */ +/************************************************************************/ + +static int CheckSTFunctions(sqlite3_context* pContext, + int argc, sqlite3_value** argv, + OGRGeometry** ppoGeom1, + OGRGeometry** ppoGeom2, + int *pnSRSId ) +{ + *ppoGeom1 = NULL; + *ppoGeom2 = NULL; + + if( argc != 2) + { + return FALSE; + } + + *ppoGeom1 = OGR2SQLITE_GetGeom(pContext, argc, argv, pnSRSId); + if( *ppoGeom1 == NULL ) + return FALSE; + + *ppoGeom2 = OGR2SQLITE_GetGeom(pContext, argc - 1, argv + 1, NULL); + if( *ppoGeom2 == NULL ) + { + delete *ppoGeom1; + *ppoGeom1 = NULL; + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* OGR2SQLITE_ST_int_geomgeom_op() */ +/************************************************************************/ + +#define OGR2SQLITE_ST_int_geomgeom_op(op) \ +static \ +void OGR2SQLITE_ST_##op (sqlite3_context* pContext, \ + int argc, sqlite3_value** argv) \ +{ \ + OGRGeometry* poGeom1 = NULL; \ + OGRGeometry* poGeom2 = NULL; \ + if( !CheckSTFunctions(pContext, argc, argv, &poGeom1, &poGeom2, NULL) ) \ + { \ + sqlite3_result_int(pContext, 0); \ + return; \ + } \ + \ + sqlite3_result_int( pContext, poGeom1-> op (poGeom2) ); \ + \ + delete poGeom1; \ + delete poGeom2; \ +} + +OGR2SQLITE_ST_int_geomgeom_op(Intersects) +OGR2SQLITE_ST_int_geomgeom_op(Equals) +OGR2SQLITE_ST_int_geomgeom_op(Disjoint) +OGR2SQLITE_ST_int_geomgeom_op(Touches) +OGR2SQLITE_ST_int_geomgeom_op(Crosses) +OGR2SQLITE_ST_int_geomgeom_op(Within) +OGR2SQLITE_ST_int_geomgeom_op(Contains) +OGR2SQLITE_ST_int_geomgeom_op(Overlaps) + +/************************************************************************/ +/* OGR2SQLITE_ST_int_geom_op() */ +/************************************************************************/ + +#define OGR2SQLITE_ST_int_geom_op(op) \ +static \ +void OGR2SQLITE_ST_##op (sqlite3_context* pContext, \ + int argc, sqlite3_value** argv) \ +{ \ + OGRGeometry* poGeom = OGR2SQLITE_GetGeom(pContext, argc, argv, NULL); \ + if( poGeom != NULL ) \ + sqlite3_result_int( pContext, poGeom-> op () ); \ + else \ + sqlite3_result_int( pContext, 0 ); \ + \ + delete poGeom; \ +} + +OGR2SQLITE_ST_int_geom_op(IsEmpty) +OGR2SQLITE_ST_int_geom_op(IsSimple) +OGR2SQLITE_ST_int_geom_op(IsValid) + +/************************************************************************/ +/* OGR2SQLITE_ST_geom_geomgeom_op() */ +/************************************************************************/ + +#define OGR2SQLITE_ST_geom_geomgeom_op(op) \ +static \ +void OGR2SQLITE_ST_##op (sqlite3_context* pContext, \ + int argc, sqlite3_value** argv) \ +{ \ + OGRGeometry* poGeom1 = NULL; \ + OGRGeometry* poGeom2 = NULL; \ + int nSRSId = -1; \ + if( !CheckSTFunctions(pContext, argc, argv, &poGeom1, &poGeom2, &nSRSId) ) \ + { \ + sqlite3_result_null(pContext); \ + return; \ + } \ + \ + OGR2SQLITE_SetGeom_AndDestroy(pContext, \ + poGeom1-> op (poGeom2), \ + nSRSId); \ + \ + delete poGeom1; \ + delete poGeom2; \ +} + +OGR2SQLITE_ST_geom_geomgeom_op(Intersection) +OGR2SQLITE_ST_geom_geomgeom_op(Difference) +OGR2SQLITE_ST_geom_geomgeom_op(Union) +OGR2SQLITE_ST_geom_geomgeom_op(SymDifference) + +/************************************************************************/ +/* OGR2SQLITE_ST_SRID() */ +/************************************************************************/ + +static +void OGR2SQLITE_ST_SRID(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + int nSRSId = -1; + OGRGeometry* poGeom = OGR2SQLITE_GetGeom(pContext, argc, argv, &nSRSId); + if( poGeom != NULL ) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + sqlite3_result_int( pContext, nSRSId ); + CPLPopErrorHandler(); + } + else + sqlite3_result_null(pContext); + delete poGeom; +} + +/************************************************************************/ +/* OGR2SQLITE_ST_Area() */ +/************************************************************************/ + +static +void OGR2SQLITE_ST_Area(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + OGRGeometry* poGeom = OGR2SQLITE_GetGeom(pContext, argc, argv, NULL); + if( poGeom != NULL ) + { + CPLPushErrorHandler(CPLQuietErrorHandler); + sqlite3_result_double( pContext, OGR_G_Area((OGRGeometryH)poGeom) ); + CPLPopErrorHandler(); + } + else + sqlite3_result_null(pContext); + delete poGeom; +} + +/************************************************************************/ +/* OGR2SQLITE_ST_Buffer() */ +/************************************************************************/ + +static +void OGR2SQLITE_ST_Buffer(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + int nSRSId = -1; + OGRGeometry* poGeom = OGR2SQLITE_GetGeom(pContext, argc, argv, &nSRSId); + int bGotVal; + double dfDist = OGR2SQLITE_GetValAsDouble(argv[1], &bGotVal); + if( poGeom != NULL && bGotVal ) + OGR2SQLITE_SetGeom_AndDestroy(pContext, poGeom->Buffer(dfDist), nSRSId); + else + sqlite3_result_null(pContext); + delete poGeom; +} + +/************************************************************************/ +/* OGR2SQLITE_ST_MakePoint() */ +/************************************************************************/ + +static +void OGR2SQLITE_ST_MakePoint(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + double dfX, dfY = 0.0; + int bGotVal; + dfX = OGR2SQLITE_GetValAsDouble(argv[0], &bGotVal); + if( bGotVal ) + dfY = OGR2SQLITE_GetValAsDouble(argv[1], &bGotVal); + if( !bGotVal ) + { + sqlite3_result_null(pContext); + return; + } + + OGRPoint* poPoint; + if( argc == 3 ) + { + double dfZ = OGR2SQLITE_GetValAsDouble(argv[2], &bGotVal); + if( !bGotVal ) + { + sqlite3_result_null(pContext); + return; + } + + poPoint = new OGRPoint(dfX, dfY, dfZ); + } + else + poPoint = new OGRPoint(dfX, dfY); + + OGR2SQLITE_SetGeom_AndDestroy(pContext, poPoint, -1); +} + +#endif // #ifdef MINIMAL_SPATIAL_FUNCTIONS + + +/************************************************************************/ +/* OGRSQLITE_hstore_get_value() */ +/************************************************************************/ + +static +void OGRSQLITE_hstore_get_value(sqlite3_context* pContext, + CPL_UNUSED int argc, + sqlite3_value** argv) +{ + if( sqlite3_value_type (argv[0]) != SQLITE_TEXT || + sqlite3_value_type (argv[1]) != SQLITE_TEXT ) + { + sqlite3_result_null (pContext); + return; + } + + const char* pszHStore = (const char*)sqlite3_value_text(argv[0]); + const char* pszSearchedKey = (const char*)sqlite3_value_text(argv[1]); + char* pszValue = OGRHStoreGetValue(pszHStore, pszSearchedKey); + if( pszValue != NULL ) + sqlite3_result_text( pContext, pszValue, -1, CPLFree ); + else + sqlite3_result_null( pContext ); +} + +/************************************************************************/ +/* OGRSQLiteRegisterSQLFunctions() */ +/************************************************************************/ + +static +void* OGRSQLiteRegisterSQLFunctions(sqlite3* hDB) +{ + OGRSQLiteExtensionData* pData = new OGRSQLiteExtensionData(hDB); + + sqlite3_create_function(hDB, "ogr_version", 0, SQLITE_ANY, NULL, + OGR2SQLITE_ogr_version, NULL, NULL); + + sqlite3_create_function(hDB, "ogr_version", 1, SQLITE_ANY, NULL, + OGR2SQLITE_ogr_version, NULL, NULL); + + sqlite3_create_function(hDB, "ogr_deflate", 1, SQLITE_ANY, NULL, + OGR2SQLITE_ogr_deflate, NULL, NULL); + + sqlite3_create_function(hDB, "ogr_deflate", 2, SQLITE_ANY, NULL, + OGR2SQLITE_ogr_deflate, NULL, NULL); + + sqlite3_create_function(hDB, "ogr_inflate", 1, SQLITE_ANY, NULL, + OGR2SQLITE_ogr_inflate, NULL, NULL); + + sqlite3_create_function(hDB, "ogr_geocode", -1, SQLITE_ANY, pData, + OGR2SQLITE_ogr_geocode, NULL, NULL); + + sqlite3_create_function(hDB, "ogr_geocode_reverse", -1, SQLITE_ANY, pData, + OGR2SQLITE_ogr_geocode_reverse, NULL, NULL); + + sqlite3_create_function(hDB, "ogr_datasource_load_layers", 1, SQLITE_ANY, hDB, + OGR2SQLITE_ogr_datasource_load_layers, NULL, NULL); + + sqlite3_create_function(hDB, "ogr_datasource_load_layers", 2, SQLITE_ANY, hDB, + OGR2SQLITE_ogr_datasource_load_layers, NULL, NULL); + + sqlite3_create_function(hDB, "ogr_datasource_load_layers", 3, SQLITE_ANY, hDB, + OGR2SQLITE_ogr_datasource_load_layers, NULL, NULL); + +#if notdef + sqlite3_create_function(hDB, "ogr_GetConfigOption", 1, SQLITE_ANY, NULL, + OGR2SQLITE_ogr_GetConfigOption, NULL, NULL); + + sqlite3_create_function(hDB, "ogr_SetConfigOption", 2, SQLITE_ANY, NULL, + OGR2SQLITE_ogr_SetConfigOption, NULL, NULL); +#endif + + // Custom and undocumented function, not sure I'll keep it. + sqlite3_create_function(hDB, "Transform3", 3, SQLITE_ANY, pData, + OGR2SQLITE_Transform, NULL, NULL); + + // HSTORE functions + sqlite3_create_function(hDB, "hstore_get_value", 2, SQLITE_ANY, NULL, + OGRSQLITE_hstore_get_value, NULL, NULL); + +#ifdef MINIMAL_SPATIAL_FUNCTIONS + /* Check if spatialite is available */ + int rc = sqlite3_exec(hDB, "SELECT spatialite_version()", NULL, NULL, NULL); + + /* Reset error flag */ + sqlite3_exec(hDB, "SELECT 1", NULL, NULL, NULL); + + if( rc != SQLITE_OK && + CSLTestBoolean(CPLGetConfigOption("OGR_SQLITE_SPATIAL_FUNCTIONS", "YES")) ) + { + CPLDebug("SQLITE", + "Spatialite not available. Implementing a few functions"); + +#define REGISTER_ST_op(argc, op) \ + sqlite3_create_function(hDB, #op, argc, SQLITE_ANY, NULL, \ + OGR2SQLITE_ST_##op, NULL, NULL); \ + sqlite3_create_function(hDB, "ST_" #op, argc, SQLITE_ANY, NULL, \ + OGR2SQLITE_ST_##op, NULL, NULL); + + REGISTER_ST_op(1, AsText); + REGISTER_ST_op(1, AsBinary); + REGISTER_ST_op(1, GeomFromText); + REGISTER_ST_op(2, GeomFromText); + REGISTER_ST_op(1, GeomFromWKB); + REGISTER_ST_op(2, GeomFromWKB); + + REGISTER_ST_op(1, IsEmpty); + REGISTER_ST_op(1, IsSimple); + REGISTER_ST_op(1, IsValid); + + REGISTER_ST_op(2, Intersects); + REGISTER_ST_op(2, Equals); + REGISTER_ST_op(2, Disjoint); + REGISTER_ST_op(2, Touches); + REGISTER_ST_op(2, Crosses); + REGISTER_ST_op(2, Within); + REGISTER_ST_op(2, Contains); + REGISTER_ST_op(2, Overlaps); + + REGISTER_ST_op(2, Intersection); + REGISTER_ST_op(2, Difference); + // Union() is invalid + sqlite3_create_function(hDB, "ST_Union", 2, SQLITE_ANY, NULL, + OGR2SQLITE_ST_Union, NULL, NULL); + REGISTER_ST_op(2, SymDifference); + + REGISTER_ST_op(1, SRID); + REGISTER_ST_op(1, Area); + REGISTER_ST_op(2, Buffer); + REGISTER_ST_op(2, MakePoint); + REGISTER_ST_op(3, MakePoint); + } +#endif // #ifdef MINIMAL_SPATIAL_FUNCTIONS + + pData->SetRegExpCache(OGRSQLiteRegisterRegExpFunction(hDB)); + + return pData; +} + +/************************************************************************/ +/* OGRSQLiteUnregisterSQLFunctions() */ +/************************************************************************/ + +static +void OGRSQLiteUnregisterSQLFunctions(void* hHandle) +{ + OGRSQLiteExtensionData* pData = (OGRSQLiteExtensionData* )hHandle; + delete pData; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitesqlfunctions.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitesqlfunctions.h new file mode 100644 index 000000000..da04f76c4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitesqlfunctions.h @@ -0,0 +1,38 @@ +/****************************************************************************** + * $Id: ogrsqlitesqlfunctions.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Extension SQL functions + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SQLITE_SQL_FUNCTIONS_INCLUDED +#define _OGR_SQLITE_SQL_FUNCTIONS_INCLUDED + +#include "ogr_sqlite.h" + +static void* OGRSQLiteRegisterSQLFunctions(sqlite3* hDB); +static void OGRSQLiteUnregisterSQLFunctions(void* hHandle); + +#endif // _OGR_SQLITE_SQL_FUNCTIONS_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitetablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitetablelayer.cpp new file mode 100644 index 000000000..1cad0263c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitetablelayer.cpp @@ -0,0 +1,3733 @@ +/****************************************************************************** + * $Id: ogrsqlitetablelayer.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRSQLiteTableLayer class, access to an existing table. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2004, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_sqlite.h" +#include "ogr_p.h" +#include "cpl_time.h" +#include + +#define UNSUPPORTED_OP_READ_ONLY "%s : unsupported operation on a read-only datasource." + +CPL_CVSID("$Id: ogrsqlitetablelayer.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +/************************************************************************/ +/* OGRSQLiteTableLayer() */ +/************************************************************************/ + +OGRSQLiteTableLayer::OGRSQLiteTableLayer( OGRSQLiteDataSource *poDSIn ) + +{ + poDS = poDSIn; + + bLaunderColumnNames = TRUE; + + /* SpatiaLite v.2.4.0 (or any subsequent) is required + to support 2.5D: if an obsolete version of the library + is found we'll unconditionally activate 2D casting mode. + */ + bSpatialite2D = poDS->GetSpatialiteVersionNumber() < 24; + + iNextShapeId = 0; + + poFeatureDefn = NULL; + pszTableName = NULL; + pszEscapedTableName = NULL; + + bDeferredSpatialIndexCreation = FALSE; + + hInsertStmt = NULL; + + bLayerDefnError = FALSE; + + bStatisticsNeedsToBeFlushed = FALSE; + nFeatureCount = -1; + + int bDisableInsertTriggers = CSLTestBoolean(CPLGetConfigOption( + "OGR_SQLITE_DISABLE_INSERT_TRIGGERS", "YES")); + bHasCheckedTriggers = !bDisableInsertTriggers; + bDeferredCreation = FALSE; + pszCreationGeomFormat = NULL; + iFIDAsRegularColumnIndex = -1; +} + +/************************************************************************/ +/* ~OGRSQLiteTableLayer() */ +/************************************************************************/ + +OGRSQLiteTableLayer::~OGRSQLiteTableLayer() + +{ + ClearStatement(); + ClearInsertStmt(); + + char* pszErrMsg = NULL; + int nGeomFieldCount = (poFeatureDefn) ? poFeatureDefn->GetGeomFieldCount() : 0; + for(int i=0;imyGetGeomFieldDefn(i); + // Restore temporarily disabled triggers + for(int j = 0; j < (int)poGeomFieldDefn->aosDisabledTriggers.size(); j++ ) + { + CPLDebug("SQLite", "Restoring trigger %s", + poGeomFieldDefn->aosDisabledTriggers[j].first.c_str()); + // This may fail since CreateSpatialIndex() reinstalls triggers, so + // don't check result + sqlite3_exec( poDS->GetDB(), + poGeomFieldDefn->aosDisabledTriggers[j].second.c_str(), + NULL, NULL, &pszErrMsg ); + if( pszErrMsg ) + sqlite3_free( pszErrMsg ); + pszErrMsg = NULL; + } + + // Update geometry_columns_time + if( poGeomFieldDefn->aosDisabledTriggers.size() != 0 ) + { + char* pszSQL3 = sqlite3_mprintf( + "UPDATE geometry_columns_time SET last_insert = strftime('%%Y-%%m-%%dT%%H:%%M:%%fZ', 'now') " + "WHERE Lower(f_table_name) = Lower('%q') AND Lower(f_geometry_column) = Lower('%q')", + pszTableName, poGeomFieldDefn->GetNameRef()); + sqlite3_exec( poDS->GetDB(), pszSQL3, NULL, NULL, &pszErrMsg ); + if( pszErrMsg ) + sqlite3_free( pszErrMsg ); + pszErrMsg = NULL; + } + } + + CPLFree(pszTableName); + CPLFree(pszEscapedTableName); + CPLFree(pszCreationGeomFormat); +} + +/************************************************************************/ +/* CreateSpatialIndexIfNecessary() */ +/************************************************************************/ + +void OGRSQLiteTableLayer::CreateSpatialIndexIfNecessary() +{ + if( bDeferredSpatialIndexCreation ) + { + for(int iGeomCol = 0; iGeomCol < poFeatureDefn->GetGeomFieldCount(); iGeomCol ++) + CreateSpatialIndex(iGeomCol); + bDeferredSpatialIndexCreation = FALSE; + } +} + +/************************************************************************/ +/* ClearInsertStmt() */ +/************************************************************************/ + +void OGRSQLiteTableLayer::ClearInsertStmt() +{ + if( hInsertStmt != NULL ) + { + sqlite3_finalize( hInsertStmt ); + hInsertStmt = NULL; + } + osLastInsertStmt = ""; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +CPLErr OGRSQLiteTableLayer::Initialize( const char *pszTableName, + int bIsVirtualShapeIn, + int bDeferredCreation ) +{ + SetDescription( pszTableName ); + + this->bIsVirtualShape = bIsVirtualShapeIn; + this->pszTableName = CPLStrdup(pszTableName); + this->bDeferredCreation = bDeferredCreation; + pszEscapedTableName = CPLStrdup(OGRSQLiteEscape(pszTableName)); + + if( strchr(pszTableName, '(') != NULL && + pszTableName[strlen(pszTableName)-1] == ')' ) + { + char* pszErrMsg = NULL; + int nRowCount = 0, nColCount = 0; + char** papszResult = NULL; + const char* pszSQL = CPLSPrintf("SELECT * FROM sqlite_master WHERE name = '%s'", + pszEscapedTableName); + int rc = sqlite3_get_table( poDS->GetDB(), + pszSQL, + &papszResult, &nRowCount, + &nColCount, &pszErrMsg ); + int bFound = ( rc == SQLITE_OK && nRowCount == 1 ); + sqlite3_free_table(papszResult); + sqlite3_free( pszErrMsg ); + + if( !bFound ) + { + char* pszGeomCol = CPLStrdup(strchr(pszTableName, '(')+1); + pszGeomCol[strlen(pszGeomCol)-1] = 0; + *strchr(this->pszTableName, '(') = 0; + pszTableName = this->pszTableName; + CPLFree(pszEscapedTableName), + pszEscapedTableName = CPLStrdup(OGRSQLiteEscape(pszTableName)); + EstablishFeatureDefn(pszGeomCol); + CPLFree(pszGeomCol); + if( poFeatureDefn->GetGeomFieldCount() == 0 ) + return CE_Failure; + } + } + + return CE_None; +} + +/************************************************************************/ +/* GetGeomFormat() */ +/************************************************************************/ + +static OGRSQLiteGeomFormat GetGeomFormat( const char* pszGeomFormat ) +{ + OGRSQLiteGeomFormat eGeomFormat = OSGF_None; + if( pszGeomFormat ) + { + if ( EQUAL(pszGeomFormat, "WKT") ) + eGeomFormat = OSGF_WKT; + else if ( EQUAL(pszGeomFormat,"WKB") ) + eGeomFormat = OSGF_WKB; + else if ( EQUAL(pszGeomFormat,"FGF") ) + eGeomFormat = OSGF_FGF; + else if( EQUAL(pszGeomFormat,"SpatiaLite") ) + eGeomFormat = OSGF_SpatiaLite; + } + return eGeomFormat; +} + +/************************************************************************/ +/* SetCreationParameters() */ +/************************************************************************/ + +void OGRSQLiteTableLayer::SetCreationParameters( const char *pszFIDColumnName, + OGRwkbGeometryType eGeomType, + const char *pszGeomFormat, + const char *pszGeometryName, + OGRSpatialReference *poSRS, + int nSRSId ) + +{ + pszFIDColumn = CPLStrdup(pszFIDColumnName); + poFeatureDefn = new OGRSQLiteFeatureDefn(pszTableName); + poFeatureDefn->SetGeomType(wkbNone); + poFeatureDefn->Reference(); + pszCreationGeomFormat = (pszGeomFormat) ? CPLStrdup(pszGeomFormat) : NULL; + if( eGeomType != wkbNone ) + { + if( nSRSId == UNINITIALIZED_SRID ) + nSRSId = poDS->GetUndefinedSRID(); + OGRSQLiteGeomFormat eGeomFormat = GetGeomFormat(pszGeomFormat); + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + new OGRSQLiteGeomFieldDefn(pszGeometryName, -1); + poGeomFieldDefn->SetType(eGeomType); + poGeomFieldDefn->nSRSId = nSRSId; + poGeomFieldDefn->eGeomFormat = eGeomFormat; + poGeomFieldDefn->SetSpatialRef(poSRS); + poFeatureDefn->AddGeomFieldDefn(poGeomFieldDefn, FALSE); + } +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char* OGRSQLiteTableLayer::GetName() +{ + return GetDescription(); +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char** OGRSQLiteTableLayer::GetMetadata( const char * pszDomain ) +{ + GetLayerDefn(); + return OGRSQLiteLayer::GetMetadata(pszDomain); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char * OGRSQLiteTableLayer::GetMetadataItem( const char * pszName, + const char * pszDomain ) +{ + GetLayerDefn(); + return OGRSQLiteLayer::GetMetadataItem(pszName, pszDomain); +} + +/************************************************************************/ +/* EstablishFeatureDefn() */ +/************************************************************************/ + +CPLErr OGRSQLiteTableLayer::EstablishFeatureDefn(const char* pszGeomCol) +{ + sqlite3 *hDB = poDS->GetDB(); + int rc; + const char *pszSQL; + sqlite3_stmt *hColStmt = NULL; + +/* -------------------------------------------------------------------- */ +/* Get the column definitions for this table. */ +/* -------------------------------------------------------------------- */ + + pszSQL = CPLSPrintf( "SELECT _rowid_, * FROM '%s' LIMIT 1", + pszEscapedTableName ); + + rc = sqlite3_prepare( hDB, pszSQL, strlen(pszSQL), &hColStmt, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to query table %s for column definitions : %s.", + pszTableName, sqlite3_errmsg(hDB) ); + + return CE_Failure; + } + + rc = sqlite3_step( hColStmt ); + if ( rc != SQLITE_DONE && rc != SQLITE_ROW ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In Initialize(): sqlite3_step(%s):\n %s", + pszSQL, sqlite3_errmsg(hDB) ); + sqlite3_finalize( hColStmt ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* What should we use as FID? If there is a primary key */ +/* integer field, then this will be used as the _rowid_, and we */ +/* will pick up the real column name here. Otherwise, we will */ +/* just use fid. */ +/* */ +/* Note that the select _rowid_ will return the real column */ +/* name if the rowid corresponds to another primary key */ +/* column. */ +/* -------------------------------------------------------------------- */ + CPLFree( pszFIDColumn ); + pszFIDColumn = CPLStrdup(OGRSQLiteParamsUnquote(sqlite3_column_name( hColStmt, 0 ))); + +/* -------------------------------------------------------------------- */ +/* Collect the rest of the fields. */ +/* -------------------------------------------------------------------- */ + if( pszGeomCol ) + { + std::set aosGeomCols; + aosGeomCols.insert(pszGeomCol); + std::set aosIgnoredCols(poDS->GetGeomColsForTable(pszTableName)); + aosIgnoredCols.erase(pszGeomCol); + BuildFeatureDefn( GetDescription(), hColStmt, aosGeomCols, aosIgnoredCols); + } + else + { + std::set aosIgnoredCols; + BuildFeatureDefn( GetDescription(), hColStmt, + poDS->GetGeomColsForTable(pszTableName), aosIgnoredCols ); + } + sqlite3_finalize( hColStmt ); + +/* -------------------------------------------------------------------- */ +/* Find if the FID holds 64bit values */ +/* -------------------------------------------------------------------- */ + pszSQL = CPLSPrintf("SELECT MAX(%s) FROM '%s'", + OGRSQLiteEscape(pszFIDColumn).c_str(), + pszEscapedTableName); + hColStmt = NULL; + rc = sqlite3_prepare( hDB, pszSQL, strlen(pszSQL), &hColStmt, NULL ); + if( rc == SQLITE_OK ) + { + rc = sqlite3_step( hColStmt ); + if( rc == SQLITE_ROW ) + { + GIntBig nMaxId = sqlite3_column_int64( hColStmt, 0 ); + if( nMaxId > INT_MAX ) + SetMetadataItem(OLMD_FID64, "YES"); + } + } + sqlite3_finalize( hColStmt ); + +/* -------------------------------------------------------------------- */ +/* Set the properties of the geometry column. */ +/* -------------------------------------------------------------------- */ + int bHasSpatialiteCol = FALSE; + for(int i=0; iGetGeomFieldCount(); i++ ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(i); + poGeomFieldDefn->nSRSId = poDS->GetUndefinedSRID(); + + if( poDS->IsSpatialiteDB() ) + { + if( poDS->HasSpatialite4Layout() ) + { + pszSQL = CPLSPrintf("SELECT srid, geometry_type, coord_dimension, spatial_index_enabled FROM geometry_columns WHERE lower(f_table_name) = lower('%s') AND lower(f_geometry_column) = lower('%s')", + pszEscapedTableName, + OGRSQLiteEscape(poGeomFieldDefn->GetNameRef()).c_str()); + } + else + { + pszSQL = CPLSPrintf("SELECT srid, type, coord_dimension, spatial_index_enabled FROM geometry_columns WHERE lower(f_table_name) = lower('%s') AND lower(f_geometry_column) = lower('%s')", + pszEscapedTableName, + OGRSQLiteEscape(poGeomFieldDefn->GetNameRef()).c_str()); + } + } + else + { + pszSQL = CPLSPrintf("SELECT srid, geometry_type, coord_dimension, geometry_format FROM geometry_columns WHERE lower(f_table_name) = lower('%s') AND lower(f_geometry_column) = lower('%s')", + pszEscapedTableName, + OGRSQLiteEscape(poGeomFieldDefn->GetNameRef()).c_str()); + } + char* pszErrMsg = NULL; + int nRowCount = 0, nColCount = 0; + char** papszResult = NULL; + rc = sqlite3_get_table( hDB, + pszSQL, + &papszResult, &nRowCount, + &nColCount, &pszErrMsg ); + OGRwkbGeometryType eGeomType = wkbUnknown; + OGRSQLiteGeomFormat eGeomFormat = OSGF_None; + if( rc == SQLITE_OK && nRowCount == 1 ) + { + char **papszRow = papszResult + nColCount; + if( papszRow[1] == NULL || papszRow[2] == NULL ) + { + CPLDebug("SQLite", "Did not get expected col value"); + continue; + } + if( papszRow[0] != NULL ) + poGeomFieldDefn->nSRSId = atoi(papszRow[0]); + if( poDS->IsSpatialiteDB() ) + { + int bHasM = FALSE; + if( papszRow[3] != NULL ) + poGeomFieldDefn->bHasSpatialIndex = atoi(papszRow[3]); + if( poDS->HasSpatialite4Layout() ) + { + int nGeomType = atoi(papszRow[1]); + + if( nGeomType >= 0 && nGeomType <= 7 ) /* XY */ + eGeomType = (OGRwkbGeometryType) nGeomType; + else if( nGeomType >= 1000 && nGeomType <= 1007 ) /* XYZ */ + eGeomType = wkbSetZ(wkbFlatten(nGeomType)); + else if( nGeomType >= 2000 && nGeomType <= 2007 ) /* XYM */ + { + eGeomType = wkbFlatten(nGeomType); + bHasM = TRUE; + } + else if( nGeomType >= 3000 && nGeomType <= 3007 ) /* XYZM */ + { + eGeomType = wkbSetZ(wkbFlatten(nGeomType)); + bHasM = TRUE; + } + } + else + { + eGeomType = OGRFromOGCGeomType(papszRow[1]); + + if( strcmp ( papszRow[2], "XYZ" ) == 0 || + strcmp ( papszRow[2], "XYZM" ) == 0 || + strcmp ( papszRow[2], "3" ) == 0) // SpatiaLite's own 3D geometries + eGeomType = wkbSetZ(eGeomType); + + if( strcmp ( papszRow[2], "XYM" ) == 0 || + strcmp ( papszRow[2], "XYZM" ) == 0 ) // M coordinate declared + bHasM = TRUE; + } + poGeomFieldDefn->bHasM = bHasM; + eGeomFormat = OSGF_SpatiaLite; + } + else + { + eGeomType = (OGRwkbGeometryType) atoi(papszRow[1]); + if( atoi(papszRow[2]) > 2 ) + eGeomType = wkbSetZ(eGeomType); + eGeomFormat = GetGeomFormat( papszRow[3] ); + } + } + sqlite3_free_table(papszResult); + sqlite3_free( pszErrMsg ); + + poGeomFieldDefn->eGeomFormat = eGeomFormat; + poGeomFieldDefn->SetType( eGeomType ); + poGeomFieldDefn->SetSpatialRef(poDS->FetchSRS(poGeomFieldDefn->nSRSId)); + + if( eGeomFormat == OSGF_SpatiaLite ) + bHasSpatialiteCol = TRUE; + } + + if ( bHasSpatialiteCol && + poDS->IsSpatialiteLoaded() && + poDS->GetSpatialiteVersionNumber() < 24 && poDS->GetUpdate() ) + { + // we need to test version required by Spatialite TRIGGERs + // hColStmt = NULL; + const char *pszSQL = CPLSPrintf( "SELECT sql FROM sqlite_master WHERE type = 'trigger' AND tbl_name = '%s' AND sql LIKE '%%RTreeAlign%%'", + pszEscapedTableName ); + + int nRowTriggerCount, nColTriggerCount; + char **papszTriggerResult, *pszErrMsg; + + /* rc = */ sqlite3_get_table( hDB, pszSQL, &papszTriggerResult, + &nRowTriggerCount, &nColTriggerCount, &pszErrMsg ); + if( nRowTriggerCount >= 1 ) + { + // obsolete library version not supporting new triggers + // enforcing ReadOnly mode + CPLDebug("SQLITE", "Enforcing ReadOnly mode : obsolete library version not supporting new triggers"); + poDS->SetUpdate(FALSE); + } + + sqlite3_free_table( papszTriggerResult ); + } +/* -------------------------------------------------------------------- */ +/* Check if there are default values and nullable status */ +/* -------------------------------------------------------------------- */ + + char **papszResult; + int nRowCount, nColCount; + char *pszErrMsg = NULL; + /* #|name|type|notnull|default|pk */ + char* pszSQL3 = sqlite3_mprintf("PRAGMA table_info('%q')", pszTableName); + rc = sqlite3_get_table( hDB, pszSQL3, &papszResult, &nRowCount, + &nColCount, &pszErrMsg ); + sqlite3_free( pszSQL3 ); + if( rc != SQLITE_OK ) + { + sqlite3_free( pszErrMsg ); + } + else + { + if( nColCount == 6 ) + { + for(int i=0;iGetFieldIndex(pszName); + if( idx >= 0 ) + { + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(idx); + if( poFieldDefn->GetType() == OFTString && + !EQUAL(pszDefault, "NULL") && + !EQUALN(pszDefault, "CURRENT_", strlen("CURRENT_")) && + pszDefault[0] != '(' && + pszDefault[0] != '\'' && + CPLGetValueType(pszDefault) == CPL_VALUE_STRING ) + { + CPLString osDefault("'"); + char* pszTmp = CPLEscapeString(pszDefault, -1, CPLES_SQL); + osDefault += pszTmp; + CPLFree(pszTmp); + osDefault += "'"; + poFieldDefn->SetDefault(osDefault); + } + else if( (poFieldDefn->GetType() == OFTDate || poFieldDefn->GetType() == OFTDateTime) && + !EQUAL(pszDefault, "NULL") && + !EQUALN(pszDefault, "CURRENT_", strlen("CURRENT_")) && + pszDefault[0] != '(' && + pszDefault[0] != '\'' && + !(pszDefault[0] >= '0' && pszDefault[0] <= '9') && + CPLGetValueType(pszDefault) == CPL_VALUE_STRING ) + { + CPLString osDefault("("); + osDefault += pszDefault; + osDefault += ")"; + poFieldDefn->SetDefault(osDefault); + } + else + poFieldDefn->SetDefault(pszDefault); + } + } + if( pszName != NULL && pszNotNull != NULL && + EQUAL(pszNotNull, "1") ) + { + int idx = poFeatureDefn->GetFieldIndex(pszName); + if( idx >= 0 ) + poFeatureDefn->GetFieldDefn(idx)->SetNullable(0); + else + { + idx = poFeatureDefn->GetGeomFieldIndex(pszName); + if( idx >= 0 ) + poFeatureDefn->GetGeomFieldDefn(idx)->SetNullable(0); + } + } + } + } + sqlite3_free_table(papszResult); + } + + return CE_None; +} + +/************************************************************************/ +/* RecomputeOrdinals() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::RecomputeOrdinals() +{ + sqlite3 *hDB = poDS->GetDB(); + int rc; + const char *pszSQL; + sqlite3_stmt *hColStmt = NULL; +/* -------------------------------------------------------------------- */ +/* Get the column definitions for this table. */ +/* -------------------------------------------------------------------- */ + + pszSQL = CPLSPrintf( "SELECT _rowid_, * FROM '%s' LIMIT 1", + pszEscapedTableName ); + + rc = sqlite3_prepare( hDB, pszSQL, strlen(pszSQL), &hColStmt, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to query table %s for column definitions : %s.", + pszTableName, sqlite3_errmsg(hDB) ); + + return CE_Failure; + } + + rc = sqlite3_step( hColStmt ); + if ( rc != SQLITE_DONE && rc != SQLITE_ROW ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In Initialize(): sqlite3_step(%s):\n %s", + pszSQL, sqlite3_errmsg(hDB) ); + sqlite3_finalize( hColStmt ); + return CE_Failure; + } + + int nRawColumns = sqlite3_column_count( hColStmt ); + + CPLFree(panFieldOrdinals); + panFieldOrdinals = (int *) CPLMalloc( sizeof(int) * poFeatureDefn->GetFieldCount() ); + int nCountFieldOrdinals = 0; + int nCountGeomFieldOrdinals = 0; + iFIDCol = -1; + + int iCol; + for( iCol = 0; iCol < nRawColumns; iCol++ ) + { + CPLString osName = + OGRSQLiteParamsUnquote(sqlite3_column_name( hColStmt, iCol )); + int nIdx = poFeatureDefn->GetFieldIndex(osName); + if( pszFIDColumn != NULL && strcmp(osName, pszFIDColumn) == 0 ) + { + if( iFIDCol < 0 ) + { + iFIDCol = iCol; + if( nIdx >= 0 ) /* in case it has also been created as a regular field */ + nCountFieldOrdinals ++; + } + continue; + } + if( nIdx >= 0 ) + { + panFieldOrdinals[nIdx] = iCol; + nCountFieldOrdinals ++; + } + else + { + nIdx = poFeatureDefn->GetGeomFieldIndex(osName); + if( nIdx >= 0 ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(nIdx); + poGeomFieldDefn->iCol = iCol; + nCountGeomFieldOrdinals ++; + } + } + } + CPLAssert(nCountFieldOrdinals == poFeatureDefn->GetFieldCount() ); + CPLAssert(nCountGeomFieldOrdinals == poFeatureDefn->GetGeomFieldCount() ); + CPLAssert(pszFIDColumn == NULL || iFIDCol >= 0 ); + + sqlite3_finalize( hColStmt ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn* OGRSQLiteTableLayer::GetLayerDefn() +{ + if (poFeatureDefn) + return poFeatureDefn; + + EstablishFeatureDefn(NULL); + + if (poFeatureDefn == NULL) + { + bLayerDefnError = TRUE; + + poFeatureDefn = new OGRSQLiteFeatureDefn( GetDescription() ); + poFeatureDefn->Reference(); + } + else + LoadStatistics(); + + return poFeatureDefn; +} + +/************************************************************************/ +/* ResetStatement() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::ResetStatement() + +{ + int rc; + CPLString osSQL; + + if( bDeferredCreation ) RunDeferredCreationIfNecessary(); + + ClearStatement(); + + iNextShapeId = 0; + + osSQL.Printf( "SELECT _rowid_, * FROM '%s' %s", + pszEscapedTableName, + osWHERE.c_str() ); + + +//#ifdef HAVE_SQLITE3_PREPARE_V2 +// rc = sqlite3_prepare_v2( poDS->GetDB(), osSQL, osSQL.size(), +// &hStmt, NULL ); +//#else + rc = sqlite3_prepare( poDS->GetDB(), osSQL, osSQL.size(), + &hStmt, NULL ); +//#endif + + if( rc == SQLITE_OK ) + { + return OGRERR_NONE; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "In ResetStatement(): sqlite3_prepare(%s):\n %s", + osSQL.c_str(), sqlite3_errmsg(poDS->GetDB()) ); + hStmt = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRSQLiteTableLayer::GetNextFeature() + +{ + if( bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE ) + return NULL; + + if (HasLayerDefnError()) + return NULL; + + OGRFeature* poFeature = OGRSQLiteLayer::GetNextFeature(); + if( poFeature && iFIDAsRegularColumnIndex >= 0 ) + { + poFeature->SetField(iFIDAsRegularColumnIndex, poFeature->GetFID()); + } + return poFeature; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRSQLiteTableLayer::GetFeature( GIntBig nFeatureId ) + +{ + if( bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE ) + return NULL; + + if (HasLayerDefnError()) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we don't have an explicit FID column, just read through */ +/* the result set iteratively to find our target. */ +/* -------------------------------------------------------------------- */ + if( pszFIDColumn == NULL ) + return OGRSQLiteLayer::GetFeature( nFeatureId ); + +/* -------------------------------------------------------------------- */ +/* Setup explicit query statement to fetch the record we want. */ +/* -------------------------------------------------------------------- */ + CPLString osSQL; + int rc; + + ClearStatement(); + + iNextShapeId = nFeatureId; + + osSQL.Printf( "SELECT _rowid_, * FROM '%s' WHERE \"%s\" = " CPL_FRMT_GIB, + pszEscapedTableName, + OGRSQLiteEscape(pszFIDColumn).c_str(), nFeatureId ); + + CPLDebug( "OGR_SQLITE", "exec(%s)", osSQL.c_str() ); + + rc = sqlite3_prepare( poDS->GetDB(), osSQL, osSQL.size(), + &hStmt, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In GetFeature(): sqlite3_prepare(%s):\n %s", + osSQL.c_str(), sqlite3_errmsg(poDS->GetDB()) ); + + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Get the feature if possible. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = NULL; + + poFeature = GetNextRawFeature(); + + ResetReading(); + + return poFeature; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::SetAttributeFilter( const char *pszQuery ) + +{ + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + + if( pszQuery == NULL ) + osQuery = ""; + else + osQuery = pszQuery; + + BuildWhere(); + + ResetReading(); + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRSQLiteTableLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) +{ + SetSpatialFilter(0,poGeomIn); +} + +void OGRSQLiteTableLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeomIn ) + +{ + if( iGeomField == 0 ) + { + m_iGeomFieldFilter = 0; + } + else + { + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + return; + } + + m_iGeomFieldFilter = iGeomField; + } + + if( InstallFilter( poGeomIn ) ) + { + BuildWhere(); + + ResetReading(); + } +} + +/************************************************************************/ +/* CheckSpatialIndexTable() */ +/************************************************************************/ + +int OGRSQLiteTableLayer::CheckSpatialIndexTable(int iGeomCol) +{ + GetLayerDefn(); + if( iGeomCol < 0 || iGeomCol >= poFeatureDefn->GetGeomFieldCount() ) + return FALSE; + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(iGeomCol); + if (HasSpatialIndex(iGeomCol) && !poGeomFieldDefn->bHasCheckedSpatialIndexTable) + { + poGeomFieldDefn->bHasCheckedSpatialIndexTable = TRUE; + char **papszResult; + int nRowCount, nColCount; + char *pszErrMsg = NULL; + + CPLString osSQL; + + /* This will ensure that RTree support is available */ + osSQL.Printf("SELECT pkid FROM 'idx_%s_%s' WHERE xmax > 0 AND xmin < 0 AND ymax > 0 AND ymin < 0", + pszEscapedTableName, OGRSQLiteEscape(poGeomFieldDefn->GetNameRef()).c_str()); + + int rc = sqlite3_get_table( poDS->GetDB(), osSQL.c_str(), + &papszResult, &nRowCount, + &nColCount, &pszErrMsg ); + + if( rc != SQLITE_OK ) + { + CPLDebug("SQLITE", "Count not find or use idx_%s_%s layer (%s). Disabling spatial index", + pszEscapedTableName, poGeomFieldDefn->GetNameRef(), pszErrMsg); + sqlite3_free( pszErrMsg ); + poGeomFieldDefn->bHasSpatialIndex = FALSE; + } + else + { + sqlite3_free_table(papszResult); + } + } + + return poGeomFieldDefn->bHasSpatialIndex; +} + +/************************************************************************/ +/* HasFastSpatialFilter() */ +/************************************************************************/ + +int OGRSQLiteTableLayer::HasFastSpatialFilter(int iGeomCol) +{ + OGRPolygon oFakePoly; + const char* pszWKT = "POLYGON((0 0,0 1,1 1,1 0,0 0))"; + oFakePoly.importFromWkt((char**) &pszWKT); + CPLString osSpatialWhere = GetSpatialWhere(iGeomCol, &oFakePoly); + return osSpatialWhere.find("ROWID") == 0; +} + +/************************************************************************/ +/* GetSpatialWhere() */ +/************************************************************************/ + +CPLString OGRSQLiteTableLayer::GetSpatialWhere(int iGeomCol, + OGRGeometry* poFilterGeom) +{ + if( !poDS->IsSpatialiteDB() || + iGeomCol < 0 || iGeomCol >= GetLayerDefn()->GetGeomFieldCount() ) + return ""; + + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(iGeomCol); + if( poFilterGeom != NULL && CheckSpatialIndexTable(iGeomCol) ) + { + return FormatSpatialFilterFromRTree(poFilterGeom, "ROWID", + pszEscapedTableName, + OGRSQLiteEscape(poGeomFieldDefn->GetNameRef()).c_str()); + } + + if( poFilterGeom != NULL && + poDS->IsSpatialiteLoaded() && !poGeomFieldDefn->bHasSpatialIndex ) + { + return FormatSpatialFilterFromMBR(poFilterGeom, + OGRSQLiteEscapeName(poGeomFieldDefn->GetNameRef()).c_str()); + } + + return ""; +} + +/************************************************************************/ +/* BuildWhere() */ +/* */ +/* Build the WHERE statement appropriate to the current set of */ +/* criteria (spatial and attribute queries). */ +/************************************************************************/ + +void OGRSQLiteTableLayer::BuildWhere() + +{ + osWHERE = ""; + + CPLString osSpatialWHERE = GetSpatialWhere(m_iGeomFieldFilter, + m_poFilterGeom); + if (osSpatialWHERE.size() != 0) + { + osWHERE = "WHERE "; + osWHERE += osSpatialWHERE; + } + + if( osQuery.size() > 0 ) + { + if( osWHERE.size() == 0 ) + { + osWHERE = "WHERE "; + osWHERE += osQuery; + } + else + { + osWHERE += " AND ("; + osWHERE += osQuery; + osWHERE += ")"; + } + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSQLiteTableLayer::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap,OLCFastFeatureCount)) + return m_poFilterGeom == NULL || HasSpatialIndex(0); + + else if (EQUAL(pszCap,OLCFastSpatialFilter)) + return HasSpatialIndex(0); + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + { + return GetLayerDefn()->GetGeomFieldCount() >= 1 && + myGetLayerDefn()->myGetGeomFieldDefn(0)->bCachedExtentIsValid; + } + + else if( EQUAL(pszCap,OLCRandomRead) ) + return pszFIDColumn != NULL; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + { + return poDS->GetUpdate(); + } + + else if( EQUAL(pszCap,OLCDeleteFeature) ) + { + return poDS->GetUpdate() && pszFIDColumn != NULL; + } + + else if( EQUAL(pszCap,OLCCreateField) || + EQUAL(pszCap,OLCCreateGeomField) ) + return poDS->GetUpdate(); + + else if( EQUAL(pszCap,OLCDeleteField) ) + return poDS->GetUpdate(); + + else if( EQUAL(pszCap,OLCAlterFieldDefn) ) + return poDS->GetUpdate(); + + else if( EQUAL(pszCap,OLCReorderFields) ) + return poDS->GetUpdate(); + + else if( EQUAL(pszCap,OLCCurveGeometries) ) + return poDS->TestCapability(ODsCCurveGeometries); + + else + return OGRSQLiteLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRSQLiteTableLayer::GetFeatureCount( int bForce ) + +{ + if (HasLayerDefnError()) + return 0; + + if( !TestCapability(OLCFastFeatureCount) ) + return OGRSQLiteLayer::GetFeatureCount( bForce ); + + if (nFeatureCount >= 0 && m_poFilterGeom == NULL && + osQuery.size() == 0 ) + { + return nFeatureCount; + } + +/* -------------------------------------------------------------------- */ +/* Form count SQL. */ +/* -------------------------------------------------------------------- */ + const char *pszSQL; + + if (m_poFilterGeom != NULL && CheckSpatialIndexTable(m_iGeomFieldFilter) && + strlen(osQuery) == 0) + { + OGREnvelope sEnvelope; + + m_poFilterGeom->getEnvelope( &sEnvelope ); + const char* pszGeomCol = poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)->GetNameRef(); + pszSQL = CPLSPrintf("SELECT count(*) FROM 'idx_%s_%s' WHERE " + "xmax >= %.12f AND xmin <= %.12f AND ymax >= %.12f AND ymin <= %.12f", + pszEscapedTableName, OGRSQLiteEscape(pszGeomCol).c_str(), + sEnvelope.MinX - 1e-11, + sEnvelope.MaxX + 1e-11, + sEnvelope.MinY - 1e-11, + sEnvelope.MaxY + 1e-11); + } + else + { + pszSQL = CPLSPrintf( "SELECT count(*) FROM '%s' %s", + pszEscapedTableName, osWHERE.c_str() ); + } + + CPLDebug("SQLITE", "Running %s", pszSQL); + +/* -------------------------------------------------------------------- */ +/* Execute. */ +/* -------------------------------------------------------------------- */ + char **papszResult, *pszErrMsg; + int nRowCount, nColCount; + GIntBig nResult = -1; + + if( sqlite3_get_table( poDS->GetDB(), pszSQL, &papszResult, + &nRowCount, &nColCount, &pszErrMsg ) != SQLITE_OK ) + return -1; + + if( nRowCount == 1 && nColCount == 1 ) + { + nResult = CPLAtoGIntBig(papszResult[1]); + + if( m_poFilterGeom == NULL && osQuery.size() == 0 ) + { + nFeatureCount = nResult; + bStatisticsNeedsToBeFlushed = TRUE; + } + } + + sqlite3_free_table( papszResult ); + + return nResult; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + return GetExtent(0, psExtent, bForce); +} + +OGRErr OGRSQLiteTableLayer::GetExtent(int iGeomField, OGREnvelope *psExtent, int bForce) +{ + if (HasLayerDefnError()) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* If this layer has a none geometry type, then we can */ +/* reasonably assume there are not extents available. */ +/* -------------------------------------------------------------------- */ + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() || + GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetType() == wkbNone ) + { + if( iGeomField != 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } + return OGRERR_FAILURE; + } + + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(iGeomField); + if (poGeomFieldDefn->bCachedExtentIsValid) + { + memcpy(psExtent, &poGeomFieldDefn->oCachedExtent, sizeof(poGeomFieldDefn->oCachedExtent)); + return OGRERR_NONE; + } + + if (CheckSpatialIndexTable(iGeomField) && + !CSLTestBoolean(CPLGetConfigOption("OGR_SQLITE_EXACT_EXTENT", "NO"))) + { + const char* pszSQL; + + pszSQL = CPLSPrintf("SELECT MIN(xmin), MIN(ymin), MAX(xmax), MAX(ymax) FROM 'idx_%s_%s'", + pszEscapedTableName, OGRSQLiteEscape(poGeomFieldDefn->GetNameRef()).c_str()); + + CPLDebug("SQLITE", "Running %s", pszSQL); + +/* -------------------------------------------------------------------- */ +/* Execute. */ +/* -------------------------------------------------------------------- */ + char **papszResult, *pszErrMsg; + int nRowCount, nColCount; + + if( sqlite3_get_table( poDS->GetDB(), pszSQL, &papszResult, + &nRowCount, &nColCount, &pszErrMsg ) != SQLITE_OK ) + return OGRSQLiteLayer::GetExtent(psExtent, bForce); + + OGRErr eErr = OGRERR_FAILURE; + + if( nRowCount == 1 && nColCount == 4 && + papszResult[4+0] != NULL && + papszResult[4+1] != NULL && + papszResult[4+2] != NULL && + papszResult[4+3] != NULL) + { + psExtent->MinX = CPLAtof(papszResult[4+0]); + psExtent->MinY = CPLAtof(papszResult[4+1]); + psExtent->MaxX = CPLAtof(papszResult[4+2]); + psExtent->MaxY = CPLAtof(papszResult[4+3]); + eErr = OGRERR_NONE; + + if( m_poFilterGeom == NULL && osQuery.size() == 0 ) + { + poGeomFieldDefn->bCachedExtentIsValid = TRUE; + bStatisticsNeedsToBeFlushed = TRUE; + memcpy(&poGeomFieldDefn->oCachedExtent, psExtent, sizeof(poGeomFieldDefn->oCachedExtent)); + } + } + + sqlite3_free_table( papszResult ); + + if (eErr == OGRERR_NONE) + return eErr; + } + + OGRErr eErr; + if( iGeomField == 0 ) + eErr = OGRSQLiteLayer::GetExtent(psExtent, bForce); + else + eErr = OGRSQLiteLayer::GetExtent(iGeomField, psExtent, bForce); + if( eErr == OGRERR_NONE && m_poFilterGeom == NULL && osQuery.size() == 0 ) + { + poGeomFieldDefn->bCachedExtentIsValid = TRUE; + bStatisticsNeedsToBeFlushed = TRUE; + memcpy(&poGeomFieldDefn->oCachedExtent, psExtent, sizeof(poGeomFieldDefn->oCachedExtent)); + } + return eErr; +} + +/************************************************************************/ +/* OGRSQLiteFieldDefnToSQliteFieldDefn() */ +/************************************************************************/ + +CPLString OGRSQLiteFieldDefnToSQliteFieldDefn( OGRFieldDefn* poFieldDefn, + int bSQLiteDialectInternalUse ) +{ + switch( poFieldDefn->GetType() ) + { + case OFTInteger: + if (poFieldDefn->GetSubType() == OFSTBoolean) + return "INTEGER_BOOLEAN"; + else if (poFieldDefn->GetSubType() == OFSTInt16 ) + return "INTEGER_INT16"; + else + return "INTEGER"; + break; + case OFTInteger64: + return "BIGINT"; + case OFTReal: + if (bSQLiteDialectInternalUse && poFieldDefn->GetSubType() == OFSTFloat32) + return "FLOAT_FLOAT32"; + else + return "FLOAT"; + break; + case OFTBinary : return "BLOB"; break; + case OFTString : + { + if( poFieldDefn->GetWidth() > 0 ) + return CPLSPrintf("VARCHAR(%d)", poFieldDefn->GetWidth()); + else + return "VARCHAR"; + break; + } + case OFTDateTime: return "TIMESTAMP"; break; + case OFTDate : return "DATE"; break; + case OFTTime : return "TIME"; break; + case OFTIntegerList: + if (bSQLiteDialectInternalUse ) + return "INTEGERLIST"; + else + return "VARCHAR"; + break; + case OFTInteger64List: + if (bSQLiteDialectInternalUse ) + return "INTEGER64LIST"; + else + return "VARCHAR"; + break; + case OFTRealList: + if (bSQLiteDialectInternalUse ) + return "REALLIST"; + else + return "VARCHAR"; + break; + case OFTStringList: + if (bSQLiteDialectInternalUse ) + return "STRINGLIST"; + else + return "VARCHAR"; + break; + default : return "VARCHAR"; break; + } +} + +/************************************************************************/ +/* FieldDefnToSQliteFieldDefn() */ +/************************************************************************/ + +CPLString OGRSQLiteTableLayer::FieldDefnToSQliteFieldDefn( OGRFieldDefn* poFieldDefn ) +{ + CPLString osRet = OGRSQLiteFieldDefnToSQliteFieldDefn(poFieldDefn, FALSE); + if( poFieldDefn->GetType() == OFTString && + CSLFindString(papszCompressedColumns, poFieldDefn->GetNameRef()) >= 0 ) + osRet += "_deflate"; + + return osRet; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::CreateField( OGRFieldDefn *poFieldIn, + CPL_UNUSED int bApproxOK ) +{ + OGRFieldDefn oField( poFieldIn ); + + if (HasLayerDefnError()) + return OGRERR_FAILURE; + + if (!poDS->GetUpdate()) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "CreateField"); + return OGRERR_FAILURE; + } + + if( pszFIDColumn != NULL && + EQUAL( oField.GetNameRef(), pszFIDColumn ) && + oField.GetType() != OFTInteger && + oField.GetType() != OFTInteger64 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s", + oField.GetNameRef()); + return CE_Failure; + } + + ClearInsertStmt(); + + if( poDS->IsSpatialiteDB() && EQUAL( oField.GetNameRef(), "ROWID") ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "In a Spatialite DB, a 'ROWID' column that is not the integer " + "primary key can corrupt spatial index. " + "See https://www.gaia-gis.it/fossil/libspatialite/wiki?name=Shadowed+ROWID+issues"); + } + +/* -------------------------------------------------------------------- */ +/* Do we want to "launder" the column names into SQLite */ +/* friendly format? */ +/* -------------------------------------------------------------------- */ + if( bLaunderColumnNames ) + { + char *pszSafeName = poDS->LaunderName( oField.GetNameRef() ); + + oField.SetName( pszSafeName ); + CPLFree( pszSafeName ); + } + + + if( (oField.GetType() == OFTTime || oField.GetType() == OFTDate || + oField.GetType() == OFTDateTime) && + !(CSLTestBoolean( + CPLGetConfigOption("OGR_SQLITE_ENABLE_DATETIME", "YES"))) ) + { + oField.SetType(OFTString); + } + + if( !bDeferredCreation ) + { + /* ADD COLUMN only avaliable since sqlite 3.1.3 */ + if (CSLTestBoolean(CPLGetConfigOption("OGR_SQLITE_USE_ADD_COLUMN", "YES")) && + sqlite3_libversion_number() > 3 * 1000000 + 1 * 1000 + 3) + { + int rc; + char *pszErrMsg = NULL; + sqlite3 *hDB = poDS->GetDB(); + CPLString osCommand; + + CPLString osFieldType(FieldDefnToSQliteFieldDefn(&oField)); + osCommand.Printf("ALTER TABLE '%s' ADD COLUMN '%s' %s", + pszEscapedTableName, + OGRSQLiteEscape(oField.GetNameRef()).c_str(), + osFieldType.c_str()); + if( !oField.IsNullable() ) + { + osCommand += " NOT NULL"; + } + if( oField.GetDefault() != NULL && !oField.IsDefaultDriverSpecific() ) + { + osCommand += " DEFAULT "; + osCommand += oField.GetDefault(); + } + else if( !oField.IsNullable() ) + { + // This is kind of dumb, but SQLite mandates a DEFAULT value + // when adding a NOT NULL column in an ALTER TABLE ADD COLUMN + // statement, which defeats the purpose of NOT NULL, + // whereas it doesn't in CREATE TABLE + osCommand += " DEFAULT ''"; + } + + #ifdef DEBUG + CPLDebug( "OGR_SQLITE", "exec(%s)", osCommand.c_str() ); + #endif + + rc = sqlite3_exec( hDB, osCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to add field %s to table %s:\n %s", + oField.GetNameRef(), poFeatureDefn->GetName(), + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return OGRERR_FAILURE; + } + } + else + { + OGRErr eErr = AddColumnAncientMethod(oField); + if (eErr != OGRERR_NONE) + return eErr; + } + } + +/* -------------------------------------------------------------------- */ +/* Add the field to the OGRFeatureDefn. */ +/* -------------------------------------------------------------------- */ + poFeatureDefn->AddFieldDefn( &oField ); + + if( pszFIDColumn != NULL && + EQUAL( oField.GetNameRef(), pszFIDColumn ) ) + { + iFIDAsRegularColumnIndex = poFeatureDefn->GetFieldCount() - 1; + } + + if( !bDeferredCreation ) + RecomputeOrdinals(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateGeomField() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::CreateGeomField( OGRGeomFieldDefn *poGeomFieldIn, + CPL_UNUSED int bApproxOK ) +{ + OGRwkbGeometryType eType = poGeomFieldIn->GetType(); + if( eType == wkbNone ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot create geometry field of type wkbNone"); + return OGRERR_FAILURE; + } + + OGRSQLiteGeomFieldDefn *poGeomField = + new OGRSQLiteGeomFieldDefn( poGeomFieldIn->GetNameRef(), -1 ); + if( EQUAL(poGeomField->GetNameRef(), "") ) + { + if( poFeatureDefn->GetGeomFieldCount() == 0 ) + poGeomField->SetName( "GEOMETRY" ); + else + poGeomField->SetName( + CPLSPrintf("GEOMETRY%d", poFeatureDefn->GetGeomFieldCount()+1) ); + } + poGeomField->SetSpatialRef(poGeomFieldIn->GetSpatialRef()); + +/* -------------------------------------------------------------------- */ +/* Do we want to "launder" the column names into Postgres */ +/* friendly format? */ +/* -------------------------------------------------------------------- */ + if( bLaunderColumnNames ) + { + char *pszSafeName = poDS->LaunderName( poGeomField->GetNameRef() ); + + poGeomField->SetName( pszSafeName ); + CPLFree( pszSafeName ); + } + + OGRSpatialReference* poSRS = poGeomField->GetSpatialRef(); + int nSRSId = -1; + if( poSRS != NULL ) + nSRSId = poDS->FetchSRSId( poSRS ); + + poGeomField->SetType(eType); + poGeomField->SetNullable( poGeomFieldIn->IsNullable() ); + poGeomField->nSRSId = nSRSId; + if( poDS->IsSpatialiteDB() ) + poGeomField->eGeomFormat = OSGF_SpatiaLite; + else if( pszCreationGeomFormat ) + poGeomField->eGeomFormat = GetGeomFormat(pszCreationGeomFormat); + else + poGeomField->eGeomFormat = OSGF_WKB ; + +/* -------------------------------------------------------------------- */ +/* Create the new field. */ +/* -------------------------------------------------------------------- */ + if( !bDeferredCreation ) + { + if( RunAddGeometryColumn(poGeomField, TRUE) != OGRERR_NONE ) + { + delete poGeomField; + + return OGRERR_FAILURE; + } + } + + poFeatureDefn->AddGeomFieldDefn( poGeomField, FALSE ); + + if( !bDeferredCreation ) + RecomputeOrdinals(); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* RunAddGeometryColumn() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::RunAddGeometryColumn( OGRSQLiteGeomFieldDefn *poGeomFieldDefn, + int bAddColumnsForNonSpatialite ) +{ + OGRwkbGeometryType eType = poGeomFieldDefn->GetType(); + int nCoordDim; + const char* pszGeomCol = poGeomFieldDefn->GetNameRef(); + int nSRSId = poGeomFieldDefn->nSRSId; + CPLString osCommand; + char* pszErrMsg = NULL; + + if( eType == wkbFlatten(eType) ) + nCoordDim = 2; + else + nCoordDim = 3; + + if( bAddColumnsForNonSpatialite && !poDS->IsSpatialiteDB() ) + { + osCommand = CPLSPrintf("ALTER TABLE '%s' ADD COLUMN ", + pszEscapedTableName ); + if( poGeomFieldDefn->eGeomFormat == OSGF_WKT ) + { + osCommand += CPLSPrintf(" '%s' VARCHAR", + OGRSQLiteEscape(poGeomFieldDefn->GetNameRef()).c_str() ); + } + else + { + osCommand += CPLSPrintf(" '%s' BLOB", + OGRSQLiteEscape(poGeomFieldDefn->GetNameRef()).c_str() ); + } + if( !poGeomFieldDefn->IsNullable() ) + osCommand += " NOT NULL DEFAULT ''"; + +#ifdef DEBUG + CPLDebug( "OGR_SQLITE", "exec(%s)", osCommand.c_str() ); +#endif + + int rc = sqlite3_exec( poDS->GetDB(), osCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to geometry field:\n%s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return OGRERR_FAILURE; + } + } + + if ( poDS->IsSpatialiteDB() ) + { + /* + / SpatiaLite full support: calling AddGeometryColumn() + / + / IMPORTANT NOTICE: on SpatiaLite any attempt aimed + / to directly INSERT a row into GEOMETRY_COLUMNS + / [by-passing AddGeometryColumn() as absolutely required] + / will severely [and irremediably] corrupt the DB !!! + */ + const char *pszType = OGRToOGCGeomType(eType); + if (pszType[0] == '\0') + pszType = "GEOMETRY"; + + /* + / SpatiaLite v.2.4.0 (or any subsequent) is required + / to support 2.5D: if an obsolete version of the library + / is found we'll unconditionally activate 2D casting mode + */ + int iSpatialiteVersion = poDS->GetSpatialiteVersionNumber(); + if ( iSpatialiteVersion < 24 && nCoordDim == 3 ) + { + CPLDebug("SQLITE", "Spatialite < 2.4.0 --> 2.5D geometry not supported. Casting to 2D"); + nCoordDim = 2; + } + + osCommand.Printf( "SELECT AddGeometryColumn(" + "'%s', '%s', %d, '%s', %d", + pszEscapedTableName, + OGRSQLiteEscape(pszGeomCol).c_str(), nSRSId, + pszType, nCoordDim ); + if( iSpatialiteVersion >= 30 && !poGeomFieldDefn->IsNullable() ) + osCommand += ", 1"; + osCommand += ")"; + } + else + { + const char* pszGeomFormat = + (poGeomFieldDefn->eGeomFormat == OSGF_WKT ) ? "WKT" : + (poGeomFieldDefn->eGeomFormat == OSGF_WKB ) ? "WKB" : + (poGeomFieldDefn->eGeomFormat == OSGF_FGF ) ? "FGF" : + "Spatialite"; + if( nSRSId > 0 ) + { + osCommand.Printf( + "INSERT INTO geometry_columns " + "(f_table_name, f_geometry_column, geometry_format, " + "geometry_type, coord_dimension, srid) VALUES " + "('%s','%s','%s', %d, %d, %d)", + pszEscapedTableName, + OGRSQLiteEscape(pszGeomCol).c_str(), pszGeomFormat, + (int) wkbFlatten(eType), nCoordDim, nSRSId ); + } + else + { + osCommand.Printf( + "INSERT INTO geometry_columns " + "(f_table_name, f_geometry_column, geometry_format, " + "geometry_type, coord_dimension) VALUES " + "('%s','%s','%s', %d, %d)", + pszEscapedTableName, + OGRSQLiteEscape(pszGeomCol).c_str(), pszGeomFormat, + (int) wkbFlatten(eType), nCoordDim ); + } + } + +#ifdef DEBUG + CPLDebug( "OGR_SQLITE", "exec(%s)", osCommand.c_str() ); +#endif + + int rc = sqlite3_exec( poDS->GetDB(), osCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to geometry field:\n%s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* InitFieldListForRecrerate() */ +/************************************************************************/ + +void OGRSQLiteTableLayer::InitFieldListForRecrerate(char* & pszNewFieldList, + char* & pszFieldListForSelect, + int nExtraSpace) +{ + int iField, nFieldListLen = 100 + 2 * nExtraSpace; + + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(iField); + nFieldListLen += + 2 * strlen(poFieldDefn->GetNameRef()) + 70; + if( poFieldDefn->GetDefault() != NULL ) + nFieldListLen += 10 + strlen( poFieldDefn->GetDefault() ); + } + + nFieldListLen += 50 + (pszFIDColumn ? 2 * strlen(pszFIDColumn) : strlen("OGC_FID")); + for( iField = 0; iField < poFeatureDefn->GetGeomFieldCount(); iField++ ) + { + nFieldListLen += 70 + 2 * strlen(poFeatureDefn->GetGeomFieldDefn(iField)->GetNameRef()); + } + + pszFieldListForSelect = (char *) CPLCalloc(1,nFieldListLen); + pszNewFieldList = (char *) CPLCalloc(1,nFieldListLen); + +/* -------------------------------------------------------------------- */ +/* Build list of old fields, and the list of new fields. */ +/* -------------------------------------------------------------------- */ + sprintf( pszFieldListForSelect, "\"%s\"", pszFIDColumn ? OGRSQLiteEscapeName(pszFIDColumn).c_str() : "OGC_FID" ); + sprintf( pszNewFieldList, "\"%s\" INTEGER PRIMARY KEY",pszFIDColumn ? OGRSQLiteEscapeName(pszFIDColumn).c_str() : "OGC_FID" ); + + for( iField = 0; iField < poFeatureDefn->GetGeomFieldCount(); iField++ ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(iField); + strcat( pszFieldListForSelect, "," ); + strcat( pszNewFieldList, "," ); + + strcat( pszFieldListForSelect, "\""); + strcat( pszFieldListForSelect, OGRSQLiteEscapeName(poGeomFieldDefn->GetNameRef()) ); + strcat( pszFieldListForSelect, "\""); + + strcat( pszNewFieldList, "\""); + strcat( pszNewFieldList, OGRSQLiteEscapeName(poGeomFieldDefn->GetNameRef()) ); + strcat( pszNewFieldList, "\""); + + if ( poGeomFieldDefn->eGeomFormat == OSGF_WKT ) + strcat( pszNewFieldList, " VARCHAR" ); + else + strcat( pszNewFieldList, " BLOB" ); + if( !poGeomFieldDefn->IsNullable() ) + strcat( pszNewFieldList, " NOT NULL" ); + } +} + +/************************************************************************/ +/* AddColumnDef() */ +/************************************************************************/ + +void OGRSQLiteTableLayer::AddColumnDef(char* pszNewFieldList, + OGRFieldDefn* poFldDefn) +{ + sprintf( pszNewFieldList+strlen(pszNewFieldList), + ", '%s' %s", OGRSQLiteEscape(poFldDefn->GetNameRef()).c_str(), + FieldDefnToSQliteFieldDefn(poFldDefn).c_str() ); + if( !poFldDefn->IsNullable() ) + sprintf( pszNewFieldList+strlen(pszNewFieldList), " NOT NULL" ); + if( poFldDefn->GetDefault() != NULL && !poFldDefn->IsDefaultDriverSpecific() ) + { + sprintf( pszNewFieldList+strlen(pszNewFieldList), " DEFAULT %s", + poFldDefn->GetDefault() ); + } +} + +/************************************************************************/ +/* AddColumnAncientMethod() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::AddColumnAncientMethod( OGRFieldDefn& oField) +{ + +/* -------------------------------------------------------------------- */ +/* How much space do we need for the list of fields. */ +/* -------------------------------------------------------------------- */ + int iField; + char *pszOldFieldList, *pszNewFieldList; + + InitFieldListForRecrerate(pszNewFieldList, pszOldFieldList, + strlen( oField.GetNameRef() )); + +/* -------------------------------------------------------------------- */ +/* Build list of old fields, and the list of new fields. */ +/* -------------------------------------------------------------------- */ + + int iNextOrdinal = 3; /* _rowid_ is 1, OGC_FID is 2 */ + + iNextOrdinal += poFeatureDefn->GetGeomFieldCount(); + + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(iField); + + // we already added OGC_FID so don't do it again + if( EQUAL(poFldDefn->GetNameRef(),pszFIDColumn ? pszFIDColumn : "OGC_FID") ) + continue; + + sprintf( pszOldFieldList+strlen(pszOldFieldList), + ", \"%s\"", OGRSQLiteEscapeName(poFldDefn->GetNameRef()).c_str() ); + + AddColumnDef(pszNewFieldList, poFldDefn); + + iNextOrdinal++; + } + +/* -------------------------------------------------------------------- */ +/* Add the new field. */ +/* -------------------------------------------------------------------- */ + AddColumnDef(pszNewFieldList, &oField); + +/* ==================================================================== */ +/* Backup, destroy, recreate and repopulate the table. SQLite */ +/* has no ALTER TABLE so we have to do all this to add a */ +/* column. */ +/* ==================================================================== */ + +/* -------------------------------------------------------------------- */ +/* Do this all in a transaction. */ +/* -------------------------------------------------------------------- */ + poDS->SoftStartTransaction(); + +/* -------------------------------------------------------------------- */ +/* Save existing related triggers and index */ +/* -------------------------------------------------------------------- */ + int rc; + char *pszErrMsg = NULL; + sqlite3 *hDB = poDS->GetDB(); + CPLString osSQL; + + osSQL.Printf( "SELECT sql FROM sqlite_master WHERE type IN ('trigger','index') AND tbl_name='%s'", + pszEscapedTableName ); + + int nRowTriggerIndexCount, nColTriggerIndexCount; + char **papszTriggerIndexResult = NULL; + rc = sqlite3_get_table( hDB, osSQL.c_str(), &papszTriggerIndexResult, + &nRowTriggerIndexCount, &nColTriggerIndexCount, &pszErrMsg ); + +/* -------------------------------------------------------------------- */ +/* Make a backup of the table. */ +/* -------------------------------------------------------------------- */ + + if( rc == SQLITE_OK ) + rc = sqlite3_exec( hDB, + CPLSPrintf( "CREATE TEMPORARY TABLE t1_back(%s)", + pszOldFieldList ), + NULL, NULL, &pszErrMsg ); + + if( rc == SQLITE_OK ) + rc = sqlite3_exec( hDB, + CPLSPrintf( "INSERT INTO t1_back SELECT %s FROM '%s'", + pszOldFieldList, + pszEscapedTableName ), + NULL, NULL, &pszErrMsg ); + + +/* -------------------------------------------------------------------- */ +/* Drop the original table, and recreate with new field. */ +/* -------------------------------------------------------------------- */ + if( rc == SQLITE_OK ) + rc = sqlite3_exec( hDB, + CPLSPrintf( "DROP TABLE '%s'", + pszEscapedTableName ), + NULL, NULL, &pszErrMsg ); + + if( rc == SQLITE_OK ) + { + const char *pszCmd = + CPLSPrintf( "CREATE TABLE '%s' (%s)", + pszEscapedTableName, + pszNewFieldList ); + rc = sqlite3_exec( hDB, pszCmd, + NULL, NULL, &pszErrMsg ); + + CPLDebug( "OGR_SQLITE", "exec(%s)", pszCmd ); + } + +/* -------------------------------------------------------------------- */ +/* Copy backup field values into new table. */ +/* -------------------------------------------------------------------- */ + + if( rc == SQLITE_OK ) + rc = sqlite3_exec( hDB, + CPLSPrintf( "INSERT INTO '%s' SELECT %s, NULL FROM t1_back", + pszEscapedTableName, + pszOldFieldList ), + NULL, NULL, &pszErrMsg ); + + CPLFree( pszOldFieldList ); + CPLFree( pszNewFieldList ); + +/* -------------------------------------------------------------------- */ +/* Cleanup backup table. */ +/* -------------------------------------------------------------------- */ + + if( rc == SQLITE_OK ) + rc = sqlite3_exec( hDB, + CPLSPrintf( "DROP TABLE t1_back" ), + NULL, NULL, &pszErrMsg ); + +/* -------------------------------------------------------------------- */ +/* Recreate existing related tables, triggers and index */ +/* -------------------------------------------------------------------- */ + + if( rc == SQLITE_OK ) + { + int i; + + for(i = 1; i <= nRowTriggerIndexCount && nColTriggerIndexCount == 1 && rc == SQLITE_OK; i++) + { + if (papszTriggerIndexResult[i] != NULL && papszTriggerIndexResult[i][0] != '\0') + rc = sqlite3_exec( hDB, + papszTriggerIndexResult[i], + NULL, NULL, &pszErrMsg ); + } + } + +/* -------------------------------------------------------------------- */ +/* COMMIT on success or ROLLBACK on failuire. */ +/* -------------------------------------------------------------------- */ + + sqlite3_free_table( papszTriggerIndexResult ); + + if( rc == SQLITE_OK ) + { + poDS->SoftCommitTransaction(); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to add field %s to table %s:\n %s", + oField.GetNameRef(), poFeatureDefn->GetName(), + pszErrMsg ); + sqlite3_free( pszErrMsg ); + + poDS->SoftRollbackTransaction(); + + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* RecreateTable() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::RecreateTable(const char* pszFieldListForSelect, + const char* pszNewFieldList, + const char* pszGenericErrorMessage) +{ +/* -------------------------------------------------------------------- */ +/* Do this all in a transaction. */ +/* -------------------------------------------------------------------- */ + poDS->SoftStartTransaction(); + +/* -------------------------------------------------------------------- */ +/* Save existing related triggers and index */ +/* -------------------------------------------------------------------- */ + int rc; + char *pszErrMsg = NULL; + sqlite3 *hDB = poDS->GetDB(); + CPLString osSQL; + + osSQL.Printf( "SELECT sql FROM sqlite_master WHERE type IN ('trigger','index') AND tbl_name='%s'", + pszEscapedTableName ); + + int nRowTriggerIndexCount, nColTriggerIndexCount; + char **papszTriggerIndexResult = NULL; + rc = sqlite3_get_table( hDB, osSQL.c_str(), &papszTriggerIndexResult, + &nRowTriggerIndexCount, &nColTriggerIndexCount, &pszErrMsg ); + +/* -------------------------------------------------------------------- */ +/* Make a backup of the table. */ +/* -------------------------------------------------------------------- */ + + if( rc == SQLITE_OK ) + rc = sqlite3_exec( hDB, + CPLSPrintf( "CREATE TABLE t1_back(%s)", + pszNewFieldList ), + NULL, NULL, &pszErrMsg ); + + if( rc == SQLITE_OK ) + rc = sqlite3_exec( hDB, + CPLSPrintf( "INSERT INTO t1_back SELECT %s FROM '%s'", + pszFieldListForSelect, + pszEscapedTableName ), + NULL, NULL, &pszErrMsg ); + + +/* -------------------------------------------------------------------- */ +/* Drop the original table */ +/* -------------------------------------------------------------------- */ + if( rc == SQLITE_OK ) + rc = sqlite3_exec( hDB, + CPLSPrintf( "DROP TABLE '%s'", + pszEscapedTableName ), + NULL, NULL, &pszErrMsg ); + +/* -------------------------------------------------------------------- */ +/* Rename backup table as new table */ +/* -------------------------------------------------------------------- */ + if( rc == SQLITE_OK ) + { + const char *pszCmd = + CPLSPrintf( "ALTER TABLE t1_back RENAME TO '%s'", + pszEscapedTableName); + rc = sqlite3_exec( hDB, pszCmd, + NULL, NULL, &pszErrMsg ); + } + +/* -------------------------------------------------------------------- */ +/* Recreate existing related tables, triggers and index */ +/* -------------------------------------------------------------------- */ + + if( rc == SQLITE_OK ) + { + int i; + + for(i = 1; i <= nRowTriggerIndexCount && nColTriggerIndexCount == 1 && rc == SQLITE_OK; i++) + { + if (papszTriggerIndexResult[i] != NULL && papszTriggerIndexResult[i][0] != '\0') + rc = sqlite3_exec( hDB, + papszTriggerIndexResult[i], + NULL, NULL, &pszErrMsg ); + } + } + +/* -------------------------------------------------------------------- */ +/* COMMIT on success or ROLLBACK on failuire. */ +/* -------------------------------------------------------------------- */ + + sqlite3_free_table( papszTriggerIndexResult ); + + if( rc == SQLITE_OK ) + { + poDS->SoftCommitTransaction(); + + return OGRERR_NONE; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s:\n %s", + pszGenericErrorMessage, + pszErrMsg ); + sqlite3_free( pszErrMsg ); + + poDS->SoftRollbackTransaction(); + + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* DeleteField() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::DeleteField( int iFieldToDelete ) +{ + if (HasLayerDefnError()) + return OGRERR_FAILURE; + + if (!poDS->GetUpdate()) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "DeleteField"); + return OGRERR_FAILURE; + } + + if (iFieldToDelete < 0 || iFieldToDelete >= poFeatureDefn->GetFieldCount()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + + ResetReading(); + +/* -------------------------------------------------------------------- */ +/* Build list of old fields, and the list of new fields. */ +/* -------------------------------------------------------------------- */ + int iField; + char *pszNewFieldList, *pszFieldListForSelect; + InitFieldListForRecrerate(pszNewFieldList, pszFieldListForSelect); + + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(iField); + + if (iField == iFieldToDelete) + continue; + + sprintf( pszFieldListForSelect+strlen(pszFieldListForSelect), + ", \"%s\"", OGRSQLiteEscapeName(poFldDefn->GetNameRef()).c_str() ); + + AddColumnDef(pszNewFieldList, poFldDefn); + } + +/* -------------------------------------------------------------------- */ +/* Recreate table. */ +/* -------------------------------------------------------------------- */ + CPLString osErrorMsg; + osErrorMsg.Printf("Failed to remove field %s from table %s", + poFeatureDefn->GetFieldDefn(iFieldToDelete)->GetNameRef(), + poFeatureDefn->GetName()); + + OGRErr eErr = RecreateTable(pszFieldListForSelect, + pszNewFieldList, + osErrorMsg.c_str()); + + CPLFree( pszFieldListForSelect ); + CPLFree( pszNewFieldList ); + + if (eErr != OGRERR_NONE) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Finish */ +/* -------------------------------------------------------------------- */ + eErr = poFeatureDefn->DeleteFieldDefn( iFieldToDelete ); + + RecomputeOrdinals(); + + return eErr; +} + +/************************************************************************/ +/* AlterFieldDefn() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::AlterFieldDefn( int iFieldToAlter, OGRFieldDefn* poNewFieldDefn, int nFlags ) +{ + if (HasLayerDefnError()) + return OGRERR_FAILURE; + + if (!poDS->GetUpdate()) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "AlterFieldDefn"); + return OGRERR_FAILURE; + } + + if (iFieldToAlter < 0 || iFieldToAlter >= poFeatureDefn->GetFieldCount()) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Invalid field index"); + return OGRERR_FAILURE; + } + + ClearInsertStmt(); + ResetReading(); + +/* -------------------------------------------------------------------- */ +/* Build list of old fields, and the list of new fields. */ +/* -------------------------------------------------------------------- */ + int iField; + char *pszNewFieldList, *pszFieldListForSelect; + InitFieldListForRecrerate(pszNewFieldList, pszFieldListForSelect, + strlen(poNewFieldDefn->GetNameRef()) + + 50 + + (poNewFieldDefn->GetDefault() ? strlen(poNewFieldDefn->GetDefault()) : 0) + ); + + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(iField); + + sprintf( pszFieldListForSelect+strlen(pszFieldListForSelect), + ", \"%s\"", OGRSQLiteEscapeName(poFldDefn->GetNameRef()).c_str() ); + + if (iField == iFieldToAlter) + { + OGRFieldDefn oTmpFieldDefn(poFldDefn); + if( (nFlags & ALTER_NAME_FLAG) ) + oTmpFieldDefn.SetName(poNewFieldDefn->GetNameRef()); + if( (nFlags & ALTER_TYPE_FLAG) ) + oTmpFieldDefn.SetType(poNewFieldDefn->GetType()); + if (nFlags & ALTER_WIDTH_PRECISION_FLAG) + { + oTmpFieldDefn.SetWidth(poNewFieldDefn->GetWidth()); + oTmpFieldDefn.SetPrecision(poNewFieldDefn->GetPrecision()); + } + if( (nFlags & ALTER_NULLABLE_FLAG) ) + { + oTmpFieldDefn.SetNullable(poNewFieldDefn->IsNullable()); + } + if( (nFlags & ALTER_DEFAULT_FLAG) ) + { + oTmpFieldDefn.SetDefault(poNewFieldDefn->GetDefault()); + } + + sprintf( pszNewFieldList+strlen(pszNewFieldList), + ", '%s' %s", + OGRSQLiteEscape(oTmpFieldDefn.GetNameRef()).c_str(), + FieldDefnToSQliteFieldDefn(&oTmpFieldDefn).c_str() ); + if ( (nFlags & ALTER_NAME_FLAG) && + oTmpFieldDefn.GetType() == OFTString && + CSLFindString(papszCompressedColumns, poFldDefn->GetNameRef()) >= 0 ) + { + sprintf( pszNewFieldList+strlen(pszNewFieldList), "_deflate"); + } + if( !oTmpFieldDefn.IsNullable() ) + sprintf( pszNewFieldList+strlen(pszNewFieldList), " NOT NULL" ); + if( oTmpFieldDefn.GetDefault() ) + { + sprintf( pszNewFieldList+strlen(pszNewFieldList), " DEFAULT %s", + oTmpFieldDefn.GetDefault()); + } + } + else + { + AddColumnDef(pszNewFieldList, poFldDefn); + } + } + +/* -------------------------------------------------------------------- */ +/* Recreate table. */ +/* -------------------------------------------------------------------- */ + CPLString osErrorMsg; + osErrorMsg.Printf("Failed to alter field %s from table %s", + poFeatureDefn->GetFieldDefn(iFieldToAlter)->GetNameRef(), + poFeatureDefn->GetName()); + + OGRErr eErr = RecreateTable(pszFieldListForSelect, + pszNewFieldList, + osErrorMsg.c_str()); + + CPLFree( pszFieldListForSelect ); + CPLFree( pszNewFieldList ); + + if (eErr != OGRERR_NONE) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Finish */ +/* -------------------------------------------------------------------- */ + + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(iFieldToAlter); + + if (nFlags & ALTER_TYPE_FLAG) + { + int iIdx; + if( poNewFieldDefn->GetType() != OFTString && + (iIdx = CSLFindString(papszCompressedColumns, + poFieldDefn->GetNameRef())) >= 0 ) + { + papszCompressedColumns = CSLRemoveStrings(papszCompressedColumns, + iIdx, 1, NULL); + } + poFieldDefn->SetType(poNewFieldDefn->GetType()); + } + if (nFlags & ALTER_NAME_FLAG) + { + int iIdx; + if( (iIdx = CSLFindString(papszCompressedColumns, + poFieldDefn->GetNameRef())) >= 0 ) + { + CPLFree(papszCompressedColumns[iIdx]); + papszCompressedColumns[iIdx] = + CPLStrdup(poNewFieldDefn->GetNameRef()); + } + poFieldDefn->SetName(poNewFieldDefn->GetNameRef()); + } + if (nFlags & ALTER_WIDTH_PRECISION_FLAG) + { + poFieldDefn->SetWidth(poNewFieldDefn->GetWidth()); + poFieldDefn->SetPrecision(poNewFieldDefn->GetPrecision()); + } + if (nFlags & ALTER_NULLABLE_FLAG) + poFieldDefn->SetNullable(poNewFieldDefn->IsNullable()); + if (nFlags & ALTER_DEFAULT_FLAG) + poFieldDefn->SetDefault(poNewFieldDefn->GetDefault()); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ReorderFields() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::ReorderFields( int* panMap ) +{ + if (HasLayerDefnError()) + return OGRERR_FAILURE; + + if (!poDS->GetUpdate()) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "ReorderFields"); + return OGRERR_FAILURE; + } + + if (poFeatureDefn->GetFieldCount() == 0) + return OGRERR_NONE; + + OGRErr eErr = OGRCheckPermutation(panMap, poFeatureDefn->GetFieldCount()); + if (eErr != OGRERR_NONE) + return eErr; + + ClearInsertStmt(); + ResetReading(); + +/* -------------------------------------------------------------------- */ +/* Build list of old fields, and the list of new fields. */ +/* -------------------------------------------------------------------- */ + int iField; + char *pszNewFieldList, *pszFieldListForSelect; + InitFieldListForRecrerate(pszNewFieldList, pszFieldListForSelect); + + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + OGRFieldDefn *poFldDefn = poFeatureDefn->GetFieldDefn(panMap[iField]); + + sprintf( pszFieldListForSelect+strlen(pszFieldListForSelect), + ", \"%s\"", OGRSQLiteEscapeName(poFldDefn->GetNameRef()).c_str() ); + + AddColumnDef(pszNewFieldList, poFldDefn); + } + +/* -------------------------------------------------------------------- */ +/* Recreate table. */ +/* -------------------------------------------------------------------- */ + CPLString osErrorMsg; + osErrorMsg.Printf("Failed to reorder fields from table %s", + poFeatureDefn->GetName()); + + eErr = RecreateTable(pszFieldListForSelect, + pszNewFieldList, + osErrorMsg.c_str()); + + CPLFree( pszFieldListForSelect ); + CPLFree( pszNewFieldList ); + + if (eErr != OGRERR_NONE) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Finish */ +/* -------------------------------------------------------------------- */ + + eErr = poFeatureDefn->ReorderFieldDefns( panMap ); + + RecomputeOrdinals(); + + return eErr; +} + +/************************************************************************/ +/* BindValues() */ +/************************************************************************/ + +/* the bBindNullValues is set to TRUE by SetFeature() for UPDATE statements, */ +/* and to FALSE by CreateFeature() for INSERT statements; */ + +OGRErr OGRSQLiteTableLayer::BindValues( OGRFeature *poFeature, + sqlite3_stmt* hStmt, + int bBindNullValues ) +{ + int rc; + sqlite3 *hDB = poDS->GetDB(); + +/* -------------------------------------------------------------------- */ +/* Bind the geometry */ +/* -------------------------------------------------------------------- */ + int nBindField = 1; + int iField; + int nFieldCount = poFeatureDefn->GetGeomFieldCount(); + for( iField = 0; iField < nFieldCount; iField++ ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(iField); + OGRSQLiteGeomFormat eGeomFormat = poGeomFieldDefn->eGeomFormat; + if( eGeomFormat == OSGF_FGF ) + continue; + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(iField); + if ( poGeom != NULL ) + { + if ( eGeomFormat == OSGF_WKT ) + { + char *pszWKT = NULL; + poGeom->exportToWkt( &pszWKT ); + rc = sqlite3_bind_text( hStmt, nBindField++, pszWKT, -1, CPLFree ); + } + else if( eGeomFormat == OSGF_WKB ) + { + int nWKBLen = poGeom->WkbSize(); + GByte *pabyWKB = (GByte *) CPLMalloc(nWKBLen + 1); + + poGeom->exportToWkb( wkbNDR, pabyWKB ); + rc = sqlite3_bind_blob( hStmt, nBindField++, pabyWKB, nWKBLen, CPLFree ); + } + else if ( eGeomFormat == OSGF_SpatiaLite ) + { + int nBLOBLen; + GByte *pabySLBLOB; + + int nSRSId = poGeomFieldDefn->nSRSId; + int bHasM = poGeomFieldDefn->bHasM; + ExportSpatiaLiteGeometry( poGeom, nSRSId, wkbNDR, bHasM, + bSpatialite2D, bUseComprGeom, &pabySLBLOB, &nBLOBLen ); + rc = sqlite3_bind_blob( hStmt, nBindField++, pabySLBLOB, + nBLOBLen, CPLFree ); + } + else + { + rc = SQLITE_OK; + CPLAssert(0); + } + } + else + { + if (bBindNullValues) + rc = sqlite3_bind_null( hStmt, nBindField++ ); + else + rc = SQLITE_OK; + } + + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "sqlite3_bind_blob/text() failed:\n %s", + sqlite3_errmsg(hDB) ); + return OGRERR_FAILURE; + } + } + +/* -------------------------------------------------------------------- */ +/* Bind field values. */ +/* -------------------------------------------------------------------- */ + nFieldCount = poFeatureDefn->GetFieldCount(); + for( iField = 0; iField < nFieldCount; iField++ ) + { + const char *pszRawValue; + if( iField == iFIDAsRegularColumnIndex ) + continue; + + if( !poFeature->IsFieldSet( iField ) ) + { + if (bBindNullValues) + rc = sqlite3_bind_null( hStmt, nBindField++ ); + else + rc = SQLITE_OK; + } + else + { + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(iField); + switch( poFieldDefn->GetType() ) + { + case OFTInteger: + { + int nFieldVal = poFeature->GetFieldAsInteger( iField ); + rc = sqlite3_bind_int(hStmt, nBindField++, nFieldVal); + break; + } + + case OFTInteger64: + { + GIntBig nFieldVal = poFeature->GetFieldAsInteger64( iField ); + rc = sqlite3_bind_int64(hStmt, nBindField++, nFieldVal); + break; + } + + case OFTReal: + { + double dfFieldVal = poFeature->GetFieldAsDouble( iField ); + rc = sqlite3_bind_double(hStmt, nBindField++, dfFieldVal); + break; + } + + case OFTBinary: + { + int nDataLength = 0; + GByte* pabyData = + poFeature->GetFieldAsBinary( iField, &nDataLength ); + rc = sqlite3_bind_blob(hStmt, nBindField++, + pabyData, nDataLength, SQLITE_TRANSIENT); + break; + } + + case OFTDateTime: + { + char* pszStr = OGRGetXMLDateTime(poFeature->GetRawFieldRef(iField)); + rc = sqlite3_bind_text(hStmt, nBindField++, + pszStr, -1, SQLITE_TRANSIENT); + CPLFree(pszStr); + break; + } + + case OFTDate: + { + int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZ; + poFeature->GetFieldAsDateTime(iField, &nYear, &nMonth, &nDay, + &nHour, &nMinute, &nSecond, &nTZ); + char szBuffer[64]; + sprintf(szBuffer, "%04d-%02d-%02d", nYear, nMonth, nDay); + rc = sqlite3_bind_text(hStmt, nBindField++, + szBuffer, -1, SQLITE_TRANSIENT); + break; + } + + case OFTTime: + { + int nYear, nMonth, nDay, nHour, nMinute, nTZ; + float fSecond; + poFeature->GetFieldAsDateTime(iField, &nYear, &nMonth, &nDay, + &nHour, &nMinute, &fSecond, &nTZ ); + char szBuffer[64]; + if( OGR_GET_MS(fSecond) != 0 ) + sprintf(szBuffer, "%02d:%02d:%06.3f", nHour, nMinute, fSecond); + else + sprintf(szBuffer, "%02d:%02d:%02d", nHour, nMinute, (int)fSecond); + rc = sqlite3_bind_text(hStmt, nBindField++, + szBuffer, -1, SQLITE_TRANSIENT); + break; + } + + case OFTStringList: + { + char** papszValues = poFeature->GetFieldAsStringList( iField ); + CPLString osValue; + osValue += CPLSPrintf("(%d:", CSLCount(papszValues)); + for(int i=0; papszValues[i] != NULL; i++) + { + if( i != 0 ) + osValue += ","; + osValue += papszValues[i]; + } + osValue += ")"; + rc = sqlite3_bind_text(hStmt, nBindField++, + osValue.c_str(), -1, SQLITE_TRANSIENT); + break; + } + + default: + { + pszRawValue = poFeature->GetFieldAsString( iField ); + if( CSLFindString(papszCompressedColumns, + poFeatureDefn->GetFieldDefn(iField)->GetNameRef()) >= 0 ) + { + size_t nBytesOut = 0; + void* pOut = CPLZLibDeflate( pszRawValue, + strlen(pszRawValue), -1, + NULL, 0, + &nBytesOut ); + if( pOut != NULL ) + { + rc = sqlite3_bind_blob(hStmt, nBindField++, + pOut, + nBytesOut, + CPLFree); + } + else + rc = SQLITE_ERROR; + } + else + { + rc = sqlite3_bind_text(hStmt, nBindField++, + pszRawValue, -1, SQLITE_TRANSIENT); + } + break; + } + } + } + + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "sqlite3_bind_() for column %s failed:\n %s", + poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), + sqlite3_errmsg(hDB) ); + return OGRERR_FAILURE; + } + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::ISetFeature( OGRFeature *poFeature ) + +{ + if (HasLayerDefnError()) + return OGRERR_FAILURE; + + if( pszFIDColumn == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SetFeature() without any FID column." ); + return OGRERR_FAILURE; + } + + if( poFeature->GetFID() == OGRNullFID ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SetFeature() with unset FID fails." ); + return OGRERR_FAILURE; + } + + if (!poDS->GetUpdate()) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "SetFeature"); + return OGRERR_FAILURE; + } + + /* In case the FID column has also been created as a regular field */ + if( iFIDAsRegularColumnIndex >= 0 ) + { + if( !poFeature->IsFieldSet( iFIDAsRegularColumnIndex ) || + poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) != poFeature->GetFID() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Inconsistent values of FID and field of same name"); + return CE_Failure; + } + } + + if( bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + + sqlite3 *hDB = poDS->GetDB(); + CPLString osCommand; + int bNeedComma = FALSE; + + ResetReading(); + +/* -------------------------------------------------------------------- */ +/* Form the UPDATE command. */ +/* -------------------------------------------------------------------- */ + osCommand += CPLSPrintf( "UPDATE '%s' SET ", pszEscapedTableName ); + +/* -------------------------------------------------------------------- */ +/* Add geometry field name. */ +/* -------------------------------------------------------------------- */ + int iField; + int nFieldCount = poFeatureDefn->GetGeomFieldCount(); + for( iField = 0; iField < nFieldCount; iField++ ) + { + OGRSQLiteGeomFormat eGeomFormat = + poFeatureDefn->myGetGeomFieldDefn(iField)->eGeomFormat; + if( eGeomFormat == OSGF_FGF ) + continue; + + osCommand += "\""; + osCommand += OGRSQLiteEscapeName( poFeatureDefn->GetGeomFieldDefn(iField)->GetNameRef()); + osCommand += "\" = ?"; + + bNeedComma = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Add field names. */ +/* -------------------------------------------------------------------- */ + nFieldCount = poFeatureDefn->GetFieldCount(); + for( iField = 0; iField < nFieldCount; iField++ ) + { + if( iField == iFIDAsRegularColumnIndex ) + continue; + if( bNeedComma ) + osCommand += ","; + + osCommand += "\""; + osCommand += OGRSQLiteEscapeName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef()); + osCommand += "\" = ?"; + + bNeedComma = TRUE; + } + + if (!bNeedComma) + return OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* Merge final command. */ +/* -------------------------------------------------------------------- */ + osCommand += " WHERE \""; + osCommand += OGRSQLiteEscapeName(pszFIDColumn); + osCommand += CPLSPrintf("\" = " CPL_FRMT_GIB, poFeature->GetFID()); + +/* -------------------------------------------------------------------- */ +/* Prepare the statement. */ +/* -------------------------------------------------------------------- */ + int rc; + sqlite3_stmt *hUpdateStmt; + +#ifdef DEBUG + CPLDebug( "OGR_SQLITE", "prepare(%s)", osCommand.c_str() ); +#endif + + rc = sqlite3_prepare( hDB, osCommand, -1, &hUpdateStmt, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In SetFeature(): sqlite3_prepare(%s):\n %s", + osCommand.c_str(), sqlite3_errmsg(hDB) ); + + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Bind values. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr = BindValues( poFeature, hUpdateStmt, TRUE ); + if (eErr != OGRERR_NONE) + { + sqlite3_finalize( hUpdateStmt ); + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Execute the update. */ +/* -------------------------------------------------------------------- */ + rc = sqlite3_step( hUpdateStmt ); + + if( rc != SQLITE_OK && rc != SQLITE_DONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "sqlite3_step() failed:\n %s", + sqlite3_errmsg(hDB) ); + + sqlite3_finalize( hUpdateStmt ); + return OGRERR_FAILURE; + } + + sqlite3_finalize( hUpdateStmt ); + + eErr = (sqlite3_changes(hDB) > 0) ? OGRERR_NONE : OGRERR_NON_EXISTING_FEATURE; + if( eErr == OGRERR_NONE ) + { + nFieldCount = poFeatureDefn->GetGeomFieldCount(); + for( iField = 0; iField < nFieldCount; iField++ ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(iField); + OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iField); + if( poGeomFieldDefn->bCachedExtentIsValid && + poGeom != NULL && !poGeom->IsEmpty() ) + { + OGREnvelope sGeomEnvelope; + poGeom->getEnvelope(&sGeomEnvelope); + poGeomFieldDefn->oCachedExtent.Merge(sGeomEnvelope); + } + } + bStatisticsNeedsToBeFlushed = TRUE; + } + + return eErr; +} + +/************************************************************************/ +/* AreTriggersSimilar */ +/************************************************************************/ + +static int AreTriggersSimilar(const char* pszExpectedTrigger, + const char* pszTriggerSQL) +{ + int i; + for(i=0; pszTriggerSQL[i] != '\0' && pszExpectedTrigger[i] != '\0'; i++) + { + if( pszTriggerSQL[i] == pszExpectedTrigger[i] ) + continue; + if( pszTriggerSQL[i] == '\n' && pszExpectedTrigger[i] == ' ' ) + continue; + if( pszTriggerSQL[i] == ' ' && pszExpectedTrigger[i] == '\n' ) + continue; + return FALSE; + } + return pszTriggerSQL[i] == '\0' && pszExpectedTrigger[i] == '\0'; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + sqlite3 *hDB = poDS->GetDB(); + CPLString osCommand; + int bNeedComma = FALSE; + + if (HasLayerDefnError()) + return OGRERR_FAILURE; + + if (!poDS->GetUpdate()) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "CreateFeature"); + return OGRERR_FAILURE; + } + + if( bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + + // For speed-up, disable Spatialite triggers that : + // * check the geometry type + // * update the last_insert columns in geometry_columns_time and the spatial index + // We do that only if there's no spatial index currently active + // We'll check ourselves the first constraint and update last_insert + // at layer closing + if( !bHasCheckedTriggers && + poDS->HasSpatialite4Layout() && poFeatureDefn->GetGeomFieldCount() > 0 ) + { + bHasCheckedTriggers = TRUE; + + char* pszErrMsg = NULL; + + // Backup INSERT ON triggers + int nRowCount = 0, nColCount = 0; + char **papszResult = NULL; + char* pszSQL3 = sqlite3_mprintf("SELECT name, sql FROM sqlite_master WHERE " + "tbl_name = '%q' AND type = 'trigger' AND (name LIKE 'ggi_%%' OR name LIKE 'tmi_%%')", + pszTableName); + sqlite3_get_table( poDS->GetDB(), + pszSQL3, + &papszResult, + &nRowCount, &nColCount, &pszErrMsg ); + sqlite3_free(pszSQL3); + + if( pszErrMsg ) + sqlite3_free( pszErrMsg ); + pszErrMsg = NULL; + + for(int j=0;jGetGeomFieldCount();j++) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(j); + if( !((bDeferredSpatialIndexCreation || !poGeomFieldDefn->bHasSpatialIndex) && + !poGeomFieldDefn->bHasM) ) + continue; + const char* pszGeomCol = poGeomFieldDefn->GetNameRef(); + + for(int i=0;iGetDB(), pszSQL3, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + CPLDebug("SQLITE", "Error %s", pszErrMsg ? pszErrMsg : ""); + else + { + CPLDebug("SQLite", "Dropping trigger %s", pszTriggerName); + poGeomFieldDefn->aosDisabledTriggers.push_back(std::pair(pszTriggerName, pszTriggerSQL)); + } + sqlite3_free(pszSQL3); + if( pszErrMsg ) + sqlite3_free( pszErrMsg ); + pszErrMsg = NULL; + } + else + { + CPLDebug("SQLite", "Cannot drop %s trigger. Doesn't match expected definition", + pszTriggerName); + } + } + } + } + + sqlite3_free_table( papszResult ); + } + + ResetReading(); + + for(int j=0;jGetGeomFieldCount();j++) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(j); + OGRGeometry *poGeom = poFeature->GetGeomFieldRef(j); + if( poGeomFieldDefn->aosDisabledTriggers.size() != 0 && poGeom != NULL ) + { + OGRwkbGeometryType eGeomType = poGeomFieldDefn->GetType(); + if( eGeomType != wkbUnknown && poGeom->getGeometryType() != eGeomType ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot insert feature with geometry of type %s%s in column %s. Type %s%s expected", + OGRToOGCGeomType(poGeom->getGeometryType()), + (wkbFlatten(poGeom->getGeometryType()) != poGeom->getGeometryType()) ? "Z" :"", + poGeomFieldDefn->GetNameRef(), + OGRToOGCGeomType(eGeomType), + (wkbFlatten(eGeomType) != eGeomType) ? "Z": "" ); + return OGRERR_FAILURE; + } + } + } + + int bReuseStmt = FALSE; + + /* If there's a unset field with a default value, then we must create */ + /* a specific INSERT statement to avoid unset fields to be bound to NULL */ + int bHasDefaultValue = FALSE; + int iField; + int nFieldCount = poFeatureDefn->GetFieldCount(); + for( iField = 0; iField < nFieldCount; iField++ ) + { + if( !poFeature->IsFieldSet( iField ) && + poFeature->GetFieldDefnRef(iField)->GetDefault() != NULL ) + { + bHasDefaultValue = TRUE; + break; + } + } + + /* In case the FID column has also been created as a regular field */ + if( iFIDAsRegularColumnIndex >= 0 ) + { + if( poFeature->GetFID() == OGRNullFID ) + { + if( poFeature->IsFieldSet( iFIDAsRegularColumnIndex ) ) + { + poFeature->SetFID( + poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex)); + } + } + else + { + if( !poFeature->IsFieldSet( iFIDAsRegularColumnIndex ) || + poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) != poFeature->GetFID() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Inconsistent values of FID and field of same name"); + return CE_Failure; + } + } + } + + int bTemporaryStatement = (poFeature->GetFID() != OGRNullFID || bHasDefaultValue); + if( hInsertStmt == NULL || bTemporaryStatement ) + { + CPLString osValues; + +/* -------------------------------------------------------------------- */ +/* Form the INSERT command. */ +/* -------------------------------------------------------------------- */ + osCommand += CPLSPrintf( "INSERT INTO '%s' (", pszEscapedTableName ); + +/* -------------------------------------------------------------------- */ +/* Add FID if we have a cleartext FID column. */ +/* -------------------------------------------------------------------- */ + if( pszFIDColumn != NULL + && poFeature->GetFID() != OGRNullFID ) + { + osCommand += "\""; + osCommand += OGRSQLiteEscapeName(pszFIDColumn); + osCommand += "\""; + + osValues += CPLSPrintf( CPL_FRMT_GIB, poFeature->GetFID() ); + bNeedComma = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Add geometry. */ +/* -------------------------------------------------------------------- */ + nFieldCount = poFeatureDefn->GetGeomFieldCount(); + for( iField = 0; iField < nFieldCount; iField++ ) + { + OGRSQLiteGeomFormat eGeomFormat = + poFeatureDefn->myGetGeomFieldDefn(iField)->eGeomFormat; + if( eGeomFormat == OSGF_FGF ) + continue; + if( bHasDefaultValue && poFeature->GetGeomFieldRef(iField) == NULL ) + continue; + if( bNeedComma ) + { + osCommand += ","; + osValues += ","; + } + + osCommand += "\""; + osCommand += OGRSQLiteEscapeName(poFeatureDefn->GetGeomFieldDefn(iField)->GetNameRef()); + osCommand += "\""; + + osValues += "?"; + + bNeedComma = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Add field values. */ +/* -------------------------------------------------------------------- */ + nFieldCount = poFeatureDefn->GetFieldCount(); + for( iField = 0; iField < nFieldCount; iField++ ) + { + if( iField == iFIDAsRegularColumnIndex ) + continue; + if( bHasDefaultValue && !poFeature->IsFieldSet( iField ) ) + continue; + + if( bNeedComma ) + { + osCommand += ","; + osValues += ","; + } + + osCommand += "\""; + osCommand += OGRSQLiteEscapeName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef()); + osCommand += "\""; + + osValues += "?"; + + bNeedComma = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Merge final command. */ +/* -------------------------------------------------------------------- */ + osCommand += ") VALUES ("; + osCommand += osValues; + osCommand += ")"; + + if (bNeedComma == FALSE) + osCommand = CPLSPrintf( "INSERT INTO '%s' DEFAULT VALUES", pszEscapedTableName ); + } + else + bReuseStmt = TRUE; + +/* -------------------------------------------------------------------- */ +/* Prepare the statement. */ +/* -------------------------------------------------------------------- */ + int rc; + + if( !bReuseStmt && (hInsertStmt == NULL || osCommand != osLastInsertStmt) ) + { + #ifdef DEBUG + CPLDebug( "OGR_SQLITE", "prepare(%s)", osCommand.c_str() ); + #endif + + ClearInsertStmt(); + if( poFeature->GetFID() == OGRNullFID ) + osLastInsertStmt = osCommand; + +#ifdef HAVE_SQLITE3_PREPARE_V2 + rc = sqlite3_prepare_v2( hDB, osCommand, -1, &hInsertStmt, NULL ); +#else + rc = sqlite3_prepare( hDB, osCommand, -1, &hInsertStmt, NULL ); +#endif + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In CreateFeature(): sqlite3_prepare(%s):\n %s", + osCommand.c_str(), sqlite3_errmsg(hDB) ); + + ClearInsertStmt(); + return OGRERR_FAILURE; + } + } + +/* -------------------------------------------------------------------- */ +/* Bind values. */ +/* -------------------------------------------------------------------- */ + OGRErr eErr = BindValues( poFeature, hInsertStmt, !bHasDefaultValue ); + if (eErr != OGRERR_NONE) + { + sqlite3_reset( hInsertStmt ); + return eErr; + } + +/* -------------------------------------------------------------------- */ +/* Execute the insert. */ +/* -------------------------------------------------------------------- */ + rc = sqlite3_step( hInsertStmt ); + + if( rc != SQLITE_OK && rc != SQLITE_DONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "sqlite3_step() failed:\n %s (%d)", + sqlite3_errmsg(hDB), rc ); + sqlite3_reset( hInsertStmt ); + ClearInsertStmt(); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Capture the FID/rowid. */ +/* -------------------------------------------------------------------- */ + const sqlite_int64 nFID = sqlite3_last_insert_rowid( hDB ); + if(nFID > 0) + { + poFeature->SetFID( nFID ); + if( iFIDAsRegularColumnIndex >= 0 ) + poFeature->SetField( iFIDAsRegularColumnIndex, nFID ); + } + + sqlite3_reset( hInsertStmt ); + + if( bTemporaryStatement ) + ClearInsertStmt(); + + nFieldCount = poFeatureDefn->GetGeomFieldCount(); + for( iField = 0; iField < nFieldCount; iField++ ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(iField); + OGRGeometry *poGeom = poFeature->GetGeomFieldRef(iField); + + if( (poGeomFieldDefn->bCachedExtentIsValid || nFeatureCount == 0) && + poGeom != NULL && !poGeom->IsEmpty() ) + { + OGREnvelope sGeomEnvelope; + poGeom->getEnvelope(&sGeomEnvelope); + poGeomFieldDefn->oCachedExtent.Merge(sGeomEnvelope); + poGeomFieldDefn->bCachedExtentIsValid = TRUE; + bStatisticsNeedsToBeFlushed = TRUE; + } + } + + if( nFeatureCount >= 0 ) + { + bStatisticsNeedsToBeFlushed = TRUE; + nFeatureCount ++; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::DeleteFeature( GIntBig nFID ) + +{ + CPLString osSQL; + int rc; + char *pszErrMsg = NULL; + + if (HasLayerDefnError()) + return OGRERR_FAILURE; + + if( pszFIDColumn == NULL ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "Can't delete feature on a layer without FID column."); + return OGRERR_FAILURE; + } + + if (!poDS->GetUpdate()) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "DeleteFeature"); + return OGRERR_FAILURE; + } + + if( bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE ) + return OGRERR_FAILURE; + + ResetReading(); + + osSQL.Printf( "DELETE FROM '%s' WHERE \"%s\" = " CPL_FRMT_GIB, + pszEscapedTableName, + OGRSQLiteEscapeName(pszFIDColumn).c_str(), nFID ); + + CPLDebug( "OGR_SQLITE", "exec(%s)", osSQL.c_str() ); + + rc = sqlite3_exec( poDS->GetDB(), osSQL, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In DeleteFeature(): sqlite3_exec(%s):\n %s", + osSQL.c_str(), pszErrMsg ); + sqlite3_free( pszErrMsg ); + return OGRERR_FAILURE; + } + + OGRErr eErr = (sqlite3_changes(poDS->GetDB()) > 0) ? OGRERR_NONE : OGRERR_NON_EXISTING_FEATURE; + if( eErr == OGRERR_NONE ) + { + int nFieldCount = poFeatureDefn->GetGeomFieldCount(); + for( int iField = 0; iField < nFieldCount; iField++ ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(iField); + poGeomFieldDefn->bCachedExtentIsValid = FALSE; + } + nFeatureCount --; + bStatisticsNeedsToBeFlushed = TRUE; + } + + return eErr; +} + +/************************************************************************/ +/* CreateSpatialIndex() */ +/************************************************************************/ + +int OGRSQLiteTableLayer::CreateSpatialIndex(int iGeomCol) +{ + CPLString osCommand; + + if( bDeferredCreation ) RunDeferredCreationIfNecessary(); + + if( iGeomCol < 0 || iGeomCol >= poFeatureDefn->GetGeomFieldCount() ) + return FALSE; + + osCommand.Printf("SELECT CreateSpatialIndex('%s', '%s')", + pszEscapedTableName, + OGRSQLiteEscape(poFeatureDefn->GetGeomFieldDefn(iGeomCol)->GetNameRef()).c_str()); + + char* pszErrMsg = NULL; + sqlite3 *hDB = poDS->GetDB(); +#ifdef DEBUG + CPLDebug( "OGR_SQLITE", "exec(%s)", osCommand.c_str() ); +#endif + int rc = sqlite3_exec( hDB, osCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create spatial index:\n%s", pszErrMsg ); + sqlite3_free( pszErrMsg ); + return FALSE; + } + + poFeatureDefn->myGetGeomFieldDefn(iGeomCol)->bHasSpatialIndex = TRUE; + return TRUE; +} + + +/************************************************************************/ +/* RunDeferredCreationIfNecessary() */ +/************************************************************************/ + +OGRErr OGRSQLiteTableLayer::RunDeferredCreationIfNecessary() +{ + if( !bDeferredCreation ) + return OGRERR_NONE; + bDeferredCreation = FALSE; + + const char* pszLayerName = poFeatureDefn->GetName(); + + int rc; + char *pszErrMsg; + CPLString osCommand; + + osCommand.Printf( "CREATE TABLE '%s' ( %s INTEGER PRIMARY KEY", + pszEscapedTableName, + pszFIDColumn ); + + int i; + if ( !poDS->IsSpatialiteDB() ) + { + for(i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(i); + + if( poGeomFieldDefn->eGeomFormat == OSGF_WKT ) + { + osCommand += CPLSPrintf(", '%s' VARCHAR", + OGRSQLiteEscape(poGeomFieldDefn->GetNameRef()).c_str() ); + } + else + { + osCommand += CPLSPrintf(", '%s' BLOB", + OGRSQLiteEscape(poGeomFieldDefn->GetNameRef()).c_str() ); + } + if( !poGeomFieldDefn->IsNullable() ) + { + osCommand += " NOT NULL"; + } + } + } + + for(i = 0; i < poFeatureDefn->GetFieldCount(); i++ ) + { + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(i); + if( i == iFIDAsRegularColumnIndex ) + continue; + CPLString osFieldType(FieldDefnToSQliteFieldDefn(poFieldDefn)); + osCommand += CPLSPrintf(", '%s' %s", + OGRSQLiteEscape(poFieldDefn->GetNameRef()).c_str(), + osFieldType.c_str()); + if( !poFieldDefn->IsNullable() ) + { + osCommand += " NOT NULL"; + } + const char* pszDefault = poFieldDefn->GetDefault(); + if( pszDefault != NULL && + (!poFieldDefn->IsDefaultDriverSpecific() || + (pszDefault[0] == '(' && pszDefault[strlen(pszDefault)-1] == ')' && + (EQUALN(pszDefault+1, "strftime", strlen("strftime")) || + EQUALN(pszDefault+1, " strftime", strlen(" strftime"))))) ) + { + osCommand += " DEFAULT "; + osCommand += poFieldDefn->GetDefault(); + } + } + osCommand += ")"; + +#ifdef DEBUG + CPLDebug( "OGR_SQLITE", "exec(%s)", osCommand.c_str() ); +#endif + + rc = sqlite3_exec( poDS->GetDB(), osCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create table %s: %s", + pszLayerName, pszErrMsg ); + sqlite3_free( pszErrMsg ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Eventually we should be adding this table to a table of */ +/* "geometric layers", capturing the WKT projection, and */ +/* perhaps some other housekeeping. */ +/* -------------------------------------------------------------------- */ + if( poDS->HasGeometryColumns() ) + { + /* Sometimes there is an old cruft entry in the geometry_columns + * table if things were not properly cleaned up before. We make + * an effort to clean out such cruft. + */ + osCommand.Printf( + "DELETE FROM geometry_columns WHERE f_table_name = '%s'", + pszEscapedTableName ); + +#ifdef DEBUG + CPLDebug( "OGR_SQLITE", "exec(%s)", osCommand.c_str() ); +#endif + + rc = sqlite3_exec( poDS->GetDB(), osCommand, NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + sqlite3_free( pszErrMsg ); + return FALSE; + } + + for(i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(i); + RunAddGeometryColumn(poGeomFieldDefn, FALSE); + } + } + + if (RecomputeOrdinals() != OGRERR_NONE ) + return OGRERR_FAILURE; + + if( poDS->IsSpatialiteDB() && poDS->GetLayerCount() == 1) + { + /* To create the layer_statistics and spatialite_history tables */ + sqlite3_exec( poDS->GetDB(), "SELECT UpdateLayerStatistics()", NULL, NULL, NULL ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* HasSpatialIndex() */ +/************************************************************************/ + +int OGRSQLiteTableLayer::HasSpatialIndex(int iGeomCol) +{ + GetLayerDefn(); + if( iGeomCol < 0 || iGeomCol >= poFeatureDefn->GetGeomFieldCount() ) + return FALSE; + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(iGeomCol); + + CreateSpatialIndexIfNecessary(); + + return poGeomFieldDefn->bHasSpatialIndex; +} + +/************************************************************************/ +/* InitFeatureCount() */ +/************************************************************************/ + +void OGRSQLiteTableLayer::InitFeatureCount() +{ + nFeatureCount = 0; + bStatisticsNeedsToBeFlushed = TRUE; +} + +/************************************************************************/ +/* InvalidateCachedFeatureCountAndExtent() */ +/************************************************************************/ + +void OGRSQLiteTableLayer::InvalidateCachedFeatureCountAndExtent() +{ + nFeatureCount = -1; + for(int iGeomCol=0;iGeomColGetGeomFieldCount();iGeomCol++) + poFeatureDefn->myGetGeomFieldDefn(iGeomCol)->bCachedExtentIsValid = FALSE; + bStatisticsNeedsToBeFlushed = TRUE; +} + +/************************************************************************/ +/* DoStatisticsNeedToBeFlushed() */ +/************************************************************************/ + +int OGRSQLiteTableLayer::DoStatisticsNeedToBeFlushed() +{ + return bStatisticsNeedsToBeFlushed && + poDS->IsSpatialiteDB() && + poDS->IsSpatialiteLoaded(); +} + +/************************************************************************/ +/* ForceStatisticsToBeFlushed() */ +/************************************************************************/ + +void OGRSQLiteTableLayer::ForceStatisticsToBeFlushed() +{ + bStatisticsNeedsToBeFlushed = TRUE; +} + +/************************************************************************/ +/* AreStatisticsValid() */ +/************************************************************************/ + +int OGRSQLiteTableLayer::AreStatisticsValid() +{ + return nFeatureCount >= 0; +} + +/************************************************************************/ +/* LoadStatisticsSpatialite4DB() */ +/************************************************************************/ + +void OGRSQLiteTableLayer::LoadStatisticsSpatialite4DB() +{ + for(int iCol = 0; iCol < GetLayerDefn()->GetGeomFieldCount(); iCol++ ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(iCol); + const char* pszGeomCol = poGeomFieldDefn->GetNameRef(); + + CPLString osSQL; + CPLString osLastEvtDate; + osSQL.Printf("SELECT MAX(last_insert, last_update, last_delete) FROM geometry_columns_time WHERE " + "f_table_name = '%s' AND f_geometry_column = '%s'", + pszEscapedTableName, OGRSQLiteEscape(pszGeomCol).c_str()); + + sqlite3 *hDB = poDS->GetDB(); + int nRowCount = 0, nColCount = 0; + char **papszResult = NULL; + + sqlite3_get_table( hDB, osSQL.c_str(), &papszResult, + &nRowCount, &nColCount, NULL ); + + /* Make it a Unix timestamp */ + int nYear, nMonth, nDay, nHour, nMinute; + float fSecond; + if( nRowCount == 1 && nColCount == 1 && papszResult[1] != NULL && + sscanf( papszResult[1], "%04d-%02d-%02dT%02d:%02d:%f", + &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond ) == 6 ) + { + osLastEvtDate = papszResult[1]; + } + + sqlite3_free_table( papszResult ); + papszResult = NULL; + + if( osLastEvtDate.size() == 0 ) + return; + + osSQL.Printf("SELECT last_verified, row_count, extent_min_x, extent_min_y, " + "extent_max_x, extent_max_y FROM geometry_columns_statistics WHERE " + "f_table_name = '%s' AND f_geometry_column = '%s'", + pszEscapedTableName, OGRSQLiteEscape(pszGeomCol).c_str()); + + nRowCount = 0; + nColCount = 0; + sqlite3_get_table( hDB, osSQL.c_str(), &papszResult, + &nRowCount, &nColCount, NULL ); + + if( nRowCount == 1 && nColCount == 6 && papszResult[6] != NULL && + sscanf( papszResult[6], "%04d-%02d-%02dT%02d:%02d:%f", + &nYear, &nMonth, &nDay, &nHour, &nMinute, &fSecond ) == 6 ) + { + CPLString osLastVerified(papszResult[6]); + + /* Check that the information in geometry_columns_statistics is more */ + /* recent than geometry_columns_time */ + if( osLastVerified.compare(osLastEvtDate) > 0 ) + { + char **papszRow = papszResult + 6; + const char* pszRowCount = papszRow[1]; + const char* pszMinX = papszRow[2]; + const char* pszMinY = papszRow[3]; + const char* pszMaxX = papszRow[4]; + const char* pszMaxY = papszRow[5]; + + CPLDebug("SQLITE", "Loading statistics for %s,%s", pszTableName, + pszGeomCol); + + if( pszRowCount != NULL ) + { + nFeatureCount = CPLAtoGIntBig( pszRowCount ); + if( nFeatureCount == 0) + { + nFeatureCount = -1; + pszMinX = NULL; + } + else + { + CPLDebug("SQLite", "Layer %s feature count : " CPL_FRMT_GIB, + pszTableName, nFeatureCount); + } + } + + if( pszMinX != NULL && pszMinY != NULL && + pszMaxX != NULL && pszMaxY != NULL ) + { + poGeomFieldDefn->bCachedExtentIsValid = TRUE; + poGeomFieldDefn->oCachedExtent.MinX = CPLAtof(pszMinX); + poGeomFieldDefn->oCachedExtent.MinY = CPLAtof(pszMinY); + poGeomFieldDefn->oCachedExtent.MaxX = CPLAtof(pszMaxX); + poGeomFieldDefn->oCachedExtent.MaxY = CPLAtof(pszMaxY); + CPLDebug("SQLite", "Layer %s extent : %s,%s,%s,%s", + pszTableName, pszMinX,pszMinY,pszMaxX,pszMaxY); + } + } + } + + sqlite3_free_table( papszResult ); + papszResult = NULL; + } +} + +/************************************************************************/ +/* LoadStatistics() */ +/************************************************************************/ + +void OGRSQLiteTableLayer::LoadStatistics() +{ + if( !poDS->IsSpatialiteDB() || !poDS->IsSpatialiteLoaded() ) + return; + + if( poDS->HasSpatialite4Layout() ) + { + LoadStatisticsSpatialite4DB(); + return; + } + + if( GetLayerDefn()->GetGeomFieldCount() != 1 ) + return; + const char* pszGeomCol = poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef(); + + GIntBig nFileTimestamp = poDS->GetFileTimestamp(); + if( nFileTimestamp == 0 ) + return; + + /* Find the most recent event in the 'spatialite_history' that is */ + /* a UpdateLayerStatistics event on all tables and geometry columns */ + CPLString osSQL; + osSQL.Printf("SELECT MAX(timestamp) FROM spatialite_history WHERE " + "((table_name = '%s' AND geometry_column = '%s') OR " + "(table_name = 'ALL-TABLES' AND geometry_column = 'ALL-GEOMETRY-COLUMNS')) AND " + "event = 'UpdateLayerStatistics'", + pszEscapedTableName, OGRSQLiteEscape(pszGeomCol).c_str()); + + sqlite3 *hDB = poDS->GetDB(); + int nRowCount = 0, nColCount = 0; + char **papszResult = NULL, *pszErrMsg = NULL; + + sqlite3_get_table( hDB, osSQL.c_str(), &papszResult, + &nRowCount, &nColCount, &pszErrMsg ); + + /* Make it a Unix timestamp */ + int nYear, nMonth, nDay, nHour, nMinute, nSecond; + struct tm brokendown; + GIntBig nTS = -1; + if( nRowCount >= 1 && nColCount == 1 && papszResult[1] != NULL && + sscanf( papszResult[1], "%04d-%02d-%02d %02d:%02d:%02d", + &nYear, &nMonth, &nDay, &nHour, &nMinute, &nSecond ) == 6 ) + { + brokendown.tm_year = nYear - 1900; + brokendown.tm_mon = nMonth - 1; + brokendown.tm_mday = nDay; + brokendown.tm_hour = nHour; + brokendown.tm_min = nMinute; + brokendown.tm_sec = nSecond; + nTS = CPLYMDHMSToUnixTime(&brokendown); + } + + /* If it is equal to the modified timestamp of the DB (as a file) */ + /* then we can safely use the data from the layer_statistics, since */ + /* it will be up-to-date */ + if( nFileTimestamp == nTS || nFileTimestamp == nTS + 1 ) + { + osSQL.Printf("SELECT row_count, extent_min_x, extent_min_y, extent_max_x, extent_max_y " + "FROM layer_statistics WHERE table_name = '%s' AND geometry_column = '%s'", + pszEscapedTableName, OGRSQLiteEscape(pszGeomCol).c_str()); + + sqlite3_free_table( papszResult ); + papszResult = NULL; + + sqlite3_get_table( hDB, osSQL.c_str(), &papszResult, + &nRowCount, &nColCount, &pszErrMsg ); + + if( nRowCount == 1 ) + { + char **papszRow = papszResult + 5; + const char* pszRowCount = papszRow[0]; + const char* pszMinX = papszRow[1]; + const char* pszMinY = papszRow[2]; + const char* pszMaxX = papszRow[3]; + const char* pszMaxY = papszRow[4]; + + CPLDebug("SQLITE", "File timestamp matches layer statistics timestamp. " + "Loading statistics for %s", pszTableName); + + if( pszRowCount != NULL ) + { + nFeatureCount = CPLAtoGIntBig( pszRowCount ); + CPLDebug("SQLite", "Layer %s feature count : " CPL_FRMT_GIB, + pszTableName, nFeatureCount); + } + + if( pszMinX != NULL && pszMinY != NULL && + pszMaxX != NULL && pszMaxY != NULL ) + { + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(0); + poGeomFieldDefn->bCachedExtentIsValid = TRUE; + poGeomFieldDefn->oCachedExtent.MinX = CPLAtof(pszMinX); + poGeomFieldDefn->oCachedExtent.MinY = CPLAtof(pszMinY); + poGeomFieldDefn->oCachedExtent.MaxX = CPLAtof(pszMaxX); + poGeomFieldDefn->oCachedExtent.MaxY = CPLAtof(pszMaxY); + CPLDebug("SQLite", "Layer %s extent : %s,%s,%s,%s", + pszTableName, pszMinX,pszMinY,pszMaxX,pszMaxY); + } + } + } + + if( pszErrMsg ) + sqlite3_free( pszErrMsg ); + + sqlite3_free_table( papszResult ); +} + +/************************************************************************/ +/* SaveStatistics() */ +/************************************************************************/ + +int OGRSQLiteTableLayer::SaveStatistics() +{ + if( !bStatisticsNeedsToBeFlushed || !poDS->IsSpatialiteDB() || + !poDS->IsSpatialiteLoaded() || poDS->HasSpatialite4Layout() ) + return -1; + if( GetLayerDefn()->GetGeomFieldCount() != 1 ) + return -1; + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = poFeatureDefn->myGetGeomFieldDefn(0); + const char* pszGeomCol = poGeomFieldDefn->GetNameRef(); + + CPLString osSQL; + sqlite3 *hDB = poDS->GetDB(); + char* pszErrMsg = NULL; + + if( nFeatureCount >= 0 ) + { + /* Update or add entry in the layer_statistics table */ + if( poGeomFieldDefn->bCachedExtentIsValid ) + { + osSQL.Printf("INSERT OR REPLACE INTO layer_statistics (raster_layer, " + "table_name, geometry_column, row_count, extent_min_x, " + "extent_min_y, extent_max_x, extent_max_y) VALUES (" + "0, '%s', '%s', " CPL_FRMT_GIB ", %s, %s, %s, %s)", + pszEscapedTableName, OGRSQLiteEscape(pszGeomCol).c_str(), + nFeatureCount, + // Insure that only Decimal.Points are used, never local settings such as Decimal.Comma. + CPLString().FormatC(poGeomFieldDefn->oCachedExtent.MinX,"%.18g").c_str(), + CPLString().FormatC(poGeomFieldDefn->oCachedExtent.MinY,"%.18g").c_str(), + CPLString().FormatC(poGeomFieldDefn->oCachedExtent.MaxX,"%.18g").c_str(), + CPLString().FormatC(poGeomFieldDefn->oCachedExtent.MaxY,"%.18g").c_str()); + } + else + { + osSQL.Printf("INSERT OR REPLACE INTO layer_statistics (raster_layer, " + "table_name, geometry_column, row_count, extent_min_x, " + "extent_min_y, extent_max_x, extent_max_y) VALUES (" + "0, '%s', '%s', " CPL_FRMT_GIB ", NULL, NULL, NULL, NULL)", + pszEscapedTableName, OGRSQLiteEscape(pszGeomCol).c_str(), + nFeatureCount); + } + } + else + { + /* Remove any existing entry in layer_statistics if for some reason */ + /* we know that it will out-of-sync */ + osSQL.Printf("DELETE FROM layer_statistics WHERE " + "table_name = '%s' AND geometry_column = '%s'", + pszEscapedTableName, OGRSQLiteEscape(pszGeomCol).c_str()); + } + + int rc = sqlite3_exec( hDB, osSQL.c_str(), NULL, NULL, &pszErrMsg ); + if( rc != SQLITE_OK ) + { + CPLDebug("SQLITE", "Error %s", pszErrMsg ? pszErrMsg : "unknown"); + sqlite3_free( pszErrMsg ); + return FALSE; + } + else + return TRUE; +} + +/************************************************************************/ +/* SetCompressedColumns() */ +/************************************************************************/ + +void OGRSQLiteTableLayer::SetCompressedColumns( const char* pszCompressedColumns ) +{ + papszCompressedColumns = CSLTokenizeString2( pszCompressedColumns, ",", + CSLT_HONOURSTRINGS ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitevfs.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitevfs.cpp new file mode 100644 index 000000000..5089e2124 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitevfs.cpp @@ -0,0 +1,430 @@ +/****************************************************************************** + * $Id: ogrsqlitevfs.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements SQLite VFS + * Author: Even Rouault, + + ****************************************************************************** + * Copyright (c) 2011-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_atomic_ops.h" +#include "ogr_sqlite.h" + +CPL_CVSID("$Id: ogrsqlitevfs.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#ifdef DEBUG_IO +# define DEBUG_ONLY +#else +# define DEBUG_ONLY CPL_UNUSED +#endif + +//#define DEBUG_IO 1 + +#ifdef HAVE_SQLITE_VFS + +typedef struct +{ + char szVFSName[64]; + sqlite3_vfs *pDefaultVFS; + pfnNotifyFileOpenedType pfn; + void *pfnUserData; + int nCounter; +} OGRSQLiteVFSAppDataStruct; + +#define GET_UNDERLYING_VFS(pVFS) ((OGRSQLiteVFSAppDataStruct* )pVFS->pAppData)->pDefaultVFS + +typedef struct +{ + const struct sqlite3_io_methods *pMethods; + VSILFILE *fp; + int bDeleteOnClose; + char *pszFilename; +} OGRSQLiteFileStruct; + +static int OGRSQLiteIOClose(sqlite3_file* pFile) +{ + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; +#ifdef DEBUG_IO + CPLDebug("SQLITE", "OGRSQLiteIOClose(%p (%s))", pMyFile->fp, pMyFile->pszFilename); +#endif + VSIFCloseL(pMyFile->fp); + if (pMyFile->bDeleteOnClose) + VSIUnlink(pMyFile->pszFilename); + CPLFree(pMyFile->pszFilename); + return SQLITE_OK; +} + +static int OGRSQLiteIORead(sqlite3_file* pFile, void* pBuffer, int iAmt, sqlite3_int64 iOfst) +{ + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; + VSIFSeekL(pMyFile->fp, (vsi_l_offset) iOfst, SEEK_SET); + int nRead = (int)VSIFReadL(pBuffer, 1, iAmt, pMyFile->fp); +#ifdef DEBUG_IO + CPLDebug("SQLITE", "OGRSQLiteIORead(%p, %d, %d) = %d", pMyFile->fp, iAmt, (int)iOfst, nRead); +#endif + if (nRead < iAmt) + { + memset(((char*)pBuffer) + nRead, 0, iAmt - nRead); + return SQLITE_IOERR_SHORT_READ; + } + return SQLITE_OK; +} + +static int OGRSQLiteIOWrite(sqlite3_file* pFile, const void* pBuffer, int iAmt, sqlite3_int64 iOfst) +{ + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; + VSIFSeekL(pMyFile->fp, (vsi_l_offset) iOfst, SEEK_SET); + int nWritten = (int)VSIFWriteL(pBuffer, 1, iAmt, pMyFile->fp); +#ifdef DEBUG_IO + CPLDebug("SQLITE", "OGRSQLiteIOWrite(%p, %d, %d) = %d", pMyFile->fp, iAmt, (int)iOfst, nWritten); +#endif + if (nWritten < iAmt) + { + return SQLITE_IOERR_WRITE; + } + return SQLITE_OK; +} + +static int OGRSQLiteIOTruncate(sqlite3_file* pFile, sqlite3_int64 size) +{ + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; +#ifdef DEBUG_IO + CPLDebug("SQLITE", "OGRSQLiteIOTruncate(%p, " CPL_FRMT_GIB ")", pMyFile->fp, size); +#endif + int nRet = VSIFTruncateL(pMyFile->fp, size); + return (nRet == 0) ? SQLITE_OK : SQLITE_IOERR_TRUNCATE; +} + +static int OGRSQLiteIOSync(DEBUG_ONLY sqlite3_file* pFile, DEBUG_ONLY int flags) +{ +#ifdef DEBUG_IO + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; + CPLDebug("SQLITE", "OGRSQLiteIOSync(%p, %d)", pMyFile->fp, flags); +#endif + return SQLITE_OK; +} + +static int OGRSQLiteIOFileSize(sqlite3_file* pFile, sqlite3_int64 *pSize) +{ + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; + vsi_l_offset nCurOffset = VSIFTellL(pMyFile->fp); + VSIFSeekL(pMyFile->fp, 0, SEEK_END); + *pSize = VSIFTellL(pMyFile->fp); + VSIFSeekL(pMyFile->fp, nCurOffset, SEEK_SET); +#ifdef DEBUG_IO + CPLDebug("SQLITE", "OGRSQLiteIOFileSize(%p) = " CPL_FRMT_GIB, pMyFile->fp, *pSize); +#endif + return SQLITE_OK; +} + +static int OGRSQLiteIOLock(DEBUG_ONLY sqlite3_file* pFile, DEBUG_ONLY int flags) +{ +#ifdef DEBUG_IO + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; + CPLDebug("SQLITE", "OGRSQLiteIOLock(%p)", pMyFile->fp); +#endif + return SQLITE_OK; +} + +static int OGRSQLiteIOUnlock(DEBUG_ONLY sqlite3_file* pFile, DEBUG_ONLY int flags) +{ +#ifdef DEBUG_IO + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; + CPLDebug("SQLITE", "OGRSQLiteIOUnlock(%p)", pMyFile->fp); +#endif + return SQLITE_OK; +} + +static int OGRSQLiteIOCheckReservedLock(DEBUG_ONLY sqlite3_file* pFile, + DEBUG_ONLY int *pResOut) +{ +#ifdef DEBUG_IO + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; + CPLDebug("SQLITE", "OGRSQLiteIOCheckReservedLock(%p)", pMyFile->fp); +#endif + *pResOut = 0; + return SQLITE_OK; +} + +static int OGRSQLiteIOFileControl(DEBUG_ONLY sqlite3_file* pFile, + DEBUG_ONLY int op, + DEBUG_ONLY void *pArg) +{ +#ifdef DEBUG_IO + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; + CPLDebug("SQLITE", "OGRSQLiteIOFileControl(%p, %d)", pMyFile->fp, op); +#endif + return SQLITE_NOTFOUND; +} + +static int OGRSQLiteIOSectorSize(DEBUG_ONLY sqlite3_file* pFile) +{ +#ifdef DEBUG_IO + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; + CPLDebug("SQLITE", "OGRSQLiteIOSectorSize(%p)", pMyFile->fp); +#endif + return 0; +} + +static int OGRSQLiteIODeviceCharacteristics(DEBUG_ONLY sqlite3_file* pFile) +{ +#ifdef DEBUG_IO + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; + CPLDebug("SQLITE", "OGRSQLiteIODeviceCharacteristics(%p)", pMyFile->fp); +#endif + return 0; +} + +static const sqlite3_io_methods OGRSQLiteIOMethods = +{ + 1, + OGRSQLiteIOClose, + OGRSQLiteIORead, + OGRSQLiteIOWrite, + OGRSQLiteIOTruncate, + OGRSQLiteIOSync, + OGRSQLiteIOFileSize, + OGRSQLiteIOLock, + OGRSQLiteIOUnlock, + OGRSQLiteIOCheckReservedLock, + OGRSQLiteIOFileControl, + OGRSQLiteIOSectorSize, + OGRSQLiteIODeviceCharacteristics +}; + +static int OGRSQLiteVFSOpen(sqlite3_vfs* pVFS, + const char *zName, + sqlite3_file* pFile, + int flags, + int *pOutFlags) +{ +#ifdef DEBUG_IO + CPLDebug("SQLITE", "OGRSQLiteVFSOpen(%s, %d)", zName ? zName : "(null)", flags); +#endif + + OGRSQLiteVFSAppDataStruct* pAppData = (OGRSQLiteVFSAppDataStruct* )pVFS->pAppData; + + if (zName == NULL) + { + zName = CPLSPrintf("/vsimem/sqlite/%p_%d", + pVFS, CPLAtomicInc(&(pAppData->nCounter))); + } + + OGRSQLiteFileStruct* pMyFile = (OGRSQLiteFileStruct*) pFile; + pMyFile->pMethods = NULL; + pMyFile->bDeleteOnClose = FALSE; + pMyFile->pszFilename = NULL; + if ( flags & SQLITE_OPEN_READONLY ) + pMyFile->fp = VSIFOpenL(zName, "rb"); + else if ( flags & SQLITE_OPEN_CREATE ) + pMyFile->fp = VSIFOpenL(zName, "wb+"); + else if ( flags & SQLITE_OPEN_READWRITE ) + pMyFile->fp = VSIFOpenL(zName, "rb+"); + else + pMyFile->fp = NULL; + + if (pMyFile->fp == NULL) + return SQLITE_CANTOPEN; + +#ifdef DEBUG_IO + CPLDebug("SQLITE", "OGRSQLiteVFSOpen() = %p", pMyFile->fp); +#endif + + pfnNotifyFileOpenedType pfn = pAppData->pfn; + if (pfn) + { + pfn(pAppData->pfnUserData, zName, pMyFile->fp); + } + + pMyFile->pMethods = &OGRSQLiteIOMethods; + pMyFile->bDeleteOnClose = ( flags & SQLITE_OPEN_DELETEONCLOSE ); + pMyFile->pszFilename = CPLStrdup(zName); + + if (pOutFlags != NULL) + *pOutFlags = flags; + + return SQLITE_OK; +} + +static int OGRSQLiteVFSDelete(DEBUG_ONLY sqlite3_vfs* pVFS, + const char *zName, int DEBUG_ONLY syncDir) +{ +#ifdef DEBUG_IO + CPLDebug("SQLITE", "OGRSQLiteVFSDelete(%s)", zName); +#endif + VSIUnlink(zName); + return SQLITE_OK; +} + +static int OGRSQLiteVFSAccess (DEBUG_ONLY sqlite3_vfs* pVFS, + const char *zName, + int flags, + int *pResOut) +{ +#ifdef DEBUG_IO + CPLDebug("SQLITE", "OGRSQLiteVFSAccess(%s, %d)", zName, flags); +#endif + VSIStatBufL sStatBufL; + int nRet; + if (flags == SQLITE_ACCESS_EXISTS) + { + /* Do not try to check the presence of a journal on /vsicurl ! */ + if ( strncmp(zName, "/vsicurl/", 9) == 0 && + strlen(zName) > strlen("-journal") && + strcmp(zName + strlen(zName) - strlen("-journal"), "-journal") == 0 ) + nRet = -1; + else + nRet = VSIStatExL(zName, &sStatBufL, VSI_STAT_EXISTS_FLAG); + } + else if (flags == SQLITE_ACCESS_READ) + { + VSILFILE* fp = VSIFOpenL(zName, "rb"); + nRet = fp ? 0 : -1; + if (fp) + VSIFCloseL(fp); + } + else if (flags == SQLITE_ACCESS_READWRITE) + { + VSILFILE* fp = VSIFOpenL(zName, "rb+"); + nRet = fp ? 0 : -1; + if (fp) + VSIFCloseL(fp); + } + else + nRet = -1; + *pResOut = (nRet == 0); + return SQLITE_OK; +} + +static int OGRSQLiteVFSFullPathname (sqlite3_vfs* pVFS, const char *zName, int nOut, char *zOut) +{ + sqlite3_vfs* pUnderlyingVFS = GET_UNDERLYING_VFS(pVFS); +#ifdef DEBUG_IO + CPLDebug("SQLITE", "OGRSQLiteVFSFullPathname(%s)", zName); +#endif + if (zName[0] == '/') + { + strncpy(zOut, zName, nOut); + zOut[nOut-1] = '\0'; + return SQLITE_OK; + } + return pUnderlyingVFS->xFullPathname(pUnderlyingVFS, zName, nOut, zOut); +} + +static void* OGRSQLiteVFSDlOpen (sqlite3_vfs* pVFS, const char *zFilename) +{ + sqlite3_vfs* pUnderlyingVFS = GET_UNDERLYING_VFS(pVFS); + //CPLDebug("SQLITE", "OGRSQLiteVFSDlOpen(%s)", zFilename); + return pUnderlyingVFS->xDlOpen(pUnderlyingVFS, zFilename); +} + +static void OGRSQLiteVFSDlError (sqlite3_vfs* pVFS, int nByte, char *zErrMsg) +{ + sqlite3_vfs* pUnderlyingVFS = GET_UNDERLYING_VFS(pVFS); + //CPLDebug("SQLITE", "OGRSQLiteVFSDlError()"); + pUnderlyingVFS->xDlError(pUnderlyingVFS, nByte, zErrMsg); +} + +/* xDlSym member signature changed in sqlite 3.6.7 (http://www.sqlite.org/changes.html) */ +/* This was supposed to be done "in a way that is backwards compatible but which might cause compiler warnings" */ +/* Perhaps in C, but definitely not in C++ ( #4515 ) */ +#if SQLITE_VERSION_NUMBER >= 3006007 +static void (*OGRSQLiteVFSDlSym (sqlite3_vfs* pVFS,void* pHandle, const char *zSymbol))(void) +#else +static void (*OGRSQLiteVFSDlSym (sqlite3_vfs* pVFS,void* pHandle, const char *zSymbol)) +#endif +{ + sqlite3_vfs* pUnderlyingVFS = GET_UNDERLYING_VFS(pVFS); + //CPLDebug("SQLITE", "OGRSQLiteVFSDlSym(%s)", zSymbol); + return pUnderlyingVFS->xDlSym(pUnderlyingVFS, pHandle, zSymbol); +} + +static void OGRSQLiteVFSDlClose (sqlite3_vfs* pVFS, void* pHandle) +{ + sqlite3_vfs* pUnderlyingVFS = GET_UNDERLYING_VFS(pVFS); + //CPLDebug("SQLITE", "OGRSQLiteVFSDlClose(%p)", pHandle); + pUnderlyingVFS->xDlClose(pUnderlyingVFS, pHandle); +} + +static int OGRSQLiteVFSRandomness (sqlite3_vfs* pVFS, int nByte, char *zOut) +{ + sqlite3_vfs* pUnderlyingVFS = GET_UNDERLYING_VFS(pVFS); + //CPLDebug("SQLITE", "OGRSQLiteVFSRandomness()"); + return pUnderlyingVFS->xRandomness(pUnderlyingVFS, nByte, zOut); +} + +static int OGRSQLiteVFSSleep (sqlite3_vfs* pVFS, int microseconds) +{ + sqlite3_vfs* pUnderlyingVFS = GET_UNDERLYING_VFS(pVFS); + //CPLDebug("SQLITE", "OGRSQLiteVFSSleep()"); + return pUnderlyingVFS->xSleep(pUnderlyingVFS, microseconds); +} + +static int OGRSQLiteVFSCurrentTime (sqlite3_vfs* pVFS, double* p1) +{ + sqlite3_vfs* pUnderlyingVFS = GET_UNDERLYING_VFS(pVFS); + //CPLDebug("SQLITE", "OGRSQLiteVFSCurrentTime()"); + return pUnderlyingVFS->xCurrentTime(pUnderlyingVFS, p1); +} + +static int OGRSQLiteVFSGetLastError (sqlite3_vfs* pVFS, int p1, char *p2) +{ + sqlite3_vfs* pUnderlyingVFS = GET_UNDERLYING_VFS(pVFS); + //CPLDebug("SQLITE", "OGRSQLiteVFSGetLastError()"); + return pUnderlyingVFS->xGetLastError(pUnderlyingVFS, p1, p2); +} + +sqlite3_vfs* OGRSQLiteCreateVFS(pfnNotifyFileOpenedType pfn, void* pfnUserData) +{ + sqlite3_vfs* pDefaultVFS = sqlite3_vfs_find(NULL); + sqlite3_vfs* pMyVFS = (sqlite3_vfs*) CPLCalloc(1, sizeof(sqlite3_vfs)); + + OGRSQLiteVFSAppDataStruct* pVFSAppData = + (OGRSQLiteVFSAppDataStruct*) CPLCalloc(1, sizeof(OGRSQLiteVFSAppDataStruct)); + sprintf(pVFSAppData->szVFSName, "OGRSQLITEVFS_%p", pVFSAppData); + pVFSAppData->pDefaultVFS = pDefaultVFS; + pVFSAppData->pfn = pfn; + pVFSAppData->pfnUserData = pfnUserData; + pVFSAppData->nCounter = 0; + + pMyVFS->iVersion = 1; + pMyVFS->szOsFile = sizeof(OGRSQLiteFileStruct); + pMyVFS->mxPathname = pDefaultVFS->mxPathname; + pMyVFS->zName = pVFSAppData->szVFSName; + pMyVFS->pAppData = pVFSAppData; + pMyVFS->xOpen = OGRSQLiteVFSOpen; + pMyVFS->xDelete = OGRSQLiteVFSDelete; + pMyVFS->xAccess = OGRSQLiteVFSAccess; + pMyVFS->xFullPathname = OGRSQLiteVFSFullPathname; + pMyVFS->xDlOpen = OGRSQLiteVFSDlOpen; + pMyVFS->xDlError = OGRSQLiteVFSDlError; + pMyVFS->xDlSym = OGRSQLiteVFSDlSym; + pMyVFS->xDlClose = OGRSQLiteVFSDlClose; + pMyVFS->xRandomness = OGRSQLiteVFSRandomness; + pMyVFS->xSleep = OGRSQLiteVFSSleep; + pMyVFS->xCurrentTime = OGRSQLiteVFSCurrentTime; + pMyVFS->xGetLastError = OGRSQLiteVFSGetLastError; + return pMyVFS; +} + +#endif // HAVE_SQLITE_VFS diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteviewlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteviewlayer.cpp new file mode 100644 index 000000000..feb7108b1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteviewlayer.cpp @@ -0,0 +1,580 @@ +/****************************************************************************** + * $Id: ogrsqliteviewlayer.cpp 28386 2015-01-30 17:04:25Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRSQLiteViewLayer class, access to an existing spatialite view. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_sqlite.h" +#include + +CPL_CVSID("$Id: ogrsqliteviewlayer.cpp 28386 2015-01-30 17:04:25Z rouault $"); + +/************************************************************************/ +/* OGRSQLiteViewLayer() */ +/************************************************************************/ + +OGRSQLiteViewLayer::OGRSQLiteViewLayer( OGRSQLiteDataSource *poDSIn ) + +{ + poDS = poDSIn; + + iNextShapeId = 0; + + poFeatureDefn = NULL; + pszViewName = NULL; + pszEscapedTableName = NULL; + pszEscapedUnderlyingTableName = NULL; + bHasSpatialIndex = FALSE; + + bHasCheckedSpatialIndexTable = FALSE; + + bLayerDefnError = FALSE; +} + +/************************************************************************/ +/* ~OGRSQLiteViewLayer() */ +/************************************************************************/ + +OGRSQLiteViewLayer::~OGRSQLiteViewLayer() + +{ + ClearStatement(); + CPLFree(pszViewName); + CPLFree(pszEscapedTableName); + CPLFree(pszEscapedUnderlyingTableName); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +CPLErr OGRSQLiteViewLayer::Initialize( const char *pszViewName, + const char *pszViewGeometry, + const char *pszViewRowid, + const char *pszUnderlyingTableName, + const char *pszUnderlyingGeometryColumn) + +{ + this->pszViewName = CPLStrdup(pszViewName); + SetDescription( pszViewName ); + + osGeomColumn = pszViewGeometry; + eGeomFormat = OSGF_SpatiaLite; + + CPLFree( pszFIDColumn ); + pszFIDColumn = CPLStrdup( pszViewRowid ); + + osUnderlyingTableName = pszUnderlyingTableName; + osUnderlyingGeometryColumn = pszUnderlyingGeometryColumn; + poUnderlyingLayer = NULL; + + //this->bHasM = bHasM; + + pszEscapedTableName = CPLStrdup(OGRSQLiteEscape(pszViewName)); + pszEscapedUnderlyingTableName = CPLStrdup(OGRSQLiteEscape(pszUnderlyingTableName)); + + return CE_None; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn* OGRSQLiteViewLayer::GetLayerDefn() +{ + if (poFeatureDefn) + return poFeatureDefn; + + EstablishFeatureDefn(); + + if (poFeatureDefn == NULL) + { + bLayerDefnError = TRUE; + + poFeatureDefn = new OGRSQLiteFeatureDefn( pszViewName ); + poFeatureDefn->Reference(); + } + + return poFeatureDefn; +} + +/************************************************************************/ +/* GetUnderlyingLayer() */ +/************************************************************************/ + +OGRSQLiteLayer* OGRSQLiteViewLayer::GetUnderlyingLayer() +{ + if( poUnderlyingLayer == NULL ) + { + if( strchr(osUnderlyingTableName, '(') == NULL ) + { + CPLString osNewUnderlyingTableName; + osNewUnderlyingTableName.Printf("%s(%s)", + osUnderlyingTableName.c_str(), + osUnderlyingGeometryColumn.c_str()); + poUnderlyingLayer = + (OGRSQLiteLayer*) poDS->GetLayerByName(osNewUnderlyingTableName); + } + if( poUnderlyingLayer == NULL ) + poUnderlyingLayer = + (OGRSQLiteLayer*) poDS->GetLayerByName(osUnderlyingTableName); + } + return poUnderlyingLayer; +} + +/************************************************************************/ +/* GetGeomType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRSQLiteViewLayer::GetGeomType() +{ + if (poFeatureDefn) + return poFeatureDefn->GetGeomType(); + + OGRSQLiteLayer* poUnderlyingLayer = GetUnderlyingLayer(); + if (poUnderlyingLayer) + return poUnderlyingLayer->GetGeomType(); + + return wkbUnknown; +} + +/************************************************************************/ +/* EstablishFeatureDefn() */ +/************************************************************************/ + +CPLErr OGRSQLiteViewLayer::EstablishFeatureDefn() +{ + int rc; + sqlite3 *hDB = poDS->GetDB(); + sqlite3_stmt *hColStmt = NULL; + const char *pszSQL; + + OGRSQLiteLayer* poUnderlyingLayer = GetUnderlyingLayer(); + if (poUnderlyingLayer == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find underlying layer %s for view %s", + osUnderlyingTableName.c_str(), pszViewName); + return CE_Failure; + } + if ( !poUnderlyingLayer->IsTableLayer() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Underlying layer %s for view %s is not a regular table", + osUnderlyingTableName.c_str(), pszViewName); + return CE_Failure; + } + + int nUnderlyingLayerGeomFieldIndex = + poUnderlyingLayer->GetLayerDefn()->GetGeomFieldIndex(osUnderlyingGeometryColumn); + if ( nUnderlyingLayerGeomFieldIndex < 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Underlying layer %s for view %s has not expected geometry column name %s", + osUnderlyingTableName.c_str(), pszViewName, + osUnderlyingGeometryColumn.c_str()); + return CE_Failure; + } + + this->bHasSpatialIndex = poUnderlyingLayer->HasSpatialIndex(nUnderlyingLayerGeomFieldIndex); + +/* -------------------------------------------------------------------- */ +/* Get the column definitions for this table. */ +/* -------------------------------------------------------------------- */ + hColStmt = NULL; + pszSQL = CPLSPrintf( "SELECT \"%s\", * FROM '%s' LIMIT 1", + OGRSQLiteEscapeName(pszFIDColumn).c_str(), + pszEscapedTableName ); + + rc = sqlite3_prepare( hDB, pszSQL, strlen(pszSQL), &hColStmt, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to query table %s for column definitions : %s.", + pszViewName, sqlite3_errmsg(hDB) ); + + return CE_Failure; + } + + rc = sqlite3_step( hColStmt ); + if ( rc != SQLITE_DONE && rc != SQLITE_ROW ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In Initialize(): sqlite3_step(%s):\n %s", + pszSQL, sqlite3_errmsg(hDB) ); + sqlite3_finalize( hColStmt ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Collect the rest of the fields. */ +/* -------------------------------------------------------------------- */ + std::set aosGeomCols; + std::set aosIgnoredCols; + aosGeomCols.insert(osGeomColumn); + BuildFeatureDefn( pszViewName, hColStmt, aosGeomCols, aosIgnoredCols ); + sqlite3_finalize( hColStmt ); + +/* -------------------------------------------------------------------- */ +/* Set the properties of the geometry column. */ +/* -------------------------------------------------------------------- */ + if( poFeatureDefn->GetGeomFieldCount() != 0 ) + { + OGRSQLiteGeomFieldDefn* poSrcGeomFieldDefn = + poUnderlyingLayer->myGetLayerDefn()->myGetGeomFieldDefn(nUnderlyingLayerGeomFieldIndex); + OGRSQLiteGeomFieldDefn* poGeomFieldDefn = + poFeatureDefn->myGetGeomFieldDefn(0); + poGeomFieldDefn->SetType(poSrcGeomFieldDefn->GetType()); + poGeomFieldDefn->SetSpatialRef(poSrcGeomFieldDefn->GetSpatialRef()); + poGeomFieldDefn->nSRSId = poSrcGeomFieldDefn->nSRSId; + if( eGeomFormat != OSGF_None ) + poGeomFieldDefn->eGeomFormat = eGeomFormat; + } + + return CE_None; +} + +/************************************************************************/ +/* ResetStatement() */ +/************************************************************************/ + +OGRErr OGRSQLiteViewLayer::ResetStatement() + +{ + int rc; + CPLString osSQL; + + ClearStatement(); + + iNextShapeId = 0; + + osSQL.Printf( "SELECT \"%s\", * FROM '%s' %s", + OGRSQLiteEscapeName(pszFIDColumn).c_str(), + pszEscapedTableName, + osWHERE.c_str() ); + + rc = sqlite3_prepare( poDS->GetDB(), osSQL, osSQL.size(), + &hStmt, NULL ); + + if( rc == SQLITE_OK ) + { + return OGRERR_NONE; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "In ResetStatement(): sqlite3_prepare(%s):\n %s", + osSQL.c_str(), sqlite3_errmsg(poDS->GetDB()) ); + hStmt = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRSQLiteViewLayer::GetNextFeature() + +{ + if (HasLayerDefnError()) + return NULL; + + return OGRSQLiteLayer::GetNextFeature(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRSQLiteViewLayer::GetFeature( GIntBig nFeatureId ) + +{ + if (HasLayerDefnError()) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we don't have an explicit FID column, just read through */ +/* the result set iteratively to find our target. */ +/* -------------------------------------------------------------------- */ + if( pszFIDColumn == NULL ) + return OGRSQLiteLayer::GetFeature( nFeatureId ); + +/* -------------------------------------------------------------------- */ +/* Setup explicit query statement to fetch the record we want. */ +/* -------------------------------------------------------------------- */ + CPLString osSQL; + int rc; + + ClearStatement(); + + iNextShapeId = nFeatureId; + + osSQL.Printf( "SELECT \"%s\", * FROM '%s' WHERE \"%s\" = %d", + OGRSQLiteEscapeName(pszFIDColumn).c_str(), + pszEscapedTableName, + OGRSQLiteEscapeName(pszFIDColumn).c_str(), + (int) nFeatureId ); + + CPLDebug( "OGR_SQLITE", "exec(%s)", osSQL.c_str() ); + + rc = sqlite3_prepare( poDS->GetDB(), osSQL, osSQL.size(), + &hStmt, NULL ); + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "In GetFeature(): sqlite3_prepare(%s):\n %s", + osSQL.c_str(), sqlite3_errmsg(poDS->GetDB()) ); + + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Get the feature if possible. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = NULL; + + poFeature = GetNextRawFeature(); + + ResetReading(); + + return poFeature; +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRSQLiteViewLayer::SetAttributeFilter( const char *pszQuery ) + +{ + if( pszQuery == NULL ) + osQuery = ""; + else + osQuery = pszQuery; + + BuildWhere(); + + ResetReading(); + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRSQLiteViewLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) + +{ + if( InstallFilter( poGeomIn ) ) + { + BuildWhere(); + + ResetReading(); + } +} + +/************************************************************************/ +/* GetSpatialWhere() */ +/************************************************************************/ + +CPLString OGRSQLiteViewLayer::GetSpatialWhere(int iGeomCol, + OGRGeometry* poFilterGeom) +{ + if (HasLayerDefnError() || poFeatureDefn == NULL || + iGeomCol < 0 || iGeomCol >= poFeatureDefn->GetGeomFieldCount()) + return ""; + + if( poFilterGeom != NULL && bHasSpatialIndex ) + { + OGREnvelope sEnvelope; + + poFilterGeom->getEnvelope( &sEnvelope ); + + /* We first check that the spatial index table exists */ + if (!bHasCheckedSpatialIndexTable) + { + bHasCheckedSpatialIndexTable = TRUE; + char **papszResult; + int nRowCount, nColCount; + char *pszErrMsg = NULL; + + CPLString osSQL; + osSQL.Printf("SELECT name FROM sqlite_master " + "WHERE name='idx_%s_%s'", + pszEscapedUnderlyingTableName, + OGRSQLiteEscape(osUnderlyingGeometryColumn).c_str()); + + int rc = sqlite3_get_table( poDS->GetDB(), osSQL.c_str(), + &papszResult, &nRowCount, + &nColCount, &pszErrMsg ); + + if( rc != SQLITE_OK ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Error: %s", + pszErrMsg ); + sqlite3_free( pszErrMsg ); + bHasSpatialIndex = FALSE; + } + else + { + if (nRowCount != 1) + { + bHasSpatialIndex = FALSE; + } + + sqlite3_free_table(papszResult); + } + } + + if (bHasSpatialIndex) + { + return FormatSpatialFilterFromRTree(poFilterGeom, + CPLSPrintf("\"%s\"", OGRSQLiteEscapeName(pszFIDColumn).c_str()), + pszEscapedUnderlyingTableName, + OGRSQLiteEscape(osUnderlyingGeometryColumn).c_str()); + } + else + { + CPLDebug("SQLITE", "Count not find idx_%s_%s layer. Disabling spatial index", + pszEscapedUnderlyingTableName, osUnderlyingGeometryColumn.c_str()); + } + + } + + if( poFilterGeom != NULL && poDS->IsSpatialiteLoaded() ) + { + return FormatSpatialFilterFromMBR(poFilterGeom, + OGRSQLiteEscapeName(poFeatureDefn->GetGeomFieldDefn(iGeomCol)->GetNameRef()).c_str()); + } + + return ""; +} + +/************************************************************************/ +/* BuildWhere() */ +/* */ +/* Build the WHERE statement appropriate to the current set of */ +/* criteria (spatial and attribute queries). */ +/************************************************************************/ + +void OGRSQLiteViewLayer::BuildWhere() + +{ + osWHERE = ""; + + CPLString osSpatialWHERE = GetSpatialWhere(m_iGeomFieldFilter, + m_poFilterGeom); + if (osSpatialWHERE.size() != 0) + { + osWHERE = "WHERE "; + osWHERE += osSpatialWHERE; + } + + if( osQuery.size() > 0 ) + { + if( osWHERE.size() == 0 ) + { + osWHERE = "WHERE "; + osWHERE += osQuery; + } + else + { + osWHERE += " AND ("; + osWHERE += osQuery; + osWHERE += ")"; + } + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSQLiteViewLayer::TestCapability( const char * pszCap ) + +{ + if (HasLayerDefnError()) + return FALSE; + + if (EQUAL(pszCap,OLCFastFeatureCount)) + return m_poFilterGeom == NULL || osGeomColumn.size() == 0 || + bHasSpatialIndex; + + else if (EQUAL(pszCap,OLCFastSpatialFilter)) + return bHasSpatialIndex; + + else + return OGRSQLiteLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRSQLiteViewLayer::GetFeatureCount( int bForce ) + +{ + if (HasLayerDefnError()) + return 0; + + if( !TestCapability(OLCFastFeatureCount) ) + return OGRSQLiteLayer::GetFeatureCount( bForce ); + +/* -------------------------------------------------------------------- */ +/* Form count SQL. */ +/* -------------------------------------------------------------------- */ + const char *pszSQL; + + pszSQL = CPLSPrintf( "SELECT count(*) FROM '%s' %s", + pszEscapedTableName, osWHERE.c_str() ); + +/* -------------------------------------------------------------------- */ +/* Execute. */ +/* -------------------------------------------------------------------- */ + char **papszResult, *pszErrMsg; + int nRowCount, nColCount; + int nResult = -1; + + if( sqlite3_get_table( poDS->GetDB(), pszSQL, &papszResult, + &nColCount, &nRowCount, &pszErrMsg ) != SQLITE_OK ) + return -1; + + if( nRowCount == 1 && nColCount == 1 ) + nResult = atoi(papszResult[1]); + + sqlite3_free_table( papszResult ); + + return nResult; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitevirtualogr.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitevirtualogr.cpp new file mode 100644 index 000000000..9a66f005b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitevirtualogr.cpp @@ -0,0 +1,2485 @@ +/****************************************************************************** + * $Id: ogrsqlitevirtualogr.cpp 28900 2015-04-14 09:40:34Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: SQLite Virtual Table module using OGR layers + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrsqlitevirtualogr.h" +#include "ogr_api.h" +#include "swq.h" +#include "ogr_p.h" +#include +#include + +#ifdef HAVE_SQLITE_VFS + +/************************************************************************/ +/* OGR2SQLITE_Register() */ +/************************************************************************/ + +CPL_C_START +int CPL_DLL OGR2SQLITE_static_register (sqlite3* hDB, char **pzErrMsg, void* pApi); +CPL_C_END + +/* We call this function so that each time a db is created, */ +/* OGR2SQLITE_static_register is called, to initialize the sqlite3_api */ +/* structure with the right pointers. */ +/* We need to declare this function before including sqlite3ext.h, since */ +/* sqlite 3.8.7, sqlite3_auto_extension can be a macro (#5725) */ + +void OGR2SQLITE_Register() +{ + sqlite3_auto_extension ((void (*)(void)) OGR2SQLITE_static_register); +} + +#define VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED +//#define DEBUG_OGR2SQLITE + +#if defined(SPATIALITE_AMALGAMATION) +#include "ogrsqlite3ext.h" +#else +#include "sqlite3ext.h" +#endif + +/* Declaration of sqlite3_api structure */ +static SQLITE_EXTENSION_INIT1 + +/* The layout of fields is : + 0 : RegularField0 + ... + n-1 : RegularField(n-1) + n : OGR_STYLE (may be HIDDEN) + n+1 : GEOMETRY +*/ + +#define COMPILATION_ALLOWED +#include "ogrsqlitesqlfunctions.cpp" /* yes the .cpp file, to make it work on Windows with load_extension('gdalXX.dll') */ +#undef COMPILATION_ALLOWED + +/************************************************************************/ +/* OGR2SQLITEModule */ +/************************************************************************/ + +class OGR2SQLITEModule +{ +#ifdef DEBUG + void* pDummy; /* to track memory leaks */ +#endif + sqlite3* hDB; /* *NOT* to be freed */ + + GDALDataset* poDS; /* *NOT* to be freed */ + std::vector apoExtraDS; /* each datasource to be freed */ + + OGRSQLiteDataSource* poSQLiteDS; /* *NOT* to be freed, might be NULL */ + + std::map< CPLString, OGRLayer* > oMapVTableToOGRLayer; + + void* hHandleSQLFunctions; + + public: + OGR2SQLITEModule(); + ~OGR2SQLITEModule(); + + int Setup(GDALDataset* poDS, + OGRSQLiteDataSource* poSQLiteDS); + int Setup(sqlite3* hDB); + + GDALDataset* GetDS() { return poDS; } + + int AddExtraDS(OGRDataSource* poDS); + OGRDataSource *GetExtraDS(int nIndex); + + int FetchSRSId(OGRSpatialReference* poSRS); + + void RegisterVTable(const char* pszVTableName, OGRLayer* poLayer); + void UnregisterVTable(const char* pszVTableName); + OGRLayer* GetLayerForVTable(const char* pszVTableName); + + void SetHandleSQLFunctions(void* hHandleSQLFunctionsIn); +}; + +/************************************************************************/ +/* OGR2SQLITEModule() */ +/************************************************************************/ + +OGR2SQLITEModule::OGR2SQLITEModule() : + hDB(NULL), poDS(NULL), poSQLiteDS(NULL), hHandleSQLFunctions(NULL) +{ +#ifdef DEBUG + pDummy = CPLMalloc(1); +#endif +} + +/************************************************************************/ +/* ~OGR2SQLITEModule */ +/************************************************************************/ + +OGR2SQLITEModule::~OGR2SQLITEModule() +{ +#ifdef DEBUG + CPLFree(pDummy); +#endif + + for(int i=0;i<(int)apoExtraDS.size();i++) + delete apoExtraDS[i]; + + OGRSQLiteUnregisterSQLFunctions(hHandleSQLFunctions); +} + +/************************************************************************/ +/* SetHandleSQLFunctions() */ +/************************************************************************/ + +void OGR2SQLITEModule::SetHandleSQLFunctions(void* hHandleSQLFunctionsIn) +{ + CPLAssert(hHandleSQLFunctions == NULL); + hHandleSQLFunctions = hHandleSQLFunctionsIn; +} + +/************************************************************************/ +/* AddExtraDS() */ +/************************************************************************/ + +int OGR2SQLITEModule::AddExtraDS(OGRDataSource* poDS) +{ + int nRet = (int)apoExtraDS.size(); + apoExtraDS.push_back(poDS); + return nRet; +} + +/************************************************************************/ +/* GetExtraDS() */ +/************************************************************************/ + +OGRDataSource* OGR2SQLITEModule::GetExtraDS(int nIndex) +{ + if( nIndex < 0 || nIndex >= (int)apoExtraDS.size() ) + return NULL; + return apoExtraDS[nIndex]; +} + +/************************************************************************/ +/* Setup() */ +/************************************************************************/ + +int OGR2SQLITEModule::Setup(GDALDataset* poDSIn, + OGRSQLiteDataSource* poSQLiteDSIn) +{ + CPLAssert(poDS == NULL); + CPLAssert(poSQLiteDS == NULL); + poDS = poDSIn; + poSQLiteDS = poSQLiteDSIn; + return Setup(poSQLiteDS->GetDB()); +} + +/************************************************************************/ +/* FetchSRSId() */ +/************************************************************************/ + +int OGR2SQLITEModule::FetchSRSId(OGRSpatialReference* poSRS) +{ + int nSRSId; + + if( poSQLiteDS != NULL ) + { + nSRSId = poSQLiteDS->GetUndefinedSRID(); + if( poSRS != NULL ) + nSRSId = poSQLiteDS->FetchSRSId(poSRS); + } + else + { + nSRSId = -1; + if( poSRS != NULL ) + { + const char* pszAuthorityName = poSRS->GetAuthorityName(NULL); + if (pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG")) + { + const char* pszAuthorityCode = poSRS->GetAuthorityCode(NULL); + if ( pszAuthorityCode != NULL && strlen(pszAuthorityCode) > 0 ) + { + nSRSId = atoi(pszAuthorityCode); + } + } + } + } + + return nSRSId; +} + +/************************************************************************/ +/* RegisterVTable() */ +/************************************************************************/ + +void OGR2SQLITEModule::RegisterVTable(const char* pszVTableName, + OGRLayer* poLayer) +{ + oMapVTableToOGRLayer[pszVTableName] = poLayer; +} + +/************************************************************************/ +/* UnregisterVTable() */ +/************************************************************************/ + +void OGR2SQLITEModule::UnregisterVTable(const char* pszVTableName) +{ + oMapVTableToOGRLayer[pszVTableName] = NULL; +} + +/************************************************************************/ +/* GetLayerForVTable() */ +/************************************************************************/ + +OGRLayer* OGR2SQLITEModule::GetLayerForVTable(const char* pszVTableName) +{ + std::map::iterator oIter = + oMapVTableToOGRLayer.find(pszVTableName); + if( oIter == oMapVTableToOGRLayer.end() ) + return NULL; + + OGRLayer* poLayer = oIter->second; + if( poLayer == NULL ) + { + /* If the associate layer is null, then try to "ping" the virtual */ + /* table since we know that we have managed to create it before */ + if( sqlite3_exec(hDB, + CPLSPrintf("PRAGMA table_info(\"%s\")", + OGRSQLiteEscapeName(pszVTableName).c_str()), + NULL, NULL, NULL) == SQLITE_OK ) + { + poLayer = oMapVTableToOGRLayer[pszVTableName]; + } + } + + return poLayer; +} + +/* See http://www.sqlite.org/vtab.html for the documentation on how to + implement a new module for the Virtual Table mechanism. */ + +/************************************************************************/ +/* OGR2SQLITE_vtab */ +/************************************************************************/ + +typedef struct +{ + /* Mandatory fields by sqlite3: don't change or reorder them ! */ + const sqlite3_module *pModule; + int nRef; + char *zErrMsg; + + /* Extension fields */ + char *pszVTableName; + OGR2SQLITEModule *poModule; + GDALDataset *poDS; + int bCloseDS; + OGRLayer *poLayer; + int nMyRef; +} OGR2SQLITE_vtab; + +/************************************************************************/ +/* OGR2SQLITE_vtab_cursor */ +/************************************************************************/ + +typedef struct +{ + /* Mandatory fields by sqlite3: don't change or reorder them ! */ + OGR2SQLITE_vtab *pVTab; + + /* Extension fields */ + OGRDataSource *poDupDataSource; + OGRLayer *poLayer; + OGRFeature *poFeature; + + /* nFeatureCount >= 0 if the layer has a feast feature count capability. */ + /* In which case nNextWishedIndex and nCurFeatureIndex */ + /* will be used to avoid useless GetNextFeature() */ + /* Helps in SELECT COUNT(*) FROM xxxx scenarios */ + GIntBig nFeatureCount; + GIntBig nNextWishedIndex; + GIntBig nCurFeatureIndex; + + GByte *pabyGeomBLOB; + int nGeomBLOBLen; +} OGR2SQLITE_vtab_cursor; + + +/************************************************************************/ +/* OGR2SQLITE_GetNameForGeometryColumn() */ +/************************************************************************/ + +CPLString OGR2SQLITE_GetNameForGeometryColumn(OGRLayer* poLayer) +{ + if( poLayer->GetGeometryColumn() != NULL && + !EQUAL(poLayer->GetGeometryColumn(), "") ) + { + return poLayer->GetGeometryColumn(); + } + else + { + CPLString osGeomCol("GEOMETRY"); + int bTry = 2; + while( poLayer->GetLayerDefn()->GetFieldIndex(osGeomCol) >= 0 ) + { + osGeomCol.Printf("GEOMETRY%d", bTry++); + } + return osGeomCol; + } +} + +#ifdef VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED + +/************************************************************************/ +/* OGR2SQLITEDetectSuspiciousUsage() */ +/************************************************************************/ + +static int OGR2SQLITEDetectSuspiciousUsage(sqlite3* hDB, + const char* pszVirtualTableName, + char**pzErr) +{ + char **papszResult = NULL; + int nRowCount = 0, nColCount = 0; + int i; + + std::vector aosDatabaseNames; + + /* Collect database names */ + sqlite3_get_table( hDB, "PRAGMA database_list", + &papszResult, &nRowCount, &nColCount, NULL ); + + for(i = 1; i <= nRowCount; i++) + { + const char* pszUnescapedName = papszResult[i * nColCount + 1]; + aosDatabaseNames.push_back( + CPLSPrintf("\"%s\".sqlite_master", + OGRSQLiteEscapeName(pszUnescapedName).c_str())); + } + + /* Add special database (just in case, not sure it is really needed) */ + aosDatabaseNames.push_back("sqlite_temp_master"); + + sqlite3_free_table(papszResult); + papszResult = NULL; + + /* Check the triggers of each database */ + for(i = 0; i < (int)aosDatabaseNames.size(); i++ ) + { + nRowCount = 0; nColCount = 0; + + const char* pszSQL; + + pszSQL = CPLSPrintf("SELECT name, sql FROM %s " + "WHERE (type = 'trigger' OR type = 'view') AND (" + "sql LIKE '%%%s%%' OR " + "sql LIKE '%%\"%s\"%%' OR " + "sql LIKE '%%ogr_layer_%%' )", + aosDatabaseNames[i].c_str(), + pszVirtualTableName, + OGRSQLiteEscapeName(pszVirtualTableName).c_str()); + + sqlite3_get_table( hDB, pszSQL, &papszResult, &nRowCount, &nColCount, + NULL ); + + sqlite3_free_table(papszResult); + papszResult = NULL; + + if( nRowCount > 0 ) + { + if( !CSLTestBoolean(CPLGetConfigOption("ALLOW_VIRTUAL_OGR_FROM_TRIGGER_AND_VIEW", "NO")) ) + { + *pzErr = sqlite3_mprintf( + "A trigger and/or view might reference VirtualOGR table '%s'.\n" + "This is suspicious practice that could be used to steal data without your consent.\n" + "Disabling access to it unless you define the ALLOW_VIRTUAL_OGR_FROM_TRIGGER_AND_VIEW " + "configuration option to YES.", + pszVirtualTableName); + return TRUE; + } + } + } + + return FALSE; +} + +#endif // VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED + +/************************************************************************/ +/* OGR2SQLITE_ConnectCreate() */ +/************************************************************************/ + +static +int OGR2SQLITE_DisconnectDestroy(sqlite3_vtab *pVTab); + +static +int OGR2SQLITE_ConnectCreate(sqlite3* hDB, void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, char**pzErr) +{ + OGR2SQLITEModule* poModule = (OGR2SQLITEModule*) pAux; + OGRLayer* poLayer = NULL; + GDALDataset* poDS = NULL; + int bExposeOGR_STYLE = FALSE; + int bCloseDS = FALSE; + int bInternalUse = FALSE; + int i; + +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "ConnectCreate(%s)", argv[2]); +#endif + + /*for(i=0;iGetDS(); + if( poDS != NULL && argc == 6 && + CPLGetValueType(argv[3]) == CPL_VALUE_INTEGER ) + { + bInternalUse = TRUE; + if( argc != 6 ) + { + *pzErr = sqlite3_mprintf( + "Expected syntax: CREATE VIRTUAL TABLE xxx USING " + "VirtualOGR(ds_idx, layer_name, expose_ogr_style)"); + return SQLITE_ERROR; + } + + int nDSIndex = atoi(argv[3]); + if( nDSIndex >= 0 ) + { + poDS = poModule->GetExtraDS(nDSIndex); + if( poDS == NULL ) + { + *pzErr = sqlite3_mprintf("Invalid dataset index : %d", nDSIndex); + return SQLITE_ERROR; + } + } + CPLString osLayerName(OGRSQLiteParamsUnquote(argv[4])); + + poLayer = poDS->GetLayerByName(osLayerName); + if( poLayer == NULL ) + { + *pzErr = sqlite3_mprintf( "Cannot find layer '%s' in '%s'", + osLayerName.c_str(), poDS->GetDescription() ); + return SQLITE_ERROR; + } + + bExposeOGR_STYLE = atoi(OGRSQLiteParamsUnquote(argv[5])); + } +#ifdef VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED +/* -------------------------------------------------------------------- */ +/* If called from outside (OGR loaded as a sqlite3 extension) */ +/* -------------------------------------------------------------------- */ + else + { + if( argc < 4 || argc > 7 ) + { + *pzErr = sqlite3_mprintf( + "Expected syntax: CREATE VIRTUAL TABLE xxx USING " + "VirtualOGR(datasource_name[, update_mode, [layer_name[, expose_ogr_style]]])"); + return SQLITE_ERROR; + } + + if( OGR2SQLITEDetectSuspiciousUsage(hDB, argv[2], pzErr) ) + { + return SQLITE_ERROR; + } + + CPLString osDSName(OGRSQLiteParamsUnquote(argv[3])); + CPLString osUpdate(OGRSQLiteParamsUnquote((argc >= 5) ? argv[4] : "0")); + + if( !EQUAL(osUpdate, "1") && !EQUAL(osUpdate, "0") ) + { + *pzErr = sqlite3_mprintf( + "update_mode parameter should be 0 or 1"); + return SQLITE_ERROR; + } + + int bUpdate = atoi(osUpdate); + + poDS = (OGRDataSource* )OGROpenShared(osDSName, bUpdate, NULL); + if( poDS == NULL ) + { + *pzErr = sqlite3_mprintf( "Cannot open datasource '%s'", osDSName.c_str() ); + return SQLITE_ERROR; + } + + CPLString osLayerName; + if( argc >= 6 ) + { + osLayerName = OGRSQLiteParamsUnquote(argv[5]); + poLayer = poDS->GetLayerByName(osLayerName); + } + else + { + if( poDS->GetLayerCount() == 0 ) + { + *pzErr = sqlite3_mprintf( "Datasource '%s' has no layers", + osDSName.c_str() ); + poDS->Release(); + return SQLITE_ERROR; + } + + if( poDS->GetLayerCount() > 1 ) + { + *pzErr = sqlite3_mprintf( "Datasource '%s' has more than one layers, and none was explicitly selected.", + osDSName.c_str() ); + poDS->Release(); + return SQLITE_ERROR; + } + + poLayer = poDS->GetLayer(0); + } + + if( poLayer == NULL ) + { + *pzErr = sqlite3_mprintf( "Cannot find layer '%s' in '%s'", osLayerName.c_str(), osDSName.c_str() ); + poDS->Release(); + return SQLITE_ERROR; + } + + if( argc == 7 ) + { + bExposeOGR_STYLE = atoi(OGRSQLiteParamsUnquote(argv[6])); + } + + bCloseDS = TRUE; + } +#endif // VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED + OGR2SQLITE_vtab* vtab = + (OGR2SQLITE_vtab*) CPLCalloc(1, sizeof(OGR2SQLITE_vtab)); + /* We dont need to fill the non-extended fields */ + vtab->pszVTableName = CPLStrdup(OGRSQLiteEscapeName(argv[2])); + vtab->poModule = poModule; + vtab->poDS = poDS; + vtab->bCloseDS = bCloseDS; + vtab->poLayer = poLayer; + vtab->nMyRef = 0; + + poModule->RegisterVTable(vtab->pszVTableName, poLayer); + + *ppVTab = (sqlite3_vtab*) vtab; + + CPLString osSQL; + osSQL = "CREATE TABLE "; + osSQL += "\""; + osSQL += OGRSQLiteEscapeName(argv[2]); + osSQL += "\""; + osSQL += "("; + + int bAddComma = FALSE; + + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + for(i=0;iGetFieldCount();i++) + { + if( bAddComma ) + osSQL += ","; + bAddComma = TRUE; + + OGRFieldDefn* poFieldDefn = poFDefn->GetFieldDefn(i); + + osSQL += "\""; + osSQL += OGRSQLiteEscapeName(poFieldDefn->GetNameRef()); + osSQL += "\""; + osSQL += " "; + osSQL += OGRSQLiteFieldDefnToSQliteFieldDefn(poFieldDefn, + bInternalUse); + } + + if( bAddComma ) + osSQL += ","; + bAddComma = TRUE; + osSQL += "OGR_STYLE VARCHAR"; + if( !bExposeOGR_STYLE ) + osSQL += " HIDDEN"; + + for(i=0;iGetGeomFieldCount();i++) + { + if( bAddComma ) + osSQL += ","; + bAddComma = TRUE; + + OGRGeomFieldDefn* poFieldDefn = poFDefn->GetGeomFieldDefn(i); + + osSQL += "\""; + if( i == 0 ) + osSQL += OGRSQLiteEscapeName(OGR2SQLITE_GetNameForGeometryColumn(poLayer)); + else + osSQL += OGRSQLiteEscapeName(poFieldDefn->GetNameRef()); + osSQL += "\""; + osSQL += " BLOB"; + + /* We use a special column type, e.g. BLOB_POINT_25D_4326 */ + /* when the virtual table is created by OGRSQLiteExecuteSQL() */ + /* and thus for interal use only. */ + if( bInternalUse ) + { + osSQL += "_"; + osSQL += OGRToOGCGeomType(poFieldDefn->GetType()); + osSQL += "_"; + osSQL += wkbHasZ(poFieldDefn->GetType()) ? "25D" : "2D"; + OGRSpatialReference* poSRS = poFieldDefn->GetSpatialRef(); + if( poSRS == NULL && i == 0 ) + poSRS = poLayer->GetSpatialRef(); + int nSRID = poModule->FetchSRSId(poSRS); + if( nSRID >= 0 ) + { + osSQL += "_"; + osSQL += CPLSPrintf("%d", nSRID); + } + } + } + + osSQL += ")"; + + CPLDebug("OGR2SQLITE", "sqlite3_declare_vtab(%s)", osSQL.c_str()); + if (sqlite3_declare_vtab (hDB, osSQL.c_str()) != SQLITE_OK) + { + *pzErr = sqlite3_mprintf("CREATE VIRTUAL: invalid SQL statement : %s", + osSQL.c_str()); + OGR2SQLITE_DisconnectDestroy((sqlite3_vtab*) vtab); + return SQLITE_ERROR; + } + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITE_BestIndex() */ +/************************************************************************/ + +static +int OGR2SQLITE_BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info* pIndex) +{ + int i; + OGR2SQLITE_vtab* pMyVTab = (OGR2SQLITE_vtab*) pVTab; + OGRFeatureDefn* poFDefn = pMyVTab->poLayer->GetLayerDefn(); + +#ifdef DEBUG_OGR2SQLITE + CPLString osQueryPatternUsable, osQueryPatternNotUsable; + for (i = 0; i < pIndex->nConstraint; i++) + { + int iCol = pIndex->aConstraint[i].iColumn; + const char* pszFieldName; + if( iCol == -1 ) + pszFieldName = "FID"; + else if( iCol >= 0 && iCol < poFDefn->GetFieldCount() ) + pszFieldName = poFDefn->GetFieldDefn(iCol)->GetNameRef(); + else + pszFieldName = "unknown_field"; + + const char* pszOp; + switch(pIndex->aConstraint[i].op) + { + case SQLITE_INDEX_CONSTRAINT_EQ: pszOp = " = "; break; + case SQLITE_INDEX_CONSTRAINT_GT: pszOp = " > "; break; + case SQLITE_INDEX_CONSTRAINT_LE: pszOp = " <= "; break; + case SQLITE_INDEX_CONSTRAINT_LT: pszOp = " < "; break; + case SQLITE_INDEX_CONSTRAINT_GE: pszOp = " >= "; break; + case SQLITE_INDEX_CONSTRAINT_MATCH: pszOp = " MATCH "; break; + default: pszOp = " (unknown op) "; break; + } + + if (pIndex->aConstraint[i].usable) + { + if (osQueryPatternUsable.size()) osQueryPatternUsable += " AND "; + osQueryPatternUsable += pszFieldName; + osQueryPatternUsable += pszOp; + osQueryPatternUsable += "?"; + } + else + { + if (osQueryPatternNotUsable.size()) osQueryPatternNotUsable += " AND "; + osQueryPatternNotUsable += pszFieldName; + osQueryPatternNotUsable += pszOp; + osQueryPatternNotUsable += "?"; + } + } + CPLDebug("OGR2SQLITE", "BestIndex, usable ( %s ), not usable ( %s )", + osQueryPatternUsable.c_str(), osQueryPatternNotUsable.c_str()); +#endif + + int nConstraints = 0; + for (i = 0; i < pIndex->nConstraint; i++) + { + int iCol = pIndex->aConstraint[i].iColumn; + if (pIndex->aConstraint[i].usable && + pIndex->aConstraint[i].op != SQLITE_INDEX_CONSTRAINT_MATCH && + iCol < poFDefn->GetFieldCount() && + (iCol < 0 || poFDefn->GetFieldDefn(iCol)->GetType() != OFTBinary)) + { + pIndex->aConstraintUsage[i].argvIndex = nConstraints + 1; + pIndex->aConstraintUsage[i].omit = TRUE; + + nConstraints ++; + } + else + { + pIndex->aConstraintUsage[i].argvIndex = 0; + pIndex->aConstraintUsage[i].omit = FALSE; + } + } + + int* panConstraints = NULL; + + if( nConstraints ) + { + panConstraints = (int*) + sqlite3_malloc( sizeof(int) * (1 + 2 * nConstraints) ); + panConstraints[0] = nConstraints; + + nConstraints = 0; + + for (i = 0; i < pIndex->nConstraint; i++) + { + if (pIndex->aConstraintUsage[i].omit) + { + panConstraints[2 * nConstraints + 1] = + pIndex->aConstraint[i].iColumn; + panConstraints[2 * nConstraints + 2] = + pIndex->aConstraint[i].op; + + nConstraints ++; + } + } + } + + pIndex->orderByConsumed = FALSE; + pIndex->idxNum = 0; + + if (nConstraints != 0) + { + pIndex->idxStr = (char *) panConstraints; + pIndex->needToFreeIdxStr = TRUE; + } + else + { + pIndex->idxStr = NULL; + pIndex->needToFreeIdxStr = FALSE; + } + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITE_DisconnectDestroy() */ +/************************************************************************/ + +static +int OGR2SQLITE_DisconnectDestroy(sqlite3_vtab *pVTab) +{ + OGR2SQLITE_vtab* pMyVTab = (OGR2SQLITE_vtab*) pVTab; + +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "DisconnectDestroy(%s)",pMyVTab->pszVTableName); +#endif + + sqlite3_free(pMyVTab->zErrMsg); + if( pMyVTab->bCloseDS ) + pMyVTab->poDS->Release(); + pMyVTab->poModule->UnregisterVTable(pMyVTab->pszVTableName); + CPLFree(pMyVTab->pszVTableName); + CPLFree(pMyVTab); + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITE_Open() */ +/************************************************************************/ + +static +int OGR2SQLITE_Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) +{ + OGR2SQLITE_vtab* pMyVTab = (OGR2SQLITE_vtab*) pVTab; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Open(%s, %s)", + pMyVTab->poDS->GetName(), pMyVTab->poLayer->GetName()); +#endif + + OGRDataSource* poDupDataSource = NULL; + OGRLayer* poLayer = NULL; + + if( pMyVTab->nMyRef == 0 ) + { + poLayer = pMyVTab->poLayer; + } + else + { + poDupDataSource = + (OGRDataSource*) OGROpen(pMyVTab->poDS->GetDescription(), FALSE, NULL); + if( poDupDataSource == NULL ) + return SQLITE_ERROR; + poLayer = poDupDataSource->GetLayerByName( + pMyVTab->poLayer->GetName()); + if( poLayer == NULL ) + { + delete poDupDataSource; + return SQLITE_ERROR; + } + if( !poLayer->GetLayerDefn()-> + IsSame(pMyVTab->poLayer->GetLayerDefn()) ) + { + delete poDupDataSource; + return SQLITE_ERROR; + } + } + pMyVTab->nMyRef ++; + + OGR2SQLITE_vtab_cursor* pCursor = (OGR2SQLITE_vtab_cursor*) + CPLCalloc(1, sizeof(OGR2SQLITE_vtab_cursor)); + /* We dont need to fill the non-extended fields */ + *ppCursor = (sqlite3_vtab_cursor *)pCursor; + + pCursor->poDupDataSource = poDupDataSource; + pCursor->poLayer = poLayer; + pCursor->poLayer->ResetReading(); + pCursor->poFeature = NULL; + pCursor->nNextWishedIndex = 0; + pCursor->nCurFeatureIndex = -1; + pCursor->nFeatureCount = -1; + + pCursor->pabyGeomBLOB = NULL; + pCursor->nGeomBLOBLen = -1; + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITE_Close() */ +/************************************************************************/ + +static +int OGR2SQLITE_Close(sqlite3_vtab_cursor* pCursor) +{ + OGR2SQLITE_vtab_cursor* pMyCursor = (OGR2SQLITE_vtab_cursor*) pCursor; + OGR2SQLITE_vtab* pMyVTab = pMyCursor->pVTab; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Close(%s, %s)", + pMyVTab->poDS->GetName(), pMyVTab->poLayer->GetName()); +#endif + pMyVTab->nMyRef --; + + delete pMyCursor->poFeature; + delete pMyCursor->poDupDataSource; + + CPLFree(pMyCursor->pabyGeomBLOB); + + CPLFree(pCursor); + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITE_Filter() */ +/************************************************************************/ + +static +int OGR2SQLITE_Filter(sqlite3_vtab_cursor* pCursor, + CPL_UNUSED int idxNum, + const char *idxStr, + int argc, + sqlite3_value **argv) +{ + OGR2SQLITE_vtab_cursor* pMyCursor = (OGR2SQLITE_vtab_cursor*) pCursor; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Filter"); +#endif + + int* panConstraints = (int*) idxStr; + int nConstraints = panConstraints ? panConstraints[0] : 0; + + if( nConstraints != argc ) + return SQLITE_ERROR; + + CPLString osAttributeFilter; + + OGRFeatureDefn* poFDefn = pMyCursor->poLayer->GetLayerDefn(); + + int i; + for (i = 0; i < argc; i++) + { + int nCol = panConstraints[2 * i + 1]; + OGRFieldDefn* poFieldDefn = NULL; + if( nCol >= 0 ) + { + poFieldDefn = poFDefn->GetFieldDefn(nCol); + if( poFieldDefn == NULL ) + return SQLITE_ERROR; + } + + if( i != 0 ) + osAttributeFilter += " AND "; + + if( poFieldDefn != NULL ) + { + const char* pszFieldName = poFieldDefn->GetNameRef(); + char ch; + int bNeedsQuoting = swq_is_reserved_keyword(pszFieldName); + for(int j = 0; !bNeedsQuoting && + (ch = pszFieldName[j]) != '\0'; j++ ) + { + if (!(isalnum((int)ch) || ch == '_')) + bNeedsQuoting = FALSE; + } + + if( bNeedsQuoting ) + { + osAttributeFilter += '"'; + osAttributeFilter += OGRSQLiteEscapeName(pszFieldName); + osAttributeFilter += '"'; + } + else + { + osAttributeFilter += pszFieldName; + } + } + else + osAttributeFilter += "FID"; + + switch(panConstraints[2 * i + 2]) + { + case SQLITE_INDEX_CONSTRAINT_EQ: osAttributeFilter += " = "; break; + case SQLITE_INDEX_CONSTRAINT_GT: osAttributeFilter += " > "; break; + case SQLITE_INDEX_CONSTRAINT_LE: osAttributeFilter += " <= "; break; + case SQLITE_INDEX_CONSTRAINT_LT: osAttributeFilter += " < "; break; + case SQLITE_INDEX_CONSTRAINT_GE: osAttributeFilter += " >= "; break; + default: + { + sqlite3_free(pMyCursor->pVTab->zErrMsg); + pMyCursor->pVTab->zErrMsg = sqlite3_mprintf( + "Unhandled constraint operator : %d", + panConstraints[2 * i + 2]); + return SQLITE_ERROR; + } + } + + if (sqlite3_value_type (argv[i]) == SQLITE_INTEGER) + { + osAttributeFilter += + CPLSPrintf(CPL_FRMT_GIB, sqlite3_value_int64 (argv[i])); + } + else if (sqlite3_value_type (argv[i]) == SQLITE_FLOAT) + { // Insure that only Decimal.Points are used, never local settings such as Decimal.Comma. + osAttributeFilter += + CPLSPrintf("%.18g", sqlite3_value_double (argv[i])); + } + else if (sqlite3_value_type (argv[i]) == SQLITE_TEXT) + { + osAttributeFilter += "'"; + osAttributeFilter += OGRSQLiteEscape((const char*) sqlite3_value_text (argv[i])); + osAttributeFilter += "'"; + } + else + { + sqlite3_free(pMyCursor->pVTab->zErrMsg); + pMyCursor->pVTab->zErrMsg = sqlite3_mprintf( + "Unhandled constraint data type : %d", + sqlite3_value_type (argv[i])); + return SQLITE_ERROR; + } + } + +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Attribute filter : %s", + osAttributeFilter.c_str()); +#endif + + if( pMyCursor->poLayer->SetAttributeFilter( osAttributeFilter.size() ? + osAttributeFilter.c_str() : NULL) != OGRERR_NONE ) + { + sqlite3_free(pMyCursor->pVTab->zErrMsg); + pMyCursor->pVTab->zErrMsg = sqlite3_mprintf( + "Cannot apply attribute filter : %s", + osAttributeFilter.c_str()); + return SQLITE_ERROR; + } + + if( pMyCursor->poLayer->TestCapability(OLCFastFeatureCount) ) + { + pMyCursor->nFeatureCount = pMyCursor->poLayer->GetFeatureCount(); + pMyCursor->poLayer->ResetReading(); + } + else + pMyCursor->nFeatureCount = -1; + + if( pMyCursor->nFeatureCount < 0 ) + { + pMyCursor->poFeature = pMyCursor->poLayer->GetNextFeature(); +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "GetNextFeature() --> " CPL_FRMT_GIB, + pMyCursor->poFeature ? pMyCursor->poFeature->GetFID() : -1); +#endif + } + + pMyCursor->nNextWishedIndex = 0; + pMyCursor->nCurFeatureIndex = -1; + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITE_Next() */ +/************************************************************************/ + +static +int OGR2SQLITE_Next(sqlite3_vtab_cursor* pCursor) +{ + OGR2SQLITE_vtab_cursor* pMyCursor = (OGR2SQLITE_vtab_cursor*) pCursor; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Next"); +#endif + + pMyCursor->nNextWishedIndex ++; + if( pMyCursor->nFeatureCount < 0 ) + { + delete pMyCursor->poFeature; + pMyCursor->poFeature = pMyCursor->poLayer->GetNextFeature(); + + CPLFree(pMyCursor->pabyGeomBLOB); + pMyCursor->pabyGeomBLOB = NULL; + pMyCursor->nGeomBLOBLen = -1; + +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "GetNextFeature() --> " CPL_FRMT_GIB, + pMyCursor->poFeature ? pMyCursor->poFeature->GetFID() : -1); +#endif + } + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITE_Eof() */ +/************************************************************************/ + +static +int OGR2SQLITE_Eof(sqlite3_vtab_cursor* pCursor) +{ + OGR2SQLITE_vtab_cursor* pMyCursor = (OGR2SQLITE_vtab_cursor*) pCursor; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Eof"); +#endif + + if( pMyCursor->nFeatureCount < 0 ) + { + return (pMyCursor->poFeature == NULL); + } + else + { + return ( pMyCursor->nNextWishedIndex >= pMyCursor->nFeatureCount ); + } +} + +/************************************************************************/ +/* OGR2SQLITE_GoToWishedIndex() */ +/************************************************************************/ + +static void OGR2SQLITE_GoToWishedIndex(OGR2SQLITE_vtab_cursor* pMyCursor) +{ + if( pMyCursor->nFeatureCount >= 0 ) + { + if( pMyCursor->nCurFeatureIndex < pMyCursor->nNextWishedIndex ) + { + do + { + pMyCursor->nCurFeatureIndex ++; + + delete pMyCursor->poFeature; + pMyCursor->poFeature = pMyCursor->poLayer->GetNextFeature(); +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "GetNextFeature() --> " CPL_FRMT_GIB, + pMyCursor->poFeature ? pMyCursor->poFeature->GetFID() : -1); +#endif + } + while( pMyCursor->nCurFeatureIndex < pMyCursor->nNextWishedIndex ); + + CPLFree(pMyCursor->pabyGeomBLOB); + pMyCursor->pabyGeomBLOB = NULL; + pMyCursor->nGeomBLOBLen = -1; + } + } +} + +/************************************************************************/ +/* OGR2SQLITE_ExportGeometry() */ +/************************************************************************/ + +static void OGR2SQLITE_ExportGeometry(OGRGeometry* poGeom, int nSRSId, + GByte*& pabyGeomBLOB, + int& nGeomBLOBLen) +{ + if( OGRSQLiteLayer::ExportSpatiaLiteGeometry( + poGeom, nSRSId, wkbNDR, FALSE, FALSE, FALSE, + &pabyGeomBLOB, + &nGeomBLOBLen ) != CE_None ) + { + nGeomBLOBLen = 0; + } + /* This is a hack: we add the original curve geometry after */ + /* the spatialite blob */ + else if( poGeom->hasCurveGeometry() ) + { + int nWkbSize = poGeom->WkbSize(); + + pabyGeomBLOB = (GByte*) CPLRealloc(pabyGeomBLOB, + nGeomBLOBLen + nWkbSize + 1); + poGeom->exportToWkb(wkbNDR, pabyGeomBLOB + nGeomBLOBLen); + /* Sheat a bit and add a end-of-blob spatialite marker */ + pabyGeomBLOB[nGeomBLOBLen + nWkbSize] = 0xFE; + nGeomBLOBLen += nWkbSize + 1; + } +} + +/************************************************************************/ +/* OGR2SQLITE_Column() */ +/************************************************************************/ + +static +int OGR2SQLITE_Column(sqlite3_vtab_cursor* pCursor, + sqlite3_context* pContext, int nCol) +{ + OGR2SQLITE_vtab_cursor* pMyCursor = (OGR2SQLITE_vtab_cursor*) pCursor; + OGRFeature* poFeature; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Column %d", nCol); +#endif + + OGR2SQLITE_GoToWishedIndex(pMyCursor); + + poFeature = pMyCursor->poFeature; + if( poFeature == NULL) + return SQLITE_ERROR; + + OGRFeatureDefn* poFDefn = pMyCursor->poLayer->GetLayerDefn(); + int nFieldCount = poFDefn->GetFieldCount(); + + if( nCol == nFieldCount ) + { + sqlite3_result_text(pContext, + poFeature->GetStyleString(), + -1, SQLITE_TRANSIENT); + return SQLITE_OK; + } + else if( nCol == (nFieldCount + 1) && + poFDefn->GetGeomType() != wkbNone ) + { + if( pMyCursor->nGeomBLOBLen < 0 ) + { + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if( poGeom == NULL ) + { + pMyCursor->nGeomBLOBLen = 0; + } + else + { + CPLAssert(pMyCursor->pabyGeomBLOB == NULL); + + OGRSpatialReference* poSRS = poGeom->getSpatialReference(); + int nSRSId = pMyCursor->pVTab->poModule->FetchSRSId(poSRS); + + OGR2SQLITE_ExportGeometry(poGeom, nSRSId, + pMyCursor->pabyGeomBLOB, + pMyCursor->nGeomBLOBLen); + } + } + + if( pMyCursor->nGeomBLOBLen == 0 ) + { + sqlite3_result_null(pContext); + } + else + { + GByte *pabyGeomBLOBDup = (GByte*) + CPLMalloc(pMyCursor->nGeomBLOBLen); + memcpy(pabyGeomBLOBDup, + pMyCursor->pabyGeomBLOB, pMyCursor->nGeomBLOBLen); + sqlite3_result_blob(pContext, pabyGeomBLOBDup, + pMyCursor->nGeomBLOBLen, CPLFree); + } + + return SQLITE_OK; + } + else if( nCol > (nFieldCount + 1) && + nCol - (nFieldCount + 1) < poFDefn->GetGeomFieldCount() ) + { + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(nCol - (nFieldCount + 1)); + if( poGeom == NULL ) + { + sqlite3_result_null(pContext); + } + else + { + OGRSpatialReference* poSRS = poGeom->getSpatialReference(); + int nSRSId = pMyCursor->pVTab->poModule->FetchSRSId(poSRS); + + GByte* pabyGeomBLOB = NULL; + int nGeomBLOBLen = 0; + OGR2SQLITE_ExportGeometry(poGeom, nSRSId, pabyGeomBLOB, nGeomBLOBLen); + + if( nGeomBLOBLen == 0 ) + { + sqlite3_result_null(pContext); + } + else + { + sqlite3_result_blob(pContext, pabyGeomBLOB, + nGeomBLOBLen, CPLFree); + } + } + return SQLITE_OK; + } + else if( nCol < 0 || nCol >= nFieldCount ) + { + return SQLITE_ERROR; + } + else if( !poFeature->IsFieldSet(nCol) ) + { + sqlite3_result_null(pContext); + return SQLITE_OK; + } + + switch( poFDefn->GetFieldDefn(nCol)->GetType() ) + { + case OFTInteger: + sqlite3_result_int(pContext, + poFeature->GetFieldAsInteger(nCol)); + break; + + case OFTInteger64: + sqlite3_result_int64(pContext, + poFeature->GetFieldAsInteger64(nCol)); + break; + + case OFTReal: + sqlite3_result_double(pContext, + poFeature->GetFieldAsDouble(nCol)); + break; + + case OFTBinary: + { + int nSize; + GByte* pBlob = poFeature->GetFieldAsBinary(nCol, &nSize); + sqlite3_result_blob(pContext, pBlob, nSize, SQLITE_TRANSIENT); + break; + } + + case OFTDateTime: + { + char* pszStr = OGRGetXMLDateTime(poFeature->GetRawFieldRef(nCol)); + sqlite3_result_text(pContext, pszStr, -1, SQLITE_TRANSIENT); + CPLFree(pszStr); + break; + } + + case OFTDate: + { + int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZ; + poFeature->GetFieldAsDateTime(nCol, &nYear, &nMonth, &nDay, + &nHour, &nMinute, &nSecond, &nTZ); + char szBuffer[64]; + sprintf(szBuffer, "%04d-%02d-%02d", nYear, nMonth, nDay); + sqlite3_result_text(pContext, + szBuffer, + -1, SQLITE_TRANSIENT); + break; + } + + case OFTTime: + { + int nYear, nMonth, nDay, nHour, nMinute, nTZ; + float fSecond; + poFeature->GetFieldAsDateTime(nCol, &nYear, &nMonth, &nDay, + &nHour, &nMinute, &fSecond, &nTZ ); + char szBuffer[64]; + if( OGR_GET_MS(fSecond) != 0 ) + sprintf(szBuffer, "%02d:%02d:%06.3f", nHour, nMinute, fSecond); + else + sprintf(szBuffer, "%02d:%02d:%02d", nHour, nMinute, (int)fSecond); + sqlite3_result_text(pContext, + szBuffer, + -1, SQLITE_TRANSIENT); + break; + } + + default: + sqlite3_result_text(pContext, + poFeature->GetFieldAsString(nCol), + -1, SQLITE_TRANSIENT); + break; + } + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITE_Rowid() */ +/************************************************************************/ + +static +int OGR2SQLITE_Rowid(sqlite3_vtab_cursor* pCursor, sqlite3_int64 *pRowid) +{ + OGR2SQLITE_vtab_cursor* pMyCursor = (OGR2SQLITE_vtab_cursor*) pCursor; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Rowid"); +#endif + + OGR2SQLITE_GoToWishedIndex(pMyCursor); + + if( pMyCursor->poFeature == NULL) + return SQLITE_ERROR; + + *pRowid = pMyCursor->poFeature->GetFID(); + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITE_Rename() */ +/************************************************************************/ + +static +int OGR2SQLITE_Rename(CPL_UNUSED sqlite3_vtab *pVtab, CPL_UNUSED const char *zNew) +{ + //CPLDebug("OGR2SQLITE", "Rename"); + return SQLITE_ERROR; +} + +#if 0 +/************************************************************************/ +/* OGR2SQLITE_FindFunction() */ +/************************************************************************/ + +static +int OGR2SQLITE_FindFunction(sqlite3_vtab *pVtab, + int nArg, + const char *zName, + void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), + void **ppArg) +{ + CPLDebug("OGR2SQLITE", "FindFunction %s", zName); + + return 0; +} +#endif + +/************************************************************************/ +/* OGR2SQLITE_FeatureFromArgs() */ +/************************************************************************/ + +static OGRFeature* OGR2SQLITE_FeatureFromArgs(OGRLayer* poLayer, + int argc, + sqlite3_value **argv) +{ + OGRFeatureDefn* poLayerDefn = poLayer->GetLayerDefn(); + int nFieldCount = poLayerDefn->GetFieldCount(); + int nGeomFieldCount = poLayerDefn->GetGeomFieldCount(); + if( argc != 2 + nFieldCount + 1 + nGeomFieldCount) + { + CPLDebug("OGR2SQLITE", "Did not get expect argument count : %d, %d", argc, + 2 + nFieldCount + 1 + nGeomFieldCount); + return NULL; + } + + OGRFeature* poFeature = new OGRFeature(poLayerDefn); + int i; + for(i = 0; i < nFieldCount; i++) + { + switch( sqlite3_value_type(argv[2 + i]) ) + { + case SQLITE_INTEGER: + poFeature->SetField(i, sqlite3_value_int64(argv[2 + i])); + break; + case SQLITE_FLOAT: + poFeature->SetField(i, sqlite3_value_double(argv[2 + i])); + break; + case SQLITE_TEXT: + { + const char* pszValue = (const char*) sqlite3_value_text(argv[2 + i]); + switch( poLayerDefn->GetFieldDefn(i)->GetType() ) + { + case OFTDate: + case OFTTime: + case OFTDateTime: + { + if( !OGRSQLITEStringToDateTimeField( poFeature, i, pszValue ) ) + poFeature->SetField(i, pszValue); + break; + } + + default: + poFeature->SetField(i, pszValue); + break; + } + break; + } + case SQLITE_BLOB: + { + GByte* paby = (GByte *) sqlite3_value_blob (argv[2 + i]); + int nLen = sqlite3_value_bytes (argv[2 + i]); + poFeature->SetField(i, nLen, paby); + break; + } + default: + break; + } + } + + int nStyleIdx = 2 + nFieldCount; + if( sqlite3_value_type(argv[nStyleIdx]) == SQLITE_TEXT ) + { + poFeature->SetStyleString((const char*) sqlite3_value_text(argv[nStyleIdx])); + } + + for(i = 0; i < nGeomFieldCount; i++) + { + int nGeomFieldIdx = 2 + nFieldCount + 1 + i; + if( sqlite3_value_type(argv[nGeomFieldIdx]) == SQLITE_BLOB ) + { + GByte* pabyBlob = (GByte *) sqlite3_value_blob (argv[nGeomFieldIdx]); + int nLen = sqlite3_value_bytes (argv[nGeomFieldIdx]); + OGRGeometry* poGeom = NULL; + if( OGRSQLiteLayer::ImportSpatiaLiteGeometry( + pabyBlob, nLen, &poGeom ) == CE_None ) + { +/* OGRwkbGeometryType eGeomFieldType = + poFeature->GetDefnRef()->GetGeomFieldDefn(i)->GetType(); + if( OGR_GT_IsCurve(eGeomFieldType) && !OGR_GT_IsCurve(poGeom->getGeometryType()) ) + { + OGRGeometry* poCurveGeom = poGeom->getCurveGeometry(); + poFeature->SetGeomFieldDirectly(i, poCurveGeom); + delete poCurveGeom; + } + else*/ + poFeature->SetGeomFieldDirectly(i, poGeom); + } + } + } + + if( sqlite3_value_type(argv[1]) == SQLITE_INTEGER ) + poFeature->SetFID( sqlite3_value_int64(argv[1]) ); + + return poFeature; +} + +/************************************************************************/ +/* OGR2SQLITE_Update() */ +/************************************************************************/ + +static +int OGR2SQLITE_Update(sqlite3_vtab *pVTab, + int argc, + sqlite3_value **argv, + sqlite_int64 *pRowid) +{ + CPLDebug("OGR2SQLITE", "OGR2SQLITE_Update"); + + OGR2SQLITE_vtab* pMyVTab = (OGR2SQLITE_vtab*) pVTab; + OGRLayer* poLayer = pMyVTab->poLayer; + + if( argc == 1 ) + { + /* DELETE */ + + OGRErr eErr = poLayer->DeleteFeature(sqlite3_value_int64(argv[0])); + + return ( eErr == OGRERR_NONE ) ? SQLITE_OK : SQLITE_ERROR; + } + else if( argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_NULL ) + { + /* INSERT */ + + OGRFeature* poFeature = OGR2SQLITE_FeatureFromArgs(poLayer, argc, argv); + if( poFeature == NULL ) + return SQLITE_ERROR; + + OGRErr eErr = poLayer->CreateFeature(poFeature); + if( eErr == OGRERR_NONE ) + *pRowid = poFeature->GetFID(); + + delete poFeature; + + return ( eErr == OGRERR_NONE ) ? SQLITE_OK : SQLITE_ERROR; + } + else if( argc > 1 && sqlite3_value_type(argv[0]) == SQLITE_INTEGER && + sqlite3_value_type(argv[1]) == SQLITE_INTEGER && + sqlite3_value_int64(argv[0]) == sqlite3_value_int64(argv[1]) ) + { + /* UPDATE */ + + OGRFeature* poFeature = OGR2SQLITE_FeatureFromArgs(poLayer, argc, argv); + if( poFeature == NULL ) + return SQLITE_ERROR; + + OGRErr eErr = poLayer->SetFeature(poFeature); + + delete poFeature; + + return ( eErr == OGRERR_NONE ) ? SQLITE_OK : SQLITE_ERROR; + } + + // UPDATE table SET rowid=rowid+1 WHERE ... unsupported + + return SQLITE_ERROR; +} + +/************************************************************************/ +/* sOGR2SQLITEModule */ +/************************************************************************/ + +static const struct sqlite3_module sOGR2SQLITEModule = +{ + 1, /* iVersion */ + OGR2SQLITE_ConnectCreate, /* xCreate */ + OGR2SQLITE_ConnectCreate, /* xConnect */ + OGR2SQLITE_BestIndex, + OGR2SQLITE_DisconnectDestroy, /* xDisconnect */ + OGR2SQLITE_DisconnectDestroy, /* xDestroy */ + OGR2SQLITE_Open, + OGR2SQLITE_Close, + OGR2SQLITE_Filter, + OGR2SQLITE_Next, + OGR2SQLITE_Eof, + OGR2SQLITE_Column, + OGR2SQLITE_Rowid, + OGR2SQLITE_Update, + NULL, /* xBegin */ + NULL, /* xSync */ + NULL, /* xCommit */ + NULL, /* xFindFunctionRollback */ + NULL, /* xFindFunction */ // OGR2SQLITE_FindFunction; + OGR2SQLITE_Rename +}; + +/************************************************************************/ +/* OGR2SQLITE_GetLayer() */ +/************************************************************************/ + +static +OGRLayer* OGR2SQLITE_GetLayer(const char* pszFuncName, + sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + if( argc != 1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "%s: %s(): %s", + "VirtualOGR", + pszFuncName, + "Invalid number of arguments"); + sqlite3_result_null (pContext); + return NULL; + } + + if( sqlite3_value_type (argv[0]) != SQLITE_TEXT ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "%s: %s(): %s", + "VirtualOGR", + pszFuncName, + "Invalid argument type"); + sqlite3_result_null (pContext); + return NULL; + } + + const char* pszVTableName = (const char*)sqlite3_value_text(argv[0]); + + OGR2SQLITEModule* poModule = + (OGR2SQLITEModule*) sqlite3_user_data(pContext); + + OGRLayer* poLayer = poModule->GetLayerForVTable(OGRSQLiteParamsUnquote(pszVTableName)); + if( poLayer == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "%s: %s(): %s", + "VirtualOGR", + pszFuncName, + "Unknown virtual table"); + sqlite3_result_null (pContext); + return NULL; + } + + return poLayer; +} + +/************************************************************************/ +/* OGR2SQLITE_ogr_layer_Extent() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_layer_Extent(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + OGRLayer* poLayer = OGR2SQLITE_GetLayer("ogr_layer_Extent", + pContext, argc, argv); + if( poLayer == NULL ) + return; + + OGR2SQLITEModule* poModule = + (OGR2SQLITEModule*) sqlite3_user_data(pContext); + + if( poLayer->GetGeomType() == wkbNone ) + { + sqlite3_result_null (pContext); + return; + } + + OGREnvelope sExtent; + if( poLayer->GetExtent(&sExtent) != OGRERR_NONE ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "%s: %s(): %s", + "VirtualOGR", + "ogr_layer_Extent", + "Cannot fetch layer extent"); + sqlite3_result_null (pContext); + return; + } + + OGRPolygon oPoly; + OGRLinearRing* poRing = new OGRLinearRing(); + oPoly.addRingDirectly(poRing); + poRing->addPoint(sExtent.MinX, sExtent.MinY); + poRing->addPoint(sExtent.MaxX, sExtent.MinY); + poRing->addPoint(sExtent.MaxX, sExtent.MaxY); + poRing->addPoint(sExtent.MinX, sExtent.MaxY); + poRing->addPoint(sExtent.MinX, sExtent.MinY); + + GByte* pabySLBLOB = NULL; + int nBLOBLen = 0; + int nSRID = poModule->FetchSRSId(poLayer->GetSpatialRef()); + if( OGRSQLiteLayer::ExportSpatiaLiteGeometry( + &oPoly, nSRID, wkbNDR, FALSE, + FALSE, FALSE, &pabySLBLOB, &nBLOBLen ) == CE_None ) + { + sqlite3_result_blob(pContext, pabySLBLOB, nBLOBLen, CPLFree); + } + else + { + sqlite3_result_null (pContext); + } +} + +/************************************************************************/ +/* OGR2SQLITE_ogr_layer_SRID() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_layer_SRID(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + OGRLayer* poLayer = OGR2SQLITE_GetLayer("OGR2SQLITE_ogr_layer_SRID", + pContext, argc, argv); + if( poLayer == NULL ) + return; + + OGR2SQLITEModule* poModule = + (OGR2SQLITEModule*) sqlite3_user_data(pContext); + + if( poLayer->GetGeomType() == wkbNone ) + { + sqlite3_result_null (pContext); + return; + } + + int nSRID = poModule->FetchSRSId(poLayer->GetSpatialRef()); + sqlite3_result_int(pContext, nSRID); +} + +/************************************************************************/ +/* OGR2SQLITE_ogr_layer_GeometryType() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_layer_GeometryType(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + OGRLayer* poLayer = OGR2SQLITE_GetLayer("OGR2SQLITE_ogr_layer_GeometryType", + pContext, argc, argv); + if( poLayer == NULL ) + return; + + OGRwkbGeometryType eType = poLayer->GetGeomType(); + + if( eType == wkbNone ) + { + sqlite3_result_null (pContext); + return; + } + + const char* psz2DName = OGRToOGCGeomType(eType); + if( wkbHasZ(eType) ) + sqlite3_result_text( pContext, CPLSPrintf("%s Z", psz2DName), -1, SQLITE_TRANSIENT ); + else + sqlite3_result_text( pContext, psz2DName, -1, SQLITE_TRANSIENT ); +} + +/************************************************************************/ +/* OGR2SQLITE_ogr_layer_FeatureCount() */ +/************************************************************************/ + +static +void OGR2SQLITE_ogr_layer_FeatureCount(sqlite3_context* pContext, + int argc, sqlite3_value** argv) +{ + OGRLayer* poLayer = OGR2SQLITE_GetLayer("OGR2SQLITE_ogr_layer_FeatureCount", + pContext, argc, argv); + if( poLayer == NULL ) + return; + + sqlite3_result_int64( pContext, poLayer->GetFeatureCount() ); +} + +/************************************************************************/ +/* OGR2SQLITEDestroyModule() */ +/************************************************************************/ + +static void OGR2SQLITEDestroyModule(void* pData) +{ + CPLDebug("OGR", "Unloading VirtualOGR module"); + delete (OGR2SQLITEModule*) pData; +} + +/* ENABLE_VIRTUAL_OGR_SPATIAL_INDEX is not defined */ +#ifdef ENABLE_VIRTUAL_OGR_SPATIAL_INDEX + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_vtab */ +/************************************************************************/ + +typedef struct +{ + /* Mandatory fields by sqlite3: don't change or reorder them ! */ + const sqlite3_module *pModule; + int nRef; + char *zErrMsg; + + /* Extension fields */ + char *pszVTableName; + OGR2SQLITEModule *poModule; + OGRDataSource *poDS; + int bCloseDS; + OGRLayer *poLayer; + int nMyRef; +} OGR2SQLITESpatialIndex_vtab; + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_vtab_cursor */ +/************************************************************************/ + +typedef struct +{ + /* Mandatory fields by sqlite3: don't change or reorder them ! */ + OGR2SQLITESpatialIndex_vtab *pVTab; + + /* Extension fields */ + OGRDataSource *poDupDataSource; + OGRLayer *poLayer; + OGRFeature *poFeature; + int bHasSetBounds; + double dfMinX, dfMinY, dfMaxX, dfMaxY; +} OGR2SQLITESpatialIndex_vtab_cursor; + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_ConnectCreate() */ +/************************************************************************/ + +static +int OGR2SQLITESpatialIndex_ConnectCreate(sqlite3* hDB, void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, char**pzErr) +{ + OGR2SQLITEModule* poModule = (OGR2SQLITEModule*) pAux; + OGRLayer* poLayer = NULL; + OGRDataSource* poDS = NULL; + int bCloseDS = FALSE; + int i; + +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "ConnectCreate(%s)", argv[2]); +#endif + + /*for(i=0;iGetDS(); + if( poDS == NULL ) + return SQLITE_ERROR; + + if( argc != 10 ) + { + *pzErr = sqlite3_mprintf( + "Expected syntax: CREATE VIRTUAL TABLE xxx USING " + "VirtualOGRSpatialIndex(ds_idx, layer_name, pkid, xmin, xmax, ymin, ymax)"); + return SQLITE_ERROR; + } + + int nDSIndex = atoi(argv[3]); + if( nDSIndex >= 0 ) + { + poDS = poModule->GetExtraDS(nDSIndex); + if( poDS == NULL ) + { + *pzErr = sqlite3_mprintf("Invalid dataset index : %d", nDSIndex); + return SQLITE_ERROR; + } + } + + poDS = (OGRDataSource*) OGROpen( poDS->GetName(), FALSE, NULL); + if( poDS == NULL ) + { + return SQLITE_ERROR; + } + bCloseDS = TRUE; + + CPLString osLayerName(OGRSQLiteParamsUnquote(argv[4])); + + poLayer = poDS->GetLayerByName(osLayerName); + if( poLayer == NULL ) + { + *pzErr = sqlite3_mprintf( "Cannot find layer '%s' in '%s'", + osLayerName.c_str(), poDS->GetName() ); + return SQLITE_ERROR; + } + + OGR2SQLITESpatialIndex_vtab* vtab = + (OGR2SQLITESpatialIndex_vtab*) CPLCalloc(1, sizeof(OGR2SQLITESpatialIndex_vtab)); + /* We dont need to fill the non-extended fields */ + vtab->pszVTableName = CPLStrdup(OGRSQLiteEscapeName(argv[2])); + vtab->poModule = poModule; + vtab->poDS = poDS; + vtab->bCloseDS = bCloseDS; + vtab->poLayer = poLayer; + vtab->nMyRef = 0; + + *ppVTab = (sqlite3_vtab*) vtab; + + CPLString osSQL; + osSQL = "CREATE TABLE "; + osSQL += "\""; + osSQL += OGRSQLiteEscapeName(argv[2]); + osSQL += "\""; + osSQL += "("; + + int bAddComma = FALSE; + + for(i=0;i<5;i++) + { + if( bAddComma ) + osSQL += ","; + bAddComma = TRUE; + + osSQL += "\""; + osSQL += OGRSQLiteEscapeName(OGRSQLiteParamsUnquote(argv[5+i])); + osSQL += "\""; + osSQL += " "; + osSQL += (i == 0) ? "INTEGER" : "FLOAT"; + } + + osSQL += ")"; + + CPLDebug("OGR2SQLITE", "sqlite3_declare_vtab(%s)", osSQL.c_str()); + if (sqlite3_declare_vtab (hDB, osSQL.c_str()) != SQLITE_OK) + { + *pzErr = sqlite3_mprintf("CREATE VIRTUAL: invalid SQL statement : %s", + osSQL.c_str()); + return SQLITE_ERROR; + } + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_BestIndex() */ +/************************************************************************/ + +static +int OGR2SQLITESpatialIndex_BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info* pIndex) +{ +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "BestIndex"); +#endif + + int i; + + int bMinX = FALSE, bMinY = FALSE, bMaxX = FALSE, bMaxY = FALSE; + + for (i = 0; i < pIndex->nConstraint; i++) + { + int iCol = pIndex->aConstraint[i].iColumn; + /* MinX */ + if( !bMinX && iCol == 1 && pIndex->aConstraint[i].usable && + (pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_LE || + pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_LT) ) + bMinX = TRUE; + /* MaxX */ + else if( !bMaxX && iCol == 2 && pIndex->aConstraint[i].usable && + (pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_GE || + pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_GT) ) + bMaxX = TRUE; + /* MinY */ + else if( !bMinY && iCol == 3 && pIndex->aConstraint[i].usable && + (pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_LE || + pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_LT) ) + bMinY = TRUE; + /* MaxY */ + else if( !bMaxY && iCol == 4 && pIndex->aConstraint[i].usable && + (pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_GE || + pIndex->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_GT) ) + bMaxY = TRUE; + else + break; + } + + if( bMinX && bMinY && bMaxX && bMaxY ) + { + CPLAssert( pIndex->nConstraint == 4 ); + + int nConstraints = 0; + for (i = 0; i < pIndex->nConstraint; i++) + { + pIndex->aConstraintUsage[i].argvIndex = nConstraints + 1; + pIndex->aConstraintUsage[i].omit = TRUE; + + nConstraints ++; + } + + int* panConstraints = (int*) + sqlite3_malloc( sizeof(int) * (1 + 2 * nConstraints) ); + panConstraints[0] = nConstraints; + + nConstraints = 0; + + for (i = 0; i < pIndex->nConstraint; i++) + { + if (pIndex->aConstraintUsage[i].omit) + { + panConstraints[2 * nConstraints + 1] = + pIndex->aConstraint[i].iColumn; + panConstraints[2 * nConstraints + 2] = + pIndex->aConstraint[i].op; + + nConstraints ++; + } + } + + pIndex->idxStr = (char *) panConstraints; + pIndex->needToFreeIdxStr = TRUE; + + pIndex->orderByConsumed = FALSE; + pIndex->idxNum = 0; + + return SQLITE_OK; + } + else + { + CPLDebug("OGR2SQLITE", "OGR2SQLITESpatialIndex_BestIndex: unhandled request"); + return SQLITE_ERROR; +/* + for (i = 0; i < pIndex->nConstraint; i++) + { + pIndex->aConstraintUsage[i].argvIndex = 0; + pIndex->aConstraintUsage[i].omit = FALSE; + } + + pIndex->idxStr = NULL; + pIndex->needToFreeIdxStr = FALSE; +*/ + } +} + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_DisconnectDestroy() */ +/************************************************************************/ + +static +int OGR2SQLITESpatialIndex_DisconnectDestroy(sqlite3_vtab *pVTab) +{ + OGR2SQLITESpatialIndex_vtab* pMyVTab = (OGR2SQLITESpatialIndex_vtab*) pVTab; + +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "DisconnectDestroy(%s)",pMyVTab->pszVTableName); +#endif + + sqlite3_free(pMyVTab->zErrMsg); + if( pMyVTab->bCloseDS ) + delete pMyVTab->poDS; + CPLFree(pMyVTab->pszVTableName); + CPLFree(pMyVTab); + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_Open() */ +/************************************************************************/ + +static +int OGR2SQLITESpatialIndex_Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) +{ + OGR2SQLITESpatialIndex_vtab* pMyVTab = (OGR2SQLITESpatialIndex_vtab*) pVTab; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Open(%s, %s)", + pMyVTab->poDS->GetName(), pMyVTab->poLayer->GetName()); +#endif + + OGRDataSource* poDupDataSource = NULL; + OGRLayer* poLayer = NULL; + + if( pMyVTab->nMyRef == 0 ) + { + poLayer = pMyVTab->poLayer; + } + else + { + poDupDataSource = + (OGRDataSource*) OGROpen(pMyVTab->poDS->GetName(), FALSE, NULL); + if( poDupDataSource == NULL ) + return SQLITE_ERROR; + poLayer = poDupDataSource->GetLayerByName( + pMyVTab->poLayer->GetName()); + if( poLayer == NULL ) + { + delete poDupDataSource; + return SQLITE_ERROR; + } + if( !poLayer->GetLayerDefn()-> + IsSame(pMyVTab->poLayer->GetLayerDefn()) ) + { + delete poDupDataSource; + return SQLITE_ERROR; + } + } + pMyVTab->nMyRef ++; + + OGR2SQLITESpatialIndex_vtab_cursor* pCursor = (OGR2SQLITESpatialIndex_vtab_cursor*) + CPLCalloc(1, sizeof(OGR2SQLITESpatialIndex_vtab_cursor)); + /* We dont need to fill the non-extended fields */ + *ppCursor = (sqlite3_vtab_cursor *)pCursor; + + pCursor->poDupDataSource = poDupDataSource; + pCursor->poLayer = poLayer; + pCursor->poLayer->ResetReading(); + pCursor->poFeature = NULL; + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_Close() */ +/************************************************************************/ + +static +int OGR2SQLITESpatialIndex_Close(sqlite3_vtab_cursor* pCursor) +{ + OGR2SQLITESpatialIndex_vtab_cursor* pMyCursor = (OGR2SQLITESpatialIndex_vtab_cursor*) pCursor; + OGR2SQLITESpatialIndex_vtab* pMyVTab = pMyCursor->pVTab; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Close(%s, %s)", + pMyVTab->poDS->GetName(), pMyVTab->poLayer->GetName()); +#endif + pMyVTab->nMyRef --; + + delete pMyCursor->poFeature; + delete pMyCursor->poDupDataSource; + + CPLFree(pCursor); + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_Filter() */ +/************************************************************************/ + +static +int OGR2SQLITESpatialIndex_Filter(sqlite3_vtab_cursor* pCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv) +{ + OGR2SQLITESpatialIndex_vtab_cursor* pMyCursor = (OGR2SQLITESpatialIndex_vtab_cursor*) pCursor; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Filter"); +#endif + + int* panConstraints = (int*) idxStr; + int nConstraints = panConstraints ? panConstraints[0] : 0; + + if( nConstraints != argc ) + return SQLITE_ERROR; + + int i; + double dfMinX = 0, dfMaxX = 0, dfMinY = 0, dfMaxY = 0; + for (i = 0; i < argc; i++) + { + int nCol = panConstraints[2 * i + 1]; + if( nCol < 0 ) + return SQLITE_ERROR; + + double dfVal; + if (sqlite3_value_type (argv[i]) == SQLITE_INTEGER) + dfVal = sqlite3_value_int64 (argv[i]); + else if (sqlite3_value_type (argv[i]) == SQLITE_FLOAT) + dfVal = sqlite3_value_double (argv[i]); + else + return SQLITE_ERROR; + + if( nCol == 1 ) + dfMaxX = dfVal; + else if( nCol == 2 ) + dfMinX = dfVal; + else if( nCol == 3 ) + dfMaxY = dfVal; + else if( nCol == 4 ) + dfMinY = dfVal; + else + return SQLITE_ERROR; + } + +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Spatial filter : %.18g, %.18g, %.18g, %.18g", + dfMinX, dfMinY, dfMaxX, dfMaxY); +#endif + + pMyCursor->poLayer->SetSpatialFilterRect(dfMinX, dfMinY, dfMaxX, dfMaxY); + pMyCursor->poLayer->ResetReading(); + + pMyCursor->poFeature = pMyCursor->poLayer->GetNextFeature(); + pMyCursor->bHasSetBounds = FALSE; + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_Next() */ +/************************************************************************/ + +static +int OGR2SQLITESpatialIndex_Next(sqlite3_vtab_cursor* pCursor) +{ + OGR2SQLITESpatialIndex_vtab_cursor* pMyCursor = (OGR2SQLITESpatialIndex_vtab_cursor*) pCursor; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Next"); +#endif + + delete pMyCursor->poFeature; + pMyCursor->poFeature = pMyCursor->poLayer->GetNextFeature(); + pMyCursor->bHasSetBounds = FALSE; + + return SQLITE_OK; +} + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_Eof() */ +/************************************************************************/ + +static +int OGR2SQLITESpatialIndex_Eof(sqlite3_vtab_cursor* pCursor) +{ + OGR2SQLITESpatialIndex_vtab_cursor* pMyCursor = (OGR2SQLITESpatialIndex_vtab_cursor*) pCursor; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Eof"); +#endif + + return (pMyCursor->poFeature == NULL); +} + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_Column() */ +/************************************************************************/ + +static +int OGR2SQLITESpatialIndex_Column(sqlite3_vtab_cursor* pCursor, + sqlite3_context* pContext, int nCol) +{ + OGR2SQLITESpatialIndex_vtab_cursor* pMyCursor = (OGR2SQLITESpatialIndex_vtab_cursor*) pCursor; + OGRFeature* poFeature; +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Column %d", nCol); +#endif + + poFeature = pMyCursor->poFeature; + if( poFeature == NULL) + return SQLITE_ERROR; + + if( nCol == 0 ) + { + CPLDebug("OGR2SQLITE", "--> FID = " CPL_FRMT_GIB, poFeature->GetFID()); + sqlite3_result_int64(pContext, poFeature->GetFID()); + return SQLITE_OK; + } + + if( !pMyCursor->bHasSetBounds ) + { + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if( poGeom != NULL && !poGeom->IsEmpty() ) + { + OGREnvelope sEnvelope; + poGeom->getEnvelope(&sEnvelope); + pMyCursor->bHasSetBounds = TRUE; + pMyCursor->dfMinX = sEnvelope.MinX; + pMyCursor->dfMinY = sEnvelope.MinY; + pMyCursor->dfMaxX = sEnvelope.MaxX; + pMyCursor->dfMaxY = sEnvelope.MaxY; + } + } + if( !pMyCursor->bHasSetBounds ) + { + sqlite3_result_null(pContext); + return SQLITE_OK; + } + + if( nCol == 1 ) + { + sqlite3_result_double(pContext, pMyCursor->dfMinX); + return SQLITE_OK; + } + if( nCol == 2 ) + { + sqlite3_result_double(pContext, pMyCursor->dfMaxX); + return SQLITE_OK; + } + if( nCol == 3 ) + { + sqlite3_result_double(pContext, pMyCursor->dfMinY); + return SQLITE_OK; + } + if( nCol == 4 ) + { + sqlite3_result_double(pContext, pMyCursor->dfMaxY); + return SQLITE_OK; + } + + return SQLITE_ERROR; +} + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_Rowid() */ +/************************************************************************/ + +static +int OGR2SQLITESpatialIndex_Rowid(sqlite3_vtab_cursor* pCursor, sqlite3_int64 *pRowid) +{ +#ifdef DEBUG_OGR2SQLITE + CPLDebug("OGR2SQLITE", "Rowid"); +#endif + + return SQLITE_ERROR; +} + +/************************************************************************/ +/* OGR2SQLITESpatialIndex_Rename() */ +/************************************************************************/ + +static +int OGR2SQLITESpatialIndex_Rename(sqlite3_vtab *pVtab, const char *zNew) +{ + //CPLDebug("OGR2SQLITE", "Rename"); + return SQLITE_ERROR; +} + +/************************************************************************/ +/* sOGR2SQLITESpatialIndex */ +/************************************************************************/ + +static const struct sqlite3_module sOGR2SQLITESpatialIndex = +{ + 1, /* iVersion */ + OGR2SQLITESpatialIndex_ConnectCreate, /* xCreate */ + OGR2SQLITESpatialIndex_ConnectCreate, /* xConnect */ + OGR2SQLITESpatialIndex_BestIndex, + OGR2SQLITESpatialIndex_DisconnectDestroy, /* xDisconnect */ + OGR2SQLITESpatialIndex_DisconnectDestroy, /* xDestroy */ + OGR2SQLITESpatialIndex_Open, + OGR2SQLITESpatialIndex_Close, + OGR2SQLITESpatialIndex_Filter, + OGR2SQLITESpatialIndex_Next, + OGR2SQLITESpatialIndex_Eof, + OGR2SQLITESpatialIndex_Column, + OGR2SQLITESpatialIndex_Rowid, + NULL, /* xUpdate */ + NULL, /* xBegin */ + NULL, /* xSync */ + NULL, /* xCommit */ + NULL, /* xFindFunctionRollback */ + NULL, /* xFindFunction */ + OGR2SQLITESpatialIndex_Rename +}; +#endif // ENABLE_VIRTUAL_OGR_SPATIAL_INDEX + +/************************************************************************/ +/* Setup() */ +/************************************************************************/ + +int OGR2SQLITEModule::Setup(sqlite3* hDB) +{ + int rc; + + this->hDB = hDB; + + rc = sqlite3_create_module_v2(hDB, "VirtualOGR", &sOGR2SQLITEModule, this, + OGR2SQLITEDestroyModule); + if( rc != SQLITE_OK ) + return FALSE; + +#ifdef ENABLE_VIRTUAL_OGR_SPATIAL_INDEX + rc = sqlite3_create_module(hDB, "VirtualOGRSpatialIndex", + &sOGR2SQLITESpatialIndex, this); + if( rc != SQLITE_OK ) + return FALSE; +#endif // ENABLE_VIRTUAL_OGR_SPATIAL_INDEX + + rc= sqlite3_create_function(hDB, "ogr_layer_Extent", 1, SQLITE_ANY, this, + OGR2SQLITE_ogr_layer_Extent, NULL, NULL); + if( rc != SQLITE_OK ) + return FALSE; + + rc= sqlite3_create_function(hDB, "ogr_layer_SRID", 1, SQLITE_ANY, this, + OGR2SQLITE_ogr_layer_SRID, NULL, NULL); + if( rc != SQLITE_OK ) + return FALSE; + + rc= sqlite3_create_function(hDB, "ogr_layer_GeometryType", 1, SQLITE_ANY, this, + OGR2SQLITE_ogr_layer_GeometryType, NULL, NULL); + if( rc != SQLITE_OK ) + return FALSE; + + rc= sqlite3_create_function(hDB, "ogr_layer_FeatureCount", 1, SQLITE_ANY, this, + OGR2SQLITE_ogr_layer_FeatureCount, NULL, NULL); + if( rc != SQLITE_OK ) + return FALSE; + + SetHandleSQLFunctions(OGRSQLiteRegisterSQLFunctions(hDB)); + + return TRUE; +} + +/************************************************************************/ +/* OGR2SQLITE_Setup() */ +/************************************************************************/ + +OGR2SQLITEModule* OGR2SQLITE_Setup(GDALDataset* poDS, + OGRSQLiteDataSource* poSQLiteDS) +{ + OGR2SQLITEModule* poModule = new OGR2SQLITEModule(); + poModule->Setup(poDS, poSQLiteDS); + return poModule; +} + +/************************************************************************/ +/* OGR2SQLITE_AddExtraDS() */ +/************************************************************************/ + +int OGR2SQLITE_AddExtraDS(OGR2SQLITEModule* poModule, OGRDataSource* poDS) +{ + return poModule->AddExtraDS(poDS); +} + +#ifdef VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED + +/************************************************************************/ +/* sqlite3_extension_init() */ +/************************************************************************/ + +CPL_C_START +int CPL_DLL sqlite3_extension_init (sqlite3 * hDB, char **pzErrMsg, + const sqlite3_api_routines * pApi); +CPL_C_END + +/* Entry point for dynamically loaded extension (typically called by load_extension()) */ +int sqlite3_extension_init (sqlite3 * hDB, char **pzErrMsg, + const sqlite3_api_routines * pApi) +{ + CPLDebug("OGR", "OGR SQLite extension loading..."); + + SQLITE_EXTENSION_INIT2(pApi); + + *pzErrMsg = NULL; + + OGRRegisterAll(); + + OGR2SQLITEModule* poModule = new OGR2SQLITEModule(); + if( poModule->Setup(hDB) ) + { + CPLDebug("OGR", "OGR SQLite extension loaded"); + return SQLITE_OK; + } + else + return SQLITE_ERROR; +} + +#endif // VIRTUAL_OGR_DYNAMIC_EXTENSION_ENABLED + +/************************************************************************/ +/* OGR2SQLITE_static_register() */ +/************************************************************************/ + +#ifndef WIN32 +extern const struct sqlite3_api_routines OGRSQLITE_static_routines; +#endif + +int OGR2SQLITE_static_register (sqlite3 * hDB, char **pzErrMsg, void * _pApi) +{ + const sqlite3_api_routines * pApi = (const sqlite3_api_routines * )_pApi; +#ifndef WIN32 + if( pApi->create_module == NULL ) + { + pApi = &OGRSQLITE_static_routines; + } +#endif + SQLITE_EXTENSION_INIT2 (pApi); + + *pzErrMsg = NULL; + + /* The config option is turned off by ogrsqliteexecutesql.cpp that needs */ + /* to create a custom module */ + if( CSLTestBoolean(CPLGetConfigOption("OGR_SQLITE_STATIC_VIRTUAL_OGR", "YES")) ) + { + /* Can happen if sqlite is compiled with SQLITE_OMIT_LOAD_EXTENSION (with sqlite 3.6.10 for example) */ + /* We return here OK since it is not vital for regular SQLite dababases */ + /* to load the OGR SQL functions */ + if( pApi->create_module == NULL ) + return SQLITE_OK; + + OGR2SQLITEModule* poModule = new OGR2SQLITEModule(); + return poModule->Setup(hDB) ? SQLITE_OK : SQLITE_ERROR; + } + else + { + /* Can happen if sqlite is compiled with SQLITE_OMIT_LOAD_EXTENSION (with sqlite 3.6.10 for example) */ + /* We return fail since Setup() will later be called, and crash */ + /* if create_module isn't available */ + if( pApi->create_module == NULL ) + return SQLITE_ERROR; + } + + return SQLITE_OK; +} + +#endif // HAVE_SQLITE_VFS diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitevirtualogr.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitevirtualogr.h new file mode 100644 index 000000000..2bf143366 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/ogrsqlitevirtualogr.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * $Id: ogrsqlitevirtualogr.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: SQLite Virtual Table module using OGR layers + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SQLITE_VIRTUAL_OGR_H_INCLUDED +#define _OGR_SQLITE_VIRTUAL_OGR_H_INCLUDED + +#include "ogr_sqlite.h" + +#ifdef HAVE_SQLITE_VFS + +class OGR2SQLITEModule; + +OGR2SQLITEModule* OGR2SQLITE_Setup(GDALDataset* poDS, + OGRSQLiteDataSource* poSQLiteDS); + +int OGR2SQLITE_AddExtraDS(OGR2SQLITEModule* poModule, OGRDataSource* poDS); + +void OGR2SQLITE_Register(); + +CPLString OGR2SQLITE_GetNameForGeometryColumn(OGRLayer* poLayer); + +#endif // HAVE_SQLITE_VFS + +#endif // _OGR_SQLITE_VIRTUAL_OGR_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/test_load_virtual_ogr.c b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/test_load_virtual_ogr.c new file mode 100644 index 000000000..4a936e81a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sqlite/test_load_virtual_ogr.c @@ -0,0 +1,193 @@ +/****************************************************************************** + * $Id: test_load_virtual_ogr.c 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Test dynamic loading of SQLite Virtual Table module using OGR layers + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifdef HAVE_SPATIALITE + #ifdef SPATIALITE_AMALGAMATION + /* + / using an AMALGAMATED version of SpatiaLite + / a private internal copy of SQLite is included: + / so we are required including the SpatiaLite's + / own header + / + / IMPORTANT NOTICE: using AMALAGATION is only + / useful on Windows (to skip DLL hell related oddities) + */ + #include + #else + /* + / You MUST NOT use AMALGAMATION on Linux or any + / other "sane" operating system !!!! + */ + #include "sqlite3.h" + #endif +#else +#include "sqlite3.h" +#endif + +#include +#include + +#undef NDEBUG +#include + +#ifndef _MSC_VER +#include +#endif + +#if _MSC_VER +#define snprintf _snprintf +#endif + +int main(int argc, char* argv[]) +{ + sqlite3* db = NULL; + char* pszErrMsg = NULL; + char** papszResult = NULL; + int nRowCount = 0, nColCount = 0; + int rc; + char szBuffer[256]; + + if( argc < 2 || argc > 4 ) + { + fprintf(stderr, "Usage: test_load_virtual_ogr libgdal.so|gdalXX.dll [datasource_name] [layer_name]\n"); + exit(1); + } + + sqlite3_open("tmp.db", &db); + if( db == NULL ) + { + fprintf(stderr, "cannot open DB.\n"); + exit(1); + } + + rc = sqlite3_enable_load_extension(db, 1); + if( rc != SQLITE_OK ) + { + fprintf(stderr, "sqlite3_enable_load_extension() failed\n"); + sqlite3_close(db); + unlink("tmp.db"); + exit(1); + } + + rc = sqlite3_load_extension(db, argv[1], NULL, &pszErrMsg); + if( rc != SQLITE_OK ) + { + fprintf(stderr, "sqlite3_load_extension(%s) failed: %s\n", argv[1], pszErrMsg); + sqlite3_free( pszErrMsg ); + sqlite3_close(db); + unlink("tmp.db"); + exit(1); + } + + rc = sqlite3_get_table( db, "SELECT ogr_version()", + &papszResult, &nRowCount, &nColCount, + &pszErrMsg ); + + if( rc == SQLITE_OK && nRowCount == 1 && nColCount == 1 ) + { + printf("SELECT ogr_version() returned : %s\n", papszResult[1]); + } + else + { + fprintf(stderr, "SELECT ogr_version() failed: %s\n", pszErrMsg); + sqlite3_free( pszErrMsg ); + sqlite3_close(db); + unlink("tmp.db"); + exit(1); + } + sqlite3_free_table(papszResult); + + if( argc >= 3 ) + { + if( argc == 3 ) + { + snprintf(szBuffer, sizeof(szBuffer), + "CREATE VIRTUAL TABLE foo USING VirtualOGR('%s')", + argv[2]); + } + else + { + snprintf(szBuffer, sizeof(szBuffer), + "CREATE VIRTUAL TABLE foo USING VirtualOGR('%s', 0, '%s', 1)", + argv[2], argv[3]); + } + + rc = sqlite3_exec(db, szBuffer, NULL, NULL, &pszErrMsg); + if( rc != SQLITE_OK ) + { + fprintf(stderr, "%s failed: %s\n", szBuffer, pszErrMsg); + sqlite3_free( pszErrMsg ); + sqlite3_close(db); + unlink("tmp.db"); + exit(1); + } + else + { + if( argc == 3 ) + printf("Managed to open '%s'\n", argv[2]); + else + printf("Managed to open '%s':'%s'\n", argv[2], argv[3]); + + assert(SQLITE_OK == sqlite3_exec(db, "CREATE TABLE spy_table (spy_content VARCHAR)", NULL, NULL, NULL)); + + assert(SQLITE_OK == sqlite3_exec(db, "CREATE TABLE regular_table (bar VARCHAR)", NULL, NULL, NULL)); + + assert(SQLITE_OK == sqlite3_exec(db, "CREATE TRIGGER spy_trigger INSERT ON regular_table BEGIN " + "INSERT OR REPLACE INTO spy_table (spy_content) " + "SELECT OGR_STYLE FROM foo; END;", NULL, NULL, NULL)); + + sqlite3_close(db); + db = NULL; + + sqlite3_open("tmp.db", &db); + if( db == NULL ) + { + fprintf(stderr, "cannot reopen DB.\n"); + unlink("tmp.db"); + exit(1); + } + + assert(SQLITE_OK == sqlite3_enable_load_extension(db, 1)); + + assert(SQLITE_OK == sqlite3_load_extension(db, argv[1], NULL, NULL)); + + pszErrMsg = NULL; + assert(SQLITE_OK != sqlite3_exec(db, "INSERT INTO regular_table (bar) VALUES ('bar')", NULL, NULL, &pszErrMsg)); + + fprintf(stderr, "Expected error. We got : %s\n", pszErrMsg); + + sqlite3_free(pszErrMsg); + } + } + + sqlite3_close(db); + unlink("tmp.db"); + + return 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/GNUmakefile new file mode 100644 index 000000000..98262f0b5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrsuadriver.o ogrsuadatasource.o ogrsualayer.o + +CPPFLAGS := -I.. -I../.. -I../xplane $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_sua.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/drv_sua.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/drv_sua.html new file mode 100644 index 000000000..fe7071219 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/drv_sua.html @@ -0,0 +1,26 @@ + + +SUA - Tim Newport-Peace's Special Use Airspace Format + + + + +

    SUA - Tim Newport-Peace's Special Use Airspace Format

    + +(GDAL/OGR >= 1.8.0)

    + +This driver reads files describing Special Use Airspaces in the Tim Newport-Peace's .SUA format

    + +Airspace are returned as features of a single layer, with a geometry of type Polygon and the following fields : +TYPE, CLASS, TITLE, TOPS, BASE.

    + +Airspace geometries made of arcs will be tesselated.

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/makefile.vc new file mode 100644 index 000000000..8094f9087 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrsuadriver.obj ogrsuadatasource.obj ogrsualayer.obj +EXTRAFLAGS = -I.. -I..\.. -I..\xplane + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/ogr_sua.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/ogr_sua.h new file mode 100644 index 000000000..a6b3a75cd --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/ogr_sua.h @@ -0,0 +1,91 @@ +/****************************************************************************** + * $Id: ogr_sua.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: SUA Translator + * Purpose: Definition of classes for OGR .sua driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SUA_H_INCLUDED +#define _OGR_SUA_H_INCLUDED + +#include "ogrsf_frmts.h" + +/************************************************************************/ +/* OGRSUALayer */ +/************************************************************************/ + +class OGRSUALayer : public OGRLayer +{ + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference *poSRS; + + VSILFILE* fpSUA; + int bEOF; + int bHasLastLine; + CPLString osLastLine; + + int nNextFID; + + OGRFeature * GetNextRawFeature(); + + public: + OGRSUALayer(VSILFILE* fp); + ~OGRSUALayer(); + + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poFeatureDefn; } + + virtual int TestCapability( const char * ); +}; + +/************************************************************************/ +/* OGRSUADataSource */ +/************************************************************************/ + +class OGRSUADataSource : public OGRDataSource +{ + char* pszName; + + OGRLayer** papoLayers; + int nLayers; + + public: + OGRSUADataSource(); + ~OGRSUADataSource(); + + int Open( const char * pszFilename ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); +}; + +#endif /* ndef _OGR_SUA_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/ogrsuadatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/ogrsuadatasource.cpp new file mode 100644 index 000000000..242583688 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/ogrsuadatasource.cpp @@ -0,0 +1,103 @@ +/****************************************************************************** + * $Id: ogrsuadatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: SUA Translator + * Purpose: Implements OGRSUADataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sua.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrsuadatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRSUADataSource() */ +/************************************************************************/ + +OGRSUADataSource::OGRSUADataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; +} + +/************************************************************************/ +/* ~OGRSUADataSource() */ +/************************************************************************/ + +OGRSUADataSource::~OGRSUADataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + CPLFree( pszName ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSUADataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRSUADataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRSUADataSource::Open( const char * pszFilename ) + +{ + pszName = CPLStrdup( pszFilename ); + + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return FALSE; + + nLayers = 1; + papoLayers = (OGRLayer**) CPLMalloc(sizeof(OGRLayer*)); + papoLayers[0] = new OGRSUALayer(fp); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/ogrsuadriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/ogrsuadriver.cpp new file mode 100644 index 000000000..9e6659243 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/ogrsuadriver.cpp @@ -0,0 +1,129 @@ +/****************************************************************************** + * $Id: ogrsuadriver.cpp 29253 2015-05-27 08:49:16Z rouault $ + * + * Project: SUA Translator + * Purpose: Implements OGRSUADriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sua.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrsuadriver.cpp 29253 2015-05-27 08:49:16Z rouault $"); + +extern "C" void RegisterOGRSUA(); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRSUADriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->eAccess == GA_Update || + poOpenInfo->fpL == NULL || + !poOpenInfo->TryToIngest(10000) ) + return NULL; + + int bIsSUA = ( strstr((const char*)poOpenInfo->pabyHeader, "\nTYPE=") != NULL && + strstr((const char*)poOpenInfo->pabyHeader, "\nTITLE=") != NULL && + (strstr((const char*)poOpenInfo->pabyHeader, "\nPOINT=") != NULL || + strstr((const char*)poOpenInfo->pabyHeader, "\nCIRCLE ") != NULL)); + if( !bIsSUA ) + { + /* Some files such http://soaringweb.org/Airspace/CZ/CZ_combined_2014_05_01.sua */ + /* have very long comments in the header, so we will have to check */ + /* further, but only do this is we have a hint that the file might be */ + /* a candidate */ + int nLen = poOpenInfo->nHeaderBytes; + if( nLen < 10000 ) + return NULL; + /* Check the 'Airspace' word in the header */ + if( strstr((const char*)poOpenInfo->pabyHeader, "Airspace") == NULL ) + return NULL; + // Check that the header is at least UTF-8 + // but do not take into account partial UTF-8 characters at the end + int nTruncated = 0; + while(nLen > 0) + { + if( (poOpenInfo->pabyHeader[nLen-1] & 0xc0) != 0x80 ) + { + break; + } + nLen --; + nTruncated ++; + if( nTruncated == 7 ) + return NULL; + } + if( !CPLIsUTF8((const char*)poOpenInfo->pabyHeader, nLen) ) + return NULL; + if( !poOpenInfo->TryToIngest(30000) ) + return NULL; + bIsSUA = ( strstr((const char*)poOpenInfo->pabyHeader, "\nTYPE=") != NULL && + strstr((const char*)poOpenInfo->pabyHeader, "\nTITLE=") != NULL && + (strstr((const char*)poOpenInfo->pabyHeader, "\nPOINT=") != NULL || + strstr((const char*)poOpenInfo->pabyHeader, "\nCIRCLE ") != NULL) ); + if( !bIsSUA ) + return NULL; + } + + OGRSUADataSource *poDS = new OGRSUADataSource(); + + if( !poDS->Open( poOpenInfo->pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRSUA() */ +/************************************************************************/ + +void RegisterOGRSUA() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "SUA" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SUA" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Tim Newport-Peace's Special Use Airspace Format" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_sua.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRSUADriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/ogrsualayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/ogrsualayer.cpp new file mode 100644 index 000000000..a3df45659 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sua/ogrsualayer.cpp @@ -0,0 +1,368 @@ +/****************************************************************************** + * $Id: ogrsualayer.cpp 27942 2014-11-11 00:57:41Z rouault $ + * + * Project: SUA Translator + * Purpose: Implements OGRSUALayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_sua.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "ogr_xplane_geo_utils.h" +#include "ogr_srs_api.h" + +CPL_CVSID("$Id: ogrsualayer.cpp 27942 2014-11-11 00:57:41Z rouault $"); + +/************************************************************************/ +/* OGRSUALayer() */ +/************************************************************************/ + +OGRSUALayer::OGRSUALayer( VSILFILE* fp ) + +{ + fpSUA = fp; + nNextFID = 0; + bEOF = FALSE; + bHasLastLine = FALSE; + + poSRS = new OGRSpatialReference(SRS_WKT_WGS84); + + poFeatureDefn = new OGRFeatureDefn( "layer" ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPolygon ); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + OGRFieldDefn oField1( "TYPE", OFTString); + poFeatureDefn->AddFieldDefn( &oField1 ); + OGRFieldDefn oField2( "CLASS", OFTString); + poFeatureDefn->AddFieldDefn( &oField2 ); + OGRFieldDefn oField3( "TITLE", OFTString); + poFeatureDefn->AddFieldDefn( &oField3 ); + OGRFieldDefn oField4( "TOPS", OFTString); + poFeatureDefn->AddFieldDefn( &oField4 ); + OGRFieldDefn oField5( "BASE", OFTString); + poFeatureDefn->AddFieldDefn( &oField5 ); +} + +/************************************************************************/ +/* ~OGRSUALayer() */ +/************************************************************************/ + +OGRSUALayer::~OGRSUALayer() + +{ + if( poSRS != NULL ) + poSRS->Release(); + + poFeatureDefn->Release(); + + VSIFCloseL( fpSUA ); +} + + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRSUALayer::ResetReading() + +{ + nNextFID = 0; + bEOF = FALSE; + bHasLastLine = FALSE; + VSIFSeekL( fpSUA, 0, SEEK_SET ); +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRSUALayer::GetNextFeature() +{ + OGRFeature *poFeature; + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + + + +/************************************************************************/ +/* GetLatLon() */ +/************************************************************************/ + +static int GetLatLon(const char* pszStr, double& dfLat, double& dfLon) +{ + if (pszStr[7] != ' ') + return FALSE; + if (pszStr[0] != 'N' && pszStr[0] != 'S') + return FALSE; + if (pszStr[8] != 'E' && pszStr[8] != 'W') + return FALSE; + + char szDeg[4], szMin[3], szSec[3]; + szDeg[0] = pszStr[1]; + szDeg[1] = pszStr[2]; + szDeg[2] = 0; + szMin[0] = pszStr[3]; + szMin[1] = pszStr[4]; + szMin[2] = 0; + szSec[0] = pszStr[5]; + szSec[1] = pszStr[6]; + szSec[2] = 0; + + dfLat = atoi(szDeg) + atoi(szMin) / 60. + atoi(szSec) / 3600.; + if (pszStr[0] == 'S') + dfLat = -dfLat; + + szDeg[0] = pszStr[9]; + szDeg[1] = pszStr[10]; + szDeg[2] = pszStr[11]; + szDeg[3] = 0; + szMin[0] = pszStr[12]; + szMin[1] = pszStr[13]; + szMin[2] = 0; + szSec[0] = pszStr[14]; + szSec[1] = pszStr[15]; + szSec[2] = 0; + + dfLon = atoi(szDeg) + atoi(szMin) / 60. + atoi(szSec) / 3600.; + if (pszStr[8] == 'W') + dfLon = -dfLon; + + return TRUE; +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRSUALayer::GetNextRawFeature() +{ + const char* pszLine; + CPLString osTYPE, osCLASS, osTITLE, osTOPS, osBASE; + OGRLinearRing oLR; + double dfLastLat = 0, dfLastLon = 0; + int bFirst = TRUE; + + if (bEOF) + return NULL; + + while(TRUE) + { + if (bFirst && bHasLastLine) + { + pszLine = osLastLine.c_str(); + bFirst = FALSE; + } + else + { + pszLine = CPLReadLine2L(fpSUA, 1024, NULL); + if (pszLine == NULL) + { + bEOF = TRUE; + if (oLR.getNumPoints() == 0) + return NULL; + break; + } + osLastLine = pszLine; + bHasLastLine = TRUE; + } + + if (pszLine[0] == '#' || pszLine[0] == '\0') + continue; + + if (EQUALN(pszLine, "TYPE=", 5)) + { + if (osTYPE.size() != 0) + break; + osTYPE = pszLine + 5; + } + else if (EQUALN(pszLine, "CLASS=", 6)) + { + if (osCLASS.size() != 0) + break; + osCLASS = pszLine + 6; + } + else if (EQUALN(pszLine, "TITLE=", 6)) + { + if (osTITLE.size() != 0) + break; + osTITLE = pszLine + 6; + } + else if (EQUALN(pszLine, "TOPS=", 5)) + osTOPS = pszLine + 5; + else if (EQUALN(pszLine, "BASE=", 5)) + osBASE = pszLine + 5; + else if (EQUALN(pszLine, "POINT=", 6)) + { + pszLine += 6; + if (strlen(pszLine) != 16) + continue; + + double dfLat, dfLon; + if (!GetLatLon(pszLine, dfLat, dfLon)) + continue; + + oLR.addPoint(dfLon, dfLat); + dfLastLat = dfLat; + dfLastLon = dfLon; + } + else if (EQUALN(pszLine, "CLOCKWISE", 9) || EQUALN(pszLine, "ANTI-CLOCKWISE", 14)) + { + if (oLR.getNumPoints() == 0) + continue; + + int bClockWise = EQUALN(pszLine, "CLOCKWISE", 9); + + /*const char* pszRADIUS = strstr(pszLine, "RADIUS="); + if (pszRADIUS == NULL) + continue; + double dfRADIUS = CPLAtof(pszRADIUS + 7) * 1852;*/ + + const char* pszCENTRE = strstr(pszLine, "CENTRE="); + if (pszCENTRE == NULL) + continue; + pszCENTRE += 7; + if (strlen(pszCENTRE) < 17 || pszCENTRE[16] != ' ') + continue; + double dfCenterLat, dfCenterLon; + if (!GetLatLon(pszCENTRE, dfCenterLat, dfCenterLon)) + continue; + + const char* pszTO = strstr(pszLine, "TO="); + if (pszTO == NULL) + continue; + pszTO += 3; + if (strlen(pszTO) != 16) + continue; + double dfToLat, dfToLon; + if (!GetLatLon(pszTO, dfToLat, dfToLon)) + continue; + + double dfStartDistance = OGRXPlane_Distance(dfCenterLat, dfCenterLon, dfLastLat, dfLastLon); + double dfEndDistance = OGRXPlane_Distance(dfCenterLat, dfCenterLon, dfToLat, dfToLon); + double dfStartAngle = OGRXPlane_Track(dfCenterLat, dfCenterLon, dfLastLat, dfLastLon); + double dfEndAngle = OGRXPlane_Track(dfCenterLat, dfCenterLon, dfToLat, dfToLon); + if (bClockWise && dfEndAngle < dfStartAngle) + dfEndAngle += 360; + else if (!bClockWise && dfStartAngle < dfEndAngle) + dfEndAngle -= 360; + + int nSign = (bClockWise) ? 1 : -1; + double dfAngle; + for(dfAngle = dfStartAngle; (dfAngle - dfEndAngle) * nSign < 0; dfAngle += nSign) + { + double dfLat, dfLon; + double pct = (dfAngle - dfStartAngle) / (dfEndAngle - dfStartAngle); + double dfDist = dfStartDistance * (1-pct) + dfEndDistance * pct; + OGRXPlane_ExtendPosition(dfCenterLat, dfCenterLon, dfDist, dfAngle, &dfLat, &dfLon); + oLR.addPoint(dfLon, dfLat); + } + oLR.addPoint(dfToLon, dfToLat); + + dfLastLat = oLR.getY(oLR.getNumPoints() - 1); + dfLastLon = oLR.getX(oLR.getNumPoints() - 1); + } + else if (EQUALN(pszLine, "CIRCLE", 6)) + { + const char* pszRADIUS = strstr(pszLine, "RADIUS="); + if (pszRADIUS == NULL) + continue; + double dfRADIUS = CPLAtof(pszRADIUS + 7) * 1852; + + const char* pszCENTRE = strstr(pszLine, "CENTRE="); + if (pszCENTRE == NULL) + continue; + pszCENTRE += 7; + if (strlen(pszCENTRE) != 16) + continue; + double dfCenterLat, dfCenterLon; + if (!GetLatLon(pszCENTRE, dfCenterLat, dfCenterLon)) + continue; + + double dfAngle; + double dfLat, dfLon; + for(dfAngle = 0; dfAngle < 360; dfAngle += 1) + { + OGRXPlane_ExtendPosition(dfCenterLat, dfCenterLon, dfRADIUS, dfAngle, &dfLat, &dfLon); + oLR.addPoint(dfLon, dfLat); + } + OGRXPlane_ExtendPosition(dfCenterLat, dfCenterLon, dfRADIUS, 0, &dfLat, &dfLon); + oLR.addPoint(dfLon, dfLat); + + dfLastLat = oLR.getY(oLR.getNumPoints() - 1); + dfLastLon = oLR.getX(oLR.getNumPoints() - 1); + } + else if (EQUALN(pszLine, "INCLUDE", 7) || EQUALN(pszLine, "END", 3)) + { + } + else + { + CPLDebug("SUA", "Unexpected content : %s", pszLine); + } + } + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField(0, osTYPE.c_str()); + poFeature->SetField(1, osCLASS.c_str()); + poFeature->SetField(2, osTITLE.c_str()); + poFeature->SetField(3, osTOPS.c_str()); + poFeature->SetField(4, osBASE.c_str()); + + OGRPolygon* poPoly = new OGRPolygon(); + poPoly->assignSpatialReference(poSRS); + oLR.closeRings(); + poPoly->addRing(&oLR); + poFeature->SetGeometryDirectly(poPoly); + poFeature->SetFID(nNextFID++); + + return poFeature; +} +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSUALayer::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/GNUmakefile new file mode 100644 index 000000000..e9144b52c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/GNUmakefile @@ -0,0 +1,19 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrsvgdriver.o ogrsvgdatasource.o ogrsvglayer.o + +ifeq ($(HAVE_EXPAT),yes) +CPPFLAGS += -DHAVE_EXPAT +endif + +CPPFLAGS := -I.. -I../.. $(EXPAT_INCLUDE) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_svg.h + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/drv_svg.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/drv_svg.html new file mode 100644 index 000000000..b8c642ab3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/drv_svg.html @@ -0,0 +1,34 @@ + + +SVG - Scalable Vector Graphics + + + + +

    SVG - Scalable Vector Graphics

    + +(OGR >= 1.9.0)

    + +OGR has support for SVG reading (if GDAL is built with expat library support).

    + +Currently, it will only read SVG files that are the output from Cloudmade Vector Stream Server

    + +All coordinates are relative to the Pseudo-mercator SRS (EPSG:3857).

    + +The driver will return 3 layers : +

      +
    • points
    • +
    • lines
    • +
    • polygons
    • +
    +

    + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/makefile.vc new file mode 100644 index 000000000..26b101a21 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/makefile.vc @@ -0,0 +1,18 @@ + +OBJ = ogrsvgdriver.obj ogrsvgdatasource.obj ogrsvglayer.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF EXPAT_DIR +EXTRAFLAGS = -I.. -I..\.. $(EXPAT_INCLUDE) -DHAVE_EXPAT=1 +!ELSE +EXTRAFLAGS = -I.. -I..\.. +!ENDIF + +default: $(OBJ) + +clean: + -del *.obj *.pdb + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/ogr_svg.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/ogr_svg.h new file mode 100644 index 000000000..5fe49ee07 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/ogr_svg.h @@ -0,0 +1,171 @@ +/****************************************************************************** + * $Id: ogr_svg.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: SVG Translator + * Purpose: Definition of classes for OGR .svg driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SVG_H_INCLUDED +#define _OGR_SVG_H_INCLUDED + +#include "ogrsf_frmts.h" + +#ifdef HAVE_EXPAT +#include "ogr_expat.h" +#endif + +class OGRSVGDataSource; + +typedef enum +{ + SVG_POINTS, + SVG_LINES, + SVG_POLYGONS, +} SVGGeometryType; + +/************************************************************************/ +/* OGRSVGLayer */ +/************************************************************************/ + +class OGRSVGLayer : public OGRLayer +{ + OGRFeatureDefn* poFeatureDefn; + OGRSpatialReference *poSRS; + OGRSVGDataSource* poDS; + CPLString osLayerName; + + SVGGeometryType svgGeomType; + + int nTotalFeatures; + int nNextFID; + VSILFILE* fpSVG; /* Large file API */ + +#ifdef HAVE_EXPAT + XML_Parser oParser; + XML_Parser oSchemaParser; +#endif + char* pszSubElementValue; + int nSubElementValueLen; + int iCurrentField; + + OGRFeature* poFeature; + OGRFeature ** ppoFeatureTab; + int nFeatureTabLength; + int nFeatureTabIndex; + + int depthLevel; + int interestingDepthLevel; + int inInterestingElement; + + int bStopParsing; +#ifdef HAVE_EXPAT + int nWithoutEventCounter; + int nDataHandlerCounter; + + OGRSVGLayer *poCurLayer; +#endif + + private: + void LoadSchema(); + + public: + OGRSVGLayer(const char *pszFilename, + const char* layerName, + SVGGeometryType svgGeomType, + OGRSVGDataSource* poDS); + ~OGRSVGLayer(); + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual const char* GetName() { return osLayerName.c_str(); } + virtual OGRwkbGeometryType GetGeomType(); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + + virtual OGRFeatureDefn * GetLayerDefn(); + + virtual int TestCapability( const char * ); + +#ifdef HAVE_EXPAT + void startElementCbk(const char *pszName, const char **ppszAttr); + void endElementCbk(const char *pszName); + void dataHandlerCbk(const char *data, int nLen); + + void startElementLoadSchemaCbk(const char *pszName, const char **ppszAttr); + void endElementLoadSchemaCbk(const char *pszName); + void dataHandlerLoadSchemaCbk(const char *data, int nLen); +#endif +}; + +/************************************************************************/ +/* OGRSVGDataSource */ +/************************************************************************/ + +typedef enum +{ + SVG_VALIDITY_UNKNOWN, + SVG_VALIDITY_INVALID, + SVG_VALIDITY_VALID +} OGRSVGValidity; + +class OGRSVGDataSource : public OGRDataSource +{ + char* pszName; + + OGRSVGLayer** papoLayers; + int nLayers; + +#ifdef HAVE_EXPAT + OGRSVGValidity eValidity; +#endif + int bIsCloudmade; + +#ifdef HAVE_EXPAT + XML_Parser oCurrentParser; + int nDataHandlerCounter; +#endif + + public: + OGRSVGDataSource(); + ~OGRSVGDataSource(); + + int Open( const char * pszFilename ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); + + +#ifdef HAVE_EXPAT + void startElementValidateCbk(const char *pszName, const char **ppszAttr); + void dataHandlerValidateCbk(const char *data, int nLen); +#endif +}; + +#endif /* ndef _OGR_SVG_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/ogrsvgdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/ogrsvgdatasource.cpp new file mode 100644 index 000000000..51a57f509 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/ogrsvgdatasource.cpp @@ -0,0 +1,273 @@ +/****************************************************************************** + * $Id: ogrsvgdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: SVG Translator + * Purpose: Implements OGRSVGDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_svg.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrsvgdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRSVGDataSource() */ +/************************************************************************/ + +OGRSVGDataSource::OGRSVGDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + bIsCloudmade = FALSE; + + pszName = NULL; +} + +/************************************************************************/ +/* ~OGRSVGDataSource() */ +/************************************************************************/ + +OGRSVGDataSource::~OGRSVGDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + CPLFree( pszName ); +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRSVGDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +#ifdef HAVE_EXPAT + +/************************************************************************/ +/* startElementValidateCbk() */ +/************************************************************************/ + +void OGRSVGDataSource::startElementValidateCbk(const char *pszName, + const char **ppszAttr) +{ + if (eValidity == SVG_VALIDITY_UNKNOWN) + { + if (strcmp(pszName, "svg") == 0) + { + int i; + eValidity = SVG_VALIDITY_VALID; + for(i=0; ppszAttr[i] != NULL; i+= 2) + { + if (strcmp(ppszAttr[i], "xmlns:cm") == 0 && + strcmp(ppszAttr[i+1], "http://cloudmade.com/") == 0) + { + bIsCloudmade = TRUE; + break; + } + } + } + else + { + eValidity = SVG_VALIDITY_INVALID; + } + } +} + + +/************************************************************************/ +/* dataHandlerValidateCbk() */ +/************************************************************************/ + +void OGRSVGDataSource::dataHandlerValidateCbk(CPL_UNUSED const char *data, + CPL_UNUSED int nLen) +{ + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "File probably corrupted (million laugh pattern)"); + XML_StopParser(oCurrentParser, XML_FALSE); + } +} + + +static void XMLCALL startElementValidateCbk(void *pUserData, + const char *pszName, const char **ppszAttr) +{ + OGRSVGDataSource* poDS = (OGRSVGDataSource*) pUserData; + poDS->startElementValidateCbk(pszName, ppszAttr); +} + +static void XMLCALL dataHandlerValidateCbk(void *pUserData, const char *data, int nLen) +{ + OGRSVGDataSource* poDS = (OGRSVGDataSource*) pUserData; + poDS->dataHandlerValidateCbk(data, nLen); +} +#endif + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRSVGDataSource::Open( const char * pszFilename ) + +{ +#ifdef HAVE_EXPAT + pszName = CPLStrdup( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Try to open the file. */ +/* -------------------------------------------------------------------- */ + CPLString osFilename(pszFilename); + if (EQUAL(CPLGetExtension(pszFilename), "svgz") && + strstr(pszFilename, "/vsigzip/") == NULL) + { + osFilename = CPLString("/vsigzip/") + pszFilename; + pszFilename = osFilename.c_str(); + } + + VSILFILE* fp = VSIFOpenL(pszFilename, "r"); + if (fp == NULL) + return FALSE; + + eValidity = SVG_VALIDITY_UNKNOWN; + + XML_Parser oParser = OGRCreateExpatXMLParser(); + oCurrentParser = oParser; + XML_SetUserData(oParser, this); + XML_SetElementHandler(oParser, ::startElementValidateCbk, NULL); + XML_SetCharacterDataHandler(oParser, ::dataHandlerValidateCbk); + + char aBuf[BUFSIZ]; + int nDone; + unsigned int nLen; + int nCount = 0; + + /* Begin to parse the file and look for the element */ + /* It *MUST* be the first element of an XML file */ + /* So once we have read the first element, we know if we can */ + /* handle the file or not with that driver */ + do + { + nDataHandlerCounter = 0; + nLen = (unsigned int) VSIFReadL( aBuf, 1, sizeof(aBuf), fp ); + nDone = VSIFEofL(fp); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + if (nLen <= BUFSIZ-1) + aBuf[nLen] = 0; + else + aBuf[BUFSIZ-1] = 0; + if (strstr(aBuf, " 0 ); + + XML_ParserFree(oParser); + + VSIFCloseL(fp); + + if (eValidity == SVG_VALIDITY_VALID) + { + if (bIsCloudmade) + { + nLayers = 3; + papoLayers =(OGRSVGLayer **) CPLRealloc(papoLayers, + nLayers * sizeof(OGRSVGLayer*)); + papoLayers[0] = new OGRSVGLayer( pszFilename, "points", SVG_POINTS, this ); + papoLayers[1] = new OGRSVGLayer( pszFilename, "lines", SVG_LINES, this ); + papoLayers[2] = new OGRSVGLayer( pszFilename, "polygons", SVG_POLYGONS, this ); + } + else + { + CPLDebug("SVG", + "%s seems to be a SVG file, but not a Cloudmade vector one.", + pszFilename); + } + } + + return (nLayers > 0); +#else + char aBuf[256]; + VSILFILE* fp = VSIFOpenL(pszFilename, "r"); + if (fp) + { + unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, 255, fp ); + aBuf[nLen] = 0; + if (strstr(aBuf, " + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_svg.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrsvgdriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +CPL_C_START +void RegisterOGRSVG(); +CPL_C_END + +// g++ -g -Wall -fPIC ogr/ogrsf_frmts/svg/*.c* -shared -o ogr_SVG.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/svg -L. -lgdal -DHAVE_EXPAT + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *OGRSVGDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( poOpenInfo->eAccess == GA_Update || poOpenInfo->fpL == NULL ) + return NULL; + + if( strstr((const char*)poOpenInfo->pabyHeader, "Open( poOpenInfo->pszFilename ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRSVG() */ +/************************************************************************/ + +void RegisterOGRSVG() + +{ + if (! GDAL_CHECK_VERSION("OGR/SVG driver")) + return; + GDALDriver *poDriver; + + if( GDALGetDriverByName( "SVG" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "SVG" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Scalable Vector Graphics" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "svg" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_svg.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRSVGDriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/ogrsvglayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/ogrsvglayer.cpp new file mode 100644 index 000000000..a6ef58f0a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/svg/ogrsvglayer.cpp @@ -0,0 +1,844 @@ +/****************************************************************************** + * $Id: ogrsvglayer.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: SVG Translator + * Purpose: Implements OGRSVGLayer class. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_svg.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrsvglayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRSVGLayer() */ +/************************************************************************/ + +OGRSVGLayer::OGRSVGLayer( const char* pszFilename, + const char* pszLayerName, + SVGGeometryType svgGeomType, + OGRSVGDataSource* poDS) + +{ + nNextFID = 0; + + this->poDS = poDS; + this->svgGeomType = svgGeomType; + osLayerName = pszLayerName; + SetDescription( pszLayerName ); + + poFeatureDefn = NULL; + + nTotalFeatures = 0; + + ppoFeatureTab = NULL; + nFeatureTabIndex = 0; + nFeatureTabLength = 0; + pszSubElementValue = NULL; + nSubElementValueLen = 0; + bStopParsing = FALSE; + + poSRS = new OGRSpatialReference("PROJCS[\"WGS 84 / Pseudo-Mercator\"," + "GEOGCS[\"WGS 84\"," + " DATUM[\"WGS_1984\"," + " SPHEROID[\"WGS 84\",6378137,298.257223563," + " AUTHORITY[\"EPSG\",\"7030\"]]," + " AUTHORITY[\"EPSG\",\"6326\"]]," + " PRIMEM[\"Greenwich\",0," + " AUTHORITY[\"EPSG\",\"8901\"]]," + " UNIT[\"degree\",0.0174532925199433," + " AUTHORITY[\"EPSG\",\"9122\"]]," + " AUTHORITY[\"EPSG\",\"4326\"]]," + "UNIT[\"metre\",1," + " AUTHORITY[\"EPSG\",\"9001\"]]," + "PROJECTION[\"Mercator_1SP\"]," + "PARAMETER[\"central_meridian\",0]," + "PARAMETER[\"scale_factor\",1]," + "PARAMETER[\"false_easting\",0]," + "PARAMETER[\"false_northing\",0]," + "EXTENSION[\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs\"]," + "AUTHORITY[\"EPSG\",\"3857\"]," + "AXIS[\"X\",EAST]," + "AXIS[\"Y\",NORTH]]"); + + poFeature = NULL; + +#ifdef HAVE_EXPAT + oParser = NULL; +#endif + + fpSVG = VSIFOpenL( pszFilename, "r" ); + if( fpSVG == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename); + return; + } + + ResetReading(); +} + +/************************************************************************/ +/* ~OGRSVGLayer() */ +/************************************************************************/ + +OGRSVGLayer::~OGRSVGLayer() + +{ +#ifdef HAVE_EXPAT + if (oParser) + XML_ParserFree(oParser); +#endif + if (poFeatureDefn) + poFeatureDefn->Release(); + + if( poSRS != NULL ) + poSRS->Release(); + + CPLFree(pszSubElementValue); + + int i; + for(i=nFeatureTabIndex;istartElementCbk(pszName, ppszAttr); +} + +static void XMLCALL endElementCbk(void *pUserData, const char *pszName) +{ + ((OGRSVGLayer*)pUserData)->endElementCbk(pszName); +} + +static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen) +{ + ((OGRSVGLayer*)pUserData)->dataHandlerCbk(data, nLen); +} + +#endif + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRSVGLayer::ResetReading() + +{ + nNextFID = 0; + if (fpSVG) + { + VSIFSeekL( fpSVG, 0, SEEK_SET ); +#ifdef HAVE_EXPAT + if (oParser) + XML_ParserFree(oParser); + + oParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk); + XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk); + XML_SetUserData(oParser, this); +#endif + } + + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + iCurrentField = -1; + + int i; + for(i=nFeatureTabIndex;icloseRings(); + return; + } + else if (ch == '+' || ch == '-' || ch == '.' || + (ch >= '0' && ch <= '9')) + { + if (iBuffer == 30) + { + CPLDebug("SVG", "Too big number"); + return; + } + szBuffer[iBuffer ++] = ch; + } + else if (ch == ' ' || ch == 0) + { + if (iBuffer > 0) + { + szBuffer[iBuffer] = 0; + if (iNumber == 1) + { + /* Cloudmade --> negate y */ + double dfNumber = -CPLAtof(szBuffer); + + if (bRelativeLineto) + { + dfX += dfPrevNumber; + dfY += dfNumber; + } + else + { + dfX = dfPrevNumber; + dfY = dfNumber; + } + poLS->addPoint(dfX, dfY); + nPointCount ++; + + iNumber = 0; + } + else + { + iNumber = 1; + dfPrevNumber = CPLAtof(szBuffer); + } + + iBuffer = 0; + } + if (ch == 0) + break; + } + } +} + +/************************************************************************/ +/* startElementCbk() */ +/************************************************************************/ + +void OGRSVGLayer::startElementCbk(const char *pszName, const char **ppszAttr) +{ + int i; + + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + if (svgGeomType == SVG_POINTS && + strcmp(pszName, "circle") == 0 && + strcmp(OGRSVGGetClass(ppszAttr), "point") == 0) + { + int bHasFoundX = FALSE, bHasFoundY = FALSE; + double dfX = 0, dfY = 0; + for (i = 0; ppszAttr[i]; i += 2) + { + if (strcmp(ppszAttr[i], "cx") == 0) + { + bHasFoundX = TRUE; + dfX = CPLAtof(ppszAttr[i + 1]); + } + else if (strcmp(ppszAttr[i], "cy") == 0) + { + bHasFoundY = TRUE; + /* Cloudmade --> negate y */ + dfY = - CPLAtof(ppszAttr[i + 1]); + } + } + if (bHasFoundX && bHasFoundY) + { + interestingDepthLevel = depthLevel; + inInterestingElement = TRUE; + + if (poFeature) + delete poFeature; + + poFeature = new OGRFeature( poFeatureDefn ); + + poFeature->SetFID( nNextFID++ ); + OGRPoint* poPoint = new OGRPoint( dfX, dfY ); + poPoint->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly( poPoint ); + } + } + else if (svgGeomType == SVG_LINES && + strcmp(pszName, "path") == 0 && + strcmp(OGRSVGGetClass(ppszAttr), "line") == 0) + { + const char* pszD = NULL; + for (i = 0; ppszAttr[i]; i += 2) + { + if (strcmp(ppszAttr[i], "d") == 0) + { + pszD = ppszAttr[i + 1]; + break; + } + } + if (pszD) + { + interestingDepthLevel = depthLevel; + inInterestingElement = TRUE; + + if (poFeature) + delete poFeature; + + poFeature = new OGRFeature( poFeatureDefn ); + + poFeature->SetFID( nNextFID++ ); + OGRLineString* poLS = new OGRLineString(); + OGRSVGParseD(poLS, pszD); + poLS->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly( poLS ); + } + } + else if (svgGeomType == SVG_POLYGONS && + strcmp(pszName, "path") == 0 && + strcmp(OGRSVGGetClass(ppszAttr), "polygon") == 0) + { + const char* pszD = NULL; + for (i = 0; ppszAttr[i]; i += 2) + { + if (strcmp(ppszAttr[i], "d") == 0) + { + pszD = ppszAttr[i + 1]; + break; + } + } + if (pszD) + { + interestingDepthLevel = depthLevel; + inInterestingElement = TRUE; + + if (poFeature) + delete poFeature; + + poFeature = new OGRFeature( poFeatureDefn ); + + poFeature->SetFID( nNextFID++ ); + OGRPolygon* poPolygon = new OGRPolygon(); + OGRLinearRing* poLS = new OGRLinearRing(); + OGRSVGParseD(poLS, pszD); + poPolygon->addRingDirectly(poLS); + poPolygon->assignSpatialReference(poSRS); + poFeature->SetGeometryDirectly( poPolygon ); + } + } + else if (inInterestingElement && + depthLevel == interestingDepthLevel + 1 && + strncmp(pszName, "cm:", 3) == 0) + { + iCurrentField = poFeatureDefn->GetFieldIndex(pszName + 3); + } + + depthLevel++; +} + +/************************************************************************/ +/* endElementCbk() */ +/************************************************************************/ + +void OGRSVGLayer::endElementCbk(CPL_UNUSED const char *pszName) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + depthLevel--; + + if (inInterestingElement) + { + if (depthLevel == interestingDepthLevel) + { + inInterestingElement = FALSE; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + ppoFeatureTab = (OGRFeature**) + CPLRealloc(ppoFeatureTab, + sizeof(OGRFeature*) * (nFeatureTabLength + 1)); + ppoFeatureTab[nFeatureTabLength] = poFeature; + nFeatureTabLength++; + } + else + { + delete poFeature; + } + poFeature = NULL; + } + else if (depthLevel == interestingDepthLevel + 1) + { + if (poFeature && iCurrentField >= 0 && nSubElementValueLen) + { + pszSubElementValue[nSubElementValueLen] = 0; + poFeature->SetField( iCurrentField, pszSubElementValue); + } + + CPLFree(pszSubElementValue); + pszSubElementValue = NULL; + nSubElementValueLen = 0; + iCurrentField = -1; + } + } +} + +/************************************************************************/ +/* dataHandlerCbk() */ +/************************************************************************/ + +void OGRSVGLayer::dataHandlerCbk(const char *data, int nLen) +{ + if (bStopParsing) return; + + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "File probably corrupted (million laugh pattern)"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + + nWithoutEventCounter = 0; + + if (iCurrentField >= 0) + { + char* pszNewSubElementValue = (char*) VSIRealloc(pszSubElementValue, + nSubElementValueLen + nLen + 1); + if (pszNewSubElementValue == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + pszSubElementValue = pszNewSubElementValue; + memcpy(pszSubElementValue + nSubElementValueLen, data, nLen); + nSubElementValueLen += nLen; + if (nSubElementValueLen > 100000) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + } + } +} +#endif + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRSVGLayer::GetNextFeature() +{ + GetLayerDefn(); + + if (fpSVG == NULL) + return NULL; + + if (bStopParsing) + return NULL; + +#ifdef HAVE_EXPAT + if (nFeatureTabIndex < nFeatureTabLength) + { + return ppoFeatureTab[nFeatureTabIndex++]; + } + + if (VSIFEofL(fpSVG)) + return NULL; + + char aBuf[BUFSIZ]; + + CPLFree(ppoFeatureTab); + ppoFeatureTab = NULL; + nFeatureTabLength = 0; + nFeatureTabIndex = 0; + nWithoutEventCounter = 0; + iCurrentField = -1; + + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = (unsigned int) + VSIFReadL( aBuf, 1, sizeof(aBuf), fpSVG ); + nDone = VSIFEofL(fpSVG); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of SVG file failed : %s at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + bStopParsing = TRUE; + break; + } + nWithoutEventCounter ++; + } while (!nDone && nFeatureTabLength == 0 && !bStopParsing && + nWithoutEventCounter < 1000); + + if (nWithoutEventCounter == 1000) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + return (nFeatureTabLength) ? ppoFeatureTab[nFeatureTabIndex++] : NULL; +#else + return NULL; +#endif +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSVGLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCFastFeatureCount) ) + return m_poAttrQuery == NULL && m_poFilterGeom == NULL && + nTotalFeatures > 0; + + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return TRUE; + + else + return FALSE; +} + + +/************************************************************************/ +/* LoadSchema() */ +/************************************************************************/ + +#ifdef HAVE_EXPAT + +static void XMLCALL startElementLoadSchemaCbk(void *pUserData, + const char *pszName, + const char **ppszAttr) +{ + ((OGRSVGLayer*)pUserData)->startElementLoadSchemaCbk(pszName, ppszAttr); +} + +static void XMLCALL endElementLoadSchemaCbk(void *pUserData, const char *pszName) +{ + ((OGRSVGLayer*)pUserData)->endElementLoadSchemaCbk(pszName); +} + +static void XMLCALL dataHandlerLoadSchemaCbk(void *pUserData, + const char *data, int nLen) +{ + ((OGRSVGLayer*)pUserData)->dataHandlerLoadSchemaCbk(data, nLen); +} + + +/** This function parses the whole file to build the schema */ +void OGRSVGLayer::LoadSchema() +{ + CPLAssert(poFeatureDefn == NULL); + + for(int i=0;iGetLayerCount();i++) + { + OGRSVGLayer* poLayer = (OGRSVGLayer*)poDS->GetLayer(i); + poLayer->poFeatureDefn = new OGRFeatureDefn( poLayer->osLayerName ); + poLayer->poFeatureDefn->Reference(); + poLayer->poFeatureDefn->SetGeomType(poLayer->GetGeomType()); + poLayer->poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poLayer->poSRS); + } + + oSchemaParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oSchemaParser, ::startElementLoadSchemaCbk, + ::endElementLoadSchemaCbk); + XML_SetCharacterDataHandler(oSchemaParser, ::dataHandlerLoadSchemaCbk); + XML_SetUserData(oSchemaParser, this); + + if (fpSVG == NULL) + return; + + VSIFSeekL( fpSVG, 0, SEEK_SET ); + + inInterestingElement = FALSE; + depthLevel = 0; + nWithoutEventCounter = 0; + bStopParsing = FALSE; + + char aBuf[BUFSIZ]; + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = + (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpSVG ); + nDone = VSIFEofL(fpSVG); + if (XML_Parse(oSchemaParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of SVG file failed : %s at line %d, column %d", + XML_ErrorString(XML_GetErrorCode(oSchemaParser)), + (int)XML_GetCurrentLineNumber(oSchemaParser), + (int)XML_GetCurrentColumnNumber(oSchemaParser)); + bStopParsing = TRUE; + break; + } + nWithoutEventCounter ++; + } while (!nDone && !bStopParsing && nWithoutEventCounter < 1000); + + if (nWithoutEventCounter == 1000) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + XML_ParserFree(oSchemaParser); + oSchemaParser = NULL; + + VSIFSeekL( fpSVG, 0, SEEK_SET ); +} + + +/************************************************************************/ +/* startElementLoadSchemaCbk() */ +/************************************************************************/ + +void OGRSVGLayer::startElementLoadSchemaCbk(const char *pszName, + const char **ppszAttr) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + if (strcmp(pszName, "circle") == 0 && + strcmp(OGRSVGGetClass(ppszAttr), "point") == 0) + { + poCurLayer = (OGRSVGLayer*)poDS->GetLayer(0); + poCurLayer->nTotalFeatures ++; + inInterestingElement = TRUE; + interestingDepthLevel = depthLevel; + } + else if (strcmp(pszName, "path") == 0 && + strcmp(OGRSVGGetClass(ppszAttr), "line") == 0) + { + poCurLayer = (OGRSVGLayer*)poDS->GetLayer(1); + poCurLayer->nTotalFeatures ++; + inInterestingElement = TRUE; + interestingDepthLevel = depthLevel; + } + else if (strcmp(pszName, "path") == 0 && + strcmp(OGRSVGGetClass(ppszAttr), "polygon") == 0) + { + poCurLayer = (OGRSVGLayer*)poDS->GetLayer(2); + poCurLayer->nTotalFeatures ++; + inInterestingElement = TRUE; + interestingDepthLevel = depthLevel; + } + else if (inInterestingElement) + { + if (depthLevel == interestingDepthLevel + 1 && + strncmp(pszName, "cm:", 3) == 0) + { + pszName += 3; + if (poCurLayer->poFeatureDefn->GetFieldIndex(pszName) < 0) + { + OGRFieldDefn oFieldDefn(pszName, OFTString); + if (strcmp(pszName, "timestamp") == 0) + oFieldDefn.SetType(OFTDateTime); + else if (strcmp(pszName, "way_area") == 0 || + strcmp(pszName, "area") == 0) + oFieldDefn.SetType(OFTReal); + else if (strcmp(pszName, "z_order") == 0) + oFieldDefn.SetType(OFTInteger); + poCurLayer->poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + } + } + + depthLevel++; +} + +/************************************************************************/ +/* endElementLoadSchemaCbk() */ +/************************************************************************/ + +void OGRSVGLayer::endElementLoadSchemaCbk(CPL_UNUSED const char *pszName) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + depthLevel--; + + if (inInterestingElement && + depthLevel == interestingDepthLevel) + { + inInterestingElement = FALSE; + } +} + +/************************************************************************/ +/* dataHandlerLoadSchemaCbk() */ +/************************************************************************/ + +void OGRSVGLayer::dataHandlerLoadSchemaCbk(CPL_UNUSED const char *data, + CPL_UNUSED int nLen) +{ + if (bStopParsing) return; + + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "File probably corrupted (million laugh pattern)"); + XML_StopParser(oSchemaParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + + nWithoutEventCounter = 0; +} +#else +void OGRSVGLayer::LoadSchema() +{ +} +#endif + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRSVGLayer::GetLayerDefn() +{ + if (poFeatureDefn == NULL) + { + LoadSchema(); + } + + return poFeatureDefn; +} + +/************************************************************************/ +/* GetGeomType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRSVGLayer::GetGeomType() +{ + if (svgGeomType == SVG_POINTS) + return wkbPoint; + else if (svgGeomType == SVG_LINES) + return wkbLineString; + else + return wkbPolygon; +} + +/************************************************************************/ +/* GetGeomType() */ +/************************************************************************/ + +GIntBig OGRSVGLayer::GetFeatureCount( int bForce ) +{ + if (m_poAttrQuery != NULL || m_poFilterGeom != NULL) + return OGRLayer::GetFeatureCount(bForce); + + GetLayerDefn(); + + return nTotalFeatures; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/GNUmakefile new file mode 100644 index 000000000..51d15e2fc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrsxfdriver.o ogrsxfdatasource.o ogrsxflayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_sxf.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/drv_sxf.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/drv_sxf.html new file mode 100644 index 000000000..fe3b71420 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/drv_sxf.html @@ -0,0 +1,42 @@ + + +SXF + + + + +

    Storage and eXchange Format - SXF

    + +(GDAL/OGR >= 1.10.2)

    + +This driver reads SXF files, open format often associated with Russian GIS Software Panorama.

    +The driver is read only, but supports deletion of data source. The driver supports SXF binary files version 3.0 and higher.

    +The SXF layer support the following capabilities: +

      +
    • Strings as UTF8 +
    • Random Read +
    • Fast Feature Count +
    • Fast Get Extent +
    • Fast Set Next By Index +
    +The driver uses classifiers (RSC files) to map feature from SXF to layers. Features that do not belong to any layer are put to the layer named "Not_Classified". The layers with zero features are not present in data source.

    +To be used automatically, the RSC file should have the same name as SXF file. User can provide RSC file path using config option SXF_RSC_FILENAME. This config option overrides the use of same name RSC.

    +The RSC file usually stores long and short layer name. The long name is usually in Russian, and short in English. The SXF_LAYER_FULLNAME config option allows choosing which layer names to use. If SXF_LAYER_FULLNAME is TRUE - the driver uses long names, if FALSE - short.

    +The attributes are read from SXF file. Maximum number of fields is created for the same layer features with different number of attributes. If attribute has a code mapped to RSC file, driver adds only the code (don't get real value from RSC, as the value type may differ from field type).

    +If config option SXF_SET_VERTCS set to ON, the layers spatial reference will include vertical coordinate system description if exist.

    + + +

    See Also

    + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/makefile.vc new file mode 100644 index 000000000..7a3c61702 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrsxfdriver.obj ogrsxfdatasource.obj ogrsxflayer.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/ogr_sxf.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/ogr_sxf.h new file mode 100644 index 000000000..4dcf01d50 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/ogr_sxf.h @@ -0,0 +1,154 @@ +/****************************************************************************** + * $Id: ogr_sxf.h $ + * + * Project: SXF Translator + * Purpose: Include file defining classes for OGR SXF driver, datasource and layers. + * Author: Ben Ahmed Daho Ali, bidandou(at)yahoo(dot)fr + * Dmitry Baryshnikov, polimax@mail.ru + * Alexandr Lisovenko, alexander.lisovenko@gmail.com + * + ****************************************************************************** + * Copyright (c) 2011, Ben Ahmed Daho Ali + * Copyright (c) 2013, NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_SXF_H_INCLUDED +#define _OGR_SXF_H_INCLUDED + +#include +#include +#include + +#include "ogrsf_frmts.h" +#include "org_sxf_defs.h" + +#define CHECK_BIT(var,pos) ((var) & (1<<(pos))) +#define TO_DEGREES 57.2957795130823208766 +#define TO_RADIANS 0.017453292519943295769 + +/************************************************************************/ +/* OGRSXFLayer */ +/************************************************************************/ +class OGRSXFLayer : public OGRLayer +{ +protected: + OGRFeatureDefn* poFeatureDefn; + VSILFILE* fpSXF; + GByte nLayerID; + std::map mnClassificators; + std::map mnRecordDesc; + std::map::const_iterator oNextIt; + SXFMapDescription stSXFMapDescription; + std::set snAttributeCodes; + int m_nSXFFormatVer; + CPLString sFIDColumn_; + CPLMutex **m_hIOMutex; + double m_dfCoeff; + virtual OGRFeature * GetNextRawFeature(long nFID); + + GUInt32 TranslateXYH(const SXFRecordDescription& certifInfo, + const char *psBuff, GUInt32 nBufLen, + double *dfX, double *dfY, double *dfH = NULL); + + + OGRFeature *TranslatePoint(const SXFRecordDescription& certifInfo, const char * psRecordBuf, GUInt32 nBufLen); + OGRFeature *TranslateText(const SXFRecordDescription& certifInfo, const char * psBuff, GUInt32 nBufLen); + OGRFeature *TranslatePolygon(const SXFRecordDescription& certifInfo, const char * psBuff, GUInt32 nBufLen); + OGRFeature *TranslateLine(const SXFRecordDescription& certifInfo, const char * psBuff, GUInt32 nBufLen); + OGRFeature *TranslateVetorAngle(const SXFRecordDescription& certifInfo, const char * psBuff, GUInt32 nBufLen); +public: + OGRSXFLayer(VSILFILE* fp, CPLMutex** hIOMutex, GByte nID, const char* pszLayerName, int nVer, const SXFMapDescription& sxfMapDesc); + ~OGRSXFLayer(); + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + virtual OGRErr SetNextByIndex(GIntBig nIndex); + virtual OGRFeature *GetFeature(GIntBig nFID); + virtual OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn;} + + virtual int TestCapability( const char * ); + + virtual GIntBig GetFeatureCount(int bForce = TRUE); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRSpatialReference *GetSpatialRef(); + virtual const char* GetFIDColumn(); + + virtual GByte GetId() const { return nLayerID; }; + virtual void AddClassifyCode(unsigned nClassCode, const char *szName = NULL); + virtual int AddRecord(long nFID, unsigned nClassCode, vsi_l_offset nOffset, bool bHasSemantic, size_t nSemanticsSize); +}; + + +/************************************************************************/ +/* OGRSXFDataSource */ +/************************************************************************/ + +class OGRSXFDataSource : public OGRDataSource +{ + SXFPassport oSXFPassport; + + CPLString pszName; + + OGRLayer** papoLayers; + size_t nLayers; + + VSILFILE* fpSXF; + CPLMutex *hIOMutex; + void FillLayers(void); + void CreateLayers(); + void CreateLayers(VSILFILE* fpRSC); + OGRErr ReadSXFInformationFlags(VSILFILE* fpSXF, SXFPassport& passport); + OGRErr ReadSXFDescription(VSILFILE* fpSXF, SXFPassport& passport); + void SetVertCS(const long iVCS, SXFPassport& passport); + OGRErr ReadSXFMapDescription(VSILFILE* fpSXF, SXFPassport& passport); + OGRSXFLayer* GetLayerById(GByte); +public: + OGRSXFDataSource(); + ~OGRSXFDataSource(); + + int Open( const char * pszFilename, + int bUpdate ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); + void CloseFile(); +}; + +/************************************************************************/ +/* OGRSXFDriver */ +/************************************************************************/ + +class OGRSXFDriver : public OGRSFDriver +{ + public: + ~OGRSXFDriver(); + + const char* GetName(); + OGRDataSource* Open( const char *, int ); + OGRErr DeleteDataSource(const char* pszName); + int TestCapability(const char *); +}; + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/ogrsxfdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/ogrsxfdatasource.cpp new file mode 100644 index 000000000..be15e0135 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/ogrsxfdatasource.cpp @@ -0,0 +1,1314 @@ +/****************************************************************************** + * $Id: ogr_sxfdatasource.cpp $ + * + * Project: SXF Translator + * Purpose: Definition of classes for OGR SXF Datasource. + * Author: Ben Ahmed Daho Ali, bidandou(at)yahoo(dot)fr + * Dmitry Baryshnikov, polimax@mail.ru + * Alexandr Lisovenko, alexander.lisovenko@gmail.com + * + ****************************************************************************** + * Copyright (c) 2011, Ben Ahmed Daho Ali + * Copyright (c) 2013, NextGIS + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_sxf.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" + +#include +#include +#include + +CPL_CVSID("$Id: ogrsxfdatasource.cpp $"); + +static const long aoVCS[] = +{ + 0, + 5705, //1 + 5711, //2 + 0, //3 + 5710, //4 + 5710, //5 + 0, //6 + 0, //7 + 0, //8 + 0, //9 + 5716, //10 + 5733, //11 + 0, //12 + 0, //13 + 0, //14 + 0, //15 + 5709, //16 + 5776, //17 + 0, //18 + 0, //19 + 5717, //20 + 5613, //21 + 0, //22 + 5775, //23 + 5702, //24 + 0, //25 + 0, //26 + 5714 //27 +}; + +#define NUMBER_OF_VERTICALCS (sizeof(aoVCS)/sizeof(aoVCS[0])) + +/************************************************************************/ +/* OGRSXFDataSource() */ +/************************************************************************/ + +OGRSXFDataSource::OGRSXFDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + fpSXF = NULL; + hIOMutex = NULL; + + oSXFPassport.stMapDescription.pSpatRef = NULL; +} + +/************************************************************************/ +/* ~OGRSXFDataSource() */ +/************************************************************************/ + +OGRSXFDataSource::~OGRSXFDataSource() + +{ + for( size_t i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + if (NULL != oSXFPassport.stMapDescription.pSpatRef) + { + oSXFPassport.stMapDescription.pSpatRef->Release(); + } + + CloseFile(); + + if (hIOMutex != NULL) + { + CPLDestroyMutex(hIOMutex); + hIOMutex = NULL; + } +} + +/************************************************************************/ +/* CloseFile() */ +/************************************************************************/ +void OGRSXFDataSource::CloseFile() +{ + if (NULL != fpSXF) + { + VSIFCloseL( fpSXF ); + fpSXF = NULL; + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSXFDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRSXFDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= (int)nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRSXFDataSource::Open( const char * pszFilename, int bUpdateIn) +{ + size_t nObjectsRead; + int nFileHeaderSize; + + if (bUpdateIn) + { + return FALSE; + } + + pszName = pszFilename; + + fpSXF = VSIFOpenL(pszName, "rb"); + if ( fpSXF == NULL ) + { + CPLError(CE_Warning, CPLE_OpenFailed, "SXF open file %s failed", pszFilename); + return FALSE; + } + + //read header + nFileHeaderSize = sizeof(SXFHeader); + SXFHeader stSXFFileHeader; + nObjectsRead = VSIFReadL(&stSXFFileHeader, nFileHeaderSize, 1, fpSXF); + + if (nObjectsRead != 1) + { + CPLError(CE_Failure, CPLE_None, "SXF head read failed"); + CloseFile(); + return FALSE; + } + + //check version + oSXFPassport.version = 0; + if (stSXFFileHeader.nHeaderLength > 256) //if size == 400 then version >= 4 + { + oSXFPassport.version = stSXFFileHeader.nFormatVersion[2]; + } + else + { + oSXFPassport.version = stSXFFileHeader.nFormatVersion[1]; + } + + if ( oSXFPassport.version < 3 ) + { + CPLError(CE_Failure, CPLE_NotSupported , "SXF File version not supported"); + CloseFile(); + return FALSE; + } + + // read description + if (ReadSXFDescription(fpSXF, oSXFPassport) != OGRERR_NONE) + { + CPLError(CE_Failure, CPLE_NotSupported, "SXF. Wrong description."); + CloseFile(); + return FALSE; + } + + + //read flags + if (ReadSXFInformationFlags(fpSXF, oSXFPassport) != OGRERR_NONE) + { + CPLError(CE_Failure, CPLE_NotSupported, "SXF. Wrong state of the data."); + CloseFile(); + return FALSE; + } + + if (oSXFPassport.informationFlags.bProjectionDataCompliance == false) + { + CPLError( CE_Failure, CPLE_NotSupported, + "SXF. Data are not corresponde to the projection." ); + CloseFile(); + return FALSE; + } + + //read spatial data + if (ReadSXFMapDescription(fpSXF, oSXFPassport) != OGRERR_NONE) + { + CPLError(CE_Failure, CPLE_NotSupported, "SXF. Wrong state of the data."); + CloseFile(); + return FALSE; + } + + if(oSXFPassport.informationFlags.bRealCoordinatesCompliance == false ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "SXF. Given material may be rotated in the conditional system of coordinates" ); + } + +/*---------------- TRY READ THE RSC FILE HEADER -----------------------*/ + + CPLString soRSCRileName; + const char* pszRSCRileName = CPLGetConfigOption("SXF_RSC_FILENAME", ""); + if (CPLCheckForFile((char *)pszRSCRileName, NULL) == TRUE) + { + soRSCRileName = pszRSCRileName; + } + + if(soRSCRileName.empty()) + { + pszRSCRileName = CPLResetExtension(pszFilename, "rsc"); + if (CPLCheckForFile((char *)pszRSCRileName, NULL) == TRUE) + { + soRSCRileName = pszRSCRileName; + } + } + + if(soRSCRileName.empty()) + { + pszRSCRileName = CPLResetExtension(pszFilename, "RSC"); + if (CPLCheckForFile((char *)pszRSCRileName, NULL) == TRUE) + { + soRSCRileName = pszRSCRileName; + } + } + + + //1. Create layers from RSC file or create default set of layers from osm.rsc + + if (soRSCRileName.empty()) + { + CPLError(CE_Warning, CPLE_None, "RSC file for %s not exist", pszFilename); + } + else + { + VSILFILE* fpRSC; + + fpRSC = VSIFOpenL(soRSCRileName, "rb"); + if (fpRSC == NULL) + { + CPLError(CE_Warning, CPLE_OpenFailed, "RSC file %s open failed", + soRSCRileName.c_str()); + } + else + { + CPLDebug( "OGRSXFDataSource", "RSC Filename: %s", + soRSCRileName.c_str() ); + CreateLayers(fpRSC); + VSIFCloseL(fpRSC); + } + } + + if (nLayers == 0)//create default set of layers + { + CreateLayers(); + } + + FillLayers(); + + return TRUE; +} + +OGRErr OGRSXFDataSource::ReadSXFDescription(VSILFILE* fpSXF, SXFPassport& passport) +{ + /* int nObjectsRead; */ + + if (passport.version == 3) + { + //78 + GByte buff[62]; + /* nObjectsRead = */ VSIFReadL(&buff, 62, 1, fpSXF); + char date[3] = { 0 }; + + //read year + memcpy(date, buff, 2); + passport.dtCrateDate.nYear = atoi(date); + if (passport.dtCrateDate.nYear < 50) + passport.dtCrateDate.nYear += 2000; + else + passport.dtCrateDate.nYear += 1900; + + memcpy(date, buff + 2, 2); + + passport.dtCrateDate.nMonth = atoi(date); + + memcpy(date, buff + 4, 2); + + passport.dtCrateDate.nDay = atoi(date); + + char szName[26] = { 0 }; + memcpy(szName, buff + 8, 24); + char* pszRecoded = CPLRecode(szName + 2, "CP1251", CPL_ENC_UTF8); + passport.sMapSheet = pszRecoded; //TODO: check the encoding in SXF created in Linux + CPLFree(pszRecoded); + + memcpy(&passport.nScale, buff + 32, 4); + CPL_LSBPTR32(&passport.nScale); + + memset(szName, 0, 26); + memcpy(szName, buff + 36, 26); + pszRecoded = CPLRecode(szName, "CP866", CPL_ENC_UTF8); + passport.sMapSheetName = pszRecoded; //TODO: check the encoding in SXF created in Linux + CPLFree(pszRecoded); + + } + else if (passport.version == 4) + { + //96 + GByte buff[80]; + /* nObjectsRead = */ VSIFReadL(&buff, 80, 1, fpSXF); + char date[5] = { 0 }; + + //read year + memcpy(date, buff, 4); + passport.dtCrateDate.nYear = atoi(date); + + memset(date, 0, 5); + memcpy(date, buff + 4, 2); + + passport.dtCrateDate.nMonth = atoi(date); + + memcpy(date, buff + 6, 2); + + passport.dtCrateDate.nDay = atoi(date); + + char szName[32] = { 0 }; + memcpy(szName, buff + 12, 32); + char* pszRecoded = CPLRecode(szName + 2, "CP1251", CPL_ENC_UTF8); + passport.sMapSheet = pszRecoded; //TODO: check the encoding in SXF created in Linux + CPLFree(pszRecoded); + + memcpy(&passport.nScale, buff + 44, 4); + CPL_LSBPTR32(&passport.nScale); + + memset(szName, 0, 32); + memcpy(szName, buff + 48, 32); + pszRecoded = CPLRecode(szName, "CP1251", CPL_ENC_UTF8); + passport.sMapSheetName = pszRecoded; //TODO: check the encoding in SXF created in Linux + CPLFree(pszRecoded); + } + + return OGRERR_NONE; +} + +OGRErr OGRSXFDataSource::ReadSXFInformationFlags(VSILFILE* fpSXF, SXFPassport& passport) +{ + /* int nObjectsRead; */ + GByte val[4]; + /* nObjectsRead = */ VSIFReadL(&val, 4, 1, fpSXF); + + if (!(CHECK_BIT(val[0], 0) && CHECK_BIT(val[0], 1))) + { + return OGRERR_UNSUPPORTED_OPERATION; + } + + if (CHECK_BIT(val[0], 2)) + { + passport.informationFlags.bProjectionDataCompliance = true; + } + else + { + passport.informationFlags.bProjectionDataCompliance = false; + } + + if (CHECK_BIT(val[0], 4)) + { + passport.informationFlags.bRealCoordinatesCompliance = true; + } + else + { + passport.informationFlags.bRealCoordinatesCompliance = false; + } + + if (CHECK_BIT(val[0], 6)) + { + passport.informationFlags.stCodingType = SXF_SEM_TXT; + } + else + { + if (CHECK_BIT(val[0], 5)) + { + passport.informationFlags.stCodingType = SXF_SEM_HEX; + } + else + { + passport.informationFlags.stCodingType = SXF_SEM_DEC; + } + } + + if (CHECK_BIT(val[0], 7)) + { + passport.informationFlags.stGenType = SXF_GT_LARGE_SCALE; + } + else + { + passport.informationFlags.stGenType = SXF_GT_SMALL_SCALE; + } + + //version specific + + if (passport.version == 3) + { + //degrees are ints * 100 000 000 + //meters are ints / 10 + passport.informationFlags.stEnc = SXF_ENC_DOS; + passport.informationFlags.stCoordAcc = SXF_COORD_ACC_DM; + passport.informationFlags.bSort = false; + } + else if (passport.version == 4) + { + passport.informationFlags.stEnc = (SXFTextEncoding)val[1]; + passport.informationFlags.stCoordAcc = (SXFCoordinatesAccuracy)val[2]; + if (CHECK_BIT(val[3], 0)) + { + passport.informationFlags.bSort = true; + } + else + { + passport.informationFlags.bSort = false; + } + } + + return OGRERR_NONE; +} + +void OGRSXFDataSource::SetVertCS(const long iVCS, SXFPassport& passport) +{ + if (!CSLTestBoolean(CPLGetConfigOption("SXF_SET_VERTCS", "NO"))) + return; + + const long nEPSG = aoVCS[iVCS]; + + if (nEPSG == 0) + { + CPLError(CE_Warning, CPLE_NotSupported, "SXF. Vertical coordinate system (SXF index %ld) not supported", iVCS); + return; + } + + OGRSpatialReference* sr = new OGRSpatialReference(); + OGRErr eImportFromEPSGErr = sr->importFromEPSG(nEPSG); + if (eImportFromEPSGErr != OGRERR_NONE) + { + CPLError( CE_Warning, CPLE_None, "SXF. Vertical coordinate system (SXF index %ld, EPSG %ld) import from EPSG error", iVCS, nEPSG); + return; + } + + if (sr->IsVertical() != 1) + { + CPLError( CE_Warning, CPLE_None, "SXF. Coordinate system (SXF index %ld, EPSG %ld) is not Vertical", iVCS, nEPSG); + return; + } + + //passport.stMapDescription.pSpatRef->SetVertCS("Baltic", "Baltic Sea"); + OGRErr eSetVertCSErr = passport.stMapDescription.pSpatRef->SetVertCS(sr->GetAttrValue("VERT_CS"), sr->GetAttrValue("VERT_DATUM")); + if (eSetVertCSErr != OGRERR_NONE) + { + CPLError(CE_Warning, CPLE_None, "SXF. Vertical coordinate system (SXF index %ld, EPSG %ld) set error", iVCS, nEPSG); + return; + } +} +OGRErr OGRSXFDataSource::ReadSXFMapDescription(VSILFILE* fpSXF, SXFPassport& passport) +{ + /* int nObjectsRead;*/ + int i; + passport.stMapDescription.Env.MaxX = -100000000; + passport.stMapDescription.Env.MinX = 100000000; + passport.stMapDescription.Env.MaxY = -100000000; + passport.stMapDescription.Env.MinY = 100000000; + + bool bIsX = true;// passport.informationFlags.bRealCoordinatesCompliance; //if real coordinates we need to swap x & y + + //version specific + if (passport.version == 3) + { + short nNoObjClass, nNoSemClass; + /* nObjectsRead = */ VSIFReadL(&nNoObjClass, 2, 1, fpSXF); + /* nObjectsRead = */ VSIFReadL(&nNoSemClass, 2, 1, fpSXF); + GByte baMask[8]; + /* nObjectsRead = */ VSIFReadL(&baMask, 8, 1, fpSXF); + + int nCorners[8]; + + //get projected corner coords + /* nObjectsRead = */ VSIFReadL(&nCorners, 32, 1, fpSXF); + + for (i = 0; i < 8; i++) + { + passport.stMapDescription.stProjCoords[i] = double(nCorners[i]) / 10.0; + if (bIsX) //X + { + if (passport.stMapDescription.Env.MaxY < passport.stMapDescription.stProjCoords[i]) + passport.stMapDescription.Env.MaxY = passport.stMapDescription.stProjCoords[i]; + if (passport.stMapDescription.Env.MinY > passport.stMapDescription.stProjCoords[i]) + passport.stMapDescription.Env.MinY = passport.stMapDescription.stProjCoords[i]; + } + else + { + if (passport.stMapDescription.Env.MaxX < passport.stMapDescription.stProjCoords[i]) + passport.stMapDescription.Env.MaxX = passport.stMapDescription.stProjCoords[i]; + if (passport.stMapDescription.Env.MinX > passport.stMapDescription.stProjCoords[i]) + passport.stMapDescription.Env.MinX = passport.stMapDescription.stProjCoords[i]; + } + bIsX = !bIsX; + } + //get geographic corner coords + /* nObjectsRead = */ VSIFReadL(&nCorners, 32, 1, fpSXF); + + for (i = 0; i < 8; i++) + { + passport.stMapDescription.stGeoCoords[i] = double(nCorners[i]) * 0.00000057295779513082; //from radians to degree * 100 000 000 + } + } + else if (passport.version == 4) + { + int nEPSG; + /* nObjectsRead = */ VSIFReadL(&nEPSG, 4, 1, fpSXF); + + if (nEPSG != 0) + { + passport.stMapDescription.pSpatRef = new OGRSpatialReference(); + passport.stMapDescription.pSpatRef->importFromEPSG(nEPSG); + } + + double dfCorners[8]; + /* nObjectsRead = */ VSIFReadL(&dfCorners, 64, 1, fpSXF); + + for (i = 0; i < 8; i++) + { + passport.stMapDescription.stProjCoords[i] = dfCorners[i]; + if (bIsX) //X + { + if (passport.stMapDescription.Env.MaxY < passport.stMapDescription.stProjCoords[i]) + passport.stMapDescription.Env.MaxY = passport.stMapDescription.stProjCoords[i]; + if (passport.stMapDescription.Env.MinY > passport.stMapDescription.stProjCoords[i]) + passport.stMapDescription.Env.MinY = passport.stMapDescription.stProjCoords[i]; + } + else + { + if (passport.stMapDescription.Env.MaxX < passport.stMapDescription.stProjCoords[i]) + passport.stMapDescription.Env.MaxX = passport.stMapDescription.stProjCoords[i]; + if (passport.stMapDescription.Env.MinX > passport.stMapDescription.stProjCoords[i]) + passport.stMapDescription.Env.MinX = passport.stMapDescription.stProjCoords[i]; + } + bIsX = !bIsX; + + } + //get geographic corner coords + /* nObjectsRead = */ VSIFReadL(&dfCorners, 64, 1, fpSXF); + + for (i = 0; i < 8; i++) + { + passport.stMapDescription.stGeoCoords[i] = dfCorners[i] * TO_DEGREES; // to degree + } + + } + + if (NULL != passport.stMapDescription.pSpatRef) + { + return OGRERR_NONE; + } + + GByte anData[8] = { 0 }; + /* nObjectsRead = */ VSIFReadL(&anData, 8, 1, fpSXF); + long iEllips = anData[0]; + long iVCS = anData[1]; + long iProjSys = anData[2]; + /* long iDatum = anData[3]; Unused. */ + double dfProjScale = 1; + + double adfPrjParams[8] = { 0 }; + + if (passport.version == 3) + { + switch (anData[5]) + { + case 1: + passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_DECIMETRE; + break; + case 2: + passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_CENTIMETRE; + break; + case 3: + passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_MILLIMETRE; + break; + case 130: + passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_RADIAN; + break; + case 129: + passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_DEGREE; + break; + default: + passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_METRE; + break; + } + + + VSIFSeekL(fpSXF, 212, SEEK_SET); + struct _buff{ + GUInt32 nRes; + GInt16 anFrame[8]; + GUInt32 nFrameCode; + } buff; + /* nObjectsRead = */ VSIFReadL(&buff, 20, 1, fpSXF); + passport.stMapDescription.nResolution = buff.nRes; //resolution + + for (i = 0; i < 8; i++) + passport.stMapDescription.stFrameCoords[i] = buff.anFrame[i]; + + int anParams[5]; + /* nObjectsRead = */ VSIFReadL(&anParams, 20, 1, fpSXF); + + if (anParams[0] != -1) + dfProjScale = double(anParams[0]) / 100000000.0; + + if (anParams[2] != -1) + passport.stMapDescription.dfXOr = double(anParams[2]) / 100000000.0 * TO_DEGREES; + else + passport.stMapDescription.dfXOr = 0; + + if (anParams[3] != -1) + passport.stMapDescription.dfYOr = double(anParams[2]) / 100000000.0 * TO_DEGREES; + else + passport.stMapDescription.dfYOr = 0; + + passport.stMapDescription.dfFalseNorthing = 0; + passport.stMapDescription.dfFalseEasting = 0; + + + //adfPrjParams[0] = double(anParams[0]) / 100000000.0; // to radians + //adfPrjParams[1] = double(anParams[1]) / 100000000.0; + //adfPrjParams[2] = double(anParams[2]) / 100000000.0; + //adfPrjParams[3] = double(anParams[3]) / 100000000.0; + adfPrjParams[4] = dfProjScale;//? + //adfPrjParams[5] = 0;//? + //adfPrjParams[6] = 0;//? + //adfPrjParams[7] = 0;// importFromPanorama calc it by itself + + } + else if (passport.version == 4) + { + switch (anData[5]) + { + case 64: + passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_RADIAN; + break; + case 65: + passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_DEGREE; + break; + default: + passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_METRE; + break; + } + + VSIFSeekL(fpSXF, 312, SEEK_SET); + GUInt32 buff[10]; + /* nObjectsRead = */ VSIFReadL(&buff, 40, 1, fpSXF); + + passport.stMapDescription.nResolution = buff[0]; //resolution + for (i = 0; i < 8; i++) + passport.stMapDescription.stFrameCoords[i] = buff[1 + i]; + + double adfParams[6]; + /* nObjectsRead = */ VSIFReadL(&adfParams, 48, 1, fpSXF); + + if (adfParams[1] != -1) + dfProjScale = adfParams[1]; + passport.stMapDescription.dfXOr = adfParams[2] * TO_DEGREES; + passport.stMapDescription.dfYOr = adfParams[3] * TO_DEGREES; + passport.stMapDescription.dfFalseNorthing = adfParams[4]; + passport.stMapDescription.dfFalseEasting = adfParams[5]; + + //adfPrjParams[0] = adfParams[0]; // to radians + //adfPrjParams[1] = adfParams[1]; + //adfPrjParams[2] = adfParams[2]; + //adfPrjParams[3] = adfParams[3]; + adfPrjParams[4] = dfProjScale;//? + //adfPrjParams[5] = adfParams[4]; + //adfPrjParams[6] = adfParams[5]; + //adfPrjParams[7] = 0;// importFromPanorama calc it by itself + } + + passport.stMapDescription.dfScale = passport.nScale; + + double dfCoeff = double(passport.stMapDescription.dfScale) / passport.stMapDescription.nResolution; + passport.stMapDescription.bIsRealCoordinates = passport.informationFlags.bRealCoordinatesCompliance; + passport.stMapDescription.stCoordAcc = passport.informationFlags.stCoordAcc; + + if (!passport.stMapDescription.bIsRealCoordinates) + { + if (passport.stMapDescription.stFrameCoords[0] == 0 && passport.stMapDescription.stFrameCoords[1] == 0 && passport.stMapDescription.stFrameCoords[2] == 0 && passport.stMapDescription.stFrameCoords[3] == 0 && passport.stMapDescription.stFrameCoords[4] == 0 && passport.stMapDescription.stFrameCoords[5] == 0 && passport.stMapDescription.stFrameCoords[6] == 0 && passport.stMapDescription.stFrameCoords[7] == 0) + { + passport.stMapDescription.bIsRealCoordinates = true; + } + else + { + //origin + passport.stMapDescription.dfXOr = passport.stMapDescription.stProjCoords[1] - passport.stMapDescription.stFrameCoords[1] * dfCoeff; + passport.stMapDescription.dfYOr = passport.stMapDescription.stProjCoords[0] - passport.stMapDescription.stFrameCoords[0] * dfCoeff; + } + } + + //normalize some coordintatessystems + if (iEllips == 1 && iProjSys == 1) // Pulkovo 1942 / Gauss-Kruger + { + double dfCenterLongEnv = passport.stMapDescription.stGeoCoords[1] + fabs(passport.stMapDescription.stGeoCoords[5] - passport.stMapDescription.stGeoCoords[1]) / 2; + + int nZoneEnv = (int)( (dfCenterLongEnv + 3.0) / 6.0 + 0.5 ); + + if (nZoneEnv > 1 && nZoneEnv < 33) + { + int nEPSG = 28400 + nZoneEnv; + passport.stMapDescription.pSpatRef = new OGRSpatialReference(); + OGRErr eErr = passport.stMapDescription.pSpatRef->importFromEPSG(nEPSG); + SetVertCS(iVCS, passport); + return eErr; + } + else + { + adfPrjParams[7] = nZoneEnv; + + if (adfPrjParams[5] == 0)//False Easting + { + if (passport.stMapDescription.Env.MaxX < 500000) + adfPrjParams[5] = 500000; + else + adfPrjParams[5] = nZoneEnv * 1000000 + 500000; + } + } + } + else if (iEllips == 9 && iProjSys == 17) // WGS84 / UTM + { + double dfCenterLongEnv = passport.stMapDescription.stGeoCoords[1] + fabs(passport.stMapDescription.stGeoCoords[5] - passport.stMapDescription.stGeoCoords[1]) / 2; + int nZoneEnv = (int)(30 + (dfCenterLongEnv + 3.0) / 6.0 + 0.5); + bool bNorth = passport.stMapDescription.stGeoCoords[6] + (passport.stMapDescription.stGeoCoords[2] - passport.stMapDescription.stGeoCoords[6]) / 2 < 0; + int nEPSG; + if (bNorth) + { + nEPSG = 32600 + nZoneEnv; + } + else + { + nEPSG = 32700 + nZoneEnv; + } + passport.stMapDescription.pSpatRef = new OGRSpatialReference(); + OGRErr eErr = passport.stMapDescription.pSpatRef->importFromEPSG(nEPSG); + SetVertCS(iVCS, passport); + return eErr; + } + else if (iEllips == 45 && iProjSys == 35) //Mercator 3395 on sphere wgs84 + { + passport.stMapDescription.pSpatRef = new OGRSpatialReference("PROJCS[\"WGS_1984_Web_Mercator\",GEOGCS[\"GCS_WGS_1984_Major_Auxiliary_Sphere\",DATUM[\"WGS_1984_Major_Auxiliary_Sphere\",SPHEROID[\"WGS_1984_Major_Auxiliary_Sphere\",6378137.0,0.0]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Mercator_1SP\"],PARAMETER[\"False_Easting\",0.0],PARAMETER[\"False_Northing\",0.0],PARAMETER[\"Central_Meridian\",0.0],PARAMETER[\"latitude_of_origin\",0.0],UNIT[\"Meter\",1.0]]"); + OGRErr eErr = OGRERR_NONE; //passport.stMapDescription.pSpatRef->importFromEPSG(3395); + SetVertCS(iVCS, passport); + return eErr; + } + else if (iEllips == 9 && iProjSys == 34) //Miller 54003 on sphere wgs84 + { + passport.stMapDescription.pSpatRef = new OGRSpatialReference("PROJCS[\"World_Miller_Cylindrical\",GEOGCS[\"GCS_GLOBE\", DATUM[\"GLOBE\", SPHEROID[\"GLOBE\", 6367444.6571, 0.0]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Miller_Cylindrical\"],PARAMETER[\"False_Easting\",0],PARAMETER[\"False_Northing\",0],PARAMETER[\"Central_Meridian\",0],UNIT[\"Meter\",1],AUTHORITY[\"EPSG\",\"54003\"]]"); + OGRErr eErr = OGRERR_NONE; //passport.stMapDescription.pSpatRef->importFromEPSG(3395); + //OGRErr eErr = passport.stMapDescription.pSpatRef->importFromEPSG(54003); + + SetVertCS(iVCS, passport); + return eErr; + } + + //TODO: Need to normalise more SRS: + //PAN_PROJ_WAG1 + //PAN_PROJ_MERCAT + //PAN_PROJ_PS + //PAN_PROJ_POLYC + //PAN_PROJ_EC + //PAN_PROJ_LCC + //PAN_PROJ_STEREO + //PAN_PROJ_AE + //PAN_PROJ_GNOMON + //PAN_PROJ_MOLL + //PAN_PROJ_LAEA + //PAN_PROJ_EQC + //PAN_PROJ_CEA + //PAN_PROJ_IMWP + // + + passport.stMapDescription.pSpatRef = new OGRSpatialReference(); + OGRErr eErr = passport.stMapDescription.pSpatRef->importFromPanorama(anData[2], anData[3], anData[0], adfPrjParams); + SetVertCS(iVCS, passport); + return eErr; +} + +void OGRSXFDataSource::FillLayers() +{ + CPLDebug("SXF","Create layers"); + + //2. Read all records (only classify code and offset) and add this to correspondence layer + GUInt32 nFID; + int nObjectsRead = 0; + size_t i; + vsi_l_offset nOffset = 0, nOffsetSemantic; + size_t nDeletedLayerIndex; + + //get record count + GUInt32 nRecordCountMax = 0; + if (oSXFPassport.version == 3) + { + VSIFSeekL(fpSXF, 288, SEEK_SET); + nObjectsRead = VSIFReadL(&nRecordCountMax, 4, 1, fpSXF); + nOffset = 300; + } + else if (oSXFPassport.version == 4) + { + VSIFSeekL(fpSXF, 440, SEEK_SET); + nObjectsRead = VSIFReadL(&nRecordCountMax, 4, 1, fpSXF); + nOffset = 452; + } + /* else nOffset and nObjectsRead will be 0 */ + + if (nObjectsRead != 1) + { + CPLError(CE_Failure, CPLE_FileIO, "Get record count failed"); + return; + } + + VSIFSeekL(fpSXF, nOffset, SEEK_SET); + + for (nFID = 0; nFID < nRecordCountMax; nFID++) + { + GInt32 buff[6]; + nObjectsRead = VSIFReadL(&buff, 24, 1, fpSXF); + + if (nObjectsRead != 1 || buff[0] != IDSXFOBJ) + { + CPLError(CE_Failure, CPLE_FileIO, "Read record %d failed", nFID); + return; + } + + bool bHasSemantic = CHECK_BIT(buff[5], 9); + if (bHasSemantic) //check has attributes + { + //we have already 24 byte readed + nOffsetSemantic = 8 + buff[2]; + VSIFSeekL(fpSXF, nOffsetSemantic, SEEK_CUR); + } + + int nSemanticSize = buff[1] - 32 - buff[2]; + if( nSemanticSize < 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid value"); + break; + } + for (i = 0; i < nLayers; i++) + { + OGRSXFLayer* pOGRSXFLayer = (OGRSXFLayer*)papoLayers[i]; + if (pOGRSXFLayer && pOGRSXFLayer->AddRecord(nFID, buff[3], nOffset, bHasSemantic, nSemanticSize) == TRUE) + { + break; + } + } + nOffset += buff[1]; + VSIFSeekL(fpSXF, nOffset, SEEK_SET); + } + + //3. delete empty layers + for (i = 0; i < nLayers; i++) + { + OGRSXFLayer* pOGRSXFLayer = (OGRSXFLayer*)papoLayers[i]; + if (pOGRSXFLayer && pOGRSXFLayer->GetFeatureCount() == 0) + { + delete pOGRSXFLayer; + nDeletedLayerIndex = i; + while (nDeletedLayerIndex < nLayers - 1) + { + papoLayers[nDeletedLayerIndex] = papoLayers[nDeletedLayerIndex + 1]; + nDeletedLayerIndex++; + } + nLayers--; + i--; + } + else if (pOGRSXFLayer) + pOGRSXFLayer->ResetReading(); + } +} + +OGRSXFLayer* OGRSXFDataSource::GetLayerById(GByte nID) +{ + for (size_t i = 0; i < nLayers; i++) + { + OGRSXFLayer* pOGRSXFLayer = (OGRSXFLayer*)papoLayers[i]; + if (pOGRSXFLayer && pOGRSXFLayer->GetId() == nID) + { + return pOGRSXFLayer; + } + } + return NULL; +} + +void OGRSXFDataSource::CreateLayers() +{ + //codes get from OSM.rsc http://gistoolkit.ru/download/classifiers/osm.zip + + //default layers set + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + OGRSXFLayer* pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 0, CPLString("SYSTEM"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + //default codes + for (size_t i = 1000000001; i < 1000000015; i++) + { + pLayer->AddClassifyCode(i); + } + pLayer->AddClassifyCode(91000000); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 1, CPLString("boundary"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(81110000); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 2, CPLString("water"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(31410000); + pLayer->AddClassifyCode(31120000); + pLayer->AddClassifyCode(31710000); + pLayer->AddClassifyCode(72310000); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 3, CPLString("city"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(41100000); + pLayer->AddClassifyCode(91100001); + pLayer->AddClassifyCode(91100002); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 4, CPLString("poi"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(123); + pLayer->AddClassifyCode(32410000); + pLayer->AddClassifyCode(44200000); + pLayer->AddClassifyCode(44200010); + pLayer->AddClassifyCode(47140000); + pLayer->AddClassifyCode(51121000); + pLayer->AddClassifyCode(51130000); + pLayer->AddClassifyCode(51410000); + pLayer->AddClassifyCode(51410001); + pLayer->AddClassifyCode(51420000); + pLayer->AddClassifyCode(53110000); + pLayer->AddClassifyCode(53311400); + pLayer->AddClassifyCode(53421000); + pLayer->AddClassifyCode(53510000); + pLayer->AddClassifyCode(53612000); + pLayer->AddClassifyCode(53612100); + pLayer->AddClassifyCode(53612101); + pLayer->AddClassifyCode(53612102); + pLayer->AddClassifyCode(53612103); + pLayer->AddClassifyCode(53612104); + pLayer->AddClassifyCode(53612105); + pLayer->AddClassifyCode(53612106); + pLayer->AddClassifyCode(53612107); + pLayer->AddClassifyCode(53612200); + pLayer->AddClassifyCode(53612201); + pLayer->AddClassifyCode(53612202); + pLayer->AddClassifyCode(53612203); + pLayer->AddClassifyCode(53612204); + pLayer->AddClassifyCode(53612205); + pLayer->AddClassifyCode(53612211); + pLayer->AddClassifyCode(53612300); + pLayer->AddClassifyCode(53612301); + pLayer->AddClassifyCode(53612302); + pLayer->AddClassifyCode(53612303); + pLayer->AddClassifyCode(53612304); + pLayer->AddClassifyCode(53612400); + pLayer->AddClassifyCode(53612401); + pLayer->AddClassifyCode(53612402); + pLayer->AddClassifyCode(53612403); + pLayer->AddClassifyCode(53612404); + pLayer->AddClassifyCode(53623000); + pLayer->AddClassifyCode(53623100); + pLayer->AddClassifyCode(53623110); + pLayer->AddClassifyCode(53623200); + pLayer->AddClassifyCode(53623300); + pLayer->AddClassifyCode(53623400); + pLayer->AddClassifyCode(53623500); + pLayer->AddClassifyCode(53623600); + pLayer->AddClassifyCode(53624000); + pLayer->AddClassifyCode(53624001); + pLayer->AddClassifyCode(53624002); + pLayer->AddClassifyCode(53624003); + pLayer->AddClassifyCode(53624004); + pLayer->AddClassifyCode(53624005); + pLayer->AddClassifyCode(53630000); + pLayer->AddClassifyCode(53631000); + pLayer->AddClassifyCode(53632101); + pLayer->AddClassifyCode(53632102); + pLayer->AddClassifyCode(53632103); + pLayer->AddClassifyCode(53632104); + pLayer->AddClassifyCode(53632105); + pLayer->AddClassifyCode(53632106); + pLayer->AddClassifyCode(53633001); + pLayer->AddClassifyCode(53633101); + pLayer->AddClassifyCode(53633102); + pLayer->AddClassifyCode(53633112); + pLayer->AddClassifyCode(53633114); + pLayer->AddClassifyCode(53635000); + pLayer->AddClassifyCode(53640000); + pLayer->AddClassifyCode(53641000); + pLayer->AddClassifyCode(53642000); + pLayer->AddClassifyCode(53643000); + pLayer->AddClassifyCode(53644000); + pLayer->AddClassifyCode(53646011); + pLayer->AddClassifyCode(53646013); + pLayer->AddClassifyCode(53646014); + pLayer->AddClassifyCode(53650000); + pLayer->AddClassifyCode(53650001); + pLayer->AddClassifyCode(53650002); + pLayer->AddClassifyCode(53650003); + pLayer->AddClassifyCode(53650004); + pLayer->AddClassifyCode(53650006); + pLayer->AddClassifyCode(53660000); + pLayer->AddClassifyCode(53661001); + pLayer->AddClassifyCode(53661002); + pLayer->AddClassifyCode(53661003); + pLayer->AddClassifyCode(53661004); + pLayer->AddClassifyCode(53661005); + pLayer->AddClassifyCode(53661006); + pLayer->AddClassifyCode(53661007); + pLayer->AddClassifyCode(53661008); + pLayer->AddClassifyCode(53661009); + pLayer->AddClassifyCode(53661010); + pLayer->AddClassifyCode(53661021); + pLayer->AddClassifyCode(53661100); + pLayer->AddClassifyCode(53662001); + pLayer->AddClassifyCode(53662002); + pLayer->AddClassifyCode(53662003); + pLayer->AddClassifyCode(53662004); + pLayer->AddClassifyCode(53672600); + pLayer->AddClassifyCode(53673300); + pLayer->AddClassifyCode(53700000); + pLayer->AddClassifyCode(53710000); + pLayer->AddClassifyCode(53720100); + pLayer->AddClassifyCode(53720200); + pLayer->AddClassifyCode(53720300); + pLayer->AddClassifyCode(53720301); + pLayer->AddClassifyCode(53720400); + pLayer->AddClassifyCode(53720500); + pLayer->AddClassifyCode(53720510); + pLayer->AddClassifyCode(53720520); + pLayer->AddClassifyCode(53720970); + pLayer->AddClassifyCode(53890000); + + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 5, CPLString("highway"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(10715); + pLayer->AddClassifyCode(11118); + pLayer->AddClassifyCode(2000253); + pLayer->AddClassifyCode(2000727); + pLayer->AddClassifyCode(51133200); + pLayer->AddClassifyCode(51220000); + pLayer->AddClassifyCode(61230000); + pLayer->AddClassifyCode(61230000); + pLayer->AddClassifyCode(62132000); + pLayer->AddClassifyCode(62213100); + pLayer->AddClassifyCode(62213101); + pLayer->AddClassifyCode(62213102); + pLayer->AddClassifyCode(62223000); + pLayer->AddClassifyCode(62331000); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 6, CPLString("railway"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(61111000); + pLayer->AddClassifyCode(61121100); + pLayer->AddClassifyCode(61121200); + pLayer->AddClassifyCode(61122000); + pLayer->AddClassifyCode(62131000); + + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 7, CPLString("building"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(44100000); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 8, CPLString("landuse"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(97); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 9, CPLString("vegetation"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(71111111); + pLayer->AddClassifyCode(71325000); + pLayer->AddClassifyCode(53890000); + pLayer->AddClassifyCode(22700000); + pLayer->AddClassifyCode(32282000); + pLayer->AddClassifyCode(71211000); + pLayer->AddClassifyCode(72120000); + pLayer->AddClassifyCode(71314000); + pLayer->AddClassifyCode(71312000); + + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 10, CPLString("fire"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(96); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 11, CPLString("roaddesign"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(60000000); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 13, CPLString("RoadStructure"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(62315000); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 14, CPLString("signature"), oSXFPassport.version, oSXFPassport.stMapDescription); + papoLayers[nLayers] = pLayer; + nLayers++; + + pLayer->AddClassifyCode(91100000); + pLayer->AddClassifyCode(91200000); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + papoLayers[nLayers] = new OGRSXFLayer(fpSXF, &hIOMutex, 255, CPLString("Not_Classified"), oSXFPassport.version, oSXFPassport.stMapDescription); + nLayers++; + +} + +void OGRSXFDataSource::CreateLayers(VSILFILE* fpRSC) +{ + + RSCHeader stRSCFileHeader; + int nFileHeaderSize = sizeof(stRSCFileHeader); + int nObjectsRead = VSIFReadL(&stRSCFileHeader, nFileHeaderSize, 1, fpRSC); + + if (nObjectsRead != 1) + { + CPLError(CE_Warning, CPLE_None, "RSC head read failed"); + return; + } + + GByte szLayersID[4]; + struct _layer{ + GUInt32 nLength; + char szName[32]; + char szShortName[16]; + GByte nNo; + GByte nPos; + GUInt16 nSematicCount; + }; + + GUInt32 i; + size_t nLayerStructSize = sizeof(_layer); + + VSIFSeekL(fpRSC, stRSCFileHeader.Layers.nOffset - sizeof(szLayersID), SEEK_SET); + VSIFReadL(&szLayersID, sizeof(szLayersID), 1, fpRSC); + vsi_l_offset nOffset = stRSCFileHeader.Layers.nOffset; + _layer LAYER; + + for (i = 0; i < stRSCFileHeader.Layers.nRecordCount; ++i) + { + VSIFReadL(&LAYER, nLayerStructSize, 1, fpRSC); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + bool bLayerFullName = CSLTestBoolean(CPLGetConfigOption("SXF_LAYER_FULLNAME", "NO")); + + char* pszRecoded; + if (bLayerFullName) + { + if (stRSCFileHeader.nFontEnc == 125) + pszRecoded = CPLRecode(LAYER.szName, "KOI8-R", CPL_ENC_UTF8); + else if (stRSCFileHeader.nFontEnc == 126) + pszRecoded = CPLRecode(LAYER.szName, "CP1251", CPL_ENC_UTF8); + else + pszRecoded = CPLStrdup(LAYER.szName); + + papoLayers[nLayers] = new OGRSXFLayer(fpSXF, &hIOMutex, LAYER.nNo, CPLString(pszRecoded), oSXFPassport.version, oSXFPassport.stMapDescription); + } + else + { + if (stRSCFileHeader.nFontEnc == 125) + pszRecoded = CPLRecode(LAYER.szShortName, "KOI8-R", CPL_ENC_UTF8); + else if (stRSCFileHeader.nFontEnc == 126) + pszRecoded = CPLRecode(LAYER.szShortName, "CP1251", CPL_ENC_UTF8); + else + pszRecoded = CPLStrdup(LAYER.szShortName); + + papoLayers[nLayers] = new OGRSXFLayer(fpSXF, &hIOMutex, LAYER.nNo, CPLString(pszRecoded), oSXFPassport.version, oSXFPassport.stMapDescription); + } + CPLFree(pszRecoded); + nLayers++; + + nOffset += LAYER.nLength; + VSIFSeekL(fpRSC, nOffset, SEEK_SET); + } + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1)); + papoLayers[nLayers] = new OGRSXFLayer(fpSXF, &hIOMutex, 255, CPLString("Not_Classified"), oSXFPassport.version, oSXFPassport.stMapDescription); + nLayers++; + + + char szObjectsID[4]; + struct _object{ + unsigned nLength; + unsigned nClassifyCode; + unsigned nObjectNumber; + unsigned nObjectCode; + char szShortName[32]; + char szName[32]; + char szGeomType; + char szLayernNo; + char szUnimportantSeg[14]; + }; + + VSIFSeekL(fpRSC, stRSCFileHeader.Objects.nOffset - sizeof(szObjectsID), SEEK_SET); + VSIFReadL(&szObjectsID, sizeof(szObjectsID), 1, fpRSC); + nOffset = stRSCFileHeader.Objects.nOffset; + _object OBJECT; + + for (unsigned i = 0; i < stRSCFileHeader.Objects.nRecordCount; ++i) + { + VSIFReadL(&OBJECT, sizeof(_object), 1, fpRSC); + + OGRSXFLayer* pLayer = GetLayerById(OBJECT.szLayernNo); + if (NULL != pLayer) + { + char* pszRecoded; + if (stRSCFileHeader.nFontEnc == 125) + pszRecoded = CPLRecode(OBJECT.szName, "KOI8-R", CPL_ENC_UTF8); + else if (stRSCFileHeader.nFontEnc == 126) + pszRecoded = CPLRecode(OBJECT.szName, "CP1251", CPL_ENC_UTF8); + else + pszRecoded = CPLStrdup(OBJECT.szName); //already in CPL_ENC_UTF8 + pLayer->AddClassifyCode(OBJECT.nClassifyCode, pszRecoded); + //printf("%d;%s\n", OBJECT.nClassifyCode, OBJECT.szName); + CPLFree(pszRecoded); + } + + nOffset += OBJECT.nLength; + VSIFSeekL(fpRSC, nOffset, SEEK_SET); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/ogrsxfdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/ogrsxfdriver.cpp new file mode 100644 index 000000000..92a3a802b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/ogrsxfdriver.cpp @@ -0,0 +1,146 @@ +/****************************************************************************** + * $Id: ogr_sxfdriver.cpp $ + * + * Project: SXF Translator + * Purpose: Definition of classes for OGR SXF driver. + * Author: Ben Ahmed Daho Ali, bidandou(at)yahoo(dot)fr + * Dmitry Baryshnikov, polimax@mail.ru + * + ****************************************************************************** + * Copyright (c) 2011, Ben Ahmed Daho Ali + * Copyright (c) 2013, NextGIS + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_sxf.h" + +CPL_CVSID("$Id: ogrsxfdriver.cpp $"); + + +extern "C" void RegisterOGRSXF(); + +/************************************************************************/ +/* ~OGRSXFDriver() */ +/************************************************************************/ + +OGRSXFDriver::~OGRSXFDriver() +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRSXFDriver::GetName() + +{ + return "SXF"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRSXFDriver::Open( const char * pszFilename, int bUpdate ) + +{ +/* -------------------------------------------------------------------- */ +/* Determine what sort of object this is. */ +/* -------------------------------------------------------------------- */ + + VSIStatBufL sStatBuf; + if (!EQUAL(CPLGetExtension(pszFilename), "sxf") || + VSIStatL(pszFilename, &sStatBuf) != 0 || + !VSI_ISREG(sStatBuf.st_mode)) + return FALSE; + + OGRSXFDataSource *poDS = new OGRSXFDataSource(); + + if( !poDS->Open( pszFilename, bUpdate ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* DeleteDataSource() */ +/************************************************************************/ + +OGRErr OGRSXFDriver::DeleteDataSource(const char* pszName) +{ + int iExt; + //TODO: add more extensions if aplicable + static const char *apszExtensions[] = { "szf", "rsc", "SZF", "RSC", NULL }; + + VSIStatBufL sStatBuf; + if (VSIStatL(pszName, &sStatBuf) != 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "%s does not appear to be a valid sxf file.", + pszName); + + return OGRERR_FAILURE; + } + + for (iExt = 0; apszExtensions[iExt] != NULL; iExt++) + { + const char *pszFile = CPLResetExtension(pszName, + apszExtensions[iExt]); + if (VSIStatL(pszFile, &sStatBuf) == 0) + VSIUnlink(pszFile); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSXFDriver::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap, ODrCDeleteDataSource)) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRSXF() */ +/************************************************************************/ +void RegisterOGRSXF() +{ + OGRSFDriver* poDriver = new OGRSXFDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Storage and eXchange Format" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_sxf.html" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "sxf" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/ogrsxflayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/ogrsxflayer.cpp new file mode 100644 index 000000000..5459cc52e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/ogrsxflayer.cpp @@ -0,0 +1,1496 @@ +/****************************************************************************** + * $Id: ogr_sxflayer.cpp $ + * + * Project: SXF Translator + * Purpose: Definition of classes for OGR SXF Layers. + * Author: Ben Ahmed Daho Ali, bidandou(at)yahoo(dot)fr + * Dmitry Baryshnikov, polimax@mail.ru + * Alexandr Lisovenko, alexander.lisovenko@gmail.com + * + ****************************************************************************** + * Copyright (c) 2011, Ben Ahmed Daho Ali + * Copyright (c) 2013, NextGIS + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#define _USE_MATH_DEFINES + +#include "ogr_sxf.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_p.h" +#include "ogr_srs_api.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: ogrsxflayer.cpp $"); + +/************************************************************************/ +/* OGRSXFLayer() */ +/************************************************************************/ + +OGRSXFLayer::OGRSXFLayer(VSILFILE* fp, CPLMutex** hIOMutex, GByte nID, const char* pszLayerName, int nVer, const SXFMapDescription& sxfMapDesc) : OGRLayer() +{ + sFIDColumn_ = "ogc_fid"; + fpSXF = fp; + nLayerID = nID; + stSXFMapDescription = sxfMapDesc; + stSXFMapDescription.pSpatRef->Reference(); + m_nSXFFormatVer = nVer; + oNextIt = mnRecordDesc.begin(); + m_hIOMutex = hIOMutex; + m_dfCoeff = stSXFMapDescription.dfScale / stSXFMapDescription.nResolution; + poFeatureDefn = new OGRFeatureDefn(pszLayerName); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + poFeatureDefn->SetGeomType(wkbUnknown); + if (poFeatureDefn->GetGeomFieldCount() != 0) + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(stSXFMapDescription.pSpatRef); + + //OGRGeomFieldDefn oGeomFieldDefn("Shape", wkbGeometryCollection); + //oGeomFieldDefn.SetSpatialRef(stSXFMapDescription.pSpatRef); + //poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn); + + OGRFieldDefn oFIDField = OGRFieldDefn(sFIDColumn_, OFTInteger); + poFeatureDefn->AddFieldDefn(&oFIDField); + + OGRFieldDefn oClCodeField = OGRFieldDefn( "CLCODE", OFTInteger ); + oClCodeField.SetWidth(10); + poFeatureDefn->AddFieldDefn( &oClCodeField ); + + OGRFieldDefn oClNameField = OGRFieldDefn( "CLNAME", OFTString ); + oClNameField.SetWidth(32); + poFeatureDefn->AddFieldDefn( &oClNameField ); + + OGRFieldDefn oNumField = OGRFieldDefn( "OBJECTNUMB", OFTInteger ); + oNumField.SetWidth(10); + poFeatureDefn->AddFieldDefn( &oNumField ); + + OGRFieldDefn oAngField = OGRFieldDefn("ANGLE", OFTReal); + poFeatureDefn->AddFieldDefn(&oAngField); + + OGRFieldDefn oTextField( "TEXT", OFTString ); + oTextField.SetWidth(255); + poFeatureDefn->AddFieldDefn( &oTextField ); +} + +/************************************************************************/ +/* ~OGRSXFLayer() */ +/************************************************************************/ + +OGRSXFLayer::~OGRSXFLayer() +{ + stSXFMapDescription.pSpatRef->Release(); + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* AddClassifyCode(unsigned nClassCode) */ +/* Add layer supported classify codes. Only records with this code can */ +/* be in layer */ +/************************************************************************/ + +void OGRSXFLayer::AddClassifyCode(unsigned nClassCode, const char *szName) +{ + if (szName != NULL) + { + mnClassificators[nClassCode] = CPLString(szName); + } + else + { + CPLString szIdName; + szIdName.Printf("%d", nClassCode); + mnClassificators[nClassCode] = szIdName; + } +} + +/************************************************************************/ +/* AddRecord() */ +/************************************************************************/ + +int OGRSXFLayer::AddRecord(long nFID, unsigned nClassCode, vsi_l_offset nOffset, bool bHasSemantic, size_t nSemanticsSize) +{ + if (mnClassificators.empty() || mnClassificators.find(nClassCode) != mnClassificators.end()) + { + mnRecordDesc[nFID] = nOffset; + //add addtionals semantics (attribute fields) + if (bHasSemantic) + { + size_t offset = 0; + + while (offset < nSemanticsSize) + { + SXFRecordAttributeInfo stAttrInfo; + bool bAddField = false; + size_t nCurrOff = 0; + int nReadObj = VSIFReadL(&stAttrInfo, 4, 1, fpSXF); + if (nReadObj == 1) + { + CPLString oFieldName; + if (snAttributeCodes.find(stAttrInfo.nCode) == snAttributeCodes.end()) + { + bAddField = true; + snAttributeCodes.insert(stAttrInfo.nCode); + oFieldName.Printf("SC_%d", stAttrInfo.nCode); + } + + SXFRecordAttributeType eType = (SXFRecordAttributeType)stAttrInfo.nType; + + offset += 4; + + + switch (eType) //TODO: set field type form RSC as here sometimes we have the codes and string values can be get from RSC by this code + { + case SXF_RAT_ASCIIZ_DOS: + { + if (bAddField) + { + OGRFieldDefn oField(oFieldName, OFTString); + oField.SetWidth(255); + poFeatureDefn->AddFieldDefn(&oField); + } + offset += stAttrInfo.nScale + 1; + nCurrOff = stAttrInfo.nScale + 1; + break; + } + case SXF_RAT_ONEBYTE: + { + if (bAddField) + { + OGRFieldDefn oField(oFieldName, OFTReal); + poFeatureDefn->AddFieldDefn(&oField); + } + offset += 1; + nCurrOff = 1; + break; + } + case SXF_RAT_TWOBYTE: + { + if (bAddField) + { + OGRFieldDefn oField(oFieldName, OFTReal); + poFeatureDefn->AddFieldDefn(&oField); + } + offset += 2; + nCurrOff = 2; + break; + } + case SXF_RAT_FOURBYTE: + { + if (bAddField) + { + OGRFieldDefn oField(oFieldName, OFTReal); + poFeatureDefn->AddFieldDefn(&oField); + } + offset += 4; + nCurrOff = 4; + break; + } + case SXF_RAT_EIGHTBYTE: + { + if (bAddField) + { + OGRFieldDefn oField(oFieldName, OFTReal); + poFeatureDefn->AddFieldDefn(&oField); + } + offset += 8; + nCurrOff = 8; + break; + } + case SXF_RAT_ANSI_WIN: + { + if (bAddField) + { + OGRFieldDefn oField(oFieldName, OFTString); + oField.SetWidth(255); + poFeatureDefn->AddFieldDefn(&oField); + } + unsigned nLen = unsigned(stAttrInfo.nScale) + 1; + offset += nLen; + nCurrOff = nLen; + break; + } + case SXF_RAT_UNICODE: + { + if (bAddField) + { + OGRFieldDefn oField(oFieldName, OFTString); + oField.SetWidth(255); + poFeatureDefn->AddFieldDefn(&oField); + } + unsigned nLen = (unsigned(stAttrInfo.nScale) + 1) * 2; + offset += nLen; + nCurrOff = nLen; + break; + } + case SXF_RAT_BIGTEXT: + { + if (bAddField) + { + OGRFieldDefn oField(oFieldName, OFTString); + oField.SetWidth(1024); + poFeatureDefn->AddFieldDefn(&oField); + } + GUInt32 scale2; + VSIFReadL(&scale2, sizeof(GUInt32), 1, fpSXF); + CPL_LSBUINT32PTR(&scale2); + + offset += scale2; + nCurrOff = scale2; + break; + } + default: + break; + } + } + if( nCurrOff == 0 ) + break; + VSIFSeekL(fpSXF, nCurrOff, SEEK_CUR); + } + } + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* SetNextByIndex() */ +/************************************************************************/ + +OGRErr OGRSXFLayer::SetNextByIndex(GIntBig nIndex) +{ + if (nIndex < 0 || nIndex > (long)mnRecordDesc.size()) + return OGRERR_FAILURE; + + oNextIt = mnRecordDesc.begin(); + std::advance(oNextIt, nIndex); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRSXFLayer::GetFeature(GIntBig nFID) +{ + std::map::const_iterator IT = mnRecordDesc.find(nFID); + if (IT != mnRecordDesc.end()) + { + VSIFSeekL(fpSXF, IT->second, SEEK_SET); + OGRFeature *poFeature = GetNextRawFeature(IT->first); + if (poFeature != NULL && poFeature->GetGeometryRef() != NULL && GetSpatialRef() != NULL) + { + poFeature->GetGeometryRef()->assignSpatialReference(GetSpatialRef()); + } + return poFeature; + } + + return NULL; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRSXFLayer::GetSpatialRef() +{ + return stSXFMapDescription.pSpatRef; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRSXFLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + if (bForce) + { + return OGRLayer::GetExtent(psExtent, bForce); + } + else + { + psExtent->MinX = stSXFMapDescription.Env.MinX; + psExtent->MaxX = stSXFMapDescription.Env.MaxX; + psExtent->MinY = stSXFMapDescription.Env.MinY; + psExtent->MaxY = stSXFMapDescription.Env.MaxY; + + return OGRERR_NONE; + } +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRSXFLayer::GetFeatureCount(int bForce) +{ + if (m_poFilterGeom == NULL && m_poAttrQuery == NULL) + return static_cast(mnRecordDesc.size()); + else + return OGRLayer::GetFeatureCount(bForce); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRSXFLayer::ResetReading() + +{ + oNextIt = mnRecordDesc.begin(); +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRSXFLayer::GetNextFeature() +{ + CPLMutexHolderD(m_hIOMutex); + while (oNextIt != mnRecordDesc.end()) + { + VSIFSeekL(fpSXF, oNextIt->second, SEEK_SET); + OGRFeature *poFeature = GetNextRawFeature(oNextIt->first); + + ++oNextIt; + + if (poFeature == NULL) + continue; + + if ((m_poFilterGeom == NULL + || FilterGeometry(poFeature->GetGeometryRef())) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate(poFeature))) + { + if (poFeature->GetGeometryRef() != NULL && GetSpatialRef() != NULL) + { + poFeature->GetGeometryRef()->assignSpatialReference(GetSpatialRef()); + } + + return poFeature; + } + + delete poFeature; + } + return NULL; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRSXFLayer::TestCapability( const char * pszCap ) + +{ + if (EQUAL(pszCap, OLCStringsAsUTF8)) + return TRUE; + else if (EQUAL(pszCap, OLCRandomRead)) + return TRUE; + else if (EQUAL(pszCap, OLCFastFeatureCount)) + return TRUE; + else if (EQUAL(pszCap, OLCFastGetExtent)) + return TRUE; + else if (EQUAL(pszCap, OLCFastSetNextByIndex)) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* TranslateXYH() */ +/************************************************************************/ +/**** + * TODO : Take into account informations given in the passport + * like unit of mesurement, type and dimensions (integer, float, double) of coordinate, + * the vector format .... + */ + +GUInt32 OGRSXFLayer::TranslateXYH(const SXFRecordDescription& certifInfo, + const char *psBuff, GUInt32 nBufLen, + double *dfX, double *dfY, double *dfH) +{ + //Xp, Yp(м) = Xo, Yo(м) + (Xd, Yd / R * S), (1) + + int offset = 0; + switch (certifInfo.eValType) + { + case SXF_VT_SHORT: + { + if( nBufLen < 4 ) + return 0; + GInt16 x, y; + memcpy(&y, psBuff, 2); + CPL_LSBINT16PTR(&y); + memcpy(&x, psBuff + 2, 2); + CPL_LSBINT16PTR(&x); + + if (stSXFMapDescription.bIsRealCoordinates) + { + *dfX = (double)x; + *dfY = (double)y; + } + else + { + if (m_nSXFFormatVer == 3) + { + *dfX = stSXFMapDescription.dfXOr + (double)x * m_dfCoeff; + *dfY = stSXFMapDescription.dfYOr + (double)y * m_dfCoeff; + } + else if (m_nSXFFormatVer == 4) + { + //TODO: check on real data + *dfX = stSXFMapDescription.dfXOr + (double)x * m_dfCoeff; + *dfY = stSXFMapDescription.dfYOr + (double)y * m_dfCoeff; + } + } + + offset += 4; + + if (dfH != NULL) + { + if( nBufLen < 4 + 4 ) + return 0; + float h; + memcpy(&h, psBuff + 4, 4); // H always in float + CPL_LSBPTR32(&h); + *dfH = (double)h; + + offset += 4; + } + } + break; + case SXF_VT_FLOAT: + { + if( nBufLen < 8 ) + return 0; + float x, y; + memcpy(&y, psBuff, 4); + CPL_LSBPTR32(&y); + memcpy(&x, psBuff + 4, 4); + CPL_LSBPTR32(&x); + + if (stSXFMapDescription.bIsRealCoordinates) + { + *dfX = (double)x; + *dfY = (double)y; + } + else + { + *dfX = stSXFMapDescription.dfXOr + (double)x * m_dfCoeff; + *dfY = stSXFMapDescription.dfYOr + (double)y * m_dfCoeff; + } + + offset += 8; + + if (dfH != NULL) + { + if( nBufLen < 8 + 4 ) + return 0; + float h; + memcpy(&h, psBuff + 8, 4); // H always in float + CPL_LSBPTR32(&h); + *dfH = (double)h; + + offset += 4; + } + } + break; + case SXF_VT_INT: + { + if( nBufLen < 8 ) + return 0; + GInt32 x, y; + memcpy(&y, psBuff, 4); + CPL_LSBPTR32(&y); + memcpy(&x, psBuff + 4, 4); + CPL_LSBPTR32(&x); + + if (stSXFMapDescription.bIsRealCoordinates) + { + *dfX = (double)x; + *dfY = (double)y; + } + else + { + //TODO: check on real data + if (m_nSXFFormatVer == 3) + { + *dfX = stSXFMapDescription.dfXOr + (double)x * m_dfCoeff; + *dfY = stSXFMapDescription.dfYOr + (double)y * m_dfCoeff; + } + else if (m_nSXFFormatVer == 4) + { + *dfX = stSXFMapDescription.dfXOr + (double)x * m_dfCoeff; + *dfY = stSXFMapDescription.dfYOr + (double)y * m_dfCoeff; + } + } + offset += 8; + + if (dfH != NULL) + { + if( nBufLen < 8 + 4 ) + return 0; + float h; + memcpy(&h, psBuff + 8, 4); // H always in float + CPL_LSBPTR32(&h); + *dfH = (double)h; + + offset += 4; + } + } + break; + case SXF_VT_DOUBLE: + { + if( nBufLen < 16 ) + return 0; + double x, y; + memcpy(&y, psBuff, 8); + CPL_LSBPTR64(&y); + memcpy(&x, psBuff + 8, 8); + CPL_LSBPTR64(&x); + + if (stSXFMapDescription.bIsRealCoordinates) + { + *dfX = x; + *dfY = y; + } + else + { + *dfX = stSXFMapDescription.dfXOr + x * m_dfCoeff; + *dfY = stSXFMapDescription.dfYOr + y * m_dfCoeff; + } + + offset += 16; + + if (dfH != NULL) + { + if( nBufLen < 16 + 8 ) + return 0; + double h; + memcpy(&h, psBuff + 16, 8); // H in double + CPL_LSBPTR64(&h); + *dfH = (double)h; + + offset += 8; + } + } + break; + }; + + return offset; +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRSXFLayer::GetNextRawFeature(long nFID) +{ + SXFRecordHeader stRecordHeader; + int nObjectRead; + + nObjectRead = VSIFReadL(&stRecordHeader, sizeof(SXFRecordHeader), 1, fpSXF); + + if (nObjectRead != 1 || stRecordHeader.nID != IDSXFOBJ) + { + CPLError(CE_Failure, CPLE_FileIO, "SXF. Read record failed."); + return NULL; + } + + SXFGeometryType eGeomType = SXF_GT_Unknown; + GByte code = 0; + + if (m_nSXFFormatVer == 3) + { + if (CHECK_BIT(stRecordHeader.nRef[2], 3)) + { + if (CHECK_BIT(stRecordHeader.nRef[2], 4)) + { + code = 0x22; + stRecordHeader.nSubObjectCount = 0; + } + else + { + code = 0x21; + stRecordHeader.nSubObjectCount = 0; + } + } + else + { + code = stRecordHeader.nRef[0] & 3;//get first 2 bits + } + } + else if (m_nSXFFormatVer == 4) + { + if (CHECK_BIT(stRecordHeader.nRef[2], 5)) + { + stRecordHeader.nSubObjectCount = 0; + } + + //check if vector + code = stRecordHeader.nRef[0] & 15;//get first 4 bits + if (code == 0x04) // xxxx0100 + { + code = 0x21; + stRecordHeader.nSubObjectCount = 0; + //if (CHECK_BIT(stRecordHeader.nRef[2], 5)) + //{ + // code = 0x22; + // stRecordHeader.nSubObjectCount = 0; + //} + //else + //{ + // code = 0x21; + // stRecordHeader.nSubObjectCount = 0; + //} + //if (CHECK_BIT(stRecordHeader.nRef[2], 4)) + //{ + //} + //else + //{ + //} + } + } + + if (code == 0x00) // xxxx0000 + eGeomType = SXF_GT_Line; + else if (code == 0x01) // xxxx0001 + eGeomType = SXF_GT_Polygon; + else if (code == 0x02) // xxxx0010 + eGeomType = SXF_GT_Point; + else if (code == 0x03) // xxxx0011 + eGeomType = SXF_GT_Text; + //beginning 4.0 + else if (code == 0x04) // xxxx0100 + { + CPLError(CE_Warning, CPLE_NotSupported, + "SXF. Not support type."); + eGeomType = SXF_GT_Vector; + } + else if (code == 0x05) // xxxx0101 + eGeomType = SXF_GT_TextTemplate; + else if (code == 0x21) + eGeomType = SXF_GT_VectorAngle; + else if (code == 0x22) + eGeomType = SXF_GT_VectorScaled; + + bool bHasAttributes = CHECK_BIT(stRecordHeader.nRef[1], 1); + bool bHasRefVector = CHECK_BIT(stRecordHeader.nRef[1], 3); + if (bHasRefVector == true) + CPLError(CE_Failure, CPLE_NotSupported, + "SXF. Parsing the vector of the tying not support."); + + SXFRecordDescription stCertInfo; + if (stRecordHeader.nPointCountSmall == 65535) + { + stCertInfo.nPointCount = stRecordHeader.nPointCount; + } + else + { + stCertInfo.nPointCount = stRecordHeader.nPointCountSmall; + } + stCertInfo.nSubObjectCount = stRecordHeader.nSubObjectCount; + + bool bFloatType = 0, bBigType = 0; + bool b3D(true); + if (m_nSXFFormatVer == 3) + { + b3D = CHECK_BIT(stRecordHeader.nRef[2], 1); + bFloatType = CHECK_BIT(stRecordHeader.nRef[2], 2); + bBigType = CHECK_BIT(stRecordHeader.nRef[1], 2); + stCertInfo.bHasTextSign = CHECK_BIT(stRecordHeader.nRef[2], 5); + } + else if (m_nSXFFormatVer == 4) + { + b3D = CHECK_BIT(stRecordHeader.nRef[2], 1); + bFloatType = CHECK_BIT(stRecordHeader.nRef[2], 2); + bBigType = CHECK_BIT(stRecordHeader.nRef[1], 2); + stCertInfo.bHasTextSign = CHECK_BIT(stRecordHeader.nRef[2], 3); + } + // Else trouble. + + if (b3D) //xÑ…Ñ…Ñ…Ñ…Ñ…1Ñ… + stCertInfo.bDim = 1; + else + stCertInfo.bDim = 0; + + if (bFloatType) + { + if (bBigType) + { + stCertInfo.eValType = SXF_VT_DOUBLE; + } + else + { + stCertInfo.eValType = SXF_VT_FLOAT; + } + } + else + { + if (bBigType) + { + stCertInfo.eValType = SXF_VT_INT; + } + else + { + stCertInfo.eValType = SXF_VT_SHORT; + } + } + + + stCertInfo.bFormat = CHECK_BIT(stRecordHeader.nRef[2], 0); + stCertInfo.eGeomType = eGeomType; + + OGRFeature *poFeature = NULL; + if( stRecordHeader.nGeometryLength > 100 * 1024 * 1024 ) + return NULL; + char * recordCertifBuf = (char *)VSIMalloc(stRecordHeader.nGeometryLength); + if( recordCertifBuf == NULL ) + return NULL; + nObjectRead = VSIFReadL(recordCertifBuf, stRecordHeader.nGeometryLength, 1, fpSXF); + if (nObjectRead != 1) + { + CPLError(CE_Failure, CPLE_FileIO, + "SXF. Read geometry failed."); + CPLFree(recordCertifBuf); + return NULL; + } + + if (eGeomType == SXF_GT_Point) + poFeature = TranslatePoint(stCertInfo, recordCertifBuf, + stRecordHeader.nGeometryLength); + else if (eGeomType == SXF_GT_Line || eGeomType == SXF_GT_VectorScaled) + poFeature = TranslateLine(stCertInfo, recordCertifBuf, + stRecordHeader.nGeometryLength); + else if (eGeomType == SXF_GT_Polygon) + poFeature = TranslatePolygon(stCertInfo, recordCertifBuf, + stRecordHeader.nGeometryLength); + else if (eGeomType == SXF_GT_Text) + poFeature = TranslateText(stCertInfo, recordCertifBuf, + stRecordHeader.nGeometryLength); + else if (eGeomType == SXF_GT_VectorAngle) + { + poFeature = TranslateVetorAngle(stCertInfo, recordCertifBuf, + stRecordHeader.nGeometryLength); + } + else if (eGeomType == SXF_GT_Vector ) + { + CPLError( CE_Warning, CPLE_NotSupported, + "SXF. Geometry type Vector do not support." ); + CPLFree(recordCertifBuf); + return NULL; + } + else if (eGeomType == SXF_GT_TextTemplate ) // TODO realise this + { + CPLError( CE_Warning, CPLE_NotSupported, + "SXF. Geometry type Text Template do not support." ); + CPLFree(recordCertifBuf); + return NULL; + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "SXF. Unsupported geometry type."); + CPLFree(recordCertifBuf); + return NULL; + } + + if( poFeature == NULL ) + { + CPLFree(recordCertifBuf); + return NULL; + } + + poFeature->SetField(sFIDColumn_, (int)nFID); + + poFeature->SetField("CLCODE", (int)stRecordHeader.nClassifyCode); + + CPLString szName = mnClassificators[stRecordHeader.nClassifyCode]; + + if (szName.empty()) + { + szName.Printf("%d", stRecordHeader.nClassifyCode); + } + poFeature->SetField("CLNAME", szName); + + poFeature->SetField("OBJECTNUMB", stRecordHeader.nSubObjectCount); + + if (bHasAttributes) + { + if( stRecordHeader.nFullLength < 32 || + stRecordHeader.nGeometryLength > stRecordHeader.nFullLength - 32 ) + { + CPLFree(recordCertifBuf); + delete poFeature; + return NULL; + } + size_t nSemanticsSize = stRecordHeader.nFullLength - 32 - stRecordHeader.nGeometryLength; + if( nSemanticsSize > 1024 * 1024 ) + { + CPLFree(recordCertifBuf); + delete poFeature; + return NULL; + } + char * psSemanticsdBuf = (char *)VSIMalloc(nSemanticsSize); + if( psSemanticsdBuf == NULL ) + { + CPLFree(recordCertifBuf); + delete poFeature; + return NULL; + } + char * psSemanticsdBufOrig = psSemanticsdBuf; + nObjectRead = VSIFReadL(psSemanticsdBuf, nSemanticsSize, 1, fpSXF); + if (nObjectRead == 1) + { + size_t offset = 0; + double nVal = 0; + + while (offset + sizeof(SXFRecordAttributeInfo) < nSemanticsSize) + { + char *psSemanticsdBufBeg = psSemanticsdBuf + offset; + SXFRecordAttributeInfo stAttInfo = *(SXFRecordAttributeInfo*)psSemanticsdBufBeg; + offset += 4; + + CPLString oFieldName; + oFieldName.Printf("SC_%d", stAttInfo.nCode); + + CPLString oFieldValue; + + SXFRecordAttributeType eType = (SXFRecordAttributeType)stAttInfo.nType; + + switch (eType) + { + case SXF_RAT_ASCIIZ_DOS: + { + unsigned nLen = unsigned(stAttInfo.nScale) + 1; + if( nLen > nSemanticsSize || nSemanticsSize - nLen < offset ) + { + nSemanticsSize = 0; + break; + } + char * value = (char*)CPLMalloc(nLen); + memcpy(value, psSemanticsdBuf + offset, nLen); + value[nLen-1] = 0; + char* pszRecoded = CPLRecode(value, "CP866", CPL_ENC_UTF8); + poFeature->SetField(oFieldName, pszRecoded); + CPLFree(pszRecoded); + CPLFree(value); + + offset += stAttInfo.nScale + 1; + break; + } + case SXF_RAT_ONEBYTE: + { + GByte nTmpVal; + if( offset + sizeof(GByte) > nSemanticsSize ) + { + nSemanticsSize = 0; + break; + } + memcpy(&nTmpVal, psSemanticsdBuf + offset, sizeof(GByte)); + nVal = double(nTmpVal) * pow(10.0, (double)stAttInfo.nScale); + + poFeature->SetField(oFieldName, nVal); + offset += 1; + break; + } + case SXF_RAT_TWOBYTE: + { + GInt16 nTmpVal; + if( offset + sizeof(GInt16) > nSemanticsSize ) + { + nSemanticsSize = 0; + break; + } + memcpy(&nTmpVal, psSemanticsdBuf + offset, sizeof(GInt16)); + nVal = double(CPL_LSBWORD16(nTmpVal)) * pow(10.0, (double)stAttInfo.nScale); + + poFeature->SetField(oFieldName, nVal); + offset += 2; + break; + } + case SXF_RAT_FOURBYTE: + { + GInt32 nTmpVal; + if( offset + sizeof(GInt32) > nSemanticsSize ) + { + nSemanticsSize = 0; + break; + } + memcpy(&nTmpVal, psSemanticsdBuf + offset, sizeof(GInt32)); + nVal = double(CPL_LSBWORD32(nTmpVal)) * pow(10.0, (double)stAttInfo.nScale); + + poFeature->SetField(oFieldName, nVal); + offset += 4; + break; + } + case SXF_RAT_EIGHTBYTE: + { + double dfTmpVal; + if( offset + sizeof(double) > nSemanticsSize ) + { + nSemanticsSize = 0; + break; + } + memcpy(&dfTmpVal, psSemanticsdBuf + offset, sizeof(double)); + CPL_LSBPTR64(&dfTmpVal); + double d = dfTmpVal * pow(10.0, (double)stAttInfo.nScale); + + poFeature->SetField(oFieldName, d); + + offset += 8; + break; + } + case SXF_RAT_ANSI_WIN: + { + unsigned nLen = unsigned(stAttInfo.nScale) + 1; + if( nLen > nSemanticsSize || nSemanticsSize - nLen < offset ) + { + nSemanticsSize = 0; + break; + } + char * value = (char*)CPLMalloc(nLen); + memcpy(value, psSemanticsdBuf + offset, nLen); + value[nLen-1] = 0; + char* pszRecoded = CPLRecode(value, "CP1251", CPL_ENC_UTF8); + poFeature->SetField(oFieldName, pszRecoded); + CPLFree(pszRecoded); + CPLFree(value); + + offset += nLen; + break; + } + case SXF_RAT_UNICODE: + { + unsigned nLen = (unsigned(stAttInfo.nScale) + 1) * 2; + if(nLen < 2 || nLen > nSemanticsSize || nSemanticsSize - nLen < offset ) + { + nSemanticsSize = 0; + break; + } + char* value = (char*)CPLMalloc(nLen); + memcpy(value, psSemanticsdBuf + offset, nLen - 2); + value[nLen-1] = 0; + value[nLen-2] = 0; + char* dst = (char*)CPLMalloc(nLen); + int nCount = 0; + for(int i = 0; (unsigned)i < nLen; i += 2) + { + unsigned char ucs = value[i]; + + if (ucs < 0x80U) + { + dst[nCount++] = ucs; + } + else + { + dst[nCount++] = 0xc0 | (ucs >> 6); + dst[nCount++] = 0x80 | (ucs & 0x3F); + } + } + + poFeature->SetField(oFieldName, dst); + CPLFree(dst); + CPLFree(value); + + offset += nLen; + break; + } + case SXF_RAT_BIGTEXT: + { + GUInt32 scale2; + if( offset + sizeof(GUInt32) > nSemanticsSize ) + { + nSemanticsSize = 0; + break; + } + memcpy(&scale2, psSemanticsdBuf + offset, sizeof(GUInt32)); + CPL_LSBUINT32PTR(&scale2); + /* FIXME add ?: offset += sizeof(GUInt32); */ + if( scale2 > nSemanticsSize - 1 || nSemanticsSize - (scale2 + 1) < offset ) + { + nSemanticsSize = 0; + break; + } + + char * value = (char*)CPLMalloc(scale2 + 1); + memcpy(value, psSemanticsdBuf + offset, scale2 + 1); + value[scale2] = 0; + char* pszRecoded = CPLRecode(value, CPL_ENC_UTF16, CPL_ENC_UTF8); + poFeature->SetField(oFieldName, pszRecoded); + CPLFree(pszRecoded); + CPLFree(value); + + offset += scale2; + break; + } + default: + CPLFree(recordCertifBuf); + CPLFree(psSemanticsdBufOrig); + delete poFeature; + return NULL; + } + } + } + CPLFree(psSemanticsdBufOrig); + } + + poFeature->SetFID(nFID); + + CPLFree(recordCertifBuf); + + return poFeature; +} + +/************************************************************************/ +/* TranslatePoint () */ +/************************************************************************/ + +OGRFeature *OGRSXFLayer::TranslatePoint(const SXFRecordDescription& certifInfo, + const char * psRecordBuf, GUInt32 nBufLen) +{ + double dfX = 1.0; + double dfY = 1.0; + double dfZ = 0.0; + GUInt32 nOffset = 0; + GUInt32 nDelta = 0; + + if (certifInfo.bDim == 1) + { + nDelta = TranslateXYH( certifInfo, psRecordBuf , nBufLen, &dfX, &dfY, &dfZ ); + } + else + { + nDelta = TranslateXYH( certifInfo, psRecordBuf , nBufLen, &dfX, &dfY ); + } + + if( nDelta == 0 ) + return NULL; + nOffset += nDelta; + + //OGRFeatureDefn *fd = poFeatureDefn->Clone(); + //fd->SetGeomType( wkbMultiPoint ); + // OGRFeature *poFeature = new OGRFeature(fd); + OGRFeature *poFeature = new OGRFeature(poFeatureDefn); + OGRMultiPoint* poMPt = new OGRMultiPoint(); + + poMPt->addGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ) ); + +/*---------------------- Reading SubObjects --------------------------------*/ + + for(int count=0 ; count < certifInfo.nSubObjectCount ; count++) + { + if( nOffset + 4 > nBufLen ) + break; + + GUInt16 nSubObj; + memcpy(&nSubObj, psRecordBuf + nOffset, 2); + CPL_LSBUINT16PTR(&nSubObj); + + GUInt16 nCoords; + memcpy(&nCoords, psRecordBuf + nOffset + 2, 2); + CPL_LSBUINT16PTR(&nCoords); + + nOffset +=4; + + for (int i=0; i < nCoords ; i++) + { + const char * psCoords = psRecordBuf + nOffset ; + + if (certifInfo.bDim == 1) + { + nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY, &dfZ ); + } + else + { + dfZ = 0.0; + nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY ); + } + + if( nDelta == 0 ) + break; + nOffset += nDelta; + + poMPt->addGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ) ); + } + } + +/***** + * TODO : + * - Translate graphics + * - Translate 3D vector + */ + + poFeature->SetGeometryDirectly( poMPt ); + + return poFeature; +} + +/************************************************************************/ +/* TranslateLine () */ +/************************************************************************/ + +OGRFeature *OGRSXFLayer::TranslateLine(const SXFRecordDescription& certifInfo, + const char * psRecordBuf, GUInt32 nBufLen) +{ + double dfX = 1.0; + double dfY = 1.0; + double dfZ = 0.0; + GUInt32 nOffset = 0; + GUInt32 count; + GUInt32 nDelta = 0; + + //OGRFeatureDefn *fd = poFeatureDefn->Clone(); + //fd->SetGeomType( wkbMultiLineString ); + // OGRFeature *poFeature = new OGRFeature(fd); + + OGRFeature *poFeature = new OGRFeature(poFeatureDefn); + OGRMultiLineString *poMLS = new OGRMultiLineString (); + +/*---------------------- Reading Primary Line --------------------------------*/ + + OGRLineString* poLS = new OGRLineString(); + + for(count=0 ; count < certifInfo.nPointCount ; count++) + { + const char * psCoords = psRecordBuf + nOffset ; + + if (certifInfo.bDim == 1) + { + nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY, &dfZ ); + } + else + { + dfZ = 0.0; + nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY ); + } + + if( nDelta == 0 ) + break; + nOffset += nDelta; + + poLS->addPoint( dfX, dfY, dfZ ); + } + + poMLS->addGeometry( poLS ); + +/*---------------------- Reading Sub Lines --------------------------------*/ + + for(int count=0 ; count < certifInfo.nSubObjectCount ; count++) + { + poLS->empty(); + + if( nOffset + 4 > nBufLen ) + break; + + GUInt16 nSubObj; + memcpy(&nSubObj, psRecordBuf + nOffset, 2); + CPL_LSBUINT16PTR(&nSubObj); + + GUInt16 nCoords; + memcpy(&nCoords, psRecordBuf + nOffset + 2, 2); + CPL_LSBUINT16PTR(&nCoords); + + nOffset +=4; + + for (int i=0; i < nCoords ; i++) + { + const char * psCoords = psRecordBuf + nOffset ; + if (certifInfo.bDim == 1) + { + nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY, &dfZ ); + } + else + { + dfZ = 0.0; + nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY ); + } + + if( nDelta == 0 ) + break; + nOffset += nDelta; + + poLS->addPoint( dfX, dfY, dfZ ); + } + + poMLS->addGeometry( poLS ); + } // for + + delete poLS; + poFeature->SetGeometryDirectly( poMLS ); + +/***** + * TODO : + * - Translate graphics + * - Translate 3D vector + */ + + return poFeature; +} + +/************************************************************************/ +/* TranslateVetorAngle() */ +/************************************************************************/ + +OGRFeature *OGRSXFLayer::TranslateVetorAngle(const SXFRecordDescription& certifInfo, + const char * psRecordBuf, GUInt32 nBufLen) +{ + if (certifInfo.nPointCount != 2) + { + CPLError(CE_Failure, CPLE_NotSupported, + "SXF. The vector object should have 2 points, but not."); + return NULL; + } + + double dfX = 1.0; + double dfY = 1.0; + double dfZ = 0.0; + GUInt32 nOffset = 0; + GUInt32 count; + GUInt32 nDelta = 0; + + OGRFeature *poFeature = new OGRFeature(poFeatureDefn); + OGRPoint *poPT = new OGRPoint(); + + /*---------------------- Reading Primary Line --------------------------------*/ + + OGRLineString* poLS = new OGRLineString(); + + for (count = 0; count < certifInfo.nPointCount; count++) + { + const char * psCoords = psRecordBuf + nOffset; + + if (certifInfo.bDim == 1) + { + nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY, &dfZ ); + } + else + { + dfZ = 0.0; + nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY ); + } + if (nDelta == 0) + break; + nOffset += nDelta; + + poLS->addPoint(dfX, dfY, dfZ); + } + + poLS->StartPoint(poPT); + + OGRPoint *poAngPT = new OGRPoint(); + poLS->EndPoint(poAngPT); + + double xDiff = poPT->getX() - poAngPT->getX(); + double yDiff = poPT->getY() - poAngPT->getY(); + double dfAngle = atan2(xDiff, yDiff) * TO_DEGREES - 90; + if (dfAngle < 0) + dfAngle += 360; + + poFeature->SetGeometryDirectly(poPT); + poFeature->SetField("ANGLE", dfAngle); + + delete poAngPT; + delete poLS; + + return poFeature; +} + + +/************************************************************************/ +/* TranslatePolygon () */ +/************************************************************************/ + +OGRFeature *OGRSXFLayer::TranslatePolygon(const SXFRecordDescription& certifInfo, + const char * psRecordBuf, GUInt32 nBufLen) +{ + double dfX = 1.0; + double dfY = 1.0; + double dfZ = 0.0; + GUInt32 nOffset = 0; + GUInt32 count; + GUInt32 nDelta = 0; + + OGRFeature *poFeature = new OGRFeature(poFeatureDefn); + OGRPolygon *poPoly = new OGRPolygon(); + OGRLineString* poLS = new OGRLineString(); + +/*---------------------- Reading Primary Polygon --------------------------------*/ + for(count=0 ; count < certifInfo.nPointCount ; count++) + { + const char * psBuf = psRecordBuf + nOffset; + if (certifInfo.bDim == 1) + { + nDelta = TranslateXYH( certifInfo, psBuf, nBufLen - nOffset, &dfX, &dfY, &dfZ ); + } + else + { + dfZ = 0.0; + nDelta = TranslateXYH( certifInfo, psBuf, nBufLen - nOffset, &dfX, &dfY ); + } + + if( nDelta == 0 ) + break; + nOffset += nDelta; + poLS->addPoint( dfX, dfY, dfZ ); + + } // for + + OGRLinearRing *poLR = new OGRLinearRing(); + poLR->addSubLineString( poLS, 0 ); + + poPoly->addRingDirectly( poLR ); + +/*---------------------- Reading Sub Lines --------------------------------*/ + + for(int count=0 ; count < certifInfo.nSubObjectCount ; count++) + { + poLS->empty(); + + if( nOffset + 4 > nBufLen ) + break; + + GUInt16 nSubObj; + memcpy(&nSubObj, psRecordBuf + nOffset, 2); + CPL_LSBUINT16PTR(&nSubObj); + + GUInt16 nCoords; + memcpy(&nCoords, psRecordBuf + nOffset + 2, 2); + CPL_LSBUINT16PTR(&nCoords); + + nOffset +=4; + + int i; + for (i=0; i < nCoords ; i++) + { + const char * psCoords = psRecordBuf + nOffset ; + if (certifInfo.bDim == 1) + { + nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY, &dfZ ); + } + else + { + dfZ = 0.0; + nDelta = TranslateXYH( certifInfo, psCoords, nBufLen - nOffset, &dfX, &dfY ); + } + + if( nDelta == 0 ) + break; + nOffset += nDelta; + + poLS->addPoint( dfX, dfY, dfZ ); + } + + OGRLinearRing *poLR = new OGRLinearRing(); + poLR->addSubLineString( poLS, 0 ); + + poPoly->addRingDirectly( poLR ); + } // for + + poFeature->SetGeometryDirectly( poPoly ); //poLS); + delete poLS; + +/***** + * TODO : + * - Translate graphics + * - Translate 3D vector + */ + return poFeature; +} + +/************************************************************************/ +/* TranslateText () */ +/************************************************************************/ +OGRFeature *OGRSXFLayer::TranslateText(const SXFRecordDescription& certifInfo, + const char * psRecordBuf, GUInt32 nBufLen) +{ + double dfX = 1.0; + double dfY = 1.0; + double dfZ = 0.0; + GUInt32 nOffset = 0; + GUInt32 count; + GUInt32 nDelta = 0; + //OGRFeatureDefn *fd = poFeatureDefn->Clone(); + //fd->SetGeomType( wkbLineString ); + // OGRFeature *poFeature = new OGRFeature(fd); + + OGRFeature *poFeature = new OGRFeature(poFeatureDefn); + OGRLineString* poLS = new OGRLineString(); + + for(count=0 ; count < certifInfo.nPointCount ; count++) + { + const char * psBuf = psRecordBuf + nOffset; + if (certifInfo.bDim == 1) + { + nDelta = TranslateXYH( certifInfo, psBuf, nBufLen - nOffset, &dfX, &dfY, &dfZ ); + } + else + { + dfZ = 0.0; + nDelta = TranslateXYH( certifInfo, psBuf, nBufLen - nOffset, &dfX, &dfY ); + } + if( nDelta == 0 ) + break; + + nOffset += nDelta; + poLS->addPoint( dfX, dfY, dfZ ); + } + + poFeature->SetGeometryDirectly( poLS ); + +/*------------------ READING TEXT VALUE ---------------------------------------*/ + + if ( certifInfo.nSubObjectCount == 0 && certifInfo.bHasTextSign == true) + { + if( nOffset + 1 > nBufLen ) + return poFeature; + const char * pszTxt = psRecordBuf + nOffset; + GByte nTextL = (GByte) *pszTxt; + if( nOffset + 1 + nTextL > nBufLen ) + return poFeature; + + char * pszTextBuf = (char *)CPLMalloc( nTextL+1 ); + + strncpy(pszTextBuf, (pszTxt+1), nTextL); + pszTextBuf[nTextL] = '\0'; + + //TODO: Check encoding from sxf + poFeature->SetField("TEXT", pszTextBuf); + + CPLFree( pszTextBuf ); + } + + +/***** + * TODO : + * - Translate graphics + * - Translate 3D vector + */ + + return poFeature; +} + +const char* OGRSXFLayer::GetFIDColumn() +{ + return sFIDColumn_.c_str(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/org_sxf_defs.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/org_sxf_defs.h new file mode 100644 index 000000000..22ddba2f3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/sxf/org_sxf_defs.h @@ -0,0 +1,373 @@ +/****************************************************************************** + * $Id: org_sxf_defs.h $ + * + * Project: SXF Translator + * Purpose: Include file defining Records Structures for file reading and + * basic constants. + * Author: Ben Ahmed Daho Ali, bidandou(at)yahoo(dot)fr + * Dmitry Baryshnikov, polimax@mail.ru + * Alexandr Lisovenko, alexander.lisovenko@gmail.com + * + ****************************************************************************** + * Copyright (c) 2011, Ben Ahmed Daho Ali + * Copyright (c) 2013, NextGIS + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + ****************************************************************************** + * Structure of the SXF file : + * ---------------------- + * - Header + * - Passport + * - Descriptor of data + * - Records + * - Title of the record + * - The certificate of the object (the geomety) + * - sub-objects + * - The graphic description of object + * - The description of the vector of the tying of the 3d- model of object + * - Semantics of object + * + * Notes : + * ------- + * Note 1. Flag of the state of data (2 bits): + * xxxxxx11- given in the state e (size of the exchange of data). + * + * Note 2. Flag of the correspondence to projection (1 bit): + * xxxxx0xx - do not correspond to the projection (i.e. map it can have turning + * relative to true position and certain deformation); + * xxxxx1xx - correspond to projection. + * + * Note 3. Flag of the presence of real coordinates (2 bits): + * xxx00xxx - entire certificate of objects is represented in the conditional + * system of coordinates (in the samples); + * xxx11xxx - entire certificate of objects is represented in the real coordinates + * in the locality in accordance with the specifications of sheet + * (projection, the coordinate system, unit of measurement), + * the data about the scale and the discretion of digitization bear + * reference nature. + * + * Note 4. Flag of the method of coding (2 bits): + * x00xxxxx - the classification codes of objects and semantic characteristics + * are represented by the decimal numbers, recorded in the binary + * form (for example: the code of the object “32100000” will be written + * down in the form 0x01E9CEA0, the code of semantics “253” - in the form 0x00FD). + * + * Note 5. Table of generalization (1 bit): + * 0xxxxxxx - the level of generalization is assigned according to the table of the + * general maps (it is described in Table 2.4); + * 1xxxxxxx - noload condition the level of generalization is assigned according to + * the table of the large-scale maps (it is described in Table 2.5). + * + * Note 6. Flag of coding the texts of the Texts objects (1 bytes): + * 0- in the coding ASCIIZ (Dos); + * 1- in the coding ANSI (Windows); + * 2- in the coding KOI-8 (Unix). + * + * Note 7. Flag of the accuracy of coordinates (1 bytes): + * 0 – are not established; + * 1 – the increased accuracy of storage of coordinates (meters, radians or degrees); + * 2 – of coordinate are recorded with an accuracy to centimeter (meters, 2 signs after comma); + * 3 – coordinates are recorded with an accuracy to millimeter (meters, 3 sign after comma). + * + * Note 8. Form of the framework (1 byte): + * -1- it is not established; + * 0- map is unconfined by the framework; + * 1- trapeziform without the salient points; + * 2- trapeziform with the salient points; + * 3- rectangular; + * 4- circular; + * 5- arbitrary. + * + * Note 9. Sign of output to the framework (4 bits): + * 0000xxxx - there are no outputs to the framework; + * 1000xxxx - northern framework; + * 0100xxxx - eastern framework; + * 0010xxxx - southern framework; + * 0001xxxx - western framework. + * + * Note 10. Size of the element of certificate (1 bit): + * xxxxx0xx - 2 bytes (for the integer value); + * 4 bytes (for the floating point); + * xxxxx1xx - 4 bytes (for the integer value); + * 8 bytes (for the floating point). + * + * Note 11. Sign of certificate with the text (1 bit): + * xxxx0xxx - certificate contains only the coordinates of points; + * xxxx1xxx - no-load condition certificate contains the text of signature, + * is allowed only for the objects of the type "signature" or + * "the template of signature". + * + * Note 12. [Masshtabiruemost] of drawing (sign) (1 bit): + * xx0xxxxx - arbitrary symbol of object not scaled; + * xx1xxxxx - the arbitrary symbol of object is scaled during the mapping. + * + * Note 13. Sign of the construction of spline on the certificate (2 bits): + * 00xxxxxx – the construction of spline with the visualization is not carried out; + * 01xxxxxx – smoothing out spline (cutting angles); + * 10xxxxxx – enveloping spline (it penetrates all points of certificate). + ****************************************************************************/ + +#ifndef SXF_DEFS_H +#define SXF_DEFS_H + +#define IDSXF 0x00465853 /* SXF */ + +#define IDSXFDATA 0x00544144 /* DAT */ +#define IDSXFOBJ 0X7FFF7FFF /* Object */ +#define IDSXFGRAPH 0X7FFF7FFE /* graphics section */ +#define IDSXFVECT3D 0X7FFF7FFD /* 3D vector section */ + +#include + +#include "cpl_port.h" + +enum SXFDataState /* Flag of the state of the data (Note 1) */ +{ + SXF_DS_UNKNOWN = 0, + SXF_DS_EXCHANGE = 8 +}; + +enum SXFCodingType /* Flag of the semantics coding type (Note 4) */ +{ + SXF_SEM_DEC = 0, + SXF_SEM_HEX = 1, + SXF_SEM_TXT = 2 +}; + +enum SXFGeneralizationType /* Flag of the source for generalization data (Note 5) */ +{ + SXF_GT_SMALL_SCALE = 0, + SXF_GT_LARGE_SCALE = 1 +}; + +enum SXFTextEncoding /* Flag of text encoding (Note 6) */ +{ + SXF_ENC_DOS = 0, + SXF_ENC_WIN = 1, + SXF_ENC_KOI_8 = 2 +}; + +enum SXFCoordinatesAccuracy /* Flag of coordinate storing accuracy (Note 7) */ +{ + SXF_COORD_ACC_UNDEFINED = 0, + SXF_COORD_ACC_HIGH = 1, //meters, radians or degree + SXF_COORD_ACC_CM = 2, //cantimeters + SXF_COORD_ACC_MM = 3, //millimeters + SXF_COORD_ACC_DM = 4 //decimeters +}; + +typedef struct +{ +// SXFDataState stDataState; /* Flag of the state of the data (Note 1) may be will be needed in future*/ + bool bProjectionDataCompliance; /* Flag of the correspondence to the projection (Note 2) */ + bool bRealCoordinatesCompliance; /* Flag of the presence of the real coordinates (Note 3) */ + SXFCodingType stCodingType; /* Flag of the semantics coding type (Note 4) */ + SXFGeneralizationType stGenType; /* Flag of the source for generalization data (Note 5) */ + SXFTextEncoding stEnc; /* Flag of text encoding (Note 6) */ + SXFCoordinatesAccuracy stCoordAcc; /* Flag of coordinate storing accuracy (Note 7) */ + bool bSort; +} SXFInformationFlags; + +enum SXFCoordinateMeasUnit +{ + SXF_COORD_MU_METRE = 1, + SXF_COORD_MU_DECIMETRE, + SXF_COORD_MU_CENTIMETRE, + SXF_COORD_MU_MILLIMETRE, + SXF_COORD_MU_DEGREE, + SXF_COORD_MU_RADIAN +} ; + +typedef struct +{ + long double stProjCoords[8]; //X(0) & Y(1) South West, X(2) & Y(3) North West, X(4) & Y(5) North East, X(6) & Y(7) South East + long double stGeoCoords[8]; + long double stFrameCoords[8]; + OGREnvelope Env; + OGRSpatialReference *pSpatRef; + SXFCoordinateMeasUnit eUnitInPlan; + double dfXOr; + double dfYOr; + double dfFalseNorthing; + double dfFalseEasting; + GUInt32 nResolution; + long double dfScale; + bool bIsRealCoordinates; + SXFCoordinatesAccuracy stCoordAcc; + +} SXFMapDescription; + + +enum SXFCoordinateType +{ + SXF_CT_RECTANGULAR = 0, + SXF_CT_GEODETIC +}; + + +/* + * List of SXF file format geometry types. + */ +enum SXFGeometryType +{ + SXF_GT_Unknown = -1, + SXF_GT_Line = 0, /* MultiLineString geometric object */ + SXF_GT_Polygon = 1, /* Polygon geometric object */ + SXF_GT_Point = 2, /* MultiPoint geometric object */ + SXF_GT_Text = 3, /* LineString geometric object with associated label */ + SXF_GT_Vector = 4, /* Vector geometric object with associated label */ + SXF_GT_TextTemplate = 5, /* Text template */ + SXF_GT_VectorAngle = 21, /* Rotated symbol */ + SXF_GT_VectorScaled = 22 /* Scaled symbol */ +}; + +enum SXFValueType +{ + SXF_VT_SHORT = 0, /* 2 byte integer */ + SXF_VT_FLOAT = 1, /* 2 byte float */ + SXF_VT_INT = 2, /* 4 byte integer*/ + SXF_VT_DOUBLE = 3 /* 8 byte float */ +}; + +typedef struct +{ + SXFGeometryType eGeomType; // Geometry type (Note 1) + SXFValueType eValType; // size of values (Note 3) + int bFormat; // Has 3D vector (Note 4) /* Format of the certificate (0- linear size, 1-vector format ) */ + GByte bDim; // Dimensionality of the idea (0- 2D, 1- 3D) (Note 6) + int bHasTextSign; // Sign of certificate with the text (Note 8) + GUInt32 nPointCount; // Point count + GUInt16 nSubObjectCount; // The sub object count + +} SXFRecordDescription; + +typedef struct{ + GUInt32 nID; /* Identifier of the beginning of record (0x7FFF7FFF) */ + GUInt32 nFullLength; /* The overall length of record (with the title) */ + GUInt32 nGeometryLength; /* Length of certificate (in bytes) */ + GUInt32 nClassifyCode; /* Classification code */ + GUInt16 anGroup[2]; /* 0 - group no, 1 - no in group */ + GByte nRef[3]; /* Reference data */ + GByte byPadding; + GUInt32 nPointCount; /* Point count */ + GUInt16 nSubObjectCount; /* The sub object count */ + GUInt16 nPointCountSmall; /* Point count in small geometries */ +} SXFRecordHeader; + +typedef struct +{ + GUInt16 nCode; //type + char nType; + char nScale; +} SXFRecordAttributeInfo; + +enum SXFRecordAttributeType +{ + SXF_RAT_ASCIIZ_DOS = 0, //text in DOS encoding + SXF_RAT_ONEBYTE = 1, //number 1 byte + SXF_RAT_TWOBYTE = 2, //number 2 byte + SXF_RAT_FOURBYTE = 4, //number 4 byte + SXF_RAT_EIGHTBYTE = 8, //float point number 8 byte + SXF_RAT_ANSI_WIN = 126, //text in Win encoding + SXF_RAT_UNICODE = 127, //text in unicode + SXF_RAT_BIGTEXT = 128 //text more than 255 chars +}; + +/************************************************************************/ +/* SXFPassport */ +/************************************************************************/ + +typedef struct{ + GUInt16 nYear, nMonth, nDay; +} SXFDate; + +struct SXFPassport +{ + GUInt32 version; + SXFDate dtCrateDate; + CPLString sMapSheet; + GUInt32 nScale; + CPLString sMapSheetName; + SXFInformationFlags informationFlags; + SXFMapDescription stMapDescription; +}; + +typedef struct +{ + char szID[4]; //the file ID should be "SXF" + GUInt32 nHeaderLength; //the Header length + GByte nFormatVersion[4]; //the format version (e.g. 4) + GUInt32 nCheckSum; //check sum +} SXFHeader; + + +/************************************************************************/ +/* RSCInfo */ +/************************************************************************/ + +/* + RSC File record +*/ +typedef struct { + GUInt32 nOffset; //RSC Section offset in bytes from the beginning of the RSC file + GUInt32 nLenght; //RSC Section record length + GUInt32 nRecordCount; //count of records in the section +} RSCSection; + +/* + RSC File header +*/ +typedef struct{ + char szID[4]; + GUInt32 nFileLength; + GUInt32 nVersion; + GUInt32 nEncoding; + GUInt32 nFileState; + GUInt32 nFileModState; + GUInt32 nLang; //1 - en, 2 - rus + GUInt32 nNextID; + GByte date[8]; + char szMapType[32]; + char szClassifyName[32]; + char szClassifyCode[8]; + GUInt32 nScale; + char nScales[4]; + RSCSection Objects; + RSCSection Semantic; + RSCSection ClassifySemantic; + RSCSection Defaults; + RSCSection Semantics; + RSCSection Layers; + RSCSection Limits; + RSCSection Parameters; + RSCSection Print; + RSCSection Palettes; + RSCSection Fonts; + RSCSection Libs; + RSCSection ImageParams; + RSCSection Tables; + GByte nFlagKeysAsCodes; + GByte nFlagPalleteMods; + GByte Reserved[30]; + GUInt32 nFontEnc; + GUInt32 nColorsInPalette; +} RSCHeader; + +#endif /* SXF_DEFS_H */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/GNUmakefile new file mode 100644 index 000000000..a8c43b06d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/GNUmakefile @@ -0,0 +1,45 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrtigerdriver.o \ + ogrtigerdatasource.o \ + ogrtigerlayer.o \ + tigerfilebase.o \ + tigercompletechain.o \ + tigeraltname.o \ + tigerfeatureids.o \ + tigerzipcodes.o \ + tigerlandmarks.o \ + tigerarealandmarks.o \ + tigerkeyfeatures.o \ + tigerpolygon.o \ + tigerpolygoncorrections.o \ + tigerentitynames.o \ + tigerpolygoneconomic.o \ + tigeridhistory.o \ + tigerpolychainlink.o \ + tigerpip.o \ + tigerspatialmetadata.o \ + tigertlidrange.o \ + tigerzerocellid.o \ + tigeroverunder.o \ + tigerzipplus4.o \ + tigerpoint.o + + + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +LIBS := $(GDAL_LIB) $(LIBS) + + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +$(O_OBJ): ogr_tiger.h + +clean: + rm -f *.o $(O_OBJ) + +tigerinfo$(EXE): tigerinfo.$(OBJ_EXT) + $(LD) $(LDFLAGS) tigerinfo.$(OBJ_EXT) $(CONFIG_LIBS) -o tigerinfo$(EXE) diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/drv_tiger.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/drv_tiger.html new file mode 100644 index 000000000..00c2adba9 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/drv_tiger.html @@ -0,0 +1,229 @@ + + +U.S. Census TIGER/Line + + + + +

    U.S. Census TIGER/Line

    + +TIGER/Line file sets are support for read access.

    + +TIGER/Line files are a digital database of geographic features, such as roads, +railroads, rivers, lakes, political boundaries, census statistical boundaries, +etc. covering the entire United States. The data base contains information +about these features such as their location in latitude and longitude, the +name, the type of feature, address ranges for most streets, the geographic +relationship to other features, and other related information. They are the +public product created from the Census Bureau's TIGER (Topologically +Integrated Geographic Encoding and Referencing) data base of geographic +information. TIGER was developed at the Census Bureau to support the mapping +and related geographic activities required by the decennial census and sample +survey programs.

    + +Note that the TIGER/Line product does not include census demographic +statistics. Those are sold by the Census Bureau in a separate format +(not directly supported by FME), but those statistics do relate back to +census blocks in TIGER/Line files.

    + +To open a TIGER/Line dataset, select the directory containing one or more +sets of data files. The regions are counties, or county equivelents. Each +county consists of a series of files with a common basename, and different +extentions. For instance, county 1 in state 26 (Michigan) consists of +the following file set in Tiger98.

    + +

    +TGR26001.RT1
    +TGR26001.RT2
    +TGR26001.RT3
    +TGR26001.RT4
    +TGR26001.RT5
    +TGR26001.RT6
    +TGR26001.RT7
    +TGR26001.RT8
    +TGR26001.RT9
    +TGR26001.RTA
    +TGR26001.RTC
    +TGR26001.RTH
    +TGR26001.RTI
    +TGR26001.RTP
    +TGR26001.RTR
    +TGR26001.RTS
    +TGR26001.RTZ
    +
    + +The TIGER/Line coordinate system is hardcoded to NAD83 lat/long degrees. +This should be appropriate for all recent years of TIGER/Line production.

    + +There is no update or creation support in the TIGER/Line driver.

    + +The reader was implemented for TIGER/Line 1998 files, but some effort has +gone into ensuring compatibility with 1992, 1995, 1997, 1999, 2000, 2002, 2003 +and 2004 TIGER/Line products as well. The 2005 products have also been +reported to work fine. It is believe that any TIGER/Line +product from the 1990's should work with the reader, with the possible loss of +some version specific information.

    + + +

    Feature Representation

    + +With a few exceptions, a feature is created for each record of a +TIGER/Line data file. Each file (ie. .RT1, .RTA) is translated to an +appropriate OGR feature type, with attribute names matching those in the +TIGER/Line product manual.

    + +The TIGER/Line RT (record type), and VERSION attributes are generally +discarded, but the MODULE attribute is added to each feature. The +MODULE attribute contains the basename (eg. TGR26001) of the county +module from which the feature originated. For some keys (such as LAND, +POLYID, and CENID) this MODULE attribute is needed to make the key unique +when the dataset (directory) consists of more than one county of data.

    + +Following is a list of feature types, and their relationship to the +TIGER/Line product.

    + +

    CompleteChain

    + +A CompleteChain is a polyline with an associated TLID (TIGER/Line ID). +The CompleteChain features are established from a type 1 record +(Complete Chain Basic Data Record), and if available it's associated +type 3 record (Complete Chain Geographic Entity Codes). As well, any +type 2 records (Complete Chain Shape Coordinates) available are used to +fill in intermediate shape points on the arc. The TLID is the primary +key, and is unique within the entire national TIGER/Line coverage.

    + +These features always have a line geometry.

    + +

    AltName

    + +These features are derived from the type 4 record (Index to Alternate +Feature Identifiers), and relate a TLID to 1 to 4 alternate feature name +numbers (the FEAT attribute) which are kept separately as FeatureIds features. +The standard reader pipeline attaches the names from the FeatureIds +features as array attributes ALT_FEDIRS{}, ALT_FEDIRP{}, ALT_FENAME{} and +ALT_FETYPE{}. The ALT_FENAME{} is a list of feature names associated with +the TLID on the AltName feature.

    + +Note that zero, one or more AltName records may exist for a given TLID, and +each AltName record can contain between one and four alternate names. +Because it is still very difficult to utilize AltName features to relate +altername names to CompleteChains, it is anticipated that the standard +reader pipeline for TIGER/Line files will be upgraded in the future, resulting +in a simplification of alternate names.

    + +These features have no associated geometry.

    + +

    FeatureIds

    + +These features are derived from type 5 (Complete Chain Feature Identifiers) +records. Each feature contains a feature name (FENAME), and it's associated +feature id code (FEAT). The FEAT attribute is the primary key, and is +unique within the county module. FeatureIds have a one to many relationship +with AltName features, and KeyFeatures features.

    + +These features have no associated geometry.

    + +

    ZipCodes

    + +These features are derived from type 6 (Additional Address Range and +ZIP Code Data) records. These features are intended to augment the +ZIP Code information kept directly on CompleteChain features, and there +is a many to one relationship between ZipCodes features and CompleteChain +features.

    + +These features have no associated geometry.

    + +

    Landmarks

    + +These features are derived from type 7 (Landmark Features) records. They +relate to point, or area landmarks. For area landmarks there is a one to +one relationship with an AreaLandmark record. The LAND attribute is +the primary key, and is unique within the county module.

    + +These features may have an associated point geometry. Landmarks associated +with polygons will not have the polygon geometry attached. It would need +to be collected (via the AreaLandmark feature) from a Polygon feature.

    + +

    AreaLandmarks

    + +These features are derived from type 8 (Polygons Linked to Area Landmarks) +records. Each associates a Landmark feature (attribute LAND) with a +Polygon feature (attribute POLYID). This feature has a many to many +relationship with Polygon features.

    + +These features have no associated geometry.

    + +

    KeyFeatures

    + +These features are derived from type 9 (Polygon Geographic Entity Codes) +records. They may be associated with a FeatureIds feature (via the FEAT +attribute), and a Polygon feature (via the POLYID attribute).

    + +These features have no associated geometry.

    + +

    Polygon

    + +These features are derived from type A (Polygon Geographic Entity Codes) +records and if available the related type S (Polygon Additional Geographic +Entity Codes) records. The POLYID attribute is the primary key, uniquely +identifying a polygon within a county module.

    + +These features do not have any geometry associated with them as read by +the OGR TIGER driver. It needs to be externally related using the +PolyChainLink. The gdal/pymod/samples/tigerpoly.py script may be used +to read a TIGER dataset and extract the polygon layer with geometry +as a shapefile. + +

    EntityNames

    + +These features are derived from type C (Geographic Entity Names) records.

    + +These features have no associated geometry.

    + +

    IDHistory

    + +These features are derived from type H (TIGER/Line ID History) records. +They can be used to trace the splitting, merging, creation and deletion +of CompleteChain features.

    + +These features have no associated geometry.

    + +

    PolyChainLink

    + +These features are derived from type I (Link Between Complete Chains and +Polygons) records. They are normally all consumed by the standard reader +pipeline while attaching CompleteChain geometries to Polygon features to +establish their polygon geometries. PolyChainLink features have a many to +one relationship with Polygon features, and a one to one relationship with +CompleteChain features.

    + +These features have no associated geometry.

    + +

    PIP

    + +These features are derived from type P (Polygon Internal Point) records. +They relate to a Polygon feature via the POLYID attribute, and can be used +to establish an internal point for Polygon features.

    + +These features have a point geometry.

    + +

    ZipPlus4

    + +These features are derived from type Z (ZIP+4 Codes) records. ZipPlus4 +features have a many to one relationship with CompleteChain features. +

    + +These features have no associated geometry. +

    + +

    See Also

    + +
      + +
    • http://www.census.gov/geo/www/tiger/: More information on the TIGER/Line file format, and data +product can be found on this U.S. Census web page. + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/makefile.vc new file mode 100644 index 000000000..903f6b32d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/makefile.vc @@ -0,0 +1,24 @@ + +OBJ = ogrtigerdriver.obj ogrtigerdatasource.obj ogrtigerlayer.obj \ + tigercompletechain.obj tigerfilebase.obj \ + tigeraltname.obj tigerfeatureids.obj tigerzipcodes.obj \ + tigerlandmarks.obj tigerarealandmarks.obj tigerkeyfeatures.obj\ + tigerpolychainlink.obj tigerpolygon.obj tigerpip.obj \ + tigerentitynames.obj tigeridhistory.obj tigertlidrange.obj \ + tigerzipplus4.obj tigerpolygoncorrections.obj \ + tigerzerocellid.obj tigeroverunder.obj tigerpoint.obj \ + tigerpolygoneconomic.obj tigerspatialmetadata.obj + +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/ogr_tiger.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/ogr_tiger.h new file mode 100644 index 000000000..a95ec09c6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/ogr_tiger.h @@ -0,0 +1,605 @@ +/*-*-C++-*-*/ +/****************************************************************************** + * $Id: ogr_tiger.h 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: TIGER/Line Translator + * Purpose: Main declarations for Tiger translator. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_TIGER_H_INCLUDED +#define _OGR_TIGER_H_INCLUDED + +#include "cpl_conv.h" +#include "ogrsf_frmts.h" + +class OGRTigerDataSource; + +/* +** TIGER Versions +** +** 0000 TIGER/Line Precensus Files, 1990 +** 0002 TIGER/Line Initial Voting District Codes Files, 1990 +** 0003 TIGER/Line Files, 1990 +** 0005 TIGER/Line Files, 1992 +** 0021 TIGER/Line Files, 1994 +** 0024 TIGER/Line Files, 1995 +** 0697 to 1098 TIGER/Line Files, 1997 +** 1298 to 0499 TIGER/Line Files, 1998 +** 0600 to 0800 TIGER/Line Files, 1999 +** 1000 to 1100 TIGER/Line Files, Redistricting Census 2000 +** 0301 to 0801 TIGER/Line Files, Census 2000 +** +** 0302 to 0502 TIGER/Line Files, UA 2000 +** ???? ???? +** +** 0602 & higher TIGER/Line Files, 2002 +** ???? ???? +*/ + +typedef enum { + TIGER_1990_Precensus = 0, + TIGER_1990 = 1, + TIGER_1992 = 2, + TIGER_1994 = 3, + TIGER_1995 = 4, + TIGER_1997 = 5, + TIGER_1998 = 6, + TIGER_1999 = 7, + TIGER_2000_Redistricting = 8, + TIGER_2000_Census = 9, + TIGER_UA2000 = 10, + TIGER_2002 = 11, + TIGER_2003 = 12, + TIGER_2004 = 13, + TIGER_Unknown +} TigerVersion; + +TigerVersion TigerClassifyVersion( int ); +const char * TigerVersionString( TigerVersion ); + +/*****************************************************************************/ +/* The TigerFieldInfo and TigerRecordInfo structures hold information about */ +/* the schema of a TIGER record type. In each layer implementation file */ +/* there are statically initialized variables of these types that describe */ +/* the record types associated with that layer. In the case where different */ +/* TIGER versions have different schemas, there is a */ +/* TigerFieldInfo/TigerRecordInfo for each version, and the constructor */ +/* for the layer chooses a pointer to the correct set based on the version. */ +/*****************************************************************************/ + +typedef struct TigerFieldInfo { + char pszFieldName[11]; // name of the field + char cFmt; // format of the field ('L' or 'R') + char cType; // type of the field ('A' or 'N') + char OGRtype; // OFTType of the field (OFTInteger, OFTString, ...?) + unsigned char nBeg; // beginning column number for field + unsigned char nEnd; // ending column number for field + unsigned char nLen; // length of field + + int bDefine:1; // whether to add this field to the FeatureDefn + int bSet:1; // whether to set this field in GetFeature() + int bWrite:1; // whether to write this field in CreateFeature() +} TigerFieldInfo; + +typedef struct TigerRecordInfo { + const TigerFieldInfo *pasFields; + unsigned char nFieldCount; + unsigned char nRecordLength; +} TigerRecordInfo; + +// OGR_TIGER_RECBUF_LEN should be a number that is larger than the +// longest possible record length for any record type; it's used to +// create arrays to hold the records. At the time of this writing the +// longest record (RT1) has length 228, but I'm choosing 500 because +// it's a good round number and will allow for growth without having +// to modify this file. The code never holds more than a few records +// in memory at a time, so having OGR_TIGER_RECBUF_LEN be much larger +// than is really necessary won't affect the amount of memory required +// in a substantial way. +// mbp Fri Dec 20 19:19:59 2002 +#define OGR_TIGER_RECBUF_LEN 500 + +/************************************************************************/ +/* TigerFileBase */ +/************************************************************************/ + +class TigerFileBase +{ +protected: + OGRTigerDataSource *poDS; + + char *pszModule; + char *pszShortModule; + VSILFILE *fpPrimary; + + OGRFeatureDefn *poFeatureDefn; + + int nFeatures; + int nRecordLength; + + int OpenFile( const char *, const char * ); + void EstablishFeatureCount(); + + static int EstablishRecordLength( VSILFILE * ); + + void SetupVersion(); + + int nVersionCode; + TigerVersion nVersion; + +public: + TigerFileBase( const TigerRecordInfo *psRTInfoIn = NULL, + const char *m_pszFileCodeIn = NULL ); + virtual ~TigerFileBase(); + + TigerVersion GetVersion() { return nVersion; } + int GetVersionCode() { return nVersionCode; } + + virtual const char *GetShortModule() { return pszShortModule; } + virtual const char *GetModule() { return pszModule; } + virtual int SetWriteModule( const char *, int, OGRFeature * ); + + virtual int GetFeatureCount() { return nFeatures; } + + OGRFeatureDefn *GetFeatureDefn() { return poFeatureDefn; } + + static const char* GetField( const char *, int, int ); + static void SetField( OGRFeature *, const char *, const char *, + int, int ); + + int WriteField( OGRFeature *, const char *, char *, + int, int, char, char ); + int WriteRecord( char *pachRecord, int nRecLen, + const char *pszType, VSILFILE *fp = NULL ); + int WritePoint( char *pachRecord, int nStart, + double dfX, double dfY ); + + virtual int SetModule( const char * pszModule ); + virtual OGRFeature *GetFeature( int nRecordId ); + virtual OGRErr CreateFeature( OGRFeature *poFeature ); + + protected: + void WriteFields(const TigerRecordInfo *psRTInfo, + OGRFeature *poFeature, + char *szRecord); + + void AddFieldDefns(const TigerRecordInfo *psRTInfo, + OGRFeatureDefn *poFeatureDefn); + + + void SetFields(const TigerRecordInfo *psRTInfo, + OGRFeature *poFeature, + char *achRecord); + + + const TigerRecordInfo *psRTInfo; + const char *m_pszFileCode; +}; + +/************************************************************************/ +/* TigerCompleteChain */ +/************************************************************************/ + +class TigerCompleteChain : public TigerFileBase +{ + VSILFILE *fpShape; + int *panShapeRecordId; + + VSILFILE *fpRT3; + int bUsingRT3; + int nRT1RecOffset; + + int GetShapeRecordId( int, int ); + int AddShapePoints( int, int, OGRLineString *, int ); + + void AddFieldDefnsPre2002(); + OGRFeature *GetFeaturePre2002( int ); + OGRErr WriteRecordsPre2002( OGRFeature *, OGRLineString * ); + + OGRErr WriteRecords2002( OGRFeature *, OGRLineString * ); + OGRFeature *GetFeature2002( int ); + void AddFieldDefns2002(); + + const TigerRecordInfo *psRT1Info; + const TigerRecordInfo *psRT2Info; + const TigerRecordInfo *psRT3Info; + +public: + TigerCompleteChain( OGRTigerDataSource *, + const char * ); + virtual ~TigerCompleteChain(); + + virtual int SetModule( const char * ); + + virtual OGRFeature *GetFeature( int ); + + virtual OGRErr CreateFeature( OGRFeature *poFeature ); + + virtual int SetWriteModule( const char *, int, OGRFeature * ); +}; + +/************************************************************************/ +/* TigerAltName (Type 4 records) */ +/************************************************************************/ + +class TigerAltName : public TigerFileBase +{ + public: + TigerAltName( OGRTigerDataSource *, + const char * ); + + virtual OGRFeature *GetFeature( int ); + + virtual OGRErr CreateFeature( OGRFeature *poFeature ); +}; + +/************************************************************************/ +/* TigerFeatureIds (Type 5 records) */ +/************************************************************************/ + +class TigerFeatureIds : public TigerFileBase +{ + public: + TigerFeatureIds( OGRTigerDataSource *, + const char * ); +}; + +/************************************************************************/ +/* TigerZipCodes (Type 6 records) */ +/************************************************************************/ + +class TigerZipCodes : public TigerFileBase +{ +public: + TigerZipCodes( OGRTigerDataSource *, const char * ); +}; + +/************************************************************************/ +/* TigerPoint */ +/* This is an abstract base class for TIGER layers with point geometry. */ +/* Since much of the implementation of these layers is similar, I've */ +/* put it into this base class to avoid duplication in the actual */ +/* layer classes. mbp Sat Jan 4 16:41:19 2003. */ +/************************************************************************/ + +class TigerPoint : public TigerFileBase +{ + protected: + TigerPoint(int bRequireGeom, + const TigerRecordInfo *psRTInfoIn = NULL, + const char *m_pszFileCodeIn = NULL); + + // The boolean bRequireGeom indicates whether + // the layer requires each feature to actual + // have a geom. It's used in CreateFeature() to + // decide whether to report an error when a + // missing geom is detected. + + private: + int bRequireGeom; + + public: + virtual OGRFeature *GetFeature( int nFID) { return TigerFileBase::GetFeature(nFID); } /* to avoid -Woverloaded-virtual warnings */ + OGRFeature *GetFeature( int nRecordId, + int nX0, int nX1, + int nY0, int nY1 ); + + + virtual OGRErr CreateFeature( OGRFeature *poFeature) { return TigerFileBase::CreateFeature(poFeature); } /* to avoid -Woverloaded-virtual warnings */ + OGRErr CreateFeature( OGRFeature *poFeature, + int nIndex ); + +}; + +/************************************************************************/ +/* TigerLandmarks (Type 7 records) */ +/************************************************************************/ + +class TigerLandmarks : public TigerPoint +{ + public: + TigerLandmarks( OGRTigerDataSource *, const char * ); + + virtual OGRFeature *GetFeature( int ); + + virtual OGRErr CreateFeature( OGRFeature *poFeature ); +}; + +/************************************************************************/ +/* TigerAreaLandmarks (Type 8 records) */ +/************************************************************************/ + +class TigerAreaLandmarks : public TigerFileBase +{ +public: + TigerAreaLandmarks( OGRTigerDataSource *, const char * ); +}; + +/************************************************************************/ +/* TigerKeyFeatures (Type 9 records) */ +/************************************************************************/ + +class TigerKeyFeatures : public TigerFileBase +{ +public: + TigerKeyFeatures( OGRTigerDataSource *, const char * ); +}; + +/************************************************************************/ +/* TigerPolygon (Type A&S records) */ +/************************************************************************/ + +class TigerPolygon : public TigerFileBase +{ + private: + const TigerRecordInfo *psRTAInfo; + const TigerRecordInfo *psRTSInfo; + + VSILFILE *fpRTS; + int bUsingRTS; + int nRTSRecLen; + +public: + TigerPolygon( OGRTigerDataSource *, const char * ); + virtual ~TigerPolygon(); + + virtual int SetModule( const char * ); + + virtual OGRFeature *GetFeature( int ); + + virtual int SetWriteModule( const char *, int, OGRFeature * ); + virtual OGRErr CreateFeature( OGRFeature *poFeature ); +}; + +/************************************************************************/ +/* TigerPolygonCorrections (Type B records) */ +/************************************************************************/ + +class TigerPolygonCorrections : public TigerFileBase +{ +public: + TigerPolygonCorrections( OGRTigerDataSource *, const char * ); +}; + +/************************************************************************/ +/* TigerEntityNames (Type C records) */ +/************************************************************************/ + +class TigerEntityNames : public TigerFileBase +{ +public: + TigerEntityNames( OGRTigerDataSource *, const char * ); +}; + +/************************************************************************/ +/* TigerPolygonEconomic (Type E records) */ +/************************************************************************/ + +class TigerPolygonEconomic : public TigerFileBase +{ +public: + TigerPolygonEconomic( OGRTigerDataSource *, const char * ); + +}; + +/************************************************************************/ +/* TigerIDHistory (Type H records) */ +/************************************************************************/ + +class TigerIDHistory : public TigerFileBase +{ +public: + TigerIDHistory( OGRTigerDataSource *, const char * ); +}; + +/************************************************************************/ +/* TigerPolyChainLink (Type I records) */ +/************************************************************************/ + +class TigerPolyChainLink : public TigerFileBase +{ +public: + TigerPolyChainLink( OGRTigerDataSource *, const char * ); +}; + +/************************************************************************/ +/* TigerSpatialMetadata (Type M records) */ +/************************************************************************/ + +class TigerSpatialMetadata : public TigerFileBase +{ +public: + TigerSpatialMetadata( OGRTigerDataSource *, const char * ); +}; + +/************************************************************************/ +/* TigerPIP (Type P records) */ +/************************************************************************/ + +class TigerPIP : public TigerPoint +{ +public: + TigerPIP( OGRTigerDataSource *, const char * ); + + virtual OGRFeature *GetFeature( int ); + + virtual OGRErr CreateFeature( OGRFeature *poFeature ); +}; + +/************************************************************************/ +/* TigerTLIDRange (Type R records) */ +/************************************************************************/ + +class TigerTLIDRange : public TigerFileBase +{ +public: + TigerTLIDRange( OGRTigerDataSource *, const char * ); +}; + +/************************************************************************/ +/* TigerZeroCellID (Type T records) */ +/************************************************************************/ + +class TigerZeroCellID : public TigerFileBase +{ +public: + TigerZeroCellID( OGRTigerDataSource *, const char * ); +}; + +/************************************************************************/ +/* TigerOverUnder (Type U records) */ +/************************************************************************/ + +class TigerOverUnder : public TigerPoint +{ +public: + TigerOverUnder( OGRTigerDataSource *, const char * ); + + virtual OGRFeature *GetFeature( int ); + + virtual OGRErr CreateFeature( OGRFeature *poFeature ); +}; + +/************************************************************************/ +/* TigerZipPlus4 (Type Z records) */ +/************************************************************************/ + +class TigerZipPlus4 : public TigerFileBase +{ + public: + TigerZipPlus4( OGRTigerDataSource *, const char * ); +}; + +/************************************************************************/ +/* OGRTigerLayer */ +/************************************************************************/ + +class OGRTigerLayer : public OGRLayer +{ + TigerFileBase *poReader; + + OGRTigerDataSource *poDS; + + int nFeatureCount; + int *panModuleFCount; + int *panModuleOffset; + + int iLastFeatureId; + int iLastModule; + + public: + OGRTigerLayer( OGRTigerDataSource * poDS, + TigerFileBase * ); + virtual ~OGRTigerLayer(); + + void ResetReading(); + OGRFeature * GetNextFeature(); + OGRFeature *GetFeature( GIntBig nFeatureId ); + + OGRFeatureDefn * GetLayerDefn(); + + GIntBig GetFeatureCount( int ); + + int TestCapability( const char * ); + + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); +}; + +/************************************************************************/ +/* OGRTigerDataSource */ +/************************************************************************/ + +class OGRTigerDataSource : public OGRDataSource +{ + char *pszName; + + int nLayers; + OGRTigerLayer **papoLayers; + + OGRSpatialReference *poSpatialRef; + + char **papszOptions; + + char *pszPath; + + int nModules; + char **papszModules; + + int nVersionCode; + TigerVersion nVersion; + + int bWriteMode; + + TigerVersion TigerCheckVersion( TigerVersion, const char * ); + + public: + OGRTigerDataSource(); + ~OGRTigerDataSource(); + + int GetWriteMode() { return bWriteMode; } + + TigerVersion GetVersion() { return nVersion; } + int GetVersionCode() { return nVersionCode; } + + void SetOptionList( char ** ); + const char *GetOption( const char * ); + + int Open( const char * pszName, int bTestOpen = FALSE, + char ** papszFileList = NULL ); + + int Create( const char *pszName, char **papszOptions ); + + const char *GetName() { return pszName; } + int GetLayerCount(); + OGRLayer *GetLayer( int ); + OGRLayer *GetLayer( const char *pszLayerName ); + + void AddLayer( OGRTigerLayer * ); + int TestCapability( const char * ); + + OGRSpatialReference *GetSpatialRef() { return poSpatialRef; } + + const char *GetDirPath() { return pszPath; } + char *BuildFilename( const char * pszModule, + const char * pszExtension ); + + + int GetModuleCount() { return nModules; } + const char *GetModule( int ); + int CheckModule( const char *pszModule ); + void AddModule( const char *pszModule ); + + void DeleteModuleFiles( const char *pszModule ); + + virtual OGRLayer *ICreateLayer( const char *, + OGRSpatialReference * = NULL, + OGRwkbGeometryType = wkbUnknown, + char ** = NULL ); +}; + +#endif /* ndef _OGR_TIGER_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/ogrtigerdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/ogrtigerdatasource.cpp new file mode 100644 index 000000000..ae5caf699 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/ogrtigerdatasource.cpp @@ -0,0 +1,982 @@ +/****************************************************************************** + * $Id: ogrtigerdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements OGRTigerDataSource class + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: ogrtigerdatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* TigerClassifyVersion() */ +/************************************************************************/ + +TigerVersion TigerClassifyVersion( int nVersionCode ) + +{ + TigerVersion nVersion; + int nYear, nMonth; + +/* +** TIGER Versions +** +** 0000 TIGER/Line Precensus Files, 1990 +** 0002 TIGER/Line Initial Voting District Codes Files, 1990 +** 0003 TIGER/Line Files, 1990 +** 0005 TIGER/Line Files, 1992 +** 0021 TIGER/Line Files, 1994 +** 0024 TIGER/Line Files, 1995 +** 9706 to 9810 TIGER/Line Files, 1997 +** 9812 to 9904 TIGER/Line Files, 1998 +** 0006 to 0008 TIGER/Line Files, 1999 +** 0010 to 0011 TIGER/Line Files, Redistricting Census 2000 +** 0103 to 0108 TIGER/Line Files, Census 2000 +** +** 0203 to 0205 TIGER/Line Files, UA 2000 +** ???? ???? +** +** 0206 to 0299 TIGER/Line Files, 2002 +** 0300 to 0399 TIGER/Line Files, 2003 +** 0400+ TIGER/Line Files, 2004 - one sample is 0405 +** ???? +*/ + + nVersion = TIGER_Unknown; + if( nVersionCode == 0 ) + nVersion = TIGER_1990_Precensus; + else if( nVersionCode == 2 ) + nVersion = TIGER_1990; + else if( nVersionCode == 3 ) + nVersion = TIGER_1992; + else if( nVersionCode == 5 ) + nVersion = TIGER_1994; + else if( nVersionCode == 21 ) + nVersion = TIGER_1994; + else if( nVersionCode == 24 ) + nVersion = TIGER_1995; + + else if( nVersionCode == 9999 ) /* special hack, fme bug 7625 */ + nVersion = TIGER_UA2000; + + nYear = nVersionCode % 100; + nMonth = nVersionCode / 100; + + nVersionCode = nYear * 100 + nMonth; + + if( nVersion != TIGER_Unknown ) + /* do nothing */; + else if( nVersionCode >= 9706 && nVersionCode <= 9810 ) + nVersion = TIGER_1997; + else if( nVersionCode >= 9812 && nVersionCode <= 9904 ) + nVersion = TIGER_1998; + else if( nVersionCode >= 6 /*0006*/ && nVersionCode <= 8 /*0008*/ ) + nVersion = TIGER_1999; + else if( nVersionCode >= 10 /*0010*/ && nVersionCode <= 11 /*0011*/ ) + nVersion = TIGER_2000_Redistricting; + else if( nVersionCode >= 103 /*0103*/ && nVersionCode <= 108 /*0108*/ ) + nVersion = TIGER_2000_Census; + else if( nVersionCode >= 203 /*0302*/ && nVersionCode <= 205 /*0502*/ ) + nVersion = TIGER_UA2000; + else if( nVersionCode >= 210 /*1002*/ && nVersionCode <= 306 /*0603*/) + nVersion = TIGER_2002; + else if( nVersionCode >= 312 /*1203*/ && nVersionCode <= 403 /*0304*/) + nVersion = TIGER_2003; + else if( nVersionCode >= 404 ) + nVersion = TIGER_2004; + + return nVersion; +} + +/************************************************************************/ +/* TigerVersionString() */ +/************************************************************************/ + +const char * TigerVersionString( TigerVersion nVersion ) +{ + + if (nVersion == TIGER_1990_Precensus) { return "TIGER_1990_Precensus"; } + if (nVersion == TIGER_1990) { return "TIGER_1990"; } + if (nVersion == TIGER_1992) { return "TIGER_1992"; } + if (nVersion == TIGER_1994) { return "TIGER_1994"; } + if (nVersion == TIGER_1995) { return "TIGER_1995"; } + if (nVersion == TIGER_1997) { return "TIGER_1997"; } + if (nVersion == TIGER_1998) { return "TIGER_1998"; } + if (nVersion == TIGER_1999) { return "TIGER_1999"; } + if (nVersion == TIGER_2000_Redistricting) { return "TIGER_2000_Redistricting"; } + if (nVersion == TIGER_UA2000) { return "TIGER_UA2000"; } + if (nVersion == TIGER_2002) { return "TIGER_2002"; } + if (nVersion == TIGER_2003) { return "TIGER_2003"; } + if (nVersion == TIGER_2004) { return "TIGER_2004"; } + if (nVersion == TIGER_Unknown) { return "TIGER_Unknown"; } + return "???"; +} + +/************************************************************************/ +/* TigerCheckVersion() */ +/* */ +/* Some tiger products seem to be generated with version info */ +/* that doesn't match the tiger specs. We can sometimes */ +/* recognise the wrongness by checking the record length of */ +/* some well known changing files and adjusting the version */ +/* based on this. */ +/************************************************************************/ + +TigerVersion OGRTigerDataSource::TigerCheckVersion( TigerVersion nOldVersion, + const char *pszFilename ) + +{ + if( nOldVersion != TIGER_2002 ) + return nOldVersion; + + char *pszRTCFilename = BuildFilename( pszFilename, "C" ); + VSILFILE *fp = VSIFOpenL( pszRTCFilename, "rb" ); + CPLFree( pszRTCFilename ); + + if( fp == NULL ) + return nOldVersion; + + char szHeader[115]; + + if( VSIFReadL( szHeader, sizeof(szHeader)-1, 1, fp ) < 1 ) + { + VSIFCloseL( fp ); + return nOldVersion; + } + + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Is the record length 112? If so, it is an older version */ +/* than 2002. */ +/* -------------------------------------------------------------------- */ + if( szHeader[112] == 10 || szHeader[112] == 13 ) + { + CPLDebug( "TIGER", "Forcing version back to UA2000 since RTC records are short." ); + return TIGER_UA2000; + } + else + return nOldVersion; +} + +/************************************************************************/ +/* OGRTigerDataSource() */ +/************************************************************************/ + +OGRTigerDataSource::OGRTigerDataSource() + +{ + bWriteMode = FALSE; + + nLayers = 0; + papoLayers = NULL; + + nModules = 0; + papszModules = NULL; + + pszName = NULL; + pszPath = NULL; + + papszOptions = NULL; + + poSpatialRef = new OGRSpatialReference( "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\",6378137,298.257222101]],PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]]" ); +} + +/************************************************************************/ +/* ~OGRTigerDataSource() */ +/************************************************************************/ + +OGRTigerDataSource::~OGRTigerDataSource() + +{ + int i; + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + + CPLFree( pszName ); + CPLFree( pszPath ); + + CSLDestroy( papszOptions ); + + CSLDestroy( papszModules ); + + delete poSpatialRef; +} + +/************************************************************************/ +/* AddLayer() */ +/************************************************************************/ + +void OGRTigerDataSource::AddLayer( OGRTigerLayer * poNewLayer ) + +{ + poNewLayer->SetDescription( poNewLayer->GetName() ); + papoLayers = (OGRTigerLayer **) + CPLRealloc( papoLayers, sizeof(void*) * ++nLayers ); + + papoLayers[nLayers-1] = poNewLayer; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRTigerDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRTigerDataSource::GetLayer( const char *pszLayerName ) + +{ + for( int iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(papoLayers[iLayer]->GetLayerDefn()->GetName(),pszLayerName) ) + return papoLayers[iLayer]; + } + + return NULL; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int OGRTigerDataSource::GetLayerCount() + +{ + return nLayers; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRTigerDataSource::Open( const char * pszFilename, int bTestOpen, + char ** papszLimitedFileList ) + +{ + VSIStatBufL stat; + char **papszFileList = NULL; + int i; + + pszName = CPLStrdup( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Is the given path a directory or a regular file? */ +/* -------------------------------------------------------------------- */ + if( VSIStatExL( pszFilename, &stat, VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) != 0 + || (!VSI_ISDIR(stat.st_mode) && !VSI_ISREG(stat.st_mode)) ) + { + if( !bTestOpen ) + CPLError( CE_Failure, CPLE_AppDefined, + "%s is neither a file or directory, Tiger access failed.\n", + pszFilename ); + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Build a list of filenames we figure are Tiger files. */ +/* -------------------------------------------------------------------- */ + if( VSI_ISREG(stat.st_mode) ) + { + char szModule[128]; + + if( strlen(CPLGetFilename(pszFilename)) == 0 ) + { + return FALSE; + } + + pszPath = CPLStrdup( CPLGetPath(pszFilename) ); + + strncpy( szModule, CPLGetFilename(pszFilename), sizeof(szModule)-1 ); + /* Make sure the buffer is 0 terminated */ + szModule[sizeof(szModule)-1] = '\0'; + + /* And now remove last character of filename */ + szModule[strlen(szModule)-1] = '\0'; + + papszFileList = CSLAddString( papszFileList, szModule ); + } + else + { + char **candidateFileList = CPLReadDir( pszFilename ); + int i; + + pszPath = CPLStrdup( pszFilename ); + + for( i = 0; + candidateFileList != NULL && candidateFileList[i] != NULL; + i++ ) + { + int nCandidateLen = strlen(candidateFileList[i]); + + if( papszLimitedFileList != NULL + && CSLFindString(papszLimitedFileList, + CPLGetBasename(candidateFileList[i])) == -1 ) + { + continue; + } + + if( nCandidateLen > 4 + && candidateFileList[i][nCandidateLen-4] == '.' + && candidateFileList[i][nCandidateLen-1] == '1') + { + char szModule[128]; + + strncpy( szModule, candidateFileList[i], + strlen(candidateFileList[i])-1 ); + + szModule[strlen(candidateFileList[i])-1] = '\0'; + + papszFileList = CSLAddString(papszFileList, szModule); + } + } + + CSLDestroy( candidateFileList ); + + if( CSLCount(papszFileList) == 0 ) + { + if( !bTestOpen ) + CPLError( CE_Failure, CPLE_OpenFailed, + "No candidate Tiger files (TGR*.RT1) found in\n" + "directory: %s", + pszFilename ); + + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Loop over all these files trying to open them. In testopen */ +/* mode we first read the first 80 characters, to verify that */ +/* it looks like an Tiger file. Note that we don't keep the file */ +/* open ... we don't want to occupy alot of file handles when */ +/* handling a whole directory. */ +/* -------------------------------------------------------------------- */ + papszModules = NULL; + + for( i = 0; papszFileList[i] != NULL; i++ ) + { + if( bTestOpen || i == 0 ) + { + char szHeader[500]; + VSILFILE *fp; + char *pszRecStart = NULL; + int bIsGDT = FALSE; + char *pszFilename; + + pszFilename = BuildFilename( papszFileList[i], "1" ); + + fp = VSIFOpenL( pszFilename, "rb" ); + CPLFree( pszFilename ); + + if( fp == NULL ) + continue; + + if( VSIFReadL( szHeader, sizeof(szHeader)-1, 1, fp ) < 1 ) + { + VSIFCloseL( fp ); + continue; + } + + VSIFCloseL( fp ); + + pszRecStart = szHeader; + szHeader[sizeof(szHeader)-1] = '\0'; + + if( EQUALN(pszRecStart,"Copyright (C)",13) + && strstr(pszRecStart,"Geographic Data Tech") != NULL ) + { + bIsGDT = TRUE; + + while( *pszRecStart != '\0' + && *pszRecStart != 10 + && *pszRecStart != 13 ) + pszRecStart++; + + while( *pszRecStart == 10 || *pszRecStart == 13 ) + pszRecStart++; + } + + if( pszRecStart[0] != '1' ) + continue; + + if( !isdigit(pszRecStart[1]) || !isdigit(pszRecStart[2]) + || !isdigit(pszRecStart[3]) || !isdigit(pszRecStart[4]) ) + continue; + + nVersionCode = atoi(TigerFileBase::GetField( pszRecStart, 2, 5 )); + nVersion = TigerClassifyVersion( nVersionCode ); + nVersion = TigerCheckVersion( nVersion, papszFileList[i] ); + + CPLDebug( "OGR", "Tiger Version Code=%d, Classified as %s ", + nVersionCode, TigerVersionString(nVersion) ); + + if( nVersionCode != 0 + && nVersionCode != 2 + && nVersionCode != 3 + && nVersionCode != 5 + && nVersionCode != 21 + && nVersionCode != 24 + && pszRecStart[3] != '9' + && pszRecStart[3] != '0' + && !bIsGDT ) + continue; + + // we could (and should) add a bunch more validation here. + } + + papszModules = CSLAddString( papszModules, papszFileList[i] ); + } + + CSLDestroy( papszFileList ); + + nModules = CSLCount( papszModules ); + + if( nModules == 0 ) + { + if( !bTestOpen ) + { + if( VSI_ISREG(stat.st_mode) ) + CPLError( CE_Failure, CPLE_OpenFailed, + "No TIGER/Line files (TGR*.RT1) found in\n" + "directory: %s", + pszFilename ); + else + CPLError( CE_Failure, CPLE_OpenFailed, + "File %s does not appear to be a TIGER/Line .RT1 file.", + pszFilename ); + } + + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Do we have a user provided version override? */ +/* -------------------------------------------------------------------- */ + if( CPLGetConfigOption( "TIGER_VERSION", NULL ) != NULL ) + { + const char *pszRequestedVersion = + CPLGetConfigOption( "TIGER_VERSION", NULL ); + + if( EQUALN(pszRequestedVersion,"TIGER_",6) ) + { + int iCode; + + for( iCode = 1; iCode < TIGER_Unknown; iCode++ ) + { + if( EQUAL(TigerVersionString((TigerVersion)iCode), + pszRequestedVersion) ) + { + nVersion = (TigerVersion) iCode; + break; + } + } + + if( iCode == TIGER_Unknown ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to recognise TIGER_VERSION setting: %s", + pszRequestedVersion ); + return FALSE; + } + + CPLDebug( "OGR", "OVERRIDE Tiger Version %s ", + TigerVersionString(nVersion) ); + } + else + { + nVersionCode = atoi(pszRequestedVersion); + nVersion = TigerClassifyVersion( nVersionCode ); + + CPLDebug( "OGR", + "OVERRIDE Tiger Version Code=%d, Classified as %s ", + nVersionCode, TigerVersionString(nVersion) ); + } + } + +/* -------------------------------------------------------------------- */ +/* Create the layers which appear to exist. */ +/* -------------------------------------------------------------------- */ + // RT1, RT2, RT3 + AddLayer( new OGRTigerLayer( this, + new TigerCompleteChain( this, + papszModules[0]) )); + + /* should we have kept track of whether we encountered an RT4 file? */ + // RT4 + AddLayer( new OGRTigerLayer( this, + new TigerAltName( this, + papszModules[0]) )); + + // RT5 + AddLayer( new OGRTigerLayer( this, + new TigerFeatureIds( this, + papszModules[0]) )); + + // RT6 + AddLayer( new OGRTigerLayer( this, + new TigerZipCodes( this, + papszModules[0]) )); + // RT7 + AddLayer( new OGRTigerLayer( this, + new TigerLandmarks( this, + papszModules[0]) )); + + // RT8 + AddLayer( new OGRTigerLayer( this, + new TigerAreaLandmarks( this, + papszModules[0]) )); + + // RT9 + if (nVersion < TIGER_2002) { + AddLayer( new OGRTigerLayer( this, + new TigerKeyFeatures( this, + papszModules[0]) )); + } + + // RTA, RTS + AddLayer( new OGRTigerLayer( this, + new TigerPolygon( this, + papszModules[0]) )); + + // RTB + if (nVersion >= TIGER_2002) { + AddLayer( new OGRTigerLayer( this, + new TigerPolygonCorrections( this, + papszModules[0]) )); + } + + // RTC + AddLayer( new OGRTigerLayer( this, + new TigerEntityNames( this, + papszModules[0]) )); + + // RTE + if (nVersion >= TIGER_2002) { + AddLayer( new OGRTigerLayer( this, + new TigerPolygonEconomic( this, + papszModules[0]) )); + } + + // RTH + AddLayer( new OGRTigerLayer( this, + new TigerIDHistory( this, + papszModules[0]) )); + + // RTI + AddLayer( new OGRTigerLayer( this, + new TigerPolyChainLink( this, + papszModules[0]) )); + + // RTM + AddLayer( new OGRTigerLayer( this, + new TigerSpatialMetadata( this, + papszModules[0] ) ) ); + + // RTP + AddLayer( new OGRTigerLayer( this, + new TigerPIP( this, + papszModules[0]) )); + + // RTR + AddLayer( new OGRTigerLayer( this, + new TigerTLIDRange( this, + papszModules[0]) )); + + // RTT + if (nVersion >= TIGER_2002) { + AddLayer( new OGRTigerLayer( this, + new TigerZeroCellID( this, + papszModules[0]) )); + } + + // RTU + if (nVersion >= TIGER_2002) { + AddLayer( new OGRTigerLayer( this, + new TigerOverUnder( this, + papszModules[0]) )); + } + + // RTZ + AddLayer( new OGRTigerLayer( this, + new TigerZipPlus4( this, + papszModules[0]) )); + + return TRUE; +} + +/************************************************************************/ +/* SetOptions() */ +/************************************************************************/ + +void OGRTigerDataSource::SetOptionList( char ** papszNewOptions ) + +{ + CSLDestroy( papszOptions ); + papszOptions = CSLDuplicate( papszNewOptions ); +} + +/************************************************************************/ +/* GetOption() */ +/************************************************************************/ + +const char *OGRTigerDataSource::GetOption( const char * pszOption ) + +{ + return CSLFetchNameValue( papszOptions, pszOption ); +} + +/************************************************************************/ +/* GetModule() */ +/************************************************************************/ + +const char *OGRTigerDataSource::GetModule( int iModule ) + +{ + if( iModule < 0 || iModule >= nModules ) + return NULL; + else + return papszModules[iModule]; +} + +/************************************************************************/ +/* CheckModule() */ +/* */ +/* This is used by the writer to check if this module has been */ +/* written to before. */ +/************************************************************************/ + +int OGRTigerDataSource::CheckModule( const char *pszModule ) + +{ + int i; + + for( i = 0; i < nModules; i++ ) + { + if( EQUAL(pszModule,papszModules[i]) ) + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* AddModule() */ +/************************************************************************/ + +void OGRTigerDataSource::AddModule( const char *pszModule ) + +{ + if( CheckModule( pszModule ) ) + return; + + papszModules = CSLAddString( papszModules, pszModule ); + nModules++; +} + +/************************************************************************/ +/* BuildFilename() */ +/************************************************************************/ + +char *OGRTigerDataSource::BuildFilename( const char *pszModuleName, + const char *pszExtension ) + +{ + char *pszFilename; + char szLCExtension[3]; + +/* -------------------------------------------------------------------- */ +/* Force the record type to lower case if the filename appears */ +/* to be in lower case. */ +/* -------------------------------------------------------------------- */ + if( *pszExtension >= 'A' && *pszExtension <= 'Z' && *pszModuleName == 't' ) + { + szLCExtension[0] = (*pszExtension) + 'a' - 'A'; + szLCExtension[1] = '\0'; + pszExtension = szLCExtension; + } + +/* -------------------------------------------------------------------- */ +/* Build the filename. */ +/* -------------------------------------------------------------------- */ + pszFilename = (char *) CPLMalloc(strlen(GetDirPath()) + + strlen(pszModuleName) + + strlen(pszExtension) + 10); + + if( strlen(GetDirPath()) == 0 ) + sprintf( pszFilename, "%s%s", + pszModuleName, pszExtension ); + else + sprintf( pszFilename, "%s/%s%s", + GetDirPath(), pszModuleName, pszExtension ); + + return pszFilename; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRTigerDataSource::TestCapability( const char *pszCap ) + +{ + if( EQUAL(pszCap,ODsCCreateLayer) ) + return GetWriteMode(); + else + return FALSE; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRTigerDataSource::Create( const char *pszNameIn, char **papszOptions ) + +{ + VSIStatBufL stat; + +/* -------------------------------------------------------------------- */ +/* Try to create directory if it doesn't already exist. */ +/* -------------------------------------------------------------------- */ + if( VSIStatL( pszNameIn, &stat ) != 0 ) + { + VSIMkdir( pszNameIn, 0755 ); + } + + if( VSIStatL( pszNameIn, &stat ) != 0 || !VSI_ISDIR(stat.st_mode) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s is not a directory, nor can be directly created as one.", + pszName ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Store various information. */ +/* -------------------------------------------------------------------- */ + pszPath = CPLStrdup( pszNameIn ); + pszName = CPLStrdup( pszNameIn ); + bWriteMode = TRUE; + + SetOptionList( papszOptions ); + +/* -------------------------------------------------------------------- */ +/* Work out the version. */ +/* -------------------------------------------------------------------- */ +// nVersionCode = 1000; /* census 2000 */ + + nVersionCode = 1002; /* census 2002 */ + if( GetOption("VERSION") != NULL ) + { + nVersionCode = atoi(GetOption("VERSION")); + nVersionCode = MAX(0,MIN(9999,nVersionCode)); + } + nVersion = TigerClassifyVersion(nVersionCode); + + return TRUE; +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer *OGRTigerDataSource::ICreateLayer( const char *pszLayerName, + OGRSpatialReference *poSpatRef, + CPL_UNUSED OGRwkbGeometryType eGType, + CPL_UNUSED char **papszOptions ) +{ + OGRTigerLayer *poLayer = NULL; + + if( GetLayer( pszLayerName ) != NULL ) + return GetLayer( pszLayerName ); + + if( poSpatRef != NULL && + (!poSpatRef->IsGeographic() + || !EQUAL(poSpatRef->GetAttrValue("DATUM"), + "North_American_Datum_1983")) ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Requested coordinate system wrong for Tiger, " + "forcing to GEOGCS NAD83." ); + } + + if( EQUAL(pszLayerName,"PIP") ) + { + poLayer = new OGRTigerLayer( this, + new TigerPIP( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"ZipPlus4") ) + { + poLayer = new OGRTigerLayer( this, + new TigerZipPlus4( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"TLIDRange") ) + { + poLayer = new OGRTigerLayer( this, + new TigerTLIDRange( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"PolyChainLink") ) + { + poLayer = new OGRTigerLayer( this, + new TigerPolyChainLink( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"CompleteChain") ) + { + poLayer = new OGRTigerLayer( this, + new TigerCompleteChain( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"AltName") ) + { + poLayer = new OGRTigerLayer( this, + new TigerAltName( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"FeatureIds") ) + { + poLayer = new OGRTigerLayer( this, + new TigerFeatureIds( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"ZipCodes") ) + { + poLayer = new OGRTigerLayer( this, + new TigerZipCodes( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"Landmarks") ) + { + poLayer = new OGRTigerLayer( this, + new TigerLandmarks( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"AreaLandmarks") ) + { + poLayer = new OGRTigerLayer( this, + new TigerAreaLandmarks( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"KeyFeatures") ) + { + poLayer = new OGRTigerLayer( this, + new TigerKeyFeatures( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"EntityNames") ) + { + poLayer = new OGRTigerLayer( this, + new TigerEntityNames( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"IDHistory") ) + { + poLayer = new OGRTigerLayer( this, + new TigerIDHistory( this, NULL ) ); + } + else if( EQUAL(pszLayerName,"Polygon") ) + { + poLayer = new OGRTigerLayer( this, + new TigerPolygon( this, NULL ) ); + } + + else if( EQUAL(pszLayerName,"PolygonCorrections") ) + { + poLayer = new OGRTigerLayer( this, + new TigerPolygonCorrections( this, NULL ) ); + } + + else if( EQUAL(pszLayerName,"PolygonEconomic") ) + { + poLayer = new OGRTigerLayer( this, + new TigerPolygonEconomic( this, NULL ) ); + } + + else if( EQUAL(pszLayerName,"SpatialMetadata") ) + { + poLayer = new OGRTigerLayer( this, + new TigerSpatialMetadata( this, NULL ) ); + } + + else if( EQUAL(pszLayerName,"ZeroCellID") ) + { + poLayer = new OGRTigerLayer( this, + new TigerZeroCellID( this, NULL ) ); + } + + else if( EQUAL(pszLayerName,"OverUnder") ) + { + poLayer = new OGRTigerLayer( this, + new TigerOverUnder( this, NULL ) ); + } + + if( poLayer == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to create layer %s, not a known TIGER/Line layer.", + pszLayerName ); + } + else + AddLayer( poLayer ); + + return poLayer; +} + +/************************************************************************/ +/* DeleteModuleFiles() */ +/************************************************************************/ + +void OGRTigerDataSource::DeleteModuleFiles( const char *pszModule ) + +{ + char **papszDirFiles = CPLReadDir( GetDirPath() ); + int i, nCount = CSLCount(papszDirFiles); + + for( i = 0; i < nCount; i++ ) + { + if( EQUALN(pszModule,papszDirFiles[i],strlen(pszModule)) ) + { + const char *pszFilename; + + pszFilename = CPLFormFilename( GetDirPath(), + papszDirFiles[i], + NULL ); + if( VSIUnlink( pszFilename ) != 0 ) + { + CPLDebug( "OGR_TIGER", "Failed to unlink %s", pszFilename ); + } + } + } + + CSLDestroy( papszDirFiles ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/ogrtigerdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/ogrtigerdriver.cpp new file mode 100644 index 000000000..96511606e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/ogrtigerdriver.cpp @@ -0,0 +1,134 @@ +/****************************************************************************** + * $Id: ogrtigerdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements OGRTigerDriver + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrtigerdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +static GDALDataset *OGRTigerDriverOpen( GDALOpenInfo* poOpenInfo ) + +{ + if( !poOpenInfo->bStatOK ) + return NULL; + char** papszSiblingFiles = poOpenInfo->GetSiblingFiles(); + if( papszSiblingFiles != NULL ) + { + int i; + int bFoundCompatibleFile = FALSE; + for( i = 0; papszSiblingFiles[i] != NULL; i++ ) + { + int nLen = (int)strlen(papszSiblingFiles[i]); + if( nLen > 4 && + papszSiblingFiles[i][nLen-4] == '.' && + papszSiblingFiles[i][nLen-1] == '1' ) + { + bFoundCompatibleFile = TRUE; + break; + } + } + if( !bFoundCompatibleFile ) + return FALSE; + } + + OGRTigerDataSource *poDS = new OGRTigerDataSource; + + if( !poDS->Open( poOpenInfo->pszFilename, TRUE ) ) + { + delete poDS; + poDS = NULL; + } + + if( poDS != NULL && poOpenInfo->eAccess == GA_Update ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Tiger Driver doesn't support update." ); + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +static GDALDataset *OGRTigerDriverCreate( const char * pszName, + CPL_UNUSED int nBands, + CPL_UNUSED int nXSize, + CPL_UNUSED int nYSize, + CPL_UNUSED GDALDataType eDT, + char **papszOptions ) +{ + OGRTigerDataSource *poDS; + + poDS = new OGRTigerDataSource(); + + if( poDS->Create( pszName, papszOptions ) ) + return poDS; + else + { + delete poDS; + return NULL; + } +} + +/************************************************************************/ +/* RegisterOGRTiger() */ +/************************************************************************/ + +void RegisterOGRTiger() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "TIGER" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "TIGER" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "U.S. Census TIGER/Line" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_tiger.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRTigerDriverOpen; + poDriver->pfnCreate = OGRTigerDriverCreate; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/ogrtigerlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/ogrtigerlayer.cpp new file mode 100644 index 000000000..adbfdae2d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/ogrtigerlayer.cpp @@ -0,0 +1,277 @@ +/****************************************************************************** + * $Id: ogrtigerlayer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: TIGER/Line Translator + * Purpose: Implements OGRTigerLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" + +CPL_CVSID("$Id: ogrtigerlayer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGRTigerLayer() */ +/* */ +/* Note that the OGRTigerLayer assumes ownership of the passed */ +/* OGRFeatureDefn object. */ +/************************************************************************/ + +OGRTigerLayer::OGRTigerLayer( OGRTigerDataSource *poDSIn, + TigerFileBase * poReaderIn ) + +{ + poDS = poDSIn; + poReader = poReaderIn; + + iLastFeatureId = 0; + iLastModule = -1; + + nFeatureCount = 0; + panModuleFCount = NULL; + panModuleOffset = NULL; + +/* -------------------------------------------------------------------- */ +/* Setup module feature counts. */ +/* -------------------------------------------------------------------- */ + if( !poDS->GetWriteMode() ) + { + panModuleFCount = (int *) + CPLCalloc(poDS->GetModuleCount(),sizeof(int)); + panModuleOffset = (int *) + CPLCalloc(poDS->GetModuleCount()+1,sizeof(int)); + + nFeatureCount = 0; + + for( int iModule = 0; iModule < poDS->GetModuleCount(); iModule++ ) + { + if( poReader->SetModule( poDS->GetModule(iModule) ) ) + panModuleFCount[iModule] = poReader->GetFeatureCount(); + else + panModuleFCount[iModule] = 0; + + panModuleOffset[iModule] = nFeatureCount; + nFeatureCount += panModuleFCount[iModule]; + } + + // this entry is just to make range comparisons easy without worrying + // about falling off the end of the array. + panModuleOffset[poDS->GetModuleCount()] = nFeatureCount; + } + + poReader->SetModule( NULL ); +} + +/************************************************************************/ +/* ~OGRTigerLayer() */ +/************************************************************************/ + +OGRTigerLayer::~OGRTigerLayer() + +{ + if( m_nFeaturesRead > 0 && poReader->GetFeatureDefn() != NULL ) + { + CPLDebug( "TIGER", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poReader->GetFeatureDefn()->GetName() ); + } + + delete poReader; + + CPLFree( panModuleFCount ); + CPLFree( panModuleOffset ); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRTigerLayer::ResetReading() + +{ + iLastFeatureId = 0; + iLastModule = -1; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRTigerLayer::GetFeature( GIntBig nFeatureId ) + +{ + if( nFeatureId < 1 || nFeatureId > nFeatureCount ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we don't have the current module open for the requested */ +/* data, then open it now. */ +/* -------------------------------------------------------------------- */ + if( iLastModule == -1 + || nFeatureId <= panModuleOffset[iLastModule] + || nFeatureId > panModuleOffset[iLastModule+1] ) + { + for( iLastModule = 0; + iLastModule < poDS->GetModuleCount() + && nFeatureId > panModuleOffset[iLastModule+1]; + iLastModule++ ) {} + + if( !poReader->SetModule( poDS->GetModule(iLastModule) ) ) + { + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Fetch the feature associated with the record. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + + poFeature = + poReader->GetFeature( (int)nFeatureId-panModuleOffset[iLastModule]-1 ); + + if( poFeature != NULL ) + { + poFeature->SetFID( nFeatureId ); + + if( poFeature->GetGeometryRef() != NULL ) + poFeature->GetGeometryRef()->assignSpatialReference( + poDS->GetSpatialRef() ); + + poFeature->SetField( 0, poReader->GetShortModule() ); + + m_nFeaturesRead++; + } + + return poFeature; +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRTigerLayer::GetNextFeature() + +{ +/* -------------------------------------------------------------------- */ +/* Read features till we find one that satisfies our current */ +/* spatial criteria. */ +/* -------------------------------------------------------------------- */ + while( iLastFeatureId < nFeatureCount ) + { + OGRFeature *poFeature = GetFeature( ++iLastFeatureId ); + + if( poFeature == NULL ) + break; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } + + return NULL; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRTigerLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) ) + return FALSE; + + else if( EQUAL(pszCap,OLCFastFeatureCount) ) + return TRUE; + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return FALSE; + + else if( EQUAL(pszCap,OLCSequentialWrite) ) + return poDS->GetWriteMode(); + + else + return FALSE; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn *OGRTigerLayer::GetLayerDefn() + +{ + OGRFeatureDefn* poFDefn = poReader->GetFeatureDefn(); + if( poFDefn != NULL ) + { + if( poFDefn->GetGeomFieldCount() > 0 ) + poFDefn->GetGeomFieldDefn(0)->SetSpatialRef(poDS->GetSpatialRef()); + } + return poFDefn; +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRTigerLayer::CreateField( CPL_UNUSED OGRFieldDefn *poField, + CPL_UNUSED int bApproxOK ) +{ + /* notdef/TODO: I should add some checking here eventually. */ + + return OGRERR_NONE; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRTigerLayer::ICreateFeature( OGRFeature *poFeature ) + +{ + return poReader->CreateFeature( poFeature ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRTigerLayer::GetFeatureCount( int bForce ) + +{ + if( m_poFilterGeom == NULL && m_poAttrQuery == NULL ) + return nFeatureCount; + else + return OGRLayer::GetFeatureCount( bForce ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigeraltname.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigeraltname.cpp new file mode 100644 index 000000000..68ccdfe52 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigeraltname.cpp @@ -0,0 +1,172 @@ +/****************************************************************************** + * $Id: tigeraltname.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerAltName, providing access to RT4 files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigeraltname.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "4" + +static const TigerFieldInfo rt4_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "TLID", 'R', 'N', OFTInteger, 6, 15, 10, 1, 1, 1 }, + { "RTSQ", 'R', 'N', OFTInteger, 16, 18, 3, 1, 1, 1 }, + { "FEAT", ' ', ' ', OFTIntegerList, 0, 0, 8, 1, 0, 0 } + // Note: we don't mention the FEAT1, FEAT2, FEAT3, FEAT4, FEAT5 fields + // here because they're handled separately in the code below; they correspond + // to the FEAT array field here. +}; + +static const TigerRecordInfo rt4_info = + { + rt4_fields, + sizeof(rt4_fields) / sizeof(TigerFieldInfo), + 58 + }; + + +/************************************************************************/ +/* TigerAltName() */ +/************************************************************************/ + +TigerAltName::TigerAltName( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(&rt4_info, FILE_CODE) +{ + OGRFieldDefn oField("",OFTInteger); + + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "AltName" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + /* -------------------------------------------------------------------- */ + /* Fields from type 4 record. */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns( psRTInfo, poFeatureDefn ); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *TigerAltName::GetFeature( int nRecordId ) + +{ + char achRecord[OGR_TIGER_RECBUF_LEN]; + + if( nRecordId < 0 || nRecordId >= nFeatures ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Request for out-of-range feature %d of %s4", + nRecordId, pszModule ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the raw record data from the file. */ +/* -------------------------------------------------------------------- */ + if( fpPrimary == NULL ) + return NULL; + + if( VSIFSeekL( fpPrimary, nRecordId * nRecordLength, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to %d of %s4", + nRecordId * nRecordLength, pszModule ); + return NULL; + } + + if( VSIFReadL( achRecord, psRTInfo->nRecordLength, 1, fpPrimary ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read record %d of %s4", + nRecordId, pszModule ); + return NULL; + } + + /* -------------------------------------------------------------------- */ + /* Set fields. */ + /* -------------------------------------------------------------------- */ + + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + int anFeatList[5]; + int nFeatCount=0; + + SetFields( psRTInfo, poFeature, achRecord ); + + for( int iFeat = 0; iFeat < 5; iFeat++ ) + { + const char * pszFieldText; + + pszFieldText = GetField( achRecord, 19 + iFeat*8, 26 + iFeat*8 ); + + if( *pszFieldText != '\0' ) + anFeatList[nFeatCount++] = atoi(pszFieldText); + } + + poFeature->SetField( "FEAT", nFeatCount, anFeatList ); + + return poFeature; +} + +/************************************************************************/ +/* CreateFeature() */ +/************************************************************************/ + +OGRErr TigerAltName::CreateFeature( OGRFeature *poFeature ) + +{ + char szRecord[OGR_TIGER_RECBUF_LEN]; + const int *panValue; + int nValueCount = 0; + + if( !SetWriteModule( FILE_CODE, psRTInfo->nRecordLength+2, poFeature ) ) + return OGRERR_FAILURE; + + memset( szRecord, ' ', psRTInfo->nRecordLength ); + + WriteFields( psRTInfo, poFeature, szRecord ); + + panValue = poFeature->GetFieldAsIntegerList( "FEAT", &nValueCount ); + for( int i = 0; i < nValueCount; i++ ) + { + char szWork[9]; + + sprintf( szWork, "%8d", panValue[i] ); + strncpy( szRecord + 18 + 8 * i, szWork, 8 ); + } + + WriteRecord( szRecord, psRTInfo->nRecordLength, FILE_CODE ); + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerarealandmarks.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerarealandmarks.cpp new file mode 100644 index 000000000..a071697b1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerarealandmarks.cpp @@ -0,0 +1,74 @@ +/****************************************************************************** + * $Id: tigerarealandmarks.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerAreaLandmarks, providing access to .RT8 files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerarealandmarks.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "8" + +static const TigerFieldInfo rt8_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTString, 6, 10, 5, 1, 1, 1 }, + { "STATE", 'L', 'N', OFTInteger, 6, 7, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 8, 10, 3, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 }, + { "LAND", 'R', 'N', OFTInteger, 26, 35, 10, 1, 1, 1 } +}; + +static const TigerRecordInfo rt8_info = + { + rt8_fields, + sizeof(rt8_fields) / sizeof(TigerFieldInfo), + 36 + }; + +/************************************************************************/ +/* TigerAreaLandmarks() */ +/************************************************************************/ + +TigerAreaLandmarks::TigerAreaLandmarks( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(&rt8_info, FILE_CODE) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "AreaLandmarks" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + /* -------------------------------------------------------------------- */ + /* Fields from type 8 record. */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns( psRTInfo, poFeatureDefn ); + +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigercompletechain.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigercompletechain.cpp new file mode 100644 index 000000000..4f349fbf4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigercompletechain.cpp @@ -0,0 +1,808 @@ +/****************************************************************************** + * $Id: tigercompletechain.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerCompleteChain, providing access to RT1 and + * related files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigercompletechain.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +static const TigerFieldInfo rt1_2002_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "TLID", 'R', 'N', OFTInteger, 6, 15, 10, 1, 1, 1 }, + { "SIDE1", 'R', 'N', OFTInteger, 16, 16, 1, 1, 1, 1 }, + { "SOURCE", 'L', 'A', OFTString, 17, 17, 1, 1, 1, 1 }, + { "FEDIRP", 'L', 'A', OFTString, 18, 19, 2, 1, 1, 1 }, + { "FENAME", 'L', 'A', OFTString, 20, 49, 30, 1, 1, 1 }, + { "FETYPE", 'L', 'A', OFTString, 50, 53, 4, 1, 1, 1 }, + { "FEDIRS", 'L', 'A', OFTString, 54, 55, 2, 1, 1, 1 }, + { "CFCC", 'L', 'A', OFTString, 56, 58, 3, 1, 1, 1 }, + { "FRADDL", 'R', 'A', OFTString, 59, 69, 11, 1, 1, 1 }, + { "TOADDL", 'R', 'A', OFTString, 70, 80, 11, 1, 1, 1 }, + { "FRADDR", 'R', 'A', OFTString, 81, 91, 11, 1, 1, 1 }, + { "TOADDR", 'R', 'A', OFTString, 92, 102, 11, 1, 1, 1 }, + { "FRIADDL", 'L', 'A', OFTString, 103, 103, 1, 1, 1, 1 }, + { "TOIADDL", 'L', 'A', OFTString, 104, 104, 1, 1, 1, 1 }, + { "FRIADDR", 'L', 'A', OFTString, 105, 105, 1, 1, 1, 1 }, + { "TOIADDR", 'L', 'A', OFTString, 106, 106, 1, 1, 1, 1 }, + { "ZIPL", 'L', 'N', OFTInteger, 107, 111, 5, 1, 1, 1 }, + { "ZIPR", 'L', 'N', OFTInteger, 112, 116, 5, 1, 1, 1 }, + { "AIANHHFPL", 'L', 'N', OFTInteger, 117, 121, 5, 1, 1, 1 }, + { "AIANHHFPR", 'L', 'N', OFTInteger, 122, 126, 5, 1, 1, 1 }, + { "AIHHTLIL", 'L', 'A', OFTString, 127, 127, 1, 1, 1, 1 }, + { "AIHHTLIR", 'L', 'A', OFTString, 128, 128, 1, 1, 1, 1 }, + { "CENSUS1", 'L', 'A', OFTString, 129, 129, 1, 1, 1, 1 }, + { "CENSUS2", 'L', 'A', OFTString, 130, 130, 1, 1, 1, 1 }, + { "STATEL", 'L', 'N', OFTInteger, 131, 132, 2, 1, 1, 1 }, + { "STATER", 'L', 'N', OFTInteger, 133, 134, 2, 1, 1, 1 }, + { "COUNTYL", 'L', 'N', OFTInteger, 135, 137, 3, 1, 1, 1 }, + { "COUNTYR", 'L', 'N', OFTInteger, 138, 140, 3, 1, 1, 1 }, + + { "COUSUBL", 'L', 'N', OFTInteger, 141, 145, 5, 1, 1, 1 }, + { "COUSUBR", 'L', 'N', OFTInteger, 146, 150, 5, 1, 1, 1 }, + { "SUBMCDL", 'L', 'N', OFTInteger, 151, 155, 5, 1, 1, 1 }, + { "SUBMCDR", 'L', 'N', OFTInteger, 156, 160, 5, 1, 1, 1 }, + { "PLACEL", 'L', 'N', OFTInteger, 161, 165, 5, 1, 1, 1 }, + { "PLACER", 'L', 'N', OFTInteger, 166, 170, 5, 1, 1, 1 }, + { "TRACTL", 'L', 'N', OFTInteger, 171, 176, 6, 1, 1, 1 }, + { "TRACTR", 'L', 'N', OFTInteger, 177, 182, 6, 1, 1, 1 }, + { "BLOCKL", 'L', 'N', OFTInteger, 183, 186, 4, 1, 1, 1 }, + { "BLOCKR", 'L', 'N', OFTInteger, 187, 190, 4, 1, 1, 1 } +}; +static const TigerRecordInfo rt1_2002_info = + { + rt1_2002_fields, + sizeof(rt1_2002_fields) / sizeof(TigerFieldInfo), + 228 + }; + +static const TigerFieldInfo rt1_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "TLID", 'R', 'N', OFTInteger, 6, 15, 10, 1, 1, 1 }, + { "SIDE1", 'R', 'N', OFTInteger, 16, 16, 1, 1, 1, 1 }, + { "SOURCE", 'L', 'A', OFTString, 17, 17, 1, 1, 1, 1 }, + { "FEDIRP", 'L', 'A', OFTString, 18, 19, 2, 1, 1, 1 }, + { "FENAME", 'L', 'A', OFTString, 20, 49, 30, 1, 1, 1 }, + { "FETYPE", 'L', 'A', OFTString, 50, 53, 4, 1, 1, 1 }, + { "FEDIRS", 'L', 'A', OFTString, 54, 55, 2, 1, 1, 1 }, + { "CFCC", 'L', 'A', OFTString, 56, 58, 3, 1, 1, 1 }, + { "FRADDL", 'R', 'A', OFTString, 59, 69, 11, 1, 1, 1 }, + { "TOADDL", 'R', 'A', OFTString, 70, 80, 11, 1, 1, 1 }, + { "FRADDR", 'R', 'A', OFTString, 81, 91, 11, 1, 1, 1 }, + { "TOADDR", 'R', 'A', OFTString, 92, 102, 11, 1, 1, 1 }, + { "FRIADDL", 'L', 'A', OFTInteger, 103, 103, 1, 1, 1, 1 }, + { "TOIADDL", 'L', 'A', OFTInteger, 104, 104, 1, 1, 1, 1 }, + { "FRIADDR", 'L', 'A', OFTInteger, 105, 105, 1, 1, 1, 1 }, + { "TOIADDR", 'L', 'A', OFTInteger, 106, 106, 1, 1, 1, 1 }, + { "ZIPL", 'L', 'N', OFTInteger, 107, 111, 5, 1, 1, 1 }, + { "ZIPR", 'L', 'N', OFTInteger, 112, 116, 5, 1, 1, 1 }, + { "FAIRL", 'L', 'N', OFTInteger, 117, 121, 5, 1, 1, 1 }, + { "FAIRR", 'L', 'N', OFTInteger, 122, 126, 5, 1, 1, 1 }, + { "TRUSTL", 'L', 'A', OFTString, 127, 127, 1, 1, 1, 1 }, + { "TRUSTR", 'L', 'A', OFTString, 128, 128, 1, 1, 1, 1 }, + { "CENSUS1", 'L', 'A', OFTString, 129, 129, 1, 1, 1, 1 }, + { "CENSUS2", 'L', 'A', OFTString, 130, 130, 1, 1, 1, 1 }, + { "STATEL", 'L', 'N', OFTInteger, 131, 132, 2, 1, 1, 1 }, + { "STATER", 'L', 'N', OFTInteger, 133, 134, 2, 1, 1, 1 }, + { "COUNTYL", 'L', 'N', OFTInteger, 135, 137, 3, 1, 1, 1 }, + { "COUNTYR", 'L', 'N', OFTInteger, 138, 140, 3, 1, 1, 1 }, + + { "FMCDL", 'L', 'N', OFTInteger, 141, 145, 5, 1, 1, 1 }, + { "FMCDR", 'L', 'N', OFTInteger, 146, 150, 5, 1, 1, 1 }, + { "FSMCDL", 'L', 'N', OFTInteger, 151, 155, 5, 1, 1, 1 }, + { "FSMCDR", 'L', 'N', OFTInteger, 156, 160, 5, 1, 1, 1 }, + { "FPLL", 'L', 'N', OFTInteger, 161, 165, 5, 1, 1, 1 }, + { "FPLR", 'L', 'N', OFTInteger, 166, 170, 5, 1, 1, 1 }, + { "CTBNAL", 'L', 'N', OFTInteger, 171, 176, 6, 1, 1, 1 }, + { "CTBNAR", 'L', 'N', OFTInteger, 177, 182, 6, 1, 1, 1 }, + { "BLKL", 'L', 'N', OFTString, 183, 186, 4, 1, 1, 1 }, + { "BLKR", 'L', 'N', OFTString, 187, 190, 4, 1, 1, 1 } +}; +static const TigerRecordInfo rt1_info = + { + rt1_fields, + sizeof(rt1_fields) / sizeof(TigerFieldInfo), + 228 + }; + +static const TigerRecordInfo rt2_info = + { + NULL, // RT2 is handled specially in the code below; the only + 0, // thing from this structure that is used is: + 208 // <--- nRecordLength + }; + + +static const TigerFieldInfo rt3_2000_Redistricting_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "TLID", 'R', 'N', OFTInteger, 6, 15, 10, 0, 0, 1 }, + { "STATE90L", 'L', 'N', OFTInteger, 16, 17, 2, 1, 1, 1 }, + { "STATE90R", 'L', 'N', OFTInteger, 18, 19, 2, 1, 1, 1 }, + { "COUN90L", 'L', 'N', OFTInteger, 20, 22, 3, 1, 1, 1 }, + { "COUN90R", 'L', 'N', OFTInteger, 23, 25, 3, 1, 1, 1 }, + { "FMCD90L", 'L', 'N', OFTInteger, 26, 30, 5, 1, 1, 1 }, + { "FMCD90R", 'L', 'N', OFTInteger, 31, 35, 5, 1, 1, 1 }, + { "FPL90L", 'L', 'N', OFTInteger, 36, 40, 5, 1, 1, 1 }, + { "FPL90R", 'L', 'N', OFTInteger, 41, 45, 5, 1, 1, 1 }, + { "CTBNA90L", 'L', 'N', OFTInteger, 46, 51, 6, 1, 1, 1 }, + { "CTBNA90R", 'L', 'N', OFTInteger, 52, 57, 6, 1, 1, 1 }, + { "AIR90L", 'L', 'N', OFTInteger, 58, 61, 4, 1, 1, 1 }, + { "AIR90R", 'L', 'N', OFTInteger, 62, 65, 4, 1, 1, 1 }, + { "TRUST90L", 'L', 'A', OFTString, 66, 66, 1, 1, 1, 1 }, + { "TRUST90R", 'L', 'A', OFTString, 67, 67, 1, 1, 1, 1 }, + { "BLK90L", 'L', 'A', OFTString, 70, 73, 4, 1, 1, 1 }, + { "BLK90R", 'L', 'A', OFTString, 74, 77, 4, 1, 1, 1 }, + { "AIRL", 'L', 'N', OFTInteger, 78, 81, 4, 1, 1, 1 }, + { "AIRR", 'L', 'N', OFTInteger, 82, 85, 4, 1, 1, 1 }, + + { "ANRCL", 'L', 'N', OFTInteger, 86, 90, 5, 1, 1, 1 }, + { "ANRCR", 'L', 'N', OFTInteger, 91, 95, 5, 1, 1, 1 }, + { "AITSCEL", 'L', 'N', OFTInteger, 96, 98, 3, 1, 1, 1 }, + { "AITSCER", 'L', 'N', OFTInteger, 99, 101, 3, 1, 1, 1 }, + { "AITSL", 'L', 'N', OFTInteger, 102, 106, 5 , 1, 1, 1 }, + { "AITSR", 'L', 'N', OFTInteger, 107, 111, 5, 1, 1, 1 } +}; +static const TigerRecordInfo rt3_2000_Redistricting_info = + { + rt3_2000_Redistricting_fields, + sizeof(rt3_2000_Redistricting_fields) / sizeof(TigerFieldInfo), + 111 + }; + +static const TigerFieldInfo rt3_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "TLID", 'R', 'N', OFTInteger, 6, 15, 10, 0, 0, 1 }, + { "STATE90L", 'L', 'N', OFTInteger, 16, 17, 2, 1, 1, 1 }, + { "STATE90R", 'L', 'N', OFTInteger, 18, 19, 2, 1, 1, 1 }, + { "COUN90L", 'L', 'N', OFTInteger, 20, 22, 3, 1, 1, 1 }, + { "COUN90R", 'L', 'N', OFTInteger, 23, 25, 3, 1, 1, 1 }, + { "FMCD90L", 'L', 'N', OFTInteger, 26, 30, 5, 1, 1, 1 }, + { "FMCD90R", 'L', 'N', OFTInteger, 31, 35, 5, 1, 1, 1 }, + { "FPL90L", 'L', 'N', OFTInteger, 36, 40, 5, 1, 1, 1 }, + { "FPL90R", 'L', 'N', OFTInteger, 41, 45, 5, 1, 1, 1 }, + { "CTBNA90L", 'L', 'N', OFTInteger, 46, 51, 6, 1, 1, 1 }, + { "CTBNA90R", 'L', 'N', OFTInteger, 52, 57, 6, 1, 1, 1 }, + { "AIR90L", 'L', 'N', OFTInteger, 58, 61, 4, 1, 1, 1 }, + { "AIR90R", 'L', 'N', OFTInteger, 62, 65, 4, 1, 1, 1 }, + { "TRUST90L", 'L', 'A', OFTInteger, 66, 66, 1, 1, 1, 1 }, + { "TRUST90R", 'L', 'A', OFTInteger, 67, 67, 1, 1, 1, 1 }, + { "BLK90L", 'L', 'A', OFTString, 70, 73, 4, 1, 1, 1 }, + { "BLK90R", 'L', 'A', OFTString, 74, 77, 4, 1, 1, 1 }, + { "AIRL", 'L', 'N', OFTInteger, 78, 81, 4, 1, 1, 1 }, + { "AIRR", 'L', 'N', OFTInteger, 82, 85, 4, 1, 1, 1 }, + + { "VTDL", 'L', 'A', OFTString, 104, 107, 4, 1, 1, 1 }, + { "VTDR", 'L', 'A', OFTString, 108, 111, 4, 1, 1, 1 } + +}; +static const TigerRecordInfo rt3_info = + { + rt3_fields, + sizeof(rt3_fields) / sizeof(TigerFieldInfo), + 111 + }; + +/************************************************************************/ +/* TigerCompleteChain() */ +/************************************************************************/ + +TigerCompleteChain::TigerCompleteChain( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "CompleteChain" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbLineString ); + + + if (poDS->GetVersion() >= TIGER_2002) { + psRT1Info = &rt1_2002_info; + bUsingRT3 = FALSE; + } else { + psRT1Info = &rt1_info; + bUsingRT3 = TRUE; + } + + psRT2Info = &rt2_info; + + nRT1RecOffset = 0; + + if (poDS->GetVersion() >= TIGER_2000_Redistricting) { + psRT3Info = &rt3_2000_Redistricting_info; + } else { + psRT3Info = &rt3_info; + } + + fpRT3 = NULL; + + panShapeRecordId = NULL; + fpShape = NULL; + + /* -------------------------------------------------------------------- */ + /* Fields from type 1 record. */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns( psRT1Info, poFeatureDefn ); + + /* -------------------------------------------------------------------- */ + /* Fields from type 3 record. Eventually we should verify that */ + /* a .RT3 file is available before adding these fields. */ + /* -------------------------------------------------------------------- */ + if( bUsingRT3 ) { + AddFieldDefns( psRT3Info, poFeatureDefn ); + } +} + +/************************************************************************/ +/* ~TigerCompleteChain() */ +/************************************************************************/ + +TigerCompleteChain::~TigerCompleteChain() + +{ + CPLFree( panShapeRecordId ); + + if( fpRT3 != NULL ) + VSIFCloseL( fpRT3 ); + + if( fpShape != NULL ) + VSIFCloseL( fpShape ); +} + +/************************************************************************/ +/* SetModule() */ +/************************************************************************/ + +int TigerCompleteChain::SetModule( const char * pszModule ) + +{ + if( !OpenFile( pszModule, "1" ) ) + return FALSE; + + EstablishFeatureCount(); + +/* -------------------------------------------------------------------- */ +/* Is this a copyright record inserted at the beginning of the */ +/* RT1 file by the folks at GDT? If so, setup to ignore the */ +/* first record. */ +/* -------------------------------------------------------------------- */ + nRT1RecOffset = 0; + if( pszModule ) + { + char achHeader[10]; + + VSIFSeekL( fpPrimary, 0, SEEK_SET ); + VSIFReadL( achHeader, sizeof(achHeader), 1, fpPrimary ); + + if( EQUALN(achHeader,"Copyright",8) ) + { + nRT1RecOffset = 1; + nFeatures--; + } + } + +/* -------------------------------------------------------------------- */ +/* Open the RT3 file */ +/* -------------------------------------------------------------------- */ + if( bUsingRT3 ) + { + if( fpRT3 != NULL ) + { + VSIFCloseL( fpRT3 ); + fpRT3 = NULL; + } + + if( pszModule ) + { + char *pszFilename; + + pszFilename = poDS->BuildFilename( pszModule, "3" ); + + fpRT3 = VSIFOpenL( pszFilename, "rb" ); + + CPLFree( pszFilename ); + } + } + +/* -------------------------------------------------------------------- */ +/* Close the shape point file, if open and free the list of */ +/* record ids. */ +/* -------------------------------------------------------------------- */ + if( fpShape != NULL ) + { + VSIFCloseL( fpShape ); + fpShape = NULL; + } + + CPLFree( panShapeRecordId ); + panShapeRecordId = NULL; + +/* -------------------------------------------------------------------- */ +/* Try to open the RT2 file corresponding to this RT1 file. */ +/* -------------------------------------------------------------------- */ + if( pszModule != NULL ) + { + char *pszFilename; + + pszFilename = poDS->BuildFilename( pszModule, "2" ); + + fpShape = VSIFOpenL( pszFilename, "rb" ); + + if( fpShape == NULL ) + { + if( nRT1RecOffset == 0 ) + CPLError( CE_Warning, CPLE_OpenFailed, + "Failed to open %s, intermediate shape arcs will not be available.\n", + pszFilename ); + } + else + panShapeRecordId = (int *)CPLCalloc(sizeof(int),(size_t)GetFeatureCount()); + + CPLFree( pszFilename ); + } + + return TRUE; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *TigerCompleteChain::GetFeature( int nRecordId ) + +{ + char achRecord[OGR_TIGER_RECBUF_LEN]; + + if( nRecordId < 0 || nRecordId >= nFeatures ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Request for out-of-range feature %d of %s1", + nRecordId, pszModule ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the raw record data from the file. */ +/* -------------------------------------------------------------------- */ + if( fpPrimary == NULL ) + return NULL; + + if( VSIFSeekL( fpPrimary, (nRecordId+nRT1RecOffset) * nRecordLength, + SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to %d of %s1", + nRecordId * nRecordLength, pszModule ); + return NULL; + } + + if( VSIFReadL( achRecord, psRT1Info->nRecordLength, 1, fpPrimary ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read %d bytes of record %d of %s1 at offset %d", + psRT1Info->nRecordLength, nRecordId, pszModule, + (nRecordId+nRT1RecOffset) * nRecordLength ); + return NULL; + } + + /* -------------------------------------------------------------------- */ + /* Set fields. */ + /* -------------------------------------------------------------------- */ + + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + SetFields( psRT1Info, poFeature, achRecord ); + + /* -------------------------------------------------------------------- */ + /* Read RT3 record, and apply fields. */ + /* -------------------------------------------------------------------- */ + + if( fpRT3 != NULL ) + { + char achRT3Rec[OGR_TIGER_RECBUF_LEN]; + int nRT3RecLen = psRT3Info->nRecordLength + nRecordLength - psRT1Info->nRecordLength; + + if( VSIFSeekL( fpRT3, nRecordId * nRT3RecLen, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to %d of %s3", + nRecordId * nRT3RecLen, pszModule ); + return NULL; + } + + if( VSIFReadL( achRT3Rec, psRT3Info->nRecordLength, 1, fpRT3 ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read record %d of %s3", + nRecordId, pszModule ); + return NULL; + } + + SetFields( psRT3Info, poFeature, achRT3Rec ); + + } + +/* -------------------------------------------------------------------- */ +/* Set geometry */ +/* -------------------------------------------------------------------- */ + OGRLineString *poLine = new OGRLineString(); + + poLine->setPoint(0, + atoi(GetField(achRecord, 191, 200)) / 1000000.0, + atoi(GetField(achRecord, 201, 209)) / 1000000.0 ); + + if( !AddShapePoints( poFeature->GetFieldAsInteger("TLID"), nRecordId, + poLine, 0 ) ) + { + delete poFeature; + return NULL; + } + + poLine->addPoint(atoi(GetField(achRecord, 210, 219)) / 1000000.0, + atoi(GetField(achRecord, 220, 228)) / 1000000.0 ); + + poFeature->SetGeometryDirectly( poLine ); + + return poFeature; +} + +/************************************************************************/ +/* AddShapePoints() */ +/* */ +/* Record zero or more shape records associated with this line */ +/* and add the points to the passed line geometry. */ +/************************************************************************/ + +int TigerCompleteChain::AddShapePoints( int nTLID, int nRecordId, + OGRLineString * poLine, + CPL_UNUSED int nSeqNum ) +{ + int nShapeRecId; + + nShapeRecId = GetShapeRecordId( nRecordId, nTLID ); + + // -2 means an error occured. + if( nShapeRecId == -2 ) + return FALSE; + + // -1 means there are no extra shape vertices, but things worked fine. + if( nShapeRecId == -1 ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Read all the sequential records with the same TLID. */ +/* -------------------------------------------------------------------- */ + char achShapeRec[OGR_TIGER_RECBUF_LEN]; + int nShapeRecLen = psRT2Info->nRecordLength + nRecordLength - psRT1Info->nRecordLength; + + for( ; TRUE; nShapeRecId++ ) + { + int nBytesRead = 0; + + if( VSIFSeekL( fpShape, (nShapeRecId-1) * nShapeRecLen, + SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to %d of %s2", + (nShapeRecId-1) * nShapeRecLen, pszModule ); + return FALSE; + } + + nBytesRead = VSIFReadL( achShapeRec, 1, psRT2Info->nRecordLength, + fpShape ); + + /* + ** Handle case where the last record in the file is full. We will + ** try to read another record but not find it. We require that we + ** have found at least one shape record for this case though. + */ + if( nBytesRead <= 0 && VSIFEofL( fpShape ) + && poLine->getNumPoints() > 0 ) + break; + + if( nBytesRead != psRT2Info->nRecordLength ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read %d bytes of record %d of %s2 at offset %d", + psRT2Info->nRecordLength, nShapeRecId, pszModule, + (nShapeRecId-1) * nShapeRecLen ); + return FALSE; + } + + if( atoi(GetField(achShapeRec,6,15)) != nTLID ) + break; + +/* -------------------------------------------------------------------- */ +/* Translate the locations into OGRLineString vertices. */ +/* -------------------------------------------------------------------- */ + int iVertex; + + for( iVertex = 0; iVertex < 10; iVertex++ ) + { + int iStart = 19 + 19*iVertex; + int nX = atoi(GetField(achShapeRec,iStart,iStart+9)); + int nY = atoi(GetField(achShapeRec,iStart+10,iStart+18)); + + if( nX == 0 && nY == 0 ) + break; + + poLine->addPoint( nX / 1000000.0, nY / 1000000.0 ); + } + +/* -------------------------------------------------------------------- */ +/* Don't get another record if this one was incomplete. */ +/* -------------------------------------------------------------------- */ + if( iVertex < 10 ) + break; + } + + return TRUE; +} + +/************************************************************************/ +/* GetShapeRecordId() */ +/* */ +/* Get the record id of the first record of shape points for */ +/* the provided TLID (complete chain). */ +/************************************************************************/ + +int TigerCompleteChain::GetShapeRecordId( int nChainId, int nTLID ) + +{ + CPLAssert( nChainId >= 0 && nChainId < GetFeatureCount() ); + + if( fpShape == NULL || panShapeRecordId == NULL ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Do we already have the answer? */ +/* -------------------------------------------------------------------- */ + if( panShapeRecordId[nChainId] != 0 ) + return panShapeRecordId[nChainId]; + +/* -------------------------------------------------------------------- */ +/* If we don't already have this value, then search from the */ +/* previous known record. */ +/* -------------------------------------------------------------------- */ + int iTestChain, nWorkingRecId; + + for( iTestChain = nChainId-1; + iTestChain >= 0 && panShapeRecordId[iTestChain] <= 0; + iTestChain-- ) {} + + if( iTestChain < 0 ) + { + iTestChain = -1; + nWorkingRecId = 1; + } + else + { + nWorkingRecId = panShapeRecordId[iTestChain]+1; + } + +/* -------------------------------------------------------------------- */ +/* If we have non existent records following (-1's) we can */ +/* narrow our search a bit. */ +/* -------------------------------------------------------------------- */ + while( panShapeRecordId[iTestChain+1] == -1 ) + { + iTestChain++; + } + +/* -------------------------------------------------------------------- */ +/* Read records up to the maximum distance that is possibly */ +/* required, looking for our target TLID. */ +/* -------------------------------------------------------------------- */ + int nMaxChainToRead = nChainId - iTestChain; + int nChainsRead = 0; + char achShapeRec[OGR_TIGER_RECBUF_LEN]; + int nShapeRecLen = psRT2Info->nRecordLength + nRecordLength - psRT1Info->nRecordLength; + + while( nChainsRead < nMaxChainToRead ) + { + if( VSIFSeekL( fpShape, (nWorkingRecId-1) * nShapeRecLen, + SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to %d of %s2", + (nWorkingRecId-1) * nShapeRecLen, pszModule ); + return -2; + } + + if( VSIFReadL( achShapeRec, psRT2Info->nRecordLength, 1, fpShape ) != 1 ) + { + if( !VSIFEofL( fpShape ) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read record %d of %s2", + nWorkingRecId-1, pszModule ); + return -2; + } + else + return -1; + } + + if( atoi(GetField(achShapeRec,6,15)) == nTLID ) + { + panShapeRecordId[nChainId] = nWorkingRecId; + + return nWorkingRecId; + } + + if( atoi(GetField(achShapeRec,16,18)) == 1 ) + { + nChainsRead++; + } + + nWorkingRecId++; + } + + panShapeRecordId[nChainId] = -1; + + return -1; +} + +/************************************************************************/ +/* SetWriteModule() */ +/************************************************************************/ +int TigerCompleteChain::SetWriteModule( const char *pszFileCode, int nRecLen, + OGRFeature *poFeature ) + +{ + int bSuccess; + + bSuccess = TigerFileBase::SetWriteModule( pszFileCode, nRecLen, poFeature); + if( !bSuccess ) + return bSuccess; + +/* -------------------------------------------------------------------- */ +/* Open the RT3 file */ +/* -------------------------------------------------------------------- */ + if( bUsingRT3 ) + { + if( fpRT3 != NULL ) + { + VSIFCloseL( fpRT3 ); + fpRT3 = NULL; + } + + if( pszModule ) + { + char *pszFilename; + + pszFilename = poDS->BuildFilename( pszModule, "3" ); + + fpRT3 = VSIFOpenL( pszFilename, "ab" ); + + CPLFree( pszFilename ); + } + } + +/* -------------------------------------------------------------------- */ +/* Close the shape point file, if open and free the list of */ +/* record ids. */ +/* -------------------------------------------------------------------- */ + if( fpShape != NULL ) + { + VSIFCloseL( fpShape ); + fpShape = NULL; + } + + if( pszModule ) + { + char *pszFilename; + + pszFilename = poDS->BuildFilename( pszModule, "2" ); + + fpShape = VSIFOpenL( pszFilename, "ab" ); + + CPLFree( pszFilename ); + } + + return TRUE; +} + +/************************************************************************/ +/* CreateFeature() */ +/************************************************************************/ + +OGRErr TigerCompleteChain::CreateFeature( OGRFeature *poFeature ) + +{ + char szRecord[OGR_TIGER_RECBUF_LEN]; + OGRLineString *poLine = (OGRLineString *) poFeature->GetGeometryRef(); + + if( poLine == NULL + || (poLine->getGeometryType() != wkbLineString + && poLine->getGeometryType() != wkbLineString25D) ) + return OGRERR_FAILURE; + + /* -------------------------------------------------------------------- */ + /* Write basic data record ("RT1") */ + /* -------------------------------------------------------------------- */ + if( !SetWriteModule( "1", psRT1Info->nRecordLength+2, poFeature ) ) + return OGRERR_FAILURE; + memset( szRecord, ' ', psRT1Info->nRecordLength ); + WriteFields( psRT1Info, poFeature, szRecord ); + WritePoint( szRecord, 191, poLine->getX(0), poLine->getY(0) ); + WritePoint( szRecord, 210, + poLine->getX(poLine->getNumPoints()-1), + poLine->getY(poLine->getNumPoints()-1) ); + WriteRecord( szRecord, psRT1Info->nRecordLength, "1" ); + + /* -------------------------------------------------------------------- */ + /* Write geographic entity codes (RT3) */ + /* -------------------------------------------------------------------- */ + if (bUsingRT3) { + memset( szRecord, ' ', psRT3Info->nRecordLength ); + WriteFields( psRT3Info, poFeature, szRecord ); + WriteRecord( szRecord, psRT3Info->nRecordLength, "3", fpRT3 ); + } + + /* -------------------------------------------------------------------- */ + /* Write shapes sections (RT2) */ + /* -------------------------------------------------------------------- */ + if( poLine->getNumPoints() > 2 ) + { + int nPoints = poLine->getNumPoints(); + int iPoint, nRTSQ = 1; + + for( iPoint = 1; iPoint < nPoints-1; ) + { + int i; + char szTemp[5]; + + memset( szRecord, ' ', psRT2Info->nRecordLength ); + + WriteField( poFeature, "TLID", szRecord, 6, 15, 'R', 'N' ); + + sprintf( szTemp, "%3d", nRTSQ ); + strncpy( ((char *)szRecord) + 15, szTemp, 4 ); + + for( i = 0; i < 10; i++ ) + { + if( iPoint < nPoints-1 ) + WritePoint( szRecord, 19+19*i, + poLine->getX(iPoint), poLine->getY(iPoint) ); + else + WritePoint( szRecord, 19+19*i, 0.0, 0.0 ); + + iPoint++; + } + + WriteRecord( szRecord, psRT2Info->nRecordLength, "2", fpShape ); + + nRTSQ++; + } + } + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerentitynames.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerentitynames.cpp new file mode 100644 index 000000000..b81854794 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerentitynames.cpp @@ -0,0 +1,140 @@ +/****************************************************************************** + * $Id: tigerentitynames.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerEntityNames, providing access to .RTC files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerentitynames.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "C" + +static const TigerFieldInfo rtC_2002_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "STATE", 'L', 'N', OFTInteger, 6, 7, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 8, 10, 3, 1, 1, 1 }, + { "DATAYR", 'L', 'A', OFTString, 11, 14, 4, 1, 1, 1 }, + { "FIPS", 'L', 'N', OFTInteger, 15, 19, 5, 1, 1, 1 }, + { "FIPSCC", 'L', 'A', OFTString, 20, 21, 2, 1, 1, 1 }, + { "PLACEDC", 'L', 'A', OFTString, 22, 22, 1, 1, 1, 1 }, + { "LSADC", 'L', 'A', OFTString, 23, 24, 2, 1, 1, 1 }, + { "ENTITY", 'L', 'A', OFTString, 25, 25, 1, 1, 1, 1 }, + { "MA", 'L', 'N', OFTInteger, 26, 29, 4, 1, 1, 1 }, + { "SD", 'L', 'N', OFTInteger, 30, 34, 5, 1, 1, 1 }, + { "AIANHH", 'L', 'N', OFTInteger, 35, 38, 4, 1, 1, 1 }, + { "VTDTRACT", 'R', 'A', OFTString, 39, 44, 6, 1, 1, 1 }, + { "UAUGA", 'L', 'N', OFTInteger, 45, 49, 5, 1, 1, 1 }, + { "AITSCE", 'L', 'N', OFTInteger, 50, 52, 3, 1, 1, 1 }, + { "RS_C1", 'L', 'N', OFTInteger, 53, 54, 2, 1, 1, 1 }, + { "RS_C2", 'L', 'N', OFTInteger, 55, 62, 8, 1, 1, 1 }, + { "NAME", 'L', 'A', OFTString, 63, 122, 60, 1, 1, 1 }, +}; +static const TigerRecordInfo rtC_2002_info = + { + rtC_2002_fields, + sizeof(rtC_2002_fields) / sizeof(TigerFieldInfo), + 122 + }; + +static const TigerFieldInfo rtC_2000_Redistricting_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "STATE", 'L', 'N', OFTInteger, 6, 7, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 8, 10, 3, 1, 1, 1 }, + { "FIPSYR", 'L', 'N', OFTString, 11, 14, 4, 1, 1, 1 }, + { "FIPS", 'L', 'N', OFTInteger, 15, 19, 5, 1, 1, 1 }, + { "FIPSCC", 'L', 'A', OFTString, 20, 21, 2, 1, 1, 1 }, + { "PDC", 'L', 'A', OFTString, 22, 22, 1, 1, 1, 1 }, + { "LASAD", 'L', 'A', OFTString, 23, 24, 2, 1, 1, 1 }, + { "ENTITY", 'L', 'A', OFTString, 25, 25, 1, 1, 1, 1 }, + { "MA", 'L', 'N', OFTInteger, 26, 29, 4, 1, 1, 1 }, + { "SD", 'L', 'N', OFTInteger, 30, 34, 5, 1, 1, 1 }, + { "AIR", 'L', 'N', OFTInteger, 35, 38, 4, 1, 1, 1 }, + { "VTD", 'R', 'A', OFTString, 39, 44, 6, 1, 1, 1 }, + { "UA", 'L', 'N', OFTInteger, 45, 49, 5, 1, 1, 1 }, + { "AITSCE", 'L', 'N', OFTInteger, 50, 52, 3, 1, 1, 1 }, + { "NAME", 'L', 'A', OFTString, 53, 112, 66, 1, 1, 1 } +}; +static const TigerRecordInfo rtC_2000_Redistricting_info = + { + rtC_2000_Redistricting_fields, + sizeof(rtC_2000_Redistricting_fields) / sizeof(TigerFieldInfo), + 112 + }; + +static const TigerFieldInfo rtC_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "STATE", 'L', 'N', OFTInteger, 6, 7, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 8, 10, 3, 1, 1, 1 }, + { "FIPSYR", 'L', 'N', OFTString, 11, 12, 4, 1, 1, 1 }, + { "FIPS", 'L', 'N', OFTInteger, 13, 17, 5, 1, 1, 1 }, + { "FIPSCC", 'L', 'A', OFTString, 18, 19, 2, 1, 1, 1 }, + { "PDC", 'L', 'A', OFTString, 20, 20, 1, 1, 1, 1 }, + { "LASAD", 'L', 'A', OFTString, 21, 22, 2, 1, 1, 1 }, + { "ENTITY", 'L', 'A', OFTString, 23, 23, 1, 1, 1, 1 }, + { "MA", 'L', 'N', OFTInteger, 24, 27, 4, 1, 1, 1 }, + { "SD", 'L', 'N', OFTInteger, 28, 32, 5, 1, 1, 1 }, + { "AIR", 'L', 'N', OFTInteger, 33, 36, 4, 1, 1, 1 }, + { "VTD", 'R', 'A', OFTString, 37, 42, 6, 1, 1, 1 }, + { "UA", 'L', 'N', OFTInteger, 43, 46, 4, 1, 1, 1 }, + { "NAME", 'L', 'A', OFTString, 47, 112, 66, 1, 1, 1 } +}; +static const TigerRecordInfo rtC_info = + { + rtC_fields, + sizeof(rtC_fields) / sizeof(TigerFieldInfo), + 112 + }; + + +/************************************************************************/ +/* TigerEntityNames() */ +/************************************************************************/ + +TigerEntityNames::TigerEntityNames( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(NULL, FILE_CODE) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "EntityNames" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint ); + + if( poDS->GetVersion() >= TIGER_2002 ) { + psRTInfo = &rtC_2002_info; + } else if( poDS->GetVersion() >= TIGER_2000_Redistricting ) { + psRTInfo = &rtC_2000_Redistricting_info; + } else { + psRTInfo = &rtC_info; + } + + AddFieldDefns( psRTInfo, poFeatureDefn ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerfeatureids.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerfeatureids.cpp new file mode 100644 index 000000000..8a4550ed9 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerfeatureids.cpp @@ -0,0 +1,94 @@ +/****************************************************************************** + * $Id: tigerfeatureids.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerFeatureIds, providing access to .RT5 files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerfeatureids.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "5" + +static const TigerFieldInfo rt5_2002_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "FEAT", 'R', 'N', OFTInteger, 11, 18, 8, 1, 1, 1 }, + { "FEDIRP", 'L', 'A', OFTString, 19, 20, 2, 1, 1, 1 }, + { "FENAME", 'L', 'A', OFTString, 21, 50, 30, 1, 1, 1 }, + { "FETYPE", 'L', 'A', OFTString, 51, 54, 4, 1, 1, 1 }, + { "FEDIRS", 'L', 'A', OFTString, 55, 56, 2, 1, 1, 1 }, +}; +static const TigerRecordInfo rt5_2002_info = + { + rt5_2002_fields, + sizeof(rt5_2002_fields) / sizeof(TigerFieldInfo), + 56 + }; + +static const TigerFieldInfo rt5_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTString, 2, 6, 5, 1, 1, 1 }, + { "STATE", 'L', 'N', OFTInteger, 2, 3, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 4, 6, 3, 1, 1, 1 }, + { "FEAT", 'R', 'N', OFTInteger, 7, 14, 8, 1, 1, 1 }, + { "FEDIRP", 'L', 'A', OFTString, 15, 16, 2, 1, 1, 1 }, + { "FENAME", 'L', 'A', OFTString, 17, 46, 30, 1, 1, 1 }, + { "FETYPE", 'L', 'A', OFTString, 47, 50, 4, 1, 1, 1 }, + { "FEDIRS", 'L', 'A', OFTString, 51, 52, 2, 1, 1, 1 } +}; + +static const TigerRecordInfo rt5_info = + { + rt5_fields, + sizeof(rt5_fields) / sizeof(TigerFieldInfo), + 52 + }; + +/************************************************************************/ +/* TigerFeatureIds() */ +/************************************************************************/ + +TigerFeatureIds::TigerFeatureIds( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(NULL, FILE_CODE) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "FeatureIds" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + if (poDS->GetVersion() >= TIGER_2002) { + psRTInfo = &rt5_2002_info; + } else { + psRTInfo = &rt5_info; + } + + AddFieldDefns( psRTInfo, poFeatureDefn ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerfilebase.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerfilebase.cpp new file mode 100644 index 000000000..15fe77e77 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerfilebase.cpp @@ -0,0 +1,626 @@ +/****************************************************************************** + * $Id: tigerfilebase.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerBaseFile class, providing common services to all + * the tiger file readers. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" +#include "cpl_error.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: tigerfilebase.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* TigerFileBase() */ +/************************************************************************/ + +TigerFileBase::TigerFileBase( const TigerRecordInfo *psRTInfoIn, + const char *m_pszFileCodeIn ) + +{ + pszShortModule = NULL; + pszModule = NULL; + fpPrimary = NULL; + poFeatureDefn = NULL; + nFeatures = 0; + nVersionCode = 0; + nVersion = TIGER_Unknown; + + psRTInfo = psRTInfoIn; + m_pszFileCode = m_pszFileCodeIn; +} + +/************************************************************************/ +/* ~TigerFileBase() */ +/************************************************************************/ + +TigerFileBase::~TigerFileBase() + +{ + CPLFree( pszModule ); + CPLFree( pszShortModule ); + + if( poFeatureDefn != NULL ) + { + poFeatureDefn->Release(); + poFeatureDefn = NULL; + } + + if( fpPrimary != NULL ) + { + VSIFCloseL( fpPrimary ); + fpPrimary = NULL; + } +} + +/************************************************************************/ +/* OpenFile() */ +/************************************************************************/ + +int TigerFileBase::OpenFile( const char * pszModuleToOpen, + const char *pszExtension ) + +{ + char *pszFilename; + + CPLFree( pszModule ); + pszModule = NULL; + CPLFree( pszShortModule ); + pszShortModule = NULL; + + if( fpPrimary != NULL ) + { + VSIFCloseL( fpPrimary ); + fpPrimary = NULL; + } + + if( pszModuleToOpen == NULL ) + return TRUE; + + pszFilename = poDS->BuildFilename( pszModuleToOpen, pszExtension ); + + fpPrimary = VSIFOpenL( pszFilename, "rb" ); + + CPLFree( pszFilename ); + + if( fpPrimary != NULL ) + { + pszModule = CPLStrdup(pszModuleToOpen); + pszShortModule = CPLStrdup(pszModuleToOpen); + for( int i = 0; pszShortModule[i] != '\0'; i++ ) + { + if( pszShortModule[i] == '.' ) + pszShortModule[i] = '\0'; + } + + SetupVersion(); + + return TRUE; + } + else + return FALSE; +} + +/************************************************************************/ +/* SetupVersion() */ +/************************************************************************/ + +void TigerFileBase::SetupVersion() + +{ + char aszRecordHead[6]; + + VSIFSeekL( fpPrimary, 0, SEEK_SET ); + VSIFReadL( aszRecordHead, 1, 5, fpPrimary ); + aszRecordHead[5] = '\0'; + nVersionCode = atoi(aszRecordHead+1); + VSIFSeekL( fpPrimary, 0, SEEK_SET ); + + nVersion = TigerClassifyVersion( nVersionCode ); +} + +/************************************************************************/ +/* EstablishRecordLength() */ +/************************************************************************/ + +int TigerFileBase::EstablishRecordLength( VSILFILE * fp ) + +{ + char chCurrent; + int nRecLen = 0; + + if( fp == NULL || VSIFSeekL( fp, 0, SEEK_SET ) != 0 ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Read through to the end of line. */ +/* -------------------------------------------------------------------- */ + chCurrent = '\0'; + while( VSIFReadL( &chCurrent, 1, 1, fp ) == 1 + && chCurrent != 10 + && chCurrent != 13 ) + { + nRecLen++; + } + +/* -------------------------------------------------------------------- */ +/* Is the file zero length? */ +/* -------------------------------------------------------------------- */ + if( nRecLen == 0 ) + { + return -1; + } + + nRecLen++; /* for the 10 or 13 we encountered */ + +/* -------------------------------------------------------------------- */ +/* Read through line terminator characters. We are trying to */ +/* handle cases of CR, CR/LF and LF/CR gracefully. */ +/* -------------------------------------------------------------------- */ + while( VSIFReadL( &chCurrent, 1, 1, fp ) == 1 + && (chCurrent == 10 || chCurrent == 13 ) ) + { + nRecLen++; + } + + VSIFSeekL( fp, 0, SEEK_SET ); + + return nRecLen; +} + +/************************************************************************/ +/* EstablishFeatureCount() */ +/************************************************************************/ + +void TigerFileBase::EstablishFeatureCount() + +{ + if( fpPrimary == NULL ) + return; + + nRecordLength = EstablishRecordLength( fpPrimary ); + + if( nRecordLength == -1 ) + { + nRecordLength = 1; + nFeatures = 0; + return; + } + +/* -------------------------------------------------------------------- */ +/* Now we think we know the fixed record length for the file */ +/* (including line terminators). Get the total file size, and */ +/* divide by this length to get the presumed number of records. */ +/* -------------------------------------------------------------------- */ + long nFileSize; + + VSIFSeekL( fpPrimary, 0, SEEK_END ); + nFileSize = VSIFTellL( fpPrimary ); + + if( (nFileSize % nRecordLength) != 0 ) + { + CPLError( CE_Warning, CPLE_FileIO, + "TigerFileBase::EstablishFeatureCount(): " + "File length %d doesn't divide by record length %d.\n", + (int) nFileSize, (int) nRecordLength ); + } + + nFeatures = nFileSize / nRecordLength; +} + +/************************************************************************/ +/* GetField() */ +/************************************************************************/ + +const char* TigerFileBase::GetField( const char * pachRawDataRecord, + int nStartChar, int nEndChar ) + +{ + char aszField[128]; + int nLength = nEndChar - nStartChar + 1; + + CPLAssert( nEndChar - nStartChar + 2 < (int) sizeof(aszField) ); + + strncpy( aszField, pachRawDataRecord + nStartChar - 1, nLength ); + + aszField[nLength] = '\0'; + while( nLength > 0 && aszField[nLength-1] == ' ' ) + aszField[--nLength] = '\0'; + + return CPLSPrintf("%s", aszField); +} + +/************************************************************************/ +/* SetField() */ +/* */ +/* Set a field on an OGRFeature from a tiger record, or leave */ +/* NULL if the value isn't found. */ +/************************************************************************/ + +void TigerFileBase::SetField( OGRFeature *poFeature, const char *pszField, + const char *pachRecord, int nStart, int nEnd ) + +{ + const char *pszFieldValue = GetField( pachRecord, nStart, nEnd ); + + if( pszFieldValue[0] == '\0' ) + return; + + poFeature->SetField( pszField, pszFieldValue ); +} + +/************************************************************************/ +/* WriteField() */ +/* */ +/* Write a field into a record buffer with the indicated */ +/* formatting, or leave blank if not found. */ +/************************************************************************/ + +int TigerFileBase::WriteField( OGRFeature *poFeature, const char *pszField, + char *pachRecord, int nStart, int nEnd, + char chFormat, char chType ) + +{ + int iField = poFeature->GetFieldIndex( pszField ); + char szValue[512], szFormat[32]; + + CPLAssert( nEnd - nStart + 1 < (int) sizeof(szValue)-1 ); + + if( iField < 0 || !poFeature->IsFieldSet( iField ) ) + return FALSE; + + if( chType == 'N' && chFormat == 'L' ) + { + sprintf( szFormat, "%%0%dd", nEnd - nStart + 1 ); + sprintf( szValue, szFormat, poFeature->GetFieldAsInteger( iField ) ); + } + else if( chType == 'N' && chFormat == 'R' ) + { + sprintf( szFormat, "%%%dd", nEnd - nStart + 1 ); + sprintf( szValue, szFormat, poFeature->GetFieldAsInteger( iField ) ); + } + else if( chType == 'A' && chFormat == 'L' ) + { + strncpy( szValue, poFeature->GetFieldAsString( iField ), + sizeof(szValue) - 1 ); + szValue[sizeof(szValue) - 1] = 0; + if( (int) strlen(szValue) < nEnd - nStart + 1 ) + memset( szValue + strlen(szValue), ' ', + nEnd - nStart + 1 - strlen(szValue) ); + } + else if( chType == 'A' && chFormat == 'R' ) + { + sprintf( szFormat, "%%%ds", nEnd - nStart + 1 ); + sprintf( szValue, szFormat, poFeature->GetFieldAsString( iField ) ); + } + else + { + CPLAssert( FALSE ); + return FALSE; + } + + strncpy( pachRecord + nStart - 1, szValue, nEnd - nStart + 1 ); + + return TRUE; +} + +/************************************************************************/ +/* WritePoint() */ +/************************************************************************/ + +int TigerFileBase::WritePoint( char *pachRecord, int nStart, + double dfX, double dfY ) + +{ + char szTemp[20]; + + if( dfX == 0.0 && dfY == 0.0 ) + { + memcpy( pachRecord + nStart - 1, "+000000000+00000000", 19 ); + } + else + { + sprintf( szTemp, "%+10d%+9d", + (int) floor(dfX * 1000000 + 0.5), + (int) floor(dfY * 1000000 + 0.5) ); + strncpy( pachRecord + nStart - 1, szTemp, 19 ); + } + + return TRUE; +} + +/************************************************************************/ +/* WriteRecord() */ +/************************************************************************/ + +int TigerFileBase::WriteRecord( char *pachRecord, int nRecLen, + const char *pszType, VSILFILE * fp ) + +{ + if( fp == NULL ) + fp = fpPrimary; + + pachRecord[0] = *pszType; + + + /* + * Prior to TIGER_2002, type 5 files lacked the version. So write + * the version in the record iff we're using TIGER_2002 or higher, + * or if this is not type "5" + */ + if ( (poDS->GetVersion() >= TIGER_2002) || + (!EQUAL(pszType, "5")) ) + { + char szVersion[5]; + sprintf( szVersion, "%04d", poDS->GetVersionCode() ); + strncpy( pachRecord + 1, szVersion, 4 ); + } + + VSIFWriteL( pachRecord, nRecLen, 1, fp ); + VSIFWriteL( (void *) "\r\n", 2, 1, fp ); + + return TRUE; +} + +/************************************************************************/ +/* SetWriteModule() */ +/* */ +/* Setup our access to be to the module indicated in the feature. */ +/************************************************************************/ + +int TigerFileBase::SetWriteModule( const char *pszExtension, + CPL_UNUSED int nRecLen, + OGRFeature *poFeature ) +{ +/* -------------------------------------------------------------------- */ +/* Work out what module we should be writing to. */ +/* -------------------------------------------------------------------- */ + const char *pszTargetModule = poFeature->GetFieldAsString( "MODULE" ); + char szFullModule[30]; + + /* TODO/notdef: eventually more logic based on FILE and STATE/COUNTY can + be inserted here. */ + + if( pszTargetModule == NULL ) + return FALSE; + + sprintf( szFullModule, "%s.RT", pszTargetModule ); + +/* -------------------------------------------------------------------- */ +/* Is this our current module? */ +/* -------------------------------------------------------------------- */ + if( pszModule != NULL && EQUAL(szFullModule,pszModule) ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Cleanup the previous file, if any. */ +/* -------------------------------------------------------------------- */ + if( fpPrimary != NULL ) + { + VSIFCloseL( fpPrimary ); + fpPrimary = NULL; + } + + if( pszModule != NULL ) + { + CPLFree( pszModule ); + pszModule = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Is this a module we have never written to before? If so, we */ +/* will try to blow away any existing files in this file set. */ +/* -------------------------------------------------------------------- */ + if( !poDS->CheckModule( szFullModule ) ) + { + poDS->DeleteModuleFiles( szFullModule ); + poDS->AddModule( szFullModule ); + } + +/* -------------------------------------------------------------------- */ +/* Does this file already exist? */ +/* -------------------------------------------------------------------- */ + char *pszFilename; + + pszFilename = poDS->BuildFilename( szFullModule, pszExtension ); + + fpPrimary = VSIFOpenL( pszFilename, "ab" ); + CPLFree(pszFilename); + if( fpPrimary == NULL ) + return FALSE; + + pszModule = CPLStrdup( szFullModule ); + + return TRUE; +} + +/************************************************************************/ +/* AddFieldDefns() */ +/************************************************************************/ +void TigerFileBase::AddFieldDefns(const TigerRecordInfo *psRTInfo, + OGRFeatureDefn *poFeatureDefn) +{ + OGRFieldDefn oField("",OFTInteger); + int i, bLFieldHack; + + bLFieldHack = + CSLTestBoolean( CPLGetConfigOption( "TIGER_LFIELD_AS_STRING", "NO" ) ); + + for (i=0; inFieldCount; ++i) { + if (psRTInfo->pasFields[i].bDefine) { + OGRFieldType eFT = (OGRFieldType)psRTInfo->pasFields[i].OGRtype; + + if( bLFieldHack + && psRTInfo->pasFields[i].cFmt == 'L' + && psRTInfo->pasFields[i].cType == 'N' ) + eFT = OFTString; + + oField.Set( psRTInfo->pasFields[i].pszFieldName, eFT, + psRTInfo->pasFields[i].nLen ); + poFeatureDefn->AddFieldDefn( &oField ); + } + } +} + +/************************************************************************/ +/* SetFields() */ +/************************************************************************/ + +void TigerFileBase::SetFields(const TigerRecordInfo *psRTInfo, + OGRFeature *poFeature, + char *achRecord) +{ + int i; + for (i=0; inFieldCount; ++i) { + if (psRTInfo->pasFields[i].bSet) { + SetField( poFeature, + psRTInfo->pasFields[i].pszFieldName, + achRecord, + psRTInfo->pasFields[i].nBeg, + psRTInfo->pasFields[i].nEnd ); + } + } +} + +/************************************************************************/ +/* WriteField() */ +/************************************************************************/ +void TigerFileBase::WriteFields(const TigerRecordInfo *psRTInfo, + OGRFeature *poFeature, + char *szRecord) +{ + int i; + for (i=0; inFieldCount; ++i) { + if (psRTInfo->pasFields[i].bWrite) { + WriteField( poFeature, + psRTInfo->pasFields[i].pszFieldName, + szRecord, + psRTInfo->pasFields[i].nBeg, + psRTInfo->pasFields[i].nEnd, + psRTInfo->pasFields[i].cFmt, + psRTInfo->pasFields[i].cType ); + } + } +} + + + +/************************************************************************/ +/* SetModule() */ +/************************************************************************/ + +int TigerFileBase::SetModule( const char * pszModule ) + +{ + if (m_pszFileCode == NULL) + return FALSE; + + if( !OpenFile( pszModule, m_pszFileCode ) ) + return FALSE; + + EstablishFeatureCount(); + + return TRUE; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *TigerFileBase::GetFeature( int nRecordId ) + +{ + char achRecord[OGR_TIGER_RECBUF_LEN]; + + if (psRTInfo == NULL) + return NULL; + + if( nRecordId < 0 || nRecordId >= nFeatures ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Request for out-of-range feature %d of %s", + nRecordId, pszModule ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the raw record data from the file. */ +/* -------------------------------------------------------------------- */ + if( fpPrimary == NULL ) + return NULL; + + if( VSIFSeekL( fpPrimary, nRecordId * nRecordLength, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to %d of %s", + nRecordId * nRecordLength, pszModule ); + return NULL; + } + + if( VSIFReadL( achRecord, psRTInfo->nRecordLength, 1, fpPrimary ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read record %d of %s", + nRecordId, pszModule ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Set fields. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + SetFields( psRTInfo, poFeature, achRecord ); + + return poFeature; +} + +/************************************************************************/ +/* CreateFeature() */ +/************************************************************************/ + +OGRErr TigerFileBase::CreateFeature( OGRFeature *poFeature ) + +{ + char szRecord[OGR_TIGER_RECBUF_LEN]; + + if (psRTInfo == NULL) + return OGRERR_FAILURE; + + if( !SetWriteModule( m_pszFileCode, psRTInfo->nRecordLength+2, poFeature ) ) + return OGRERR_FAILURE; + + memset( szRecord, ' ', psRTInfo->nRecordLength ); + + WriteFields( psRTInfo, poFeature, szRecord ); + + WriteRecord( szRecord, psRTInfo->nRecordLength, m_pszFileCode ); + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigeridhistory.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigeridhistory.cpp new file mode 100644 index 000000000..cf87a5b23 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigeridhistory.cpp @@ -0,0 +1,76 @@ +/****************************************************************************** + * $Id: tigeridhistory.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerIDHistory, providing access to .RTH files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigeridhistory.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "H" + +static const TigerFieldInfo rtH_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTString, 6, 10, 5, 1, 1, 1 }, + { "STATE", 'L', 'N', OFTInteger, 6, 7, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 8, 10, 3, 1, 1, 1 }, + { "TLID", 'R', 'N', OFTInteger, 11, 20, 10, 1, 1, 1 }, + { "HIST", 'L', 'A', OFTString, 21, 21, 1, 1, 1, 1 }, + { "SOURCE", 'L', 'A', OFTString, 22, 22, 1, 1, 1, 1 }, + { "TLIDFR1", 'R', 'N', OFTInteger, 23, 32, 10, 1, 1, 1 }, + { "TLIDFR2", 'R', 'N', OFTInteger, 33, 42, 10, 1, 1, 1 }, + { "TLIDTO1", 'R', 'N', OFTInteger, 43, 52, 10, 1, 1, 1 }, + { "TLIDTO2", 'R', 'N', OFTInteger, 53, 62, 10, 1, 1, 1 } +}; +static const TigerRecordInfo rtH_info = + { + rtH_fields, + sizeof(rtH_fields) / sizeof(TigerFieldInfo), + 62 + }; + +/************************************************************************/ +/* TigerIDHistory() */ +/************************************************************************/ + +TigerIDHistory::TigerIDHistory( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(&rtH_info, FILE_CODE) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "IDHistory" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + /* -------------------------------------------------------------------- */ + /* Fields from record type H */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns(psRTInfo, poFeatureDefn); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerinfo.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerinfo.cpp new file mode 100644 index 000000000..02138104f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerinfo.cpp @@ -0,0 +1,238 @@ +/****************************************************************************** + * $Id: tigerinfo.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Simple client for viewing OGR driver data. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrsf_frmts.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +#include "ogr_tiger.h" + +CPL_CVSID("$Id: tigerinfo.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +int bReadOnly = FALSE; +int bVerbose = TRUE; + +static void Usage(); + +static void ReportOnLayer( OGRLayer * ); + +/************************************************************************/ +/* main() */ +/************************************************************************/ + +int main( int nArgc, char ** papszArgv ) + +{ + const char *pszDataSource = NULL; + char **papszLayers = NULL; + +/* -------------------------------------------------------------------- */ +/* Register format(s). */ +/* -------------------------------------------------------------------- */ + RegisterOGRTiger(); + +/* -------------------------------------------------------------------- */ +/* Processing command line arguments. */ +/* -------------------------------------------------------------------- */ + for( int iArg = 1; iArg < nArgc; iArg++ ) + { + if( EQUAL(papszArgv[iArg],"-ro") ) + bReadOnly = TRUE; + else if( EQUAL(papszArgv[iArg],"-q") ) + bVerbose = FALSE; + else if( papszArgv[iArg][0] == '-' ) + { + Usage(); + } + else if( pszDataSource == NULL ) + pszDataSource = papszArgv[iArg]; + else + papszLayers = CSLAddString( papszLayers, papszArgv[iArg] ); + } + + if( pszDataSource == NULL ) + Usage(); + +/* -------------------------------------------------------------------- */ +/* Open data source. */ +/* -------------------------------------------------------------------- */ + OGRDataSource *poDS; + OGRSFDriver *poDriver; + + poDS = OGRSFDriverRegistrar::Open( pszDataSource, !bReadOnly, &poDriver ); + if( poDS == NULL && !bReadOnly ) + { + poDS = OGRSFDriverRegistrar::Open( pszDataSource, FALSE, &poDriver ); + if( poDS != NULL && bVerbose ) + { + printf( "Had to open data source read-only.\n" ); + bReadOnly = TRUE; + } + } + +/* -------------------------------------------------------------------- */ +/* Report failure */ +/* -------------------------------------------------------------------- */ + if( poDS == NULL ) + { + OGRSFDriverRegistrar *poR = OGRSFDriverRegistrar::GetRegistrar(); + + printf( "FAILURE:\n" + "Unable to open datasource `%s' with the following drivers.\n", + pszDataSource ); + + for( int iDriver = 0; iDriver < poR->GetDriverCount(); iDriver++ ) + { + printf( " -> %s\n", poR->GetDriver(iDriver)->GetName() ); + } + + exit( 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Some information messages. */ +/* -------------------------------------------------------------------- */ + if( bVerbose ) + { + printf( "INFO: Open of `%s'\n" + "using driver `%s' successful.\n", + pszDataSource, poDriver->GetName() ); + printf("Tiger Version: %s\n", + TigerVersionString(((OGRTigerDataSource*)poDS)->GetVersion())); + } + + + + if( bVerbose && !EQUAL(pszDataSource,poDS->GetName()) ) + { + printf( "INFO: Internal data source name `%s'\n" + " different from user name `%s'.\n", + poDS->GetName(), pszDataSource ); + } + + +/* -------------------------------------------------------------------- */ +/* Process each data source layer. */ +/* -------------------------------------------------------------------- */ + for( int iLayer = 0; iLayer < poDS->GetLayerCount(); iLayer++ ) + { + OGRLayer *poLayer = poDS->GetLayer(iLayer); + + if( poLayer == NULL ) + { + printf( "FAILURE: Couldn't fetch advertised layer %d!\n", + iLayer ); + exit( 1 ); + } + + if( CSLCount(papszLayers) == 0 ) + { + printf( "%d: %s\n", + iLayer+1, + poLayer->GetLayerDefn()->GetName() ); + } + else if( CSLFindString( papszLayers, + poLayer->GetLayerDefn()->GetName() ) != -1 ) + { + ReportOnLayer( poLayer ); + } + } + +/* -------------------------------------------------------------------- */ +/* Close down. */ +/* -------------------------------------------------------------------- */ + delete poDS; + +#ifdef DBMALLOC + malloc_dump(1); +#endif + + return 0; +} + +/************************************************************************/ +/* Usage() */ +/************************************************************************/ + +static void Usage() + +{ + printf( "Usage: ogrinfo [-ro] [-q] datasource_name [layer [layer ...]]\n"); + exit( 1 ); +} + +/************************************************************************/ +/* ReportOnLayer() */ +/************************************************************************/ + +static void ReportOnLayer( OGRLayer * poLayer ) + +{ + OGRFeatureDefn *poDefn = poLayer->GetLayerDefn(); + + printf( "\n" ); + + printf( "Layer name: %s\n", poDefn->GetName() ); + + printf( "Feature Count: %d\n", poLayer->GetFeatureCount() ); + + if( bVerbose ) + { + char *pszWKT; + + if( poLayer->GetSpatialRef() == NULL ) + pszWKT = CPLStrdup( "(NULL)" ); + else + poLayer->GetSpatialRef()->exportToWkt( &pszWKT ); + + printf( "Layer SRS WKT: %s\n", pszWKT ); + CPLFree( pszWKT ); + } + + for( int iAttr = 0; iAttr < poDefn->GetFieldCount(); iAttr++ ) + { + OGRFieldDefn *poField = poDefn->GetFieldDefn( iAttr ); + + printf( "%s: %s (%d.%d)\n", + poField->GetNameRef(), + poField->GetFieldTypeName( poField->GetType() ), + poField->GetWidth(), + poField->GetPrecision() ); + } + +/* -------------------------------------------------------------------- */ +/* Read, and dump features. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poFeature; + while( (poFeature = poLayer->GetNextFeature()) != NULL ) + { + poFeature->DumpReadable( stdout ); + delete poFeature; + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerkeyfeatures.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerkeyfeatures.cpp new file mode 100644 index 000000000..50fe03774 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerkeyfeatures.cpp @@ -0,0 +1,80 @@ +/****************************************************************************** + * $Id: tigerkeyfeatures.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerKeyFeatures, providing access to .RT9 files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerkeyfeatures.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "9" + +static const TigerFieldInfo rt9_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTString, 6, 10, 5, 1, 1, 1 }, + { "STATE", 'L', 'N', OFTInteger, 6, 7, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 8, 10, 3, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 }, + { "SOURCE", 'L', 'A', OFTString, 26, 26, 1, 1, 1, 1 }, + { "CFCC", 'L', 'A', OFTString, 27, 29, 3, 1, 1, 1 }, + { "KGLNAME", 'L', 'A', OFTString, 30, 59, 30, 1, 1, 1 }, + { "KGLADD", 'R', 'A', OFTString, 60, 70, 11, 1, 1, 1 }, + { "KGLZIP", 'L', 'N', OFTInteger, 71, 75, 5, 1, 1, 1 }, + { "KGLZIP4", 'L', 'N', OFTInteger, 76, 79, 4, 1, 1, 1 }, + { "FEAT", 'R', 'N', OFTInteger, 80, 87, 8, 1, 1, 1 } +}; + +static const TigerRecordInfo rt9_info = + { + rt9_fields, + sizeof(rt9_fields) / sizeof(TigerFieldInfo), + 88 + }; + +/************************************************************************/ +/* TigerKeyFeatures() */ +/************************************************************************/ + +TigerKeyFeatures::TigerKeyFeatures( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(&rt9_info, FILE_CODE) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "KeyFeatures" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + /* -------------------------------------------------------------------- */ + /* Fields from type 9 record. */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns( psRTInfo, poFeatureDefn ); + +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerlandmarks.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerlandmarks.cpp new file mode 100644 index 000000000..d5b8d325b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerlandmarks.cpp @@ -0,0 +1,107 @@ +/****************************************************************************** + * $Id: tigerlandmarks.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerLandmarks, providing access to .RT7 files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerlandmarks.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "7" + +static const TigerFieldInfo rt7_2002_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "LAND", 'R', 'N', OFTInteger, 11, 20, 10, 1, 1, 1 }, + { "SOURCE", 'L', 'A', OFTString, 21, 21, 1, 1, 1, 1 }, + { "CFCC", 'L', 'A', OFTString, 22, 24, 3, 1, 1, 1 }, + { "LANAME", 'L', 'A', OFTString, 25, 54, 30, 1, 1, 1 }, + { "LALONG", 'R', 'N', OFTInteger, 55, 64, 10, 1, 1, 1 }, + { "LALAT", 'R', 'N', OFTInteger, 65, 73, 9, 1, 1, 1 }, + { "FILLER", 'L', 'A', OFTString, 74, 74, 1, 1, 1, 1 }, +}; +static const TigerRecordInfo rt7_2002_info = + { + rt7_2002_fields, + sizeof(rt7_2002_fields) / sizeof(TigerFieldInfo), + 74 + }; + +static const TigerFieldInfo rt7_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTString, 6, 10, 5, 1, 0, 1 }, + { "STATE", 'L', 'N', OFTInteger, 6, 7, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 8, 10, 3, 1, 1, 1 }, + { "LAND", 'R', 'N', OFTInteger, 11, 20, 10, 1, 1, 1 }, + { "SOURCE", 'L', 'A', OFTString, 21, 21, 1, 1, 1, 1 }, + { "CFCC", 'L', 'A', OFTString, 22, 24, 3, 1, 1, 1 }, + { "LANAME", 'L', 'A', OFTString, 25, 54, 30, 1, 1, 1 } +}; +static const TigerRecordInfo rt7_info = + { + rt7_fields, + sizeof(rt7_fields) / sizeof(TigerFieldInfo), + 74 + }; + +/************************************************************************/ +/* TigerLandmarks() */ +/************************************************************************/ + +TigerLandmarks::TigerLandmarks( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) + : TigerPoint(FALSE, NULL, FILE_CODE) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "Landmarks" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint ); + + if (poDS->GetVersion() >= TIGER_2002) { + psRTInfo = &rt7_2002_info; + } else { + psRTInfo = &rt7_info; + } + + AddFieldDefns( psRTInfo, poFeatureDefn ); +} + +OGRFeature *TigerLandmarks::GetFeature( int nRecordId ) +{ + return TigerPoint::GetFeature( nRecordId, + 55, 64, + 65, 73 ); +} + +OGRErr TigerLandmarks::CreateFeature( OGRFeature *poFeature ) +{ + return TigerPoint::CreateFeature( poFeature, + 55 ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigeroverunder.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigeroverunder.cpp new file mode 100644 index 000000000..62c109ce4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigeroverunder.cpp @@ -0,0 +1,86 @@ +/****************************************************************************** + * $Id: tigeroverunder.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerOverUnder, providing access to .RTU files. + * Author: Mark Phillips, mbp@geomtech.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam, Mark Phillips + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigeroverunder.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "U" + +static const TigerFieldInfo rtU_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "TZID", 'R', 'N', OFTInteger, 11, 20, 10, 1, 1, 1 }, + { "RTSQ", 'R', 'N', OFTInteger, 21, 21, 1, 1, 1, 1 }, + { "TLIDOV1", 'R', 'N', OFTInteger, 22, 31, 10, 1, 1, 1 }, + { "TLIDOV2", 'R', 'N', OFTInteger, 32, 41, 10, 1, 1, 1 }, + { "TLIDUN1", 'R', 'N', OFTInteger, 42, 51, 10, 1, 1, 1 }, + { "TLIDUN2", 'R', 'N', OFTInteger, 52, 61, 10, 1, 1, 1 }, + { "FRLONG", 'R', 'N', OFTInteger, 62, 71, 10, 1, 1, 1 }, + { "FRLAT", 'R', 'N', OFTInteger, 72, 80, 9, 1, 1, 1 }, +}; +static const TigerRecordInfo rtU_info = + { + rtU_fields, + sizeof(rtU_fields) / sizeof(TigerFieldInfo), + 80 + }; + + +/************************************************************************/ +/* TigerOverUnder() */ +/************************************************************************/ + +TigerOverUnder::TigerOverUnder( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerPoint(TRUE, &rtU_info, FILE_CODE) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "OverUnder" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + AddFieldDefns( psRTInfo, poFeatureDefn ); + +} + +OGRFeature *TigerOverUnder::GetFeature( int nRecordId ) +{ + return TigerPoint::GetFeature( nRecordId, + 62, 71, + 72, 80 ); +} + +OGRErr TigerOverUnder::CreateFeature( OGRFeature *poFeature ) +{ + return TigerPoint::CreateFeature( poFeature, + 62 ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpip.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpip.cpp new file mode 100644 index 000000000..9ab831275 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpip.cpp @@ -0,0 +1,103 @@ +/****************************************************************************** + * $Id: tigerpip.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerPIP, providing access to .RTP files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerpip.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "P" + +static const TigerFieldInfo rtP_2002_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 }, + { "POLYLONG", 'R', 'N', OFTInteger, 26, 35, 10, 1, 1, 1 }, + { "POLYLAT", 'R', 'N', OFTInteger, 36, 44, 9, 1, 1, 1 }, + { "WATER", 'L', 'N', OFTInteger, 45, 45, 1, 1, 1, 1 }, +}; +static const TigerRecordInfo rtP_2002_info = + { + rtP_2002_fields, + sizeof(rtP_2002_fields) / sizeof(TigerFieldInfo), + 45 + }; + +static const TigerFieldInfo rtP_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTString, 6, 10, 5, 1, 1, 1 }, + { "STATE", 'L', 'N', OFTInteger, 6, 7, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 8, 10, 3, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 } +}; +static const TigerRecordInfo rtP_info = + { + rtP_fields, + sizeof(rtP_fields) / sizeof(TigerFieldInfo), + 44 + }; + + +/************************************************************************/ +/* TigerPIP() */ +/************************************************************************/ + +TigerPIP::TigerPIP( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) + : TigerPoint(TRUE, NULL, FILE_CODE) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "PIP" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbPoint ); + + if (poDS->GetVersion() >= TIGER_2002) { + psRTInfo = &rtP_2002_info; + } else { + psRTInfo = &rtP_info; + } + AddFieldDefns( psRTInfo, poFeatureDefn ); +} + +OGRFeature *TigerPIP::GetFeature( int nRecordId ) +{ + return TigerPoint::GetFeature( nRecordId, + 26, 35, + 36, 44 ); +} + +OGRErr TigerPIP::CreateFeature( OGRFeature *poFeature ) +{ + return TigerPoint::CreateFeature( poFeature, + 26 ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpoint.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpoint.cpp new file mode 100644 index 000000000..dcede73d6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpoint.cpp @@ -0,0 +1,135 @@ +/****************************************************************************** + * $Id: tigerpoint.cpp 23871 2012-02-02 03:24:07Z warmerdam $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerPoint class. + * Author: Mark Phillips, mbp@geomtech.com + * + ****************************************************************************** + * Copyright (c) 2002, Mark Phillips + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerpoint.cpp 23871 2012-02-02 03:24:07Z warmerdam $"); + +/************************************************************************/ +/* TigerPoint() */ +/************************************************************************/ +TigerPoint::TigerPoint( int bRequireGeom, const TigerRecordInfo *psRTInfoIn, + const char *m_pszFileCodeIn ) : TigerFileBase(psRTInfoIn, m_pszFileCodeIn) +{ + this->bRequireGeom = bRequireGeom; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ +OGRFeature *TigerPoint::GetFeature( int nRecordId, + int nX0, int nX1, + int nY0, int nY1 ) +{ + char achRecord[OGR_TIGER_RECBUF_LEN]; + + if( nRecordId < 0 || nRecordId >= nFeatures ) { + CPLError( CE_Failure, CPLE_FileIO, + "Request for out-of-range feature %d of %sP", + nRecordId, pszModule ); + return NULL; + } + + /* -------------------------------------------------------------------- */ + /* Read the raw record data from the file. */ + /* -------------------------------------------------------------------- */ + + if( fpPrimary == NULL ) + return NULL; + + if( VSIFSeekL( fpPrimary, nRecordId * nRecordLength, SEEK_SET ) != 0 ) { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to %d of %sP", + nRecordId * nRecordLength, pszModule ); + return NULL; + } + + if( VSIFReadL( achRecord, psRTInfo->nRecordLength, 1, fpPrimary ) != 1 ) { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read record %d of %sP", + nRecordId, pszModule ); + return NULL; + } + + /* -------------------------------------------------------------------- */ + /* Set fields. */ + /* -------------------------------------------------------------------- */ + + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + SetFields( psRTInfo, poFeature, achRecord); + + /* -------------------------------------------------------------------- */ + /* Set geometry */ + /* -------------------------------------------------------------------- */ + + double dfX, dfY; + + dfX = atoi(GetField(achRecord, nX0, nX1)) / 1000000.0; + dfY = atoi(GetField(achRecord, nY0, nY1)) / 1000000.0; + + if( dfX != 0.0 || dfY != 0.0 ) { + poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY ) ); + } + + return poFeature; +} + +/************************************************************************/ +/* CreateFeature() */ +/************************************************************************/ +OGRErr TigerPoint::CreateFeature( OGRFeature *poFeature, + int pointIndex) + +{ + char szRecord[OGR_TIGER_RECBUF_LEN]; + OGRPoint *poPoint = (OGRPoint *) poFeature->GetGeometryRef(); + + if( !SetWriteModule( m_pszFileCode, psRTInfo->nRecordLength+2, poFeature ) ) + return OGRERR_FAILURE; + + memset( szRecord, ' ', psRTInfo->nRecordLength ); + + WriteFields( psRTInfo, poFeature, szRecord ); + + if( poPoint != NULL + && (poPoint->getGeometryType() == wkbPoint + || poPoint->getGeometryType() == wkbPoint25D) ) { + WritePoint( szRecord, pointIndex, poPoint->getX(), poPoint->getY() ); + } else { + if (bRequireGeom) { + return OGRERR_FAILURE; + } + } + + WriteRecord( szRecord, psRTInfo->nRecordLength, m_pszFileCode ); + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpolychainlink.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpolychainlink.cpp new file mode 100644 index 000000000..49920257a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpolychainlink.cpp @@ -0,0 +1,108 @@ +/****************************************************************************** + * $Id: tigerpolychainlink.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerPolyChainLink, providing access to .RTI files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerpolychainlink.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "I" + +static const TigerFieldInfo rtI_2002_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "TLID", 'R', 'N', OFTInteger, 11, 20, 10, 1, 1, 1 }, + { "TZIDS", 'R', 'N', OFTInteger, 21, 30, 10, 1, 1, 1 }, + { "TZIDE", 'R', 'N', OFTInteger, 31, 40, 10, 1, 1, 1 }, + { "CENIDL", 'L', 'A', OFTString, 41, 45, 5, 1, 1, 1 }, + { "POLYIDL", 'R', 'N', OFTInteger, 46, 55, 10, 1, 1, 1 }, + { "CENIDR", 'L', 'A', OFTString, 56, 60, 5, 1, 1, 1 }, + { "POLYIDR", 'R', 'N', OFTInteger, 61, 70, 10, 1, 1, 1 }, + { "SOURCE", 'L', 'A', OFTString, 71, 80, 10, 1, 1, 1 }, + { "FTSEG", 'L', 'A', OFTString, 81, 97, 17, 1, 1, 1 }, + { "RS_I1", 'L', 'A', OFTString, 98, 107, 10, 1, 1, 1 }, + { "RS_I2", 'L', 'A', OFTString, 108, 117, 10, 1, 1, 1 }, + { "RS_I3", 'L', 'A', OFTString, 118, 127, 10, 1, 1, 1 }, +}; +static const TigerRecordInfo rtI_2002_info = + { + rtI_2002_fields, + sizeof(rtI_2002_fields) / sizeof(TigerFieldInfo), + 127 + }; + +static const TigerFieldInfo rtI_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "TLID", 'R', 'N', OFTInteger, 6, 15, 10, 1, 1, 1 }, + { "FILE", 'L', 'N', OFTString, 16, 20, 5, 1, 1, 1 }, + { "STATE", 'L', 'N', OFTInteger, 16, 17, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 18, 20, 3, 1, 1, 1 }, + { "RTLINK", 'L', 'A', OFTString, 21, 21, 1, 1, 1, 1 }, + { "CENIDL", 'L', 'A', OFTString, 22, 26, 5, 1, 1, 1 }, + { "POLYIDL", 'R', 'N', OFTInteger, 27, 36, 10, 1, 1, 1 }, + { "CENIDR", 'L', 'A', OFTString, 37, 41, 5, 1, 1, 1 }, + { "POLYIDR", 'R', 'N', OFTInteger, 42, 51, 10, 1, 1, 1 } +}; +static const TigerRecordInfo rtI_info = + { + rtI_fields, + sizeof(rtI_fields) / sizeof(TigerFieldInfo), + 52 + }; + + +/************************************************************************/ +/* TigerPolyChainLink() */ +/************************************************************************/ + +TigerPolyChainLink::TigerPolyChainLink( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(NULL, FILE_CODE) +{ + OGRFieldDefn oField("",OFTInteger); + + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "PolyChainLink" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + if (poDS->GetVersion() >= TIGER_2002) { + psRTInfo = &rtI_2002_info; + } else { + psRTInfo = &rtI_info; + } + + /* -------------------------------------------------------------------- */ + /* Fields from type I record. */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns( psRTInfo, poFeatureDefn ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpolygon.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpolygon.cpp new file mode 100644 index 000000000..fcd3905c1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpolygon.cpp @@ -0,0 +1,629 @@ +/****************************************************************************** + * $Id: tigerpolygon.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerPolygon, providing access to .RTA files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerpolygon.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +static const TigerFieldInfo rtA_2002_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 }, + { "STATECU", 'L', 'N', OFTInteger, 26, 27, 2, 1, 1, 1 }, + { "COUNTYCU", 'L', 'N', OFTInteger, 28, 30, 3, 1, 1, 1 }, + + { "TRACT", 'L', 'N', OFTInteger, 31, 36, 6, 1, 1, 1 }, + { "BLOCK", 'L', 'N', OFTInteger, 37, 40, 4, 1, 1, 1 }, + { "BLOCKSUFCU", 'L', 'A', OFTString, 41, 41, 1, 1, 1, 1 }, + + { "RS_A1", 'L', 'A', OFTString, 42, 42, 1, 1, 1, 1 }, + { "AIANHHFPCU", 'L', 'N', OFTInteger, 43, 47, 5, 1, 1, 1 }, + { "AIANHHCU", 'L', 'N', OFTInteger, 48, 51, 4, 1, 1, 1 }, + { "AIHHTLICU", 'L', 'A', OFTString, 52, 52, 1, 1, 1, 1 }, + { "ANRCCU", 'L', 'N', OFTInteger, 53, 57, 5, 1, 1, 1 }, + { "AITSCECU", 'L', 'N', OFTInteger, 58, 60, 3, 1, 1, 1 }, + { "AITSCU", 'L', 'N', OFTInteger, 61, 65, 5, 1, 1, 1 }, + { "CONCITCU", 'L', 'N', OFTInteger, 66, 70, 5, 1, 1, 1 }, + { "COUSUBCU", 'L', 'N', OFTInteger, 71, 75, 5, 1, 1, 1 }, + { "SUBMCDCU", 'L', 'N', OFTInteger, 76, 80, 5, 1, 1, 1 }, + { "PLACECU", 'L', 'N', OFTInteger, 81, 85, 5, 1, 1, 1 }, + { "SDELMCU", 'L', 'A', OFTString, 86, 90, 5, 1, 1, 1 }, + { "SDSECCU", 'L', 'A', OFTString, 91, 95, 5, 1, 1, 1 }, + { "SDUNICU", 'L', 'A', OFTString, 96, 100, 5, 1, 1, 1 }, + { "MSACMSACU", 'L', 'N', OFTInteger, 101, 104, 4, 1, 1, 1 }, + { "PMSACU", 'L', 'N', OFTInteger, 105, 108, 4, 1, 1, 1 }, + { "NECMACU", 'L', 'N', OFTInteger, 109, 112, 4, 1, 1, 1 }, + { "CDCU", 'R', 'N', OFTInteger, 113, 114, 2, 1, 1, 1 }, + { "RS_A2", 'L', 'A', OFTString, 115, 119, 5, 1, 1, 1 }, + { "RS_A3", 'R', 'A', OFTString, 120, 122, 3, 1, 1, 1 }, + { "RS_A4", 'R', 'A', OFTString, 123, 128, 6, 1, 1, 1 }, + { "RS_A5", 'R', 'A', OFTString, 129, 131, 3, 1, 1, 1 }, + { "RS_A6", 'R', 'A', OFTString, 132, 134, 3, 1, 1, 1 }, + { "RS_A7", 'R', 'A', OFTString, 135, 139, 5, 1, 1, 1 }, + { "RS_A8", 'R', 'A', OFTString, 140, 145, 6, 1, 1, 1 }, + { "RS_A9", 'L', 'A', OFTString, 146, 151, 6, 1, 1, 1 }, + { "RS_A10", 'L', 'A', OFTString, 152, 157, 6, 1, 1, 1 }, + { "RS_A11", 'L', 'A', OFTString, 158, 163, 6, 1, 1, 1 }, + { "RS_A12", 'L', 'A', OFTString, 164, 169, 6, 1, 1, 1 }, + { "RS_A13", 'L', 'A', OFTString, 170, 175, 6, 1, 1, 1 }, + { "RS_A14", 'L', 'A', OFTString, 176, 181, 6, 1, 1, 1 }, + { "RS_A15", 'L', 'A', OFTString, 182, 186, 5, 1, 1, 1 }, + { "RS_A16", 'L', 'A', OFTString, 187, 187, 1, 1, 1, 1 }, + { "RS_A17", 'L', 'A', OFTString, 188, 193, 6, 1, 1, 1 }, + { "RS_A18", 'L', 'A', OFTString, 194, 199, 6, 1, 1, 1 }, + { "RS_A19", 'L', 'A', OFTString, 200, 210, 11, 1, 1, 1 }, +}; +static const TigerRecordInfo rtA_2002_info = + { + rtA_2002_fields, + sizeof(rtA_2002_fields) / sizeof(TigerFieldInfo), + 210 + }; + + +static const TigerFieldInfo rtA_2003_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 }, + { "STATECU", 'L', 'N', OFTInteger, 26, 27, 2, 1, 1, 1 }, + { "COUNTYCU", 'L', 'N', OFTInteger, 28, 30, 3, 1, 1, 1 }, + + { "TRACT", 'L', 'N', OFTInteger, 31, 36, 6, 1, 1, 1 }, + { "BLOCK", 'L', 'N', OFTInteger, 37, 40, 4, 1, 1, 1 }, + { "BLOCKSUFCU", 'L', 'A', OFTString, 41, 41, 1, 1, 1, 1 }, + + { "RS_A1", 'L', 'A', OFTString, 42, 42, 1, 1, 1, 1 }, + { "AIANHHFPCU", 'L', 'N', OFTInteger, 43, 47, 5, 1, 1, 1 }, + { "AIANHHCU", 'L', 'N', OFTInteger, 48, 51, 4, 1, 1, 1 }, + { "AIHHTLICU", 'L', 'A', OFTString, 52, 52, 1, 1, 1, 1 }, + { "ANRCCU", 'L', 'N', OFTInteger, 53, 57, 5, 1, 1, 1 }, + { "AITSCECU", 'L', 'N', OFTInteger, 58, 60, 3, 1, 1, 1 }, + { "AITSCU", 'L', 'N', OFTInteger, 61, 65, 5, 1, 1, 1 }, + { "CONCITCU", 'L', 'N', OFTInteger, 66, 70, 5, 1, 1, 1 }, + { "COUSUBCU", 'L', 'N', OFTInteger, 71, 75, 5, 1, 1, 1 }, + { "SUBMCDCU", 'L', 'N', OFTInteger, 76, 80, 5, 1, 1, 1 }, + { "PLACECU", 'L', 'N', OFTInteger, 81, 85, 5, 1, 1, 1 }, + { "SDELMCU", 'L', 'A', OFTString, 86, 90, 5, 1, 1, 1 }, + { "SDSECCU", 'L', 'A', OFTString, 91, 95, 5, 1, 1, 1 }, + { "SDUNICU", 'L', 'A', OFTString, 96, 100, 5, 1, 1, 1 }, + { "RS_A20", 'L', 'A', OFTString, 101, 104, 4, 1, 1, 1 }, + { "RS_A21", 'L', 'A', OFTString, 105, 108, 4, 1, 1, 1 }, + { "RS_A22", 'L', 'A', OFTString, 109, 112, 4, 1, 1, 1 }, + { "CDCU", 'R', 'N', OFTInteger, 113, 114, 2, 1, 1, 1 }, + { "ZCTA5CU", 'L', 'A', OFTString, 115, 119, 5, 1, 1, 1 }, + { "ZCTA3CU", 'R', 'A', OFTString, 120, 122, 3, 1, 1, 1 }, + { "RS_A4", 'R', 'A', OFTString, 123, 128, 6, 1, 1, 1 }, + { "RS_A5", 'R', 'A', OFTString, 129, 131, 3, 1, 1, 1 }, + { "RS_A6", 'R', 'A', OFTString, 132, 134, 3, 1, 1, 1 }, + { "RS_A7", 'R', 'A', OFTString, 135, 139, 5, 1, 1, 1 }, + { "RS_A8", 'R', 'A', OFTString, 140, 145, 6, 1, 1, 1 }, + { "RS_A9", 'L', 'A', OFTString, 146, 151, 6, 1, 1, 1 }, + { "CBSACU", 'L', 'A', OFTInteger, 152, 156, 5, 1, 1, 1 }, + { "CSACU", 'L', 'A', OFTInteger, 157, 159, 3, 1, 1, 1 }, + { "NECTACU", 'L', 'A', OFTInteger, 160, 164, 5, 1, 1, 1 }, + { "CNECTACU", 'L', 'A', OFTInteger, 165, 167, 3, 1, 1, 1 }, + { "METDIVCU", 'L', 'A', OFTInteger, 168, 172, 5, 1, 1, 1 }, + { "NECTADIVCU", 'L', 'A', OFTInteger, 173, 177, 5, 1, 1, 1 }, + { "RS_A14", 'L', 'A', OFTString, 178, 181, 4, 1, 1, 1 }, + { "RS_A15", 'L', 'A', OFTString, 182, 186, 5, 1, 1, 1 }, + { "RS_A16", 'L', 'A', OFTString, 187, 187, 1, 1, 1, 1 }, + { "RS_A17", 'L', 'A', OFTString, 188, 193, 6, 1, 1, 1 }, + { "RS_A18", 'L', 'A', OFTString, 194, 199, 6, 1, 1, 1 }, + { "RS_A19", 'L', 'A', OFTString, 200, 210, 11, 1, 1, 1 }, +}; +static const TigerRecordInfo rtA_2003_info = + { + rtA_2003_fields, + sizeof(rtA_2003_fields) / sizeof(TigerFieldInfo), + 210 + }; + + +static const TigerFieldInfo rtA_2004_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 }, + { "STATECU", 'L', 'N', OFTInteger, 26, 27, 2, 1, 1, 1 }, + { "COUNTYCU", 'L', 'N', OFTInteger, 28, 30, 3, 1, 1, 1 }, + + { "TRACT", 'L', 'N', OFTInteger, 31, 36, 6, 1, 1, 1 }, + { "BLOCK", 'L', 'N', OFTInteger, 37, 40, 4, 1, 1, 1 }, + { "BLOCKSUFCU", 'L', 'A', OFTString, 41, 41, 1, 1, 1, 1 }, + + { "RS_A1", 'L', 'A', OFTString, 42, 42, 1, 1, 1, 1 }, + { "AIANHHFPCU", 'L', 'N', OFTInteger, 43, 47, 5, 1, 1, 1 }, + { "AIANHHCU", 'L', 'N', OFTInteger, 48, 51, 4, 1, 1, 1 }, + { "AIHHTLICU", 'L', 'A', OFTString, 52, 52, 1, 1, 1, 1 }, + { "ANRCCU", 'L', 'N', OFTInteger, 53, 57, 5, 1, 1, 1 }, + { "AITSCECU", 'L', 'N', OFTInteger, 58, 60, 3, 1, 1, 1 }, + { "AITSCU", 'L', 'N', OFTInteger, 61, 65, 5, 1, 1, 1 }, + { "CONCITCU", 'L', 'N', OFTInteger, 66, 70, 5, 1, 1, 1 }, + { "COUSUBCU", 'L', 'N', OFTInteger, 71, 75, 5, 1, 1, 1 }, + { "SUBMCDCU", 'L', 'N', OFTInteger, 76, 80, 5, 1, 1, 1 }, + { "PLACECU", 'L', 'N', OFTInteger, 81, 85, 5, 1, 1, 1 }, + { "SDELMCU", 'L', 'A', OFTString, 86, 90, 5, 1, 1, 1 }, + { "SDSECCU", 'L', 'A', OFTString, 91, 95, 5, 1, 1, 1 }, + { "SDUNICU", 'L', 'A', OFTString, 96, 100, 5, 1, 1, 1 }, + { "RS_A20", 'L', 'A', OFTString, 101, 104, 4, 1, 1, 1 }, + { "RS_A21", 'L', 'A', OFTString, 105, 108, 4, 1, 1, 1 }, + { "RS_A22", 'L', 'A', OFTString, 109, 112, 4, 1, 1, 1 }, + { "CDCU", 'R', 'N', OFTInteger, 113, 114, 2, 1, 1, 1 }, + { "ZCTA5CU", 'L', 'A', OFTString, 115, 119, 5, 1, 1, 1 }, + { "ZCTA3CU", 'R', 'A', OFTString, 120, 122, 3, 1, 1, 1 }, + { "RS_A4", 'R', 'A', OFTString, 123, 128, 6, 1, 1, 1 }, + { "RS_A5", 'R', 'A', OFTString, 129, 131, 3, 1, 1, 1 }, + { "RS_A6", 'R', 'A', OFTString, 132, 134, 3, 1, 1, 1 }, + { "RS_A7", 'R', 'A', OFTString, 135, 139, 5, 1, 1, 1 }, + { "RS_A8", 'R', 'A', OFTString, 140, 145, 6, 1, 1, 1 }, + { "RS_A9", 'L', 'A', OFTString, 146, 151, 6, 1, 1, 1 }, + { "CBSACU", 'L', 'A', OFTInteger, 152, 156, 5, 1, 1, 1 }, + { "CSACU", 'L', 'A', OFTInteger, 157, 159, 3, 1, 1, 1 }, + { "NECTACU", 'L', 'A', OFTInteger, 160, 164, 5, 1, 1, 1 }, + { "CNECTACU", 'L', 'A', OFTInteger, 165, 167, 3, 1, 1, 1 }, + { "METDIVCU", 'L', 'A', OFTInteger, 168, 172, 5, 1, 1, 1 }, + { "NECTADIVCU", 'L', 'A', OFTInteger, 173, 177, 5, 1, 1, 1 }, + { "RS_A14", 'L', 'A', OFTString, 178, 181, 4, 1, 1, 1 }, + { "UACU", 'L', 'N', OFTInteger, 182, 186, 5, 1, 1, 1 }, + { "URCU", 'L', 'A', OFTString, 187, 187, 1, 1, 1, 1 }, + { "RS_A17", 'L', 'A', OFTString, 188, 193, 6, 1, 1, 1 }, + { "RS_A18", 'L', 'A', OFTString, 194, 199, 6, 1, 1, 1 }, + { "RS_A19", 'L', 'A', OFTString, 200, 210, 11, 1, 1, 1 }, +}; +static const TigerRecordInfo rtA_2004_info = + { + rtA_2004_fields, + sizeof(rtA_2004_fields) / sizeof(TigerFieldInfo), + 210 + }; + + +static const TigerFieldInfo rtA_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTString, 6, 10, 5, 1, 1, 1 }, + { "STATE", 'L', 'N', OFTInteger, 6, 7, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 8, 10, 3, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 }, + { "FAIR", 'L', 'N', OFTInteger, 26, 30, 5, 1, 1, 1 }, + { "FMCD", 'L', 'N', OFTInteger, 31, 35, 5, 1, 1, 1 }, + { "FPL", 'L', 'N', OFTInteger, 36, 40, 5, 1, 1, 1 }, + { "CTBNA90", 'L', 'N', OFTInteger, 41, 46, 6, 1, 1, 1 }, + { "BLK90", 'L', 'A', OFTString, 47, 50, 4, 1, 1, 1 }, + { "CD106", 'L', 'N', OFTInteger, 51, 52, 2, 1, 1, 1 }, + { "CD108", 'L', 'N', OFTInteger, 53, 54, 2, 1, 1, 1 }, + { "SDELM", 'L', 'A', OFTString, 55, 59, 5, 1, 1, 1 }, + { "SDSEC", 'L', 'N', OFTString, 65, 69, 5, 1, 1, 1 }, + { "SDUNI", 'L', 'A', OFTString, 70, 74, 5, 1, 1, 1 }, + { "TAZ", 'R', 'A', OFTString, 75, 80, 6, 1, 1, 1 }, + { "UA", 'L', 'N', OFTInteger, 81, 84, 4, 1, 1, 1 }, + { "URBFLAG", 'L', 'A', OFTString, 85, 85, 1, 1, 1, 1 }, + { "CTPP", 'L', 'A', OFTString, 86, 89, 4, 1, 1, 1 }, + { "STATE90", 'L', 'N', OFTInteger, 90, 91, 2, 1, 1, 1 }, + { "COUN90", 'L', 'N', OFTInteger, 92, 94, 3, 1, 1, 1 }, + { "AIR90", 'L', 'N', OFTInteger, 95, 98, 4, 1, 1, 1 } +}; + +static const TigerRecordInfo rtA_info = + { + rtA_fields, + sizeof(rtA_fields) / sizeof(TigerFieldInfo), + 98 + }; + + +static const TigerFieldInfo rtS_2002_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 0, 0, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 0, 0, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 0, 0, 1 }, + { "STATE", 'L', 'N', OFTInteger, 26, 27, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 28, 30, 3, 1, 1, 1 }, + { "TRACT", 'L', 'N', OFTInteger, 31, 36, 6, 0, 0, 1 }, + { "BLOCK", 'L', 'N', OFTInteger, 37, 40, 4, 0, 0, 1 }, + { "BLKGRP", 'L', 'N', OFTInteger, 41, 41, 1, 1, 1, 1 }, + { "AIANHHFP", 'L', 'N', OFTInteger, 42, 46, 5, 1, 1, 1 }, + { "AIANHH", 'L', 'N', OFTInteger, 47, 50, 4, 1, 1, 1 }, + { "AIHHTLI", 'L', 'A', OFTString, 51, 51, 1, 1, 1, 1 }, + { "ANRC", 'L', 'N', OFTInteger, 52, 56, 5, 1, 1, 1 }, + { "AITSCE", 'L', 'N', OFTInteger, 57, 59, 3, 1, 1, 1 }, + { "AITS", 'L', 'N', OFTInteger, 60, 64, 5, 1, 1, 1 }, + { "CONCIT", 'L', 'N', OFTInteger, 65, 69, 5, 1, 1, 1 }, + { "COUSUB", 'L', 'N', OFTInteger, 70, 74, 5, 1, 1, 1 }, + { "SUBMCD", 'L', 'N', OFTInteger, 75, 79, 5, 1, 1, 1 }, + { "PLACE", 'L', 'N', OFTInteger, 80, 84, 5, 1, 1, 1 }, + { "SDELM", 'L', 'N', OFTInteger, 85, 89, 5, 1, 1, 1 }, + { "SDSEC", 'L', 'N', OFTInteger, 90, 94, 5, 1, 1, 1 }, + { "SDUNI", 'L', 'N', OFTInteger, 95, 99, 5, 1, 1, 1 }, + { "MSACMSA", 'L', 'N', OFTInteger, 100, 103, 4, 1, 1, 1 }, + { "PMSA", 'L', 'N', OFTInteger, 104, 107, 4, 1, 1, 1 }, + { "NECMA", 'L', 'N', OFTInteger, 108, 111, 4, 1, 1, 1 }, + { "CD106", 'L', 'N', OFTInteger, 112, 113, 2, 1, 1, 1 }, + // Note: spec has CD106 with 'R', but sample data file (08005) seems to + // have been written with 'L', so I'm using 'L' here. mbp Tue Dec 24 19:03:40 2002 + { "CD108", 'R', 'N', OFTInteger, 114, 115, 2, 1, 1, 1 }, + { "PUMA5", 'L', 'N', OFTInteger, 116, 120, 5, 1, 1, 1 }, + { "PUMA1", 'L', 'N', OFTInteger, 121, 125, 5, 1, 1, 1 }, + { "ZCTA5", 'L', 'A', OFTString, 126, 130, 5, 1, 1, 1 }, + { "ZCTA3", 'L', 'A', OFTString, 131, 133, 3, 1, 1, 1 }, + { "TAZ", 'L', 'A', OFTString, 134, 139, 6, 1, 1, 1 }, + { "TAZCOMB", 'L', 'A', OFTString, 140, 145, 6, 1, 1, 1 }, + { "UA", 'L', 'N', OFTInteger, 146, 150, 5, 1, 1, 1 }, + { "UR", 'L', 'A', OFTString, 151, 151, 1, 1, 1, 1 }, + { "VTD", 'R', 'A', OFTString, 152, 157, 6, 1, 1, 1 }, + { "SLDU", 'R', 'A', OFTString, 158, 160, 3, 1, 1, 1 }, + { "SLDL", 'R', 'A', OFTString, 161, 163, 3, 1, 1, 1 }, + { "UGA", 'L', 'A', OFTString, 164, 168, 5, 1, 1, 1 }, +}; +static const TigerRecordInfo rtS_2002_info = + { + rtS_2002_fields, + sizeof(rtS_2002_fields) / sizeof(TigerFieldInfo), + 168 + }; + + +static const TigerFieldInfo rtS_2000_Redistricting_fields[] = { + { "FILE", 'L', 'N', OFTString, 6, 10, 5, 0, 0, 1 }, + { "STATE", 'L', 'N', OFTInteger, 6, 7, 2, 0, 0, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 8, 10, 3, 0, 0, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 0, 0, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 0, 0, 1 }, + { "WATER", 'L', 'N', OFTString, 26, 26, 1, 1, 1, 1 }, + { "CMSAMSA", 'L', 'N', OFTInteger, 27, 30, 4, 1, 1, 1 }, + { "PMSA", 'L', 'N', OFTInteger, 31, 34, 4, 1, 1, 1 }, + { "AIANHH", 'L', 'N', OFTInteger, 35, 39, 5, 1, 1, 1 }, + { "AIR", 'L', 'N', OFTInteger, 40, 43, 4, 1, 1, 1 }, + { "TRUST", 'L', 'A', OFTString, 44, 44, 1, 1, 1, 1 }, + { "ANRC", 'L', 'A', OFTInteger, 45, 46, 2, 1, 1, 1 }, + { "STATECU", 'L', 'N', OFTInteger, 47, 48, 2, 1, 1, 1 }, + { "COUNTYCU", 'L', 'N', OFTInteger, 49, 51, 3, 1, 1, 1 }, + { "FCCITY", 'L', 'N', OFTInteger, 52, 56, 5, 1, 1, 1 }, + { "FMCD", 'L', 'N', OFTInteger, 57, 61, 5, 0, 0, 1 }, + { "FSMCD", 'L', 'N', OFTInteger, 62, 66, 5, 1, 1, 1 }, + { "PLACE", 'L', 'N', OFTInteger, 67, 71, 5, 1, 1, 1 }, + { "CTBNA00", 'L', 'N', OFTInteger, 72, 77, 6, 1, 1, 1 }, + { "BLK00", 'L', 'N', OFTString, 78, 81, 4, 1, 1, 1 }, + { "RS10", 'R', 'N', OFTInteger, 82, 82, 0, 0, 1, 1 }, + { "CDCU", 'L', 'N', OFTInteger, 83, 84, 2, 1, 1, 1 }, + + { "SLDU", 'R', 'A', OFTString, 85, 87, 3, 1, 1, 1 }, + { "SLDL", 'R', 'A', OFTString, 88, 90, 3, 1, 1, 1 }, + { "UGA", 'L', 'A', OFTString, 91, 95, 5, 1, 1, 1 }, + { "BLKGRP", 'L', 'N', OFTInteger, 96, 96, 1, 1, 1, 1 }, + { "VTD", 'R', 'A', OFTString, 97, 102, 6, 1, 1, 1 }, + { "STATECOL", 'L', 'N', OFTInteger, 103, 104, 2, 1, 1, 1 }, + { "COUNTYCOL", 'L', 'N', OFTInteger, 105, 107, 3, 1, 1, 1 }, + { "BLOCKCOL", 'R', 'N', OFTInteger, 108, 112, 5, 1, 1, 1 }, + { "BLKSUFCOL", 'L', 'A', OFTString, 113, 113, 1, 1, 1, 1 }, + { "ZCTA5", 'L', 'A', OFTString, 114, 118, 5, 1, 1, 1 } + +}; + +static const TigerRecordInfo rtS_2000_Redistricting_info = + { + rtS_2000_Redistricting_fields, + sizeof(rtS_2000_Redistricting_fields) / sizeof(TigerFieldInfo), + 120 + }; + +static const TigerFieldInfo rtS_fields[] = { + { "FILE", 'L', 'N', OFTString, 6, 10, 5, 0, 0, 1 }, + { "STATE", 'L', 'N', OFTInteger, 6, 7, 2, 0, 0, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 8, 10, 3, 0, 0, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 0, 0, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 0, 0, 1 }, + + { "WATER", 'L', 'N', OFTString, 26, 26, 1, 1, 1, 1 }, + { "CMSAMSA", 'L', 'N', OFTInteger, 27, 30, 4, 1, 1, 1 }, + { "PMSA", 'L', 'N', OFTInteger, 31, 34, 4, 1, 1, 1 }, + { "AIANHH", 'L', 'N', OFTInteger, 35, 39, 5, 1, 1, 1 }, + { "AIR", 'L', 'N', OFTInteger, 40, 43, 4, 1, 1, 1 }, + { "TRUST", 'L', 'A', OFTString, 44, 44, 1, 1, 1, 1 }, + { "ANRC", 'L', 'A', OFTInteger, 45, 46, 2, 1, 1, 1 }, + { "STATECU", 'L', 'N', OFTInteger, 47, 48, 2, 1, 1, 1 }, + { "COUNTYCU", 'L', 'N', OFTInteger, 49, 51, 3, 1, 1, 1 }, + { "FCCITY", 'L', 'N', OFTInteger, 52, 56, 5, 1, 1, 1 }, + { "FMCD", 'L', 'N', OFTInteger, 57, 61, 5, 0, 0, 1 }, + { "FSMCD", 'L', 'N', OFTInteger, 62, 66, 5, 1, 1, 1 }, + { "PLACE", 'L', 'N', OFTInteger, 67, 71, 5, 1, 1, 1 }, + { "CTBNA00", 'L', 'N', OFTInteger, 72, 77, 6, 1, 1, 1 }, + { "BLK00", 'L', 'N', OFTString, 78, 81, 4, 1, 1, 1 }, + { "RS10", 'R', 'N', OFTInteger, 82, 82, 0, 0, 1, 1 }, + { "CDCU", 'L', 'N', OFTInteger, 83, 84, 2, 1, 1, 1 }, + + { "STSENATE", 'L', 'A', OFTString, 85, 90, 6, 1, 1, 1 }, + { "STHOUSE", 'L', 'A', OFTString, 91, 96, 6, 1, 1, 1 }, + { "VTD00", 'L', 'A', OFTString, 97, 102, 6, 1, 1, 1 } +}; +static const TigerRecordInfo rtS_info = + { + rtS_fields, + sizeof(rtS_fields) / sizeof(TigerFieldInfo), + 120 + }; + +/************************************************************************/ +/* TigerPolygon() */ +/************************************************************************/ + +TigerPolygon::TigerPolygon( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "Polygon" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + fpRTS = NULL; + bUsingRTS = TRUE; + + if( poDS->GetVersion() >= TIGER_2004 ) { + psRTAInfo = &rtA_2004_info; + } else if( poDS->GetVersion() >= TIGER_2003 ) { + psRTAInfo = &rtA_2003_info; + } else if( poDS->GetVersion() >= TIGER_2002 ) { + psRTAInfo = &rtA_2002_info; + } else { + psRTAInfo = &rtA_info; + } + + if( poDS->GetVersion() >= TIGER_2002 ) { + psRTSInfo = &rtS_2002_info; + } else if( poDS->GetVersion() >= TIGER_2000_Redistricting ) { + psRTSInfo = &rtS_2000_Redistricting_info; + } else { + psRTSInfo = &rtS_info; + } + + /* -------------------------------------------------------------------- */ + /* Fields from type A record. */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns(psRTAInfo, poFeatureDefn); + + /* -------------------------------------------------------------------- */ + /* Add the RTS records if it is available. */ + /* -------------------------------------------------------------------- */ + + if( bUsingRTS ) { + AddFieldDefns(psRTSInfo, poFeatureDefn); + } +} + +/************************************************************************/ +/* ~TigerPolygon() */ +/************************************************************************/ + +TigerPolygon::~TigerPolygon() + +{ + if( fpRTS != NULL ) + VSIFCloseL( fpRTS ); +} + +/************************************************************************/ +/* SetModule() */ +/************************************************************************/ + +int TigerPolygon::SetModule( const char * pszModule ) + +{ + if( !OpenFile( pszModule, "A" ) ) + return FALSE; + + EstablishFeatureCount(); + +/* -------------------------------------------------------------------- */ +/* Open the RTS file */ +/* -------------------------------------------------------------------- */ + if( bUsingRTS ) + { + if( fpRTS != NULL ) + { + VSIFCloseL( fpRTS ); + fpRTS = NULL; + } + + if( pszModule ) + { + char *pszFilename; + + pszFilename = poDS->BuildFilename( pszModule, "S" ); + + fpRTS = VSIFOpenL( pszFilename, "rb" ); + + CPLFree( pszFilename ); + + nRTSRecLen = EstablishRecordLength( fpRTS ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *TigerPolygon::GetFeature( int nRecordId ) + +{ + char achRecord[OGR_TIGER_RECBUF_LEN]; + + if( nRecordId < 0 || nRecordId >= nFeatures ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Request for out-of-range feature %d of %sA", + nRecordId, pszModule ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the raw record data from the file. */ +/* -------------------------------------------------------------------- */ + if( fpPrimary == NULL ) + return NULL; + + if( VSIFSeekL( fpPrimary, nRecordId * nRecordLength, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to %d of %sA", + nRecordId * nRecordLength, pszModule ); + return NULL; + } + + if( VSIFReadL( achRecord, nRecordLength, 1, fpPrimary ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read record %d of %sA", + nRecordId, pszModule ); + return NULL; + } + + /* -------------------------------------------------------------------- */ + /* Set fields. */ + /* -------------------------------------------------------------------- */ + + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + SetFields( psRTAInfo, poFeature, achRecord ); + + /* -------------------------------------------------------------------- */ + /* Read RTS record, and apply fields. */ + /* -------------------------------------------------------------------- */ + + if( fpRTS != NULL ) + { + char achRTSRec[OGR_TIGER_RECBUF_LEN]; + + if( VSIFSeekL( fpRTS, nRecordId * nRTSRecLen, SEEK_SET ) != 0 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to seek to %d of %sS", + nRecordId * nRTSRecLen, pszModule ); + return NULL; + } + + if( VSIFReadL( achRTSRec, psRTSInfo->nRecordLength, 1, fpRTS ) != 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to read record %d of %sS", + nRecordId, pszModule ); + return NULL; + } + + SetFields( psRTSInfo, poFeature, achRTSRec ); + + } + + return poFeature; +} + +/************************************************************************/ +/* SetWriteModule() */ +/************************************************************************/ + +int TigerPolygon::SetWriteModule( const char *pszFileCode, int nRecLen, + OGRFeature *poFeature ) + +{ + int bSuccess; + + bSuccess = TigerFileBase::SetWriteModule( pszFileCode, nRecLen, poFeature); + if( !bSuccess ) + return bSuccess; + +/* -------------------------------------------------------------------- */ +/* Open the RT3 file */ +/* -------------------------------------------------------------------- */ + if( bUsingRTS ) + { + if( fpRTS != NULL ) + { + VSIFCloseL( fpRTS ); + fpRTS = NULL; + } + + if( pszModule ) + { + char *pszFilename; + + pszFilename = poDS->BuildFilename( pszModule, "S" ); + + fpRTS = VSIFOpenL( pszFilename, "ab" ); + + CPLFree( pszFilename ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* CreateFeature() */ +/************************************************************************/ + +OGRErr TigerPolygon::CreateFeature( OGRFeature *poFeature ) + +{ + char szRecord[OGR_TIGER_RECBUF_LEN]; + +/* -------------------------------------------------------------------- */ +/* Write basic data record ("RTA") */ +/* -------------------------------------------------------------------- */ + + if( !SetWriteModule( "A", psRTAInfo->nRecordLength+2, poFeature ) ) + return OGRERR_FAILURE; + + memset( szRecord, ' ', psRTAInfo->nRecordLength ); + + WriteFields( psRTAInfo, poFeature, szRecord ); + WriteRecord( szRecord, psRTAInfo->nRecordLength, "A" ); + +/* -------------------------------------------------------------------- */ +/* Prepare S record. */ +/* -------------------------------------------------------------------- */ + + memset( szRecord, ' ', psRTSInfo->nRecordLength ); + + WriteFields( psRTSInfo, poFeature, szRecord ); + WriteRecord( szRecord, psRTSInfo->nRecordLength, "S", fpRTS ); + + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpolygoncorrections.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpolygoncorrections.cpp new file mode 100644 index 000000000..ca8234145 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpolygoncorrections.cpp @@ -0,0 +1,88 @@ +/****************************************************************************** + * $Id: tigerpolygoncorrections.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerPolygonCorrections, providing access to .RTB files. + * Author: Mark Phillips, mbp@geomtech.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam, Mark Phillips + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerpolygoncorrections.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "B" + +static const TigerFieldInfo rtB_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 }, + { "STATECQ", 'L', 'N', OFTInteger, 26, 27, 2, 1, 1, 1 }, + { "COUNTYCQ", 'L', 'N', OFTInteger, 28, 30, 3, 1, 1, 1 }, + { "TRACTCQ", 'L', 'N', OFTInteger, 31, 36, 6, 1, 1, 1 }, + { "BLOCKCQ", 'L', 'A', OFTString, 37, 41, 5, 1, 1, 1 }, + { "AIANHHFPCQ", 'L', 'N', OFTInteger, 42, 46, 5, 1, 1, 1 }, + { "AIANHHCQ", 'L', 'N', OFTInteger, 47, 50, 4, 1, 1, 1 }, + { "AIHHTLICQ", 'L', 'A', OFTString, 51, 51, 1, 1, 1, 1 }, + { "AITSCECQ", 'L', 'N', OFTInteger, 52, 54, 3, 1, 1, 1 }, + { "AITSCQ", 'L', 'N', OFTInteger, 55, 59, 5, 1, 1, 1 }, + { "ANRCCQ", 'L', 'N', OFTInteger, 60, 64, 5, 1, 1, 1 }, + { "CONCITCQ", 'L', 'N', OFTInteger, 65, 69, 5, 1, 1, 1 }, + { "COUSUBCQ", 'L', 'N', OFTInteger, 70, 74, 5, 1, 1, 1 }, + { "SUBMCDCQ", 'L', 'N', OFTInteger, 75, 79, 5, 1, 1, 1 }, + { "PLACECQ", 'L', 'N', OFTInteger, 80, 84, 5, 1, 1, 1 }, + { "UACC", 'L', 'N', OFTInteger, 85, 89, 5, 1, 1, 1 }, + { "URCC", 'L', 'A', OFTString, 90, 90, 1, 1, 1, 1 }, + { "RS-B1", 'L', 'A', OFTString, 91, 98, 12, 1, 1, 1 }, +}; +static const TigerRecordInfo rtB_info = + { + rtB_fields, + sizeof(rtB_fields) / sizeof(TigerFieldInfo), + 98 + }; + +/************************************************************************/ +/* TigerPolygonCorrections() */ +/************************************************************************/ + +TigerPolygonCorrections::TigerPolygonCorrections( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(&rtB_info, FILE_CODE) +{ + OGRFieldDefn oField("",OFTInteger); + + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "PolygonCorrections" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + /* -------------------------------------------------------------------- */ + /* Fields from type B record. */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns( psRTInfo, poFeatureDefn ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpolygoneconomic.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpolygoneconomic.cpp new file mode 100644 index 000000000..c9e8fd142 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerpolygoneconomic.cpp @@ -0,0 +1,100 @@ +/****************************************************************************** + * $Id: tigerpolygoneconomic.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerPolygonEconomic, providing access to .RTE files. + * Author: Mark Phillips, mbp@geomtech.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam, Mark Phillips + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerpolygoneconomic.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "E" + +/* I think this was the expected RTE format, but was never deployed, leaving + it in the code in case I am missing something. + +static TigerFieldInfo rtE_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 }, + { "STATEEC", 'L', 'N', OFTInteger, 26, 27, 2, 1, 1, 1 }, + { "COUNTYEC", 'L', 'N', OFTInteger, 28, 30, 3, 1, 1, 1 }, + { "CONCITEC", 'L', 'N', OFTInteger, 31, 35, 5, 1, 1, 1 }, + { "COUSUBEC", 'L', 'N', OFTInteger, 36, 40, 5, 1, 1, 1 }, + { "PLACEEC", 'L', 'N', OFTInteger, 41, 45, 5, 1, 1, 1 }, + { "AIANHHFPEC", 'L', 'N', OFTInteger, 46, 50, 5, 1, 1, 1 }, + { "AIANHHEC", 'L', 'N', OFTInteger, 51, 54, 4, 1, 1, 1 }, + { "AIAHHTLIEC", 'L', 'A', OFTString, 55, 55, 1, 1, 1, 1 }, + { "RS_E1", 'L', 'A', OFTString, 56, 73, 18, 1, 1, 1 } +}; +*/ + +static const TigerFieldInfo rtE_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "POLYID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 }, + { "STATEEC", 'L', 'N', OFTInteger, 26, 27, 2, 1, 1, 1 }, + { "COUNTYEC", 'L', 'N', OFTInteger, 28, 30, 3, 1, 1, 1 }, + { "RS_E1", 'L', 'A', OFTString, 31, 35, 5, 1, 1, 1 }, + { "RS_E2", 'L', 'A', OFTString, 36, 40, 5, 1, 1, 1 }, + { "PLACEEC", 'L', 'N', OFTInteger, 41, 45, 5, 1, 1, 1 }, + { "RS-E3", 'L', 'A', OFTString, 46, 50, 5, 1, 1, 1 }, + { "RS-E4", 'L', 'A', OFTString, 51, 54, 4, 1, 1, 1 }, + { "RS-E5", 'L', 'A', OFTString, 55, 55, 1, 1, 1, 1 }, + { "COMMREGEC", 'L', 'N', OFTInteger, 56, 56, 1, 1, 1, 1 }, + { "RS_E6", 'L', 'A', OFTString, 57, 73, 17, 1, 1, 1 } +}; +static const TigerRecordInfo rtE_info = + { + rtE_fields, + sizeof(rtE_fields) / sizeof(TigerFieldInfo), + 73 + }; + +/************************************************************************/ +/* TigerPolygonEconomic() */ +/************************************************************************/ + +TigerPolygonEconomic::TigerPolygonEconomic( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(&rtE_info, FILE_CODE) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "PolygonEconomic" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + /* -------------------------------------------------------------------- */ + /* Fields from type E record. */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns( psRTInfo, poFeatureDefn ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerspatialmetadata.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerspatialmetadata.cpp new file mode 100644 index 000000000..d917c5274 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerspatialmetadata.cpp @@ -0,0 +1,75 @@ +/****************************************************************************** + * $Id: tigerspatialmetadata.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerSpatialMetadata, providing access to .RTM files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerspatialmetadata.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "M" + +static const TigerFieldInfo rtM_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "TLID", 'R', 'N', OFTInteger, 6, 15, 10, 1, 1, 1 }, + { "RTSQ", 'R', 'N', OFTInteger, 16, 18, 3, 1, 1, 1 }, + { "SOURCEID", 'L', 'A', OFTString, 19, 28, 10, 1, 1, 1 }, + { "ID", 'L', 'A', OFTString, 29, 46, 18, 1, 1, 1 }, + { "IDFLAG", 'R', 'A', OFTString, 47, 47, 1, 1, 1, 1 }, + { "RS-M1", 'L', 'A', OFTString, 48, 65, 18, 1, 1, 1 }, + { "RS-M2", 'L', 'A', OFTString, 66, 67, 2, 1, 1, 1 }, + { "RS-M3", 'L', 'A', OFTString, 68, 90, 23, 1, 1, 1 } +}; +static const TigerRecordInfo rtM_info = + { + rtM_fields, + sizeof(rtM_fields) / sizeof(TigerFieldInfo), + 90 + }; + +/************************************************************************/ +/* TigerSpatialMetadata() */ +/************************************************************************/ + +TigerSpatialMetadata::TigerSpatialMetadata( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(&rtM_info, FILE_CODE) + +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "SpatialMetadata" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + /* -------------------------------------------------------------------- */ + /* Fields from record type H */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns(psRTInfo, poFeatureDefn); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigertlidrange.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigertlidrange.cpp new file mode 100644 index 000000000..86e74cdd8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigertlidrange.cpp @@ -0,0 +1,101 @@ +/****************************************************************************** + * $Id: tigertlidrange.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerTLIDRange, providing access to .RTR files. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigertlidrange.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "R" + +static const TigerFieldInfo rtR_2002_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "TLMAXID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 }, + { "TLMINID", 'R', 'N', OFTInteger, 26, 35, 10, 1, 1, 1 }, + { "TLIGHID", 'R', 'N', OFTInteger, 36, 45, 10, 1, 1, 1 }, + { "TZMAXID", 'R', 'N', OFTInteger, 46, 55, 10, 1, 1, 1 }, + { "TZMINID", 'R', 'N', OFTInteger, 56, 65, 10, 1, 1, 1 }, + { "TZHIGHID", 'R', 'N', OFTInteger, 66, 75, 10, 1, 1, 1 }, + { "FILLER", 'L', 'A', OFTString, 76, 76, 1, 1, 1, 1 }, +}; +static const TigerRecordInfo rtR_2002_info = + { + rtR_2002_fields, + sizeof(rtR_2002_fields) / sizeof(TigerFieldInfo), + 76 + }; + +static const TigerFieldInfo rtR_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTString, 6, 10, 5, 1, 1, 1 }, + { "STATE", 'L', 'N', OFTInteger, 6, 7, 2, 1, 1, 1 }, + { "COUNTY", 'L', 'N', OFTInteger, 8, 10, 3, 1, 1, 1 }, + { "CENID", 'L', 'A', OFTString, 11, 15, 5, 1, 1, 1 }, + { "MAXID", 'R', 'N', OFTInteger, 16, 25, 10, 1, 1, 1 }, + { "MINID", 'R', 'N', OFTInteger, 26, 35, 10, 1, 1, 1 }, + { "HIGHID", 'R', 'N', OFTInteger, 36, 45, 10, 1, 1, 1 } +}; + +static const TigerRecordInfo rtR_info = + { + rtR_fields, + sizeof(rtR_fields) / sizeof(TigerFieldInfo), + 46 + }; + +/************************************************************************/ +/* TigerTLIDRange() */ +/************************************************************************/ + +TigerTLIDRange::TigerTLIDRange( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(NULL, FILE_CODE) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "TLIDRange" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + if (poDS->GetVersion() >= TIGER_2002) { + psRTInfo = &rtR_2002_info; + } else { + psRTInfo = &rtR_info; + } + + /* -------------------------------------------------------------------- */ + /* Fields from type R record. */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns( psRTInfo, poFeatureDefn ); + +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerzerocellid.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerzerocellid.cpp new file mode 100644 index 000000000..a96d0557f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerzerocellid.cpp @@ -0,0 +1,71 @@ +/****************************************************************************** + * $Id: tigerzerocellid.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerZeroCellID, providing access to .RTT files. + * Author: Mark Phillips, mbp@geomtech.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam, Mark Phillips + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerzerocellid.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "T" + +static const TigerFieldInfo rtT_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "FILE", 'L', 'N', OFTInteger, 6, 10, 5, 1, 1, 1 }, + { "TZID", 'R', 'N', OFTInteger, 11, 20, 10, 1, 1, 1 }, + { "SOURCE", 'L', 'A', OFTString, 21, 30, 10, 1, 1, 1 }, + { "FTRP", 'L', 'A', OFTString, 31, 47, 17, 1, 1, 1 } +}; +static const TigerRecordInfo rtT_info = + { + rtT_fields, + sizeof(rtT_fields) / sizeof(TigerFieldInfo), + 47 + }; + +/************************************************************************/ +/* TigerZeroCellID() */ +/************************************************************************/ + +TigerZeroCellID::TigerZeroCellID( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(&rtT_info, FILE_CODE) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "ZeroCellID" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + +/* -------------------------------------------------------------------- */ +/* Fields from type T record. */ +/* -------------------------------------------------------------------- */ + + AddFieldDefns( psRTInfo, poFeatureDefn ); + +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerzipcodes.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerzipcodes.cpp new file mode 100644 index 000000000..face12a04 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerzipcodes.cpp @@ -0,0 +1,79 @@ +/****************************************************************************** + * $Id: tigerzipcodes.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerZipCodes, providing access to .RT6 files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerzipcodes.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "6" + +static const TigerFieldInfo rt6_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "TLID", 'R', 'N', OFTInteger, 6, 15, 10, 1, 1, 1 }, + { "RTSQ", 'R', 'N', OFTInteger, 16, 18, 3, 1, 1, 1 }, + { "FRADDL", 'R', 'A', OFTString, 19, 29, 11, 1, 1, 1 }, + { "TOADDL", 'R', 'A', OFTString, 30, 40, 11, 1, 1, 1 }, + { "FRADDR", 'R', 'A', OFTString, 41, 51, 11, 1, 1, 1 }, + { "TOADDR", 'R', 'A', OFTString, 52, 62, 11, 1, 1, 1 }, + { "FRIADDL", 'L', 'A', OFTInteger, 63, 63, 1, 1, 1, 1 }, + { "TOIADDL", 'L', 'A', OFTInteger, 64, 64, 1, 1, 1, 1 }, + { "FRIADDR", 'L', 'A', OFTInteger, 65, 65, 1, 1, 1, 1 }, + { "TOIADDR", 'L', 'A', OFTInteger, 66, 66, 1, 1, 1, 1 }, + { "ZIPL", 'L', 'N', OFTInteger, 67, 71, 5, 1, 1, 1 }, + { "ZIPR", 'L', 'N', OFTInteger, 72, 76, 5, 1, 1, 1 } +}; +static const TigerRecordInfo rt6_info = + { + rt6_fields, + sizeof(rt6_fields) / sizeof(TigerFieldInfo), + 76 + }; + +/************************************************************************/ +/* TigerZipCodes() */ +/************************************************************************/ + +TigerZipCodes::TigerZipCodes( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(&rt6_info, FILE_CODE) + +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "ZipCodes" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + /* -------------------------------------------------------------------- */ + /* Fields from type 6 record. */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns( psRTInfo, poFeatureDefn ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerzipplus4.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerzipplus4.cpp new file mode 100644 index 000000000..c1805138f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/tiger/tigerzipplus4.cpp @@ -0,0 +1,71 @@ +/****************************************************************************** + * $Id: tigerzipplus4.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: TIGER/Line Translator + * Purpose: Implements TigerZipPlus4, providing access to .RTZ files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_tiger.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: tigerzipplus4.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +#define FILE_CODE "Z" + +static const TigerFieldInfo rtZ_fields[] = { + // fieldname fmt type OFTType beg end len bDefine bSet bWrite + { "MODULE", ' ', ' ', OFTString, 0, 0, 8, 1, 0, 0 }, + { "TLID", 'R', 'N', OFTInteger, 6, 15, 10, 1, 1, 1 }, + { "RTSQ", 'R', 'N', OFTInteger, 16, 18, 3, 1, 1, 1 }, + { "ZIP4L", 'L', 'N', OFTInteger, 19, 22, 4, 1, 1, 1 }, + { "ZIP4R", 'L', 'N', OFTInteger, 23, 26, 4, 1, 1, 1 } +}; +static const TigerRecordInfo rtZ_info = + { + rtZ_fields, + sizeof(rtZ_fields) / sizeof(TigerFieldInfo), + 26 + }; + +/************************************************************************/ +/* TigerZipPlus4() */ +/************************************************************************/ + +TigerZipPlus4::TigerZipPlus4( OGRTigerDataSource * poDSIn, + CPL_UNUSED const char * pszPrototypeModule ) : + TigerFileBase(&rtZ_info, FILE_CODE) +{ + poDS = poDSIn; + poFeatureDefn = new OGRFeatureDefn( "ZipPlus4" ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + /* -------------------------------------------------------------------- */ + /* Fields from type Z record. */ + /* -------------------------------------------------------------------- */ + + AddFieldDefns( psRTInfo, poFeatureDefn ); + +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/GNUmakefile new file mode 100644 index 000000000..900699adb --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/GNUmakefile @@ -0,0 +1,21 @@ + +include ../../../GDALmake.opt + +CORE_OBJ = vfkreader.o vfkreadersqlite.o \ + vfkdatablock.o vfkdatablocksqlite.o \ + vfkpropertydefn.o \ + vfkfeature.o vfkfeaturesqlite.o \ + vfkproperty.o + +OGR_OBJ = ogrvfkdriver.o ogrvfkdatasource.o ogrvfklayer.o + +OBJ = $(CORE_OBJ) $(OGR_OBJ) + +CPPFLAGS := -I.. -I../.. $(SQLITE_INC) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_vfk.h vfkreader.h vfkreaderp.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/drv_vfk.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/drv_vfk.html new file mode 100644 index 000000000..9fd5dee5b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/drv_vfk.html @@ -0,0 +1,95 @@ + + +VFK - Czech Cadastral Exchange Data Format + + + + +

      VFK - Czech Cadastral Exchange Data Format

      + +This driver reads VFK files, ie. data in the Czech cadastral +exchange data format. The VFK file is recognized as an OGR +datasource with zero or more OGR layers. + +

      +Note: starting with OGR 1.10, the driver is compiled only if GDAL +is built with SQLite support. + +

      +Points are represented as wkbPoints, lines and boundaries as +wkbLineStrings and areas as wkbPolygons. wkbMulti* primitives are not +used. Feature types cannot be mixed in one layer. + +

      Configuration

      + +Starting with OGR 1.9, the driver reads and writes VFK data from/to +internal SQLite database. Default name of the database is +"input_vfk_file.db". Since OGR 1.10, you can define +an alternative DB name with OGR_VFK_DB_NAME configuration +option. If OGR_VFK_DB_OVERWRITE=YES configuration option is +given, than the driver overwrites existing SQLite database and reads +data from original input VFK file. By default, the internal SQLite +database is not automatically deleted. Since OGR 1.11, the internal +database is deleted if OGR_VFK_DB_DELETE=YES configuration +option is given. + +

      +Starting with OGR 1.10, resolved geometries are stored also in SQLite +database. It means that geometries are resolved only once when +building SQLite database from original VFK data. Geometries are stored +in WKB format. Note that GDAL doesn't need to be built with SpatiaLite +support. Geometries are not stored in DB +when OGR_VFK_DB_SPATIAL=NO configuration option is given. In +this case the geometries are resolved from DB. + +

      Internal working and performance tweaking

      + +Since OGR 1.9, the driver uses an internal SQLite database to resolve +geometries. By default, this file will be written in the same +directory as input VFK file. If SQLite database already exists then +the driver reads VFK features directly from the database (and not from +original VFK file). + +

      +Since OGR 1.11, the driver reads by default all data blocks. When +configuration option OGR_VFK_DB_READ_ALL_BLOCKS=NO is given +than the driver reads only data blocks which are requested by the +user. + +

      Datasource name

      + +Datasource name is a full path to the VFK file. + +

      The driver supports reading files managed by VSI Virtual File +System API, which include "regular" files, as well as files in the +/vsizip/, /vsigzip/, and /vsicurl/ read-only domains. + +

      Layer names

      + +VFK data blocks are used as layer names. + +

      Filters

      + +

      Attribute filter

      + +OGR internal SQL engine is used to evaluate the expression. Evaluation +is done once when the attribute filter is set. + +

      Spatial filter

      + +Bounding boxes of features stored in topology structure are used to +evaluate if a features matches current spatial filter. Evaluation is +done once when the spatial filter is set. + +

      References

      + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/makefile.vc new file mode 100644 index 000000000..83e257899 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/makefile.vc @@ -0,0 +1,20 @@ +CORE_OBJ = vfkreader.obj vfkreadersqlite.obj \ + vfkdatablock.obj vfkdatablocksqlite.obj \ + vfkpropertydefn.obj \ + vfkfeature.obj vfkfeaturesqlite.obj \ + vfkproperty.obj + +OGR_OBJ = ogrvfkdriver.obj ogrvfkdatasource.obj ogrvfklayer.obj + +OBJ = $(CORE_OBJ) $(OGR_OBJ) + +EXTRAFLAGS = -I.. -I..\.. $(SQLITE_INC) -DHAVE_SQLITE + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/ogr_vfk.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/ogr_vfk.h new file mode 100644 index 000000000..a8bf04818 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/ogr_vfk.h @@ -0,0 +1,120 @@ +/****************************************************************************** + * $Id: ogr_vfk.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for OGR/VFK driver. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2009-2010, Martin Landa + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_OGR_VFK_H_INCLUDED +#define GDAL_OGR_VFK_H_INCLUDED + +#include +#include + +#include "ogrsf_frmts.h" +#include "vfkreader.h" + +class OGRVFKDataSource; + +/************************************************************************/ +/* OGRVFKLayer */ +/************************************************************************/ + +class OGRVFKLayer:public OGRLayer +{ +private: + /* spatial reference */ + OGRSpatialReference *poSRS; + + /* feature definition */ + OGRFeatureDefn *poFeatureDefn; + + /* OGR data source */ + OGRVFKDataSource *poDS; + + /* VFK data block */ + IVFKDataBlock *poDataBlock; + + /* get next feature */ + int m_iNextFeature; + + /* private methods */ + OGRGeometry *CreateGeometry(IVFKFeature *); + OGRFeature *GetFeature(IVFKFeature *); + +public: + OGRVFKLayer(const char *, OGRSpatialReference *, + OGRwkbGeometryType, OGRVFKDataSource *); + ~OGRVFKLayer(); + + OGRFeature *GetNextFeature(); + OGRFeature *GetFeature(GIntBig); + + OGRFeatureDefn *GetLayerDefn() { return poFeatureDefn; } + + void ResetReading(); + + int TestCapability(const char *); + + GIntBig GetFeatureCount(int = TRUE); +}; + +/************************************************************************/ +/* OGRVFKDataSource */ +/************************************************************************/ +class OGRVFKDataSource:public OGRDataSource +{ +private: + /* list of available layers */ + OGRVFKLayer **papoLayers; + int nLayers; + + char * pszName; + + /* input related parameters */ + IVFKReader *poReader; + + /* private methods */ + OGRVFKLayer *CreateLayerFromBlock(const IVFKDataBlock *); + +public: + OGRVFKDataSource(); + ~OGRVFKDataSource(); + + int Open(const char *, int); + + const char *GetName() { return pszName; } + + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer(int); + + int TestCapability(const char *); + + IVFKReader *GetReader() const { return poReader; } +}; + +#endif // GDAL_OGR_VFK_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/ogrvfkdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/ogrvfkdatasource.cpp new file mode 100644 index 000000000..acdd24587 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/ogrvfkdatasource.cpp @@ -0,0 +1,204 @@ +/****************************************************************************** + * $Id: ogrvfkdatasource.cpp 26906 2014-01-31 16:28:21Z martinl $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRVFKDatasource class. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2009-2010, 2013 Martin Landa + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#include "ogr_vfk.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrvfkdatasource.cpp 26906 2014-01-31 16:28:21Z martinl $"); + +/*! + \brief OGRVFKDataSource constructor +*/ +OGRVFKDataSource::OGRVFKDataSource() +{ + pszName = NULL; + + poReader = NULL; + + papoLayers = NULL; + nLayers = 0; +} + +/*! + \brief OGRVFKDataSource destructor +*/ +OGRVFKDataSource::~OGRVFKDataSource() +{ + CPLFree(pszName); + + if (poReader) + delete poReader; + + for(int i = 0; i < nLayers; i++) + delete papoLayers[i]; + + CPLFree(papoLayers); +} + +/*! + \brief Open VFK datasource + + \param pszNewName datasource name + \param bTestOpen True to test if datasource is possible to open + + \return TRUE on success or FALSE on failure +*/ +int OGRVFKDataSource::Open(const char *pszNewName, int bTestOpen) +{ + FILE * fp; + char szHeader[1000]; + + /* open the source file */ + fp = VSIFOpen(pszNewName, "r"); + if (fp == NULL) { + if (!bTestOpen) + CPLError(CE_Failure, CPLE_OpenFailed, + "Failed to open VFK file `%s'", + pszNewName); + + return FALSE; + } + + /* If we aren't sure it is VFK, load a header chunk and check + for signs it is VFK */ + if (bTestOpen) { + size_t nRead = VSIFRead(szHeader, 1, sizeof(szHeader), fp); + if (nRead <= 0) { + VSIFClose(fp); + return FALSE; + } + szHeader[MIN(nRead, sizeof(szHeader))-1] = '\0'; + + // TODO: improve check + if (strncmp(szHeader, "&H", 2) != 0) { + VSIFClose(fp); + return FALSE; + } + } + + /* We assume now that it is VFK. Close and instantiate a + VFKReader on it. */ + VSIFClose(fp); + + pszName = CPLStrdup(pszNewName); + + poReader = CreateVFKReader(pszNewName); + if (poReader == NULL) { + CPLError(CE_Failure, CPLE_AppDefined, + "File %s appears to be VFK but the VFK reader can't" + "be instantiated", + pszNewName); + return FALSE; + } + + /* read data blocks, i.e. &B */ + poReader->ReadDataBlocks(); + + /* get list of layers */ + papoLayers = (OGRVFKLayer **) CPLCalloc(sizeof(OGRVFKLayer *), poReader->GetDataBlockCount()); + + for (int iLayer = 0; iLayer < poReader->GetDataBlockCount(); iLayer++) { + papoLayers[iLayer] = CreateLayerFromBlock(poReader->GetDataBlock(iLayer)); + nLayers++; + } + + /* read data records if required */ + if (CSLTestBoolean(CPLGetConfigOption("OGR_VFK_DB_READ_ALL_BLOCKS", "YES"))) + poReader->ReadDataRecords(); + + return TRUE; +} + +/*! + \brief Get VFK layer + + \param iLayer layer number + + \return pointer to OGRLayer instance or NULL on error +*/ +OGRLayer *OGRVFKDataSource::GetLayer(int iLayer) +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + + return papoLayers[iLayer]; +} + +/*! + \brief Test datasource capabilies + + \param pszCap capability + + \return TRUE if supported or FALSE if not supported +*/ +int OGRVFKDataSource::TestCapability(const char * pszCap) +{ + if (EQUAL(pszCap, "IsPreProcessed") && poReader) { + if (poReader->IsPreProcessed()) + return TRUE; + } + + return FALSE; +} + +/*! + \brief Create OGR layer from VFKDataBlock + + \param poDataBlock pointer to VFKDataBlock instance + + \return pointer to OGRVFKLayer instance or NULL on error +*/ +OGRVFKLayer *OGRVFKDataSource::CreateLayerFromBlock(const IVFKDataBlock *poDataBlock) +{ + OGRVFKLayer *poLayer; + + poLayer = NULL; + + /* create an empty layer */ + poLayer = new OGRVFKLayer(poDataBlock->GetName(), NULL, + poDataBlock->GetGeometryType(), this); + + /* define attributes (properties) */ + for (int iField = 0; iField < poDataBlock->GetPropertyCount(); iField++) { + VFKPropertyDefn *poProperty = poDataBlock->GetProperty(iField); + OGRFieldDefn oField(poProperty->GetName(), poProperty->GetType()); + + if(poProperty->GetWidth() > 0) + oField.SetWidth(poProperty->GetWidth()); + if(poProperty->GetPrecision() > 0) + oField.SetPrecision(poProperty->GetPrecision()); + + poLayer->GetLayerDefn()->AddFieldDefn(&oField); + } + + return poLayer; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/ogrvfkdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/ogrvfkdriver.cpp new file mode 100644 index 000000000..5c73bab61 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/ogrvfkdriver.cpp @@ -0,0 +1,94 @@ +/****************************************************************************** + * $Id: ogrvfkdriver.cpp 27384 2014-05-24 12:28:12Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRVFKDriver class. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2009-2010, Martin Landa + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#include "ogr_vfk.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrvfkdriver.cpp 27384 2014-05-24 12:28:12Z rouault $"); + +static int OGRVFKDriverIdentify(GDALOpenInfo* poOpenInfo) +{ + return ( poOpenInfo->fpL != NULL && + poOpenInfo->nHeaderBytes >= 2 && + strncmp((const char*)poOpenInfo->pabyHeader, "&H", 2) == 0 ); +} + +/* + \brief Open existing data source + \return NULL on failure +*/ +static GDALDataset *OGRVFKDriverOpen(GDALOpenInfo* poOpenInfo) +{ + OGRVFKDataSource *poDS; + + if( poOpenInfo->eAccess == GA_Update || + !OGRVFKDriverIdentify(poOpenInfo) ) + return NULL; + + poDS = new OGRVFKDataSource(); + + if(!poDS->Open(poOpenInfo->pszFilename, TRUE) || poDS->GetLayerCount() == 0) { + delete poDS; + return NULL; + } + else + return poDS; +} + + +/*! + \brief Register VFK driver +*/ +void RegisterOGRVFK() +{ + if (!GDAL_CHECK_VERSION("OGR/VFK driver")) + return; + GDALDriver *poDriver; + + if( GDALGetDriverByName( "VFK" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "VFK" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "Czech Cadastral Exchange Data Format" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "vfk" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_vfk.html" ); + + poDriver->pfnOpen = OGRVFKDriverOpen; + poDriver->pfnIdentify = OGRVFKDriverIdentify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/ogrvfklayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/ogrvfklayer.cpp new file mode 100644 index 000000000..7cfce1972 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/ogrvfklayer.cpp @@ -0,0 +1,256 @@ +/****************************************************************************** + * $Id: ogrvfklayer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRVFKLayer class. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2009-2010, Martin Landa + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#include "ogr_vfk.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrvfklayer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/*! + \brief OGRVFKLayer constructor + + \param pszName layer name + \param poSRSIn spatial reference + \param eReqType WKB geometry type + \param poDSIn data source where to registrate OGR layer +*/ +OGRVFKLayer::OGRVFKLayer(const char *pszName, + OGRSpatialReference *poSRSIn, + OGRwkbGeometryType eReqType, + OGRVFKDataSource *poDSIn) +{ + /* set spatial reference */ + if( poSRSIn == NULL ) { + /* default is S-JTSK (EPSG: 5514) */ + poSRS = new OGRSpatialReference(); + if (poSRS->importFromEPSG(5514) != OGRERR_NONE) { + delete poSRS; + poSRS = NULL; + } + } + else { + poSRS = poSRSIn->Clone(); + } + + /* layer datasource */ + poDS = poDSIn; + + /* feature definition */ + poFeatureDefn = new OGRFeatureDefn(pszName); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType(eReqType); + + /* data block reference */ + poDataBlock = poDS->GetReader()->GetDataBlock(pszName); +} + +/*! + \brief OGRVFKLayer() destructor +*/ +OGRVFKLayer::~OGRVFKLayer() +{ + if(poFeatureDefn) + poFeatureDefn->Release(); + + if(poSRS) + poSRS->Release(); +} + +/*! + \brief Test capability (random access, etc.) + + \param pszCap capability name +*/ +int OGRVFKLayer::TestCapability(const char * pszCap) +{ + if (EQUAL(pszCap, OLCRandomRead)) { + return TRUE; /* ? */ + } + + return FALSE; +} + +/*! + \brief Reset reading + + \todo To be implemented +*/ +void OGRVFKLayer::ResetReading() +{ + m_iNextFeature = 0; + poDataBlock->ResetReading(); +} + +/*! + \brief Create geometry from VFKFeature + + \param poVfkFeature pointer to VFKFeature + + \return pointer to OGRGeometry or NULL on error +*/ +OGRGeometry *OGRVFKLayer::CreateGeometry(IVFKFeature * poVfkFeature) +{ + return poVfkFeature->GetGeometry(); +} + +/*! + \brief Get feature count + + This method overwrites OGRLayer::GetFeatureCount(), + + \param bForce skip (return -1) + + \return number of features +*/ +GIntBig OGRVFKLayer::GetFeatureCount(CPL_UNUSED int bForce) +{ + int nfeatures; + + /* note that 'nfeatures' is 0 when data are not read from DB */ + nfeatures = (int)poDataBlock->GetFeatureCount(); + if (m_poFilterGeom || m_poAttrQuery || nfeatures < 1) { + /* force real feature count */ + nfeatures = (int)OGRLayer::GetFeatureCount(); + } + + CPLDebug("OGR-VFK", "OGRVFKLayer::GetFeatureCount(): name=%s -> n=%d", + GetName(), nfeatures); + + return nfeatures; +} + +/*! + \brief Get next feature + + \return pointer to OGRFeature instance +*/ +OGRFeature *OGRVFKLayer::GetNextFeature() +{ + VFKFeature *poVFKFeature; + + OGRFeature *poOGRFeature; + OGRGeometry *poOGRGeom; + + poOGRFeature = NULL; + poOGRGeom = NULL; + + /* loop till we find and translate a feature meeting all our + requirements + */ + while (TRUE) { + /* cleanup last feature, and get a new raw vfk feature */ + if (poOGRGeom != NULL) { + delete poOGRGeom; + poOGRGeom = NULL; + } + + poVFKFeature = (VFKFeature *) poDataBlock->GetNextFeature(); + if (!poVFKFeature) + return NULL; + + /* skip feature with unknown geometry type */ + if (poVFKFeature->GetGeometryType() == wkbUnknown) + continue; + + poOGRFeature = GetFeature(poVFKFeature); + if (poOGRFeature) + return poOGRFeature; + } +} + +/*! + \brief Get feature by fid + + \param nFID feature id (-1 for next) + + \return pointer to OGRFeature or NULL not found +*/ +OGRFeature *OGRVFKLayer::GetFeature(GIntBig nFID) +{ + IVFKFeature *poVFKFeature; + + poVFKFeature = poDataBlock->GetFeature(nFID); + + if (!poVFKFeature) + return NULL; + + CPLAssert(nFID == poVFKFeature->GetFID()); + CPLDebug("OGR-VFK", "OGRVFKLayer::GetFeature(): name=%s fid=" CPL_FRMT_GIB, GetName(), nFID); + + return GetFeature(poVFKFeature); +} + +/*! + \brief Get feature (private) + + \return pointer to OGRFeature or NULL not found +*/ +OGRFeature *OGRVFKLayer::GetFeature(IVFKFeature *poVFKFeature) +{ + OGRGeometry *poGeom; + + /* skip feature with unknown geometry type */ + if (poVFKFeature->GetGeometryType() == wkbUnknown) + return NULL; + + /* get features geometry */ + poGeom = CreateGeometry(poVFKFeature); + if (poGeom != NULL) + poGeom->assignSpatialReference(poSRS); + + /* does it satisfy the spatial query, if there is one? */ + if (m_poFilterGeom != NULL && poGeom && !FilterGeometry(poGeom)) { + return NULL; + } + + /* convert the whole feature into an OGRFeature */ + OGRFeature *poOGRFeature = new OGRFeature(GetLayerDefn()); + poOGRFeature->SetFID(poVFKFeature->GetFID()); + // poOGRFeature->SetFID(++m_iNextFeature); + + poVFKFeature->LoadProperties(poOGRFeature); + + /* test against the attribute query */ + if (m_poAttrQuery != NULL && + !m_poAttrQuery->Evaluate(poOGRFeature)) { + delete poOGRFeature; + return NULL; + } + + if (poGeom) + poOGRFeature->SetGeometryDirectly(poGeom->clone()); + + return poOGRFeature; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkdatablock.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkdatablock.cpp new file mode 100644 index 000000000..d65a66a07 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkdatablock.cpp @@ -0,0 +1,1045 @@ +/****************************************************************************** + * $Id: vfkdatablock.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: VFK Reader - Data block definition + * Purpose: Implements VFKDataBlock class. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2009-2013, Martin Landa + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#include + +#include "vfkreader.h" +#include "vfkreaderp.h" + +#include "cpl_conv.h" +#include "cpl_error.h" + +/*! + \brief VFK Data Block constructor + + \param pszName data block name +*/ +IVFKDataBlock::IVFKDataBlock(const char *pszName, const IVFKReader *poReader) +{ + m_pszName = CPLStrdup(pszName); + + m_nPropertyCount = 0; + m_papoProperty = NULL; + + m_nFeatureCount = -1; /* load data on first request */ + m_papoFeature = NULL; + + m_iNextFeature = -1; + + m_nGeometryType = wkbUnknown; + m_bGeometry = FALSE; /* geometry is not loaded by default */ + m_bGeometryPerBlock = TRUE; /* load geometry per block/feature */ + + m_poReader = (IVFKReader *) poReader; + + m_nRecordCount[RecordValid] = 0L; /* number of valid records */ + m_nRecordCount[RecordSkipped] = 0L; /* number of skipped (invalid) records */ + m_nRecordCount[RecordDuplicated] = 0L; /* number of duplicated records */ +} + +/*! + \brief VFKDataBlock destructor +*/ +IVFKDataBlock::~IVFKDataBlock() +{ + CPLFree(m_pszName); + + for (int i = 0; i < m_nPropertyCount; i++) { + if (m_papoProperty[i]) + delete m_papoProperty[i]; + } + CPLFree(m_papoProperty); + + for (int i = 0; i < m_nFeatureCount; i++) { + if (m_papoFeature[i]) + delete m_papoFeature[i]; + } + CPLFree(m_papoFeature); +} + +/*! + \brief Get property definition + + \param iIndex property index + + \return pointer to VFKPropertyDefn definition or NULL on failure +*/ +VFKPropertyDefn *IVFKDataBlock::GetProperty(int iIndex) const +{ + if(iIndex < 0 || iIndex >= m_nPropertyCount) + return NULL; + + return m_papoProperty[iIndex]; +} + +/*! + \brief Set properties + + \param poLine pointer to line +*/ +void IVFKDataBlock::SetProperties(const char *poLine) +{ + const char *poChar, *poProp; + char *pszName, *pszType; + int nLength; + + pszName = pszType = NULL; + + /* skip data block name */ + for (poChar = poLine; *poChar != '0' && *poChar != ';'; poChar++) + ; + if (*poChar == '\0') + return; + + poChar++; + + /* read property name/type */ + poProp = poChar; + nLength = 0; + while(*poChar != '\0') { + if (*poChar == ' ') { + pszName = (char *) CPLRealloc(pszName, nLength + 1); + strncpy(pszName, poProp, nLength); + pszName[nLength] = '\0'; + + poProp = ++poChar; + nLength = 0; + } + else if (*poChar == ';') { + pszType = (char *) CPLRealloc(pszType, nLength + 1); + strncpy(pszType, poProp, nLength); + pszType[nLength] = '\0'; + + /* add property */ + if (pszName && *pszName != '\0' && + pszType && *pszType != '\0') + AddProperty(pszName, pszType); + + poProp = ++poChar; + nLength = 0; + } + poChar++; + nLength++; + } + + pszType = (char *) CPLRealloc(pszType, nLength + 1); + strncpy(pszType, poProp, nLength); + pszType[nLength] = '\0'; + + /* add property */ + if (pszName && *pszName != '\0' && + pszType && *pszType != '\0') + AddProperty(pszName, pszType); + + CPLFree(pszName); + CPLFree(pszType); +} + +/*! + \brief Add data block property + + \param pszName property name + \param pszType property type + + \return number of properties +*/ +int IVFKDataBlock::AddProperty(const char *pszName, const char *pszType) +{ + VFKPropertyDefn *poNewProperty = new VFKPropertyDefn(pszName, pszType, + m_poReader->IsLatin2()); + + m_nPropertyCount++; + + m_papoProperty = (VFKPropertyDefn **) + CPLRealloc(m_papoProperty, sizeof (VFKPropertyDefn *) * m_nPropertyCount); + m_papoProperty[m_nPropertyCount-1] = poNewProperty; + + return m_nPropertyCount; +} + +/*! + \brief Get number of features for given data block + + \return number of features +*/ +GIntBig IVFKDataBlock::GetFeatureCount() +{ + if (m_nFeatureCount < 0) { + m_poReader->ReadDataRecords(this); /* read VFK data records */ + if (m_bGeometryPerBlock && !m_bGeometry) { + LoadGeometry(); /* get real number of features */ + } + } + + return m_nFeatureCount; +} + +/*! + \brief Set number of features per data block + + \param nNewCount number of features + \param bIncrement increment current value +*/ +void IVFKDataBlock::SetFeatureCount(int nNewCount, bool bIncrement) +{ + if (bIncrement) { + m_nFeatureCount += nNewCount; + } + else { + m_nFeatureCount = nNewCount; + } +} + +/*! + \brief Reset reading + + \param iIdx force index +*/ +void IVFKDataBlock::ResetReading(int iIdx) +{ + if (iIdx > -1) { + m_iNextFeature = iIdx; + } + else { + m_iNextFeature = 0; + } +} + +/*! + \brief Get next feature + + \return pointer to VFKFeature instance or NULL on error +*/ +IVFKFeature *IVFKDataBlock::GetNextFeature() +{ + if (m_nFeatureCount < 0) { + m_poReader->ReadDataRecords(this); + } + + if (m_bGeometryPerBlock && !m_bGeometry) { + LoadGeometry(); + } + + if (m_iNextFeature < 0) + ResetReading(); + + if (m_iNextFeature < 0 || m_iNextFeature >= m_nFeatureCount) + return NULL; + + return m_papoFeature[m_iNextFeature++]; +} + +/*! + \brief Get previous feature + + \return pointer to VFKFeature instance or NULL on error +*/ +IVFKFeature *IVFKDataBlock::GetPreviousFeature() +{ + if (m_nFeatureCount < 0) { + m_poReader->ReadDataRecords(this); + } + + if (m_bGeometryPerBlock && !m_bGeometry) { + LoadGeometry(); + } + + if (m_iNextFeature < 0) + ResetReading(); + + if (m_iNextFeature < 0 || m_iNextFeature >= m_nFeatureCount) + return NULL; + + return m_papoFeature[m_iNextFeature--]; +} + +/*! + \brief Get first feature + + \return pointer to VFKFeature instance or NULL on error +*/ +IVFKFeature *IVFKDataBlock::GetFirstFeature() +{ + if (m_nFeatureCount < 0) { + m_poReader->ReadDataRecords(this); + } + + if (m_bGeometryPerBlock && !m_bGeometry) { + LoadGeometry(); + } + + if (m_nFeatureCount < 1) + return NULL; + + return m_papoFeature[0]; +} + +/*! + \brief Get last feature + + \return pointer to VFKFeature instance or NULL on error +*/ +IVFKFeature *IVFKDataBlock::GetLastFeature() +{ + if (m_nFeatureCount < 0) { + m_poReader->ReadDataRecords(this); + } + + if (m_bGeometryPerBlock && !m_bGeometry) { + LoadGeometry(); + } + + if (m_nFeatureCount < 1) + return NULL; + + return m_papoFeature[m_nFeatureCount-1]; +} + +/*! + \brief Get property index by name + + \param pszName property name + + \return property index or -1 on error (property name not found) +*/ +int IVFKDataBlock::GetPropertyIndex(const char *pszName) const +{ + for (int i = 0; i < m_nPropertyCount; i++) + if (EQUAL(pszName,m_papoProperty[i]->GetName())) + return i; + + return -1; +} + +/*! + \brief Set geometry type (point, linestring, polygon) + + \return geometry type +*/ +OGRwkbGeometryType IVFKDataBlock::SetGeometryType() +{ + m_nGeometryType = wkbNone; /* pure attribute records */ + + if (EQUAL (m_pszName, "SOBR") || + EQUAL (m_pszName, "OBBP") || + EQUAL (m_pszName, "SPOL") || + EQUAL (m_pszName, "OB") || + EQUAL (m_pszName, "OP") || + EQUAL (m_pszName, "OBPEJ")) + m_nGeometryType = wkbPoint; + + else if (EQUAL (m_pszName, "SBP") || + EQUAL (m_pszName, "HP") || + EQUAL (m_pszName, "DPM")) + m_nGeometryType = wkbLineString; + + else if (EQUAL (m_pszName, "PAR") || + EQUAL (m_pszName, "BUD")) + m_nGeometryType = wkbPolygon; + + return m_nGeometryType; +} + +/*! + \brief Get geometry type + + \return geometry type +*/ +OGRwkbGeometryType IVFKDataBlock::GetGeometryType() const +{ + return m_nGeometryType; +} + +/*! + \brief Get feature by index + + \param iIndex feature index + + \return pointer to feature definition or NULL on failure +*/ +IVFKFeature *IVFKDataBlock::GetFeatureByIndex(int iIndex) const +{ + if(iIndex < 0 || iIndex >= m_nFeatureCount) + return NULL; + + return m_papoFeature[iIndex]; +} + +/*! + \brief Get feature by FID + + Modifies next feature id. + + \param nFID feature id + + \return pointer to feature definition or NULL on failure (not found) +*/ +IVFKFeature *IVFKDataBlock::GetFeature(GIntBig nFID) +{ + if (m_nFeatureCount < 0) { + m_poReader->ReadDataRecords(this); + } + + if (nFID < 1 || nFID > m_nFeatureCount) + return NULL; + + if (m_bGeometryPerBlock && !m_bGeometry) { + LoadGeometry(); + } + + return GetFeatureByIndex(int (nFID) - 1); /* zero-based index */ +} + +/*! + \brief Load geometry + + Print warning when some invalid features are detected. + + \return number of invalid features or -1 on failure +*/ +int IVFKDataBlock::LoadGeometry() +{ + int nInvalid; +#ifdef DEBUG_TIMING + clock_t start, end; +#endif + + if (m_bGeometry) + return 0; + + nInvalid = 0; + m_bGeometry = TRUE; +#ifdef DEBUG_TIMING + start = clock(); +#endif + + if (m_nFeatureCount < 0) { + m_poReader->ReadDataRecords(this); + } + + if (EQUAL (m_pszName, "SOBR") || + EQUAL (m_pszName, "SPOL") || + EQUAL (m_pszName, "OP") || + EQUAL (m_pszName, "OBPEJ") || + EQUAL (m_pszName, "OB") || + EQUAL (m_pszName, "OBBP")) { + /* -> wkbPoint */ + nInvalid = LoadGeometryPoint(); + } + else if (EQUAL (m_pszName, "SBP")) { + /* -> wkbLineString */ + nInvalid = LoadGeometryLineStringSBP(); + } + else if (EQUAL (m_pszName, "HP") || + EQUAL (m_pszName, "DPM")) { + /* -> wkbLineString */ + nInvalid = LoadGeometryLineStringHP(); + } + else if (EQUAL (m_pszName, "PAR") || + EQUAL (m_pszName, "BUD")) { + /* -> wkbPolygon */ + nInvalid = LoadGeometryPolygon(); + } + +#ifdef DEBUG_TIMING + end = clock(); +#endif + + if (nInvalid > 0) { + CPLError(CE_Warning, CPLE_AppDefined, + "%s: %d features with invalid or empty geometry", m_pszName, nInvalid); + } + +#ifdef DEBUG_TIMING + CPLDebug("OGR-VFK", "VFKDataBlock::LoadGeometry(): name=%s time=%ld sec", + m_pszName, (long)((end - start) / CLOCKS_PER_SEC)); +#endif + + return nInvalid; +} + +/*! + \brief Add linestring to a ring (private) + + \param[in,out] papoRing list of rings + \param poLine pointer to linestring to be added to a ring + \param bNewRing create new ring + \param bBackword allow backward direction + + \return TRUE on success or FALSE on failure +*/ +bool IVFKDataBlock::AppendLineToRing(PointListArray *papoRing, const OGRLineString *poLine, + bool bNewRing, bool bBackward) +{ + OGRPoint *poFirst, *poLast; + OGRPoint *poFirstNew, *poLastNew; + + OGRPoint pt; + PointList poList; + + /* OGRLineString -> PointList */ + for (int i = 0; i < poLine->getNumPoints(); i++) { + poLine->getPoint(i, &pt); + poList.push_back(pt); + } + + /* create new ring */ + if (bNewRing) { + papoRing->push_back(new PointList(poList)); + return TRUE; + } + + poFirstNew = &(poList.front()); + poLastNew = &(poList.back()); + for (PointListArray::const_iterator i = papoRing->begin(), e = papoRing->end(); + i != e; ++i) { + PointList *ring = (*i); + poFirst = &(ring->front()); + poLast = &(ring->back()); + if (!poFirst || !poLast || poLine->getNumPoints() < 2) + return FALSE; + + if (poFirstNew->Equals(poLast)) { + /* forward, skip first point */ + ring->insert(ring->end(), poList.begin()+1, poList.end()); + return TRUE; + } + + if (bBackward && poFirstNew->Equals(poFirst)) { + /* backward, skip last point */ + ring->insert(ring->begin(), poList.rbegin(), poList.rend()-1); + return TRUE; + } + + if (poLastNew->Equals(poLast)) { + /* backward, skip first point */ + ring->insert(ring->end(), poList.rbegin()+1, poList.rend()); + return TRUE; + } + + if (bBackward && poLastNew->Equals(poFirst)) { + /* forward, skip last point */ + ring->insert(ring->begin(), poList.begin(), poList.end()-1); + return TRUE; + } + } + + return FALSE; +} + +/*! + \brief Set next feature + + \param poFeature pointer to current feature + + \return index of current feature or -1 on failure +*/ +int IVFKDataBlock::SetNextFeature(const IVFKFeature *poFeature) +{ + for (int i = 0; i < m_nFeatureCount; i++) { + if (m_papoFeature[i] == poFeature) { + m_iNextFeature = i + 1; + return i; + } + } + + return -1; +} + +/*! + \brief Add feature + + \param poNewFeature pointer to VFKFeature instance +*/ +void IVFKDataBlock::AddFeature(IVFKFeature *poNewFeature) +{ + m_nFeatureCount++; + + m_papoFeature = (IVFKFeature **) + CPLRealloc(m_papoFeature, sizeof (IVFKFeature *) * m_nFeatureCount); + m_papoFeature[m_nFeatureCount-1] = poNewFeature; +} + +/*! + \brief Get number of records + + \param iRec record type (valid, skipped, duplicated) + + \return number of records +*/ +int IVFKDataBlock::GetRecordCount(RecordType iRec) const +{ + return (int) m_nRecordCount[iRec]; +} + +/*! + \brief Increment number of records + + \param iRec record type (valid, skipped, duplicated) +*/ +void IVFKDataBlock::SetIncRecordCount(RecordType iRec) +{ + m_nRecordCount[iRec]++; +} + +/*! + \brief Get first found feature based on it's properties + + Note: modifies next feature. + + \param idx property index + \param value property value + \param poList list of features (NULL to loop all features) + + \return pointer to feature definition or NULL on failure (not found) +*/ +VFKFeature *VFKDataBlock::GetFeature(int idx, GUIntBig value, VFKFeatureList *poList) +{ + GUIntBig iPropertyValue; + VFKFeature *poVfkFeature; + + if (poList) { + for (VFKFeatureList::iterator i = poList->begin(), e = poList->end(); + i != e; ++i) { + poVfkFeature = *i; + iPropertyValue = strtoul(poVfkFeature->GetProperty(idx)->GetValueS(), NULL, 0); + if (iPropertyValue == value) { + poList->erase(i); /* ??? */ + return poVfkFeature; + } + } + } + else { + for (int i = 0; i < m_nFeatureCount; i++) { + poVfkFeature = (VFKFeature *) GetFeatureByIndex(i); + iPropertyValue = strtoul(poVfkFeature->GetProperty(idx)->GetValueS(), NULL, 0); + if (iPropertyValue == value) { + m_iNextFeature = i + 1; + return poVfkFeature; + } + } + } + + return NULL; +} + +/*! + \brief Get features based on properties + + \param idx property index + \param value property value + + \return list of features +*/ +VFKFeatureList VFKDataBlock::GetFeatures(int idx, GUIntBig value) +{ + GUIntBig iPropertyValue; + VFKFeature *poVfkFeature; + std::vector poResult; + + for (int i = 0; i < m_nFeatureCount; i++) { + poVfkFeature = (VFKFeature *) GetFeatureByIndex(i); + iPropertyValue = strtoul(poVfkFeature->GetProperty(idx)->GetValueS(), NULL, 0); + if (iPropertyValue == value) { + poResult.push_back(poVfkFeature); + } + } + + return poResult; +} + +/*! + \brief Get features based on properties + + \param idx1 property index + \param idx2 property index + \param value property value + + \return list of features +*/ +VFKFeatureList VFKDataBlock::GetFeatures(int idx1, int idx2, GUIntBig value) +{ + GUIntBig iPropertyValue1, iPropertyValue2; + VFKFeature *poVfkFeature; + std::vector poResult; + + for (int i = 0; i < m_nFeatureCount; i++) { + poVfkFeature = (VFKFeature *) GetFeatureByIndex(i); + iPropertyValue1 = strtoul(poVfkFeature->GetProperty(idx1)->GetValueS(), NULL, 0); + if (idx2 < 0) { + if (iPropertyValue1 == value) { + poResult.push_back(poVfkFeature); + } + } + else { + iPropertyValue2 = strtoul(poVfkFeature->GetProperty(idx2)->GetValueS(), NULL, 0); + if (iPropertyValue1 == value || iPropertyValue2 == value) { + poResult.push_back(poVfkFeature); + } + } + } + + return poResult; +} + +/*! + \brief Get feature count based on property value + + \param pszName property name + \param pszValue property value + + \return number of features or -1 on error +*/ +GIntBig VFKDataBlock::GetFeatureCount(const char *pszName, const char *pszValue) +{ + int nfeatures, propIdx; + VFKFeature *poVFKFeature; + + propIdx = GetPropertyIndex(pszName); + if (propIdx < 0) + return -1; + + nfeatures = 0; + for (int i = 0; i < ((IVFKDataBlock *) this)->GetFeatureCount(); i++) { + poVFKFeature = (VFKFeature *) ((IVFKDataBlock *) this)->GetFeature(i); + if (!poVFKFeature) + return -1; + if (EQUAL (poVFKFeature->GetProperty(propIdx)->GetValueS(), pszValue)) + nfeatures++; + } + + return nfeatures; +} + +/*! + \brief Load geometry (point layers) + + \return number of invalid features +*/ +int VFKDataBlock::LoadGeometryPoint() +{ + /* -> wkbPoint */ + long nInvalid; + double x, y; + int i_idxX, i_idxY; + + VFKFeature *poFeature; + + nInvalid = 0; + i_idxY = GetPropertyIndex("SOURADNICE_Y"); + i_idxX = GetPropertyIndex("SOURADNICE_X"); + if (i_idxY < 0 || i_idxX < 0) { + CPLError(CE_Failure, CPLE_NotSupported, + "Corrupted data (%s).\n", m_pszName); + return nInvalid; + } + + for (int j = 0; j < ((IVFKDataBlock *) this)->GetFeatureCount(); j++) { + poFeature = (VFKFeature *) GetFeatureByIndex(j); + x = -1.0 * poFeature->GetProperty(i_idxY)->GetValueD(); + y = -1.0 * poFeature->GetProperty(i_idxX)->GetValueD(); + OGRPoint pt(x, y); + if (!poFeature->SetGeometry(&pt)) + nInvalid++; + } + + return nInvalid; +} + +/*! + \brief Load geometry (linestring SBP layer) + + \return number of invalid features +*/ +int VFKDataBlock::LoadGeometryLineStringSBP() +{ + int idxId, idxBp_Id, idxPCB; + GUIntBig id, ipcb; + int nInvalid; + + VFKDataBlock *poDataBlockPoints; + VFKFeature *poFeature, *poPoint, *poLine; + + OGRLineString oOGRLine; + + nInvalid = 0; + poLine = NULL; + + poDataBlockPoints = (VFKDataBlock *) m_poReader->GetDataBlock("SOBR"); + if (NULL == poDataBlockPoints) { + CPLError(CE_Failure, CPLE_NotSupported, + "Data block %s not found.\n", m_pszName); + return nInvalid; + } + + poDataBlockPoints->LoadGeometry(); + idxId = poDataBlockPoints->GetPropertyIndex("ID"); + idxBp_Id = GetPropertyIndex("BP_ID"); + idxPCB = GetPropertyIndex("PORADOVE_CISLO_BODU"); + if (idxId < 0 || idxBp_Id < 0 || idxPCB < 0) { + CPLError(CE_Failure, CPLE_NotSupported, + "Corrupted data (%s).\n", m_pszName); + return nInvalid; + } + + for (int j = 0; j < ((IVFKDataBlock *) this)->GetFeatureCount(); j++) { + poFeature = (VFKFeature *) GetFeatureByIndex(j); + CPLAssert(NULL != poFeature); + + poFeature->SetGeometry(NULL); + id = strtoul(poFeature->GetProperty(idxBp_Id)->GetValueS(), NULL, 0); + ipcb = strtoul(poFeature->GetProperty(idxPCB)->GetValueS(), NULL, 0); + if (ipcb == 1) { + if (!oOGRLine.IsEmpty()) { + oOGRLine.setCoordinateDimension(2); /* force 2D */ + if (!poLine->SetGeometry(&oOGRLine)) + nInvalid++; + oOGRLine.empty(); /* restore line */ + } + poLine = poFeature; + } + else { + poFeature->SetGeometryType(wkbUnknown); + } + poPoint = poDataBlockPoints->GetFeature(idxId, id); + if (!poPoint) + continue; + OGRPoint *pt = (OGRPoint *) poPoint->GetGeometry(); + oOGRLine.addPoint(pt); + } + /* add last line */ + oOGRLine.setCoordinateDimension(2); /* force 2D */ + if (poLine) { + if (!poLine->SetGeometry(&oOGRLine)) + nInvalid++; + } + poDataBlockPoints->ResetReading(); + + return nInvalid; +} + +/*! + \brief Load geometry (linestring HP/DPM layer) + + \return number of invalid features +*/ +int VFKDataBlock::LoadGeometryLineStringHP() +{ + long nInvalid; + int idxId, idxMy_Id, idxPCB; + GUIntBig id; + + VFKDataBlock *poDataBlockLines; + VFKFeature *poFeature, *poLine; + VFKFeatureList poLineList; + + nInvalid = 0; + + poDataBlockLines = (VFKDataBlock *) m_poReader->GetDataBlock("SBP"); + if (NULL == poDataBlockLines) { + CPLError(CE_Failure, CPLE_NotSupported, + "Data block %s not found.\n", m_pszName); + return nInvalid; + } + + poDataBlockLines->LoadGeometry(); + idxId = GetPropertyIndex("ID"); + if (EQUAL (m_pszName, "HP")) + idxMy_Id = poDataBlockLines->GetPropertyIndex("HP_ID"); + else + idxMy_Id = poDataBlockLines->GetPropertyIndex("DPM_ID"); + idxPCB = poDataBlockLines->GetPropertyIndex("PORADOVE_CISLO_BODU"); + if (idxId < 0 || idxMy_Id < 0 || idxPCB < 0) { + CPLError(CE_Failure, CPLE_NotSupported, + "Corrupted data (%s).\n", m_pszName); + return nInvalid; + } + + poLineList = poDataBlockLines->GetFeatures(idxPCB, 1); /* reduce to first segment */ + for (int i = 0; i < ((IVFKDataBlock *) this)->GetFeatureCount(); i++) { + poFeature = (VFKFeature *) GetFeatureByIndex(i); + CPLAssert(NULL != poFeature); + id = strtoul(poFeature->GetProperty(idxId)->GetValueS(), NULL, 0); + poLine = poDataBlockLines->GetFeature(idxMy_Id, id, &poLineList); + if (!poLine || !poLine->GetGeometry()) + continue; + if (!poFeature->SetGeometry(poLine->GetGeometry())) + nInvalid++; + } + poDataBlockLines->ResetReading(); + + return nInvalid; +} + +/*! + \brief Load geometry (polygon BUD/PAR layers) + + \return number of invalid features +*/ +int VFKDataBlock::LoadGeometryPolygon() +{ + long nInvalid; + bool bIsPar, bNewRing, bFound; + + GUIntBig id, idOb; + int nCount, nCountMax; + int idxId, idxPar1, idxPar2, idxBud, idxOb, idxIdOb; + + VFKFeature *poFeature; + VFKDataBlock *poDataBlockLines1, *poDataBlockLines2; + + VFKFeatureList poLineList; + PointListArray poRingList; /* first is to be considered as exterior */ + + OGRLinearRing ogrRing; + OGRPolygon ogrPolygon; + + idxPar1 = idxPar2 = idxBud = idxOb = idxIdOb = 0; + nInvalid = 0; + if (EQUAL (m_pszName, "PAR")) { + poDataBlockLines1 = (VFKDataBlock *) m_poReader->GetDataBlock("HP"); + poDataBlockLines2 = poDataBlockLines1; + bIsPar = TRUE; + } + else { + poDataBlockLines1 = (VFKDataBlock *) m_poReader->GetDataBlock("OB"); + poDataBlockLines2 = (VFKDataBlock *) m_poReader->GetDataBlock("SBP"); + bIsPar = FALSE; + } + if (NULL == poDataBlockLines1 || NULL == poDataBlockLines2) { + CPLError(CE_Failure, CPLE_NotSupported, + "Data block %s not found.\n", m_pszName); + return nInvalid; + } + + poDataBlockLines1->LoadGeometry(); + poDataBlockLines2->LoadGeometry(); + idxId = GetPropertyIndex("ID"); + if (idxId < 0) { + CPLError(CE_Failure, CPLE_NotSupported, + "Corrupted data (%s).\n", m_pszName); + return nInvalid; + } + + if (bIsPar) { + idxPar1 = poDataBlockLines1->GetPropertyIndex("PAR_ID_1"); + idxPar2 = poDataBlockLines1->GetPropertyIndex("PAR_ID_2"); + if (idxPar1 < 0 || idxPar2 < 0) { + CPLError(CE_Failure, CPLE_NotSupported, + "Corrupted data (%s).\n", m_pszName); + return nInvalid; + } + } + else { /* BUD */ + idxIdOb = poDataBlockLines1->GetPropertyIndex("ID"); + idxBud = poDataBlockLines1->GetPropertyIndex("BUD_ID"); + idxOb = poDataBlockLines2->GetPropertyIndex("OB_ID"); + if (idxIdOb < 0 || idxBud < 0 || idxOb < 0) { + CPLError(CE_Failure, CPLE_NotSupported, + "Corrupted data (%s).\n", m_pszName); + return nInvalid; + } + } + + for (int i = 0; i < ((IVFKDataBlock *) this)->GetFeatureCount(); i++) { + poFeature = (VFKFeature *) GetFeatureByIndex(i); + CPLAssert(NULL != poFeature); + id = strtoul(poFeature->GetProperty(idxId)->GetValueS(), NULL, 0); + if (bIsPar) { + poLineList = poDataBlockLines1->GetFeatures(idxPar1, idxPar2, id); + } + else { + VFKFeature *poLineOb, *poLineSbp; + std::vector poLineListOb; + poLineListOb = poDataBlockLines1->GetFeatures(idxBud, id); + for (std::vector::const_iterator iOb = poLineListOb.begin(), eOb = poLineListOb.end(); + iOb != eOb; ++iOb) { + poLineOb = (*iOb); + idOb = strtoul(poLineOb->GetProperty(idxIdOb)->GetValueS(), NULL, 0); + poLineSbp = poDataBlockLines2->GetFeature(idxOb, idOb); + if (poLineSbp) + poLineList.push_back(poLineSbp); + } + } + if (poLineList.size() < 1) + continue; + + /* clear */ + ogrPolygon.empty(); + poRingList.clear(); + + /* collect rings (points) */ + bFound = FALSE; + nCount = 0; + nCountMax = poLineList.size() * 2; + while (poLineList.size() > 0 && nCount < nCountMax) { + bNewRing = !bFound ? TRUE : FALSE; + bFound = FALSE; + for (VFKFeatureList::iterator iHp = poLineList.begin(), eHp = poLineList.end(); + iHp != eHp; ++iHp) { + const OGRLineString *pLine = (OGRLineString *) (*iHp)->GetGeometry(); + if (pLine && AppendLineToRing(&poRingList, pLine, bNewRing)) { + bFound = TRUE; + poLineList.erase(iHp); + break; + } + } + nCount++; + } + /* create rings */ + for (PointListArray::const_iterator iRing = poRingList.begin(), eRing = poRingList.end(); + iRing != eRing; ++iRing) { + PointList *poList = *iRing; + ogrRing.empty(); + for (PointList::iterator iPoint = poList->begin(), ePoint = poList->end(); + iPoint != ePoint; ++iPoint) { + ogrRing.addPoint(&(*iPoint)); + } + ogrPolygon.addRing(&ogrRing); + } + /* set polygon */ + ogrPolygon.setCoordinateDimension(2); /* force 2D */ + if (!poFeature->SetGeometry(&ogrPolygon)) + nInvalid++; + } + + /* free ring list */ + for (PointListArray::iterator iRing = poRingList.begin(), + eRing = poRingList.end(); iRing != eRing; ++iRing) { + delete (*iRing); + *iRing = NULL; + } + poDataBlockLines1->ResetReading(); + poDataBlockLines2->ResetReading(); + + return nInvalid; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkdatablocksqlite.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkdatablocksqlite.cpp new file mode 100644 index 000000000..3c073ac5a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkdatablocksqlite.cpp @@ -0,0 +1,1091 @@ +/****************************************************************************** + * $Id: vfkdatablocksqlite.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: VFK Reader - Data block definition (SQLite) + * Purpose: Implements VFKDataBlockSQLite + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2012-2014, Martin Landa + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#include + +#include "vfkreader.h" +#include "vfkreaderp.h" + +#include "cpl_conv.h" +#include "cpl_error.h" + +/*! + \brief Load geometry (point layers) + + \return number of invalid features +*/ +int VFKDataBlockSQLite::LoadGeometryPoint() +{ + int nInvalid, rowId, nGeometries; + bool bSkipInvalid; + /* long iFID; */ + double x, y; + + CPLString osSQL; + sqlite3_stmt *hStmt; + + VFKFeatureSQLite *poFeature; + VFKReaderSQLite *poReader; + + nInvalid = nGeometries = 0; + poReader = (VFKReaderSQLite*) m_poReader; + + if (LoadGeometryFromDB()) /* try to load geometry from DB */ + return 0; + + bSkipInvalid = EQUAL(m_pszName, "OB") || EQUAL(m_pszName, "OP") || EQUAL(m_pszName, "OBBP"); + osSQL.Printf("SELECT SOURADNICE_Y,SOURADNICE_X,%s,rowid FROM %s", + FID_COLUMN, m_pszName); + hStmt = poReader->PrepareStatement(osSQL.c_str()); + + if (poReader->IsSpatial()) + poReader->ExecuteSQL("BEGIN"); + + while(poReader->ExecuteSQL(hStmt) == OGRERR_NONE) { + /* read values */ + x = -1.0 * sqlite3_column_double(hStmt, 0); /* S-JTSK coordinate system expected */ + y = -1.0 * sqlite3_column_double(hStmt, 1); +#ifdef DEBUG + const long iFID = sqlite3_column_double(hStmt, 2); +#endif + rowId = sqlite3_column_int(hStmt, 3); + + poFeature = (VFKFeatureSQLite *) GetFeatureByIndex(rowId - 1); + CPLAssert(NULL != poFeature && poFeature->GetFID() == iFID); + + /* create geometry */ + OGRPoint pt(x, y); + if (!poFeature->SetGeometry(&pt)) { + nInvalid++; + continue; + } + + /* store also geometry in DB */ + if (poReader->IsSpatial() && + SaveGeometryToDB(&pt, rowId) != OGRERR_FAILURE) + nGeometries++; + } + + /* update number of geometries in VFK_DB_TABLE table */ + UpdateVfkBlocks(nGeometries); + + if (poReader->IsSpatial()) + poReader->ExecuteSQL("COMMIT"); + + return bSkipInvalid ? 0 : nInvalid; +} + +/*! + \brief Set geometry for linestrings + + \param poLine VFK feature + \param oOGRLine line geometry + \param[in,out] bValid TRUE when feature's geometry is valid + \param[in,out] rowIdFeat list of row ids which forms linestring + \param[in,out] nGeometries number of features with valid geometry +*/ +bool VFKDataBlockSQLite::SetGeometryLineString(VFKFeatureSQLite *poLine, OGRLineString *oOGRLine, + bool& bValid, const char *ftype, + std::vector& rowIdFeat, int& nGeometries) +{ + int npoints; + VFKReaderSQLite *poReader; + + poReader = (VFKReaderSQLite*) m_poReader; + + oOGRLine->setCoordinateDimension(2); /* force 2D */ + + /* check also VFK validity */ + if (bValid) { + /* Feature types + + - '3' - line (2 points) + - '4' - linestring (at least 2 points) + - '11' - curve (at least 2 points) + - '15' - circle (3 points) + - '15 r' - circle (center point & radius) + - '16' - arc (3 points) + */ + + npoints = oOGRLine->getNumPoints(); + if (EQUAL(ftype, "3") && npoints > 2) { + /* be less pedantic, just inform user about data + * inconsistency + + bValid = FALSE; + */ + CPLDebug("OGR-VFK", + "Line (fid=" CPL_FRMT_GIB ") defined by more than two vertices", + poLine->GetFID()); + } + else if (EQUAL(ftype, "11") && npoints < 2) { + bValid = FALSE; + CPLError(CE_Warning, CPLE_AppDefined, + "Curve (fid=" CPL_FRMT_GIB ") defined by less than two vertices", + poLine->GetFID()); + } + else if (EQUAL(ftype, "15") && npoints != 3) { + bValid = FALSE; + CPLError(CE_Warning, CPLE_AppDefined, + "Circle (fid=" CPL_FRMT_GIB ") defined by invalid number of vertices (%d)", + poLine->GetFID(), oOGRLine->getNumPoints()); + } + else if (strlen(ftype) > 2 && EQUALN(ftype, "15", 2) && npoints != 1) { + bValid = FALSE; + CPLError(CE_Warning, CPLE_AppDefined, + "Circle (fid=" CPL_FRMT_GIB ") defined by invalid number of vertices (%d)", + poLine->GetFID(), oOGRLine->getNumPoints()); + } + else if (EQUAL(ftype, "16") && npoints != 3) { + bValid = FALSE; + CPLError(CE_Warning, CPLE_AppDefined, + "Arc (fid=" CPL_FRMT_GIB ") defined by invalid number of vertices (%d)", + poLine->GetFID(), oOGRLine->getNumPoints()); + } + } + + /* set geometry (NULL for invalid features) */ + if (bValid) { + if (!poLine->SetGeometry(oOGRLine, ftype)) { + bValid = FALSE; + } + } + else { + poLine->SetGeometry(NULL); + } + + /* update fid column */ + UpdateFID(poLine->GetFID(), rowIdFeat); + + /* store also geometry in DB */ + CPLAssert(0 != rowIdFeat.size()); + if (bValid && poReader->IsSpatial() && + SaveGeometryToDB(bValid ? poLine->GetGeometry() : NULL, + rowIdFeat[0]) != OGRERR_FAILURE) + nGeometries++; + + rowIdFeat.clear(); + oOGRLine->empty(); /* restore line */ + + return bValid; +} + +/*! + \brief Load geometry (linestring SBP layer) + + \return number of invalid features +*/ +int VFKDataBlockSQLite::LoadGeometryLineStringSBP() +{ + int nInvalid, nGeometries, rowId, iIdx; + CPLString szFType, szFTypeLine; + + GUIntBig id, ipcb; + bool bValid; + + std::vector rowIdFeat; + CPLString osSQL; + sqlite3_stmt *hStmt; + + VFKReaderSQLite *poReader; + VFKDataBlockSQLite *poDataBlockPoints; + VFKFeatureSQLite *poFeature, *poPoint, *poLine; + + OGRLineString oOGRLine; + + nInvalid = nGeometries = 0; + poReader = (VFKReaderSQLite*) m_poReader; + poLine = NULL; + + poDataBlockPoints = (VFKDataBlockSQLite *) m_poReader->GetDataBlock("SOBR"); + if (NULL == poDataBlockPoints) { + CPLError(CE_Failure, CPLE_FileIO, + "Data block %s not found.\n", m_pszName); + return nInvalid; + } + + poDataBlockPoints->LoadGeometry(); + + if (LoadGeometryFromDB()) /* try to load geometry from DB */ + return 0; + + osSQL.Printf("UPDATE %s SET %s = -1", m_pszName, FID_COLUMN); + poReader->ExecuteSQL(osSQL.c_str()); + bValid = TRUE; + iIdx = 0; + for (int i = 0; i < 2; i++) { + /* first collect linestrings related to HP, OB or DPM + then collect rest of linestrings */ + if (i == 0) + osSQL.Printf("SELECT BP_ID,PORADOVE_CISLO_BODU,PARAMETRY_SPOJENI,_rowid_ FROM '%s' WHERE " + "HP_ID IS NOT NULL OR OB_ID IS NOT NULL OR DPM_ID IS NOT NULL " + "ORDER BY HP_ID,OB_ID,DPM_ID,PORADOVE_CISLO_BODU", m_pszName); + else + osSQL.Printf("SELECT BP_ID,PORADOVE_CISLO_BODU,PARAMETRY_SPOJENI,_rowid_ FROM '%s' WHERE " + "OB_ID IS NULL AND HP_ID IS NULL AND DPM_ID IS NULL " + "ORDER BY ID,PORADOVE_CISLO_BODU", m_pszName); + + hStmt = poReader->PrepareStatement(osSQL.c_str()); + + if (poReader->IsSpatial()) + poReader->ExecuteSQL("BEGIN"); + + while(poReader->ExecuteSQL(hStmt) == OGRERR_NONE) { + // read values + id = sqlite3_column_double(hStmt, 0); + ipcb = sqlite3_column_double(hStmt, 1); + szFType = (char *) sqlite3_column_text(hStmt, 2); + rowId = sqlite3_column_int(hStmt, 3); + + if (ipcb == 1) { + poFeature = (VFKFeatureSQLite *) GetFeatureByIndex(iIdx); + CPLAssert(NULL != poFeature); + poFeature->SetRowId(rowId); + + /* set geometry & reset */ + if (poLine && !SetGeometryLineString(poLine, &oOGRLine, + bValid, szFTypeLine, rowIdFeat, nGeometries)) { + nInvalid++; + } + + bValid = TRUE; + poLine = poFeature; + szFTypeLine = szFType; + iIdx++; + } + + poPoint = (VFKFeatureSQLite *) poDataBlockPoints->GetFeature("ID", id); + if (poPoint) { + OGRPoint *pt = (OGRPoint *) poPoint->GetGeometry(); + if (pt) { + oOGRLine.addPoint(pt); + } + else { + CPLDebug("OGR-VFK", + "Geometry (point ID = " CPL_FRMT_GUIB ") not valid", id); + bValid = FALSE; + } + } + else { + CPLDebug("OGR-VFK", + "Point ID = " CPL_FRMT_GUIB " not found (rowid = %d)", + id, rowId); + bValid = FALSE; + } + + /* add vertex to the linestring */ + rowIdFeat.push_back(rowId); + } + + /* add last line */ + if (poLine && !SetGeometryLineString(poLine, &oOGRLine, + bValid, szFType.c_str(), rowIdFeat, nGeometries)) { + nInvalid++; + } + poLine = NULL; + + if (poReader->IsSpatial()) + poReader->ExecuteSQL("COMMIT"); + } + + /* update number of geometries in VFK_DB_TABLE table */ + UpdateVfkBlocks(nGeometries); + + return nInvalid; +} + +/*! + \brief Load geometry (linestring HP/DPM layer) + + \return number of invalid features +*/ +int VFKDataBlockSQLite::LoadGeometryLineStringHP() +{ + int nInvalid, nGeometries; + int rowId; + long iFID; + + CPLString osColumn, osSQL; + const char *vrColumn[2]; + GUIntBig vrValue[2]; + + sqlite3_stmt *hStmt; + + OGRGeometry *poOgrGeometry; + VFKReaderSQLite *poReader; + VFKDataBlockSQLite *poDataBlockLines; + VFKFeatureSQLite *poFeature, *poLine; + + nInvalid = nGeometries = 0; + poReader = (VFKReaderSQLite*) m_poReader; + + poDataBlockLines = (VFKDataBlockSQLite *) m_poReader->GetDataBlock("SBP"); + if (NULL == poDataBlockLines) { + CPLError(CE_Failure, CPLE_FileIO, + "Data block %s not found", m_pszName); + return nInvalid; + } + + poDataBlockLines->LoadGeometry(); + + if (LoadGeometryFromDB()) /* try to load geometry from DB */ + return 0; + + osColumn.Printf("%s_ID", m_pszName); + vrColumn[0] = osColumn.c_str(); + vrColumn[1] = "PORADOVE_CISLO_BODU"; + vrValue[1] = 1; /* reduce to first segment */ + + osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName); + /* TODO: handle points in DPM */ + if (EQUAL(m_pszName, "DPM")) + osSQL += " WHERE SOURADNICE_X IS NULL"; + hStmt = poReader->PrepareStatement(osSQL.c_str()); + + if (poReader->IsSpatial()) + poReader->ExecuteSQL("BEGIN"); + + while(poReader->ExecuteSQL(hStmt) == OGRERR_NONE) { + /* read values */ + vrValue[0] = sqlite3_column_double(hStmt, 0); + iFID = sqlite3_column_double(hStmt, 1); + rowId = sqlite3_column_int(hStmt, 2); + + poFeature = (VFKFeatureSQLite *) GetFeatureByIndex(rowId - 1); + CPLAssert(NULL != poFeature && poFeature->GetFID() == iFID); + + poLine = poDataBlockLines->GetFeature(vrColumn, vrValue, 2, TRUE); + if (!poLine) { + poOgrGeometry = NULL; + } + else { + poOgrGeometry = poLine->GetGeometry(); + } + if (!poOgrGeometry || !poFeature->SetGeometry(poOgrGeometry)) { + CPLDebug("OGR-VFK", "VFKDataBlockSQLite::LoadGeometryLineStringHP(): name=%s fid=%ld " + "id=" CPL_FRMT_GUIB " -> %s geometry", m_pszName, iFID, vrValue[0], + poOgrGeometry ? "invalid" : "empty"); + nInvalid++; + continue; + } + + /* store also geometry in DB */ + if (poReader->IsSpatial() && + SaveGeometryToDB(poOgrGeometry, rowId) != OGRERR_FAILURE && + poOgrGeometry) + nGeometries++; + } + + /* update number of geometries in VFK_DB_TABLE table */ + UpdateVfkBlocks(nGeometries); + + if (poReader->IsSpatial()) + poReader->ExecuteSQL("COMMIT"); + + return nInvalid; +} + +/*! + \brief Load geometry (polygon BUD/PAR layers) + + \return number of invalid features +*/ +int VFKDataBlockSQLite::LoadGeometryPolygon() +{ + int nInvalidNoLines, nInvalidNoRings, nGeometries, nBridges; + int rowId, nCount, nCountMax; + size_t nLines; + long iFID; + bool bIsPar, bNewRing, bFound; + + CPLString osSQL; + const char *vrColumn[2]; + GUIntBig vrValue[2]; + GUIntBig id, idOb; + + sqlite3_stmt *hStmt; + + VFKReaderSQLite *poReader; + VFKDataBlockSQLite *poDataBlockLines1, *poDataBlockLines2; + VFKFeatureSQLite *poFeature; + + VFKFeatureSQLiteList poLineList; + /* first is to be considered as exterior */ + PointListArray poRingList; + + std::vector poLinearRingList; + OGRPolygon ogrPolygon; + OGRLinearRing *poOgrRing; + + nInvalidNoLines = nInvalidNoRings = nGeometries = 0; + poReader = (VFKReaderSQLite*) m_poReader; + + if (EQUAL (m_pszName, "PAR")) { + poDataBlockLines1 = (VFKDataBlockSQLite *) m_poReader->GetDataBlock("HP"); + poDataBlockLines2 = poDataBlockLines1; + bIsPar = TRUE; + } + else { + poDataBlockLines1 = (VFKDataBlockSQLite *) m_poReader->GetDataBlock("OB"); + poDataBlockLines2 = (VFKDataBlockSQLite *) m_poReader->GetDataBlock("SBP"); + bIsPar = FALSE; + } + if (NULL == poDataBlockLines1 || NULL == poDataBlockLines2) { + CPLError(CE_Failure, CPLE_FileIO, + "Data block %s not found", m_pszName); + return -1; + } + + poDataBlockLines1->LoadGeometry(); + poDataBlockLines2->LoadGeometry(); + + if (LoadGeometryFromDB()) /* try to load geometry from DB */ + return 0; + + if (bIsPar) { + vrColumn[0] = "PAR_ID_1"; + vrColumn[1] = "PAR_ID_2"; + } + else { + vrColumn[0] = "OB_ID"; + vrColumn[1] = "PORADOVE_CISLO_BODU"; + vrValue[1] = 1; + } + + osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName); + hStmt = poReader->PrepareStatement(osSQL.c_str()); + + if (poReader->IsSpatial()) + poReader->ExecuteSQL("BEGIN"); + + while(poReader->ExecuteSQL(hStmt) == OGRERR_NONE) { + nBridges = 0; + + /* read values */ + id = sqlite3_column_double(hStmt, 0); + iFID = sqlite3_column_double(hStmt, 1); + rowId = sqlite3_column_int(hStmt, 2); + + poFeature = (VFKFeatureSQLite *) GetFeatureByIndex(rowId - 1); + CPLAssert(NULL != poFeature && poFeature->GetFID() == iFID); + + if (bIsPar) { + vrValue[0] = vrValue[1] = id; + poLineList = poDataBlockLines1->GetFeatures(vrColumn, vrValue, 2); + } + else { + VFKFeatureSQLite *poLineSbp; + std::vector poLineListOb; + sqlite3_stmt *hStmtOb; + + osSQL.Printf("SELECT ID FROM %s WHERE BUD_ID = " CPL_FRMT_GUIB, + poDataBlockLines1->GetName(), id); + if (poReader->IsSpatial()) { + CPLString osColumn; + + osColumn.Printf(" AND %s IS NULL", GEOM_COLUMN); + osSQL += osColumn; + } + hStmtOb = poReader->PrepareStatement(osSQL.c_str()); + + while(poReader->ExecuteSQL(hStmtOb) == OGRERR_NONE) { + idOb = sqlite3_column_double(hStmtOb, 0); + vrValue[0] = idOb; + poLineSbp = poDataBlockLines2->GetFeature(vrColumn, vrValue, 2); + if (poLineSbp) + poLineList.push_back(poLineSbp); + } + } + nLines = poLineList.size(); + if (nLines < 1) { + CPLDebug("OGR-VFK", + "%s: unable to collect rings for polygon fid = %ld (no lines)", + m_pszName, iFID); + nInvalidNoLines++; + continue; + } + + /* clear */ + ogrPolygon.empty(); + poRingList.clear(); + + /* collect rings from lines */ + bFound = FALSE; + nCount = 0; + nCountMax = nLines * 2; + while (poLineList.size() > 0 && nCount < nCountMax) { + bNewRing = !bFound ? TRUE : FALSE; + bFound = FALSE; + int i = 1; + for (VFKFeatureSQLiteList::iterator iHp = poLineList.begin(), eHp = poLineList.end(); + iHp != eHp; ++iHp, ++i) { + const OGRLineString *pLine = (OGRLineString *) (*iHp)->GetGeometry(); + if (pLine && AppendLineToRing(&poRingList, pLine, bNewRing)) { + bFound = TRUE; + poLineList.erase(iHp); + break; + } + } + nCount++; + } + CPLDebug("OGR-VFK", "%s: fid = %ld nlines = %d -> nrings = %d", m_pszName, + iFID, (int)nLines, (int)poRingList.size()); + + if (poLineList.size() > 0) { + CPLDebug("OGR-VFK", + "%s: unable to collect rings for polygon fid = %ld", + m_pszName, iFID); + nInvalidNoRings++; + continue; + } + + /* build rings */ + poLinearRingList.clear(); + int i = 1; + for (PointListArray::const_iterator iRing = poRingList.begin(), eRing = poRingList.end(); + iRing != eRing; ++iRing) { + OGRPoint *poPoint; + PointList *poList = *iRing; + + poLinearRingList.push_back(new OGRLinearRing()); + poOgrRing = poLinearRingList.back(); + CPLAssert(NULL != poOgrRing); + + for (PointList::iterator iPoint = poList->begin(), ePoint = poList->end(); + iPoint != ePoint; ++iPoint) { + poPoint = &(*iPoint); + poOgrRing->addPoint(poPoint); + } + i++; + } + + /* find exterior ring */ + if (poLinearRingList.size() > 1) { + double dArea, dMaxArea; + std::vector::iterator exteriorRing; + + exteriorRing = poLinearRingList.begin(); + dMaxArea = -1.; + for (std::vector::iterator iRing = poLinearRingList.begin(), + eRing = poLinearRingList.end(); iRing != eRing; ++iRing) { + poOgrRing = *iRing; + if (!IsRingClosed(poOgrRing)) + continue; /* skip unclosed rings */ + + dArea = poOgrRing->get_Area(); + if (dArea > dMaxArea) { + dMaxArea = dArea; + exteriorRing = iRing; + } + } + if (exteriorRing != poLinearRingList.begin()) { + std::swap(*poLinearRingList.begin(), *exteriorRing); + } + } + + /* build polygon from rings */ + for (std::vector::iterator iRing = poLinearRingList.begin(), + eRing = poLinearRingList.end(); iRing != eRing; ++iRing) { + poOgrRing = *iRing; + + /* check if ring is closed */ + if (IsRingClosed(poOgrRing)) { + ogrPolygon.addRing(poOgrRing); + } + else { + if (poOgrRing->getNumPoints() == 2) { + CPLDebug("OGR-VFK", "%s: Polygon (fid = %ld) bridge removed", + m_pszName, iFID); + nBridges++; + } + else { + CPLDebug("OGR-VFK", + "%s: Polygon (fid = %ld) unclosed ring skipped", + m_pszName, iFID); + } + } + delete poOgrRing; + *iRing = NULL; + } + + /* set polygon */ + ogrPolygon.setCoordinateDimension(2); /* force 2D */ + if (ogrPolygon.getNumInteriorRings() + nBridges != (int) poLinearRingList.size() - 1 || + !poFeature->SetGeometry(&ogrPolygon)) { + nInvalidNoRings++; + continue; + } + + /* store also geometry in DB */ + if (poReader->IsSpatial() && + SaveGeometryToDB(&ogrPolygon, rowId) != OGRERR_FAILURE) + nGeometries++; + } + + /* free ring list */ + for (PointListArray::iterator iRing = poRingList.begin(), eRing = poRingList.end(); + iRing != eRing; ++iRing) { + delete (*iRing); + *iRing = NULL; + } + + CPLDebug("OGR-VFK", "%s: nolines = %d norings = %d", + m_pszName, nInvalidNoLines, nInvalidNoRings); + + /* update number of geometries in VFK_DB_TABLE table */ + UpdateVfkBlocks(nGeometries); + + if (poReader->IsSpatial()) + poReader->ExecuteSQL("COMMIT"); + + return nInvalidNoLines + nInvalidNoRings; +} + +/*! + \brief Get feature by FID + + Modifies next feature id. + + \param nFID feature id + + \return pointer to feature definition or NULL on failure (not found) +*/ +IVFKFeature *VFKDataBlockSQLite::GetFeature(GIntBig nFID) +{ + int rowId; + CPLString osSQL; + VFKReaderSQLite *poReader; + + sqlite3_stmt *hStmt; + + if (m_nFeatureCount < 0) { + m_poReader->ReadDataRecords(this); + } + + if (nFID < 1 || nFID > m_nFeatureCount) + return NULL; + + if (m_bGeometryPerBlock && !m_bGeometry) { + LoadGeometry(); + } + + poReader = (VFKReaderSQLite*) m_poReader; + + osSQL.Printf("SELECT rowid FROM %s WHERE %s = " CPL_FRMT_GIB, + m_pszName, FID_COLUMN, nFID); + if (EQUAL(m_pszName, "SBP")) { + osSQL += " AND PORADOVE_CISLO_BODU = 1"; + } + hStmt = poReader->PrepareStatement(osSQL.c_str()); + + rowId = -1; + if (poReader->ExecuteSQL(hStmt) == OGRERR_NONE) { + rowId = sqlite3_column_int(hStmt, 0); + } + sqlite3_finalize(hStmt); + + return GetFeatureByIndex(rowId - 1); +} + +/*! + \brief Get first found feature based on it's property + + \param column property name + \param value property value + \param bGeom True to check also geometry != NULL + + \return pointer to feature definition or NULL on failure (not found) +*/ +VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char *column, GUIntBig value, + bool bGeom) +{ + int idx; + CPLString osSQL; + VFKReaderSQLite *poReader; + + sqlite3_stmt *hStmt; + + poReader = (VFKReaderSQLite*) m_poReader; + + osSQL.Printf("SELECT %s from %s WHERE %s = " CPL_FRMT_GUIB, + FID_COLUMN, m_pszName, column, value); + if (bGeom) { + CPLString osColumn; + + osColumn.Printf(" AND %s IS NOT NULL", GEOM_COLUMN); + osSQL += osColumn; + } + + hStmt = poReader->PrepareStatement(osSQL.c_str()); + if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE) + return NULL; + + idx = sqlite3_column_int(hStmt, 0) - 1; + sqlite3_finalize(hStmt); + + if (idx < 0 || idx >= m_nFeatureCount) // ? assert + return NULL; + + return (VFKFeatureSQLite *) GetFeatureByIndex(idx); +} + +/*! + \brief Get first found feature based on it's properties (AND) + + \param column array of property names + \param value array of property values + \param num number of array items + \param bGeom True to check also geometry != NULL + + \return pointer to feature definition or NULL on failure (not found) +*/ +VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char **column, GUIntBig *value, int num, + bool bGeom) +{ + int idx; + CPLString osSQL, osItem; + VFKReaderSQLite *poReader; + + sqlite3_stmt *hStmt; + + poReader = (VFKReaderSQLite*) m_poReader; + + osSQL.Printf("SELECT %s FROM %s WHERE ", FID_COLUMN, m_pszName); + for (int i = 0; i < num; i++) { + if (i > 0) + osItem.Printf(" AND %s = " CPL_FRMT_GUIB, column[i], value[i]); + else + osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]); + osSQL += osItem; + } + if (bGeom) { + osItem.Printf(" AND %s IS NOT NULL", GEOM_COLUMN); + osSQL += osItem; + } + + hStmt = poReader->PrepareStatement(osSQL.c_str()); + if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE) + return NULL; + + idx = sqlite3_column_int(hStmt, 0) - 1; /* rowid starts at 1 */ + sqlite3_finalize(hStmt); + + if (idx < 0 || idx >= m_nFeatureCount) // ? assert + return NULL; + + return (VFKFeatureSQLite *) GetFeatureByIndex(idx); +} + +/*! + \brief Get features based on properties + + \param column array of property names + \param value array of property values + \param num number of array items + + \return list of features +*/ +VFKFeatureSQLiteList VFKDataBlockSQLite::GetFeatures(const char **column, GUIntBig *value, int num) +{ + int iRowId; + CPLString osSQL, osItem; + + VFKReaderSQLite *poReader; + VFKFeatureSQLiteList fList; + + sqlite3_stmt *hStmt; + + poReader = (VFKReaderSQLite*) m_poReader; + + osSQL.Printf("SELECT rowid from %s WHERE ", m_pszName); + for (int i = 0; i < num; i++) { + if (i > 0) + osItem.Printf(" OR %s = " CPL_FRMT_GUIB, column[i], value[i]); + else + osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]); + osSQL += osItem; + } + osSQL += " ORDER BY "; + osSQL += FID_COLUMN; + + hStmt = poReader->PrepareStatement(osSQL.c_str()); + while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE) { + iRowId = sqlite3_column_int(hStmt, 0); + fList.push_back((VFKFeatureSQLite *)GetFeatureByIndex(iRowId - 1)); + } + + return fList; +} + +/*! + \brief Save geometry to DB (as WKB) + + \param poGeom pointer to OGRGeometry to be saved + \param iRowId row id to update + + \return OGRERR_NONE on success otherwise OGRERR_FAILURE +*/ +OGRErr VFKDataBlockSQLite::SaveGeometryToDB(const OGRGeometry *poGeom, int iRowId) +{ + int rc, nWKBLen; + GByte *pabyWKB; + CPLString osSQL; + + sqlite3_stmt *hStmt; + + VFKReaderSQLite *poReader; + + poReader = (VFKReaderSQLite*) m_poReader; + + if (poGeom) { + nWKBLen = poGeom->WkbSize(); + pabyWKB = (GByte *) CPLMalloc(nWKBLen + 1); + poGeom->exportToWkb(wkbNDR, pabyWKB); + + osSQL.Printf("UPDATE %s SET %s = ? WHERE rowid = %d", + m_pszName, GEOM_COLUMN, iRowId); + hStmt = poReader->PrepareStatement(osSQL.c_str()); + + rc = sqlite3_bind_blob(hStmt, 1, pabyWKB, nWKBLen, CPLFree); + if (rc != SQLITE_OK) { + sqlite3_finalize(hStmt); + CPLError(CE_Failure, CPLE_AppDefined, + "Storing geometry in DB failed"); + return OGRERR_FAILURE; + } + } + else { /* invalid */ + osSQL.Printf("UPDATE %s SET %s = NULL WHERE rowid = %d", + m_pszName, GEOM_COLUMN, iRowId); + hStmt = poReader->PrepareStatement(osSQL.c_str()); + } + + return poReader->ExecuteSQL(hStmt); /* calls sqlite3_finalize() */ +} + +/*! + \brief Load geometry from DB + + \return TRUE geometry successfully loaded otherwise FALSE +*/ +bool VFKDataBlockSQLite::LoadGeometryFromDB() +{ + int nInvalid, nGeometries, nGeometriesCount, nBytes, rowId; +#ifdef DEBUG + long iFID; +#endif + bool bSkipInvalid; + + CPLString osSQL; + + OGRGeometry *poGeometry; + + VFKFeatureSQLite *poFeature; + VFKReaderSQLite *poReader; + + sqlite3_stmt *hStmt; + + poReader = (VFKReaderSQLite*) m_poReader; + + if (!poReader->IsSpatial()) /* check if DB is spatial */ + return FALSE; + + osSQL.Printf("SELECT num_geometries FROM %s WHERE table_name = '%s'", + VFK_DB_TABLE, m_pszName); + hStmt = poReader->PrepareStatement(osSQL.c_str()); + if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE) + return FALSE; + nGeometries = sqlite3_column_int(hStmt, 0); + sqlite3_finalize(hStmt); + + if (nGeometries < 1) + return FALSE; + + bSkipInvalid = EQUAL(m_pszName, "OB") || EQUAL(m_pszName, "OP") || EQUAL(m_pszName, "OBBP"); + + /* load geometry from DB */ + nInvalid = nGeometriesCount = 0; + osSQL.Printf("SELECT %s,rowid,%s FROM %s ", + GEOM_COLUMN, FID_COLUMN, m_pszName); + if (EQUAL(m_pszName, "SBP")) + osSQL += "WHERE PORADOVE_CISLO_BODU = 1 "; + osSQL += "ORDER BY "; + osSQL += FID_COLUMN; + hStmt = poReader->PrepareStatement(osSQL.c_str()); + + rowId = 0; + while(poReader->ExecuteSQL(hStmt) == OGRERR_NONE) { + rowId++; // =sqlite3_column_int(hStmt, 1); +#ifdef DEBUG + iFID = +#endif + sqlite3_column_double(hStmt, 2); + + poFeature = (VFKFeatureSQLite *) GetFeatureByIndex(rowId - 1); + CPLAssert(NULL != poFeature && poFeature->GetFID() == iFID); + + // read geometry from DB + nBytes = sqlite3_column_bytes(hStmt, 0); + if (nBytes > 0 && + OGRGeometryFactory::createFromWkb((GByte*) sqlite3_column_blob(hStmt, 0), + NULL, &poGeometry, nBytes) == OGRERR_NONE) { + nGeometriesCount++; + if (!poFeature->SetGeometry(poGeometry)) { + nInvalid++; + } + delete poGeometry; + } + else { + nInvalid++; + } + } + + CPLDebug("OGR-VFK", "%s: %d geometries loaded from DB", + m_pszName, nGeometriesCount); + + if (nGeometriesCount != nGeometries) { + CPLError(CE_Warning, CPLE_AppDefined, + "%s: %d geometries loaded (should be %d)", + m_pszName, nGeometriesCount, nGeometries); + } + + if (nInvalid > 0 && !bSkipInvalid) { + CPLError(CE_Warning, CPLE_AppDefined, + "%s: %d features with invalid or empty geometry", + m_pszName, nInvalid); + } + + return TRUE; +} + +/*! + \brief Update VFK_DB_TABLE table + + \param nGeometries number of geometries to update +*/ +void VFKDataBlockSQLite::UpdateVfkBlocks(int nGeometries) { + int nFeatCount; + CPLString osSQL; + + VFKReaderSQLite *poReader; + + poReader = (VFKReaderSQLite*) m_poReader; + + /* update number of features in VFK_DB_TABLE table */ + nFeatCount = (int)GetFeatureCount(); + if (nFeatCount > 0) { + osSQL.Printf("UPDATE %s SET num_features = %d WHERE table_name = '%s'", + VFK_DB_TABLE, nFeatCount, m_pszName); + poReader->ExecuteSQL(osSQL.c_str()); + } + + /* update number of geometries in VFK_DB_TABLE table */ + if (nGeometries > 0) { + CPLDebug("OGR-VFK", + "VFKDataBlockSQLite::UpdateVfkBlocks(): name=%s -> " + "%d geometries saved to internal DB", m_pszName, nGeometries); + + osSQL.Printf("UPDATE %s SET num_geometries = %d WHERE table_name = '%s'", + VFK_DB_TABLE, nGeometries, m_pszName); + poReader->ExecuteSQL(osSQL.c_str()); + } +} + +/*! + \brief Update feature id (see SBP) + + \param iFID feature id to set up + \param rowId list of rows to update +*/ +void VFKDataBlockSQLite::UpdateFID(GIntBig iFID, std::vector rowId) +{ + CPLString osSQL, osValue; + VFKReaderSQLite *poReader; + + poReader = (VFKReaderSQLite*) m_poReader; + + /* update number of geometries in VFK_DB_TABLE table */ + osSQL.Printf("UPDATE %s SET %s = " CPL_FRMT_GIB " WHERE rowid IN (", + m_pszName, FID_COLUMN, iFID); + for (size_t i = 0; i < rowId.size(); i++) { + if (i > 0) + osValue.Printf(",%d", rowId[i]); + else + osValue.Printf("%d", rowId[i]); + osSQL += osValue; + } + osSQL += ")"; + + poReader->ExecuteSQL(osSQL.c_str()); +} + +/*! + \brief Check is ring is closed + + \param poRing pointer to OGRLinearRing to check + + \return TRUE if closed otherwise FALSE +*/ +bool VFKDataBlockSQLite::IsRingClosed(const OGRLinearRing *poRing) +{ + int nPoints; + + nPoints = poRing->getNumPoints(); + if (nPoints < 3) + return FALSE; + + if (poRing->getX(0) == poRing->getX(nPoints-1) && + poRing->getY(0) == poRing->getY(nPoints-1)) + return TRUE; + + return FALSE; +} + +/*! + \brief Get primary key + + \return property name or NULL +*/ +const char *VFKDataBlockSQLite::GetKey() const +{ + const char *pszKey; + const VFKPropertyDefn *poPropDefn; + + if (GetPropertyCount() > 1) { + poPropDefn = GetProperty(0); + pszKey = poPropDefn->GetName(); + if (EQUAL(pszKey, "ID")) + return pszKey; + } + + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkfeature.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkfeature.cpp new file mode 100644 index 000000000..1d39a3498 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkfeature.cpp @@ -0,0 +1,695 @@ +/****************************************************************************** + * $Id: vfkfeature.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: VFK Reader - Feature definition + * Purpose: Implements IVFKFeature/VFKFeature class. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2009-2010, 2012-2015, Martin Landa + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#include "vfkreader.h" +#include "vfkreaderp.h" + +#include "cpl_conv.h" +#include "cpl_error.h" + +/*! + \brief IVFKFeature constructor + + \param poDataBlock pointer to VFKDataBlock instance +*/ +IVFKFeature::IVFKFeature(IVFKDataBlock *poDataBlock) +{ + CPLAssert(NULL != poDataBlock); + m_poDataBlock = poDataBlock; + + m_nFID = -1; + m_nGeometryType = poDataBlock->GetGeometryType(); + m_bGeometry = FALSE; + m_bValid = FALSE; + m_paGeom = NULL; +} + +/*! + \brief IVFKFeature destructor +*/ +IVFKFeature::~IVFKFeature() +{ + if (m_paGeom) + delete m_paGeom; + + m_poDataBlock = NULL; +} + +/*! + \brief Set feature geometry type +*/ +void IVFKFeature::SetGeometryType(OGRwkbGeometryType nGeomType) +{ + m_nGeometryType = nGeomType; +} + +/*! + \brief Set feature id + + FID: 0 for next, -1 for same + + \param nFID feature id +*/ +void IVFKFeature::SetFID(GIntBig nFID) +{ + if (m_nFID > 0) { + m_nFID = nFID; + } + else { + m_nFID = m_poDataBlock->GetFeatureCount() + 1; + } +} + +/*! + \brief Set feature geometry + + Also checks if given geometry is valid + + \param poGeom pointer to OGRGeometry + \param ftype geometry VFK type + + \return TRUE on valid feature or otherwise FALSE +*/ +bool IVFKFeature::SetGeometry(OGRGeometry *poGeom, const char *ftype) +{ + m_bGeometry = TRUE; + + delete m_paGeom; + m_paGeom = NULL; + m_bValid = TRUE; + + if (!poGeom) { + return m_bValid; + } + + /* check empty geometries */ + if (m_nGeometryType == wkbNone && poGeom->IsEmpty()) { + CPLError(CE_Warning, CPLE_AppDefined, + "%s: empty geometry fid = " CPL_FRMT_GIB, + m_poDataBlock->GetName(), m_nFID); + m_bValid = FALSE; + } + + /* check coordinates */ + if (m_nGeometryType == wkbPoint) { + double x, y; + x = ((OGRPoint *) poGeom)->getX(); + y = ((OGRPoint *) poGeom)->getY(); + if (x > -430000 || x < -910000 || + y > -930000 || y < -1230000) { + CPLDebug("OGR-VFK", "%s: invalid point fid = " CPL_FRMT_GIB, + m_poDataBlock->GetName(), m_nFID); + m_bValid = FALSE; + } + } + + /* check degenerated polygons */ + if (m_nGeometryType == wkbPolygon) { + OGRLinearRing *poRing; + poRing = ((OGRPolygon *) poGeom)->getExteriorRing(); + if (!poRing || poRing->getNumPoints() < 3) { + CPLDebug("OGR-VFK", "%s: invalid polygon fid = " CPL_FRMT_GIB, + m_poDataBlock->GetName(), m_nFID); + m_bValid = FALSE; + } + } + + if (m_bValid) { + if (ftype) { + OGRPoint pt; + OGRGeometry *poGeomCurved; + OGRCircularString poGeomString; + + poGeomCurved = NULL; + if (EQUAL(ftype, "15") || EQUAL(ftype, "16")) { /* -> circle or arc */ + int npoints; + + npoints = ((OGRLineString *) poGeom)->getNumPoints(); + for (int i = 0; i < npoints; i++) { + ((OGRLineString *) poGeom)->getPoint(i, &pt); + poGeomString.addPoint(&pt); + } + if (EQUAL(ftype, "15")) { + /* compute center and radius of a circle */ + double x[3], y[3]; + double m1, n1, m2, n2, c1, c2, mx; + double c_x, c_y; + + for (int i = 0; i < npoints; i++) { + ((OGRLineString *) poGeom)->getPoint(i, &pt); + x[i] = pt.getX(); + y[i] = pt.getY(); + } + + m1 = (x[0] + x[1]) / 2.0; + n1 = (y[0] + y[1]) / 2.0; + + m2 = (x[0] + x[2]) / 2.0; + n2 = (y[0] + y[2]) / 2.0; + + c1 = (x[1] - x[0]) * m1 + (y[1] - y[0]) * n1; + c2 = (x[2] - x[0]) * m2 + (y[2] - y[0]) * n2; + + mx = (x[1] - x[0]) * (y[2] - y[0]) + (y[1] - y[0]) * (x[0] - x[2]); + + c_x = (c1 * (y[2] - y[0]) + c2 * (y[0] - y[1])) / mx; + c_y = (c1 * (x[0] - x[2]) + c2 * (x[1] - x[0])) / mx; + + /* compute a new intermediate point */ + pt.setX(c_x - (x[1] - c_x)); + pt.setY(c_y - (y[1] - c_y)); + poGeomString.addPoint(&pt); + + /* add last point */ + ((OGRLineString *) poGeom)->getPoint(0, &pt); + poGeomString.addPoint(&pt); + + } + } + else if (strlen(ftype) > 2 && EQUALN(ftype, "15", 2)) { /* -> circle with radius */ + float r; + char s[3]; /* 15 */ + + r = 0; + if (2 != sscanf(ftype, "%s %f", s, &r) || r < 0) { + CPLDebug("OGR-VFK", "%s: invalid circle (unknown or negative radius) " + "fid = " CPL_FRMT_GIB, m_poDataBlock->GetName(), m_nFID); + m_bValid = FALSE; + } + else { + double c_x, c_y; + + ((OGRLineString *) poGeom)->getPoint(0, &pt); + c_x = pt.getX(); + c_y = pt.getY(); + + /* define first point on a circle */ + pt.setX(c_x + r); + pt.setY(c_y); + poGeomString.addPoint(&pt); + + /* define second point on a circle */ + pt.setX(c_x); + pt.setY(c_y + r); + poGeomString.addPoint(&pt); + + /* define third point on a circle */ + pt.setX(c_x - r); + pt.setY(c_y); + poGeomString.addPoint(&pt); + + /* define fourth point on a circle */ + pt.setX(c_x); + pt.setY(c_y - r); + poGeomString.addPoint(&pt); + + /* define last point (=first) on a circle */ + pt.setX(c_x + r); + pt.setY(c_y); + poGeomString.addPoint(&pt); + } + + } + else if (EQUAL(ftype, "11")) { /* curve */ + int npoints; + + npoints = ((OGRLineString *) poGeom)->getNumPoints(); + if (npoints > 2) { /* circular otherwise line string */ + for (int i = 0; i < npoints; i++) { + ((OGRLineString *) poGeom)->getPoint(i, &pt); + poGeomString.addPoint(&pt); + } + } + } + + if (!poGeomString.IsEmpty()) + poGeomCurved = poGeomString.CurveToLine(); + + if (poGeomCurved) { + int npoints; + + npoints = ((OGRLineString *) poGeomCurved)->getNumPoints(); + CPLDebug("OGR-VFK", "%s: curve (type=%s) to linestring (npoints=%d) fid = " CPL_FRMT_GIB, + m_poDataBlock->GetName(), ftype, + npoints, m_nFID); + if (npoints > 1) + m_paGeom = (OGRGeometry *) poGeomCurved->clone(); + delete poGeomCurved; + } + } + + if (!m_paGeom) { + /* check degenerated linestrings */ + if (m_nGeometryType == wkbLineString) { + int npoints; + + npoints = ((OGRLineString *) poGeom)->getNumPoints(); + if (npoints < 2) { + CPLError(CE_Warning, CPLE_AppDefined, + "%s: invalid linestring (%d vertices) fid = " CPL_FRMT_GIB, + m_poDataBlock->GetName(), npoints, m_nFID); + m_bValid = FALSE; + } + } + + if (m_bValid) + m_paGeom = (OGRGeometry *) poGeom->clone(); /* make copy */ + } + } + + return m_bValid; +} + +/*! + \brief Get feature geometry + + \return pointer to OGRGeometry or NULL on error +*/ +OGRGeometry *IVFKFeature::GetGeometry() +{ + if (m_nGeometryType != wkbNone && !m_bGeometry) + LoadGeometry(); + + return m_paGeom; +} + + +/*! + \brief Load geometry + + \return TRUE on success or FALSE on failure +*/ +bool IVFKFeature::LoadGeometry() +{ + const char *pszName; + + if (m_bGeometry) + return TRUE; + + pszName = m_poDataBlock->GetName(); + + if (EQUAL (pszName, "SOBR") || + EQUAL (pszName, "OBBP") || + EQUAL (pszName, "SPOL") || + EQUAL (pszName, "OB") || + EQUAL (pszName, "OP") || + EQUAL (pszName, "OBPEJ")) { + /* -> wkbPoint */ + + return LoadGeometryPoint(); + } + else if (EQUAL (pszName, "SBP")) { + /* -> wkbLineString */ + return LoadGeometryLineStringSBP(); + } + else if (EQUAL (pszName, "HP") || + EQUAL (pszName, "DPM")) { + /* -> wkbLineString */ + return LoadGeometryLineStringHP(); + } + else if (EQUAL (pszName, "PAR") || + EQUAL (pszName, "BUD")) { + /* -> wkbPolygon */ + return LoadGeometryPolygon(); + } + + return FALSE; +} + +/*! + \brief VFKFeature constructor + + \param poDataBlock pointer to VFKDataBlock instance +*/ +VFKFeature::VFKFeature(IVFKDataBlock *poDataBlock, GIntBig iFID) : IVFKFeature(poDataBlock) +{ + m_nFID = iFID; + m_propertyList.assign(poDataBlock->GetPropertyCount(), VFKProperty()); + CPLAssert(size_t (poDataBlock->GetPropertyCount()) == m_propertyList.size()); +} + +/*! + \brief Set feature properties + + \param pszLine pointer to line containing feature definition + + \return TRUE on success or FALSE on failure +*/ +bool VFKFeature::SetProperties(const char *pszLine) +{ + unsigned int iIndex, nLength; + const char *poChar, *poProp; + char* pszProp; + bool inString; + + std::vector oPropList; + + pszProp = NULL; + + for (poChar = pszLine; *poChar != '\0' && *poChar != ';'; poChar++) + /* skip data block name */ + ; + if (*poChar == '\0') + return FALSE; /* nothing to read */ + + poChar++; /* skip ';' after data block name*/ + + /* read properties into the list */ + poProp = poChar; + iIndex = nLength = 0; + inString = FALSE; + while(*poChar != '\0') { + if (*poChar == '"' && + (*(poChar-1) == ';' || *(poChar+1) == ';' || *(poChar+1) == '\0')) { + poChar++; /* skip '"' */ + inString = inString ? FALSE : TRUE; + if (inString) { + poProp = poChar; + if (*poChar == '"' && (*(poChar+1) == ';' || *(poChar+1) == '\0')) { + poChar++; + inString = FALSE; + } + } + if (*poChar == '\0') + break; + } + if (*poChar == ';' && !inString) { + pszProp = (char *) CPLRealloc(pszProp, nLength + 1); + if (nLength > 0) + strncpy(pszProp, poProp, nLength); + pszProp[nLength] = '\0'; + oPropList.push_back(pszProp); + iIndex++; + poProp = ++poChar; + nLength = 0; + } + else { + poChar++; + nLength++; + } + } + /* append last property */ + if (inString) { + nLength--; /* ignore '"' */ + } + pszProp = (char *) CPLRealloc(pszProp, nLength + 1); + if (nLength > 0) + strncpy(pszProp, poProp, nLength); + pszProp[nLength] = '\0'; + oPropList.push_back(pszProp); + + /* set properties from the list */ + if (oPropList.size() != (size_t) m_poDataBlock->GetPropertyCount()) { + /* try to read also invalid records */ + CPLError(CE_Warning, CPLE_AppDefined, + "%s: invalid number of properties %d should be %d", + m_poDataBlock->GetName(), + (int) oPropList.size(), m_poDataBlock->GetPropertyCount()); + return FALSE; + } + iIndex = 0; + for (std::vector::iterator ip = oPropList.begin(); + ip != oPropList.end(); ++ip) { + SetProperty(iIndex++, (*ip).c_str()); + } + + /* set fid + if (EQUAL(m_poDataBlock->GetName(), "SBP")) { + GUIntBig id; + const VFKProperty *poVfkProperty; + + poVfkProperty = GetProperty("PORADOVE_CISLO_BODU"); + if (poVfkProperty) + { + id = strtoul(poVfkProperty->GetValueS(), NULL, 0); + if (id == 1) + SetFID(0); + else + SetFID(-1); + } + } + else { + SetFID(0); + } + */ + CPLFree(pszProp); + + return TRUE; +} + +/*! + \brief Set feature property + + \param iIndex property index + \param pszValue property value + + \return TRUE on success + \return FALSE on failure +*/ +bool VFKFeature::SetProperty(int iIndex, const char *pszValue) +{ + if (iIndex < 0 || iIndex >= m_poDataBlock->GetPropertyCount() || + size_t(iIndex) >= m_propertyList.size()) + return FALSE; + + if (strlen(pszValue) < 1) + m_propertyList[iIndex] = VFKProperty(); + else { + OGRFieldType fType; + + const char *pszEncoding; + char *pszValueEnc; + + fType = m_poDataBlock->GetProperty(iIndex)->GetType(); + switch (fType) { + case OFTInteger: + m_propertyList[iIndex] = VFKProperty(atoi(pszValue)); + break; + case OFTReal: + m_propertyList[iIndex] = VFKProperty(CPLAtof(pszValue)); + break; + default: + pszEncoding = m_poDataBlock->GetProperty(iIndex)->GetEncoding(); + if (pszEncoding) { + pszValueEnc = CPLRecode(pszValue, pszEncoding, + CPL_ENC_UTF8); + m_propertyList[iIndex] = VFKProperty(pszValueEnc); + CPLFree(pszValueEnc); + } + else { + m_propertyList[iIndex] = VFKProperty(pszValue); + } + break; + } + } + return TRUE; +} + +/*! + \brief Get property value by index + + \param iIndex property index + + \return property value + \return NULL on error +*/ +const VFKProperty *VFKFeature::GetProperty(int iIndex) const +{ + if (iIndex < 0 || iIndex >= m_poDataBlock->GetPropertyCount() || + size_t(iIndex) >= m_propertyList.size()) + return NULL; + + const VFKProperty* poProperty = &m_propertyList[iIndex]; + return poProperty; +} + +/*! + \brief Get property value by name + + \param pszName property name + + \return property value + \return NULL on error +*/ +const VFKProperty *VFKFeature::GetProperty(const char *pszName) const +{ + return GetProperty(m_poDataBlock->GetPropertyIndex(pszName)); +} + +/*! + \brief Load geometry (point layers) + + \todo Really needed? + + \return TRUE on success + \return FALSE on failure +*/ +bool VFKFeature::LoadGeometryPoint() +{ + double x, y; + int i_idxX, i_idxY; + + i_idxY = m_poDataBlock->GetPropertyIndex("SOURADNICE_Y"); + i_idxX = m_poDataBlock->GetPropertyIndex("SOURADNICE_X"); + if (i_idxY < 0 || i_idxX < 0) + return FALSE; + + x = -1.0 * GetProperty(i_idxY)->GetValueD(); + y = -1.0 * GetProperty(i_idxX)->GetValueD(); + OGRPoint pt(x, y); + SetGeometry(&pt); + + return TRUE; +} + +/*! + \brief Load geometry (linestring SBP layer) + + \todo Really needed? + + \return TRUE on success or FALSE on failure +*/ +bool VFKFeature::LoadGeometryLineStringSBP() +{ + int id, idxId, idxBp_Id, idxPCB, ipcb; + + VFKDataBlock *poDataBlockPoints; + VFKFeature *poPoint, *poLine; + + OGRLineString OGRLine; + + poDataBlockPoints = (VFKDataBlock *) m_poDataBlock->GetReader()->GetDataBlock("SOBR"); + if (!poDataBlockPoints) + return FALSE; + + idxId = poDataBlockPoints->GetPropertyIndex("ID"); + idxBp_Id = m_poDataBlock->GetPropertyIndex("BP_ID"); + idxPCB = m_poDataBlock->GetPropertyIndex("PORADOVE_CISLO_BODU"); + if (idxId < 0 || idxBp_Id < 0 || idxPCB < 0) + return false; + + poLine = this; + while (TRUE) + { + id = poLine->GetProperty(idxBp_Id)->GetValueI(); + ipcb = poLine->GetProperty(idxPCB)->GetValueI(); + if (OGRLine.getNumPoints() > 0 && ipcb == 1) + { + m_poDataBlock->GetPreviousFeature(); /* push back */ + break; + } + + poPoint = poDataBlockPoints->GetFeature(idxId, id); + if (!poPoint) + { + continue; + } + OGRPoint *pt = (OGRPoint *) poPoint->GetGeometry(); + OGRLine.addPoint(pt); + + poLine = (VFKFeature *) m_poDataBlock->GetNextFeature(); + if (!poLine) + break; + }; + + OGRLine.setCoordinateDimension(2); /* force 2D */ + SetGeometry(&OGRLine); + + /* reset reading */ + poDataBlockPoints->ResetReading(); + + return TRUE; +} + +/*! + \brief Load geometry (linestring HP/DPM layer) + + \todo Really needed? + + \return TRUE on success or FALSE on failure +*/ +bool VFKFeature::LoadGeometryLineStringHP() +{ + int id, idxId, idxHp_Id; + VFKDataBlock *poDataBlockLines; + VFKFeature *poLine; + + poDataBlockLines = (VFKDataBlock *) m_poDataBlock->GetReader()->GetDataBlock("SBP"); + if (!poDataBlockLines) + return FALSE; + + idxId = m_poDataBlock->GetPropertyIndex("ID"); + idxHp_Id = poDataBlockLines->GetPropertyIndex("HP_ID"); + if (idxId < 0 || idxHp_Id < 0) + return FALSE; + + id = GetProperty(idxId)->GetValueI(); + poLine = poDataBlockLines->GetFeature(idxHp_Id, id); + if (!poLine || !poLine->GetGeometry()) + return FALSE; + + SetGeometry(poLine->GetGeometry()); + poDataBlockLines->ResetReading(); + + return TRUE; +} + +/*! + \brief Load geometry (polygon BUD/PAR layers) + + \todo Implement (really needed?) + + \return TRUE on success or FALSE on failure +*/ +bool VFKFeature::LoadGeometryPolygon() +{ + return FALSE; +} +OGRErr VFKFeature::LoadProperties(OGRFeature *poFeature) +{ + for (int iField = 0; iField < m_poDataBlock->GetPropertyCount(); iField++) { + if (GetProperty(iField)->IsNull()) + continue; + OGRFieldType fType = poFeature->GetDefnRef()->GetFieldDefn(iField)->GetType(); + if (fType == OFTInteger) + poFeature->SetField(iField, + GetProperty(iField)->GetValueI()); + else if (fType == OFTReal) + poFeature->SetField(iField, + GetProperty(iField)->GetValueD()); + else + poFeature->SetField(iField, + GetProperty(iField)->GetValueS()); + } + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkfeaturesqlite.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkfeaturesqlite.cpp new file mode 100644 index 000000000..b66630097 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkfeaturesqlite.cpp @@ -0,0 +1,244 @@ +/****************************************************************************** + * $Id: vfkfeaturesqlite.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: VFK Reader - Feature definition (SQLite) + * Purpose: Implements VFKFeatureSQLite class. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2012-2013, Martin Landa + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#include "vfkreader.h" +#include "vfkreaderp.h" + +#include "cpl_conv.h" +#include "cpl_error.h" + +/*! + \brief VFKFeatureSQLite constructor (from DB) + + Read VFK feature from DB + + \param poDataBlock pointer to related IVFKDataBlock +*/ +VFKFeatureSQLite::VFKFeatureSQLite(IVFKDataBlock *poDataBlock) : IVFKFeature(poDataBlock) +{ + m_hStmt = NULL; + m_iRowId = (int)m_poDataBlock->GetFeatureCount() + 1; /* starts at 1 */ + + /* set FID from DB */ + SetFIDFromDB(); /* -> m_nFID */ +} + +/*! + \brief VFKFeatureSQLite constructor + + \param poDataBlock pointer to related IVFKDataBlock + \param iRowId feature DB rowid (starts at 1) + \param nFID feature id +*/ +VFKFeatureSQLite::VFKFeatureSQLite(IVFKDataBlock *poDataBlock, int iRowId, GIntBig nFID) : IVFKFeature(poDataBlock) +{ + m_hStmt = NULL; + m_iRowId = iRowId; + m_nFID = nFID; +} + +/*! + \brief Read FID from DB +*/ +OGRErr VFKFeatureSQLite::SetFIDFromDB() +{ + CPLString osSQL; + + osSQL.Printf("SELECT %s FROM %s WHERE rowid = %d", + FID_COLUMN, m_poDataBlock->GetName(), m_iRowId); + if (ExecuteSQL(osSQL.c_str()) != OGRERR_NONE) + return OGRERR_FAILURE; + + m_nFID = sqlite3_column_int(m_hStmt, 0); + + FinalizeSQL(); + + return OGRERR_NONE; +} + +/*! + \brief Set DB row id + + \param iRowId row id to be set +*/ +void VFKFeatureSQLite::SetRowId(int iRowId) +{ + m_iRowId = iRowId; +} + +/*! + \brief Finalize SQL statement +*/ +void VFKFeatureSQLite::FinalizeSQL() +{ + sqlite3_finalize(m_hStmt); + m_hStmt = NULL; +} + +/*! + \brief Execute SQL (select) statement + + \param pszSQLCommand SQL command string + + \return OGRERR_NONE on success or OGRERR_FAILURE on error +*/ +OGRErr VFKFeatureSQLite::ExecuteSQL(const char *pszSQLCommand) +{ + int rc; + + sqlite3 *poDB; + + VFKReaderSQLite *poReader = (VFKReaderSQLite *) m_poDataBlock->GetReader(); + poDB = poReader->m_poDB; + + rc = sqlite3_prepare(poDB, pszSQLCommand, strlen(pszSQLCommand), + &m_hStmt, NULL); + if (rc != SQLITE_OK) { + CPLError(CE_Failure, CPLE_AppDefined, + "In ExecuteSQL(): sqlite3_prepare(%s):\n %s", + pszSQLCommand, sqlite3_errmsg(poDB)); + + if(m_hStmt != NULL) { + FinalizeSQL(); + } + return OGRERR_FAILURE; + } + rc = sqlite3_step(m_hStmt); + if (rc != SQLITE_ROW) { + CPLError(CE_Failure, CPLE_AppDefined, + "In ExecuteSQL(): sqlite3_step(%s):\n %s", + pszSQLCommand, sqlite3_errmsg(poDB)); + + if (m_hStmt) { + FinalizeSQL(); + } + + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/*! + \brief VFKFeatureSQLite constructor (derived from VFKFeature) + + Read VFK feature from VFK file and insert it into DB +*/ +VFKFeatureSQLite::VFKFeatureSQLite(const VFKFeature *poVFKFeature) : IVFKFeature(poVFKFeature->m_poDataBlock) +{ + m_nFID = poVFKFeature->m_nFID; + m_hStmt = NULL; + m_iRowId = (int)m_poDataBlock->GetFeatureCount() + 1; /* starts at 1 */ +} + +/*! + \brief Load geometry (point layers) + + \todo Implement (really needed?) + + \return TRUE on success or FALSE on failure +*/ +bool VFKFeatureSQLite::LoadGeometryPoint() +{ + return FALSE; +} + +/*! + \brief Load geometry (linestring SBP layer) + + \todo Implement (really needed?) + + \return TRUE on success or FALSE on failure +*/ +bool VFKFeatureSQLite::LoadGeometryLineStringSBP() +{ + return FALSE; +} + +/*! + \brief Load geometry (linestring HP/DPM layer) + + \todo Implement (really needed?) + + \return TRUE on success or FALSE on failure +*/ +bool VFKFeatureSQLite::LoadGeometryLineStringHP() +{ + return FALSE; +} + +/*! + \brief Load geometry (polygon BUD/PAR layers) + + \todo Implement (really needed?) + + \return TRUE on success or FALSE on failure +*/ +bool VFKFeatureSQLite::LoadGeometryPolygon() +{ + return FALSE; +} + +/*! + \brief Load feature properties from DB + + \param poFeature pointer to OGR feature + + \return OGRERR_NONE on success or OGRERR_FAILURE on failure +*/ +OGRErr VFKFeatureSQLite::LoadProperties(OGRFeature *poFeature) +{ + CPLString osSQL; + + osSQL.Printf("SELECT * FROM %s WHERE rowid = %d", + m_poDataBlock->GetName(), m_iRowId); + if (ExecuteSQL(osSQL.c_str()) != OGRERR_NONE) + return OGRERR_FAILURE; + + for (int iField = 0; iField < m_poDataBlock->GetPropertyCount(); iField++) { + if (sqlite3_column_type(m_hStmt, iField) == SQLITE_NULL) /* skip null values */ + continue; + OGRFieldType fType = poFeature->GetDefnRef()->GetFieldDefn(iField)->GetType(); + if (fType == OFTInteger) + poFeature->SetField(iField, + sqlite3_column_int(m_hStmt, iField)); + else if (fType == OFTReal) + poFeature->SetField(iField, + sqlite3_column_double(m_hStmt, iField)); + else + poFeature->SetField(iField, + (const char *) sqlite3_column_text(m_hStmt, iField)); + } + + FinalizeSQL(); + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkproperty.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkproperty.cpp new file mode 100644 index 000000000..d39d2e396 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkproperty.cpp @@ -0,0 +1,130 @@ +/****************************************************************************** + * $Id: vfkproperty.cpp 27995 2014-11-21 20:14:07Z rouault $ + * + * Project: VFK Reader - Property definition + * Purpose: Implements VFKProperty class. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2009-2010, Martin Landa + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#include "vfkreader.h" +#include "vfkreaderp.h" + +#include "cpl_conv.h" +#include "cpl_error.h" + +/*! + \brief Set VFK property (null) +*/ +VFKProperty::VFKProperty() + : m_bIsNull(TRUE), m_nValue(0), m_dValue(0.0) +{ +} + +/*! + \brief Set VFK property (integer) +*/ +VFKProperty::VFKProperty(int iValue) + : m_bIsNull(FALSE), m_nValue(iValue), m_dValue(0.0) +{ +} + +/*! + \brief Set VFK property (double) +*/ +VFKProperty::VFKProperty(double dValue) + : m_bIsNull(FALSE), m_nValue(0), m_dValue(dValue) +{ +} + +/*! + \brief Set VFK property (string) +*/ +VFKProperty::VFKProperty(const char *pszValue) + : m_bIsNull(FALSE), m_nValue(0), m_dValue(0.0), m_strValue(0 != pszValue ? pszValue : "") +{ +} + +/*! + \brief Set VFK property (string) +*/ +VFKProperty::VFKProperty(CPLString const& strValue) + : m_bIsNull(FALSE), m_nValue(0), m_dValue(0.0), m_strValue(strValue) +{ +} + +/*! + \brief VFK property destructor +*/ +VFKProperty::~VFKProperty() +{ +} + +/*! + \brief Copy constructor. +*/ +VFKProperty::VFKProperty(VFKProperty const& other) + : m_bIsNull(other.m_bIsNull), + m_nValue(other.m_nValue), m_dValue(other.m_dValue), m_strValue(other.m_strValue) +{ +} + +/*! + \brief Assignment operator. +*/ +VFKProperty& VFKProperty::operator=(VFKProperty const& other) +{ + if (&other != this) { + m_bIsNull = other.m_bIsNull; + m_nValue = other.m_nValue; + m_dValue = other.m_dValue; + m_strValue = other.m_strValue; + } + return *this; +} + +/*! + \brief Get string property + + \param escape TRUE to escape characters for SQL + + \return string buffer +*/ +const char *VFKProperty::GetValueS(bool escape) const +{ + size_t ipos; + + if (!escape) + return m_strValue.c_str(); + + CPLString strValue(m_strValue); + ipos = 0; + while (std::string::npos != (ipos = strValue.find("'", ipos))) { + strValue.replace(ipos, 1, "\'\'", 2); + ipos += 2; + } + + return CPLSPrintf("%s", strValue.c_str()); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkpropertydefn.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkpropertydefn.cpp new file mode 100644 index 000000000..f15a07197 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkpropertydefn.cpp @@ -0,0 +1,133 @@ +/****************************************************************************** + * $Id: vfkpropertydefn.cpp 25201 2012-11-04 11:11:31Z martinl $ + * + * Project: VFK Reader - Data block property definition + * Purpose: Implements VFKPropertyDefn class. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2009-2010, 2012, Martin Landa + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#include "vfkreader.h" +#include "vfkreaderp.h" + +#include "cpl_conv.h" +#include "cpl_error.h" + +/*! + \brief VFKPropertyDefn constructor + + \param pszName property name + \param pszType property type (original, string) + \param bLatin2 TRUE for "ISO-8859-2" otherwise "WINDOWS-1250" is used (only for "text" type) +*/ +VFKPropertyDefn::VFKPropertyDefn(const char *pszName, const char *pszType, + bool bLatin2) +{ + char *poChar, *poWidth, *pszWidth; + int nLength; + + m_pszName = CPLStrdup(pszName); + m_pszType = CPLStrdup(pszType); + + poWidth = poChar = m_pszType + 1; + for (nLength = 0; *poChar && *poChar != '.'; nLength++, poChar++) + ; + + /* width */ + pszWidth = (char *) CPLMalloc(nLength+1); + strncpy(pszWidth, poWidth, nLength); + pszWidth[nLength] = '\0'; + + m_nWidth = atoi(pszWidth); + CPLFree(pszWidth); + + /* precision */ + m_nPrecision = 0; + + /* type */ + m_pszEncoding = NULL; + if (*m_pszType == 'N') { + if (*poChar == '.') { + m_eFType = OFTReal; + m_nPrecision = atoi(poChar+1); + } + else { + if (m_nWidth < 10) + m_eFType = OFTInteger; + else { + m_eFType = OFTString; + } + } + } + else if (*m_pszType == 'T') { + /* string */ + m_eFType = OFTString; + m_pszEncoding = bLatin2 ? CPLStrdup("ISO-8859-2") : CPLStrdup("WINDOWS-1250"); + } + else if (*m_pszType == 'D') { + /* date */ + /* m_eFType = OFTDateTime; */ + m_eFType = OFTString; + m_nWidth = 25; + } + else { + /* unknown - string */ + m_eFType = OFTString; + m_pszEncoding = bLatin2 ? CPLStrdup("ISO-8859-2") : CPLStrdup("WINDOWS-1250"); + } +} + +/*! + \brief VFKPropertyDefn destructor +*/ +VFKPropertyDefn::~VFKPropertyDefn() +{ + CPLFree(m_pszName); + CPLFree(m_pszType); + if (m_pszEncoding) + CPLFree(m_pszEncoding); +} + +/*! + \brief Get SQL data type + + \return string with data type ("text" by default) +*/ +CPLString VFKPropertyDefn::GetTypeSQL() const +{ + switch(m_eFType) { + case OFTInteger: + return CPLString("integer"); + case OFTReal: + return CPLString("real"); + case OFTString: + if (m_pszType[0] == 'N') + return CPLString("integer"); + else + return CPLString("text"); + default: + return CPLString("text"); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkreader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkreader.cpp new file mode 100644 index 000000000..251a9e011 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkreader.cpp @@ -0,0 +1,565 @@ +/****************************************************************************** + * $Id: vfkreader.cpp 27973 2014-11-16 17:40:22Z martinl $ + * + * Project: VFK Reader + * Purpose: Implements VFKReader class. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2009-2014, Martin Landa + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#include "vfkreader.h" +#include "vfkreaderp.h" + +#include "cpl_conv.h" +#include "cpl_error.h" +#include "cpl_string.h" + +#define SUPPORT_GEOMETRY + +#ifdef SUPPORT_GEOMETRY +# include "ogr_geometry.h" +#endif + +static char *GetDataBlockName(const char *); + +/*! + \brief IVFKReader desctructor +*/ +IVFKReader::~IVFKReader() +{ +} + +/*! + \brief Create new instance of VFKReader + + \return pointer to VFKReader instance +*/ +IVFKReader *CreateVFKReader(const char *pszFilename) +{ + return new VFKReaderSQLite(pszFilename); +} + +/*! + \brief VFKReader constructor +*/ +VFKReader::VFKReader(const char *pszFilename) +{ + m_nDataBlockCount = 0; + m_papoDataBlock = NULL; + m_bLatin2 = TRUE; /* encoding ISO-8859-2 or WINDOWS-1250 */ + + /* open VFK file for reading */ + CPLAssert(NULL != pszFilename); + m_pszFilename = CPLStrdup(pszFilename); + m_poFD = VSIFOpen(m_pszFilename, "rb"); + if (m_poFD == NULL) { + CPLError(CE_Failure, CPLE_OpenFailed, + "Failed to open file %s.", m_pszFilename); + } +} + +/*! + \brief VFKReader destructor +*/ +VFKReader::~VFKReader() +{ + CPLFree(m_pszFilename); + + if (m_poFD) + VSIFClose(m_poFD); + + /* clear data blocks */ + for (int i = 0; i < m_nDataBlockCount; i++) + delete m_papoDataBlock[i]; + CPLFree(m_papoDataBlock); +} + +char *GetDataBlockName(const char *pszLine) +{ + int n; + const char *pszLineChar; + char *pszBlockName; + + for (pszLineChar = pszLine + 2, n = 0; *pszLineChar != '\0' && *pszLineChar != ';'; pszLineChar++, n++) + ; + + if (*pszLineChar == '\0') + return NULL; + + pszBlockName = (char *) CPLMalloc(n + 1); + strncpy(pszBlockName, pszLine + 2, n); + pszBlockName[n] = '\0'; + + return pszBlockName; +} + +/*! + \brief Read a line from file + + \param bRecode do recoding + + \return a NULL terminated string which should be freed with CPLFree(). +*/ +char *VFKReader::ReadLine(bool bRecode) +{ + const char *pszRawLine; + char *pszLine; + + pszRawLine = CPLReadLine(m_poFD); + if (pszRawLine == NULL) + return NULL; + + if (bRecode) + pszLine = CPLRecode(pszRawLine, + m_bLatin2 ? "ISO-8859-2" : "WINDOWS-1250", + CPL_ENC_UTF8); + else { + pszLine = (char *) CPLMalloc(strlen(pszRawLine) + 1); + strcpy(pszLine, pszRawLine); + } + + return pszLine; +} + +/*! + \brief Load data block definitions (&B) + + Call VFKReader::OpenFile() before this function. + + \return number of data blocks or -1 on error +*/ +int VFKReader::ReadDataBlocks() +{ + char *pszLine, *pszBlockName; + bool bInHeader; + + IVFKDataBlock *poNewDataBlock; + + CPLAssert(NULL != m_pszFilename); + + VSIFSeek(m_poFD, 0, SEEK_SET); + bInHeader = TRUE; + while ((pszLine = ReadLine()) != NULL) { + if (strlen(pszLine) < 2 || pszLine[0] != '&') { + CPLFree(pszLine); + continue; + } + + if (pszLine[1] == 'B') { + if (bInHeader) + bInHeader = FALSE; /* 'B' record closes the header section */ + + pszBlockName = GetDataBlockName(pszLine); + if (pszBlockName == NULL) { + CPLError(CE_Failure, CPLE_NotSupported, + "Corrupted data - line\n%s\n", pszLine); + CPLFree(pszLine); + return -1; + } + poNewDataBlock = (IVFKDataBlock *) CreateDataBlock(pszBlockName); + CPLFree(pszBlockName); + poNewDataBlock->SetGeometryType(); + poNewDataBlock->SetProperties(pszLine); + AddDataBlock(poNewDataBlock, pszLine); + } + else if (pszLine[1] == 'H') { + /* header - metadata */ + AddInfo(pszLine); + } + else if (pszLine[1] == 'K' && strlen(pszLine) == 2) { + /* end of file */ + CPLFree(pszLine); + break; + } + else if (bInHeader && pszLine[1] == 'D') { + /* process 'D' records in the header section */ + AddInfo(pszLine); + } + + CPLFree(pszLine); + } + + return m_nDataBlockCount; +} + + +/*! + \brief Load data records (&D) + + Call VFKReader::OpenFile() before this function. + + \param poDataBlock limit to selected data block or NULL for all + + \return number of data records or -1 on error +*/ +int VFKReader::ReadDataRecords(IVFKDataBlock *poDataBlock) +{ + const char *pszName; + char *pszBlockName, *pszLine; + CPLString osBlockNameLast; + int nLength, iLine, nSkipped, nDupl, nRecords; + int iDataBlock; + bool bInHeader; + + IVFKDataBlock *poDataBlockCurrent; + + CPLString pszMultiLine; + + VFKFeature *poNewFeature; + + if (poDataBlock) { /* read only given data block */ + poDataBlockCurrent = poDataBlock; + poDataBlockCurrent->SetFeatureCount(0); + pszName = poDataBlockCurrent->GetName(); + } + else { /* read all data blocks */ + pszName = NULL; + for (iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++) { + poDataBlockCurrent = GetDataBlock(iDataBlock); + poDataBlockCurrent->SetFeatureCount(0); + } + poDataBlockCurrent = NULL; + } + + VSIFSeek(m_poFD, 0, SEEK_SET); + iLine = nSkipped = nDupl = nRecords = 0; + bInHeader = TRUE; + while ((pszLine = ReadLine()) != NULL) { + iLine++; + nLength = strlen(pszLine); + if (nLength < 2) { + CPLFree(pszLine); + continue; + } + + if (bInHeader && pszLine[1] == 'B') + bInHeader = FALSE; /* 'B' record closes the header section */ + + if (pszLine[1] == 'D') { + if (bInHeader) { + /* skip 'D' records from the header section, already + * processed as metadata */ + CPLFree(pszLine); + continue; + } + + pszBlockName = GetDataBlockName(pszLine); + + if (pszBlockName && (!pszName || EQUAL(pszBlockName, pszName))) { + /* merge lines if needed + + See http://en.wikipedia.org/wiki/ISO/IEC_8859 + - \244 - general currency sign + */ + if (pszLine[nLength - 1] == '\244') { + /* remove \244 (currency sign) from string */ + pszLine[nLength - 1] = '\0'; + + pszMultiLine.clear(); + pszMultiLine = pszLine; + CPLFree(pszLine); + + while ((pszLine = ReadLine()) != NULL && + pszLine[strlen(pszLine) - 1] == '\244') { + /* append line */ + pszMultiLine += pszLine; + /* remove 0244 (currency sign) from string */ + pszMultiLine.erase(pszMultiLine.size() - 1); + + CPLFree(pszLine); + } + pszMultiLine += pszLine; + CPLFree(pszLine); + + nLength = pszMultiLine.size(); + pszLine = (char *) CPLMalloc(nLength + 1); + strncpy(pszLine, pszMultiLine.c_str(), nLength); + pszLine[nLength] = '\0'; + } + + if (!poDataBlock && pszBlockName) { /* read all data blocks */ + if (osBlockNameLast.empty() || + !EQUAL(pszBlockName, osBlockNameLast.c_str())) { + poDataBlockCurrent = GetDataBlock(pszBlockName); + osBlockNameLast = CPLString(pszBlockName); + } + } + if (!poDataBlockCurrent) { + CPLFree(pszBlockName); + continue; // assert ? + } + + poNewFeature = new VFKFeature(poDataBlockCurrent, + poDataBlockCurrent->GetFeatureCount() + 1); + if (poNewFeature->SetProperties(pszLine)) { + if (AddFeature(poDataBlockCurrent, poNewFeature) != OGRERR_NONE) { + CPLDebug("OGR-VFK", + "%s: duplicated VFK data recored skipped (line %d).\n%s\n", + pszBlockName, iLine, pszLine); + poDataBlockCurrent->SetIncRecordCount(RecordDuplicated); + } + else { + nRecords++; + poDataBlockCurrent->SetIncRecordCount(RecordValid); + } + delete poNewFeature; + } + else { + CPLDebug("OGR-VFK", + "Invalid VFK data record skipped (line %d).\n%s\n", iLine, pszLine); + poDataBlockCurrent->SetIncRecordCount(RecordSkipped); + } + } + CPLFree(pszBlockName); + } + else if (pszLine[1] == 'K' && strlen(pszLine) == 2) { + /* end of file */ + CPLFree(pszLine); + break; + } + + CPLFree(pszLine); + } + + for (iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++) { + poDataBlockCurrent = GetDataBlock(iDataBlock); + + if (poDataBlock && poDataBlock != poDataBlockCurrent) + continue; + + nSkipped = poDataBlockCurrent->GetRecordCount(RecordSkipped); + nDupl = poDataBlockCurrent->GetRecordCount(RecordDuplicated); + if (nSkipped > 0) + CPLError(CE_Warning, CPLE_AppDefined, + "%s: %d invalid VFK data records skipped", + poDataBlockCurrent->GetName(), nSkipped); + if (nDupl > 0) + CPLError(CE_Warning, CPLE_AppDefined, + "%s: %d duplicated VFK data records skipped", + poDataBlockCurrent->GetName(), nDupl); + + CPLDebug("OGR-VFK", "VFKReader::ReadDataRecords(): name=%s n=%d", + poDataBlockCurrent->GetName(), + poDataBlockCurrent->GetRecordCount(RecordValid)); + } + + return nRecords; +} + +IVFKDataBlock *VFKReader::CreateDataBlock(const char *pszBlockName) +{ + return (IVFKDataBlock *) new VFKDataBlock(pszBlockName, (IVFKReader *) this); +} + +/*! + \brief Add new data block + + \param poNewDataBlock pointer to VFKDataBlock instance + \param pszDefn unused (FIXME ?) +*/ +void VFKReader::AddDataBlock(IVFKDataBlock *poNewDataBlock, + CPL_UNUSED const char *pszDefn) +{ + m_nDataBlockCount++; + + m_papoDataBlock = (IVFKDataBlock **) + CPLRealloc(m_papoDataBlock, sizeof (IVFKDataBlock *) * m_nDataBlockCount); + m_papoDataBlock[m_nDataBlockCount-1] = poNewDataBlock; +} + +/*! + \brief Add feature + + \param poDataBlock pointer to VFKDataBlock instance + \param poFeature pointer to VFKFeature instance +*/ +OGRErr VFKReader::AddFeature(IVFKDataBlock *poDataBlock, VFKFeature *poFeature) +{ + poDataBlock->AddFeature(poFeature); + return OGRERR_NONE; +} + +/*! + \brief Get data block + + \param i index (starting with 0) + + \return pointer to VFKDataBlock instance or NULL on failure +*/ +IVFKDataBlock *VFKReader::GetDataBlock(int i) const +{ + if (i < 0 || i >= m_nDataBlockCount) + return NULL; + + return m_papoDataBlock[i]; +} + +/*! + \brief Get data block + + \param pszName data block name + + \return pointer to VFKDataBlock instance or NULL on failure +*/ +IVFKDataBlock *VFKReader::GetDataBlock(const char *pszName) const +{ + for (int i = 0; i < m_nDataBlockCount; i++) { + if (EQUAL(GetDataBlock(i)->GetName(), pszName)) + return GetDataBlock(i); + } + + return NULL; +} + +/*! + \brief Load geometry (loop datablocks) + + \return number of invalid features +*/ +int VFKReader::LoadGeometry() +{ + long int nfeatures; + + nfeatures = 0; + for (int i = 0; i < m_nDataBlockCount; i++) { + nfeatures += m_papoDataBlock[i]->LoadGeometry(); + } + + CPLDebug("OGR_VFK", "VFKReader::LoadGeometry(): invalid=%ld", nfeatures); + + return nfeatures; +} + +/*! + \brief Add info + + \param pszLine pointer to line +*/ +void VFKReader::AddInfo(const char *pszLine) +{ + int i, iKeyLength, iValueLength; + int nSkip, nOffset; + char *pszKey, *pszValue, *pszValueEnc; + const char *poChar, *poKey; + CPLString key, value; + + if (pszLine[1] == 'H') + nOffset = 2; + else + nOffset = 1; /* &DKATUZE */ + + poChar = poKey = pszLine + nOffset; /* &H */ + iKeyLength = 0; + while (*poChar != '\0' && *poChar != ';') { + iKeyLength++; + poChar ++; + } + if (*poChar == '\0') + return; + + pszKey = (char *) CPLMalloc(iKeyLength + 1); + strncpy(pszKey, poKey, iKeyLength); + pszKey[iKeyLength] = '\0'; + + poChar++; /* skip ; */ + + iValueLength = 0; + nSkip = 3; /* &H + ; */ + while (*poChar != '\0') { + if (*poChar == '"' && iValueLength == 0) { + nSkip++; + } + else { + iValueLength++; + } + poChar++; + } + if (nSkip > 3) + iValueLength--; + + pszValue = (char *) CPLMalloc(iValueLength + 1); + for (i = 0; i < iValueLength; i++) { + pszValue[i] = pszLine[iKeyLength+nSkip+i]; + if (pszValue[i] == '"') { + pszValue[i] = '\''; /* " -> ' */ + } + } + + pszValue[iValueLength] = '\0'; + + + /* recode values, assuming Latin2 */ + if (EQUAL(pszKey, "CODEPAGE")) { + if (!EQUAL(pszValue, "WE8ISO8859P2")) + m_bLatin2 = FALSE; + } + + pszValueEnc = CPLRecode(pszValue, + m_bLatin2 ? "ISO-8859-2" : "WINDOWS-1250", + CPL_ENC_UTF8); + if (poInfo.find(pszKey) == poInfo.end() ) { + poInfo[pszKey] = pszValueEnc; + } + else { + int nCount; + size_t iFound; + char *pszKeyUniq; + + /* max. number of duplicated keys can be 101 */ + pszKeyUniq = (char *) CPLMalloc(strlen(pszKey) + 5); + + nCount = 1; /* assuming at least one match */ + for(std::map::iterator i = poInfo.begin(); + i != poInfo.end(); ++i) { + iFound = i->first.find("_"); + if (iFound != std::string::npos && + EQUALN(pszKey, i->first.c_str(), iFound)) + nCount += 1; + } + + sprintf(pszKeyUniq, "%s_%d", pszKey, nCount); + poInfo[pszKeyUniq] = pszValueEnc; + } + + CPLFree(pszKey); + CPLFree(pszValue); + CPLFree(pszValueEnc); +} + +/*! + \brief Get info + + \param key key string + + \return pointer to value string or NULL if key not found +*/ +const char *VFKReader::GetInfo(const char *key) +{ + if (poInfo.find(key) == poInfo.end()) + return NULL; + + return poInfo[key].c_str(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkreader.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkreader.h new file mode 100644 index 000000000..99fc96888 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkreader.h @@ -0,0 +1,372 @@ +/****************************************************************************** + * $Id: vfkreader.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: VFK Reader + * Purpose: Public Declarations for OGR free VFK Reader code. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2009-2014, Martin Landa + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_OGR_VFK_VFKREADER_H_INCLUDED +#define GDAL_OGR_VFK_VFKREADER_H_INCLUDED + +#include +#include + +#include "ogrsf_frmts.h" + +#include "cpl_port.h" +#include "cpl_minixml.h" +#include "cpl_string.h" + +#include "sqlite3.h" + +class IVFKReader; +class IVFKDataBlock; +class VFKFeature; +class VFKFeatureSQLite; + +typedef std::vector VFKFeatureList; +typedef std::vector VFKFeatureSQLiteList; + +#define FID_COLUMN "ogr_fid" +#define GEOM_COLUMN "geometry" + +#define VFK_DB_HEADER "vfk_header" +#define VFK_DB_TABLE "vfk_tables" + + +enum RecordType { RecordValid, RecordSkipped, RecordDuplicated }; + +/************************************************************************/ +/* VFKProperty */ +/************************************************************************/ +class CPL_DLL VFKProperty +{ +private: + bool m_bIsNull; + + int m_nValue; + double m_dValue; + CPLString m_strValue; + +public: + VFKProperty(); + explicit VFKProperty(int); + explicit VFKProperty(double); + explicit VFKProperty(const char*); + explicit VFKProperty(CPLString const&); + virtual ~VFKProperty(); + + VFKProperty(VFKProperty const& other); + VFKProperty& operator=(VFKProperty const& other); + + bool IsNull() const { return m_bIsNull; } + int GetValueI() const { return m_nValue; } + double GetValueD() const { return m_dValue; } + const char *GetValueS(bool = FALSE) const; +}; + +/************************************************************************/ +/* IVFKFeature */ +/************************************************************************/ +class CPL_DLL IVFKFeature +{ +protected: + IVFKDataBlock *m_poDataBlock; + GIntBig m_nFID; + OGRwkbGeometryType m_nGeometryType; + bool m_bGeometry; + bool m_bValid; + OGRGeometry *m_paGeom; + + virtual bool LoadGeometryPoint() = 0; + virtual bool LoadGeometryLineStringSBP() = 0; + virtual bool LoadGeometryLineStringHP() = 0; + virtual bool LoadGeometryPolygon() = 0; + +public: + IVFKFeature(IVFKDataBlock *); + virtual ~IVFKFeature(); + + GIntBig GetFID() const { return m_nFID; } + void SetFID(GIntBig); + void SetGeometryType(OGRwkbGeometryType); + + bool IsValid() const { return m_bValid; } + + IVFKDataBlock *GetDataBlock() const { return m_poDataBlock; } + OGRwkbGeometryType GetGeometryType() const { return m_nGeometryType; } + bool SetGeometry(OGRGeometry *, const char * = NULL); + OGRGeometry *GetGeometry(); + + bool LoadGeometry(); + virtual OGRErr LoadProperties(OGRFeature *) = 0; +}; + +/************************************************************************/ +/* VFKFeature */ +/************************************************************************/ +class CPL_DLL VFKFeature : public IVFKFeature +{ +private: + typedef std::vector VFKPropertyList; + + VFKPropertyList m_propertyList; + + bool SetProperty(int, const char *); + + friend class VFKFeatureSQLite; + + bool LoadGeometryPoint(); + bool LoadGeometryLineStringSBP(); + bool LoadGeometryLineStringHP(); + bool LoadGeometryPolygon(); + +public: + VFKFeature(IVFKDataBlock *, GIntBig); + + bool SetProperties(const char *); + const VFKProperty *GetProperty(int) const; + const VFKProperty *GetProperty(const char *) const; + + OGRErr LoadProperties(OGRFeature *); + + bool AppendLineToRing(int, const OGRLineString *); +}; + +/************************************************************************/ +/* VFKFeatureSQLite */ +/************************************************************************/ +class CPL_DLL VFKFeatureSQLite : public IVFKFeature +{ +private: + int m_iRowId; /* rowid in DB */ + sqlite3_stmt *m_hStmt; + + bool LoadGeometryPoint(); + bool LoadGeometryLineStringSBP(); + bool LoadGeometryLineStringHP(); + bool LoadGeometryPolygon(); + + OGRErr SetFIDFromDB(); + OGRErr ExecuteSQL(const char *); + void FinalizeSQL(); + +public: + VFKFeatureSQLite(IVFKDataBlock *); + VFKFeatureSQLite(IVFKDataBlock *, int, GIntBig); + VFKFeatureSQLite(const VFKFeature *); + + OGRErr LoadProperties(OGRFeature *); + void SetRowId(int); +}; + +/************************************************************************/ +/* VFKPropertyDefn */ +/************************************************************************/ +class CPL_DLL VFKPropertyDefn +{ +private: + char *m_pszName; + + char *m_pszType; + char *m_pszEncoding; + OGRFieldType m_eFType; + + int m_nWidth; + int m_nPrecision; + +public: + VFKPropertyDefn(const char*, const char *, bool); + virtual ~VFKPropertyDefn(); + + const char *GetName() const { return m_pszName; } + int GetWidth() const { return m_nWidth; } + int GetPrecision() const { return m_nPrecision; } + OGRFieldType GetType() const { return m_eFType; } + CPLString GetTypeSQL() const; + GBool IsIntBig() const { return m_pszType[0] == 'N'; } + const char *GetEncoding() const { return m_pszEncoding; } +}; + +/************************************************************************/ +/* IVFKDataBlock */ +/************************************************************************/ +class CPL_DLL IVFKDataBlock +{ +private: + IVFKFeature **m_papoFeature; + + int m_nPropertyCount; + VFKPropertyDefn **m_papoProperty; + + int AddProperty(const char *, const char *); + +protected: + typedef std::vector PointList; + typedef std::vector PointListArray; + + char *m_pszName; + bool m_bGeometry; + + OGRwkbGeometryType m_nGeometryType; + bool m_bGeometryPerBlock; + + int m_nFeatureCount; + int m_iNextFeature; + + IVFKReader *m_poReader; + + GIntBig m_nRecordCount[3]; + + bool AppendLineToRing(PointListArray *, const OGRLineString *, bool, bool = FALSE); + int LoadData(); + + virtual int LoadGeometryPoint() = 0; + virtual int LoadGeometryLineStringSBP() = 0; + virtual int LoadGeometryLineStringHP() = 0; + virtual int LoadGeometryPolygon() = 0; + +public: + IVFKDataBlock(const char *, const IVFKReader *); + virtual ~IVFKDataBlock(); + + const char *GetName() const { return m_pszName; } + + int GetPropertyCount() const { return m_nPropertyCount; } + VFKPropertyDefn *GetProperty(int) const; + void SetProperties(const char *); + int GetPropertyIndex(const char *) const; + + GIntBig GetFeatureCount(); + void SetFeatureCount(int, bool = FALSE); + IVFKFeature *GetFeatureByIndex(int) const; + IVFKFeature *GetFeature(GIntBig); + void AddFeature(IVFKFeature *); + + void ResetReading(int iIdx = -1); + IVFKFeature *GetNextFeature(); + IVFKFeature *GetPreviousFeature(); + IVFKFeature *GetFirstFeature(); + IVFKFeature *GetLastFeature(); + int SetNextFeature(const IVFKFeature *); + + OGRwkbGeometryType SetGeometryType(); + OGRwkbGeometryType GetGeometryType() const; + + int LoadGeometry(); + + IVFKReader *GetReader() const { return m_poReader; } + int GetRecordCount(RecordType = RecordValid) const; + void SetIncRecordCount(RecordType); +}; + +/************************************************************************/ +/* VFKDataBlock */ +/************************************************************************/ +class CPL_DLL VFKDataBlock : public IVFKDataBlock +{ +private: + int LoadGeometryPoint(); + int LoadGeometryLineStringSBP(); + int LoadGeometryLineStringHP(); + int LoadGeometryPolygon(); + +public: + VFKDataBlock(const char *pszName, const IVFKReader *poReader) : IVFKDataBlock(pszName, poReader) {} + + VFKFeature *GetFeature(int, GUIntBig, VFKFeatureList* = NULL); + VFKFeatureList GetFeatures(int, GUIntBig); + VFKFeatureList GetFeatures(int, int, GUIntBig); + + GIntBig GetFeatureCount(const char *, const char *); +}; + +/************************************************************************/ +/* VFKDataBlockSQLite */ +/************************************************************************/ +class CPL_DLL VFKDataBlockSQLite : public IVFKDataBlock +{ +private: + bool SetGeometryLineString(VFKFeatureSQLite *, OGRLineString *, + bool&, const char *, + std::vector&, int&); + + int LoadGeometryPoint(); + int LoadGeometryLineStringSBP(); + int LoadGeometryLineStringHP(); + int LoadGeometryPolygon(); + + bool LoadGeometryFromDB(); + OGRErr SaveGeometryToDB(const OGRGeometry *, int); + + bool IsRingClosed(const OGRLinearRing *); + void UpdateVfkBlocks(int); + void UpdateFID(GIntBig, std::vector); + +public: + VFKDataBlockSQLite(const char *pszName, const IVFKReader *poReader) : IVFKDataBlock(pszName, poReader) {} + + const char *GetKey() const; + IVFKFeature *GetFeature(GIntBig); + VFKFeatureSQLite *GetFeature(const char *, GUIntBig, bool = FALSE); + VFKFeatureSQLite *GetFeature(const char **, GUIntBig *, int, bool = FALSE); + VFKFeatureSQLiteList GetFeatures(const char **, GUIntBig *, int); +}; + +/************************************************************************/ +/* IVFKReader */ +/************************************************************************/ +class CPL_DLL IVFKReader +{ +private: + virtual void AddInfo(const char *) = 0; + +protected: + virtual IVFKDataBlock *CreateDataBlock(const char *) = 0; + virtual void AddDataBlock(IVFKDataBlock * = NULL, const char * = NULL) = 0; + virtual OGRErr AddFeature(IVFKDataBlock * = NULL, VFKFeature * = NULL) = 0; + +public: + virtual ~IVFKReader(); + + virtual bool IsLatin2() const = 0; + virtual bool IsSpatial() const = 0; + virtual bool IsPreProcessed() const = 0; + virtual int ReadDataBlocks() = 0; + virtual int ReadDataRecords(IVFKDataBlock * = NULL) = 0; + virtual int LoadGeometry() = 0; + + virtual int GetDataBlockCount() const = 0; + virtual IVFKDataBlock *GetDataBlock(int) const = 0; + virtual IVFKDataBlock *GetDataBlock(const char *) const = 0; + + virtual const char *GetInfo(const char *) = 0; +}; + +IVFKReader *CreateVFKReader(const char *); + +#endif // GDAL_OGR_VFK_VFKREADER_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkreaderp.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkreaderp.h new file mode 100644 index 000000000..4e8a9ac74 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkreaderp.h @@ -0,0 +1,123 @@ +/****************************************************************************** + * $Id: vfkreaderp.h 26911 2014-02-01 11:46:09Z martinl $ + * + * Project: VFK Reader + * Purpose: Private Declarations for OGR free VFK Reader code. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2009-2010, 2012-2013, Martin Landa + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_OGR_VFK_VFKREADERP_H_INCLUDED +#define GDAL_OGR_VFK_VFKREADERP_H_INCLUDED + +#include +#include + +#include "vfkreader.h" +#include "ogr_api.h" + +#include "sqlite3.h" + +class VFKReader; + +/************************************************************************/ +/* VFKReader */ +/************************************************************************/ +class VFKReader : public IVFKReader +{ +private: + bool m_bLatin2; + + FILE *m_poFD; + char *ReadLine(bool = FALSE); + + void AddInfo(const char *); + +protected: + char *m_pszFilename; + int m_nDataBlockCount; + IVFKDataBlock **m_papoDataBlock; + + IVFKDataBlock *CreateDataBlock(const char *); + void AddDataBlock(IVFKDataBlock *, const char *); + OGRErr AddFeature(IVFKDataBlock *, VFKFeature *); + + /* metadata */ + std::map poInfo; + +public: + VFKReader(const char *); + virtual ~VFKReader(); + + bool IsLatin2() const { return m_bLatin2; } + bool IsSpatial() const { return FALSE; } + bool IsPreProcessed() const { return FALSE; } + int ReadDataBlocks(); + int ReadDataRecords(IVFKDataBlock * = NULL); + int LoadGeometry(); + + int GetDataBlockCount() const { return m_nDataBlockCount; } + IVFKDataBlock *GetDataBlock(int) const; + IVFKDataBlock *GetDataBlock(const char *) const; + + const char *GetInfo(const char *); +}; + +/************************************************************************/ +/* VFKReaderSQLite */ +/************************************************************************/ + +class VFKReaderSQLite : public VFKReader +{ +private: + char *m_pszDBname; + sqlite3 *m_poDB; + bool m_bSpatial; + bool m_bNewDb; + + IVFKDataBlock *CreateDataBlock(const char *); + void AddDataBlock(IVFKDataBlock *, const char *); + OGRErr AddFeature(IVFKDataBlock *, VFKFeature *); + + void StoreInfo2DB(); + + void CreateIndex(const char *, const char *, const char *, bool = TRUE); + + friend class VFKFeatureSQLite; +public: + VFKReaderSQLite(const char *); + virtual ~VFKReaderSQLite(); + + bool IsSpatial() const { return m_bSpatial; } + bool IsPreProcessed() const { return !m_bNewDb; } + int ReadDataBlocks(); + int ReadDataRecords(IVFKDataBlock * = NULL); + + sqlite3_stmt *PrepareStatement(const char *); + OGRErr ExecuteSQL(const char *, bool = FALSE); + OGRErr ExecuteSQL(sqlite3_stmt *); +}; + +#endif // GDAL_OGR_VFK_VFKREADERP_H_INCLUDED diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkreadersqlite.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkreadersqlite.cpp new file mode 100644 index 000000000..c62ef9b02 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vfk/vfkreadersqlite.cpp @@ -0,0 +1,647 @@ +/****************************************************************************** + * $Id: vfkreadersqlite.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: VFK Reader (SQLite) + * Purpose: Implements VFKReaderSQLite class. + * Author: Martin Landa, landa.martin gmail.com + * + ****************************************************************************** + * Copyright (c) 2012-2014, Martin Landa + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi.h" + +#include "vfkreader.h" +#include "vfkreaderp.h" + +#include "cpl_conv.h" +#include "cpl_error.h" + +#include + +#define SUPPORT_GEOMETRY + +#ifdef SUPPORT_GEOMETRY +# include "ogr_geometry.h" +#endif + +/*! + \brief VFKReaderSQLite constructor +*/ +VFKReaderSQLite::VFKReaderSQLite(const char *pszFilename) : VFKReader(pszFilename) +{ + const char *pszDbNameConf; + CPLString pszDbName; + CPLString osCommand; + VSIStatBufL sStatBufDb, sStatBufVfk; + + /* open tmp SQLite DB (re-use DB file if already exists) */ + pszDbNameConf = CPLGetConfigOption("OGR_VFK_DB_NAME", NULL); + if (pszDbNameConf) { + pszDbName = pszDbNameConf; + } + else { + pszDbName = CPLResetExtension(m_pszFilename, "db"); + } + m_pszDBname = new char [pszDbName.length()+1]; + std::strcpy(m_pszDBname, pszDbName.c_str()); + CPLDebug("OGR-VFK", "Using internal DB: %s", + m_pszDBname); + + if (CSLTestBoolean(CPLGetConfigOption("OGR_VFK_DB_SPATIAL", "YES"))) + m_bSpatial = TRUE; /* build geometry from DB */ + else + m_bSpatial = FALSE; /* store also geometry in DB */ + + m_bNewDb = TRUE; + if (VSIStatL(pszDbName, &sStatBufDb) == 0) { + if (CSLTestBoolean(CPLGetConfigOption("OGR_VFK_DB_OVERWRITE", "NO"))) { + m_bNewDb = TRUE; /* overwrite existing DB */ + CPLDebug("OGR-VFK", "Internal DB (%s) already exists and will be overwritten", + m_pszDBname); + VSIUnlink(pszDbName); + } + else { + if (VSIStatL(pszFilename, &sStatBufVfk) == 0 && + sStatBufVfk.st_mtime > sStatBufDb.st_mtime) { + CPLDebug("OGR-VFK", + "Found %s but ignoring because it appears\n" + "be older than the associated VFK file.", + pszDbName.c_str()); + m_bNewDb = TRUE; + VSIUnlink(pszDbName); + } + else { + m_bNewDb = FALSE; /* re-use exising DB */ + } + } + } + + /* + if (m_bNewDb) { + CPLError(CE_Warning, CPLE_AppDefined, + "INFO: No internal SQLite DB found. Reading VFK data may take some time..."); + } + */ + + CPLDebug("OGR-VFK", "New DB: %s Spatial: %s", + m_bNewDb ? "yes" : "no", m_bSpatial ? "yes" : "no"); + + if (SQLITE_OK != sqlite3_open(pszDbName, &m_poDB)) { + CPLError(CE_Failure, CPLE_AppDefined, + "Creating SQLite DB failed"); + } + else { + char* pszErrMsg = NULL; + sqlite3_exec(m_poDB, "PRAGMA synchronous = OFF", NULL, NULL, &pszErrMsg); + sqlite3_free(pszErrMsg); + } + + if (m_bNewDb) { + /* new DB, create support metadata tables */ + osCommand.Printf("CREATE TABLE %s (file_name text, table_name text, num_records integer, " + "num_features integer, num_geometries integer, table_defn text)", + VFK_DB_TABLE); + ExecuteSQL(osCommand.c_str()); + + /* header table */ + osCommand.Printf("CREATE TABLE %s (key text, value text)", VFK_DB_HEADER); + ExecuteSQL(osCommand.c_str()); + } +} + +/*! + \brief VFKReaderSQLite destructor +*/ +VFKReaderSQLite::~VFKReaderSQLite() +{ + /* close tmp SQLite DB */ + if (SQLITE_OK != sqlite3_close(m_poDB)) { + CPLError(CE_Failure, CPLE_AppDefined, + "Closing SQLite DB failed\n %s", + sqlite3_errmsg(m_poDB)); + } + CPLDebug("OGR-VFK", "Internal DB (%s) closed", + m_pszDBname); + + /* delete tmp SQLite DB if requested */ + if (CSLTestBoolean(CPLGetConfigOption("OGR_VFK_DB_DELETE", "NO"))) { + CPLDebug("OGR-VFK", "Internal DB (%s) deleted", + m_pszDBname); + VSIUnlink(m_pszDBname); + } +} + +/*! + \brief Load data block definitions (&B) + + Call VFKReader::OpenFile() before this function. + + \return number of data blocks or -1 on error +*/ +int VFKReaderSQLite::ReadDataBlocks() +{ + int nDataBlocks = -1; + CPLString osSQL; + const char *pszName, *pszDefn; + IVFKDataBlock *poNewDataBlock; + + sqlite3_stmt *hStmt; + + osSQL.Printf("SELECT table_name, table_defn FROM %s", VFK_DB_TABLE); + hStmt = PrepareStatement(osSQL.c_str()); + while(ExecuteSQL(hStmt) == OGRERR_NONE) { + pszName = (const char*) sqlite3_column_text(hStmt, 0); + pszDefn = (const char*) sqlite3_column_text(hStmt, 1); + poNewDataBlock = (IVFKDataBlock *) CreateDataBlock(pszName); + poNewDataBlock->SetGeometryType(); + poNewDataBlock->SetProperties(pszDefn); + VFKReader::AddDataBlock(poNewDataBlock, NULL); + } + + if (m_nDataBlockCount == 0) { + sqlite3_exec(m_poDB, "BEGIN", 0, 0, 0); + /* CREATE TABLE ... */ + nDataBlocks = VFKReader::ReadDataBlocks(); + sqlite3_exec(m_poDB, "COMMIT", 0, 0, 0); + + StoreInfo2DB(); + } + + return nDataBlocks; +} + +/*! + \brief Load data records (&D) + + Call VFKReader::OpenFile() before this function. + + \param poDataBlock limit to selected data block or NULL for all + + \return number of data records or -1 on error +*/ +int VFKReaderSQLite::ReadDataRecords(IVFKDataBlock *poDataBlock) +{ + int nDataRecords; + int iDataBlock; + const char *pszName; + CPLString osSQL; + + IVFKDataBlock *poDataBlockCurrent; + + sqlite3_stmt *hStmt; + + pszName = NULL; + nDataRecords = 0; + + if (poDataBlock) { /* read records only for selected data block */ + /* table name */ + pszName = poDataBlock->GetName(); + + /* check for existing records (re-use already inserted data) */ + osSQL.Printf("SELECT num_records FROM %s WHERE " + "table_name = '%s'", + VFK_DB_TABLE, pszName); + hStmt = PrepareStatement(osSQL.c_str()); + nDataRecords = -1; + if (ExecuteSQL(hStmt) == OGRERR_NONE) { + nDataRecords = sqlite3_column_int(hStmt, 0); + } + sqlite3_finalize(hStmt); + } + else { + /* read all data blocks */ + + /* check for existing records (re-use already inserted data) */ + osSQL.Printf("SELECT COUNT(*) FROM %s WHERE num_records = -1", VFK_DB_TABLE); + hStmt = PrepareStatement(osSQL.c_str()); + if (ExecuteSQL(hStmt) == OGRERR_NONE && + sqlite3_column_int(hStmt, 0) == 0) + nDataRecords = 0; /* -> read from DB */ + else + nDataRecords = -1; /* -> read from VFK file */ + + sqlite3_finalize(hStmt); + } + + if (nDataRecords > -1) { /* read records from DB */ + /* read from DB */ + long iFID; + int iRowId; + VFKFeatureSQLite *poNewFeature = NULL; + + poDataBlockCurrent = NULL; + for (iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++) { + poDataBlockCurrent = GetDataBlock(iDataBlock); + + if (poDataBlock && poDataBlock != poDataBlockCurrent) + continue; + + poDataBlockCurrent->SetFeatureCount(0); /* avoid recursive call */ + + pszName = poDataBlockCurrent->GetName(); + CPLAssert(NULL != pszName); + + osSQL.Printf("SELECT %s,_rowid_ FROM %s ", + FID_COLUMN, pszName); + if (EQUAL(pszName, "SBP")) + osSQL += "WHERE PORADOVE_CISLO_BODU = 1 "; + osSQL += "ORDER BY "; + osSQL += FID_COLUMN; + hStmt = PrepareStatement(osSQL.c_str()); + nDataRecords = 0; + while (ExecuteSQL(hStmt) == OGRERR_NONE) { + iFID = sqlite3_column_int(hStmt, 0); + iRowId = sqlite3_column_int(hStmt, 1); + poNewFeature = new VFKFeatureSQLite(poDataBlockCurrent, iRowId, iFID); + poDataBlockCurrent->AddFeature(poNewFeature); + nDataRecords++; + } + + /* check DB consistency */ + osSQL.Printf("SELECT num_features FROM %s WHERE table_name = '%s'", + VFK_DB_TABLE, pszName); + hStmt = PrepareStatement(osSQL.c_str()); + if (ExecuteSQL(hStmt) == OGRERR_NONE) { + int nFeatDB; + + nFeatDB = sqlite3_column_int(hStmt, 0); + if (nFeatDB > 0 && nFeatDB != poDataBlockCurrent->GetFeatureCount()) + CPLError(CE_Failure, CPLE_AppDefined, + "%s: Invalid number of features " CPL_FRMT_GIB " (should be %d)", + pszName, poDataBlockCurrent->GetFeatureCount(), nFeatDB); + } + sqlite3_finalize(hStmt); + } + } + else { /* read from VFK file and insert records into DB */ + /* begin transaction */ + ExecuteSQL("BEGIN"); + + /* INSERT ... */ + nDataRecords = VFKReader::ReadDataRecords(poDataBlock); + + /* update VFK_DB_TABLE table */ + poDataBlockCurrent = NULL; + for (iDataBlock = 0; iDataBlock < GetDataBlockCount(); iDataBlock++) { + poDataBlockCurrent = GetDataBlock(iDataBlock); + + if (poDataBlock && poDataBlock != poDataBlockCurrent) + continue; + + osSQL.Printf("UPDATE %s SET num_records = %d WHERE " + "table_name = '%s'", + VFK_DB_TABLE, poDataBlockCurrent->GetRecordCount(), + poDataBlockCurrent->GetName()); + + ExecuteSQL(osSQL); + } + + /* commit transaction */ + ExecuteSQL("COMMIT"); + } + + return nDataRecords; +} + +/*! + \brief Store header info to VFK_DB_HEADER +*/ +void VFKReaderSQLite::StoreInfo2DB() +{ + CPLString osSQL; + const char *value; + char q; + + for(std::map::iterator i = poInfo.begin(); + i != poInfo.end(); ++i) { + value = i->second.c_str(); + + q = (value[0] == '"') ? ' ' : '"'; + + osSQL.Printf("INSERT INTO %s VALUES(\"%s\", %c%s%c)", + VFK_DB_HEADER, i->first.c_str(), + q, value, q); + ExecuteSQL(osSQL); + } +} + +/*! + \brief Create index + + If creating unique index fails, then non-unique index is created instead. + + \param name index name + \param table table name + \param column column(s) name + \param unique TRUE to create unique index +*/ +void VFKReaderSQLite::CreateIndex(const char *name, const char *table, const char *column, + bool unique) +{ + CPLString osSQL; + + if (unique) { + osSQL.Printf("CREATE UNIQUE INDEX %s ON %s (%s)", + name, table, column); + if (ExecuteSQL(osSQL.c_str()) == OGRERR_NONE) { + return; + } + } + + osSQL.Printf("CREATE INDEX %s ON %s (%s)", + name, table, column); + ExecuteSQL(osSQL.c_str()); +} + +/*! + \brief Create new data block + + \param pszBlockName name of the block to be created + + \return pointer to VFKDataBlockSQLite instance +*/ +IVFKDataBlock *VFKReaderSQLite::CreateDataBlock(const char *pszBlockName) +{ + /* create new data block, ie. table in DB */ + return new VFKDataBlockSQLite(pszBlockName, (IVFKReader *) this); +} + +/*! + \brief Create DB table from VFKDataBlock (SQLITE only) + + \param poDataBlock pointer to VFKDataBlock instance +*/ +void VFKReaderSQLite::AddDataBlock(IVFKDataBlock *poDataBlock, const char *pszDefn) +{ + const char *pszBlockName; + const char *pszKey; + CPLString osCommand, osColumn; + bool bUnique; + + VFKPropertyDefn *poPropertyDefn; + + sqlite3_stmt *hStmt; + + bUnique = !CSLTestBoolean(CPLGetConfigOption("OGR_VFK_DB_IGNORE_DUPLICATES", "NO")); + + pszBlockName = poDataBlock->GetName(); + + /* register table in VFK_DB_TABLE */ + osCommand.Printf("SELECT COUNT(*) FROM %s WHERE " + "table_name = '%s'", + VFK_DB_TABLE, pszBlockName); + hStmt = PrepareStatement(osCommand.c_str()); + + if (ExecuteSQL(hStmt) == OGRERR_NONE && + sqlite3_column_int(hStmt, 0) == 0) { + + osCommand.Printf("CREATE TABLE '%s' (", pszBlockName); + for (int i = 0; i < poDataBlock->GetPropertyCount(); i++) { + poPropertyDefn = poDataBlock->GetProperty(i); + if (i > 0) + osCommand += ","; + osColumn.Printf("%s %s", poPropertyDefn->GetName(), + poPropertyDefn->GetTypeSQL().c_str()); + osCommand += osColumn; + } + osColumn.Printf(",%s integer", FID_COLUMN); + osCommand += osColumn; + if (poDataBlock->GetGeometryType() != wkbNone) { + osColumn.Printf(",%s blob", GEOM_COLUMN); + osCommand += osColumn; + } + osCommand += ")"; + ExecuteSQL(osCommand.c_str()); /* CREATE TABLE */ + + /* create indeces */ + osCommand.Printf("%s_%s", pszBlockName, FID_COLUMN); + CreateIndex(osCommand.c_str(), pszBlockName, FID_COLUMN, + !EQUAL(pszBlockName, "SBP")); + + pszKey = ((VFKDataBlockSQLite *) poDataBlock)->GetKey(); + if (pszKey) { + osCommand.Printf("%s_%s", pszBlockName, pszKey); + CreateIndex(osCommand.c_str(), pszBlockName, pszKey, bUnique); + } + + if (EQUAL(pszBlockName, "SBP")) { + /* create extra indices for SBP */ + CreateIndex("SBP_OB", pszBlockName, "OB_ID", FALSE); + CreateIndex("SBP_HP", pszBlockName, "HP_ID", FALSE); + CreateIndex("SBP_DPM", pszBlockName, "DPM_ID", FALSE); + CreateIndex("SBP_OB_HP_DPM", pszBlockName, "OB_ID,HP_ID,DPM_ID", bUnique); + CreateIndex("SBP_OB_POR", pszBlockName, "OB_ID,PORADOVE_CISLO_BODU", FALSE); + CreateIndex("SBP_HP_POR", pszBlockName, "HP_ID,PORADOVE_CISLO_BODU", FALSE); + CreateIndex("SBP_DPM_POR", pszBlockName, "DPM_ID,PORADOVE_CISLO_BODU", FALSE); + } + else if (EQUAL(pszBlockName, "HP")) { + /* create extra indices for HP */ + CreateIndex("HP_PAR1", pszBlockName, "PAR_ID_1", FALSE); + CreateIndex("HP_PAR2", pszBlockName, "PAR_ID_2", FALSE); + } + else if (EQUAL(pszBlockName, "OB")) { + /* create extra indices for OP */ + CreateIndex("OB_BUD", pszBlockName, "BUD_ID", FALSE); + } + + /* update VFK_DB_TABLE meta-table */ + osCommand.Printf("INSERT INTO %s (file_name, table_name, " + "num_records, num_features, num_geometries, table_defn) VALUES " + "('%s', '%s', -1, 0, 0, '%s')", + VFK_DB_TABLE, m_pszFilename, pszBlockName, pszDefn); + + ExecuteSQL(osCommand.c_str()); + + sqlite3_finalize(hStmt); + } + + return VFKReader::AddDataBlock(poDataBlock, NULL); +} + +/*! + \brief Prepare SQL statement + + \param pszSQLCommand SQL statement to be prepared + + \return pointer to sqlite3_stmt instance or NULL on error +*/ +sqlite3_stmt *VFKReaderSQLite::PrepareStatement(const char *pszSQLCommand) +{ + int rc; + sqlite3_stmt *hStmt = NULL; + + CPLDebug("OGR-VFK", "VFKReaderSQLite::PrepareStatement(): %s", pszSQLCommand); + + rc = sqlite3_prepare(m_poDB, pszSQLCommand, strlen(pszSQLCommand), + &hStmt, NULL); + + if (rc != SQLITE_OK) { + CPLError(CE_Failure, CPLE_AppDefined, + "In PrepareStatement(): sqlite3_prepare(%s):\n %s", + pszSQLCommand, sqlite3_errmsg(m_poDB)); + + if(hStmt != NULL) { + sqlite3_finalize(hStmt); + } + + return NULL; + } + + return hStmt; +} + +/*! + \brief Execute prepared SQL statement + + \param hStmt pointer to sqlite3_stmt + + \return OGRERR_NONE on success +*/ +OGRErr VFKReaderSQLite::ExecuteSQL(sqlite3_stmt *hStmt) +{ + int rc; + + // assert + + rc = sqlite3_step(hStmt); + if (rc != SQLITE_ROW) { + if (rc == SQLITE_DONE) { + sqlite3_finalize(hStmt); + return OGRERR_NOT_ENOUGH_DATA; + } + + CPLError(CE_Failure, CPLE_AppDefined, + "In ExecuteSQL(): sqlite3_step:\n %s", + sqlite3_errmsg(m_poDB)); + if (hStmt) + sqlite3_finalize(hStmt); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; + +} + +/*! + \brief Execute SQL statement (SQLITE only) + + \param pszSQLCommand SQL command to execute + \param bQuiet TRUE to print debug message on failure instead of error message + + \return OGRERR_NONE on success or OGRERR_FAILURE on failure +*/ +OGRErr VFKReaderSQLite::ExecuteSQL(const char *pszSQLCommand, bool bQuiet) +{ + char *pszErrMsg = NULL; + + if (SQLITE_OK != sqlite3_exec(m_poDB, pszSQLCommand, NULL, NULL, &pszErrMsg)) { + if (!bQuiet) + CPLError(CE_Failure, CPLE_AppDefined, + "In ExecuteSQL(%s): %s", + pszSQLCommand, pszErrMsg); + else + CPLError(CE_Warning, CPLE_AppDefined, + "In ExecuteSQL(%s): %s", + pszSQLCommand, pszErrMsg); + + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/*! + \brief Add feature + + \param poDataBlock pointer to VFKDataBlock instance + \param poFeature pointer to VFKFeature instance +*/ +OGRErr VFKReaderSQLite::AddFeature(IVFKDataBlock *poDataBlock, VFKFeature *poFeature) +{ + CPLString osCommand; + CPLString osValue; + + const char *pszBlockName; + + OGRFieldType ftype; + + const VFKProperty *poProperty; + + VFKFeatureSQLite *poNewFeature; + + pszBlockName = poDataBlock->GetName(); + osCommand.Printf("INSERT INTO '%s' VALUES(", pszBlockName); + + for (int i = 0; i < poDataBlock->GetPropertyCount(); i++) { + ftype = poDataBlock->GetProperty(i)->GetType(); + poProperty = poFeature->GetProperty(i); + if (i > 0) + osCommand += ","; + if (poProperty->IsNull()) + osValue.Printf("NULL"); + else { + switch (ftype) { + case OFTInteger: + osValue.Printf("%d", poProperty->GetValueI()); + break; + case OFTReal: + osValue.Printf("%f", poProperty->GetValueD()); + break; + case OFTString: + if (poDataBlock->GetProperty(i)->IsIntBig()) + osValue.Printf("%s", poProperty->GetValueS()); + else + osValue.Printf("'%s'", poProperty->GetValueS(TRUE)); + break; + default: + osValue.Printf("'%s'", poProperty->GetValueS()); + break; + } + } + osCommand += osValue; + } + osValue.Printf("," CPL_FRMT_GIB, poFeature->GetFID()); + if (poDataBlock->GetGeometryType() != wkbNone) { + osValue += ",NULL"; + } + osValue += ")"; + osCommand += osValue; + + if (ExecuteSQL(osCommand.c_str(), TRUE) != OGRERR_NONE) + return OGRERR_FAILURE; + + if (EQUAL(pszBlockName, "SBP")) { + poProperty = poFeature->GetProperty("PORADOVE_CISLO_BODU"); + CPLAssert(NULL != poProperty); + if (!EQUAL(poProperty->GetValueS(), "1")) + return OGRERR_NONE; + } + + poNewFeature = new VFKFeatureSQLite(poDataBlock, poDataBlock->GetRecordCount(RecordValid) + 1, + poFeature->GetFID()); + poDataBlock->AddFeature(poNewFeature); + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/GNUmakefile new file mode 100644 index 000000000..33cdbbbd8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrvrtdatasource.o ogrvrtlayer.o ogrvrtdriver.o + +CPPFLAGS := -I.. -I../.. -I../generic $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_vrt.h ../generic/ogrwarpedlayer.h ../generic/ogrunionlayer.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/drv_vrt.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/drv_vrt.html new file mode 100644 index 000000000..258ecb194 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/drv_vrt.html @@ -0,0 +1,526 @@ + + +Virtual Format + + + + +

      Virtual Format

      + +OGR Virtual Format is a driver that transforms features read from other +drivers based on criteria specified in an XML control file. It is primarily +used to derive spatial layers from flat tables with spatial information in +attribute columns. It can also be used to associate coordinate system +information with a datasource, merge layers from different datasources into +a single data source, or even just to provide an anchor file for access to +non-file oriented datasources.

      + +The virtual files are currently normally prepared by hand.

      + +

      Creation Issues

      + +Before GDAL 1.7.0, the OGR VRT driver was read-only.

      +Since GDAL 1.7.0, the CreateFeature(), SetFeature() and DeleteFeature() operations +are supported on a layer of a VRT dataset, if the following conditions are met : +

        +
      • the VRT dataset is opened in update mode
      • +
      • the underlying source layer supports those operations
      • +
      • the SrcLayer element is used (as opposed to the SrcSQL element)
      • +
      • the FID of the VRT features is the same as the FID of the source features, that +is to say, the FID element is not specified
      • +

      + +

      Virtual File Format

      + +The root element of the XML control file is OGRVRTDataSource. It has +an OGRVRTLayer (or OGRVRTWarpedLayer or OGRVRTUnionLayer starting +with GDAL 1.10.0) child for each layer in the virtual datasource, and a +Metadata element.

      + +A XML schema of the OGR VRT format +is available. Starting with GDAL 1.11, when GDAL is configured with libXML2 support, that +schema will be used to validate the VRT documents. Non-conformities will be reported only as +warnings. That validation can be disabled by setting the GDAL_XML_VALIDATION configuration option to NO.

      + +

      +Metadata (optional): (GDAL >= 2.0) This element contains a list of metadata name/value +pairs associated with the dataset as a whole. It has +<MDI> (metadata item) subelements which have a "key" attribute and the value +as the data of the element. The Metadata element can be repeated multiple times, +in which case it must be accompanied with a "domain" attribute to indicate the +name of the metadata domain. +

      + +A OGRVRTLayer element should have a name attribute with the layer name, and may have the +following subelements: + +
        +
      • SrcDataSource (mandatory): The value is the name of the datasource +that this layer will be derived from. The element may optionally have +a relativeToVRT attribute which defaults to "0", but if "1" indicates +that the source datasource should be interpreted as relative to the virtual +file. This can be any OGR supported dataset, including ODBC, CSV, etc. +The element may also have a shared attribute to control whether the +datasource should be opened in shared mode. Defaults to OFF for SrcLayer use and ON for SrcSQL use.
      • + +
        + +
      • OpenOptions (optional): (GDAL >= 2.0) This element may list a number of open +options as child elements of the form <OOI key="key_name">value_name</OOI>
      • + +
        + +
      • Metadata (optional): (GDAL >= 2.0) This element contains a list of metadata name/value +pairs associated with the layer as a whole. It has +<MDI> (metadata item) subelements which have a "key" attribute and the value +as the data of the element. The Metadata element can be repeated multiple times, +in which case it must be accompanied with a "domain" attribute to indicate the +name of the metadata domain. +
      • + +
        + +
      • SrcLayer (optional): The value is the name of the layer on the +source data source from which this virtual layer should be derived. If this +element isn't provided, then the SrcSQL element must be provided.
      • + +
        + +
      • SrcSQL (optional): An SQL statement to execute to generate the +desired layer result. This should be provided instead of the SrcLayer for +statement derived results. Some limitations may apply for SQL derived +layers. Starting with OGR 1.10, an optional dialect attribute can be specified +on the SrcSQL element to specify which SQL "dialect" should be used : +possible values are currently OGRSQL or +SQLITE. If dialect is not specified, +the default dialect of the datasource will be used.
      • + +
        + +
      • FID (optional): Name of the attribute column from which the +FID of features should be derived. If not provided, the FID of the source +features will be used directly. The layer will report the FID column name only +if it is also reported as a regular field. Starting with GDAL 2.0, a "name" +attribute can be specified on the FID element so that the FID column name is +always reported.
      • + +
        + +
      • Style (optional): Name of the attribute column from which the +style of features should be derived. If not provided, the style of the source +features will be used directly.
      • + +
        + +
      • GeometryType (optional): The geometry type to be assigned to the +layer. +If not provided it will be taken from the source layer. The value should +be one of "wkbNone", "wkbUnknown", "wkbPoint", "wkbLineString", "wkbPolygon", +"wkbMultiPoint", +"wkbMultiLineString", "wkbMultiPolygon", or "wkbGeometryCollection". +Optionally "25D" may be appended to mark it as including Z coordinates. +Defaults to "wkbUnknown" indicating that any geometry type is allowed.
      • + +
        + +
      • LayerSRS (optional): The value of this element is the spatial +reference to use for the layer. If not provided, it is inherited from the +source layer. The value may be WKT or any other input that is accepted +by the OGRSpatialReference::SetUserInput() method. If the value is NULL, then +no SRS will be used for the layer.
      • + +
        + +
      • GeometryField (optional): This element is used to define +how the geometry for features should be derived.
        +If not provided the geometry of the source feature is copied directly.
        +The type of geometry encoding is indicated with the encoding attribute which may have the +value "WKT", "WKB" or "PointFromColumns".
        +If the encoding is "WKT" or "WKB" then the field attribute will have +the name of the field containing the WKT or WKB geometry.
        +If the encoding is "PointFromColumns" then the x, y and z attributes +will have the names of the columns to be used for the X, Y and Z coordinates. +The z attribute is optional.
        +Starting with GDAL 1.7.0, the optional reportSrcColumn attribute can be used to specify +whether the source geometry fields (the fields set in the field, x, x +or z attributes) should be reported as fields of the VRT layer. It defaults to TRUE. +If set to FALSE, the source geometry fields will only be used to build the geometry of the features of the VRT layer.
      • +

        +Starting with OGR 1.11, the GeometryField element can be repeated as many times +as necessary to create multiple geometry fields. It accepts a name +attribute (recommended) that will be used to define the VRT geometry field name. When encoding is +not specified, the field attribute will be used to determine the corresponding +geometry field name in the source layer. If neither encoding nor field +are specified, it is assumed that the name of source geometry field is the value +of the name attribute. +

        +

        +Starting with GDAL 2.0, the optional nullable attribute can be used to specify +whether the geometry field is nullable. It defaults to "true". +

        +

        +When several geometry fields are used, the following child elements of GeometryField +can be defined to explicitly set the geometry type, SRS, source region, or extent. +

          +
        • GeometryType (optional) : same syntax as OGRVRTLayer-level GeometryType.
        • +
        • SRS (optional) : same syntax as OGRVRTLayer-level LayerSRS (note SRS vs LayerSRS)
        • +
        • SrcRegion (optional) : same syntax as OGRVRTLayer-level SrcRegion
        • +
        • ExtentXMin, ExtentYMin, ExtentXMax and ExtentXMax (optional) : +same syntax as OGRVRTLayer-level elements of same name
        • +
        +

        +

        If no GeometryField element is specified, all the geometry fields of the +source layer will be exposed by the VRT layer. In order not to expose any geometry +field of the source layer, you need to specify OGRVRTLayer-level GeometryType +element to wkbNone. +

        +
        + +
      • SrcRegion (optionnal, from GDAL 1.7.0) : This element is used +to define an initial spatial filter for the source features. This spatial +filter will be combined with any spatial filter explicitly set on the VRT +layer with the SetSpatialFilter() method. The value of the element must be +a valid WKT string defining a polygon. An optional clip attribute +can be set to "TRUE" to clip the geometries to the source region, otherwise +the source geometries are not modified.
      • + +
        + +
      • Field (optional, from GDAL 1.7.0): One or more attribute fields +may be defined with Field elements. If no Field elements are defined, the +fields of the source layer/sql will be defined on the VRT layer. The Field +may have the following attributes: +
          +
        • name (required): the name of the field. +
        • type: the field type, one of "Integer", "IntegerList", +"Real", "RealList", "String", "StringList", "Binary", "Date", "Time", +or "DateTime". Defaults to "String". +
        • subtype: (GDAL >= 2.0) the field subtype, one of "None", "Boolean", +"Int16", "Float32". Defaults to "None". +
        • width: the field width. Defaults to unknown. +
        • precision: the field width. Defaults to zero. +
        • src: the name of the source field to be copied to this one. +Defaults to the value of "name". +
        • nullable (GDAL >= 2.0) can be used to specify +whether the field is nullable. It defaults to "true".
        • +
        +
      • + +
        + +
      • FeatureCount (optional, from GDAL 1.10.0) : This element is used +to define the feature count of the layer (when no spatial or attribute filter is set). +This can be useful on static data, when getting the feature count from the source layer is slow.
      • + +
        + +
      • ExtentXMin, ExtentYMin, ExtentXMax and ExtentXMax (optional, from GDAL 1.10.0) : +Those elements are used to define the extent of the layer. This can be useful on static data, when +getting the extent from the source layer is slow.
      • + +
        + +
      + +

      + +A OGRVRTWarpedLayer element (GDAL >= 1.10.0) is used to do on-the-fly reprojection of +a source layer. It may have the following subelements: + +

        + +
      • OGRVRTLayer, OGRVRTWarpedLayer or OGRVRTUnionLayer (mandatory): +the source layer to reproject.
      • +
        + +
      • SrcSRS (optional): The value of this element is the spatial +reference to use for the layer before reprojection. If not specified, it is deduced from +the source layer.
      • +
        + +
      • TargetSRS (mandatory): The value of this element is the spatial +reference to use for the layer after reprojection.
      • +
        + +
      • ExtentXMin, ExtentYMin, ExtentXMax and ExtentXMax (optional, from GDAL 1.10.0) : +Those elements are used to define the extent of the layer. This can be useful on static data, when +getting the extent from the source layer is slow.
      • +
        + +
      • WarpedGeomFieldName (optional, from GDAL 1.11) : The value of this element +is the geometry field name of the source layer to wrap. If not specified, the first geometry +field will be used. If there are several geometry fields, only the one matching +WarpedGeomFieldName will be warped; the other ones will be untouched.
      • +
        + +
      + +

      + +A OGRVRTUnionLayer element (GDAL >= 1.10.0) is used to concatenate the content of source +layers. It should have a name and may have the following subelements: + +

        + +
      • OGRVRTLayer, OGRVRTWarpedLayer or OGRVRTUnionLayer (mandatory and may be repeated): +a source layer to add in the union.
      • +
        + +
      • PreserveSrcFID (optional) : may be ON or OFF. If set to ON, the FID from the source layer +will be used, otherwise a counter will be used. Defaults to OFF.
      • +
        + +
      • SourceLayerFieldName (optional) : if specified, an additional field (named with +the value of SourceLayerFieldName) will be added in the layer field definition. For each feature, +the value of this field will be set with the name of the layer from which the feature comes from.
      • +
        + +
      • GeometryType (optional) : see above for the syntax. If not specified, the geometry type +will be deduced from the geometry type of all source layers.
      • +
        + +
      • LayerSRS (optional) : see above for the syntax. If not specified, the SRS will be the +SRS of the first source layer.
      • +
        + +
      • FieldStrategy (optional, exclusive with Field or GeometryField) : +may be FirstLayer to use the fields from the first layer found, +Union to use a super-set of all the fields from all source layers, or +Intersection to use a sub-set of all the common fields from all source layers. Defaults to Union.
      • +
        + +
      • Field (optional, exclusive with FieldStrategy) : see above for the syntax. Note: the src attribute +is not supported in the context of a OGRVRTUnionLayer element (field names are assumed to be identical).
      • +
        + +
      • GeometryField (optional, exclusive with FieldStrategy, GDAL >= 1.11) : the name attribute and +the following sub-elements GeometryType, SRS and Extent[X|Y][Min|Max] are available.
      • +
        + +
      • FeatureCount (optional) : see above for the syntax
      • +
        + +
      • ExtentXMin, ExtentYMin, ExtentXMax and ExtentXMax (optional) : see above for the syntax
      • +
        + +
      + + +

      Example: ODBC Point Layer

      + +In the following example (disease.ovf) the worms table from the ODBC +database DISEASE is used to form a spatial layer. The virtual file +uses the "x" and "y" columns to get the spatial location. It also marks +the layer as a point layer, and as being in the WGS84 coordinate system.

      + +

      <OGRVRTDataSource>
      +
      +    <OGRVRTLayer name="worms">
      +        <SrcDataSource>ODBC:DISEASE,worms</SrcDataSource> 
      + 	<SrcLayer>worms</SrcLayer> 
      +	<GeometryType>wkbPoint</GeometryType> 
      +        <LayerSRS>WGS84</LayerSRS>
      +	<GeometryField encoding="PointFromColumns" x="x" y="y"/> 
      +    </OGRVRTLayer>
      +
      +</OGRVRTDataSource>
      +
      + +

      Example: Renaming attributes

      + +It can be useful in some circumstances to be able to rename the field names +from a source layer to other names. This is particularly true when you want +to transcode to a format whose schema is fixed, such as GPX +(<name>, <desc>, etc.). This can be accomplished using +SQL this way:

      + +

      <OGRVRTDataSource>
      +    <OGRVRTLayer name="remapped_layer">
      +        <SrcDataSource>your_source.shp</SrcDataSource>
      +        <SrcSQL>SELECT src_field_1 AS name, src_field_2 AS desc FROM your_source_layer_name</SrcSQL>
      +    </OGRVRTLayer>
      +</OGRVRTDataSource>
      +
      + +This can also be accomplished (from GDAL 1.7.0) using explicit field +definitions: + +
      +<OGRVRTDataSource>
      +    <OGRVRTLayer name="remapped_layer">
      +        <SrcDataSource>your_source.shp</SrcDataSource>
      +        <SrcLayer>your_source</SrcSQL>
      +        <Field name="name" src="src_field_1" />
      +        <Field name="desc" src="src_field_2" type="String" width="45" />
      +    </OGRVRTLayer>
      +</OGRVRTDataSource>
      +
      + +

      Example: Transparent spatial filtering (GDAL >= 1.7.0)

      + +The following example will only return features from the source layer that +intersect the (0,40)-(10,50) region. Furthermore, returned geometries will be clipped +to fit into that region.

      + +

      <OGRVRTDataSource>
      +    <OGRVRTLayer name="source">
      +        <SrcDataSource>source.shp</SrcDataSource>
      +        <SrcRegion clip="true">POLYGON((0 40,10 40,10 50,0 50,0 40))</SrcRegion>
      +    </OGRVRTLayer>
      +</OGRVRTDataSource>
      +
      + +

      Example: Reprojected layer (GDAL >= 1.10.0)

      + +The following example will return the source.shp layer reprojected to EPSG:4326.

      + +

      <OGRVRTDataSource>
      +    <OGRVRTWarpedLayer>
      +        <OGRVRTLayer name="source">
      +            <SrcDataSource>source.shp</SrcDataSource>
      +        </OGRVRTLayer>
      +        <TargetSRS>EPSG:4326</TargetSRS>
      +    </OGRVRTWarpedLayer>
      +</OGRVRTDataSource>
      +
      + +

      Example: Union layer (GDAL >= 1.10.0)

      + +The following example will return a layer that is the concatenation of source1.shp +and source2.shp.

      + +

      <OGRVRTDataSource>
      +    <OGRVRTUnionLayer name="unionLayer">
      +        <OGRVRTLayer name="source1">
      +            <SrcDataSource>source1.shp</SrcDataSource>
      +        </OGRVRTLayer>
      +        <OGRVRTLayer name="source2">
      +            <SrcDataSource>source2.shp</SrcDataSource>
      +        </OGRVRTLayer>
      +    </OGRVRTUnionLayer>
      +</OGRVRTDataSource>
      +
      + +

      Example: SQLite/Spatialite SQL dialect (GDAL >=1.10.0)

      + +The following example will return four different layers which are generated in a fly +from the same polygon shapefile. The first one is the shapefile layer as it stands. +The second layer gives simplified polygons by applying Spatialite function "Simplify" +with parameter tolerance=10. In the third layer the original geometries are replaced by +their convace hulls. In the fourth layer Spatialite function PointOnSurface is used for +replacing the original geometries by points which are inside the corresponding source +polygons. Note that for using the last three layers of this VRT file GDAL must be +compiled with SQLite and Spatialite.

      + +

      +<OGRVRTDataSource>
      +    <OGRVRTLayer name="polygons">
      +        <SrcDataSource>polygons.shp</SrcDataSource>
      +        </OGRVRTLayer>
      +    <OGRVRTLayer name="polygons_as_simplified">
      +        <SrcDataSource>polygons.shp</SrcDataSource>
      +        <SrcSQL dialect="sqlite">SELECT Simplify(geometry,10) from polygons</SrcSQL>
      +    </OGRVRTLayer>
      +    <OGRVRTLayer name="polygons_as_hulls">
      +        <SrcDataSource>polygons.shp</SrcDataSource>
      +        <SrcSQL dialect="sqlite">SELECT ConvexHull(geometry) from polygons</SrcSQL>
      +    </OGRVRTLayer>
      +    <OGRVRTLayer name="polygons_as_points">
      +        <SrcDataSource>polygons.shp</SrcDataSource>
      +        <SrcSQL dialect="sqlite">SELECT PointOnSurface(geometry) from polygons</SrcSQL>
      +    </OGRVRTLayer>
      +</OGRVRTDataSource>
      +
      + +

      Example: Multiple geometry fields (GDAL >= 1.11)

      + +

      +The following example will expose all the attribute and geometry fields of the +source layer: +

      +
      <OGRVRTDataSource>
      +    <OGRVRTLayer name="test">
      +        <SrcDataSource>PG:dbname=testdb</SrcDataSource>
      +    </OGRVRTLayer>
      +</OGRVRTDataSource>
      +
      + +

      +To expose only part (or all!) of the fields: +

      +
      <OGRVRTDataSource>
      +    <OGRVRTLayer name="other_test">
      +        <SrcDataSource>PG:dbname=testdb</SrcDataSource>
      +        <SrcLayer>test</SrcLayer>
      +        <GeometryField name="pg_geom_field_1" />
      +        <GeometryField name="vrt_geom_field_2" field="pg_geom_field_2">
      +            <GeometryType>wkbPolygon</GeometryType>
      +            <SRS>EPSG:4326</SRS>
      +            <ExtentXMin>-180</ExtentXMin>
      +            <ExtentYMin>-90</ExtentYMin>
      +            <ExtentXMax>180</ExtentXMax>
      +            <ExtentYMax>90</ExtentYMax>
      +        </GeometryField>
      +        <Field name="vrt_field_1" src="src_field_1" />
      +    </OGRVRTLayer>w
      +</OGRVRTDataSource>
      +
      + +To reproject the 'pg_geom_field_2' geometry field to EPSG:4326:

      + +

      <OGRVRTDataSource>
      +    <OGRVRTWarpedLayer>
      +        <OGRVRTLayer name="other_test">
      +            <SrcDataSource>PG:dbname=testdb</SrcDataSource>
      +        </OGRVRTLayer>
      +        <WarpedGeomFieldName>pg_geom_field_2</WarpedGeomFieldName>
      +        <TargetSRS>EPSG:32631</TargetSRS>
      +    </OGRVRTWarpedLayer>
      +</OGRVRTDataSource>
      +
      + +To make the union of several multi-geometry layers and keep only a few of them:

      + +

      <OGRVRTDataSource>
      +    <OGRVRTUnionLayer name="unionLayer">
      +        <OGRVRTLayer name="source1">
      +            <SrcDataSource>PG:dbname=testdb</SrcDataSource>
      +        </OGRVRTLayer>
      +        <OGRVRTLayer name="source2">
      +            <SrcDataSource>PG:dbname=testdb</SrcDataSource>
      +        </OGRVRTLayer>
      +        <GeometryField name="pg_geom_field_2">
      +            <GeometryType>wkbPolygon</GeometryType>
      +            <SRS>EPSG:4326</SRS>
      +            <ExtentXMin>-180</ExtentXMin>
      +            <ExtentYMin>-90</ExtentYMin>
      +            <ExtentXMax>180</ExtentXMax>
      +            <ExtentYMax>90</ExtentYMax>
      +        </GeometryField>
      +        <GeometryField name="pg_geom_field_3" />
      +        <Field name="src_field_1" />
      +    </OGRVRTUnionLayer>
      +</OGRVRTDataSource>
      +
      + +

      Other Notes

      + +
        + +
      • When the GeometryField is "WKT" spatial filtering is applied +after extracting all rows from the source datasource. Essentially that means +there is no fast spatial filtering on WKT derived geometries.

        + +

      • When the GeometryField is "PointFromColumns", and a SrcLayer (as opposed +to SrcSQL) is used, and a spatial filter is in effect on the virtual layer +then the spatial filter will be internally translated into an attribute filter +on the X and Y columns in the SrcLayer. In cases where fast spatial filtering +is important it can be helpful to index the X and Y columns in the source +datastore, if that is possible. For instance if the source is an RDBMS. +You can turn off that feature by setting the useSpatialSubquery attribute +of the GeometryField element to FALSE.

        + +

      + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/makefile.vc new file mode 100644 index 000000000..1027bcd53 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrvrtdriver.obj ogrvrtdatasource.obj ogrvrtlayer.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. -I..\generic + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + -del *.exe diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/ogr_vrt.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/ogr_vrt.h new file mode 100644 index 000000000..bd4ccfc82 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/ogr_vrt.h @@ -0,0 +1,284 @@ +/****************************************************************************** + * $Id: ogr_vrt.h 28522 2015-02-18 14:16:40Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Private definitions for OGR/VRT driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2009-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_VRT_H_INCLUDED +#define _OGR_VRT_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "cpl_error.h" +#include "cpl_minixml.h" +#include "ogrlayerpool.h" + +#include +#include +#include + +typedef enum { + VGS_None, + VGS_Direct, + VGS_PointFromColumns, + VGS_WKT, + VGS_WKB, + VGS_Shape +} OGRVRTGeometryStyle; + +/************************************************************************/ +/* OGRVRTGeomFieldProps */ +/************************************************************************/ + +class OGRVRTGeomFieldProps +{ + public: + CPLString osName; /* Name of the VRT geometry field */ + OGRwkbGeometryType eGeomType; + OGRSpatialReference *poSRS; + + int bSrcClip; + OGRGeometry *poSrcRegion; + + // Geometry interpretation related. + OGRVRTGeometryStyle eGeometryStyle; + + /* points to a OGRField for VGS_WKT, VGS_WKB, VGS_Shape and OGRGeomField for VGS_Direct */ + int iGeomField; + + // VGS_PointFromColumn + int iGeomXField, iGeomYField, iGeomZField; + int bReportSrcColumn; + int bUseSpatialSubquery; + + OGREnvelope sStaticEnvelope; + + int bNullable; + + OGRVRTGeomFieldProps(); + ~OGRVRTGeomFieldProps(); +}; + +/************************************************************************/ +/* OGRVRTLayer */ +/************************************************************************/ + +class OGRVRTDataSource; + +class OGRVRTLayer : public OGRLayer +{ + protected: + OGRVRTDataSource* poDS; + std::vector apoGeomFieldProps; + + int bHasFullInitialized; + CPLString osName; + CPLXMLNode *psLTree; + CPLString osVRTDirectory; + + OGRFeatureDefn *poFeatureDefn; + + GDALDataset *poSrcDS; + OGRLayer *poSrcLayer; + OGRFeatureDefn *poSrcFeatureDefn; + int bNeedReset; + int bSrcLayerFromSQL; + int bSrcDSShared; + int bAttrFilterPassThrough; + + char *pszAttrFilter; + + int iFIDField; // -1 means pass through. + CPLString osFIDFieldName; + int iStyleField; // -1 means pass through. + + // Attribute Mapping + std::vector anSrcField; + std::vector abDirectCopy; + + int bUpdate; + + OGRFeature *TranslateFeature( OGRFeature*& , int bUseSrcRegion ); + OGRFeature *TranslateVRTFeatureToSrcFeature( OGRFeature* poVRTFeature); + + int ResetSourceReading(); + + int FullInitialize(); + + OGRFeatureDefn *GetSrcLayerDefn(); + void ClipAndAssignSRS(OGRFeature* poFeature); + + GIntBig nFeatureCount; + + int bError; + + int ParseGeometryField(CPLXMLNode* psNode, + CPLXMLNode* psNodeParent, + OGRVRTGeomFieldProps* poProps); + + public: + OGRVRTLayer(OGRVRTDataSource* poDSIn); + virtual ~OGRVRTLayer(); + + int FastInitialize( CPLXMLNode *psLTree, + const char *pszVRTDirectory, + int bUpdate); + + virtual const char *GetName() { return osName.c_str(); } + virtual OGRwkbGeometryType GetGeomType(); + +/* -------------------------------------------------------------------- */ +/* Caution : all the below methods should care of calling */ +/* FullInitialize() if not already done */ +/* -------------------------------------------------------------------- */ + + virtual void ResetReading(); + virtual OGRFeature *GetNextFeature(); + + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + + virtual OGRFeatureDefn *GetLayerDefn(); + + virtual OGRSpatialReference *GetSpatialRef(); + + virtual GIntBig GetFeatureCount( int ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual int TestCapability( const char * ); + + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, + int bForce = TRUE); + + virtual void SetSpatialFilter( OGRGeometry * poGeomIn ); + virtual void SetSpatialFilter( int iGeomField, OGRGeometry * poGeomIn ); + + virtual OGRErr ICreateFeature( OGRFeature* poFeature ); + + virtual OGRErr ISetFeature( OGRFeature* poFeature ); + + virtual OGRErr DeleteFeature( GIntBig nFID ); + + virtual OGRErr SyncToDisk(); + + virtual const char *GetFIDColumn(); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + virtual OGRErr SetIgnoredFields( const char **papszFields ); + + GDALDataset* GetSrcDataset(); +}; + +/************************************************************************/ +/* OGRVRTDataSource */ +/************************************************************************/ + +typedef enum +{ + OGR_VRT_PROXIED_LAYER, + OGR_VRT_LAYER, + OGR_VRT_OTHER_LAYER, +} OGRLayerType; + +class OGRVRTDataSource : public OGRDataSource +{ + OGRLayer **papoLayers; + OGRLayerType *paeLayerType; + int nLayers; + + char *pszName; + + CPLXMLNode *psTree; + + int nCallLevel; + + std::set aosOtherDSNameSet; + + OGRLayer* InstanciateWarpedLayer(CPLXMLNode *psLTree, + const char *pszVRTDirectory, + int bUpdate, + int nRecLevel); + OGRLayer* InstanciateUnionLayer(CPLXMLNode *psLTree, + const char *pszVRTDirectory, + int bUpdate, + int nRecLevel); + + OGRLayerPool* poLayerPool; + + OGRVRTDataSource *poParentDS; + int bRecursionDetected; + + public: + OGRVRTDataSource(GDALDriver* poDriver); + ~OGRVRTDataSource(); + + OGRLayer* InstanciateLayer(CPLXMLNode *psLTree, + const char *pszVRTDirectory, + int bUpdate, + int nRecLevel = 0); + + OGRLayer* InstanciateLayerInternal(CPLXMLNode *psLTree, + const char *pszVRTDirectory, + int bUpdate, + int nRecLevel); + + int Initialize( CPLXMLNode *psXML, const char *pszName, + int bUpdate ); + + const char *GetName() { return pszName; } + int GetLayerCount() { return nLayers; } + OGRLayer *GetLayer( int ); + + int TestCapability( const char * ); + + virtual char **GetFileList(); + + /* Anti-recursion mechanism for standard Open */ + void SetCallLevel(int nCallLevelIn) { nCallLevel = nCallLevelIn; } + int GetCallLevel() { return nCallLevel; } + + void SetParentDS(OGRVRTDataSource* poParentDSIn) { poParentDS = poParentDSIn; } + OGRVRTDataSource* GetParentDS() { return poParentDS; } + + void SetRecursionDetected() { bRecursionDetected = TRUE; } + int GetRecursionDetected() { return bRecursionDetected; } + + /* Anti-recursion mechanism for shared Open */ + void AddForbiddenNames(const char* pszOtherDSName); + int IsInForbiddenNames(const char* pszOtherDSName); +}; + +OGRwkbGeometryType OGRVRTGetGeometryType(const char* pszGType, int* pbError); + +#endif /* ndef _OGR_VRT_H_INCLUDED */ + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/ogrvrtdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/ogrvrtdatasource.cpp new file mode 100644 index 000000000..9ff234929 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/ogrvrtdatasource.cpp @@ -0,0 +1,980 @@ +/****************************************************************************** + * $Id: ogrvrtdatasource.cpp 28950 2015-04-18 23:24:49Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRVRTDataSource class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2009-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_vrt.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogrwarpedlayer.h" +#include "ogrunionlayer.h" + +CPL_CVSID("$Id: ogrvrtdatasource.cpp 28950 2015-04-18 23:24:49Z rouault $"); + +/************************************************************************/ +/* OGRVRTGetGeometryType() */ +/************************************************************************/ + +typedef struct +{ + OGRwkbGeometryType eType; + const char *pszName; +} OGRGeomTypeName; + +static const OGRGeomTypeName asGeomTypeNames[] = { /* 25D versions are implicit */ + { wkbUnknown, "wkbUnknown" }, + { wkbPoint, "wkbPoint" }, + { wkbLineString, "wkbLineString" }, + { wkbPolygon, "wkbPolygon" }, + { wkbMultiPoint, "wkbMultiPoint" }, + { wkbMultiLineString, "wkbMultiLineString" }, + { wkbMultiPolygon, "wkbMultiPolygon" }, + { wkbGeometryCollection, "wkbGeometryCollection" }, + { wkbCircularString, "wkbCircularString" }, + { wkbCompoundCurve, "wkbCompoundCurve" }, + { wkbCurvePolygon, "wkbCurvePolygon" }, + { wkbGeometryCollection, "wkbMultiCurve" }, + { wkbMultiSurface, "wkbMultiSurface" }, + { wkbNone, "wkbNone" }, + { wkbNone, NULL } +}; + +OGRwkbGeometryType OGRVRTGetGeometryType(const char* pszGType, int* pbError) +{ + int iType; + OGRwkbGeometryType eGeomType = wkbUnknown; + + if (pbError) + *pbError = FALSE; + + for( iType = 0; asGeomTypeNames[iType].pszName != NULL; iType++ ) + { + if( EQUALN(pszGType, asGeomTypeNames[iType].pszName, + strlen(asGeomTypeNames[iType].pszName)) ) + { + eGeomType = asGeomTypeNames[iType].eType; + + if( strstr(pszGType,"25D") != NULL || strstr(pszGType,"Z") != NULL ) + eGeomType = wkbSetZ(eGeomType); + break; + } + } + + if( asGeomTypeNames[iType].pszName == NULL ) + { + if (pbError) + *pbError = TRUE; + } + + return eGeomType; +} + +/************************************************************************/ +/* OGRVRTDataSource() */ +/************************************************************************/ + +OGRVRTDataSource::OGRVRTDataSource(GDALDriver* poDriver) + +{ + pszName = NULL; + papoLayers = NULL; + paeLayerType = NULL; + nLayers = 0; + psTree = NULL; + nCallLevel = 0; + poLayerPool = NULL; + poParentDS = NULL; + bRecursionDetected = FALSE; + this->poDriver = poDriver; +} + +/************************************************************************/ +/* ~OGRVRTDataSource() */ +/************************************************************************/ + +OGRVRTDataSource::~OGRVRTDataSource() + +{ + int i; + + CPLFree( pszName ); + + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + + CPLFree( papoLayers ); + CPLFree( paeLayerType ); + + if( psTree != NULL) + CPLDestroyXMLNode( psTree ); + + delete poLayerPool; +} + +/************************************************************************/ +/* InstanciateWarpedLayer() */ +/************************************************************************/ + +OGRLayer* OGRVRTDataSource::InstanciateWarpedLayer( + CPLXMLNode *psLTree, + const char *pszVRTDirectory, + int bUpdate, + int nRecLevel) +{ + if( !EQUAL(psLTree->pszValue,"OGRVRTWarpedLayer") ) + return NULL; + + CPLXMLNode *psSubNode; + OGRLayer* poSrcLayer = NULL; + + for( psSubNode=psLTree->psChild; + psSubNode != NULL; + psSubNode=psSubNode->psNext ) + { + if( psSubNode->eType != CXT_Element ) + continue; + + poSrcLayer = InstanciateLayer(psSubNode, pszVRTDirectory, + bUpdate, nRecLevel + 1); + if( poSrcLayer != NULL ) + break; + } + + if( poSrcLayer == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot instanciate source layer" ); + return NULL; + } + + const char* pszTargetSRS = CPLGetXMLValue(psLTree, "TargetSRS", NULL); + if( pszTargetSRS == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing TargetSRS element within OGRVRTWarpedLayer" ); + delete poSrcLayer; + return NULL; + } + + const char* pszGeomFieldName = CPLGetXMLValue(psLTree, "WarpedGeomFieldName", NULL); + int iGeomField = 0; + if( pszGeomFieldName != NULL ) + { + iGeomField = poSrcLayer->GetLayerDefn()->GetGeomFieldIndex(pszGeomFieldName); + if( iGeomField < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot find source geometry field '%s'", pszGeomFieldName ); + delete poSrcLayer; + return NULL; + } + } + + OGRSpatialReference* poSrcSRS; + OGRSpatialReference* poTargetSRS; + const char* pszSourceSRS = CPLGetXMLValue(psLTree, "SrcSRS", NULL); + + if( pszSourceSRS == NULL ) + { + poSrcSRS = poSrcLayer->GetLayerDefn()->GetGeomFieldDefn(iGeomField)->GetSpatialRef(); + if( poSrcSRS != NULL) + poSrcSRS = poSrcSRS->Clone(); + } + else + { + poSrcSRS = new OGRSpatialReference(); + if( poSrcSRS->SetFromUserInput(pszSourceSRS) != OGRERR_NONE ) + { + delete poSrcSRS; + poSrcSRS = NULL; + } + } + + if( poSrcSRS == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to import source SRS" ); + delete poSrcLayer; + return NULL; + } + + poTargetSRS = new OGRSpatialReference(); + if( poTargetSRS->SetFromUserInput(pszTargetSRS) != OGRERR_NONE ) + { + delete poTargetSRS; + poTargetSRS = NULL; + } + + if( poTargetSRS == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to import target SRS" ); + delete poSrcSRS; + delete poSrcLayer; + return NULL; + } + + if( pszSourceSRS == NULL && poSrcSRS->IsSame(poTargetSRS) ) + { + delete poSrcSRS; + delete poTargetSRS; + return poSrcLayer; + } + + OGRCoordinateTransformation* poCT = + OGRCreateCoordinateTransformation( poSrcSRS, poTargetSRS ); + OGRCoordinateTransformation* poReversedCT = (poCT != NULL) ? + OGRCreateCoordinateTransformation( poTargetSRS, poSrcSRS ) : NULL; + + delete poSrcSRS; + delete poTargetSRS; + + if( poCT == NULL ) + { + delete poSrcLayer; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Build the OGRWarpedLayer. */ +/* -------------------------------------------------------------------- */ + + OGRWarpedLayer* poLayer = new OGRWarpedLayer(poSrcLayer, iGeomField, + TRUE, poCT, poReversedCT); + +/* -------------------------------------------------------------------- */ +/* Set Extent if provided */ +/* -------------------------------------------------------------------- */ + const char* pszExtentXMin = CPLGetXMLValue( psLTree, "ExtentXMin", NULL ); + const char* pszExtentYMin = CPLGetXMLValue( psLTree, "ExtentYMin", NULL ); + const char* pszExtentXMax = CPLGetXMLValue( psLTree, "ExtentXMax", NULL ); + const char* pszExtentYMax = CPLGetXMLValue( psLTree, "ExtentYMax", NULL ); + if( pszExtentXMin != NULL && pszExtentYMin != NULL && + pszExtentXMax != NULL && pszExtentYMax != NULL ) + { + poLayer->SetExtent( CPLAtof(pszExtentXMin), + CPLAtof(pszExtentYMin), + CPLAtof(pszExtentXMax), + CPLAtof(pszExtentYMax) ); + } + + return poLayer; +} + +/************************************************************************/ +/* InstanciateUnionLayer() */ +/************************************************************************/ + +OGRLayer* OGRVRTDataSource::InstanciateUnionLayer( + CPLXMLNode *psLTree, + const char *pszVRTDirectory, + int bUpdate, + int nRecLevel) +{ + CPLXMLNode *psSubNode; + + if( !EQUAL(psLTree->pszValue,"OGRVRTUnionLayer") ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Get layer name. */ +/* -------------------------------------------------------------------- */ + const char *pszLayerName = CPLGetXMLValue( psLTree, "name", NULL ); + + if( pszLayerName == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing name attribute on OGRVRTUnionLayer" ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Do we have a fixed geometry type? If not derive from the */ +/* source layer. */ +/* -------------------------------------------------------------------- */ + const char* pszGType = CPLGetXMLValue( psLTree, "GeometryType", NULL ); + int bGlobalGeomTypeSet = FALSE; + OGRwkbGeometryType eGlobalGeomType = wkbUnknown; + if( pszGType != NULL ) + { + int bError; + bGlobalGeomTypeSet = TRUE; + eGlobalGeomType = OGRVRTGetGeometryType(pszGType, &bError); + if( bError ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GeometryType %s not recognised.", + pszGType ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Apply a spatial reference system if provided */ +/* -------------------------------------------------------------------- */ + const char* pszLayerSRS = CPLGetXMLValue( psLTree, "LayerSRS", NULL ); + OGRSpatialReference* poGlobalSRS = NULL; + int bGlobalSRSSet = FALSE; + if( pszLayerSRS != NULL ) + { + bGlobalSRSSet = TRUE; + if( !EQUAL(pszLayerSRS,"NULL") ) + { + OGRSpatialReference oSRS; + + if( oSRS.SetFromUserInput( pszLayerSRS ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to import LayerSRS `%s'.", pszLayerSRS ); + return FALSE; + } + poGlobalSRS = oSRS.Clone(); + } + } + +/* -------------------------------------------------------------------- */ +/* Find field declarations. */ +/* -------------------------------------------------------------------- */ + OGRFieldDefn** papoFields = NULL; + int nFields = 0; + OGRUnionLayerGeomFieldDefn** papoGeomFields = NULL; + int nGeomFields = 0; + + for( psSubNode=psLTree->psChild; + psSubNode != NULL; + psSubNode=psSubNode->psNext ) + { + if( psSubNode->eType != CXT_Element ) + continue; + + if( psSubNode->eType == CXT_Element && EQUAL(psSubNode->pszValue,"Field") ) + { +/* -------------------------------------------------------------------- */ +/* Field name. */ +/* -------------------------------------------------------------------- */ + const char *pszName = CPLGetXMLValue( psSubNode, "name", NULL ); + if( pszName == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify Field name." ); + break; + } + + OGRFieldDefn oFieldDefn( pszName, OFTString ); + +/* -------------------------------------------------------------------- */ +/* Type */ +/* -------------------------------------------------------------------- */ + const char *pszArg = CPLGetXMLValue( psSubNode, "type", NULL ); + + if( pszArg != NULL ) + { + int iType; + + for( iType = 0; iType <= (int) OFTMaxType; iType++ ) + { + if( EQUAL(pszArg,OGRFieldDefn::GetFieldTypeName( + (OGRFieldType)iType)) ) + { + oFieldDefn.SetType( (OGRFieldType) iType ); + break; + } + } + + if( iType > (int) OFTMaxType ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify Field type '%s'.", + pszArg ); + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Width and precision. */ +/* -------------------------------------------------------------------- */ + int nWidth = atoi(CPLGetXMLValue( psSubNode, "width", "0" )); + if (nWidth < 0) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Invalid width for field %s.", + pszName ); + break; + } + oFieldDefn.SetWidth(nWidth); + + int nPrecision = atoi(CPLGetXMLValue( psSubNode, "precision", "0" )); + if (nPrecision < 0 || nPrecision > 1024) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Invalid precision for field %s.", + pszName ); + break; + } + oFieldDefn.SetPrecision(nPrecision); + + papoFields = (OGRFieldDefn**) CPLRealloc(papoFields, + sizeof(OGRFieldDefn*) * (nFields + 1)); + papoFields[nFields] = new OGRFieldDefn(&oFieldDefn); + nFields ++; + } + + else if( psSubNode->eType == CXT_Element && + EQUAL(psSubNode->pszValue,"GeometryField") ) + { + const char *pszName = CPLGetXMLValue( psSubNode, "name", NULL ); + if( pszName == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify GeometryField name." ); + break; + } + + pszGType = CPLGetXMLValue( psSubNode, "GeometryType", NULL ); + if( pszGType == NULL && nGeomFields == 0 ) + pszGType = CPLGetXMLValue( psLTree, "GeometryType", NULL ); + OGRwkbGeometryType eGeomType = wkbUnknown; + int bGeomTypeSet = FALSE; + if( pszGType != NULL ) + { + int bError; + eGeomType = OGRVRTGetGeometryType(pszGType, &bError); + bGeomTypeSet = TRUE; + if( bError || eGeomType == wkbNone ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GeometryType %s not recognised.", + pszGType ); + break; + } + } + + const char* pszSRS = CPLGetXMLValue( psSubNode, "SRS", NULL ); + if( pszSRS == NULL && nGeomFields == 0 ) + pszSRS = CPLGetXMLValue( psLTree, "LayerSRS", NULL ); + OGRSpatialReference* poSRS = NULL; + int bSRSSet = FALSE; + if( pszSRS != NULL ) + { + bSRSSet = TRUE; + if( !EQUAL(pszSRS,"NULL") ) + { + OGRSpatialReference oSRS; + + if( oSRS.SetFromUserInput( pszSRS ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to import SRS `%s'.", pszSRS ); + break; + } + poSRS = oSRS.Clone(); + } + } + + OGRUnionLayerGeomFieldDefn* poFieldDefn = + new OGRUnionLayerGeomFieldDefn(pszName, eGeomType); + if( poSRS != NULL ) + { + poFieldDefn->SetSpatialRef(poSRS); + poSRS->Dereference(); + } + poFieldDefn->bGeomTypeSet = bGeomTypeSet; + poFieldDefn->bSRSSet = bSRSSet; + + const char* pszExtentXMin = CPLGetXMLValue( psSubNode, "ExtentXMin", NULL ); + const char* pszExtentYMin = CPLGetXMLValue( psSubNode, "ExtentYMin", NULL ); + const char* pszExtentXMax = CPLGetXMLValue( psSubNode, "ExtentXMax", NULL ); + const char* pszExtentYMax = CPLGetXMLValue( psSubNode, "ExtentYMax", NULL ); + if( pszExtentXMin != NULL && pszExtentYMin != NULL && + pszExtentXMax != NULL && pszExtentYMax != NULL ) + { + poFieldDefn->sStaticEnvelope.MinX = CPLAtof(pszExtentXMin); + poFieldDefn->sStaticEnvelope.MinY = CPLAtof(pszExtentYMin); + poFieldDefn->sStaticEnvelope.MaxX = CPLAtof(pszExtentXMax); + poFieldDefn->sStaticEnvelope.MaxY = CPLAtof(pszExtentYMax); + } + + papoGeomFields = (OGRUnionLayerGeomFieldDefn**) CPLRealloc(papoGeomFields, + sizeof(OGRUnionLayerGeomFieldDefn*) * (nGeomFields + 1)); + papoGeomFields[nGeomFields] = poFieldDefn; + nGeomFields ++; + } + } + +/* -------------------------------------------------------------------- */ +/* Set Extent if provided */ +/* -------------------------------------------------------------------- */ + const char* pszExtentXMin = CPLGetXMLValue( psLTree, "ExtentXMin", NULL ); + const char* pszExtentYMin = CPLGetXMLValue( psLTree, "ExtentYMin", NULL ); + const char* pszExtentXMax = CPLGetXMLValue( psLTree, "ExtentXMax", NULL ); + const char* pszExtentYMax = CPLGetXMLValue( psLTree, "ExtentYMax", NULL ); + + if( eGlobalGeomType != wkbNone && nGeomFields == 0 && + (bGlobalGeomTypeSet || bGlobalSRSSet || + (pszExtentXMin != NULL && pszExtentYMin != NULL && + pszExtentXMax != NULL && pszExtentYMax != NULL)) ) + { + OGRUnionLayerGeomFieldDefn* poFieldDefn = + new OGRUnionLayerGeomFieldDefn("", eGlobalGeomType); + if( poGlobalSRS != NULL ) + { + poFieldDefn->SetSpatialRef(poGlobalSRS); + poGlobalSRS->Dereference(); + poGlobalSRS = NULL; + } + poFieldDefn->bGeomTypeSet = bGlobalGeomTypeSet; + poFieldDefn->bSRSSet = bGlobalSRSSet; + if( pszExtentXMin != NULL && pszExtentYMin != NULL && + pszExtentXMax != NULL && pszExtentYMax != NULL ) + { + poFieldDefn->sStaticEnvelope.MinX = CPLAtof(pszExtentXMin); + poFieldDefn->sStaticEnvelope.MinY = CPLAtof(pszExtentYMin); + poFieldDefn->sStaticEnvelope.MaxX = CPLAtof(pszExtentXMax); + poFieldDefn->sStaticEnvelope.MaxY = CPLAtof(pszExtentYMax); + } + + papoGeomFields = (OGRUnionLayerGeomFieldDefn**) CPLRealloc(papoGeomFields, + sizeof(OGRUnionLayerGeomFieldDefn*) * (nGeomFields + 1)); + papoGeomFields[nGeomFields] = poFieldDefn; + nGeomFields ++; + } + else + { + delete poGlobalSRS; + poGlobalSRS = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Find source layers */ +/* -------------------------------------------------------------------- */ + + int nSrcLayers = 0; + OGRLayer** papoSrcLayers = NULL; + + for( psSubNode=psLTree->psChild; + psSubNode != NULL; + psSubNode=psSubNode->psNext ) + { + if( psSubNode->eType != CXT_Element ) + continue; + + OGRLayer* poSrcLayer = InstanciateLayer(psSubNode, pszVRTDirectory, + bUpdate, nRecLevel + 1); + if( poSrcLayer != NULL ) + { + papoSrcLayers = (OGRLayer**) + CPLRealloc(papoSrcLayers, sizeof(OGRLayer*) * (nSrcLayers + 1)); + papoSrcLayers[nSrcLayers] = poSrcLayer; + nSrcLayers ++; + } + } + + if( nSrcLayers == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot find source layers" ); + int iField; + for(iField = 0; iField < nFields; iField++) + delete papoFields[iField]; + CPLFree(papoFields); + for(iField = 0; iField < nGeomFields; iField++) + delete papoGeomFields[iField]; + CPLFree(papoGeomFields); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Build the OGRUnionLayer. */ +/* -------------------------------------------------------------------- */ + OGRUnionLayer* poLayer = new OGRUnionLayer( pszLayerName, + nSrcLayers, + papoSrcLayers, + TRUE ); + +/* -------------------------------------------------------------------- */ +/* Set the source layer field name attribute. */ +/* -------------------------------------------------------------------- */ + const char* pszSourceLayerFieldName = + CPLGetXMLValue( psLTree, "SourceLayerFieldName", NULL ); + poLayer->SetSourceLayerFieldName(pszSourceLayerFieldName); + +/* -------------------------------------------------------------------- */ +/* Set the PreserveSrcFID attribute. */ +/* -------------------------------------------------------------------- */ + int bPreserveSrcFID = FALSE; + const char* pszPreserveFID = CPLGetXMLValue( psLTree, "PreserveSrcFID", NULL ); + if( pszPreserveFID != NULL ) + bPreserveSrcFID = CSLTestBoolean(pszPreserveFID); + poLayer->SetPreserveSrcFID(bPreserveSrcFID); + +/* -------------------------------------------------------------------- */ +/* Set fields */ +/* -------------------------------------------------------------------- */ + FieldUnionStrategy eFieldStrategy = FIELD_UNION_ALL_LAYERS; + const char* pszFieldStrategy = CPLGetXMLValue( psLTree, "FieldStrategy", NULL ); + if( pszFieldStrategy != NULL ) + { + if( EQUAL(pszFieldStrategy, "FirstLayer") ) + eFieldStrategy = FIELD_FROM_FIRST_LAYER; + else if( EQUAL(pszFieldStrategy, "Union") ) + eFieldStrategy = FIELD_UNION_ALL_LAYERS; + else if( EQUAL(pszFieldStrategy, "Intersection") ) + eFieldStrategy = FIELD_INTERSECTION_ALL_LAYERS; + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unhandled value for FieldStrategy `%s'.", pszFieldStrategy ); + } + } + if( nFields != 0 || nGeomFields > 1 ) + { + if( pszFieldStrategy != NULL ) + CPLError( CE_Warning, CPLE_AppDefined, + "Ignoring FieldStrategy value, because explicit Field or GeometryField is provided") ; + eFieldStrategy = FIELD_SPECIFIED; + } + + poLayer->SetFields(eFieldStrategy, nFields, papoFields, + (nGeomFields == 0 && eGlobalGeomType == wkbNone) ? -1 : nGeomFields, + papoGeomFields); + int iField; + for(iField = 0; iField < nFields; iField++) + delete papoFields[iField]; + CPLFree(papoFields); + for(iField = 0; iField < nGeomFields; iField++) + delete papoGeomFields[iField]; + CPLFree(papoGeomFields); + +/* -------------------------------------------------------------------- */ +/* Set FeatureCount if provided */ +/* -------------------------------------------------------------------- */ + const char* pszFeatureCount = CPLGetXMLValue( psLTree, "FeatureCount", NULL ); + if( pszFeatureCount != NULL ) + { + poLayer->SetFeatureCount(atoi(pszFeatureCount)); + } + + return poLayer; +} + +/************************************************************************/ +/* InstanciateLayerInternal() */ +/************************************************************************/ + +OGRLayer* OGRVRTDataSource::InstanciateLayerInternal(CPLXMLNode *psLTree, + const char *pszVRTDirectory, + int bUpdate, + int nRecLevel) +{ +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + if( EQUAL(psLTree->pszValue,"OGRVRTLayer") ) + { + OGRVRTLayer* poVRTLayer = new OGRVRTLayer(this); + + if( !poVRTLayer->FastInitialize( psLTree, pszVRTDirectory, bUpdate ) ) + { + delete poVRTLayer; + return NULL; + } + + return poVRTLayer; + } + else if( EQUAL(psLTree->pszValue,"OGRVRTWarpedLayer") && nRecLevel < 30 ) + { + return InstanciateWarpedLayer( psLTree, pszVRTDirectory, + bUpdate, nRecLevel + 1 ); + } + else if( EQUAL(psLTree->pszValue,"OGRVRTUnionLayer") && nRecLevel < 30 ) + { + return InstanciateUnionLayer( psLTree, pszVRTDirectory, + bUpdate, nRecLevel + 1 ); + } + else + return NULL; +} + +/************************************************************************/ +/* OGRVRTOpenProxiedLayer() */ +/************************************************************************/ + +typedef struct +{ + OGRVRTDataSource* poDS; + CPLXMLNode *psNode; + char *pszVRTDirectory; + int bUpdate; +} PooledInitData; + +static OGRLayer* OGRVRTOpenProxiedLayer(void* pUserData) +{ + PooledInitData* pData = (PooledInitData*) pUserData; + return pData->poDS->InstanciateLayerInternal(pData->psNode, + pData->pszVRTDirectory, + pData->bUpdate, + 0); +} + +/************************************************************************/ +/* OGRVRTFreeProxiedLayerUserData() */ +/************************************************************************/ + +static void OGRVRTFreeProxiedLayerUserData(void* pUserData) +{ + PooledInitData* pData = (PooledInitData*) pUserData; + CPLFree(pData->pszVRTDirectory); + CPLFree(pData); +} + +/************************************************************************/ +/* InstanciateLayer() */ +/************************************************************************/ + +OGRLayer* OGRVRTDataSource::InstanciateLayer(CPLXMLNode *psLTree, + const char *pszVRTDirectory, + int bUpdate, + int nRecLevel) +{ + if( poLayerPool != NULL && EQUAL(psLTree->pszValue,"OGRVRTLayer")) + { + PooledInitData* pData = (PooledInitData*) CPLMalloc(sizeof(PooledInitData)); + pData->poDS = this; + pData->psNode = psLTree; + pData->pszVRTDirectory = CPLStrdup(pszVRTDirectory); + pData->bUpdate = bUpdate; + return new OGRProxiedLayer(poLayerPool, + OGRVRTOpenProxiedLayer, + OGRVRTFreeProxiedLayerUserData, + pData); + } + else + { + return InstanciateLayerInternal(psLTree, pszVRTDirectory, + bUpdate, nRecLevel); + } +} + +/************************************************************************/ +/* CountOGRVRTLayers() */ +/************************************************************************/ + +static int CountOGRVRTLayers(CPLXMLNode *psTree) +{ + if( psTree->eType != CXT_Element ) + return 0; + + int nCount = 0; + if( EQUAL(psTree->pszValue, "OGRVRTLayer") ) + nCount ++; + + CPLXMLNode* psNode; + for( psNode=psTree->psChild; psNode != NULL; psNode=psNode->psNext ) + { + nCount += CountOGRVRTLayers(psNode); + } + + return nCount; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +int OGRVRTDataSource::Initialize( CPLXMLNode *psTree, const char *pszNewName, + int bUpdate ) + +{ + CPLAssert( nLayers == 0 ); + + this->psTree = psTree; + +/* -------------------------------------------------------------------- */ +/* Set name, and capture the directory path so we can use it */ +/* for relative datasources. */ +/* -------------------------------------------------------------------- */ + CPLString osVRTDirectory = CPLGetPath( pszNewName ); + + pszName = CPLStrdup( pszNewName ); + +/* -------------------------------------------------------------------- */ +/* Look for the OGRVRTDataSource node, it might be after an */ +/* node. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psVRTDSXML = CPLGetXMLNode( psTree, "=OGRVRTDataSource" ); + if( psVRTDSXML == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Did not find the node in the root of the document,\n" + "this is not really an OGR VRT." ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Determine if we must proxy layers. */ +/* -------------------------------------------------------------------- */ + int nOGRVRTLayerCount = CountOGRVRTLayers(psVRTDSXML); + + int nMaxSimultaneouslyOpened = atoi(CPLGetConfigOption("OGR_VRT_MAX_OPENED", "100")); + if( nMaxSimultaneouslyOpened < 1 ) + nMaxSimultaneouslyOpened = 1; + if( nOGRVRTLayerCount > nMaxSimultaneouslyOpened ) + poLayerPool = new OGRLayerPool(nMaxSimultaneouslyOpened); + +/* -------------------------------------------------------------------- */ +/* Apply any dataset level metadata. */ +/* -------------------------------------------------------------------- */ + oMDMD.XMLInit( psVRTDSXML, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Look for layers. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psLTree; + + for( psLTree=psVRTDSXML->psChild; psLTree != NULL; psLTree=psLTree->psNext ) + { + if( psLTree->eType != CXT_Element ) + continue; + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRLayer *poLayer = InstanciateLayer(psLTree, osVRTDirectory, bUpdate); + if( poLayer == NULL ) + continue; + +/* -------------------------------------------------------------------- */ +/* Add layer to data source layer list. */ +/* -------------------------------------------------------------------- */ + nLayers ++; + papoLayers = (OGRLayer **) + CPLRealloc( papoLayers, sizeof(OGRLayer *) * nLayers ); + papoLayers[nLayers-1] = poLayer; + + paeLayerType = (OGRLayerType*) + CPLRealloc( paeLayerType, sizeof(int) * nLayers ); + if( poLayerPool != NULL && EQUAL(psLTree->pszValue,"OGRVRTLayer")) + { + paeLayerType[nLayers - 1] = OGR_VRT_PROXIED_LAYER; + } + else if( EQUAL(psLTree->pszValue,"OGRVRTLayer") ) + { + paeLayerType[nLayers - 1] = OGR_VRT_LAYER; + } + else + { + paeLayerType[nLayers - 1] = OGR_VRT_OTHER_LAYER; + } + } + + return TRUE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRVRTDataSource::TestCapability( const char * pszCap ) +{ + if( EQUAL(pszCap,ODsCCurveGeometries) ) + return TRUE; + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRVRTDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* AddForbiddenNames() */ +/************************************************************************/ + +void OGRVRTDataSource::AddForbiddenNames(const char* pszOtherDSName) +{ + aosOtherDSNameSet.insert(pszOtherDSName); +} + +/************************************************************************/ +/* IsInForbiddenNames() */ +/************************************************************************/ + +int OGRVRTDataSource::IsInForbiddenNames(const char* pszOtherDSName) +{ + return aosOtherDSNameSet.find(pszOtherDSName) != aosOtherDSNameSet.end(); +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **OGRVRTDataSource::GetFileList() +{ + CPLStringList oList; + oList.AddString( GetName() ); + for(int i=0; iGetUnderlyingLayer(); + break; + case OGR_VRT_LAYER: + poVRTLayer = (OGRVRTLayer*) poLayer; + break; + default: + break; + } + if( poVRTLayer != NULL ) + { + GDALDataset* poSrcDS = poVRTLayer->GetSrcDataset(); + if( poSrcDS != NULL ) + { + char** papszFileList = poSrcDS->GetFileList(); + char** papszIter = papszFileList; + for(; papszIter != NULL && *papszIter != NULL; papszIter++ ) + { + if( oList.FindString(*papszIter) < 0 ) + oList.AddString(*papszIter); + } + CSLDestroy(papszFileList); + } + } + } + return oList.StealList(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/ogrvrtdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/ogrvrtdriver.cpp new file mode 100644 index 000000000..3f121ed84 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/ogrvrtdriver.cpp @@ -0,0 +1,217 @@ +/****************************************************************************** + * $Id: ogrvrtdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRVRTDriver class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2009-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_vrt.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrvrtdriver.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRVRTErrorHandler() */ +/************************************************************************/ + +static void CPL_STDCALL OGRVRTErrorHandler(CPL_UNUSED CPLErr eErr, + CPL_UNUSED int nType, + const char* pszMsg) +{ + std::vector* paosErrors = (std::vector* )CPLGetErrorHandlerUserData(); + paosErrors->push_back(pszMsg); +} + +/************************************************************************/ +/* OGRVRTDriverIdentify() */ +/************************************************************************/ + +static int OGRVRTDriverIdentify( GDALOpenInfo* poOpenInfo ) +{ + if( !poOpenInfo->bStatOK ) + { +/* -------------------------------------------------------------------- */ +/* Are we being passed the XML definition directly? */ +/* Skip any leading spaces/blanks. */ +/* -------------------------------------------------------------------- */ + const char *pszTestXML = poOpenInfo->pszFilename; + while( *pszTestXML != '\0' && isspace( (unsigned char)*pszTestXML ) ) + pszTestXML++; + if( EQUALN(pszTestXML,"",18) ) + { + return TRUE; + } + return FALSE; + } + + return ( poOpenInfo->fpL != NULL && + strstr((const char*)poOpenInfo->pabyHeader,"pszFilename; + while( *pszTestXML != '\0' && isspace( (unsigned char)*pszTestXML ) ) + pszTestXML++; + + if( EQUALN(pszTestXML,"",18) ) + { + pszXML = CPLStrdup(pszTestXML); + } + +/* -------------------------------------------------------------------- */ +/* Open file and check if it contains appropriate XML. */ +/* -------------------------------------------------------------------- */ + else + { + VSIStatBufL sStatBuf; + if ( VSIStatL( poOpenInfo->pszFilename, &sStatBuf ) != 0 || + sStatBuf.st_size > 1024 * 1024 ) + { + CPLDebug( "VRT", "Unreasonable long file, not likely really VRT" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* It is the right file, now load the full XML definition. */ +/* -------------------------------------------------------------------- */ + int nLen = (int) sStatBuf.st_size; + + pszXML = (char *) VSIMalloc(nLen+1); + if (pszXML == NULL) + return NULL; + + pszXML[nLen] = '\0'; + VSIFSeekL( poOpenInfo->fpL, 0, SEEK_SET ); + if( ((int) VSIFReadL( pszXML, 1, nLen, poOpenInfo->fpL )) != nLen ) + { + CPLFree( pszXML ); + return NULL; + } + VSIFCloseL( poOpenInfo->fpL ); + poOpenInfo->fpL = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Parse the XML. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psTree = CPLParseXMLString( pszXML ); + + if( psTree == NULL ) + { + CPLFree( pszXML ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* XML Validation. */ +/* -------------------------------------------------------------------- */ + if( CSLTestBoolean(CPLGetConfigOption("GDAL_XML_VALIDATION", "YES")) ) + { + const char* pszXSD = CPLFindFile( "gdal", "ogrvrt.xsd" ); + if( pszXSD != NULL ) + { + std::vector aosErrors; + CPLPushErrorHandlerEx(OGRVRTErrorHandler, &aosErrors); + int bRet = CPLValidateXML(pszXML, pszXSD, NULL); + CPLPopErrorHandler(); + if( !bRet ) + { + if( aosErrors.size() > 0 && + strstr(aosErrors[0].c_str(), "missing libxml2 support") == NULL ) + { + for(size_t i = 0; i < aosErrors.size(); i++) + { + CPLError(CE_Warning, CPLE_AppDefined, "%s", aosErrors[i].c_str()); + } + } + } + CPLErrorReset(); + } + } + CPLFree( pszXML ); + +/* -------------------------------------------------------------------- */ +/* Create a virtual datasource configured based on this XML input. */ +/* -------------------------------------------------------------------- */ + poDS = new OGRVRTDataSource((GDALDriver*)GDALGetDriverByName( "OGR_VRT" )); + + /* psTree is owned by poDS */ + if( !poDS->Initialize( psTree, poOpenInfo->pszFilename, + poOpenInfo->eAccess == GA_Update ) ) + { + delete poDS; + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRVRT() */ +/************************************************************************/ + +void RegisterOGRVRT() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "OGR_VRT" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "OGR_VRT" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "VRT - Virtual Datasource" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "vrt" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_vrt.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnOpen = OGRVRTDriverOpen; + poDriver->pfnIdentify = OGRVRTDriverIdentify; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/ogrvrtlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/ogrvrtlayer.cpp new file mode 100644 index 000000000..2efa7056e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/vrt/ogrvrtlayer.cpp @@ -0,0 +1,2476 @@ +/****************************************************************************** + * $Id: ogrvrtlayer.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRVRTLayer class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2009-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogr_vrt.h" +#include "cpl_string.h" +#include "ogrpgeogeometry.h" +#include + +CPL_CVSID("$Id: ogrvrtlayer.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +#define UNSUPPORTED_OP_READ_ONLY "%s : unsupported operation on a read-only datasource." + + +/************************************************************************/ +/* OGRVRTGeomFieldProps() */ +/************************************************************************/ + +OGRVRTGeomFieldProps::OGRVRTGeomFieldProps() +{ + eGeomType = wkbUnknown; + bUseSpatialSubquery = FALSE; + eGeometryStyle = VGS_Direct; + poSRS = NULL; + iGeomField = iGeomXField = iGeomYField = iGeomZField = -1; + bSrcClip = FALSE; + poSrcRegion = NULL; + bReportSrcColumn = TRUE; + bNullable = TRUE; +} + +/************************************************************************/ +/* ~OGRVRTGeomFieldProps() */ +/************************************************************************/ + +OGRVRTGeomFieldProps::~OGRVRTGeomFieldProps() +{ + if( poSRS != NULL ) + poSRS->Release(); + if( poSrcRegion != NULL ) + delete poSrcRegion; +} + +/************************************************************************/ +/* OGRVRTLayer() */ +/************************************************************************/ + +OGRVRTLayer::OGRVRTLayer(OGRVRTDataSource* poDSIn) + +{ + poDS = poDSIn; + + bHasFullInitialized = FALSE; + psLTree = NULL; + + poFeatureDefn = NULL; + poSrcLayer = NULL; + poSrcDS = NULL; + poSrcFeatureDefn = NULL; + + iFIDField = -1; + iStyleField = -1; + + pszAttrFilter = NULL; + + bNeedReset = TRUE; + bSrcLayerFromSQL = FALSE; + + bUpdate = FALSE; + bAttrFilterPassThrough = FALSE; + + nFeatureCount = -1; + bError = FALSE; +} + +/************************************************************************/ +/* ~OGRVRTLayer() */ +/************************************************************************/ + +OGRVRTLayer::~OGRVRTLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "VRT", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + for(size_t i=0;iSetIgnoredFields(NULL); + poSrcLayer->SetAttributeFilter(NULL); + poSrcLayer->SetSpatialFilter(NULL); + } + + if( bSrcLayerFromSQL && poSrcLayer ) + poSrcDS->ReleaseResultSet( poSrcLayer ); + + GDALClose( (GDALDatasetH) poSrcDS ); + } + + if( poFeatureDefn ) + poFeatureDefn->Release(); + + CPLFree( pszAttrFilter ); +} + +/************************************************************************/ +/* GetSrcLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn* OGRVRTLayer::GetSrcLayerDefn() +{ + if (poSrcFeatureDefn) + return poSrcFeatureDefn; + + if (poSrcLayer) + poSrcFeatureDefn = poSrcLayer->GetLayerDefn(); + + return poSrcFeatureDefn; +} + +/************************************************************************/ +/* FastInitialize() */ +/************************************************************************/ + +int OGRVRTLayer::FastInitialize( CPLXMLNode *psLTree, const char *pszVRTDirectory, + int bUpdate) + +{ + this->psLTree = psLTree; + this->bUpdate = bUpdate; + osVRTDirectory = pszVRTDirectory; + + if( !EQUAL(psLTree->pszValue,"OGRVRTLayer") ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Get layer name. */ +/* -------------------------------------------------------------------- */ + const char *pszLayerName = CPLGetXMLValue( psLTree, "name", NULL ); + + if( pszLayerName == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing name attribute on OGRVRTLayer" ); + return FALSE; + } + + osName = pszLayerName; + SetDescription( pszLayerName ); + +/* -------------------------------------------------------------------- */ +/* Do we have a fixed geometry type? If so use it */ +/* -------------------------------------------------------------------- */ + CPLXMLNode* psGeometryFieldNode = CPLGetXMLNode(psLTree, "GeometryField"); + const char *pszGType = CPLGetXMLValue( psLTree, "GeometryType", NULL ); + if( pszGType == NULL && psGeometryFieldNode != NULL ) + pszGType = CPLGetXMLValue( psGeometryFieldNode, "GeometryType", NULL ); + OGRwkbGeometryType eGeomType = wkbUnknown; + if( pszGType != NULL ) + { + int bError; + eGeomType = OGRVRTGetGeometryType(pszGType, &bError); + if( bError ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GeometryType %s not recognised.", + pszGType ); + return FALSE; + } + } + + if( eGeomType != wkbNone ) + { + apoGeomFieldProps.push_back(new OGRVRTGeomFieldProps()); + apoGeomFieldProps[0]->eGeomType = eGeomType; + } + +/* -------------------------------------------------------------------- */ +/* Apply a spatial reference system if provided */ +/* -------------------------------------------------------------------- */ + const char* pszLayerSRS = CPLGetXMLValue( psLTree, "LayerSRS", NULL ); + if( pszLayerSRS == NULL && psGeometryFieldNode != NULL ) + pszLayerSRS = CPLGetXMLValue( psGeometryFieldNode, "SRS", NULL ); + if( apoGeomFieldProps.size() != 0 && pszLayerSRS != NULL ) + { + if( !(EQUAL(pszLayerSRS,"NULL")) ) + { + OGRSpatialReference oSRS; + + if( oSRS.SetFromUserInput( pszLayerSRS ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to import LayerSRS `%s'.", pszLayerSRS ); + return FALSE; + } + apoGeomFieldProps[0]->poSRS = oSRS.Clone(); + } + } + +/* -------------------------------------------------------------------- */ +/* Set FeatureCount if provided */ +/* -------------------------------------------------------------------- */ + const char* pszFeatureCount = CPLGetXMLValue( psLTree, "FeatureCount", NULL ); + if( pszFeatureCount != NULL ) + { + nFeatureCount = CPLAtoGIntBig(pszFeatureCount); + } + +/* -------------------------------------------------------------------- */ +/* Set Extent if provided */ +/* -------------------------------------------------------------------- */ + const char* pszExtentXMin = CPLGetXMLValue( psLTree, "ExtentXMin", NULL ); + const char* pszExtentYMin = CPLGetXMLValue( psLTree, "ExtentYMin", NULL ); + const char* pszExtentXMax = CPLGetXMLValue( psLTree, "ExtentXMax", NULL ); + const char* pszExtentYMax = CPLGetXMLValue( psLTree, "ExtentYMax", NULL ); + if( pszExtentXMin == NULL && psGeometryFieldNode != NULL ) + { + pszExtentXMin = CPLGetXMLValue( psGeometryFieldNode, "ExtentXMin", NULL ); + pszExtentYMin = CPLGetXMLValue( psGeometryFieldNode, "ExtentYMin", NULL ); + pszExtentXMax = CPLGetXMLValue( psGeometryFieldNode, "ExtentXMax", NULL ); + pszExtentYMax = CPLGetXMLValue( psGeometryFieldNode, "ExtentYMax", NULL ); + } + if( apoGeomFieldProps.size() != 0 && + pszExtentXMin != NULL && pszExtentYMin != NULL && + pszExtentXMax != NULL && pszExtentYMax != NULL ) + { + apoGeomFieldProps[0]->sStaticEnvelope.MinX = CPLAtof(pszExtentXMin); + apoGeomFieldProps[0]->sStaticEnvelope.MinY = CPLAtof(pszExtentYMin); + apoGeomFieldProps[0]->sStaticEnvelope.MaxX = CPLAtof(pszExtentXMax); + apoGeomFieldProps[0]->sStaticEnvelope.MaxY = CPLAtof(pszExtentYMax); + } + + return TRUE; +} + +/************************************************************************/ +/* ParseGeometryField() */ +/************************************************************************/ + +int OGRVRTLayer::ParseGeometryField(CPLXMLNode* psNode, + CPLXMLNode* psNodeParent, + OGRVRTGeomFieldProps* poProps) +{ + const char* pszName = CPLGetXMLValue( psNode, "name", NULL); + poProps->osName = (pszName ) ? pszName : ""; + if( pszName == NULL && + apoGeomFieldProps.size() > 1 && poProps != apoGeomFieldProps[0] ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "A 'name' attribute should be defined when there are several geometry fields"); + } + +/* -------------------------------------------------------------------- */ +/* Do we have a fixed geometry type? */ +/* -------------------------------------------------------------------- */ + const char* pszGType = CPLGetXMLValue( psNode, "GeometryType", NULL ); + if( pszGType == NULL && poProps == apoGeomFieldProps[0] ) + pszGType = CPLGetXMLValue( psNodeParent, "GeometryType", NULL ); + if( pszGType != NULL ) + { + int bError; + poProps->eGeomType = OGRVRTGetGeometryType(pszGType, &bError); + if( bError ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GeometryType %s not recognised.", + pszGType ); + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Determine which field(s) to get the geometry from */ +/* -------------------------------------------------------------------- */ + const char *pszEncoding = CPLGetXMLValue( psNode,"encoding", "direct"); + + if( EQUAL(pszEncoding,"Direct") ) + poProps->eGeometryStyle = VGS_Direct; + else if( EQUAL(pszEncoding,"None") ) + poProps->eGeometryStyle = VGS_None; + else if( EQUAL(pszEncoding,"WKT") ) + poProps->eGeometryStyle = VGS_WKT; + else if( EQUAL(pszEncoding,"WKB") ) + poProps->eGeometryStyle = VGS_WKB; + else if( EQUAL(pszEncoding,"Shape") ) + poProps->eGeometryStyle = VGS_Shape; + else if( EQUAL(pszEncoding,"PointFromColumns") ) + { + poProps->eGeometryStyle = VGS_PointFromColumns; + poProps->bUseSpatialSubquery = + CSLTestBoolean( + CPLGetXMLValue(psNode, + "GeometryField.useSpatialSubquery", + "TRUE")); + + poProps->iGeomXField = GetSrcLayerDefn()->GetFieldIndex( + CPLGetXMLValue( psNode, "x", "missing" ) ); + poProps->iGeomYField = GetSrcLayerDefn()->GetFieldIndex( + CPLGetXMLValue( psNode, "y", "missing" ) ); + poProps->iGeomZField = GetSrcLayerDefn()->GetFieldIndex( + CPLGetXMLValue( psNode, "z", "missing" ) ); + + if( poProps->iGeomXField == -1 || poProps->iGeomYField == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify source X or Y field for PointFromColumns encoding." ); + return FALSE; + } + + if( pszGType == NULL ) + { + if( poProps->iGeomZField != -1 ) + poProps->eGeomType = wkbPoint25D; + else + poProps->eGeomType = wkbPoint; + } + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "encoding=\"%s\" not recognised.", pszEncoding ); + return FALSE; + } + + if( poProps->eGeometryStyle == VGS_WKT + || poProps->eGeometryStyle == VGS_WKB + || poProps->eGeometryStyle == VGS_Shape ) + { + const char *pszFieldName = + CPLGetXMLValue( psNode, "field", "missing" ); + + poProps->iGeomField = GetSrcLayerDefn()->GetFieldIndex(pszFieldName); + + if( poProps->iGeomField == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify source field '%s' for geometry.", + pszFieldName ); + return FALSE; + } + } + else if( poProps->eGeometryStyle == VGS_Direct ) + { + const char *pszFieldName = + CPLGetXMLValue( psNode, "field", NULL ); + + if( pszFieldName != NULL || GetSrcLayerDefn()->GetGeomFieldCount() > 1 ) + { + if( pszFieldName == NULL ) + pszFieldName = poProps->osName; + poProps->iGeomField = GetSrcLayerDefn()->GetGeomFieldIndex(pszFieldName); + + if( poProps->iGeomField == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify source geometry field '%s' for geometry.", + pszFieldName ); + return FALSE; + } + } + else if( GetSrcLayerDefn()->GetGeomFieldCount() == 1 ) + { + poProps->iGeomField = 0; + } + else if( psNode != NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify source geometry field." ); + return FALSE; + } + } + + poProps->bReportSrcColumn = + CSLTestBoolean(CPLGetXMLValue( psNode, "reportSrcColumn", "YES" )); + +/* -------------------------------------------------------------------- */ +/* Guess geometry type if not explicitly provided (or computed) */ +/* -------------------------------------------------------------------- */ + if( pszGType == NULL && poProps->eGeomType == wkbUnknown ) + { + if( GetSrcLayerDefn()->GetGeomFieldCount() == 1 ) + poProps->eGeomType = poSrcLayer->GetGeomType(); + else if( poProps->eGeometryStyle == VGS_Direct && + poProps->iGeomField >= 0 ) + { + poProps->eGeomType = + GetSrcLayerDefn()->GetGeomFieldDefn(poProps->iGeomField)->GetType(); + } + } + +/* -------------------------------------------------------------------- */ +/* Copy spatial reference system from source if not provided */ +/* -------------------------------------------------------------------- */ + const char* pszSRS = CPLGetXMLValue( psNode, "SRS", NULL ); + if( pszSRS == NULL && poProps == apoGeomFieldProps[0] ) + pszSRS = CPLGetXMLValue( psNodeParent, "LayerSRS", NULL ); + if( pszSRS == NULL ) + { + OGRSpatialReference* poSRS = NULL; + if( GetSrcLayerDefn()->GetGeomFieldCount() == 1 ) + { + poSRS = poSrcLayer->GetSpatialRef(); + } + else if( poProps->eGeometryStyle == VGS_Direct && + poProps->iGeomField >= 0 ) + { + poSRS = GetSrcLayerDefn()-> + GetGeomFieldDefn(poProps->iGeomField)->GetSpatialRef(); + } + if( poSRS != NULL ) + poProps->poSRS = poSRS->Clone(); + } + else if( poProps->poSRS == NULL ) + { + if( !(EQUAL(pszSRS,"NULL")) ) + { + OGRSpatialReference oSRS; + + if( oSRS.SetFromUserInput( pszSRS ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to import SRS `%s'.", pszSRS ); + return FALSE; + } + poProps->poSRS = oSRS.Clone(); + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have a SrcRegion? */ +/* -------------------------------------------------------------------- */ + const char *pszSrcRegion = CPLGetXMLValue( psNode, "SrcRegion", NULL ); + if( pszSrcRegion == NULL && poProps == apoGeomFieldProps[0] ) + pszSrcRegion = CPLGetXMLValue( psNodeParent, "SrcRegion", NULL ); + if( pszSrcRegion != NULL ) + { + OGRGeometryFactory::createFromWkt( (char**) &pszSrcRegion, NULL, &poProps->poSrcRegion ); + if( poProps->poSrcRegion == NULL || wkbFlatten(poProps->poSrcRegion->getGeometryType()) != wkbPolygon) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Ignoring SrcRegion. It must be a valid WKT polygon"); + delete poProps->poSrcRegion; + poProps->poSrcRegion = NULL; + } + + poProps->bSrcClip = CSLTestBoolean(CPLGetXMLValue( psNode, "SrcRegion.clip", "FALSE" )); + } + +/* -------------------------------------------------------------------- */ +/* Set Extent if provided */ +/* -------------------------------------------------------------------- */ + const char* pszExtentXMin = CPLGetXMLValue( psNode, "ExtentXMin", NULL ); + const char* pszExtentYMin = CPLGetXMLValue( psNode, "ExtentYMin", NULL ); + const char* pszExtentXMax = CPLGetXMLValue( psNode, "ExtentXMax", NULL ); + const char* pszExtentYMax = CPLGetXMLValue( psNode, "ExtentYMax", NULL ); + if( pszExtentXMin != NULL && pszExtentYMin != NULL && + pszExtentXMax != NULL && pszExtentYMax != NULL ) + { + poProps->sStaticEnvelope.MinX = CPLAtof(pszExtentXMin); + poProps->sStaticEnvelope.MinY = CPLAtof(pszExtentYMin); + poProps->sStaticEnvelope.MaxX = CPLAtof(pszExtentXMax); + poProps->sStaticEnvelope.MaxY = CPLAtof(pszExtentYMax); + } + + poProps->bNullable = CSLTestBoolean(CPLGetXMLValue( psNode, "nullable", "TRUE" )); + + return TRUE; +} + +/************************************************************************/ +/* FullInitialize() */ +/************************************************************************/ + +int OGRVRTLayer::FullInitialize() + +{ + const char *pszSharedSetting = NULL; + const char *pszSQL = NULL; + const char *pszSrcFIDFieldName = NULL; + const char *pszStyleFieldName = NULL; + CPLXMLNode *psChild = NULL; + int bFoundGeometryField = FALSE; + + if (bHasFullInitialized) + return TRUE; + + bHasFullInitialized = TRUE; + + poFeatureDefn = new OGRFeatureDefn( osName ); + poFeatureDefn->Reference(); + + if (poDS->GetRecursionDetected()) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Figure out the data source name. It may be treated relative */ +/* to vrt filename, but normally it is used directly. */ +/* -------------------------------------------------------------------- */ + char *pszSrcDSName = (char *) CPLGetXMLValue(psLTree,"SrcDataSource",NULL); + + if( pszSrcDSName == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Missing SrcDataSource for layer %s.", osName.c_str() ); + goto error; + } + + if( CSLTestBoolean(CPLGetXMLValue( psLTree, "SrcDataSource.relativetoVRT", + "0")) ) + { + static const char* apszPrefixes[] = { "CSV:", "GPSBABEL:" }; + int bDone = FALSE; + for( size_t i = 0; i < sizeof(apszPrefixes) / sizeof(apszPrefixes[0]); i ++) + { + const char* pszPrefix = apszPrefixes[i]; + if( EQUALN(pszSrcDSName, pszPrefix, strlen(pszPrefix)) ) + { + const char* pszLastPart = strrchr(pszSrcDSName, ':') + 1; + /* CSV:z:/foo.xyz */ + if( (pszLastPart[0] == '/' || pszLastPart[0] == '\\') && + pszLastPart - pszSrcDSName >= 3 && pszLastPart[-3] == ':' ) + pszLastPart -= 2; + CPLString osPrefix(pszSrcDSName); + osPrefix.resize(pszLastPart - pszSrcDSName); + pszSrcDSName = CPLStrdup( (osPrefix + + CPLProjectRelativeFilename( osVRTDirectory, pszLastPart )).c_str() ); + bDone = TRUE; + break; + } + } + if( !bDone ) + { + pszSrcDSName = CPLStrdup( + CPLProjectRelativeFilename( osVRTDirectory, pszSrcDSName ) ); + } + } + else + { + pszSrcDSName = CPLStrdup(pszSrcDSName); + } + +/* -------------------------------------------------------------------- */ +/* Are we accessing this datasource in shared mode? We default */ +/* to shared for SrcSQL requests, but we also allow the XML to */ +/* control our shared setting with an attribute on the */ +/* datasource element. */ +/* -------------------------------------------------------------------- */ + pszSharedSetting = CPLGetXMLValue( psLTree, + "SrcDataSource.shared", + NULL ); + if( pszSharedSetting == NULL ) + { + if( CPLGetXMLValue( psLTree, "SrcSQL", NULL ) == NULL ) + pszSharedSetting = "OFF"; + else + pszSharedSetting = "ON"; + } + + bSrcDSShared = CSLTestBoolean( pszSharedSetting ); + + // update mode doesn't make sense if we have a SrcSQL element + if (CPLGetXMLValue( psLTree, "SrcSQL", NULL ) != NULL) + bUpdate = FALSE; + +/* -------------------------------------------------------------------- */ +/* Try to access the datasource. */ +/* -------------------------------------------------------------------- */ +try_again: + CPLErrorReset(); + if( EQUAL(pszSrcDSName,"@dummy@") ) + { + GDALDriver *poMemDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("Memory"); + if (poMemDriver != NULL) + { + poSrcDS = poMemDriver->Create( "@dummy@", 0, 0, 0, GDT_Unknown, NULL ); + poSrcDS->CreateLayer( "@dummy@" ); + } + } + else if( bSrcDSShared ) + { + if (poDS->IsInForbiddenNames(pszSrcDSName)) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cyclic VRT opening detected !"); + poDS->SetRecursionDetected(); + } + else + { + char** papszOpenOptions = GDALDeserializeOpenOptionsFromXML(psLTree); + int nFlags = GDAL_OF_VECTOR | GDAL_OF_SHARED; + if( bUpdate ) nFlags |= GDAL_OF_UPDATE; + poSrcDS = (GDALDataset*) GDALOpenEx( pszSrcDSName, nFlags, NULL, + (const char* const* )papszOpenOptions, NULL ); + CSLDestroy(papszOpenOptions); + /* Is it a VRT datasource ? */ + if (poSrcDS != NULL && poSrcDS->GetDriver() == poDS->GetDriver()) + { + OGRVRTDataSource* poVRTSrcDS = (OGRVRTDataSource*)poSrcDS; + poVRTSrcDS->AddForbiddenNames(poDS->GetName()); + } + } + } + else + { + if (poDS->GetCallLevel() < 32) + { + char** papszOpenOptions = GDALDeserializeOpenOptionsFromXML(psLTree); + int nFlags = GDAL_OF_VECTOR; + if( bUpdate ) nFlags |= GDAL_OF_UPDATE; + poSrcDS = (GDALDataset*) GDALOpenEx( pszSrcDSName, nFlags, NULL, + (const char* const* )papszOpenOptions, NULL ); + CSLDestroy(papszOpenOptions); + /* Is it a VRT datasource ? */ + if (poSrcDS != NULL && poSrcDS->GetDriver() == poDS->GetDriver()) + { + OGRVRTDataSource* poVRTSrcDS = (OGRVRTDataSource*)poSrcDS; + poVRTSrcDS->SetCallLevel(poDS->GetCallLevel() + 1); + poVRTSrcDS->SetParentDS(poDS); + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Trying to open a VRT from a VRT from a VRT from ... [32 times] a VRT !"); + + poDS->SetRecursionDetected(); + + OGRVRTDataSource* poParent = poDS->GetParentDS(); + while(poParent != NULL) + { + poParent->SetRecursionDetected(); + poParent = poParent->GetParentDS(); + } + } + } + + if( poSrcDS == NULL ) + { + if (bUpdate) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Cannot open datasource `%s' in update mode. Trying again in read-only mode", + pszSrcDSName ); + bUpdate = FALSE; + goto try_again; + } + if( strlen(CPLGetLastErrorMsg()) == 0 ) + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to open datasource `%s'.", + pszSrcDSName ); + goto error; + } + +/* -------------------------------------------------------------------- */ +/* Apply any metadata. */ +/* -------------------------------------------------------------------- */ + oMDMD.XMLInit( psLTree, TRUE ); + +/* -------------------------------------------------------------------- */ +/* Is this layer derived from an SQL query result? */ +/* -------------------------------------------------------------------- */ + pszSQL = CPLGetXMLValue( psLTree, "SrcSQL", NULL ); + + if( pszSQL != NULL ) + { + const char* pszDialect = CPLGetXMLValue( psLTree, "SrcSQL.dialect", NULL ); + if( pszDialect != NULL && pszDialect[0] == '\0' ) + pszDialect = NULL; + poSrcLayer = poSrcDS->ExecuteSQL( pszSQL, NULL, pszDialect ); + if( poSrcLayer == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SQL statement failed, or returned no layer result:\n%s", + pszSQL ); + goto error; + } + bSrcLayerFromSQL = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Fetch the layer if it is a regular layer. */ +/* -------------------------------------------------------------------- */ + if( poSrcLayer == NULL ) + { + const char *pszSrcLayerName = CPLGetXMLValue( psLTree, "SrcLayer", + osName ); + + poSrcLayer = poSrcDS->GetLayerByName( pszSrcLayerName ); + if( poSrcLayer == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to find layer '%s' on datasource '%s'.", + pszSrcLayerName, pszSrcDSName ); + goto error; + } + } + + CPLFree( pszSrcDSName ); + pszSrcDSName = NULL; + +/* -------------------------------------------------------------------- */ +/* Search for GeometryField definitions */ +/* -------------------------------------------------------------------- */ + if( apoGeomFieldProps.size() != 0 ) + { + /* First pass: create as many OGRVRTGeomFieldProps as there are */ + /* GeometryField elements */ + for( psChild = psLTree->psChild; psChild != NULL; psChild=psChild->psNext ) + { + if( psChild->eType == CXT_Element && + EQUAL(psChild->pszValue,"GeometryField") ) + { + if( !bFoundGeometryField ) + { + bFoundGeometryField = TRUE; + } + else + apoGeomFieldProps.push_back(new OGRVRTGeomFieldProps()); + } + } + + if( !bFoundGeometryField ) + { + /* If no GeometryField is found but several source geometry fields */ + /* exist, use them */ + if( GetSrcLayerDefn()->GetGeomFieldCount() > 1 ) + { + delete apoGeomFieldProps[0]; + apoGeomFieldProps.resize(0); + for( int iGeomField = 0; + iGeomField < GetSrcLayerDefn()->GetGeomFieldCount(); + iGeomField++ ) + { + OGRVRTGeomFieldProps* poProps; + poProps = new OGRVRTGeomFieldProps(); + apoGeomFieldProps.push_back(poProps); + OGRGeomFieldDefn* poFDefn = + GetSrcLayerDefn()->GetGeomFieldDefn(iGeomField); + poProps->osName = poFDefn->GetNameRef(); + poProps->eGeomType = poFDefn->GetType(); + if( poFDefn->GetSpatialRef() != NULL ) + poProps->poSRS = poFDefn->GetSpatialRef()->Clone(); + poProps->iGeomField = iGeomField; + poProps->bNullable = poFDefn->IsNullable(); + } + } + + /* Otherwise use the top-level elements such as SrcRegion */ + else + { + if( !ParseGeometryField(NULL, psLTree, apoGeomFieldProps[0] ) ) + goto error; + } + } + else + { + /* Second pass: fill the OGRVRTGeomFieldProps objects from the */ + /* GeometryField definitions */ + int iGeomField = 0; + for( psChild = psLTree->psChild; psChild != NULL; psChild=psChild->psNext ) + { + if( psChild->eType == CXT_Element && + EQUAL(psChild->pszValue,"GeometryField") ) + { + if( !ParseGeometryField(psChild, psLTree, + apoGeomFieldProps[iGeomField] ) ) + goto error; + iGeomField ++; + } + } + } + + /* Instanciate real geometry fields from VRT properties */ + poFeatureDefn->SetGeomType(wkbNone); + for( size_t i = 0; i < apoGeomFieldProps.size(); i ++ ) + { + OGRGeomFieldDefn oFieldDefn( apoGeomFieldProps[i]->osName, + apoGeomFieldProps[i]->eGeomType ); + oFieldDefn.SetSpatialRef( apoGeomFieldProps[i]->poSRS ); + oFieldDefn.SetNullable( apoGeomFieldProps[i]->bNullable ); + poFeatureDefn->AddGeomFieldDefn(&oFieldDefn); + } + + poFeatureDefn->SetGeomType( apoGeomFieldProps[0]->eGeomType ); + } + else + { + poFeatureDefn->SetGeomType(wkbNone); + } + +/* -------------------------------------------------------------------- */ +/* Figure out what should be used as an FID. */ +/* -------------------------------------------------------------------- */ + pszSrcFIDFieldName = CPLGetXMLValue( psLTree, "FID", NULL ); + + if( pszSrcFIDFieldName != NULL ) + { + iFIDField = + GetSrcLayerDefn()->GetFieldIndex( pszSrcFIDFieldName ); + if( iFIDField == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify FID field '%s'.", + pszSrcFIDFieldName ); + goto error; + } + + // User facing FID column name. If not defined we will report the + // source FID column name only if it is exposed as a field too (#4637) + osFIDFieldName = CPLGetXMLValue( psLTree, "FID.name", "" ); + } + +/* -------------------------------------------------------------------- */ +/* Figure out what should be used as a Style */ +/* -------------------------------------------------------------------- */ + pszStyleFieldName = CPLGetXMLValue( psLTree, "Style", NULL ); + + if( pszStyleFieldName != NULL ) + { + iStyleField = + GetSrcLayerDefn()->GetFieldIndex( pszStyleFieldName ); + if( iStyleField == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify Style field '%s'.", + pszStyleFieldName ); + goto error; + } + } + +/* ==================================================================== */ +/* Search for schema definitions in the VRT. */ +/* ==================================================================== */ + bAttrFilterPassThrough = TRUE; + for( psChild = psLTree->psChild; psChild != NULL; psChild=psChild->psNext ) + { + if( psChild->eType == CXT_Element && EQUAL(psChild->pszValue,"Field") ) + { +/* -------------------------------------------------------------------- */ +/* Field name. */ +/* -------------------------------------------------------------------- */ + const char *pszName = CPLGetXMLValue( psChild, "name", NULL ); + if( pszName == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify Field name." ); + goto error; + } + + OGRFieldDefn oFieldDefn( pszName, OFTString ); + +/* -------------------------------------------------------------------- */ +/* Type */ +/* -------------------------------------------------------------------- */ + const char *pszArg = CPLGetXMLValue( psChild, "type", NULL ); + + if( pszArg != NULL ) + { + int iType; + + for( iType = 0; iType <= (int) OFTMaxType; iType++ ) + { + if( EQUAL(pszArg,OGRFieldDefn::GetFieldTypeName( + (OGRFieldType)iType)) ) + { + oFieldDefn.SetType( (OGRFieldType) iType ); + break; + } + } + + if( iType > (int) OFTMaxType ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify Field type '%s'.", + pszArg ); + goto error; + } + } + +/* -------------------------------------------------------------------- */ +/* Subtype */ +/* -------------------------------------------------------------------- */ + pszArg = CPLGetXMLValue( psChild, "subtype", NULL ); + if( pszArg != NULL ) + { + int iType; + OGRFieldSubType eSubType = OFSTNone; + + for( iType = 0; iType <= (int) OFSTMaxSubType; iType++ ) + { + if( EQUAL(pszArg,OGRFieldDefn::GetFieldSubTypeName( + (OGRFieldSubType)iType)) ) + { + eSubType = (OGRFieldSubType) iType; + break; + } + } + + if( iType > (int) OFSTMaxSubType ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify Field subtype '%s'.", + pszArg ); + goto error; + } + + if( !OGR_AreTypeSubTypeCompatible(oFieldDefn.GetType(), eSubType) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Invalid subtype '%s' for type '%s'.", + pszArg, OGRFieldDefn::GetFieldTypeName(oFieldDefn.GetType()) ); + goto error; + } + + oFieldDefn.SetSubType( eSubType ); + } + +/* -------------------------------------------------------------------- */ +/* Width and precision. */ +/* -------------------------------------------------------------------- */ + int nWidth = atoi(CPLGetXMLValue( psChild, "width", "0" )); + if (nWidth < 0) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Invalid width for field %s.", + pszName ); + goto error; + } + oFieldDefn.SetWidth(nWidth); + + int nPrecision = atoi(CPLGetXMLValue( psChild, "precision", "0" )); + if (nPrecision < 0 || nPrecision > 1024) + { + CPLError( CE_Failure, CPLE_IllegalArg, + "Invalid precision for field %s.", + pszName ); + goto error; + } + oFieldDefn.SetPrecision(nPrecision); + +/* -------------------------------------------------------------------- */ +/* Nullable attribute. */ +/* -------------------------------------------------------------------- */ + int bNullable = CSLTestBoolean(CPLGetXMLValue( psChild, "nullable", "true" )); + oFieldDefn.SetNullable(bNullable); + +/* -------------------------------------------------------------------- */ +/* Default attribute. */ +/* -------------------------------------------------------------------- */ + oFieldDefn.SetDefault(CPLGetXMLValue( psChild, "default",NULL)); + +/* -------------------------------------------------------------------- */ +/* Create the field. */ +/* -------------------------------------------------------------------- */ + poFeatureDefn->AddFieldDefn( &oFieldDefn ); + + abDirectCopy.push_back( FALSE ); + +/* -------------------------------------------------------------------- */ +/* Source field. */ +/* -------------------------------------------------------------------- */ + int iSrcField = + GetSrcLayerDefn()->GetFieldIndex( pszName ); + + pszArg = CPLGetXMLValue( psChild, "src", NULL ); + + if( pszArg != NULL ) + { + iSrcField = + GetSrcLayerDefn()->GetFieldIndex( pszArg ); + if( iSrcField == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find source field '%s'.", + pszArg ); + goto error; + } + } + + if (iSrcField < 0 || (pszArg != NULL && strcmp(pszArg, pszName) != 0)) + bAttrFilterPassThrough = FALSE; + else + { + OGRFieldDefn* poSrcFieldDefn = GetSrcLayerDefn()->GetFieldDefn(iSrcField); + if (poSrcFieldDefn->GetType() != oFieldDefn.GetType()) + bAttrFilterPassThrough = FALSE; + } + + anSrcField.push_back( iSrcField ); + } + } + + CPLAssert( poFeatureDefn->GetFieldCount() == (int) anSrcField.size() ); + +/* -------------------------------------------------------------------- */ +/* Create the schema, if it was not explicitly in the VRT. */ +/* -------------------------------------------------------------------- */ + if( poFeatureDefn->GetFieldCount() == 0 ) + { + int iSrcField; + int nSrcFieldCount = GetSrcLayerDefn()->GetFieldCount(); + + for( iSrcField = 0; iSrcField < nSrcFieldCount; iSrcField++ ) + { + int bSkip = FALSE; + for( size_t iGF = 0; iGF < apoGeomFieldProps.size(); iGF++ ) + { + if( apoGeomFieldProps[iGF]->bReportSrcColumn == FALSE && + (iSrcField == apoGeomFieldProps[iGF]->iGeomXField || + iSrcField == apoGeomFieldProps[iGF]->iGeomYField || + iSrcField == apoGeomFieldProps[iGF]->iGeomZField || + iSrcField == apoGeomFieldProps[iGF]->iGeomField) ) + { + bSkip = TRUE; + break; + } + } + if( bSkip ) + continue; + + poFeatureDefn->AddFieldDefn( GetSrcLayerDefn()->GetFieldDefn( iSrcField ) ); + anSrcField.push_back( iSrcField ); + abDirectCopy.push_back( TRUE ); + } + + bAttrFilterPassThrough = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Is VRT layer definition identical to the source layer defn ? */ +/* If so, use it directly, and save the translation of features. */ +/* -------------------------------------------------------------------- */ + if (poSrcFeatureDefn != NULL && iFIDField == -1 && iStyleField == -1 && + poSrcFeatureDefn->IsSame(poFeatureDefn)) + { + int bSame = TRUE; + for(size_t i = 0; i < apoGeomFieldProps.size(); i++ ) + { + if( apoGeomFieldProps[i]->eGeometryStyle != VGS_Direct || + apoGeomFieldProps[i]->iGeomField != (int)i ) + { + bSame = FALSE; + break; + } + } + if( bSame ) + { + CPLDebug("VRT", "Source feature definition is identical to VRT feature definition. Use optimized path"); + poFeatureDefn->Release(); + poFeatureDefn = poSrcFeatureDefn; + poFeatureDefn->Reference(); + for(int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + if( apoGeomFieldProps[i]->poSRS != NULL ) + apoGeomFieldProps[i]->poSRS->Release(); + apoGeomFieldProps[i]->poSRS = poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef(); + if( apoGeomFieldProps[i]->poSRS != NULL ) + apoGeomFieldProps[i]->poSRS->Reference(); + } + } + } + + CPLAssert( poFeatureDefn->GetGeomFieldCount() == (int)apoGeomFieldProps.size() ); + +/* -------------------------------------------------------------------- */ +/* Allow vrt to override whether attribute filters should be */ +/* passed through. */ +/* -------------------------------------------------------------------- */ + if( CPLGetXMLValue( psLTree, "attrFilterPassThrough", NULL ) != NULL ) + bAttrFilterPassThrough = + CSLTestBoolean( + CPLGetXMLValue(psLTree, "attrFilterPassThrough", + "TRUE") ); + + SetIgnoredFields(NULL); + + return TRUE; + +error: + bError = TRUE; + CPLFree( pszSrcDSName ); + poFeatureDefn->Release(); + poFeatureDefn = new OGRFeatureDefn( osName ); + poFeatureDefn->Reference(); + return FALSE; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRVRTLayer::ResetReading() + +{ + bNeedReset = TRUE; +} + +/************************************************************************/ +/* ResetSourceReading() */ +/************************************************************************/ + +int OGRVRTLayer::ResetSourceReading() + +{ + int bSuccess = TRUE; + +/* -------------------------------------------------------------------- */ +/* Do we want to let source layer do spatial restriction? */ +/* -------------------------------------------------------------------- */ + char *pszFilter = NULL; + for(size_t i=0; i < apoGeomFieldProps.size(); i++ ) + { + if( (m_poFilterGeom || apoGeomFieldProps[i]->poSrcRegion) && + apoGeomFieldProps[i]->bUseSpatialSubquery && + apoGeomFieldProps[i]->eGeometryStyle == VGS_PointFromColumns ) + { + const char *pszXField, *pszYField; + OGRFieldDefn* poXField = poSrcLayer->GetLayerDefn()->GetFieldDefn(apoGeomFieldProps[i]->iGeomXField); + OGRFieldDefn* poYField = poSrcLayer->GetLayerDefn()->GetFieldDefn(apoGeomFieldProps[i]->iGeomYField); + + pszXField = poXField->GetNameRef(); + pszYField = poYField->GetNameRef(); + if (apoGeomFieldProps[i]->bUseSpatialSubquery) + { + OGRFieldType xType = poXField->GetType(); + OGRFieldType yType = poYField->GetType(); + if (!((xType == OFTReal || xType == OFTInteger || xType == OFTInteger64) && + (yType == OFTReal || yType == OFTInteger || yType == OFTInteger64))) + { + CPLError(CE_Warning, CPLE_AppDefined, + "The '%s' and/or '%s' fields of the source layer are not declared as numeric fields,\n" + "so the spatial filter cannot be turned into an attribute filter on them", + pszXField, pszYField); + apoGeomFieldProps[i]->bUseSpatialSubquery = FALSE; + } + } + if (apoGeomFieldProps[i]->bUseSpatialSubquery) + { + OGREnvelope sEnvelope; + CPLString osFilter; + + if (apoGeomFieldProps[i]->poSrcRegion != NULL) + { + if (m_poFilterGeom == NULL) + apoGeomFieldProps[i]->poSrcRegion->getEnvelope( &sEnvelope ); + else + { + OGRGeometry* poIntersection = + apoGeomFieldProps[i]->poSrcRegion->Intersection(m_poFilterGeom); + if (poIntersection && !poIntersection->IsEmpty()) + { + poIntersection->getEnvelope( &sEnvelope ); + } + else + { + sEnvelope.MinX = 0; + sEnvelope.MaxX = 0; + sEnvelope.MinY = 0; + sEnvelope.MaxY = 0; + } + delete poIntersection; + } + } + else + m_poFilterGeom->getEnvelope( &sEnvelope ); + + if( !CPLIsInf(sEnvelope.MinX) ) + osFilter += CPLSPrintf("%s > %.15g", pszXField, sEnvelope.MinX); + else if( sEnvelope.MinX > 0 ) + osFilter += "0 = 1"; + + if( !CPLIsInf(sEnvelope.MaxX) ) + { + if( osFilter.size() ) osFilter += " AND "; + osFilter += CPLSPrintf("%s < %.15g", pszXField, sEnvelope.MaxX); + } + else if( sEnvelope.MaxX < 0 ) + { + if( osFilter.size() ) osFilter += " AND "; + osFilter += "0 = 1"; + } + + if( !CPLIsInf(sEnvelope.MinY) ) + { + if( osFilter.size() ) osFilter += " AND "; + osFilter += CPLSPrintf("%s > %.15g", pszYField, sEnvelope.MinY); + } + else if( sEnvelope.MinY > 0 ) + { + if( osFilter.size() ) osFilter += " AND "; + osFilter += "0 = 1"; + } + + if( !CPLIsInf(sEnvelope.MaxY) ) + { + if( osFilter.size() ) osFilter += " AND "; + osFilter += CPLSPrintf("%s < %.15g", pszYField, sEnvelope.MaxY); + } + else if( sEnvelope.MaxY < 0 ) + { + if( osFilter.size() ) osFilter += " AND "; + osFilter += "0 = 1"; + } + + if( osFilter.size() != 0 ) + { + pszFilter = CPLStrdup(osFilter); + } + } + + /* Just do it on one geometry field. To complicated otherwise ! */ + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Install spatial + attr filter query on source layer. */ +/* -------------------------------------------------------------------- */ + if( pszFilter == NULL && pszAttrFilter == NULL ) + bSuccess = (poSrcLayer->SetAttributeFilter( NULL ) == CE_None); + + else if( pszFilter != NULL && pszAttrFilter == NULL ) + bSuccess = (poSrcLayer->SetAttributeFilter( pszFilter ) == CE_None); + + else if( pszFilter == NULL && pszAttrFilter != NULL ) + bSuccess = (poSrcLayer->SetAttributeFilter( pszAttrFilter ) == CE_None); + + else + { + CPLString osMerged = pszFilter; + + osMerged += " AND ("; + osMerged += pszAttrFilter; + osMerged += ")"; + + bSuccess = (poSrcLayer->SetAttributeFilter(osMerged) == CE_None); + } + + CPLFree( pszFilter ); + +/* -------------------------------------------------------------------- */ +/* Clear spatial filter (to be safe) for non direct geometries */ +/* and reset reading. */ +/* -------------------------------------------------------------------- */ + if (m_iGeomFieldFilter < (int)apoGeomFieldProps.size() && + apoGeomFieldProps[m_iGeomFieldFilter]->eGeometryStyle == VGS_Direct && + apoGeomFieldProps[m_iGeomFieldFilter]->iGeomField >= 0 ) + { + OGRGeometry* poSpatialGeom = NULL; + OGRGeometry* poSrcRegion = apoGeomFieldProps[m_iGeomFieldFilter]->poSrcRegion; + int bToDelete = FALSE; + + if (poSrcRegion == NULL) + poSpatialGeom = m_poFilterGeom; + else if (m_poFilterGeom == NULL) + poSpatialGeom = poSrcRegion; + else + { + if( wkbFlatten(m_poFilterGeom->getGeometryType()) != wkbPolygon ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Spatial filter should be polygon when a SrcRegion is defined. Ignoring it"); + poSpatialGeom = poSrcRegion; + } + else + { + int bDoIntersection = TRUE; + if( m_bFilterIsEnvelope ) + { + OGREnvelope sEnvelope; + m_poFilterGeom->getEnvelope(&sEnvelope); + if( CPLIsInf(sEnvelope.MinX) && CPLIsInf(sEnvelope.MinY) && + CPLIsInf(sEnvelope.MaxX) && CPLIsInf(sEnvelope.MaxY) && + sEnvelope.MinX < 0 && sEnvelope.MinY < 0 && + sEnvelope.MaxY > 0 && sEnvelope.MaxY > 0 ) + { + poSpatialGeom = poSrcRegion; + bDoIntersection = FALSE; + } + } + if( bDoIntersection ) + { + poSpatialGeom = m_poFilterGeom->Intersection(poSrcRegion); + bToDelete = TRUE; + } + } + } + poSrcLayer->SetSpatialFilter( apoGeomFieldProps[m_iGeomFieldFilter]->iGeomField, + poSpatialGeom ); + if( bToDelete ) + delete poSpatialGeom; + } + else + poSrcLayer->SetSpatialFilter( NULL ); + poSrcLayer->ResetReading(); + bNeedReset = FALSE; + + return bSuccess; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRVRTLayer::GetNextFeature() + +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return NULL; + if( bError ) + return NULL; + + if( bNeedReset ) + { + if( !ResetSourceReading() ) + return NULL; + } + + for( ; TRUE; ) + { + OGRFeature *poSrcFeature, *poFeature; + + poSrcFeature = poSrcLayer->GetNextFeature(); + if( poSrcFeature == NULL ) + return NULL; + + if (poFeatureDefn == poSrcFeatureDefn) + { + poFeature = poSrcFeature; + ClipAndAssignSRS(poFeature); + } + else + { + poFeature = TranslateFeature( poSrcFeature, TRUE ); + delete poSrcFeature; + } + + if( poFeature == NULL ) + return NULL; + + if( ((m_iGeomFieldFilter < (int)apoGeomFieldProps.size() && + apoGeomFieldProps[m_iGeomFieldFilter]->eGeometryStyle == VGS_Direct) + || m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeomFieldRef(m_iGeomFieldFilter) )) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +/************************************************************************/ +/* ClipAndAssignSRS() */ +/************************************************************************/ + +void OGRVRTLayer::ClipAndAssignSRS(OGRFeature* poFeature) +{ + for(int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + /* Clip the geometry to the SrcRegion if asked */ + OGRGeometry* poGeom = poFeature->GetGeomFieldRef(i); + if (apoGeomFieldProps[i]->poSrcRegion != NULL && + apoGeomFieldProps[i]->bSrcClip && + poGeom != NULL) + { + poGeom = poGeom->Intersection(apoGeomFieldProps[i]->poSrcRegion); + poFeature->SetGeomFieldDirectly(i, poGeom); + } + + if (poGeom != NULL && apoGeomFieldProps[i]->poSRS != NULL) + poGeom->assignSpatialReference(apoGeomFieldProps[i]->poSRS); + } +} + +/************************************************************************/ +/* TranslateFeature() */ +/* */ +/* Translate a source feature into a feature for this layer. */ +/************************************************************************/ + +OGRFeature *OGRVRTLayer::TranslateFeature( OGRFeature*& poSrcFeat, int bUseSrcRegion ) + +{ +retry: + OGRFeature *poDstFeat = new OGRFeature( poFeatureDefn ); + + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Handle FID. */ +/* -------------------------------------------------------------------- */ + if( iFIDField == -1 ) + poDstFeat->SetFID( poSrcFeat->GetFID() ); + else + poDstFeat->SetFID( poSrcFeat->GetFieldAsInteger64( iFIDField ) ); + +/* -------------------------------------------------------------------- */ +/* Handle style string. */ +/* -------------------------------------------------------------------- */ + if( iStyleField != -1 ) + { + if( poSrcFeat->IsFieldSet(iStyleField) ) + poDstFeat->SetStyleString( + poSrcFeat->GetFieldAsString(iStyleField) ); + } + else + { + if( poSrcFeat->GetStyleString() != NULL ) + poDstFeat->SetStyleString(poSrcFeat->GetStyleString()); + } + + for(int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRVRTGeometryStyle eGeometryStyle = apoGeomFieldProps[i]->eGeometryStyle; + int iGeomField = apoGeomFieldProps[i]->iGeomField; + /* -------------------------------------------------------------------- */ + /* Handle the geometry. Eventually there will be several more */ + /* supported options. */ + /* -------------------------------------------------------------------- */ + if( eGeometryStyle == VGS_None || + GetLayerDefn()->GetGeomFieldDefn(i)->IsIgnored() ) + { + /* do nothing */ + } + else if( eGeometryStyle == VGS_WKT && iGeomField != -1 ) + { + char *pszWKT = (char *) poSrcFeat->GetFieldAsString( + iGeomField ); + + if( pszWKT != NULL ) + { + OGRGeometry *poGeom = NULL; + + OGRGeometryFactory::createFromWkt( &pszWKT, NULL, &poGeom ); + if( poGeom == NULL ) + CPLDebug( "OGR_VRT", "Did not get geometry from %s", + pszWKT ); + + poDstFeat->SetGeomFieldDirectly( i, poGeom ); + } + } + else if( eGeometryStyle == VGS_WKB && iGeomField != -1 ) + { + int nBytes; + GByte *pabyWKB; + int bNeedFree = FALSE; + + if( poSrcFeat->GetFieldDefnRef(iGeomField)->GetType() == OFTBinary ) + { + pabyWKB = poSrcFeat->GetFieldAsBinary( iGeomField, &nBytes ); + } + else + { + const char *pszWKT = poSrcFeat->GetFieldAsString( iGeomField ); + + pabyWKB = CPLHexToBinary( pszWKT, &nBytes ); + bNeedFree = TRUE; + } + + if( pabyWKB != NULL ) + { + OGRGeometry *poGeom = NULL; + + if( OGRGeometryFactory::createFromWkb( pabyWKB, NULL, &poGeom, + nBytes ) == OGRERR_NONE ) + poDstFeat->SetGeomFieldDirectly( i, poGeom ); + } + + if( bNeedFree ) + CPLFree( pabyWKB ); + } + else if( eGeometryStyle == VGS_Shape && iGeomField != -1 ) + { + int nBytes; + GByte *pabyWKB; + int bNeedFree = FALSE; + + if( poSrcFeat->GetFieldDefnRef(iGeomField)->GetType() == OFTBinary ) + { + pabyWKB = poSrcFeat->GetFieldAsBinary( iGeomField, &nBytes ); + } + else + { + const char *pszWKT = poSrcFeat->GetFieldAsString( iGeomField ); + + pabyWKB = CPLHexToBinary( pszWKT, &nBytes ); + bNeedFree = TRUE; + } + + if( pabyWKB != NULL ) + { + OGRGeometry *poGeom = NULL; + + if( OGRCreateFromShapeBin( pabyWKB, &poGeom, nBytes ) == OGRERR_NONE ) + poDstFeat->SetGeomFieldDirectly( i, poGeom ); + } + + if( bNeedFree ) + CPLFree( pabyWKB ); + } + else if( eGeometryStyle == VGS_Direct && iGeomField != -1 ) + { + poDstFeat->SetGeomField( i, poSrcFeat->GetGeomFieldRef(iGeomField) ); + } + else if( eGeometryStyle == VGS_PointFromColumns ) + { + if( apoGeomFieldProps[i]->iGeomZField != -1 ) + poDstFeat->SetGeomFieldDirectly( i, + new OGRPoint( poSrcFeat->GetFieldAsDouble( apoGeomFieldProps[i]->iGeomXField ), + poSrcFeat->GetFieldAsDouble( apoGeomFieldProps[i]->iGeomYField ), + poSrcFeat->GetFieldAsDouble( apoGeomFieldProps[i]->iGeomZField ) ) ); + else + poDstFeat->SetGeomFieldDirectly( i, + new OGRPoint( poSrcFeat->GetFieldAsDouble( apoGeomFieldProps[i]->iGeomXField ), + poSrcFeat->GetFieldAsDouble( apoGeomFieldProps[i]->iGeomYField ) ) ); + } + else + { + /* add other options here. */ + } + + /* In the non direct case, we need to check that the geometry intersects the source */ + /* region before an optionnal clipping */ + if( bUseSrcRegion && + apoGeomFieldProps[i]->eGeometryStyle != VGS_Direct && + apoGeomFieldProps[i]->poSrcRegion != NULL ) + { + OGRGeometry* poGeom = poDstFeat->GetGeomFieldRef(i); + if (poGeom != NULL && !poGeom->Intersects(apoGeomFieldProps[i]->poSrcRegion)) + { + delete poSrcFeat; + delete poDstFeat; + + /* Fetch next source feature and retry translating it */ + poSrcFeat = poSrcLayer->GetNextFeature(); + if (poSrcFeat == NULL) + return NULL; + + goto retry; + } + } + } + + ClipAndAssignSRS(poDstFeat); + +/* -------------------------------------------------------------------- */ +/* Copy fields. */ +/* -------------------------------------------------------------------- */ + int iVRTField; + + for( iVRTField = 0; iVRTField < poFeatureDefn->GetFieldCount(); iVRTField++ ) + { + if( anSrcField[iVRTField] == -1 ) + continue; + + OGRFieldDefn *poDstDefn = poFeatureDefn->GetFieldDefn( iVRTField ); + OGRFieldDefn *poSrcDefn = poSrcLayer->GetLayerDefn()->GetFieldDefn( anSrcField[iVRTField] ); + + if( !poSrcFeat->IsFieldSet( anSrcField[iVRTField] ) || poDstDefn->IsIgnored() ) + continue; + + if( abDirectCopy[iVRTField] + && poDstDefn->GetType() == poSrcDefn->GetType() ) + { + poDstFeat->SetField( iVRTField, + poSrcFeat->GetRawFieldRef( anSrcField[iVRTField] ) ); + } + else + { + /* Eventually we need to offer some more sophisticated translation + options here for more esoteric types. */ + if (poDstDefn->GetType() == OFTReal) + poDstFeat->SetField( iVRTField, + poSrcFeat->GetFieldAsDouble(anSrcField[iVRTField])); + else + poDstFeat->SetField( iVRTField, + poSrcFeat->GetFieldAsString(anSrcField[iVRTField])); + } + } + + return poDstFeat; +} + + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRVRTLayer::GetFeature( GIntBig nFeatureId ) + +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return NULL; + + bNeedReset = TRUE; + +/* -------------------------------------------------------------------- */ +/* If the FID is directly mapped, we can do a simple */ +/* GetFeature() to get our target feature. Otherwise we need */ +/* to setup an appropriate query to get it. */ +/* -------------------------------------------------------------------- */ + OGRFeature *poSrcFeature, *poFeature; + + if( iFIDField == -1 ) + { + poSrcFeature = poSrcLayer->GetFeature( nFeatureId ); + } + else + { + const char* pszFID = poSrcLayer->GetLayerDefn()->GetFieldDefn(iFIDField)->GetNameRef(); + char* pszFIDQuery = (char*)CPLMalloc(strlen(pszFID) + 64); + + poSrcLayer->ResetReading(); + sprintf( pszFIDQuery, "%s = " CPL_FRMT_GIB, pszFID, nFeatureId ); + poSrcLayer->SetSpatialFilter( NULL ); + poSrcLayer->SetAttributeFilter( pszFIDQuery ); + CPLFree(pszFIDQuery); + + poSrcFeature = poSrcLayer->GetNextFeature(); + } + + if( poSrcFeature == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Translate feature and return it. */ +/* -------------------------------------------------------------------- */ + if (poFeatureDefn == poSrcFeatureDefn) + { + poFeature = poSrcFeature; + ClipAndAssignSRS(poFeature); + } + else + { + poFeature = TranslateFeature( poSrcFeature, FALSE ); + delete poSrcFeature; + } + + return poFeature; +} + +/************************************************************************/ +/* SetNextByIndex() */ +/************************************************************************/ + +OGRErr OGRVRTLayer::SetNextByIndex( GIntBig nIndex ) +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return OGRERR_FAILURE; + + if( bNeedReset ) + { + if( !ResetSourceReading() ) + return OGRERR_FAILURE; + } + + if (TestCapability(OLCFastSetNextByIndex)) + return poSrcLayer->SetNextByIndex(nIndex); + + return OGRLayer::SetNextByIndex(nIndex); +} + +/************************************************************************/ +/* TranslateVRTFeatureToSrcFeature() */ +/* */ +/* Translate a VRT feature into a feature for the source layer */ +/************************************************************************/ + +OGRFeature* OGRVRTLayer::TranslateVRTFeatureToSrcFeature( OGRFeature* poVRTFeature) +{ + OGRFeature *poSrcFeat = new OGRFeature( poSrcLayer->GetLayerDefn() ); + + poSrcFeat->SetFID( poVRTFeature->GetFID() ); + +/* -------------------------------------------------------------------- */ +/* Handle style string. */ +/* -------------------------------------------------------------------- */ + if( iStyleField != -1 ) + { + if( poVRTFeature->GetStyleString() != NULL ) + poSrcFeat->SetField( iStyleField, poVRTFeature->GetStyleString() ); + } + else + { + if( poVRTFeature->GetStyleString() != NULL ) + poSrcFeat->SetStyleString(poVRTFeature->GetStyleString()); + } + +/* -------------------------------------------------------------------- */ +/* Handle the geometry. Eventually there will be several more */ +/* supported options. */ +/* -------------------------------------------------------------------- */ + for(int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + OGRVRTGeometryStyle eGeometryStyle = apoGeomFieldProps[i]->eGeometryStyle; + int iGeomField = apoGeomFieldProps[i]->iGeomField; + + if( eGeometryStyle == VGS_None ) + { + /* do nothing */ + } + else if( eGeometryStyle == VGS_WKT && iGeomField >= 0 ) + { + OGRGeometry* poGeom = poVRTFeature->GetGeomFieldRef(i); + if (poGeom != NULL) + { + char* pszWKT = NULL; + if (poGeom->exportToWkt(&pszWKT) == OGRERR_NONE) + { + poSrcFeat->SetField(iGeomField, pszWKT); + } + CPLFree(pszWKT); + } + } + else if( eGeometryStyle == VGS_WKB && iGeomField >= 0) + { + OGRGeometry* poGeom = poVRTFeature->GetGeomFieldRef(i); + if (poGeom != NULL) + { + int nSize = poGeom->WkbSize(); + GByte* pabyData = (GByte*)CPLMalloc(nSize); + if (poGeom->exportToWkb(wkbNDR, pabyData) == OGRERR_NONE) + { + if ( poSrcFeat->GetFieldDefnRef(iGeomField)->GetType() == OFTBinary ) + { + poSrcFeat->SetField(iGeomField, nSize, pabyData); + } + else + { + char* pszHexWKB = CPLBinaryToHex(nSize, pabyData); + poSrcFeat->SetField(iGeomField, pszHexWKB); + CPLFree(pszHexWKB); + } + } + CPLFree(pabyData); + } + } + else if( eGeometryStyle == VGS_Shape ) + { + CPLDebug("OGR_VRT", "Update of VGS_Shape geometries not supported"); + } + else if( eGeometryStyle == VGS_Direct && iGeomField >= 0 ) + { + poSrcFeat->SetGeomField( iGeomField, poVRTFeature->GetGeomFieldRef(i) ); + } + else if( eGeometryStyle == VGS_PointFromColumns ) + { + OGRGeometry* poGeom = poVRTFeature->GetGeomFieldRef(i); + if (poGeom != NULL) + { + if (wkbFlatten(poGeom->getGeometryType()) != wkbPoint) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Cannot set a non ponctual geometry for PointFromColumns geometry"); + } + else + { + poSrcFeat->SetField( apoGeomFieldProps[i]->iGeomXField, + ((OGRPoint*)poGeom)->getX() ); + poSrcFeat->SetField( apoGeomFieldProps[i]->iGeomYField, + ((OGRPoint*)poGeom)->getY() ); + if( apoGeomFieldProps[i]->iGeomZField != -1 ) + { + poSrcFeat->SetField( apoGeomFieldProps[i]->iGeomZField, + ((OGRPoint*)poGeom)->getZ() ); + } + } + } + } + else + { + /* add other options here. */ + } + + OGRGeometry* poGeom = poSrcFeat->GetGeomFieldRef(i); + if ( poGeom != NULL && apoGeomFieldProps[i]->poSRS != NULL ) + poGeom->assignSpatialReference(apoGeomFieldProps[i]->poSRS); + } + +/* -------------------------------------------------------------------- */ +/* Copy fields. */ +/* -------------------------------------------------------------------- */ + + int iVRTField; + + for( iVRTField = 0; iVRTField < poFeatureDefn->GetFieldCount(); iVRTField++ ) + { + int bSkip = FALSE; + for(int i = 0; i < poFeatureDefn->GetGeomFieldCount(); i++ ) + { + /* Do not set source geometry columns. Have been set just above */ + if (anSrcField[iVRTField] == apoGeomFieldProps[i]->iGeomField || + anSrcField[iVRTField] == apoGeomFieldProps[i]->iGeomXField || + anSrcField[iVRTField] == apoGeomFieldProps[i]->iGeomYField || + anSrcField[iVRTField] == apoGeomFieldProps[i]->iGeomZField) + { + bSkip = TRUE; + break; + } + } + if( bSkip ) + continue; + + OGRFieldDefn *poVRTDefn = poFeatureDefn->GetFieldDefn( iVRTField ); + OGRFieldDefn *poSrcDefn = poSrcLayer->GetLayerDefn()->GetFieldDefn( anSrcField[iVRTField] ); + + if( abDirectCopy[iVRTField] + && poVRTDefn->GetType() == poSrcDefn->GetType() ) + { + poSrcFeat->SetField( anSrcField[iVRTField], + poVRTFeature->GetRawFieldRef( iVRTField ) ); + } + else + { + /* Eventually we need to offer some more sophisticated translation + options here for more esoteric types. */ + poSrcFeat->SetField( anSrcField[iVRTField], + poVRTFeature->GetFieldAsString(iVRTField)); + } + } + + return poSrcFeat; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRVRTLayer::ICreateFeature( OGRFeature* poVRTFeature ) +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return OGRERR_FAILURE; + + if(!bUpdate) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "CreateFeature"); + return OGRERR_FAILURE; + } + + if( iFIDField != -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The CreateFeature() operation is not supported if the FID option is specified." ); + return OGRERR_FAILURE; + } + + if( poSrcFeatureDefn == poFeatureDefn ) + return poSrcLayer->CreateFeature(poVRTFeature); + + OGRFeature* poSrcFeature = TranslateVRTFeatureToSrcFeature(poVRTFeature); + poSrcFeature->SetFID(OGRNullFID); + OGRErr eErr = poSrcLayer->CreateFeature(poSrcFeature); + if (eErr == OGRERR_NONE) + { + poVRTFeature->SetFID(poSrcFeature->GetFID()); + } + delete poSrcFeature; + return eErr; +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRVRTLayer::ISetFeature( OGRFeature* poVRTFeature ) +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return OGRERR_FAILURE; + + if(!bUpdate) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "SetFeature"); + return OGRERR_FAILURE; + } + + if( iFIDField != -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The SetFeature() operation is not supported if the FID option is specified." ); + return OGRERR_FAILURE; + } + + if( poSrcFeatureDefn == poFeatureDefn ) + return poSrcLayer->SetFeature(poVRTFeature); + + OGRFeature* poSrcFeature = TranslateVRTFeatureToSrcFeature(poVRTFeature); + OGRErr eErr = poSrcLayer->SetFeature(poSrcFeature); + delete poSrcFeature; + return eErr; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRVRTLayer::DeleteFeature( GIntBig nFID ) + +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return OGRERR_FAILURE; + + if(!bUpdate ) + { + CPLError( CE_Failure, CPLE_NotSupported, + UNSUPPORTED_OP_READ_ONLY, + "DeleteFeature"); + return OGRERR_FAILURE; + } + + if( iFIDField != -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The DeleteFeature() operation is not supported if the FID option is specified." ); + return OGRERR_FAILURE; + } + + return poSrcLayer->DeleteFeature(nFID); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRVRTLayer::SetAttributeFilter( const char *pszNewQuery ) + +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return OGRERR_FAILURE; + + if( bAttrFilterPassThrough ) + { + CPLFree( pszAttrFilter ); + if( pszNewQuery == NULL || strlen(pszNewQuery) == 0 ) + pszAttrFilter = NULL; + else + pszAttrFilter = CPLStrdup( pszNewQuery ); + + ResetReading(); + return OGRERR_NONE; + } + else + { + /* setup m_poAttrQuery */ + return OGRLayer::SetAttributeFilter( pszNewQuery ); + } +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRVRTLayer::TestCapability( const char * pszCap ) + +{ + if ( EQUAL(pszCap,OLCFastFeatureCount) && + nFeatureCount >= 0 && + m_poFilterGeom == NULL && m_poAttrQuery == NULL ) + return TRUE; + + if ( EQUAL(pszCap,OLCFastGetExtent) && + apoGeomFieldProps.size() == 1 && + apoGeomFieldProps[0]->sStaticEnvelope.IsInit() ) + return TRUE; + + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return FALSE; + + if ( EQUAL(pszCap,OLCFastFeatureCount) || + EQUAL(pszCap,OLCFastSetNextByIndex) ) + { + if( m_poAttrQuery == NULL ) + { + int bForward = TRUE; + for( size_t i=0; ieGeometryStyle == VGS_Direct || + (apoGeomFieldProps[i]->poSrcRegion == NULL && m_poFilterGeom == NULL)) ) + { + bForward = FALSE; + break; + } + } + if( bForward ) + { + return poSrcLayer->TestCapability(pszCap); + } + } + return FALSE; + } + + else if( EQUAL(pszCap,OLCFastSpatialFilter) ) + return apoGeomFieldProps.size() == 1 && + apoGeomFieldProps[0]->eGeometryStyle == VGS_Direct && + m_poAttrQuery == NULL && + poSrcLayer->TestCapability(pszCap); + + else if ( EQUAL(pszCap,OLCFastGetExtent) ) + return apoGeomFieldProps.size() == 1 && + apoGeomFieldProps[0]->eGeometryStyle == VGS_Direct && + m_poAttrQuery == NULL && + (apoGeomFieldProps[0]->poSrcRegion == NULL || + apoGeomFieldProps[0]->bSrcClip) && + poSrcLayer->TestCapability(pszCap); + + else if( EQUAL(pszCap,OLCRandomRead) ) + return iFIDField == -1 && poSrcLayer->TestCapability(pszCap); + + else if( EQUAL(pszCap,OLCSequentialWrite) + || EQUAL(pszCap,OLCRandomWrite) + || EQUAL(pszCap,OLCDeleteFeature) ) + return bUpdate && iFIDField == -1 && poSrcLayer->TestCapability(pszCap); + + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return poSrcLayer->TestCapability(pszCap); + + else if( EQUAL(pszCap,OLCTransactions) ) + return bUpdate && poSrcLayer->TestCapability(pszCap); + + else if( EQUAL(pszCap,OLCIgnoreFields) ) + return poSrcLayer->TestCapability(pszCap); + + else if( EQUAL(pszCap,OLCCurveGeometries) ) + return TRUE; + + return FALSE; +} + +/************************************************************************/ +/* GetSpatialRef() */ +/************************************************************************/ + +OGRSpatialReference *OGRVRTLayer::GetSpatialRef() + +{ + if ((CPLGetXMLValue( psLTree, "LayerSRS", NULL ) != NULL || + CPLGetXMLValue( psLTree, "GeometryField.SRS", NULL ) != NULL) && + apoGeomFieldProps.size() >= 1) + return apoGeomFieldProps[0]->poSRS; + + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return NULL; + + if( apoGeomFieldProps.size() >= 1 ) + return apoGeomFieldProps[0]->poSRS; + else + return NULL; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRVRTLayer::GetExtent( OGREnvelope *psExtent, int bForce ) +{ + return GetExtent( 0, psExtent, bForce ); +} + +OGRErr OGRVRTLayer::GetExtent( int iGeomField, OGREnvelope *psExtent, int bForce ) +{ + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ) + return OGRERR_FAILURE; + + if( apoGeomFieldProps[iGeomField]->sStaticEnvelope.IsInit() ) + { + memcpy(psExtent,&apoGeomFieldProps[iGeomField]->sStaticEnvelope, + sizeof(OGREnvelope)); + return OGRERR_NONE; + } + + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return OGRERR_FAILURE; + + if ( apoGeomFieldProps[iGeomField]->eGeometryStyle == VGS_Direct && + m_poAttrQuery == NULL && + (apoGeomFieldProps[iGeomField]->poSrcRegion == NULL || + apoGeomFieldProps[iGeomField]->bSrcClip) ) + { + if( bNeedReset ) + ResetSourceReading(); + + OGRErr eErr = poSrcLayer->GetExtent( + apoGeomFieldProps[iGeomField]->iGeomField, psExtent, bForce); + if( eErr != OGRERR_NONE || apoGeomFieldProps[iGeomField]->poSrcRegion == NULL ) + return eErr; + + OGREnvelope sSrcRegionEnvelope; + apoGeomFieldProps[iGeomField]->poSrcRegion->getEnvelope(&sSrcRegionEnvelope); + + psExtent->Intersect(sSrcRegionEnvelope); + return eErr; + } + + return OGRLayer::GetExtentInternal(iGeomField, psExtent, bForce); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRVRTLayer::GetFeatureCount( int bForce ) + +{ + if (nFeatureCount >= 0 && + m_poFilterGeom == NULL && m_poAttrQuery == NULL) + { + return nFeatureCount; + } + + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return 0; + + if (TestCapability(OLCFastFeatureCount)) + { + if( bNeedReset ) + ResetSourceReading(); + + return poSrcLayer->GetFeatureCount( bForce ); + } + + return OGRLayer::GetFeatureCount( bForce ); +} + + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRVRTLayer::SetSpatialFilter( OGRGeometry * poGeomIn ) +{ + SetSpatialFilter(0, poGeomIn); +} + +void OGRVRTLayer::SetSpatialFilter( int iGeomField, OGRGeometry * poGeomIn ) +{ + if( iGeomField < 0 || iGeomField >= GetLayerDefn()->GetGeomFieldCount() ) + { + if( poGeomIn != NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Invalid geometry field index : %d", iGeomField); + } + return; + } + + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return; + + if( apoGeomFieldProps[iGeomField]->eGeometryStyle == VGS_Direct) + bNeedReset = TRUE; + + m_iGeomFieldFilter = iGeomField; + if( InstallFilter( poGeomIn ) ) + ResetReading(); +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr OGRVRTLayer::SyncToDisk() +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return OGRERR_FAILURE; + + return poSrcLayer->SyncToDisk(); +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn *OGRVRTLayer::GetLayerDefn() +{ + if (!bHasFullInitialized) FullInitialize(); + + return poFeatureDefn; +} + +/************************************************************************/ +/* GetGeomType() */ +/************************************************************************/ + +OGRwkbGeometryType OGRVRTLayer::GetGeomType() +{ + if( CPLGetXMLValue( psLTree, "GeometryType", NULL ) != NULL || + CPLGetXMLValue( psLTree, "GeometryField.GeometryType", NULL ) != NULL ) + { + if( apoGeomFieldProps.size() >= 1) + return apoGeomFieldProps[0]->eGeomType; + return wkbNone; + } + + return GetLayerDefn()->GetGeomType(); +} + +/************************************************************************/ +/* GetFIDColumn() */ +/************************************************************************/ + +const char * OGRVRTLayer::GetFIDColumn() +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return ""; + + if( osFIDFieldName.size() ) + return osFIDFieldName; + + const char* pszFIDColumn; + if (iFIDField == -1) + { + /* If pass-through, then query the source layer FID column */ + pszFIDColumn = poSrcLayer->GetFIDColumn(); + if (pszFIDColumn == NULL || EQUAL(pszFIDColumn, "")) + return ""; + } + else + { + /* Otherwise get the name from the index in the source layer definition */ + OGRFieldDefn* poFDefn = GetSrcLayerDefn()->GetFieldDefn(iFIDField); + pszFIDColumn = poFDefn->GetNameRef(); + } + + /* Check that the FIDColumn is actually reported in the VRT layer definition */ + if (GetLayerDefn()->GetFieldIndex(pszFIDColumn) != -1) + return pszFIDColumn; + else + return ""; +} + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +OGRErr OGRVRTLayer::StartTransaction() +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || !bUpdate || poDS->GetRecursionDetected()) return OGRERR_FAILURE; + + return poSrcLayer->StartTransaction(); +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr OGRVRTLayer::CommitTransaction() +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || !bUpdate || poDS->GetRecursionDetected()) return OGRERR_FAILURE; + + return poSrcLayer->CommitTransaction(); +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +OGRErr OGRVRTLayer::RollbackTransaction() +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || !bUpdate || poDS->GetRecursionDetected()) return OGRERR_FAILURE; + + return poSrcLayer->RollbackTransaction(); +} + +/************************************************************************/ +/* SetIgnoredFields() */ +/************************************************************************/ + +OGRErr OGRVRTLayer::SetIgnoredFields( const char **papszFields ) +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return OGRERR_FAILURE; + + if( !poSrcLayer->TestCapability(OLCIgnoreFields) ) + return OGRERR_FAILURE; + + OGRErr eErr = OGRLayer::SetIgnoredFields(papszFields); + if( eErr != OGRERR_NONE ) + return eErr; + + const char** papszIter = papszFields; + char** papszFieldsSrc = NULL; + OGRFeatureDefn* poSrcFeatureDefn = poSrcLayer->GetLayerDefn(); + + /* Translate explicitly ignored fields of VRT layers to their equivalent */ + /* source fields. */ + while ( papszIter != NULL && *papszIter != NULL ) + { + const char* pszFieldName = *papszIter; + if ( EQUAL(pszFieldName, "OGR_GEOMETRY") || + EQUAL(pszFieldName, "OGR_STYLE") ) + { + papszFieldsSrc = CSLAddString(papszFieldsSrc, pszFieldName); + } + else + { + int iVRTField = GetLayerDefn()->GetFieldIndex(pszFieldName); + if( iVRTField >= 0 ) + { + int iSrcField = anSrcField[iVRTField]; + if (iSrcField >= 0) + { + /* If we are asked to ignore x or y for a VGS_PointFromColumns */ + /* geometry field, we must NOT pass that order to the underlying */ + /* layer */ + int bOKToIgnore = TRUE; + for(int iGeomVRTField = 0; + iGeomVRTField < GetLayerDefn()->GetGeomFieldCount(); iGeomVRTField++) + { + if( (iSrcField == apoGeomFieldProps[iGeomVRTField]->iGeomXField || + iSrcField == apoGeomFieldProps[iGeomVRTField]->iGeomYField || + iSrcField == apoGeomFieldProps[iGeomVRTField]->iGeomZField) ) + { + bOKToIgnore = FALSE; + break; + } + } + if( bOKToIgnore ) + { + OGRFieldDefn *poSrcDefn = poSrcFeatureDefn->GetFieldDefn( iSrcField ); + papszFieldsSrc = CSLAddString(papszFieldsSrc, poSrcDefn->GetNameRef()); + } + } + } + else + { + iVRTField = GetLayerDefn()->GetGeomFieldIndex(pszFieldName); + if( iVRTField >= 0 && + apoGeomFieldProps[iVRTField]->eGeometryStyle == VGS_Direct ) + { + int iSrcField = apoGeomFieldProps[iVRTField]->iGeomField; + if( iSrcField >= 0 ) + { + OGRGeomFieldDefn *poSrcDefn = + poSrcFeatureDefn->GetGeomFieldDefn( iSrcField ); + papszFieldsSrc = + CSLAddString(papszFieldsSrc, poSrcDefn->GetNameRef()); + } + } + } + } + papszIter++; + } + + /* Add source fields that are not referenced by VRT layer */ + int* panSrcFieldsUsed = (int*) CPLCalloc(sizeof(int), poSrcFeatureDefn->GetFieldCount()); + for(int iVRTField = 0; iVRTField < GetLayerDefn()->GetFieldCount(); iVRTField++) + { + int iSrcField = anSrcField[iVRTField]; + if (iSrcField >= 0) + panSrcFieldsUsed[iSrcField] = TRUE; + } + for(int iVRTField = 0; + iVRTField < GetLayerDefn()->GetGeomFieldCount(); iVRTField++) + { + OGRVRTGeometryStyle eGeometryStyle = + apoGeomFieldProps[iVRTField]->eGeometryStyle; + /* For a VGS_PointFromColumns geometry field, we must not ignore */ + /* the fields that help building it */ + if( eGeometryStyle == VGS_PointFromColumns ) + { + int iSrcField = apoGeomFieldProps[iVRTField]->iGeomXField; + if (iSrcField >= 0) + panSrcFieldsUsed[iSrcField] = TRUE; + iSrcField = apoGeomFieldProps[iVRTField]->iGeomYField; + if (iSrcField >= 0) + panSrcFieldsUsed[iSrcField] = TRUE; + iSrcField = apoGeomFieldProps[iVRTField]->iGeomZField; + if (iSrcField >= 0) + panSrcFieldsUsed[iSrcField] = TRUE; + } + /* Similarly for other kinds of geometry fields */ + else if( eGeometryStyle == VGS_WKT || eGeometryStyle == VGS_WKB || + eGeometryStyle == VGS_Shape ) + { + int iSrcField = apoGeomFieldProps[iVRTField]->iGeomField; + if (iSrcField >= 0) + panSrcFieldsUsed[iSrcField] = TRUE; + } + } + if( iStyleField >= 0 ) + panSrcFieldsUsed[iStyleField] = TRUE; + if( iFIDField >= 0 ) + panSrcFieldsUsed[iFIDField] = TRUE; + for(int iSrcField = 0; iSrcField < poSrcFeatureDefn->GetFieldCount(); iSrcField ++) + { + if( !panSrcFieldsUsed[iSrcField] ) + { + OGRFieldDefn *poSrcDefn = poSrcFeatureDefn->GetFieldDefn( iSrcField ); + papszFieldsSrc = CSLAddString(papszFieldsSrc, poSrcDefn->GetNameRef()); + } + } + CPLFree(panSrcFieldsUsed); + + /* Add source geometry fields that are not referenced by VRT layer */ + panSrcFieldsUsed = (int*) CPLCalloc(sizeof(int), + poSrcFeatureDefn->GetGeomFieldCount()); + for(int iVRTField = 0; + iVRTField < GetLayerDefn()->GetGeomFieldCount(); iVRTField++) + { + if( apoGeomFieldProps[iVRTField]->eGeometryStyle == VGS_Direct ) + { + int iSrcField = apoGeomFieldProps[iVRTField]->iGeomField; + if (iSrcField >= 0) + panSrcFieldsUsed[iSrcField] = TRUE; + } + } + for(int iSrcField = 0; + iSrcField < poSrcFeatureDefn->GetGeomFieldCount(); iSrcField ++) + { + if( !panSrcFieldsUsed[iSrcField] ) + { + OGRGeomFieldDefn *poSrcDefn = + poSrcFeatureDefn->GetGeomFieldDefn( iSrcField ); + papszFieldsSrc = CSLAddString(papszFieldsSrc, poSrcDefn->GetNameRef()); + } + } + CPLFree(panSrcFieldsUsed); + + eErr = poSrcLayer->SetIgnoredFields((const char**)papszFieldsSrc); + + CSLDestroy(papszFieldsSrc); + + return eErr; +} + +/************************************************************************/ +/* GetSrcDataset() */ +/************************************************************************/ + +GDALDataset* OGRVRTLayer::GetSrcDataset() +{ + if (!bHasFullInitialized) FullInitialize(); + if (!poSrcLayer || poDS->GetRecursionDetected()) return NULL; + return poSrcDS; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/GNUmakefile new file mode 100644 index 000000000..140ca8da3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrwalkdriver.o ogrwalkdatasource.o ogrwalklayer.o ogrwalktablelayer.o ogrwalkselectlayer.o ogrwalktool.o + +CPPFLAGS := -I.. -I../.. -I../generic -I../pgeo $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogrwalk.h ../generic/ogrwarpedlayer.h ../generic/ogrunionlayer.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/drv_walk.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/drv_walk.html new file mode 100644 index 000000000..6dca63327 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/drv_walk.html @@ -0,0 +1,39 @@ + + + + + + Walk Spatial Data + + + +

      Walk - Walk Spatial Data

      + +

      (OGR >= 1.11)

      + +

      OGR optionally supports reading Walk spatial data via ODBC. Walk spatial data is a Microsoft Access database developed by Walkinfo Technologies mainly for land surveying, evaluation, planning, checking and data analysis in China.

      + +

      Walk .mdb are accessed by passing the file name of + the .mdb file to be accessed as the data source name. On Windows, + no ODBC DSN is required. On Linux, there are problems with DSN-less + connection due to incomplete or buggy implementation of this feature + in the MDB Tools package, + So, it is required to configure Data Source Name (DSN) if the MDB + Tools driver is used (check instructions below).

      + +

      OGR treats all feature tables as layers. Most geometry types + should be supported (arcs and circles are translated into line segments, while other curves are currently converted into straight lines). Coordinate system information should be properly + associated with layers. Currently no effort is made to preserve styles and annotations.

      +

      Currently the OGR Walk driver does not take + advantage of spatial indexes for fast spatial queries.

      + +

      By default, SQL statements are handled by OGR SQL engine. SQL commands can also be passed directly to the ODBC database engine when SQL dialect is not "OGRSQL". In that case, the queries will deal with tables (such as "XXXXFeatures", where XXXX is the name of a layer) instead of layers.

      + +

      How to use Walk driver with unixODBC and MDB Tools (on Unix and Linux)

      + +

      Refer to the similar section of the PGeo driver. The prefix to use + for this driver is Walk:

      + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/makefile.vc new file mode 100644 index 000000000..5efd83031 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrwalkdriver.obj ogrwalkdatasource.obj ogrwalklayer.obj ogrwalktablelayer.obj ogrwalkselectlayer.obj ogrwalktool.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +EXTRAFLAGS = -I.. -I..\.. -I..\generic -I..\pgeo + +default: $(OBJ) + +clean: + -del *.lib + -del *.obj *.pdb + -del *.exe diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogis_geometry_wkb_struct.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogis_geometry_wkb_struct.h new file mode 100644 index 000000000..2d59465c3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogis_geometry_wkb_struct.h @@ -0,0 +1,234 @@ +/****************************************************************************** + * $Id: ogis_geometry_wkb_struct.h + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Definition of GeometryWkb Structs + * Author: Xian Chen, chenxian at walkinfo.com.cn + * + ****************************************************************************** + * Copyright (c) 2013, ZJU Walkinfo Technology Corp., Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/**************************************************************************/ +/* Basic Type definitions */ +/* unsigned char : 1 BYTE */ +/* GUInt32 : 32 bit unsigned integer (4 bytes) */ +/* double : double precision number (8 bytes) */ +/* Building Blocks : Point, LinearRing */ +/**************************************************************************/ + +#ifndef _OGIS_GEOMETRY_WKB_STRUCT_H +#define _OGIS_GEOMETRY_WKB_STRUCT_H + +#define CPL_LSBPTRPOINT(p) \ +{ \ + CPL_LSBPTR32(&p.x); \ + CPL_LSBPTR32(&p.y); \ + CPL_LSBPTR32(&p.z); \ +} + +#ifdef CPL_MSB +#define CPL_LSBPTRPOINTS(p,n) \ +{ \ + GUInt32 count; \ + for(count=0; count + +/************************************************************************/ +/* OGRWalkDataSource() */ +/************************************************************************/ + +OGRWalkDataSource::OGRWalkDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; +} + +/************************************************************************/ +/* ~OGRWalkDataSource() */ +/************************************************************************/ + +OGRWalkDataSource::~OGRWalkDataSource() + +{ + int i; + + CPLFree( pszName ); + + for( i = 0; i < nLayers; i++ ) + { + CPLAssert( NULL != papoLayers[i] ); + + delete papoLayers[i]; + } + + CPLFree( papoLayers ); +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRWalkDataSource::Open( const char * pszNewName, int bUpdate ) +{ +/* -------------------------------------------------------------------- */ +/* If this is the name of an MDB file, then construct the */ +/* appropriate connection string. Otherwise clip of WALK: to */ +/* get the DSN. */ +/* -------------------------------------------------------------------- */ + char *pszDSN; + + if( EQUALN(pszNewName,"WALK:",5) ) + pszDSN = CPLStrdup( pszNewName + 5 ); + else + { + const char *pszDSNStringTemplate = "DRIVER=Microsoft Access Driver (*.mdb);DBQ=%s"; + pszDSN = (char *) CPLMalloc(strlen(pszNewName)+strlen(pszDSNStringTemplate)+100); + + sprintf( pszDSN, pszDSNStringTemplate, pszNewName ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize based on the DSN. */ +/* -------------------------------------------------------------------- */ + CPLDebug( "Walk", "EstablishSession(%s)", pszDSN ); + + if( !oSession.EstablishSession( pszDSN, NULL, NULL ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to initialize ODBC connection to DSN for %s,\n" + "%s", pszDSN, oSession.GetLastError() ); + CPLFree( pszDSN ); + return FALSE; + } + + CPLFree( pszDSN ); + + pszName = CPLStrdup( pszNewName ); + + bDSUpdate = bUpdate; + +/* -------------------------------------------------------------------- */ +/* Collect list of layers and their attributes. */ +/* -------------------------------------------------------------------- */ + std::vector apapszGeomColumns; + CPLODBCStatement oStmt( &oSession ); + + oStmt.Append( "SELECT LayerID, LayerName, minE, maxE, minN, maxN, Memo FROM WalkLayers" ); + + if( !oStmt.ExecuteSQL() ) + { + CPLDebug( "Walk", + "SELECT on WalkLayers fails, perhaps not a walk database?\n%s", + oSession.GetLastError() ); + return FALSE; + } + + while( oStmt.Fetch() ) + { + int i, iNew = apapszGeomColumns.size(); + char **papszRecord = NULL; + + for( i = 1; i < 7; i++ ) + papszRecord = CSLAddString( papszRecord, oStmt.GetColData(i) ); //Add LayerName, Extent and Memo + + apapszGeomColumns.resize(iNew+1); + apapszGeomColumns[iNew] = papszRecord; + } + +/* -------------------------------------------------------------------- */ +/* Create a layer for each spatial table. */ +/* -------------------------------------------------------------------- */ + unsigned int iTable; + + papoLayers = (OGRWalkLayer **) CPLCalloc(apapszGeomColumns.size(), + sizeof( void * )); + + for( iTable = 0; iTable < apapszGeomColumns.size(); iTable++ ) + { + char **papszRecord = apapszGeomColumns[iTable]; + + OGRWalkTableLayer *poLayer = new OGRWalkTableLayer( this ); + + if( poLayer->Initialize( papszRecord[0], // LayerName + "Geometry", // Geometry Column Name + CPLAtof(papszRecord[1]), // Extent MinE + CPLAtof(papszRecord[2]), // Extent MaxE + CPLAtof(papszRecord[3]), // Extent MinN + CPLAtof(papszRecord[4]), // Extent MaxN + papszRecord[5]) // Memo for SpatialRef + != CE_None ) + { + delete poLayer; + } + else + papoLayers[nLayers++] = poLayer; + + CSLDestroy( papszRecord ); + } + + return TRUE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRWalkDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRWalkDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + return OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect ); + +/* -------------------------------------------------------------------- */ +/* Execute normal SQL statement in Walk. */ +/* Table_name = Layer_name + Postfix */ +/* Postfix: "Features", "Annotations" or "Styles" */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement *poStmt = new CPLODBCStatement( &oSession ); + + CPLDebug( "Walk", "ExecuteSQL(%s) called.", pszSQLCommand ); + poStmt->Append( pszSQLCommand ); + if( !poStmt->ExecuteSQL() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", oSession.GetLastError() ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Are there result columns for this statement? */ +/* -------------------------------------------------------------------- */ + if( poStmt->GetColCount() == 0 ) + { + delete poStmt; + CPLErrorReset(); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a results layer. It will take ownership of the */ +/* statement. */ +/* -------------------------------------------------------------------- */ + OGRWalkSelectLayer *poLayer = NULL; + + poLayer = new OGRWalkSelectLayer( this, poStmt ); + + if( poSpatialFilter != NULL ) + poLayer->SetSpatialFilter( poSpatialFilter ); + + return poLayer; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRWalkDataSource::ReleaseResultSet( OGRLayer * poLayer ) + +{ + delete poLayer; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalkdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalkdriver.cpp new file mode 100644 index 000000000..847310e68 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalkdriver.cpp @@ -0,0 +1,144 @@ +/****************************************************************************** + * $Id: ogrwalkdriver.cpp + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRWalkDriver class. + * Author: Xian Chen, chenxian at walkinfo.com.cn + * + ****************************************************************************** + * Copyright (c) 2013, ZJU Walkinfo Technology Corp., Ltd. + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrwalk.h" + +/************************************************************************/ +/* ~OGRWalkDriver() */ +/************************************************************************/ + +OGRWalkDriver::~OGRWalkDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRWalkDriver::GetName() + +{ + return "Walk"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRWalkDriver::Open( const char * pszFilename, int bUpdate ) +{ + + if( EQUALN(pszFilename, "PGEO:", strlen("PGEO:")) ) + return NULL; + + if( EQUALN(pszFilename, "GEOMEDIA:", strlen("GEOMEDIA:")) ) + return NULL; + + if( !EQUALN(pszFilename,"WALK:", strlen("WALK:")) + && !EQUAL(CPLGetExtension(pszFilename), "MDB") ) + return NULL; + +#ifndef WIN32 + // Try to register MDB Tools driver + // + // ODBCINST.INI NOTE: + // This operation requires write access to odbcinst.ini file + // located in directory pointed by ODBCINISYS variable. + // Usually, it points to /etc, so non-root users can overwrite this + // setting ODBCINISYS with location they have write access to, e.g.: + // $ export ODBCINISYS=$HOME/etc + // $ touch $ODBCINISYS/odbcinst.ini + // + // See: http://www.unixodbc.org/internals.html + // + if ( !InstallMdbDriver() ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unable to install MDB driver for ODBC, MDB access may not supported.\n" ); + } + else + CPLDebug( "Walk", "MDB Tools driver installed successfully!"); + +#endif /* ndef WIN32 */ + + OGRWalkDataSource *poDS = new OGRWalkDataSource(); + + if( !poDS->Open( pszFilename, bUpdate ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRWalkDriver::CreateDataSource( const char * pszName, + CPL_UNUSED char **papszOptions ) +{ + //if( !EQUAL(CPLGetExtension(pszName), "MDB") ) + // return NULL; + + OGRWalkDataSource *poDS = new OGRWalkDataSource(); + + if( !poDS->Open( pszName, TRUE ) ) + { + delete poDS; + CPLError( CE_Failure, CPLE_AppDefined, + "Walk driver doesn't currently support database creation.\n" + "Please create database with the `createdb' command." ); + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRWalkDriver::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRWalk() */ +/************************************************************************/ + +void RegisterOGRWalk() + +{ + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver( new OGRWalkDriver ); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalklayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalklayer.cpp new file mode 100644 index 000000000..385d5378e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalklayer.cpp @@ -0,0 +1,389 @@ +/****************************************************************************** + * $Id: ogrwalklayer.cpp + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRWalkLayer class. + * Author: Xian Chen, chenxian at walkinfo.com.cn + * + ****************************************************************************** + * Copyright (c) 2013, ZJU Walkinfo Technology Corp., Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrwalk.h" + +/************************************************************************/ +/* OGRWalkLayer() */ +/************************************************************************/ + +OGRWalkLayer::OGRWalkLayer( ) + +{ + poDS = NULL; + + bGeomColumnWKB = FALSE; + pszGeomColumn = NULL; + pszFIDColumn = NULL; + panFieldOrdinals = NULL; + + poStmt = NULL; + + poFeatureDefn = NULL; + iNextShapeId = 0; + + poSRS = NULL; +} + +/************************************************************************/ +/* ~OGRWalkLayer() */ +/************************************************************************/ + +OGRWalkLayer::~OGRWalkLayer() + +{ + if( m_nFeaturesRead > 0 && poFeatureDefn != NULL ) + { + CPLDebug( "Walk", "%d features read on layer '%s'.", + (int) m_nFeaturesRead, + poFeatureDefn->GetName() ); + } + + if( poFeatureDefn != NULL ) + { + poFeatureDefn->Release(); + poFeatureDefn = NULL; + } + + if( poSRS ) + poSRS->Release(); +} + +/************************************************************************/ +/* BuildFeatureDefn() */ +/* */ +/* Build feature definition from a set of column definitions */ +/* set on a statement. Sift out geometry and FID fields. */ +/************************************************************************/ + +CPLErr OGRWalkLayer::BuildFeatureDefn( const char *pszLayerName, + CPLODBCStatement *poStmt ) + +{ + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + int nRawColumns = poStmt->GetColCount(); + + poFeatureDefn->Reference(); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + panFieldOrdinals = (int *) CPLMalloc( sizeof(int) * nRawColumns ); + + for( int iCol = 0; iCol < nRawColumns; iCol++ ) + { + OGRFieldDefn oField( poStmt->GetColName(iCol), OFTString ); + + oField.SetWidth( MAX(0,poStmt->GetColSize( iCol )) ); + + if( pszGeomColumn != NULL + && EQUAL(poStmt->GetColName(iCol),pszGeomColumn) ) //If Geometry Column, continue to next field + continue; + + switch( CPLODBCStatement::GetTypeMapping(poStmt->GetColType(iCol)) ) + { + case SQL_C_SSHORT: + case SQL_C_USHORT: + case SQL_C_SLONG: + case SQL_C_ULONG: + oField.SetType( OFTInteger ); + break; + + case SQL_C_SBIGINT: + case SQL_C_UBIGINT: + oField.SetType( OFTInteger64 ); + break; + + case SQL_C_BINARY: + oField.SetType( OFTBinary ); + break; + + case SQL_C_NUMERIC: + oField.SetType( OFTReal ); + oField.SetPrecision( poStmt->GetColPrecision(iCol) ); + break; + + case SQL_C_FLOAT: + case SQL_C_DOUBLE: + oField.SetType( OFTReal ); + oField.SetWidth( 0 ); + break; + + case SQL_C_DATE: + oField.SetType( OFTDate ); + break; + + case SQL_C_TIME: + oField.SetType( OFTTime ); + break; + + case SQL_C_TIMESTAMP: + oField.SetType( OFTDateTime ); + break; + + default: + /* leave it as OFTString */; + } + + poFeatureDefn->AddFieldDefn( &oField ); + panFieldOrdinals[poFeatureDefn->GetFieldCount() - 1] = iCol+1; + } + +/* -------------------------------------------------------------------- */ +/* If we don't already have an FID, check if there is a special */ +/* FID named column available. */ +/* -------------------------------------------------------------------- */ + if( pszFIDColumn == NULL ) + { + const char *pszOGR_FID = CPLGetConfigOption("WALK_OGR_FID","FeatureID"); + if( poFeatureDefn->GetFieldIndex( pszOGR_FID ) != -1 ) + pszFIDColumn = CPLStrdup(pszOGR_FID); + } + + if( pszFIDColumn != NULL ) + CPLDebug( "Walk", "Using column %s as FID for table %s.", + pszFIDColumn, poFeatureDefn->GetName() ); + else + CPLDebug( "Walk", "Table %s has no identified FID column.", + poFeatureDefn->GetName() ); + + return CE_None; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRWalkLayer::ResetReading() + +{ + iNextShapeId = 0; +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRWalkLayer::GetNextFeature() + +{ + while( TRUE ) + { + OGRFeature *poFeature; + + poFeature = GetNextRawFeature(); + if( poFeature == NULL ) + return NULL; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + return poFeature; + + delete poFeature; + } +} + +OGRFeature *OGRWalkLayer::GetNextRawFeature() + +{ + if( GetStatement() == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we are marked to restart then do so, and fetch a record. */ +/* -------------------------------------------------------------------- */ + if( !poStmt->Fetch() ) + { + delete poStmt; + poStmt = NULL; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a feature from the current result. */ +/* -------------------------------------------------------------------- */ + int iField; + OGRFeature *poFeature = new OGRFeature( poFeatureDefn ); + + if( pszFIDColumn != NULL && poStmt->GetColId(pszFIDColumn) > -1 ) + poFeature->SetFID( + atoi(poStmt->GetColData(poStmt->GetColId(pszFIDColumn))) ); + else + poFeature->SetFID( iNextShapeId ); + + iNextShapeId++; + m_nFeaturesRead++; + +/* -------------------------------------------------------------------- */ +/* Set the fields. */ +/* -------------------------------------------------------------------- */ + for( iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + int iSrcField = panFieldOrdinals[iField]-1; + const char *pszValue = poStmt->GetColData( iSrcField ); + + if( pszValue == NULL ) + /* no value */; + else if( poFeature->GetFieldDefnRef(iField)->GetType() == OFTBinary ) + poFeature->SetField( iField, + poStmt->GetColDataLength(iSrcField), + (GByte *) pszValue ); + else + poFeature->SetField( iField, pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Try to extract a geometry. */ +/* -------------------------------------------------------------------- */ + if( pszGeomColumn != NULL ) + { + int iField = poStmt->GetColId( pszGeomColumn ); + const char *pszGeomBin = poStmt->GetColData( iField ); + int nGeomLength = poStmt->GetColDataLength( iField ); + OGRGeometry *poGeom = NULL; + OGRErr eErr = OGRERR_NONE; + + if( pszGeomBin != NULL && bGeomColumnWKB ) + { + WKBGeometry *WalkGeom = (WKBGeometry *)CPLMalloc(sizeof(WKBGeometry)); + if( Binary2WkbGeom((unsigned char *)pszGeomBin, WalkGeom, nGeomLength) + != OGRERR_NONE ) + return NULL; + eErr = TranslateWalkGeom(&poGeom, WalkGeom); + + DeleteWKBGeometry(*WalkGeom); + CPLFree(WalkGeom); + } + + if ( eErr != OGRERR_NONE ) + { + const char *pszMessage; + + switch ( eErr ) + { + case OGRERR_NOT_ENOUGH_DATA: + pszMessage = "Not enough data to deserialize"; + break; + case OGRERR_UNSUPPORTED_GEOMETRY_TYPE: + pszMessage = "Unsupported geometry type"; + break; + case OGRERR_CORRUPT_DATA: + pszMessage = "Corrupt data"; + break; + default: + pszMessage = "Unrecognized error"; + } + CPLError(CE_Failure, CPLE_AppDefined, + "GetNextRawFeature(): %s", pszMessage); + } + + if( poGeom != NULL && eErr == OGRERR_NONE ) + { + poGeom->assignSpatialReference( poSRS ); + poFeature->SetGeometryDirectly( poGeom ); + } + } + + return poFeature; +} + +/************************************************************************/ +/* LookupSpatialRef() */ +/************************************************************************/ + +void OGRWalkLayer::LookupSpatialRef( const char * pszMemo ) + +{ + char *pszProj4 = NULL; + const char *pszStart = NULL; + char *pszEnd = NULL; + + if ( !pszMemo ) + return; + +/* -------------------------------------------------------------------- */ +/* Only proj4 is currently used */ +/* -------------------------------------------------------------------- */ + if ( (pszStart = strstr(pszMemo, "")) != NULL ) + { + pszProj4 = CPLStrdup( pszStart + 7 ); + if ( (pszEnd = (char *)strstr(pszProj4, "")) != NULL ) + pszEnd[0] = '\0'; + } + else if ( (pszStart = strstr(pszMemo, "proj4={")) != NULL ) + { + pszProj4 = CPLStrdup( pszStart + 7 ); + if ( (pszEnd = (char *)strstr(pszProj4, "};")) != NULL ) + pszEnd[0] = '\0'; + } + +/* -------------------------------------------------------------------- */ +/* No Spatial Reference specified */ +/* -------------------------------------------------------------------- */ + if ( !pszProj4 ) + return; + + if ( strlen(pszProj4) > 0 ) + { + poSRS = new OGRSpatialReference(); + + if( poSRS->importFromProj4( pszProj4 ) != OGRERR_NONE ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "importFromProj4() failed on SRS '%s'.", + pszProj4); + delete poSRS; + poSRS = NULL; + } + } + + CPLFree( pszProj4 ); +} + +/************************************************************************/ +/* GetFIDColumn */ +/************************************************************************/ + +const char *OGRWalkLayer::GetFIDColumn() + +{ + return pszFIDColumn ? pszFIDColumn : ""; +} + +/************************************************************************/ +/* GetGeometryColumn() */ +/************************************************************************/ + +const char *OGRWalkLayer::GetGeometryColumn() + +{ + return pszGeomColumn ? pszGeomColumn : ""; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalkselectlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalkselectlayer.cpp new file mode 100644 index 000000000..a7bc6e2e5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalkselectlayer.cpp @@ -0,0 +1,139 @@ +/****************************************************************************** + * $Id: ogrwalkselectlayer.cpp + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRWalkSelectLayer class, layer access to the results + * of a SELECT statement executed via ExecuteSQL(). + * Author: Xian Chen, chenxian at walkinfo.com.cn + * + ****************************************************************************** + * Copyright (c) 2013, ZJU Walkinfo Technology Corp., Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "ogrwalk.h" + +/************************************************************************/ +/* OGRWalkSelectLayer() */ +/************************************************************************/ + +OGRWalkSelectLayer::OGRWalkSelectLayer( OGRWalkDataSource *poDSIn, + CPLODBCStatement * poStmtIn ) + +{ + poDS = poDSIn; + + iNextShapeId = 0; + poFeatureDefn = NULL; + + poStmt = poStmtIn; + pszBaseStatement = CPLStrdup( poStmtIn->GetCommand() ); + + BuildFeatureDefn( "SELECT", poStmt ); +} + +/************************************************************************/ +/* ~OGRWalkSelectLayer() */ +/************************************************************************/ + +OGRWalkSelectLayer::~OGRWalkSelectLayer() + +{ + ClearStatement(); +} + +/************************************************************************/ +/* ClearStatement() */ +/************************************************************************/ + +void OGRWalkSelectLayer::ClearStatement() + +{ + if( poStmt != NULL ) + { + delete poStmt; + poStmt = NULL; + } +} + +/************************************************************************/ +/* GetStatement() */ +/************************************************************************/ + +CPLODBCStatement *OGRWalkSelectLayer::GetStatement() + +{ + if( poStmt == NULL ) + ResetStatement(); + + return poStmt; +} + +/************************************************************************/ +/* ResetStatement() */ +/************************************************************************/ + +OGRErr OGRWalkSelectLayer::ResetStatement() + +{ + ClearStatement(); + + iNextShapeId = 0; + + CPLDebug( "Walk", "Recreating statement." ); + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( pszBaseStatement ); + + if( poStmt->ExecuteSQL() ) + return OGRERR_NONE; + else + { + delete poStmt; + poStmt = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRWalkSelectLayer::ResetReading() + +{ + if( iNextShapeId != 0 ) + ClearStatement(); + + OGRWalkLayer::ResetReading(); +} + +/************************************************************************/ +/* GetExtent() */ +/* */ +/* Since SELECT layers currently cannot ever have geometry, we */ +/* can optimize the GetExtent() method! */ +/************************************************************************/ + +OGRErr OGRWalkSelectLayer::GetExtent(OGREnvelope *, int ) + +{ + return OGRERR_FAILURE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalktablelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalktablelayer.cpp new file mode 100644 index 000000000..cb95a05f1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalktablelayer.cpp @@ -0,0 +1,362 @@ +/****************************************************************************** + * $Id: ogrwalktablelayer.cpp + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRWalkTableLayer class, access to an existing table. + * Author: Xian Chen, chenxian at walkinfo.com.cn + * + ****************************************************************************** + * Copyright (c) 2013, ZJU Walkinfo Technology Corp., Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrwalk.h" +#include "cpl_conv.h" + +/************************************************************************/ +/* OGRWalkTableLayer() */ +/************************************************************************/ + +OGRWalkTableLayer::OGRWalkTableLayer( OGRWalkDataSource *poDSIn ) + +{ + poDS = poDSIn; + + pszQuery = NULL; + + iNextShapeId = 0; + poFeatureDefn = NULL; + + memset( &sExtent, 0, sizeof(sExtent) ); +} + +/************************************************************************/ +/* ~OGRWalkTableLayer() */ +/************************************************************************/ + +OGRWalkTableLayer::~OGRWalkTableLayer() + +{ + CPLFree( pszQuery ); + ClearStatement(); +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +CPLErr OGRWalkTableLayer::Initialize( const char *pszLayerName, + const char *pszGeomCol, + double minE, + double maxE, + double minN, + double maxN, + const char *pszMemo) + +{ + SetDescription( pszLayerName ); + + CPLODBCSession *poSession = poDS->GetSession(); + + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + + sExtent.MinX = minE; + sExtent.MaxX = maxE; + sExtent.MinY = minN; + sExtent.MaxY = maxN; + +/* -------------------------------------------------------------------- */ +/* Look up the Spatial Reference */ +/* -------------------------------------------------------------------- */ + LookupSpatialRef( pszMemo ); + +/* -------------------------------------------------------------------- */ +/* Generate the Feature Tablename from the Layer Name */ +/* which is in the form Features */ +/* -------------------------------------------------------------------- */ + char* pszFeatureTableName = (char *) CPLMalloc(strlen(pszLayerName)+10); + + sprintf(pszFeatureTableName, "%sFeatures", pszLayerName); + +/* -------------------------------------------------------------------- */ +/* Do we have a simple primary key? */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oGetKey( poSession ); + + if( oGetKey.GetPrimaryKeys( pszFeatureTableName, NULL, NULL ) + && oGetKey.Fetch() ) + { + pszFIDColumn = CPLStrdup(oGetKey.GetColData( 3 )); + + if( oGetKey.Fetch() ) // more than one field in key! + { + CPLFree( pszFIDColumn ); + pszFIDColumn = NULL; + + CPLDebug( "Walk", "Table %s has multiple primary key fields, " + "ignoring them all.", pszFeatureTableName ); + } + } + +/* -------------------------------------------------------------------- */ +/* Have we been provided a geometry column? */ +/* -------------------------------------------------------------------- */ + CPLFree( pszGeomColumn ); + if( pszGeomCol == NULL ) + pszGeomColumn = NULL; + else + pszGeomColumn = CPLStrdup( pszGeomCol ); + +/* -------------------------------------------------------------------- */ +/* Get the column definitions for this table. */ +/* -------------------------------------------------------------------- */ + CPLODBCStatement oGetCol( poSession ); + CPLErr eErr; + + if( !oGetCol.GetColumns( pszFeatureTableName, NULL, NULL ) ) + { + CPLFree( pszFeatureTableName ); + return CE_Failure; + } + + eErr = BuildFeatureDefn( pszLayerName, &oGetCol ); + if( eErr != CE_None ) + { + CPLFree( pszFeatureTableName ); + return eErr; + } + + if( poFeatureDefn->GetFieldCount() == 0 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "No column definitions found for table '%s', layer not usable.", + pszLayerName ); + CPLFree( pszFeatureTableName ); + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* If we got a geometry column, does it exist? Is it binary? */ +/* -------------------------------------------------------------------- */ + if( pszGeomColumn != NULL ) + { + int iColumn = oGetCol.GetColId( pszGeomColumn ); + if( iColumn < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Column %s requested for geometry, but it does not exist.", + pszGeomColumn ); + CPLFree( pszGeomColumn ); + pszGeomColumn = NULL; + } + else + { + if( CPLODBCStatement::GetTypeMapping( + oGetCol.GetColType( iColumn )) == SQL_C_BINARY ) + bGeomColumnWKB = TRUE; + } + } + + CPLFree( pszFeatureTableName ); + + return CE_None; +} + +/************************************************************************/ +/* ClearStatement() */ +/************************************************************************/ + +void OGRWalkTableLayer::ClearStatement() + +{ + if( poStmt != NULL ) + { + delete poStmt; + poStmt = NULL; + } +} + +/************************************************************************/ +/* GetStatement() */ +/************************************************************************/ + +CPLODBCStatement *OGRWalkTableLayer::GetStatement() + +{ + if( poStmt == NULL ) + ResetStatement(); + + return poStmt; +} + +/************************************************************************/ +/* ResetStatement() */ +/************************************************************************/ + +OGRErr OGRWalkTableLayer::ResetStatement() + +{ + ClearStatement(); + + iNextShapeId = 0; + + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( "SELECT * FROM " ); + poStmt->Append( poFeatureDefn->GetName() ); + poStmt->Append( "Features" ); + + /* Append attribute query if we have it */ + if( (pszQuery != NULL) && strcmp(pszQuery, "") ) + poStmt->Appendf( " WHERE %s", pszQuery ); + + CPLDebug( "Walk", "ExecuteSQL(%s)", poStmt->GetCommand() ); + if( poStmt->ExecuteSQL() ) + return OGRERR_NONE; + else + { + delete poStmt; + poStmt = NULL; + return OGRERR_FAILURE; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRWalkTableLayer::ResetReading() + +{ + ClearStatement(); + OGRWalkLayer::ResetReading(); +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature *OGRWalkTableLayer::GetFeature( GIntBig nFeatureId ) + +{ + if( pszFIDColumn == NULL ) + return OGRWalkLayer::GetFeature( nFeatureId ); + + ClearStatement(); + + iNextShapeId = nFeatureId; + + poStmt = new CPLODBCStatement( poDS->GetSession() ); + poStmt->Append( "SELECT * FROM " ); + poStmt->Append( poFeatureDefn->GetName() ); + poStmt->Append( "Features" ); + poStmt->Appendf( " WHERE %s = " CPL_FRMT_GIB, pszFIDColumn, nFeatureId ); + + if( !poStmt->ExecuteSQL() ) + { + delete poStmt; + poStmt = NULL; + return NULL; + } + + return GetNextRawFeature(); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRWalkTableLayer::SetAttributeFilter( const char *pszQuery ) + +{ + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : NULL; + + if( (pszQuery == NULL && this->pszQuery == NULL) + || (pszQuery != NULL && this->pszQuery != NULL + && EQUAL(pszQuery,this->pszQuery)) ) + return OGRERR_NONE; + + CPLFree( this->pszQuery ); + this->pszQuery = (pszQuery != NULL ) ? CPLStrdup( pszQuery ) : NULL; + + ClearStatement(); + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRWalkTableLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCRandomRead) ) + return TRUE; + + else + return OGRWalkLayer::TestCapability( pszCap ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/* */ +/* If a spatial filter is in effect, we turn control over to */ +/* the generic counter. Otherwise we return the total count. */ +/* Eventually we should consider implementing a more efficient */ +/* way of counting features matching a spatial query. */ +/************************************************************************/ + +GIntBig OGRWalkTableLayer::GetFeatureCount( int bForce ) + +{ + if( m_poFilterGeom != NULL ) + return OGRWalkLayer::GetFeatureCount( bForce ); + + CPLODBCStatement oStmt( poDS->GetSession() ); + oStmt.Append( "SELECT COUNT(*) FROM " ); + oStmt.Append( poFeatureDefn->GetName() ); + oStmt.Append( "Features" ); + + if( (pszQuery != NULL) && strcmp(pszQuery, "") ) + oStmt.Appendf( " WHERE %s", pszQuery ); + + if( !oStmt.ExecuteSQL() || !oStmt.Fetch() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "GetFeatureCount() failed on query %s.\n%s", + oStmt.GetCommand(), poDS->GetSession()->GetLastError() ); + return OGRWalkLayer::GetFeatureCount(bForce); + } + + return atoi(oStmt.GetColData(0)); +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRWalkTableLayer::GetExtent( OGREnvelope *psExtent, CPL_UNUSED int bForce ) +{ + *psExtent = sExtent; + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalktool.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalktool.cpp new file mode 100644 index 000000000..27586884b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/walk/ogrwalktool.cpp @@ -0,0 +1,753 @@ +/****************************************************************************** + * $Id: ogrwalktool.cpp + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements Walk Binary Data to Walk Geometry and OGC WKB + * Author: Xian Chen, chenxian at walkinfo.com.cn + * + ****************************************************************************** + * Copyright (c) 2013, ZJU Walkinfo Technology Corp., Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrwalk.h" + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +/************************************************************************/ +/* OGRWalkArcCenterFromEdgePoints() */ +/* */ +/* Compute the center of an arc/circle from three edge points. */ +/************************************************************************/ + +static int +OGRWalkArcCenterFromEdgePoints( double x_c0, double y_c0, + double x_c1, double y_c1, + double x_c2, double y_c2, + double *x_center, double *y_center ) + +{ +/* -------------------------------------------------------------------- */ +/* Compute the inverse of the slopes connecting the first and */ +/* second points. Also compute the center point of the two */ +/* points ... the point our crossing line will go through. */ +/* -------------------------------------------------------------------- */ + double m1, x1, y1; + + if( (y_c1 - y_c0) != 0.0 ) + m1 = (x_c0 - x_c1) / (y_c1 - y_c0); + else + m1 = 1e+10; + + x1 = (x_c0 + x_c1) * 0.5; + y1 = (y_c0 + y_c1) * 0.5; + +/* -------------------------------------------------------------------- */ +/* Compute the same for the second point compared to the third */ +/* point. */ +/* -------------------------------------------------------------------- */ + double m2, x2, y2; + + if( (y_c2 - y_c1) != 0.0 ) + m2 = (x_c1 - x_c2) / (y_c2 - y_c1); + else + m2 = 1e+10; + + x2 = (x_c1 + x_c2) * 0.5; + y2 = (y_c1 + y_c2) * 0.5; + +/* -------------------------------------------------------------------- */ +/* Turn these into the Ax+By+C = 0 form of the lines. */ +/* -------------------------------------------------------------------- */ + double a1, a2, b1, b2, c1, c2; + + a1 = m1; + a2 = m2; + + b1 = -1.0; + b2 = -1.0; + + c1 = (y1 - m1*x1); + c2 = (y2 - m2*x2); + +/* -------------------------------------------------------------------- */ +/* Compute the intersection of the two lines through the center */ +/* of the circle, using Kramers rule. */ +/* -------------------------------------------------------------------- */ + double det_inv; + + if( a1*b2 - a2*b1 == 0.0 ) + return FALSE; + + det_inv = 1 / (a1*b2 - a2*b1); + + *x_center = (b1*c2 - b2*c1) * det_inv; + *y_center = (a2*c1 - a1*c2) * det_inv; + + return TRUE; +} + +/************************************************************************/ +/* OGRWalkArcToLineString() */ +/************************************************************************/ +static int +OGRWalkArcToLineString( double dfStartX, double dfStartY, + double dfAlongX, double dfAlongY, + double dfEndX, double dfEndY, + double dfCenterX, double dfCenterY, + double dfCenterZ, double dfRadius, + int nNumPoints, OGRLineString *poLS ) +{ + double dfStartAngle, dfEndAngle, dfAlongAngle; + double dfDeltaX, dfDeltaY; + + dfDeltaX = dfStartX - dfCenterX; + dfDeltaY = dfStartY - dfCenterY; + dfStartAngle = -1 * atan2(dfDeltaY,dfDeltaX) * 180.0 / PI; + + dfDeltaX = dfAlongX - dfCenterX; + dfDeltaY = dfAlongY - dfCenterY; + dfAlongAngle = -1 * atan2(dfDeltaY,dfDeltaX) * 180.0 / PI; + + dfDeltaX = dfEndX - dfCenterX; + dfDeltaY = dfEndY - dfCenterY; + dfEndAngle = -1 * atan2(dfDeltaY,dfDeltaX) * 180.0 / PI; + + // Try positive (clockwise?) winding. + while( dfAlongAngle < dfStartAngle ) + dfAlongAngle += 360.0; + + while( dfEndAngle < dfAlongAngle ) + dfEndAngle += 360.0; + + if( nNumPoints == 3 ) //Arc + { + if( dfEndAngle - dfStartAngle > 360.0 ) + { + while( dfAlongAngle > dfStartAngle ) + dfAlongAngle -= 360.0; + + while( dfEndAngle > dfAlongAngle ) + dfEndAngle -= 360.0; + } + } + else if( nNumPoints == 5 ) //Circle + { + // If anticlockwise, then start angle - end angle = 360.0; + // Otherwise end angle - start angle = 360.0; + if( dfEndAngle - dfStartAngle > 360.0 ) + dfEndAngle = dfStartAngle - 360.0; + else + dfEndAngle = dfStartAngle + 360.0; + } + else + return FALSE; + + OGRLineString* poArcpoLS = + (OGRLineString*)OGRGeometryFactory::approximateArcAngles( + dfCenterX, dfCenterY, dfCenterZ, + dfRadius, dfRadius, 0.0, + dfStartAngle, dfEndAngle, 0.0 ); + + if( poArcpoLS == NULL ) + return FALSE; + + poLS->addSubLineString(poArcpoLS); + delete poArcpoLS; + + return TRUE; +} + +/************************************************************************/ +/* Binary2WkbMGeom() */ +/************************************************************************/ +OGRErr Binary2WkbMGeom(unsigned char *& p, WKBGeometry* geom, int nBytes) +{ + GUInt32 i,j,k; + + if( nBytes < 28 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "WalkGeom binary size (%d) too small", + nBytes); + return OGRERR_FAILURE; + } + + memcpy(&geom->wkbType, p, 4); + CPL_LSBPTR32( &geom->wkbType ); + p += 4; + + switch(geom->wkbType) + { + case wkbPoint: + memcpy(&geom->point, p, sizeof(WKBPoint)); + CPL_LSBPTRPOINT(geom->point); + p += sizeof(WKBPoint); + break; + case wkbLineString: + memcpy(&geom->linestring.numSegments, p, 4); + CPL_LSBPTR32(&geom->linestring.numSegments); + p += 4; + + geom->linestring.segments = new CurveSegment[geom->linestring.numSegments]; + + for(i = 0; i < geom->linestring.numSegments; i++) + { + memcpy(&geom->linestring.segments[i].lineType, p, 4); + CPL_LSBPTR32(&geom->linestring.segments[i].lineType); + p += 4; + memcpy(&geom->linestring.segments[i].numPoints, p, 4); + CPL_LSBPTR32(&geom->linestring.segments[i].numPoints); + p += 4; + geom->linestring.segments[i].points = + new Point[geom->linestring.segments[i].numPoints]; + memcpy(geom->linestring.segments[i].points, p, + sizeof(Point) * geom->linestring.segments[i].numPoints); + CPL_LSBPTRPOINTS(geom->linestring.segments[i].points, + geom->linestring.segments[i].numPoints); + p += sizeof(Point) * geom->linestring.segments[i].numPoints; + } + break; + case wkbPolygon: + memcpy(&geom->polygon.numRings, p, 4); + CPL_LSBPTR32(&geom->polygon.numRings); + p += 4; + geom->polygon.rings = new LineString[geom->polygon.numRings]; + + for(i = 0; i < geom->polygon.numRings; i++) + { + memcpy(&geom->polygon.rings[i].numSegments, p, 4); + CPL_LSBPTR32(&geom->polygon.rings[i].numSegments); + p += 4; + geom->polygon.rings[i].segments = + new CurveSegment[geom->polygon.rings[i].numSegments]; + + for(j = 0; j < geom->polygon.rings[i].numSegments; j++) + { + memcpy(&geom->polygon.rings[i].segments[j].lineType, p, 4); + CPL_LSBPTR32(&geom->polygon.rings[i].segments[j].lineType); + p += 4; + memcpy(&geom->polygon.rings[i].segments[j].numPoints, p, 4); + CPL_LSBPTR32(&geom->polygon.rings[i].segments[j].numPoints); + p += 4; + geom->polygon.rings[i].segments[j].points = + new Point[geom->polygon.rings[i].segments[j].numPoints]; + memcpy(geom->polygon.rings[i].segments[j].points, p, + sizeof(Point) * geom->polygon.rings[i].segments[j].numPoints); + CPL_LSBPTRPOINTS(geom->polygon.rings[i].segments[j].points, + geom->polygon.rings[i].segments[j].numPoints); + p += sizeof(Point) * geom->polygon.rings[i].segments[j].numPoints; + } + } + break; + case wkbMultiPoint: + memcpy(&geom->mpoint.num_wkbPoints, p, 4); + CPL_LSBPTR32(&geom->mpoint.num_wkbPoints); + p += 4; + geom->mpoint.WKBPoints = new WKBPoint[geom->mpoint.num_wkbPoints]; + memcpy(geom->mpoint.WKBPoints, p, sizeof(WKBPoint) * geom->mpoint.num_wkbPoints); + CPL_LSBPTRPOINTS(geom->mpoint.WKBPoints, geom->mpoint.num_wkbPoints); + p += sizeof(WKBPoint) * geom->mpoint.num_wkbPoints; + break; + case wkbMultiLineString: + memcpy(&geom->mlinestring.num_wkbLineStrings, p, 4); + CPL_LSBPTR32(&geom->mlinestring.num_wkbLineStrings); + p += 4; + geom->mlinestring.WKBLineStrings = + new WKBLineString[geom->mlinestring.num_wkbLineStrings]; + + for(i = 0; i < geom->mlinestring.num_wkbLineStrings; i++) + { + memcpy(&geom->mlinestring.WKBLineStrings[i].numSegments, p, 4); + CPL_LSBPTR32(&geom->mlinestring.WKBLineStrings[i].numSegments); + p += 4; + geom->mlinestring.WKBLineStrings[i].segments = + new CurveSegment[geom->mlinestring.WKBLineStrings[i].numSegments]; + + for(j = 0; j < geom->mlinestring.WKBLineStrings[i].numSegments; j++) + { + memcpy(&geom->mlinestring.WKBLineStrings[i].segments[j].lineType, p, 4); + CPL_LSBPTR32(&geom->mlinestring.WKBLineStrings[i].segments[j].lineType); + p += 4; + memcpy(&geom->mlinestring.WKBLineStrings[i].segments[j].numPoints, p, 4); + CPL_LSBPTR32(&geom->mlinestring.WKBLineStrings[i].segments[j].numPoints); + p += 4; + geom->mlinestring.WKBLineStrings[i].segments[j].points = + new Point[geom->mlinestring.WKBLineStrings[i].segments[j].numPoints]; + memcpy(geom->mlinestring.WKBLineStrings[i].segments[j].points, p, + sizeof(Point) * geom->mlinestring.WKBLineStrings[i].segments[j].numPoints); + CPL_LSBPTRPOINTS(geom->mlinestring.WKBLineStrings[i].segments[j].points, + geom->mlinestring.WKBLineStrings[i].segments[j].numPoints); + p += sizeof(Point) * geom->mlinestring.WKBLineStrings[i].segments[j].numPoints; + } + } + break; + case wkbMultiPolygon: + memcpy(&geom->mpolygon.num_wkbPolygons, p, 4); + CPL_LSBPTR32(&geom->mpolygon.num_wkbPolygons); + p += 4; + geom->mpolygon.WKBPolygons = new WKBPolygon[geom->mpolygon.num_wkbPolygons]; + + for(i = 0; i < geom->mpolygon.num_wkbPolygons; i++) + { + memcpy(&geom->mpolygon.WKBPolygons[i].numRings, p, 4); + CPL_LSBPTR32(&geom->mpolygon.WKBPolygons[i].numRings); + p += 4; + geom->mpolygon.WKBPolygons[i].rings = + new LineString[geom->mpolygon.WKBPolygons[i].numRings]; + + for(j = 0; j < geom->mpolygon.WKBPolygons[i].numRings; j++) + { + memcpy(&geom->mpolygon.WKBPolygons[i].rings[j].numSegments, p, 4); + CPL_LSBPTR32(&geom->mpolygon.WKBPolygons[i].rings[j].numSegments); + p += 4; + geom->mpolygon.WKBPolygons[i].rings[j].segments = + new CurveSegment[geom->mpolygon.WKBPolygons[i].rings[j].numSegments]; + + for(k = 0; k < geom->mpolygon.WKBPolygons[i].rings[j].numSegments; k++) + { + memcpy(&geom->mpolygon.WKBPolygons[i].rings[j].segments[k].lineType, p, 4); + CPL_LSBPTR32(&geom->mpolygon.WKBPolygons[i].rings[j].segments[k].lineType); + p += 4; + memcpy(&geom->mpolygon.WKBPolygons[i].rings[j].segments[k].numPoints, p, 4); + CPL_LSBPTR32(&geom->mpolygon.WKBPolygons[i].rings[j].segments[k].numPoints); + p += 4; + geom->mpolygon.WKBPolygons[i].rings[j].segments[k].points = + new Point[geom->mpolygon.WKBPolygons[i].rings[j].segments[k].numPoints]; + memcpy(geom->mpolygon.WKBPolygons[i].rings[j].segments[k].points, + p, sizeof(Point) * + geom->mpolygon.WKBPolygons[i].rings[j].segments[k].numPoints); + CPL_LSBPTRPOINTS(geom->mpolygon.WKBPolygons[i].rings[j].segments[k].points, + geom->mpolygon.WKBPolygons[i].rings[j].segments[k].numPoints); + p += sizeof(Point) * + geom->mpolygon.WKBPolygons[i].rings[j].segments[k].numPoints; + } + } + } + break; + default: + return OGRERR_FAILURE; + } + return OGRERR_NONE; +} + +/************************************************************************/ +/* Binary2WkbGeom() */ +/************************************************************************/ +OGRErr Binary2WkbGeom(unsigned char *p, WKBGeometry* geom, int nBytes) +{ + GUInt32 i; + + if( nBytes < 28 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "WalkGeom binary size (%d) too small", + nBytes); + return OGRERR_FAILURE; + } + + memcpy(&geom->wkbType, p, 4); + CPL_LSBPTR32( &geom->wkbType ); + + switch(geom->wkbType) + { + case wkbPoint: + case wkbLineString: + case wkbPolygon: + case wkbMultiPoint: + case wkbMultiLineString: + case wkbMultiPolygon: + return Binary2WkbMGeom(p, geom, nBytes); + case wkbGeometryCollection: + p += 4; + memcpy(&geom->mgeometries.num_wkbSGeometries, p, 4); + CPL_LSBPTR32( &geom->mgeometries.num_wkbSGeometries ); + p += 4; + geom->mgeometries.WKBGeometries = + new WKBSimpleGeometry[geom->mgeometries.num_wkbSGeometries]; + + for(i = 0; i < geom->mgeometries.num_wkbSGeometries; i++) + Binary2WkbMGeom(p, (WKBGeometry*)(&geom->mgeometries.WKBGeometries[i]), nBytes-8); + break; + default: + geom->wkbType=wkbUnknown; + return OGRERR_FAILURE; + } + return OGRERR_NONE; +} + +/************************************************************************/ +/* TranslateWalkPoint() */ +/************************************************************************/ +OGRBoolean TranslateWalkPoint(OGRPoint *poPoint, WKBPoint* pWalkWkbPoint) +{ + if ( poPoint == NULL || pWalkWkbPoint == NULL ) + return FALSE; + + poPoint->setX(pWalkWkbPoint->x); + poPoint->setY(pWalkWkbPoint->y); + poPoint->setZ(pWalkWkbPoint->z); + return TRUE; +} + +/************************************************************************/ +/* TranslateCurveSegment() */ +/************************************************************************/ +OGRBoolean TranslateCurveSegment(OGRLineString *poLS, CurveSegment* pSegment) +{ + if ( poLS == NULL || pSegment == NULL ) + return FALSE; + + switch(pSegment->lineType) + { + case wkLineType3PArc: + case wkLineType3PCircle: + { + double dfCenterX, dfCenterY, dfCenterZ, dfRadius; + + if ( !OGRWalkArcCenterFromEdgePoints( pSegment->points[0].x, pSegment->points[0].y, + pSegment->points[1].x, pSegment->points[1].y, + pSegment->points[2].x, pSegment->points[2].y, + &dfCenterX, &dfCenterY ) ) + return FALSE; + + //Use Z value of the first point + dfCenterZ = pSegment->points[0].z; + dfRadius = sqrt( (dfCenterX - pSegment->points[0].x) * (dfCenterX - pSegment->points[0].x) + + (dfCenterY - pSegment->points[0].y) * (dfCenterY - pSegment->points[0].y) ); + + if ( !OGRWalkArcToLineString( pSegment->points[0].x, pSegment->points[0].y, + pSegment->points[1].x, pSegment->points[1].y, + pSegment->points[2].x, pSegment->points[2].y, + dfCenterX, dfCenterY, dfCenterZ, dfRadius, + pSegment->numPoints, poLS ) ) + return FALSE; + } + break; + case wkLineTypeStraight: + default: + { + for (GUInt32 i = 0; i < pSegment->numPoints; ++i) + { + Point point = pSegment->points[i]; + poLS->addPoint(point.x, point.y, point.z); + } + } + break; + } + + return TRUE; +} + +/************************************************************************/ +/* TranslateWalkLineString() */ +/************************************************************************/ +OGRBoolean TranslateWalkLineString(OGRLineString *poLS, LineString* pLineString) +{ + if ( poLS == NULL || pLineString == NULL ) + return FALSE; + + for (GUInt32 i = 0; i < pLineString->numSegments; ++i) + { + if ( !TranslateCurveSegment(poLS, &pLineString->segments[i]) ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* TranslateWalkLinearring() */ +/************************************************************************/ +OGRBoolean TranslateWalkLinearring(OGRLinearRing *poRing, LineString* pLineString) +{ + if ( poRing == NULL || pLineString == NULL ) + return FALSE; + + for(GUInt32 i = 0; i < pLineString->numSegments; i++) + TranslateCurveSegment(poRing, &pLineString->segments[i]); + + return TRUE; +} + +/************************************************************************/ +/* TranslateWalkPolygon() */ +/************************************************************************/ +OGRBoolean TranslateWalkPolygon(OGRPolygon *poPolygon, WKBPolygon* pWalkWkbPolgon) +{ + if ( poPolygon == NULL || pWalkWkbPolgon == NULL ) + return FALSE; + + for (GUInt32 i = 0; i < pWalkWkbPolgon->numRings; ++i) + { + OGRLinearRing* poRing = new OGRLinearRing(); + LineString* lineString = &pWalkWkbPolgon->rings[i]; + TranslateWalkLinearring(poRing, lineString); + poPolygon->addRingDirectly(poRing); + } + + return TRUE; +} + +/************************************************************************/ +/* TranslateWalkGeom() */ +/************************************************************************/ +OGRErr TranslateWalkGeom(OGRGeometry **ppoGeom, WKBGeometry* geom) +{ + if ( ppoGeom == NULL || geom == NULL ) + return OGRERR_NOT_ENOUGH_DATA; + + OGRGeometry* poGeom = + OGRGeometryFactory::createGeometry(wkbFlatten(geom->wkbType)); + + if ( poGeom == NULL ) + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + + switch (geom->wkbType) + { + case wkbPoint: + { + if (!TranslateWalkPoint((OGRPoint *)poGeom, &geom->point)) + return OGRERR_CORRUPT_DATA; + } + break; + case wkbLineString: + { + if (!TranslateWalkLineString((OGRLineString *)poGeom, &geom->linestring)) + return OGRERR_CORRUPT_DATA; + } + break; + case wkbPolygon: + { + if (!TranslateWalkPolygon((OGRPolygon *)poGeom, &geom->polygon)) + return OGRERR_CORRUPT_DATA; + } + break; + case wkbMultiPoint: + { + for (GUInt32 i = 0; i < geom->mpoint.num_wkbPoints; ++i) + { + OGRPoint* poPoint = new OGRPoint(); + if (!TranslateWalkPoint(poPoint, &geom->mpoint.WKBPoints[i])) + return OGRERR_CORRUPT_DATA; + ((OGRMultiPoint *)poGeom)->addGeometryDirectly(poPoint); + } + } + break; + case wkbMultiLineString: + { + for (GUInt32 i = 0; i < geom->mlinestring.num_wkbLineStrings; ++i) + { + OGRLineString* poLS = new OGRLineString(); + if (!TranslateWalkLineString(poLS, &geom->mlinestring.WKBLineStrings[i])) + return OGRERR_CORRUPT_DATA; + ((OGRMultiLineString *)poGeom)->addGeometryDirectly(poLS); + } + } + break; + case wkbMultiPolygon: + { + for (GUInt32 i = 0; i < geom->mpolygon.num_wkbPolygons; ++i) + { + OGRPolygon* poPolygon = new OGRPolygon(); + if (!TranslateWalkPolygon(poPolygon, &geom->mpolygon.WKBPolygons[i])) + return OGRERR_CORRUPT_DATA; + ((OGRMultiPolygon *)poGeom)->addGeometryDirectly(poPolygon); + } + } + break; + case wkbGeometryCollection: + { + for (GUInt32 i = 0; i < geom->mgeometries.num_wkbSGeometries; ++i) + { + WKBSimpleGeometry* sg = &geom->mgeometries.WKBGeometries[i]; + switch (sg->wkbType) + { + case wkbPoint: + { + OGRPoint* poPoint = new OGRPoint(); + if (!TranslateWalkPoint(poPoint, &sg->point)) + return OGRERR_CORRUPT_DATA; + ((OGRGeometryCollection *)poGeom)->addGeometryDirectly(poPoint); + } + break; + case wkbLineString: + { + OGRLineString* poLS = new OGRLineString(); + if (!TranslateWalkLineString(poLS, &sg->linestring)) + return OGRERR_CORRUPT_DATA; + ((OGRGeometryCollection *)poGeom)->addGeometryDirectly(poLS); + } + break; + case wkbPolygon: + { + OGRPolygon* poPolygon = new OGRPolygon(); + if (!TranslateWalkPolygon(poPolygon, &sg->polygon)) + return OGRERR_CORRUPT_DATA; + ((OGRGeometryCollection *)poGeom)->addGeometryDirectly(poPolygon); + } + break; + default: return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + } + } + break; + default: + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + + *ppoGeom = poGeom; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteCurveSegment() */ +/************************************************************************/ +void DeleteCurveSegment(CurveSegment &obj) +{ + if(obj.numPoints) + delete [] obj.points; +} + +/************************************************************************/ +/* DeleteWKBMultiPoint() */ +/************************************************************************/ +void DeleteWKBMultiPoint(WKBMultiPoint &obj) +{ + if (obj.num_wkbPoints) + { + delete[] obj.WKBPoints; + obj.num_wkbPoints = 0; + } +} + +/************************************************************************/ +/* DeleteWKBLineString() */ +/************************************************************************/ +void DeleteWKBLineString(WKBLineString &obj) +{ + if(obj.numSegments) + { + for (GUInt32 i = 0; i < obj.numSegments; i++) + DeleteCurveSegment(obj.segments[i]); + delete [] obj.segments; + obj.numSegments = 0; + } +} + +/************************************************************************/ +/* DeleteWKBMultiLineString() */ +/************************************************************************/ +void DeleteWKBMultiLineString(WKBMultiLineString &obj) +{ + if (obj.num_wkbLineStrings) + { + for (GUInt32 i = 0; i < obj.num_wkbLineStrings; i++) + DeleteWKBLineString(obj.WKBLineStrings[i]); + + delete [] obj.WKBLineStrings; + obj.num_wkbLineStrings = 0; + } +} + +/************************************************************************/ +/* DeleteWKBPolygon() */ +/************************************************************************/ +void DeleteWKBPolygon(WKBPolygon &obj) +{ + if (obj.numRings) + { + for (GUInt32 i = 0; i < obj.numRings; i++) + DeleteWKBLineString(obj.rings[i]); + + delete [] obj.rings; + obj.numRings = 0; + } +} + +/************************************************************************/ +/* DeleteWKBMultiPolygon() */ +/************************************************************************/ +void DeleteWKBMultiPolygon(WKBMultiPolygon &obj) +{ + if (obj.num_wkbPolygons) + { + for (GUInt32 i = 0; i < obj.num_wkbPolygons; i++) + DeleteWKBPolygon(obj.WKBPolygons[i]); + + delete [] obj.WKBPolygons; + obj.num_wkbPolygons = 0; + } +} + +/************************************************************************/ +/* DeleteWKBGeometryCollection() */ +/************************************************************************/ +void DeleteWKBGeometryCollection(WKBGeometryCollection &obj) +{ + if (obj.num_wkbSGeometries) + { + for (GUInt32 i = 0; i < obj.num_wkbSGeometries; i++) + DeleteWKBGeometry(*(WKBGeometry*)&obj.WKBGeometries[i]); + + delete [] obj.WKBGeometries; + obj.num_wkbSGeometries = 0; + } +} + +/************************************************************************/ +/* DeleteWKBGeometry() */ +/************************************************************************/ +void DeleteWKBGeometry(WKBGeometry &obj) +{ + switch (obj.wkbType) + { + case wkbPoint: + break; + + case wkbLineString: + DeleteWKBLineString(obj.linestring); + break; + + case wkbPolygon: + DeleteWKBPolygon(obj.polygon); + break; + + case wkbMultiPoint: + DeleteWKBMultiPoint(obj.mpoint); + break; + + case wkbMultiLineString: + DeleteWKBMultiLineString(obj.mlinestring); + break; + + case wkbMultiPolygon: + DeleteWKBMultiPolygon(obj.mpolygon); + break; + + case wkbGeometryCollection: + DeleteWKBGeometryCollection(obj.mgeometries); + break; + } + obj.wkbType = wkbUnknown; +} \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/GNUmakefile new file mode 100644 index 000000000..891ff2262 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/GNUmakefile @@ -0,0 +1,15 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrwaspdriver.o ogrwaspdatasource.o ogrwasplayer.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ:.o=.$(OBJ_EXT)): ogrwasp.h diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/drv_wasp.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/drv_wasp.html new file mode 100644 index 000000000..56633ce14 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/drv_wasp.html @@ -0,0 +1,29 @@ + + +WAsP - WAsP .map format + + + + +

      WAsP - WAsP .map format

      + +(GDAL/OGR >= 1.11.0)

      + +This driver writes .map files to be used with WAsP. The only allowed geometries are linestrings.

      + +

      Configuration options

      + +
        +
      • WASP_FIELDS : a coma separated list of fields. For elevation, the name of the height field. For rouhgness, the name of the left and right roughness fields resp.
      • +
      • WASP_MERGE : this may be set to "NO". Used only when generating roughness from polygons. All polygon boundaries will be output (including those with the same left and righ roughness). This is useful (along with option -skipfailures) for debugging incorect input geometries.
      • +
      • WASP_GEOM_FIELD : in case input has several geometry columns and the first one (default) is not the right one.
      • +
      • WASP_TOLERANCE : specify a tolerance for line simplification of output (calls geos).
      • +
      • WASP_ADJ_TOLER : points that are less than tolerance appart from previous point on x and on y are ommited.
      • +
      • WASP_POINT_TO_CIRCLE_RADIUS : lines that became points du to simplification are replaces by 8 point circles (octogons).
      • +
      + +

      +Note that if not option is specified, the layer is assumed to be an elevation layer where the elevation is the z-components of the linestrings' points. +

      + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/makefile.vc new file mode 100644 index 000000000..83ba113b8 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrwaspdriver.obj ogrwaspdatasource.obj ogrwasplayer.obj +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/ogrwasp.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/ogrwasp.h new file mode 100644 index 000000000..2bdd41e4a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/ogrwasp.h @@ -0,0 +1,223 @@ +/****************************************************************************** + * + * Project: WAsP Translator + * Purpose: Definition of classes for OGR .map driver. + * Author: Vincent Mora, vincent dot mora at oslandia dot com + * + ****************************************************************************** + * Copyright (c) 2014, Oslandia + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_WASP_H_INCLUDED +#define _OGR_WASP_H_INCLUDED + +#include "ogrsf_frmts.h" + +#include +#include +#include + +/************************************************************************/ +/* OGRWAsPLayer */ +/************************************************************************/ + +class OGRWAsPLayer : public OGRLayer +{ + /* stuff for polygon processing */ + + /* note if shared ptr are available, replace the ptr in the two structs */ + /* and remove deletion of array elements in ~OGRWAsPLayer() */ + struct Zone + { + OGREnvelope oEnvelope; + OGRPolygon * poPolygon; + double dfZ; + }; + + struct Boundary + { + OGRLineString * poLine; + double dfLeft; + double dfRight; + }; + + const bool bMerge; + std::vector< Zone > oZones; + std::vector< Boundary > oBoundaries; + + static bool isEqual( const double & dfRouhness1, const double & dfRouhness2 ) { return fabs( dfRouhness1 - dfRouhness2 ) < 1e-3; } + + /* end of stuff for polygon processing */ + + int iFeatureCount; + + const CPLString sName; + VSILFILE * hFile; + + /* for roughness zone, two fields for linestrings (left/right), one for polygons */ + /* for elevation, one fiels (height) */ + const CPLString sFirstField; + const CPLString sSecondField; + const CPLString sGeomField; + int iFirstFieldIdx; + int iSecondFieldIdx; + int iGeomFieldIdx; + + OGRFeatureDefn * poLayerDefn; + OGRSpatialReference * poSpatialReference; + + vsi_l_offset iOffsetFeatureBegin; + + enum OpenMode {READ_ONLY, WRITE_ONLY}; + OpenMode eMode; + + std::auto_ptr pdfTolerance; + std::auto_ptr pdfAdjacentPointTolerance; + std::auto_ptr pdfPointToCircleRadius; + + OGRErr WriteRoughness( OGRLineString *, + const double & dfZleft, + const double & dfZright ); + OGRErr WriteRoughness( OGRPolygon *, + const double & dfZ ); + OGRErr WriteRoughness( OGRGeometry *, + const double & dfZleft, + const double & dfZright ); + + OGRErr WriteElevation( OGRLineString *, const double & dfZ ); + OGRErr WriteElevation( OGRGeometry *, const double & dfZ ); + + static double AvgZ( OGRLineString * poGeom ); + static double AvgZ( OGRPolygon * poGeom ); + static double AvgZ( OGRGeometryCollection * poGeom ); + static double AvgZ( OGRGeometry * poGeom ); + + /* return a simplified line (caller is responsible for resource ) + * + * if pdfTolerance is not NULL, + * calls GEOS symplify + * + * if pdfAdjacentPointTolerance is not NULL, + * remove consecutive points that are less than torelance appart + * in x and y + * + * if pdfPointToCircleRadius is not NULL, + * lines that have been simplified to a point are converted to a 8 pt circle + * */ + OGRLineString * Simplify( const OGRLineString & line ) const; + + public: + /* For writing */ + /* Takes ownership of poTolerance */ + OGRWAsPLayer( const char * pszName, + VSILFILE * hFile, + OGRSpatialReference * poSpatialRef, + const CPLString & sFirstField, + const CPLString & sSecondField, + const CPLString & sGeomField, + bool bMerge, + double * pdfTolerance, + double * pdfAdjacentPointTolerance, + double * pdfPointToCircleRadius ); + + /* For reading */ + OGRWAsPLayer( const char * pszName, + VSILFILE * hFile, + OGRSpatialReference * poSpatialRef ); + + ~OGRWAsPLayer(); + + virtual OGRFeatureDefn * GetLayerDefn() { return poLayerDefn; } + + virtual void ResetReading(); + virtual int TestCapability( const char * ); + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ); + virtual OGRErr CreateGeomField( OGRGeomFieldDefn *poGeomField, + int bApproxOK = TRUE ); + + virtual OGRErr ICreateFeature( OGRFeature * poFeature ); + + virtual OGRFeature *GetNextFeature(); + OGRFeature *GetNextRawFeature(); + virtual OGRwkbGeometryType GetGeomType() { return wkbLineString25D; } + virtual OGRSpatialReference *GetSpatialRef() { return poSpatialReference; } + virtual const char *GetName() { return sName.c_str(); } +}; + +/************************************************************************/ +/* OGRWAsPDataSource */ +/************************************************************************/ + +class OGRWAsPDataSource : public OGRDataSource +{ + CPLString sFilename; + VSILFILE * hFile; + std::auto_ptr oLayer; + + void GetOptions(CPLString & sFirstField, + CPLString & sSecondField, + CPLString & sGeomField, + bool & bMerge) const; + public: + /** @note takes ownership of hFile (i.e. responsibility for closing) */ + OGRWAsPDataSource( const char * pszName, + VSILFILE * hFile ); + ~OGRWAsPDataSource(); + + virtual const char *GetName() { return sFilename.c_str(); } + virtual int GetLayerCount() { return oLayer.get() ? 1 : 0; } + virtual OGRLayer *GetLayer( int ); + virtual OGRLayer *GetLayerByName( const char * ); + + virtual OGRLayer *ICreateLayer( const char *pszName, + OGRSpatialReference *poSpatialRef = NULL, + OGRwkbGeometryType eGType = wkbUnknown, + char ** papszOptions = NULL ); + + virtual int TestCapability( const char * ); + OGRErr Load( bool bSilent = false ); +}; + +/************************************************************************/ +/* OGRWAsPDriver */ +/************************************************************************/ + +class OGRWAsPDriver : public OGRSFDriver +{ + + public: + ~OGRWAsPDriver() {} + + virtual const char* GetName() { return "WAsP"; } + virtual OGRDataSource* Open( const char *, int ); + + virtual OGRDataSource *CreateDataSource( const char *pszName, + char ** = NULL ); + + virtual OGRErr DeleteDataSource (const char *pszName); + + virtual int TestCapability( const char * ); +}; + + +#endif /* ndef _OGR_WASP_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/ogrwaspdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/ogrwaspdatasource.cpp new file mode 100644 index 000000000..874aab964 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/ogrwaspdatasource.cpp @@ -0,0 +1,335 @@ +/****************************************************************************** + * $Id: ogrwaspdatasource.cpp 25307 2012-12-15 09:04:40Z rouault $ + * + * Project: WAsP Translator + * Purpose: Implements OGRWAsPDataSource class + * Author: Vincent Mora, vincent dot mora at oslandia dot com + * + ****************************************************************************** + * Copyright (c) 2014, Oslandia + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrwasp.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +#include +#include + +/************************************************************************/ +/* OGRWAsPDataSource() */ +/************************************************************************/ + +OGRWAsPDataSource::OGRWAsPDataSource( const char * pszName, + VSILFILE * hFileHandle ) + : sFilename( pszName ) + , hFile( hFileHandle ) + +{ +} + +/************************************************************************/ +/* ~OGRWAsPDataSource() */ +/************************************************************************/ + +OGRWAsPDataSource::~OGRWAsPDataSource() + +{ + oLayer.reset(); /* we write to file int layer dtor */ + VSIFCloseL( hFile ); /* nothing smart can be done here in case of error */ +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRWAsPDataSource::TestCapability( const char * pszCap ) + +{ + return EQUAL(pszCap,ODsCCreateLayer) && oLayer.get() == NULL; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer *OGRWAsPDataSource::GetLayerByName( const char * pszName ) + +{ + return ( oLayer.get() && EQUAL( pszName, oLayer->GetName() ) ) + ? oLayer.get() + : NULL; +} + +/************************************************************************/ +/* Load() */ +/************************************************************************/ + +OGRErr OGRWAsPDataSource::Load(bool bSilent) + +{ + /* if we don't have a layer, we read from file */ + if ( oLayer.get() ) + { + if (!bSilent) CPLError( CE_Failure, CPLE_NotSupported, "layer already loaded"); + return OGRERR_FAILURE; + } + /* Parse the first line of the file in case it'a a spatial ref*/ + const char * pszLine = CPLReadLine2L( hFile, 1024, NULL ); + if ( !pszLine ) + { + if (!bSilent) CPLError( CE_Failure, CPLE_FileIO, "empty file"); + return OGRERR_FAILURE; + } + CPLString sLine( pszLine ); + sLine = sLine.substr(0, sLine.find("|")); + OGRSpatialReference * poSpatialRef = new OGRSpatialReference; + if ( poSpatialRef->importFromProj4( sLine.c_str() ) != OGRERR_NONE ) + { + if (!bSilent) CPLError( CE_Warning, CPLE_FileIO, "cannot find spatial reference"); + delete poSpatialRef; + poSpatialRef = NULL; + } + + /* TODO Parse those line since they define a coordinate transformation */ + CPLReadLineL( hFile ); + CPLReadLineL( hFile ); + CPLReadLineL( hFile ); + + oLayer.reset( new OGRWAsPLayer( CPLGetBasename(sFilename.c_str()), + hFile, + poSpatialRef ) ); + if (poSpatialRef) poSpatialRef->Release(); + + const vsi_l_offset iOffset = VSIFTellL( hFile ); + pszLine = CPLReadLineL( hFile ); + if ( !pszLine ) + { + if (!bSilent) CPLError( CE_Failure, CPLE_FileIO, "no feature in file"); + oLayer.reset(); + return OGRERR_FAILURE; + } + + double dfValues[4]; + int iNumValues = 0; + { + std::istringstream iss(pszLine); + while ( iNumValues < 4 && (iss >> dfValues[iNumValues] ) ){ ++iNumValues; } + + if ( iNumValues < 2 ) + { + if (!bSilent && iNumValues) + CPLError(CE_Failure, CPLE_FileIO, "no enough values" ); + else if (!bSilent) + CPLError(CE_Failure, CPLE_FileIO, "no feature in file" ); + + oLayer.reset(); + return OGRERR_FAILURE; + } + } + + if ( iNumValues == 3 || iNumValues == 4 ) + { + OGRFieldDefn left("z_left", OFTReal); + OGRFieldDefn right("z_right", OFTReal); + oLayer->CreateField( &left ); + oLayer->CreateField( &right ); + } + if ( iNumValues == 2 || iNumValues == 4 ) + { + OGRFieldDefn height("elevation", OFTReal); + oLayer->CreateField( &height ); + } + + VSIFSeekL( hFile, iOffset, SEEK_SET ); + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + + +OGRLayer *OGRWAsPDataSource::GetLayer( int iLayer ) + +{ + return ( iLayer == 0 ) ? oLayer.get() : NULL; +} + + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer *OGRWAsPDataSource::ICreateLayer(const char *pszName, + OGRSpatialReference *poSpatialRef, + OGRwkbGeometryType eGType, + char ** papszOptions) + +{ + + if ( eGType != wkbLineString + && eGType != wkbLineString25D + && eGType != wkbMultiLineString + && eGType != wkbMultiLineString25D + && eGType != wkbPolygon + && eGType != wkbPolygon25D + && eGType != wkbMultiPolygon + && eGType != wkbMultiPolygon25D ) + { + CPLError( CE_Failure, + CPLE_NotSupported, + "unsupported geometry type %s", OGRGeometryTypeToName( eGType ) ); + return NULL; + } + + if ( !OGRGeometryFactory::haveGEOS() + && ( eGType == wkbPolygon + || eGType == wkbPolygon25D + || eGType == wkbMultiPolygon + || eGType == wkbMultiPolygon25D )) + { + CPLError( CE_Failure, + CPLE_NotSupported, + "unsupported geometry type %s without GEOS support", OGRGeometryTypeToName( eGType ) ); + return NULL; + } + + if ( oLayer.get() ) + { + CPLError( CE_Failure, + CPLE_NotSupported, + "this data source does not support more than one layer" ); + return NULL; + } + + CPLString sFirstField, sSecondField, sGeomField; + + const char *pszFields = CSLFetchNameValue( papszOptions, "WASP_FIELDS" ); + const CPLString sFields( pszFields ? pszFields : "" ); + if ( ! sFields.empty() ) + { + /* parse the coma separated list of fields */ + const size_t iComa = sFields.find(','); + if ( std::string::npos != iComa ) + { + sFirstField = sFields.substr(0, iComa); + sSecondField = sFields.substr( iComa + 1 ); + } + else + { + sFirstField = sFields; + } + } + + const char *pszGeomField = CSLFetchNameValue( papszOptions, "WASP_GEOM_FIELD" ); + sGeomField = CPLString( pszGeomField ? pszGeomField : "" ); + + const bool bMerge = CSLTestBoolean(CSLFetchNameValueDef( papszOptions, "WASP_MERGE", "YES" )); + + std::auto_ptr pdfTolerance; + { + const char *pszToler = CSLFetchNameValue( papszOptions, "WASP_TOLERANCE" ); + + if (pszToler) + { + if ( !OGRGeometryFactory::haveGEOS() ) + { + CPLError( CE_Warning, + CPLE_IllegalArg, + "GEOS support not enabled, ignoring option WASP_TOLERANCE" ); + } + else + { + pdfTolerance.reset( new double ); + if (!(std::istringstream( pszToler ) >> *pdfTolerance )) + { + CPLError( CE_Failure, + CPLE_IllegalArg, + "cannot set tolerance from %s", pszToler ); + return NULL; + } + } + } + } + + std::auto_ptr pdfAdjacentPointTolerance; + { + const char *pszAdjToler = CSLFetchNameValue( papszOptions, "WASP_ADJ_TOLER" ); + if ( pszAdjToler ) + { + pdfAdjacentPointTolerance.reset( new double ); + if (!(std::istringstream( pszAdjToler ) >> *pdfAdjacentPointTolerance )) + { + CPLError( CE_Failure, + CPLE_IllegalArg, + "cannot set tolerance from %s", pszAdjToler ); + return NULL; + } + } + } + + std::auto_ptr pdfPointToCircleRadius; + { + const char *pszPtToCircRad = CSLFetchNameValue( papszOptions, "WASP_POINT_TO_CIRCLE_RADIUS" ); + if ( pszPtToCircRad ) + { + pdfPointToCircleRadius.reset( new double ); + if (!(std::istringstream( pszPtToCircRad ) >> *pdfPointToCircleRadius )) + { + CPLError( CE_Failure, + CPLE_IllegalArg, + "cannot set tolerance from %s", pszPtToCircRad ); + return NULL; + } + } + } + + oLayer.reset( new OGRWAsPLayer( CPLGetBasename(pszName), + hFile, + poSpatialRef, + sFirstField, + sSecondField, + sGeomField, + bMerge, + pdfTolerance.release(), + pdfAdjacentPointTolerance.release(), + pdfPointToCircleRadius.release() ) ); + + char * ppszWktSpatialRef = NULL ; + if ( poSpatialRef + && poSpatialRef->exportToProj4( &ppszWktSpatialRef ) == OGRERR_NONE ) + { + VSIFPrintfL( hFile, "%s\n", ppszWktSpatialRef ); + OGRFree( ppszWktSpatialRef ); + } + else + { + VSIFPrintfL( hFile, "no spatial ref sys\n" ); + } + + VSIFPrintfL( hFile, " 0.0 0.0 0.0 0.0\n" ); + VSIFPrintfL( hFile, " 1.0 0.0 1.0 0.0\n" ); + VSIFPrintfL( hFile, " 1.0 0.0\n" ); + return oLayer.get(); +} + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/ogrwaspdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/ogrwaspdriver.cpp new file mode 100644 index 000000000..d41b4d1fa --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/ogrwaspdriver.cpp @@ -0,0 +1,119 @@ +/****************************************************************************** + * + * Project: WAsP Translator + * Purpose: Implements OGRWAsPDriver. + * Author: Vincent Mora, vincent dot mora at oslandia dot com + * + ****************************************************************************** + * Copyright (c) 2014, Oslandia + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrwasp.h" +#include "cpl_conv.h" +#include + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRWAsPDriver::Open( const char * pszFilename, int bUpdate ) + +{ + if (bUpdate) + { + return NULL; + } + + if (!EQUAL(CPLGetExtension(pszFilename), "map")) + { + return NULL; + } + + VSILFILE * fh = VSIFOpenL( pszFilename, "r" ); + if ( !fh ) + { + /*CPLError( CE_Failure, CPLE_FileIO, "cannot open file %s", pszFilename );*/ + return NULL; + } + std::auto_ptr pDataSource( new OGRWAsPDataSource( pszFilename, fh )); + + if ( pDataSource->Load(true) != OGRERR_NONE ) + { + return NULL; + } + return pDataSource.release(); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRWAsPDriver::TestCapability( const char * pszCap ) + +{ + return EQUAL(pszCap,ODrCCreateDataSource) + || EQUAL(pszCap,ODrCDeleteDataSource); +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource * OGRWAsPDriver::CreateDataSource( const char *pszName, char ** ) + +{ + VSILFILE * fh = VSIFOpenL( pszName, "w" ); + if ( !fh ) + { + CPLError( CE_Failure, CPLE_FileIO, "cannot open file %s", pszName ); + return NULL; + } + return new OGRWAsPDataSource( pszName, fh ); +} + +/************************************************************************/ +/* DeleteDataSource() */ +/************************************************************************/ + +OGRErr OGRWAsPDriver::DeleteDataSource (const char *pszName) + +{ + return VSIUnlink( pszName ) == 0 ? OGRERR_NONE : OGRERR_FAILURE; +} + +/************************************************************************/ +/* RegisterOGRWAsP() */ +/************************************************************************/ + +void RegisterOGRWAsP() + +{ + OGRSFDriver* poDriver = new OGRWAsPDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "WAsP .map format" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "map" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_wasp.html" ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/ogrwasplayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/ogrwasplayer.cpp new file mode 100644 index 000000000..34891ab65 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wasp/ogrwasplayer.cpp @@ -0,0 +1,936 @@ +/****************************************************************************** + * + * Project: WAsP Translator + * Purpose: Implements OGRWAsPLayer class. + * Author: Vincent Mora, vincent dot mora at oslandia dot com + * + ****************************************************************************** + * Copyright (c) 2014, Oslandia + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrwasp.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogrsf_frmts.h" + +#include +#include +#include + +#ifndef M_PI +# define M_PI 3.1415926535897932384626433832795 +#endif + +/************************************************************************/ +/* OGRWAsPLayer() */ +/************************************************************************/ + +OGRWAsPLayer::OGRWAsPLayer( const char * pszName, + VSILFILE * hFileHandle, + OGRSpatialReference * poSpatialRef ) + : bMerge( false ) + , iFeatureCount(0) + , sName( pszName ) + , hFile( hFileHandle ) + , iFirstFieldIdx( 0 ) + , iSecondFieldIdx( 1 ) + , iGeomFieldIdx( 0 ) + , poLayerDefn( new OGRFeatureDefn( pszName ) ) + , poSpatialReference( poSpatialRef ) + , iOffsetFeatureBegin( VSIFTellL( hFile ) ) + , eMode( READ_ONLY ) + +{ + SetDescription( poLayerDefn->GetName() ); + poLayerDefn->Reference(); + poLayerDefn->SetGeomType( wkbLineString25D ); + poLayerDefn->GetGeomFieldDefn(0)->SetType( wkbLineString25D ); + poLayerDefn->GetGeomFieldDefn(0)->SetSpatialRef( poSpatialReference ); + if (poSpatialReference) poSpatialReference->Reference(); +} + +OGRWAsPLayer::OGRWAsPLayer( const char * pszName, + VSILFILE * hFileHandle, + OGRSpatialReference * poSpatialRef, + const CPLString & sFirstFieldParam, + const CPLString & sSecondFieldParam, + const CPLString & sGeomFieldParam, + bool bMergeParam, + double * pdfToleranceParam, + double * pdfAdjacentPointToleranceParam, + double * pdfPointToCircleRadiusParam ) + : bMerge( bMergeParam ) + , iFeatureCount(0) + , sName( pszName ) + , hFile( hFileHandle ) + , sFirstField( sFirstFieldParam ) + , sSecondField( sSecondFieldParam ) + , sGeomField( sGeomFieldParam ) + , iFirstFieldIdx( -1 ) + , iSecondFieldIdx( -1 ) + , iGeomFieldIdx( sGeomFieldParam.empty() ? 0 : -1 ) + , poLayerDefn( new OGRFeatureDefn( pszName ) ) + , poSpatialReference( poSpatialRef ) + , iOffsetFeatureBegin( VSIFTellL( hFile ) ) /* avoids coverity warning */ + , eMode( WRITE_ONLY ) + , pdfTolerance( pdfToleranceParam ) + , pdfAdjacentPointTolerance( pdfAdjacentPointToleranceParam ) + , pdfPointToCircleRadius( pdfPointToCircleRadiusParam ) +{ + poLayerDefn->Reference(); + if (poSpatialReference) poSpatialReference->Reference(); +} + +/************************************************************************/ +/* ~OGRWAsPLayer() */ +/************************************************************************/ + +OGRWAsPLayer::~OGRWAsPLayer() + +{ + if ( bMerge ) + { + /* If polygon where used, we have to merge lines before output */ + /* lines must be merged if they have the same left/right values */ + /* and touch at end points. */ + /* Those lines appear when polygon with the same roughness touch */ + /* since the boundary between them is not wanted */ + /* We do it here since we are sure we have all polygons */ + /* We first detect touching lines, then the kind of touching, */ + /* candidates for merging are pairs of neighbors with corresponding */ + /* left/right values. Finally we merge */ + + typedef std::map< std::pair, std::vector > PointMap; + PointMap oMap; + for ( size_t i = 0; i < oBoundaries.size(); i++) + { + const Boundary & p = oBoundaries[i]; + OGRPoint startP, endP; + p.poLine->StartPoint( &startP ); + p.poLine->EndPoint( &endP ); + oMap[ std::make_pair(startP.getX(), startP.getY()) ].push_back( i ); + oMap[ std::make_pair(endP.getX(), endP.getY()) ].push_back( i ); + } + + std::vector endNeighbors( oBoundaries.size(), -1 ); + std::vector startNeighbors( oBoundaries.size(), -1 ); + for ( PointMap::const_iterator it = oMap.begin(); it != oMap.end(); it++ ) + { + if ( it->second.size() != 2 ) continue; + int i = it->second[0]; + int j = it->second[1]; + + const Boundary & p = oBoundaries[i]; + OGRPoint startP, endP; + p.poLine->StartPoint( &startP ); + p.poLine->EndPoint( &endP ); + const Boundary & q = oBoundaries[j]; + OGRPoint startQ, endQ; + q.poLine->StartPoint( &startQ ); + q.poLine->EndPoint( &endQ ); + if ( isEqual( p.dfRight, q.dfRight) && isEqual( p.dfLeft, q.dfLeft ) ) + { + if ( endP.Equals( &startQ ) ) + { + endNeighbors[i] = j; + startNeighbors[j] = i; + } + if ( endQ.Equals( &startP ) ) + { + endNeighbors[j] = i; + startNeighbors[i] = j; + } + } + if ( isEqual( p.dfRight, q.dfLeft) && isEqual( p.dfRight, q.dfLeft ) ) + { + if ( startP.Equals( &startQ ) ) + { + startNeighbors[i] = j; + startNeighbors[j] = i; + } + if ( endP.Equals( &endQ ) ) + { + endNeighbors[j] = i; + endNeighbors[i] = j; + } + } + } + + /* output all end lines (one neighbor only) and all their neighbors*/ + std::vector oHasBeenMerged( oBoundaries.size(), false); + for ( size_t i = 0; i < oBoundaries.size(); i++) + { + if ( !oHasBeenMerged[i] && ( startNeighbors[i] < 0 || endNeighbors[i] < 0 ) ) + { + oHasBeenMerged[i] = true; + Boundary * p = &oBoundaries[i]; + int j = startNeighbors[i] < 0 ? endNeighbors[i] : startNeighbors[i]; + if ( startNeighbors[i] >= 0 ) + { + /* reverse the line and left/right */ + p->poLine->reversePoints(); + std::swap( p->dfLeft, p->dfRight ); + } + while ( j >= 0 ) + { + assert( !oHasBeenMerged[j] ); + oHasBeenMerged[j] = true; + + OGRLineString * other = oBoundaries[j].poLine; + OGRPoint endP, startOther; + p->poLine->EndPoint( &endP ); + other->StartPoint( &startOther ); + if ( !endP.Equals( &startOther ) ) other->reversePoints(); + p->poLine->addSubLineString( other, 1 ); + + /* next neighbor */ + if ( endNeighbors[j] >= 0 && !oHasBeenMerged[endNeighbors[j]] ) + j = endNeighbors[j]; + else if ( startNeighbors[j] >= 0 && !oHasBeenMerged[startNeighbors[j]] ) + j = startNeighbors[j]; + else + j = -1; + } + WriteRoughness( p->poLine, p->dfLeft, p->dfRight ); + } + } + /* output all rings */ + for ( size_t i = 0; i < oBoundaries.size(); i++) + { + if ( oHasBeenMerged[i] ) continue; + oHasBeenMerged[i] = true; + Boundary * p = &oBoundaries[i]; + int j = startNeighbors[i] < 0 ? endNeighbors[i] : startNeighbors[i]; + assert( j != -1 ); + if ( startNeighbors[i] >= 0 ) + { + /* reverse the line and left/right */ + p->poLine->reversePoints(); + std::swap( p->dfLeft, p->dfRight ); + } + while ( !oHasBeenMerged[j] ) + { + oHasBeenMerged[j] = true; + + OGRLineString * other = oBoundaries[j].poLine; + OGRPoint endP, startOther; + p->poLine->EndPoint( &endP ); + other->StartPoint( &startOther ); + if ( !endP.Equals( &startOther ) ) other->reversePoints(); + p->poLine->addSubLineString( other, 1 ); + + /* next neighbor */ + if ( endNeighbors[j] >= 0 ) + j = endNeighbors[j]; + else if ( startNeighbors[j] >= 0 ) + j = startNeighbors[j]; + else + assert(false); /* there must be a neighbor since it's a ring */ + } + WriteRoughness( p->poLine, p->dfLeft, p->dfRight ); + } + } + else + { + for ( size_t i = 0; i < oBoundaries.size(); i++) + { + Boundary * p = &oBoundaries[i]; + WriteRoughness( p->poLine, p->dfLeft, p->dfRight ); + } + } + poLayerDefn->Release(); + if (poSpatialReference) poSpatialReference->Release(); + for ( size_t i=0; i( line.clone() ); + + std::auto_ptr< OGRLineString > poLine( + static_cast( + pdfTolerance.get() && *pdfTolerance > 0 + ? line.Simplify( *pdfTolerance ) + : line.clone() ) ); + + OGRPoint startPt, endPt; + poLine->StartPoint( &startPt ); + poLine->EndPoint( &endPt ); + const bool isRing = startPt.Equals( &endPt ); + + if ( pdfAdjacentPointTolerance.get() && *pdfAdjacentPointTolerance > 0) + { + /* remove consecutive points that are too close */ + std::auto_ptr< OGRLineString > newLine( new OGRLineString ); + const double dist = *pdfAdjacentPointTolerance; + OGRPoint pt; + poLine->StartPoint( &pt ); + newLine->addPoint( &pt ); + const int iNumPoints= poLine->getNumPoints(); + unsigned rem = 0; + for (int v=1; vgetX(v) - pt.getX()) > dist || + fabs(poLine->getY(v) - pt.getY()) > dist ) + { + poLine->getPoint( v, &pt ); + newLine->addPoint( &pt ); + } + else + { + ++rem; + } + } + + /* force closed loop if initially closed */ + if ( isRing ) + newLine->setPoint( newLine->getNumPoints() - 1, &startPt ); + + poLine.reset( newLine.release() ); + } + + if ( pdfPointToCircleRadius.get() && *pdfPointToCircleRadius > 0 ) + { + const double radius = *pdfPointToCircleRadius; + +#undef WASP_EXPERIMENTAL_CODE +#ifdef WASP_EXPERIMENTAL_CODE + if ( 3 == poLine->getNumPoints() && isRing ) + { + OGRPoint p0, p1; + poLine->getPoint( 0, &p0 ); + poLine->getPoint( 1, &p1 ); + const double dir[2] = { + p1.getX() - p0.getX(), + p1.getY() - p0.getY() }; + const double dirNrm = sqrt( dir[0]*dir[0] + dir[1]*dir[1] ); + if ( dirNrm > radius ) + { + /* convert to rectangle by finding the direction */ + /* and offsetting */ + const double ortho[2] = {-radius*dir[1]/dirNrm, radius*dir[0]/dirNrm}; + poLine->setNumPoints(5); + poLine->setPoint(0, p0.getX() - ortho[0], p0.getY() - ortho[1]); + poLine->setPoint(1, p1.getX() - ortho[0], p1.getY() - ortho[1]); + poLine->setPoint(2, p1.getX() + ortho[0], p1.getY() + ortho[1]); + poLine->setPoint(3, p0.getX() + ortho[0], p0.getY() + ortho[1]); + poLine->setPoint(4, p0.getX() - ortho[0], p0.getY() - ortho[1]); + } + else + { + /* reduce to a point to be dealt with just after*/ + poLine->setNumPoints(1); + poLine->setPoint(0, + 0.5*(p0.getX()+p1.getX()), + 0.5*(p0.getY()+p1.getY())); + } + } +#endif + + if ( 1 == poLine->getNumPoints() ) + { + const int nbPt = 8; + const double cx = poLine->getX(0); + const double cy = poLine->getY(0); + poLine->setNumPoints( nbPt + 1 ); + for ( int v = 0; v<=nbPt; v++ ) + { + /* the % is necessary to make sure the ring */ + /* is really closed and not open due to */ + /* roundoff error of cos(2pi) and sin(2pi) */ + poLine->setPoint(v, + cx + radius*cos((v%nbPt)*(2*M_PI/nbPt)), + cy + radius*sin((v%nbPt)*(2*M_PI/nbPt)) ); + } + } + + } + + return poLine.release(); +} + +/************************************************************************/ +/* WriteElevation() */ +/************************************************************************/ + +OGRErr OGRWAsPLayer::WriteElevation( OGRLineString * poGeom, const double & dfZ ) + +{ + std::auto_ptr< OGRLineString > poLine( Simplify( *poGeom ) ); + + const int iNumPoints = poLine->getNumPoints(); + if ( !iNumPoints ) return OGRERR_NONE; /* empty geom */ + + VSIFPrintfL( hFile, "%11.3f %11d", dfZ, iNumPoints ); + + for (int v=0; vgetX(v), poLine->getY(v) ); + } + VSIFPrintfL( hFile, "\n" ); + + return OGRERR_NONE; +} + +OGRErr OGRWAsPLayer::WriteElevation( OGRGeometry * poGeom, const double & dfZ ) + +{ + switch ( poGeom->getGeometryType() ) + { + case wkbLineString: + case wkbLineString25D: + return WriteElevation( static_cast(poGeom), dfZ ); + case wkbMultiLineString25D: + case wkbMultiLineString: + { + OGRGeometryCollection * collection = static_cast(poGeom); + for ( int i=0; igetNumGeometries(); i++ ) + { + const OGRErr err = WriteElevation( collection->getGeometryRef(i), dfZ ); + if ( OGRERR_NONE != err ) return err; + } + return OGRERR_NONE; + } + default: + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot handle geometry of type %s", + OGRGeometryTypeToName( poGeom->getGeometryType() ) ); + break; + } + } + return OGRERR_FAILURE; /* avoid visual warning */ +} + + +/************************************************************************/ +/* WriteRoughness() */ +/************************************************************************/ + + +OGRErr OGRWAsPLayer::WriteRoughness( OGRPolygon * poGeom, const double & dfZ ) + +{ + /* text intersection with polygons in the stack */ + /* for linestrings intersections, write linestring */ + /* for polygon intersection error */ + /* for point intersection do nothing */ + + OGRErr err = OGRERR_NONE; + OGREnvelope oEnvelope; + poGeom->getEnvelope( &oEnvelope ); + for ( size_t i=0; iIntersection( poGeom ); + if ( poIntersection ) + { + switch (poIntersection->getGeometryType()) + { + case wkbLineString: + case wkbLineString25D: + { + Boundary oB = {static_cast(poIntersection->clone()), dfZ, oZones[i].dfZ }; + oBoundaries.push_back( oB ); + } + break; + case wkbMultiLineString: + case wkbMultiLineString25D: + { + /*TODO join the multilinestring into linestring*/ + OGRGeometryCollection * collection = static_cast(poIntersection); + OGRLineString * oLine = NULL; + OGRPoint * oStart = new OGRPoint; + OGRPoint * oEnd = new OGRPoint; + for ( int j=0; jgetNumGeometries(); j++ ) + { + OGRLineString * poLine = static_cast(collection->getGeometryRef(j)); + assert(poLine); + poLine->StartPoint( oStart ); + + if ( !oLine || !oLine->getNumPoints() || oStart->Equals( oEnd ) ) + { + if (oLine) oLine->addSubLineString ( poLine, 1 ); + else oLine = static_cast( poLine->clone() ); + oLine->EndPoint( oEnd ); + } + else + { + Boundary oB = {oLine, dfZ, oZones[i].dfZ}; + oBoundaries.push_back( oB ); + oLine = static_cast( poLine->clone() ); + oLine->EndPoint( oEnd ); + } + } + Boundary oB = {oLine, dfZ, oZones[i].dfZ}; + oBoundaries.push_back( oB ); + delete oStart; + delete oEnd; + } + break; + case wkbPolygon: + case wkbPolygon25D: + { + OGREnvelope oErrorRegion = oZones[i].oEnvelope; + oErrorRegion.Intersect( oEnvelope ); + CPLError(CE_Failure, CPLE_NotSupported, + "Overlaping polygons in rectangle (%.16g %.16g, %.16g %.16g))", + oErrorRegion.MinX, + oErrorRegion.MinY, + oErrorRegion.MaxX, + oErrorRegion.MaxY ); + err = OGRERR_FAILURE; + } + break; + case wkbGeometryCollection: + case wkbGeometryCollection25D: + { + OGRGeometryCollection * collection = static_cast(poIntersection); + for ( int j=0; jgetNumGeometries(); j++ ) + { + const OGRwkbGeometryType eType = collection->getGeometryRef(j)->getGeometryType(); + if ( wkbFlatten(eType) == wkbPolygon ) + { + OGREnvelope oErrorRegion = oZones[i].oEnvelope; + oErrorRegion.Intersect( oEnvelope ); + CPLError(CE_Failure, CPLE_NotSupported, + "Overlaping polygons in rectangle (%.16g %.16g, %.16g %.16g))", + oErrorRegion.MinX, + oErrorRegion.MinY, + oErrorRegion.MaxX, + oErrorRegion.MaxY ); + err = OGRERR_FAILURE; + } + } + } + break; + case wkbPoint: + case wkbPoint25D: + /* do nothing */ + break; + default: + CPLError(CE_Failure, CPLE_NotSupported, + "Unhandled polygon intersection of type %s", + OGRGeometryTypeToName( poIntersection->getGeometryType() ) ); + err = OGRERR_FAILURE; + } + } + delete poIntersection; + } + } + + Zone oZ = { oEnvelope, static_cast(poGeom->clone()), dfZ }; + oZones.push_back( oZ ); + return err; +} + +OGRErr OGRWAsPLayer::WriteRoughness( OGRLineString * poGeom, const double & dfZleft, const double & dfZright ) + +{ + std::auto_ptr< OGRLineString > poLine( Simplify( *poGeom ) ); + + const int iNumPoints = poLine->getNumPoints(); + if ( !iNumPoints ) return OGRERR_NONE; /* empty geom */ + + VSIFPrintfL( hFile, "%11.3f %11.3f %11d", dfZleft, dfZright, iNumPoints ); + + for (int v=0; vgetX(v), poLine->getY(v) ); + } + VSIFPrintfL( hFile, "\n" ); + + return OGRERR_NONE; +} + +OGRErr OGRWAsPLayer::WriteRoughness( OGRGeometry * poGeom, const double & dfZleft, const double & dfZright ) + +{ + switch ( poGeom->getGeometryType() ) + { + case wkbLineString: + case wkbLineString25D: + return WriteRoughness( static_cast(poGeom), dfZleft, dfZright ); + case wkbPolygon: + case wkbPolygon25D: + return WriteRoughness( static_cast(poGeom), dfZleft ); + case wkbMultiPolygon: + case wkbMultiPolygon25D: + case wkbMultiLineString25D: + case wkbMultiLineString: + { + OGRGeometryCollection * collection = static_cast(poGeom); + for ( int i=0; igetNumGeometries(); i++ ) + { + const OGRErr err = WriteRoughness( collection->getGeometryRef(i), dfZleft, dfZright ); + if ( OGRERR_NONE != err ) return err; + } + return OGRERR_NONE; + } + default: + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot handle geometry of type %s", + OGRGeometryTypeToName( poGeom->getGeometryType() ) ); + break; + } + } + return OGRERR_FAILURE; /* avoid visual warning */ +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRWAsPLayer::ICreateFeature( OGRFeature * poFeature ) + +{ + if ( WRITE_ONLY != eMode) + { + CPLError(CE_Failure, CPLE_IllegalArg , "Layer is open read only" ); + return OGRERR_FAILURE; + } + + /* This mainly checks for errors or inconsistencies */ + /* the real work is done by WriteElevation or WriteRoughness */ + if ( -1 == iFirstFieldIdx && !sFirstField.empty() ) + { + CPLError(CE_Failure, CPLE_IllegalArg , "Cannot find field %s", sFirstField.c_str() ); + return OGRERR_FAILURE; + } + if ( -1 == iSecondFieldIdx && !sSecondField.empty() ) + { + CPLError(CE_Failure, CPLE_IllegalArg, "Cannot find field %s", sSecondField.c_str() ); + return OGRERR_FAILURE; + } + if ( -1 == iGeomFieldIdx && !sGeomField.empty() ) + { + CPLError(CE_Failure, CPLE_IllegalArg, "Cannot find field %s", sSecondField.c_str() ); + return OGRERR_FAILURE; + } + OGRGeometry * geom = poFeature->GetGeomFieldRef(iGeomFieldIdx); + if ( !geom ) return OGRERR_NONE; /* null geom, nothing to do */ + + const OGRwkbGeometryType geomType = geom->getGeometryType(); + const double bPolygon = (geomType == wkbPolygon) + || (geomType == wkbPolygon25D) + || (geomType == wkbMultiPolygon) + || (geomType == wkbMultiPolygon25D); + const bool bRoughness = (-1 != iSecondFieldIdx) || bPolygon ; + + + double z1; + if ( -1 != iFirstFieldIdx ) + { + if (!poFeature->IsFieldSet(iFirstFieldIdx)) + { + CPLError(CE_Failure, CPLE_NotSupported, "Field %d %s is NULL", iFirstFieldIdx, sFirstField.c_str() ); + return OGRERR_FAILURE; + } + z1 = poFeature->GetFieldAsDouble(iFirstFieldIdx); + } + else + { + /* Case of z value for elevation or roughness, so we compute it */ + OGRPoint centroid; + if ( geom->getCoordinateDimension() != 3 ) + { + + CPLError(CE_Failure, CPLE_NotSupported, "No field defined and no Z coordinate" ); + return OGRERR_FAILURE; + } + z1 = AvgZ( geom ); + } + + double z2; + if ( -1 != iSecondFieldIdx ) + { + if (!poFeature->IsFieldSet(iSecondFieldIdx)) + { + CPLError(CE_Failure, CPLE_NotSupported, "Field %d %s is NULL", iSecondFieldIdx, sSecondField.c_str() ); + return OGRERR_FAILURE; + } + z2 = poFeature->GetFieldAsDouble(iSecondFieldIdx); + } + else if ( bRoughness && !bPolygon ) + { + CPLError(CE_Failure, CPLE_IllegalArg, "No right roughness field" ); + return OGRERR_FAILURE; + } + + return bRoughness ? WriteRoughness( geom, z1, z2 ) : WriteElevation( geom, z1 ); +} + +/************************************************************************/ +/* CreateField() */ +/************************************************************************/ + +OGRErr OGRWAsPLayer::CreateField( OGRFieldDefn *poField, + CPL_UNUSED int bApproxOK ) +{ + poLayerDefn->AddFieldDefn( poField ); + + /* Update field indexes */ + if ( -1 == iFirstFieldIdx && ! sFirstField.empty() ) + iFirstFieldIdx = poLayerDefn->GetFieldIndex( sFirstField.c_str() ); + if ( -1 == iSecondFieldIdx && ! sSecondField.empty() ) + iSecondFieldIdx = poLayerDefn->GetFieldIndex( sSecondField.c_str() ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CreateGeomField() */ +/************************************************************************/ + +OGRErr OGRWAsPLayer::CreateGeomField( OGRGeomFieldDefn *poGeomFieldIn, + CPL_UNUSED int bApproxOK ) +{ + poLayerDefn->AddGeomFieldDefn( poGeomFieldIn, FALSE ); + + /* Update geom field index */ + if ( -1 == iGeomFieldIdx ) + { + iGeomFieldIdx = poLayerDefn->GetGeomFieldIndex( sGeomField.c_str() ); + } + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRWAsPLayer::GetNextFeature() +{ + if ( READ_ONLY != eMode) + { + CPLError(CE_Failure, CPLE_IllegalArg , "Layer is open write only" ); + return NULL; + } + + OGRFeature *poFeature; + + GetLayerDefn(); + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if((m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRWAsPLayer::GetNextRawFeature() + +{ + const char * pszLine = CPLReadLineL( hFile ); + if ( !pszLine ) return NULL; + + double dfValues[4]; + int iNumValues = 0; + { + std::istringstream iss(pszLine); + while ( iNumValues < 4 && (iss >> dfValues[iNumValues] ) ){ ++iNumValues ;} + + if ( iNumValues < 2 ) + { + if (iNumValues) CPLError(CE_Failure, CPLE_FileIO, "No enough values" ); + return NULL; + } + } + + if( poLayerDefn->GetFieldCount() != iNumValues-1 ) + { + CPLError(CE_Failure, CPLE_FileIO, "looking for %d values and found %d on line: %s", poLayerDefn->GetFieldCount(), iNumValues-1, pszLine ); + return NULL; + } + + std::auto_ptr< OGRFeature > poFeature( new OGRFeature( poLayerDefn ) ); + poFeature->SetFID( ++iFeatureCount ); + for ( int i=0; iSetField( i, dfValues[i] ); + + const int iNumValuesToRead = 2*dfValues[iNumValues-1]; + int iReadValues = 0; + std::vector values(iNumValuesToRead); + for ( pszLine = CPLReadLineL( hFile ); + pszLine; + pszLine = iNumValuesToRead > iReadValues ? CPLReadLineL( hFile ) : NULL ) + { + std::istringstream iss(pszLine); + while ( iNumValuesToRead > iReadValues && (iss >> values[iReadValues] ) ){++iReadValues;} + } + if ( iNumValuesToRead != iReadValues ) + { + CPLError(CE_Failure, CPLE_FileIO, "No enough values for linestring" ); + return NULL; + } + std::auto_ptr< OGRLineString > poLine( new OGRLineString ); + poLine->setCoordinateDimension(3); + poLine->assignSpatialReference( poSpatialReference ); + for ( int i=0; iaddPoint( values[i], values[i+1], 0 ); + } + poFeature->SetGeomFieldDirectly(0, poLine.release() ); + + return poFeature.release(); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRWAsPLayer::TestCapability( const char * pszCap ) + +{ + return ( WRITE_ONLY == eMode && + (EQUAL(pszCap,OLCSequentialWrite) || + EQUAL(pszCap,OLCCreateField) || + EQUAL(pszCap,OLCCreateGeomField) ) ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +void OGRWAsPLayer::ResetReading() +{ + iFeatureCount = 0; + VSIFSeekL( hFile, iOffsetFeatureBegin, SEEK_SET ); +} + + +/************************************************************************/ +/* AvgZ() */ +/************************************************************************/ + +double OGRWAsPLayer::AvgZ( OGRLineString * poGeom ) + +{ + const int iNumPoints = poGeom->getNumPoints(); + double sum = 0; + for (int v=0; vgetZ(v); + } + return iNumPoints ? sum/iNumPoints : 0; +} + +double OGRWAsPLayer::AvgZ( OGRPolygon * poGeom ) + +{ + return AvgZ( poGeom->getExteriorRing() ); +} + +double OGRWAsPLayer::AvgZ( OGRGeometryCollection * poGeom ) + +{ + return poGeom->getNumGeometries() ? AvgZ( poGeom->getGeometryRef(0) ) : 0; +} + +double OGRWAsPLayer::AvgZ( OGRGeometry * poGeom ) + +{ + switch ( poGeom->getGeometryType() ) + { + case wkbLineString: + case wkbLineString25D: + return AvgZ( static_cast< OGRLineString * >(poGeom) ); + case wkbPolygon: + case wkbPolygon25D: + return AvgZ( static_cast< OGRPolygon * >(poGeom) ); + case wkbMultiLineString: + case wkbMultiLineString25D: + + case wkbMultiPolygon: + case wkbMultiPolygon25D: + return AvgZ( static_cast< OGRGeometryCollection * >(poGeom) ); + default: + CPLError( CE_Warning, CPLE_NotSupported, "Unsuported geometry type in OGRWAsPLayer::AvgZ()"); + break; + } + return 0; /* avoid warning */ +} + + + +/************************************************************************/ +/* DouglasPeucker() */ +/************************************************************************/ + +//void DouglasPeucker(PointList[], epsilon) +// +//{ +// // Find the point with the maximum distance +// double dmax = 0; +// int index = 0; +// int end = length(PointList). +// for (int i = 1; i dmax ) +// { +// index = i +// dmax = d +// } +// } +// // If max distance is greater than epsilon, recursively simplify +// if ( dmax > epsilon ) +// { +// // Recursive call +// recResults1[] = DouglasPeucker(PointList[1...index], epsilon) +// recResults2[] = DouglasPeucker(PointList[index...end], epsilon) +// +// // Build the result list +// ResultList[] = {recResults1[1...end-1] recResults2[1...end]} +// } +// else +// { +// ResultList[] = {PointList[1], PointList[end]} +// } +// // Return the result +// return ResultList[] +//} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/GNUmakefile new file mode 100644 index 000000000..b19766f6d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrwfsdriver.o ogrwfsdatasource.o ogrwfslayer.o ogrwfsfilter.o ogrwfsjoinlayer.o + +CPPFLAGS := -I.. -I../.. -I../gml $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_wfs.h ../../swq.h ../gml/gmlreader.h ../gml/parsexsd.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/WFSServersList.txt b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/WFSServersList.txt new file mode 100644 index 000000000..274dd6969 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/WFSServersList.txt @@ -0,0 +1,98 @@ +DISCLAIMER: At the time when you'll read this, you can expect some dead links ! + +GeoServer: +http://sigma.openplans.org/geoserver/ows (dead link) +http://demo.opengeo.org/geoserver/wfs (WFS-T with write permissions) +http://giswebservices.massgis.state.ma.us/geoserver/wfs +http://gem.demo.opengeo.org/geoserver/wfs +http://geohub.jrc.ec.europa.eu/effis/ows +http://v2.suite.opengeo.org/geoserver/ows +http://data.wien.gv.at/daten/wfs # layers with identical names within 2 different namespaces +http://geoserv.weichand.de:8080/geoserver/wfs?VERSION=2.0.0 (WFS 2.0 capable, with paging) +http://briseide02.ingr.briseide.eu:8080/geoserver/briseide/ows?VERSION=2.0.0 (WFS 2.0 capable, with paging) +http://gis.facilities.ucsb.edu:8080/geoserver/wfs +http://kartenn.region-bretagne.fr/geoserver/wfs +http://agismap1.faa.gov/geoserver/ows (AIXM data) + +MapServer: +http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp (4.8.1) +http://www2.dmsolutions.ca/cgi-bin/mswfs_gmap +http://www.rijkswaterstaat.nl/services/geoservices/overzichtskaartnl +http://devgeo.cciw.ca/cgi-bin/mapserv/carts +http://map.ns.ec.gc.ca/MapServer/mapserv.exe?map=/mapserver/services/envdat/config.map (4.8.3) +http://nsidc.org/cgi-bin/acap.pl +http://sdmdataaccess.nrcs.usda.gov/Spatial/SDMNAD83Geographic.wfs +http://demo.mapserver.org/cgi-bin/wfs? +http://hip.latuviitta.org/cgi-bin/mapserver_wfs +http://geolittoral.application.equipement.gouv.fr/wfs/metropole?VERSION=1.0.0 (5.6) + +Deegree: +http://www.nokis.org/deegree2/ogcwebservice (not responding, requires namespace to be passed) +http://www.idee.es/IDEE-WFS/ogcwebservice (responding, requires namespace to be passed) +http://demo.deegree.org/deegree-wfs/services +http://testing.deegree.org/deegree-wfs/services (WFS-T, but permission denied) +http://lkee-xplanung2.lat-lon.de/xplan-wfs/services (with complex schema that we cannot understand with the XSD parser) +http://deegree3-testing.deegree.org/deegree-geosciml-demo/services (offline currently) +http://deegree3-demo.deegree.org:80/inspire-workspace/services (GML 3.2.1 output only and WFS 2.0 capable) +http://deegree3-demo.deegree.org:80/utah-workspace/services (WFS 2.0 capable) +http://inspire.kademo.nl/deegree-wfs/services +http://www.iai.fzk.de/wfs (Deegree 2) + +TinyOWS: +http://dev4.mapgears.com/cgi-bin/tinyows (old & buggy version) +http://www.tinyows.org/cgi-bin/tinyows (WFS-T with write permissions) +http://gis-lab.info/scripts/tinyows/tinyows.cgi +http://hip.latuviitta.org/cgi-bin/tinyows + +CubeWerx: +http://ceoware2.ccrs.nrcan.gc.ca/cubewerx/cwwfs/cubeserv.cgi?datastore=CEOWARE2 +http://cgns.nrcan.gc.ca/wfs/cubeserv.cgi?DATASTORE=cgns +http://portal.cubewerx.com/cubewerx/cubeserv.cgi?DATASTORE=Foundation +http://portal.cubewerx.com/cubewerx/cubeserv.cgi?CONFIG=haiti_vgi&DATASTORE=vgi (offline: was WFS-T with write permissions) + +Intergraph: +http://regis.intergraph.com/wfs/dcmetro/request.asp +http://geonames.nga.mil/nameswfs/request.aspx (fails often) +http://ideg.xunta.es/WFS_POL/request.aspx + +ESRI: +http://map.ngdc.noaa.gov/wfsconnector/com.esri.wfs.Esrimap/Sample_Index_f (no layers published) +http://map.ngdc.noaa.gov/wfsconnector/com.esri.wfs.Esrimap/dart_atlantic_f (works) +http://siga.cna.gob.mx/wfsconnector/com.esri.wfs.Esrimap/Acuiferos_Features (encoding reported as UTF-8, but ISO-8859-1 probably) +http://sentinel.ga.gov.au/wfsconnector/com.esri.wfs.Esrimap (works) +http://sima.gencat.cat/wfsconnector/com.esri.wfs.Esrimap/SIMA_WFS (returns invalid XML in GetFeature) +http://egisws01.nos.noaa.gov/wfsconnector/com.esri.wfs.Esrimap/nosdataexplorer_wfs (Mark invalid error in GetFeature) +http://services.nationalmap.gov/ArcGIS/services/geonames/MapServer/WFSServer +http://129.116.104.176/ArcGIS/services/Climate/CFSRMonthly/MapServer/WFSServer (offline) + +Microimages ?: +http://publicatlas.microimages.com/tntgateway/tntgateway.cgi + +Ionic: +http://webservices.ionicsoft.com/ionicweb/wfs/BOSTON_ORA (1.0.0 WFS-T) +http://webservices.ionicsoft.com/gazetteer/wfs/GNS_GAZ +http://demo.ionicsoft.com/agiRSW/wfs/PYLONS + +Safe Software: +http://142.176.62.103:8194/servlet/WFS_NS_NAMES (offline) + +ERDAS Apollo: +http://apollo.erdas.com/erdas-apollo/vector/Cherokee + +SnowFlake software: +http://demo.snowflakesoftware.com:8080/AIXM51FINAL_DONLON/GOPublisherWFS + +Mapinfo: +http://www.mapinfo.com/miwfs + +??? +http://services.interactive-instruments.de/xtra/cgi-bin/wfs?Request=GetCapabilities (doesn't work well) + +??? (WFS 2.0) +http://services.cuzk.cz/wfs/inspire-cp-wfs.asp? + +Catalog with a lot of dead links, but a few of the above actually come from here : +http://svn.geotools.org/branches/xdo-branch/plugin/wms/test/org/geotools/data/wms/test/test-data/servers.txt + +Other catalog with a lot of alive links :-) : +http://ows-search-engine.appspot.com/index?st=WFS diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/drv_wfs.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/drv_wfs.html new file mode 100644 index 000000000..d9d206f21 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/drv_wfs.html @@ -0,0 +1,279 @@ + + +WFS - OGC WFS service + + + + +

      WFS - OGC WFS service

      + +(GDAL/OGR >= 1.8.0)

      + +This driver can connect to a OGC WFS service. It supports WFS 1.0, 1.1 and 2.0 protocols. GDAL/OGR must be built with Curl support in order to the +WFS driver to be compiled. Usually WFS requests return results in GML format, so the GML driver should generally be set-up for read support +(thus requiring GDAL/OGR to be built with Xerces or Expat support). It is sometimes possible to use alternate underlying formats when the server supports them +(such as OUTPUTFORMAT=json).

      + +The driver supports read-only services, as well as Transactionnal ones (WFS-T).

      + +

      Dataset name syntax

      + +The minimal syntax to open a WFS datasource is : WFS:http://path/to/WFS/service or http://path/to/WFS/service?SERVICE=WFS

      + +Additionnal optional parameters can be specified such as TYPENAME, VERSION, MAXFEATURES as specified in WFS specification.

      + +The name provided to the TYPENAME parameter must be exactly the layer name reported by OGR, +in particular with its namespace prefix when its exists. Note: starting with GDAL 1.10, several type names can be provided and separated by comma.

      + +It is also possible to specify the name of an XML file whose content matches the following syntax +(the <OGRWFSDataSource> element must be the first bytes of the file): +

      +<OGRWFSDataSource>
      +    <URL>http://path/to/WFS/service[?OPTIONAL_PARAMETER1=VALUE[&amp;OPTIONNAL_PARAMETER2=VALUE]]</URL>
      +</OGRWFSDataSource>
      +

      + +Note: the URL must be XML-escaped, for example the & character must be written as &amp;

      + +At the first opening, the content of the result of the GetCapabilities request will be appended to the file, so that it can be cached +for later openings of the dataset. The same applies for the DescribeFeatureType request issued to discover the field definition of each layer.

      + +The service description file has the following additional elements as +immediate children of the OGRWFSDataSource element that may be optionally +set.

      + +

        +
      • Timeout: The timeout to use for remote service requests. If not provided, the libcurl default is used. +
      • UserPwd: May be supplied with userid:password to pass a +userid and password to the remote server. +
      • HttpAuth: May be BASIC, NTLM or ANY to control the authentication scheme to be used. +
      • Version: Set a specific WFS version to use (either 1.0.0 or 1.1.0). +
      • PagingAllowed: Set to ON if paging must be enabled. See "Request paging" section. +
      • PageSize: Page size when paging is enabled. See "Request paging" section. +
      • BaseStartIndex: (OGR >= 1.10) Base start index when paging is enabled. See "Request paging" section. +
      • COOKIE: HTTP cookies that are passed in HTTP requests, formatted as COOKIE1=VALUE1; COOKIE2=VALUE2 +
      + +

      Request paging

      + +Before OGR 1.10, when reading the first feature from a layer, the whole layer content will be fetched from the server.

      + +Some servers (such as MapServer >= 6.0) support a vendor specific option, STARTINDEX, that allow to do the requests per "page", +and thus to avoid downloading the whole content of the layer in a single request. The OGR WFS client will use paging when the OGR_WFS_PAGING_ALLOWED +configuration option is set to ON. The page size (number of features fetched in a single request) is limited to 100 by default. +It can be changed by setting the OGR_WFS_PAGE_SIZE configuration option.
      +WFS 1.1 specification has clarified that the first feature in paging is at index 0. But some server implementations of WFS 1.1 +have considered that it was at index 1 (including MapServer <= 6.2). Starting with OGR 1.10, the default base start index is 0, +as mandated by the specification. The OGR_WFS_BASE_START_INDEX configuration option can however be set to 1 to be compatible +with the server implementations that considered the first feature to be at index 1.
      +Those 3 options (OGR_WFS_PAGING_ALLOWED, OGR_WFS_PAGE_SIZE, OGR_WFS_BASE_START_INDEX) can also be set in a WFS XML description +file with the elements of similar names (PagingAllowed, PageSize, BaseStartIndex).

      + +Starting with OGR 1.10, the WFS driver will read the GML content as a stream instead as a whole file, which will improve +interactivity and help when the content cannot fit into memory. This can be turned off by setting the OGR_WFS_USE_STREAMING +configuration option to NO if this is not desirable (for example, when iterating several times on a layer that can fit into memory). +When streaming is enabled, GZip compression is also requested. It has been observed that some WFS servers, that cannot do on-the-fly +compression, will cache on their side the whole content to be sent before sending the first bytes on the wire. To avoid this, +you can set the CPL_CURL_GZIP configuration option to NO.

      + +Starting with GDAL 2.0, the WFS driver will automatically detect if server supports +paging, when requesting a WFS 2.0 server.

      + +

      Filtering

      + +The driver will forward any spatial filter set with SetSpatialFilter() to the server. It also makes its best effort to do the same for attribute +filters set with SetAttributeFilter() when possible (turning OGR SQL language into OGC filter description). When this is not possible, +it will default to client-side only filtering, which can be a slow operation because involving fetching all the features from the servers.

      + +Starting with GDAL 2.0, the following spatial functions can be used : +

        +
      • the 8 spatial binary predicate: ST_Equals, ST_Disjoint, ST_Touches, +ST_Contains, ST_Intersects, ST_Within, ST_Crosses and ST_Overlaps that take +2 geometry arguments. Typically the geometry column name, and a constant geometry such +as built with ST_MakeEnvelope or ST_GeomFromText.
      • +
      • ST_DWithin(geom1,geom2,distance_in_meters)
      • +
      • ST_Beyond(geom1,geom2,distance_in_meters)
      • +
      • ST_MakeEnvelope(xmin,ymin,xmax,ymax[,srs]): to build an envelope. srs +can be an integer (an EPSG code), or a string directly set as the srsName attribute +of the gml:Envelope. GDAL will take care of needed axis swapping, so coordinates +should be expressed in the "natural GIS order" (for example long,lat for geodetic +systems)
      • +
      • ST_GeomFromText(wkt,[srs]): to build a geometry from its WKT representation.
      • +
      +Note that those spatial functions are only supported as server-side filters.

      + +

      Layer joins

      + +

      +Starting with GDAL 2.0, and for WFS 2.0 servers that support joins, SELECT +statements that involve joins will be run on server side. Spatial joins can +also be done by using the above mentionned spatial functions, if the server +supports spatial joins.

      + +

      There might be restrictions set by server on the complexity of the joins. +The OGR WFS driver also restricts column selection to be column names, potentially +with aliases and type casts, but not expressions. The ON and WHERE clauses must +also be evaluated on server side, so no OGR special fields are allowed for example. +ORDER BY clauses are supported, but the fields must belong to the primary table.

      + +

      +Example of valid statement : +

      +SELECT t1.id, t1.val1, t1.geom, t2.val1 FROM my_table AS t1 JOIN another_table AS t2 ON t1.id = t2.t1id
      +
      +or +
      +SELECT * FROM my_table AS t1 JOIN another_table AS t2 ON ST_Intersects(t1.geom, t2.geom)
      +
      +

      + +

      Write support / WFS-T

      + +The WFS-T protocol only eanbles the user to operate at feature level. No datasource, layer or field creations are possible.

      + +Write support is only enabled when the datasource is opened in update mode.

      + +The mapping between the operations of the WFS Transaction service and the OGR concepts is the following : +

        +
      • OGRFeature::CreateFeature() <==> WFS insert operation
      • +
      • OGRFeature::SetFeature() <==> WFS update operation
      • +
      • OGRFeature::DeleteFeature() <==> WFS delete operation
      • +
      + +Lock operations (LockFeature service) are not available at that time.

      + +There are a few caveouts to keep in mind. OGR feature ID (FID) is an integer based value, whereas WFS/GML +gml:id attribute is a string. Thus it is not always possible to match both values. The WFS driver exposes +then the gml:id attribute of a feature as a 'gml_id' field.

      + +When inserting a new feature with CreateFeature(), and if the command is successful, OGR will fetch the +returned gml:id and set the 'gml_id' field of the feature accordingly. It will also try to set the OGR FID +if the gml:id is of the form layer_name.numeric_value. Otherwise the FID will be left to its unset default value.

      + +When updating an existing feature with SetFeature(), the OGR FID field will be ignored. The request issued +to the driver will only take into account the value of the gml:id field of the feature. The same applies for +DeleteFeature().

      + +

      Write support and OGR transactions

      + +The above operations are by default issued to the server synchronously with the OGR API call. This however +can cause performance penalties when issuing a lot of commands due to many client/server exchanges.

      + +It is possible to surround those operations between OGRLayer::StartTransaction() and OGRLayer::CommitTransaction(). +The operations will be stored into memory and only executed at the time CommitTransaction() is called.

      + +The drawback for CreateFeature() is that the user cannot know which gml:id have been assigned to the inserted +features. A special SQL statement has been introduced into the WFS driver to workaround this : by issuing +the "SELECT _LAST_INSERTED_FIDS_ FROM layer_name" (where layer_name is to be replaced with the actual layer_name) +command through the OGRDataSource::ExecuteSQL(), a layer will be returned with as many rows +with a single attribue gml_id as the count of inserted features during the last commited transaction.

      + +Note : currently, only CreateFeature() makes use of OGR transaction mechanism. SetFeature() and DeleteFeature() +will still be issued immediately. + +

      Special SQL commands

      + +The following SQL / pseudo-SQL commands passed to OGRDataSource::ExecuteSQL() are specific of the WFS driver :

      + +

        + +
      • "DELETE FROM layer_name WHERE expression" : this will result into +a WFS delete operation. This can be a fast way of deleting one or several features. In particularly, this +can be a faster replacement for OGRLayer::DeleteFeature() when the gml:id is known, but the feature has not +been fetched from the server.

      • + +
      • "SELECT _LAST_INSERTED_FIDS_ FROM layer_name" : see above paragraph.

      • + +
      + +Currently, any other SQL command will be processed by the generic layer, meaning client-side only processing. +Server side spatial and attribute filtering must be done through the SetSpatialFilter() and SetAttributeFilter() +interfaces.

      + +

      Special layer : WFSLayerMetadata

      + +

      (OGR >= 1.9.0)

      + +

      A "hidden" layer called "WFSLayerMetadata" is filled with records with metadata for each WFS layer.

      + +

      Each record contains a "layer_name", "title" and "abstract" field, from the document returned by GetCapabilities.

      + +

      That layer is returned through GetLayerByName("WFSLayerMetadata").

      + +

      Special layer : WFSGetCapabilities

      + +

      (OGR >= 1.9.0)

      + +

      A "hidden" layer called "WFSGetCapabilities" is filled with the raw XML result of the GetCapabilities request.

      + +

      That layer is returned through GetLayerByName("WFSGetCapabilities").

      + +

      Open options (GDAL >= 2.0)

      + +The following options are available: +
        +
      • URL=url: URL to the WFS server endpoint. Required when using the +"WFS:" string as the connection string.
      • +
      • TRUST_CAPABILITIES_BOUNDS=YES/NO: Whether to trust layer bounds +declared in GetCapabilities response, for faster GetExtent() runtime. Defaults to NO
      • +
      • EMPTY_AS_NULL=YES/NO: (GDAL >=2.0) By default (EMPTY_AS_NULL=YES), +fields with empty content will be reported as being NULL, instead of being an empty +string. This is the historic behaviour. However this will prevent such fields to +be declared as not-nullable if the application schema declared them as mandatory. +So this option can be set to NO to have both empty strings being report as such, +and mandatory fields being reported as not nullable.
      • +
      • INVERT_AXIS_ORDER_IF_LAT_LONG=YES/NO: (GDAL >=2.0) Whether to +present SRS and coordinate ordering in traditional GIS order. Defaults to YES.
      • +
      • CONSIDER_EPSG_AS_URN=YES/NO/AUTO: (GDAL >=2.0) Whether to +consider srsName like EPSG:XXXX as respecting EPSG axis order. Defaults to AUTO.
      • +
      • EXPOSE_GML_ID=YES/NO: (GDAL >=2.0) Whether to expose the gml:id +attribute of a GML feature as the gml_id OGR field. Note that hiding gml_id will prevent +WFS-T from working. Defaults to YES.
      • +
      + +

      Examples

      + +
    • +Listing the types of a WFS server : +
      +ogrinfo -ro WFS:http://www2.dmsolutions.ca/cgi-bin/mswfs_gmap
      +
      +

      + +

    • +Listing the types of a WFS server whose layer structures are cached in a XML file: +
      +ogrinfo -ro mswfs_gmap.xml
      +
      +

      + +

    • +Listing the features of the popplace layer, with a spatial filter : +
      +ogrinfo -ro WFS:http://www2.dmsolutions.ca/cgi-bin/mswfs_gmap popplace -spat 0 0 2961766.250000 3798856.750000
      +
      + +
    • +Retrieving the features of gml:id "world.2" and "world.3" from the tows:world layer : +
      +ogrinfo "WFS:http://www.tinyows.org/cgi-bin/tinyows" tows:world -ro -al -where "gml_id='world.2' or gml_id='world.3'"
      +
      + +
    • +Display layer metadata (OGR >= 1.9.0): +
      +ogrinfo -ro -al "WFS:http://v2.suite.opengeo.org/geoserver/ows" WFSLayerMetadata
      +
      + +

      + + +

      See Also

      + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/makefile.vc new file mode 100644 index 000000000..56872f5a5 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrwfsdriver.obj ogrwfsdatasource.obj ogrwfslayer.obj ogrwfsfilter.obj ogrwfsjoinlayer.obj +EXTRAFLAGS = -I.. -I..\.. -I..\gml + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogr_wfs.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogr_wfs.h new file mode 100644 index 000000000..803007238 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogr_wfs.h @@ -0,0 +1,404 @@ +/****************************************************************************** + * $Id: ogr_wfs.h 29273 2015-06-02 08:08:38Z rouault $ + * + * Project: WFS Translator + * Purpose: Definition of classes for OGR WFS driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_WFS_H_INCLUDED +#define _OGR_WFS_H_INCLUDED + +#include +#include +#include + +#include "cpl_minixml.h" +#include "ogrsf_frmts.h" +#include "gmlreader.h" +#include "cpl_http.h" +#include "swq.h" + +CPLXMLNode* WFSFindNode(CPLXMLNode* psXML, const char* pszRootName); +void OGRWFSRecursiveUnlink( const char *pszName ); +CPLString WFS_TurnSQLFilterToOGCFilter( const swq_expr_node* poExpr, + OGRDataSource* poDS, + OGRFeatureDefn* poFDefn, + int nVersion, + int bPropertyIsNotEqualToSupported, + int bUseFeatureId, + int bGmlObjectIdNeedsGMLPrefix, + const char* pszNSPrefix, + int* pbOutNeedsNullCheck); +swq_custom_func_registrar* WFSGetCustomFuncRegistrar(); + +const char* FindSubStringInsensitive(const char* pszStr, + const char* pszSubStr); + +CPLString WFS_EscapeURL(const char* pszURL); +CPLString WFS_DecodeURL(const CPLString &osSrc); + +class OGRWFSSortDesc +{ + public: + CPLString osColumn; + int bAsc; + + OGRWFSSortDesc(const CPLString& osColumn, int bAsc) : osColumn(osColumn), bAsc(bAsc) {} + OGRWFSSortDesc(const OGRWFSSortDesc& other) : osColumn(other.osColumn), bAsc(other.bAsc) {} +}; + +/************************************************************************/ +/* OGRWFSLayer */ +/************************************************************************/ + +class OGRWFSDataSource; + +class OGRWFSLayer : public OGRLayer +{ + OGRWFSDataSource* poDS; + + OGRFeatureDefn* poFeatureDefn; + int bGotApproximateLayerDefn; + GMLFeatureClass* poGMLFeatureClass; + + int bAxisOrderAlreadyInverted; + OGRSpatialReference *poSRS; + + char* pszBaseURL; + char* pszName; + char* pszNS; + char* pszNSVal; + + int bStreamingDS; + GDALDataset *poBaseDS; + OGRLayer *poBaseLayer; + int bHasFetched; + int bReloadNeeded; + + CPLString osGeometryColumnName; + OGRwkbGeometryType eGeomType; + GIntBig nFeatures; + int bCountFeaturesInGetNextFeature; + + int CanRunGetFeatureCountAndGetExtentTogether(); + + CPLString MakeGetFeatureURL(int nMaxFeatures, int bRequestHits); + int MustRetryIfNonCompliantServer(const char* pszServerAnswer); + GDALDataset* FetchGetFeature(int nMaxFeatures); + OGRFeatureDefn* DescribeFeatureType(); + GIntBig ExecuteGetFeatureResultTypeHits(); + + double dfMinX, dfMinY, dfMaxX, dfMaxY; + int bHasExtents; + + OGRGeometry *poFetchedFilterGeom; + + CPLString osSQLWhere; + CPLString osWFSWhere; + + CPLString osTargetNamespace; + CPLString GetDescribeFeatureTypeURL(int bWithNS); + + int nExpectedInserts; + CPLString osGlobalInsert; + std::vector aosFIDList; + + int bInTransaction; + + CPLString GetPostHeader(); + + int bUseFeatureIdAtLayerLevel; + + int bPagingActive; + int nPagingStartIndex; + int nFeatureRead; + int nFeatureCountRequested; + + OGRFeatureDefn* BuildLayerDefnFromFeatureClass(GMLFeatureClass* poClass); + + char *pszRequiredOutputFormat; + + std::vector aoSortColumns; + + public: + OGRWFSLayer(OGRWFSDataSource* poDS, + OGRSpatialReference* poSRS, + int bAxisOrderAlreadyInverted, + const char* pszBaseURL, + const char* pszName, + const char* pszNS, + const char* pszNSVal); + + ~OGRWFSLayer(); + + OGRWFSLayer* Clone(); + + + const char *GetName() { return pszName; } + + virtual void ResetReading(); + virtual OGRFeature* GetNextFeature(); + virtual OGRFeature* GetFeature(GIntBig nFID); + + virtual OGRFeatureDefn * GetLayerDefn(); + + virtual int TestCapability( const char * ); + + virtual void SetSpatialFilter( OGRGeometry * ); + + virtual OGRErr SetAttributeFilter( const char * ); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + + void SetExtents(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY); + virtual OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE); + + virtual OGRErr ICreateFeature( OGRFeature *poFeature ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + virtual OGRErr StartTransaction(); + virtual OGRErr CommitTransaction(); + virtual OGRErr RollbackTransaction(); + + int HasLayerDefn() { return poFeatureDefn != NULL; } + + OGRFeatureDefn* ParseSchema(CPLXMLNode* psSchema); + OGRFeatureDefn* BuildLayerDefn(OGRFeatureDefn* poSrcFDefn = NULL); + + OGRErr DeleteFromFilter( CPLString osOGCFilter ); + + const std::vector& GetLastInsertedFIDList() { return aosFIDList; } + + const char *GetShortName(); + + void SetRequiredOutputFormat(const char* pszRequiredOutputFormatIn); + + const char *GetRequiredOutputFormat() { return pszRequiredOutputFormat; }; + + void SetOrderBy(const std::vector& aoSortColumnsIn); + int HasGotApproximateLayerDefn() { GetLayerDefn(); return bGotApproximateLayerDefn; } + + const char* GetNamespacePrefix() { return pszNS; } + const char* GetNamespaceName() { return pszNSVal; } +}; + +/************************************************************************/ +/* OGRWFSJoinLayer */ +/************************************************************************/ + +class OGRWFSJoinLayer : public OGRLayer +{ + OGRWFSDataSource *poDS; + OGRFeatureDefn *poFeatureDefn; + + CPLString osGlobalFilter; + CPLString osSortBy; + int bDistinct; + std::set aoSetMD5; + + std::vector apoLayers; + + GDALDataset *poBaseDS; + OGRLayer *poBaseLayer; + int bReloadNeeded; + int bHasFetched; + + int bPagingActive; + int nPagingStartIndex; + int nFeatureRead; + int nFeatureCountRequested; + + std::vector aoSrcFieldNames, aoSrcGeomFieldNames; + + CPLString osFeatureTypes; + + OGRWFSJoinLayer(OGRWFSDataSource* poDS, + const swq_select* psSelectInfo, + const CPLString& osGlobalFilter); + CPLString MakeGetFeatureURL(int bRequestHits = FALSE); + GDALDataset* FetchGetFeature(); + GIntBig ExecuteGetFeatureResultTypeHits(); + + public: + + static OGRWFSJoinLayer* Build(OGRWFSDataSource* poDS, + const swq_select* psSelectInfo); + ~OGRWFSJoinLayer(); + + virtual void ResetReading(); + virtual OGRFeature* GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn(); + + virtual int TestCapability( const char * ); + + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + + virtual void SetSpatialFilter( OGRGeometry * ); + + virtual OGRErr SetAttributeFilter( const char * ); +}; + +/************************************************************************/ +/* OGRWFSDataSource */ +/************************************************************************/ + +class OGRWFSDataSource : public OGRDataSource +{ + char* pszName; + int bRewriteFile; + CPLXMLNode* psFileXML; + + OGRWFSLayer** papoLayers; + int nLayers; + std::map oMap; + + int bUpdate; + + int bGetFeatureSupportHits; + CPLString osVersion; + int bNeedNAMESPACE; + int bHasMinOperators; + int bHasNullCheck; + int bPropertyIsNotEqualToSupported; + int bUseFeatureId; + int bGmlObjectIdNeedsGMLPrefix; + int bRequiresEnvelopeSpatialFilter; + int DetectRequiresEnvelopeSpatialFilter(CPLXMLNode* psRoot); + + int bTransactionSupport; + char** papszIdGenMethods; + int DetectTransactionSupport(CPLXMLNode* psRoot); + + CPLString osBaseURL; + CPLString osPostTransactionURL; + + CPLXMLNode* LoadFromFile( const char * pszFilename ); + + int bUseHttp10; + + char** papszHttpOptions; + + int bPagingAllowed; + int nPageSize; + int nBaseStartIndex; + int DetectSupportPagingWFS2(CPLXMLNode* psRoot); + + int bStandardJoinsWFS2; + int DetectSupportStandardJoinsWFS2(CPLXMLNode* psRoot); + + int bLoadMultipleLayerDefn; + std::set aoSetAlreadyTriedLayers; + + CPLString osLayerMetadataCSV; + CPLString osLayerMetadataTmpFileName; + OGRDataSource *poLayerMetadataDS; + OGRLayer *poLayerMetadataLayer; + + CPLString osGetCapabilities; + const char *apszGetCapabilities[2]; + GDALDataset *poLayerGetCapabilitiesDS; + OGRLayer *poLayerGetCapabilitiesLayer; + + int bKeepLayerNamePrefix; + + int bEmptyAsNull; + + int bInvertAxisOrderIfLatLong; + CPLString osConsiderEPSGAsURN; + int bExposeGMLId; + + CPLHTTPResult* SendGetCapabilities(const char* pszBaseURL, + CPLString& osTypeName); + + int GetLayerIndex(const char* pszName); + + public: + OGRWFSDataSource(); + ~OGRWFSDataSource(); + + int Open( const char * pszFilename, + int bUpdate, + char** papszOpenOptions ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + virtual OGRLayer* GetLayerByName(const char* pszLayerName); + + virtual int TestCapability( const char * ); + + virtual OGRLayer * ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ); + virtual void ReleaseResultSet( OGRLayer * poResultsSet ); + + int UpdateMode() { return bUpdate; } + int SupportTransactions() { return bTransactionSupport; } + void DisableSupportHits() { bGetFeatureSupportHits = FALSE; } + int GetFeatureSupportHits() { return bGetFeatureSupportHits; } + const char *GetVersion() { return osVersion.c_str(); } + + int IsOldDeegree(const char* pszErrorString); + int GetNeedNAMESPACE() { return bNeedNAMESPACE; } + int HasMinOperators() { return bHasMinOperators; } + int HasNullCheck() { return bHasNullCheck; } + int UseFeatureId() { return bUseFeatureId; } + int RequiresEnvelopeSpatialFilter() { return bRequiresEnvelopeSpatialFilter; } + void SetGmlObjectIdNeedsGMLPrefix() { bGmlObjectIdNeedsGMLPrefix = TRUE; } + int DoesGmlObjectIdNeedGMLPrefix() { return bGmlObjectIdNeedsGMLPrefix; } + + void SetPropertyIsNotEqualToUnSupported() { bPropertyIsNotEqualToSupported = FALSE; } + int PropertyIsNotEqualToSupported() { return bPropertyIsNotEqualToSupported; } + + CPLString GetPostTransactionURL(); + + void SaveLayerSchema(const char* pszLayerName, CPLXMLNode* psSchema); + + CPLHTTPResult* HTTPFetch( const char* pszURL, char** papszOptions ); + + int IsPagingAllowed() const { return bPagingAllowed; } + int GetPageSize() const { return nPageSize; } + int GetBaseStartIndex() const { return nBaseStartIndex; } + + void LoadMultipleLayerDefn(const char* pszLayerName, + char* pszNS, char* pszNSVal); + + int GetKeepLayerNamePrefix() { return bKeepLayerNamePrefix; } + const CPLString& GetBaseURL() { return osBaseURL; } + + int IsEmptyAsNull() const { return bEmptyAsNull; } + int InvertAxisOrderIfLatLong() const { return bInvertAxisOrderIfLatLong; } + const CPLString& GetConsiderEPSGAsURN() const { return osConsiderEPSGAsURN; } + + int ExposeGMLId() const { return bExposeGMLId; } + + virtual char** GetMetadataDomainList(); + virtual char** GetMetadata( const char * pszDomain = "" ); +}; + +#endif /* ndef _OGR_WFS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfsdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfsdatasource.cpp new file mode 100644 index 000000000..4fb5c9700 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfsdatasource.cpp @@ -0,0 +1,2370 @@ +/****************************************************************************** + * $Id: ogrwfsdatasource.cpp 29273 2015-06-02 08:08:38Z rouault $ + * + * Project: WFS Translator + * Purpose: Implements OGRWFSDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_port.h" +#include "ogr_wfs.h" +#include "ogr_api.h" +#include "cpl_minixml.h" +#include "cpl_http.h" +#include "gmlutils.h" +#include "parsexsd.h" +#include "swq.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrwfsdatasource.cpp 29273 2015-06-02 08:08:38Z rouault $"); + +#define DEFAULT_BASE_START_INDEX 0 +#define DEFAULT_PAGE_SIZE 100 + +typedef struct +{ + const char* pszPath; + const char* pszMDI; +} MetadataItem; + +static const MetadataItem asMetadata[] = +{ + { "Service.Title", "TITLE" }, /*1.0 */ + { "ServiceIdentification.Title", "TITLE" }, /* 1.1 or 2.0 */ + { "Service.Abstract", "ABSTRACT" }, /* 1.0 */ + { "ServiceIdentification.Abstract", "ABSTRACT" }, /* 1.1 or 2.0 */ + { "ServiceProvider.ProviderName", "PROVIDER_NAME" }, /* 1.1 or 2.0 */ +}; + +/************************************************************************/ +/* WFSFindNode() */ +/************************************************************************/ + +CPLXMLNode* WFSFindNode(CPLXMLNode* psXML, const char* pszRootName) +{ + CPLXMLNode* psIter = psXML; + while(psIter) + { + if (psIter->eType == CXT_Element) + { + const char* pszNodeName = psIter->pszValue; + const char* pszSep = strchr(pszNodeName, ':'); + if (pszSep) + pszNodeName = pszSep + 1; + if (EQUAL(pszNodeName, pszRootName)) + { + return psIter; + } + } + psIter = psIter->psNext; + } + + psIter = psXML->psChild; + while(psIter) + { + if (psIter->eType == CXT_Element) + { + const char* pszNodeName = psIter->pszValue; + const char* pszSep = strchr(pszNodeName, ':'); + if (pszSep) + pszNodeName = pszSep + 1; + if (EQUAL(pszNodeName, pszRootName)) + { + return psIter; + } + } + psIter = psIter->psNext; + } + return NULL; +} + +/************************************************************************/ +/* OGRWFSWrappedResultLayer */ +/************************************************************************/ + +class OGRWFSWrappedResultLayer : public OGRLayer +{ + GDALDataset *poDS; + OGRLayer *poLayer; + + public: + OGRWFSWrappedResultLayer(GDALDataset* poDS, OGRLayer* poLayer) + { + this->poDS = poDS; + this->poLayer = poLayer; + } + ~OGRWFSWrappedResultLayer() + { + delete poDS; + } + + virtual void ResetReading() { poLayer->ResetReading(); } + virtual OGRFeature *GetNextFeature() { return poLayer->GetNextFeature(); } + virtual OGRErr SetNextByIndex( GIntBig nIndex ) { return poLayer->SetNextByIndex(nIndex); } + virtual OGRFeature *GetFeature( GIntBig nFID ) { return poLayer->GetFeature(nFID); } + virtual OGRFeatureDefn *GetLayerDefn() { return poLayer->GetLayerDefn(); } + virtual GIntBig GetFeatureCount( int bForce = TRUE ) { return poLayer->GetFeatureCount(bForce); } + virtual int TestCapability( const char * pszCap ) { return poLayer->TestCapability(pszCap); } +}; + + +/************************************************************************/ +/* OGRWFSDataSource() */ +/************************************************************************/ + +OGRWFSDataSource::OGRWFSDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; + + bUpdate = FALSE; + bGetFeatureSupportHits = FALSE; + bNeedNAMESPACE = FALSE; + bHasMinOperators = FALSE; + bHasNullCheck = FALSE; + bPropertyIsNotEqualToSupported = TRUE; /* advertized by deegree but not implemented */ + bTransactionSupport = FALSE; + papszIdGenMethods = NULL; + bUseFeatureId = FALSE; /* CubeWerx doesn't like GmlObjectId */ + bGmlObjectIdNeedsGMLPrefix = FALSE; + bRequiresEnvelopeSpatialFilter = FALSE; + + bRewriteFile = FALSE; + psFileXML = NULL; + + bUseHttp10 = FALSE; + papszHttpOptions = NULL; + + bPagingAllowed = CSLTestBoolean(CPLGetConfigOption("OGR_WFS_PAGING_ALLOWED", "OFF")); + nPageSize = DEFAULT_PAGE_SIZE; + nBaseStartIndex = DEFAULT_BASE_START_INDEX; + if (bPagingAllowed) + { + const char* pszOption; + + pszOption = CPLGetConfigOption("OGR_WFS_PAGE_SIZE", NULL); + if( pszOption != NULL ) + { + nPageSize = atoi(pszOption); + if (nPageSize <= 0) + nPageSize = DEFAULT_PAGE_SIZE; + } + + pszOption = CPLGetConfigOption("OGR_WFS_BASE_START_INDEX", NULL); + if( pszOption != NULL ) + nBaseStartIndex = atoi(pszOption); + } + + bStandardJoinsWFS2 = FALSE; + + bLoadMultipleLayerDefn = CSLTestBoolean(CPLGetConfigOption("OGR_WFS_LOAD_MULTIPLE_LAYER_DEFN", "TRUE")); + + poLayerMetadataDS = NULL; + poLayerMetadataLayer = NULL; + + poLayerGetCapabilitiesDS = NULL; + poLayerGetCapabilitiesLayer = NULL; + + bKeepLayerNamePrefix = FALSE; + apszGetCapabilities[0] = NULL; + apszGetCapabilities[1] = NULL; + bEmptyAsNull = TRUE; + + bInvertAxisOrderIfLatLong = TRUE; + bExposeGMLId = TRUE; +} + +/************************************************************************/ +/* ~OGRWFSDataSource() */ +/************************************************************************/ + +OGRWFSDataSource::~OGRWFSDataSource() + +{ + if (psFileXML) + { + if (bRewriteFile) + { + CPLSerializeXMLTreeToFile(psFileXML, pszName); + } + + CPLDestroyXMLNode(psFileXML); + } + + int i; + for( i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + if (osLayerMetadataTmpFileName.size() != 0) + VSIUnlink(osLayerMetadataTmpFileName); + delete poLayerMetadataDS; + delete poLayerGetCapabilitiesDS; + + CPLFree( pszName ); + CSLDestroy( papszIdGenMethods ); + CSLDestroy( papszHttpOptions ); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRWFSDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRWFSDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerByName() */ +/************************************************************************/ + +OGRLayer* OGRWFSDataSource::GetLayerByName(const char* pszName) +{ + if ( ! pszName ) + return NULL; + + if (EQUAL(pszName, "WFSLayerMetadata")) + { + if (osLayerMetadataTmpFileName.size() != 0) + return poLayerMetadataLayer; + + osLayerMetadataTmpFileName = CPLSPrintf("/vsimem/tempwfs_%p/WFSLayerMetadata.csv", this); + osLayerMetadataCSV = "layer_name,title,abstract\n" + osLayerMetadataCSV; + + VSIFCloseL(VSIFileFromMemBuffer(osLayerMetadataTmpFileName, + (GByte*) osLayerMetadataCSV.c_str(), + osLayerMetadataCSV.size(), FALSE)); + poLayerMetadataDS = (OGRDataSource*) OGROpen(osLayerMetadataTmpFileName, + FALSE, NULL); + if (poLayerMetadataDS) + poLayerMetadataLayer = poLayerMetadataDS->GetLayer(0); + return poLayerMetadataLayer; + } + else if (EQUAL(pszName, "WFSGetCapabilities")) + { + if (poLayerGetCapabilitiesLayer != NULL) + return poLayerGetCapabilitiesLayer; + + GDALDriver* poMEMDrv = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("Memory"); + if (poMEMDrv == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot load 'Memory' driver"); + return NULL; + } + + poLayerGetCapabilitiesDS = poMEMDrv->Create("WFSGetCapabilities", 0, 0, 0, GDT_Unknown, NULL); + poLayerGetCapabilitiesLayer = poLayerGetCapabilitiesDS->CreateLayer("WFSGetCapabilities", NULL, wkbNone, NULL); + OGRFieldDefn oFDefn("content", OFTString); + poLayerGetCapabilitiesLayer->CreateField(&oFDefn); + OGRFeature* poFeature = new OGRFeature(poLayerGetCapabilitiesLayer->GetLayerDefn()); + poFeature->SetField(0, osGetCapabilities); + poLayerGetCapabilitiesLayer->CreateFeature(poFeature); + delete poFeature; + + return poLayerGetCapabilitiesLayer; + } + + int nIndex = GetLayerIndex(pszName); + if (nIndex < 0) + return NULL; + else + return papoLayers[nIndex]; +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char** OGRWFSDataSource::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALDataset::GetMetadataDomainList(), + TRUE, + "", "xml:capabilities", NULL); + +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char** OGRWFSDataSource::GetMetadata( const char * pszDomain ) +{ + if( pszDomain != NULL && EQUAL(pszDomain, "xml:capabilities") ) + { + apszGetCapabilities[0] = osGetCapabilities.c_str(); + apszGetCapabilities[1] = NULL; + return (char**) apszGetCapabilities; + } + return GDALDataset::GetMetadata(pszDomain); +} + +/************************************************************************/ +/* GetLayerIndex() */ +/************************************************************************/ + +int OGRWFSDataSource::GetLayerIndex(const char* pszName) +{ + int i; + int bHasFoundLayerWithColon = FALSE; + + /* first a case sensitive check */ + for( i = 0; i < nLayers; i++ ) + { + OGRWFSLayer *poLayer = papoLayers[i]; + + if( strcmp( pszName, poLayer->GetName() ) == 0 ) + return i; + + bHasFoundLayerWithColon |= (strchr( poLayer->GetName(), ':') != NULL); + } + + /* then case insensitive */ + for( i = 0; i < nLayers; i++ ) + { + OGRWFSLayer *poLayer = papoLayers[i]; + + if( EQUAL( pszName, poLayer->GetName() ) ) + return i; + } + + /* now try looking after the colon character */ + if (!bKeepLayerNamePrefix && bHasFoundLayerWithColon && strchr(pszName, ':') == NULL) + { + for( i = 0; i < nLayers; i++ ) + { + OGRWFSLayer *poLayer = papoLayers[i]; + + const char* pszAfterColon = strchr( poLayer->GetName(), ':'); + if( pszAfterColon && EQUAL( pszName, pszAfterColon + 1 ) ) + return i; + } + } + + return -1; +} + +/************************************************************************/ +/* FindSubStringInsensitive() */ +/************************************************************************/ + +const char* FindSubStringInsensitive(const char* pszStr, + const char* pszSubStr) +{ + size_t nSubStrPos = CPLString(pszStr).ifind(pszSubStr); + if (nSubStrPos == std::string::npos) + return NULL; + return pszStr + nSubStrPos; +} + +/************************************************************************/ +/* DetectIfGetFeatureSupportHits() */ +/************************************************************************/ + +static int DetectIfGetFeatureSupportHits(CPLXMLNode* psRoot) +{ + CPLXMLNode* psOperationsMetadata = + CPLGetXMLNode(psRoot, "OperationsMetadata"); + if (!psOperationsMetadata) + { + CPLDebug("WFS", "Could not find "); + return FALSE; + } + + CPLXMLNode* psChild = psOperationsMetadata->psChild; + while(psChild) + { + if (psChild->eType == CXT_Element && + strcmp(psChild->pszValue, "Operation") == 0 && + strcmp(CPLGetXMLValue(psChild, "name", ""), "GetFeature") == 0) + { + break; + } + psChild = psChild->psNext; + } + if (!psChild) + { + CPLDebug("WFS", "Could not find "); + return FALSE; + } + + psChild = psChild->psChild; + while(psChild) + { + if (psChild->eType == CXT_Element && + strcmp(psChild->pszValue, "Parameter") == 0 && + strcmp(CPLGetXMLValue(psChild, "name", ""), "resultType") == 0) + { + break; + } + psChild = psChild->psNext; + } + if (!psChild) + { + CPLDebug("WFS", "Could not find "); + return FALSE; + } + + psChild = psChild->psChild; + while(psChild) + { + if (psChild->eType == CXT_Element && + strcmp(psChild->pszValue, "Value") == 0) + { + CPLXMLNode* psChild2 = psChild->psChild; + while(psChild2) + { + if (psChild2->eType == CXT_Text && + strcmp(psChild2->pszValue, "hits") == 0) + { + CPLDebug("WFS", "GetFeature operation supports hits"); + return TRUE; + } + psChild2 = psChild2->psNext; + } + } + psChild = psChild->psNext; + } + + return FALSE; +} + +/************************************************************************/ +/* DetectRequiresEnvelopeSpatialFilter() */ +/************************************************************************/ + +int OGRWFSDataSource::DetectRequiresEnvelopeSpatialFilter(CPLXMLNode* psRoot) +{ + /* This is a heuristic to detect Deegree 3 servers, such as */ + /* http://deegree3-demo.deegree.org:80/deegree-utah-demo/services */ + /* that are very GML3 strict, and don't like in a */ + /* request, but requires instead , but some servers (such as MapServer) */ + /* don't like so we are obliged to detect the kind of server */ + + int nCount; + CPLXMLNode* psChild; + + CPLXMLNode* psGeometryOperands = + CPLGetXMLNode(psRoot, "Filter_Capabilities.Spatial_Capabilities.GeometryOperands"); + if (!psGeometryOperands) + { + return FALSE; + } + + nCount = 0; + psChild = psGeometryOperands->psChild; + while(psChild) + { + nCount ++; + psChild = psChild->psNext; + } + /* Magic number... Might be fragile */ + return (nCount == 19); +} + +/************************************************************************/ +/* GetPostTransactionURL() */ +/************************************************************************/ + +CPLString OGRWFSDataSource::GetPostTransactionURL() +{ + if (osPostTransactionURL.size()) + return osPostTransactionURL; + + osPostTransactionURL = osBaseURL; + const char* pszPostTransactionURL = osPostTransactionURL.c_str(); + const char* pszEsperluet = strchr(pszPostTransactionURL, '?'); + if (pszEsperluet) + osPostTransactionURL.resize(pszEsperluet - pszPostTransactionURL); + + return osPostTransactionURL; +} + +/************************************************************************/ +/* DetectTransactionSupport() */ +/************************************************************************/ + +int OGRWFSDataSource::DetectTransactionSupport(CPLXMLNode* psRoot) +{ + CPLXMLNode* psTransactionWFS100 = + CPLGetXMLNode(psRoot, "Capability.Request.Transaction"); + if (psTransactionWFS100) + { + CPLXMLNode* psPostURL = CPLGetXMLNode(psTransactionWFS100, "DCPType.HTTP.Post"); + if (psPostURL) + { + const char* pszPOSTURL = CPLGetXMLValue(psPostURL, "onlineResource", NULL); + if (pszPOSTURL) + { + osPostTransactionURL = pszPOSTURL; + } + } + + bTransactionSupport = TRUE; + return TRUE; + } + + CPLXMLNode* psOperationsMetadata = + CPLGetXMLNode(psRoot, "OperationsMetadata"); + if (!psOperationsMetadata) + { + return FALSE; + } + + CPLXMLNode* psChild = psOperationsMetadata->psChild; + while(psChild) + { + if (psChild->eType == CXT_Element && + strcmp(psChild->pszValue, "Operation") == 0 && + strcmp(CPLGetXMLValue(psChild, "name", ""), "Transaction") == 0) + { + break; + } + psChild = psChild->psNext; + } + if (!psChild) + { + CPLDebug("WFS", "No transaction support"); + return FALSE; + } + + bTransactionSupport = TRUE; + CPLDebug("WFS", "Transaction support !"); + + + CPLXMLNode* psPostURL = CPLGetXMLNode(psChild, "DCP.HTTP.Post"); + if (psPostURL) + { + const char* pszPOSTURL = CPLGetXMLValue(psPostURL, "href", NULL); + if (pszPOSTURL) + osPostTransactionURL = pszPOSTURL; + } + + psChild = psChild->psChild; + while(psChild) + { + if (psChild->eType == CXT_Element && + strcmp(psChild->pszValue, "Parameter") == 0 && + strcmp(CPLGetXMLValue(psChild, "name", ""), "idgen") == 0) + { + break; + } + psChild = psChild->psNext; + } + if (!psChild) + { + papszIdGenMethods = CSLAddString(NULL, "GenerateNew"); + return TRUE; + } + + psChild = psChild->psChild; + while(psChild) + { + if (psChild->eType == CXT_Element && + strcmp(psChild->pszValue, "Value") == 0) + { + CPLXMLNode* psChild2 = psChild->psChild; + while(psChild2) + { + if (psChild2->eType == CXT_Text) + { + papszIdGenMethods = CSLAddString(papszIdGenMethods, + psChild2->pszValue); + } + psChild2 = psChild2->psNext; + } + } + psChild = psChild->psNext; + } + + return TRUE; +} + +/************************************************************************/ +/* DetectSupportPagingWFS2() */ +/************************************************************************/ + +int OGRWFSDataSource::DetectSupportPagingWFS2(CPLXMLNode* psRoot) +{ + const char* pszPagingAllowed = CPLGetConfigOption("OGR_WFS_PAGING_ALLOWED", NULL); + if( pszPagingAllowed != NULL && !CSLTestBoolean(pszPagingAllowed) ) + return FALSE; + + CPLXMLNode* psOperationsMetadata = + CPLGetXMLNode(psRoot, "OperationsMetadata"); + if (!psOperationsMetadata) + { + return FALSE; + } + + CPLXMLNode* psChild = psOperationsMetadata->psChild; + while(psChild) + { + if (psChild->eType == CXT_Element && + strcmp(psChild->pszValue, "Constraint") == 0 && + strcmp(CPLGetXMLValue(psChild, "name", ""), "ImplementsResultPaging") == 0) + { + if( !EQUAL(CPLGetXMLValue(psChild, "DefaultValue", ""), "TRUE") ) + { + psChild = NULL; + break; + } + break; + } + psChild = psChild->psNext; + } + if (!psChild) + { + CPLDebug("WFS", "No paging support"); + return FALSE; + } + + psChild = psOperationsMetadata->psChild; + while(psChild) + { + if (psChild->eType == CXT_Element && + strcmp(psChild->pszValue, "Operation") == 0 && + strcmp(CPLGetXMLValue(psChild, "name", ""), "GetFeature") == 0) + { + break; + } + psChild = psChild->psNext; + } + if (psChild && CPLGetConfigOption("OGR_WFS_PAGE_SIZE", NULL) == NULL) + { + psChild = psChild->psChild; + while(psChild) + { + if (psChild->eType == CXT_Element && + strcmp(psChild->pszValue, "Constraint") == 0 && + strcmp(CPLGetXMLValue(psChild, "name", ""), "CountDefault") == 0) + { + int nVal = atoi(CPLGetXMLValue(psChild, "DefaultValue", "0")); + if( nVal > 0 ) + nPageSize = nVal; + + break; + } + psChild = psChild->psNext; + } + } + const char* pszOption = CPLGetConfigOption("OGR_WFS_PAGE_SIZE", NULL); + if( pszOption != NULL ) + { + nPageSize = atoi(pszOption); + if (nPageSize <= 0) + nPageSize = DEFAULT_PAGE_SIZE; + } + + CPLDebug("WFS", "Paging support with page size %d", nPageSize); + bPagingAllowed = TRUE; + + return TRUE; +} + +/************************************************************************/ +/* DetectSupportStandardJoinsWFS2() */ +/************************************************************************/ + +int OGRWFSDataSource::DetectSupportStandardJoinsWFS2(CPLXMLNode* psRoot) +{ + CPLXMLNode* psOperationsMetadata = + CPLGetXMLNode(psRoot, "OperationsMetadata"); + if (!psOperationsMetadata) + { + return FALSE; + } + + CPLXMLNode* psChild = psOperationsMetadata->psChild; + while(psChild) + { + if (psChild->eType == CXT_Element && + strcmp(psChild->pszValue, "Constraint") == 0 && + strcmp(CPLGetXMLValue(psChild, "name", ""), "ImplementsStandardJoins") == 0) + { + if( !EQUAL(CPLGetXMLValue(psChild, "DefaultValue", ""), "TRUE") ) + { + psChild = NULL; + break; + } + break; + } + psChild = psChild->psNext; + } + if (!psChild) + { + CPLDebug("WFS", "No ImplementsStandardJoins support"); + return FALSE; + } + bStandardJoinsWFS2 = TRUE; + return TRUE; +} + +/************************************************************************/ +/* FindComparisonOperator() */ +/************************************************************************/ + +static int FindComparisonOperator(CPLXMLNode* psNode, const char* pszVal) +{ + CPLXMLNode* psChild = psNode->psChild; + while(psChild) + { + if (psChild->eType == CXT_Element && + strcmp(psChild->pszValue, "ComparisonOperator") == 0) + { + if (strcmp(CPLGetXMLValue(psChild, NULL, ""), pszVal) == 0) + return TRUE; + + /* For WFS 2.0.0 */ + const char* pszName = CPLGetXMLValue(psChild, "name", NULL); + if (pszName != NULL && strncmp(pszName, "PropertyIs", 10) == 0 && + strcmp(pszName + 10, pszVal) == 0) + return TRUE; + } + psChild = psChild->psNext; + } + return FALSE; +} + +/************************************************************************/ +/* LoadFromFile() */ +/************************************************************************/ + +CPLXMLNode* OGRWFSDataSource::LoadFromFile( const char * pszFilename ) +{ + VSILFILE *fp; + char achHeader[1024]; + + VSIStatBufL sStatBuf; + if (VSIStatExL( pszFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) != 0 || + VSI_ISDIR(sStatBuf.st_mode)) + return NULL; + + fp = VSIFOpenL( pszFilename, "rb" ); + + if( fp == NULL ) + return NULL; + + int nRead; + if( (nRead = VSIFReadL( achHeader, 1, sizeof(achHeader) - 1, fp )) == 0 ) + { + VSIFCloseL( fp ); + return NULL; + } + achHeader[nRead] = 0; + + if( !EQUALN(achHeader,"",18) && + strstr(achHeader,"pabyData, + "pabyData, + "pabyData, + "pabyData); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + return psResult; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRWFSDataSource::Open( const char * pszFilename, int bUpdateIn, + char** papszOpenOptions ) + +{ + bUpdate = bUpdateIn; + CPLFree(pszName); + pszName = CPLStrdup(pszFilename); + + CPLXMLNode* psWFSCapabilities = NULL; + CPLXMLNode* psXML = LoadFromFile(pszFilename); + CPLString osTypeName; + const char* pszBaseURL = NULL; + + bEmptyAsNull = CSLFetchBoolean(papszOpenOptions, "EMPTY_AS_NULL", TRUE); + + if (psXML == NULL) + { + if (!EQUALN(pszFilename, "WFS:", 4) && + FindSubStringInsensitive(pszFilename, "SERVICE=WFS") == NULL) + { + return FALSE; + } + + pszBaseURL = CSLFetchNameValue(papszOpenOptions, "URL"); + if( pszBaseURL == NULL ) + { + pszBaseURL = pszFilename; + if (EQUALN(pszFilename, "WFS:", 4)) + pszBaseURL += 4; + } + + osBaseURL = pszBaseURL; + + if (strncmp(pszBaseURL, "http://", 7) != 0 && + strncmp(pszBaseURL, "https://", 8) != 0 && + strncmp(pszBaseURL, "/vsimem/", strlen("/vsimem/")) != 0) + return FALSE; + + CPLString strOriginalTypeName = ""; + CPLHTTPResult* psResult = SendGetCapabilities(pszBaseURL, strOriginalTypeName); + osTypeName = WFS_DecodeURL(strOriginalTypeName); + if (psResult == NULL) + { + return FALSE; + } + + if (strstr((const char*) psResult->pabyData, "CubeWerx")) + { + /* At least true for CubeWerx Suite 4.15.1 */ + bUseFeatureId = TRUE; + } + else if (strstr((const char*) psResult->pabyData, "deegree")) + { + bGmlObjectIdNeedsGMLPrefix = TRUE; + } + + psXML = CPLParseXMLString( (const char*) psResult->pabyData ); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + return FALSE; + } + osGetCapabilities = (const char*) psResult->pabyData; + + CPLHTTPDestroyResult(psResult); + } + else if ( WFSFindNode( psXML, "OGRWFSDataSource" ) == NULL && + WFSFindNode( psXML, "WFS_Capabilities" ) != NULL ) + { + /* This is directly the Capabilities document */ + char* pszXML = CPLSerializeXMLTree(WFSFindNode( psXML, "WFS_Capabilities" )); + osGetCapabilities = pszXML; + CPLFree(pszXML); + } + else + { + CPLXMLNode* psRoot = WFSFindNode( psXML, "OGRWFSDataSource" ); + if (psRoot == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find "); + CPLDestroyXMLNode( psXML ); + return FALSE; + } + + pszBaseURL = CPLGetXMLValue(psRoot, "URL", NULL); + if (pszBaseURL == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find "); + CPLDestroyXMLNode( psXML ); + return FALSE; + } + osBaseURL = pszBaseURL; + +/* -------------------------------------------------------------------- */ +/* Capture other parameters. */ +/* -------------------------------------------------------------------- */ + const char *pszParm; + + pszParm = CPLGetXMLValue( psRoot, "Timeout", NULL ); + if( pszParm ) + papszHttpOptions = + CSLSetNameValue(papszHttpOptions, + "TIMEOUT", pszParm ); + + pszParm = CPLGetXMLValue( psRoot, "HTTPAUTH", NULL ); + if( pszParm ) + papszHttpOptions = + CSLSetNameValue( papszHttpOptions, + "HTTPAUTH", pszParm ); + + pszParm = CPLGetXMLValue( psRoot, "USERPWD", NULL ); + if( pszParm ) + papszHttpOptions = + CSLSetNameValue( papszHttpOptions, + "USERPWD", pszParm ); + + pszParm = CPLGetXMLValue( psRoot, "COOKIE", NULL ); + if( pszParm ) + papszHttpOptions = + CSLSetNameValue( papszHttpOptions, + "COOKIE", pszParm ); + + pszParm = CPLGetXMLValue( psRoot, "Version", NULL ); + if( pszParm ) + osVersion = pszParm; + + pszParm = CPLGetXMLValue( psRoot, "PagingAllowed", NULL ); + if( pszParm ) + bPagingAllowed = CSLTestBoolean(pszParm); + + pszParm = CPLGetXMLValue( psRoot, "PageSize", NULL ); + if( pszParm ) + { + nPageSize = atoi(pszParm); + if (nPageSize <= 0) + nPageSize = DEFAULT_PAGE_SIZE; + } + + pszParm = CPLGetXMLValue( psRoot, "BaseStartIndex", NULL ); + if( pszParm ) + nBaseStartIndex = atoi(pszParm); + + CPLString strOriginalTypeName = CPLURLGetValue(pszBaseURL, "TYPENAME"); + if( strOriginalTypeName.size() == 0 ) + strOriginalTypeName = CPLURLGetValue(pszBaseURL, "TYPENAMES"); + osTypeName = WFS_DecodeURL(strOriginalTypeName); + + psWFSCapabilities = WFSFindNode( psRoot, "WFS_Capabilities" ); + if (psWFSCapabilities == NULL) + { + CPLHTTPResult* psResult = SendGetCapabilities(pszBaseURL, strOriginalTypeName); + osTypeName = WFS_DecodeURL(strOriginalTypeName); + + if (psResult == NULL) + { + CPLDestroyXMLNode( psXML ); + return FALSE; + } + + CPLXMLNode* psXML2 = CPLParseXMLString( (const char*) psResult->pabyData ); + if (psXML2 == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + CPLDestroyXMLNode( psXML ); + return FALSE; + } + + CPLHTTPDestroyResult(psResult); + + psWFSCapabilities = WFSFindNode( psXML2, "WFS_Capabilities" ); + if (psWFSCapabilities == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find "); + CPLDestroyXMLNode( psXML ); + CPLDestroyXMLNode( psXML2 ); + return FALSE; + } + + CPLAddXMLChild(psXML, CPLCloneXMLTree(psWFSCapabilities)); + + int bOK = CPLSerializeXMLTreeToFile(psXML, pszFilename); + + CPLDestroyXMLNode( psXML ); + CPLDestroyXMLNode( psXML2 ); + + if (bOK) + return Open(pszFilename, bUpdate, papszOpenOptions); + else + return FALSE; + } + else + { + psFileXML = psXML; + + /* To avoid to have nodes after WFSCapabilities */ + CPLXMLNode* psAfterWFSCapabilities = psWFSCapabilities->psNext; + psWFSCapabilities->psNext = NULL; + char* pszXML = CPLSerializeXMLTree(psWFSCapabilities); + psWFSCapabilities->psNext = psAfterWFSCapabilities; + osGetCapabilities = pszXML; + CPLFree(pszXML); + } + } + + bInvertAxisOrderIfLatLong = + CSLTestBoolean(CSLFetchNameValueDef(papszOpenOptions, + "INVERT_AXIS_ORDER_IF_LAT_LONG", + CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", "YES"))); + osConsiderEPSGAsURN = + CSLFetchNameValueDef(papszOpenOptions, + "CONSIDER_EPSG_AS_URN", + CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", "AUTO")); + bExposeGMLId = + CSLTestBoolean(CSLFetchNameValueDef(papszOpenOptions, + "EXPOSE_GML_ID", + CPLGetConfigOption("GML_EXPOSE_GML_ID", "YES"))); + + CPLXMLNode* psStrippedXML = CPLCloneXMLTree(psXML); + CPLStripXMLNamespace( psStrippedXML, NULL, TRUE ); + psWFSCapabilities = CPLGetXMLNode( psStrippedXML, "=WFS_Capabilities" ); + if (psWFSCapabilities == NULL) + { + psWFSCapabilities = CPLGetXMLNode( psStrippedXML, "=OGRWFSDataSource.WFS_Capabilities" ); + } + if (psWFSCapabilities == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find "); + if (!psFileXML) CPLDestroyXMLNode( psXML ); + CPLDestroyXMLNode( psStrippedXML ); + return FALSE; + } + + if (pszBaseURL == NULL) + { + /* This is directly the Capabilities document */ + pszBaseURL = CPLGetXMLValue( psWFSCapabilities, "OperationsMetadata.Operation.DCP.HTTP.Get.href", NULL ); + if (pszBaseURL == NULL) /* WFS 1.0.0 variant */ + pszBaseURL = CPLGetXMLValue( psWFSCapabilities, "Capability.Request.GetCapabilities.DCPType.HTTP.Get.onlineResource", NULL ); + + if (pszBaseURL == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find base URL"); + if (!psFileXML) CPLDestroyXMLNode( psXML ); + CPLDestroyXMLNode( psStrippedXML ); + return FALSE; + } + + osBaseURL = pszBaseURL; + } + + pszBaseURL = NULL; + + for(int i=0; i < (int)(sizeof(asMetadata) / sizeof(asMetadata[0])); i++ ) + { + const char* pszVal = CPLGetXMLValue( psWFSCapabilities, asMetadata[i].pszPath, NULL ); + if( pszVal ) + SetMetadataItem(asMetadata[i].pszMDI, pszVal); + } + + if (osVersion.size() == 0) + osVersion = CPLGetXMLValue(psWFSCapabilities, "version", "1.0.0"); + if (strcmp(osVersion.c_str(), "1.0.0") == 0) + bUseFeatureId = TRUE; + else + { + /* Some servers happen to support RESULTTYPE=hits in 1.0.0, but there */ + /* is no way to advertisze this */ + if (atoi(osVersion) >= 2) + bGetFeatureSupportHits = TRUE; /* WFS >= 2.0.0 supports hits */ + else + bGetFeatureSupportHits = DetectIfGetFeatureSupportHits(psWFSCapabilities); + bRequiresEnvelopeSpatialFilter = DetectRequiresEnvelopeSpatialFilter(psWFSCapabilities); + } + + if ( atoi(osVersion) >= 2 ) + { + CPLString osMaxFeatures = CPLURLGetValue(osBaseURL, "COUNT" ); + /* Ok, people are used to MAXFEATURES, so be nice to recognize it if it is used for WFS 2.0 ... */ + if (osMaxFeatures.size() == 0 ) + { + osMaxFeatures = CPLURLGetValue(osBaseURL, "MAXFEATURES"); + if( osMaxFeatures.size() != 0 && + CSLTestBoolean(CPLGetConfigOption("OGR_WFS_FIX_MAXFEATURES", "YES")) ) + { + CPLDebug("WFS", "MAXFEATURES wrongly used for WFS 2.0. Using COUNT instead"); + osBaseURL = CPLURLAddKVP(osBaseURL, "MAXFEATURES", NULL); + osBaseURL = CPLURLAddKVP(osBaseURL, "COUNT", osMaxFeatures); + } + } + + DetectSupportPagingWFS2(psWFSCapabilities); + DetectSupportStandardJoinsWFS2(psWFSCapabilities); + } + + DetectTransactionSupport(psWFSCapabilities); + + if (bUpdate && !bTransactionSupport) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Server is read-only WFS; no WFS-T feature advertized"); + if (!psFileXML) CPLDestroyXMLNode( psXML ); + CPLDestroyXMLNode( psStrippedXML ); + return FALSE; + } + + CPLXMLNode* psFilterCap = CPLGetXMLNode(psWFSCapabilities, "Filter_Capabilities.Scalar_Capabilities"); + if (psFilterCap) + { + bHasMinOperators = CPLGetXMLNode(psFilterCap, "LogicalOperators") != NULL || + CPLGetXMLNode(psFilterCap, "Logical_Operators") != NULL; + if (CPLGetXMLNode(psFilterCap, "ComparisonOperators")) + psFilterCap = CPLGetXMLNode(psFilterCap, "ComparisonOperators"); + else if (CPLGetXMLNode(psFilterCap, "Comparison_Operators")) + psFilterCap = CPLGetXMLNode(psFilterCap, "Comparison_Operators"); + else + psFilterCap = NULL; + if (psFilterCap) + { + if (CPLGetXMLNode(psFilterCap, "Simple_Comparisons") == NULL) + { + bHasMinOperators &= FindComparisonOperator(psFilterCap, "LessThan"); + bHasMinOperators &= FindComparisonOperator(psFilterCap, "GreaterThan"); + if (atoi(osVersion) >= 2) + { + bHasMinOperators &= FindComparisonOperator(psFilterCap, "LessThanOrEqualTo"); + bHasMinOperators &= FindComparisonOperator(psFilterCap, "GreaterThanOrEqualTo"); + } + else + { + bHasMinOperators &= FindComparisonOperator(psFilterCap, "LessThanEqualTo"); + bHasMinOperators &= FindComparisonOperator(psFilterCap, "GreaterThanEqualTo"); + } + bHasMinOperators &= FindComparisonOperator(psFilterCap, "EqualTo"); + bHasMinOperators &= FindComparisonOperator(psFilterCap, "NotEqualTo"); + bHasMinOperators &= FindComparisonOperator(psFilterCap, "Like"); + } + else + { + bHasMinOperators &= CPLGetXMLNode(psFilterCap, "Simple_Comparisons") != NULL && + CPLGetXMLNode(psFilterCap, "Like") != NULL; + } + bHasNullCheck = FindComparisonOperator(psFilterCap, "NullCheck") || + FindComparisonOperator(psFilterCap, "Null") || /* WFS 2.0.0 */ + CPLGetXMLNode(psFilterCap, "NullCheck") != NULL; + } + else + { + bHasMinOperators = FALSE; + } + } + + CPLXMLNode* psChild = CPLGetXMLNode(psWFSCapabilities, "FeatureTypeList"); + if (psChild == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find "); + if (!psFileXML) CPLDestroyXMLNode( psXML ); + CPLDestroyXMLNode( psStrippedXML ); + return FALSE; + } + + CPLXMLNode* psChildIter; + + /* Check if there are layer names whose identical except their prefix */ + std::set aosSetLayerNames; + for(psChildIter = psChild->psChild; + psChildIter != NULL; + psChildIter = psChildIter->psNext) + { + if (psChildIter->eType == CXT_Element && + strcmp(psChildIter->pszValue, "FeatureType") == 0) + { + const char* pszName = CPLGetXMLValue(psChildIter, "Name", NULL); + if (pszName != NULL) + { + const char* pszShortName = strchr(pszName, ':'); + if (pszShortName) + pszName = pszShortName + 1; + if (aosSetLayerNames.find(pszName) != aosSetLayerNames.end()) + { + bKeepLayerNamePrefix = TRUE; + CPLDebug("WFS", "At least 2 layers have names that are only distinguishable by keeping the prefix"); + break; + } + aosSetLayerNames.insert(pszName); + } + } + } + + char** papszTypenames = NULL; + if (osTypeName.size() != 0) + papszTypenames = CSLTokenizeStringComplex( osTypeName, ",", FALSE, FALSE ); + + for(psChildIter = psChild->psChild; + psChildIter != NULL; + psChildIter = psChildIter->psNext) + { + if (psChildIter->eType == CXT_Element && + strcmp(psChildIter->pszValue, "FeatureType") == 0) + { + const char* pszNS = NULL; + const char* pszNSVal = NULL; + CPLXMLNode* psFeatureTypeIter = psChildIter->psChild; + while(psFeatureTypeIter != NULL) + { + if (psFeatureTypeIter->eType == CXT_Attribute) + { + pszNS = psFeatureTypeIter->pszValue; + pszNSVal = psFeatureTypeIter->psChild->pszValue; + } + psFeatureTypeIter = psFeatureTypeIter->psNext; + } + + const char* pszName = CPLGetXMLValue(psChildIter, "Name", NULL); + const char* pszTitle = CPLGetXMLValue(psChildIter, "Title", NULL); + const char* pszAbstract = CPLGetXMLValue(psChildIter, "Abstract", NULL); + if (pszName != NULL && + (papszTypenames == NULL || + CSLFindString(papszTypenames, pszName) != -1)) + { + const char* pszDefaultSRS = + CPLGetXMLValue(psChildIter, "DefaultSRS", NULL); + if (pszDefaultSRS == NULL) + pszDefaultSRS = CPLGetXMLValue(psChildIter, "SRS", NULL); + if (pszDefaultSRS == NULL) + pszDefaultSRS = CPLGetXMLValue(psChildIter, "DefaultCRS", NULL); /* WFS 2.0.0 */ + + CPLXMLNode* psOutputFormats = CPLGetXMLNode(psChildIter, "OutputFormats"); + CPLString osOutputFormat; + if (psOutputFormats) + { + std::vector osFormats; + CPLXMLNode* psOutputFormatIter = psOutputFormats->psChild; + while(psOutputFormatIter) + { + if (psOutputFormatIter->eType == CXT_Element && + EQUAL(psOutputFormatIter->pszValue, "Format") && + psOutputFormatIter->psChild != NULL && + psOutputFormatIter->psChild->eType == CXT_Text) + { + osFormats.push_back(psOutputFormatIter->psChild->pszValue); + } + psOutputFormatIter = psOutputFormatIter->psNext; + } + + if (strcmp(osVersion.c_str(), "1.1.0") == 0 && osFormats.size() > 0) + { + int bFoundGML31 = FALSE; + for(size_t i=0;iGetAttrNode( "GEOGCS" ); + if( poGEOGCS != NULL ) + poGEOGCS->StripNodes( "AXIS" ); + + OGR_SRSNode *poPROJCS = poSRS->GetAttrNode( "PROJCS" ); + if (poPROJCS != NULL && poSRS->EPSGTreatsAsNorthingEasting()) + poPROJCS->StripNodes( "AXIS" ); + } + } + } + + CPLXMLNode* psBBox = NULL; + CPLXMLNode* psLatLongBBox = NULL; + /* int bFoundBBox = FALSE; */ + double dfMinX = 0, dfMinY = 0, dfMaxX = 0, dfMaxY = 0; + if ((psBBox = CPLGetXMLNode(psChildIter, "WGS84BoundingBox")) != NULL) + { + const char* pszLC = CPLGetXMLValue(psBBox, "LowerCorner", NULL); + const char* pszUC = CPLGetXMLValue(psBBox, "UpperCorner", NULL); + if (pszLC != NULL && pszUC != NULL) + { + CPLString osConcat(pszLC); + osConcat += " "; + osConcat += pszUC; + char** papszTokens; + papszTokens = CSLTokenizeStringComplex( + osConcat, " ,", FALSE, FALSE ); + if (CSLCount(papszTokens) == 4) + { + /* bFoundBBox = TRUE; */ + dfMinX = CPLAtof(papszTokens[0]); + dfMinY = CPLAtof(papszTokens[1]); + dfMaxX = CPLAtof(papszTokens[2]); + dfMaxY = CPLAtof(papszTokens[3]); + } + CSLDestroy(papszTokens); + } + } + else if ((psLatLongBBox = CPLGetXMLNode(psChildIter, + "LatLongBoundingBox")) != NULL) + { + const char* pszMinX = + CPLGetXMLValue(psLatLongBBox, "minx", NULL); + const char* pszMinY = + CPLGetXMLValue(psLatLongBBox, "miny", NULL); + const char* pszMaxX = + CPLGetXMLValue(psLatLongBBox, "maxx", NULL); + const char* pszMaxY = + CPLGetXMLValue(psLatLongBBox, "maxy", NULL); + if (pszMinX != NULL && pszMinY != NULL && + pszMaxX != NULL && pszMaxY != NULL) + { + /* bFoundBBox = TRUE; */ + dfMinX = CPLAtof(pszMinX); + dfMinY = CPLAtof(pszMinY); + dfMaxX = CPLAtof(pszMaxX); + dfMaxY = CPLAtof(pszMaxY); + } + } + + char* pszCSVEscaped; + + pszCSVEscaped = CPLEscapeString(pszName, -1, CPLES_CSV); + osLayerMetadataCSV += pszCSVEscaped; + CPLFree(pszCSVEscaped); + + osLayerMetadataCSV += ","; + if (pszTitle) + { + pszCSVEscaped = CPLEscapeString(pszTitle, -1, CPLES_CSV); + osLayerMetadataCSV += pszCSVEscaped; + CPLFree(pszCSVEscaped); + } + osLayerMetadataCSV += ","; + if (pszAbstract) + { + pszCSVEscaped = CPLEscapeString(pszAbstract, -1, CPLES_CSV); + osLayerMetadataCSV += pszCSVEscaped; + CPLFree(pszCSVEscaped); + } + osLayerMetadataCSV += "\n"; + + OGRWFSLayer* poLayer = new OGRWFSLayer( + this, poSRS, bAxisOrderAlreadyInverted, + osBaseURL, pszName, pszNS, pszNSVal); + if (osOutputFormat.size()) + poLayer->SetRequiredOutputFormat(osOutputFormat); + + if( pszTitle ) + poLayer->SetMetadataItem("TITLE", pszTitle); + if( pszAbstract ) + poLayer->SetMetadataItem("ABSTRACT", pszAbstract); + CPLXMLNode* psKeywords = CPLGetXMLNode(psChildIter, "Keywords"); + if( psKeywords ) + { + int nKeywordCounter = 1; + for( CPLXMLNode* psKeyword = psKeywords->psChild; + psKeyword != NULL; psKeyword = psKeyword->psNext ) + { + if( psKeyword->eType == CXT_Element ) + { + poLayer->SetMetadataItem(CPLSPrintf("KEYWORD_%d", nKeywordCounter), + psKeyword->psChild->pszValue); + nKeywordCounter ++; + } + else if( psKeyword->eType == CXT_Text ) + { + poLayer->SetMetadataItem("KEYWORDS", + psKeyword->pszValue); + } + } + } + + if (poSRS) + { + char* pszProj4 = NULL; + if (poSRS->exportToProj4(&pszProj4) == OGRERR_NONE) + { + /* See http://trac.osgeo.org/gdal/ticket/4041 */ + int bTrustBounds = + CSLFetchBoolean(papszOpenOptions, "TRUST_CAPABILITIES_BOUNDS", + CSLTestBoolean(CPLGetConfigOption("OGR_WFS_TRUST_CAPABILITIES_BOUNDS", "FALSE"))); + + if (((bTrustBounds || (dfMinX == -180 && dfMinY == -90 && dfMaxX == 180 && dfMaxY == 90)) && + (strcmp(pszProj4, "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ") == 0 || + strcmp(pszProj4, "+proj=longlat +datum=WGS84 +no_defs ") == 0)) || + strcmp(pszDefaultSRS, "urn:ogc:def:crs:OGC:1.3:CRS84") == 0) + { + poLayer->SetExtents(dfMinX, dfMinY, dfMaxX, dfMaxY); + } + + else if( bTrustBounds ) + { + OGRSpatialReference oWGS84; + oWGS84.SetWellKnownGeogCS("WGS84"); + OGRCoordinateTransformation* poCT; + CPLPushErrorHandler(CPLQuietErrorHandler); + poCT = OGRCreateCoordinateTransformation(&oWGS84, poSRS); + if (poCT) + { + double dfULX = dfMinX; + double dfULY = dfMaxY; + double dfURX = dfMaxX; + double dfURY = dfMaxY; + double dfLLX = dfMinX; + double dfLLY = dfMinY; + double dfLRX = dfMaxX; + double dfLRY = dfMinY; + if (poCT->Transform(1, &dfULX, &dfULY, NULL) && + poCT->Transform(1, &dfURX, &dfURY, NULL) && + poCT->Transform(1, &dfLLX, &dfLLY, NULL) && + poCT->Transform(1, &dfLRX, &dfLRY, NULL)) + { + dfMinX = dfULX; + dfMinX = MIN(dfMinX, dfURX); + dfMinX = MIN(dfMinX, dfLLX); + dfMinX = MIN(dfMinX, dfLRX); + + dfMinY = dfULY; + dfMinY = MIN(dfMinY, dfURY); + dfMinY = MIN(dfMinY, dfLLY); + dfMinY = MIN(dfMinY, dfLRY); + + dfMaxX = dfULX; + dfMaxX = MAX(dfMaxX, dfURX); + dfMaxX = MAX(dfMaxX, dfLLX); + dfMaxX = MAX(dfMaxX, dfLRX); + + dfMaxY = dfULY; + dfMaxY = MAX(dfMaxY, dfURY); + dfMaxY = MAX(dfMaxY, dfLLY); + dfMaxY = MAX(dfMaxY, dfLRY); + + poLayer->SetExtents(dfMinX, dfMinY, dfMaxX, dfMaxY); + } + } + delete poCT; + CPLPopErrorHandler(); + CPLErrorReset(); + } + + } + CPLFree(pszProj4); + } + + papoLayers = (OGRWFSLayer **)CPLRealloc(papoLayers, + sizeof(OGRWFSLayer*) * (nLayers + 1)); + papoLayers[nLayers ++] = poLayer; + + if (psFileXML != NULL) + { + CPLXMLNode* psIter = psXML->psChild; + while(psIter) + { + if (psIter->eType == CXT_Element && + EQUAL(psIter->pszValue, "OGRWFSLayer") && + strcmp(CPLGetXMLValue(psIter, "name", ""), pszName) == 0) + { + CPLXMLNode* psSchema = WFSFindNode( psIter->psChild, "schema" ); + if (psSchema) + { + OGRFeatureDefn* poSrcFDefn = poLayer->ParseSchema(psSchema); + if (poSrcFDefn) + poLayer->BuildLayerDefn(poSrcFDefn); + } + break; + } + psIter = psIter->psNext; + } + } + } + } + } + + CSLDestroy(papszTypenames); + + if (!psFileXML) CPLDestroyXMLNode( psXML ); + CPLDestroyXMLNode( psStrippedXML ); + + return TRUE; +} + +/************************************************************************/ +/* LoadMultipleLayerDefn() */ +/************************************************************************/ + +/* TinyOWS doesn't support POST, but MapServer, GeoServer and Deegree do */ +#define USE_GET_FOR_DESCRIBE_FEATURE_TYPE 1 + +void OGRWFSDataSource::LoadMultipleLayerDefn(const char* pszLayerName, + char* pszNS, char* pszNSVal) +{ + if (!bLoadMultipleLayerDefn) + return; + + if (aoSetAlreadyTriedLayers.find(pszLayerName) != aoSetAlreadyTriedLayers.end()) + return; + + char* pszPrefix = CPLStrdup(pszLayerName); + char* pszColumn = strchr(pszPrefix, ':'); + if (pszColumn) + *pszColumn = 0; + else + *pszPrefix = 0; + + OGRWFSLayer* poRefLayer = (OGRWFSLayer*)GetLayerByName(pszLayerName); + if (poRefLayer == NULL) + return; + + const char* pszRequiredOutputFormat = poRefLayer->GetRequiredOutputFormat(); + +#if USE_GET_FOR_DESCRIBE_FEATURE_TYPE == 1 + CPLString osLayerToFetch(pszLayerName); +#else + CPLString osTypeNameToPost; + osTypeNameToPost += " "; + osTypeNameToPost += pszLayerName; + osTypeNameToPost += "\n"; +#endif + + int nLayersToFetch = 1; + aoSetAlreadyTriedLayers.insert(pszLayerName); + + for(int i=0;iHasLayerDefn()) + { + /* We must be careful to requests only layers with the same prefix/namespace */ + const char* pszName = papoLayers[i]->GetName(); + if (((pszPrefix[0] == 0 && strchr(pszName, ':') == NULL) || + (pszPrefix[0] != 0 && strncmp(pszName, pszPrefix, strlen(pszPrefix)) == 0 && + pszName[strlen(pszPrefix)] == ':')) && + ((pszRequiredOutputFormat == NULL && papoLayers[i]->GetRequiredOutputFormat() == NULL) || + (pszRequiredOutputFormat != NULL && papoLayers[i]->GetRequiredOutputFormat() != NULL && + strcmp(pszRequiredOutputFormat, papoLayers[i]->GetRequiredOutputFormat()) == 0))) + { + if (aoSetAlreadyTriedLayers.find(pszName) != aoSetAlreadyTriedLayers.end()) + continue; + aoSetAlreadyTriedLayers.insert(pszName); + +#if USE_GET_FOR_DESCRIBE_FEATURE_TYPE == 1 + if (nLayersToFetch > 0) + osLayerToFetch += ","; + osLayerToFetch += papoLayers[i]->GetName(); +#else + osTypeNameToPost += " "; + osTypeNameToPost += pszName; + osTypeNameToPost += "\n"; +#endif + nLayersToFetch ++; + + /* Avoid fetching to many layer definition at a time */ + if (nLayersToFetch >= 50) + break; + } + } + } + + CPLFree(pszPrefix); + pszPrefix = NULL; + +#if USE_GET_FOR_DESCRIBE_FEATURE_TYPE == 1 + CPLString osURL(osBaseURL); + osURL = CPLURLAddKVP(osURL, "SERVICE", "WFS"); + osURL = CPLURLAddKVP(osURL, "VERSION", GetVersion()); + osURL = CPLURLAddKVP(osURL, "REQUEST", "DescribeFeatureType"); + osURL = CPLURLAddKVP(osURL, "TYPENAME", WFS_EscapeURL(osLayerToFetch)); + osURL = CPLURLAddKVP(osURL, "PROPERTYNAME", NULL); + osURL = CPLURLAddKVP(osURL, "MAXFEATURES", NULL); + osURL = CPLURLAddKVP(osURL, "FILTER", NULL); + osURL = CPLURLAddKVP(osURL, "OUTPUTFORMAT", pszRequiredOutputFormat ? WFS_EscapeURL(pszRequiredOutputFormat).c_str() : NULL); + + if (pszNS && GetNeedNAMESPACE()) + { + /* Older Deegree version require NAMESPACE */ + /* This has been now corrected */ + CPLString osValue("xmlns("); + osValue += pszNS; + osValue += "="; + osValue += pszNSVal; + osValue += ")"; + osURL = CPLURLAddKVP(osURL, "NAMESPACE", WFS_EscapeURL(osValue)); + } + + CPLHTTPResult* psResult = HTTPFetch( osURL, NULL); +#else + CPLString osPost; + osPost += "\n"; + osPost += "GetRequiredOutputFormat(); + if (pszRequiredOutputFormat) + { + osPost += "\n"; + osPost += " outputFormat=\""; + osPost += pszRequiredOutputFormat; + osPost += "\""; + } + osPost += ">\n"; + osPost += osTypeNameToPost; + osPost += "\n"; + + //CPLDebug("WFS", "%s", osPost.c_str()); + + char** papszOptions = NULL; + papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str()); + papszOptions = CSLAddNameValue(papszOptions, "HEADERS", + "Content-Type: application/xml; charset=UTF-8"); + + CPLHTTPResult* psResult = HTTPFetch(GetPostTransactionURL(), papszOptions); + CSLDestroy(papszOptions); +#endif + + if (psResult == NULL) + { + bLoadMultipleLayerDefn = FALSE; + return; + } + + if (strstr((const char*)psResult->pabyData, "pabyData)) + { + /* just silently forgive */ + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s", + psResult->pabyData); + } + CPLHTTPDestroyResult(psResult); + bLoadMultipleLayerDefn = FALSE; + return; + } + + CPLXMLNode* psXML = CPLParseXMLString( (const char*) psResult->pabyData ); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + bLoadMultipleLayerDefn = FALSE; + return; + } + CPLHTTPDestroyResult(psResult); + + CPLXMLNode* psSchema = WFSFindNode(psXML, "schema"); + if (psSchema == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find "); + CPLDestroyXMLNode( psXML ); + bLoadMultipleLayerDefn = FALSE; + return; + } + + CPLString osTmpFileName; + + osTmpFileName = CPLSPrintf("/vsimem/tempwfs_%p/file.xsd", this); + CPLSerializeXMLTreeToFile(psSchema, osTmpFileName); + + std::vector aosClasses; + int bFullyUnderstood = FALSE; + GMLParseXSD( osTmpFileName, aosClasses, bFullyUnderstood ); + + int nLayersFound = 0; + if ((int)aosClasses.size() > 0) + { + std::vector::const_iterator iter = aosClasses.begin(); + std::vector::const_iterator eiter = aosClasses.end(); + while (iter != eiter) + { + GMLFeatureClass* poClass = *iter; + iter ++; + + OGRWFSLayer* poLayer; + + if (bKeepLayerNamePrefix && pszNS != NULL && strchr(poClass->GetName(), ':') == NULL) + { + CPLString osWithPrefix(pszNS); + osWithPrefix += ":"; + osWithPrefix += poClass->GetName(); + poLayer = (OGRWFSLayer* )GetLayerByName(osWithPrefix); + } + else + poLayer = (OGRWFSLayer* )GetLayerByName(poClass->GetName()); + + if (poLayer) + { + if (!poLayer->HasLayerDefn()) + { + nLayersFound ++; + + CPLXMLNode* psSchemaForLayer = CPLCloneXMLTree(psSchema); + CPLStripXMLNamespace( psSchemaForLayer, NULL, TRUE ); + CPLXMLNode* psIter = psSchemaForLayer->psChild; + int bHasAlreadyImportedGML = FALSE; + int bFoundComplexType = FALSE; + int bFoundElement = FALSE; + while(psIter != NULL) + { + CPLXMLNode* psIterNext = psIter->psNext; + if (psIter->eType == CXT_Element && + strcmp(psIter->pszValue,"complexType") == 0) + { + const char* pszName = CPLGetXMLValue(psIter, "name", ""); + CPLString osExpectedName(poLayer->GetShortName()); + osExpectedName += "Type"; + CPLString osExpectedName2(poLayer->GetShortName()); + osExpectedName2 += "_Type"; + if (strcmp(pszName, osExpectedName) == 0 || + strcmp(pszName, osExpectedName2) == 0 || + strcmp(pszName, poLayer->GetShortName()) == 0) + { + bFoundComplexType = TRUE; + } + else + { + CPLRemoveXMLChild( psSchemaForLayer, psIter ); + CPLDestroyXMLNode(psIter); + } + } + else if (psIter->eType == CXT_Element && + strcmp(psIter->pszValue,"element") == 0) + { + const char* pszName = CPLGetXMLValue(psIter, "name", ""); + CPLString osExpectedName(poLayer->GetShortName()); + osExpectedName += "Type"; + CPLString osExpectedName2(poLayer->GetShortName()); + osExpectedName2 += "_Type"; + + const char* pszType = CPLGetXMLValue(psIter, "type", ""); + CPLString osExpectedType(poLayer->GetName()); + osExpectedType += "Type"; + CPLString osExpectedType2(poLayer->GetName()); + osExpectedType2 += "_Type"; + if (strcmp(pszType, osExpectedType) == 0 || + strcmp(pszType, osExpectedType2) == 0 || + strcmp(pszType, poLayer->GetName()) == 0 || + (strchr(pszType, ':') && + (strcmp(strchr(pszType, ':') + 1, osExpectedType) == 0 || + strcmp(strchr(pszType, ':') + 1, osExpectedType2) == 0))) + { + bFoundElement = TRUE; + } + else if (*pszType == '\0' && + CPLGetXMLNode(psIter, "complexType") != NULL && + (strcmp(pszName, osExpectedName) == 0 || + strcmp(pszName, osExpectedName2) == 0 || + strcmp(pszName, poLayer->GetShortName()) == 0) ) + { + bFoundElement = TRUE; + bFoundComplexType = TRUE; + } + else + { + CPLRemoveXMLChild( psSchemaForLayer, psIter ); + CPLDestroyXMLNode(psIter); + } + } + else if (psIter->eType == CXT_Element && + strcmp(psIter->pszValue,"import") == 0 && + strcmp(CPLGetXMLValue(psIter, "namespace", ""), + "http://www.opengis.net/gml") == 0) + { + if (bHasAlreadyImportedGML) + { + CPLRemoveXMLChild( psSchemaForLayer, psIter ); + CPLDestroyXMLNode(psIter); + } + else + bHasAlreadyImportedGML = TRUE; + } + psIter = psIterNext; + } + + if (bFoundComplexType && bFoundElement) + { + OGRFeatureDefn* poSrcFDefn = poLayer->ParseSchema(psSchemaForLayer); + if (poSrcFDefn) + { + poLayer->BuildLayerDefn(poSrcFDefn); + SaveLayerSchema(poLayer->GetName(), psSchemaForLayer); + } + } + + CPLDestroyXMLNode(psSchemaForLayer); + } + else + { + CPLDebug("WFS", "Found several time schema for layer %s in server response. Shouldn't happen", + poClass->GetName()); + } + } + delete poClass; + } + } + + if (nLayersFound != nLayersToFetch) + { + CPLDebug("WFS", "Turn off loading of multiple layer definitions at a single time"); + bLoadMultipleLayerDefn = FALSE; + } + + VSIUnlink(osTmpFileName); + + CPLDestroyXMLNode( psXML ); +} + +/************************************************************************/ +/* SaveLayerSchema() */ +/************************************************************************/ + +void OGRWFSDataSource::SaveLayerSchema(const char* pszLayerName, CPLXMLNode* psSchema) +{ + if (psFileXML != NULL) + { + bRewriteFile = TRUE; + CPLXMLNode* psLayerNode = CPLCreateXMLNode(NULL, CXT_Element, "OGRWFSLayer"); + CPLSetXMLValue(psLayerNode, "#name", pszLayerName); + CPLAddXMLChild(psLayerNode, CPLCloneXMLTree(psSchema)); + CPLAddXMLChild(psFileXML, psLayerNode); + } +} + +/************************************************************************/ +/* IsOldDeegree() */ +/************************************************************************/ + +int OGRWFSDataSource::IsOldDeegree(const char* pszErrorString) +{ + if (!bNeedNAMESPACE && + strstr(pszErrorString, "Invalid \"TYPENAME\" parameter. No binding for prefix") != NULL) + { + bNeedNAMESPACE = TRUE; + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* WFS_EscapeURL() */ +/************************************************************************/ + +CPLString WFS_EscapeURL(const char* pszURL) +{ + CPLString osEscapedURL; + + /* Difference with CPLEscapeString(, CPLES_URL) : we do not escape */ + /* colon (:) or comma (,). Causes problems with servers such as http://www.mapinfo.com/miwfs? */ + + for( int i = 0; pszURL[i] != '\0' ; i++ ) + { + char ch = pszURL[i]; + if( (ch >= 'a' && ch <= 'z') + || (ch >= 'A' && ch <= 'Z') + || (ch >= '0' && ch <= '9') + || ch == '_' || ch == '.' + || ch == ':' || ch == ',' ) + { + osEscapedURL += ch; + } + else + { + char szPercentEncoded[10]; + sprintf( szPercentEncoded, "%%%02X", ((unsigned char*)pszURL)[i] ); + osEscapedURL += szPercentEncoded; + } + } + + return osEscapedURL; +} + +/************************************************************************/ +/* WFS_DecodeURL() */ +/************************************************************************/ + +CPLString WFS_DecodeURL(const CPLString &osSrc) +{ + CPLString ret; + char ch; + int ii; + for (size_t i=0; i(ii); + ret+=ch; + i=i+2; + } + else + { + ret+=osSrc[i]; + } + } + return (ret); +} + +/************************************************************************/ +/* HTTPFetch() */ +/************************************************************************/ + +CPLHTTPResult* OGRWFSDataSource::HTTPFetch( const char* pszURL, char** papszOptions ) +{ + char** papszNewOptions = CSLDuplicate(papszOptions); + if (bUseHttp10) + papszNewOptions = CSLAddNameValue(papszNewOptions, "HTTP_VERSION", "1.0"); + if (papszHttpOptions) + papszNewOptions = CSLMerge(papszNewOptions, papszHttpOptions); + CPLHTTPResult* psResult = CPLHTTPFetch( pszURL, papszNewOptions ); + CSLDestroy(papszNewOptions); + + if (psResult == NULL) + { + return NULL; + } + if (psResult->nStatus != 0 || psResult->pszErrBuf != NULL) + { + /* A few buggy servers return chunked data with errouneous remaining bytes value */ + /* curl doesn't like this. Retry with HTTP 1.0 protocol instead that doesn't support */ + /* chunked data */ + if (psResult->pszErrBuf && + strstr(psResult->pszErrBuf, "transfer closed with outstanding read data remaining") && + !bUseHttp10) + { + CPLDebug("WFS", "Probably buggy remote server. Retrying with HTTP 1.0 protocol"); + bUseHttp10 = TRUE; + CPLHTTPDestroyResult(psResult); + return HTTPFetch(pszURL, papszOptions); + } + + CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s (%d)", + (psResult->pszErrBuf) ? psResult->pszErrBuf : "unknown", psResult->nStatus); + CPLHTTPDestroyResult(psResult); + return NULL; + } + if (psResult->pabyData == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Empty content returned by server"); + CPLHTTPDestroyResult(psResult); + return NULL; + } + return psResult; +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +OGRLayer * OGRWFSDataSource::ExecuteSQL( const char *pszSQLCommand, + OGRGeometry *poSpatialFilter, + const char *pszDialect ) + +{ + swq_select_parse_options oParseOptions; + oParseOptions.poCustomFuncRegistrar = WFSGetCustomFuncRegistrar(); + +/* -------------------------------------------------------------------- */ +/* Use generic implementation for recognized dialects */ +/* -------------------------------------------------------------------- */ + if( IsGenericSQLDialect(pszDialect) ) + { + OGRLayer* poResLayer = GDALDataset::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect, + &oParseOptions ); + oMap[poResLayer] = NULL; + return poResLayer; + } + +/* -------------------------------------------------------------------- */ +/* Deal with "SELECT _LAST_INSERTED_FIDS_ FROM layername" statement */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand, "SELECT _LAST_INSERTED_FIDS_ FROM ", 33) ) + { + const char* pszIter = pszSQLCommand + 33; + while(*pszIter && *pszIter != ' ') + pszIter ++; + + CPLString osName = pszSQLCommand + 33; + osName.resize(pszIter - (pszSQLCommand + 33)); + OGRWFSLayer* poLayer = (OGRWFSLayer*)GetLayerByName(osName); + if (poLayer == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unknown layer : %s", osName.c_str()); + return NULL; + } + + GDALDriver* poMEMDrv = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("Memory"); + if (poMEMDrv == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot load 'Memory' driver"); + return NULL; + } + + GDALDataset* poMEMDS = poMEMDrv->Create("dummy_name", 0, 0, 0, GDT_Unknown, NULL); + OGRLayer* poMEMLayer = poMEMDS->CreateLayer("FID_LIST", NULL, wkbNone, NULL); + OGRFieldDefn oFDefn("gml_id", OFTString); + poMEMLayer->CreateField(&oFDefn); + + const std::vector& aosFIDList = poLayer->GetLastInsertedFIDList(); + std::vector::const_iterator iter = aosFIDList.begin(); + std::vector::const_iterator eiter = aosFIDList.end(); + while (iter != eiter) + { + const CPLString& osFID = *iter; + OGRFeature* poFeature = new OGRFeature(poMEMLayer->GetLayerDefn()); + poFeature->SetField(0, osFID); + poMEMLayer->CreateFeature(poFeature); + delete poFeature; + iter ++; + } + + OGRLayer* poResLayer = new OGRWFSWrappedResultLayer(poMEMDS, poMEMLayer); + oMap[poResLayer] = NULL; + return poResLayer; + } + +/* -------------------------------------------------------------------- */ +/* Deal with "DELETE FROM layer_name WHERE expression" statement */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszSQLCommand, "DELETE FROM ", 12) ) + { + const char* pszIter = pszSQLCommand + 12; + while(*pszIter && *pszIter != ' ') + pszIter ++; + if (*pszIter == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid statement"); + return NULL; + } + + CPLString osName = pszSQLCommand + 12; + osName.resize(pszIter - (pszSQLCommand + 12)); + OGRWFSLayer* poLayer = (OGRWFSLayer*)GetLayerByName(osName); + if (poLayer == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Unknown layer : %s", osName.c_str()); + return NULL; + } + + while(*pszIter && *pszIter == ' ') + pszIter ++; + if (!EQUALN(pszIter, "WHERE ", 5)) + { + CPLError(CE_Failure, CPLE_AppDefined, "WHERE clause missing"); + return NULL; + } + pszIter += 5; + + const char* pszQuery = pszIter; + + /* Check with the generic SQL engine that this is a valid WHERE clause */ + OGRFeatureQuery oQuery; + OGRErr eErr = oQuery.Compile( poLayer->GetLayerDefn(), pszQuery ); + if( eErr != OGRERR_NONE ) + { + return NULL; + } + + /* Now turn this into OGC Filter language if possible */ + int bNeedsNullCheck = FALSE; + int nVersion = (strcmp(GetVersion(),"1.0.0") == 0) ? 100 : 110; + swq_expr_node* poNode = (swq_expr_node*) oQuery.GetSWQExpr(); + poNode->ReplaceBetweenByGEAndLERecurse(); + CPLString osOGCFilter = WFS_TurnSQLFilterToOGCFilter(poNode, + NULL, + poLayer->GetLayerDefn(), + nVersion, + bPropertyIsNotEqualToSupported, + bUseFeatureId, + bGmlObjectIdNeedsGMLPrefix, + "", + &bNeedsNullCheck); + if (bNeedsNullCheck && !HasNullCheck()) + osOGCFilter = ""; + + if (osOGCFilter.size() == 0) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot convert WHERE clause into a OGC filter"); + return NULL; + } + + poLayer->DeleteFromFilter(osOGCFilter); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Deal with "SELECT xxxx ORDER BY" statement */ +/* -------------------------------------------------------------------- */ + if (EQUALN(pszSQLCommand, "SELECT", 6)) + { + swq_select* psSelectInfo = new swq_select(); + if( psSelectInfo->preparse( pszSQLCommand, TRUE ) != CPLE_None ) + { + delete psSelectInfo; + return NULL; + } + int iLayer; + if( strcmp(GetVersion(),"1.0.0") != 0 && + psSelectInfo->table_count == 1 && + psSelectInfo->table_defs[0].data_source == NULL && + (iLayer = GetLayerIndex( psSelectInfo->table_defs[0].table_name )) >= 0 && + psSelectInfo->join_count == 0 && + psSelectInfo->order_specs > 0 && + psSelectInfo->poOtherSelect == NULL ) + { + OGRWFSLayer* poSrcLayer = papoLayers[iLayer]; + std::vector aoSortColumns; + int i; + for(i=0;iorder_specs;i++) + { + int nFieldIndex = poSrcLayer->GetLayerDefn()->GetFieldIndex( + psSelectInfo->order_defs[i].field_name); + if (poSrcLayer->HasGotApproximateLayerDefn() || nFieldIndex < 0) + break; + + /* Make sure to have the right case */ + const char* pszFieldName = poSrcLayer->GetLayerDefn()-> + GetFieldDefn(nFieldIndex)->GetNameRef(); + + OGRWFSSortDesc oSortDesc(pszFieldName, + psSelectInfo->order_defs[i].ascending_flag); + aoSortColumns.push_back(oSortDesc); + } + + if( i == psSelectInfo->order_specs ) + { + OGRWFSLayer* poDupLayer = poSrcLayer->Clone(); + + poDupLayer->SetOrderBy(aoSortColumns); + int nBackup = psSelectInfo->order_specs; + psSelectInfo->order_specs = 0; + char* pszSQLWithoutOrderBy = psSelectInfo->Unparse(); + CPLDebug("WFS", "SQL without ORDER BY: %s", pszSQLWithoutOrderBy); + psSelectInfo->order_specs = nBackup; + delete psSelectInfo; + psSelectInfo = NULL; + + /* Just set poDupLayer in the papoLayers for the time of the */ + /* base ExecuteSQL(), so that the OGRGenSQLResultsLayer references */ + /* that temporary layer */ + papoLayers[iLayer] = poDupLayer; + + OGRLayer* poResLayer = GDALDataset::ExecuteSQL( pszSQLWithoutOrderBy, + poSpatialFilter, + pszDialect, + &oParseOptions ); + papoLayers[iLayer] = poSrcLayer; + + CPLFree(pszSQLWithoutOrderBy); + + if (poResLayer != NULL) + oMap[poResLayer] = poDupLayer; + else + delete poDupLayer; + return poResLayer; + } + } + else if( bStandardJoinsWFS2 && + psSelectInfo->join_count > 0 && + psSelectInfo->poOtherSelect == NULL ) + { + // Just to make sure everything is valid, but we won't use + // that one as we want to run the join on server-side + oParseOptions.bAllowFieldsInSecondaryTablesInWhere = TRUE; + oParseOptions.bAddSecondaryTablesGeometryFields = TRUE; + oParseOptions.bAlwaysPrefixWithTableName = TRUE; + oParseOptions.bAllowDistinctOnGeometryField = TRUE; + oParseOptions.bAllowDistinctOnMultipleFields = TRUE; + GDALSQLParseInfo* psParseInfo = BuildParseInfo(psSelectInfo, + &oParseOptions); + oParseOptions.bAllowFieldsInSecondaryTablesInWhere = FALSE; + oParseOptions.bAddSecondaryTablesGeometryFields = FALSE; + oParseOptions.bAlwaysPrefixWithTableName = FALSE; + oParseOptions.bAllowDistinctOnGeometryField = FALSE; + oParseOptions.bAllowDistinctOnMultipleFields = FALSE; + int bOK = psParseInfo != NULL; + DestroyParseInfo(psParseInfo); + + OGRLayer* poResLayer = NULL; + if( bOK ) + { + poResLayer = OGRWFSJoinLayer::Build(this, psSelectInfo); + oMap[poResLayer] = NULL; + } + + delete psSelectInfo; + return poResLayer; + } + + delete psSelectInfo; + } + + OGRLayer* poResLayer = OGRDataSource::ExecuteSQL( pszSQLCommand, + poSpatialFilter, + pszDialect, + &oParseOptions ); + oMap[poResLayer] = NULL; + return poResLayer; +} + +/************************************************************************/ +/* ReleaseResultSet() */ +/************************************************************************/ + +void OGRWFSDataSource::ReleaseResultSet( OGRLayer * poResultsSet ) +{ + if (poResultsSet == NULL) + return; + + std::map::iterator oIter = oMap.find(poResultsSet); + if (oIter != oMap.end()) + { + /* Destroy first the result layer, because it still references */ + /* the poDupLayer (oIter->second) */ + delete poResultsSet; + + delete oIter->second; + oMap.erase(oIter); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Trying to destroy an invalid result set !"); + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfsdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfsdriver.cpp new file mode 100644 index 000000000..e308a47cc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfsdriver.cpp @@ -0,0 +1,127 @@ +/****************************************************************************** + * $Id $ + * + * Project: WFS Translator + * Purpose: Implements OGRWFSDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_wfs.h" +#include "cpl_conv.h" + +// g++ -fPIC -g -Wall ogr/ogrsf_frmts/wfs/*.cpp -shared -o ogr_WFS.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/gml -Iogr/ogrsf_frmts/wfs -L. -lgdal + +CPL_CVSID("$Id: ogrwfsdriver.cpp 29273 2015-06-02 08:08:38Z rouault $"); + +extern "C" void RegisterOGRWFS(); + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +static int OGRWFSDriverIdentify( GDALOpenInfo* poOpenInfo ) + +{ + if( !EQUALN(poOpenInfo->pszFilename, "WFS:", 4) ) + { + if( poOpenInfo->fpL == NULL ) + return FALSE; + if( !EQUALN((const char*)poOpenInfo->pabyHeader,"",18) && + strstr((const char*)poOpenInfo->pabyHeader,"pabyHeader,"Open( poOpenInfo->pszFilename, + poOpenInfo->eAccess == GA_Update, + poOpenInfo->papszOpenOptions ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* RegisterOGRWFS() */ +/************************************************************************/ + +void RegisterOGRWFS() + +{ + GDALDriver *poDriver; + + if( GDALGetDriverByName( "WFS" ) == NULL ) + { + poDriver = new GDALDriver(); + + poDriver->SetDescription( "WFS" ); + poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "OGC WFS (Web Feature Service)" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_wfs.html" ); + + poDriver->SetMetadataItem( GDAL_DMD_CONNECTION_PREFIX, "WFS:" ); + + poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST, +"" +" " +" " ); + + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + + poDriver->pfnIdentify = OGRWFSDriverIdentify; + poDriver->pfnOpen = OGRWFSDriverOpen; + + GetGDALDriverManager()->RegisterDriver( poDriver ); + } +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfsfilter.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfsfilter.cpp new file mode 100644 index 000000000..cfb91b724 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfsfilter.cpp @@ -0,0 +1,812 @@ +/****************************************************************************** + * $Id: ogrwfsfilter.cpp 29029 2015-04-26 21:22:57Z rouault $ + * + * Project: WFS Translator + * Purpose: Implements OGR SQL into OGC Filter translation. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_wfs.h" +#include "ogr_p.h" + +CPL_CVSID("$Id: ogrwfsfilter.cpp 29029 2015-04-26 21:22:57Z rouault $"); + +typedef struct +{ + int nVersion; + int bPropertyIsNotEqualToSupported; + int bOutNeedsNullCheck; + OGRDataSource* poDS; + OGRFeatureDefn* poFDefn; + int nUniqueGeomGMLId; + OGRSpatialReference* poSRS; + const char* pszNSPrefix; +} ExprDumpFilterOptions; + +/************************************************************************/ +/* WFS_ExprDumpGmlObjectIdFilter() */ +/************************************************************************/ + +static int WFS_ExprDumpGmlObjectIdFilter(CPLString& osFilter, + const swq_expr_node* poExpr, + int bUseFeatureId, + int bGmlObjectIdNeedsGMLPrefix, + int nVersion) +{ + if (poExpr->eNodeType == SNT_OPERATION && + poExpr->nOperation == SWQ_EQ && + poExpr->nSubExprCount == 2 && + poExpr->papoSubExpr[0]->eNodeType == SNT_COLUMN && + strcmp(poExpr->papoSubExpr[0]->string_value, "gml_id") == 0 && + poExpr->papoSubExpr[1]->eNodeType == SNT_CONSTANT) + { + if (bUseFeatureId) + osFilter += "= 200) + osFilter += "papoSubExpr[1]->field_type == SWQ_INTEGER || + poExpr->papoSubExpr[1]->field_type == SWQ_INTEGER64 ) + osFilter += CPLSPrintf(CPL_FRMT_GIB, poExpr->papoSubExpr[1]->int_value); + else if( poExpr->papoSubExpr[1]->field_type == SWQ_STRING ) + { + char* pszXML = CPLEscapeString(poExpr->papoSubExpr[1]->string_value, -1, CPLES_XML); + osFilter += pszXML; + CPLFree(pszXML); + } + else + return FALSE; + osFilter += "\"/>"; + return TRUE; + } + else if (poExpr->eNodeType == SNT_OPERATION && + poExpr->nOperation == SWQ_OR && + poExpr->nSubExprCount == 2 ) + { + return WFS_ExprDumpGmlObjectIdFilter(osFilter, poExpr->papoSubExpr[0], + bUseFeatureId, bGmlObjectIdNeedsGMLPrefix, nVersion) && + WFS_ExprDumpGmlObjectIdFilter(osFilter, poExpr->papoSubExpr[1], + bUseFeatureId, bGmlObjectIdNeedsGMLPrefix, nVersion); + } + return FALSE; +} + +/************************************************************************/ +/* WFS_ExprDumpRawLitteral() */ +/************************************************************************/ + +static int WFS_ExprDumpRawLitteral(CPLString& osFilter, + const swq_expr_node* poExpr) +{ + if( poExpr->field_type == SWQ_INTEGER || + poExpr->field_type == SWQ_INTEGER64 ) + osFilter += CPLSPrintf(CPL_FRMT_GIB, poExpr->int_value); + else if( poExpr->field_type == SWQ_FLOAT ) + osFilter += CPLSPrintf("%.16g", poExpr->float_value); + else if( poExpr->field_type == SWQ_STRING ) + { + char* pszXML = CPLEscapeString(poExpr->string_value, -1, CPLES_XML); + osFilter += pszXML; + CPLFree(pszXML); + } + else if( poExpr->field_type == SWQ_TIMESTAMP ) + { + OGRField sDate; + if( !OGRParseDate(poExpr->string_value, &sDate, 0) ) + return FALSE; + char* pszDate = OGRGetXMLDateTime(&sDate); + osFilter += pszDate; + CPLFree(pszDate); + } + else + return FALSE; + return TRUE; +} + +/************************************************************************/ +/* WFS_ExprGetSRSName() */ +/************************************************************************/ + +static const char* WFS_ExprGetSRSName( const swq_expr_node* poExpr, + int iSubArgIndex, + ExprDumpFilterOptions* psOptions, + OGRSpatialReference& oSRS ) +{ + if( poExpr->nSubExprCount == iSubArgIndex + 1 && + poExpr->papoSubExpr[iSubArgIndex]->field_type == SWQ_STRING ) + { + if( oSRS.SetFromUserInput(poExpr->papoSubExpr[iSubArgIndex]->string_value) == OGRERR_NONE ) + { + return poExpr->papoSubExpr[iSubArgIndex]->string_value; + } + } + else if ( poExpr->nSubExprCount == iSubArgIndex + 1 && + poExpr->papoSubExpr[iSubArgIndex]->field_type == SWQ_INTEGER ) + { + if( oSRS.importFromEPSGA((int)poExpr->papoSubExpr[iSubArgIndex]->int_value) == OGRERR_NONE ) + { + return CPLSPrintf("urn:ogc:def:crs:EPSG::%d", (int)poExpr->papoSubExpr[iSubArgIndex]->int_value); + } + } + else if( poExpr->nSubExprCount == iSubArgIndex && + psOptions->poSRS != NULL ) + { + if( psOptions->poSRS->GetAuthorityName(NULL) && + EQUAL(psOptions->poSRS->GetAuthorityName(NULL), "EPSG") && + psOptions->poSRS->GetAuthorityCode(NULL) && + oSRS.importFromEPSGA(atoi(psOptions->poSRS->GetAuthorityCode(NULL))) == OGRERR_NONE ) + { + return CPLSPrintf("urn:ogc:def:crs:EPSG::%s", psOptions->poSRS->GetAuthorityCode(NULL)); + } + } + return NULL; +} + +/************************************************************************/ +/* WFS_ExprDumpAsOGCFilter() */ +/************************************************************************/ + +static int WFS_ExprDumpAsOGCFilter(CPLString& osFilter, + const swq_expr_node* poExpr, + int bExpectBinary, + ExprDumpFilterOptions* psOptions) +{ + if( poExpr->eNodeType == SNT_COLUMN ) + { + if (bExpectBinary) + return FALSE; + + /* Special fields not understood by server */ + if (EQUAL(poExpr->string_value, "gml_id") || + EQUAL(poExpr->string_value, "FID") || + EQUAL(poExpr->string_value, "OGR_GEOMETRY") || + EQUAL(poExpr->string_value, "OGR_GEOM_WKT") || + EQUAL(poExpr->string_value, "OGR_GEOM_AREA") || + EQUAL(poExpr->string_value, "OGR_STYLE")) + { + CPLDebug("WFS", "Attribute refers to a OGR special field. Cannot use server-side filtering"); + return FALSE; + } + + const char* pszFieldname = NULL; + int nIndex; + int bSameTable = psOptions->poFDefn != NULL && + ( poExpr->table_name == NULL || + EQUAL(poExpr->table_name, psOptions->poFDefn->GetName()) ); + if( bSameTable ) + { + if( (nIndex = psOptions->poFDefn->GetFieldIndex(poExpr->string_value)) >= 0 ) + { + pszFieldname = psOptions->poFDefn->GetFieldDefn(nIndex)->GetNameRef(); + } + else if( (nIndex = psOptions->poFDefn->GetGeomFieldIndex(poExpr->string_value)) >= 0 ) + { + pszFieldname = psOptions->poFDefn->GetGeomFieldDefn(nIndex)->GetNameRef(); + } + } + else if( psOptions->poDS != NULL ) + { + OGRLayer* poLayer = psOptions->poDS->GetLayerByName(poExpr->table_name); + if( poLayer ) + { + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + if( (nIndex = poFDefn->GetFieldIndex(poExpr->string_value)) >= 0 ) + { + pszFieldname = CPLSPrintf("%s/%s", + poLayer->GetName(), + poFDefn->GetFieldDefn(nIndex)->GetNameRef()); + } + else if( (nIndex = poFDefn->GetGeomFieldIndex(poExpr->string_value)) >= 0 ) + { + pszFieldname = CPLSPrintf("%s/%s", + poLayer->GetName(), + poFDefn->GetGeomFieldDefn(nIndex)->GetNameRef()); + } + } + } + + if( psOptions->poFDefn == NULL && psOptions->poDS == NULL ) + pszFieldname = poExpr->string_value; + + if( pszFieldname == NULL ) + { + if( poExpr->table_name != NULL ) + CPLDebug("WFS", "Field \"%s\".\"%s\" unknown. Cannot use server-side filtering", + poExpr->table_name, poExpr->string_value); + else + CPLDebug("WFS", "Field \"%s\" unknown. Cannot use server-side filtering", + poExpr->string_value); + return FALSE; + } + + if (psOptions->nVersion >= 200) + osFilter += CPLSPrintf("<%sValueReference>", psOptions->pszNSPrefix); + else + osFilter += CPLSPrintf("<%sPropertyName>", psOptions->pszNSPrefix); + char* pszFieldnameXML = CPLEscapeString(pszFieldname, -1, CPLES_XML); + osFilter += pszFieldnameXML; + CPLFree(pszFieldnameXML); + if (psOptions->nVersion >= 200) + osFilter += CPLSPrintf("", psOptions->pszNSPrefix); + else + osFilter += CPLSPrintf("", psOptions->pszNSPrefix); + + return TRUE; + } + + if( poExpr->eNodeType == SNT_CONSTANT ) + { + if (bExpectBinary) + return FALSE; + + osFilter += CPLSPrintf("<%sLiteral>", psOptions->pszNSPrefix); + if( !WFS_ExprDumpRawLitteral(osFilter, poExpr) ) + return FALSE; + osFilter += CPLSPrintf("", psOptions->pszNSPrefix); + + return TRUE; + } + + if( poExpr->eNodeType != SNT_OPERATION ) + return FALSE; /* shouldn't happen */ + + if( poExpr->nOperation == SWQ_NOT ) + { + osFilter += CPLSPrintf("<%sNot>", psOptions->pszNSPrefix); + if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[0], TRUE, psOptions)) + return FALSE; + osFilter += CPLSPrintf("", psOptions->pszNSPrefix); + return TRUE; + } + + if( poExpr->nOperation == SWQ_LIKE ) + { + CPLString osVal; + char ch; + char firstCh = 0; + int i; + if (psOptions->nVersion == 100) + osFilter += CPLSPrintf("<%sPropertyIsLike wildCard='*' singleChar='_' escape='!'>", psOptions->pszNSPrefix); + else + osFilter += CPLSPrintf("<%sPropertyIsLike wildCard='*' singleChar='_' escapeChar='!'>", psOptions->pszNSPrefix); + if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[0], FALSE, psOptions)) + return FALSE; + if (poExpr->papoSubExpr[1]->eNodeType != SNT_CONSTANT && + poExpr->papoSubExpr[1]->field_type != SWQ_STRING) + return FALSE; + osFilter += CPLSPrintf("<%sLiteral>", psOptions->pszNSPrefix); + + /* Escape value according to above special characters */ + /* For URL compatibility reason, we remap the OGR SQL '%' wildchard into '*' */ + i = 0; + ch = poExpr->papoSubExpr[1]->string_value[i]; + if (ch == '\'' || ch == '"') + { + firstCh = ch; + i ++; + } + for(;(ch = poExpr->papoSubExpr[1]->string_value[i]) != '\0';i++) + { + if (ch == '%') + osVal += "*"; + else if (ch == '!') + osVal += "!!"; + else if (ch == '*') + osVal += "!*"; + else if (ch == firstCh && poExpr->papoSubExpr[1]->string_value[i + 1] == 0) + break; + else + { + char ach[2]; + ach[0] = ch; + ach[1] = 0; + osVal += ach; + } + } + char* pszXML = CPLEscapeString(osVal, -1, CPLES_XML); + osFilter += pszXML; + CPLFree(pszXML); + osFilter += CPLSPrintf("", psOptions->pszNSPrefix); + osFilter += CPLSPrintf("", psOptions->pszNSPrefix); + return TRUE; + } + + if( poExpr->nOperation == SWQ_ISNULL ) + { + osFilter += CPLSPrintf("<%sPropertyIsNull>", psOptions->pszNSPrefix); + if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[0], FALSE, psOptions)) + return FALSE; + osFilter += CPLSPrintf("", psOptions->pszNSPrefix); + psOptions->bOutNeedsNullCheck = TRUE; + return TRUE; + } + + if( poExpr->nOperation == SWQ_EQ || + poExpr->nOperation == SWQ_NE || + poExpr->nOperation == SWQ_LE || + poExpr->nOperation == SWQ_LT || + poExpr->nOperation == SWQ_GE || + poExpr->nOperation == SWQ_GT ) + { + int nOperation = poExpr->nOperation; + int bAddClosingNot = FALSE; + if (!psOptions->bPropertyIsNotEqualToSupported && nOperation == SWQ_NE) + { + osFilter += CPLSPrintf("<%sNot>", psOptions->pszNSPrefix); + nOperation = SWQ_EQ; + bAddClosingNot = TRUE; + } + + const char* pszName = NULL; + switch(nOperation) + { + case SWQ_EQ: pszName = "PropertyIsEqualTo"; break; + case SWQ_NE: pszName = "PropertyIsNotEqualTo"; break; + case SWQ_LE: pszName = "PropertyIsLessThanOrEqualTo"; break; + case SWQ_LT: pszName = "PropertyIsLessThan"; break; + case SWQ_GE: pszName = "PropertyIsGreaterThanOrEqualTo"; break; + case SWQ_GT: pszName = "PropertyIsGreaterThan"; break; + default: break; + } + osFilter += "<"; + osFilter += psOptions->pszNSPrefix; + osFilter += pszName; + osFilter += ">"; + if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[0], FALSE, psOptions)) + return FALSE; + if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[1], FALSE, psOptions)) + return FALSE; + osFilter += "pszNSPrefix; + osFilter += pszName; + osFilter += ">"; + if (bAddClosingNot) + osFilter += CPLSPrintf("", psOptions->pszNSPrefix); + return TRUE; + } + + if( poExpr->nOperation == SWQ_AND || + poExpr->nOperation == SWQ_OR ) + { + const char* pszName = (poExpr->nOperation == SWQ_AND) ? "And" : "Or"; + osFilter += "<"; + osFilter += psOptions->pszNSPrefix; + osFilter += pszName; + osFilter += ">"; + if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[0], TRUE, psOptions)) + return FALSE; + if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[1], TRUE, psOptions)) + return FALSE; + osFilter += "pszNSPrefix; + osFilter += pszName; + osFilter += ">"; + return TRUE; + } + + if( poExpr->nOperation == SWQ_CUSTOM_FUNC && + EQUAL(poExpr->string_value, "ST_MakeEnvelope") ) + { + OGRSpatialReference oSRS; + const char* pszSRSName = WFS_ExprGetSRSName( poExpr, 4, psOptions, oSRS ); + int bAxisSwap = FALSE; + + osFilter += "papoSubExpr[(bAxisSwap) ? 1 : 0])) + return FALSE; + osFilter += " "; + if (!WFS_ExprDumpRawLitteral(osFilter, poExpr->papoSubExpr[(bAxisSwap) ? 0 : 1])) + return FALSE; + osFilter += ""; + osFilter += ""; + if (!WFS_ExprDumpRawLitteral(osFilter, poExpr->papoSubExpr[(bAxisSwap) ? 3 : 2])) + return FALSE; + osFilter += " "; + if (!WFS_ExprDumpRawLitteral(osFilter, poExpr->papoSubExpr[(bAxisSwap) ? 2 : 3])) + return FALSE; + osFilter += ""; + osFilter += ""; + return TRUE; + } + + if( poExpr->nOperation == SWQ_CUSTOM_FUNC && + EQUAL(poExpr->string_value, "ST_GeomFromText") ) + { + OGRSpatialReference oSRS; + const char* pszSRSName = WFS_ExprGetSRSName( poExpr, 1, psOptions, oSRS ); + OGRGeometry* poGeom = NULL; + char* pszWKT = (char*)poExpr->papoSubExpr[0]->string_value; + OGRGeometryFactory::createFromWkt(&pszWKT, NULL, &poGeom); + char** papszOptions = NULL; + papszOptions = CSLSetNameValue(papszOptions, "FORMAT", "GML3"); + if( pszSRSName != NULL ) + { + if( oSRS.EPSGTreatsAsLatLong() || oSRS.EPSGTreatsAsNorthingEasting() ) + { + OGR_SRSNode *poGEOGCS = oSRS.GetAttrNode( "GEOGCS" ); + if( poGEOGCS != NULL ) + poGEOGCS->StripNodes( "AXIS" ); + + OGR_SRSNode *poPROJCS = oSRS.GetAttrNode( "PROJCS" ); + if (poPROJCS != NULL && oSRS.EPSGTreatsAsNorthingEasting()) + poPROJCS->StripNodes( "AXIS" ); + } + + if( EQUALN(pszSRSName, "urn:ogc:def:crs:EPSG::", strlen("urn:ogc:def:crs:EPSG::")) ) + papszOptions = CSLSetNameValue(papszOptions, "GML3_LONGSRS", "YES"); + else + papszOptions = CSLSetNameValue(papszOptions, "GML3_LONGSRS", "NO"); + + poGeom->assignSpatialReference(&oSRS); + } + papszOptions = CSLSetNameValue(papszOptions, "GMLID", + CPLSPrintf("id%d", psOptions->nUniqueGeomGMLId ++)); + char* pszGML = OGR_G_ExportToGMLEx( (OGRGeometryH)poGeom, papszOptions ); + osFilter += pszGML; + CSLDestroy(papszOptions); + delete poGeom; + CPLFree(pszGML); + return TRUE; + } + + if( poExpr->nOperation == SWQ_CUSTOM_FUNC ) + { + const char* pszName = + EQUAL(poExpr->string_value, "ST_Equals") ? "Equals" : + EQUAL(poExpr->string_value, "ST_Disjoint") ? "Disjoint" : + EQUAL(poExpr->string_value, "ST_Touches") ? "Touches" : + EQUAL(poExpr->string_value, "ST_Contains") ? "Contains" : + EQUAL(poExpr->string_value, "ST_Intersects") ? "Intersects" : + EQUAL(poExpr->string_value, "ST_Within") ? "Within" : + EQUAL(poExpr->string_value, "ST_Crosses") ? "Crosses" : + EQUAL(poExpr->string_value, "ST_Overlaps") ? "Overlaps" : + EQUAL(poExpr->string_value, "ST_DWithin") ? "DWithin" : + EQUAL(poExpr->string_value, "ST_Beyond") ? "Beyond" : + NULL; + if( pszName == NULL ) + return FALSE; + osFilter += "<"; + osFilter += psOptions->pszNSPrefix; + osFilter += pszName; + osFilter += ">"; + for(int i=0;i<2;i++) + { + if( i == 1 && poExpr->papoSubExpr[0]->eNodeType == SNT_COLUMN && + poExpr->papoSubExpr[1]->eNodeType == SNT_OPERATION && + poExpr->papoSubExpr[1]->nOperation == SWQ_CUSTOM_FUNC && + (EQUAL(poExpr->papoSubExpr[1]->string_value, "ST_GeomFromText") || + EQUAL(poExpr->papoSubExpr[1]->string_value, "ST_MakeEnvelope")) ) + { + int bSameTable = psOptions->poFDefn != NULL && + ( poExpr->papoSubExpr[0]->table_name == NULL || + EQUAL(poExpr->papoSubExpr[0]->table_name, psOptions->poFDefn->GetName()) ); + if( bSameTable ) + { + int nIndex; + if( (nIndex = psOptions->poFDefn->GetGeomFieldIndex(poExpr->papoSubExpr[0]->string_value)) >= 0 ) + { + psOptions->poSRS = psOptions->poFDefn->GetGeomFieldDefn(nIndex)->GetSpatialRef(); + } + } + else if( psOptions->poDS != NULL ) + { + OGRLayer* poLayer = psOptions->poDS->GetLayerByName(poExpr->papoSubExpr[0]->table_name); + if( poLayer ) + { + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + int nIndex; + if( (nIndex = poFDefn->GetGeomFieldIndex(poExpr->papoSubExpr[0]->string_value)) >= 0 ) + { + psOptions->poSRS = poFDefn->GetGeomFieldDefn(nIndex)->GetSpatialRef(); + } + } + } + } + int bRet = WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[i], FALSE, psOptions); + psOptions->poSRS = NULL; + if( !bRet ) + return FALSE; + } + if( poExpr->nSubExprCount > 2 ) + { + osFilter += CPLSPrintf("<%sDistance unit=\"m\">", psOptions->pszNSPrefix); + if (!WFS_ExprDumpRawLitteral(osFilter, poExpr->papoSubExpr[2]) ) + return FALSE; + osFilter += CPLSPrintf("", psOptions->pszNSPrefix); + } + osFilter += "pszNSPrefix; + osFilter += pszName; + osFilter += ">"; + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* WFS_TurnSQLFilterToOGCFilter() */ +/************************************************************************/ + +CPLString WFS_TurnSQLFilterToOGCFilter( const swq_expr_node* poExpr, + OGRDataSource* poDS, + OGRFeatureDefn* poFDefn, + int nVersion, + int bPropertyIsNotEqualToSupported, + int bUseFeatureId, + int bGmlObjectIdNeedsGMLPrefix, + const char* pszNSPrefix, + int* pbOutNeedsNullCheck ) +{ + CPLString osFilter; + /* If the filter is only made of querying one or several gml_id */ + /* (with OR operator), we turn this to list */ + if (!WFS_ExprDumpGmlObjectIdFilter(osFilter, poExpr, bUseFeatureId, + bGmlObjectIdNeedsGMLPrefix, nVersion)) + { + ExprDumpFilterOptions sOptions; + sOptions.nVersion = nVersion; + sOptions.bPropertyIsNotEqualToSupported = bPropertyIsNotEqualToSupported; + sOptions.bOutNeedsNullCheck = FALSE; + sOptions.poDS = poDS; + sOptions.poFDefn = poFDefn; + sOptions.nUniqueGeomGMLId = 1; + sOptions.poSRS = NULL; + sOptions.pszNSPrefix = pszNSPrefix; + osFilter = ""; + if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr, TRUE, &sOptions)) + osFilter = ""; + /*else + CPLDebug("WFS", "Filter %s", osFilter.c_str());*/ + *pbOutNeedsNullCheck = sOptions.bOutNeedsNullCheck; + } + + return osFilter; +} + +/************************************************************************/ +/* OGRWFSSpatialBooleanPredicateChecker() */ +/************************************************************************/ + +static swq_field_type OGRWFSSpatialBooleanPredicateChecker( swq_expr_node *op, + CPL_UNUSED int bAllowMismatchTypeOnFieldComparison ) +{ + if( op->nSubExprCount != 2 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong number of arguments for %s", + op->string_value); + return SWQ_ERROR; + } + for(int i=0;inSubExprCount;i++) + { + if( op->papoSubExpr[i]->field_type != SWQ_GEOMETRY ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for argument %d of %s", + i+1, op->string_value); + return SWQ_ERROR; + } + } + return SWQ_BOOLEAN; +} + +/************************************************************************/ +/* OGRWFSCheckSRIDArg() */ +/************************************************************************/ + +static int OGRWFSCheckSRIDArg( swq_expr_node *op, int iSubArgIndex ) +{ + if( op->papoSubExpr[iSubArgIndex]->field_type == SWQ_INTEGER ) + { + OGRSpatialReference oSRS; + if( oSRS.importFromEPSGA((int)op->papoSubExpr[iSubArgIndex]->int_value) != OGRERR_NONE ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong value for argument %d of %s", + iSubArgIndex + 1, op->string_value); + return FALSE; + } + } + else if( op->papoSubExpr[iSubArgIndex]->field_type == SWQ_STRING ) + { + OGRSpatialReference oSRS; + if( oSRS.SetFromUserInput(op->papoSubExpr[iSubArgIndex]->string_value) != OGRERR_NONE ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong value for argument %d of %s", + iSubArgIndex + 1, op->string_value); + return FALSE; + } + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for argument %d of %s", + iSubArgIndex + 1, op->string_value); + return FALSE; + } + return TRUE; +} + +/************************************************************************/ +/* OGRWFSMakeEnvelopeChecker() */ +/************************************************************************/ + +static swq_field_type OGRWFSMakeEnvelopeChecker( swq_expr_node *op, + CPL_UNUSED int bAllowMismatchTypeOnFieldComparison ) +{ + if( op->nSubExprCount != 4 && op->nSubExprCount != 5 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong number of arguments for %s", + op->string_value); + return SWQ_ERROR; + } + for(int i=0;i<4;i++) + { + if( op->papoSubExpr[i]->field_type != SWQ_INTEGER && + op->papoSubExpr[i]->field_type != SWQ_INTEGER64 && + op->papoSubExpr[i]->field_type != SWQ_FLOAT ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for argument %d of %s", + i+1, op->string_value); + return SWQ_ERROR; + } + } + if( op->nSubExprCount == 5 ) + { + if( !OGRWFSCheckSRIDArg( op, 4 ) ) + return SWQ_ERROR; + } + return SWQ_GEOMETRY; +} + +/************************************************************************/ +/* OGRWFSGeomFromTextChecker() */ +/************************************************************************/ + +static swq_field_type OGRWFSGeomFromTextChecker( swq_expr_node *op, + CPL_UNUSED int bAllowMismatchTypeOnFieldComparison ) +{ + if( op->nSubExprCount != 1&& op->nSubExprCount != 2 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong number of arguments for %s", + op->string_value); + return SWQ_ERROR; + } + if( op->papoSubExpr[0]->field_type != SWQ_STRING ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for argument %d of %s", + 1, op->string_value); + return SWQ_ERROR; + } + OGRGeometry* poGeom = NULL; + char* pszWKT = (char*)op->papoSubExpr[0]->string_value; + if( OGRGeometryFactory::createFromWkt(&pszWKT, NULL, &poGeom) != OGRERR_NONE ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong value for argument %d of %s", + 1, op->string_value); + return SWQ_ERROR; + } + delete poGeom; + if( op->nSubExprCount == 2 ) + { + if( !OGRWFSCheckSRIDArg( op, 1 ) ) + return SWQ_ERROR; + } + return SWQ_GEOMETRY; +} + +/************************************************************************/ +/* OGRWFSDWithinBeyondChecker() */ +/************************************************************************/ + +static swq_field_type OGRWFSDWithinBeyondChecker( swq_expr_node *op, + CPL_UNUSED int bAllowMismatchTypeOnFieldComparison ) +{ + if( op->nSubExprCount != 3 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong number of arguments for %s", + op->string_value); + return SWQ_ERROR; + } + for(int i=0;i<2;i++) + { + if( op->papoSubExpr[i]->field_type != SWQ_GEOMETRY ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for argument %d of %s", + i+1, op->string_value); + return SWQ_ERROR; + } + } + if( op->papoSubExpr[2]->field_type != SWQ_INTEGER && + op->papoSubExpr[2]->field_type != SWQ_INTEGER64 && + op->papoSubExpr[2]->field_type != SWQ_FLOAT ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for argument %d of %s", + 2+1, op->string_value); + return SWQ_ERROR; + } + return SWQ_BOOLEAN; +} + +/************************************************************************/ +/* OGRWFSCustomFuncRegistrar */ +/************************************************************************/ + +class OGRWFSCustomFuncRegistrar: public swq_custom_func_registrar +{ + public: + OGRWFSCustomFuncRegistrar() {}; + virtual const swq_operation *GetOperator( const char * ) ; +}; + +/************************************************************************/ +/* WFSGetCustomFuncRegistrar() */ +/************************************************************************/ + +swq_custom_func_registrar* WFSGetCustomFuncRegistrar() +{ + static OGRWFSCustomFuncRegistrar obj; + return &obj; +} + +/************************************************************************/ +/* GetOperator() */ +/************************************************************************/ + +static const swq_operation OGRWFSSpatialOps[] = { +{ "ST_Equals", SWQ_CUSTOM_FUNC, NULL, OGRWFSSpatialBooleanPredicateChecker }, +{ "ST_Disjoint", SWQ_CUSTOM_FUNC, NULL, OGRWFSSpatialBooleanPredicateChecker }, +{ "ST_Touches", SWQ_CUSTOM_FUNC, NULL, OGRWFSSpatialBooleanPredicateChecker }, +{ "ST_Contains", SWQ_CUSTOM_FUNC, NULL, OGRWFSSpatialBooleanPredicateChecker }, +/*{ "ST_Covers", SWQ_CUSTOM_FUNC, NULL, OGRWFSSpatialBooleanPredicateChecker },*/ +{ "ST_Intersects", SWQ_CUSTOM_FUNC, NULL, OGRWFSSpatialBooleanPredicateChecker }, +{ "ST_Within", SWQ_CUSTOM_FUNC, NULL, OGRWFSSpatialBooleanPredicateChecker }, +/*{ "ST_CoveredBy", SWQ_CUSTOM_FUNC, NULL, OGRWFSSpatialBooleanPredicateChecker },*/ +{ "ST_Crosses", SWQ_CUSTOM_FUNC, NULL, OGRWFSSpatialBooleanPredicateChecker }, +{ "ST_Overlaps", SWQ_CUSTOM_FUNC, NULL, OGRWFSSpatialBooleanPredicateChecker }, +{ "ST_DWithin", SWQ_CUSTOM_FUNC, NULL, OGRWFSDWithinBeyondChecker }, +{ "ST_Beyond", SWQ_CUSTOM_FUNC, NULL, OGRWFSDWithinBeyondChecker }, +{ "ST_MakeEnvelope", SWQ_CUSTOM_FUNC, NULL, OGRWFSMakeEnvelopeChecker }, +{ "ST_GeomFromText", SWQ_CUSTOM_FUNC, NULL, OGRWFSGeomFromTextChecker } +}; + +const swq_operation *OGRWFSCustomFuncRegistrar::GetOperator( const char * pszName ) +{ + for(int i=0; i< (int)(sizeof(OGRWFSSpatialOps)/sizeof(OGRWFSSpatialOps[0]));i++) + { + if( EQUAL(OGRWFSSpatialOps[i].pszName, pszName) ) + return &OGRWFSSpatialOps[i]; + } + return NULL; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfsjoinlayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfsjoinlayer.cpp new file mode 100644 index 000000000..3c655ec7a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfsjoinlayer.cpp @@ -0,0 +1,801 @@ +/****************************************************************************** + * $Id: ogrwfsjoinlayer.cpp 29064 2015-04-30 08:04:34Z rouault $ + * + * Project: WFS Translator + * Purpose: Implements OGRWFSJoinLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2015, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_wfs.h" +#include "../../frmts/wms/md5.h" + +CPL_CVSID("$Id: ogrwfsjoinlayer.cpp 29064 2015-04-30 08:04:34Z rouault $"); + +/************************************************************************/ +/* OGRWFSJoinLayer() */ +/************************************************************************/ + +OGRWFSJoinLayer::OGRWFSJoinLayer(OGRWFSDataSource* poDS, + const swq_select* psSelectInfo, + const CPLString& osGlobalFilter) +{ + this->poDS = poDS; + this->osGlobalFilter = osGlobalFilter; + poFeatureDefn = NULL; + bPagingActive = FALSE; + nPagingStartIndex = 0; + nFeatureRead = 0; + nFeatureCountRequested = 0; + poBaseDS = NULL; + poBaseLayer = NULL; + bReloadNeeded = FALSE; + bHasFetched = FALSE; + bDistinct = (psSelectInfo->query_mode == SWQM_DISTINCT_LIST); + + CPLString osName("join_"); + CPLString osLayerName; + osLayerName = psSelectInfo->table_defs[0].table_name; + apoLayers.push_back((OGRWFSLayer*)poDS->GetLayerByName(osLayerName)); + osName += osLayerName; + for(int i=0;ijoin_count;i++) + { + osName += "_"; + osLayerName = psSelectInfo->table_defs[ + psSelectInfo->join_defs[i].secondary_table].table_name; + apoLayers.push_back((OGRWFSLayer*)poDS->GetLayerByName(osLayerName)); + osName += osLayerName; + } + + osFeatureTypes = "("; + for(int i=0;i<(int)apoLayers.size();i++) + { + if( i > 0 ) + osFeatureTypes += ","; + osFeatureTypes += apoLayers[i]->GetName(); + } + osFeatureTypes += ")"; + + SetDescription(osName); + + poFeatureDefn = new OGRFeatureDefn(GetDescription()); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType(wkbNone); + + for( int i = 0; i < psSelectInfo->result_columns; i++ ) + { + swq_col_def *def = psSelectInfo->column_defs + i; + int table_index; + if( def->table_index >= 0 ) + table_index = def->table_index; + else + { + CPLAssert(def->expr->eNodeType == SNT_OPERATION && def->expr->nOperation == SWQ_CAST); + table_index = def->expr->papoSubExpr[0]->table_index; + } + OGRWFSLayer* poLayer = apoLayers[table_index]; + const char* pszTableAlias = psSelectInfo->table_defs[table_index].table_alias; + const char* pszTablePrefix = pszTableAlias ? pszTableAlias : poLayer->GetShortName(); + int idx = poLayer->GetLayerDefn()->GetFieldIndex(def->field_name); + if( idx >= 0 ) + { + OGRFieldDefn oFieldDefn(poLayer->GetLayerDefn()->GetFieldDefn(idx)); + const char* pszSrcFieldname = CPLSPrintf("%s.%s", + poLayer->GetShortName(), + oFieldDefn.GetNameRef()); + const char* pszFieldname = CPLSPrintf("%s.%s", + pszTablePrefix, + oFieldDefn.GetNameRef()); + aoSrcFieldNames.push_back(pszSrcFieldname); + oFieldDefn.SetName(def->field_alias ? def->field_alias : pszFieldname); + if( def->expr && def->expr->eNodeType == SNT_OPERATION && def->expr->nOperation == SWQ_CAST) + { + switch( def->field_type ) + { + case SWQ_INTEGER: oFieldDefn.SetType(OFTInteger); break; + case SWQ_INTEGER64: oFieldDefn.SetType(OFTInteger64); break; + case SWQ_FLOAT: oFieldDefn.SetType(OFTReal); break; + case SWQ_STRING: oFieldDefn.SetType(OFTString); break; + case SWQ_BOOLEAN: oFieldDefn.SetType(OFTInteger); oFieldDefn.SetSubType(OFSTBoolean); break; + case SWQ_DATE: oFieldDefn.SetType(OFTDate); break; + case SWQ_TIME: oFieldDefn.SetType(OFTTime); break; + case SWQ_TIMESTAMP: oFieldDefn.SetType(OFTDateTime); break; + default: break; + } + } + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + else + { + idx = poLayer->GetLayerDefn()->GetGeomFieldIndex(def->field_name); + if (idx >= 0 ) + { + OGRGeomFieldDefn oFieldDefn(poLayer->GetLayerDefn()->GetGeomFieldDefn(idx)); + const char* pszSrcFieldname = CPLSPrintf("%s.%s", + poLayer->GetShortName(), + oFieldDefn.GetNameRef()); + const char* pszFieldname = CPLSPrintf("%s.%s", + pszTablePrefix, + oFieldDefn.GetNameRef()); + aoSrcGeomFieldNames.push_back(pszSrcFieldname); + oFieldDefn.SetName(def->field_alias ? def->field_alias : pszFieldname); + poFeatureDefn->AddGeomFieldDefn(&oFieldDefn); + } + } + } + + for(int i=0;iorder_specs;i++) + { + int nFieldIndex = apoLayers[0]->GetLayerDefn()->GetFieldIndex( + psSelectInfo->order_defs[i].field_name); + if (nFieldIndex < 0) + break; + + /* Make sure to have the right case */ + const char* pszFieldName = apoLayers[0]->GetLayerDefn()-> + GetFieldDefn(nFieldIndex)->GetNameRef(); + if( osSortBy.size() ) + osSortBy += ","; + osSortBy += pszFieldName; + if( !psSelectInfo->order_defs[i].ascending_flag ) + osSortBy += " DESC"; + } + + CPLXMLNode* psGlobalSchema = CPLCreateXMLNode( NULL, CXT_Element, "Schema" ); + for(int i=0;i<(int)apoLayers.size();i++) + { + CPLString osTmpFileName = CPLSPrintf("/vsimem/tempwfs_%p/file.xsd", apoLayers[i]); + CPLPushErrorHandler(CPLQuietErrorHandler); + CPLXMLNode* psSchema = CPLParseXMLFile(osTmpFileName); + CPLPopErrorHandler(); + if( psSchema == NULL ) + { + CPLDestroyXMLNode(psGlobalSchema); + psGlobalSchema = NULL; + break; + } + CPLXMLNode* psIter; + for(psIter = psSchema->psChild; psIter != NULL; psIter = psIter->psNext ) + { + if( psIter->eType == CXT_Element ) + break; + } + CPLAddXMLChild(psGlobalSchema, CPLCloneXMLTree(psIter)); + CPLDestroyXMLNode(psSchema); + } + if( psGlobalSchema ) + { + CPLString osTmpFileName = CPLSPrintf("/vsimem/tempwfs_%p/file.xsd", this); + CPLSerializeXMLTreeToFile(psGlobalSchema, osTmpFileName); + CPLDestroyXMLNode(psGlobalSchema); + } +} + +/************************************************************************/ +/* ~OGRWFSJoinLayer() */ +/************************************************************************/ + +OGRWFSJoinLayer::~OGRWFSJoinLayer() +{ + if( poFeatureDefn != NULL ) + poFeatureDefn->Release(); + if( poBaseDS != NULL ) + GDALClose(poBaseDS); + + CPLString osTmpDirName = CPLSPrintf("/vsimem/tempwfs_%p", this); + OGRWFSRecursiveUnlink(osTmpDirName); +} + +/************************************************************************/ +/* OGRWFSRemoveReferenceToTableAlias() */ +/************************************************************************/ + +static void OGRWFSRemoveReferenceToTableAlias(swq_expr_node* poNode, + const swq_select* psSelectInfo) +{ + if( poNode->eNodeType == SNT_COLUMN ) + { + if( poNode->table_name != NULL ) + { + for(int i=0;i < psSelectInfo->table_count;i++) + { + if( psSelectInfo->table_defs[i].table_alias != NULL && + EQUAL(poNode->table_name, psSelectInfo->table_defs[i].table_alias) ) + { + CPLFree(poNode->table_name); + poNode->table_name = CPLStrdup(psSelectInfo->table_defs[i].table_name); + break; + } + } + } + } + else if( poNode->eNodeType == SNT_OPERATION ) + { + for(int i=0;i < poNode->nSubExprCount;i++) + OGRWFSRemoveReferenceToTableAlias(poNode->papoSubExpr[i], + psSelectInfo); + } +} + +/************************************************************************/ +/* Build() */ +/************************************************************************/ + +OGRWFSJoinLayer* OGRWFSJoinLayer::Build(OGRWFSDataSource* poDS, + const swq_select* psSelectInfo) +{ + CPLString osGlobalFilter; + + for( int i = 0; i < psSelectInfo->result_columns; i++ ) + { + swq_col_def *def = psSelectInfo->column_defs + i; + if( !(def->col_func == SWQCF_NONE && + (def->expr == NULL || + def->expr->eNodeType == SNT_COLUMN || + (def->expr->eNodeType == SNT_OPERATION && def->expr->nOperation == SWQ_CAST))) ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Only column names supported in column selection"); + return NULL; + } + } + + if( psSelectInfo->join_count > 1 || psSelectInfo->where_expr != NULL ) + osGlobalFilter += ""; + for(int i=0;ijoin_count;i++) + { + OGRWFSRemoveReferenceToTableAlias(psSelectInfo->join_defs[i].poExpr, + psSelectInfo); + int bOutNeedsNullCheck; + CPLString osFilter = WFS_TurnSQLFilterToOGCFilter( + psSelectInfo->join_defs[i].poExpr, + poDS, + NULL, + 200, + TRUE, /* bPropertyIsNotEqualToSupported */ + FALSE, /* bUseFeatureId */ + FALSE, /* bGmlObjectIdNeedsGMLPrefix */ + "", + &bOutNeedsNullCheck); + if( osFilter.size() == 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported JOIN clause"); + return NULL; + } + osGlobalFilter += osFilter; + } + if( psSelectInfo->where_expr != NULL ) + { + OGRWFSRemoveReferenceToTableAlias(psSelectInfo->where_expr, + psSelectInfo); + int bOutNeedsNullCheck; + CPLString osFilter = WFS_TurnSQLFilterToOGCFilter( + psSelectInfo->where_expr, + poDS, + NULL, + 200, + TRUE, /* bPropertyIsNotEqualToSupported */ + FALSE, /* bUseFeatureId */ + FALSE, /* bGmlObjectIdNeedsGMLPrefix */ + "", + &bOutNeedsNullCheck); + if( osFilter.size() == 0 ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Unsupported WHERE clause"); + return NULL; + } + osGlobalFilter += osFilter; + } + if( psSelectInfo->join_count > 1 || psSelectInfo->where_expr != NULL ) + osGlobalFilter += ""; + CPLDebug("WFS", "osGlobalFilter = %s", osGlobalFilter.c_str()); + + OGRWFSJoinLayer* poLayer = new OGRWFSJoinLayer(poDS, psSelectInfo, + osGlobalFilter); + return poLayer; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRWFSJoinLayer::ResetReading() +{ + if (bPagingActive) + bReloadNeeded = TRUE; + nPagingStartIndex = 0; + nFeatureRead = 0; + nFeatureCountRequested = 0; + if (bReloadNeeded) + { + GDALClose(poBaseDS); + poBaseDS = NULL; + poBaseLayer = NULL; + bHasFetched = FALSE; + bReloadNeeded = FALSE; + } + if (poBaseLayer) + poBaseLayer->ResetReading(); + aoSetMD5.clear(); +} + +/************************************************************************/ +/* MakeGetFeatureURL() */ +/************************************************************************/ + +CPLString OGRWFSJoinLayer::MakeGetFeatureURL(int bRequestHits) +{ + CPLString osURL(poDS->GetBaseURL()); + osURL = CPLURLAddKVP(osURL, "SERVICE", "WFS"); + osURL = CPLURLAddKVP(osURL, "VERSION", poDS->GetVersion()); + osURL = CPLURLAddKVP(osURL, "REQUEST", "GetFeature"); + osURL = CPLURLAddKVP(osURL, "TYPENAMES", WFS_EscapeURL(osFeatureTypes)); + + int nRequestMaxFeatures = 0; + if (poDS->IsPagingAllowed() && !bRequestHits && + CPLURLGetValue(osURL, "COUNT").size() == 0 ) + { + osURL = CPLURLAddKVP(osURL, "STARTINDEX", + CPLSPrintf("%d", nPagingStartIndex + + poDS->GetBaseStartIndex())); + nRequestMaxFeatures = poDS->GetPageSize(); + nFeatureCountRequested = nRequestMaxFeatures; + bPagingActive = TRUE; + } + + if (nRequestMaxFeatures) + { + osURL = CPLURLAddKVP(osURL, + "COUNT", + CPLSPrintf("%d", nRequestMaxFeatures)); + } + + CPLString osFilter; + osFilter = " oMapNS; + for(int i=0;i<(int)apoLayers.size();i++) + { + const char* pszNS = apoLayers[i]->GetNamespacePrefix(); + const char* pszNSVal = apoLayers[i]->GetNamespaceName(); + if( pszNS && pszNSVal ) + oMapNS[pszNS] = pszNSVal; + } + std::map::iterator oIter = oMapNS.begin(); + for(; oIter != oMapNS.end(); ++oIter ) + { + osFilter += " xmlns:"; + osFilter += oIter->first; + osFilter += "=\""; + osFilter += oIter->second; + osFilter += "\""; + } + osFilter += " xmlns:gml=\"http://www.opengis.net/gml/3.2\">"; + osFilter += osGlobalFilter; + osFilter += ""; + + osURL = CPLURLAddKVP(osURL, "FILTER", WFS_EscapeURL(osFilter)); + + if (bRequestHits) + { + osURL = CPLURLAddKVP(osURL, "RESULTTYPE", "hits"); + } + else if( osSortBy.size() ) + { + osURL = CPLURLAddKVP(osURL, "SORTBY", WFS_EscapeURL(osSortBy)); + } + + return osURL; +} + +/************************************************************************/ +/* FetchGetFeature() */ +/************************************************************************/ + +GDALDataset* OGRWFSJoinLayer::FetchGetFeature() +{ + + CPLString osURL = MakeGetFeatureURL(); + CPLDebug("WFS", "%s", osURL.c_str()); + + CPLHTTPResult* psResult = NULL; + + /* Try streaming when the output format is GML and that we have a .xsd */ + /* that we are able to understand */ + CPLString osXSDFileName = CPLSPrintf("/vsimem/tempwfs_%p/file.xsd", this); + VSIStatBufL sBuf; + if (CSLTestBoolean(CPLGetConfigOption("OGR_WFS_USE_STREAMING", "YES")) && + VSIStatL(osXSDFileName, &sBuf) == 0 && GDALGetDriverByName("GML") != NULL) + { + const char* pszStreamingName = CPLSPrintf("/vsicurl_streaming/%s", + osURL.c_str()); + if( strncmp(osURL, "/vsimem/", strlen("/vsimem/")) == 0 && + CSLTestBoolean(CPLGetConfigOption("CPL_CURL_ENABLE_VSIMEM", "FALSE")) ) + { + pszStreamingName = osURL.c_str(); + } + + const char* const apszAllowedDrivers[] = { "GML", NULL }; + const char* apszOpenOptions[2] = { NULL, NULL }; + apszOpenOptions[0] = CPLSPrintf("XSD=%s", osXSDFileName.c_str()); + GDALDataset* poGML_DS = (GDALDataset*) + GDALOpenEx(pszStreamingName, GDAL_OF_VECTOR, apszAllowedDrivers, + apszOpenOptions, NULL); + if (poGML_DS) + { + //bStreamingDS = TRUE; + return poGML_DS; + } + + /* In case of failure, read directly the content to examine */ + /* it, if it is XML error content */ + char szBuffer[2048]; + int nRead = 0; + VSILFILE* fp = VSIFOpenL(pszStreamingName, "rb"); + if (fp) + { + nRead = (int)VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fp); + szBuffer[nRead] = '\0'; + VSIFCloseL(fp); + } + + if (nRead != 0) + { + if (strstr(szBuffer, "HTTPFetch( osURL, NULL); + if (psResult == NULL) + { + return NULL; + } + + CPLString osTmpDirName = CPLSPrintf("/vsimem/tempwfs_%p", this); + VSIMkdir(osTmpDirName, 0); + + GByte *pabyData = psResult->pabyData; + int nDataLen = psResult->nDataLen; + + if (strstr((const char*)pabyData, "pabyData = NULL; + + CPLHTTPDestroyResult(psResult); + + OGRDataSource* poDS; + + poDS = (OGRDataSource*) OGROpen(osTmpFileName, FALSE, NULL); + if (poDS == NULL) + { + if( strstr((const char*)pabyData, " 1000) + pabyData[1000] = 0; + CPLError(CE_Failure, CPLE_AppDefined, + "Error: cannot parse %s", pabyData); + } + return NULL; + } + + OGRLayer* poLayer = poDS->GetLayer(0); + if (poLayer == NULL) + { + OGRDataSource::DestroyDataSource(poDS); + return NULL; + } + + return poDS; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature* OGRWFSJoinLayer::GetNextFeature() +{ + while(TRUE) + { + if (bPagingActive && nFeatureRead == nPagingStartIndex + nFeatureCountRequested) + { + bReloadNeeded = TRUE; + nPagingStartIndex = nFeatureRead; + } + if (bReloadNeeded) + { + GDALClose(poBaseDS); + poBaseDS = NULL; + poBaseLayer = NULL; + bHasFetched = FALSE; + bReloadNeeded = FALSE; + } + if (poBaseDS == NULL && !bHasFetched) + { + bHasFetched = TRUE; + poBaseDS = FetchGetFeature(); + if (poBaseDS) + { + poBaseLayer = poBaseDS->GetLayer(0); + poBaseLayer->ResetReading(); + } + } + if (!poBaseLayer) + return NULL; + + OGRFeature* poSrcFeature = poBaseLayer->GetNextFeature(); + if (poSrcFeature == NULL) + return NULL; + nFeatureRead ++; + + OGRFeature* poNewFeature = new OGRFeature(poFeatureDefn); + + struct cvs_MD5Context sMD5Context; + if( bDistinct ) + cvs_MD5Init(&sMD5Context); + + for(int i=0;i<(int)aoSrcFieldNames.size();i++) + { + int iSrcField = poSrcFeature->GetFieldIndex(aoSrcFieldNames[i]); + if( iSrcField >= 0 && poSrcFeature->IsFieldSet(iSrcField) ) + { + OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType(); + if( eType == poSrcFeature->GetFieldDefnRef(iSrcField)->GetType() ) + { + poNewFeature->SetField(i, poSrcFeature->GetRawFieldRef(iSrcField)); + } + else if( eType == OFTString ) + poNewFeature->SetField(i, poSrcFeature->GetFieldAsString(iSrcField)); + else if( eType == OFTInteger ) + poNewFeature->SetField(i, poSrcFeature->GetFieldAsInteger(iSrcField)); + else if( eType == OFTInteger64 ) + poNewFeature->SetField(i, poSrcFeature->GetFieldAsInteger64(iSrcField)); + else if( eType == OFTReal ) + poNewFeature->SetField(i, poSrcFeature->GetFieldAsDouble(iSrcField)); + else + poNewFeature->SetField(i, poSrcFeature->GetFieldAsString(iSrcField)); + if( bDistinct ) + { + if( eType == OFTInteger ) + { + int nVal = poNewFeature->GetFieldAsInteger(i); + cvs_MD5Update( &sMD5Context, (const GByte*)&nVal, sizeof(nVal)); + } + else if( eType == OFTInteger64 ) + { + GIntBig nVal = poNewFeature->GetFieldAsInteger64(i); + cvs_MD5Update( &sMD5Context, (const GByte*)&nVal, sizeof(nVal)); + } + else if( eType == OFTReal ) + { + double dfVal = poNewFeature->GetFieldAsDouble(i); + cvs_MD5Update( &sMD5Context, (const GByte*)&dfVal, sizeof(dfVal)); + } + else + { + const char* pszStr = poNewFeature->GetFieldAsString(i); + cvs_MD5Update( &sMD5Context, (const GByte*)pszStr, strlen(pszStr)); + } + } + } + } + for(int i=0;i<(int)aoSrcGeomFieldNames.size();i++) + { + int iSrcField = poSrcFeature->GetGeomFieldIndex(aoSrcGeomFieldNames[i]); + if( iSrcField >= 0) + { + OGRGeometry* poGeom = poSrcFeature->StealGeometry(iSrcField); + if( poGeom ) + { + poGeom->assignSpatialReference(poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef()); + poNewFeature->SetGeomFieldDirectly(i, poGeom); + + if( bDistinct ) + { + int nSize = poGeom->WkbSize(); + GByte* pabyGeom = (GByte*)CPLMalloc(nSize); + poGeom->exportToWkb(wkbNDR, pabyGeom); + cvs_MD5Update( &sMD5Context, (const GByte*)pabyGeom, nSize); + CPLFree(pabyGeom); + } + } + } + } + + poNewFeature->SetFID(nFeatureRead); + delete poSrcFeature; + + if( bDistinct ) + { + CPLString osDigest = "0123456789abcdef"; + cvs_MD5Final((unsigned char*)osDigest.c_str(), &sMD5Context); + if( aoSetMD5.find(osDigest) == aoSetMD5.end() ) + { + aoSetMD5.insert(osDigest); + return poNewFeature; + } + else + delete poNewFeature; + } + else + return poNewFeature; + } +} + +/************************************************************************/ +/* ExecuteGetFeatureResultTypeHits() */ +/************************************************************************/ + +GIntBig OGRWFSJoinLayer::ExecuteGetFeatureResultTypeHits() +{ + char* pabyData = NULL; + CPLString osURL = MakeGetFeatureURL(TRUE); + CPLDebug("WFS", "%s", osURL.c_str()); + + CPLHTTPResult* psResult = poDS->HTTPFetch( osURL, NULL); + if (psResult == NULL) + { + return -1; + } + + pabyData = (char*) psResult->pabyData; + psResult->pabyData = NULL; + + if (strstr(pabyData, ""); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + CPLFree(pabyData); + return -1; + } + + const char* pszValue = CPLGetXMLValue(psRoot, "numberMatched", NULL); /* WFS 2.0.0 */ + if (pszValue == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find numberMatched"); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + CPLFree(pabyData); + return -1; + } + + GIntBig nFeatures = CPLAtoGIntBig(pszValue); + + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + CPLFree(pabyData); + + return nFeatures; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRWFSJoinLayer::GetFeatureCount( int bForce ) +{ + GIntBig nFeatures; + + if( !bDistinct ) + { + nFeatures = ExecuteGetFeatureResultTypeHits(); + if (nFeatures >= 0) + return nFeatures; + } + + nFeatures = OGRLayer::GetFeatureCount(bForce); + return nFeatures; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn* OGRWFSJoinLayer::GetLayerDefn() +{ + return poFeatureDefn; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRWFSJoinLayer::TestCapability( const char * ) +{ + return FALSE; +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRWFSJoinLayer::SetSpatialFilter( OGRGeometry * poGeom ) +{ + if( poGeom != NULL ) + CPLError(CE_Failure, CPLE_NotSupported, + "Setting a spatial filter on a layer resulting from a WFS join is unsupported"); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRWFSJoinLayer::SetAttributeFilter( const char *pszFilter ) +{ + if( pszFilter != NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Setting an attribute filter on a layer resulting from a WFS join is unsupported"); + return OGRERR_FAILURE; + } + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfslayer.cpp new file mode 100644 index 000000000..2944bfd27 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/wfs/ogrwfslayer.cpp @@ -0,0 +1,2653 @@ +/****************************************************************************** + * $Id: ogrwfslayer.cpp 29273 2015-06-02 08:08:38Z rouault $ + * + * Project: WFS Translator + * Purpose: Implements OGRWFSLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_port.h" +#include "ogr_wfs.h" +#include "ogr_api.h" +#include "cpl_minixml.h" +#include "cpl_http.h" +#include "parsexsd.h" + +CPL_CVSID("$Id: ogrwfslayer.cpp 29273 2015-06-02 08:08:38Z rouault $"); + + +/************************************************************************/ +/* OGRWFSRecursiveUnlink() */ +/************************************************************************/ + +void OGRWFSRecursiveUnlink( const char *pszName ) + +{ + char **papszFileList; + int i; + + papszFileList = CPLReadDir( pszName ); + + for( i = 0; papszFileList != NULL && papszFileList[i] != NULL; i++ ) + { + VSIStatBufL sStatBuf; + + if( EQUAL(papszFileList[i],".") || EQUAL(papszFileList[i],"..") ) + continue; + + CPLString osFullFilename = + CPLFormFilename( pszName, papszFileList[i], NULL ); + + VSIStatL( osFullFilename, &sStatBuf ); + + if( VSI_ISREG( sStatBuf.st_mode ) ) + { + VSIUnlink( osFullFilename ); + } + else if( VSI_ISDIR( sStatBuf.st_mode ) ) + { + OGRWFSRecursiveUnlink( osFullFilename ); + } + } + + CSLDestroy( papszFileList ); + + VSIRmdir( pszName ); +} + +/************************************************************************/ +/* OGRWFSLayer() */ +/************************************************************************/ + +OGRWFSLayer::OGRWFSLayer( OGRWFSDataSource* poDS, + OGRSpatialReference* poSRS, + int bAxisOrderAlreadyInverted, + const char* pszBaseURL, + const char* pszName, + const char* pszNS, + const char* pszNSVal ) + +{ + this->poDS = poDS; + this->poSRS = poSRS; + this->bAxisOrderAlreadyInverted = bAxisOrderAlreadyInverted; + this->pszBaseURL = CPLStrdup(pszBaseURL); + this->pszName = CPLStrdup(pszName); + this->pszNS = pszNS ? CPLStrdup(pszNS) : NULL; + this->pszNSVal = pszNSVal ? CPLStrdup(pszNSVal) : NULL; + + SetDescription( pszName ); + + poFeatureDefn = NULL; + poGMLFeatureClass = NULL; + bGotApproximateLayerDefn = FALSE; + + bStreamingDS = FALSE; + poBaseDS = NULL; + poBaseLayer = NULL; + bReloadNeeded = FALSE; + bHasFetched = FALSE; + eGeomType = wkbUnknown; + nFeatures = -1; + bCountFeaturesInGetNextFeature = FALSE; + + dfMinX = dfMinY = dfMaxX = dfMaxY = 0; + bHasExtents = FALSE; + poFetchedFilterGeom = NULL; + + nExpectedInserts = 0; + bInTransaction = FALSE; + bUseFeatureIdAtLayerLevel = FALSE; + + bPagingActive = FALSE; + nPagingStartIndex = 0; + nFeatureRead = 0; + nFeatureCountRequested = 0; + + pszRequiredOutputFormat = NULL; +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +OGRWFSLayer* OGRWFSLayer::Clone() +{ + OGRWFSLayer* poDupLayer = new OGRWFSLayer(poDS, poSRS, bAxisOrderAlreadyInverted, + pszBaseURL, pszName, pszNS, pszNSVal); + if (poSRS) + poSRS->Reference(); + poDupLayer->poFeatureDefn = GetLayerDefn()->Clone(); + poDupLayer->poFeatureDefn->Reference(); + poDupLayer->bGotApproximateLayerDefn = bGotApproximateLayerDefn; + poDupLayer->eGeomType = poDupLayer->poFeatureDefn->GetGeomType(); + poDupLayer->pszRequiredOutputFormat = pszRequiredOutputFormat ? CPLStrdup(pszRequiredOutputFormat) : NULL; + + /* Copy existing schema file if already found */ + CPLString osSrcFileName = CPLSPrintf("/vsimem/tempwfs_%p/file.xsd", this); + CPLString osTargetFileName = CPLSPrintf("/vsimem/tempwfs_%p/file.xsd", poDupLayer); + CPLCopyFile(osTargetFileName, osSrcFileName); + + return poDupLayer; +} + +/************************************************************************/ +/* ~OGRWFSLayer() */ +/************************************************************************/ + +OGRWFSLayer::~OGRWFSLayer() + +{ + if (bInTransaction) + CommitTransaction(); + + if( poSRS != NULL ) + poSRS->Release(); + + if (poFeatureDefn != NULL) + poFeatureDefn->Release(); + delete poGMLFeatureClass; + + CPLFree(pszBaseURL); + CPLFree(pszName); + CPLFree(pszNS); + CPLFree(pszNSVal); + + GDALClose(poBaseDS); + + delete poFetchedFilterGeom; + + CPLString osTmpDirName = CPLSPrintf("/vsimem/tempwfs_%p", this); + OGRWFSRecursiveUnlink(osTmpDirName); + + CPLFree(pszRequiredOutputFormat); +} + +/************************************************************************/ +/* GetDescribeFeatureTypeURL() */ +/************************************************************************/ + +CPLString OGRWFSLayer::GetDescribeFeatureTypeURL(CPL_UNUSED int bWithNS) +{ + CPLString osURL(pszBaseURL); + osURL = CPLURLAddKVP(osURL, "SERVICE", "WFS"); + osURL = CPLURLAddKVP(osURL, "VERSION", poDS->GetVersion()); + osURL = CPLURLAddKVP(osURL, "REQUEST", "DescribeFeatureType"); + osURL = CPLURLAddKVP(osURL, "TYPENAME", WFS_EscapeURL(pszName)); + osURL = CPLURLAddKVP(osURL, "PROPERTYNAME", NULL); + osURL = CPLURLAddKVP(osURL, "MAXFEATURES", NULL); + osURL = CPLURLAddKVP(osURL, "COUNT", NULL); + osURL = CPLURLAddKVP(osURL, "FILTER", NULL); + osURL = CPLURLAddKVP(osURL, "OUTPUTFORMAT", pszRequiredOutputFormat ? WFS_EscapeURL(pszRequiredOutputFormat).c_str() : NULL); + + if (pszNS && poDS->GetNeedNAMESPACE()) + { + /* Older Deegree version require NAMESPACE (e.g. http://www.nokis.org/deegree2/ogcwebservice) */ + /* This has been now corrected */ + CPLString osValue("xmlns("); + osValue += pszNS; + osValue += "="; + osValue += pszNSVal; + osValue += ")"; + osURL = CPLURLAddKVP(osURL, "NAMESPACE", WFS_EscapeURL(osValue)); + } + + return osURL; +} + +/************************************************************************/ +/* DescribeFeatureType() */ +/************************************************************************/ + +OGRFeatureDefn* OGRWFSLayer::DescribeFeatureType() +{ + CPLString osURL = GetDescribeFeatureTypeURL(TRUE); + + CPLDebug("WFS", "%s", osURL.c_str()); + + CPLHTTPResult* psResult = poDS->HTTPFetch( osURL, NULL); + if (psResult == NULL) + { + return NULL; + } + + if (strstr((const char*)psResult->pabyData, "IsOldDeegree((const char*)psResult->pabyData)) + { + CPLHTTPDestroyResult(psResult); + return DescribeFeatureType(); + } + CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + CPLXMLNode* psXML = CPLParseXMLString( (const char*) psResult->pabyData ); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + return NULL; + } + CPLHTTPDestroyResult(psResult); + + CPLXMLNode* psSchema = WFSFindNode(psXML, "schema"); + if (psSchema == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find "); + CPLDestroyXMLNode( psXML ); + + return NULL; + } + + OGRFeatureDefn* poFDefn = ParseSchema(psSchema); + if (poFDefn) + poDS->SaveLayerSchema(pszName, psSchema); + + CPLDestroyXMLNode( psXML ); + return poFDefn; +} + +/************************************************************************/ +/* ParseSchema() */ +/************************************************************************/ + +OGRFeatureDefn* OGRWFSLayer::ParseSchema(CPLXMLNode* psSchema) +{ + osTargetNamespace = CPLGetXMLValue(psSchema, "targetNamespace", ""); + + CPLString osTmpFileName; + + osTmpFileName = CPLSPrintf("/vsimem/tempwfs_%p/file.xsd", this); + CPLSerializeXMLTreeToFile(psSchema, osTmpFileName); + + std::vector aosClasses; + int bFullyUnderstood = FALSE; + int bHaveSchema = GMLParseXSD( osTmpFileName, aosClasses, bFullyUnderstood ); + + if (bHaveSchema && aosClasses.size() == 1) + { + //CPLDebug("WFS", "Creating %s for %s", osTmpFileName.c_str(), GetName()); + return BuildLayerDefnFromFeatureClass(aosClasses[0]); + } + else if (bHaveSchema) + { + std::vector::const_iterator iter = aosClasses.begin(); + std::vector::const_iterator eiter = aosClasses.end(); + while (iter != eiter) + { + GMLFeatureClass* poClass = *iter; + iter ++; + delete poClass; + } + } + + VSIUnlink(osTmpFileName); + + return NULL; +} +/************************************************************************/ +/* BuildLayerDefnFromFeatureClass() */ +/************************************************************************/ + +OGRFeatureDefn* OGRWFSLayer::BuildLayerDefnFromFeatureClass(GMLFeatureClass* poClass) +{ + this->poGMLFeatureClass = poClass; + + OGRFeatureDefn* poFDefn = new OGRFeatureDefn( pszName ); + poFDefn->SetGeomType(wkbNone); + if( poGMLFeatureClass->GetGeometryPropertyCount() > 0 ) + { + poFDefn->SetGeomType( (OGRwkbGeometryType)poGMLFeatureClass->GetGeometryProperty(0)->GetType() ); + poFDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + } + +/* -------------------------------------------------------------------- */ +/* Added attributes (properties). */ +/* -------------------------------------------------------------------- */ + if( poDS->ExposeGMLId() ) + { + OGRFieldDefn oField( "gml_id", OFTString ); + oField.SetNullable(FALSE); + poFDefn->AddFieldDefn( &oField ); + } + + for( int iField = 0; iField < poGMLFeatureClass->GetPropertyCount(); iField++ ) + { + GMLPropertyDefn *poProperty = poGMLFeatureClass->GetProperty( iField ); + OGRFieldType eFType; + + if( poProperty->GetType() == GMLPT_Untyped ) + eFType = OFTString; + else if( poProperty->GetType() == GMLPT_String ) + eFType = OFTString; + else if( poProperty->GetType() == GMLPT_Integer || + poProperty->GetType() == GMLPT_Boolean || + poProperty->GetType() == GMLPT_Short ) + eFType = OFTInteger; + else if( poProperty->GetType() == GMLPT_Integer64 ) + eFType = OFTInteger64; + else if( poProperty->GetType() == GMLPT_Real || + poProperty->GetType() == GMLPT_Float ) + eFType = OFTReal; + else if( poProperty->GetType() == GMLPT_StringList ) + eFType = OFTStringList; + else if( poProperty->GetType() == GMLPT_IntegerList || + poProperty->GetType() == GMLPT_BooleanList ) + eFType = OFTIntegerList; + else if( poProperty->GetType() == GMLPT_Integer64List ) + eFType = OFTInteger64List; + else if( poProperty->GetType() == GMLPT_RealList ) + eFType = OFTRealList; + else + eFType = OFTString; + + OGRFieldDefn oField( poProperty->GetName(), eFType ); + if ( EQUALN(oField.GetNameRef(), "ogr:", 4) ) + oField.SetName(poProperty->GetName()+4); + if( poProperty->GetWidth() > 0 ) + oField.SetWidth( poProperty->GetWidth() ); + if( poProperty->GetPrecision() > 0 ) + oField.SetPrecision( poProperty->GetPrecision() ); + if( poProperty->GetType() == GMLPT_Boolean || + poProperty->GetType() == GMLPT_BooleanList ) + oField.SetSubType(OFSTBoolean); + else if( poProperty->GetType() == GMLPT_Short) + oField.SetSubType(OFSTInt16); + else if( poProperty->GetType() == GMLPT_Float) + oField.SetSubType(OFSTFloat32); + if( !poDS->IsEmptyAsNull() ) + oField.SetNullable(poProperty->IsNullable()); + + poFDefn->AddFieldDefn( &oField ); + } + + if( poGMLFeatureClass->GetGeometryPropertyCount() > 0 ) + { + const char* pszGeometryColumnName = poGMLFeatureClass->GetGeometryProperty(0)->GetSrcElement(); + if (pszGeometryColumnName[0] != '\0') + { + osGeometryColumnName = pszGeometryColumnName; + if( poFDefn->GetGeomFieldCount() > 0 ) + { + poFDefn->GetGeomFieldDefn(0)->SetNullable(poGMLFeatureClass->GetGeometryProperty(0)->IsNullable()); + poFDefn->GetGeomFieldDefn(0)->SetName(pszGeometryColumnName); + } + } + } + + return poFDefn; +} + +/************************************************************************/ +/* MakeGetFeatureURL() */ +/************************************************************************/ + +CPLString OGRWFSLayer::MakeGetFeatureURL(int nRequestMaxFeatures, int bRequestHits) +{ + CPLString osURL(pszBaseURL); + osURL = CPLURLAddKVP(osURL, "SERVICE", "WFS"); + osURL = CPLURLAddKVP(osURL, "VERSION", poDS->GetVersion()); + osURL = CPLURLAddKVP(osURL, "REQUEST", "GetFeature"); + if( atoi(poDS->GetVersion()) >= 2 ) + osURL = CPLURLAddKVP(osURL, "TYPENAMES", WFS_EscapeURL(pszName)); + else + osURL = CPLURLAddKVP(osURL, "TYPENAME", WFS_EscapeURL(pszName)); + if (pszRequiredOutputFormat) + osURL = CPLURLAddKVP(osURL, "OUTPUTFORMAT", WFS_EscapeURL(pszRequiredOutputFormat)); + + if (poDS->IsPagingAllowed() && !bRequestHits) + { + osURL = CPLURLAddKVP(osURL, "STARTINDEX", + CPLSPrintf("%d", nPagingStartIndex + + poDS->GetBaseStartIndex())); + nRequestMaxFeatures = poDS->GetPageSize(); + nFeatureCountRequested = nRequestMaxFeatures; + bPagingActive = TRUE; + } + + if (nRequestMaxFeatures) + { + osURL = CPLURLAddKVP(osURL, + atoi(poDS->GetVersion()) >= 2 ? "COUNT" : "MAXFEATURES", + CPLSPrintf("%d", nRequestMaxFeatures)); + } + if (pszNS && poDS->GetNeedNAMESPACE()) + { + /* Older Deegree version require NAMESPACE (e.g. http://www.nokis.org/deegree2/ogcwebservice) */ + /* This has been now corrected */ + CPLString osValue("xmlns("); + osValue += pszNS; + osValue += "="; + osValue += pszNSVal; + osValue += ")"; + osURL = CPLURLAddKVP(osURL, "NAMESPACE", WFS_EscapeURL(osValue)); + } + + delete poFetchedFilterGeom; + poFetchedFilterGeom = NULL; + + CPLString osGeomFilter; + + if (m_poFilterGeom != NULL && osGeometryColumnName.size() > 0) + { + OGREnvelope oEnvelope; + m_poFilterGeom->getEnvelope(&oEnvelope); + + poFetchedFilterGeom = m_poFilterGeom->clone(); + + osGeomFilter = ""; + if (atoi(poDS->GetVersion()) >= 2) + osGeomFilter += ""; + else + osGeomFilter += ""; + if (pszNS) + { + osGeomFilter += pszNS; + osGeomFilter += ":"; + } + osGeomFilter += osGeometryColumnName; + if (atoi(poDS->GetVersion()) >= 2) + osGeomFilter += ""; + else + osGeomFilter += ""; + + if ( atoi(poDS->GetVersion()) >= 2 ) + { + osGeomFilter += "%.16f %.16f%.16f %.16f", + oEnvelope.MinY, oEnvelope.MinX, oEnvelope.MaxY, oEnvelope.MaxX); + } + else + osGeomFilter += CPLSPrintf("%.16f %.16f%.16f %.16f", + oEnvelope.MinX, oEnvelope.MinY, oEnvelope.MaxX, oEnvelope.MaxY); + osGeomFilter += ""; + } + else if ( poDS->RequiresEnvelopeSpatialFilter() ) + { + osGeomFilter += ""; + if (bAxisOrderAlreadyInverted) + { + /* We can go here in WFS 1.1 with geographic coordinate systems */ + /* that are natively return in lat,long order, but as we have */ + /* presented long,lat order to the user, we must switch back */ + /* for the server... */ + osGeomFilter += CPLSPrintf("%.16f%.16f%.16f%.16f", + oEnvelope.MinY, oEnvelope.MinX, oEnvelope.MaxY, oEnvelope.MaxX); + } + else + osGeomFilter += CPLSPrintf("%.16f%.16f%.16f%.16f", + oEnvelope.MinX, oEnvelope.MinY, oEnvelope.MaxX, oEnvelope.MaxY); + osGeomFilter += ""; + } + else + { + osGeomFilter += ""; + osGeomFilter += ""; + if (bAxisOrderAlreadyInverted) + { + /* We can go here in WFS 1.1 with geographic coordinate systems */ + /* that are natively return in lat,long order, but as we have */ + /* presented long,lat order to the user, we must switch back */ + /* for the server... */ + osGeomFilter += CPLSPrintf("%.16f,%.16f %.16f,%.16f", oEnvelope.MinY, oEnvelope.MinX, oEnvelope.MaxY, oEnvelope.MaxX); + } + else + osGeomFilter += CPLSPrintf("%.16f,%.16f %.16f,%.16f", oEnvelope.MinX, oEnvelope.MinY, oEnvelope.MaxX, oEnvelope.MaxY); + osGeomFilter += ""; + osGeomFilter += ""; + } + osGeomFilter += ""; + } + + if (osGeomFilter.size() != 0 || osWFSWhere.size() != 0) + { + CPLString osFilter; + if (atoi(poDS->GetVersion()) >= 2) + osFilter = "GetVersion()) >= 2) + osFilter += " xmlns:gml=\"http://www.opengis.net/gml/3.2\">"; + else + osFilter += " xmlns:gml=\"http://www.opengis.net/gml\">"; + if (osGeomFilter.size() != 0 && osWFSWhere.size() != 0) + osFilter += ""; + osFilter += osWFSWhere; + osFilter += osGeomFilter; + if (osGeomFilter.size() != 0 && osWFSWhere.size() != 0) + osFilter += ""; + osFilter += ""; + + osURL = CPLURLAddKVP(osURL, "FILTER", WFS_EscapeURL(osFilter)); + } + + if (bRequestHits) + { + osURL = CPLURLAddKVP(osURL, "RESULTTYPE", "hits"); + } + else if (aoSortColumns.size() != 0) + { + CPLString osSortBy; + for( int i=0; i < (int)aoSortColumns.size(); i++) + { + if( i > 0 ) + osSortBy += ","; + osSortBy += aoSortColumns[i].osColumn; + if( !aoSortColumns[i].bAsc ) + { + if (atoi(poDS->GetVersion()) >= 2) + osSortBy += " DESC"; + else + osSortBy += " D"; + } + } + osURL = CPLURLAddKVP(osURL, "SORTBY", WFS_EscapeURL(osSortBy)); + } + + /* If no PROPERTYNAME is specified, build one if there are ignored fields */ + CPLString osPropertyName = CPLURLGetValue(osURL, "PROPERTYNAME"); + const char* pszPropertyName = osPropertyName.c_str(); + if (pszPropertyName[0] == 0 && poFeatureDefn != NULL) + { + int bHasIgnoredField = FALSE; + CPLString osPropertyName; + for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ ) + { + if (EQUAL(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), "gml_id")) + { + /* fake field : skip it */ + } + else if (poFeatureDefn->GetFieldDefn(iField)->IsIgnored()) + { + bHasIgnoredField = TRUE; + } + else + { + if (osPropertyName.size() != 0) + osPropertyName += ","; + osPropertyName += poFeatureDefn->GetFieldDefn(iField)->GetNameRef(); + } + } + if (osGeometryColumnName.size() != 0) + { + if (poFeatureDefn->IsGeometryIgnored()) + { + bHasIgnoredField = TRUE; + } + else + { + if (osPropertyName.size() != 0) + osPropertyName += ","; + osPropertyName += osGeometryColumnName; + } + } + + if (bHasIgnoredField && osPropertyName.size()) + { + osPropertyName = "(" + osPropertyName + ")"; + osURL = CPLURLAddKVP(osURL, "PROPERTYNAME", WFS_EscapeURL(osPropertyName)); + } + } + + return osURL; +} + + +/************************************************************************/ +/* OGRWFSFetchContentDispositionFilename() */ +/************************************************************************/ + +const char* OGRWFSFetchContentDispositionFilename(char** papszHeaders) +{ + char** papszIter = papszHeaders; + while(papszIter && *papszIter) + { + /* For multipart, we have in raw format, but without end-of-line characters */ + if (strncmp(*papszIter, "Content-Disposition: attachment; filename=", 42) == 0) + { + return *papszIter + 42; + } + /* For single part, the headers are in KEY=VAL format, but with e-o-l ... */ + else if (strncmp(*papszIter, "Content-Disposition=attachment; filename=", 41) == 0) + { + char* pszVal = (char*)(*papszIter + 41); + char* pszEOL = strchr(pszVal, '\r'); + if (pszEOL) *pszEOL = 0; + pszEOL = strchr(pszVal, '\n'); + if (pszEOL) *pszEOL = 0; + return pszVal; + } + papszIter ++; + } + return NULL; +} + +/************************************************************************/ +/* MustRetryIfNonCompliantServer() */ +/************************************************************************/ + +int OGRWFSLayer::MustRetryIfNonCompliantServer(const char* pszServerAnswer) +{ + int bRetry = FALSE; + + /* Deegree server does not support PropertyIsNotEqualTo */ + /* We have to turn it into */ + if (osWFSWhere.size() != 0 && poDS->PropertyIsNotEqualToSupported() && + strstr(pszServerAnswer, "Unknown comparison operation: 'PropertyIsNotEqualTo'") != NULL) + { + poDS->SetPropertyIsNotEqualToUnSupported(); + bRetry = TRUE; + } + + /* Deegree server requires the gml: prefix in GmlObjectId element, but ESRI */ + /* doesn't like it at all ! Other servers don't care... */ + if (osWFSWhere.size() != 0 && !poDS->DoesGmlObjectIdNeedGMLPrefix() && + strstr(pszServerAnswer, "<GmlObjectId> requires 'gml:id'-attribute!") != NULL) + { + poDS->SetGmlObjectIdNeedsGMLPrefix(); + bRetry = TRUE; + } + + /* GeoServer can return the error 'Only FeatureIds are supported when encoding id filters to SDE' */ + if (osWFSWhere.size() != 0 && !bUseFeatureIdAtLayerLevel && + strstr(pszServerAnswer, "Only FeatureIds are supported") != NULL) + { + bUseFeatureIdAtLayerLevel = TRUE; + bRetry = TRUE; + } + + if (bRetry) + { + SetAttributeFilter(osSQLWhere); + bHasFetched = TRUE; + bReloadNeeded = FALSE; + } + + return bRetry; +} + +/************************************************************************/ +/* FetchGetFeature() */ +/************************************************************************/ + +GDALDataset* OGRWFSLayer::FetchGetFeature(int nRequestMaxFeatures) +{ + + CPLString osURL = MakeGetFeatureURL(nRequestMaxFeatures, FALSE); + CPLDebug("WFS", "%s", osURL.c_str()); + + CPLHTTPResult* psResult = NULL; + + CPLString osOutputFormat = CPLURLGetValue(osURL, "OUTPUTFORMAT"); + + /* Try streaming when the output format is GML and that we have a .xsd */ + /* that we are able to understand */ + CPLString osXSDFileName = CPLSPrintf("/vsimem/tempwfs_%p/file.xsd", this); + VSIStatBufL sBuf; + if (CSLTestBoolean(CPLGetConfigOption("OGR_WFS_USE_STREAMING", "YES")) && + (osOutputFormat.size() == 0 || osOutputFormat.ifind("GML") != std::string::npos) && + VSIStatL(osXSDFileName, &sBuf) == 0 && GDALGetDriverByName("GML") != NULL) + { + const char* pszStreamingName = CPLSPrintf("/vsicurl_streaming/%s", + osURL.c_str()); + if( strncmp(osURL, "/vsimem/", strlen("/vsimem/")) == 0 && + CSLTestBoolean(CPLGetConfigOption("CPL_CURL_ENABLE_VSIMEM", "FALSE")) ) + { + pszStreamingName = osURL.c_str(); + } + + const char* const apszAllowedDrivers[] = { "GML", NULL }; + const char* apszOpenOptions[6] = { NULL, NULL, NULL, NULL, NULL, NULL }; + apszOpenOptions[0] = CPLSPrintf("XSD=%s", osXSDFileName.c_str()); + apszOpenOptions[1] = CPLSPrintf("EMPTY_AS_NULL=%s", poDS->IsEmptyAsNull() ? "YES" : "NO"); + int iGMLOOIdex = 2; + if( CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", NULL) == NULL ) + { + apszOpenOptions[iGMLOOIdex] = CPLSPrintf("INVERT_AXIS_ORDER_IF_LAT_LONG=%s", + poDS->InvertAxisOrderIfLatLong() ? "YES" : "NO"); + iGMLOOIdex ++; + } + if( CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", NULL) == NULL ) + { + apszOpenOptions[iGMLOOIdex] = CPLSPrintf("CONSIDER_EPSG_AS_URN=%s", + poDS->GetConsiderEPSGAsURN().c_str()); + iGMLOOIdex ++; + } + if( CPLGetConfigOption("GML_EXPOSE_GML_ID", NULL) == NULL ) + { + apszOpenOptions[iGMLOOIdex] = CPLSPrintf("EXPOSE_GML_ID=%s", + poDS->ExposeGMLId() ? "YES" : "NO"); + iGMLOOIdex ++; + } + + GDALDataset* poGML_DS = (GDALDataset*) + GDALOpenEx(pszStreamingName, GDAL_OF_VECTOR, apszAllowedDrivers, + apszOpenOptions, NULL); + if (poGML_DS) + { + bStreamingDS = TRUE; + return poGML_DS; + } + + /* In case of failure, read directly the content to examine */ + /* it, if it is XML error content */ + char szBuffer[2048]; + int nRead = 0; + VSILFILE* fp = VSIFOpenL(pszStreamingName, "rb"); + if (fp) + { + nRead = (int)VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fp); + szBuffer[nRead] = '\0'; + VSIFCloseL(fp); + } + + if (nRead != 0) + { + if (MustRetryIfNonCompliantServer(szBuffer)) + return FetchGetFeature(nRequestMaxFeatures); + + if (strstr(szBuffer, "IsOldDeegree(szBuffer)) + { + return FetchGetFeature(nRequestMaxFeatures); + } + + CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s", + szBuffer); + return NULL; + } + } + } + + bStreamingDS = FALSE; + psResult = poDS->HTTPFetch( osURL, NULL); + if (psResult == NULL) + { + return NULL; + } + + const char* pszContentType = ""; + if (psResult->pszContentType) + pszContentType = psResult->pszContentType; + + CPLString osTmpDirName = CPLSPrintf("/vsimem/tempwfs_%p", this); + VSIMkdir(osTmpDirName, 0); + + GByte *pabyData = psResult->pabyData; + int nDataLen = psResult->nDataLen; + int bIsMultiPart = FALSE; + const char* pszAttachementFilename = NULL; + + if(strstr(pszContentType,"multipart") + && CPLHTTPParseMultipartMime(psResult) ) + { + int i; + bIsMultiPart = TRUE; + OGRWFSRecursiveUnlink(osTmpDirName); + VSIMkdir(osTmpDirName, 0); + for(i=0;inMimePartCount;i++) + { + CPLString osTmpFileName = osTmpDirName + "/"; + pszAttachementFilename = + OGRWFSFetchContentDispositionFilename( + psResult->pasMimePart[i].papszHeaders); + + if (pszAttachementFilename) + osTmpFileName += pszAttachementFilename; + else + osTmpFileName += CPLSPrintf("file_%d", i); + + GByte* pData = (GByte*)VSIMalloc(psResult->pasMimePart[i].nDataLen); + if (pData) + { + memcpy(pData, psResult->pasMimePart[i].pabyData, psResult->pasMimePart[i].nDataLen); + VSILFILE *fp = VSIFileFromMemBuffer( osTmpFileName, + pData, + psResult->pasMimePart[i].nDataLen, TRUE); + VSIFCloseL(fp); + } + } + } + else + pszAttachementFilename = + OGRWFSFetchContentDispositionFilename( + psResult->papszHeaders); + + int bJSON = FALSE; + int bCSV = FALSE; + int bKML = FALSE; + int bKMZ = FALSE; + int bZIP = FALSE; + int bGZIP = FALSE; + + const char* pszOutputFormat = osOutputFormat.c_str(); + + if (FindSubStringInsensitive(pszContentType, "json") || + FindSubStringInsensitive(pszOutputFormat, "json")) + { + bJSON = TRUE; + } + else if (FindSubStringInsensitive(pszContentType, "csv") || + FindSubStringInsensitive(pszOutputFormat, "csv")) + { + bCSV = TRUE; + } + else if (FindSubStringInsensitive(pszContentType, "kml") || + FindSubStringInsensitive(pszOutputFormat, "kml")) + { + bKML = TRUE; + } + else if (FindSubStringInsensitive(pszContentType, "kmz") || + FindSubStringInsensitive(pszOutputFormat, "kmz")) + { + bKMZ = TRUE; + } + else if (strstr(pszContentType, "application/zip") != NULL) + { + bZIP = TRUE; + } + else if (strstr(pszContentType, "application/gzip") != NULL) + { + bGZIP = TRUE; + } + + if (MustRetryIfNonCompliantServer((const char*)pabyData)) + { + CPLHTTPDestroyResult(psResult); + return FetchGetFeature(nRequestMaxFeatures); + } + + if (strstr((const char*)pabyData, "IsOldDeegree((const char*)pabyData)) + { + CPLHTTPDestroyResult(psResult); + return FetchGetFeature(nRequestMaxFeatures); + } + + CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s", + pabyData); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + CPLString osTmpFileName; + + if (!bIsMultiPart) + { + if (bJSON) + osTmpFileName = osTmpDirName + "/file.geojson"; + else if (bZIP) + osTmpFileName = osTmpDirName + "/file.zip"; + else if (bCSV) + osTmpFileName = osTmpDirName + "/file.csv"; + else if (bKML) + osTmpFileName = osTmpDirName + "/file.kml"; + else if (bKMZ) + osTmpFileName = osTmpDirName + "/file.kmz"; + /* GML is a special case. It needs the .xsd file that has been saved */ + /* as file.xsd, so we cannot used the attachement filename */ + else if (pszAttachementFilename && + !EQUAL(CPLGetExtension(pszAttachementFilename), "GML")) + { + osTmpFileName = osTmpDirName + "/"; + osTmpFileName += pszAttachementFilename; + } + else + { + osTmpFileName = osTmpDirName + "/file.gfs"; + VSIUnlink(osTmpFileName); + + osTmpFileName = osTmpDirName + "/file.gml"; + } + + VSILFILE *fp = VSIFileFromMemBuffer( osTmpFileName, pabyData, + nDataLen, TRUE); + VSIFCloseL(fp); + psResult->pabyData = NULL; + + if (bZIP) + { + osTmpFileName = "/vsizip/" + osTmpFileName; + } + else if (bGZIP) + { + osTmpFileName = "/vsigzip/" + osTmpFileName; + } + } + else + { + pabyData = NULL; + nDataLen = 0; + osTmpFileName = osTmpDirName; + } + + CPLHTTPDestroyResult(psResult); + + const char* const * papszOpenOptions = NULL; + const char* apszGMLOpenOptions[4] = { NULL, NULL, NULL, NULL }; + int iGMLOOIdex = 0; + if( CPLGetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG", NULL) == NULL ) + { + apszGMLOpenOptions[iGMLOOIdex] = CPLSPrintf("INVERT_AXIS_ORDER_IF_LAT_LONG=%s", + poDS->InvertAxisOrderIfLatLong() ? "YES" : "NO"); + iGMLOOIdex ++; + } + if( CPLGetConfigOption("GML_CONSIDER_EPSG_AS_URN", NULL) == NULL ) + { + apszGMLOpenOptions[iGMLOOIdex] = CPLSPrintf("CONSIDER_EPSG_AS_URN=%s", + poDS->GetConsiderEPSGAsURN().c_str()); + iGMLOOIdex ++; + } + if( CPLGetConfigOption("GML_EXPOSE_GML_ID", NULL) == NULL ) + { + apszGMLOpenOptions[iGMLOOIdex] = CPLSPrintf("EXPOSE_GML_ID=%s", + poDS->ExposeGMLId() ? "YES" : "NO"); + iGMLOOIdex ++; + } + + GDALDriverH hDrv = GDALIdentifyDriver(osTmpFileName, NULL); + if( hDrv != NULL && hDrv == GDALGetDriverByName("GML") ) + papszOpenOptions = apszGMLOpenOptions; + + GDALDataset* poPageDS = (GDALDataset*) GDALOpenEx(osTmpFileName, + GDAL_OF_VECTOR, NULL, papszOpenOptions, NULL); + if (poPageDS == NULL && (bZIP || bIsMultiPart)) + { + char** papszFileList = VSIReadDir(osTmpFileName); + int i; + for( i = 0; papszFileList != NULL && papszFileList[i] != NULL; i++ ) + { + CPLString osFullFilename = + CPLFormFilename( osTmpFileName, papszFileList[i], NULL ); + GDALDriverH hDrv = GDALIdentifyDriver(osFullFilename, NULL); + if( hDrv != NULL && hDrv == GDALGetDriverByName("GML") ) + papszOpenOptions = apszGMLOpenOptions; + poPageDS = (GDALDataset*) GDALOpenEx(osFullFilename, + GDAL_OF_VECTOR, NULL, papszOpenOptions, NULL); + if (poPageDS != NULL) + break; + } + + CSLDestroy( papszFileList ); + } + + if (poPageDS == NULL) + { + if (pabyData != NULL && !bJSON && !bZIP && + strstr((const char*)pabyData, " 1000) + pabyData[1000] = 0; + CPLError(CE_Failure, CPLE_AppDefined, + "Error: cannot parse %s", pabyData); + } + return NULL; + } + + OGRLayer* poLayer = poPageDS->GetLayer(0); + if (poLayer == NULL) + { + GDALClose(poPageDS); + return NULL; + } + + return poPageDS; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRWFSLayer::GetLayerDefn() +{ + if (poFeatureDefn) + return poFeatureDefn; + + poDS->LoadMultipleLayerDefn(GetName(), pszNS, pszNSVal); + + if (poFeatureDefn) + return poFeatureDefn; + + return BuildLayerDefn(); +} + +/************************************************************************/ +/* BuildLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRWFSLayer::BuildLayerDefn(OGRFeatureDefn* poSrcFDefn) +{ + int bUnsetWidthPrecision = FALSE; + + poFeatureDefn = new OGRFeatureDefn( pszName ); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + poFeatureDefn->Reference(); + + GDALDataset* poDS = NULL; + + if (poSrcFDefn == NULL) + poSrcFDefn = DescribeFeatureType(); + if (poSrcFDefn == NULL) + { + poDS = FetchGetFeature(1); + if (poDS == NULL) + { + return poFeatureDefn; + } + poSrcFDefn = poDS->GetLayer(0)->GetLayerDefn(); + bGotApproximateLayerDefn = TRUE; + /* We cannot trust width and precision based on a single feature */ + bUnsetWidthPrecision = TRUE; + } + + CPLString osPropertyName = CPLURLGetValue(pszBaseURL, "PROPERTYNAME"); + const char* pszPropertyName = osPropertyName.c_str(); + + int i; + poFeatureDefn->SetGeomType(poSrcFDefn->GetGeomType()); + if( poSrcFDefn->GetGeomFieldCount() > 0 ) + poFeatureDefn->GetGeomFieldDefn(0)->SetName(poSrcFDefn->GetGeomFieldDefn(0)->GetNameRef()); + for(i=0;iGetFieldCount();i++) + { + if (pszPropertyName[0] != 0) + { + if (strstr(pszPropertyName, + poSrcFDefn->GetFieldDefn(i)->GetNameRef()) != NULL) + poFeatureDefn->AddFieldDefn(poSrcFDefn->GetFieldDefn(i)); + else + bGotApproximateLayerDefn = TRUE; + } + else + { + OGRFieldDefn oFieldDefn(poSrcFDefn->GetFieldDefn(i)); + if (bUnsetWidthPrecision) + { + oFieldDefn.SetWidth(0); + oFieldDefn.SetPrecision(0); + } + poFeatureDefn->AddFieldDefn(&oFieldDefn); + } + } + + if (poDS) + GDALClose(poDS); + else + delete poSrcFDefn; + + return poFeatureDefn; +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRWFSLayer::ResetReading() + +{ + GetLayerDefn(); + if (bPagingActive) + bReloadNeeded = TRUE; + nPagingStartIndex = 0; + nFeatureRead = 0; + nFeatureCountRequested = 0; + if (bReloadNeeded) + { + GDALClose(poBaseDS); + poBaseDS = NULL; + poBaseLayer = NULL; + bHasFetched = FALSE; + bReloadNeeded = FALSE; + } + if (poBaseLayer) + poBaseLayer->ResetReading(); +} + + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRWFSLayer::GetNextFeature() +{ + GetLayerDefn(); + + while(TRUE) + { + if (bPagingActive && nFeatureRead == nPagingStartIndex + nFeatureCountRequested) + { + bReloadNeeded = TRUE; + nPagingStartIndex = nFeatureRead; + } + if (bReloadNeeded) + { + GDALClose(poBaseDS); + poBaseDS = NULL; + poBaseLayer = NULL; + bHasFetched = FALSE; + bReloadNeeded = FALSE; + } + if (poBaseDS == NULL && !bHasFetched) + { + bHasFetched = TRUE; + poBaseDS = FetchGetFeature(0); + if (poBaseDS) + { + poBaseLayer = poBaseDS->GetLayer(0); + poBaseLayer->ResetReading(); + + /* Check that the layer field definition is consistent with the one */ + /* we got in BuildLayerDefn() */ + if (poFeatureDefn->GetFieldCount() != poBaseLayer->GetLayerDefn()->GetFieldCount()) + bGotApproximateLayerDefn = TRUE; + else + { + int iField; + for(iField = 0;iField < poFeatureDefn->GetFieldCount(); iField++) + { + OGRFieldDefn* poFDefn1 = poFeatureDefn->GetFieldDefn(iField); + OGRFieldDefn* poFDefn2 = poBaseLayer->GetLayerDefn()->GetFieldDefn(iField); + if (strcmp(poFDefn1->GetNameRef(), poFDefn2->GetNameRef()) != 0 || + poFDefn1->GetType() != poFDefn2->GetType()) + { + bGotApproximateLayerDefn = TRUE; + break; + } + } + } + } + } + if (!poBaseLayer) + return NULL; + + OGRFeature* poSrcFeature = poBaseLayer->GetNextFeature(); + if (poSrcFeature == NULL) + return NULL; + nFeatureRead ++; + if( bCountFeaturesInGetNextFeature ) + nFeatures ++; + + OGRGeometry* poGeom = poSrcFeature->GetGeometryRef(); + if( m_poFilterGeom != NULL && poGeom != NULL && + !FilterGeometry( poGeom ) ) + { + delete poSrcFeature; + continue; + } + + /* Client-side attribue filtering with underlying layer defn */ + /* identical to exposed layer defn */ + if( !bGotApproximateLayerDefn && + osWFSWhere.size() == 0 && + m_poAttrQuery != NULL && + !m_poAttrQuery->Evaluate( poSrcFeature ) ) + { + delete poSrcFeature; + continue; + } + + OGRFeature* poNewFeature = new OGRFeature(poFeatureDefn); + if (bGotApproximateLayerDefn) + { + poNewFeature->SetFrom(poSrcFeature); + + /* Client-side attribue filtering */ + if( m_poAttrQuery != NULL && + osWFSWhere.size() == 0 && + !m_poAttrQuery->Evaluate( poNewFeature ) ) + { + delete poSrcFeature; + delete poNewFeature; + continue; + } + } + else + { + int iField; + for(iField = 0;iField < poFeatureDefn->GetFieldCount(); iField++) + poNewFeature->SetField( iField, poSrcFeature->GetRawFieldRef(iField) ); + poNewFeature->SetStyleString(poSrcFeature->GetStyleString()); + poNewFeature->SetGeometryDirectly(poSrcFeature->StealGeometry()); + } + poNewFeature->SetFID(poSrcFeature->GetFID()); + poGeom = poNewFeature->GetGeometryRef(); + + /* FIXME? I don't really know what we should do with WFS 1.1.0 */ + /* and non-GML format !!! I guess 50% WFS servers must do it wrong anyway */ + /* GeoServer does currently axis inversion for non GML output, but */ + /* apparently this is not correct : http://jira.codehaus.org/browse/GEOS-3657 */ + if (poGeom != NULL && + bAxisOrderAlreadyInverted && + strcmp(poBaseDS->GetDriverName(), "GML") != 0) + { + poGeom->swapXY(); + } + + if (poGeom && poSRS) + poGeom->assignSpatialReference(poSRS); + delete poSrcFeature; + return poNewFeature; + } +} + +/************************************************************************/ +/* SetSpatialFilter() */ +/************************************************************************/ + +void OGRWFSLayer::SetSpatialFilter( OGRGeometry * poGeom ) +{ + if (bStreamingDS) + bReloadNeeded = TRUE; + else if (poFetchedFilterGeom == NULL && poBaseDS != NULL) + { + /* If there was no filter set, and that we set one */ + /* the new result set can only be a subset of the whole */ + /* so no need to reload from source */ + bReloadNeeded = FALSE; + } + else if (poFetchedFilterGeom != NULL && poGeom != NULL && poBaseDS != NULL) + { + OGREnvelope oOldEnvelope, oNewEnvelope; + poFetchedFilterGeom->getEnvelope(&oOldEnvelope); + poGeom->getEnvelope(&oNewEnvelope); + /* Optimization : we don't need to request the server */ + /* if the new BBOX is inside the old BBOX as we have */ + /* already all the features */ + bReloadNeeded = ! oOldEnvelope.Contains(oNewEnvelope); + } + else + bReloadNeeded = TRUE; + nFeatures = -1; + OGRLayer::SetSpatialFilter(poGeom); + ResetReading(); +} + +/************************************************************************/ +/* SetAttributeFilter() */ +/************************************************************************/ + +OGRErr OGRWFSLayer::SetAttributeFilter( const char * pszFilter ) +{ + if (pszFilter != NULL && pszFilter[0] == 0) + pszFilter = NULL; + + CPLString osOldWFSWhere(osWFSWhere); + + CPLFree(m_pszAttrQueryString); + m_pszAttrQueryString = (pszFilter) ? CPLStrdup(pszFilter) : NULL; + + delete m_poAttrQuery; + m_poAttrQuery = NULL; + + if( pszFilter != NULL ) + { + m_poAttrQuery = new OGRFeatureQuery(); + + OGRErr eErr = m_poAttrQuery->Compile( GetLayerDefn(), pszFilter, TRUE, + WFSGetCustomFuncRegistrar() ); + if( eErr != OGRERR_NONE ) + { + delete m_poAttrQuery; + m_poAttrQuery = NULL; + return eErr; + } + } + + if (poDS->HasMinOperators() && m_poAttrQuery != NULL ) + { + swq_expr_node* poNode = (swq_expr_node*) m_poAttrQuery->GetSWQExpr(); + poNode->ReplaceBetweenByGEAndLERecurse(); + + int bNeedsNullCheck = FALSE; + int nVersion = (strcmp(poDS->GetVersion(),"1.0.0") == 0) ? 100 : + (atoi(poDS->GetVersion()) >= 2) ? 200 : 110; + if( poNode->field_type != SWQ_BOOLEAN ) + osWFSWhere = ""; + else + osWFSWhere = WFS_TurnSQLFilterToOGCFilter(poNode, + NULL, + GetLayerDefn(), + nVersion, + poDS->PropertyIsNotEqualToSupported(), + poDS->UseFeatureId() || bUseFeatureIdAtLayerLevel, + poDS->DoesGmlObjectIdNeedGMLPrefix(), + "", + &bNeedsNullCheck); + if (bNeedsNullCheck && !poDS->HasNullCheck()) + osWFSWhere = ""; + } + else + osWFSWhere = ""; + + if (m_poAttrQuery != NULL && osWFSWhere.size() == 0) + { + CPLDebug("WFS", "Using client-side only mode for filter \"%s\"", pszFilter); + OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter); + if (eErr != OGRERR_NONE) + return eErr; + } + ResetReading(); + + osSQLWhere = (pszFilter) ? pszFilter : ""; + + if (osWFSWhere != osOldWFSWhere) + bReloadNeeded = TRUE; + else + bReloadNeeded = FALSE; + nFeatures = -1; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRWFSLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,OLCFastFeatureCount) ) + { + if (nFeatures >= 0) + return TRUE; + + return poBaseLayer != NULL && m_poFilterGeom == NULL && + m_poAttrQuery == NULL && poBaseLayer->TestCapability(pszCap) && + (!poDS->IsPagingAllowed() && poBaseLayer->GetFeatureCount() < poDS->GetPageSize()); + } + + else if( EQUAL(pszCap,OLCFastGetExtent) ) + { + if (bHasExtents) + return TRUE; + + return poBaseLayer != NULL && + poBaseLayer->TestCapability(pszCap); + } + + else if( EQUAL(pszCap,OLCStringsAsUTF8) ) + return poBaseLayer != NULL && poBaseLayer->TestCapability(pszCap); + + else if( EQUAL(pszCap, OLCSequentialWrite) || + EQUAL(pszCap, OLCDeleteFeature) || + EQUAL(pszCap, OLCRandomWrite) ) + { + GetLayerDefn(); + return poDS->SupportTransactions() && poDS->UpdateMode() && + poFeatureDefn->GetFieldIndex("gml_id") == 0; + } + else if ( EQUAL(pszCap, OLCTransactions) ) + { + return poDS->SupportTransactions() && poDS->UpdateMode(); + } + else if( EQUAL(pszCap,OLCIgnoreFields) ) + { + return poBaseDS == NULL; + } + + return FALSE; +} + +/************************************************************************/ +/* ExecuteGetFeatureResultTypeHits() */ +/************************************************************************/ + +GIntBig OGRWFSLayer::ExecuteGetFeatureResultTypeHits() +{ + char* pabyData = NULL; + CPLString osURL = MakeGetFeatureURL(0, TRUE); + if (pszRequiredOutputFormat) + osURL = CPLURLAddKVP(osURL, "OUTPUTFORMAT", WFS_EscapeURL(pszRequiredOutputFormat)); + CPLDebug("WFS", "%s", osURL.c_str()); + + CPLHTTPResult* psResult = poDS->HTTPFetch( osURL, NULL); + if (psResult == NULL) + { + return -1; + } + + /* http://demo.snowflakesoftware.com:8080/Obstacle_AIXM_ZIP/GOPublisherWFS returns */ + /* zip content, including for RESULTTYPE=hits */ + if (psResult->pszContentType != NULL && + strstr(psResult->pszContentType, "application/zip") != NULL) + { + CPLString osTmpFileName; + osTmpFileName.Printf("/vsimem/wfstemphits_%p.zip", this); + VSILFILE *fp = VSIFileFromMemBuffer( osTmpFileName, psResult->pabyData, + psResult->nDataLen, FALSE); + VSIFCloseL(fp); + + CPLString osZipTmpFileName("/vsizip/" + osTmpFileName); + + char** papszDirContent = CPLReadDir(osZipTmpFileName); + if (CSLCount(papszDirContent) != 1) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot parse result of RESULTTYPE=hits request : more than one file in zip"); + CSLDestroy(papszDirContent); + CPLHTTPDestroyResult(psResult); + VSIUnlink(osTmpFileName); + return -1; + } + + CPLString osFileInZipTmpFileName = osZipTmpFileName + "/"; + osFileInZipTmpFileName += papszDirContent[0]; + + fp = VSIFOpenL(osFileInZipTmpFileName.c_str(), "rb"); + if (fp == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot parse result of RESULTTYPE=hits request : cannot open one file in zip"); + CSLDestroy(papszDirContent); + CPLHTTPDestroyResult(psResult); + VSIUnlink(osTmpFileName); + return -1; + } + VSIStatBufL sBuf; + VSIStatL(osFileInZipTmpFileName.c_str(), &sBuf); + pabyData = (char*) CPLMalloc((size_t)(sBuf.st_size + 1)); + pabyData[sBuf.st_size] = 0; + VSIFReadL(pabyData, 1, (size_t)sBuf.st_size, fp); + VSIFCloseL(fp); + + CSLDestroy(papszDirContent); + VSIUnlink(osTmpFileName); + } + else + { + pabyData = (char*) psResult->pabyData; + psResult->pabyData = NULL; + } + + if (strstr(pabyData, "IsOldDeegree(pabyData)) + { + CPLHTTPDestroyResult(psResult); + return ExecuteGetFeatureResultTypeHits(); + } + CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s", + pabyData); + CPLHTTPDestroyResult(psResult); + CPLFree(pabyData); + return -1; + } + + CPLXMLNode* psXML = CPLParseXMLString( pabyData ); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + pabyData); + CPLHTTPDestroyResult(psResult); + CPLFree(pabyData); + return -1; + } + + CPLStripXMLNamespace( psXML, NULL, TRUE ); + CPLXMLNode* psRoot = CPLGetXMLNode( psXML, "=FeatureCollection" ); + if (psRoot == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find "); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + CPLFree(pabyData); + return -1; + } + + const char* pszValue = CPLGetXMLValue(psRoot, "numberOfFeatures", NULL); + if (pszValue == NULL) + pszValue = CPLGetXMLValue(psRoot, "numberMatched", NULL); /* WFS 2.0.0 */ + if (pszValue == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find numberOfFeatures"); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + CPLFree(pabyData); + + poDS->DisableSupportHits(); + return -1; + } + + GIntBig nFeatures = CPLAtoGIntBig(pszValue); + /* Hum, http://deegree3-testing.deegree.org:80/deegree-inspire-node/services?MAXFEATURES=10&SERVICE=WFS&VERSION=1.1.0&REQUEST=GetFeature&TYPENAME=ad:Address&OUTPUTFORMAT=text/xml;%20subtype=gml/3.2.1&RESULTTYPE=hits */ + /* returns more than MAXFEATURES features... So truncate to MAXFEATURES */ + CPLString osMaxFeatures = CPLURLGetValue(osURL, atoi(poDS->GetVersion()) >= 2 ? "COUNT" : "MAXFEATURES"); + if (osMaxFeatures.size() != 0) + { + GIntBig nMaxFeatures = CPLAtoGIntBig(osMaxFeatures); + if (nFeatures > nMaxFeatures) + { + CPLDebug("WFS", "Truncating result from " CPL_FRMT_GIB " to " CPL_FRMT_GIB, + nFeatures, nMaxFeatures); + nFeatures = nMaxFeatures; + } + } + + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + CPLFree(pabyData); + + return nFeatures; +} +/************************************************************************/ +/* CanRunGetFeatureCountAndGetExtentTogether() */ +/************************************************************************/ + +int OGRWFSLayer::CanRunGetFeatureCountAndGetExtentTogether() +{ + /* In some cases, we can evaluate the result of GetFeatureCount() */ + /* and GetExtent() with the same data */ + CPLString osRequestURL = MakeGetFeatureURL(0, FALSE); + return( !bHasExtents && nFeatures < 0 && + osRequestURL.ifind("FILTER") == std::string::npos && + osRequestURL.ifind("MAXFEATURES") == std::string::npos && + osRequestURL.ifind("COUNT") == std::string::npos && + !(GetLayerDefn()->IsGeometryIgnored()) ); +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRWFSLayer::GetFeatureCount( int bForce ) +{ + if (nFeatures >= 0) + return nFeatures; + + if (TestCapability(OLCFastFeatureCount)) + return poBaseLayer->GetFeatureCount(bForce); + + if ((m_poAttrQuery == NULL || osWFSWhere.size() != 0) && + poDS->GetFeatureSupportHits()) + { + nFeatures = ExecuteGetFeatureResultTypeHits(); + if (nFeatures >= 0) + return nFeatures; + } + + /* If we have not yet the base layer, try to read one */ + /* feature, and then query again OLCFastFeatureCount on the */ + /* base layer. In case the WFS response would contain the */ + /* number of features */ + if (poBaseLayer == NULL) + { + ResetReading(); + OGRFeature* poFeature = GetNextFeature(); + delete poFeature; + ResetReading(); + + if (TestCapability(OLCFastFeatureCount)) + return poBaseLayer->GetFeatureCount(bForce); + } + + /* In some cases, we can evaluate the result of GetFeatureCount() */ + /* and GetExtent() with the same data */ + if( CanRunGetFeatureCountAndGetExtentTogether() ) + { + OGREnvelope sDummy; + GetExtent(&sDummy); + } + + if( nFeatures < 0 ) + nFeatures = OGRLayer::GetFeatureCount(bForce); + + return nFeatures; +} + + +/************************************************************************/ +/* SetExtent() */ +/************************************************************************/ + +void OGRWFSLayer::SetExtents(double dfMinX, double dfMinY, double dfMaxX, double dfMaxY) +{ + this->dfMinX = dfMinX; + this->dfMinY = dfMinY; + this->dfMaxX = dfMaxX; + this->dfMaxY = dfMaxY; + bHasExtents = TRUE; +} + +/************************************************************************/ +/* GetExtent() */ +/************************************************************************/ + +OGRErr OGRWFSLayer::GetExtent(OGREnvelope *psExtent, int bForce) +{ + if (bHasExtents) + { + psExtent->MinX = dfMinX; + psExtent->MinY = dfMinY; + psExtent->MaxX = dfMaxX; + psExtent->MaxY = dfMaxY; + return OGRERR_NONE; + } + + /* If we have not yet the base layer, try to read one */ + /* feature, and then query again OLCFastGetExtent on the */ + /* base layer. In case the WFS response would contain the */ + /* global extent */ + if (poBaseLayer == NULL) + { + ResetReading(); + OGRFeature* poFeature = GetNextFeature(); + delete poFeature; + ResetReading(); + } + + if (TestCapability(OLCFastGetExtent)) + return poBaseLayer->GetExtent(psExtent, bForce); + + /* In some cases, we can evaluate the result of GetFeatureCount() */ + /* and GetExtent() with the same data */ + if( CanRunGetFeatureCountAndGetExtentTogether() ) + { + bCountFeaturesInGetNextFeature = TRUE; + nFeatures = 0; + } + + OGRErr eErr = OGRLayer::GetExtent(psExtent, bForce); + + if( bCountFeaturesInGetNextFeature ) + { + if( eErr == OGRERR_NONE ) + { + dfMinX = psExtent->MinX; + dfMinY = psExtent->MinY; + dfMaxX = psExtent->MaxX; + dfMaxY = psExtent->MaxY; + bHasExtents = TRUE; + } + else + { + nFeatures = -1; + } + bCountFeaturesInGetNextFeature = FALSE; + } + + return eErr; +} + +/************************************************************************/ +/* GetShortName() */ +/************************************************************************/ + +const char* OGRWFSLayer::GetShortName() +{ + const char* pszShortName = strchr(pszName, ':'); + if (pszShortName == NULL) + pszShortName = pszName; + else + pszShortName ++; + return pszShortName; +} + + +/************************************************************************/ +/* GetPostHeader() */ +/************************************************************************/ + +CPLString OGRWFSLayer::GetPostHeader() +{ + CPLString osPost; + osPost += "\n"; + osPost += "GetVersion(); osPost += "\"\n"; + osPost += " xmlns:gml=\"http://www.opengis.net/gml\"\n"; + osPost += " xmlns:ogc=\"http://www.opengis.net/ogc\"\n"; + osPost += " xsi:schemaLocation=\"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/"; + osPost += poDS->GetVersion(); + osPost += "/wfs.xsd "; + osPost += osTargetNamespace; + osPost += " "; + + char* pszXMLEncoded = CPLEscapeString( + GetDescribeFeatureTypeURL(FALSE), -1, CPLES_XML); + osPost += pszXMLEncoded; + CPLFree(pszXMLEncoded); + + osPost += "\">\n"; + + return osPost; +} + +/************************************************************************/ +/* ICreateFeature() */ +/************************************************************************/ + +OGRErr OGRWFSLayer::ICreateFeature( OGRFeature *poFeature ) +{ + if (!TestCapability(OLCSequentialWrite)) + { + if (!poDS->SupportTransactions()) + CPLError(CE_Failure, CPLE_AppDefined, + "CreateFeature() not supported: no WMS-T features advertized by server"); + else if (!poDS->UpdateMode()) + CPLError(CE_Failure, CPLE_AppDefined, + "CreateFeature() not supported: datasource opened as read-only"); + return OGRERR_FAILURE; + } + + if (poGMLFeatureClass == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot insert feature because we didn't manage to parse the .XSD schema"); + return OGRERR_FAILURE; + } + + if (poFeatureDefn->GetFieldIndex("gml_id") != 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find gml_id field"); + return OGRERR_FAILURE; + } + + if (poFeature->IsFieldSet(0)) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot insert a feature when gml_id field is already set"); + return OGRERR_FAILURE; + } + + CPLString osPost; + + const char* pszShortName = GetShortName(); + + if (!bInTransaction) + { + osPost += GetPostHeader(); + osPost += " \n"; + } + osPost += " \n"; + + int i; + for(i=1; i <= poFeature->GetFieldCount(); i++) + { + if (poGMLFeatureClass->GetGeometryPropertyCount() == 1 && + poGMLFeatureClass->GetGeometryProperty(0)->GetAttributeIndex() == i - 1) + { + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if (poGeom != NULL && osGeometryColumnName.size() != 0) + { + if (poGeom->getSpatialReference() == NULL) + poGeom->assignSpatialReference(poSRS); + char* pszGML; + if (strcmp(poDS->GetVersion(), "1.1.0") == 0) + { + char** papszOptions = CSLAddString(NULL, "FORMAT=GML3"); + pszGML = OGR_G_ExportToGMLEx((OGRGeometryH)poGeom, papszOptions); + CSLDestroy(papszOptions); + } + else + pszGML = OGR_G_ExportToGML((OGRGeometryH)poGeom); + osPost += " GetFieldCount()) + break; + + if (poFeature->IsFieldSet(i)) + { + OGRFieldDefn* poFDefn = poFeature->GetFieldDefnRef(i); + osPost += " GetNameRef(); + osPost += ">"; + if (poFDefn->GetType() == OFTInteger) + osPost += CPLSPrintf("%d", poFeature->GetFieldAsInteger(i)); + else if (poFDefn->GetType() == OFTInteger64) + osPost += CPLSPrintf(CPL_FRMT_GIB, poFeature->GetFieldAsInteger64(i)); + else if (poFDefn->GetType() == OFTReal) + osPost += CPLSPrintf("%.16g", poFeature->GetFieldAsDouble(i)); + else + { + char* pszXMLEncoded = CPLEscapeString(poFeature->GetFieldAsString(i), + -1, CPLES_XML); + osPost += pszXMLEncoded; + CPLFree(pszXMLEncoded); + } + osPost += "GetNameRef(); + osPost += ">\n"; + } + + } + + osPost += " HTTPFetch(poDS->GetPostTransactionURL(), papszOptions); + CSLDestroy(papszOptions); + + if (psResult == NULL) + { + return OGRERR_FAILURE; + } + + if (strstr((const char*)psResult->pabyData, + "pabyData, + "pabyData); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLDebug("WFS", "Response: %s", psResult->pabyData); + + CPLXMLNode* psXML = CPLParseXMLString( (const char*) psResult->pabyData ); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLStripXMLNamespace( psXML, NULL, TRUE ); + int bUse100Schema = FALSE; + CPLXMLNode* psRoot = CPLGetXMLNode( psXML, "=TransactionResponse" ); + if (psRoot == NULL) + { + psRoot = CPLGetXMLNode( psXML, "=WFS_TransactionResponse" ); + if (psRoot) + bUse100Schema = TRUE; + } + + if (psRoot == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find "); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLXMLNode* psFeatureID = NULL; + + if (bUse100Schema) + { + if (CPLGetXMLNode( psRoot, "TransactionResult.Status.FAILED" )) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Insert failed : %s", + psResult->pabyData); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + psFeatureID = + CPLGetXMLNode( psRoot, "InsertResult.FeatureId"); + if (psFeatureID == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find InsertResult.FeatureId"); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + } + else + { + psFeatureID = + CPLGetXMLNode( psRoot, "InsertResults.Feature.FeatureId"); + if (psFeatureID == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find InsertResults.Feature.FeatureId"); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + } + + const char* pszFID = CPLGetXMLValue(psFeatureID, "fid", NULL); + if (pszFID == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find fid"); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + poFeature->SetField("gml_id", pszFID); + + /* If the returned fid is of the form layer_name.num, then use */ + /* num as the OGR FID */ + if (strncmp(pszFID, pszShortName, strlen(pszShortName)) == 0 && + pszFID[strlen(pszShortName)] == '.') + { + GIntBig nFID = CPLAtoGIntBig(pszFID + strlen(pszShortName) + 1); + poFeature->SetFID(nFID); + } + + CPLDebug("WFS", "Got FID = " CPL_FRMT_GIB, poFeature->GetFID()); + + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + + /* Invalidate layer */ + bReloadNeeded = TRUE; + nFeatures = -1; + bHasExtents = FALSE; + + return OGRERR_NONE; +} + + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRWFSLayer::ISetFeature( OGRFeature *poFeature ) +{ + if (!TestCapability(OLCRandomWrite)) + { + if (!poDS->SupportTransactions()) + CPLError(CE_Failure, CPLE_AppDefined, + "SetFeature() not supported: no WMS-T features advertized by server"); + else if (!poDS->UpdateMode()) + CPLError(CE_Failure, CPLE_AppDefined, + "SetFeature() not supported: datasource opened as read-only"); + return OGRERR_FAILURE; + } + + if (poFeatureDefn->GetFieldIndex("gml_id") != 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find gml_id field"); + return OGRERR_FAILURE; + } + + if (poFeature->IsFieldSet(0) == FALSE) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot update a feature when gml_id field is not set"); + return OGRERR_FAILURE; + } + + if (bInTransaction) + { + CPLError(CE_Warning, CPLE_AppDefined, + "SetFeature() not yet dealt in transaction. Issued immediately"); + } + + const char* pszShortName = GetShortName(); + + CPLString osPost; + osPost += GetPostHeader(); + + osPost += " \n"; + + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if ( osGeometryColumnName.size() != 0 ) + { + osPost += " \n"; + osPost += " "; osPost += osGeometryColumnName; osPost += "\n"; + if (poGeom != NULL) + { + if (poGeom->getSpatialReference() == NULL) + poGeom->assignSpatialReference(poSRS); + char* pszGML; + if (strcmp(poDS->GetVersion(), "1.1.0") == 0) + { + char** papszOptions = CSLAddString(NULL, "FORMAT=GML3"); + pszGML = OGR_G_ExportToGMLEx((OGRGeometryH)poGeom, papszOptions); + CSLDestroy(papszOptions); + } + else + pszGML = OGR_G_ExportToGML((OGRGeometryH)poGeom); + osPost += " "; + osPost += pszGML; + osPost += "\n"; + CPLFree(pszGML); + } + osPost += " \n"; + } + + int i; + for(i=1; i < poFeature->GetFieldCount(); i++) + { + OGRFieldDefn* poFDefn = poFeature->GetFieldDefnRef(i); + + osPost += " \n"; + osPost += " "; osPost += poFDefn->GetNameRef(); osPost += "\n"; + if (poFeature->IsFieldSet(i)) + { + osPost += " "; + if (poFDefn->GetType() == OFTInteger) + osPost += CPLSPrintf("%d", poFeature->GetFieldAsInteger(i)); + else if (poFDefn->GetType() == OFTInteger64) + osPost += CPLSPrintf(CPL_FRMT_GIB, poFeature->GetFieldAsInteger64(i)); + else if (poFDefn->GetType() == OFTReal) + osPost += CPLSPrintf("%.16g", poFeature->GetFieldAsDouble(i)); + else + { + char* pszXMLEncoded = CPLEscapeString(poFeature->GetFieldAsString(i), + -1, CPLES_XML); + osPost += pszXMLEncoded; + CPLFree(pszXMLEncoded); + } + osPost += "\n"; + } + osPost += " \n"; + } + osPost += " \n"; + if (poDS->UseFeatureId() || bUseFeatureIdAtLayerLevel) + osPost += " GetVersion()) >= 2) + osPost += " GetFieldAsString(0); osPost += "\"/>\n"; + osPost += " \n"; + osPost += " \n"; + osPost += "\n"; + + CPLDebug("WFS", "Post : %s", osPost.c_str()); + + char** papszOptions = NULL; + papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str()); + papszOptions = CSLAddNameValue(papszOptions, "HEADERS", + "Content-Type: application/xml; charset=UTF-8"); + + CPLHTTPResult* psResult = poDS->HTTPFetch(poDS->GetPostTransactionURL(), papszOptions); + CSLDestroy(papszOptions); + + if (psResult == NULL) + { + return OGRERR_FAILURE; + } + + if (strstr((const char*)psResult->pabyData, + "pabyData, + "pabyData); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLDebug("WFS", "Response: %s", psResult->pabyData); + + CPLXMLNode* psXML = CPLParseXMLString( (const char*) psResult->pabyData ); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLStripXMLNamespace( psXML, NULL, TRUE ); + int bUse100Schema = FALSE; + CPLXMLNode* psRoot = CPLGetXMLNode( psXML, "=TransactionResponse" ); + if (psRoot == NULL) + { + psRoot = CPLGetXMLNode( psXML, "=WFS_TransactionResponse" ); + if (psRoot) + bUse100Schema = TRUE; + } + if (psRoot == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find "); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + if (bUse100Schema) + { + if (CPLGetXMLNode( psRoot, "TransactionResult.Status.FAILED" )) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Update failed : %s", + psResult->pabyData); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + } + + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + + /* Invalidate layer */ + bReloadNeeded = TRUE; + nFeatures = -1; + bHasExtents = FALSE; + + return OGRERR_NONE; +} +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature* OGRWFSLayer::GetFeature(GIntBig nFID) +{ + GetLayerDefn(); + if (poBaseLayer == NULL && poFeatureDefn->GetFieldIndex("gml_id") == 0) + { + /* This is lovely hackish. We assume that then gml_id will be */ + /* layer_name.number. This is actually what we can observe with */ + /* GeoServer and TinyOWS */ + CPLString osVal = CPLSPrintf("gml_id = '%s." CPL_FRMT_GIB "'", GetShortName(), nFID); + CPLString osOldSQLWhere(osSQLWhere); + SetAttributeFilter(osVal); + OGRFeature* poFeature = GetNextFeature(); + const char* pszOldFilter = osOldSQLWhere.size() ? osOldSQLWhere.c_str() : NULL; + SetAttributeFilter(pszOldFilter); + if (poFeature) + return poFeature; + } + + return OGRLayer::GetFeature(nFID); +} + +/************************************************************************/ +/* DeleteFromFilter() */ +/************************************************************************/ + +OGRErr OGRWFSLayer::DeleteFromFilter( CPLString osOGCFilter ) +{ + if (!TestCapability(OLCDeleteFeature)) + { + if (!poDS->SupportTransactions()) + CPLError(CE_Failure, CPLE_AppDefined, + "DeleteFromFilter() not supported: no WMS-T features advertized by server"); + else if (!poDS->UpdateMode()) + CPLError(CE_Failure, CPLE_AppDefined, + "DeleteFromFilter() not supported: datasource opened as read-only"); + return OGRERR_FAILURE; + } + + if (poFeatureDefn->GetFieldIndex("gml_id") != 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find gml_id field"); + return OGRERR_FAILURE; + } + const char* pszShortName = GetShortName(); + + CPLString osPost; + osPost += GetPostHeader(); + + osPost += " \n"; + osPost += " \n"; + osPost += osOGCFilter; + osPost += " \n"; + osPost += " \n"; + osPost += "\n"; + + CPLDebug("WFS", "Post : %s", osPost.c_str()); + + char** papszOptions = NULL; + papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str()); + papszOptions = CSLAddNameValue(papszOptions, "HEADERS", + "Content-Type: application/xml; charset=UTF-8"); + + CPLHTTPResult* psResult = poDS->HTTPFetch(poDS->GetPostTransactionURL(), papszOptions); + CSLDestroy(papszOptions); + + if (psResult == NULL) + { + return OGRERR_FAILURE; + } + + if (strstr((const char*)psResult->pabyData, "pabyData, "pabyData); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLDebug("WFS", "Response: %s", psResult->pabyData); + + CPLXMLNode* psXML = CPLParseXMLString( (const char*) psResult->pabyData ); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLStripXMLNamespace( psXML, NULL, TRUE ); + int bUse100Schema = FALSE; + CPLXMLNode* psRoot = CPLGetXMLNode( psXML, "=TransactionResponse" ); + if (psRoot == NULL) + { + psRoot = CPLGetXMLNode( psXML, "=WFS_TransactionResponse" ); + if (psRoot) + bUse100Schema = TRUE; + } + if (psRoot == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find "); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + if (bUse100Schema) + { + if (CPLGetXMLNode( psRoot, "TransactionResult.Status.FAILED" )) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Delete failed : %s", + psResult->pabyData); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + } + + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + + /* Invalidate layer */ + bReloadNeeded = TRUE; + nFeatures = -1; + bHasExtents = FALSE; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRWFSLayer::DeleteFeature( GIntBig nFID ) +{ + if (!TestCapability(OLCDeleteFeature)) + { + if (!poDS->SupportTransactions()) + CPLError(CE_Failure, CPLE_AppDefined, + "DeleteFeature() not supported: no WMS-T features advertized by server"); + else if (!poDS->UpdateMode()) + CPLError(CE_Failure, CPLE_AppDefined, + "DeleteFeature() not supported: datasource opened as read-only"); + return OGRERR_FAILURE; + } + + if (poFeatureDefn->GetFieldIndex("gml_id") != 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find gml_id field"); + return OGRERR_FAILURE; + } + + OGRFeature* poFeature = GetFeature(nFID); + if (poFeature == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find feature " CPL_FRMT_GIB, nFID); + return OGRERR_FAILURE; + } + + const char* pszGMLID = poFeature->GetFieldAsString("gml_id"); + if (pszGMLID == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot delete a feature with gml_id unset"); + delete poFeature; + return OGRERR_FAILURE; + } + + if (bInTransaction) + { + CPLError(CE_Warning, CPLE_AppDefined, + "DeleteFeature() not yet dealt in transaction. Issued immediately"); + } + + CPLString osGMLID = pszGMLID; + pszGMLID = NULL; + delete poFeature; + poFeature = NULL; + + CPLString osFilter; + osFilter = "\n"; + return DeleteFromFilter(osFilter); +} + + +/************************************************************************/ +/* StartTransaction() */ +/************************************************************************/ + +OGRErr OGRWFSLayer::StartTransaction() +{ + if (!TestCapability(OLCTransactions)) + { + if (!poDS->SupportTransactions()) + CPLError(CE_Failure, CPLE_AppDefined, + "StartTransaction() not supported: no WMS-T features advertized by server"); + else if (!poDS->UpdateMode()) + CPLError(CE_Failure, CPLE_AppDefined, + "StartTransaction() not supported: datasource opened as read-only"); + return OGRERR_FAILURE; + } + + if (bInTransaction) + { + CPLError(CE_Failure, CPLE_AppDefined, + "StartTransaction() has already been called"); + return OGRERR_FAILURE; + } + + bInTransaction = TRUE; + osGlobalInsert = ""; + nExpectedInserts = 0; + aosFIDList.resize(0); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +OGRErr OGRWFSLayer::CommitTransaction() +{ + if (!TestCapability(OLCTransactions)) + { + if (!poDS->SupportTransactions()) + CPLError(CE_Failure, CPLE_AppDefined, + "CommitTransaction() not supported: no WMS-T features advertized by server"); + else if (!poDS->UpdateMode()) + CPLError(CE_Failure, CPLE_AppDefined, + "CommitTransaction() not supported: datasource opened as read-only"); + return OGRERR_FAILURE; + } + + if (!bInTransaction) + { + CPLError(CE_Failure, CPLE_AppDefined, + "StartTransaction() has not yet been called"); + return OGRERR_FAILURE; + } + + if (osGlobalInsert.size() != 0) + { + CPLString osPost = GetPostHeader(); + osPost += " \n"; + osPost += osGlobalInsert; + osPost += " \n"; + osPost += "\n"; + + bInTransaction = FALSE; + osGlobalInsert = ""; + int nExpectedInserts = this->nExpectedInserts; + this->nExpectedInserts = 0; + + CPLDebug("WFS", "Post : %s", osPost.c_str()); + + char** papszOptions = NULL; + papszOptions = CSLAddNameValue(papszOptions, "POSTFIELDS", osPost.c_str()); + papszOptions = CSLAddNameValue(papszOptions, "HEADERS", + "Content-Type: application/xml; charset=UTF-8"); + + CPLHTTPResult* psResult = poDS->HTTPFetch(poDS->GetPostTransactionURL(), papszOptions); + CSLDestroy(papszOptions); + + if (psResult == NULL) + { + return OGRERR_FAILURE; + } + + if (strstr((const char*)psResult->pabyData, + "pabyData, + "pabyData); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLDebug("WFS", "Response: %s", psResult->pabyData); + + CPLXMLNode* psXML = CPLParseXMLString( (const char*) psResult->pabyData ); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s", + psResult->pabyData); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLStripXMLNamespace( psXML, NULL, TRUE ); + int bUse100Schema = FALSE; + CPLXMLNode* psRoot = CPLGetXMLNode( psXML, "=TransactionResponse" ); + if (psRoot == NULL) + { + psRoot = CPLGetXMLNode( psXML, "=WFS_TransactionResponse" ); + if (psRoot) + bUse100Schema = TRUE; + } + + if (psRoot == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find "); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + if (bUse100Schema) + { + if (CPLGetXMLNode( psRoot, "TransactionResult.Status.FAILED" )) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Insert failed : %s", + psResult->pabyData); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + /* TODO */ + } + else + { + int nGotInserted = atoi(CPLGetXMLValue(psRoot, "TransactionSummary.totalInserted", "")); + if (nGotInserted != nExpectedInserts) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Only %d features were inserted whereas %d where expected", + nGotInserted, nExpectedInserts); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + CPLXMLNode* psInsertResults = + CPLGetXMLNode( psRoot, "InsertResults"); + if (psInsertResults == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find node InsertResults"); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + + aosFIDList.resize(0); + + CPLXMLNode* psChild = psInsertResults->psChild; + while(psChild) + { + const char* pszFID = CPLGetXMLValue(psChild, "FeatureId.fid", NULL); + if (pszFID == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot find fid"); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + aosFIDList.push_back(pszFID); + + psChild = psChild->psNext; + } + + if ((int)aosFIDList.size() != nGotInserted) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Inconsistent InsertResults: did not get expected FID count"); + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + return OGRERR_FAILURE; + } + } + + CPLDestroyXMLNode( psXML ); + CPLHTTPDestroyResult(psResult); + } + + bInTransaction = FALSE; + osGlobalInsert = ""; + nExpectedInserts = 0; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +OGRErr OGRWFSLayer::RollbackTransaction() +{ + if (!TestCapability(OLCTransactions)) + { + if (!poDS->SupportTransactions()) + CPLError(CE_Failure, CPLE_AppDefined, + "RollbackTransaction() not supported: no WMS-T features advertized by server"); + else if (!poDS->UpdateMode()) + CPLError(CE_Failure, CPLE_AppDefined, + "RollbackTransaction() not supported: datasource opened as read-only"); + return OGRERR_FAILURE; + } + + if (!bInTransaction) + { + CPLError(CE_Failure, CPLE_AppDefined, + "StartTransaction() has not yet been called"); + return OGRERR_FAILURE; + } + + bInTransaction = FALSE; + osGlobalInsert = ""; + nExpectedInserts = 0; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetRequiredOutputFormat() */ +/************************************************************************/ + +void OGRWFSLayer::SetRequiredOutputFormat(const char* pszRequiredOutputFormatIn) +{ + CPLFree(pszRequiredOutputFormat); + if (pszRequiredOutputFormatIn) + { + pszRequiredOutputFormat = CPLStrdup(pszRequiredOutputFormatIn); + } + else + { + pszRequiredOutputFormat = NULL; + } +} + +/************************************************************************/ +/* SetOrderBy() */ +/************************************************************************/ + +void OGRWFSLayer::SetOrderBy(const std::vector& aoSortColumnsIn) +{ + aoSortColumns = aoSortColumnsIn; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/GNUmakefile new file mode 100644 index 000000000..732182ca1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/GNUmakefile @@ -0,0 +1,14 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrxlsdriver.o ogrxlsdatasource.o ogrxlslayer.o + +CPPFLAGS := -I.. -I../.. $(FREEXL_INCLUDE) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_xls.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/drv_xls.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/drv_xls.html new file mode 100644 index 000000000..1a4f72b02 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/drv_xls.html @@ -0,0 +1,35 @@ + + +XLS - MS Excel format + + + + +

      XLS - MS Excel format

      + +(GDAL/OGR >= 1.9.0)

      + +This driver reads spreadsheets in MS Excel format. GDAL/OGR must be built against the FreeXL library (GPL/LPL/MPL licenced), +and the driver has the same restrictions as the FreeXL library itself as far as which and how Excel files are supported. +(At the time of writing - with FreeXL 1.0.0a -, it means in particular that formulas are not supported.)

      + +Each sheet is presented as a OGR layer. No geometry support is available directly (but you may use the OGR VRT capabilities for that).

      + +

      Configuration options

      + +
        +
      • OGR_XLS_HEADERS = FORCE / DISABLE / AUTO : By default, the driver will read the first lines of each sheet to detect if the +first line might be the name of columns. If set to FORCE, the driver will consider the first line will be taken as the header line. +If set to DISABLE, it will be considered as the first feature. Otherwise auto-dection will occur.
      • +
      • OGR_XLS_FIELD_TYPES = STRING / AUTO : By default, the driver will try to detect the data type of fields. If set to STRING, +all fields will be of String type.
      • +
      + +

      See Also

      + + + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/makefile.vc new file mode 100644 index 000000000..cb0229f76 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/makefile.vc @@ -0,0 +1,15 @@ + +OBJ = ogrxlsdriver.obj ogrxlsdatasource.obj ogrxlslayer.obj +EXTRAFLAGS = -I.. -I..\.. $(FREEXL_CFLAGS) + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/ogr_xls.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/ogr_xls.h new file mode 100644 index 000000000..7e972f70e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/ogr_xls.h @@ -0,0 +1,129 @@ +/****************************************************************************** + * $Id: ogr_xls.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: XLS Translator + * Purpose: Definition of classes for OGR .xls driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_XLS_H_INCLUDED +#define _OGR_XLS_H_INCLUDED + +#include "ogrsf_frmts.h" + +/************************************************************************/ +/* OGRXLSLayer */ +/************************************************************************/ + +class OGRXLSDataSource; + +class OGRXLSLayer : public OGRLayer +{ + OGRXLSDataSource* poDS; + OGRFeatureDefn* poFeatureDefn; + + char *pszName; + int iSheet; + int bFirstLineIsHeaders; + int nRows; + unsigned short nCols; + + int nNextFID; + + OGRFeature * GetNextRawFeature(); + + void DetectHeaderLine(const void* xlshandle); + void DetectColumnTypes(const void* xlshandle, + int* paeFieldTypes); + + public: + OGRXLSLayer(OGRXLSDataSource* poDSIn, + const char* pszSheetname, + int iSheetIn, + int nRowsIn, + unsigned short nColsIn); + ~OGRXLSLayer(); + + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + + virtual OGRFeatureDefn * GetLayerDefn(); + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + + virtual const char *GetName() { return pszName; } + virtual OGRwkbGeometryType GetGeomType() { return wkbNone; } + + virtual int TestCapability( const char * ); + + virtual OGRSpatialReference *GetSpatialRef() { return NULL; } + +}; + +/************************************************************************/ +/* OGRXLSDataSource */ +/************************************************************************/ + +class OGRXLSDataSource : public OGRDataSource +{ + char* pszName; + + OGRLayer** papoLayers; + int nLayers; + + const void* xlshandle; + + public: + OGRXLSDataSource(); + ~OGRXLSDataSource(); + + int Open( const char * pszFilename, + int bUpdate ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); + + const void *GetXLSHandle(); +}; + +/************************************************************************/ +/* OGRXLSDriver */ +/************************************************************************/ + +class OGRXLSDriver : public OGRSFDriver +{ + public: + ~OGRXLSDriver(); + + virtual const char* GetName(); + virtual OGRDataSource* Open( const char *, int ); + virtual int TestCapability( const char * ); +}; + + +#endif /* ndef _OGR_XLS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/ogrxlsdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/ogrxlsdatasource.cpp new file mode 100644 index 000000000..23ec9cb8b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/ogrxlsdatasource.cpp @@ -0,0 +1,170 @@ +/****************************************************************************** + * $Id: ogrxlsdatasource.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: XLS Translator + * Purpose: Implements OGRXLSDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include + +#ifdef _WIN32 +# include +#endif + +#include "ogr_xls.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrxlsdatasource.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGRXLSDataSource() */ +/************************************************************************/ + +OGRXLSDataSource::OGRXLSDataSource() + +{ + papoLayers = NULL; + nLayers = 0; + + pszName = NULL; + + xlshandle = NULL; +} + +/************************************************************************/ +/* ~OGRXLSDataSource() */ +/************************************************************************/ + +OGRXLSDataSource::~OGRXLSDataSource() + +{ + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + + CPLFree( pszName ); + + if (xlshandle) + freexl_close(xlshandle); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRXLSDataSource::TestCapability( CPL_UNUSED const char * pszCap ) + +{ + return FALSE; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRXLSDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRXLSDataSource::Open( const char * pszFilename, int bUpdateIn) + +{ + if (bUpdateIn) + { + return FALSE; + } + +#ifdef _WIN32 + if( CSLTestBoolean( CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + pszName = CPLRecode( pszFilename, CPL_ENC_UTF8, CPLString().Printf( "CP%d", GetACP() ) ); + else + pszName = CPLStrdup( pszFilename ); +#else + pszName = CPLStrdup( pszFilename ); +#endif + +// -------------------------------------------------------------------- +// Does this appear to be a .xls file? +// -------------------------------------------------------------------- + + /* Open only for getting info. To get cell values, we have to use freexl_open */ + if (freexl_open_info (pszName, &xlshandle) != FREEXL_OK) + return FALSE; + + unsigned int nSheets = 0; + if (freexl_get_info (xlshandle, FREEXL_BIFF_SHEET_COUNT, &nSheets) != FREEXL_OK) + return FALSE; + + for(unsigned short i=0; i<(unsigned short)nSheets; i++) + { + freexl_select_active_worksheet(xlshandle, i); + + const char* pszSheetname = NULL; + if (freexl_get_worksheet_name(xlshandle, i, &pszSheetname) != FREEXL_OK) + return FALSE; + + unsigned int nRows = 0; + unsigned short nCols = 0; + if (freexl_worksheet_dimensions(xlshandle, &nRows, &nCols) != FREEXL_OK) + return FALSE; + + /* Skip empty sheets */ + if (nRows == 0) + continue; + + papoLayers = (OGRLayer**) CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers ++] = new OGRXLSLayer(this, pszSheetname, i, (int)nRows, nCols); + } + + freexl_close(xlshandle); + xlshandle = NULL; + + return TRUE; +} + +/************************************************************************/ +/* GetXLSHandle() */ +/************************************************************************/ + +const void* OGRXLSDataSource::GetXLSHandle() +{ + if (xlshandle) + return xlshandle; + + if (freexl_open (pszName, &xlshandle) != FREEXL_OK) + return NULL; + + return xlshandle; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/ogrxlsdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/ogrxlsdriver.cpp new file mode 100644 index 000000000..9071ab15b --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/ogrxlsdriver.cpp @@ -0,0 +1,107 @@ +/****************************************************************************** + * $Id: ogrxlsdriver.cpp 27794 2014-10-04 10:13:46Z rouault $ + * + * Project: XLS Translator + * Purpose: Implements OGRXLSDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_xls.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrxlsdriver.cpp 27794 2014-10-04 10:13:46Z rouault $"); + +/************************************************************************/ +/* ~OGRXLSDriver() */ +/************************************************************************/ + +OGRXLSDriver::~OGRXLSDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRXLSDriver::GetName() + +{ + return "XLS"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRXLSDriver::Open( const char * pszFilename, int bUpdate ) + +{ + if (bUpdate) + { + return NULL; + } + + if (!EQUAL(CPLGetExtension(pszFilename), "XLS")) + { + return NULL; + } + + OGRXLSDataSource *poDS = new OGRXLSDataSource(); + + if( !poDS->Open( pszFilename, bUpdate ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRXLSDriver::TestCapability( CPL_UNUSED const char * pszCap ) + +{ + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRXLS() */ +/************************************************************************/ + +void RegisterOGRXLS() + +{ + OGRSFDriver* poDriver = new OGRXLSDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "MS Excel format" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "xls" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_xls.html" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/ogrxlslayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/ogrxlslayer.cpp new file mode 100644 index 000000000..1fdd9366a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xls/ogrxlslayer.cpp @@ -0,0 +1,376 @@ +/****************************************************************************** + * $Id: ogrxlslayer.cpp 28382 2015-01-30 15:29:41Z rouault $ + * + * Project: XLS Translator + * Purpose: Implements OGRXLSLayer class. + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2011-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include + +#include "ogr_xls.h" +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: ogrxlslayer.cpp 28382 2015-01-30 15:29:41Z rouault $"); + +/************************************************************************/ +/* OGRXLSLayer() */ +/************************************************************************/ + +OGRXLSLayer::OGRXLSLayer( OGRXLSDataSource* poDSIn, + const char* pszSheetname, + int iSheetIn, + int nRowsIn, + unsigned short nColsIn ) + +{ + poDS = poDSIn; + iSheet = iSheetIn; + nNextFID = 0; + bFirstLineIsHeaders = FALSE; + poFeatureDefn = NULL; + pszName = CPLStrdup(pszSheetname); + nRows = nRowsIn; + nCols = nColsIn; + SetDescription( pszName ); +} + +/************************************************************************/ +/* ~OGRXLSLayer() */ +/************************************************************************/ + +OGRXLSLayer::~OGRXLSLayer() + +{ + CPLFree(pszName); + if (poFeatureDefn) + poFeatureDefn->Release(); +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRXLSLayer::ResetReading() + +{ + if (poFeatureDefn != NULL) + { + nNextFID = bFirstLineIsHeaders ? 1 : 0; + } +} + +/************************************************************************/ +/* DetectHeaderLine() */ +/************************************************************************/ + +void OGRXLSLayer::DetectHeaderLine(const void* xlshandle) + +{ + unsigned short i; + FreeXL_CellValue sCellValue; + int nCountTextOnSecondLine = 0; + for(i = 0; i < nCols && nRows >= 2; i ++) + { + if (freexl_get_cell_value(xlshandle, 0, i, &sCellValue) == FREEXL_OK) + { + if (sCellValue.type != FREEXL_CELL_TEXT && + sCellValue.type != FREEXL_CELL_SST_TEXT) + { + /* If the values in the first line are not text, then it is */ + /* not a header line */ + break; + } + } + if (freexl_get_cell_value(xlshandle, 1, i, &sCellValue) == FREEXL_OK) + { + if (sCellValue.type == FREEXL_CELL_TEXT || + sCellValue.type == FREEXL_CELL_SST_TEXT) + { + /* If there are only text values on the second line, then we cannot */ + /* know if it is a header line or just a regular line */ + nCountTextOnSecondLine ++; + } + } + } + + const char* pszXLSHeaders = CPLGetConfigOption("OGR_XLS_HEADERS", ""); + if (EQUAL(pszXLSHeaders, "FORCE")) + bFirstLineIsHeaders = TRUE; + else if (EQUAL(pszXLSHeaders, "DISABLE")) + bFirstLineIsHeaders = FALSE; + else if (i == nCols && nCountTextOnSecondLine != nCols) + bFirstLineIsHeaders = TRUE; +} + +/************************************************************************/ +/* DetectColumnTypes() */ +/************************************************************************/ + +void OGRXLSLayer::DetectColumnTypes(const void* xlshandle, + int* paeFieldTypes) + +{ + int j; + unsigned short i; + FreeXL_CellValue sCellValue; + for(j = bFirstLineIsHeaders ? 1 : 0; j < nRows; j ++) + { + for(i = 0; i < nCols; i ++) + { + if (freexl_get_cell_value(xlshandle, j, i, &sCellValue) == FREEXL_OK) + { + OGRFieldType eType = (OGRFieldType) paeFieldTypes[i]; + switch (sCellValue.type) + { + case FREEXL_CELL_INT: + eType = OFTInteger; + break; + case FREEXL_CELL_DOUBLE: + eType = OFTReal; + break; + case FREEXL_CELL_TEXT: + case FREEXL_CELL_SST_TEXT: + eType = OFTString; + break; + case FREEXL_CELL_DATE: + eType = OFTDate; + break; + case FREEXL_CELL_DATETIME: + eType = OFTDateTime; + break; + case FREEXL_CELL_TIME: + eType = OFTTime; + break; + case FREEXL_CELL_NULL: + break; + default: + break; + } + + if (paeFieldTypes[i] < 0) + { + paeFieldTypes[i] = (int) eType; + } + else if ((int)eType != paeFieldTypes[i]) + { + if ((paeFieldTypes[i] == OFTDate || + paeFieldTypes[i] == OFTTime || + paeFieldTypes[i] == OFTDateTime) && + (eType == OFTDate || eType == OFTTime || eType == OFTDateTime)) + paeFieldTypes[i] = OFTDateTime; + else if (paeFieldTypes[i] == OFTReal && eType == OFTInteger) + /* nothing */ ; + else if (paeFieldTypes[i] == OFTInteger && eType == OFTReal) + paeFieldTypes[i] = OFTReal; + else + paeFieldTypes[i] = OFTString; + } + } + } + } +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRXLSLayer::GetLayerDefn() +{ + if (poFeatureDefn) + return poFeatureDefn; + + poFeatureDefn = new OGRFeatureDefn( pszName ); + poFeatureDefn->Reference(); + poFeatureDefn->SetGeomType( wkbNone ); + + const void* xlshandle = poDS->GetXLSHandle(); + if (xlshandle == NULL) + return poFeatureDefn; + + freexl_select_active_worksheet(xlshandle, (unsigned short)iSheet); + + if (nRows > 0) + { + unsigned short i; + FreeXL_CellValue sCellValue; + + DetectHeaderLine(xlshandle); + + int* paeFieldTypes = (int* ) + CPLMalloc(nCols * sizeof(int)); + for(i = 0; i < nCols; i ++) + { + paeFieldTypes[i] = -1; + } + + const char* pszXLSFieldTypes = + CPLGetConfigOption("OGR_XLS_FIELD_TYPES", ""); + if (!EQUAL(pszXLSFieldTypes, "STRING")) + DetectColumnTypes(xlshandle, paeFieldTypes); + + for(i = 0; i < nCols; i ++) + { + OGRFieldType eType = (OGRFieldType) paeFieldTypes[i]; + if (paeFieldTypes[i] < 0) + eType = OFTString; + if (bFirstLineIsHeaders && + freexl_get_cell_value(xlshandle, 0, i, &sCellValue) == FREEXL_OK && + (sCellValue.type == FREEXL_CELL_TEXT || + sCellValue.type == FREEXL_CELL_SST_TEXT)) + { + OGRFieldDefn oField(sCellValue.value.text_value, eType); + poFeatureDefn->AddFieldDefn(&oField); + } + else + { + OGRFieldDefn oField(CPLSPrintf("Field%d", i+1), eType); + poFeatureDefn->AddFieldDefn(&oField); + } + } + + CPLFree(paeFieldTypes); + + } + + ResetReading(); + + return poFeatureDefn; +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRXLSLayer::GetFeatureCount( int bForce ) +{ + if ( m_poAttrQuery == NULL /* && m_poFilterGeom == NULL */ ) + { + const char* pszXLSHeaders = CPLGetConfigOption("OGR_XLS_HEADERS", ""); + if(EQUAL(pszXLSHeaders, "DISABLE")) + return nRows; + + GetLayerDefn(); + return bFirstLineIsHeaders ? nRows - 1 : nRows; + } + + return OGRLayer::GetFeatureCount(bForce); +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRXLSLayer::GetNextFeature() +{ + GetLayerDefn(); + + OGRFeature *poFeature; + + while(TRUE) + { + poFeature = GetNextRawFeature(); + if (poFeature == NULL) + return NULL; + + if(/*(m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && */ (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + else + delete poFeature; + } +} + +/************************************************************************/ +/* GetNextRawFeature() */ +/************************************************************************/ + +OGRFeature *OGRXLSLayer::GetNextRawFeature() +{ + if (nNextFID == nRows) + return NULL; + + const void* xlshandle = poDS->GetXLSHandle(); + if (xlshandle == NULL) + return NULL; + + freexl_select_active_worksheet(xlshandle, (unsigned short)iSheet); + + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + FreeXL_CellValue sCellValue; + for(unsigned short i=0;i<(unsigned short )poFeatureDefn->GetFieldCount(); i++) + { + if (freexl_get_cell_value(xlshandle, nNextFID, i, &sCellValue) == FREEXL_OK) + { + switch (sCellValue.type) + { + case FREEXL_CELL_INT: + poFeature->SetField(i, sCellValue.value.int_value); + break; + case FREEXL_CELL_DOUBLE: + poFeature->SetField(i, sCellValue.value.double_value); + break; + case FREEXL_CELL_TEXT: + case FREEXL_CELL_SST_TEXT: + poFeature->SetField(i, sCellValue.value.text_value); + break; + case FREEXL_CELL_DATE: + case FREEXL_CELL_DATETIME: + case FREEXL_CELL_TIME: + poFeature->SetField(i, sCellValue.value.text_value); + break; + case FREEXL_CELL_NULL: + break; + default: + CPLDebug("XLS", "Unknown cell type = %d", sCellValue.type); + break; + } + } + } + + poFeature->SetFID(nNextFID + 1); + nNextFID ++; + + return poFeature; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRXLSLayer::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap, OLCFastFeatureCount) ) + return m_poAttrQuery == NULL /* && m_poFilterGeom == NULL */; + + return FALSE; +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/GNUmakefile new file mode 100644 index 000000000..97fb5478f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/GNUmakefile @@ -0,0 +1,18 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrxlsxdriver.o ogrxlsxdatasource.o + +ifeq ($(HAVE_EXPAT),yes) +CPPFLAGS += -DHAVE_EXPAT +endif + +CPPFLAGS := -I.. -I../.. -I../mem $(EXPAT_INCLUDE) $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) + +$(O_OBJ): ogr_xlsx.h ../mem/ogr_mem.h \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/drv_xlsx.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/drv_xlsx.html new file mode 100644 index 000000000..956a46851 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/drv_xlsx.html @@ -0,0 +1,39 @@ + + +XLSX - Office Open XML spreadsheet + + + + +

      XLSX - MS Office Open XML spreadsheet

      + +(GDAL/OGR >= 1.10.0)

      + +This driver can read, write and update spreadsheets in Microsoft Office Open XML (a.k.a. OOXML) spreadsheet format, generated by applications like Microsoft Office 2007 and later versions. +LibreOffice/OpenOffice can also export documents in that format since their v3 version.

      + +The driver is only available if GDAL/OGR is compiled against the Expat library.

      + +Each sheet is presented as a OGR layer. No geometry support is available directly (but you may use the OGR VRT capabilities for that).

      + +Note 1 : depending on the application that produced the file, the driver might succeed or not in retrieving the result of formulas. Some +applications write the evaluated result of formulas in the document, in which case the driver will be able to retrieve it. Otherwise the +raw formula string will be returned.

      + +Note 2 : spreadsheets with passwords are not supported.

      + +Note 3 : when updating an existing document, all existing styles, formatting, formulas and other concepts (charts, drawings, macros, ...) +not understood by OGR will be lost : the document is re-written from scratch from the OGR data model.

      + +

      Configuration options

      + +
        +
      • OGR_XLSX_HEADERS = FORCE / DISABLE / AUTO : By default, the driver will read the first lines of each sheet to detect if the +first line might be the name of columns. If set to FORCE, the driver will consider the first line will be taken as the header line. +If set to DISABLE, it will be considered as the first feature. Otherwise auto-dection will occur.
      • +
      • OGR_XLSX_FIELD_TYPES = STRING / AUTO : By default, the driver will try to detect the data type of fields. If set to STRING, +all fields will be of String type.
      • +
      + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/makefile.vc new file mode 100644 index 000000000..6d15dd1ce --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/makefile.vc @@ -0,0 +1,20 @@ + +OBJ = ogrxlsxdriver.obj ogrxlsxdatasource.obj + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +!IFDEF EXPAT_DIR +EXTRAFLAGS = -I.. -I..\.. -I..\mem $(EXPAT_INCLUDE) -DHAVE_EXPAT=1 +!ELSE +EXTRAFLAGS = -I.. -I..\.. -I..\mem +!ENDIF + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/ogr_xlsx.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/ogr_xlsx.h new file mode 100644 index 000000000..abefe672f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/ogr_xlsx.h @@ -0,0 +1,276 @@ +/****************************************************************************** + * $Id: ogr_xlsx.h 28900 2015-04-14 09:40:34Z rouault $ + * + * Project: XLSX Translator + * Purpose: Definition of classes for OGR OpenOfficeSpreadsheet .xlsx driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_XLSX_H_INCLUDED +#define _OGR_XLSX_H_INCLUDED + +#include "ogrsf_frmts.h" + +#include "ogr_expat.h" +#include "ogr_mem.h" + +#include +#include +#include + +/************************************************************************/ +/* OGRXLSXLayer */ +/************************************************************************/ + +class OGRXLSXDataSource; + +class OGRXLSXLayer : public OGRMemLayer +{ + int bInit; + OGRXLSXDataSource* poDS; + int nSheetId; + void Init(); + int bUpdated; + int bHasHeaderLine; + + public: + OGRXLSXLayer( OGRXLSXDataSource* poDSIn, + int nSheetIdIn, + const char * pszName, + int bUpdateIn = FALSE); + + int HasBeenUpdated() { return bUpdated; } + void SetUpdated(int bUpdatedIn = TRUE); + + int GetHasHeaderLine() { return bHasHeaderLine; } + void SetHasHeaderLine(int bIn) { bHasHeaderLine = bIn; } + + const char *GetName() { return OGRMemLayer::GetLayerDefn()->GetName(); }; + OGRwkbGeometryType GetGeomType() { return wkbNone; } + virtual OGRSpatialReference *GetSpatialRef() { return NULL; } + + void ResetReading() + { Init(); OGRMemLayer::ResetReading(); } + + /* For external usage. Mess with FID */ + virtual OGRFeature * GetNextFeature(); + virtual OGRFeature *GetFeature( GIntBig nFeatureId ); + virtual OGRErr ISetFeature( OGRFeature *poFeature ); + virtual OGRErr DeleteFeature( GIntBig nFID ); + + virtual OGRErr SetNextByIndex( GIntBig nIndex ) + { Init(); return OGRMemLayer::SetNextByIndex(nIndex); } + + OGRErr ICreateFeature( OGRFeature *poFeature ) + { Init(); SetUpdated(); return OGRMemLayer::ICreateFeature(poFeature); } + + OGRFeatureDefn * GetLayerDefn() + { Init(); return OGRMemLayer::GetLayerDefn(); } + + GIntBig GetFeatureCount( int bForce ) + { Init(); return OGRMemLayer::GetFeatureCount(bForce); } + + virtual OGRErr CreateField( OGRFieldDefn *poField, + int bApproxOK = TRUE ) + { Init(); SetUpdated(); return OGRMemLayer::CreateField(poField, bApproxOK); } + + virtual OGRErr DeleteField( int iField ) + { Init(); SetUpdated(); return OGRMemLayer::DeleteField(iField); } + + virtual OGRErr ReorderFields( int* panMap ) + { Init(); SetUpdated(); return OGRMemLayer::ReorderFields(panMap); } + + virtual OGRErr AlterFieldDefn( int iField, OGRFieldDefn* poNewFieldDefn, int nFlags ) + { Init(); SetUpdated(); return OGRMemLayer::AlterFieldDefn(iField, poNewFieldDefn, nFlags); } + + int TestCapability( const char * pszCap ) + { Init(); return OGRMemLayer::TestCapability(pszCap); } + + virtual OGRErr SyncToDisk(); +}; + +/************************************************************************/ +/* OGRXLSXDataSource */ +/************************************************************************/ +#define STACK_SIZE 5 + +typedef enum +{ + STATE_DEFAULT, + + /* for sharedString.xml */ + STATE_T, + + /* for sheet?.xml */ + STATE_SHEETDATA, + STATE_ROW, + STATE_CELL, + STATE_TEXTV, +} HandlerStateEnum; + +typedef struct +{ + HandlerStateEnum eVal; + int nBeginDepth; +} HandlerState; + +class XLSXFieldTypeExtended +{ +public: + OGRFieldType eType; + int bHasMS; + + XLSXFieldTypeExtended() : eType(OFTMaxType), bHasMS(FALSE) {} + XLSXFieldTypeExtended(OGRFieldType eType, + int bHasMS = FALSE) : + eType(eType), bHasMS(bHasMS) {} +}; + +class OGRXLSXDataSource : public OGRDataSource +{ + char* pszName; + int bUpdatable; + int bUpdated; + + int nLayers; + OGRLayer **papoLayers; + + void AnalyseSharedStrings(VSILFILE* fpSharedStrings); + void AnalyseWorkbook(VSILFILE* fpWorkbook); + void AnalyseStyles(VSILFILE* fpStyles); + + std::vector apoSharedStrings; + std::string osCurrentString; + + int bFirstLineIsHeaders; + int bAutodetectTypes; + + XML_Parser oParser; + int bStopParsing; + int nWithoutEventCounter; + int nDataHandlerCounter; + int nCurLine; + int nCurCol; + + OGRXLSXLayer *poCurLayer; + + int nStackDepth; + int nDepth; + HandlerState stateStack[STACK_SIZE]; + + CPLString osValueType; + CPLString osValue; + + std::vector apoFirstLineValues; + std::vector apoFirstLineTypes; + std::vector apoCurLineValues; + std::vector apoCurLineTypes; + + int bInCellXFS; + std::map apoMapStyleFormats; + std::vector apoStyles; + + void PushState(HandlerStateEnum eVal); + void startElementDefault(const char *pszName, const char **ppszAttr); + void startElementTable(const char *pszName, const char **ppszAttr); + void endElementTable(const char *pszName); + void startElementRow(const char *pszName, const char **ppszAttr); + void endElementRow(const char *pszName); + void startElementCell(const char *pszName, const char **ppszAttr); + void endElementCell(const char *pszName); + void dataHandlerTextV(const char *data, int nLen); + + void DetectHeaderLine(); + + OGRFieldType GetOGRFieldType(const char* pszValue, + const char* pszValueType); + + void DeleteLayer( const char *pszLayerName ); + + public: + OGRXLSXDataSource(); + ~OGRXLSXDataSource(); + + int Open( const char * pszFilename, + VSILFILE* fpWorkbook, + VSILFILE* fpSharedStrings, + VSILFILE* fpStyles, + int bUpdate ); + int Create( const char * pszName, char **papszOptions ); + + virtual const char* GetName() { return pszName; } + + virtual int GetLayerCount(); + virtual OGRLayer* GetLayer( int ); + + virtual int TestCapability( const char * ); + + virtual OGRLayer* ICreateLayer( const char * pszLayerName, + OGRSpatialReference *poSRS, + OGRwkbGeometryType eType, + char ** papszOptions ); + virtual OGRErr DeleteLayer(int iLayer); + + virtual void FlushCache(); + + void startElementCbk(const char *pszName, const char **ppszAttr); + void endElementCbk(const char *pszName); + void dataHandlerCbk(const char *data, int nLen); + + void startElementSSCbk(const char *pszName, const char **ppszAttr); + void endElementSSCbk(const char *pszName); + void dataHandlerSSCbk(const char *data, int nLen); + + void startElementWBCbk(const char *pszName, const char **ppszAttr); + + void startElementStylesCbk(const char *pszName, const char **ppszAttr); + void endElementStylesCbk(const char *pszName); + + void BuildLayer(OGRXLSXLayer* poLayer, int nSheetId); + + int GetUpdatable() { return bUpdatable; } + void SetUpdated() { bUpdated = TRUE; } +}; + +/************************************************************************/ +/* OGRXLSXDriver */ +/************************************************************************/ + +class OGRXLSXDriver : public OGRSFDriver +{ + public: + ~OGRXLSXDriver(); + + virtual const char* GetName(); + virtual OGRDataSource* Open( const char *, int ); + virtual int TestCapability( const char * ); + + virtual OGRDataSource *CreateDataSource( const char *pszName, + char ** = NULL ); + virtual OGRErr DeleteDataSource( const char *pszName ); + +}; + + +#endif /* ndef _OGR_XLSX_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/ogrxlsxdatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/ogrxlsxdatasource.cpp new file mode 100644 index 000000000..8a6add27a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/ogrxlsxdatasource.cpp @@ -0,0 +1,2034 @@ +/****************************************************************************** + * $Id: ogrxlsxdatasource.cpp 28900 2015-04-14 09:40:34Z rouault $ + * + * Project: XLSX Translator + * Purpose: Implements OGRXLSXDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_xlsx.h" +#include "ogr_p.h" +#include "cpl_conv.h" +#include "cpl_time.h" + +CPL_CVSID("$Id: ogrxlsxdatasource.cpp 28900 2015-04-14 09:40:34Z rouault $"); + +/************************************************************************/ +/* OGRXLSXLayer() */ +/************************************************************************/ + +OGRXLSXLayer::OGRXLSXLayer( OGRXLSXDataSource* poDSIn, + int nSheetIdIn, + const char * pszName, + int bUpdatedIn) : + OGRMemLayer(pszName, NULL, wkbNone) +{ + bInit = FALSE; + nSheetId = nSheetIdIn; + poDS = poDSIn; + bUpdated = bUpdatedIn; + bHasHeaderLine = FALSE; +} + +/************************************************************************/ +/* Init() */ +/************************************************************************/ + +void OGRXLSXLayer::Init() +{ + if (!bInit) + { + bInit = TRUE; + CPLDebug("XLSX", "Init(%s)", GetName()); + poDS->BuildLayer(this, nSheetId); + } +} + +/************************************************************************/ +/* Updated() */ +/************************************************************************/ + +void OGRXLSXLayer::SetUpdated(int bUpdatedIn) +{ + if (bUpdatedIn && !bUpdated && poDS->GetUpdatable()) + { + bUpdated = TRUE; + poDS->SetUpdated(); + } + else if (bUpdated && !bUpdatedIn) + { + bUpdated = FALSE; + } +} + +/************************************************************************/ +/* SyncToDisk() */ +/************************************************************************/ + +OGRErr OGRXLSXLayer::SyncToDisk() +{ + poDS->FlushCache(); + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature* OGRXLSXLayer::GetNextFeature() +{ + Init(); + OGRFeature* poFeature = OGRMemLayer::GetNextFeature(); + if (poFeature) + poFeature->SetFID(poFeature->GetFID() + 1 + bHasHeaderLine); + return poFeature; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature* OGRXLSXLayer::GetFeature( GIntBig nFeatureId ) +{ + Init(); + OGRFeature* poFeature = OGRMemLayer::GetFeature(nFeatureId - (1 + bHasHeaderLine)); + if (poFeature) + poFeature->SetFID(nFeatureId); + return poFeature; +} + +/************************************************************************/ +/* ISetFeature() */ +/************************************************************************/ + +OGRErr OGRXLSXLayer::ISetFeature( OGRFeature *poFeature ) +{ + Init(); + if (poFeature == NULL) + return OGRMemLayer::ISetFeature(poFeature); + + GIntBig nFID = poFeature->GetFID(); + if (nFID != OGRNullFID) + poFeature->SetFID(nFID - (1 + bHasHeaderLine)); + SetUpdated(); + OGRErr eErr = OGRMemLayer::ISetFeature(poFeature); + poFeature->SetFID(nFID); + return eErr; +} + +/************************************************************************/ +/* DeleteFeature() */ +/************************************************************************/ + +OGRErr OGRXLSXLayer::DeleteFeature( GIntBig nFID ) +{ + Init(); + SetUpdated(); + return OGRMemLayer::DeleteFeature(nFID - (1 + bHasHeaderLine)); +} + +/************************************************************************/ +/* OGRXLSXDataSource() */ +/************************************************************************/ + +OGRXLSXDataSource::OGRXLSXDataSource() + +{ + pszName = NULL; + bUpdatable = FALSE; + bUpdated = FALSE; + + nLayers = 0; + papoLayers = NULL; + + bFirstLineIsHeaders = FALSE; + + oParser = NULL; + bStopParsing = FALSE; + nWithoutEventCounter = 0; + nDataHandlerCounter = 0; + nStackDepth = 0; + nDepth = 0; + nCurLine = 0; + nCurCol = 0; + stateStack[0].eVal = STATE_DEFAULT; + stateStack[0].nBeginDepth = 0; + bInCellXFS = FALSE; + + poCurLayer = NULL; + + const char* pszXLSXFieldTypes = + CPLGetConfigOption("OGR_XLSX_FIELD_TYPES", ""); + bAutodetectTypes = !EQUAL(pszXLSXFieldTypes, "STRING"); +} + +/************************************************************************/ +/* ~OGRXLSXDataSource() */ +/************************************************************************/ + +OGRXLSXDataSource::~OGRXLSXDataSource() + +{ + FlushCache(); + + CPLFree( pszName ); + + for(int i=0;i= nLayers) + return NULL; + + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetLayerCount() */ +/************************************************************************/ + +int OGRXLSXDataSource::GetLayerCount() +{ + return nLayers; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRXLSXDataSource::Open( const char * pszFilename, + VSILFILE* fpWorkbook, + VSILFILE* fpSharedStrings, + VSILFILE* fpStyles, + int bUpdateIn ) + +{ + bUpdatable = bUpdateIn; + + pszName = CPLStrdup( pszFilename ); + + AnalyseWorkbook(fpWorkbook); + AnalyseSharedStrings(fpSharedStrings); + AnalyseStyles(fpStyles); + + /* Remove empty layers at the end, which tend to be there */ + while(nLayers > 1) + { + if (papoLayers[nLayers-1]->GetFeatureCount() == 0) + { + delete papoLayers[nLayers-1]; + nLayers --; + } + else + break; + } + + return TRUE; +} + +/************************************************************************/ +/* Create() */ +/************************************************************************/ + +int OGRXLSXDataSource::Create( const char * pszFilename, + CPL_UNUSED char **papszOptions ) +{ + bUpdated = TRUE; + bUpdatable = TRUE; + + pszName = CPLStrdup( pszFilename ); + + return TRUE; +} + +/************************************************************************/ +/* startElementCbk() */ +/************************************************************************/ + +static void XMLCALL startElementCbk(void *pUserData, const char *pszName, + const char **ppszAttr) +{ + ((OGRXLSXDataSource*)pUserData)->startElementCbk(pszName, ppszAttr); +} + +void OGRXLSXDataSource::startElementCbk(const char *pszName, + const char **ppszAttr) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + switch(stateStack[nStackDepth].eVal) + { + case STATE_DEFAULT: startElementDefault(pszName, ppszAttr); break; + case STATE_SHEETDATA: startElementTable(pszName, ppszAttr); break; + case STATE_ROW: startElementRow(pszName, ppszAttr); break; + case STATE_CELL: startElementCell(pszName, ppszAttr); break; + case STATE_TEXTV: break; + default: break; + } + nDepth++; +} + +/************************************************************************/ +/* endElementCbk() */ +/************************************************************************/ + +static void XMLCALL endElementCbk(void *pUserData, const char *pszName) +{ + ((OGRXLSXDataSource*)pUserData)->endElementCbk(pszName); +} + +void OGRXLSXDataSource::endElementCbk(const char *pszName) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + nDepth--; + switch(stateStack[nStackDepth].eVal) + { + case STATE_DEFAULT: break; + case STATE_SHEETDATA: endElementTable(pszName); break; + case STATE_ROW: endElementRow(pszName); break; + case STATE_CELL: endElementCell(pszName); break; + case STATE_TEXTV: break; + default: break; + } + + if (stateStack[nStackDepth].nBeginDepth == nDepth) + nStackDepth --; +} + +/************************************************************************/ +/* dataHandlerCbk() */ +/************************************************************************/ + +static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen) +{ + ((OGRXLSXDataSource*)pUserData)->dataHandlerCbk(data, nLen); +} + +void OGRXLSXDataSource::dataHandlerCbk(const char *data, int nLen) +{ + if (bStopParsing) return; + + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "File probably corrupted (million laugh pattern)"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + + nWithoutEventCounter = 0; + + switch(stateStack[nStackDepth].eVal) + { + case STATE_DEFAULT: break; + case STATE_SHEETDATA: break; + case STATE_ROW: break; + case STATE_CELL: break; + case STATE_TEXTV: dataHandlerTextV(data, nLen); + default: break; + } +} + +/************************************************************************/ +/* PushState() */ +/************************************************************************/ + +void OGRXLSXDataSource::PushState(HandlerStateEnum eVal) +{ + if (nStackDepth + 1 == STACK_SIZE) + { + bStopParsing = TRUE; + return; + } + nStackDepth ++; + stateStack[nStackDepth].eVal = eVal; + stateStack[nStackDepth].nBeginDepth = nDepth; +} + +/************************************************************************/ +/* GetAttributeValue() */ +/************************************************************************/ + +static const char* GetAttributeValue(const char **ppszAttr, + const char* pszKey, + const char* pszDefaultVal) +{ + while(*ppszAttr) + { + if (strcmp(ppszAttr[0], pszKey) == 0) + return ppszAttr[1]; + ppszAttr += 2; + } + return pszDefaultVal; +} + +/************************************************************************/ +/* GetOGRFieldType() */ +/************************************************************************/ + +OGRFieldType OGRXLSXDataSource::GetOGRFieldType(const char* pszValue, + const char* pszValueType) +{ + if (!bAutodetectTypes || pszValueType == NULL) + return OFTString; + else if (strcmp(pszValueType, "string") == 0) + return OFTString; + else if (strcmp(pszValueType, "float") == 0) + { + CPLValueType eValueType = CPLGetValueType(pszValue); + if (eValueType == CPL_VALUE_STRING) + return OFTString; + else if (eValueType == CPL_VALUE_INTEGER) + { + GIntBig nVal = CPLAtoGIntBig(pszValue); + if( (GIntBig)(int)nVal != nVal ) + return OFTInteger64; + else + return OFTInteger; + } + else + return OFTReal; + } + else if (strcmp(pszValueType, "datetime") == 0 || + strcmp(pszValueType, "datetime_ms") == 0) + { + return OFTDateTime; + } + else if (strcmp(pszValueType, "date") == 0) + { + return OFTDate; + } + else if (strcmp(pszValueType, "time") == 0) + { + return OFTTime; + } + else + return OFTString; +} + +/************************************************************************/ +/* SetField() */ +/************************************************************************/ + +static void SetField(OGRFeature* poFeature, + int i, + const char* pszValue, + const char* pszCellType) +{ + if (pszValue[0] == '\0') + return; + + OGRFieldType eType = poFeature->GetFieldDefnRef(i)->GetType(); + + if (strcmp(pszCellType, "time") == 0 || + strcmp(pszCellType, "date") == 0 || + strcmp(pszCellType, "datetime") == 0 || + strcmp(pszCellType, "datetime_ms") == 0) + { + struct tm sTm; + double dfNumberOfDaysSince1900 = CPLAtof(pszValue); +#define NUMBER_OF_DAYS_BETWEEN_1900_AND_1970 25569 +#define NUMBER_OF_SECONDS_PER_DAY 86400 + GIntBig nUnixTime = (GIntBig)((dfNumberOfDaysSince1900 - + NUMBER_OF_DAYS_BETWEEN_1900_AND_1970 )* + NUMBER_OF_SECONDS_PER_DAY); + CPLUnixTimeToYMDHMS(nUnixTime, &sTm); + + if (eType == OFTTime || eType == OFTDate || eType == OFTDateTime) + { + double fFracSec = fmod(fmod(dfNumberOfDaysSince1900,1) * 3600 * 24, 1); + poFeature->SetField(i, sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday, + sTm.tm_hour, sTm.tm_min, sTm.tm_sec + fFracSec, 0 ); + } + else if (strcmp(pszCellType, "time") == 0) + { + poFeature->SetField(i, CPLSPrintf("%02d:%02d:%02d", + sTm.tm_hour, sTm.tm_min, sTm.tm_sec)); + } + else if (strcmp(pszCellType, "date") == 0) + { + poFeature->SetField(i, CPLSPrintf("%04d/%02d/%02d", + sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday)); + } + else /* if (strcmp(pszCellType, "datetime") == 0) */ + { + double fFracSec = fmod(fmod(dfNumberOfDaysSince1900,1) * 3600 * 24, 1); + poFeature->SetField(i, + sTm.tm_year + 1900, sTm.tm_mon + 1, sTm.tm_mday, + sTm.tm_hour, sTm.tm_min, sTm.tm_sec + fFracSec, 0); + } + } + else + poFeature->SetField(i, pszValue); +} + +/************************************************************************/ +/* DetectHeaderLine() */ +/************************************************************************/ + +void OGRXLSXDataSource::DetectHeaderLine() + +{ + int bHeaderLineCandidate = TRUE; + size_t i; + for(i = 0; i < apoFirstLineTypes.size(); i++) + { + if (apoFirstLineTypes[i] != "string") + { + /* If the values in the first line are not text, then it is */ + /* not a header line */ + bHeaderLineCandidate = FALSE; + break; + } + } + + size_t nCountTextOnCurLine = 0; + size_t nCountNonEmptyOnCurLine = 0; + for(i = 0; bHeaderLineCandidate && i < apoCurLineTypes.size(); i++) + { + if (apoCurLineTypes[i] == "string") + { + /* If there are only text values on the second line, then we cannot */ + /* know if it is a header line or just a regular line */ + nCountTextOnCurLine ++; + } + else if (apoCurLineTypes[i] != "") + { + nCountNonEmptyOnCurLine ++; + } + } + + const char* pszXLSXHeaders = CPLGetConfigOption("OGR_XLSX_HEADERS", ""); + bFirstLineIsHeaders = FALSE; + if (EQUAL(pszXLSXHeaders, "FORCE")) + bFirstLineIsHeaders = TRUE; + else if (EQUAL(pszXLSXHeaders, "DISABLE")) + bFirstLineIsHeaders = FALSE; + else if (bHeaderLineCandidate && + apoFirstLineTypes.size() != 0 && + apoFirstLineTypes.size() == apoCurLineTypes.size() && + nCountTextOnCurLine != apoFirstLineTypes.size() && + nCountNonEmptyOnCurLine != 0) + { + bFirstLineIsHeaders = TRUE; + } + CPLDebug("XLSX", "%s %s", + poCurLayer->GetName(), + bFirstLineIsHeaders ? "has header line" : "has no header line"); +} + +/************************************************************************/ +/* startElementDefault() */ +/************************************************************************/ + +void OGRXLSXDataSource::startElementDefault(const char *pszName, + CPL_UNUSED const char **ppszAttr) +{ + if (strcmp(pszName, "sheetData") == 0) + { + apoFirstLineValues.resize(0); + apoFirstLineTypes.resize(0); + nCurLine = 0; + PushState(STATE_SHEETDATA); + } +} + +/************************************************************************/ +/* startElementTable() */ +/************************************************************************/ + +void OGRXLSXDataSource::startElementTable(const char *pszName, + const char **ppszAttr) +{ + if (strcmp(pszName, "row") == 0) + { + PushState(STATE_ROW); + + int nNewCurLine = atoi( + GetAttributeValue(ppszAttr, "r", "0")) - 1; + for(;nCurLineCreateField(&oFieldDefn); + } + + OGRFeature* poFeature = new OGRFeature(poCurLayer->GetLayerDefn()); + for(i = 0; i < apoFirstLineValues.size(); i++) + { + SetField(poFeature, i, apoFirstLineValues[i].c_str(), + apoFirstLineTypes[i].c_str()); + } + poCurLayer->CreateFeature(poFeature); + delete poFeature; + } + + if (poCurLayer) + { + ((OGRMemLayer*)poCurLayer)->SetUpdatable(bUpdatable); + ((OGRMemLayer*)poCurLayer)->SetAdvertizeUTF8(TRUE); + ((OGRXLSXLayer*)poCurLayer)->SetUpdated(FALSE); + } + + poCurLayer = NULL; + } +} + +/************************************************************************/ +/* startElementRow() */ +/************************************************************************/ + +void OGRXLSXDataSource::startElementRow(const char *pszName, + const char **ppszAttr) +{ + if (strcmp(pszName, "c") == 0) + { + PushState(STATE_CELL); + + const char* pszR = GetAttributeValue(ppszAttr, "r", NULL); + if (pszR) + { + /* Convert col number from base 26 */ + int nNewCurCol = (pszR[0] - 'A'); + int i = 1; + while(pszR[i] >= 'A' && pszR[i] <= 'Z') + { + nNewCurCol = nNewCurCol * 26 + (pszR[i] - 'A'); + i ++; + } + for(;nCurCol= 0 && nS < (int)apoStyles.size()) + { + XLSXFieldTypeExtended eType = apoStyles[nS]; + if (eType.eType == OFTDateTime) + { + if( eType.bHasMS ) + osValueType = "datetime_ms"; + else + osValueType = "datetime"; + } + else if (eType.eType == OFTDate) + osValueType = "date"; + else if (eType.eType == OFTTime) + osValueType = "time"; + } + else if (nS != -1) + CPLDebug("XLSX", "Cannot find style %d", nS); + + const char* pszT = GetAttributeValue(ppszAttr, "t", ""); + if ( EQUAL(pszT,"s")) + osValueType = "stringLookup"; + else if( EQUAL(pszT,"inlineStr") ) + osValueType = "string"; + + osValue = ""; + } +} + +/************************************************************************/ +/* endElementRow() */ +/************************************************************************/ + +void OGRXLSXDataSource::endElementRow(CPL_UNUSED const char *pszName) +{ + if (stateStack[nStackDepth].nBeginDepth == nDepth) + { + CPLAssert(strcmp(pszName, "row") == 0); + + OGRFeature* poFeature; + size_t i; + + /* Backup first line values and types in special arrays */ + if (nCurLine == 0) + { + apoFirstLineTypes = apoCurLineTypes; + apoFirstLineValues = apoCurLineValues; + + #if skip_leading_empty_rows + if (apoFirstLineTypes.size() == 0) + { + /* Skip leading empty rows */ + apoFirstLineTypes.resize(0); + apoFirstLineValues.resize(0); + return; + } + #endif + } + + if (nCurLine == 1) + { + DetectHeaderLine(); + + poCurLayer->SetHasHeaderLine(bFirstLineIsHeaders); + + if (bFirstLineIsHeaders) + { + for(i = 0; i < apoFirstLineValues.size(); i++) + { + const char* pszFieldName = apoFirstLineValues[i].c_str(); + if (pszFieldName[0] == '\0') + pszFieldName = CPLSPrintf("Field%d", (int)i + 1); + OGRFieldType eType = OFTString; + if (i < apoCurLineValues.size()) + { + eType = GetOGRFieldType(apoCurLineValues[i].c_str(), + apoCurLineTypes[i].c_str()); + } + OGRFieldDefn oFieldDefn(pszFieldName, eType); + poCurLayer->CreateField(&oFieldDefn); + } + } + else + { + for(i = 0; i < apoFirstLineValues.size(); i++) + { + const char* pszFieldName = + CPLSPrintf("Field%d", (int)i + 1); + OGRFieldType eType = GetOGRFieldType( + apoFirstLineValues[i].c_str(), + apoFirstLineTypes[i].c_str()); + OGRFieldDefn oFieldDefn(pszFieldName, eType); + poCurLayer->CreateField(&oFieldDefn); + } + + poFeature = new OGRFeature(poCurLayer->GetLayerDefn()); + for(i = 0; i < apoFirstLineValues.size(); i++) + { + SetField(poFeature, i, apoFirstLineValues[i].c_str(), + apoFirstLineTypes[i].c_str()); + } + poCurLayer->CreateFeature(poFeature); + delete poFeature; + } + } + + if (nCurLine >= 1) + { + /* Add new fields found on following lines. */ + if (apoCurLineValues.size() > + (size_t)poCurLayer->GetLayerDefn()->GetFieldCount()) + { + for(i = (size_t)poCurLayer->GetLayerDefn()->GetFieldCount(); + i < apoCurLineValues.size(); + i++) + { + const char* pszFieldName = + CPLSPrintf("Field%d", (int)i + 1); + OGRFieldType eType = GetOGRFieldType( + apoCurLineValues[i].c_str(), + apoCurLineTypes[i].c_str()); + OGRFieldDefn oFieldDefn(pszFieldName, eType); + poCurLayer->CreateField(&oFieldDefn); + } + } + + /* Update field type if necessary */ + if (bAutodetectTypes) + { + for(i = 0; i < apoCurLineValues.size(); i++) + { + if (apoCurLineValues[i].size()) + { + OGRFieldType eValType = GetOGRFieldType( + apoCurLineValues[i].c_str(), + apoCurLineTypes[i].c_str()); + OGRFieldType eFieldType = + poCurLayer->GetLayerDefn()->GetFieldDefn(i)->GetType(); + if (eFieldType == OFTDateTime && + (eValType == OFTDate || eValType == OFTTime) ) + { + /* ok */ + } + else if (eFieldType == OFTReal && (eValType == OFTInteger || eValType == OFTInteger64)) + { + /* ok */; + } + else if (eFieldType == OFTInteger64 && eValType == OFTInteger ) + { + /* ok */; + } + else if (eFieldType != OFTString && eValType != eFieldType) + { + OGRFieldDefn oNewFieldDefn( + poCurLayer->GetLayerDefn()->GetFieldDefn(i)); + if ((eFieldType == OFTDate || eFieldType == OFTTime) && + eValType == OFTDateTime) + oNewFieldDefn.SetType(OFTDateTime); + else if ((eFieldType == OFTInteger || eFieldType == OFTInteger64) && + eValType == OFTReal) + oNewFieldDefn.SetType(OFTReal); + else if( eFieldType == OFTInteger && eValType == OFTInteger64 ) + oNewFieldDefn.SetType(OFTInteger64); + else + oNewFieldDefn.SetType(OFTString); + poCurLayer->AlterFieldDefn(i, &oNewFieldDefn, + ALTER_TYPE_FLAG); + } + } + } + } + + /* Add feature for current line */ + poFeature = new OGRFeature(poCurLayer->GetLayerDefn()); + for(i = 0; i < apoCurLineValues.size(); i++) + { + SetField(poFeature, i, apoCurLineValues[i].c_str(), + apoCurLineTypes[i].c_str()); + } + poCurLayer->CreateFeature(poFeature); + delete poFeature; + } + + nCurLine++; + } +} + +/************************************************************************/ +/* startElementCell() */ +/************************************************************************/ + +void OGRXLSXDataSource::startElementCell(const char *pszName, + CPL_UNUSED const char **ppszAttr) +{ + if (osValue.size() == 0 && strcmp(pszName, "v") == 0) + { + PushState(STATE_TEXTV); + } + else if (osValue.size() == 0 && strcmp(pszName, "t") == 0) + { + PushState(STATE_TEXTV); + } +} + +/************************************************************************/ +/* endElementCell() */ +/************************************************************************/ + +void OGRXLSXDataSource::endElementCell(CPL_UNUSED const char *pszName) +{ + if (stateStack[nStackDepth].nBeginDepth == nDepth) + { + CPLAssert(strcmp(pszName, "c") == 0); + + if (osValueType == "stringLookup") + { + int nIndex = atoi(osValue); + if (nIndex >= 0 && nIndex < (int)(apoSharedStrings.size())) + osValue = apoSharedStrings[nIndex]; + else + CPLDebug("XLSX", "Cannot find string %d", nIndex); + osValueType = "string"; + } + + apoCurLineValues.push_back(osValue); + apoCurLineTypes.push_back(osValueType); + + nCurCol += 1; + } +} + +/************************************************************************/ +/* dataHandlerTextV() */ +/************************************************************************/ + +void OGRXLSXDataSource::dataHandlerTextV(const char *data, int nLen) +{ + osValue.append(data, nLen); +} + +/************************************************************************/ +/* BuildLayer() */ +/************************************************************************/ + +void OGRXLSXDataSource::BuildLayer(OGRXLSXLayer* poLayer, int nSheetId) +{ + poCurLayer = poLayer; + + CPLString osSheetFilename( + CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml", pszName, nSheetId)); + const char* pszSheetFilename = osSheetFilename.c_str(); + VSILFILE* fp = VSIFOpenL(pszSheetFilename, "rb"); + if (fp == NULL) + return; + + int bUpdatedBackup = bUpdated; + + oParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk); + XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk); + XML_SetUserData(oParser, this); + + VSIFSeekL( fp, 0, SEEK_SET ); + + bStopParsing = FALSE; + nWithoutEventCounter = 0; + nDataHandlerCounter = 0; + nStackDepth = 0; + nDepth = 0; + stateStack[0].eVal = STATE_DEFAULT; + stateStack[0].nBeginDepth = 0; + + char aBuf[BUFSIZ]; + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = + (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fp ); + nDone = VSIFEofL(fp); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of %s file failed : %s at line %d, column %d", + pszSheetFilename, + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + bStopParsing = TRUE; + } + nWithoutEventCounter ++; + } while (!nDone && !bStopParsing && nWithoutEventCounter < 10); + + XML_ParserFree(oParser); + oParser = NULL; + + if (nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + VSIFCloseL(fp); + + bUpdated = bUpdatedBackup; +} + +/************************************************************************/ +/* startElementSSCbk() */ +/************************************************************************/ + +static void XMLCALL startElementSSCbk(void *pUserData, const char *pszName, + const char **ppszAttr) +{ + ((OGRXLSXDataSource*)pUserData)->startElementSSCbk(pszName, ppszAttr); +} + +void OGRXLSXDataSource::startElementSSCbk(const char *pszName, + CPL_UNUSED const char **ppszAttr) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + switch(stateStack[nStackDepth].eVal) + { + case STATE_DEFAULT: + { + if (strcmp(pszName,"t") == 0) + { + PushState(STATE_T); + osCurrentString = ""; + } + break; + } + default: + break; + } + nDepth++; +} + +/************************************************************************/ +/* endElementSSCbk() */ +/************************************************************************/ + +static void XMLCALL endElementSSCbk(void *pUserData, const char *pszName) +{ + ((OGRXLSXDataSource*)pUserData)->endElementSSCbk(pszName); +} + +void OGRXLSXDataSource::endElementSSCbk(CPL_UNUSED const char *pszName) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + + nDepth--; + switch(stateStack[nStackDepth].eVal) + { + case STATE_DEFAULT: break; + case STATE_T: + { + if (stateStack[nStackDepth].nBeginDepth == nDepth) + { + apoSharedStrings.push_back(osCurrentString); + } + break; + } + default: break; + } + + if (stateStack[nStackDepth].nBeginDepth == nDepth) + nStackDepth --; +} + +/************************************************************************/ +/* dataHandlerSSCbk() */ +/************************************************************************/ + +static void XMLCALL dataHandlerSSCbk(void *pUserData, const char *data, int nLen) +{ + ((OGRXLSXDataSource*)pUserData)->dataHandlerSSCbk(data, nLen); +} + +void OGRXLSXDataSource::dataHandlerSSCbk(const char *data, int nLen) +{ + if (bStopParsing) return; + + nDataHandlerCounter ++; + if (nDataHandlerCounter >= BUFSIZ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "File probably corrupted (million laugh pattern)"); + XML_StopParser(oParser, XML_FALSE); + bStopParsing = TRUE; + return; + } + + nWithoutEventCounter = 0; + + switch(stateStack[nStackDepth].eVal) + { + case STATE_DEFAULT: break; + case STATE_T: osCurrentString.append(data, nLen); break; + default: break; + } +} + +/************************************************************************/ +/* AnalyseSharedStrings() */ +/************************************************************************/ + +void OGRXLSXDataSource::AnalyseSharedStrings(VSILFILE* fpSharedStrings) +{ + if (fpSharedStrings == NULL) + return; + + oParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oParser, ::startElementSSCbk, ::endElementSSCbk); + XML_SetCharacterDataHandler(oParser, ::dataHandlerSSCbk); + XML_SetUserData(oParser, this); + + VSIFSeekL( fpSharedStrings, 0, SEEK_SET ); + + bStopParsing = FALSE; + nWithoutEventCounter = 0; + nDataHandlerCounter = 0; + nStackDepth = 0; + nDepth = 0; + stateStack[0].eVal = STATE_DEFAULT; + stateStack[0].nBeginDepth = 0; + + char aBuf[BUFSIZ]; + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = + (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpSharedStrings ); + nDone = VSIFEofL(fpSharedStrings); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of %s file failed : %s at line %d, column %d", + "sharedStrings.xml", + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + bStopParsing = TRUE; + } + nWithoutEventCounter ++; + } while (!nDone && !bStopParsing && nWithoutEventCounter < 10); + + XML_ParserFree(oParser); + oParser = NULL; + + if (nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + VSIFCloseL(fpSharedStrings); + fpSharedStrings = NULL; +} + + +/************************************************************************/ +/* startElementWBCbk() */ +/************************************************************************/ + +static void XMLCALL startElementWBCbk(void *pUserData, const char *pszName, + const char **ppszAttr) +{ + ((OGRXLSXDataSource*)pUserData)->startElementWBCbk(pszName, ppszAttr); +} + +void OGRXLSXDataSource::startElementWBCbk(const char *pszName, + const char **ppszAttr) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + if (strcmp(pszName,"sheet") == 0) + { + const char* pszSheetName = GetAttributeValue(ppszAttr, "name", NULL); + /*const char* pszSheetId = GetAttributeValue(ppszAttr, "sheetId", NULL);*/ + if (pszSheetName /*&& pszSheetId*/) + { + /*int nSheetId = atoi(pszSheetId);*/ + int nSheetId = nLayers + 1; + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers++] = new OGRXLSXLayer(this, nSheetId, pszSheetName); + } + } +} + +/************************************************************************/ +/* AnalyseWorkbook() */ +/************************************************************************/ + +void OGRXLSXDataSource::AnalyseWorkbook(VSILFILE* fpWorkbook) +{ + oParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oParser, ::startElementWBCbk, NULL); + XML_SetUserData(oParser, this); + + VSIFSeekL( fpWorkbook, 0, SEEK_SET ); + + bStopParsing = FALSE; + nWithoutEventCounter = 0; + nDataHandlerCounter = 0; + + char aBuf[BUFSIZ]; + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = + (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpWorkbook ); + nDone = VSIFEofL(fpWorkbook); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of %s file failed : %s at line %d, column %d", + "workbook.xml", + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + bStopParsing = TRUE; + } + nWithoutEventCounter ++; + } while (!nDone && !bStopParsing && nWithoutEventCounter < 10); + + XML_ParserFree(oParser); + oParser = NULL; + + if (nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + VSIFCloseL(fpWorkbook); +} + + +/************************************************************************/ +/* startElementStylesCbk() */ +/************************************************************************/ + +static void XMLCALL startElementStylesCbk(void *pUserData, const char *pszName, + const char **ppszAttr) +{ + ((OGRXLSXDataSource*)pUserData)->startElementStylesCbk(pszName, ppszAttr); +} + +void OGRXLSXDataSource::startElementStylesCbk(const char *pszName, + const char **ppszAttr) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + if (strcmp(pszName,"numFmt") == 0) + { + const char* pszFormatCode = GetAttributeValue(ppszAttr, "formatCode", NULL); + const char* pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1"); + int nNumFmtId = atoi(pszNumFmtId); + if (pszFormatCode && nNumFmtId >= 164) + { + int bHasDate = strstr(pszFormatCode, "DD") != NULL || + strstr(pszFormatCode, "YY") != NULL; + int bHasTime = strstr(pszFormatCode, "HH") != NULL; + if (bHasDate && bHasTime) + apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTDateTime, + strstr(pszFormatCode, "SS.000") != NULL ); + else if (bHasDate) + apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTDate); + else if (bHasTime) + apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTTime); + else + apoMapStyleFormats[nNumFmtId] = XLSXFieldTypeExtended(OFTReal); + } + } + else if (strcmp(pszName,"cellXfs") == 0) + { + bInCellXFS = TRUE; + } + else if (bInCellXFS && strcmp(pszName,"xf") == 0) + { + const char* pszNumFmtId = GetAttributeValue(ppszAttr, "numFmtId", "-1"); + int nNumFmtId = atoi(pszNumFmtId); + XLSXFieldTypeExtended eType(OFTReal); + if (nNumFmtId >= 0) + { + if (nNumFmtId < 164) + { + // From http://social.msdn.microsoft.com/Forums/en-US/oxmlsdk/thread/e27aaf16-b900-4654-8210-83c5774a179c/ + if (nNumFmtId >= 14 && nNumFmtId <= 17) + eType = XLSXFieldTypeExtended(OFTDate); + else if (nNumFmtId >= 18 && nNumFmtId <= 21) + eType = XLSXFieldTypeExtended(OFTTime); + else if (nNumFmtId == 22) + eType = XLSXFieldTypeExtended(OFTDateTime); + } + else + { + std::map::iterator oIter = apoMapStyleFormats.find(nNumFmtId); + if (oIter != apoMapStyleFormats.end()) + eType = oIter->second; + else + CPLDebug("XLSX", "Cannot find entry in with numFmtId=%d", nNumFmtId); + } + } + //printf("style[%d] = %d\n", apoStyles.size(), eType); + + apoStyles.push_back(eType); + } +} + +/************************************************************************/ +/* endElementStylesCbk() */ +/************************************************************************/ + +static void XMLCALL endElementStylesCbk(void *pUserData, const char *pszName) +{ + ((OGRXLSXDataSource*)pUserData)->endElementStylesCbk(pszName); +} + +void OGRXLSXDataSource::endElementStylesCbk(const char *pszName) +{ + if (bStopParsing) return; + + nWithoutEventCounter = 0; + if (strcmp(pszName,"cellXfs") == 0) + { + bInCellXFS = FALSE; + } +} + +/************************************************************************/ +/* AnalyseStyles() */ +/************************************************************************/ + +void OGRXLSXDataSource::AnalyseStyles(VSILFILE* fpStyles) +{ + if (fpStyles == NULL) + return; + + oParser = OGRCreateExpatXMLParser(); + XML_SetElementHandler(oParser, ::startElementStylesCbk, ::endElementStylesCbk); + XML_SetUserData(oParser, this); + + VSIFSeekL( fpStyles, 0, SEEK_SET ); + + bStopParsing = FALSE; + nWithoutEventCounter = 0; + nDataHandlerCounter = 0; + bInCellXFS = FALSE; + + char aBuf[BUFSIZ]; + int nDone; + do + { + nDataHandlerCounter = 0; + unsigned int nLen = + (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpStyles ); + nDone = VSIFEofL(fpStyles); + if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR) + { + CPLError(CE_Failure, CPLE_AppDefined, + "XML parsing of %s file failed : %s at line %d, column %d", + "styles.xml", + XML_ErrorString(XML_GetErrorCode(oParser)), + (int)XML_GetCurrentLineNumber(oParser), + (int)XML_GetCurrentColumnNumber(oParser)); + bStopParsing = TRUE; + } + nWithoutEventCounter ++; + } while (!nDone && !bStopParsing && nWithoutEventCounter < 10); + + XML_ParserFree(oParser); + oParser = NULL; + + if (nWithoutEventCounter == 10) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Too much data inside one element. File probably corrupted"); + bStopParsing = TRUE; + } + + VSIFCloseL(fpStyles); +} + +/************************************************************************/ +/* ICreateLayer() */ +/************************************************************************/ + +OGRLayer * +OGRXLSXDataSource::ICreateLayer( const char * pszLayerName, + CPL_UNUSED OGRSpatialReference *poSRS, + CPL_UNUSED OGRwkbGeometryType eType, + char ** papszOptions ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( !bUpdatable ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened read-only.\n" + "New layer %s cannot be created.\n", + pszName, pszLayerName ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do we already have this layer? If so, should we blow it */ +/* away? */ +/* -------------------------------------------------------------------- */ + int iLayer; + + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) ) + { + if( CSLFetchNameValue( papszOptions, "OVERWRITE" ) != NULL + && !EQUAL(CSLFetchNameValue(papszOptions,"OVERWRITE"),"NO") ) + { + DeleteLayer( pszLayerName ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %s already exists, CreateLayer failed.\n" + "Use the layer creation option OVERWRITE=YES to " + "replace it.", + pszLayerName ); + return NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create the layer object. */ +/* -------------------------------------------------------------------- */ + OGRLayer* poLayer = new OGRXLSXLayer(this, nLayers + 1, pszLayerName, TRUE); + + papoLayers = (OGRLayer**)CPLRealloc(papoLayers, (nLayers + 1) * sizeof(OGRLayer*)); + papoLayers[nLayers] = poLayer; + nLayers ++; + + bUpdated = TRUE; + + return poLayer; +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +void OGRXLSXDataSource::DeleteLayer( const char *pszLayerName ) + +{ + int iLayer; + +/* -------------------------------------------------------------------- */ +/* Verify we are in update mode. */ +/* -------------------------------------------------------------------- */ + if( !bUpdatable ) + { + CPLError( CE_Failure, CPLE_NoWriteAccess, + "Data source %s opened read-only.\n" + "Layer %s cannot be deleted.\n", + pszName, pszLayerName ); + + return; + } + +/* -------------------------------------------------------------------- */ +/* Try to find layer. */ +/* -------------------------------------------------------------------- */ + for( iLayer = 0; iLayer < nLayers; iLayer++ ) + { + if( EQUAL(pszLayerName,papoLayers[iLayer]->GetName()) ) + break; + } + + if( iLayer == nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to delete layer '%s', but this layer is not known to OGR.", + pszLayerName ); + return; + } + + DeleteLayer(iLayer); +} + +/************************************************************************/ +/* DeleteLayer() */ +/************************************************************************/ + +OGRErr OGRXLSXDataSource::DeleteLayer(int iLayer) +{ + if( iLayer < 0 || iLayer >= nLayers ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Layer %d not in legal range of 0 to %d.", + iLayer, nLayers-1 ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Blow away our OGR structures related to the layer. This is */ +/* pretty dangerous if anything has a reference to this layer! */ +/* -------------------------------------------------------------------- */ + + delete papoLayers[iLayer]; + memmove( papoLayers + iLayer, papoLayers + iLayer + 1, + sizeof(void *) * (nLayers - iLayer - 1) ); + nLayers--; + + bUpdated = TRUE; + + return OGRERR_NONE; +} + +/************************************************************************/ +/* WriteOverride() */ +/************************************************************************/ + +static void WriteOverride(VSILFILE* fp, const char* pszPartName, const char* pszContentType) +{ + VSIFPrintfL(fp, "\n", + pszPartName, pszContentType); +} + +#define XML_HEADER "\n" +#define MAIN_NS "xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"" +#define SCHEMA_OD "http://schemas.openxmlformats.org/officeDocument/2006" +#define SCHEMA_OD_RS "http://schemas.openxmlformats.org/officeDocument/2006/relationships" +#define SCHEMA_PACKAGE "http://schemas.openxmlformats.org/package/2006" +#define SCHEMA_PACKAGE_RS "http://schemas.openxmlformats.org/package/2006/relationships" + +/************************************************************************/ +/* WriteContentTypes() */ +/************************************************************************/ + +static void WriteContentTypes(const char* pszName, int nLayers) +{ + VSILFILE* fp; + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/[Content_Types].xml", pszName), "wb"); + VSIFPrintfL(fp, XML_HEADER); + VSIFPrintfL(fp, "\n", SCHEMA_PACKAGE); + WriteOverride(fp, "/_rels/.rels", "application/vnd.openxmlformats-package.relationships+xml"); + WriteOverride(fp, "/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml"); + WriteOverride(fp, "/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml"); + WriteOverride(fp, "/xl/_rels/workbook.xml.rels", "application/vnd.openxmlformats-package.relationships+xml"); + for(int i=0;i\n"); + VSIFCloseL(fp); +} + +/************************************************************************/ +/* WriteApp() */ +/************************************************************************/ + +static void WriteApp(const char* pszName) +{ + VSILFILE* fp; + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/docProps/app.xml", pszName), "wb"); + VSIFPrintfL(fp, XML_HEADER); + VSIFPrintfL(fp, "\n", SCHEMA_OD, SCHEMA_OD); + VSIFPrintfL(fp, "0\n"); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); +} + +/************************************************************************/ +/* WriteCore() */ +/************************************************************************/ + +static void WriteCore(const char* pszName) +{ + VSILFILE* fp; + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/docProps/core.xml", pszName), "wb"); + VSIFPrintfL(fp, XML_HEADER); + VSIFPrintfL(fp, "\n", SCHEMA_PACKAGE); + VSIFPrintfL(fp, "0\n"); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); +} + +/************************************************************************/ +/* WriteWorkbook() */ +/************************************************************************/ + +static void WriteWorkbook(const char* pszName, OGRDataSource* poDS) +{ + VSILFILE* fp; + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/workbook.xml", pszName), "wb"); + VSIFPrintfL(fp, XML_HEADER); + VSIFPrintfL(fp, "\n", MAIN_NS, SCHEMA_OD_RS); + VSIFPrintfL(fp, "\n"); + /* + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + */ + VSIFPrintfL(fp, "\n"); + for(int i=0;iGetLayerCount();i++) + { + OGRXLSXLayer* poLayer = (OGRXLSXLayer*) poDS->GetLayer(i); + const char* pszLayerName = poLayer->GetName(); + char* pszXML = OGRGetXML_UTF8_EscapedString(pszLayerName); + VSIFPrintfL(fp, "\n", pszXML, i+1, i+2); + CPLFree(pszXML); + } + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); +} + +/************************************************************************/ +/* BuildColString() */ +/************************************************************************/ + +static void BuildColString(char szCol[5], int nCol) +{ + /* + A Z AA AZ BA BZ ZA ZZ AAA ZZZ AAAA + 0 25 26 51 52 77 676 701 702 18277 18278 + */ + int k = 0; + szCol[k++] = (nCol % 26) + 'A'; + while(nCol >= 26) + { + nCol /= 26; + nCol --; /* We wouldn't need that if this was a proper base 26 numeration scheme ! */ + szCol[k++] = (nCol % 26) + 'A'; + } + szCol[k] = 0; + for(int l=0;l& oStringMap, + std::vector& oStringList) +{ + VSILFILE* fp; + int j; + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/worksheets/sheet%d.xml", pszName, iLayer + 1), "wb"); + VSIFPrintfL(fp, XML_HEADER); + VSIFPrintfL(fp, "\n", MAIN_NS, SCHEMA_OD_RS); + /* + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n", + (i == 0) ? "true" : "false"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n");*/ + + poLayer->ResetReading(); + + OGRFeature* poFeature = poLayer->GetNextFeature(); + + OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn(); + int bHasHeaders = FALSE; + int iRow = 1; + + VSIFPrintfL(fp, "\n"); + for(j=0;jGetFieldCount();j++) + { + int nWidth = 15; + if (poFDefn->GetFieldDefn(j)->GetType() == OFTDateTime) + nWidth = 29; + VSIFPrintfL(fp, "
    \n", + j+1, 1024, nWidth); + + if (strcmp(poFDefn->GetFieldDefn(j)->GetNameRef(), + CPLSPrintf("Field%d", j+1)) != 0) + bHasHeaders = TRUE; + } + VSIFPrintfL(fp, "\n"); + + VSIFPrintfL(fp, "\n"); + + if (bHasHeaders && poFeature != NULL) + { + VSIFPrintfL(fp, "\n", iRow); + for(j=0;jGetFieldCount();j++) + { + const char* pszVal = poFDefn->GetFieldDefn(j)->GetNameRef(); + std::map::iterator oIter = oStringMap.find(pszVal); + int nStringIndex; + if (oIter != oStringMap.end()) + nStringIndex = oIter->second; + else + { + nStringIndex = (int)oStringList.size(); + oStringMap[pszVal] = nStringIndex; + oStringList.push_back(pszVal); + } + + char szCol[5]; + BuildColString(szCol, j); + + VSIFPrintfL(fp, "\n", szCol, iRow); + VSIFPrintfL(fp, "%d\n", nStringIndex); + VSIFPrintfL(fp, "\n"); + } + VSIFPrintfL(fp, "\n"); + + iRow ++; + } + + while(poFeature != NULL) + { + VSIFPrintfL(fp, "\n", iRow); + for(j=0;jGetFieldCount();j++) + { + if (poFeature->IsFieldSet(j)) + { + char szCol[5]; + BuildColString(szCol, j); + + OGRFieldType eType = poFDefn->GetFieldDefn(j)->GetType(); + + if (eType == OFTReal) + { + VSIFPrintfL(fp, "\n", szCol, iRow); + VSIFPrintfL(fp, "%.16f\n", poFeature->GetFieldAsDouble(j)); + VSIFPrintfL(fp, "\n"); + } + else if (eType == OFTInteger) + { + VSIFPrintfL(fp, "\n", szCol, iRow); + VSIFPrintfL(fp, "%d\n", poFeature->GetFieldAsInteger(j)); + VSIFPrintfL(fp, "\n"); + } + else if (eType == OFTInteger64) + { + VSIFPrintfL(fp, "\n", szCol, iRow); + VSIFPrintfL(fp, "" CPL_FRMT_GIB "\n", poFeature->GetFieldAsInteger64(j)); + VSIFPrintfL(fp, "\n"); + } + else if (eType == OFTDate || eType == OFTDateTime || eType == OFTTime) + { + int nYear, nMonth, nDay, nHour, nMinute, nTZFlag; + float fSecond; + poFeature->GetFieldAsDateTime(j, &nYear, &nMonth, &nDay, + &nHour, &nMinute, &fSecond, &nTZFlag ); + struct tm brokendowntime; + memset(&brokendowntime, 0, sizeof(brokendowntime)); + brokendowntime.tm_year = (eType == OFTTime) ? 70 : nYear - 1900; + brokendowntime.tm_mon = (eType == OFTTime) ? 0 : nMonth - 1; + brokendowntime.tm_mday = (eType == OFTTime) ? 1 : nDay; + brokendowntime.tm_hour = nHour; + brokendowntime.tm_min = nMinute; + brokendowntime.tm_sec = (int)fSecond; + GIntBig nUnixTime = CPLYMDHMSToUnixTime(&brokendowntime); + double dfNumberOfDaysSince1900 = (1.0 * nUnixTime / NUMBER_OF_SECONDS_PER_DAY); + dfNumberOfDaysSince1900 += fmod(fSecond,1) / NUMBER_OF_SECONDS_PER_DAY; + int s = (eType == OFTDate) ? 1 : (eType == OFTDateTime) ? 2 : 3; + if( eType == OFTDateTime && OGR_GET_MS(fSecond) ) + s = 4; + VSIFPrintfL(fp, "\n", szCol, iRow, s); + if (eType != OFTTime) + dfNumberOfDaysSince1900 += NUMBER_OF_DAYS_BETWEEN_1900_AND_1970; + if (eType == OFTDate) + VSIFPrintfL(fp, "%d\n", (int)(dfNumberOfDaysSince1900 + 0.1)); + else + VSIFPrintfL(fp, "%.16f\n", dfNumberOfDaysSince1900); + VSIFPrintfL(fp, "\n"); + } + else + { + const char* pszVal = poFeature->GetFieldAsString(j); + std::map::iterator oIter = oStringMap.find(pszVal); + int nStringIndex; + if (oIter != oStringMap.end()) + nStringIndex = oIter->second; + else + { + nStringIndex = (int)oStringList.size(); + oStringMap[pszVal] = nStringIndex; + oStringList.push_back(pszVal); + } + VSIFPrintfL(fp, "\n", szCol, iRow); + VSIFPrintfL(fp, "%d\n", nStringIndex); + VSIFPrintfL(fp, "\n"); + } + } + } + VSIFPrintfL(fp, "\n"); + + iRow ++; + delete poFeature; + poFeature = poLayer->GetNextFeature(); + } + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); +} + +/************************************************************************/ +/* WriteSharedStrings() */ +/************************************************************************/ + +static void WriteSharedStrings(const char* pszName, + CPL_UNUSED std::map& oStringMap, + std::vector& oStringList) +{ + VSILFILE* fp; + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/sharedStrings.xml", pszName), "wb"); + VSIFPrintfL(fp, XML_HEADER); + VSIFPrintfL(fp, "\n", + MAIN_NS, + (int)oStringList.size()); + for(int i = 0; i < (int)oStringList.size(); i++) + { + VSIFPrintfL(fp, "\n"); + char* pszXML = OGRGetXML_UTF8_EscapedString(oStringList[i].c_str()); + VSIFPrintfL(fp, "%s\n", pszXML); + CPLFree(pszXML); + VSIFPrintfL(fp, "\n"); + } + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); +} + +/************************************************************************/ +/* WriteStyles() */ +/************************************************************************/ + +static void WriteStyles(const char* pszName) +{ + VSILFILE* fp; + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/styles.xml", pszName), "wb"); + VSIFPrintfL(fp, XML_HEADER); + VSIFPrintfL(fp, "\n", MAIN_NS); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); +} + +/************************************************************************/ +/* WriteWorkbookRels() */ +/************************************************************************/ + +static void WriteWorkbookRels(const char* pszName, int nLayers) +{ + VSILFILE* fp; + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/_rels/workbook.xml.rels", pszName), "wb"); + VSIFPrintfL(fp, XML_HEADER); + VSIFPrintfL(fp, "\n", SCHEMA_PACKAGE_RS); + VSIFPrintfL(fp, "\n", SCHEMA_OD_RS); + for(int i=0;i\n", + 2 + i, SCHEMA_OD_RS, 1 + i); + } + VSIFPrintfL(fp, "\n", + 2 + nLayers, SCHEMA_OD_RS); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); +} + +/************************************************************************/ +/* WriteDotRels() */ +/************************************************************************/ + +static void WriteDotRels(const char* pszName) +{ + VSILFILE* fp; + + fp = VSIFOpenL(CPLSPrintf("/vsizip/%s/_rels/.rels", pszName), "wb"); + VSIFPrintfL(fp, XML_HEADER); + VSIFPrintfL(fp, "\n", SCHEMA_PACKAGE_RS); + VSIFPrintfL(fp, "\n", SCHEMA_OD_RS); + VSIFPrintfL(fp, "\n", SCHEMA_PACKAGE_RS); + VSIFPrintfL(fp, "\n", SCHEMA_OD_RS); + VSIFPrintfL(fp, "\n"); + VSIFCloseL(fp); +} + +/************************************************************************/ +/* FlushCache() */ +/************************************************************************/ + +void OGRXLSXDataSource::FlushCache() +{ + int i; + + if (!bUpdated) + return; + + VSIStatBufL sStat; + if (VSIStatL(pszName, &sStat) == 0) + { + if (VSIUnlink( pszName ) != 0) + { + CPLError(CE_Failure, CPLE_FileIO, + "Cannot delete %s", pszName); + return; + } + } + + /* Cause all layers to be initialized */ + for(int i = 0; iGetLayerDefn(); + } + + /* Maintain new ZIP files opened */ + VSILFILE* fpZIP = VSIFOpenL(CPLSPrintf("/vsizip/%s", pszName), "wb"); + if (fpZIP == NULL) + { + CPLError(CE_Failure, CPLE_FileIO, + "Cannot create %s", pszName); + return; + } + + WriteContentTypes(pszName, nLayers); + + //VSIMkdir(CPLSPrintf("/vsizip/%s/docProps", pszName),0755); + WriteApp(pszName); + WriteCore(pszName); + + //VSIMkdir(CPLSPrintf("/vsizip/%s/xl", pszName),0755); + WriteWorkbook(pszName, this); + + std::map oStringMap; + std::vector oStringList; + + //VSIMkdir(CPLSPrintf("/vsizip/%s/xl/worksheets", pszName),0755); + for(i=0;iSetUpdated(FALSE); + } + + return; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/ogrxlsxdriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/ogrxlsxdriver.cpp new file mode 100644 index 000000000..e37813f49 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xlsx/ogrxlsxdriver.cpp @@ -0,0 +1,205 @@ +/****************************************************************************** + * $Id: ogrxlsxdriver.cpp 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: XLSX Translator + * Purpose: Implements OGRXLSXDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_xlsx.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: ogrxlsxdriver.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +extern "C" void RegisterOGRXLSX(); + +// g++ -DHAVE_EXPAT -g -Wall -fPIC ogr/ogrsf_frmts/xlsx/*.cpp -shared -o ogr_XLSX.so -Iport -Igcore -Iogr -Iogr/ogrsf_frmts -Iogr/ogrsf_frmts/mem -Iogr/ogrsf_frmts/xlsx -L. -lgdal + +/************************************************************************/ +/* ~OGRXLSXDriver() */ +/************************************************************************/ + +OGRXLSXDriver::~OGRXLSXDriver() + +{ +} + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRXLSXDriver::GetName() + +{ + return "XLSX"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +#define XLSX_MIMETYPE "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" + +OGRDataSource *OGRXLSXDriver::Open( const char * pszFilename, int bUpdate ) + +{ + if (!EQUAL(CPLGetExtension(pszFilename), "XLSX")) + return NULL; + + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return NULL; + + int bOK = FALSE; + char szBuffer[2048]; + if (VSIFReadL(szBuffer, sizeof(szBuffer), 1, fp) == 1 && + memcmp(szBuffer, "PK", 2) == 0) + { + bOK = TRUE; + } + + VSIFCloseL(fp); + + if (!bOK) + return NULL; + + VSILFILE* fpContent = VSIFOpenL(CPLSPrintf("/vsizip/%s/[Content_Types].xml", pszFilename), "rb"); + if (fpContent == NULL) + return NULL; + + int nRead = (int)VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fpContent); + szBuffer[nRead] = 0; + + VSIFCloseL(fpContent); + + if (strstr(szBuffer, XLSX_MIMETYPE) == NULL) + return NULL; + + VSILFILE* fpWorkbook = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/workbook.xml", pszFilename), "rb"); + if (fpWorkbook == NULL) + return NULL; + + VSILFILE* fpSharedStrings = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/sharedStrings.xml", pszFilename), "rb"); + VSILFILE* fpStyles = VSIFOpenL(CPLSPrintf("/vsizip/%s/xl/styles.xml", pszFilename), "rb"); + + OGRXLSXDataSource *poDS = new OGRXLSXDataSource(); + + if( !poDS->Open( pszFilename, fpWorkbook, fpSharedStrings, fpStyles, bUpdate ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* CreateDataSource() */ +/************************************************************************/ + +OGRDataSource *OGRXLSXDriver::CreateDataSource( const char * pszName, + char **papszOptions ) + +{ + if (!EQUAL(CPLGetExtension(pszName), "XLSX")) + { + CPLError( CE_Failure, CPLE_AppDefined, "File extension should be XLSX" ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* First, ensure there isn't any such file yet. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + + if( VSIStatL( pszName, &sStatBuf ) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "It seems a file system object called '%s' already exists.", + pszName ); + + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Try to create datasource. */ +/* -------------------------------------------------------------------- */ + OGRXLSXDataSource *poDS; + + poDS = new OGRXLSXDataSource(); + + if( !poDS->Create( pszName, papszOptions ) ) + { + delete poDS; + return NULL; + } + else + return poDS; +} + +/************************************************************************/ +/* DeleteDataSource() */ +/************************************************************************/ + +OGRErr OGRXLSXDriver::DeleteDataSource( const char *pszName ) +{ + if (VSIUnlink( pszName ) == 0) + return OGRERR_NONE; + else + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRXLSXDriver::TestCapability( const char * pszCap ) + +{ + if( EQUAL(pszCap,ODrCCreateDataSource) ) + return TRUE; + else if( EQUAL(pszCap,ODrCDeleteDataSource) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRXLSX() */ +/************************************************************************/ + +void RegisterOGRXLSX() + +{ + OGRSFDriver* poDriver = new OGRXLSXDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "MS Office Open XML spreadsheet" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "xlsx" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_xlsx.html" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date DateTime Time" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/GNUmakefile b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/GNUmakefile new file mode 100644 index 000000000..e68dfce9a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/GNUmakefile @@ -0,0 +1,21 @@ + + +include ../../../GDALmake.opt + +OBJ = ogrxplanedriver.o ogrxplanedatasource.o ogrxplanelayer.o ogr_xplane_geo_utils.o \ + ogr_xplane_reader.o ogr_xplane_apt_reader.o ogr_xplane_nav_reader.o \ + ogr_xplane_fix_reader.o ogr_xplane_awy_reader.o + +CPPFLAGS := -I.. -I../.. $(CPPFLAGS) + +default: $(O_OBJ:.o=.$(OBJ_EXT)) + +clean: + rm -f *.o $(O_OBJ) test_geo_utils$(EXE) + +$(O_OBJ): ogr_xplane.h ogr_xplane_reader.h ogr_xplane_geo_utils.h \ + ogr_xplane_apt_reader.h ogr_xplane_nav_reader.h \ + ogr_xplane_fix_reader.h ogr_xplane_awy_reader.h ../../ogr_geometry.h + +test_geo_utils$(EXE): test_geo_utils.$(OBJ_EXT) ogr_xplane_geo_utils.$(OBJ_EXT) + $(LD) $(LDFLAGS) test_geo_utils.$(OBJ_EXT) ogr_xplane_geo_utils.$(OBJ_EXT) -o test_geo_utils$(EXE) -lm \ No newline at end of file diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/drv_xplane.html b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/drv_xplane.html new file mode 100644 index 000000000..aaf0bd676 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/drv_xplane.html @@ -0,0 +1,611 @@ + + +X-Plane/Flightgear aeronautical data (available from GDAL 1.6.0) + + + + +

    X-Plane/Flightgear aeronautical data

    + +The X-Plane aeronautical data is supported for read access. This data is for example used by the X-Plane and Flighgear software.

    + +The driver is able to read the following files :

    + +

    + + + + + +
    FilenameDescriptionSupported versions
    apt.datAirport data850, 810
    nav.dat (or earth_nav.dat)Navigation aids810, 740
    fix.dat (or earth_fix.dat)IFR intersections600
    awy.dat (or earth_awy.dat)Airways640
    + +

    + +Each file will be reported as a set of layers whose data schema is given below. +The data schema is generally as close as possible to the original schema data described +in the X-Plane specificiation. However, please note that meters (or kilometers) are always used +to report heights, elevations, distances (widths, lengths), etc., even if the original data +are sometimes expressed in feet or nautical miles.

    + +Data is reported as being expressed in WGS84 datum (latitude, longitude), altough the specificiation +is not very clear on that subject.

    + +The OGR_XPLANE_READ_WHOLE_FILE configuration option can be set to FALSE when reading a big file +in regards with the available RAM (especially true for apt.dat). This option forces the driver not to cache features in RAM, +but just to fetch the features of the current layer. Of course, this will have a negative impact on performance. + +

    Examples

    + +Converting all the layers contained in 'apt.dat' in a set of shapefiles : +
    +% ogr2ogr apt_shapes apt.dat
    +
    + +

    +Converting all the layers contained in 'apt.dat' into a PostreSQL database : +

    +% PG_USE_COPY=yes ogr2ogr -overwrite -f PostgreSQL PG:"dbname=apt" apt.dat
    +
    + +

    See Also

    + + + +
    + +

    Airport data (apt.dat)

    + +This file contains the description of elements defining airports, heliports, seabases, with their runways and taxiways, ATC frequencies, etc.

    + +The following layers are reported :

    + +

    + +

    +All the layers other than APT will refer to the airport thanks to the "apt_icao" column, +that can serve as a foreign key.

    + +

    APT layer

    + +Main description for an aiport. The position reported will be the position of the tower view point if present, otherwise the +position of the first runway threshold found.

    + +Fields: +

      +
    • apt_icao: String (4.0). ICAO code for the airport. +
    • apt_name: String (0.0). Full name of the airport. +
    • type: Integer (1.0). Airport type : 0 for regular airport, 1 for seaplane/floatplane base, 2 for heliport (added in GDAL 1.7.0) +
    • elevation_m: Real (8.2). Elevation of the airport (in meters). +
    • has_tower: Integer (1.0). Set to 1 if the airport has a tower view point. +
    • hgt_tower_m: Real (8.2). Height of the tower view point if present. +
    • tower_name: String (32.0). Name of the tower view point if present. +
    +

    + + +

    RunwayThreshold layer

    + +This layer contains the description of one threshold of a runway.
    +The runway itself is fully be described by its 2 thresholds, and the RunwayPolygon layer.

    + +Note : when a runway has a displaced threshold, the threshold will be reported as 2 features : one at +the non-displaced threshold position (is_displaced=0), and another one at the displaced threshold position (is_displaced=1).

    + +Fields: +

      +
    • apt_icao: String (4.0). ICAO code for the airport of this runway threshold. +
    • rwy_num: String (3.0). Code for the runway, such as 18, 02L, etc... Unique for each aiport. +
    • width_m: Real (3.0). Width in meters. +
    • surface: String (0.0). Type of the surface among : +
        +
      • Asphalt +
      • Concrete +
      • Turf/grass +
      • Dirt +
      • Gravel +
      • Dry lakebed +
      • Water +
      • Snow +
      • Transparent +
      +
    • shoulder: String (0.0). Type of the runway shoulder among : +
        +
      • None +
      • Asphalt +
      • Concrete +
      +
    • smoothness: Real (4.2). Runway smoothness. Percentage between 0.00 and 1.00. 0.25 is the default value. +
    • centerline_lights: Integer (1.0). Set to 1 if the runway has centre-line lights +
    • edge_lighting: String (0.0). Type of edge lighting among : +
        +
      • None +
      • Yes (when imported from V810 records) +
      • LIRL . Low intensity runway lights (proposed for V90x) +
      • MIRL : Medium intensity runway lights +
      • HIRL : High intensity runway lights (proposed for V90x) +
      +
    • distance_remaining_signs: Integer (1.0). Set to 1 if the runway has 'distance remaining' lights. +
    • displaced_threshold_m: Real (3.0). Distance between the threshold and the displaced threshold. +
    • is_displaced: Integer (1.0). Set to 1 if the position is the position of the displaced threshold. +
    • stopway_length_m: Real (3.0). Length of stopway/blastpad/over-run at the approach end of runway in meters +
    • markings: String (0.0). Runway markings for the end of the runway among : +
        +
      • None +
      • Visual +
      • Non-precision approach +
      • Precision approach +
      • UK-style non-precision +
      • UK-style precision +
      +
    • approach_lighting: String (0.0). Approach lighting for the end of the runway among : +
        +
      • None +
      • ALSF-I +
      • ALSF-II +
      • Calvert +
      • Calvert ISL Cat II and III +
      • SSALR +
      • SSALS (V810 records) +
      • SSALF +
      • SALS +
      • MALSR +
      • MALSF +
      • MALS +
      • ODALS +
      • RAIL +
      +
    • touchdown_lights: Integer (1.0). Set to 1 if the runway has touchdown-zone lights (TDZL) +
    • REIL: String (0.0). Runway End Identifier Lights (REIL) among : +
        +
      • None +
      • Omni-directional +
      • Unidirectionnal +
      +
    • length_m: Real (5.0). (Computed field). Length in meters between the 2 thresholds at both ends of the runway. The displaced thresholds are not taken into account in this computation. +
    • true_heading_deg: Real (6.2). (Computed field). True heading in degree at the approach of the end of the runway. +
    + + +

    RunwayPolygon layer

    + +This layer contains the rectangular shape of a runway. It is computed from the runway threshold information. +When not specified, the meaning of the fields is the same as the RunwayThreshold layer. + +Fields: +
      +
    • apt_icao: String (4.0) +
    • rwy_num1: String (3.0). Code for first runway threshold. For example 20L. +
    • rwy_num2: String (3.0). Code for the second the runway threshold. For example 02R. +
    • width_m: Real (3.0) +
    • surface: String (0.0) +
    • shoulder: String (0.0) +
    • smoothness: Real (4.2) +
    • centerline_lights: Integer (1.0) +
    • edge_lighting: String (0.0) +
    • distance_remaining_signs: Integer (1.0) +
    • length_m: Real (5.0) +
    • true_heading_deg: Real (6.2). True heading from the first runway to the second runway. +
    + + +

    WaterRunwayThreshold (Point)

    + +Fields: +
      +
    • apt_icao: String (4.0) +
    • rwy_num: String (3.0). Code for the runway, such as 18. Unique for each aiport. +
    • width_m: Real (3.0) +
    • has_buoys: Integer (1.0). Set to 1 if the runway should be marked with buoys bobbing in the water +
    • length_m: Real (5.0). (Computed field) Length between the two ends of the water runway. +
    • true_heading_deg: Real (6.2). (Computed field). True heading in degree at the approach of the end of the runway. +
    + +

    WaterRunwayPolygon (Polygon)

    + +This layer contains the rectangular shape of a water runway. It is computed from the water runway threshold information. + +Fields: +
      +
    • apt_icao: String (4.0) +
    • rwy_num1: String (3.0) +
    • rwy_num2: String (3.0) +
    • width_m: Real (3.0) +
    • has_buoys: Integer (1.0) +
    • length_m: Real (5.0) +
    • true_heading_deg: Real (6.2) +
    + +

    Stopway layer (Polygon)

    + +(Starting with GDAL 1.7.0) + +This layer contains the rectangular shape of a stopway/blastpad/over-run that may be found at the beginning of +a runway. It is part of the tarmac but not intended to be used for normal operations. +It is computed from the runway stopway/blastpad/over-run length information and only present when +this length is non zero. +When not specified, the meaning of the fields is the same as the RunwayThreshold layer. + +Fields: +
      +
    • apt_icao: String (4.0) +
    • rwy_num: String (3.0). +
    • width_m: Real (3.0) +
    • length_m: Real (5.0) : Length of stopway/blastpad/over-run at the approach end of runway in meters. +
    + +

    Helipad (Point)

    + +This layer contains the center of a helipad. + +Fields: +
      +
    • apt_icao: String (4.0) +
    • helipad_name: String (5.0). Name of the helipad in the format "Hxx". Unique for each aiport. +
    • true_heading_deg: Real (6.2) +
    • length_m: Real (5.0) +
    • width_m: Real (3.0) +
    • surface: String (0.0). See above runway surface codes. +
    • markings: String (0.0). See above runway markings codes. +
    • shoulder: String (0.0). See above runway shoulder codes. +
    • smoothness: Real (4.2). See above runway smoothness description. +
    • edge_lighting: String (0.0). Helipad edge lighting among : +
        +
      • None +
      • Yes (V810 records) +
      • Yellow +
      • White (proposed for V90x) +
      • Red (V810 records) +
      +
    + + +

    HelipadPolygon (Polygon)

    + +This layer contains the rectangular shape of a helipad. The fields are identical to the Helipad layer. + +

    TaxiwayRectangle (Polygon) - V810 record

    + +This layer contains the rectangular shape of a taxiway. + +Fields: +
      +
    • apt_icao: String (4.0) +
    • true_heading_deg: Real (6.2) +
    • length_m: Real (5.0) +
    • width_m: Real (3.0) +
    • surface: String (0.0). See above runway surface codes. +
    • smoothness: Real (4.2). See above runway smoothness description. +
    • edge_lighting: Integer (1.0). Set to 1 if the taxiway has edge lighting. +
    + +

    Pavement (Polygon)

    + +This layer contains polygonal chunks of pavement for taxiways and aprons. The polygons may include holes.

    +The source file may contain Bezier curves as sides of the polygon. Due to the lack of support for such geometry into OGR Simple Feature model, Bezier curves are discretized into linear pieces.

    + +Fields: +

      +
    • apt_icao: String (4.0) +
    • name: String (0.0) +
    • surface: String (0.0). See above runway surface codes. +
    • smoothness: Real (4.2). See above runway smoothness description. +
    • texture_heading: Real (6.2). Pavement texture grain direction in true degrees +
    + +

    APTBoundary (Polygon)

    + +This layer contains the boundary of the aiport. There is at the maximum one such feature per aiport. +The polygon may include holes. Bezier curves are discretized into linear pieces.

    + +Fields: +

      +
    • apt_icao: String (4.0) +
    • name: String (0.0) +
    + +

    APTLinearFeature (Line String)

    + +This layer contains linear features. Bezier curves are discretized into linear pieces.

    + +Fields: +

      +
    • apt_icao: String (4.0) +
    • name: String (0.0) +
    + +

    StartupLocation (Point)

    + +Define gate positions, ramp locations etc.

    + +Fields: +

      +
    • apt_icao: String (4.0) +
    • name: String (0.0) +
    • true_heading_deg: Real (6.2) +
    + +

    APTLightBeacon (Point)

    + +Define airport light beacons.

    + +Fields: +

      +
    • apt_icao: String (4.0) +
    • name: String (0.0) +
    • color: String (0.0). Color of the light beacon among : +
        +
      • None +
      • White-green: land airport +
      • White-yellow: seaplane base +
      • Green-yellow-white: heliports +
      • White-white-green: military field +
      +
    + +

    APTWindsock (Point)

    + +Define airport windsocks.

    + +Fields: +

      +
    • apt_icao: String (4.0) +
    • name: String (0.0) +
    • is_illuminated: Integer (1.0) +
    • +
    + +

    TaxiwaySign (Point)

    + +Define airport taxiway signs.

    + +Fields: +

      +
    • apt_icao: String (4.0) +
    • text: String (0.0). This is somehow encoded into a specific format. See X-Plane specification for more details. +
    • true_heading_deg: Real (6.2) +
    • size: Integer (1.0). From 1 to 5. See X-Plane specification for more details. +
    + +

    VASI_PAPI_WIGWAG (Point)

    + +Define a VASI, PAPI or Wig-Wag. For PAPIs and Wig-Wags, the coordinate is the centre of the display. For VASIs, this is the mid point between the two VASI light units.

    + +Fields: +

      +
    • apt_icao: String (4.0) +
    • rwy_num: String (3.0). Foreign key to the rwy_num field of the RunwayThreshold layer. +
    • type: String (0.0). Type among : +
        +
      • VASI +
      • PAPI Left +
      • PAPI Right +
      • Space Shuttle PAPI +
      • Tri-colour VASI +
      • Wig-Wag lights +
      +
    • true_heading_deg: Real (6.2) +
    • visual_glide_deg: Real (4.2) +
    + +

    ATCFreq (None)

    + +Define an airport ATC frequency. Note that this layer has no geometry.

    + +Fields: +

      +
    • apt_icao: String (4.0) +
    • atc_type: String (4.0). Type of the frequency among (derived from the record type number) : +
        +
      • ATIS : AWOS (Automatic Weather Observation System), ASOS (Automatic Surface Observation System) or ATIS (Automated Terminal Information System) +
      • CTAF : Unicom or CTAF (USA), radio (UK) +
      • CLD : Clearance delivery (CLD) +
      • GND : Ground +
      • TWR : Tower +
      • APP : Approach +
      • DEP : Departure +
      +
    • freq_name: String (0.0). Name of the ATC frequency. This is often an abbreviation (such as GND for "Ground"). +
    • freq_mhz: Real (7.3). Frequency in MHz. +
    + +
    + +

    Navigation aids (nav.dat)

    + +This file contains the description of various navigation aids beacons.

    + +The following layers are reported :

    +

    + +

    ILS (Point)

    + +Localiser that is part of a full ILS, or Stand-alone localiser (LOC), also including a LDA (Landing Directional Aid) or SDF (Simplified Directional Facility).

    + +Fields : +

      +
    • navaid_id: String (4.0). Identification of nav-aid. *NOT* unique. +
    • apt_icao: String (4.0). Foreign key to the apt_icao field of the RunwayThreshold layer. +
    • rwy_num: String (3.0). Foreign key to the rwy_num field of the RunwayThreshold layer. +
    • subtype: String (10.0). Sub-type among : +
        +
      • ILS-cat-I +
      • ILS-cat-II +
      • ILS-cat-III +
      • LOC +
      • LDA +
      • SDF +
      • IGS +
      • LDA-GS +
      +
    • elevation_m: Real (8.2). Elevation of nav-aid in meters. +
    • freq_mhz: Real (7.3). Frequency of nav-aid in MHz. +
    • range_km: Real (7.3). Range of nav-aid in km. +
    • true_heading_deg: Real (6.2). True heading of the localiser in degree. +
    + +

    VOR (Point)

    + +Navaid of type VOR, VORTAC or VOR-DME.

    + +Fields : +

      +
    • navaid_id: String (4.0). Identification of nav-aid. *NOT* unique. +
    • navaid_name: String (0.0) +
    • subtype: String (10.0). Among VOR, VORTAC or VOR-DME +
    • elevation_m: Real (8.2) +
    • freq_mhz: Real (7.3) +
    • range_km: Real (7.3) +
    • slaved_variation_deg: Real (6.2). Indicates the slaved variation of a VOR/VORTAC in degrees. +
    + +

    NDB (Point)

    + +Fields : +
      +
    • navaid_id: String (4.0). Identification of nav-aid. *NOT* unique. +
    • navaid_name: String (0.0) +
    • subtype: String (10.0). Among NDB, LOM, NDB-DME. +
    • elevation_m: Real (8.2) +
    • freq_khz: Real (7.3). Frenquency in kHz +
    • range_km: Real (7.3) +
    + +

    GS - Glideslope (Point)

    + +Glideslope nav-aid.

    + +Fields : +

      +
    • navaid_id: String (4.0). Identification of nav-aid. *NOT* unique. +
    • apt_icao: String (4.0). Foreign key to the apt_icao field of the RunwayThreshold layer. +
    • rwy_num: String (3.0). Foreign key to the rwy_num field of the RunwayThreshold layer. +
    • elevation_m: Real (8.2) +
    • freq_mhz: Real (7.3) +
    • range_km: Real (7.3) +
    • true_heading_deg: Real (6.2). True heading of the glideslope in degree. +
    • glide_slope: Real (6.2). Glide-slope angle in degree (typically 3 degree) +
    + +

    Marker - ILS marker beacons. (Point)

    + +Nav-aids of type Outer Marker (OM), Middle Marker (MM) or Inner Marker (IM).

    + +Fields: +

      +
    • apt_icao: String (4.0). Foreign key to the apt_icao field of the RunwayThreshold layer. +
    • rwy_num: String (3.0). Foreign key to the rwy_num field of the RunwayThreshold layer. +
    • subtype: String (10.0). Among OM, MM or IM. +
    • elevation_m: Real (8.2) +
    • true_heading_deg: Real (6.2). True heading of the glideslope in degree. +
    + +

    DME (Point)

    + +DME, including the DME element of an VORTAC, VOR-DME or NDB-DME.

    + +Fields: +

      +
    • navaid_id: String (4.0). Identification of nav-aid. *NOT* unique. +
    • navaid_name: String (0.0) +
    • subtype: String (10.0). Among VORTAC, VOR-DME, TACAN or NDB-DME +
    • elevation_m: Real (8.2) +
    • freq_mhz: Real (7.3) +
    • range_km: Real (7.3) +
    • bias_km: Real (6.2). This bias must be subtracted from the calculated distance to the DME to give the desired cockpit reading +
    + +

    DMEILS (Point)

    + +DME element of an ILS.

    + +Fields: +

      +
    • navaid_id: String (4.0). Identification of nav-aid. *NOT* unique. +
    • apt_icao: String (4.0). Foreign key to the apt_icao field of the RunwayThreshold layer. +
    • rwy_num: String (3.0). Foreign key to the rwy_num field of the RunwayThreshold layer. +
    • elevation_m: Real (8.2) +
    • freq_mhz: Real (7.3) +
    • range_km: Real (7.3) +
    • bias_km: Real (6.2). This bias must be subtracted from the calculated distance to the DME to give the desired cockpit reading +
    + +
    + +

    IFR intersections (fix.dat)

    + +This file contain IFR intersections (often referred to as "fixes").

    + +The following layer is reported :

    +

      +
    • FIX (Point)
    • +
    + +

    FIX (Point)

    + +Fields: +
      +
    • fix_name: String (5.0). Intersection name. *NOT* unique. +
    + +
    + +

    Airways (awy.dat)

    + +This file contains the description of airway segments.

    + +The following layers are reported :

    +

    + +

    AirwaySegment (Line String)

    + +Fields: +
      +
    • segment_name: String (0.0) +
    • point1_name: String (0.0) : Name of intersection or nav-aid at the beginning of this segment +
    • point2_name: String (0.0) : Name of intersection or nav-aid at the beginning of this segment +
    • is_high: Integer (1.0) : Set to 1 if this is a "High" airway. +
    • base_FL: Integer (3.0) : Fligh level (hundreds of feet) of the base of the airway. +
    • top_FL: Integer (3.0) : Fligh level (hundreds of feet) of the top of the airway. +
    + +

    AirwayIntersection (Point)

    + +Fields: +
      +
    • name: String (0.0) : Name of intersection or nav-aid +
    + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/makefile.vc b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/makefile.vc new file mode 100644 index 000000000..d23d95b20 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/makefile.vc @@ -0,0 +1,18 @@ + +OBJ = ogrxplanedriver.obj ogrxplanedatasource.obj ogrxplanelayer.obj ogr_xplane_geo_utils.obj \ + ogr_xplane_reader.obj ogr_xplane_apt_reader.obj ogr_xplane_nav_reader.obj \ + ogr_xplane_fix_reader.obj ogr_xplane_awy_reader.obj + +EXTRAFLAGS = -I.. -I..\.. + +GDAL_ROOT = ..\..\.. + +!INCLUDE $(GDAL_ROOT)\nmake.opt + +default: $(OBJ) + +clean: + -del *.obj *.pdb + + + diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane.h new file mode 100644 index 000000000..8d74a35fa --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane.h @@ -0,0 +1,131 @@ +/****************************************************************************** + * $Id: ogr_xplane.h $ + * + * Project: X-Plane aeronautical data reader + * Purpose: Definition of classes for OGR X-Plane aeronautical data driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_XPLANE_H_INCLUDED +#define _OGR_XPLANE_H_INCLUDED + +#include "ogrsf_frmts.h" + +class OGRXPlaneReader; +class OGRXPlaneDataSource; + +/************************************************************************/ +/* OGRXPlaneLayer */ +/************************************************************************/ + +class OGRXPlaneLayer : public OGRLayer +{ + private: + int nFID; + int nFeatureArraySize; + int nFeatureArrayMaxSize; + int nFeatureArrayIndex; + + OGRFeature** papoFeatures; + OGRSpatialReference *poSRS; + + OGRXPlaneDataSource* poDS; + + protected: + OGRXPlaneReader* poReader; + OGRFeatureDefn* poFeatureDefn; + OGRXPlaneLayer(const char* pszLayerName); + + void RegisterFeature(OGRFeature* poFeature); + + public: + virtual ~OGRXPlaneLayer(); + + void SetDataSource(OGRXPlaneDataSource* poDS); + + void SetReader(OGRXPlaneReader* poReader); + int IsEmpty() { return nFeatureArraySize == 0; } + void AutoAdjustColumnsWidth(); + + virtual void ResetReading(); + virtual OGRFeature * GetNextFeature(); + virtual OGRFeature * GetFeature( GIntBig nFID ); + virtual OGRErr SetNextByIndex( GIntBig nIndex ); + virtual GIntBig GetFeatureCount( int bForce = TRUE ); + + virtual OGRFeatureDefn * GetLayerDefn(); + virtual int TestCapability( const char * pszCap ); +}; + + +/************************************************************************/ +/* OGRXPlaneDataSource */ +/************************************************************************/ + +class OGRXPlaneDataSource : public OGRDataSource +{ + char* pszName; + + OGRXPlaneLayer** papoLayers; + int nLayers; + + OGRXPlaneReader* poReader; + int bReadWholeFile; + int bWholeFiledReadingDone; + + void Reset(); + + public: + OGRXPlaneDataSource(); + ~OGRXPlaneDataSource(); + + int Open( const char * pszFilename, int bReadWholeFile = TRUE ); + + void RegisterLayer( OGRXPlaneLayer* poLayer ); + + virtual int GetLayerCount() { return nLayers; } + virtual OGRLayer* GetLayer( int ); + virtual const char* GetName() { return pszName; } + + virtual int TestCapability( const char * pszCap ); + + void ReadWholeFileIfNecessary(); +}; + +/************************************************************************/ +/* OGRXPlaneDriver */ +/************************************************************************/ + +class OGRXPlaneDriver : public OGRSFDriver +{ + public: + + virtual const char* GetName(); + OGRDataSource* Open( const char *, int ); + + virtual int TestCapability( const char * pszCap ); +}; + + +#endif /* ndef _OGR_XPLANE_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_apt_reader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_apt_reader.cpp new file mode 100644 index 000000000..f993629c0 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_apt_reader.cpp @@ -0,0 +1,3219 @@ +/****************************************************************************** + * $Id: ogr_xplane_apt_reader.cpp + * + * Project: X-Plane apt.dat file reader + * Purpose: Implements OGRXPlaneAptReader class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_xplane_apt_reader.h" +#include "ogr_xplane_geo_utils.h" + +CPL_CVSID("$Id: ogr_xplane_apt_reader.cpp 29211 2015-05-19 19:40:57Z rouault $"); + +/************************************************************************/ +/* OGRXPlaneCreateAptFileReader */ +/************************************************************************/ + +OGRXPlaneReader* OGRXPlaneCreateAptFileReader( OGRXPlaneDataSource* poDataSource ) +{ + OGRXPlaneReader* poReader = new OGRXPlaneAptReader(poDataSource); + return poReader; +} + +/************************************************************************/ +/* OGRXPlaneAptReader() */ +/************************************************************************/ +OGRXPlaneAptReader::OGRXPlaneAptReader() +{ + poDataSource = NULL; + nVersion = APT_V_UNKNOWN; + + poAPTLayer = NULL; + poRunwayLayer = NULL; + poRunwayThresholdLayer = NULL; + poStopwayLayer = NULL; + poWaterRunwayLayer = NULL; + poWaterRunwayThresholdLayer = NULL; + poHelipadLayer = NULL; + poHelipadPolygonLayer = NULL; + poTaxiwayRectangleLayer =NULL; + poPavementLayer = NULL; + poAPTBoundaryLayer = NULL; + poAPTLinearFeatureLayer = NULL; + poATCFreqLayer = NULL; + poStartupLocationLayer = NULL; + poAPTLightBeaconLayer = NULL; + poAPTWindsockLayer = NULL; + poTaxiwaySignLayer = NULL; + poVASI_PAPI_WIGWAG_Layer = NULL; + poTaxiLocationLayer = NULL; + + Rewind(); +} + +/************************************************************************/ +/* OGRXPlaneAptReader() */ +/************************************************************************/ + +OGRXPlaneAptReader::OGRXPlaneAptReader( OGRXPlaneDataSource* poDataSourceIn ) +{ + poDataSource = poDataSourceIn; + nVersion = APT_V_UNKNOWN; + + poAPTLayer = new OGRXPlaneAPTLayer(); + poRunwayLayer = new OGRXPlaneRunwayLayer(); + poRunwayThresholdLayer = new OGRXPlaneRunwayThresholdLayer(); + poStopwayLayer = new OGRXPlaneStopwayLayer(); + poWaterRunwayLayer = new OGRXPlaneWaterRunwayLayer(); + poWaterRunwayThresholdLayer = new OGRXPlaneWaterRunwayThresholdLayer(); + poHelipadLayer = new OGRXPlaneHelipadLayer(); + poHelipadPolygonLayer = new OGRXPlaneHelipadPolygonLayer(); + poTaxiwayRectangleLayer = new OGRXPlaneTaxiwayRectangleLayer(); + poPavementLayer = new OGRXPlanePavementLayer(); + poAPTBoundaryLayer = new OGRXPlaneAPTBoundaryLayer(); + poAPTLinearFeatureLayer = new OGRXPlaneAPTLinearFeatureLayer(); + poATCFreqLayer = new OGRXPlaneATCFreqLayer(); + poStartupLocationLayer = new OGRXPlaneStartupLocationLayer(); + poAPTLightBeaconLayer = new OGRXPlaneAPTLightBeaconLayer(); + poAPTWindsockLayer = new OGRXPlaneAPTWindsockLayer(); + poTaxiwaySignLayer = new OGRXPlaneTaxiwaySignLayer(); + poVASI_PAPI_WIGWAG_Layer = new OGRXPlane_VASI_PAPI_WIGWAG_Layer(); + poTaxiLocationLayer = NULL; + + poDataSource->RegisterLayer(poAPTLayer); + poDataSource->RegisterLayer(poRunwayLayer); + poDataSource->RegisterLayer(poRunwayThresholdLayer); + poDataSource->RegisterLayer(poStopwayLayer); + poDataSource->RegisterLayer(poWaterRunwayLayer); + poDataSource->RegisterLayer(poWaterRunwayThresholdLayer); + poDataSource->RegisterLayer(poHelipadLayer); + poDataSource->RegisterLayer(poHelipadPolygonLayer); + poDataSource->RegisterLayer(poTaxiwayRectangleLayer); + poDataSource->RegisterLayer(poPavementLayer); + poDataSource->RegisterLayer(poAPTBoundaryLayer); + poDataSource->RegisterLayer(poAPTLinearFeatureLayer); + poDataSource->RegisterLayer(poATCFreqLayer); + poDataSource->RegisterLayer(poStartupLocationLayer); + poDataSource->RegisterLayer(poAPTLightBeaconLayer); + poDataSource->RegisterLayer(poAPTWindsockLayer); + poDataSource->RegisterLayer(poTaxiwaySignLayer); + poDataSource->RegisterLayer(poVASI_PAPI_WIGWAG_Layer); + + Rewind(); +} + +/************************************************************************/ +/* CloneForLayer() */ +/************************************************************************/ + +OGRXPlaneReader* OGRXPlaneAptReader::CloneForLayer(OGRXPlaneLayer* poLayer) +{ + OGRXPlaneAptReader* poReader = new OGRXPlaneAptReader(); + + poReader->poInterestLayer = poLayer; + SET_IF_INTEREST_LAYER(poAPTLayer); + SET_IF_INTEREST_LAYER(poRunwayLayer); + SET_IF_INTEREST_LAYER(poRunwayThresholdLayer); + SET_IF_INTEREST_LAYER(poStopwayLayer); + SET_IF_INTEREST_LAYER(poWaterRunwayLayer); + SET_IF_INTEREST_LAYER(poWaterRunwayThresholdLayer); + SET_IF_INTEREST_LAYER(poHelipadLayer); + SET_IF_INTEREST_LAYER(poHelipadPolygonLayer); + SET_IF_INTEREST_LAYER(poTaxiwayRectangleLayer); + SET_IF_INTEREST_LAYER(poPavementLayer); + SET_IF_INTEREST_LAYER(poAPTBoundaryLayer); + SET_IF_INTEREST_LAYER(poAPTLinearFeatureLayer); + SET_IF_INTEREST_LAYER(poATCFreqLayer); + SET_IF_INTEREST_LAYER(poStartupLocationLayer); + SET_IF_INTEREST_LAYER(poAPTLightBeaconLayer); + SET_IF_INTEREST_LAYER(poAPTWindsockLayer); + SET_IF_INTEREST_LAYER(poTaxiwaySignLayer); + SET_IF_INTEREST_LAYER(poVASI_PAPI_WIGWAG_Layer); + SET_IF_INTEREST_LAYER(poTaxiLocationLayer); + + if (pszFilename) + { + poReader->pszFilename = CPLStrdup(pszFilename); + poReader->fp = VSIFOpenL( pszFilename, "rb" ); + } + + return poReader; +} + +/************************************************************************/ +/* Rewind() */ +/************************************************************************/ + +void OGRXPlaneAptReader::Rewind() +{ + bAptHeaderFound = FALSE; + bTowerFound = FALSE; + dfLatTower = 0; + dfLonTower = 0; + dfHeightTower = 0; + bRunwayFound = FALSE; + dfLatFirstRwy = 0; + dfLonFirstRwy = 0; + nAPTType = -1; + + bResumeLine = FALSE; + + OGRXPlaneReader::Rewind(); +} + + +/************************************************************************/ +/* IsRecognizedVersion() */ +/************************************************************************/ + +int OGRXPlaneAptReader::IsRecognizedVersion( const char* pszVersionString) +{ + if (EQUALN(pszVersionString, "810 Version", 11)) + nVersion = APT_V_810; + else if (EQUALN(pszVersionString, "850 Version", 11)) + nVersion = APT_V_850; + else if (EQUALN(pszVersionString, "1000 Version", 12)) + nVersion = APT_V_1000; + else + nVersion = APT_V_UNKNOWN; + + if (nVersion == APT_V_1000) + { + if (poDataSource) + { + poTaxiLocationLayer = new OGRXPlaneTaxiLocationLayer(); + poDataSource->RegisterLayer(poTaxiLocationLayer); + } + } + + return nVersion != APT_V_UNKNOWN; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +void OGRXPlaneAptReader::Read() +{ + const char* pszLine = NULL; + + if (!bResumeLine) + { + CPLAssert(papszTokens == NULL); + } + + while(bResumeLine || (pszLine = CPLReadLineL(fp)) != NULL) + { + int nType; + if (!bResumeLine) + { + papszTokens = CSLTokenizeString(pszLine); + nTokens = CSLCount(papszTokens); + nLineNumber ++; + bResumeLine = FALSE; + } + + do + { + bResumeLine = FALSE; + + if (nTokens == 1 && strcmp(papszTokens[0], "99") == 0) + { + CSLDestroy(papszTokens); + papszTokens = NULL; + bEOF = TRUE; + if (bAptHeaderFound) + { + if (poAPTLayer) + { + poAPTLayer->AddFeature(osAptICAO, osAptName, nAPTType, dfElevation, + bTowerFound || bRunwayFound, + (bTowerFound) ? dfLatTower : dfLatFirstRwy, + (bTowerFound) ? dfLonTower : dfLonFirstRwy, + bTowerFound, dfHeightTower, osTowerName); + } + } + return; + } + else if (nTokens == 0 || assertMinCol(2) == FALSE) + { + break; + } + + nType = atoi(papszTokens[0]); + switch(nType) + { + case APT_AIRPORT_HEADER: + case APT_SEAPLANE_HEADER: + case APT_HELIPORT_HEADER: + if (bAptHeaderFound) + { + bAptHeaderFound = FALSE; + if (poAPTLayer) + { + poAPTLayer->AddFeature(osAptICAO, osAptName, nAPTType, dfElevation, + bTowerFound || bRunwayFound, + (bTowerFound) ? dfLatTower : dfLatFirstRwy, + (bTowerFound) ? dfLonTower : dfLonFirstRwy, + bTowerFound, dfHeightTower, osTowerName); + } + } + ParseAptHeaderRecord(); + nAPTType = nType; + + break; + + case APT_RUNWAY_TAXIWAY_V_810: + if (poAPTLayer || + poRunwayLayer || poRunwayThresholdLayer || + poStopwayLayer || + poHelipadLayer || poHelipadPolygonLayer || + poVASI_PAPI_WIGWAG_Layer || poTaxiwayRectangleLayer) + { + ParseRunwayTaxiwayV810Record(); + } + break; + + case APT_TOWER: + if (poAPTLayer) + ParseTowerRecord(); + break; + + case APT_STARTUP_LOCATION: + if (poStartupLocationLayer) + ParseStartupLocationRecord(); + break; + + case APT_LIGHT_BEACONS: + if (poAPTLightBeaconLayer) + ParseLightBeaconRecord(); + break; + + case APT_WINDSOCKS: + if (poAPTWindsockLayer) + ParseWindsockRecord(); + break; + + case APT_TAXIWAY_SIGNS: + if (poTaxiwaySignLayer) + ParseTaxiwaySignRecord(); + break; + + case APT_VASI_PAPI_WIGWAG: + if (poVASI_PAPI_WIGWAG_Layer) + ParseVasiPapiWigWagRecord(); + break; + + case APT_ATC_AWOS_ASOS_ATIS: + case APT_ATC_CTAF: + case APT_ATC_CLD: + case APT_ATC_GND: + case APT_ATC_TWR: + case APT_ATC_APP: + case APT_ATC_DEP: + if (poATCFreqLayer) + ParseATCRecord(nType); + break; + + case APT_RUNWAY: + if (poAPTLayer || poRunwayLayer || poRunwayThresholdLayer || poStopwayLayer) + ParseRunwayRecord(); + break; + + case APT_WATER_RUNWAY: + if (poWaterRunwayLayer || poWaterRunwayThresholdLayer) + ParseWaterRunwayRecord(); + break; + + case APT_HELIPAD: + if (poHelipadLayer || poHelipadPolygonLayer) + ParseHelipadRecord(); + break; + + case APT_PAVEMENT_HEADER: + if (poPavementLayer) + ParsePavement(); + break; + + case APT_LINEAR_HEADER: + if (poAPTLinearFeatureLayer) + ParseAPTLinearFeature(); + break; + + case APT_BOUNDARY_HEADER: + if (poAPTBoundaryLayer) + ParseAPTBoundary(); + break; + + case APT_TAXI_LOCATION: + if (poTaxiLocationLayer) + ParseTaxiLocation(); + break; + + default: + CPLDebug("XPLANE", "Line %d, Unknown code : %d", nLineNumber, nType); + break; + } + } while(bResumeLine); + + CSLDestroy(papszTokens); + papszTokens = NULL; + + if (poInterestLayer && poInterestLayer->IsEmpty() == FALSE) + return; + } + + bEOF = TRUE; +} + + +/************************************************************************/ +/* ParseAptHeaderRecord() */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseAptHeaderRecord() +{ + bAptHeaderFound = FALSE; + bTowerFound = FALSE; + bRunwayFound = FALSE; + + RET_IF_FAIL(assertMinCol(6)); + + /* feet to meter */ + RET_IF_FAIL(readDoubleWithBoundsAndConversion(&dfElevation, 1, "elevation", FEET_TO_METER, -1000., 10000.)); + bControlTower = atoi(papszTokens[2]); + // papszTokens[3] ignored + osAptICAO = papszTokens[4]; + osAptName = readStringUntilEnd(5); + + bAptHeaderFound = TRUE; +} + +/************************************************************************/ +/* ParseRunwayTaxiwayV810Record() */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseRunwayTaxiwayV810Record() +{ + double dfLat, dfLon, dfTrueHeading, dfLength, dfWidth; + double adfDisplacedThresholdLength[2]; + double adfStopwayLength[2]; + const char* pszRwyNum; + int /*aeVisualApproachLightingCode[2], */ aeRunwayLightingCode[2], aeApproachLightingCode[2]; + int eSurfaceCode, eShoulderCode, eMarkings; + double dfSmoothness; + double adfVisualGlidePathAngle[2]; + int bHasDistanceRemainingSigns; + + RET_IF_FAIL(assertMinCol(15)); + + RET_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + pszRwyNum = papszTokens[3]; + RET_IF_FAIL(readTrueHeading(&dfTrueHeading, 4)); + RET_IF_FAIL(readDouble(&dfLength, 5, "length")); + dfLength *= FEET_TO_METER; + adfDisplacedThresholdLength[0] = atoi(papszTokens[6]) * FEET_TO_METER; + if (strchr(papszTokens[6], '.') != NULL) + adfDisplacedThresholdLength[1] = atoi(strchr(papszTokens[6], '.') + 1) * FEET_TO_METER; + adfStopwayLength[0] = atoi(papszTokens[7]) * FEET_TO_METER; + if (strchr(papszTokens[7], '.') != NULL) + adfStopwayLength[1] = atoi(strchr(papszTokens[7], '.') + 1) * FEET_TO_METER; + RET_IF_FAIL(readDouble(&dfWidth, 8, "width")); + dfWidth *= FEET_TO_METER; + if (strlen(papszTokens[9]) == 6) + { + /*aeVisualApproachLightingCode[0] = papszTokens[9][0] - '0'; */ + aeRunwayLightingCode[0] = papszTokens[9][1] - '0'; + aeApproachLightingCode[0] = papszTokens[9][2] - '0'; + /* aeVisualApproachLightingCode[1] = papszTokens[9][3] - '0'; */ + aeRunwayLightingCode[1] = papszTokens[9][4] - '0'; + aeApproachLightingCode[1] = papszTokens[9][5] - '0'; + } + else + { + aeRunwayLightingCode[0] = 0; + aeApproachLightingCode[0] = 0; + aeRunwayLightingCode[1] = 0; + aeApproachLightingCode[1] = 0; + } + eSurfaceCode = atoi(papszTokens[10]); + eShoulderCode = atoi(papszTokens[11]); + eMarkings = atoi(papszTokens[12]); + RET_IF_FAIL(readDoubleWithBounds(&dfSmoothness, 13, "runway smoothness", 0., 1.)); + bHasDistanceRemainingSigns = atoi(papszTokens[14]); + if (nTokens == 16) + { + adfVisualGlidePathAngle[0] = atoi(papszTokens[15]) / 100.; + if (strchr(papszTokens[15], '.') != NULL) + adfVisualGlidePathAngle[1] = atoi(strchr(papszTokens[15], '.') + 1) / 100.; + else + adfVisualGlidePathAngle[1] = 0; + } + else + { + adfVisualGlidePathAngle[0] = 0; + adfVisualGlidePathAngle[1] = 0; + } + + if (strcmp(pszRwyNum, "xxx") == 000) + { + /* Taxiway */ + if (poTaxiwayRectangleLayer) + poTaxiwayRectangleLayer->AddFeature(osAptICAO, dfLat, dfLon, + dfTrueHeading, dfLength, dfWidth, + RunwaySurfaceEnumeration.GetText(eSurfaceCode), + dfSmoothness, aeRunwayLightingCode[0] == 1); + } + else if (pszRwyNum[0] >= '0' && pszRwyNum[0] <= '9' && strlen(pszRwyNum) >= 2) + { + /* Runway */ + double adfLat[2], adfLon[2]; + CPLString aosRwyNum[2]; + OGRFeature* poFeature; + int abReil[2]; + + int num1 = atoi(pszRwyNum); + int num2 = (num1 > 18) ? num1 - 18 : num1 + 18; + if (pszRwyNum[2] == '0' || pszRwyNum[2] == 'x') + { + aosRwyNum[0].Printf("%02d", num1); + aosRwyNum[1].Printf("%02d", num2); + } + else + { + aosRwyNum[0] = pszRwyNum; + aosRwyNum[1].Printf("%02d%c", num2, + (aosRwyNum[0][2] == 'L') ? 'R' : + (aosRwyNum[0][2] == 'R') ? 'L' : aosRwyNum[0][2]); + } + + OGRXPlane_ExtendPosition(dfLat, dfLon, dfLength / 2, dfTrueHeading + 180, &adfLat[0], &adfLon[0]); + OGRXPlane_ExtendPosition(dfLat, dfLon, dfLength / 2, dfTrueHeading, &adfLat[1], &adfLon[1]); + + for(int i=0;i<2;i++) + abReil[i] = (aeRunwayLightingCode[i] >= 3 && aeRunwayLightingCode[i] <= 5) ; + + if (!bRunwayFound) + { + dfLatFirstRwy = adfLat[0]; + dfLonFirstRwy = adfLon[0]; + bRunwayFound = TRUE; + } + + if (nAPTType == APT_SEAPLANE_HEADER || eSurfaceCode == 13) + { + /* Special case for water-runways. No special record in V8.10 */ + OGRFeature* apoWaterRunwayThreshold[2] = {NULL, NULL}; + int bBuoys; + int i; + + bBuoys = TRUE; + + for(i=0;i<2;i++) + { + if (poWaterRunwayThresholdLayer) + { + apoWaterRunwayThreshold[i] = + poWaterRunwayThresholdLayer->AddFeature + (osAptICAO, aosRwyNum[i], adfLat[i], adfLon[i], dfWidth, bBuoys); + } + + } + + if (poWaterRunwayThresholdLayer) + { + poWaterRunwayThresholdLayer->SetRunwayLengthAndHeading(apoWaterRunwayThreshold[0], dfLength, + OGRXPlane_Track(adfLat[0], adfLon[0], adfLat[1], adfLon[1])); + poWaterRunwayThresholdLayer->SetRunwayLengthAndHeading(apoWaterRunwayThreshold[1], dfLength, + OGRXPlane_Track(adfLat[1], adfLon[1], adfLat[0], adfLon[0])); + } + + if (poWaterRunwayLayer) + { + poWaterRunwayLayer->AddFeature(osAptICAO, aosRwyNum[0], aosRwyNum[1], + adfLat[0], adfLon[0], adfLat[1], adfLon[1], + dfWidth, bBuoys); + } + } + else + { + if (poRunwayThresholdLayer) + { + for(int i=0;i<2;i++) + { + poFeature = + poRunwayThresholdLayer->AddFeature + (osAptICAO, aosRwyNum[i], + adfLat[i], adfLon[i], dfWidth, + RunwaySurfaceEnumeration.GetText(eSurfaceCode), + RunwayShoulderEnumeration.GetText(eShoulderCode), + dfSmoothness, + (aeRunwayLightingCode[i] == 4 || aeRunwayLightingCode[i] == 5) /* bHasCenterLineLights */, + (aeRunwayLightingCode[i] >= 2 && aeRunwayLightingCode[i] <= 5) ? "Yes" : "None" /* pszEdgeLighting */, + bHasDistanceRemainingSigns, + adfDisplacedThresholdLength[i], adfStopwayLength[i], + RunwayMarkingEnumeration.GetText(eMarkings), + RunwayApproachLightingEnumerationV810.GetText(aeApproachLightingCode[i]), + (aeRunwayLightingCode[i] == 5) /* bHasTouchdownLights */, + (abReil[i] && abReil[i]) ? "Omni-directional" : + (abReil[i] && !abReil[1-i]) ? "Unidirectional" : "None" /* eReil */); + poRunwayThresholdLayer->SetRunwayLengthAndHeading(poFeature, dfLength, + (i == 0) ? dfTrueHeading : (dfTrueHeading < 180) ? dfTrueHeading + 180 : dfTrueHeading - 180); + if (adfDisplacedThresholdLength[i] != 0) + poRunwayThresholdLayer->AddFeatureFromNonDisplacedThreshold(poFeature); + } + } + + if (poRunwayLayer) + { + poRunwayLayer->AddFeature(osAptICAO, aosRwyNum[0], aosRwyNum[1], + adfLat[0], adfLon[0], adfLat[1], adfLon[1], + dfWidth, + RunwaySurfaceEnumeration.GetText(eSurfaceCode), + RunwayShoulderEnumeration.GetText(eShoulderCode), + dfSmoothness, + (aeRunwayLightingCode[0] == 4 || aeRunwayLightingCode[0] == 5), + (aeRunwayLightingCode[0] >= 2 && aeRunwayLightingCode[0] <= 5) ? "Yes" : "None" /* pszEdgeLighting */, + bHasDistanceRemainingSigns); + } + + if (poStopwayLayer) + { + for(int i=0;i<2;i++) + { + if (adfStopwayLength[i] != 0) + { + double dfHeading = OGRXPlane_Track(adfLat[i], adfLon[i], + adfLat[1-i], adfLon[1-i]); + poStopwayLayer->AddFeature(osAptICAO, aosRwyNum[i], + adfLat[i], adfLon[i], dfHeading, dfWidth, adfStopwayLength[i]); + } + } + } + + if (poVASI_PAPI_WIGWAG_Layer) + { + for(int i=0;i<2;i++) + { + if (aeApproachLightingCode[i]) + poVASI_PAPI_WIGWAG_Layer->AddFeature(osAptICAO, aosRwyNum[i], + RunwayVisualApproachPathIndicatorEnumerationV810.GetText(aeApproachLightingCode[i]), + adfLat[i], adfLon[i], + (i == 0) ? dfTrueHeading : (dfTrueHeading < 180) ? dfTrueHeading + 180 : dfTrueHeading- 180, + adfVisualGlidePathAngle[i]); + } + } + } + } + else if (pszRwyNum[0] == 'H') + { + /* Helipads can belong to regular airports or heliports */ + CPLString osHelipadName = pszRwyNum; + if (strlen(pszRwyNum) == 3 && pszRwyNum[2] == 'x') + osHelipadName[2] = 0; + + if (!bRunwayFound) + { + dfLatFirstRwy = dfLat; + dfLonFirstRwy = dfLon; + bRunwayFound = TRUE; + } + + if (poHelipadLayer) + { + poHelipadLayer->AddFeature(osAptICAO, osHelipadName, dfLat, dfLon, + dfTrueHeading, dfLength, dfWidth, + RunwaySurfaceEnumeration.GetText(eSurfaceCode), + RunwayMarkingEnumeration.GetText(eMarkings), + RunwayShoulderEnumeration.GetText(eShoulderCode), + dfSmoothness, + (aeRunwayLightingCode[0] >= 2 && aeRunwayLightingCode[0] <= 5) ? "Yes" : "None" /* pszEdgeLighting */); + } + + if (poHelipadPolygonLayer) + { + poHelipadPolygonLayer->AddFeature(osAptICAO, osHelipadName, dfLat, dfLon, + dfTrueHeading, dfLength, dfWidth, + RunwaySurfaceEnumeration.GetText(eSurfaceCode), + RunwayMarkingEnumeration.GetText(eMarkings), + RunwayShoulderEnumeration.GetText(eShoulderCode), + dfSmoothness, + (aeRunwayLightingCode[0] >= 2 && aeRunwayLightingCode[0] <= 5) ? "Yes" : "None" /* pszEdgeLighting */); + } + } + else + { + CPLDebug("XPlane", "Line %d : Unexpected runway number : %s", + nLineNumber, pszRwyNum); + } + +} + +/************************************************************************/ +/* ParseRunwayRecord() */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseRunwayRecord() +{ + double dfWidth; + int eSurfaceCode, eShoulderCode; + double dfSmoothness; + int bHasCenterLineLights, bHasDistanceRemainingSigns; + int eEdgeLighting; + int nCurToken; + int nRwy = 0; + double adfLat[2], adfLon[2]; + OGRFeature* apoRunwayThreshold[2] = { NULL, NULL }; + double dfLength; + CPLString aosRunwayId[2]; + double adfDisplacedThresholdLength[2]; + double adfStopwayLength[2]; + + RET_IF_FAIL(assertMinCol(8 + 9 + 9)); + + RET_IF_FAIL(readDouble(&dfWidth, 1, "runway width")); + eSurfaceCode = atoi(papszTokens[2]); + eShoulderCode = atoi(papszTokens[3]); + RET_IF_FAIL(readDoubleWithBounds(&dfSmoothness, 4, "runway smoothness", 0., 1.)); + bHasCenterLineLights = atoi(papszTokens[5]); + eEdgeLighting = atoi(papszTokens[6]); + bHasDistanceRemainingSigns = atoi(papszTokens[7]); + + for( nRwy=0, nCurToken = 8 ; nRwy<=1 ; nRwy++, nCurToken += 9 ) + { + double dfLat, dfLon; + int eMarkings, eApproachLightingCode, eREIL; + int bHasTouchdownLights; + + aosRunwayId[nRwy] = papszTokens[nCurToken + 0]; /* for example : 08, 24R, or xxx */ + RET_IF_FAIL(readLatLon(&dfLat, &dfLon, nCurToken + 1)); + adfLat[nRwy] = dfLat; + adfLon[nRwy] = dfLon; + RET_IF_FAIL(readDouble(&adfDisplacedThresholdLength[nRwy], nCurToken + 3, "displaced threshold length")); + RET_IF_FAIL(readDouble(&adfStopwayLength[nRwy], nCurToken + 4, "stopway/blastpad/over-run length")); + eMarkings = atoi(papszTokens[nCurToken + 5]); + eApproachLightingCode = atoi(papszTokens[nCurToken + 6]); + bHasTouchdownLights = atoi(papszTokens[nCurToken + 7]); + eREIL = atoi(papszTokens[nCurToken + 8]); + + if (!bRunwayFound) + { + dfLatFirstRwy = dfLat; + dfLonFirstRwy = dfLon; + bRunwayFound = TRUE; + } + + if (poRunwayThresholdLayer) + { + apoRunwayThreshold[nRwy] = + poRunwayThresholdLayer->AddFeature + (osAptICAO, aosRunwayId[nRwy], + dfLat, dfLon, dfWidth, + RunwaySurfaceEnumeration.GetText(eSurfaceCode), + RunwayShoulderEnumeration.GetText(eShoulderCode), + dfSmoothness, bHasCenterLineLights, + RunwayEdgeLightingEnumeration.GetText(eEdgeLighting), bHasDistanceRemainingSigns, + adfDisplacedThresholdLength[nRwy], adfStopwayLength[nRwy], + RunwayMarkingEnumeration.GetText(eMarkings), + RunwayApproachLightingEnumeration.GetText(eApproachLightingCode), + bHasTouchdownLights, + RunwayREILEnumeration.GetText(eREIL)); + } + } + + dfLength = OGRXPlane_Distance(adfLat[0], adfLon[0], adfLat[1], adfLon[1]); + if (poRunwayThresholdLayer) + { + poRunwayThresholdLayer->SetRunwayLengthAndHeading(apoRunwayThreshold[0], dfLength, + OGRXPlane_Track(adfLat[0], adfLon[0], adfLat[1], adfLon[1])); + poRunwayThresholdLayer->SetRunwayLengthAndHeading(apoRunwayThreshold[1], dfLength, + OGRXPlane_Track(adfLat[1], adfLon[1], adfLat[0], adfLon[0])); + if (adfDisplacedThresholdLength[0] != 0) + poRunwayThresholdLayer->AddFeatureFromNonDisplacedThreshold(apoRunwayThreshold[0]); + if (adfDisplacedThresholdLength[1] != 0) + poRunwayThresholdLayer->AddFeatureFromNonDisplacedThreshold(apoRunwayThreshold[1]); + } + + if (poRunwayLayer) + { + poRunwayLayer->AddFeature(osAptICAO, aosRunwayId[0], aosRunwayId[1], + adfLat[0], adfLon[0], adfLat[1], adfLon[1], + dfWidth, + RunwaySurfaceEnumeration.GetText(eSurfaceCode), + RunwayShoulderEnumeration.GetText(eShoulderCode), + dfSmoothness, bHasCenterLineLights, + RunwayEdgeLightingEnumeration.GetText(eEdgeLighting), + bHasDistanceRemainingSigns); + } + + if (poStopwayLayer) + { + for(int i=0;i<2;i++) + { + if (adfStopwayLength[i] != 0) + { + double dfHeading = OGRXPlane_Track(adfLat[i], adfLon[i], + adfLat[1-i], adfLon[1-i]); + poStopwayLayer->AddFeature(osAptICAO, aosRunwayId[i], + adfLat[i], adfLon[i], dfHeading, dfWidth, adfStopwayLength[i]); + } + } + } +} + +/************************************************************************/ +/* ParseWaterRunwayRecord() */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseWaterRunwayRecord() +{ + double adfLat[2], adfLon[2]; + OGRFeature* apoWaterRunwayThreshold[2] = {NULL, NULL}; + double dfWidth, dfLength; + int bBuoys; + CPLString aosRunwayId[2]; + int i; + + RET_IF_FAIL(assertMinCol(9)); + + RET_IF_FAIL(readDouble(&dfWidth, 1, "runway width")); + bBuoys = atoi(papszTokens[2]); + + for(i=0;i<2;i++) + { + aosRunwayId[i] = papszTokens[3 + 3*i]; + RET_IF_FAIL(readLatLon(&adfLat[i], &adfLon[i], 4 + 3*i)); + + if (poWaterRunwayThresholdLayer) + { + apoWaterRunwayThreshold[i] = + poWaterRunwayThresholdLayer->AddFeature + (osAptICAO, aosRunwayId[i], adfLat[i], adfLon[i], dfWidth, bBuoys); + } + + } + + dfLength = OGRXPlane_Distance(adfLat[0], adfLon[0], adfLat[1], adfLon[1]); + + if (poWaterRunwayThresholdLayer) + { + poWaterRunwayThresholdLayer->SetRunwayLengthAndHeading(apoWaterRunwayThreshold[0], dfLength, + OGRXPlane_Track(adfLat[0], adfLon[0], adfLat[1], adfLon[1])); + poWaterRunwayThresholdLayer->SetRunwayLengthAndHeading(apoWaterRunwayThreshold[1], dfLength, + OGRXPlane_Track(adfLat[1], adfLon[1], adfLat[0], adfLon[0])); + } + + if (poWaterRunwayLayer) + { + poWaterRunwayLayer->AddFeature(osAptICAO, aosRunwayId[0], aosRunwayId[1], + adfLat[0], adfLon[0], adfLat[1], adfLon[1], + dfWidth, bBuoys); + } +} + +/************************************************************************/ +/* ParseHelipadRecord() */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseHelipadRecord() +{ + double dfLat, dfLon, dfTrueHeading, dfLength, dfWidth, dfSmoothness; + int eSurfaceCode, eMarkings, eShoulderCode, eEdgeLighting; + const char* pszHelipadName; + + RET_IF_FAIL(assertMinCol(12)); + + pszHelipadName = papszTokens[1]; + RET_IF_FAIL(readLatLon(&dfLat, &dfLon, 2)); + RET_IF_FAIL(readTrueHeading(&dfTrueHeading, 4)); + RET_IF_FAIL(readDouble(&dfLength, 5, "length")); + RET_IF_FAIL(readDouble(&dfWidth, 6, "width")); + eSurfaceCode = atoi(papszTokens[7]); + eMarkings = atoi(papszTokens[8]); + eShoulderCode = atoi(papszTokens[9]); + RET_IF_FAIL(readDoubleWithBounds(&dfSmoothness, 10, "helipad smoothness", 0., 1.)); + eEdgeLighting = atoi(papszTokens[11]); + + if (poHelipadLayer) + { + poHelipadLayer->AddFeature(osAptICAO, pszHelipadName, dfLat, dfLon, + dfTrueHeading, dfLength, dfWidth, + RunwaySurfaceEnumeration.GetText(eSurfaceCode), + RunwayMarkingEnumeration.GetText(eMarkings), + RunwayShoulderEnumeration.GetText(eShoulderCode), + dfSmoothness, + HelipadEdgeLightingEnumeration.GetText(eEdgeLighting)); + } + + if (poHelipadPolygonLayer) + { + poHelipadPolygonLayer->AddFeature(osAptICAO, pszHelipadName, dfLat, dfLon, + dfTrueHeading, dfLength, dfWidth, + RunwaySurfaceEnumeration.GetText(eSurfaceCode), + RunwayMarkingEnumeration.GetText(eMarkings), + RunwayShoulderEnumeration.GetText(eShoulderCode), + dfSmoothness, + HelipadEdgeLightingEnumeration.GetText(eEdgeLighting)); + } +} + + + +/************************************************************************/ +/* AddBezierCurve() */ +/************************************************************************/ + +#define CUBIC_INTERPOL(A, B, C, D) ((A)*(b*b*b) + 3*(B)*(b*b)*a + 3 *(C)*b*(a*a) + (D)*(a*a*a)) + +void OGRXPlaneAptReader::AddBezierCurve(OGRLineString& lineString, + double dfLatA, double dfLonA, + double dfCtrPtLatA, double dfCtrPtLonA, + double dfSymCtrlPtLatB, double dfSymCtrlPtLonB, + double dfLatB, double dfLonB) +{ + int step; + for(step = 0; step <= 10; step++) + { + double a = step / 10.; + double b = 1. - a; + double dfCtrlPtLonB = dfLonB - (dfSymCtrlPtLonB - dfLonB); + double dfCtrlPtLatB = dfLatB - (dfSymCtrlPtLatB - dfLatB); + lineString.addPoint(CUBIC_INTERPOL(dfLonA, dfCtrPtLonA, dfCtrlPtLonB, dfLonB), + CUBIC_INTERPOL(dfLatA, dfCtrPtLatA, dfCtrlPtLatB, dfLatB)); + } +} + + +#define QUADRATIC_INTERPOL(A, B, C) ((A)*(b*b) + 2*(B)*b*a + (C)*(a*a)) + +void OGRXPlaneAptReader::AddBezierCurve(OGRLineString& lineString, + double dfLatA, double dfLonA, + double dfCtrPtLat, double dfCtrPtLon, + double dfLatB, double dfLonB) +{ + int step; + for(step = 0; step <= 10; step++) + { + double a = step / 10.; + double b = 1. - a; + lineString.addPoint(QUADRATIC_INTERPOL(dfLonA, dfCtrPtLon, dfLonB), + QUADRATIC_INTERPOL(dfLatA, dfCtrPtLat, dfLatB)); + } +} + +static OGRGeometry* OGRXPlaneAptReaderSplitPolygon(OGRPolygon& polygon) +{ + OGRPolygon** papoPolygons = new OGRPolygon* [1 + polygon.getNumInteriorRings()]; + + papoPolygons[0] = new OGRPolygon(); + papoPolygons[0]->addRing(polygon.getExteriorRing()); + for(int i=0;iaddRing(polygon.getInteriorRing(i)); + } + + int bIsValid; + OGRGeometry* poGeom; + poGeom = OGRGeometryFactory::organizePolygons((OGRGeometry**)papoPolygons, + 1 + polygon.getNumInteriorRings(), + &bIsValid, NULL); + + delete[] papoPolygons; + + return poGeom; +} + +/************************************************************************/ +/* FixPolygonTopology() */ +/************************************************************************/ + +/* +Intended to fix several topological problems, like when a point of an interior ring +is on the edge of the external ring, or other topological anomalies. +*/ + +OGRGeometry* OGRXPlaneAptReader::FixPolygonTopology(OGRPolygon& polygon) +{ + OGRPolygon* poPolygon = &polygon; + OGRPolygon* poPolygonTemp = NULL; + OGRLinearRing* poExternalRing = poPolygon->getExteriorRing(); + if (poExternalRing->getNumPoints() < 4) + { + CPLDebug("XPLANE", "Discarded degenerated polygon at line %d", nLineNumber); + return NULL; + } + + for(int i=0;igetNumInteriorRings();i++) + { + OGRLinearRing* poInternalRing = poPolygon->getInteriorRing(i); + if (poInternalRing->getNumPoints() < 4) + { + CPLDebug("XPLANE", "Discarded degenerated interior ring (%d) at line %d", i, nLineNumber); + OGRPolygon* poPolygon2 = new OGRPolygon(); + poPolygon2->addRing(poExternalRing); + for(int j=0;jgetNumInteriorRings();j++) + { + if (i != j) + poPolygon2->addRing(poPolygon->getInteriorRing(j)); + } + delete poPolygonTemp; + poPolygon = poPolygonTemp = poPolygon2; + i --; + continue; + } + + int nOutside = 0; + int jOutside = -1; + for(int j=0;jgetNumPoints();j++) + { + OGRPoint pt; + poInternalRing->getPoint(j, &pt); + if (poExternalRing->isPointInRing(&pt) == FALSE) + { + nOutside++; + jOutside = j; + } + } + + if (nOutside == 1) + { + int j = jOutside; + OGRPoint pt; + poInternalRing->getPoint(j, &pt); + OGRPoint newPt; + int bSuccess = FALSE; + for(int k=-1;k<=1 && !bSuccess;k+=2) + { + for(int l=-1;l<=1 && !bSuccess;l+=2) + { + newPt.setX(pt.getX() + k * 1e-7); + newPt.setY(pt.getY() + l * 1e-7); + if (poExternalRing->isPointInRing(&newPt)) + { + poInternalRing->setPoint(j, newPt.getX(), newPt.getY()); + bSuccess = TRUE; + } + } + } + if (!bSuccess) + { + CPLDebug("XPLANE", + "Didn't manage to fix polygon topology at line %d", nLineNumber); + + /* Invalid topology. Will split into several pieces */ + OGRGeometry* poRet = OGRXPlaneAptReaderSplitPolygon(*poPolygon); + delete poPolygonTemp; + return poRet; + } + } + else + { + /* Two parts. Or other strange cases */ + OGRGeometry* poRet = OGRXPlaneAptReaderSplitPolygon(*poPolygon); + delete poPolygonTemp; + return poRet; + } + } + + /* The geometry is right */ + OGRGeometry* poRet = poPolygon->clone(); + delete poPolygonTemp; + return poRet; +} + +/************************************************************************/ +/* ParsePolygonalGeometry() */ +/************************************************************************/ + +/* This function will eat records until the end of the polygon */ +/* Return TRUE if the main parser must re-scan the current record */ + +#define RET_FALSE_IF_FAIL(x) if (!(x)) return FALSE; + +int OGRXPlaneAptReader::ParsePolygonalGeometry(OGRGeometry** ppoGeom) +{ + double dfLat, dfLon; + double dfFirstLat = 0., dfFirstLon = 0.; + double dfLastLat = 0., dfLastLon = 0.; + double dfLatBezier = 0., dfLonBezier = 0.; + double dfFirstLatBezier = 0., dfFirstLonBezier = 0.; + double dfLastLatBezier = 0., dfLastLonBezier = 0.; + int bIsFirst = TRUE; + int bFirstIsBezier = TRUE; + /* int bLastIsValid = FALSE; */ + int bLastIsBezier = FALSE; + int bLastPartIsClosed = FALSE; + const char* pszLine; + OGRPolygon polygon; + + OGRLinearRing linearRing; + + *ppoGeom = NULL; + + while((pszLine = CPLReadLineL(fp)) != NULL) + { + int nType = -1; + papszTokens = CSLTokenizeString(pszLine); + nTokens = CSLCount(papszTokens); + + nLineNumber ++; + + if (nTokens == 1 && strcmp(papszTokens[0], "99") == 0) + { + if (bLastPartIsClosed == FALSE) + { + CPLDebug("XPlane", "Line %d : Unexpected token when reading a polygon : %d", + nLineNumber, nType); + } + else + { + *ppoGeom = FixPolygonTopology(polygon); + } + + return TRUE; + } + if (nTokens == 0 || assertMinCol(2) == FALSE) + { + CSLDestroy(papszTokens); + continue; + } + + nType = atoi(papszTokens[0]); + if (nType == APT_NODE) + { + RET_FALSE_IF_FAIL(assertMinCol(3)); + RET_FALSE_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + + if (bLastIsBezier && !bIsFirst && + !(dfLastLat == dfLat && dfLastLon == dfLon)) + { + AddBezierCurve(linearRing, + dfLastLat, dfLastLon, + dfLastLatBezier, dfLastLonBezier, + dfLat, dfLon); + } + else + linearRing.addPoint(dfLon, dfLat); + + bLastPartIsClosed = FALSE; + bLastIsBezier = FALSE; + } + else if (nType == APT_NODE_WITH_BEZIER) + { + RET_FALSE_IF_FAIL(assertMinCol(5)); + RET_FALSE_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + RET_FALSE_IF_FAIL(readLatLon(&dfLatBezier, &dfLonBezier, 3)); + + if (bLastIsBezier) + { + AddBezierCurve(linearRing, + dfLastLat, dfLastLon, + dfLastLatBezier, dfLastLonBezier, + dfLatBezier, dfLonBezier, + dfLat, dfLon); + } + else if (!bIsFirst && !(dfLastLat == dfLat && dfLastLon == dfLon)) + { + double dfCtrLatBezier = dfLat - (dfLatBezier - dfLat); + double dfCtrLonBezier = dfLon - (dfLonBezier - dfLon); + AddBezierCurve(linearRing, + dfLastLat, dfLastLon, + dfCtrLatBezier, dfCtrLonBezier, + dfLat, dfLon); + } + + bLastPartIsClosed = FALSE; + bLastIsBezier = TRUE; + dfLastLatBezier = dfLatBezier; + dfLastLonBezier = dfLonBezier; + } + else if (nType == APT_NODE_CLOSE) + { + RET_FALSE_IF_FAIL(assertMinCol(3)); + RET_FALSE_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + if (bIsFirst) + { + CPLDebug("XPlane", "Line %d : Unexpected token when reading a polygon : %d", + nLineNumber, nType); + return TRUE; + } + + if (bLastIsBezier && !bIsFirst && + !(dfLastLat == dfLat && dfLastLon == dfLon)) + { + AddBezierCurve(linearRing, + dfLastLat, dfLastLon, + dfLastLatBezier, dfLastLonBezier, + dfLat, dfLon); + } + else + linearRing.addPoint(dfLon, dfLat); + + linearRing.closeRings(); + + polygon.addRing(&linearRing); + linearRing.empty(); + + bLastPartIsClosed = TRUE; + bLastIsBezier = FALSE; + } + else if (nType == APT_NODE_CLOSE_WITH_BEZIER) + { + RET_FALSE_IF_FAIL(assertMinCol(5)); + RET_FALSE_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + RET_FALSE_IF_FAIL(readLatLon(&dfLatBezier, &dfLonBezier, 3)); + if (bIsFirst) + { + CPLDebug("XPlane", "Line %d : Unexpected token when reading a polygon : %d", + nLineNumber, nType); + return TRUE; + } + + if (bLastIsBezier) + { + AddBezierCurve(linearRing, + dfLastLat, dfLastLon, + dfLastLatBezier, dfLastLonBezier, + dfLatBezier, dfLonBezier, + dfLat, dfLon); + } + else if (!bIsFirst && !(dfLastLat == dfLat && dfLastLon == dfLon)) + { + double dfCtrLatBezier = dfLat - (dfLatBezier - dfLat); + double dfCtrLonBezier = dfLon - (dfLonBezier - dfLon); + AddBezierCurve(linearRing, + dfLastLat, dfLastLon, + dfCtrLatBezier, dfCtrLonBezier, + dfLat, dfLon); + } + else + { + linearRing.addPoint(dfLon, dfLat); + } + + if (bFirstIsBezier) + { + AddBezierCurve(linearRing, + dfLat, dfLon, + dfLatBezier, dfLonBezier, + dfFirstLatBezier, dfFirstLonBezier, + dfFirstLat, dfFirstLon); + } + else + { + linearRing.closeRings(); + } + + polygon.addRing(&linearRing); + linearRing.empty(); + + bLastPartIsClosed = TRUE; + bLastIsBezier = FALSE; /* we don't want to draw an arc between two parts */ + } + else + { + if (nType == APT_NODE_END || nType == APT_NODE_END_WITH_BEZIER || + bLastPartIsClosed == FALSE) + { + CPLDebug("XPlane", "Line %d : Unexpected token when reading a polygon : %d", + nLineNumber, nType); + } + else + { + *ppoGeom = FixPolygonTopology(polygon); + } + + return TRUE; + } + + if (bIsFirst) + { + dfFirstLat = dfLat; + dfFirstLon = dfLon; + dfFirstLatBezier = dfLatBezier; + dfFirstLonBezier = dfLonBezier; + bFirstIsBezier = bLastIsBezier; + } + bIsFirst = bLastPartIsClosed; + + dfLastLat = dfLat; + dfLastLon = dfLon; + /* bLastIsValid = TRUE; */ + + CSLDestroy(papszTokens); + } + + CPLAssert(0); + + papszTokens = NULL; + + return FALSE; +} + + +/************************************************************************/ +/* ParsePavement() */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParsePavement() +{ + int eSurfaceCode; + double dfSmoothness, dfTextureHeading; + CPLString osPavementName; + + RET_IF_FAIL(assertMinCol(4)); + + eSurfaceCode = atoi(papszTokens[1]); + + RET_IF_FAIL(readDoubleWithBounds(&dfSmoothness, 2, "pavement smoothness", 0., 1.)); + + RET_IF_FAIL(readTrueHeading(&dfTextureHeading, 3, "texture heading")); + + osPavementName = readStringUntilEnd(4); + + CSLDestroy(papszTokens); + papszTokens = NULL; + + OGRGeometry* poGeom; + bResumeLine = ParsePolygonalGeometry(&poGeom); + if (poGeom != NULL && poPavementLayer) + { + if (poGeom->getGeometryType() == wkbPolygon) + { + poPavementLayer->AddFeature(osAptICAO, osPavementName, + RunwaySurfaceEnumeration.GetText(eSurfaceCode), + dfSmoothness, dfTextureHeading, + (OGRPolygon*)poGeom); + } + else + { + OGRGeometryCollection* poGeomCollection = (OGRGeometryCollection*)poGeom; + for(int i=0;igetNumGeometries();i++) + { + OGRGeometry* poSubGeom = poGeomCollection->getGeometryRef(i); + if (poSubGeom->getGeometryType() == wkbPolygon && + ((OGRPolygon*)poSubGeom)->getExteriorRing()->getNumPoints() >= 4) + { + poPavementLayer->AddFeature(osAptICAO, osPavementName, + RunwaySurfaceEnumeration.GetText(eSurfaceCode), + dfSmoothness, dfTextureHeading, + (OGRPolygon*)poSubGeom); + } + } + } + } + if (poGeom != NULL) + delete poGeom; +} + +/************************************************************************/ +/* ParseAPTBoundary() */ +/************************************************************************/ + +/* This function will eat records until the end of the boundary definition */ +/* Return TRUE if the main parser must re-scan the current record */ + +void OGRXPlaneAptReader::ParseAPTBoundary() +{ + CPLString osBoundaryName; + + RET_IF_FAIL(assertMinCol(2)); + + osBoundaryName = readStringUntilEnd(2); + + CSLDestroy(papszTokens); + papszTokens = NULL; + + OGRGeometry* poGeom; + bResumeLine = ParsePolygonalGeometry(&poGeom); + if (poGeom != NULL && poAPTBoundaryLayer) + { + if (poGeom->getGeometryType() == wkbPolygon) + { + poAPTBoundaryLayer->AddFeature(osAptICAO, osBoundaryName, + (OGRPolygon*)poGeom); + } + else + { + OGRGeometryCollection* poGeomCollection = (OGRGeometryCollection*)poGeom; + for(int i=0;igetNumGeometries();i++) + { + OGRGeometry* poSubGeom = poGeomCollection->getGeometryRef(i); + if (poSubGeom->getGeometryType() == wkbPolygon && + ((OGRPolygon*)poSubGeom)->getExteriorRing()->getNumPoints() >= 4) + { + poAPTBoundaryLayer->AddFeature(osAptICAO, osBoundaryName, + (OGRPolygon*)poSubGeom); + } + } + } + } + if (poGeom != NULL) + delete poGeom; +} + + +/************************************************************************/ +/* ParseLinearGeometry() */ +/************************************************************************/ + +/* This function will eat records until the end of the multilinestring */ +/* Return TRUE if the main parser must re-scan the current record */ + +int OGRXPlaneAptReader::ParseLinearGeometry(OGRMultiLineString& multilinestring, int* pbIsValid) +{ + double dfLat, dfLon; + double dfFirstLat = 0., dfFirstLon = 0.; + double dfLastLat = 0., dfLastLon = 0.; + double dfLatBezier = 0., dfLonBezier = 0.; + double dfFirstLatBezier = 0., dfFirstLonBezier = 0.; + double dfLastLatBezier = 0., dfLastLonBezier = 0.; + int bIsFirst = TRUE; + int bFirstIsBezier = TRUE; + /* int bLastIsValid = FALSE; */ + int bLastIsBezier = FALSE; + int bLastPartIsClosedOrEnded = FALSE; + const char* pszLine; + + OGRLineString lineString; + + while((pszLine = CPLReadLineL(fp)) != NULL) + { + int nType = -1; + papszTokens = CSLTokenizeString(pszLine); + nTokens = CSLCount(papszTokens); + + nLineNumber ++; + + if (nTokens == 1 && strcmp(papszTokens[0], "99") == 0) + { + if (bLastPartIsClosedOrEnded == FALSE) + { + CPLDebug("XPlane", "Line %d : Unexpected token when reading a linear feature : %d", + nLineNumber, nType); + } + else if (multilinestring.getNumGeometries() == 0) + { + CPLDebug("XPlane", "Line %d : Linear geometry is invalid or empty", + nLineNumber); + } + else + { + *pbIsValid = TRUE; + } + return TRUE; + } + if (nTokens == 0 || assertMinCol(2) == FALSE) + { + CSLDestroy(papszTokens); + continue; + } + + nType = atoi(papszTokens[0]); + if (nType == APT_NODE) + { + RET_FALSE_IF_FAIL(assertMinCol(3)); + RET_FALSE_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + + if (bLastIsBezier && !bIsFirst && + !(dfLastLat == dfLat && dfLastLon == dfLon)) + { + AddBezierCurve(lineString, + dfLastLat, dfLastLon, + dfLastLatBezier, dfLastLonBezier, + dfLat, dfLon); + } + else + lineString.addPoint(dfLon, dfLat); + + bLastPartIsClosedOrEnded = FALSE; + bLastIsBezier = FALSE; + } + else if (nType == APT_NODE_WITH_BEZIER) + { + RET_FALSE_IF_FAIL(assertMinCol(5)); + RET_FALSE_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + RET_FALSE_IF_FAIL(readLatLon(&dfLatBezier, &dfLonBezier, 3)); + + if (bLastIsBezier) + { + AddBezierCurve(lineString, + dfLastLat, dfLastLon, + dfLastLatBezier, dfLastLonBezier, + dfLatBezier, dfLonBezier, + dfLat, dfLon); + } + else if (!bIsFirst && !(dfLastLat == dfLat && dfLastLon == dfLon)) + { + double dfCtrLatBezier = dfLat - (dfLatBezier - dfLat); + double dfCtrLonBezier = dfLon - (dfLonBezier - dfLon); + AddBezierCurve(lineString, + dfLastLat, dfLastLon, + dfCtrLatBezier, dfCtrLonBezier, + dfLat, dfLon); + } + + bLastPartIsClosedOrEnded = FALSE; + bLastIsBezier = TRUE; + dfLastLatBezier = dfLatBezier; + dfLastLonBezier = dfLonBezier; + } + else if (nType == APT_NODE_CLOSE || nType == APT_NODE_END) + { + RET_FALSE_IF_FAIL(assertMinCol(3)); + RET_FALSE_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + if (bIsFirst) + { + CPLDebug("XPlane", "Line %d : Unexpected token when reading a linear feature : %d", + nLineNumber, nType); + return TRUE; + } + + if (bLastIsBezier && !(dfLastLat == dfLat && dfLastLon == dfLon)) + { + AddBezierCurve(lineString, + dfLastLat, dfLastLon, + dfLastLatBezier, dfLastLonBezier, + dfLat, dfLon); + } + else + lineString.addPoint(dfLon, dfLat); + + if (nType == APT_NODE_CLOSE ) + lineString.closeRings(); + + if (lineString.getNumPoints() < 2) + { + CPLDebug("XPlane", "Line %d : A linestring has less than 2 points", + nLineNumber); + } + else + { + multilinestring.addGeometry(&lineString); + } + lineString.empty(); + + bLastPartIsClosedOrEnded = TRUE; + bLastIsBezier = FALSE; + } + else if (nType == APT_NODE_CLOSE_WITH_BEZIER || nType == APT_NODE_END_WITH_BEZIER) + { + RET_FALSE_IF_FAIL(assertMinCol(5)); + RET_FALSE_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + RET_FALSE_IF_FAIL(readLatLon(&dfLatBezier, &dfLonBezier, 3)); + if (bIsFirst) + { + CPLDebug("XPlane", "Line %d : Unexpected token when reading a linear feature : %d", + nLineNumber, nType); + return TRUE; + } + + if (bLastIsBezier) + { + AddBezierCurve(lineString, + dfLastLat, dfLastLon, + dfLastLatBezier, dfLastLonBezier, + dfLatBezier, dfLonBezier, + dfLat, dfLon); + } + else if (!bIsFirst && !(dfLastLat == dfLat && dfLastLon == dfLon)) + { + double dfCtrLatBezier = dfLat - (dfLatBezier - dfLat); + double dfCtrLonBezier = dfLon - (dfLonBezier - dfLon); + AddBezierCurve(lineString, + dfLastLat, dfLastLon, + dfCtrLatBezier, dfCtrLonBezier, + dfLat, dfLon); + } + else + { + lineString.addPoint(dfLon, dfLat); + } + + if (nType == APT_NODE_CLOSE_WITH_BEZIER) + { + if (bFirstIsBezier) + { + AddBezierCurve(lineString, + dfLat, dfLon, + dfLatBezier, dfLonBezier, + dfFirstLatBezier, dfFirstLonBezier, + dfFirstLat, dfFirstLon); + } + else + { + lineString.closeRings(); + } + } + + if (lineString.getNumPoints() < 2) + { + CPLDebug("XPlane", "Line %d : A linestring has less than 2 points", + nLineNumber); + } + else + { + multilinestring.addGeometry(&lineString); + } + lineString.empty(); + + bLastPartIsClosedOrEnded = TRUE; + bLastIsBezier = FALSE; /* we don't want to draw an arc between two parts */ + } + else + { + if (bLastPartIsClosedOrEnded == FALSE) + { + CPLDebug("XPlane", "Line %d : Unexpected token when reading a linear feature : %d", + nLineNumber, nType); + } + else if (multilinestring.getNumGeometries() == 0) + { + CPLDebug("XPlane", "Line %d : Linear geometry is invalid or empty", + nLineNumber); + } + else + { + *pbIsValid = TRUE; + } + return TRUE; + } + + if (bIsFirst) + { + dfFirstLat = dfLat; + dfFirstLon = dfLon; + dfFirstLatBezier = dfLatBezier; + dfFirstLonBezier = dfLonBezier; + bFirstIsBezier = bLastIsBezier; + } + bIsFirst = bLastPartIsClosedOrEnded; + + dfLastLat = dfLat; + dfLastLon = dfLon; + /* bLastIsValid = TRUE; */ + + CSLDestroy(papszTokens); + } + + CPLAssert(0); + + papszTokens = NULL; + + return FALSE; +} + +/************************************************************************/ +/* ParseAPTLinearFeature() */ +/************************************************************************/ + +/* This function will eat records until the end of the linear feature definition */ +/* Return TRUE if the main parser must re-scan the current record */ + +void OGRXPlaneAptReader::ParseAPTLinearFeature() +{ + CPLString osLinearFeatureName; + + RET_IF_FAIL(assertMinCol(2)); + + osLinearFeatureName = readStringUntilEnd(2); + + CSLDestroy(papszTokens); + papszTokens = NULL; + + OGRMultiLineString multilinestring; + int bIsValid = FALSE; + bResumeLine = ParseLinearGeometry(multilinestring, &bIsValid); + if (bIsValid && poAPTLinearFeatureLayer) + { + poAPTLinearFeatureLayer->AddFeature(osAptICAO, osLinearFeatureName, + &multilinestring); + } +} + +/************************************************************************/ +/* ParseTowerRecord() */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseTowerRecord() +{ + RET_IF_FAIL(assertMinCol(6)); + + RET_IF_FAIL(readLatLon(&dfLatTower, &dfLonTower, 1)); + + /* feet to meter */ + RET_IF_FAIL(readDoubleWithBoundsAndConversion(&dfHeightTower, 3, "tower height", FEET_TO_METER, 0., 300.)); + + // papszTokens[4] ignored + + osTowerName = readStringUntilEnd(5); + + bTowerFound = TRUE; +} + + +/************************************************************************/ +/* ParseATCRecord() */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseATCRecord(int nType) +{ + double dfFrequency; + CPLString osFreqName; + + RET_IF_FAIL(assertMinCol(2)); + + RET_IF_FAIL(readDouble(&dfFrequency, 1, "frequency")); + dfFrequency /= 100.; + + osFreqName = readStringUntilEnd(2); + + if (poATCFreqLayer) + { + poATCFreqLayer->AddFeature(osAptICAO, + (nType == APT_ATC_AWOS_ASOS_ATIS) ? "ATIS" : + (nType == APT_ATC_CTAF) ? "CTAF" : + (nType == APT_ATC_CLD) ? "CLD" : + (nType == APT_ATC_GND) ? "GND" : + (nType == APT_ATC_TWR) ? "TWR" : + (nType == APT_ATC_APP) ? "APP" : + (nType == APT_ATC_DEP) ? "DEP" : "UNK", + osFreqName, dfFrequency); + } +} + + +/************************************************************************/ +/* ParseStartupLocationRecord() */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseStartupLocationRecord() +{ + double dfLat, dfLon, dfTrueHeading; + CPLString osName; + + RET_IF_FAIL(assertMinCol(4)); + + RET_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + + RET_IF_FAIL(readTrueHeading(&dfTrueHeading, 3)); + + osName = readStringUntilEnd(4); + + if (poStartupLocationLayer) + poStartupLocationLayer->AddFeature(osAptICAO, osName, dfLat, dfLon, dfTrueHeading); +} + +/************************************************************************/ +/* ParseLightBeaconRecord() */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseLightBeaconRecord() +{ + double dfLat, dfLon; + int eColor; + CPLString osName; + + RET_IF_FAIL(assertMinCol(4)); + RET_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + eColor = atoi(papszTokens[3]); + osName = readStringUntilEnd(4); + + if (poAPTLightBeaconLayer) + poAPTLightBeaconLayer->AddFeature(osAptICAO, osName, dfLat, dfLon, + APTLightBeaconColorEnumeration.GetText(eColor)); +} + +/************************************************************************/ +/* ParseWindsockRecord() */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseWindsockRecord() +{ + double dfLat, dfLon; + int bIsIllumnited; + CPLString osName; + + RET_IF_FAIL(assertMinCol(4)); + + RET_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + bIsIllumnited = atoi(papszTokens[3]); + osName = readStringUntilEnd(4); + + if (poAPTWindsockLayer) + poAPTWindsockLayer->AddFeature(osAptICAO, osName, dfLat, dfLon, + bIsIllumnited); +} + +/************************************************************************/ +/* ParseTaxiwaySignRecord */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseTaxiwaySignRecord() +{ + double dfLat, dfLon; + double dfTrueHeading; + int nSize; + CPLString osText; + + RET_IF_FAIL(assertMinCol(7)); + + RET_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + RET_IF_FAIL(readTrueHeading(&dfTrueHeading, 3, "heading")); + /* papszTokens[4] : ignored. Taxiway sign style */ + nSize = atoi(papszTokens[5]); + osText = readStringUntilEnd(6); + + if (poTaxiwaySignLayer) + poTaxiwaySignLayer->AddFeature(osAptICAO, osText, dfLat, dfLon, + dfTrueHeading, nSize); +} + +/************************************************************************/ +/* ParseVasiPapiWigWagRecord() */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseVasiPapiWigWagRecord() +{ + double dfLat, dfLon; + int eType; + double dfTrueHeading, dfVisualGlidePathAngle; + const char* pszRwyNum; + + RET_IF_FAIL(assertMinCol(7)); + + RET_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + eType = atoi(papszTokens[3]); + RET_IF_FAIL(readTrueHeading(&dfTrueHeading, 4, "heading")); + RET_IF_FAIL(readDoubleWithBounds(&dfVisualGlidePathAngle, 5, "visual glidepath angle", 0, 90)); + pszRwyNum = papszTokens[6]; + /* papszTokens[7] : ignored. Type of lighting object represented */ + + if (poVASI_PAPI_WIGWAG_Layer) + poVASI_PAPI_WIGWAG_Layer->AddFeature(osAptICAO, pszRwyNum, VASI_PAPI_WIGWAG_Enumeration.GetText(eType), + dfLat, dfLon, + dfTrueHeading, dfVisualGlidePathAngle); +} + +/************************************************************************/ +/* ParseTaxiLocation */ +/************************************************************************/ + +void OGRXPlaneAptReader::ParseTaxiLocation() +{ + double dfLat, dfLon; + double dfTrueHeading; + CPLString osLocationType; + CPLString osAirplaneTypes; + CPLString osName; + + RET_IF_FAIL(assertMinCol(7)); + + RET_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + RET_IF_FAIL(readTrueHeading(&dfTrueHeading, 3, "heading")); + osLocationType = papszTokens[4]; + osAirplaneTypes = papszTokens[5]; + osName = readStringUntilEnd(6); + + if (poTaxiLocationLayer) + poTaxiLocationLayer->AddFeature(osAptICAO, dfLat, dfLon, + dfTrueHeading, osLocationType, + osAirplaneTypes, osName); +} + +/************************************************************************/ +/* OGRXPlaneAPTLayer() */ +/************************************************************************/ + + +OGRXPlaneAPTLayer::OGRXPlaneAPTLayer() : OGRXPlaneLayer("APT") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldID("apt_icao", OFTString ); + oFieldID.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldID ); + + OGRFieldDefn oFieldName("apt_name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oType("type", OFTInteger ); + oType.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oType ); + + OGRFieldDefn oFieldElev("elevation_m", OFTReal ); + oFieldElev.SetWidth( 8 ); + oFieldElev.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldElev ); + + OGRFieldDefn oFieldHasTower("has_tower", OFTInteger ); + oFieldHasTower.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldHasTower ); + + OGRFieldDefn oFieldHeightTower("hgt_tower_m", OFTReal ); + oFieldHeightTower.SetWidth( 8 ); + oFieldHeightTower.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldHeightTower ); + + OGRFieldDefn oFieldTowerName("tower_name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldTowerName ); + +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneAPTLayer::AddFeature(const char* pszAptICAO, + const char* pszAptName, + int nAPTType, + double dfElevation, + int bHasCoordinates, + double dfLat, + double dfLon, + int bHasTower, + double dfHeightTower, + const char* pszTowerName) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszAptName ); + poFeature->SetField( nCount++, (nAPTType == APT_AIRPORT_HEADER) ? 0 : + (nAPTType == APT_SEAPLANE_HEADER) ? 1 : + /*(nAPTType == APT_HELIPORT_HEADER)*/ 2 ); + poFeature->SetField( nCount++, dfElevation ); + poFeature->SetField( nCount++, bHasTower ); + if (bHasCoordinates) + { + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + } + else + { + CPLDebug("XPlane", "Airport %s/%s has no coordinates", pszAptICAO, pszAptName); + } + if (bHasTower) + { + poFeature->SetField( nCount++, dfHeightTower ); + poFeature->SetField( nCount++, pszTowerName ); + } + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneRunwayThresholdLayer() */ +/************************************************************************/ + + +OGRXPlaneRunwayThresholdLayer::OGRXPlaneRunwayThresholdLayer() : OGRXPlaneLayer("RunwayThreshold") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldRwyNum("rwy_num", OFTString ); + oFieldRwyNum.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRwyNum ); + + OGRFieldDefn oFieldWidth("width_m", OFTReal ); + oFieldWidth.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldWidth ); + + OGRFieldDefn oFieldSurface("surface", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldSurface ); + + OGRFieldDefn oFieldShoulder("shoulder", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldShoulder ); + + OGRFieldDefn oFieldSmoothness("smoothness", OFTReal ); + oFieldSmoothness.SetWidth( 4 ); + oFieldSmoothness.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldSmoothness ); + + OGRFieldDefn oFieldCenterLineLights("centerline_lights", OFTInteger ); + oFieldCenterLineLights.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldCenterLineLights ); + + OGRFieldDefn oFieldEdgeLigthing("edge_lighting", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldEdgeLigthing ); + + OGRFieldDefn oFieldDistanceRemainingSigns("distance_remaining_signs", OFTInteger ); + oFieldDistanceRemainingSigns.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldDistanceRemainingSigns ); + + OGRFieldDefn oFieldDisplacedThreshold("displaced_threshold_m", OFTReal ); + oFieldDisplacedThreshold.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldDisplacedThreshold ); + + OGRFieldDefn oFieldIsDisplaced("is_displaced", OFTInteger ); + oFieldIsDisplaced.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldIsDisplaced ); + + OGRFieldDefn oFieldStopwayLength("stopway_length_m", OFTReal ); + oFieldStopwayLength.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldStopwayLength ); + + OGRFieldDefn oFieldMarkings("markings", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldMarkings ); + + OGRFieldDefn oFieldApproachLighting("approach_lighting", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldApproachLighting ); + + OGRFieldDefn oFieldTouchdownLights("touchdown_lights", OFTInteger ); + oFieldTouchdownLights.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldTouchdownLights ); + + OGRFieldDefn oFieldREIL("REIL", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldREIL ); + + OGRFieldDefn oFieldLength("length_m", OFTReal ); + oFieldLength.SetWidth( 5 ); + poFeatureDefn->AddFieldDefn( &oFieldLength ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneRunwayThresholdLayer::AddFeature (const char* pszAptICAO, + const char* pszRwyNum, + double dfLat, + double dfLon, + double dfWidth, + const char* pszSurfaceType, + const char* pszShoulderType, + double dfSmoothness, + int bHasCenterLineLights, + const char* pszEdgeLighting, + int bHasDistanceRemainingSigns, + double dfDisplacedThresholdLength, + double dfStopwayLength, + const char* pszMarkings, + const char* pszApproachLightingCode, + int bHasTouchdownLights, + const char* pszREIL) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszRwyNum ); + poFeature->SetField( nCount++, dfWidth ); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, pszSurfaceType ); + poFeature->SetField( nCount++, pszShoulderType ); + poFeature->SetField( nCount++, dfSmoothness ); + poFeature->SetField( nCount++, bHasCenterLineLights ); + poFeature->SetField( nCount++, pszEdgeLighting ); + poFeature->SetField( nCount++, bHasDistanceRemainingSigns ); + poFeature->SetField( nCount++, dfDisplacedThresholdLength ); + poFeature->SetField( nCount++, FALSE); + poFeature->SetField( nCount++, dfStopwayLength ); + poFeature->SetField( nCount++, pszMarkings ); + poFeature->SetField( nCount++, pszApproachLightingCode ); + poFeature->SetField( nCount++, bHasTouchdownLights ); + poFeature->SetField( nCount++, pszREIL ); + + RegisterFeature(poFeature); + + return poFeature; +} + +void OGRXPlaneRunwayThresholdLayer::SetRunwayLengthAndHeading(OGRFeature* poFeature, + double dfLength, + double dfHeading) +{ + int nCount = 16; + poFeature->SetField( nCount++, dfLength ); + poFeature->SetField( nCount++, dfHeading ); +} + +/************************************************************************/ +/* AddFeatureFromNonDisplacedThreshold() */ +/************************************************************************/ + +OGRFeature* OGRXPlaneRunwayThresholdLayer:: + AddFeatureFromNonDisplacedThreshold(OGRFeature* poNonDisplacedThresholdFeature) +{ + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + poFeature->SetFrom(poNonDisplacedThresholdFeature, FALSE); + + double dfDisplacedThresholdLength = poFeature->GetFieldAsDouble("displaced_threshold_m"); + double dfTrueHeading = poFeature->GetFieldAsDouble("true_heading_deg"); + poFeature->SetField("is_displaced", TRUE); + OGRPoint* poPoint = (OGRPoint*)poFeature->GetGeometryRef(); + double dfLatDisplaced, dfLonDisplaced; + OGRXPlane_ExtendPosition(poPoint->getY(), poPoint->getX(), + dfDisplacedThresholdLength, dfTrueHeading, + &dfLatDisplaced, &dfLonDisplaced); + poPoint->setX(dfLonDisplaced); + poPoint->setY(dfLatDisplaced); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneRunwayLayer() */ +/************************************************************************/ + + + +OGRXPlaneRunwayLayer::OGRXPlaneRunwayLayer() : OGRXPlaneLayer("RunwayPolygon") +{ + poFeatureDefn->SetGeomType( wkbPolygon ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldRwyNum1("rwy_num1", OFTString ); + oFieldRwyNum1.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRwyNum1 ); + + OGRFieldDefn oFieldRwyNum2("rwy_num2", OFTString ); + oFieldRwyNum2.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRwyNum2 ); + + OGRFieldDefn oFieldWidth("width_m", OFTReal ); + oFieldWidth.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldWidth ); + + OGRFieldDefn oFieldSurface("surface", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldSurface ); + + OGRFieldDefn oFieldShoulder("shoulder", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldShoulder ); + + OGRFieldDefn oFieldSmoothness("smoothness", OFTReal ); + oFieldSmoothness.SetWidth( 4 ); + oFieldSmoothness.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldSmoothness ); + + OGRFieldDefn oFieldCenterLineLights("centerline_lights", OFTInteger ); + oFieldCenterLineLights.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldCenterLineLights ); + + OGRFieldDefn oFieldEdgeLigthing("edge_lighting", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldEdgeLigthing ); + + OGRFieldDefn oFieldDistanceRemainingSigns("distance_remaining_signs", OFTInteger ); + oFieldDistanceRemainingSigns.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldDistanceRemainingSigns ); + + OGRFieldDefn oFieldLength("length_m", OFTReal ); + oFieldLength.SetWidth( 5 ); + poFeatureDefn->AddFieldDefn( &oFieldLength ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + + +OGRFeature* + OGRXPlaneRunwayLayer::AddFeature (const char* pszAptICAO, + const char* pszRwyNum1, + const char* pszRwyNum2, + double dfLat1, + double dfLon1, + double dfLat2, + double dfLon2, + double dfWidth, + const char* pszSurfaceType, + const char* pszShoulderType, + double dfSmoothness, + int bHasCenterLineLights, + const char* pszEdgeLighting, + int bHasDistanceRemainingSigns) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + double dfLength = OGRXPlane_Distance(dfLat1, dfLon1, dfLat2, dfLon2); + double dfTrack12 = OGRXPlane_Track(dfLat1, dfLon1, dfLat2, dfLon2); + double dfTrack21 = OGRXPlane_Track(dfLat2, dfLon2, dfLat1, dfLon1); + double adfLat[4], adfLon[4]; + + OGRXPlane_ExtendPosition(dfLat1, dfLon1, dfWidth / 2, dfTrack12 - 90, &adfLat[0], &adfLon[0]); + OGRXPlane_ExtendPosition(dfLat2, dfLon2, dfWidth / 2, dfTrack21 + 90, &adfLat[1], &adfLon[1]); + OGRXPlane_ExtendPosition(dfLat2, dfLon2, dfWidth / 2, dfTrack21 - 90, &adfLat[2], &adfLon[2]); + OGRXPlane_ExtendPosition(dfLat1, dfLon1, dfWidth / 2, dfTrack12 + 90, &adfLat[3], &adfLon[3]); + + OGRLinearRing* linearRing = new OGRLinearRing(); + linearRing->setNumPoints(5); + int i; + for(i=0;i<4;i++) + linearRing->setPoint(i, adfLon[i], adfLat[i]); + linearRing->setPoint(4, adfLon[0], adfLat[0]); + OGRPolygon* polygon = new OGRPolygon(); + polygon->addRingDirectly( linearRing ); + poFeature->SetGeometryDirectly( polygon ); + + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszRwyNum1 ); + poFeature->SetField( nCount++, pszRwyNum2 ); + poFeature->SetField( nCount++, dfWidth ); + poFeature->SetField( nCount++, pszSurfaceType ); + poFeature->SetField( nCount++, pszShoulderType ); + poFeature->SetField( nCount++, dfSmoothness ); + poFeature->SetField( nCount++, bHasCenterLineLights ); + poFeature->SetField( nCount++, pszEdgeLighting ); + poFeature->SetField( nCount++, bHasDistanceRemainingSigns ); + poFeature->SetField( nCount++, dfLength ); + poFeature->SetField( nCount++, dfTrack12 ); + + RegisterFeature(poFeature); + + return poFeature; +} + + +/************************************************************************/ +/* OGRXPlaneStopwayLayer() */ +/************************************************************************/ + + + +OGRXPlaneStopwayLayer::OGRXPlaneStopwayLayer() : OGRXPlaneLayer("Stopway") +{ + poFeatureDefn->SetGeomType( wkbPolygon ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldRwyNum1("rwy_num", OFTString ); + oFieldRwyNum1.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRwyNum1 ); + + OGRFieldDefn oFieldWidth("width_m", OFTReal ); + oFieldWidth.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldWidth ); + + OGRFieldDefn oFieldLength("length_m", OFTReal ); + oFieldLength.SetWidth( 5 ); + poFeatureDefn->AddFieldDefn( &oFieldLength ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + + +OGRFeature* + OGRXPlaneStopwayLayer::AddFeature(const char* pszAptICAO, + const char* pszRwyNum, + double dfLatThreshold, + double dfLonThreshold, + double dfRunwayHeading, + double dfWidth, + double dfStopwayLength) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + double dfLat2, dfLon2; + double adfLat[4], adfLon[4]; + + OGRXPlane_ExtendPosition(dfLatThreshold, dfLonThreshold, dfStopwayLength, 180 + dfRunwayHeading, &dfLat2, &dfLon2); + + OGRXPlane_ExtendPosition(dfLatThreshold, dfLonThreshold, dfWidth / 2, dfRunwayHeading - 90, &adfLat[0], &adfLon[0]); + OGRXPlane_ExtendPosition(dfLat2, dfLon2, dfWidth / 2, dfRunwayHeading - 90, &adfLat[1], &adfLon[1]); + OGRXPlane_ExtendPosition(dfLat2, dfLon2, dfWidth / 2, dfRunwayHeading + 90, &adfLat[2], &adfLon[2]); + OGRXPlane_ExtendPosition(dfLatThreshold, dfLonThreshold, dfWidth / 2, dfRunwayHeading + 90, &adfLat[3], &adfLon[3]); + + OGRLinearRing* linearRing = new OGRLinearRing(); + linearRing->setNumPoints(5); + int i; + for(i=0;i<4;i++) + linearRing->setPoint(i, adfLon[i], adfLat[i]); + linearRing->setPoint(4, adfLon[0], adfLat[0]); + OGRPolygon* polygon = new OGRPolygon(); + polygon->addRingDirectly( linearRing ); + poFeature->SetGeometryDirectly( polygon ); + + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszRwyNum ); + poFeature->SetField( nCount++, dfWidth ); + poFeature->SetField( nCount++, dfStopwayLength ); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneWaterRunwayThresholdLayer() */ +/************************************************************************/ + + +OGRXPlaneWaterRunwayThresholdLayer::OGRXPlaneWaterRunwayThresholdLayer() : OGRXPlaneLayer("WaterRunwayThreshold") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldRwyNum("rwy_num", OFTString ); + oFieldRwyNum.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRwyNum ); + + OGRFieldDefn oFieldWidth("width_m", OFTReal ); + oFieldWidth.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldWidth ); + + OGRFieldDefn oFieldHasBuoys("has_buoys", OFTInteger ); + oFieldHasBuoys.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldHasBuoys ); + + OGRFieldDefn oFieldLength("length_m", OFTReal ); + oFieldLength.SetWidth( 5 ); + poFeatureDefn->AddFieldDefn( &oFieldLength ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneWaterRunwayThresholdLayer::AddFeature (const char* pszAptICAO, + const char* pszRwyNum, + double dfLat, + double dfLon, + double dfWidth, + int bBuoys) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszRwyNum ); + poFeature->SetField( nCount++, dfWidth ); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, bBuoys ); + + RegisterFeature(poFeature); + + return poFeature; +} + +void OGRXPlaneWaterRunwayThresholdLayer::SetRunwayLengthAndHeading(OGRFeature* poFeature, + double dfLength, + double dfHeading) +{ + int nCount = 4; + poFeature->SetField( nCount++, dfLength ); + poFeature->SetField( nCount++, dfHeading ); +} + + +/************************************************************************/ +/* OGRXPlaneWaterRunwayLayer() */ +/************************************************************************/ + + + +OGRXPlaneWaterRunwayLayer::OGRXPlaneWaterRunwayLayer() : OGRXPlaneLayer("WaterRunwayPolygon") +{ + poFeatureDefn->SetGeomType( wkbPolygon ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldRwyNum1("rwy_num1", OFTString ); + oFieldRwyNum1.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRwyNum1 ); + + OGRFieldDefn oFieldRwyNum2("rwy_num2", OFTString ); + oFieldRwyNum2.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRwyNum2 ); + + OGRFieldDefn oFieldWidth("width_m", OFTReal ); + oFieldWidth.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldWidth ); + + OGRFieldDefn oFieldHasBuoys("has_buoys", OFTInteger ); + oFieldHasBuoys.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldHasBuoys ); + + OGRFieldDefn oFieldLength("length_m", OFTReal ); + oFieldLength.SetWidth( 5 ); + poFeatureDefn->AddFieldDefn( &oFieldLength ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneWaterRunwayLayer::AddFeature (const char* pszAptICAO, + const char* pszRwyNum1, + const char* pszRwyNum2, + double dfLat1, + double dfLon1, + double dfLat2, + double dfLon2, + double dfWidth, + int bBuoys) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + double dfLength = OGRXPlane_Distance(dfLat1, dfLon1, dfLat2, dfLon2); + double dfTrack12 = OGRXPlane_Track(dfLat1, dfLon1, dfLat2, dfLon2); + double dfTrack21 = OGRXPlane_Track(dfLat2, dfLon2, dfLat1, dfLon1); + double adfLat[4], adfLon[4]; + + OGRXPlane_ExtendPosition(dfLat1, dfLon1, dfWidth / 2, dfTrack12 - 90, &adfLat[0], &adfLon[0]); + OGRXPlane_ExtendPosition(dfLat2, dfLon2, dfWidth / 2, dfTrack21 + 90, &adfLat[1], &adfLon[1]); + OGRXPlane_ExtendPosition(dfLat2, dfLon2, dfWidth / 2, dfTrack21 - 90, &adfLat[2], &adfLon[2]); + OGRXPlane_ExtendPosition(dfLat1, dfLon1, dfWidth / 2, dfTrack12 + 90, &adfLat[3], &adfLon[3]); + + OGRLinearRing* linearRing = new OGRLinearRing(); + linearRing->setNumPoints(5); + int i; + for(i=0;i<4;i++) + linearRing->setPoint(i, adfLon[i], adfLat[i]); + linearRing->setPoint(4, adfLon[0], adfLat[0]); + OGRPolygon* polygon = new OGRPolygon(); + polygon->addRingDirectly( linearRing ); + poFeature->SetGeometryDirectly( polygon ); + + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszRwyNum1 ); + poFeature->SetField( nCount++, pszRwyNum2 ); + poFeature->SetField( nCount++, dfWidth ); + poFeature->SetField( nCount++, bBuoys ); + poFeature->SetField( nCount++, dfLength ); + poFeature->SetField( nCount++, dfTrack12 ); + + RegisterFeature(poFeature); + + return poFeature; +} + + +/************************************************************************/ +/* OGRXPlaneHelipadLayer() */ +/************************************************************************/ + + +OGRXPlaneHelipadLayer::OGRXPlaneHelipadLayer() : OGRXPlaneLayer("Helipad") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldHelipadName("helipad_name", OFTString ); + oFieldHelipadName.SetWidth( 5 ); + poFeatureDefn->AddFieldDefn( &oFieldHelipadName ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); + + OGRFieldDefn oFieldLength("length_m", OFTReal ); + oFieldLength.SetWidth( 5 ); + poFeatureDefn->AddFieldDefn( &oFieldLength ); + + OGRFieldDefn oFieldWidth("width_m", OFTReal ); + oFieldWidth.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldWidth ); + + OGRFieldDefn oFieldSurface("surface", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldSurface ); + + OGRFieldDefn oFieldMarkings("markings", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldMarkings ); + + OGRFieldDefn oFieldShoulder("shoulder", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldShoulder ); + + OGRFieldDefn oFieldSmoothness("smoothness", OFTReal ); + oFieldSmoothness.SetWidth( 4 ); + oFieldSmoothness.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldSmoothness ); + + OGRFieldDefn oFieldEdgeLighting("edge_lighting", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldEdgeLighting ); + +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneHelipadLayer::AddFeature (const char* pszAptICAO, + const char* pszHelipadNum, + double dfLat, + double dfLon, + double dfTrueHeading, + double dfLength, + double dfWidth, + const char* pszSurfaceType, + const char* pszMarkings, + const char* pszShoulderType, + double dfSmoothness, + const char* pszEdgeLighting) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszHelipadNum ); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, dfTrueHeading ); + poFeature->SetField( nCount++, dfLength ); + poFeature->SetField( nCount++, dfWidth ); + poFeature->SetField( nCount++, pszSurfaceType ); + poFeature->SetField( nCount++, pszMarkings ); + poFeature->SetField( nCount++, pszShoulderType ); + poFeature->SetField( nCount++, dfSmoothness ); + poFeature->SetField( nCount++, pszEdgeLighting ); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneHelipadPolygonLayer() */ +/************************************************************************/ + + +OGRXPlaneHelipadPolygonLayer::OGRXPlaneHelipadPolygonLayer() : OGRXPlaneLayer("HelipadPolygon") +{ + poFeatureDefn->SetGeomType( wkbPolygon ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldHelipadName("helipad_name", OFTString ); + oFieldHelipadName.SetWidth( 5 ); + poFeatureDefn->AddFieldDefn( &oFieldHelipadName ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); + + OGRFieldDefn oFieldLength("length_m", OFTReal ); + oFieldLength.SetWidth( 5 ); + poFeatureDefn->AddFieldDefn( &oFieldLength ); + + OGRFieldDefn oFieldWidth("width_m", OFTReal ); + oFieldWidth.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldWidth ); + + OGRFieldDefn oFieldSurface("surface", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldSurface ); + + OGRFieldDefn oFieldMarkings("markings", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldMarkings ); + + OGRFieldDefn oFieldShoulder("shoulder", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldShoulder ); + + OGRFieldDefn oFieldSmoothness("smoothness", OFTReal ); + oFieldSmoothness.SetWidth( 4 ); + oFieldSmoothness.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldSmoothness ); + + OGRFieldDefn oFieldEdgeLighting("edge_lighting", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldEdgeLighting ); + +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneHelipadPolygonLayer::AddFeature (const char* pszAptICAO, + const char* pszHelipadNum, + double dfLat, + double dfLon, + double dfTrueHeading, + double dfLength, + double dfWidth, + const char* pszSurfaceType, + const char* pszMarkings, + const char* pszShoulderType, + double dfSmoothness, + const char* pszEdgeLighting) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + double dfBeforeLat, dfBeforeLon; + double dfAfterLat, dfAfterLon; + double adfLat[4], adfLon[4]; + + OGRXPlane_ExtendPosition(dfLat, dfLon, dfLength / 2, dfTrueHeading + 180, &dfBeforeLat, &dfBeforeLon); + OGRXPlane_ExtendPosition(dfLat, dfLon, dfLength / 2, dfTrueHeading, &dfAfterLat, &dfAfterLon); + + OGRXPlane_ExtendPosition(dfBeforeLat, dfBeforeLon, dfWidth / 2, dfTrueHeading - 90, &adfLat[0], &adfLon[0]); + OGRXPlane_ExtendPosition(dfAfterLat, dfAfterLon, dfWidth / 2, dfTrueHeading - 90, &adfLat[1], &adfLon[1]); + OGRXPlane_ExtendPosition(dfAfterLat, dfAfterLon, dfWidth / 2, dfTrueHeading + 90, &adfLat[2], &adfLon[2]); + OGRXPlane_ExtendPosition(dfBeforeLat, dfBeforeLon, dfWidth / 2, dfTrueHeading + 90, &adfLat[3], &adfLon[3]); + + OGRLinearRing* linearRing = new OGRLinearRing(); + linearRing->setNumPoints(5); + int i; + for(i=0;i<4;i++) + linearRing->setPoint(i, adfLon[i], adfLat[i]); + linearRing->setPoint(4, adfLon[0], adfLat[0]); + OGRPolygon* polygon = new OGRPolygon(); + polygon->addRingDirectly( linearRing ); + poFeature->SetGeometryDirectly( polygon ); + + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszHelipadNum ); + poFeature->SetField( nCount++, dfTrueHeading ); + poFeature->SetField( nCount++, dfLength ); + poFeature->SetField( nCount++, dfWidth ); + poFeature->SetField( nCount++, pszSurfaceType ); + poFeature->SetField( nCount++, pszMarkings ); + poFeature->SetField( nCount++, pszShoulderType ); + poFeature->SetField( nCount++, dfSmoothness ); + poFeature->SetField( nCount++, pszEdgeLighting ); + + RegisterFeature(poFeature); + + return poFeature; +} + + +/************************************************************************/ +/* OGRXPlaneTaxiwayRectangleLayer() */ +/************************************************************************/ + + +OGRXPlaneTaxiwayRectangleLayer::OGRXPlaneTaxiwayRectangleLayer() : OGRXPlaneLayer("TaxiwayRectangle") +{ + poFeatureDefn->SetGeomType( wkbPolygon ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); + + OGRFieldDefn oFieldLength("length_m", OFTReal ); + oFieldLength.SetWidth( 5 ); + poFeatureDefn->AddFieldDefn( &oFieldLength ); + + OGRFieldDefn oFieldWidth("width_m", OFTReal ); + oFieldWidth.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldWidth ); + + OGRFieldDefn oFieldSurface("surface", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldSurface ); + + OGRFieldDefn oFieldSmoothness("smoothness", OFTReal ); + oFieldSmoothness.SetWidth( 4 ); + oFieldSmoothness.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldSmoothness ); + + OGRFieldDefn oFieldBlueEdgeLighting("edge_lighting", OFTInteger ); + oFieldBlueEdgeLighting.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldBlueEdgeLighting ); + +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneTaxiwayRectangleLayer::AddFeature(const char* pszAptICAO, + double dfLat, + double dfLon, + double dfTrueHeading, + double dfLength, + double dfWidth, + const char* pszSurfaceType, + double dfSmoothness, + int bBlueEdgeLights) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + double dfBeforeLat, dfBeforeLon; + double dfAfterLat, dfAfterLon; + double adfLat[4], adfLon[4]; + + OGRXPlane_ExtendPosition(dfLat, dfLon, dfLength / 2, dfTrueHeading + 180, &dfBeforeLat, &dfBeforeLon); + OGRXPlane_ExtendPosition(dfLat, dfLon, dfLength / 2, dfTrueHeading, &dfAfterLat, &dfAfterLon); + + OGRXPlane_ExtendPosition(dfBeforeLat, dfBeforeLon, dfWidth / 2, dfTrueHeading - 90, &adfLat[0], &adfLon[0]); + OGRXPlane_ExtendPosition(dfAfterLat, dfAfterLon, dfWidth / 2, dfTrueHeading - 90, &adfLat[1], &adfLon[1]); + OGRXPlane_ExtendPosition(dfAfterLat, dfAfterLon, dfWidth / 2, dfTrueHeading + 90, &adfLat[2], &adfLon[2]); + OGRXPlane_ExtendPosition(dfBeforeLat, dfBeforeLon, dfWidth / 2, dfTrueHeading + 90, &adfLat[3], &adfLon[3]); + + OGRLinearRing* linearRing = new OGRLinearRing(); + linearRing->setNumPoints(5); + int i; + for(i=0;i<4;i++) + linearRing->setPoint(i, adfLon[i], adfLat[i]); + linearRing->setPoint(4, adfLon[0], adfLat[0]); + OGRPolygon* polygon = new OGRPolygon(); + polygon->addRingDirectly( linearRing ); + poFeature->SetGeometryDirectly( polygon ); + + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, dfTrueHeading ); + poFeature->SetField( nCount++, dfLength ); + poFeature->SetField( nCount++, dfWidth ); + poFeature->SetField( nCount++, pszSurfaceType ); + poFeature->SetField( nCount++, dfSmoothness ); + poFeature->SetField( nCount++, bBlueEdgeLights ); + + RegisterFeature(poFeature); + + return poFeature; +} + + +/************************************************************************/ +/* OGRXPlanePavementLayer() */ +/************************************************************************/ + + +OGRXPlanePavementLayer::OGRXPlanePavementLayer() : OGRXPlaneLayer("Pavement") +{ + poFeatureDefn->SetGeomType( wkbPolygon ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldName("name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oFieldSurface("surface", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldSurface ); + + OGRFieldDefn oFieldSmoothness("smoothness", OFTReal ); + oFieldSmoothness.SetWidth( 4 ); + oFieldSmoothness.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldSmoothness ); + + OGRFieldDefn oFieldTextureHeading("texture_heading", OFTReal ); + oFieldTextureHeading.SetWidth( 6 ); + oFieldTextureHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTextureHeading ); + +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlanePavementLayer::AddFeature(const char* pszAptICAO, + const char* pszPavementName, + const char* pszSurfaceType, + double dfSmoothness, + double dfTextureHeading, + OGRPolygon* poPolygon) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + poFeature->SetGeometry( poPolygon ); + + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszPavementName ); + poFeature->SetField( nCount++, pszSurfaceType ); + poFeature->SetField( nCount++, dfSmoothness ); + poFeature->SetField( nCount++, dfTextureHeading ); + + RegisterFeature(poFeature); + + return poFeature; +} + + + +/************************************************************************/ +/* OGRXPlaneAPTBoundaryLayer() */ +/************************************************************************/ + + +OGRXPlaneAPTBoundaryLayer::OGRXPlaneAPTBoundaryLayer() : OGRXPlaneLayer("APTBoundary") +{ + poFeatureDefn->SetGeomType( wkbPolygon ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldName("name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneAPTBoundaryLayer::AddFeature(const char* pszAptICAO, + const char* pszBoundaryName, + OGRPolygon* poPolygon) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + poFeature->SetGeometry( poPolygon ); + + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszBoundaryName ); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneAPTLinearFeatureLayer() */ +/************************************************************************/ + + +OGRXPlaneAPTLinearFeatureLayer::OGRXPlaneAPTLinearFeatureLayer() : OGRXPlaneLayer("APTLinearFeature") +{ + poFeatureDefn->SetGeomType( wkbMultiLineString ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldName("name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneAPTLinearFeatureLayer::AddFeature(const char* pszAptICAO, + const char* pszLinearFeatureName, + OGRMultiLineString* poMultilineString) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + + poFeature->SetGeometry( poMultilineString ); + + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszLinearFeatureName ); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneATCFreqLayer() */ +/************************************************************************/ + + +OGRXPlaneATCFreqLayer::OGRXPlaneATCFreqLayer() : OGRXPlaneLayer("ATCFreq") +{ + poFeatureDefn->SetGeomType( wkbNone ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldATCFreqType("atc_type", OFTString ); + oFieldATCFreqType.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldATCFreqType ); + + OGRFieldDefn oFieldATCFreqName("freq_name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldATCFreqName ); + + OGRFieldDefn oFieldFreq("freq_mhz", OFTReal ); + oFieldFreq.SetWidth( 7 ); + oFieldFreq.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldFreq ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneATCFreqLayer::AddFeature (const char* pszAptICAO, + const char* pszATCType, + const char* pszATCFreqName, + double dfFrequency) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszATCType ); + poFeature->SetField( nCount++, pszATCFreqName ); + poFeature->SetField( nCount++, dfFrequency ); + + RegisterFeature(poFeature); + + return poFeature; +} + + +/************************************************************************/ +/* OGRXPlaneStartupLocationLayer() */ +/************************************************************************/ + +OGRXPlaneStartupLocationLayer::OGRXPlaneStartupLocationLayer() : OGRXPlaneLayer("StartupLocation") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldName("name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneStartupLocationLayer::AddFeature (const char* pszAptICAO, + const char* pszName, + double dfLat, + double dfLon, + double dfTrueHeading) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszName ); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, dfTrueHeading ); + + RegisterFeature(poFeature); + + return poFeature; +} + + +/************************************************************************/ +/* OGRXPlaneAPTLightBeaconLayer() */ +/************************************************************************/ + +OGRXPlaneAPTLightBeaconLayer::OGRXPlaneAPTLightBeaconLayer() : OGRXPlaneLayer("APTLightBeacon") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldName("name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oFieldColor("color", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldColor ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneAPTLightBeaconLayer::AddFeature (const char* pszAptICAO, + const char* pszName, + double dfLat, + double dfLon, + const char* pszColor) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszName ); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, pszColor ); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneAPTWindsockLayer() */ +/************************************************************************/ + +OGRXPlaneAPTWindsockLayer::OGRXPlaneAPTWindsockLayer() : OGRXPlaneLayer("APTWindsock") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldName("name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oFieldIsIlluminated("is_illuminated", OFTInteger ); + oFieldIsIlluminated.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldIsIlluminated ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneAPTWindsockLayer::AddFeature (const char* pszAptICAO, + const char* pszName, + double dfLat, + double dfLon, + int bIsIllumnited) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszName ); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, bIsIllumnited ); + + RegisterFeature(poFeature); + + return poFeature; +} + + +/************************************************************************/ +/* OGRXPlaneTaxiwaySignLayer() */ +/************************************************************************/ + +OGRXPlaneTaxiwaySignLayer::OGRXPlaneTaxiwaySignLayer() : OGRXPlaneLayer("TaxiwaySign") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldText("text", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldText ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); + + OGRFieldDefn oFieldSize("size", OFTInteger ); + oFieldSize.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldSize ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneTaxiwaySignLayer::AddFeature (const char* pszAptICAO, + const char* pszText, + double dfLat, + double dfLon, + double dfHeading, + int nSize) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszText ); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, dfHeading ); + poFeature->SetField( nCount++, nSize ); + + RegisterFeature(poFeature); + + return poFeature; +} + + +/************************************************************************/ +/* OGRXPlane_VASI_PAPI_WIGWAG_Layer() */ +/************************************************************************/ + +OGRXPlane_VASI_PAPI_WIGWAG_Layer::OGRXPlane_VASI_PAPI_WIGWAG_Layer() : OGRXPlaneLayer("VASI_PAPI_WIGWAG") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldRwyNum("rwy_num", OFTString ); + oFieldRwyNum.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRwyNum ); + + OGRFieldDefn oFieldType("type", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldType ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); + + OGRFieldDefn oFieldVisualGlidePathAngle("visual_glide_deg", OFTReal ); + oFieldVisualGlidePathAngle.SetWidth( 4 ); + oFieldVisualGlidePathAngle.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldVisualGlidePathAngle ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlane_VASI_PAPI_WIGWAG_Layer::AddFeature (const char* pszAptICAO, + const char* pszRwyNum, + const char* pszObjectType, + double dfLat, + double dfLon, + double dfHeading, + double dfVisualGlidePathAngle) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszRwyNum ); + poFeature->SetField( nCount++, pszObjectType ); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, dfHeading ); + poFeature->SetField( nCount++, dfVisualGlidePathAngle ); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneTaxiLocationLayer() */ +/************************************************************************/ + +OGRXPlaneTaxiLocationLayer::OGRXPlaneTaxiLocationLayer() : OGRXPlaneLayer("TaxiLocation") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); + + OGRFieldDefn oFieldLocationType("location_type", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldLocationType ); + + OGRFieldDefn oFieldAirplaneTypes("airplane_types", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldAirplaneTypes ); + + OGRFieldDefn oFieldName("name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneTaxiLocationLayer::AddFeature (const char* pszAptICAO, + double dfLat, + double dfLon, + double dfHeading, + const char* pszLocationType, + const char* pszAirplaneTypes, + const char* pszName) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, dfHeading ); + poFeature->SetField( nCount++, pszLocationType ); + poFeature->SetField( nCount++, pszAirplaneTypes ); + poFeature->SetField( nCount++, pszName ); + + RegisterFeature(poFeature); + + return poFeature; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_apt_reader.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_apt_reader.h new file mode 100644 index 000000000..97d13c9b4 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_apt_reader.h @@ -0,0 +1,694 @@ +/****************************************************************************** + * $Id: ogr_xplane_apt_reader.h + * + * Project: X-Plane apt.dat file reader headers + * Purpose: Definition of classes for X-Plane apt.dat file reader + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_XPLANE_APT_READER_H_INCLUDED +#define _OGR_XPLANE_APT_READER_H_INCLUDED + +#include "ogr_xplane.h" +#include "ogr_xplane_reader.h" + +/************************************************************************/ +/* OGRXPlaneAPTLayer */ +/************************************************************************/ + + +class OGRXPlaneAPTLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneAPTLayer(); + + /* If the airport has a tower, its coordinates are the tower coordinates */ + /* If it has no tower, then we pick up the coordinates of the threshold of its first found runway */ + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszAptName, + int nAPTType, + double dfElevation, + int bHasCoordinates = FALSE, + double dfLat = 0, + double dfLon = 0, + int bHasTower = FALSE, + double dfHeightTower = 0, + const char* pszTowerName = NULL); +}; + + +/************************************************************************/ +/* OGRXPlaneRunwayThresholdLayer */ +/************************************************************************/ + +static const sEnumerationElement runwaySurfaceType[] = +{ + { 1, "Asphalt" }, + { 2, "Concrete" }, + { 3, "Turf/grass" }, + { 4, "Dirt" }, + { 5, "Gravel" }, + { 6, "Asphalt" /* helipad V810 */}, + { 7, "Concrete" /* helipad V810 */}, + { 8, "Turf/grass" /* helipad V810 */}, + { 9, "Dirt" /* helipad V810 */}, + { 10, "Asphalt" /* taxiway V810 */ }, + { 11, "Concrete" /* taxiway V810 */ }, + { 12, "Dry lakebed" }, + { 13, "Water" }, + { 14, "Snow/ice" /* V850 */ }, + { 15, "Transparent" /* V850 */ } +}; + +static const sEnumerationElement runwayShoulderType[] = +{ + { 0, "None" }, + { 1, "Asphalt" }, + { 2, "Concrete" } +}; + +static const sEnumerationElement runwayMarkingType[] = +{ + { 0, "None" }, + { 1, "Visual" }, + { 2, "Non-precision approach" }, + { 3, "Precision approach" }, + { 4, "UK-style non-precision" }, + { 5, "UK-style precision" } +}; + +static const sEnumerationElement approachLightingType[] = +{ + { 0, "None" }, + { 1, "ALSF-I" }, + { 2, "ALSF-II" }, + { 3, "Calvert" }, + { 4, "Calvert ISL Cat II and III" }, + { 5, "SSALR" }, + { 6, "SSALF" }, + { 7, "SALS" }, + { 8, "MALSR" }, + { 9, "MALSF" }, + { 10, "MALS" }, + { 11, "ODALS" }, + { 12, "RAIL" } +}; + +static const sEnumerationElement approachLightingTypeV810[] = +{ + { 1, "None" }, + { 2, "SSALS" }, + { 3, "SALSF" }, + { 4, "ALSF-I" }, + { 5, "ALSF-II" }, + { 6, "ODALS" }, + { 7, "Calvert" }, + { 8, "Calvert ISL Cat II and III" }, +}; + +static const sEnumerationElement runwayEdgeLigthingType[] = +{ + { 0, "None" }, + { 1, "LIRL" }, /* proposed for V90x */ + { 2, "MIRL" }, + { 3, "HIRL" } /* proposed for V90x */ +}; + +static const sEnumerationElement runwayREILType[] = +{ + { 0, "None" }, + { 1, "Omni-directional" }, + { 2, "Unidirectional" } +}; + +static const sEnumerationElement runwayVisualApproachPathIndicatorTypeV810[] = +{ + { 1, "None" }, + { 2, "VASI" }, + { 3, "PAPI Right" }, + { 4, "Space Shuttle PAPI" } +}; + +DEFINE_XPLANE_ENUMERATION(RunwaySurfaceEnumeration, runwaySurfaceType); +DEFINE_XPLANE_ENUMERATION(RunwayShoulderEnumeration, runwayShoulderType); +DEFINE_XPLANE_ENUMERATION(RunwayMarkingEnumeration, runwayMarkingType); +DEFINE_XPLANE_ENUMERATION(RunwayApproachLightingEnumeration, approachLightingType); +DEFINE_XPLANE_ENUMERATION(RunwayApproachLightingEnumerationV810, approachLightingTypeV810); +DEFINE_XPLANE_ENUMERATION(RunwayEdgeLightingEnumeration, runwayEdgeLigthingType); +DEFINE_XPLANE_ENUMERATION(RunwayREILEnumeration, runwayREILType); +DEFINE_XPLANE_ENUMERATION(RunwayVisualApproachPathIndicatorEnumerationV810, runwayVisualApproachPathIndicatorTypeV810); + +class OGRXPlaneRunwayThresholdLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneRunwayThresholdLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszRwyNum, + double dfLat, + double dfLon, + double dfWidth, + const char* pszSurfaceType, + const char* pszShoulderType, + double dfSmoothness, + int bHasCenterLineLights, + const char* pszEdgeLighting, + int bHasDistanceRemainingSigns, + double dfDisplacedThresholdLength, + double dfStopwayLength, + const char* pszMarkings, + const char* pszApproachLightingCode, + int bHasTouchdownLights, + const char* pszREIL); + + /* Set a few computed values */ + void SetRunwayLengthAndHeading(OGRFeature* poFeature, + double dfLength, + double dfHeading); + + OGRFeature* AddFeatureFromNonDisplacedThreshold(OGRFeature* poNonDisplacedThresholdFeature); +}; + +/************************************************************************/ +/* OGRXPlaneRunwayLayer */ +/************************************************************************/ + + +class OGRXPlaneRunwayLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneRunwayLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszRwyNum1, + const char* pszRwyNum2, + double dfLat1, + double dfLon1, + double dfLat2, + double dfLon2, + double dfWidth, + const char* pszSurfaceType, + const char* pszShoulderType, + double dfSmoothness, + int bHasCenterLineLights, + const char* pszEdgeLighting, + int bHasDistanceRemainingSigns); +}; + + +/************************************************************************/ +/* OGRXPlaneStopwayLayer */ +/************************************************************************/ + + +class OGRXPlaneStopwayLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneStopwayLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszRwyNum, + double dfLatThreshold, + double dfLonThreshold, + double dfRunwayHeading, + double dfWidth, + double dfStopwayLength); +}; + +/************************************************************************/ +/* OGRXPlaneWaterRunwayThresholdLayer */ +/************************************************************************/ + + +class OGRXPlaneWaterRunwayThresholdLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneWaterRunwayThresholdLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszRwyNum, + double dfLat, + double dfLon, + double dfWidth, + int bBuoys); + + /* Set a few computed values */ + void SetRunwayLengthAndHeading(OGRFeature* poFeature, + double dfLength, + double dfHeading); +}; + + +/************************************************************************/ +/* OGRXPlaneWaterRunwayLayer */ +/************************************************************************/ + +/* Polygonal object */ + +class OGRXPlaneWaterRunwayLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneWaterRunwayLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszRwyNum1, + const char* pszRwyNum2, + double dfLat1, + double dfLon1, + double dfLat2, + double dfLon2, + double dfWidth, + int bBuoys); +}; + + +/************************************************************************/ +/* OGRXPlaneHelipadLayer */ +/************************************************************************/ + +static const sEnumerationElement helipadEdgeLigthingType[] = +{ + { 0, "None" }, + { 1, "Yellow" }, + { 2, "White" }, /* proposed for V90x */ + { 3, "Red" } /* proposed for V90x */ +}; + +DEFINE_XPLANE_ENUMERATION(HelipadEdgeLightingEnumeration, helipadEdgeLigthingType); + +class OGRXPlaneHelipadLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneHelipadLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszHelipadNum, + double dfLat, + double dfLon, + double dfTrueHeading, + double dfLength, + double dfWidth, + const char* pszSurfaceType, + const char* pszMarkings, + const char* pszShoulderType, + double dfSmoothness, + const char* pszEdgeLighing); +}; + +/************************************************************************/ +/* OGRXPlaneHelipadPolygonLayer */ +/************************************************************************/ + + +class OGRXPlaneHelipadPolygonLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneHelipadPolygonLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszHelipadNum, + double dfLat, + double dfLon, + double dfTrueHeading, + double dfLength, + double dfWidth, + const char* pszSurfaceType, + const char* pszMarkings, + const char* pszShoulderType, + double dfSmoothness, + const char* pszEdgeLighing); +}; + + +/************************************************************************/ +/* OGRXPlaneTaxiwayRectangleLayer */ +/************************************************************************/ + + +class OGRXPlaneTaxiwayRectangleLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneTaxiwayRectangleLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + double dfLat, + double dfLon, + double dfTrueHeading, + double dfLength, + double dfWidth, + const char* pszSurfaceType, + double dfSmoothness, + int bBlueEdgeLights); +}; + + +/************************************************************************/ +/* OGRXPlanePavementLayer */ +/************************************************************************/ + + +class OGRXPlanePavementLayer : public OGRXPlaneLayer +{ + public: + OGRXPlanePavementLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszPavementName, + const char* pszSurfaceType, + double dfSmoothness, + double dfTextureHeading, + OGRPolygon* poPolygon); +}; + +/************************************************************************/ +/* OGRXPlaneAPTBoundaryLayer */ +/************************************************************************/ + + +class OGRXPlaneAPTBoundaryLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneAPTBoundaryLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszBoundaryName, + OGRPolygon* poPolygon); +}; + + +/************************************************************************/ +/* OGRXPlaneAPTLinearFeatureLayer */ +/************************************************************************/ + + +class OGRXPlaneAPTLinearFeatureLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneAPTLinearFeatureLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszLinearFeatureName, + OGRMultiLineString* poMultilineString); +}; + + +/************************************************************************/ +/* OGRXPlaneATCFreqLayer */ +/************************************************************************/ + +class OGRXPlaneATCFreqLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneATCFreqLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszATCType, + const char* pszATCFreqName, + double dfFrequency); +}; + + +/************************************************************************/ +/* OGRXPlaneStartupLocationLayer */ +/************************************************************************/ + +class OGRXPlaneStartupLocationLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneStartupLocationLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszName, + double dfLat, + double dfLon, + double dfTrueHeading); +}; + +/************************************************************************/ +/* OGRXPlaneAPTLightBeaconLayer */ +/************************************************************************/ + + +static const sEnumerationElement APTLightBeaconColorType[] = +{ + { 0, "None" }, + { 1, "White-green" }, /* land airport */ + { 2, "White-yellow" }, /* seaplane base */ + { 3, "Green-yellow-white" }, /* heliports */ + { 4, "White-white-green" } /* military field */ +}; + +DEFINE_XPLANE_ENUMERATION(APTLightBeaconColorEnumeration, APTLightBeaconColorType); + +class OGRXPlaneAPTLightBeaconLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneAPTLightBeaconLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszName, + double dfLat, + double dfLon, + const char* pszColor); +}; + +/************************************************************************/ +/* OGRXPlaneAPTWindsockLayer */ +/************************************************************************/ + +class OGRXPlaneAPTWindsockLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneAPTWindsockLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszName, + double dfLat, + double dfLon, + int bIsIllumnited); +}; + + +/************************************************************************/ +/* OGRXPlaneTaxiwaySignLayer */ +/************************************************************************/ + +class OGRXPlaneTaxiwaySignLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneTaxiwaySignLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszText, + double dfLat, + double dfLon, + double dfHeading, + int nSize); +}; + +/************************************************************************/ +/* OGRXPlane_VASI_PAPI_WIGWAG_Layer */ +/************************************************************************/ + +static const sEnumerationElement VASI_PAPI_WIGWAG_Type[] = +{ + { 1, "VASI" }, + { 2, "PAPI Left" }, + { 3, "PAPI Right" }, + { 4, "Space Shuttle PAPI" }, + { 5, "Tri-colour VASI" }, + { 6, "Wig-Wag lights" } +}; + +DEFINE_XPLANE_ENUMERATION(VASI_PAPI_WIGWAG_Enumeration, VASI_PAPI_WIGWAG_Type); + +class OGRXPlane_VASI_PAPI_WIGWAG_Layer : public OGRXPlaneLayer +{ + public: + OGRXPlane_VASI_PAPI_WIGWAG_Layer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszRwyNum, + const char* pszObjectType, + double dfLat, + double dfLon, + double dfHeading, + double dfVisualGlidePathAngle); +}; + +/************************************************************************/ +/* OGRXPlaneTaxiLocationLayer */ +/************************************************************************/ + +class OGRXPlaneTaxiLocationLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneTaxiLocationLayer(); + + OGRFeature* AddFeature(const char* pszAptICAO, + double dfLat, + double dfLon, + double dfHeading, + const char* pszLocationType, + const char* pszAirplaneTypes, + const char* pszName); +}; + +typedef enum +{ + APT_V_UNKNOWN = 0, + APT_V_810 = 810, + APT_V_850 = 850, + APT_V_1000 = 1000, +} AptVersion; + +enum +{ + APT_AIRPORT_HEADER = 1, + APT_RUNWAY_TAXIWAY_V_810 = 10, + APT_TOWER = 14, + APT_STARTUP_LOCATION = 15, /* deprecated in V_1000 */ + APT_SEAPLANE_HEADER = 16, + APT_HELIPORT_HEADER = 17, + APT_LIGHT_BEACONS = 18, + APT_WINDSOCKS = 19, + APT_TAXIWAY_SIGNS = 20, + APT_VASI_PAPI_WIGWAG = 21, + APT_ATC_AWOS_ASOS_ATIS = 50, + APT_ATC_CTAF = 51, + APT_ATC_CLD = 52, + APT_ATC_GND = 53, + APT_ATC_TWR = 54, + APT_ATC_APP = 55, + APT_ATC_DEP = 56, + APT_RUNWAY = 100, + APT_WATER_RUNWAY = 101, + APT_HELIPAD = 102, + APT_PAVEMENT_HEADER = 110, + APT_NODE = 111, + APT_NODE_WITH_BEZIER = 112, + APT_NODE_CLOSE = 113, + APT_NODE_CLOSE_WITH_BEZIER = 114, + APT_NODE_END = 115, + APT_NODE_END_WITH_BEZIER = 116, + APT_LINEAR_HEADER = 120, + APT_BOUNDARY_HEADER = 130, + APT_TAXI_LOCATION = 1300, /* added in V_1000 */ +}; + + + +/************************************************************************/ +/* OGRXPlaneAptReader */ +/************************************************************************/ + +class OGRXPlaneAptReader : public OGRXPlaneReader +{ + private: + OGRXPlaneDataSource* poDataSource; + + OGRXPlaneAPTLayer* poAPTLayer; + OGRXPlaneRunwayLayer* poRunwayLayer; + OGRXPlaneStopwayLayer* poStopwayLayer; + OGRXPlaneRunwayThresholdLayer* poRunwayThresholdLayer; + OGRXPlaneWaterRunwayLayer* poWaterRunwayLayer; + OGRXPlaneWaterRunwayThresholdLayer* poWaterRunwayThresholdLayer; + OGRXPlaneHelipadLayer* poHelipadLayer; + OGRXPlaneHelipadPolygonLayer* poHelipadPolygonLayer; + OGRXPlaneTaxiwayRectangleLayer* poTaxiwayRectangleLayer; + OGRXPlanePavementLayer* poPavementLayer; + OGRXPlaneAPTBoundaryLayer* poAPTBoundaryLayer; + OGRXPlaneAPTLinearFeatureLayer* poAPTLinearFeatureLayer; + OGRXPlaneATCFreqLayer* poATCFreqLayer; + OGRXPlaneStartupLocationLayer* poStartupLocationLayer; + OGRXPlaneAPTLightBeaconLayer* poAPTLightBeaconLayer; + OGRXPlaneAPTWindsockLayer* poAPTWindsockLayer; + OGRXPlaneTaxiwaySignLayer* poTaxiwaySignLayer; + OGRXPlane_VASI_PAPI_WIGWAG_Layer* poVASI_PAPI_WIGWAG_Layer; + OGRXPlaneTaxiLocationLayer* poTaxiLocationLayer; + + AptVersion nVersion; + + int bAptHeaderFound; + double dfElevation; + int bControlTower; + CPLString osAptICAO; + CPLString osAptName; + int nAPTType; + + int bTowerFound; + double dfLatTower, dfLonTower; + double dfHeightTower; + CPLString osTowerName; + + int bRunwayFound; + double dfLatFirstRwy , dfLonFirstRwy; + + int bResumeLine; + + private: + OGRXPlaneAptReader(); + + void ParseAptHeaderRecord(); + void ParsePavement(); + void ParseAPTBoundary(); + void ParseAPTLinearFeature(); + void ParseRunwayTaxiwayV810Record(); + void ParseRunwayRecord(); + void ParseWaterRunwayRecord(); + void ParseHelipadRecord(); + void ParseTowerRecord(); + void ParseATCRecord(int nType); + void ParseStartupLocationRecord(); + void ParseLightBeaconRecord(); + void ParseWindsockRecord(); + void ParseTaxiwaySignRecord(); + void ParseVasiPapiWigWagRecord(); + void ParseTaxiLocation(); + + OGRGeometry* FixPolygonTopology(OGRPolygon& polygon); + int ParsePolygonalGeometry(OGRGeometry** ppoGeom); + int ParseLinearGeometry(OGRMultiLineString& multilinestring, int* pbIsValid); + + static void AddBezierCurve (OGRLineString& lineString, + double dfLatA, double dfLonA, + double dfCtrPtLatA, double dfCtrPtLonA, + double dfSymCtrlPtLatB, double dfSymCtrlPtLonB, + double dfLatB, double dfLonB); + static void AddBezierCurve (OGRLineString& lineString, + double dfLatA, double dfLonA, + double dfCtrPtLat, double dfCtrPtLon, + double dfLatB, double dfLonB); + + protected: + virtual void Read(); + + public: + OGRXPlaneAptReader( OGRXPlaneDataSource* poDataSource ); + virtual OGRXPlaneReader* CloneForLayer(OGRXPlaneLayer* poLayer); + virtual int IsRecognizedVersion( const char* pszVersionString); + virtual void Rewind(); +}; + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_awy_reader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_awy_reader.cpp new file mode 100644 index 000000000..1c46aef72 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_awy_reader.cpp @@ -0,0 +1,421 @@ +/****************************************************************************** + * $Id: ogr_xplane_awy_reader.cpp + * + * Project: X-Plane awy.dat file reader + * Purpose: Implements OGRXPlaneAwyReader class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_xplane_awy_reader.h" + +CPL_CVSID("$Id: ogr_xplane_awy_reader.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* OGRXPlaneCreateAwyFileReader */ +/************************************************************************/ + +OGRXPlaneReader* OGRXPlaneCreateAwyFileReader( OGRXPlaneDataSource* poDataSource ) +{ + OGRXPlaneReader* poReader = new OGRXPlaneAwyReader(poDataSource); + return poReader; +} + + +/************************************************************************/ +/* OGRXPlaneAwyReader() */ +/************************************************************************/ +OGRXPlaneAwyReader::OGRXPlaneAwyReader() +{ + poAirwaySegmentLayer = NULL; + poAirwayIntersectionLayer = NULL; +} + +/************************************************************************/ +/* OGRXPlaneAwyReader() */ +/************************************************************************/ + +OGRXPlaneAwyReader::OGRXPlaneAwyReader( OGRXPlaneDataSource* poDataSource ) +{ + poAirwaySegmentLayer = new OGRXPlaneAirwaySegmentLayer(); + poAirwayIntersectionLayer = new OGRXPlaneAirwayIntersectionLayer(); + + poDataSource->RegisterLayer(poAirwaySegmentLayer); + poDataSource->RegisterLayer(poAirwayIntersectionLayer); +} + +/************************************************************************/ +/* CloneForLayer() */ +/************************************************************************/ + +OGRXPlaneReader* OGRXPlaneAwyReader::CloneForLayer(OGRXPlaneLayer* poLayer) +{ + OGRXPlaneAwyReader* poReader = new OGRXPlaneAwyReader(); + + poReader->poInterestLayer = poLayer; + + SET_IF_INTEREST_LAYER(poAirwaySegmentLayer); + SET_IF_INTEREST_LAYER(poAirwayIntersectionLayer); + + if (pszFilename) + { + poReader->pszFilename = CPLStrdup(pszFilename); + poReader->fp = VSIFOpenL( pszFilename, "rt" ); + } + + return poReader; +} + + +/************************************************************************/ +/* IsRecognizedVersion() */ +/************************************************************************/ + +int OGRXPlaneAwyReader::IsRecognizedVersion( const char* pszVersionString) +{ + return EQUALN(pszVersionString, "640 Version", 11); +} + + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +void OGRXPlaneAwyReader::Read() +{ + const char* pszLine; + while((pszLine = CPLReadLineL(fp)) != NULL) + { + papszTokens = CSLTokenizeString(pszLine); + nTokens = CSLCount(papszTokens); + + nLineNumber ++; + + if (nTokens == 1 && strcmp(papszTokens[0], "99") == 0) + { + CSLDestroy(papszTokens); + papszTokens = NULL; + bEOF = TRUE; + return; + } + else if (nTokens == 0 || assertMinCol(10) == FALSE) + { + CSLDestroy(papszTokens); + papszTokens = NULL; + continue; + } + + ParseRecord(); + + CSLDestroy(papszTokens); + papszTokens = NULL; + + if (poInterestLayer && poInterestLayer->IsEmpty() == FALSE) + return; + } + + papszTokens = NULL; + bEOF = TRUE; +} + +/************************************************************************/ +/* ParseRecord() */ +/************************************************************************/ + +void OGRXPlaneAwyReader::ParseRecord() +{ + const char* pszFirstPointName; + const char* pszSecondPointName; + const char* pszAirwaySegmentName; + double dfLat1, dfLon1; + double dfLat2, dfLon2; + int bIsHigh; + int nBaseFL, nTopFL; + + pszFirstPointName = papszTokens[0]; + RET_IF_FAIL(readLatLon(&dfLat1, &dfLon1, 1)); + pszSecondPointName = papszTokens[3]; + RET_IF_FAIL(readLatLon(&dfLat2, &dfLon2, 4)); + bIsHigh = atoi(papszTokens[6]) == 2; + nBaseFL = atoi(papszTokens[7]); + nTopFL = atoi(papszTokens[8]); + pszAirwaySegmentName = papszTokens[9]; + + if (poAirwayIntersectionLayer) + { + poAirwayIntersectionLayer->AddFeature(pszFirstPointName, dfLat1, dfLon1); + poAirwayIntersectionLayer->AddFeature(pszSecondPointName, dfLat2, dfLon2); + } + + if (poAirwaySegmentLayer) + { +/* + poAirwaySegmentLayer->AddFeature(pszAirwaySegmentName, + pszFirstPointName, + pszSecondPointName, + dfLat1, dfLon1, dfLat2, dfLon2, + bIsHigh, nBaseFL, nTopFL); +*/ + if (strchr(pszAirwaySegmentName, '-')) + { + char** papszSegmentNames = CSLTokenizeString2( pszAirwaySegmentName, "-", CSLT_HONOURSTRINGS ); + int i = 0; + while(papszSegmentNames[i]) + { + poAirwaySegmentLayer->AddFeature(papszSegmentNames[i], + pszFirstPointName, + pszSecondPointName, + dfLat1, dfLon1, dfLat2, dfLon2, + bIsHigh, nBaseFL, nTopFL); + i++; + } + CSLDestroy(papszSegmentNames); + } + else + { + poAirwaySegmentLayer->AddFeature(pszAirwaySegmentName, + pszFirstPointName, + pszSecondPointName, + dfLat1, dfLon1, dfLat2, dfLon2, + bIsHigh, nBaseFL, nTopFL); + } + } +} + + +/************************************************************************/ +/* OGRXPlaneAirwaySegmentLayer() */ +/************************************************************************/ + +OGRXPlaneAirwaySegmentLayer::OGRXPlaneAirwaySegmentLayer() : OGRXPlaneLayer("AirwaySegment") +{ + poFeatureDefn->SetGeomType( wkbLineString ); + + OGRFieldDefn oFieldSegmentName("segment_name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldSegmentName ); + + OGRFieldDefn oFieldPoint1Name("point1_name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldPoint1Name ); + + OGRFieldDefn oFieldPoint2Name("point2_name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldPoint2Name ); + + OGRFieldDefn oFieldIsHigh("is_high", OFTInteger ); + oFieldIsHigh.SetWidth( 1 ); + poFeatureDefn->AddFieldDefn( &oFieldIsHigh ); + + OGRFieldDefn oFieldBase("base_FL", OFTInteger ); + oFieldBase.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldBase ); + + OGRFieldDefn oFieldTop("top_FL", OFTInteger ); + oFieldTop.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldTop ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneAirwaySegmentLayer::AddFeature(const char* pszAirwaySegmentName, + const char* pszFirstPointName, + const char* pszSecondPointName, + double dfLat1, + double dfLon1, + double dfLat2, + double dfLon2, + int bIsHigh, + int nBaseFL, + int nTopFL) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + if (fabs(dfLon1 - dfLon2) < 270) + { + OGRLineString* lineString = new OGRLineString(); + lineString->addPoint(dfLon1, dfLat1); + lineString->addPoint(dfLon2, dfLat2); + poFeature->SetGeometryDirectly( lineString ); + } + else + { + /* Crossing antemeridian */ + OGRMultiLineString* multiLineString = new OGRMultiLineString(); + OGRLineString* lineString1 = new OGRLineString(); + OGRLineString* lineString2 = new OGRLineString(); + double dfLatInt; + lineString1->addPoint(dfLon1, dfLat1); + if (dfLon1 < dfLon2) + { + dfLatInt = dfLat1 + (dfLat2 - dfLat1) * (-180 - dfLon1) / ((dfLon2 - 360) - dfLon1); + lineString1->addPoint(-180, dfLatInt); + lineString2->addPoint(180, dfLatInt); + } + else + { + dfLatInt = dfLat1 + (dfLat2 - dfLat1) * (180 - dfLon1) / ((dfLon2 + 360) - dfLon1); + lineString1->addPoint(180, dfLatInt); + lineString2->addPoint(-180, dfLatInt); + } + lineString2->addPoint(dfLon2, dfLat2); + multiLineString->addGeometryDirectly( lineString1 ); + multiLineString->addGeometryDirectly( lineString2 ); + poFeature->SetGeometryDirectly( multiLineString ); + } + poFeature->SetField( nCount++, pszAirwaySegmentName ); + poFeature->SetField( nCount++, pszFirstPointName ); + poFeature->SetField( nCount++, pszSecondPointName ); + poFeature->SetField( nCount++, bIsHigh ); + poFeature->SetField( nCount++, nBaseFL ); + poFeature->SetField( nCount++, nTopFL ); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* EqualAirwayIntersectionFeature */ +/************************************************************************/ + +static int EqualAirwayIntersectionFeatureFunc(const void* _feature1, const void* _feature2) +{ + OGRFeature* feature1 = (OGRFeature*)_feature1; + OGRFeature* feature2 = (OGRFeature*)_feature2; + if (strcmp(feature1->GetFieldAsString(0), feature2->GetFieldAsString(0)) == 0) + { + OGRPoint* point1 = (OGRPoint*) feature1->GetGeometryRef(); + OGRPoint* point2 = (OGRPoint*) feature2->GetGeometryRef(); + return (point1->getX() == point2->getX() && point1->getY() == point2->getY()); + } + return FALSE; +} + + +/************************************************************************/ +/* OGRXPlaneAirwayHashDouble() */ +/************************************************************************/ + +static unsigned long OGRXPlaneAirwayHashDouble(const double& dfVal) +{ + /* To make a long story short, we must copy the double into */ + /* an array in order to respect C strict-aliasing rule */ + /* We can't directly cast into an unsigned int* */ + /* See #2521 for the longer version */ + unsigned int anValue[2]; + memcpy(anValue, &dfVal, sizeof(double)); + return anValue[0] ^ anValue[1]; +} + +/************************************************************************/ +/* HashAirwayIntersectionFeatureFunc */ +/************************************************************************/ + +static unsigned long HashAirwayIntersectionFeatureFunc(const void* _feature) +{ + OGRFeature* feature = (OGRFeature*)_feature; + OGRPoint* point = (OGRPoint*) feature->GetGeometryRef(); + unsigned long hash = CPLHashSetHashStr((unsigned char*)feature->GetFieldAsString(0)); + const double x = point->getX(); + const double y = point->getY(); + return hash ^ OGRXPlaneAirwayHashDouble(x) ^ OGRXPlaneAirwayHashDouble(y); +} + +/************************************************************************/ +/* FreeAirwayIntersectionFeatureFunc */ +/************************************************************************/ + +static void FreeAirwayIntersectionFeatureFunc(void* _feature) +{ + delete (OGRFeature*)_feature; +} + +/************************************************************************/ +/* OGRXPlaneAirwayIntersectionLayer() */ +/************************************************************************/ + +OGRXPlaneAirwayIntersectionLayer::OGRXPlaneAirwayIntersectionLayer() : OGRXPlaneLayer("AirwayIntersection") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldName("name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + poSet = CPLHashSetNew(HashAirwayIntersectionFeatureFunc, + EqualAirwayIntersectionFeatureFunc, + FreeAirwayIntersectionFeatureFunc); +} + +/************************************************************************/ +/* ~OGRXPlaneAirwayIntersectionLayer() */ +/************************************************************************/ + +OGRXPlaneAirwayIntersectionLayer::~OGRXPlaneAirwayIntersectionLayer() +{ + CPLHashSetDestroy(poSet); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneAirwayIntersectionLayer::AddFeature(const char* pszIntersectionName, + double dfLat, + double dfLon) +{ + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( 0, pszIntersectionName ); + + if (CPLHashSetLookup(poSet, poFeature) == NULL) + { + CPLHashSetInsert(poSet, poFeature->Clone()); + RegisterFeature(poFeature); + + return poFeature; + } + else + { + delete poFeature; + return NULL; + } +} + +/************************************************************************/ +/* ResetReading() */ +/************************************************************************/ + +void OGRXPlaneAirwayIntersectionLayer::ResetReading() +{ + if (poReader) + { + CPLHashSetDestroy(poSet); + poSet = CPLHashSetNew(HashAirwayIntersectionFeatureFunc, + EqualAirwayIntersectionFeatureFunc, + FreeAirwayIntersectionFeatureFunc); + } + + OGRXPlaneLayer::ResetReading(); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_awy_reader.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_awy_reader.h new file mode 100644 index 000000000..78f07fa6a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_awy_reader.h @@ -0,0 +1,102 @@ +/****************************************************************************** + * $Id: ogr_xplane_awy_reader.cpp + * + * Project: X-Plane awy.dat file reader header + * Purpose: Definition of classes for X-Plane awy.dat file reader + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_XPLANE_AWY_READER_H_INCLUDED +#define _OGR_XPLANE_AWY_READER_H_INCLUDED + +#include "ogr_xplane.h" +#include "ogr_xplane_reader.h" + +#include "cpl_hash_set.h" + +/************************************************************************/ +/* OGRXPlaneAirwaySegmentLayer */ +/************************************************************************/ + + +class OGRXPlaneAirwaySegmentLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneAirwaySegmentLayer(); + OGRFeature* AddFeature(const char* pszAirwaySegmentName, + const char* pszFirstPointName, + const char* pszSecondPointName, + double dfLat1, + double dfLon1, + double dfLat2, + double dfLon2, + int bIsHigh, + int nBaseFL, + int nTopFL); +}; + +/************************************************************************/ +/* OGRXPlaneAirwayIntersectionLayer */ +/************************************************************************/ + +class OGRXPlaneAirwayIntersectionLayer : public OGRXPlaneLayer +{ + private: + CPLHashSet* poSet; + + public: + OGRXPlaneAirwayIntersectionLayer(); + ~OGRXPlaneAirwayIntersectionLayer(); + + OGRFeature* AddFeature(const char* pszIntersectionName, + double dfLat, + double dfLon); + + virtual void ResetReading(); +}; + +/************************************************************************/ +/* OGRXPlaneAwyReader */ +/************************************************************************/ + +class OGRXPlaneAwyReader : public OGRXPlaneReader +{ + private: + OGRXPlaneAirwaySegmentLayer* poAirwaySegmentLayer; + OGRXPlaneAirwayIntersectionLayer* poAirwayIntersectionLayer; + + private: + OGRXPlaneAwyReader(); + void ParseRecord(); + + protected: + virtual void Read(); + + public: + OGRXPlaneAwyReader( OGRXPlaneDataSource* poDataSource ); + virtual OGRXPlaneReader* CloneForLayer(OGRXPlaneLayer* poLayer); + virtual int IsRecognizedVersion( const char* pszVersionString); +}; + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_fix_reader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_fix_reader.cpp new file mode 100644 index 000000000..373128ed3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_fix_reader.cpp @@ -0,0 +1,182 @@ +/****************************************************************************** + * $Id: ogr_xplane_fix_reader.cpp + * + * Project: X-Plane fix.dat file reader + * Purpose: Implements OGRXPlaneFixReader class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_xplane_fix_reader.h" + +CPL_CVSID("$Id: ogr_xplane_fix_reader.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/************************************************************************/ +/* OGRXPlaneCreateFixFileReader */ +/************************************************************************/ + +OGRXPlaneReader* OGRXPlaneCreateFixFileReader( OGRXPlaneDataSource* poDataSource ) +{ + OGRXPlaneReader* poReader = new OGRXPlaneFixReader(poDataSource); + return poReader; +} + + +/************************************************************************/ +/* OGRXPlaneFixReader() */ +/************************************************************************/ +OGRXPlaneFixReader::OGRXPlaneFixReader() +{ + poFIXLayer = NULL; +} + +/************************************************************************/ +/* OGRXPlaneFixReader() */ +/************************************************************************/ + +OGRXPlaneFixReader::OGRXPlaneFixReader( OGRXPlaneDataSource* poDataSource ) +{ + poFIXLayer = new OGRXPlaneFIXLayer(); + + poDataSource->RegisterLayer(poFIXLayer); +} + +/************************************************************************/ +/* CloneForLayer() */ +/************************************************************************/ + +OGRXPlaneReader* OGRXPlaneFixReader::CloneForLayer(OGRXPlaneLayer* poLayer) +{ + OGRXPlaneFixReader* poReader = new OGRXPlaneFixReader(); + + poReader->poInterestLayer = poLayer; + + SET_IF_INTEREST_LAYER(poFIXLayer); + + if (pszFilename) + { + poReader->pszFilename = CPLStrdup(pszFilename); + poReader->fp = VSIFOpenL( pszFilename, "rt" ); + } + + return poReader; +} + +/************************************************************************/ +/* IsRecognizedVersion() */ +/************************************************************************/ + +int OGRXPlaneFixReader::IsRecognizedVersion( const char* pszVersionString) +{ + return EQUALN(pszVersionString, "600 Version", 11); +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +void OGRXPlaneFixReader::Read() +{ + const char* pszLine; + while((pszLine = CPLReadLineL(fp)) != NULL) + { + papszTokens = CSLTokenizeString(pszLine); + nTokens = CSLCount(papszTokens); + + nLineNumber ++; + + if (nTokens == 1 && strcmp(papszTokens[0], "99") == 0) + { + CSLDestroy(papszTokens); + papszTokens = NULL; + bEOF = TRUE; + return; + } + else if (nTokens == 0 || assertMinCol(3) == FALSE) + { + CSLDestroy(papszTokens); + papszTokens = NULL; + continue; + } + + ParseRecord(); + + CSLDestroy(papszTokens); + papszTokens = NULL; + + if (poInterestLayer && poInterestLayer->IsEmpty() == FALSE) + return; + } + + papszTokens = NULL; + bEOF = TRUE; +} + +/************************************************************************/ +/* ParseRecord() */ +/************************************************************************/ + +void OGRXPlaneFixReader::ParseRecord() +{ + double dfLat, dfLon; + CPLString osName; + + RET_IF_FAIL(readLatLon(&dfLat, &dfLon, 0)); + osName = readStringUntilEnd(2); + + if (poFIXLayer) + poFIXLayer->AddFeature(osName, dfLat, dfLon); +} + + +/************************************************************************/ +/* OGRXPlaneFIXLayer() */ +/************************************************************************/ + +OGRXPlaneFIXLayer::OGRXPlaneFIXLayer() : OGRXPlaneLayer("FIX") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldName("fix_name", OFTString ); + oFieldName.SetPrecision(5); + poFeatureDefn->AddFieldDefn( &oFieldName ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneFIXLayer::AddFeature(const char* pszFixName, + double dfLat, + double dfLon) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, pszFixName ); + + RegisterFeature(poFeature); + + return poFeature; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_fix_reader.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_fix_reader.h new file mode 100644 index 000000000..8772eda33 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_fix_reader.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * $Id: ogr_xplane_fix_reader.cpp + * + * Project: X-Plane fix.dat file reader header + * Purpose: Definition of classes for X-Plane fix.dat file reader + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_XPLANE_FIX_READER_H_INCLUDED +#define _OGR_XPLANE_FIX_READER_H_INCLUDED + +#include "ogr_xplane.h" +#include "ogr_xplane_reader.h" + +/************************************************************************/ +/* OGRXPlaneFIXLayer */ +/************************************************************************/ + + +class OGRXPlaneFIXLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneFIXLayer(); + OGRFeature* AddFeature(const char* pszFixName, + double dfLat, + double dfLon); +}; + +/************************************************************************/ +/* OGRXPlaneFixReader */ +/************************************************************************/ + +class OGRXPlaneFixReader : public OGRXPlaneReader +{ + private: + OGRXPlaneFIXLayer* poFIXLayer; + + private: + OGRXPlaneFixReader(); + void ParseRecord(); + + protected: + virtual void Read(); + + public: + OGRXPlaneFixReader( OGRXPlaneDataSource* poDataSource ); + virtual OGRXPlaneReader* CloneForLayer(OGRXPlaneLayer* poLayer); + virtual int IsRecognizedVersion( const char* pszVersionString); +}; + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_geo_utils.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_geo_utils.cpp new file mode 100644 index 000000000..272732250 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_geo_utils.cpp @@ -0,0 +1,173 @@ +/****************************************************************************** + * $Id: ogr_xplane_geo_utils.cpp + * + * Project: X-Plane aeronautical data reader + * Purpose: Geo-computations + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_xplane_geo_utils.h" +#include +#include "cpl_port.h" + +CPL_CVSID("$Id: ogr_xplane_geo_utils.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +#ifndef M_PI +# define M_PI 3.1415926535897932384626433832795 +#endif + +#define RAD2METER ((180./M_PI)*60.*1852.) +#define METER2RAD (1/RAD2METER) + +#define DEG2RAD (M_PI/180.) +#define RAD2DEG (1/DEG2RAD) + +static +double OGRXPlane_Safe_acos(double x) +{ + if (x > 1) + x = 1; + else if (x < -1) + x = -1; + return acos(x); +} + +/************************************************************************/ +/* OGRXPlane_Distance() */ +/************************************************************************/ + +double OGRXPlane_Distance(double LatA_deg, double LonA_deg, + double LatB_deg, double LonB_deg) +{ + double LatA_rad, LatB_rad; + double cosa, cosb, sina, sinb, cosP; + double cos_angle; + + cosP = cos((LonB_deg - LonA_deg) * DEG2RAD); + LatA_rad = LatA_deg * DEG2RAD; + LatB_rad = LatB_deg * DEG2RAD; + cosa = cos(LatA_rad); + sina = sin(LatA_rad); + cosb = cos(LatB_rad); + sinb = sin(LatB_rad); + cos_angle = sina*sinb + cosa*cosb*cosP; + return OGRXPlane_Safe_acos(cos_angle) * RAD2METER; +} + +/************************************************************************/ +/* OGRXPlane_Track() */ +/************************************************************************/ + +double OGRXPlane_Track(double LatA_deg, double LonA_deg, + double LatB_deg, double LonB_deg) +{ + if (fabs (LatA_deg - 90) < 1e-10 || fabs (LatB_deg + 90) < 1e-10) + { + return 180; + } + else if (fabs (LatA_deg + 90) < 1e-10 || fabs (LatB_deg - 90) < 1e-10) + { + return 0; + } + else + { + double cos_LatA, sin_LatA, diffG, cos_diffG, sin_diffG; + double denom; + double track; + double LatA_rad = LatA_deg * DEG2RAD; + double LatB_rad = LatB_deg * DEG2RAD; + + cos_LatA = cos(LatA_rad); + sin_LatA = sin(LatA_rad); + + diffG = (LonA_deg - LonB_deg) * DEG2RAD; + cos_diffG = cos(diffG); + sin_diffG = sin(diffG); + + denom = sin_LatA * cos_diffG - cos_LatA * tan(LatB_rad); + + track = atan (sin_diffG / denom) * RAD2DEG; + + if (denom > 0.0) + { + track = 180 + track; + } + else if (track < 0) + { + track = 360 + track; + } + + return track; + } +} + +/************************************************************************/ +/* OGRXPlane_ExtendPosition() */ +/************************************************************************/ + +int OGRXPlane_ExtendPosition(double dfLatA_deg, double dfLonA_deg, + double dfDistance, double dfHeading, + double* pdfLatB_deg, double* pdfLonB_deg) +{ + double dfHeadingRad, cos_Heading, sin_Heading; + double dfDistanceRad, cos_Distance, sin_Distance; + double dfLatA_rad, cos_complement_LatA, sin_complement_LatA; + double cos_complement_latB, complement_latB; + double Cos_dG, dG_deg; + + dfHeadingRad = dfHeading * DEG2RAD; + cos_Heading = cos (dfHeadingRad); + sin_Heading = sin (dfHeadingRad); + + dfDistanceRad = dfDistance * METER2RAD; + cos_Distance = cos (dfDistanceRad); + sin_Distance = sin (dfDistanceRad); + + dfLatA_rad = dfLatA_deg * DEG2RAD; + cos_complement_LatA = sin(dfLatA_rad); + sin_complement_LatA = cos(dfLatA_rad); + + cos_complement_latB = cos_Distance * cos_complement_LatA + + sin_Distance * sin_complement_LatA * cos_Heading; + + complement_latB = OGRXPlane_Safe_acos(cos_complement_latB); + + Cos_dG = (cos_Distance - cos_complement_latB * cos_complement_LatA) / + (sin(complement_latB) * sin_complement_LatA); + *pdfLatB_deg = 90 - complement_latB * RAD2DEG; + + dG_deg = OGRXPlane_Safe_acos(Cos_dG) * RAD2DEG; + + if (sin_Heading < 0) + *pdfLonB_deg = dfLonA_deg - dG_deg; + else + *pdfLonB_deg = dfLonA_deg + dG_deg; + + if (*pdfLonB_deg > 180) + *pdfLonB_deg -= 360; + else if (*pdfLonB_deg <= -180) + *pdfLonB_deg += 360; + + return 1; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_geo_utils.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_geo_utils.h new file mode 100644 index 000000000..3220fd86c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_geo_utils.h @@ -0,0 +1,44 @@ +/****************************************************************************** + * $Id: ogr_xplane_geo_utils.h $ + * + * Project: X-Plane aeronautical data reader + * Purpose: Definition of geo-computation functions + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_XPLANE_GEO_UTILS_H_INCLUDED +#define _OGR_XPLANE_GEO_UTILS_H_INCLUDED + +double OGRXPlane_Distance(double dfLatA_deg, double dfLonA_deg, + double dfLatB_deg, double dfLonB_deg); + +double OGRXPlane_Track(double dfLatA_deg, double dfLonA_deg, + double dfLatB_deg, double dfLonB_deg); + +int OGRXPlane_ExtendPosition(double dfLatA_deg, double dfLonA_deg, + double dfDistance, double dfHeading, + double* pdfLatB_deg, double* pdfLonB_deg); + + +#endif /* ndef _OGR_XPLANE_GEO_UTILS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_nav_reader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_nav_reader.cpp new file mode 100644 index 000000000..1712aad78 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_nav_reader.cpp @@ -0,0 +1,901 @@ +/****************************************************************************** + * $Id: ogr_xplane_nav_reader.cpp + * + * Project: X-Plane nav.dat file reader + * Purpose: Implements OGRXPlaneNavReader class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_xplane_nav_reader.h" + +CPL_CVSID("$Id: ogr_xplane_nav_reader.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRXPlaneCreateNavFileReader */ +/************************************************************************/ + +OGRXPlaneReader* OGRXPlaneCreateNavFileReader( OGRXPlaneDataSource* poDataSource ) +{ + OGRXPlaneReader* poReader = new OGRXPlaneNavReader(poDataSource); + return poReader; +} + + +/************************************************************************/ +/* OGRXPlaneNavReader() */ +/************************************************************************/ +OGRXPlaneNavReader::OGRXPlaneNavReader() +{ + poILSLayer = NULL; + poVORLayer = NULL; + poNDBLayer = NULL; + poGSLayer = NULL; + poMarkerLayer = NULL; + poDMELayer = NULL; + poDMEILSLayer = NULL; +} + +/************************************************************************/ +/* OGRXPlaneNavReader() */ +/************************************************************************/ + +OGRXPlaneNavReader::OGRXPlaneNavReader( OGRXPlaneDataSource* poDataSource ) +{ + poILSLayer = new OGRXPlaneILSLayer(); + poVORLayer = new OGRXPlaneVORLayer(); + poNDBLayer = new OGRXPlaneNDBLayer(); + poGSLayer = new OGRXPlaneGSLayer(); + poMarkerLayer = new OGRXPlaneMarkerLayer(); + poDMELayer = new OGRXPlaneDMELayer(); + poDMEILSLayer = new OGRXPlaneDMEILSLayer(); + + poDataSource->RegisterLayer(poILSLayer); + poDataSource->RegisterLayer(poVORLayer); + poDataSource->RegisterLayer(poNDBLayer); + poDataSource->RegisterLayer(poGSLayer); + poDataSource->RegisterLayer(poMarkerLayer); + poDataSource->RegisterLayer(poDMELayer); + poDataSource->RegisterLayer(poDMEILSLayer); +} + +/************************************************************************/ +/* CloneForLayer() */ +/************************************************************************/ + +OGRXPlaneReader* OGRXPlaneNavReader::CloneForLayer(OGRXPlaneLayer* poLayer) +{ + OGRXPlaneNavReader* poReader = new OGRXPlaneNavReader(); + + poReader->poInterestLayer = poLayer; + + SET_IF_INTEREST_LAYER(poILSLayer); + SET_IF_INTEREST_LAYER(poVORLayer); + SET_IF_INTEREST_LAYER(poNDBLayer); + SET_IF_INTEREST_LAYER(poGSLayer); + SET_IF_INTEREST_LAYER(poMarkerLayer); + SET_IF_INTEREST_LAYER(poDMELayer); + SET_IF_INTEREST_LAYER(poDMEILSLayer); + + if (pszFilename) + { + poReader->pszFilename = CPLStrdup(pszFilename); + poReader->fp = VSIFOpenL( pszFilename, "rb" ); + } + + return poReader; +} + +/************************************************************************/ +/* IsRecognizedVersion() */ +/************************************************************************/ + +int OGRXPlaneNavReader::IsRecognizedVersion( const char* pszVersionString) +{ + return EQUALN(pszVersionString, "810 Version", 11) || + EQUALN(pszVersionString, "740 Version", 11); +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +void OGRXPlaneNavReader::Read() +{ + const char* pszLine; + while((pszLine = CPLReadLineL(fp)) != NULL) + { + int nType; + papszTokens = CSLTokenizeString(pszLine); + nTokens = CSLCount(papszTokens); + + nLineNumber ++; + + if (nTokens == 1 && strcmp(papszTokens[0], "99") == 0) + { + CSLDestroy(papszTokens); + papszTokens = NULL; + bEOF = TRUE; + return; + } + else if (nTokens == 0 || assertMinCol(9) == FALSE) + { + CSLDestroy(papszTokens); + papszTokens = NULL; + continue; + } + + nType = atoi(papszTokens[0]); + if (!((nType >= NAVAID_NDB && nType <= NAVAID_IM) || + nType == NAVAID_DME_COLOC || nType == NAVAID_DME_STANDALONE)) + { + CPLDebug("XPlane", "Line %d : bad feature code '%s'", + nLineNumber, papszTokens[0]); + CSLDestroy(papszTokens); + papszTokens = NULL; + continue; + } + + ParseRecord(nType); + + CSLDestroy(papszTokens); + papszTokens = NULL; + + if (poInterestLayer && poInterestLayer->IsEmpty() == FALSE) + return; + } + + papszTokens = NULL; + bEOF = TRUE; +} + +/************************************************************************/ +/* ParseRecord() */ +/************************************************************************/ + +void OGRXPlaneNavReader::ParseRecord(int nType) +{ + double dfVal, dfLat, dfLon, dfElevation, dfFrequency, dfRange; + double dfSlavedVariation = 0, dfTrueHeading = 0, + dfDMEBias = 0, dfSlope = 0; + char* pszNavaidId; + + RET_IF_FAIL(readLatLon(&dfLat, &dfLon, 1)); + + /* feet to meter */ + RET_IF_FAIL(readDoubleWithBoundsAndConversion(&dfElevation, 3, "elevation", FEET_TO_METER, -1000., 10000.)); + + RET_IF_FAIL(readDouble(&dfFrequency, 4, "frequency")); + /* NDB frequencies are in kHz. Others must be divided by 100 */ + /* to get a frequency in MHz */ + if (nType != NAVAID_NDB) + dfFrequency /= 100.; + + /* nautical miles to kilometer */ + RET_IF_FAIL(readDouble(&dfRange, 5, "range")); + dfRange *= NM_TO_KM; + + pszNavaidId = papszTokens[7]; + + if (nType == NAVAID_NDB) + { + const char* pszSubType = ""; + CPLString osNavaidName; + if (EQUAL(papszTokens[nTokens-1], "NDB") || + EQUAL(papszTokens[nTokens-1], "LOM") || + EQUAL(papszTokens[nTokens-1], "NDB-DME")) + { + pszSubType = papszTokens[nTokens-1]; + nTokens--; + } + else + { + CPLDebug("XPlane", "Unexpected NDB subtype : %s", papszTokens[nTokens-1]); + } + + osNavaidName = readStringUntilEnd(8); + + if (poNDBLayer) + poNDBLayer->AddFeature(pszNavaidId, osNavaidName, pszSubType, + dfLat, dfLon, + dfElevation, dfFrequency, dfRange); + } + else if (nType == NAVAID_VOR) + { + const char* pszSubType = ""; + CPLString osNavaidName; + + RET_IF_FAIL(readDoubleWithBounds(&dfSlavedVariation, 6, "slaved variation", -180., 180.)); + + if (EQUAL(papszTokens[nTokens-1], "VOR") || + EQUAL(papszTokens[nTokens-1], "VORTAC") || + EQUAL(papszTokens[nTokens-1], "VOR-DME")) + { + pszSubType = papszTokens[nTokens-1]; + nTokens--; + } + else + { + CPLDebug("XPlane", "Unexpected VOR subtype : %s", papszTokens[nTokens-1]); + } + + osNavaidName = readStringUntilEnd(8); + + if (poVORLayer) + poVORLayer->AddFeature(pszNavaidId, osNavaidName, pszSubType, + dfLat, dfLon, + dfElevation, dfFrequency, dfRange, dfSlavedVariation); + } + else if (nType == NAVAID_LOC_ILS || nType == NAVAID_LOC_STANDALONE) + { + const char* pszAptICAO, * pszRwyNum, * pszSubType; + + RET_IF_FAIL(readDoubleWithBounds(&dfTrueHeading, 6, "true heading", 0., 360.)); + + RET_IF_FAIL(assertMinCol(11)); + + pszAptICAO = papszTokens[8]; + pszRwyNum = papszTokens[9]; + pszSubType = papszTokens[10]; + + if (EQUAL(pszSubType, "ILS-cat-I") || + EQUAL(pszSubType, "ILS-cat-II") || + EQUAL(pszSubType, "ILS-cat-III") || + EQUAL(pszSubType, "LOC") || + EQUAL(pszSubType, "LDA") || + EQUAL(pszSubType, "SDF") || + EQUAL(pszSubType, "IGS") || + EQUAL(pszSubType, "LDA-GS")) + { + if (poILSLayer) + poILSLayer->AddFeature(pszNavaidId, pszAptICAO, pszRwyNum, pszSubType, + dfLat, dfLon, + dfElevation, dfFrequency, dfRange, dfTrueHeading); + } + else + { + CPLDebug("XPlane", "Line %d : invalid localizer subtype: '%s'", + nLineNumber, pszSubType); + return; + } + } + else if (nType == NAVAID_GS) + { + const char* pszAptICAO, * pszRwyNum, * pszSubType; + + RET_IF_FAIL(readDouble(&dfVal, 6, "slope & heading")); + dfSlope = (int)(dfVal / 1000) / 100.; + dfTrueHeading = dfVal - dfSlope * 100000; + if (dfTrueHeading < 0 || dfTrueHeading > 360) + { + CPLDebug("XPlane", "Line %d : invalid true heading '%f'", + nLineNumber, dfTrueHeading); + return; + } + + RET_IF_FAIL(assertMinCol(11)); + + pszAptICAO = papszTokens[8]; + pszRwyNum = papszTokens[9]; + pszSubType = papszTokens[10]; + + if (EQUAL(pszSubType, "GS") ) + { + if (poGSLayer) + poGSLayer->AddFeature(pszNavaidId, pszAptICAO, pszRwyNum, + dfLat, dfLon, + dfElevation, dfFrequency, dfRange, dfTrueHeading, dfSlope); + } + else + { + CPLDebug("XPlane", "Line %d : invalid glideslope subtype: '%s'", + nLineNumber, pszSubType); + return; + } + } + else if (nType == NAVAID_OM || nType == NAVAID_MM || nType == NAVAID_IM) + { + const char* pszAptICAO, * pszRwyNum, * pszSubType; + + RET_IF_FAIL(readDoubleWithBounds(&dfTrueHeading, 6, "true heading", 0., 360.)); + + RET_IF_FAIL(assertMinCol(11)); + + pszAptICAO = papszTokens[8]; + pszRwyNum = papszTokens[9]; + pszSubType = papszTokens[10]; + + if (EQUAL(pszSubType, "OM") || + EQUAL(pszSubType, "MM") || + EQUAL(pszSubType, "IM") ) + { + if (poMarkerLayer) + poMarkerLayer->AddFeature(pszAptICAO, pszRwyNum, pszSubType, + dfLat, dfLon, + dfElevation, dfTrueHeading); + } + else + { + CPLDebug("XPlane", "Line %d : invalid localizer marker subtype: '%s'", + nLineNumber, pszSubType); + return; + } + } + else if (nType == NAVAID_DME_COLOC || nType == NAVAID_DME_STANDALONE) + { + const char* pszSubType = ""; + CPLString osNavaidName; + + RET_IF_FAIL(readDouble(&dfDMEBias, 6, "DME bias")); + dfDMEBias *= NM_TO_KM; + + if (EQUAL(papszTokens[nTokens-1], "DME-ILS")) + { + char* pszAptICAO, * pszRwyNum /* , * pszSubType */; + if (nTokens != 11) + { + CPLDebug("XPlane", "Line %d : not enough columns : %d", + nLineNumber, nTokens); + return; + } + + pszAptICAO = papszTokens[8]; + pszRwyNum = papszTokens[9]; + /* pszSubType = papszTokens[10]; */ + + if (poDMEILSLayer) + poDMEILSLayer->AddFeature(pszNavaidId, pszAptICAO, pszRwyNum, + dfLat, dfLon, + dfElevation, dfFrequency, dfRange, dfDMEBias); + } + else + { + if (EQUAL(papszTokens[nTokens-1], "DME")) + { + nTokens--; + if (EQUAL(papszTokens[nTokens-1], "VORTAC") || + EQUAL(papszTokens[nTokens-1], "VOR-DME") || + EQUAL(papszTokens[nTokens-1], "TACAN") || + EQUAL(papszTokens[nTokens-1], "NDB-DME")) + { + /* pszSubType = papszTokens[nTokens-1]; */ + nTokens--; + } + } + else + { + CPLDebug("XPlane", "Line %d : Unexpected DME subtype : %s", + nLineNumber, papszTokens[nTokens-1]); + } + + osNavaidName = readStringUntilEnd(8); + + if (poDMELayer) + poDMELayer->AddFeature(pszNavaidId, osNavaidName, pszSubType, + dfLat, dfLon, + dfElevation, dfFrequency, dfRange, dfDMEBias); + } + } + else + { + CPLAssert(0); + } + +} + + +/************************************************************************/ +/* OGRXPlaneILSLayer() */ +/************************************************************************/ + +OGRXPlaneILSLayer::OGRXPlaneILSLayer() : OGRXPlaneLayer("ILS") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldID("navaid_id", OFTString ); + oFieldID.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldID ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldRwyNum("rwy_num", OFTString ); + oFieldRwyNum.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRwyNum ); + + OGRFieldDefn oFieldSubType("subtype", OFTString ); + oFieldSubType.SetWidth( 10 ); + poFeatureDefn->AddFieldDefn( &oFieldSubType ); + + OGRFieldDefn oFieldElev("elevation_m", OFTReal ); + oFieldElev.SetWidth( 8 ); + oFieldElev.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldElev ); + + OGRFieldDefn oFieldFreq("freq_mhz", OFTReal ); + oFieldFreq.SetWidth( 7 ); + oFieldFreq.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldFreq ); + + OGRFieldDefn oFieldRange("range_km", OFTReal ); + oFieldRange.SetWidth( 7 ); + oFieldRange.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRange ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneILSLayer::AddFeature(const char* pszNavaidID, + const char* pszAptICAO, + const char* pszRwyNum, + const char* pszSubType, + double dfLat, + double dfLon, + double dfEle, + double dfFreq, + double dfRange, + double dfTrueHeading) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, pszNavaidID ); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszRwyNum ); + poFeature->SetField( nCount++, pszSubType ); + poFeature->SetField( nCount++, dfEle ); + poFeature->SetField( nCount++, dfFreq ); + poFeature->SetField( nCount++, dfRange ); + poFeature->SetField( nCount++, dfTrueHeading ); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneVORLayer() */ +/************************************************************************/ + + +OGRXPlaneVORLayer::OGRXPlaneVORLayer() : OGRXPlaneLayer("VOR") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldID("navaid_id", OFTString ); + oFieldID.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldID ); + + OGRFieldDefn oFieldName("navaid_name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oFieldSubType("subtype", OFTString ); + oFieldSubType.SetWidth( 10 ); + poFeatureDefn->AddFieldDefn( &oFieldSubType ); + + OGRFieldDefn oFieldElev("elevation_m", OFTReal ); + oFieldElev.SetWidth( 8 ); + oFieldElev.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldElev ); + + OGRFieldDefn oFieldFreq("freq_mhz", OFTReal ); + oFieldFreq.SetWidth( 7 ); + oFieldFreq.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldFreq ); + + OGRFieldDefn oFieldRange("range_km", OFTReal ); + oFieldRange.SetWidth( 7 ); + oFieldRange.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRange ); + + OGRFieldDefn oFieldSlavedVariation("slaved_variation_deg", OFTReal ); + oFieldSlavedVariation.SetWidth( 6 ); + oFieldSlavedVariation.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldSlavedVariation ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneVORLayer::AddFeature(const char* pszNavaidID, + const char* pszNavaidName, + const char* pszSubType, + double dfLat, + double dfLon, + double dfEle, + double dfFreq, + double dfRange, + double dfSlavedVariation) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, pszNavaidID ); + poFeature->SetField( nCount++, pszNavaidName ); + poFeature->SetField( nCount++, pszSubType ); + poFeature->SetField( nCount++, dfEle ); + poFeature->SetField( nCount++, dfFreq ); + poFeature->SetField( nCount++, dfRange ); + poFeature->SetField( nCount++, dfSlavedVariation ); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneNDBLayer() */ +/************************************************************************/ + +OGRXPlaneNDBLayer::OGRXPlaneNDBLayer() : OGRXPlaneLayer("NDB") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldID("navaid_id", OFTString ); + oFieldID.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldID ); + + OGRFieldDefn oFieldName("navaid_name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oFieldSubType("subtype", OFTString ); + oFieldSubType.SetWidth( 10 ); + poFeatureDefn->AddFieldDefn( &oFieldSubType ); + + OGRFieldDefn oFieldElev("elevation_m", OFTReal ); + oFieldElev.SetWidth( 8 ); + oFieldElev.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldElev ); + + OGRFieldDefn oFieldFreq("freq_khz", OFTReal ); + oFieldFreq.SetWidth( 7 ); + oFieldFreq.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldFreq ); + + OGRFieldDefn oFieldRange("range_km", OFTReal ); + oFieldRange.SetWidth( 7 ); + oFieldRange.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRange ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneNDBLayer::AddFeature(const char* pszNavaidID, + const char* pszNavaidName, + const char* pszSubType, + double dfLat, + double dfLon, + double dfEle, + double dfFreq, + double dfRange) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, pszNavaidID ); + poFeature->SetField( nCount++, pszNavaidName ); + poFeature->SetField( nCount++, pszSubType ); + poFeature->SetField( nCount++, dfEle ); + poFeature->SetField( nCount++, dfFreq ); + poFeature->SetField( nCount++, dfRange ); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneGSLayer */ +/************************************************************************/ + +OGRXPlaneGSLayer::OGRXPlaneGSLayer() : OGRXPlaneLayer("GS") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldID("navaid_id", OFTString ); + oFieldID.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldID ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldRwyNum("rwy_num", OFTString ); + oFieldRwyNum.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRwyNum ); + + OGRFieldDefn oFieldElev("elevation_m", OFTReal ); + oFieldElev.SetWidth( 8 ); + oFieldElev.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldElev ); + + OGRFieldDefn oFieldFreq("freq_mhz", OFTReal ); + oFieldFreq.SetWidth( 7 ); + oFieldFreq.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldFreq ); + + OGRFieldDefn oFieldRange("range_km", OFTReal ); + oFieldRange.SetWidth( 7 ); + oFieldRange.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRange ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); + + OGRFieldDefn oFieldGlideSlope("glide_slope", OFTReal ); + oFieldGlideSlope.SetWidth( 6 ); + oFieldGlideSlope.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldGlideSlope ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneGSLayer::AddFeature(const char* pszNavaidID, + const char* pszAptICAO, + const char* pszRwyNum, + double dfLat, + double dfLon, + double dfEle, + double dfFreq, + double dfRange, + double dfTrueHeading, + double dfSlope) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, pszNavaidID ); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszRwyNum ); + poFeature->SetField( nCount++, dfEle ); + poFeature->SetField( nCount++, dfFreq ); + poFeature->SetField( nCount++, dfRange ); + poFeature->SetField( nCount++, dfTrueHeading ); + poFeature->SetField( nCount++, dfSlope ); + + RegisterFeature(poFeature); + + return poFeature; +} + + +/************************************************************************/ +/* OGRXPlaneMarkerLayer */ +/************************************************************************/ + +OGRXPlaneMarkerLayer::OGRXPlaneMarkerLayer() : OGRXPlaneLayer("Marker") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldRwyNum("rwy_num", OFTString ); + oFieldRwyNum.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRwyNum ); + + OGRFieldDefn oFieldSubType("subtype", OFTString ); + oFieldSubType.SetWidth( 10 ); + poFeatureDefn->AddFieldDefn( &oFieldSubType ); + + OGRFieldDefn oFieldElev("elevation_m", OFTReal ); + oFieldElev.SetWidth( 8 ); + oFieldElev.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldElev ); + + OGRFieldDefn oFieldTrueHeading("true_heading_deg", OFTReal ); + oFieldTrueHeading.SetWidth( 6 ); + oFieldTrueHeading.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldTrueHeading ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneMarkerLayer::AddFeature(const char* pszAptICAO, + const char* pszRwyNum, + const char* pszSubType, + double dfLat, + double dfLon, + double dfEle, + double dfTrueHeading) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszRwyNum ); + poFeature->SetField( nCount++, pszSubType ); + poFeature->SetField( nCount++, dfEle ); + poFeature->SetField( nCount++, dfTrueHeading ); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneDMEILSLayer */ +/************************************************************************/ + +OGRXPlaneDMEILSLayer::OGRXPlaneDMEILSLayer() : OGRXPlaneLayer("DMEILS") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldID("navaid_id", OFTString ); + oFieldID.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldID ); + + OGRFieldDefn oFieldAptICAO("apt_icao", OFTString ); + oFieldAptICAO.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldAptICAO ); + + OGRFieldDefn oFieldRwyNum("rwy_num", OFTString ); + oFieldRwyNum.SetWidth( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRwyNum ); + + OGRFieldDefn oFieldElev("elevation_m", OFTReal ); + oFieldElev.SetWidth( 8 ); + oFieldElev.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldElev ); + + OGRFieldDefn oFieldFreq("freq_mhz", OFTReal ); + oFieldFreq.SetWidth( 7 ); + oFieldFreq.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldFreq ); + + OGRFieldDefn oFieldRange("range_km", OFTReal ); + oFieldRange.SetWidth( 7 ); + oFieldRange.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRange ); + + OGRFieldDefn oFieldBias("bias_km", OFTReal ); + oFieldBias.SetWidth( 6 ); + oFieldBias.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldBias ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneDMEILSLayer::AddFeature(const char* pszNavaidID, + const char* pszAptICAO, + const char* pszRwyNum, + double dfLat, + double dfLon, + double dfEle, + double dfFreq, + double dfRange, + double dfBias) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, pszNavaidID ); + poFeature->SetField( nCount++, pszAptICAO ); + poFeature->SetField( nCount++, pszRwyNum ); + poFeature->SetField( nCount++, dfEle ); + poFeature->SetField( nCount++, dfFreq ); + poFeature->SetField( nCount++, dfRange ); + poFeature->SetField( nCount++, dfBias ); + + RegisterFeature(poFeature); + + return poFeature; +} + +/************************************************************************/ +/* OGRXPlaneDMELayer */ +/************************************************************************/ + + +OGRXPlaneDMELayer::OGRXPlaneDMELayer() : OGRXPlaneLayer("DME") +{ + poFeatureDefn->SetGeomType( wkbPoint ); + + OGRFieldDefn oFieldID("navaid_id", OFTString ); + oFieldID.SetWidth( 4 ); + poFeatureDefn->AddFieldDefn( &oFieldID ); + + OGRFieldDefn oFieldName("navaid_name", OFTString ); + poFeatureDefn->AddFieldDefn( &oFieldName ); + + OGRFieldDefn oFieldSubType("subtype", OFTString ); + oFieldSubType.SetWidth( 10 ); + poFeatureDefn->AddFieldDefn( &oFieldSubType ); + + OGRFieldDefn oFieldElev("elevation_m", OFTReal ); + oFieldElev.SetWidth( 8 ); + oFieldElev.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldElev ); + + OGRFieldDefn oFieldFreq("freq_mhz", OFTReal ); + oFieldFreq.SetWidth( 7 ); + oFieldFreq.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldFreq ); + + OGRFieldDefn oFieldRange("range_km", OFTReal ); + oFieldRange.SetWidth( 7 ); + oFieldRange.SetPrecision( 3 ); + poFeatureDefn->AddFieldDefn( &oFieldRange ); + + OGRFieldDefn oFieldBias("bias_km", OFTReal ); + oFieldBias.SetWidth( 6 ); + oFieldBias.SetPrecision( 2 ); + poFeatureDefn->AddFieldDefn( &oFieldBias ); +} + +/************************************************************************/ +/* AddFeature() */ +/************************************************************************/ + +OGRFeature* + OGRXPlaneDMELayer::AddFeature(const char* pszNavaidID, + const char* pszNavaidName, + const char* pszSubType, + double dfLat, + double dfLon, + double dfEle, + double dfFreq, + double dfRange, + double dfBias) +{ + int nCount = 0; + OGRFeature* poFeature = new OGRFeature(poFeatureDefn); + poFeature->SetGeometryDirectly( new OGRPoint( dfLon, dfLat ) ); + poFeature->SetField( nCount++, pszNavaidID ); + poFeature->SetField( nCount++, pszNavaidName ); + poFeature->SetField( nCount++, pszSubType ); + poFeature->SetField( nCount++, dfEle ); + poFeature->SetField( nCount++, dfFreq ); + poFeature->SetField( nCount++, dfRange ); + poFeature->SetField( nCount++, dfBias ); + + RegisterFeature(poFeature); + + return poFeature; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_nav_reader.h b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_nav_reader.h new file mode 100644 index 000000000..45b6d9b5d --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_nav_reader.h @@ -0,0 +1,220 @@ +/****************************************************************************** + * $Id: ogr_xplane_nav_reader.cpp + * + * Project: X-Plane nav.dat file reader header + * Purpose: Definition of classes for X-Plane nav.dat file reader + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_XPLANE_NAV_READER_H_INCLUDED +#define _OGR_XPLANE_NAV_READER_H_INCLUDED + +#include "ogr_xplane.h" +#include "ogr_xplane_reader.h" + +/************************************************************************/ +/* OGRXPlaneILSLayer */ +/************************************************************************/ + + +class OGRXPlaneILSLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneILSLayer(); + OGRFeature* AddFeature(const char* pszNavaidID, + const char* pszAptICAO, + const char* pszRwyNum, + const char* pszSubType, + double dfLat, + double dfLon, + double dfEle, + double dfFreq, + double dfRange, + double dfTrueHeading); +}; + +/************************************************************************/ +/* OGRXPlaneVORLayer */ +/************************************************************************/ + + +class OGRXPlaneVORLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneVORLayer(); + OGRFeature* AddFeature(const char* pszNavaidID, + const char* pszNavaidName, + const char* pszSubType, + double dfLat, + double dfLon, + double dfEle, + double dfFreq, + double dfRange, + double dfSlavedVariation); +}; + +/************************************************************************/ +/* OGRXPlaneNDBLayer */ +/************************************************************************/ + + +class OGRXPlaneNDBLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneNDBLayer(); + OGRFeature* AddFeature(const char* pszNavaidID, + const char* pszNavaidName, + const char* pszSubType, + double dfLat, + double dfLon, + double dfEle, + double dfFreq, + double dfRange); +}; + +/************************************************************************/ +/* OGRXPlaneGSLayer */ +/************************************************************************/ + + +class OGRXPlaneGSLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneGSLayer(); + OGRFeature* AddFeature(const char* pszNavaidID, + const char* pszAptICAO, + const char* pszRwyNum, + double dfLat, + double dfLon, + double dfEle, + double dfFreq, + double dfRange, + double dfTrueHeading, + double dfSlope); +}; + +/************************************************************************/ +/* OGRXPlaneMarkerLayer */ +/************************************************************************/ + + +class OGRXPlaneMarkerLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneMarkerLayer(); + OGRFeature* AddFeature(const char* pszAptICAO, + const char* pszRwyNum, + const char* pszSubType, + double dfLat, + double dfLon, + double dfEle, + double dfTrueHeading); +}; + +/************************************************************************/ +/* OGRXPlaneDMEILSLayer */ +/************************************************************************/ + + +class OGRXPlaneDMEILSLayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneDMEILSLayer(); + OGRFeature* AddFeature(const char* pszNavaidID, + const char* pszAptICAO, + const char* pszRwyNum, + double dfLat, + double dfLon, + double dfEle, + double dfFreq, + double dfRange, + double dfDMEBias); +}; + + +/************************************************************************/ +/* OGRXPlaneDMELayer */ +/************************************************************************/ + + +class OGRXPlaneDMELayer : public OGRXPlaneLayer +{ + public: + OGRXPlaneDMELayer(); + OGRFeature* AddFeature(const char* pszNavaidID, + const char* pszNavaidName, + const char* pszSubType, + double dfLat, + double dfLon, + double dfEle, + double dfFreq, + double dfRange, + double dfDMEBias); +}; + + +enum +{ + NAVAID_NDB = 2, + NAVAID_VOR = 3, /* VOR, VORTAC or VOR-DME.*/ + NAVAID_LOC_ILS = 4, /* Localiser that is part of a full ILS */ + NAVAID_LOC_STANDALONE = 5, /* Stand-alone localiser (LOC), also including a LDA (Landing Directional Aid) or SDF (Simplified Directional Facility) */ + NAVAID_GS = 6, /* Glideslope */ + NAVAID_OM = 7, /* Outer marker */ + NAVAID_MM = 8, /* Middle marker */ + NAVAID_IM = 9, /* Inner marker */ + NAVAID_DME_COLOC = 12, /* DME (including the DME element of an ILS, VORTAC or VOR-DME) */ + NAVAID_DME_STANDALONE = 13, /* DME (including the DME element of an NDB-DME) */ +}; + + +/************************************************************************/ +/* OGRXPlaneNavReader */ +/************************************************************************/ + +class OGRXPlaneNavReader : public OGRXPlaneReader +{ + private: + OGRXPlaneILSLayer* poILSLayer; + OGRXPlaneVORLayer* poVORLayer; + OGRXPlaneNDBLayer* poNDBLayer; + OGRXPlaneGSLayer* poGSLayer ; + OGRXPlaneMarkerLayer* poMarkerLayer; + OGRXPlaneDMELayer* poDMELayer; + OGRXPlaneDMEILSLayer* poDMEILSLayer; + + private: + OGRXPlaneNavReader(); + void ParseRecord(int nType); + + protected: + virtual void Read(); + + public: + OGRXPlaneNavReader( OGRXPlaneDataSource* poDataSource ); + virtual OGRXPlaneReader* CloneForLayer(OGRXPlaneLayer* poLayer); + virtual int IsRecognizedVersion( const char* pszVersionString); +}; + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_reader.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_reader.cpp new file mode 100644 index 000000000..40aa20a30 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogr_xplane_reader.cpp @@ -0,0 +1,337 @@ +/****************************************************************************** + * $Id: ogr_xplane_reader.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: X-Plane aeronautical data reader + * Purpose: Definition of classes for OGR X-Plane aeronautical data driver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" + +#include "ogr_xplane_reader.h" + +CPL_CVSID("$Id: ogr_xplane_reader.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/***********************************************************************/ +/* OGRXPlaneReader() */ +/***********************************************************************/ + +OGRXPlaneReader::OGRXPlaneReader() +{ + papszTokens = NULL; + fp = NULL; + pszFilename = NULL; + bEOF = FALSE; + nLineNumber = 0; + poInterestLayer = NULL; + nTokens = 0; +} + +/***********************************************************************/ +/* ~OGRXPlaneReader() */ +/***********************************************************************/ + +OGRXPlaneReader::~OGRXPlaneReader() +{ + CPLFree(pszFilename); + pszFilename = NULL; + + CSLDestroy(papszTokens); + papszTokens = NULL; + + if (fp != NULL) + VSIFCloseL(fp); + fp = NULL; +} + +/************************************************************************/ +/* StartParsing() */ +/************************************************************************/ + +int OGRXPlaneReader::StartParsing( const char * pszFilename ) +{ + fp = VSIFOpenL( pszFilename, "rb" ); + if (fp == NULL) + return FALSE; + + fp = (VSILFILE*) VSICreateBufferedReaderHandle ( (VSIVirtualHandle*) fp ); + + const char* pszLine = CPLReadLineL(fp); + if (!pszLine || (strcmp(pszLine, "I") != 0 && + strcmp(pszLine, "A") != 0)) + { + VSIFCloseL(fp); + fp = NULL; + return FALSE; + } + + pszLine = CPLReadLineL(fp); + if (!pszLine || IsRecognizedVersion(pszLine) == FALSE) + { + VSIFCloseL(fp); + fp = NULL; + return FALSE; + } + + CPLFree(this->pszFilename); + this->pszFilename = CPLStrdup(pszFilename); + + nLineNumber = 2; + CPLDebug("XPlane", "Version/Copyright : %s", pszLine); + + Rewind(); + + return TRUE; +} + +/************************************************************************/ +/* Rewind() */ +/************************************************************************/ + +void OGRXPlaneReader::Rewind() +{ + if (fp != NULL) + { + VSIRewindL(fp); + CPLReadLineL(fp); + CPLReadLineL(fp); + + nLineNumber = 2; + + CSLDestroy(papszTokens); + papszTokens = NULL; + + bEOF = FALSE; + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +int OGRXPlaneReader::GetNextFeature() +{ + if (fp == NULL || bEOF == TRUE || poInterestLayer == NULL) + return FALSE; + + Read(); + return TRUE; +} + +/************************************************************************/ +/* ReadWholeFile() */ +/************************************************************************/ + +int OGRXPlaneReader::ReadWholeFile() +{ + if (fp == NULL || bEOF == TRUE || nLineNumber != 2 || poInterestLayer != NULL) + return FALSE; + + Read(); + return TRUE; +} + + +/***********************************************************************/ +/* assertMinCol() */ +/***********************************************************************/ + +int OGRXPlaneReader::assertMinCol(int nMinColNum) +{ + if (nTokens < nMinColNum) + { + CPLDebug("XPlane", "Line %d : not enough columns : %d. %d is the minimum required", + nLineNumber, nTokens, nMinColNum); + return FALSE; + } + return TRUE; +} + + +/***********************************************************************/ +/* readDouble() */ +/***********************************************************************/ + +int OGRXPlaneReader::readDouble(double* pdfValue, int iToken, const char* pszTokenDesc) +{ + char* pszNext; + *pdfValue = CPLStrtod(papszTokens[iToken], &pszNext); + if (*pszNext != '\0' ) + { + CPLDebug("XPlane", "Line %d : invalid %s '%s'", + nLineNumber, pszTokenDesc, papszTokens[iToken]); + return FALSE; + } + return TRUE; +} + +/***********************************************************************/ +/* readDoubleWithBoundsAndConversion() */ +/***********************************************************************/ + +int OGRXPlaneReader::readDoubleWithBoundsAndConversion( + double* pdfValue, int iToken, const char* pszTokenDesc, + double dfFactor, double dfLowerBound, double dfUpperBound) +{ + int bRet = readDouble(pdfValue, iToken, pszTokenDesc); + if (bRet) + { + *pdfValue *= dfFactor; + if (*pdfValue < dfLowerBound || *pdfValue > dfUpperBound) + { + CPLDebug("XPlane", "Line %d : %s '%s' out of bounds [%f, %f]", + nLineNumber, pszTokenDesc, papszTokens[iToken], + dfLowerBound / dfFactor, dfUpperBound / dfFactor); + return FALSE; + } + } + return bRet; +} + +/***********************************************************************/ +/* readDoubleWithBounds() */ +/***********************************************************************/ + +int OGRXPlaneReader::readDoubleWithBounds( + double* pdfValue, int iToken, const char* pszTokenDesc, + double dfLowerBound, double dfUpperBound) +{ + return readDoubleWithBoundsAndConversion(pdfValue, iToken, pszTokenDesc, + 1., dfLowerBound, dfUpperBound); +} + +/***********************************************************************/ +/* readStringUntilEnd() */ +/***********************************************************************/ + +CPLString OGRXPlaneReader::readStringUntilEnd(int iFirstTokenIndice) +{ + CPLString osResult; + if (nTokens > iFirstTokenIndice) + { + int i; + int nIDsToSum = nTokens - iFirstTokenIndice; + const unsigned char* pszStr = (const unsigned char*)papszTokens[iFirstTokenIndice]; + for(int j=0;pszStr[j];j++) + { + if (pszStr[j] >= 32 && pszStr[j] <= 127) + osResult += pszStr[j]; + else + CPLDebug("XPlane", "Line %d : string with non ASCII characters", nLineNumber); + } + for(i=1;i= 32 && pszStr[j] <= 127) + osResult += pszStr[j]; + else + CPLDebug("XPlane", "Line %d : string with non ASCII characters", nLineNumber); + } + } + } + return osResult; +} + + +/***********************************************************************/ +/* readLatLon() */ +/***********************************************************************/ + +int OGRXPlaneReader::readLatLon(double* pdfLat, double* pdfLon, int iToken) +{ + int bRet = readDoubleWithBounds(pdfLat, iToken, "latitude", -90., 90.); + bRet &= readDoubleWithBounds(pdfLon, iToken + 1, "longitude", -180., 180.); + return bRet; +} + +/***********************************************************************/ +/* readTrueHeading() */ +/***********************************************************************/ + +int OGRXPlaneReader::readTrueHeading(double* pdfTrueHeading, int iToken, const char* pszTokenDesc) +{ + int bRet = readDoubleWithBounds(pdfTrueHeading, iToken, pszTokenDesc, -180., 360.); + if (bRet) + { + if (*pdfTrueHeading < 0.) + *pdfTrueHeading += 180.; + } + return bRet; +} + + + +/***********************************************************************/ +/* OGRXPlaneEnumeration() */ +/***********************************************************************/ + + +OGRXPlaneEnumeration::OGRXPlaneEnumeration(const char *pszEnumerationName, + const sEnumerationElement* osElements, + int nElements) : + m_pszEnumerationName(pszEnumerationName), + m_osElements(osElements), + m_nElements(nElements) +{ +} + +/***********************************************************************/ +/* GetText() */ +/***********************************************************************/ + +const char* OGRXPlaneEnumeration::GetText(int eValue) +{ + int i; + for(i=0;i + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OGR_XPLANE_READER_H_INCLUDED +#define _OGR_XPLANE_READER_H_INCLUDED + +#include "cpl_port.h" +#include "cpl_string.h" + +#define FEET_TO_METER 0.30479999798832 +#define NM_TO_KM 1.852 + +#define RET_IF_FAIL(x) if (!(x)) return; + +#define SET_IF_INTEREST_LAYER(x) poReader->x = ((OGRXPlaneLayer*)x == poLayer) ? x : NULL + +class OGRXPlaneLayer; +class OGRXPlaneDataSource; + +/************************************************************************/ +/* OGRXPlaneReader */ +/***********************************************************************/ + +class OGRXPlaneReader +{ + protected: + int nLineNumber; + char** papszTokens; + int nTokens; + VSILFILE* fp; + char* pszFilename; + int bEOF; + OGRXPlaneLayer* poInterestLayer; + + OGRXPlaneReader(); + + public: + virtual ~OGRXPlaneReader(); + + virtual OGRXPlaneReader* CloneForLayer(OGRXPlaneLayer* poLayer) = 0; + virtual int IsRecognizedVersion( const char* pszVersionString) = 0; + virtual int StartParsing( const char * pszFilename ); + virtual void Read() = 0; + virtual int ReadWholeFile(); + virtual int GetNextFeature(); + virtual void Rewind(); + + int assertMinCol(int nMinColNum); + int readDouble(double* pdfValue, int iToken, const char* pszTokenDesc); + int readDoubleWithBounds(double* pdfValue, int iToken, const char* pszTokenDesc, + double dfLowerBound, double dfUpperBound); + int readDoubleWithBoundsAndConversion(double* pdfValue, int iToken, const char* pszTokenDesc, + double dfFactor, double dfLowerBound, double dfUpperBound); + CPLString readStringUntilEnd(int iFirstToken); + + int readLatLon(double* pdfLat, double* pdfLon, int iToken); + int readTrueHeading(double* pdfTrueHeading, int iToken, const char* pszTokenDesc = "true heading"); +}; + +/************************************************************************/ +/* OGRXPlaneEnumeration */ +/***********************************************************************/ + +typedef struct +{ + int eValue; + const char* pszText; +} sEnumerationElement; + +class OGRXPlaneEnumeration +{ + private: + const char* m_pszEnumerationName; + const sEnumerationElement* m_osElements; + int m_nElements; + + public: + OGRXPlaneEnumeration(const char *pszEnumerationName, + const sEnumerationElement* osElements, + int nElements); + + const char* GetText(int eValue); + + int GetValue(const char* pszText); +}; + +#define DEFINE_XPLANE_ENUMERATION(enumerationName, enumerationValues) \ +static OGRXPlaneEnumeration enumerationName( #enumerationName, enumerationValues, \ + sizeof( enumerationValues ) / sizeof( enumerationValues[0] ) ); + + +/***********************************************************************/ +/* OGRXPlaneCreateAptFileReader */ +/***********************************************************************/ + +OGRXPlaneReader* OGRXPlaneCreateAptFileReader( OGRXPlaneDataSource* poDataSource ); + +/***********************************************************************/ +/* OGRXPlaneCreateNavFileReader */ +/***********************************************************************/ + +OGRXPlaneReader* OGRXPlaneCreateNavFileReader( OGRXPlaneDataSource* poDataSource ); + +/***********************************************************************/ +/* OGRXPlaneCreateAwyFileReader */ +/***********************************************************************/ + +OGRXPlaneReader* OGRXPlaneCreateAwyFileReader( OGRXPlaneDataSource* poDataSource ); + +/***********************************************************************/ +/* OGRXPlaneCreateFixFileReader */ +/***********************************************************************/ + +OGRXPlaneReader* OGRXPlaneCreateFixFileReader( OGRXPlaneDataSource* poDataSource ); + +#endif diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogrxplanedatasource.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogrxplanedatasource.cpp new file mode 100644 index 000000000..0442ed780 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogrxplanedatasource.cpp @@ -0,0 +1,185 @@ +/****************************************************************************** + * $Id: ogrxplanedatasource.cpp + * + * Project: X-Plane aeronautical data reader + * Purpose: Implements OGRXPlaneDataSource class + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_xplane.h" +#include "ogr_xplane_reader.h" + +CPL_CVSID("$Id: ogrxplanedatasource.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* OGRXPlaneDataSource() */ +/************************************************************************/ + +OGRXPlaneDataSource::OGRXPlaneDataSource() + +{ + pszName = NULL; + papoLayers = NULL; + nLayers = 0; + poReader = NULL; + bReadWholeFile = TRUE; + bWholeFiledReadingDone = FALSE; +} + +/************************************************************************/ +/* ~OGRXPlaneDataSource() */ +/************************************************************************/ + +OGRXPlaneDataSource::~OGRXPlaneDataSource() + +{ + Reset(); +} + +/************************************************************************/ +/* Reset() */ +/************************************************************************/ + +void OGRXPlaneDataSource::Reset() +{ + if ( poReader != NULL) + { + delete poReader; + poReader = NULL; + } + + CPLFree( pszName ); + pszName = NULL; + + for( int i = 0; i < nLayers; i++ ) + delete papoLayers[i]; + CPLFree( papoLayers ); + papoLayers = NULL; + nLayers = 0; +} + +/************************************************************************/ +/* GetLayer() */ +/************************************************************************/ + +OGRLayer *OGRXPlaneDataSource::GetLayer( int iLayer ) + +{ + if( iLayer < 0 || iLayer >= nLayers ) + return NULL; + else + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* RegisterLayer() */ +/************************************************************************/ + +void OGRXPlaneDataSource::RegisterLayer(OGRXPlaneLayer* poLayer) +{ + poLayer->SetDataSource(this); + + papoLayers = (OGRXPlaneLayer**) CPLRealloc(papoLayers, + (nLayers + 1) * sizeof(OGRXPlaneLayer*)); + papoLayers[nLayers++] = poLayer; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +int OGRXPlaneDataSource::Open( const char * pszFilename, int bReadWholeFile ) + +{ + Reset(); + + this->bReadWholeFile = bReadWholeFile; + + const char* pszShortFilename = CPLGetFilename(pszFilename); + if (EQUAL(pszShortFilename, "nav.dat") || + EQUAL(pszShortFilename, "earth_nav.dat")) + { + poReader = OGRXPlaneCreateNavFileReader(this); + } + else if (EQUAL(pszShortFilename, "apt.dat")) + { + poReader = OGRXPlaneCreateAptFileReader(this); + } + else if (EQUAL(pszShortFilename, "fix.dat") || + EQUAL(pszShortFilename, "earth_fix.dat")) + { + poReader = OGRXPlaneCreateFixFileReader(this); + } + else if (EQUAL(pszShortFilename, "awy.dat") || + EQUAL(pszShortFilename, "earth_awy.dat")) + { + poReader = OGRXPlaneCreateAwyFileReader(this); + } + + int bRet; + if (poReader && poReader->StartParsing(pszFilename) == FALSE) + { + delete poReader; + poReader = NULL; + } + if (poReader) + { + pszName = CPLStrdup(pszFilename); + + if ( !bReadWholeFile ) + { + for( int i = 0; i < nLayers; i++ ) + papoLayers[i]->SetReader(poReader->CloneForLayer(papoLayers[i])); + } + bRet = TRUE; + } + else + bRet = FALSE; + + return bRet; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRXPlaneDataSource::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* ReadWholeFileIfNecessary() */ +/************************************************************************/ + +void OGRXPlaneDataSource::ReadWholeFileIfNecessary() +{ + if (bReadWholeFile && !bWholeFiledReadingDone) + { + poReader->ReadWholeFile(); + for( int i = 0; i < nLayers; i++ ) + papoLayers[i]->AutoAdjustColumnsWidth(); + bWholeFiledReadingDone = TRUE; + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogrxplanedriver.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogrxplanedriver.cpp new file mode 100644 index 000000000..131e706e7 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogrxplanedriver.cpp @@ -0,0 +1,95 @@ +/****************************************************************************** + * $Id: ogrxplanedriver.cpp + * + * Project: X-Plane aeronautical data reader + * Purpose: Implements OGRXPlaneDriver. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_xplane.h" +#include "cpl_conv.h" + +/************************************************************************/ +/* GetName() */ +/************************************************************************/ + +const char *OGRXPlaneDriver::GetName() + +{ + return "XPlane"; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +OGRDataSource *OGRXPlaneDriver::Open( const char * pszFilename, int bUpdate ) + +{ + if ( bUpdate ) + { + return NULL; + } + + if( !EQUAL(CPLGetExtension(pszFilename), "dat") ) + return NULL; + + OGRXPlaneDataSource *poDS = new OGRXPlaneDataSource(); + + int bReadWholeFile = CSLTestBoolean(CPLGetConfigOption("OGR_XPLANE_READ_WHOLE_FILE", "TRUE")); + + if( !poDS->Open( pszFilename, bReadWholeFile ) ) + { + delete poDS; + poDS = NULL; + } + + return poDS; +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRXPlaneDriver::TestCapability( CPL_UNUSED const char * pszCap ) +{ + return FALSE; +} + +/************************************************************************/ +/* RegisterOGRXPlane() */ +/************************************************************************/ + +void RegisterOGRXPlane() + +{ + OGRSFDriver* poDriver = new OGRXPlaneDriver; + poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, + "X-Plane/Flightgear aeronautical data" ); + poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "dat" ); + poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, + "drv_xplane.html" ); + poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); + OGRSFDriverRegistrar::GetRegistrar()->RegisterDriver(poDriver); +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogrxplanelayer.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogrxplanelayer.cpp new file mode 100644 index 000000000..a742dbff3 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/ogrxplanelayer.cpp @@ -0,0 +1,339 @@ +/****************************************************************************** + * $Id: ogrxplanelayer.cpp + * + * Project: XPlane Translator + * Purpose: Implements OGRXPlaneLayer class. + * Author: Even Rouault, even dot rouault at mines dash paris dot org + * + ****************************************************************************** + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_xplane.h" +#include "ogr_xplane_geo_utils.h" +#include "ogr_xplane_reader.h" + +CPL_CVSID("$Id: ogrxplanelayer.cpp 28375 2015-01-30 12:06:11Z rouault $"); + +/************************************************************************/ +/* OGRXPlaneLayer() */ +/************************************************************************/ + +OGRXPlaneLayer::OGRXPlaneLayer( const char* pszLayerName ) + +{ + nFID = 0; + nFeatureArraySize = 0; + nFeatureArrayMaxSize = 0; + nFeatureArrayIndex = 0; + papoFeatures = NULL; + poDS = NULL; + + poFeatureDefn = new OGRFeatureDefn( pszLayerName ); + SetDescription( poFeatureDefn->GetName() ); + poFeatureDefn->Reference(); + + poSRS = new OGRSpatialReference(); + poSRS->SetWellKnownGeogCS("WGS84"); + poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); + + poReader = NULL; +} + + +/************************************************************************/ +/* ~OGRXPlaneLayer() */ +/************************************************************************/ + +OGRXPlaneLayer::~OGRXPlaneLayer() + +{ + poFeatureDefn->Release(); + + poSRS->Release(); + + for(int i=0;iRewind(); + } + nFeatureArrayIndex = 0; +} + +/************************************************************************/ +/* SetReader() */ +/************************************************************************/ + +void OGRXPlaneLayer::SetReader(OGRXPlaneReader* poReader) +{ + if (this->poReader) + { + delete this->poReader; + } + this->poReader = poReader; +} + +/************************************************************************/ +/* AutoAdjustColumnsWidth() */ +/************************************************************************/ + +void OGRXPlaneLayer::AutoAdjustColumnsWidth() +{ + if (poReader != NULL) + { + CPLError(CE_Failure, CPLE_NotSupported, + "AutoAdjustColumnsWidth() only supported when reading the whole file"); + return; + } + + for(int col=0;colGetFieldCount();col++) + { + OGRFieldDefn* poFieldDefn = poFeatureDefn->GetFieldDefn(col); + if (poFieldDefn->GetWidth() == 0) + { + if (poFieldDefn->GetType() == OFTString || + poFieldDefn->GetType() == OFTInteger) + { + int nMaxLen = 0; + for(int i=0;iGetFieldAsString(col)); + if (nLen > nMaxLen) + nMaxLen = nLen; + } + poFieldDefn->SetWidth(nMaxLen); + } + else + { + CPLDebug("XPlane", "Field %s of layer %s is of unknown size", + poFieldDefn->GetNameRef(), poFeatureDefn->GetName()); + } + } + } +} + +/************************************************************************/ +/* GetNextFeature() */ +/************************************************************************/ + +OGRFeature *OGRXPlaneLayer::GetNextFeature() +{ + OGRFeature *poFeature; + + if (poReader) + { + while(TRUE) + { + if ( nFeatureArrayIndex == nFeatureArraySize) + { + nFeatureArrayIndex = nFeatureArraySize = 0; + + if (poReader->GetNextFeature() == FALSE) + return NULL; + if (nFeatureArraySize == 0) + return NULL; + } + + do + { + poFeature = papoFeatures[nFeatureArrayIndex]; + papoFeatures[nFeatureArrayIndex] = NULL; + nFeatureArrayIndex++; + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature; + } + + delete poFeature; + } while(nFeatureArrayIndex < nFeatureArraySize); + } + } + else + poDS->ReadWholeFileIfNecessary(); + + while(nFeatureArrayIndex < nFeatureArraySize) + { + poFeature = papoFeatures[nFeatureArrayIndex ++]; + CPLAssert (poFeature != NULL); + + if( (m_poFilterGeom == NULL + || FilterGeometry( poFeature->GetGeometryRef() ) ) + && (m_poAttrQuery == NULL + || m_poAttrQuery->Evaluate( poFeature )) ) + { + return poFeature->Clone(); + } + } + + return NULL; +} + +/************************************************************************/ +/* GetFeature() */ +/************************************************************************/ + +OGRFeature * OGRXPlaneLayer::GetFeature( GIntBig nFID ) +{ + if (poReader) + return OGRLayer::GetFeature(nFID); + else + poDS->ReadWholeFileIfNecessary(); + + if (nFID >= 0 && nFID < nFeatureArraySize) + { + return papoFeatures[nFID]->Clone(); + } + else + { + return NULL; + } +} + +/************************************************************************/ +/* GetFeatureCount() */ +/************************************************************************/ + +GIntBig OGRXPlaneLayer::GetFeatureCount( int bForce ) +{ + if (poReader == NULL && m_poFilterGeom == NULL && m_poAttrQuery == NULL) + { + poDS->ReadWholeFileIfNecessary(); + return nFeatureArraySize; + } + else + return OGRLayer::GetFeatureCount( bForce ) ; +} + + +/************************************************************************/ +/* SetNextByIndex() */ +/************************************************************************/ + +OGRErr OGRXPlaneLayer::SetNextByIndex( GIntBig nIndex ) +{ + if (poReader == NULL && m_poFilterGeom == NULL && m_poAttrQuery == NULL) + { + poDS->ReadWholeFileIfNecessary(); + if (nIndex < 0 || nIndex >= nFeatureArraySize) + return OGRERR_FAILURE; + + nFeatureArrayIndex = (int)nIndex; + return OGRERR_NONE; + } + else + return OGRLayer::SetNextByIndex(nIndex); +} + +/************************************************************************/ +/* TestCapability() */ +/************************************************************************/ + +int OGRXPlaneLayer::TestCapability( const char * pszCap ) +{ + if (EQUAL(pszCap,OLCFastFeatureCount) || + EQUAL(pszCap,OLCRandomRead) || + EQUAL(pszCap,OLCFastSetNextByIndex)) + { + if (poReader == NULL && m_poFilterGeom == NULL && m_poAttrQuery == NULL) + return TRUE; + } + + return FALSE; +} + + +/************************************************************************/ +/* RegisterFeature() */ +/************************************************************************/ + +void OGRXPlaneLayer::RegisterFeature( OGRFeature* poFeature ) +{ + CPLAssert (poFeature != NULL); + + OGRGeometry* poGeom = poFeature->GetGeometryRef(); + if (poGeom) + poGeom->assignSpatialReference( poSRS ); + + if (nFeatureArraySize == nFeatureArrayMaxSize) + { + nFeatureArrayMaxSize = 2 * nFeatureArrayMaxSize + 1; + papoFeatures = (OGRFeature**)CPLRealloc(papoFeatures, + nFeatureArrayMaxSize * sizeof(OGRFeature*)); + } + papoFeatures[nFeatureArraySize] = poFeature; + poFeature->SetFID( nFID ); + nFID ++; + nFeatureArraySize ++; +} + +/************************************************************************/ +/* GetLayerDefn() */ +/************************************************************************/ + +OGRFeatureDefn * OGRXPlaneLayer::GetLayerDefn() +{ + poDS->ReadWholeFileIfNecessary(); + return poFeatureDefn; +} + +/************************************************************************/ +/* SetDataSource() */ +/************************************************************************/ + +void OGRXPlaneLayer::SetDataSource(OGRXPlaneDataSource* poDS) +{ + this->poDS = poDS; +} diff --git a/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/test_geo_utils.cpp b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/test_geo_utils.cpp new file mode 100644 index 000000000..3bc62f63a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsf_frmts/xplane/test_geo_utils.cpp @@ -0,0 +1,25 @@ +#include +#include "ogr_xplane_geo_utils.h" + +int main(int argc, char* argv[]) +{ + double latA = 49, lonA = 2; + double latB = 49.1, lonB = 2.1; + double latC, lonC; + double heading; + double distance; + + heading = OGRXPlane_Track(latA, lonA, latB, lonB); + distance = OGRXPlane_Distance(latA, lonA, latB, lonB); + OGRXPlane_ExtendPosition(latA, lonA, distance, heading, &latC, &lonC); + printf("heading=%f, distance=%f\n", heading, distance); + printf("%.15f=%.15f, %.15f=%.15f\n", latB, latC, lonB, lonC); + + heading = OGRXPlane_Track(latB, lonB, latA, lonA); + distance = OGRXPlane_Distance(latB, lonB, latA, lonA); + OGRXPlane_ExtendPosition(latB, lonB, distance, heading, &latC, &lonC); + printf("heading=%f, distance=%f\n", heading, distance); + printf("%.15f=%.15f, %.15f=%.15f\n", latA, latC, lonA, lonC); + + return 0; +} diff --git a/bazaar/plugin/gdal/ogr/ogrspatialreference.cpp b/bazaar/plugin/gdal/ogr/ogrspatialreference.cpp new file mode 100644 index 000000000..44627fac6 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrspatialreference.cpp @@ -0,0 +1,7605 @@ +/****************************************************************************** + * $Id: ogrspatialreference.cpp 29104 2015-05-02 01:44:39Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRSpatialReference class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Les Technologies SoftMap Inc. + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_spatialref.h" +#include "ogr_p.h" +#include "cpl_csv.h" +#include "cpl_http.h" +#include "cpl_atomic_ops.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: ogrspatialreference.cpp 29104 2015-05-02 01:44:39Z rouault $"); + +// The current opinion is that WKT longitudes like central meridian +// should be relative to greenwich, not the prime meridian in use. +// Define the following if they should be relative to the prime meridian +// of then geogcs. +#undef WKT_LONGITUDE_RELATIVE_TO_PM + +/************************************************************************/ +/* OGRPrintDouble() */ +/************************************************************************/ + +void OGRPrintDouble( char * pszStrBuf, double dfValue ) + +{ + CPLsprintf( pszStrBuf, "%.16g", dfValue ); + + int nLen = strlen(pszStrBuf); + + // The following hack is intended to truncate some "precision" in cases + // that appear to be roundoff error. + if( nLen > 15 + && (strcmp(pszStrBuf+nLen-6,"999999") == 0 + || strcmp(pszStrBuf+nLen-6,"000001") == 0) ) + { + CPLsprintf( pszStrBuf, "%.15g", dfValue ); + } + + // force to user periods regardless of locale. + if( strchr( pszStrBuf, ',' ) != NULL ) + { + char *pszDelim = strchr( pszStrBuf, ',' ); + *pszDelim = '.'; + } +} + +/************************************************************************/ +/* OGRSpatialReference() */ +/************************************************************************/ + +/** + * \brief Constructor. + * + * This constructor takes an optional string argument which if passed + * should be a WKT representation of an SRS. Passing this is equivalent + * to not passing it, and then calling importFromWkt() with the WKT string. + * + * Note that newly created objects are given a reference count of one. + * + * The C function OSRNewSpatialReference() does the same thing as this + * constructor. + * + * @param pszWKT well known text definition to which the object should + * be initialized, or NULL (the default). + */ + +OGRSpatialReference::OGRSpatialReference( const char * pszWKT ) + +{ + bNormInfoSet = FALSE; + nRefCount = 1; + poRoot = NULL; + + if( pszWKT != NULL ) + importFromWkt( (char **) &pszWKT ); +} + +/************************************************************************/ +/* OSRNewSpatialReference() */ +/************************************************************************/ + +/** + * \brief Constructor. + * + * This function is the same as OGRSpatialReference::OGRSpatialReference() + */ +OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference( const char *pszWKT ) + +{ + OGRSpatialReference * poSRS; + + poSRS = new OGRSpatialReference(); + + if( pszWKT != NULL && strlen(pszWKT) > 0 ) + { + if( poSRS->importFromWkt( (char **) (&pszWKT) ) != OGRERR_NONE ) + { + delete poSRS; + poSRS = NULL; + } + } + + return (OGRSpatialReferenceH) poSRS; +} + +/************************************************************************/ +/* OGRSpatialReference() */ +/* */ +/* Simple copy constructor. See also Clone(). */ +/************************************************************************/ + +OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther) + +{ + bNormInfoSet = FALSE; + nRefCount = 1; + poRoot = NULL; + + if( oOther.poRoot != NULL ) + poRoot = oOther.poRoot->Clone(); +} + +/************************************************************************/ +/* ~OGRSpatialReference() */ +/************************************************************************/ + +/** + * \brief OGRSpatialReference destructor. + * + * The C function OSRDestroySpatialReference() does the same thing as this + * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference() + * + * @deprecated + */ + +OGRSpatialReference::~OGRSpatialReference() + +{ + if( poRoot != NULL ) + delete poRoot; +} + +/************************************************************************/ +/* DestroySpatialReference() */ +/************************************************************************/ + +/** + * \brief OGRSpatialReference destructor. + * + * This static method will destroy a OGRSpatialReference. It is + * equivalent to calling delete on the object, but it ensures that the + * deallocation is properly executed within the OGR libraries heap on + * platforms where this can matter (win32). + * + * This function is the same as OSRDestroySpatialReference() + * + * @param poSRS the object to delete + * + * @since GDAL 1.7.0 + */ + +void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference* poSRS) +{ + delete poSRS; +} + +/************************************************************************/ +/* OSRDestroySpatialReference() */ +/************************************************************************/ + +/** + * \brief OGRSpatialReference destructor. + * + * This function is the same as OGRSpatialReference::~OGRSpatialReference() + * and OGRSpatialReference::DestroySpatialReference() + * + * @param hSRS the object to delete + */ +void CPL_STDCALL OSRDestroySpatialReference( OGRSpatialReferenceH hSRS ) + +{ + delete ((OGRSpatialReference *) hSRS); +} + +/************************************************************************/ +/* Clear() */ +/************************************************************************/ + +/** + * \brief Wipe current definition. + * + * Returns OGRSpatialReference to a state with no definition, as it + * exists when first created. It does not affect reference counts. + */ + +void OGRSpatialReference::Clear() + +{ + if( poRoot ) + delete poRoot; + + poRoot = NULL; + + bNormInfoSet = FALSE; + dfFromGreenwich = 1.0; + dfToMeter = 1.0; + dfToDegrees = 1.0; +} + +/************************************************************************/ +/* operator=() */ +/************************************************************************/ + +OGRSpatialReference & +OGRSpatialReference::operator=(const OGRSpatialReference &oSource) + +{ + Clear(); + + if( oSource.poRoot != NULL ) + poRoot = oSource.poRoot->Clone(); + + return *this; +} + +/************************************************************************/ +/* Reference() */ +/************************************************************************/ + +/** + * \brief Increments the reference count by one. + * + * The reference count is used keep track of the number of OGRGeometry objects + * referencing this SRS. + * + * The method does the same thing as the C function OSRReference(). + * + * @return the updated reference count. + */ + +int OGRSpatialReference::Reference() + +{ + return CPLAtomicInc(&nRefCount); +} + +/************************************************************************/ +/* OSRReference() */ +/************************************************************************/ + +/** + * \brief Increments the reference count by one. + * + * This function is the same as OGRSpatialReference::Reference() + */ +int OSRReference( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRReference", 0 ); + + return ((OGRSpatialReference *) hSRS)->Reference(); +} + +/************************************************************************/ +/* Dereference() */ +/************************************************************************/ + +/** + * \brief Decrements the reference count by one. + * + * The method does the same thing as the C function OSRDereference(). + * + * @return the updated reference count. + */ + +int OGRSpatialReference::Dereference() + +{ + if( nRefCount <= 0 ) + CPLDebug( "OSR", + "Dereference() called on an object with refcount %d," + "likely already destroyed!", + nRefCount ); + return CPLAtomicDec(&nRefCount); +} + +/************************************************************************/ +/* OSRDereference() */ +/************************************************************************/ + +/** + * \brief Decrements the reference count by one. + * + * This function is the same as OGRSpatialReference::Dereference() + */ +int OSRDereference( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRDereference", 0 ); + + return ((OGRSpatialReference *) hSRS)->Dereference(); +} + +/************************************************************************/ +/* GetReferenceCount() */ +/************************************************************************/ + +/** + * \fn int OGRSpatialReference::GetReferenceCount() const; + * + * \brief Fetch current reference count. + * + * @return the current reference count. + */ + +/************************************************************************/ +/* Release() */ +/************************************************************************/ + +/** + * \brief Decrements the reference count by one, and destroy if zero. + * + * The method does the same thing as the C function OSRRelease(). + */ + +void OGRSpatialReference::Release() + +{ + CPLAssert( NULL != this ); + + if( Dereference() <= 0 ) + delete this; +} + +/************************************************************************/ +/* OSRRelease() */ +/************************************************************************/ + +/** + * \brief Decrements the reference count by one, and destroy if zero. + * + * This function is the same as OGRSpatialReference::Release() + */ +void OSRRelease( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER0( hSRS, "OSRRelease" ); + + ((OGRSpatialReference *) hSRS)->Release(); +} + +/************************************************************************/ +/* SetRoot() */ +/************************************************************************/ + +/** + * \brief Set the root SRS node. + * + * If the object has an existing tree of OGR_SRSNodes, they are destroyed + * as part of assigning the new root. Ownership of the passed OGR_SRSNode is + * is assumed by the OGRSpatialReference. + * + * @param poNewRoot object to assign as root. + */ + +void OGRSpatialReference::SetRoot( OGR_SRSNode * poNewRoot ) + +{ + if( poRoot != NULL ) + delete poRoot; + + poRoot = poNewRoot; +} + +/************************************************************************/ +/* GetAttrNode() */ +/************************************************************************/ + +/** + * \brief Find named node in tree. + * + * This method does a pre-order traversal of the node tree searching for + * a node with this exact value (case insensitive), and returns it. Leaf + * nodes are not considered, under the assumption that they are just + * attribute value nodes. + * + * If a node appears more than once in the tree (such as UNIT for instance), + * the first encountered will be returned. Use GetNode() on a subtree to be + * more specific. + * + * @param pszNodePath the name of the node to search for. May contain multiple + * components such as "GEOGCS|UNIT". + * + * @return a pointer to the node found, or NULL if none. + */ + +OGR_SRSNode *OGRSpatialReference::GetAttrNode( const char * pszNodePath ) + +{ + char **papszPathTokens; + OGR_SRSNode *poNode; + + papszPathTokens = CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE); + + if( CSLCount( papszPathTokens ) < 1 ) + { + CSLDestroy(papszPathTokens); + return NULL; + } + + poNode = GetRoot(); + for( int i = 0; poNode != NULL && papszPathTokens[i] != NULL; i++ ) + { + poNode = poNode->GetNode( papszPathTokens[i] ); + } + + CSLDestroy( papszPathTokens ); + + return poNode; +} + +const OGR_SRSNode * +OGRSpatialReference::GetAttrNode( const char * pszNodePath ) const + +{ + OGR_SRSNode *poNode; + + poNode = ((OGRSpatialReference *) this)->GetAttrNode(pszNodePath); + + return poNode; +} + +/************************************************************************/ +/* GetAttrValue() */ +/************************************************************************/ + +/** + * \brief Fetch indicated attribute of named node. + * + * This method uses GetAttrNode() to find the named node, and then extracts + * the value of the indicated child. Thus a call to GetAttrValue("UNIT",1) + * would return the second child of the UNIT node, which is normally the + * length of the linear unit in meters. + * + * This method does the same thing as the C function OSRGetAttrValue(). + * + * @param pszNodeName the tree node to look for (case insensitive). + * @param iAttr the child of the node to fetch (zero based). + * + * @return the requested value, or NULL if it fails for any reason. + */ + +const char *OGRSpatialReference::GetAttrValue( const char * pszNodeName, + int iAttr ) const + +{ + const OGR_SRSNode *poNode; + + poNode = GetAttrNode( pszNodeName ); + if( poNode == NULL ) + return NULL; + + if( iAttr < 0 || iAttr >= poNode->GetChildCount() ) + return NULL; + + return poNode->GetChild(iAttr)->GetValue(); +} + +/************************************************************************/ +/* OSRGetAttrValue() */ +/************************************************************************/ + +/** + * \brief Fetch indicated attribute of named node. + * + * This function is the same as OGRSpatialReference::GetAttrValue() + */ +const char * CPL_STDCALL OSRGetAttrValue( OGRSpatialReferenceH hSRS, + const char * pszKey, int iChild ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetAttrValue", NULL ); + + return ((OGRSpatialReference *) hSRS)->GetAttrValue( pszKey, iChild ); +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +/** + * \brief Make a duplicate of this OGRSpatialReference. + * + * This method is the same as the C function OSRClone(). + * + * @return a new SRS, which becomes the responsibility of the caller. + */ + +OGRSpatialReference *OGRSpatialReference::Clone() const + +{ + OGRSpatialReference *poNewRef; + + poNewRef = new OGRSpatialReference(); + + if( poRoot != NULL ) + poNewRef->poRoot = poRoot->Clone(); + + return poNewRef; +} + +/************************************************************************/ +/* OSRClone() */ +/************************************************************************/ + +/** + * \brief Make a duplicate of this OGRSpatialReference. + * + * This function is the same as OGRSpatialReference::Clone() + */ +OGRSpatialReferenceH CPL_STDCALL OSRClone( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRClone", NULL ); + + return (OGRSpatialReferenceH) ((OGRSpatialReference *) hSRS)->Clone(); +} + +/************************************************************************/ +/* dumpReadable() */ +/* */ +/* Dump pretty wkt to stdout, mostly for debugging. */ +/************************************************************************/ + +void OGRSpatialReference::dumpReadable() + +{ + char *pszPrettyWkt = NULL; + + exportToPrettyWkt( &pszPrettyWkt, FALSE ); + printf( "%s\n", pszPrettyWkt ); + CPLFree( pszPrettyWkt ); +} + +/************************************************************************/ +/* exportToPrettyWkt() */ +/************************************************************************/ + +/** + * Convert this SRS into a a nicely formatted WKT string for display to a person. + * + * Note that the returned WKT string should be freed with OGRFree() or + * CPLFree() when no longer needed. It is the responsibility of the caller. + * + * This method is the same as the C function OSRExportToPrettyWkt(). + * + * @param ppszResult the resulting string is returned in this pointer. + * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be stripped off + * + * @return currently OGRERR_NONE is always returned, but the future it + * is possible error conditions will develop. + */ + +OGRErr OGRSpatialReference::exportToPrettyWkt( char ** ppszResult, + int bSimplify ) const + +{ + if( poRoot == NULL ) + { + *ppszResult = CPLStrdup(""); + return OGRERR_NONE; + } + + if( bSimplify ) + { + OGRSpatialReference *poSimpleClone = Clone(); + OGRErr eErr; + + poSimpleClone->GetRoot()->StripNodes( "AXIS" ); + poSimpleClone->GetRoot()->StripNodes( "AUTHORITY" ); + poSimpleClone->GetRoot()->StripNodes( "EXTENSION" ); + eErr = poSimpleClone->GetRoot()->exportToPrettyWkt( ppszResult, 1 ); + delete poSimpleClone; + return eErr; + } + else + return poRoot->exportToPrettyWkt( ppszResult, 1 ); +} + +/************************************************************************/ +/* OSRExportToPrettyWkt() */ +/************************************************************************/ + + +/** + * \brief Convert this SRS into a a nicely formatted WKT string for display to a person. + * + * This function is the same as OGRSpatialReference::exportToPrettyWkt(). + */ + +OGRErr CPL_STDCALL OSRExportToPrettyWkt( OGRSpatialReferenceH hSRS, char ** ppszReturn, + int bSimplify) + +{ + VALIDATE_POINTER1( hSRS, "OSRExportToPrettyWkt", CE_Failure ); + + *ppszReturn = NULL; + + return ((OGRSpatialReference *) hSRS)->exportToPrettyWkt( ppszReturn, + bSimplify ); +} + +/************************************************************************/ +/* exportToWkt() */ +/************************************************************************/ + +/** + * \brief Convert this SRS into WKT format. + * + * Note that the returned WKT string should be freed with OGRFree() or + * CPLFree() when no longer needed. It is the responsibility of the caller. + * + * This method is the same as the C function OSRExportToWkt(). + * + * @param ppszResult the resulting string is returned in this pointer. + * + * @return currently OGRERR_NONE is always returned, but the future it + * is possible error conditions will develop. + */ + +OGRErr OGRSpatialReference::exportToWkt( char ** ppszResult ) const + +{ + if( poRoot == NULL ) + { + *ppszResult = CPLStrdup(""); + return OGRERR_NONE; + } + else + { + return poRoot->exportToWkt(ppszResult); + } +} + +/************************************************************************/ +/* OSRExportToWkt() */ +/************************************************************************/ + +/** + * \brief Convert this SRS into WKT format. + * + * This function is the same as OGRSpatialReference::exportToWkt(). + */ + +OGRErr CPL_STDCALL OSRExportToWkt( OGRSpatialReferenceH hSRS, + char ** ppszReturn ) + +{ + VALIDATE_POINTER1( hSRS, "OSRExportToWkt", CE_Failure ); + + *ppszReturn = NULL; + + return ((OGRSpatialReference *) hSRS)->exportToWkt( ppszReturn ); +} + +/************************************************************************/ +/* importFromWkt() */ +/************************************************************************/ + +/** + * \brief Import from WKT string. + * + * This method will wipe the existing SRS definition, and + * reassign it based on the contents of the passed WKT string. Only as + * much of the input string as needed to construct this SRS is consumed from + * the input string, and the input string pointer + * is then updated to point to the remaining (unused) input. + * + * This method is the same as the C function OSRImportFromWkt(). + * + * @param ppszInput Pointer to pointer to input. The pointer is updated to + * point to remaining unused input text. + * + * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it + * fails for any reason. + */ + +OGRErr OGRSpatialReference::importFromWkt( char ** ppszInput ) + +{ + if ( !ppszInput || !*ppszInput ) + return OGRERR_FAILURE; + + Clear(); + + poRoot = new OGR_SRSNode(); + + OGRErr eErr = poRoot->importFromWkt( ppszInput ); + if (eErr != OGRERR_NONE) + return eErr; + +/* -------------------------------------------------------------------- */ +/* The following seems to try and detect and unconsumed */ +/* VERTCS[] coordinate system definition (ESRI style) and to */ +/* import and attach it to the existing root. Likely we will */ +/* need to extend this somewhat to bring it into an acceptable */ +/* OGRSpatialReference organization at some point. */ +/* -------------------------------------------------------------------- */ + if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS")) + { + if(((*ppszInput)[0]) == ',') + (*ppszInput)++; + OGR_SRSNode *poNewChild = new OGR_SRSNode(); + poRoot->AddChild( poNewChild ); + return poNewChild->importFromWkt( ppszInput ); + } + + return eErr; +} + +/************************************************************************/ +/* OSRImportFromWkt() */ +/************************************************************************/ + +/** + * \brief Import from WKT string. + * + * This function is the same as OGRSpatialReference::importFromWkt(). + */ + +OGRErr OSRImportFromWkt( OGRSpatialReferenceH hSRS, char **ppszInput ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromWkt", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromWkt( ppszInput ); +} + +/************************************************************************/ +/* SetNode() */ +/************************************************************************/ + +/** + * \brief Set attribute value in spatial reference. + * + * Missing intermediate nodes in the path will be created if not already + * in existance. If the attribute has no children one will be created and + * assigned the value otherwise the zeroth child will be assigned the value. + * + * This method does the same as the C function OSRSetAttrValue(). + * + * @param pszNodePath full path to attribute to be set. For instance + * "PROJCS|GEOGCS|UNIT". + * + * @param pszNewNodeValue value to be assigned to node, such as "meter". + * This may be NULL if you just want to force creation of the intermediate + * path. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetNode( const char * pszNodePath, + const char * pszNewNodeValue ) + +{ + char **papszPathTokens; + int i; + OGR_SRSNode *poNode; + + papszPathTokens = CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE); + + if( CSLCount( papszPathTokens ) < 1 ) + return OGRERR_FAILURE; + + if( GetRoot() == NULL || !EQUAL(papszPathTokens[0],GetRoot()->GetValue()) ) + { + SetRoot( new OGR_SRSNode( papszPathTokens[0] ) ); + } + + poNode = GetRoot(); + for( i = 1; papszPathTokens[i] != NULL; i++ ) + { + int j; + + for( j = 0; j < poNode->GetChildCount(); j++ ) + { + if( EQUAL(poNode->GetChild( j )->GetValue(),papszPathTokens[i]) ) + { + poNode = poNode->GetChild(j); + j = -1; + break; + } + } + + if( j != -1 ) + { + OGR_SRSNode *poNewNode = new OGR_SRSNode( papszPathTokens[i] ); + poNode->AddChild( poNewNode ); + poNode = poNewNode; + } + } + + CSLDestroy( papszPathTokens ); + + if( pszNewNodeValue != NULL ) + { + if( poNode->GetChildCount() > 0 ) + poNode->GetChild(0)->SetValue( pszNewNodeValue ); + else + poNode->AddChild( new OGR_SRSNode( pszNewNodeValue ) ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetAttrValue() */ +/************************************************************************/ + +/** + * \brief Set attribute value in spatial reference. + * + * This function is the same as OGRSpatialReference::SetNode() + */ +OGRErr CPL_STDCALL OSRSetAttrValue( OGRSpatialReferenceH hSRS, + const char * pszPath, const char * pszValue ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetAttrValue", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetNode( pszPath, pszValue ); +} + +/************************************************************************/ +/* SetNode() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetNode( const char *pszNodePath, + double dfValue ) + +{ + char szValue[64]; + + if( ABS(dfValue - (int) dfValue) == 0.0 ) + sprintf( szValue, "%d", (int) dfValue ); + else + OGRPrintDouble( szValue, dfValue ); + + return SetNode( pszNodePath, szValue ); +} + +/************************************************************************/ +/* SetAngularUnits() */ +/************************************************************************/ + +/** + * \brief Set the angular units for the geographic coordinate system. + * + * This method creates a UNIT subnode with the specified values as a + * child of the GEOGCS node. + * + * This method does the same as the C function OSRSetAngularUnits(). + * + * @param pszUnitsName the units name to be used. Some preferred units + * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE. + * + * @param dfInRadians the value to multiple by an angle in the indicated + * units to transform to radians. Some standard conversion factors can + * be found in ogr_srs_api.h. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetAngularUnits( const char * pszUnitsName, + double dfInRadians ) + +{ + OGR_SRSNode *poCS; + OGR_SRSNode *poUnits; + char szValue[128]; + + bNormInfoSet = FALSE; + + poCS = GetAttrNode( "GEOGCS" ); + + if( poCS == NULL ) + return OGRERR_FAILURE; + + OGRPrintDouble( szValue, dfInRadians ); + + if( poCS->FindChild( "UNIT" ) >= 0 ) + { + poUnits = poCS->GetChild( poCS->FindChild( "UNIT" ) ); + if (poUnits->GetChildCount() < 2) + return OGRERR_FAILURE; + poUnits->GetChild(0)->SetValue( pszUnitsName ); + poUnits->GetChild(1)->SetValue( szValue ); + } + else + { + poUnits = new OGR_SRSNode( "UNIT" ); + poUnits->AddChild( new OGR_SRSNode( pszUnitsName ) ); + poUnits->AddChild( new OGR_SRSNode( szValue ) ); + + poCS->AddChild( poUnits ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetAngularUnits() */ +/************************************************************************/ + +/** + * \brief Set the angular units for the geographic coordinate system. + * + * This function is the same as OGRSpatialReference::SetAngularUnits() + */ +OGRErr OSRSetAngularUnits( OGRSpatialReferenceH hSRS, + const char * pszUnits, double dfInRadians ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetAngularUnits", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetAngularUnits( pszUnits, + dfInRadians ); +} + +/************************************************************************/ +/* GetAngularUnits() */ +/************************************************************************/ + +/** + * \brief Fetch angular geographic coordinate system units. + * + * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV + * will be assumed. This method only checks directly under the GEOGCS node + * for units. + * + * This method does the same thing as the C function OSRGetAngularUnits(). + * + * @param ppszName a pointer to be updated with the pointer to the + * units name. The returned value remains internal to the OGRSpatialReference + * and shouldn't be freed, or modified. It may be invalidated on the next + * OGRSpatialReference call. + * + * @return the value to multiply by angular distances to transform them to + * radians. + */ + +double OGRSpatialReference::GetAngularUnits( char ** ppszName ) const + +{ + const OGR_SRSNode *poCS = GetAttrNode( "GEOGCS" ); + + if( ppszName != NULL ) + *ppszName = (char* ) "degree"; + + if( poCS == NULL ) + return CPLAtof(SRS_UA_DEGREE_CONV); + + for( int iChild = 0; iChild < poCS->GetChildCount(); iChild++ ) + { + const OGR_SRSNode *poChild = poCS->GetChild(iChild); + + if( EQUAL(poChild->GetValue(),"UNIT") + && poChild->GetChildCount() >= 2 ) + { + if( ppszName != NULL ) + *ppszName = (char *) poChild->GetChild(0)->GetValue(); + + return CPLAtof( poChild->GetChild(1)->GetValue() ); + } + } + + return 1.0; +} + +/************************************************************************/ +/* OSRGetAngularUnits() */ +/************************************************************************/ + +/** + * \brief Fetch angular geographic coordinate system units. + * + * This function is the same as OGRSpatialReference::GetAngularUnits() + */ +double OSRGetAngularUnits( OGRSpatialReferenceH hSRS, char ** ppszName ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetAngularUnits", 0 ); + + return ((OGRSpatialReference *) hSRS)->GetAngularUnits( ppszName ); +} + +/************************************************************************/ +/* SetLinearUnitsAndUpdateParameters() */ +/************************************************************************/ + +/** + * \brief Set the linear units for the projection. + * + * This method creates a UNIT subnode with the specified values as a + * child of the PROJCS or LOCAL_CS node. It works the same as the + * SetLinearUnits() method, but it also updates all existing linear + * projection parameter values from the old units to the new units. + * + * @param pszName the units name to be used. Some preferred units + * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT + * and SRS_UL_US_FOOT. + * + * @param dfInMeters the value to multiple by a length in the indicated + * units to transform to meters. Some standard conversion factors can + * be found in ogr_srs_api.h. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters( + const char *pszName, double dfInMeters ) + +{ + double dfOldInMeters = GetLinearUnits(); + OGR_SRSNode *poPROJCS = GetAttrNode( "PROJCS" ); + + if( dfInMeters == 0.0 ) + return OGRERR_FAILURE; + + if( dfInMeters == dfOldInMeters || poPROJCS == NULL ) + return SetLinearUnits( pszName, dfInMeters ); + + for( int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ ) + { + const OGR_SRSNode *poChild = poPROJCS->GetChild(iChild); + + if( EQUAL(poChild->GetValue(),"PARAMETER") + && poChild->GetChildCount() > 1 ) + { + char *pszParmName = CPLStrdup(poChild->GetChild(0)->GetValue()); + + if( IsLinearParameter( pszParmName ) ) + { + double dfOldValue = GetProjParm( pszParmName ); + + SetProjParm( pszParmName, + dfOldValue * dfOldInMeters / dfInMeters ); + } + + CPLFree( pszParmName ); + } + } + + return SetLinearUnits( pszName, dfInMeters ); +} + +/************************************************************************/ +/* OSRSetLinearUnitsAndUpdateParameters() */ +/************************************************************************/ + +/** + * \brief Set the linear units for the projection. + * + * This function is the same as OGRSpatialReference::SetLinearUnitsAndUpdateParameters() + */ +OGRErr OSRSetLinearUnitsAndUpdateParameters( OGRSpatialReferenceH hSRS, + const char * pszUnits, + double dfInMeters ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetLinearUnitsAndUpdateParameters", + CE_Failure ); + + return ((OGRSpatialReference *) hSRS)-> + SetLinearUnitsAndUpdateParameters( pszUnits, dfInMeters ); +} + +/************************************************************************/ +/* SetLinearUnits() */ +/************************************************************************/ + +/** + * \brief Set the linear units for the projection. + * + * This method creates a UNIT subnode with the specified values as a + * child of the PROJCS, GEOCCS or LOCAL_CS node. + * + * This method does the same as the C function OSRSetLinearUnits(). + * + * @param pszUnitsName the units name to be used. Some preferred units + * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT + * and SRS_UL_US_FOOT. + * + * @param dfInMeters the value to multiple by a length in the indicated + * units to transform to meters. Some standard conversion factors can + * be found in ogr_srs_api.h. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetLinearUnits( const char * pszUnitsName, + double dfInMeters ) + +{ + return SetTargetLinearUnits( NULL, pszUnitsName, dfInMeters ); +} + +/************************************************************************/ +/* OSRSetLinearUnits() */ +/************************************************************************/ + +/** + * \brief Set the linear units for the projection. + * + * This function is the same as OGRSpatialReference::SetLinearUnits() + */ +OGRErr OSRSetLinearUnits( OGRSpatialReferenceH hSRS, + const char * pszUnits, double dfInMeters ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetLinearUnits", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetLinearUnits( pszUnits, + dfInMeters ); +} + +/************************************************************************/ +/* SetTargetLinearUnits() */ +/************************************************************************/ + +/** + * \brief Set the linear units for the projection. + * + * This method creates a UNIT subnode with the specified values as a + * child of the target node. + * + * This method does the same as the C function OSRSetTargetLinearUnits(). + * + * @param pszTargetKey the keyword to set the linear units for. ie. "PROJCS" or "VERT_CS" + * + * @param pszUnitsName the units name to be used. Some preferred units + * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT + * and SRS_UL_US_FOOT. + * + * @param dfInMeters the value to multiple by a length in the indicated + * units to transform to meters. Some standard conversion factors can + * be found in ogr_srs_api.h. + * + * @return OGRERR_NONE on success. + * + * @since OGR 1.9.0 + */ + +OGRErr OGRSpatialReference::SetTargetLinearUnits( const char *pszTargetKey, + const char * pszUnitsName, + double dfInMeters ) + +{ + OGR_SRSNode *poCS; + OGR_SRSNode *poUnits; + char szValue[128]; + + bNormInfoSet = FALSE; + + if( pszTargetKey == NULL ) + { + poCS = GetAttrNode( "PROJCS" ); + + if( poCS == NULL ) + poCS = GetAttrNode( "LOCAL_CS" ); + if( poCS == NULL ) + poCS = GetAttrNode( "GEOCCS" ); + if( poCS == NULL && IsVertical() ) + poCS = GetAttrNode( "VERT_CS" ); + } + else + poCS = GetAttrNode( pszTargetKey ); + + if( poCS == NULL ) + return OGRERR_FAILURE; + + if( dfInMeters == (int) dfInMeters ) + sprintf( szValue, "%d", (int) dfInMeters ); + else + OGRPrintDouble( szValue, dfInMeters ); + + if( poCS->FindChild( "UNIT" ) >= 0 ) + { + poUnits = poCS->GetChild( poCS->FindChild( "UNIT" ) ); + if (poUnits->GetChildCount() < 2) + return OGRERR_FAILURE; + poUnits->GetChild(0)->SetValue( pszUnitsName ); + poUnits->GetChild(1)->SetValue( szValue ); + if( poUnits->FindChild( "AUTHORITY" ) != -1 ) + poUnits->DestroyChild( poUnits->FindChild( "AUTHORITY" ) ); + } + else + { + poUnits = new OGR_SRSNode( "UNIT" ); + poUnits->AddChild( new OGR_SRSNode( pszUnitsName ) ); + poUnits->AddChild( new OGR_SRSNode( szValue ) ); + + poCS->AddChild( poUnits ); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetLinearUnits() */ +/************************************************************************/ + +/** + * \brief Set the linear units for the target node. + * + * This function is the same as OGRSpatialReference::SetTargetLinearUnits() + * + * @since OGR 1.9.0 + */ +OGRErr OSRSetTargetLinearUnits( OGRSpatialReferenceH hSRS, + const char *pszTargetKey, + const char * pszUnits, double dfInMeters ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetTargetLinearUnits", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)-> + SetTargetLinearUnits( pszTargetKey, pszUnits, dfInMeters ); +} + +/************************************************************************/ +/* GetLinearUnits() */ +/************************************************************************/ + +/** + * \brief Fetch linear projection units. + * + * If no units are available, a value of "Meters" and 1.0 will be assumed. + * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node + * for units. + * + * This method does the same thing as the C function OSRGetLinearUnits()/ + * + * @param ppszName a pointer to be updated with the pointer to the + * units name. The returned value remains internal to the OGRSpatialReference + * and shouldn't be freed, or modified. It may be invalidated on the next + * OGRSpatialReference call. + * + * @return the value to multiply by linear distances to transform them to + * meters. + */ + +double OGRSpatialReference::GetLinearUnits( char ** ppszName ) const + +{ + return GetTargetLinearUnits( NULL, ppszName ); +} + +/************************************************************************/ +/* OSRGetLinearUnits() */ +/************************************************************************/ + +/** + * \brief Fetch linear projection units. + * + * This function is the same as OGRSpatialReference::GetLinearUnits() + */ +double OSRGetLinearUnits( OGRSpatialReferenceH hSRS, char ** ppszName ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetLinearUnits", 0 ); + + return ((OGRSpatialReference *) hSRS)->GetLinearUnits( ppszName ); +} + +/************************************************************************/ +/* GetTargetLinearUnits() */ +/************************************************************************/ + +/** + * \brief Fetch linear units for target. + * + * If no units are available, a value of "Meters" and 1.0 will be assumed. + * + * This method does the same thing as the C function OSRGetTargetLinearUnits()/ + * + * @param pszTargetKey the key to look on. ie. "PROJCS" or "VERT_CS". + * @param ppszName a pointer to be updated with the pointer to the + * units name. The returned value remains internal to the OGRSpatialReference + * and shouldn't be freed, or modified. It may be invalidated on the next + * OGRSpatialReference call. + * + * @return the value to multiply by linear distances to transform them to + * meters. + * + * @since OGR 1.9.0 + */ + +double OGRSpatialReference::GetTargetLinearUnits( const char *pszTargetKey, + char ** ppszName ) const + +{ + const OGR_SRSNode *poCS; + + if( pszTargetKey == NULL ) + { + poCS = GetAttrNode( "PROJCS" ); + + if( poCS == NULL ) + poCS = GetAttrNode( "LOCAL_CS" ); + if( poCS == NULL ) + poCS = GetAttrNode( "GEOCCS" ); + if( poCS == NULL && IsVertical() ) + poCS = GetAttrNode( "VERT_CS" ); + } + else + poCS = GetAttrNode( pszTargetKey ); + + if( ppszName != NULL ) + *ppszName = (char*) "unknown"; + + if( poCS == NULL ) + return 1.0; + + for( int iChild = 0; iChild < poCS->GetChildCount(); iChild++ ) + { + const OGR_SRSNode *poChild = poCS->GetChild(iChild); + + if( EQUAL(poChild->GetValue(),"UNIT") + && poChild->GetChildCount() >= 2 ) + { + if( ppszName != NULL ) + *ppszName = (char *) poChild->GetChild(0)->GetValue(); + + return CPLAtof( poChild->GetChild(1)->GetValue() ); + } + } + + return 1.0; +} + +/************************************************************************/ +/* OSRGetTargetLinearUnits() */ +/************************************************************************/ + +/** + * \brief Fetch linear projection units. + * + * This function is the same as OGRSpatialReference::GetTargetLinearUnits() + * + * @since OGR 1.9.0 + */ +double OSRGetTargetLinearUnits( OGRSpatialReferenceH hSRS, + const char *pszTargetKey, + char ** ppszName ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetTargetLinearUnits", 0 ); + + return ((OGRSpatialReference *) hSRS)->GetTargetLinearUnits( pszTargetKey, + ppszName ); +} + +/************************************************************************/ +/* GetPrimeMeridian() */ +/************************************************************************/ + +/** + * \brief Fetch prime meridian info. + * + * Returns the offset of the prime meridian from greenwich in degrees, + * and the prime meridian name (if requested). If no PRIMEM value exists + * in the coordinate system definition a value of "Greenwich" and an + * offset of 0.0 is assumed. + * + * If the prime meridian name is returned, the pointer is to an internal + * copy of the name. It should not be freed, altered or depended on after + * the next OGR call. + * + * This method is the same as the C function OSRGetPrimeMeridian(). + * + * @param ppszName return location for prime meridian name. If NULL, name + * is not returned. + * + * @return the offset to the GEOGCS prime meridian from greenwich in decimal + * degrees. + */ + +double OGRSpatialReference::GetPrimeMeridian( char **ppszName ) const + +{ + const OGR_SRSNode *poPRIMEM = GetAttrNode( "PRIMEM" ); + + if( poPRIMEM != NULL && poPRIMEM->GetChildCount() >= 2 + && CPLAtof(poPRIMEM->GetChild(1)->GetValue()) != 0.0 ) + { + if( ppszName != NULL ) + *ppszName = (char *) poPRIMEM->GetChild(0)->GetValue(); + return CPLAtof(poPRIMEM->GetChild(1)->GetValue()); + } + + if( ppszName != NULL ) + *ppszName = (char*) SRS_PM_GREENWICH; + + return 0.0; +} + +/************************************************************************/ +/* OSRGetPrimeMeridian() */ +/************************************************************************/ + +/** + * \brief Fetch prime meridian info. + * + * This function is the same as OGRSpatialReference::GetPrimeMeridian() + */ +double OSRGetPrimeMeridian( OGRSpatialReferenceH hSRS, char **ppszName ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetPrimeMeridian", 0 ); + + return ((OGRSpatialReference *) hSRS)->GetPrimeMeridian( ppszName ); +} + +/************************************************************************/ +/* SetGeogCS() */ +/************************************************************************/ + +/** + * \brief Set geographic coordinate system. + * + * This method is used to set the datum, ellipsoid, prime meridian and + * angular units for a geographic coordinate system. It can be used on it's + * own to establish a geographic spatial reference, or applied to a + * projected coordinate system to establish the underlying geographic + * coordinate system. + * + * This method does the same as the C function OSRSetGeogCS(). + * + * @param pszGeogName user visible name for the geographic coordinate system + * (not to serve as a key). + * + * @param pszDatumName key name for this datum. The OpenGIS specification + * lists some known values, and otherwise EPSG datum names with a standard + * transformation are considered legal keys. + * + * @param pszSpheroidName user visible spheroid name (not to serve as a key) + * + * @param dfSemiMajor the semi major axis of the spheroid. + * + * @param dfInvFlattening the inverse flattening for the spheroid. + * This can be computed from the semi minor axis as + * 1/f = 1.0 / (1.0 - semiminor/semimajor). + * + * @param pszPMName the name of the prime merdidian (not to serve as a key) + * If this is NULL a default value of "Greenwich" will be used. + * + * @param dfPMOffset the longitude of greenwich relative to this prime + * meridian. + * + * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some + * standard names). If NULL a value of "degrees" will be assumed. + * + * @param dfConvertToRadians value to multiply angular units by to transform + * them to radians. A value of SRS_UL_DEGREE_CONV will be used if + * pszAngularUnits is NULL. + * + * @return OGRERR_NONE on success. + */ + +OGRErr +OGRSpatialReference::SetGeogCS( const char * pszGeogName, + const char * pszDatumName, + const char * pszSpheroidName, + double dfSemiMajor, double dfInvFlattening, + const char * pszPMName, double dfPMOffset, + const char * pszAngularUnits, + double dfConvertToRadians ) + +{ + bNormInfoSet = FALSE; + +/* -------------------------------------------------------------------- */ +/* For a geocentric coordinate system we want to set the datum */ +/* and ellipsoid based on the GEOGCS. Create the GEOGCS in a */ +/* temporary srs and use the copy method which has special */ +/* handling for GEOCCS. */ +/* -------------------------------------------------------------------- */ + if( IsGeocentric() ) + { + OGRSpatialReference oGCS; + + oGCS.SetGeogCS( pszGeogName, pszDatumName, pszSpheroidName, + dfSemiMajor, dfInvFlattening, + pszPMName, dfPMOffset, + pszAngularUnits, dfConvertToRadians ); + return CopyGeogCSFrom( &oGCS ); + } + +/* -------------------------------------------------------------------- */ +/* Do we already have a GEOGCS? If so, blow it away so it can */ +/* be properly replaced. */ +/* -------------------------------------------------------------------- */ + if( GetAttrNode( "GEOGCS" ) != NULL ) + { + OGR_SRSNode *poCS; + + if( EQUAL(GetRoot()->GetValue(),"GEOGCS") ) + Clear(); + else if( (poCS = GetAttrNode( "PROJCS" )) != NULL + && poCS->FindChild( "GEOGCS" ) != -1 ) + poCS->DestroyChild( poCS->FindChild( "GEOGCS" ) ); + else + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Set defaults for various parameters. */ +/* -------------------------------------------------------------------- */ + if( pszGeogName == NULL ) + pszGeogName = "unnamed"; + + if( pszPMName == NULL ) + pszPMName = SRS_PM_GREENWICH; + + if( pszDatumName == NULL ) + pszDatumName = "unknown"; + + if( pszSpheroidName == NULL ) + pszSpheroidName = "unnamed"; + + if( pszAngularUnits == NULL ) + { + pszAngularUnits = SRS_UA_DEGREE; + dfConvertToRadians = CPLAtof(SRS_UA_DEGREE_CONV); + } + +/* -------------------------------------------------------------------- */ +/* Build the GEOGCS object. */ +/* -------------------------------------------------------------------- */ + char szValue[128]; + OGR_SRSNode *poGeogCS, *poSpheroid, *poDatum, *poPM, *poUnits; + + poGeogCS = new OGR_SRSNode( "GEOGCS" ); + poGeogCS->AddChild( new OGR_SRSNode( pszGeogName ) ); + +/* -------------------------------------------------------------------- */ +/* Setup the spheroid. */ +/* -------------------------------------------------------------------- */ + poSpheroid = new OGR_SRSNode( "SPHEROID" ); + poSpheroid->AddChild( new OGR_SRSNode( pszSpheroidName ) ); + + OGRPrintDouble( szValue, dfSemiMajor ); + poSpheroid->AddChild( new OGR_SRSNode(szValue) ); + + OGRPrintDouble( szValue, dfInvFlattening ); + poSpheroid->AddChild( new OGR_SRSNode(szValue) ); + +/* -------------------------------------------------------------------- */ +/* Setup the Datum. */ +/* -------------------------------------------------------------------- */ + poDatum = new OGR_SRSNode( "DATUM" ); + poDatum->AddChild( new OGR_SRSNode(pszDatumName) ); + poDatum->AddChild( poSpheroid ); + +/* -------------------------------------------------------------------- */ +/* Setup the prime meridian. */ +/* -------------------------------------------------------------------- */ + if( dfPMOffset == 0.0 ) + strcpy( szValue, "0" ); + else + OGRPrintDouble( szValue, dfPMOffset ); + + poPM = new OGR_SRSNode( "PRIMEM" ); + poPM->AddChild( new OGR_SRSNode( pszPMName ) ); + poPM->AddChild( new OGR_SRSNode( szValue ) ); + +/* -------------------------------------------------------------------- */ +/* Setup the rotational units. */ +/* -------------------------------------------------------------------- */ + OGRPrintDouble( szValue, dfConvertToRadians ); + + poUnits = new OGR_SRSNode( "UNIT" ); + poUnits->AddChild( new OGR_SRSNode(pszAngularUnits) ); + poUnits->AddChild( new OGR_SRSNode(szValue) ); + +/* -------------------------------------------------------------------- */ +/* Complete the GeogCS */ +/* -------------------------------------------------------------------- */ + poGeogCS->AddChild( poDatum ); + poGeogCS->AddChild( poPM ); + poGeogCS->AddChild( poUnits ); + +/* -------------------------------------------------------------------- */ +/* Attach below the PROJCS if there is one, or make this the root. */ +/* -------------------------------------------------------------------- */ + if( GetRoot() != NULL && EQUAL(GetRoot()->GetValue(),"PROJCS") ) + poRoot->InsertChild( poGeogCS, 1 ); + else + SetRoot( poGeogCS ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetGeogCS() */ +/************************************************************************/ + +/** + * \brief Set geographic coordinate system. + * + * This function is the same as OGRSpatialReference::SetGeogCS() + */ +OGRErr OSRSetGeogCS( OGRSpatialReferenceH hSRS, + const char * pszGeogName, + const char * pszDatumName, + const char * pszSpheroidName, + double dfSemiMajor, double dfInvFlattening, + const char * pszPMName, double dfPMOffset, + const char * pszAngularUnits, + double dfConvertToRadians ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetGeogCS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetGeogCS( + pszGeogName, pszDatumName, + pszSpheroidName, dfSemiMajor, dfInvFlattening, + pszPMName, dfPMOffset, pszAngularUnits, dfConvertToRadians ); + +} + +/************************************************************************/ +/* SetWellKnownGeogCS() */ +/************************************************************************/ + +/** + * \brief Set a GeogCS based on well known name. + * + * This may be called on an empty OGRSpatialReference to make a geographic + * coordinate system, or on something with an existing PROJCS node to + * set the underlying geographic coordinate system of a projected coordinate + * system. + * + * The following well known text values are currently supported: + *
      + *
    • "WGS84": same as "EPSG:4326" but has no dependence on EPSG data files. + *
    • "WGS72": same as "EPSG:4322" but has no dependence on EPSG data files. + *
    • "NAD27": same as "EPSG:4267" but has no dependence on EPSG data files. + *
    • "NAD83": same as "EPSG:4269" but has no dependence on EPSG data files. + *
    • "EPSG:n": same as doing an ImportFromEPSG(n). + *
    + * + * @param pszName name of well known geographic coordinate system. + * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't + * recognised, the target object is already initialized, or an EPSG value + * can't be successfully looked up. + */ + +OGRErr OGRSpatialReference::SetWellKnownGeogCS( const char * pszName ) + +{ + OGRSpatialReference oSRS2; + OGRErr eErr; + +/* -------------------------------------------------------------------- */ +/* Check for EPSG authority numbers. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszName, "EPSG:",5) ) + { + eErr = oSRS2.importFromEPSG( atoi(pszName+5) ); + if( eErr != OGRERR_NONE ) + return eErr; + + if( !oSRS2.IsGeographic() ) + return OGRERR_FAILURE; + + return CopyGeogCSFrom( &oSRS2 ); + } + +/* -------------------------------------------------------------------- */ +/* Check for EPSGA authority numbers. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszName, "EPSGA:",6) ) + { + eErr = oSRS2.importFromEPSGA( atoi(pszName+6) ); + if( eErr != OGRERR_NONE ) + return eErr; + + if( !oSRS2.IsGeographic() ) + return OGRERR_FAILURE; + + return CopyGeogCSFrom( &oSRS2 ); + } + +/* -------------------------------------------------------------------- */ +/* Check for simple names. */ +/* -------------------------------------------------------------------- */ + char *pszWKT = NULL; + + if( EQUAL(pszName, "WGS84") || EQUAL(pszName,"CRS84") || EQUAL(pszName,"CRS:84") ) + pszWKT = (char* ) SRS_WKT_WGS84; + + else if( EQUAL(pszName, "WGS72") ) + pszWKT = (char* ) "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\",SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],TOWGS84[0,0,4.5,0,0,0.554,0.2263],AUTHORITY[\"EPSG\",\"6322\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4322\"]]"; + + else if( EQUAL(pszName, "NAD27") || EQUAL(pszName, "CRS27") || EQUAL(pszName,"CRS:27") ) + pszWKT = (char* ) "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\",SPHEROID[\"Clarke 1866\",6378206.4,294.978698213898,AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4267\"]]"; + + else if( EQUAL(pszName, "NAD83") || EQUAL(pszName,"CRS83") || EQUAL(pszName,"CRS:83") ) + pszWKT = (char* ) "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6269\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4269\"]]"; + + else + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Import the WKT */ +/* -------------------------------------------------------------------- */ + eErr = oSRS2.importFromWkt( &pszWKT ); + if( eErr != OGRERR_NONE ) + return eErr; + +/* -------------------------------------------------------------------- */ +/* Copy over. */ +/* -------------------------------------------------------------------- */ + return CopyGeogCSFrom( &oSRS2 ); +} + +/************************************************************************/ +/* OSRSetWellKnownGeogCS() */ +/************************************************************************/ + +/** + * \brief Set a GeogCS based on well known name. + * + * This function is the same as OGRSpatialReference::SetWellKnownGeogCS() + */ +OGRErr OSRSetWellKnownGeogCS( OGRSpatialReferenceH hSRS, const char *pszName ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetWellKnownGeogCS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetWellKnownGeogCS( pszName ); +} + +/************************************************************************/ +/* CopyGeogCSFrom() */ +/************************************************************************/ + +/** + * \brief Copy GEOGCS from another OGRSpatialReference. + * + * The GEOGCS information is copied into this OGRSpatialReference from another. + * If this object has a PROJCS root already, the GEOGCS is installed within + * it, otherwise it is installed as the root. + * + * @param poSrcSRS the spatial reference to copy the GEOGCS information from. + * + * @return OGRERR_NONE on success or an error code. + */ + + +OGRErr OGRSpatialReference::CopyGeogCSFrom( + const OGRSpatialReference * poSrcSRS ) + +{ + const OGR_SRSNode *poGeogCS = NULL; + + bNormInfoSet = FALSE; + +/* -------------------------------------------------------------------- */ +/* Handle geocentric coordinate systems specially. We just */ +/* want to copy the DATUM and PRIMEM nodes. */ +/* -------------------------------------------------------------------- */ + if( IsGeocentric() ) + { + if( GetRoot()->FindChild( "DATUM" ) != -1 ) + GetRoot()->DestroyChild( GetRoot()->FindChild( "DATUM" ) ); + if( GetRoot()->FindChild( "PRIMEM" ) != -1 ) + GetRoot()->DestroyChild( GetRoot()->FindChild( "PRIMEM" ) ); + + const OGR_SRSNode *poDatum = poSrcSRS->GetAttrNode( "DATUM" ); + const OGR_SRSNode *poPrimeM = poSrcSRS->GetAttrNode( "PRIMEM" ); + + if( poDatum == NULL || poPrimeM == NULL ) + return OGRERR_FAILURE; + + poRoot->InsertChild( poDatum->Clone(), 1 ); + poRoot->InsertChild( poPrimeM->Clone(), 2 ); + + return OGRERR_NONE; + + } + +/* -------------------------------------------------------------------- */ +/* Do we already have a GEOGCS? If so, blow it away so it can */ +/* be properly replaced. */ +/* -------------------------------------------------------------------- */ + if( GetAttrNode( "GEOGCS" ) != NULL ) + { + OGR_SRSNode *poPROJCS; + + if( EQUAL(GetRoot()->GetValue(),"GEOGCS") ) + Clear(); + else if( (poPROJCS = GetAttrNode( "PROJCS" )) != NULL + && poPROJCS->FindChild( "GEOGCS" ) != -1 ) + poPROJCS->DestroyChild( poPROJCS->FindChild( "GEOGCS" ) ); + else + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Find the GEOGCS node on the source. */ +/* -------------------------------------------------------------------- */ + poGeogCS = poSrcSRS->GetAttrNode( "GEOGCS" ); + if( poGeogCS == NULL ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Attach below the PROJCS if there is one, or make this the root. */ +/* -------------------------------------------------------------------- */ + if( GetRoot() != NULL && EQUAL(GetRoot()->GetValue(),"PROJCS") ) + poRoot->InsertChild( poGeogCS->Clone(), 1 ); + else + SetRoot( poGeogCS->Clone() ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRCopyGeogCSFrom() */ +/************************************************************************/ + +/** + * \brief Copy GEOGCS from another OGRSpatialReference. + * + * This function is the same as OGRSpatialReference::CopyGeogCSFrom() + */ +OGRErr OSRCopyGeogCSFrom( OGRSpatialReferenceH hSRS, + OGRSpatialReferenceH hSrcSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRCopyGeogCSFrom", CE_Failure ); + VALIDATE_POINTER1( hSrcSRS, "OSRCopyGeogCSFrom", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->CopyGeogCSFrom( + (const OGRSpatialReference *) hSrcSRS ); +} + +/************************************************************************/ +/* SetFromUserInput() */ +/************************************************************************/ + +/** + * \brief Set spatial reference from various text formats. + * + * This method will examine the provided input, and try to deduce the + * format, and then use it to initialize the spatial reference system. It + * may take the following forms: + * + *
      + *
    1. Well Known Text definition - passed on to importFromWkt(). + *
    2. "EPSG:n" - number passed on to importFromEPSG(). + *
    3. "EPSGA:n" - number passed on to importFromEPSGA(). + *
    4. "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections. + *
    5. "urn:ogc:def:crs:EPSG::n" - ogc urns + *
    6. PROJ.4 definitions - passed on to importFromProj4(). + *
    7. filename - file read for WKT, XML or PROJ.4 definition. + *
    8. well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83, + * WGS84 or WGS72. + *
    9. WKT (directly or in a file) in ESRI format should be prefixed with + * ESRI:: to trigger an automatic morphFromESRI(). + *
    10. "IGNF:xxx" - "+init=IGNF:xxx" passed on to importFromProj4(). + *
    + * + * It is expected that this method will be extended in the future to support + * XML and perhaps a simplified "minilanguage" for indicating common UTM and + * State Plane definitions. + * + * This method is intended to be flexible, but by it's nature it is + * imprecise as it must guess information about the format intended. When + * possible applications should call the specific method appropriate if the + * input is known to be in a particular format. + * + * This method does the same thing as the OSRSetFromUserInput() function. + * + * @param pszDefinition text definition to try to deduce SRS from. + * + * @return OGRERR_NONE on success, or an error code if the name isn't + * recognised, the definition is corrupt, or an EPSG value can't be + * successfully looked up. + */ + +OGRErr OGRSpatialReference::SetFromUserInput( const char * pszDefinition ) + +{ + int bESRI = FALSE; + OGRErr err; + + if( EQUALN(pszDefinition,"ESRI::",6) ) + { + bESRI = TRUE; + pszDefinition += 6; + } + +/* -------------------------------------------------------------------- */ +/* Is it a recognised syntax? */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszDefinition,"PROJCS",6) + || EQUALN(pszDefinition,"GEOGCS",6) + || EQUALN(pszDefinition,"COMPD_CS",8) + || EQUALN(pszDefinition,"GEOCCS",6) + || EQUALN(pszDefinition,"VERT_CS",7) + || EQUALN(pszDefinition,"LOCAL_CS",8) ) + { + err = importFromWkt( (char **) &pszDefinition ); + if( err == OGRERR_NONE && bESRI ) + err = morphFromESRI(); + + return err; + } + + if( EQUALN(pszDefinition,"EPSG:",5) + || EQUALN(pszDefinition,"EPSGA:",6) ) + { + OGRErr eStatus; + + if( EQUALN(pszDefinition,"EPSG:",5) ) + eStatus = importFromEPSG( atoi(pszDefinition+5) ); + + else /* if( EQUALN(pszDefinition,"EPSGA:",6) ) */ + eStatus = importFromEPSGA( atoi(pszDefinition+6) ); + + // Do we want to turn this into a compound definition + // with a vertical datum? + if( eStatus == OGRERR_NONE && strchr( pszDefinition, '+' ) != NULL ) + { + OGRSpatialReference oVertSRS; + + eStatus = oVertSRS.importFromEPSG( + atoi(strchr(pszDefinition,'+')+1) ); + if( eStatus == OGRERR_NONE ) + { + OGR_SRSNode *poHorizSRS = GetRoot()->Clone(); + + Clear(); + + CPLString osName = poHorizSRS->GetChild(0)->GetValue(); + osName += " + "; + osName += oVertSRS.GetRoot()->GetChild(0)->GetValue(); + + SetNode( "COMPD_CS", osName ); + GetRoot()->AddChild( poHorizSRS ); + GetRoot()->AddChild( oVertSRS.GetRoot()->Clone() ); + } + + return eStatus; + } + else + return eStatus; + } + + if( EQUALN(pszDefinition,"urn:ogc:def:crs:",16) + || EQUALN(pszDefinition,"urn:ogc:def:crs,crs:",20) + || EQUALN(pszDefinition,"urn:x-ogc:def:crs:",18) + || EQUALN(pszDefinition,"urn:opengis:crs:",16) + || EQUALN(pszDefinition,"urn:opengis:def:crs:",20)) + return importFromURN( pszDefinition ); + + if( EQUALN(pszDefinition,"http://opengis.net/def/crs",26) + || EQUALN(pszDefinition,"http://www.opengis.net/def/crs",30) + || EQUALN(pszDefinition,"www.opengis.net/def/crs",23)) + return importFromCRSURL( pszDefinition ); + + if( EQUALN(pszDefinition,"AUTO:",5) ) + return importFromWMSAUTO( pszDefinition ); + + if( EQUALN(pszDefinition,"OGC:",4) ) // WMS/WCS OGC codes like OGC:CRS84 + return SetWellKnownGeogCS( pszDefinition+4 ); + + if( EQUALN(pszDefinition,"CRS:",4) ) + return SetWellKnownGeogCS( pszDefinition ); + + if( EQUALN(pszDefinition,"DICT:",5) + && strstr(pszDefinition,",") ) + { + char *pszFile = CPLStrdup(pszDefinition+5); + char *pszCode = strstr(pszFile,",") + 1; + + pszCode[-1] = '\0'; + + err = importFromDict( pszFile, pszCode ); + CPLFree( pszFile ); + + if( err == OGRERR_NONE && bESRI ) + err = morphFromESRI(); + + return err; + } + + if( EQUAL(pszDefinition,"NAD27") + || EQUAL(pszDefinition,"NAD83") + || EQUAL(pszDefinition,"WGS84") + || EQUAL(pszDefinition,"WGS72") ) + { + Clear(); + return SetWellKnownGeogCS( pszDefinition ); + } + + if( strstr(pszDefinition,"+proj") != NULL + || strstr(pszDefinition,"+init") != NULL ) + return importFromProj4( pszDefinition ); + + if( EQUALN(pszDefinition,"IGNF:", 5) ) + { + char* pszProj4Str = (char*) CPLMalloc(6 + strlen(pszDefinition) + 1); + strcpy(pszProj4Str, "+init="); + strcat(pszProj4Str, pszDefinition); + err = importFromProj4( pszProj4Str ); + CPLFree(pszProj4Str); + + return err; + } + + if( EQUALN(pszDefinition,"http://",7) ) + { + return importFromUrl (pszDefinition); + } + + if( EQUAL(pszDefinition,"osgb:BNG") ) + { + return importFromEPSG(27700); + } + +/* -------------------------------------------------------------------- */ +/* Try to open it as a file. */ +/* -------------------------------------------------------------------- */ + FILE *fp; + int nBufMax = 100000; + char *pszBufPtr, *pszBuffer; + int nBytes; + + fp = VSIFOpen( pszDefinition, "rt" ); + if( fp == NULL ) + return OGRERR_CORRUPT_DATA; + + pszBuffer = (char *) CPLMalloc(nBufMax); + nBytes = VSIFRead( pszBuffer, 1, nBufMax-1, fp ); + VSIFClose( fp ); + + if( nBytes == nBufMax-1 ) + { + CPLDebug( "OGR", + "OGRSpatialReference::SetFromUserInput(%s), opened file\n" + "but it is to large for our generous buffer. Is it really\n" + "just a WKT definition?", pszDefinition ); + CPLFree( pszBuffer ); + return OGRERR_FAILURE; + } + + pszBuffer[nBytes] = '\0'; + + pszBufPtr = pszBuffer; + while( pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n' ) + pszBufPtr++; + + if( pszBufPtr[0] == '<' ) + err = importFromXML( pszBufPtr ); + else if( (strstr(pszBuffer,"+proj") != NULL + || strstr(pszBuffer,"+init") != NULL) + && (strstr(pszBuffer,"EXTENSION") == NULL + && strstr(pszBuffer,"extension") == NULL) ) + err = importFromProj4( pszBufPtr ); + else + { + if( EQUALN(pszBufPtr,"ESRI::",6) ) + { + bESRI = TRUE; + pszBufPtr += 6; + } + + err = importFromWkt( &pszBufPtr ); + if( err == OGRERR_NONE && bESRI ) + err = morphFromESRI(); + } + + CPLFree( pszBuffer ); + + return err; +} + +/************************************************************************/ +/* OSRSetFromUserInput() */ +/************************************************************************/ + +/** + * \brief Set spatial reference from various text formats. + * + * This function is the same as OGRSpatialReference::SetFromUserInput() + */ +OGRErr CPL_STDCALL OSRSetFromUserInput( OGRSpatialReferenceH hSRS, + const char *pszDef ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetFromUserInput", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetFromUserInput( pszDef ); +} + + +/************************************************************************/ +/* ImportFromUrl() */ +/************************************************************************/ + +/** + * \brief Set spatial reference from a URL. + * + * This method will download the spatial reference at a given URL and + * feed it into SetFromUserInput for you. + * + * This method does the same thing as the OSRImportFromUrl() function. + * + * @param pszUrl text definition to try to deduce SRS from. + * + * @return OGRERR_NONE on success, or an error code with the curl + * error message if it is unable to dowload data. + */ + +OGRErr OGRSpatialReference::importFromUrl( const char * pszUrl ) + +{ + + + if( !EQUALN(pszUrl,"http://",7) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The given string is not recognized as a URL" + "starting with 'http://' -- %s", pszUrl ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Fetch the result. */ +/* -------------------------------------------------------------------- */ + CPLErrorReset(); + + const char* pszHeaders = "HEADERS=Accept: application/x-ogcwkt"; + const char* pszTimeout = "TIMEOUT=10"; + char *apszOptions[] = { + (char *) pszHeaders, + (char *) pszTimeout, + NULL + }; + + CPLHTTPResult *psResult = CPLHTTPFetch( pszUrl, apszOptions ); + +/* -------------------------------------------------------------------- */ +/* Try to handle errors. */ +/* -------------------------------------------------------------------- */ + + if ( psResult == NULL) + return OGRERR_FAILURE; + if( psResult->nDataLen == 0 + || CPLGetLastErrorNo() != 0 || psResult->pabyData == NULL ) + { + if (CPLGetLastErrorNo() == 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "No data was returned from the given URL" ); + } + CPLHTTPDestroyResult( psResult ); + return OGRERR_FAILURE; + } + + if (psResult->nStatus != 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Curl reports error: %d: %s", psResult->nStatus, psResult->pszErrBuf ); + CPLHTTPDestroyResult( psResult ); + return OGRERR_FAILURE; + } + + if( EQUALN( (const char*) psResult->pabyData,"http://",7) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "The data that was downloaded also starts with 'http://' " + "and cannot be passed into SetFromUserInput. Is this " + "really a spatial reference definition? "); + CPLHTTPDestroyResult( psResult ); + return OGRERR_FAILURE; + } + if( OGRERR_NONE != SetFromUserInput( (const char *) psResult->pabyData )) { + CPLHTTPDestroyResult( psResult ); + return OGRERR_FAILURE; + } + + CPLHTTPDestroyResult( psResult ); + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRimportFromUrl() */ +/************************************************************************/ + +/** + * \brief Set spatial reference from a URL. + * + * This function is the same as OGRSpatialReference::importFromUrl() + */ +OGRErr OSRImportFromUrl( OGRSpatialReferenceH hSRS, const char *pszUrl ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromUrl", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->importFromUrl( pszUrl ); +} + +/************************************************************************/ +/* importFromURNPart() */ +/************************************************************************/ +OGRErr OGRSpatialReference::importFromURNPart(const char* pszAuthority, + const char* pszCode, + const char* pszURN) +{ + +/* -------------------------------------------------------------------- */ +/* Is this an EPSG code? Note that we import it with EPSG */ +/* preferred axis ordering for geographic coordinate systems! */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszAuthority,"EPSG",4) ) + return importFromEPSGA( atoi(pszCode) ); + +/* -------------------------------------------------------------------- */ +/* Is this an IAU code? Lets try for the IAU2000 dictionary. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszAuthority,"IAU",3) ) + return importFromDict( "IAU2000.wkt", pszCode ); + +/* -------------------------------------------------------------------- */ +/* Is this an OGC code? */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(pszAuthority,"OGC",3) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "URN %s has unrecognised authority.", + pszURN ); + return OGRERR_FAILURE; + } + + if( EQUALN(pszCode,"CRS84",5) ) + return SetWellKnownGeogCS( pszCode ); + else if( EQUALN(pszCode,"CRS83",5) ) + return SetWellKnownGeogCS( pszCode ); + else if( EQUALN(pszCode,"CRS27",5) ) + return SetWellKnownGeogCS( pszCode ); + else if( EQUALN(pszCode,"84",2) ) /* urn:ogc:def:crs:OGC:2:84 */ + return SetWellKnownGeogCS( "CRS84" ); + +/* -------------------------------------------------------------------- */ +/* Handle auto codes. We need to convert from format */ +/* AUTO42001:99:8888 to format AUTO:42001,99,8888. */ +/* -------------------------------------------------------------------- */ + else if( EQUALN(pszCode,"AUTO",4) ) + { + char szWMSAuto[100]; + int i; + + if( strlen(pszCode) > sizeof(szWMSAuto)-2 ) + return OGRERR_FAILURE; + + strcpy( szWMSAuto, "AUTO:" ); + strcpy( szWMSAuto + 5, pszCode + 4 ); + for( i = 5; szWMSAuto[i] != '\0'; i++ ) + { + if( szWMSAuto[i] == ':' ) + szWMSAuto[i] = ','; + } + + return importFromWMSAUTO( szWMSAuto ); + } + +/* -------------------------------------------------------------------- */ +/* Not a recognise OGC item. */ +/* -------------------------------------------------------------------- */ + CPLError( CE_Failure, CPLE_AppDefined, + "URN %s value not supported.", + pszURN ); + + return OGRERR_FAILURE; +} + +/************************************************************************/ +/* importFromURN() */ +/* */ +/* See OGC recommendation paper 06-023r1 or later for details. */ +/************************************************************************/ + +/** + * \brief Initialize from OGC URN. + * + * Initializes this spatial reference from a coordinate system defined + * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation + * paper 06-023r1. Currently EPSG and OGC authority values are supported, + * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88). + * + * This method is also support through SetFromUserInput() which can + * normally be used for URNs. + * + * @param pszURN the urn string. + * + * @return OGRERR_NONE on success or an error code. + */ + +OGRErr OGRSpatialReference::importFromURN( const char *pszURN ) + +{ + const char *pszCur; + + if( EQUALN(pszURN,"urn:ogc:def:crs:",16) ) + pszCur = pszURN + 16; + else if( EQUALN(pszURN,"urn:ogc:def:crs,crs:",20) ) + pszCur = pszURN + 20; + else if( EQUALN(pszURN,"urn:x-ogc:def:crs:",18) ) + pszCur = pszURN + 18; + else if( EQUALN(pszURN,"urn:opengis:crs:",16) ) + pszCur = pszURN + 16; + else if( EQUALN(pszURN,"urn:opengis:def:crs:",20) ) + pszCur = pszURN + 20; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "URN %s not a supported format.", pszURN ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Clear any existing definition. */ +/* -------------------------------------------------------------------- */ + if( GetRoot() != NULL ) + { + delete poRoot; + poRoot = NULL; + } + +/* -------------------------------------------------------------------- */ +/* Find code (ignoring version) out of string like: */ +/* */ +/* authority:[version]:code */ +/* -------------------------------------------------------------------- */ + const char *pszAuthority = pszCur; + + // skip authority + while( *pszCur != ':' && *pszCur ) + pszCur++; + if( *pszCur == ':' ) + pszCur++; + + // skip version + const char* pszBeforeVersion = pszCur; + while( *pszCur != ':' && *pszCur ) + pszCur++; + if( *pszCur == ':' ) + pszCur++; + else + /* We come here in the case, the content to parse is authority:code (instead of authority::code) */ + /* which is probably illegal according to http://www.opengeospatial.org/ogcUrnPolicy */ + /* but such content is found for example in what is returned by GeoServer */ + pszCur = pszBeforeVersion; + + const char *pszCode = pszCur; + + const char* pszComma = strchr(pszCur, ','); + if (pszComma == NULL) + return importFromURNPart(pszAuthority, pszCode, pszURN); + + + /* There's a second part with the vertical SRS */ + pszCur = pszComma + 1; + if (strncmp(pszCur, "crs:", 4) != 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "URN %s not a supported format.", pszURN ); + return OGRERR_FAILURE; + } + + pszCur += 4; + + char* pszFirstCode = CPLStrdup(pszCode); + pszFirstCode[pszComma - pszCode] = '\0'; + OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN); + CPLFree(pszFirstCode); + + // Do we want to turn this into a compound definition + // with a vertical datum? + if( eStatus == OGRERR_NONE ) + { + OGRSpatialReference oVertSRS; + + /* -------------------------------------------------------------------- */ + /* Find code (ignoring version) out of string like: */ + /* */ + /* authority:[version]:code */ + /* -------------------------------------------------------------------- */ + pszAuthority = pszCur; + + // skip authority + while( *pszCur != ':' && *pszCur ) + pszCur++; + if( *pszCur == ':' ) + pszCur++; + + // skip version + pszBeforeVersion = pszCur; + while( *pszCur != ':' && *pszCur ) + pszCur++; + if( *pszCur == ':' ) + pszCur++; + else + pszCur = pszBeforeVersion; + + pszCode = pszCur; + + eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN); + if( eStatus == OGRERR_NONE ) + { + OGR_SRSNode *poHorizSRS = GetRoot()->Clone(); + + Clear(); + + CPLString osName = poHorizSRS->GetChild(0)->GetValue(); + osName += " + "; + osName += oVertSRS.GetRoot()->GetChild(0)->GetValue(); + + SetNode( "COMPD_CS", osName ); + GetRoot()->AddChild( poHorizSRS ); + GetRoot()->AddChild( oVertSRS.GetRoot()->Clone() ); + } + + return eStatus; + } + else + return eStatus; +} + +/************************************************************************/ +/* importFromCRSURL() */ +/* */ +/* See OGC Best Practice document 11-135 for details. */ +/************************************************************************/ + +/** + * \brief Initialize from OGC URL. + * + * Initializes this spatial reference from a coordinate system defined + * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice + * paper 11-135. Currently EPSG and OGC authority values are supported, + * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88). + * + * This method is also supported through SetFromUserInput() which can + * normally be used for URLs. + * + * @param pszURL the URL string. + * + * @return OGRERR_NONE on success or an error code. + */ + +OGRErr OGRSpatialReference::importFromCRSURL( const char *pszURL ) + +{ + const char *pszCur; + + if( EQUALN(pszURL,"http://opengis.net/def/crs",26) ) + pszCur = pszURL + 26; + else if( EQUALN(pszURL,"http://www.opengis.net/def/crs",30) ) + pszCur = pszURL + 30; + else if( EQUALN(pszURL,"www.opengis.net/def/crs",23) ) + pszCur = pszURL + 23; + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "URL %s not a supported format.", pszURL ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Clear any existing definition. */ +/* -------------------------------------------------------------------- */ + if( GetRoot() != NULL ) + { + delete poRoot; + poRoot = NULL; + } + + if( EQUALN(pszCur, "-compound?1=", 12) ) + { +/* -------------------------------------------------------------------- */ +/* It's a compound CRS, of the form: */ +/* */ +/* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */ +/* -------------------------------------------------------------------- */ + pszCur += 12; + + // extract each component CRS URL + int iComponentUrl = 2; + + CPLString osName = ""; + Clear(); + + while (iComponentUrl != -1) + { + char searchStr[5]; + sprintf(searchStr, "&%d=", iComponentUrl); + + const char* pszUrlEnd = strstr(pszCur, searchStr); + + // figure out the next component URL + char* pszComponentUrl; + + if( pszUrlEnd ) + { + size_t nLen = pszUrlEnd - pszCur; + pszComponentUrl = (char*) CPLMalloc(nLen + 1); + strncpy(pszComponentUrl, pszCur, nLen); + pszComponentUrl[nLen] = '\0'; + + ++iComponentUrl; + pszCur += nLen + strlen(searchStr); + } + else + { + if( iComponentUrl == 2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Compound CRS URLs must have at least two component CRSs." ); + return OGRERR_FAILURE; + } + else + { + pszComponentUrl = CPLStrdup(pszCur); + // no more components + iComponentUrl = -1; + } + } + + OGRSpatialReference oComponentSRS; + OGRErr eStatus = oComponentSRS.importFromCRSURL( pszComponentUrl ); + + CPLFree(pszComponentUrl); + pszComponentUrl = NULL; + + if( eStatus == OGRERR_NONE ) + { + if( osName.length() != 0 ) + { + osName += " + "; + } + osName += oComponentSRS.GetRoot()->GetValue(); + SetNode( "COMPD_CS", osName ); + GetRoot()->AddChild( oComponentSRS.GetRoot()->Clone() ); + } + else + return eStatus; + } + + + return OGRERR_NONE; + } + else + { +/* -------------------------------------------------------------------- */ +/* It's a normal CRS URL, of the form: */ +/* */ +/* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */ +/* -------------------------------------------------------------------- */ + ++pszCur; + const char *pszAuthority = pszCur; + + // skip authority + while( *pszCur != '/' && *pszCur ) + pszCur++; + if( *pszCur == '/' ) + pszCur++; + + + // skip version + while( *pszCur != '/' && *pszCur ) + pszCur++; + if( *pszCur == '/' ) + pszCur++; + + const char *pszCode = pszCur; + + return importFromURNPart( pszAuthority, pszCode, pszURL ); + } +} + +/************************************************************************/ +/* importFromWMSAUTO() */ +/************************************************************************/ + +/** + * \brief Initialize from WMSAUTO string. + * + * Note that the WMS 1.3 specification does not include the + * units code, while apparently earlier specs do. We try to + * guess around this. + * + * @param pszDefinition the WMSAUTO string + * + * @return OGRERR_NONE on success or an error code. + */ +OGRErr OGRSpatialReference::importFromWMSAUTO( const char * pszDefinition ) + +{ + char **papszTokens; + int nProjId, nUnitsId; + double dfRefLong, dfRefLat = 0.0; + +/* -------------------------------------------------------------------- */ +/* Tokenize */ +/* -------------------------------------------------------------------- */ + if( EQUALN(pszDefinition,"AUTO:",5) ) + pszDefinition += 5; + + papszTokens = CSLTokenizeStringComplex( pszDefinition, ",", FALSE, TRUE ); + + if( CSLCount(papszTokens) == 4 ) + { + nProjId = atoi(papszTokens[0]); + nUnitsId = atoi(papszTokens[1]); + dfRefLong = CPLAtof(papszTokens[2]); + dfRefLat = CPLAtof(papszTokens[3]); + } + else if( CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005 ) + { + nProjId = atoi(papszTokens[0]); + nUnitsId = atoi(papszTokens[1]); + dfRefLong = CPLAtof(papszTokens[2]); + dfRefLat = 0.0; + } + else if( CSLCount(papszTokens) == 3 ) + { + nProjId = atoi(papszTokens[0]); + nUnitsId = 9001; + dfRefLong = CPLAtof(papszTokens[1]); + dfRefLat = CPLAtof(papszTokens[2]); + + } + else if( CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005 ) + { + nProjId = atoi(papszTokens[0]); + nUnitsId = 9001; + dfRefLong = CPLAtof(papszTokens[1]); + } + else + { + CSLDestroy( papszTokens ); + CPLError( CE_Failure, CPLE_AppDefined, + "AUTO projection has wrong number of arguments, expected\n" + "AUTO:proj_id,units_id,ref_long,ref_lat or" + "AUTO:proj_id,ref_long,ref_lat" ); + return OGRERR_FAILURE; + } + + CSLDestroy( papszTokens ); + +/* -------------------------------------------------------------------- */ +/* Build coordsys. */ +/* -------------------------------------------------------------------- */ + Clear(); + + switch( nProjId ) + { + case 42001: // Auto UTM + SetUTM( (int) floor( (dfRefLong + 180.0) / 6.0 ) + 1, + dfRefLat >= 0.0 ); + break; + + case 42002: // Auto TM (strangely very UTM-like). + SetTM( 0, dfRefLong, 0.9996, + 500000.0, (dfRefLat >= 0.0) ? 0.0 : 10000000.0 ); + break; + + case 42003: // Auto Orthographic. + SetOrthographic( dfRefLat, dfRefLong, 0.0, 0.0 ); + break; + + case 42004: // Auto Equirectangular + SetEquirectangular( dfRefLat, dfRefLong, 0.0, 0.0 ); + break; + + case 42005: + SetMollweide( dfRefLong, 0.0, 0.0 ); + break; + + default: + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported projection id in importFromWMSAUTO(): %d", + nProjId ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Set units. */ +/* -------------------------------------------------------------------- */ + + switch( nUnitsId ) + { + case 9001: + SetLinearUnits( SRS_UL_METER, 1.0 ); + break; + + case 9002: + SetLinearUnits( "Foot", 0.3048 ); + break; + + case 9003: + SetLinearUnits( "US survey foot", CPLAtof(SRS_UL_US_FOOT_CONV) ); + break; + + default: + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported units code (%d).", + nUnitsId ); + return OGRERR_FAILURE; + break; + } + + SetAuthority( "PROJCS|UNIT", "EPSG", nUnitsId ); + +/* -------------------------------------------------------------------- */ +/* Set WGS84. */ +/* -------------------------------------------------------------------- */ + SetWellKnownGeogCS( "WGS84" ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* GetSemiMajor() */ +/************************************************************************/ + +/** + * \brief Get spheroid semi major axis. + * + * This method does the same thing as the C function OSRGetSemiMajor(). + * + * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis + * can be found. + * + * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found. + */ + +double OGRSpatialReference::GetSemiMajor( OGRErr * pnErr ) const + +{ + const OGR_SRSNode *poSpheroid = GetAttrNode( "SPHEROID" ); + + if( pnErr != NULL ) + *pnErr = OGRERR_NONE; + + if( poSpheroid != NULL && poSpheroid->GetChildCount() >= 3 ) + { + return CPLAtof( poSpheroid->GetChild(1)->GetValue() ); + } + else + { + if( pnErr != NULL ) + *pnErr = OGRERR_FAILURE; + + return SRS_WGS84_SEMIMAJOR; + } +} + +/************************************************************************/ +/* OSRGetSemiMajor() */ +/************************************************************************/ + +/** + * \brief Get spheroid semi major axis. + * + * This function is the same as OGRSpatialReference::GetSemiMajor() + */ +double OSRGetSemiMajor( OGRSpatialReferenceH hSRS, OGRErr *pnErr ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetSemiMajor", 0 ); + + return ((OGRSpatialReference *) hSRS)->GetSemiMajor( pnErr ); +} + +/************************************************************************/ +/* GetInvFlattening() */ +/************************************************************************/ + +/** + * \brief Get spheroid inverse flattening. + * + * This method does the same thing as the C function OSRGetInvFlattening(). + * + * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening + * can be found. + * + * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found. + */ + +double OGRSpatialReference::GetInvFlattening( OGRErr * pnErr ) const + +{ + const OGR_SRSNode *poSpheroid = GetAttrNode( "SPHEROID" ); + + if( pnErr != NULL ) + *pnErr = OGRERR_NONE; + + if( poSpheroid != NULL && poSpheroid->GetChildCount() >= 3 ) + { + return CPLAtof( poSpheroid->GetChild(2)->GetValue() ); + } + else + { + if( pnErr != NULL ) + *pnErr = OGRERR_FAILURE; + + return SRS_WGS84_INVFLATTENING; + } +} + +/************************************************************************/ +/* OSRGetInvFlattening() */ +/************************************************************************/ + +/** + * \brief Get spheroid inverse flattening. + * + * This function is the same as OGRSpatialReference::GetInvFlattening() + */ +double OSRGetInvFlattening( OGRSpatialReferenceH hSRS, OGRErr *pnErr ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetInvFlattening", 0 ); + + return ((OGRSpatialReference *) hSRS)->GetInvFlattening( pnErr ); +} + +/************************************************************************/ +/* GetSemiMinor() */ +/************************************************************************/ + +/** + * \brief Get spheroid semi minor axis. + * + * This method does the same thing as the C function OSRGetSemiMinor(). + * + * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis + * can be found. + * + * @return semi-minor axis, or WGS84 semi minor if it can't be found. + */ + +double OGRSpatialReference::GetSemiMinor( OGRErr * pnErr ) const + +{ + double dfInvFlattening, dfSemiMajor; + + dfSemiMajor = GetSemiMajor( pnErr ); + dfInvFlattening = GetInvFlattening( pnErr ); + + return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening); +} + +/************************************************************************/ +/* OSRGetSemiMinor() */ +/************************************************************************/ + +/** + * \brief Get spheroid semi minor axis. + * + * This function is the same as OGRSpatialReference::GetSemiMinor() + */ +double OSRGetSemiMinor( OGRSpatialReferenceH hSRS, OGRErr *pnErr ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetSemiMinor", 0 ); + + return ((OGRSpatialReference *) hSRS)->GetSemiMinor( pnErr ); +} + +/************************************************************************/ +/* SetLocalCS() */ +/************************************************************************/ + +/** + * \brief Set the user visible LOCAL_CS name. + * + * This method is the same as the C function OSRSetLocalCS(). + * + * This method will ensure a LOCAL_CS node is created as the root, + * and set the provided name on it. It must be used before SetLinearUnits(). + * + * @param pszName the user visible name to assign. Not used as a key. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetLocalCS( const char * pszName ) + +{ + OGR_SRSNode *poCS = GetAttrNode( "LOCAL_CS" ); + + if( poCS == NULL && GetRoot() != NULL ) + { + CPLDebug( "OGR", + "OGRSpatialReference::SetLocalCS(%s) failed.\n" + "It appears an incompatible root node (%s) already exists.\n", + pszName, GetRoot()->GetValue() ); + return OGRERR_FAILURE; + } + else + { + SetNode( "LOCAL_CS", pszName ); + return OGRERR_NONE; + } +} + +/************************************************************************/ +/* OSRSetLocalCS() */ +/************************************************************************/ + +/** + * \brief Set the user visible LOCAL_CS name. + * + * This function is the same as OGRSpatialReference::SetLocalCS() + */ +OGRErr OSRSetLocalCS( OGRSpatialReferenceH hSRS, const char * pszName ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetLocalCS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetLocalCS( pszName ); +} + +/************************************************************************/ +/* SetGeocCS() */ +/************************************************************************/ + +/** + * \brief Set the user visible GEOCCS name. + * + * This method is the same as the C function OSRSetGeocCS(). + + * This method will ensure a GEOCCS node is created as the root, + * and set the provided name on it. If used on a GEOGCS coordinate system, + * the DATUM and PRIMEM nodes from the GEOGCS will be tarnsferred over to + * the GEOGCS. + * + * @param pszName the user visible name to assign. Not used as a key. + * + * @return OGRERR_NONE on success. + * + * @since OGR 1.9.0 + */ + +OGRErr OGRSpatialReference::SetGeocCS( const char * pszName ) + +{ + OGR_SRSNode *poGeogCS = NULL; + OGR_SRSNode *poGeocCS = GetAttrNode( "GEOCCS" ); + + if( poRoot != NULL && EQUAL(poRoot->GetValue(),"GEOGCS") ) + { + poGeogCS = poRoot; + poRoot = NULL; + } + + if( poGeocCS == NULL && GetRoot() != NULL ) + { + CPLDebug( "OGR", + "OGRSpatialReference::SetGeocCS(%s) failed.\n" + "It appears an incompatible root node (%s) already exists.\n", + pszName, GetRoot()->GetValue() ); + return OGRERR_FAILURE; + } + + SetNode( "GEOCCS", pszName ); + + if( poGeogCS != NULL ) + { + OGR_SRSNode *poDatum = poGeogCS->GetNode( "DATUM" ); + OGR_SRSNode *poPRIMEM = poGeogCS->GetNode( "PRIMEM" ); + if ( poDatum != NULL && poPRIMEM != NULL ) + { + poRoot->InsertChild( poDatum->Clone(), 1 ); + poRoot->InsertChild( poPRIMEM->Clone(), 2 ); + } + delete poGeogCS; + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetGeocCS() */ +/************************************************************************/ + +/** + * \brief Set the user visible PROJCS name. + * + * This function is the same as OGRSpatialReference::SetGeocCS() + * + * @since OGR 1.9.0 + */ +OGRErr OSRSetGeocCS( OGRSpatialReferenceH hSRS, const char * pszName ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetGeocCS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetGeocCS( pszName ); +} + +/************************************************************************/ +/* SetVertCS() */ +/************************************************************************/ + +/** + * \brief Set the user visible VERT_CS name. + * + * This method is the same as the C function OSRSetVertCS(). + + * This method will ensure a VERT_CS node is created if needed. If the + * existing coordinate system is GEOGCS or PROJCS rooted, then it will be + * turned into a COMPD_CS. + * + * @param pszVertCSName the user visible name of the vertical coordinate + * system. Not used as a key. + * + * @param pszVertDatumName the user visible name of the vertical datum. It + * is helpful if this matches the EPSG name. + * + * @param nVertDatumType the OGC vertical datum type, usually 2005. + * + * @return OGRERR_NONE on success. + * + * @since OGR 1.9.0 + */ + +OGRErr OGRSpatialReference::SetVertCS( const char * pszVertCSName, + const char * pszVertDatumName, + int nVertDatumType ) + +{ +/* -------------------------------------------------------------------- */ +/* Handle the case where we want to make a compound coordinate */ +/* system. */ +/* -------------------------------------------------------------------- */ + if( IsProjected() || IsGeographic() ) + { + OGR_SRSNode *poNewRoot = new OGR_SRSNode( "COMPD_CS" ); + poNewRoot->AddChild( poRoot ); + poRoot = poNewRoot; + } + + else if( GetAttrNode( "VERT_CS" ) == NULL ) + Clear(); + +/* -------------------------------------------------------------------- */ +/* If we already have a VERT_CS, wipe and recreate the root */ +/* otherwise create the VERT_CS now. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poVertCS = GetAttrNode( "VERT_CS" ); + + if( poVertCS != NULL ) + { + poVertCS->ClearChildren(); + } + else + { + poVertCS = new OGR_SRSNode( "VERT_CS" ); + if( poRoot != NULL && EQUAL(poRoot->GetValue(),"COMPD_CS") ) + { + poRoot->AddChild( poVertCS ); + } + else + SetRoot( poVertCS ); + } + +/* -------------------------------------------------------------------- */ +/* Set the name, datumname, and type. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poVertDatum; + + poVertCS->AddChild( new OGR_SRSNode( pszVertCSName ) ); + + poVertDatum = new OGR_SRSNode( "VERT_DATUM" ); + poVertCS->AddChild( poVertDatum ); + + poVertDatum->AddChild( new OGR_SRSNode( pszVertDatumName ) ); + + CPLString osVertDatumType; + osVertDatumType.Printf( "%d", nVertDatumType ); + poVertDatum->AddChild( new OGR_SRSNode( osVertDatumType ) ); + + // add default axis node. + OGR_SRSNode *poAxis = new OGR_SRSNode( "AXIS" ); + + poAxis->AddChild( new OGR_SRSNode( "Up" ) ); + poAxis->AddChild( new OGR_SRSNode( "UP" ) ); + + poVertCS->AddChild( poAxis ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetVertCS() */ +/************************************************************************/ + +/** + * \brief Setup the vertical coordinate system. + * + * This function is the same as OGRSpatialReference::SetVertCS() + * + * @since OGR 1.9.0 + */ +OGRErr OSRSetVertCS( OGRSpatialReferenceH hSRS, + const char * pszVertCSName, + const char * pszVertDatumName, + int nVertDatumType ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetVertCS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetVertCS( pszVertCSName, + pszVertDatumName, + nVertDatumType ); +} + +/************************************************************************/ +/* SetCompoundCS() */ +/************************************************************************/ + +/** + * \brief Setup a compound coordinate system. + * + * This method is the same as the C function OSRSetCompoundCS(). + + * This method is replace the current SRS with a COMPD_CS coordinate system + * consisting of the passed in horizontal and vertical coordinate systems. + * + * @param pszName the name of the compound coordinate system. + * + * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS). + * + * @param poVertSRS the vertical SRS (VERT_CS). + * + * @return OGRERR_NONE on success. + */ + +OGRErr +OGRSpatialReference::SetCompoundCS( const char *pszName, + const OGRSpatialReference *poHorizSRS, + const OGRSpatialReference *poVertSRS ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify these are legal horizontal and vertical coordinate */ +/* systems. */ +/* -------------------------------------------------------------------- */ + if( !poVertSRS->IsVertical() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SetCompoundCS() fails, vertical component is not VERT_CS." ); + return OGRERR_FAILURE; + } + if( !poHorizSRS->IsProjected() + && !poHorizSRS->IsGeographic() ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SetCompoundCS() fails, horizontal component is not PROJCS or GEOGCS." ); + return OGRERR_FAILURE; + } + +/* -------------------------------------------------------------------- */ +/* Replace with compound srs. */ +/* -------------------------------------------------------------------- */ + Clear(); + + poRoot = new OGR_SRSNode( "COMPD_CS" ); + poRoot->AddChild( new OGR_SRSNode( pszName ) ); + poRoot->AddChild( poHorizSRS->GetRoot()->Clone() ); + poRoot->AddChild( poVertSRS->GetRoot()->Clone() ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetCompoundCS() */ +/************************************************************************/ + +/** + * \brief Setup a compound coordinate system. + * + * This function is the same as OGRSpatialReference::SetCompoundCS() + */ +OGRErr OSRSetCompoundCS( OGRSpatialReferenceH hSRS, + const char *pszName, + OGRSpatialReferenceH hHorizSRS, + OGRSpatialReferenceH hVertSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetCompoundCS", CE_Failure ); + VALIDATE_POINTER1( hHorizSRS, "OSRSetCompoundCS", CE_Failure ); + VALIDATE_POINTER1( hVertSRS, "OSRSetCompoundCS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)-> + SetCompoundCS( pszName, + (OGRSpatialReference *) hHorizSRS, + (OGRSpatialReference *) hVertSRS ); +} + +/************************************************************************/ +/* SetProjCS() */ +/************************************************************************/ + +/** + * \brief Set the user visible PROJCS name. + * + * This method is the same as the C function OSRSetProjCS(). + * + * This method will ensure a PROJCS node is created as the root, + * and set the provided name on it. If used on a GEOGCS coordinate system, + * the GEOGCS node will be demoted to be a child of the new PROJCS root. + * + * @param pszName the user visible name to assign. Not used as a key. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetProjCS( const char * pszName ) + +{ + OGR_SRSNode *poGeogCS = NULL; + OGR_SRSNode *poProjCS = GetAttrNode( "PROJCS" ); + + if( poRoot != NULL && EQUAL(poRoot->GetValue(),"GEOGCS") ) + { + poGeogCS = poRoot; + poRoot = NULL; + } + + if( poProjCS == NULL && GetRoot() != NULL ) + { + CPLDebug( "OGR", + "OGRSpatialReference::SetProjCS(%s) failed.\n" + "It appears an incompatible root node (%s) already exists.\n", + pszName, GetRoot()->GetValue() ); + return OGRERR_FAILURE; + } + + SetNode( "PROJCS", pszName ); + + if( poGeogCS != NULL ) + poRoot->InsertChild( poGeogCS, 1 ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetProjCS() */ +/************************************************************************/ + +/** + * \brief Set the user visible PROJCS name. + * + * This function is the same as OGRSpatialReference::SetProjCS() + */ +OGRErr OSRSetProjCS( OGRSpatialReferenceH hSRS, const char * pszName ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetProjCS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetProjCS( pszName ); +} + +/************************************************************************/ +/* SetProjection() */ +/************************************************************************/ + +/** + * \brief Set a projection name. + * + * This method is the same as the C function OSRSetProjection(). + * + * @param pszProjection the projection name, which should be selected from + * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetProjection( const char * pszProjection ) + +{ + OGR_SRSNode *poGeogCS = NULL; + OGRErr eErr; + + if( poRoot != NULL && EQUAL(poRoot->GetValue(),"GEOGCS") ) + { + poGeogCS = poRoot; + poRoot = NULL; + } + + if( !GetAttrNode( "PROJCS" ) ) + { + SetNode( "PROJCS", "unnamed" ); + } + + eErr = SetNode( "PROJCS|PROJECTION", pszProjection ); + if( eErr != OGRERR_NONE ) + return eErr; + + if( poGeogCS != NULL ) + poRoot->InsertChild( poGeogCS, 1 ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetProjection() */ +/************************************************************************/ + +/** + * \brief Set a projection name. + * + * This function is the same as OGRSpatialReference::SetProjection() + */ +OGRErr OSRSetProjection( OGRSpatialReferenceH hSRS, + const char * pszProjection ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetProjection", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetProjection( pszProjection ); +} + +/************************************************************************/ +/* SetProjParm() */ +/************************************************************************/ + +/** + * \brief Set a projection parameter value. + * + * Adds a new PARAMETER under the PROJCS with the indicated name and value. + * + * This method is the same as the C function OSRSetProjParm(). + * + * Please check http://www.remotesensing.org/geotiff/proj_list pages for + * legal parameter names for specific projections. + * + * + * @param pszParmName the parameter name, which should be selected from + * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN. + * + * @param dfValue value to assign. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetProjParm( const char * pszParmName, + double dfValue ) + +{ + OGR_SRSNode *poPROJCS = GetAttrNode( "PROJCS" ); + OGR_SRSNode *poParm; + char szValue[64]; + + if( poPROJCS == NULL ) + return OGRERR_FAILURE; + + OGRPrintDouble( szValue, dfValue ); + +/* -------------------------------------------------------------------- */ +/* Try to find existing parameter with this name. */ +/* -------------------------------------------------------------------- */ + for( int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ ) + { + poParm = poPROJCS->GetChild( iChild ); + + if( EQUAL(poParm->GetValue(),"PARAMETER") + && poParm->GetChildCount() == 2 + && EQUAL(poParm->GetChild(0)->GetValue(),pszParmName) ) + { + poParm->GetChild(1)->SetValue( szValue ); + return OGRERR_NONE; + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise create a new parameter and append. */ +/* -------------------------------------------------------------------- */ + poParm = new OGR_SRSNode( "PARAMETER" ); + poParm->AddChild( new OGR_SRSNode( pszParmName ) ); + poParm->AddChild( new OGR_SRSNode( szValue ) ); + + poPROJCS->AddChild( poParm ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetProjParm() */ +/************************************************************************/ + +/** + * \brief Set a projection parameter value. + * + * This function is the same as OGRSpatialReference::SetProjParm() + */ +OGRErr OSRSetProjParm( OGRSpatialReferenceH hSRS, + const char * pszParmName, double dfValue ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetProjParm", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetProjParm( pszParmName, dfValue ); +} + +/************************************************************************/ +/* FindProjParm() */ +/************************************************************************/ + +/** + * \brief Return the child index of the named projection parameter on + * its parent PROJCS node. + * + * @param pszParameter projection parameter to look for + * @param poPROJCS projection CS node to look in. If NULL is passed, + * the PROJCS node of the SpatialReference object will be searched. + * + * @return the child index of the named projection parameter. -1 on failure + */ +int OGRSpatialReference::FindProjParm( const char *pszParameter, + const OGR_SRSNode *poPROJCS ) const + +{ + const OGR_SRSNode *poParameter = NULL; + + if( poPROJCS == NULL ) + poPROJCS = GetAttrNode( "PROJCS" ); + + if( poPROJCS == NULL ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Search for requested parameter. */ +/* -------------------------------------------------------------------- */ + int iChild; + + for( iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ ) + { + poParameter = poPROJCS->GetChild(iChild); + + if( EQUAL(poParameter->GetValue(),"PARAMETER") + && poParameter->GetChildCount() == 2 + && EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(), + pszParameter) ) + { + return iChild; + } + } + +/* -------------------------------------------------------------------- */ +/* Try similar names, for selected parameters. */ +/* -------------------------------------------------------------------- */ + iChild = -1; + + if( EQUAL(pszParameter,SRS_PP_LATITUDE_OF_ORIGIN) ) + { + iChild = FindProjParm( SRS_PP_LATITUDE_OF_CENTER, poPROJCS ); + } + else if( EQUAL(pszParameter,SRS_PP_CENTRAL_MERIDIAN) ) + { + iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS ); + if( iChild == -1 ) + iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS ); + } + + return iChild; +} + +/************************************************************************/ +/* GetProjParm() */ +/************************************************************************/ + +/** + * \brief Fetch a projection parameter value. + * + * NOTE: This code should be modified to translate non degree angles into + * degrees based on the GEOGCS unit. This has not yet been done. + * + * This method is the same as the C function OSRGetProjParm(). + * + * @param pszName the name of the parameter to fetch, from the set of + * SRS_PP codes in ogr_srs_api.h. + * + * @param dfDefaultValue the value to return if this parameter doesn't exist. + * + * @param pnErr place to put error code on failure. Ignored if NULL. + * + * @return value of parameter. + */ + +double OGRSpatialReference::GetProjParm( const char * pszName, + double dfDefaultValue, + OGRErr *pnErr ) const + +{ + const OGR_SRSNode *poPROJCS = GetAttrNode( "PROJCS" ); + + if( pnErr != NULL ) + *pnErr = OGRERR_NONE; + +/* -------------------------------------------------------------------- */ +/* Find the desired parameter. */ +/* -------------------------------------------------------------------- */ + int iChild = FindProjParm( pszName, poPROJCS ); + + if( iChild != -1 ) + { + const OGR_SRSNode *poParameter = NULL; + poParameter = poPROJCS->GetChild(iChild); + return CPLAtof(poParameter->GetChild(1)->GetValue()); + } + +/* -------------------------------------------------------------------- */ +/* Return default value on failure. */ +/* -------------------------------------------------------------------- */ + if( pnErr != NULL ) + *pnErr = OGRERR_FAILURE; + + return dfDefaultValue; +} + +/************************************************************************/ +/* OSRGetProjParm() */ +/************************************************************************/ + +/** + * \brief Fetch a projection parameter value. + * + * This function is the same as OGRSpatialReference::GetProjParm() + */ +double OSRGetProjParm( OGRSpatialReferenceH hSRS, const char *pszName, + double dfDefaultValue, OGRErr *pnErr ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetProjParm", 0 ); + + return ((OGRSpatialReference *) hSRS)-> + GetProjParm(pszName, dfDefaultValue, pnErr); +} + +/************************************************************************/ +/* GetNormProjParm() */ +/************************************************************************/ + +/** + * \brief Fetch a normalized projection parameter value. + * + * This method is the same as GetProjParm() except that the value of + * the parameter is "normalized" into degrees or meters depending on + * whether it is linear or angular. + * + * This method is the same as the C function OSRGetNormProjParm(). + * + * @param pszName the name of the parameter to fetch, from the set of + * SRS_PP codes in ogr_srs_api.h. + * + * @param dfDefaultValue the value to return if this parameter doesn't exist. + * + * @param pnErr place to put error code on failure. Ignored if NULL. + * + * @return value of parameter. + */ + +double OGRSpatialReference::GetNormProjParm( const char * pszName, + double dfDefaultValue, + OGRErr *pnErr ) const + +{ + double dfRawResult; + OGRErr nError; + + if( pnErr == NULL ) + pnErr = &nError; + + GetNormInfo(); + + dfRawResult = GetProjParm( pszName, dfDefaultValue, pnErr ); + + // If we got the default just return it unadjusted. + if( *pnErr != OGRERR_NONE ) + return dfRawResult; + + if( dfToDegrees != 1.0 && IsAngularParameter(pszName) ) + dfRawResult *= dfToDegrees; + + if( dfToMeter != 1.0 && IsLinearParameter( pszName ) ) + return dfRawResult * dfToMeter; +#ifdef WKT_LONGITUDE_RELATIVE_TO_PM + else if( dfFromGreenwich != 0.0 && IsLongitudeParameter( pszName ) ) + return dfRawResult + dfFromGreenwich; +#endif + else + return dfRawResult; +} + +/************************************************************************/ +/* OSRGetNormProjParm() */ +/************************************************************************/ + +/** + * \brief This function is the same as OGRSpatialReference:: + * + * This function is the same as OGRSpatialReference::GetNormProjParm() + */ +double OSRGetNormProjParm( OGRSpatialReferenceH hSRS, const char *pszName, + double dfDefaultValue, OGRErr *pnErr ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetNormProjParm", 0 ); + + return ((OGRSpatialReference *) hSRS)-> + GetNormProjParm(pszName, dfDefaultValue, pnErr); +} + +/************************************************************************/ +/* SetNormProjParm() */ +/************************************************************************/ + +/** + * \brief Set a projection parameter with a normalized value. + * + * This method is the same as SetProjParm() except that the value of + * the parameter passed in is assumed to be in "normalized" form (decimal + * degrees for angular values, meters for linear values. The values are + * converted in a form suitable for the GEOGCS and linear units in effect. + * + * This method is the same as the C function OSRSetNormProjParm(). + * + * @param pszName the parameter name, which should be selected from + * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN. + * + * @param dfValue value to assign. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetNormProjParm( const char * pszName, + double dfValue ) + +{ + GetNormInfo(); + + if( (dfToDegrees != 1.0 || dfFromGreenwich != 0.0) + && IsAngularParameter(pszName) ) + { +#ifdef WKT_LONGITUDE_RELATIVE_TO_PM + if( dfFromGreenwich != 0.0 && IsLongitudeParameter( pszName ) ) + dfValue -= dfFromGreenwich; +#endif + + dfValue /= dfToDegrees; + } + else if( dfToMeter != 1.0 && IsLinearParameter( pszName ) ) + dfValue /= dfToMeter; + + return SetProjParm( pszName, dfValue ); +} + +/************************************************************************/ +/* OSRSetNormProjParm() */ +/************************************************************************/ + +/** + * \brief Set a projection parameter with a normalized value. + * + * This function is the same as OGRSpatialReference::SetNormProjParm() + */ +OGRErr OSRSetNormProjParm( OGRSpatialReferenceH hSRS, + const char * pszParmName, double dfValue ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetNormProjParm", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)-> + SetNormProjParm( pszParmName, dfValue ); +} + +/************************************************************************/ +/* SetTM() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetTM( double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_TRANSVERSE_MERCATOR ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetTM() */ +/************************************************************************/ + +OGRErr OSRSetTM( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetTM", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetTM( + dfCenterLat, dfCenterLong, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetTMVariant() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetTMVariant( + const char *pszVariantName, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( pszVariantName ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetTMVariant() */ +/************************************************************************/ + +OGRErr OSRSetTMVariant( OGRSpatialReferenceH hSRS, + const char *pszVariantName, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetTMVariant", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetTMVariant( + pszVariantName, + dfCenterLat, dfCenterLong, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetTMSO() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetTMSO( double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* SetTPED() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetTPED( double dfLat1, double dfLong1, + double dfLat2, double dfLong2, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_TWO_POINT_EQUIDISTANT ); + SetNormProjParm( SRS_PP_LATITUDE_OF_1ST_POINT, dfLat1 ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_1ST_POINT, dfLong1 ); + SetNormProjParm( SRS_PP_LATITUDE_OF_2ND_POINT, dfLat2 ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_2ND_POINT, dfLong2 ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetTPED() */ +/************************************************************************/ + +OGRErr OSRSetTPED( OGRSpatialReferenceH hSRS, + double dfLat1, double dfLong1, + double dfLat2, double dfLong2, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetTPED", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetTPED( + dfLat1, dfLong1, dfLat2, dfLong2, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* OSRSetTMSO() */ +/************************************************************************/ + +OGRErr OSRSetTMSO( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetTMSO", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetTMSO( + dfCenterLat, dfCenterLong, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetTMG() */ +/************************************************************************/ + +OGRErr +OGRSpatialReference::SetTMG( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_TUNISIA_MINING_GRID ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetTMG() */ +/************************************************************************/ + +OGRErr OSRSetTMG( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetTMG", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetTMG( + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetACEA() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetACEA( double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_ALBERS_CONIC_EQUAL_AREA ); + SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdP1 ); + SetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, dfStdP2 ); + SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfCenterLat ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetACEA() */ +/************************************************************************/ + +OGRErr OSRSetACEA( OGRSpatialReferenceH hSRS, + double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetACEA", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetACEA( + dfStdP1, dfStdP2, + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetAE() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetAE( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_AZIMUTHAL_EQUIDISTANT ); + SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfCenterLat ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetAE() */ +/************************************************************************/ + +OGRErr OSRSetAE( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetACEA", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetAE( + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetBonne() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetBonne( + double dfStdP1, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_BONNE ); + SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdP1 ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetBonne() */ +/************************************************************************/ + +OGRErr OSRSetBonne( OGRSpatialReferenceH hSRS, + double dfStdP1, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetBonne", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetBonne( + dfStdP1, dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetCEA() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetCEA( double dfStdP1, double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_CYLINDRICAL_EQUAL_AREA ); + SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdP1 ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetCEA() */ +/************************************************************************/ + +OGRErr OSRSetCEA( OGRSpatialReferenceH hSRS, + double dfStdP1, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetCEA", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetCEA( + dfStdP1, dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetCS() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetCS( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_CASSINI_SOLDNER ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetCS() */ +/************************************************************************/ + +OGRErr OSRSetCS( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetCS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetCS( + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetEC() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetEC( double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_EQUIDISTANT_CONIC ); + SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdP1 ); + SetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, dfStdP2 ); + SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfCenterLat ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetEC() */ +/************************************************************************/ + +OGRErr OSRSetEC( OGRSpatialReferenceH hSRS, + double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetEC", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetEC( + dfStdP1, dfStdP2, + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetEckert() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetEckert( int nVariation /* 1-6 */, + double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + if( nVariation == 1 ) + SetProjection( SRS_PT_ECKERT_I ); + else if( nVariation == 2 ) + SetProjection( SRS_PT_ECKERT_II ); + else if( nVariation == 3 ) + SetProjection( SRS_PT_ECKERT_III ); + else if( nVariation == 4 ) + SetProjection( SRS_PT_ECKERT_IV ); + else if( nVariation == 5 ) + SetProjection( SRS_PT_ECKERT_V ); + else if( nVariation == 6 ) + SetProjection( SRS_PT_ECKERT_VI ); + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported Eckert variation (%d).", + nVariation ); + return OGRERR_UNSUPPORTED_SRS; + } + + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetEckert() */ +/************************************************************************/ + +OGRErr OSRSetEckert( OGRSpatialReferenceH hSRS, + int nVariation, + double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetEckert", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetEckert( + nVariation, dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetEckertIV() */ +/* */ +/* Deprecated */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetEckertIV( double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_ECKERT_IV ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetEckertIV() */ +/************************************************************************/ + +OGRErr OSRSetEckertIV( OGRSpatialReferenceH hSRS, + double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetEckertIV", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetEckertIV( + dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetEckertVI() */ +/* */ +/* Deprecated */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetEckertVI( double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_ECKERT_VI ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetEckertVI() */ +/************************************************************************/ + +OGRErr OSRSetEckertVI( OGRSpatialReferenceH hSRS, + double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetEckertVI", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetEckertVI( + dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetEquirectangular() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetEquirectangular( + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_EQUIRECTANGULAR ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetEquirectangular() */ +/************************************************************************/ + +OGRErr OSRSetEquirectangular( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetEquirectangular", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetEquirectangular( + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetEquirectangular2() */ +/* Generalized form */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetEquirectangular2( + double dfCenterLat, double dfCenterLong, + double dfStdParallel1, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_EQUIRECTANGULAR ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1 ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetEquirectangular2() */ +/************************************************************************/ + +OGRErr OSRSetEquirectangular2( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfStdParallel1, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetEquirectangular2", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetEquirectangular2( + dfCenterLat, dfCenterLong, + dfStdParallel1, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetGS() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetGS( double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_GALL_STEREOGRAPHIC ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetGS() */ +/************************************************************************/ + +OGRErr OSRSetGS( OGRSpatialReferenceH hSRS, + double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetGS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetGS( + dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetGH() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetGH( double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_GOODE_HOMOLOSINE ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetGH() */ +/************************************************************************/ + +OGRErr OSRSetGH( OGRSpatialReferenceH hSRS, + double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetGH", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetGH( + dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetIGH() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetIGH() + +{ + SetProjection( SRS_PT_IGH ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetIGH() */ +/************************************************************************/ + +OGRErr OSRSetIGH( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetIGH", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetIGH(); +} + +/************************************************************************/ +/* SetGEOS() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetGEOS( double dfCentralMeridian, + double dfSatelliteHeight, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_GEOSTATIONARY_SATELLITE ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian ); + SetNormProjParm( SRS_PP_SATELLITE_HEIGHT, dfSatelliteHeight ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetGEOS() */ +/************************************************************************/ + +OGRErr OSRSetGEOS( OGRSpatialReferenceH hSRS, + double dfCentralMeridian, + double dfSatelliteHeight, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetGEOS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetGEOS( + dfCentralMeridian, dfSatelliteHeight, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetGaussSchreiberTMercator() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetGaussSchreiberTMercator( + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_GAUSSSCHREIBERTMERCATOR ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetGaussSchreiberTMercator() */ +/************************************************************************/ + +OGRErr OSRSetGaussSchreiberTMercator( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetGaussSchreiberTMercator", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetGaussSchreiberTMercator( + dfCenterLat, dfCenterLong, dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetGnomonic() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetGnomonic( + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_GNOMONIC ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetGnomonic() */ +/************************************************************************/ + +OGRErr OSRSetGnomonic( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetGnomonic", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetGnomonic( + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetHOMAC() */ +/************************************************************************/ + +/** + * \brief Set an Hotine Oblique Mercator Azimuth Center projection using + * azimuth angle. + * + * This projection corresponds to EPSG projection method 9815, also + * sometimes known as hotine oblique mercator (variant B). + * + * This method does the same thing as the C function OSRSetHOMAC(). + * + * @param dfCenterLat Latitude of the projection origin. + * @param dfCenterLong Longitude of the projection origin. + * @param dfAzimuth Azimuth, measured clockwise from North, of the projection + * centerline. + * @param dfRectToSkew ?. + * @param dfScale Scale factor applies to the projection origin. + * @param dfFalseEasting False easting. + * @param dfFalseNorthing False northing. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetHOMAC( double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER ); + SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfCenterLat ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, dfCenterLong ); + SetNormProjParm( SRS_PP_AZIMUTH, dfAzimuth ); + SetNormProjParm( SRS_PP_RECTIFIED_GRID_ANGLE, dfRectToSkew ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetHOMAC() */ +/************************************************************************/ + +/** + * \brief Set an Oblique Mercator projection using azimuth angle. + * + * This is the same as the C++ method OGRSpatialReference::SetHOMAC() + */ +OGRErr OSRSetHOMAC( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetHOMAC", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetHOMAC( + dfCenterLat, dfCenterLong, + dfAzimuth, dfRectToSkew, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetHOM() */ +/************************************************************************/ + +/** + * \brief Set a Hotine Oblique Mercator projection using azimuth angle. + * + * This projection corresponds to EPSG projection method 9812, also + * sometimes known as hotine oblique mercator (variant A).. + * + * This method does the same thing as the C function OSRSetHOM(). + * + * @param dfCenterLat Latitude of the projection origin. + * @param dfCenterLong Longitude of the projection origin. + * @param dfAzimuth Azimuth, measured clockwise from North, of the projection + * centerline. + * @param dfRectToSkew ?. + * @param dfScale Scale factor applies to the projection origin. + * @param dfFalseEasting False easting. + * @param dfFalseNorthing False northing. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetHOM( double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_HOTINE_OBLIQUE_MERCATOR ); + SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfCenterLat ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, dfCenterLong ); + SetNormProjParm( SRS_PP_AZIMUTH, dfAzimuth ); + SetNormProjParm( SRS_PP_RECTIFIED_GRID_ANGLE, dfRectToSkew ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetHOM() */ +/************************************************************************/ +/** + * \brief Set a Hotine Oblique Mercator projection using azimuth angle. + * + * This is the same as the C++ method OGRSpatialReference::SetHOM() + */ +OGRErr OSRSetHOM( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfRectToSkew, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetHOM", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetHOM( + dfCenterLat, dfCenterLong, + dfAzimuth, dfRectToSkew, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetHOM2PNO() */ +/************************************************************************/ + +/** + * \brief Set a Hotine Oblique Mercator projection using two points on projection + * centerline. + * + * This method does the same thing as the C function OSRSetHOM2PNO(). + * + * @param dfCenterLat Latitude of the projection origin. + * @param dfLat1 Latitude of the first point on center line. + * @param dfLong1 Longitude of the first point on center line. + * @param dfLat2 Latitude of the second point on center line. + * @param dfLong2 Longitude of the second point on center line. + * @param dfScale Scale factor applies to the projection origin. + * @param dfFalseEasting False easting. + * @param dfFalseNorthing False northing. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetHOM2PNO( double dfCenterLat, + double dfLat1, double dfLong1, + double dfLat2, double dfLong2, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN ); + SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfCenterLat ); + SetNormProjParm( SRS_PP_LATITUDE_OF_POINT_1, dfLat1 ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_POINT_1, dfLong1 ); + SetNormProjParm( SRS_PP_LATITUDE_OF_POINT_2, dfLat2 ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_POINT_2, dfLong2 ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetHOM2PNO() */ +/************************************************************************/ +/** + * \brief Set a Hotine Oblique Mercator projection using two points on projection + * centerline. + * + * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO() + */ +OGRErr OSRSetHOM2PNO( OGRSpatialReferenceH hSRS, + double dfCenterLat, + double dfLat1, double dfLong1, + double dfLat2, double dfLong2, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetHOM2PNO", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetHOM2PNO( + dfCenterLat, + dfLat1, dfLong1, + dfLat2, dfLong2, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetIWMPolyconic() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetIWMPolyconic( + double dfLat1, double dfLat2, + double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_IMW_POLYCONIC ); + SetNormProjParm( SRS_PP_LATITUDE_OF_1ST_POINT, dfLat1 ); + SetNormProjParm( SRS_PP_LATITUDE_OF_2ND_POINT, dfLat2 ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetIWMPolyconic() */ +/************************************************************************/ + +OGRErr OSRSetIWMPolyconic( OGRSpatialReferenceH hSRS, + double dfLat1, double dfLat2, + double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetIWMPolyconic", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetIWMPolyconic( + dfLat1, dfLat2, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetKrovak() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetKrovak( double dfCenterLat, double dfCenterLong, + double dfAzimuth, + double dfPseudoStdParallel1, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_KROVAK ); + SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfCenterLat ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, dfCenterLong ); + SetNormProjParm( SRS_PP_AZIMUTH, dfAzimuth ); + SetNormProjParm( SRS_PP_PSEUDO_STD_PARALLEL_1, dfPseudoStdParallel1 ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetKrovak() */ +/************************************************************************/ + +OGRErr OSRSetKrovak( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfAzimuth, double dfPseudoStdParallel1, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetKrovak", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetKrovak( + dfCenterLat, dfCenterLong, + dfAzimuth, dfPseudoStdParallel1, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetLAEA() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetLAEA( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA ); + SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfCenterLat ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetLAEA() */ +/************************************************************************/ + +OGRErr OSRSetLAEA( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetLAEA", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetLAEA( + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetLCC() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetLCC( double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP ); + SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdP1 ); + SetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, dfStdP2 ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetLCC() */ +/************************************************************************/ + +OGRErr OSRSetLCC( OGRSpatialReferenceH hSRS, + double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetLCC", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetLCC( + dfStdP1, dfStdP2, + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetLCC1SP() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetLCC1SP( double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetLCC1SP() */ +/************************************************************************/ + +OGRErr OSRSetLCC1SP( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetLCC1SP", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetLCC1SP( + dfCenterLat, dfCenterLong, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetLCCB() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetLCCB( double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM ); + SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdP1 ); + SetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, dfStdP2 ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetLCCB() */ +/************************************************************************/ + +OGRErr OSRSetLCCB( OGRSpatialReferenceH hSRS, + double dfStdP1, double dfStdP2, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetLCCB", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetLCCB( + dfStdP1, dfStdP2, + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetMC() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetMC( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_MILLER_CYLINDRICAL ); + SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfCenterLat ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetMC() */ +/************************************************************************/ + +OGRErr OSRSetMC( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetMC", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetMC( + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetMercator() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetMercator( double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_MERCATOR_1SP ); + + if( dfCenterLat != 0.0 ) + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetMercator() */ +/************************************************************************/ + +OGRErr OSRSetMercator( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetMercator", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetMercator( + dfCenterLat, dfCenterLong, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetMercator2SP() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetMercator2SP( + double dfStdP1, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_MERCATOR_2SP ); + + SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdP1 ); + if( dfCenterLat != 0.0 ) + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetMercator2SP() */ +/************************************************************************/ + +OGRErr OSRSetMercator2SP( OGRSpatialReferenceH hSRS, + double dfStdP1, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetMercator2SP", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetMercator2SP( + dfStdP1, + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetMollweide() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetMollweide( double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_MOLLWEIDE ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetMollweide() */ +/************************************************************************/ + +OGRErr OSRSetMollweide( OGRSpatialReferenceH hSRS, + double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetMollweide", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetMollweide( + dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetNZMG() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetNZMG( double dfCenterLat, double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_NEW_ZEALAND_MAP_GRID ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetNZMG() */ +/************************************************************************/ + +OGRErr OSRSetNZMG( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetNZMG", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetNZMG( + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetOS() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetOS( double dfOriginLat, double dfCMeridian, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_OBLIQUE_STEREOGRAPHIC ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfOriginLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCMeridian ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetOS() */ +/************************************************************************/ + +OGRErr OSRSetOS( OGRSpatialReferenceH hSRS, + double dfOriginLat, double dfCMeridian, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetOS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetOS( + dfOriginLat, dfCMeridian, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetOrthographic() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetOrthographic( + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_ORTHOGRAPHIC ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetOrthographic() */ +/************************************************************************/ + +OGRErr OSRSetOrthographic( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetOrthographic", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetOrthographic( + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetPolyconic() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetPolyconic( + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + // note: it seems that by some definitions this should include a + // scale_factor parameter. + + SetProjection( SRS_PT_POLYCONIC ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetPolyconic() */ +/************************************************************************/ + +OGRErr OSRSetPolyconic( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetPolyconic", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetPolyconic( + dfCenterLat, dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetPS() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetPS( + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_POLAR_STEREOGRAPHIC ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetPS() */ +/************************************************************************/ + +OGRErr OSRSetPS( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetPS", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetPS( + dfCenterLat, dfCenterLong, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetRobinson() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetRobinson( double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_ROBINSON ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetRobinson() */ +/************************************************************************/ + +OGRErr OSRSetRobinson( OGRSpatialReferenceH hSRS, + double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetRobinson", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetRobinson( + dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetSinusoidal() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetSinusoidal( double dfCenterLong, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_SINUSOIDAL ); + SetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, dfCenterLong ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetSinusoidal() */ +/************************************************************************/ + +OGRErr OSRSetSinusoidal( OGRSpatialReferenceH hSRS, + double dfCenterLong, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetSinusoidal", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetSinusoidal( + dfCenterLong, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetStereographic() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetStereographic( + double dfOriginLat, double dfCMeridian, + double dfScale, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_STEREOGRAPHIC ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfOriginLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCMeridian ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetStereographic() */ +/************************************************************************/ + +OGRErr OSRSetStereographic( OGRSpatialReferenceH hSRS, + double dfOriginLat, double dfCMeridian, + double dfScale, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetStereographic", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetStereographic( + dfOriginLat, dfCMeridian, + dfScale, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetSOC() */ +/* */ +/* NOTE: This definition isn't really used in practice any more */ +/* and should be considered deprecated. It seems that swiss */ +/* oblique mercator is now define as Hotine_Oblique_Mercator */ +/* with an azimuth of 90 and a rectified_grid_angle of 90. See */ +/* EPSG:2056 and Bug 423. */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetSOC( double dfLatitudeOfOrigin, + double dfCentralMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL ); + SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetSOC() */ +/************************************************************************/ + +OGRErr OSRSetSOC( OGRSpatialReferenceH hSRS, + double dfLatitudeOfOrigin, double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetSOC", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetSOC( + dfLatitudeOfOrigin, dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetVDG() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetVDG( double dfCMeridian, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + SetProjection( SRS_PT_VANDERGRINTEN ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCMeridian ); + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetVDG() */ +/************************************************************************/ + +OGRErr OSRSetVDG( OGRSpatialReferenceH hSRS, + double dfCentralMeridian, + double dfFalseEasting, double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetVDG", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetVDG( + dfCentralMeridian, + dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetUTM() */ +/************************************************************************/ + +/** + * \brief Set UTM projection definition. + * + * This will generate a projection definition with the full set of + * transverse mercator projection parameters for the given UTM zone. + * If no PROJCS[] description is set yet, one will be set to look + * like "UTM Zone %d, {Northern, Southern} Hemisphere". + * + * This method is the same as the C function OSRSetUTM(). + * + * @param nZone UTM zone. + * + * @param bNorth TRUE for northern hemisphere, or FALSE for southern + * hemisphere. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetUTM( int nZone, int bNorth ) + +{ + SetProjection( SRS_PT_TRANSVERSE_MERCATOR ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0 ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, nZone * 6 - 183 ); + SetNormProjParm( SRS_PP_SCALE_FACTOR, 0.9996 ); + SetNormProjParm( SRS_PP_FALSE_EASTING, 500000.0 ); + + if( bNorth ) + SetNormProjParm( SRS_PP_FALSE_NORTHING, 0 ); + else + SetNormProjParm( SRS_PP_FALSE_NORTHING, 10000000 ); + + if( EQUAL(GetAttrValue("PROJCS"),"unnamed") ) + { + char szUTMName[128]; + + if( bNorth ) + sprintf( szUTMName, "UTM Zone %d, Northern Hemisphere", nZone ); + else + sprintf( szUTMName, "UTM Zone %d, Southern Hemisphere", nZone ); + + SetNode( "PROJCS", szUTMName ); + } + + SetLinearUnits( SRS_UL_METER, 1.0 ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetUTM() */ +/************************************************************************/ + +/** + * \brief Set UTM projection definition. + * + * This is the same as the C++ method OGRSpatialReference::SetUTM() + */ +OGRErr OSRSetUTM( OGRSpatialReferenceH hSRS, int nZone, int bNorth ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetUTM", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetUTM( nZone, bNorth ); +} + +/************************************************************************/ +/* GetUTMZone() */ +/* */ +/* Returns zero if it isn't UTM. */ +/************************************************************************/ + +/** + * \brief Get utm zone information. + * + * This is the same as the C function OSRGetUTMZone(). + * + * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a + * zone which is negative in the southern hemisphere instead of having the + * pbNorth flag used in the C and C++ interface. + * + * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or + * FALSE if southern. + * + * @return UTM zone number or zero if this isn't a UTM definition. + */ + +int OGRSpatialReference::GetUTMZone( int * pbNorth ) const + +{ + const char *pszProjection = GetAttrValue( "PROJECTION" ); + + if( pszProjection == NULL + || !EQUAL(pszProjection,SRS_PT_TRANSVERSE_MERCATOR) ) + return 0; + + if( GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) != 0.0 ) + return 0; + + if( GetProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) != 0.9996 ) + return 0; + + if( fabs(GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 )-500000.0) > 0.001 ) + return 0; + + double dfFalseNorthing = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0); + + if( dfFalseNorthing != 0.0 + && fabs(dfFalseNorthing-10000000.0) > 0.001 ) + return 0; + + if( pbNorth != NULL ) + *pbNorth = (dfFalseNorthing == 0); + + double dfCentralMeridian = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, + 0.0); + double dfZone = ( dfCentralMeridian + 186.0 ) / 6.0; + + if( ABS(dfZone - (int) dfZone - 0.5 ) > 0.00001 + || dfCentralMeridian < -177.00001 + || dfCentralMeridian > 177.000001 ) + return 0; + else + return (int) dfZone; +} + +/************************************************************************/ +/* OSRGetUTMZone() */ +/************************************************************************/ + +/** + * \brief Get utm zone information. + * + * This is the same as the C++ method OGRSpatialReference::GetUTMZone() + */ +int OSRGetUTMZone( OGRSpatialReferenceH hSRS, int *pbNorth ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetUTMZone", 0 ); + + return ((OGRSpatialReference *) hSRS)->GetUTMZone( pbNorth ); +} + +/************************************************************************/ +/* SetWagner() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetWagner( int nVariation /* 1 -- 7 */, + double dfCenterLat, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + if( nVariation == 1 ) + SetProjection( SRS_PT_WAGNER_I ); + else if( nVariation == 2 ) + SetProjection( SRS_PT_WAGNER_II ); + else if( nVariation == 3 ) + { + SetProjection( SRS_PT_WAGNER_III ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + } + else if( nVariation == 4 ) + SetProjection( SRS_PT_WAGNER_IV ); + else if( nVariation == 5 ) + SetProjection( SRS_PT_WAGNER_V ); + else if( nVariation == 6 ) + SetProjection( SRS_PT_WAGNER_VI ); + else if( nVariation == 7 ) + SetProjection( SRS_PT_WAGNER_VII ); + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unsupported Wagner variation (%d).", nVariation ); + return OGRERR_UNSUPPORTED_SRS; + } + + SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting ); + SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetWagner() */ +/************************************************************************/ + +OGRErr OSRSetWagner( OGRSpatialReferenceH hSRS, + int nVariation, double dfCenterLat, + double dfFalseEasting, + double dfFalseNorthing ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetWagner", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetWagner( + nVariation, dfCenterLat, dfFalseEasting, dfFalseNorthing ); +} + +/************************************************************************/ +/* SetQSC() */ +/************************************************************************/ + +OGRErr OGRSpatialReference::SetQSC( double dfCenterLat, double dfCenterLong ) + +{ + SetProjection( SRS_PT_QSC ); + SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat ); + SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetQSC() */ +/************************************************************************/ + +OGRErr OSRSetQSC( OGRSpatialReferenceH hSRS, + double dfCenterLat, double dfCenterLong ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetQSC", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetQSC( + dfCenterLat, dfCenterLong ); +} + +/************************************************************************/ +/* SetAuthority() */ +/************************************************************************/ + +/** + * \brief Set the authority for a node. + * + * This method is the same as the C function OSRSetAuthority(). + * + * @param pszTargetKey the partial or complete path to the node to + * set an authority on. ie. "PROJCS", "GEOGCS" or "GEOGCS|UNIT". + * + * @param pszAuthority authority name, such as "EPSG". + * + * @param nCode code for value with this authority. + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetAuthority( const char *pszTargetKey, + const char * pszAuthority, + int nCode ) + +{ +/* -------------------------------------------------------------------- */ +/* Find the node below which the authority should be put. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poNode = GetAttrNode( pszTargetKey ); + + if( poNode == NULL ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* If there is an existing AUTHORITY child blow it away before */ +/* trying to set a new one. */ +/* -------------------------------------------------------------------- */ + int iOldChild = poNode->FindChild( "AUTHORITY" ); + if( iOldChild != -1 ) + poNode->DestroyChild( iOldChild ); + +/* -------------------------------------------------------------------- */ +/* Create a new authority node. */ +/* -------------------------------------------------------------------- */ + char szCode[32]; + OGR_SRSNode *poAuthNode; + + sprintf( szCode, "%d", nCode ); + + poAuthNode = new OGR_SRSNode( "AUTHORITY" ); + poAuthNode->AddChild( new OGR_SRSNode( pszAuthority ) ); + poAuthNode->AddChild( new OGR_SRSNode( szCode ) ); + + poNode->AddChild( poAuthNode ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetAuthority() */ +/************************************************************************/ + +/** + * \brief Set the authority for a node. + * + * This function is the same as OGRSpatialReference::SetAuthority(). + */ +OGRErr OSRSetAuthority( OGRSpatialReferenceH hSRS, + const char *pszTargetKey, + const char * pszAuthority, + int nCode ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetAuthority", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetAuthority( pszTargetKey, + pszAuthority, + nCode ); +} + +/************************************************************************/ +/* GetAuthorityCode() */ +/************************************************************************/ + +/** + * \brief Get the authority code for a node. + * + * This method is used to query an AUTHORITY[] node from within the + * WKT tree, and fetch the code value. + * + * While in theory values may be non-numeric, for the EPSG authority all + * code values should be integral. + * + * This method is the same as the C function OSRGetAuthorityCode(). + * + * @param pszTargetKey the partial or complete path to the node to + * get an authority from. ie. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to + * search for an authority node on the root element. + * + * @return value code from authority node, or NULL on failure. The value + * returned is internal and should not be freed or modified. + */ + +const char * +OGRSpatialReference::GetAuthorityCode( const char *pszTargetKey ) const + +{ +/* -------------------------------------------------------------------- */ +/* Find the node below which the authority should be put. */ +/* -------------------------------------------------------------------- */ + const OGR_SRSNode *poNode; + + if( pszTargetKey == NULL ) + poNode = poRoot; + else + poNode= ((OGRSpatialReference *) this)->GetAttrNode( pszTargetKey ); + + if( poNode == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Fetch AUTHORITY child if there is one. */ +/* -------------------------------------------------------------------- */ + if( poNode->FindChild("AUTHORITY") == -1 ) + return NULL; + + poNode = poNode->GetChild(poNode->FindChild("AUTHORITY")); + +/* -------------------------------------------------------------------- */ +/* Create a new authority node. */ +/* -------------------------------------------------------------------- */ + if( poNode->GetChildCount() < 2 ) + return NULL; + + return poNode->GetChild(1)->GetValue(); +} + +/************************************************************************/ +/* OSRGetAuthorityCode() */ +/************************************************************************/ + +/** + * \brief Get the authority code for a node. + * + * This function is the same as OGRSpatialReference::GetAuthorityCode(). + */ +const char *OSRGetAuthorityCode( OGRSpatialReferenceH hSRS, + const char *pszTargetKey ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetAuthorityCode", NULL ); + + return ((OGRSpatialReference *) hSRS)->GetAuthorityCode( pszTargetKey ); +} + +/************************************************************************/ +/* GetAuthorityName() */ +/************************************************************************/ + +/** + * \brief Get the authority name for a node. + * + * This method is used to query an AUTHORITY[] node from within the + * WKT tree, and fetch the authority name value. + * + * The most common authority is "EPSG". + * + * This method is the same as the C function OSRGetAuthorityName(). + * + * @param pszTargetKey the partial or complete path to the node to + * get an authority from. ie. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to + * search for an authority node on the root element. + * + * @return value code from authority node, or NULL on failure. The value + * returned is internal and should not be freed or modified. + */ + +const char * +OGRSpatialReference::GetAuthorityName( const char *pszTargetKey ) const + +{ +/* -------------------------------------------------------------------- */ +/* Find the node below which the authority should be put. */ +/* -------------------------------------------------------------------- */ + const OGR_SRSNode *poNode; + + if( pszTargetKey == NULL ) + poNode = poRoot; + else + poNode= ((OGRSpatialReference *) this)->GetAttrNode( pszTargetKey ); + + if( poNode == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Fetch AUTHORITY child if there is one. */ +/* -------------------------------------------------------------------- */ + if( poNode->FindChild("AUTHORITY") == -1 ) + return NULL; + + poNode = poNode->GetChild(poNode->FindChild("AUTHORITY")); + +/* -------------------------------------------------------------------- */ +/* Create a new authority node. */ +/* -------------------------------------------------------------------- */ + if( poNode->GetChildCount() < 2 ) + return NULL; + + return poNode->GetChild(0)->GetValue(); +} + +/************************************************************************/ +/* OSRGetAuthorityName() */ +/************************************************************************/ + +/** + * \brief Get the authority name for a node. + * + * This function is the same as OGRSpatialReference::GetAuthorityName(). + */ +const char *OSRGetAuthorityName( OGRSpatialReferenceH hSRS, + const char *pszTargetKey ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetAuthorityName", NULL ); + + return ((OGRSpatialReference *) hSRS)->GetAuthorityName( pszTargetKey ); +} + +/************************************************************************/ +/* StripVertical() */ +/************************************************************************/ + +/** + * \brief Convert a compound cs into a horizontal CS. + * + * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS + * nodes are stripped resulting and only the horizontal coordinate system + * portion remains (normally PROJCS, GEOGCS or LOCAL_CS). + * + * If this is not a compound coordinate system then nothing is changed. + * + * @since OGR 1.8.0 + */ + +OGRErr OGRSpatialReference::StripVertical() + +{ + if( GetRoot() == NULL + || !EQUAL(GetRoot()->GetValue(),"COMPD_CS") ) + return OGRERR_NONE; + + OGR_SRSNode *poHorizontalCS = GetRoot()->GetChild( 1 ); + if( poHorizontalCS != NULL ) + poHorizontalCS = poHorizontalCS->Clone(); + SetRoot( poHorizontalCS ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* StripCTParms() */ +/************************************************************************/ + +/** + * \brief Strip OGC CT Parameters. + * + * This method will remove all components of the coordinate system + * that are specific to the OGC CT Specification. That is it will attempt + * to strip it down to being compatible with the Simple Features 1.0 + * specification. + * + * This method is the same as the C function OSRStripCTParms(). + * + * @param poCurrent node to operate on. NULL to operate on whole tree. + * + * @return OGRERR_NONE on success or an error code. + */ + +OGRErr OGRSpatialReference::StripCTParms( OGR_SRSNode * poCurrent ) + +{ + if( poCurrent == NULL ) + { + StripVertical(); + poCurrent = GetRoot(); + } + + if( poCurrent == NULL ) + return OGRERR_NONE; + + if( poCurrent == GetRoot() && EQUAL(poCurrent->GetValue(),"LOCAL_CS") ) + { + delete poCurrent; + poRoot = NULL; + + return OGRERR_NONE; + } + + if( poCurrent == NULL ) + return OGRERR_NONE; + + poCurrent->StripNodes( "AUTHORITY" ); + poCurrent->StripNodes( "TOWGS84" ); + poCurrent->StripNodes( "AXIS" ); + poCurrent->StripNodes( "EXTENSION" ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRStripCTParms() */ +/************************************************************************/ + +/** + * \brief Strip OGC CT Parameters. + * + * This function is the same as OGRSpatialReference::StripCTParms(). + */ +OGRErr OSRStripCTParms( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRStripCTParms", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->StripCTParms( NULL ); +} + +/************************************************************************/ +/* IsCompound() */ +/************************************************************************/ + +/** + * \brief Check if coordinate system is compound. + * + * This method is the same as the C function OSRIsCompound(). + * + * @return TRUE if this is rooted with a COMPD_CS node. + */ + +int OGRSpatialReference::IsCompound() const + +{ + if( poRoot == NULL ) + return FALSE; + + return EQUAL(poRoot->GetValue(),"COMPD_CS"); +} + +/************************************************************************/ +/* OSRIsCompound() */ +/************************************************************************/ + +/** + * \brief Check if the coordinate system is compound. + * + * This function is the same as OGRSpatialReference::IsCompound(). + */ +int OSRIsCompound( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRIsCompound", 0 ); + + return ((OGRSpatialReference *) hSRS)->IsCompound(); +} + +/************************************************************************/ +/* IsProjected() */ +/************************************************************************/ + +/** + * \brief Check if projected coordinate system. + * + * This method is the same as the C function OSRIsProjected(). + * + * @return TRUE if this contains a PROJCS node indicating a it is a + * projected coordinate system. + */ + +int OGRSpatialReference::IsProjected() const + +{ + if( poRoot == NULL ) + return FALSE; + + if( EQUAL(poRoot->GetValue(),"PROJCS") ) + return TRUE; + else if( EQUAL(poRoot->GetValue(),"COMPD_CS") ) + return GetAttrNode( "PROJCS" ) != NULL; + else + return FALSE; +} + +/************************************************************************/ +/* OSRIsProjected() */ +/************************************************************************/ +/** + * \brief Check if projected coordinate system. + * + * This function is the same as OGRSpatialReference::IsProjected(). + */ +int OSRIsProjected( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRIsProjected", 0 ); + + return ((OGRSpatialReference *) hSRS)->IsProjected(); +} + +/************************************************************************/ +/* IsGeocentric() */ +/************************************************************************/ + +/** + * \brief Check if geocentric coordinate system. + * + * This method is the same as the C function OSRIsGeocentric(). + * + * @return TRUE if this contains a GEOCCS node indicating a it is a + * geocentric coordinate system. + * + * @since OGR 1.9.0 + */ + +int OGRSpatialReference::IsGeocentric() const + +{ + if( poRoot == NULL ) + return FALSE; + + if( EQUAL(poRoot->GetValue(),"GEOCCS") ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* OSRIsGeocentric() */ +/************************************************************************/ +/** + * \brief Check if geocentric coordinate system. + * + * This function is the same as OGRSpatialReference::IsGeocentric(). + * + * @since OGR 1.9.0 + */ +int OSRIsGeocentric( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRIsGeocentric", 0 ); + + return ((OGRSpatialReference *) hSRS)->IsGeocentric(); +} + +/************************************************************************/ +/* IsGeographic() */ +/************************************************************************/ + +/** + * \brief Check if geographic coordinate system. + * + * This method is the same as the C function OSRIsGeographic(). + * + * @return TRUE if this spatial reference is geographic ... that is the + * root is a GEOGCS node. + */ + +int OGRSpatialReference::IsGeographic() const + +{ + if( GetRoot() == NULL ) + return FALSE; + + if( EQUAL(poRoot->GetValue(),"GEOGCS") ) + return TRUE; + else if( EQUAL(poRoot->GetValue(),"COMPD_CS") ) + return GetAttrNode( "GEOGCS" ) != NULL + && GetAttrNode( "PROJCS" ) == NULL; + else + return FALSE; +} + +/************************************************************************/ +/* OSRIsGeographic() */ +/************************************************************************/ +/** + * \brief Check if geographic coordinate system. + * + * This function is the same as OGRSpatialReference::IsGeographic(). + */ +int OSRIsGeographic( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRIsGeographic", 0 ); + + return ((OGRSpatialReference *) hSRS)->IsGeographic(); +} + +/************************************************************************/ +/* IsLocal() */ +/************************************************************************/ + +/** + * \brief Check if local coordinate system. + * + * This method is the same as the C function OSRIsLocal(). + * + * @return TRUE if this spatial reference is local ... that is the + * root is a LOCAL_CS node. + */ + +int OGRSpatialReference::IsLocal() const + +{ + if( GetRoot() == NULL ) + return FALSE; + + return EQUAL(GetRoot()->GetValue(),"LOCAL_CS"); +} + +/************************************************************************/ +/* OSRIsLocal() */ +/************************************************************************/ +/** + * \brief Check if local coordinate system. + * + * This function is the same as OGRSpatialReference::IsLocal(). + */ +int OSRIsLocal( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRIsLocal", 0 ); + + return ((OGRSpatialReference *) hSRS)->IsLocal(); +} + +/************************************************************************/ +/* IsVertical() */ +/************************************************************************/ + +/** + * \brief Check if vertical coordinate system. + * + * This method is the same as the C function OSRIsVertical(). + * + * @return TRUE if this contains a VERT_CS node indicating a it is a + * vertical coordinate system. + * + * @since OGR 1.8.0 + */ + +int OGRSpatialReference::IsVertical() const + +{ + if( poRoot == NULL ) + return FALSE; + + if( EQUAL(poRoot->GetValue(),"VERT_CS") ) + return TRUE; + else if( EQUAL(poRoot->GetValue(),"COMPD_CS") ) + return GetAttrNode( "VERT_CS" ) != NULL; + else + return FALSE; +} + +/************************************************************************/ +/* OSRIsVertical() */ +/************************************************************************/ +/** + * \brief Check if vertical coordinate system. + * + * This function is the same as OGRSpatialReference::IsVertical(). + * + * @since OGR 1.8.0 + */ +int OSRIsVertical( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRIsVertical", 0 ); + + return ((OGRSpatialReference *) hSRS)->IsVertical(); +} + +/************************************************************************/ +/* CloneGeogCS() */ +/************************************************************************/ + +/** + * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference object. + * + * @return a new SRS, which becomes the responsibility of the caller. + */ +OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const + +{ + const OGR_SRSNode *poGeogCS; + OGRSpatialReference * poNewSRS; + +/* -------------------------------------------------------------------- */ +/* We have to reconstruct the GEOGCS node for geocentric */ +/* coordinate systems. */ +/* -------------------------------------------------------------------- */ + if( IsGeocentric() ) + { + const OGR_SRSNode *poDatum = GetAttrNode( "DATUM" ); + const OGR_SRSNode *poPRIMEM = GetAttrNode( "PRIMEM" ); + OGR_SRSNode *poGeogCS; + + if( poDatum == NULL || poPRIMEM == NULL ) + return NULL; + + poGeogCS = new OGR_SRSNode( "GEOGCS" ); + poGeogCS->AddChild( new OGR_SRSNode( "unnamed" ) ); + poGeogCS->AddChild( poDatum->Clone() ); + poGeogCS->AddChild( poPRIMEM->Clone() ); + + poNewSRS = new OGRSpatialReference(); + poNewSRS->SetRoot( poGeogCS ); + + poNewSRS->SetAngularUnits( "degree", CPLAtof(SRS_UA_DEGREE_CONV) ); + + return poNewSRS; + } + +/* -------------------------------------------------------------------- */ +/* For all others we just search the tree, and duplicate. */ +/* -------------------------------------------------------------------- */ + poGeogCS = GetAttrNode( "GEOGCS" ); + if( poGeogCS == NULL ) + return NULL; + + poNewSRS = new OGRSpatialReference(); + poNewSRS->SetRoot( poGeogCS->Clone() ); + + return poNewSRS; +} + +/************************************************************************/ +/* OSRCloneGeogCS() */ +/************************************************************************/ +/** + * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference object. + * + * This function is the same as OGRSpatialReference::CloneGeogCS(). + */ +OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS( OGRSpatialReferenceH hSource ) + +{ + VALIDATE_POINTER1( hSource, "OSRCloneGeogCS", NULL ); + + return (OGRSpatialReferenceH) + ((OGRSpatialReference *) hSource)->CloneGeogCS(); +} + +/************************************************************************/ +/* IsSameGeogCS() */ +/************************************************************************/ + +/** + * \brief Do the GeogCS'es match? + * + * This method is the same as the C function OSRIsSameGeogCS(). + * + * @param poOther the SRS being compared against. + * + * @return TRUE if they are the same or FALSE otherwise. + */ + +int OGRSpatialReference::IsSameGeogCS( const OGRSpatialReference *poOther ) const + +{ + const char *pszThisValue, *pszOtherValue; + +/* -------------------------------------------------------------------- */ +/* Does the datum name match? Note that we assume */ +/* compatibility if either is missing a datum. */ +/* -------------------------------------------------------------------- */ + pszThisValue = this->GetAttrValue( "DATUM" ); + pszOtherValue = poOther->GetAttrValue( "DATUM" ); + + if( pszThisValue != NULL && pszOtherValue != NULL + && !EQUAL(pszThisValue,pszOtherValue) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Do the datum TOWGS84 values match if present? */ +/* -------------------------------------------------------------------- */ + double adfTOWGS84[7], adfOtherTOWGS84[7]; + int i; + + this->GetTOWGS84( adfTOWGS84, 7 ); + poOther->GetTOWGS84( adfOtherTOWGS84, 7 ); + + for( i = 0; i < 7; i++ ) + { + if( fabs(adfTOWGS84[i] - adfOtherTOWGS84[i]) > 0.00001 ) + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Do the prime meridians match? If missing assume a value of zero.*/ +/* -------------------------------------------------------------------- */ + pszThisValue = this->GetAttrValue( "PRIMEM", 1 ); + if( pszThisValue == NULL ) + pszThisValue = "0.0"; + + pszOtherValue = poOther->GetAttrValue( "PRIMEM", 1 ); + if( pszOtherValue == NULL ) + pszOtherValue = "0.0"; + + if( CPLAtof(pszOtherValue) != CPLAtof(pszThisValue) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Do the units match? */ +/* -------------------------------------------------------------------- */ + pszThisValue = this->GetAttrValue( "GEOGCS|UNIT", 1 ); + if( pszThisValue == NULL ) + pszThisValue = SRS_UA_DEGREE_CONV; + + pszOtherValue = poOther->GetAttrValue( "GEOGCS|UNIT", 1 ); + if( pszOtherValue == NULL ) + pszOtherValue = SRS_UA_DEGREE_CONV; + + if( ABS(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Does the spheroid match. Check semi major, and inverse */ +/* flattening. */ +/* -------------------------------------------------------------------- */ + pszThisValue = this->GetAttrValue( "SPHEROID", 1 ); + pszOtherValue = poOther->GetAttrValue( "SPHEROID", 1 ); + if( pszThisValue != NULL && pszOtherValue != NULL + && ABS(CPLAtof(pszThisValue) - CPLAtof(pszOtherValue)) > 0.01 ) + return FALSE; + + pszThisValue = this->GetAttrValue( "SPHEROID", 2 ); + pszOtherValue = poOther->GetAttrValue( "SPHEROID", 2 ); + if( pszThisValue != NULL && pszOtherValue != NULL + && ABS(CPLAtof(pszThisValue) - CPLAtof(pszOtherValue)) > 0.0001 ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* OSRIsSameGeogCS() */ +/************************************************************************/ + +/** + * \brief Do the GeogCS'es match? + * + * This function is the same as OGRSpatialReference::IsSameGeogCS(). + */ +int OSRIsSameGeogCS( OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2 ) + +{ + VALIDATE_POINTER1( hSRS1, "OSRIsSameGeogCS", 0 ); + VALIDATE_POINTER1( hSRS2, "OSRIsSameGeogCS", 0 ); + + return ((OGRSpatialReference *) hSRS1)->IsSameGeogCS( + (OGRSpatialReference *) hSRS2 ); +} + +/************************************************************************/ +/* IsSameVertCS() */ +/************************************************************************/ + +/** + * \brief Do the VertCS'es match? + * + * This method is the same as the C function OSRIsSameVertCS(). + * + * @param poOther the SRS being compared against. + * + * @return TRUE if they are the same or FALSE otherwise. + */ + +int OGRSpatialReference::IsSameVertCS( const OGRSpatialReference *poOther ) const + +{ + const char *pszThisValue, *pszOtherValue; + +/* -------------------------------------------------------------------- */ +/* Does the datum name match? */ +/* -------------------------------------------------------------------- */ + pszThisValue = this->GetAttrValue( "VERT_DATUM" ); + pszOtherValue = poOther->GetAttrValue( "VERT_DATUM" ); + + if( pszThisValue == NULL || pszOtherValue == NULL + || !EQUAL(pszThisValue, pszOtherValue) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Do the units match? */ +/* -------------------------------------------------------------------- */ + pszThisValue = this->GetAttrValue( "VERT_CS|UNIT", 1 ); + if( pszThisValue == NULL ) + pszThisValue = "1.0"; + + pszOtherValue = poOther->GetAttrValue( "VERT_CS|UNIT", 1 ); + if( pszOtherValue == NULL ) + pszOtherValue = "1.0"; + + if( ABS(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001 ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* OSRIsSameVertCS() */ +/************************************************************************/ + +/** + * \brief Do the VertCS'es match? + * + * This function is the same as OGRSpatialReference::IsSameVertCS(). + */ +int OSRIsSameVertCS( OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2 ) + +{ + VALIDATE_POINTER1( hSRS1, "OSRIsSameVertCS", 0 ); + VALIDATE_POINTER1( hSRS2, "OSRIsSameVertCS", 0 ); + + return ((OGRSpatialReference *) hSRS1)->IsSameVertCS( + (OGRSpatialReference *) hSRS2 ); +} + +/************************************************************************/ +/* IsSame() */ +/************************************************************************/ + +/** + * \brief Do these two spatial references describe the same system ? + * + * @param poOtherSRS the SRS being compared to. + * + * @return TRUE if equivalent or FALSE otherwise. + */ + +int OGRSpatialReference::IsSame( const OGRSpatialReference * poOtherSRS ) const + +{ + if( GetRoot() == NULL && poOtherSRS->GetRoot() == NULL ) + return TRUE; + else if( GetRoot() == NULL || poOtherSRS->GetRoot() == NULL ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Compare geographic coordinate system. */ +/* -------------------------------------------------------------------- */ + if( !IsSameGeogCS( poOtherSRS ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Do the have the same root types? Ie. is one PROJCS and one */ +/* GEOGCS or perhaps LOCALCS? */ +/* -------------------------------------------------------------------- */ + if( !EQUAL(GetRoot()->GetValue(),poOtherSRS->GetRoot()->GetValue()) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Compare projected coordinate system. */ +/* -------------------------------------------------------------------- */ + if( IsProjected() ) + { + const char *pszValue1, *pszValue2; + const OGR_SRSNode *poPROJCS = GetAttrNode( "PROJCS" ); + + pszValue1 = this->GetAttrValue( "PROJECTION" ); + pszValue2 = poOtherSRS->GetAttrValue( "PROJECTION" ); + if( pszValue1 == NULL || pszValue2 == NULL + || !EQUAL(pszValue1,pszValue2) ) + return FALSE; + + for( int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ ) + { + const OGR_SRSNode *poNode; + + poNode = poPROJCS->GetChild( iChild ); + if( !EQUAL(poNode->GetValue(),"PARAMETER") + || poNode->GetChildCount() != 2 ) + continue; + + /* this this eventually test within some epsilon? */ + if( this->GetProjParm( poNode->GetChild(0)->GetValue() ) + != poOtherSRS->GetProjParm( poNode->GetChild(0)->GetValue() ) ) + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* If they are LOCALCS/PROJCS, do they have the same units? */ +/* -------------------------------------------------------------------- */ + if( IsLocal() || IsProjected() ) + { + if( GetLinearUnits() != 0.0 ) + { + double dfRatio; + + dfRatio = poOtherSRS->GetLinearUnits() / GetLinearUnits(); + if( dfRatio < 0.9999999999 || dfRatio > 1.000000001 ) + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Compare vertical coordinate system. */ +/* -------------------------------------------------------------------- */ + if( IsVertical() && !IsSameVertCS( poOtherSRS ) ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* OSRIsSame() */ +/************************************************************************/ + +/** + * \brief Do these two spatial references describe the same system ? + * + * This function is the same as OGRSpatialReference::IsSame(). + */ +int OSRIsSame( OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2 ) + +{ + VALIDATE_POINTER1( hSRS1, "OSRIsSame", 0 ); + VALIDATE_POINTER1( hSRS2, "OSRIsSame", 0 ); + + return ((OGRSpatialReference *) hSRS1)->IsSame( + (OGRSpatialReference *) hSRS2 ); +} + +/************************************************************************/ +/* SetTOWGS84() */ +/************************************************************************/ + +/** + * \brief Set the Bursa-Wolf conversion to WGS84. + * + * This will create the TOWGS84 node as a child of the DATUM. It will fail + * if there is no existing DATUM node. Unlike most OGRSpatialReference + * methods it will insert itself in the appropriate order, and will replace + * an existing TOWGS84 node if there is one. + * + * The parameters have the same meaning as EPSG transformation 9606 + * (Position Vector 7-param. transformation). + * + * This method is the same as the C function OSRSetTOWGS84(). + * + * @param dfDX X child in meters. + * @param dfDY Y child in meters. + * @param dfDZ Z child in meters. + * @param dfEX X rotation in arc seconds (optional, defaults to zero). + * @param dfEY Y rotation in arc seconds (optional, defaults to zero). + * @param dfEZ Z rotation in arc seconds (optional, defaults to zero). + * @param dfPPM scaling factor (parts per million). + * + * @return OGRERR_NONE on success. + */ + +OGRErr OGRSpatialReference::SetTOWGS84( double dfDX, double dfDY, double dfDZ, + double dfEX, double dfEY, double dfEZ, + double dfPPM ) + +{ + OGR_SRSNode *poDatum, *poTOWGS84; + int iPosition; + char szValue[64]; + + poDatum = GetAttrNode( "DATUM" ); + if( poDatum == NULL ) + return OGRERR_FAILURE; + + if( poDatum->FindChild( "TOWGS84" ) != -1 ) + poDatum->DestroyChild( poDatum->FindChild( "TOWGS84" ) ); + + iPosition = poDatum->GetChildCount(); + if( poDatum->FindChild("AUTHORITY") != -1 ) + { + iPosition = poDatum->FindChild("AUTHORITY"); + } + + poTOWGS84 = new OGR_SRSNode("TOWGS84"); + + OGRPrintDouble( szValue, dfDX ); + poTOWGS84->AddChild( new OGR_SRSNode( szValue ) ); + + OGRPrintDouble( szValue, dfDY ); + poTOWGS84->AddChild( new OGR_SRSNode( szValue ) ); + + OGRPrintDouble( szValue, dfDZ ); + poTOWGS84->AddChild( new OGR_SRSNode( szValue ) ); + + OGRPrintDouble( szValue, dfEX ); + poTOWGS84->AddChild( new OGR_SRSNode( szValue ) ); + + OGRPrintDouble( szValue, dfEY ); + poTOWGS84->AddChild( new OGR_SRSNode( szValue ) ); + + OGRPrintDouble( szValue, dfEZ ); + poTOWGS84->AddChild( new OGR_SRSNode( szValue ) ); + + OGRPrintDouble( szValue, dfPPM ); + poTOWGS84->AddChild( new OGR_SRSNode( szValue ) ); + + poDatum->InsertChild( poTOWGS84, iPosition ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetTOWGS84() */ +/************************************************************************/ + +/** + * \brief Set the Bursa-Wolf conversion to WGS84. + * + * This function is the same as OGRSpatialReference::SetTOWGS84(). + */ +OGRErr OSRSetTOWGS84( OGRSpatialReferenceH hSRS, + double dfDX, double dfDY, double dfDZ, + double dfEX, double dfEY, double dfEZ, + double dfPPM ) + +{ + VALIDATE_POINTER1( hSRS, "OSRSetTOWGS84", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->SetTOWGS84( dfDX, dfDY, dfDZ, + dfEX, dfEY, dfEZ, + dfPPM ); +} + +/************************************************************************/ +/* GetTOWGS84() */ +/************************************************************************/ + +/** + * \brief Fetch TOWGS84 parameters, if available. + * + * @param padfCoeff array into which up to 7 coefficients are placed. + * @param nCoeffCount size of padfCoeff - defaults to 7. + * + * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no + * TOWGS84 node available. + */ + +OGRErr OGRSpatialReference::GetTOWGS84( double * padfCoeff, + int nCoeffCount ) const + +{ + const OGR_SRSNode *poNode = GetAttrNode( "TOWGS84" ); + + memset( padfCoeff, 0, sizeof(double) * nCoeffCount ); + + if( poNode == NULL ) + return OGRERR_FAILURE; + + for( int i = 0; i < nCoeffCount && i < poNode->GetChildCount(); i++ ) + { + padfCoeff[i] = CPLAtof(poNode->GetChild(i)->GetValue()); + } + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRGetTOWGS84() */ +/************************************************************************/ + +/** + * \brief Fetch TOWGS84 parameters, if available. + * + * This function is the same as OGRSpatialReference::GetTOWGS84(). + */ +OGRErr OSRGetTOWGS84( OGRSpatialReferenceH hSRS, + double * padfCoeff, int nCoeffCount ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetTOWGS84", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->GetTOWGS84( padfCoeff, nCoeffCount); +} + +/************************************************************************/ +/* IsAngularParameter() */ +/* */ +/* Is the passed projection parameter an angular one? */ +/************************************************************************/ + +int OGRSpatialReference::IsAngularParameter( const char *pszParameterName ) + +{ + if( EQUALN(pszParameterName,"long",4) + || EQUALN(pszParameterName,"lati",4) + || EQUAL(pszParameterName,SRS_PP_CENTRAL_MERIDIAN) + || EQUALN(pszParameterName,"standard_parallel",17) + || EQUAL(pszParameterName,SRS_PP_AZIMUTH) + || EQUAL(pszParameterName,SRS_PP_RECTIFIED_GRID_ANGLE) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* IsLongitudeParameter() */ +/* */ +/* Is the passed projection parameter an angular longitude */ +/* (relative to a prime meridian)? */ +/************************************************************************/ + +int OGRSpatialReference::IsLongitudeParameter( const char *pszParameterName ) + +{ + if( EQUALN(pszParameterName,"long",4) + || EQUAL(pszParameterName,SRS_PP_CENTRAL_MERIDIAN) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* IsLinearParameter() */ +/* */ +/* Is the passed projection parameter an linear one measured in */ +/* meters or some similar linear measure. */ +/************************************************************************/ + +int OGRSpatialReference::IsLinearParameter( const char *pszParameterName ) + +{ + if( EQUALN(pszParameterName,"false_",6) + || EQUAL(pszParameterName,SRS_PP_SATELLITE_HEIGHT) ) + return TRUE; + else + return FALSE; +} + +/************************************************************************/ +/* GetNormInfo() */ +/************************************************************************/ + +/** + * \brief Set the internal information for normalizing linear, and angular values. + */ +void OGRSpatialReference::GetNormInfo(void) const + +{ + if( bNormInfoSet ) + return; + +/* -------------------------------------------------------------------- */ +/* Initialize values. */ +/* -------------------------------------------------------------------- */ + OGRSpatialReference *poThis = (OGRSpatialReference *) this; + + poThis->bNormInfoSet = TRUE; + + poThis->dfFromGreenwich = GetPrimeMeridian(NULL); + poThis->dfToMeter = GetLinearUnits(NULL); + poThis->dfToDegrees = GetAngularUnits(NULL) / CPLAtof(SRS_UA_DEGREE_CONV); + if( fabs(poThis->dfToDegrees-1.0) < 0.000000001 ) + poThis->dfToDegrees = 1.0; +} + +/************************************************************************/ +/* FixupOrdering() */ +/************************************************************************/ + +/** + * \brief Correct parameter ordering to match CT Specification. + * + * Some mechanisms to create WKT using OGRSpatialReference, and some + * imported WKT fail to maintain the order of parameters required according + * to the BNF definitions in the OpenGIS SF-SQL and CT Specifications. This + * method attempts to massage things back into the required order. + * + * This method is the same as the C function OSRFixupOrdering(). + * + * @return OGRERR_NONE on success or an error code if something goes + * wrong. + */ + +OGRErr OGRSpatialReference::FixupOrdering() + +{ + if( GetRoot() != NULL ) + return GetRoot()->FixupOrdering(); + else + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRFixupOrdering() */ +/************************************************************************/ + +/** + * \brief Correct parameter ordering to match CT Specification. + * + * This function is the same as OGRSpatialReference::FixupOrdering(). + */ +OGRErr OSRFixupOrdering( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRFixupOrdering", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->FixupOrdering(); +} + +/************************************************************************/ +/* Fixup() */ +/************************************************************************/ + +/** + * \brief Fixup as needed. + * + * Some mechanisms to create WKT using OGRSpatialReference, and some + * imported WKT, are not valid according to the OGC CT specification. This + * method attempts to fill in any missing defaults that are required, and + * fixup ordering problems (using OSRFixupOrdering()) so that the resulting + * WKT is valid. + * + * This method should be expected to evolve over time to as problems are + * discovered. The following are amoung the fixup actions this method will + * take: + * + * - Fixup the ordering of nodes to match the BNF WKT ordering, using + * the FixupOrdering() method. + * + * - Add missing linear or angular units nodes. + * + * This method is the same as the C function OSRFixup(). + * + * @return OGRERR_NONE on success or an error code if something goes + * wrong. + */ + +OGRErr OGRSpatialReference::Fixup() + +{ +/* -------------------------------------------------------------------- */ +/* Ensure linear units defaulted to METER if missing for PROJCS, */ +/* GEOCCS or LOCAL_CS. */ +/* -------------------------------------------------------------------- */ + const OGR_SRSNode *poCS = GetAttrNode( "PROJCS" ); + + if( poCS == NULL ) + poCS = GetAttrNode( "LOCAL_CS" ); + + if( poCS == NULL ) + poCS = GetAttrNode( "GEOCCS" ); + + if( poCS != NULL && poCS->FindChild( "UNIT" ) == -1 ) + SetLinearUnits( SRS_UL_METER, 1.0 ); + +/* -------------------------------------------------------------------- */ +/* Ensure angular units defaulted to degrees on the GEOGCS. */ +/* -------------------------------------------------------------------- */ + poCS = GetAttrNode( "GEOGCS" ); + if( poCS != NULL && poCS->FindChild( "UNIT" ) == -1 ) + SetAngularUnits( SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV) ); + + return FixupOrdering(); +} + +/************************************************************************/ +/* OSRFixup() */ +/************************************************************************/ + +/** + * \brief Fixup as needed. + * + * This function is the same as OGRSpatialReference::Fixup(). + */ +OGRErr OSRFixup( OGRSpatialReferenceH hSRS ) + +{ + VALIDATE_POINTER1( hSRS, "OSRFixup", CE_Failure ); + + return ((OGRSpatialReference *) hSRS)->Fixup(); +} + +/************************************************************************/ +/* GetExtension() */ +/************************************************************************/ + +/** + * \brief Fetch extension value. + * + * Fetch the value of the named EXTENSION item for the identified + * target node. + * + * @param pszTargetKey the name or path to the parent node of the EXTENSION. + * @param pszName the name of the extension being fetched. + * @param pszDefault the value to return if the extension is not found. + * + * @return node value if successful or pszDefault on failure. + */ + +const char *OGRSpatialReference::GetExtension( const char *pszTargetKey, + const char *pszName, + const char *pszDefault ) const + +{ +/* -------------------------------------------------------------------- */ +/* Find the target node. */ +/* -------------------------------------------------------------------- */ + const OGR_SRSNode *poNode; + + if( pszTargetKey == NULL ) + poNode = poRoot; + else + poNode= ((OGRSpatialReference *) this)->GetAttrNode( pszTargetKey ); + + if( poNode == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Fetch matching EXTENSION if there is one. */ +/* -------------------------------------------------------------------- */ + for( int i = poNode->GetChildCount()-1; i >= 0; i-- ) + { + const OGR_SRSNode *poChild = poNode->GetChild(i); + + if( EQUAL(poChild->GetValue(),"EXTENSION") + && poChild->GetChildCount() >= 2 ) + { + if( EQUAL(poChild->GetChild(0)->GetValue(),pszName) ) + return poChild->GetChild(1)->GetValue(); + } + } + + return pszDefault; +} + +/************************************************************************/ +/* SetExtension() */ +/************************************************************************/ +/** + * \brief Set extension value. + * + * Set the value of the named EXTENSION item for the identified + * target node. + * + * @param pszTargetKey the name or path to the parent node of the EXTENSION. + * @param pszName the name of the extension being fetched. + * @param pszValue the value to set + * + * @return OGRERR_NONE on success + */ + +OGRErr OGRSpatialReference::SetExtension( const char *pszTargetKey, + const char *pszName, + const char *pszValue ) + +{ +/* -------------------------------------------------------------------- */ +/* Find the target node. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poNode; + + if( pszTargetKey == NULL ) + poNode = poRoot; + else + poNode= ((OGRSpatialReference *) this)->GetAttrNode( pszTargetKey ); + + if( poNode == NULL ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Fetch matching EXTENSION if there is one. */ +/* -------------------------------------------------------------------- */ + for( int i = poNode->GetChildCount()-1; i >= 0; i-- ) + { + OGR_SRSNode *poChild = poNode->GetChild(i); + + if( EQUAL(poChild->GetValue(),"EXTENSION") + && poChild->GetChildCount() >= 2 ) + { + if( EQUAL(poChild->GetChild(0)->GetValue(),pszName) ) + { + poChild->GetChild(1)->SetValue( pszValue ); + return OGRERR_NONE; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Create a new EXTENSION node. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poAuthNode; + + poAuthNode = new OGR_SRSNode( "EXTENSION" ); + poAuthNode->AddChild( new OGR_SRSNode( pszName ) ); + poAuthNode->AddChild( new OGR_SRSNode( pszValue ) ); + + poNode->AddChild( poAuthNode ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRCleanup() */ +/************************************************************************/ + +CPL_C_START +void CleanupESRIDatumMappingTable(); +CPL_C_END +static void CleanupSRSWGS84Thread(); + +/** + * \brief Cleanup cached SRS related memory. + * + * This function will attempt to cleanup any cache spatial reference + * related information, such as cached tables of coordinate systems. + */ +void OSRCleanup( void ) + +{ + CleanupESRIDatumMappingTable(); + CSVDeaccess( NULL ); + OCTCleanupProjMutex(); + CleanupSRSWGS84Thread(); +} + +/************************************************************************/ +/* GetAxis() */ +/************************************************************************/ + +/** + * \brief Fetch the orientation of one axis. + * + * Fetches the the request axis (iAxis - zero based) from the + * indicated portion of the coordinate system (pszTargetKey) which + * should be either "GEOGCS" or "PROJCS". + * + * No CPLError is issued on routine failures (such as not finding the AXIS). + * + * This method is equivalent to the C function OSRGetAxis(). + * + * @param pszTargetKey the coordinate system part to query ("PROJCS" or "GEOGCS"). + * @param iAxis the axis to query (0 for first, 1 for second). + * @param peOrientation location into which to place the fetch orientation, may be NULL. + * + * @return the name of the axis or NULL on failure. + */ + +const char * +OGRSpatialReference::GetAxis( const char *pszTargetKey, int iAxis, + OGRAxisOrientation *peOrientation ) const + +{ + if( peOrientation != NULL ) + *peOrientation = OAO_Other; + +/* -------------------------------------------------------------------- */ +/* Find the target node. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poNode; + + if( pszTargetKey == NULL ) + poNode = poRoot; + else + poNode= ((OGRSpatialReference *) this)->GetAttrNode( pszTargetKey ); + + if( poNode == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Find desired child AXIS. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poAxis = NULL; + int iChild, nChildCount = poNode->GetChildCount(); + + for( iChild = 0; iChild < nChildCount; iChild++ ) + { + OGR_SRSNode *poChild = poNode->GetChild( iChild ); + + if( !EQUAL(poChild->GetValue(),"AXIS") ) + continue; + + if( iAxis == 0 ) + { + poAxis = poChild; + break; + } + iAxis--; + } + + if( poAxis == NULL ) + return NULL; + + if( poAxis->GetChildCount() < 2 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Extract name and orientation if possible. */ +/* -------------------------------------------------------------------- */ + if( peOrientation != NULL ) + { + const char *pszOrientation = poAxis->GetChild(1)->GetValue(); + + if( EQUAL(pszOrientation,"NORTH") ) + *peOrientation = OAO_North; + else if( EQUAL(pszOrientation,"EAST") ) + *peOrientation = OAO_East; + else if( EQUAL(pszOrientation,"SOUTH") ) + *peOrientation = OAO_South; + else if( EQUAL(pszOrientation,"WEST") ) + *peOrientation = OAO_West; + else if( EQUAL(pszOrientation,"UP") ) + *peOrientation = OAO_Up; + else if( EQUAL(pszOrientation,"DOWN") ) + *peOrientation = OAO_Down; + else if( EQUAL(pszOrientation,"OTHER") ) + *peOrientation = OAO_Other; + else + { + CPLDebug( "OSR", "Unrecognised orientation value '%s'.", + pszOrientation ); + } + } + + return poAxis->GetChild(0)->GetValue(); +} + +/************************************************************************/ +/* OSRGetAxis() */ +/************************************************************************/ + +/** + * \brief Fetch the orientation of one axis. + * + * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis + */ +const char *OSRGetAxis( OGRSpatialReferenceH hSRS, + const char *pszTargetKey, int iAxis, + OGRAxisOrientation *peOrientation ) + +{ + VALIDATE_POINTER1( hSRS, "OSRGetAxis", NULL ); + + return ((OGRSpatialReference *) hSRS)->GetAxis( pszTargetKey, iAxis, + peOrientation ); +} + +/************************************************************************/ +/* OSRAxisEnumToName() */ +/************************************************************************/ + +/** + * \brief Return the string representation for the OGRAxisOrientation enumeration. + * + * For example "NORTH" for OAO_North. + * + * @return an internal string + */ +const char *OSRAxisEnumToName( OGRAxisOrientation eOrientation ) + +{ + if( eOrientation == OAO_North ) + return "NORTH"; + if( eOrientation == OAO_East ) + return "EAST"; + if( eOrientation == OAO_South ) + return "SOUTH"; + if( eOrientation == OAO_West ) + return "WEST"; + if( eOrientation == OAO_Up ) + return "UP"; + if( eOrientation == OAO_Down ) + return "DOWN"; + if( eOrientation == OAO_Other ) + return "OTHER"; + + return "UNKNOWN"; +} + +/************************************************************************/ +/* SetAxes() */ +/************************************************************************/ + +/** + * \brief Set the axes for a coordinate system. + * + * Set the names, and orientations of the axes for either a projected + * (PROJCS) or geographic (GEOGCS) coordinate system. + * + * This method is equivalent to the C function OSRSetAxes(). + * + * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS. + * @param pszXAxisName name of first axis, normally "Long" or "Easting". + * @param eXAxisOrientation normally OAO_East. + * @param pszYAxisName name of second axis, normally "Lat" or "Northing". + * @param eYAxisOrientation normally OAO_North. + * + * @return OGRERR_NONE on success or an error code. + */ + +OGRErr +OGRSpatialReference::SetAxes( const char *pszTargetKey, + const char *pszXAxisName, + OGRAxisOrientation eXAxisOrientation, + const char *pszYAxisName, + OGRAxisOrientation eYAxisOrientation ) + +{ +/* -------------------------------------------------------------------- */ +/* Find the target node. */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poNode; + + if( pszTargetKey == NULL ) + poNode = poRoot; + else + poNode= ((OGRSpatialReference *) this)->GetAttrNode( pszTargetKey ); + + if( poNode == NULL ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Strip any existing AXIS children. */ +/* -------------------------------------------------------------------- */ + while( poNode->FindChild( "AXIS" ) >= 0 ) + poNode->DestroyChild( poNode->FindChild( "AXIS" ) ); + +/* -------------------------------------------------------------------- */ +/* Insert desired axes */ +/* -------------------------------------------------------------------- */ + OGR_SRSNode *poAxis = new OGR_SRSNode( "AXIS" ); + + poAxis->AddChild( new OGR_SRSNode( pszXAxisName ) ); + poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(eXAxisOrientation) )); + + poNode->AddChild( poAxis ); + + poAxis = new OGR_SRSNode( "AXIS" ); + + poAxis->AddChild( new OGR_SRSNode( pszYAxisName ) ); + poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(eYAxisOrientation) )); + + poNode->AddChild( poAxis ); + + return OGRERR_NONE; +} + +/************************************************************************/ +/* OSRSetAxes() */ +/************************************************************************/ +/** + * \brief Set the axes for a coordinate system. + * + * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes + */ +OGRErr OSRSetAxes( OGRSpatialReferenceH hSRS, + const char *pszTargetKey, + const char *pszXAxisName, + OGRAxisOrientation eXAxisOrientation, + const char *pszYAxisName, + OGRAxisOrientation eYAxisOrientation ) +{ + VALIDATE_POINTER1( hSRS, "OSRSetAxes", OGRERR_FAILURE ); + + return ((OGRSpatialReference *) hSRS)->SetAxes( pszTargetKey, + pszXAxisName, + eXAxisOrientation, + pszYAxisName, + eYAxisOrientation ); +} + +#ifdef HAVE_MITAB +char CPL_DLL *MITABSpatialRef2CoordSys( OGRSpatialReference * ); +OGRSpatialReference CPL_DLL * MITABCoordSys2SpatialRef( const char * ); +#endif + +/************************************************************************/ +/* OSRExportToMICoordSys() */ +/************************************************************************/ +/** + * \brief Export coordinate system in Mapinfo style CoordSys format. + * + * This method is the equivalent of the C++ method OGRSpatialReference::exportToMICoordSys + */ +OGRErr OSRExportToMICoordSys( OGRSpatialReferenceH hSRS, char ** ppszReturn ) + +{ + VALIDATE_POINTER1( hSRS, "OSRExportToMICoordSys", CE_Failure ); + + *ppszReturn = NULL; + + return ((OGRSpatialReference *) hSRS)->exportToMICoordSys( ppszReturn ); +} + +/************************************************************************/ +/* exportToMICoordSys() */ +/************************************************************************/ + +/** + * \brief Export coordinate system in Mapinfo style CoordSys format. + * + * Note that the returned WKT string should be freed with OGRFree() or + * CPLFree() when no longer needed. It is the responsibility of the caller. + * + * This method is the same as the C function OSRExportToMICoordSys(). + * + * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys + * definition will be assigned. + * + * @return OGRERR_NONE on success, OGRERR_FAILURE on failure, + * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in. + */ + +OGRErr OGRSpatialReference::exportToMICoordSys( char **ppszResult ) const + +{ +#ifdef HAVE_MITAB + *ppszResult = MITABSpatialRef2CoordSys( (OGRSpatialReference *) this ); + if( *ppszResult != NULL && strlen(*ppszResult) > 0 ) + return OGRERR_NONE; + else + return OGRERR_FAILURE; +#else + CPLError( CE_Failure, CPLE_NotSupported, + "MITAB not available, CoordSys support disabled." ); + + return OGRERR_UNSUPPORTED_OPERATION; +#endif +} + +/************************************************************************/ +/* OSRImportFromMICoordSys() */ +/************************************************************************/ +/** + * \brief Import Mapinfo style CoordSys definition. + * + * This method is the equivalent of the C++ method OGRSpatialReference::importFromMICoordSys + */ + +OGRErr OSRImportFromMICoordSys( OGRSpatialReferenceH hSRS, + const char *pszCoordSys ) + +{ + VALIDATE_POINTER1( hSRS, "OSRImportFromMICoordSys", CE_Failure ); + + return ((OGRSpatialReference *)hSRS)->importFromMICoordSys( pszCoordSys ); +} + +/************************************************************************/ +/* importFromMICoordSys() */ +/************************************************************************/ + +/** + * \brief Import Mapinfo style CoordSys definition. + * + * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys definition string. + * + * This method is the equivalent of the C function OSRImportFromMICoordSys(). + * + * @param pszCoordSys Mapinfo style CoordSys definition string. + * + * @return OGRERR_NONE on success, OGRERR_FAILURE on failure, + * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in. + */ + +OGRErr OGRSpatialReference::importFromMICoordSys( const char *pszCoordSys ) + +{ +#ifdef HAVE_MITAB + OGRSpatialReference *poResult = MITABCoordSys2SpatialRef( pszCoordSys ); + + if( poResult == NULL ) + return OGRERR_FAILURE; + + *this = *poResult; + delete poResult; + + return OGRERR_NONE; +#else + CPLError( CE_Failure, CPLE_NotSupported, + "MITAB not available, CoordSys support disabled." ); + + return OGRERR_UNSUPPORTED_OPERATION; +#endif +} + +/************************************************************************/ +/* OSRCalcInvFlattening() */ +/************************************************************************/ + +/** + * \brief Compute inverse flattening from semi-major and semi-minor axis + * + * @param dfSemiMajor Semi-major axis length. + * @param dfSemiMinor Semi-minor axis length. + * + * @return inverse flattening, or 0 if both axis are equal. + * @since GDAL 2.0 + */ + +double OSRCalcInvFlattening( double dfSemiMajor, double dfSemiMinor ) +{ + if( fabs(dfSemiMajor-dfSemiMinor) < 1e-1 ) + return 0; + else if( dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor ) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "OSRCalcInvFlattening(): Wrong input values"); + return 0; + } + else + return dfSemiMajor / (dfSemiMajor - dfSemiMinor); +} + +/************************************************************************/ +/* OSRCalcInvFlattening() */ +/************************************************************************/ + +/** + * \brief Compute semi-minor axis from semi-major axis and inverse flattening. + * + * @param dfSemiMajor Semi-major axis length. + * @param dfInvFlattening Inverse flattening or 0 for sphere. + * + * @return semi-minor axis + * @since GDAL 2.0 + */ + +double OSRCalcSemiMinorFromInvFlattening( double dfSemiMajor, double dfInvFlattening ) +{ + if( fabs(dfInvFlattening) < 0.000000000001 ) + return dfSemiMajor; + else if( dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0 ) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "OSRCalcSemiMinorFromInvFlattening(): Wrong input values"); + return dfSemiMajor; + } + else + return dfSemiMajor * (1.0 - 1.0/dfInvFlattening); +} + +/************************************************************************/ +/* GetWGS84SRS() */ +/************************************************************************/ + +static OGRSpatialReference* poSRSWGS84 = NULL; +static CPLMutex* hMutex = NULL; + +/** + * \brief Returns an instance of a SRS object with WGS84 WKT. + * + * The reference counter of the returned object is not increased by this operation. + * + * @return instance. + * @since GDAL 2.0 + */ + +OGRSpatialReference* OGRSpatialReference::GetWGS84SRS() +{ + CPLMutexHolderD(&hMutex); + if( poSRSWGS84 == NULL ) + poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84); + return poSRSWGS84; +} + +/************************************************************************/ +/* CleanupSRSWGS84Thread() */ +/************************************************************************/ + +static void CleanupSRSWGS84Thread() +{ + if( hMutex != NULL ) + { + poSRSWGS84->Release(); + poSRSWGS84 = NULL; + CPLDestroyMutex(hMutex); + hMutex = NULL; + } +} diff --git a/bazaar/plugin/gdal/ogr/ogrsurface.cpp b/bazaar/plugin/gdal/ogr/ogrsurface.cpp new file mode 100644 index 000000000..ee0b99dac --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrsurface.cpp @@ -0,0 +1,97 @@ +/****************************************************************************** + * $Id: ogrsurface.cpp 27959 2014-11-14 18:29:21Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: The OGRSurface class. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogr_geometry.h" +#include "ogr_p.h" + +/** + * \fn double OGRSurface::get_Area() const; + * + * \brief Get the area of the surface object. + * + * For polygons the area is computed as the area of the outer ring less + * the area of all internal rings. + * + * This method relates to the SFCOM ISurface::get_Area() method. + * + * @return the area of the feature in square units of the spatial reference + * system in use. + */ + +/** + * \fn OGRErr OGRSurface::PointOnSurface( OGRPoint * poPoint ) const; + * + * \brief This method relates to the SFCOM ISurface::get_PointOnSurface() method. + * + * NOTE: Only implemented when GEOS included in build. + * + * @param poPoint point to be set with an internal point. + * + * @return OGRERR_NONE if it succeeds or OGRERR_FAILURE otherwise. + */ + +/************************************************************************/ +/* CastToPolygon() */ +/************************************************************************/ + +/** + * \brief Cast to polygon + * + * The passed in geometry is consumed and a new one returned (or NULL in case + * of failure) + * + * @param poSurface the input geometry - ownership is passed to the method. + * @return new geometry. + */ + +OGRPolygon* OGRSurface::CastToPolygon(OGRSurface* poSurface) +{ + OGRSurfaceCasterToPolygon pfn = poSurface->GetCasterToPolygon(); + return pfn(poSurface); +} + +/************************************************************************/ +/* CastToCurvePolygon() */ +/************************************************************************/ + +/** + * \brief Cast to curve polygon + * + * The passed in geometry is consumed and a new one returned (or NULL in case + * of failure) + * + * @param poSurface the input geometry - ownership is passed to the method. + * @return new geometry. + */ + +OGRCurvePolygon* OGRSurface::CastToCurvePolygon(OGRSurface* poSurface) +{ + OGRSurfaceCasterToCurvePolygon pfn = poSurface->GetCasterToCurvePolygon(); + return pfn(poSurface); +} diff --git a/bazaar/plugin/gdal/ogr/ogrutils.cpp b/bazaar/plugin/gdal/ogr/ogrutils.cpp new file mode 100644 index 000000000..f6079dcbc --- /dev/null +++ b/bazaar/plugin/gdal/ogr/ogrutils.cpp @@ -0,0 +1,1363 @@ +/****************************************************************************** + * $Id: ogrutils.cpp 28900 2015-04-14 09:40:34Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Utility functions for OGR classes, including some related to + * parsing well known text format vectors. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_vsi.h" + +#include + +#include "ogr_geometry.h" +#include "ogr_p.h" + +#ifdef OGR_ENABLED +# include "ogrsf_frmts.h" +#endif /* OGR_ENABLED */ + +#include "gdal.h" + +CPL_CVSID("$Id: ogrutils.cpp 28900 2015-04-14 09:40:34Z rouault $"); + +/************************************************************************/ +/* OGRFormatDouble() */ +/************************************************************************/ + +void OGRFormatDouble( char *pszBuffer, int nBufferLen, double dfVal, char chDecimalSep, int nPrecision ) +{ + int i; + int nTruncations = 0; + char szFormat[16]; + sprintf(szFormat, "%%.%df", nPrecision); + + int ret = CPLsnprintf(pszBuffer, nBufferLen, szFormat, dfVal); + /* Windows CRT doesn't conform with C99 and return -1 when buffer is truncated */ + if (ret >= nBufferLen || ret == -1) + { + CPLsnprintf(pszBuffer, nBufferLen, "%s", "too_big"); + return; + } + + while(nPrecision > 0) + { + i = 0; + int nCountBeforeDot = 0; + int iDotPos = -1; + while( pszBuffer[i] != '\0' ) + { + if ((pszBuffer[i] == '.' || pszBuffer[i] == ',') && chDecimalSep != '\0') + { + iDotPos = i; + pszBuffer[i] = chDecimalSep; + } + else if (iDotPos < 0 && pszBuffer[i] != '-') + nCountBeforeDot ++; + i++; + } + + /* -------------------------------------------------------------------- */ + /* Trim trailing 00000x's as they are likely roundoff error. */ + /* -------------------------------------------------------------------- */ + if( i > 10 && iDotPos >=0 ) + { + if (/* && pszBuffer[i-1] == '1' &&*/ + pszBuffer[i-2] == '0' + && pszBuffer[i-3] == '0' + && pszBuffer[i-4] == '0' + && pszBuffer[i-5] == '0' + && pszBuffer[i-6] == '0' ) + { + pszBuffer[--i] = '\0'; + } + else if( i - 8 > iDotPos && /* pszBuffer[i-1] == '1' */ + /* && pszBuffer[i-2] == '0' && */ + (nCountBeforeDot >= 4 || pszBuffer[i-3] == '0') + && (nCountBeforeDot >= 5 || pszBuffer[i-4] == '0') + && (nCountBeforeDot >= 6 || pszBuffer[i-5] == '0') + && (nCountBeforeDot >= 7 || pszBuffer[i-6] == '0') + && (nCountBeforeDot >= 8 || pszBuffer[i-7] == '0') + && pszBuffer[i-8] == '0' + && pszBuffer[i-9] == '0') + { + i -= 8; + pszBuffer[i] = '\0'; + } + } + + /* -------------------------------------------------------------------- */ + /* Trim trailing zeros. */ + /* -------------------------------------------------------------------- */ + while( i > 2 && pszBuffer[i-1] == '0' && pszBuffer[i-2] != '.' ) + { + pszBuffer[--i] = '\0'; + } + + /* -------------------------------------------------------------------- */ + /* Detect trailing 99999X's as they are likely roundoff error. */ + /* -------------------------------------------------------------------- */ + if( i > 10 && + iDotPos >= 0 && + nPrecision + nTruncations >= 15) + { + if (/*pszBuffer[i-1] == '9' && */ + pszBuffer[i-2] == '9' + && pszBuffer[i-3] == '9' + && pszBuffer[i-4] == '9' + && pszBuffer[i-5] == '9' + && pszBuffer[i-6] == '9' ) + { + nPrecision --; + nTruncations ++; + sprintf(szFormat, "%%.%df", nPrecision); + CPLsnprintf(pszBuffer, nBufferLen, szFormat, dfVal); + continue; + } + else if (i - 9 > iDotPos && /*pszBuffer[i-1] == '9' && */ + /*pszBuffer[i-2] == '9' && */ + (nCountBeforeDot >= 4 || pszBuffer[i-3] == '9') + && (nCountBeforeDot >= 5 || pszBuffer[i-4] == '9') + && (nCountBeforeDot >= 6 || pszBuffer[i-5] == '9') + && (nCountBeforeDot >= 7 || pszBuffer[i-6] == '9') + && (nCountBeforeDot >= 8 || pszBuffer[i-7] == '9') + && pszBuffer[i-8] == '9' + && pszBuffer[i-9] == '9') + { + nPrecision --; + nTruncations ++; + sprintf(szFormat, "%%.%df", nPrecision); + CPLsnprintf(pszBuffer, nBufferLen, szFormat, dfVal); + continue; + } + } + + break; + } + +} + +/************************************************************************/ +/* OGRMakeWktCoordinate() */ +/* */ +/* Format a well known text coordinate, trying to keep the */ +/* ASCII representation compact, but accurate. These rules */ +/* will have to tighten up in the future. */ +/* */ +/* Currently a new point should require no more than 64 */ +/* characters barring the X or Y value being extremely large. */ +/************************************************************************/ + +void OGRMakeWktCoordinate( char *pszTarget, double x, double y, double z, + int nDimension ) + +{ + const size_t bufSize = 75; + const size_t maxTargetSize = 75; /* Assumed max length of the target buffer. */ + + char szX[bufSize]; + char szY[bufSize]; + char szZ[bufSize]; + + szZ[0] = '\0'; + + int nLenX, nLenY; + + if( x == (int) x && y == (int) y ) + { + snprintf( szX, bufSize, "%d", (int) x ); + snprintf( szY, bufSize, "%d", (int) y ); + } + else + { + OGRFormatDouble( szX, bufSize, x, '.' ); + OGRFormatDouble( szY, bufSize, y, '.' ); + } + + nLenX = strlen(szX); + nLenY = strlen(szY); + + if( nDimension == 3 ) + { + if( z == (int) z ) + { + snprintf( szZ, bufSize, "%d", (int) z ); + } + else + { + OGRFormatDouble( szZ, bufSize, z, '.' ); + } + } + + if( nLenX + 1 + nLenY + ((nDimension == 3) ? (1 + strlen(szZ)) : 0) >= maxTargetSize ) + { +#ifdef DEBUG + CPLDebug( "OGR", + "Yow! Got this big result in OGRMakeWktCoordinate()\n" + "%s %s %s", + szX, szY, szZ ); +#endif + if( nDimension == 3 ) + strcpy( pszTarget, "0 0 0"); + else + strcpy( pszTarget, "0 0"); + } + else + { + memcpy( pszTarget, szX, nLenX ); + pszTarget[nLenX] = ' '; + memcpy( pszTarget + nLenX + 1, szY, nLenY ); + if (nDimension == 3) + { + pszTarget[nLenX + 1 + nLenY] = ' '; + strcpy( pszTarget + nLenX + 1 + nLenY + 1, szZ ); + } + else + { + pszTarget[nLenX + 1 + nLenY] = '\0'; + } + } +} + +/************************************************************************/ +/* OGRWktReadToken() */ +/* */ +/* Read one token or delimeter and put into token buffer. Pre */ +/* and post white space is swallowed. */ +/************************************************************************/ + +const char *OGRWktReadToken( const char * pszInput, char * pszToken ) + +{ + if( pszInput == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Swallow pre-white space. */ +/* -------------------------------------------------------------------- */ + while( *pszInput == ' ' || *pszInput == '\t' ) + pszInput++; + +/* -------------------------------------------------------------------- */ +/* If this is a delimeter, read just one character. */ +/* -------------------------------------------------------------------- */ + if( *pszInput == '(' || *pszInput == ')' || *pszInput == ',' ) + { + pszToken[0] = *pszInput; + pszToken[1] = '\0'; + + pszInput++; + } + +/* -------------------------------------------------------------------- */ +/* Or if it alpha numeric read till we reach non-alpha numeric */ +/* text. */ +/* -------------------------------------------------------------------- */ + else + { + int iChar = 0; + + while( iChar < OGR_WKT_TOKEN_MAX-1 + && ((*pszInput >= 'a' && *pszInput <= 'z') + || (*pszInput >= 'A' && *pszInput <= 'Z') + || (*pszInput >= '0' && *pszInput <= '9') + || *pszInput == '.' + || *pszInput == '+' + || *pszInput == '-') ) + { + pszToken[iChar++] = *(pszInput++); + } + + pszToken[iChar++] = '\0'; + } + +/* -------------------------------------------------------------------- */ +/* Eat any trailing white space. */ +/* -------------------------------------------------------------------- */ + while( *pszInput == ' ' || *pszInput == '\t' ) + pszInput++; + + return( pszInput ); +} + +/************************************************************************/ +/* OGRWktReadPoints() */ +/* */ +/* Read a point string. The point list must be contained in */ +/* brackets and each point pair separated by a comma. */ +/************************************************************************/ + +const char * OGRWktReadPoints( const char * pszInput, + OGRRawPoint ** ppaoPoints, double **ppadfZ, + int * pnMaxPoints, + int * pnPointsRead ) + +{ + const char *pszOrigInput = pszInput; + *pnPointsRead = 0; + + if( pszInput == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Eat any leading white space. */ +/* -------------------------------------------------------------------- */ + while( *pszInput == ' ' || *pszInput == '\t' ) + pszInput++; + +/* -------------------------------------------------------------------- */ +/* If this isn't an opening bracket then we have a problem! */ +/* -------------------------------------------------------------------- */ + if( *pszInput != '(' ) + { + CPLDebug( "OGR", + "Expected '(', but got %s in OGRWktReadPoints().\n", + pszInput ); + + return pszInput; + } + + pszInput++; + +/* ==================================================================== */ +/* This loop reads a single point. It will continue till we */ +/* run out of well formed points, or a closing bracket is */ +/* encountered. */ +/* ==================================================================== */ + char szDelim[OGR_WKT_TOKEN_MAX]; + + do { +/* -------------------------------------------------------------------- */ +/* Read the X and Y values, verify they are numeric. */ +/* -------------------------------------------------------------------- */ + char szTokenX[OGR_WKT_TOKEN_MAX]; + char szTokenY[OGR_WKT_TOKEN_MAX]; + + pszInput = OGRWktReadToken( pszInput, szTokenX ); + pszInput = OGRWktReadToken( pszInput, szTokenY ); + + if( (!isdigit(szTokenX[0]) && szTokenX[0] != '-' && szTokenX[0] != '.' ) + || (!isdigit(szTokenY[0]) && szTokenY[0] != '-' && szTokenY[0] != '.') ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Do we need to grow the point list to hold this point? */ +/* -------------------------------------------------------------------- */ + if( *pnPointsRead == *pnMaxPoints ) + { + *pnMaxPoints = *pnMaxPoints * 2 + 10; + *ppaoPoints = (OGRRawPoint *) + CPLRealloc(*ppaoPoints, sizeof(OGRRawPoint) * *pnMaxPoints); + + if( *ppadfZ != NULL ) + { + *ppadfZ = (double *) + CPLRealloc(*ppadfZ, sizeof(double) * *pnMaxPoints); + } + } + +/* -------------------------------------------------------------------- */ +/* Add point to list. */ +/* -------------------------------------------------------------------- */ + (*ppaoPoints)[*pnPointsRead].x = CPLAtof(szTokenX); + (*ppaoPoints)[*pnPointsRead].y = CPLAtof(szTokenY); + +/* -------------------------------------------------------------------- */ +/* Do we have a Z coordinate? */ +/* -------------------------------------------------------------------- */ + pszInput = OGRWktReadToken( pszInput, szDelim ); + + if( isdigit(szDelim[0]) || szDelim[0] == '-' || szDelim[0] == '.' ) + { + if( *ppadfZ == NULL ) + { + *ppadfZ = (double *) CPLCalloc(sizeof(double),*pnMaxPoints); + } + + (*ppadfZ)[*pnPointsRead] = CPLAtof(szDelim); + + pszInput = OGRWktReadToken( pszInput, szDelim ); + } + else if ( *ppadfZ != NULL ) + (*ppadfZ)[*pnPointsRead] = 0.0; + + (*pnPointsRead)++; + +/* -------------------------------------------------------------------- */ +/* Do we have a M coordinate? */ +/* If we do, just skip it. */ +/* -------------------------------------------------------------------- */ + if( isdigit(szDelim[0]) || szDelim[0] == '-' || szDelim[0] == '.' ) + { + pszInput = OGRWktReadToken( pszInput, szDelim ); + } + +/* -------------------------------------------------------------------- */ +/* Read next delimeter ... it should be a comma if there are */ +/* more points. */ +/* -------------------------------------------------------------------- */ + if( szDelim[0] != ')' && szDelim[0] != ',' ) + { + CPLDebug( "OGR", + "Corrupt input in OGRWktReadPoints()\n" + "Got `%s' when expecting `,' or `)', near `%s' in %s.\n", + szDelim, pszInput, pszOrigInput ); + return NULL; + } + + } while( szDelim[0] == ',' ); + + return pszInput; +} + +/************************************************************************/ +/* OGRMalloc() */ +/* */ +/* Cover for CPLMalloc() */ +/************************************************************************/ + +void *OGRMalloc( size_t size ) + +{ + return CPLMalloc( size ); +} + +/************************************************************************/ +/* OGRCalloc() */ +/* */ +/* Cover for CPLCalloc() */ +/************************************************************************/ + +void * OGRCalloc( size_t count, size_t size ) + +{ + return CPLCalloc( count, size ); +} + +/************************************************************************/ +/* OGRRealloc() */ +/* */ +/* Cover for CPLRealloc() */ +/************************************************************************/ + +void *OGRRealloc( void * pOld, size_t size ) + +{ + return CPLRealloc( pOld, size ); +} + +/************************************************************************/ +/* OGRFree() */ +/* */ +/* Cover for CPLFree(). */ +/************************************************************************/ + +void OGRFree( void * pMemory ) + +{ + CPLFree( pMemory ); +} + +/** + * General utility option processing. + * + * This function is intended to provide a variety of generic commandline + * options for all OGR commandline utilities. It takes care of the following + * commandline options: + * + * --version: report version of GDAL in use. + * --license: report GDAL license info. + * --format [format]: report details of one format driver. + * --formats: report all format drivers configured. + * --optfile filename: expand an option file into the argument list. + * --config key value: set system configuration option. + * --debug [on/off/value]: set debug level. + * --pause: Pause for user input (allows time to attach debugger) + * --locale [locale]: Install a locale using setlocale() (debugging) + * --help-general: report detailed help on general options. + * + * The argument array is replaced "in place" and should be freed with + * CSLDestroy() when no longer needed. The typical usage looks something + * like the following. Note that the formats should be registered so that + * the --formats option will work properly. + * + * int main( int argc, char ** argv ) + * { + * OGRRegisterAll(); + * + * argc = OGRGeneralCmdLineProcessor( argc, &argv, 0 ); + * if( argc < 1 ) + * exit( -argc ); + * + * @param nArgc number of values in the argument list. + * @param ppapszArgv pointer to the argument list array (will be updated in place). + * @param nOptions unused. + * + * @return updated nArgc argument count. Return of 0 requests terminate + * without error, return of -1 requests exit with error code. + */ + +int OGRGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv, int nOptions ) + +{ + (void) nOptions; + return GDALGeneralCmdLineProcessor( nArgc, ppapszArgv, GDAL_OF_VECTOR ); +} + +/************************************************************************/ +/* OGRParseDate() */ +/* */ +/* Parse a variety of text date formats into an OGRField. */ +/************************************************************************/ + +/** + * Parse date string. + * + * This function attempts to parse a date string in a variety of formats + * into the OGRField.Date format suitable for use with OGR. Generally + * speaking this function is expecting values like: + * + * YYYY-MM-DD HH:MM:SS[.sss]+nn + * or YYYY-MM-DDTHH:MM:SS[.sss]Z (ISO 8601 format) + * + * The seconds may also have a decimal portion (which is ignored). And + * just dates (YYYY-MM-DD) or just times (HH:MM:SS[.sss]) are also supported. + * The date may also be in YYYY/MM/DD format. If the year is less than 100 + * and greater than 30 a "1900" century value will be set. If it is less than + * 30 and greater than -1 then a "2000" century value will be set. In + * the future this function may be generalized, and additional control + * provided through nOptions, but an nOptions value of "0" should always do + * a reasonable default form of processing. + * + * The value of psField will be indeterminate if the function fails (returns + * FALSE). + * + * @param pszInput the input date string. + * @param psField the OGRField that will be updated with the parsed result. + * @param nOptions parsing options, for now always 0. + * + * @return TRUE if apparently successful or FALSE on failure. + */ + +int OGRParseDate( const char *pszInput, + OGRField *psField, + CPL_UNUSED int nOptions ) +{ + int bGotSomething = FALSE; + + psField->Date.Year = 0; + psField->Date.Month = 0; + psField->Date.Day = 0; + psField->Date.Hour = 0; + psField->Date.Minute = 0; + psField->Date.Second = 0; + psField->Date.TZFlag = 0; + psField->Date.Reserved = 0; + +/* -------------------------------------------------------------------- */ +/* Do we have a date? */ +/* -------------------------------------------------------------------- */ + while( *pszInput == ' ' ) + pszInput++; + + if( strstr(pszInput,"-") != NULL || strstr(pszInput,"/") != NULL ) + { + int nYear = atoi(pszInput); + if( nYear != (GInt16)nYear ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Years < -32768 or > 32767 are not supported"); + return FALSE; + } + psField->Date.Year = (GInt16)nYear; + if( psField->Date.Year < 100 && psField->Date.Year >= 30 ) + psField->Date.Year += 1900; + else if( psField->Date.Year < 30 && psField->Date.Year >= 0 ) + psField->Date.Year += 2000; + + while( *pszInput >= '0' && *pszInput <= '9' ) + pszInput++; + if( *pszInput != '-' && *pszInput != '/' ) + return FALSE; + else + pszInput++; + + psField->Date.Month = (GByte)atoi(pszInput); + if( psField->Date.Month > 12 ) + return FALSE; + + while( *pszInput >= '0' && *pszInput <= '9' ) + pszInput++; + if( *pszInput != '-' && *pszInput != '/' ) + return FALSE; + else + pszInput++; + + psField->Date.Day = (GByte)atoi(pszInput); + if( psField->Date.Day > 31 ) + return FALSE; + + while( *pszInput >= '0' && *pszInput <= '9' ) + pszInput++; + + bGotSomething = TRUE; + + /* If ISO 8601 format */ + if( *pszInput == 'T' ) + pszInput ++; + } + +/* -------------------------------------------------------------------- */ +/* Do we have a time? */ +/* -------------------------------------------------------------------- */ + while( *pszInput == ' ' ) + pszInput++; + + if( strstr(pszInput,":") != NULL ) + { + psField->Date.Hour = (GByte)atoi(pszInput); + if( psField->Date.Hour > 23 ) + return FALSE; + + while( *pszInput >= '0' && *pszInput <= '9' ) + pszInput++; + if( *pszInput != ':' ) + return FALSE; + else + pszInput++; + + psField->Date.Minute = (GByte)atoi(pszInput); + if( psField->Date.Minute > 59 ) + return FALSE; + + while( *pszInput >= '0' && *pszInput <= '9' ) + pszInput++; + if( *pszInput == ':' ) + { + pszInput++; + + psField->Date.Second = (float)CPLAtof(pszInput); + if( psField->Date.Second > 61 ) + return FALSE; + + while( (*pszInput >= '0' && *pszInput <= '9') + || *pszInput == '.' ) + { + pszInput++; + } + + /* If ISO 8601 format */ + if( *pszInput == 'Z' ) + { + psField->Date.TZFlag = 100; + } + } + + bGotSomething = TRUE; + } + + // No date or time! + if( !bGotSomething ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Do we have a timezone? */ +/* -------------------------------------------------------------------- */ + while( *pszInput == ' ' ) + pszInput++; + + if( *pszInput == '-' || *pszInput == '+' ) + { + // +HH integral offset + if( strlen(pszInput) <= 3 ) + psField->Date.TZFlag = (GByte)(100 + atoi(pszInput) * 4); + + else if( pszInput[3] == ':' // +HH:MM offset + && atoi(pszInput+4) % 15 == 0 ) + { + psField->Date.TZFlag = (GByte)(100 + + atoi(pszInput+1) * 4 + + (atoi(pszInput+4) / 15)); + + if( pszInput[0] == '-' ) + psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100; + } + else if( isdigit(pszInput[3]) && isdigit(pszInput[4]) // +HHMM offset + && atoi(pszInput+3) % 15 == 0 ) + { + psField->Date.TZFlag = (GByte)(100 + + static_cast(CPLScanLong(pszInput+1,2)) * 4 + + (atoi(pszInput+3) / 15)); + + if( pszInput[0] == '-' ) + psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100; + } + else if( isdigit(pszInput[3]) && pszInput[4] == '\0' // +HMM offset + && atoi(pszInput+2) % 15 == 0 ) + { + psField->Date.TZFlag = (GByte)(100 + + static_cast(CPLScanLong(pszInput+1,1)) * 4 + + (atoi(pszInput+2) / 15)); + + if( pszInput[0] == '-' ) + psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100; + } + // otherwise ignore any timezone info. + } + + return TRUE; +} + + +/************************************************************************/ +/* OGRParseXMLDateTime() */ +/************************************************************************/ + +int OGRParseXMLDateTime( const char* pszXMLDateTime, + OGRField* psField) +{ + int year = 0, month = 0, day = 0, hour = 0, minute = 0, TZHour, TZMinute; + float second = 0; + char c; + int TZ = 0; + int bRet = FALSE; + + /* Date is expressed as a UTC date */ + if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f%c", + &year, &month, &day, &hour, &minute, &second, &c) == 7 && c == 'Z') + { + TZ = 100; + bRet = TRUE; + } + /* Date is expressed as a UTC date, with a timezone */ + else if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f%c%02d:%02d", + &year, &month, &day, &hour, &minute, &second, &c, &TZHour, &TZMinute) == 9 && + (c == '+' || c == '-')) + { + TZ = 100 + ((c == '+') ? 1 : -1) * ((TZHour * 60 + TZMinute) / 15); + bRet = TRUE; + } + /* Date is expressed into an unknown timezone */ + else if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f", + &year, &month, &day, &hour, &minute, &second) == 6) + { + TZ = 0; + bRet = TRUE; + } + /* Date is expressed as a UTC date with only year:month:day */ + else if (sscanf(pszXMLDateTime, "%04d-%02d-%02d", &year, &month, &day) == 3) + { + TZ = 0; + bRet = TRUE; + } + + if (bRet) + { + psField->Date.Year = (GInt16)year; + psField->Date.Month = (GByte)month; + psField->Date.Day = (GByte)day; + psField->Date.Hour = (GByte)hour; + psField->Date.Minute = (GByte)minute; + psField->Date.Second = second; + psField->Date.TZFlag = (GByte)TZ; + psField->Date.Reserved = 0; + } + + return bRet; +} + +/************************************************************************/ +/* OGRParseRFC822DateTime() */ +/************************************************************************/ + +static const char* aszMonthStr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +int OGRParseRFC822DateTime( const char* pszRFC822DateTime, OGRField* psField ) +{ + /* Following http://asg.web.cmu.edu/rfc/rfc822.html#sec-5 : [Fri,] 28 Dec 2007 05:24[:17] GMT */ + char** papszTokens = CSLTokenizeStringComplex( pszRFC822DateTime, " ,:", TRUE, FALSE ); + char** papszVal = papszTokens; + int bRet = FALSE; + int nTokens = CSLCount(papszTokens); + if (nTokens >= 6) + { + if ( ! ((*papszVal)[0] >= '0' && (*papszVal)[0] <= '9') ) + { + /* Ignore day of week */ + papszVal ++; + } + + int day = atoi(*papszVal); + papszVal ++; + + int month = 0; + + for(int i = 0; i < 12; i++) + { + if (EQUAL(*papszVal, aszMonthStr[i])) + month = i + 1; + } + papszVal ++; + + int year = atoi(*papszVal); + papszVal ++; + if( year < 100 && year >= 30 ) + year += 1900; + else if( year < 30 && year >= 0 ) + year += 2000; + + int hour = atoi(*papszVal); + papszVal ++; + + int minute = atoi(*papszVal); + papszVal ++; + + int second = 0; + if (*papszVal != NULL && (*papszVal)[0] >= '0' && (*papszVal)[0] <= '9') + { + second = atoi(*papszVal); + papszVal ++; + } + + if (month != 0) + { + bRet = TRUE; + int TZ = 0; + + if (*papszVal == NULL) + { + } + else if (strlen(*papszVal) == 5 && + ((*papszVal)[0] == '+' || (*papszVal)[0] == '-')) + { + char szBuf[3]; + szBuf[0] = (*papszVal)[1]; + szBuf[1] = (*papszVal)[2]; + szBuf[2] = 0; + int TZHour = atoi(szBuf); + szBuf[0] = (*papszVal)[3]; + szBuf[1] = (*papszVal)[4]; + szBuf[2] = 0; + int TZMinute = atoi(szBuf); + TZ = 100 + (((*papszVal)[0] == '+') ? 1 : -1) * ((TZHour * 60 + TZMinute) / 15); + } + else + { + const char* aszTZStr[] = { "GMT", "UT", "Z", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", "PDT" }; + int anTZVal[] = { 0, 0, 0, -5, -4, -6, -5, -7, -6, -8, -7 }; + for(int i = 0; i < 11; i++) + { + if (EQUAL(*papszVal, aszTZStr[i])) + { + TZ = 100 + anTZVal[i] * 4; + break; + } + } + } + + psField->Date.Year = (GInt16)year; + psField->Date.Month = (GByte)month; + psField->Date.Day = (GByte)day; + psField->Date.Hour = (GByte)hour; + psField->Date.Minute = (GByte)minute; + psField->Date.Second = second; + psField->Date.TZFlag = (GByte)TZ; + psField->Date.Reserved = 0; + } + } + CSLDestroy(papszTokens); + return bRet; +} + + +/** + * Returns the day of the week in Gregorian calendar + * + * @param day : day of the month, between 1 and 31 + * @param month : month of the year, between 1 (Jan) and 12 (Dec) + * @param year : year + + * @return day of the week : 0 for Monday, ... 6 for Sunday + */ + +int OGRGetDayOfWeek(int day, int month, int year) +{ + /* Reference: Zeller's congruence */ + int q = day; + int m; + if (month >=3) + m = month; + else + { + m = month + 12; + year --; + } + int K = year % 100; + int J = year / 100; + int h = ( q + (((m+1)*26)/10) + K + K/4 + J/4 + 5 * J) % 7; + return ( h + 5 ) % 7; +} + + +/************************************************************************/ +/* OGRGetRFC822DateTime() */ +/************************************************************************/ + +char* OGRGetRFC822DateTime(const OGRField* psField) +{ + char* pszTZ = NULL; + const char* aszDayOfWeek[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; + + int dayofweek = OGRGetDayOfWeek(psField->Date.Day, psField->Date.Month, + psField->Date.Year); + + int month = psField->Date.Month; + if (month < 1 || month > 12) + month = 1; + + int TZFlag = psField->Date.TZFlag; + if (TZFlag == 0 || TZFlag == 100) + { + pszTZ = CPLStrdup("GMT"); + } + else + { + int TZOffset = ABS(TZFlag - 100) * 15; + int TZHour = TZOffset / 60; + int TZMinute = TZOffset - TZHour * 60; + pszTZ = CPLStrdup(CPLSPrintf("%c%02d%02d", TZFlag > 100 ? '+' : '-', + TZHour, TZMinute)); + } + char* pszRet = CPLStrdup(CPLSPrintf("%s, %02d %s %04d %02d:%02d:%02d %s", + aszDayOfWeek[dayofweek], psField->Date.Day, aszMonthStr[month - 1], + psField->Date.Year, psField->Date.Hour, + psField->Date.Minute, (int)psField->Date.Second, pszTZ)); + CPLFree(pszTZ); + return pszRet; +} + +/************************************************************************/ +/* OGRGetXMLDateTime() */ +/************************************************************************/ + +char* OGRGetXMLDateTime(const OGRField* psField) +{ + char* pszRet; + int year = psField->Date.Year; + int month = psField->Date.Month; + int day = psField->Date.Day; + int hour = psField->Date.Hour; + int minute = psField->Date.Minute; + float second = psField->Date.Second; + int TZFlag = psField->Date.TZFlag; + if (TZFlag == 0 || TZFlag == 100) + { + if( OGR_GET_MS(second) ) + pszRet = CPLStrdup(CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%06.3fZ", + year, month, day, hour, minute, second)); + else + pszRet = CPLStrdup(CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", + year, month, day, hour, minute, (int)second)); + } + else + { + int TZOffset = ABS(TZFlag - 100) * 15; + int TZHour = TZOffset / 60; + int TZMinute = TZOffset - TZHour * 60; + if( OGR_GET_MS(second) ) + pszRet = CPLStrdup(CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%06.3f%c%02d:%02d", + year, month, day, hour, minute, second, + (TZFlag > 100) ? '+' : '-', TZHour, TZMinute)); + else + pszRet = CPLStrdup(CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", + year, month, day, hour, minute, (int)second, + (TZFlag > 100) ? '+' : '-', TZHour, TZMinute)); + } + return pszRet; +} + +/************************************************************************/ +/* OGRGetXML_UTF8_EscapedString() */ +/************************************************************************/ + +char* OGRGetXML_UTF8_EscapedString(const char* pszString) +{ + char *pszEscaped; + if (!CPLIsUTF8(pszString, -1) && + CSLTestBoolean(CPLGetConfigOption("OGR_FORCE_ASCII", "YES"))) + { + static int bFirstTime = TRUE; + if (bFirstTime) + { + bFirstTime = FALSE; + CPLError(CE_Warning, CPLE_AppDefined, + "%s is not a valid UTF-8 string. Forcing it to ASCII.\n" + "If you still want the original string and change the XML file encoding\n" + "afterwards, you can define OGR_FORCE_ASCII=NO as configuration option.\n" + "This warning won't be issued anymore", pszString); + } + else + { + CPLDebug("OGR", "%s is not a valid UTF-8 string. Forcing it to ASCII", + pszString); + } + char* pszTemp = CPLForceToASCII(pszString, -1, '?'); + pszEscaped = CPLEscapeString( pszTemp, -1, CPLES_XML ); + CPLFree(pszTemp); + } + else + pszEscaped = CPLEscapeString( pszString, -1, CPLES_XML ); + return pszEscaped; +} + +/************************************************************************/ +/* OGRCompareDate() */ +/************************************************************************/ + +int OGRCompareDate( OGRField *psFirstTuple, + OGRField *psSecondTuple ) +{ + /* FIXME? : We ignore TZFlag */ + + if (psFirstTuple->Date.Year < psSecondTuple->Date.Year) + return -1; + else if (psFirstTuple->Date.Year > psSecondTuple->Date.Year) + return 1; + + if (psFirstTuple->Date.Month < psSecondTuple->Date.Month) + return -1; + else if (psFirstTuple->Date.Month > psSecondTuple->Date.Month) + return 1; + + if (psFirstTuple->Date.Day < psSecondTuple->Date.Day) + return -1; + else if (psFirstTuple->Date.Day > psSecondTuple->Date.Day) + return 1; + + if (psFirstTuple->Date.Hour < psSecondTuple->Date.Hour) + return -1; + else if (psFirstTuple->Date.Hour > psSecondTuple->Date.Hour) + return 1; + + if (psFirstTuple->Date.Minute < psSecondTuple->Date.Minute) + return -1; + else if (psFirstTuple->Date.Minute > psSecondTuple->Date.Minute) + return 1; + + if (psFirstTuple->Date.Second < psSecondTuple->Date.Second) + return -1; + else if (psFirstTuple->Date.Second > psSecondTuple->Date.Second) + return 1; + + return 0; +} + +/************************************************************************/ +/* OGRFastAtof() */ +/************************************************************************/ + +/* On Windows, CPLAtof() is very slow if the number */ +/* is followed by other long content. */ +/* So we just extract the number into a short string */ +/* before calling CPLAtof() on it */ +static +double OGRCallAtofOnShortString(const char* pszStr) +{ + char szTemp[128]; + int nCounter = 0; + const char* p = pszStr; + while(*p == ' ' || *p == '\t') + p++; + while(*p == '+' || + *p == '-' || + (*p >= '0' && *p <= '9') || + *p == '.' || + (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D')) + { + szTemp[nCounter++] = *(p++); + if (nCounter == 127) + return CPLAtof(pszStr); + } + szTemp[nCounter] = '\0'; + return CPLAtof(szTemp); +} + +/** Same contract as CPLAtof, except than it doesn't always call the + * system CPLAtof() that may be slow on some platforms. For simple but + * common strings, it'll use a faster implementation (up to 20x faster + * than CPLAtof() on MS runtime libraries) that has no garanty to return + * exactly the same floating point number. + */ + +double OGRFastAtof(const char* pszStr) +{ + double dfVal = 0; + double dfSign = 1.0; + const char* p = pszStr; + + static const double adfTenPower[] = + { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, + 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, + 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31 + }; + + while(*p == ' ' || *p == '\t') + p++; + + if (*p == '+') + p++; + else if (*p == '-') + { + dfSign = -1.0; + p++; + } + + while(TRUE) + { + if (*p >= '0' && *p <= '9') + { + dfVal = dfVal * 10.0 + (*p - '0'); + p++; + } + else if (*p == '.') + { + p++; + break; + } + else if (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D') + return OGRCallAtofOnShortString(pszStr); + else + return dfSign * dfVal; + } + + unsigned int countFractionnal = 0; + while(TRUE) + { + if (*p >= '0' && *p <= '9') + { + dfVal = dfVal * 10.0 + (*p - '0'); + countFractionnal ++; + p++; + } + else if (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D') + return OGRCallAtofOnShortString(pszStr); + else + { + if (countFractionnal < sizeof(adfTenPower) / sizeof(adfTenPower[0])) + return dfSign * (dfVal / adfTenPower[countFractionnal]); + else + return OGRCallAtofOnShortString(pszStr); + } + } +} + +/** + * Check that panPermutation is a permutation of [0,nSize-1]. + * @param panPermutation an array of nSize elements. + * @param nSize size of the array. + * @return OGRERR_NONE if panPermutation is a permutation of [0,nSize-1]. + * @since OGR 1.9.0 + */ +OGRErr OGRCheckPermutation(int* panPermutation, int nSize) +{ + OGRErr eErr = OGRERR_NONE; + int* panCheck = (int*)CPLCalloc(nSize, sizeof(int)); + int i; + for(i=0;i= nSize) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Bad value for element %d", i); + eErr = OGRERR_FAILURE; + break; + } + if (panCheck[panPermutation[i]] != 0) + { + CPLError(CE_Failure, CPLE_IllegalArg, + "Array is not a permutation of [0,%d]", + nSize - 1); + eErr = OGRERR_FAILURE; + break; + } + panCheck[panPermutation[i]] = 1; + } + CPLFree(panCheck); + return eErr; +} + + +OGRErr OGRReadWKBGeometryType( unsigned char * pabyData, OGRwkbVariant eWkbVariant, + OGRwkbGeometryType *peGeometryType, OGRBoolean *pbIs3D ) +{ + if ( ! (peGeometryType && pbIs3D) ) + return OGRERR_FAILURE; + +/* -------------------------------------------------------------------- */ +/* Get the byte order byte. */ +/* -------------------------------------------------------------------- */ + OGRwkbByteOrder eByteOrder = DB2_V72_FIX_BYTE_ORDER((OGRwkbByteOrder) *pabyData); + if (!( eByteOrder == wkbXDR || eByteOrder == wkbNDR )) + return OGRERR_CORRUPT_DATA; + +/* -------------------------------------------------------------------- */ +/* Get the geometry feature type. For now we assume that */ +/* geometry type is between 0 and 255 so we only have to fetch */ +/* one byte. */ +/* -------------------------------------------------------------------- */ + int bIs3D = FALSE; + int iRawType; + + memcpy(&iRawType, pabyData + 1, 4); + if ( OGR_SWAP(eByteOrder)) + { + CPL_SWAP32PTR(&iRawType); + } + + /* Old-style OGC z-bit is flipped? */ + if ( wkb25DBitInternalUse & iRawType ) + { + /* Clean off top 3 bytes */ + iRawType &= 0x000000FF; + bIs3D = TRUE; + } + + /* ISO SQL/MM style Z types (between 1001 and 1012)? */ + if ( iRawType >= 1001 && iRawType <= (int)wkbMultiSurfaceZ ) + { + /* Remove the ISO padding */ + iRawType -= 1000; + bIs3D = TRUE; + } + + /* ISO SQL/MM Part3 draft -> Deprecated */ + /* See http://jtc1sc32.org/doc/N1101-1150/32N1107-WD13249-3--spatial.pdf */ + if (iRawType == 1000001) + iRawType = wkbCircularString; + else if (iRawType == 1000002) + iRawType = wkbCompoundCurve; + else if (iRawType == 1000003) + iRawType = wkbCurvePolygon; + else if (iRawType == 1000004) + iRawType = wkbMultiCurve; + else if (iRawType == 1000005) + iRawType = wkbMultiSurface; + else if (iRawType == 3000001) + { + iRawType = wkbPoint; + bIs3D = TRUE; + } + else if (iRawType == 3000002) + { + iRawType = wkbLineString; + bIs3D = TRUE; + } + else if (iRawType == 3000003) + { + iRawType = wkbCircularString; + bIs3D = TRUE; + } + else if (iRawType == 3000004) + { + iRawType = wkbCompoundCurve; + bIs3D = TRUE; + } + else if (iRawType == 3000005) + { + iRawType = wkbPolygon; + bIs3D = TRUE; + } + else if (iRawType == 3000006) + { + iRawType = wkbCurvePolygon; + bIs3D = TRUE; + } + else if (iRawType == 3000007) + { + iRawType = wkbMultiPoint; + bIs3D = TRUE; + } + else if (iRawType == 3000008) + { + iRawType = wkbMultiCurve; + bIs3D = TRUE; + } + else if (iRawType == 3000009) + { + iRawType = wkbMultiLineString; + bIs3D = TRUE; + } + else if (iRawType == 3000010) + { + iRawType = wkbMultiSurface; + bIs3D = TRUE; + } + else if (iRawType == 3000011) + { + iRawType = wkbMultiPolygon; + bIs3D = TRUE; + } + else if (iRawType == 3000012) + { + iRawType = wkbGeometryCollection; + bIs3D = TRUE; + } + + /* Sometimes the Z flag is in the 2nd byte? */ + if ( iRawType & (wkb25DBitInternalUse >> 16) ) + { + /* Clean off top 3 bytes */ + iRawType &= 0x000000FF; + bIs3D = TRUE; + } + + if( eWkbVariant == wkbVariantPostGIS1 ) + { + if( iRawType == POSTGIS15_CURVEPOLYGON ) + iRawType = wkbCurvePolygon; + else if( iRawType == POSTGIS15_MULTICURVE ) + iRawType = wkbMultiCurve; + else if( iRawType == POSTGIS15_MULTISURFACE ) + iRawType = wkbMultiSurface; + } + + /* Nothing left but (hopefully) basic 2D types */ + + /* What if what we have is still out of range? */ + if ( iRawType < 1 || iRawType > (int)wkbMultiSurface ) + { + CPLError(CE_Failure, CPLE_NotSupported, "Unsupported WKB type %d", iRawType); + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + + *pbIs3D = bIs3D; + *peGeometryType = (OGRwkbGeometryType)iRawType; + + return OGRERR_NONE; +} diff --git a/bazaar/plugin/gdal/ogr/osr_cs_wkt.c b/bazaar/plugin/gdal/ogr/osr_cs_wkt.c new file mode 100644 index 000000000..f84d4bf88 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/osr_cs_wkt.c @@ -0,0 +1,211 @@ +/****************************************************************************** + * $Id: osr_cs_wkt.c 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: CS WKT parser + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include + +#define EQUALN_CST(x,y) (strncmp(x, y, strlen(y)) == 0) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + +#include "osr_cs_wkt.h" + +#include "cpl_port.h" + +/************************************************************************/ +/* osr_cs_wkt_error() */ +/************************************************************************/ + +void osr_cs_wkt_error( osr_cs_wkt_parse_context *context, const char *msg ) +{ + int i, n; + char* szPtr; + sprintf(context->szErrorMsg, + "Parsing error : %s. Error occured around:\n", msg ); + n = context->pszLastSuccess - context->pszInput; + + szPtr = context->szErrorMsg + strlen(context->szErrorMsg); + for( i = MAX(0,n-40); i < n + 40 && context->pszInput[i]; i ++ ) + *(szPtr ++) = context->pszInput[i]; + *(szPtr ++) = '\n'; + for(i=0;ipszNext; + +/* -------------------------------------------------------------------- */ +/* Skip white space. */ +/* -------------------------------------------------------------------- */ + while( *pszInput == ' ' || *pszInput == '\t' + || *pszInput == 10 || *pszInput == 13 ) + pszInput++; + + context->pszLastSuccess = pszInput; + + if( *pszInput == '\0' ) + { + context->pszNext = pszInput; + return EOF; + } + +/* -------------------------------------------------------------------- */ +/* Recognize node names. */ +/* -------------------------------------------------------------------- */ + for(i = 0; i < sizeof(tokens) / sizeof(tokens[0]); i++) + { + if( EQUALN_CST(pszInput, tokens[i].pszToken) ) + { + context->pszNext = pszInput + strlen(tokens[i].pszToken); + return tokens[i].nTokenVal; + } + } + +/* -------------------------------------------------------------------- */ +/* Recognize double quoted strings. */ +/* -------------------------------------------------------------------- */ + if( *pszInput == '"' ) + { + pszInput ++; + while( *pszInput != '\0' && *pszInput != '"' ) + pszInput ++; + if( *pszInput == '\0' ) + { + context->pszNext = pszInput; + return EOF; + } + context->pszNext = pszInput + 1; + return T_STRING; + } + +/* -------------------------------------------------------------------- */ +/* Recognize numerical values. */ +/* -------------------------------------------------------------------- */ + + if( ((*pszInput == '-' || *pszInput == '+') && + pszInput[1] >= '0' && pszInput[1] <= '9' ) || + (*pszInput >= '0' && *pszInput <= '9') ) + { + if( *pszInput == '-' || *pszInput == '+' ) + pszInput ++; + + // collect non-decimal part of number + while( *pszInput >= '0' && *pszInput <= '9' ) + pszInput++; + + // collect decimal places. + if( *pszInput == '.' ) + { + pszInput++; + while( *pszInput >= '0' && *pszInput <= '9' ) + pszInput++; + } + + // collect exponent + if( *pszInput == 'e' || *pszInput == 'E' ) + { + pszInput++; + if( *pszInput == '-' || *pszInput == '+' ) + pszInput++; + while( *pszInput >= '0' && *pszInput <= '9' ) + pszInput++; + } + + context->pszNext = pszInput; + + return T_NUMBER; + } + +/* -------------------------------------------------------------------- */ +/* Recognize identifiers. */ +/* -------------------------------------------------------------------- */ + if( (*pszInput >= 'A' && *pszInput <= 'Z') || + (*pszInput >= 'a' && *pszInput <= 'z')) + { + pszInput ++; + while( (*pszInput >= 'A' && *pszInput <= 'Z') || + (*pszInput >= 'a' && *pszInput <= 'z') ) + pszInput ++; + context->pszNext = pszInput; + return T_IDENTIFIER; + } + +/* -------------------------------------------------------------------- */ +/* Handle special tokens. */ +/* -------------------------------------------------------------------- */ + context->pszNext = pszInput+1; + return *pszInput; +} diff --git a/bazaar/plugin/gdal/ogr/osr_cs_wkt.h b/bazaar/plugin/gdal/ogr/osr_cs_wkt.h new file mode 100644 index 000000000..4e3decc3e --- /dev/null +++ b/bazaar/plugin/gdal/ogr/osr_cs_wkt.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * $Id: osr_cs_wkt.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: CS WKT parser + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _OSR_CS_WKT_H_INCLUDED_ +#define _OSR_CS_WKT_H_INCLUDED_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + const char *pszInput; + const char *pszLastSuccess; + const char *pszNext; + char szErrorMsg[512]; +} osr_cs_wkt_parse_context; + +#include "osr_cs_wkt_parser.h" + +void osr_cs_wkt_error( osr_cs_wkt_parse_context *context, const char *msg ); +int osr_cs_wkt_lex(YYSTYPE* pNode, osr_cs_wkt_parse_context *context); +int osr_cs_wkt_parse(osr_cs_wkt_parse_context *context); + +#ifdef __cplusplus +} +#endif + +#endif /* _OSR_CS_WKT_H_INCLUDED_ */ diff --git a/bazaar/plugin/gdal/ogr/osr_cs_wkt_grammar.y b/bazaar/plugin/gdal/ogr/osr_cs_wkt_grammar.y new file mode 100644 index 000000000..a2e939d62 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/osr_cs_wkt_grammar.y @@ -0,0 +1,301 @@ +%{ +/****************************************************************************** + * $Id: osr_cs_wkt_grammar.y 27975 2014-11-17 12:37:48Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: CS WKT parser grammar + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013 Even Rouault, + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "osr_cs_wkt.h" + +%} + +%define api.pure +/* if the next %define is commented out, Bison 2.4 should be sufficient */ +/* but will produce less prettier error messages */ +%define parse.error verbose +%require "3.0" + +%parse-param {osr_cs_wkt_parse_context *context} +%lex-param {osr_cs_wkt_parse_context *context} + +%token T_PARAM_MT "PARAM_MT" +%token T_CONCAT_MT "CONCAT_MT" +%token T_INVERSE_MT "INVERSE_MT" +%token T_PASSTHROUGH_MT "PASSTHROUGH_MT" +%token T_PROJCS "PROJCS" +%token T_PROJECTION "PROJECTION" +%token T_GEOGCS "GEOGCS" +%token T_DATUM "DATUM" +%token T_SPHEROID "SPHEROID" +%token T_PRIMEM "PRIMEM" +%token T_UNIT "UNIT" +%token T_GEOCCS "GEOCCS" +%token T_AUTHORITY "AUTHORITY" +%token T_VERT_CS "VERT_CS" +%token T_VERT_DATUM "VERT_DATUM" +%token T_COMPD_CS "COMPD_CS" +%token T_AXIS "AXIS" +%token T_TOWGS84 "TOWGS84" +%token T_FITTED_CS "FITTED_CS" +%token T_LOCAL_CS "LOCAL_CS" +%token T_LOCAL_DATUM "LOCAL_DATUM" +%token T_PARAMETER "PARAMETER" + +%token T_EXTENSION "EXTENSION" + +%token T_STRING "string" +%token T_NUMBER "number" +%token T_IDENTIFIER "identifier" + +%token END 0 "end of string" + +%% + +input: + coordinate_system + +/* Derived from BNF grammar in OGC 01-009 OpenGIS Implementation */ +/* Coordinate Transformation Services Revision 1.00 */ +/* with the following additions : */ +/* - accept an EXTENSION node at the end of GEOGCS, PROJCS, COMPD_CS, VERT_DATUM */ +/* - accept 3 parameters in TOWGS84 */ + +/* 7.1 Math Transform WKT */ + +begin_node: + '[' + +begin_node_name: + begin_node T_STRING + +end_node: + ']' + +math_transform: + param_mt | concat_mt | inv_mt | passthrough_mt + +param_mt: + T_PARAM_MT begin_node_name opt_parameter_list end_node + +parameter: + T_PARAMETER begin_node_name ',' T_NUMBER end_node + +opt_parameter_list: + ',' parameter + | ',' parameter opt_parameter_list + +concat_mt: + T_CONCAT_MT begin_node math_transform opt_math_transform_list end_node + +opt_math_transform_list: + | math_transform + | math_transform ',' opt_math_transform_list + +inv_mt: + T_INVERSE_MT begin_node math_transform end_node + +passthrough_mt: + T_PASSTHROUGH_MT begin_node integer ',' math_transform end_node + +/* FIXME */ +integer: + T_NUMBER + +/* 7.2 Coordinate System WKT */ + +coordinate_system: + horz_cs | geocentric_cs | vert_cs | compd_cs | fitted_cs | local_cs + +horz_cs: + geographic_cs | projected_cs + +/* opt_extension is an extension of the CT spec */ +projected_cs: + T_PROJCS begin_node_name ',' geographic_cs ',' projection ',' + opt_parameter_list_linear_unit opt_twin_axis_extension_authority end_node + +opt_parameter_list_linear_unit: + linear_unit + | parameter_list_linear_unit + +parameter_list_linear_unit: + parameter ',' parameter_list_linear_unit + | parameter ',' linear_unit + +opt_twin_axis_extension_authority: + | ',' twin_axis opt_extension_authority + | ',' extension opt_authority + | ',' authority + +opt_authority: + | ',' authority + +extension: + T_EXTENSION begin_node_name ',' T_STRING end_node + +projection: + T_PROJECTION begin_node_name opt_authority end_node + +geographic_cs: + T_GEOGCS begin_node_name',' datum ',' prime_meridian ',' + angular_unit opt_twin_axis_extension_authority end_node + +datum: + T_DATUM begin_node_name ',' spheroid opt_towgs84_authority_extension end_node + +opt_towgs84_authority_extension: + | ',' towgs84 opt_extension_authority + | ',' extension opt_authority + | ',' authority + +spheroid: + T_SPHEROID begin_node_name ',' semi_major_axis ',' + inverse_flattening opt_authority end_node + +semi_major_axis: + T_NUMBER + +inverse_flattening: + T_NUMBER + +prime_meridian: + T_PRIMEM begin_node_name ',' longitude opt_authority end_node + +longitude: + T_NUMBER + +angular_unit: + unit + +linear_unit: + unit + +unit: + T_UNIT begin_node_name ',' conversion_factor opt_authority end_node + +conversion_factor: + T_NUMBER + +geocentric_cs: + T_GEOCCS begin_node_name ',' datum ',' prime_meridian ',' + linear_unit opt_three_axis_authority end_node + +opt_three_axis_authority: + | ',' three_axis opt_authority + | ',' authority + +three_axis: + axis ',' axis ',' axis + +authority: + T_AUTHORITY begin_node_name ',' T_STRING end_node + +vert_cs: + T_VERT_CS begin_node_name ',' vert_datum ',' linear_unit opt_axis_authority end_node + +opt_axis_authority: + | ',' axis opt_authority + | ',' authority + +vert_datum: + T_VERT_DATUM begin_node_name ',' datum_type opt_extension_authority end_node + +opt_extension_authority: + | ',' extension opt_authority + | ',' authority + +datum_type: + T_NUMBER + +compd_cs: + T_COMPD_CS begin_node_name ',' head_cs ',' tail_cs opt_extension_authority end_node + +head_cs: + coordinate_system + +tail_cs: + coordinate_system + +twin_axis: axis ',' axis + +axis: + T_AXIS begin_node_name ',' T_IDENTIFIER end_node +/* Extension of the CT spec */ +/* | T_AXIS '[' T_STRING ',' T_STRING ']'*/ + +towgs84: + T_TOWGS84 begin_node towgs84_parameters end_node + +towgs84_parameters: + seven_parameters +/* Extension of the CT spec */ + | three_parameters + +three_parameters: + dx ',' dy ',' dz + +seven_parameters: + dx ',' dy ',' dz ',' ex ',' ey ',' ez ',' ppm + +dx: + T_NUMBER + +dy: + T_NUMBER + +dz: + T_NUMBER + +ex: + T_NUMBER + +ey: + T_NUMBER + +ez: + T_NUMBER + +ppm: + T_NUMBER + +fitted_cs: + T_FITTED_CS begin_node_name ',' to_base ',' base_cs end_node + +to_base: + math_transform + +base_cs: + coordinate_system + +local_cs: + T_LOCAL_CS begin_node_name ',' local_datum ',' unit ',' axis opt_axis_list_authority end_node + +opt_axis_list_authority: + | ',' authority + | ',' axis opt_axis_list_authority + +local_datum: + T_LOCAL_DATUM begin_node_name ',' datum_type opt_authority end_node diff --git a/bazaar/plugin/gdal/ogr/osr_cs_wkt_parser.c b/bazaar/plugin/gdal/ogr/osr_cs_wkt_parser.c new file mode 100644 index 000000000..68dacd37c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/osr_cs_wkt_parser.c @@ -0,0 +1,1641 @@ +/* A Bison parser, made by GNU Bison 3.0. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.0" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse osr_cs_wkt_parse +#define yylex osr_cs_wkt_lex +#define yyerror osr_cs_wkt_error +#define yydebug osr_cs_wkt_debug +#define yynerrs osr_cs_wkt_nerrs + + +/* Copy the first part of user declarations. */ + + +/****************************************************************************** + * $Id: osr_cs_wkt_parser.c 27975 2014-11-17 12:37:48Z rouault $ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: CS WKT parser grammar + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013 Even Rouault, + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "osr_cs_wkt.h" + + + + +# ifndef YY_NULL +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULL nullptr +# else +# define YY_NULL 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* In a future release of Bison, this section will be replaced + by #include "osr_cs_wkt_parser.h". */ +#ifndef YY_OSR_CS_WKT_OSR_CS_WKT_PARSER_H_INCLUDED +# define YY_OSR_CS_WKT_OSR_CS_WKT_PARSER_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int osr_cs_wkt_debug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + END = 0, + T_PARAM_MT = 258, + T_CONCAT_MT = 259, + T_INVERSE_MT = 260, + T_PASSTHROUGH_MT = 261, + T_PROJCS = 262, + T_PROJECTION = 263, + T_GEOGCS = 264, + T_DATUM = 265, + T_SPHEROID = 266, + T_PRIMEM = 267, + T_UNIT = 268, + T_GEOCCS = 269, + T_AUTHORITY = 270, + T_VERT_CS = 271, + T_VERT_DATUM = 272, + T_COMPD_CS = 273, + T_AXIS = 274, + T_TOWGS84 = 275, + T_FITTED_CS = 276, + T_LOCAL_CS = 277, + T_LOCAL_DATUM = 278, + T_PARAMETER = 279, + T_EXTENSION = 280, + T_STRING = 281, + T_NUMBER = 282, + T_IDENTIFIER = 283 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int osr_cs_wkt_parse (osr_cs_wkt_parse_context *context); + +#endif /* !YY_OSR_CS_WKT_OSR_CS_WKT_PARSER_H_INCLUDED */ + +/* Copy the second part of user declarations. */ + + + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if (! defined __GNUC__ || __GNUC__ < 2 \ + || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)) +# define __attribute__(Spec) /* empty */ +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 27 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 197 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 32 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 66 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 96 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 252 + +/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned + by yylex, with out-of-bounds checking. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 283 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, without out-of-bounds checking. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 31, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 29, 2, 30, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 78, 78, 89, 92, 95, 98, 98, 98, 98, + 101, 104, 107, 108, 111, 113, 114, 115, 118, 121, + 125, 130, 130, 130, 130, 130, 130, 133, 133, 137, + 141, 142, 145, 146, 148, 149, 150, 151, 153, 154, + 157, 160, 163, 167, 169, 170, 171, 172, 175, 179, + 182, 185, 188, 191, 194, 197, 200, 203, 206, 207, + 208, 211, 214, 217, 219, 220, 221, 224, 226, 227, + 228, 231, 234, 237, 240, 242, 245, 250, 253, 255, + 258, 261, 264, 267, 270, 273, 276, 279, 282, 285, + 288, 291, 294, 296, 297, 298, 301 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of string\"", "error", "$undefined", "\"PARAM_MT\"", + "\"CONCAT_MT\"", "\"INVERSE_MT\"", "\"PASSTHROUGH_MT\"", "\"PROJCS\"", + "\"PROJECTION\"", "\"GEOGCS\"", "\"DATUM\"", "\"SPHEROID\"", + "\"PRIMEM\"", "\"UNIT\"", "\"GEOCCS\"", "\"AUTHORITY\"", "\"VERT_CS\"", + "\"VERT_DATUM\"", "\"COMPD_CS\"", "\"AXIS\"", "\"TOWGS84\"", + "\"FITTED_CS\"", "\"LOCAL_CS\"", "\"LOCAL_DATUM\"", "\"PARAMETER\"", + "\"EXTENSION\"", "\"string\"", "\"number\"", "\"identifier\"", "'['", + "']'", "','", "$accept", "input", "begin_node", "begin_node_name", + "end_node", "math_transform", "param_mt", "parameter", + "opt_parameter_list", "concat_mt", "opt_math_transform_list", "inv_mt", + "passthrough_mt", "integer", "coordinate_system", "horz_cs", + "projected_cs", "opt_parameter_list_linear_unit", + "parameter_list_linear_unit", "opt_twin_axis_extension_authority", + "opt_authority", "extension", "projection", "geographic_cs", "datum", + "opt_towgs84_authority_extension", "spheroid", "semi_major_axis", + "inverse_flattening", "prime_meridian", "longitude", "angular_unit", + "linear_unit", "unit", "conversion_factor", "geocentric_cs", + "opt_three_axis_authority", "three_axis", "authority", "vert_cs", + "opt_axis_authority", "vert_datum", "opt_extension_authority", + "datum_type", "compd_cs", "head_cs", "tail_cs", "twin_axis", "axis", + "towgs84", "towgs84_parameters", "three_parameters", "seven_parameters", + "dx", "dy", "dz", "ex", "ey", "ez", "ppm", "fitted_cs", "to_base", + "base_cs", "local_cs", "opt_axis_list_authority", "local_datum", YY_NULL +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 91, + 93, 44 +}; +# endif + +#define YYPACT_NINF -114 + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-114))) + +#define YYTABLE_NINF -1 + +#define yytable_value_is_error(Yytable_value) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 131, -2, -2, -2, -2, -2, -2, -2, 28, -114, + -114, -114, -114, -114, -114, -114, -114, -114, -114, 10, + 19, 21, 24, 33, 35, 37, 40, -114, -114, 63, + 65, 65, 56, 131, 91, 59, 53, -2, 54, 55, + -2, 57, -114, 58, -2, -2, -2, -2, -114, -114, + -114, -114, -114, 68, -2, 69, 93, 72, 92, 92, + 75, 94, 131, 77, 91, 91, 82, 131, 81, 94, + -2, 83, 102, -2, 85, 87, 95, -2, 88, -114, + -114, 89, 101, 96, 91, 96, -114, 98, -114, 96, + 95, 100, 104, 11, -2, 108, 112, 94, 94, -114, + 89, 113, 38, 96, 14, 96, -2, 77, -114, -114, + 117, 96, -114, 91, -114, 104, 132, 135, 96, 123, + 124, -114, -114, 125, 31, 96, 130, 124, -114, 127, + 96, 133, -2, -2, -114, 104, -114, -2, 104, -114, + -114, 128, -114, 91, -114, 96, 96, 134, -114, -114, + 11, 0, 96, 136, -2, 104, -114, 89, -114, -114, + 104, 96, 38, 96, -114, -114, 104, 137, 138, -114, + 139, -114, 140, -114, -114, -114, 38, 96, -114, -114, + 104, -114, 89, 141, -114, -114, 142, 144, -114, -114, + 96, -114, 104, -114, 143, -114, 96, 149, 99, 150, + 96, -114, 134, -114, -114, -114, 132, 151, -114, 96, + -114, -114, 146, -114, -114, 132, -114, 96, 96, 96, + -114, -114, -114, -114, 104, -114, 152, 153, -114, -114, + -114, 96, -114, 154, 132, -114, 155, -114, -114, 156, + 159, -114, 157, 162, -114, 160, 163, -114, 161, 166, + -114, -114 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 21, 28, 27, 22, 23, 24, 25, 26, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 73, 0, 0, 0, 0, 0, 90, 6, + 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 64, 54, + 74, 68, 0, 0, 15, 0, 20, 0, 91, 0, + 0, 0, 38, 0, 0, 44, 0, 0, 0, 71, + 68, 0, 0, 0, 0, 0, 0, 12, 5, 10, + 16, 0, 18, 0, 89, 38, 0, 0, 0, 0, + 34, 31, 30, 0, 0, 0, 0, 34, 53, 58, + 0, 0, 0, 0, 66, 38, 63, 0, 38, 70, + 72, 0, 13, 15, 14, 0, 0, 93, 39, 41, + 0, 0, 0, 0, 0, 38, 47, 68, 43, 52, + 38, 0, 0, 0, 67, 56, 38, 0, 0, 65, + 0, 69, 0, 17, 19, 96, 0, 0, 32, 33, + 38, 37, 68, 0, 29, 49, 0, 0, 46, 45, + 0, 42, 38, 60, 0, 57, 0, 0, 0, 0, + 0, 94, 93, 92, 36, 35, 0, 0, 82, 0, + 79, 78, 0, 51, 59, 0, 55, 0, 0, 0, + 11, 95, 75, 50, 38, 77, 0, 0, 62, 76, + 40, 0, 83, 0, 0, 48, 0, 61, 84, 80, + 0, 85, 0, 0, 86, 0, 0, 87, 0, 0, + 88, 81 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -114, -114, -24, 4, -85, -33, -114, 79, 73, -114, + 23, -114, -114, -114, -20, -114, -114, -114, 12, 67, + -101, -108, -114, 167, 164, -114, -114, -114, -114, 105, + -114, -114, -81, -67, -114, -114, -114, -114, -34, -114, + -114, -114, -95, 107, -114, -114, -114, -114, -113, -114, + -114, -114, -114, -114, -114, -114, -114, -114, -114, -114, + -114, -114, -114, -114, -21, -114 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 8, 19, 20, 109, 110, 49, 119, 83, 50, + 111, 51, 52, 87, 9, 10, 11, 120, 121, 152, + 118, 138, 71, 12, 38, 125, 95, 186, 224, 74, + 160, 127, 78, 79, 166, 13, 163, 192, 134, 14, + 103, 41, 105, 100, 15, 43, 81, 182, 135, 157, + 209, 210, 211, 212, 233, 239, 242, 245, 248, 251, + 16, 53, 89, 17, 177, 55 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_uint8 yytable[] = +{ + 112, 48, 91, 147, 114, 130, 21, 22, 23, 24, + 25, 26, 122, 42, 146, 132, 155, 129, 136, 133, + 140, 64, 65, 66, 77, 137, 144, 18, 27, 132, + 128, 84, 85, 149, 169, 106, 28, 171, 183, 137, + 158, 57, 80, 180, 60, 164, 132, 88, 63, 194, + 29, 154, 30, 132, 188, 31, 137, 133, 68, 190, + 174, 175, 189, 202, 32, 196, 33, 184, 34, 179, + 139, 35, 2, 40, 92, 37, 191, 96, 195, 204, + 145, 101, 54, 148, 56, 58, 59, 205, 61, 62, + 156, 214, 203, 222, 44, 45, 46, 47, 123, 67, + 69, 70, 227, 72, 73, 213, 76, 77, 82, 86, + 141, 216, 90, 94, 93, 220, 97, 181, 98, 102, + 104, 237, 99, 231, 225, 106, 108, 218, 193, 113, + 187, 116, 228, 229, 230, 117, 167, 168, 1, 124, + 2, 170, 201, 126, 131, 3, 235, 4, 143, 5, + 132, 133, 6, 7, 150, 151, 153, 159, 162, 172, + 165, 107, 178, 185, 75, 176, 173, 200, 197, 198, + 199, 208, 206, 207, 215, 217, 219, 226, 223, 232, + 142, 221, 238, 0, 234, 236, 241, 240, 243, 244, + 247, 246, 249, 250, 161, 39, 36, 115 +}; + +static const yytype_int16 yycheck[] = +{ + 85, 34, 69, 116, 89, 100, 2, 3, 4, 5, + 6, 7, 93, 33, 115, 15, 124, 98, 103, 19, + 105, 45, 46, 47, 13, 25, 111, 29, 0, 15, + 97, 64, 65, 118, 135, 24, 26, 138, 151, 25, + 125, 37, 62, 151, 40, 130, 15, 67, 44, 162, + 31, 20, 31, 15, 155, 31, 25, 19, 54, 160, + 145, 146, 157, 176, 31, 166, 31, 152, 31, 150, + 104, 31, 9, 17, 70, 10, 161, 73, 163, 180, + 113, 77, 23, 117, 31, 31, 31, 182, 31, 31, + 124, 192, 177, 206, 3, 4, 5, 6, 94, 31, + 31, 8, 215, 31, 12, 190, 31, 13, 31, 27, + 106, 196, 31, 11, 31, 200, 31, 151, 31, 31, + 31, 234, 27, 224, 209, 24, 30, 28, 162, 31, + 154, 31, 217, 218, 219, 31, 132, 133, 7, 31, + 9, 137, 176, 31, 31, 14, 231, 16, 31, 18, + 15, 19, 21, 22, 31, 31, 31, 27, 31, 31, + 27, 82, 150, 27, 59, 31, 143, 27, 31, 31, + 31, 27, 31, 31, 31, 26, 26, 31, 27, 27, + 107, 202, 27, -1, 31, 31, 27, 31, 31, 27, + 27, 31, 31, 27, 127, 31, 29, 90 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 7, 9, 14, 16, 18, 21, 22, 33, 46, + 47, 48, 55, 67, 71, 76, 92, 95, 29, 34, + 35, 35, 35, 35, 35, 35, 35, 0, 26, 31, + 31, 31, 31, 31, 31, 31, 55, 10, 56, 56, + 17, 73, 46, 77, 3, 4, 5, 6, 37, 38, + 41, 43, 44, 93, 23, 97, 31, 35, 31, 31, + 35, 31, 31, 35, 34, 34, 34, 31, 35, 31, + 8, 54, 31, 12, 61, 61, 31, 13, 64, 65, + 46, 78, 31, 40, 37, 37, 27, 45, 46, 94, + 31, 65, 35, 31, 11, 58, 35, 31, 31, 27, + 75, 35, 31, 72, 31, 74, 24, 39, 30, 36, + 37, 42, 36, 31, 36, 75, 31, 31, 52, 39, + 49, 50, 64, 35, 31, 57, 31, 63, 65, 64, + 74, 31, 15, 19, 70, 80, 36, 25, 53, 70, + 36, 35, 40, 31, 36, 37, 52, 80, 70, 36, + 31, 31, 51, 31, 20, 53, 70, 81, 36, 27, + 62, 51, 31, 68, 36, 27, 66, 35, 35, 52, + 35, 52, 31, 42, 36, 36, 31, 96, 50, 64, + 53, 70, 79, 80, 36, 27, 59, 34, 52, 74, + 52, 36, 69, 70, 80, 36, 52, 31, 31, 31, + 27, 70, 80, 36, 52, 74, 31, 31, 27, 82, + 83, 84, 85, 36, 52, 31, 36, 26, 28, 26, + 36, 96, 80, 27, 60, 36, 31, 80, 36, 36, + 36, 52, 27, 86, 31, 36, 31, 80, 27, 87, + 31, 27, 88, 31, 27, 89, 31, 27, 90, 31, + 27, 91 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 32, 33, 34, 35, 36, 37, 37, 37, 37, + 38, 39, 40, 40, 41, 42, 42, 42, 43, 44, + 45, 46, 46, 46, 46, 46, 46, 47, 47, 48, + 49, 49, 50, 50, 51, 51, 51, 51, 52, 52, + 53, 54, 55, 56, 57, 57, 57, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 68, + 68, 69, 70, 71, 72, 72, 72, 73, 74, 74, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 96, 96, 97 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 1, 2, 1, 1, 1, 1, 1, + 4, 5, 2, 3, 5, 0, 1, 3, 4, 6, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 10, + 1, 1, 3, 3, 0, 3, 3, 2, 0, 2, + 5, 4, 10, 6, 0, 3, 3, 2, 8, 1, + 1, 6, 1, 1, 1, 6, 1, 10, 0, 3, + 2, 5, 5, 8, 0, 3, 2, 6, 0, 3, + 2, 1, 8, 1, 1, 3, 5, 4, 1, 1, + 5, 13, 1, 1, 1, 1, 1, 1, 1, 7, + 1, 1, 10, 0, 2, 3, 6 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*----------------------------------------. +| Print this symbol's value on YYOUTPUT. | +`----------------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, osr_cs_wkt_parse_context *context) +{ + FILE *yyo = yyoutput; + YYUSE (yyo); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + YYUSE (yytype); +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, osr_cs_wkt_parse_context *context) +{ + YYFPRINTF (yyoutput, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep, context); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, osr_cs_wkt_parse_context *context) +{ + unsigned long int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[yyssp[yyi + 1 - yynrhs]], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, context); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +yystrlen (const char *yystr) +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULL; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, osr_cs_wkt_parse_context *context) +{ + YYUSE (yyvaluep); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (osr_cs_wkt_parse_context *context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs; + + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, context); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, context); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} diff --git a/bazaar/plugin/gdal/ogr/osr_cs_wkt_parser.h b/bazaar/plugin/gdal/ogr/osr_cs_wkt_parser.h new file mode 100644 index 000000000..c4678054c --- /dev/null +++ b/bazaar/plugin/gdal/ogr/osr_cs_wkt_parser.h @@ -0,0 +1,89 @@ +/* A Bison parser, made by GNU Bison 3.0. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_OSR_CS_WKT_OSR_CS_WKT_PARSER_H_INCLUDED +# define YY_OSR_CS_WKT_OSR_CS_WKT_PARSER_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int osr_cs_wkt_debug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + END = 0, + T_PARAM_MT = 258, + T_CONCAT_MT = 259, + T_INVERSE_MT = 260, + T_PASSTHROUGH_MT = 261, + T_PROJCS = 262, + T_PROJECTION = 263, + T_GEOGCS = 264, + T_DATUM = 265, + T_SPHEROID = 266, + T_PRIMEM = 267, + T_UNIT = 268, + T_GEOCCS = 269, + T_AUTHORITY = 270, + T_VERT_CS = 271, + T_VERT_DATUM = 272, + T_COMPD_CS = 273, + T_AXIS = 274, + T_TOWGS84 = 275, + T_FITTED_CS = 276, + T_LOCAL_CS = 277, + T_LOCAL_DATUM = 278, + T_PARAMETER = 279, + T_EXTENSION = 280, + T_STRING = 281, + T_NUMBER = 282, + T_IDENTIFIER = 283 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int osr_cs_wkt_parse (osr_cs_wkt_parse_context *context); + +#endif /* !YY_OSR_CS_WKT_OSR_CS_WKT_PARSER_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/osr_tutorial.dox b/bazaar/plugin/gdal/ogr/osr_tutorial.dox new file mode 100644 index 000000000..4565afa08 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/osr_tutorial.dox @@ -0,0 +1,397 @@ +/*! \page osr_tutorial OGR Projections Tutorial + +\section osr_tutorial_intro Introduction + +The OGRSpatialReference, and OGRCoordinateTransformation classes provide +services to represent coordinate systems (projections and datums) and to +transform between them. These services are loosely modelled on the +OpenGIS Coordinate Transformations specification, and use the same +Well Known Text format for describing coordinate systems. + +Some background on OpenGIS coordinate systems and services can be found +in the Simple Features for COM, and Spatial Reference Systems Abstract Model +documents available from the Open Geospatial Consortium. +The GeoTIFF Projections Transform List +may also be of assistance in +understanding formulations of projections in WKT. The +EPSG +Geodesy web page is also a useful resource. + +\section osr_tutorial_cs Defining a Geographic Coordinate System + +Coordinate systems are encapsulated in the OGRSpatialReference class. There +are a number of ways of initializing an OGRSpatialReference object to a +valid coordinate system. There are two primary kinds of coordinate systems. +The first is geographic (positions are measured in long/lat) and the second +is projected (such as UTM - positions are measured in meters or feet). + +A Geographic coordinate system contains information on the datum (which implies +an spheroid described by a semi-major axis, and inverse flattening), prime +meridian (normally Greenwich), and an angular units type which is normally +degrees. The following code initializes a geographic coordinate system +on supplying all this information along with a user visible name for the +geographic coordinate system. + +\code + OGRSpatialReference oSRS; + + oSRS.SetGeogCS( "My geographic coordinate system", + "WGS_1984", + "My WGS84 Spheroid", + SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, + "Greenwich", 0.0, + "degree", SRS_UA_DEGREE_CONV ); +\endcode + +Of these values, the names "My geographic coordinate system", "My WGS84 +Spheroid", "Greenwich" and "degree" are not keys, but are used for display +to the user. However, the datum name "WGS_1984" is used as a key to identify +the datum, and there are rules on what values can be used. NOTE: Prepare +writeup somewhere on valid datums! + +The OGRSpatialReference has built in support for a few well known coordinate +systems, which include "NAD27", "NAD83", "WGS72" and "WGS84" which can be +defined in a single call to SetWellKnownGeogCS(). + +\code + oSRS.SetWellKnownGeogCS( "WGS84" ); +\endcode + +Furthermore, any geographic coordinate system in the EPSG database can +be set by it's GCS code number if the EPSG database is available. + +\code + oSRS.SetWellKnownGeogCS( "EPSG:4326" ); +\endcode + +For serialization, and transmission of projection definitions to other +packages, the OpenGIS Well Known Text format for coordinate systems is +used. An OGRSpatialReference can be initialized from well known text, or +converted back into well known text. + +\code + char *pszWKT = NULL; + + oSRS.SetWellKnownGeogCS( "WGS84" ); + oSRS.exportToWkt( &pszWKT ); + printf( "%s\n", pszWKT ); +\endcode + +gives something like: + +
    +GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,
    +AUTHORITY["EPSG",7030]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG",6326]],
    +PRIMEM["Greenwich",0,AUTHORITY["EPSG",8901]],UNIT["DMSH",0.0174532925199433,
    +AUTHORITY["EPSG",9108]],AXIS["Lat",NORTH],AXIS["Long",EAST],AUTHORITY["EPSG",
    +4326]]
    +
    + +or in more readable form: + +
    +GEOGCS["WGS 84",
    +    DATUM["WGS_1984",
    +        SPHEROID["WGS 84",6378137,298.257223563,
    +            AUTHORITY["EPSG",7030]],
    +        TOWGS84[0,0,0,0,0,0,0],
    +        AUTHORITY["EPSG",6326]],
    +    PRIMEM["Greenwich",0,AUTHORITY["EPSG",8901]],
    +    UNIT["DMSH",0.0174532925199433,AUTHORITY["EPSG",9108]],
    +    AXIS["Lat",NORTH],
    +    AXIS["Long",EAST],
    +    AUTHORITY["EPSG",4326]]
    +
    + +The OGRSpatialReference::importFromWkt() method can be used to set an +OGRSpatialReference from a WKT coordinate system definition. + +\section osr_tutorial_proj Defining a Projected Coordinate System + +A projected coordinate system (such as UTM, Lambert Conformal Conic, etc) +requires and underlying geographic coordinate system as well as a definition +for the projection transform used to translate between linear positions +(in meters or feet) and angular long/lat positions. The following code +defines a UTM zone 17 projected coordinate system with an underlying +geographic coordinate system (datum) of WGS84. + +\code + OGRSpatialReference oSRS; + + oSRS.SetProjCS( "UTM 17 (WGS84) in northern hemisphere." ); + oSRS.SetWellKnownGeogCS( "WGS84" ); + oSRS.SetUTM( 17, TRUE ); +\endcode + +Calling SetProjCS() sets a user +name for the projected coordinate system and establishes that the system +is projected. The SetWellKnownGeogCS() associates a geographic coordinate +system, and the SetUTM() call sets detailed projection transformation +parameters. At this time the above order is important in order to +create a valid definition, but in the future the object will automatically +reorder the internal representation as needed to remain valid. For now +be careful of the order of steps defining an OGRSpatialReference! + +The above definition would give a WKT version that looks something like +the following. Note that the UTM 17 was expanded into the details +transverse mercator definition of the UTM zone. + +
    +PROJCS["UTM 17 (WGS84) in northern hemisphere.",
    +    GEOGCS["WGS 84",
    +        DATUM["WGS_1984",
    +            SPHEROID["WGS 84",6378137,298.257223563,
    +                AUTHORITY["EPSG",7030]],
    +            TOWGS84[0,0,0,0,0,0,0],
    +            AUTHORITY["EPSG",6326]],
    +        PRIMEM["Greenwich",0,AUTHORITY["EPSG",8901]],
    +        UNIT["DMSH",0.0174532925199433,AUTHORITY["EPSG",9108]],
    +        AXIS["Lat",NORTH],
    +        AXIS["Long",EAST],
    +        AUTHORITY["EPSG",4326]],
    +    PROJECTION["Transverse_Mercator"],
    +    PARAMETER["latitude_of_origin",0],
    +    PARAMETER["central_meridian",-81],
    +    PARAMETER["scale_factor",0.9996],
    +    PARAMETER["false_easting",500000],
    +    PARAMETER["false_northing",0]]
    +
    + +There are methods for many projection methods including SetTM() (Transverse +Mercator), SetLCC() (Lambert Conformal Conic), and SetMercator(). + +\section osr_tutorial_query Querying Coordinate System + +Once an OGRSpatialReference has been established, various information about +it can be queried. It can be established if it is a projected or +geographic coordinate system using the OGRSpatialReference::IsProjected() and +OGRSpatialReference::IsGeographic() methods. The +OGRSpatialReference::GetSemiMajor(), OGRSpatialReference::GetSemiMinor() and +OGRSpatialReference::GetInvFlattening() methods can be used to get +information about the spheroid. The OGRSpatialReference::GetAttrValue() +method can be used to get the PROJCS, GEOGCS, DATUM, SPHEROID, and PROJECTION +names strings. The OGRSpatialReference::GetProjParm() method can be used to +get the projection parameters. The OGRSpatialReference::GetLinearUnits() +method can be used to fetch the linear units type, and translation to meters. + +The following code (from ogr_srs_proj4.cpp) demonstrates use +of GetAttrValue() to get the projection, and GetProjParm() to get projection +parameters. The GetAttrValue() method searches for the first "value" +node associated with the named entry in the WKT text representation. +The #define'ed constants for projection parameters (such as +SRS_PP_CENTRAL_MERIDIAN) should be used when fetching projection parameter +with GetProjParm(). The code for the Set methods of the various projections +in ogrspatialreference.cpp can be consulted to find which parameters apply to +which projections. + +\code + const char *pszProjection = poSRS->GetAttrValue("PROJECTION"); + + if( pszProjection == NULL ) + { + if( poSRS->IsGeographic() ) + sprintf( szProj4+strlen(szProj4), "+proj=longlat " ); + else + sprintf( szProj4+strlen(szProj4), "unknown " ); + } + else if( EQUAL(pszProjection,SRS_PT_CYLINDRICAL_EQUAL_AREA) ) + { + sprintf( szProj4+strlen(szProj4), + "+proj=cea +lon_0=%.9f +lat_ts=%.9f +x_0=%.3f +y_0=%.3f ", + poSRS->GetProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), + poSRS->GetProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), + poSRS->GetProjParm(SRS_PP_FALSE_EASTING,0.0), + poSRS->GetProjParm(SRS_PP_FALSE_NORTHING,0.0) ); + } + ... +\endcode + +\section osr_tutorial_transform Coordinate Transformation + +The OGRCoordinateTransformation class is used for translating positions +between different coordinate systems. New transformation objects are +created using OGRCreateCoordinateTransformation(), and then the +OGRCoordinateTransformation::Transform() method can be used to convert +points between coordinate systems. + +\code + OGRSpatialReference oSourceSRS, oTargetSRS; + OGRCoordinateTransformation *poCT; + double x, y; + + oSourceSRS.importFromEPSG( atoi(papszArgv[i+1]) ); + oTargetSRS.importFromEPSG( atoi(papszArgv[i+2]) ); + + poCT = OGRCreateCoordinateTransformation( &oSourceSRS, + &oTargetSRS ); + x = atof( papszArgv[i+3] ); + y = atof( papszArgv[i+4] ); + + if( poCT == NULL || !poCT->Transform( 1, &x, &y ) ) + printf( "Transformation failed.\n" ); + else + printf( "(%f,%f) -> (%f,%f)\n", + atof( papszArgv[i+3] ), + atof( papszArgv[i+4] ), + x, y ); +\endcode + +There are a couple of points at which transformations can +fail. First, OGRCreateCoordinateTransformation() may fail, +generally because the internals recognise that no transformation +between the indicated systems can be established. This might +be due to use of a projection not supported by the internal +PROJ.4 library, differing datums for which no relationship +is known, or one of the coordinate systems being inadequately +defined. If OGRCreateCoordinateTransformation() fails it will +return a NULL. + +The OGRCoordinateTransformation::Transform() method itself can +also fail. This may be as a delayed result of one of the above +problems, or as a result of an operation being numerically +undefined for one or more of the passed in points. The +Transform() function will return TRUE on success, or FALSE +if any of the points fail to transform. The point array is +left in an indeterminate state on error. + +Though not shown above, the coordinate transformation service can +take 3D points, and will adjust elevations for elevation differents +in spheroids, and datums. At some point in the future shifts +between different vertical datums may also be applied. If no Z is +passed, it is assume that the point is on the geoide. + +The following example shows how to conveniently create a lat/long coordinate +system using the same geographic coordinate system as a projected coordinate +system, and using that to transform between projected coordinates and +lat/long. + +\code + OGRSpatialReference oUTM, *poLatLong; + OGRCoordinateTransformation *poTransform; + + oUTM.SetProjCS("UTM 17 / WGS84"); + oUTM.SetWellKnownGeogCS( "WGS84" ); + oUTM.SetUTM( 17 ); + + poLatLong = oUTM.CloneGeogCS(); + + poTransform = OGRCreateCoordinateTransformation( &oUTM, poLatLong ); + if( poTransform == NULL ) + { + ... + } + + ... + + if( !poTransform->Transform( nPoints, x, y, z ) ) + ... +\endcode + +\section osr_tutorial_apis Alternate Interfaces + +A C interface to the coordinate system services is defined in +ogr_srs_api.h, and Python bindings are available via the osr.py module. +Methods are close analogs of the C++ methods but C and Python bindings +are missing for some C++ methods. + +

    C Bindings

    + +\code +typedef void *OGRSpatialReferenceH; +typedef void *OGRCoordinateTransformationH; + +OGRSpatialReferenceH OSRNewSpatialReference( const char * ); +void OSRDestroySpatialReference( OGRSpatialReferenceH ); + +int OSRReference( OGRSpatialReferenceH ); +int OSRDereference( OGRSpatialReferenceH ); + +OGRErr OSRImportFromEPSG( OGRSpatialReferenceH, int ); +OGRErr OSRImportFromWkt( OGRSpatialReferenceH, char ** ); +OGRErr OSRExportToWkt( OGRSpatialReferenceH, char ** ); + +OGRErr OSRSetAttrValue( OGRSpatialReferenceH hSRS, const char * pszNodePath, + const char * pszNewNodeValue ); +const char *OSRGetAttrValue( OGRSpatialReferenceH hSRS, + const char * pszName, int iChild); + +OGRErr OSRSetLinearUnits( OGRSpatialReferenceH, const char *, double ); +double OSRGetLinearUnits( OGRSpatialReferenceH, char ** ); + +int OSRIsGeographic( OGRSpatialReferenceH ); +int OSRIsProjected( OGRSpatialReferenceH ); +int OSRIsSameGeogCS( OGRSpatialReferenceH, OGRSpatialReferenceH ); +int OSRIsSame( OGRSpatialReferenceH, OGRSpatialReferenceH ); + +OGRErr OSRSetProjCS( OGRSpatialReferenceH hSRS, const char * pszName ); +OGRErr OSRSetWellKnownGeogCS( OGRSpatialReferenceH hSRS, + const char * pszName ); + +OGRErr OSRSetGeogCS( OGRSpatialReferenceH hSRS, + const char * pszGeogName, + const char * pszDatumName, + const char * pszEllipsoidName, + double dfSemiMajor, double dfInvFlattening, + const char * pszPMName , + double dfPMOffset , + const char * pszUnits, + double dfConvertToRadians ); + +double OSRGetSemiMajor( OGRSpatialReferenceH, OGRErr * ); +double OSRGetSemiMinor( OGRSpatialReferenceH, OGRErr * ); +double OSRGetInvFlattening( OGRSpatialReferenceH, OGRErr * ); + +OGRErr OSRSetAuthority( OGRSpatialReferenceH hSRS, + const char * pszTargetKey, + const char * pszAuthority, + int nCode ); +OGRErr OSRSetProjParm( OGRSpatialReferenceH, const char *, double ); +double OSRGetProjParm( OGRSpatialReferenceH hSRS, + const char * pszParmName, + double dfDefault, + OGRErr * ); + +OGRErr OSRSetUTM( OGRSpatialReferenceH hSRS, int nZone, int bNorth ); +int OSRGetUTMZone( OGRSpatialReferenceH hSRS, int *pbNorth ); + +OGRCoordinateTransformationH +OCTNewCoordinateTransformation( OGRSpatialReferenceH hSourceSRS, + OGRSpatialReferenceH hTargetSRS ); +void OCTDestroyCoordinateTransformation( OGRCoordinateTransformationH ); + +int OCTTransform( OGRCoordinateTransformationH hCT, + int nCount, double *x, double *y, double *z ); +\endcode + +

    Python Bindings

    + +\code +class osr.SpatialReference + def __init__(self,obj=None): + def ImportFromWkt( self, wkt ): + def ExportToWkt(self): + def ImportFromEPSG(self,code): + def IsGeographic(self): + def IsProjected(self): + def GetAttrValue(self, name, child = 0): + def SetAttrValue(self, name, value): + def SetWellKnownGeogCS(self, name): + def SetProjCS(self, name = "unnamed" ): + def IsSameGeogCS(self, other): + def IsSame(self, other): + def SetLinearUnits(self, units_name, to_meters ): + def SetUTM(self, zone, is_north = 1): + +class CoordinateTransformation: + def __init__(self,source,target): + def TransformPoint(self, x, y, z = 0): + def TransformPoints(self, points): +\endcode + +\section osr_tutorial_impl Internal Implementation + +The OGRCoordinateTransformation service is implemented on top of the +PROJ.4 library originally +written by Gerald Evenden of the USGS. + +*/ diff --git a/bazaar/plugin/gdal/ogr/style_pen1.gif b/bazaar/plugin/gdal/ogr/style_pen1.gif new file mode 100644 index 000000000..35b66ac79 Binary files /dev/null and b/bazaar/plugin/gdal/ogr/style_pen1.gif differ diff --git a/bazaar/plugin/gdal/ogr/style_pen2.gif b/bazaar/plugin/gdal/ogr/style_pen2.gif new file mode 100644 index 000000000..0640e22dc Binary files /dev/null and b/bazaar/plugin/gdal/ogr/style_pen2.gif differ diff --git a/bazaar/plugin/gdal/ogr/style_pen3.gif b/bazaar/plugin/gdal/ogr/style_pen3.gif new file mode 100644 index 000000000..cb62da2a0 Binary files /dev/null and b/bazaar/plugin/gdal/ogr/style_pen3.gif differ diff --git a/bazaar/plugin/gdal/ogr/style_textanchor.gif b/bazaar/plugin/gdal/ogr/style_textanchor.gif new file mode 100644 index 000000000..dd9352bf1 Binary files /dev/null and b/bazaar/plugin/gdal/ogr/style_textanchor.gif differ diff --git a/bazaar/plugin/gdal/ogr/swq.cpp b/bazaar/plugin/gdal/ogr/swq.cpp new file mode 100644 index 000000000..c7d0fe340 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/swq.cpp @@ -0,0 +1,883 @@ +/****************************************************************************** + * + * Component: OGDI Driver Support Library + * Purpose: Generic SQL WHERE Expression Implementation. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2001 Information Interoperability Institute (3i) + * Copyright (c) 2010-2013, Even Rouault + * + * Permission to use, copy, modify and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies, that + * both the copyright notice and this permission notice appear in + * supporting documentation, and that the name of 3i not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. 3i makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + ****************************************************************************/ + +#include + +#include "cpl_conv.h" +#include "cpl_multiproc.h" +#include "swq.h" +#include "swq_parser.hpp" +#include "cpl_time.h" + +#define YYSTYPE swq_expr_node* + +/************************************************************************/ +/* swqlex() */ +/************************************************************************/ + +void swqerror( swq_parse_context *context, const char *msg ) +{ + CPLString osMsg; + osMsg.Printf( "SQL Expression Parsing Error: %s. Occured around :\n", msg ); + + int i; + int n = context->pszLastValid - context->pszInput; + + for( i = MAX(0,n-40); i < n + 40 && context->pszInput[i] != '\0'; i ++ ) + osMsg += context->pszInput[i]; + osMsg += "\n"; + for(i=0;ipszNext; + + *ppNode = NULL; + +/* -------------------------------------------------------------------- */ +/* Do we have a start symbol to return? */ +/* -------------------------------------------------------------------- */ + if( context->nStartToken != 0 ) + { + int nRet = context->nStartToken; + context->nStartToken = 0; + return nRet; + } + +/* -------------------------------------------------------------------- */ +/* Skip white space. */ +/* -------------------------------------------------------------------- */ + while( *pszInput == ' ' || *pszInput == '\t' + || *pszInput == 10 || *pszInput == 13 ) + pszInput++; + + context->pszLastValid = pszInput; + + if( *pszInput == '\0' ) + { + context->pszNext = pszInput; + return EOF; + } + +/* -------------------------------------------------------------------- */ +/* Handle string constants. */ +/* -------------------------------------------------------------------- */ + if( *pszInput == '"' || *pszInput == '\'' ) + { + char *token; + int i_token; + char chQuote = *pszInput; + int bFoundEndQuote = FALSE; + + int nRet = *pszInput == '"' ? SWQT_IDENTIFIER : SWQT_STRING; + + pszInput++; + + token = (char *) CPLMalloc(strlen(pszInput)+1); + i_token = 0; + + while( *pszInput != '\0' ) + { + if( chQuote == '"' && *pszInput == '\\' && pszInput[1] == '"' ) + pszInput++; + else if( chQuote == '\'' && *pszInput == '\\' && pszInput[1] == '\'' ) + pszInput++; + else if( chQuote == '\'' && *pszInput == '\'' && pszInput[1] == '\'' ) + pszInput++; + else if( *pszInput == chQuote ) + { + pszInput++; + bFoundEndQuote = TRUE; + break; + } + + token[i_token++] = *(pszInput++); + } + token[i_token] = '\0'; + + if( !bFoundEndQuote ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Did not find end-of-string character"); + CPLFree( token ); + return 0; + } + + *ppNode = new swq_expr_node( token ); + CPLFree( token ); + + context->pszNext = pszInput; + + return nRet; + } + +/* -------------------------------------------------------------------- */ +/* Handle numbers. */ +/* -------------------------------------------------------------------- */ + else if( *pszInput >= '0' && *pszInput <= '9' ) + { + CPLString osToken; + const char *pszNext = pszInput + 1; + + osToken += *pszInput; + + // collect non-decimal part of number + while( *pszNext >= '0' && *pszNext <= '9' ) + osToken += *(pszNext++); + + // collect decimal places. + if( *pszNext == '.' ) + { + osToken += *(pszNext++); + while( *pszNext >= '0' && *pszNext <= '9' ) + osToken += *(pszNext++); + } + + // collect exponent + if( *pszNext == 'e' || *pszNext == 'E' ) + { + osToken += *(pszNext++); + if( *pszNext == '-' || *pszNext == '+' ) + osToken += *(pszNext++); + while( *pszNext >= '0' && *pszNext <= '9' ) + osToken += *(pszNext++); + } + + context->pszNext = pszNext; + + if( strstr(osToken,".") + || strstr(osToken,"e") + || strstr(osToken,"E") ) + { + *ppNode = new swq_expr_node( CPLAtof(osToken) ); + return SWQT_FLOAT_NUMBER; + } + else + { + GIntBig nVal = CPLAtoGIntBig(osToken); + if( (GIntBig)(int)nVal == nVal ) + *ppNode = new swq_expr_node( (int)nVal ); + else + *ppNode = new swq_expr_node( nVal ); + return SWQT_INTEGER_NUMBER; + } + } + +/* -------------------------------------------------------------------- */ +/* Handle alpha-numerics. */ +/* -------------------------------------------------------------------- */ + else if( isalnum( *pszInput ) ) + { + int nReturn = SWQT_IDENTIFIER; + CPLString osToken; + const char *pszNext = pszInput + 1; + + osToken += *pszInput; + + // collect text characters + while( isalnum( *pszNext ) || *pszNext == '_' + || ((unsigned char) *pszNext) > 127 ) + osToken += *(pszNext++); + + context->pszNext = pszNext; + + if( EQUAL(osToken,"IN") ) + nReturn = SWQT_IN; + else if( EQUAL(osToken,"LIKE") ) + nReturn = SWQT_LIKE; + else if( EQUAL(osToken,"ILIKE") ) + nReturn = SWQT_LIKE; + else if( EQUAL(osToken,"ESCAPE") ) + nReturn = SWQT_ESCAPE; + else if( EQUAL(osToken,"NULL") ) + nReturn = SWQT_NULL; + else if( EQUAL(osToken,"IS") ) + nReturn = SWQT_IS; + else if( EQUAL(osToken,"NOT") ) + nReturn = SWQT_NOT; + else if( EQUAL(osToken,"AND") ) + nReturn = SWQT_AND; + else if( EQUAL(osToken,"OR") ) + nReturn = SWQT_OR; + else if( EQUAL(osToken,"BETWEEN") ) + nReturn = SWQT_BETWEEN; + else if( EQUAL(osToken,"SELECT") ) + nReturn = SWQT_SELECT; + else if( EQUAL(osToken,"LEFT") ) + nReturn = SWQT_LEFT; + else if( EQUAL(osToken,"JOIN") ) + nReturn = SWQT_JOIN; + else if( EQUAL(osToken,"WHERE") ) + nReturn = SWQT_WHERE; + else if( EQUAL(osToken,"ON") ) + nReturn = SWQT_ON; + else if( EQUAL(osToken,"ORDER") ) + nReturn = SWQT_ORDER; + else if( EQUAL(osToken,"BY") ) + nReturn = SWQT_BY; + else if( EQUAL(osToken,"FROM") ) + nReturn = SWQT_FROM; + else if( EQUAL(osToken,"AS") ) + nReturn = SWQT_AS; + else if( EQUAL(osToken,"ASC") ) + nReturn = SWQT_ASC; + else if( EQUAL(osToken,"DESC") ) + nReturn = SWQT_DESC; + else if( EQUAL(osToken,"DISTINCT") ) + nReturn = SWQT_DISTINCT; + else if( EQUAL(osToken,"CAST") ) + nReturn = SWQT_CAST; + else if( EQUAL(osToken,"UNION") ) + nReturn = SWQT_UNION; + else if( EQUAL(osToken,"ALL") ) + nReturn = SWQT_ALL; + + /* Unhandled by OGR SQL */ + else if( EQUAL(osToken,"LIMIT") || + EQUAL(osToken,"OUTER") || + EQUAL(osToken,"INNER") ) + nReturn = SWQT_RESERVED_KEYWORD; + + else + { + *ppNode = new swq_expr_node( osToken ); + nReturn = SWQT_IDENTIFIER; + } + + return nReturn; + } + +/* -------------------------------------------------------------------- */ +/* Handle special tokens. */ +/* -------------------------------------------------------------------- */ + else + { + context->pszNext = pszInput+1; + return *pszInput; + } +} + +/************************************************************************/ +/* swq_select_summarize() */ +/************************************************************************/ + +const char * +swq_select_summarize( swq_select *select_info, + int dest_column, const char *value ) + +{ + swq_col_def *def = select_info->column_defs + dest_column; + swq_summary *summary; + +/* -------------------------------------------------------------------- */ +/* Do various checking. */ +/* -------------------------------------------------------------------- */ + if( select_info->query_mode == SWQM_RECORDSET ) + return "swq_select_summarize() called on non-summary query."; + + if( dest_column < 0 || dest_column >= select_info->result_columns ) + return "dest_column out of range in swq_select_summarize()."; + + if( def->col_func == SWQCF_NONE && !def->distinct_flag ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create the summary information if this is the first row */ +/* being processed. */ +/* -------------------------------------------------------------------- */ + if( select_info->column_summary == NULL && value != NULL ) + { + int i; + + select_info->column_summary = (swq_summary *) + CPLMalloc(sizeof(swq_summary) * select_info->result_columns); + memset( select_info->column_summary, 0, + sizeof(swq_summary) * select_info->result_columns ); + + for( i = 0; i < select_info->result_columns; i++ ) + { + select_info->column_summary[i].min = 1e20; + select_info->column_summary[i].max = -1e20; + strcpy(select_info->column_summary[i].szMin, "9999/99/99 99:99:99"); + strcpy(select_info->column_summary[i].szMax, "0000/00/00 00:00:00"); + } + } + + if( select_info->column_summary == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If distinct processing is on, process that now. */ +/* -------------------------------------------------------------------- */ + summary = select_info->column_summary + dest_column; + + if( def->distinct_flag ) + { + GIntBig i; + + /* This should be implemented with a much more complicated + data structure to achieve any sort of efficiency. */ + for( i = 0; i < summary->count; i++ ) + { + if( value == NULL ) + { + if (summary->distinct_list[i] == NULL) + break; + } + else if( summary->distinct_list[i] != NULL && + strcmp(value,summary->distinct_list[i]) == 0 ) + break; + } + + if( i == summary->count ) + { + char **old_list = summary->distinct_list; + + summary->distinct_list = (char **) + CPLMalloc(sizeof(char *) * (size_t)(summary->count+1)); + memcpy( summary->distinct_list, old_list, + sizeof(char *) * (size_t)summary->count ); + summary->distinct_list[(summary->count)++] = + (value != NULL) ? CPLStrdup( value ) : NULL; + + CPLFree(old_list); + } + } + +/* -------------------------------------------------------------------- */ +/* Process various options. */ +/* -------------------------------------------------------------------- */ + + switch( def->col_func ) + { + case SWQCF_MIN: + if( value != NULL && value[0] != '\0' ) + { + if(def->field_type == SWQ_DATE || + def->field_type == SWQ_TIME || + def->field_type == SWQ_TIMESTAMP) + { + if( strcmp( value, summary->szMin ) < 0 ) + { + strncpy( summary->szMin, value, sizeof(summary->szMin) ); + summary->szMin[sizeof(summary->szMin) - 1] = '\0'; + } + } + else + { + double df_val = CPLAtof(value); + if( df_val < summary->min ) + summary->min = df_val; + } + } + break; + case SWQCF_MAX: + if( value != NULL && value[0] != '\0' ) + { + if(def->field_type == SWQ_DATE || + def->field_type == SWQ_TIME || + def->field_type == SWQ_TIMESTAMP) + { + if( strcmp( value, summary->szMax ) > 0 ) + { + strncpy( summary->szMax, value, sizeof(summary->szMax) ); + summary->szMax[sizeof(summary->szMax) - 1] = '\0'; + } + } + else + { + double df_val = CPLAtof(value); + if( df_val > summary->max ) + summary->max = df_val; + } + } + break; + case SWQCF_AVG: + case SWQCF_SUM: + if( value != NULL && value[0] != '\0' ) + { + if(def->field_type == SWQ_DATE || + def->field_type == SWQ_TIME || + def->field_type == SWQ_TIMESTAMP) + { + int nYear, nMonth, nDay, nHour = 0, nMin = 0; + float fSec = 0 ; + if( sscanf(value, "%04d/%02d/%02d %02d:%02d:%f", + &nYear, &nMonth, &nDay, &nHour, &nMin, &fSec) == 6 || + sscanf(value, "%04d/%02d/%02d", + &nYear, &nMonth, &nDay) == 3 ) + { + struct tm brokendowntime; + brokendowntime.tm_year = nYear - 1900; + brokendowntime.tm_mon = nMonth - 1; + brokendowntime.tm_mday = nDay; + brokendowntime.tm_hour = nHour; + brokendowntime.tm_min = nMin; + brokendowntime.tm_sec = (int)fSec; + summary->count++; + summary->sum += CPLYMDHMSToUnixTime(&brokendowntime); + summary->sum += fmod((double)fSec, 1); + } + } + else + { + summary->count++; + summary->sum += CPLAtof(value); + } + } + break; + + case SWQCF_COUNT: + if( value != NULL && !def->distinct_flag ) + summary->count++; + break; + + case SWQCF_NONE: + break; + + case SWQCF_CUSTOM: + return "swq_select_summarize() called on custom field function."; + + default: + return "swq_select_summarize() - unexpected col_func"; + } + + return NULL; +} +/************************************************************************/ +/* sort comparison functions. */ +/************************************************************************/ + +static int FORCE_CDECL swq_compare_int( const void *item1, const void *item2 ) +{ + GIntBig v1, v2; + + const char* pszStr1 = *((const char **) item1); + const char* pszStr2 = *((const char **) item2); + if (pszStr1 == NULL) + return (pszStr2 == NULL) ? 0 : -1; + else if (pszStr2 == NULL) + return 1; + + v1 = CPLAtoGIntBig(pszStr1); + v2 = CPLAtoGIntBig(pszStr2); + + if( v1 < v2 ) + return -1; + else if( v1 == v2 ) + return 0; + else + return 1; +} + +static int FORCE_CDECL swq_compare_real( const void *item1, const void *item2 ) +{ + double v1, v2; + + const char* pszStr1 = *((const char **) item1); + const char* pszStr2 = *((const char **) item2); + if (pszStr1 == NULL) + return (pszStr2 == NULL) ? 0 : -1; + else if (pszStr2 == NULL) + return 1; + + v1 = CPLAtof(pszStr1); + v2 = CPLAtof(pszStr2); + + if( v1 < v2 ) + return -1; + else if( v1 == v2 ) + return 0; + else + return 1; +} + +static int FORCE_CDECL swq_compare_string( const void *item1, const void *item2 ) +{ + const char* pszStr1 = *((const char **) item1); + const char* pszStr2 = *((const char **) item2); + if (pszStr1 == NULL) + return (pszStr2 == NULL) ? 0 : -1; + else if (pszStr2 == NULL) + return 1; + + return strcmp( pszStr1, pszStr2 ); +} + +/************************************************************************/ +/* swq_select_finish_summarize() */ +/* */ +/* Call to complete summarize work. Does stuff like ordering */ +/* the distinct list for instance. */ +/************************************************************************/ + +const char *swq_select_finish_summarize( swq_select *select_info ) + +{ + int (FORCE_CDECL *compare_func)(const void *, const void*); + GIntBig count = 0; + char **distinct_list = NULL; + + if( select_info->query_mode != SWQM_DISTINCT_LIST + || select_info->order_specs == 0 ) + return NULL; + + if( select_info->order_specs > 1 ) + return "Can't ORDER BY a DISTINCT list by more than one key."; + + if( select_info->order_defs[0].field_index != + select_info->column_defs[0].field_index ) + return "Only selected DISTINCT field can be used for ORDER BY."; + + if( select_info->column_summary == NULL ) + return NULL; + + if( select_info->column_defs[0].field_type == SWQ_INTEGER || + select_info->column_defs[0].field_type == SWQ_INTEGER64 ) + compare_func = swq_compare_int; + else if( select_info->column_defs[0].field_type == SWQ_FLOAT ) + compare_func = swq_compare_real; + else + compare_func = swq_compare_string; + + distinct_list = select_info->column_summary[0].distinct_list; + count = select_info->column_summary[0].count; + + qsort( distinct_list, (size_t)count, sizeof(char *), compare_func ); + +/* -------------------------------------------------------------------- */ +/* Do we want the list ascending in stead of descending? */ +/* -------------------------------------------------------------------- */ + if( !select_info->order_defs[0].ascending_flag ) + { + char *saved; + GIntBig i; + + for( i = 0; i < count/2; i++ ) + { + saved = distinct_list[i]; + distinct_list[i] = distinct_list[count-i-1]; + distinct_list[count-i-1] = saved; + } + } + + return NULL; +} + +/************************************************************************/ +/* swq_select_free() */ +/************************************************************************/ + +void swq_select_free( swq_select *select_info ) + +{ + delete select_info; +} + +/************************************************************************/ +/* swq_identify_field() */ +/************************************************************************/ +int swq_identify_field_internal( const char* table_name, const char *field_token, + swq_field_list *field_list, + swq_field_type *this_type, int *table_id, + int bOneMoreTimeOK ); + +int swq_identify_field( const char* table_name, const char *field_token, + swq_field_list *field_list, + swq_field_type *this_type, int *table_id ) + +{ + return swq_identify_field_internal(table_name, field_token, field_list, + this_type, table_id, TRUE); +} + +int swq_identify_field_internal( const char* table_name, const char *field_token, + swq_field_list *field_list, + swq_field_type *this_type, int *table_id, + int bOneMoreTimeOK ) + +{ + int i; + if( table_name == NULL ) table_name = ""; + + int tables_enabled; + + if( field_list->table_count > 0 && field_list->table_ids != NULL ) + tables_enabled = TRUE; + else + tables_enabled = FALSE; + +/* -------------------------------------------------------------------- */ +/* Search for matching field. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < field_list->count; i++ ) + { + int t_id = 0; + + if( !EQUAL( field_list->names[i], field_token ) ) + continue; + + /* Do the table specifications match? */ + if( tables_enabled ) + { + t_id = field_list->table_ids[i]; + if( table_name[0] != '\0' + && !EQUAL(table_name,field_list->table_defs[t_id].table_alias)) + continue; + +// if( t_id != 0 && table_name[0] == '\0' ) +// continue; + } + else if( table_name[0] != '\0' ) + break; + + /* We have a match, return various information */ + if( this_type != NULL ) + { + if( field_list->types != NULL ) + *this_type = field_list->types[i]; + else + *this_type = SWQ_OTHER; + } + + if( table_id != NULL ) + *table_id = t_id; + + if( field_list->ids == NULL ) + return i; + else + return field_list->ids[i]; + } + +/* -------------------------------------------------------------------- */ +/* When there is no ambiguity, try to accept quoting errors... */ +/* -------------------------------------------------------------------- */ + if( bOneMoreTimeOK && !CSLTestBoolean(CPLGetConfigOption("OGR_SQL_STRICT", "FALSE")) ) + { + if( table_name[0] ) + { + CPLString osAggregatedName(CPLSPrintf("%s.%s", table_name, field_token)); + + // Check there's no table called table_name, or a field called with + // the aggregated name + for( i = 0; i < field_list->count; i++ ) + { + if( tables_enabled ) + { + int t_id = field_list->table_ids[i]; + if( EQUAL(table_name,field_list->table_defs[t_id].table_alias) ) + break; + } + } + if( i == field_list->count ) + { + int ret = swq_identify_field_internal( NULL, + osAggregatedName, + field_list, + this_type, table_id, FALSE ); + if( ret >= 0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Passed field name %s.%s should have been surrounded by double quotes. " + "Accepted since there is no ambiguity...", + table_name, field_token); + } + return ret; + } + } + else + { + // If the fieldname is a.b (and there's no . in b), then + // it might be an error in providing it as being quoted where it should + // not have been quoted. + const char* pszDot = strchr(field_token, '.'); + if( pszDot && strchr(pszDot+1, '.') == NULL ) + { + CPLString osTableName(field_token); + osTableName.resize(pszDot - field_token); + CPLString osFieldName(pszDot + 1); + + int ret = swq_identify_field_internal( osTableName, + osFieldName, + field_list, + this_type, table_id, FALSE ); + if( ret >= 0 ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "Passed field name %s should NOT have been surrounded by double quotes. " + "Accepted since there is no ambiguity...", + field_token); + } + return ret; + } + } + } + +/* -------------------------------------------------------------------- */ +/* No match, return failure. */ +/* -------------------------------------------------------------------- */ + if( this_type != NULL ) + *this_type = SWQ_OTHER; + + if( table_id != NULL ) + *table_id = 0; + + return -1; +} + +/************************************************************************/ +/* swq_expr_compile() */ +/************************************************************************/ + +CPLErr swq_expr_compile( const char *where_clause, + int field_count, + char **field_names, + swq_field_type *field_types, + int bCheck, + swq_custom_func_registrar* poCustomFuncRegistrar, + swq_expr_node **expr_out ) + +{ + swq_field_list field_list; + + field_list.count = field_count; + field_list.names = field_names; + field_list.types = field_types; + field_list.table_ids = NULL; + field_list.ids = NULL; + + field_list.table_count = 0; + field_list.table_defs = NULL; + + return swq_expr_compile2( where_clause, &field_list, + bCheck, poCustomFuncRegistrar, expr_out ); +} + + +/************************************************************************/ +/* swq_expr_compile2() */ +/************************************************************************/ + +CPLErr swq_expr_compile2( const char *where_clause, + swq_field_list *field_list, + int bCheck, + swq_custom_func_registrar* poCustomFuncRegistrar, + swq_expr_node **expr_out ) + +{ + + swq_parse_context context; + + context.pszInput = where_clause; + context.pszNext = where_clause; + context.pszLastValid = where_clause; + context.nStartToken = SWQT_VALUE_START; + context.bAcceptCustomFuncs = poCustomFuncRegistrar != NULL; + + if( swqparse( &context ) == 0 + && bCheck && context.poRoot->Check( field_list, FALSE, FALSE, poCustomFuncRegistrar ) != SWQ_ERROR ) + { + *expr_out = context.poRoot; + + return CE_None; + } + else + { + delete context.poRoot; + *expr_out = NULL; + return CE_Failure; + } +} + +/************************************************************************/ +/* swq_is_reserved_keyword() */ +/************************************************************************/ + +static const char* apszSQLReservedKeywords[] = { + "OR", + "AND", + "NOT", + "LIKE", + "IS", + "NULL", + "IN", + "BETWEEN", + "CAST", + "DISTINCT", + "ESCAPE", + "SELECT", + "LEFT", + "JOIN", + "WHERE", + "ON", + "ORDER", + "BY", + "FROM", + "AS", + "ASC", + "DESC", + "UNION", + "ALL" +}; + +int swq_is_reserved_keyword(const char* pszStr) +{ + for(int i = 0; i < (int)(sizeof(apszSQLReservedKeywords)/sizeof(char*)); i++) + { + if (EQUAL(pszStr, apszSQLReservedKeywords[i])) + return TRUE; + } + return FALSE; +} + +/************************************************************************/ +/* SWQFieldTypeToString() */ +/************************************************************************/ + +const char* SWQFieldTypeToString( swq_field_type field_type ) +{ + switch(field_type) + { + case SWQ_INTEGER: return "integer"; + case SWQ_INTEGER64: return "bigint"; + case SWQ_FLOAT: return "float"; + case SWQ_STRING: return "string"; + case SWQ_BOOLEAN: return "boolean"; + case SWQ_DATE: return "date"; + case SWQ_TIME: return "time"; + case SWQ_TIMESTAMP: return "timestamp"; + case SWQ_GEOMETRY: return "geometry"; + case SWQ_NULL: return "null"; + default: return "unknown"; + } +} diff --git a/bazaar/plugin/gdal/ogr/swq.h b/bazaar/plugin/gdal/ogr/swq.h new file mode 100644 index 000000000..d46a78a13 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/swq.h @@ -0,0 +1,392 @@ +/****************************************************************************** + * + * Component: OGDI Driver Support Library + * Purpose: Generic SQL WHERE Expression Evaluator Declarations. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2001 Information Interoperability Institute (3i) + * Copyright (c) 2010-2013, Even Rouault + * Permission to use, copy, modify and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies, that + * both the copyright notice and this permission notice appear in + * supporting documentation, and that the name of 3i not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. 3i makes no + * representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + ****************************************************************************/ + +#ifndef _SWQ_H_INCLUDED_ +#define _SWQ_H_INCLUDED_ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_core.h" + +#if defined(_WIN32) && !defined(_WIN32_WCE) +# define strcasecmp stricmp +#elif defined(_WIN32_WCE) +# define strcasecmp _stricmp +#endif + +typedef enum { + SWQ_OR, + SWQ_AND, + SWQ_NOT, + SWQ_EQ, + SWQ_NE, + SWQ_GE, + SWQ_LE, + SWQ_LT, + SWQ_GT, + SWQ_LIKE, + SWQ_ISNULL, + SWQ_IN, + SWQ_BETWEEN, + SWQ_ADD, + SWQ_SUBTRACT, + SWQ_MULTIPLY, + SWQ_DIVIDE, + SWQ_MODULUS, + SWQ_CONCAT, + SWQ_SUBSTR, + SWQ_HSTORE_GET_VALUE, + SWQ_AVG, + SWQ_MIN, + SWQ_MAX, + SWQ_COUNT, + SWQ_SUM, + SWQ_CAST, + SWQ_CUSTOM_FUNC, /* only if parsing done in bAcceptCustomFuncs mode */ + SWQ_ARGUMENT_LIST /* temporary value only set during parsing and replaced by something else at the end */ +} swq_op; + +typedef enum { + SWQ_INTEGER, + SWQ_INTEGER64, + SWQ_FLOAT, + SWQ_STRING, + SWQ_BOOLEAN, // integer + SWQ_DATE, // string + SWQ_TIME, // string + SWQ_TIMESTAMP,// string + SWQ_GEOMETRY, + SWQ_NULL, + SWQ_OTHER, + SWQ_ERROR +} swq_field_type; + +#define SWQ_IS_INTEGER(x) ((x) == SWQ_INTEGER || (x) == SWQ_INTEGER64) + +typedef enum { + SNT_CONSTANT, + SNT_COLUMN, + SNT_OPERATION +} swq_node_type; + + +class swq_field_list; +class swq_expr_node; +class swq_select; +class OGRGeometry; + +typedef swq_expr_node *(*swq_field_fetcher)( swq_expr_node *op, + void *record_handle ); +typedef swq_expr_node *(*swq_op_evaluator)(swq_expr_node *op, + swq_expr_node **sub_field_values ); +typedef swq_field_type (*swq_op_checker)( swq_expr_node *op, + int bAllowMismatchTypeOnFieldComparison ); + +class swq_custom_func_registrar; + +class swq_expr_node { +public: + swq_expr_node(); + + swq_expr_node( const char * ); + swq_expr_node( int ); + swq_expr_node( GIntBig ); + swq_expr_node( double ); + swq_expr_node( OGRGeometry* ); + swq_expr_node( swq_op ); + + ~swq_expr_node(); + + void Initialize(); + CPLString UnparseOperationFromUnparsedSubExpr(char** apszSubExpr); + char *Unparse( swq_field_list *, char chColumnQuote ); + void Dump( FILE *fp, int depth ); + swq_field_type Check( swq_field_list *, int bAllowFieldsInSecondaryTables, + int bAllowMismatchTypeOnFieldComparison, + swq_custom_func_registrar* poCustomFuncRegistrar ); + swq_expr_node* Evaluate( swq_field_fetcher pfnFetcher, + void *record ); + swq_expr_node* Clone(); + + void ReplaceBetweenByGEAndLERecurse(); + + swq_node_type eNodeType; + swq_field_type field_type; + + /* only for SNT_OPERATION */ + void PushSubExpression( swq_expr_node * ); + void ReverseSubExpressions(); + int nOperation; + int nSubExprCount; + swq_expr_node **papoSubExpr; + + /* only for SNT_COLUMN */ + int field_index; + int table_index; + char *table_name; + + /* only for SNT_CONSTANT */ + int is_null; + GIntBig int_value; + double float_value; + OGRGeometry *geometry_value; + + /* shared by SNT_COLUMN, SNT_CONSTANT and also possibly SNT_OPERATION when */ + /* nOperation == SWQ_CUSTOM_FUNC */ + char *string_value; /* column name when SNT_COLUMN */ + + + static CPLString QuoteIfNecessary( const CPLString &, char chQuote = '\'' ); + static CPLString Quote( const CPLString &, char chQuote = '\'' ); +}; + +typedef struct { + const char* pszName; + swq_op eOperation; + swq_op_evaluator pfnEvaluator; + swq_op_checker pfnChecker; +} swq_operation; + +class swq_op_registrar { +public: + static const swq_operation *GetOperator( const char * ); + static const swq_operation *GetOperator( swq_op eOperation ); +}; + +class swq_custom_func_registrar +{ + public: + virtual ~swq_custom_func_registrar() {} + virtual const swq_operation *GetOperator( const char * ) = 0; +}; + + +typedef struct { + char *data_source; + char *table_name; + char *table_alias; +} swq_table_def; + +class swq_field_list { +public: + int count; + char **names; + swq_field_type *types; + int *table_ids; + int *ids; + + int table_count; + swq_table_def *table_defs; +}; + +class swq_parse_context { +public: + swq_parse_context() : nStartToken(0), pszInput(NULL), pszNext(NULL), + pszLastValid(NULL), bAcceptCustomFuncs(FALSE), + poRoot(NULL), poCurSelect(NULL) {} + + int nStartToken; + const char *pszInput; + const char *pszNext; + const char *pszLastValid; + int bAcceptCustomFuncs; + + swq_expr_node *poRoot; + + swq_select *poCurSelect; +}; + +/* Compile an SQL WHERE clause into an internal form. The field_list is +** the list of fields in the target 'table', used to render where into +** field numbers instead of names. +*/ +int swqparse( swq_parse_context *context ); +int swqlex( swq_expr_node **ppNode, swq_parse_context *context ); +void swqerror( swq_parse_context *context, const char *msg ); + +int swq_identify_field( const char* table_name, + const char *token, swq_field_list *field_list, + swq_field_type *this_type, int *table_id ); + +CPLErr swq_expr_compile( const char *where_clause, + int field_count, + char **field_list, + swq_field_type *field_types, + int bCheck, + swq_custom_func_registrar* poCustomFuncRegistrar, + swq_expr_node **expr_root ); + +CPLErr swq_expr_compile2( const char *where_clause, + swq_field_list *field_list, + int bCheck, + swq_custom_func_registrar* poCustomFuncRegistrar, + swq_expr_node **expr_root ); + +/* +** Evaluation related. +*/ +int swq_test_like( const char *input, const char *pattern ); + +swq_expr_node *SWQGeneralEvaluator( swq_expr_node *, swq_expr_node **); +swq_field_type SWQGeneralChecker( swq_expr_node *node, int bAllowMismatchTypeOnFieldComparison ); +swq_expr_node *SWQCastEvaluator( swq_expr_node *, swq_expr_node **); +swq_field_type SWQCastChecker( swq_expr_node *node, int bAllowMismatchTypeOnFieldComparison ); +const char* SWQFieldTypeToString( swq_field_type field_type ); + +/****************************************************************************/ + +#define SWQP_ALLOW_UNDEFINED_COL_FUNCS 0x01 + +#define SWQM_SUMMARY_RECORD 1 +#define SWQM_RECORDSET 2 +#define SWQM_DISTINCT_LIST 3 + +typedef enum { + SWQCF_NONE = 0, + SWQCF_AVG = SWQ_AVG, + SWQCF_MIN = SWQ_MIN, + SWQCF_MAX = SWQ_MAX, + SWQCF_COUNT = SWQ_COUNT, + SWQCF_SUM = SWQ_SUM, + SWQCF_CUSTOM +} swq_col_func; + +typedef struct { + swq_col_func col_func; + char *table_name; + char *field_name; + char *field_alias; + int table_index; + int field_index; + swq_field_type field_type; + swq_field_type target_type; + OGRFieldSubType target_subtype; + int field_length; + int field_precision; + int distinct_flag; + OGRwkbGeometryType eGeomType; + int nSRID; + swq_expr_node *expr; +} swq_col_def; + +typedef struct { + GIntBig count; + + char **distinct_list; /* items of the list can be NULL */ + double sum; + double min; + double max; + char szMin[32]; + char szMax[32]; +} swq_summary; + +typedef struct { + char *table_name; + char *field_name; + int table_index; + int field_index; + int ascending_flag; +} swq_order_def; + +typedef struct { + int secondary_table; + swq_expr_node *poExpr; +} swq_join_def; + +class swq_select_parse_options +{ +public: + swq_custom_func_registrar* poCustomFuncRegistrar; + int bAllowFieldsInSecondaryTablesInWhere; + int bAddSecondaryTablesGeometryFields; + int bAlwaysPrefixWithTableName; + int bAllowDistinctOnGeometryField; + int bAllowDistinctOnMultipleFields; + + swq_select_parse_options(): poCustomFuncRegistrar(NULL), + bAllowFieldsInSecondaryTablesInWhere(FALSE), + bAddSecondaryTablesGeometryFields(FALSE), + bAlwaysPrefixWithTableName(FALSE), + bAllowDistinctOnGeometryField(FALSE), + bAllowDistinctOnMultipleFields(FALSE) {} +}; + +class swq_select +{ + void postpreparse(); + +public: + swq_select(); + ~swq_select(); + + int query_mode; + + char *raw_select; + + int PushField( swq_expr_node *poExpr, const char *pszAlias=NULL, + int distinct_flag = FALSE ); + int result_columns; + swq_col_def *column_defs; + swq_summary *column_summary; + + int PushTableDef( const char *pszDataSource, + const char *pszTableName, + const char *pszAlias ); + int table_count; + swq_table_def *table_defs; + + void PushJoin( int iSecondaryTable, swq_expr_node* poExpr ); + int join_count; + swq_join_def *join_defs; + + swq_expr_node *where_expr; + + void PushOrderBy( const char* pszTableName, const char *pszFieldName, int bAscending ); + int order_specs; + swq_order_def *order_defs; + + swq_select *poOtherSelect; + void PushUnionAll( swq_select* poOtherSelectIn ); + + CPLErr preparse( const char *select_statement, + int bAcceptCustomFuncs = FALSE ); + CPLErr expand_wildcard( swq_field_list *field_list, + int bAlwaysPrefixWithTableName ); + CPLErr parse( swq_field_list *field_list, + swq_select_parse_options* poParseOptions ); + + char *Unparse(); + void Dump( FILE * ); +}; + +CPLErr swq_select_parse( swq_select *select_info, + swq_field_list *field_list, + int parse_flags ); + +const char *swq_select_finish_summarize( swq_select *select_info ); +const char *swq_select_summarize( swq_select *select_info, + int dest_column, + const char *value ); + +int swq_is_reserved_keyword(const char* pszStr); + +char* OGRHStoreGetValue(const char* pszHStore, const char* pszSearchedKey); + +#endif /* def _SWQ_H_INCLUDED_ */ diff --git a/bazaar/plugin/gdal/ogr/swq_expr_node.cpp b/bazaar/plugin/gdal/ogr/swq_expr_node.cpp new file mode 100644 index 000000000..c1fa5288a --- /dev/null +++ b/bazaar/plugin/gdal/ogr/swq_expr_node.cpp @@ -0,0 +1,829 @@ +/****************************************************************************** + * + * Component: OGR SQL Engine + * Purpose: Implementation of the swq_expr_node class used to represent a + * node in an SQL expression. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_multiproc.h" +#include "swq.h" +#include "ogr_geometry.h" +#include + +/************************************************************************/ +/* swq_expr_node() */ +/************************************************************************/ + +swq_expr_node::swq_expr_node() + +{ + Initialize(); +} + +/************************************************************************/ +/* swq_expr_node(int) */ +/************************************************************************/ + +swq_expr_node::swq_expr_node( int nValueIn ) + +{ + Initialize(); + + int_value = nValueIn; +} + +/************************************************************************/ +/* swq_expr_node(GIntBig) */ +/************************************************************************/ + +swq_expr_node::swq_expr_node( GIntBig nValueIn ) + +{ + Initialize(); + + field_type = SWQ_INTEGER64; + int_value = nValueIn; +} + +/************************************************************************/ +/* swq_expr_node(double) */ +/************************************************************************/ + +swq_expr_node::swq_expr_node( double dfValueIn ) + +{ + Initialize(); + + field_type = SWQ_FLOAT; + float_value = dfValueIn; +} + +/************************************************************************/ +/* swq_expr_node(const char*) */ +/************************************************************************/ + +swq_expr_node::swq_expr_node( const char *pszValueIn ) + +{ + Initialize(); + + field_type = SWQ_STRING; + string_value = CPLStrdup( pszValueIn ? pszValueIn : "" ); + is_null = pszValueIn == NULL; +} + +/************************************************************************/ +/* swq_expr_node(OGRGeometry *) */ +/************************************************************************/ + +swq_expr_node::swq_expr_node( OGRGeometry *poGeomIn ) + +{ + Initialize(); + + field_type = SWQ_GEOMETRY; + geometry_value = poGeomIn ? poGeomIn->clone() : NULL; + is_null = poGeomIn == NULL; +} + +/************************************************************************/ +/* swq_expr_node(swq_op) */ +/************************************************************************/ + +swq_expr_node::swq_expr_node( swq_op eOp ) + +{ + Initialize(); + + eNodeType = SNT_OPERATION; + + nOperation = (int) eOp; + nSubExprCount = 0; + papoSubExpr = NULL; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void swq_expr_node::Initialize() + +{ + eNodeType = SNT_CONSTANT; + field_type = SWQ_INTEGER; + int_value = 0; + + is_null = FALSE; + table_name = NULL; + string_value = NULL; + geometry_value = NULL; + papoSubExpr = NULL; + nSubExprCount = 0; +} + +/************************************************************************/ +/* ~swq_expr_node() */ +/************************************************************************/ + +swq_expr_node::~swq_expr_node() + +{ + CPLFree( table_name ); + CPLFree( string_value ); + + int i; + for( i = 0; i < nSubExprCount; i++ ) + delete papoSubExpr[i]; + CPLFree( papoSubExpr ); + delete geometry_value; +} + +/************************************************************************/ +/* PushSubExpression() */ +/************************************************************************/ + +void swq_expr_node::PushSubExpression( swq_expr_node *child ) + +{ + nSubExprCount++; + papoSubExpr = (swq_expr_node **) + CPLRealloc( papoSubExpr, sizeof(void*) * nSubExprCount ); + + papoSubExpr[nSubExprCount-1] = child; +} + +/************************************************************************/ +/* ReverseSubExpressions() */ +/************************************************************************/ + +void swq_expr_node::ReverseSubExpressions() + +{ + int i; + for( i = 0; i < nSubExprCount / 2; i++ ) + { + swq_expr_node *temp; + + temp = papoSubExpr[i]; + papoSubExpr[i] = papoSubExpr[nSubExprCount - i - 1]; + papoSubExpr[nSubExprCount - i - 1] = temp; + } +} + +/************************************************************************/ +/* Check() */ +/* */ +/* Check argument types, etc. */ +/************************************************************************/ + +swq_field_type swq_expr_node::Check( swq_field_list *poFieldList, + int bAllowFieldsInSecondaryTables, + int bAllowMismatchTypeOnFieldComparison, + swq_custom_func_registrar* poCustomFuncRegistrar ) + +{ +/* -------------------------------------------------------------------- */ +/* Otherwise we take constants literally. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_CONSTANT ) + return field_type; + +/* -------------------------------------------------------------------- */ +/* If this is intended to be a field definition, but has not */ +/* yet been looked up, we do so now. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_COLUMN && field_index == -1 ) + { + field_index = + swq_identify_field( table_name, string_value, poFieldList, + &field_type, &table_index ); + + if( field_index < 0 ) + { + if( table_name ) + CPLError( CE_Failure, CPLE_AppDefined, + "\"%s\".\"%s\" not recognised as an available field.", + table_name, string_value ); + else + CPLError( CE_Failure, CPLE_AppDefined, + "\"%s\" not recognised as an available field.", + string_value ); + + return SWQ_ERROR; + } + + if( !bAllowFieldsInSecondaryTables && table_index != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot use field '%s' of a secondary table in this context", + string_value ); + return SWQ_ERROR; + } + } + + if( eNodeType == SNT_COLUMN ) + return field_type; + +/* -------------------------------------------------------------------- */ +/* We are dealing with an operation - fetch the definition. */ +/* -------------------------------------------------------------------- */ + const swq_operation *poOp = + (nOperation == SWQ_CUSTOM_FUNC && poCustomFuncRegistrar != NULL ) ? + poCustomFuncRegistrar->GetOperator(string_value) : + swq_op_registrar::GetOperator((swq_op)nOperation); + + if( poOp == NULL ) + { + if( nOperation == SWQ_CUSTOM_FUNC ) + CPLError( CE_Failure, CPLE_AppDefined, + "Check(): Unable to find definition for operator %s.", + string_value ); + else + CPLError( CE_Failure, CPLE_AppDefined, + "Check(): Unable to find definition for operator %d.", + nOperation ); + return SWQ_ERROR; + } + +/* -------------------------------------------------------------------- */ +/* Check subexpressions first. */ +/* -------------------------------------------------------------------- */ + int i; + + for( i = 0; i < nSubExprCount; i++ ) + { + if( papoSubExpr[i]->Check(poFieldList, bAllowFieldsInSecondaryTables, + bAllowMismatchTypeOnFieldComparison, + poCustomFuncRegistrar) == SWQ_ERROR ) + return SWQ_ERROR; + } + +/* -------------------------------------------------------------------- */ +/* Check this node. */ +/* -------------------------------------------------------------------- */ + field_type = poOp->pfnChecker( this, bAllowMismatchTypeOnFieldComparison ); + + return field_type; +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +void swq_expr_node::Dump( FILE * fp, int depth ) + +{ + char spaces[60]; + int i; + + for( i = 0; i < depth*2 && i < (int) sizeof(spaces) - 1; i++ ) + spaces[i] = ' '; + spaces[i] = '\0'; + + if( eNodeType == SNT_COLUMN ) + { + fprintf( fp, "%s Field %d\n", spaces, field_index ); + return; + } + + if( eNodeType == SNT_CONSTANT ) + { + if( field_type == SWQ_INTEGER || field_type == SWQ_INTEGER64 || + field_type == SWQ_BOOLEAN ) + fprintf( fp, "%s " CPL_FRMT_GIB "\n", spaces, int_value ); + else if( field_type == SWQ_FLOAT ) + fprintf( fp, "%s %.15g\n", spaces, float_value ); + else if( field_type == SWQ_GEOMETRY ) + { + if( geometry_value == NULL ) + fprintf( fp, "%s (null)\n", spaces ); + else + { + char* pszWKT = NULL; + geometry_value->exportToWkt(&pszWKT); + fprintf( fp, "%s %s\n", spaces, pszWKT ); + CPLFree(pszWKT); + } + } + else + fprintf( fp, "%s %s\n", spaces, string_value ); + return; + } + + CPLAssert( eNodeType == SNT_OPERATION ); + + const swq_operation *op_def = + swq_op_registrar::GetOperator( (swq_op) nOperation ); + if( op_def ) + fprintf( fp, "%s%s\n", spaces, op_def->pszName ); + else + fprintf( fp, "%s%s\n", spaces, string_value ); + + for( i = 0; i < nSubExprCount; i++ ) + papoSubExpr[i]->Dump( fp, depth+1 ); +} + + +/************************************************************************/ +/* QuoteIfNecessary() */ +/* */ +/* Add quoting if necessary to unparse a string. */ +/************************************************************************/ + +CPLString swq_expr_node::QuoteIfNecessary( const CPLString &osExpr, char chQuote ) + +{ + if( osExpr[0] == '_' ) + return Quote(osExpr, chQuote); + if( osExpr == "*" ) + return osExpr; + + for( int i = 0; i < (int) osExpr.size(); i++ ) + { + char ch = osExpr[i]; + if ((!(isalnum((int)ch) || ch == '_')) || ch == '.') + { + return Quote(osExpr, chQuote); + } + } + + if (swq_is_reserved_keyword(osExpr)) + { + return Quote(osExpr, chQuote); + } + + return osExpr; +} + +/************************************************************************/ +/* Quote() */ +/* */ +/* Add quoting necessary to unparse a string. */ +/************************************************************************/ + +CPLString swq_expr_node::Quote( const CPLString &osTarget, char chQuote ) + +{ + CPLString osNew; + int i; + + osNew += chQuote; + + for( i = 0; i < (int) osTarget.size(); i++ ) + { + if( osTarget[i] == chQuote ) + { + osNew += chQuote; + osNew += chQuote; + } + else + osNew += osTarget[i]; + } + osNew += chQuote; + + return osNew; +} + +/************************************************************************/ +/* Unparse() */ +/************************************************************************/ + +char *swq_expr_node::Unparse( swq_field_list *field_list, char chColumnQuote ) + +{ + CPLString osExpr; + +/* -------------------------------------------------------------------- */ +/* Handle constants. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_CONSTANT ) + { + if (is_null) + return CPLStrdup("NULL"); + + if( field_type == SWQ_INTEGER || field_type == SWQ_INTEGER64 || + field_type == SWQ_BOOLEAN ) + osExpr.Printf( CPL_FRMT_GIB, int_value ); + else if( field_type == SWQ_FLOAT ) + { + osExpr.Printf( "%.15g", float_value ); + /* Make sure this is interpreted as a floating point value */ + /* and not as an integer later */ + if (strchr(osExpr, '.') == NULL && strchr(osExpr, 'e') == NULL && + strchr(osExpr, 'E') == NULL) + osExpr += '.'; + } + else + { + osExpr = Quote( string_value ); + } + + return CPLStrdup(osExpr); + } + +/* -------------------------------------------------------------------- */ +/* Handle columns. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_COLUMN ) + { + if( field_list == NULL ) + { + if( table_name ) + osExpr.Printf( "%s.%s", + QuoteIfNecessary(table_name, chColumnQuote).c_str(), + QuoteIfNecessary(string_value, chColumnQuote).c_str() ); + else + osExpr.Printf( "%s", + QuoteIfNecessary(string_value, chColumnQuote).c_str() ); + } + else if( field_index != -1 + && table_index < field_list->table_count + && table_index > 0 ) + { + for(int i = 0; i < field_list->count; i++ ) + { + if( field_list->table_ids[i] == table_index && + field_list->ids[i] == field_index ) + { + osExpr.Printf( "%s.%s", + QuoteIfNecessary(field_list->table_defs[table_index].table_name, chColumnQuote).c_str(), + QuoteIfNecessary(field_list->names[i], chColumnQuote).c_str() ); + break; + } + } + } + else if( field_index != -1 ) + { + for(int i = 0; i < field_list->count; i++ ) + { + if( field_list->table_ids[i] == table_index && + field_list->ids[i] == field_index ) + { + osExpr.Printf( "%s", QuoteIfNecessary(field_list->names[i], chColumnQuote).c_str() ); + break; + } + } + } + + if( osExpr.size() == 0 ) + { + return CPLStrdup(CPLSPrintf("%c%c", chColumnQuote, chColumnQuote)); + } + + /* The string is just alphanum and not a reserved SQL keyword, no needs to quote and escape */ + return CPLStrdup(osExpr.c_str()); + } + +/* -------------------------------------------------------------------- */ +/* Operation - start by unparsing all the subexpressions. */ +/* -------------------------------------------------------------------- */ + std::vector apszSubExpr; + int i; + + for( i = 0; i < nSubExprCount; i++ ) + apszSubExpr.push_back( papoSubExpr[i]->Unparse(field_list, chColumnQuote) ); + + osExpr = UnparseOperationFromUnparsedSubExpr(&apszSubExpr[0]); + +/* -------------------------------------------------------------------- */ +/* cleanup subexpressions. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nSubExprCount; i++ ) + CPLFree( apszSubExpr[i] ); + + return CPLStrdup( osExpr.c_str() ); +} + +/************************************************************************/ +/* UnparseOperationFromUnparsedSubExpr() */ +/************************************************************************/ + +CPLString swq_expr_node::UnparseOperationFromUnparsedSubExpr(char** apszSubExpr) +{ + int i; + CPLString osExpr; + +/* -------------------------------------------------------------------- */ +/* Put things together in a fashion depending on the operator. */ +/* -------------------------------------------------------------------- */ + const swq_operation *poOp = + swq_op_registrar::GetOperator( (swq_op) nOperation ); + + if( poOp == NULL && nOperation != SWQ_CUSTOM_FUNC ) + { + CPLAssert( FALSE ); + return osExpr; + } + + switch( nOperation ) + { + // binary infix operators. + case SWQ_OR: + case SWQ_AND: + case SWQ_EQ: + case SWQ_NE: + case SWQ_GT: + case SWQ_LT: + case SWQ_GE: + case SWQ_LE: + case SWQ_LIKE: + case SWQ_ADD: + case SWQ_SUBTRACT: + case SWQ_MULTIPLY: + case SWQ_DIVIDE: + case SWQ_MODULUS: + CPLAssert( nSubExprCount >= 2 ); + if (papoSubExpr[0]->eNodeType == SNT_COLUMN || + papoSubExpr[0]->eNodeType == SNT_CONSTANT) + { + osExpr += apszSubExpr[0]; + } + else + { + osExpr += "("; + osExpr += apszSubExpr[0]; + osExpr += ")"; + } + osExpr += " "; + osExpr += poOp->pszName; + osExpr += " "; + if (papoSubExpr[1]->eNodeType == SNT_COLUMN || + papoSubExpr[1]->eNodeType == SNT_CONSTANT) + { + osExpr += apszSubExpr[1]; + } + else + { + osExpr += "("; + osExpr += apszSubExpr[1]; + osExpr += ")"; + } + if( nOperation == SWQ_LIKE && nSubExprCount == 3 ) + osExpr += CPLSPrintf( " ESCAPE (%s)", apszSubExpr[2] ); + break; + + case SWQ_NOT: + CPLAssert( nSubExprCount == 1 ); + osExpr.Printf( "NOT (%s)", apszSubExpr[0] ); + break; + + case SWQ_ISNULL: + CPLAssert( nSubExprCount == 1 ); + osExpr.Printf( "%s IS NULL", apszSubExpr[0] ); + break; + + case SWQ_IN: + osExpr.Printf( "%s IN (", apszSubExpr[0] ); + for( i = 1; i < nSubExprCount; i++ ) + { + if( i > 1 ) + osExpr += ","; + osExpr += "("; + osExpr += apszSubExpr[i]; + osExpr += ")"; + } + osExpr += ")"; + break; + + case SWQ_BETWEEN: + CPLAssert( nSubExprCount == 3 ); + osExpr.Printf( "%s %s (%s) AND (%s)", + apszSubExpr[0], + poOp->pszName, + apszSubExpr[1], + apszSubExpr[2] ); + break; + + case SWQ_CAST: + osExpr = "CAST("; + for( i = 0; i < nSubExprCount; i++ ) + { + if( i == 1 ) + osExpr += " AS "; + else if( i > 2 ) + osExpr += ", "; + + int nLen = (int)strlen(apszSubExpr[i]); + if( (i == 1 && + (apszSubExpr[i][0] == '\'' && nLen > 2 && apszSubExpr[i][nLen-1] == '\'')) || + (i == 2 && EQUAL(apszSubExpr[1], "'GEOMETRY")) ) + { + apszSubExpr[i][nLen-1] = '\0'; + osExpr += apszSubExpr[i] + 1; + } + else + osExpr += apszSubExpr[i]; + + if( i == 1 && nSubExprCount > 2) + osExpr += "("; + else if (i > 1 && i == nSubExprCount - 1) + osExpr += ")"; + } + osExpr += ")"; + break; + + default: // function style. + if( nOperation != SWQ_CUSTOM_FUNC ) + osExpr.Printf( "%s(", poOp->pszName ); + else + osExpr.Printf( "%s(", string_value ); + for( i = 0; i < nSubExprCount; i++ ) + { + if( i > 0 ) + osExpr += ","; + osExpr += "("; + osExpr += apszSubExpr[i]; + osExpr += ")"; + } + osExpr += ")"; + break; + } + + return osExpr; +} + +/************************************************************************/ +/* Clone() */ +/************************************************************************/ + +swq_expr_node *swq_expr_node::Clone() +{ + swq_expr_node* poRetNode = new swq_expr_node(); + + poRetNode->eNodeType = eNodeType; + poRetNode->field_type = field_type; + if( eNodeType == SNT_OPERATION ) + { + poRetNode->nOperation = nOperation; + poRetNode->nSubExprCount = nSubExprCount; + poRetNode->papoSubExpr = (swq_expr_node **) + CPLMalloc( sizeof(void*) * nSubExprCount ); + for(int i=0;ipapoSubExpr[i] = papoSubExpr[i]->Clone(); + } + else if( eNodeType == SNT_COLUMN ) + { + poRetNode->field_index = field_index; + poRetNode->table_index = table_index; + poRetNode->table_name = table_name ? CPLStrdup(table_name) : NULL; + } + else if( eNodeType == SNT_CONSTANT ) + { + poRetNode->is_null = is_null; + poRetNode->int_value = int_value; + poRetNode->float_value = float_value; + if( geometry_value ) + poRetNode->geometry_value = geometry_value->clone(); + else + poRetNode->geometry_value = NULL; + } + poRetNode->string_value = string_value ? CPLStrdup(string_value) : NULL; + return poRetNode; +} + +/************************************************************************/ +/* Evaluate() */ +/************************************************************************/ + +swq_expr_node *swq_expr_node::Evaluate( swq_field_fetcher pfnFetcher, + void *pRecord ) + +{ + swq_expr_node *poRetNode = NULL; + +/* -------------------------------------------------------------------- */ +/* Duplicate ourselves if we are already a constant. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_CONSTANT ) + { + return Clone(); + } + +/* -------------------------------------------------------------------- */ +/* If this is a field value from a record, fetch and return it. */ +/* -------------------------------------------------------------------- */ + if( eNodeType == SNT_COLUMN ) + { + return pfnFetcher( this, pRecord ); + } + +/* -------------------------------------------------------------------- */ +/* This is an operation, collect the arguments keeping track of */ +/* which we will need to free. */ +/* -------------------------------------------------------------------- */ + std::vector apoValues; + std::vector anValueNeedsFree; + int i, bError = FALSE; + + for( i = 0; i < nSubExprCount && !bError; i++ ) + { + if( papoSubExpr[i]->eNodeType == SNT_CONSTANT ) + { + // avoid duplication. + apoValues.push_back( papoSubExpr[i] ); + anValueNeedsFree.push_back( FALSE ); + } + else + { + swq_expr_node* poSubExprVal = papoSubExpr[i]->Evaluate(pfnFetcher,pRecord); + if( poSubExprVal == NULL ) + bError = TRUE; + else + { + apoValues.push_back(poSubExprVal); + anValueNeedsFree.push_back( TRUE ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Fetch the operator definition and function. */ +/* -------------------------------------------------------------------- */ + if( !bError ) + { + const swq_operation *poOp = + swq_op_registrar::GetOperator( (swq_op) nOperation ); + if( poOp == NULL ) + { + if( nOperation == SWQ_CUSTOM_FUNC ) + CPLError( CE_Failure, CPLE_AppDefined, + "Evaluate(): Unable to find definition for operator %s.", + string_value ); + else + CPLError( CE_Failure, CPLE_AppDefined, + "Evaluate(): Unable to find definition for operator %d.", + nOperation ); + poRetNode = NULL; + } + else + poRetNode = poOp->pfnEvaluator( this, &(apoValues[0]) ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < (int) apoValues.size(); i++ ) + { + if( anValueNeedsFree[i] ) + delete apoValues[i]; + } + + return poRetNode; +} + +/************************************************************************/ +/* ReplaceBetweenByGEAndLERecurse() */ +/************************************************************************/ + +void swq_expr_node::ReplaceBetweenByGEAndLERecurse() +{ + if( eNodeType != SNT_OPERATION ) + return; + + if( nOperation != SWQ_BETWEEN ) + { + for(int i=0;iReplaceBetweenByGEAndLERecurse(); + return; + } + + if( nSubExprCount != 3 ) + return; + + swq_expr_node* poExpr0 = papoSubExpr[0]; + swq_expr_node* poExpr1 = papoSubExpr[1]; + swq_expr_node* poExpr2 = papoSubExpr[2]; + + nSubExprCount = 2; + nOperation = SWQ_AND; + papoSubExpr[0] = new swq_expr_node(SWQ_GE); + papoSubExpr[0]->PushSubExpression(poExpr0); + papoSubExpr[0]->PushSubExpression(poExpr1); + papoSubExpr[1] = new swq_expr_node(SWQ_LE); + papoSubExpr[1]->PushSubExpression(poExpr0->Clone()); + papoSubExpr[1]->PushSubExpression(poExpr2); +} diff --git a/bazaar/plugin/gdal/ogr/swq_op_general.cpp b/bazaar/plugin/gdal/ogr/swq_op_general.cpp new file mode 100644 index 000000000..bef9f5e58 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/swq_op_general.cpp @@ -0,0 +1,1409 @@ +/****************************************************************************** + * + * Component: OGR SQL Engine + * Purpose: Implementation of SWQGeneralEvaluator and SWQGeneralChecker + * functions used to represent functions during evaluation and + * parsing. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "swq.h" +#include "ogr_geometry.h" + +/************************************************************************/ +/* swq_test_like() */ +/* */ +/* Does input match pattern? */ +/************************************************************************/ + +int swq_test_like( const char *input, const char *pattern, char chEscape ) + +{ + if( input == NULL || pattern == NULL ) + return 0; + + while( *input != '\0' ) + { + if( *pattern == '\0' ) + return 0; + + else if( *pattern == chEscape ) + { + pattern++; + if( *pattern == '\0' ) + return 0; + if( tolower(*pattern) != tolower(*input) ) + return 0; + else + { + input++; + pattern++; + } + } + + else if( *pattern == '_' ) + { + input++; + pattern++; + } + else if( *pattern == '%' ) + { + int eat; + + if( pattern[1] == '\0' ) + return 1; + + /* try eating varying amounts of the input till we get a positive*/ + for( eat = 0; input[eat] != '\0'; eat++ ) + { + if( swq_test_like(input+eat,pattern+1, chEscape) ) + return 1; + } + + return 0; + } + else + { + if( tolower(*pattern) != tolower(*input) ) + return 0; + else + { + input++; + pattern++; + } + } + } + + if( *pattern != '\0' && strcmp(pattern,"%") != 0 ) + return 0; + else + return 1; +} + +/************************************************************************/ +/* OGRHStoreGetValue() */ +/************************************************************************/ + +static char* OGRHStoreCheckEnd(char* pszIter, int bIsKey) +{ + pszIter ++; + for( ; *pszIter != '\0'; pszIter ++ ) + { + if( bIsKey ) + { + if( *pszIter == ' ' ) + ; + else if( *pszIter == '=' && pszIter[1] == '>' ) + return pszIter + 2; + else + return NULL; + } + else + { + if( *pszIter == ' ' ) + ; + else if( *pszIter == ',' ) + return pszIter + 1; + else + return NULL; + } + } + return pszIter; +} + +static char* OGRHStoreGetNextString(char* pszIter, + char** ppszOut, + int bIsKey) +{ + char ch; + int bInString = FALSE; + char* pszOut = NULL; + *ppszOut = NULL; + for( ; (ch = *pszIter) != '\0'; pszIter ++ ) + { + if( bInString ) + { + if( ch == '"' ) + { + *pszOut = '\0'; + return OGRHStoreCheckEnd(pszIter, bIsKey); + } + else if( ch == '\\') + { + pszIter ++; + if( (ch = *pszIter) == '\0' ) + return NULL; + } + *pszOut = ch; + pszOut ++; + } + else + { + if( ch == ' ' ) + { + if( pszOut != NULL ) + { + *pszIter = '\0'; + return OGRHStoreCheckEnd(pszIter, bIsKey); + } + } + else if( bIsKey && ch == '=' && pszIter[1] == '>' ) + { + if( pszOut != NULL ) + { + *pszIter = '\0'; + return pszIter + 2; + } + } + else if( !bIsKey && ch == ',' ) + { + if( pszOut != NULL ) + { + *pszIter = '\0'; + return pszIter + 1; + } + } + else if( ch == '"' ) + { + pszOut = *ppszOut = pszIter + 1; + bInString = TRUE; + } + else if( pszOut == NULL ) + pszOut = *ppszOut = pszIter; + } + } + + if( !bInString && pszOut != NULL ) + { + return pszIter; + } + return NULL; +} + +static char* OGRHStoreGetNextKeyValue(char* pszHStore, + char** ppszKey, + char** ppszValue) +{ + char* pszNext = OGRHStoreGetNextString(pszHStore, ppszKey, TRUE); + if( pszNext == NULL || *pszNext == '\0' ) + return NULL; + return OGRHStoreGetNextString(pszNext, ppszValue, FALSE); +} + +char* OGRHStoreGetValue(const char* pszHStore, const char* pszSearchedKey) +{ + char* pszHStoreDup = CPLStrdup(pszHStore); + char* pszHStoreIter = pszHStoreDup; + char* pszRet = NULL; + + while( TRUE ) + { + char* pszKey, *pszValue; + pszHStoreIter = OGRHStoreGetNextKeyValue(pszHStoreIter, &pszKey, &pszValue); + if( pszHStoreIter == NULL ) + { + break; + } + if( strcmp(pszKey, pszSearchedKey) == 0 ) + { + pszRet = CPLStrdup(pszValue); + break; + } + if( *pszHStoreIter == '\0' ) + { + break; + } + } + CPLFree(pszHStoreDup); + return pszRet; +} + +/************************************************************************/ +/* SWQGeneralEvaluator() */ +/************************************************************************/ + +swq_expr_node *SWQGeneralEvaluator( swq_expr_node *node, + swq_expr_node **sub_node_values ) + +{ + swq_expr_node *poRet = NULL; + +/* -------------------------------------------------------------------- */ +/* Floating point operations. */ +/* -------------------------------------------------------------------- */ + if( sub_node_values[0]->field_type == SWQ_FLOAT + || (node->nSubExprCount > 1 + && sub_node_values[1]->field_type == SWQ_FLOAT) ) + + { + poRet = new swq_expr_node(0); + poRet->field_type = node->field_type; + + if( SWQ_IS_INTEGER(sub_node_values[0]->field_type) ) + sub_node_values[0]->float_value = (double) sub_node_values[0]->int_value; + if( node->nSubExprCount > 1 && + SWQ_IS_INTEGER(sub_node_values[1]->field_type) ) + sub_node_values[1]->float_value = (double)sub_node_values[1]->int_value; + + if( node->nOperation != SWQ_ISNULL ) + { + for( int i = 0; i < node->nSubExprCount; i++ ) + { + if( sub_node_values[i]->is_null ) + { + if( poRet->field_type == SWQ_BOOLEAN ) + { + poRet->int_value = FALSE; + return poRet; + } + else if( poRet->field_type == SWQ_FLOAT ) + { + poRet->float_value = 0; + poRet->is_null = 1; + return poRet; + } + else if( SWQ_IS_INTEGER(poRet->field_type) || + node->nOperation == SWQ_MODULUS ) + { + poRet->field_type = SWQ_INTEGER; + poRet->int_value = 0; + poRet->is_null = 1; + return poRet; + } + } + } + } + + switch( (swq_op) node->nOperation ) + { + case SWQ_EQ: + poRet->int_value = sub_node_values[0]->float_value + == sub_node_values[1]->float_value; + break; + + case SWQ_NE: + poRet->int_value = sub_node_values[0]->float_value + != sub_node_values[1]->float_value; + break; + + case SWQ_GT: + poRet->int_value = sub_node_values[0]->float_value + > sub_node_values[1]->float_value; + break; + + case SWQ_LT: + poRet->int_value = sub_node_values[0]->float_value + < sub_node_values[1]->float_value; + break; + + case SWQ_GE: + poRet->int_value = sub_node_values[0]->float_value + >= sub_node_values[1]->float_value; + break; + + case SWQ_LE: + poRet->int_value = sub_node_values[0]->float_value + <= sub_node_values[1]->float_value; + break; + + case SWQ_IN: + { + int i; + poRet->int_value = 0; + for( i = 1; i < node->nSubExprCount; i++ ) + { + if( sub_node_values[0]->float_value + == sub_node_values[i]->float_value ) + { + poRet->int_value = 1; + break; + } + } + } + break; + + case SWQ_BETWEEN: + poRet->int_value = sub_node_values[0]->float_value + >= sub_node_values[1]->float_value && + sub_node_values[0]->float_value + <= sub_node_values[2]->float_value; + break; + + case SWQ_ISNULL: + poRet->int_value = sub_node_values[0]->is_null; + break; + + case SWQ_ADD: + poRet->float_value = sub_node_values[0]->float_value + + sub_node_values[1]->float_value; + break; + + case SWQ_SUBTRACT: + poRet->float_value = sub_node_values[0]->float_value + - sub_node_values[1]->float_value; + break; + + case SWQ_MULTIPLY: + poRet->float_value = sub_node_values[0]->float_value + * sub_node_values[1]->float_value; + break; + + case SWQ_DIVIDE: + if( sub_node_values[1]->float_value == 0 ) + poRet->float_value = INT_MAX; + else + poRet->float_value = sub_node_values[0]->float_value + / sub_node_values[1]->float_value; + break; + + case SWQ_MODULUS: + { + GIntBig nRight = (GIntBig) sub_node_values[1]->float_value; + poRet->field_type = SWQ_INTEGER; + if (nRight == 0) + poRet->int_value = INT_MAX; + else + poRet->int_value = ((GIntBig) sub_node_values[0]->float_value) + % nRight; + break; + } + + default: + CPLAssert( FALSE ); + delete poRet; + poRet = NULL; + break; + } + } +/* -------------------------------------------------------------------- */ +/* integer/boolean operations. */ +/* -------------------------------------------------------------------- */ + else if( SWQ_IS_INTEGER(sub_node_values[0]->field_type) + || sub_node_values[0]->field_type == SWQ_BOOLEAN ) + { + poRet = new swq_expr_node(0); + poRet->field_type = node->field_type; + + if( node->nOperation != SWQ_ISNULL ) + { + for( int i = 0; i < node->nSubExprCount; i++ ) + { + if( sub_node_values[i]->is_null ) + { + if( poRet->field_type == SWQ_BOOLEAN ) + { + poRet->int_value = FALSE; + return poRet; + } + else if( SWQ_IS_INTEGER(poRet->field_type) ) + { + poRet->int_value = 0; + poRet->is_null = 1; + return poRet; + } + } + } + } + + switch( (swq_op) node->nOperation ) + { + case SWQ_AND: + poRet->int_value = sub_node_values[0]->int_value + && sub_node_values[1]->int_value; + break; + + case SWQ_OR: + poRet->int_value = sub_node_values[0]->int_value + || sub_node_values[1]->int_value; + break; + + case SWQ_NOT: + poRet->int_value = !sub_node_values[0]->int_value; + break; + + case SWQ_EQ: + poRet->int_value = sub_node_values[0]->int_value + == sub_node_values[1]->int_value; + break; + + case SWQ_NE: + poRet->int_value = sub_node_values[0]->int_value + != sub_node_values[1]->int_value; + break; + + case SWQ_GT: + poRet->int_value = sub_node_values[0]->int_value + > sub_node_values[1]->int_value; + break; + + case SWQ_LT: + poRet->int_value = sub_node_values[0]->int_value + < sub_node_values[1]->int_value; + break; + + case SWQ_GE: + poRet->int_value = sub_node_values[0]->int_value + >= sub_node_values[1]->int_value; + break; + + case SWQ_LE: + poRet->int_value = sub_node_values[0]->int_value + <= sub_node_values[1]->int_value; + break; + + case SWQ_IN: + { + int i; + poRet->int_value = 0; + for( i = 1; i < node->nSubExprCount; i++ ) + { + if( sub_node_values[0]->int_value + == sub_node_values[i]->int_value ) + { + poRet->int_value = 1; + break; + } + } + } + break; + + case SWQ_BETWEEN: + poRet->int_value = sub_node_values[0]->int_value + >= sub_node_values[1]->int_value && + sub_node_values[0]->int_value + <= sub_node_values[2]->int_value; + break; + + case SWQ_ISNULL: + poRet->int_value = sub_node_values[0]->is_null; + break; + + case SWQ_ADD: + poRet->int_value = sub_node_values[0]->int_value + + sub_node_values[1]->int_value; + break; + + case SWQ_SUBTRACT: + poRet->int_value = sub_node_values[0]->int_value + - sub_node_values[1]->int_value; + break; + + case SWQ_MULTIPLY: + poRet->int_value = sub_node_values[0]->int_value + * sub_node_values[1]->int_value; + break; + + case SWQ_DIVIDE: + if( sub_node_values[1]->int_value == 0 ) + poRet->int_value = INT_MAX; + else + poRet->int_value = sub_node_values[0]->int_value + / sub_node_values[1]->int_value; + break; + + case SWQ_MODULUS: + if( sub_node_values[1]->int_value == 0 ) + poRet->int_value = INT_MAX; + else + poRet->int_value = sub_node_values[0]->int_value + % sub_node_values[1]->int_value; + break; + + default: + CPLAssert( FALSE ); + delete poRet; + poRet = NULL; + break; + } + } + +/* -------------------------------------------------------------------- */ +/* String operations. */ +/* -------------------------------------------------------------------- */ + else + { + poRet = new swq_expr_node(0); + poRet->field_type = node->field_type; + + if( node->nOperation != SWQ_ISNULL ) + { + for( int i = 0; i < node->nSubExprCount; i++ ) + { + if( sub_node_values[i]->is_null ) + { + if( poRet->field_type == SWQ_BOOLEAN ) + { + poRet->int_value = FALSE; + return poRet; + } + else if( poRet->field_type == SWQ_STRING ) + { + poRet->string_value = CPLStrdup(""); + poRet->is_null = 1; + return poRet; + } + } + } + } + + switch( (swq_op) node->nOperation ) + { + case SWQ_EQ: + { + /* When comparing timestamps, the +00 at the end might be discarded */ + /* if the other member has no explicit timezone */ + if( (sub_node_values[0]->field_type == SWQ_TIMESTAMP || + sub_node_values[0]->field_type == SWQ_STRING) && + (sub_node_values[1]->field_type == SWQ_TIMESTAMP || + sub_node_values[1]->field_type == SWQ_STRING) && + strlen(sub_node_values[0]->string_value) > 3 && + strlen(sub_node_values[1]->string_value) > 3 && + (strcmp(sub_node_values[0]->string_value + strlen(sub_node_values[0]->string_value)-3, "+00") == 0 && + sub_node_values[1]->string_value[strlen(sub_node_values[1]->string_value)-3] == ':') ) + { + poRet->int_value = + EQUALN(sub_node_values[0]->string_value, + sub_node_values[1]->string_value, + strlen(sub_node_values[1]->string_value)); + } + else if( (sub_node_values[0]->field_type == SWQ_TIMESTAMP || + sub_node_values[0]->field_type == SWQ_STRING) && + (sub_node_values[1]->field_type == SWQ_TIMESTAMP || + sub_node_values[1]->field_type == SWQ_STRING) && + strlen(sub_node_values[0]->string_value) > 3 && + strlen(sub_node_values[1]->string_value) > 3 && + (sub_node_values[0]->string_value[strlen(sub_node_values[0]->string_value)-3] == ':') && + strcmp(sub_node_values[1]->string_value + strlen(sub_node_values[1]->string_value)-3, "+00") == 0) + { + poRet->int_value = + EQUALN(sub_node_values[0]->string_value, + sub_node_values[1]->string_value, + strlen(sub_node_values[0]->string_value)); + } + else + { + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) == 0; + } + break; + } + + case SWQ_NE: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) != 0; + break; + + case SWQ_GT: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) > 0; + break; + + case SWQ_LT: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) < 0; + break; + + case SWQ_GE: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) >= 0; + break; + + case SWQ_LE: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) <= 0; + break; + + case SWQ_IN: + { + int i; + poRet->int_value = 0; + for( i = 1; i < node->nSubExprCount; i++ ) + { + if( strcasecmp(sub_node_values[0]->string_value, + sub_node_values[i]->string_value) == 0 ) + { + poRet->int_value = 1; + break; + } + } + } + break; + + case SWQ_BETWEEN: + poRet->int_value = + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[1]->string_value) >= 0 && + strcasecmp(sub_node_values[0]->string_value, + sub_node_values[2]->string_value) <= 0; + break; + + case SWQ_LIKE: + { + char chEscape = '\0'; + if( node->nSubExprCount == 3 ) + chEscape = sub_node_values[2]->string_value[0]; + poRet->int_value = swq_test_like(sub_node_values[0]->string_value, + sub_node_values[1]->string_value, + chEscape); + break; + } + + case SWQ_ISNULL: + poRet->int_value = sub_node_values[0]->is_null; + break; + + case SWQ_CONCAT: + case SWQ_ADD: + { + CPLString osResult = sub_node_values[0]->string_value; + int i; + + for( i = 1; i < node->nSubExprCount; i++ ) + osResult += sub_node_values[i]->string_value; + + poRet->string_value = CPLStrdup(osResult); + poRet->is_null = sub_node_values[0]->is_null; + break; + } + + case SWQ_SUBSTR: + { + int nOffset, nSize; + const char *pszSrcStr = sub_node_values[0]->string_value; + + if( SWQ_IS_INTEGER(sub_node_values[1]->field_type) ) + nOffset = (int)sub_node_values[1]->int_value; + else if( sub_node_values[1]->field_type == SWQ_FLOAT ) + nOffset = (int) sub_node_values[1]->float_value; + else + nOffset = 0; + + if( node->nSubExprCount < 3 ) + nSize = 100000; + else if( SWQ_IS_INTEGER(sub_node_values[2]->field_type) ) + nSize = (int)sub_node_values[2]->int_value; + else if( sub_node_values[2]->field_type == SWQ_FLOAT ) + nSize = (int) sub_node_values[2]->float_value; + else + nSize = 0; + + int nSrcStrLen = (int)strlen(pszSrcStr); + + + /* In SQL, the first character is at offset 1 */ + /* And 0 is considered as 1 */ + if (nOffset > 0) + nOffset --; + /* Some implementations allow negative offsets, to start */ + /* from the end of the string */ + else if( nOffset < 0 ) + { + if( nSrcStrLen + nOffset >= 0 ) + nOffset = nSrcStrLen + nOffset; + else + nOffset = 0; + } + + if( nSize < 0 || nOffset > nSrcStrLen ) + { + nOffset = 0; + nSize = 0; + } + else if( nOffset + nSize > nSrcStrLen ) + nSize = nSrcStrLen - nOffset; + + CPLString osResult = pszSrcStr + nOffset; + if( (int)osResult.size() > nSize ) + osResult.resize( nSize ); + + poRet->string_value = CPLStrdup(osResult); + poRet->is_null = sub_node_values[0]->is_null; + break; + } + + case SWQ_HSTORE_GET_VALUE: + { + const char *pszHStore = sub_node_values[0]->string_value; + const char *pszSearchedKey = sub_node_values[1]->string_value; + char* pszRet = OGRHStoreGetValue(pszHStore, pszSearchedKey); + poRet->string_value = pszRet ? pszRet : CPLStrdup(""); + poRet->is_null = (pszRet == NULL); + break; + } + + default: + CPLAssert( FALSE ); + delete poRet; + poRet = NULL; + break; + } + } + + return poRet; +} + +/************************************************************************/ +/* SWQAutoPromoteIntegerToInteger64OrFloat() */ +/************************************************************************/ + +static void SWQAutoPromoteIntegerToInteger64OrFloat( swq_expr_node *poNode ) + +{ + if( poNode->nSubExprCount < 2 ) + return; + + swq_field_type eArgType = poNode->papoSubExpr[0]->field_type; + int i; + + // We allow mixes of integer, integer64 and float, and string and dates. + // When encountered, we promote integers/integer64 to floats, + // integer to integer64 and strings to dates. We do that now. + for( i = 1; i < poNode->nSubExprCount; i++ ) + { + swq_expr_node *poSubNode = poNode->papoSubExpr[i]; + if( SWQ_IS_INTEGER(eArgType) + && poSubNode->field_type == SWQ_FLOAT ) + eArgType = SWQ_FLOAT; + else if( eArgType == SWQ_INTEGER + && poSubNode->field_type == SWQ_INTEGER64 ) + eArgType = SWQ_INTEGER64; + } + + for( i = 0; i < poNode->nSubExprCount; i++ ) + { + swq_expr_node *poSubNode = poNode->papoSubExpr[i]; + + if( eArgType == SWQ_FLOAT + && SWQ_IS_INTEGER(poSubNode->field_type) ) + { + if( poSubNode->eNodeType == SNT_CONSTANT ) + { + poSubNode->float_value = (double) poSubNode->int_value; + poSubNode->field_type = SWQ_FLOAT; + } + } + else if( eArgType == SWQ_INTEGER64 && poSubNode->field_type == SWQ_INTEGER ) + { + if( poSubNode->eNodeType == SNT_CONSTANT ) + { + poSubNode->field_type = SWQ_INTEGER64; + } + } + } +} + +/************************************************************************/ +/* SWQAutoPromoteStringToDateTime() */ +/************************************************************************/ + +static void SWQAutoPromoteStringToDateTime( swq_expr_node *poNode ) + +{ + if( poNode->nSubExprCount < 2 ) + return; + + swq_field_type eArgType = poNode->papoSubExpr[0]->field_type; + int i; + + // We allow mixes of integer and float, and string and dates. + // When encountered, we promote integers to floats, and strings to + // dates. We do that now. + for( i = 1; i < poNode->nSubExprCount; i++ ) + { + swq_expr_node *poSubNode = poNode->papoSubExpr[i]; + + if( eArgType == SWQ_STRING + && (poSubNode->field_type == SWQ_DATE + || poSubNode->field_type == SWQ_TIME + || poSubNode->field_type == SWQ_TIMESTAMP) ) + eArgType = SWQ_TIMESTAMP; + } + + for( i = 0; i < poNode->nSubExprCount; i++ ) + { + swq_expr_node *poSubNode = poNode->papoSubExpr[i]; + + if( eArgType == SWQ_TIMESTAMP + && (poSubNode->field_type == SWQ_STRING + || poSubNode->field_type == SWQ_DATE + || poSubNode->field_type == SWQ_TIME) ) + { + if( poSubNode->eNodeType == SNT_CONSTANT ) + { + poSubNode->field_type = SWQ_TIMESTAMP; + } + } + } +} + +/************************************************************************/ +/* SWQAutoConvertStringToNumeric() */ +/* */ +/* Convert string constants to integer or float constants */ +/* when there is a mix of arguments of type numeric and string */ +/************************************************************************/ + +static void SWQAutoConvertStringToNumeric( swq_expr_node *poNode ) + +{ + if( poNode->nSubExprCount < 2 ) + return; + + swq_field_type eArgType = poNode->papoSubExpr[0]->field_type; + int i; + + for( i = 1; i < poNode->nSubExprCount; i++ ) + { + swq_expr_node *poSubNode = poNode->papoSubExpr[i]; + + /* identify the mixture of the argument type */ + if( (eArgType == SWQ_STRING + && (SWQ_IS_INTEGER(poSubNode->field_type) + || poSubNode->field_type == SWQ_FLOAT)) || + (SWQ_IS_INTEGER(eArgType) + && poSubNode->field_type == SWQ_STRING) ) + { + eArgType = SWQ_FLOAT; + break; + } + } + + for( i = 0; i < poNode->nSubExprCount; i++ ) + { + swq_expr_node *poSubNode = poNode->papoSubExpr[i]; + + if( eArgType == SWQ_FLOAT + && poSubNode->field_type == SWQ_STRING ) + { + if( poSubNode->eNodeType == SNT_CONSTANT ) + { + /* apply the string to numeric conversion */ + char* endPtr = NULL; + poSubNode->float_value = CPLStrtod(poSubNode->string_value, &endPtr); + if ( !(endPtr == NULL || *endPtr == '\0') ) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Conversion failed when converting the string value '%s' to data type float.", + poSubNode->string_value); + continue; + } + + /* we should also fill the integer value in this case */ + poSubNode->int_value = (GIntBig)poSubNode->float_value; + poSubNode->field_type = SWQ_FLOAT; + } + } + } +} + +/************************************************************************/ +/* SWQCheckSubExprAreNotGeometries() */ +/************************************************************************/ + +static int SWQCheckSubExprAreNotGeometries( swq_expr_node *poNode ) +{ + for( int i = 0; i < poNode->nSubExprCount; i++ ) + { + if( poNode->papoSubExpr[i]->field_type == SWQ_GEOMETRY ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot use geometry field in this operation." ); + return FALSE; + } + } + return TRUE; +} + +/************************************************************************/ +/* SWQGeneralChecker() */ +/* */ +/* Check the general purpose functions have appropriate types, */ +/* and count and indicate the function return type under the */ +/* circumstances. */ +/************************************************************************/ + +swq_field_type SWQGeneralChecker( swq_expr_node *poNode, + int bAllowMismatchTypeOnFieldComparison ) + +{ + swq_field_type eRetType = SWQ_ERROR; + swq_field_type eArgType = SWQ_OTHER; + int nArgCount = -1; + + switch( (swq_op) poNode->nOperation ) + { + case SWQ_AND: + case SWQ_OR: + case SWQ_NOT: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_BOOLEAN; + break; + + case SWQ_EQ: + case SWQ_NE: + case SWQ_GT: + case SWQ_LT: + case SWQ_GE: + case SWQ_LE: + case SWQ_IN: + case SWQ_BETWEEN: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_BOOLEAN; + SWQAutoConvertStringToNumeric( poNode ); + SWQAutoPromoteIntegerToInteger64OrFloat( poNode ); + SWQAutoPromoteStringToDateTime( poNode ); + eArgType = poNode->papoSubExpr[0]->field_type; + break; + + case SWQ_ISNULL: + eRetType = SWQ_BOOLEAN; + break; + + case SWQ_LIKE: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_BOOLEAN; + eArgType = SWQ_STRING; + break; + + case SWQ_MODULUS: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_INTEGER; + eArgType = SWQ_INTEGER; + break; + + case SWQ_ADD: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + SWQAutoPromoteIntegerToInteger64OrFloat( poNode ); + if( poNode->papoSubExpr[0]->field_type == SWQ_STRING ) + eRetType = eArgType = SWQ_STRING; + else if( poNode->papoSubExpr[0]->field_type == SWQ_FLOAT ) + eRetType = eArgType = SWQ_FLOAT; + else if( poNode->papoSubExpr[0]->field_type == SWQ_INTEGER64 ) + eRetType = eArgType = SWQ_INTEGER64; + else + eRetType = eArgType = SWQ_INTEGER; + break; + + case SWQ_SUBTRACT: + case SWQ_MULTIPLY: + case SWQ_DIVIDE: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + SWQAutoPromoteIntegerToInteger64OrFloat( poNode ); + if( poNode->papoSubExpr[0]->field_type == SWQ_FLOAT ) + eRetType = eArgType = SWQ_FLOAT; + else if( poNode->papoSubExpr[0]->field_type == SWQ_INTEGER64 ) + eRetType = eArgType = SWQ_INTEGER64; + else + eRetType = eArgType = SWQ_INTEGER; + break; + + case SWQ_CONCAT: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_STRING; + eArgType = SWQ_STRING; + break; + + case SWQ_SUBSTR: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_STRING; + if( poNode->nSubExprCount > 3 || poNode->nSubExprCount < 2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Expected 2 or 3 arguments to SUBSTR(), but got %d.", + poNode->nSubExprCount ); + return SWQ_ERROR; + } + if( poNode->papoSubExpr[0]->field_type != SWQ_STRING + || poNode->papoSubExpr[1]->field_type != SWQ_INTEGER + || (poNode->nSubExprCount > 2 + && poNode->papoSubExpr[2]->field_type != SWQ_INTEGER) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Wrong argument type for SUBSTR(), expected SUBSTR(string,int,int) or SUBSTR(string,int)." ); + return SWQ_ERROR; + } + break; + + case SWQ_HSTORE_GET_VALUE: + if( !SWQCheckSubExprAreNotGeometries(poNode) ) + return SWQ_ERROR; + eRetType = SWQ_STRING; + if( poNode->nSubExprCount != 2 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Expected 2 arguments to hstore_get_value(), but got %d.", + poNode->nSubExprCount ); + return SWQ_ERROR; + } + if( poNode->papoSubExpr[0]->field_type != SWQ_STRING + || poNode->papoSubExpr[1]->field_type != SWQ_STRING ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Wrong argument type for hstore_get_value(), expected hstore_get_value(string,string)." ); + return SWQ_ERROR; + } + break; + + default: + { + const swq_operation *poOp = + swq_op_registrar::GetOperator((swq_op)poNode->nOperation); + + CPLError( CE_Failure, CPLE_AppDefined, + "SWQGeneralChecker() called on unsupported operation %s.", + poOp->pszName); + return SWQ_ERROR; + } + } +/* -------------------------------------------------------------------- */ +/* Check argument types. */ +/* -------------------------------------------------------------------- */ + if( eArgType != SWQ_OTHER ) + { + int i; + + if( SWQ_IS_INTEGER(eArgType) || eArgType == SWQ_BOOLEAN ) + eArgType = SWQ_FLOAT; + + for( i = 0; i < poNode->nSubExprCount; i++ ) + { + swq_field_type eThisArgType = poNode->papoSubExpr[i]->field_type; + if( SWQ_IS_INTEGER(eThisArgType) || eThisArgType == SWQ_BOOLEAN ) + eThisArgType = SWQ_FLOAT; + + if( eArgType != eThisArgType ) + { + // Conveniency for join. We allow comparing numeric columns + // and string columns, by casting string columns to numeric + if( bAllowMismatchTypeOnFieldComparison && + poNode->nSubExprCount == 2 && + poNode->nOperation == SWQ_EQ && + poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN && + poNode->papoSubExpr[i]->eNodeType == SNT_COLUMN && + eArgType == SWQ_FLOAT && eThisArgType == SWQ_STRING ) + { + swq_expr_node* poNewNode = new swq_expr_node(SWQ_CAST); + poNewNode->PushSubExpression(poNode->papoSubExpr[i]); + poNewNode->PushSubExpression(new swq_expr_node("FLOAT")); + SWQCastChecker(poNewNode, FALSE); + poNode->papoSubExpr[i] = poNewNode; + break; + } + if( bAllowMismatchTypeOnFieldComparison && + poNode->nSubExprCount == 2 && + poNode->nOperation == SWQ_EQ && + poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN && + poNode->papoSubExpr[i]->eNodeType == SNT_COLUMN && + eThisArgType == SWQ_FLOAT && eArgType == SWQ_STRING ) + { + swq_expr_node* poNewNode = new swq_expr_node(SWQ_CAST); + poNewNode->PushSubExpression(poNode->papoSubExpr[0]); + poNewNode->PushSubExpression(new swq_expr_node("FLOAT")); + SWQCastChecker(poNewNode, FALSE); + poNode->papoSubExpr[0] = poNewNode; + break; + } + + const swq_operation *poOp = + swq_op_registrar::GetOperator((swq_op)poNode->nOperation); + + CPLError( CE_Failure, CPLE_AppDefined, + "Type mismatch or improper type of arguments to %s operator.", + poOp->pszName ); + return SWQ_ERROR; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Validate the arg count if requested. */ +/* -------------------------------------------------------------------- */ + if( nArgCount != -1 + && nArgCount != poNode->nSubExprCount ) + { + const swq_operation *poOp = + swq_op_registrar::GetOperator((swq_op)poNode->nOperation); + + CPLError( CE_Failure, CPLE_AppDefined, + "Expected %d arguments to %s, but got %d arguments.", + nArgCount, + poOp->pszName, + poNode->nSubExprCount ); + return SWQ_ERROR; + } + + return eRetType; +} + +/************************************************************************/ +/* SWQCastEvaluator() */ +/************************************************************************/ + +swq_expr_node *SWQCastEvaluator( swq_expr_node *node, + swq_expr_node **sub_node_values ) + +{ + swq_expr_node *poRetNode = NULL; + swq_expr_node *poSrcNode = sub_node_values[0]; + + switch( node->field_type ) + { + case SWQ_INTEGER: + { + poRetNode = new swq_expr_node( 0 ); + poRetNode->is_null = poSrcNode->is_null; + + switch( poSrcNode->field_type ) + { + case SWQ_INTEGER: + case SWQ_BOOLEAN: + poRetNode->int_value = poSrcNode->int_value; + break; + + case SWQ_INTEGER64: + // TODO: warn in case of overflow ? + poRetNode->int_value = (int) poSrcNode->int_value; + break; + + case SWQ_FLOAT: + poRetNode->int_value = (int) poSrcNode->float_value; + break; + + default: + poRetNode->int_value = atoi(poSrcNode->string_value); + break; + } + } + break; + + case SWQ_INTEGER64: + { + poRetNode = new swq_expr_node( 0 ); + poRetNode->is_null = poSrcNode->is_null; + + switch( poSrcNode->field_type ) + { + case SWQ_INTEGER: + case SWQ_INTEGER64: + case SWQ_BOOLEAN: + poRetNode->int_value = poSrcNode->int_value; + break; + + case SWQ_FLOAT: + poRetNode->int_value = (GIntBig) poSrcNode->float_value; + break; + + default: + poRetNode->int_value = CPLAtoGIntBig(poSrcNode->string_value); + break; + } + } + break; + + case SWQ_FLOAT: + { + poRetNode = new swq_expr_node( 0.0 ); + poRetNode->is_null = poSrcNode->is_null; + + switch( poSrcNode->field_type ) + { + case SWQ_INTEGER: + case SWQ_INTEGER64: + case SWQ_BOOLEAN: + poRetNode->float_value = (double) poSrcNode->int_value; + break; + + case SWQ_FLOAT: + poRetNode->float_value = poSrcNode->float_value; + break; + + default: + poRetNode->float_value = CPLAtof(poSrcNode->string_value); + break; + } + } + break; + + case SWQ_GEOMETRY: + { + poRetNode = new swq_expr_node( (OGRGeometry*) NULL ); + if( !poSrcNode->is_null ) + { + switch( poSrcNode->field_type ) + { + case SWQ_GEOMETRY: + { + poRetNode->geometry_value = + poSrcNode->geometry_value->clone(); + poRetNode->is_null = FALSE; + break; + } + + case SWQ_STRING: + { + char* pszTmp = poSrcNode->string_value; + OGRGeometryFactory::createFromWkt(&pszTmp, NULL, + &(poRetNode->geometry_value)); + if( poRetNode->geometry_value != NULL ) + poRetNode->is_null = FALSE; + break; + } + + default: + break; + } + } + break; + } + + // everything else is a string. + default: + { + CPLString osRet; + + switch( poSrcNode->field_type ) + { + case SWQ_INTEGER: + case SWQ_BOOLEAN: + case SWQ_INTEGER64: + osRet.Printf( CPL_FRMT_GIB, poSrcNode->int_value ); + break; + + case SWQ_FLOAT: + osRet.Printf( "%.15g", poSrcNode->float_value ); + break; + + case SWQ_GEOMETRY: + { + if( poSrcNode->geometry_value != NULL ) + { + char* pszWKT; + poSrcNode->geometry_value->exportToWkt(&pszWKT); + osRet = pszWKT; + CPLFree(pszWKT); + } + else + osRet = ""; + break; + } + + default: + osRet = poSrcNode->string_value; + break; + } + + if( node->nSubExprCount > 2 ) + { + int nWidth; + + nWidth = (int) sub_node_values[2]->int_value; + if( nWidth > 0 && (int) strlen(osRet) > nWidth ) + osRet.resize(nWidth); + } + + poRetNode = new swq_expr_node( osRet.c_str() ); + poRetNode->is_null = poSrcNode->is_null; + } + } + + return poRetNode; +} + +/************************************************************************/ +/* SWQCastChecker() */ +/************************************************************************/ + +swq_field_type SWQCastChecker( swq_expr_node *poNode, + CPL_UNUSED int bAllowMismatchTypeOnFieldComparison ) + +{ + swq_field_type eType = SWQ_ERROR; + const char *pszTypeName = poNode->papoSubExpr[1]->string_value; + + if( poNode->papoSubExpr[0]->field_type == SWQ_GEOMETRY && + !(EQUAL(pszTypeName,"character") || + EQUAL(pszTypeName,"geometry")) ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot cast geometry to %s", + pszTypeName ); + } + + else if( EQUAL(pszTypeName,"boolean") ) + eType = SWQ_BOOLEAN; + else if( EQUAL(pszTypeName,"character") ) + eType = SWQ_STRING; + else if( EQUAL(pszTypeName,"integer") ) + eType = SWQ_INTEGER; + else if( EQUAL(pszTypeName,"bigint") ) + eType = SWQ_INTEGER64; + else if( EQUAL(pszTypeName,"smallint") ) + eType = SWQ_INTEGER; + else if( EQUAL(pszTypeName,"float") ) + eType = SWQ_FLOAT; + else if( EQUAL(pszTypeName,"numeric") ) + eType = SWQ_FLOAT; + else if( EQUAL(pszTypeName,"timestamp") ) + eType = SWQ_TIMESTAMP; + else if( EQUAL(pszTypeName,"date") ) + eType = SWQ_DATE; + else if( EQUAL(pszTypeName,"time") ) + eType = SWQ_TIME; + else if( EQUAL(pszTypeName,"geometry") ) + { + if( !(poNode->papoSubExpr[0]->field_type == SWQ_GEOMETRY || + poNode->papoSubExpr[0]->field_type == SWQ_STRING) ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Cannot cast %s to geometry", + SWQFieldTypeToString(poNode->papoSubExpr[0]->field_type) ); + } + else + eType = SWQ_GEOMETRY; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognized typename %s in CAST operator.", + pszTypeName ); + } + + poNode->field_type = eType; + + return eType; +} diff --git a/bazaar/plugin/gdal/ogr/swq_op_registrar.cpp b/bazaar/plugin/gdal/ogr/swq_op_registrar.cpp new file mode 100644 index 000000000..b5445dcb1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/swq_op_registrar.cpp @@ -0,0 +1,125 @@ +/****************************************************************************** + * + * Component: OGR SQL Engine + * Purpose: Implementation of the swq_op_registrar class used to + * represent operations possible in an SQL expression. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "swq.h" + +static swq_field_type SWQColumnFuncChecker( swq_expr_node *poNode, + int bAllowMismatchTypeOnFieldComparison ); + +static const swq_operation swq_apsOperations[] = +{ + { "OR", SWQ_OR, SWQGeneralEvaluator, SWQGeneralChecker }, + { "AND", SWQ_AND, SWQGeneralEvaluator, SWQGeneralChecker }, + { "NOT", SWQ_NOT , SWQGeneralEvaluator, SWQGeneralChecker }, + { "=", SWQ_EQ , SWQGeneralEvaluator, SWQGeneralChecker }, + { "<>", SWQ_NE , SWQGeneralEvaluator, SWQGeneralChecker }, + { ">=", SWQ_GE , SWQGeneralEvaluator, SWQGeneralChecker }, + { "<=", SWQ_LE , SWQGeneralEvaluator, SWQGeneralChecker }, + { "<", SWQ_LT , SWQGeneralEvaluator, SWQGeneralChecker }, + { ">", SWQ_GT , SWQGeneralEvaluator, SWQGeneralChecker }, + { "LIKE", SWQ_LIKE , SWQGeneralEvaluator, SWQGeneralChecker }, + { "IS NULL", SWQ_ISNULL , SWQGeneralEvaluator, SWQGeneralChecker }, + { "IN", SWQ_IN , SWQGeneralEvaluator, SWQGeneralChecker }, + { "BETWEEN", SWQ_BETWEEN , SWQGeneralEvaluator, SWQGeneralChecker }, + { "+", SWQ_ADD , SWQGeneralEvaluator, SWQGeneralChecker }, + { "-", SWQ_SUBTRACT , SWQGeneralEvaluator, SWQGeneralChecker }, + { "*", SWQ_MULTIPLY , SWQGeneralEvaluator, SWQGeneralChecker }, + { "/", SWQ_DIVIDE , SWQGeneralEvaluator, SWQGeneralChecker }, + { "%", SWQ_MODULUS , SWQGeneralEvaluator, SWQGeneralChecker }, + { "CONCAT", SWQ_CONCAT , SWQGeneralEvaluator, SWQGeneralChecker }, + { "SUBSTR", SWQ_SUBSTR , SWQGeneralEvaluator, SWQGeneralChecker }, + { "HSTORE_GET_VALUE", SWQ_HSTORE_GET_VALUE , SWQGeneralEvaluator, SWQGeneralChecker }, + + { "AVG", SWQ_AVG, SWQGeneralEvaluator, SWQColumnFuncChecker }, + { "MIN", SWQ_MIN, SWQGeneralEvaluator, SWQColumnFuncChecker }, + { "MAX", SWQ_MAX, SWQGeneralEvaluator, SWQColumnFuncChecker }, + { "COUNT", SWQ_COUNT, SWQGeneralEvaluator, SWQColumnFuncChecker }, + { "SUM", SWQ_SUM, SWQGeneralEvaluator, SWQColumnFuncChecker }, + + { "CAST", SWQ_CAST, SWQCastEvaluator, SWQCastChecker } +}; + +#define N_OPERATIONS (sizeof(swq_apsOperations) / sizeof(swq_apsOperations[0])) + +/************************************************************************/ +/* GetOperator() */ +/************************************************************************/ + +const swq_operation *swq_op_registrar::GetOperator( const char *pszName ) + +{ + unsigned int i; + for( i = 0; i < N_OPERATIONS; i++ ) + { + if( EQUAL(pszName,swq_apsOperations[i].pszName) ) + return &(swq_apsOperations[i]); + } + + return NULL; +} + +/************************************************************************/ +/* GetOperator() */ +/************************************************************************/ + +const swq_operation *swq_op_registrar::GetOperator( swq_op eOperator ) + +{ + unsigned int i; + + for( i = 0; i < N_OPERATIONS; i++ ) + { + if( eOperator == swq_apsOperations[i].eOperation ) + return &(swq_apsOperations[i]); + } + + return NULL; +} + +/************************************************************************/ +/* SWQColumnFuncChecker() */ +/* */ +/* Column summary functions are not legal in any context except */ +/* as a root operator on column definitions. They are removed */ +/* from this tree before checking so we just need to issue an */ +/* error if they are used in any other context. */ +/************************************************************************/ + +static swq_field_type SWQColumnFuncChecker( swq_expr_node *poNode, + CPL_UNUSED int bAllowMismatchTypeOnFieldComparison ) +{ + const swq_operation *poOp = + swq_op_registrar::GetOperator((swq_op)poNode->nOperation); + CPLError( CE_Failure, CPLE_AppDefined, + "Column Summary Function '%s' found in an inappropriate context.", + (poOp) ? poOp->pszName : "" ); + return SWQ_ERROR; +} diff --git a/bazaar/plugin/gdal/ogr/swq_parser.cpp b/bazaar/plugin/gdal/ogr/swq_parser.cpp new file mode 100644 index 000000000..4371b136f --- /dev/null +++ b/bazaar/plugin/gdal/ogr/swq_parser.cpp @@ -0,0 +1,2680 @@ +/* A Bison parser, made by GNU Bison 3.0. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.0" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse swqparse +#define yylex swqlex +#define yyerror swqerror +#define yydebug swqdebug +#define yynerrs swqnerrs + + +/* Copy the first part of user declarations. */ +#line 1 "swq_parser.y" /* yacc.c:339 */ + +/****************************************************************************** + * + * Component: OGR SQL Engine + * Purpose: expression and select parser grammar. + * Requires Bison 2.4.0 or newer to process. Use "make parser" target. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_geometry.h" +#include "swq.h" + +#define YYSTYPE swq_expr_node* + +/* Defining YYSTYPE_IS_TRIVIAL is needed because the parser is generated as a C++ file. */ +/* See http://www.gnu.org/s/bison/manual/html_node/Memory-Management.html that suggests */ +/* increase YYINITDEPTH instead, but this will consume memory. */ +/* Setting YYSTYPE_IS_TRIVIAL overcomes this limitation, but might be fragile because */ +/* it appears to be a non documented feature of Bison */ +#define YYSTYPE_IS_TRIVIAL 1 + + +#line 119 "swq_parser.cpp" /* yacc.c:339 */ + +# ifndef YY_NULL +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULL nullptr +# else +# define YY_NULL 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* In a future release of Bison, this section will be replaced + by #include "swq_parser.hpp". */ +#ifndef YY_SWQ_SWQ_PARSER_HPP_INCLUDED +# define YY_SWQ_SWQ_PARSER_HPP_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int swqdebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + END = 0, + SWQT_INTEGER_NUMBER = 258, + SWQT_FLOAT_NUMBER = 259, + SWQT_STRING = 260, + SWQT_IDENTIFIER = 261, + SWQT_IN = 262, + SWQT_LIKE = 263, + SWQT_ESCAPE = 264, + SWQT_BETWEEN = 265, + SWQT_NULL = 266, + SWQT_IS = 267, + SWQT_SELECT = 268, + SWQT_LEFT = 269, + SWQT_JOIN = 270, + SWQT_WHERE = 271, + SWQT_ON = 272, + SWQT_ORDER = 273, + SWQT_BY = 274, + SWQT_FROM = 275, + SWQT_AS = 276, + SWQT_ASC = 277, + SWQT_DESC = 278, + SWQT_DISTINCT = 279, + SWQT_CAST = 280, + SWQT_UNION = 281, + SWQT_ALL = 282, + SWQT_VALUE_START = 283, + SWQT_SELECT_START = 284, + SWQT_NOT = 285, + SWQT_OR = 286, + SWQT_AND = 287, + SWQT_UMINUS = 288, + SWQT_RESERVED_KEYWORD = 289 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int swqparse (swq_parse_context *context); + +#endif /* !YY_SWQ_SWQ_PARSER_HPP_INCLUDED */ + +/* Copy the second part of user declarations. */ + +#line 205 "swq_parser.cpp" /* yacc.c:358 */ + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef __attribute__ +/* This feature is available in gcc versions 2.5 and later. */ +# if (! defined __GNUC__ || __GNUC__ < 2 \ + || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)) +# define __attribute__(Spec) /* empty */ +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 20 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 416 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 48 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 20 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 87 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 185 + +/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned + by yylex, with out-of-bounds checking. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 289 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, without out-of-bounds checking. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 36, 2, 2, 2, 41, 2, 2, + 44, 45, 39, 37, 46, 38, 47, 40, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 34, 33, 35, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 42, 43 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 112, 112, 113, 118, 124, 129, 137, 145, 152, + 160, 168, 176, 184, 192, 200, 208, 216, 224, 232, + 245, 254, 268, 277, 292, 301, 315, 322, 336, 342, + 349, 356, 368, 373, 378, 382, 387, 392, 397, 413, + 420, 427, 434, 441, 448, 484, 492, 498, 505, 514, + 532, 552, 553, 556, 561, 567, 568, 570, 578, 579, + 582, 591, 602, 616, 638, 668, 702, 726, 755, 761, + 764, 765, 770, 771, 777, 784, 785, 788, 789, 792, + 798, 804, 812, 822, 833, 844, 857, 868 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of string\"", "error", "$undefined", "\"integer number\"", + "\"floating point number\"", "\"string\"", "\"identifier\"", "\"IN\"", + "\"LIKE\"", "\"ESCAPE\"", "\"BETWEEN\"", "\"NULL\"", "\"IS\"", + "\"SELECT\"", "\"LEFT\"", "\"JOIN\"", "\"WHERE\"", "\"ON\"", "\"ORDER\"", + "\"BY\"", "\"FROM\"", "\"AS\"", "\"ASC\"", "\"DESC\"", "\"DISTINCT\"", + "\"CAST\"", "\"UNION\"", "\"ALL\"", "SWQT_VALUE_START", + "SWQT_SELECT_START", "\"NOT\"", "\"OR\"", "\"AND\"", "'='", "'<'", "'>'", + "'!'", "'+'", "'-'", "'*'", "'/'", "'%'", "SWQT_UMINUS", + "\"reserved keyword\"", "'('", "')'", "','", "'.'", "$accept", "input", + "value_expr", "value_expr_list", "field_value", "value_expr_non_logical", + "type_def", "select_statement", "select_core", "opt_union_all", + "union_all", "select_field_list", "column_spec", "as_clause", + "opt_where", "opt_joins", "opt_order_by", "sort_spec_list", "sort_spec", + "table_def", YY_NULL +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 61, 60, 62, 33, 43, 45, 42, + 47, 37, 288, 289, 40, 41, 44, 46 +}; +# endif + +#define YYPACT_NINF -119 + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-119))) + +#define YYTABLE_NINF -1 + +#define yytable_value_is_error(Yytable_value) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + -9, 204, -6, 10, -119, -119, -119, -30, -119, -32, + 204, 240, 204, 342, -119, 356, 54, 12, -119, 4, + -119, 204, 35, 204, 380, -119, 263, 18, 204, 240, + 50, 87, 204, 204, 97, 112, 158, 33, 240, 240, + 240, 240, 240, -23, 194, -119, 297, 36, 28, 31, + 56, -119, -6, 256, 42, -119, 335, -119, 204, 82, + 72, -119, 70, 55, 204, 240, 247, 350, 204, 204, + -119, 204, 204, -119, 204, -119, 204, 80, 80, -119, + -119, -119, 148, 0, 94, -119, 118, -119, 47, 194, + 4, -119, -119, 204, -119, 120, 85, 204, 240, -119, + 204, 124, 284, -119, -119, -119, -119, -119, -119, 130, + 95, -119, 47, -119, 96, -5, 75, -119, -119, -119, + 100, 101, -119, -119, 356, 103, 204, 240, 108, 115, + 2, 75, 151, 159, -119, 153, 47, 150, 23, -119, + -119, -119, 356, 2, -119, 150, 2, 2, 47, 154, + 204, 152, 61, 83, -119, 152, -119, -119, 157, 204, + 342, 156, -119, -119, 173, -119, 174, -119, 204, 305, + 130, 134, 135, 305, -119, 116, -119, 136, -119, -119, + -119, -119, -119, 130, -119 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 2, 0, 0, 0, 32, 33, 34, 30, 37, 0, + 0, 0, 0, 3, 35, 5, 0, 0, 4, 55, + 1, 0, 0, 0, 8, 38, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 30, 0, 62, 60, 0, 58, 0, + 0, 51, 0, 29, 0, 31, 0, 36, 0, 18, + 0, 26, 0, 0, 0, 0, 7, 6, 0, 0, + 9, 0, 0, 12, 0, 13, 0, 39, 40, 41, + 42, 43, 0, 0, 0, 69, 0, 61, 0, 0, + 55, 57, 56, 0, 44, 0, 0, 0, 0, 27, + 0, 19, 0, 15, 16, 14, 10, 17, 11, 0, + 0, 63, 0, 68, 0, 82, 72, 59, 52, 28, + 46, 0, 22, 20, 24, 0, 0, 0, 30, 0, + 64, 72, 0, 0, 83, 0, 0, 70, 0, 45, + 23, 21, 25, 66, 65, 70, 84, 86, 0, 0, + 0, 75, 0, 0, 67, 75, 85, 87, 0, 0, + 71, 0, 53, 47, 0, 49, 0, 54, 0, 72, + 0, 0, 0, 72, 73, 79, 76, 78, 48, 50, + 74, 80, 81, 0, 77 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -119, -119, -1, -56, -106, 7, -119, 129, 167, 99, + -119, -39, -119, -61, 40, -118, 38, 11, -119, -108 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 3, 53, 54, 14, 15, 121, 18, 19, 51, + 52, 47, 48, 87, 151, 137, 162, 176, 177, 116 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_uint8 yytable[] = +{ + 13, 85, 96, 129, 131, 84, 55, 16, 85, 24, + 20, 26, 23, 145, 21, 46, 86, 22, 25, 1, + 2, 82, 56, 86, 83, 16, 152, 59, 149, 153, + 50, 66, 67, 70, 73, 75, 60, 119, 17, 111, + 158, 55, 133, 46, 125, 77, 78, 79, 80, 81, + 117, 174, 114, 115, 134, 180, 88, 4, 5, 6, + 43, 61, 58, 101, 175, 8, 76, 103, 104, 144, + 105, 106, 102, 107, 89, 108, 90, 175, 44, 9, + 62, 99, 154, 91, 10, 156, 157, 94, 46, 135, + 136, 97, 11, 45, 63, 64, 123, 65, 12, 100, + 4, 5, 6, 7, 98, 124, 163, 164, 8, 38, + 39, 40, 41, 42, 112, 4, 5, 6, 7, 40, + 41, 42, 9, 8, 113, 141, 120, 10, 165, 166, + 122, 68, 69, 126, 142, 11, 128, 9, 181, 182, + 130, 12, 10, 132, 138, 71, 139, 72, 140, 160, + 11, 4, 5, 6, 7, 22, 12, 146, 169, 8, + 143, 4, 5, 6, 7, 147, 150, 173, 148, 8, + 161, 159, 109, 9, 168, 170, 171, 172, 10, 178, + 179, 92, 183, 9, 49, 155, 11, 110, 10, 118, + 0, 74, 12, 167, 184, 0, 11, 4, 5, 6, + 43, 0, 12, 0, 0, 8, 0, 4, 5, 6, + 7, 0, 0, 0, 0, 8, 0, 0, 0, 9, + 0, 0, 0, 0, 10, 0, 0, 0, 0, 9, + 0, 0, 11, 45, 10, 0, 0, 0, 12, 0, + 0, 0, 11, 4, 5, 6, 7, 0, 12, 0, + 0, 8, 0, 0, 27, 28, 0, 29, 0, 30, + 0, 0, 0, 27, 28, 9, 29, 0, 30, 0, + 27, 28, 0, 29, 0, 30, 0, 31, 11, 33, + 34, 35, 36, 37, 12, 0, 31, 32, 33, 34, + 35, 36, 37, 31, 32, 33, 34, 35, 36, 37, + 0, 0, 93, 85, 27, 28, 0, 29, 57, 30, + 0, 0, 27, 28, 0, 29, 127, 30, 86, 135, + 136, 38, 39, 40, 41, 42, 0, 31, 32, 33, + 34, 35, 36, 37, 0, 31, 32, 33, 34, 35, + 36, 37, 27, 28, 0, 29, 0, 30, 0, 27, + 28, 0, 29, 0, 30, 0, 95, 27, 28, 0, + 29, 0, 30, 0, 0, 31, 32, 33, 34, 35, + 36, 37, 31, 32, 33, 34, 35, 36, 37, 0, + 31, 0, 0, 34, 35, 36, 37, 27, 28, 0, + 29, 0, 30, 38, 39, 40, 41, 42, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 34, 35, 36, 37 +}; + +static const yytype_int16 yycheck[] = +{ + 1, 6, 58, 109, 112, 44, 6, 13, 6, 10, + 0, 12, 44, 131, 44, 16, 21, 47, 11, 28, + 29, 44, 23, 21, 47, 13, 3, 28, 136, 6, + 26, 32, 33, 34, 35, 36, 29, 93, 44, 39, + 148, 6, 47, 44, 100, 38, 39, 40, 41, 42, + 89, 169, 5, 6, 115, 173, 20, 3, 4, 5, + 6, 11, 44, 64, 170, 11, 33, 68, 69, 130, + 71, 72, 65, 74, 46, 76, 45, 183, 24, 25, + 30, 11, 143, 27, 30, 146, 147, 45, 89, 14, + 15, 9, 38, 39, 7, 8, 97, 10, 44, 44, + 3, 4, 5, 6, 32, 98, 45, 46, 11, 37, + 38, 39, 40, 41, 20, 3, 4, 5, 6, 39, + 40, 41, 25, 11, 6, 126, 6, 30, 45, 46, + 45, 34, 35, 9, 127, 38, 6, 25, 22, 23, + 45, 44, 30, 47, 44, 33, 45, 35, 45, 150, + 38, 3, 4, 5, 6, 47, 44, 6, 159, 11, + 45, 3, 4, 5, 6, 6, 16, 168, 15, 11, + 18, 17, 24, 25, 17, 19, 3, 3, 30, 45, + 45, 52, 46, 25, 17, 145, 38, 39, 30, 90, + -1, 33, 44, 155, 183, -1, 38, 3, 4, 5, + 6, -1, 44, -1, -1, 11, -1, 3, 4, 5, + 6, -1, -1, -1, -1, 11, -1, -1, -1, 25, + -1, -1, -1, -1, 30, -1, -1, -1, -1, 25, + -1, -1, 38, 39, 30, -1, -1, -1, 44, -1, + -1, -1, 38, 3, 4, 5, 6, -1, 44, -1, + -1, 11, -1, -1, 7, 8, -1, 10, -1, 12, + -1, -1, -1, 7, 8, 25, 10, -1, 12, -1, + 7, 8, -1, 10, -1, 12, -1, 30, 38, 32, + 33, 34, 35, 36, 44, -1, 30, 31, 32, 33, + 34, 35, 36, 30, 31, 32, 33, 34, 35, 36, + -1, -1, 46, 6, 7, 8, -1, 10, 45, 12, + -1, -1, 7, 8, -1, 10, 32, 12, 21, 14, + 15, 37, 38, 39, 40, 41, -1, 30, 31, 32, + 33, 34, 35, 36, -1, 30, 31, 32, 33, 34, + 35, 36, 7, 8, -1, 10, -1, 12, -1, 7, + 8, -1, 10, -1, 12, -1, 21, 7, 8, -1, + 10, -1, 12, -1, -1, 30, 31, 32, 33, 34, + 35, 36, 30, 31, 32, 33, 34, 35, 36, -1, + 30, -1, -1, 33, 34, 35, 36, 7, 8, -1, + 10, -1, 12, 37, 38, 39, 40, 41, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 33, 34, 35, 36 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 28, 29, 49, 3, 4, 5, 6, 11, 25, + 30, 38, 44, 50, 52, 53, 13, 44, 55, 56, + 0, 44, 47, 44, 50, 53, 50, 7, 8, 10, + 12, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 6, 24, 39, 50, 59, 60, 56, + 26, 57, 58, 50, 51, 6, 50, 45, 44, 50, + 53, 11, 30, 7, 8, 10, 50, 50, 34, 35, + 50, 33, 35, 50, 33, 50, 33, 53, 53, 53, + 53, 53, 44, 47, 59, 6, 21, 61, 20, 46, + 45, 27, 55, 46, 45, 21, 51, 9, 32, 11, + 44, 50, 53, 50, 50, 50, 50, 50, 50, 24, + 39, 39, 20, 6, 5, 6, 67, 59, 57, 51, + 6, 54, 45, 50, 53, 51, 9, 32, 6, 52, + 45, 67, 47, 47, 61, 14, 15, 63, 44, 45, + 45, 50, 53, 45, 61, 63, 6, 6, 15, 67, + 16, 62, 3, 6, 61, 62, 61, 61, 67, 17, + 50, 18, 64, 45, 46, 45, 46, 64, 17, 50, + 19, 3, 3, 50, 63, 52, 65, 66, 45, 45, + 63, 22, 23, 46, 65 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 48, 49, 49, 49, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, + 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, + 54, 55, 55, 56, 56, 57, 57, 58, 59, 59, + 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, + 62, 62, 63, 63, 63, 64, 64, 65, 65, 66, + 66, 66, 67, 67, 67, 67, 67, 67 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 0, 2, 2, 1, 3, 3, 2, 3, + 4, 4, 3, 3, 4, 4, 4, 4, 3, 4, + 5, 6, 5, 6, 5, 6, 3, 4, 3, 1, + 1, 3, 1, 1, 1, 1, 3, 1, 2, 3, + 3, 3, 3, 3, 4, 6, 1, 4, 6, 4, + 6, 2, 4, 7, 8, 0, 2, 2, 1, 3, + 1, 2, 1, 3, 4, 5, 5, 6, 2, 1, + 0, 2, 0, 5, 6, 0, 3, 3, 1, 1, + 2, 2, 1, 2, 3, 4, 3, 4 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*----------------------------------------. +| Print this symbol's value on YYOUTPUT. | +`----------------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, swq_parse_context *context) +{ + FILE *yyo = yyoutput; + YYUSE (yyo); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + YYUSE (yytype); +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, swq_parse_context *context) +{ + YYFPRINTF (yyoutput, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep, context); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, swq_parse_context *context) +{ + unsigned long int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[yyssp[yyi + 1 - yynrhs]], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, context); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +yystrlen (const char *yystr) +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULL; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, swq_parse_context *context) +{ + YYUSE (yyvaluep); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yytype) + { + case 3: /* "integer number" */ +#line 107 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1181 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 4: /* "floating point number" */ +#line 107 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1187 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 5: /* "string" */ +#line 107 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1193 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 6: /* "identifier" */ +#line 107 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1199 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 50: /* value_expr */ +#line 108 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1205 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 51: /* value_expr_list */ +#line 108 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1211 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 52: /* field_value */ +#line 108 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1217 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 53: /* value_expr_non_logical */ +#line 108 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1223 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 54: /* type_def */ +#line 108 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1229 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + case 67: /* table_def */ +#line 108 "swq_parser.y" /* yacc.c:1257 */ + { delete ((*yyvaluep)); } +#line 1235 "swq_parser.cpp" /* yacc.c:1257 */ + break; + + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (swq_parse_context *context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs; + + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; /* workaround bug with gcc 4.1 -O2 */ memset(yyssa, 0, sizeof(yyssa)); + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, context); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 3: +#line 114 "swq_parser.y" /* yacc.c:1646 */ + { + context->poRoot = (yyvsp[0]); + } +#line 1505 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 4: +#line 119 "swq_parser.y" /* yacc.c:1646 */ + { + context->poRoot = (yyvsp[0]); + } +#line 1513 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 5: +#line 125 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + } +#line 1521 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 6: +#line 130 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_AND ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1532 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 7: +#line 138 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_OR ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1543 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 8: +#line 146 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_NOT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1553 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 9: +#line 153 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_EQ ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1564 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 10: +#line 161 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_NE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1575 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 11: +#line 169 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_NE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1586 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 12: +#line 177 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_LT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1597 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 13: +#line 185 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_GT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1608 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 14: +#line 193 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_LE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1619 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 15: +#line 201 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_LE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1630 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 16: +#line 209 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_LE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1641 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 17: +#line 217 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_GE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1652 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 18: +#line 225 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_LIKE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1663 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 19: +#line 233 "swq_parser.y" /* yacc.c:1646 */ + { + swq_expr_node *like; + like = new swq_expr_node( SWQ_LIKE ); + like->field_type = SWQ_BOOLEAN; + like->PushSubExpression( (yyvsp[-3]) ); + like->PushSubExpression( (yyvsp[0]) ); + + (yyval) = new swq_expr_node( SWQ_NOT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( like ); + } +#line 1679 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 20: +#line 246 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_LIKE ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-4]) ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1691 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 21: +#line 255 "swq_parser.y" /* yacc.c:1646 */ + { + swq_expr_node *like; + like = new swq_expr_node( SWQ_LIKE ); + like->field_type = SWQ_BOOLEAN; + like->PushSubExpression( (yyvsp[-5]) ); + like->PushSubExpression( (yyvsp[-2]) ); + like->PushSubExpression( (yyvsp[0]) ); + + (yyval) = new swq_expr_node( SWQ_NOT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( like ); + } +#line 1708 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 22: +#line 269 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[-1]); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->nOperation = SWQ_IN; + (yyval)->PushSubExpression( (yyvsp[-4]) ); + (yyval)->ReverseSubExpressions(); + } +#line 1720 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 23: +#line 278 "swq_parser.y" /* yacc.c:1646 */ + { + swq_expr_node *in; + + in = (yyvsp[-1]); + in->field_type = SWQ_BOOLEAN; + in->nOperation = SWQ_IN; + in->PushSubExpression( (yyvsp[-5]) ); + in->ReverseSubExpressions(); + + (yyval) = new swq_expr_node( SWQ_NOT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( in ); + } +#line 1738 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 24: +#line 293 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_BETWEEN ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-4]) ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1750 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 25: +#line 302 "swq_parser.y" /* yacc.c:1646 */ + { + swq_expr_node *between; + between = new swq_expr_node( SWQ_BETWEEN ); + between->field_type = SWQ_BOOLEAN; + between->PushSubExpression( (yyvsp[-5]) ); + between->PushSubExpression( (yyvsp[-2]) ); + between->PushSubExpression( (yyvsp[0]) ); + + (yyval) = new swq_expr_node( SWQ_NOT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( between ); + } +#line 1767 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 26: +#line 316 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_ISNULL ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( (yyvsp[-2]) ); + } +#line 1777 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 27: +#line 323 "swq_parser.y" /* yacc.c:1646 */ + { + swq_expr_node *isnull; + + isnull = new swq_expr_node( SWQ_ISNULL ); + isnull->field_type = SWQ_BOOLEAN; + isnull->PushSubExpression( (yyvsp[-3]) ); + + (yyval) = new swq_expr_node( SWQ_NOT ); + (yyval)->field_type = SWQ_BOOLEAN; + (yyval)->PushSubExpression( isnull ); + } +#line 1793 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 28: +#line 337 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + (yyvsp[0])->PushSubExpression( (yyvsp[-2]) ); + } +#line 1802 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 29: +#line 343 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_ARGUMENT_LIST ); /* temporary value */ + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1811 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 30: +#line 350 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); // validation deferred. + (yyval)->eNodeType = SNT_COLUMN; + (yyval)->field_index = (yyval)->table_index = -1; + } +#line 1821 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 31: +#line 357 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[-2]); // validation deferred. + (yyval)->eNodeType = SNT_COLUMN; + (yyval)->field_index = (yyval)->table_index = -1; + (yyval)->table_name = (yyval)->string_value; + (yyval)->string_value = CPLStrdup((yyvsp[0])->string_value); + delete (yyvsp[0]); + (yyvsp[0]) = NULL; + } +#line 1835 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 32: +#line 369 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + } +#line 1843 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 33: +#line 374 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + } +#line 1851 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 34: +#line 379 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + } +#line 1859 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 35: +#line 383 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[0]); + } +#line 1867 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 36: +#line 388 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[-1]); + } +#line 1875 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 37: +#line 393 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node((const char*)NULL); + } +#line 1883 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 38: +#line 398 "swq_parser.y" /* yacc.c:1646 */ + { + if ((yyvsp[0])->eNodeType == SNT_CONSTANT) + { + (yyval) = (yyvsp[0]); + (yyval)->int_value *= -1; + (yyval)->float_value *= -1; + } + else + { + (yyval) = new swq_expr_node( SWQ_MULTIPLY ); + (yyval)->PushSubExpression( new swq_expr_node(-1) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } + } +#line 1902 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 39: +#line 414 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_ADD ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1912 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 40: +#line 421 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_SUBTRACT ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1922 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 41: +#line 428 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_MULTIPLY ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1932 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 42: +#line 435 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_DIVIDE ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1942 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 43: +#line 442 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_MODULUS ); + (yyval)->PushSubExpression( (yyvsp[-2]) ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 1952 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 44: +#line 449 "swq_parser.y" /* yacc.c:1646 */ + { + const swq_operation *poOp = + swq_op_registrar::GetOperator( (yyvsp[-3])->string_value ); + + if( poOp == NULL ) + { + if( context->bAcceptCustomFuncs ) + { + (yyval) = (yyvsp[-1]); + (yyval)->eNodeType = SNT_OPERATION; + (yyval)->nOperation = SWQ_CUSTOM_FUNC; + (yyval)->string_value = CPLStrdup((yyvsp[-3])->string_value); + (yyval)->ReverseSubExpressions(); + delete (yyvsp[-3]); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Undefined function '%s' used.", + (yyvsp[-3])->string_value ); + delete (yyvsp[-3]); + delete (yyvsp[-1]); + YYERROR; + } + } + else + { + (yyval) = (yyvsp[-1]); + (yyval)->eNodeType = SNT_OPERATION; + (yyval)->nOperation = poOp->eOperation; + (yyval)->ReverseSubExpressions(); + delete (yyvsp[-3]); + } + } +#line 1991 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 45: +#line 485 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = (yyvsp[-1]); + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->ReverseSubExpressions(); + } +#line 2001 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 46: +#line 493 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_CAST ); + (yyval)->PushSubExpression( (yyvsp[0]) ); + } +#line 2010 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 47: +#line 499 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_CAST ); + (yyval)->PushSubExpression( (yyvsp[-1]) ); + (yyval)->PushSubExpression( (yyvsp[-3]) ); + } +#line 2020 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 48: +#line 506 "swq_parser.y" /* yacc.c:1646 */ + { + (yyval) = new swq_expr_node( SWQ_CAST ); + (yyval)->PushSubExpression( (yyvsp[-1]) ); + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[-5]) ); + } +#line 2031 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 49: +#line 515 "swq_parser.y" /* yacc.c:1646 */ + { + OGRwkbGeometryType eType = OGRFromOGCGeomType((yyvsp[-1])->string_value); + if( !EQUAL((yyvsp[-3])->string_value,"GEOMETRY") || + (wkbFlatten(eType) == wkbUnknown && + !EQUALN((yyvsp[-1])->string_value, "GEOMETRY", strlen("GEOMETRY"))) ) + { + yyerror (context, "syntax error"); + delete (yyvsp[-3]); + delete (yyvsp[-1]); + YYERROR; + } + (yyval) = new swq_expr_node( SWQ_CAST ); + (yyval)->PushSubExpression( (yyvsp[-1]) ); + (yyval)->PushSubExpression( (yyvsp[-3]) ); + } +#line 2051 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 50: +#line 533 "swq_parser.y" /* yacc.c:1646 */ + { + OGRwkbGeometryType eType = OGRFromOGCGeomType((yyvsp[-3])->string_value); + if( !EQUAL((yyvsp[-5])->string_value,"GEOMETRY") || + (wkbFlatten(eType) == wkbUnknown && + !EQUALN((yyvsp[-3])->string_value, "GEOMETRY", strlen("GEOMETRY"))) ) + { + yyerror (context, "syntax error"); + delete (yyvsp[-5]); + delete (yyvsp[-3]); + delete (yyvsp[-1]); + YYERROR; + } + (yyval) = new swq_expr_node( SWQ_CAST ); + (yyval)->PushSubExpression( (yyvsp[-1]) ); + (yyval)->PushSubExpression( (yyvsp[-3]) ); + (yyval)->PushSubExpression( (yyvsp[-5]) ); + } +#line 2073 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 53: +#line 557 "swq_parser.y" /* yacc.c:1646 */ + { + delete (yyvsp[-3]); + } +#line 2081 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 54: +#line 562 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->query_mode = SWQM_DISTINCT_LIST; + delete (yyvsp[-3]); + } +#line 2090 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 57: +#line 571 "swq_parser.y" /* yacc.c:1646 */ + { + swq_select* poNewSelect = new swq_select(); + context->poCurSelect->PushUnionAll(poNewSelect); + context->poCurSelect = poNewSelect; + } +#line 2100 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 60: +#line 583 "swq_parser.y" /* yacc.c:1646 */ + { + if( !context->poCurSelect->PushField( (yyvsp[0]) ) ) + { + delete (yyvsp[0]); + YYERROR; + } + } +#line 2112 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 61: +#line 592 "swq_parser.y" /* yacc.c:1646 */ + { + if( !context->poCurSelect->PushField( (yyvsp[-1]), (yyvsp[0])->string_value ) ) + { + delete (yyvsp[-1]); + delete (yyvsp[0]); + YYERROR; + } + delete (yyvsp[0]); + } +#line 2126 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 62: +#line 603 "swq_parser.y" /* yacc.c:1646 */ + { + swq_expr_node *poNode = new swq_expr_node(); + poNode->eNodeType = SNT_COLUMN; + poNode->string_value = CPLStrdup( "*" ); + poNode->table_index = poNode->field_index = -1; + + if( !context->poCurSelect->PushField( poNode ) ) + { + delete poNode; + YYERROR; + } + } +#line 2143 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 63: +#line 617 "swq_parser.y" /* yacc.c:1646 */ + { + CPLString osTableName; + + osTableName = (yyvsp[-2])->string_value; + + delete (yyvsp[-2]); + (yyvsp[-2]) = NULL; + + swq_expr_node *poNode = new swq_expr_node(); + poNode->eNodeType = SNT_COLUMN; + poNode->table_name = CPLStrdup(osTableName ); + poNode->string_value = CPLStrdup( "*" ); + poNode->table_index = poNode->field_index = -1; + + if( !context->poCurSelect->PushField( poNode ) ) + { + delete poNode; + YYERROR; + } + } +#line 2168 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 64: +#line 639 "swq_parser.y" /* yacc.c:1646 */ + { + // special case for COUNT(*), confirm it. + if( !EQUAL((yyvsp[-3])->string_value,"COUNT") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax Error with %s(*).", + (yyvsp[-3])->string_value ); + delete (yyvsp[-3]); + YYERROR; + } + + delete (yyvsp[-3]); + (yyvsp[-3]) = NULL; + + swq_expr_node *poNode = new swq_expr_node(); + poNode->eNodeType = SNT_COLUMN; + poNode->string_value = CPLStrdup( "*" ); + poNode->table_index = poNode->field_index = -1; + + swq_expr_node *count = new swq_expr_node( (swq_op)SWQ_COUNT ); + count->PushSubExpression( poNode ); + + if( !context->poCurSelect->PushField( count ) ) + { + delete count; + YYERROR; + } + } +#line 2201 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 65: +#line 669 "swq_parser.y" /* yacc.c:1646 */ + { + // special case for COUNT(*), confirm it. + if( !EQUAL((yyvsp[-4])->string_value,"COUNT") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax Error with %s(*).", + (yyvsp[-4])->string_value ); + delete (yyvsp[-4]); + delete (yyvsp[0]); + YYERROR; + } + + delete (yyvsp[-4]); + (yyvsp[-4]) = NULL; + + swq_expr_node *poNode = new swq_expr_node(); + poNode->eNodeType = SNT_COLUMN; + poNode->string_value = CPLStrdup( "*" ); + poNode->table_index = poNode->field_index = -1; + + swq_expr_node *count = new swq_expr_node( (swq_op)SWQ_COUNT ); + count->PushSubExpression( poNode ); + + if( !context->poCurSelect->PushField( count, (yyvsp[0])->string_value ) ) + { + delete count; + delete (yyvsp[0]); + YYERROR; + } + + delete (yyvsp[0]); + } +#line 2238 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 66: +#line 703 "swq_parser.y" /* yacc.c:1646 */ + { + // special case for COUNT(DISTINCT x), confirm it. + if( !EQUAL((yyvsp[-4])->string_value,"COUNT") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DISTINCT keyword can only be used in COUNT() operator." ); + delete (yyvsp[-4]); + delete (yyvsp[-1]); + YYERROR; + } + + delete (yyvsp[-4]); + + swq_expr_node *count = new swq_expr_node( SWQ_COUNT ); + count->PushSubExpression( (yyvsp[-1]) ); + + if( !context->poCurSelect->PushField( count, NULL, TRUE ) ) + { + delete count; + YYERROR; + } + } +#line 2265 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 67: +#line 727 "swq_parser.y" /* yacc.c:1646 */ + { + // special case for COUNT(DISTINCT x), confirm it. + if( !EQUAL((yyvsp[-5])->string_value,"COUNT") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DISTINCT keyword can only be used in COUNT() operator." ); + delete (yyvsp[-5]); + delete (yyvsp[-2]); + delete (yyvsp[0]); + YYERROR; + } + + swq_expr_node *count = new swq_expr_node( SWQ_COUNT ); + count->PushSubExpression( (yyvsp[-2]) ); + + if( !context->poCurSelect->PushField( count, (yyvsp[0])->string_value, TRUE ) ) + { + delete (yyvsp[-5]); + delete count; + delete (yyvsp[0]); + YYERROR; + } + + delete (yyvsp[-5]); + delete (yyvsp[0]); + } +#line 2296 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 68: +#line 756 "swq_parser.y" /* yacc.c:1646 */ + { + delete (yyvsp[-1]); + (yyval) = (yyvsp[0]); + } +#line 2305 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 71: +#line 766 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->where_expr = (yyvsp[0]); + } +#line 2313 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 73: +#line 772 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->PushJoin( (yyvsp[-3])->int_value, + (yyvsp[-1]) ); + delete (yyvsp[-3]); + } +#line 2323 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 74: +#line 778 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->PushJoin( (yyvsp[-3])->int_value, + (yyvsp[-1]) ); + delete (yyvsp[-3]); + } +#line 2333 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 79: +#line 793 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->PushOrderBy( (yyvsp[0])->table_name, (yyvsp[0])->string_value, TRUE ); + delete (yyvsp[0]); + (yyvsp[0]) = NULL; + } +#line 2343 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 80: +#line 799 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->PushOrderBy( (yyvsp[-1])->table_name, (yyvsp[-1])->string_value, TRUE ); + delete (yyvsp[-1]); + (yyvsp[-1]) = NULL; + } +#line 2353 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 81: +#line 805 "swq_parser.y" /* yacc.c:1646 */ + { + context->poCurSelect->PushOrderBy( (yyvsp[-1])->table_name, (yyvsp[-1])->string_value, FALSE ); + delete (yyvsp[-1]); + (yyvsp[-1]) = NULL; + } +#line 2363 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 82: +#line 813 "swq_parser.y" /* yacc.c:1646 */ + { + int iTable; + iTable =context->poCurSelect->PushTableDef( NULL, (yyvsp[0])->string_value, + NULL ); + delete (yyvsp[0]); + + (yyval) = new swq_expr_node( iTable ); + } +#line 2376 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 83: +#line 823 "swq_parser.y" /* yacc.c:1646 */ + { + int iTable; + iTable = context->poCurSelect->PushTableDef( NULL, (yyvsp[-1])->string_value, + (yyvsp[0])->string_value ); + delete (yyvsp[-1]); + delete (yyvsp[0]); + + (yyval) = new swq_expr_node( iTable ); + } +#line 2390 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 84: +#line 834 "swq_parser.y" /* yacc.c:1646 */ + { + int iTable; + iTable = context->poCurSelect->PushTableDef( (yyvsp[-2])->string_value, + (yyvsp[0])->string_value, NULL ); + delete (yyvsp[-2]); + delete (yyvsp[0]); + + (yyval) = new swq_expr_node( iTable ); + } +#line 2404 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 85: +#line 845 "swq_parser.y" /* yacc.c:1646 */ + { + int iTable; + iTable = context->poCurSelect->PushTableDef( (yyvsp[-3])->string_value, + (yyvsp[-1])->string_value, + (yyvsp[0])->string_value ); + delete (yyvsp[-3]); + delete (yyvsp[-1]); + delete (yyvsp[0]); + + (yyval) = new swq_expr_node( iTable ); + } +#line 2420 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 86: +#line 858 "swq_parser.y" /* yacc.c:1646 */ + { + int iTable; + iTable = context->poCurSelect->PushTableDef( (yyvsp[-2])->string_value, + (yyvsp[0])->string_value, NULL ); + delete (yyvsp[-2]); + delete (yyvsp[0]); + + (yyval) = new swq_expr_node( iTable ); + } +#line 2434 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + case 87: +#line 869 "swq_parser.y" /* yacc.c:1646 */ + { + int iTable; + iTable = context->poCurSelect->PushTableDef( (yyvsp[-3])->string_value, + (yyvsp[-1])->string_value, + (yyvsp[0])->string_value ); + delete (yyvsp[-3]); + delete (yyvsp[-1]); + delete (yyvsp[0]); + + (yyval) = new swq_expr_node( iTable ); + } +#line 2450 "swq_parser.cpp" /* yacc.c:1646 */ + break; + + +#line 2454 "swq_parser.cpp" /* yacc.c:1646 */ + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, context); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} diff --git a/bazaar/plugin/gdal/ogr/swq_parser.hpp b/bazaar/plugin/gdal/ogr/swq_parser.hpp new file mode 100644 index 000000000..80f43c204 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/swq_parser.hpp @@ -0,0 +1,95 @@ +/* A Bison parser, made by GNU Bison 3.0. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_SWQ_SWQ_PARSER_HPP_INCLUDED +# define YY_SWQ_SWQ_PARSER_HPP_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int swqdebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + END = 0, + SWQT_INTEGER_NUMBER = 258, + SWQT_FLOAT_NUMBER = 259, + SWQT_STRING = 260, + SWQT_IDENTIFIER = 261, + SWQT_IN = 262, + SWQT_LIKE = 263, + SWQT_ESCAPE = 264, + SWQT_BETWEEN = 265, + SWQT_NULL = 266, + SWQT_IS = 267, + SWQT_SELECT = 268, + SWQT_LEFT = 269, + SWQT_JOIN = 270, + SWQT_WHERE = 271, + SWQT_ON = 272, + SWQT_ORDER = 273, + SWQT_BY = 274, + SWQT_FROM = 275, + SWQT_AS = 276, + SWQT_ASC = 277, + SWQT_DESC = 278, + SWQT_DISTINCT = 279, + SWQT_CAST = 280, + SWQT_UNION = 281, + SWQT_ALL = 282, + SWQT_VALUE_START = 283, + SWQT_SELECT_START = 284, + SWQT_NOT = 285, + SWQT_OR = 286, + SWQT_AND = 287, + SWQT_UMINUS = 288, + SWQT_RESERVED_KEYWORD = 289 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int swqparse (swq_parse_context *context); + +#endif /* !YY_SWQ_SWQ_PARSER_HPP_INCLUDED */ diff --git a/bazaar/plugin/gdal/ogr/swq_parser.y b/bazaar/plugin/gdal/ogr/swq_parser.y new file mode 100644 index 000000000..28f10dfd1 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/swq_parser.y @@ -0,0 +1,879 @@ +%{ +/****************************************************************************** + * + * Component: OGR SQL Engine + * Purpose: expression and select parser grammar. + * Requires Bison 2.4.0 or newer to process. Use "make parser" target. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "ogr_geometry.h" +#include "swq.h" + +#define YYSTYPE swq_expr_node* + +/* Defining YYSTYPE_IS_TRIVIAL is needed because the parser is generated as a C++ file. */ +/* See http://www.gnu.org/s/bison/manual/html_node/Memory-Management.html that suggests */ +/* increase YYINITDEPTH instead, but this will consume memory. */ +/* Setting YYSTYPE_IS_TRIVIAL overcomes this limitation, but might be fragile because */ +/* it appears to be a non documented feature of Bison */ +#define YYSTYPE_IS_TRIVIAL 1 + +%} + +%define api.pure +/* if the next %define is commented out, Bison 2.4 should be sufficient */ +/* but will produce less prettier error messages */ +%define parse.error verbose +%require "3.0" + +%parse-param {swq_parse_context *context} +%lex-param {swq_parse_context *context} + +%token SWQT_INTEGER_NUMBER "integer number" +%token SWQT_FLOAT_NUMBER "floating point number" +%token SWQT_STRING "string" +%token SWQT_IDENTIFIER "identifier" +%token SWQT_IN "IN" +%token SWQT_LIKE "LIKE" +%token SWQT_ESCAPE "ESCAPE" +%token SWQT_BETWEEN "BETWEEN" +%token SWQT_NULL "NULL" +%token SWQT_IS "IS" +%token SWQT_SELECT "SELECT" +%token SWQT_LEFT "LEFT" +%token SWQT_JOIN "JOIN" +%token SWQT_WHERE "WHERE" +%token SWQT_ON "ON" +%token SWQT_ORDER "ORDER" +%token SWQT_BY "BY" +%token SWQT_FROM "FROM" +%token SWQT_AS "AS" +%token SWQT_ASC "ASC" +%token SWQT_DESC "DESC" +%token SWQT_DISTINCT "DISTINCT" +%token SWQT_CAST "CAST" +%token SWQT_UNION "UNION" +%token SWQT_ALL "ALL" + +%token SWQT_VALUE_START +%token SWQT_SELECT_START + +%token END 0 "end of string" + +%token SWQT_NOT "NOT" +%token SWQT_OR "OR" +%token SWQT_AND "AND" + +%left SWQT_OR +%left SWQT_AND +%left SWQT_NOT + +%left '=' '<' '>' '!' SWQT_BETWEEN SWQT_IN SWQT_LIKE SWQT_IS +%left SWQT_ESCAPE + +%left '+' '-' +%left '*' '/' '%' +%left SWQT_UMINUS + +%token SWQT_RESERVED_KEYWORD "reserved keyword" + +/* Any grammar rule that does $$ = must be listed afterwards */ +/* as well as SWQT_INTEGER_NUMBER SWQT_FLOAT_NUMBER SWQT_STRING SWQT_IDENTIFIER that are allocated by swqlex() */ +%destructor { delete $$; } SWQT_INTEGER_NUMBER SWQT_FLOAT_NUMBER SWQT_STRING SWQT_IDENTIFIER +%destructor { delete $$; } value_expr_list field_value value_expr value_expr_non_logical type_def table_def + +%% + +input: + | SWQT_VALUE_START value_expr + { + context->poRoot = $2; + } + + | SWQT_SELECT_START select_statement + { + context->poRoot = $2; + } + +value_expr: + value_expr_non_logical + { + $$ = $1; + } + + | value_expr SWQT_AND value_expr + { + $$ = new swq_expr_node( SWQ_AND ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr SWQT_OR value_expr + { + $$ = new swq_expr_node( SWQ_OR ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | SWQT_NOT value_expr + { + $$ = new swq_expr_node( SWQ_NOT ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $2 ); + } + + | value_expr '=' value_expr + { + $$ = new swq_expr_node( SWQ_EQ ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr '<' '>' value_expr + { + $$ = new swq_expr_node( SWQ_NE ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $4 ); + } + + | value_expr '!' '=' value_expr + { + $$ = new swq_expr_node( SWQ_NE ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $4 ); + } + + | value_expr '<' value_expr + { + $$ = new swq_expr_node( SWQ_LT ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr '>' value_expr + { + $$ = new swq_expr_node( SWQ_GT ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr '<' '=' value_expr + { + $$ = new swq_expr_node( SWQ_LE ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $4 ); + } + + | value_expr '=' '<' value_expr + { + $$ = new swq_expr_node( SWQ_LE ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $4 ); + } + + | value_expr '=' '>' value_expr + { + $$ = new swq_expr_node( SWQ_LE ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $4 ); + } + + | value_expr '>' '=' value_expr + { + $$ = new swq_expr_node( SWQ_GE ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $4 ); + } + + | value_expr SWQT_LIKE value_expr + { + $$ = new swq_expr_node( SWQ_LIKE ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr SWQT_NOT SWQT_LIKE value_expr + { + swq_expr_node *like; + like = new swq_expr_node( SWQ_LIKE ); + like->field_type = SWQ_BOOLEAN; + like->PushSubExpression( $1 ); + like->PushSubExpression( $4 ); + + $$ = new swq_expr_node( SWQ_NOT ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( like ); + } + + | value_expr SWQT_LIKE value_expr SWQT_ESCAPE value_expr + { + $$ = new swq_expr_node( SWQ_LIKE ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + $$->PushSubExpression( $5 ); + } + + | value_expr SWQT_NOT SWQT_LIKE value_expr SWQT_ESCAPE value_expr + { + swq_expr_node *like; + like = new swq_expr_node( SWQ_LIKE ); + like->field_type = SWQ_BOOLEAN; + like->PushSubExpression( $1 ); + like->PushSubExpression( $4 ); + like->PushSubExpression( $6 ); + + $$ = new swq_expr_node( SWQ_NOT ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( like ); + } + + | value_expr SWQT_IN '(' value_expr_list ')' + { + $$ = $4; + $$->field_type = SWQ_BOOLEAN; + $$->nOperation = SWQ_IN; + $$->PushSubExpression( $1 ); + $$->ReverseSubExpressions(); + } + + | value_expr SWQT_NOT SWQT_IN '(' value_expr_list ')' + { + swq_expr_node *in; + + in = $5; + in->field_type = SWQ_BOOLEAN; + in->nOperation = SWQ_IN; + in->PushSubExpression( $1 ); + in->ReverseSubExpressions(); + + $$ = new swq_expr_node( SWQ_NOT ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( in ); + } + + | value_expr SWQT_BETWEEN value_expr_non_logical SWQT_AND value_expr_non_logical + { + $$ = new swq_expr_node( SWQ_BETWEEN ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + $$->PushSubExpression( $5 ); + } + + | value_expr SWQT_NOT SWQT_BETWEEN value_expr_non_logical SWQT_AND value_expr_non_logical + { + swq_expr_node *between; + between = new swq_expr_node( SWQ_BETWEEN ); + between->field_type = SWQ_BOOLEAN; + between->PushSubExpression( $1 ); + between->PushSubExpression( $4 ); + between->PushSubExpression( $6 ); + + $$ = new swq_expr_node( SWQ_NOT ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( between ); + } + + | value_expr SWQT_IS SWQT_NULL + { + $$ = new swq_expr_node( SWQ_ISNULL ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( $1 ); + } + + | value_expr SWQT_IS SWQT_NOT SWQT_NULL + { + swq_expr_node *isnull; + + isnull = new swq_expr_node( SWQ_ISNULL ); + isnull->field_type = SWQ_BOOLEAN; + isnull->PushSubExpression( $1 ); + + $$ = new swq_expr_node( SWQ_NOT ); + $$->field_type = SWQ_BOOLEAN; + $$->PushSubExpression( isnull ); + } + +value_expr_list: + value_expr ',' value_expr_list + { + $$ = $3; + $3->PushSubExpression( $1 ); + } + + | value_expr + { + $$ = new swq_expr_node( SWQ_ARGUMENT_LIST ); /* temporary value */ + $$->PushSubExpression( $1 ); + } + +field_value: + SWQT_IDENTIFIER + { + $$ = $1; // validation deferred. + $$->eNodeType = SNT_COLUMN; + $$->field_index = $$->table_index = -1; + } + + | SWQT_IDENTIFIER '.' SWQT_IDENTIFIER + { + $$ = $1; // validation deferred. + $$->eNodeType = SNT_COLUMN; + $$->field_index = $$->table_index = -1; + $$->table_name = $$->string_value; + $$->string_value = CPLStrdup($3->string_value); + delete $3; + $3 = NULL; + } + +value_expr_non_logical: + SWQT_INTEGER_NUMBER + { + $$ = $1; + } + + | SWQT_FLOAT_NUMBER + { + $$ = $1; + } + + | SWQT_STRING + { + $$ = $1; + } + | field_value + { + $$ = $1; + } + + | '(' value_expr ')' + { + $$ = $2; + } + + | SWQT_NULL + { + $$ = new swq_expr_node((const char*)NULL); + } + + | '-' value_expr_non_logical %prec SWQT_UMINUS + { + if ($2->eNodeType == SNT_CONSTANT) + { + $$ = $2; + $$->int_value *= -1; + $$->float_value *= -1; + } + else + { + $$ = new swq_expr_node( SWQ_MULTIPLY ); + $$->PushSubExpression( new swq_expr_node(-1) ); + $$->PushSubExpression( $2 ); + } + } + + | value_expr_non_logical '+' value_expr_non_logical + { + $$ = new swq_expr_node( SWQ_ADD ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr_non_logical '-' value_expr_non_logical + { + $$ = new swq_expr_node( SWQ_SUBTRACT ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr_non_logical '*' value_expr_non_logical + { + $$ = new swq_expr_node( SWQ_MULTIPLY ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr_non_logical '/' value_expr_non_logical + { + $$ = new swq_expr_node( SWQ_DIVIDE ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | value_expr_non_logical '%' value_expr_non_logical + { + $$ = new swq_expr_node( SWQ_MODULUS ); + $$->PushSubExpression( $1 ); + $$->PushSubExpression( $3 ); + } + + | SWQT_IDENTIFIER '(' value_expr_list ')' + { + const swq_operation *poOp = + swq_op_registrar::GetOperator( $1->string_value ); + + if( poOp == NULL ) + { + if( context->bAcceptCustomFuncs ) + { + $$ = $3; + $$->eNodeType = SNT_OPERATION; + $$->nOperation = SWQ_CUSTOM_FUNC; + $$->string_value = CPLStrdup($1->string_value); + $$->ReverseSubExpressions(); + delete $1; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Undefined function '%s' used.", + $1->string_value ); + delete $1; + delete $3; + YYERROR; + } + } + else + { + $$ = $3; + $$->eNodeType = SNT_OPERATION; + $$->nOperation = poOp->eOperation; + $$->ReverseSubExpressions(); + delete $1; + } + } + + | SWQT_CAST '(' value_expr SWQT_AS type_def ')' + { + $$ = $5; + $$->PushSubExpression( $3 ); + $$->ReverseSubExpressions(); + } + +type_def: + SWQT_IDENTIFIER + { + $$ = new swq_expr_node( SWQ_CAST ); + $$->PushSubExpression( $1 ); + } + + | SWQT_IDENTIFIER '(' SWQT_INTEGER_NUMBER ')' + { + $$ = new swq_expr_node( SWQ_CAST ); + $$->PushSubExpression( $3 ); + $$->PushSubExpression( $1 ); + } + + | SWQT_IDENTIFIER '(' SWQT_INTEGER_NUMBER ',' SWQT_INTEGER_NUMBER ')' + { + $$ = new swq_expr_node( SWQ_CAST ); + $$->PushSubExpression( $5 ); + $$->PushSubExpression( $3 ); + $$->PushSubExpression( $1 ); + } + + /* e.g. GEOMETRY(POINT) */ + | SWQT_IDENTIFIER '(' SWQT_IDENTIFIER ')' + { + OGRwkbGeometryType eType = OGRFromOGCGeomType($3->string_value); + if( !EQUAL($1->string_value,"GEOMETRY") || + (wkbFlatten(eType) == wkbUnknown && + !EQUALN($3->string_value, "GEOMETRY", strlen("GEOMETRY"))) ) + { + yyerror (context, "syntax error"); + delete $1; + delete $3; + YYERROR; + } + $$ = new swq_expr_node( SWQ_CAST ); + $$->PushSubExpression( $3 ); + $$->PushSubExpression( $1 ); + } + + /* e.g. GEOMETRY(POINT,4326) */ + | SWQT_IDENTIFIER '(' SWQT_IDENTIFIER ',' SWQT_INTEGER_NUMBER ')' + { + OGRwkbGeometryType eType = OGRFromOGCGeomType($3->string_value); + if( !EQUAL($1->string_value,"GEOMETRY") || + (wkbFlatten(eType) == wkbUnknown && + !EQUALN($3->string_value, "GEOMETRY", strlen("GEOMETRY"))) ) + { + yyerror (context, "syntax error"); + delete $1; + delete $3; + delete $5; + YYERROR; + } + $$ = new swq_expr_node( SWQ_CAST ); + $$->PushSubExpression( $5 ); + $$->PushSubExpression( $3 ); + $$->PushSubExpression( $1 ); + } + +select_statement: + select_core opt_union_all + | '(' select_core ')' opt_union_all + +select_core: + SWQT_SELECT select_field_list SWQT_FROM table_def opt_joins opt_where opt_order_by + { + delete $4; + } + + | SWQT_SELECT SWQT_DISTINCT select_field_list SWQT_FROM table_def opt_joins opt_where opt_order_by + { + context->poCurSelect->query_mode = SWQM_DISTINCT_LIST; + delete $5; + } + +opt_union_all: + | union_all select_statement + +union_all: SWQT_UNION SWQT_ALL + { + swq_select* poNewSelect = new swq_select(); + context->poCurSelect->PushUnionAll(poNewSelect); + context->poCurSelect = poNewSelect; + } + +select_field_list: + column_spec + | column_spec ',' select_field_list + +column_spec: + value_expr + { + if( !context->poCurSelect->PushField( $1 ) ) + { + delete $1; + YYERROR; + } + } + + | value_expr as_clause + { + if( !context->poCurSelect->PushField( $1, $2->string_value ) ) + { + delete $1; + delete $2; + YYERROR; + } + delete $2; + } + + | '*' + { + swq_expr_node *poNode = new swq_expr_node(); + poNode->eNodeType = SNT_COLUMN; + poNode->string_value = CPLStrdup( "*" ); + poNode->table_index = poNode->field_index = -1; + + if( !context->poCurSelect->PushField( poNode ) ) + { + delete poNode; + YYERROR; + } + } + + | SWQT_IDENTIFIER '.' '*' + { + CPLString osTableName; + + osTableName = $1->string_value; + + delete $1; + $1 = NULL; + + swq_expr_node *poNode = new swq_expr_node(); + poNode->eNodeType = SNT_COLUMN; + poNode->table_name = CPLStrdup(osTableName ); + poNode->string_value = CPLStrdup( "*" ); + poNode->table_index = poNode->field_index = -1; + + if( !context->poCurSelect->PushField( poNode ) ) + { + delete poNode; + YYERROR; + } + } + + | SWQT_IDENTIFIER '(' '*' ')' + { + // special case for COUNT(*), confirm it. + if( !EQUAL($1->string_value,"COUNT") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax Error with %s(*).", + $1->string_value ); + delete $1; + YYERROR; + } + + delete $1; + $1 = NULL; + + swq_expr_node *poNode = new swq_expr_node(); + poNode->eNodeType = SNT_COLUMN; + poNode->string_value = CPLStrdup( "*" ); + poNode->table_index = poNode->field_index = -1; + + swq_expr_node *count = new swq_expr_node( (swq_op)SWQ_COUNT ); + count->PushSubExpression( poNode ); + + if( !context->poCurSelect->PushField( count ) ) + { + delete count; + YYERROR; + } + } + + | SWQT_IDENTIFIER '(' '*' ')' as_clause + { + // special case for COUNT(*), confirm it. + if( !EQUAL($1->string_value,"COUNT") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Syntax Error with %s(*).", + $1->string_value ); + delete $1; + delete $5; + YYERROR; + } + + delete $1; + $1 = NULL; + + swq_expr_node *poNode = new swq_expr_node(); + poNode->eNodeType = SNT_COLUMN; + poNode->string_value = CPLStrdup( "*" ); + poNode->table_index = poNode->field_index = -1; + + swq_expr_node *count = new swq_expr_node( (swq_op)SWQ_COUNT ); + count->PushSubExpression( poNode ); + + if( !context->poCurSelect->PushField( count, $5->string_value ) ) + { + delete count; + delete $5; + YYERROR; + } + + delete $5; + } + + | SWQT_IDENTIFIER '(' SWQT_DISTINCT field_value ')' + { + // special case for COUNT(DISTINCT x), confirm it. + if( !EQUAL($1->string_value,"COUNT") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DISTINCT keyword can only be used in COUNT() operator." ); + delete $1; + delete $4; + YYERROR; + } + + delete $1; + + swq_expr_node *count = new swq_expr_node( SWQ_COUNT ); + count->PushSubExpression( $4 ); + + if( !context->poCurSelect->PushField( count, NULL, TRUE ) ) + { + delete count; + YYERROR; + } + } + + | SWQT_IDENTIFIER '(' SWQT_DISTINCT field_value ')' as_clause + { + // special case for COUNT(DISTINCT x), confirm it. + if( !EQUAL($1->string_value,"COUNT") ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "DISTINCT keyword can only be used in COUNT() operator." ); + delete $1; + delete $4; + delete $6; + YYERROR; + } + + swq_expr_node *count = new swq_expr_node( SWQ_COUNT ); + count->PushSubExpression( $4 ); + + if( !context->poCurSelect->PushField( count, $6->string_value, TRUE ) ) + { + delete $1; + delete count; + delete $6; + YYERROR; + } + + delete $1; + delete $6; + } + +as_clause: + SWQT_AS SWQT_IDENTIFIER + { + delete $1; + $$ = $2; + } + + | SWQT_IDENTIFIER + + +opt_where: + | SWQT_WHERE value_expr + { + context->poCurSelect->where_expr = $2; + } + +opt_joins: + | SWQT_JOIN table_def SWQT_ON value_expr opt_joins + { + context->poCurSelect->PushJoin( $2->int_value, + $4 ); + delete $2; + } + | SWQT_LEFT SWQT_JOIN table_def SWQT_ON value_expr opt_joins + { + context->poCurSelect->PushJoin( $3->int_value, + $5 ); + delete $3; + } + +opt_order_by: + | SWQT_ORDER SWQT_BY sort_spec_list + +sort_spec_list: + sort_spec ',' sort_spec_list + | sort_spec + +sort_spec: + field_value + { + context->poCurSelect->PushOrderBy( $1->table_name, $1->string_value, TRUE ); + delete $1; + $1 = NULL; + } + | field_value SWQT_ASC + { + context->poCurSelect->PushOrderBy( $1->table_name, $1->string_value, TRUE ); + delete $1; + $1 = NULL; + } + | field_value SWQT_DESC + { + context->poCurSelect->PushOrderBy( $1->table_name, $1->string_value, FALSE ); + delete $1; + $1 = NULL; + } + +table_def: + SWQT_IDENTIFIER + { + int iTable; + iTable =context->poCurSelect->PushTableDef( NULL, $1->string_value, + NULL ); + delete $1; + + $$ = new swq_expr_node( iTable ); + } + + | SWQT_IDENTIFIER as_clause + { + int iTable; + iTable = context->poCurSelect->PushTableDef( NULL, $1->string_value, + $2->string_value ); + delete $1; + delete $2; + + $$ = new swq_expr_node( iTable ); + } + + | SWQT_STRING '.' SWQT_IDENTIFIER + { + int iTable; + iTable = context->poCurSelect->PushTableDef( $1->string_value, + $3->string_value, NULL ); + delete $1; + delete $3; + + $$ = new swq_expr_node( iTable ); + } + + | SWQT_STRING '.' SWQT_IDENTIFIER as_clause + { + int iTable; + iTable = context->poCurSelect->PushTableDef( $1->string_value, + $3->string_value, + $4->string_value ); + delete $1; + delete $3; + delete $4; + + $$ = new swq_expr_node( iTable ); + } + + | SWQT_IDENTIFIER '.' SWQT_IDENTIFIER + { + int iTable; + iTable = context->poCurSelect->PushTableDef( $1->string_value, + $3->string_value, NULL ); + delete $1; + delete $3; + + $$ = new swq_expr_node( iTable ); + } + + | SWQT_IDENTIFIER '.' SWQT_IDENTIFIER as_clause + { + int iTable; + iTable = context->poCurSelect->PushTableDef( $1->string_value, + $3->string_value, + $4->string_value ); + delete $1; + delete $3; + delete $4; + + $$ = new swq_expr_node( iTable ); + } diff --git a/bazaar/plugin/gdal/ogr/swq_select.cpp b/bazaar/plugin/gdal/ogr/swq_select.cpp new file mode 100644 index 000000000..c73154787 --- /dev/null +++ b/bazaar/plugin/gdal/ogr/swq_select.cpp @@ -0,0 +1,1235 @@ +/****************************************************************************** + * + * Component: OGR SQL Engine + * Purpose: swq_select class implementation. + * Author: Frank Warmerdam + * + ****************************************************************************** + * Copyright (C) 2010 Frank Warmerdam + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "swq.h" +#include "swq_parser.hpp" +#include "ogr_geometry.h" + +/************************************************************************/ +/* swq_select() */ +/************************************************************************/ + +swq_select::swq_select() + +{ + query_mode = 0; + raw_select = NULL; + + result_columns = 0; + column_defs = NULL; + column_summary = NULL; + + table_count = 0; + table_defs = NULL; + + join_count = 0; + join_defs = NULL; + + where_expr = NULL; + + order_specs = 0; + order_defs = NULL; + + poOtherSelect = NULL; +} + +/************************************************************************/ +/* ~swq_select() */ +/************************************************************************/ + +swq_select::~swq_select() + +{ + int i; + + delete where_expr; + CPLFree( raw_select ); + + for( i = 0; i < table_count; i++ ) + { + swq_table_def *table_def = table_defs + i; + + CPLFree( table_def->data_source ); + CPLFree( table_def->table_name ); + CPLFree( table_def->table_alias ); + } + if( table_defs != NULL ) + CPLFree( table_defs ); + + for( i = 0; i < result_columns; i++ ) + { + CPLFree( column_defs[i].table_name ); + CPLFree( column_defs[i].field_name ); + CPLFree( column_defs[i].field_alias ); + + delete column_defs[i].expr; + + if( column_summary != NULL + && column_summary[i].distinct_list != NULL ) + { + int j; + + for( j = 0; j < column_summary[i].count; j++ ) + CPLFree( column_summary[i].distinct_list[j] ); + + CPLFree( column_summary[i].distinct_list ); + } + } + + CPLFree( column_defs ); + + CPLFree( column_summary ); + + for( i = 0; i < order_specs; i++ ) + { + CPLFree( order_defs[i].table_name ); + CPLFree( order_defs[i].field_name ); + } + + CPLFree( order_defs ); + + for( i = 0; i < join_count; i++ ) + { + delete join_defs[i].poExpr; + } + CPLFree( join_defs ); + + delete poOtherSelect; +} + +/************************************************************************/ +/* preparse() */ +/* */ +/* Parse the expression but without knowing the available */ +/* tables and fields. */ +/************************************************************************/ + +CPLErr swq_select::preparse( const char *select_statement, + int bAcceptCustomFuncs ) + +{ +/* -------------------------------------------------------------------- */ +/* Prepare a parser context. */ +/* -------------------------------------------------------------------- */ + swq_parse_context context; + + context.pszInput = select_statement; + context.pszNext = select_statement; + context.pszLastValid = select_statement; + context.nStartToken = SWQT_SELECT_START; + context.bAcceptCustomFuncs = bAcceptCustomFuncs; + context.poCurSelect = this; + +/* -------------------------------------------------------------------- */ +/* Do the parse. */ +/* -------------------------------------------------------------------- */ + if( swqparse( &context ) != 0 ) + { + delete context.poRoot; + return CE_Failure; + } + + postpreparse(); + + return CE_None; +} + +/************************************************************************/ +/* postpreparse() */ +/************************************************************************/ + +void swq_select::postpreparse() +{ +/* -------------------------------------------------------------------- */ +/* Reorder the joins in the order they appear in the SQL string. */ +/* -------------------------------------------------------------------- */ + int i; + for(i = 0; i < join_count / 2; i++) + { + swq_join_def sTmp; + memcpy(&sTmp, &join_defs[i], sizeof(swq_join_def)); + memcpy(&join_defs[i], &join_defs[join_count - 1 - i], sizeof(swq_join_def)); + memcpy(&join_defs[join_count - 1 - i], &sTmp, sizeof(swq_join_def)); + } + + /* We make that strong assumption in ogr_gensql */ + for(i = 0; i < join_count; i++) + { + CPLAssert(join_defs[i].secondary_table == i + 1); + } + + if( poOtherSelect != NULL) + poOtherSelect->postpreparse(); +} + +/************************************************************************/ +/* Dump() */ +/************************************************************************/ + +void swq_select::Dump( FILE *fp ) + +{ + int i; + + fprintf( fp, "SELECT Statement:\n" ); + +/* -------------------------------------------------------------------- */ +/* query mode. */ +/* -------------------------------------------------------------------- */ + if( query_mode == SWQM_SUMMARY_RECORD ) + fprintf( fp, " QUERY MODE: SUMMARY RECORD\n" ); + else if( query_mode == SWQM_RECORDSET ) + fprintf( fp, " QUERY MODE: RECORDSET\n" ); + else if( query_mode == SWQM_DISTINCT_LIST ) + fprintf( fp, " QUERY MODE: DISTINCT LIST\n" ); + else + fprintf( fp, " QUERY MODE: %d/unknown\n", query_mode ); + +/* -------------------------------------------------------------------- */ +/* column_defs */ +/* -------------------------------------------------------------------- */ + fprintf( fp, " Result Columns:\n" ); + for( i = 0; i < result_columns; i++ ) + { + swq_col_def *def = column_defs + i; + + fprintf( fp, " Table name: %s\n", def->table_name ); + fprintf( fp, " Name: %s\n", def->field_name ); + + if( def->field_alias ) + fprintf( fp, " Alias: %s\n", def->field_alias ); + + if( def->col_func == SWQCF_NONE ) + /* nothing */; + else if( def->col_func == SWQCF_AVG ) + fprintf( fp, " Function: AVG\n" ); + else if( def->col_func == SWQCF_MIN ) + fprintf( fp, " Function: MIN\n" ); + else if( def->col_func == SWQCF_MAX ) + fprintf( fp, " Function: MAX\n" ); + else if( def->col_func == SWQCF_COUNT ) + fprintf( fp, " Function: COUNT\n" ); + else if( def->col_func == SWQCF_SUM ) + fprintf( fp, " Function: SUM\n" ); + else if( def->col_func == SWQCF_CUSTOM ) + fprintf( fp, " Function: CUSTOM\n" ); + else + fprintf( fp, " Function: UNKNOWN!\n" ); + + if( def->distinct_flag ) + fprintf( fp, " DISTINCT flag set\n" ); + + fprintf( fp, " Field Index: %d, Table Index: %d\n", + def->field_index, def->table_index ); + + fprintf( fp, " Field Type: %d\n", def->field_type ); + fprintf( fp, " Target Type: %d\n", def->target_type ); + fprintf( fp, " Target SubType: %d\n", def->target_subtype ); + fprintf( fp, " Length: %d, Precision: %d\n", + def->field_length, def->field_precision ); + + if( def->expr != NULL ) + { + fprintf( fp, " Expression:\n" ); + def->expr->Dump( fp, 3 ); + } + } + +/* -------------------------------------------------------------------- */ +/* table_defs */ +/* -------------------------------------------------------------------- */ + fprintf( fp, " Table Defs: %d\n", table_count ); + for( i = 0; i < table_count; i++ ) + { + fprintf( fp, " datasource=%s, table_name=%s, table_alias=%s\n", + table_defs[i].data_source, + table_defs[i].table_name, + table_defs[i].table_alias ); + } + +/* -------------------------------------------------------------------- */ +/* join_defs */ +/* -------------------------------------------------------------------- */ + if( join_count > 0 ) + fprintf( fp, " joins:\n" ); + + for( i = 0; i < join_count; i++ ) + { + fprintf( fp, " %d:\n", i ); + join_defs[i].poExpr->Dump( fp, 4 ); + fprintf( fp, " Secondary Table: %d\n", + join_defs[i].secondary_table ); + } + +/* -------------------------------------------------------------------- */ +/* Where clause. */ +/* -------------------------------------------------------------------- */ + if( where_expr != NULL ) + { + fprintf( fp, " WHERE:\n" ); + where_expr->Dump( fp, 2 ); + } + +/* -------------------------------------------------------------------- */ +/* Order by */ +/* -------------------------------------------------------------------- */ + + for( i = 0; i < order_specs; i++ ) + { + fprintf( fp, " ORDER BY: %s (%d/%d)", + order_defs[i].field_name, + order_defs[i].table_index, + order_defs[i].field_index ); + if( order_defs[i].ascending_flag ) + fprintf( fp, " ASC\n" ); + else + fprintf( fp, " DESC\n" ); + } +} + +/************************************************************************/ +/* Unparse() */ +/************************************************************************/ + +char* swq_select::Unparse() +{ + int i; + CPLString osSelect("SELECT "); + if( query_mode == SWQM_DISTINCT_LIST ) + osSelect += "DISTINCT "; + + for( i = 0; i < result_columns; i++ ) + { + swq_col_def *def = column_defs + i; + + if( i > 0 ) + osSelect += ", "; + + if( def->expr != NULL && def->col_func == SWQCF_NONE ) + { + char* pszTmp = def->expr->Unparse(NULL, '"'); + osSelect += pszTmp; + CPLFree(pszTmp); + } + else + { + if( def->col_func == SWQCF_AVG ) + osSelect += "AVG("; + else if( def->col_func == SWQCF_MIN ) + osSelect += "MIN("; + else if( def->col_func == SWQCF_MAX ) + osSelect += "MAX("; + else if( def->col_func == SWQCF_COUNT ) + osSelect += "COUNT("; + else if( def->col_func == SWQCF_SUM ) + osSelect += "SUM("; + + if( def->distinct_flag && def->col_func == SWQCF_COUNT ) + osSelect += "DISTINCT "; + + if( (def->field_alias == NULL || table_count > 1) && + def->table_name != NULL && def->table_name[0] != '\0' ) + { + osSelect += swq_expr_node::QuoteIfNecessary(def->table_name, '"'); + osSelect += "."; + } + osSelect += swq_expr_node::QuoteIfNecessary(def->field_name, '"'); + } + + if( def->field_alias != NULL && + strcmp(def->field_name, def->field_alias) != 0 ) + { + osSelect += " AS "; + osSelect += swq_expr_node::QuoteIfNecessary(def->field_alias, '"'); + } + + if( def->col_func != SWQCF_NONE ) + osSelect += ")"; + } + + osSelect += " FROM "; + if( table_defs[0].data_source != NULL ) + { + osSelect += "'"; + osSelect += table_defs[0].data_source; + osSelect += "'."; + } + osSelect += swq_expr_node::QuoteIfNecessary(table_defs[0].table_name, '"'); + if( table_defs[0].table_alias != NULL && + strcmp(table_defs[0].table_name, table_defs[0].table_alias) != 0 ) + { + osSelect += " AS "; + osSelect += swq_expr_node::QuoteIfNecessary(table_defs[0].table_alias, '"'); + } + + for( i = 0; i < join_count; i++ ) + { + int iTable = join_defs[i].secondary_table; + osSelect += " JOIN "; + if( table_defs[iTable].data_source != NULL ) + { + osSelect += "'"; + osSelect += table_defs[iTable].data_source; + osSelect += "'."; + } + osSelect += swq_expr_node::QuoteIfNecessary(table_defs[iTable].table_name, '"'); + if( table_defs[iTable].table_alias != NULL && + strcmp(table_defs[iTable].table_name, table_defs[iTable].table_alias) != 0 ) + { + osSelect += " AS "; + osSelect += swq_expr_node::QuoteIfNecessary(table_defs[iTable].table_alias, '"'); + } + osSelect += " ON "; + char* pszTmp = join_defs[i].poExpr->Unparse(NULL, '"'); + osSelect += pszTmp; + CPLFree(pszTmp); + } + + if( where_expr != NULL ) + { + osSelect += " WHERE "; + char* pszTmp = where_expr->Unparse(NULL, '"'); + osSelect += pszTmp; + CPLFree(pszTmp); + } + + for( i = 0; i < order_specs; i++ ) + { + osSelect += " ORDER BY "; + osSelect += swq_expr_node::QuoteIfNecessary(order_defs[i].field_name, '"'); + if( !order_defs[i].ascending_flag ) + osSelect += " DESC"; + } + + return CPLStrdup(osSelect); +} + +/************************************************************************/ +/* PushField() */ +/* */ +/* Create a new field definition by name and possibly alias. */ +/************************************************************************/ + +int swq_select::PushField( swq_expr_node *poExpr, const char *pszAlias, + int distinct_flag ) + +{ + if( query_mode == SWQM_DISTINCT_LIST && distinct_flag ) + { + CPLError(CE_Failure, CPLE_NotSupported, "SELECT DISTINCT and COUNT(DISTINCT...) not supported together"); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Grow the array. */ +/* -------------------------------------------------------------------- */ + result_columns++; + + column_defs = (swq_col_def *) + CPLRealloc( column_defs, sizeof(swq_col_def) * result_columns ); + + swq_col_def *col_def = column_defs + result_columns - 1; + + memset( col_def, 0, sizeof(swq_col_def) ); + +/* -------------------------------------------------------------------- */ +/* Try to capture a field name. */ +/* -------------------------------------------------------------------- */ + if( poExpr->eNodeType == SNT_COLUMN ) + { + col_def->table_name = + CPLStrdup(poExpr->table_name ? poExpr->table_name : ""); + col_def->field_name = + CPLStrdup(poExpr->string_value); + } + else if( poExpr->eNodeType == SNT_OPERATION + && (poExpr->nOperation == SWQ_CAST || + (poExpr->nOperation >= SWQ_AVG && + poExpr->nOperation <= SWQ_SUM)) + && poExpr->nSubExprCount >= 1 + && poExpr->papoSubExpr[0]->eNodeType == SNT_COLUMN ) + { + col_def->table_name = + CPLStrdup(poExpr->papoSubExpr[0]->table_name ? + poExpr->papoSubExpr[0]->table_name : ""); + col_def->field_name = + CPLStrdup(poExpr->papoSubExpr[0]->string_value); + } + else + { + col_def->table_name = CPLStrdup(""); + col_def->field_name = CPLStrdup(""); + } + +/* -------------------------------------------------------------------- */ +/* Initialize fields. */ +/* -------------------------------------------------------------------- */ + if( pszAlias != NULL ) + col_def->field_alias = CPLStrdup( pszAlias ); + else if( pszAlias == NULL && poExpr->eNodeType == SNT_OPERATION + && poExpr->nSubExprCount >= 1 + && ( poExpr->nOperation == SWQ_CONCAT || + poExpr->nOperation == SWQ_SUBSTR ) + && poExpr->papoSubExpr[0]->eNodeType == SNT_COLUMN ) + { + const swq_operation *op = swq_op_registrar::GetOperator( + (swq_op) poExpr->nOperation ); + + col_def->field_alias = CPLStrdup( CPLSPrintf("%s_%s", op->pszName, + poExpr->papoSubExpr[0]->string_value)); + } + + col_def->table_index = -1; + col_def->field_index = -1; + col_def->field_type = SWQ_OTHER; + col_def->field_precision = -1; + col_def->target_type = SWQ_OTHER; + col_def->target_subtype = OFSTNone; + col_def->col_func = SWQCF_NONE; + col_def->distinct_flag = distinct_flag; + +/* -------------------------------------------------------------------- */ +/* Do we have a CAST operator in play? */ +/* -------------------------------------------------------------------- */ + if( poExpr->eNodeType == SNT_OPERATION + && poExpr->nOperation == SWQ_CAST ) + { + const char *pszTypeName = poExpr->papoSubExpr[1]->string_value; + int parse_precision = 0; + + if( EQUAL(pszTypeName,"character") ) + { + col_def->target_type = SWQ_STRING; + col_def->field_length = 1; + } + else if( strcasecmp(pszTypeName,"boolean") == 0 ) + { + col_def->target_type = SWQ_BOOLEAN; + } + else if( strcasecmp(pszTypeName,"integer") == 0 ) + { + col_def->target_type = SWQ_INTEGER; + } + else if( strcasecmp(pszTypeName,"bigint") == 0 ) + { + col_def->target_type = SWQ_INTEGER64; + } + else if( strcasecmp(pszTypeName,"smallint") == 0 ) + { + col_def->target_type = SWQ_INTEGER; + col_def->target_subtype = OFSTInt16; + } + else if( strcasecmp(pszTypeName,"float") == 0 ) + { + col_def->target_type = SWQ_FLOAT; + } + else if( strcasecmp(pszTypeName,"numeric") == 0 ) + { + col_def->target_type = SWQ_FLOAT; + parse_precision = 1; + } + else if( strcasecmp(pszTypeName,"timestamp") == 0 ) + { + col_def->target_type = SWQ_TIMESTAMP; + } + else if( strcasecmp(pszTypeName,"date") == 0 ) + { + col_def->target_type = SWQ_DATE; + } + else if( strcasecmp(pszTypeName,"time") == 0 ) + { + col_def->target_type = SWQ_TIME; + } + else if( strcasecmp(pszTypeName,"geometry") == 0 ) + { + col_def->target_type = SWQ_GEOMETRY; + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognized typename %s in CAST operator.", + pszTypeName ); + CPLFree(col_def->table_name); + col_def->table_name = NULL; + CPLFree(col_def->field_name); + col_def->field_name = NULL; + CPLFree(col_def->field_alias); + col_def->field_alias = NULL; + result_columns--; + return FALSE; + } + + if( col_def->target_type == SWQ_GEOMETRY ) + { + if( poExpr->nSubExprCount > 2 ) + { + if( poExpr->papoSubExpr[2]->field_type != SWQ_STRING ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "First argument of CAST operator should be an geometry type identifier." ); + CPLFree(col_def->table_name); + col_def->table_name = NULL; + CPLFree(col_def->field_name); + col_def->field_name = NULL; + CPLFree(col_def->field_alias); + col_def->field_alias = NULL; + result_columns--; + return FALSE; + } + + col_def->eGeomType = + OGRFromOGCGeomType(poExpr->papoSubExpr[2]->string_value); + + // SRID + if( poExpr->nSubExprCount > 3 ) + { + col_def->nSRID = (int)poExpr->papoSubExpr[3]->int_value; + } + } + } + else + { + // field width. + if( poExpr->nSubExprCount > 2 ) + { + if( poExpr->papoSubExpr[2]->field_type != SWQ_INTEGER ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "First argument of CAST operator should be of integer type." ); + CPLFree(col_def->table_name); + col_def->table_name = NULL; + CPLFree(col_def->field_name); + col_def->field_name = NULL; + CPLFree(col_def->field_alias); + col_def->field_alias = NULL; + result_columns--; + return FALSE; + } + col_def->field_length = (int)poExpr->papoSubExpr[2]->int_value; + } + + // field width. + if( poExpr->nSubExprCount > 3 && parse_precision ) + { + col_def->field_precision = (int)poExpr->papoSubExpr[3]->int_value; + if( col_def->field_precision == 0 ) + { + if( col_def->field_length < 10 ) + col_def->target_type = SWQ_INTEGER; + else if( col_def->field_length < 19 ) + col_def->target_type = SWQ_INTEGER64; + } + } + } + } + +/* -------------------------------------------------------------------- */ +/* Do we have a special column function in play? */ +/* -------------------------------------------------------------------- */ + if( poExpr->eNodeType == SNT_OPERATION + && poExpr->nOperation >= SWQ_AVG + && poExpr->nOperation <= SWQ_SUM ) + { + if( poExpr->nSubExprCount != 1 ) + { + const swq_operation *poOp = + swq_op_registrar::GetOperator( (swq_op)poExpr->nOperation ); + CPLError( CE_Failure, CPLE_AppDefined, + "Column Summary Function '%s' has wrong number of arguments.", + poOp->pszName ); + CPLFree(col_def->table_name); + col_def->table_name = NULL; + CPLFree(col_def->field_name); + col_def->field_name = NULL; + CPLFree(col_def->field_alias); + col_def->field_alias = NULL; + result_columns--; + return FALSE; + } + else if( poExpr->papoSubExpr[0]->eNodeType != SNT_COLUMN ) + { + const swq_operation *poOp = + swq_op_registrar::GetOperator( (swq_op)poExpr->nOperation ); + CPLError( CE_Failure, CPLE_AppDefined, + "Argument of column Summary Function '%s' should be a column.", + poOp->pszName ); + CPLFree(col_def->table_name); + col_def->table_name = NULL; + CPLFree(col_def->field_name); + col_def->field_name = NULL; + CPLFree(col_def->field_alias); + col_def->field_alias = NULL; + result_columns--; + return FALSE; + } + else + { + col_def->col_func = + (swq_col_func) poExpr->nOperation; + + swq_expr_node *poSubExpr = poExpr->papoSubExpr[0]; + + poExpr->papoSubExpr[0] = NULL; + poExpr->nSubExprCount = 0; + delete poExpr; + + poExpr = poSubExpr; + } + } + + col_def->expr = poExpr; + + return TRUE; +} + +/************************************************************************/ +/* PushTableDef() */ +/************************************************************************/ + +int swq_select::PushTableDef( const char *pszDataSource, + const char *pszName, + const char *pszAlias ) + +{ + table_count++; + + table_defs = (swq_table_def *) + CPLRealloc( table_defs, sizeof(swq_table_def) * table_count ); + + if( pszDataSource != NULL ) + table_defs[table_count-1].data_source = CPLStrdup(pszDataSource); + else + table_defs[table_count-1].data_source = NULL; + + table_defs[table_count-1].table_name = CPLStrdup(pszName); + + if( pszAlias != NULL ) + table_defs[table_count-1].table_alias = CPLStrdup(pszAlias); + else + table_defs[table_count-1].table_alias = CPLStrdup(pszName); + + return table_count-1; +} + +/************************************************************************/ +/* PushOrderBy() */ +/************************************************************************/ + +void swq_select::PushOrderBy( const char* pszTableName, const char *pszFieldName, int bAscending ) + +{ + order_specs++; + order_defs = (swq_order_def *) + CPLRealloc( order_defs, sizeof(swq_order_def) * order_specs ); + + order_defs[order_specs-1].table_name = CPLStrdup(pszTableName ? pszTableName : ""); + order_defs[order_specs-1].field_name = CPLStrdup(pszFieldName); + order_defs[order_specs-1].table_index = -1; + order_defs[order_specs-1].field_index = -1; + order_defs[order_specs-1].ascending_flag = bAscending; +} + +/************************************************************************/ +/* PushJoin() */ +/************************************************************************/ + +void swq_select::PushJoin( int iSecondaryTable, swq_expr_node* poExpr ) + +{ + join_count++; + join_defs = (swq_join_def *) + CPLRealloc( join_defs, sizeof(swq_join_def) * join_count ); + + join_defs[join_count-1].secondary_table = iSecondaryTable; + join_defs[join_count-1].poExpr = poExpr; +} + +/************************************************************************/ +/* PushUnionAll() */ +/************************************************************************/ + +void swq_select::PushUnionAll( swq_select* poOtherSelectIn ) +{ + CPLAssert(poOtherSelect == NULL); + poOtherSelect = poOtherSelectIn; +} + +/************************************************************************/ +/* expand_wildcard() */ +/* */ +/* This function replaces the '*' in a "SELECT *" with the list */ +/* provided list of fields. Itis used by swq_select_parse(), */ +/* but may be called in advance by applications wanting the */ +/* "default" field list to be different than the full list of */ +/* fields. */ +/************************************************************************/ + +CPLErr swq_select::expand_wildcard( swq_field_list *field_list, + int bAlwaysPrefixWithTableName ) + +{ + int isrc; + +/* ==================================================================== */ +/* Check each pre-expansion field. */ +/* ==================================================================== */ + for( isrc = 0; isrc < result_columns; isrc++ ) + { + const char *src_tablename = column_defs[isrc].table_name; + const char *src_fieldname = column_defs[isrc].field_name; + int itable, new_fields, i, iout; + + if( *src_fieldname == '\0' + || src_fieldname[strlen(src_fieldname)-1] != '*' ) + continue; + + /* We don't want to expand COUNT(*) */ + if( column_defs[isrc].col_func == SWQCF_COUNT ) + continue; + +/* -------------------------------------------------------------------- */ +/* Parse out the table name, verify it, and establish the */ +/* number of fields to insert from it. */ +/* -------------------------------------------------------------------- */ + if( src_tablename[0] == 0 && strcmp(src_fieldname,"*") == 0 ) + { + itable = -1; + new_fields = field_list->count; + } + else + { + for( itable = 0; itable < field_list->table_count; itable++ ) + { + if( strcasecmp(src_tablename, + field_list->table_defs[itable].table_alias ) == 0 ) + break; + } + + if( itable == field_list->table_count ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Table %s not recognised from %s.%s definition.", + src_tablename, src_tablename, src_fieldname ); + return CE_Failure; + } + + /* count the number of fields in this table. */ + new_fields = 0; + for( i = 0; i < field_list->count; i++ ) + { + if( field_list->table_ids[i] == itable ) + new_fields++; + } + } + + if (new_fields > 0) + { +/* -------------------------------------------------------------------- */ +/* Reallocate the column list larger. */ +/* -------------------------------------------------------------------- */ + CPLFree( column_defs[isrc].table_name ); + CPLFree( column_defs[isrc].field_name ); + delete column_defs[isrc].expr; + + column_defs = (swq_col_def *) + CPLRealloc( column_defs, + sizeof(swq_col_def) * + (result_columns + new_fields - 1 ) ); + +/* -------------------------------------------------------------------- */ +/* Push the old definitions that came after the one to be */ +/* replaced further up in the array. */ +/* -------------------------------------------------------------------- */ + if (new_fields != 1) + { + for( i = result_columns-1; i > isrc; i-- ) + { + memcpy( column_defs + i + new_fields - 1, + column_defs + i, + sizeof( swq_col_def ) ); + } + } + + result_columns += (new_fields - 1 ); + +/* -------------------------------------------------------------------- */ +/* Zero out all the stuff in the target column definitions. */ +/* -------------------------------------------------------------------- */ + memset( column_defs + isrc, 0, + new_fields * sizeof(swq_col_def) ); + } + else + { +/* -------------------------------------------------------------------- */ +/* The wildcard expands to nothing */ +/* -------------------------------------------------------------------- */ + CPLFree( column_defs[isrc].table_name ); + CPLFree( column_defs[isrc].field_name ); + delete column_defs[isrc].expr; + + memmove( column_defs + isrc, + column_defs + isrc + 1, + sizeof( swq_col_def ) * (result_columns-1-isrc) ); + + result_columns --; + } + +/* -------------------------------------------------------------------- */ +/* Assign the selected fields. */ +/* -------------------------------------------------------------------- */ + iout = isrc; + + for( i = 0; i < field_list->count; i++ ) + { + swq_col_def *def; + int compose = (itable != -1) || bAlwaysPrefixWithTableName; + + /* skip this field if it isn't in the target table. */ + if( itable != -1 && itable != field_list->table_ids[i] ) + continue; + + /* set up some default values. */ + def = column_defs + iout; + def->field_precision = -1; + def->target_type = SWQ_OTHER; + def->target_subtype = OFSTNone; + + /* does this field duplicate an earlier one? */ + if( field_list->table_ids[i] != 0 + && !compose ) + { + int other; + + for( other = 0; other < i; other++ ) + { + if( strcasecmp(field_list->names[i], + field_list->names[other]) == 0 ) + { + compose = 1; + break; + } + } + } + + int itable = field_list->table_ids[i]; + const char *field_name = field_list->names[i]; + const char *table_alias = + field_list->table_defs[itable].table_alias; + + def->table_name = CPLStrdup(table_alias); + def->field_name = CPLStrdup(field_name); + if( !compose ) + def->field_alias = CPLStrdup( field_list->names[i] ); + + iout++; + + /* All the other table info will be provided by the later + parse operation. */ + } + + /* If there are several occurrences of '*', go on, but stay on the */ + /* same index in case '*' is expanded to nothing */ + /* (the -- is to compensate the fact that isrc will be incremented in */ + /* the after statement of the for loop) */ + isrc --; + } + + return CE_None; +} + +/************************************************************************/ +/* CheckCompatibleJoinExpr() */ +/************************************************************************/ + +static int CheckCompatibleJoinExpr( swq_expr_node* poExpr, + int secondary_table, + swq_field_list* field_list ) +{ + if( poExpr->eNodeType == SNT_CONSTANT ) + return TRUE; + + if( poExpr->eNodeType == SNT_COLUMN ) + { + CPLAssert( poExpr->field_index != -1 ); + CPLAssert( poExpr->table_index != -1 ); + if( poExpr->table_index != 0 && poExpr->table_index != secondary_table ) + { + if( poExpr->table_name ) + CPLError( CE_Failure, CPLE_AppDefined, + "Field %s.%s in JOIN clause does not correspond to the primary table nor the joint (secondary) table.", + poExpr->table_name, + poExpr->string_value ); + else + CPLError( CE_Failure, CPLE_AppDefined, + "Field %s in JOIN clause does not correspond to the primary table nor the joint (secondary) table.", + poExpr->string_value ); + return FALSE; + } + + return TRUE; + } + + if( poExpr->eNodeType == SNT_OPERATION ) + { + for(int i=0;inSubExprCount;i++) + { + if( !CheckCompatibleJoinExpr( poExpr->papoSubExpr[i], + secondary_table, + field_list ) ) + return FALSE; + } + return TRUE; + } + + return FALSE; +} + +/************************************************************************/ +/* parse() */ +/* */ +/* This method really does post-parse processing. */ +/************************************************************************/ + +CPLErr swq_select::parse( swq_field_list *field_list, + swq_select_parse_options* poParseOptions ) +{ + int i; + CPLErr eError; + + int bAlwaysPrefixWithTableName = poParseOptions && + poParseOptions->bAlwaysPrefixWithTableName; + eError = expand_wildcard( field_list, bAlwaysPrefixWithTableName ); + if( eError != CE_None ) + return eError; + + swq_custom_func_registrar* poCustomFuncRegistrar = NULL; + if( poParseOptions != NULL ) + poCustomFuncRegistrar = poParseOptions->poCustomFuncRegistrar; + +/* -------------------------------------------------------------------- */ +/* Identify field information. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < result_columns; i++ ) + { + swq_col_def *def = column_defs + i; + + if( def->expr != NULL && def->expr->eNodeType != SNT_COLUMN ) + { + def->field_index = -1; + def->table_index = -1; + + if( def->expr->Check( field_list, TRUE, FALSE, poCustomFuncRegistrar ) == SWQ_ERROR ) + return CE_Failure; + + def->field_type = def->expr->field_type; + } + else + { + swq_field_type this_type; + + /* identify field */ + def->field_index = swq_identify_field( def->table_name, + def->field_name, field_list, + &this_type, + &(def->table_index) ); + + /* record field type */ + def->field_type = this_type; + + if( def->field_index == -1 && def->col_func != SWQCF_COUNT ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised field name %s.", + def->table_name[0] ? CPLSPrintf("%s.%s", def->table_name, def->field_name) : def->field_name ); + return CE_Failure; + } + } + + /* identify column function if present */ + if( (def->col_func == SWQCF_MIN + || def->col_func == SWQCF_MAX + || def->col_func == SWQCF_AVG + || def->col_func == SWQCF_SUM) + && (def->field_type == SWQ_STRING || + def->field_type == SWQ_GEOMETRY) ) + { + // possibly this is already enforced by the checker? + const swq_operation *op = swq_op_registrar::GetOperator( + (swq_op) def->col_func ); + CPLError( CE_Failure, CPLE_AppDefined, + "Use of field function %s() on %s field %s illegal.", + op->pszName, + SWQFieldTypeToString(def->field_type), + def->field_name ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Check if we are producing a one row summary result or a set */ +/* of records. Generate an error if we get conflicting */ +/* indications. */ +/* -------------------------------------------------------------------- */ + + int bAllowDistinctOnMultipleFields = ( + poParseOptions && poParseOptions->bAllowDistinctOnMultipleFields ); + if( query_mode == SWQM_DISTINCT_LIST && result_columns > 1 && + !bAllowDistinctOnMultipleFields ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "SELECT DISTINCT not supported on multiple columns." ); + return CE_Failure; + } + + for( i = 0; i < result_columns; i++ ) + { + swq_col_def *def = column_defs + i; + int this_indicator = -1; + + if( query_mode == SWQM_DISTINCT_LIST && def->field_type == SWQ_GEOMETRY ) + { + int bAllowDistinctOnGeometryField = ( + poParseOptions && poParseOptions->bAllowDistinctOnGeometryField ); + if( !bAllowDistinctOnGeometryField ) + { + CPLError( CE_Failure, CPLE_NotSupported, + "SELECT DISTINCT on a geometry not supported." ); + return CE_Failure; + } + } + + if( def->col_func == SWQCF_MIN + || def->col_func == SWQCF_MAX + || def->col_func == SWQCF_AVG + || def->col_func == SWQCF_SUM + || def->col_func == SWQCF_COUNT ) + { + this_indicator = SWQM_SUMMARY_RECORD; + if( def->col_func == SWQCF_COUNT && + def->distinct_flag && + def->field_type == SWQ_GEOMETRY ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "SELECT COUNT DISTINCT on a geometry not supported." ); + return CE_Failure; + } + } + else if( def->col_func == SWQCF_NONE ) + { + if( query_mode == SWQM_DISTINCT_LIST ) + { + def->distinct_flag = TRUE; + this_indicator = SWQM_DISTINCT_LIST; + } + else + this_indicator = SWQM_RECORDSET; + } + + if( this_indicator != query_mode + && this_indicator != -1 + && query_mode != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Field list implies mixture of regular recordset mode, summary mode or distinct field list mode." ); + return CE_Failure; + } + + if( this_indicator != -1 ) + query_mode = this_indicator; + } + + if (result_columns == 0) + { + query_mode = SWQM_RECORDSET; + } + +/* -------------------------------------------------------------------- */ +/* Process column names in JOIN specs. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < join_count; i++ ) + { + swq_join_def *def = join_defs + i; + if( def->poExpr->Check( field_list, TRUE, TRUE, poCustomFuncRegistrar ) == SWQ_ERROR ) + return CE_Failure; + if( !CheckCompatibleJoinExpr( def->poExpr, def->secondary_table, field_list ) ) + return CE_Failure; + } + +/* -------------------------------------------------------------------- */ +/* Process column names in order specs. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < order_specs; i++ ) + { + swq_order_def *def = order_defs + i; + + /* identify field */ + swq_field_type field_type; + def->field_index = swq_identify_field( def->table_name, + def->field_name, field_list, + &field_type, &(def->table_index) ); + if( def->field_index == -1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised field name %s in ORDER BY.", + def->table_name[0] ? CPLSPrintf("%s.%s", def->table_name, def->field_name) : def->field_name ); + return CE_Failure; + } + + if( def->table_index != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot use field '%s' of a secondary table in a ORDER BY clause", + def->field_name ); + return CE_Failure; + } + + if( field_type == SWQ_GEOMETRY ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot use geometry field '%s' in a ORDER BY clause", + def->field_name ); + return CE_Failure; + } + } + +/* -------------------------------------------------------------------- */ +/* Post process the where clause, subbing in field indexes and */ +/* doing final validation. */ +/* -------------------------------------------------------------------- */ + int bAllowFieldsInSecondaryTablesInWhere = FALSE; + if( poParseOptions != NULL ) + bAllowFieldsInSecondaryTablesInWhere = poParseOptions->bAllowFieldsInSecondaryTablesInWhere; + if( where_expr != NULL + && where_expr->Check( field_list, bAllowFieldsInSecondaryTablesInWhere, FALSE, poCustomFuncRegistrar ) == SWQ_ERROR ) + { + return CE_Failure; + } + + return CE_None; +} diff --git a/bazaar/plugin/gdal/port/GNUmakefile b/bazaar/plugin/gdal/port/GNUmakefile new file mode 100644 index 000000000..e9949d20c --- /dev/null +++ b/bazaar/plugin/gdal/port/GNUmakefile @@ -0,0 +1,61 @@ +# +# CPL (Common Portability Library) makefile +# +ifneq ($(wildcard ../GDALmake.op?),) +include ../GDALmake.opt +else +include GDALmake.opt +endif + +ifeq ($(LIBZ_SETTING),internal) +XTRA_OPT = -I../frmts/zlib +else +XTRA_OPT = +endif + +CPPFLAGS := $(CPPFLAGS) $(CURL_INC) $(XTRA_OPT) + +OBJ = cpl_conv.o cpl_error.o cpl_string.o cplgetsymbol.o cplstringlist.o \ + cpl_strtod.o cpl_path.o cpl_csv.o cpl_findfile.o cpl_minixml.o \ + cpl_multiproc.o cpl_list.o cpl_getexecpath.o cplstring.o \ + cpl_vsil_win32.o cpl_vsisimple.o cpl_vsil.o cpl_vsi_mem.o \ + cpl_vsil_unix_stdio_64.o cpl_http.o cpl_hash_set.o cplkeywordparser.o \ + cpl_recode.o cpl_recode_iconv.o cpl_recode_stub.o cpl_quad_tree.o \ + cpl_atomic_ops.o cpl_vsil_subfile.o cpl_time.o \ + cpl_vsil_stdout.o cpl_vsil_sparsefile.o cpl_vsil_abstract_archive.o \ + cpl_vsil_tar.o cpl_vsil_stdin.o cpl_vsil_buffered_reader.o \ + cpl_base64.o cpl_vsil_curl.o cpl_vsil_curl_streaming.o \ + cpl_vsil_cache.o cpl_xml_validate.o cpl_spawn.o \ + cpl_google_oauth2.o cpl_progress.o cpl_virtualmem.o + +ifeq ($(ODBC_SETTING),yes) +OBJ := $(OBJ) cpl_odbc.o +endif + + +ifeq ($(CURL_SETTING),yes) +CPPFLAGS := $(CPPFLAGS) -DHAVE_CURL +endif + +ifneq ($(LIBZ_SETTING),no) +OBJ := $(OBJ) cpl_vsil_gzip.o cpl_minizip_ioapi.o \ + cpl_minizip_unzip.o cpl_minizip_zip.o +CPPFLAGS := $(CPPFLAGS) -DHAVE_LIBZ +endif + +ifeq ($(HAVE_LIBXML2),yes) +CPPFLAGS := $(CPPFLAGS) $(LIBXML2_INC) -DHAVE_LIBXML2 +endif + +default: $(OBJ:.o=.$(OBJ_EXT)) + +$(OBJ): cpl_vsi_virtual.h + +clean: + $(RM) *.o $(O_OBJ) + +install: + for f in *.h ; do $(INSTALL_DATA) $$f $(DESTDIR)$(INST_INCLUDE) ; done + +xmlreformat: xmlreformat.o + $(CXX) $(CXXFLAGS) xmlreformat.o $(CONFIG_LIBS) -o xmlreformat diff --git a/bazaar/plugin/gdal/port/LICENCE_minizip b/bazaar/plugin/gdal/port/LICENCE_minizip new file mode 100644 index 000000000..9cc83dca0 --- /dev/null +++ b/bazaar/plugin/gdal/port/LICENCE_minizip @@ -0,0 +1,55 @@ +This is version 2005-Feb-10 of the Info-ZIP copyright and license. +The definitive version of this document should be available at +ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. + + +Copyright (c) 1990-2005 Info-ZIP. All rights reserved. + +For the purposes of this copyright and license, "Info-ZIP" is defined as +the following set of individuals: + + Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, + Jean-loup Gailly, Hunter Goatley, Ed Gordon, Ian Gorman, Chris Herborth, + Dirk Haase, Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, + David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, + Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, + Kai Uwe Rommel, Steve Salisbury, Dave Smith, Steven M. Schweda, + Christian Spieler, Cosmin Truta, Antoine Verheijen, Paul von Behren, + Rich Wales, Mike White + +This software is provided "as is," without warranty of any kind, express +or implied. In no event shall Info-ZIP or its contributors be held liable +for any direct, indirect, incidental, special or consequential damages +arising out of the use of or inability to use this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. Redistributions of source code must retain the above copyright notice, + definition, disclaimer, and this list of conditions. + + 2. Redistributions in binary form (compiled executables) must reproduce + the above copyright notice, definition, disclaimer, and this list of + conditions in documentation and/or other materials provided with the + distribution. The sole exception to this condition is redistribution + of a standard UnZipSFX binary (including SFXWiz) as part of a + self-extracting archive; that is permitted without inclusion of this + license, as long as the normal SFX banner has not been removed from + the binary or disabled. + + 3. Altered versions--including, but not limited to, ports to new operating + systems, existing ports with new graphical interfaces, and dynamic, + shared, or static library versions--must be plainly marked as such + and must not be misrepresented as being the original source. Such + altered versions also must not be misrepresented as being Info-ZIP + releases--including, but not limited to, labeling of the altered + versions with the names "Info-ZIP" (or any variation thereof, including, + but not limited to, different capitalizations), "Pocket UnZip," "WiZ" + or "MacZip" without the explicit permission of Info-ZIP. Such altered + versions are further prohibited from misrepresentative use of the + Zip-Bugs or Info-ZIP e-mail addresses or of the Info-ZIP URL(s). + + 4. Info-ZIP retains the right to use the names "Info-ZIP," "Zip," "UnZip," + "UnZipSFX," "WiZ," "Pocket UnZip," "Pocket Zip," and "MacZip" for its + own source and binary releases. diff --git a/bazaar/plugin/gdal/port/cpl_atomic_ops.cpp b/bazaar/plugin/gdal/port/cpl_atomic_ops.cpp new file mode 100644 index 000000000..76965be2b --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_atomic_ops.cpp @@ -0,0 +1,112 @@ +/********************************************************************** + * $Id: cpl_atomic_ops.cpp 28459 2015-02-12 13:48:21Z rouault $ + * + * Name: cpl_atomic_ops.cpp + * Project: CPL - Common Portability Library + * Purpose: Atomic operation functions. + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_atomic_ops.h" + +#if defined(__MACH__) && defined(__APPLE__) + +#include + +int CPLAtomicAdd(volatile int* ptr, int increment) +{ + return OSAtomicAdd32(increment, (int*)(ptr)); +} + +#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) + +#include + +int CPLAtomicAdd(volatile int* ptr, int increment) +{ +#if defined(_MSC_VER) && (_MSC_VER <= 1200) + return InterlockedExchangeAdd((LONG*)(ptr), (LONG)(increment)) + increment; +#else + return InterlockedExchangeAdd((volatile LONG*)(ptr), (LONG)(increment)) + increment; +#endif +} + +#elif defined(__MINGW32__) && defined(__i386__) + +#include + +int CPLAtomicAdd(volatile int* ptr, int increment) +{ + return InterlockedExchangeAdd((LONG*)(ptr), (LONG)(increment)) + increment; +} + +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +int CPLAtomicAdd(volatile int* ptr, int increment) +{ + int temp = increment; + __asm__ __volatile__("lock; xaddl %0,%1" + : "+r" (temp), "+m" (*ptr) + : : "memory"); + return temp + increment; +} + +#elif defined(HAVE_GCC_ATOMIC_BUILTINS) +/* Starting with GCC 4.1.0, built-in functions for atomic memory access are provided. */ +/* see http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html */ +/* We use a ./configure test to determine whether this builtins are available */ +/* as it appears that the GCC 4.1 version used on debian etch is broken when linking */ +/* such instructions... */ +int CPLAtomicAdd(volatile int* ptr, int increment) +{ + if (increment > 0) + return __sync_add_and_fetch(ptr, increment); + else + return __sync_sub_and_fetch(ptr, -increment); +} + +#elif !defined(CPL_MULTIPROC_PTHREAD) +#warning "Needs real lock API to implement properly atomic increment" + +/* Dummy implementation */ +int CPLAtomicAdd(volatile int* ptr, int increment) +{ + (*ptr) += increment; + return *ptr; +} +#else + +#include "cpl_multiproc.h" + +static CPLMutex *hAtomicOpMutex = NULL; + +/* Slow, but safe, implemenation using a mutex */ +int CPLAtomicAdd(volatile int* ptr, int increment) +{ + CPLMutexHolder oMutex(&hAtomicOpMutex); + (*ptr) += increment; + return *ptr; +} + +#endif diff --git a/bazaar/plugin/gdal/port/cpl_atomic_ops.h b/bazaar/plugin/gdal/port/cpl_atomic_ops.h new file mode 100644 index 000000000..efe91936d --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_atomic_ops.h @@ -0,0 +1,87 @@ +/********************************************************************** + * $Id: cpl_atomic_ops.h 27869 2014-10-17 02:22:42Z rouault $ + * + * Name: cpl_atomic_ops.h + * Project: CPL - Common Portability Library + * Purpose: Atomic operation functions. + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_ATOMIC_OPS_INCLUDED +#define _CPL_ATOMIC_OPS_INCLUDED + +#include "cpl_port.h" + +CPL_C_START + +/** Add a value to a pointed integer in a thread and SMP-safe way + * and return the resulting value of the operation. + * + * This function, which in most cases is implemented by a few + * efficient machine instructions, guarantees that the value pointed + * by ptr will be incremented in a thread and SMP-safe way. + * The variables for this function must be aligned on a 32-bit boundary. + * + * Depending on the platforms, this function can also act as a + * memory barrier, but this should not be assumed. + * + * Current platforms/architectures where an efficient implementation + * exists are MacOSX, MS Windows, i386/x86_64 with GCC and platforms + * supported by GCC 4.1 or higher. For other platforms supporting + * the pthread library, and when GDAL is configured with thread-support, + * the atomicity will be done with a mutex, but with + * reduced efficiently. For the remaining platforms, a simple addition + * with no locking will be done... + * + * @param ptr a pointer to an integer to increment + * @param increment the amount to add to the pointed integer + * @return the pointed value AFTER the result of the addition + */ +int CPL_DLL CPLAtomicAdd(volatile int* ptr, int increment); + +/** Increment of 1 the pointed integer in a thread and SMP-safe way + * and return the resulting value of the operation. + * + * @see CPLAtomicAdd for the details and guarantees of this atomic + * operation + * + * @param ptr a pointer to an integer to increment + * @return the pointed value AFTER the opeation: *ptr + 1 + */ +#define CPLAtomicInc(ptr) CPLAtomicAdd(ptr, 1) + +/** Decrement of 1 the pointed integer in a thread and SMP-safe way + * and return the resulting value of the operation. + * + * @see CPLAtomicAdd for the details and guarantees of this atomic + * operation + * + * @param ptr a pointer to an integer to decrement + * @return the pointed value AFTER the opeation: *ptr - 1 + */ +#define CPLAtomicDec(ptr) CPLAtomicAdd(ptr, -1) + +CPL_C_END + +#endif /* _CPL_ATOMIC_OPS_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_base64.cpp b/bazaar/plugin/gdal/port/cpl_base64.cpp new file mode 100644 index 000000000..b3946bc52 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_base64.cpp @@ -0,0 +1,242 @@ +/****************************************************************************** + * $Id: cpl_base64.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: Common Portability Library + * Purpose: Encoding/Decoding Base64 strings + * Author: Paul Ramsey + * Dave Blasby + * René Nyffenegger + * + ****************************************************************************** + * Copyright (c) 2008 Paul Ramsey + * Copyright (c) 2002 Refractions Research + * Copyright (C) 2004-2008 René Nyffenegger + * Copyright (c) 2010-2013, Even Rouault + * + * (see also part way down the file for license terms for René's code) + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of this Software or works derived from this Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_string.h" + +CPL_CVSID("$Id: cpl_base64.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/* Derived from MapServer's mappostgis.c */ + +/* +** Decode a base64 character. +*/ +static const unsigned char CPLBase64DecodeChar[256] = { + /* not Base64 characters */ + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64, + /* + */ + 62, + /* not Base64 characters */ + 64,64,64, + /* / */ + 63, + /* 0-9 */ + 52,53,54,55,56,57,58,59,60,61, + /* not Base64 characters */ + 64,64,64,64,64,64,64, + /* A-Z */ + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25, + /* not Base64 characters */ + 64,64,64,64,64,64, + /* a-z */ + 26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51, + /* not Base64 characters */ + 64,64,64,64,64, + /* not Base64 characters (upper 128 characters) */ + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 + }; + +/************************************************************************/ +/* CPLBase64DecodeInPlace() */ +/* */ +/* Decode base64 string "pszBase64" (null terminated) in place */ +/* Returns length of decoded array or 0 on failure. */ +/************************************************************************/ + +int CPLBase64DecodeInPlace(GByte* pszBase64) +{ + if (pszBase64 && *pszBase64) { + + unsigned char *p = pszBase64; + int i, j, k; + + /* Drop illegal chars first */ + for (i=0, j=0; pszBase64[i]; i++) { + unsigned char c = pszBase64[i]; + if ( (CPLBase64DecodeChar[c] != 64) || (c == '=') ) { + pszBase64[j++] = c; + } + } + + for (k=0; k>4) ); + if( p - pszBase64 == i ) + break; + if (c3 != '=') { + *p++=(((b2&0xf)<<4)|(b3>>2) ); + if( p - pszBase64 == i ) + break; + } + if (c4 != '=') { + *p++=(((b3&0x3)<<6)|b4 ); + if( p - pszBase64 == i ) + break; + } + } + return(p-pszBase64); + } + return 0; +} + +/* + * This function was extracted from the base64 cpp utility published by + * René Nyffenegger. The code was modified into a form suitable for use in + * CPL. The original code can be found at + * http://www.adp-gmbh.ch/cpp/common/base64.html. + * + * The following is the original notice of this function. + * + * base64.cpp and base64.h + * + * Copyright (C) 2004-2008 René Nyffenegger + * + * This source code is provided 'as-is', without any express or implied + * warranty. In no event will the author be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented; you must not + * claim that you wrote the original source code. If you use this source code + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original source code. + * + * 3. This notice may not be removed or altered from any source distribution. + * + * René Nyffenegger rene.nyffenegger@adp-gmbh.ch +*/ + +/************************************************************************/ +/* CPLBase64Encode() */ +/************************************************************************/ + +char *CPLBase64Encode( int nDataLen, const GByte *pabyBytesToEncode ) + +{ + static const std::string base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + int i = 0; + int j = 0; + std::string result(""); + unsigned char charArray3[3]; + unsigned char charArray4[4]; + + while( nDataLen-- ) + { + charArray3[i++] = *(pabyBytesToEncode++); + + if( i == 3 ) + { + charArray4[0] = (charArray3[0] & 0xfc) >> 2; + charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4); + charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); + charArray4[3] = charArray3[2] & 0x3f; + + for( i = 0; i < 4; i++ ) + { + result += base64Chars[charArray4[i]]; + } + + i = 0; + } + } + + if( i ) + { + for( j = i; j < 3; j++ ) + { + charArray3[j] = '\0'; + } + + charArray4[0] = (charArray3[0] & 0xfc) >> 2; + charArray4[1] = ((charArray3[0] & 0x03) << 4) + ((charArray3[1] & 0xf0) >> 4); + charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6); + charArray4[3] = charArray3[2] & 0x3f; + + for ( j = 0; j < (i + 1); j++ ) + { + result += base64Chars[charArray4[j]]; + } + + while( i++ < 3 ) + result += '='; + } + + return (CPLStrdup(result.c_str())); +} + diff --git a/bazaar/plugin/gdal/port/cpl_config.h.in b/bazaar/plugin/gdal/port/cpl_config.h.in new file mode 100644 index 000000000..313729fd3 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_config.h.in @@ -0,0 +1,232 @@ +/* port/cpl_config.h.in. Generated from configure.in by autoheader. */ + +/* Define if you want to use pthreads based multiprocessing support */ +#undef CPL_MULTIPROC_PTHREAD + +/* Define to 1 if you have the `PTHREAD_MUTEX_RECURSIVE' constant. */ +#undef HAVE_PTHREAD_MUTEX_RECURSIVE + +/* Define to 1 if you have the `PTHREAD_MUTEX_ADAPTIVE_NP' constant. */ +#undef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP + +/* Define to 1 if you have the `pthread_spinlock_t' type. */ +#undef HAVE_PTHREAD_SPINLOCK + +/* Define to 1 if you have the 5 args `mremap' function. */ +#undef HAVE_5ARGS_MREMAP + +/* --prefix directory for GDAL install */ +#undef GDAL_PREFIX + +/* Define to 1 if you have the header file. */ +#undef HAVE_ASSERT_H + +/* Define to 1 if you have the `atoll' function. */ +#undef HAVE_ATOLL + +/* Define to 1 if you have the header file. */ +#undef HAVE_CSF_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_DBMALLOC_H + +/* Define to 1 if you have the declaration of `strtof', and to 0 if you don't. + */ +#undef HAVE_DECL_STRTOF + +/* Define to 1 if you have the header file. */ +#undef HAVE_DIRECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the header file. */ +#undef HAVE_ERRNO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_FLOAT_H + +/* Define to 1 if you have the `getcwd' function. */ +#undef HAVE_GETCWD + +/* Define if you have the iconv() function and it works. */ +#undef HAVE_ICONV + +/* Define as 0 or 1 according to the floating point format suported by the + machine */ +#undef HAVE_IEEEFP + +/* Define to 1 if the system has the type `int16'. */ +#undef HAVE_INT16 + +/* Define to 1 if the system has the type `int32'. */ +#undef HAVE_INT32 + +/* Define to 1 if the system has the type `int8'. */ +#undef HAVE_INT8 + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_JPEGLIB_H + +/* Define to 1 if you have the `dl' library (-ldl). */ +#undef HAVE_LIBDL + +/* Define to 1 if you have the `m' library (-lm). */ +#undef HAVE_LIBM + +/* Define to 1 if you have the `pq' library (-lpq). */ +#undef HAVE_LIBPQ + +/* Define to 1 if you have the `rt' library (-lrt). */ +#undef HAVE_LIBRT + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LOCALE_H + +/* Define to 1, if your compiler supports long long data type */ +#undef HAVE_LONG_LONG + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PNG_H + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strtof' function. */ +#undef HAVE_STRTOF + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_VALUES_H + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if you have the `readlink' function. */ +#undef HAVE_READLINK + +/* Define to 1 if you have the `posix_spawnp' function. */ +#undef HAVE_POSIX_SPAWNP + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the `lstat' function. */ +#undef HAVE_LSTAT + +/* Set the native cpu bit order (FILLORDER_LSB2MSB or FILLORDER_MSB2LSB) */ +#undef HOST_FILLORDER + +/* Define as const if the declaration of iconv() needs const. */ +#undef ICONV_CONST + +/* For .cpp files, define as const if the declaration of iconv() needs const. */ +#undef ICONV_CPP_CONST + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Define for Mac OSX Framework build */ +#undef MACOSX_FRAMEWORK + +/* The size of `int', as computed by sizeof. */ +#undef SIZEOF_INT + +/* The size of `long', as computed by sizeof. */ +#undef SIZEOF_LONG + +/* The size of `unsigned long', as computed by sizeof. */ +#undef SIZEOF_UNSIGNED_LONG + +/* The size of `void*', as computed by sizeof. */ +#undef SIZEOF_VOIDP + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if you have fseek64, ftell64 */ +#undef UNIX_STDIO_64 + +/* Define to 1 if you want to use the -fvisibility GCC flag */ +#undef USE_GCC_VISIBILITY_FLAG + +/* Define to 1 if GCC atomic builtins are available */ +#undef HAVE_GCC_ATOMIC_BUILTINS + +/* Define to name of 64bit fopen function */ +#undef VSI_FOPEN64 + +/* Define to name of 64bit ftruncate function */ +#undef VSI_FTRUNCATE64 + +/* Define to name of 64bit fseek func */ +#undef VSI_FSEEK64 + +/* Define to name of 64bit ftell func */ +#undef VSI_FTELL64 + +/* Define to 1, if you have 64 bit STDIO API */ +#undef VSI_LARGE_API_SUPPORTED + +/* Define to 1, if you have LARGEFILE64_SOURCE */ +#undef VSI_NEED_LARGEFILE64_SOURCE + +/* Define to name of 64bit stat function */ +#undef VSI_STAT64 + +/* Define to name of 64bit stat structure */ +#undef VSI_STAT64_T + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define to 1 if you have the `getaddrinfo' function. */ +#undef HAVE_GETADDRINFO + +/* Define to 1 if you have the _SC_PHYS_PAGES' constant. */ +#undef HAVE_SC_PHYS_PAGES + +/* Use this file to override settings in instances where you're doing FAT compiles + on Apple. It is currently off by default because it doesn't seem to work with + newish ( XCode >= 3/28/11) XCodes */ +/* #include "cpl_config_extras.h" */ diff --git a/bazaar/plugin/gdal/port/cpl_config.h.vc b/bazaar/plugin/gdal/port/cpl_config.h.vc new file mode 100644 index 000000000..8fd46418d --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_config.h.vc @@ -0,0 +1,121 @@ + +/* We define this here in general so that a VC++ build will publically + declare STDCALL interfaces even if an application is built against it + using MinGW */ + +#ifndef CPL_DISABLE_STDCALL +# define CPL_STDCALL __stdcall +#endif + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have the vprintf function. */ +#define HAVE_VPRINTF 1 +#define HAVE_VSNPRINTF 1 +#define HAVE_SNPRINTF 1 +#if defined(_MSC_VER) && (_MSC_VER < 1500) +# define vsnprintf _vsnprintf +#endif +#if defined(_MSC_VER) && (_MSC_VER < 1900) +# define snprintf _snprintf +#endif + +#define HAVE_GETCWD 1 +/* gmt_notunix.h from GMT project also redefines getcwd. See #3138 */ +#ifndef getcwd +#define getcwd _getcwd +#endif + +/* Define if you have the ANSI C header files. */ +#ifndef STDC_HEADERS +# define STDC_HEADERS 1 +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_ASSERT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +#undef HAVE_LIBDL + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +#define HAVE_FLOAT_H 1 + +#define HAVE_ERRNO_H 1 + +#define HAVE_SEARCH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRECT_H + +/* Define to 1 if you have the `localtime_r' function. */ +#undef HAVE_LOCALTIME_R + +#undef HAVE_DLFCN_H +#undef HAVE_DBMALLOC_H +#undef HAVE_LIBDBMALLOC +#undef WORDS_BIGENDIAN + +/* The size of a `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of a `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of a `unsigned long', as computed by sizeof. */ +#define SIZEOF_UNSIGNED_LONG 4 + +/* The size of `void*', as computed by sizeof. */ +#ifdef _WIN64 +# define SIZEOF_VOIDP 8 +#else +# define SIZEOF_VOIDP 4 +#endif + +/* Set the native cpu bit order */ +#define HOST_FILLORDER FILLORDER_LSB2MSB + +/* Define as 0 or 1 according to the floating point format suported by the + machine */ +#define HAVE_IEEEFP 1 + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +# ifndef inline +# define inline __inline +# endif +#endif + +#define lfind _lfind + +#if defined(_MSC_VER) && (_MSC_VER < 1310) +# define VSI_STAT64 _stat +# define VSI_STAT64_T _stat +#else +# define VSI_STAT64 _stat64 +# define VSI_STAT64_T __stat64 +#endif + +/* VC6 doesn't known intptr_t */ +#if defined(_MSC_VER) && (_MSC_VER <= 1200) + typedef int intptr_t; +#endif + +#pragma warning(disable: 4786) + +/* #define CPL_DISABLE_DLL */ + diff --git a/bazaar/plugin/gdal/port/cpl_config.h.wince b/bazaar/plugin/gdal/port/cpl_config.h.wince new file mode 100644 index 000000000..f856d5e39 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_config.h.wince @@ -0,0 +1,161 @@ +/* $Id: cpl_config.h.wince 14805 2008-07-04 18:55:42Z mloskot $/ + +/* + * cpl_config.h with definitions for Windows CE platform. + */ +#ifndef _WIN32_WCE +# error This version of cpl_config.h header is dedicated for Windows CE platform! +#endif + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have the snprintf function. */ +#define HAVE_SNPRINTF +#define snprintf _snprintf + +/* Define if you have the vprintf function. */ +#define HAVE_VPRINTF +#define HAVE_VSNPRINTF +#define vsnprintf _vsnprintf + +/* Windows CE does not support getcwd function. */ +#undef HAVE_GETCWD +/* #define getcwd _getcwd */ + +/* Define if you have the stdicmp function. */ +#define HAVE_STRICMP +#define stricmp _stricmp + +/* Define if you have the strnicmp function. */ +#define HAVE_STRNICMP +#define strnicmp _strnicmp + +/* Define if you have the strdup function. */ +#define HAVE_STRDUP +#define strdup _strdup + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR +#define strerror wceex_strerror /* defined in cpl_wince.h */ + +/* Define if you have the rewind function. */ +#define HAVE_REWIND +#define rewind wceex_rewind /* defined in cpl_wince.h */ + +/* Define if you have the stat function. */ +#define HAVE_STAT +#define stat wceex_stat + +/* Define if you have the unlink function. */ +#define HAVE_UNLINK +#define unlink wceex_unlink + +/* Define if you have the mkdir function. */ +#define HAVE_MKDIR +#define mkdir wceex_mkdir + +/* Define if you have the rmdir function. */ +#define HAVE_RMDIR +#define rmdir wceex_rmdir + +/* Define if you have the rename function. */ +#define HAVE_RENAME +#define rename wceex_rename + +/* Define if you have the abort function. */ +#define HAVE_ABORT +#define abort wceex_abort + +/* Define if you have the _findfirst function. */ +#define HAVE_FINDFIRST +#define _findfirst wceex_findfirst + +/* Define if you have the _findnext function. */ +#define HAVE_FINDNEXT +#define _findnext wceex_findnext + +/* Define if you have the _findclose function. */ +#define HAVE_FINDNEXT +#define _findclose wceex_findclose + +/* Define if you have the time function. */ +#define HAVE_TIME +#define time wceex_time /* XXX - mloskot */ + +/* Define if you have the time function. */ +#define HAVE_GMTIME +#define gmtime wceex_gmtime + +/* Define if you have the localtime function. */ +#define HAVE_LOCALTIME +#define localtime wceex_localtime + +/* Define if you have the ctime function. */ +#define HAVE_CTIME +#define ctime wceex_ctime + +/* Define if you have the ctime function. */ +/* wceex_setlocale provides ONLY dummy implementation. */ +#define HAVE_SETLOCALE +#define setlocale wceex_setlocale + +/* Define to 1 if you have the `bsearch' function. */ +#define HAVE_BSEARCH +#define bsearch wceex_bsearch + +/* Define to 1 if you have the `lfind' function. */ +#define HAVE_LFIND +#define lfind wceex_lfind + +/* Define if you have the ANSI C header files. */ +#ifndef STDC_HEADERS +# define STDC_HEADERS +#endif + +/* Windows CE is not have errno.h file: */ +#if defined(_WIN32_WCE) && !defined(NO_ERRNO_H) +# undef HAVE_ERRNO_H +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_SEARCH_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define if you have the header file. */ +#undef HAVE_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_SQL_H + +/* Define if you have the header file. */ +#undef HAVE_SQLEXT_H + +#undef HAVE_LIBDL + +#undef HAVE_DLFCN_H +#undef HAVE_DBMALLOC_H +#undef HAVE_LIBDBMALLOC +#undef WORDS_BIGENDIAN + +/* The size of a `int', as computed by sizeof. */ +#define SIZEOF_INT 4 + +/* The size of a `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* #define CPL_DISABLE_DLL */ +/* #define CPL_DISABLE_STDCALL */ + +/* Enable VSIStat64 for Windows CE (temporarily) */ +/* TODO: To be removed as specified in Ticket #2452 */ +#define VSI_STAT64 stat + diff --git a/bazaar/plugin/gdal/port/cpl_config_extras.h b/bazaar/plugin/gdal/port/cpl_config_extras.h new file mode 100644 index 000000000..654f2fcad --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_config_extras.h @@ -0,0 +1,40 @@ + +#ifndef INCLUDED_CPL_CONFIG_EXTRAS +#define INCLUDED_CPL_CONFIG_EXTRAS + +#if defined(__APPLE__) + +#ifdef __BIG_ENDIAN__ + #define HOST_FILLORDER FILLORDER_MSB2LSB +#else + #define HOST_FILLORDER FILLORDER_LSB2MSB +#endif + + +#ifdef __LP64__ + #define SIZEOF_UNSIGNED_LONG 8 +#else + #define SIZEOF_UNSIGNED_LONG 4 +#endif + +#ifdef __LP64__ + #define SIZEOF_VOIDP 8 +#else + #define SIZEOF_VOIDP 4 +#endif + +#ifdef __BIG_ENDIAN__ + #define WORDS_BIGENDIAN 1 +#else + #undef WORDS_BIGENDIAN +#endif + +#undef VSI_STAT64 +#undef VSI_STAT64_T + +#define VSI_STAT64 stat +#define VSI_STAT64_T stat + +#endif // APPLE + +#endif //INCLUDED_CPL_CONFIG_EXTRAS diff --git a/bazaar/plugin/gdal/port/cpl_conv.cpp b/bazaar/plugin/gdal/port/cpl_conv.cpp new file mode 100644 index 000000000..5fbffff4c --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_conv.cpp @@ -0,0 +1,2753 @@ +/****************************************************************************** + * $Id: cpl_conv.cpp 28601 2015-03-03 11:06:40Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Convenience functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifdef MSVC_USE_VLD +#include +#endif +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_vsi.h" +#include "cpl_multiproc.h" +#include + +CPL_CVSID("$Id: cpl_conv.cpp 28601 2015-03-03 11:06:40Z rouault $"); + +#if defined(WIN32CE) +# include "cpl_wince.h" +#endif + +static CPLMutex *hConfigMutex = NULL; +static volatile char **papszConfigOptions = NULL; + +/* Used by CPLOpenShared() and friends */ +static CPLMutex *hSharedFileMutex = NULL; +static volatile int nSharedFileCount = 0; +static volatile CPLSharedFileInfo *pasSharedFileList = NULL; + +/* Used by CPLsetlocale() */ +static CPLMutex *hSetLocaleMutex = NULL; + +/* Note: ideally this should be added in CPLSharedFileInfo* */ +/* but CPLSharedFileInfo is exposed in the API, hence that trick */ +/* to hide this detail */ +typedef struct +{ + GIntBig nPID; // pid of opening thread +} CPLSharedFileInfoExtra; + +static volatile CPLSharedFileInfoExtra *pasSharedFileListExtra = NULL; + +/************************************************************************/ +/* CPLCalloc() */ +/************************************************************************/ + +/** + * Safe version of calloc(). + * + * This function is like the C library calloc(), but raises a CE_Fatal + * error with CPLError() if it fails to allocate the desired memory. It + * should be used for small memory allocations that are unlikely to fail + * and for which the application is unwilling to test for out of memory + * conditions. It uses VSICalloc() to get the memory, so any hooking of + * VSICalloc() will apply to CPLCalloc() as well. CPLFree() or VSIFree() + * can be used free memory allocated by CPLCalloc(). + * + * @param nCount number of objects to allocate. + * @param nSize size (in bytes) of object to allocate. + * @return pointer to newly allocated memory, only NULL if nSize * nCount is + * NULL. + */ + +void *CPLCalloc( size_t nCount, size_t nSize ) + +{ + void *pReturn; + + if( nSize * nCount == 0 ) + return NULL; + + pReturn = CPLMalloc( nCount * nSize ); + memset( pReturn, 0, nCount * nSize ); + return pReturn; +} + +/************************************************************************/ +/* CPLMalloc() */ +/************************************************************************/ + +/** + * Safe version of malloc(). + * + * This function is like the C library malloc(), but raises a CE_Fatal + * error with CPLError() if it fails to allocate the desired memory. It + * should be used for small memory allocations that are unlikely to fail + * and for which the application is unwilling to test for out of memory + * conditions. It uses VSIMalloc() to get the memory, so any hooking of + * VSIMalloc() will apply to CPLMalloc() as well. CPLFree() or VSIFree() + * can be used free memory allocated by CPLMalloc(). + * + * @param nSize size (in bytes) of memory block to allocate. + * @return pointer to newly allocated memory, only NULL if nSize is zero. + */ + +void *CPLMalloc( size_t nSize ) + +{ + void *pReturn; + + CPLVerifyConfiguration(); + + if( nSize == 0 ) + return NULL; + + if( long(nSize) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CPLMalloc(%ld): Silly size requested.\n", + (long) nSize ); + return NULL; + } + + pReturn = VSIMalloc( nSize ); + if( pReturn == NULL ) + { + if( nSize > 0 && nSize < 2000 ) + { + char szSmallMsg[60]; + + sprintf( szSmallMsg, + "CPLMalloc(): Out of memory allocating %ld bytes.", + (long) nSize ); + CPLEmergencyError( szSmallMsg ); + } + else + CPLError( CE_Fatal, CPLE_OutOfMemory, + "CPLMalloc(): Out of memory allocating %ld bytes.\n", + (long) nSize ); + } + + return pReturn; +} + +/************************************************************************/ +/* CPLRealloc() */ +/************************************************************************/ + +/** + * Safe version of realloc(). + * + * This function is like the C library realloc(), but raises a CE_Fatal + * error with CPLError() if it fails to allocate the desired memory. It + * should be used for small memory allocations that are unlikely to fail + * and for which the application is unwilling to test for out of memory + * conditions. It uses VSIRealloc() to get the memory, so any hooking of + * VSIRealloc() will apply to CPLRealloc() as well. CPLFree() or VSIFree() + * can be used free memory allocated by CPLRealloc(). + * + * It is also safe to pass NULL in as the existing memory block for + * CPLRealloc(), in which case it uses VSIMalloc() to allocate a new block. + * + * @param pData existing memory block which should be copied to the new block. + * @param nNewSize new size (in bytes) of memory block to allocate. + * @return pointer to allocated memory, only NULL if nNewSize is zero. + */ + + +void * CPLRealloc( void * pData, size_t nNewSize ) + +{ + void *pReturn; + + if ( nNewSize == 0 ) + { + VSIFree(pData); + return NULL; + } + + if( long(nNewSize) < 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CPLRealloc(%ld): Silly size requested.\n", + (long) nNewSize ); + return NULL; + } + + if( pData == NULL ) + pReturn = VSIMalloc( nNewSize ); + else + pReturn = VSIRealloc( pData, nNewSize ); + + if( pReturn == NULL ) + { + if( nNewSize > 0 && nNewSize < 2000 ) + { + char szSmallMsg[60]; + + sprintf( szSmallMsg, + "CPLRealloc(): Out of memory allocating %ld bytes.", + (long) nNewSize ); + CPLEmergencyError( szSmallMsg ); + } + else + CPLError( CE_Fatal, CPLE_OutOfMemory, + "CPLRealloc(): Out of memory allocating %ld bytes.\n", + (long) nNewSize ); + } + + return pReturn; +} + +/************************************************************************/ +/* CPLStrdup() */ +/************************************************************************/ + +/** + * Safe version of strdup() function. + * + * This function is similar to the C library strdup() function, but if + * the memory allocation fails it will issue a CE_Fatal error with + * CPLError() instead of returning NULL. It uses VSIStrdup(), so any + * hooking of that function will apply to CPLStrdup() as well. Memory + * allocated with CPLStrdup() can be freed with CPLFree() or VSIFree(). + * + * It is also safe to pass a NULL string into CPLStrdup(). CPLStrdup() + * will allocate and return a zero length string (as opposed to a NULL + * string). + * + * @param pszString input string to be duplicated. May be NULL. + * @return pointer to a newly allocated copy of the string. Free with + * CPLFree() or VSIFree(). + */ + +char *CPLStrdup( const char * pszString ) + +{ + char *pszReturn; + + if( pszString == NULL ) + pszString = ""; + + pszReturn = (char *) CPLMalloc(strlen(pszString)+1); + if( pszReturn == NULL ) + { + CPLError( CE_Fatal, CPLE_OutOfMemory, + "CPLStrdup(): Out of memory allocating %ld bytes.\n", + (long) strlen(pszString) ); + + } + + strcpy( pszReturn, pszString ); + return( pszReturn ); +} + +/************************************************************************/ +/* CPLStrlwr() */ +/************************************************************************/ + +/** + * Convert each characters of the string to lower case. + * + * For example, "ABcdE" will be converted to "abcde". + * This function is locale dependent. + * + * @param pszString input string to be converted. + * @return pointer to the same string, pszString. + */ + +char *CPLStrlwr( char *pszString ) + +{ + if (pszString) + { + char *pszTemp = pszString; + + while (*pszTemp) + { + *pszTemp = (char) tolower (*pszTemp); + pszTemp++; + } + } + + return pszString; +} + +/************************************************************************/ +/* CPLFGets() */ +/* */ +/* Note: CR = \r = ASCII 13 */ +/* LF = \n = ASCII 10 */ +/************************************************************************/ + +/** + * Reads in at most one less than nBufferSize characters from the fp + * stream and stores them into the buffer pointed to by pszBuffer. + * Reading stops after an EOF or a newline. If a newline is read, it + * is _not_ stored into the buffer. A '\\0' is stored after the last + * character in the buffer. All three types of newline terminators + * recognized by the CPLFGets(): single '\\r' and '\\n' and '\\r\\n' + * combination. + * + * @param pszBuffer pointer to the targeting character buffer. + * @param nBufferSize maximum size of the string to read (not including + * termonating '\\0'). + * @param fp file pointer to read from. + * @return pointer to the pszBuffer containing a string read + * from the file or NULL if the error or end of file was encountered. + */ + +char *CPLFGets( char *pszBuffer, int nBufferSize, FILE * fp ) + +{ + int nActuallyRead, nOriginalOffset; + + if ( nBufferSize == 0 || pszBuffer == NULL || fp == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Let the OS level call read what it things is one line. This */ +/* will include the newline. On windows, if the file happens */ +/* to be in text mode, the CRLF will have been converted to */ +/* just the newline (LF). If it is in binary mode it may well */ +/* have both. */ +/* -------------------------------------------------------------------- */ + nOriginalOffset = VSIFTell( fp ); + if( VSIFGets( pszBuffer, nBufferSize, fp ) == NULL ) + return NULL; + + nActuallyRead = strlen(pszBuffer); + if ( nActuallyRead == 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we found \r and out buffer is full, it is possible there */ +/* is also a pending \n. Check for it. */ +/* -------------------------------------------------------------------- */ + if( nBufferSize == nActuallyRead+1 + && pszBuffer[nActuallyRead-1] == 13 ) + { + int chCheck; + chCheck = fgetc( fp ); + if( chCheck != 10 ) + { + // unget the character. + VSIFSeek( fp, nOriginalOffset+nActuallyRead, SEEK_SET ); + } + } + +/* -------------------------------------------------------------------- */ +/* Trim off \n, \r or \r\n if it appears at the end. We don't */ +/* need to do any "seeking" since we want the newline eaten. */ +/* -------------------------------------------------------------------- */ + if( nActuallyRead > 1 + && pszBuffer[nActuallyRead-1] == 10 + && pszBuffer[nActuallyRead-2] == 13 ) + { + pszBuffer[nActuallyRead-2] = '\0'; + } + else if( pszBuffer[nActuallyRead-1] == 10 + || pszBuffer[nActuallyRead-1] == 13 ) + { + pszBuffer[nActuallyRead-1] = '\0'; + } + +/* -------------------------------------------------------------------- */ +/* Search within the string for a \r (MacOS convention */ +/* apparently), and if we find it we need to trim the string, */ +/* and seek back. */ +/* -------------------------------------------------------------------- */ + char *pszExtraNewline = strchr( pszBuffer, 13 ); + + if( pszExtraNewline != NULL ) + { + int chCheck; + + nActuallyRead = pszExtraNewline - pszBuffer + 1; + + *pszExtraNewline = '\0'; + VSIFSeek( fp, nOriginalOffset + nActuallyRead - 1, SEEK_SET ); + + /* + * This hackery is necessary to try and find our correct + * spot on win32 systems with text mode line translation going + * on. Sometimes the fseek back overshoots, but it doesn't + * "realize it" till a character has been read. Try to read till + * we get to the right spot and get our CR. + */ + chCheck = fgetc( fp ); + while( (chCheck != 13 && chCheck != EOF) + || VSIFTell(fp) < nOriginalOffset + nActuallyRead ) + { + static volatile int bWarned = FALSE; + + if( !bWarned ) + { + bWarned = TRUE; + CPLDebug( "CPL", "CPLFGets() correcting for DOS text mode translation seek problem." ); + } + chCheck = fgetc( fp ); + } + } + + return pszBuffer; +} + +/************************************************************************/ +/* CPLReadLineBuffer() */ +/* */ +/* Fetch readline buffer, and ensure it is the desired size, */ +/* reallocating if needed. Manages TLS (thread local storage) */ +/* issues for the buffer. */ +/* We use a special trick to track the actual size of the buffer */ +/* The first 4 bytes are reserved to store it as a int, hence the */ +/* -4 / +4 hacks with the size and pointer. */ +/************************************************************************/ +static char *CPLReadLineBuffer( int nRequiredSize ) + +{ + +/* -------------------------------------------------------------------- */ +/* A required size of -1 means the buffer should be freed. */ +/* -------------------------------------------------------------------- */ + if( nRequiredSize == -1 ) + { + if( CPLGetTLS( CTLS_RLBUFFERINFO ) != NULL ) + { + CPLFree( CPLGetTLS( CTLS_RLBUFFERINFO ) ); + CPLSetTLS( CTLS_RLBUFFERINFO, NULL, FALSE ); + } + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* If the buffer doesn't exist yet, create it. */ +/* -------------------------------------------------------------------- */ + GUInt32 *pnAlloc = (GUInt32 *) CPLGetTLS( CTLS_RLBUFFERINFO ); + + if( pnAlloc == NULL ) + { + pnAlloc = (GUInt32 *) CPLMalloc(200); + *pnAlloc = 196; + CPLSetTLS( CTLS_RLBUFFERINFO, pnAlloc, TRUE ); + } + +/* -------------------------------------------------------------------- */ +/* If it is too small, grow it bigger. */ +/* -------------------------------------------------------------------- */ + if( ((int) *pnAlloc) -1 < nRequiredSize ) + { + int nNewSize = nRequiredSize + 4 + 500; + if (nNewSize <= 0) + { + VSIFree( pnAlloc ); + CPLSetTLS( CTLS_RLBUFFERINFO, NULL, FALSE ); + CPLError( CE_Failure, CPLE_OutOfMemory, + "CPLReadLineBuffer(): Trying to allocate more than 2 GB." ); + return NULL; + } + + GUInt32* pnAllocNew = (GUInt32 *) VSIRealloc(pnAlloc,nNewSize); + if( pnAllocNew == NULL ) + { + VSIFree( pnAlloc ); + CPLSetTLS( CTLS_RLBUFFERINFO, NULL, FALSE ); + CPLError( CE_Failure, CPLE_OutOfMemory, + "CPLReadLineBuffer(): Out of memory allocating %ld bytes.", + (long) nNewSize ); + return NULL; + } + pnAlloc = pnAllocNew; + + *pnAlloc = nNewSize - 4; + CPLSetTLS( CTLS_RLBUFFERINFO, pnAlloc, TRUE ); + } + + return (char *) (pnAlloc+1); +} + +/************************************************************************/ +/* CPLReadLine() */ +/************************************************************************/ + +/** + * Simplified line reading from text file. + * + * Read a line of text from the given file handle, taking care + * to capture CR and/or LF and strip off ... equivelent of + * DKReadLine(). Pointer to an internal buffer is returned. + * The application shouldn't free it, or depend on it's value + * past the next call to CPLReadLine(). + * + * Note that CPLReadLine() uses VSIFGets(), so any hooking of VSI file + * services should apply to CPLReadLine() as well. + * + * CPLReadLine() maintains an internal buffer, which will appear as a + * single block memory leak in some circumstances. CPLReadLine() may + * be called with a NULL FILE * at any time to free this working buffer. + * + * @param fp file pointer opened with VSIFOpen(). + * + * @return pointer to an internal buffer containing a line of text read + * from the file or NULL if the end of file was encountered. + */ + +const char *CPLReadLine( FILE * fp ) + +{ + char *pszRLBuffer = CPLReadLineBuffer(1); + int nReadSoFar = 0; + +/* -------------------------------------------------------------------- */ +/* Cleanup case. */ +/* -------------------------------------------------------------------- */ + if( fp == NULL ) + { + CPLReadLineBuffer( -1 ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Loop reading chunks of the line till we get to the end of */ +/* the line. */ +/* -------------------------------------------------------------------- */ + int nBytesReadThisTime; + + do { +/* -------------------------------------------------------------------- */ +/* Grow the working buffer if we have it nearly full. Fail out */ +/* of read line if we can't reallocate it big enough (for */ +/* instance for a _very large_ file with no newlines). */ +/* -------------------------------------------------------------------- */ + if( nReadSoFar > 100 * 1024 * 1024 ) + return NULL; /* it is dubious that we need to read a line longer than 100 MB ! */ + pszRLBuffer = CPLReadLineBuffer( nReadSoFar + 129 ); + if( pszRLBuffer == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Do the actual read. */ +/* -------------------------------------------------------------------- */ + if( CPLFGets( pszRLBuffer+nReadSoFar, 128, fp ) == NULL + && nReadSoFar == 0 ) + return NULL; + + nBytesReadThisTime = strlen(pszRLBuffer+nReadSoFar); + nReadSoFar += nBytesReadThisTime; + + } while( nBytesReadThisTime >= 127 + && pszRLBuffer[nReadSoFar-1] != 13 + && pszRLBuffer[nReadSoFar-1] != 10 ); + + return( pszRLBuffer ); +} + +/************************************************************************/ +/* CPLReadLineL() */ +/************************************************************************/ + +/** + * Simplified line reading from text file. + * + * Similar to CPLReadLine(), but reading from a large file API handle. + * + * @param fp file pointer opened with VSIFOpenL(). + * + * @return pointer to an internal buffer containing a line of text read + * from the file or NULL if the end of file was encountered. + */ + +const char *CPLReadLineL( VSILFILE * fp ) +{ + return CPLReadLine2L( fp, -1, NULL ); +} + +/************************************************************************/ +/* CPLReadLine2L() */ +/************************************************************************/ + +/** + * Simplified line reading from text file. + * + * Similar to CPLReadLine(), but reading from a large file API handle. + * + * @param fp file pointer opened with VSIFOpenL(). + * @param nMaxCars maximum number of characters allowed, or -1 for no limit. + * @param papszOptions NULL-terminated array of options. Unused for now. + + * @return pointer to an internal buffer containing a line of text read + * from the file or NULL if the end of file was encountered or the maximum + * number of characters allowed readched. + * + * @since GDAL 1.7.0 + */ + +const char *CPLReadLine2L( VSILFILE * fp, int nMaxCars, char** papszOptions ) + +{ + (void) papszOptions; + +/* -------------------------------------------------------------------- */ +/* Cleanup case. */ +/* -------------------------------------------------------------------- */ + if( fp == NULL ) + { + CPLReadLineBuffer( -1 ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Loop reading chunks of the line till we get to the end of */ +/* the line. */ +/* -------------------------------------------------------------------- */ + char *pszRLBuffer; + const size_t nChunkSize = 40; + char szChunk[nChunkSize]; + size_t nChunkBytesRead = 0; + int nBufLength = 0; + size_t nChunkBytesConsumed = 0; + + while( TRUE ) + { +/* -------------------------------------------------------------------- */ +/* Read a chunk from the input file. */ +/* -------------------------------------------------------------------- */ + if ( nBufLength > INT_MAX - (int)nChunkSize - 1 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Too big line : more than 2 billion characters!." ); + CPLReadLineBuffer( -1 ); + return NULL; + } + + pszRLBuffer = CPLReadLineBuffer( nBufLength + nChunkSize + 1 ); + if( pszRLBuffer == NULL ) + return NULL; + + if( nChunkBytesRead == nChunkBytesConsumed + 1 ) + { + + // case where one character is left over from last read. + szChunk[0] = szChunk[nChunkBytesConsumed]; + + nChunkBytesConsumed = 0; + nChunkBytesRead = VSIFReadL( szChunk+1, 1, nChunkSize-1, fp ) + 1; + } + else + { + nChunkBytesConsumed = 0; + + // fresh read. + nChunkBytesRead = VSIFReadL( szChunk, 1, nChunkSize, fp ); + if( nChunkBytesRead == 0 ) + { + if( nBufLength == 0 ) + return NULL; + else + break; + } + } + +/* -------------------------------------------------------------------- */ +/* copy over characters watching for end-of-line. */ +/* -------------------------------------------------------------------- */ + int bBreak = FALSE; + while( nChunkBytesConsumed < nChunkBytesRead-1 && !bBreak ) + { + if( (szChunk[nChunkBytesConsumed] == 13 + && szChunk[nChunkBytesConsumed+1] == 10) + || (szChunk[nChunkBytesConsumed] == 10 + && szChunk[nChunkBytesConsumed+1] == 13) ) + { + nChunkBytesConsumed += 2; + bBreak = TRUE; + } + else if( szChunk[nChunkBytesConsumed] == 10 + || szChunk[nChunkBytesConsumed] == 13 ) + { + nChunkBytesConsumed += 1; + bBreak = TRUE; + } + else + { + pszRLBuffer[nBufLength++] = szChunk[nChunkBytesConsumed++]; + if (nMaxCars >= 0 && nBufLength == nMaxCars) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Maximum number of characters allowed reached."); + return NULL; + } + } + } + + if( bBreak ) + break; + +/* -------------------------------------------------------------------- */ +/* If there is a remaining character and it is not a newline */ +/* consume it. If it is a newline, but we are clearly at the */ +/* end of the file then consume it. */ +/* -------------------------------------------------------------------- */ + if( nChunkBytesConsumed == nChunkBytesRead-1 + && nChunkBytesRead < nChunkSize ) + { + if( szChunk[nChunkBytesConsumed] == 10 + || szChunk[nChunkBytesConsumed] == 13 ) + { + nChunkBytesConsumed++; + break; + } + + pszRLBuffer[nBufLength++] = szChunk[nChunkBytesConsumed++]; + break; + } + } + +/* -------------------------------------------------------------------- */ +/* If we have left over bytes after breaking out, seek back to */ +/* ensure they remain to be read next time. */ +/* -------------------------------------------------------------------- */ + if( nChunkBytesConsumed < nChunkBytesRead ) + { + size_t nBytesToPush = nChunkBytesRead - nChunkBytesConsumed; + + VSIFSeekL( fp, VSIFTellL( fp ) - nBytesToPush, SEEK_SET ); + } + + pszRLBuffer[nBufLength] = '\0'; + + return( pszRLBuffer ); +} + +/************************************************************************/ +/* CPLScanString() */ +/************************************************************************/ + +/** + * Scan up to a maximum number of characters from a given string, + * allocate a buffer for a new string and fill it with scanned characters. + * + * @param pszString String containing characters to be scanned. It may be + * terminated with a null character. + * + * @param nMaxLength The maximum number of character to read. Less + * characters will be read if a null character is encountered. + * + * @param bTrimSpaces If TRUE, trim ending spaces from the input string. + * Character considered as empty using isspace(3) function. + * + * @param bNormalize If TRUE, replace ':' symbol with the '_'. It is needed if + * resulting string will be used in CPL dictionaries. + * + * @return Pointer to the resulting string buffer. Caller responsible to free + * this buffer with CPLFree(). + */ + +char *CPLScanString( const char *pszString, int nMaxLength, + int bTrimSpaces, int bNormalize ) +{ + char *pszBuffer; + + if ( !pszString ) + return NULL; + + if ( !nMaxLength ) + return CPLStrdup( "" ); + + pszBuffer = (char *)CPLMalloc( nMaxLength + 1 ); + if ( !pszBuffer ) + return NULL; + + strncpy( pszBuffer, pszString, nMaxLength ); + pszBuffer[nMaxLength] = '\0'; + + if ( bTrimSpaces ) + { + size_t i = strlen( pszBuffer ); + while ( i-- > 0 && isspace((unsigned char)pszBuffer[i]) ) + pszBuffer[i] = '\0'; + } + + if ( bNormalize ) + { + size_t i = strlen( pszBuffer ); + while ( i-- > 0 ) + { + if ( pszBuffer[i] == ':' ) + pszBuffer[i] = '_'; + } + } + + return pszBuffer; +} + +/************************************************************************/ +/* CPLScanLong() */ +/************************************************************************/ + +/** + * Scan up to a maximum number of characters from a string and convert + * the result to a long. + * + * @param pszString String containing characters to be scanned. It may be + * terminated with a null character. + * + * @param nMaxLength The maximum number of character to consider as part + * of the number. Less characters will be considered if a null character + * is encountered. + * + * @return Long value, converted from its ASCII form. + */ + +long CPLScanLong( const char *pszString, int nMaxLength ) +{ + long iValue; + char *pszValue = (char *)CPLMalloc( nMaxLength + 1); + +/* -------------------------------------------------------------------- */ +/* Compute string into local buffer, and terminate it. */ +/* -------------------------------------------------------------------- */ + strncpy( pszValue, pszString, nMaxLength ); + pszValue[nMaxLength] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Use atol() to fetch out the result */ +/* -------------------------------------------------------------------- */ + iValue = atol( pszValue ); + + CPLFree( pszValue ); + return iValue; +} + + +/************************************************************************/ +/* CPLScanULong() */ +/************************************************************************/ + +/** + * Scan up to a maximum number of characters from a string and convert + * the result to a unsigned long. + * + * @param pszString String containing characters to be scanned. It may be + * terminated with a null character. + * + * @param nMaxLength The maximum number of character to consider as part + * of the number. Less characters will be considered if a null character + * is encountered. + * + * @return Unsigned long value, converted from its ASCII form. + */ + +unsigned long CPLScanULong( const char *pszString, int nMaxLength ) +{ + unsigned long uValue; + char *pszValue = (char *)CPLMalloc( nMaxLength + 1); + +/* -------------------------------------------------------------------- */ +/* Compute string into local buffer, and terminate it. */ +/* -------------------------------------------------------------------- */ + strncpy( pszValue, pszString, nMaxLength ); + pszValue[nMaxLength] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Use strtoul() to fetch out the result */ +/* -------------------------------------------------------------------- */ + uValue = strtoul( pszValue, NULL, 10 ); + + CPLFree( pszValue ); + return uValue; +} + +/************************************************************************/ +/* CPLScanUIntBig() */ +/************************************************************************/ + +/** + * Extract big integer from string. + * + * Scan up to a maximum number of characters from a string and convert + * the result to a GUIntBig. + * + * @param pszString String containing characters to be scanned. It may be + * terminated with a null character. + * + * @param nMaxLength The maximum number of character to consider as part + * of the number. Less characters will be considered if a null character + * is encountered. + * + * @return GUIntBig value, converted from its ASCII form. + */ + +GUIntBig CPLScanUIntBig( const char *pszString, int nMaxLength ) +{ + GUIntBig iValue; + char *pszValue = (char *)CPLMalloc( nMaxLength + 1); + +/* -------------------------------------------------------------------- */ +/* Compute string into local buffer, and terminate it. */ +/* -------------------------------------------------------------------- */ + strncpy( pszValue, pszString, nMaxLength ); + pszValue[nMaxLength] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Fetch out the result */ +/* -------------------------------------------------------------------- */ +#if defined(__MSVCRT__) || (defined(WIN32) && defined(_MSC_VER)) + iValue = (GUIntBig)_atoi64( pszValue ); +# elif HAVE_ATOLL + iValue = atoll( pszValue ); +#else + iValue = atol( pszValue ); +#endif + + CPLFree( pszValue ); + return iValue; +} + +/************************************************************************/ +/* CPLAtoGIntBig() */ +/************************************************************************/ + +/** + * Convert a string to a 64 bit signed integer. + * + * @param pszString String containing 64 bit signed integer. + * @return 64 bit signed integer. + * @since GDAL 2.0 + */ + +GIntBig CPLAtoGIntBig( const char* pszString ) +{ +#if defined(__MSVCRT__) || (defined(WIN32) && defined(_MSC_VER)) + return _atoi64( pszString ); +# elif HAVE_ATOLL + return atoll( pszString ); +#else + return atol( pszString ); +#endif +} + +#if defined(__MINGW32__) + +// mingw atoll() doesn't return ERANGE in case of overflow +int CPLAtoGIntBigExHasOverflow(const char* pszString, GIntBig nVal) +{ + if( strlen(pszString) <= 18 ) + return FALSE; + while( *pszString == ' ' ) + pszString ++; + if( *pszString == '+' ) + pszString ++; + char szBuffer[32]; + sprintf(szBuffer, CPL_FRMT_GIB, nVal); + return strcmp(szBuffer, pszString) != 0; +} + +#endif + +/************************************************************************/ +/* CPLAtoGIntBigEx() */ +/************************************************************************/ + +/** + * Convert a string to a 64 bit signed integer. + * + * @param pszString String containing 64 bit signed integer. + * @param bWarn Issue a warning if an overflow occurs during conversion + * @param pbOverflow Pointer to an integer to store if an overflow occured, or NULL + * @return 64 bit signed integer. + * @since GDAL 2.0 + */ + +GIntBig CPLAtoGIntBigEx( const char* pszString, int bWarn, int *pbOverflow ) +{ + GIntBig nVal; + errno = 0; +#if defined(__MSVCRT__) || (defined(WIN32) && defined(_MSC_VER)) + nVal = _atoi64( pszString ); +# elif HAVE_ATOLL + nVal = atoll( pszString ); +#else + nVal = atol( pszString ); +#endif + if( errno == ERANGE +#if defined(__MINGW32__) + || CPLAtoGIntBigExHasOverflow(pszString, nVal) +#endif + ) + { + if( pbOverflow ) *pbOverflow = TRUE; + if( bWarn ) + { + CPLError(CE_Warning, CPLE_AppDefined, + "64 bit integer overflow when converting %s", + pszString); + } + while( *pszString == ' ' ) + pszString ++; + return (*pszString == '-' ) ? GINTBIG_MIN : GINTBIG_MAX; + } + else if( pbOverflow ) *pbOverflow = FALSE; + return nVal; +} + +/************************************************************************/ +/* CPLScanPointer() */ +/************************************************************************/ + +/** + * Extract pointer from string. + * + * Scan up to a maximum number of characters from a string and convert + * the result to a pointer. + * + * @param pszString String containing characters to be scanned. It may be + * terminated with a null character. + * + * @param nMaxLength The maximum number of character to consider as part + * of the number. Less characters will be considered if a null character + * is encountered. + * + * @return pointer value, converted from its ASCII form. + */ + +void *CPLScanPointer( const char *pszString, int nMaxLength ) +{ + void *pResult; + char szTemp[128]; + +/* -------------------------------------------------------------------- */ +/* Compute string into local buffer, and terminate it. */ +/* -------------------------------------------------------------------- */ + if( nMaxLength > (int) sizeof(szTemp)-1 ) + nMaxLength = sizeof(szTemp)-1; + + strncpy( szTemp, pszString, nMaxLength ); + szTemp[nMaxLength] = '\0'; + +/* -------------------------------------------------------------------- */ +/* On MSVC we have to scanf pointer values without the 0x */ +/* prefix. */ +/* -------------------------------------------------------------------- */ + if( EQUALN(szTemp,"0x",2) ) + { + pResult = NULL; + +#if defined(__MSVCRT__) || (defined(WIN32) && defined(_MSC_VER)) + sscanf( szTemp+2, "%p", &pResult ); +#else + sscanf( szTemp, "%p", &pResult ); + + /* Solaris actually behaves like MSVCRT... */ + if (pResult == NULL) + { + sscanf( szTemp+2, "%p", &pResult ); + } +#endif + } + + else + { +#if SIZEOF_VOIDP == 8 + pResult = (void *) CPLScanUIntBig( szTemp, nMaxLength ); +#else + pResult = (void *) CPLScanULong( szTemp, nMaxLength ); +#endif + } + + return pResult; +} + +/************************************************************************/ +/* CPLScanDouble() */ +/************************************************************************/ + +/** + * Extract double from string. + * + * Scan up to a maximum number of characters from a string and convert the + * result to a double. This function uses CPLAtof() to convert string to + * double value, so it uses a comma as a decimal delimiter. + * + * @param pszString String containing characters to be scanned. It may be + * terminated with a null character. + * + * @param nMaxLength The maximum number of character to consider as part + * of the number. Less characters will be considered if a null character + * is encountered. + * + * @return Double value, converted from its ASCII form. + */ + +double CPLScanDouble( const char *pszString, int nMaxLength ) +{ + int i; + double dfValue; + char *pszValue = (char *)CPLMalloc( nMaxLength + 1); + +/* -------------------------------------------------------------------- */ +/* Compute string into local buffer, and terminate it. */ +/* -------------------------------------------------------------------- */ + strncpy( pszValue, pszString, nMaxLength ); + pszValue[nMaxLength] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Make a pass through converting 'D's to 'E's. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nMaxLength; i++ ) + if ( pszValue[i] == 'd' || pszValue[i] == 'D' ) + pszValue[i] = 'E'; + +/* -------------------------------------------------------------------- */ +/* The conversion itself. */ +/* -------------------------------------------------------------------- */ + dfValue = CPLAtof( pszValue ); + + CPLFree( pszValue ); + return dfValue; +} + +/************************************************************************/ +/* CPLPrintString() */ +/************************************************************************/ + +/** + * Copy the string pointed to by pszSrc, NOT including the terminating + * `\\0' character, to the array pointed to by pszDest. + * + * @param pszDest Pointer to the destination string buffer. Should be + * large enough to hold the resulting string. + * + * @param pszSrc Pointer to the source buffer. + * + * @param nMaxLen Maximum length of the resulting string. If string length + * is greater than nMaxLen, it will be truncated. + * + * @return Number of characters printed. + */ + +int CPLPrintString( char *pszDest, const char *pszSrc, int nMaxLen ) +{ + char *pszTemp = pszDest; + int nChars = 0; + + if ( !pszDest ) + return 0; + + if ( !pszSrc ) + { + *pszDest = '\0'; + return 1; + } + + while ( nChars < nMaxLen && *pszSrc ) + { + *pszTemp++ = *pszSrc++; + nChars++; + } + + return nChars; +} + +/************************************************************************/ +/* CPLPrintStringFill() */ +/************************************************************************/ + +/** + * Copy the string pointed to by pszSrc, NOT including the terminating + * `\\0' character, to the array pointed to by pszDest. Remainder of the + * destination string will be filled with space characters. This is only + * difference from the PrintString(). + * + * @param pszDest Pointer to the destination string buffer. Should be + * large enough to hold the resulting string. + * + * @param pszSrc Pointer to the source buffer. + * + * @param nMaxLen Maximum length of the resulting string. If string length + * is greater than nMaxLen, it will be truncated. + * + * @return Number of characters printed. + */ + +int CPLPrintStringFill( char *pszDest, const char *pszSrc, int nMaxLen ) +{ + char *pszTemp = pszDest; + + if ( !pszDest ) + return 0; + + if ( !pszSrc ) + { + memset( pszDest, ' ', nMaxLen ); + return nMaxLen; + } + + while ( nMaxLen && *pszSrc ) + { + *pszTemp++ = *pszSrc++; + nMaxLen--; + } + + if ( nMaxLen ) + memset( pszTemp, ' ', nMaxLen ); + + return nMaxLen; +} + +/************************************************************************/ +/* CPLPrintInt32() */ +/************************************************************************/ + +/** + * Print GInt32 value into specified string buffer. This string will not + * be NULL-terminated. + * + * @param pszBuffer Pointer to the destination string buffer. Should be + * large enough to hold the resulting string. Note, that the string will + * not be NULL-terminated, so user should do this himself, if needed. + * + * @param iValue Numerical value to print. + * + * @param nMaxLen Maximum length of the resulting string. If string length + * is greater than nMaxLen, it will be truncated. + * + * @return Number of characters printed. + */ + +int CPLPrintInt32( char *pszBuffer, GInt32 iValue, int nMaxLen ) +{ + char szTemp[64]; + + if ( !pszBuffer ) + return 0; + + if ( nMaxLen >= 64 ) + nMaxLen = 63; + +#if UINT_MAX == 65535 + sprintf( szTemp, "%*ld", nMaxLen, iValue ); +#else + sprintf( szTemp, "%*d", nMaxLen, iValue ); +#endif + + return CPLPrintString( pszBuffer, szTemp, nMaxLen ); +} + +/************************************************************************/ +/* CPLPrintUIntBig() */ +/************************************************************************/ + +/** + * Print GUIntBig value into specified string buffer. This string will not + * be NULL-terminated. + * + * @param pszBuffer Pointer to the destination string buffer. Should be + * large enough to hold the resulting string. Note, that the string will + * not be NULL-terminated, so user should do this himself, if needed. + * + * @param iValue Numerical value to print. + * + * @param nMaxLen Maximum length of the resulting string. If string length + * is greater than nMaxLen, it will be truncated. + * + * @return Number of characters printed. + */ + +int CPLPrintUIntBig( char *pszBuffer, GUIntBig iValue, int nMaxLen ) +{ + char szTemp[64]; + + if ( !pszBuffer ) + return 0; + + if ( nMaxLen >= 64 ) + nMaxLen = 63; + +#if defined(__MSVCRT__) || (defined(WIN32) && defined(_MSC_VER)) + sprintf( szTemp, "%*I64d", nMaxLen, iValue ); +# elif HAVE_LONG_LONG + sprintf( szTemp, "%*lld", nMaxLen, (long long) iValue ); +// sprintf( szTemp, "%*Ld", nMaxLen, (long long) iValue ); +#else + sprintf( szTemp, "%*ld", nMaxLen, iValue ); +#endif + + return CPLPrintString( pszBuffer, szTemp, nMaxLen ); +} + +/************************************************************************/ +/* CPLPrintPointer() */ +/************************************************************************/ + +/** + * Print pointer value into specified string buffer. This string will not + * be NULL-terminated. + * + * @param pszBuffer Pointer to the destination string buffer. Should be + * large enough to hold the resulting string. Note, that the string will + * not be NULL-terminated, so user should do this himself, if needed. + * + * @param pValue Pointer to ASCII encode. + * + * @param nMaxLen Maximum length of the resulting string. If string length + * is greater than nMaxLen, it will be truncated. + * + * @return Number of characters printed. + */ + +int CPLPrintPointer( char *pszBuffer, void *pValue, int nMaxLen ) +{ + char szTemp[64]; + + if ( !pszBuffer ) + return 0; + + if ( nMaxLen >= 64 ) + nMaxLen = 63; + + sprintf( szTemp, "%p", pValue ); + + // On windows, and possibly some other platforms the sprintf("%p") + // does not prefix things with 0x so it is hard to know later if the + // value is hex encoded. Fix this up here. + + if( !EQUALN(szTemp,"0x",2) ) + sprintf( szTemp, "0x%p", pValue ); + + return CPLPrintString( pszBuffer, szTemp, nMaxLen ); +} + +/************************************************************************/ +/* CPLPrintDouble() */ +/************************************************************************/ + +/** + * Print double value into specified string buffer. Exponential character + * flag 'E' (or 'e') will be replaced with 'D', as in Fortran. Resulting + * string will not to be NULL-terminated. + * + * @param pszBuffer Pointer to the destination string buffer. Should be + * large enough to hold the resulting string. Note, that the string will + * not be NULL-terminated, so user should do this himself, if needed. + * + * @param pszFormat Format specifier (for example, "%16.9E"). + * + * @param dfValue Numerical value to print. + * + * @param pszLocale Pointer to a character string containing locale name + * ("C", "POSIX", "us_US", "ru_RU.KOI8-R" etc.). If NULL we will not + * manipulate with locale settings and current process locale will be used for + * printing. With the pszLocale option we can control what exact locale + * will be used for printing a numeric value to the string (in most cases + * it should be C/POSIX). + * + * @return Number of characters printed. + */ + +int CPLPrintDouble( char *pszBuffer, const char *pszFormat, + double dfValue, CPL_UNUSED const char *pszLocale ) +{ + if ( !pszBuffer ) + return 0; + + const int double_buffer_size = 64; + char szTemp[double_buffer_size]; + +#if defined(HAVE_SNPRINTF) + CPLsnprintf( szTemp, double_buffer_size, pszFormat, dfValue ); +#else + CPLsprintf( szTemp, pszFormat, dfValue ); +#endif + szTemp[double_buffer_size - 1] = '\0'; + + for( int i = 0; szTemp[i] != '\0'; i++ ) + { + if( szTemp[i] == 'E' || szTemp[i] == 'e' ) + szTemp[i] = 'D'; + } + + return CPLPrintString( pszBuffer, szTemp, 64 ); +} + +/************************************************************************/ +/* CPLPrintTime() */ +/************************************************************************/ + +/** + * Print specified time value accordingly to the format options and + * specified locale name. This function does following: + * + * - if locale parameter is not NULL, the current locale setting will be + * stored and replaced with the specified one; + * - format time value with the strftime(3) function; + * - restore back current locale, if was saved. + * + * @param pszBuffer Pointer to the destination string buffer. Should be + * large enough to hold the resulting string. Note, that the string will + * not be NULL-terminated, so user should do this himself, if needed. + * + * @param nMaxLen Maximum length of the resulting string. If string length is + * greater than nMaxLen, it will be truncated. + * + * @param pszFormat Controls the output format. Options are the same as + * for strftime(3) function. + * + * @param poBrokenTime Pointer to the broken-down time structure. May be + * requested with the VSIGMTime() and VSILocalTime() functions. + * + * @param pszLocale Pointer to a character string containing locale name + * ("C", "POSIX", "us_US", "ru_RU.KOI8-R" etc.). If NULL we will not + * manipulate with locale settings and current process locale will be used for + * printing. Be aware that it may be unsuitable to use current locale for + * printing time, because all names will be printed in your native language, + * as well as time format settings also may be ajusted differently from the + * C/POSIX defaults. To solve these problems this option was introdiced. + * + * @return Number of characters printed. + */ + +#ifndef WIN32CE /* XXX - mloskot - strftime is not available yet. */ + +int CPLPrintTime( char *pszBuffer, int nMaxLen, const char *pszFormat, + const struct tm *poBrokenTime, const char *pszLocale ) +{ + char *pszTemp = (char *)CPLMalloc( (nMaxLen + 1) * sizeof(char) ); + int nChars; + +#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) + char *pszCurLocale = NULL; + + if ( pszLocale || EQUAL( pszLocale, "" ) ) + { + // Save the current locale + pszCurLocale = CPLsetlocale(LC_ALL, NULL ); + // Set locale to the specified value + CPLsetlocale( LC_ALL, pszLocale ); + } +#else + (void) pszLocale; +#endif + + if ( !strftime( pszTemp, nMaxLen + 1, pszFormat, poBrokenTime ) ) + memset( pszTemp, 0, nMaxLen + 1); + +#if defined(HAVE_LOCALE_H) && defined(HAVE_SETLOCALE) + // Restore stored locale back + if ( pszCurLocale ) + CPLsetlocale( LC_ALL, pszCurLocale ); +#endif + + nChars = CPLPrintString( pszBuffer, pszTemp, nMaxLen ); + + CPLFree( pszTemp ); + + return nChars; +} + +#endif + +/************************************************************************/ +/* CPLVerifyConfiguration() */ +/************************************************************************/ + +void CPLVerifyConfiguration() + +{ +/* -------------------------------------------------------------------- */ +/* Verify data types. */ +/* -------------------------------------------------------------------- */ + CPLAssert( sizeof(GInt32) == 4 ); + CPLAssert( sizeof(GInt16) == 2 ); + CPLAssert( sizeof(GByte) == 1 ); + + if( sizeof(GInt32) != 4 ) + CPLError( CE_Fatal, CPLE_AppDefined, + "sizeof(GInt32) == %d ... yow!\n", + (int) sizeof(GInt32) ); + +/* -------------------------------------------------------------------- */ +/* Verify byte order */ +/* -------------------------------------------------------------------- */ + GInt32 nTest; + + nTest = 1; + +#ifdef CPL_LSB + if( ((GByte *) &nTest)[0] != 1 ) +#endif +#ifdef CPL_MSB + if( ((GByte *) &nTest)[3] != 1 ) +#endif + CPLError( CE_Fatal, CPLE_AppDefined, + "CPLVerifyConfiguration(): byte order set wrong.\n" ); +} + +/* Uncomment to get list of options that have been fetched and set */ +//#define DEBUG_CONFIG_OPTIONS + +#ifdef DEBUG_CONFIG_OPTIONS + +#include +#include "cpl_multiproc.h" + +static void* hRegisterConfigurationOptionMutex = 0; +static std::set* paoGetKeys = NULL; +static std::set* paoSetKeys = NULL; + +/************************************************************************/ +/* CPLShowAccessedOptions() */ +/************************************************************************/ + +static void CPLShowAccessedOptions() +{ + std::set::iterator aoIter; + + printf("Configuration options accessed in reading : "), + aoIter = paoGetKeys->begin(); + while(aoIter != paoGetKeys->end()) + { + printf("%s, ", (*aoIter).c_str()); + aoIter ++; + } + printf("\n"); + + printf("Configuration options accessed in writing : "), + aoIter = paoSetKeys->begin(); + while(aoIter != paoSetKeys->end()) + { + printf("%s, ", (*aoIter).c_str()); + aoIter ++; + } + printf("\n"); + + delete paoGetKeys; + delete paoSetKeys; + paoGetKeys = paoSetKeys = NULL; +} + +/************************************************************************/ +/* CPLAccessConfigOption() */ +/************************************************************************/ + +static void CPLAccessConfigOption(const char* pszKey, int bGet) +{ + CPLMutexHolderD(&hRegisterConfigurationOptionMutex); + if (paoGetKeys == NULL) + { + paoGetKeys = new std::set; + paoSetKeys = new std::set; + atexit(CPLShowAccessedOptions); + } + if (bGet) + paoGetKeys->insert(pszKey); + else + paoSetKeys->insert(pszKey); +} +#endif + +/************************************************************************/ +/* CPLGetConfigOption() */ +/************************************************************************/ + +/** + * Get the value of a configuration option. + * + * The value is the value of a (key, value) option set with CPLSetConfigOption(). + * If the given option was no defined with CPLSetConfigOption(), it tries to find + * it in environment variables. + * + * Note: the string returned by CPLGetConfigOption() might be short-lived, and in + * particular it will become invalid after a call to CPLSetConfigOption() with the + * same key. + * + * To override temporary a potentially existing option with a new value, you can + * use the following snippet : + *
    +  *     // backup old value
    +  *     const char* pszOldValTmp = CPLGetConfigOption(pszKey, NULL);
    +  *     char* pszOldVal = pszOldValTmp ? CPLStrdup(pszOldValTmp) : NULL;
    +  *     // override with new value
    +  *     CPLSetConfigOption(pszKey, pszNewVal);
    +  *     // do something useful
    +  *     // restore old value
    +  *     CPLSetConfigOption(pszKey, pszOldVal);
    +  *     CPLFree(pszOldVal);
    +  * 
    + * + * @param pszKey the key of the option to retrieve + * @param pszDefault a default value if the key does not match existing defined options (may be NULL) + * @return the value associated to the key, or the default value if not found + * + * @see CPLSetConfigOption(), http://trac.osgeo.org/gdal/wiki/ConfigOptions + */ +const char * CPL_STDCALL +CPLGetConfigOption( const char *pszKey, const char *pszDefault ) + +{ +#ifdef DEBUG_CONFIG_OPTIONS + CPLAccessConfigOption(pszKey, TRUE); +#endif + + const char *pszResult = NULL; + + char **papszTLConfigOptions = (char **) CPLGetTLS( CTLS_CONFIGOPTIONS ); + if( papszTLConfigOptions != NULL ) + pszResult = CSLFetchNameValue( papszTLConfigOptions, pszKey ); + + if( pszResult == NULL ) + { + CPLMutexHolderD( &hConfigMutex ); + + pszResult = CSLFetchNameValue( (char **) papszConfigOptions, pszKey ); + } + +#if !defined(WIN32CE) + if( pszResult == NULL ) + pszResult = getenv( pszKey ); +#endif + + if( pszResult == NULL ) + return pszDefault; + else + return pszResult; +} + +/************************************************************************/ +/* CPLSetConfigOption() */ +/************************************************************************/ + +/** + * Set a configuration option for GDAL/OGR use. + * + * Those options are defined as a (key, value) couple. The value corresponding + * to a key can be got later with the CPLGetConfigOption() method. + * + * This mechanism is similar to environment variables, but options set with + * CPLSetConfigOption() overrides, for CPLGetConfigOption() point of view, + * values defined in the environment. + * + * If CPLSetConfigOption() is called several times with the same key, the + * value provided during the last call will be used. + * + * Options can also be passed on the command line of most GDAL utilities + * with the with '--config KEY VALUE'. For example, + * ogrinfo --config CPL_DEBUG ON ~/data/test/point.shp + * + * This function can also be used to clear a setting by passing NULL as the + * value (note: passing NULL will not unset an existing environment variable; + * it will just unset a value previously set by CPLSetConfigOption()). + * + * @param pszKey the key of the option + * @param pszValue the value of the option, or NULL to clear a setting. + * + * @see http://trac.osgeo.org/gdal/wiki/ConfigOptions + */ +void CPL_STDCALL +CPLSetConfigOption( const char *pszKey, const char *pszValue ) + +{ +#ifdef DEBUG_CONFIG_OPTIONS + CPLAccessConfigOption(pszKey, FALSE); +#endif + CPLMutexHolderD( &hConfigMutex ); + + papszConfigOptions = (volatile char **) + CSLSetNameValue( (char **) papszConfigOptions, pszKey, pszValue ); +} + +/************************************************************************/ +/* CPLSetThreadLocalTLSFreeFunc() */ +/************************************************************************/ + +/* non-stdcall wrapper function for CSLDestroy() (#5590) */ +static void CPLSetThreadLocalTLSFreeFunc( void* pData ) +{ + CSLDestroy( (char**) pData ); +} + +/************************************************************************/ +/* CPLSetThreadLocalConfigOption() */ +/************************************************************************/ + +/** + * Set a configuration option for GDAL/OGR use. + * + * Those options are defined as a (key, value) couple. The value corresponding + * to a key can be got later with the CPLGetConfigOption() method. + * + * This function sets the configuration option that only applies in the + * current thread, as opposed to CPLSetConfigOption() which sets an option + * that applies on all threads. + * + * This function can also be used to clear a setting by passing NULL as the + * value (note: passing NULL will not unset an existing environment variable; + * it will just unset a value previously set by CPLSetThreadLocalConfigOption()). + * + * @param pszKey the key of the option + * @param pszValue the value of the option, or NULL to clear a setting. + */ + +void CPL_STDCALL +CPLSetThreadLocalConfigOption( const char *pszKey, const char *pszValue ) + +{ +#ifdef DEBUG_CONFIG_OPTIONS + CPLAccessConfigOption(pszKey, FALSE); +#endif + + char **papszTLConfigOptions = (char **) CPLGetTLS( CTLS_CONFIGOPTIONS ); + + papszTLConfigOptions = + CSLSetNameValue( papszTLConfigOptions, pszKey, pszValue ); + + CPLSetTLSWithFreeFunc( CTLS_CONFIGOPTIONS, papszTLConfigOptions, + CPLSetThreadLocalTLSFreeFunc ); +} + +/************************************************************************/ +/* CPLFreeConfig() */ +/************************************************************************/ + +void CPL_STDCALL CPLFreeConfig() + +{ + { + CPLMutexHolderD( &hConfigMutex ); + + CSLDestroy( (char **) papszConfigOptions); + papszConfigOptions = NULL; + + char **papszTLConfigOptions = (char **) CPLGetTLS( CTLS_CONFIGOPTIONS ); + if( papszTLConfigOptions != NULL ) + { + CSLDestroy( papszTLConfigOptions ); + CPLSetTLS( CTLS_CONFIGOPTIONS, NULL, FALSE ); + } + } + CPLDestroyMutex( hConfigMutex ); + hConfigMutex = NULL; +} + +/************************************************************************/ +/* CPLStat() */ +/* */ +/* Same as VSIStat() except it works on "C:" as if it were */ +/* "C:\". */ +/************************************************************************/ + +int CPLStat( const char *pszPath, VSIStatBuf *psStatBuf ) + +{ + if( strlen(pszPath) == 2 && pszPath[1] == ':' ) + { + char szAltPath[4]; + + szAltPath[0] = pszPath[0]; + szAltPath[1] = pszPath[1]; + szAltPath[2] = '\\'; + szAltPath[3] = '\0'; + return VSIStat( szAltPath, psStatBuf ); + } + else + return VSIStat( pszPath, psStatBuf ); +} + +/************************************************************************/ +/* proj_strtod() */ +/************************************************************************/ +static double +proj_strtod(char *nptr, char **endptr) + +{ + char c, *cp = nptr; + double result; + + /* + * Scan for characters which cause problems with VC++ strtod() + */ + while ((c = *cp) != '\0') { + if (c == 'd' || c == 'D') { + + /* + * Found one, so NUL it out, call strtod(), + * then restore it and return + */ + *cp = '\0'; + result = CPLStrtod(nptr, endptr); + *cp = c; + return result; + } + ++cp; + } + + /* no offending characters, just handle normally */ + + return CPLStrtod(nptr, endptr); +} + +/************************************************************************/ +/* CPLDMSToDec() */ +/************************************************************************/ + +static const char*sym = "NnEeSsWw"; +static const double vm[] = { 1.0, 0.0166666666667, 0.00027777778 }; + +double CPLDMSToDec( const char *is ) + +{ + int sign, n, nl; + char *p, *s, work[64]; + double v, tv; + + /* copy sting into work space */ + while (isspace((unsigned char)(sign = *is))) ++is; + for (n = sizeof(work), s = work, p = (char *)is; isgraph(*p) && --n ; ) + *s++ = *p++; + *s = '\0'; + /* it is possible that a really odd input (like lots of leading + zeros) could be truncated in copying into work. But ... */ + sign = *(s = work); + if (sign == '+' || sign == '-') s++; + else sign = '+'; + for (v = 0., nl = 0 ; nl < 3 ; nl = n + 1 ) { + if (!(isdigit(*s) || *s == '.')) break; + if ((tv = proj_strtod(s, &s)) == HUGE_VAL) + return tv; + switch (*s) { + case 'D': case 'd': + n = 0; break; + case '\'': + n = 1; break; + case '"': + n = 2; break; + case 'r': case 'R': + if (nl) { + return 0.0; + } + ++s; + v = tv; + goto skip; + default: + v += tv * vm[nl]; + skip: n = 4; + continue; + } + if (n < nl) { + return 0.0; + } + v += tv * vm[n]; + ++s; + } + /* postfix sign */ + if (*s && ((p = (char *) strchr(sym, *s))) != NULL) { + sign = (p - sym) >= 4 ? '-' : '+'; + ++s; + } + if (sign == '-') + v = -v; + + return v; +} + + +/************************************************************************/ +/* CPLDecToDMS() */ +/* */ +/* Translate a decimal degrees value to a DMS string with */ +/* hemisphere. */ +/************************************************************************/ + +const char *CPLDecToDMS( double dfAngle, const char * pszAxis, + int nPrecision ) + +{ + VALIDATE_POINTER1( pszAxis, "CPLDecToDMS", "" ); + + int nDegrees, nMinutes; + double dfSeconds, dfABSAngle, dfEpsilon; + char szFormat[30]; + const char *pszHemisphere; + static CPL_THREADLOCAL char szBuffer[50] = { 0 }; + + + dfEpsilon = (0.5/3600.0) * pow(0.1,nPrecision); + + dfABSAngle = ABS(dfAngle) + dfEpsilon; + if (dfABSAngle > 361) + { + return "Invalid angle"; + } + + nDegrees = (int) dfABSAngle; + nMinutes = (int) ((dfABSAngle - nDegrees) * 60); + dfSeconds = dfABSAngle * 3600 - nDegrees*3600 - nMinutes*60; + + if( dfSeconds > dfEpsilon * 3600.0 ) + dfSeconds -= dfEpsilon * 3600.0; + + if( EQUAL(pszAxis,"Long") && dfAngle < 0.0 ) + pszHemisphere = "W"; + else if( EQUAL(pszAxis,"Long") ) + pszHemisphere = "E"; + else if( dfAngle < 0.0 ) + pszHemisphere = "S"; + else + pszHemisphere = "N"; + + CPLsprintf( szFormat, "%%3dd%%2d\'%%%d.%df\"%s", nPrecision+3, nPrecision, pszHemisphere ); + CPLsprintf( szBuffer, szFormat, nDegrees, nMinutes, dfSeconds ); + + return( szBuffer ); +} + +/************************************************************************/ +/* CPLPackedDMSToDec() */ +/************************************************************************/ + +/** + * Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees. + * + * This function converts a packed DMS angle to seconds. The standard + * packed DMS format is: + * + * degrees * 1000000 + minutes * 1000 + seconds + * + * Example: ang = 120025045.25 yields + * deg = 120 + * min = 25 + * sec = 45.25 + * + * The algorithm used for the conversion is as follows: + * + * 1. The absolute value of the angle is used. + * + * 2. The degrees are separated out: + * deg = ang/1000000 (fractional portion truncated) + * + * 3. The minutes are separated out: + * min = (ang - deg * 1000000) / 1000 (fractional portion truncated) + * + * 4. The seconds are then computed: + * sec = ang - deg * 1000000 - min * 1000 + * + * 5. The total angle in seconds is computed: + * sec = deg * 3600.0 + min * 60.0 + sec + * + * 6. The sign of sec is set to that of the input angle. + * + * Packed DMS values used by the USGS GCTP package and probably by other + * software. + * + * NOTE: This code does not validate input value. If you give the wrong + * value, you will get the wrong result. + * + * @param dfPacked Angle in packed DMS format. + * + * @return Angle in decimal degrees. + * + */ + +double CPLPackedDMSToDec( double dfPacked ) +{ + double dfDegrees, dfMinutes, dfSeconds, dfSign; + + dfSign = ( dfPacked < 0.0 )? -1 : 1; + + dfSeconds = ABS( dfPacked ); + dfDegrees = floor(dfSeconds / 1000000.0); + dfSeconds = dfSeconds - dfDegrees * 1000000.0; + dfMinutes = floor(dfSeconds / 1000.0); + dfSeconds = dfSeconds - dfMinutes * 1000.0; + dfSeconds = dfSign * ( dfDegrees * 3600.0 + dfMinutes * 60.0 + dfSeconds); + dfDegrees = dfSeconds / 3600.0; + + return dfDegrees; +} + +/************************************************************************/ +/* CPLDecToPackedDMS() */ +/************************************************************************/ +/** + * Convert decimal degrees into packed DMS value (DDDMMMSSS.SS). + * + * This function converts a value, specified in decimal degrees into + * packed DMS angle. The standard packed DMS format is: + * + * degrees * 1000000 + minutes * 1000 + seconds + * + * See also CPLPackedDMSToDec(). + * + * @param dfDec Angle in decimal degrees. + * + * @return Angle in packed DMS format. + * + */ + +double CPLDecToPackedDMS( double dfDec ) +{ + double dfDegrees, dfMinutes, dfSeconds, dfSign; + + dfSign = ( dfDec < 0.0 )? -1 : 1; + + dfDec = ABS( dfDec ); + dfDegrees = floor( dfDec ); + dfMinutes = floor( ( dfDec - dfDegrees ) * 60.0 ); + dfSeconds = ( dfDec - dfDegrees ) * 3600.0 - dfMinutes * 60.0; + + return dfSign * (dfDegrees * 1000000.0 + dfMinutes * 1000.0 + dfSeconds); +} + +/************************************************************************/ +/* CPLStringToComplex() */ +/************************************************************************/ + +void CPL_DLL CPLStringToComplex( const char *pszString, + double *pdfReal, double *pdfImag ) + +{ + int i; + int iPlus = -1, iImagEnd = -1; + + while( *pszString == ' ' ) + pszString++; + + *pdfReal = CPLAtof(pszString); + *pdfImag = 0.0; + + for( i = 0; pszString[i] != '\0' && pszString[i] != ' ' && i < 100; i++ ) + { + if( pszString[i] == '+' && i > 0 ) + iPlus = i; + if( pszString[i] == '-' && i > 0 ) + iPlus = i; + if( pszString[i] == 'i' ) + iImagEnd = i; + } + + if( iPlus > -1 && iImagEnd > -1 && iPlus < iImagEnd ) + { + *pdfImag = CPLAtof(pszString + iPlus); + } + + return; +} + +/************************************************************************/ +/* CPLOpenShared() */ +/************************************************************************/ + +/** + * Open a shared file handle. + * + * Some operating systems have limits on the number of file handles that can + * be open at one time. This function attempts to maintain a registry of + * already open file handles, and reuse existing ones if the same file + * is requested by another part of the application. + * + * Note that access is only shared for access types "r", "rb", "r+" and + * "rb+". All others will just result in direct VSIOpen() calls. Keep in + * mind that a file is only reused if the file name is exactly the same. + * Different names referring to the same file will result in different + * handles. + * + * The VSIFOpen() or VSIFOpenL() function is used to actually open the file, + * when an existing file handle can't be shared. + * + * @param pszFilename the name of the file to open. + * @param pszAccess the normal fopen()/VSIFOpen() style access string. + * @param bLarge If TRUE VSIFOpenL() (for large files) will be used instead of + * VSIFOpen(). + * + * @return a file handle or NULL if opening fails. + */ + +FILE *CPLOpenShared( const char *pszFilename, const char *pszAccess, + int bLarge ) + +{ + int i; + int bReuse; + CPLMutexHolderD( &hSharedFileMutex ); + GIntBig nPID = CPLGetPID(); + +/* -------------------------------------------------------------------- */ +/* Is there an existing file we can use? */ +/* -------------------------------------------------------------------- */ + bReuse = EQUAL(pszAccess,"rb") || EQUAL(pszAccess, "rb+"); + + for( i = 0; bReuse && i < nSharedFileCount; i++ ) + { + if( strcmp(pasSharedFileList[i].pszFilename,pszFilename) == 0 + && !bLarge == !pasSharedFileList[i].bLarge + && EQUAL(pasSharedFileList[i].pszAccess,pszAccess) + && nPID == pasSharedFileListExtra[i].nPID) + { + pasSharedFileList[i].nRefCount++; + return pasSharedFileList[i].fp; + } + } + +/* -------------------------------------------------------------------- */ +/* Open the file. */ +/* -------------------------------------------------------------------- */ + FILE *fp; + + if( bLarge ) + fp = (FILE*) VSIFOpenL( pszFilename, pszAccess ); + else + fp = VSIFOpen( pszFilename, pszAccess ); + + if( fp == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Add an entry to the list. */ +/* -------------------------------------------------------------------- */ + nSharedFileCount++; + + pasSharedFileList = (CPLSharedFileInfo *) + CPLRealloc( (void *) pasSharedFileList, + sizeof(CPLSharedFileInfo) * nSharedFileCount ); + pasSharedFileListExtra = (CPLSharedFileInfoExtra *) + CPLRealloc( (void *) pasSharedFileListExtra, + sizeof(CPLSharedFileInfoExtra) * nSharedFileCount ); + + pasSharedFileList[nSharedFileCount-1].fp = fp; + pasSharedFileList[nSharedFileCount-1].nRefCount = 1; + pasSharedFileList[nSharedFileCount-1].bLarge = bLarge; + pasSharedFileList[nSharedFileCount-1].pszFilename =CPLStrdup(pszFilename); + pasSharedFileList[nSharedFileCount-1].pszAccess = CPLStrdup(pszAccess); + pasSharedFileListExtra[nSharedFileCount-1].nPID = nPID; + + return fp; +} + +/************************************************************************/ +/* CPLCloseShared() */ +/************************************************************************/ + +/** + * Close shared file. + * + * Dereferences the indicated file handle, and closes it if the reference + * count has dropped to zero. A CPLError() is issued if the file is not + * in the shared file list. + * + * @param fp file handle from CPLOpenShared() to deaccess. + */ + +void CPLCloseShared( FILE * fp ) + +{ + CPLMutexHolderD( &hSharedFileMutex ); + int i; + +/* -------------------------------------------------------------------- */ +/* Search for matching information. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nSharedFileCount && fp != pasSharedFileList[i].fp; i++ ){} + + if( i == nSharedFileCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to find file handle %p in CPLCloseShared().", + fp ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Dereference and return if there are still some references. */ +/* -------------------------------------------------------------------- */ + if( --pasSharedFileList[i].nRefCount > 0 ) + return; + +/* -------------------------------------------------------------------- */ +/* Close the file, and remove the information. */ +/* -------------------------------------------------------------------- */ + if( pasSharedFileList[i].bLarge ) + VSIFCloseL( (VSILFILE*) pasSharedFileList[i].fp ); + else + VSIFClose( pasSharedFileList[i].fp ); + + CPLFree( pasSharedFileList[i].pszFilename ); + CPLFree( pasSharedFileList[i].pszAccess ); + + nSharedFileCount --; + memmove( (void *) (pasSharedFileList + i), + (void *) (pasSharedFileList + nSharedFileCount), + sizeof(CPLSharedFileInfo) ); + memmove( (void *) (pasSharedFileListExtra + i), + (void *) (pasSharedFileListExtra + nSharedFileCount), + sizeof(CPLSharedFileInfoExtra) ); + + if( nSharedFileCount == 0 ) + { + CPLFree( (void *) pasSharedFileList ); + pasSharedFileList = NULL; + CPLFree( (void *) pasSharedFileListExtra ); + pasSharedFileListExtra = NULL; + } +} + + +/************************************************************************/ +/* CPLCleanupSharedFileMutex() */ +/************************************************************************/ + +void CPLCleanupSharedFileMutex() +{ + if( hSharedFileMutex != NULL ) + { + CPLDestroyMutex(hSharedFileMutex); + hSharedFileMutex = NULL; + } +} + +/************************************************************************/ +/* CPLGetSharedList() */ +/************************************************************************/ + +/** + * Fetch list of open shared files. + * + * @param pnCount place to put the count of entries. + * + * @return the pointer to the first in the array of shared file info + * structures. + */ + +CPLSharedFileInfo *CPLGetSharedList( int *pnCount ) + +{ + if( pnCount != NULL ) + *pnCount = nSharedFileCount; + + return (CPLSharedFileInfo *) pasSharedFileList; +} + +/************************************************************************/ +/* CPLDumpSharedList() */ +/************************************************************************/ + +/** + * Report open shared files. + * + * Dumps all open shared files to the indicated file handle. If the + * file handle is NULL information is sent via the CPLDebug() call. + * + * @param fp File handle to write to. + */ + +void CPLDumpSharedList( FILE *fp ) + +{ + int i; + + if( nSharedFileCount > 0 ) + { + if( fp == NULL ) + CPLDebug( "CPL", "%d Shared files open.", nSharedFileCount ); + else + fprintf( fp, "%d Shared files open.", nSharedFileCount ); + } + + for( i = 0; i < nSharedFileCount; i++ ) + { + if( fp == NULL ) + CPLDebug( "CPL", + "%2d %d %4s %s", + pasSharedFileList[i].nRefCount, + pasSharedFileList[i].bLarge, + pasSharedFileList[i].pszAccess, + pasSharedFileList[i].pszFilename ); + else + fprintf( fp, "%2d %d %4s %s", + pasSharedFileList[i].nRefCount, + pasSharedFileList[i].bLarge, + pasSharedFileList[i].pszAccess, + pasSharedFileList[i].pszFilename ); + } +} + +/************************************************************************/ +/* CPLUnlinkTree() */ +/************************************************************************/ + +/** + * @return 0 on successful completion, -1 if function fails. + */ + +int CPLUnlinkTree( const char *pszPath ) + +{ +/* -------------------------------------------------------------------- */ +/* First, ensure there is such a file. */ +/* -------------------------------------------------------------------- */ + VSIStatBufL sStatBuf; + + if( VSIStatL( pszPath, &sStatBuf ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "It seems no file system object called '%s' exists.", + pszPath ); + + return -1; + } + +/* -------------------------------------------------------------------- */ +/* If it's a simple file, just delete it. */ +/* -------------------------------------------------------------------- */ + if( VSI_ISREG( sStatBuf.st_mode ) ) + { + if( VSIUnlink( pszPath ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to unlink %s.", + pszPath ); + + return -1; + } + else + return 0; + } + +/* -------------------------------------------------------------------- */ +/* If it is a directory recurse then unlink the directory. */ +/* -------------------------------------------------------------------- */ + else if( VSI_ISDIR( sStatBuf.st_mode ) ) + { + char **papszItems = CPLReadDir( pszPath ); + int i; + + for( i = 0; papszItems != NULL && papszItems[i] != NULL; i++ ) + { + char *pszSubPath; + int nErr; + + if( EQUAL(papszItems[i],".") || EQUAL(papszItems[i],"..") ) + continue; + + pszSubPath = CPLStrdup( + CPLFormFilename( pszPath, papszItems[i], NULL ) ); + + nErr = CPLUnlinkTree( pszSubPath ); + CPLFree( pszSubPath ); + + if( nErr != 0 ) + { + CSLDestroy( papszItems ); + return nErr; + } + } + + CSLDestroy( papszItems ); + + if( VSIRmdir( pszPath ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, "Failed to unlink %s.", + pszPath ); + + return -1; + } + else + return 0; + } + +/* -------------------------------------------------------------------- */ +/* otherwise report an error. */ +/* -------------------------------------------------------------------- */ + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Failed to unlink %s.\nUnrecognised filesystem object.", + pszPath ); + return 1000; + } +} + +/************************************************************************/ +/* CPLCopyFile() */ +/************************************************************************/ + +int CPLCopyFile( const char *pszNewPath, const char *pszOldPath ) + +{ + VSILFILE *fpOld, *fpNew; + GByte *pabyBuffer; + size_t nBufferSize; + size_t nBytesRead; + int nRet = 0; + +/* -------------------------------------------------------------------- */ +/* Open old and new file. */ +/* -------------------------------------------------------------------- */ + fpOld = VSIFOpenL( pszOldPath, "rb" ); + if( fpOld == NULL ) + return -1; + + fpNew = VSIFOpenL( pszNewPath, "wb" ); + if( fpNew == NULL ) + { + VSIFCloseL( fpOld ); + return -1; + } + +/* -------------------------------------------------------------------- */ +/* Prepare buffer. */ +/* -------------------------------------------------------------------- */ + nBufferSize = 1024 * 1024; + pabyBuffer = (GByte *) CPLMalloc(nBufferSize); + +/* -------------------------------------------------------------------- */ +/* Copy file over till we run out of stuff. */ +/* -------------------------------------------------------------------- */ + do { + nBytesRead = VSIFReadL( pabyBuffer, 1, nBufferSize, fpOld ); + if( long(nBytesRead) < 0 ) + nRet = -1; + + if( nRet == 0 + && VSIFWriteL( pabyBuffer, 1, nBytesRead, fpNew ) < nBytesRead ) + nRet = -1; + } while( nRet == 0 && nBytesRead == nBufferSize ); + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + VSIFCloseL( fpNew ); + VSIFCloseL( fpOld ); + + CPLFree( pabyBuffer ); + + return nRet; +} + +/************************************************************************/ +/* CPLCopyTree() */ +/************************************************************************/ + +int CPLCopyTree( const char *pszNewPath, const char *pszOldPath ) + +{ + VSIStatBufL sStatBuf; + + if( VSIStatL( pszOldPath, &sStatBuf ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "It seems no file system object called '%s' exists.", + pszOldPath ); + + return -1; + } + if( VSIStatL( pszNewPath, &sStatBuf ) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "It seems that a file system object called '%s' already exists.", + pszNewPath ); + + return -1; + } + + if( VSI_ISDIR( sStatBuf.st_mode ) ) + { + if( VSIMkdir( pszNewPath, 0755 ) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Cannot create directory '%s'.", + pszNewPath ); + + return -1; + } + + char **papszItems = CPLReadDir( pszOldPath ); + int i; + + for( i = 0; papszItems != NULL && papszItems[i] != NULL; i++ ) + { + char *pszNewSubPath; + char *pszOldSubPath; + int nErr; + + if( EQUAL(papszItems[i],".") || EQUAL(papszItems[i],"..") ) + continue; + + pszNewSubPath = CPLStrdup( + CPLFormFilename( pszNewPath, papszItems[i], NULL ) ); + pszOldSubPath = CPLStrdup( + CPLFormFilename( pszOldPath, papszItems[i], NULL ) ); + + nErr = CPLCopyTree( pszNewSubPath, pszOldSubPath ); + + CPLFree( pszNewSubPath ); + CPLFree( pszOldSubPath ); + + if( nErr != 0 ) + { + CSLDestroy( papszItems ); + return nErr; + } + } + CSLDestroy( papszItems ); + + return 0; + } + else if( VSI_ISREG( sStatBuf.st_mode ) ) + { + return CPLCopyFile( pszNewPath, pszOldPath ); + } + else + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unrecognised filesystem object : '%s'.", + pszOldPath ); + return -1; + } +} + +/************************************************************************/ +/* CPLMoveFile() */ +/************************************************************************/ + +int CPLMoveFile( const char *pszNewPath, const char *pszOldPath ) + +{ + if( VSIRename( pszOldPath, pszNewPath ) == 0 ) + return 0; + + int nRet = CPLCopyFile( pszNewPath, pszOldPath ); + + if( nRet == 0 ) + VSIUnlink( pszOldPath ); + + return nRet; +} + +/************************************************************************/ +/* ==================================================================== */ +/* CPLLocaleC */ +/* ==================================================================== */ +/************************************************************************/ + +#include + +/************************************************************************/ +/* CPLLocaleC() */ +/************************************************************************/ + +CPLLocaleC::CPLLocaleC() + +{ + if( CSLTestBoolean(CPLGetConfigOption("GDAL_DISABLE_CPLLOCALEC","NO")) ) + { + pszOldLocale = NULL; + } + else + { + pszOldLocale = CPLStrdup(CPLsetlocale(LC_NUMERIC,NULL)); + if( EQUAL(pszOldLocale,"C") + || EQUAL(pszOldLocale,"POSIX") + || CPLsetlocale(LC_NUMERIC,"C") == NULL ) + { + CPLFree( pszOldLocale ); + pszOldLocale = NULL; + } + } +} + +/************************************************************************/ +/* ~CPLLocaleC() */ +/************************************************************************/ + +CPLLocaleC::~CPLLocaleC() + +{ + if( pszOldLocale != NULL ) + { + CPLsetlocale( LC_NUMERIC, pszOldLocale ); + CPLFree( pszOldLocale ); + } +} + + +/************************************************************************/ +/* CPLsetlocale() */ +/************************************************************************/ + +/** + * Prevents parallel executions of setlocale(). + * + * Calling setlocale() concurrently from two or more threads is a + * potential data race. A mutex is used to provide a critical region so + * that only one thread at a time can be executing setlocale(). + * + * The return should not be freed, and copied quickly as it may be invalidated + * by a following next call to CPLsetlocale(). + * + * @param category See your compiler's documentation on setlocale. + * @param locale See your compiler's documentation on setlocale. + * + * @return See your compiler's documentation on setlocale. + */ +char* CPLsetlocale (int category, const char* locale) +{ + CPLMutexHolder oHolder(&hSetLocaleMutex); + char* pszRet = setlocale(category, locale); + if( pszRet == NULL ) + return pszRet; + return (char*)CPLSPrintf("%s", pszRet); /* to make it thread-locale storage */ +} + + +/************************************************************************/ +/* CPLCleanupSetlocaleMutex() */ +/************************************************************************/ + +void CPLCleanupSetlocaleMutex(void) +{ + if( hSetLocaleMutex != NULL ) + CPLDestroyMutex(hSetLocaleMutex); + hSetLocaleMutex = NULL; +} + +/************************************************************************/ +/* CPLCheckForFile() */ +/************************************************************************/ + +/** + * Check for file existance. + * + * The function checks if a named file exists in the filesystem, hopefully + * in an efficient fashion if a sibling file list is available. It exists + * primarily to do faster file checking for functions like GDAL open methods + * that get a list of files from the target directory. + * + * If the sibling file list exists (is not NULL) it is assumed to be a list + * of files in the same directory as the target file, and it will be checked + * (case insensitively) for a match. If a match is found, pszFilename is + * updated with the correct case and TRUE is returned. + * + * If papszSiblingFiles is NULL, a VSIStatL() is used to test for the files + * existance, and no case insensitive testing is done. + * + * @param pszFilename name of file to check for - filename case updated in some cases. + * @param papszSiblingFiles a list of files in the same directory as + * pszFilename if available, or NULL. This list should have no path components. + * + * @return TRUE if a match is found, or FALSE if not. + */ + +int CPLCheckForFile( char *pszFilename, char **papszSiblingFiles ) + +{ +/* -------------------------------------------------------------------- */ +/* Fallback case if we don't have a sibling file list. */ +/* -------------------------------------------------------------------- */ + if( papszSiblingFiles == NULL ) + { + VSIStatBufL sStatBuf; + + return VSIStatL( pszFilename, &sStatBuf ) == 0; + } + +/* -------------------------------------------------------------------- */ +/* We have sibling files, compare the non-path filename portion */ +/* of pszFilename too all entries. */ +/* -------------------------------------------------------------------- */ + CPLString osFileOnly = CPLGetFilename( pszFilename ); + int i; + + for( i = 0; papszSiblingFiles[i] != NULL; i++ ) + { + if( EQUAL(papszSiblingFiles[i],osFileOnly) ) + { + strcpy( pszFilename + strlen(pszFilename) - strlen(osFileOnly), + papszSiblingFiles[i] ); + return TRUE; + } + } + + return FALSE; +} + +/************************************************************************/ +/* Stub implementation of zip services if we don't have libz. */ +/************************************************************************/ + +#if !defined(HAVE_LIBZ) + +void *CPLCreateZip( const char *pszZipFilename, char **papszOptions ) + +{ + CPLError( CE_Failure, CPLE_NotSupported, + "This GDAL/OGR build does not include zlib and zip services." ); + return NULL; +} + +CPLErr CPLCreateFileInZip( void *hZip, const char *pszFilename, + char **papszOptions ) + +{ + return CE_Failure; +} + +CPLErr CPLWriteFileInZip( void *hZip, const void *pBuffer, int nBufferSize ) + +{ + return CE_Failure; +} + +CPLErr CPLCloseFileInZip( void *hZip ) + +{ + return CE_Failure; +} + +CPLErr CPLCloseZip( void *hZip ) + +{ + return CE_Failure; +} + +void* CPLZLibDeflate( const void* ptr, size_t nBytes, int nLevel, + void* outptr, size_t nOutAvailableBytes, + size_t* pnOutBytes ) +{ + if( pnOutBytes != NULL ) + *pnOutBytes = 0; + return NULL; +} + +void* CPLZLibInflate( const void* ptr, size_t nBytes, + void* outptr, size_t nOutAvailableBytes, + size_t* pnOutBytes ) +{ + if( pnOutBytes != NULL ) + *pnOutBytes = 0; + return NULL; +} + +#endif /* !defined(HAVE_LIBZ) */ diff --git a/bazaar/plugin/gdal/port/cpl_conv.h b/bazaar/plugin/gdal/port/cpl_conv.h new file mode 100644 index 000000000..45ca02155 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_conv.h @@ -0,0 +1,278 @@ +/****************************************************************************** + * $Id: cpl_conv.h 28601 2015-03-03 11:06:40Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Convenience functions declarations. + * This is intended to remain light weight. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_CONV_H_INCLUDED +#define CPL_CONV_H_INCLUDED + +#include "cpl_port.h" +#include "cpl_vsi.h" +#include "cpl_error.h" + +/** + * \file cpl_conv.h + * + * Various convenience functions for CPL. + * + */ + +/* -------------------------------------------------------------------- */ +/* Runtime check of various configuration items. */ +/* -------------------------------------------------------------------- */ +CPL_C_START + +void CPL_DLL CPLVerifyConfiguration(void); + +const char CPL_DLL * CPL_STDCALL +CPLGetConfigOption( const char *, const char * ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL CPL_STDCALL CPLSetConfigOption( const char *, const char * ); +void CPL_DLL CPL_STDCALL CPLSetThreadLocalConfigOption( const char *pszKey, + const char *pszValue ); +void CPL_DLL CPL_STDCALL CPLFreeConfig(void); + +/* -------------------------------------------------------------------- */ +/* Safe malloc() API. Thin cover over VSI functions with fatal */ +/* error reporting if memory allocation fails. */ +/* -------------------------------------------------------------------- */ +void CPL_DLL *CPLMalloc( size_t ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL *CPLCalloc( size_t, size_t ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL *CPLRealloc( void *, size_t ) CPL_WARN_UNUSED_RESULT; +char CPL_DLL *CPLStrdup( const char * ) CPL_WARN_UNUSED_RESULT; +char CPL_DLL *CPLStrlwr( char *); + +#define CPLFree VSIFree + +/* -------------------------------------------------------------------- */ +/* Read a line from a text file, and strip of CR/LF. */ +/* -------------------------------------------------------------------- */ +char CPL_DLL *CPLFGets( char *, int, FILE *); +const char CPL_DLL *CPLReadLine( FILE * ); +const char CPL_DLL *CPLReadLineL( VSILFILE * ); +const char CPL_DLL *CPLReadLine2L( VSILFILE * , int nMaxCols, char** papszOptions); + +/* -------------------------------------------------------------------- */ +/* Convert ASCII string to floationg point number */ +/* (THESE FUNCTIONS ARE NOT LOCALE AWARE!). */ +/* -------------------------------------------------------------------- */ +double CPL_DLL CPLAtof(const char *); +double CPL_DLL CPLAtofDelim(const char *, char); +double CPL_DLL CPLStrtod(const char *, char **); +double CPL_DLL CPLStrtodDelim(const char *, char **, char); +float CPL_DLL CPLStrtof(const char *, char **); +float CPL_DLL CPLStrtofDelim(const char *, char **, char); + +/* -------------------------------------------------------------------- */ +/* Convert number to string. This function is locale agnostic */ +/* (ie. it will support "," or "." regardless of current locale) */ +/* -------------------------------------------------------------------- */ +double CPL_DLL CPLAtofM(const char *); + +/* -------------------------------------------------------------------- */ +/* Read a numeric value from an ASCII character string. */ +/* -------------------------------------------------------------------- */ +char CPL_DLL *CPLScanString( const char *, int, int, int ); +double CPL_DLL CPLScanDouble( const char *, int ); +long CPL_DLL CPLScanLong( const char *, int ); +unsigned long CPL_DLL CPLScanULong( const char *, int ); +GUIntBig CPL_DLL CPLScanUIntBig( const char *, int ); +GIntBig CPL_DLL CPLAtoGIntBig( const char* pszString ); +GIntBig CPL_DLL CPLAtoGIntBigEx( const char* pszString, int bWarn, int *pbOverflow ); +void CPL_DLL *CPLScanPointer( const char *, int ); + +/* -------------------------------------------------------------------- */ +/* Print a value to an ASCII character string. */ +/* -------------------------------------------------------------------- */ +int CPL_DLL CPLPrintString( char *, const char *, int ); +int CPL_DLL CPLPrintStringFill( char *, const char *, int ); +int CPL_DLL CPLPrintInt32( char *, GInt32 , int ); +int CPL_DLL CPLPrintUIntBig( char *, GUIntBig , int ); +int CPL_DLL CPLPrintDouble( char *, const char *, double, const char * ); +int CPL_DLL CPLPrintTime( char *, int , const char *, const struct tm *, + const char * ); +int CPL_DLL CPLPrintPointer( char *, void *, int ); + +/* -------------------------------------------------------------------- */ +/* Fetch a function from DLL / so. */ +/* -------------------------------------------------------------------- */ + +void CPL_DLL *CPLGetSymbol( const char *, const char * ); + +/* -------------------------------------------------------------------- */ +/* Fetch executable path. */ +/* -------------------------------------------------------------------- */ +int CPL_DLL CPLGetExecPath( char *pszPathBuf, int nMaxLength ); + +/* -------------------------------------------------------------------- */ +/* Filename handling functions. */ +/* -------------------------------------------------------------------- */ +const char CPL_DLL *CPLGetPath( const char * ); +const char CPL_DLL *CPLGetDirname( const char * ); +const char CPL_DLL *CPLGetFilename( const char * ); +const char CPL_DLL *CPLGetBasename( const char * ); +const char CPL_DLL *CPLGetExtension( const char * ); +char CPL_DLL *CPLGetCurrentDir(void); +const char CPL_DLL *CPLFormFilename( const char *pszPath, + const char *pszBasename, + const char *pszExtension ); +const char CPL_DLL *CPLFormCIFilename( const char *pszPath, + const char *pszBasename, + const char *pszExtension ); +const char CPL_DLL *CPLResetExtension( const char *, const char * ); +const char CPL_DLL *CPLProjectRelativeFilename( const char *pszProjectDir, + const char *pszSecondaryFilename ); +int CPL_DLL CPLIsFilenameRelative( const char *pszFilename ); +const char CPL_DLL *CPLExtractRelativePath(const char *, const char *, int *); +const char CPL_DLL *CPLCleanTrailingSlash( const char * ); +char CPL_DLL **CPLCorrespondingPaths( const char *pszOldFilename, + const char *pszNewFilename, + char **papszFileList ); +int CPL_DLL CPLCheckForFile( char *pszFilename, char **papszSiblingList ); + +const char CPL_DLL *CPLGenerateTempFilename( const char *pszStem ); + +/* -------------------------------------------------------------------- */ +/* Find File Function */ +/* -------------------------------------------------------------------- */ +typedef const char *(*CPLFileFinder)(const char *, const char *); + +const char CPL_DLL *CPLFindFile(const char *pszClass, + const char *pszBasename); +const char CPL_DLL *CPLDefaultFindFile(const char *pszClass, + const char *pszBasename); +void CPL_DLL CPLPushFileFinder( CPLFileFinder pfnFinder ); +CPLFileFinder CPL_DLL CPLPopFileFinder(void); +void CPL_DLL CPLPushFinderLocation( const char * ); +void CPL_DLL CPLPopFinderLocation(void); +void CPL_DLL CPLFinderClean(void); + +/* -------------------------------------------------------------------- */ +/* Safe version of stat() that works properly on stuff like "C:". */ +/* -------------------------------------------------------------------- */ +int CPL_DLL CPLStat( const char *, VSIStatBuf * ); + +/* -------------------------------------------------------------------- */ +/* Reference counted file handle manager. Makes sharing file */ +/* handles more practical. */ +/* -------------------------------------------------------------------- */ +typedef struct { + FILE *fp; + int nRefCount; + int bLarge; + char *pszFilename; + char *pszAccess; +} CPLSharedFileInfo; + +FILE CPL_DLL *CPLOpenShared( const char *, const char *, int ); +void CPL_DLL CPLCloseShared( FILE * ); +CPLSharedFileInfo CPL_DLL *CPLGetSharedList( int * ); +void CPL_DLL CPLDumpSharedList( FILE * ); +void CPL_DLL CPLCleanupSharedFileMutex( void ); + +/* -------------------------------------------------------------------- */ +/* DMS to Dec to DMS conversion. */ +/* -------------------------------------------------------------------- */ +double CPL_DLL CPLDMSToDec( const char *is ); +const char CPL_DLL *CPLDecToDMS( double dfAngle, const char * pszAxis, + int nPrecision ); +double CPL_DLL CPLPackedDMSToDec( double ); +double CPL_DLL CPLDecToPackedDMS( double dfDec ); + +void CPL_DLL CPLStringToComplex( const char *pszString, + double *pdfReal, double *pdfImag ); + +/* -------------------------------------------------------------------- */ +/* Misc other functions. */ +/* -------------------------------------------------------------------- */ +int CPL_DLL CPLUnlinkTree( const char * ); +int CPL_DLL CPLCopyFile( const char *pszNewPath, const char *pszOldPath ); +int CPL_DLL CPLCopyTree( const char *pszNewPath, const char *pszOldPath ); +int CPL_DLL CPLMoveFile( const char *pszNewPath, const char *pszOldPath ); + +/* -------------------------------------------------------------------- */ +/* ZIP Creation. */ +/* -------------------------------------------------------------------- */ +#define CPL_ZIP_API_OFFERED +void CPL_DLL *CPLCreateZip( const char *pszZipFilename, char **papszOptions ); +CPLErr CPL_DLL CPLCreateFileInZip( void *hZip, const char *pszFilename, + char **papszOptions ); +CPLErr CPL_DLL CPLWriteFileInZip( void *hZip, const void *pBuffer, int nBufferSize ); +CPLErr CPL_DLL CPLCloseFileInZip( void *hZip ); +CPLErr CPL_DLL CPLCloseZip( void *hZip ); + +/* -------------------------------------------------------------------- */ +/* ZLib compression */ +/* -------------------------------------------------------------------- */ + +void CPL_DLL *CPLZLibDeflate( const void* ptr, size_t nBytes, int nLevel, + void* outptr, size_t nOutAvailableBytes, + size_t* pnOutBytes ); +void CPL_DLL *CPLZLibInflate( const void* ptr, size_t nBytes, + void* outptr, size_t nOutAvailableBytes, + size_t* pnOutBytes ); + +/* -------------------------------------------------------------------- */ +/* XML validation. */ +/* -------------------------------------------------------------------- */ +int CPL_DLL CPLValidateXML(const char* pszXMLFilename, + const char* pszXSDFilename, + char** papszOptions); + +/* -------------------------------------------------------------------- */ +/* Locale handling. Prevents parallel executions of setlocale(). */ +/* -------------------------------------------------------------------- */ +char* CPLsetlocale (int category, const char* locale); +void CPLCleanupSetlocaleMutex(void); + +CPL_C_END + +/* -------------------------------------------------------------------- */ +/* C++ object for temporariliy forcing a LC_NUMERIC locale to "C". */ +/* -------------------------------------------------------------------- */ + +#if defined(__cplusplus) && !defined(CPL_SUPRESS_CPLUSPLUS) + +class CPL_DLL CPLLocaleC +{ +public: + CPLLocaleC(); + ~CPLLocaleC(); + +private: + char *pszOldLocale; + + /* Make it non-copyable */ + CPLLocaleC(CPLLocaleC&); + CPLLocaleC& operator=(CPLLocaleC&); +}; + +#endif /* def __cplusplus */ + + +#endif /* ndef CPL_CONV_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_csv.cpp b/bazaar/plugin/gdal/port/cpl_csv.cpp new file mode 100644 index 000000000..29ee2cfd7 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_csv.cpp @@ -0,0 +1,1200 @@ +/****************************************************************************** + * $Id: cpl_csv.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: CSV (comma separated value) file access. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_csv.h" +#include "cpl_conv.h" +#include "cpl_multiproc.h" +#include "gdal_csv.h" + +CPL_CVSID("$Id: cpl_csv.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +/* ==================================================================== */ +/* The CSVTable is a persistant set of info about an open CSV */ +/* table. While it doesn't currently maintain a record index, */ +/* or in-memory copy of the table, it could be changed to do so */ +/* in the future. */ +/* ==================================================================== */ +typedef struct ctb { + FILE *fp; + + struct ctb *psNext; + + char *pszFilename; + + char **papszFieldNames; + + char **papszRecFields; + + int iLastLine; + + int bNonUniqueKey; + + /* Cache for whole file */ + int nLineCount; + char **papszLines; + int *panLineIndex; + char *pszRawData; +} CSVTable; + + +static void CSVDeaccessInternal( CSVTable **ppsCSVTableList, int bCanUseTLS, const char * pszFilename ); + +/************************************************************************/ +/* CSVFreeTLS() */ +/************************************************************************/ +static void CSVFreeTLS(void* pData) +{ + CSVDeaccessInternal( (CSVTable **)pData, FALSE, NULL ); + CPLFree(pData); +} + +/* It would likely be better to share this list between threads, but + that will require some rework. */ + +/************************************************************************/ +/* CSVAccess() */ +/* */ +/* This function will fetch a handle to the requested table. */ +/* If not found in the ``open table list'' the table will be */ +/* opened and added to the list. Eventually this function may */ +/* become public with an abstracted return type so that */ +/* applications can set options about the table. For now this */ +/* isn't done. */ +/************************************************************************/ + +static CSVTable *CSVAccess( const char * pszFilename ) + +{ + CSVTable *psTable; + FILE *fp; + +/* -------------------------------------------------------------------- */ +/* Fetch the table, and allocate the thread-local pointer to it */ +/* if there isn't already one. */ +/* -------------------------------------------------------------------- */ + CSVTable **ppsCSVTableList; + + ppsCSVTableList = (CSVTable **) CPLGetTLS( CTLS_CSVTABLEPTR ); + if( ppsCSVTableList == NULL ) + { + ppsCSVTableList = (CSVTable **) CPLCalloc(1,sizeof(CSVTable*)); + CPLSetTLSWithFreeFunc( CTLS_CSVTABLEPTR, ppsCSVTableList, CSVFreeTLS ); + } + +/* -------------------------------------------------------------------- */ +/* Is the table already in the list. */ +/* -------------------------------------------------------------------- */ + for( psTable = *ppsCSVTableList; + psTable != NULL; + psTable = psTable->psNext ) + { + if( EQUAL(psTable->pszFilename,pszFilename) ) + { + /* + * Eventually we should consider promoting to the front of + * the list to accelerate frequently accessed tables. + */ + + return( psTable ); + } + } + +/* -------------------------------------------------------------------- */ +/* If not, try to open it. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpen( pszFilename, "rb" ); + if( fp == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Create an information structure about this table, and add to */ +/* the front of the list. */ +/* -------------------------------------------------------------------- */ + psTable = (CSVTable *) CPLCalloc(sizeof(CSVTable),1); + + psTable->fp = fp; + psTable->pszFilename = CPLStrdup( pszFilename ); + psTable->bNonUniqueKey = FALSE; /* as far as we know now */ + psTable->psNext = *ppsCSVTableList; + + *ppsCSVTableList = psTable; + +/* -------------------------------------------------------------------- */ +/* Read the table header record containing the field names. */ +/* -------------------------------------------------------------------- */ + psTable->papszFieldNames = CSVReadParseLine( fp ); + + return( psTable ); +} + +/************************************************************************/ +/* CSVDeaccess() */ +/************************************************************************/ + +static void CSVDeaccessInternal( CSVTable **ppsCSVTableList, int bCanUseTLS, const char * pszFilename ) + +{ + CSVTable *psLast, *psTable; + + if( ppsCSVTableList == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* A NULL means deaccess all tables. */ +/* -------------------------------------------------------------------- */ + if( pszFilename == NULL ) + { + while( *ppsCSVTableList != NULL ) + CSVDeaccessInternal( ppsCSVTableList, bCanUseTLS, (*ppsCSVTableList)->pszFilename ); + + return; + } + +/* -------------------------------------------------------------------- */ +/* Find this table. */ +/* -------------------------------------------------------------------- */ + psLast = NULL; + for( psTable = *ppsCSVTableList; + psTable != NULL && !EQUAL(psTable->pszFilename,pszFilename); + psTable = psTable->psNext ) + { + psLast = psTable; + } + + if( psTable == NULL ) + { + if (bCanUseTLS) + CPLDebug( "CPL_CSV", "CPLDeaccess( %s ) - no match.", pszFilename ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Remove the link from the list. */ +/* -------------------------------------------------------------------- */ + if( psLast != NULL ) + psLast->psNext = psTable->psNext; + else + *ppsCSVTableList = psTable->psNext; + +/* -------------------------------------------------------------------- */ +/* Free the table. */ +/* -------------------------------------------------------------------- */ + if( psTable->fp != NULL ) + VSIFClose( psTable->fp ); + + CSLDestroy( psTable->papszFieldNames ); + CSLDestroy( psTable->papszRecFields ); + CPLFree( psTable->pszFilename ); + CPLFree( psTable->panLineIndex ); + CPLFree( psTable->pszRawData ); + CPLFree( psTable->papszLines ); + + CPLFree( psTable ); + + if (bCanUseTLS) + CPLReadLine( NULL ); +} + +void CSVDeaccess( const char * pszFilename ) +{ + CSVTable **ppsCSVTableList; +/* -------------------------------------------------------------------- */ +/* Fetch the table, and allocate the thread-local pointer to it */ +/* if there isn't already one. */ +/* -------------------------------------------------------------------- */ + ppsCSVTableList = (CSVTable **) CPLGetTLS( CTLS_CSVTABLEPTR ); + + CSVDeaccessInternal(ppsCSVTableList, TRUE, pszFilename); +} + +/************************************************************************/ +/* CSVSplitLine() */ +/* */ +/* Tokenize a CSV line into fields in the form of a string */ +/* list. This is used instead of the CPLTokenizeString() */ +/* because it provides correct CSV escaping and quoting */ +/* semantics. */ +/************************************************************************/ + +static char **CSVSplitLine( const char *pszString, char chDelimiter ) + +{ + char **papszRetList = NULL; + char *pszToken; + int nTokenMax, nTokenLen; + + pszToken = (char *) CPLCalloc(10,1); + nTokenMax = 10; + + while( pszString != NULL && *pszString != '\0' ) + { + int bInString = FALSE; + + nTokenLen = 0; + + /* Try to find the next delimeter, marking end of token */ + for( ; *pszString != '\0'; pszString++ ) + { + + /* End if this is a delimeter skip it and break. */ + if( !bInString && *pszString == chDelimiter ) + { + pszString++; + break; + } + + if( *pszString == '"' ) + { + if( !bInString || pszString[1] != '"' ) + { + bInString = !bInString; + continue; + } + else /* doubled quotes in string resolve to one quote */ + { + pszString++; + } + } + + if( nTokenLen >= nTokenMax-2 ) + { + nTokenMax = nTokenMax * 2 + 10; + pszToken = (char *) CPLRealloc( pszToken, nTokenMax ); + } + + pszToken[nTokenLen] = *pszString; + nTokenLen++; + } + + pszToken[nTokenLen] = '\0'; + papszRetList = CSLAddString( papszRetList, pszToken ); + + /* If the last token is an empty token, then we have to catch + * it now, otherwise we won't reenter the loop and it will be lost. + */ + if ( *pszString == '\0' && *(pszString-1) == chDelimiter ) + { + papszRetList = CSLAddString( papszRetList, "" ); + } + } + + if( papszRetList == NULL ) + papszRetList = (char **) CPLCalloc(sizeof(char *),1); + + CPLFree( pszToken ); + + return papszRetList; +} + +/************************************************************************/ +/* CSVFindNextLine() */ +/* */ +/* Find the start of the next line, while at the same time zero */ +/* terminating this line. Take into account that there may be */ +/* newline indicators within quoted strings, and that quotes */ +/* can be escaped with a backslash. */ +/************************************************************************/ + +static char *CSVFindNextLine( char *pszThisLine ) + +{ + int nQuoteCount = 0, i; + + for( i = 0; pszThisLine[i] != '\0'; i++ ) + { + if( pszThisLine[i] == '\"' + && (i == 0 || pszThisLine[i-1] != '\\') ) + nQuoteCount++; + + if( (pszThisLine[i] == 10 || pszThisLine[i] == 13) + && (nQuoteCount % 2) == 0 ) + break; + } + + while( pszThisLine[i] == 10 || pszThisLine[i] == 13 ) + pszThisLine[i++] = '\0'; + + if( pszThisLine[i] == '\0' ) + return NULL; + else + return pszThisLine + i; +} + +/************************************************************************/ +/* CSVIngest() */ +/* */ +/* Load entire file into memory and setup index if possible. */ +/************************************************************************/ + +static void CSVIngest( const char *pszFilename ) + +{ + CSVTable *psTable = CSVAccess( pszFilename ); + int nFileLen, i, nMaxLineCount, iLine = 0; + char *pszThisLine; + + if( psTable->pszRawData != NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Ingest whole file. */ +/* -------------------------------------------------------------------- */ + VSIFSeek( psTable->fp, 0, SEEK_END ); + nFileLen = VSIFTell( psTable->fp ); + VSIRewind( psTable->fp ); + + psTable->pszRawData = (char *) CPLMalloc(nFileLen+1); + if( (int) VSIFRead( psTable->pszRawData, 1, nFileLen, psTable->fp ) + != nFileLen ) + { + CPLFree( psTable->pszRawData ); + psTable->pszRawData = NULL; + + CPLError( CE_Failure, CPLE_FileIO, "Read of file %s failed.", + psTable->pszFilename ); + return; + } + + psTable->pszRawData[nFileLen] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Get count of newlines so we can allocate line array. */ +/* -------------------------------------------------------------------- */ + nMaxLineCount = 0; + for( i = 0; i < nFileLen; i++ ) + { + if( psTable->pszRawData[i] == 10 ) + nMaxLineCount++; + } + + psTable->papszLines = (char **) CPLCalloc(sizeof(char*),nMaxLineCount); + +/* -------------------------------------------------------------------- */ +/* Build a list of record pointers into the raw data buffer */ +/* based on line terminators. Zero terminate the line */ +/* strings. */ +/* -------------------------------------------------------------------- */ + /* skip header line */ + pszThisLine = CSVFindNextLine( psTable->pszRawData ); + + while( pszThisLine != NULL && iLine < nMaxLineCount ) + { + psTable->papszLines[iLine++] = pszThisLine; + pszThisLine = CSVFindNextLine( pszThisLine ); + } + + psTable->nLineCount = iLine; + +/* -------------------------------------------------------------------- */ +/* Allocate and populate index array. Ensure they are in */ +/* ascending order so that binary searches can be done on the */ +/* array. */ +/* -------------------------------------------------------------------- */ + psTable->panLineIndex = (int *) CPLMalloc(sizeof(int)*psTable->nLineCount); + for( i = 0; i < psTable->nLineCount; i++ ) + { + psTable->panLineIndex[i] = atoi(psTable->papszLines[i]); + + if( i > 0 && psTable->panLineIndex[i] < psTable->panLineIndex[i-1] ) + { + CPLFree( psTable->panLineIndex ); + psTable->panLineIndex = NULL; + break; + } + } + + psTable->iLastLine = -1; + +/* -------------------------------------------------------------------- */ +/* We should never need the file handle against, so close it. */ +/* -------------------------------------------------------------------- */ + VSIFClose( psTable->fp ); + psTable->fp = NULL; +} + +/************************************************************************/ +/* CSVDetectSeperator() */ +/************************************************************************/ + +/** Detect which field separator is used. + * + * Currently, it can detect comma, semicolon, space or tabulation. In case of + * ambiguity or no separator found, comma will be considered as the separator. + * + * @return ',', ';', ' ' or '\t' + */ +char CSVDetectSeperator (const char* pszLine) +{ + int bInString = FALSE; + char chDelimiter = '\0'; + int nCountSpace = 0; + + for( ; *pszLine != '\0'; pszLine++ ) + { + if( !bInString && (*pszLine == ',' || *pszLine == ';' || *pszLine == '\t')) + { + if (chDelimiter == '\0') + chDelimiter = *pszLine; + else if (chDelimiter != *pszLine) + { + /* The separator is not consistent on the line. */ + CPLDebug("CSV", "Inconsistent separator. '%c' and '%c' found. Using ',' as default", + chDelimiter, *pszLine); + chDelimiter = ','; + break; + } + } + else if( !bInString && *pszLine == ' ' ) + nCountSpace ++; + else if( *pszLine == '"' ) + { + if( !bInString || pszLine[1] != '"' ) + { + bInString = !bInString; + continue; + } + else /* doubled quotes in string resolve to one quote */ + { + pszLine++; + } + } + } + + if (chDelimiter == '\0') + { + if( nCountSpace > 0 ) + chDelimiter = ' '; + else + chDelimiter = ','; + } + + return chDelimiter; +} + +/************************************************************************/ +/* CSVReadParseLine() */ +/* */ +/* Read one line, and return split into fields. The return */ +/* result is a stringlist, in the sense of the CSL functions. */ +/************************************************************************/ + +char **CSVReadParseLine( FILE * fp ) +{ + return CSVReadParseLine2(fp, ','); +} + +char **CSVReadParseLine2( FILE * fp, char chDelimiter ) + +{ + const char *pszLine; + char *pszWorkLine; + char **papszReturn; + + CPLAssert( fp != NULL ); + if( fp == NULL ) + return( NULL ); + + pszLine = CPLReadLine( fp ); + if( pszLine == NULL ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* If there are no quotes, then this is the simple case. */ +/* Parse, and return tokens. */ +/* -------------------------------------------------------------------- */ + if( strchr(pszLine,'\"') == NULL ) + return CSVSplitLine( pszLine, chDelimiter ); + +/* -------------------------------------------------------------------- */ +/* We must now count the quotes in our working string, and as */ +/* long as it is odd, keep adding new lines. */ +/* -------------------------------------------------------------------- */ + pszWorkLine = CPLStrdup( pszLine ); + + int i = 0, nCount = 0; + int nWorkLineLength = strlen(pszWorkLine); + + while( TRUE ) + { + for( ; pszWorkLine[i] != '\0'; i++ ) + { + if( pszWorkLine[i] == '\"' + && (i == 0 || pszWorkLine[i-1] != '\\') ) + nCount++; + } + + if( nCount % 2 == 0 ) + break; + + pszLine = CPLReadLine( fp ); + if( pszLine == NULL ) + break; + + int nLineLen = strlen(pszLine); + + char* pszWorkLineTmp = (char *) + VSIRealloc(pszWorkLine, + nWorkLineLength + nLineLen + 2); + if (pszWorkLineTmp == NULL) + break; + pszWorkLine = pszWorkLineTmp; + strcat( pszWorkLine + nWorkLineLength, "\n" ); // This gets lost in CPLReadLine(). + strcat( pszWorkLine + nWorkLineLength, pszLine ); + + nWorkLineLength += nLineLen + 1; + } + + papszReturn = CSVSplitLine( pszWorkLine, chDelimiter ); + + CPLFree( pszWorkLine ); + + return papszReturn; +} + +/************************************************************************/ +/* CSVCompare() */ +/* */ +/* Compare a field to a search value using a particular */ +/* criteria. */ +/************************************************************************/ + +static int CSVCompare( const char * pszFieldValue, const char * pszTarget, + CSVCompareCriteria eCriteria ) + +{ + if( eCriteria == CC_ExactString ) + { + return( strcmp( pszFieldValue, pszTarget ) == 0 ); + } + else if( eCriteria == CC_ApproxString ) + { + return( EQUAL( pszFieldValue, pszTarget ) ); + } + else if( eCriteria == CC_Integer ) + { + return( atoi(pszFieldValue) == atoi(pszTarget) ); + } + + return FALSE; +} + +/************************************************************************/ +/* CSVScanLines() */ +/* */ +/* Read the file scanline for lines where the key field equals */ +/* the indicated value with the suggested comparison criteria. */ +/* Return the first matching line split into fields. */ +/************************************************************************/ + +char **CSVScanLines( FILE *fp, int iKeyField, const char * pszValue, + CSVCompareCriteria eCriteria ) + +{ + char **papszFields = NULL; + int bSelected = FALSE, nTestValue; + + CPLAssert( pszValue != NULL ); + CPLAssert( iKeyField >= 0 ); + CPLAssert( fp != NULL ); + + nTestValue = atoi(pszValue); + + while( !bSelected ) { + papszFields = CSVReadParseLine( fp ); + if( papszFields == NULL ) + return( NULL ); + + if( CSLCount( papszFields ) < iKeyField+1 ) + { + /* not selected */ + } + else if( eCriteria == CC_Integer + && atoi(papszFields[iKeyField]) == nTestValue ) + { + bSelected = TRUE; + } + else + { + bSelected = CSVCompare( papszFields[iKeyField], pszValue, + eCriteria ); + } + + if( !bSelected ) + { + CSLDestroy( papszFields ); + papszFields = NULL; + } + } + + return( papszFields ); +} + +/************************************************************************/ +/* CSVScanLinesIndexed() */ +/* */ +/* Read the file scanline for lines where the key field equals */ +/* the indicated value with the suggested comparison criteria. */ +/* Return the first matching line split into fields. */ +/************************************************************************/ + +static char ** +CSVScanLinesIndexed( CSVTable *psTable, int nKeyValue ) + +{ + int iTop, iBottom, iMiddle, iResult = -1; + + CPLAssert( psTable->panLineIndex != NULL ); + +/* -------------------------------------------------------------------- */ +/* Find target record with binary search. */ +/* -------------------------------------------------------------------- */ + iTop = psTable->nLineCount-1; + iBottom = 0; + + while( iTop >= iBottom ) + { + iMiddle = (iTop + iBottom) / 2; + if( psTable->panLineIndex[iMiddle] > nKeyValue ) + iTop = iMiddle - 1; + else if( psTable->panLineIndex[iMiddle] < nKeyValue ) + iBottom = iMiddle + 1; + else + { + iResult = iMiddle; + // if a key is not unique, select the first instance of it. + while( iResult > 0 + && psTable->panLineIndex[iResult-1] == nKeyValue ) + { + psTable->bNonUniqueKey = TRUE; + iResult--; + } + break; + } + } + + if( iResult == -1 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Parse target line, and update iLastLine indicator. */ +/* -------------------------------------------------------------------- */ + psTable->iLastLine = iResult; + + return CSVSplitLine( psTable->papszLines[iResult], ',' ); +} + +/************************************************************************/ +/* CSVScanLinesIngested() */ +/* */ +/* Read the file scanline for lines where the key field equals */ +/* the indicated value with the suggested comparison criteria. */ +/* Return the first matching line split into fields. */ +/************************************************************************/ + +static char ** +CSVScanLinesIngested( CSVTable *psTable, int iKeyField, const char * pszValue, + CSVCompareCriteria eCriteria ) + +{ + char **papszFields = NULL; + int bSelected = FALSE, nTestValue; + + CPLAssert( pszValue != NULL ); + CPLAssert( iKeyField >= 0 ); + + nTestValue = atoi(pszValue); + +/* -------------------------------------------------------------------- */ +/* Short cut for indexed files. */ +/* -------------------------------------------------------------------- */ + if( iKeyField == 0 && eCriteria == CC_Integer + && psTable->panLineIndex != NULL ) + return CSVScanLinesIndexed( psTable, nTestValue ); + +/* -------------------------------------------------------------------- */ +/* Scan from in-core lines. */ +/* -------------------------------------------------------------------- */ + while( !bSelected && psTable->iLastLine+1 < psTable->nLineCount ) { + psTable->iLastLine++; + papszFields = CSVSplitLine( psTable->papszLines[psTable->iLastLine], ',' ); + + if( CSLCount( papszFields ) < iKeyField+1 ) + { + /* not selected */ + } + else if( eCriteria == CC_Integer + && atoi(papszFields[iKeyField]) == nTestValue ) + { + bSelected = TRUE; + } + else + { + bSelected = CSVCompare( papszFields[iKeyField], pszValue, + eCriteria ); + } + + if( !bSelected ) + { + CSLDestroy( papszFields ); + papszFields = NULL; + } + } + + return( papszFields ); +} + +/************************************************************************/ +/* CSVGetNextLine() */ +/* */ +/* Fetch the next line of a CSV file based on a passed in */ +/* filename. Returns NULL at end of file, or if file is not */ +/* really established. */ +/************************************************************************/ + +char **CSVGetNextLine( const char *pszFilename ) + +{ + CSVTable *psTable; + +/* -------------------------------------------------------------------- */ +/* Get access to the table. */ +/* -------------------------------------------------------------------- */ + CPLAssert( pszFilename != NULL ); + + psTable = CSVAccess( pszFilename ); + if( psTable == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* If we use CSVGetNextLine() we can pretty much assume we have */ +/* a non-unique key. */ +/* -------------------------------------------------------------------- */ + psTable->bNonUniqueKey = TRUE; + +/* -------------------------------------------------------------------- */ +/* Do we have a next line available? This only works for */ +/* ingested tables I believe. */ +/* -------------------------------------------------------------------- */ + if( psTable->iLastLine+1 >= psTable->nLineCount ) + return NULL; + + psTable->iLastLine++; + CSLDestroy( psTable->papszRecFields ); + psTable->papszRecFields = + CSVSplitLine( psTable->papszLines[psTable->iLastLine], ',' ); + + return psTable->papszRecFields; +} + +/************************************************************************/ +/* CSVScanFile() */ +/* */ +/* Scan a whole file using criteria similar to above, but also */ +/* taking care of file opening and closing. */ +/************************************************************************/ + +char **CSVScanFile( const char * pszFilename, int iKeyField, + const char * pszValue, CSVCompareCriteria eCriteria ) + +{ + CSVTable *psTable; + +/* -------------------------------------------------------------------- */ +/* Get access to the table. */ +/* -------------------------------------------------------------------- */ + CPLAssert( pszFilename != NULL ); + + if( iKeyField < 0 ) + return NULL; + + psTable = CSVAccess( pszFilename ); + if( psTable == NULL ) + return NULL; + + CSVIngest( pszFilename ); + +/* -------------------------------------------------------------------- */ +/* Does the current record match the criteria? If so, just */ +/* return it again. */ +/* -------------------------------------------------------------------- */ + if( iKeyField >= 0 + && iKeyField < CSLCount(psTable->papszRecFields) + && CSVCompare(pszValue,psTable->papszRecFields[iKeyField],eCriteria) + && !psTable->bNonUniqueKey ) + { + return psTable->papszRecFields; + } + +/* -------------------------------------------------------------------- */ +/* Scan the file from the beginning, replacing the ``current */ +/* record'' in our structure with the one that is found. */ +/* -------------------------------------------------------------------- */ + psTable->iLastLine = -1; + CSLDestroy( psTable->papszRecFields ); + + if( psTable->pszRawData != NULL ) + psTable->papszRecFields = + CSVScanLinesIngested( psTable, iKeyField, pszValue, eCriteria ); + else + { + VSIRewind( psTable->fp ); + CPLReadLine( psTable->fp ); /* throw away the header line */ + + psTable->papszRecFields = + CSVScanLines( psTable->fp, iKeyField, pszValue, eCriteria ); + } + + return( psTable->papszRecFields ); +} + +/************************************************************************/ +/* CPLGetFieldId() */ +/* */ +/* Read the first record of a CSV file (rewinding to be sure), */ +/* and find the field with the indicated name. Returns -1 if */ +/* it fails to find the field name. Comparison is case */ +/* insensitive, but otherwise exact. After this function has */ +/* been called the file pointer will be positioned just after */ +/* the first record. */ +/************************************************************************/ + +int CSVGetFieldId( FILE * fp, const char * pszFieldName ) + +{ + char **papszFields; + int i; + + CPLAssert( fp != NULL && pszFieldName != NULL ); + + VSIRewind( fp ); + + papszFields = CSVReadParseLine( fp ); + for( i = 0; papszFields != NULL && papszFields[i] != NULL; i++ ) + { + if( EQUAL(papszFields[i],pszFieldName) ) + { + CSLDestroy( papszFields ); + return i; + } + } + + CSLDestroy( papszFields ); + + return -1; +} + +/************************************************************************/ +/* CSVGetFileFieldId() */ +/* */ +/* Same as CPLGetFieldId(), except that we get the file based */ +/* on filename, rather than having an existing handle. */ +/************************************************************************/ + +int CSVGetFileFieldId( const char * pszFilename, const char * pszFieldName ) + +{ + CSVTable *psTable; + int i; + +/* -------------------------------------------------------------------- */ +/* Get access to the table. */ +/* -------------------------------------------------------------------- */ + CPLAssert( pszFilename != NULL ); + + psTable = CSVAccess( pszFilename ); + if( psTable == NULL ) + return -1; + +/* -------------------------------------------------------------------- */ +/* Find the requested field. */ +/* -------------------------------------------------------------------- */ + for( i = 0; + psTable->papszFieldNames != NULL + && psTable->papszFieldNames[i] != NULL; + i++ ) + { + if( EQUAL(psTable->papszFieldNames[i],pszFieldName) ) + { + return i; + } + } + + return -1; +} + + +/************************************************************************/ +/* CSVScanFileByName() */ +/* */ +/* Same as CSVScanFile(), but using a field name instead of a */ +/* field number. */ +/************************************************************************/ + +char **CSVScanFileByName( const char * pszFilename, + const char * pszKeyFieldName, + const char * pszValue, CSVCompareCriteria eCriteria ) + +{ + int iKeyField; + + iKeyField = CSVGetFileFieldId( pszFilename, pszKeyFieldName ); + if( iKeyField == -1 ) + return NULL; + + return( CSVScanFile( pszFilename, iKeyField, pszValue, eCriteria ) ); +} + +/************************************************************************/ +/* CSVGetField() */ +/* */ +/* The all-in-one function to fetch a particular field value */ +/* from a CSV file. Note this function will return an empty */ +/* string, rather than NULL if it fails to find the desired */ +/* value for some reason. The caller can't establish that the */ +/* fetch failed. */ +/************************************************************************/ + +const char *CSVGetField( const char * pszFilename, + const char * pszKeyFieldName, + const char * pszKeyFieldValue, + CSVCompareCriteria eCriteria, + const char * pszTargetField ) + +{ + CSVTable *psTable; + char **papszRecord; + int iTargetField; + +/* -------------------------------------------------------------------- */ +/* Find the table. */ +/* -------------------------------------------------------------------- */ + psTable = CSVAccess( pszFilename ); + if( psTable == NULL ) + return ""; + +/* -------------------------------------------------------------------- */ +/* Find the correct record. */ +/* -------------------------------------------------------------------- */ + papszRecord = CSVScanFileByName( pszFilename, pszKeyFieldName, + pszKeyFieldValue, eCriteria ); + + if( papszRecord == NULL ) + return ""; + +/* -------------------------------------------------------------------- */ +/* Figure out which field we want out of this. */ +/* -------------------------------------------------------------------- */ + iTargetField = CSVGetFileFieldId( pszFilename, pszTargetField ); + if( iTargetField < 0 ) + return ""; + + if( iTargetField >= CSLCount( papszRecord ) ) + return ""; + + return( papszRecord[iTargetField] ); +} + +/************************************************************************/ +/* GDALDefaultCSVFilename() */ +/************************************************************************/ + +typedef struct +{ + char szPath[512]; + int bCSVFinderInitialized; +} DefaultCSVFileNameTLS; + + +const char * GDALDefaultCSVFilename( const char *pszBasename ) + +{ +/* -------------------------------------------------------------------- */ +/* Do we already have this file accessed? If so, just return */ +/* the existing path without any further probing. */ +/* -------------------------------------------------------------------- */ + CSVTable **ppsCSVTableList; + + ppsCSVTableList = (CSVTable **) CPLGetTLS( CTLS_CSVTABLEPTR ); + if( ppsCSVTableList != NULL ) + { + CSVTable *psTable; + int nBasenameLen = strlen(pszBasename); + + for( psTable = *ppsCSVTableList; + psTable != NULL; + psTable = psTable->psNext ) + { + int nFullLen = strlen(psTable->pszFilename); + + if( nFullLen > nBasenameLen + && strcmp(psTable->pszFilename+nFullLen-nBasenameLen, + pszBasename) == 0 + && strchr("/\\",psTable->pszFilename[+nFullLen-nBasenameLen-1]) + != NULL ) + { + return psTable->pszFilename; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise we need to look harder for it. */ +/* -------------------------------------------------------------------- */ + DefaultCSVFileNameTLS* pTLSData = + (DefaultCSVFileNameTLS *) CPLGetTLS( CTLS_CSVDEFAULTFILENAME ); + if (pTLSData == NULL) + { + pTLSData = (DefaultCSVFileNameTLS*) CPLCalloc(1, sizeof(DefaultCSVFileNameTLS)); + CPLSetTLS( CTLS_CSVDEFAULTFILENAME, pTLSData, TRUE ); + } + + FILE *fp = NULL; + const char *pszResult; + + pszResult = CPLFindFile( "epsg_csv", pszBasename ); + + if( pszResult != NULL ) + return pszResult; + + if( !pTLSData->bCSVFinderInitialized ) + { + pTLSData->bCSVFinderInitialized = TRUE; + + if( CPLGetConfigOption("GEOTIFF_CSV",NULL) != NULL ) + CPLPushFinderLocation( CPLGetConfigOption("GEOTIFF_CSV",NULL)); + + if( CPLGetConfigOption("GDAL_DATA",NULL) != NULL ) + CPLPushFinderLocation( CPLGetConfigOption("GDAL_DATA",NULL) ); + + pszResult = CPLFindFile( "epsg_csv", pszBasename ); + + if( pszResult != NULL ) + return pszResult; + } + +#ifdef GDAL_PREFIX + #ifdef MACOSX_FRAMEWORK + strcpy( pTLSData->szPath, GDAL_PREFIX "/Resources/epsg_csv/" ); + CPLStrlcat( pTLSData->szPath, pszBasename, sizeof(pTLSData->szPath) ); + #else + strcpy( pTLSData->szPath, GDAL_PREFIX "/share/epsg_csv/" ); + CPLStrlcat( pTLSData->szPath, pszBasename, sizeof(pTLSData->szPath) ); + #endif +#else + strcpy( pTLSData->szPath, "/usr/local/share/epsg_csv/" ); + CPLStrlcat( pTLSData->szPath, pszBasename, sizeof(pTLSData->szPath) ); +#endif + if( (fp = fopen( pTLSData->szPath, "rt" )) == NULL ) + CPLStrlcpy( pTLSData->szPath, pszBasename, sizeof(pTLSData->szPath) ); + + if( fp != NULL ) + fclose( fp ); + + return( pTLSData->szPath ); +} + +/************************************************************************/ +/* CSVFilename() */ +/* */ +/* Return the full path to a particular CSV file. This will */ +/* eventually be something the application can override. */ +/************************************************************************/ + +CPL_C_START +static const char *(*pfnCSVFilenameHook)(const char *) = NULL; +CPL_C_END + +const char * CSVFilename( const char *pszBasename ) + +{ + if( pfnCSVFilenameHook == NULL ) + return GDALDefaultCSVFilename( pszBasename ); + else + return( pfnCSVFilenameHook( pszBasename ) ); +} + +/************************************************************************/ +/* SetCSVFilenameHook() */ +/* */ +/* Applications can use this to set a function that will */ +/* massage CSV filenames. */ +/************************************************************************/ + +/** + * Override CSV file search method. + * + * @param pfnNewHook The pointer to a function which will return the + * full path for a given filename. + * + +This function allows an application to override how the GTIFGetDefn() and related function find the CSV (Comma Separated +Value) values required. The pfnHook argument should be a pointer to a function that will take in a CSV filename and return a +full path to the file. The returned string should be to an internal static buffer so that the caller doesn't have to free the result. + +Example:
    + +The listgeo utility uses the following override function if the user +specified a CSV file directory with the -t commandline switch (argument +put into CSVDirName).

    + +

    +
    +    ...
    +
    +
    +    SetCSVFilenameHook( CSVFileOverride );
    +
    +    ...
    +
    +
    +static const char *CSVFileOverride( const char * pszInput )
    +
    +{
    +    static char         szPath[1024];
    +
    +#ifdef WIN32
    +    sprintf( szPath, "%s\\%s", CSVDirName, pszInput );
    +#else    
    +    sprintf( szPath, "%s/%s", CSVDirName, pszInput );
    +#endif    
    +
    +    return( szPath );
    +}
    +
    + +*/ + +CPL_C_START +void SetCSVFilenameHook( const char *(*pfnNewHook)( const char * ) ) + +{ + pfnCSVFilenameHook = pfnNewHook; +} +CPL_C_END diff --git a/bazaar/plugin/gdal/port/cpl_csv.h b/bazaar/plugin/gdal/port/cpl_csv.h new file mode 100644 index 000000000..24e41ad79 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_csv.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * $Id: cpl_csv.h 16759 2009-04-09 21:32:43Z rouault $ + * + * Project: Common Portability Library + * Purpose: Functions for reading and scaning CSV (comma separated, + * variable length text files holding tables) files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_CSV_H_INCLUDED +#define CPL_CSV_H_INCLUDED + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_vsi.h" + +CPL_C_START + +typedef enum { + CC_ExactString, + CC_ApproxString, + CC_Integer +} CSVCompareCriteria; + +const char CPL_DLL *CSVFilename( const char * ); + +char CPL_DLL CSVDetectSeperator( const char *pszLine ); + +char CPL_DLL **CSVReadParseLine( FILE *fp); +char CPL_DLL **CSVReadParseLine2( FILE *fp, char chDelimiter ); +char CPL_DLL **CSVScanLines( FILE *, int, const char *, CSVCompareCriteria ); +char CPL_DLL **CSVScanFile( const char *, int, const char *, + CSVCompareCriteria ); +char CPL_DLL **CSVScanFileByName( const char *, const char *, const char *, + CSVCompareCriteria ); +char CPL_DLL **CSVGetNextLine( const char * ); +int CPL_DLL CSVGetFieldId( FILE *, const char * ); +int CPL_DLL CSVGetFileFieldId( const char *, const char * ); + +void CPL_DLL CSVDeaccess( const char * ); + +const char CPL_DLL *CSVGetField( const char *, const char *, const char *, + CSVCompareCriteria, const char * ); + +void CPL_DLL SetCSVFilenameHook( const char *(*)(const char *) ); + +CPL_C_END + +#endif /* ndef CPL_CSV_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/port/cpl_error.cpp b/bazaar/plugin/gdal/port/cpl_error.cpp new file mode 100644 index 000000000..32dc39671 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_error.cpp @@ -0,0 +1,1001 @@ +/********************************************************************** + * $Id: cpl_error.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Name: cpl_error.cpp + * Project: CPL - Common Portability Library + * Purpose: Error handling functions. + * Author: Daniel Morissette, danmo@videotron.ca + * + ********************************************************************** + * Copyright (c) 1998, Daniel Morissette + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_error.h" +#include "cpl_string.h" +#include "cpl_vsi.h" +#include "cpl_conv.h" +#include "cpl_multiproc.h" + +#if defined(WIN32CE) +# include "cpl_wince.h" +# include +#endif + +#define TIMESTAMP_DEBUG +//#define MEMORY_DEBUG + +CPL_CVSID("$Id: cpl_error.cpp 29330 2015-06-14 12:11:11Z rouault $"); + +static CPLMutex *hErrorMutex = NULL; +static void *pErrorHandlerUserData = NULL; +static CPLErrorHandler pfnErrorHandler = CPLDefaultErrorHandler; + +#if !defined(HAVE_VSNPRINTF) +# define DEFAULT_LAST_ERR_MSG_SIZE 20000 +#else +# define DEFAULT_LAST_ERR_MSG_SIZE 500 +#endif + +typedef struct errHandler +{ + struct errHandler *psNext; + void *pUserData; + CPLErrorHandler pfnHandler; +} CPLErrorHandlerNode; + +typedef struct { + int nLastErrNo; + CPLErr eLastErrType; + CPLErrorHandlerNode *psHandlerStack; + int nLastErrMsgMax; + int nFailureIntoWarning; + char szLastErrMsg[DEFAULT_LAST_ERR_MSG_SIZE]; + /* Do not add anything here. szLastErrMsg must be the last field. See CPLRealloc() below */ +} CPLErrorContext; + +/************************************************************************/ +/* CPLGetErrorContext() */ +/************************************************************************/ + +static CPLErrorContext *CPLGetErrorContext() + +{ + CPLErrorContext *psCtx = + (CPLErrorContext *) CPLGetTLS( CTLS_ERRORCONTEXT ); + + if( psCtx == NULL ) + { + psCtx = (CPLErrorContext *) VSICalloc(sizeof(CPLErrorContext),1); + if (psCtx == NULL) { + CPLEmergencyError("Out of memory attempting to report error"); + } + psCtx->eLastErrType = CE_None; + psCtx->nLastErrMsgMax = sizeof(psCtx->szLastErrMsg); + CPLSetTLS( CTLS_ERRORCONTEXT, psCtx, TRUE ); + } + + return psCtx; +} + +/************************************************************************/ +/* CPLGetErrorHandlerUserData() */ +/************************************************************************/ + +/** + * Fetch the user data for the error context + * + * Fetches the user data for the current error context. You can + * set the user data for the error context when you add your handler by + * issuing CPLSetErrorHandlerEx() and CPLPushErrorHandlerEx(). Note that + * user data is primarily intended for providing context within error handlers + * themselves, but they could potentially be abused in other useful ways with the usual + * caveat emptor understanding. + * + * @return the user data pointer for the error context + */ + +void* CPL_STDCALL CPLGetErrorHandlerUserData(void) +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + return (void*) psCtx->psHandlerStack ? psCtx->psHandlerStack->pUserData : pErrorHandlerUserData; +} + +/********************************************************************** + * CPLError() + **********************************************************************/ + +/** + * Report an error. + * + * This function reports an error in a manner that can be hooked + * and reported appropriate by different applications. + * + * The effect of this function can be altered by applications by installing + * a custom error handling using CPLSetErrorHandler(). + * + * The eErrClass argument can have the value CE_Warning indicating that the + * message is an informational warning, CE_Failure indicating that the + * action failed, but that normal recover mechanisms will be used or + * CE_Fatal meaning that a fatal error has occured, and that CPLError() + * should not return. + * + * The default behaviour of CPLError() is to report errors to stderr, + * and to abort() after reporting a CE_Fatal error. It is expected that + * some applications will want to suppress error reporting, and will want to + * install a C++ exception, or longjmp() approach to no local fatal error + * recovery. + * + * Regardless of how application error handlers or the default error + * handler choose to handle an error, the error number, and message will + * be stored for recovery with CPLGetLastErrorNo() and CPLGetLastErrorMsg(). + * + * @param eErrClass one of CE_Warning, CE_Failure or CE_Fatal. + * @param err_no the error number (CPLE_*) from cpl_error.h. + * @param fmt a printf() style format string. Any additional arguments + * will be treated as arguments to fill in this format in a manner + * similar to printf(). + */ + +void CPLError(CPLErr eErrClass, int err_no, const char *fmt, ...) +{ + va_list args; + + /* Expand the error message + */ + va_start(args, fmt); + CPLErrorV( eErrClass, err_no, fmt, args ); + va_end(args); +} + +/************************************************************************/ +/* CPLErrorV() */ +/************************************************************************/ + +void CPLErrorV(CPLErr eErrClass, int err_no, const char *fmt, va_list args ) +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + + if (psCtx->nFailureIntoWarning > 0 && eErrClass == CE_Failure) + eErrClass = CE_Warning; + +/* -------------------------------------------------------------------- */ +/* Expand the error message */ +/* -------------------------------------------------------------------- */ +#if defined(HAVE_VSNPRINTF) + { + int nPR; + va_list wrk_args; + +#ifdef va_copy + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + +/* -------------------------------------------------------------------- */ +/* If CPL_ACCUM_ERROR_MSG=ON accumulate the error messages, */ +/* rather than just replacing the last error message. */ +/* -------------------------------------------------------------------- */ + int nPreviousSize = 0; + if ( psCtx->psHandlerStack != NULL && + EQUAL(CPLGetConfigOption( "CPL_ACCUM_ERROR_MSG", "" ), "ON")) + { + nPreviousSize = strlen(psCtx->szLastErrMsg); + if (nPreviousSize) + { + if (nPreviousSize + 1 + 1 >= psCtx->nLastErrMsgMax) + { + psCtx->nLastErrMsgMax *= 3; + psCtx = (CPLErrorContext *) + CPLRealloc(psCtx, sizeof(CPLErrorContext) - DEFAULT_LAST_ERR_MSG_SIZE + psCtx->nLastErrMsgMax + 1); + CPLSetTLS( CTLS_ERRORCONTEXT, psCtx, TRUE ); + } + psCtx->szLastErrMsg[nPreviousSize] = '\n'; + psCtx->szLastErrMsg[nPreviousSize+1] = '0'; + nPreviousSize ++; + } + } + + while( ((nPR = CPLvsnprintf( psCtx->szLastErrMsg+nPreviousSize, + psCtx->nLastErrMsgMax-nPreviousSize, fmt, wrk_args )) == -1 + || nPR >= psCtx->nLastErrMsgMax-nPreviousSize-1) + && psCtx->nLastErrMsgMax < 1000000 ) + { +#ifdef va_copy + va_end( wrk_args ); + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + psCtx->nLastErrMsgMax *= 3; + psCtx = (CPLErrorContext *) + CPLRealloc(psCtx, sizeof(CPLErrorContext) - DEFAULT_LAST_ERR_MSG_SIZE + psCtx->nLastErrMsgMax + 1); + CPLSetTLS( CTLS_ERRORCONTEXT, psCtx, TRUE ); + } + + va_end( wrk_args ); + } +#else + // !HAVE_VSNPRINTF + CPLvsnprintf( psCtx->szLastErrMsg, psCtx->nLastErrMsgMax, fmt, args); +#endif + +/* -------------------------------------------------------------------- */ +/* Obfuscate any password in error message */ +/* -------------------------------------------------------------------- */ + + char* pszPassword = strstr(psCtx->szLastErrMsg, "password="); + if( pszPassword != NULL ) + { + char* pszIter = pszPassword + strlen("password="); + while( *pszIter != ' ' && *pszIter != '\0' ) + { + *pszIter = 'X'; + pszIter ++; + } + } + +/* -------------------------------------------------------------------- */ +/* If the user provided an handling function, then */ +/* call it, otherwise print the error to stderr and return. */ +/* -------------------------------------------------------------------- */ + psCtx->nLastErrNo = err_no; + psCtx->eLastErrType = eErrClass; + + if( CPLGetConfigOption("CPL_LOG_ERRORS",NULL) != NULL ) + CPLDebug( "CPLError", "%s", psCtx->szLastErrMsg ); + +/* -------------------------------------------------------------------- */ +/* Invoke the current error handler. */ +/* -------------------------------------------------------------------- */ + if( psCtx->psHandlerStack != NULL ) + { + psCtx->psHandlerStack->pfnHandler(eErrClass, err_no, + psCtx->szLastErrMsg); + } + else + { + CPLMutexHolderD( &hErrorMutex ); + if( pfnErrorHandler != NULL ) + pfnErrorHandler(eErrClass, err_no, psCtx->szLastErrMsg); + } + + if( eErrClass == CE_Fatal ) + abort(); +} + +/************************************************************************/ +/* CPLEmergencyError() */ +/************************************************************************/ + +/** + * Fatal error when things are bad. + * + * This function should be called in an emergency situation where + * it is unlikely that a regular error report would work. This would + * include in the case of heap exhaustion for even small allocations, + * or any failure in the process of reporting an error (such as TLS + * allocations). + * + * This function should never return. After the error message has been + * reported as best possible, the application will abort() similarly to how + * CPLError() aborts on CE_Fatal class errors. + * + * @param pszMessage the error message to report. + */ + +void CPLEmergencyError( const char *pszMessage ) +{ + CPLErrorContext *psCtx = NULL; + static int bInEmergencyError = FALSE; + + // If we are already in emergency error then one of the + // following failed, so avoid them the second time through. + if( !bInEmergencyError ) + { + bInEmergencyError = TRUE; + psCtx = (CPLErrorContext *) CPLGetTLS( CTLS_ERRORCONTEXT ); + + if( psCtx != NULL && psCtx->psHandlerStack != NULL ) + { + psCtx->psHandlerStack->pfnHandler( CE_Fatal, CPLE_AppDefined, + pszMessage ); + } + else if( pfnErrorHandler != NULL ) + { + pfnErrorHandler( CE_Fatal, CPLE_AppDefined, pszMessage ); + } + } + + // Ultimate fallback. + fprintf( stderr, "FATAL: %s\n", pszMessage ); + + abort(); +} + +/************************************************************************/ +/* CPLGetProcessMemorySize() */ +/************************************************************************/ + +#ifdef MEMORY_DEBUG + +#ifdef __linux +static int CPLGetProcessMemorySize() +{ + FILE* fp = fopen("/proc/self/status", "r"); + if( fp == NULL ) + return -1; + int nRet = -1; + char szLine[128]; + while (fgets(szLine, sizeof(szLine), fp) != NULL) + { + if (strncmp(szLine, "VmSize:", 7) == 0) + { + const char* pszPtr = szLine; + while( !(*pszPtr == '\0' || (*pszPtr >= '0' && *pszPtr <= '9')) ) + pszPtr ++; + nRet = atoi(pszPtr); + break; + } + } + fclose(fp); + return nRet; +} +#else +#error CPLGetProcessMemorySize() unimplemented for this OS +#endif + +#endif // def MEMORY_DEBUG + +/************************************************************************/ +/* CPLDebug() */ +/************************************************************************/ + +/** + * Display a debugging message. + * + * The category argument is used in conjunction with the CPL_DEBUG + * environment variable to establish if the message should be displayed. + * If the CPL_DEBUG environment variable is not set, no debug messages + * are emitted (use CPLError(CE_Warning,...) to ensure messages are displayed). + * If CPL_DEBUG is set, but is an empty string or the word "ON" then all + * debug messages are shown. Otherwise only messages whose category appears + * somewhere within the CPL_DEBUG value are displayed (as determinted by + * strstr()). + * + * Categories are usually an identifier for the subsystem producing the + * error. For instance "GDAL" might be used for the GDAL core, and "TIFF" + * for messages from the TIFF translator. + * + * @param pszCategory name of the debugging message category. + * @param pszFormat printf() style format string for message to display. + * Remaining arguments are assumed to be for format. + */ + +void CPLDebug( const char * pszCategory, const char * pszFormat, ... ) + +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + char *pszMessage; + va_list args; + const char *pszDebug = CPLGetConfigOption("CPL_DEBUG",NULL); + +#define ERROR_MAX 25000 + +/* -------------------------------------------------------------------- */ +/* Does this message pass our current criteria? */ +/* -------------------------------------------------------------------- */ + if( pszDebug == NULL ) + return; + + if( !EQUAL(pszDebug,"ON") && !EQUAL(pszDebug,"") ) + { + size_t i, nLen = strlen(pszCategory); + + for( i = 0; pszDebug[i] != '\0'; i++ ) + { + if( EQUALN(pszCategory,pszDebug+i,nLen) ) + break; + } + + if( pszDebug[i] == '\0' ) + return; + } + +/* -------------------------------------------------------------------- */ +/* Allocate a block for the error. */ +/* -------------------------------------------------------------------- */ + pszMessage = (char *) VSIMalloc( ERROR_MAX ); + if( pszMessage == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Dal -- always log a timestamp as the first part of the line */ +/* to ensure one is looking at what one should be looking at! */ +/* -------------------------------------------------------------------- */ + + pszMessage[0] = '\0'; +#ifdef TIMESTAMP_DEBUG + if( CPLGetConfigOption( "CPL_TIMESTAMP", NULL ) != NULL ) + { + strcpy( pszMessage, VSICTime( VSITime(NULL) ) ); + + // On windows anyway, ctime puts a \n at the end, but I'm not + // convinced this is standard behaviour, so we'll get rid of it + // carefully + + if (pszMessage[strlen(pszMessage) -1 ] == '\n') + { + pszMessage[strlen(pszMessage) - 1] = 0; // blow it out + } + strcat( pszMessage, ": " ); + } +#endif + +/* -------------------------------------------------------------------- */ +/* Add the process memory size. */ +/* -------------------------------------------------------------------- */ +#ifdef MEMORY_DEBUG + char szVmSize[32]; + CPLsprintf( szVmSize, "[VmSize: %d] ", CPLGetProcessMemorySize()); + strcat( pszMessage, szVmSize ); +#endif + + //sprintf(pszMessage,"[%d] ", (int)getpid()); + +/* -------------------------------------------------------------------- */ +/* Add the category. */ +/* -------------------------------------------------------------------- */ + strcat( pszMessage, pszCategory ); + strcat( pszMessage, ": " ); + +/* -------------------------------------------------------------------- */ +/* Format the application provided portion of the debug message. */ +/* -------------------------------------------------------------------- */ + va_start(args, pszFormat); + + CPLvsnprintf(pszMessage+strlen(pszMessage), ERROR_MAX - strlen(pszMessage), + pszFormat, args); + + va_end(args); + +/* -------------------------------------------------------------------- */ +/* Obfuscate any password in error message */ +/* -------------------------------------------------------------------- */ + + char* pszPassword = strstr(pszMessage, "password="); + if( pszPassword != NULL ) + { + char* pszIter = pszPassword + strlen("password="); + while( *pszIter != ' ' && *pszIter != '\0' ) + { + *pszIter = 'X'; + pszIter ++; + } + } + +/* -------------------------------------------------------------------- */ +/* Invoke the current error handler. */ +/* -------------------------------------------------------------------- */ + if( psCtx->psHandlerStack != NULL ) + { + psCtx->psHandlerStack->pfnHandler( CE_Debug, CPLE_None, pszMessage ); + } + else + { + CPLMutexHolderD( &hErrorMutex ); + if( pfnErrorHandler != NULL ) + pfnErrorHandler( CE_Debug, CPLE_None, pszMessage ); + } + + VSIFree( pszMessage ); +} + +/********************************************************************** + * CPLErrorReset() + **********************************************************************/ + +/** + * Erase any traces of previous errors. + * + * This is normally used to ensure that an error which has been recovered + * from does not appear to be still in play with high level functions. + */ + +void CPL_STDCALL CPLErrorReset() +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + + psCtx->nLastErrNo = CPLE_None; + psCtx->szLastErrMsg[0] = '\0'; + psCtx->eLastErrType = CE_None; +} + +/********************************************************************** + * CPLErrorSetState() + **********************************************************************/ + +/** + * Restore an error state, without emitting an error. + * + * Can be useful if a routine might call CPLErrorReset() and one wants to + * preserve the previous error state. + * + * @since GDAL 2.0 + */ + +void CPL_DLL CPLErrorSetState( CPLErr eErrClass, int err_no, const char* pszMsg ) +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + + psCtx->nLastErrNo = err_no; + strncpy(psCtx->szLastErrMsg, pszMsg, psCtx->nLastErrMsgMax); + psCtx->szLastErrMsg[MAX(psCtx->nLastErrMsgMax-1, (int)strlen(pszMsg))] = '\0'; + psCtx->eLastErrType = eErrClass; +} + +/********************************************************************** + * CPLGetLastErrorNo() + **********************************************************************/ + +/** + * Fetch the last error number. + * + * Fetches the last error number posted with CPLError(), that hasn't + * been cleared by CPLErrorReset(). This is the error number, not the error class. + * + * @return the error number of the last error to occur, or CPLE_None (0) + * if there are no posted errors. + */ + +int CPL_STDCALL CPLGetLastErrorNo() +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + + return psCtx->nLastErrNo; +} + +/********************************************************************** + * CPLGetLastErrorType() + **********************************************************************/ + +/** + * Fetch the last error type. + * + * Fetches the last error type posted with CPLError(), that hasn't + * been cleared by CPLErrorReset(). This is the error class, not the error number. + * + * @return the error type of the last error to occur, or CE_None (0) + * if there are no posted errors. + */ + +CPLErr CPL_STDCALL CPLGetLastErrorType() +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + + return psCtx->eLastErrType; +} + +/********************************************************************** + * CPLGetLastErrorMsg() + **********************************************************************/ + +/** + * Get the last error message. + * + * Fetches the last error message posted with CPLError(), that hasn't + * been cleared by CPLErrorReset(). The returned pointer is to an internal + * string that should not be altered or freed. + * + * @return the last error message, or NULL if there is no posted error + * message. + */ + +const char* CPL_STDCALL CPLGetLastErrorMsg() +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + + return psCtx->szLastErrMsg; +} + +/************************************************************************/ +/* CPLDefaultErrorHandler() */ +/************************************************************************/ + +void CPL_STDCALL CPLDefaultErrorHandler( CPLErr eErrClass, int nError, + const char * pszErrorMsg ) + +{ + static int bLogInit = FALSE; + static FILE * fpLog = stderr; + static int nCount = 0; + static int nMaxErrors = -1; + + if (eErrClass != CE_Debug) + { + if( nMaxErrors == -1 ) + { + nMaxErrors = + atoi(CPLGetConfigOption( "CPL_MAX_ERROR_REPORTS", "1000" )); + } + + nCount++; + if (nCount > nMaxErrors && nMaxErrors > 0 ) + return; + } + + if( !bLogInit ) + { + bLogInit = TRUE; + + fpLog = stderr; + if( CPLGetConfigOption( "CPL_LOG", NULL ) != NULL ) + { + const char* pszAccess = "wt"; + if( CPLGetConfigOption( "CPL_LOG_APPEND", NULL ) != NULL ) + pszAccess = "at"; + fpLog = fopen( CPLGetConfigOption("CPL_LOG",""), pszAccess ); + if( fpLog == NULL ) + fpLog = stderr; + } + } + + if( eErrClass == CE_Debug ) + fprintf( fpLog, "%s\n", pszErrorMsg ); + else if( eErrClass == CE_Warning ) + fprintf( fpLog, "Warning %d: %s\n", nError, pszErrorMsg ); + else + fprintf( fpLog, "ERROR %d: %s\n", nError, pszErrorMsg ); + + if (eErrClass != CE_Debug + && nMaxErrors > 0 + && nCount == nMaxErrors ) + { + fprintf( fpLog, + "More than %d errors or warnings have been reported. " + "No more will be reported from now.\n", + nMaxErrors ); + } + + fflush( fpLog ); +} + +/************************************************************************/ +/* CPLQuietErrorHandler() */ +/************************************************************************/ + +void CPL_STDCALL CPLQuietErrorHandler( CPLErr eErrClass , int nError, + const char * pszErrorMsg ) + +{ + if( eErrClass == CE_Debug ) + CPLDefaultErrorHandler( eErrClass, nError, pszErrorMsg ); +} + +/************************************************************************/ +/* CPLLoggingErrorHandler() */ +/************************************************************************/ + +void CPL_STDCALL CPLLoggingErrorHandler( CPLErr eErrClass, int nError, + const char * pszErrorMsg ) + +{ + static int bLogInit = FALSE; + static FILE * fpLog = stderr; + + if( !bLogInit ) + { + const char *cpl_log = NULL; + + CPLSetConfigOption( "CPL_TIMESTAMP", "ON" ); + + bLogInit = TRUE; + + cpl_log = CPLGetConfigOption("CPL_LOG", NULL ); + + fpLog = stderr; + if( cpl_log != NULL && EQUAL(cpl_log,"OFF") ) + { + fpLog = NULL; + } + else if( cpl_log != NULL ) + { + char* pszPath; + int i = 0; + + pszPath = (char*)CPLMalloc(strlen(cpl_log) + 20); + strcpy(pszPath, cpl_log); + + while( (fpLog = fopen( pszPath, "rt" )) != NULL ) + { + fclose( fpLog ); + + /* generate sequenced log file names, inserting # before ext.*/ + if (strrchr(cpl_log, '.') == NULL) + { + CPLsprintf( pszPath, "%s_%d%s", cpl_log, i++, + ".log" ); + } + else + { + size_t pos = 0; + char *cpl_log_base = CPLStrdup(cpl_log); + pos = strcspn(cpl_log_base, "."); + if (pos > 0) + { + cpl_log_base[pos] = '\0'; + } + CPLsprintf( pszPath, "%s_%d%s", cpl_log_base, + i++, ".log" ); + CPLFree(cpl_log_base); + } + } + + fpLog = fopen( pszPath, "wt" ); + CPLFree(pszPath); + } + } + + if( fpLog == NULL ) + return; + + if( eErrClass == CE_Debug ) + fprintf( fpLog, "%s\n", pszErrorMsg ); + else if( eErrClass == CE_Warning ) + fprintf( fpLog, "Warning %d: %s\n", nError, pszErrorMsg ); + else + fprintf( fpLog, "ERROR %d: %s\n", nError, pszErrorMsg ); + + fflush( fpLog ); +} + +/********************************************************************** + * CPLTurnFailureIntoWarning() * + **********************************************************************/ + +void CPLTurnFailureIntoWarning(int bOn ) +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + psCtx->nFailureIntoWarning += (bOn) ? 1 : -1; + if (psCtx->nFailureIntoWarning < 0) + { + CPLDebug("CPL", "Wrong nesting of CPLTurnFailureIntoWarning(TRUE) / CPLTurnFailureIntoWarning(FALSE)"); + } +} + +/********************************************************************** + * CPLSetErrorHandlerEx() * + **********************************************************************/ + +/** + * Install custom error handle with user's data. This method is + * essentially CPLSetErrorHandler with an added pointer to pUserData. + * The pUserData is not returned in the CPLErrorHandler, however, and + * must be fetched via CPLGetLastErrorUserData + * + * @param pfnErrorHandlerNew new error handler function. + * @param pUserData User data to carry along with the error context. + * @return returns the previously installed error handler. + */ + +CPLErrorHandler CPL_STDCALL +CPLSetErrorHandlerEx( CPLErrorHandler pfnErrorHandlerNew, + void* pUserData ) +{ + CPLErrorHandler pfnOldHandler = pfnErrorHandler; + CPLErrorContext *psCtx = CPLGetErrorContext(); + + if( psCtx->psHandlerStack != NULL ) + { + CPLDebug( "CPL", + "CPLSetErrorHandler() called with an error handler on\n" + "the local stack. New error handler will not be used immediately.\n" ); + } + + + { + CPLMutexHolderD( &hErrorMutex ); + + pfnOldHandler = pfnErrorHandler; + + if( pfnErrorHandler == NULL ) + pfnErrorHandler = CPLDefaultErrorHandler; + else + pfnErrorHandler = pfnErrorHandlerNew; + + pErrorHandlerUserData = pUserData; + } + + return pfnOldHandler; +} + + +/********************************************************************** + * CPLSetErrorHandler() * + **********************************************************************/ + +/** + * Install custom error handler. + * + * Allow the library's user to specify an error handler function. + * A valid error handler is a C function with the following prototype: + * + *
    + *     void MyErrorHandler(CPLErr eErrClass, int err_no, const char *msg)
    + * 
    + * + * Pass NULL to come back to the default behavior. The default behaviour + * (CPLDefaultErrorHandler()) is to write the message to stderr. + * + * The msg will be a partially formatted error message not containing the + * "ERROR %d:" portion emitted by the default handler. Message formatting + * is handled by CPLError() before calling the handler. If the error + * handler function is passed a CE_Fatal class error and returns, then + * CPLError() will call abort(). Applications wanting to interrupt this + * fatal behaviour will have to use longjmp(), or a C++ exception to + * indirectly exit the function. + * + * Another standard error handler is CPLQuietErrorHandler() which doesn't + * make any attempt to report the passed error or warning messages but + * will process debug messages via CPLDefaultErrorHandler. + * + * Note that error handlers set with CPLSetErrorHandler() apply to all + * threads in an application, while error handlers set with CPLPushErrorHandler + * are thread-local. However, any error handlers pushed with + * CPLPushErrorHandler (and not removed with CPLPopErrorHandler) take + * precidence over the global error handlers set with CPLSetErrorHandler(). + * Generally speaking CPLSetErrorHandler() would be used to set a desired + * global error handler, while CPLPushErrorHandler() would be used to install + * a temporary local error handler, such as CPLQuietErrorHandler() to suppress + * error reporting in a limited segment of code. + * + * @param pfnErrorHandlerNew new error handler function. + * @return returns the previously installed error handler. + */ +CPLErrorHandler CPL_STDCALL +CPLSetErrorHandler( CPLErrorHandler pfnErrorHandlerNew ) +{ + return CPLSetErrorHandlerEx(pfnErrorHandlerNew, NULL); +} + +/************************************************************************/ +/* CPLPushErrorHandler() */ +/************************************************************************/ + +/** + * Push a new CPLError handler. + * + * This pushes a new error handler on the thread-local error handler + * stack. This handler will be used until removed with CPLPopErrorHandler(). + * + * The CPLSetErrorHandler() docs have further information on how + * CPLError handlers work. + * + * @param pfnErrorHandlerNew new error handler function. + */ + +void CPL_STDCALL CPLPushErrorHandler( CPLErrorHandler pfnErrorHandlerNew ) + +{ + CPLPushErrorHandlerEx(pfnErrorHandlerNew, NULL); +} + + +/************************************************************************/ +/* CPLPushErrorHandlerEx() */ +/************************************************************************/ + +/** + * Push a new CPLError handler with user data on the error context. + * + * This pushes a new error handler on the thread-local error handler + * stack. This handler will be used until removed with CPLPopErrorHandler(). + * Obtain the user data back by using CPLGetErrorContext(). + * + * The CPLSetErrorHandler() docs have further information on how + * CPLError handlers work. + * + * @param pfnErrorHandlerNew new error handler function. + * @param pUserData User data to put on the error context. + */ +void CPL_STDCALL CPLPushErrorHandlerEx( CPLErrorHandler pfnErrorHandlerNew, + void* pUserData ) + +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + CPLErrorHandlerNode *psNode; + + psNode = (CPLErrorHandlerNode *) CPLMalloc(sizeof(CPLErrorHandlerNode)); + psNode->psNext = psCtx->psHandlerStack; + psNode->pfnHandler = pfnErrorHandlerNew; + psNode->pUserData = pUserData; + psCtx->psHandlerStack = psNode; +} + +/************************************************************************/ +/* CPLPopErrorHandler() */ +/************************************************************************/ + +/** + * Pop error handler off stack. + * + * Discards the current error handler on the error handler stack, and restores + * the one in use before the last CPLPushErrorHandler() call. This method + * has no effect if there are no error handlers on the current threads error + * handler stack. + */ + +void CPL_STDCALL CPLPopErrorHandler() + +{ + CPLErrorContext *psCtx = CPLGetErrorContext(); + + if( psCtx->psHandlerStack != NULL ) + { + CPLErrorHandlerNode *psNode = psCtx->psHandlerStack; + + psCtx->psHandlerStack = psNode->psNext; + VSIFree( psNode ); + } +} + +/************************************************************************/ +/* _CPLAssert() */ +/* */ +/* This function is called only when an assertion fails. */ +/************************************************************************/ + +/** + * Report failure of a logical assertion. + * + * Applications would normally use the CPLAssert() macro which expands + * into code calling _CPLAssert() only if the condition fails. _CPLAssert() + * will generate a CE_Fatal error call to CPLError(), indicating the file + * name, and line number of the failed assertion, as well as containing + * the assertion itself. + * + * There is no reason for application code to call _CPLAssert() directly. + */ + +void CPL_STDCALL _CPLAssert( const char * pszExpression, const char * pszFile, + int iLine ) + +{ + CPLError( CE_Fatal, CPLE_AssertionFailed, + "Assertion `%s' failed\n" + "in file `%s', line %d\n", + pszExpression, pszFile, iLine ); +} + + +/************************************************************************/ +/* CPLCleanupErrorMutex() */ +/************************************************************************/ + +void CPLCleanupErrorMutex() +{ + if( hErrorMutex != NULL ) + { + CPLDestroyMutex(hErrorMutex); + hErrorMutex = NULL; + } +} diff --git a/bazaar/plugin/gdal/port/cpl_error.h b/bazaar/plugin/gdal/port/cpl_error.h new file mode 100644 index 000000000..49389609a --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_error.h @@ -0,0 +1,135 @@ +/********************************************************************** + * $Id: cpl_error.h 27384 2014-05-24 12:28:12Z rouault $ + * + * Name: cpl_error.h + * Project: CPL - Common Portability Library + * Purpose: CPL Error handling + * Author: Daniel Morissette, danmo@videotron.ca + * + ********************************************************************** + * Copyright (c) 1998, Daniel Morissette + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_ERROR_H_INCLUDED +#define CPL_ERROR_H_INCLUDED + +#include "cpl_port.h" + +/*===================================================================== + Error handling functions (cpl_error.c) + =====================================================================*/ + +/** + * \file cpl_error.h + * + * CPL error handling services. + */ + +CPL_C_START + +typedef enum +{ + CE_None = 0, + CE_Debug = 1, + CE_Warning = 2, + CE_Failure = 3, + CE_Fatal = 4 +} CPLErr; + +void CPL_DLL CPLError(CPLErr eErrClass, int err_no, const char *fmt, ...) CPL_PRINT_FUNC_FORMAT (3, 4); +void CPL_DLL CPLErrorV(CPLErr, int, const char *, va_list ); +void CPL_DLL CPLEmergencyError( const char * ); +void CPL_DLL CPL_STDCALL CPLErrorReset( void ); +int CPL_DLL CPL_STDCALL CPLGetLastErrorNo( void ); +CPLErr CPL_DLL CPL_STDCALL CPLGetLastErrorType( void ); +const char CPL_DLL * CPL_STDCALL CPLGetLastErrorMsg( void ); +void CPL_DLL * CPL_STDCALL CPLGetErrorHandlerUserData(void); +void CPL_DLL CPLErrorSetState( CPLErr eErrClass, int err_no, const char* pszMsg ); +void CPL_DLL CPLCleanupErrorMutex( void ); + +typedef void (CPL_STDCALL *CPLErrorHandler)(CPLErr, int, const char*); + +void CPL_DLL CPL_STDCALL CPLLoggingErrorHandler( CPLErr, int, const char * ); +void CPL_DLL CPL_STDCALL CPLDefaultErrorHandler( CPLErr, int, const char * ); +void CPL_DLL CPL_STDCALL CPLQuietErrorHandler( CPLErr, int, const char * ); +void CPLTurnFailureIntoWarning(int bOn ); + +CPLErrorHandler CPL_DLL CPL_STDCALL CPLSetErrorHandler(CPLErrorHandler); +CPLErrorHandler CPL_DLL CPL_STDCALL CPLSetErrorHandlerEx(CPLErrorHandler, void*); +void CPL_DLL CPL_STDCALL CPLPushErrorHandler( CPLErrorHandler ); +void CPL_DLL CPL_STDCALL CPLPushErrorHandlerEx( CPLErrorHandler, void* ); +void CPL_DLL CPL_STDCALL CPLPopErrorHandler(void); + +void CPL_DLL CPL_STDCALL CPLDebug( const char *, const char *, ... ) CPL_PRINT_FUNC_FORMAT (2, 3); +void CPL_DLL CPL_STDCALL _CPLAssert( const char *, const char *, int ); + +#ifdef DEBUG +# define CPLAssert(expr) ((expr) ? (void)(0) : _CPLAssert(#expr,__FILE__,__LINE__)) +#else +# define CPLAssert(expr) +#endif + +CPL_C_END + +/* + * Helper macros used for input parameters validation. + */ +#ifdef DEBUG +# define VALIDATE_POINTER_ERR CE_Fatal +#else +# define VALIDATE_POINTER_ERR CE_Failure +#endif + +#define VALIDATE_POINTER0(ptr, func) \ + do { if( NULL == ptr ) \ + { \ + CPLErr const ret = VALIDATE_POINTER_ERR; \ + CPLError( ret, CPLE_ObjectNull, \ + "Pointer \'%s\' is NULL in \'%s\'.\n", #ptr, (func)); \ + return; }} while(0) + +#define VALIDATE_POINTER1(ptr, func, rc) \ + do { if( NULL == ptr ) \ + { \ + CPLErr const ret = VALIDATE_POINTER_ERR; \ + CPLError( ret, CPLE_ObjectNull, \ + "Pointer \'%s\' is NULL in \'%s\'.\n", #ptr, (func)); \ + return (rc); }} while(0) + +/* ==================================================================== */ +/* Well known error codes. */ +/* ==================================================================== */ + +#define CPLE_None 0 +#define CPLE_AppDefined 1 +#define CPLE_OutOfMemory 2 +#define CPLE_FileIO 3 +#define CPLE_OpenFailed 4 +#define CPLE_IllegalArg 5 +#define CPLE_NotSupported 6 +#define CPLE_AssertionFailed 7 +#define CPLE_NoWriteAccess 8 +#define CPLE_UserInterrupt 9 +#define CPLE_ObjectNull 10 + +/* 100 - 299 reserved for GDAL */ + +#endif /* CPL_ERROR_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_findfile.cpp b/bazaar/plugin/gdal/port/cpl_findfile.cpp new file mode 100644 index 000000000..e7416f823 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_findfile.cpp @@ -0,0 +1,267 @@ +/****************************************************************************** + * $Id: cpl_findfile.cpp 27547 2014-07-23 16:27:50Z goatbar $ + * + * Project: CPL - Common Portability Library + * Purpose: Generic data file location finder, with application hooking. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: cpl_findfile.cpp 27547 2014-07-23 16:27:50Z goatbar $"); + +typedef struct +{ + int bFinderInitialized; + int nFileFinders; + CPLFileFinder *papfnFinders; + char **papszFinderLocations; +} FindFileTLS; + + +/************************************************************************/ +/* CPLFindFileDeinitTLS() */ +/************************************************************************/ + +static void CPLPopFinderLocationInternal(FindFileTLS* pTLSData); +static CPLFileFinder CPLPopFileFinderInternal(FindFileTLS* pTLSData); + +static void CPLFindFileFreeTLS(void* pData) +{ + FindFileTLS* pTLSData = (FindFileTLS*) pData; + if( pTLSData->bFinderInitialized ) + { + while( pTLSData->papszFinderLocations != NULL ) + CPLPopFinderLocationInternal(pTLSData); + while( CPLPopFileFinderInternal(pTLSData) != NULL ) {} + + pTLSData->bFinderInitialized = FALSE; + } + CPLFree(pTLSData); +} + +/************************************************************************/ +/* CPLGetFindFileTLS() */ +/************************************************************************/ + +static FindFileTLS* CPLGetFindFileTLS() +{ + FindFileTLS* pTLSData = + (FindFileTLS *) CPLGetTLS( CTLS_FINDFILE ); + if (pTLSData == NULL) + { + pTLSData = (FindFileTLS*) CPLCalloc(1, sizeof(FindFileTLS)); + CPLSetTLSWithFreeFunc( CTLS_FINDFILE, pTLSData, CPLFindFileFreeTLS ); + } + return pTLSData; +} + +/************************************************************************/ +/* CPLFinderInit() */ +/************************************************************************/ + +static FindFileTLS* CPLFinderInit() + +{ + FindFileTLS* pTLSData = CPLGetFindFileTLS(); + if( !pTLSData->bFinderInitialized ) + { + pTLSData->bFinderInitialized = TRUE; + CPLPushFileFinder( CPLDefaultFindFile ); + + CPLPushFinderLocation( "." ); + + if( CPLGetConfigOption( "GDAL_DATA", NULL ) != NULL ) + { + CPLPushFinderLocation( CPLGetConfigOption( "GDAL_DATA", NULL ) ); + } + else + { +#ifdef GDAL_PREFIX + #ifdef MACOSX_FRAMEWORK + CPLPushFinderLocation( GDAL_PREFIX "/Resources/gdal" ); + #else + CPLPushFinderLocation( GDAL_PREFIX "/share/gdal" ); + #endif +#else + CPLPushFinderLocation( "/usr/local/share/gdal" ); +#endif + } + } + return pTLSData; +} + +/************************************************************************/ +/* CPLFinderClean() */ +/************************************************************************/ + +void CPLFinderClean() + +{ + FindFileTLS* pTLSData = CPLGetFindFileTLS(); + CPLFindFileFreeTLS(pTLSData); + CPLSetTLS( CTLS_FINDFILE, NULL, FALSE ); +} + +/************************************************************************/ +/* CPLDefaultFindFile() */ +/************************************************************************/ + +const char *CPLDefaultFindFile( const char *pszClass, + const char *pszBasename ) + +{ + FindFileTLS* pTLSData = CPLGetFindFileTLS(); + int i, nLocations = CSLCount( pTLSData->papszFinderLocations ); + + (void) pszClass; + + for( i = nLocations-1; i >= 0; i-- ) + { + const char *pszResult; + VSIStatBuf sStat; + + pszResult = CPLFormFilename( pTLSData->papszFinderLocations[i], pszBasename, + NULL ); + + if( VSIStat( pszResult, &sStat ) == 0 ) + return pszResult; + } + + return NULL; +} + +/************************************************************************/ +/* CPLFindFile() */ +/************************************************************************/ + +const char *CPLFindFile( const char *pszClass, const char *pszBasename ) + +{ + int i; + + FindFileTLS* pTLSData = CPLFinderInit(); + + for( i = pTLSData->nFileFinders-1; i >= 0; i-- ) + { + const char * pszResult; + + pszResult = (pTLSData->papfnFinders[i])( pszClass, pszBasename ); + if( pszResult != NULL ) + return pszResult; + } + + return NULL; +} + +/************************************************************************/ +/* CPLPushFileFinder() */ +/************************************************************************/ + +void CPLPushFileFinder( CPLFileFinder pfnFinder ) + +{ + FindFileTLS* pTLSData = CPLFinderInit(); + + pTLSData->papfnFinders = (CPLFileFinder *) + CPLRealloc(pTLSData->papfnFinders, sizeof(void*) * ++pTLSData->nFileFinders); + pTLSData->papfnFinders[pTLSData->nFileFinders-1] = pfnFinder; +} + +/************************************************************************/ +/* CPLPopFileFinder() */ +/************************************************************************/ + +CPLFileFinder CPLPopFileFinderInternal(FindFileTLS* pTLSData) + +{ + CPLFileFinder pfnReturn; + + if( pTLSData->nFileFinders == 0 ) + return NULL; + + pfnReturn = pTLSData->papfnFinders[--pTLSData->nFileFinders]; + + if( pTLSData->nFileFinders == 0) + { + CPLFree( pTLSData->papfnFinders ); + pTLSData->papfnFinders = NULL; + } + + return pfnReturn; +} + +CPLFileFinder CPLPopFileFinder() + +{ + return CPLPopFileFinderInternal(CPLFinderInit()); +} + +/************************************************************************/ +/* CPLPushFinderLocation() */ +/************************************************************************/ + +void CPLPushFinderLocation( const char *pszLocation ) + +{ + FindFileTLS* pTLSData = CPLFinderInit(); + + pTLSData->papszFinderLocations = CSLAddString( pTLSData->papszFinderLocations, + pszLocation ); +} + + +/************************************************************************/ +/* CPLPopFinderLocation() */ +/************************************************************************/ + +static void CPLPopFinderLocationInternal(FindFileTLS* pTLSData) + +{ + int nCount; + + if( pTLSData->papszFinderLocations == NULL ) + return; + + nCount = CSLCount(pTLSData->papszFinderLocations); + if( nCount == 0 ) + return; + + CPLFree( pTLSData->papszFinderLocations[nCount-1] ); + pTLSData->papszFinderLocations[nCount-1] = NULL; + + if( nCount == 1 ) + { + CPLFree( pTLSData->papszFinderLocations ); + pTLSData->papszFinderLocations = NULL; + } +} + +void CPLPopFinderLocation() +{ + CPLPopFinderLocationInternal(CPLFinderInit()); +} diff --git a/bazaar/plugin/gdal/port/cpl_getexecpath.cpp b/bazaar/plugin/gdal/port/cpl_getexecpath.cpp new file mode 100644 index 000000000..cea397705 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_getexecpath.cpp @@ -0,0 +1,144 @@ +/********************************************************************** + * $Id: cpl_getexecpath.cpp 27721 2014-09-22 12:42:28Z goatbar $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement CPLGetExecPath(). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" + +CPL_CVSID("$Id: cpl_getexecpath.cpp 27721 2014-09-22 12:42:28Z goatbar $"); + +#if defined(WIN32) || defined(WIN32CE) + +#define HAVE_IMPLEMENTATION 1 + +#if defined(WIN32CE) +# include "cpl_win32ce_api.h" +#else +# include +#endif + +/************************************************************************/ +/* CPLGetExecPath() */ +/************************************************************************/ + +int CPLGetExecPath( char *pszPathBuf, int nMaxLength ) +{ +#ifndef WIN32CE + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + wchar_t *pwszPathBuf = (wchar_t*) + CPLCalloc(nMaxLength+1,sizeof(wchar_t)); + + if( GetModuleFileNameW( NULL, pwszPathBuf, nMaxLength ) == 0 ) + { + CPLFree( pwszPathBuf ); + return FALSE; + } + else + { + char *pszDecoded = + CPLRecodeFromWChar(pwszPathBuf,CPL_ENC_UCS2,CPL_ENC_UTF8); + + strncpy( pszPathBuf, pszDecoded, nMaxLength ); + CPLFree( pszDecoded ); + CPLFree( pwszPathBuf ); + return TRUE; + } + } + else + { + if( GetModuleFileName( NULL, pszPathBuf, nMaxLength ) == 0 ) + return FALSE; + else + return TRUE; + } +#else + if( CE_GetModuleFileNameA( NULL, pszPathBuf, nMaxLength ) == 0 ) + return FALSE; + else + return TRUE; +#endif +} + +#endif + +/************************************************************************/ +/* CPLGetExecPath() */ +/************************************************************************/ + +#if !defined(HAVE_IMPLEMENTATION) && defined(__linux) + +#include "cpl_multiproc.h" + +#define HAVE_IMPLEMENTATION 1 + +int CPLGetExecPath( char *pszPathBuf, int nMaxLength ) +{ + long nPID = getpid(); + CPLString osExeLink; + ssize_t nResultLen; + + osExeLink.Printf( "/proc/%ld/exe", nPID ); + nResultLen = readlink( osExeLink, pszPathBuf, nMaxLength ); + if( nResultLen >= 0 ) + pszPathBuf[nResultLen] = '\0'; + else + pszPathBuf[0] = '\0'; + + return nResultLen > 0; +} + +#endif + +/************************************************************************/ +/* CPLGetExecPath() */ +/************************************************************************/ + +/** + * Fetch path of executable. + * + * The path to the executable currently running is returned. This path + * includes the name of the executable. Currently this only works on + * win32 and linux platforms. The returned path is UTF-8 encoded. + * + * @param pszPathBuf the buffer into which the path is placed. + * @param nMaxLength the buffer size, MAX_PATH+1 is suggested. + * + * @return FALSE on failure or TRUE on success. + */ + +#ifndef HAVE_IMPLEMENTATION + +int CPLGetExecPath( CPL_UNUSED char *pszPathBuf, CPL_UNUSED int nMaxLength ) +{ + return FALSE; +} + +#endif + diff --git a/bazaar/plugin/gdal/port/cpl_google_oauth2.cpp b/bazaar/plugin/gdal/port/cpl_google_oauth2.cpp new file mode 100644 index 000000000..32ecaf6dd --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_google_oauth2.cpp @@ -0,0 +1,347 @@ +/****************************************************************************** + * $Id$ + * + * Project: Common Portability Library + * Purpose: Google OAuth2 Authentication Services + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_http.h" + +CPL_CVSID("$Id$"); + +/* ==================================================================== */ +/* Values related to OAuth2 authorization to use fusion */ +/* tables. Many of these values are related to the */ +/* gdalautotest@gmail.com account for GDAL managed by Even */ +/* Rouault and Frank Warmerdam. Some information about OAuth2 */ +/* as managed by that account can be found at the following url */ +/* when logged in as gdalautotest@gmail.com: */ +/* */ +/* https://code.google.com/apis/console/#project:265656308688:access*/ +/* */ +/* Applications wanting to use their own client id and secret */ +/* can set the following configuration options: */ +/* - GOA2_CLIENT_ID */ +/* - GOA2_CLIENT_SECRET */ +/* ==================================================================== */ +#define GDAL_CLIENT_ID "265656308688.apps.googleusercontent.com" +#define GDAL_CLIENT_SECRET "0IbTUDOYzaL6vnIdWTuQnvLz" + +#define GOOGLE_AUTH_URL "https://accounts.google.com/o/oauth2" + +/************************************************************************/ +/* ParseSimpleJson() */ +/* */ +/* Return a string list of name/value pairs extracted from a */ +/* JSON doc. The Google OAuth2 web service returns simple JSON */ +/* responses. The parsing as done currently is very fragile */ +/* and depends on JSON documents being in a very very simple */ +/* form. */ +/************************************************************************/ + +static CPLStringList ParseSimpleJson(const char *pszJson) + +{ +/* -------------------------------------------------------------------- */ +/* We are expecting simple documents like the following with no */ +/* heirarchy or complex structure. */ +/* -------------------------------------------------------------------- */ +/* + { + "access_token":"1/fFBGRNJru1FQd44AzqT3Zg", + "expires_in":3920, + "token_type":"Bearer" + } +*/ + + CPLStringList oWords( + CSLTokenizeString2(pszJson, " \n\t,:{}", CSLT_HONOURSTRINGS )); + CPLStringList oNameValue; + + for( int i=0; i < oWords.size(); i += 2 ) + { + oNameValue.SetNameValue( oWords[i], oWords[i+1] ); + } + + return oNameValue; +} + +/************************************************************************/ +/* GOA2GetAuthorizationURL() */ +/************************************************************************/ + +/** + * Return authorization url for a given scope. + * + * Returns the URL that a user should visit, and use for authentication + * in order to get an "auth token" indicating their willingness to use a + * service. + * + * Note that when the user visits this url they will be asked to login + * (using a google/gmail/etc) account, and to authorize use of the + * requested scope for the application "GDAL/OGR". Once they have done + * so, they will be presented with a lengthy string they should "enter + * into their application". This is the "auth token" to be passed to + * GOA2GetRefreshToken(). The "auth token" can only be used once. + * + * This function should never fail. + * + * @param pszScope the service being requested, not yet URL encoded, such as + * "https://www.googleapis.com/auth/fusiontables". + * + * @return the URL to visit - should be freed with CPLFree(). + */ + +char *GOA2GetAuthorizationURL(const char *pszScope) + +{ + CPLString osScope; + CPLString osURL; + + osScope.Seize(CPLEscapeString(pszScope, -1, CPLES_URL)); + osURL.Printf( "%s/auth?scope=%s&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&client_id=%s", + GOOGLE_AUTH_URL, + osScope.c_str(), + CPLGetConfigOption("GOA2_CLIENT_ID", GDAL_CLIENT_ID)); + return CPLStrdup(osURL); +} + +/************************************************************************/ +/* GOA2GetRefreshToken() */ +/************************************************************************/ + +/** + * Turn Auth Token into a Refresh Token. + * + * A one time "auth token" provided by the user is turned into a + * reusable "refresh token" using a google oauth2 web service. + * + * A CPLError will be reported if the translation fails for some reason. + * Common reasons include the auth token already having been used before, + * it not being appropriate for the passed scope and configured client api + * or http connection problems. NULL is returned on error. + * + * @param pszAuthToken the authorization token from the user. + * @param pszScope the scope for which it is valid. + * + * @return refresh token, to be freed with CPLFree(), null on failure. + */ + +char CPL_DLL *GOA2GetRefreshToken( const char *pszAuthToken, + const char *pszScope ) + +{ +/* -------------------------------------------------------------------- */ +/* Prepare request. */ +/* -------------------------------------------------------------------- */ + CPLString osItem; + CPLStringList oOptions; + + oOptions.AddString( + "HEADERS=Content-Type: application/x-www-form-urlencoded" ); + + osItem.Printf( + "POSTFIELDS=" + "code=%s" + "&client_id=%s" + "&client_secret=%s" + "&redirect_uri=urn:ietf:wg:oauth:2.0:oob" + "&grant_type=authorization_code", + pszAuthToken, + CPLGetConfigOption("GOA2_CLIENT_ID", GDAL_CLIENT_ID), + CPLGetConfigOption("GOA2_CLIENT_SECRET", GDAL_CLIENT_SECRET)); + oOptions.AddString(osItem); + +/* -------------------------------------------------------------------- */ +/* Submit request by HTTP. */ +/* -------------------------------------------------------------------- */ + CPLHTTPResult * psResult = + CPLHTTPFetch( GOOGLE_AUTH_URL "/token", oOptions); + + if (psResult == NULL) + return NULL; + +/* -------------------------------------------------------------------- */ +/* One common mistake is to try and reuse the auth token. */ +/* After the first use it will return invalid_grant. */ +/* -------------------------------------------------------------------- */ + if( psResult->pabyData != NULL + && strstr((const char *) psResult->pabyData,"invalid_grant") != NULL) + { + CPLString osURL; + osURL.Seize( GOA2GetAuthorizationURL(pszScope) ); + CPLError( CE_Failure, CPLE_AppDefined, + "Attempt to use a OAuth2 authorization code multiple times.\n" + "Request a fresh authorization token at\n%s.", + osURL.c_str() ); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + if (psResult->pabyData == NULL || + psResult->pszErrBuf != NULL) + { + if( psResult->pszErrBuf != NULL ) + CPLDebug( "GOA2", "%s", psResult->pszErrBuf ); + if( psResult->pabyData != NULL ) + CPLDebug( "GOA2", "%s", psResult->pabyData ); + + CPLError( CE_Failure, CPLE_AppDefined, + "Fetching OAuth2 access code from auth code failed."); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + CPLDebug( "GOA2", "Access Token Response:\n%s", + (const char *) psResult->pabyData ); + +/* -------------------------------------------------------------------- */ +/* This response is in JSON and will look something like: */ +/* -------------------------------------------------------------------- */ +/* +{ + "access_token" : "ya29.AHES6ZToqkIJkat5rIqMixR1b8PlWBACNO8OYbqqV-YF1Q13E2Kzjw", + "token_type" : "Bearer", + "expires_in" : 3600, + "refresh_token" : "1/eF88pciwq9Tp_rHEhuiIv9AS44Ufe4GOymGawTVPGYo" +} +*/ + CPLStringList oResponse = ParseSimpleJson( + (const char *) psResult->pabyData ); + CPLHTTPDestroyResult(psResult); + + CPLString osAccessToken = oResponse.FetchNameValueDef( "access_token", "" ); + CPLString osRefreshToken = oResponse.FetchNameValueDef( "refresh_token", "" ); + CPLDebug("GOA2", "Access Token : '%s'", osAccessToken.c_str()); + CPLDebug("GOA2", "Refresh Token : '%s'", osRefreshToken.c_str()); + + if( osRefreshToken.size() == 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify a refresh token in the OAuth2 response."); + return NULL; + } + else + { + // Currently we discard the access token and just return the refresh token + return CPLStrdup(osRefreshToken); + } +} + +/************************************************************************/ +/* GOA2GetAccessToken() */ +/************************************************************************/ + +/** + * Fetch access token using refresh token. + * + * The permanent refresh token is used to fetch a temporary (usually one + * hour) access token using Google OAuth2 web services. + * + * A CPLError will be reported if the request fails for some reason. + * Common reasons include the refresh token having been revoked by the + * user or http connection problems. + * + * @param pszRefreshToken the refresh token from GOA2GetRefreshToken(). + * @param pszScope the scope for which it is valid. + * + * @return access token, to be freed with CPLFree(), null on failure. + */ + +char *GOA2GetAccessToken( const char *pszRefreshToken, + CPL_UNUSED const char *pszScope ) +{ +/* -------------------------------------------------------------------- */ +/* Prepare request. */ +/* -------------------------------------------------------------------- */ + CPLString osItem; + CPLStringList oOptions; + + oOptions.AddString( + "HEADERS=Content-Type: application/x-www-form-urlencoded" ); + + osItem.Printf( + "POSTFIELDS=" + "refresh_token=%s" + "&client_id=%s" + "&client_secret=%s" + "&grant_type=refresh_token", + pszRefreshToken, + CPLGetConfigOption("GOA2_CLIENT_ID", GDAL_CLIENT_ID), + CPLGetConfigOption("GOA2_CLIENT_SECRET", GDAL_CLIENT_SECRET)); + oOptions.AddString(osItem); + +/* -------------------------------------------------------------------- */ +/* Submit request by HTTP. */ +/* -------------------------------------------------------------------- */ + CPLHTTPResult *psResult = CPLHTTPFetch(GOOGLE_AUTH_URL "/token", oOptions); + + if (psResult == NULL) + return NULL; + + if (psResult->pabyData == NULL || + psResult->pszErrBuf != NULL) + { + if( psResult->pszErrBuf != NULL ) + CPLDebug( "GFT", "%s", psResult->pszErrBuf ); + if( psResult->pabyData != NULL ) + CPLDebug( "GFT", "%s", psResult->pabyData ); + + CPLError( CE_Failure, CPLE_AppDefined, + "Fetching OAuth2 access code from auth code failed."); + CPLHTTPDestroyResult(psResult); + return NULL; + } + + CPLDebug( "GOA2", "Refresh Token Response:\n%s", + (const char *) psResult->pabyData ); + +/* -------------------------------------------------------------------- */ +/* This response is in JSON and will look something like: */ +/* -------------------------------------------------------------------- */ +/* +{ +"access_token":"1/fFBGRNJru1FQd44AzqT3Zg", +"expires_in":3920, +"token_type":"Bearer" +} +*/ + CPLStringList oResponse = ParseSimpleJson( + (const char *) psResult->pabyData ); + CPLHTTPDestroyResult(psResult); + + CPLString osAccessToken = oResponse.FetchNameValueDef( "access_token", "" ); + + CPLDebug("GOA2", "Access Token : '%s'", osAccessToken.c_str()); + + if (osAccessToken.size() == 0) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to identify an access token in the OAuth2 response."); + return NULL; + } + else + return CPLStrdup(osAccessToken); +} diff --git a/bazaar/plugin/gdal/port/cpl_hash_set.cpp b/bazaar/plugin/gdal/port/cpl_hash_set.cpp new file mode 100644 index 000000000..01c1804ef --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_hash_set.cpp @@ -0,0 +1,455 @@ +/********************************************************************** + * $Id: cpl_hash_set.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Name: cpl_hash_set.cpp + * Project: CPL - Common Portability Library + * Purpose: Hash set functions. + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2008-2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_hash_set.h" +#include "cpl_list.h" + +struct _CPLHashSet +{ + CPLHashSetHashFunc fnHashFunc; + CPLHashSetEqualFunc fnEqualFunc; + CPLHashSetFreeEltFunc fnFreeEltFunc; + CPLList** tabList; + int nSize; + int nIndiceAllocatedSize; + int nAllocatedSize; +#ifdef HASH_DEBUG + int nCollisions; +#endif +}; + +static const int anPrimes[] = +{ 53, 97, 193, 389, 769, 1543, 3079, 6151, + 12289, 24593, 49157, 98317, 196613, 393241, + 786433, 1572869, 3145739, 6291469, 12582917, + 25165843, 50331653, 100663319, 201326611, + 402653189, 805306457, 1610612741 }; + +/************************************************************************/ +/* CPLHashSetNew() */ +/************************************************************************/ + +/** + * Creates a new hash set + * + * The hash function must return a hash value for the elements to insert. + * If fnHashFunc is NULL, CPLHashSetHashPointer will be used. + * + * The equal function must return if two elements are equal. + * If fnEqualFunc is NULL, CPLHashSetEqualPointer will be used. + * + * The free function is used to free elements inserted in the hash set, + * when the hash set is destroyed, when elements are removed or replaced. + * If fnFreeEltFunc is NULL, elements inserted into the hash set will not be freed. + * + * @param fnHashFunc hash function. May be NULL. + * @param fnEqualFunc equal function. May be NULL. + * @param fnFreeEltFunc element free function. May be NULL. + * + * @return a new hash set + */ + +CPLHashSet* CPLHashSetNew(CPLHashSetHashFunc fnHashFunc, + CPLHashSetEqualFunc fnEqualFunc, + CPLHashSetFreeEltFunc fnFreeEltFunc) +{ + CPLHashSet* set = (CPLHashSet*) CPLMalloc(sizeof(CPLHashSet)); + set->fnHashFunc = (fnHashFunc) ? fnHashFunc : CPLHashSetHashPointer; + set->fnEqualFunc = (fnEqualFunc) ? fnEqualFunc : CPLHashSetEqualPointer; + set->fnFreeEltFunc = fnFreeEltFunc; + set->nSize = 0; + set->tabList = (CPLList**) CPLCalloc(sizeof(CPLList*), 53); + set->nIndiceAllocatedSize = 0; + set->nAllocatedSize = 53; +#ifdef HASH_DEBUG + set->nCollisions = 0; +#endif + return set; +} + + +/************************************************************************/ +/* CPLHashSetSize() */ +/************************************************************************/ + +/** + * Returns the number of elements inserted in the hash set + * + * Note: this is not the internal size of the hash set + * + * @param set the hash set + * + * @return the number of elements in the hash set + */ + +int CPLHashSetSize(const CPLHashSet* set) +{ + CPLAssert(set != NULL); + return set->nSize; +} + +/************************************************************************/ +/* CPLHashSetDestroy() */ +/************************************************************************/ + +/** + * Destroys an allocated hash set. + * + * This function also frees the elements if a free function was + * provided at the creation of the hash set. + * + * @param set the hash set + */ + +void CPLHashSetDestroy(CPLHashSet* set) +{ + CPLAssert(set != NULL); + for(int i=0;inAllocatedSize;i++) + { + if (set->fnFreeEltFunc) + { + CPLList* cur = set->tabList[i]; + while(cur) + { + set->fnFreeEltFunc(cur->pData); + cur = cur->psNext; + } + } + CPLListDestroy(set->tabList[i]); + } + CPLFree(set->tabList); + CPLFree(set); +} + +/************************************************************************/ +/* CPLHashSetForeach() */ +/************************************************************************/ + + +/** + * Walk through the hash set and runs the provided function on all the + * elements + * + * This function is provided the user_data argument of CPLHashSetForeach. + * It must return TRUE to go on the walk through the hash set, or FALSE to + * make it stop. + * + * Note : the structure of the hash set must *NOT* be modified during the + * walk. + * + * @param set the hash set. + * @param fnIterFunc the function called on each element. + * @param user_data the user data provided to the function. + */ + +void CPLHashSetForeach(CPLHashSet* set, + CPLHashSetIterEltFunc fnIterFunc, + void* user_data) +{ + CPLAssert(set != NULL); + if (!fnIterFunc) return; + + for(int i=0;inAllocatedSize;i++) + { + CPLList* cur = set->tabList[i]; + while(cur) + { + if (fnIterFunc(cur->pData, user_data) == FALSE) + return; + + cur = cur->psNext; + } + } +} + +/************************************************************************/ +/* CPLHashSetRehash() */ +/************************************************************************/ + +static void CPLHashSetRehash(CPLHashSet* set) +{ + int nNewAllocatedSize = anPrimes[set->nIndiceAllocatedSize]; + CPLList** newTabList = (CPLList**) CPLCalloc(sizeof(CPLList*), nNewAllocatedSize); +#ifdef HASH_DEBUG + CPLDebug("CPLHASH", "hashSet=%p, nSize=%d, nCollisions=%d, fCollisionRate=%.02f", + set, set->nSize, set->nCollisions, set->nCollisions * 100.0 / set->nSize); + set->nCollisions = 0; +#endif + for(int i=0;inAllocatedSize;i++) + { + CPLList* cur = set->tabList[i]; + while(cur) + { + unsigned long nNewHashVal = set->fnHashFunc(cur->pData) % nNewAllocatedSize; +#ifdef HASH_DEBUG + if (newTabList[nNewHashVal]) + set->nCollisions ++; +#endif + newTabList[nNewHashVal] = CPLListInsert(newTabList[nNewHashVal], cur->pData, 0); + cur = cur->psNext; + } + CPLListDestroy(set->tabList[i]); + } + CPLFree(set->tabList); + set->tabList = newTabList; + set->nAllocatedSize = nNewAllocatedSize; +} + + +/************************************************************************/ +/* CPLHashSetFindPtr() */ +/************************************************************************/ + +static void** CPLHashSetFindPtr(CPLHashSet* set, const void* elt) +{ + unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize; + CPLList* cur = set->tabList[nHashVal]; + while(cur) + { + if (set->fnEqualFunc(cur->pData, elt)) + return &cur->pData; + cur = cur->psNext; + } + return NULL; +} + +/************************************************************************/ +/* CPLHashSetInsert() */ +/************************************************************************/ + +/** + * Inserts an element into a hash set. + * + * If the element was already inserted in the hash set, the previous + * element is replaced by the new element. If a free function was provided, + * it is used to free the previously inserted element + * + * @param set the hash set + * @param elt the new element to insert in the hash set + * + * @return TRUE if the element was not already in the hash set + */ + +int CPLHashSetInsert(CPLHashSet* set, void* elt) +{ + CPLAssert(set != NULL); + void** pElt = CPLHashSetFindPtr(set, elt); + if (pElt) + { + if (set->fnFreeEltFunc) + set->fnFreeEltFunc(*pElt); + + *pElt = elt; + return FALSE; + } + + if (set->nSize >= 2 * set->nAllocatedSize / 3) + { + set->nIndiceAllocatedSize++; + CPLHashSetRehash(set); + } + + unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize; +#ifdef HASH_DEBUG + if (set->tabList[nHashVal]) + set->nCollisions ++; +#endif + set->tabList[nHashVal] = CPLListInsert(set->tabList[nHashVal], (void*) elt, 0); + set->nSize++; + + return TRUE; +} + +/************************************************************************/ +/* CPLHashSetLookup() */ +/************************************************************************/ + +/** + * Returns the element found in the hash set corresponding to the element to look up + * The element must not be modified. + * + * @param set the hash set + * @param elt the element to look up in the hash set + * + * @return the element found in the hash set or NULL + */ + +void* CPLHashSetLookup(CPLHashSet* set, const void* elt) +{ + CPLAssert(set != NULL); + void** pElt = CPLHashSetFindPtr(set, elt); + if (pElt) + return *pElt; + else + return NULL; +} + +/************************************************************************/ +/* CPLHashSetRemove() */ +/************************************************************************/ + +/** + * Removes an element from a hash set + * + * @param set the hash set + * @param elt the new element to remove from the hash set + * + * @return TRUE if the element was in the hash set + */ + +int CPLHashSetRemove(CPLHashSet* set, const void* elt) +{ + CPLAssert(set != NULL); + if (set->nIndiceAllocatedSize > 0 && set->nSize <= set->nAllocatedSize / 2) + { + set->nIndiceAllocatedSize--; + CPLHashSetRehash(set); + } + + int nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize; + CPLList* cur = set->tabList[nHashVal]; + CPLList* prev = NULL; + while(cur) + { + if (set->fnEqualFunc(cur->pData, elt)) + { + if (prev) + prev->psNext = cur->psNext; + else + set->tabList[nHashVal] = cur->psNext; + + if (set->fnFreeEltFunc) + set->fnFreeEltFunc(cur->pData); + + CPLFree(cur); +#ifdef HASH_DEBUG + if (set->tabList[nHashVal]) + set->nCollisions --; +#endif + + set->nSize--; + return TRUE; + } + prev = cur; + cur = cur->psNext; + } + return FALSE; +} + + +/************************************************************************/ +/* CPLHashSetHashPointer() */ +/************************************************************************/ + +/** + * Hash function for an arbitrary pointer + * + * @param elt the arbitrary pointer to hash + * + * @return the hash value of the pointer + */ + +unsigned long CPLHashSetHashPointer(const void* elt) +{ + return (unsigned long)(GUIntBig) elt; +} + +/************************************************************************/ +/* CPLHashSetEqualPointer() */ +/************************************************************************/ + +/** + * Equality function for arbitrary pointers + * + * @param elt1 the first arbitrary pointer to compare + * @param elt2 the second arbitrary pointer to compare + * + * @return TRUE if the pointers are equal + */ + +int CPLHashSetEqualPointer(const void* elt1, const void* elt2) +{ + return elt1 == elt2; +} + +/************************************************************************/ +/* CPLHashSetHashStr() */ +/************************************************************************/ + +/** + * Hash function for a zero-terminated string + * + * @param elt the string to hash. May be NULL. + * + * @return the hash value of the string + */ + +unsigned long CPLHashSetHashStr(const void *elt) +{ + unsigned char* pszStr = (unsigned char*)elt; + unsigned long hash = 0; + int c; + + if (pszStr == NULL) + return 0; + + while ((c = *pszStr++) != '\0') + hash = c + (hash << 6) + (hash << 16) - hash; + + return hash; +} + +/************************************************************************/ +/* CPLHashSetEqualStr() */ +/************************************************************************/ + +/** + * Equality function for strings + * + * @param elt1 the first string to compare. May be NULL. + * @param elt2 the second string to compare. May be NULL. + * + * @return TRUE if the strings are equal + */ + +int CPLHashSetEqualStr(const void* elt1, const void* elt2) +{ + const char* pszStr1 = (const char*)elt1; + const char* pszStr2 = (const char*)elt2; + if (pszStr1 == NULL && pszStr2 != NULL) + return FALSE; + else if (pszStr1 != NULL && pszStr2 == NULL) + return FALSE; + else if (pszStr1 == NULL && pszStr2 == NULL) + return TRUE; + else + return strcmp(pszStr1, pszStr2) == 0; +} diff --git a/bazaar/plugin/gdal/port/cpl_hash_set.h b/bazaar/plugin/gdal/port/cpl_hash_set.h new file mode 100644 index 000000000..fad62fe18 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_hash_set.h @@ -0,0 +1,92 @@ +/********************************************************************** + * $Id: cpl_hash_set.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Name: cpl_hash_set.h + * Project: CPL - Common Portability Library + * Purpose: Hash set functions. + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2008-2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_HASH_SET_H_INCLUDED +#define _CPL_HASH_SET_H_INCLUDED + +#include "cpl_port.h" + +/** + * \file cpl_hash_set.h + * + * Hash set implementation. + * + * An hash set is a data structure that holds elements that are unique + * according to a comparison function. Operations on the hash set, such as + * insertion, removal or lookup, are supposed to be fast if an efficient + * "hash" function is provided. + */ + +CPL_C_START + +/* Types */ + +typedef struct _CPLHashSet CPLHashSet; + +typedef unsigned long (*CPLHashSetHashFunc)(const void* elt); + +typedef int (*CPLHashSetEqualFunc)(const void* elt1, const void* elt2); + +typedef void (*CPLHashSetFreeEltFunc)(void* elt); + +typedef int (*CPLHashSetIterEltFunc)(void* elt, void* user_data); + +/* Functions */ + +CPLHashSet CPL_DLL * CPLHashSetNew(CPLHashSetHashFunc fnHashFunc, + CPLHashSetEqualFunc fnEqualFunc, + CPLHashSetFreeEltFunc fnFreeEltFunc); + +void CPL_DLL CPLHashSetDestroy(CPLHashSet* set); + +int CPL_DLL CPLHashSetSize(const CPLHashSet* set); + +void CPL_DLL CPLHashSetForeach(CPLHashSet* set, + CPLHashSetIterEltFunc fnIterFunc, + void* user_data); + +int CPL_DLL CPLHashSetInsert(CPLHashSet* set, void* elt); + +void CPL_DLL * CPLHashSetLookup(CPLHashSet* set, const void* elt); + +int CPL_DLL CPLHashSetRemove(CPLHashSet* set, const void* elt); + +unsigned long CPL_DLL CPLHashSetHashPointer(const void* elt); + +int CPL_DLL CPLHashSetEqualPointer(const void* elt1, const void* elt2); + +unsigned long CPL_DLL CPLHashSetHashStr(const void * pszStr); + +int CPL_DLL CPLHashSetEqualStr(const void* pszStr1, const void* pszStr2); + +CPL_C_END + +#endif /* _CPL_HASH_SET_H_INCLUDED */ + diff --git a/bazaar/plugin/gdal/port/cpl_http.cpp b/bazaar/plugin/gdal/port/cpl_http.cpp new file mode 100644 index 000000000..13dadf6b5 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_http.cpp @@ -0,0 +1,896 @@ +/****************************************************************************** + * $Id: cpl_http.cpp 29152 2015-05-04 11:45:44Z rouault $ + * + * Project: libcurl based HTTP client + * Purpose: libcurl based HTTP client + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2006, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include "cpl_http.h" +#include "cpl_multiproc.h" + +#ifdef HAVE_CURL +# include + +void CPLHTTPSetOptions(CURL *http_handle, char** papszOptions); + +/* CURLINFO_RESPONSE_CODE was known as CURLINFO_HTTP_CODE in libcurl 7.10.7 and earlier */ +#if LIBCURL_VERSION_NUM < 0x070a07 +#define CURLINFO_RESPONSE_CODE CURLINFO_HTTP_CODE +#endif + +#endif + +CPL_CVSID("$Id: cpl_http.cpp 29152 2015-05-04 11:45:44Z rouault $"); + +// list of named persistent http sessions + +#ifdef HAVE_CURL +static std::map* poSessionMap = NULL; +static CPLMutex *hSessionMapMutex = NULL; +#endif + +/************************************************************************/ +/* CPLWriteFct() */ +/* */ +/* Append incoming text to our collection buffer, reallocating */ +/* it larger as needed. */ +/************************************************************************/ + +#ifdef HAVE_CURL +static size_t +CPLWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo) + +{ + CPLHTTPResult *psResult = (CPLHTTPResult *) reqInfo; + int nNewSize; + + nNewSize = psResult->nDataLen + nmemb*size + 1; + if( nNewSize > psResult->nDataAlloc ) + { + psResult->nDataAlloc = (int) (nNewSize * 1.25 + 100); + GByte* pabyNewData = (GByte *) VSIRealloc(psResult->pabyData, + psResult->nDataAlloc); + if( pabyNewData == NULL ) + { + VSIFree(psResult->pabyData); + psResult->pabyData = NULL; + psResult->pszErrBuf = CPLStrdup(CPLString().Printf("Out of memory allocating %d bytes for HTTP data buffer.", psResult->nDataAlloc)); + psResult->nDataAlloc = psResult->nDataLen = 0; + + return 0; + } + psResult->pabyData = pabyNewData; + } + + memcpy( psResult->pabyData + psResult->nDataLen, buffer, + nmemb * size ); + + psResult->nDataLen += nmemb * size; + psResult->pabyData[psResult->nDataLen] = 0; + + return nmemb; +} + +/************************************************************************/ +/* CPLHdrWriteFct() */ +/************************************************************************/ +static size_t CPLHdrWriteFct(void *buffer, size_t size, size_t nmemb, void *reqInfo) +{ + CPLHTTPResult *psResult = (CPLHTTPResult *) reqInfo; + // copy the buffer to a char* and initialize with zeros (zero terminate as well) + char* pszHdr = (char*)CPLCalloc(nmemb + 1, size); + CPLPrintString(pszHdr, (char *)buffer, nmemb * size); + char *pszKey = NULL; + const char *pszValue = CPLParseNameValue(pszHdr, &pszKey ); + psResult->papszHeaders = CSLSetNameValue(psResult->papszHeaders, pszKey, pszValue); + CPLFree(pszHdr); + CPLFree(pszKey); + return nmemb; +} + +#endif /* def HAVE_CURL */ + +/************************************************************************/ +/* CPLHTTPFetch() */ +/************************************************************************/ + +/** + * \brief Fetch a document from an url and return in a string. + * + * @param pszURL valid URL recognized by underlying download library (libcurl) + * @param papszOptions option list as a NULL-terminated array of strings. May be NULL. + * The following options are handled : + *
      + *
    • TIMEOUT=val, where val is in seconds
    • + *
    • HEADERS=val, where val is an extra header to use when getting a web page. + * For example "Accept: application/x-ogcwkt" + *
    • HTTPAUTH=[BASIC/NTLM/GSSNEGOTIATE/ANY] to specify an authentication scheme to use. + *
    • USERPWD=userid:password to specify a user and password for authentication + *
    • POSTFIELDS=val, where val is a nul-terminated string to be passed to the server + * with a POST request. + *
    • PROXY=val, to make requests go through a proxy server, where val is of the + * form proxy.server.com:port_number + *
    • PROXYUSERPWD=val, where val is of the form username:password + *
    • PROXYAUTH=[BASIC/NTLM/DIGEST/ANY] to specify an proxy authentication scheme to use. + *
    • NETRC=[YES/NO] to enable or disable use of $HOME/.netrc, default YES. + *
    • CUSTOMREQUEST=val, where val is GET, PUT, POST, DELETE, etc.. (GDAL >= 1.9.0) + *
    • COOKIE=val, where val is formatted as COOKIE1=VALUE1; COOKIE2=VALUE2; ... + *
    • MAX_RETRY=val, where val is the maximum number of retry attempts if a 503 or + * 504 HTTP error occurs. Default is 0. (GDAL >= 2.0) + *
    • RETRY_DELAY=val, where val is the number of seconds between retry attempts. + * Default is 30. (GDAL >= 2.0) + *
    + * + * Alternatively, if not defined in the papszOptions arguments, the PROXY, + * PROXYUSERPWD, PROXYAUTH, NETRC, MAX_RETRY and RETRY_DELAY values are searched in the configuration + * options named GDAL_HTTP_PROXY, GDAL_HTTP_PROXYUSERPWD, GDAL_PROXY_AUTH, + * GDAL_HTTP_NETRC, GDAL_HTTP_MAX_RETRY and GDAL_HTTP_RETRY_DELAY. + * + * @return a CPLHTTPResult* structure that must be freed by + * CPLHTTPDestroyResult(), or NULL if libcurl support is disabled + */ +CPLHTTPResult *CPLHTTPFetch( const char *pszURL, char **papszOptions ) + +{ + if( strncmp(pszURL, "/vsimem/", strlen("/vsimem/")) == 0 && + /* Disabled by default for potential security issues */ + CSLTestBoolean(CPLGetConfigOption("CPL_CURL_ENABLE_VSIMEM", "FALSE")) ) + { + CPLString osURL(pszURL); + const char* pszPost = CSLFetchNameValue( papszOptions, "POSTFIELDS" ); + if( pszPost != NULL ) /* Hack: we append post content to filename */ + { + osURL += "&POSTFIELDS="; + osURL += pszPost; + } + vsi_l_offset nLength = 0; + CPLHTTPResult* psResult = (CPLHTTPResult* )CPLCalloc(1, sizeof(CPLHTTPResult)); + GByte* pabyData = VSIGetMemFileBuffer( osURL, &nLength, FALSE ); + if( pabyData == NULL ) + { + CPLDebug("HTTP", "Cannot find %s", osURL.c_str()); + psResult->nStatus = 1; + psResult->pszErrBuf = CPLStrdup(CPLSPrintf("HTTP error code : %d", 404)); + CPLError( CE_Failure, CPLE_AppDefined, "%s", psResult->pszErrBuf ); + } + else if( nLength != 0 ) + { + psResult->nDataLen = (size_t)nLength; + psResult->pabyData = (GByte*) CPLMalloc((size_t)nLength + 1); + memcpy(psResult->pabyData, pabyData, (size_t)nLength); + psResult->pabyData[(size_t)nLength] = 0; + } + + if( psResult->pabyData != NULL && + strncmp((const char*)psResult->pabyData, "Content-Type: ", + strlen("Content-Type: ")) == 0 ) + { + const char* pszContentType = (const char*)psResult->pabyData + strlen("Content-type: "); + const char* pszEOL = strchr(pszContentType, '\r'); + if( pszEOL ) + pszEOL = strchr(pszContentType, '\n'); + if( pszEOL ) + { + int nLength = pszEOL - pszContentType; + psResult->pszContentType = (char*)CPLMalloc(nLength + 1); + memcpy(psResult->pszContentType, pszContentType, nLength); + psResult->pszContentType[nLength] = 0; + } + } + + return psResult; + } + +#ifndef HAVE_CURL + (void) papszOptions; + (void) pszURL; + + CPLError( CE_Failure, CPLE_NotSupported, + "GDAL/OGR not compiled with libcurl support, remote requests not supported." ); + return NULL; +#else + +/* -------------------------------------------------------------------- */ +/* Are we using a persistent named session? If so, search for */ +/* or create it. */ +/* */ +/* Currently this code does not attempt to protect against */ +/* multiple threads asking for the same named session. If that */ +/* occurs it will be in use in multiple threads at once which */ +/* might have bad consequences depending on what guarantees */ +/* libcurl gives - which I have not investigated. */ +/* -------------------------------------------------------------------- */ + CURL *http_handle = NULL; + + const char *pszPersistent = CSLFetchNameValue( papszOptions, "PERSISTENT" ); + const char *pszClosePersistent = CSLFetchNameValue( papszOptions, "CLOSE_PERSISTENT" ); + if (pszPersistent) + { + CPLString osSessionName = pszPersistent; + CPLMutexHolder oHolder( &hSessionMapMutex ); + + if( poSessionMap == NULL ) + poSessionMap = new std::map; + if( poSessionMap->count( osSessionName ) == 0 ) + { + (*poSessionMap)[osSessionName] = curl_easy_init(); + CPLDebug( "HTTP", "Establish persistent session named '%s'.", + osSessionName.c_str() ); + } + + http_handle = (*poSessionMap)[osSessionName]; + } +/* -------------------------------------------------------------------- */ +/* Are we requested to close a persistent named session? */ +/* -------------------------------------------------------------------- */ + else if (pszClosePersistent) + { + CPLString osSessionName = pszClosePersistent; + CPLMutexHolder oHolder( &hSessionMapMutex ); + + if( poSessionMap ) + { + std::map::iterator oIter = poSessionMap->find( osSessionName ); + if( oIter != poSessionMap->end() ) + { + curl_easy_cleanup(oIter->second); + poSessionMap->erase(oIter); + if( poSessionMap->size() == 0 ) + { + delete poSessionMap; + poSessionMap = NULL; + } + CPLDebug( "HTTP", "Ended persistent session named '%s'.", + osSessionName.c_str() ); + } + else + { + CPLDebug( "HTTP", "Could not find persistent session named '%s'.", + osSessionName.c_str() ); + } + } + + return NULL; + } + else + http_handle = curl_easy_init(); + +/* -------------------------------------------------------------------- */ +/* Setup the request. */ +/* -------------------------------------------------------------------- */ + char szCurlErrBuf[CURL_ERROR_SIZE+1]; + CPLHTTPResult *psResult; + struct curl_slist *headers=NULL; + + const char* pszArobase = strchr(pszURL, '@'); + const char* pszSlash = strchr(pszURL, '/'); + const char* pszColon = (pszSlash) ? strchr(pszSlash, ':') : NULL; + if (pszArobase != NULL && pszColon != NULL && pszArobase - pszColon > 0) + { + /* http://user:password@www.example.com */ + char* pszSanitizedURL = CPLStrdup(pszURL); + pszSanitizedURL[pszColon-pszURL] = 0; + CPLDebug( "HTTP", "Fetch(%s:#password#%s)", pszSanitizedURL, pszArobase ); + CPLFree(pszSanitizedURL); + } + else + { + CPLDebug( "HTTP", "Fetch(%s)", pszURL ); + } + + psResult = (CPLHTTPResult *) CPLCalloc(1,sizeof(CPLHTTPResult)); + + curl_easy_setopt(http_handle, CURLOPT_URL, pszURL ); + + CPLHTTPSetOptions(http_handle, papszOptions); + + // turn off SSL verification, accept all servers with ssl + curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYPEER, FALSE); + + /* Set Headers.*/ + const char *pszHeaders = CSLFetchNameValue( papszOptions, "HEADERS" ); + if( pszHeaders != NULL ) { + CPLDebug ("HTTP", "These HTTP headers were set: %s", pszHeaders); + headers = curl_slist_append(headers, pszHeaders); + curl_easy_setopt(http_handle, CURLOPT_HTTPHEADER, headers); + } + + // are we making a head request + const char* pszNoBody = NULL; + if ((pszNoBody = CSLFetchNameValue( papszOptions, "NO_BODY" )) != NULL) + { + if (CSLTestBoolean(pszNoBody)) + { + CPLDebug ("HTTP", "HEAD Request: %s", pszURL); + curl_easy_setopt(http_handle, CURLOPT_NOBODY, 1L); + } + } + + // capture response headers + curl_easy_setopt(http_handle, CURLOPT_HEADERDATA, psResult); + curl_easy_setopt(http_handle, CURLOPT_HEADERFUNCTION, CPLHdrWriteFct); + + curl_easy_setopt(http_handle, CURLOPT_WRITEDATA, psResult ); + curl_easy_setopt(http_handle, CURLOPT_WRITEFUNCTION, CPLWriteFct ); + + szCurlErrBuf[0] = '\0'; + + curl_easy_setopt(http_handle, CURLOPT_ERRORBUFFER, szCurlErrBuf ); + + static int bHasCheckVersion = FALSE; + static int bSupportGZip = FALSE; + if (!bHasCheckVersion) + { + bSupportGZip = strstr(curl_version(), "zlib/") != NULL; + bHasCheckVersion = TRUE; + } + int bGZipRequested = FALSE; + if (bSupportGZip && CSLTestBoolean(CPLGetConfigOption("CPL_CURL_GZIP", "YES"))) + { + bGZipRequested = TRUE; + curl_easy_setopt(http_handle, CURLOPT_ENCODING, "gzip"); + } + +/* -------------------------------------------------------------------- */ +/* If 502, 503 or 504 status code retry this HTTP call until max */ +/* retry has been rearched */ +/* -------------------------------------------------------------------- */ + const char *pszRetryDelay = CSLFetchNameValue( papszOptions, "RETRY_DELAY" ); + if( pszRetryDelay == NULL ) + pszRetryDelay = CPLGetConfigOption( "GDAL_HTTP_RETRY_DELAY", "30" ); + const char *pszMaxRetries = CSLFetchNameValue( papszOptions, "MAX_RETRY" ); + if( pszMaxRetries == NULL ) + pszMaxRetries = CPLGetConfigOption( "GDAL_HTTP_MAX_RETRY", "0" ); + int nRetryDelaySecs = atoi(pszRetryDelay); + int nMaxRetries = atoi(pszMaxRetries); + int nRetryCount = 0; + bool bRequestRetry; + + do + { + bRequestRetry = FALSE; + +/* -------------------------------------------------------------------- */ +/* Execute the request, waiting for results. */ +/* -------------------------------------------------------------------- */ + psResult->nStatus = (int) curl_easy_perform( http_handle ); + +/* -------------------------------------------------------------------- */ +/* Fetch content-type if possible. */ +/* -------------------------------------------------------------------- */ + psResult->pszContentType = NULL; + curl_easy_getinfo( http_handle, CURLINFO_CONTENT_TYPE, + &(psResult->pszContentType) ); + if( psResult->pszContentType != NULL ) + psResult->pszContentType = CPLStrdup(psResult->pszContentType); + +/* -------------------------------------------------------------------- */ +/* Have we encountered some sort of error? */ +/* -------------------------------------------------------------------- */ + if( strlen(szCurlErrBuf) > 0 ) + { + int bSkipError = FALSE; + + /* Some servers such as http://115.113.193.14/cgi-bin/world/qgis_mapserv.fcgi?VERSION=1.1.1&SERVICE=WMS&REQUEST=GetCapabilities */ + /* invalidly return Content-Length as the uncompressed size, with makes curl to wait for more data */ + /* and time-out finally. If we got the expected data size, then we don't emit an error */ + /* but turn off GZip requests */ + if (bGZipRequested && + strstr(szCurlErrBuf, "transfer closed with") && + strstr(szCurlErrBuf, "bytes remaining to read")) + { + const char* pszContentLength = + CSLFetchNameValue(psResult->papszHeaders, "Content-Length"); + if (pszContentLength && psResult->nDataLen != 0 && + atoi(pszContentLength) == psResult->nDataLen) + { + const char* pszCurlGZIPOption = CPLGetConfigOption("CPL_CURL_GZIP", NULL); + if (pszCurlGZIPOption == NULL) + { + CPLSetConfigOption("CPL_CURL_GZIP", "NO"); + CPLDebug("HTTP", "Disabling CPL_CURL_GZIP, because %s doesn't support it properly", + pszURL); + } + psResult->nStatus = 0; + bSkipError = TRUE; + } + } + if (!bSkipError) + { + psResult->pszErrBuf = CPLStrdup(szCurlErrBuf); + CPLError( CE_Failure, CPLE_AppDefined, + "%s", szCurlErrBuf ); + } + } + else + { + /* HTTP errors do not trigger curl errors. But we need to */ + /* propagate them to the caller though */ + long response_code = 0; + curl_easy_getinfo(http_handle, CURLINFO_RESPONSE_CODE, &response_code); + + if (response_code >= 400 && response_code < 600) + { + /* If HTTP 502, 503 or 504 gateway timeout error retry after a pause */ + if ((response_code >= 502 && response_code <= 504) && nRetryCount < nMaxRetries) + { + CPLError(CE_Warning, CPLE_AppDefined, + "HTTP error code: %d - %s. Retrying again in %d secs", + (int)response_code, pszURL, nRetryDelaySecs); + CPLSleep(nRetryDelaySecs); + nRetryCount++; + + CPLFree(psResult->pszContentType); + psResult->pszContentType = NULL; + CSLDestroy(psResult->papszHeaders); + psResult->papszHeaders = NULL; + CPLFree(psResult->pabyData); + psResult->pabyData = NULL; + psResult->nDataLen = 0; + psResult->nDataAlloc = 0; + + bRequestRetry = TRUE; + } + else + { + psResult->pszErrBuf = CPLStrdup(CPLSPrintf("HTTP error code : %d", (int)response_code)); + CPLError( CE_Failure, CPLE_AppDefined, "%s", psResult->pszErrBuf ); + } + } + } + } + while (bRequestRetry); + + if (!pszPersistent) + curl_easy_cleanup( http_handle ); + + curl_slist_free_all(headers); + + return psResult; +#endif /* def HAVE_CURL */ +} + +#ifdef HAVE_CURL +/************************************************************************/ +/* CPLHTTPSetOptions() */ +/************************************************************************/ + +void CPLHTTPSetOptions(CURL *http_handle, char** papszOptions) +{ + if (CSLTestBoolean(CPLGetConfigOption("CPL_CURL_VERBOSE", "NO"))) + curl_easy_setopt(http_handle, CURLOPT_VERBOSE, 1); + + const char *pszHttpVersion = CSLFetchNameValue( papszOptions, "HTTP_VERSION"); + if( pszHttpVersion && strcmp(pszHttpVersion, "1.0") == 0 ) + curl_easy_setopt(http_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 ); + + /* Support control over HTTPAUTH */ + const char *pszHttpAuth = CSLFetchNameValue( papszOptions, "HTTPAUTH" ); + if( pszHttpAuth == NULL ) + pszHttpAuth = CPLGetConfigOption( "GDAL_HTTP_AUTH", NULL ); + if( pszHttpAuth == NULL ) + /* do nothing */; + + /* CURLOPT_HTTPAUTH is defined in curl 7.11.0 or newer */ +#if LIBCURL_VERSION_NUM >= 0x70B00 + else if( EQUAL(pszHttpAuth,"BASIC") ) + curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC ); + else if( EQUAL(pszHttpAuth,"NTLM") ) + curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_NTLM ); + else if( EQUAL(pszHttpAuth,"ANY") ) + curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY ); +#ifdef CURLAUTH_GSSNEGOTIATE + else if( EQUAL(pszHttpAuth,"NEGOTIATE") ) + curl_easy_setopt(http_handle, CURLOPT_HTTPAUTH, CURLAUTH_GSSNEGOTIATE ); +#endif + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unsupported HTTPAUTH value '%s', ignored.", + pszHttpAuth ); + } +#else + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "HTTPAUTH option needs curl >= 7.11.0" ); + } +#endif + + /* Support use of .netrc - default enabled */ + const char *pszHttpNetrc = CSLFetchNameValue( papszOptions, "NETRC" ); + if( pszHttpNetrc == NULL ) + pszHttpNetrc = CPLGetConfigOption( "GDAL_HTTP_NETRC", "YES" ); + if( pszHttpNetrc == NULL || CSLTestBoolean(pszHttpNetrc) ) + curl_easy_setopt(http_handle, CURLOPT_NETRC, 1L); + + /* Support setting userid:password */ + const char *pszUserPwd = CSLFetchNameValue( papszOptions, "USERPWD" ); + if (pszUserPwd == NULL) + pszUserPwd = CPLGetConfigOption("GDAL_HTTP_USERPWD", NULL); + if( pszUserPwd != NULL ) + curl_easy_setopt(http_handle, CURLOPT_USERPWD, pszUserPwd ); + + /* Set Proxy parameters */ + const char* pszProxy = CSLFetchNameValue( papszOptions, "PROXY" ); + if (pszProxy == NULL) + pszProxy = CPLGetConfigOption("GDAL_HTTP_PROXY", NULL); + if (pszProxy) + curl_easy_setopt(http_handle,CURLOPT_PROXY,pszProxy); + + const char* pszProxyUserPwd = CSLFetchNameValue( papszOptions, "PROXYUSERPWD" ); + if (pszProxyUserPwd == NULL) + pszProxyUserPwd = CPLGetConfigOption("GDAL_HTTP_PROXYUSERPWD", NULL); + if (pszProxyUserPwd) + curl_easy_setopt(http_handle,CURLOPT_PROXYUSERPWD,pszProxyUserPwd); + + /* Support control over PROXYAUTH */ + const char *pszProxyAuth = CSLFetchNameValue( papszOptions, "PROXYAUTH" ); + if( pszProxyAuth == NULL ) + pszProxyAuth = CPLGetConfigOption( "GDAL_PROXY_AUTH", NULL ); + if( pszProxyAuth == NULL ) + /* do nothing */; + /* CURLOPT_PROXYAUTH is defined in curl 7.11.0 or newer */ +#if LIBCURL_VERSION_NUM >= 0x70B00 + else if( EQUAL(pszProxyAuth,"BASIC") ) + curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH, CURLAUTH_BASIC ); + else if( EQUAL(pszProxyAuth,"NTLM") ) + curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM ); + else if( EQUAL(pszProxyAuth,"DIGEST") ) + curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST ); + else if( EQUAL(pszProxyAuth,"ANY") ) + curl_easy_setopt(http_handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "Unsupported PROXYAUTH value '%s', ignored.", + pszProxyAuth ); + } +#else + else + { + CPLError( CE_Warning, CPLE_AppDefined, + "PROXYAUTH option needs curl >= 7.11.0" ); + } +#endif + + /* Enable following redirections. Requires libcurl 7.10.1 at least */ + curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1 ); + curl_easy_setopt(http_handle, CURLOPT_MAXREDIRS, 10 ); + + /* Set timeout.*/ + const char *pszTimeout = CSLFetchNameValue( papszOptions, "TIMEOUT" ); + if (pszTimeout == NULL) + pszTimeout = CPLGetConfigOption("GDAL_HTTP_TIMEOUT", NULL); + if( pszTimeout != NULL ) + curl_easy_setopt(http_handle, CURLOPT_TIMEOUT, atoi(pszTimeout) ); + + /* Disable some SSL verification */ + const char *pszUnsafeSSL = CSLFetchNameValue( papszOptions, "UNSAFESSL" ); + if (pszUnsafeSSL == NULL) + pszUnsafeSSL = CPLGetConfigOption("GDAL_HTTP_UNSAFESSL", NULL); + if (pszUnsafeSSL != NULL && CSLTestBoolean(pszUnsafeSSL)) + { + curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(http_handle, CURLOPT_SSL_VERIFYHOST, 0L); + } + + /* Set Referer */ + const char *pszReferer = CSLFetchNameValue(papszOptions, "REFERER"); + if (pszReferer != NULL) + curl_easy_setopt(http_handle, CURLOPT_REFERER, pszReferer); + + /* Set User-Agent */ + const char *pszUserAgent = CSLFetchNameValue(papszOptions, "USERAGENT"); + if (pszUserAgent == NULL) + pszUserAgent = CPLGetConfigOption("GDAL_HTTP_USERAGENT", NULL); + if (pszUserAgent != NULL) + curl_easy_setopt(http_handle, CURLOPT_USERAGENT, pszUserAgent); + + /* NOSIGNAL should be set to true for timeout to work in multithread + * environments on Unix, requires libcurl 7.10 or more recent. + * (this force avoiding the use of signal handlers) + */ +#if LIBCURL_VERSION_NUM >= 0x070A00 + curl_easy_setopt(http_handle, CURLOPT_NOSIGNAL, 1 ); +#endif + + /* Set POST mode */ + const char* pszPost = CSLFetchNameValue( papszOptions, "POSTFIELDS" ); + if( pszPost != NULL ) + { + CPLDebug("HTTP", "These POSTFIELDS were sent:%.4000s", pszPost); + curl_easy_setopt(http_handle, CURLOPT_POST, 1 ); + curl_easy_setopt(http_handle, CURLOPT_POSTFIELDS, pszPost ); + } + + const char* pszCustomRequest = CSLFetchNameValue( papszOptions, "CUSTOMREQUEST" ); + if( pszCustomRequest != NULL ) + { + curl_easy_setopt(http_handle, CURLOPT_CUSTOMREQUEST, pszCustomRequest ); + } + + const char* pszCookie = CSLFetchNameValue(papszOptions, "COOKIE"); + if (pszCookie == NULL) + pszCookie = CPLGetConfigOption("GDAL_HTTP_COOKIE", NULL); + if (pszCookie != NULL) + curl_easy_setopt(http_handle, CURLOPT_COOKIE, pszCookie); +} +#endif /* def HAVE_CURL */ + +/************************************************************************/ +/* CPLHTTPEnabled() */ +/************************************************************************/ + +/** + * \brief Return if CPLHTTP services can be useful + * + * Those services depend on GDAL being build with libcurl support. + * + * @return TRUE if libcurl support is enabled + */ +int CPLHTTPEnabled() + +{ +#ifdef HAVE_CURL + return TRUE; +#else + return FALSE; +#endif +} + +/************************************************************************/ +/* CPLHTTPCleanup() */ +/************************************************************************/ + +/** + * \brief Cleanup function to call at application termination + */ +void CPLHTTPCleanup() + +{ +#ifdef HAVE_CURL + if( !hSessionMapMutex ) + return; + + { + CPLMutexHolder oHolder( &hSessionMapMutex ); + std::map::iterator oIt; + if( poSessionMap ) + { + for( oIt=poSessionMap->begin(); oIt != poSessionMap->end(); oIt++ ) + curl_easy_cleanup( oIt->second ); + delete poSessionMap; + poSessionMap = NULL; + } + } + + // not quite a safe sequence. + CPLDestroyMutex( hSessionMapMutex ); + hSessionMapMutex = NULL; +#endif +} + +/************************************************************************/ +/* CPLHTTPDestroyResult() */ +/************************************************************************/ + +/** + * \brief Clean the memory associated with the return value of CPLHTTPFetch() + * + * @param psResult pointer to the return value of CPLHTTPFetch() + */ +void CPLHTTPDestroyResult( CPLHTTPResult *psResult ) + +{ + if( psResult ) + { + CPLFree( psResult->pabyData ); + CPLFree( psResult->pszErrBuf ); + CPLFree( psResult->pszContentType ); + CSLDestroy( psResult->papszHeaders ); + + int i; + for(i=0;inMimePartCount;i++) + { + CSLDestroy( psResult->pasMimePart[i].papszHeaders ); + } + CPLFree(psResult->pasMimePart); + + CPLFree( psResult ); + } +} + +/************************************************************************/ +/* CPLHTTPParseMultipartMime() */ +/************************************************************************/ + +/** + * \brief Parses a a MIME multipart message + * + * This function will iterate over each part and put it in a separate + * element of the pasMimePart array of the provided psResult structure. + * + * @param psResult pointer to the return value of CPLHTTPFetch() + * @return TRUE if the message contains MIME multipart message. + */ +int CPLHTTPParseMultipartMime( CPLHTTPResult *psResult ) + +{ +/* -------------------------------------------------------------------- */ +/* Is it already done? */ +/* -------------------------------------------------------------------- */ + if( psResult->nMimePartCount > 0 ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Find the boundary setting in the content type. */ +/* -------------------------------------------------------------------- */ + const char *pszBound = NULL; + + if( psResult->pszContentType != NULL ) + pszBound = strstr(psResult->pszContentType,"boundary="); + + if( pszBound == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to parse multi-part mime, no boundary setting." ); + return FALSE; + } + + CPLString osBoundary; + char **papszTokens = + CSLTokenizeStringComplex( pszBound + 9, "\n ;", + TRUE, FALSE ); + + if( CSLCount(papszTokens) == 0 || strlen(papszTokens[0]) == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to parse multi-part mime, boundary not parsable." ); + CSLDestroy( papszTokens ); + return FALSE; + } + + osBoundary = "--"; + osBoundary += papszTokens[0]; + CSLDestroy( papszTokens ); + +/* -------------------------------------------------------------------- */ +/* Find the start of the first chunk. */ +/* -------------------------------------------------------------------- */ + char *pszNext; + pszNext = (char *) + strstr((const char *) psResult->pabyData,osBoundary.c_str()); + + if( pszNext == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, "No parts found." ); + return FALSE; + } + + pszNext += strlen(osBoundary); + while( *pszNext != '\n' && *pszNext != '\r' && *pszNext != '\0' ) + pszNext++; + if( *pszNext == '\r' ) + pszNext++; + if( *pszNext == '\n' ) + pszNext++; + +/* -------------------------------------------------------------------- */ +/* Loop over parts... */ +/* -------------------------------------------------------------------- */ + while( TRUE ) + { + psResult->nMimePartCount++; + psResult->pasMimePart = (CPLMimePart *) + CPLRealloc(psResult->pasMimePart, + sizeof(CPLMimePart) * psResult->nMimePartCount ); + + CPLMimePart *psPart = psResult->pasMimePart+psResult->nMimePartCount-1; + + memset( psPart, 0, sizeof(CPLMimePart) ); + +/* -------------------------------------------------------------------- */ +/* Collect headers. */ +/* -------------------------------------------------------------------- */ + while( *pszNext != '\n' && *pszNext != '\r' && *pszNext != '\0' ) + { + char *pszEOL = strstr(pszNext,"\n"); + + if( pszEOL == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error while parsing multipart content (at line %d)", __LINE__); + return FALSE; + } + + *pszEOL = '\0'; + int bRestoreAntislashR = FALSE; + if (pszEOL - pszNext > 1 && pszEOL[-1] == '\r') + { + bRestoreAntislashR = TRUE; + pszEOL[-1] = '\0'; + } + psPart->papszHeaders = + CSLAddString( psPart->papszHeaders, pszNext ); + if (bRestoreAntislashR) + pszEOL[-1] = '\r'; + *pszEOL = '\n'; + + pszNext = pszEOL + 1; + } + + if( *pszNext == '\r' ) + pszNext++; + if( *pszNext == '\n' ) + pszNext++; + +/* -------------------------------------------------------------------- */ +/* Work out the data block size. */ +/* -------------------------------------------------------------------- */ + psPart->pabyData = (GByte *) pszNext; + + int nBytesAvail = psResult->nDataLen - + (pszNext - (const char *) psResult->pabyData); + + while( nBytesAvail > 0 + && (*pszNext != '-' + || strncmp(pszNext,osBoundary,strlen(osBoundary)) != 0) ) + { + pszNext++; + nBytesAvail--; + } + + if( nBytesAvail == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error while parsing multipart content (at line %d)", __LINE__); + return FALSE; + } + + psPart->nDataLen = pszNext - (const char *) psPart->pabyData; + pszNext += strlen(osBoundary); + + if( strncmp(pszNext,"--",2) == 0 ) + { + break; + } + + if( *pszNext == '\r' ) + pszNext++; + if( *pszNext == '\n' ) + pszNext++; + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error while parsing multipart content (at line %d)", __LINE__); + return FALSE; + } + } + + return TRUE; +} diff --git a/bazaar/plugin/gdal/port/cpl_http.h b/bazaar/plugin/gdal/port/cpl_http.h new file mode 100644 index 000000000..6747853f4 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_http.h @@ -0,0 +1,105 @@ +/****************************************************************************** + * $Id: cpl_http.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: Common Portability Library + * Purpose: Function wrapper for libcurl HTTP access. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2006, Frank Warmerdam + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_HTTP_H_INCLUDED +#define CPL_HTTP_H_INCLUDED + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_vsi.h" + +/** + * \file cpl_http.h + * + * Interface for downloading HTTP, FTP documents + */ + +CPL_C_START + +/*! Describe a part of a multipart message */ +typedef struct { + /*! NULL terminated array of headers */ char **papszHeaders; + + /*! Buffer with data of the part */ GByte *pabyData; + /*! Buffer length */ int nDataLen; +} CPLMimePart; + +/*! Describe the result of a CPLHTTPFetch() call */ +typedef struct { + /*! cURL error code : 0=success, non-zero if request failed */ + int nStatus; + + /*! Content-Type of the response */ + char *pszContentType; + + /*! Error message from curl, or NULL */ + char *pszErrBuf; + + /*! Length of the pabyData buffer */ + int nDataLen; + int nDataAlloc; + + /*! Buffer with downloaded data */ + GByte *pabyData; + + /*! Headers returned */ + char **papszHeaders; + + /*! Number of parts in a multipart message */ + int nMimePartCount; + + /*! Array of parts (resolved by CPLHTTPParseMultipartMime()) */ + CPLMimePart *pasMimePart; + +} CPLHTTPResult; + +int CPL_DLL CPLHTTPEnabled( void ); +CPLHTTPResult CPL_DLL *CPLHTTPFetch( const char *pszURL, char **papszOptions); +void CPL_DLL CPLHTTPCleanup( void ); +void CPL_DLL CPLHTTPDestroyResult( CPLHTTPResult *psResult ); +int CPL_DLL CPLHTTPParseMultipartMime( CPLHTTPResult *psResult ); + +/* -------------------------------------------------------------------- */ +/* The following is related to OAuth2 authorization around */ +/* google services like fusion tables, and potentially others */ +/* in the future. Code in cpl_google_oauth2.cpp. */ +/* */ +/* These services are built on CPL HTTP services. */ +/* -------------------------------------------------------------------- */ + +char CPL_DLL *GOA2GetAuthorizationURL( const char *pszScope ); +char CPL_DLL *GOA2GetRefreshToken( const char *pszAuthToken, + const char *pszScope ); +char CPL_DLL *GOA2GetAccessToken( const char *pszRefreshToken, + const char *pszScope ); + +CPL_C_END + +#endif /* ndef CPL_HTTP_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_list.cpp b/bazaar/plugin/gdal/port/cpl_list.cpp new file mode 100644 index 000000000..50593176c --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_list.cpp @@ -0,0 +1,339 @@ +/********************************************************************** + * $Id: cpl_list.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Name: cpl_list.cpp + * Project: CPL - Common Portability Library + * Purpose: List functions. + * Author: Andrey Kiselev, dron@remotesensing.org + * + ********************************************************************** + * Copyright (c) 2003, Andrey Kiselev + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_list.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: cpl_list.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +/*===================================================================== + List manipulation functions. + =====================================================================*/ + +/************************************************************************/ +/* CPLListAppend() */ +/************************************************************************/ + +/** + * Append an object list and return a pointer to the modified list. + * If the input list is NULL, then a new list is created. + * + * @param psList pointer to list head. + * @param pData pointer to inserted data object. May be NULL. + * + * @return pointer to the head of modified list. + */ + +CPLList *CPLListAppend( CPLList *psList, void *pData ) +{ + CPLList *psLast; + + /* Allocate room for the new object */ + if ( psList == NULL ) + { + psLast = psList = (CPLList *)CPLMalloc( sizeof(CPLList) ); + } + else + { + psLast = CPLListGetLast( psList ); + psLast = psLast->psNext = (CPLList *)CPLMalloc( sizeof(CPLList) ); + } + + /* Append object to the end of list */ + psLast->pData = pData; + psLast->psNext = NULL; + + return psList; +} + +/************************************************************************/ +/* CPLListInsert() */ +/************************************************************************/ + +/** + * Insert an object into list at specified position (zero based). + * If the input list is NULL, then a new list is created. + * + * @param psList pointer to list head. + * @param pData pointer to inserted data object. May be NULL. + * @param nPosition position number to insert an object. + * + * @return pointer to the head of modified list. + */ + +CPLList *CPLListInsert( CPLList *psList, void *pData, int nPosition ) +{ + CPLList *psCurrent; + int i, nCount; + + if ( nPosition < 0 ) + return psList; /* Nothing to do!*/ + + nCount = CPLListCount( psList ); + + if ( nPosition == 0) + { + CPLList *psNew = (CPLList *)CPLMalloc( sizeof(CPLList) ); + psNew->pData = pData; + psNew->psNext = psList; + psList = psNew; + } + else if ( nCount < nPosition ) + { + /* Allocate room for the new object */ + CPLList* psLast = CPLListGetLast(psList); + for ( i = nCount; i <= nPosition - 1; i++ ) + { + psLast = CPLListAppend( psLast, NULL ); + if (psList == NULL) + psList = psLast; + else + psLast = psLast->psNext; + } + psLast = CPLListAppend( psLast, pData ); + if (psList == NULL) + psList = psLast; + } + else + { + CPLList *psNew = (CPLList *)CPLMalloc( sizeof(CPLList) ); + psNew->pData = pData; + + psCurrent = psList; + for ( i = 0; i < nPosition - 1; i++ ) + psCurrent = psCurrent->psNext; + psNew->psNext = psCurrent->psNext; + psCurrent->psNext = psNew; + } + + return psList; +} + +/************************************************************************/ +/* CPLListGetLast() */ +/************************************************************************/ + +/** + * Return the pointer to last element in a list. + * + * @param psList pointer to list head. + * + * @return pointer to last element in a list. + */ + +CPLList *CPLListGetLast( CPLList *psList ) +{ + CPLList *psCurrent = psList; + + if ( psList == NULL ) + return NULL; + + while ( psCurrent->psNext ) + psCurrent = psCurrent->psNext; + + return psCurrent; +} + +/************************************************************************/ +/* CPLListGet() */ +/************************************************************************/ + +/** + * Return the pointer to the specified element in a list. + * + * @param psList pointer to list head. + * @param nPosition the index of the element in the list, 0 being the first element + * + * @return pointer to the specified element in a list. + */ + +CPLList *CPLListGet( CPLList *psList, int nPosition ) +{ + int iItem = 0; + CPLList *psCurrent = psList; + + if ( nPosition < 0 ) + return NULL; + + while ( iItem < nPosition && psCurrent ) + { + psCurrent = psCurrent->psNext; + iItem++; + } + + return psCurrent; +} + +/************************************************************************/ +/* CPLListCount() */ +/************************************************************************/ + +/** + * Return the number of elements in a list. + * + * @param psList pointer to list head. + * + * @return number of elements in a list. + */ + +int CPLListCount( CPLList *psList ) +{ + int nItems = 0; + CPLList *psCurrent = psList; + + while ( psCurrent ) + { + nItems++; + psCurrent = psCurrent->psNext; + } + + return nItems; +} + +/************************************************************************/ +/* CPLListRemove() */ +/************************************************************************/ + +/** + * Remove the element from the specified position (zero based) in a list. Data + * object contained in removed element must be freed by the caller first. + * + * @param psList pointer to list head. + * @param nPosition position number to delet an element. + * + * @return pointer to the head of modified list. + */ + +CPLList *CPLListRemove( CPLList *psList, int nPosition ) +{ + CPLList *psCurrent, *psRemoved; + int i; + + if ( psList == NULL) + { + return NULL; + } + else if ( nPosition < 0) + { + return psList; /* Nothing to do!*/ + } + else if ( nPosition == 0 ) + { + psCurrent = psList->psNext; + CPLFree( psList ); + psList = psCurrent; + } + else + { + psCurrent = psList; + for ( i = 0; i < nPosition - 1; i++ ) + { + psCurrent = psCurrent->psNext; + /* psCurrent == NULL if nPosition >= CPLListCount(psList) */ + if (psCurrent == NULL) + return psList; + } + psRemoved = psCurrent->psNext; + /* psRemoved == NULL if nPosition >= CPLListCount(psList) */ + if (psRemoved == NULL) + return psList; + psCurrent->psNext = psRemoved->psNext; + CPLFree( psRemoved ); + } + + return psList; +} + +/************************************************************************/ +/* CPLListDestroy() */ +/************************************************************************/ + +/** + * Destroy a list. Caller responsible for freeing data objects contained in + * list elements. + * + * @param psList pointer to list head. + * + */ + +void CPLListDestroy( CPLList *psList ) +{ + CPLList *psNext; + CPLList *psCurrent = psList; + + while ( psCurrent ) + { + psNext = psCurrent->psNext; + CPLFree( psCurrent ); + psCurrent = psNext; + } +} + +/************************************************************************/ +/* CPLListGetNext() */ +/************************************************************************/ + +/** + * Return the pointer to next element in a list. + * + * @param psElement pointer to list element. + * + * @return pointer to the list element preceded by the given element. + */ + +CPLList *CPLListGetNext( CPLList *psElement ) +{ + if ( psElement == NULL ) + return NULL; + else + return psElement->psNext; +} + +/************************************************************************/ +/* CPLListGetData() */ +/************************************************************************/ + +/** + * Return pointer to the data object contained in given list element. + * + * @param psElement pointer to list element. + * + * @return pointer to the data object contained in given list element. + */ + +void *CPLListGetData( CPLList *psElement ) +{ + if ( psElement == NULL ) + return NULL; + else + return psElement->pData; +} + diff --git a/bazaar/plugin/gdal/port/cpl_list.h b/bazaar/plugin/gdal/port/cpl_list.h new file mode 100644 index 000000000..be0f72440 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_list.h @@ -0,0 +1,72 @@ +/********************************************************************** + * $Id: cpl_list.h 26927 2014-02-11 15:54:59Z goatbar $ + * + * Name: cpl_list.h + * Project: CPL - Common Portability Library + * Purpose: List functions. + * Author: Andrey Kiselev, dron@remotesensing.org + * + ********************************************************************** + * Copyright (c) 2003, Andrey Kiselev + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_LIST_H_INCLUDED +#define _CPL_LIST_H_INCLUDED + +#include "cpl_port.h" + +/** + * \file cpl_list.h + * + * Simplest list implementation. List contains only pointers to stored + * objects, not objects itself. All operations regarding allocation and + * freeing memory for objects should be performed by the caller. + * + */ + +CPL_C_START + +/** List element structure. */ +typedef struct _CPLList +{ + /*! Pointer to the data object. Should be allocated and freed by the + * caller. + * */ + void *pData; + /*! Pointer to the next element in list. NULL, if current element is the + * last one. + */ + struct _CPLList *psNext; +} CPLList; + +CPLList CPL_DLL *CPLListAppend( CPLList *psList, void *pData ); +CPLList CPL_DLL *CPLListInsert( CPLList *psList, void *pData, int nPosition ); +CPLList CPL_DLL *CPLListGetLast( CPLList *psList ); +CPLList CPL_DLL *CPLListGet( CPLList *psList, int nPosition ); +int CPL_DLL CPLListCount( CPLList *psList ); +CPLList CPL_DLL *CPLListRemove( CPLList *psList, int nPosition ); +void CPL_DLL CPLListDestroy( CPLList *psList ); +CPLList CPL_DLL *CPLListGetNext( CPLList *psElement ); +void CPL_DLL *CPLListGetData( CPLList *psElement ); + +CPL_C_END + +#endif /* _CPL_LIST_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_minixml.cpp b/bazaar/plugin/gdal/port/cpl_minixml.cpp new file mode 100644 index 000000000..63eb55aaf --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_minixml.cpp @@ -0,0 +1,2066 @@ +/********************************************************************** + * $Id: cpl_minixml.cpp 29087 2015-05-01 09:51:50Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implementation of MiniXML Parser and handling. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2007-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * Independent Security Audit 2003/04/05 Andrey Kiselev: + * Completed audit of this module. Any documents may be parsed without + * buffer overflows and stack corruptions. + * + * Security Audit 2003/03/28 warmerda: + * Completed security audit. I believe that this module may be safely used + * to parse, and serialize arbitrary documents provided by a potentially + * hostile source. + * + */ + +#include "cpl_minixml.h" +#include "cpl_error.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: cpl_minixml.cpp 29087 2015-05-01 09:51:50Z rouault $"); + +typedef enum { + TNone, + TString, + TOpen, + TClose, + TEqual, + TToken, + TSlashClose, + TQuestionClose, + TComment, + TLiteral +} XMLTokenType; + +typedef struct +{ + CPLXMLNode *psFirstNode; + CPLXMLNode *psLastChild; +} StackContext; + +typedef struct { + const char *pszInput; + int nInputOffset; + int nInputLine; + int bInElement; + XMLTokenType eTokenType; + char *pszToken; + size_t nTokenMaxSize; + size_t nTokenSize; + + int nStackMaxSize; + int nStackSize; + StackContext *papsStack; + + CPLXMLNode *psFirstNode; + CPLXMLNode *psLastNode; +} ParseContext; + +static CPLXMLNode *_CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, + const char *pszText ); + +/************************************************************************/ +/* ReadChar() */ +/************************************************************************/ + +static CPL_INLINE char ReadChar( ParseContext *psContext ) + +{ + char chReturn; + + chReturn = psContext->pszInput[psContext->nInputOffset++]; + + if( chReturn == '\0' ) + psContext->nInputOffset--; + else if( chReturn == 10 ) + psContext->nInputLine++; + + return chReturn; +} + +/************************************************************************/ +/* UnreadChar() */ +/************************************************************************/ + +static CPL_INLINE void UnreadChar( ParseContext *psContext, char chToUnread ) + +{ + if( chToUnread == '\0' ) + { + /* do nothing */ + } + else + { + CPLAssert( chToUnread + == psContext->pszInput[psContext->nInputOffset-1] ); + + psContext->nInputOffset--; + + if( chToUnread == 10 ) + psContext->nInputLine--; + } +} + +/************************************************************************/ +/* ReallocToken() */ +/************************************************************************/ + +static int ReallocToken( ParseContext *psContext ) +{ + if (psContext->nTokenMaxSize > INT_MAX / 2) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating %d*2 bytes", (int)psContext->nTokenMaxSize); + VSIFree(psContext->pszToken); + psContext->pszToken = NULL; + return FALSE; + } + + psContext->nTokenMaxSize *= 2; + char* pszToken = (char *) + VSIRealloc(psContext->pszToken,psContext->nTokenMaxSize); + if (pszToken == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating %d bytes", (int)psContext->nTokenMaxSize); + VSIFree(psContext->pszToken); + psContext->pszToken = NULL; + return FALSE; + } + psContext->pszToken = pszToken; + return TRUE; +} + +/************************************************************************/ +/* AddToToken() */ +/************************************************************************/ + +static CPL_INLINE int _AddToToken( ParseContext *psContext, char chNewChar ) + +{ + if( psContext->nTokenSize >= psContext->nTokenMaxSize - 2 ) + { + if (!ReallocToken(psContext)) + return FALSE; + } + + psContext->pszToken[psContext->nTokenSize++] = chNewChar; + psContext->pszToken[psContext->nTokenSize] = '\0'; + return TRUE; +} + +#define AddToToken(psContext, chNewChar) if (!_AddToToken(psContext, chNewChar)) goto fail; + +/************************************************************************/ +/* ReadToken() */ +/************************************************************************/ + +static XMLTokenType ReadToken( ParseContext *psContext ) + +{ + char chNext; + + psContext->nTokenSize = 0; + psContext->pszToken[0] = '\0'; + + chNext = ReadChar( psContext ); + while( isspace((unsigned char)chNext) ) + chNext = ReadChar( psContext ); + +/* -------------------------------------------------------------------- */ +/* Handle comments. */ +/* -------------------------------------------------------------------- */ + if( chNext == '<' + && EQUALN(psContext->pszInput+psContext->nInputOffset,"!--",3) ) + { + psContext->eTokenType = TComment; + + // Skip "!--" characters + ReadChar(psContext); + ReadChar(psContext); + ReadChar(psContext); + + while( !EQUALN(psContext->pszInput+psContext->nInputOffset,"-->",3) + && (chNext = ReadChar(psContext)) != '\0' ) + AddToToken( psContext, chNext ); + + // Skip "-->" characters + ReadChar(psContext); + ReadChar(psContext); + ReadChar(psContext); + } +/* -------------------------------------------------------------------- */ +/* Handle DOCTYPE. */ +/* -------------------------------------------------------------------- */ + else if( chNext == '<' + && EQUALN(psContext->pszInput+psContext->nInputOffset,"!DOCTYPE",8) ) + { + int bInQuotes = FALSE; + psContext->eTokenType = TLiteral; + + AddToToken( psContext, '<' ); + do { + chNext = ReadChar(psContext); + if( chNext == '\0' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Parse error in DOCTYPE on or before line %d, " + "reached end of file without '>'.", + psContext->nInputLine ); + + break; + } + + /* The markup declaration block within a DOCTYPE tag consists of: + * - a left square bracket [ + * - a list of declarations + * - a right square bracket ] + * Example: + * + */ + if( chNext == '[' ) + { + AddToToken( psContext, chNext ); + + do + { + chNext = ReadChar( psContext ); + if (chNext == ']') + break; + AddToToken( psContext, chNext ); + } + while( chNext != '\0' + && !EQUALN(psContext->pszInput+psContext->nInputOffset,"]>", 2) ); + + if (chNext == '\0') + { + CPLError( CE_Failure, CPLE_AppDefined, + "Parse error in DOCTYPE on or before line %d, " + "reached end of file without ']'.", + psContext->nInputLine ); + break; + } + + if (chNext != ']') + { + chNext = ReadChar( psContext ); + AddToToken( psContext, chNext ); + + // Skip ">" character, will be consumed below + chNext = ReadChar( psContext ); + } + } + + + if( chNext == '\"' ) + bInQuotes = !bInQuotes; + + if( chNext == '>' && !bInQuotes ) + { + AddToToken( psContext, '>' ); + break; + } + + AddToToken( psContext, chNext ); + } while( TRUE ); + } +/* -------------------------------------------------------------------- */ +/* Handle CDATA. */ +/* -------------------------------------------------------------------- */ + else if( chNext == '<' + && EQUALN(psContext->pszInput+psContext->nInputOffset,"![CDATA[",8) ) + { + psContext->eTokenType = TString; + + // Skip !CDATA[ + ReadChar( psContext ); + ReadChar( psContext ); + ReadChar( psContext ); + ReadChar( psContext ); + ReadChar( psContext ); + ReadChar( psContext ); + ReadChar( psContext ); + ReadChar( psContext ); + + while( !EQUALN(psContext->pszInput+psContext->nInputOffset,"]]>",3) + && (chNext = ReadChar(psContext)) != '\0' ) + AddToToken( psContext, chNext ); + + // Skip "]]>" characters + ReadChar(psContext); + ReadChar(psContext); + ReadChar(psContext); + } +/* -------------------------------------------------------------------- */ +/* Simple single tokens of interest. */ +/* -------------------------------------------------------------------- */ + else if( chNext == '<' && !psContext->bInElement ) + { + psContext->eTokenType = TOpen; + psContext->bInElement = TRUE; + } + else if( chNext == '>' && psContext->bInElement ) + { + psContext->eTokenType = TClose; + psContext->bInElement = FALSE; + } + else if( chNext == '=' && psContext->bInElement ) + { + psContext->eTokenType = TEqual; + } + else if( chNext == '\0' ) + { + psContext->eTokenType = TNone; + } +/* -------------------------------------------------------------------- */ +/* Handle the /> token terminator. */ +/* -------------------------------------------------------------------- */ + else if( chNext == '/' && psContext->bInElement + && psContext->pszInput[psContext->nInputOffset] == '>' ) + { + chNext = ReadChar( psContext ); + CPLAssert( chNext == '>' ); + + psContext->eTokenType = TSlashClose; + psContext->bInElement = FALSE; + } +/* -------------------------------------------------------------------- */ +/* Handle the ?> token terminator. */ +/* -------------------------------------------------------------------- */ + else if( chNext == '?' && psContext->bInElement + && psContext->pszInput[psContext->nInputOffset] == '>' ) + { + chNext = ReadChar( psContext ); + + CPLAssert( chNext == '>' ); + + psContext->eTokenType = TQuestionClose; + psContext->bInElement = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Collect a quoted string. */ +/* -------------------------------------------------------------------- */ + else if( psContext->bInElement && chNext == '"' ) + { + psContext->eTokenType = TString; + + while( (chNext = ReadChar(psContext)) != '"' + && chNext != '\0' ) + AddToToken( psContext, chNext ); + + if( chNext != '"' ) + { + psContext->eTokenType = TNone; + CPLError( CE_Failure, CPLE_AppDefined, + "Parse error on line %d, reached EOF before closing quote.", + psContext->nInputLine ); + } + + /* Do we need to unescape it? */ + if( strchr(psContext->pszToken,'&') != NULL ) + { + int nLength; + char *pszUnescaped = CPLUnescapeString( psContext->pszToken, + &nLength, CPLES_XML ); + strcpy( psContext->pszToken, pszUnescaped ); + CPLFree( pszUnescaped ); + psContext->nTokenSize = strlen(psContext->pszToken ); + } + } + + else if( psContext->bInElement && chNext == '\'' ) + { + psContext->eTokenType = TString; + + while( (chNext = ReadChar(psContext)) != '\'' + && chNext != '\0' ) + AddToToken( psContext, chNext ); + + if( chNext != '\'' ) + { + psContext->eTokenType = TNone; + CPLError( CE_Failure, CPLE_AppDefined, + "Parse error on line %d, reached EOF before closing quote.", + psContext->nInputLine ); + } + + /* Do we need to unescape it? */ + if( strchr(psContext->pszToken,'&') != NULL ) + { + int nLength; + char *pszUnescaped = CPLUnescapeString( psContext->pszToken, + &nLength, CPLES_XML ); + strcpy( psContext->pszToken, pszUnescaped ); + CPLFree( pszUnescaped ); + psContext->nTokenSize = strlen(psContext->pszToken ); + } + } + +/* -------------------------------------------------------------------- */ +/* Collect an unquoted string, terminated by a open angle */ +/* bracket. */ +/* -------------------------------------------------------------------- */ + else if( !psContext->bInElement ) + { + psContext->eTokenType = TString; + + AddToToken( psContext, chNext ); + while( (chNext = ReadChar(psContext)) != '<' + && chNext != '\0' ) + AddToToken( psContext, chNext ); + UnreadChar( psContext, chNext ); + + /* Do we need to unescape it? */ + if( strchr(psContext->pszToken,'&') != NULL ) + { + int nLength; + char *pszUnescaped = CPLUnescapeString( psContext->pszToken, + &nLength, CPLES_XML ); + strcpy( psContext->pszToken, pszUnescaped ); + CPLFree( pszUnescaped ); + psContext->nTokenSize = strlen(psContext->pszToken ); + } + } + +/* -------------------------------------------------------------------- */ +/* Collect a regular token terminated by white space, or */ +/* special character(s) like an equal sign. */ +/* -------------------------------------------------------------------- */ + else + { + psContext->eTokenType = TToken; + + /* add the first character to the token regardless of what it is */ + AddToToken( psContext, chNext ); + + for( chNext = ReadChar(psContext); + (chNext >= 'A' && chNext <= 'Z') + || (chNext >= 'a' && chNext <= 'z') + || chNext == '-' + || chNext == '_' + || chNext == '.' + || chNext == ':' + || (chNext >= '0' && chNext <= '9'); + chNext = ReadChar(psContext) ) + { + AddToToken( psContext, chNext ); + } + + UnreadChar(psContext, chNext); + } + + return psContext->eTokenType; + +fail: + psContext->eTokenType = TNone; + return TNone; +} + +/************************************************************************/ +/* PushNode() */ +/************************************************************************/ + +static int PushNode( ParseContext *psContext, CPLXMLNode *psNode ) + +{ + if( psContext->nStackMaxSize <= psContext->nStackSize ) + { + psContext->nStackMaxSize += 10; + + if (psContext->nStackMaxSize >= (int)(INT_MAX / sizeof(StackContext))) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating %d*%d bytes", (int)sizeof(StackContext), psContext->nStackMaxSize); + VSIFree(psContext->papsStack); + psContext->papsStack = NULL; + return FALSE; + } + StackContext* papsStack; + papsStack = (StackContext *)VSIRealloc(psContext->papsStack, + sizeof(StackContext) * psContext->nStackMaxSize); + if (papsStack == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Out of memory allocating %d bytes", (int)(sizeof(StackContext) * psContext->nStackMaxSize)); + VSIFree(psContext->papsStack); + psContext->papsStack = NULL; + return FALSE; + } + psContext->papsStack = papsStack; + } + + psContext->papsStack[psContext->nStackSize].psFirstNode = psNode; + psContext->papsStack[psContext->nStackSize].psLastChild = NULL; + psContext->nStackSize ++; + return TRUE; +} + +/************************************************************************/ +/* AttachNode() */ +/* */ +/* Attach the passed node as a child of the current node. */ +/* Special handling exists for adding siblings to psFirst if */ +/* there is nothing on the stack. */ +/************************************************************************/ + +static void AttachNode( ParseContext *psContext, CPLXMLNode *psNode ) + +{ + if( psContext->psFirstNode == NULL ) + { + psContext->psFirstNode = psNode; + psContext->psLastNode = psNode; + } + else if( psContext->nStackSize == 0 ) + { + psContext->psLastNode->psNext = psNode; + psContext->psLastNode = psNode; + } + else if( psContext->papsStack[psContext->nStackSize-1].psFirstNode->psChild == NULL ) + { + psContext->papsStack[psContext->nStackSize-1].psFirstNode->psChild = psNode; + psContext->papsStack[psContext->nStackSize-1].psLastChild = psNode; + } + else + { + psContext->papsStack[psContext->nStackSize-1].psLastChild->psNext = psNode; + psContext->papsStack[psContext->nStackSize-1].psLastChild = psNode; + } +} + +/************************************************************************/ +/* CPLParseXMLString() */ +/************************************************************************/ + +/** + * \brief Parse an XML string into tree form. + * + * The passed document is parsed into a CPLXMLNode tree representation. + * If the document is not well formed XML then NULL is returned, and errors + * are reported via CPLError(). No validation beyond wellformedness is + * done. The CPLParseXMLFile() convenience function can be used to parse + * from a file. + * + * The returned document tree is is owned by the caller and should be freed + * with CPLDestroyXMLNode() when no longer needed. + * + * If the document has more than one "root level" element then those after the + * first will be attached to the first as siblings (via the psNext pointers) + * even though there is no common parent. A document with no XML structure + * (no angle brackets for instance) would be considered well formed, and + * returned as a single CXT_Text node. + * + * @param pszString the document to parse. + * + * @return parsed tree or NULL on error. + */ + +CPLXMLNode *CPLParseXMLString( const char *pszString ) + +{ + ParseContext sContext; + + CPLErrorReset(); + + if( pszString == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CPLParseXMLString() called with NULL pointer." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Check for a UTF-8 BOM and skip if found */ +/* */ +/* TODO: BOM is variable-length parameter and depends on encoding. */ +/* Add BOM detection for other encodings. */ +/* -------------------------------------------------------------------- */ + + // Used to skip to actual beginning of XML data + if( ( (unsigned char)pszString[0] == 0xEF ) + && ( (unsigned char)pszString[1] == 0xBB ) + && ( (unsigned char)pszString[2] == 0xBF) ) + { + pszString += 3; + } + +/* -------------------------------------------------------------------- */ +/* Initialize parse context. */ +/* -------------------------------------------------------------------- */ + sContext.pszInput = pszString; + sContext.nInputOffset = 0; + sContext.nInputLine = 0; + sContext.bInElement = FALSE; + sContext.nTokenMaxSize = 10; + sContext.pszToken = (char *) VSIMalloc(sContext.nTokenMaxSize); + if (sContext.pszToken == NULL) + return NULL; + sContext.nTokenSize = 0; + sContext.eTokenType = TNone; + sContext.nStackMaxSize = 0; + sContext.nStackSize = 0; + sContext.papsStack = NULL; + sContext.psFirstNode = NULL; + sContext.psLastNode = NULL; + +/* ==================================================================== */ +/* Loop reading tokens. */ +/* ==================================================================== */ + while( ReadToken( &sContext ) != TNone ) + { +/* -------------------------------------------------------------------- */ +/* Create a new element. */ +/* -------------------------------------------------------------------- */ + if( sContext.eTokenType == TOpen ) + { + CPLXMLNode *psElement; + + if( ReadToken(&sContext) != TToken ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %d: Didn't find element token after open angle bracket.", + sContext.nInputLine ); + break; + } + + if( sContext.pszToken[0] != '/' ) + { + psElement = _CPLCreateXMLNode( NULL, CXT_Element, + sContext.pszToken ); + if (!psElement) break; + AttachNode( &sContext, psElement ); + if (!PushNode( &sContext, psElement )) + break; + } + else + { + if( sContext.nStackSize == 0 + || !EQUAL(sContext.pszToken+1, + sContext.papsStack[sContext.nStackSize-1].psFirstNode->pszValue) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %d: <%.500s> doesn't have matching <%.500s>.", + sContext.nInputLine, + sContext.pszToken, sContext.pszToken+1 ); + break; + } + else + { + if (strcmp(sContext.pszToken+1, + sContext.papsStack[sContext.nStackSize-1].psFirstNode->pszValue) != 0) + { + /* TODO: at some point we could just error out like any other */ + /* sane XML parser would do */ + CPLError( CE_Warning, CPLE_AppDefined, + "Line %d: <%.500s> matches <%.500s>, but the case isn't the same. " + "Going on, but this is invalid XML that might be rejected in " + "future versions.", + sContext.nInputLine, + sContext.papsStack[sContext.nStackSize-1].psFirstNode->pszValue, + sContext.pszToken ); + } + + if( ReadToken(&sContext) != TClose ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %d: Missing close angle bracket after <%.500s.", + sContext.nInputLine, + sContext.pszToken ); + break; + } + + /* pop element off stack */ + sContext.nStackSize--; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Add an attribute to a token. */ +/* -------------------------------------------------------------------- */ + else if( sContext.eTokenType == TToken ) + { + CPLXMLNode *psAttr; + + psAttr = _CPLCreateXMLNode(NULL, CXT_Attribute, sContext.pszToken); + if (!psAttr) break; + AttachNode( &sContext, psAttr ); + + if( ReadToken(&sContext) != TEqual ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %d: Didn't find expected '=' for value of attribute '%.500s'.", + sContext.nInputLine, psAttr->pszValue ); + break; + } + + if( ReadToken(&sContext) == TToken ) + { + /* TODO: at some point we could just error out like any other */ + /* sane XML parser would do */ + CPLError( CE_Warning, CPLE_AppDefined, + "Line %d: Attribute value should be single or double quoted. " + "Going on, but this is invalid XML that might be rejected in " + "future versions.", + sContext.nInputLine ); + } + else if( sContext.eTokenType != TString ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %d: Didn't find expected attribute value.", + sContext.nInputLine ); + break; + } + + if (!_CPLCreateXMLNode( psAttr, CXT_Text, sContext.pszToken )) break; + } + +/* -------------------------------------------------------------------- */ +/* Close the start section of an element. */ +/* -------------------------------------------------------------------- */ + else if( sContext.eTokenType == TClose ) + { + if( sContext.nStackSize == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %d: Found unbalanced '>'.", + sContext.nInputLine ); + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Close the start section of an element, and pop it */ +/* immediately. */ +/* -------------------------------------------------------------------- */ + else if( sContext.eTokenType == TSlashClose ) + { + if( sContext.nStackSize == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %d: Found unbalanced '/>'.", + sContext.nInputLine ); + break; + } + + sContext.nStackSize--; + } + +/* -------------------------------------------------------------------- */ +/* Close the start section of a element, and pop it */ +/* immediately. */ +/* -------------------------------------------------------------------- */ + else if( sContext.eTokenType == TQuestionClose ) + { + if( sContext.nStackSize == 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %d: Found unbalanced '?>'.", + sContext.nInputLine ); + break; + } + else if( sContext.papsStack[sContext.nStackSize-1].psFirstNode->pszValue[0] != '?' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Line %d: Found '?>' without matching 'pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + CPLFree( sContext.pszToken ); + if( sContext.papsStack != NULL ) + CPLFree( sContext.papsStack ); + + if( CPLGetLastErrorType() == CE_Failure ) + { + CPLDestroyXMLNode( sContext.psFirstNode ); + sContext.psFirstNode = NULL; + sContext.psLastNode = NULL; + } + + return sContext.psFirstNode; +} + +/************************************************************************/ +/* _GrowBuffer() */ +/************************************************************************/ + +static void _GrowBuffer( size_t nNeeded, + char **ppszText, unsigned int *pnMaxLength ) + +{ + if( nNeeded+1 >= *pnMaxLength ) + { + *pnMaxLength = MAX(*pnMaxLength * 2,nNeeded+1); + *ppszText = (char *) CPLRealloc(*ppszText, *pnMaxLength); + } +} + +/************************************************************************/ +/* CPLSerializeXMLNode() */ +/************************************************************************/ + +static void +CPLSerializeXMLNode( const CPLXMLNode *psNode, int nIndent, + char **ppszText, unsigned int *pnLength, + unsigned int *pnMaxLength ) + +{ + if( psNode == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Ensure the buffer is plenty large to hold this additional */ +/* string. */ +/* -------------------------------------------------------------------- */ + *pnLength += strlen(*ppszText + *pnLength); + _GrowBuffer( strlen(psNode->pszValue) + *pnLength + 40 + nIndent, + ppszText, pnMaxLength ); + +/* -------------------------------------------------------------------- */ +/* Text is just directly emitted. */ +/* -------------------------------------------------------------------- */ + if( psNode->eType == CXT_Text ) + { + char *pszEscaped = CPLEscapeString( psNode->pszValue, -1, CPLES_XML_BUT_QUOTES ); + + CPLAssert( psNode->psChild == NULL ); + + /* Escaped text might be bigger than expected. */ + _GrowBuffer( strlen(pszEscaped) + *pnLength, + ppszText, pnMaxLength ); + strcat( *ppszText + *pnLength, pszEscaped ); + + CPLFree( pszEscaped ); + } + +/* -------------------------------------------------------------------- */ +/* Attributes require a little formatting. */ +/* -------------------------------------------------------------------- */ + else if( psNode->eType == CXT_Attribute ) + { + CPLAssert( psNode->psChild != NULL + && psNode->psChild->eType == CXT_Text ); + + sprintf( *ppszText + *pnLength, " %s=\"", psNode->pszValue ); + *pnLength += strlen(*ppszText + *pnLength); + + char *pszEscaped = CPLEscapeString( psNode->psChild->pszValue, -1, CPLES_XML ); + + _GrowBuffer( strlen(pszEscaped) + *pnLength, + ppszText, pnMaxLength ); + strcat( *ppszText + *pnLength, pszEscaped ); + + CPLFree( pszEscaped ); + + *pnLength += strlen(*ppszText + *pnLength); + _GrowBuffer( 3 + *pnLength, ppszText, pnMaxLength ); + strcat( *ppszText + *pnLength, "\"" ); + } + +/* -------------------------------------------------------------------- */ +/* Handle comment output. */ +/* -------------------------------------------------------------------- */ + else if( psNode->eType == CXT_Comment ) + { + int i; + + CPLAssert( psNode->psChild == NULL ); + + for( i = 0; i < nIndent; i++ ) + (*ppszText)[(*pnLength)++] = ' '; + + sprintf( *ppszText + *pnLength, "\n", + psNode->pszValue ); + } + +/* -------------------------------------------------------------------- */ +/* Handle literal output (like ) */ +/* -------------------------------------------------------------------- */ + else if( psNode->eType == CXT_Literal ) + { + int i; + + CPLAssert( psNode->psChild == NULL ); + + for( i = 0; i < nIndent; i++ ) + (*ppszText)[(*pnLength)++] = ' '; + + strcpy( *ppszText + *pnLength, psNode->pszValue ); + strcat( *ppszText + *pnLength, "\n" ); + } + +/* -------------------------------------------------------------------- */ +/* Elements actually have to deal with general children, and */ +/* various formatting issues. */ +/* -------------------------------------------------------------------- */ + else if( psNode->eType == CXT_Element ) + { + int bHasNonAttributeChildren = FALSE; + CPLXMLNode *psChild; + + memset( *ppszText + *pnLength, ' ', nIndent ); + *pnLength += nIndent; + (*ppszText)[*pnLength] = '\0'; + + sprintf( *ppszText + *pnLength, "<%s", psNode->pszValue ); + + /* Serialize *all* the attribute children, regardless of order */ + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Attribute ) + CPLSerializeXMLNode( psChild, 0, ppszText, pnLength, + pnMaxLength ); + else + bHasNonAttributeChildren = TRUE; + } + + if( !bHasNonAttributeChildren ) + { + _GrowBuffer( *pnLength + 40, + ppszText, pnMaxLength ); + + if( psNode->pszValue[0] == '?' ) + strcat( *ppszText + *pnLength, "?>\n" ); + else + strcat( *ppszText + *pnLength, " />\n" ); + } + else + { + int bJustText = TRUE; + + strcat( *ppszText + *pnLength, ">" ); + + for( psChild = psNode->psChild; + psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType == CXT_Attribute ) + continue; + + if( psChild->eType != CXT_Text && bJustText ) + { + bJustText = FALSE; + strcat( *ppszText + *pnLength, "\n" ); + } + + CPLSerializeXMLNode( psChild, nIndent + 2, ppszText, pnLength, + pnMaxLength ); + } + + *pnLength += strlen(*ppszText + *pnLength); + _GrowBuffer( strlen(psNode->pszValue) + *pnLength + 40 + nIndent, + ppszText, pnMaxLength ); + + if( !bJustText ) + { + memset( *ppszText + *pnLength, ' ', nIndent ); + *pnLength += nIndent; + (*ppszText)[*pnLength] = '\0'; + } + + *pnLength += strlen(*ppszText + *pnLength); + sprintf( *ppszText + *pnLength, "\n", psNode->pszValue ); + } + } +} + +/************************************************************************/ +/* CPLSerializeXMLTree() */ +/************************************************************************/ + +/** + * \brief Convert tree into string document. + * + * This function converts a CPLXMLNode tree representation of a document + * into a flat string representation. White space indentation is used + * visually preserve the tree structure of the document. The returned + * document becomes owned by the caller and should be freed with CPLFree() + * when no longer needed. + * + * @param psNode the node to serialize. + * + * @return the document on success or NULL on failure. + */ + +char *CPLSerializeXMLTree( const CPLXMLNode *psNode ) + +{ + unsigned int nMaxLength = 100, nLength = 0; + char *pszText = NULL; + const CPLXMLNode *psThis; + + pszText = (char *) CPLMalloc(nMaxLength); + pszText[0] = '\0'; + + for( psThis = psNode; psThis != NULL; psThis = psThis->psNext ) + CPLSerializeXMLNode( psThis, 0, &pszText, &nLength, &nMaxLength ); + + return pszText; +} + +/************************************************************************/ +/* CPLCreateXMLNode() */ +/************************************************************************/ + +/** + * \brief Create an document tree item. + * + * Create a single CPLXMLNode object with the desired value and type, and + * attach it as a child of the indicated parent. + * + * @param poParent the parent to which this node should be attached as a + * child. May be NULL to keep as free standing. + * @param eType the type of the newly created node + * @param pszText the value of the newly created node + * + * @return the newly created node, now owned by the caller (or parent node). + */ + +CPLXMLNode *CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, + const char *pszText ) + +{ + CPLXMLNode *psNode; + +/* -------------------------------------------------------------------- */ +/* Create new node. */ +/* -------------------------------------------------------------------- */ + psNode = (CPLXMLNode *) CPLCalloc(sizeof(CPLXMLNode),1); + + psNode->eType = eType; + psNode->pszValue = CPLStrdup( pszText ); + +/* -------------------------------------------------------------------- */ +/* Attach to parent, if provided. */ +/* -------------------------------------------------------------------- */ + if( poParent != NULL ) + { + if( poParent->psChild == NULL ) + poParent->psChild = psNode; + else + { + CPLXMLNode *psLink = poParent->psChild; + + while( psLink->psNext != NULL ) + psLink = psLink->psNext; + + psLink->psNext = psNode; + } + } + + return psNode; +} + +/************************************************************************/ +/* _CPLCreateXMLNode() */ +/************************************************************************/ + +/* Same as CPLCreateXMLNode() but can return NULL in case of out-of-memory */ +/* situation */ + +static CPLXMLNode *_CPLCreateXMLNode( CPLXMLNode *poParent, CPLXMLNodeType eType, + const char *pszText ) + +{ + CPLXMLNode *psNode; + +/* -------------------------------------------------------------------- */ +/* Create new node. */ +/* -------------------------------------------------------------------- */ + psNode = (CPLXMLNode *) VSICalloc(sizeof(CPLXMLNode),1); + if (psNode == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate CPLXMLNode"); + return NULL; + } + + psNode->eType = eType; + psNode->pszValue = VSIStrdup( pszText ); + if (psNode->pszValue == NULL) + { + CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate psNode->pszValue"); + VSIFree(psNode); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Attach to parent, if provided. */ +/* -------------------------------------------------------------------- */ + if( poParent != NULL ) + { + if( poParent->psChild == NULL ) + poParent->psChild = psNode; + else + { + CPLXMLNode *psLink = poParent->psChild; + + while( psLink->psNext != NULL ) + psLink = psLink->psNext; + + psLink->psNext = psNode; + } + } + + return psNode; +} + +/************************************************************************/ +/* CPLDestroyXMLNode() */ +/************************************************************************/ + +/** + * \brief Destroy a tree. + * + * This function frees resources associated with a CPLXMLNode and all its + * children nodes. + * + * @param psNode the tree to free. + */ + +void CPLDestroyXMLNode( CPLXMLNode *psNode ) + +{ + while(psNode != NULL) + { + if( psNode->pszValue != NULL ) + CPLFree( psNode->pszValue ); + + if( psNode->psChild != NULL ) + { + CPLXMLNode* psNext = psNode->psNext; + psNode->psNext = psNode->psChild; + /* Move the child and its siblings as the next */ + /* siblings of the current node */ + if (psNext != NULL) + { + CPLXMLNode* psIter = psNode->psChild; + while(psIter->psNext != NULL) + psIter = psIter->psNext; + psIter->psNext = psNext; + } + } + + CPLXMLNode* psNext = psNode->psNext; + + CPLFree( psNode ); + + psNode = psNext; + } +} + +/************************************************************************/ +/* CPLSearchXMLNode() */ +/************************************************************************/ + +/** + * \brief Search for a node in document. + * + * Searches the children (and potentially siblings) of the documented + * passed in for the named element or attribute. To search following + * siblings as well as children, prefix the pszElement name with an equal + * sign. This function does an in-order traversal of the document tree. + * So it will first match against the current node, then it's first child, + * that childs first child, and so on. + * + * Use CPLGetXMLNode() to find a specific child, or along a specific + * node path. + * + * @param psRoot the subtree to search. This should be a node of type + * CXT_Element. NULL is safe. + * + * @param pszElement the name of the element or attribute to search for. + * + * @return The matching node or NULL on failure. + */ + +CPLXMLNode *CPLSearchXMLNode( CPLXMLNode *psRoot, const char *pszElement ) + +{ + int bSideSearch = FALSE; + CPLXMLNode *psChild, *psResult; + + if( psRoot == NULL || pszElement == NULL ) + return NULL; + + if( *pszElement == '=' ) + { + bSideSearch = TRUE; + pszElement++; + } + +/* -------------------------------------------------------------------- */ +/* Does this node match? */ +/* -------------------------------------------------------------------- */ + if( (psRoot->eType == CXT_Element + || psRoot->eType == CXT_Attribute) + && EQUAL(pszElement,psRoot->pszValue) ) + return psRoot; + +/* -------------------------------------------------------------------- */ +/* Search children. */ +/* -------------------------------------------------------------------- */ + for( psChild = psRoot->psChild; psChild != NULL; psChild = psChild->psNext) + { + if( (psChild->eType == CXT_Element + || psChild->eType == CXT_Attribute) + && EQUAL(pszElement,psChild->pszValue) ) + return psChild; + + if( psChild->psChild != NULL ) + { + psResult = CPLSearchXMLNode( psChild, pszElement ); + if( psResult != NULL ) + return psResult; + } + } + +/* -------------------------------------------------------------------- */ +/* Search siblings if we are in side search mode. */ +/* -------------------------------------------------------------------- */ + if( bSideSearch ) + { + for( psRoot = psRoot->psNext; psRoot != NULL; psRoot = psRoot->psNext ) + { + psResult = CPLSearchXMLNode( psRoot, pszElement ); + if( psResult != NULL ) + return psResult; + } + } + + return NULL; +} + +/************************************************************************/ +/* CPLGetXMLNode() */ +/************************************************************************/ + +/** + * \brief Find node by path. + * + * Searches the document or subdocument indicated by psRoot for an element + * (or attribute) with the given path. The path should consist of a set of + * element names separated by dots, not including the name of the root + * element (psRoot). If the requested element is not found NULL is returned. + * + * Attribute names may only appear as the last item in the path. + * + * The search is done from the root nodes children, but all intermediate + * nodes in the path must be specified. Seaching for "name" would only find + * a name element or attribute if it is a direct child of the root, not at any + * level in the subdocument. + * + * If the pszPath is prefixed by "=" then the search will begin with the + * root node, and it's siblings, instead of the root nodes children. This + * is particularly useful when searching within a whole document which is + * often prefixed by one or more "junk" nodes like the declaration. + * + * @param psRoot the subtree in which to search. This should be a node of + * type CXT_Element. NULL is safe. + * + * @param pszPath the list of element names in the path (dot separated). + * + * @return the requested element node, or NULL if not found. + */ + +CPLXMLNode *CPLGetXMLNode( CPLXMLNode *psRoot, const char *pszPath ) + +{ + char *apszTokens[2]; + char **papszTokens; + int iToken = 0; + int bSideSearch = FALSE; + + if( psRoot == NULL || pszPath == NULL ) + return NULL; + + if( *pszPath == '=' ) + { + bSideSearch = TRUE; + pszPath++; + } + + /* Slight optimization : avoid using CSLTokenizeStringComplex that */ + /* does memory allocations when it is not really necessary */ + if (strchr(pszPath, '.')) + papszTokens = CSLTokenizeStringComplex( pszPath, ".", FALSE, FALSE ); + else + { + apszTokens[0] = (char*) pszPath; + apszTokens[1] = NULL; + papszTokens = apszTokens; + } + + while( papszTokens[iToken] != NULL && psRoot != NULL ) + { + CPLXMLNode *psChild; + + if( bSideSearch ) + { + psChild = psRoot; + bSideSearch = FALSE; + } + else + psChild = psRoot->psChild; + + for( ; psChild != NULL; psChild = psChild->psNext ) + { + if( psChild->eType != CXT_Text + && EQUAL(papszTokens[iToken],psChild->pszValue) ) + break; + } + + if( psChild == NULL ) + { + psRoot = NULL; + break; + } + + psRoot = psChild; + iToken++; + } + + if (papszTokens != apszTokens) + CSLDestroy( papszTokens ); + return psRoot; +} + +/************************************************************************/ +/* CPLGetXMLValue() */ +/************************************************************************/ + +/** + * \brief Fetch element/attribute value. + * + * Searches the document for the element/attribute value associated with + * the path. The corresponding node is internally found with CPLGetXMLNode() + * (see there for details on path handling). Once found, the value is + * considered to be the first CXT_Text child of the node. + * + * If the attribute/element search fails, or if the found node has not + * value then the passed default value is returned. + * + * The returned value points to memory within the document tree, and should + * not be altered or freed. + * + * @param psRoot the subtree in which to search. This should be a node of + * type CXT_Element. NULL is safe. + * + * @param pszPath the list of element names in the path (dot separated). An + * empty path means get the value of the psRoot node. + * + * @param pszDefault the value to return if a corresponding value is not + * found, may be NULL. + * + * @return the requested value or pszDefault if not found. + */ + +const char *CPLGetXMLValue( CPLXMLNode *psRoot, const char *pszPath, + const char *pszDefault ) + +{ + CPLXMLNode *psTarget; + + if( pszPath == NULL || *pszPath == '\0' ) + psTarget = psRoot; + else + psTarget = CPLGetXMLNode( psRoot, pszPath ); + + if( psTarget == NULL ) + return pszDefault; + + if( psTarget->eType == CXT_Attribute ) + { + CPLAssert( psTarget->psChild != NULL + && psTarget->psChild->eType == CXT_Text ); + + return psTarget->psChild->pszValue; + } + + if( psTarget->eType == CXT_Element ) + { + // Find first non-attribute child, and verify it is a single text + // with no siblings + + psTarget = psTarget->psChild; + + while( psTarget != NULL && psTarget->eType == CXT_Attribute ) + psTarget = psTarget->psNext; + + if( psTarget != NULL + && psTarget->eType == CXT_Text + && psTarget->psNext == NULL ) + return psTarget->pszValue; + } + + return pszDefault; +} + +/************************************************************************/ +/* CPLAddXMLChild() */ +/************************************************************************/ + +/** + * \brief Add child node to parent. + * + * The passed child is added to the list of children of the indicated + * parent. Normally the child is added at the end of the parents child + * list, but attributes (CXT_Attribute) will be inserted after any other + * attributes but before any other element type. Ownership of the child + * node is effectively assumed by the parent node. If the child has + * siblings (it's psNext is not NULL) they will be trimmed, but if the child + * has children they are carried with it. + * + * @param psParent the node to attach the child to. May not be NULL. + * + * @param psChild the child to add to the parent. May not be NULL. Should + * not be a child of any other parent. + */ + +void CPLAddXMLChild( CPLXMLNode *psParent, CPLXMLNode *psChild ) + +{ + CPLXMLNode *psSib; + + if( psParent->psChild == NULL ) + { + psParent->psChild = psChild; + return; + } + + // Insert at head of list if first child is not attribute. + if( psChild->eType == CXT_Attribute + && psParent->psChild->eType != CXT_Attribute ) + { + psChild->psNext = psParent->psChild; + psParent->psChild = psChild; + return; + } + + // Search for end of list. + for( psSib = psParent->psChild; + psSib->psNext != NULL; + psSib = psSib->psNext ) + { + // Insert attributes if the next node is not an attribute. + if( psChild->eType == CXT_Attribute + && psSib->psNext != NULL + && psSib->psNext->eType != CXT_Attribute ) + { + psChild->psNext = psSib->psNext; + psSib->psNext = psChild; + return; + } + } + + psSib->psNext = psChild; +} + +/************************************************************************/ +/* CPLRemoveXMLChild() */ +/************************************************************************/ + +/** + * \brief Remove child node from parent. + * + * The passed child is removed from the child list of the passed parent, + * but the child is not destroyed. The child retains ownership of it's + * own children, but is cleanly removed from the child list of the parent. + * + * @param psParent the node to the child is attached to. + * + * @param psChild the child to remove. + * + * @return TRUE on success or FALSE if the child was not found. + */ + +int CPLRemoveXMLChild( CPLXMLNode *psParent, CPLXMLNode *psChild ) + +{ + CPLXMLNode *psLast = NULL, *psThis; + + if( psParent == NULL ) + return FALSE; + + for( psThis = psParent->psChild; + psThis != NULL; + psLast = psThis, psThis = psThis->psNext ) + { + if( psThis == psChild ) + { + if( psLast == NULL ) + psParent->psChild = psThis->psNext; + else + psLast->psNext = psThis->psNext; + + psThis->psNext = NULL; + return TRUE; + } + } + + return FALSE; +} + +/************************************************************************/ +/* CPLAddXMLSibling() */ +/************************************************************************/ + +/** + * \brief Add new sibling. + * + * The passed psNewSibling is added to the end of siblings of the + * psOlderSibling node. That is, it is added to the end of the psNext + * chain. There is no special handling if psNewSibling is an attribute. + * If this is required, use CPLAddXMLChild(). + * + * @param psOlderSibling the node to attach the sibling after. + * + * @param psNewSibling the node to add at the end of psOlderSiblings psNext + * chain. + */ + +void CPLAddXMLSibling( CPLXMLNode *psOlderSibling, CPLXMLNode *psNewSibling ) + +{ + if( psOlderSibling == NULL ) + return; + + while( psOlderSibling->psNext != NULL ) + psOlderSibling = psOlderSibling->psNext; + + psOlderSibling->psNext = psNewSibling; +} + +/************************************************************************/ +/* CPLCreateXMLElementAndValue() */ +/************************************************************************/ + +/** + * \brief Create an element and text value. + * + * This is function is a convenient short form for: + * + * \code + * CPLXMLNode *psTextNode; + * CPLXMLNode *psElementNode; + * + * psElementNode = CPLCreateXMLNode( psParent, CXT_Element, pszName ); + * psTextNode = CPLCreateXMLNode( psElementNode, CXT_Text, pszValue ); + * + * return psElementNode; + * \endcode + * + * It creates a CXT_Element node, with a CXT_Text child, and + * attaches the element to the passed parent. + * + * @param psParent the parent node to which the resulting node should + * be attached. May be NULL to keep as freestanding. + * + * @param pszName the element name to create. + * @param pszValue the text to attach to the element. Must not be NULL. + * + * @return the pointer to the new element node. + */ + +CPLXMLNode *CPLCreateXMLElementAndValue( CPLXMLNode *psParent, + const char *pszName, + const char *pszValue ) + +{ + CPLXMLNode *psElementNode; + + psElementNode = CPLCreateXMLNode( psParent, CXT_Element, pszName ); + CPLCreateXMLNode( psElementNode, CXT_Text, pszValue ); + + return psElementNode; +} + +/************************************************************************/ +/* CPLCreateXMLElementAndValue() */ +/************************************************************************/ + +/** + * \brief Create an attribute and text value. + * + * This is function is a convenient short form for: + * + * \code + * CPLXMLNode *psAttributeNode; + * + * psAttributeNode = CPLCreateXMLNode( psParent, CXT_Attribute, pszName ); + * CPLCreateXMLNode( psAttributeNode, CXT_Text, pszValue ); + * \endcode + * + * It creates a CXT_Attribute node, with a CXT_Text child, and + * attaches the element to the passed parent. + * + * @param psParent the parent node to which the resulting node should + * be attached. May be NULL to keep as freestanding. + * + * @param pszName the attribute name to create. + * @param pszValue the text to attach to the attribute. Must not be NULL. + * + * @since GDAL 2.0 + */ + +void CPLAddXMLAttributeAndValue( CPLXMLNode *psParent, + const char *pszName, + const char *pszValue ) +{ + CPLXMLNode *psAttributeNode; + + psAttributeNode = CPLCreateXMLNode( psParent, CXT_Attribute, pszName ); + CPLCreateXMLNode( psAttributeNode, CXT_Text, pszValue ); +} + +/************************************************************************/ +/* CPLCloneXMLTree() */ +/************************************************************************/ + +/** + * \brief Copy tree. + * + * Creates a deep copy of a CPLXMLNode tree. + * + * @param psTree the tree to duplicate. + * + * @return a copy of the whole tree. + */ + +CPLXMLNode *CPLCloneXMLTree( CPLXMLNode *psTree ) + +{ + CPLXMLNode *psPrevious = NULL; + CPLXMLNode *psReturn = NULL; + + while( psTree != NULL ) + { + CPLXMLNode *psCopy; + + psCopy = CPLCreateXMLNode( NULL, psTree->eType, psTree->pszValue ); + if( psReturn == NULL ) + psReturn = psCopy; + if( psPrevious != NULL ) + psPrevious->psNext = psCopy; + + if( psTree->psChild != NULL ) + psCopy->psChild = CPLCloneXMLTree( psTree->psChild ); + + psPrevious = psCopy; + psTree = psTree->psNext; + } + + return psReturn; +} + +/************************************************************************/ +/* CPLSetXMLValue() */ +/************************************************************************/ + +/** + * \brief Set element value by path. + * + * Find (or create) the target element or attribute specified in the + * path, and assign it the indicated value. + * + * Any path elements that do not already exist will be created. The target + * nodes value (the first CXT_Text child) will be replaced with the provided + * value. + * + * If the target node is an attribute instead of an element, the name + * should be prefixed with a #. + * + * Example: + * CPLSetXMLValue( "Citation.Id.Description", "DOQ dataset" ); + * CPLSetXMLValue( "Citation.Id.Description.#name", "doq" ); + * + * @param psRoot the subdocument to be updated. + * + * @param pszPath the dot seperated path to the target element/attribute. + * + * @param pszValue the text value to assign. + * + * @return TRUE on success. + */ + +int CPLSetXMLValue( CPLXMLNode *psRoot, const char *pszPath, + const char *pszValue ) + +{ + char **papszTokens; + int iToken = 0; + + papszTokens = CSLTokenizeStringComplex( pszPath, ".", FALSE, FALSE ); + + while( papszTokens[iToken] != NULL && psRoot != NULL ) + { + CPLXMLNode *psChild; + int bIsAttribute = FALSE; + const char *pszName = papszTokens[iToken]; + + if( pszName[0] == '#' ) + { + bIsAttribute = TRUE; + pszName++; + } + + if( psRoot->eType != CXT_Element ) + return FALSE; + + for( psChild = psRoot->psChild; psChild != NULL; + psChild = psChild->psNext ) + { + if( psChild->eType != CXT_Text + && EQUAL(pszName,psChild->pszValue) ) + break; + } + + if( psChild == NULL ) + { + if( bIsAttribute ) + psChild = CPLCreateXMLNode( psRoot, CXT_Attribute, pszName ); + else + psChild = CPLCreateXMLNode( psRoot, CXT_Element, pszName ); + } + + psRoot = psChild; + iToken++; + } + + CSLDestroy( papszTokens ); + +/* -------------------------------------------------------------------- */ +/* Find the "text" child if there is one. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psTextChild = psRoot->psChild; + + while( psTextChild != NULL && psTextChild->eType != CXT_Text ) + psTextChild = psTextChild->psNext; + +/* -------------------------------------------------------------------- */ +/* Now set a value node under this node. */ +/* -------------------------------------------------------------------- */ + + if( psTextChild == NULL ) + CPLCreateXMLNode( psRoot, CXT_Text, pszValue ); + else + { + CPLFree( psTextChild->pszValue ); + psTextChild->pszValue = CPLStrdup( pszValue ); + } + + return TRUE; +} + +/************************************************************************/ +/* CPLStripXMLNamespace() */ +/************************************************************************/ + +/** + * \brief Strip indicated namespaces. + * + * The subdocument (psRoot) is recursively examined, and any elements + * with the indicated namespace prefix will have the namespace prefix + * stripped from the element names. If the passed namespace is NULL, then + * all namespace prefixes will be stripped. + * + * Nodes other than elements should remain unaffected. The changes are + * made "in place", and should not alter any node locations, only the + * pszValue field of affected nodes. + * + * @param psRoot the document to operate on. + * @param pszNamespace the name space prefix (not including colon), or NULL. + * @param bRecurse TRUE to recurse over whole document, or FALSE to only + * operate on the passed node. + */ + +void CPLStripXMLNamespace( CPLXMLNode *psRoot, + const char *pszNamespace, + int bRecurse ) + +{ + size_t nNameSpaceLen = (pszNamespace) ? strlen(pszNamespace) : 0; + + while( psRoot != NULL ) + { + + if( psRoot->eType == CXT_Element || psRoot->eType == CXT_Attribute ) + { + if( pszNamespace != NULL ) + { + if( EQUALN(pszNamespace,psRoot->pszValue,nNameSpaceLen) + && psRoot->pszValue[nNameSpaceLen] == ':' ) + { + memmove(psRoot->pszValue, psRoot->pszValue+nNameSpaceLen+1, + strlen(psRoot->pszValue+nNameSpaceLen+1) + 1); + } + } + else + { + const char *pszCheck; + + for( pszCheck = psRoot->pszValue; *pszCheck != '\0'; pszCheck++ ) + { + if( *pszCheck == ':' ) + { + memmove(psRoot->pszValue, pszCheck + 1, strlen(pszCheck + 1) + 1); + break; + } + } + } + } + + if( bRecurse ) + { + if( psRoot->psChild != NULL ) + CPLStripXMLNamespace( psRoot->psChild, pszNamespace, 1 ); + + psRoot = psRoot->psNext; + } + else + break; + } +} + +/************************************************************************/ +/* CPLParseXMLFile() */ +/************************************************************************/ + +/** + * \brief Parse XML file into tree. + * + * The named file is opened, loaded into memory as a big string, and + * parsed with CPLParseXMLString(). Errors in reading the file or parsing + * the XML will be reported by CPLError(). + * + * The "large file" API is used, so XML files can come from virtualized + * files. + * + * @param pszFilename the file to open. + * + * @return NULL on failure, or the document tree on success. + */ + +CPLXMLNode *CPLParseXMLFile( const char *pszFilename ) + +{ + GByte *pabyOut = NULL; + char *pszDoc; + CPLXMLNode *psTree; + +/* -------------------------------------------------------------------- */ +/* Ingest the file. */ +/* -------------------------------------------------------------------- */ + if( !VSIIngestFile( NULL, pszFilename, &pabyOut, NULL, -1 ) ) + return NULL; + pszDoc = (char*) pabyOut; + +/* -------------------------------------------------------------------- */ +/* Parse it. */ +/* -------------------------------------------------------------------- */ + psTree = CPLParseXMLString( pszDoc ); + CPLFree( pszDoc ); + + return psTree; +} + +/************************************************************************/ +/* CPLSerializeXMLTreeToFile() */ +/************************************************************************/ + +/** + * \brief Write document tree to a file. + * + * The passed document tree is converted into one big string (with + * CPLSerializeXMLTree()) and then written to the named file. Errors writing + * the file will be reported by CPLError(). The source document tree is + * not altered. If the output file already exists it will be overwritten. + * + * @param psTree the document tree to write. + * @param pszFilename the name of the file to write to. + * @return TRUE on success, FALSE otherwise. + */ + +int CPLSerializeXMLTreeToFile( const CPLXMLNode *psTree, const char *pszFilename ) + +{ + char *pszDoc; + VSILFILE *fp; + vsi_l_offset nLength; + +/* -------------------------------------------------------------------- */ +/* Serialize document. */ +/* -------------------------------------------------------------------- */ + pszDoc = CPLSerializeXMLTree( psTree ); + if( pszDoc == NULL ) + return FALSE; + + nLength = strlen(pszDoc); + +/* -------------------------------------------------------------------- */ +/* Create file. */ +/* -------------------------------------------------------------------- */ + fp = VSIFOpenL( pszFilename, "wt" ); + if( fp == NULL ) + { + CPLError( CE_Failure, CPLE_OpenFailed, + "Failed to open %.500s to write.", pszFilename ); + CPLFree( pszDoc ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Write file. */ +/* -------------------------------------------------------------------- */ + if( VSIFWriteL( pszDoc, 1, (size_t)nLength, fp ) != nLength ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Failed to write whole XML document (%.500s).", + pszFilename ); + VSIFCloseL( fp ); + CPLFree( pszDoc ); + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + VSIFCloseL( fp ); + CPLFree( pszDoc ); + + return TRUE; +} + +/************************************************************************/ +/* CPLCleanXMLElementName() */ +/************************************************************************/ + +/** + * \brief Make string into safe XML token. + * + * Modififies a string in place to try and make it into a legal + * XML token that can be used as an element name. This is accomplished + * by changing any characters not legal in a token into an underscore. + * + * NOTE: This function should implement the rules in section 2.3 of + * http://www.w3.org/TR/xml11/ but it doesn't yet do that properly. We + * only do a rough approximation of that. + * + * @param pszTarget the string to be adjusted. It is altered in place. + */ + +void CPLCleanXMLElementName( char *pszTarget ) +{ + if( pszTarget == NULL ) + return; + + for( ; *pszTarget != '\0'; pszTarget++ ) + { + if( (*((unsigned char *) pszTarget) & 0x80) || isalnum( *pszTarget ) + || *pszTarget == '_' || *pszTarget == '.' ) + { + /* ok */ + } + else + { + *pszTarget = '_'; + } + } +} diff --git a/bazaar/plugin/gdal/port/cpl_minixml.h b/bazaar/plugin/gdal/port/cpl_minixml.h new file mode 100644 index 000000000..731c8d7ed --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_minixml.h @@ -0,0 +1,160 @@ +/********************************************************************** + * $Id: cpl_minixml.h 28690 2015-03-08 20:06:12Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Declarations for MiniXML Handler. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_MINIXML_H_INCLUDED +#define _CPL_MINIXML_H_INCLUDED + +#include "cpl_port.h" + +/** + * \file cpl_minixml.h + * + * Definitions for CPL mini XML Parser/Serializer. + */ + +CPL_C_START + +typedef enum +{ + /*! Node is an element */ CXT_Element = 0, + /*! Node is a raw text value */ CXT_Text = 1, + /*! Node is attribute */ CXT_Attribute = 2, + /*! Node is an XML comment. */ CXT_Comment = 3, + /*! Node is a special literal */ CXT_Literal = 4 +} CPLXMLNodeType; + +/** + * Document node structure. + * + * This C structure is used to hold a single text fragment representing a + * component of the document when parsed. It should be allocated with the + * appropriate CPL function, and freed with CPLDestroyXMLNode(). The structure + * contents should not normally be altered by application code, but may be + * freely examined by application code. + * + * Using the psChild and psNext pointers, a heirarchical tree structure + * for a document can be represented as a tree of CPLXMLNode structures. + */ + +typedef struct CPLXMLNode +{ + /** + * \brief Node type + * + * One of CXT_Element, CXT_Text, CXT_Attribute, CXT_Comment, + * or CXT_Literal. + */ + CPLXMLNodeType eType; + + /** + * \brief Node value + * + * For CXT_Element this is the name of the element, without the angle + * brackets. Note there is a single CXT_Element even when the document + * contains a start and end element tag. The node represents the pair. + * All text or other elements between the start and end tag will appear + * as children nodes of this CXT_Element node. + * + * For CXT_Attribute the pszValue is the attribute name. The value of + * the attribute will be a CXT_Text child. + * + * For CXT_Text this is the text itself (value of an attribute, or a + * text fragment between an element start and end tags. + * + * For CXT_Literal it is all the literal text. Currently this is just + * used for !DOCTYPE lines, and the value would be the entire line. + * + * For CXT_Comment the value is all the literal text within the comment, + * but not including the comment start/end indicators ("<--" and "-->"). + */ + char *pszValue; + + /** + * \brief Next sibling. + * + * Pointer to next sibling, that is the next node appearing after this + * one that has the same parent as this node. NULL if this node is the + * last child of the parent element. + */ + struct CPLXMLNode *psNext; + + /** + * \brief Child node. + * + * Pointer to first child node, if any. Only CXT_Element and CXT_Attribute + * nodes should have children. For CXT_Attribute it should be a single + * CXT_Text value node, while CXT_Element can have any kind of child. + * The full list of children for a node are identified by walking the + * psNext's starting with the psChild node. + */ + + struct CPLXMLNode *psChild; +} CPLXMLNode; + + +CPLXMLNode CPL_DLL *CPLParseXMLString( const char * ); +void CPL_DLL CPLDestroyXMLNode( CPLXMLNode * ); +CPLXMLNode CPL_DLL *CPLGetXMLNode( CPLXMLNode *poRoot, + const char *pszPath ); +CPLXMLNode CPL_DLL *CPLSearchXMLNode( CPLXMLNode *poRoot, + const char *pszTarget ); +const char CPL_DLL *CPLGetXMLValue( CPLXMLNode *poRoot, + const char *pszPath, + const char *pszDefault ); +CPLXMLNode CPL_DLL *CPLCreateXMLNode( CPLXMLNode *poParent, + CPLXMLNodeType eType, + const char *pszText ); +char CPL_DLL *CPLSerializeXMLTree( const CPLXMLNode *psNode ); +void CPL_DLL CPLAddXMLChild( CPLXMLNode *psParent, + CPLXMLNode *psChild ); +int CPL_DLL CPLRemoveXMLChild( CPLXMLNode *psParent, + CPLXMLNode *psChild ); +void CPL_DLL CPLAddXMLSibling( CPLXMLNode *psOlderSibling, + CPLXMLNode *psNewSibling ); +CPLXMLNode CPL_DLL *CPLCreateXMLElementAndValue( CPLXMLNode *psParent, + const char *pszName, + const char *pszValue ); +void CPL_DLL CPLAddXMLAttributeAndValue( CPLXMLNode *psParent, + const char *pszName, + const char *pszValue ); +CPLXMLNode CPL_DLL *CPLCloneXMLTree( CPLXMLNode *psTree ); +int CPL_DLL CPLSetXMLValue( CPLXMLNode *psRoot, const char *pszPath, + const char *pszValue ); +void CPL_DLL CPLStripXMLNamespace( CPLXMLNode *psRoot, + const char *pszNameSpace, + int bRecurse ); +void CPL_DLL CPLCleanXMLElementName( char * ); + +CPLXMLNode CPL_DLL *CPLParseXMLFile( const char *pszFilename ); +int CPL_DLL CPLSerializeXMLTreeToFile( const CPLXMLNode *psTree, + const char *pszFilename ); + +CPL_C_END + +#endif /* _CPL_MINIXML_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_minizip_ioapi.cpp b/bazaar/plugin/gdal/port/cpl_minizip_ioapi.cpp new file mode 100644 index 000000000..189692e9a --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_minizip_ioapi.cpp @@ -0,0 +1,167 @@ +/* Modified version by Even Rouault. : + - change fill_fopen_filefunc to cpl_fill_fopen_filefunc + - port to VSIL*L API + - Remove old C style function prototypes + - Add support for ZIP64 + + * Copyright (c) 2008-2010, Even Rouault + + Original licence available in port/LICENCE_minizip +*/ + + +/* ioapi.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#include "cpl_vsi.h" + +#include +#include +#include + +#include "zlib.h" +#include "cpl_minizip_ioapi.h" + + +static +voidpf ZCALLBACK fopen_file_func OF(( + voidpf opaque, + const char* filename, + int mode)); + +static +uLong ZCALLBACK fread_file_func OF(( + voidpf opaque, + voidpf stream, + void* buf, + uLong size)); + +static +uLong ZCALLBACK fwrite_file_func OF(( + voidpf opaque, + voidpf stream, + const void* buf, + uLong size)); + +static +uLong64 ZCALLBACK ftell_file_func OF(( + voidpf opaque, + voidpf stream)); + +static +long ZCALLBACK fseek_file_func OF(( + voidpf opaque, + voidpf stream, + uLong64 offset, + int origin)); + +static +int ZCALLBACK fclose_file_func OF(( + voidpf opaque, + voidpf stream)); + +static +int ZCALLBACK ferror_file_func OF(( + voidpf opaque, + voidpf stream)); + +static +voidpf ZCALLBACK fopen_file_func (CPL_UNUSED voidpf opaque, const char* filename, int mode) +{ + VSILFILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = VSIFOpenL(filename, mode_fopen); + return file; +} + +static +uLong ZCALLBACK fread_file_func (CPL_UNUSED voidpf opaque, voidpf stream, void* buf, uLong size) +{ + uLong ret; + ret = (uLong)VSIFReadL(buf, 1, (size_t)size, (VSILFILE *)stream); + return ret; +} + +static +uLong ZCALLBACK fwrite_file_func (CPL_UNUSED voidpf opaque, voidpf stream, const void* buf, uLong size) +{ + uLong ret; + ret = (uLong)VSIFWriteL(buf, 1, (size_t)size, (VSILFILE *)stream); + return ret; +} + +static +uLong64 ZCALLBACK ftell_file_func (CPL_UNUSED voidpf opaque, voidpf stream) +{ + uLong64 ret; + ret = VSIFTellL((VSILFILE *)stream); + return ret; +} + +static +long ZCALLBACK fseek_file_func (CPL_UNUSED voidpf opaque, voidpf stream, uLong64 offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + VSIFSeekL((VSILFILE *)stream, offset, fseek_origin); + return ret; +} + +static +int ZCALLBACK fclose_file_func (CPL_UNUSED voidpf opaque, voidpf stream) +{ + int ret; + ret = VSIFCloseL((VSILFILE *)stream); + return ret; +} + +static +int ZCALLBACK ferror_file_func (CPL_UNUSED voidpf opaque, + CPL_UNUSED voidpf stream) +{ + int ret; + ret = 0; // FIXME + //ret = ferror((FILE *)stream); + return ret; +} + +void cpl_fill_fopen_filefunc (zlib_filefunc_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/bazaar/plugin/gdal/port/cpl_minizip_ioapi.h b/bazaar/plugin/gdal/port/cpl_minizip_ioapi.h new file mode 100644 index 000000000..35156d21a --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_minizip_ioapi.h @@ -0,0 +1,90 @@ +/* Modified version by Even Rouault. : + - change fill_fopen_filefunc to cpl_fill_fopen_filefunc + - Add support for ZIP64 + + * Copyright (c) 2008-2012, Even Rouault + + Original licence available in port/LICENCE_minizip +*/ + +/* ioapi.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#ifndef CPL_MINIZIP_IOAPI_H_INCLUDED +#define CPL_MINIZIP_IOAPI_H_INCLUDED + +#include "cpl_vsi.h" +#define uLong64 vsi_l_offset + +/* Gentoo removed OF from their copy of zconf.h (https://bugs.gentoo.org/show_bug.cgi?id=383179) */ +/* but our copy of minizip needs it. */ +#ifndef OF +#define OF(args) args +#endif + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + +#ifndef ZCALLBACK + +#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +#define ZCALLBACK CALLBACK +#else +#define ZCALLBACK +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef uLong64 (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong64 offset, int origin)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + + + +void cpl_fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) +#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) +#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) +#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) +#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) + + +#ifdef __cplusplus +} +#endif + +#endif /* CPL_MINIZIP_IOAPI_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_minizip_unzip.cpp b/bazaar/plugin/gdal/port/cpl_minizip_unzip.cpp new file mode 100644 index 000000000..20670ef79 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_minizip_unzip.cpp @@ -0,0 +1,1954 @@ +/* Modified version by Even Rouault. : + - Addition of cpl_unzGetCurrentFileZStreamPos + - Decoration of symbol names unz* -> cpl_unz* + - Undef EXPORT so that we are sure the symbols are not exported + - Remove old C style function prototypes + - Add support for ZIP64 + - Recode filename to UTF-8 if GP 11 is unset + - Use Info-ZIP Unicode Path Extra Field (0x7075) to get UTF-8 filenames + - ZIP64: accept number_disk == 0 in unzlocal_SearchCentralDir64() + + * Copyright (c) 2008-2014, Even Rouault + + Original licence available in port/LICENCE_minizip +*/ + + +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + Read unzip.h for more info +*/ + +/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of +compatibility with older software. The following is from the original crypt.c. Code +woven in by Terry Thorsen 1/2003. +*/ +/* + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html +*/ +/* + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + */ + +/* + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + */ + + +#include +#include +#include + +#include "zlib.h" +#include "cpl_minizip_unzip.h" +#include "cpl_string.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong64 offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong64 pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong64 offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong64 pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong64 rest_read_compressed; /* number of byte to be decompressed */ + uLong64 rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong64 byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong64 byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong64 num_file; /* number of the current file in the zipfile*/ + uLong64 pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong64 current_file_ok; /* flag about the usability of the current file*/ + uLong64 central_pos; /* position of the beginning of the central dir*/ + + uLong64 size_central_dir; /* size of the central directory */ + uLong64 offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; + + int isZip64; + +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unzlocal_getByte(const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getShort (const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getLong (const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong64 OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong64 *pX)); + + +local int unzlocal_getLong64 (const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong64 *pX) +{ + uLong64 x ; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong64)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong64)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong64)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong64)i)<<24; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong64)i)<<32; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong64)i)<<40; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong64)i)<<48; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong64)i)<<56; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT cpl_unzStringFileNameCompare (const char* fileName1, + const char* fileName2, + int iCaseSensitivity) + +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong64 unzlocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong64 unzlocal_SearchCentralDir(const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + uLong64 uSizeFile; + uLong64 uBackRead; + uLong64 uMaxBack=0xffff; /* maximum size of global comment */ + uLong64 uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + + +/* + Locate the Central directory 64 of a zipfile (at the end, just before + the global comment) +*/ +local uLong64 unzlocal_SearchCentralDir64 OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong64 unzlocal_SearchCentralDir64(const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + uLong64 uSizeFile; + uLong64 uBackRead; + uLong64 uMaxBack=0xffff; /* maximum size of global comment */ + uLong64 uPosFound=0; + uLong uL; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (unzlocal_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (unzlocal_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + uLong64 relativeOffset; + if (unzlocal_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) + return 0; + + /* total number of disks */ + if (unzlocal_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + /* Some .zip declare 0 disks, such as in http://trac.osgeo.org/gdal/ticket/5615 */ + if (uL != 1 && uL != 0) + return 0; + + /* Goto end of central directory record */ + if (ZSEEK(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (unzlocal_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + if (uL != 0x06064b50) + return 0; + + return relativeOffset; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT cpl_unzOpen2 (const char *path, + zlib_filefunc_def* pzlib_filefunc_def) +{ + unz_s us; + unz_s *s; + uLong64 central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong64 number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + if (pzlib_filefunc_def==NULL) + cpl_fill_fopen_filefunc(&us.z_filefunc); + else + us.z_filefunc = *pzlib_filefunc_def; + + us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir64(&us.z_filefunc,us.filestream); + if (central_pos) + { + uLong uS; + uLong64 uL64; + + us.isZip64 = 1; + + //printf("ZIP64 file !\n"); + + if (ZSEEK(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* size of zip64 end of central directory record */ + if (unzlocal_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version made by */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version needed to extract */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (unzlocal_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory */ + if (unzlocal_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + us.gi.size_comment = 0; + } + else + { + central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + us.isZip64 = 0; + + if (ZSEEK(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.gi.number_entry = uL; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + number_entry_CD = uL; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.size_central_dir = uL; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.offset_central_dir = uL; + + /* zipfile comment length */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + } + + if ((central_pospfile_in_zip_read!=NULL) + cpl_unzCloseCurrentFile(file); + + ZCLOSE(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT cpl_unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (uLong64 ulDosDate, tm_unz* ptm) +{ + uLong64 uDate; + uDate = (uLong64)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + CPL_UNUSED void *extraField, + CPL_UNUSED uLong extraFieldBufferSize, + CPL_UNUSED char *szComment, + CPL_UNUSED uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + uLong uL; + int bHasUTF8Filename = FALSE; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (ZSEEK(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = uL; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = uL; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + { + if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + } + lSeek -= uSizeRead; + } + +#if 0 + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong64 uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; +#endif + if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) + { + if (lSeek!=0) + { + if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + uLong acc = 0; + while(acc < file_info.size_file_extra) + { + uLong headerId; + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) + err=UNZ_ERRNO; + + uLong dataSize; + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) + err=UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { + uLong64 u64; + if( file_info.uncompressed_size == 0xFFFFFFFF ) + { + if (unzlocal_getLong64(&s->z_filefunc, s->filestream,&u64) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = u64; + } + + if( file_info.compressed_size == 0xFFFFFFFF ) + { + if (unzlocal_getLong64(&s->z_filefunc, s->filestream,&u64) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = u64; + } + + /* Relative Header offset */ + if( file_info_internal.offset_curfile == 0xFFFFFFFF ) + { + if (unzlocal_getLong64(&s->z_filefunc, s->filestream,&u64) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = u64; + } + + /* Disk Start Number */ + if( file_info.disk_num_start == 0xFFFF ) + { + uLong uL; + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.disk_num_start = uL; + } + } + /* Info-ZIP Unicode Path Extra Field (0x7075) */ + else if( headerId == 0x7075 && dataSize > 5 && + file_info.size_filename<=fileNameBufferSize && + szFileName != NULL ) + { + int version; + if (unzlocal_getByte(&s->z_filefunc, s->filestream,&version) != UNZ_OK) + err=UNZ_ERRNO; + if( version != 1 ) + { + /* If version != 1, ignore that extra field */ + if (ZSEEK(s->z_filefunc, s->filestream,dataSize - 1,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + else + { + uLong nameCRC32; + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&nameCRC32) != UNZ_OK) + err=UNZ_ERRNO; + + /* Check expected CRC for filename */ + if( nameCRC32 == crc32(0, (const Bytef*)szFileName, file_info.size_filename) ) + { + uLong utf8Size = dataSize - 1 - 4; + uLong uSizeRead ; + + bHasUTF8Filename = TRUE; + + if (utf8Sizez_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + else if( utf8Size > fileNameBufferSize ) + { + if (ZSEEK(s->z_filefunc, s->filestream,utf8Size - fileNameBufferSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + } + else + { + /* ignore unicode name if CRC mismatch */ + if (ZSEEK(s->z_filefunc, s->filestream,dataSize - 1- 4,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + } + } + else + { + if (ZSEEK(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + + acc += 2 + 2 + dataSize; + } + } + + if( !bHasUTF8Filename && szFileName != NULL && + (file_info.flag & (1 << 11)) == 0 && + file_info.size_filenamez_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; +#endif + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT cpl_unzGetCurrentFileInfo (unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT cpl_unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT cpl_unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT cpl_unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info cur_file_infoSaved; + unz_file_info_internal cur_file_info_internalSaved; + uLong64 num_fileSaved; + uLong64 pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = cpl_unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = cpl_unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (cpl_unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = cpl_unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + uLong64 pos_in_zip_directory; // offset in file + uLong64 num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT cpl_unzGetFilePos(unzFile file, unz_file_pos* file_pos) +{ + unz_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT cpl_unzGoToFilePos(unzFile file, unz_file_pos* file_pos) +{ + unz_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (unz_s* s, uInt* piSizeVar, + uLong64 * poffset_local_extrafield, + uInt * psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT cpl_unzOpenCurrentFile3 (unzFile file, int* method, + int* level, int raw, const char* password) +{ + int err=UNZ_OK; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong64 offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + cpl_unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_DEFLATED) && + (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT cpl_unzOpenCurrentFile (unzFile file) +{ + return cpl_unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT cpl_unzOpenCurrentFilePassword (unzFile file, const char* password) +{ + return cpl_unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT cpl_unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) +{ + return cpl_unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/** Addition for GDAL : START */ + +extern uLong64 ZEXPORT cpl_unzGetCurrentFileZStreamPos( unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + s=(unz_s*)file; + if (file==NULL) + return 0; //UNZ_PARAMERROR; + pfile_in_zip_read_info=s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) + return 0; //UNZ_PARAMERROR; + return pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile; +} + +/** Addition for GDAL : END */ + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT cpl_unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->read_buffer == NULL) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong64 uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong64 uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT cpl_unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT cpl_unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT cpl_unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong64 size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT cpl_unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT cpl_unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) +{ + /* int err=UNZ_OK; */ + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern uLong64 ZEXPORT cpl_unzGetOffset (unzFile file) +{ + unz_s* s; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern int ZEXPORT cpl_unzSetOffset (unzFile file, uLong64 pos) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} diff --git a/bazaar/plugin/gdal/port/cpl_minizip_unzip.h b/bazaar/plugin/gdal/port/cpl_minizip_unzip.h new file mode 100644 index 000000000..f60d6178e --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_minizip_unzip.h @@ -0,0 +1,381 @@ +/* Modified version by Even Rouault. : + - Addition of cpl_unzGetCurrentFileZStreamPos + - Decoration of symbol names unz* -> cpl_unz* + - Undef EXPORT so that we are sure the symbols are not exported + - Add support for ZIP64 + + * Copyright (c) 2008, Even Rouault + + Original licence available in port/LICENCE_minizip +*/ + +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef CPL_MINIZIP_UNZIP_H_INCLUDED +#define CPL_MINIZIP_UNZIP_H_INCLUDED + +#include "cpl_vsi.h" +#define uLong64 vsi_l_offset + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include +#endif + +#ifndef CPL_MINIZIP_IOAPI_H_INCLUDED +#include "cpl_minizip_ioapi.h" +#endif + +/* GDAL addition */ +#define NOUNCRYPT +#undef ZEXPORT +#define ZEXPORT + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong64 number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong64 compressed_size; /* compressed size 4 bytes */ + uLong64 uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT cpl_unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT cpl_unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern unzFile ZEXPORT cpl_unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT cpl_unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT cpl_unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT cpl_unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT cpl_unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT cpl_unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT cpl_unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong64 pos_in_zip_directory; /* offset in zip file directory */ + uLong64 num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT cpl_unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT cpl_unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT cpl_unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + + +/** Addition for GDAL : START */ + +extern uLong64 ZEXPORT cpl_unzGetCurrentFileZStreamPos OF(( unzFile file)); + +/** Addition for GDAL : END */ + + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT cpl_unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT cpl_unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT cpl_unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT cpl_unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT cpl_unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT cpl_unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT cpl_unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT cpl_unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT cpl_unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern uLong64 ZEXPORT cpl_unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT cpl_unzSetOffset (unzFile file, uLong64 pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* CPL_MINIZIP_UNZIP_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_minizip_zip.cpp b/bazaar/plugin/gdal/port/cpl_minizip_zip.cpp new file mode 100644 index 000000000..b5c94e7a1 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_minizip_zip.cpp @@ -0,0 +1,1374 @@ +/****************************************************************************** + * $Id: cpl_minizip_zip.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: CPL - Common Portability Library + * Author: Frank Warmerdam, warmerdam@pobox.com + * Purpose: Adjusted minizip "zip.c" source code for zip services. + * + * Modified version by Even Rouault. : + * - Decoration of symbol names unz* -> cpl_unz* + * - Undef EXPORT so that we are sure the symbols are not exported + * - Remove old C style function prototypes + * - Added CPL* simplified API at bottom. + * + * Original licence available in port/LICENCE_minizip + * + *****************************************************************************/ + +/* zip.c -- IO on .zip files using zlib + Version 1.01e, February 12th, 2005 + + 27 Dec 2004 Rolf Kalbermatter + Modification to zipOpen2 to support globalComment retrieval. + + Copyright (C) 1998-2005 Gilles Vollant + * Copyright (c) 2010-2012, Even Rouault + + Read zip.h for more info +*/ + + +#include +#include +#include +#include +#include "zlib.h" +#include "cpl_conv.h" +#include "cpl_minizip_zip.h" +#include "cpl_port.h" +#include "cpl_string.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif + +CPL_UNUSED const char zip_copyright[] = + " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + uLong pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralheader; /* size of the central header for cur file */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile_info; + +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile_info ci; /* info on the file curretly writing */ + + uLong begin_pos; /* position of the beginning of the zipfile */ + uLong add_position_when_writting_offset; + uLong number_entry; +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif +} zip_internal; + + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(linkedlist_datablock_internal*ldi) +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(linkedlist_data*ll) +{ + ll->first_block = ll->last_block = NULL; +} + +local int add_data_in_datablock(linkedlist_data*ll, + const void *buf, uLong len) +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;ifilled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 or 4 (byte, short or long) +*/ + +local int ziplocal_putValue OF((const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, uLong x, int nbByte)); + +local int ziplocal_putValue (const zlib_filefunc_def*pzlib_filefunc_def, + voidpf filestream, uLong x, int nbByte) +{ + unsigned char buf[4]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void ziplocal_putValue_inmemory OF((void* dest, uLong x, int nbByte)); +local void ziplocal_putValue_inmemory (void *dest, uLong x, int nbByte) +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong ziplocal_TmzDateToDosDate(const tm_zip *ptm, + CPL_UNUSED uLong dosDate) +{ + uLong year = (uLong)ptm->tm_year; + if (year>1980) + year-=1980; + else if (year>80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int ziplocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int ziplocal_getByte(const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int ziplocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int ziplocal_getShort (const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, uLong *pX) +{ + uLong x ; + int i; + int err; + + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int ziplocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int ziplocal_getLong ( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX ) +{ + uLong x ; + int i; + int err; + + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong ziplocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong ziplocal_SearchCentralDir( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream ) +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = (uLong) ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + +/************************************************************/ +extern zipFile ZEXPORT cpl_zipOpen2 ( + const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def ) +{ + zip_internal ziinit; + zip_internal* zi; + int err=ZIP_OK; + + + if (pzlib_filefunc_def==NULL) + cpl_fill_fopen_filefunc(&ziinit.z_filefunc); + else + ziinit.z_filefunc = *pzlib_filefunc_def; + + ziinit.filestream = (*(ziinit.z_filefunc.zopen_file)) + (ziinit.z_filefunc.opaque, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + ziinit.begin_pos = (uLong) ZTELL(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + zi = (zip_internal*)ALLOC(sizeof(zip_internal)); + if (zi==NULL) + { + ZCLOSE(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory */ + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry; + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong size_comment; + + central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream); + if (central_pos==0) + err=ZIP_ERRNO; + + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* zipfile global comment length */ + if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((central_pos0) + { + ziinit.globalcomment = (char*) ALLOC(size_comment+1); + if (ziinit.globalcomment) + { + size_comment = ZREAD(ziinit.z_filefunc, ziinit.filestream,ziinit.globalcomment,size_comment); + ziinit.globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - + (offset_central_dir+size_central_dir); + ziinit.add_position_when_writting_offset = byte_before_the_zipfile; + + { + uLong size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + offset_central_dir + byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + uLong read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + if (ZREAD(ziinit.z_filefunc, ziinit.filestream,buf_read,read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&ziinit.central_dir,buf_read, + (uLong)read_this); + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + ziinit.begin_pos = byte_before_the_zipfile; + ziinit.number_entry = number_entry_CD; + + if (ZSEEK(ziinit.z_filefunc, ziinit.filestream, + offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT cpl_zipOpen (const char *pathname, int append) +{ + return cpl_zipOpen2(pathname,append,NULL,NULL); +} + +extern int ZEXPORT cpl_zipOpenNewFileInZip3 ( + zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + CPL_UNUSED uLong crcForCrypting ) +{ + zip_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; + + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = cpl_zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate); + } + + zi->ci.flag = 0; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if (level==2) + zi->ci.flag |= 4; + if (level==1) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = (uLong) ZTELL(zi->z_filefunc,zi->filestream) ; + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + + size_extrafield_global + size_comment; + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader); + + ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)VERSIONMADEBY,2); + ziplocal_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + ziplocal_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + ziplocal_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + ziplocal_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + ziplocal_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + ziplocal_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + ziplocal_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + ziplocal_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + ziplocal_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + ziplocal_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + ziplocal_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header- zi->add_position_when_writting_offset,4); + + for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + /* write the local header */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC,4); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield_local,2); + + if ((err==ZIP_OK) && (size_filename>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + + if ((err==ZIP_OK) && (size_extrafield_local>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream,extrafield_local,size_extrafield_local) + !=size_extrafield_local) + err = ZIP_ERRNO; + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, + Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = 1; + } +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT cpl_zipOpenNewFileInZip2( + zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw ) +{ + return cpl_zipOpenNewFileInZip3 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0); +} + +extern int ZEXPORT cpl_zipOpenNewFileInZip ( + zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level ) +{ + return cpl_zipOpenNewFileInZip2 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0); +} + +local int zipFlushWriteBuffer( + zip_internal* zi ) +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;ici.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, + zi->ci.buffered_data[i],t); +#endif + } + if (ZWRITE(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) + !=zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + zi->ci.pos_in_buffered_data = 0; + return err; +} + +extern int ZEXPORT cpl_zipWriteInFileInZip ( + zipFile file, + const void* buf, + unsigned len ) +{ + zip_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.avail_in = len; + zi->ci.crc32 = crc32(zi->ci.crc32,(const Bytef *) buf,len); + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zipFlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + for (i=0;ici.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + } + + return err; +} + +extern int ZEXPORT cpl_zipCloseFileInZipRaw ( + zipFile file, + uLong uncompressed_size, + uLong crc32 ) +{ + zip_internal* zi; + uLong compressed_size; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zipFlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + if (zipFlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + err=deflateEnd(&zi->ci.stream); + zi->ci.stream_initialised = 0; + } + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = (uLong)zi->ci.stream.total_in; + } + compressed_size = (uLong)zi->ci.stream.total_out; +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + ziplocal_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + ziplocal_putValue_inmemory(zi->ci.central_header+20, + compressed_size,4); /*compr size*/ + if (zi->ci.stream.data_type == Z_ASCII) + ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + ziplocal_putValue_inmemory(zi->ci.central_header+24, + uncompressed_size,4); /*uncompr size*/ + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir,zi->ci.central_header, + (uLong)zi->ci.size_centralheader); + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + long cur_pos_inzip = (uLong) ZTELL(zi->z_filefunc,zi->filestream); + if (ZSEEK(zi->z_filefunc,zi->filestream, + zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if (err==ZIP_OK) /* compressed size, unknown */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + + if (ZSEEK(zi->z_filefunc,zi->filestream, + cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT cpl_zipCloseFileInZip ( + zipFile file ) +{ + return cpl_zipCloseFileInZipRaw (file,0,0); +} + +extern int ZEXPORT cpl_zipClose ( + zipFile file, + const char* global_comment) +{ + zip_internal* zi; + int err = 0; + uLong size_centraldir = 0; + uLong centraldir_pos_inzip; + uInt size_global_comment; + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = cpl_zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + if (global_comment==NULL) + size_global_comment = 0; + else + size_global_comment = (uInt)strlen(global_comment); + + centraldir_pos_inzip = (uLong) ZTELL(zi->z_filefunc,zi->filestream); + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block ; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream, + ldi->data,ldi->filled_in_this_block) + !=ldi->filled_in_this_block ) + err = ZIP_ERRNO; + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_datablock(zi->central_dir.first_block); + + if (err==ZIP_OK) /* Magic End */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + + if (err==ZIP_OK) /* size of the central directory */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the + starting disk number */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream, + (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + + if (err==ZIP_OK) /* zipfile comment length */ + err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if ((err==ZIP_OK) && (size_global_comment>0)) + if (ZWRITE(zi->z_filefunc,zi->filestream, + global_comment,size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + + if (ZCLOSE(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} + +/************************************************************************/ +/* ==================================================================== */ +/* The following is a simplified CPL API for creating ZIP files */ +/* exported from cpl_conv.h. */ +/* ==================================================================== */ +/************************************************************************/ + +#include "cpl_minizip_unzip.h" + +typedef struct +{ + zipFile hZip; + char **papszFilenames; +} CPLZip; + +/************************************************************************/ +/* CPLCreateZip() */ +/************************************************************************/ + +void *CPLCreateZip( const char *pszZipFilename, char **papszOptions ) + +{ + (void) papszOptions; + + int bAppend = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "APPEND", "FALSE")); + char** papszFilenames = NULL; + + if( bAppend ) + { + zipFile unzF = cpl_unzOpen(pszZipFilename); + if( unzF != NULL ) + { + if( cpl_unzGoToFirstFile(unzF) == UNZ_OK ) + { + do + { + char fileName[8193]; + unz_file_info file_info; + cpl_unzGetCurrentFileInfo (unzF, &file_info, fileName, + sizeof(fileName) - 1, NULL, 0, NULL, 0); + fileName[sizeof(fileName) - 1] = '\0'; + papszFilenames = CSLAddString(papszFilenames, fileName); + } + while( cpl_unzGoToNextFile(unzF) == UNZ_OK ); + } + cpl_unzClose(unzF); + } + } + + zipFile hZip = cpl_zipOpen( pszZipFilename, bAppend ? APPEND_STATUS_ADDINZIP : APPEND_STATUS_CREATE); + if( hZip == NULL ) + { + CSLDestroy(papszFilenames); + return NULL; + } + + CPLZip* psZip = (CPLZip*)CPLMalloc(sizeof(CPLZip)); + psZip->hZip = hZip; + psZip->papszFilenames = papszFilenames; + return psZip; +} + +/************************************************************************/ +/* CPLCreateFileInZip() */ +/************************************************************************/ + +CPLErr CPLCreateFileInZip( void *hZip, const char *pszFilename, + char **papszOptions ) + +{ + int nErr; + + (void) papszOptions; + + CPLZip* psZip = (CPLZip*)hZip; + if( psZip == NULL ) + return CE_Failure; + + if( CSLFindString(psZip->papszFilenames, pszFilename ) >= 0) + { + CPLError(CE_Failure, CPLE_AppDefined, + "%s already exists in ZIP file", pszFilename); + return CE_Failure; + } + + int bCompressed = CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "COMPRESSED", "TRUE")); + + nErr = cpl_zipOpenNewFileInZip( psZip->hZip, pszFilename, NULL, + NULL, 0, NULL, 0, "", + bCompressed ? Z_DEFLATED : 0, bCompressed ? Z_DEFAULT_COMPRESSION : 0 ); + + if( nErr != ZIP_OK ) + return CE_Failure; + else + { + psZip->papszFilenames = CSLAddString(psZip->papszFilenames, pszFilename); + return CE_None; + } +} + +/************************************************************************/ +/* CPLWriteFileInZip() */ +/************************************************************************/ + +CPLErr CPLWriteFileInZip( void *hZip, const void *pBuffer, int nBufferSize ) + +{ + int nErr; + + CPLZip* psZip = (CPLZip*)hZip; + if( psZip == NULL ) + return CE_Failure; + + nErr = cpl_zipWriteInFileInZip( psZip->hZip, pBuffer, + (unsigned int) nBufferSize ); + + if( nErr != ZIP_OK ) + return CE_Failure; + else + return CE_None; +} + +/************************************************************************/ +/* CPLCloseFileInZip() */ +/************************************************************************/ + +CPLErr CPLCloseFileInZip( void *hZip ) + +{ + int nErr; + + CPLZip* psZip = (CPLZip*)hZip; + if( psZip == NULL ) + return CE_Failure; + + nErr = cpl_zipCloseFileInZip( psZip->hZip ); + + if( nErr != ZIP_OK ) + return CE_Failure; + else + return CE_None; +} + +/************************************************************************/ +/* CPLCloseZip() */ +/************************************************************************/ + +CPLErr CPLCloseZip( void *hZip ) + +{ + int nErr; + + CPLZip* psZip = (CPLZip*)hZip; + if( psZip == NULL ) + return CE_Failure; + + nErr = cpl_zipClose(psZip->hZip, NULL); + + psZip->hZip = NULL; + CSLDestroy(psZip->papszFilenames); + psZip->papszFilenames = NULL; + CPLFree(psZip); + + if( nErr != ZIP_OK ) + return CE_Failure; + else + return CE_None; +} diff --git a/bazaar/plugin/gdal/port/cpl_minizip_zip.h b/bazaar/plugin/gdal/port/cpl_minizip_zip.h new file mode 100644 index 000000000..f5dae03a0 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_minizip_zip.h @@ -0,0 +1,259 @@ +/****************************************************************************** + * $Id: cpl_minizip_zip.h 20794 2010-10-08 16:58:27Z warmerdam $ + * + * Project: CPL - Common Portability Library + * Author: Frank Warmerdam, warmerdam@pobox.com + * Purpose: Adjusted minizip "zip.h" include file for zip services. + * + * Modified version by Even Rouault. : + * - Decoration of symbol names unz* -> cpl_unz* + * - Undef EXPORT so that we are sure the symbols are not exported + * - Remove old C style function prototypes + * - Added CPL* simplified API at bottom. + * + * Original licence available in port/LICENCE_minizip + * + *****************************************************************************/ + +/* zip.h -- IO for compress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow creates .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + For uncompress .zip file, look at unzip.h + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.html for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef CPL_MINIZIP_ZIP_H_INCLUDED +#define CPL_MINIZIP_ZIP_H_INCLUDED + +#include "cpl_vsi.h" +#define uLong64 vsi_l_offset + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef CPL_MINIZIP_IOAPI_H_INCLUDED +#include "cpl_minizip_ioapi.h" +#endif + +#define NOCRYPT +#undef ZEXPORT +#define ZEXPORT + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT cpl_zipOpen OF((const char *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT cpl_zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern int ZEXPORT cpl_zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) +*/ + + +extern int ZEXPORT cpl_zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT cpl_zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCtypting)); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCtypting : crc of file to compress (needed for crypting) + */ + + +extern int ZEXPORT cpl_zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT cpl_zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT cpl_zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); +/* + Close the current file in the zipfile, for fiel opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT cpl_zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip_H */ diff --git a/bazaar/plugin/gdal/port/cpl_multiproc.cpp b/bazaar/plugin/gdal/port/cpl_multiproc.cpp new file mode 100644 index 000000000..87c814995 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_multiproc.cpp @@ -0,0 +1,2252 @@ +/********************************************************************** + * $Id: cpl_multiproc.cpp 29304 2015-06-05 16:44:41Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: CPL Multi-Threading, and process handling portability functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "cpl_multiproc.h" + +#include "cpl_conv.h" + +#if !defined(WIN32CE) +# include +#else +# include +#endif + +CPL_CVSID("$Id: cpl_multiproc.cpp 29304 2015-06-05 16:44:41Z rouault $"); + +#if defined(CPL_MULTIPROC_STUB) && !defined(DEBUG) +# define MUTEX_NONE +#endif + +//#define DEBUG_MUTEX + +#if defined(DEBUG) && (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) +#define DEBUG_CONTENTION +#endif + +typedef struct _CPLSpinLock CPLSpinLock; + +struct _CPLLock +{ + CPLLockType eType; + union + { + CPLMutex *hMutex; + CPLSpinLock *hSpinLock; + } u; + +#ifdef DEBUG_CONTENTION + int bDebugPerf; + GUIntBig nStartTime; + GIntBig nMaxDiff; + double dfAvgDiff; + GUIntBig nIters; +#endif +}; + +#ifdef DEBUG_CONTENTION +static GUIntBig CPLrdtsc(void) +{ + unsigned int a, d, x, y; + __asm__ volatile ("cpuid" : "=a" (x), "=d" (y) : "a"(0) : "cx", "bx" ); + __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d) ); + return ((GUIntBig )a) | (((GUIntBig )d) << 32); +} + +static GUIntBig CPLrdtscp(void) +{ + unsigned int a, d, x, y; + __asm__ volatile ("rdtscp" : "=a" (a), "=d" (d) ); + __asm__ volatile ("cpuid" : "=a" (x), "=d" (y) : "a"(0) : "cx", "bx" ); + return ((GUIntBig )a) | (((GUIntBig )d) << 32); +} +#endif + +static CPLSpinLock *CPLCreateSpinLock( void ); /* returned NON acquired */ +static int CPLCreateOrAcquireSpinLockInternal( CPLLock** ); +static int CPLAcquireSpinLock( CPLSpinLock* ); +static void CPLReleaseSpinLock( CPLSpinLock* ); +static void CPLDestroySpinLock( CPLSpinLock* ); + +/* We don't want it to be publicly used since it solves rather tricky issues */ +/* that are better to remain hidden... */ +void CPLFinalizeTLS(); + +/************************************************************************/ +/* CPLMutexHolder() */ +/************************************************************************/ + +CPLMutexHolder::CPLMutexHolder( CPLMutex **phMutex, double dfWaitInSeconds, + const char *pszFileIn, + int nLineIn, + int nOptions ) + +{ +#ifndef MUTEX_NONE + pszFile = pszFileIn; + nLine = nLineIn; + +#ifdef DEBUG_MUTEX + /* + * XXX: There is no way to use CPLDebug() here because it works with + * mutexes itself so we will fall in infinite recursion. Good old + * fprintf() will do the job right. + */ + fprintf( stderr, + "CPLMutexHolder: Request %p for pid %ld at %d/%s.\n", + *phMutex, (long) CPLGetPID(), nLine, pszFile ); +#endif + + if( !CPLCreateOrAcquireMutexEx( phMutex, dfWaitInSeconds, nOptions ) ) + { + fprintf( stderr, "CPLMutexHolder: Failed to acquire mutex!\n" ); + hMutex = NULL; + } + else + { +#ifdef DEBUG_MUTEX + fprintf( stderr, + "CPLMutexHolder: Acquired %p for pid %ld at %d/%s.\n", + *phMutex, (long) CPLGetPID(), nLine, pszFile ); +#endif + + hMutex = *phMutex; + } +#endif /* ndef MUTEX_NONE */ +} + +/************************************************************************/ +/* CPLMutexHolder() */ +/************************************************************************/ + +CPLMutexHolder::CPLMutexHolder( CPLMutex *hMutexIn, double dfWaitInSeconds, + const char *pszFileIn, + int nLineIn ) + +{ +#ifndef MUTEX_NONE + pszFile = pszFileIn; + nLine = nLineIn; + hMutex = hMutexIn; + + if( hMutex != NULL && + !CPLAcquireMutex( hMutex, dfWaitInSeconds ) ) + { + fprintf( stderr, "CPLMutexHolder: Failed to acquire mutex!\n" ); + hMutex = NULL; + } +#endif /* ndef MUTEX_NONE */ +} + +/************************************************************************/ +/* ~CPLMutexHolder() */ +/************************************************************************/ + +CPLMutexHolder::~CPLMutexHolder() + +{ +#ifndef MUTEX_NONE + if( hMutex != NULL ) + { +#ifdef DEBUG_MUTEX + fprintf( stderr, + "~CPLMutexHolder: Release %p for pid %ld at %d/%s.\n", + hMutex, (long) CPLGetPID(), nLine, pszFile ); +#endif + CPLReleaseMutex( hMutex ); + } +#endif /* ndef MUTEX_NONE */ +} + +int CPLCreateOrAcquireMutex( CPLMutex **phMutex, double dfWaitInSeconds ) +{ + return CPLCreateOrAcquireMutexEx(phMutex, dfWaitInSeconds, CPL_MUTEX_RECURSIVE); +} + +/************************************************************************/ +/* CPLCreateOrAcquireMutex() */ +/************************************************************************/ + +#ifndef CPL_MULTIPROC_PTHREAD + +#ifndef MUTEX_NONE +static CPLMutex *hCOAMutex = NULL; +#endif + +int CPLCreateOrAcquireMutexEx( CPLMutex **phMutex, double dfWaitInSeconds, int nOptions ) + +{ + int bSuccess = FALSE; + +#ifndef MUTEX_NONE + + /* + ** ironically, creation of this initial mutex is not threadsafe + ** even though we use it to ensure that creation of other mutexes + ** is threadsafe. + */ + if( hCOAMutex == NULL ) + { + hCOAMutex = CPLCreateMutex(); + if (hCOAMutex == NULL) + { + *phMutex = NULL; + return FALSE; + } + } + else + { + CPLAcquireMutex( hCOAMutex, dfWaitInSeconds ); + } + + if( *phMutex == NULL ) + { + *phMutex = CPLCreateMutexEx( nOptions ); + bSuccess = *phMutex != NULL; + CPLReleaseMutex( hCOAMutex ); + } + else + { + CPLReleaseMutex( hCOAMutex ); + + bSuccess = CPLAcquireMutex( *phMutex, dfWaitInSeconds ); + } +#endif /* ndef MUTEX_NONE */ + + return bSuccess; +} + +/************************************************************************/ +/* CPLCreateOrAcquireMutexInternal() */ +/************************************************************************/ + +static +int CPLCreateOrAcquireMutexInternal( CPLLock **phLock, double dfWaitInSeconds, + CPLLockType eType ) + +{ + int bSuccess = FALSE; + +#ifndef MUTEX_NONE + + /* + ** ironically, creation of this initial mutex is not threadsafe + ** even though we use it to ensure that creation of other mutexes + ** is threadsafe. + */ + if( hCOAMutex == NULL ) + { + hCOAMutex = CPLCreateMutex(); + if (hCOAMutex == NULL) + { + *phLock = NULL; + return FALSE; + } + } + else + { + CPLAcquireMutex( hCOAMutex, dfWaitInSeconds ); + } + + if( *phLock == NULL ) + { + *phLock = (CPLLock*) calloc(1, sizeof(CPLLock)); + if( *phLock ) + { + (*phLock)->eType = eType; + (*phLock)->u.hMutex = CPLCreateMutexEx( + (eType == LOCK_RECURSIVE_MUTEX) ? CPL_MUTEX_RECURSIVE : CPL_MUTEX_ADAPTIVE ); + if( (*phLock)->u.hMutex == NULL ) + { + free(*phLock); + *phLock = NULL; + } + } + bSuccess = *phLock != NULL; + CPLReleaseMutex( hCOAMutex ); + } + else + { + CPLReleaseMutex( hCOAMutex ); + + bSuccess = CPLAcquireMutex( (*phLock)->u.hMutex, dfWaitInSeconds ); + } +#endif /* ndef MUTEX_NONE */ + + return bSuccess; +} + +#endif /* CPL_MULTIPROC_PTHREAD */ + +/************************************************************************/ +/* CPLCleanupMasterMutex() */ +/************************************************************************/ + +void CPLCleanupMasterMutex() +{ +#ifndef CPL_MULTIPROC_PTHREAD +#ifndef MUTEX_NONE + if( hCOAMutex != NULL ) + { + CPLDestroyMutex( hCOAMutex ); + hCOAMutex = NULL; + } +#endif +#endif +} + +/************************************************************************/ +/* CPLCleanupTLSList() */ +/* */ +/* Free resources associated with a TLS vector (implementation */ +/* independent). */ +/************************************************************************/ + +static void CPLCleanupTLSList( void **papTLSList ) + +{ + int i; + +// printf( "CPLCleanupTLSList(%p)\n", papTLSList ); + + if( papTLSList == NULL ) + return; + + for( i = 0; i < CTLS_MAX; i++ ) + { + if( papTLSList[i] != NULL && papTLSList[i+CTLS_MAX] != NULL ) + { + CPLTLSFreeFunc pfnFree = (CPLTLSFreeFunc) papTLSList[i + CTLS_MAX]; + pfnFree( papTLSList[i] ); + papTLSList[i] = NULL; + } + } + + CPLFree( papTLSList ); +} + +#if defined(CPL_MULTIPROC_STUB) +/************************************************************************/ +/* ==================================================================== */ +/* CPL_MULTIPROC_STUB */ +/* */ +/* Stub implementation. Mutexes don't provide exclusion, file */ +/* locking is achieved with extra "lock files", and thread */ +/* creation doesn't work. The PID is always just one. */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* CPLGetNumCPUs() */ +/************************************************************************/ + +int CPLGetNumCPUs() +{ + return 1; +} + +/************************************************************************/ +/* CPLGetThreadingModel() */ +/************************************************************************/ + +const char *CPLGetThreadingModel() + +{ + return "stub"; +} + +/************************************************************************/ +/* CPLCreateMutex() */ +/************************************************************************/ + +CPLMutex *CPLCreateMutex() + +{ +#ifndef MUTEX_NONE + unsigned char *pabyMutex = (unsigned char *) malloc( 4 ); + + pabyMutex[0] = 1; + pabyMutex[1] = 'r'; + pabyMutex[2] = 'e'; + pabyMutex[3] = 'd'; + + return (CPLMutex *) pabyMutex; +#else + return (CPLMutex *) 0xdeadbeef; +#endif +} + +CPLMutex *CPLCreateMutexEx(CPL_UNUSED int nOptions) + +{ + return CPLCreateMutex(); +} + +/************************************************************************/ +/* CPLAcquireMutex() */ +/************************************************************************/ + +int CPLAcquireMutex( CPLMutex *hMutex, + CPL_UNUSED double dfWaitInSeconds ) +{ +#ifndef MUTEX_NONE + unsigned char *pabyMutex = (unsigned char *) hMutex; + + CPLAssert( pabyMutex[1] == 'r' && pabyMutex[2] == 'e' + && pabyMutex[3] == 'd' ); + + pabyMutex[0] += 1; + + return TRUE; +#else + return TRUE; +#endif +} + +/************************************************************************/ +/* CPLReleaseMutex() */ +/************************************************************************/ + +void CPLReleaseMutex( CPLMutex *hMutex ) + +{ +#ifndef MUTEX_NONE + unsigned char *pabyMutex = (unsigned char *) hMutex; + + CPLAssert( pabyMutex[1] == 'r' && pabyMutex[2] == 'e' + && pabyMutex[3] == 'd' ); + + if( pabyMutex[0] < 1 ) + CPLDebug( "CPLMultiProc", + "CPLReleaseMutex() called on mutex with %d as ref count!", + pabyMutex[0] ); + + pabyMutex[0] -= 1; +#endif +} + +/************************************************************************/ +/* CPLDestroyMutex() */ +/************************************************************************/ + +void CPLDestroyMutex( CPLMutex *hMutex ) + +{ +#ifndef MUTEX_NONE + unsigned char *pabyMutex = (unsigned char *) hMutex; + + CPLAssert( pabyMutex[1] == 'r' && pabyMutex[2] == 'e' + && pabyMutex[3] == 'd' ); + + free( pabyMutex ); +#endif +} + +/************************************************************************/ +/* CPLCreateCond() */ +/************************************************************************/ + +CPLCond *CPLCreateCond() +{ + return NULL; +} + +/************************************************************************/ +/* CPLCondWait() */ +/************************************************************************/ + +void CPLCondWait( CPL_UNUSED CPLCond *hCond, CPL_UNUSED CPLMutex* hMutex ) +{ +} + +/************************************************************************/ +/* CPLCondSignal() */ +/************************************************************************/ + +void CPLCondSignal( CPL_UNUSED CPLCond *hCond ) +{ +} + +/************************************************************************/ +/* CPLCondBroadcast() */ +/************************************************************************/ + +void CPLCondBroadcast( CPL_UNUSED CPLCond *hCond ) +{ +} + +/************************************************************************/ +/* CPLDestroyCond() */ +/************************************************************************/ + +void CPLDestroyCond( CPL_UNUSED CPLCond *hCond ) +{ +} + +/************************************************************************/ +/* CPLLockFile() */ +/* */ +/* Lock a file. This implementation has a terrible race */ +/* condition. If we don't succeed in opening the lock file, we */ +/* assume we can create one and own the target file, but other */ +/* processes might easily try creating the target file at the */ +/* same time, overlapping us. Death! Mayhem! The traditional */ +/* solution is to use open() with _O_CREAT|_O_EXCL but this */ +/* function and these arguments aren't trivially portable. */ +/* Also, this still leaves a race condition on NFS drivers */ +/* (apparently). */ +/************************************************************************/ + +void *CPLLockFile( const char *pszPath, double dfWaitInSeconds ) + +{ + FILE *fpLock; + char *pszLockFilename; + +/* -------------------------------------------------------------------- */ +/* We use a lock file with a name derived from the file we want */ +/* to lock to represent the file being locked. Note that for */ +/* the stub implementation the target file does not even need */ +/* to exist to be locked. */ +/* -------------------------------------------------------------------- */ + pszLockFilename = (char *) CPLMalloc(strlen(pszPath) + 30); + sprintf( pszLockFilename, "%s.lock", pszPath ); + + fpLock = fopen( pszLockFilename, "r" ); + while( fpLock != NULL && dfWaitInSeconds > 0.0 ) + { + fclose( fpLock ); + CPLSleep( MIN(dfWaitInSeconds,0.5) ); + dfWaitInSeconds -= 0.5; + + fpLock = fopen( pszLockFilename, "r" ); + } + + if( fpLock != NULL ) + { + fclose( fpLock ); + CPLFree( pszLockFilename ); + return NULL; + } + + fpLock = fopen( pszLockFilename, "w" ); + + if( fpLock == NULL ) + { + CPLFree( pszLockFilename ); + return NULL; + } + + fwrite( "held\n", 1, 5, fpLock ); + fclose( fpLock ); + + return pszLockFilename; +} + +/************************************************************************/ +/* CPLUnlockFile() */ +/************************************************************************/ + +void CPLUnlockFile( void *hLock ) + +{ + char *pszLockFilename = (char *) hLock; + + if( hLock == NULL ) + return; + + VSIUnlink( pszLockFilename ); + + CPLFree( pszLockFilename ); +} + +/************************************************************************/ +/* CPLGetPID() */ +/************************************************************************/ + +GIntBig CPLGetPID() + +{ + return 1; +} + +/************************************************************************/ +/* CPLCreateThread(); */ +/************************************************************************/ + +int CPLCreateThread( CPL_UNUSED CPLThreadFunc pfnMain, CPL_UNUSED void *pArg ) +{ + CPLDebug( "CPLCreateThread", "Fails to dummy implementation" ); + + return -1; +} + +/************************************************************************/ +/* CPLCreateJoinableThread() */ +/************************************************************************/ + +CPLJoinableThread* CPLCreateJoinableThread( CPL_UNUSED CPLThreadFunc pfnMain, CPL_UNUSED void *pThreadArg ) +{ + CPLDebug( "CPLCreateJoinableThread", "Fails to dummy implementation" ); + + return NULL; +} + +/************************************************************************/ +/* CPLJoinThread() */ +/************************************************************************/ + +void CPLJoinThread(CPL_UNUSED CPLJoinableThread* hJoinableThread) +{ +} + +/************************************************************************/ +/* CPLSleep() */ +/************************************************************************/ + +void CPLSleep( double dfWaitInSeconds ) +{ + time_t ltime; + time_t ttime; + + time( <ime ); + ttime = ltime + (int) (dfWaitInSeconds+0.5); + + for( ; ltime < ttime; time(<ime) ) + { + /* currently we just busy wait. Perhaps we could at least block on + io? */ + } +} + +/************************************************************************/ +/* CPLGetTLSList() */ +/************************************************************************/ + +static void **papTLSList = NULL; + +static void **CPLGetTLSList() + +{ + if( papTLSList == NULL ) + { + papTLSList = (void **) VSICalloc(sizeof(void*),CTLS_MAX*2); + if( papTLSList == NULL ) + CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!"); + } + + return papTLSList; +} + +/************************************************************************/ +/* CPLFinalizeTLS() */ +/************************************************************************/ + +void CPLFinalizeTLS() +{ + CPLCleanupTLS(); +} + +/************************************************************************/ +/* CPLCleanupTLS() */ +/************************************************************************/ + +void CPLCleanupTLS() + +{ + CPLCleanupTLSList( papTLSList ); + papTLSList = NULL; +} + +/* endif CPL_MULTIPROC_STUB */ + +#elif defined(CPL_MULTIPROC_WIN32) + + + /************************************************************************/ + /* ==================================================================== */ + /* CPL_MULTIPROC_WIN32 */ + /* */ + /* WIN32 Implementation of multiprocessing functions. */ + /* ==================================================================== */ + /************************************************************************/ + +/* InitializeCriticalSectionAndSpinCount requires _WIN32_WINNT >= 0x403 */ +#define _WIN32_WINNT 0x0500 + +#include + +/* windows.h header must be included above following lines. */ +#if defined(WIN32CE) +# include "cpl_win32ce_api.h" +# define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) +#endif + +/************************************************************************/ +/* CPLGetNumCPUs() */ +/************************************************************************/ + +int CPLGetNumCPUs() +{ + SYSTEM_INFO info; + GetSystemInfo(&info); + DWORD dwNum = info.dwNumberOfProcessors; + if( dwNum < 1 ) + return 1; + return (int)dwNum; +} + +/************************************************************************/ +/* CPLGetThreadingModel() */ +/************************************************************************/ + +const char *CPLGetThreadingModel() + +{ + return "win32"; +} + +/************************************************************************/ +/* CPLCreateMutex() */ +/************************************************************************/ + +CPLMutex *CPLCreateMutex() + +{ +#ifdef USE_WIN32_MUTEX + HANDLE hMutex; + + hMutex = CreateMutex( NULL, TRUE, NULL ); + + return (CPLMutex *) hMutex; +#else + CRITICAL_SECTION *pcs; + + /* Do not use CPLMalloc() since its debugging infrastructure */ + /* can call the CPL*Mutex functions... */ + pcs = (CRITICAL_SECTION *)malloc(sizeof(*pcs)); + if( pcs ) + { + InitializeCriticalSectionAndSpinCount(pcs, 4000); + EnterCriticalSection(pcs); + } + + return (CPLMutex *) pcs; +#endif +} + +CPLMutex *CPLCreateMutexEx(CPL_UNUSED int nOptions) + +{ + return CPLCreateMutex(); +} + +/************************************************************************/ +/* CPLAcquireMutex() */ +/************************************************************************/ + +int CPLAcquireMutex( CPLMutex *hMutexIn, double dfWaitInSeconds ) + +{ +#ifdef USE_WIN32_MUTEX + HANDLE hMutex = (HANDLE) hMutexIn; + DWORD hr; + + hr = WaitForSingleObject( hMutex, (int) (dfWaitInSeconds * 1000) ); + + return hr != WAIT_TIMEOUT; +#else + CRITICAL_SECTION *pcs = (CRITICAL_SECTION *)hMutexIn; + BOOL ret; + + if( dfWaitInSeconds >= 1000.0 ) + { + // We assume this is the synonymous for infinite, so it is more + // efficient to use EnterCriticalSection() directly + EnterCriticalSection(pcs); + ret = TRUE; + } + else + { + while( (ret = TryEnterCriticalSection(pcs)) == 0 && dfWaitInSeconds > 0.0 ) + { + CPLSleep( MIN(dfWaitInSeconds,0.01) ); + dfWaitInSeconds -= 0.01; + } + } + + return ret; +#endif +} + +/************************************************************************/ +/* CPLReleaseMutex() */ +/************************************************************************/ + +void CPLReleaseMutex( CPLMutex *hMutexIn ) + +{ +#ifdef USE_WIN32_MUTEX + HANDLE hMutex = (HANDLE) hMutexIn; + + ReleaseMutex( hMutex ); +#else + CRITICAL_SECTION *pcs = (CRITICAL_SECTION *)hMutexIn; + + LeaveCriticalSection(pcs); +#endif +} + +/************************************************************************/ +/* CPLDestroyMutex() */ +/************************************************************************/ + +void CPLDestroyMutex( CPLMutex *hMutexIn ) + +{ +#ifdef USE_WIN32_MUTEX + HANDLE hMutex = (HANDLE) hMutexIn; + + CloseHandle( hMutex ); +#else + CRITICAL_SECTION *pcs = (CRITICAL_SECTION *)hMutexIn; + + DeleteCriticalSection( pcs ); + free( pcs ); +#endif +} + +/************************************************************************/ +/* CPLCreateCond() */ +/************************************************************************/ + +struct _WaiterItem +{ + HANDLE hEvent; + struct _WaiterItem* psNext; +}; +typedef struct _WaiterItem WaiterItem; + +typedef struct +{ + CPLMutex *hInternalMutex; + WaiterItem *psWaiterList; +} Win32Cond; + +CPLCond *CPLCreateCond() +{ + Win32Cond* psCond = (Win32Cond*) malloc(sizeof(Win32Cond)); + if (psCond == NULL) + return NULL; + psCond->hInternalMutex = CPLCreateMutex(); + if (psCond->hInternalMutex == NULL) + { + free(psCond); + return NULL; + } + CPLReleaseMutex(psCond->hInternalMutex); + psCond->psWaiterList = NULL; + return (CPLCond*) psCond; +} + +/************************************************************************/ +/* CPLCondWait() */ +/************************************************************************/ + +static void CPLTLSFreeEvent(void* pData) +{ + CloseHandle((HANDLE)pData); +} + +void CPLCondWait( CPLCond *hCond, CPLMutex* hClientMutex ) +{ + Win32Cond* psCond = (Win32Cond*) hCond; + + HANDLE hEvent = (HANDLE) CPLGetTLS(CTLS_WIN32_COND); + if (hEvent == NULL) + { + hEvent = CreateEvent(NULL, /* security attributes */ + 0, /* manual reset = no */ + 0, /* initial state = unsignaled */ + NULL /* no name */); + CPLAssert(hEvent != NULL); + + CPLSetTLSWithFreeFunc(CTLS_WIN32_COND, hEvent, CPLTLSFreeEvent); + } + + /* Insert the waiter into the waiter list of the condition */ + CPLAcquireMutex(psCond->hInternalMutex, 1000.0); + + WaiterItem* psItem = (WaiterItem*)malloc(sizeof(WaiterItem)); + CPLAssert(psItem != NULL); + + psItem->hEvent = hEvent; + psItem->psNext = psCond->psWaiterList; + + psCond->psWaiterList = psItem; + + CPLReleaseMutex(psCond->hInternalMutex); + + /* Release the client mutex before waiting for the event being signaled */ + CPLReleaseMutex(hClientMutex); + + // Ideally we would check that we do not get WAIT_FAILED but it is hard + // to report a failure. + WaitForSingleObject(hEvent, INFINITE); + + /* Reacquire the client mutex */ + CPLAcquireMutex(hClientMutex, 1000.0); +} + +/************************************************************************/ +/* CPLCondSignal() */ +/************************************************************************/ + +void CPLCondSignal( CPLCond *hCond ) +{ + Win32Cond* psCond = (Win32Cond*) hCond; + + /* Signal the first registered event, and remove it from the list */ + CPLAcquireMutex(psCond->hInternalMutex, 1000.0); + + WaiterItem* psIter = psCond->psWaiterList; + if (psIter != NULL) + { + SetEvent(psIter->hEvent); + psCond->psWaiterList = psIter->psNext; + free(psIter); + } + + CPLReleaseMutex(psCond->hInternalMutex); +} + +/************************************************************************/ +/* CPLCondBroadcast() */ +/************************************************************************/ + +void CPLCondBroadcast( CPLCond *hCond ) +{ + Win32Cond* psCond = (Win32Cond*) hCond; + + /* Signal all the registered events, and remove them from the list */ + CPLAcquireMutex(psCond->hInternalMutex, 1000.0); + + WaiterItem* psIter = psCond->psWaiterList; + while (psIter != NULL) + { + WaiterItem* psNext = psIter->psNext; + SetEvent(psIter->hEvent); + free(psIter); + psIter = psNext; + } + psCond->psWaiterList = NULL; + + CPLReleaseMutex(psCond->hInternalMutex); +} + +/************************************************************************/ +/* CPLDestroyCond() */ +/************************************************************************/ + +void CPLDestroyCond( CPLCond *hCond ) +{ + Win32Cond* psCond = (Win32Cond*) hCond; + CPLDestroyMutex(psCond->hInternalMutex); + psCond->hInternalMutex = NULL; + CPLAssert(psCond->psWaiterList == NULL); + free(psCond); +} + +/************************************************************************/ +/* CPLLockFile() */ +/************************************************************************/ + +void *CPLLockFile( const char *pszPath, double dfWaitInSeconds ) + +{ + char *pszLockFilename; + HANDLE hLockFile; + + pszLockFilename = (char *) CPLMalloc(strlen(pszPath) + 30); + sprintf( pszLockFilename, "%s.lock", pszPath ); + + hLockFile = + CreateFile( pszLockFilename, GENERIC_WRITE, 0, NULL,CREATE_NEW, + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_DELETE_ON_CLOSE, NULL ); + + while( GetLastError() == ERROR_ALREADY_EXISTS + && dfWaitInSeconds > 0.0 ) + { + CloseHandle( hLockFile ); + CPLSleep( MIN(dfWaitInSeconds,0.125) ); + dfWaitInSeconds -= 0.125; + + hLockFile = + CreateFile( pszLockFilename, GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_DELETE_ON_CLOSE, + NULL ); + } + + CPLFree( pszLockFilename ); + + if( hLockFile == INVALID_HANDLE_VALUE ) + return NULL; + + if( GetLastError() == ERROR_ALREADY_EXISTS ) + { + CloseHandle( hLockFile ); + return NULL; + } + + return (void *) hLockFile; +} + +/************************************************************************/ +/* CPLUnlockFile() */ +/************************************************************************/ + +void CPLUnlockFile( void *hLock ) + +{ + HANDLE hLockFile = (HANDLE) hLock; + + CloseHandle( hLockFile ); +} + +/************************************************************************/ +/* CPLGetPID() */ +/************************************************************************/ + +GIntBig CPLGetPID() + +{ + return (GIntBig) GetCurrentThreadId(); +} + +/************************************************************************/ +/* CPLStdCallThreadJacket() */ +/************************************************************************/ + +typedef struct { + void *pAppData; + CPLThreadFunc pfnMain; + HANDLE hThread; +} CPLStdCallThreadInfo; + +static DWORD WINAPI CPLStdCallThreadJacket( void *pData ) + +{ + CPLStdCallThreadInfo *psInfo = (CPLStdCallThreadInfo *) pData; + + psInfo->pfnMain( psInfo->pAppData ); + + if (psInfo->hThread == NULL) + CPLFree( psInfo ); /* Only for detached threads */ + + CPLCleanupTLS(); + + return 0; +} + +/************************************************************************/ +/* CPLCreateThread() */ +/* */ +/* The WIN32 CreateThread() call requires an entry point that */ +/* has __stdcall conventions, so we provide a jacket function */ +/* to supply that. */ +/************************************************************************/ + +int CPLCreateThread( CPLThreadFunc pfnMain, void *pThreadArg ) + +{ + HANDLE hThread; + DWORD nThreadId; + CPLStdCallThreadInfo *psInfo; + + psInfo = (CPLStdCallThreadInfo*) CPLCalloc(sizeof(CPLStdCallThreadInfo),1); + psInfo->pAppData = pThreadArg; + psInfo->pfnMain = pfnMain; + psInfo->hThread = NULL; + + hThread = CreateThread( NULL, 0, CPLStdCallThreadJacket, psInfo, + 0, &nThreadId ); + + if( hThread == NULL ) + return -1; + + CloseHandle( hThread ); + + return nThreadId; +} + +/************************************************************************/ +/* CPLCreateJoinableThread() */ +/************************************************************************/ + +CPLJoinableThread* CPLCreateJoinableThread( CPLThreadFunc pfnMain, void *pThreadArg ) + +{ + HANDLE hThread; + DWORD nThreadId; + CPLStdCallThreadInfo *psInfo; + + psInfo = (CPLStdCallThreadInfo*) CPLCalloc(sizeof(CPLStdCallThreadInfo),1); + psInfo->pAppData = pThreadArg; + psInfo->pfnMain = pfnMain; + + hThread = CreateThread( NULL, 0, CPLStdCallThreadJacket, psInfo, + 0, &nThreadId ); + + if( hThread == NULL ) + return NULL; + + psInfo->hThread = hThread; + return (CPLJoinableThread*) psInfo; +} + +/************************************************************************/ +/* CPLJoinThread() */ +/************************************************************************/ + +void CPLJoinThread(CPLJoinableThread* hJoinableThread) +{ + CPLStdCallThreadInfo *psInfo = (CPLStdCallThreadInfo *) hJoinableThread; + + WaitForSingleObject(psInfo->hThread, INFINITE); + CloseHandle( psInfo->hThread ); + CPLFree( psInfo ); +} + +/************************************************************************/ +/* CPLSleep() */ +/************************************************************************/ + +void CPLSleep( double dfWaitInSeconds ) + +{ + Sleep( (DWORD) (dfWaitInSeconds * 1000.0) ); +} + +static int bTLSKeySetup = FALSE; +static DWORD nTLSKey; + +/************************************************************************/ +/* CPLGetTLSList() */ +/************************************************************************/ + +static void **CPLGetTLSList() + +{ + void **papTLSList; + + if( !bTLSKeySetup ) + { + nTLSKey = TlsAlloc(); + if( nTLSKey == TLS_OUT_OF_INDEXES ) + { + CPLEmergencyError( "CPLGetTLSList(): TlsAlloc() failed!" ); + } + bTLSKeySetup = TRUE; + } + + papTLSList = (void **) TlsGetValue( nTLSKey ); + if( papTLSList == NULL ) + { + papTLSList = (void **) VSICalloc(sizeof(void*),CTLS_MAX*2); + if( papTLSList == NULL ) + CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!"); + if( TlsSetValue( nTLSKey, papTLSList ) == 0 ) + { + CPLEmergencyError( "CPLGetTLSList(): TlsSetValue() failed!" ); + } + } + + return papTLSList; +} + +/************************************************************************/ +/* CPLFinalizeTLS() */ +/************************************************************************/ + +void CPLFinalizeTLS() +{ + CPLCleanupTLS(); +} + +/************************************************************************/ +/* CPLCleanupTLS() */ +/************************************************************************/ + +void CPLCleanupTLS() + +{ + void **papTLSList; + + if( !bTLSKeySetup ) + return; + + papTLSList = (void **) TlsGetValue( nTLSKey ); + if( papTLSList == NULL ) + return; + + TlsSetValue( nTLSKey, NULL ); + + CPLCleanupTLSList( papTLSList ); +} + +/* endif CPL_MULTIPROC_WIN32 */ + +#elif defined(CPL_MULTIPROC_PTHREAD) + +#include +#include +#include + + /************************************************************************/ + /* ==================================================================== */ + /* CPL_MULTIPROC_PTHREAD */ + /* */ + /* PTHREAD Implementation of multiprocessing functions. */ + /* ==================================================================== */ + /************************************************************************/ + +/************************************************************************/ +/* CPLGetNumCPUs() */ +/************************************************************************/ + +int CPLGetNumCPUs() +{ +#ifdef _SC_NPROCESSORS_ONLN + return (int)sysconf(_SC_NPROCESSORS_ONLN); +#else + return 1; +#endif +} + +/************************************************************************/ +/* CPLCreateOrAcquireMutex() */ +/************************************************************************/ + +static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER; +static CPLMutex *CPLCreateMutexInternal(int bAlreadyInGlobalLock, int nOptions); + +int CPLCreateOrAcquireMutexEx( CPLMutex **phMutex, double dfWaitInSeconds, + int nOptions ) + +{ + int bSuccess = FALSE; + + pthread_mutex_lock(&global_mutex); + if( *phMutex == NULL ) + { + *phMutex = CPLCreateMutexInternal(TRUE, nOptions); + bSuccess = *phMutex != NULL; + pthread_mutex_unlock(&global_mutex); + } + else + { + pthread_mutex_unlock(&global_mutex); + + bSuccess = CPLAcquireMutex( *phMutex, dfWaitInSeconds ); + } + + return bSuccess; +} + +/************************************************************************/ +/* CPLCreateOrAcquireMutexInternal() */ +/************************************************************************/ + +static +int CPLCreateOrAcquireMutexInternal( CPLLock **phLock, double dfWaitInSeconds, + CPLLockType eType ) +{ + int bSuccess = FALSE; + + pthread_mutex_lock(&global_mutex); + if( *phLock == NULL ) + { + *phLock = (CPLLock*) calloc(1, sizeof(CPLLock)); + if( *phLock ) + { + (*phLock)->eType = eType; + (*phLock)->u.hMutex = CPLCreateMutexInternal(TRUE, + (eType == LOCK_RECURSIVE_MUTEX) ? CPL_MUTEX_RECURSIVE : CPL_MUTEX_ADAPTIVE ); + if( (*phLock)->u.hMutex == NULL ) + { + free(*phLock); + *phLock = NULL; + } + } + bSuccess = *phLock != NULL; + pthread_mutex_unlock(&global_mutex); + } + else + { + pthread_mutex_unlock(&global_mutex); + + bSuccess = CPLAcquireMutex( (*phLock)->u.hMutex, dfWaitInSeconds ); + } + + return bSuccess; +} + +/************************************************************************/ +/* CPLGetThreadingModel() */ +/************************************************************************/ + +const char *CPLGetThreadingModel() + +{ + return "pthread"; +} + +/************************************************************************/ +/* CPLCreateMutex() */ +/************************************************************************/ + +typedef struct _MutexLinkedElt MutexLinkedElt; +struct _MutexLinkedElt +{ + pthread_mutex_t sMutex; + int nOptions; + _MutexLinkedElt *psPrev; + _MutexLinkedElt *psNext; +}; +static MutexLinkedElt* psMutexList = NULL; + +static void CPLInitMutex(MutexLinkedElt* psItem) +{ + /* When an adaptive mutex is required, we can safely fallback to recursive */ + /* mutex if we don't have HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */ +#if defined(HAVE_PTHREAD_MUTEX_ADAPTIVE_NP) + if( psItem->nOptions == CPL_MUTEX_ADAPTIVE ) + { + pthread_mutexattr_t attr; + pthread_mutexattr_init( &attr ); + pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ADAPTIVE_NP ); + pthread_mutex_init( &(psItem->sMutex), &attr ); + } +#endif + +#if defined(PTHREAD_MUTEX_RECURSIVE) || defined(HAVE_PTHREAD_MUTEX_RECURSIVE) + { + pthread_mutexattr_t attr; + pthread_mutexattr_init( &attr ); + pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); + pthread_mutex_init( &(psItem->sMutex), &attr ); + } +/* BSDs have PTHREAD_MUTEX_RECURSIVE as an enum, not a define. */ +/* But they have #define MUTEX_TYPE_COUNTING_FAST PTHREAD_MUTEX_RECURSIVE */ +#elif defined(MUTEX_TYPE_COUNTING_FAST) + { + pthread_mutexattr_t attr; + pthread_mutexattr_init( &attr ); + pthread_mutexattr_settype( &attr, MUTEX_TYPE_COUNTING_FAST ); + pthread_mutex_init( &(psItem->sMutex), &attr ); + } +#elif defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) + pthread_mutex_t tmp_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + psItem->sMutex = tmp_mutex; +#else +#error "Recursive mutexes apparently unsupported, configure --without-threads" +#endif +} + +static CPLMutex *CPLCreateMutexInternal(int bAlreadyInGlobalLock, int nOptions) +{ + MutexLinkedElt* psItem = (MutexLinkedElt *) malloc(sizeof(MutexLinkedElt)); + if (psItem == NULL) + return NULL; + + if( !bAlreadyInGlobalLock ) + pthread_mutex_lock(&global_mutex); + psItem->psPrev = NULL; + psItem->psNext = psMutexList; + if( psMutexList ) + psMutexList->psPrev = psItem; + psMutexList = psItem; + if( !bAlreadyInGlobalLock ) + pthread_mutex_unlock(&global_mutex); + + psItem->nOptions = nOptions; + CPLInitMutex(psItem); + + // mutexes are implicitly acquired when created. + CPLAcquireMutex( (CPLMutex*)psItem, 0.0 ); + + return (CPLMutex*)psItem; +} + +CPLMutex *CPLCreateMutex() +{ + return CPLCreateMutexInternal(FALSE, CPL_MUTEX_RECURSIVE); +} + +CPLMutex *CPLCreateMutexEx(int nOptions) +{ + return CPLCreateMutexInternal(FALSE, nOptions); +} + +/************************************************************************/ +/* CPLAcquireMutex() */ +/************************************************************************/ + +int CPLAcquireMutex( CPLMutex *hMutexIn, CPL_UNUSED double dfWaitInSeconds ) +{ + int err; + + /* we need to add timeout support */ + MutexLinkedElt* psItem = (MutexLinkedElt *) hMutexIn; + err = pthread_mutex_lock( &(psItem->sMutex) ); + + if( err != 0 ) + { + if( err == EDEADLK ) + fprintf(stderr, "CPLAcquireMutex: Error = %d/EDEADLK", err ); + else + fprintf(stderr, "CPLAcquireMutex: Error = %d", err ); + + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* CPLReleaseMutex() */ +/************************************************************************/ + +void CPLReleaseMutex( CPLMutex *hMutexIn ) + +{ + MutexLinkedElt* psItem = (MutexLinkedElt *) hMutexIn; + pthread_mutex_unlock( &(psItem->sMutex) ); +} + +/************************************************************************/ +/* CPLDestroyMutex() */ +/************************************************************************/ + +void CPLDestroyMutex( CPLMutex *hMutexIn ) + +{ + MutexLinkedElt* psItem = (MutexLinkedElt *) hMutexIn; + pthread_mutex_destroy( &(psItem->sMutex) ); + pthread_mutex_lock(&global_mutex); + if( psItem->psPrev ) + psItem->psPrev->psNext = psItem->psNext; + if( psItem->psNext ) + psItem->psNext->psPrev = psItem->psPrev; + if( psItem == psMutexList ) + psMutexList = psItem->psNext; + pthread_mutex_unlock(&global_mutex); + free( hMutexIn ); +} + +/************************************************************************/ +/* CPLReinitAllMutex() */ +/************************************************************************/ + +/* Used by gdalclientserver.cpp just after forking, to avoid */ +/* deadlocks while mixing threads with fork */ +void CPLReinitAllMutex(); +void CPLReinitAllMutex() +{ + MutexLinkedElt* psItem = psMutexList; + while(psItem != NULL ) + { + CPLInitMutex(psItem); + psItem = psItem->psNext; + } + pthread_mutex_t tmp_global_mutex = PTHREAD_MUTEX_INITIALIZER; + global_mutex = tmp_global_mutex; +} + +/************************************************************************/ +/* CPLCreateCond() */ +/************************************************************************/ + +CPLCond *CPLCreateCond() +{ + pthread_cond_t* pCond = (pthread_cond_t* )malloc(sizeof(pthread_cond_t)); + if (pCond) + pthread_cond_init(pCond, NULL); + return (CPLCond*) pCond; +} + +/************************************************************************/ +/* CPLCondWait() */ +/************************************************************************/ + +void CPLCondWait( CPLCond *hCond, CPLMutex* hMutex ) +{ + pthread_cond_t* pCond = (pthread_cond_t* )hCond; + MutexLinkedElt* psItem = (MutexLinkedElt *) hMutex; + pthread_mutex_t * pMutex = &(psItem->sMutex); + pthread_cond_wait(pCond, pMutex); +} + +/************************************************************************/ +/* CPLCondSignal() */ +/************************************************************************/ + +void CPLCondSignal( CPLCond *hCond ) +{ + pthread_cond_t* pCond = (pthread_cond_t* )hCond; + pthread_cond_signal(pCond); +} + +/************************************************************************/ +/* CPLCondBroadcast() */ +/************************************************************************/ + +void CPLCondBroadcast( CPLCond *hCond ) +{ + pthread_cond_t* pCond = (pthread_cond_t* )hCond; + pthread_cond_broadcast(pCond); +} + +/************************************************************************/ +/* CPLDestroyCond() */ +/************************************************************************/ + +void CPLDestroyCond( CPLCond *hCond ) +{ + pthread_cond_t* pCond = (pthread_cond_t* )hCond; + pthread_cond_destroy(pCond); + free(hCond); +} + +/************************************************************************/ +/* CPLLockFile() */ +/* */ +/* This is really a stub implementation, see first */ +/* CPLLockFile() for caveats. */ +/************************************************************************/ + +void *CPLLockFile( const char *pszPath, double dfWaitInSeconds ) + +{ + FILE *fpLock; + char *pszLockFilename; + +/* -------------------------------------------------------------------- */ +/* We use a lock file with a name derived from the file we want */ +/* to lock to represent the file being locked. Note that for */ +/* the stub implementation the target file does not even need */ +/* to exist to be locked. */ +/* -------------------------------------------------------------------- */ + pszLockFilename = (char *) CPLMalloc(strlen(pszPath) + 30); + sprintf( pszLockFilename, "%s.lock", pszPath ); + + fpLock = fopen( pszLockFilename, "r" ); + while( fpLock != NULL && dfWaitInSeconds > 0.0 ) + { + fclose( fpLock ); + CPLSleep( MIN(dfWaitInSeconds,0.5) ); + dfWaitInSeconds -= 0.5; + + fpLock = fopen( pszLockFilename, "r" ); + } + + if( fpLock != NULL ) + { + fclose( fpLock ); + CPLFree( pszLockFilename ); + return NULL; + } + + fpLock = fopen( pszLockFilename, "w" ); + + if( fpLock == NULL ) + { + CPLFree( pszLockFilename ); + return NULL; + } + + fwrite( "held\n", 1, 5, fpLock ); + fclose( fpLock ); + + return pszLockFilename; +} + +/************************************************************************/ +/* CPLUnlockFile() */ +/************************************************************************/ + +void CPLUnlockFile( void *hLock ) + +{ + char *pszLockFilename = (char *) hLock; + + if( hLock == NULL ) + return; + + VSIUnlink( pszLockFilename ); + + CPLFree( pszLockFilename ); +} + +/************************************************************************/ +/* CPLGetPID() */ +/************************************************************************/ + +GIntBig CPLGetPID() + +{ + return (GIntBig) pthread_self(); +} + +/************************************************************************/ +/* CPLStdCallThreadJacket() */ +/************************************************************************/ + +typedef struct { + void *pAppData; + CPLThreadFunc pfnMain; + pthread_t hThread; + int bJoinable; +} CPLStdCallThreadInfo; + +static void *CPLStdCallThreadJacket( void *pData ) + +{ + CPLStdCallThreadInfo *psInfo = (CPLStdCallThreadInfo *) pData; + + psInfo->pfnMain( psInfo->pAppData ); + + if (!psInfo->bJoinable) + CPLFree( psInfo ); + + return NULL; +} + +/************************************************************************/ +/* CPLCreateThread() */ +/* */ +/* The WIN32 CreateThread() call requires an entry point that */ +/* has __stdcall conventions, so we provide a jacket function */ +/* to supply that. */ +/************************************************************************/ + +int CPLCreateThread( CPLThreadFunc pfnMain, void *pThreadArg ) + +{ + + CPLStdCallThreadInfo *psInfo; + pthread_attr_t hThreadAttr; + + psInfo = (CPLStdCallThreadInfo*) CPLCalloc(sizeof(CPLStdCallThreadInfo),1); + psInfo->pAppData = pThreadArg; + psInfo->pfnMain = pfnMain; + psInfo->bJoinable = FALSE; + + pthread_attr_init( &hThreadAttr ); + pthread_attr_setdetachstate( &hThreadAttr, PTHREAD_CREATE_DETACHED ); + if( pthread_create( &(psInfo->hThread), &hThreadAttr, + CPLStdCallThreadJacket, (void *) psInfo ) != 0 ) + { + CPLFree( psInfo ); + return -1; + } + + return 1; /* can we return the actual thread pid? */ +} + +/************************************************************************/ +/* CPLCreateJoinableThread() */ +/************************************************************************/ + +CPLJoinableThread* CPLCreateJoinableThread( CPLThreadFunc pfnMain, void *pThreadArg ) + +{ + CPLStdCallThreadInfo *psInfo; + pthread_attr_t hThreadAttr; + + psInfo = (CPLStdCallThreadInfo*) CPLCalloc(sizeof(CPLStdCallThreadInfo),1); + psInfo->pAppData = pThreadArg; + psInfo->pfnMain = pfnMain; + psInfo->bJoinable = TRUE; + + pthread_attr_init( &hThreadAttr ); + pthread_attr_setdetachstate( &hThreadAttr, PTHREAD_CREATE_JOINABLE ); + if( pthread_create( &(psInfo->hThread), &hThreadAttr, + CPLStdCallThreadJacket, (void *) psInfo ) != 0 ) + { + CPLFree( psInfo ); + return NULL; + } + + return (CPLJoinableThread*) psInfo; +} + +/************************************************************************/ +/* CPLJoinThread() */ +/************************************************************************/ + +void CPLJoinThread(CPLJoinableThread* hJoinableThread) +{ + CPLStdCallThreadInfo *psInfo = (CPLStdCallThreadInfo*) hJoinableThread; + + void* status; + pthread_join( psInfo->hThread, &status); + + CPLFree(psInfo); +} + +/************************************************************************/ +/* CPLSleep() */ +/************************************************************************/ + +void CPLSleep( double dfWaitInSeconds ) + +{ + struct timespec sRequest, sRemain; + + sRequest.tv_sec = (int) floor(dfWaitInSeconds); + sRequest.tv_nsec = (int) ((dfWaitInSeconds - sRequest.tv_sec)*1000000000); + nanosleep( &sRequest, &sRemain ); +} + +static pthread_key_t oTLSKey; +static pthread_once_t oTLSKeySetup = PTHREAD_ONCE_INIT; + +/************************************************************************/ +/* CPLMake_key() */ +/************************************************************************/ + +static void CPLMake_key() + +{ + if( pthread_key_create( &oTLSKey, (void (*)(void*)) CPLCleanupTLSList ) != 0 ) + { + CPLError( CE_Fatal, CPLE_AppDefined, "pthread_key_create() failed!" ); + } +} + +/************************************************************************/ +/* CPLFinalizeTLS() */ +/************************************************************************/ + +void CPLFinalizeTLS() +{ + CPLCleanupTLS(); + /* See #5509 for the explanation why this may be needed */ + pthread_key_delete(oTLSKey); +} + +/************************************************************************/ +/* CPLCleanupTLS() */ +/************************************************************************/ + +void CPLCleanupTLS() + +{ + void **papTLSList; + + papTLSList = (void **) pthread_getspecific( oTLSKey ); + if( papTLSList == NULL ) + return; + + pthread_setspecific( oTLSKey, NULL ); + + CPLCleanupTLSList( papTLSList ); +} + +/************************************************************************/ +/* CPLGetTLSList() */ +/************************************************************************/ + +static void **CPLGetTLSList() + +{ + void **papTLSList; + + if ( pthread_once(&oTLSKeySetup, CPLMake_key) != 0 ) + { + CPLEmergencyError( "CPLGetTLSList(): pthread_once() failed!" ); + } + + papTLSList = (void **) pthread_getspecific( oTLSKey ); + if( papTLSList == NULL ) + { + papTLSList = (void **) VSICalloc(sizeof(void*),CTLS_MAX*2); + if( papTLSList == NULL ) + CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!"); + if( pthread_setspecific( oTLSKey, papTLSList ) != 0 ) + { + CPLEmergencyError( + "CPLGetTLSList(): pthread_setspecific() failed!" ); + } + } + + return papTLSList; +} + +/************************************************************************/ +/* CPLCreateSpinLock() */ +/************************************************************************/ + +#if defined(HAVE_PTHREAD_SPINLOCK) +#define HAVE_SPINLOCK_IMPL + +struct _CPLSpinLock +{ + pthread_spinlock_t spin; +}; + +CPLSpinLock *CPLCreateSpinLock( void ) +{ + CPLSpinLock* psSpin = (CPLSpinLock*)malloc(sizeof(CPLSpinLock)); + if( pthread_spin_init(&(psSpin->spin), PTHREAD_PROCESS_PRIVATE) == 0 ) + { + return psSpin; + } + else + { + free(psSpin); + return NULL; + } +} + +/************************************************************************/ +/* CPLAcquireSpinLock() */ +/************************************************************************/ + +int CPLAcquireSpinLock( CPLSpinLock* psSpin ) +{ + return pthread_spin_lock( &(psSpin->spin) ) == 0; +} + +/************************************************************************/ +/* CPLCreateOrAcquireSpinLockInternal() */ +/************************************************************************/ + +int CPLCreateOrAcquireSpinLockInternal( CPLLock** ppsLock ) +{ + pthread_mutex_lock(&global_mutex); + if( *ppsLock == NULL ) + { + *ppsLock = (CPLLock*) calloc(1, sizeof(CPLLock)); + if( *ppsLock != NULL ) + { + (*ppsLock)->eType = LOCK_SPIN; + (*ppsLock)->u.hSpinLock = CPLCreateSpinLock(); + if( (*ppsLock)->u.hSpinLock == NULL ) + { + free(*ppsLock); + *ppsLock = NULL; + } + } + } + pthread_mutex_unlock(&global_mutex); + return( *ppsLock != NULL && CPLAcquireSpinLock( (*ppsLock)->u.hSpinLock ) ); +} + +/************************************************************************/ +/* CPLReleaseSpinLock() */ +/************************************************************************/ + +void CPLReleaseSpinLock( CPLSpinLock* psSpin ) +{ + pthread_spin_unlock( &(psSpin->spin) ); +} + +/************************************************************************/ +/* CPLDestroySpinLock() */ +/************************************************************************/ + +void CPLDestroySpinLock( CPLSpinLock* psSpin ) +{ + pthread_spin_destroy( &(psSpin->spin) ); + free( psSpin ); +} +#endif /* HAVE_PTHREAD_SPINLOCK */ + +#endif /* def CPL_MULTIPROC_PTHREAD */ + +/************************************************************************/ +/* CPLGetTLS() */ +/************************************************************************/ + +void *CPLGetTLS( int nIndex ) + +{ + void** papTLSList = CPLGetTLSList(); + + CPLAssert( nIndex >= 0 && nIndex < CTLS_MAX ); + + return papTLSList[nIndex]; +} + +/************************************************************************/ +/* CPLSetTLS() */ +/************************************************************************/ + +void CPLSetTLS( int nIndex, void *pData, int bFreeOnExit ) + +{ + CPLSetTLSWithFreeFunc(nIndex, pData, (bFreeOnExit) ? CPLFree : NULL); +} + +/************************************************************************/ +/* CPLSetTLSWithFreeFunc() */ +/************************************************************************/ + +/* Warning : the CPLTLSFreeFunc must not in any case directly or indirectly */ +/* use or fetch any TLS data, or a terminating thread will hang ! */ +void CPLSetTLSWithFreeFunc( int nIndex, void *pData, CPLTLSFreeFunc pfnFree ) + +{ + void **papTLSList = CPLGetTLSList(); + + CPLAssert( nIndex >= 0 && nIndex < CTLS_MAX ); + + papTLSList[nIndex] = pData; + papTLSList[CTLS_MAX + nIndex] = (void*) pfnFree; +} + +#ifndef HAVE_SPINLOCK_IMPL + +/* No spinlock specific API ? Fallback to mutex */ + +/************************************************************************/ +/* CPLCreateSpinLock() */ +/************************************************************************/ + +CPLSpinLock *CPLCreateSpinLock( void ) +{ + CPLSpinLock* psSpin = (CPLSpinLock *)CPLCreateMutex(); + if( psSpin ) + CPLReleaseSpinLock(psSpin); + return psSpin; +} + +/************************************************************************/ +/* CPLCreateOrAcquireSpinLock() */ +/************************************************************************/ + +int CPLCreateOrAcquireSpinLockInternal( CPLLock** ppsLock ) +{ + return CPLCreateOrAcquireMutexInternal( ppsLock, 1000, LOCK_ADAPTIVE_MUTEX ); +} + +/************************************************************************/ +/* CPLAcquireSpinLock() */ +/************************************************************************/ + +int CPLAcquireSpinLock( CPLSpinLock* psSpin ) +{ + return CPLAcquireMutex( (CPLMutex*)psSpin, 1000 ); +} + +/************************************************************************/ +/* CPLReleaseSpinLock() */ +/************************************************************************/ + +void CPLReleaseSpinLock( CPLSpinLock* psSpin ) +{ + CPLReleaseMutex( (CPLMutex*)psSpin ); +} + +/************************************************************************/ +/* CPLDestroySpinLock() */ +/************************************************************************/ + +void CPLDestroySpinLock( CPLSpinLock* psSpin ) +{ + CPLDestroyMutex( (CPLMutex*)psSpin ); +} + +#endif /* HAVE_SPINLOCK_IMPL */ + + +/************************************************************************/ +/* CPLCreateLock() */ +/************************************************************************/ + +CPLLock *CPLCreateLock( CPLLockType eType ) +{ + switch( eType ) + { + case LOCK_RECURSIVE_MUTEX: + case LOCK_ADAPTIVE_MUTEX: + { + CPLMutex* hMutex = CPLCreateMutexEx( + (eType == LOCK_RECURSIVE_MUTEX) ? CPL_MUTEX_RECURSIVE : CPL_MUTEX_ADAPTIVE); + if(!hMutex) + return NULL; + CPLReleaseMutex(hMutex); + CPLLock* psLock = (CPLLock*)malloc(sizeof(CPLLock)); + psLock->eType = eType; + psLock->u.hMutex = hMutex; +#ifdef DEBUG_CONTENTION + psLock->bDebugPerf = FALSE; +#endif + return psLock; + } + case LOCK_SPIN: + { + CPLSpinLock* hSpinLock = CPLCreateSpinLock(); + if( !hSpinLock ) + return NULL; + CPLLock* psLock = (CPLLock*)malloc(sizeof(CPLLock)); + psLock->eType = eType; + psLock->u.hSpinLock = hSpinLock; +#ifdef DEBUG_CONTENTION + psLock->bDebugPerf = FALSE; +#endif + return psLock; + } + default: + CPLAssert(0); + return NULL; + } +} + +/************************************************************************/ +/* CPLCreateOrAcquireLock() */ +/************************************************************************/ + +int CPLCreateOrAcquireLock( CPLLock** ppsLock, CPLLockType eType ) +{ + int ret; +#ifdef DEBUG_CONTENTION + GUIntBig nStartTime = 0; + if( (*ppsLock) && (*ppsLock)->bDebugPerf ) + nStartTime = CPLrdtsc(); +#endif + switch( eType ) + { + case LOCK_RECURSIVE_MUTEX: + case LOCK_ADAPTIVE_MUTEX: + { + ret = CPLCreateOrAcquireMutexInternal( ppsLock, 1000, eType); + break; + } + case LOCK_SPIN: + { + ret = CPLCreateOrAcquireSpinLockInternal( ppsLock ); + break; + } + default: + CPLAssert(0); + return FALSE; + } +#ifdef DEBUG_CONTENTION + if( ret && (*ppsLock)->bDebugPerf ) + { + (*ppsLock)->nStartTime = nStartTime; + } +#endif + return ret; +} + +/************************************************************************/ +/* CPLAcquireLock() */ +/************************************************************************/ + +int CPLAcquireLock( CPLLock* psLock ) +{ +#ifdef DEBUG_CONTENTION + if( psLock->bDebugPerf ) + psLock->nStartTime = CPLrdtsc(); +#endif + if( psLock->eType == LOCK_SPIN ) + return CPLAcquireSpinLock( psLock->u.hSpinLock ); + else + return CPLAcquireMutex( psLock->u.hMutex, 1000 ); +} + +/************************************************************************/ +/* CPLReleaseLock() */ +/************************************************************************/ + +void CPLReleaseLock( CPLLock* psLock ) +{ +#ifdef DEBUG_CONTENTION + int bHitMaxDiff = FALSE; + GIntBig nMaxDiff = 0; + double dfAvgDiff = 0; + GUIntBig nIters = 0; + if( psLock->bDebugPerf && psLock->nStartTime ) + { + GUIntBig nStopTime = CPLrdtscp(); + GIntBig nDiffTime = (GIntBig)(nStopTime - psLock->nStartTime); + if( nDiffTime > psLock->nMaxDiff ) + { + bHitMaxDiff = TRUE; + psLock->nMaxDiff = nDiffTime; + } + nMaxDiff = psLock->nMaxDiff; + psLock->nIters ++; + nIters = psLock->nIters; + psLock->dfAvgDiff += (nDiffTime - psLock->dfAvgDiff) / nIters; + dfAvgDiff = psLock->dfAvgDiff; + } +#endif + if( psLock->eType == LOCK_SPIN ) + CPLReleaseSpinLock( psLock->u.hSpinLock ); + else + CPLReleaseMutex( psLock->u.hMutex ); +#ifdef DEBUG_CONTENTION + if( psLock->bDebugPerf && (bHitMaxDiff || (psLock->nIters % 1000000) == (1000000-1) )) + { + CPLDebug("LOCK", "Lock contention : max = " CPL_FRMT_GIB ", avg = %.0f", + nMaxDiff, dfAvgDiff); + } +#endif +} + +/************************************************************************/ +/* CPLDestroyLock() */ +/************************************************************************/ + +void CPLDestroyLock( CPLLock* psLock ) +{ + if( psLock->eType == LOCK_SPIN ) + CPLDestroySpinLock( psLock->u.hSpinLock ); + else + CPLDestroyMutex( psLock->u.hMutex ); + free( psLock ); +} + +/************************************************************************/ +/* CPLLockSetDebugPerf() */ +/************************************************************************/ + +void CPLLockSetDebugPerf( CPLLock* psLock, int bEnableIn ) +{ +#ifdef DEBUG_CONTENTION + psLock->bDebugPerf = bEnableIn; +#else + if( bEnableIn ) + { + static int bOnce = FALSE; + if( !bOnce ) + { + bOnce = TRUE; + CPLDebug("LOCK", "DEBUG_CONTENTION not available"); + } + } +#endif +} + +/************************************************************************/ +/* CPLLockHolder() */ +/************************************************************************/ + +CPLLockHolder::CPLLockHolder( CPLLock **phLock, + CPLLockType eType, + const char *pszFileIn, + int nLineIn ) + +{ +#ifndef MUTEX_NONE + pszFile = pszFileIn; + nLine = nLineIn; + +#ifdef DEBUG_MUTEX + /* + * XXX: There is no way to use CPLDebug() here because it works with + * mutexes itself so we will fall in infinite recursion. Good old + * fprintf() will do the job right. + */ + fprintf( stderr, + "CPLLockHolder: Request %p for pid %ld at %d/%s.\n", + *phLock, (long) CPLGetPID(), nLine, pszFile ); +#endif + + if( !CPLCreateOrAcquireLock( phLock, eType ) ) + { + fprintf( stderr, "CPLLockHolder: Failed to acquire lock!\n" ); + hLock = NULL; + } + else + { +#ifdef DEBUG_MUTEX + fprintf( stderr, + "CPLLockHolder: Acquired %p for pid %ld at %d/%s.\n", + *phLock, (long) CPLGetPID(), nLine, pszFile ); +#endif + + hLock = *phLock; + } +#endif /* ndef MUTEX_NONE */ +} + +/************************************************************************/ +/* CPLLockHolder() */ +/************************************************************************/ + +CPLLockHolder::CPLLockHolder( CPLLock *hLockIn, + const char *pszFileIn, + int nLineIn ) + +{ +#ifndef MUTEX_NONE + pszFile = pszFileIn; + nLine = nLineIn; + hLock = hLockIn; + + if( hLock != NULL ) + { + if( !CPLAcquireLock( hLock ) ) + { + fprintf( stderr, "CPLLockHolder: Failed to acquire lock!\n" ); + hLock = NULL; + } + } +#endif /* ndef MUTEX_NONE */ +} + +/************************************************************************/ +/* ~CPLLockHolder() */ +/************************************************************************/ + +CPLLockHolder::~CPLLockHolder() + +{ +#ifndef MUTEX_NONE + if( hLock != NULL ) + { +#ifdef DEBUG_MUTEX + fprintf( stderr, + "~CPLLockHolder: Release %p for pid %ld at %d/%s.\n", + hLock, (long) CPLGetPID(), nLine, pszFile ); +#endif + CPLReleaseLock( hLock ); + } +#endif /* ndef MUTEX_NONE */ +} diff --git a/bazaar/plugin/gdal/port/cpl_multiproc.h b/bazaar/plugin/gdal/port/cpl_multiproc.h new file mode 100644 index 000000000..0b39b93d8 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_multiproc.h @@ -0,0 +1,228 @@ +/********************************************************************** + * $Id: cpl_multiproc.h 28470 2015-02-12 21:01:32Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: CPL Multi-Threading, and process handling portability functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_MULTIPROC_H_INCLUDED_ +#define _CPL_MULTIPROC_H_INCLUDED_ + +#include "cpl_port.h" + +/* +** There are three primary implementations of the multi-process support +** controlled by one of CPL_MULTIPROC_WIN32, CPL_MULTIPROC_PTHREAD or +** CPL_MULTIPROC_STUB being defined. If none are defined, the stub +** implementation will be used. +*/ + +#if defined(WIN32) && !defined(CPL_MULTIPROC_STUB) +# define CPL_MULTIPROC_WIN32 +/* MinGW can have pthread support, so disable it to avoid issues */ +/* in cpl_multiproc.cpp */ +# undef CPL_MULTIPROC_PTHREAD +#endif + +#if !defined(CPL_MULTIPROC_WIN32) && !defined(CPL_MULTIPROC_PTHREAD) \ + && !defined(CPL_MULTIPROC_STUB) && !defined(CPL_MULTIPROC_NONE) +# define CPL_MULTIPROC_STUB +#endif + +CPL_C_START + +typedef void (*CPLThreadFunc)(void *); + +void CPL_DLL *CPLLockFile( const char *pszPath, double dfWaitInSeconds ); +void CPL_DLL CPLUnlockFile( void *hLock ); + +#ifdef DEBUG +typedef struct _CPLMutex CPLMutex; +typedef struct _CPLCond CPLCond; +typedef struct _CPLJoinableThread CPLJoinableThread; +#else +#define CPLMutex void +#define CPLCond void +#define CPLJoinableThread void +#endif + +/* Options for CPLCreateMutexEx() and CPLCreateOrAcquireMutexEx() */ +#define CPL_MUTEX_RECURSIVE 0 +#define CPL_MUTEX_ADAPTIVE 1 + +CPLMutex CPL_DLL *CPLCreateMutex( void ); /* returned acquired */ +CPLMutex CPL_DLL *CPLCreateMutexEx( int nOptions ); /* returned acquired */ +int CPL_DLL CPLCreateOrAcquireMutex( CPLMutex **, double dfWaitInSeconds ); +int CPL_DLL CPLCreateOrAcquireMutexEx( CPLMutex **, double dfWaitInSeconds, int nOptions ); +int CPL_DLL CPLAcquireMutex( CPLMutex *hMutex, double dfWaitInSeconds ); +void CPL_DLL CPLReleaseMutex( CPLMutex *hMutex ); +void CPL_DLL CPLDestroyMutex( CPLMutex *hMutex ); +void CPL_DLL CPLCleanupMasterMutex( void ); + +CPLCond CPL_DLL *CPLCreateCond( void ); +void CPL_DLL CPLCondWait( CPLCond *hCond, CPLMutex* hMutex ); +void CPL_DLL CPLCondSignal( CPLCond *hCond ); +void CPL_DLL CPLCondBroadcast( CPLCond *hCond ); +void CPL_DLL CPLDestroyCond( CPLCond *hCond ); + +GIntBig CPL_DLL CPLGetPID( void ); +int CPL_DLL CPLCreateThread( CPLThreadFunc pfnMain, void *pArg ); +CPLJoinableThread CPL_DLL* CPLCreateJoinableThread( CPLThreadFunc pfnMain, void *pArg ); +void CPL_DLL CPLJoinThread(CPLJoinableThread* hJoinableThread); +void CPL_DLL CPLSleep( double dfWaitInSeconds ); + +const char CPL_DLL *CPLGetThreadingModel( void ); + +int CPL_DLL CPLGetNumCPUs( void ); + + +typedef struct _CPLLock CPLLock; + +/* Currently LOCK_ADAPTIVE_MUTEX is Linux-only and LOCK_SPIN only available */ +/* on systems with pthread_spinlock API (so not MacOsX). If a requested type */ +/* isn't available, it fallbacks to LOCK_RECURSIVE_MUTEX */ +typedef enum +{ + LOCK_RECURSIVE_MUTEX, + LOCK_ADAPTIVE_MUTEX, + LOCK_SPIN +} CPLLockType; + +CPLLock CPL_DLL *CPLCreateLock( CPLLockType eType ); /* returned NON acquired */ +int CPL_DLL CPLCreateOrAcquireLock( CPLLock**, CPLLockType eType ); +int CPL_DLL CPLAcquireLock( CPLLock* ); +void CPL_DLL CPLReleaseLock( CPLLock* ); +void CPL_DLL CPLDestroyLock( CPLLock* ); +void CPL_DLL CPLLockSetDebugPerf( CPLLock*, int bEnableIn ); /* only available on x86/x86_64 with GCC for now */ + + +CPL_C_END + +#ifdef __cplusplus + +/* Instanciates the mutex if not already done. The parameter x should be a (void**) */ +#define CPLMutexHolderD(x) CPLMutexHolder oHolder(x,1000.0,__FILE__,__LINE__); + +/* Instanciates the mutex with options if not already done. */ +/* The parameter x should be a (void**) */ +#define CPLMutexHolderExD(x, nOptions) CPLMutexHolder oHolder(x,1000.0,__FILE__,__LINE__,nOptions); + +/* This variant assumes the the mutex has already been created. If not, it will */ +/* be a no-op. The parameter x should be a (void*) */ +#define CPLMutexHolderOptionalLockD(x) CPLMutexHolder oHolder(x,1000.0,__FILE__,__LINE__); + +class CPL_DLL CPLMutexHolder +{ + private: + CPLMutex *hMutex; + const char *pszFile; + int nLine; + + public: + + /* Instanciates the mutex if not already done */ + CPLMutexHolder( CPLMutex **phMutex, double dfWaitInSeconds = 1000.0, + const char *pszFile = __FILE__, + int nLine = __LINE__, + int nOptions = CPL_MUTEX_RECURSIVE); + + /* This variant assumes the the mutex has already been created. If not, it will */ + /* be a no-op */ + CPLMutexHolder( CPLMutex* hMutex, double dfWaitInSeconds = 1000.0, + const char *pszFile = __FILE__, + int nLine = __LINE__ ); + + ~CPLMutexHolder(); +}; + +/* Instanciates the lock if not already done. The parameter x should be a (CPLLock**) */ +#define CPLLockHolderD(x, eType) CPLLockHolder oHolder(x,eType,__FILE__,__LINE__); + +/* This variant assumes the the lock has already been created. If not, it will */ +/* be a no-op. The parameter should be (CPLLock*) */ +#define CPLLockHolderOptionalLockD(x) CPLLockHolder oHolder(x,__FILE__,__LINE__); + +class CPL_DLL CPLLockHolder +{ + private: + CPLLock *hLock; + const char *pszFile; + int nLine; + + public: + + /* Instanciates the lock if not already done */ + CPLLockHolder( CPLLock **phSpin, CPLLockType eType, + const char *pszFile = __FILE__, + int nLine = __LINE__); + + /* This variant assumes the the lock has already been created. If not, it will */ + /* be a no-op */ + CPLLockHolder( CPLLock* hSpin, + const char *pszFile = __FILE__, + int nLine = __LINE__ ); + + ~CPLLockHolder(); +}; + + +#endif /* def __cplusplus */ + +/* -------------------------------------------------------------------- */ +/* Thread local storage. */ +/* -------------------------------------------------------------------- */ + +#define CTLS_RLBUFFERINFO 1 /* cpl_conv.cpp */ +#define CTLS_WIN32_COND 2 /* cpl_multiproc.cpp */ +#define CTLS_CSVTABLEPTR 3 /* cpl_csv.cpp */ +#define CTLS_CSVDEFAULTFILENAME 4 /* cpl_csv.cpp */ +#define CTLS_ERRORCONTEXT 5 /* cpl_error.cpp */ +#define CTLS_GDALDATASET_REC_PROTECT_MAP 6 /* gdaldataset.cpp */ +#define CTLS_PATHBUF 7 /* cpl_path.cpp */ +#define CTLS_UNUSED3 8 +#define CTLS_UNUSED4 9 +#define CTLS_CPLSPRINTF 10 /* cpl_string.h */ +#define CTLS_RESPONSIBLEPID 11 /* gdaldataset.cpp */ +#define CTLS_VERSIONINFO 12 /* gdal_misc.cpp */ +#define CTLS_VERSIONINFO_LICENCE 13 /* gdal_misc.cpp */ +#define CTLS_CONFIGOPTIONS 14 /* cpl_conv.cpp */ +#define CTLS_FINDFILE 15 /* cpl_findfile.cpp */ + +#define CTLS_MAX 32 + +CPL_C_START +void CPL_DLL * CPLGetTLS( int nIndex ); +void CPL_DLL CPLSetTLS( int nIndex, void *pData, int bFreeOnExit ); + +/* Warning : the CPLTLSFreeFunc must not in any case directly or indirectly */ +/* use or fetch any TLS data, or a terminating thread will hang ! */ +typedef void (*CPLTLSFreeFunc)( void* pData ); +void CPL_DLL CPLSetTLSWithFreeFunc( int nIndex, void *pData, CPLTLSFreeFunc pfnFree ); + +void CPL_DLL CPLCleanupTLS( void ); +CPL_C_END + +#endif /* _CPL_MULTIPROC_H_INCLUDED_ */ diff --git a/bazaar/plugin/gdal/port/cpl_odbc.cpp b/bazaar/plugin/gdal/port/cpl_odbc.cpp new file mode 100644 index 000000000..0dbb4a854 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_odbc.cpp @@ -0,0 +1,1828 @@ +/****************************************************************************** + * $Id: cpl_odbc.cpp 29027 2015-04-26 18:29:41Z tamas $ + * + * Project: OGR ODBC Driver + * Purpose: Declarations for ODBC Access Cover API. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * Copyright (c) 2008, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_odbc.h" +#include "cpl_vsi.h" +#include "cpl_string.h" +#include "cpl_error.h" + + +#ifndef WIN32CE /* ODBC is not supported on Windows CE. */ + +CPL_CVSID("$Id: cpl_odbc.cpp 29027 2015-04-26 18:29:41Z tamas $"); + +#ifndef SQLColumns_TABLE_CAT +#define SQLColumns_TABLE_CAT 1 +#define SQLColumns_TABLE_SCHEM 2 +#define SQLColumns_TABLE_NAME 3 +#define SQLColumns_COLUMN_NAME 4 +#define SQLColumns_DATA_TYPE 5 +#define SQLColumns_TYPE_NAME 6 +#define SQLColumns_COLUMN_SIZE 7 +#define SQLColumns_BUFFER_LENGTH 8 +#define SQLColumns_DECIMAL_DIGITS 9 +#define SQLColumns_NUM_PREC_RADIX 10 +#define SQLColumns_NULLABLE 11 +#define SQLColumns_REMARKS 12 +#define SQLColumns_COLUMN_DEF 13 +#define SQLColumns_SQL_DATA_TYPE 14 +#define SQLColumns_SQL_DATETIME_SUB 15 +#define SQLColumns_CHAR_OCTET_LENGTH 16 +#define SQLColumns_ORDINAL_POSITION 17 +#define SQLColumns_IS_NULLABLE 18 +#endif /* ndef SQLColumns_TABLE_CAT */ + +/************************************************************************/ +/* CPLODBCDriverInstaller() */ +/************************************************************************/ + +CPLODBCDriverInstaller::CPLODBCDriverInstaller() + : m_nUsageCount(0) +{ + memset( m_szPathOut, '\0', ODBC_FILENAME_MAX ); + memset( m_szError, '\0', SQL_MAX_MESSAGE_LENGTH ); +} + +/************************************************************************/ +/* InstallDriver() */ +/************************************************************************/ + +int CPLODBCDriverInstaller::InstallDriver( const char* pszDriver, + CPL_UNUSED const char* pszPathIn, + WORD fRequest ) +{ + CPLAssert( NULL != pszDriver ); + + // Try to install driver to system-wide location + if ( FALSE == SQLInstallDriverEx( pszDriver, NULL, m_szPathOut, + ODBC_FILENAME_MAX, NULL, fRequest, + &m_nUsageCount ) ) + { + const WORD nErrorNum = 1; // TODO - a function param? + CPL_UNUSED RETCODE cRet = SQL_ERROR; + + // Failure is likely related to no write permissions to + // system-wide default location, so try to install to HOME + + static char* pszEnvIni = NULL; + if (pszEnvIni == NULL) + { + // Read HOME location + char* pszEnvHome = NULL; + pszEnvHome = getenv("HOME"); + + CPLAssert( NULL != pszEnvHome ); + CPLDebug( "ODBC", "HOME=%s", pszEnvHome ); + + // Set ODBCSYSINI variable pointing to HOME location + pszEnvIni = (char *)CPLMalloc( strlen(pszEnvHome) + 12 ); + + sprintf( pszEnvIni, "ODBCSYSINI=%s", pszEnvHome ); + /* a 'man putenv' shows that we cannot free pszEnvIni */ + /* because the pointer is used directly by putenv in old glibc */ + putenv( pszEnvIni ); + + CPLDebug( "ODBC", "%s", pszEnvIni ); + } + + // Try to install ODBC driver in new location + if ( FALSE == SQLInstallDriverEx( pszDriver, NULL, m_szPathOut, + ODBC_FILENAME_MAX, NULL, fRequest, + &m_nUsageCount ) ) + { + cRet = SQLInstallerError( nErrorNum, &m_nErrorCode, + m_szError, SQL_MAX_MESSAGE_LENGTH, NULL ); + CPLAssert( SQL_SUCCESS == cRet || SQL_SUCCESS_WITH_INFO == cRet ); + + // FAIL + return FALSE; + } + } + + // SUCCESS + return TRUE; +} + +/************************************************************************/ +/* RemoveDriver() */ +/************************************************************************/ + +int CPLODBCDriverInstaller::RemoveDriver( const char* pszDriverName, int fRemoveDSN ) +{ + CPLAssert( NULL != pszDriverName ); + + if ( FALSE == SQLRemoveDriver( pszDriverName, fRemoveDSN, &m_nUsageCount ) ) + { + const WORD nErrorNum = 1; // TODO - a function param? + + // Retrieve error code and message + SQLInstallerError( nErrorNum, &m_nErrorCode, + m_szError, SQL_MAX_MESSAGE_LENGTH, NULL ); + + return FALSE; + } + + // SUCCESS + return TRUE; +} + +/************************************************************************/ +/* CPLODBCSession() */ +/************************************************************************/ + +CPLODBCSession::CPLODBCSession() + +{ + m_szLastError[0] = '\0'; + m_hEnv = NULL; + m_hDBC = NULL; + m_bInTransaction = FALSE; + m_bAutoCommit = TRUE; +} + +/************************************************************************/ +/* ~CPLODBCSession() */ +/************************************************************************/ + +CPLODBCSession::~CPLODBCSession() + +{ + CloseSession(); +} + +/************************************************************************/ +/* CloseSession() */ +/************************************************************************/ + +int CPLODBCSession::CloseSession() + +{ + if( m_hDBC!=NULL ) + { + if ( IsInTransaction() ) + CPLError( CE_Warning, CPLE_AppDefined, "Closing session with active transactions." ); + CPLDebug( "ODBC", "SQLDisconnect()" ); + SQLDisconnect( m_hDBC ); + SQLFreeConnect( m_hDBC ); + m_hDBC = NULL; + } + + if( m_hEnv!=NULL ) + { + SQLFreeEnv( m_hEnv ); + m_hEnv = NULL; + } + + return TRUE; +} + +/************************************************************************/ +/* ClearTransaction() */ +/************************************************************************/ + +int CPLODBCSession::ClearTransaction() + +{ +#if (ODBCVER >= 0x0300) + + if (m_bAutoCommit) + return TRUE; + + SQLUINTEGER bAutoCommit; + /* See if we already in manual commit mode */ + if ( Failed( SQLGetConnectAttr( m_hDBC, SQL_ATTR_AUTOCOMMIT, &bAutoCommit, sizeof(SQLUINTEGER), NULL) ) ) + return FALSE; + + if (bAutoCommit == SQL_AUTOCOMMIT_OFF) + { + /* switch the connection to auto commit mode (default) */ + if( Failed( SQLSetConnectAttr( m_hDBC, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0 ) ) ) + return FALSE; + } + + m_bInTransaction = FALSE; + m_bAutoCommit = TRUE; + +#endif + return TRUE; +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +int CPLODBCSession::BeginTransaction() + +{ +#if (ODBCVER >= 0x0300) + + SQLUINTEGER bAutoCommit; + /* See if we already in manual commit mode */ + if ( Failed( SQLGetConnectAttr( m_hDBC, SQL_ATTR_AUTOCOMMIT, &bAutoCommit, sizeof(SQLUINTEGER), NULL) ) ) + return FALSE; + + if (bAutoCommit == SQL_AUTOCOMMIT_ON) + { + /* switch the connection to manual commit mode */ + if( Failed( SQLSetConnectAttr( m_hDBC, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, 0 ) ) ) + return FALSE; + } + + m_bInTransaction = TRUE; + m_bAutoCommit = FALSE; + +#endif + return TRUE; +} + +/************************************************************************/ +/* CommitTransaction() */ +/************************************************************************/ + +int CPLODBCSession::CommitTransaction() + +{ +#if (ODBCVER >= 0x0300) + + if (m_bInTransaction) + { + if( Failed( SQLEndTran( SQL_HANDLE_DBC, m_hDBC, SQL_COMMIT ) ) ) + { + return FALSE; + } + m_bInTransaction = FALSE; + } + +#endif + return TRUE; +} + +/************************************************************************/ +/* RollbackTransaction() */ +/************************************************************************/ + +int CPLODBCSession::RollbackTransaction() + +{ +#if (ODBCVER >= 0x0300) + + if (m_bInTransaction) + { + m_bInTransaction = FALSE; + + if( Failed( SQLEndTran( SQL_HANDLE_DBC, m_hDBC, SQL_ROLLBACK ) ) ) + { + return FALSE; + } + } + +#endif + return TRUE; +} + +/************************************************************************/ +/* Failed() */ +/* */ +/* Test if a return code indicates failure, return TRUE if that */ +/* is the case. Also update error text. */ +/************************************************************************/ + +int CPLODBCSession::Failed( int nRetCode, HSTMT hStmt ) + +{ + SQLCHAR achSQLState[SQL_MAX_MESSAGE_LENGTH]; + SQLINTEGER nNativeError; + SQLSMALLINT nTextLength=0; + + m_szLastError[0] = '\0'; + + if( nRetCode == SQL_SUCCESS || nRetCode == SQL_SUCCESS_WITH_INFO ) + return FALSE; + + SQLError( m_hEnv, m_hDBC, hStmt, achSQLState, &nNativeError, + (SQLCHAR *) m_szLastError, sizeof(m_szLastError)-1, + &nTextLength ); + m_szLastError[nTextLength] = '\0'; + + if( nRetCode == SQL_ERROR && m_bInTransaction ) + RollbackTransaction(); + + return TRUE; +} + +/************************************************************************/ +/* EstablishSession() */ +/************************************************************************/ + +/** + * Connect to database and logon. + * + * @param pszDSN The name of the DSN being used to connect. This is not + * optional. + * + * @param pszUserid the userid to logon as, may be NULL if not not required, + * or provided by the DSN. + * + * @param pszPassword the password to logon with. May be NULL if not required + * or provided by the DSN. + * + * @return TRUE on success or FALSE on failure. Call GetLastError() to get + * details on failure. + */ + +int CPLODBCSession::EstablishSession( const char *pszDSN, + const char *pszUserid, + const char *pszPassword ) + +{ + CloseSession(); + + if( Failed( SQLAllocEnv( &m_hEnv ) ) ) + return FALSE; + + if( Failed( SQLAllocConnect( m_hEnv, &m_hDBC ) ) ) + { + CloseSession(); + return FALSE; + } + + SQLSetConnectOption( m_hDBC,SQL_LOGIN_TIMEOUT,30 ); + + if( pszUserid == NULL ) + pszUserid = ""; + if( pszPassword == NULL ) + pszPassword = ""; + + int bFailed; + if( strstr(pszDSN,"=") != NULL ) + { + SQLCHAR szOutConnString[1024]; + SQLSMALLINT nOutConnStringLen = 0; + + CPLDebug( "ODBC", "SQLDriverConnect(%s)", pszDSN ); + bFailed = Failed( + SQLDriverConnect( m_hDBC, NULL, + (SQLCHAR *) pszDSN, (SQLSMALLINT)strlen(pszDSN), + szOutConnString, sizeof(szOutConnString), + &nOutConnStringLen, SQL_DRIVER_NOPROMPT ) ); + } + else + { + CPLDebug( "ODBC", "SQLConnect(%s)", pszDSN ); + bFailed = Failed( + SQLConnect( m_hDBC, (SQLCHAR *) pszDSN, SQL_NTS, + (SQLCHAR *) pszUserid, SQL_NTS, + (SQLCHAR *) pszPassword, SQL_NTS ) ); + } + + if( bFailed ) + { + CPLDebug( "ODBC", "... failed: %s", GetLastError() ); + CloseSession(); + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* GetLastError() */ +/************************************************************************/ + +/** + * Returns the last ODBC error message. + * + * @return pointer to an internal buffer with the error message in it. + * Do not free or alter. Will be an empty (but not NULL) string if there is + * no pending error info. + */ + +const char *CPLODBCSession::GetLastError() + +{ + return m_szLastError; +} + +/************************************************************************/ +/* ==================================================================== */ +/* CPLODBCStatement */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* CPLODBCStatement() */ +/************************************************************************/ + +CPLODBCStatement::CPLODBCStatement( CPLODBCSession *poSession ) + +{ + m_poSession = poSession; + + if( Failed( + SQLAllocStmt( poSession->GetConnection(), &m_hStmt ) ) ) + { + m_hStmt = NULL; + return; + } + + m_nColCount = 0; + m_papszColNames = NULL; + m_panColType = NULL; + m_papszColTypeNames = NULL; + m_panColSize = NULL; + m_panColPrecision = NULL; + m_panColNullable = NULL; + m_papszColColumnDef = NULL; + + m_papszColValues = NULL; + m_panColValueLengths = NULL; + + m_pszStatement = NULL; + m_nStatementMax = 0; + m_nStatementLen = 0; +} + +/************************************************************************/ +/* ~CPLODBCStatement() */ +/************************************************************************/ + +CPLODBCStatement::~CPLODBCStatement() + +{ + Clear(); + + if( m_hStmt != NULL ) + SQLFreeStmt( m_hStmt, SQL_DROP ); +} + +/************************************************************************/ +/* ExecuteSQL() */ +/************************************************************************/ + +/** + * Execute an SQL statement. + * + * This method will execute the passed (or stored) SQL statement, + * and initialize information about the resultset if there is one. + * If a NULL statement is passed, the internal stored statement that + * has been previously set via Append() or Appendf() calls will be used. + * + * @param pszStatement the SQL statement to execute, or NULL if the + * internally saved one should be used. + * + * @return TRUE on success or FALSE if there is an error. Error details + * can be fetched with OGRODBCSession::GetLastError(). + */ + +int CPLODBCStatement::ExecuteSQL( const char *pszStatement ) + +{ + if( m_poSession == NULL || m_hStmt == NULL ) + { + // we should post an error. + return FALSE; + } + + if( pszStatement != NULL ) + { + Clear(); + Append( pszStatement ); + } + +#if (ODBCVER >= 0x0300) + + if ( !m_poSession->IsInTransaction() ) + { + /* commit pending transactions and set to autocommit mode*/ + m_poSession->ClearTransaction(); + } + +#endif + + if( Failed( + SQLExecDirect( m_hStmt, (SQLCHAR *) m_pszStatement, SQL_NTS ) ) ) + return FALSE; + + return CollectResultsInfo(); +} + +/************************************************************************/ +/* CollectResultsInfo() */ +/************************************************************************/ + +int CPLODBCStatement::CollectResultsInfo() + +{ + if( m_poSession == NULL || m_hStmt == NULL ) + { + // we should post an error. + return FALSE; + } + + if( Failed( SQLNumResultCols(m_hStmt,&m_nColCount) ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Allocate per column information. */ +/* -------------------------------------------------------------------- */ + m_papszColNames = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1)); + m_papszColValues = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1)); + m_panColValueLengths = (_SQLLEN *) CPLCalloc(sizeof(_SQLLEN),(m_nColCount+1)); + + m_panColType = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount); + m_papszColTypeNames = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1)); + m_panColSize = (_SQLULEN *) CPLCalloc(sizeof(_SQLULEN),m_nColCount); + m_panColPrecision = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount); + m_panColNullable = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount); + m_papszColColumnDef = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1)); + +/* -------------------------------------------------------------------- */ +/* Fetch column descriptions. */ +/* -------------------------------------------------------------------- */ + for( SQLUSMALLINT iCol = 0; iCol < m_nColCount; iCol++ ) + { + SQLCHAR szName[256]; + SQLSMALLINT nNameLength = 0; + + if ( Failed( SQLDescribeCol(m_hStmt, iCol+1, + szName, sizeof(szName), &nNameLength, + m_panColType + iCol, + m_panColSize + iCol, + m_panColPrecision + iCol, + m_panColNullable + iCol) ) ) + return FALSE; + + szName[nNameLength] = '\0'; // Paranoid; the string should be + // null-terminated by the driver + m_papszColNames[iCol] = CPLStrdup((const char*)szName); + + // SQLDescribeCol() fetches just a subset of column attributes. + // In addition to above data we need data type name. + if ( Failed( SQLColAttribute(m_hStmt, iCol + 1, SQL_DESC_TYPE_NAME, + szName, sizeof(szName), + &nNameLength, NULL) ) ) + return FALSE; + + szName[nNameLength] = '\0'; // Paranoid + m_papszColTypeNames[iCol] = CPLStrdup((const char*)szName); + +// CPLDebug( "ODBC", "%s %s %d", m_papszColNames[iCol], +// szName, m_panColType[iCol] ); + } + + return TRUE; +} + +/************************************************************************/ +/* GetRowCountAffected() */ +/************************************************************************/ + +int CPLODBCStatement::GetRowCountAffected() +{ + SQLLEN nResultCount=0; + SQLRowCount( m_hStmt, &nResultCount ); + + return (int)nResultCount; +} + +/************************************************************************/ +/* GetColCount() */ +/************************************************************************/ + +/** + * Fetch the resultset column count. + * + * @return the column count, or zero if there is no resultset. + */ + +int CPLODBCStatement::GetColCount() + +{ + return m_nColCount; +} + +/************************************************************************/ +/* GetColName() */ +/************************************************************************/ + +/** + * Fetch a column name. + * + * @param iCol the zero based column index. + * + * @return NULL on failure (out of bounds column), or a pointer to an + * internal copy of the column name. + */ + +const char *CPLODBCStatement::GetColName( int iCol ) + +{ + if( iCol < 0 || iCol >= m_nColCount ) + return NULL; + else + return m_papszColNames[iCol]; +} + +/************************************************************************/ +/* GetColType() */ +/************************************************************************/ + +/** + * Fetch a column data type. + * + * The return type code is a an ODBC SQL_ code, one of SQL_UNKNOWN_TYPE, + * SQL_CHAR, SQL_NUMERIC, SQL_DECIMAL, SQL_INTEGER, SQL_SMALLINT, SQL_FLOAT, + * SQL_REAL, SQL_DOUBLE, SQL_DATETIME, SQL_VARCHAR, SQL_TYPE_DATE, + * SQL_TYPE_TIME, SQL_TYPE_TIMESTAMPT. + * + * @param iCol the zero based column index. + * + * @return type code or -1 if the column is illegal. + */ + +short CPLODBCStatement::GetColType( int iCol ) + +{ + if( iCol < 0 || iCol >= m_nColCount ) + return -1; + else + return m_panColType[iCol]; +} + +/************************************************************************/ +/* GetColTypeName() */ +/************************************************************************/ + +/** + * Fetch a column data type name. + * + * Returns data source-dependent data type name; for example, "CHAR", + * "VARCHAR", "MONEY", "LONG VARBINAR", or "CHAR ( ) FOR BIT DATA". + * + * @param iCol the zero based column index. + * + * @return NULL on failure (out of bounds column), or a pointer to an + * internal copy of the column dat type name. + */ + +const char *CPLODBCStatement::GetColTypeName( int iCol ) + +{ + if( iCol < 0 || iCol >= m_nColCount ) + return NULL; + else + return m_papszColTypeNames[iCol]; +} + +/************************************************************************/ +/* GetColSize() */ +/************************************************************************/ + +/** + * Fetch the column width. + * + * @param iCol the zero based column index. + * + * @return column width, zero for unknown width columns. + */ + +short CPLODBCStatement::GetColSize( int iCol ) + +{ + if( iCol < 0 || iCol >= m_nColCount ) + return -1; + else + return (short) m_panColSize[iCol]; +} + +/************************************************************************/ +/* GetColPrecision() */ +/************************************************************************/ + +/** + * Fetch the column precision. + * + * @param iCol the zero based column index. + * + * @return column precision, may be zero or the same as column size for + * columns to which it does not apply. + */ + +short CPLODBCStatement::GetColPrecision( int iCol ) + +{ + if( iCol < 0 || iCol >= m_nColCount ) + return -1; + else + return m_panColPrecision[iCol]; +} + +/************************************************************************/ +/* GetColNullable() */ +/************************************************************************/ + +/** + * Fetch the column nullability. + * + * @param iCol the zero based column index. + * + * @return TRUE if the column may contains or FALSE otherwise. + */ + +short CPLODBCStatement::GetColNullable( int iCol ) + +{ + if( iCol < 0 || iCol >= m_nColCount ) + return -1; + else + return m_panColNullable[iCol]; +} + +/************************************************************************/ +/* GetColColumnDef() */ +/************************************************************************/ + +/** + * Fetch a column default value. + * + * Returns the default value of a column. + * + * @param iCol the zero based column index. + * + * @return NULL if the default value is not dpecified + * or the internal copy of the default value. + */ + +const char *CPLODBCStatement::GetColColumnDef( int iCol ) + +{ + if( iCol < 0 || iCol >= m_nColCount ) + return NULL; + else + return m_papszColColumnDef[iCol]; +} + +/************************************************************************/ +/* Fetch() */ +/************************************************************************/ + +/** + * Fetch a new record. + * + * Requests the next row in the current resultset using the SQLFetchScroll() + * call. Note that many ODBC drivers only support the default forward + * fetching one record at a time. Only SQL_FETCH_NEXT (the default) should + * be considered reliable on all drivers. + * + * Currently it isn't clear how to determine whether an error or a normal + * out of data condition has occured if Fetch() fails. + * + * @param nOrientation One of SQL_FETCH_NEXT, SQL_FETCH_LAST, SQL_FETCH_PRIOR, + * SQL_FETCH_ABSOLUTE, or SQL_FETCH_RELATIVE (default is SQL_FETCH_NEXT). + * + * @param nOffset the offset (number of records), ignored for some + * orientations. + * + * @return TRUE if a new row is successfully fetched, or FALSE if not. + */ + +int CPLODBCStatement::Fetch( int nOrientation, int nOffset ) + +{ + ClearColumnData(); + + if( m_hStmt == NULL || m_nColCount < 1 ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Fetch a new row. Note that some brain dead drives (such as */ +/* the unixodbc text file driver) don't implement */ +/* SQLScrollFetch(), so we try to stick to SQLFetch() if we */ +/* can). */ +/* -------------------------------------------------------------------- */ + SQLRETURN nRetCode; + + if( nOrientation == SQL_FETCH_NEXT && nOffset == 0 ) + { + nRetCode = SQLFetch( m_hStmt ); + if( Failed(nRetCode) ) + { + if ( nRetCode != SQL_NO_DATA ) + { + CPLError( CE_Failure, CPLE_AppDefined, "%s", + m_poSession->GetLastError() ); + } + return FALSE; + } + } + else + { + nRetCode = SQLFetchScroll(m_hStmt, (SQLSMALLINT) nOrientation, nOffset); + if( Failed(nRetCode) ) + { + if ( nRetCode == SQL_NO_DATA ) + { + CPLError( CE_Failure, CPLE_AppDefined, "%s", + m_poSession->GetLastError() ); + } + return FALSE; + } + } + +/* -------------------------------------------------------------------- */ +/* Pull out all the column values. */ +/* -------------------------------------------------------------------- */ + SQLSMALLINT iCol; + + for( iCol = 0; iCol < m_nColCount; iCol++ ) + { + char szWrkData[513]; + _SQLLEN cbDataLen; + SQLSMALLINT nFetchType = GetTypeMapping( m_panColType[iCol] ); + + // Handle values other than WCHAR and BINARY as CHAR. + if( nFetchType != SQL_C_BINARY && nFetchType != SQL_C_WCHAR ) + nFetchType = SQL_C_CHAR; + + szWrkData[0] = '\0'; + szWrkData[sizeof(szWrkData)-1] = '\0'; + + nRetCode = SQLGetData( m_hStmt, iCol + 1, nFetchType, + szWrkData, sizeof(szWrkData)-1, + &cbDataLen ); + +/* SQLGetData() is giving garbage values in the first 4 bytes of cbDataLen * + * in some architectures. Converting it to (int) discards the unnecessary * + * bytes. This should not be a problem unless the buffer size reaches * + * 2GB. (#3385) */ + cbDataLen = (int) cbDataLen; + + if( Failed( nRetCode ) ) + { + if ( nRetCode == SQL_NO_DATA ) + { + CPLError( CE_Failure, CPLE_AppDefined, "%s", + m_poSession->GetLastError() ); + } + return FALSE; + } + + if( cbDataLen == SQL_NULL_DATA ) + { + m_papszColValues[iCol] = NULL; + m_panColValueLengths[iCol] = 0; + } + + // assume big result: should check for state=SQLSATE 01004. + else if( nRetCode == SQL_SUCCESS_WITH_INFO ) + { + if( cbDataLen >= (_SQLLEN)(sizeof(szWrkData)-1) ) + { + cbDataLen = (_SQLLEN)(sizeof(szWrkData)-1); + if (nFetchType == SQL_C_CHAR) + while ((cbDataLen > 1) && (szWrkData[cbDataLen - 1] == 0)) + --cbDataLen; // trimming the extra terminators: bug 990 + else if (nFetchType == SQL_C_WCHAR) + while ((cbDataLen > 1) && (szWrkData[cbDataLen - 1] == 0) + && (szWrkData[cbDataLen - 2] == 0)) + cbDataLen -= 2; // trimming the extra terminators + + } + + m_papszColValues[iCol] = (char *) CPLMalloc(cbDataLen+2); + memcpy( m_papszColValues[iCol], szWrkData, cbDataLen ); + m_papszColValues[iCol][cbDataLen] = '\0'; + m_papszColValues[iCol][cbDataLen+1] = '\0'; + m_panColValueLengths[iCol] = cbDataLen; + + while( TRUE ) + { + _SQLLEN nChunkLen; + + nRetCode = SQLGetData( m_hStmt, (SQLUSMALLINT) iCol+1, + nFetchType, + szWrkData, sizeof(szWrkData)-1, + &cbDataLen ); + if( nRetCode == SQL_NO_DATA ) + break; + + if( Failed( nRetCode ) ) + { + CPLError( CE_Failure, CPLE_AppDefined, "%s", + m_poSession->GetLastError() ); + return FALSE; + } + + if( cbDataLen >= (int) (sizeof(szWrkData) - 1) + || cbDataLen == SQL_NO_TOTAL ) + { + nChunkLen = sizeof(szWrkData)-1; + if (nFetchType == SQL_C_CHAR) + while ( (nChunkLen > 1) + && (szWrkData[nChunkLen - 1] == 0) ) + --nChunkLen; // trimming the extra terminators + else if (nFetchType == SQL_C_WCHAR) + while ( (nChunkLen > 1) + && (szWrkData[nChunkLen - 1] == 0) + && (szWrkData[nChunkLen - 2] == 0) ) + nChunkLen -= 2; // trimming the extra terminators + } + else + nChunkLen = cbDataLen; + szWrkData[nChunkLen] = '\0'; + + m_papszColValues[iCol] = (char *) + CPLRealloc( m_papszColValues[iCol], + m_panColValueLengths[iCol] + nChunkLen + 2 ); + memcpy( m_papszColValues[iCol] + m_panColValueLengths[iCol], + szWrkData, nChunkLen ); + m_panColValueLengths[iCol] += nChunkLen; + m_papszColValues[iCol][m_panColValueLengths[iCol]] = '\0'; + m_papszColValues[iCol][m_panColValueLengths[iCol]+1] = '\0'; + } + } + else + { + m_panColValueLengths[iCol] = cbDataLen; + m_papszColValues[iCol] = (char *) CPLMalloc(cbDataLen+2); + memcpy( m_papszColValues[iCol], szWrkData, cbDataLen ); + m_papszColValues[iCol][cbDataLen] = '\0'; + m_papszColValues[iCol][cbDataLen+1] = '\0'; + } + + // Trim white space off end, if there is any. + if( nFetchType == SQL_C_CHAR && m_papszColValues[iCol] != NULL ) + { + char *pszTarget = m_papszColValues[iCol]; + size_t iEnd = strlen(pszTarget); + + while ( iEnd > 0 && pszTarget[iEnd - 1] == ' ' ) + pszTarget[--iEnd] = '\0'; + } + + // Convert WCHAR to UTF-8, assuming the WCHAR is UCS-2. + if( nFetchType == SQL_C_WCHAR && m_papszColValues[iCol] != NULL + && m_panColValueLengths[iCol] > 0 ) + { + wchar_t *pwszSrc = (wchar_t *) m_papszColValues[iCol]; + + m_papszColValues[iCol] = + CPLRecodeFromWChar( pwszSrc, CPL_ENC_UCS2, CPL_ENC_UTF8 ); + m_panColValueLengths[iCol] = strlen(m_papszColValues[iCol]); + + CPLFree( pwszSrc ); + } + } + + return TRUE; +} + +/************************************************************************/ +/* GetColData() */ +/************************************************************************/ + +/** + * Fetch column data. + * + * Fetches the data contents of the requested column for the currently loaded + * row. The result is returned as a string regardless of the column type. + * NULL is returned if an illegal column is given, or if the actual column + * is "NULL". + * + * @param iCol the zero based column to fetch. + * + * @param pszDefault the value to return if the column does not exist, or is + * NULL. Defaults to NULL. + * + * @return pointer to internal column data or NULL on failure. + */ + +const char *CPLODBCStatement::GetColData( int iCol, const char *pszDefault ) + +{ + if( iCol < 0 || iCol >= m_nColCount ) + return pszDefault; + else if( m_papszColValues[iCol] != NULL ) + return m_papszColValues[iCol]; + else + return pszDefault; +} + +/************************************************************************/ +/* GetColData() */ +/************************************************************************/ + +/** + * Fetch column data. + * + * Fetches the data contents of the requested column for the currently loaded + * row. The result is returned as a string regardless of the column type. + * NULL is returned if an illegal column is given, or if the actual column + * is "NULL". + * + * @param pszColName the name of the column requested. + * + * @param pszDefault the value to return if the column does not exist, or is + * NULL. Defaults to NULL. + * + * @return pointer to internal column data or NULL on failure. + */ + +const char *CPLODBCStatement::GetColData( const char *pszColName, + const char *pszDefault ) + +{ + int iCol = GetColId( pszColName ); + + if( iCol == -1 ) + return pszDefault; + else + return GetColData( iCol, pszDefault ); +} + +/************************************************************************/ +/* GetColDataLength() */ +/************************************************************************/ + +int CPLODBCStatement::GetColDataLength( int iCol ) + +{ + if( iCol < 0 || iCol >= m_nColCount ) + return 0; + else if( m_papszColValues[iCol] != NULL ) + return (int)m_panColValueLengths[iCol]; + else + return 0; +} + +/************************************************************************/ +/* GetColId() */ +/************************************************************************/ + +/** + * Fetch column index. + * + * Gets the column index corresponding with the passed name. The + * name comparisons are case insensitive. + * + * @param pszColName the name to search for. + * + * @return the column index, or -1 if not found. + */ + +int CPLODBCStatement::GetColId( const char *pszColName ) + +{ + for( SQLSMALLINT iCol = 0; iCol < m_nColCount; iCol++ ) + if( EQUAL(pszColName, m_papszColNames[iCol]) ) + return iCol; + + return -1; +} + +/************************************************************************/ +/* ClearColumnData() */ +/************************************************************************/ + +void CPLODBCStatement::ClearColumnData() + +{ + if( m_nColCount > 0 ) + { + for( int iCol = 0; iCol < m_nColCount; iCol++ ) + { + if( m_papszColValues[iCol] != NULL ) + { + CPLFree( m_papszColValues[iCol] ); + m_papszColValues[iCol] = NULL; + } + } + } +} + +/************************************************************************/ +/* Failed() */ +/************************************************************************/ + +int CPLODBCStatement::Failed( int nResultCode ) + +{ + if( m_poSession != NULL ) + return m_poSession->Failed( nResultCode, m_hStmt ); + + return TRUE; +} + +/************************************************************************/ +/* Append(const char *) */ +/************************************************************************/ + +/** + * Append text to internal command. + * + * The passed text is appended to the internal SQL command text. + * + * @param pszText text to append. + */ + +void CPLODBCStatement::Append( const char *pszText ) + +{ + size_t nTextLen = strlen(pszText); + + if( m_nStatementMax < m_nStatementLen + nTextLen + 1 ) + { + m_nStatementMax = (m_nStatementLen + nTextLen) * 2 + 100; + if( m_pszStatement == NULL ) + { + m_pszStatement = (char *) VSIMalloc(m_nStatementMax); + m_pszStatement[0] = '\0'; + } + else + { + m_pszStatement = (char *) VSIRealloc(m_pszStatement, m_nStatementMax); + } + } + + strcpy( m_pszStatement + m_nStatementLen, pszText ); + m_nStatementLen += nTextLen; +} + +/************************************************************************/ +/* AppendEscaped(const char *) */ +/************************************************************************/ + +/** + * Append text to internal command. + * + * The passed text is appended to the internal SQL command text after + * escaping any special characters so it can be used as a character string + * in an SQL statement. + * + * @param pszText text to append. + */ + +void CPLODBCStatement::AppendEscaped( const char *pszText ) + +{ + size_t iIn, iOut ,nTextLen = strlen(pszText); + char *pszEscapedText = (char *) VSIMalloc(nTextLen*2 + 1); + + for( iIn = 0, iOut = 0; iIn < nTextLen; iIn++ ) + { + switch( pszText[iIn] ) + { + case '\'': + case '\\': + pszEscapedText[iOut++] = '\\'; + pszEscapedText[iOut++] = pszText[iIn]; + break; + + default: + pszEscapedText[iOut++] = pszText[iIn]; + break; + } + } + + pszEscapedText[iOut] = '\0'; + + Append( pszEscapedText ); + CPLFree( pszEscapedText ); +} + +/************************************************************************/ +/* Append(int) */ +/************************************************************************/ + +/** + * Append to internal command. + * + * The passed value is formatted and appended to the internal SQL command text. + * + * @param nValue value to append to the command. + */ + +void CPLODBCStatement::Append( int nValue ) + +{ + char szFormattedValue[100]; + + sprintf( szFormattedValue, "%d", nValue ); + Append( szFormattedValue ); +} + +/************************************************************************/ +/* Append(double) */ +/************************************************************************/ + +/** + * Append to internal command. + * + * The passed value is formatted and appended to the internal SQL command text. + * + * @param dfValue value to append to the command. + */ + +void CPLODBCStatement::Append( double dfValue ) + +{ + char szFormattedValue[100]; + + sprintf( szFormattedValue, "%24g", dfValue ); + Append( szFormattedValue ); +} + +/************************************************************************/ +/* Appendf() */ +/************************************************************************/ + +/** + * Append to internal command. + * + * The passed format is used to format other arguments and the result is + * appended to the internal command text. Long results may not be formatted + * properly, and should be appended with the direct Append() methods. + * + * @param pszFormat printf() style format string. + * + * @return FALSE if formatting fails dueto result being too large. + */ + +int CPLODBCStatement::Appendf( const char *pszFormat, ... ) + +{ + va_list args; + char szFormattedText[8000]; + int bSuccess; + + va_start( args, pszFormat ); +#if defined(HAVE_VSNPRINTF) + bSuccess = vsnprintf( szFormattedText, sizeof(szFormattedText)-1, + pszFormat, args ) < (int) sizeof(szFormattedText)-1; +#else + vsprintf( szFormattedText, pszFormat, args ); + bSuccess = TRUE; +#endif + va_end( args ); + + if( bSuccess ) + Append( szFormattedText ); + + return bSuccess; +} + +/************************************************************************/ +/* Clear() */ +/************************************************************************/ + +/** + * Clear internal command text and result set definitions. + */ + +void CPLODBCStatement::Clear() + +{ + /* Closing the cursor if opened */ + if( m_hStmt != NULL ) + SQLFreeStmt( m_hStmt, SQL_CLOSE ); + + ClearColumnData(); + + if( m_pszStatement != NULL ) + { + CPLFree( m_pszStatement ); + m_pszStatement = NULL; + } + + m_nStatementLen = 0; + m_nStatementMax = 0; + + m_nColCount = 0; + + if( m_papszColNames ) + { + CPLFree( m_panColType ); + m_panColType = NULL; + + CSLDestroy( m_papszColTypeNames ); + m_papszColTypeNames = NULL; + + CPLFree( m_panColSize ); + m_panColSize = NULL; + + CPLFree( m_panColPrecision ); + m_panColPrecision = NULL; + + CPLFree( m_panColNullable ); + m_panColNullable = NULL; + + CSLDestroy( m_papszColColumnDef ); + m_papszColColumnDef = NULL; + + CSLDestroy( m_papszColNames ); + m_papszColNames = NULL; + + CPLFree( m_papszColValues ); + m_papszColValues = NULL; + + CPLFree( m_panColValueLengths ); + m_panColValueLengths = NULL; + } + +} + +/************************************************************************/ +/* GetColumns() */ +/************************************************************************/ + +/** + * Fetch column definitions for a table. + * + * The SQLColumn() method is used to fetch the definitions for the columns + * of a table (or other queriable object such as a view). The column + * definitions are digested and used to populate the CPLODBCStatement + * column definitions essentially as if a "SELECT * FROM tablename" had + * been done; however, no resultset will be available. + * + * @param pszTable the name of the table to query information on. This + * should not be empty. + * + * @param pszCatalog the catalog to find the table in, use NULL (the + * default) if no catalog is available. + * + * @param pszSchema the schema to find the table in, use NULL (the + * default) if no schema is available. + * + * @return TRUE on success or FALSE on failure. + */ + +int CPLODBCStatement::GetColumns( const char *pszTable, + const char *pszCatalog, + const char *pszSchema ) + +{ +#ifdef notdef + if( pszCatalog == NULL ) + pszCatalog = ""; + if( pszSchema == NULL ) + pszSchema = ""; +#endif + +#if (ODBCVER >= 0x0300) + + if ( !m_poSession->IsInTransaction() ) + { + /* commit pending transactions and set to autocommit mode*/ + m_poSession->ClearTransaction(); + } + +#endif +/* -------------------------------------------------------------------- */ +/* Fetch columns resultset for this table. */ +/* -------------------------------------------------------------------- */ + if( Failed( SQLColumns( m_hStmt, + (SQLCHAR *) pszCatalog, SQL_NTS, + (SQLCHAR *) pszSchema, SQL_NTS, + (SQLCHAR *) pszTable, SQL_NTS, + (SQLCHAR *) NULL /* "" */, SQL_NTS ) ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Allocate per column information. */ +/* -------------------------------------------------------------------- */ +#ifdef notdef + // SQLRowCount() is too unreliable (with unixodbc on AIX for instance) + // so we now avoid it. + SQLINTEGER nResultCount=0; + + if( Failed(SQLRowCount( m_hStmt, &nResultCount ) ) ) + nResultCount = 0; + + if( nResultCount < 1 ) + m_nColCount = 500; // Hopefully lots. + else + m_nColCount = nResultCount; +#endif + + m_nColCount = 500; + + m_papszColNames = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1)); + m_papszColValues = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1)); + + m_panColType = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount); + m_papszColTypeNames = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1)); + m_panColSize = (_SQLULEN *) CPLCalloc(sizeof(_SQLULEN),m_nColCount); + m_panColPrecision = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount); + m_panColNullable = (SQLSMALLINT *) CPLCalloc(sizeof(SQLSMALLINT),m_nColCount); + m_papszColColumnDef = (char **) CPLCalloc(sizeof(char *),(m_nColCount+1)); + +/* -------------------------------------------------------------------- */ +/* Establish columns to use for key information. */ +/* -------------------------------------------------------------------- */ + SQLUSMALLINT iCol; + + for( iCol = 0; iCol < m_nColCount; iCol++ ) + { + char szWrkData[8193]; + _SQLLEN cbDataLen; + + if( Failed( SQLFetch( m_hStmt ) ) ) + { + m_nColCount = iCol; + break; + } + + szWrkData[0] = '\0'; + + SQLGetData( m_hStmt, SQLColumns_COLUMN_NAME, SQL_C_CHAR, + szWrkData, sizeof(szWrkData)-1, &cbDataLen ); + m_papszColNames[iCol] = CPLStrdup(szWrkData); + + SQLGetData( m_hStmt, SQLColumns_DATA_TYPE, SQL_C_CHAR, + szWrkData, sizeof(szWrkData)-1, &cbDataLen ); + m_panColType[iCol] = (short) atoi(szWrkData); + + SQLGetData( m_hStmt, SQLColumns_TYPE_NAME, SQL_C_CHAR, + szWrkData, sizeof(szWrkData)-1, &cbDataLen ); + m_papszColTypeNames[iCol] = CPLStrdup(szWrkData); + + SQLGetData( m_hStmt, SQLColumns_COLUMN_SIZE, SQL_C_CHAR, + szWrkData, sizeof(szWrkData)-1, &cbDataLen ); + m_panColSize[iCol] = atoi(szWrkData); + + SQLGetData( m_hStmt, SQLColumns_DECIMAL_DIGITS, SQL_C_CHAR, + szWrkData, sizeof(szWrkData)-1, &cbDataLen ); + m_panColPrecision[iCol] = (short) atoi(szWrkData); + + SQLGetData( m_hStmt, SQLColumns_NULLABLE, SQL_C_CHAR, + szWrkData, sizeof(szWrkData)-1, &cbDataLen ); + m_panColNullable[iCol] = atoi(szWrkData) == SQL_NULLABLE; +#if (ODBCVER >= 0x0300) + SQLGetData( m_hStmt, SQLColumns_COLUMN_DEF, SQL_C_CHAR, + szWrkData, sizeof(szWrkData)-1, &cbDataLen ); + if (cbDataLen > 0) + m_papszColColumnDef[iCol] = CPLStrdup(szWrkData); +#endif + } + + return TRUE; +} + +/************************************************************************/ +/* GetPrimaryKeys() */ +/************************************************************************/ + +/** + * Fetch primary keys for a table. + * + * The SQLPrimaryKeys() function is used to fetch a list of fields + * forming the primary key. The result is returned as a result set matching + * the SQLPrimaryKeys() function result set. The 4th column in the result + * set is the column name of the key, and if the result set contains only + * one record then that single field will be the complete primary key. + * + * @param pszTable the name of the table to query information on. This + * should not be empty. + * + * @param pszCatalog the catalog to find the table in, use NULL (the + * default) if no catalog is available. + * + * @param pszSchema the schema to find the table in, use NULL (the + * default) if no schema is available. + * + * @return TRUE on success or FALSE on failure. + */ + +int CPLODBCStatement::GetPrimaryKeys( const char *pszTable, + const char *pszCatalog, + const char *pszSchema ) + +{ + if( pszCatalog == NULL ) + pszCatalog = ""; + if( pszSchema == NULL ) + pszSchema = ""; + +#if (ODBCVER >= 0x0300) + + if ( !m_poSession->IsInTransaction() ) + { + /* commit pending transactions and set to autocommit mode*/ + m_poSession->ClearTransaction(); + } + +#endif + +/* -------------------------------------------------------------------- */ +/* Fetch columns resultset for this table. */ +/* -------------------------------------------------------------------- */ + if( Failed( SQLPrimaryKeys( m_hStmt, + (SQLCHAR *) pszCatalog, SQL_NTS, + (SQLCHAR *) pszSchema, SQL_NTS, + (SQLCHAR *) pszTable, SQL_NTS ) ) ) + return FALSE; + else + return CollectResultsInfo(); +} + +/************************************************************************/ +/* GetTables() */ +/************************************************************************/ + +/** + * Fetch tables in database. + * + * The SQLTables() function is used to fetch a list tables in the + * database. The result is returned as a result set matching + * the SQLTables() function result set. The 3rd column in the result + * set is the table name. Only tables of type "TABLE" are returned. + * + * @param pszCatalog the catalog to find the table in, use NULL (the + * default) if no catalog is available. + * + * @param pszSchema the schema to find the table in, use NULL (the + * default) if no schema is available. + * + * @return TRUE on success or FALSE on failure. + */ + +int CPLODBCStatement::GetTables( const char *pszCatalog, + const char *pszSchema ) + +{ + CPLDebug( "ODBC", "CatalogNameL: %s\nSchema name: %s\n", + pszCatalog, pszSchema ); + +#if (ODBCVER >= 0x0300) + + if ( !m_poSession->IsInTransaction() ) + { + /* commit pending transactions and set to autocommit mode*/ + m_poSession->ClearTransaction(); + } + +#endif + +/* -------------------------------------------------------------------- */ +/* Fetch columns resultset for this table. */ +/* -------------------------------------------------------------------- */ + if( Failed( SQLTables( m_hStmt, + (SQLCHAR *) pszCatalog, SQL_NTS, + (SQLCHAR *) pszSchema, SQL_NTS, + (SQLCHAR *) NULL, SQL_NTS, + (SQLCHAR *) "'TABLE','VIEW'", SQL_NTS ) ) ) + return FALSE; + else + return CollectResultsInfo(); +} + +/************************************************************************/ +/* DumpResult() */ +/************************************************************************/ + +/** + * Dump resultset to file. + * + * The contents of the current resultset are dumped in a simply formatted + * form to the provided file. If requested, the schema definition will + * be written first. + * + * @param fp the file to write to. stdout or stderr are acceptable. + * + * @param bShowSchema TRUE to force writing schema information for the rowset + * before the rowset data itself. Default is FALSE. + */ + +void CPLODBCStatement::DumpResult( FILE *fp, int bShowSchema ) + +{ + int iCol; + +/* -------------------------------------------------------------------- */ +/* Display schema */ +/* -------------------------------------------------------------------- */ + if( bShowSchema ) + { + fprintf( fp, "Column Definitions:\n" ); + for( iCol = 0; iCol < GetColCount(); iCol++ ) + { + fprintf( fp, " %2d: %-24s ", iCol, GetColName(iCol) ); + if( GetColPrecision(iCol) > 0 + && GetColPrecision(iCol) != GetColSize(iCol) ) + fprintf( fp, " Size:%3d.%d", + GetColSize(iCol), GetColPrecision(iCol) ); + else + fprintf( fp, " Size:%5d", GetColSize(iCol) ); + + CPLString osType = GetTypeName( GetColType(iCol) ); + fprintf( fp, " Type:%s", osType.c_str() ); + if( GetColNullable(iCol) ) + fprintf( fp, " NULLABLE" ); + fprintf( fp, "\n" ); + } + fprintf( fp, "\n" ); + } + +/* -------------------------------------------------------------------- */ +/* Display results */ +/* -------------------------------------------------------------------- */ + int iRecord = 0; + while( Fetch() ) + { + fprintf( fp, "Record %d\n", iRecord++ ); + + for( iCol = 0; iCol < GetColCount(); iCol++ ) + { + fprintf( fp, " %s: %s\n", GetColName(iCol), GetColData(iCol) ); + } + } +} + +/************************************************************************/ +/* GetTypeName() */ +/************************************************************************/ + +/** + * Get name for SQL column type. + * + * Returns a string name for the indicated type code (as returned + * from CPLODBCStatement::GetColType()). + * + * @param nTypeCode the SQL_ code, such as SQL_CHAR. + * + * @return internal string, "UNKNOWN" if code not recognised. + */ + +CPLString CPLODBCStatement::GetTypeName( int nTypeCode ) + +{ + switch( nTypeCode ) + { + case SQL_CHAR: + return "CHAR"; + + case SQL_NUMERIC: + return "NUMERIC"; + + case SQL_DECIMAL: + return "DECIMAL"; + + case SQL_INTEGER: + return "INTEGER"; + + case SQL_SMALLINT: + return "SMALLINT"; + + + case SQL_FLOAT: + return "FLOAT"; + + case SQL_REAL: + return "REAL"; + + case SQL_DOUBLE: + return "DOUBLE"; + + case SQL_DATETIME: + return "DATETIME"; + + case SQL_VARCHAR: + return "VARCHAR"; + + case SQL_TYPE_DATE: + return "DATE"; + + case SQL_TYPE_TIME: + return "TIME"; + + case SQL_TYPE_TIMESTAMP: + return "TIMESTAMP"; + + default: + CPLString osResult; + osResult.Printf( "UNKNOWN:%d", nTypeCode ); + return osResult; + } +} + +/************************************************************************/ +/* GetTypeMapping() */ +/************************************************************************/ + +/** + * Get appropriate C data type for SQL column type. + * + * Returns a C data type code, corresponding to the indicated SQL data + * type code (as returned from CPLODBCStatement::GetColType()). + * + * @param nTypeCode the SQL_ code, such as SQL_CHAR. + * + * @return data type code. The valid code is always returned. If SQL + * code is not recognised, SQL_C_BINARY will be returned. + */ + +SQLSMALLINT CPLODBCStatement::GetTypeMapping( SQLSMALLINT nTypeCode ) + +{ + switch( nTypeCode ) + { + case SQL_CHAR: + case SQL_VARCHAR: + case SQL_LONGVARCHAR: + return SQL_C_CHAR; + + case SQL_WCHAR: + case SQL_WVARCHAR: + case SQL_WLONGVARCHAR: + return SQL_C_WCHAR; + + case SQL_DECIMAL: + case SQL_NUMERIC: + return SQL_C_NUMERIC; + + case SQL_SMALLINT: + return SQL_C_SSHORT; + + case SQL_INTEGER: + return SQL_C_SLONG; + + case SQL_REAL: + return SQL_C_FLOAT; + + case SQL_FLOAT: + case SQL_DOUBLE: + return SQL_C_DOUBLE; + + case SQL_BIGINT: + return SQL_C_SBIGINT; + + case SQL_BIT: + case SQL_TINYINT: +/* case SQL_TYPE_UTCDATETIME: + case SQL_TYPE_UTCTIME:*/ + case SQL_INTERVAL_MONTH: + case SQL_INTERVAL_YEAR: + case SQL_INTERVAL_YEAR_TO_MONTH: + case SQL_INTERVAL_DAY: + case SQL_INTERVAL_HOUR: + case SQL_INTERVAL_MINUTE: + case SQL_INTERVAL_SECOND: + case SQL_INTERVAL_DAY_TO_HOUR: + case SQL_INTERVAL_DAY_TO_MINUTE: + case SQL_INTERVAL_DAY_TO_SECOND: + case SQL_INTERVAL_HOUR_TO_MINUTE: + case SQL_INTERVAL_HOUR_TO_SECOND: + case SQL_INTERVAL_MINUTE_TO_SECOND: + case SQL_GUID: + return SQL_C_CHAR; + + case SQL_DATE: + case SQL_TYPE_DATE: + return SQL_C_DATE; + + case SQL_TIME: + case SQL_TYPE_TIME: + return SQL_C_TIME; + + case SQL_TIMESTAMP: + case SQL_TYPE_TIMESTAMP: + return SQL_C_TIMESTAMP; + + case SQL_BINARY: + case SQL_VARBINARY: + case SQL_LONGVARBINARY: + return SQL_C_BINARY; + + default: + return SQL_C_CHAR; + } +} + +#endif /* #ifndef WIN32CE */ diff --git a/bazaar/plugin/gdal/port/cpl_odbc.h b/bazaar/plugin/gdal/port/cpl_odbc.h new file mode 100644 index 000000000..6ada6ceac --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_odbc.h @@ -0,0 +1,288 @@ +/****************************************************************************** + * $Id: cpl_odbc.h 29025 2015-04-26 11:50:20Z tamas $ + * + * Project: OGR ODBC Driver + * Purpose: Declarations for ODBC Access Cover API. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2003, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_ODBC_H_INCLUDED +#define CPL_ODBC_H_INCLUDED + +#include "cpl_port.h" + +#ifndef WIN32CE /* ODBC is not supported on Windows CE. */ + +#ifdef WIN32 +# include +#endif + +#include +#include +#include +#include "cpl_string.h" + +#ifdef PATH_MAX +# define ODBC_FILENAME_MAX PATH_MAX +#else +# define ODBC_FILENAME_MAX (255 + 1) /* Max path length */ +#endif + + +/** + * \file cpl_odbc.h + * + * ODBC Abstraction Layer (C++). + */ + +/** + * A class providing functions to install or remove ODBC driver. + */ +class CPL_DLL CPLODBCDriverInstaller +{ + char m_szPathOut[ODBC_FILENAME_MAX]; + char m_szError[SQL_MAX_MESSAGE_LENGTH]; + DWORD m_nErrorCode; + DWORD m_nUsageCount; + + public: + + // Default constructor. + CPLODBCDriverInstaller(); + + + /** + * Installs ODBC driver or updates definition of already installed driver. + * Interanally, it calls ODBC's SQLInstallDriverEx function. + * + * @param pszDriver - The driver definition as a list of keyword-value + * pairs describing the driver (See ODBC API Reference). + * + * @param pszPathIn - Full path of the target directory of the installation, + * or a null pointer (for unixODBC, NULL is passed). + * + * @param fRequest - The fRequest argument must contain one of + * the following values: + * ODBC_INSTALL_COMPLETE - (default) complete the installation request + * ODBC_INSTALL_INQUIRY - inquire about where a driver can be installed + * + * @return TRUE indicates success, FALSE if it fails. + */ + int InstallDriver( const char* pszDriver, const char* pszPathIn, + WORD fRequest = ODBC_INSTALL_COMPLETE ); + + /** + * Removes or changes information about the driver from + * the Odbcinst.ini entry in the system information. + * + * @param pszDriverName - The name of the driver as registered in + * the Odbcinst.ini key of the system information. + * + * @param fRemoveDSN - TRUE: Remove DSNs associated with the driver + * specified in lpszDriver. FALSE: Do not remove DSNs associated + * with the driver specified in lpszDriver. + * + * @return The function returns TRUE if it is successful, + * FALSE if it fails. If no entry exists in the system information + * when this function is called, the function returns FALSE. + * In order to obtain usage count value, call GetUsageCount(). + */ + int RemoveDriver( const char* pszDriverName, int fRemoveDSN = FALSE ); + + + // The usage count of the driver after this function has been called + int GetUsageCount() const { return m_nUsageCount; } + + + // Path of the target directory where the driver should be installed. + // For details, see ODBC API Reference and lpszPathOut + // parameter of SQLInstallDriverEx + const char* GetPathOut() const { return m_szPathOut; } + + + // If InstallDriver returns FALSE, then GetLastError then + // error message can be obtained by calling this function. + // Internally, it calls ODBC's SQLInstallerError function. + const char* GetLastError() const { return m_szError; } + + + // If InstallDriver returns FALSE, then GetLastErrorCode then + // error code can be obtained by calling this function. + // Internally, it calls ODBC's SQLInstallerError function. + // See ODBC API Reference for possible error flags. + DWORD GetLastErrorCode() const { return m_nErrorCode; } +}; + +class CPLODBCStatement; + +/* On MSVC SQLULEN is missing in some cases (ie. VC6) +** but it is always a #define so test this way. On Unix +** it is a typedef so we can't always do this. +*/ +#if defined(_MSC_VER) && !defined(SQLULEN) && !defined(_WIN64) +# define MISSING_SQLULEN +#endif + +#if !defined(MISSING_SQLULEN) +/* ODBC types to support 64 bit compilation */ +# define _SQLULEN SQLULEN +# define _SQLLEN SQLLEN +#else +# define _SQLULEN SQLUINTEGER +# define _SQLLEN SQLINTEGER +#endif /* ifdef SQLULEN */ + + +/** + * A class representing an ODBC database session. + * + * Includes error collection services. + */ + +class CPL_DLL CPLODBCSession { + char m_szLastError[SQL_MAX_MESSAGE_LENGTH + 1]; + HENV m_hEnv; + HDBC m_hDBC; + int m_bInTransaction; + int m_bAutoCommit; + + public: + CPLODBCSession(); + ~CPLODBCSession(); + + int EstablishSession( const char *pszDSN, + const char *pszUserid, + const char *pszPassword ); + const char *GetLastError(); + + // Transaction handling + + int ClearTransaction(); + int BeginTransaction(); + int CommitTransaction(); + int RollbackTransaction(); + int IsInTransaction() { return m_bInTransaction; } + + // Essentially internal. + + int CloseSession(); + + int Failed( int, HSTMT = NULL ); + HDBC GetConnection() { return m_hDBC; } + HENV GetEnvironment() { return m_hEnv; } +}; + +/** + * Abstraction for statement, and resultset. + * + * Includes methods for executing an SQL statement, and for accessing the + * resultset from that statement. Also provides for executing other ODBC + * requests that produce results sets such as SQLColumns() and SQLTables() + * requests. + */ + +class CPL_DLL CPLODBCStatement { + + CPLODBCSession *m_poSession; + HSTMT m_hStmt; + + SQLSMALLINT m_nColCount; + char **m_papszColNames; + SQLSMALLINT *m_panColType; + char **m_papszColTypeNames; + _SQLULEN *m_panColSize; + SQLSMALLINT *m_panColPrecision; + SQLSMALLINT *m_panColNullable; + char **m_papszColColumnDef; + + char **m_papszColValues; + _SQLLEN *m_panColValueLengths; + + int Failed( int ); + + char *m_pszStatement; + size_t m_nStatementMax; + size_t m_nStatementLen; + + public: + CPLODBCStatement( CPLODBCSession * ); + ~CPLODBCStatement(); + + HSTMT GetStatement() { return m_hStmt; } + + // Command buffer related. + void Clear(); + void AppendEscaped( const char * ); + void Append( const char * ); + void Append( int ); + void Append( double ); + int Appendf( const char *, ... ) CPL_PRINT_FUNC_FORMAT (2, 3); + const char *GetCommand() { return m_pszStatement; } + + int ExecuteSQL( const char * = NULL ); + + // Results fetching + int Fetch( int nOrientation = SQL_FETCH_NEXT, + int nOffset = 0 ); + void ClearColumnData(); + + int GetColCount(); + const char *GetColName( int ); + short GetColType( int ); + const char *GetColTypeName( int ); + short GetColSize( int ); + short GetColPrecision( int ); + short GetColNullable( int ); + const char *GetColColumnDef( int ); + + int GetColId( const char * ); + const char *GetColData( int, const char * = NULL ); + const char *GetColData( const char *, const char * = NULL ); + int GetColDataLength( int ); + int GetRowCountAffected(); + + // Fetch special metadata. + int GetColumns( const char *pszTable, + const char *pszCatalog = NULL, + const char *pszSchema = NULL ); + int GetPrimaryKeys( const char *pszTable, + const char *pszCatalog = NULL, + const char *pszSchema = NULL ); + + int GetTables( const char *pszCatalog = NULL, + const char *pszSchema = NULL ); + + void DumpResult( FILE *fp, int bShowSchema = FALSE ); + + static CPLString GetTypeName( int ); + static SQLSMALLINT GetTypeMapping( SQLSMALLINT ); + + int CollectResultsInfo(); +}; + +#endif /* #ifndef WIN32CE */ + +#endif + + diff --git a/bazaar/plugin/gdal/port/cpl_path.cpp b/bazaar/plugin/gdal/port/cpl_path.cpp new file mode 100644 index 000000000..a27f22b59 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_path.cpp @@ -0,0 +1,1003 @@ +/********************************************************************** + * $Id: cpl_path.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Portable filename/path parsing, and forming ala "Glob API". + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" + +CPL_CVSID("$Id: cpl_path.cpp 27044 2014-03-16 23:41:27Z rouault $"); + + +/* should be size of larged possible filename */ +#define CPL_PATH_BUF_SIZE 2048 +#define CPL_PATH_BUF_COUNT 10 + +#if defined(WIN32) || defined(WIN32CE) +#define SEP_CHAR '\\' +#define SEP_STRING "\\" +#else +#define SEP_CHAR '/' +#define SEP_STRING "/" +#endif + +static const char* CPLStaticBufferTooSmall(char *pszStaticResult) +{ + CPLError(CE_Failure, CPLE_AppDefined, "Destination buffer too small"); + strcpy( pszStaticResult, "" ); + return pszStaticResult; +} + +/************************************************************************/ +/* CPLGetStaticResult() */ +/************************************************************************/ + +static char *CPLGetStaticResult() + +{ + char *pachBufRingInfo = (char *) CPLGetTLS( CTLS_PATHBUF ); + if( pachBufRingInfo == NULL ) + { + pachBufRingInfo = (char *) CPLCalloc(1, sizeof(int) + CPL_PATH_BUF_SIZE * CPL_PATH_BUF_COUNT); + CPLSetTLS( CTLS_PATHBUF, pachBufRingInfo, TRUE ); + } + +/* -------------------------------------------------------------------- */ +/* Work out which string in the "ring" we want to use this */ +/* time. */ +/* -------------------------------------------------------------------- */ + int *pnBufIndex = (int *) pachBufRingInfo; + int nOffset = sizeof(int) + *pnBufIndex * CPL_PATH_BUF_SIZE; + char *pachBuffer = pachBufRingInfo + nOffset; + + *pnBufIndex = (*pnBufIndex + 1) % CPL_PATH_BUF_COUNT; + + return pachBuffer; +} + + +/************************************************************************/ +/* CPLFindFilenameStart() */ +/************************************************************************/ + +static int CPLFindFilenameStart( const char * pszFilename ) + +{ + size_t iFileStart; + + for( iFileStart = strlen(pszFilename); + iFileStart > 0 + && pszFilename[iFileStart-1] != '/' + && pszFilename[iFileStart-1] != '\\'; + iFileStart-- ) {} + + return (int)iFileStart; +} + +/************************************************************************/ +/* CPLGetPath() */ +/************************************************************************/ + +/** + * Extract directory path portion of filename. + * + * Returns a string containing the directory path portion of the passed + * filename. If there is no path in the passed filename an empty string + * will be returned (not NULL). + * + *
    + * CPLGetPath( "abc/def.xyz" ) == "abc"
    + * CPLGetPath( "/abc/def/" ) == "/abc/def"
    + * CPLGetPath( "/" ) == "/"
    + * CPLGetPath( "/abc/def" ) == "/abc"
    + * CPLGetPath( "abc" ) == ""
    + * 
    + * + * @param pszFilename the filename potentially including a path. + * + * @return Path in an internal string which must not be freed. The string + * may be destroyed by the next CPL filename handling call. The returned + * will generally not contain a trailing path separator. + */ + +const char *CPLGetPath( const char *pszFilename ) + +{ + int iFileStart = CPLFindFilenameStart(pszFilename); + char *pszStaticResult = CPLGetStaticResult(); + + if( iFileStart >= CPL_PATH_BUF_SIZE ) + return CPLStaticBufferTooSmall(pszStaticResult); + + CPLAssert( ! (pszFilename >= pszStaticResult && pszFilename < pszStaticResult + CPL_PATH_BUF_SIZE) ); + + if( iFileStart == 0 ) + { + strcpy( pszStaticResult, "" ); + return pszStaticResult; + } + + CPLStrlcpy( pszStaticResult, pszFilename, iFileStart+1 ); + + if( iFileStart > 1 + && (pszStaticResult[iFileStart-1] == '/' + || pszStaticResult[iFileStart-1] == '\\') ) + pszStaticResult[iFileStart-1] = '\0'; + + return pszStaticResult; +} + +/************************************************************************/ +/* CPLGetDirname() */ +/************************************************************************/ + +/** + * Extract directory path portion of filename. + * + * Returns a string containing the directory path portion of the passed + * filename. If there is no path in the passed filename the dot will be + * returned. It is the only difference from CPLGetPath(). + * + *
    + * CPLGetDirname( "abc/def.xyz" ) == "abc"
    + * CPLGetDirname( "/abc/def/" ) == "/abc/def"
    + * CPLGetDirname( "/" ) == "/"
    + * CPLGetDirname( "/abc/def" ) == "/abc"
    + * CPLGetDirname( "abc" ) == "."
    + * 
    + * + * @param pszFilename the filename potentially including a path. + * + * @return Path in an internal string which must not be freed. The string + * may be destroyed by the next CPL filename handling call. The returned + * will generally not contain a trailing path separator. + */ + +const char *CPLGetDirname( const char *pszFilename ) + +{ + int iFileStart = CPLFindFilenameStart(pszFilename); + char *pszStaticResult = CPLGetStaticResult(); + + if( iFileStart >= CPL_PATH_BUF_SIZE ) + return CPLStaticBufferTooSmall(pszStaticResult); + + CPLAssert( ! (pszFilename >= pszStaticResult && pszFilename < pszStaticResult + CPL_PATH_BUF_SIZE) ); + + if( iFileStart == 0 ) + { + strcpy( pszStaticResult, "." ); + return pszStaticResult; + } + + CPLStrlcpy( pszStaticResult, pszFilename, iFileStart+1 ); + + if( iFileStart > 1 + && (pszStaticResult[iFileStart-1] == '/' + || pszStaticResult[iFileStart-1] == '\\') ) + pszStaticResult[iFileStart-1] = '\0'; + + return pszStaticResult; +} + +/************************************************************************/ +/* CPLGetFilename() */ +/************************************************************************/ + +/** + * Extract non-directory portion of filename. + * + * Returns a string containing the bare filename portion of the passed + * filename. If there is no filename (passed value ends in trailing directory + * separator) an empty string is returned. + * + *
    + * CPLGetFilename( "abc/def.xyz" ) == "def.xyz"
    + * CPLGetFilename( "/abc/def/" ) == ""
    + * CPLGetFilename( "abc/def" ) == "def"
    + * 
    + * + * @param pszFullFilename the full filename potentially including a path. + * + * @return just the non-directory portion of the path (points back into original string). + */ + +const char *CPLGetFilename( const char *pszFullFilename ) + +{ + int iFileStart = CPLFindFilenameStart( pszFullFilename ); + + return pszFullFilename + iFileStart; +} + +/************************************************************************/ +/* CPLGetBasename() */ +/************************************************************************/ + +/** + * Extract basename (non-directory, non-extension) portion of filename. + * + * Returns a string containing the file basename portion of the passed + * name. If there is no basename (passed value ends in trailing directory + * separator, or filename starts with a dot) an empty string is returned. + * + *
    + * CPLGetBasename( "abc/def.xyz" ) == "def"
    + * CPLGetBasename( "abc/def" ) == "def"
    + * CPLGetBasename( "abc/def/" ) == ""
    + * 
    + * + * @param pszFullFilename the full filename potentially including a path. + * + * @return just the non-directory, non-extension portion of the path in + * an internal string which must not be freed. The string + * may be destroyed by the next CPL filename handling call. + */ + +const char *CPLGetBasename( const char *pszFullFilename ) + +{ + size_t iFileStart = CPLFindFilenameStart( pszFullFilename ); + size_t iExtStart, nLength; + char *pszStaticResult = CPLGetStaticResult(); + + CPLAssert( ! (pszFullFilename >= pszStaticResult && pszFullFilename < pszStaticResult + CPL_PATH_BUF_SIZE) ); + + for( iExtStart = strlen(pszFullFilename); + iExtStart > iFileStart && pszFullFilename[iExtStart] != '.'; + iExtStart-- ) {} + + if( iExtStart == iFileStart ) + iExtStart = strlen(pszFullFilename); + + nLength = iExtStart - iFileStart; + + if (nLength >= CPL_PATH_BUF_SIZE) + return CPLStaticBufferTooSmall(pszStaticResult); + + CPLStrlcpy( pszStaticResult, pszFullFilename + iFileStart, nLength + 1 ); + + return pszStaticResult; +} + + +/************************************************************************/ +/* CPLGetExtension() */ +/************************************************************************/ + +/** + * Extract filename extension from full filename. + * + * Returns a string containing the extention portion of the passed + * name. If there is no extension (the filename has no dot) an empty string + * is returned. The returned extension will not include the period. + * + *
    + * CPLGetExtension( "abc/def.xyz" ) == "xyz"
    + * CPLGetExtension( "abc/def" ) == ""
    + * 
    + * + * @param pszFullFilename the full filename potentially including a path. + * + * @return just the extension portion of the path in + * an internal string which must not be freed. The string + * may be destroyed by the next CPL filename handling call. + */ + +const char *CPLGetExtension( const char *pszFullFilename ) + +{ + size_t iFileStart = CPLFindFilenameStart( pszFullFilename ); + size_t iExtStart; + char *pszStaticResult = CPLGetStaticResult(); + + CPLAssert( ! (pszFullFilename >= pszStaticResult && pszFullFilename < pszStaticResult + CPL_PATH_BUF_SIZE) ); + + for( iExtStart = strlen(pszFullFilename); + iExtStart > iFileStart && pszFullFilename[iExtStart] != '.'; + iExtStart-- ) {} + + if( iExtStart == iFileStart ) + iExtStart = strlen(pszFullFilename)-1; + + if (CPLStrlcpy( pszStaticResult, pszFullFilename+iExtStart+1, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE) + return CPLStaticBufferTooSmall(pszStaticResult); + + return pszStaticResult; +} + +/************************************************************************/ +/* CPLGetCurrentDir() */ +/************************************************************************/ + +/** + * Get the current working directory name. + * + * @return a pointer to buffer, containing current working directory path + * or NULL in case of error. User is responsible to free that buffer + * after usage with CPLFree() function. + * If HAVE_GETCWD macro is not defined, the function returns NULL. + **/ + +char *CPLGetCurrentDir() + +{ + size_t nPathMax; + char *pszDirPath; + +# ifdef _MAX_PATH + nPathMax = _MAX_PATH; +# elif PATH_MAX + nPathMax = PATH_MAX; +# else + nPathMax = 8192; +# endif + + pszDirPath = (char*)CPLMalloc( nPathMax ); + if ( !pszDirPath ) + return NULL; + +#ifdef HAVE_GETCWD + return getcwd( pszDirPath, nPathMax ); +#else + return NULL; +#endif /* HAVE_GETCWD */ +} + +/************************************************************************/ +/* CPLResetExtension() */ +/************************************************************************/ + +/** + * Replace the extension with the provided one. + * + * @param pszPath the input path, this string is not altered. + * @param pszExt the new extension to apply to the given path. + * + * @return an altered filename with the new extension. Do not + * modify or free the returned string. The string may be destroyed by the + * next CPL call. + */ + +const char *CPLResetExtension( const char *pszPath, const char *pszExt ) + +{ + char *pszStaticResult = CPLGetStaticResult(); + size_t i; + + CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) ); + +/* -------------------------------------------------------------------- */ +/* First, try and strip off any existing extension. */ +/* -------------------------------------------------------------------- */ + if (CPLStrlcpy( pszStaticResult, pszPath, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE) + return CPLStaticBufferTooSmall(pszStaticResult); + + if (*pszStaticResult) + { + for( i = strlen(pszStaticResult) - 1; i > 0; i-- ) + { + if( pszStaticResult[i] == '.' ) + { + pszStaticResult[i] = '\0'; + break; + } + + if( pszStaticResult[i] == '/' || pszStaticResult[i] == '\\' + || pszStaticResult[i] == ':' ) + break; + } + } + +/* -------------------------------------------------------------------- */ +/* Append the new extension. */ +/* -------------------------------------------------------------------- */ + if (CPLStrlcat( pszStaticResult, ".", CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE || + CPLStrlcat( pszStaticResult, pszExt, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE) + return CPLStaticBufferTooSmall(pszStaticResult); + + return pszStaticResult; +} + +/************************************************************************/ +/* CPLFormFilename() */ +/************************************************************************/ + +/** + * Build a full file path from a passed path, file basename and extension. + * + * The path, and extension are optional. The basename may in fact contain + * an extension if desired. + * + *
    + * CPLFormFilename("abc/xyz","def", ".dat" ) == "abc/xyz/def.dat"
    + * CPLFormFilename(NULL,"def", NULL ) == "def"
    + * CPLFormFilename(NULL,"abc/def.dat", NULL ) == "abc/def.dat"
    + * CPLFormFilename("/abc/xyz/","def.dat", NULL ) == "/abc/xyz/def.dat"
    + * 
    + * + * @param pszPath directory path to the directory containing the file. This + * may be relative or absolute, and may have a trailing path separator or + * not. May be NULL. + * + * @param pszBasename file basename. May optionally have path and/or + * extension. Must *NOT* be NULL. + * + * @param pszExtension file extension, optionally including the period. May + * be NULL. + * + * @return a fully formed filename in an internal static string. Do not + * modify or free the returned string. The string may be destroyed by the + * next CPL call. + */ + +const char *CPLFormFilename( const char * pszPath, + const char * pszBasename, + const char * pszExtension ) + +{ + char *pszStaticResult = CPLGetStaticResult(); + const char *pszAddedPathSep = ""; + const char *pszAddedExtSep = ""; + + CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) ); + CPLAssert( ! (pszBasename >= pszStaticResult && pszBasename < pszStaticResult + CPL_PATH_BUF_SIZE) ); + + if( pszBasename[0] == '.' && pszBasename[1] == '/' ) + pszBasename += 2; + + if( pszPath == NULL ) + pszPath = ""; + else if( strlen(pszPath) > 0 + && pszPath[strlen(pszPath)-1] != '/' + && pszPath[strlen(pszPath)-1] != '\\' ) + { + /* FIXME? would be better to ask the filesystems what they */ + /* prefer as directory separator */ + if (strncmp(pszPath, "/vsicurl/", 9) == 0) + pszAddedPathSep = "/"; + else if (strncmp(pszPath, "/vsizip/", 8) == 0) + pszAddedPathSep = "/"; + else + pszAddedPathSep = SEP_STRING; + } + + if( pszExtension == NULL ) + pszExtension = ""; + else if( pszExtension[0] != '.' && strlen(pszExtension) > 0 ) + pszAddedExtSep = "."; + + if (CPLStrlcpy( pszStaticResult, pszPath, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE || + CPLStrlcat( pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE || + CPLStrlcat( pszStaticResult, pszBasename, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE || + CPLStrlcat( pszStaticResult, pszAddedExtSep, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE || + CPLStrlcat( pszStaticResult, pszExtension, CPL_PATH_BUF_SIZE) >= CPL_PATH_BUF_SIZE) + return CPLStaticBufferTooSmall(pszStaticResult); + + return pszStaticResult; +} + +/************************************************************************/ +/* CPLFormCIFilename() */ +/************************************************************************/ + +/** + * Case insensitive file searching, returing full path. + * + * This function tries to return the path to a file regardless of + * whether the file exactly matches the basename, and extension case, or + * is all upper case, or all lower case. The path is treated as case + * sensitive. This function is equivelent to CPLFormFilename() on + * case insensitive file systems (like Windows). + * + * @param pszPath directory path to the directory containing the file. This + * may be relative or absolute, and may have a trailing path separator or + * not. May be NULL. + * + * @param pszBasename file basename. May optionally have path and/or + * extension. May not be NULL. + * + * @param pszExtension file extension, optionally including the period. May + * be NULL. + * + * @return a fully formed filename in an internal static string. Do not + * modify or free the returned string. The string may be destroyed by the + * next CPL call. + */ + +const char *CPLFormCIFilename( const char * pszPath, + const char * pszBasename, + const char * pszExtension ) + +{ + // On case insensitive filesystems, just default to + // CPLFormFilename() + if( !VSIIsCaseSensitiveFS(pszPath) ) + return CPLFormFilename( pszPath, pszBasename, pszExtension ); + + const char *pszAddedExtSep = ""; + char *pszFilename; + const char *pszFullPath; + int nLen = strlen(pszBasename)+2, i; + VSIStatBufL sStatBuf; + int nStatRet; + + if( pszExtension != NULL ) + nLen += strlen(pszExtension); + + pszFilename = (char *) CPLMalloc(nLen); + + if( pszExtension == NULL ) + pszExtension = ""; + else if( pszExtension[0] != '.' && strlen(pszExtension) > 0 ) + pszAddedExtSep = "."; + + sprintf( pszFilename, "%s%s%s", + pszBasename, pszAddedExtSep, pszExtension ); + + pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL ); + nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG ); + if( nStatRet != 0 ) + { + for( i = 0; pszFilename[i] != '\0'; i++ ) + { + if( islower(pszFilename[i]) ) + pszFilename[i] = (char) toupper(pszFilename[i]); + } + + pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL ); + nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG ); + } + + if( nStatRet != 0 ) + { + for( i = 0; pszFilename[i] != '\0'; i++ ) + { + if( isupper(pszFilename[i]) ) + pszFilename[i] = (char) tolower(pszFilename[i]); + } + + pszFullPath = CPLFormFilename( pszPath, pszFilename, NULL ); + nStatRet = VSIStatExL( pszFullPath, &sStatBuf, VSI_STAT_EXISTS_FLAG ); + } + + if( nStatRet != 0 ) + pszFullPath = CPLFormFilename( pszPath, pszBasename, pszExtension ); + + CPLFree( pszFilename ); + + return pszFullPath; +} + +/************************************************************************/ +/* CPLProjectRelativeFilename() */ +/************************************************************************/ + +/** + * Find a file relative to a project file. + * + * Given the path to a "project" directory, and a path to a secondary file + * referenced from that project, build a path to the secondary file + * that the current application can use. If the secondary path is already + * absolute, rather than relative, then it will be returned unaltered. + * + * Examples: + *
    + * CPLProjectRelativeFilename("abc/def","tmp/abc.gif") == "abc/def/tmp/abc.gif"
    + * CPLProjectRelativeFilename("abc/def","/tmp/abc.gif") == "/tmp/abc.gif"
    + * CPLProjectRelativeFilename("/xy", "abc.gif") == "/xy/abc.gif"
    + * CPLProjectRelativeFilename("/abc/def","../abc.gif") == "/abc/def/../abc.gif"
    + * CPLProjectRelativeFilename("C:\WIN","abc.gif") == "C:\WIN\abc.gif"
    + * 
    + * + * @param pszProjectDir the directory relative to which the secondary files + * path should be interpreted. + * @param pszSecondaryFilename the filename (potentially with path) that + * is to be interpreted relative to the project directory. + * + * @return a composed path to the secondary file. The returned string is + * internal and should not be altered, freed, or depending on past the next + * CPL call. + */ + +const char *CPLProjectRelativeFilename( const char *pszProjectDir, + const char *pszSecondaryFilename ) + +{ + char *pszStaticResult = CPLGetStaticResult(); + + CPLAssert( ! (pszProjectDir >= pszStaticResult && pszProjectDir < pszStaticResult + CPL_PATH_BUF_SIZE) ); + CPLAssert( ! (pszSecondaryFilename >= pszStaticResult && pszSecondaryFilename < pszStaticResult + CPL_PATH_BUF_SIZE) ); + + if( !CPLIsFilenameRelative( pszSecondaryFilename ) ) + return pszSecondaryFilename; + + if( pszProjectDir == NULL || strlen(pszProjectDir) == 0 ) + return pszSecondaryFilename; + + if (CPLStrlcpy( pszStaticResult, pszProjectDir, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE) + goto error; + + if( pszProjectDir[strlen(pszProjectDir)-1] != '/' + && pszProjectDir[strlen(pszProjectDir)-1] != '\\' ) + { + /* FIXME? would be better to ask the filesystems what they */ + /* prefer as directory separator */ + const char* pszAddedPathSep; + if (strncmp(pszStaticResult, "/vsicurl/", 9) == 0) + pszAddedPathSep = "/"; + else + pszAddedPathSep = SEP_STRING; + if (CPLStrlcat( pszStaticResult, pszAddedPathSep, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE) + goto error; + } + + if (CPLStrlcat( pszStaticResult, pszSecondaryFilename, CPL_PATH_BUF_SIZE ) >= CPL_PATH_BUF_SIZE) + goto error; + + return pszStaticResult; +error: + return CPLStaticBufferTooSmall(pszStaticResult); +} + + +/************************************************************************/ +/* CPLIsFilenameRelative() */ +/************************************************************************/ + +/** + * Is filename relative or absolute? + * + * The test is filesystem convention agnostic. That is it will test for + * Unix style and windows style path conventions regardless of the actual + * system in use. + * + * @param pszFilename the filename with path to test. + * + * @return TRUE if the filename is relative or FALSE if it is absolute. + */ + +int CPLIsFilenameRelative( const char *pszFilename ) + +{ + if( (strlen(pszFilename) > 2 + && (strncmp(pszFilename+1,":\\",2) == 0 + || strncmp(pszFilename+1,":/",2) == 0)) + || pszFilename[0] == '\\' + || pszFilename[0] == '/' ) + return FALSE; + else + return TRUE; +} + +/************************************************************************/ +/* CPLExtractRelativePath() */ +/************************************************************************/ + +/** + * Get relative path from directory to target file. + * + * Computes a relative path for pszTarget relative to pszBaseDir. + * Currently this only works if they share a common base path. The returned + * path is normally into the pszTarget string. It should only be considered + * valid as long as pszTarget is valid or till the next call to + * this function, whichever comes first. + * + * @param pszBaseDir the name of the directory relative to which the path + * should be computed. pszBaseDir may be NULL in which case the original + * target is returned without relitivizing. + * + * @param pszTarget the filename to be changed to be relative to pszBaseDir. + * + * @param pbGotRelative Pointer to location in which a flag is placed + * indicating that the returned path is relative to the basename (TRUE) or + * not (FALSE). This pointer may be NULL if flag is not desired. + * + * @return an adjusted path or the original if it could not be made relative + * to the pszBaseFile's path. + **/ + +const char *CPLExtractRelativePath( const char *pszBaseDir, + const char *pszTarget, + int *pbGotRelative ) + +{ + size_t nBasePathLen; + +/* -------------------------------------------------------------------- */ +/* If we don't have a basedir, then we can't relativize the path. */ +/* -------------------------------------------------------------------- */ + if( pszBaseDir == NULL ) + { + if( pbGotRelative != NULL ) + *pbGotRelative = FALSE; + + return pszTarget; + } + + nBasePathLen = strlen(pszBaseDir); + +/* -------------------------------------------------------------------- */ +/* One simple case is when the base dir is '.' and the target */ +/* filename is relative. */ +/* -------------------------------------------------------------------- */ + if( (nBasePathLen == 0 || EQUAL(pszBaseDir,".")) + && CPLIsFilenameRelative(pszTarget) ) + { + if( pbGotRelative != NULL ) + *pbGotRelative = TRUE; + + return pszTarget; + } + +/* -------------------------------------------------------------------- */ +/* By this point, if we don't have a base path, we can't have a */ +/* meaningful common prefix. */ +/* -------------------------------------------------------------------- */ + if( nBasePathLen == 0 ) + { + if( pbGotRelative != NULL ) + *pbGotRelative = FALSE; + + return pszTarget; + } + +/* -------------------------------------------------------------------- */ +/* If we don't have a common path prefix, then we can't get a */ +/* relative path. */ +/* -------------------------------------------------------------------- */ + if( !EQUALN(pszBaseDir,pszTarget,nBasePathLen) + || (pszTarget[nBasePathLen] != '\\' + && pszTarget[nBasePathLen] != '/') ) + { + if( pbGotRelative != NULL ) + *pbGotRelative = FALSE; + + return pszTarget; + } + +/* -------------------------------------------------------------------- */ +/* We have a relative path. Strip it off to get a string to */ +/* return. */ +/* -------------------------------------------------------------------- */ + if( pbGotRelative != NULL ) + *pbGotRelative = TRUE; + + return pszTarget + nBasePathLen + 1; +} + +/************************************************************************/ +/* CPLCleanTrailingSlash() */ +/************************************************************************/ + +/** + * Remove trailing forward/backward slash from the path for unix/windows resp. + * + * Returns a string containing the portion of the passed path string with + * trailing slash removed. If there is no path in the passed filename + * an empty string will be returned (not NULL). + * + *
    + * CPLCleanTrailingSlash( "abc/def/" ) == "abc/def"
    + * CPLCleanTrailingSlash( "abc/def" ) == "abc/def"
    + * CPLCleanTrailingSlash( "c:\abc\def\" ) == "c:\abc\def"
    + * CPLCleanTrailingSlash( "c:\abc\def" ) == "c:\abc\def"
    + * CPLCleanTrailingSlash( "abc" ) == "abc"
    + * 
    + * + * @param pszPath the path to be cleaned up + * + * @return Path in an internal string which must not be freed. The string + * may be destroyed by the next CPL filename handling call. + */ + +const char *CPLCleanTrailingSlash( const char *pszPath ) + +{ + char *pszStaticResult = CPLGetStaticResult(); + int iPathLength = strlen(pszPath); + + CPLAssert( ! (pszPath >= pszStaticResult && pszPath < pszStaticResult + CPL_PATH_BUF_SIZE) ); + + if (iPathLength >= CPL_PATH_BUF_SIZE) + return CPLStaticBufferTooSmall(pszStaticResult); + + CPLStrlcpy( pszStaticResult, pszPath, iPathLength+1 ); + + if( iPathLength > 0 + && (pszStaticResult[iPathLength-1] == '\\' + || pszStaticResult[iPathLength-1] == '/')) + pszStaticResult[iPathLength-1] = '\0'; + + return pszStaticResult; +} + +/************************************************************************/ +/* CPLCorrespondingPaths() */ +/************************************************************************/ + +/** + * Identify corresponding paths. + * + * Given a prototype old and new filename this function will attempt + * to determine corresponding names for a set of other old filenames that + * will rename them in a similar manner. This correspondance assumes there + * are two possibly kinds of renaming going on. A change of path, and a + * change of filename stem. + * + * If a consistent renaming cannot be established for all the files this + * function will return indicating an error. + * + * The returned file list becomes owned by the caller and should be destroyed + * with CSLDestroy(). + * + * @param pszOldFilename path to old prototype file. + * @param pszNewFilename path to new prototype file. + * @param papszFileList list of other files associated with pszOldFilename to + * rename similarly. + * + * @return a list of files corresponding to papszFileList but renamed to + * correspond to pszNewFilename. + */ + +char **CPLCorrespondingPaths( const char *pszOldFilename, + const char *pszNewFilename, + char **papszFileList ) + +{ + CPLString osOldPath = CPLGetPath( pszOldFilename ); + CPLString osNewPath = CPLGetPath( pszNewFilename ); + CPLString osOldBasename = CPLGetBasename( pszOldFilename ); + CPLString osNewBasename = CPLGetBasename( pszNewFilename ); + int i; + + if( CSLCount(papszFileList) == 0 ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* There is a special case for a one item list which exactly */ +/* matches the old name, to rename to the new name. */ +/* -------------------------------------------------------------------- */ + if( CSLCount(papszFileList) == 1 + && strcmp(pszOldFilename,papszFileList[0]) == 0 ) + { + return CSLAddString( NULL, pszNewFilename ); + } + +/* -------------------------------------------------------------------- */ +/* If the basename is changing, verify that all source files */ +/* have the same starting basename. */ +/* -------------------------------------------------------------------- */ + if( osOldBasename != osNewBasename ) + { + for( i=0; papszFileList[i] != NULL; i++ ) + { + if( osOldBasename == CPLGetBasename( papszFileList[i] ) ) + continue; + + CPLString osFilePath, osFileName; + + osFilePath = CPLGetPath( papszFileList[i] ); + osFileName = CPLGetFilename( papszFileList[i] ); + + if( !EQUALN(osFileName,osOldBasename,osOldBasename.size()) + || !EQUAL(osFilePath,osOldPath) + || osFileName[osOldBasename.size()] != '.' ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to rename fileset due irregular basenames."); + return NULL; + } + } + } + +/* -------------------------------------------------------------------- */ +/* If the filename portions differs, ensure they only differ in */ +/* basename. */ +/* -------------------------------------------------------------------- */ + if( osOldBasename != osNewBasename ) + { + CPLString osOldExtra = CPLGetFilename(pszOldFilename) + + strlen(osOldBasename); + CPLString osNewExtra = CPLGetFilename(pszNewFilename) + + strlen(osNewBasename); + + if( osOldExtra != osNewExtra ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Unable to rename fileset due to irregular filename correspondence." ); + return NULL; + } + } + +/* -------------------------------------------------------------------- */ +/* Generate the new filenames. */ +/* -------------------------------------------------------------------- */ + char **papszNewList = NULL; + + for( i=0; papszFileList[i] != NULL; i++ ) + { + CPLString osNewFilename; + CPLString osOldFilename = CPLGetFilename( papszFileList[i] ); + + if( osOldBasename == osNewBasename ) + osNewFilename = + CPLFormFilename( osNewPath, osOldFilename, NULL ); + else + osNewFilename = + CPLFormFilename( osNewPath, osNewBasename, + osOldFilename.c_str()+strlen(osOldBasename)); + + papszNewList = CSLAddString( papszNewList, osNewFilename ); + } + + return papszNewList; +} + +/************************************************************************/ +/* CPLGenerateTempFilename() */ +/************************************************************************/ + +/** + * Generate temporary file name. + * + * Returns a filename that may be used for a temporary file. The location + * of the file tries to follow operating system semantics but may be + * forced via the CPL_TMPDIR configuration option. + * + * @param pszStem if non-NULL this will be part of the filename. + * + * @return a filename which is valid till the next CPL call in this thread. + */ + +const char *CPLGenerateTempFilename( const char *pszStem ) + +{ + const char *pszDir = CPLGetConfigOption( "CPL_TMPDIR", NULL ); + static volatile int nTempFileCounter = 0; + + if( pszDir == NULL ) + pszDir = CPLGetConfigOption( "TMPDIR", NULL ); + + if( pszDir == NULL ) + pszDir = CPLGetConfigOption( "TEMP", NULL ); + + if( pszDir == NULL ) + pszDir = "."; + + CPLString osFilename; + + if( pszStem == NULL ) + pszStem = ""; + + osFilename.Printf( "%s%u_%d", pszStem, + (int) CPLGetPID(), nTempFileCounter++ ); + + return CPLFormFilename( pszDir, osFilename, NULL ); +} diff --git a/bazaar/plugin/gdal/port/cpl_port.h b/bazaar/plugin/gdal/port/cpl_port.h new file mode 100644 index 000000000..ebf071056 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_port.h @@ -0,0 +1,625 @@ +/****************************************************************************** + * $Id: cpl_port.h 28375 2015-01-30 12:06:11Z rouault $ + * + * Project: CPL - Common Portability Library + * Author: Frank Warmerdam, warmerdam@pobox.com + * Purpose: Include file providing low level portability services for CPL. + * This should be the first include file for any CPL based code. + * + ****************************************************************************** + * Copyright (c) 1998, 2005, Frank Warmerdam + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_BASE_H_INCLUDED +#define CPL_BASE_H_INCLUDED + +/** + * \file cpl_port.h + * + * Core portability definitions for CPL. + * + */ + +/* ==================================================================== */ +/* We will use macos_pre10 to indicate compilation with MacOS */ +/* versions before MacOS X. */ +/* ==================================================================== */ +#ifdef macintosh +# define macos_pre10 +#endif + +/* ==================================================================== */ +/* We will use WIN32 as a standard windows define. */ +/* ==================================================================== */ +#if defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) +# define WIN32 +#endif + +#if defined(_WINDOWS) && !defined(WIN32) && !defined(_WIN32_WCE) +# define WIN32 +#endif + +/* ==================================================================== */ +/* We will use WIN32CE as a standard Windows CE (Mobile) define. */ +/* ==================================================================== */ +#if defined(_WIN32_WCE) +# define WIN32CE +#endif + +/* -------------------------------------------------------------------- */ +/* The following apparently allow you to use strcpy() and other */ +/* functions judged "unsafe" by microsoft in VS 8 (2005). */ +/* -------------------------------------------------------------------- */ +#ifdef _MSC_VER +# ifndef _CRT_SECURE_NO_DEPRECATE +# define _CRT_SECURE_NO_DEPRECATE +# endif +# ifndef _CRT_NONSTDC_NO_DEPRECATE +# define _CRT_NONSTDC_NO_DEPRECATE +# endif +#endif + +#include + +/* ==================================================================== */ +/* A few sanity checks, mainly to detect problems that sometimes */ +/* arise with bad configured cross-compilation. */ +/* ==================================================================== */ + +#if !defined(SIZEOF_INT) || SIZEOF_INT != 4 +#error "Unexpected value for SIZEOF_INT" +#endif + +#if !defined(SIZEOF_UNSIGNED_LONG) || (SIZEOF_UNSIGNED_LONG != 4 && SIZEOF_UNSIGNED_LONG != 8) +#error "Unexpected value for SIZEOF_UNSIGNED_LONG" +#endif + +#if !defined(SIZEOF_VOIDP) || (SIZEOF_VOIDP != 4 && SIZEOF_VOIDP != 8) +#error "Unexpected value for SIZEOF_VOIDP" +#endif + + +/* ==================================================================== */ +/* This will disable most WIN32 stuff in a Cygnus build which */ +/* defines unix to 1. */ +/* ==================================================================== */ + +#ifdef unix +# undef WIN32 +# undef WIN32CE +#endif + +#if defined(VSI_NEED_LARGEFILE64_SOURCE) && !defined(_LARGEFILE64_SOURCE) +# define _LARGEFILE64_SOURCE 1 +#endif + +/* ==================================================================== */ +/* If iconv() is available use extended recoding module. */ +/* Stub implementation is always compiled in, because it works */ +/* faster than iconv() for encodings it supports. */ +/* ==================================================================== */ + +#if defined(HAVE_ICONV) +# define CPL_RECODE_ICONV +#endif + +#define CPL_RECODE_STUB + +/* ==================================================================== */ +/* MinGW stuff */ +/* ==================================================================== */ + +/* We need __MSVCRT_VERSION__ >= 0x0601 to have "struct __stat64" */ +/* Latest versions of mingw32 define it, but with older ones, */ +/* we need to define it manually */ +#if defined(__MINGW32__) +#ifndef __MSVCRT_VERSION__ +#define __MSVCRT_VERSION__ 0x0601 +#endif +#endif + +/* ==================================================================== */ +/* Standard include files. */ +/* ==================================================================== */ + +#include +#include +#include +#include +#include +#include +#include + +#if !defined(WIN32CE) +# include +#else +# include +# include +#endif + + +#if defined(HAVE_ERRNO_H) +# include +#endif + +#ifdef HAVE_LOCALE_H +# include +#endif + +#ifdef HAVE_DIRECT_H +# include +#endif + +#if !(defined(WIN32) || defined(WIN32CE)) +# include +#endif + +#if defined(HAVE_LIBDBMALLOC) && defined(HAVE_DBMALLOC_H) && defined(DEBUG) +# define DBMALLOC +# include +#endif + +#if !defined(DBMALLOC) && defined(HAVE_DMALLOC_H) +# define USE_DMALLOC +# include +#endif + +/* ==================================================================== */ +/* Base portability stuff ... this stuff may need to be */ +/* modified for new platforms. */ +/* ==================================================================== */ + +/*--------------------------------------------------------------------- + * types for 16 and 32 bits integers, etc... + *--------------------------------------------------------------------*/ +#if UINT_MAX == 65535 +typedef long GInt32; +typedef unsigned long GUInt32; +#else +typedef int GInt32; +typedef unsigned int GUInt32; +#endif + +typedef short GInt16; +typedef unsigned short GUInt16; +typedef unsigned char GByte; +/* hack for PDF driver and poppler >= 0.15.0 that defines incompatible "typedef bool GBool" */ +/* in include/poppler/goo/gtypes.h */ +#ifndef CPL_GBOOL_DEFINED +#define CPL_GBOOL_DEFINED +typedef int GBool; +#endif + +/* -------------------------------------------------------------------- */ +/* 64bit support */ +/* -------------------------------------------------------------------- */ + +#if defined(WIN32) && defined(_MSC_VER) + +#define VSI_LARGE_API_SUPPORTED +typedef __int64 GIntBig; +typedef unsigned __int64 GUIntBig; + +#define GINTBIG_MIN ((GIntBig)(0x80000000) << 32) +#define GINTBIG_MAX (((GIntBig)(0x7FFFFFFF) << 32) | 0xFFFFFFFFU) + +#elif HAVE_LONG_LONG + +typedef long long GIntBig; +typedef unsigned long long GUIntBig; + +#define GINTBIG_MIN ((GIntBig)(0x80000000) << 32) +#define GINTBIG_MAX (((GIntBig)(0x7FFFFFFF) << 32) | 0xFFFFFFFFU) + +#else + +typedef long GIntBig; +typedef unsigned long GUIntBig; + +#define GINTBIG_MIN INT_MIN +#define GINTBIG_MAX INT_MAX +#endif + +#if SIZEOF_VOIDP == 8 +typedef GIntBig GPtrDiff_t; +#else +typedef int GPtrDiff_t; +#endif + +#if defined(__MSVCRT__) || (defined(WIN32) && defined(_MSC_VER)) + #define CPL_FRMT_GB_WITHOUT_PREFIX "I64" +#elif HAVE_LONG_LONG + #define CPL_FRMT_GB_WITHOUT_PREFIX "ll" +#else + #define CPL_FRMT_GB_WITHOUT_PREFIX "l" +#endif + +#define CPL_FRMT_GIB "%" CPL_FRMT_GB_WITHOUT_PREFIX "d" +#define CPL_FRMT_GUIB "%" CPL_FRMT_GB_WITHOUT_PREFIX "u" + +/* Workaround VC6 bug */ +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +#define GUINTBIG_TO_DOUBLE(x) (double)(GIntBig)(x) +#else +#define GUINTBIG_TO_DOUBLE(x) (double)(x) +#endif + +/* ==================================================================== */ +/* Other standard services. */ +/* ==================================================================== */ +#ifdef __cplusplus +# define CPL_C_START extern "C" { +# define CPL_C_END } +#else +# define CPL_C_START +# define CPL_C_END +#endif + +#ifndef CPL_DLL +#if defined(_MSC_VER) && !defined(CPL_DISABLE_DLL) +# define CPL_DLL __declspec(dllexport) +#else +# if defined(USE_GCC_VISIBILITY_FLAG) +# define CPL_DLL __attribute__ ((visibility("default"))) +# else +# define CPL_DLL +# endif +#endif +#endif + +/* Should optional (normally private) interfaces be exported? */ +#ifdef CPL_OPTIONAL_APIS +# define CPL_ODLL CPL_DLL +#else +# define CPL_ODLL +#endif + +#ifndef CPL_STDCALL +#if defined(_MSC_VER) && !defined(CPL_DISABLE_STDCALL) +# define CPL_STDCALL __stdcall +#else +# define CPL_STDCALL +#endif +#endif + +#ifdef _MSC_VER +# define FORCE_CDECL __cdecl +#else +# define FORCE_CDECL +#endif + +/* TODO : support for other compilers needed */ +#if (defined(__GNUC__) && !defined(__NO_INLINE__)) || defined(_MSC_VER) +#define HAS_CPL_INLINE 1 +#define CPL_INLINE __inline +#elif defined(__SUNPRO_CC) +#define HAS_CPL_INLINE 1 +#define CPL_INLINE inline +#else +#define CPL_INLINE +#endif + +#ifndef NULL +# define NULL 0 +#endif + +#ifndef FALSE +# define FALSE 0 +#endif + +#ifndef TRUE +# define TRUE 1 +#endif + +#ifndef MAX +# define MIN(a,b) ((ab) ? a : b) +#endif + +#ifndef ABS +# define ABS(x) ((x<0) ? (-1*(x)) : x) +#endif + +#ifndef M_PI +# define M_PI 3.14159265358979323846 /* pi */ +#endif + +/* -------------------------------------------------------------------- */ +/* Macro to test equality of two floating point values. */ +/* We use fabs() function instead of ABS() macro to avoid side */ +/* effects. */ +/* -------------------------------------------------------------------- */ +#ifndef CPLIsEqual +# define CPLIsEqual(x,y) (fabs((x) - (y)) < 0.0000000000001) +#endif + +/* -------------------------------------------------------------------- */ +/* Provide macros for case insensitive string comparisons. */ +/* -------------------------------------------------------------------- */ +#ifndef EQUAL +# if defined(WIN32) || defined(WIN32CE) +# define STRCASECMP(a,b) (stricmp(a,b)) +# define STRNCASECMP(a,b,n) (strnicmp(a,b,n)) +# else +# define STRCASECMP(a,b) (strcasecmp(a,b)) +# define STRNCASECMP(a,b,n) (strncasecmp(a,b,n)) +# endif +# define EQUALN(a,b,n) (STRNCASECMP(a,b,n)==0) +# define EQUAL(a,b) (STRCASECMP(a,b)==0) +#endif + +#ifdef macos_pre10 +int strcasecmp(char * str1, char * str2); +int strncasecmp(char * str1, char * str2, int len); +char * strdup (char *instr); +#endif + +#ifndef CPL_THREADLOCAL +# define CPL_THREADLOCAL +#endif + +/* -------------------------------------------------------------------- */ +/* Handle isnan() and isinf(). Note that isinf() and isnan() */ +/* are supposed to be macros according to C99, defined in math.h */ +/* Some systems (ie. Tru64) don't have isinf() at all, so if */ +/* the macro is not defined we just assume nothing is infinite. */ +/* This may mean we have no real CPLIsInf() on systems with isinf()*/ +/* function but no corresponding macro, but I can live with */ +/* that since it isn't that important a test. */ +/* -------------------------------------------------------------------- */ +#ifdef _MSC_VER +# include +# define CPLIsNan(x) _isnan(x) +# define CPLIsInf(x) (!_isnan(x) && !_finite(x)) +# define CPLIsFinite(x) _finite(x) +#else +# define CPLIsNan(x) isnan(x) +# ifdef isinf +# define CPLIsInf(x) isinf(x) +# define CPLIsFinite(x) (!isnan(x) && !isinf(x)) +# else +# define CPLIsInf(x) FALSE +# define CPLIsFinite(x) (!isnan(x)) +# endif +#endif + +/*--------------------------------------------------------------------- + * CPL_LSB and CPL_MSB + * Only one of these 2 macros should be defined and specifies the byte + * ordering for the current platform. + * This should be defined in the Makefile, but if it is not then + * the default is CPL_LSB (Intel ordering, LSB first). + *--------------------------------------------------------------------*/ +#if defined(WORDS_BIGENDIAN) && !defined(CPL_MSB) && !defined(CPL_LSB) +# define CPL_MSB +#endif + +#if ! ( defined(CPL_LSB) || defined(CPL_MSB) ) +#define CPL_LSB +#endif + +#if defined(CPL_LSB) +# define CPL_IS_LSB 1 +#else +# define CPL_IS_LSB 0 +#endif + +/*--------------------------------------------------------------------- + * Little endian <==> big endian byte swap macros. + *--------------------------------------------------------------------*/ + +#define CPL_SWAP16(x) \ + ((GUInt16)( \ + (((GUInt16)(x) & 0x00ffU) << 8) | \ + (((GUInt16)(x) & 0xff00U) >> 8) )) + +#define CPL_SWAP16PTR(x) \ +{ \ + GByte byTemp, *_pabyDataT = (GByte *) (x); \ + \ + byTemp = _pabyDataT[0]; \ + _pabyDataT[0] = _pabyDataT[1]; \ + _pabyDataT[1] = byTemp; \ +} + +#define CPL_SWAP32(x) \ + ((GUInt32)( \ + (((GUInt32)(x) & (GUInt32)0x000000ffUL) << 24) | \ + (((GUInt32)(x) & (GUInt32)0x0000ff00UL) << 8) | \ + (((GUInt32)(x) & (GUInt32)0x00ff0000UL) >> 8) | \ + (((GUInt32)(x) & (GUInt32)0xff000000UL) >> 24) )) + +#define CPL_SWAP32PTR(x) \ +{ \ + GByte byTemp, *_pabyDataT = (GByte *) (x); \ + \ + byTemp = _pabyDataT[0]; \ + _pabyDataT[0] = _pabyDataT[3]; \ + _pabyDataT[3] = byTemp; \ + byTemp = _pabyDataT[1]; \ + _pabyDataT[1] = _pabyDataT[2]; \ + _pabyDataT[2] = byTemp; \ +} + +#define CPL_SWAP64PTR(x) \ +{ \ + GByte byTemp, *_pabyDataT = (GByte *) (x); \ + \ + byTemp = _pabyDataT[0]; \ + _pabyDataT[0] = _pabyDataT[7]; \ + _pabyDataT[7] = byTemp; \ + byTemp = _pabyDataT[1]; \ + _pabyDataT[1] = _pabyDataT[6]; \ + _pabyDataT[6] = byTemp; \ + byTemp = _pabyDataT[2]; \ + _pabyDataT[2] = _pabyDataT[5]; \ + _pabyDataT[5] = byTemp; \ + byTemp = _pabyDataT[3]; \ + _pabyDataT[3] = _pabyDataT[4]; \ + _pabyDataT[4] = byTemp; \ +} + + +/* Until we have a safe 64 bits integer data type defined, we'll replace + * this version of the CPL_SWAP64() macro with a less efficient one. + */ +/* +#define CPL_SWAP64(x) \ + ((uint64)( \ + (uint64)(((uint64)(x) & (uint64)0x00000000000000ffULL) << 56) | \ + (uint64)(((uint64)(x) & (uint64)0x000000000000ff00ULL) << 40) | \ + (uint64)(((uint64)(x) & (uint64)0x0000000000ff0000ULL) << 24) | \ + (uint64)(((uint64)(x) & (uint64)0x00000000ff000000ULL) << 8) | \ + (uint64)(((uint64)(x) & (uint64)0x000000ff00000000ULL) >> 8) | \ + (uint64)(((uint64)(x) & (uint64)0x0000ff0000000000ULL) >> 24) | \ + (uint64)(((uint64)(x) & (uint64)0x00ff000000000000ULL) >> 40) | \ + (uint64)(((uint64)(x) & (uint64)0xff00000000000000ULL) >> 56) )) +*/ + +#define CPL_SWAPDOUBLE(p) CPL_SWAP64PTR(p) + +#ifdef CPL_MSB +# define CPL_MSBWORD16(x) (x) +# define CPL_LSBWORD16(x) CPL_SWAP16(x) +# define CPL_MSBWORD32(x) (x) +# define CPL_LSBWORD32(x) CPL_SWAP32(x) +# define CPL_MSBPTR16(x) +# define CPL_LSBPTR16(x) CPL_SWAP16PTR(x) +# define CPL_MSBPTR32(x) +# define CPL_LSBPTR32(x) CPL_SWAP32PTR(x) +# define CPL_MSBPTR64(x) +# define CPL_LSBPTR64(x) CPL_SWAP64PTR(x) +#else +# define CPL_LSBWORD16(x) (x) +# define CPL_MSBWORD16(x) CPL_SWAP16(x) +# define CPL_LSBWORD32(x) (x) +# define CPL_MSBWORD32(x) CPL_SWAP32(x) +# define CPL_LSBPTR16(x) +# define CPL_MSBPTR16(x) CPL_SWAP16PTR(x) +# define CPL_LSBPTR32(x) +# define CPL_MSBPTR32(x) CPL_SWAP32PTR(x) +# define CPL_LSBPTR64(x) +# define CPL_MSBPTR64(x) CPL_SWAP64PTR(x) +#endif + +/** Return a Int16 from the 2 bytes ordered in LSB order at address x */ +#define CPL_LSBINT16PTR(x) ((*(GByte*)(x)) | ((*(GByte*)((x)+1)) << 8)) + +/** Return a Int32 from the 4 bytes ordered in LSB order at address x */ +#define CPL_LSBINT32PTR(x) ((*(GByte*)(x)) | ((*(GByte*)((x)+1)) << 8) | \ + ((*(GByte*)((x)+2)) << 16) | ((*(GByte*)((x)+3)) << 24)) + +/** Return a signed Int16 from the 2 bytes ordered in LSB order at address x */ +#define CPL_LSBSINT16PTR(x) ((GInt16) CPL_LSBINT16PTR(x)) + +/** Return a unsigned Int16 from the 2 bytes ordered in LSB order at address x */ +#define CPL_LSBUINT16PTR(x) ((GUInt16)CPL_LSBINT16PTR(x)) + +/** Return a signed Int32 from the 4 bytes ordered in LSB order at address x */ +#define CPL_LSBSINT32PTR(x) ((GInt32) CPL_LSBINT32PTR(x)) + +/** Return a unsigned Int32 from the 4 bytes ordered in LSB order at address x */ +#define CPL_LSBUINT32PTR(x) ((GUInt32)CPL_LSBINT32PTR(x)) + + +/* Utility macro to explicitly mark intentionally unreferenced parameters. */ +#ifndef UNREFERENCED_PARAM +# ifdef UNREFERENCED_PARAMETER /* May be defined by Windows API */ +# define UNREFERENCED_PARAM(param) UNREFERENCED_PARAMETER(param) +# else +# define UNREFERENCED_PARAM(param) ((void)param) +# endif /* UNREFERENCED_PARAMETER */ +#endif /* UNREFERENCED_PARAM */ + +/*********************************************************************** + * Define CPL_CVSID() macro. It can be disabled during a build by + * defining DISABLE_CPLID in the compiler options. + * + * The cvsid_aw() function is just there to prevent reports of cpl_cvsid() + * being unused. + */ + +#ifndef DISABLE_CVSID +#if defined(__GNUC__) && __GNUC__ >= 4 +# define CPL_CVSID(string) static char cpl_cvsid[] __attribute__((used)) = string; +#else +# define CPL_CVSID(string) static char cpl_cvsid[] = string; \ +static char *cvsid_aw() { return( cvsid_aw() ? ((char *) NULL) : cpl_cvsid ); } +#endif +#else +# define CPL_CVSID(string) +#endif + +/* Null terminated variadic */ +#if defined(__GNUC__) && __GNUC__ >= 4 && !defined(DOXYGEN_SKIP) +# define CPL_NULL_TERMINATED __attribute__((__sentinel__)) +#else +# define CPL_NULL_TERMINATED +#endif + +#if defined(__GNUC__) && __GNUC__ >= 3 && !defined(DOXYGEN_SKIP) +#define CPL_PRINT_FUNC_FORMAT( format_idx, arg_idx ) __attribute__((__format__ (__printf__, format_idx, arg_idx))) +#else +#define CPL_PRINT_FUNC_FORMAT( format_idx, arg_idx ) +#endif + +#if defined(__GNUC__) && __GNUC__ >= 4 && !defined(DOXYGEN_SKIP) +#define CPL_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define CPL_WARN_UNUSED_RESULT +#endif + +#if defined(__GNUC__) && __GNUC__ >= 4 +# define CPL_UNUSED __attribute((__unused__)) +#else +/* TODO: add cases for other compilers */ +# define CPL_UNUSED +#endif + +#if defined(__GNUC__) && __GNUC__ >= 3 && !defined(DOXYGEN_SKIP) +#define CPL_NO_RETURN __attribute__((noreturn)) +#else +#define CPL_NO_RETURN +#endif + +#if !defined(DOXYGEN_SKIP) +#if defined(__has_extension) + #if __has_extension(attribute_deprecated_with_message) + /* Clang extension */ + #define CPL_WARN_DEPRECATED(x) __attribute__ ((deprecated(x))) + #else + #define CPL_WARN_DEPRECATED(x) + #endif +#elif defined(__GNUC__) + #define CPL_WARN_DEPRECATED(x) __attribute__ ((deprecated)) +#else + #define CPL_WARN_DEPRECATED(x) +#endif +#endif + +#ifdef WARN_STANDARD_PRINTF +int vsnprintf(char *str, size_t size, const char* fmt, va_list args) CPL_WARN_DEPRECATED("Use CPLvsnprintf() instead"); +int snprintf(char *str, size_t size, const char* fmt, ...) CPL_PRINT_FUNC_FORMAT(3,4) CPL_WARN_DEPRECATED("Use CPLsnprintf() instead"); +int sprintf(char *str, const char* fmt, ...) CPL_PRINT_FUNC_FORMAT(2, 3) CPL_WARN_DEPRECATED("Use CPLsprintf() instead"); +#endif + +#endif /* ndef CPL_BASE_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_progress.cpp b/bazaar/plugin/gdal/port/cpl_progress.cpp new file mode 100644 index 000000000..8fe771483 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_progress.cpp @@ -0,0 +1,243 @@ +/****************************************************************************** + * $Id$ + * + * Project: CPL - Common Portability Library + * Author: Frank Warmerdam, warmerdam@pobox.com + * Purpose: Progress function implementations. + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_progress.h" +#include "cpl_conv.h" + +CPL_CVSID("$Id: gdal_misc.cpp 25494 2013-01-13 12:55:17Z etourigny $"); + +/************************************************************************/ +/* GDALDummyProgress() */ +/************************************************************************/ + +/** + * \brief Stub progress function. + * + * This is a stub (does nothing) implementation of the GDALProgressFunc() + * semantics. It is primarily useful for passing to functions that take + * a GDALProgressFunc() argument but for which the application does not want + * to use one of the other progress functions that actually do something. + */ + +int CPL_STDCALL GDALDummyProgress( CPL_UNUSED double dfComplete, + CPL_UNUSED const char *pszMessage, + CPL_UNUSED void *pData ) +{ + return TRUE; +} + +/************************************************************************/ +/* GDALScaledProgress() */ +/************************************************************************/ +typedef struct { + GDALProgressFunc pfnProgress; + void *pData; + double dfMin; + double dfMax; +} GDALScaledProgressInfo; + +/** + * \brief Scaled progress transformer. + * + * This is the progress function that should be passed along with the + * callback data returned by GDALCreateScaledProgress(). + */ + +int CPL_STDCALL GDALScaledProgress( double dfComplete, const char *pszMessage, + void *pData ) + +{ + GDALScaledProgressInfo *psInfo = (GDALScaledProgressInfo *) pData; + + /* optimization if GDALCreateScaledProgress() provided with GDALDummyProgress */ + if( psInfo == NULL ) + return TRUE; + + return psInfo->pfnProgress( dfComplete * (psInfo->dfMax - psInfo->dfMin) + + psInfo->dfMin, + pszMessage, psInfo->pData ); +} + +/************************************************************************/ +/* GDALCreateScaledProgress() */ +/************************************************************************/ + +/** + * \brief Create scaled progress transformer. + * + * Sometimes when an operations wants to report progress it actually + * invokes several subprocesses which also take GDALProgressFunc()s, + * and it is desirable to map the progress of each sub operation into + * a portion of 0.0 to 1.0 progress of the overall process. The scaled + * progress function can be used for this. + * + * For each subsection a scaled progress function is created and + * instead of passing the overall progress func down to the sub functions, + * the GDALScaledProgress() function is passed instead. + * + * @param dfMin the value to which 0.0 in the sub operation is mapped. + * @param dfMax the value to which 1.0 is the sub operation is mapped. + * @param pfnProgress the overall progress function. + * @param pData the overall progress function callback data. + * + * @return pointer to pass as pProgressArg to sub functions. Should be freed + * with GDALDestroyScaledProgress(). + * + * Example: + * + * \code + * int MyOperation( ..., GDALProgressFunc pfnProgress, void *pProgressData ); + * + * { + * void *pScaledProgress; + * + * pScaledProgress = GDALCreateScaledProgress( 0.0, 0.5, pfnProgress, + * pProgressData ); + * GDALDoLongSlowOperation( ..., GDALScaledProgress, pScaledProgress ); + * GDALDestroyScaledProgress( pScaledProgress ); + * + * pScaledProgress = GDALCreateScaledProgress( 0.5, 1.0, pfnProgress, + * pProgressData ); + * GDALDoAnotherOperation( ..., GDALScaledProgress, pScaledProgress ); + * GDALDestroyScaledProgress( pScaledProgress ); + * + * return ...; + * } + * \endcode + */ + +void * CPL_STDCALL GDALCreateScaledProgress( double dfMin, double dfMax, + GDALProgressFunc pfnProgress, + void * pData ) + +{ + GDALScaledProgressInfo *psInfo; + + if( pfnProgress == NULL || pfnProgress == GDALDummyProgress ) + return NULL; + + psInfo = (GDALScaledProgressInfo *) + CPLCalloc(sizeof(GDALScaledProgressInfo),1); + + if( ABS(dfMin-dfMax) < 0.0000001 ) + dfMax = dfMin + 0.01; + + psInfo->pData = pData; + psInfo->pfnProgress = pfnProgress; + psInfo->dfMin = dfMin; + psInfo->dfMax = dfMax; + + return (void *) psInfo; +} + +/************************************************************************/ +/* GDALDestroyScaledProgress() */ +/************************************************************************/ + +/** + * \brief Cleanup scaled progress handle. + * + * This function cleans up the data associated with a scaled progress function + * as returned by GADLCreateScaledProgress(). + * + * @param pData scaled progress handle returned by GDALCreateScaledProgress(). + */ + +void CPL_STDCALL GDALDestroyScaledProgress( void * pData ) + +{ + CPLFree( pData ); +} + +/************************************************************************/ +/* GDALTermProgress() */ +/************************************************************************/ + +/** + * \brief Simple progress report to terminal. + * + * This progress reporter prints simple progress report to the + * terminal window. The progress report generally looks something like + * this: + +\verbatim +0...10...20...30...40...50...60...70...80...90...100 - done. +\endverbatim + + * Every 2.5% of progress another number or period is emitted. Note that + * GDALTermProgress() uses internal static data to keep track of the last + * percentage reported and will get confused if two terminal based progress + * reportings are active at the same time. + * + * The GDALTermProgress() function maintains an internal memory of the + * last percentage complete reported in a static variable, and this makes + * it unsuitable to have multiple GDALTermProgress()'s active eithin a + * single thread or across multiple threads. + * + * @param dfComplete completion ratio from 0.0 to 1.0. + * @param pszMessage optional message. + * @param pProgressArg ignored callback data argument. + * + * @return Always returns TRUE indicating the process should continue. + */ + +int CPL_STDCALL GDALTermProgress( CPL_UNUSED double dfComplete, + CPL_UNUSED const char *pszMessage, + void * pProgressArg ) +{ + static int nLastTick = -1; + int nThisTick = (int) (dfComplete * 40.0); + + (void) pProgressArg; + + nThisTick = MIN(40,MAX(0,nThisTick)); + + // Have we started a new progress run? + if( nThisTick < nLastTick && nLastTick >= 39 ) + nLastTick = -1; + + if( nThisTick <= nLastTick ) + return TRUE; + + while( nThisTick > nLastTick ) + { + nLastTick++; + if( nLastTick % 4 == 0 ) + fprintf( stdout, "%d", (nLastTick / 4) * 10 ); + else + fprintf( stdout, "." ); + } + + if( nThisTick == 40 ) + fprintf( stdout, " - done.\n" ); + else + fflush( stdout ); + + return TRUE; +} diff --git a/bazaar/plugin/gdal/port/cpl_progress.h b/bazaar/plugin/gdal/port/cpl_progress.h new file mode 100644 index 000000000..aa5fe523c --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_progress.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * $Id$ + * + * Project: CPL - Common Portability Library + * Author: Frank Warmerdam, warmerdam@pobox.com + * Purpose: Prototypes and definitions for progress functions. + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_PROGRESS_H_INCLUDED +#define CPL_PROGRESS_H_INCLUDED + +#include "cpl_port.h" + +CPL_C_START + +typedef int (CPL_STDCALL *GDALProgressFunc)(double dfComplete, const char *pszMessage, void *pProgressArg); + +int CPL_DLL CPL_STDCALL GDALDummyProgress( double, const char *, void *); +int CPL_DLL CPL_STDCALL GDALTermProgress( double, const char *, void *); +int CPL_DLL CPL_STDCALL GDALScaledProgress( double, const char *, void *); +void CPL_DLL * CPL_STDCALL GDALCreateScaledProgress( double, double, + GDALProgressFunc, void * ); +void CPL_DLL CPL_STDCALL GDALDestroyScaledProgress( void * ); +CPL_C_END + +#endif /* ndef CPL_PROGRESS_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_quad_tree.cpp b/bazaar/plugin/gdal/port/cpl_quad_tree.cpp new file mode 100644 index 000000000..4cb451235 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_quad_tree.cpp @@ -0,0 +1,852 @@ +/****************************************************************************** + * $Id: cpl_quad_tree.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implementation of quadtree building and searching functions. + * Derived from shapelib and mapserver implementations + * Author: Frank Warmerdam, warmerdam@pobox.com + * Even Rouault, + * + ****************************************************************************** + * Copyright (c) 1999-2008, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + */ + +#include "cpl_conv.h" +#include "cpl_quad_tree.h" + +CPL_CVSID("$Id: cpl_quad_tree.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +#define MAX_DEFAULT_TREE_DEPTH 12 +#define MAX_SUBNODES 4 + +typedef struct _QuadTreeNode QuadTreeNode; + +struct _QuadTreeNode +{ + /* area covered by this psNode */ + CPLRectObj rect; + + int nFeatures; /* number of shapes stored at this psNode. */ + + int nNumSubNodes; /* number of active subnodes */ + + void **pahFeatures; /* list of shapes stored at this psNode. */ + CPLRectObj *pasBounds; + + QuadTreeNode *apSubNode[MAX_SUBNODES]; +}; + +struct _CPLQuadTree +{ + QuadTreeNode *psRoot; + CPLQuadTreeGetBoundsFunc pfnGetBounds; + int nFeatures; + int nMaxDepth; + int nBucketCapacity; + double dfSplitRatio; +}; + + +static void CPLQuadTreeAddFeatureInternal(CPLQuadTree *hQuadTree, + void* hFeature, + const CPLRectObj *pRect); + +/* -------------------------------------------------------------------- */ +/* If the following is 0.5, psNodes will be split in half. If it */ +/* is 0.6 then each apSubNode will contain 60% of the parent */ +/* psNode, with 20% representing overlap. This can be help to */ +/* prevent small objects on a boundary from shifting too high */ +/* up the hQuadTree. */ +/* -------------------------------------------------------------------- */ +#define DEFAULT_SPLIT_RATIO 0.55 + +/* +** Returns TRUE if rectangle a is contained in rectangle b +*/ +static CPL_INLINE int CPL_RectContained(const CPLRectObj *a, const CPLRectObj *b) +{ + if(a->minx >= b->minx && a->maxx <= b->maxx) + if(a->miny >= b->miny && a->maxy <= b->maxy) + return(TRUE); + return(FALSE); +} + +/* +** Returns TRUE if rectangles a and b overlap +*/ +static CPL_INLINE int CPL_RectOverlap(const CPLRectObj *a, const CPLRectObj *b) +{ + if(a->minx > b->maxx) return(FALSE); + if(a->maxx < b->minx) return(FALSE); + if(a->miny > b->maxy) return(FALSE); + if(a->maxy < b->miny) return(FALSE); + return(TRUE); +} + +/************************************************************************/ +/* CPLQuadTreeNodeCreate() */ +/************************************************************************/ + +static QuadTreeNode *CPLQuadTreeNodeCreate(const CPLRectObj* pRect) +{ + QuadTreeNode *psNode; + + psNode = (QuadTreeNode *) CPLMalloc(sizeof(QuadTreeNode)); + + psNode->nFeatures = 0; + psNode->pahFeatures = NULL; + psNode->pasBounds = NULL; + + psNode->nNumSubNodes = 0; + + memcpy(&(psNode->rect), pRect, sizeof(CPLRectObj)); + + return psNode; +} + +/************************************************************************/ +/* CPLQuadTreeCreate() */ +/************************************************************************/ + +/** + * Create a new quadtree + * + * @param pGlobalBounds a pointer to the global extent of all + * the elements that will be inserted + * @param pfnGetBounds a user provided function to get the bounding box of + * the inserted elements. If it is set to NULL, then + * CPLQuadTreeInsertWithBounds() must be used, and + * extra memory will be used to keep features bounds in the + * quad tree. + * + * @return a newly allocated quadtree + */ + +CPLQuadTree *CPLQuadTreeCreate(const CPLRectObj* pGlobalBounds, CPLQuadTreeGetBoundsFunc pfnGetBounds) +{ + CPLQuadTree *hQuadTree; + + CPLAssert(pGlobalBounds); + + /* -------------------------------------------------------------------- */ + /* Allocate the hQuadTree object */ + /* -------------------------------------------------------------------- */ + hQuadTree = (CPLQuadTree *) CPLMalloc(sizeof(CPLQuadTree)); + + hQuadTree->nFeatures = 0; + hQuadTree->pfnGetBounds = pfnGetBounds; + hQuadTree->nMaxDepth = 0; + hQuadTree->nBucketCapacity = 8; + + hQuadTree->dfSplitRatio = DEFAULT_SPLIT_RATIO; + + /* -------------------------------------------------------------------- */ + /* Allocate the psRoot psNode. */ + /* -------------------------------------------------------------------- */ + hQuadTree->psRoot = CPLQuadTreeNodeCreate(pGlobalBounds); + + return hQuadTree; +} + +/************************************************************************/ +/* CPLQuadTreeGetAdvisedMaxDepth() */ +/************************************************************************/ + +/** + * Returns the optimal depth of a quadtree to hold nExpectedFeatures + * + * @param nExpectedFeatures the expected maximum number of elements to be inserted + * + * @return the optimal depth of a quadtree to hold nExpectedFeatures + */ + +int CPLQuadTreeGetAdvisedMaxDepth(int nExpectedFeatures) +{ +/* -------------------------------------------------------------------- */ +/* Try to select a reasonable one */ +/* that implies approximately 8 shapes per node. */ +/* -------------------------------------------------------------------- */ + int nMaxDepth = 0; + int nMaxNodeCount = 1; + + while( nMaxNodeCount*4 < nExpectedFeatures ) + { + nMaxDepth += 1; + nMaxNodeCount = nMaxNodeCount * 2; + } + + CPLDebug( "CPLQuadTree", + "Estimated spatial index tree depth: %d", + nMaxDepth ); + + /* NOTE: Due to problems with memory allocation for deep trees, + * automatically estimated depth is limited up to 12 levels. + * See Ticket #1594 for detailed discussion. + */ + if( nMaxDepth > MAX_DEFAULT_TREE_DEPTH ) + { + nMaxDepth = MAX_DEFAULT_TREE_DEPTH; + + CPLDebug( "CPLQuadTree", + "Falling back to max number of allowed index tree levels (%d).", + MAX_DEFAULT_TREE_DEPTH ); + + } + + return nMaxDepth; +} + +/************************************************************************/ +/* CPLQuadTreeSetMaxDepth() */ +/************************************************************************/ + +/** + * Set the maximum depth of a quadtree. By default, quad trees have + * no maximum depth, but a maximum bucket capacity. + * + * @param hQuadTree the quad tree + * @param nMaxDepth the maximum depth allowed + */ + +void CPLQuadTreeSetMaxDepth(CPLQuadTree *hQuadTree, int nMaxDepth) +{ + hQuadTree->nMaxDepth = nMaxDepth; +} + +/************************************************************************/ +/* CPLQuadTreeSetBucketCapacity() */ +/************************************************************************/ + +/** + * Set the maximum capacity of a node of a quadtree. The default value is 8. + * Note that the maximum capacity will only be honoured if the features + * inserted have a point geometry. Otherwise it may be exceeded. + * + * @param hQuadTree the quad tree + * @param nBucketCapacity the maximum capactiy of a node of a quadtree + */ + +void CPLQuadTreeSetBucketCapacity(CPLQuadTree *hQuadTree, int nBucketCapacity) +{ + if( nBucketCapacity > 0 ) + hQuadTree->nBucketCapacity = nBucketCapacity; +} + +/************************************************************************/ +/* CPLQuadTreeInsert() */ +/************************************************************************/ + +/** + * Insert a feature into a quadtree + * + * @param hQuadTree the quad tree + * @param hFeature the feature to insert + */ + +void CPLQuadTreeInsert(CPLQuadTree * hQuadTree, void* hFeature) +{ + CPLRectObj bounds; + if( hQuadTree->pfnGetBounds == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "hQuadTree->pfnGetBounds == NULL"); + return; + } + hQuadTree->nFeatures ++; + hQuadTree->pfnGetBounds(hFeature, &bounds); + CPLQuadTreeAddFeatureInternal(hQuadTree, hFeature, &bounds); +} + +/************************************************************************/ +/* CPLQuadTreeInsertWithBounds() */ +/************************************************************************/ + +/** + * Insert a feature into a quadtree + * + * @param hQuadTree the quad tree + * @param hFeature the feature to insert + * @param psBounds bounds of the feature + */ +void CPLQuadTreeInsertWithBounds(CPLQuadTree *hQuadTree, + void* hFeature, + const CPLRectObj* psBounds) +{ + hQuadTree->nFeatures ++; + CPLQuadTreeAddFeatureInternal(hQuadTree, hFeature, psBounds); +} + +/************************************************************************/ +/* CPLQuadTreeNodeDestroy() */ +/************************************************************************/ + +static void CPLQuadTreeNodeDestroy(QuadTreeNode *psNode) +{ + int i; + + for(i=0; inNumSubNodes; i++ ) + { + if(psNode->apSubNode[i]) + CPLQuadTreeNodeDestroy(psNode->apSubNode[i]); + } + + if(psNode->pahFeatures) + { + CPLFree(psNode->pahFeatures); + CPLFree(psNode->pasBounds); + } + + CPLFree(psNode); +} + + +/************************************************************************/ +/* CPLQuadTreeDestroy() */ +/************************************************************************/ + +/** + * Destroy a quadtree + * + * @param hQuadTree the quad tree to destroy + */ + +void CPLQuadTreeDestroy(CPLQuadTree *hQuadTree) +{ + CPLAssert(hQuadTree); + CPLQuadTreeNodeDestroy(hQuadTree->psRoot); + CPLFree(hQuadTree); +} + + + +/************************************************************************/ +/* CPLQuadTreeSplitBounds() */ +/************************************************************************/ + +static void CPLQuadTreeSplitBounds( double dfSplitRatio, + const CPLRectObj *in, + CPLRectObj *out1, + CPLRectObj *out2) +{ + double range; + + /* -------------------------------------------------------------------- */ + /* The output bounds will be very similar to the input bounds, */ + /* so just copy over to start. */ + /* -------------------------------------------------------------------- */ + memcpy(out1, in, sizeof(CPLRectObj)); + memcpy(out2, in, sizeof(CPLRectObj)); + + /* -------------------------------------------------------------------- */ + /* Split in X direction. */ + /* -------------------------------------------------------------------- */ + if((in->maxx - in->minx) > (in->maxy - in->miny)) { + range = in->maxx - in->minx; + + out1->maxx = in->minx + range * dfSplitRatio; + out2->minx = in->maxx - range * dfSplitRatio; + } + + /* -------------------------------------------------------------------- */ + /* Otherwise split in Y direction. */ + /* -------------------------------------------------------------------- */ + else { + range = in->maxy - in->miny; + + out1->maxy = in->miny + range * dfSplitRatio; + out2->miny = in->maxy - range * dfSplitRatio; + } +} + +/************************************************************************/ +/* CPLQuadTreeNodeAddFeatureAlg1() */ +/************************************************************************/ + +static void CPLQuadTreeNodeAddFeatureAlg1( CPLQuadTree* hQuadTree, + QuadTreeNode *psNode, + void* hFeature, + const CPLRectObj* pRect) +{ + int i; + if (psNode->nNumSubNodes == 0) + { + /* If we have reached the max bucket capacity, try to insert */ + /* in a subnode if possible */ + if (psNode->nFeatures >= hQuadTree->nBucketCapacity) + { + CPLRectObj half1, half2, quad1, quad2, quad3, quad4; + + CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &psNode->rect, &half1, &half2); + CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half1, &quad1, &quad2); + CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half2, &quad3, &quad4); + + if (memcmp(&psNode->rect, &quad1, sizeof(CPLRectObj)) != 0 && + memcmp(&psNode->rect, &quad2, sizeof(CPLRectObj)) != 0 && + memcmp(&psNode->rect, &quad3, sizeof(CPLRectObj)) != 0 && + memcmp(&psNode->rect, &quad4, sizeof(CPLRectObj)) != 0 && + (CPL_RectContained(pRect, &quad1) || + CPL_RectContained(pRect, &quad2) || + CPL_RectContained(pRect, &quad3) || + CPL_RectContained(pRect, &quad4))) + { + psNode->nNumSubNodes = 4; + psNode->apSubNode[0] = CPLQuadTreeNodeCreate(&quad1); + psNode->apSubNode[1] = CPLQuadTreeNodeCreate(&quad2); + psNode->apSubNode[2] = CPLQuadTreeNodeCreate(&quad3); + psNode->apSubNode[3] = CPLQuadTreeNodeCreate(&quad4); + + int oldNumFeatures = psNode->nFeatures; + void** oldFeatures = psNode->pahFeatures; + CPLRectObj* pasOldBounds = psNode->pasBounds; + psNode->nFeatures = 0; + psNode->pahFeatures = NULL; + psNode->pasBounds = NULL; + + /* redispatch existing pahFeatures in apSubNodes */ + int i; + for(i=0;ipfnGetBounds == NULL ) + CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode, oldFeatures[i], &pasOldBounds[i]); + else + { + CPLRectObj bounds; + hQuadTree->pfnGetBounds(oldFeatures[i], &bounds); + CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode, oldFeatures[i], &bounds); + } + } + + CPLFree(oldFeatures); + CPLFree(pasOldBounds); + + /* recurse back on this psNode now that it has apSubNodes */ + CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, psNode, hFeature, pRect); + return; + } + } + } + else + { + /* -------------------------------------------------------------------- */ + /* If there are apSubNodes, then consider whether this object */ + /* will fit in them. */ + /* -------------------------------------------------------------------- */ + for(i=0; inNumSubNodes; i++ ) + { + if( CPL_RectContained(pRect, &psNode->apSubNode[i]->rect)) + { + CPLQuadTreeNodeAddFeatureAlg1( hQuadTree, psNode->apSubNode[i], hFeature, pRect); + return; + } + } + } + +/* -------------------------------------------------------------------- */ +/* If none of that worked, just add it to this psNodes list. */ +/* -------------------------------------------------------------------- */ + psNode->nFeatures++; + + if( psNode->nFeatures == 1 ) + { + CPLAssert( psNode->pahFeatures == NULL ); + psNode->pahFeatures = (void**) CPLMalloc( hQuadTree->nBucketCapacity * sizeof(void*) ); + if( hQuadTree->pfnGetBounds == NULL ) + psNode->pasBounds = (CPLRectObj*) CPLMalloc( hQuadTree->nBucketCapacity * sizeof(CPLRectObj) ); + } + else if( psNode->nFeatures > hQuadTree->nBucketCapacity ) + { + psNode->pahFeatures = (void**) CPLRealloc( psNode->pahFeatures, sizeof(void*) * psNode->nFeatures ); + if( hQuadTree->pfnGetBounds == NULL ) + psNode->pasBounds = (CPLRectObj*) CPLRealloc( psNode->pasBounds, sizeof(CPLRectObj) * psNode->nFeatures ); + } + psNode->pahFeatures[psNode->nFeatures-1] = hFeature; + if( hQuadTree->pfnGetBounds == NULL ) + psNode->pasBounds[psNode->nFeatures-1] = *pRect; + + return ; +} + + +/************************************************************************/ +/* CPLQuadTreeNodeAddFeatureAlg2() */ +/************************************************************************/ + +static void CPLQuadTreeNodeAddFeatureAlg2( CPLQuadTree *hQuadTree, + QuadTreeNode *psNode, + void* hFeature, + const CPLRectObj* pRect, + int nMaxDepth) +{ + int i; + + /* -------------------------------------------------------------------- */ + /* If there are apSubNodes, then consider whether this object */ + /* will fit in them. */ + /* -------------------------------------------------------------------- */ + if( nMaxDepth > 1 && psNode->nNumSubNodes > 0 ) + { + for(i=0; inNumSubNodes; i++ ) + { + if( CPL_RectContained(pRect, &psNode->apSubNode[i]->rect)) + { + CPLQuadTreeNodeAddFeatureAlg2( hQuadTree, psNode->apSubNode[i], + hFeature, pRect, nMaxDepth-1); + return; + } + } + } + + /* -------------------------------------------------------------------- */ + /* Otherwise, consider creating four apSubNodes if could fit into */ + /* them, and adding to the appropriate apSubNode. */ + /* -------------------------------------------------------------------- */ + else if( nMaxDepth > 1 && psNode->nNumSubNodes == 0 ) + { + CPLRectObj half1, half2, quad1, quad2, quad3, quad4; + + CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &psNode->rect, &half1, &half2); + CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half1, &quad1, &quad2); + CPLQuadTreeSplitBounds(hQuadTree->dfSplitRatio, &half2, &quad3, &quad4); + + if( memcmp(&psNode->rect, &quad1, sizeof(CPLRectObj)) != 0 && + memcmp(&psNode->rect, &quad2, sizeof(CPLRectObj)) != 0 && + memcmp(&psNode->rect, &quad3, sizeof(CPLRectObj)) != 0 && + memcmp(&psNode->rect, &quad4, sizeof(CPLRectObj)) != 0 && + (CPL_RectContained(pRect, &quad1) || + CPL_RectContained(pRect, &quad2) || + CPL_RectContained(pRect, &quad3) || + CPL_RectContained(pRect, &quad4)) ) + { + psNode->nNumSubNodes = 4; + psNode->apSubNode[0] = CPLQuadTreeNodeCreate(&quad1); + psNode->apSubNode[1] = CPLQuadTreeNodeCreate(&quad2); + psNode->apSubNode[2] = CPLQuadTreeNodeCreate(&quad3); + psNode->apSubNode[3] = CPLQuadTreeNodeCreate(&quad4); + + /* recurse back on this psNode now that it has apSubNodes */ + CPLQuadTreeNodeAddFeatureAlg2(hQuadTree, psNode, hFeature, pRect, nMaxDepth); + return; + } + } + +/* -------------------------------------------------------------------- */ +/* If none of that worked, just add it to this psNodes list. */ +/* -------------------------------------------------------------------- */ + psNode->nFeatures++; + + psNode->pahFeatures = + (void**) CPLRealloc( psNode->pahFeatures, + sizeof(void*) * psNode->nFeatures ); + if( hQuadTree->pfnGetBounds == NULL ) + { + psNode->pasBounds = + (CPLRectObj*) CPLRealloc( psNode->pasBounds, + sizeof(CPLRectObj) * psNode->nFeatures ); + } + psNode->pahFeatures[psNode->nFeatures-1] = hFeature; + if( hQuadTree->pfnGetBounds == NULL ) + { + psNode->pasBounds[psNode->nFeatures-1] = *pRect; + } +} + + +/************************************************************************/ +/* CPLQuadTreeAddFeatureInternal() */ +/************************************************************************/ + +static void CPLQuadTreeAddFeatureInternal(CPLQuadTree *hQuadTree, + void* hFeature, + const CPLRectObj *pRect) +{ + if (hQuadTree->nMaxDepth == 0) + { + CPLQuadTreeNodeAddFeatureAlg1(hQuadTree, hQuadTree->psRoot, + hFeature, pRect); + } + else + { + CPLQuadTreeNodeAddFeatureAlg2(hQuadTree, hQuadTree->psRoot, + hFeature, pRect, hQuadTree->nMaxDepth); + } +} + +/************************************************************************/ +/* CPLQuadTreeCollectFeatures() */ +/************************************************************************/ + +static void CPLQuadTreeCollectFeatures(const CPLQuadTree *hQuadTree, + const QuadTreeNode *psNode, + const CPLRectObj* pAoi, + int* pnFeatureCount, + int* pnMaxFeatures, + void*** pppFeatureList) +{ + int i; + + /* -------------------------------------------------------------------- */ + /* Does this psNode overlap the area of interest at all? If not, */ + /* return without adding to the list at all. */ + /* -------------------------------------------------------------------- */ + if(!CPL_RectOverlap(&psNode->rect, pAoi)) + return; + +/* -------------------------------------------------------------------- */ +/* Grow the list to hold the features on this psNode. */ +/* -------------------------------------------------------------------- */ + if( *pnFeatureCount + psNode->nFeatures > *pnMaxFeatures ) + { + *pnMaxFeatures = (*pnFeatureCount + psNode->nFeatures) * 2 + 20; + *pppFeatureList = (void**) + CPLRealloc(*pppFeatureList,sizeof(void*) * *pnMaxFeatures); + } + + /* -------------------------------------------------------------------- */ + /* Add the local features to the list. */ + /* -------------------------------------------------------------------- */ + for(i=0; inFeatures; i++) + { + if( hQuadTree->pfnGetBounds == NULL ) + { + if (CPL_RectOverlap(&psNode->pasBounds[i], pAoi)) + (*pppFeatureList)[(*pnFeatureCount)++] = psNode->pahFeatures[i]; + } + else + { + CPLRectObj bounds; + hQuadTree->pfnGetBounds(psNode->pahFeatures[i], &bounds); + if (CPL_RectOverlap(&bounds, pAoi)) + (*pppFeatureList)[(*pnFeatureCount)++] = psNode->pahFeatures[i]; + } + } + + /* -------------------------------------------------------------------- */ + /* Recurse to subnodes if they exist. */ + /* -------------------------------------------------------------------- */ + for(i=0; inNumSubNodes; i++) + { + if(psNode->apSubNode[i]) + CPLQuadTreeCollectFeatures(hQuadTree, psNode->apSubNode[i], pAoi, + pnFeatureCount, pnMaxFeatures, pppFeatureList); + } +} + +/************************************************************************/ +/* CPLQuadTreeSearch() */ +/************************************************************************/ + +/** + * Returns all the elements inserted whose bounding box intersects the + * provided area of interest + * + * @param hQuadTree the quad tree + * @param pAoi the pointer to the area of interest + * @param pnFeatureCount the user data provided to the function. + * + * @return an array of features that must be freed with CPLFree + */ + +void** CPLQuadTreeSearch(const CPLQuadTree *hQuadTree, + const CPLRectObj* pAoi, + int* pnFeatureCount) +{ + void** ppFeatureList = NULL; + int nMaxFeatures = 0; + int nFeatureCount = 0; + + CPLAssert(hQuadTree); + CPLAssert(pAoi); + + if (pnFeatureCount == NULL) + pnFeatureCount = &nFeatureCount; + + *pnFeatureCount = 0; + CPLQuadTreeCollectFeatures(hQuadTree, hQuadTree->psRoot, pAoi, + pnFeatureCount, &nMaxFeatures, &ppFeatureList); + + return(ppFeatureList); +} + +/************************************************************************/ +/* CPLQuadTreeNodeForeach() */ +/************************************************************************/ + +static int CPLQuadTreeNodeForeach(const QuadTreeNode *psNode, + CPLQuadTreeForeachFunc pfnForeach, + void* pUserData) +{ + int i; + for(i=0; inNumSubNodes; i++ ) + { + if (CPLQuadTreeNodeForeach(psNode->apSubNode[i], pfnForeach, pUserData) == FALSE) + return FALSE; + } + + for(i=0; inFeatures; i++) + { + if (pfnForeach(psNode->pahFeatures[i], pUserData) == FALSE) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* CPLQuadTreeForeach() */ +/************************************************************************/ + +/** + * Walk through the quadtree and runs the provided function on all the + * elements + * + * This function is provided with the user_data argument of pfnForeach. + * It must return TRUE to go on the walk through the hash set, or FALSE to + * make it stop. + * + * Note : the structure of the quadtree must *NOT* be modified during the + * walk. + * + * @param hQuadTree the quad tree + * @param pfnForeach the function called on each element. + * @param pUserData the user data provided to the function. + */ + +void CPLQuadTreeForeach(const CPLQuadTree *hQuadTree, + CPLQuadTreeForeachFunc pfnForeach, + void* pUserData) +{ + CPLAssert(hQuadTree); + CPLAssert(pfnForeach); + CPLQuadTreeNodeForeach(hQuadTree->psRoot, pfnForeach, pUserData); +} + +/************************************************************************/ +/* CPLQuadTreeDumpNode() */ +/************************************************************************/ + +static void CPLQuadTreeDumpNode(const QuadTreeNode *psNode, + int nIndentLevel, + CPLQuadTreeDumpFeatureFunc pfnDumpFeatureFunc, + void* pUserData) +{ + int i; + int count; + if (psNode->nNumSubNodes) + { + for(count=nIndentLevel;--count>=0;) + printf(" "); + printf("SubhQuadTrees :\n"); + for(i=0; inNumSubNodes; i++ ) + { + for(count=nIndentLevel+1;--count>=0;) + printf(" "); + printf("SubhQuadTree %d :\n", i+1); + CPLQuadTreeDumpNode(psNode->apSubNode[i], nIndentLevel + 2, + pfnDumpFeatureFunc, pUserData); + } + } + if (psNode->nFeatures) + { + for(count=nIndentLevel;--count>=0;) + printf(" "); + printf("Leaves (%d):\n", psNode->nFeatures); + for(i=0; inFeatures; i++) + { + if (pfnDumpFeatureFunc) + pfnDumpFeatureFunc(psNode->pahFeatures[i], nIndentLevel + 2, + pUserData); + else + { + for(count=nIndentLevel + 1;--count>=0;) + printf(" "); + printf("%p\n", psNode->pahFeatures[i]); + } + } + } +} + +/************************************************************************/ +/* CPLQuadTreeDump() */ +/************************************************************************/ + +void CPLQuadTreeDump(const CPLQuadTree *hQuadTree, + CPLQuadTreeDumpFeatureFunc pfnDumpFeatureFunc, + void* pUserData) +{ + CPLQuadTreeDumpNode(hQuadTree->psRoot, 0, pfnDumpFeatureFunc, pUserData); +} + +/************************************************************************/ +/* CPLQuadTreeGetStatsNode() */ +/************************************************************************/ + +static +void CPLQuadTreeGetStatsNode(const QuadTreeNode *psNode, + int nDepthLevel, + int* pnNodeCount, + int* pnMaxDepth, + int* pnMaxBucketCapacity) +{ + int i; + (*pnNodeCount) ++; + if (nDepthLevel > *pnMaxDepth) + *pnMaxDepth = nDepthLevel; + if (psNode->nFeatures > *pnMaxBucketCapacity) + *pnMaxBucketCapacity = psNode->nFeatures; + for(i=0; inNumSubNodes; i++ ) + { + CPLQuadTreeGetStatsNode(psNode->apSubNode[i], nDepthLevel + 1, + pnNodeCount, pnMaxDepth, pnMaxBucketCapacity); + } +} + + +/************************************************************************/ +/* CPLQuadTreeGetStats() */ +/************************************************************************/ + +void CPLQuadTreeGetStats(const CPLQuadTree *hQuadTree, + int* pnFeatureCount, + int* pnNodeCount, + int* pnMaxDepth, + int* pnMaxBucketCapacity) +{ + int nFeatureCount, nNodeCount, nMaxDepth, nMaxBucketCapacity; + CPLAssert(hQuadTree); + if (pnFeatureCount == NULL) + pnFeatureCount = &nFeatureCount; + if (pnNodeCount == NULL) + pnNodeCount = &nNodeCount; + if (pnMaxDepth == NULL) + pnMaxDepth = &nMaxDepth; + if (pnMaxBucketCapacity == NULL) + pnMaxBucketCapacity = &nMaxBucketCapacity; + + *pnFeatureCount = hQuadTree->nFeatures; + *pnNodeCount = 0; + *pnMaxDepth = 1; + *pnMaxBucketCapacity = 0; + + CPLQuadTreeGetStatsNode(hQuadTree->psRoot, 0, pnNodeCount, pnMaxDepth, pnMaxBucketCapacity); +} diff --git a/bazaar/plugin/gdal/port/cpl_quad_tree.h b/bazaar/plugin/gdal/port/cpl_quad_tree.h new file mode 100644 index 000000000..7b6bfc24e --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_quad_tree.h @@ -0,0 +1,100 @@ +/********************************************************************** + * $Id: cpl_quad_tree.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implementation of quadtree building and searching functions. + * Derived from shapelib and mapserver implementations + * Author: Frank Warmerdam, warmerdam@pobox.com + * Even Rouault, + * + ****************************************************************************** + * Copyright (c) 1999-2008, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_QUAD_TREE_H_INCLUDED +#define _CPL_QUAD_TREE_H_INCLUDED + +#include "cpl_port.h" + +/** + * \file cpl_quad_tree.h + * + * Quad tree implementation. + * + * A quadtree is a tree data structure in which each internal node + * has up to four children. Quadtrees are most often used to partition + * a two dimensional space by recursively subdividing it into four + * quadrants or regions + */ + +CPL_C_START + +/* Types */ + +typedef struct { + double minx, miny, maxx, maxy; +} CPLRectObj; + +typedef struct _CPLQuadTree CPLQuadTree; + +typedef void (*CPLQuadTreeGetBoundsFunc)(const void* hFeature, CPLRectObj* pBounds); +typedef int (*CPLQuadTreeForeachFunc)(void* pElt, void* pUserData); +typedef void (*CPLQuadTreeDumpFeatureFunc)(const void* hFeature, int nIndentLevel, void* pUserData); + +/* Functions */ + +CPLQuadTree CPL_DLL *CPLQuadTreeCreate(const CPLRectObj* pGlobalBounds, + CPLQuadTreeGetBoundsFunc pfnGetBounds); +void CPL_DLL CPLQuadTreeDestroy(CPLQuadTree *hQuadtree); + +void CPL_DLL CPLQuadTreeSetBucketCapacity(CPLQuadTree *hQuadtree, + int nBucketCapacity); +int CPL_DLL CPLQuadTreeGetAdvisedMaxDepth(int nExpectedFeatures); +void CPL_DLL CPLQuadTreeSetMaxDepth(CPLQuadTree *hQuadtree, + int nMaxDepth); + +void CPL_DLL CPLQuadTreeInsert(CPLQuadTree *hQuadtree, + void* hFeature); +void CPL_DLL CPLQuadTreeInsertWithBounds(CPLQuadTree *hQuadtree, + void* hFeature, + const CPLRectObj* psBounds); + +void CPL_DLL **CPLQuadTreeSearch(const CPLQuadTree *hQuadtree, + const CPLRectObj* pAoi, + int* pnFeatureCount); + +void CPL_DLL CPLQuadTreeForeach(const CPLQuadTree *hQuadtree, + CPLQuadTreeForeachFunc pfnForeach, + void* pUserData); + +void CPL_DLL CPLQuadTreeDump(const CPLQuadTree *hQuadtree, + CPLQuadTreeDumpFeatureFunc pfnDumpFeatureFunc, + void* pUserData); +void CPL_DLL CPLQuadTreeGetStats(const CPLQuadTree *hQuadtree, + int* pnFeatureCount, + int* pnNodeCount, + int* pnMaxDepth, + int* pnMaxBucketCapacity); + +CPL_C_END + +#endif diff --git a/bazaar/plugin/gdal/port/cpl_recode.cpp b/bazaar/plugin/gdal/port/cpl_recode.cpp new file mode 100644 index 000000000..2a8e6aa76 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_recode.cpp @@ -0,0 +1,363 @@ +/********************************************************************** + * $Id: cpl_recode.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Name: cpl_recode.cpp + * Project: CPL - Common Portability Library + * Purpose: Character set recoding and char/wchar_t conversions. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ********************************************************************** + * Copyright (c) 2011, Andrey Kiselev + * Copyright (c) 2008, Frank Warmerdam + * Copyright (c) 2011-2014, Even Rouault + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + **********************************************************************/ + +#include "cpl_string.h" + +CPL_CVSID("$Id: cpl_recode.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +#ifdef CPL_RECODE_ICONV +extern void CPLClearRecodeIconvWarningFlags(); +extern char *CPLRecodeIconv( const char *, const char *, const char * ); +extern char *CPLRecodeFromWCharIconv( const wchar_t *, + const char *, const char * ); +extern wchar_t *CPLRecodeToWCharIconv( const char *, + const char *, const char * ); +#endif /* CPL_RECODE_ICONV */ + +extern void CPLClearRecodeStubWarningFlags(); +extern char *CPLRecodeStub( const char *, const char *, const char * ); +extern char *CPLRecodeFromWCharStub( const wchar_t *, + const char *, const char * ); +extern wchar_t *CPLRecodeToWCharStub( const char *, + const char *, const char * ); +extern int CPLIsUTF8Stub( const char *, int ); + +/************************************************************************/ +/* CPLRecode() */ +/************************************************************************/ + +/** + * Convert a string from a source encoding to a destination encoding. + * + * The only guaranteed supported encodings are CPL_ENC_UTF8, CPL_ENC_ASCII + * and CPL_ENC_ISO8859_1. Currently, the following conversions are supported : + *
      + *
    • CPL_ENC_ASCII -> CPL_ENC_UTF8 or CPL_ENC_ISO8859_1 (no conversion in fact)
    • + *
    • CPL_ENC_ISO8859_1 -> CPL_ENC_UTF8
    • + *
    • CPL_ENC_UTF8 -> CPL_ENC_ISO8859_1
    • + *
    + * + * If an error occurs an error may, or may not be posted with CPLError(). + * + * @param pszSource a NULL terminated string. + * @param pszSrcEncoding the source encoding. + * @param pszDstEncoding the destination encoding. + * + * @return a NULL terminated string which should be freed with CPLFree(). + * + * @since GDAL 1.6.0 + */ + +char CPL_DLL *CPLRecode( const char *pszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ +/* -------------------------------------------------------------------- */ +/* Handle a few common short cuts. */ +/* -------------------------------------------------------------------- */ + if ( EQUAL(pszSrcEncoding, pszDstEncoding) ) + return CPLStrdup(pszSource); + + if ( EQUAL(pszSrcEncoding, CPL_ENC_ASCII) + && ( EQUAL(pszDstEncoding, CPL_ENC_UTF8) + || EQUAL(pszDstEncoding, CPL_ENC_ISO8859_1) ) ) + return CPLStrdup(pszSource); + +#ifdef CPL_RECODE_ICONV +/* -------------------------------------------------------------------- */ +/* CPL_ENC_ISO8859_1 -> CPL_ENC_UTF8 */ +/* and CPL_ENC_UTF8 -> CPL_ENC_ISO8859_1 conversions are hadled */ +/* very well by the stub implementation which is faster than the */ +/* iconv() route. Use a stub for these two ones and iconv() */ +/* everything else. */ +/* -------------------------------------------------------------------- */ + if ( ( EQUAL(pszSrcEncoding, CPL_ENC_ISO8859_1) + && EQUAL(pszDstEncoding, CPL_ENC_UTF8) ) + || ( EQUAL(pszSrcEncoding, CPL_ENC_UTF8) + && EQUAL(pszDstEncoding, CPL_ENC_ISO8859_1) ) ) + { + return CPLRecodeStub( pszSource, pszSrcEncoding, pszDstEncoding ); + } + else + { + return CPLRecodeIconv( pszSource, pszSrcEncoding, pszDstEncoding ); + } +#else /* CPL_RECODE_STUB */ + return CPLRecodeStub( pszSource, pszSrcEncoding, pszDstEncoding ); +#endif /* CPL_RECODE_ICONV */ +} + +/************************************************************************/ +/* CPLRecodeFromWChar() */ +/************************************************************************/ + +/** + * Convert wchar_t string to UTF-8. + * + * Convert a wchar_t string into a multibyte utf-8 string. The only + * guaranteed supported source encoding is CPL_ENC_UCS2, and the only + * guaranteed supported destination encodings are CPL_ENC_UTF8, CPL_ENC_ASCII + * and CPL_ENC_ISO8859_1. In some cases (ie. using iconv()) other encodings + * may also be supported. + * + * Note that the wchar_t type varies in size on different systems. On + * win32 it is normally 2 bytes, and on unix 4 bytes. + * + * If an error occurs an error may, or may not be posted with CPLError(). + * + * @param pwszSource the source wchar_t string, terminated with a 0 wchar_t. + * @param pszSrcEncoding the source encoding, typically CPL_ENC_UCS2. + * @param pszDstEncoding the destination encoding, typically CPL_ENC_UTF8. + * + * @return a zero terminated multi-byte string which should be freed with + * CPLFree(), or NULL if an error occurs. + * + * @since GDAL 1.6.0 + */ + +char CPL_DLL *CPLRecodeFromWChar( const wchar_t *pwszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ +#ifdef CPL_RECODE_ICONV +/* -------------------------------------------------------------------- */ +/* Conversions from CPL_ENC_UCS2 */ +/* to CPL_ENC_UTF8, CPL_ENC_ISO8859_1 and CPL_ENC_ASCII are well */ +/* handled by the stub implementation. */ +/* -------------------------------------------------------------------- */ + if ( (EQUAL(pszSrcEncoding, CPL_ENC_UCS2) || EQUAL(pszSrcEncoding, "WCHAR_T")) + && ( EQUAL(pszDstEncoding, CPL_ENC_UTF8) + || EQUAL(pszDstEncoding, CPL_ENC_ASCII) + || EQUAL(pszDstEncoding, CPL_ENC_ISO8859_1) ) ) + { + return CPLRecodeFromWCharStub( pwszSource, + pszSrcEncoding, pszDstEncoding ); + } + else + { + return CPLRecodeFromWCharIconv( pwszSource, + pszSrcEncoding, pszDstEncoding ); + } +#else /* CPL_RECODE_STUB */ + return CPLRecodeFromWCharStub( pwszSource, + pszSrcEncoding, pszDstEncoding ); +#endif /* CPL_RECODE_ICONV */ +} + +/************************************************************************/ +/* CPLRecodeToWChar() */ +/************************************************************************/ + +/** + * Convert UTF-8 string to a wchar_t string. + * + * Convert a 8bit, multi-byte per character input string into a wide + * character (wchar_t) string. The only guaranteed supported source encodings + * are CPL_ENC_UTF8, CPL_ENC_ASCII and CPL_ENC_ISO8869_1 (LATIN1). The only + * guaranteed supported destination encoding is CPL_ENC_UCS2. Other source + * and destination encodings may be supported depending on the underlying + * implementation. + * + * Note that the wchar_t type varies in size on different systems. On + * win32 it is normally 2 bytes, and on unix 4 bytes. + * + * If an error occurs an error may, or may not be posted with CPLError(). + * + * @param pszSource input multi-byte character string. + * @param pszSrcEncoding source encoding, typically CPL_ENC_UTF8. + * @param pszDstEncoding destination encoding, typically CPL_ENC_UCS2. + * + * @return the zero terminated wchar_t string (to be freed with CPLFree()) or + * NULL on error. + * + * @since GDAL 1.6.0 + */ + +wchar_t CPL_DLL *CPLRecodeToWChar( const char *pszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ +#ifdef CPL_RECODE_ICONV +/* -------------------------------------------------------------------- */ +/* Conversions to CPL_ENC_UCS2 */ +/* from CPL_ENC_UTF8, CPL_ENC_ISO8859_1 and CPL_ENC_ASCII are well */ +/* handled by the stub implementation. */ +/* -------------------------------------------------------------------- */ + if ( (EQUAL(pszDstEncoding, CPL_ENC_UCS2) || EQUAL(pszDstEncoding, "WCHAR_T")) + && ( EQUAL(pszSrcEncoding, CPL_ENC_UTF8) + || EQUAL(pszSrcEncoding, CPL_ENC_ASCII) + || EQUAL(pszSrcEncoding, CPL_ENC_ISO8859_1) ) ) + { + return CPLRecodeToWCharStub( pszSource, + pszSrcEncoding, pszDstEncoding ); + } + else + { + return CPLRecodeToWCharIconv( pszSource, + pszSrcEncoding, pszDstEncoding ); + } +#else /* CPL_RECODE_STUB */ + return CPLRecodeToWCharStub( pszSource, pszSrcEncoding, pszDstEncoding ); +#endif /* CPL_RECODE_ICONV */ +} + +/************************************************************************/ +/* CPLIsUTF8() */ +/************************************************************************/ + +/** + * Test if a string is encoded as UTF-8. + * + * @param pabyData input string to test + * @param nLen length of the input string, or -1 if the function must compute + * the string length. In which case it must be null terminated. + * @return TRUE if the string is encoded as UTF-8. FALSE otherwise + * + * @since GDAL 1.7.0 + */ +int CPLIsUTF8(const char* pabyData, int nLen) +{ + return CPLIsUTF8Stub( pabyData, nLen ); +} + +/************************************************************************/ +/* CPLForceToASCII() */ +/************************************************************************/ + +/** + * Return a new string that is made only of ASCII characters. If non-ASCII + * characters are found in the input string, they will be replaced by the + * provided replacement character. + * + * @param pabyData input string to test + * @param nLen length of the input string, or -1 if the function must compute + * the string length. In which case it must be null terminated. + * @param chReplacementChar character which will be used when the input stream + * contains a non ASCII character. Must be valid ASCII ! + * + * @return a new string that must be freed with CPLFree(). + * + * @since GDAL 1.7.0 + */ +char CPL_DLL *CPLForceToASCII(const char* pabyData, int nLen, char chReplacementChar) +{ + if (nLen < 0) + nLen = strlen(pabyData); + char* pszOutputString = (char*)CPLMalloc(nLen + 1); + int i; + for(i=0;i 127) + pszOutputString[i] = chReplacementChar; + else + pszOutputString[i] = pabyData[i]; + } + pszOutputString[i] = '\0'; + return pszOutputString; +} + +/************************************************************************/ +/* CPLEncodingCharSize() */ +/************************************************************************/ + +/** + * Return bytes per character for encoding. + * + * This function returns the size in bytes of the smallest character + * in this encoding. For fixed width encodings (ASCII, UCS-2, UCS-4) this + * is straight forward. For encodings like UTF8 and UTF16 which represent + * some characters as a sequence of atomic character sizes the function + * still returns the atomic character size (1 for UTF8, 2 for UTF16). + * + * This function will return the correct value for well known encodings + * with corresponding CPL_ENC_ values. It may not return the correct value + * for other encodings even if they are supported by the underlying iconv + * or windows transliteration services. Hopefully it will improve over time. + * + * @param pszEncoding the name of the encoding. + * + * @return the size of a minimal character in bytes or -1 if the size is + * unknown. + */ + +int CPLEncodingCharSize( const char *pszEncoding ) + +{ + if( EQUAL(pszEncoding,CPL_ENC_UTF8) ) + return 1; + else if( EQUAL(pszEncoding,CPL_ENC_UTF16) ) + return 2; + else if( EQUAL(pszEncoding,CPL_ENC_UCS2) ) + return 2; + else if( EQUAL(pszEncoding,CPL_ENC_UCS4) ) + return 4; + else if( EQUAL(pszEncoding,CPL_ENC_ASCII) ) + return 1; + else if( EQUALN(pszEncoding,"ISO-8859-",9) ) + return 1; + else + return -1; +} + +/************************************************************************/ +/* CPLClearRecodeWarningFlags() */ +/************************************************************************/ + +void CPLClearRecodeWarningFlags() +{ +#ifdef CPL_RECODE_ICONV + CPLClearRecodeIconvWarningFlags(); +#endif + CPLClearRecodeStubWarningFlags(); +} + + +/************************************************************************/ +/* CPLStrlenUTF8() */ +/************************************************************************/ + +/** + * Return the number of UTF-8 characters of a nul-terminated string. + * + * This is different from strlen() which returns the number of bytes. + * + * @param pszUTF8Str a nul-terminated UTF-8 string + * + * @return the number of UTF-8 characters. + */ + +int CPLStrlenUTF8(const char *pszUTF8Str) { + int i = 0, j = 0; + while (pszUTF8Str[i]) { + if ((pszUTF8Str[i] & 0xc0) != 0x80) j++; + i++; + } + return j; +} + diff --git a/bazaar/plugin/gdal/port/cpl_recode_iconv.cpp b/bazaar/plugin/gdal/port/cpl_recode_iconv.cpp new file mode 100644 index 000000000..9ed6b07a9 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_recode_iconv.cpp @@ -0,0 +1,335 @@ +/********************************************************************** + * $Id: cpl_recode_iconv.cpp 27044 2014-03-16 23:41:27Z rouault $ + * + * Name: cpl_recode_iconv.cpp + * Project: CPL - Common Portability Library + * Purpose: Character set recoding and char/wchar_t conversions implemented + * using the iconv() functionality. + * Author: Andrey Kiselev, dron@ak4719.spb.edu + * + ********************************************************************** + * Copyright (c) 2011, Andrey Kiselev + * Copyright (c) 2011-2012, Even Rouault + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + **********************************************************************/ + +#include "cpl_port.h" + +CPL_CVSID("$Id: cpl_recode_iconv.cpp 27044 2014-03-16 23:41:27Z rouault $"); + +#ifdef CPL_RECODE_ICONV + +#include +#include "cpl_string.h" + +#ifndef ICONV_CPP_CONST +#define ICONV_CPP_CONST ICONV_CONST +#endif + +#define CPL_RECODE_DSTBUF_SIZE 32768 + +/************************************************************************/ +/* CPLClearRecodeIconvWarningFlags() */ +/************************************************************************/ + +static int bHaveWarned1 = FALSE; +static int bHaveWarned2 = FALSE; + +void CPLClearRecodeIconvWarningFlags() +{ + bHaveWarned1 = FALSE; + bHaveWarned2 = FALSE; +} + +/************************************************************************/ +/* CPLRecodeIconv() */ +/************************************************************************/ + +/** + * Convert a string from a source encoding to a destination encoding + * using the iconv() function. + * + * If an error occurs an error may, or may not be posted with CPLError(). + * + * @param pszSource a NULL terminated string. + * @param pszSrcEncoding the source encoding. + * @param pszDstEncoding the destination encoding. + * + * @return a NULL terminated string which should be freed with CPLFree(). + */ + +char *CPLRecodeIconv( const char *pszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ + iconv_t sConv; + + sConv = iconv_open( pszDstEncoding, pszSrcEncoding ); + + if ( sConv == (iconv_t)-1 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Recode from %s to %s failed with the error: \"%s\".", + pszSrcEncoding, pszDstEncoding, strerror(errno) ); + + return CPLStrdup(pszSource); + } + +/* -------------------------------------------------------------------- */ +/* XXX: There is a portability issue: iconv() function could be */ +/* declared differently on different platforms. The second */ +/* argument could be declared as char** (as POSIX defines) or */ +/* as a const char**. Handle it with the ICONV_CPP_CONST macro here. */ +/* -------------------------------------------------------------------- */ + ICONV_CPP_CONST char *pszSrcBuf = (ICONV_CPP_CONST char *)pszSource; + size_t nSrcLen = strlen( pszSource ); + size_t nDstCurLen = MAX(CPL_RECODE_DSTBUF_SIZE, nSrcLen + 1); + size_t nDstLen = nDstCurLen; + char *pszDestination = (char *)CPLCalloc( nDstCurLen, sizeof(char) ); + char *pszDstBuf = pszDestination; + + while ( nSrcLen > 0 ) + { + size_t nConverted = + iconv( sConv, &pszSrcBuf, &nSrcLen, &pszDstBuf, &nDstLen ); + + if ( nConverted == (size_t)-1 ) + { + if ( errno == EILSEQ ) + { + // Skip the invalid sequence in the input string. + if (!bHaveWarned1) + { + bHaveWarned1 = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "One or several characters couldn't be converted correctly from %s to %s.\n" + "This warning will not be emitted anymore", + pszSrcEncoding, pszDstEncoding); + } + nSrcLen--, pszSrcBuf++; + continue; + } + + else if ( errno == E2BIG ) + { + // We are running out of the output buffer. + // Dynamically increase the buffer size. + size_t nTmp = nDstCurLen; + nDstCurLen *= 2; + pszDestination = + (char *)CPLRealloc( pszDestination, nDstCurLen ); + pszDstBuf = pszDestination + nTmp - nDstLen; + nDstLen += nDstCurLen - nTmp; + continue; + } + + else + break; + } + } + + pszDestination[nDstCurLen - nDstLen] = '\0'; + + iconv_close( sConv ); + + return pszDestination; +} + +/************************************************************************/ +/* CPLRecodeFromWCharIconv() */ +/************************************************************************/ + +/** + * Convert wchar_t string to UTF-8. + * + * Convert a wchar_t string into a multibyte utf-8 string + * using the iconv() function. + * + * Note that the wchar_t type varies in size on different systems. On + * win32 it is normally 2 bytes, and on unix 4 bytes. + * + * If an error occurs an error may, or may not be posted with CPLError(). + * + * @param pwszSource the source wchar_t string, terminated with a 0 wchar_t. + * @param pszSrcEncoding the source encoding, typically CPL_ENC_UCS2. + * @param pszDstEncoding the destination encoding, typically CPL_ENC_UTF8. + * + * @return a zero terminated multi-byte string which should be freed with + * CPLFree(), or NULL if an error occurs. + */ + +char *CPLRecodeFromWCharIconv( const wchar_t *pwszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ +/* -------------------------------------------------------------------- */ +/* What is the source length. */ +/* -------------------------------------------------------------------- */ + size_t nSrcLen = 0; + + while ( pwszSource[nSrcLen] != 0 ) + nSrcLen++; + +/* -------------------------------------------------------------------- */ +/* iconv() does not support wchar_t so we need to repack the */ +/* characters according to the width of a character in the */ +/* source encoding. For instance if wchar_t is 4 bytes but our */ +/* source is UTF16 then we need to pack down into 2 byte */ +/* characters before passing to iconv(). */ +/* -------------------------------------------------------------------- */ + int nTargetCharWidth = CPLEncodingCharSize( pszSrcEncoding ); + + if( nTargetCharWidth < 1 ) + { + CPLError( CE_Warning, CPLE_AppDefined, + "Recode from %s with CPLRecodeFromWChar() failed because" + " the width of characters in the encoding are not known.", + pszSrcEncoding ); + return CPLStrdup(""); + } + + GByte *pszIconvSrcBuf = (GByte*) CPLCalloc((nSrcLen+1),nTargetCharWidth); + unsigned int iSrc; + + for( iSrc = 0; iSrc <= nSrcLen; iSrc++ ) + { + if( nTargetCharWidth == 1 ) + pszIconvSrcBuf[iSrc] = (GByte) pwszSource[iSrc]; + else if( nTargetCharWidth == 2 ) + ((short *)pszIconvSrcBuf)[iSrc] = (short) pwszSource[iSrc]; + else if( nTargetCharWidth == 4 ) + ((GInt32 *)pszIconvSrcBuf)[iSrc] = pwszSource[iSrc]; + } + +/* -------------------------------------------------------------------- */ +/* Create the iconv() translation object. */ +/* -------------------------------------------------------------------- */ + iconv_t sConv; + + sConv = iconv_open( pszDstEncoding, pszSrcEncoding ); + + if ( sConv == (iconv_t)-1 ) + { + CPLFree( pszIconvSrcBuf ); + CPLError( CE_Warning, CPLE_AppDefined, + "Recode from %s to %s failed with the error: \"%s\".", + pszSrcEncoding, pszDstEncoding, strerror(errno) ); + + return CPLStrdup( "" ); + } + +/* -------------------------------------------------------------------- */ +/* XXX: There is a portability issue: iconv() function could be */ +/* declared differently on different platforms. The second */ +/* argument could be declared as char** (as POSIX defines) or */ +/* as a const char**. Handle it with the ICONV_CPP_CONST macro here. */ +/* -------------------------------------------------------------------- */ + ICONV_CPP_CONST char *pszSrcBuf = (ICONV_CPP_CONST char *) pszIconvSrcBuf; + + /* iconv expects a number of bytes, not characters */ + nSrcLen *= sizeof(wchar_t); + +/* -------------------------------------------------------------------- */ +/* Allocate destination buffer. */ +/* -------------------------------------------------------------------- */ + size_t nDstCurLen = MAX(CPL_RECODE_DSTBUF_SIZE, nSrcLen + 1); + size_t nDstLen = nDstCurLen; + char *pszDestination = (char *)CPLCalloc( nDstCurLen, sizeof(char) ); + char *pszDstBuf = pszDestination; + + while ( nSrcLen > 0 ) + { + size_t nConverted = + iconv( sConv, &pszSrcBuf, &nSrcLen, &pszDstBuf, &nDstLen ); + + if ( nConverted == (size_t)-1 ) + { + if ( errno == EILSEQ ) + { + // Skip the invalid sequence in the input string. + nSrcLen--; + pszSrcBuf += sizeof(wchar_t); + if (!bHaveWarned2) + { + bHaveWarned2 = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "One or several characters couldn't be converted correctly from %s to %s.\n" + "This warning will not be emitted anymore", + pszSrcEncoding, pszDstEncoding); + } + continue; + } + + else if ( errno == E2BIG ) + { + // We are running out of the output buffer. + // Dynamically increase the buffer size. + size_t nTmp = nDstCurLen; + nDstCurLen *= 2; + pszDestination = + (char *)CPLRealloc( pszDestination, nDstCurLen ); + pszDstBuf = pszDestination + nTmp - nDstLen; + nDstLen += nDstCurLen - nTmp; + continue; + } + + else + break; + } + } + + pszDestination[nDstCurLen - nDstLen] = '\0'; + + iconv_close( sConv ); + + CPLFree( pszIconvSrcBuf ); + + return pszDestination; +} + +/************************************************************************/ +/* CPLRecodeToWCharIconv() */ +/************************************************************************/ + +/** + * Convert UTF-8 string to a wchar_t string. + * + * Convert a 8bit, multi-byte per character input string into a wide + * character (wchar_t) string using the iconv() function. + * + * Note that the wchar_t type varies in size on different systems. On + * win32 it is normally 2 bytes, and on unix 4 bytes. + * + * If an error occurs an error may, or may not be posted with CPLError(). + * + * @param pszSource input multi-byte character string. + * @param pszSrcEncoding source encoding, typically CPL_ENC_UTF8. + * @param pszDstEncoding destination encoding, typically CPL_ENC_UCS2. + * + * @return the zero terminated wchar_t string (to be freed with CPLFree()) or + * NULL on error. + */ + +wchar_t *CPLRecodeToWCharIconv( const char *pszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ + return (wchar_t *)CPLRecodeIconv( pszSource, + pszSrcEncoding, pszDstEncoding); +} + +#endif /* CPL_RECODE_ICONV */ diff --git a/bazaar/plugin/gdal/port/cpl_recode_stub.cpp b/bazaar/plugin/gdal/port/cpl_recode_stub.cpp new file mode 100644 index 000000000..34840a56a --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_recode_stub.cpp @@ -0,0 +1,1388 @@ +/********************************************************************** + * $Id: cpl_recode_stub.cpp 29121 2015-05-02 22:53:48Z rouault $ + * + * Name: cpl_recode_stub.cpp + * Project: CPL - Common Portability Library + * Purpose: Character set recoding and char/wchar_t conversions, stub + * implementation to be used if iconv() functionality is not + * available. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + * The bulk of this code is derived from the utf.c module from FLTK. It + * was originally downloaded from: + * http://svn.easysw.com/public/fltk/fltk/trunk/src/utf.c + * + ********************************************************************** + * Copyright (c) 2008, Frank Warmerdam + * Copyright 2006 by Bill Spitzak and others. + * Copyright (c) 2009-2014, Even Rouault + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + **********************************************************************/ + +#include "cpl_string.h" + +CPL_CVSID("$Id: cpl_recode_stub.cpp 29121 2015-05-02 22:53:48Z rouault $"); + +#ifdef CPL_RECODE_STUB + +static unsigned utf8decode(const char* p, const char* end, int* len); +static unsigned utf8towc(const char* src, unsigned srclen, + wchar_t* dst, unsigned dstlen); +static unsigned utf8toa(const char* src, unsigned srclen, + char* dst, unsigned dstlen); +static unsigned utf8fromwc(char* dst, unsigned dstlen, + const wchar_t* src, unsigned srclen); +static unsigned utf8froma(char* dst, unsigned dstlen, + const char* src, unsigned srclen); +static int utf8test(const char* src, unsigned srclen); + +#ifdef _WIN32 + +#include +#include + +static char* CPLWin32Recode( const char* src, + unsigned src_code_page, unsigned dst_code_page ); +#endif + +#ifdef FUTURE_NEEDS +static const char* utf8fwd(const char* p, const char* start, const char* end); +static const char* utf8back(const char* p, const char* start, const char*end); +static int utf8encode(unsigned ucs, char* buf); +static int utf8bytes(unsigned ucs); +#endif /* def FUTURE_NEEDS */ + +/************************************************************************/ +/* ==================================================================== */ +/* Stub Implementation not depending on iconv() or WIN32 API. */ +/* ==================================================================== */ +/************************************************************************/ + +static int bHaveWarned1 = FALSE; +static int bHaveWarned2 = FALSE; +static int bHaveWarned3 = FALSE; +static int bHaveWarned4 = FALSE; +static int bHaveWarned5 = FALSE; +static int bHaveWarned6 = FALSE; + +/************************************************************************/ +/* CPLClearRecodeStubWarningFlags() */ +/************************************************************************/ + +void CPLClearRecodeStubWarningFlags() +{ + bHaveWarned1 = FALSE; + bHaveWarned2 = FALSE; + bHaveWarned3 = FALSE; + bHaveWarned4 = FALSE; + bHaveWarned5 = FALSE; + bHaveWarned6 = FALSE; +} + +/************************************************************************/ +/* CPLRecodeStub() */ +/************************************************************************/ + +/** + * Convert a string from a source encoding to a destination encoding. + * + * The only guaranteed supported encodings are CPL_ENC_UTF8, CPL_ENC_ASCII + * and CPL_ENC_ISO8859_1. Currently, the following conversions are supported : + *
      + *
    • CPL_ENC_ASCII -> CPL_ENC_UTF8 or CPL_ENC_ISO8859_1 (no conversion in fact)
    • + *
    • CPL_ENC_ISO8859_1 -> CPL_ENC_UTF8
    • + *
    • CPL_ENC_UTF8 -> CPL_ENC_ISO8859_1
    • + *
    + * + * If an error occurs an error may, or may not be posted with CPLError(). + * + * @param pszSource a NULL terminated string. + * @param pszSrcEncoding the source encoding. + * @param pszDstEncoding the destination encoding. + * + * @return a NULL terminated string which should be freed with CPLFree(). + */ + +char *CPLRecodeStub( const char *pszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ +/* -------------------------------------------------------------------- */ +/* If the source or destination is current locale(), we change */ +/* it to ISO8859-1 since our stub implementation does not */ +/* attempt to address locales properly. */ +/* -------------------------------------------------------------------- */ + + if( pszSrcEncoding[0] == '\0' ) + pszSrcEncoding = CPL_ENC_ISO8859_1; + + if( pszDstEncoding[0] == '\0' ) + pszDstEncoding = CPL_ENC_ISO8859_1; + +/* -------------------------------------------------------------------- */ +/* ISO8859 to UTF8 */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszSrcEncoding,CPL_ENC_ISO8859_1) == 0 + && strcmp(pszDstEncoding,CPL_ENC_UTF8) == 0 ) + { + int nCharCount = strlen(pszSource); + char *pszResult = (char *) CPLCalloc(1,nCharCount*2+1); + + utf8froma( pszResult, nCharCount*2+1, pszSource, nCharCount ); + + return pszResult; + } + +/* -------------------------------------------------------------------- */ +/* UTF8 to ISO8859 */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszSrcEncoding,CPL_ENC_UTF8) == 0 + && strcmp(pszDstEncoding,CPL_ENC_ISO8859_1) == 0 ) + { + int nCharCount = strlen(pszSource); + char *pszResult = (char *) CPLCalloc(1,nCharCount+1); + + utf8toa( pszSource, nCharCount, pszResult, nCharCount+1 ); + + return pszResult; + } + +#ifdef _WIN32 +/* ---------------------------------------------------------------------*/ +/* CPXXX to UTF8 */ +/* ---------------------------------------------------------------------*/ + if( strncmp(pszSrcEncoding,"CP",2) == 0 + && strcmp(pszDstEncoding,CPL_ENC_UTF8) == 0 ) + { + int nCode = atoi( pszSrcEncoding + 2 ); + if( nCode > 0 ) { + return CPLWin32Recode( pszSource, nCode, CP_UTF8 ); + } + else if( EQUAL(pszSrcEncoding, "CP_OEMCP") ) + return CPLWin32Recode( pszSource, CP_OEMCP, CP_UTF8 ); + } + +/* ---------------------------------------------------------------------*/ +/* UTF8 to CPXXX */ +/* ---------------------------------------------------------------------*/ + if( strcmp(pszSrcEncoding,CPL_ENC_UTF8) == 0 + && strncmp(pszDstEncoding,"CP",2) == 0 ) + { + int nCode = atoi( pszDstEncoding + 2 ); + if( nCode > 0 ) { + return CPLWin32Recode( pszSource, CP_UTF8, nCode ); + } + } +#endif + +/* -------------------------------------------------------------------- */ +/* Anything else to UTF-8 is treated as ISO8859-1 to UTF-8 with */ +/* a one-time warning. */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszDstEncoding,CPL_ENC_UTF8) == 0 ) + { + int nCharCount = strlen(pszSource); + char *pszResult = (char *) CPLCalloc(1,nCharCount*2+1); + + if( !bHaveWarned1 ) + { + bHaveWarned1 = 1; + CPLError( CE_Warning, CPLE_AppDefined, + "Recode from %s to UTF-8 not supported, treated as ISO8859-1 to UTF-8.", + pszSrcEncoding ); + } + + utf8froma( pszResult, nCharCount*2+1, pszSource, nCharCount ); + + return pszResult; + } + +/* -------------------------------------------------------------------- */ +/* UTF-8 to anything else is treated as UTF-8 to ISO-8859-1 */ +/* with a warning. */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszSrcEncoding,CPL_ENC_UTF8) == 0 + && strcmp(pszDstEncoding,CPL_ENC_ISO8859_1) == 0 ) + { + int nCharCount = strlen(pszSource); + char *pszResult = (char *) CPLCalloc(1,nCharCount+1); + + if( !bHaveWarned2 ) + { + bHaveWarned2 = 1; + CPLError( CE_Warning, CPLE_AppDefined, + "Recode from UTF-8 to %s not supported, treated as UTF-8 to ISO8859-1.", + pszDstEncoding ); + } + + utf8toa( pszSource, nCharCount, pszResult, nCharCount+1 ); + + return pszResult; + } + +/* -------------------------------------------------------------------- */ +/* Everything else is treated as a no-op with a warning. */ +/* -------------------------------------------------------------------- */ + { + if( !bHaveWarned3 ) + { + bHaveWarned3 = 1; + CPLError( CE_Warning, CPLE_AppDefined, + "Recode from %s to %s not supported, no change applied.", + pszSrcEncoding, pszDstEncoding ); + } + + return CPLStrdup(pszSource); + } +} + +/************************************************************************/ +/* CPLRecodeFromWCharStub() */ +/************************************************************************/ + +/** + * Convert wchar_t string to UTF-8. + * + * Convert a wchar_t string into a multibyte utf-8 string. The only + * guaranteed supported source encoding is CPL_ENC_UCS2, and the only + * guaranteed supported destination encodings are CPL_ENC_UTF8, CPL_ENC_ASCII + * and CPL_ENC_ISO8859_1. In some cases (ie. using iconv()) other encodings + * may also be supported. + * + * Note that the wchar_t type varies in size on different systems. On + * win32 it is normally 2 bytes, and on unix 4 bytes. + * + * If an error occurs an error may, or may not be posted with CPLError(). + * + * @param pwszSource the source wchar_t string, terminated with a 0 wchar_t. + * @param pszSrcEncoding the source encoding, typically CPL_ENC_UCS2. + * @param pszDstEncoding the destination encoding, typically CPL_ENC_UTF8. + * + * @return a zero terminated multi-byte string which should be freed with + * CPLFree(), or NULL if an error occurs. + */ + +char *CPLRecodeFromWCharStub( const wchar_t *pwszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ +/* -------------------------------------------------------------------- */ +/* We try to avoid changes of character set. We are just */ +/* providing for unicode to unicode. */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszSrcEncoding,"WCHAR_T") != 0 && + strcmp(pszSrcEncoding,CPL_ENC_UTF8) != 0 + && strcmp(pszSrcEncoding,CPL_ENC_UTF16) != 0 + && strcmp(pszSrcEncoding,CPL_ENC_UCS2) != 0 + && strcmp(pszSrcEncoding,CPL_ENC_UCS4) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Stub recoding implementation does not support\n" + "CPLRecodeFromWCharStub(...,%s,%s)", + pszSrcEncoding, pszDstEncoding ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* What is the source length. */ +/* -------------------------------------------------------------------- */ + int nSrcLen = 0; + + while( pwszSource[nSrcLen] != 0 ) + nSrcLen++; + +/* -------------------------------------------------------------------- */ +/* Allocate destination buffer plenty big. */ +/* -------------------------------------------------------------------- */ + char *pszResult; + int nDstBufSize, nDstLen; + + nDstBufSize = nSrcLen * 4 + 1; + pszResult = (char *) CPLMalloc(nDstBufSize); // nearly worst case. + + if (nSrcLen == 0) + { + pszResult[0] = '\0'; + return pszResult; + } + +/* -------------------------------------------------------------------- */ +/* Convert, and confirm we had enough space. */ +/* -------------------------------------------------------------------- */ + nDstLen = utf8fromwc( pszResult, nDstBufSize, pwszSource, nSrcLen ); + if( nDstLen >= nDstBufSize ) + { + CPLAssert( FALSE ); // too small! + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* If something other than UTF-8 was requested, recode now. */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszDstEncoding,CPL_ENC_UTF8) == 0 ) + return pszResult; + + char *pszFinalResult = + CPLRecodeStub( pszResult, CPL_ENC_UTF8, pszDstEncoding ); + + CPLFree( pszResult ); + + return pszFinalResult; +} + +/************************************************************************/ +/* CPLRecodeToWCharStub() */ +/************************************************************************/ + +/** + * Convert UTF-8 string to a wchar_t string. + * + * Convert a 8bit, multi-byte per character input string into a wide + * character (wchar_t) string. The only guaranteed supported source encodings + * are CPL_ENC_UTF8, CPL_ENC_ASCII and CPL_ENC_ISO8869_1 (LATIN1). The only + * guaranteed supported destination encoding is CPL_ENC_UCS2. Other source + * and destination encodings may be supported depending on the underlying + * implementation. + * + * Note that the wchar_t type varies in size on different systems. On + * win32 it is normally 2 bytes, and on unix 4 bytes. + * + * If an error occurs an error may, or may not be posted with CPLError(). + * + * @param pszSource input multi-byte character string. + * @param pszSrcEncoding source encoding, typically CPL_ENC_UTF8. + * @param pszDstEncoding destination encoding, typically CPL_ENC_UCS2. + * + * @return the zero terminated wchar_t string (to be freed with CPLFree()) or + * NULL on error. + * + * @since GDAL 1.6.0 + */ + +wchar_t *CPLRecodeToWCharStub( const char *pszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ + char *pszUTF8Source = (char *) pszSource; + + if( strcmp(pszSrcEncoding,CPL_ENC_UTF8) != 0 + && strcmp(pszSrcEncoding,CPL_ENC_ASCII) != 0 ) + { + pszUTF8Source = CPLRecodeStub( pszSource, pszSrcEncoding, CPL_ENC_UTF8 ); + if( pszUTF8Source == NULL ) + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* We try to avoid changes of character set. We are just */ +/* providing for unicode to unicode. */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszDstEncoding,"WCHAR_T") != 0 + && strcmp(pszDstEncoding,CPL_ENC_UCS2) != 0 + && strcmp(pszDstEncoding,CPL_ENC_UCS4) != 0 + && strcmp(pszDstEncoding,CPL_ENC_UTF16) != 0 ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Stub recoding implementation does not support\n" + "CPLRecodeToWCharStub(...,%s,%s)", + pszSrcEncoding, pszDstEncoding ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Do the UTF-8 to UCS-2 recoding. */ +/* -------------------------------------------------------------------- */ + int nSrcLen = strlen(pszUTF8Source); + wchar_t *pwszResult = (wchar_t *) CPLCalloc(sizeof(wchar_t),nSrcLen+1); + + utf8towc( pszUTF8Source, nSrcLen, pwszResult, nSrcLen+1 ); + + if( pszUTF8Source != pszSource ) + CPLFree( pszUTF8Source ); + + return pwszResult; +} + + +/************************************************************************/ +/* CPLIsUTF8() */ +/************************************************************************/ + +/** + * Test if a string is encoded as UTF-8. + * + * @param pabyData input string to test + * @param nLen length of the input string, or -1 if the function must compute + * the string length. In which case it must be null terminated. + * @return TRUE if the string is encoded as UTF-8. FALSE otherwise + * + * @since GDAL 1.7.0 + */ +int CPLIsUTF8Stub(const char* pabyData, int nLen) +{ + if (nLen < 0) + nLen = strlen(pabyData); + return utf8test(pabyData, (unsigned)nLen) != 0; +} + +/************************************************************************/ +/* ==================================================================== */ +/* UTF.C code from FLTK with some modifications. */ +/* ==================================================================== */ +/************************************************************************/ + +/* Set to 1 to turn bad UTF8 bytes into ISO-8859-1. If this is to zero + they are instead turned into the Unicode REPLACEMENT CHARACTER, of + value 0xfffd. + If this is on utf8decode will correctly map most (perhaps all) + human-readable text that is in ISO-8859-1. This may allow you + to completely ignore character sets in your code because virtually + everything is either ISO-8859-1 or UTF-8. +*/ +#define ERRORS_TO_ISO8859_1 1 + +/* Set to 1 to turn bad UTF8 bytes in the 0x80-0x9f range into the + Unicode index for Microsoft's CP1252 character set. You should + also set ERRORS_TO_ISO8859_1. With this a huge amount of more + available text (such as all web pages) are correctly converted + to Unicode. +*/ +#define ERRORS_TO_CP1252 1 + +/* A number of Unicode code points are in fact illegal and should not + be produced by a UTF-8 converter. Turn this on will replace the + bytes in those encodings with errors. If you do this then converting + arbitrary 16-bit data to UTF-8 and then back is not an identity, + which will probably break a lot of software. +*/ +#define STRICT_RFC3629 0 + +#if ERRORS_TO_CP1252 +// Codes 0x80..0x9f from the Microsoft CP1252 character set, translated +// to Unicode: +static unsigned short cp1252[32] = { + 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, + 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f, + 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, + 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178 +}; +#endif + +/************************************************************************/ +/* utf8decode() */ +/************************************************************************/ + +/* + Decode a single UTF-8 encoded character starting at \e p. The + resulting Unicode value (in the range 0-0x10ffff) is returned, + and \e len is set the the number of bytes in the UTF-8 encoding + (adding \e len to \e p will point at the next character). + + If \a p points at an illegal UTF-8 encoding, including one that + would go past \e end, or where a code is uses more bytes than + necessary, then *(unsigned char*)p is translated as though it is + in the Microsoft CP1252 character set and \e len is set to 1. + Treating errors this way allows this to decode almost any + ISO-8859-1 or CP1252 text that has been mistakenly placed where + UTF-8 is expected, and has proven very useful. + + If you want errors to be converted to error characters (as the + standards recommend), adding a test to see if the length is + unexpectedly 1 will work: + +\code + if (*p & 0x80) { // what should be a multibyte encoding + code = utf8decode(p,end,&len); + if (len<2) code = 0xFFFD; // Turn errors into REPLACEMENT CHARACTER + } else { // handle the 1-byte utf8 encoding: + code = *p; + len = 1; + } +\endcode + + Direct testing for the 1-byte case (as shown above) will also + speed up the scanning of strings where the majority of characters + are ASCII. +*/ +static unsigned utf8decode(const char* p, const char* end, int* len) +{ + unsigned char c = *(unsigned char*)p; + if (c < 0x80) { + *len = 1; + return c; +#if ERRORS_TO_CP1252 + } else if (c < 0xa0) { + *len = 1; + return cp1252[c-0x80]; +#endif + } else if (c < 0xc2) { + goto FAIL; + } + if (p+1 >= end || (p[1]&0xc0) != 0x80) goto FAIL; + if (c < 0xe0) { + *len = 2; + return + ((p[0] & 0x1f) << 6) + + ((p[1] & 0x3f)); + } else if (c == 0xe0) { + if (((unsigned char*)p)[1] < 0xa0) goto FAIL; + goto UTF8_3; +#if STRICT_RFC3629 + } else if (c == 0xed) { + // RFC 3629 says surrogate chars are illegal. + if (((unsigned char*)p)[1] >= 0xa0) goto FAIL; + goto UTF8_3; + } else if (c == 0xef) { + // 0xfffe and 0xffff are also illegal characters + if (((unsigned char*)p)[1]==0xbf && + ((unsigned char*)p)[2]>=0xbe) goto FAIL; + goto UTF8_3; +#endif + } else if (c < 0xf0) { + UTF8_3: + if (p+2 >= end || (p[2]&0xc0) != 0x80) goto FAIL; + *len = 3; + return + ((p[0] & 0x0f) << 12) + + ((p[1] & 0x3f) << 6) + + ((p[2] & 0x3f)); + } else if (c == 0xf0) { + if (((unsigned char*)p)[1] < 0x90) goto FAIL; + goto UTF8_4; + } else if (c < 0xf4) { + UTF8_4: + if (p+3 >= end || (p[2]&0xc0) != 0x80 || (p[3]&0xc0) != 0x80) goto FAIL; + *len = 4; +#if STRICT_RFC3629 + // RFC 3629 says all codes ending in fffe or ffff are illegal: + if ((p[1]&0xf)==0xf && + ((unsigned char*)p)[2] == 0xbf && + ((unsigned char*)p)[3] >= 0xbe) goto FAIL; +#endif + return + ((p[0] & 0x07) << 18) + + ((p[1] & 0x3f) << 12) + + ((p[2] & 0x3f) << 6) + + ((p[3] & 0x3f)); + } else if (c == 0xf4) { + if (((unsigned char*)p)[1] > 0x8f) goto FAIL; // after 0x10ffff + goto UTF8_4; + } else { + FAIL: + *len = 1; +#if ERRORS_TO_ISO8859_1 + return c; +#else + return 0xfffd; // Unicode REPLACEMENT CHARACTER +#endif + } +} + +/************************************************************************/ +/* utf8fwd() */ +/************************************************************************/ + +/* + Move \a p forward until it points to the start of a UTF-8 + character. If it already points at the start of one then it + is returned unchanged. Any UTF-8 errors are treated as though each + byte of the error is an individual character. + + \e start is the start of the string and is used to limit the + backwards search for the start of a utf8 character. + + \e end is the end of the string and is assummed to be a break + between characters. It is assummed to be greater than p. + + This function is for moving a pointer that was jumped to the + middle of a string, such as when doing a binary search for + a position. You should use either this or utf8back() depending + on which direction your algorithim can handle the pointer + moving. Do not use this to scan strings, use utf8decode() + instead. +*/ + +#ifdef FUTURE_NEEDS +static const char* utf8fwd(const char* p, const char* start, const char* end) +{ + const char* a; + int len; + // if we are not pointing at a continuation character, we are done: + if ((*p&0xc0) != 0x80) return p; + // search backwards for a 0xc0 starting the character: + for (a = p-1; ; --a) { + if (a < start) return p; + if (!(a[0]&0x80)) return p; + if ((a[0]&0x40)) break; + } + utf8decode(a,end,&len); + a += len; + if (a > p) return a; + return p; +} +#endif /* def FUTURE_NEEDS */ + +/************************************************************************/ +/* utf8back() */ +/************************************************************************/ + +/* + Move \a p backward until it points to the start of a UTF-8 + character. If it already points at the start of one then it + is returned unchanged. Any UTF-8 errors are treated as though each + byte of the error is an individual character. + + \e start is the start of the string and is used to limit the + backwards search for the start of a UTF-8 character. + + \e end is the end of the string and is assummed to be a break + between characters. It is assummed to be greater than p. + + If you wish to decrement a UTF-8 pointer, pass p-1 to this. +*/ + +#ifdef FUTURE_NEEDS +static const char* utf8back(const char* p, const char* start, const char* end) +{ + const char* a; + int len; + // if we are not pointing at a continuation character, we are done: + if ((*p&0xc0) != 0x80) return p; + // search backwards for a 0xc0 starting the character: + for (a = p-1; ; --a) { + if (a < start) return p; + if (!(a[0]&0x80)) return p; + if ((a[0]&0x40)) break; + } + utf8decode(a,end,&len); + if (a+len > p) return a; + return p; +} +#endif /* def FUTURE_NEEDS */ + +/************************************************************************/ +/* utf8bytes() */ +/************************************************************************/ + +/* Returns number of bytes that utf8encode() will use to encode the + character \a ucs. */ +#ifdef FUTURE_NEEDS +static int utf8bytes(unsigned ucs) { + if (ucs < 0x000080U) { + return 1; + } else if (ucs < 0x000800U) { + return 2; + } else if (ucs < 0x010000U) { + return 3; + } else if (ucs < 0x10ffffU) { + return 4; + } else { + return 3; // length of the illegal character encoding + } +} +#endif /* def FUTURE_NEEDS */ + +/************************************************************************/ +/* utf8encode() */ +/************************************************************************/ + +/* Write the UTF-8 encoding of \e ucs into \e buf and return the + number of bytes written. Up to 4 bytes may be written. If you know + that \a ucs is less than 0x10000 then at most 3 bytes will be written. + If you wish to speed this up, remember that anything less than 0x80 + is written as a single byte. + + If ucs is greater than 0x10ffff this is an illegal character + according to RFC 3629. These are converted as though they are + 0xFFFD (REPLACEMENT CHARACTER). + + RFC 3629 also says many other values for \a ucs are illegal (in + the range 0xd800 to 0xdfff, or ending with 0xfffe or + 0xffff). However I encode these as though they are legal, so that + utf8encode/utf8decode will be the identity for all codes between 0 + and 0x10ffff. +*/ +#ifdef FUTURE_NEEDS +static int utf8encode(unsigned ucs, char* buf) { + if (ucs < 0x000080U) { + buf[0] = ucs; + return 1; + } else if (ucs < 0x000800U) { + buf[0] = 0xc0 | (ucs >> 6); + buf[1] = 0x80 | (ucs & 0x3F); + return 2; + } else if (ucs < 0x010000U) { + buf[0] = 0xe0 | (ucs >> 12); + buf[1] = 0x80 | ((ucs >> 6) & 0x3F); + buf[2] = 0x80 | (ucs & 0x3F); + return 3; + } else if (ucs < 0x0010ffffU) { + buf[0] = 0xf0 | (ucs >> 18); + buf[1] = 0x80 | ((ucs >> 12) & 0x3F); + buf[2] = 0x80 | ((ucs >> 6) & 0x3F); + buf[3] = 0x80 | (ucs & 0x3F); + return 4; + } else { + // encode 0xfffd: + buf[0] = 0xefU; + buf[1] = 0xbfU; + buf[2] = 0xbdU; + return 3; + } +} +#endif /* def FUTURE_NEEDS */ + +/************************************************************************/ +/* utf8towc() */ +/************************************************************************/ + +/* Convert a UTF-8 sequence into an array of wchar_t. These + are used by some system calls, especially on Windows. + + \a src points at the UTF-8, and \a srclen is the number of bytes to + convert. + + \a dst points at an array to write, and \a dstlen is the number of + locations in this array. At most \a dstlen-1 words will be + written there, plus a 0 terminating word. Thus this function + will never overwrite the buffer and will always return a + zero-terminated string. If \a dstlen is zero then \a dst can be + null and no data is written, but the length is returned. + + The return value is the number of words that \e would be written + to \a dst if it were long enough, not counting the terminating + zero. If the return value is greater or equal to \a dstlen it + indicates truncation, you can then allocate a new array of size + return+1 and call this again. + + Errors in the UTF-8 are converted as though each byte in the + erroneous string is in the Microsoft CP1252 encoding. This allows + ISO-8859-1 text mistakenly identified as UTF-8 to be printed + correctly. + + Notice that sizeof(wchar_t) is 2 on Windows and is 4 on Linux + and most other systems. Where wchar_t is 16 bits, Unicode + characters in the range 0x10000 to 0x10ffff are converted to + "surrogate pairs" which take two words each (this is called UTF-16 + encoding). If wchar_t is 32 bits this rather nasty problem is + avoided. +*/ +static unsigned utf8towc(const char* src, unsigned srclen, + wchar_t* dst, unsigned dstlen) +{ + const char* p = src; + const char* e = src+srclen; + unsigned count = 0; + if (dstlen) for (;;) { + if (p >= e) {dst[count] = 0; return count;} + if (!(*p & 0x80)) { // ascii + dst[count] = *p++; + } else { + int len; unsigned ucs = utf8decode(p,e,&len); + p += len; +#ifdef _WIN32 + if (ucs < 0x10000) { + dst[count] = (wchar_t)ucs; + } else { + // make a surrogate pair: + if (count+2 >= dstlen) {dst[count] = 0; count += 2; break;} + dst[count] = (wchar_t)((((ucs-0x10000u)>>10)&0x3ff) | 0xd800); + dst[++count] = (wchar_t)((ucs&0x3ff) | 0xdc00); + } +#else + dst[count] = (wchar_t)ucs; +#endif + } + if (++count == dstlen) {dst[count-1] = 0; break;} + } + // we filled dst, measure the rest: + while (p < e) { + if (!(*p & 0x80)) p++; + else { +#ifdef _WIN32 + int len; unsigned ucs = utf8decode(p,e,&len); + p += len; + if (ucs >= 0x10000) ++count; +#else + int len; utf8decode(p,e,&len); + p += len; +#endif + } + ++count; + } + return count; +} + +/************************************************************************/ +/* utf8toa() */ +/************************************************************************/ +/* Convert a UTF-8 sequence into an array of 1-byte characters. + + If the UTF-8 decodes to a character greater than 0xff then it is + replaced with '?'. + + Errors in the UTF-8 are converted as individual bytes, same as + utf8decode() does. This allows ISO-8859-1 text mistakenly identified + as UTF-8 to be printed correctly (and possibly CP1512 on Windows). + + \a src points at the UTF-8, and \a srclen is the number of bytes to + convert. + + Up to \a dstlen bytes are written to \a dst, including a null + terminator. The return value is the number of bytes that would be + written, not counting the null terminator. If greater or equal to + \a dstlen then if you malloc a new array of size n+1 you will have + the space needed for the entire string. If \a dstlen is zero then + nothing is written and this call just measures the storage space + needed. +*/ +static unsigned utf8toa(const char* src, unsigned srclen, + char* dst, unsigned dstlen) +{ + const char* p = src; + const char* e = src+srclen; + unsigned count = 0; + if (dstlen) for (;;) { + unsigned char c; + if (p >= e) {dst[count] = 0; return count;} + c = *(unsigned char*)p; + if (c < 0xC2) { // ascii or bad code + dst[count] = c; + p++; + } else { + int len; unsigned ucs = utf8decode(p,e,&len); + p += len; + if (ucs < 0x100) dst[count] = (char)ucs; + else + { + if (!bHaveWarned4) + { + bHaveWarned4 = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "One or several characters couldn't be converted correctly from UTF-8 to ISO-8859-1.\n" + "This warning will not be emitted anymore."); + } + dst[count] = '?'; + } + } + if (++count >= dstlen) {dst[count-1] = 0; break;} + } + // we filled dst, measure the rest: + while (p < e) { + if (!(*p & 0x80)) p++; + else { + int len; + utf8decode(p,e,&len); + p += len; + } + ++count; + } + return count; +} + +/************************************************************************/ +/* utf8fromwc() */ +/************************************************************************/ +/* Turn "wide characters" as returned by some system calls + (especially on Windows) into UTF-8. + + Up to \a dstlen bytes are written to \a dst, including a null + terminator. The return value is the number of bytes that would be + written, not counting the null terminator. If greater or equal to + \a dstlen then if you malloc a new array of size n+1 you will have + the space needed for the entire string. If \a dstlen is zero then + nothing is written and this call just measures the storage space + needed. + + \a srclen is the number of words in \a src to convert. On Windows + this is not necessairly the number of characters, due to there + possibly being "surrogate pairs" in the UTF-16 encoding used. + On Unix wchar_t is 32 bits and each location is a character. + + On Unix if a src word is greater than 0x10ffff then this is an + illegal character according to RFC 3629. These are converted as + though they are 0xFFFD (REPLACEMENT CHARACTER). Characters in the + range 0xd800 to 0xdfff, or ending with 0xfffe or 0xffff are also + illegal according to RFC 3629. However I encode these as though + they are legal, so that utf8towc will return the original data. + + On Windows "surrogate pairs" are converted to a single character + and UTF-8 encoded (as 4 bytes). Mismatched halves of surrogate + pairs are converted as though they are individual characters. +*/ +static unsigned utf8fromwc(char* dst, unsigned dstlen, + const wchar_t* src, unsigned srclen) { + unsigned i = 0; + unsigned count = 0; + if (dstlen) for (;;) { + unsigned ucs; + if (i >= srclen) {dst[count] = 0; return count;} + ucs = src[i++]; + if (ucs < 0x80U) { + dst[count++] = (char)ucs; + if (count >= dstlen) {dst[count-1] = 0; break;} + } else if (ucs < 0x800U) { // 2 bytes + if (count+2 >= dstlen) {dst[count] = 0; count += 2; break;} + dst[count++] = 0xc0 | (char)(ucs >> 6); + dst[count++] = 0x80 | (char)(ucs & 0x3F); +#ifdef _WIN32 + } else if (ucs >= 0xd800 && ucs <= 0xdbff && i < srclen && + src[i] >= 0xdc00 && src[i] <= 0xdfff) { + // surrogate pair + unsigned ucs2 = src[i++]; + ucs = 0x10000U + ((ucs&0x3ff)<<10) + (ucs2&0x3ff); + // all surrogate pairs turn into 4-byte utf8 +#else + } else if (ucs >= 0x10000) { + if (ucs > 0x10ffff) { + ucs = 0xfffd; + goto J1; + } +#endif + if (count+4 >= dstlen) {dst[count] = 0; count += 4; break;} + dst[count++] = 0xf0 | (char)(ucs >> 18); + dst[count++] = 0x80 | (char)((ucs >> 12) & 0x3F); + dst[count++] = 0x80 | (char)((ucs >> 6) & 0x3F); + dst[count++] = 0x80 | (char)(ucs & 0x3F); + } else { +#ifndef _WIN32 + J1: +#endif + // all others are 3 bytes: + if (count+3 >= dstlen) {dst[count] = 0; count += 3; break;} + dst[count++] = 0xe0 | (char)(ucs >> 12); + dst[count++] = 0x80 | (char)((ucs >> 6) & 0x3F); + dst[count++] = 0x80 | (char)(ucs & 0x3F); + } + } + // we filled dst, measure the rest: + while (i < srclen) { + unsigned ucs = src[i++]; + if (ucs < 0x80U) { + count++; + } else if (ucs < 0x800U) { // 2 bytes + count += 2; +#ifdef _WIN32 + } else if (ucs >= 0xd800 && ucs <= 0xdbff && i < srclen-1 && + src[i+1] >= 0xdc00 && src[i+1] <= 0xdfff) { + // surrogate pair + ++i; +#else + } else if (ucs >= 0x10000 && ucs <= 0x10ffff) { +#endif + count += 4; + } else { + count += 3; + } + } + return count; +} + + +/************************************************************************/ +/* utf8froma() */ +/************************************************************************/ + +/* Convert an ISO-8859-1 (ie normal c-string) byte stream to UTF-8. + + It is possible this should convert Microsoft's CP1252 to UTF-8 + instead. This would translate the codes in the range 0x80-0x9f + to different characters. Currently it does not do this. + + Up to \a dstlen bytes are written to \a dst, including a null + terminator. The return value is the number of bytes that would be + written, not counting the null terminator. If greater or equal to + \a dstlen then if you malloc a new array of size n+1 you will have + the space needed for the entire string. If \a dstlen is zero then + nothing is written and this call just measures the storage space + needed. + + \a srclen is the number of bytes in \a src to convert. + + If the return value equals \a srclen then this indicates that + no conversion is necessary, as only ASCII characters are in the + string. +*/ +static unsigned utf8froma(char* dst, unsigned dstlen, + const char* src, unsigned srclen) { + const char* p = src; + const char* e = src+srclen; + unsigned count = 0; + if (dstlen) for (;;) { + unsigned char ucs; + if (p >= e) {dst[count] = 0; return count;} + ucs = *(unsigned char*)p++; + if (ucs < 0x80U) { + dst[count++] = ucs; + if (count >= dstlen) {dst[count-1] = 0; break;} + } else { // 2 bytes (note that CP1252 translate could make 3 bytes!) + if (count+2 >= dstlen) {dst[count] = 0; count += 2; break;} + dst[count++] = 0xc0 | (ucs >> 6); + dst[count++] = 0x80 | (ucs & 0x3F); + } + } + // we filled dst, measure the rest: + while (p < e) { + unsigned char ucs = *(unsigned char*)p++; + if (ucs < 0x80U) { + count++; + } else { + count += 2; + } + } + return count; +} + +#ifdef _WIN32 + +/************************************************************************/ +/* CPLWin32Recode() */ +/************************************************************************/ + +/* Convert an CODEPAGE (ie normal c-string) byte stream + to another CODEPAGE (ie normal c-string) byte stream. + + \a src is target c-string byte stream (including a null terminator). + \a src_code_page is target c-string byte code page. + \a dst_code_page is destination c-string byte code page. + + UTF7 65000 + UTF8 65001 + OEM-US 437 + OEM-ALABIC 720 + OEM-GREEK 737 + OEM-BALTIC 775 + OEM-MLATIN1 850 + OEM-LATIN2 852 + OEM-CYRILLIC 855 + OEM-TURKISH 857 + OEM-MLATIN1P 858 + OEM-HEBREW 862 + OEM-RUSSIAN 866 + + THAI 874 + SJIS 932 + GBK 936 + KOREA 949 + BIG5 950 + + EUROPE 1250 + CYRILLIC 1251 + LATIN1 1252 + GREEK 1253 + TURKISH 1254 + HEBREW 1255 + ARABIC 1256 + BALTIC 1257 + VIETNAM 1258 + + ISO-LATIN1 28591 + ISO-LATIN2 28592 + ISO-LATIN3 28593 + ISO-BALTIC 28594 + ISO-CYRILLIC 28595 + ISO-ARABIC 28596 + ISO-HEBREW 28598 + ISO-TURKISH 28599 + ISO-LATIN9 28605 + + ISO-2022-JP 50220 + +*/ + +char* CPLWin32Recode( const char* src, unsigned src_code_page, unsigned dst_code_page ) +{ + /* Convert from source code page to Unicode */ + + /* Compute the length in wide characters */ + int wlen = MultiByteToWideChar( src_code_page, MB_ERR_INVALID_CHARS, src, -1, 0, 0 ); + if (wlen == 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION) + { + if (!bHaveWarned5) + { + bHaveWarned5 = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "One or several characters could not be translated from CP%d. " + "This warning will not be emitted anymore.", src_code_page); + } + + /* Retry now without MB_ERR_INVALID_CHARS flag */ + wlen = MultiByteToWideChar( src_code_page, 0, src, -1, 0, 0 ); + } + + /* Do the actual conversion */ + wchar_t* tbuf = (wchar_t*)CPLCalloc(sizeof(wchar_t),wlen+1); + tbuf[wlen] = 0; + MultiByteToWideChar( src_code_page, 0, src, -1, tbuf, wlen+1 ); + + /* Convert from Unicode to destination code page */ + + /* Compute the length in chars */ + BOOL bUsedDefaultChar = FALSE; + int len; + if ( dst_code_page == CP_UTF7 || dst_code_page == CP_UTF8 ) + len = WideCharToMultiByte( dst_code_page, 0, tbuf, -1, 0, 0, 0, NULL ); + else + len = WideCharToMultiByte( dst_code_page, 0, tbuf, -1, 0, 0, 0, &bUsedDefaultChar ); + if (bUsedDefaultChar) + { + if (!bHaveWarned6) + { + bHaveWarned6 = TRUE; + CPLError(CE_Warning, CPLE_AppDefined, + "One or several characters could not be translated to CP%d. " + "This warning will not be emitted anymore.", dst_code_page); + } + } + + /* Do the actual conversion */ + char* pszResult = (char*)CPLCalloc(sizeof(char),len+1); + WideCharToMultiByte( dst_code_page, 0, tbuf, -1, pszResult, len+1, 0, NULL ); + pszResult[len] = 0; + + /* Cleanup */ + CPLFree(tbuf); + + return pszResult; +} + +#endif + + + +/* +** For now we disable the rest which is locale() related. We may need +** parts of it later. +*/ + +#ifdef notdef + +#ifdef _WIN32 +# include +#endif + +/*! Return true if the "locale" seems to indicate that UTF-8 encoding + is used. If true the utf8tomb and utf8frommb don't do anything + useful. + + It is highly recommended that you change your system so this + does return true. On Windows this is done by setting the + "codepage" to CP_UTF8. On Unix this is done by setting $LC_CTYPE + to a string containing the letters "utf" or "UTF" in it, or by + deleting all $LC* and $LANG environment variables. In the future + it is likely that all non-Asian Unix systems will return true, + due to the compatability of UTF-8 with ISO-8859-1. +*/ +int utf8locale(void) { + static int ret = 2; + if (ret == 2) { +#ifdef _WIN32 + ret = GetACP() == CP_UTF8; +#else + char* s; + ret = 1; // assumme UTF-8 if no locale + if (((s = getenv("LC_CTYPE")) && *s) || + ((s = getenv("LC_ALL")) && *s) || + ((s = getenv("LANG")) && *s)) { + ret = (strstr(s,"utf") || strstr(s,"UTF")); + } +#endif + } + return ret; +} + +/*! Convert the UTF-8 used by FLTK to the locale-specific encoding + used for filenames (and sometimes used for data in files). + Unfortunatley due to stupid design you will have to do this as + needed for filenames. This is a bug on both Unix and Windows. + + Up to \a dstlen bytes are written to \a dst, including a null + terminator. The return value is the number of bytes that would be + written, not counting the null terminator. If greater or equal to + \a dstlen then if you malloc a new array of size n+1 you will have + the space needed for the entire string. If \a dstlen is zero then + nothing is written and this call just measures the storage space + needed. + + If utf8locale() returns true then this does not change the data. + It is copied and truncated as necessary to + the destination buffer and \a srclen is always returned. */ +unsigned utf8tomb(const char* src, unsigned srclen, + char* dst, unsigned dstlen) +{ + if (!utf8locale()) { +#ifdef _WIN32 + wchar_t lbuf[1024]; + wchar_t* buf = lbuf; + unsigned length = utf8towc(src, srclen, buf, 1024); + unsigned ret; + if (length >= 1024) { + buf = (wchar_t*)(malloc((length+1)*sizeof(wchar_t))); + utf8towc(src, srclen, buf, length+1); + } + if (dstlen) { + // apparently this does not null-terminate, even though msdn + // documentation claims it does: + ret = + WideCharToMultiByte(GetACP(), 0, buf, length, dst, dstlen, 0, 0); + dst[ret] = 0; + } + // if it overflows or measuring length, get the actual length: + if (dstlen==0 || ret >= dstlen-1) + ret = + WideCharToMultiByte(GetACP(), 0, buf, length, 0, 0, 0, 0); + if (buf != lbuf) free((void*)buf); + return ret; +#else + wchar_t lbuf[1024]; + wchar_t* buf = lbuf; + unsigned length = utf8towc(src, srclen, buf, 1024); + int ret; + if (length >= 1024) { + buf = (wchar_t*)(malloc((length+1)*sizeof(wchar_t))); + utf8towc(src, srclen, buf, length+1); + } + if (dstlen) { + ret = wcstombs(dst, buf, dstlen); + if (ret >= dstlen-1) ret = wcstombs(0,buf,0); + } else { + ret = wcstombs(0,buf,0); + } + if (buf != lbuf) free((void*)buf); + if (ret >= 0) return (unsigned)ret; + // on any errors we return the UTF-8 as raw text... +#endif + } + // identity transform: + if (srclen < dstlen) { + memcpy(dst, src, srclen); + dst[srclen] = 0; + } else { + memcpy(dst, src, dstlen-1); + dst[dstlen-1] = 0; + } + return srclen; +} + +/*! Convert a filename from the locale-specific multibyte encoding + used by Windows to UTF-8 as used by FLTK. + + Up to \a dstlen bytes are written to \a dst, including a null + terminator. The return value is the number of bytes that would be + written, not counting the null terminator. If greater or equal to + \a dstlen then if you malloc a new array of size n+1 you will have + the space needed for the entire string. If \a dstlen is zero then + nothing is written and this call just measures the storage space + needed. + + On Unix or on Windows when a UTF-8 locale is in effect, this + does not change the data. It is copied and truncated as necessary to + the destination buffer and \a srclen is always returned. + You may also want to check if utf8test() returns non-zero, so that + the filesystem can store filenames in UTF-8 encoding regardless of + the locale. +*/ +unsigned utf8frommb(char* dst, unsigned dstlen, + const char* src, unsigned srclen) +{ + if (!utf8locale()) { +#ifdef _WIN32 + wchar_t lbuf[1024]; + wchar_t* buf = lbuf; + unsigned length; + unsigned ret; + length = + MultiByteToWideChar(GetACP(), 0, src, srclen, buf, 1024); + if (length >= 1024) { + length = MultiByteToWideChar(GetACP(), 0, src, srclen, 0, 0); + buf = (wchar_t*)(malloc(length*sizeof(wchar_t))); + MultiByteToWideChar(GetACP(), 0, src, srclen, buf, length); + } + ret = utf8fromwc(dst, dstlen, buf, length); + if (buf != lbuf) free((void*)buf); + return ret; +#else + wchar_t lbuf[1024]; + wchar_t* buf = lbuf; + int length; + unsigned ret; + length = mbstowcs(buf, src, 1024); + if (length >= 1024) { + length = mbstowcs(0, src, 0)+1; + buf = (wchar_t*)(malloc(length*sizeof(unsigned short))); + mbstowcs(buf, src, length); + } + if (length >= 0) { + ret = utf8fromwc(dst, dstlen, buf, length); + if (buf != lbuf) free((void*)buf); + return ret; + } + // errors in conversion return the UTF-8 unchanged +#endif + } + // identity transform: + if (srclen < dstlen) { + memcpy(dst, src, srclen); + dst[srclen] = 0; + } else { + memcpy(dst, src, dstlen-1); + dst[dstlen-1] = 0; + } + return srclen; +} + +#endif /* def notdef - disabled locale specific stuff */ + +/*! Examines the first \a srclen bytes in \a src and return a verdict + on whether it is UTF-8 or not. + - Returns 0 if there is any illegal UTF-8 sequences, using the + same rules as utf8decode(). Note that some UCS values considered + illegal by RFC 3629, such as 0xffff, are considered legal by this. + - Returns 1 if there are only single-byte characters (ie no bytes + have the high bit set). This is legal UTF-8, but also indicates + plain ASCII. It also returns 1 if \a srclen is zero. + - Returns 2 if there are only characters less than 0x800. + - Returns 3 if there are only characters less than 0x10000. + - Returns 4 if there are characters in the 0x10000 to 0x10ffff range. + + Because there are many illegal sequences in UTF-8, it is almost + impossible for a string in another encoding to be confused with + UTF-8. This is very useful for transitioning Unix to UTF-8 + filenames, you can simply test each filename with this to decide + if it is UTF-8 or in the locale encoding. My hope is that if + this is done we will be able to cleanly transition to a locale-less + encoding. +*/ + +static int utf8test(const char* src, unsigned srclen) { + int ret = 1; + const char* p = src; + const char* e = src+srclen; + while (p < e) { + if (*p & 0x80) { + int len; utf8decode(p,e,&len); + if (len < 2) return 0; + if (len > ret) ret = len; + p += len; + } else { + p++; + } + } + return ret; +} + +#endif /* defined(CPL_RECODE_STUB) */ diff --git a/bazaar/plugin/gdal/port/cpl_spawn.cpp b/bazaar/plugin/gdal/port/cpl_spawn.cpp new file mode 100644 index 000000000..6b8f54cf2 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_spawn.cpp @@ -0,0 +1,1014 @@ +/********************************************************************** + * $Id: cpl_spawn.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement CPLSystem(). + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_spawn.h" + +#include "cpl_error.h" +#include "cpl_conv.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" + +#define PIPE_BUFFER_SIZE 4096 + +#define IN_FOR_PARENT 0 +#define OUT_FOR_PARENT 1 + +CPL_CVSID("$Id: cpl_spawn.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE* fout); + +//int CPL_DLL CPLSystem( const char* pszApplicationName, const char* pszCommandLine ); + +/************************************************************************/ +/* FillPipeFromFile() */ +/************************************************************************/ + +static void FillPipeFromFile(VSILFILE* fin, CPL_FILE_HANDLE pipe_fd) +{ + char buf[PIPE_BUFFER_SIZE]; + while(TRUE) + { + int nRead = (int)VSIFReadL(buf, 1, PIPE_BUFFER_SIZE, fin); + if( nRead <= 0 ) + break; + if (!CPLPipeWrite(pipe_fd, buf, nRead)) + break; + } +} + +/************************************************************************/ +/* CPLSpawn() */ +/************************************************************************/ + +/** + * Runs an executable in another process. + * + * This function runs an executable, wait for it to finish and returns + * its exit code. + * + * It is implemented as CreateProcess() on Windows platforms, and fork()/exec() + * on other platforms. + * + * @param papszArgv argument list of the executable to run. papszArgv[0] is the + * name of the executable + * @param fin File handle for input data to feed to the standard input of the + * sub-process. May be NULL. + * @param fout File handle for output data to extract from the standard output of the + * sub-process. May be NULL. + * @param bDisplayErr Set to TRUE to emit the content of the standard error stream of + * the sub-process with CPLError(). + * + * @return the exit code of the spawned process, or -1 in case of error. + * + * @since GDAL 1.10.0 + */ + +int CPLSpawn(const char * const papszArgv[], VSILFILE* fin, VSILFILE* fout, + int bDisplayErr) +{ + CPLSpawnedProcess* sp = CPLSpawnAsync(NULL, papszArgv, TRUE, TRUE, TRUE, NULL); + if( sp == NULL ) + return -1; + + CPL_FILE_HANDLE in_child = CPLSpawnAsyncGetOutputFileHandle(sp); + if (fin != NULL) + FillPipeFromFile(fin, in_child); + CPLSpawnAsyncCloseOutputFileHandle(sp); + + CPL_FILE_HANDLE out_child = CPLSpawnAsyncGetInputFileHandle(sp); + if (fout != NULL) + FillFileFromPipe(out_child, fout); + CPLSpawnAsyncCloseInputFileHandle(sp); + + CPL_FILE_HANDLE err_child = CPLSpawnAsyncGetErrorFileHandle(sp); + CPLString osName; + osName.Printf("/vsimem/child_stderr_" CPL_FRMT_GIB, CPLGetPID()); + VSILFILE* ferr = VSIFOpenL(osName.c_str(), "w"); + + FillFileFromPipe(err_child, ferr); + CPLSpawnAsyncCloseErrorFileHandle(sp); + + VSIFCloseL(ferr); + vsi_l_offset nDataLength = 0; + GByte* pData = VSIGetMemFileBuffer(osName.c_str(), &nDataLength, TRUE); + if( nDataLength > 0 ) + pData[nDataLength-1] = '\0'; + if( pData && strstr((const char*)pData, "An error occured while forking process") != NULL ) + bDisplayErr = TRUE; + if( pData && bDisplayErr ) + CPLError(CE_Failure, CPLE_AppDefined, "[%s error] %s", papszArgv[0], pData); + CPLFree(pData); + + return CPLSpawnAsyncFinish(sp, TRUE, FALSE); +} + +#if defined(WIN32) + +#include + +#if 0 +/************************************************************************/ +/* CPLSystem() */ +/************************************************************************/ + +int CPLSystem( const char* pszApplicationName, const char* pszCommandLine ) +{ + int nRet = -1; + PROCESS_INFORMATION processInfo; + STARTUPINFO startupInfo; + ZeroMemory( &processInfo, sizeof(PROCESS_INFORMATION) ); + ZeroMemory( &startupInfo, sizeof(STARTUPINFO) ); + startupInfo.cb = sizeof(STARTUPINFO); + + char* pszDupedCommandLine = (pszCommandLine) ? CPLStrdup(pszCommandLine) : NULL; + + if( !CreateProcess( pszApplicationName, + pszDupedCommandLine, + NULL, + NULL, + FALSE, + CREATE_NO_WINDOW|NORMAL_PRIORITY_CLASS, + NULL, + NULL, + &startupInfo, + &processInfo) ) + { + DWORD err = GetLastError(); + CPLDebug("CPL", "'%s' failed : err = %d", pszCommandLine, (int)err); + nRet = -1; + } + else + { + WaitForSingleObject( processInfo.hProcess, INFINITE ); + + DWORD exitCode; + + // Get the exit code. + int err = GetExitCodeProcess(processInfo.hProcess, &exitCode); + + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + + if( !err ) + { + CPLDebug("CPL", "GetExitCodeProcess() failed : err = %d", err); + } + else + nRet = exitCode; + } + + CPLFree(pszDupedCommandLine); + + return nRet; +} +#endif + +/************************************************************************/ +/* CPLPipeRead() */ +/************************************************************************/ + +int CPLPipeRead(CPL_FILE_HANDLE fin, void* data, int length) +{ + GByte* pabyData = (GByte*)data; + int nRemain = length; + while( nRemain > 0 ) + { + DWORD nRead = 0; + if (!ReadFile( fin, pabyData, nRemain, &nRead, NULL)) + return FALSE; + pabyData += nRead; + nRemain -= nRead; + } + return TRUE; +} + +/************************************************************************/ +/* CPLPipeWrite() */ +/************************************************************************/ + +int CPLPipeWrite(CPL_FILE_HANDLE fout, const void* data, int length) +{ + const GByte* pabyData = (const GByte*)data; + int nRemain = length; + while( nRemain > 0 ) + { + DWORD nWritten = 0; + if (!WriteFile(fout, pabyData, nRemain, &nWritten, NULL)) + return FALSE; + pabyData += nWritten; + nRemain -= nWritten; + } + return TRUE; +} + +/************************************************************************/ +/* FillFileFromPipe() */ +/************************************************************************/ + +static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE* fout) +{ + char buf[PIPE_BUFFER_SIZE]; + while(TRUE) + { + DWORD nRead; + if (!ReadFile( pipe_fd, buf, PIPE_BUFFER_SIZE, &nRead, NULL)) + break; + if (nRead <= 0) + break; + int nWritten = (int)VSIFWriteL(buf, 1, nRead, fout); + if (nWritten < (int)nRead) + break; + } +} + +struct _CPLSpawnedProcess +{ + HANDLE hProcess; + DWORD nProcessId; + HANDLE hThread; + CPL_FILE_HANDLE fin; + CPL_FILE_HANDLE fout; + CPL_FILE_HANDLE ferr; +}; + +/************************************************************************/ +/* CPLSpawnAsync() */ +/************************************************************************/ + +CPLSpawnedProcess* CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE), + const char * const papszArgv[], + int bCreateInputPipe, + int bCreateOutputPipe, + int bCreateErrorPipe, + char** papszOptions) +{ + HANDLE pipe_in[2] = {NULL, NULL}; + HANDLE pipe_out[2] = {NULL, NULL}; + HANDLE pipe_err[2] = {NULL, NULL}; + SECURITY_ATTRIBUTES saAttr; + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + CPLString osCommandLine; + int i; + CPLSpawnedProcess* p = NULL; + + if( papszArgv == NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "On Windows, papszArgv argument must not be NULL"); + return NULL; + } + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if( bCreateInputPipe ) + { + if (!CreatePipe(&pipe_in[IN_FOR_PARENT],&pipe_in[OUT_FOR_PARENT],&saAttr, 0)) + goto err_pipe; + /* The child must not inherit from the write side of the pipe_in */ + if (!SetHandleInformation(pipe_in[OUT_FOR_PARENT],HANDLE_FLAG_INHERIT,0)) + goto err_pipe; + } + + if( bCreateOutputPipe ) + { + if (!CreatePipe(&pipe_out[IN_FOR_PARENT],&pipe_out[OUT_FOR_PARENT],&saAttr, 0)) + goto err_pipe; + /* The child must not inherit from the read side of the pipe_out */ + if (!SetHandleInformation(pipe_out[IN_FOR_PARENT],HANDLE_FLAG_INHERIT,0)) + goto err_pipe; + } + + if( bCreateErrorPipe ) + { + if (!CreatePipe(&pipe_err[IN_FOR_PARENT],&pipe_err[OUT_FOR_PARENT],&saAttr, 0)) + goto err_pipe; + /* The child must not inherit from the read side of the pipe_err */ + if (!SetHandleInformation(pipe_err[IN_FOR_PARENT],HANDLE_FLAG_INHERIT,0)) + goto err_pipe; + } + + memset(&piProcInfo, 0, sizeof(PROCESS_INFORMATION)); + memset(&siStartInfo, 0, sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdInput = (bCreateInputPipe) ? pipe_in[IN_FOR_PARENT] : GetStdHandle(STD_INPUT_HANDLE); + siStartInfo.hStdOutput = (bCreateOutputPipe) ? pipe_out[OUT_FOR_PARENT] : GetStdHandle(STD_OUTPUT_HANDLE); + siStartInfo.hStdError = (bCreateErrorPipe) ? pipe_err[OUT_FOR_PARENT] : GetStdHandle(STD_ERROR_HANDLE); + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + for(i=0;papszArgv[i] != NULL;i++) + { + if (i > 0) + osCommandLine += " "; + /* We need to quote arguments with spaces in them (if not already done) */ + if( strchr(papszArgv[i], ' ') != NULL && + papszArgv[i][0] != '"' ) + { + osCommandLine += "\""; + osCommandLine += papszArgv[i]; + osCommandLine += "\""; + } + else + osCommandLine += papszArgv[i]; + } + + if (!CreateProcess(NULL, + (CHAR*)osCommandLine.c_str(), + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + CREATE_NO_WINDOW|NORMAL_PRIORITY_CLASS, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, + &piProcInfo)) + { + CPLError(CE_Failure, CPLE_AppDefined, "Could not create process %s", + osCommandLine.c_str()); + goto err; + } + + /* Close unused end of pipe */ + if( bCreateInputPipe ) + CloseHandle(pipe_in[IN_FOR_PARENT]); + if( bCreateOutputPipe ) + CloseHandle(pipe_out[OUT_FOR_PARENT]); + if( bCreateErrorPipe ) + CloseHandle(pipe_err[OUT_FOR_PARENT]); + + p = (CPLSpawnedProcess*)CPLMalloc(sizeof(CPLSpawnedProcess)); + p->hProcess = piProcInfo.hProcess; + p->nProcessId = piProcInfo.dwProcessId; + p->hThread = piProcInfo.hThread; + p->fin = pipe_out[IN_FOR_PARENT]; + p->fout = pipe_in[OUT_FOR_PARENT]; + p->ferr = pipe_err[IN_FOR_PARENT]; + return p; + +err_pipe: + CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe"); +err: + for(i=0;i<2;i++) + { + if (pipe_in[i] != NULL) + CloseHandle(pipe_in[i]); + if (pipe_out[i] != NULL) + CloseHandle(pipe_out[i]); + if (pipe_err[i] != NULL) + CloseHandle(pipe_err[i]); + } + + return NULL; +} + +/************************************************************************/ +/* CPLSpawnAsyncGetChildProcessId() */ +/************************************************************************/ + +CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess* p) +{ + return p->nProcessId; +} + +/************************************************************************/ +/* CPLSpawnAsyncFinish() */ +/************************************************************************/ + +int CPLSpawnAsyncFinish(CPLSpawnedProcess* p, int bWait, int bKill) +{ + // Get the exit code. + DWORD exitCode = -1; + + if( bWait ) + { + WaitForSingleObject( p->hProcess, INFINITE ); + GetExitCodeProcess(p->hProcess, &exitCode); + } + else + exitCode = 0; + + CloseHandle(p->hProcess); + CloseHandle(p->hThread); + + CPLSpawnAsyncCloseInputFileHandle(p); + CPLSpawnAsyncCloseOutputFileHandle(p); + CPLSpawnAsyncCloseErrorFileHandle(p); + CPLFree(p); + + return (int)exitCode; +} + +/************************************************************************/ +/* CPLSpawnAsyncCloseInputFileHandle() */ +/************************************************************************/ + +void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess* p) +{ + if( p->fin != NULL ) + CloseHandle(p->fin); + p->fin = NULL; +} + +/************************************************************************/ +/* CPLSpawnAsyncCloseOutputFileHandle() */ +/************************************************************************/ + +void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess* p) +{ + if( p->fout != NULL ) + CloseHandle(p->fout); + p->fout = NULL; +} + +/************************************************************************/ +/* CPLSpawnAsyncCloseErrorFileHandle() */ +/************************************************************************/ + +void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess* p) +{ + if( p->ferr != NULL ) + CloseHandle(p->ferr); + p->ferr = NULL; +} + +#else + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_POSIX_SPAWNP + #include + #ifdef __APPLE__ + #include + #endif + #if defined(__APPLE__) && (!defined(TARGET_OS_IPHONE) || TARGET_OS_IPHONE==0) + #include + #define environ (*_NSGetEnviron()) + #else + extern char** environ; + #endif +#endif + +#if 0 +/************************************************************************/ +/* CPLSystem() */ +/************************************************************************/ + +/** + * Runs an executable in another process. + * + * This function runs an executable, wait for it to finish and returns + * its exit code. + * + * It is implemented as CreateProcess() on Windows platforms, and system() + * on other platforms. + * + * @param pszApplicationName the lpApplicationName for Windows (might be NULL), + * or ignored on other platforms. + * @param pszCommandLine the command line, starting with the executable name + * + * @return the exit code of the spawned process, or -1 in case of error. + * + * @since GDAL 1.10.0 + */ + +int CPLSystem( const char* pszApplicationName, const char* pszCommandLine ) +{ + return system(pszCommandLine); +} +#endif + +/************************************************************************/ +/* CPLPipeRead() */ +/************************************************************************/ + +/** + * Read data from the standard output of a forked process. + * + * @param p handle returned by CPLSpawnAsyncGetInputFileHandle(). + * @param data buffer in which to write. + * @param length number of bytes to read. + * + * @return TRUE in case of success. + * + * @since GDAL 1.10.0 + */ +int CPLPipeRead(CPL_FILE_HANDLE fin, void* data, int length) +{ + GByte* pabyData = (GByte*)data; + int nRemain = length; + while( nRemain > 0 ) + { + while(TRUE) + { + int n = read(fin, pabyData, nRemain); + if( n < 0 ) + { + if( errno == EINTR ) + continue; + else + return FALSE; + } + else if( n == 0 ) + return FALSE; + pabyData += n; + nRemain -= n; + break; + } + } + return TRUE; +} + +/************************************************************************/ +/* CPLPipeWrite() */ +/************************************************************************/ + +/** + * Write data to the standard input of a forked process. + * + * @param fout handle returned by CPLSpawnAsyncGetOutputFileHandle(). + * @param data buffer from which to read. + * @param length number of bytes to write. + * + * @return TRUE in case of success. + * + * @since GDAL 1.10.0 + */ +int CPLPipeWrite(CPL_FILE_HANDLE fout, const void* data, int length) +{ + const GByte* pabyData = (const GByte*)data; + int nRemain = length; + while( nRemain > 0 ) + { + while(TRUE) + { + int n = write(fout, pabyData, nRemain); + if( n < 0 ) + { + if( errno == EINTR ) + continue; + else + return FALSE; + } + pabyData += n; + nRemain -= n; + break; + } + } + return TRUE; +} + +/************************************************************************/ +/* FillFileFromPipe() */ +/************************************************************************/ + +static void FillFileFromPipe(CPL_FILE_HANDLE pipe_fd, VSILFILE* fout) +{ + char buf[PIPE_BUFFER_SIZE]; + while(TRUE) + { + int nRead = read(pipe_fd, buf, PIPE_BUFFER_SIZE); + if (nRead <= 0) + break; + int nWritten = (int)VSIFWriteL(buf, 1, nRead, fout); + if (nWritten < nRead) + break; + } +} + +/************************************************************************/ +/* CPLSpawnAsync() */ +/************************************************************************/ + +struct _CPLSpawnedProcess +{ + pid_t pid; + CPL_FILE_HANDLE fin; + CPL_FILE_HANDLE fout; + CPL_FILE_HANDLE ferr; +#ifdef HAVE_POSIX_SPAWNP + int bFreeActions; + posix_spawn_file_actions_t actions; +#endif +}; + +/** + * Runs an executable in another process (or fork the current process) + * and return immediately. + * + * This function launches an executable and returns immediately, while letting + * the sub-process to run asynchronously. + * + * It is implemented as CreateProcess() on Windows platforms, and fork()/exec() + * on other platforms. + * + * On Unix, a pointer of function can be provided to run in the child process, + * without exec()'ing a new executable. + * + * @param pfnMain the function to run in the child process (Unix only). + * @param papszArgv argument list of the executable to run. papszArgv[0] is the + * name of the executable. + * @param bCreateInputPipe set to TRUE to create a pipe for the child input stream. + * @param bCreateOutputPipe set to TRUE to create a pipe for the child output stream. + * @param bCreateErrorPipe set to TRUE to create a pipe for the child error stream. + * + * @return a handle, that must be freed with CPLSpawnAsyncFinish() + * + * @since GDAL 1.10.0 + */ +CPLSpawnedProcess* CPLSpawnAsync(int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE), + const char * const papszArgv[], + int bCreateInputPipe, + int bCreateOutputPipe, + int bCreateErrorPipe, + CPL_UNUSED char** papszOptions) +{ + pid_t pid; + int pipe_in[2] = { -1, -1 }; + int pipe_out[2] = { -1, -1 }; + int pipe_err[2] = { -1, -1 }; + int i; + char** papszArgvDup = CSLDuplicate((char**)papszArgv); + int bDup2In = bCreateInputPipe, + bDup2Out = bCreateOutputPipe, + bDup2Err = bCreateErrorPipe; + + if ((bCreateInputPipe && pipe(pipe_in)) || + (bCreateOutputPipe && pipe(pipe_out)) || + (bCreateErrorPipe && pipe(pipe_err))) + goto err_pipe; + + /* If we don't do any file actions, posix_spawnp() might be implemented */ + /* efficiently as a vfork()/exec() pair (or if it is not available, we */ + /* can use vfork()/exec()), so if the child is cooperative */ + /* we pass the pipe handles as commandline arguments */ + if( papszArgv != NULL ) + { + for(i=0; papszArgvDup[i] != NULL; i++) + { + if( bCreateInputPipe && strcmp(papszArgvDup[i], "{pipe_in}") == 0 ) + { + CPLFree(papszArgvDup[i]); + papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d", + pipe_in[IN_FOR_PARENT], pipe_in[OUT_FOR_PARENT])); + bDup2In = FALSE; + } + else if( bCreateOutputPipe && strcmp(papszArgvDup[i], "{pipe_out}") == 0 ) + { + CPLFree(papszArgvDup[i]); + papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d", + pipe_out[OUT_FOR_PARENT], pipe_out[IN_FOR_PARENT])); + bDup2Out = FALSE; + } + else if( bCreateErrorPipe && strcmp(papszArgvDup[i], "{pipe_err}") == 0 ) + { + CPLFree(papszArgvDup[i]); + papszArgvDup[i] = CPLStrdup(CPLSPrintf("%d,%d", + pipe_err[OUT_FOR_PARENT], pipe_err[IN_FOR_PARENT])); + bDup2Err = FALSE; + } + } + } + +#ifdef HAVE_POSIX_SPAWNP + if( papszArgv != NULL ) + { + int bHasActions = FALSE; + posix_spawn_file_actions_t actions; + + if( bDup2In ) + { + if( !bHasActions ) posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_adddup2(&actions, pipe_in[IN_FOR_PARENT], fileno(stdin)); + posix_spawn_file_actions_addclose(&actions, pipe_in[OUT_FOR_PARENT]); + bHasActions = TRUE; + } + + if( bDup2Out ) + { + if( !bHasActions ) posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_adddup2(&actions, pipe_out[OUT_FOR_PARENT], fileno(stdout)); + posix_spawn_file_actions_addclose(&actions, pipe_out[IN_FOR_PARENT]); + bHasActions = TRUE; + } + + if( bDup2Err ) + { + if( !bHasActions ) posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_adddup2(&actions, pipe_err[OUT_FOR_PARENT], fileno(stderr)); + posix_spawn_file_actions_addclose(&actions, pipe_err[IN_FOR_PARENT]); + bHasActions = TRUE; + } + + if( posix_spawnp(&pid, papszArgvDup[0], + bHasActions ? &actions : NULL, + NULL, + (char* const*) papszArgvDup, + environ) != 0 ) + { + if( bHasActions ) + posix_spawn_file_actions_destroy(&actions); + CPLError(CE_Failure, CPLE_AppDefined, "posix_spawnp() failed"); + goto err; + } + + CSLDestroy(papszArgvDup); + + /* Close unused end of pipe */ + if( bCreateInputPipe ) + close(pipe_in[IN_FOR_PARENT]); + if( bCreateOutputPipe ) + close(pipe_out[OUT_FOR_PARENT]); + if( bCreateErrorPipe ) + close(pipe_err[OUT_FOR_PARENT]); + + /* Ignore SIGPIPE */ + #ifdef SIGPIPE + signal (SIGPIPE, SIG_IGN); + #endif + CPLSpawnedProcess* p = (CPLSpawnedProcess*)CPLMalloc(sizeof(CPLSpawnedProcess)); + if( bHasActions ) + memcpy(&p->actions, &actions, sizeof(actions)); + p->bFreeActions = bHasActions; + p->pid = pid; + p->fin = pipe_out[IN_FOR_PARENT]; + p->fout = pipe_in[OUT_FOR_PARENT]; + p->ferr = pipe_err[IN_FOR_PARENT]; + return p; + } +#endif // #ifdef HAVE_POSIX_SPAWNP + +#ifdef HAVE_VFORK + if( papszArgv != NULL && !bDup2In && !bDup2Out && !bDup2Err ) + pid = vfork(); + else +#endif + pid = fork(); + if (pid == 0) + { + /* Close unused end of pipe */ + if( bDup2In ) + close(pipe_in[OUT_FOR_PARENT]); + if( bDup2Out ) + close(pipe_out[IN_FOR_PARENT]); + if( bDup2Err ) + close(pipe_err[IN_FOR_PARENT]); + +#ifndef HAVE_POSIX_SPAWNP + if( papszArgv != NULL ) + { + if( bDup2In ) + dup2(pipe_in[IN_FOR_PARENT], fileno(stdin)); + if( bDup2Out ) + dup2(pipe_out[OUT_FOR_PARENT], fileno(stdout)); + if( bDup2Err ) + dup2(pipe_err[OUT_FOR_PARENT], fileno(stderr)); + + execvp(papszArgvDup[0], (char* const*) papszArgvDup); + + _exit(1); + } + else +#endif // HAVE_POSIX_SPAWNP + { + if( bCreateErrorPipe ) + close(pipe_err[OUT_FOR_PARENT]); + + int nRet = 0; + if (pfnMain != NULL) + nRet = pfnMain((bCreateInputPipe) ? pipe_in[IN_FOR_PARENT] : fileno(stdin), + (bCreateOutputPipe) ? pipe_out[OUT_FOR_PARENT] : fileno(stdout)); + _exit(nRet); + } + } + else if( pid > 0 ) + { + CSLDestroy(papszArgvDup); + + /* Close unused end of pipe */ + if( bCreateInputPipe ) + close(pipe_in[IN_FOR_PARENT]); + if( bCreateOutputPipe ) + close(pipe_out[OUT_FOR_PARENT]); + if( bCreateErrorPipe ) + close(pipe_err[OUT_FOR_PARENT]); + + /* Ignore SIGPIPE */ +#ifdef SIGPIPE + signal (SIGPIPE, SIG_IGN); +#endif + CPLSpawnedProcess* p = (CPLSpawnedProcess*)CPLMalloc(sizeof(CPLSpawnedProcess)); +#ifdef HAVE_POSIX_SPAWNP + p->bFreeActions = FALSE; +#endif + p->pid = pid; + p->fin = pipe_out[IN_FOR_PARENT]; + p->fout = pipe_in[OUT_FOR_PARENT]; + p->ferr = pipe_err[IN_FOR_PARENT]; + return p; + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "Fork failed"); + goto err; + } + +err_pipe: + CPLError(CE_Failure, CPLE_AppDefined, "Could not create pipe"); +err: + CSLDestroy(papszArgvDup); + for(i=0;i<2;i++) + { + if (pipe_in[i] >= 0) + close(pipe_in[i]); + if (pipe_out[i] >= 0) + close(pipe_out[i]); + if (pipe_err[i] >= 0) + close(pipe_err[i]); + } + + return NULL; +} + +/************************************************************************/ +/* CPLSpawnAsyncGetChildProcessId() */ +/************************************************************************/ + +CPL_PID CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess* p) +{ + return p->pid; +} + +/************************************************************************/ +/* CPLSpawnAsyncFinish() */ +/************************************************************************/ + +/** + * Wait for the forked process to finish. + * + * @param p handle returned by CPLSpawnAsync() + * @param bWait set to TRUE to wait for the child to terminate. Otherwise the associated + * handles are just cleaned. + * @param bKill set to TRUE to force child termination (unimplemented right now). + * + * @return the return code of the forked process if bWait == TRUE, 0 otherwise + * + * @since GDAL 1.10.0 + */ +int CPLSpawnAsyncFinish(CPLSpawnedProcess* p, int bWait, CPL_UNUSED int bKill) +{ + int status = 0; + + if( bWait ) + { + while(1) + { + status = -1; + int ret = waitpid (p->pid, &status, 0); + if (ret < 0) + { + if (errno != EINTR) + { + break; + } + } + else + break; + } + } + else + bWait = FALSE; + CPLSpawnAsyncCloseInputFileHandle(p); + CPLSpawnAsyncCloseOutputFileHandle(p); + CPLSpawnAsyncCloseErrorFileHandle(p); +#ifdef HAVE_POSIX_SPAWNP + if( p->bFreeActions ) + posix_spawn_file_actions_destroy(&p->actions); +#endif + CPLFree(p); + return status; +} + +/************************************************************************/ +/* CPLSpawnAsyncCloseInputFileHandle() */ +/************************************************************************/ + +void CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess* p) +{ + if( p->fin >= 0 ) + close(p->fin); + p->fin = -1; +} + +/************************************************************************/ +/* CPLSpawnAsyncCloseOutputFileHandle() */ +/************************************************************************/ + +void CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess* p) +{ + if( p->fout >= 0 ) + close(p->fout); + p->fout = -1; +} + +/************************************************************************/ +/* CPLSpawnAsyncCloseErrorFileHandle() */ +/************************************************************************/ + +void CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess* p) +{ + if( p->ferr >= 0 ) + close(p->ferr); + p->ferr = -1; +} + +#endif + +/************************************************************************/ +/* CPLSpawnAsyncGetInputFileHandle() */ +/************************************************************************/ + +/** + * Return the file handle of the standard output of the forked process + * from which to read. + * + * @param p handle returned by CPLSpawnAsync(). + * + * @return the file handle. + * + * @since GDAL 1.10.0 + */ +CPL_FILE_HANDLE CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess* p) +{ + return p->fin; +} + +/************************************************************************/ +/* CPLSpawnAsyncGetOutputFileHandle() */ +/************************************************************************/ + +/** + * Return the file handle of the standard input of the forked process + * into which to write + * + * @param p handle returned by CPLSpawnAsync(). + * + * @return the file handle. + * + * @since GDAL 1.10.0 + */ +CPL_FILE_HANDLE CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess* p) +{ + return p->fout; +} + +/************************************************************************/ +/* CPLSpawnAsyncGetErrorFileHandle() */ +/************************************************************************/ + +/** + * Return the file handle of the standard error of the forked process + * from which to read. + * + * @param p handle returned by CPLSpawnAsync(). + * + * @return the file handle + * + * @since GDAL 1.10.0 + */ +CPL_FILE_HANDLE CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess* p) +{ + return p->ferr; +} diff --git a/bazaar/plugin/gdal/port/cpl_spawn.h b/bazaar/plugin/gdal/port/cpl_spawn.h new file mode 100644 index 000000000..37036ba63 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_spawn.h @@ -0,0 +1,79 @@ +/********************************************************************** + * $Id: cpl_spawn.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement CPLSystem(). + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_SPAWN_H_INCLUDED +#define CPL_SPAWN_H_INCLUDED + +#include "cpl_vsi.h" + +CPL_C_START + +/* -------------------------------------------------------------------- */ +/* Spawn a process. */ +/* -------------------------------------------------------------------- */ + +int CPL_DLL CPLSpawn( const char * const papszArgv[], VSILFILE* fin, VSILFILE* fout, + int bDisplayErr ); + +#ifdef WIN32 +#include +typedef HANDLE CPL_FILE_HANDLE; +#define CPL_FILE_INVALID_HANDLE NULL +typedef DWORD CPL_PID; +#else +#include +typedef int CPL_FILE_HANDLE; +#define CPL_FILE_INVALID_HANDLE -1 +typedef pid_t CPL_PID; +#endif + +typedef struct _CPLSpawnedProcess CPLSpawnedProcess; + +CPLSpawnedProcess CPL_DLL* CPLSpawnAsync( int (*pfnMain)(CPL_FILE_HANDLE, CPL_FILE_HANDLE), + const char * const papszArgv[], + int bCreateInputPipe, + int bCreateOutputPipe, + int bCreateErrorPipe, + char** papszOptions ); +CPL_PID CPL_DLL CPLSpawnAsyncGetChildProcessId(CPLSpawnedProcess* p); +int CPL_DLL CPLSpawnAsyncFinish(CPLSpawnedProcess* p, int bWait, int bKill); +CPL_FILE_HANDLE CPL_DLL CPLSpawnAsyncGetInputFileHandle(CPLSpawnedProcess* p); +CPL_FILE_HANDLE CPL_DLL CPLSpawnAsyncGetOutputFileHandle(CPLSpawnedProcess* p); +CPL_FILE_HANDLE CPL_DLL CPLSpawnAsyncGetErrorFileHandle(CPLSpawnedProcess* p); +void CPL_DLL CPLSpawnAsyncCloseInputFileHandle(CPLSpawnedProcess* p); +void CPL_DLL CPLSpawnAsyncCloseOutputFileHandle(CPLSpawnedProcess* p); +void CPL_DLL CPLSpawnAsyncCloseErrorFileHandle(CPLSpawnedProcess* p); + +int CPL_DLL CPLPipeRead(CPL_FILE_HANDLE fin, void* data, int length); +int CPL_DLL CPLPipeWrite(CPL_FILE_HANDLE fout, const void* data, int length); + +CPL_C_END + +#endif // CPL_SPAWN_H_INCLUDED + diff --git a/bazaar/plugin/gdal/port/cpl_string.cpp b/bazaar/plugin/gdal/port/cpl_string.cpp new file mode 100644 index 000000000..72b29bb5f --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_string.cpp @@ -0,0 +1,2656 @@ +/********************************************************************** + * $Id: cpl_string.cpp 28689 2015-03-08 20:05:50Z rouault $ + * + * Name: cpl_string.cpp + * Project: CPL - Common Portability Library + * Purpose: String and Stringlist manipulation functions. + * Author: Daniel Morissette, danmo@videotron.ca + * + ********************************************************************** + * Copyright (c) 1998, Daniel Morissette + * Copyright (c) 2008-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ********************************************************************** + * + * Independent Security Audit 2003/04/04 Andrey Kiselev: + * Completed audit of this module. All functions may be used without buffer + * overflows and stack corruptions with any kind of input data strings with + * except of CPLSPrintf() and CSLAppendPrintf() (see note below). + * + * Security Audit 2003/03/28 warmerda: + * Completed security audit. I believe that this module may be safely used + * to parse tokenize arbitrary input strings, assemble arbitrary sets of + * names values into string lists, unescape and escape text even if provided + * by a potentially hostile source. + * + * CPLSPrintf() and CSLAppendPrintf() may not be safely invoked on + * arbitrary length inputs since it has a fixed size output buffer on system + * without vsnprintf(). + * + **********************************************************************/ + +#undef WARN_STANDARD_PRINTF + +#include "cpl_string.h" +#include "cpl_vsi.h" +#include "cpl_multiproc.h" + +#if defined(WIN32CE) +# include +# include +#endif + +CPL_CVSID("$Id: cpl_string.cpp 28689 2015-03-08 20:05:50Z rouault $"); + +/*===================================================================== + StringList manipulation functions. + =====================================================================*/ + +/********************************************************************** + * CSLAddString() + * + * Append a string to a StringList and return a pointer to the modified + * StringList. + * If the input StringList is NULL, then a new StringList is created. + * Note that CSLAddString performance when building a list is in O(n^2) + * which can cause noticable slow down when n > 10000. + **********************************************************************/ +char **CSLAddString(char **papszStrList, const char *pszNewString) +{ + int nItems=0; + + if (pszNewString == NULL) + return papszStrList; /* Nothing to do!*/ + + /* Allocate room for the new string */ + if (papszStrList == NULL) + papszStrList = (char**) CPLCalloc(2,sizeof(char*)); + else + { + nItems = CSLCount(papszStrList); + papszStrList = (char**)CPLRealloc(papszStrList, + (nItems+2)*sizeof(char*)); + } + + /* Copy the string in the list */ + papszStrList[nItems] = CPLStrdup(pszNewString); + papszStrList[nItems+1] = NULL; + + return papszStrList; +} + +/************************************************************************/ +/* CSLCount() */ +/************************************************************************/ + +/** + * Return number of items in a string list. + * + * Returns the number of items in a string list, not counting the + * terminating NULL. Passing in NULL is safe, and will result in a count + * of zero. + * + * Lists are counted by iterating through them so long lists will + * take more time than short lists. Care should be taken to avoid using + * CSLCount() as an end condition for loops as it will result in O(n^2) + * behavior. + * + * @param papszStrList the string list to count. + * + * @return the number of entries. + */ +int CSLCount(char **papszStrList) +{ + int nItems=0; + + if (papszStrList) + { + while(*papszStrList != NULL) + { + nItems++; + papszStrList++; + } + } + + return nItems; +} + + +/************************************************************************/ +/* CSLGetField() */ +/* */ +/* Fetches the indicated field, being careful not to crash if */ +/* the field doesn't exist within this string list. The */ +/* returned pointer should not be freed, and doesn't */ +/* necessarily last long. */ +/************************************************************************/ + +const char * CSLGetField( char ** papszStrList, int iField ) + +{ + int i; + + if( papszStrList == NULL || iField < 0 ) + return( "" ); + + for( i = 0; i < iField+1; i++ ) + { + if( papszStrList[i] == NULL ) + return ""; + } + + return( papszStrList[iField] ); +} + +/************************************************************************/ +/* CSLDestroy() */ +/************************************************************************/ + +/** + * Free string list. + * + * Frees the passed string list (null terminated array of strings). + * It is safe to pass NULL. + * + * @param papszStrList the list to free. + */ +void CPL_STDCALL CSLDestroy(char **papszStrList) +{ + char **papszPtr; + + if (papszStrList) + { + papszPtr = papszStrList; + while(*papszPtr != NULL) + { + CPLFree(*papszPtr); + papszPtr++; + } + + CPLFree(papszStrList); + } +} + +/************************************************************************/ +/* CSLDuplicate() */ +/************************************************************************/ + +/** + * Clone a string list. + * + * Efficiently allocates a copy of a string list. The returned list is + * owned by the caller and should be freed with CSLDestroy(). + * + * @param papszStrList the input string list. + * + * @return newly allocated copy. + */ + +char **CSLDuplicate(char **papszStrList) +{ + char **papszNewList, **papszSrc, **papszDst; + int nLines; + + nLines = CSLCount(papszStrList); + + if (nLines == 0) + return NULL; + + papszNewList = (char **)CPLMalloc((nLines+1)*sizeof(char*)); + papszSrc = papszStrList; + papszDst = papszNewList; + + while(*papszSrc != NULL) + { + *papszDst = CPLStrdup(*papszSrc); + + papszSrc++; + papszDst++; + } + *papszDst = NULL; + + return papszNewList; +} + +/************************************************************************/ +/* CSLMerge */ +/************************************************************************/ + +/** + * \brief Merge two lists. + * + * The two lists are merged, ensuring that if any keys appear in both + * that the value from the second (papszOverride) list take precidence. + * + * @param papszOrig the original list, being modified. + * @param papszOverride the list of items being merged in. This list + * is unaltered and remains owned by the caller. + * + * @return updated list. + */ + +char **CSLMerge( char **papszOrig, char **papszOverride ) + +{ + int i; + + if( papszOrig == NULL && papszOverride != NULL ) + return CSLDuplicate( papszOverride ); + + if( papszOverride == NULL ) + return papszOrig; + + for( i = 0; papszOverride[i] != NULL; i++ ) + { + char *pszKey = NULL; + const char *pszValue = CPLParseNameValue( papszOverride[i], &pszKey ); + + papszOrig = CSLSetNameValue( papszOrig, pszKey, pszValue ); + CPLFree( pszKey ); + } + + return papszOrig; +} + +/************************************************************************/ +/* CSLLoad2() */ +/************************************************************************/ + +/** + * Load a text file into a string list. + * + * The VSI*L API is used, so VSIFOpenL() supported objects that aren't + * physical files can also be accessed. Files are returned as a string list, + * with one item in the string list per line. End of line markers are + * stripped (by CPLReadLineL()). + * + * If reading the file fails a CPLError() will be issued and NULL returned. + * + * @param pszFname the name of the file to read. + * @param nMaxLines maximum number of lines to read before stopping, or -1 for no limit. + * @param nMaxCols maximum number of characters in a line before stopping, or -1 for no limit. + * @param papszOptions NULL-terminated array of options. Unused for now. + * + * @return a string list with the files lines, now owned by caller. To be freed with CSLDestroy() + * + * @since GDAL 1.7.0 + */ + +char **CSLLoad2(const char *pszFname, int nMaxLines, int nMaxCols, char** papszOptions) +{ + VSILFILE *fp; + const char *pszLine; + char **papszStrList=NULL; + int nLines = 0; + int nAllocatedLines = 0; + + fp = VSIFOpenL(pszFname, "rb"); + + if (fp) + { + CPLErrorReset(); + while(!VSIFEofL(fp) && (nMaxLines == -1 || nLines < nMaxLines)) + { + if ( (pszLine = CPLReadLine2L(fp, nMaxCols, papszOptions)) != NULL ) + { + if (nLines + 1 >= nAllocatedLines) + { + char** papszStrListNew; + nAllocatedLines = 16 + nAllocatedLines * 2; + papszStrListNew = (char**) VSIRealloc(papszStrList, + nAllocatedLines * sizeof(char*)); + if (papszStrListNew == NULL) + { + VSIFCloseL(fp); + CPLReadLineL( NULL ); + CPLError( CE_Failure, CPLE_OutOfMemory, + "CSLLoad2(\"%s\") failed: not enough memory to allocate lines.", + pszFname ); + return papszStrList; + } + papszStrList = papszStrListNew; + } + papszStrList[nLines] = CPLStrdup(pszLine); + papszStrList[nLines + 1] = NULL; + nLines ++; + } + else + break; + } + + VSIFCloseL(fp); + + CPLReadLineL( NULL ); + } + else + { + if (CSLFetchBoolean(papszOptions, "EMIT_ERROR_IF_CANNOT_OPEN_FILE", TRUE)) + { + /* Unable to open file */ + CPLError( CE_Failure, CPLE_OpenFailed, + "CSLLoad2(\"%s\") failed: unable to open file.", + pszFname ); + } + } + + return papszStrList; +} + +/************************************************************************/ +/* CSLLoad() */ +/************************************************************************/ + +/** + * Load a text file into a string list. + * + * The VSI*L API is used, so VSIFOpenL() supported objects that aren't + * physical files can also be accessed. Files are returned as a string list, + * with one item in the string list per line. End of line markers are + * stripped (by CPLReadLineL()). + * + * If reading the file fails a CPLError() will be issued and NULL returned. + * + * @param pszFname the name of the file to read. + * + * @return a string list with the files lines, now owned by caller. To be freed with CSLDestroy() + */ + +char **CSLLoad(const char *pszFname) +{ + return CSLLoad2(pszFname, -1, -1, NULL); +} + +/********************************************************************** + * CSLSave() + * + * Write a stringlist to a text file. + * + * Returns the number of lines written, or 0 if the file could not + * be written. + **********************************************************************/ +int CSLSave(char **papszStrList, const char *pszFname) +{ + VSILFILE *fp; + int nLines = 0; + + if (papszStrList) + { + if ((fp = VSIFOpenL(pszFname, "wt")) != NULL) + { + while(*papszStrList != NULL) + { + if( VSIFPrintfL( fp, "%s\n", *papszStrList ) < 1 ) + { + CPLError( CE_Failure, CPLE_FileIO, + "CSLSave(\"%s\") failed: unable to write to output file.", + pszFname ); + break; /* A Problem happened... abort */ + } + + nLines++; + papszStrList++; + } + + VSIFCloseL(fp); + } + else + { + /* Unable to open file */ + CPLError( CE_Failure, CPLE_OpenFailed, + "CSLSave(\"%s\") failed: unable to open output file.", + pszFname ); + } + } + + return nLines; +} + +/********************************************************************** + * CSLPrint() + * + * Print a StringList to fpOut. If fpOut==NULL, then output is sent + * to stdout. + * + * Returns the number of lines printed. + **********************************************************************/ +int CSLPrint(char **papszStrList, FILE *fpOut) +{ + int nLines=0; + + if (fpOut == NULL) + fpOut = stdout; + + if (papszStrList) + { + while(*papszStrList != NULL) + { + VSIFPrintf(fpOut, "%s\n", *papszStrList); + nLines++; + papszStrList++; + } + } + + return nLines; +} + + +/********************************************************************** + * CSLInsertStrings() + * + * Copies the contents of a StringList inside another StringList + * before the specified line. + * + * nInsertAtLineNo is a 0-based line index before which the new strings + * should be inserted. If this value is -1 or is larger than the actual + * number of strings in the list then the strings are added at the end + * of the source StringList. + * + * Returns the modified StringList. + **********************************************************************/ +char **CSLInsertStrings(char **papszStrList, int nInsertAtLineNo, + char **papszNewLines) +{ + int i, nSrcLines, nDstLines, nToInsert; + char **ppszSrc, **ppszDst; + + if (papszNewLines == NULL || + ( nToInsert = CSLCount(papszNewLines) ) == 0) + return papszStrList; /* Nothing to do!*/ + + nSrcLines = CSLCount(papszStrList); + nDstLines = nSrcLines + nToInsert; + + /* Allocate room for the new strings */ + papszStrList = (char**)CPLRealloc(papszStrList, + (nDstLines+1)*sizeof(char*)); + + /* Make sure the array is NULL-terminated... it may not be if + * papszStrList was NULL before Realloc() + */ + papszStrList[nSrcLines] = NULL; + + /* Make some room in the original list at the specified location + * Note that we also have to move the NULL pointer at the end of + * the source StringList. + */ + if (nInsertAtLineNo == -1 || nInsertAtLineNo > nSrcLines) + nInsertAtLineNo = nSrcLines; + + ppszSrc = papszStrList + nSrcLines; + ppszDst = papszStrList + nDstLines; + + for (i=nSrcLines; i>=nInsertAtLineNo; i--) + { + *ppszDst = *ppszSrc; + ppszDst--; + ppszSrc--; + } + + /* Copy the strings to the list */ + ppszSrc = papszNewLines; + ppszDst = papszStrList + nInsertAtLineNo; + + for (; *ppszSrc != NULL; ppszSrc++, ppszDst++) + { + *ppszDst = CPLStrdup(*ppszSrc); + } + + return papszStrList; +} + +/********************************************************************** + * CSLInsertString() + * + * Insert a string at a given line number inside a StringList + * + * nInsertAtLineNo is a 0-based line index before which the new string + * should be inserted. If this value is -1 or is larger than the actual + * number of strings in the list then the string is added at the end + * of the source StringList. + * + * Returns the modified StringList. + **********************************************************************/ +char **CSLInsertString(char **papszStrList, int nInsertAtLineNo, + const char *pszNewLine) +{ + char *apszList[2]; + + /* Create a temporary StringList and call CSLInsertStrings() + */ + apszList[0] = (char *) pszNewLine; + apszList[1] = NULL; + + return CSLInsertStrings(papszStrList, nInsertAtLineNo, apszList); +} + + +/********************************************************************** + * CSLRemoveStrings() + * + * Remove strings inside a StringList + * + * nFirstLineToDelete is the 0-based line index of the first line to + * remove. If this value is -1 or is larger than the actual + * number of strings in list then the nNumToRemove last strings are + * removed. + * + * If ppapszRetStrings != NULL then the deleted strings won't be + * free'd, they will be stored in a new StringList and the pointer to + * this new list will be returned in *ppapszRetStrings. + * + * Returns the modified StringList. + **********************************************************************/ +char **CSLRemoveStrings(char **papszStrList, int nFirstLineToDelete, + int nNumToRemove, char ***ppapszRetStrings) +{ + int i, nSrcLines, nDstLines; + char **ppszSrc, **ppszDst; + + nSrcLines = CSLCount(papszStrList); + nDstLines = nSrcLines - nNumToRemove; + + if (nNumToRemove < 1 || nSrcLines == 0) + return papszStrList; /* Nothing to do!*/ + + /* If operation will result in an empty StringList then don't waste + * time here! + */ + if (nDstLines < 1) + { + CSLDestroy(papszStrList); + return NULL; + } + + + /* Remove lines from the source StringList... + * Either free() each line or store them to a new StringList depending on + * the caller's choice. + */ + ppszDst = papszStrList + nFirstLineToDelete; + + if (ppapszRetStrings == NULL) + { + /* free() all the strings that will be removed. + */ + for (i=0; i < nNumToRemove; i++) + { + CPLFree(*ppszDst); + *ppszDst = NULL; + } + } + else + { + /* Store the strings to remove in a new StringList + */ + *ppapszRetStrings = (char **)CPLCalloc(nNumToRemove+1, sizeof(char*)); + + for (i=0; i < nNumToRemove; i++) + { + (*ppapszRetStrings)[i] = *ppszDst; + *ppszDst = NULL; + ppszDst++; + } + } + + + /* Shift down all the lines that follow the lines to remove. + */ + if (nFirstLineToDelete == -1 || nFirstLineToDelete > nSrcLines) + nFirstLineToDelete = nDstLines; + + ppszSrc = papszStrList + nFirstLineToDelete + nNumToRemove; + ppszDst = papszStrList + nFirstLineToDelete; + + for ( ; *ppszSrc != NULL; ppszSrc++, ppszDst++) + { + *ppszDst = *ppszSrc; + } + /* Move the NULL pointer at the end of the StringList */ + *ppszDst = *ppszSrc; + + /* At this point, we could realloc() papszStrList to a smaller size, but + * since this array will likely grow again in further operations on the + * StringList we'll leave it as it is. + */ + + return papszStrList; +} + +/************************************************************************/ +/* CSLFindString() */ +/************************************************************************/ + +/** + * Find a string within a string list (case insensitive). + * + * Returns the index of the entry in the string list that contains the + * target string. The string in the string list must be a full match for + * the target, but the search is case insensitive. + * + * @param papszList the string list to be searched. + * @param pszTarget the string to be searched for. + * + * @return the index of the string within the list or -1 on failure. + */ + +int CSLFindString( char ** papszList, const char * pszTarget ) + +{ + int i; + + if( papszList == NULL ) + return -1; + + for( i = 0; papszList[i] != NULL; i++ ) + { + if( EQUAL(papszList[i],pszTarget) ) + return i; + } + + return -1; +} + +/************************************************************************/ +/* CSLFindStringCaseSensitive() */ +/************************************************************************/ + +/** + * Find a string within a string list(case sensitive) + * + * Returns the index of the entry in the string list that contains the + * target string. The string in the string list must be a full match for + * the target. + * + * @param papszList the string list to be searched. + * @param pszTarget the string to be searched for. + * + * @return the index of the string within the list or -1 on failure. + * + * @since GDAL 2.0 + */ + +int CSLFindStringCaseSensitive( char ** papszList, const char * pszTarget ) + +{ + int i; + + if( papszList == NULL ) + return -1; + + for( i = 0; papszList[i] != NULL; i++ ) + { + if( strcmp(papszList[i],pszTarget) == 0 ) + return i; + } + + return -1; +} + +/************************************************************************/ +/* CSLPartialFindString() */ +/************************************************************************/ + +/** + * Find a substring within a string list. + * + * Returns the index of the entry in the string list that contains the + * target string as a substring. The search is case sensitive (unlike + * CSLFindString()). + * + * @param papszHaystack the string list to be searched. + * @param pszNeedle the substring to be searched for. + * + * @return the index of the string within the list or -1 on failure. + */ + +int CSLPartialFindString( char **papszHaystack, const char * pszNeedle ) +{ + int i; + if (papszHaystack == NULL || pszNeedle == NULL) + return -1; + + for (i = 0; papszHaystack[i] != NULL; i++) + { + if (strstr(papszHaystack[i],pszNeedle)) + return i; + } + + return -1; +} + + +/********************************************************************** + * CSLTokenizeString() + * + * Tokenizes a string and returns a StringList with one string for + * each token. + **********************************************************************/ +char **CSLTokenizeString( const char *pszString ) +{ + return CSLTokenizeString2( pszString, " ", CSLT_HONOURSTRINGS ); +} + +/************************************************************************/ +/* CSLTokenizeStringComplex() */ +/* */ +/* Obsolete tokenizing api. */ +/************************************************************************/ + +char ** CSLTokenizeStringComplex( const char * pszString, + const char * pszDelimiters, + int bHonourStrings, int bAllowEmptyTokens ) + +{ + int nFlags = 0; + + if( bHonourStrings ) + nFlags |= CSLT_HONOURSTRINGS; + if( bAllowEmptyTokens ) + nFlags |= CSLT_ALLOWEMPTYTOKENS; + + return CSLTokenizeString2( pszString, pszDelimiters, nFlags ); +} + +/************************************************************************/ +/* CSLTokenizeString2() */ +/************************************************************************/ + +/** + * Tokenize a string. + * + * This function will split a string into tokens based on specified' + * delimeter(s) with a variety of options. The returned result is a + * string list that should be freed with CSLDestroy() when no longer + * needed. + * + * The available parsing options are: + * + * - CSLT_ALLOWEMPTYTOKENS: allow the return of empty tokens when two + * delimiters in a row occur with no other text between them. If not set, + * empty tokens will be discarded; + * - CSLT_STRIPLEADSPACES: strip leading space characters from the token (as + * reported by isspace()); + * - CSLT_STRIPENDSPACES: strip ending space characters from the token (as + * reported by isspace()); + * - CSLT_HONOURSTRINGS: double quotes can be used to hold values that should + * not be broken into multiple tokens; + * - CSLT_PRESERVEQUOTES: string quotes are carried into the tokens when this + * is set, otherwise they are removed; + * - CSLT_PRESERVEESCAPES: if set backslash escapes (for backslash itself, + * and for literal double quotes) will be preserved in the tokens, otherwise + * the backslashes will be removed in processing. + * + * \b Example: + * + * Parse a string into tokens based on various white space (space, newline, + * tab) and then print out results and cleanup. Quotes may be used to hold + * white space in tokens. + +\code + char **papszTokens; + int i; + + papszTokens = + CSLTokenizeString2( pszCommand, " \t\n", + CSLT_HONOURSTRINGS | CSLT_ALLOWEMPTYTOKENS ); + + for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; i++ ) + printf( "arg %d: '%s'", papszTokens[i] ); + CSLDestroy( papszTokens ); +\endcode + + * @param pszString the string to be split into tokens. + * @param pszDelimiters one or more characters to be used as token delimeters. + * @param nCSLTFlags an ORing of one or more of the CSLT_ flag values. + * + * @return a string list of tokens owned by the caller. + */ + +char ** CSLTokenizeString2( const char * pszString, + const char * pszDelimiters, + int nCSLTFlags ) + +{ + if( pszString == NULL ) + return (char **) CPLCalloc(sizeof(char *),1); + CPLStringList oRetList; + char *pszToken; + int nTokenMax, nTokenLen; + int bHonourStrings = (nCSLTFlags & CSLT_HONOURSTRINGS); + int bAllowEmptyTokens = (nCSLTFlags & CSLT_ALLOWEMPTYTOKENS); + int bStripLeadSpaces = (nCSLTFlags & CSLT_STRIPLEADSPACES); + int bStripEndSpaces = (nCSLTFlags & CSLT_STRIPENDSPACES); + + pszToken = (char *) CPLCalloc(10,1); + nTokenMax = 10; + + while( pszString != NULL && *pszString != '\0' ) + { + int bInString = FALSE; + int bStartString = TRUE; + + nTokenLen = 0; + + /* Try to find the next delimeter, marking end of token */ + for( ; *pszString != '\0'; pszString++ ) + { + + /* End if this is a delimeter skip it and break. */ + if( !bInString && strchr(pszDelimiters, *pszString) != NULL ) + { + pszString++; + break; + } + + /* If this is a quote, and we are honouring constant + strings, then process the constant strings, with out delim + but don't copy over the quotes */ + if( bHonourStrings && *pszString == '"' ) + { + if( nCSLTFlags & CSLT_PRESERVEQUOTES ) + { + pszToken[nTokenLen] = *pszString; + nTokenLen++; + } + + if( bInString ) + { + bInString = FALSE; + continue; + } + else + { + bInString = TRUE; + continue; + } + } + + /* + * Within string constants we allow for escaped quotes, but in + * processing them we will unescape the quotes and \\ sequence + * reduces to \ + */ + if( bInString && pszString[0] == '\\' ) + { + if ( pszString[1] == '"' || pszString[1] == '\\' ) + { + if( nCSLTFlags & CSLT_PRESERVEESCAPES ) + { + pszToken[nTokenLen] = *pszString; + nTokenLen++; + } + + pszString++; + } + } + + /* + * Strip spaces at the token start if requested. + */ + if ( !bInString && bStripLeadSpaces + && bStartString && isspace((unsigned char)*pszString) ) + continue; + + bStartString = FALSE; + + /* + * Extend token buffer if we are running close to its end. + */ + if( nTokenLen >= nTokenMax-3 ) + { + nTokenMax = nTokenMax * 2 + 10; + pszToken = (char *) CPLRealloc( pszToken, nTokenMax ); + } + + pszToken[nTokenLen] = *pszString; + nTokenLen++; + } + + /* + * Strip spaces at the token end if requested. + */ + if ( !bInString && bStripEndSpaces ) + { + while ( nTokenLen && isspace((unsigned char)pszToken[nTokenLen - 1]) ) + nTokenLen--; + } + + pszToken[nTokenLen] = '\0'; + + /* + * Add the token. + */ + if( pszToken[0] != '\0' || bAllowEmptyTokens ) + oRetList.AddString( pszToken ); + } + + /* + * If the last token was empty, then we need to capture + * it now, as the loop would skip it. + */ + if( *pszString == '\0' && bAllowEmptyTokens && oRetList.Count() > 0 + && strchr(pszDelimiters,*(pszString-1)) != NULL ) + { + oRetList.AddString( "" ); + } + + CPLFree( pszToken ); + + if( oRetList.List() == NULL ) + { + // we prefer to return empty lists as a pointer to + // a null pointer since some client code might depend on this. + oRetList.Assign( (char**) CPLCalloc(sizeof(char*),1) ); + } + + return oRetList.StealList(); +} + +/********************************************************************** + * CPLSPrintf() + * + * My own version of CPLSPrintf() that works with 10 static buffer. + * + * It returns a ref. to a static buffer that should not be freed and + * is valid only until the next call to CPLSPrintf(). + * + * NOTE: This function should move to cpl_conv.cpp. + **********************************************************************/ +/* For now, assume that a 8000 chars buffer will be enough. + */ +#define CPLSPrintf_BUF_SIZE 8000 +#define CPLSPrintf_BUF_Count 10 + +const char *CPLSPrintf(const char *fmt, ...) +{ + va_list args; + +/* -------------------------------------------------------------------- */ +/* Get the thread local buffer ring data. */ +/* -------------------------------------------------------------------- */ + char *pachBufRingInfo = (char *) CPLGetTLS( CTLS_CPLSPRINTF ); + + if( pachBufRingInfo == NULL ) + { + pachBufRingInfo = (char *) + CPLCalloc(1,sizeof(int)+CPLSPrintf_BUF_Count*CPLSPrintf_BUF_SIZE); + CPLSetTLS( CTLS_CPLSPRINTF, pachBufRingInfo, TRUE ); + } + +/* -------------------------------------------------------------------- */ +/* Work out which string in the "ring" we want to use this */ +/* time. */ +/* -------------------------------------------------------------------- */ + int *pnBufIndex = (int *) pachBufRingInfo; + int nOffset = sizeof(int) + *pnBufIndex * CPLSPrintf_BUF_SIZE; + char *pachBuffer = pachBufRingInfo + nOffset; + + *pnBufIndex = (*pnBufIndex + 1) % CPLSPrintf_BUF_Count; + +/* -------------------------------------------------------------------- */ +/* Format the result. */ +/* -------------------------------------------------------------------- */ + + va_start(args, fmt); + + int ret = CPLvsnprintf(pachBuffer, CPLSPrintf_BUF_SIZE-1, fmt, args); + if( ret < 0 || ret >= CPLSPrintf_BUF_SIZE-1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "CPLSPrintf() called with too big string. Output will be truncated !"); + } + + va_end(args); + + return pachBuffer; +} + +/********************************************************************** + * CSLAppendPrintf() + * + * Use CPLSPrintf() to append a new line at the end of a StringList. + * + * Returns the modified StringList. + **********************************************************************/ +char **CSLAppendPrintf(char **papszStrList, const char *fmt, ...) +{ + CPLString osWork; + va_list args; + + va_start( args, fmt ); + osWork.vPrintf( fmt, args ); + va_end( args ); + + return CSLAddString(papszStrList, osWork); +} + +/************************************************************************/ +/* CPLVASPrintf() */ +/* */ +/* This is intended to serve as an easy to use C callabable */ +/* vasprintf() alternative. Used in the geojson library for */ +/* instance. */ +/************************************************************************/ + +int CPLVASPrintf( char **buf, const char *fmt, va_list ap ) + +{ + CPLString osWork; + + osWork.vPrintf( fmt, ap ); + + if( buf ) + *buf = CPLStrdup(osWork.c_str()); + + return strlen(osWork); +} + +/************************************************************************/ +/* CPLvsnprintf_get_end_of_formatting() */ +/************************************************************************/ + +static const char* CPLvsnprintf_get_end_of_formatting(const char* fmt) +{ + char ch; + /*flag */ + for( ; (ch = *fmt) != '\0'; fmt ++ ) + { + if( ch == '\'' ) + continue; /* bad idea as this is locale specific */ + if( ch == '-' || ch == '+' || ch == ' ' || ch == '#' || ch == '0' ) + continue; + break; + } + + /* field width */ + for( ; (ch = *fmt) != '\0'; fmt ++ ) + { + if( ch == '$' ) + return NULL; /* we don't want to support this */ + if( *fmt >= '0' && *fmt <= '9' ) + continue; + break; + } + + /* precision */ + if( ch == '.' ) + { + fmt ++; + for( ; (ch = *fmt) != '\0'; fmt ++ ) + { + if( ch == '$' ) + return NULL; /* we don't want to support this */ + if( *fmt >= '0' && *fmt <= '9' ) + continue; + break; + } + } + + /* length modifier */ + for( ; (ch = *fmt) != '\0'; fmt ++ ) + { + if( ch == 'h' || ch == 'l' || ch == 'j' || ch == 'z' || + ch == 't' || ch == 'L' ) + continue; + else if( ch == 'I' && fmt[1] == '6' && fmt[2] == '4' ) + fmt += 2; + else + return fmt; + } + + return NULL; +} + +/************************************************************************/ +/* CPLvsnprintf() */ +/************************************************************************/ + +#define call_native_snprintf(type) \ + local_ret = snprintf(str + offset_out, size - offset_out, localfmt, va_arg(wrk_args, type)) + +/** vsnprintf() wrapper that is not sensitive to LC_NUMERIC settings. + * + * This function has the same contract as standard vsnprintf(), except that + * formatting of floating-point numbers will use decimal point, whatever the + * current locale is set. + * + * @param str output buffer + * @param size size of the output buffer (including space for terminating nul) + * @param fmt formatting string + * @param args arguments + * @return the number of characters (excluding terminating nul) that would be written if size is big enough + * @since GDAL 2.0 + */ +int CPLvsnprintf(char *str, size_t size, const char* fmt, va_list args) +{ + const char* fmt_ori = fmt; + size_t offset_out = 0; + va_list wrk_args; + char ch; + int bFormatUnknown = FALSE; + + if( size == 0 ) + return vsnprintf(str, size, fmt, args); + +#ifdef va_copy + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + + for( ; (ch = *fmt) != '\0'; fmt ++ ) + { + if( ch == '%' ) + { + const char* ptrend = CPLvsnprintf_get_end_of_formatting(fmt+1); + if( ptrend == NULL || ptrend - fmt >= 20 ) + { + bFormatUnknown = TRUE; + break; + } + char end = *ptrend; + char end_m1 = ptrend[-1]; + + char localfmt[22]; + memcpy(localfmt, fmt, ptrend - fmt + 1); + localfmt[ptrend-fmt+1] = '\0'; + + int local_ret; + if( end == '%' ) + { + if( offset_out == size-1 ) + break; + local_ret = 1; + str[offset_out] = '%'; + } + else if( end == 'd' || end == 'i' || end == 'c' ) + { + if( end_m1 == 'h' ) + call_native_snprintf(int); + else if( end_m1 == 'l' && ptrend[-2] != 'l' ) + call_native_snprintf(long); + else if( end_m1 == 'l' && ptrend[-2] == 'l' ) + call_native_snprintf(GIntBig); + else if( end_m1 == '4' && ptrend[-2] == '6' && ptrend[-3] == 'I' ) /* Microsoft I64 modifier */ + call_native_snprintf(GIntBig); + else if( end_m1 == 'z') + call_native_snprintf(size_t); + else if( (end_m1 >= 'a' && end_m1 <= 'z') || + (end_m1 >= 'A' && end_m1 <= 'Z') ) + { + bFormatUnknown = TRUE; + break; + } + else + call_native_snprintf(int); + } + else if( end == 'o' || end == 'u' || end == 'x' || end == 'X' ) + { + if( end_m1 == 'h' ) + call_native_snprintf(unsigned int); + else if( end_m1 == 'l' && ptrend[-2] != 'l' ) + call_native_snprintf(unsigned long); + else if( end_m1 == 'l' && ptrend[-2] == 'l' ) + call_native_snprintf(GUIntBig); + else if( end_m1 == '4' && ptrend[-2] == '6' && ptrend[-3] == 'I' ) /* Microsoft I64 modifier */ + call_native_snprintf(GUIntBig); + else if( end_m1 == 'z') + call_native_snprintf(size_t); + else if( (end_m1 >= 'a' && end_m1 <= 'z') || + (end_m1 >= 'A' && end_m1 <= 'Z') ) + { + bFormatUnknown = TRUE; + break; + } + else + call_native_snprintf(unsigned int); + } + else if( end == 'e' || end == 'E' || + end == 'f' || end == 'F' || + end == 'g' || end == 'G' || + end == 'a' || end == 'A' ) + { + if( end_m1 == 'L' ) + call_native_snprintf(long double); + else + call_native_snprintf(double); + /* MSVC vsnprintf() returns -1... */ + if( local_ret < 0 || offset_out + local_ret >= size ) + break; + for( int j = 0; j < local_ret; j ++ ) + { + if( str[offset_out + j] == ',' ) + { + str[offset_out + j] = '.'; + break; + } + } + } + else if( end == 's' ) + { + call_native_snprintf(char*); + } + else if( end == 'p' ) + { + call_native_snprintf(void*); + } + else + { + bFormatUnknown = TRUE; + break; + } + /* MSVC vsnprintf() returns -1... */ + if( local_ret < 0 || offset_out + local_ret >= size ) + break; + offset_out += local_ret; + fmt = ptrend; + } + else + { + if( offset_out == size-1 ) + break; + str[offset_out++] = *fmt; + } + } + if( ch == '\0' && offset_out < size ) + str[offset_out] = '\0'; + else + { + if( bFormatUnknown ) + { + CPLDebug("CPL", "CPLvsnprintf() called with unsupported formatting string: %s", fmt_ori); + } +#ifdef va_copy + va_end( wrk_args ); + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif +#if defined(HAVE_VSNPRINTF) + offset_out = vsnprintf(str, size, fmt_ori, wrk_args); +#else + offset_out = vsprintf(str, fmt_ori, wrk_args); +#endif + } + +#ifdef va_copy + va_end( wrk_args ); +#endif + + return (int)offset_out; +} + +/************************************************************************/ +/* CPLsnprintf() */ +/************************************************************************/ + +/** snprintf() wrapper that is not sensitive to LC_NUMERIC settings. + * + * This function has the same contract as standard snprintf(), except that + * formatting of floating-point numbers will use decimal point, whatever the + * current locale is set. + * + * @param str output buffer + * @param size size of the output buffer (including space for terminating nul) + * @param fmt formatting string + * @param ... arguments + * @return the number of characters (excluding terminating nul) that would be written if size is big enough + * @since GDAL 2.0 + */ +int CPLsnprintf(char *str, size_t size, const char* fmt, ...) +{ + int ret; + va_list args; + + va_start( args, fmt ); + ret = CPLvsnprintf( str, size, fmt, args ); + va_end( args ); + return ret; +} + +/************************************************************************/ +/* CPLsprintf() */ +/************************************************************************/ + +/** sprintf() wrapper that is not sensitive to LC_NUMERIC settings. + * + * This function has the same contract as standard sprintf(), except that + * formatting of floating-point numbers will use decimal point, whatever the + * current locale is set. + * + * @param str output buffer (must be large enough to hold the result) + * @param fmt formatting string + * @param ... arguments + * @return the number of characters (excluding terminating nul) written in output buffer. + * @since GDAL 2.0 + */ +int CPLsprintf(char *str, const char* fmt, ...) +{ + int ret; + va_list args; + + va_start( args, fmt ); + ret = CPLvsnprintf( str, INT_MAX, fmt, args ); + va_end( args ); + return ret; +} + +/************************************************************************/ +/* CPLprintf() */ +/************************************************************************/ + +/** printf() wrapper that is not sensitive to LC_NUMERIC settings. + * + * This function has the same contract as standard printf(), except that + * formatting of floating-point numbers will use decimal point, whatever the + * current locale is set. + * + * @param fmt formatting string + * @param ... arguments + * @return the number of characters (excluding terminating nul) written in output buffer. + * @since GDAL 2.0 + */ +int CPLprintf(const char* fmt, ...) +{ + char szBuffer[4096]; + int ret; + va_list wrk_args, args; + + va_start( args, fmt ); + +#ifdef va_copy + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + + ret = CPLvsnprintf( szBuffer, sizeof(szBuffer), fmt, wrk_args ); + + if( ret < (int)sizeof(szBuffer)-1 ) + ret = printf("%s", szBuffer); + else + { +#ifdef va_copy + va_end( wrk_args ); + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + ret = vfprintf(stdout, fmt, wrk_args); + } + + va_end( wrk_args ); + + return ret; +} + +/************************************************************************/ +/* CPLsscanf() */ +/************************************************************************/ + +/** sscanf() wrapper that is not sensitive to LC_NUMERIC settings. + * + * This function has the same contract as standard sscanf(), except that + * formatting of floating-point numbers will use decimal point, whatever the + * current locale is set. + * + * CAUTION: only works with a very limited number of formatting strings, + * consisting only of "%lf" and regular characters. + * + * param str input string + * @param fmt formatting string + * @param ... arguments + * @return the number of matched patterns; + * @since GDAL 2.0 + */ +int CPL_DLL CPLsscanf(const char* str, const char* fmt, ...) +{ + int error = FALSE; + int ret = 0; + const char* fmt_ori = fmt; + va_list args; + + va_start( args, fmt ); + for( ; *fmt != '\0' && *str != '\0'; fmt++ ) + { + if( *fmt == '%' ) + { + if( fmt[1] == 'l' && fmt[2] == 'f' ) + { + fmt += 2; + char* end; + *(va_arg(args, double*)) = CPLStrtod(str, &end); + if( end > str ) + { + ret ++; + str = end; + } + else + break; + } + else + { + error = TRUE; + break; + } + } + else if( *str != *fmt ) + break; + else + str ++; + } + va_end( args ); + + if( error ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Format %s not supported by CPLsscanf()", + fmt_ori); + } + + return ret; +} + +/************************************************************************/ +/* CSLTestBoolean() */ +/************************************************************************/ + +/** + * Test what boolean value contained in the string. + * + * If pszValue is "NO", "FALSE", "OFF" or "0" will be returned FALSE. + * Otherwise, TRUE will be returned. + * + * @param pszValue the string should be tested. + * + * @return TRUE or FALSE. + */ + +int CSLTestBoolean( const char *pszValue ) +{ + if( EQUAL(pszValue,"NO") + || EQUAL(pszValue,"FALSE") + || EQUAL(pszValue,"OFF") + || EQUAL(pszValue,"0") ) + return FALSE; + else + return TRUE; +} + +/********************************************************************** + * CSLFetchBoolean() + * + * Check for boolean key value. + * + * In a StringList of "Name=Value" pairs, look to see if there is a key + * with the given name, and if it can be interpreted as being TRUE. If + * the key appears without any "=Value" portion it will be considered true. + * If the value is NO, FALSE or 0 it will be considered FALSE otherwise + * if the key appears in the list it will be considered TRUE. If the key + * doesn't appear at all, the indicated default value will be returned. + * + * @param papszStrList the string list to search. + * @param pszKey the key value to look for (case insensitive). + * @param bDefault the value to return if the key isn't found at all. + * + * @return TRUE or FALSE + **********************************************************************/ + +int CSLFetchBoolean( char **papszStrList, const char *pszKey, int bDefault ) + +{ + const char *pszValue; + + if( CSLFindString( papszStrList, pszKey ) != -1 ) + return TRUE; + + pszValue = CSLFetchNameValue(papszStrList, pszKey ); + if( pszValue == NULL ) + return bDefault; + else + return CSLTestBoolean( pszValue ); +} + +/************************************************************************/ +/* CSLFetchNameValueDefaulted() */ +/************************************************************************/ + +const char *CSLFetchNameValueDef( char **papszStrList, + const char *pszName, + const char *pszDefault ) + +{ + const char *pszResult = CSLFetchNameValue( papszStrList, pszName ); + if( pszResult ) + return pszResult; + else + return pszDefault; +} + +/********************************************************************** + * CSLFetchNameValue() + * + * In a StringList of "Name=Value" pairs, look for the + * first value associated with the specified name. The search is not + * case sensitive. + * ("Name:Value" pairs are also supported for backward compatibility + * with older stuff.) + * + * Returns a reference to the value in the StringList that the caller + * should not attempt to free. + * + * Returns NULL if the name is not found. + **********************************************************************/ +const char *CSLFetchNameValue(char **papszStrList, const char *pszName) +{ + size_t nLen; + + if (papszStrList == NULL || pszName == NULL) + return NULL; + + nLen = strlen(pszName); + while(*papszStrList != NULL) + { + if (EQUALN(*papszStrList, pszName, nLen) + && ( (*papszStrList)[nLen] == '=' || + (*papszStrList)[nLen] == ':' ) ) + { + return (*papszStrList)+nLen+1; + } + papszStrList++; + } + return NULL; +} + +/************************************************************************/ +/* CSLFindName() */ +/************************************************************************/ + +/** + * Find StringList entry with given key name. + * + * @param papszStrList the string list to search. + * @param pszName the key value to look for (case insensitive). + * + * @return -1 on failure or the list index of the first occurance + * matching the given key. + */ + +int CSLFindName(char **papszStrList, const char *pszName) +{ + size_t nLen; + int iIndex = 0; + + if (papszStrList == NULL || pszName == NULL) + return -1; + + nLen = strlen(pszName); + while(*papszStrList != NULL) + { + if (EQUALN(*papszStrList, pszName, nLen) + && ( (*papszStrList)[nLen] == '=' || + (*papszStrList)[nLen] == ':' ) ) + { + return iIndex; + } + iIndex++; + papszStrList++; + } + return -1; +} + +/********************************************************************** + * CPLParseNameValue() + **********************************************************************/ + +/** + * Parse NAME=VALUE string into name and value components. + * + * Note that if ppszKey is non-NULL, the key (or name) portion will be + * allocated using VSIMalloc(), and returned in that pointer. It is the + * applications responsibility to free this string, but the application should + * not modify or free the returned value portion. + * + * This function also support "NAME:VALUE" strings and will strip white + * space from around the delimeter when forming name and value strings. + * + * Eventually CSLFetchNameValue() and friends may be modified to use + * CPLParseNameValue(). + * + * @param pszNameValue string in "NAME=VALUE" format. + * @param ppszKey optional pointer though which to return the name + * portion. + * @return the value portion (pointing into original string). + */ + +const char *CPLParseNameValue(const char *pszNameValue, char **ppszKey ) + +{ + int i; + const char *pszValue; + + for( i = 0; pszNameValue[i] != '\0'; i++ ) + { + if( pszNameValue[i] == '=' || pszNameValue[i] == ':' ) + { + pszValue = pszNameValue + i + 1; + while( *pszValue == ' ' || *pszValue == '\t' ) + pszValue++; + + if( ppszKey != NULL ) + { + *ppszKey = (char *) CPLMalloc(i+1); + strncpy( *ppszKey, pszNameValue, i ); + (*ppszKey)[i] = '\0'; + while( i > 0 && + ( (*ppszKey)[i] == ' ' || (*ppszKey)[i] == '\t') ) + { + (*ppszKey)[i] = '\0'; + i--; + } + } + + return pszValue; + } + } + + return NULL; +} + +/********************************************************************** + * CSLFetchNameValueMultiple() + * + * In a StringList of "Name=Value" pairs, look for all the + * values with the specified name. The search is not case + * sensitive. + * ("Name:Value" pairs are also supported for backward compatibility + * with older stuff.) + * + * Returns stringlist with one entry for each occurence of the + * specified name. The stringlist should eventually be destroyed + * by calling CSLDestroy(). + * + * Returns NULL if the name is not found. + **********************************************************************/ +char **CSLFetchNameValueMultiple(char **papszStrList, const char *pszName) +{ + size_t nLen; + char **papszValues = NULL; + + if (papszStrList == NULL || pszName == NULL) + return NULL; + + nLen = strlen(pszName); + while(*papszStrList != NULL) + { + if (EQUALN(*papszStrList, pszName, nLen) + && ( (*papszStrList)[nLen] == '=' || + (*papszStrList)[nLen] == ':' ) ) + { + papszValues = CSLAddString(papszValues, + (*papszStrList)+nLen+1); + } + papszStrList++; + } + + return papszValues; +} + + +/********************************************************************** + * CSLAddNameValue() + * + * Add a new entry to a StringList of "Name=Value" pairs, + * ("Name:Value" pairs are also supported for backward compatibility + * with older stuff.) + * + * This function does not check if a "Name=Value" pair already exists + * for that name and can generate multiple entryes for the same name. + * Use CSLSetNameValue() if you want each name to have only one value. + * + * Returns the modified stringlist. + **********************************************************************/ +char **CSLAddNameValue(char **papszStrList, + const char *pszName, const char *pszValue) +{ + char *pszLine; + + if (pszName == NULL || pszValue==NULL) + return papszStrList; + + pszLine = (char *) CPLMalloc(strlen(pszName)+strlen(pszValue)+2); + CPLsprintf( pszLine, "%s=%s", pszName, pszValue ); + papszStrList = CSLAddString(papszStrList, pszLine); + CPLFree( pszLine ); + + return papszStrList; +} + +/************************************************************************/ +/* CSLSetNameValue() */ +/************************************************************************/ + +/** + * Assign value to name in StringList. + * + * Set the value for a given name in a StringList of "Name=Value" pairs + * ("Name:Value" pairs are also supported for backward compatibility + * with older stuff.) + * + * If there is already a value for that name in the list then the value + * is changed, otherwise a new "Name=Value" pair is added. + * + * @param papszList the original list, the modified version is returned. + * @param pszName the name to be assigned a value. This should be a well + * formed token (no spaces or very special characters). + * @param pszValue the value to assign to the name. This should not contain + * any newlines (CR or LF) but is otherwise pretty much unconstrained. If + * NULL any corresponding value will be removed. + * + * @return modified stringlist. + */ + +char **CSLSetNameValue(char **papszList, + const char *pszName, const char *pszValue) +{ + char **papszPtr; + size_t nLen; + + if (pszName == NULL ) + return papszList; + + nLen = strlen(pszName); + papszPtr = papszList; + while(papszPtr && *papszPtr != NULL) + { + if (EQUALN(*papszPtr, pszName, nLen) + && ( (*papszPtr)[nLen] == '=' || + (*papszPtr)[nLen] == ':' ) ) + { + /* Found it! + * Change the value... make sure to keep the ':' or '=' + */ + char cSep; + cSep = (*papszPtr)[nLen]; + + CPLFree(*papszPtr); + + /* + * If the value is NULL, remove this entry completely/ + */ + if( pszValue == NULL ) + { + while( papszPtr[1] != NULL ) + { + *papszPtr = papszPtr[1]; + papszPtr++; + } + *papszPtr = NULL; + } + + /* + * Otherwise replace with new value. + */ + else + { + *papszPtr = (char *) CPLMalloc(strlen(pszName)+strlen(pszValue)+2); + CPLsprintf( *papszPtr, "%s%c%s", pszName, cSep, pszValue ); + } + return papszList; + } + papszPtr++; + } + + if( pszValue == NULL ) + return papszList; + + /* The name does not exist yet... create a new entry + */ + return CSLAddNameValue(papszList, pszName, pszValue); +} + +/************************************************************************/ +/* CSLSetNameValueSeparator() */ +/************************************************************************/ + +/** + * Replace the default separator (":" or "=") with the passed separator + * in the given name/value list. + * + * Note that if a separator other than ":" or "=" is used, the resulting + * list will not be manipulatable by the CSL name/value functions any more. + * + * The CPLParseNameValue() function is used to break the existing lines, + * and it also strips white space from around the existing delimiter, thus + * the old separator, and any white space will be replaced by the new + * separator. For formatting purposes it may be desireable to include some + * white space in the new separator. eg. ": " or " = ". + * + * @param papszList the list to update. Component strings may be freed + * but the list array will remain at the same location. + * + * @param pszSeparator the new separator string to insert. + * + */ + +void CSLSetNameValueSeparator( char ** papszList, const char *pszSeparator ) + +{ + int nLines = CSLCount(papszList), iLine; + + for( iLine = 0; iLine < nLines; iLine++ ) + { + char *pszKey = NULL; + const char *pszValue; + char *pszNewLine; + + pszValue = CPLParseNameValue( papszList[iLine], &pszKey ); + if( pszValue == NULL || pszKey == NULL ) + continue; + + pszNewLine = (char *) CPLMalloc( strlen(pszValue) + strlen(pszKey) + + strlen(pszSeparator) + 1 ); + strcpy( pszNewLine, pszKey ); + strcat( pszNewLine, pszSeparator ); + strcat( pszNewLine, pszValue ); + CPLFree( papszList[iLine] ); + papszList[iLine] = pszNewLine; + CPLFree( pszKey ); + } +} + +/************************************************************************/ +/* CPLEscapeString() */ +/************************************************************************/ + +/** + * Apply escaping to string to preserve special characters. + * + * This function will "escape" a variety of special characters + * to make the string suitable to embed within a string constant + * or to write within a text stream but in a form that can be + * reconstitued to it's original form. The escaping will even preserve + * zero bytes allowing preservation of raw binary data. + * + * CPLES_BackslashQuotable(0): This scheme turns a binary string into + * a form suitable to be placed within double quotes as a string constant. + * The backslash, quote, '\\0' and newline characters are all escaped in + * the usual C style. + * + * CPLES_XML(1): This scheme converts the '<', '>', '"' and '&' characters into + * their XML/HTML equivelent (<, >, " and &) making a string safe + * to embed as CDATA within an XML element. The '\\0' is not escaped and + * should not be included in the input. + * + * CPLES_URL(2): Everything except alphanumerics and the underscore are + * converted to a percent followed by a two digit hex encoding of the character + * (leading zero supplied if needed). This is the mechanism used for encoding + * values to be passed in URLs. + * + * CPLES_SQL(3): All single quotes are replaced with two single quotes. + * Suitable for use when constructing literal values for SQL commands where + * the literal will be enclosed in single quotes. + * + * CPLES_CSV(4): If the values contains commas, semicolons, tabs, double quotes, or newlines it + * placed in double quotes, and double quotes in the value are doubled. + * Suitable for use when constructing field values for .csv files. Note that + * CPLUnescapeString() currently does not support this format, only + * CPLEscapeString(). See cpl_csv.cpp for csv parsing support. + * + * @param pszInput the string to escape. + * @param nLength The number of bytes of data to preserve. If this is -1 + * the strlen(pszString) function will be used to compute the length. + * @param nScheme the encoding scheme to use. + * + * @return an escaped, zero terminated string that should be freed with + * CPLFree() when no longer needed. + */ + +char *CPLEscapeString( const char *pszInput, int nLength, + int nScheme ) + +{ + char *pszOutput; + char *pszShortOutput; + + if( nLength == -1 ) + nLength = strlen(pszInput); + + pszOutput = (char *) CPLMalloc( nLength * 6 + 1 ); + + if( nScheme == CPLES_BackslashQuotable ) + { + int iOut = 0, iIn; + + for( iIn = 0; iIn < nLength; iIn++ ) + { + if( pszInput[iIn] == '\0' ) + { + pszOutput[iOut++] = '\\'; + pszOutput[iOut++] = '0'; + } + else if( pszInput[iIn] == '\n' ) + { + pszOutput[iOut++] = '\\'; + pszOutput[iOut++] = 'n'; + } + else if( pszInput[iIn] == '"' ) + { + pszOutput[iOut++] = '\\'; + pszOutput[iOut++] = '\"'; + } + else if( pszInput[iIn] == '\\' ) + { + pszOutput[iOut++] = '\\'; + pszOutput[iOut++] = '\\'; + } + else + pszOutput[iOut++] = pszInput[iIn]; + } + pszOutput[iOut] = '\0'; + } + else if( nScheme == CPLES_URL ) /* Untested at implementation */ + { + int iOut = 0, iIn; + + for( iIn = 0; iIn < nLength; iIn++ ) + { + if( (pszInput[iIn] >= 'a' && pszInput[iIn] <= 'z') + || (pszInput[iIn] >= 'A' && pszInput[iIn] <= 'Z') + || (pszInput[iIn] >= '0' && pszInput[iIn] <= '9') + || pszInput[iIn] == '_' || pszInput[iIn] == '.' ) + { + pszOutput[iOut++] = pszInput[iIn]; + } + else + { + CPLsprintf( pszOutput+iOut, "%%%02X", ((unsigned char*)pszInput)[iIn] ); + iOut += 3; + } + } + pszOutput[iOut] = '\0'; + } + else if( nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES ) + { + int iOut = 0, iIn; + + for( iIn = 0; iIn < nLength; iIn++ ) + { + if( pszInput[iIn] == '<' ) + { + pszOutput[iOut++] = '&'; + pszOutput[iOut++] = 'l'; + pszOutput[iOut++] = 't'; + pszOutput[iOut++] = ';'; + } + else if( pszInput[iIn] == '>' ) + { + pszOutput[iOut++] = '&'; + pszOutput[iOut++] = 'g'; + pszOutput[iOut++] = 't'; + pszOutput[iOut++] = ';'; + } + else if( pszInput[iIn] == '&' ) + { + pszOutput[iOut++] = '&'; + pszOutput[iOut++] = 'a'; + pszOutput[iOut++] = 'm'; + pszOutput[iOut++] = 'p'; + pszOutput[iOut++] = ';'; + } + else if( pszInput[iIn] == '"' && nScheme != CPLES_XML_BUT_QUOTES ) + { + pszOutput[iOut++] = '&'; + pszOutput[iOut++] = 'q'; + pszOutput[iOut++] = 'u'; + pszOutput[iOut++] = 'o'; + pszOutput[iOut++] = 't'; + pszOutput[iOut++] = ';'; + } + /* Python 2 doesn't like displaying the UTF-8 character corresponding */ + /* to BOM, so escape it */ + else if( ((GByte*)pszInput)[iIn] == 0xEF && + ((GByte*)pszInput)[iIn+1] == 0xBB && + ((GByte*)pszInput)[iIn+2] == 0xBF ) + { + pszOutput[iOut++] = '&'; + pszOutput[iOut++] = '#'; + pszOutput[iOut++] = 'x'; + pszOutput[iOut++] = 'F'; + pszOutput[iOut++] = 'E'; + pszOutput[iOut++] = 'F'; + pszOutput[iOut++] = 'F'; + pszOutput[iOut++] = ';'; + iIn += 2; + } + else if( ((GByte*)pszInput)[iIn] < 0x20 + && pszInput[iIn] != 0x9 + && pszInput[iIn] != 0xA + && pszInput[iIn] != 0xD ) + { + // These control characters are unrepresentable in XML format, + // so we just drop them. #4117 + } + else + pszOutput[iOut++] = pszInput[iIn]; + } + pszOutput[iOut] = '\0'; + } + else if( nScheme == CPLES_SQL ) + { + int iOut = 0, iIn; + + for( iIn = 0; iIn < nLength; iIn++ ) + { + if( pszInput[iIn] == '\'' ) + { + pszOutput[iOut++] = '\''; + pszOutput[iOut++] = '\''; + } + else + pszOutput[iOut++] = pszInput[iIn]; + } + pszOutput[iOut] = '\0'; + } + else if( nScheme == CPLES_CSV ) + { + if( strchr( pszInput, '\"' ) == NULL + && strchr( pszInput, ',') == NULL + && strchr( pszInput, ';') == NULL + && strchr( pszInput, '\t') == NULL + && strchr( pszInput, 10) == NULL + && strchr( pszInput, 13) == NULL ) + { + strcpy( pszOutput, pszInput ); + } + else + { + int iOut = 1, iIn; + + pszOutput[0] = '\"'; + + for( iIn = 0; iIn < nLength; iIn++ ) + { + if( pszInput[iIn] == '\"' ) + { + pszOutput[iOut++] = '\"'; + pszOutput[iOut++] = '\"'; + } + else + pszOutput[iOut++] = pszInput[iIn]; + } + pszOutput[iOut++] = '\"'; + pszOutput[iOut++] = '\0'; + } + } + else + { + pszOutput[0] = '\0'; + CPLError( CE_Failure, CPLE_AppDefined, + "Undefined escaping scheme (%d) in CPLEscapeString()", + nScheme ); + } + + pszShortOutput = CPLStrdup( pszOutput ); + CPLFree( pszOutput ); + + return pszShortOutput; +} + +/************************************************************************/ +/* CPLUnescapeString() */ +/************************************************************************/ + +/** + * Unescape a string. + * + * This function does the opposite of CPLEscapeString(). Given a string + * with special values escaped according to some scheme, it will return a + * new copy of the string returned to it's original form. + * + * @param pszInput the input string. This is a zero terminated string. + * @param pnLength location to return the length of the unescaped string, + * which may in some cases include embedded '\\0' characters. + * @param nScheme the escaped scheme to undo (see CPLEscapeString() for a + * list). + * + * @return a copy of the unescaped string that should be freed by the + * application using CPLFree() when no longer needed. + */ + +char *CPLUnescapeString( const char *pszInput, int *pnLength, int nScheme ) + +{ + char *pszOutput; + int iOut=0, iIn; + + pszOutput = (char *) CPLMalloc(4 * strlen(pszInput)+1); + pszOutput[0] = '\0'; + + if( nScheme == CPLES_XML || nScheme == CPLES_XML_BUT_QUOTES ) + { + char ch; + for( iIn = 0; (ch = pszInput[iIn]) != '\0'; iIn++ ) + { + if( ch != '&' ) + { + pszOutput[iOut++] = ch; + } + else if( EQUALN(pszInput+iIn,"<",4) ) + { + pszOutput[iOut++] = '<'; + iIn += 3; + } + else if( EQUALN(pszInput+iIn,">",4) ) + { + pszOutput[iOut++] = '>'; + iIn += 3; + } + else if( EQUALN(pszInput+iIn,"&",5) ) + { + pszOutput[iOut++] = '&'; + iIn += 4; + } + else if( EQUALN(pszInput+iIn,"'",6) ) + { + pszOutput[iOut++] = '\''; + iIn += 5; + } + else if( EQUALN(pszInput+iIn,""",6) ) + { + pszOutput[iOut++] = '"'; + iIn += 5; + } + else if( EQUALN(pszInput+iIn,"&#x",3) ) + { + wchar_t anVal[2] = {0 , 0}; + iIn += 3; + + while(TRUE) + { + ch = pszInput[iIn ++]; + if (ch >= 'a' && ch <= 'f') + anVal[0] = anVal[0] * 16 + ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + anVal[0] = anVal[0] * 16 + ch - 'A' + 10; + else if (ch >= '0' && ch <= '9') + anVal[0] = anVal[0] * 16 + ch - '0'; + else + break; + } + if (ch != ';') + break; + iIn --; + + char * pszUTF8 = CPLRecodeFromWChar( anVal, "WCHAR_T", CPL_ENC_UTF8); + int nLen = strlen(pszUTF8); + memcpy(pszOutput + iOut, pszUTF8, nLen); + CPLFree(pszUTF8); + iOut += nLen; + } + else if( EQUALN(pszInput+iIn,"&#",2) ) + { + char ch; + wchar_t anVal[2] = {0 , 0}; + iIn += 2; + + while(TRUE) + { + ch = pszInput[iIn ++]; + if (ch >= '0' && ch <= '9') + anVal[0] = anVal[0] * 10 + ch - '0'; + else + break; + } + if (ch != ';') + break; + iIn --; + + char * pszUTF8 = CPLRecodeFromWChar( anVal, "WCHAR_T", CPL_ENC_UTF8); + int nLen = strlen(pszUTF8); + memcpy(pszOutput + iOut, pszUTF8, nLen); + CPLFree(pszUTF8); + iOut += nLen; + } + else + { + /* illegal escape sequence */ + CPLDebug( "CPL", + "Error unescaping CPLES_XML text, '&' character followed by unhandled escape sequence." ); + break; + } + } + } + else if( nScheme == CPLES_URL ) + { + for( iIn = 0; pszInput[iIn] != '\0'; iIn++ ) + { + if( pszInput[iIn] == '%' + && pszInput[iIn+1] != '\0' + && pszInput[iIn+2] != '\0' ) + { + int nHexChar = 0; + + if( pszInput[iIn+1] >= 'A' && pszInput[iIn+1] <= 'F' ) + nHexChar += 16 * (pszInput[iIn+1] - 'A' + 10); + else if( pszInput[iIn+1] >= 'a' && pszInput[iIn+1] <= 'f' ) + nHexChar += 16 * (pszInput[iIn+1] - 'a' + 10); + else if( pszInput[iIn+1] >= '0' && pszInput[iIn+1] <= '9' ) + nHexChar += 16 * (pszInput[iIn+1] - '0'); + else + CPLDebug( "CPL", + "Error unescaping CPLES_URL text, percent not " + "followed by two hex digits." ); + + if( pszInput[iIn+2] >= 'A' && pszInput[iIn+2] <= 'F' ) + nHexChar += pszInput[iIn+2] - 'A' + 10; + else if( pszInput[iIn+2] >= 'a' && pszInput[iIn+2] <= 'f' ) + nHexChar += pszInput[iIn+2] - 'a' + 10; + else if( pszInput[iIn+2] >= '0' && pszInput[iIn+2] <= '9' ) + nHexChar += pszInput[iIn+2] - '0'; + else + CPLDebug( "CPL", + "Error unescaping CPLES_URL text, percent not " + "followed by two hex digits." ); + + pszOutput[iOut++] = (char) nHexChar; + iIn += 2; + } + else if( pszInput[iIn] == '+' ) + { + pszOutput[iOut++] = ' '; + } + else + { + pszOutput[iOut++] = pszInput[iIn]; + } + } + } + else if( nScheme == CPLES_SQL ) + { + for( iIn = 0; pszInput[iIn] != '\0'; iIn++ ) + { + if( pszInput[iIn] == '\'' && pszInput[iIn+1] == '\'' ) + { + iIn++; + pszOutput[iOut++] = pszInput[iIn]; + } + else + { + pszOutput[iOut++] = pszInput[iIn]; + } + } + } + else /* if( nScheme == CPLES_BackslashQuoteable ) */ + { + for( iIn = 0; pszInput[iIn] != '\0'; iIn++ ) + { + if( pszInput[iIn] == '\\' ) + { + iIn++; + if( pszInput[iIn] == 'n' ) + pszOutput[iOut++] = '\n'; + else if( pszInput[iIn] == '0' ) + pszOutput[iOut++] = '\0'; + else + pszOutput[iOut++] = pszInput[iIn]; + } + else + { + pszOutput[iOut++] = pszInput[iIn]; + } + } + } + + pszOutput[iOut] = '\0'; + + if( pnLength != NULL ) + *pnLength = iOut; + + return pszOutput; +} + +/************************************************************************/ +/* CPLBinaryToHex() */ +/************************************************************************/ + +/** + * Binary to hexadecimal translation. + * + * @param nBytes number of bytes of binary data in pabyData. + * @param pabyData array of data bytes to translate. + * + * @return hexadecimal translation, zero terminated. Free with CPLFree(). + */ + +char *CPLBinaryToHex( int nBytes, const GByte *pabyData ) + +{ + char *pszHex = (char *) CPLMalloc(nBytes * 2 + 1 ); + int i; + static const char achHex[] = "0123456789ABCDEF"; + + pszHex[nBytes*2] = '\0'; + + for( i = 0; i < nBytes; i++ ) + { + int nLow = pabyData[i] & 0x0f; + int nHigh = (pabyData[i] & 0xf0) >> 4; + + pszHex[i*2] = achHex[nHigh]; + pszHex[i*2+1] = achHex[nLow]; + } + + return pszHex; +} + +/************************************************************************/ +/* CPLHexToBinary() */ +/************************************************************************/ + +/** + * Hexadecimal to binary translation + * + * @param pszHex the input hex encoded string. + * @param pnBytes the returned count of decoded bytes placed here. + * + * @return returns binary buffer of data - free with CPLFree(). + */ + +static const unsigned char hex2char[256] = { + /* not Hex characters */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + /* 0-9 */ + 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, + /* A-F */ + 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0, + /* not Hex characters */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + /* a-f */ + 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + /* not Hex characters (upper 128 characters) */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +GByte *CPLHexToBinary( const char *pszHex, int *pnBytes ) +{ + size_t nHexLen = strlen(pszHex); + size_t i; + register unsigned char h1, h2; + GByte *pabyWKB; + + pabyWKB = (GByte *) CPLMalloc(nHexLen / 2 + 2); + + for( i = 0; i < nHexLen/2; i++ ) + { + h1 = hex2char[(int)pszHex[2*i]]; + h2 = hex2char[(int)pszHex[2*i+1]]; + + /* First character is high bits, second is low bits */ + pabyWKB[i] = (GByte)((h1 << 4) | h2); + } + pabyWKB[nHexLen/2] = 0; + *pnBytes = nHexLen/2; + + return pabyWKB; + +} + +/************************************************************************/ +/* CPLGetValueType() */ +/************************************************************************/ + +/** + * Detect the type of the value contained in a string, whether it is + * a real, an integer or a string + * Leading and trailing spaces are skipped in the analysis. + * + * Note: in the context of this function, integer must be understood in a + * broad sense. It does not mean that the value can fit into a 32 bit integer + * for example. It might be larger. + * + * @param pszValue the string to analyze + * + * @return returns the type of the value contained in the string. + */ + +CPLValueType CPLGetValueType(const char* pszValue) +{ + /* + doubles : "+25.e+3", "-25.e-3", "25.e3", "25e3", " 25e3 " + not doubles: "25e 3", "25e.3", "-2-5e3", "2-5e3", "25.25.3", "-3d" + */ + + int bFoundDot = FALSE; + int bFoundExponent = FALSE; + int bIsLastCharExponent = FALSE; + int bIsReal = FALSE; + + if (pszValue == NULL) + return CPL_VALUE_STRING; + + /* Skip leading spaces */ + while( isspace( (unsigned char)*pszValue ) ) + pszValue ++; + + if (*pszValue == '\0') + return CPL_VALUE_STRING; + + /* Skip leading + or - */ + if (*pszValue == '+' || *pszValue == '-') + pszValue ++; + + for(; *pszValue != '\0'; pszValue++ ) + { + if( isdigit( *pszValue)) + { + bIsLastCharExponent = FALSE; + /* do nothing */ + } + else if ( isspace ((unsigned char)*pszValue) ) + { + const char* pszTmp = pszValue; + while( isspace( (unsigned char)*pszTmp ) ) + pszTmp ++; + if (*pszTmp == 0) + break; + else + return CPL_VALUE_STRING; + } + else if ( *pszValue == '-' || *pszValue == '+' ) + { + if (bIsLastCharExponent) + { + /* do nothing */ + } + else + return CPL_VALUE_STRING; + bIsLastCharExponent = FALSE; + } + else if ( *pszValue == '.') + { + bIsReal = TRUE; + if (!bFoundDot && bIsLastCharExponent == FALSE) + bFoundDot = TRUE; + else + return CPL_VALUE_STRING; + bIsLastCharExponent = FALSE; + } + else if (*pszValue == 'D' || *pszValue == 'd' + || *pszValue == 'E' || *pszValue == 'e' ) + { + if (!(pszValue[1] == '+' || pszValue[1] == '-' || + isdigit(pszValue[1]))) + return CPL_VALUE_STRING; + + bIsReal = TRUE; + if (!bFoundExponent) + bFoundExponent = TRUE; + else + return CPL_VALUE_STRING; + bIsLastCharExponent = TRUE; + } + else + { + return CPL_VALUE_STRING; + } + } + + return (bIsReal) ? CPL_VALUE_REAL : CPL_VALUE_INTEGER; +} + +/************************************************************************/ +/* CPLStrlcpy() */ +/************************************************************************/ + +/** + * Copy source string to a destination buffer. + * + * This function ensures that the destination buffer is always NUL terminated + * (provided that its length is at least 1). + * + * This function is designed to be a safer, more consistent, and less error + * prone replacement for strncpy. Its contract is identical to libbsd's strlcpy. + * + * Truncation can be detected by testing if the return value of CPLStrlcpy + * is greater or equal to nDestSize. + +\verbatim +char szDest[5]; +if (CPLStrlcpy(szDest, "abcde", sizeof(szDest)) >= sizeof(szDest)) + fprintf(stderr, "truncation occured !\n"); +\endverbatim + + * @param pszDest destination buffer + * @param pszSrc source string. Must be NUL terminated + * @param nDestSize size of destination buffer (including space for the NUL terminator character) + * + * @return the length of the source string (=strlen(pszSrc)) + * + * @since GDAL 1.7.0 + */ +size_t CPLStrlcpy(char* pszDest, const char* pszSrc, size_t nDestSize) +{ + char* pszDestIter = pszDest; + const char* pszSrcIter = pszSrc; + + if (nDestSize == 0) + return strlen(pszSrc); + + nDestSize --; + while(nDestSize != 0 && *pszSrcIter != '\0') + { + *pszDestIter = *pszSrcIter; + pszDestIter ++; + pszSrcIter ++; + nDestSize --; + } + *pszDestIter = '\0'; + return pszSrcIter - pszSrc + strlen(pszSrcIter); +} + +/************************************************************************/ +/* CPLStrlcat() */ +/************************************************************************/ + +/** + * Appends a source string to a destination buffer. + * + * This function ensures that the destination buffer is always NUL terminated + * (provided that its length is at least 1 and that there is at least one byte + * free in pszDest, that is to say strlen(pszDest_before) < nDestSize) + * + * This function is designed to be a safer, more consistent, and less error + * prone replacement for strncat. Its contract is identical to libbsd's strlcat. + * + * Truncation can be detected by testing if the return value of CPLStrlcat + * is greater or equal to nDestSize. + +\verbatim +char szDest[5]; +CPLStrlcpy(szDest, "ab", sizeof(szDest)); +if (CPLStrlcat(szDest, "cde", sizeof(szDest)) >= sizeof(szDest)) + fprintf(stderr, "truncation occured !\n"); +\endverbatim + + * @param pszDest destination buffer. Must be NUL terminated before running CPLStrlcat + * @param pszSrc source string. Must be NUL terminated + * @param nDestSize size of destination buffer (including space for the NUL terminator character) + * + * @return the thoretical length of the destination string after concatenation + * (=strlen(pszDest_before) + strlen(pszSrc)). + * If strlen(pszDest_before) >= nDestSize, then it returns nDestSize + strlen(pszSrc) + * + * @since GDAL 1.7.0 + */ +size_t CPLStrlcat(char* pszDest, const char* pszSrc, size_t nDestSize) +{ + char* pszDestIter = pszDest; + + while(nDestSize != 0 && *pszDestIter != '\0') + { + pszDestIter ++; + nDestSize --; + } + + return pszDestIter - pszDest + CPLStrlcpy(pszDestIter, pszSrc, nDestSize); +} + +/************************************************************************/ +/* CPLStrnlen() */ +/************************************************************************/ + +/** + * Returns the length of a NUL terminated string by reading at most + * the specified number of bytes. + * + * The CPLStrnlen() function returns MIN(strlen(pszStr), nMaxLen). + * Only the first nMaxLen bytes of the string will be read. Useful to + * test if a string contains at least nMaxLen characters without reading + * the full string up to the NUL terminating character. + * + * @param pszStr a NUL terminated string + * @param nMaxLen maximum number of bytes to read in pszStr + * + * @return strlen(pszStr) if the length is lesser than nMaxLen, otherwise + * nMaxLen if the NUL character has not been found in the first nMaxLen bytes. + * + * @since GDAL 1.7.0 + */ + +size_t CPLStrnlen (const char *pszStr, size_t nMaxLen) +{ + size_t nLen = 0; + while(nLen < nMaxLen && *pszStr != '\0') + { + nLen ++; + pszStr ++; + } + return nLen; +} diff --git a/bazaar/plugin/gdal/port/cpl_string.h b/bazaar/plugin/gdal/port/cpl_string.h new file mode 100644 index 000000000..956352faf --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_string.h @@ -0,0 +1,376 @@ +/********************************************************************** + * $Id: cpl_string.h 28025 2014-11-28 00:01:32Z rouault $ + * + * Name: cpl_string.h + * Project: CPL - Common Portability Library + * Purpose: String and StringList functions. + * Author: Daniel Morissette, dmorissette@mapgears.com + * + ********************************************************************** + * Copyright (c) 1998, Daniel Morissette + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_STRING_H_INCLUDED +#define _CPL_STRING_H_INCLUDED + +#include "cpl_vsi.h" +#include "cpl_error.h" +#include "cpl_conv.h" + +/** + * \file cpl_string.h + * + * Various convenience functions for working with strings and string lists. + * + * A StringList is just an array of strings with the last pointer being + * NULL. An empty StringList may be either a NULL pointer, or a pointer to + * a pointer memory location with a NULL value. + * + * A common convention for StringLists is to use them to store name/value + * lists. In this case the contents are treated like a dictionary of + * name/value pairs. The actual data is formatted with each string having + * the format ":" (though "=" is also an acceptable separator). + * A number of the functions in the file operate on name/value style + * string lists (such as CSLSetNameValue(), and CSLFetchNameValue()). + * + * To some extent the CPLStringList C++ class can be used to abstract + * managing string lists a bit but still be able to return them from C + * functions. + * + */ + +CPL_C_START + +char CPL_DLL **CSLAddString(char **papszStrList, const char *pszNewString) CPL_WARN_UNUSED_RESULT; +int CPL_DLL CSLCount(char **papszStrList); +const char CPL_DLL *CSLGetField( char **, int ); +void CPL_DLL CPL_STDCALL CSLDestroy(char **papszStrList); +char CPL_DLL **CSLDuplicate(char **papszStrList) CPL_WARN_UNUSED_RESULT; +char CPL_DLL **CSLMerge( char **papszOrig, char **papszOverride ) CPL_WARN_UNUSED_RESULT; + +char CPL_DLL **CSLTokenizeString(const char *pszString ) CPL_WARN_UNUSED_RESULT; +char CPL_DLL **CSLTokenizeStringComplex(const char *pszString, + const char *pszDelimiter, + int bHonourStrings, int bAllowEmptyTokens ) CPL_WARN_UNUSED_RESULT; +char CPL_DLL **CSLTokenizeString2( const char *pszString, + const char *pszDelimeter, + int nCSLTFlags ) CPL_WARN_UNUSED_RESULT; + +#define CSLT_HONOURSTRINGS 0x0001 +#define CSLT_ALLOWEMPTYTOKENS 0x0002 +#define CSLT_PRESERVEQUOTES 0x0004 +#define CSLT_PRESERVEESCAPES 0x0008 +#define CSLT_STRIPLEADSPACES 0x0010 +#define CSLT_STRIPENDSPACES 0x0020 + +int CPL_DLL CSLPrint(char **papszStrList, FILE *fpOut); +char CPL_DLL **CSLLoad(const char *pszFname) CPL_WARN_UNUSED_RESULT; +char CPL_DLL **CSLLoad2(const char *pszFname, int nMaxLines, int nMaxCols, char** papszOptions) CPL_WARN_UNUSED_RESULT; +int CPL_DLL CSLSave(char **papszStrList, const char *pszFname); + +char CPL_DLL **CSLInsertStrings(char **papszStrList, int nInsertAtLineNo, + char **papszNewLines) CPL_WARN_UNUSED_RESULT; +char CPL_DLL **CSLInsertString(char **papszStrList, int nInsertAtLineNo, + const char *pszNewLine) CPL_WARN_UNUSED_RESULT; +char CPL_DLL **CSLRemoveStrings(char **papszStrList, int nFirstLineToDelete, + int nNumToRemove, char ***ppapszRetStrings) CPL_WARN_UNUSED_RESULT; +int CPL_DLL CSLFindString( char **, const char * ); +int CPL_DLL CSLFindStringCaseSensitive( char **, const char * ); +int CPL_DLL CSLPartialFindString( char **papszHaystack, + const char * pszNeedle ); +int CPL_DLL CSLFindName(char **papszStrList, const char *pszName); +int CPL_DLL CSLTestBoolean( const char *pszValue ); +int CPL_DLL CSLFetchBoolean( char **papszStrList, const char *pszKey, + int bDefault ); + +const char CPL_DLL * + CPLParseNameValue(const char *pszNameValue, char **ppszKey ); +const char CPL_DLL * + CSLFetchNameValue(char **papszStrList, const char *pszName); +const char CPL_DLL * + CSLFetchNameValueDef(char **papszStrList, const char *pszName, + const char *pszDefault ); +char CPL_DLL ** + CSLFetchNameValueMultiple(char **papszStrList, const char *pszName); +char CPL_DLL ** + CSLAddNameValue(char **papszStrList, + const char *pszName, const char *pszValue) CPL_WARN_UNUSED_RESULT; +char CPL_DLL ** + CSLSetNameValue(char **papszStrList, + const char *pszName, const char *pszValue) CPL_WARN_UNUSED_RESULT; +void CPL_DLL CSLSetNameValueSeparator( char ** papszStrList, + const char *pszSeparator ); + +#define CPLES_BackslashQuotable 0 +#define CPLES_XML 1 +#define CPLES_URL 2 +#define CPLES_SQL 3 +#define CPLES_CSV 4 +#define CPLES_XML_BUT_QUOTES 5 + +char CPL_DLL *CPLEscapeString( const char *pszString, int nLength, + int nScheme ) CPL_WARN_UNUSED_RESULT; +char CPL_DLL *CPLUnescapeString( const char *pszString, int *pnLength, + int nScheme ) CPL_WARN_UNUSED_RESULT; + +char CPL_DLL *CPLBinaryToHex( int nBytes, const GByte *pabyData ) CPL_WARN_UNUSED_RESULT; +GByte CPL_DLL *CPLHexToBinary( const char *pszHex, int *pnBytes ) CPL_WARN_UNUSED_RESULT; + +char CPL_DLL *CPLBase64Encode( int nBytes, const GByte *pabyData ) CPL_WARN_UNUSED_RESULT; +int CPL_DLL CPLBase64DecodeInPlace(GByte* pszBase64); + +typedef enum +{ + CPL_VALUE_STRING, + CPL_VALUE_REAL, + CPL_VALUE_INTEGER +} CPLValueType; + +CPLValueType CPL_DLL CPLGetValueType(const char* pszValue); + +size_t CPL_DLL CPLStrlcpy(char* pszDest, const char* pszSrc, size_t nDestSize); +size_t CPL_DLL CPLStrlcat(char* pszDest, const char* pszSrc, size_t nDestSize); +size_t CPL_DLL CPLStrnlen (const char *pszStr, size_t nMaxLen); + +/* -------------------------------------------------------------------- */ +/* Locale independant formatting functions. */ +/* -------------------------------------------------------------------- */ +int CPL_DLL CPLvsnprintf(char *str, size_t size, const char* fmt, va_list args); +int CPL_DLL CPLsnprintf(char *str, size_t size, const char* fmt, ...) CPL_PRINT_FUNC_FORMAT(3,4); +int CPL_DLL CPLsprintf(char *str, const char* fmt, ...) CPL_PRINT_FUNC_FORMAT(2, 3); +int CPL_DLL CPLprintf(const char* fmt, ...) CPL_PRINT_FUNC_FORMAT(1, 2); +int CPL_DLL CPLsscanf(const char* str, const char* fmt, ...); /* caution: only works with limited number of formats */ + +const char CPL_DLL *CPLSPrintf(const char *fmt, ...) CPL_PRINT_FUNC_FORMAT(1, 2); +char CPL_DLL **CSLAppendPrintf(char **papszStrList, const char *fmt, ...) CPL_PRINT_FUNC_FORMAT(2, 3) CPL_WARN_UNUSED_RESULT; +int CPL_DLL CPLVASPrintf(char **buf, const char *fmt, va_list args ); + +/* -------------------------------------------------------------------- */ +/* RFC 23 character set conversion/recoding API (cpl_recode.cpp). */ +/* -------------------------------------------------------------------- */ +#define CPL_ENC_LOCALE "" +#define CPL_ENC_UTF8 "UTF-8" +#define CPL_ENC_UTF16 "UTF-16" +#define CPL_ENC_UCS2 "UCS-2" +#define CPL_ENC_UCS4 "UCS-4" +#define CPL_ENC_ASCII "ASCII" +#define CPL_ENC_ISO8859_1 "ISO-8859-1" + +int CPL_DLL CPLEncodingCharSize( const char *pszEncoding ); +void CPL_DLL CPLClearRecodeWarningFlags( void ); +char CPL_DLL *CPLRecode( const char *pszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) CPL_WARN_UNUSED_RESULT; +char CPL_DLL *CPLRecodeFromWChar( const wchar_t *pwszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) CPL_WARN_UNUSED_RESULT; +wchar_t CPL_DLL *CPLRecodeToWChar( const char *pszSource, + const char *pszSrcEncoding, + const char *pszDstEncoding ) CPL_WARN_UNUSED_RESULT; +int CPL_DLL CPLIsUTF8(const char* pabyData, int nLen); +char CPL_DLL *CPLForceToASCII(const char* pabyData, int nLen, char chReplacementChar) CPL_WARN_UNUSED_RESULT; +int CPL_DLL CPLStrlenUTF8(const char *pszUTF8Str); + +CPL_C_END + +/************************************************************************/ +/* CPLString */ +/************************************************************************/ + +#if defined(__cplusplus) && !defined(CPL_SUPRESS_CPLUSPLUS) + +#include + +/* + * Simple trick to avoid "using" declaration in header for new compilers + * but make it still working with old compilers which throw C2614 errors. + * + * Define MSVC_OLD_STUPID_BEHAVIOUR + * for old compilers: VC++ 5 and 6 as well as eVC++ 3 and 4. + */ + +/* + * Detect old MSVC++ compiler <= 6.0 + * 1200 - VC++ 6.0 + * 1200-1202 - eVC++ 4.0 + */ +#if defined(_MSC_VER) +# if (_MSC_VER <= 1202) +# define MSVC_OLD_STUPID_BEHAVIOUR +# endif +#endif + +/* Avoid C2614 errors */ +#ifdef MSVC_OLD_STUPID_BEHAVIOUR + using std::string; +# define gdal_std_string string +#else +# define gdal_std_string std::string +#endif + +/* Remove annoying warnings in Microsoft eVC++ and Microsoft Visual C++ */ +#if defined(WIN32CE) +# pragma warning(disable:4251 4275 4786) +#endif + +//! Convenient string class based on std::string. +class CPL_DLL CPLString : public gdal_std_string +{ +public: + + + CPLString(void) {} + CPLString( const std::string &oStr ) : gdal_std_string( oStr ) {} + CPLString( const char *pszStr ) : gdal_std_string( pszStr ) {} + + operator const char* (void) const { return c_str(); } + + char& operator[](std::string::size_type i) + { + return gdal_std_string::operator[](i); + } + + const char& operator[](std::string::size_type i) const + { + return gdal_std_string::operator[](i); + } + + char& operator[](int i) + { + return gdal_std_string::operator[](static_cast(i)); + } + + const char& operator[](int i) const + { + return gdal_std_string::operator[](static_cast(i)); + } + + void Clear() { resize(0); } + + // NULL safe assign and free. + void Seize(char *pszValue) + { + if (pszValue == NULL ) + Clear(); + else + { + *this = pszValue; + CPLFree(pszValue); + } + } + + /* There seems to be a bug in the way the compiler count indices... Should be CPL_PRINT_FUNC_FORMAT (1, 2) */ + CPLString &Printf( const char *pszFormat, ... ) CPL_PRINT_FUNC_FORMAT (2, 3); + CPLString &vPrintf( const char *pszFormat, va_list args ); + CPLString &FormatC( double dfValue, const char *pszFormat = NULL ); + CPLString &Trim(); + CPLString &Recode( const char *pszSrcEncoding, const char *pszDstEncoding ); + + /* case insensitive find alternates */ + size_t ifind( const std::string & str, size_t pos = 0 ) const; + size_t ifind( const char * s, size_t pos = 0 ) const; + CPLString &toupper( void ); + CPLString &tolower( void ); +}; + +CPLString CPLOPrintf(const char *pszFormat, ... ) CPL_PRINT_FUNC_FORMAT (1, 2); +CPLString CPLOvPrintf(const char *pszFormat, va_list args); + +/* -------------------------------------------------------------------- */ +/* URL processing functions, here since they depend on CPLString. */ +/* -------------------------------------------------------------------- */ +CPLString CPL_DLL CPLURLGetValue(const char* pszURL, const char* pszKey); +CPLString CPL_DLL CPLURLAddKVP(const char* pszURL, const char* pszKey, + const char* pszValue); + +/************************************************************************/ +/* CPLStringList */ +/************************************************************************/ + +//! String list class designed around our use of C "char**" string lists. +class CPL_DLL CPLStringList +{ + char **papszList; + mutable int nCount; + mutable int nAllocation; + int bOwnList; + int bIsSorted; + + void Initialize(); + void MakeOurOwnCopy(); + void EnsureAllocation( int nMaxLength ); + int FindSortedInsertionPoint( const char *pszLine ); + + public: + CPLStringList(); + CPLStringList( char **papszList, int bTakeOwnership=TRUE ); + CPLStringList( const CPLStringList& oOther ); + ~CPLStringList(); + + CPLStringList &Clear(); + + int size() const { return Count(); } + int Count() const; + + CPLStringList &AddString( const char *pszNewString ); + CPLStringList &AddStringDirectly( char *pszNewString ); + + CPLStringList &InsertString( int nInsertAtLineNo, const char *pszNewLine ) + { return InsertStringDirectly( nInsertAtLineNo, CPLStrdup(pszNewLine) ); } + CPLStringList &InsertStringDirectly( int nInsertAtLineNo, char *pszNewLine); + +// CPLStringList &InsertStrings( int nInsertAtLineNo, char **papszNewLines ); +// CPLStringList &RemoveStrings( int nFirstLineToDelete, int nNumToRemove=1 ); + + int FindString( const char *pszTarget ) const + { return CSLFindString( papszList, pszTarget ); } + int PartialFindString( const char *pszNeedle ) const + { return CSLPartialFindString( papszList, pszNeedle ); } + + int FindName( const char *pszName ) const; + int FetchBoolean( const char *pszKey, int bDefault ) const; + const char *FetchNameValue( const char *pszKey ) const; + const char *FetchNameValueDef( const char *pszKey, const char *pszDefault ) const; + CPLStringList &AddNameValue( const char *pszKey, const char *pszValue ); + CPLStringList &SetNameValue( const char *pszKey, const char *pszValue ); + + CPLStringList &Assign( char **papszListIn, int bTakeOwnership=TRUE ); + CPLStringList &operator=(char **papszListIn) { return Assign( papszListIn, TRUE ); } + CPLStringList &operator=(const CPLStringList& oOther); + + char * operator[](int i); + char * operator[](size_t i) { return (*this)[(int)i]; } + const char * operator[](int i) const; + const char * operator[](size_t i) const { return (*this)[(int)i]; } + + char **List() { return papszList; } + char **StealList(); + + CPLStringList &Sort(); + int IsSorted() const { return bIsSorted; } + + operator char**(void) { return List(); } +}; + +#endif /* def __cplusplus && !CPL_SUPRESS_CPLUSPLUS */ + +#endif /* _CPL_STRING_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_strtod.cpp b/bazaar/plugin/gdal/port/cpl_strtod.cpp new file mode 100644 index 000000000..a077e63fc --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_strtod.cpp @@ -0,0 +1,435 @@ +/****************************************************************************** + * $Id: cpl_strtod.cpp 29252 2015-05-26 10:00:08Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Functions to convert ASCII string to floating point number. + * Author: Andrey Kiselev, dron@ak4719.spb.edu. + * + ****************************************************************************** + * Copyright (c) 2006, Andrey Kiselev + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include +#include +#include + +#include "cpl_conv.h" + +CPL_CVSID("$Id: cpl_strtod.cpp 29252 2015-05-26 10:00:08Z rouault $"); + +// XXX: with GCC 2.95 strtof() function is only available when in c99 mode. +// Fix it here not touching the compiler options. +#if defined(HAVE_STRTOF) && !HAVE_DECL_STRTOF +extern "C" { +extern float strtof(const char *nptr, char **endptr); +} +#endif + +#ifndef NAN +# ifdef HUGE_VAL +# define NAN (HUGE_VAL * 0.0) +# else + +static float CPLNaN(void) +{ + float fNan; + int nNan = 0x7FC00000; + memcpy(&fNan, &nNan, 4); + return fNan; +} + +# define NAN CPLNan() +# endif +#endif + +#ifndef INFINITY + static CPL_INLINE double CPLInfinity(void) + { + static double ZERO = 0; + return 1.0 / ZERO; /* MSVC doesn't like 1.0 / 0.0 */ + } + #define INFINITY CPLInfinity() + static CPL_INLINE double CPLNegInfinity(void) + { + static double ZERO = 0; + return -1.0 / ZERO; /* MSVC doesn't like -1.0 / 0.0 */ + } + #define NEG_INFINITY CPLNegInfinity() +#else + #define NEG_INFINITY (-INFINITY) +#endif + +/************************************************************************/ +/* CPLAtofDelim() */ +/************************************************************************/ + +/** + * Converts ASCII string to floating point number. + * + * This function converts the initial portion of the string pointed to + * by nptr to double floating point representation. The behaviour is the + * same as + * + * CPLStrtodDelim(nptr, (char **)NULL, point); + * + * This function does the same as standard atof(3), but does not take locale + * in account. Instead of locale defined decimal delimiter you can specify + * your own one. Also see notes for CPLAtof() function. + * + * @param nptr Pointer to string to convert. + * @param point Decimal delimiter. + * + * @return Converted value, if any. + */ +double CPLAtofDelim(const char *nptr, char point) +{ + return CPLStrtodDelim(nptr, 0, point); +} + +/************************************************************************/ +/* CPLAtof() */ +/************************************************************************/ + +/** + * Converts ASCII string to floating point number. + * + * This function converts the initial portion of the string pointed to + * by nptr to double floating point representation. The behaviour is the + * same as + * + * CPLStrtod(nptr, (char **)NULL); + * + * This function does the same as standard atof(3), but does not take + * locale in account. That means, the decimal delimiter is always '.' + * (decimal point). Use CPLAtofDelim() function if you want to specify + * custom delimiter. + * + * IMPORTANT NOTE. + * Existence of this function does not mean you should always use it. + * Sometimes you should use standard locale aware atof(3) and its family. When + * you need to process the user's input (for example, command line parameters) + * use atof(3), because the user works in a localized environment and the user's input will + * be done according to the locale set. In particular that means we should not + * make assumptions about character used as decimal delimiter, it can be + * either "." or ",". + * But when you are parsing some ASCII file in predefined format, you most + * likely need CPLAtof(), because such files distributed across the systems + * with different locales and floating point representation should be + * considered as a part of file format. If the format uses "." as a delimiter + * the same character must be used when parsing number regardless of actual + * locale setting. + * + * @param nptr Pointer to string to convert. + * + * @return Converted value, if any. + */ +double CPLAtof(const char *nptr) +{ + return CPLStrtod(nptr, 0); +} + +/************************************************************************/ +/* CPLAtofM() */ +/************************************************************************/ + +/** + * Converts ASCII string to floating point number using any numeric locale. + * + * This function converts the initial portion of the string pointed to + * by nptr to double floating point representation. This function does the + * same as standard atof(), but it allows a variety of locale representations. + * That is it supports numeric values with either a comma or a period for + * the decimal delimiter. + * + * PS. The M stands for Multi-lingual. + * + * @param nptr The string to convert. + * + * @return Converted value, if any. Zero on failure. + */ + +double CPLAtofM( const char *nptr ) + +{ + int i; + const static int nMaxSearch = 50; + + for( i = 0; i < nMaxSearch; i++ ) + { + if( nptr[i] == ',' ) + return CPLStrtodDelim( nptr, 0, ',' ); + else if( nptr[i] == '.' || nptr[i] == '\0' ) + return CPLStrtodDelim( nptr, 0, '.' ); + } + + return CPLStrtodDelim( nptr, 0, '.' ); +} + +/************************************************************************/ +/* CPLReplacePointByLocalePoint() */ +/************************************************************************/ + +static char* CPLReplacePointByLocalePoint(const char* pszNumber, char point) +{ +#if defined(WIN32CE) || defined(__ANDROID__) + static char byPoint = 0; + if (byPoint == 0) + { + char szBuf[16]; + sprintf(szBuf, "%.1f", 1.0); + byPoint = szBuf[1]; + } + if (point != byPoint) + { + const char* pszPoint = strchr(pszNumber, point); + if (pszPoint) + { + char* pszNew = CPLStrdup(pszNumber); + pszNew[pszPoint - pszNumber] = byPoint; + return pszNew; + } + } +#else + struct lconv *poLconv = localeconv(); + if ( poLconv + && poLconv->decimal_point + && poLconv->decimal_point[0] != '\0' ) + { + char byPoint = poLconv->decimal_point[0]; + + if (point != byPoint) + { + const char* pszLocalePoint = strchr(pszNumber, byPoint); + const char* pszPoint = strchr(pszNumber, point); + if (pszPoint || pszLocalePoint) + { + char* pszNew = CPLStrdup(pszNumber); + if( pszLocalePoint ) + pszNew[pszLocalePoint - pszNumber] = ' '; + if( pszPoint ) + pszNew[pszPoint - pszNumber] = byPoint; + return pszNew; + } + } + } +#endif + return (char*) pszNumber; +} + +/************************************************************************/ +/* CPLStrtodDelim() */ +/************************************************************************/ + +/** + * Converts ASCII string to floating point number using specified delimiter. + * + * This function converts the initial portion of the string pointed to + * by nptr to double floating point representation. This function does the + * same as standard strtod(3), but does not take locale in account. Instead of + * locale defined decimal delimiter you can specify your own one. Also see + * notes for CPLAtof() function. + * + * @param nptr Pointer to string to convert. + * @param endptr If is not NULL, a pointer to the character after the last + * character used in the conversion is stored in the location referenced + * by endptr. + * @param point Decimal delimiter. + * + * @return Converted value, if any. + */ +double CPLStrtodDelim(const char *nptr, char **endptr, char point) +{ + while( *nptr == ' ' ) + nptr ++; + + if (nptr[0] == '-') + { + if (strcmp(nptr, "-1.#QNAN") == 0 || + strcmp(nptr, "-1.#IND") == 0) + { + if( endptr ) *endptr = (char*)nptr + strlen(nptr); + return NAN; + } + + if (strcmp(nptr,"-inf") == 0 || + EQUALN (nptr,"-1.#INF",strlen("-1.#INF"))) + { + if( endptr ) *endptr = (char*)nptr + strlen(nptr); + return NEG_INFINITY; + } + } + else if (nptr[0] == '1') + { + if (strcmp(nptr, "1.#QNAN") == 0) + { + if( endptr ) *endptr = (char*)nptr + strlen(nptr); + return NAN; + } + if( EQUALN (nptr,"1.#INF",strlen("1.#INF")) ) + { + if( endptr ) *endptr = (char*)nptr + strlen(nptr); + return INFINITY; + } + } + else if (nptr[0] == 'i' && strcmp(nptr,"inf") == 0) + { + if( endptr ) *endptr = (char*)nptr + strlen(nptr); + return INFINITY; + } + else if (nptr[0] == 'n' && strcmp(nptr,"nan") == 0) + { + if( endptr ) *endptr = (char*)nptr + strlen(nptr); + return NAN; + } + +/* -------------------------------------------------------------------- */ +/* We are implementing a simple method here: copy the input string */ +/* into the temporary buffer, replace the specified decimal delimiter */ +/* with the one, taken from locale settings and use standard strtod() */ +/* on that buffer. */ +/* -------------------------------------------------------------------- */ + double dfValue; + int nError; + + char* pszNumber = CPLReplacePointByLocalePoint(nptr, point); + + dfValue = strtod( pszNumber, endptr ); + nError = errno; + + if ( endptr ) + *endptr = (char *)nptr + (*endptr - pszNumber); + + if (pszNumber != (char*) nptr) + CPLFree( pszNumber ); + + errno = nError; + return dfValue; +} + +/************************************************************************/ +/* CPLStrtod() */ +/************************************************************************/ + +/** + * Converts ASCII string to floating point number. + * + * This function converts the initial portion of the string pointed to + * by nptr to double floating point representation. This function does the + * same as standard strtod(3), but does not take locale in account. That + * means, the decimal delimiter is always '.' (decimal point). Use + * CPLStrtodDelim() function if you want to specify custom delimiter. Also + * see notes for CPLAtof() function. + * + * @param nptr Pointer to string to convert. + * @param endptr If is not NULL, a pointer to the character after the last + * character used in the conversion is stored in the location referenced + * by endptr. + * + * @return Converted value, if any. + */ +double CPLStrtod(const char *nptr, char **endptr) +{ + return CPLStrtodDelim(nptr, endptr, '.'); +} + +/************************************************************************/ +/* CPLStrtofDelim() */ +/************************************************************************/ + +/** + * Converts ASCII string to floating point number using specified delimiter. + * + * This function converts the initial portion of the string pointed to + * by nptr to single floating point representation. This function does the + * same as standard strtof(3), but does not take locale in account. Instead of + * locale defined decimal delimiter you can specify your own one. Also see + * notes for CPLAtof() function. + * + * @param nptr Pointer to string to convert. + * @param endptr If is not NULL, a pointer to the character after the last + * character used in the conversion is stored in the location referenced + * by endptr. + * @param point Decimal delimiter. + * + * @return Converted value, if any. + */ +float CPLStrtofDelim(const char *nptr, char **endptr, char point) +{ +#if defined(HAVE_STRTOF) +/* -------------------------------------------------------------------- */ +/* We are implementing a simple method here: copy the input string */ +/* into the temporary buffer, replace the specified decimal delimiter */ +/* with the one, taken from locale settings and use standard strtof() */ +/* on that buffer. */ +/* -------------------------------------------------------------------- */ + + double dfValue; + int nError; + + char* pszNumber = CPLReplacePointByLocalePoint(nptr, point); + + dfValue = strtof( pszNumber, endptr ); + nError = errno; + + if ( endptr ) + *endptr = (char *)nptr + (*endptr - pszNumber); + + if (pszNumber != (char*) nptr) + CPLFree( pszNumber ); + + errno = nError; + return dfValue; + +#else + + return (float)CPLStrtodDelim(nptr, endptr, point); + +#endif /* HAVE_STRTOF */ +} + +/************************************************************************/ +/* CPLStrtof() */ +/************************************************************************/ + +/** + * Converts ASCII string to floating point number. + * + * This function converts the initial portion of the string pointed to + * by nptr to single floating point representation. This function does the + * same as standard strtof(3), but does not take locale in account. That + * means, the decimal delimiter is always '.' (decimal point). Use + * CPLStrtofDelim() function if you want to specify custom delimiter. Also + * see notes for CPLAtof() function. + * + * @param nptr Pointer to string to convert. + * @param endptr If is not NULL, a pointer to the character after the last + * character used in the conversion is stored in the location referenced + * by endptr. + * + * @return Converted value, if any. + */ +float CPLStrtof(const char *nptr, char **endptr) +{ + return CPLStrtofDelim(nptr, endptr, '.'); +} + +/* END OF FILE */ diff --git a/bazaar/plugin/gdal/port/cpl_time.cpp b/bazaar/plugin/gdal/port/cpl_time.cpp new file mode 100644 index 000000000..713cff987 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_time.cpp @@ -0,0 +1,153 @@ +/********************************************************************** + * $Id: cpl_time.cpp 18063 2009-11-21 21:11:49Z warmerdam $ + * + * Name: cpl_time.cpp + * Project: CPL - Common Portability Library + * Purpose: Time functions. + * Author: Even Rouault, + * + ********************************************************************** + * + * CPLUnixTimeToYMDHMS() is derived from timesub() in localtime.c from openbsd/freebsd/netbsd. + * CPLYMDHMSToUnixTime() has been implemented by Even Rouault and is in the public domain + * + * Cf http://svn.freebsd.org/viewvc/base/stable/7/lib/libc/stdtime/localtime.c?revision=178142&view=markup + * localtime.c comes with the following header : + * + * This file is in the public domain, so clarified as of + * 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). + */ + +#include "cpl_time.h" + +#define SECSPERMIN 60L +#define MINSPERHOUR 60L +#define HOURSPERDAY 24L +#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) +#define SECSPERDAY (SECSPERHOUR * HOURSPERDAY) +#define DAYSPERWEEK 7 +#define MONSPERYEAR 12 + +#define EPOCH_YEAR 1970 +#define EPOCH_WDAY 4 +#define TM_YEAR_BASE 1900 +#define DAYSPERNYEAR 365 +#define DAYSPERLYEAR 366 + +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) +#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) + +static const int mon_lengths[2][MONSPERYEAR] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +} ; + + +static const int year_lengths[2] = { + DAYSPERNYEAR, DAYSPERLYEAR +}; + +/************************************************************************/ +/* CPLUnixTimeToYMDHMS() */ +/************************************************************************/ + +/** Converts a time value since the Epoch (aka "unix" time) to a broken-down UTC time. + * + * This function is similar to gmtime_r(). + * This function will always set tm_isdst to 0. + * + * @param unixTime number of seconds since the Epoch. + * @param pRet address of the return structure. + * + * @return the structure pointed by pRet filled with a broken-down UTC time. + */ + +struct tm * CPLUnixTimeToYMDHMS(GIntBig unixTime, struct tm* pRet) +{ + GIntBig days = unixTime / SECSPERDAY; + GIntBig rem = unixTime % SECSPERDAY; + + while (rem < 0) { + rem += SECSPERDAY; + --days; + } + + pRet->tm_hour = (int) (rem / SECSPERHOUR); + rem = rem % SECSPERHOUR; + pRet->tm_min = (int) (rem / SECSPERMIN); + /* + ** A positive leap second requires a special + ** representation. This uses "... ??:59:60" et seq. + */ + pRet->tm_sec = (int) (rem % SECSPERMIN); + pRet->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK); + if (pRet->tm_wday < 0) + pRet->tm_wday += DAYSPERWEEK; + GIntBig y = EPOCH_YEAR; + int yleap; + while (days < 0 || days >= (GIntBig) year_lengths[yleap = isleap(y)]) + { + GIntBig newy; + + newy = y + days / DAYSPERNYEAR; + if (days < 0) + --newy; + days -= (newy - y) * DAYSPERNYEAR + + LEAPS_THRU_END_OF(newy - 1) - + LEAPS_THRU_END_OF(y - 1); + y = newy; + } + pRet->tm_year = (int) (y - TM_YEAR_BASE); + pRet->tm_yday = (int) days; + const int* ip = mon_lengths[yleap]; + for (pRet->tm_mon = 0; days >= (GIntBig) ip[pRet->tm_mon]; ++(pRet->tm_mon)) + days = days - (GIntBig) ip[pRet->tm_mon]; + pRet->tm_mday = (int) (days + 1); + pRet->tm_isdst = 0; + + return pRet; +} + +/************************************************************************/ +/* CPLYMDHMSToUnixTime() */ +/************************************************************************/ + +/** Converts a broken-down UTC time into time since the Epoch (aka "unix" time). + * + * This function is similar to mktime(), but the passed structure is not modified. + * This function ignores the tm_wday, tm_yday and tm_isdst fields of the passed value. + * No timezone shift will be applied. This function returns 0 for the 1/1/1970 00:00:00 + * + * @param brokendowntime broken-downtime UTC time. + * + * @return a number of seconds since the Epoch encoded as a value of type GIntBig, + * or -1 if the time cannot be represented. + */ + +GIntBig CPLYMDHMSToUnixTime(const struct tm *brokendowntime) +{ + GIntBig days; + int mon; + + if (brokendowntime->tm_mon < 0 || brokendowntime->tm_mon >= 12) + return -1; + + /* Number of days of the current month */ + days = brokendowntime->tm_mday - 1; + + /* Add the number of days of the current year */ + const int* ip = mon_lengths[isleap(TM_YEAR_BASE + brokendowntime->tm_year)]; + for(mon=0;montm_mon;mon++) + days += ip [mon]; + + /* Add the number of days of the other years */ + days += (TM_YEAR_BASE + (GIntBig)brokendowntime->tm_year - EPOCH_YEAR) * DAYSPERNYEAR + + LEAPS_THRU_END_OF(TM_YEAR_BASE + (GIntBig)brokendowntime->tm_year - 1) - + LEAPS_THRU_END_OF(EPOCH_YEAR - 1); + + /* Now add the secondes, minutes and hours to the number of days since EPOCH */ + return brokendowntime->tm_sec + + brokendowntime->tm_min * SECSPERMIN + + brokendowntime->tm_hour * SECSPERHOUR + + days * SECSPERDAY; +} diff --git a/bazaar/plugin/gdal/port/cpl_time.h b/bazaar/plugin/gdal/port/cpl_time.h new file mode 100644 index 000000000..b131b5cda --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_time.h @@ -0,0 +1,41 @@ +/********************************************************************** + * $Id: cpl_time.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Name: cpl_time.h + * Project: CPL - Common Portability Library + * Purpose: Time functions. + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2009, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_TIME_H_INCLUDED +#define _CPL_TIME_H_INCLUDED + +#include + +#include "cpl_port.h" + +struct tm CPL_DLL * CPLUnixTimeToYMDHMS(GIntBig unixTime, struct tm* pRet); +GIntBig CPL_DLL CPLYMDHMSToUnixTime(const struct tm *brokendowntime); + +#endif // _CPL_TIME_H_INCLUDED diff --git a/bazaar/plugin/gdal/port/cpl_virtualmem.cpp b/bazaar/plugin/gdal/port/cpl_virtualmem.cpp new file mode 100644 index 000000000..3af201069 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_virtualmem.cpp @@ -0,0 +1,2059 @@ +/********************************************************************** + * $Id: cpl_virtualmem.cpp 29330 2015-06-14 12:11:11Z rouault $ + * + * Name: cpl_virtualmem.cpp + * Project: CPL - Common Portability Library + * Purpose: Virtual memory + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "cpl_virtualmem.h" +#include "cpl_error.h" +#include "cpl_multiproc.h" +#include "cpl_atomic_ops.h" +#include "cpl_conv.h" + +#if defined(__linux) && defined(CPL_MULTIPROC_PTHREAD) + +#include /* mmap, munmap, mremap */ +#include /* select */ +#include /* open() */ +#include /* open() */ +#include +#include +#include /* open() */ +#include /* sigaction */ +#include +#include +#include +#include /* read, write, close, pipe */ +#include + +#ifndef HAVE_5ARGS_MREMAP +#include "cpl_atomic_ops.h" +#endif + +/* Linux specific (i.e. non POSIX compliant) features used : + - returning from a SIGSEGV handler is clearly a POSIX violation, but in + practice most POSIX systems should be happy. + - mremap() with 5 args is Linux specific. It is used when the user callback is invited + to fill a page, we currently mmap() a writable page, let it filled it, + and afterwards mremap() that temporary page onto the location where the + fault occured. + If we have no mremap(), the workaround is to pause other threads that + consume the current view while we are updating the faulted page, otherwise + a non-paused thread could access a page that is in the middle of being + filled... The way we pause those threads is quite original : we send them + a SIGUSR1 and wait that they are stuck in the temporary SIGUSR1 handler... + - MAP_ANONYMOUS isn't documented in Posix, but very commonly found (sometimes called MAP_ANON) + - dealing with the limitation of number of memory mapping regions, and the 65536 limit. + - other things I've not identified +*/ + + +#define ALIGN_DOWN(p,pagesize) (void*)(((size_t)(p)) / (pagesize) * (pagesize)) +#define ALIGN_UP(p,pagesize) (void*)(((size_t)(p) + (pagesize) - 1) / (pagesize) * (pagesize)) + +#define DEFAULT_PAGE_SIZE (256*256) +#define MAXIMUM_PAGE_SIZE (32*1024*1024) + +/* Linux Kernel limit */ +#define MAXIMUM_COUNT_OF_MAPPINGS 65536 + +#define BYEBYE_ADDR ((void*)(~(size_t)0)) + +#define MAPPING_FOUND "yeah" +#define MAPPING_NOT_FOUND "doh!" + +#define SET_BIT(ar,bitnumber) ar[(bitnumber)/8] |= 1 << ((bitnumber) % 8) +#define UNSET_BIT(ar,bitnumber) ar[(bitnumber)/8] &= ~(1 << ((bitnumber) % 8)) +#define TEST_BIT(ar,bitnumber) (ar[(bitnumber)/8] & (1 << ((bitnumber) % 8))) + +typedef enum +{ + OP_LOAD, + OP_STORE, + OP_MOVS_RSI_RDI, + OP_UNKNOWN +} OpType; + +struct CPLVirtualMem +{ + struct CPLVirtualMem *pVMemBase; + int nRefCount; + + int bFileMemoryMapped; /* if TRUE, only eAccessMode, pData, pDataToFree, nSize and nPageSize are valid */ + CPLVirtualMemAccessMode eAccessMode; + + size_t nPageSize; + void *pData; /* aligned on nPageSize */ + void *pDataToFree; /* returned by mmap(), potentially lower than pData */ + size_t nSize; /* requested size (unrounded) */ + + GByte *pabitMappedPages; + GByte *pabitRWMappedPages; + + int nCacheMaxSizeInPages; /* maximum size of page array */ + int *panLRUPageIndices; /* array with indices of cached pages */ + int iLRUStart; /* index in array where to write next page index */ + int nLRUSize; /* current isze of the array */ + + int iLastPage; /* last page accessed */ + int nRetry; /* number of consecutive retries to that last page */ + + int bSingleThreadUsage; + + CPLVirtualMemCachePageCbk pfnCachePage; /* called when a page is mapped */ + CPLVirtualMemUnCachePageCbk pfnUnCachePage; /* called when a (writable) page is unmapped */ + void *pCbkUserData; + CPLVirtualMemFreeUserData pfnFreeUserData; +#ifndef HAVE_5ARGS_MREMAP + CPLMutex *hMutexThreadArray; + int nThreads; + pthread_t *pahThreads; +#endif +}; + +typedef struct +{ + /* hVirtualMemManagerMutex protects the 2 following variables */ + CPLVirtualMem **pasVirtualMem; + int nVirtualMemCount; + + int pipefd_to_thread[2]; + int pipefd_from_thread[2]; + int pipefd_wait_thread[2]; + CPLJoinableThread *hHelperThread; + + struct sigaction oldact; +} CPLVirtualMemManager; + +typedef struct +{ + void *pFaultAddr; + OpType opType; + pthread_t hRequesterThread; +} CPLVirtualMemMsgToWorkerThread; + +static CPLVirtualMemManager* pVirtualMemManager = NULL; +static CPLMutex* hVirtualMemManagerMutex = NULL; + +static void CPLVirtualMemManagerInit(); + +#ifdef DEBUG_VIRTUALMEM + +/************************************************************************/ +/* fprintfstderr() */ +/************************************************************************/ + +static void fprintfstderr(const char* fmt, ...) +{ + char buffer[80]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + int offset = 0; + while(TRUE) + { + int ret = write(2, buffer + offset, strlen(buffer + offset)); + if( ret < 0 && errno == EINTR ) + ; + else + { + if( ret == (int)strlen(buffer + offset) ) + break; + offset += ret; + } + } +} + +#endif + +/************************************************************************/ +/* CPLGetPageSize() */ +/************************************************************************/ + +size_t CPLGetPageSize(void) +{ + return (size_t) sysconf(_SC_PAGESIZE); +} + +/************************************************************************/ +/* CPLVirtualMemManagerRegisterVirtualMem() */ +/************************************************************************/ + +static void CPLVirtualMemManagerRegisterVirtualMem(CPLVirtualMem* ctxt) +{ + CPLVirtualMemManagerInit(); + + assert(ctxt); + CPLAcquireMutex(hVirtualMemManagerMutex, 1000.0); + pVirtualMemManager->pasVirtualMem = (CPLVirtualMem**) CPLRealloc( + pVirtualMemManager->pasVirtualMem, sizeof(CPLVirtualMem*) * (pVirtualMemManager->nVirtualMemCount + 1) ); + pVirtualMemManager->pasVirtualMem[pVirtualMemManager->nVirtualMemCount] = ctxt; + pVirtualMemManager->nVirtualMemCount ++; + CPLReleaseMutex(hVirtualMemManagerMutex); +} + +/************************************************************************/ +/* CPLVirtualMemManagerUnregisterVirtualMem() */ +/************************************************************************/ + +static void CPLVirtualMemManagerUnregisterVirtualMem(CPLVirtualMem* ctxt) +{ + int i; + CPLAcquireMutex(hVirtualMemManagerMutex, 1000.0); + for(i=0;inVirtualMemCount;i++) + { + if( pVirtualMemManager->pasVirtualMem[i] == ctxt ) + { + if( i < pVirtualMemManager->nVirtualMemCount - 1 ) + { + memmove( pVirtualMemManager->pasVirtualMem + i, + pVirtualMemManager->pasVirtualMem + i + 1, + sizeof(CPLVirtualMem*) * (pVirtualMemManager->nVirtualMemCount - i - 1) ); + } + pVirtualMemManager->nVirtualMemCount --; + break; + } + } + CPLReleaseMutex(hVirtualMemManagerMutex); +} + +/************************************************************************/ +/* CPLVirtualMemNew() */ +/************************************************************************/ + +CPLVirtualMem* CPLVirtualMemNew(size_t nSize, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + CPLVirtualMemAccessMode eAccessMode, + CPLVirtualMemCachePageCbk pfnCachePage, + CPLVirtualMemUnCachePageCbk pfnUnCachePage, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData) +{ + CPLVirtualMem* ctxt; + void* pData; + size_t nMinPageSize = CPLGetPageSize(); + size_t nPageSize = DEFAULT_PAGE_SIZE; + size_t nCacheMaxSizeInPages; + size_t nRoundedMappingSize; + FILE* f; + int nMappings = 0; + + assert(nSize > 0); + assert(pfnCachePage != NULL); + + if( nPageSizeHint >= nMinPageSize && nPageSizeHint <= MAXIMUM_PAGE_SIZE ) + { + if( (nPageSizeHint % nMinPageSize) == 0 ) + nPageSize = nPageSizeHint; + else + { + int nbits = 0; + nPageSize = (size_t)nPageSizeHint; + while(nPageSize > 0) + { + nPageSize >>= 1; + nbits ++; + } + nPageSize = (size_t)1 << (nbits - 1); + if( nPageSize < (size_t)nPageSizeHint ) + nPageSize <<= 1; + } + } + + if( (nPageSize % nMinPageSize) != 0 ) + nPageSize = nMinPageSize; + + if( nCacheSize > nSize ) + nCacheSize = nSize; + else if( nCacheSize == 0 ) + nCacheSize = 1; + + /* Linux specific */ + /* Count the number of existing memory mappings */ + f = fopen("/proc/self/maps", "rb"); + if( f != NULL ) + { + char buffer[80]; + while( fgets(buffer, sizeof(buffer), f) != NULL ) + nMappings ++; + fclose(f); + } + + while(TRUE) + { + /* /proc/self/maps must not have more than 65K lines */ + nCacheMaxSizeInPages = (nCacheSize + 2 * nPageSize - 1) / nPageSize; + if( nCacheMaxSizeInPages > + (size_t)((MAXIMUM_COUNT_OF_MAPPINGS * 9 / 10) - nMappings) ) + nPageSize <<= 1; + else + break; + } + nRoundedMappingSize = ((nSize + 2 * nPageSize - 1) / nPageSize) * nPageSize; + pData = mmap(NULL, nRoundedMappingSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if( pData == MAP_FAILED ) + { + perror("mmap"); + return NULL; + } + ctxt = (CPLVirtualMem* )CPLCalloc(1, sizeof(CPLVirtualMem)); + ctxt->nRefCount = 1; + ctxt->bFileMemoryMapped = FALSE; + ctxt->eAccessMode = eAccessMode; + ctxt->pDataToFree = pData; + ctxt->pData = ALIGN_UP(pData, nPageSize); + ctxt->nPageSize = nPageSize; + ctxt->pabitMappedPages = (GByte*)CPLCalloc(1, (nRoundedMappingSize / nPageSize + 7) / 8); + assert(ctxt->pabitMappedPages); + ctxt->pabitRWMappedPages = (GByte*)CPLCalloc(1, (nRoundedMappingSize / nPageSize + 7) / 8); + assert(ctxt->pabitRWMappedPages); + /* we need at least 2 pages in case for a rep movs instruction */ + /* that operate in the view */ + ctxt->nCacheMaxSizeInPages = nCacheMaxSizeInPages; + ctxt->panLRUPageIndices = (int*)CPLMalloc(ctxt->nCacheMaxSizeInPages * sizeof(int)); + assert(ctxt->panLRUPageIndices); + ctxt->nSize = nSize; + ctxt->iLRUStart = 0; + ctxt->nLRUSize = 0; + ctxt->iLastPage = -1; + ctxt->nRetry = 0; + ctxt->bSingleThreadUsage = bSingleThreadUsage; + ctxt->pfnCachePage = pfnCachePage; + ctxt->pfnUnCachePage = pfnUnCachePage; + ctxt->pfnFreeUserData = pfnFreeUserData; + ctxt->pCbkUserData = pCbkUserData; +#ifndef HAVE_5ARGS_MREMAP + if( !ctxt->bSingleThreadUsage ) + { + ctxt->hMutexThreadArray = CPLCreateMutex(); + assert(ctxt->hMutexThreadArray != NULL); + CPLReleaseMutex(ctxt->hMutexThreadArray); + ctxt->nThreads = 0; + ctxt->pahThreads = NULL; + } +#endif + + CPLVirtualMemManagerRegisterVirtualMem(ctxt); + + return ctxt; +} + +/************************************************************************/ +/* CPLIsVirtualMemFileMapAvailable() */ +/************************************************************************/ + +int CPLIsVirtualMemFileMapAvailable(void) +{ + return TRUE; +} + +/************************************************************************/ +/* CPLVirtualMemFileMapNew() */ +/************************************************************************/ + +CPLVirtualMem *CPLVirtualMemFileMapNew( VSILFILE* fp, + vsi_l_offset nOffset, + vsi_l_offset nLength, + CPLVirtualMemAccessMode eAccessMode, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData ) +{ +#if SIZEOF_VOIDP == 4 + if( nLength != (size_t)nLength ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "nLength = " CPL_FRMT_GUIB " incompatible with 32 bit architecture", + nLength); + return NULL; + } +#endif + + int fd = (int) (size_t) VSIFGetNativeFileDescriptorL(fp); + if( fd == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot operate on a virtual file"); + return NULL; + } + + off_t nAlignedOffset = (nOffset / CPLGetPageSize()) * CPLGetPageSize(); + size_t nAligment = nOffset - nAlignedOffset; + size_t nMappingSize = nLength + nAligment; + + /* We need to ensure that the requested extent fits into the file size */ + /* otherwise SIGBUS errors will occur when using the mapping */ + vsi_l_offset nCurPos = VSIFTellL(fp); + VSIFSeekL(fp, 0, SEEK_END); + vsi_l_offset nFileSize = VSIFTellL(fp); + if( nFileSize < nOffset + nLength ) + { + if( eAccessMode != VIRTUALMEM_READWRITE ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Trying to map an extent outside of the file"); + VSIFSeekL(fp, nCurPos, SEEK_SET); + return NULL; + } + else + { + char ch = 0; + if( VSIFSeekL(fp, nOffset + nLength - 1, SEEK_SET) != 0 || + VSIFWriteL(&ch, 1, 1, fp) != 1 ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Cannot extend file to mapping size"); + VSIFSeekL(fp, nCurPos, SEEK_SET); + return NULL; + } + } + } + VSIFSeekL(fp, nCurPos, SEEK_SET); + + void* addr = mmap(NULL, nMappingSize, + (eAccessMode == VIRTUALMEM_READWRITE) ? PROT_READ | PROT_WRITE : PROT_READ, + MAP_SHARED, fd, nAlignedOffset); + if( addr == MAP_FAILED ) + { + int myerrno = errno; + CPLError(CE_Failure, CPLE_AppDefined, + "mmap() failed : %s", strerror(myerrno)); + return NULL; + } + + CPLVirtualMem* ctxt = (CPLVirtualMem* )CPLCalloc(1, sizeof(CPLVirtualMem)); + ctxt->nRefCount = 1; + ctxt->eAccessMode = eAccessMode; + ctxt->bFileMemoryMapped = TRUE; + ctxt->pData = (GByte*) addr + nAligment; + ctxt->pDataToFree = addr; + ctxt->nSize = nLength; + ctxt->nPageSize = CPLGetPageSize(); + ctxt->bSingleThreadUsage = FALSE; + ctxt->pfnFreeUserData = pfnFreeUserData; + ctxt->pCbkUserData = pCbkUserData; + + return ctxt; +} + +/************************************************************************/ +/* CPLVirtualMemDerivedNew() */ +/************************************************************************/ + +CPLVirtualMem *CPLVirtualMemDerivedNew(CPLVirtualMem* pVMemBase, + vsi_l_offset nOffset, + vsi_l_offset nSize, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData) +{ + if( nOffset + nSize > pVMemBase->nSize ) + return NULL; + + CPLVirtualMem* ctxt = (CPLVirtualMem* )CPLCalloc(1, sizeof(CPLVirtualMem)); + ctxt->nRefCount = 1; + ctxt->pVMemBase = pVMemBase; + pVMemBase->nRefCount ++; + ctxt->eAccessMode = pVMemBase->eAccessMode; + ctxt->bFileMemoryMapped = pVMemBase->bFileMemoryMapped; + ctxt->pData = (GByte*) pVMemBase->pData + nOffset; + ctxt->pDataToFree = NULL; + ctxt->nSize = nSize; + ctxt->nPageSize = pVMemBase->nPageSize; + ctxt->bSingleThreadUsage = pVMemBase->bSingleThreadUsage; + ctxt->pfnFreeUserData = pfnFreeUserData; + ctxt->pCbkUserData = pCbkUserData; + + return ctxt; +} + +/************************************************************************/ +/* CPLVirtualMemFree() */ +/************************************************************************/ + +void CPLVirtualMemFree(CPLVirtualMem* ctxt) +{ + size_t nRoundedMappingSize; + + if( ctxt == NULL || --(ctxt->nRefCount) > 0 ) + return; + + if( ctxt->pVMemBase != NULL ) + { + CPLVirtualMemFree(ctxt->pVMemBase); + if( ctxt->pfnFreeUserData != NULL ) + ctxt->pfnFreeUserData(ctxt->pCbkUserData); + CPLFree(ctxt); + return; + } + + if( ctxt->bFileMemoryMapped ) + { + size_t nMappingSize = ctxt->nSize + (GByte*)ctxt->pData - (GByte*)ctxt->pDataToFree; + assert(munmap(ctxt->pDataToFree, nMappingSize) == 0); + if( ctxt->pfnFreeUserData != NULL ) + ctxt->pfnFreeUserData(ctxt->pCbkUserData); + CPLFree(ctxt); + return; + } + + CPLVirtualMemManagerUnregisterVirtualMem(ctxt); + + nRoundedMappingSize = ((ctxt->nSize + 2 * ctxt->nPageSize - 1) / + ctxt->nPageSize) * ctxt->nPageSize; + if( ctxt->eAccessMode == VIRTUALMEM_READWRITE && + ctxt->pfnUnCachePage != NULL ) + { + size_t i; + for(i = 0; i < nRoundedMappingSize / ctxt->nPageSize; i++) + { + if( TEST_BIT(ctxt->pabitRWMappedPages, i) ) + { + void* addr = (char*)ctxt->pData + i * ctxt->nPageSize; + ctxt->pfnUnCachePage(ctxt, + i * ctxt->nPageSize, + addr, + ctxt->nPageSize, + ctxt->pCbkUserData); + } + } + } + assert(munmap(ctxt->pDataToFree, nRoundedMappingSize) == 0); + CPLFree(ctxt->pabitMappedPages); + CPLFree(ctxt->pabitRWMappedPages); + CPLFree(ctxt->panLRUPageIndices); +#ifndef HAVE_5ARGS_MREMAP + if( !ctxt->bSingleThreadUsage ) + { + CPLFree(ctxt->pahThreads); + CPLDestroyMutex(ctxt->hMutexThreadArray); + } +#endif + if( ctxt->pfnFreeUserData != NULL ) + ctxt->pfnFreeUserData(ctxt->pCbkUserData); + + CPLFree(ctxt); +} + +/************************************************************************/ +/* CPLVirtualMemGetAddr() */ +/************************************************************************/ + +void* CPLVirtualMemGetAddr(CPLVirtualMem* ctxt) +{ + return ctxt->pData; +} + +/************************************************************************/ +/* CPLVirtualMemIsFileMapping() */ +/************************************************************************/ + +int CPLVirtualMemIsFileMapping(CPLVirtualMem* ctxt) +{ + return ctxt->bFileMemoryMapped; +} + +/************************************************************************/ +/* CPLVirtualMemGetAccessMode() */ +/************************************************************************/ + +CPLVirtualMemAccessMode CPLVirtualMemGetAccessMode(CPLVirtualMem* ctxt) +{ + return ctxt->eAccessMode; +} + +/************************************************************************/ +/* CPLVirtualMemGetPageSize() */ +/************************************************************************/ + +size_t CPLVirtualMemGetPageSize(CPLVirtualMem* ctxt) +{ + return ctxt->nPageSize; +} + +/************************************************************************/ +/* CPLVirtualMemGetSize() */ +/************************************************************************/ + +size_t CPLVirtualMemGetSize(CPLVirtualMem* ctxt) +{ + return ctxt->nSize; +} + +#ifndef HAVE_5ARGS_MREMAP + +static volatile int nCountThreadsInSigUSR1 = 0; +static volatile int nWaitHelperThread = 0; + +/************************************************************************/ +/* CPLVirtualMemSIGUSR1Handler() */ +/************************************************************************/ + +static void CPLVirtualMemSIGUSR1Handler(int signum_unused, + siginfo_t* the_info_unused, + void* the_ctxt_unused) +{ + /* fprintfstderr("entering CPLVirtualMemSIGUSR1Handler %X\n", pthread_self()); */ + (void)signum_unused; + (void)the_info_unused; + (void)the_ctxt_unused; + /* I guess this is only POSIX correct if it is implemented by an intrinsic */ + CPLAtomicInc(&nCountThreadsInSigUSR1); + while( nWaitHelperThread ) + usleep(1); /* not explicitly indicated as signal-async-safe, but hopefully ok */ + CPLAtomicDec(&nCountThreadsInSigUSR1); + /* fprintfstderr("leaving CPLVirtualMemSIGUSR1Handler %X\n", pthread_self()); */ +} +#endif + +/************************************************************************/ +/* CPLVirtualMemIsAccessThreadSafe() */ +/************************************************************************/ + +int CPLVirtualMemIsAccessThreadSafe(CPLVirtualMem* ctxt) +{ + return !ctxt->bSingleThreadUsage; +} + +/************************************************************************/ +/* CPLVirtualMemDeclareThread() */ +/************************************************************************/ + +void CPLVirtualMemDeclareThread(CPLVirtualMem* ctxt) +{ + if( ctxt->bFileMemoryMapped ) + return; +#ifndef HAVE_5ARGS_MREMAP + assert( !ctxt->bSingleThreadUsage ); + CPLAcquireMutex(ctxt->hMutexThreadArray, 1000.0); + ctxt->pahThreads = (pthread_t*) CPLRealloc(ctxt->pahThreads, + (ctxt->nThreads + 1) * sizeof(pthread_t)); + ctxt->pahThreads[ctxt->nThreads] = pthread_self(); + ctxt->nThreads ++; + + CPLReleaseMutex(ctxt->hMutexThreadArray); +#endif +} + +/************************************************************************/ +/* CPLVirtualMemUnDeclareThread() */ +/************************************************************************/ + +void CPLVirtualMemUnDeclareThread(CPLVirtualMem* ctxt) +{ + if( ctxt->bFileMemoryMapped ) + return; +#ifndef HAVE_5ARGS_MREMAP + int i; + pthread_t self = pthread_self(); + assert( !ctxt->bSingleThreadUsage ); + CPLAcquireMutex(ctxt->hMutexThreadArray, 1000.0); + for(i = 0; i < ctxt->nThreads; i++) + { + if( ctxt->pahThreads[i] == self ) + { + if( i < ctxt->nThreads - 1 ) + memmove(ctxt->pahThreads + i + 1, + ctxt->pahThreads + i, + (ctxt->nThreads - 1 - i) * sizeof(pthread_t)); + ctxt->nThreads --; + break; + } + } + + CPLReleaseMutex(ctxt->hMutexThreadArray); +#endif +} + + +/************************************************************************/ +/* CPLVirtualMemGetPageToFill() */ +/************************************************************************/ + +/* Must be paired with CPLVirtualMemAddPage */ +static +void* CPLVirtualMemGetPageToFill(CPLVirtualMem* ctxt, void* start_page_addr) +{ + void* pPageToFill; + + if( ctxt->bSingleThreadUsage ) + { + pPageToFill = start_page_addr; + assert(mprotect(pPageToFill, ctxt->nPageSize, PROT_READ | PROT_WRITE) == 0); + } + else + { +#ifndef HAVE_5ARGS_MREMAP + CPLAcquireMutex(ctxt->hMutexThreadArray, 1000.0); + if( ctxt->nThreads == 1 ) + { + pPageToFill = start_page_addr; + assert(mprotect(pPageToFill, ctxt->nPageSize, PROT_READ | PROT_WRITE) == 0); + } + else +#endif + { + /* Allocate a temporary writable page that the user */ + /* callback can fill */ + pPageToFill = mmap(NULL, ctxt->nPageSize, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + assert(pPageToFill != MAP_FAILED); + } + } + return pPageToFill; +} + +/************************************************************************/ +/* CPLVirtualMemAddPage() */ +/************************************************************************/ + +static +void CPLVirtualMemAddPage(CPLVirtualMem* ctxt, void* target_addr, void* pPageToFill, + OpType opType, pthread_t hRequesterThread) +{ + size_t iPage = (int)(((char*)target_addr - (char*)ctxt->pData) / ctxt->nPageSize); + if( ctxt->nLRUSize == ctxt->nCacheMaxSizeInPages ) + { + /* fprintfstderr("uncaching page %d\n", iPage); */ + int nOldPage = ctxt->panLRUPageIndices[ctxt->iLRUStart]; + void* addr = (char*)ctxt->pData + nOldPage * ctxt->nPageSize; + if( ctxt->eAccessMode == VIRTUALMEM_READWRITE && + ctxt->pfnUnCachePage != NULL && + TEST_BIT(ctxt->pabitRWMappedPages, nOldPage) ) + { + size_t nToBeEvicted = ctxt->nPageSize; + if( (char*)addr + nToBeEvicted >= (char*) ctxt->pData + ctxt->nSize ) + nToBeEvicted = (char*) ctxt->pData + ctxt->nSize - (char*)addr; + + ctxt->pfnUnCachePage(ctxt, + nOldPage * ctxt->nPageSize, + addr, + nToBeEvicted, + ctxt->pCbkUserData); + } + /* "Free" the least recently used page */ + UNSET_BIT(ctxt->pabitMappedPages, nOldPage); + UNSET_BIT(ctxt->pabitRWMappedPages, nOldPage); + /* Free the old page */ + /* Not sure how portable it is to do that that way... */ + assert(mmap(addr, ctxt->nPageSize, PROT_NONE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == addr); + } + ctxt->panLRUPageIndices[ctxt->iLRUStart] = iPage; + ctxt->iLRUStart = (ctxt->iLRUStart + 1) % ctxt->nCacheMaxSizeInPages; + if( ctxt->nLRUSize < ctxt->nCacheMaxSizeInPages ) + { + ctxt->nLRUSize ++; + } + SET_BIT(ctxt->pabitMappedPages, iPage); + + if( ctxt->bSingleThreadUsage ) + { + if( opType == OP_STORE && ctxt->eAccessMode == VIRTUALMEM_READWRITE ) + { + /* let (and mark) the page writable since the instruction that triggered */ + /* the fault is a store */ + SET_BIT(ctxt->pabitRWMappedPages, iPage); + } + else if( ctxt->eAccessMode != VIRTUALMEM_READONLY ) + { + assert(mprotect(target_addr, ctxt->nPageSize, PROT_READ) == 0); + } + } + else + { +#ifdef HAVE_5ARGS_MREMAP + (void)hRequesterThread; + + if( opType == OP_STORE && ctxt->eAccessMode == VIRTUALMEM_READWRITE ) + { + /* let (and mark) the page writable since the instruction that triggered */ + /* the fault is a store */ + SET_BIT(ctxt->pabitRWMappedPages, iPage); + } + else if( ctxt->eAccessMode != VIRTUALMEM_READONLY ) + { + /* Turn the temporary page read-only before remapping it. We will only turn it */ + /* writtable when a new fault occurs (and that the mapping is writable) */ + assert(mprotect(pPageToFill, ctxt->nPageSize, PROT_READ) == 0); + } + /* Can now remap the pPageToFill onto the target page */ + assert(mremap(pPageToFill, ctxt->nPageSize, ctxt->nPageSize, + MREMAP_MAYMOVE | MREMAP_FIXED, target_addr) == target_addr); + +#else + if (ctxt->nThreads > 1 ) + { + int i; + + /* Pause threads that share this mem view */ + CPLAtomicInc(&nWaitHelperThread); + + /* Install temporary SIGUSR1 signal handler */ + struct sigaction act, oldact; + act.sa_sigaction = CPLVirtualMemSIGUSR1Handler; + sigemptyset (&act.sa_mask); + /* We don't want the sigsegv handler to be called when we are */ + /* running the sigusr1 handler */ + assert(sigaddset(&act.sa_mask, SIGSEGV) == 0); + act.sa_flags = 0; + assert(sigaction(SIGUSR1, &act, &oldact) == 0); + + for(i = 0; i < ctxt->nThreads; i++) + { + if( ctxt->pahThreads[i] != hRequesterThread ) + { + /* fprintfstderr("stopping thread %X\n", ctxt->pahThreads[i]); */ + assert(pthread_kill( ctxt->pahThreads[i], SIGUSR1 ) == 0); + } + } + + /* Wait that they are all paused */ + while( nCountThreadsInSigUSR1 != ctxt->nThreads-1 ) + usleep(1); + + /* Restore old SIGUSR1 signal handler */ + assert(sigaction(SIGUSR1, &oldact, NULL) == 0); + + assert(mprotect(target_addr, ctxt->nPageSize, PROT_READ | PROT_WRITE) == 0); + /*fprintfstderr("memcpying page %d\n", iPage);*/ + memcpy(target_addr, pPageToFill, ctxt->nPageSize); + + if( opType == OP_STORE && ctxt->eAccessMode == VIRTUALMEM_READWRITE ) + { + /* let (and mark) the page writable since the instruction that triggered */ + /* the fault is a store */ + SET_BIT(ctxt->pabitRWMappedPages, iPage); + } + else + { + assert(mprotect(target_addr, ctxt->nPageSize, PROT_READ) == 0); + } + + /* Wake up sleeping threads */ + CPLAtomicDec(&nWaitHelperThread); + while( nCountThreadsInSigUSR1 != 0 ) + usleep(1); + + assert(munmap(pPageToFill, ctxt->nPageSize) == 0); + } + else + { + if( opType == OP_STORE && ctxt->eAccessMode == VIRTUALMEM_READWRITE ) + { + /* let (and mark) the page writable since the instruction that triggered */ + /* the fault is a store */ + SET_BIT(ctxt->pabitRWMappedPages, iPage); + } + else if( ctxt->eAccessMode != VIRTUALMEM_READONLY ) + { + assert(mprotect(target_addr, ctxt->nPageSize, PROT_READ) == 0); + } + } + + CPLReleaseMutex(ctxt->hMutexThreadArray); +#endif + } +} + +/************************************************************************/ +/* CPLVirtualMemGetOpTypeImm() */ +/************************************************************************/ + +#if defined(__x86_64__) || defined(__i386__) +static OpType CPLVirtualMemGetOpTypeImm(GByte val_rip) +{ + OpType opType = OP_UNKNOWN; + if( (/*val_rip >= 0x00 &&*/ val_rip <= 0x07) || + (val_rip >= 0x40 && val_rip <= 0x47) ) /* add $,(X) */ + opType = OP_STORE; + if( (val_rip >= 0x08 && val_rip <= 0x0f) || + (val_rip >= 0x48 && val_rip <= 0x4f) ) /* or $,(X) */ + opType = OP_STORE; + if( (val_rip >= 0x20 && val_rip <= 0x27) || + (val_rip >= 0x60 && val_rip <= 0x67) ) /* and $,(X) */ + opType = OP_STORE; + if( (val_rip >= 0x28 && val_rip <= 0x2f) || + (val_rip >= 0x68 && val_rip <= 0x6f) ) /* sub $,(X) */ + opType = OP_STORE; + if( (val_rip >= 0x30 && val_rip <= 0x37) || + (val_rip >= 0x70 && val_rip <= 0x77) ) /* xor $,(X) */ + opType = OP_STORE; + if( (val_rip >= 0x38 && val_rip <= 0x3f) || + (val_rip >= 0x78 && val_rip <= 0x7f) ) /* cmp $,(X) */ + opType = OP_LOAD; + return opType; +} +#endif + +/************************************************************************/ +/* CPLVirtualMemGetOpType() */ +/************************************************************************/ + +/* We don't need exhaustivity. It is just a hint for an optimization : */ +/* if the fault occurs on a store operation, then we can directly put */ +/* the page in writable mode if the mapping allows it */ + +static OpType CPLVirtualMemGetOpType(const GByte* rip) +{ + OpType opType = OP_UNKNOWN; + +#if defined(__x86_64__) || defined(__i386__) + switch(rip[0]) + { + case 0x00: /* add %al,(%rax) */ + case 0x01: /* add %eax,(%rax) */ + opType = OP_STORE; + break; + case 0x02: /* add (%rax),%al */ + case 0x03: /* add (%rax),%eax */ + opType = OP_LOAD; + break; + + case 0x08: /* or %al,(%rax) */ + case 0x09: /* or %eax,(%rax) */ + opType = OP_STORE; + break; + case 0x0a: /* or (%rax),%al */ + case 0x0b: /* or (%rax),%eax */ + opType = OP_LOAD; + break; + + case 0x0f: + { + switch(rip[1]) + { + case 0xb6: /* movzbl (%rax),%eax */ + case 0xb7: /* movzwl (%rax),%eax */ + case 0xbe: /* movsbl (%rax),%eax */ + case 0xbf: /* movswl (%rax),%eax */ + opType = OP_LOAD; + break; + default: + break; + } + break; + } + case 0xc6: /* movb $,(%rax) */ + case 0xc7: /* movl $,(%rax) */ + opType = OP_STORE; + break; + + case 0x20: /* and %al,(%rax) */ + case 0x21: /* and %eax,(%rax) */ + opType = OP_STORE; + break; + case 0x22: /* and (%rax),%al */ + case 0x23: /* and (%rax),%eax */ + opType = OP_LOAD; + break; + + case 0x28: /* sub %al,(%rax) */ + case 0x29: /* sub %eax,(%rax) */ + opType = OP_STORE; + break; + case 0x2a: /* sub (%rax),%al */ + case 0x2b: /* sub (%rax),%eax */ + opType = OP_LOAD; + break; + + case 0x30: /* xor %al,(%rax) */ + case 0x31: /* xor %eax,(%rax) */ + opType = OP_STORE; + break; + case 0x32: /* xor (%rax),%al */ + case 0x33: /* xor (%rax),%eax */ + opType = OP_LOAD; + break; + + case 0x38: /* cmp %al,(%rax) */ + case 0x39: /* cmp %eax,(%rax) */ + opType = OP_LOAD; + break; + case 0x40: + { + switch(rip[1]) + { + case 0x00: /* add %spl,(%rax) */ + opType = OP_STORE; + break; + case 0x02: /* add (%rax),%spl */ + opType = OP_LOAD; + break; + case 0x28: /* sub %spl,(%rax) */ + opType = OP_STORE; + break; + case 0x2a: /* sub (%rax),%spl */ + opType = OP_LOAD; + break; + case 0x3a: /* cmp (%rax),%spl */ + opType = OP_LOAD; + break; + case 0x8a: /* mov (%rax),%spl */ + opType = OP_LOAD; + break; + default: + break; + } + break; + } +#if defined(__x86_64__) + case 0x41: /* reg=%al/%eax, X=%r8 */ + case 0x42: /* reg=%al/%eax, X=%rax,%r8,1 */ + case 0x43: /* reg=%al/%eax, X=%r8,%r8,1 */ + case 0x44: /* reg=%r8b/%r8w, X = %rax */ + case 0x45: /* reg=%r8b/%r8w, X = %r8 */ + case 0x46: /* reg=%r8b/%r8w, X = %rax,%r8,1 */ + case 0x47: /* reg=%r8b/%r8w, X = %r8,%r8,1 */ + { + switch(rip[1]) + { + case 0x00: /* add regb,(X) */ + case 0x01: /* add regl,(X) */ + opType = OP_STORE; + break; + case 0x02: /* add (X),regb */ + case 0x03: /* add (X),regl */ + opType = OP_LOAD; + break; + case 0x0f: + { + switch(rip[2]) + { + case 0xb6: /* movzbl (X),regl */ + case 0xb7: /* movzwl (X),regl */ + case 0xbe: /* movsbl (X),regl */ + case 0xbf: /* movswl (X),regl */ + opType = OP_LOAD; + break; + default: + break; + } + break; + } + case 0x28: /* sub regb,(X) */ + case 0x29: /* sub regl,(X) */ + opType = OP_STORE; + break; + case 0x2a: /* sub (X),regb */ + case 0x2b: /* sub (X),regl */ + opType = OP_LOAD; + break; + case 0x38: /* cmp regb,(X) */ + case 0x39: /* cmp regl,(X) */ + opType = OP_LOAD; + break; + case 0x80: /* cmpb,... $,(X) */ + case 0x81: /* cmpl,... $,(X) */ + case 0x83: /* cmpl,... $,(X) */ + opType = CPLVirtualMemGetOpTypeImm(rip[2]); + break; + case 0x88: /* mov regb,(X) */ + case 0x89: /* mov regl,(X) */ + opType = OP_STORE; + break; + case 0x8a: /* mov (X),regb */ + case 0x8b: /* mov (X),regl */ + opType = OP_LOAD; + break; + case 0xc6: /* movb $,(X) */ + case 0xc7: /* movl $,(X) */ + opType = OP_STORE; + break; + case 0x84: /* test %al,(X) */ + opType = OP_LOAD; + break; + case 0xf6: /* testb $,(X) or notb (X) */ + case 0xf7: /* testl $,(X) or notl (X)*/ + { + if( rip[2] < 0x10 ) /* test (X) */ + opType = OP_LOAD; + else /* not (X) */ + opType = OP_STORE; + break; + } + default: + break; + } + break; + } + case 0x48: /* reg=%rax, X=%rax or %rax,%rax,1 */ + case 0x49: /* reg=%rax, X=%r8 or %r8,%rax,1 */ + case 0x4a: /* reg=%rax, X=%rax,%r8,1 */ + case 0x4b: /* reg=%rax, X=%r8,%r8,1 */ + case 0x4c: /* reg=%r8, X=%rax or %rax,%rax,1 */ + case 0x4d: /* reg=%r8, X=%r8 or %r8,%rax,1 */ + case 0x4e: /* reg=%r8, X=%rax,%r8,1 */ + case 0x4f: /* reg=%r8, X=%r8,%r8,1 */ + { + switch(rip[1]) + { + case 0x01: /* add reg,(X) */ + opType = OP_STORE; + break; + case 0x03: /* add (X),reg */ + opType = OP_LOAD; + break; + + case 0x09: /* or reg,(%rax) */ + opType = OP_STORE; + break; + case 0x0b: /* or (%rax),reg */ + opType = OP_LOAD; + break; + case 0x0f: + { + switch(rip[2]) + { + case 0xc3: /* movnti reg,(X) */ + opType = OP_STORE; + break; + default: + break; + } + break; + } + case 0x21: /* and reg,(X) */ + opType = OP_STORE; + break; + case 0x23: /* and (X),reg */ + opType = OP_LOAD; + break; + + case 0x29: /* sub reg,(X) */ + opType = OP_STORE; + break; + case 0x2b: /* sub (X),reg */ + opType = OP_LOAD; + break; + + case 0x31: /* xor reg,(X) */ + opType = OP_STORE; + break; + case 0x33: /* xor (X),reg */ + opType = OP_LOAD; + break; + + case 0x39: /* cmp reg,(X) */ + opType = OP_LOAD; + break; + + case 0x81: + case 0x83: + opType = CPLVirtualMemGetOpTypeImm(rip[2]); + break; + + case 0x85: /* test reg,(X) */ + opType = OP_LOAD; + break; + + case 0x89: /* mov reg,(X) */ + opType = OP_STORE; + break; + case 0x8b: /* mov (X),reg */ + opType = OP_LOAD; + break; + + case 0xc7: /* movq $,(X) */ + opType = OP_STORE; + break; + + case 0xf7: + { + if( rip[2] < 0x10 ) /* testq $,(X) */ + opType = OP_LOAD; + else /* notq (X) */ + opType = OP_STORE; + break; + } + default: + break; + } + break; + } +#endif + case 0x66: + { + switch(rip[1]) + { + case 0x01: /* add %ax,(%rax) */ + opType = OP_STORE; + break; + case 0x03: /* add (%rax),%ax */ + opType = OP_LOAD; + break; + case 0x0f: + { + switch(rip[2]) + { + case 0x2e: /* ucomisd (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x6f: /* movdqa (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x7f: /* movdqa %xmm0,(%rax) */ + opType = OP_STORE; + break; + case 0xb6: /* movzbw (%rax),%ax */ + opType = OP_LOAD; + break; + case 0xe7: /* movntdq %xmm0,(%rax) */ + opType = OP_STORE; + break; + default: + break; + } + break; + } + case 0x29: /* sub %ax,(%rax) */ + opType = OP_STORE; + break; + case 0x2b: /* sub (%rax),%ax */ + opType = OP_LOAD; + break; + case 0x39: /* cmp %ax,(%rax) */ + opType = OP_LOAD; + break; +#if defined(__x86_64__) + case 0x41: /* reg = %ax (or %xmm0), X = %r8 */ + case 0x42: /* reg = %ax (or %xmm0), X = %rax,%r8,1 */ + case 0x43: /* reg = %ax (or %xmm0), X = %r8,%r8,1 */ + case 0x44: /* reg = %r8w (or %xmm8), X = %rax */ + case 0x45: /* reg = %r8w (or %xmm8), X = %r8 */ + case 0x46: /* reg = %r8w (or %xmm8), X = %rax,%r8,1 */ + case 0x47: /* reg = %r8w (or %xmm8), X = %r8,%r8,1 */ + { + switch(rip[2]) + { + case 0x01: /* add reg,(X) */ + opType = OP_STORE; + break; + case 0x03: /* add (X),reg */ + opType = OP_LOAD; + break; + case 0x0f: + { + switch(rip[3]) + { + case 0x2e: /* ucomisd (X),reg */ + opType = OP_LOAD; + break; + case 0x6f: /* movdqa (X),reg */ + opType = OP_LOAD; + break; + case 0x7f: /* movdqa reg,(X) */ + opType = OP_STORE; + break; + case 0xb6: /* movzbw (X),reg */ + opType = OP_LOAD; + break; + case 0xe7: /* movntdq reg,(X) */ + opType = OP_STORE; + break; + default: + break; + } + break; + } + case 0x29: /* sub reg,(X) */ + opType = OP_STORE; + break; + case 0x2b: /* sub (X),reg */ + opType = OP_LOAD; + break; + case 0x39: /* cmp reg,(X) */ + opType = OP_LOAD; + break; + case 0x81: /* cmpw,... $,(X) */ + case 0x83: /* cmpw,... $,(X) */ + opType = CPLVirtualMemGetOpTypeImm(rip[3]); + break; + case 0x85: /* test reg,(X) */ + opType = OP_LOAD; + break; + case 0x89: /* mov reg,(X) */ + opType = OP_STORE; + break; + case 0x8b: /* mov (X),reg */ + opType = OP_LOAD; + break; + case 0xc7: /* movw $,(X) */ + opType = OP_STORE; + break; + case 0xf7: + { + if( rip[3] < 0x10 ) /* testw $,(X) */ + opType = OP_LOAD; + else /* notw (X) */ + opType = OP_STORE; + break; + } + default: + break; + } + break; + } +#endif + case 0x81: /* cmpw,... $,(%rax) */ + case 0x83: /* cmpw,... $,(%rax) */ + opType = CPLVirtualMemGetOpTypeImm(rip[2]); + break; + + case 0x85: /* test %ax,(%rax) */ + opType = OP_LOAD; + break; + case 0x89: /* mov %ax,(%rax) */ + opType = OP_STORE; + break; + case 0x8b: /* mov (%rax),%ax */ + opType = OP_LOAD; + break; + case 0xc7: /* movw $,(%rax) */ + opType = OP_STORE; + break; + case 0xf3: + { + switch( rip[2] ) + { + case 0xa5: /* rep movsw %ds:(%rsi),%es:(%rdi) */ + opType = OP_MOVS_RSI_RDI; + break; + default: + break; + } + break; + } + case 0xf7: /* testw $,(%rax) or notw (%rax) */ + { + if( rip[2] < 0x10 ) /* test */ + opType = OP_LOAD; + else /* not */ + opType = OP_STORE; + break; + } + default: + break; + } + break; + } + case 0x80: /* cmpb,... $,(%rax) */ + case 0x81: /* cmpl,... $,(%rax) */ + case 0x83: /* cmpl,... $,(%rax) */ + opType = CPLVirtualMemGetOpTypeImm(rip[1]); + break; + case 0x84: /* test %al,(%rax) */ + case 0x85: /* test %eax,(%rax) */ + opType = OP_LOAD; + break; + case 0x88: /* mov %al,(%rax) */ + opType = OP_STORE; + break; + case 0x89: /* mov %eax,(%rax) */ + opType = OP_STORE; + break; + case 0x8a: /* mov (%rax),%al */ + opType = OP_LOAD; + break; + case 0x8b: /* mov (%rax),%eax */ + opType = OP_LOAD; + break; + case 0xd9: /* 387 float */ + { + if( rip[1] < 0x08 ) /* flds (%eax) */ + opType = OP_LOAD; + else if( rip[1] >= 0x18 && rip[1] <= 0x20 ) /* fstps (%eax) */ + opType = OP_STORE; + break; + } + case 0xf2: /* SSE 2 */ + { + switch(rip[1]) + { + case 0x0f: + { + switch(rip[2]) + { + case 0x10: /* movsd (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x11: /* movsd %xmm0,(%rax) */ + opType = OP_STORE; + break; + case 0x58: /* addsd (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x59: /* mulsd (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x5c: /* subsd (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x5e: /* divsd (%rax),%xmm0 */ + opType = OP_LOAD; + break; + default: + break; + } + break; + } +#if defined(__x86_64__) + case 0x41: /* reg=%xmm0, X=%r8 or %r8,%rax,1 */ + case 0x42: /* reg=%xmm0, X=%rax,%r8,1 */ + case 0x43: /* reg=%xmm0, X=%r8,%r8,1 */ + case 0x44: /* reg=%xmm8, X=%rax or %rax,%rax,1*/ + case 0x45: /* reg=%xmm8, X=%r8 or %r8,%rax,1 */ + case 0x46: /* reg=%xmm8, X=%rax,%r8,1 */ + case 0x47: /* reg=%xmm8, X=%r8,%r8,1 */ + { + switch(rip[2]) + { + case 0x0f: + { + switch(rip[3]) + { + case 0x10: /* movsd (X),reg */ + opType = OP_LOAD; + break; + case 0x11: /* movsd reg,(X) */ + opType = OP_STORE; + break; + case 0x58: /* addsd (X),reg */ + opType = OP_LOAD; + break; + case 0x59: /* mulsd (X),reg */ + opType = OP_LOAD; + break; + case 0x5c: /* subsd (X),reg */ + opType = OP_LOAD; + break; + case 0x5e: /* divsd (X),reg */ + opType = OP_LOAD; + break; + default: + break; + } + break; + } + default: + break; + } + break; + } +#endif + default: + break; + } + break; + } + case 0xf3: + { + switch(rip[1]) + { + case 0x0f: /* SSE 2 */ + { + switch(rip[2]) + { + case 0x10: /* movss (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x11: /* movss %xmm0,(%rax) */ + opType = OP_STORE; + break; + case 0x6f: /* movdqu (%rax),%xmm0 */ + opType = OP_LOAD; + break; + case 0x7f: /* movdqu %xmm0,(%rax) */ + opType = OP_STORE; + break; + default: + break; + } + break; + } +#if defined(__x86_64__) + case 0x41: /* reg=%xmm0, X=%r8 */ + case 0x42: /* reg=%xmm0, X=%rax,%r8,1 */ + case 0x43: /* reg=%xmm0, X=%r8,%r8,1 */ + case 0x44: /* reg=%xmm8, X = %rax */ + case 0x45: /* reg=%xmm8, X = %r8 */ + case 0x46: /* reg=%xmm8, X = %rax,%r8,1 */ + case 0x47: /* reg=%xmm8, X = %r8,%r8,1 */ + { + switch(rip[2]) + { + case 0x0f: /* SSE 2 */ + { + switch(rip[3]) + { + case 0x10: /* movss (X),reg */ + opType = OP_LOAD; + break; + case 0x11: /* movss reg,(X) */ + opType = OP_STORE; + break; + case 0x6f: /* movdqu (X),reg */ + opType = OP_LOAD; + break; + case 0x7f: /* movdqu reg,(X) */ + opType = OP_STORE; + break; + default: + break; + } + break; + } + default: + break; + } + break; + } + case 0x48: + { + switch(rip[2]) + { + case 0xa5: /* rep movsq %ds:(%rsi),%es:(%rdi) */ + opType = OP_MOVS_RSI_RDI; + break; + default: + break; + } + break; + } +#endif + case 0xa4: /* rep movsb %ds:(%rsi),%es:(%rdi) */ + case 0xa5: /* rep movsl %ds:(%rsi),%es:(%rdi) */ + opType = OP_MOVS_RSI_RDI; + break; + case 0xa6: /* repz cmpsb %es:(%rdi),%ds:(%rsi) */ + opType = OP_LOAD; + break; + default: + break; + } + break; + } + case 0xf6: /* testb $,(%rax) or notb (%rax) */ + case 0xf7: /* testl $,(%rax) or notl (%rax) */ + { + if( rip[1] < 0x10 ) /* test */ + opType = OP_LOAD; + else /* not */ + opType = OP_STORE; + break; + } + default: + break; + } +#endif + return opType; +} + +/************************************************************************/ +/* CPLVirtualMemManagerPinAddrInternal() */ +/************************************************************************/ + +static int CPLVirtualMemManagerPinAddrInternal(CPLVirtualMemMsgToWorkerThread* msg) +{ + char wait_ready; + char response_buf[4]; + + /* Wait for the helper thread to be ready to process another request */ + while(TRUE) + { + int ret = read(pVirtualMemManager->pipefd_wait_thread[0], &wait_ready, 1); + if( ret < 0 && errno == EINTR ) + ; + else + { + assert(ret == 1); + break; + } + } + + /* Pass the address that caused the fault to the helper thread */ + assert(write(pVirtualMemManager->pipefd_to_thread[1], msg, sizeof(*msg)) + == sizeof(*msg)); + + /* Wait that the helper thread has fixed the fault */ + while(TRUE) + { + int ret = read(pVirtualMemManager->pipefd_from_thread[0], response_buf, 4); + if( ret < 0 && errno == EINTR ) + ; + else + { + assert(ret == 4); + break; + } + } + + /* In case the helper thread did not recognize the address as being */ + /* one that it should take care of, just rely on the previous SIGSEGV */ + /* handler (with might abort the process) */ + return( memcmp(response_buf, MAPPING_FOUND, 4) == 0 ); +} + +/************************************************************************/ +/* CPLVirtualMemPin() */ +/************************************************************************/ + +void CPLVirtualMemPin(CPLVirtualMem* ctxt, + void* pAddr, size_t nSize, int bWriteOp) +{ + if( ctxt->bFileMemoryMapped ) + return; + + CPLVirtualMemMsgToWorkerThread msg; + size_t i = 0, n; + + memset(&msg, 0, sizeof(msg)); + msg.hRequesterThread = pthread_self(); + msg.opType = (bWriteOp) ? OP_STORE : OP_LOAD; + + char* pBase = (char*)ALIGN_DOWN(pAddr, ctxt->nPageSize); + n = ((char*)pAddr - pBase + nSize + ctxt->nPageSize - 1) / ctxt->nPageSize; + for(i=0; inPageSize; + CPLVirtualMemManagerPinAddrInternal(&msg); + } +} + +/************************************************************************/ +/* CPLVirtualMemManagerSIGSEGVHandler() */ +/************************************************************************/ + +#if defined(__x86_64__) +#define REG_IP REG_RIP +#define REG_SI REG_RSI +#define REG_DI REG_RDI +#elif defined(__i386__) +#define REG_IP REG_EIP +#define REG_SI REG_ESI +#define REG_DI REG_EDI +#endif + +/* We must take care of only using "asynchronous-signal-safe" functions in a signal handler */ +/* pthread_self(), read() and write() are such. See */ +/* https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers */ +static void CPLVirtualMemManagerSIGSEGVHandler(int the_signal, + siginfo_t* the_info, + void* the_ctxt) +{ + CPLVirtualMemMsgToWorkerThread msg; + + memset(&msg, 0, sizeof(msg)); + msg.pFaultAddr = the_info->si_addr; + msg.hRequesterThread = pthread_self(); + msg.opType = OP_UNKNOWN; + +#if defined(__x86_64__) || defined(__i386__) + ucontext_t* the_ucontext = (ucontext_t* )the_ctxt; + const GByte* rip = (const GByte*)the_ucontext->uc_mcontext.gregs[REG_IP]; + msg.opType = CPLVirtualMemGetOpType(rip); + /*fprintfstderr("at rip %p, bytes: %02x %02x %02x %02x\n", + rip, rip[0], rip[1], rip[2], rip[3]);*/ + if( msg.opType == OP_MOVS_RSI_RDI ) + { + void* rsi = (void*)the_ucontext->uc_mcontext.gregs[REG_SI]; + void* rdi = (void*)the_ucontext->uc_mcontext.gregs[REG_DI]; + + /*fprintfstderr("fault=%p rsi=%p rsi=%p\n", msg.pFaultAddr, rsi, rdi);*/ + if( msg.pFaultAddr == rsi ) + { + /*fprintfstderr("load\n");*/ + msg.opType = OP_LOAD; + } + else if( msg.pFaultAddr == rdi ) + { + /*fprintfstderr("store\n");*/ + msg.opType = OP_STORE; + } + } +#ifdef DEBUG_VIRTUALMEM + else if( msg.opType == OP_UNKNOWN ) + { + static int bHasWarned = FALSE; + if( !bHasWarned ) + { + bHasWarned = TRUE; + fprintfstderr("at rip %p, unknown bytes: %02x %02x %02x %02x\n", + rip, rip[0], rip[1], rip[2], rip[3]); + } + } +#endif +#endif + + /*fprintfstderr("entering handler for %X (addr=%p)\n", pthread_self(), the_info->si_addr); */ + + if( the_info->si_code != SEGV_ACCERR ) + { + pVirtualMemManager->oldact.sa_sigaction(the_signal, the_info, the_ctxt); + return; + } + + if( !CPLVirtualMemManagerPinAddrInternal(&msg) ) + { + /* In case the helper thread did not recognize the address as being */ + /* one that it should take care of, just rely on the previous SIGSEGV */ + /* handler (with might abort the process) */ + pVirtualMemManager->oldact.sa_sigaction(the_signal, the_info, the_ctxt); + } + + /*fprintfstderr("leaving handler for %X (addr=%p)\n", pthread_self(), the_info->si_addr);*/ +} + +/************************************************************************/ +/* CPLVirtualMemManagerThread() */ +/************************************************************************/ + +static void CPLVirtualMemManagerThread(void* unused_param) +{ + (void)unused_param; + + while(TRUE) + { + char i_m_ready = 1; + int i; + CPLVirtualMem* ctxt = NULL; + int bMappingFound = FALSE; + CPLVirtualMemMsgToWorkerThread msg; + + /* Signal that we are ready to process a new request */ + assert(write(pVirtualMemManager->pipefd_wait_thread[1], &i_m_ready, 1) == 1); + + /* Fetch the address to process */ + assert(read(pVirtualMemManager->pipefd_to_thread[0], &msg, + sizeof(msg)) == sizeof(msg)); + + /* If CPLVirtualMemManagerTerminate() is called, it will use BYEBYE_ADDR as a */ + /* means to ask for our termination */ + if( msg.pFaultAddr == BYEBYE_ADDR ) + break; + + /* Lookup for a mapping that contains addr */ + CPLAcquireMutex(hVirtualMemManagerMutex, 1000.0); + for(i=0;inVirtualMemCount;i++) + { + ctxt = pVirtualMemManager->pasVirtualMem[i]; + if( (char*)msg.pFaultAddr >= (char*) ctxt->pData && + (char*)msg.pFaultAddr < (char*) ctxt->pData + ctxt->nSize ) + { + bMappingFound = TRUE; + break; + } + } + CPLReleaseMutex(hVirtualMemManagerMutex); + + if( bMappingFound ) + { + char* start_page_addr = (char*)ALIGN_DOWN(msg.pFaultAddr, ctxt->nPageSize); + int iPage = (int) + (((char*)start_page_addr - (char*)ctxt->pData) / ctxt->nPageSize); + + if( iPage == ctxt->iLastPage ) + { + /* In case 2 threads try to access the same page */ + /* concurrently it is possible that we are asked to mapped */ + /* the page again whereas it is always mapped. However */ + /* if that number of successive retries is too high, this */ + /* is certainly a sign that something else happen, like */ + /* trying to write-access a read-only page */ + /* 100 is a bit of magic number. I believe it must be */ + /* at least the number of concurrent threads. 100 seems */ + /* to be really safe ! */ + ctxt->nRetry ++; + /* fprintfstderr("retry on page %d : %d\n", (int)iPage, ctxt->nRetry); */ + if( ctxt->nRetry >= 100 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "CPLVirtualMemManagerThread: trying to " + "write into read-only mapping"); + assert(write(pVirtualMemManager->pipefd_from_thread[1], + MAPPING_NOT_FOUND, 4) == 4); + break; + } + else if( msg.opType != OP_LOAD && + ctxt->eAccessMode == VIRTUALMEM_READWRITE && + !TEST_BIT(ctxt->pabitRWMappedPages, iPage) ) + { + /* fprintfstderr("switching page %d to write mode\n", (int)iPage); */ + SET_BIT(ctxt->pabitRWMappedPages, iPage); + assert(mprotect(start_page_addr, ctxt->nPageSize, + PROT_READ | PROT_WRITE) == 0); + } + } + else + { + ctxt->iLastPage = iPage; + ctxt->nRetry = 0; + + if( TEST_BIT(ctxt->pabitMappedPages, iPage) ) + { + if( msg.opType != OP_LOAD && + ctxt->eAccessMode == VIRTUALMEM_READWRITE && + !TEST_BIT(ctxt->pabitRWMappedPages, iPage) ) + { + /*fprintfstderr("switching page %d to write mode\n", + iPage);*/ + SET_BIT(ctxt->pabitRWMappedPages, iPage); + assert(mprotect(start_page_addr, ctxt->nPageSize, + PROT_READ | PROT_WRITE) == 0); + } + else + { + /*fprintfstderr("unexpected case for page %d\n", + iPage);*/ + } + } + else + { + void* pPageToFill; + size_t nToFill; + pPageToFill = CPLVirtualMemGetPageToFill(ctxt, start_page_addr); + + nToFill = ctxt->nPageSize; + if( start_page_addr + nToFill >= (char*) ctxt->pData + ctxt->nSize ) + nToFill = (char*) ctxt->pData + ctxt->nSize - start_page_addr; + + ctxt->pfnCachePage( + ctxt, + start_page_addr - (char*) ctxt->pData, + pPageToFill, + nToFill, + ctxt->pCbkUserData); + + /* Now remap this page to its target address and */ + /* register it in the LRU */ + CPLVirtualMemAddPage(ctxt, start_page_addr, pPageToFill, + msg.opType, msg.hRequesterThread); + } + } + + /* Warn the segfault handler that we have finished our job */ + assert(write(pVirtualMemManager->pipefd_from_thread[1], + MAPPING_FOUND, 4) == 4); + } + else + { + /* Warn the segfault handler that we have finished our job */ + /* but that the fault didn't occur in a memory range that is under */ + /* our responsability */ + CPLError(CE_Failure, CPLE_AppDefined, + "CPLVirtualMemManagerThread: no mapping found"); + assert(write(pVirtualMemManager->pipefd_from_thread[1], + MAPPING_NOT_FOUND, 4) == 4); + } + } +} + +/************************************************************************/ +/* CPLVirtualMemManagerInit() */ +/************************************************************************/ + +static void CPLVirtualMemManagerInit(void) +{ + CPLMutexHolderD(&hVirtualMemManagerMutex); + if( pVirtualMemManager != NULL ) + return; + + struct sigaction act; + pVirtualMemManager = (CPLVirtualMemManager*) CPLMalloc(sizeof(CPLVirtualMemManager)); + pVirtualMemManager->pasVirtualMem = NULL; + pVirtualMemManager->nVirtualMemCount = 0; + assert(pipe(pVirtualMemManager->pipefd_to_thread) == 0); + assert(pipe(pVirtualMemManager->pipefd_from_thread) == 0); + assert(pipe(pVirtualMemManager->pipefd_wait_thread) == 0); + + /* Install our custom SIGSEGV handler */ + act.sa_sigaction = CPLVirtualMemManagerSIGSEGVHandler; + sigemptyset (&act.sa_mask); + act.sa_flags = SA_SIGINFO; + assert(sigaction(SIGSEGV, &act, &pVirtualMemManager->oldact) == 0); + + /* Starts the helper thread */ + pVirtualMemManager->hHelperThread = + CPLCreateJoinableThread(CPLVirtualMemManagerThread, NULL); + assert(pVirtualMemManager->hHelperThread != NULL); +} + +/************************************************************************/ +/* CPLVirtualMemManagerTerminate() */ +/************************************************************************/ + +void CPLVirtualMemManagerTerminate(void) +{ + char wait_ready; + CPLVirtualMemMsgToWorkerThread msg; + + if( pVirtualMemManager == NULL ) + return; + + msg.pFaultAddr = BYEBYE_ADDR; + msg.opType = OP_UNKNOWN; + memset(&msg.hRequesterThread, 0, sizeof(msg.hRequesterThread)); + + /* Wait for the helper thread to be ready */ + assert(read(pVirtualMemManager->pipefd_wait_thread[0], + &wait_ready, 1) == 1); + + /* Ask it to terminate */ + assert(write(pVirtualMemManager->pipefd_to_thread[1], &msg, sizeof(msg)) == sizeof(msg)); + + /* Wait for its termination */ + CPLJoinThread(pVirtualMemManager->hHelperThread); + + /* Cleanup everything */ + while(pVirtualMemManager->nVirtualMemCount > 0) + CPLVirtualMemFree(pVirtualMemManager->pasVirtualMem[pVirtualMemManager->nVirtualMemCount - 1]); + CPLFree(pVirtualMemManager->pasVirtualMem); + + close(pVirtualMemManager->pipefd_to_thread[0]); + close(pVirtualMemManager->pipefd_to_thread[1]); + close(pVirtualMemManager->pipefd_from_thread[0]); + close(pVirtualMemManager->pipefd_from_thread[1]); + close(pVirtualMemManager->pipefd_wait_thread[0]); + close(pVirtualMemManager->pipefd_wait_thread[1]); + + /* Restore previous handler */ + sigaction(SIGSEGV, &pVirtualMemManager->oldact, NULL); + + CPLFree(pVirtualMemManager); + pVirtualMemManager = NULL; + + CPLDestroyMutex(hVirtualMemManagerMutex); + hVirtualMemManagerMutex = NULL; +} + +#else /* defined(__linux) && defined(CPL_MULTIPROC_PTHREAD) */ + +size_t CPLGetPageSize(void) +{ + return 0; +} + +CPLVirtualMem *CPLVirtualMemNew(CPL_UNUSED size_t nSize, + CPL_UNUSED size_t nCacheSize, + CPL_UNUSED size_t nPageSizeHint, + CPL_UNUSED int bSingleThreadUsage, + CPL_UNUSED CPLVirtualMemAccessMode eAccessMode, + CPL_UNUSED CPLVirtualMemCachePageCbk pfnCachePage, + CPL_UNUSED CPLVirtualMemUnCachePageCbk pfnUnCachePage, + CPL_UNUSED CPLVirtualMemFreeUserData pfnFreeUserData, + CPL_UNUSED void *pCbkUserData) +{ + CPLError(CE_Failure, CPLE_NotSupported, + "CPLVirtualMemNew() unsupported on this operating system / configuration"); + return NULL; +} + +int CPLIsVirtualMemFileMapAvailable(void) +{ + return FALSE; +} + +CPLVirtualMem *CPLVirtualMemFileMapNew(CPL_UNUSED VSILFILE* fp, + CPL_UNUSED vsi_l_offset nOffset, + CPL_UNUSED vsi_l_offset nLength, + CPL_UNUSED CPLVirtualMemAccessMode eAccessMode, + CPL_UNUSED CPLVirtualMemFreeUserData pfnFreeUserData, + CPL_UNUSED void *pCbkUserData) +{ + CPLError(CE_Failure, CPLE_NotSupported, + "CPLVirtualMemFileMapNew() unsupported on this operating system / configuration"); + return NULL; +} + +CPLVirtualMem *CPLVirtualMemDerivedNew(CPL_UNUSED CPLVirtualMem* pVMemBase, + CPL_UNUSED vsi_l_offset nOffset, + CPL_UNUSED vsi_l_offset nSize, + CPL_UNUSED CPLVirtualMemFreeUserData pfnFreeUserData, + CPL_UNUSED void *pCbkUserData) +{ + CPLError(CE_Failure, CPLE_NotSupported, + "CPLVirtualMemDerivedNew() unsupported on this operating system / configuration"); + return NULL; +} + +void CPLVirtualMemFree(CPL_UNUSED CPLVirtualMem* ctxt) +{ +} + +void* CPLVirtualMemGetAddr(CPL_UNUSED CPLVirtualMem* ctxt) +{ + return NULL; +} + +size_t CPLVirtualMemGetSize(CPL_UNUSED CPLVirtualMem* ctxt) +{ + return 0; +} + +int CPLVirtualMemIsFileMapping(CPL_UNUSED CPLVirtualMem* ctxt) +{ + return FALSE; +} + +CPLVirtualMemAccessMode CPLVirtualMemGetAccessMode(CPL_UNUSED CPLVirtualMem* ctxt) +{ + return VIRTUALMEM_READONLY; +} + +size_t CPLVirtualMemGetPageSize(CPL_UNUSED CPLVirtualMem* ctxt) +{ + return 0; +} + +int CPLVirtualMemIsAccessThreadSafe(CPL_UNUSED CPLVirtualMem* ctxt) +{ + return FALSE; +} + +void CPLVirtualMemDeclareThread(CPL_UNUSED CPLVirtualMem* ctxt) +{ +} + +void CPLVirtualMemUnDeclareThread(CPL_UNUSED CPLVirtualMem* ctxt) +{ +} + +void CPLVirtualMemPin(CPL_UNUSED CPLVirtualMem* ctxt, + CPL_UNUSED void* pAddr, + CPL_UNUSED size_t nSize, + CPL_UNUSED int bWriteOp) +{ +} + +void CPLVirtualMemManagerTerminate(void) +{ +} + +#endif /* defined(__linux) && defined(CPL_MULTIPROC_PTHREAD) */ diff --git a/bazaar/plugin/gdal/port/cpl_virtualmem.h b/bazaar/plugin/gdal/port/cpl_virtualmem.h new file mode 100644 index 000000000..2d6ed45bd --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_virtualmem.h @@ -0,0 +1,389 @@ +/********************************************************************** + * $Id: cpl_virtualmem.h 29330 2015-06-14 12:11:11Z rouault $ + * + * Name: cpl_virtualmem.h + * Project: CPL - Common Portability Library + * Purpose: Virtual memory + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef _CPL_VIRTUAL_MEM_INCLUDED +#define _CPL_VIRTUAL_MEM_INCLUDED + +#include "cpl_port.h" +#include "cpl_vsi.h" + +CPL_C_START + +/** + * \file cpl_virtualmem.h + * + * Virtual memory management. + * + * This file provides mechanism to define virtual memory mappings, whose content + * is allocated transparently and filled on-the-fly. Those virtual memory mappings + * can be much larger than the available RAM, but only parts of the virtual + * memory mapping, in the limit of the allowed the cache size, will actually be + * physically allocated. + * + * This exploits low-level mechanisms of the operating system (virtual memory + * allocation, page protection and handler of virtual memory exceptions). + * + * It is also possible to create a virtual memory mapping from a file or part + * of a file. + * + * The current implementation is Linux only. + */ + +/** Opaque type that represents a virtual memory mapping. */ +typedef struct CPLVirtualMem CPLVirtualMem; + +/** Callback triggered when a still unmapped page of virtual memory is accessed. + * The callback has the responsibility of filling the page with relevant values + * + * @param ctxt virtual memory handle. + * @param nOffset offset of the page in the memory mapping. + * @param pPageToFill address of the page to fill. Note that the address might + * be a temporary location, and not at CPLVirtualMemGetAddr() + nOffset. + * @param nToFill number of bytes of the page. + * @param pUserData user data that was passed to CPLVirtualMemNew(). + */ +typedef void (*CPLVirtualMemCachePageCbk)(CPLVirtualMem* ctxt, + size_t nOffset, + void* pPageToFill, + size_t nToFill, + void* pUserData); + +/** Callback triggered when a dirty mapped page is going to be freed. + * (saturation of cache, or termination of the virtual memory mapping). + * + * @param ctxt virtual memory handle. + * @param nOffset offset of the page in the memory mapping. + * @param pPageToBeEvicted address of the page that will be flushed. Note that the address might + * be a temporary location, and not at CPLVirtualMemGetAddr() + nOffset. + * @param nToBeEvicted number of bytes of the page. + * @param pUserData user data that was passed to CPLVirtualMemNew(). + */ +typedef void (*CPLVirtualMemUnCachePageCbk)(CPLVirtualMem* ctxt, + size_t nOffset, + const void* pPageToBeEvicted, + size_t nToBeEvicted, + void* pUserData); + +/** Callback triggered when a virtual memory mapping is destroyed. + * @param pUserData user data that was passed to CPLVirtualMemNew(). + */ +typedef void (*CPLVirtualMemFreeUserData)(void* pUserData); + +/** Access mode of a virtual memory mapping. */ +typedef enum +{ + /*! The mapping is meant at being read-only, but writes will not be prevented. + Note that any content written will be lost. */ + VIRTUALMEM_READONLY, + /*! The mapping is meant at being read-only, and this will be enforced + through the operating system page protection mechanism. */ + VIRTUALMEM_READONLY_ENFORCED, + /*! The mapping is meant at being read-write, and modified pages can be saved + thanks to the pfnUnCachePage callback */ + VIRTUALMEM_READWRITE +} CPLVirtualMemAccessMode; + + +/** Return the size of a page of virtual memory. + * + * @return the page size. + * + * @since GDAL 1.11 + */ +size_t CPL_DLL CPLGetPageSize(void); + +/** Create a new virtual memory mapping. + * + * This will reserve an area of virtual memory of size nSize, whose size + * might be potentially much larger than the physical memory available. Initially, + * no physical memory will be allocated. As soon as memory pages will be accessed, + * they will be allocated transparently and filled with the pfnCachePage callback. + * When the allowed cache size is reached, the least recently used pages will + * be unallocated. + * + * On Linux AMD64 platforms, the maximum value for nSize is 128 TB. + * On Linux x86 platforms, the maximum value for nSize is 2 GB. + * + * Only supported on Linux for now. + * + * Note that on Linux, this function will install a SIGSEGV handler. The + * original handler will be restored by CPLVirtualMemManagerTerminate(). + * + * @param nSize size in bytes of the virtual memory mapping. + * @param nCacheSize size in bytes of the maximum memory that will be really + * allocated (must ideally fit into RAM). + * @param nPageSizeHint hint for the page size. Must be a multiple of the + * system page size, returned by CPLGetPageSize(). + * Minimum value is generally 4096. Might be set to 0 to + * let the function determine a default page size. + * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads + * that will access the virtual memory mapping. This can + * optimize performance a bit. + * @param eAccessMode permission to use for the virtual memory mapping. + * @param pfnCachePage callback triggered when a still unmapped page of virtual + * memory is accessed. The callback has the responsibility + * of filling the page with relevant values. + * @param pfnUnCachePage callback triggered when a dirty mapped page is going to + * be freed (saturation of cache, or termination of the + * virtual memory mapping). Might be NULL. + * @param pfnFreeUserData callback that can be used to free pCbkUserData. Might be + * NULL + * @param pCbkUserData user data passed to pfnCachePage and pfnUnCachePage. + * + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ + +CPLVirtualMem CPL_DLL *CPLVirtualMemNew(size_t nSize, + size_t nCacheSize, + size_t nPageSizeHint, + int bSingleThreadUsage, + CPLVirtualMemAccessMode eAccessMode, + CPLVirtualMemCachePageCbk pfnCachePage, + CPLVirtualMemUnCachePageCbk pfnUnCachePage, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData); + + +/** Return if virtual memory mapping of a file is available. + * + * @return TRUE if virtual memory mapping of a file is available. + * @since GDAL 1.11 + */ +int CPL_DLL CPLIsVirtualMemFileMapAvailable(void); + +/** Create a new virtual memory mapping from a file. + * + * The file must be a "real" file recognized by the operating system, and not + * a VSI extended virtual file. + * + * In VIRTUALMEM_READWRITE mode, updates to the memory mapping will be written + * in the file. + * + * On Linux AMD64 platforms, the maximum value for nLength is 128 TB. + * On Linux x86 platforms, the maximum value for nLength is 2 GB. + * + * Only supported on Linux for now. + * + * @param fp Virtual file handle. + * @param nOffset Offset in the file to start the mapping from. + * @param nLength Length of the portion of the file to map into memory. + * @param eAccessMode Permission to use for the virtual memory mapping. This must + * be consistent with how the file has been opened. + * @param pfnFreeUserData callback that is called when the object is destroyed. + * @param pCbkUserData user data passed to pfnFreeUserData. + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ +CPLVirtualMem CPL_DLL *CPLVirtualMemFileMapNew( VSILFILE* fp, + vsi_l_offset nOffset, + vsi_l_offset nLength, + CPLVirtualMemAccessMode eAccessMode, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData ); + +/** Create a new virtual memory mapping derived from an other virtual memory + * mapping. + * + * This may be useful in case of creating mapping for pixel interleaved data. + * + * The new mapping takes a reference on the base mapping. + * + * @param pVMemBase Base virtual memory mapping + * @param nOffset Offset in the base virtual memory mapping from which to start + * the new mapping. + * @param nSize Size of the base virtual memory mapping to expose in the + * the new mapping. + * @param pfnFreeUserData callback that is called when the object is destroyed. + * @param pCbkUserData user data passed to pfnFreeUserData. + * @return a virtual memory object that must be freed by CPLVirtualMemFree(), + * or NULL in case of failure. + * + * @since GDAL 1.11 + */ +CPLVirtualMem CPL_DLL *CPLVirtualMemDerivedNew(CPLVirtualMem* pVMemBase, + vsi_l_offset nOffset, + vsi_l_offset nSize, + CPLVirtualMemFreeUserData pfnFreeUserData, + void *pCbkUserData); + +/** Free a virtual memory mapping. + * + * The pointer returned by CPLVirtualMemGetAddr() will no longer be valid. + * If the virtual memory mapping was created with read/write permissions and that + * they are dirty (i.e. modified) pages, they will be flushed through the + * pfnUnCachePage callback before being freed. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * + * @since GDAL 1.11 + */ +void CPL_DLL CPLVirtualMemFree(CPLVirtualMem* ctxt); + +/** Return the pointer to the start of a virtual memory mapping. + * + * The bytes in the range [p:p+CPLVirtualMemGetSize()-1] where p is the pointer + * returned by this function will be valid, until CPLVirtualMemFree() is called. + * + * Note that if a range of bytes used as an argument of a system call + * (such as read() or write()) contains pages that have not been "realized", the + * system call will fail with EFAULT. CPLVirtualMemPin() can be used to work + * around this issue. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @return the pointer to the start of a virtual memory mapping. + * + * @since GDAL 1.11 + */ +void CPL_DLL *CPLVirtualMemGetAddr(CPLVirtualMem* ctxt); + +/** Return the size of the virtual memory mapping. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @return the size of the virtual memory mapping. + * + * @since GDAL 1.11 + */ +size_t CPL_DLL CPLVirtualMemGetSize(CPLVirtualMem* ctxt); + +/** Return if the virtal memory mapping is a direct file mapping. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @return TRUE if the virtal memory mapping is a direct file mapping. + * + * @since GDAL 1.11 + */ +int CPL_DLL CPLVirtualMemIsFileMapping(CPLVirtualMem* ctxt); + +/** Return the access mode of the virtual memory mapping. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @return the access mode of the virtual memory mapping. + * + * @since GDAL 1.11 + */ +CPLVirtualMemAccessMode CPL_DLL CPLVirtualMemGetAccessMode(CPLVirtualMem* ctxt); + +/** Return the page size associated to a virtual memory mapping. + * + * The value returned will be at least CPLGetPageSize(), but potentially + * larger. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @return the page size + * + * @since GDAL 1.11 + */ +size_t CPL_DLL CPLVirtualMemGetPageSize(CPLVirtualMem* ctxt); + +/** Return TRUE if this memory mapping can be accessed safely from concurrent + * threads. + * + * The situation that can cause problems is when several threads try to access + * a page of the mapping that is not yet mapped. + * + * The return value of this function depends on whether bSingleThreadUsage has + * been set of not in CPLVirtualMemNew() and/or the implementation. + * + * On Linux, this will always return TRUE if bSingleThreadUsage = FALSE. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @return TRUE if this memory mapping can be accessed safely from concurrent + * threads. + * + * @since GDAL 1.11 + */ +int CPL_DLL CPLVirtualMemIsAccessThreadSafe(CPLVirtualMem* ctxt); + +/** Declare that a thread will access a virtual memory mapping. + * + * This function must be called by a thread that wants to access the + * content of a virtual memory mapping, except if the virtual memory mapping has + * been created with bSingleThreadUsage = TRUE. + * + * This function must be paired with CPLVirtualMemUnDeclareThread(). + * + * @param ctxt context returned by CPLVirtualMemNew(). + * + * @since GDAL 1.11 + */ +void CPL_DLL CPLVirtualMemDeclareThread(CPLVirtualMem* ctxt); + +/** Declare that a thread will stop accessing a virtual memory mapping. + * + * This function must be called by a thread that will no longer access the + * content of a virtual memory mapping, except if the virtual memory mapping has + * been created with bSingleThreadUsage = TRUE. + * + * This function must be paired with CPLVirtualMemDeclareThread(). + * + * @param ctxt context returned by CPLVirtualMemNew(). + * + * @since GDAL 1.11 + */ +void CPL_DLL CPLVirtualMemUnDeclareThread(CPLVirtualMem* ctxt); + +/** Make sure that a region of virtual memory will be realized. + * + * Calling this function is not required, but might be useful when debugging + * a process with tools like gdb or valgrind that do not naturally like + * segmentation fault signals. + * + * It is also needed when wanting to provide part of virtual memory mapping + * to a system call such as read() or write(). If read() or write() is called + * on a memory region not yet realized, the call will fail with EFAULT. + * + * @param ctxt context returned by CPLVirtualMemNew(). + * @param pAddr the memory region to pin. + * @param nSize the size of the memory region. + * @param bWriteOp set to TRUE if the memory are will be accessed in write mode. + * + * @since GDAL 1.11 + */ +void CPL_DLL CPLVirtualMemPin(CPLVirtualMem* ctxt, + void* pAddr, size_t nSize, int bWriteOp); + +/** Cleanup any resource and handlers related to virtual memory. + * + * This function must be called after the last CPLVirtualMem object has + * been freed. + * + * @since GDAL 2.0 + */ +void CPL_DLL CPLVirtualMemManagerTerminate(void); + + +CPL_C_END + +#endif /* _CPL_VIRTUAL_MEM_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_vsi.h b/bazaar/plugin/gdal/port/cpl_vsi.h new file mode 100644 index 000000000..23b359616 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsi.h @@ -0,0 +1,284 @@ +/****************************************************************************** + * $Id: cpl_vsi.h 28476 2015-02-13 14:40:11Z rouault $ + * + * Project: CPL - Common Portability Library + * Author: Frank Warmerdam, warmerdam@pobox.com + * Purpose: Include file defining Virtual File System (VSI) functions, a + * layer over POSIX file and other system services. + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_VSI_H_INCLUDED +#define CPL_VSI_H_INCLUDED + +#include "cpl_port.h" +/** + * \file cpl_vsi.h + * + * Standard C Covers + * + * The VSI functions are intended to be hookable aliases for Standard C + * I/O, memory allocation and other system functions. They are intended + * to allow virtualization of disk I/O so that non file data sources + * can be made to appear as files, and so that additional error trapping + * and reporting can be interested. The memory access API is aliased + * so that special application memory management services can be used. + * + * Is is intended that each of these functions retains exactly the same + * calling pattern as the original Standard C functions they relate to. + * This means we don't have to provide custom documentation, and also means + * that the default implementation is very simple. + */ + + +/* -------------------------------------------------------------------- */ +/* We need access to ``struct stat''. */ +/* -------------------------------------------------------------------- */ + +/* Unix */ +#if !defined(_WIN32) && !defined(_WIN32_WCE) +# include +#endif + +/* Windows */ +#if !defined(macos_pre10) && !defined(_WIN32_WCE) +# include +#endif + +/* Windows CE */ +#if defined(_WIN32_WCE) +# include +#endif + +CPL_C_START + +/* ==================================================================== */ +/* stdio file access functions. These may not support large */ +/* files, and don't necessarily go through the virtualization */ +/* API. */ +/* ==================================================================== */ + +FILE CPL_DLL * VSIFOpen( const char *, const char * ) CPL_WARN_UNUSED_RESULT; +int CPL_DLL VSIFClose( FILE * ); +int CPL_DLL VSIFSeek( FILE *, long, int ); +long CPL_DLL VSIFTell( FILE * ); +void CPL_DLL VSIRewind( FILE * ); +void CPL_DLL VSIFFlush( FILE * ); + +size_t CPL_DLL VSIFRead( void *, size_t, size_t, FILE * ); +size_t CPL_DLL VSIFWrite( const void *, size_t, size_t, FILE * ); +char CPL_DLL *VSIFGets( char *, int, FILE * ); +int CPL_DLL VSIFPuts( const char *, FILE * ); +int CPL_DLL VSIFPrintf( FILE *, const char *, ... ) CPL_PRINT_FUNC_FORMAT(2, 3); + +int CPL_DLL VSIFGetc( FILE * ); +int CPL_DLL VSIFPutc( int, FILE * ); +int CPL_DLL VSIUngetc( int, FILE * ); +int CPL_DLL VSIFEof( FILE * ); + +/* ==================================================================== */ +/* VSIStat() related. */ +/* ==================================================================== */ + +typedef struct stat VSIStatBuf; +int CPL_DLL VSIStat( const char *, VSIStatBuf * ); + +#ifdef _WIN32 +# define VSI_ISLNK(x) ( 0 ) /* N/A on Windows */ +# define VSI_ISREG(x) ((x) & S_IFREG) +# define VSI_ISDIR(x) ((x) & S_IFDIR) +# define VSI_ISCHR(x) ((x) & S_IFCHR) +# define VSI_ISBLK(x) ( 0 ) /* N/A on Windows */ +#else +# define VSI_ISLNK(x) S_ISLNK(x) +# define VSI_ISREG(x) S_ISREG(x) +# define VSI_ISDIR(x) S_ISDIR(x) +# define VSI_ISCHR(x) S_ISCHR(x) +# define VSI_ISBLK(x) S_ISBLK(x) +#endif + +/* ==================================================================== */ +/* 64bit stdio file access functions. If we have a big size */ +/* defined, then provide protypes for the large file API, */ +/* otherwise redefine to use the regular api. */ +/* ==================================================================== */ +typedef GUIntBig vsi_l_offset; + +/* Make VSIL_STRICT_ENFORCE active in DEBUG builds */ +#ifdef DEBUG +#define VSIL_STRICT_ENFORCE +#endif + +#ifdef VSIL_STRICT_ENFORCE +typedef struct _VSILFILE VSILFILE; +#else +typedef FILE VSILFILE; +#endif + +VSILFILE CPL_DLL * VSIFOpenL( const char *, const char * ) CPL_WARN_UNUSED_RESULT; +int CPL_DLL VSIFCloseL( VSILFILE * ); +int CPL_DLL VSIFSeekL( VSILFILE *, vsi_l_offset, int ); +vsi_l_offset CPL_DLL VSIFTellL( VSILFILE * ); +void CPL_DLL VSIRewindL( VSILFILE * ); +size_t CPL_DLL VSIFReadL( void *, size_t, size_t, VSILFILE * ); +int CPL_DLL VSIFReadMultiRangeL( int nRanges, void ** ppData, const vsi_l_offset* panOffsets, const size_t* panSizes, VSILFILE * ); +size_t CPL_DLL VSIFWriteL( const void *, size_t, size_t, VSILFILE * ); +int CPL_DLL VSIFEofL( VSILFILE * ); +int CPL_DLL VSIFTruncateL( VSILFILE *, vsi_l_offset ); +int CPL_DLL VSIFFlushL( VSILFILE * ); +int CPL_DLL VSIFPrintfL( VSILFILE *, const char *, ... ) CPL_PRINT_FUNC_FORMAT(2, 3); +int CPL_DLL VSIFPutcL( int, VSILFILE * ); + +int CPL_DLL VSIIngestFile( VSILFILE* fp, + const char* pszFilename, + GByte** ppabyRet, + vsi_l_offset* pnSize, + GIntBig nMaxSize ); + +#if defined(VSI_STAT64_T) +typedef struct VSI_STAT64_T VSIStatBufL; +#else +#define VSIStatBufL VSIStatBuf +#endif + +int CPL_DLL VSIStatL( const char *, VSIStatBufL * ); + +#define VSI_STAT_EXISTS_FLAG 0x1 +#define VSI_STAT_NATURE_FLAG 0x2 +#define VSI_STAT_SIZE_FLAG 0x4 + +int CPL_DLL VSIStatExL( const char * pszFilename, VSIStatBufL * psStatBuf, int nFlags ); + +int CPL_DLL VSIIsCaseSensitiveFS( const char * pszFilename ); + +void CPL_DLL *VSIFGetNativeFileDescriptorL( VSILFILE* ); + +/* ==================================================================== */ +/* Memory allocation */ +/* ==================================================================== */ + +void CPL_DLL *VSICalloc( size_t, size_t ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL *VSIMalloc( size_t ) CPL_WARN_UNUSED_RESULT; +void CPL_DLL VSIFree( void * ); +void CPL_DLL *VSIRealloc( void *, size_t ) CPL_WARN_UNUSED_RESULT; +char CPL_DLL *VSIStrdup( const char * ) CPL_WARN_UNUSED_RESULT; + +/** + VSIMalloc2 allocates (nSize1 * nSize2) bytes. + In case of overflow of the multiplication, or if memory allocation fails, a + NULL pointer is returned and a CE_Failure error is raised with CPLError(). + If nSize1 == 0 || nSize2 == 0, a NULL pointer will also be returned. + CPLFree() or VSIFree() can be used to free memory allocated by this function. +*/ +void CPL_DLL *VSIMalloc2( size_t nSize1, size_t nSize2 ) CPL_WARN_UNUSED_RESULT; + +/** + VSIMalloc3 allocates (nSize1 * nSize2 * nSize3) bytes. + In case of overflow of the multiplication, or if memory allocation fails, a + NULL pointer is returned and a CE_Failure error is raised with CPLError(). + If nSize1 == 0 || nSize2 == 0 || nSize3 == 0, a NULL pointer will also be returned. + CPLFree() or VSIFree() can be used to free memory allocated by this function. +*/ +void CPL_DLL *VSIMalloc3( size_t nSize1, size_t nSize2, size_t nSize3 ) CPL_WARN_UNUSED_RESULT; + +GIntBig CPL_DLL CPLGetPhysicalRAM(void); +GIntBig CPL_DLL CPLGetUsablePhysicalRAM(void); + +/* ==================================================================== */ +/* Other... */ +/* ==================================================================== */ + +#define CPLReadDir VSIReadDir +char CPL_DLL **VSIReadDir( const char * ); +char CPL_DLL **VSIReadDirRecursive( const char *pszPath ); +int CPL_DLL VSIMkdir( const char * pathname, long mode ); +int CPL_DLL VSIRmdir( const char * pathname ); +int CPL_DLL VSIUnlink( const char * pathname ); +int CPL_DLL VSIRename( const char * oldpath, const char * newpath ); +char CPL_DLL *VSIStrerror( int ); + +/* ==================================================================== */ +/* Install special file access handlers. */ +/* ==================================================================== */ +void CPL_DLL VSIInstallMemFileHandler(void); +void CPL_DLL VSIInstallLargeFileHandler(void); +void CPL_DLL VSIInstallSubFileHandler(void); +void VSIInstallCurlFileHandler(void); +void VSIInstallCurlStreamingFileHandler(void); +void VSIInstallGZipFileHandler(void); /* No reason to export that */ +void VSIInstallZipFileHandler(void); /* No reason to export that */ +void VSIInstallStdinHandler(void); /* No reason to export that */ +void VSIInstallStdoutHandler(void); /* No reason to export that */ +void CPL_DLL VSIInstallSparseFileHandler(void); +void VSIInstallTarFileHandler(void); /* No reason to export that */ +void CPL_DLL VSICleanupFileManager(void); + +VSILFILE CPL_DLL *VSIFileFromMemBuffer( const char *pszFilename, + GByte *pabyData, + vsi_l_offset nDataLength, + int bTakeOwnership ); +GByte CPL_DLL *VSIGetMemFileBuffer( const char *pszFilename, + vsi_l_offset *pnDataLength, + int bUnlinkAndSeize ); + +typedef size_t (*VSIWriteFunction)(const void* ptr, size_t size, size_t nmemb, FILE* stream); +void CPL_DLL VSIStdoutSetRedirection( VSIWriteFunction pFct, FILE* stream ); + +/* ==================================================================== */ +/* Time quering. */ +/* ==================================================================== */ + +unsigned long CPL_DLL VSITime( unsigned long * ); +const char CPL_DLL *VSICTime( unsigned long ); +struct tm CPL_DLL *VSIGMTime( const time_t *pnTime, + struct tm *poBrokenTime ); +struct tm CPL_DLL *VSILocalTime( const time_t *pnTime, + struct tm *poBrokenTime ); + +/* -------------------------------------------------------------------- */ +/* the following can be turned on for detailed logging of */ +/* almost all IO calls. */ +/* -------------------------------------------------------------------- */ +#ifdef VSI_DEBUG + +#ifndef DEBUG +# define DEBUG +#endif + +#include "cpl_error.h" + +#define VSIDebug4(f,a1,a2,a3,a4) CPLDebug( "VSI", f, a1, a2, a3, a4 ); +#define VSIDebug3( f, a1, a2, a3 ) CPLDebug( "VSI", f, a1, a2, a3 ); +#define VSIDebug2( f, a1, a2 ) CPLDebug( "VSI", f, a1, a2 ); +#define VSIDebug1( f, a1 ) CPLDebug( "VSI", f, a1 ); +#else +#define VSIDebug4( f, a1, a2, a3, a4 ) {} +#define VSIDebug3( f, a1, a2, a3 ) {} +#define VSIDebug2( f, a1, a2 ) {} +#define VSIDebug1( f, a1 ) {} +#endif + +CPL_C_END + +#endif /* ndef CPL_VSI_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_vsi_mem.cpp b/bazaar/plugin/gdal/port/cpl_vsi_mem.cpp new file mode 100644 index 000000000..fa5273601 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsi_mem.cpp @@ -0,0 +1,957 @@ +/****************************************************************************** + * $Id: cpl_vsi_mem.cpp 28976 2015-04-22 21:58:51Z rouault $ + * + * Project: VSI Virtual File System + * Purpose: Implementation of Memory Buffer virtual IO functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2007-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* Remove annoying warnings in eVC++ and VC++ 6.0 */ +#if defined(WIN32CE) +# pragma warning(disable:4786) +#endif + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include +#include + +#if defined(WIN32CE) +# include +#endif + + +CPL_CVSID("$Id: cpl_vsi_mem.cpp 28976 2015-04-22 21:58:51Z rouault $"); + +/* +** Notes on Multithreading: +** +** VSIMemFilesystemHandler: This class maintains a mutex to protect +** access and update of the oFileList array which has all the "files" in +** the memory filesystem area. It is expected that multiple threads would +** want to create and read different files at the same time and so might +** collide access oFileList without the mutex. +** +** VSIMemFile: In theory we could allow different threads to update the +** the same memory file, but for simplicity we restrict to single writer, +** multiple reader as an expectation on the application code (not enforced +** here), which means we don't need to do any protection of this class. +** +** VSIMemHandle: This is essentially a "current location" representing +** on accessor to a file, and is inherently intended only to be used in +** a single thread. +** +** In General: +** +** Multiple threads accessing the memory filesystem are ok as long as +** 1) A given VSIMemHandle (ie. FILE * at app level) isn't used by multiple +** threads at once. +** 2) A given memory file isn't accessed by more than one thread unless +** all threads are just reading. +*/ + +/************************************************************************/ +/* ==================================================================== */ +/* VSIMemFile */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIMemFile +{ +public: + CPLString osFilename; + int nRefCount; + + int bIsDirectory; + + int bOwnData; + GByte *pabyData; + vsi_l_offset nLength; + vsi_l_offset nAllocLength; + + time_t mTime; + + VSIMemFile(); + virtual ~VSIMemFile(); + + bool SetLength( vsi_l_offset nNewSize ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIMemHandle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIMemHandle : public VSIVirtualHandle +{ + public: + VSIMemFile *poFile; + vsi_l_offset nOffset; + int bUpdate; + int bEOF; + int bExtendFileAtNextWrite; + + VSIMemHandle() : poFile(NULL), nOffset(0), bUpdate(0), + bEOF(0), bExtendFileAtNextWrite(0) {} + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Close(); + virtual int Truncate( vsi_l_offset nNewSize ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIMemFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIMemFilesystemHandler : public VSIFilesystemHandler +{ +public: + std::map oFileList; + CPLMutex *hMutex; + + VSIMemFilesystemHandler(); + virtual ~VSIMemFilesystemHandler(); + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); + virtual int Unlink( const char *pszFilename ); + virtual int Mkdir( const char *pszDirname, long nMode ); + virtual int Rmdir( const char *pszDirname ); + virtual char **ReadDir( const char *pszDirname ); + virtual int Rename( const char *oldpath, const char *newpath ); + + static void NormalizePath( CPLString & ); + + int Unlink_unlocked( const char *pszFilename ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIMemFile */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VSIMemFile() */ +/************************************************************************/ + +VSIMemFile::VSIMemFile() + +{ + nRefCount = 0; + bIsDirectory = FALSE; + bOwnData = TRUE; + pabyData = NULL; + nLength = 0; + nAllocLength = 0; + time(&mTime); +} + +/************************************************************************/ +/* ~VSIMemFile() */ +/************************************************************************/ + +VSIMemFile::~VSIMemFile() + +{ + if( nRefCount != 0 ) + CPLDebug( "VSIMemFile", "Memory file %s deleted with %d references.", + osFilename.c_str(), nRefCount ); + + if( bOwnData && pabyData ) + CPLFree( pabyData ); +} + +/************************************************************************/ +/* SetLength() */ +/************************************************************************/ + +bool VSIMemFile::SetLength( vsi_l_offset nNewLength ) + +{ +/* -------------------------------------------------------------------- */ +/* Grow underlying array if needed. */ +/* -------------------------------------------------------------------- */ + if( nNewLength > nAllocLength ) + { + /* If we don't own the buffer, we cannot reallocate it because */ + /* the return address might be different from the one passed by */ + /* the caller. Hence, the caller would not be able to free */ + /* the buffer... */ + if( !bOwnData ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Cannot extended in-memory file whose ownership was not transferred"); + return false; + } + + GByte *pabyNewData; + vsi_l_offset nNewAlloc = (nNewLength + nNewLength / 10) + 5000; + if( (vsi_l_offset)(size_t)nNewAlloc != nNewAlloc ) + pabyNewData = NULL; + else + pabyNewData = (GByte *) VSIRealloc(pabyData, (size_t)nNewAlloc); + if( pabyNewData == NULL ) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Cannot extend in-memory file to " CPL_FRMT_GUIB " bytes due to out-of-memory situation", + nNewAlloc); + return false; + } + + /* Clear the new allocated part of the buffer */ + memset(pabyNewData + nAllocLength, 0, + (size_t) (nNewAlloc - nAllocLength)); + + pabyData = pabyNewData; + nAllocLength = nNewAlloc; + } + + nLength = nNewLength; + time(&mTime); + + return true; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSIMemHandle */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIMemHandle::Close() + +{ + if( --(poFile->nRefCount) == 0 ) + delete poFile; + + poFile = NULL; + + return 0; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIMemHandle::Seek( vsi_l_offset nOffset, int nWhence ) + +{ + bExtendFileAtNextWrite = FALSE; + if( nWhence == SEEK_CUR ) + this->nOffset += nOffset; + else if( nWhence == SEEK_SET ) + this->nOffset = nOffset; + else if( nWhence == SEEK_END ) + this->nOffset = poFile->nLength + nOffset; + else + { + errno = EINVAL; + return -1; + } + + bEOF = FALSE; + + if( this->nOffset > poFile->nLength ) + { + if( !bUpdate ) // Read-only files cannot be extended by seek. + { + CPLDebug( "VSIMemHandle", + "Attempt to extend read-only file '%s' to length " CPL_FRMT_GUIB " from " CPL_FRMT_GUIB ".", + poFile->osFilename.c_str(), + this->nOffset, poFile->nLength ); + + this->nOffset = poFile->nLength; + errno = EACCES; + return -1; + } + else // Writeable files are zero-extended by seek past end. + { + bExtendFileAtNextWrite = TRUE; + } + } + + return 0; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIMemHandle::Tell() + +{ + return nOffset; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIMemHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) + +{ + // FIXME: Integer overflow check should be placed here: + size_t nBytesToRead = nSize * nCount; + + if( nBytesToRead + nOffset > poFile->nLength ) + { + if (poFile->nLength < nOffset) + { + bEOF = TRUE; + return 0; + } + + nBytesToRead = (size_t)(poFile->nLength - nOffset); + nCount = nBytesToRead / nSize; + bEOF = TRUE; + } + + memcpy( pBuffer, poFile->pabyData + nOffset, (size_t)nBytesToRead ); + nOffset += nBytesToRead; + + return nCount; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIMemHandle::Write( const void * pBuffer, size_t nSize, size_t nCount ) + +{ + if( !bUpdate ) + { + errno = EACCES; + return 0; + } + if( bExtendFileAtNextWrite ) + { + bExtendFileAtNextWrite = FALSE; + if( !poFile->SetLength( nOffset ) ) + return 0; + } + + // FIXME: Integer overflow check should be placed here: + size_t nBytesToWrite = nSize * nCount; + //if( CSLTestBoolean(CPLGetConfigOption("VERBOSE_IO", "FALSE")) ) + // CPLDebug("MEM", "Write(%d bytes in [%d,%d[)", (int)nBytesToWrite, (int)nOffset, (int)(nOffset+nBytesToWrite)); + + if( nBytesToWrite + nOffset > poFile->nLength ) + { + if( !poFile->SetLength( nBytesToWrite + nOffset ) ) + return 0; + } + + memcpy( poFile->pabyData + nOffset, pBuffer, nBytesToWrite ); + nOffset += nBytesToWrite; + + time(&poFile->mTime); + + return nCount; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSIMemHandle::Eof() + +{ + return bEOF; +} + +/************************************************************************/ +/* Truncate() */ +/************************************************************************/ + +int VSIMemHandle::Truncate( vsi_l_offset nNewSize ) +{ + if( !bUpdate ) + { + errno = EACCES; + return -1; + } + + bExtendFileAtNextWrite = FALSE; + if (poFile->SetLength( nNewSize )) + return 0; + else + return -1; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSIMemFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VSIMemFilesystemHandler() */ +/************************************************************************/ + +VSIMemFilesystemHandler::VSIMemFilesystemHandler() + +{ + hMutex = NULL; +} + +/************************************************************************/ +/* ~VSIMemFilesystemHandler() */ +/************************************************************************/ + +VSIMemFilesystemHandler::~VSIMemFilesystemHandler() + +{ + std::map::const_iterator iter; + + for( iter = oFileList.begin(); iter != oFileList.end(); ++iter ) + { + iter->second->nRefCount--; + delete iter->second; + } + + if( hMutex != NULL ) + CPLDestroyMutex( hMutex ); + hMutex = NULL; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle * +VSIMemFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess ) + +{ + CPLMutexHolder oHolder( &hMutex ); + VSIMemFile *poFile; + CPLString osFilename = pszFilename; + NormalizePath( osFilename ); + +/* -------------------------------------------------------------------- */ +/* Get the filename we are opening, create if needed. */ +/* -------------------------------------------------------------------- */ + if( oFileList.find(osFilename) == oFileList.end() ) + poFile = NULL; + else + poFile = oFileList[osFilename]; + + if( strstr(pszAccess,"w") == NULL && poFile == NULL ) + { + errno = ENOENT; + return NULL; + } + + if( strstr(pszAccess,"w") ) + { + if( poFile ) + poFile->SetLength( 0 ); + else + { + poFile = new VSIMemFile; + poFile->osFilename = osFilename; + oFileList[poFile->osFilename] = poFile; + poFile->nRefCount++; // for file list + } + } + + if( poFile->bIsDirectory ) + { + errno = EISDIR; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Setup the file handle on this file. */ +/* -------------------------------------------------------------------- */ + VSIMemHandle *poHandle = new VSIMemHandle; + + poHandle->poFile = poFile; + poHandle->nOffset = 0; + poHandle->bEOF = FALSE; + if( strstr(pszAccess,"w") || strstr(pszAccess,"+") + || strstr(pszAccess,"a") ) + poHandle->bUpdate = TRUE; + else + poHandle->bUpdate = FALSE; + + poFile->nRefCount++; + + if( strstr(pszAccess,"a") ) + poHandle->nOffset = poFile->nLength; + + return poHandle; +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSIMemFilesystemHandler::Stat( const char * pszFilename, + VSIStatBufL * pStatBuf, + int nFlags ) + +{ + (void) nFlags; + + CPLMutexHolder oHolder( &hMutex ); + + CPLString osFilename = pszFilename; + NormalizePath( osFilename ); + + memset( pStatBuf, 0, sizeof(VSIStatBufL) ); + + if ( osFilename == "/vsimem/" ) + { + pStatBuf->st_size = 0; + pStatBuf->st_mode = S_IFDIR; + return 0; + } + + if( oFileList.find(osFilename) == oFileList.end() ) + { + errno = ENOENT; + return -1; + } + + VSIMemFile *poFile = oFileList[osFilename]; + + memset( pStatBuf, 0, sizeof(VSIStatBufL) ); + + if( poFile->bIsDirectory ) + { + pStatBuf->st_size = 0; + pStatBuf->st_mode = S_IFDIR; + } + else + { + pStatBuf->st_size = poFile->nLength; + pStatBuf->st_mode = S_IFREG; + pStatBuf->st_mtime = poFile->mTime; + } + + return 0; +} + +/************************************************************************/ +/* Unlink() */ +/************************************************************************/ + +int VSIMemFilesystemHandler::Unlink( const char * pszFilename ) + +{ + CPLMutexHolder oHolder( &hMutex ); + return Unlink_unlocked(pszFilename); +} + +/************************************************************************/ +/* Unlink_unlocked() */ +/************************************************************************/ + +int VSIMemFilesystemHandler::Unlink_unlocked( const char * pszFilename ) + +{ + CPLString osFilename = pszFilename; + NormalizePath( osFilename ); + + VSIMemFile *poFile; + + if( oFileList.find(osFilename) == oFileList.end() ) + { + errno = ENOENT; + return -1; + } + else + { + poFile = oFileList[osFilename]; + + if( --(poFile->nRefCount) == 0 ) + delete poFile; + + oFileList.erase( oFileList.find(osFilename) ); + + return 0; + } +} + +/************************************************************************/ +/* Mkdir() */ +/************************************************************************/ + +int VSIMemFilesystemHandler::Mkdir( const char * pszPathname, + long nMode ) + +{ + (void) nMode; + + CPLMutexHolder oHolder( &hMutex ); + + CPLString osPathname = pszPathname; + + NormalizePath( osPathname ); + + if( oFileList.find(osPathname) != oFileList.end() ) + { + errno = EEXIST; + return -1; + } + + VSIMemFile *poFile = new VSIMemFile; + + poFile->osFilename = osPathname; + poFile->bIsDirectory = TRUE; + oFileList[osPathname] = poFile; + poFile->nRefCount++; /* referenced by file list */ + + return 0; +} + +/************************************************************************/ +/* Rmdir() */ +/************************************************************************/ + +int VSIMemFilesystemHandler::Rmdir( const char * pszPathname ) + +{ + return Unlink( pszPathname ); +} + +/************************************************************************/ +/* ReadDir() */ +/************************************************************************/ + +char **VSIMemFilesystemHandler::ReadDir( const char *pszPath ) + +{ + CPLMutexHolder oHolder( &hMutex ); + + CPLString osPath = pszPath; + + NormalizePath( osPath ); + + std::map::const_iterator iter; + char **papszDir = NULL; + int nPathLen = strlen(osPath); + + if( osPath[nPathLen-1] == '/' ) + nPathLen--; + + /* In case of really big number of files in the directory, CSLAddString */ + /* can be slow (see #2158). We then directly build the list. */ + int nItems=0; + int nAllocatedItems=0; + + for( iter = oFileList.begin(); iter != oFileList.end(); ++iter ) + { + const char *pszFilePath = iter->second->osFilename.c_str(); + if( EQUALN(osPath,pszFilePath,nPathLen) + && pszFilePath[nPathLen] == '/' + && strstr(pszFilePath+nPathLen+1,"/") == NULL ) + { + if (nItems == 0) + { + papszDir = (char**) CPLCalloc(2,sizeof(char*)); + nAllocatedItems = 1; + } + else if (nItems >= nAllocatedItems) + { + nAllocatedItems = nAllocatedItems * 2; + papszDir = (char**)CPLRealloc(papszDir, + (nAllocatedItems+2)*sizeof(char*)); + } + + papszDir[nItems] = CPLStrdup(pszFilePath+nPathLen+1); + papszDir[nItems+1] = NULL; + + nItems++; + } + } + + return papszDir; +} + +/************************************************************************/ +/* Rename() */ +/************************************************************************/ + +int VSIMemFilesystemHandler::Rename( const char *pszOldPath, + const char *pszNewPath ) + +{ + CPLMutexHolder oHolder( &hMutex ); + + CPLString osOldPath = pszOldPath; + CPLString osNewPath = pszNewPath; + + NormalizePath( osOldPath ); + NormalizePath( osNewPath ); + + if ( osOldPath.compare(osNewPath) == 0 ) + return 0; + + if( oFileList.find(osOldPath) == oFileList.end() ) + { + errno = ENOENT; + return -1; + } + else + { + std::map::iterator it = oFileList.find(osOldPath); + while (it != oFileList.end() && it->first.ifind(osOldPath) == 0) + { + const CPLString osRemainder = it->first.substr(osOldPath.size()); + if (osRemainder.empty() || osRemainder[0] == '/') + { + const CPLString osNewFullPath = osNewPath + osRemainder; + Unlink_unlocked(osNewFullPath); + oFileList[osNewFullPath] = it->second; + it->second->osFilename = osNewFullPath; + oFileList.erase(it++); + } + else ++it; + } + + return 0; + } +} + +/************************************************************************/ +/* NormalizePath() */ +/************************************************************************/ + +void VSIMemFilesystemHandler::NormalizePath( CPLString &oPath ) + +{ + int i, nSize = oPath.size(); + + for( i = 0; i < nSize; i++ ) + { + if( oPath[i] == '\\' ) + oPath[i] = '/'; + } + +} + +/************************************************************************/ +/* VSIInstallLargeFileHandler() */ +/************************************************************************/ + +/** + * \brief Install "memory" file system handler. + * + * A special file handler is installed that allows block of memory to be + * treated as files. All portions of the file system underneath the base + * path "/vsimem/" will be handled by this driver. + * + * Normal VSI*L functions can be used freely to create and destroy memory + * arrays treating them as if they were real file system objects. Some + * additional methods exist to efficient create memory file system objects + * without duplicating original copies of the data or to "steal" the block + * of memory associated with a memory file. + * + * At this time the memory handler does not properly handle directory + * semantics for the memory portion of the filesystem. The VSIReadDir() + * function is not supported though this will be corrected in the future. + * + * Calling this function repeatedly should do no harm, though it is not + * necessary. It is already called the first time a virtualizable + * file access function (ie. VSIFOpenL(), VSIMkDir(), etc) is called. + * + * This code example demonstrates using GDAL to translate from one memory + * buffer to another. + * + * \code + * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength, + * vsi_l_offset *pnOutDataLength ) + * { + * // create memory file system object from buffer. + * VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData, + * nInDataLength, FALSE ) ); + * + * // Open memory buffer for read. + * GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly ); + * + * // Get output format driver. + * GDALDriverH hDriver = GDALGetDriverByName( "GTiff" ); + * GDALDatasetH hOutDS; + * + * hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL, + * NULL, NULL ); + * + * // close source file, and "unlink" it. + * GDALClose( hDS ); + * VSIUnlink( "/vsimem/work.dat" ); + * + * // seize the buffer associated with the output file. + * + * return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE ); + * } + * \endcode + */ + +void VSIInstallMemFileHandler() +{ + VSIFileManager::InstallHandler( "/vsimem/", new VSIMemFilesystemHandler ); +} + +/************************************************************************/ +/* VSIFileFromMemBuffer() */ +/************************************************************************/ + +/** + * \brief Create memory "file" from a buffer. + * + * A virtual memory file is created from the passed buffer with the indicated + * filename. Under normal conditions the filename would need to be absolute + * and within the /vsimem/ portion of the filesystem. + * + * If bTakeOwnership is TRUE, then the memory file system handler will take + * ownership of the buffer, freeing it when the file is deleted. Otherwise + * it remains the responsibility of the caller, but should not be freed as + * long as it might be accessed as a file. In no circumstances does this + * function take a copy of the pabyData contents. + * + * @param pszFilename the filename to be created. + * @param pabyData the data buffer for the file. + * @param nDataLength the length of buffer in bytes. + * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE. + * + * @return open file handle on created file (see VSIFOpenL()). + */ + +VSILFILE *VSIFileFromMemBuffer( const char *pszFilename, + GByte *pabyData, + vsi_l_offset nDataLength, + int bTakeOwnership ) + +{ + if( VSIFileManager::GetHandler("") + == VSIFileManager::GetHandler("/vsimem/") ) + VSIInstallMemFileHandler(); + + VSIMemFilesystemHandler *poHandler = (VSIMemFilesystemHandler *) + VSIFileManager::GetHandler("/vsimem/"); + + if (pszFilename == NULL) + return NULL; + + CPLString osFilename = pszFilename; + VSIMemFilesystemHandler::NormalizePath( osFilename ); + + VSIMemFile *poFile = new VSIMemFile; + + poFile->osFilename = osFilename; + poFile->bOwnData = bTakeOwnership; + poFile->pabyData = pabyData; + poFile->nLength = nDataLength; + poFile->nAllocLength = nDataLength; + + { + CPLMutexHolder oHolder( &poHandler->hMutex ); + poHandler->Unlink_unlocked(osFilename); + poHandler->oFileList[poFile->osFilename] = poFile; + poFile->nRefCount++; + } + + return (VSILFILE *) poHandler->Open( osFilename, "r+" ); +} + +/************************************************************************/ +/* VSIGetMemFileBuffer() */ +/************************************************************************/ + +/** + * \brief Fetch buffer underlying memory file. + * + * This function returns a pointer to the memory buffer underlying a + * virtual "in memory" file. If bUnlinkAndSeize is TRUE the filesystem + * object will be deleted, and ownership of the buffer will pass to the + * caller otherwise the underlying file will remain in existance. + * + * @param pszFilename the name of the file to grab the buffer of. + * @param pnDataLength (file) length returned in this variable. + * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered. + * + * @return pointer to memory buffer or NULL on failure. + */ + +GByte *VSIGetMemFileBuffer( const char *pszFilename, + vsi_l_offset *pnDataLength, + int bUnlinkAndSeize ) + +{ + VSIMemFilesystemHandler *poHandler = (VSIMemFilesystemHandler *) + VSIFileManager::GetHandler("/vsimem/"); + + if (pszFilename == NULL) + return NULL; + + CPLString osFilename = pszFilename; + VSIMemFilesystemHandler::NormalizePath( osFilename ); + + CPLMutexHolder oHolder( &poHandler->hMutex ); + + if( poHandler->oFileList.find(osFilename) == poHandler->oFileList.end() ) + return NULL; + + VSIMemFile *poFile = poHandler->oFileList[osFilename]; + GByte *pabyData; + + pabyData = poFile->pabyData; + if( pnDataLength != NULL ) + *pnDataLength = poFile->nLength; + + if( bUnlinkAndSeize ) + { + if( !poFile->bOwnData ) + CPLDebug( "VSIMemFile", + "File doesn't own data in VSIGetMemFileBuffer!" ); + else + poFile->bOwnData = FALSE; + + poHandler->oFileList.erase( poHandler->oFileList.find(osFilename) ); + --(poFile->nRefCount); + delete poFile; + } + + return pabyData; +} + diff --git a/bazaar/plugin/gdal/port/cpl_vsi_virtual.h b/bazaar/plugin/gdal/port/cpl_vsi_virtual.h new file mode 100644 index 000000000..33a8948ce --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsi_virtual.h @@ -0,0 +1,200 @@ +/****************************************************************************** + * $Id: cpl_vsi_virtual.h 28493 2015-02-16 09:49:16Z rouault $ + * + * Project: VSI Virtual File System + * Purpose: Declarations for classes related to the virtual filesystem. + * These would only be normally required by applications implmenting + * their own virtual file system classes which should be rare. + * The class interface may be fragile through versions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_VSI_VIRTUAL_H_INCLUDED +#define CPL_VSI_VIRTUAL_H_INCLUDED + +#include "cpl_vsi.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" + +#if defined(WIN32CE) +# include "cpl_wince.h" +# include +# pragma warning(disable:4786) /* Remove annoying warnings in eVC++ and VC++ 6.0 */ +#endif + +#include +#include +#include + +/************************************************************************/ +/* VSIVirtualHandle */ +/************************************************************************/ + +class CPL_DLL VSIVirtualHandle { + public: + virtual int Seek( vsi_l_offset nOffset, int nWhence ) = 0; + virtual vsi_l_offset Tell() = 0; + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ) = 0; + virtual int ReadMultiRange( int nRanges, void ** ppData, const vsi_l_offset* panOffsets, const size_t* panSizes ); + virtual size_t Write( const void *pBuffer, size_t nSize,size_t nMemb)=0; + virtual int Eof() = 0; + virtual int Flush() {return 0;} + virtual int Close() = 0; + virtual int Truncate( CPL_UNUSED vsi_l_offset nNewSize ) { return -1; } + virtual void *GetNativeFileDescriptor() { return NULL; } + virtual ~VSIVirtualHandle() { } +}; + +/************************************************************************/ +/* VSIFilesystemHandler */ +/************************************************************************/ + +class CPL_DLL VSIFilesystemHandler { + +public: + + virtual ~VSIFilesystemHandler() {} + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess) = 0; + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags) = 0; + virtual int Unlink( const char *pszFilename ) + { (void) pszFilename; errno=ENOENT; return -1; } + virtual int Mkdir( const char *pszDirname, long nMode ) + {(void)pszDirname; (void)nMode; errno=ENOENT; return -1;} + virtual int Rmdir( const char *pszDirname ) + { (void) pszDirname; errno=ENOENT; return -1; } + virtual char **ReadDir( const char *pszDirname ) + { (void) pszDirname; return NULL; } + virtual int Rename( const char *oldpath, const char *newpath ) + { (void) oldpath; (void)newpath; errno=ENOENT; return -1; } + virtual int IsCaseSensitive( const char* pszFilename ) + { (void) pszFilename; return TRUE; } +}; + +/************************************************************************/ +/* VSIFileManager */ +/************************************************************************/ + +class CPL_DLL VSIFileManager +{ +private: + VSIFilesystemHandler *poDefaultHandler; + std::map oHandlers; + + VSIFileManager(); + + static VSIFileManager *Get(); + +public: + ~VSIFileManager(); + + static VSIFilesystemHandler *GetHandler( const char * ); + static void InstallHandler( const std::string& osPrefix, + VSIFilesystemHandler * ); + /* RemoveHandler is never defined. */ + /* static void RemoveHandler( const std::string& osPrefix ); */ +}; + + +/************************************************************************/ +/* ==================================================================== */ +/* VSIArchiveFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIArchiveEntryFileOffset +{ + public: + virtual ~VSIArchiveEntryFileOffset(); +}; + +typedef struct +{ + char *fileName; + vsi_l_offset uncompressed_size; + VSIArchiveEntryFileOffset* file_pos; + int bIsDir; + GIntBig nModifiedTime; +} VSIArchiveEntry; + +typedef struct +{ + int nEntries; + VSIArchiveEntry* entries; +} VSIArchiveContent; + +class VSIArchiveReader +{ + public: + virtual ~VSIArchiveReader(); + + virtual int GotoFirstFile() = 0; + virtual int GotoNextFile() = 0; + virtual VSIArchiveEntryFileOffset* GetFileOffset() = 0; + virtual GUIntBig GetFileSize() = 0; + virtual CPLString GetFileName() = 0; + virtual GIntBig GetModifiedTime() = 0; + virtual int GotoFileOffset(VSIArchiveEntryFileOffset* pOffset) = 0; +}; + +class VSIArchiveFilesystemHandler : public VSIFilesystemHandler +{ +protected: + CPLMutex* hMutex; + /* We use a cache that contains the list of files containes in a VSIArchive file as */ + /* unarchive.c is quite inefficient in listing them. This speeds up access to VSIArchive files */ + /* containing ~1000 files like a CADRG product */ + std::map oFileList; + + virtual const char* GetPrefix() = 0; + virtual std::vector GetExtensions() = 0; + virtual VSIArchiveReader* CreateReader(const char* pszArchiveFileName) = 0; + +public: + VSIArchiveFilesystemHandler(); + virtual ~VSIArchiveFilesystemHandler(); + + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); + virtual int Unlink( const char *pszFilename ); + virtual int Rename( const char *oldpath, const char *newpath ); + virtual int Mkdir( const char *pszDirname, long nMode ); + virtual int Rmdir( const char *pszDirname ); + virtual char **ReadDir( const char *pszDirname ); + + virtual const VSIArchiveContent* GetContentOfArchive(const char* archiveFilename, VSIArchiveReader* poReader = NULL); + virtual char* SplitFilename(const char *pszFilename, CPLString &osFileInArchive, int bCheckMainFileExists); + virtual VSIArchiveReader* OpenArchiveFile(const char* archiveFilename, const char* fileInArchiveName); + virtual int FindFileInArchive(const char* archiveFilename, const char* fileInArchiveName, const VSIArchiveEntry** archiveEntry); +}; + +VSIVirtualHandle CPL_DLL *VSICreateBufferedReaderHandle(VSIVirtualHandle* poBaseHandle); +VSIVirtualHandle* VSICreateBufferedReaderHandle(VSIVirtualHandle* poBaseHandle, + const GByte* pabyBeginningContent, + vsi_l_offset nSheatFileSize); +VSIVirtualHandle* VSICreateCachedFile( VSIVirtualHandle* poBaseHandle, size_t nChunkSize = 32768, size_t nCacheSize = 0 ); +VSIVirtualHandle CPL_DLL *VSICreateGZipWritable( VSIVirtualHandle* poBaseHandle, int bRegularZLibIn, int bAutoCloseBaseHandle ); + +#endif /* ndef CPL_VSI_VIRTUAL_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_vsil.cpp b/bazaar/plugin/gdal/port/cpl_vsil.cpp new file mode 100644 index 000000000..45af62b1c --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil.cpp @@ -0,0 +1,1241 @@ +/****************************************************************************** + * $Id: cpl_vsil.cpp 28849 2015-04-05 14:05:18Z goatbar $ + * + * Project: VSI Virtual File System + * Purpose: Implementation VSI*L File API and other file system access + * methods going through file virtualization. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" +#include "cpl_multiproc.h" +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: cpl_vsil.cpp 28849 2015-04-05 14:05:18Z goatbar $"); + +/************************************************************************/ +/* VSIReadDir() */ +/************************************************************************/ + +/** + * \brief Read names in a directory. + * + * This function abstracts access to directory contains. It returns a + * list of strings containing the names of files, and directories in this + * directory. The resulting string list becomes the responsibility of the + * application and should be freed with CSLDestroy() when no longer needed. + * + * Note that no error is issued via CPLError() if the directory path is + * invalid, though NULL is returned. + * + * This function used to be known as CPLReadDir(), but the old name is now + * deprecated. + * + * @param pszPath the relative, or absolute path of a directory to read. + * UTF-8 encoded. + * @return The list of entries in the directory, or NULL if the directory + * doesn't exist. Filenames are returned in UTF-8 encoding. + */ + +char **VSIReadDir(const char *pszPath) +{ + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( pszPath ); + + return poFSHandler->ReadDir( pszPath ); +} + +/************************************************************************/ +/* VSIReadRecursive() */ +/************************************************************************/ + +typedef struct +{ + char **papszFiles; + int nCount; + int i; + char* pszPath; + char* pszDisplayedPath; +} VSIReadDirRecursiveTask; + +/** + * \brief Read names in a directory recursively. + * + * This function abstracts access to directory contents and subdirectories. + * It returns a list of strings containing the names of files and directories + * in this directory and all subdirectories. The resulting string list becomes + * the responsibility of the application and should be freed with CSLDestroy() + * when no longer needed. + * + * Note that no error is issued via CPLError() if the directory path is + * invalid, though NULL is returned. + * + * @param pszPathIn the relative, or absolute path of a directory to read. + * UTF-8 encoded. + * + * @return The list of entries in the directory and subdirectories + * or NULL if the directory doesn't exist. Filenames are returned in UTF-8 + * encoding. + * @since GDAL 1.10.0 + * + */ + +char **VSIReadDirRecursive( const char *pszPathIn ) +{ + CPLStringList oFiles = NULL; + char **papszFiles = NULL; + VSIStatBufL psStatBuf; + CPLString osTemp1, osTemp2; + int i = 0; + int nCount = -1; + + std::vector aoStack; + char* pszPath = CPLStrdup(pszPathIn); + char* pszDisplayedPath = NULL; + + while(TRUE) + { + if( nCount < 0 ) + { + // get listing + papszFiles = VSIReadDir( pszPath ); + + // get files and directories inside listing + nCount = papszFiles ? CSLCount( papszFiles ) : 0; + i = 0; + } + + for ( ; i < nCount; i++ ) + { + // Do not recurse up the tree. + if (EQUAL(".", papszFiles[i]) || EQUAL("..", papszFiles[i])) + continue; + + // build complete file name for stat + osTemp1.clear(); + osTemp1.append( pszPath ); + osTemp1.append( "/" ); + osTemp1.append( papszFiles[i] ); + + // if is file, add it + if ( VSIStatL( osTemp1.c_str(), &psStatBuf ) != 0 ) + continue; + + if( VSI_ISREG( psStatBuf.st_mode ) ) + { + if( pszDisplayedPath ) + { + osTemp1.clear(); + osTemp1.append( pszDisplayedPath ); + osTemp1.append( "/" ); + osTemp1.append( papszFiles[i] ); + oFiles.AddString( osTemp1 ); + } + else + oFiles.AddString( papszFiles[i] ); + } + else if ( VSI_ISDIR( psStatBuf.st_mode ) ) + { + // add directory entry + osTemp2.clear(); + if( pszDisplayedPath ) + { + osTemp2.append( pszDisplayedPath ); + osTemp2.append( "/" ); + } + osTemp2.append( papszFiles[i] ); + osTemp2.append( "/" ); + oFiles.AddString( osTemp2.c_str() ); + + VSIReadDirRecursiveTask sTask; + sTask.papszFiles = papszFiles; + sTask.nCount = nCount; + sTask.i = i; + sTask.pszPath = CPLStrdup(pszPath); + sTask.pszDisplayedPath = pszDisplayedPath ? CPLStrdup(pszDisplayedPath) : NULL; + aoStack.push_back(sTask); + + CPLFree(pszPath); + pszPath = CPLStrdup( osTemp1.c_str() ); + + char* pszDisplayedPathNew; + if( pszDisplayedPath ) + pszDisplayedPathNew = CPLStrdup( CPLSPrintf("%s/%s", pszDisplayedPath, papszFiles[i]) ); + else + pszDisplayedPathNew = CPLStrdup( papszFiles[i] ); + CPLFree(pszDisplayedPath); + pszDisplayedPath = pszDisplayedPathNew; + + i = 0; + papszFiles = NULL; + nCount = -1; + + break; + } + } + + if( nCount >= 0 ) + { + CSLDestroy( papszFiles ); + + if( aoStack.size() ) + { + int iLast = (int)aoStack.size() - 1; + CPLFree(pszPath); + CPLFree(pszDisplayedPath); + nCount = aoStack[iLast].nCount; + papszFiles = aoStack[iLast].papszFiles; + i = aoStack[iLast].i + 1; + pszPath = aoStack[iLast].pszPath; + pszDisplayedPath = aoStack[iLast].pszDisplayedPath; + + aoStack.resize(iLast); + } + else + break; + } + } + + CPLFree(pszPath); + CPLFree(pszDisplayedPath); + + return oFiles.StealList(); +} + + +/************************************************************************/ +/* CPLReadDir() */ +/* */ +/* This is present only to provide ABI compatability with older */ +/* versions. */ +/************************************************************************/ +#undef CPLReadDir + +CPL_C_START +char CPL_DLL **CPLReadDir( const char *pszPath ); +CPL_C_END + +char **CPLReadDir( const char *pszPath ) +{ + return VSIReadDir(pszPath); +} + +/************************************************************************/ +/* VSIMkdir() */ +/************************************************************************/ + +/** + * \brief Create a directory. + * + * Create a new directory with the indicated mode. The mode is ignored + * on some platforms. A reasonable default mode value would be 0666. + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX mkdir() function. + * + * @param pszPathname the path to the directory to create. UTF-8 encoded. + * @param mode the permissions mode. + * + * @return 0 on success or -1 on an error. + */ + +int VSIMkdir( const char *pszPathname, long mode ) + +{ + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( pszPathname ); + + return poFSHandler->Mkdir( pszPathname, mode ); +} + +/************************************************************************/ +/* VSIUnlink() */ +/*************************a***********************************************/ + +/** + * \brief Delete a file. + * + * Deletes a file object from the file system. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX unlink() function. + * + * @param pszFilename the path of the file to be deleted. UTF-8 encoded. + * + * @return 0 on success or -1 on an error. + */ + +int VSIUnlink( const char * pszFilename ) + +{ + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( pszFilename ); + + return poFSHandler->Unlink( pszFilename ); +} + +/************************************************************************/ +/* VSIRename() */ +/************************************************************************/ + +/** + * \brief Rename a file. + * + * Renames a file object in the file system. It should be possible + * to rename a file onto a new filesystem, but it is safest if this + * function is only used to rename files that remain in the same directory. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX rename() function. + * + * @param oldpath the name of the file to be renamed. UTF-8 encoded. + * @param newpath the name the file should be given. UTF-8 encoded. + * + * @return 0 on success or -1 on an error. + */ + +int VSIRename( const char * oldpath, const char * newpath ) + +{ + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( oldpath ); + + return poFSHandler->Rename( oldpath, newpath ); +} + +/************************************************************************/ +/* VSIRmdir() */ +/************************************************************************/ + +/** + * \brief Delete a directory. + * + * Deletes a directory object from the file system. On some systems + * the directory must be empty before it can be deleted. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX rmdir() function. + * + * @param pszDirname the path of the directory to be deleted. UTF-8 encoded. + * + * @return 0 on success or -1 on an error. + */ + +int VSIRmdir( const char * pszDirname ) + +{ + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( pszDirname ); + + return poFSHandler->Rmdir( pszDirname ); +} + +/************************************************************************/ +/* VSIStatL() */ +/************************************************************************/ + +/** + * \brief Get filesystem object info. + * + * Fetches status information about a filesystem object (file, directory, etc). + * The returned information is placed in the VSIStatBufL structure. For + * portability, only use the st_size (size in bytes) and st_mode (file type). + * This method is similar to VSIStat(), but will work on large files on + * systems where this requires special calls. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX stat() function. + * + * @param pszFilename the path of the filesystem object to be queried. UTF-8 encoded. + * @param psStatBuf the structure to load with information. + * + * @return 0 on success or -1 on an error. + */ + +int VSIStatL( const char * pszFilename, VSIStatBufL *psStatBuf ) + +{ + return VSIStatExL(pszFilename, psStatBuf, 0); +} + + +/************************************************************************/ +/* VSIStatExL() */ +/************************************************************************/ + +/** + * \brief Get filesystem object info. + * + * Fetches status information about a filesystem object (file, directory, etc). + * The returned information is placed in the VSIStatBufL structure. For + * portability, only use the st_size (size in bytes) and st_mode (file type). + * This method is similar to VSIStat(), but will work on large files on + * systems where this requires special calls. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX stat() function, with an extra parameter to specify + * which information is needed, which offers a potential for speed optimizations + * on specialized and potentially slow virtual filesystem objects (/vsigzip/, /vsicurl/) + * + * @param pszFilename the path of the filesystem object to be queried. UTF-8 encoded. + * @param psStatBuf the structure to load with information. + * @param nFlags 0 to get all information, or VSI_STAT_EXISTS_FLAG, VSI_STAT_NATURE_FLAG or + * VSI_STAT_SIZE_FLAG, or a combination of those to get partial info. + * + * @return 0 on success or -1 on an error. + * + * @since GDAL 1.8.0 + */ + +int VSIStatExL( const char * pszFilename, VSIStatBufL *psStatBuf, int nFlags ) + +{ + char szAltPath[4]; + /* enable to work on "C:" as if it were "C:\" */ + if( strlen(pszFilename) == 2 && pszFilename[1] == ':' ) + { + szAltPath[0] = pszFilename[0]; + szAltPath[1] = pszFilename[1]; + szAltPath[2] = '\\'; + szAltPath[3] = '\0'; + + pszFilename = szAltPath; + } + + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( pszFilename ); + + if (nFlags == 0) + nFlags = VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG | VSI_STAT_SIZE_FLAG; + + return poFSHandler->Stat( pszFilename, psStatBuf, nFlags ); +} + +/************************************************************************/ +/* VSIIsCaseSensitiveFS() */ +/************************************************************************/ + +/** + * \brief Returns if the filenames of the filesystem are case sensitive. + * + * This method retrieves to which filesystem belongs the passed filename + * and return TRUE if the filenames of that filesystem are case sensitive. + * + * Currently, this will return FALSE only for Windows real filenames. Other + * VSI virtual filesystems are case sensitive. + * + * This methods avoid ugly #ifndef WIN32 / #endif code, that is wrong when + * dealing with virtual filenames. + * + * @param pszFilename the path of the filesystem object to be tested. UTF-8 encoded. + * + * @return TRUE if the filenames of the filesystem are case sensitive. + * + * @since GDAL 1.8.0 + */ +int VSIIsCaseSensitiveFS( const char * pszFilename ) +{ + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( pszFilename ); + + return poFSHandler->IsCaseSensitive( pszFilename ); +} + +/************************************************************************/ +/* VSIFOpenL() */ +/************************************************************************/ + +/** + * \brief Open file. + * + * This function opens a file with the desired access. Large files (larger + * than 2GB) should be supported. Binary access is always implied and + * the "b" does not need to be included in the pszAccess string. + * + * Note that the "VSILFILE *" returned since GDAL 1.8.0 by this function is + * *NOT* a standard C library FILE *, and cannot be used with any functions + * other than the "VSI*L" family of functions. They aren't "real" FILE objects. + * + * On windows it is possible to define the configuration option + * GDAL_FILE_IS_UTF8 to have pszFilename treated as being in the local + * encoding instead of UTF-8, retoring the pre-1.8.0 behavior of VSIFOpenL(). + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX fopen() function. + * + * @param pszFilename the file to open. UTF-8 encoded. + * @param pszAccess access requested (ie. "r", "r+", "w". + * + * @return NULL on failure, or the file handle. + */ + +VSILFILE *VSIFOpenL( const char * pszFilename, const char * pszAccess ) + +{ + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( pszFilename ); + + VSILFILE* fp = (VSILFILE *) poFSHandler->Open( pszFilename, pszAccess ); + + VSIDebug3( "VSIFOpenL(%s,%s) = %p", pszFilename, pszAccess, fp ); + + return fp; +} + +/************************************************************************/ +/* VSIFCloseL() */ +/************************************************************************/ + +/** + * \brief Close file. + * + * This function closes the indicated file. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX fclose() function. + * + * @param fp file handle opened with VSIFOpenL(). + * + * @return 0 on success or -1 on failure. + */ + +int VSIFCloseL( VSILFILE * fp ) + +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + VSIDebug1( "VSICloseL(%p)", fp ); + + int nResult = poFileHandle->Close(); + + delete poFileHandle; + + return nResult; +} + +/************************************************************************/ +/* VSIFSeekL() */ +/************************************************************************/ + +/** + * \brief Seek to requested offset. + * + * Seek to the desired offset (nOffset) in the indicated file. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX fseek() call. + * + * @param fp file handle opened with VSIFOpenL(). + * @param nOffset offset in bytes. + * @param nWhence one of SEEK_SET, SEEK_CUR or SEEK_END. + * + * @return 0 on success or -1 one failure. + */ + +int VSIFSeekL( VSILFILE * fp, vsi_l_offset nOffset, int nWhence ) + +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + return poFileHandle->Seek( nOffset, nWhence ); +} + +/************************************************************************/ +/* VSIFTellL() */ +/************************************************************************/ + +/** + * \brief Tell current file offset. + * + * Returns the current file read/write offset in bytes from the beginning of + * the file. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX ftell() call. + * + * @param fp file handle opened with VSIFOpenL(). + * + * @return file offset in bytes. + */ + +vsi_l_offset VSIFTellL( VSILFILE * fp ) + +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + return poFileHandle->Tell(); +} + +/************************************************************************/ +/* VSIRewindL() */ +/************************************************************************/ + +void VSIRewindL( VSILFILE * fp ) + +{ + VSIFSeekL( fp, 0, SEEK_SET ); +} + +/************************************************************************/ +/* VSIFFlushL() */ +/************************************************************************/ + +/** + * \brief Flush pending writes to disk. + * + * For files in write or update mode and on filesystem types where it is + * applicable, all pending output on the file is flushed to the physical disk. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX fflush() call. + * + * @param fp file handle opened with VSIFOpenL(). + * + * @return 0 on success or -1 on error. + */ + +int VSIFFlushL( VSILFILE * fp ) + +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + return poFileHandle->Flush(); +} + +/************************************************************************/ +/* VSIFReadL() */ +/************************************************************************/ + +/** + * \brief Read bytes from file. + * + * Reads nCount objects of nSize bytes from the indicated file at the + * current offset into the indicated buffer. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX fread() call. + * + * @param pBuffer the buffer into which the data should be read (at least + * nCount * nSize bytes in size. + * @param nSize size of objects to read in bytes. + * @param nCount number of objects to read. + * @param fp file handle opened with VSIFOpenL(). + * + * @return number of objects successfully read. + */ + +size_t VSIFReadL( void * pBuffer, size_t nSize, size_t nCount, VSILFILE * fp ) + +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + return poFileHandle->Read( pBuffer, nSize, nCount ); +} + + +/************************************************************************/ +/* VSIFReadMultiRangeL() */ +/************************************************************************/ + +/** + * \brief Read several ranges of bytes from file. + * + * Reads nRanges objects of panSizes[i] bytes from the indicated file at the + * offset panOffsets[i] into the buffer ppData[i]. + * + * Ranges must be sorted in ascending start offset, and must not overlap each + * other. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory or /vsicurl/. + * + * @param nRanges number of ranges to read. + * @param ppData array of nRanges buffer into which the data should be read + * (ppData[i] must be at list panSizes[i] bytes). + * @param panOffsets array of nRanges offsets at which the data should be read. + * @param panSizes array of nRanges sizes of objects to read (in bytes). + * @param fp file handle opened with VSIFOpenL(). + * + * @return 0 in case of success, -1 otherwise. + * @since GDAL 1.9.0 + */ + +int VSIFReadMultiRangeL( int nRanges, void ** ppData, + const vsi_l_offset* panOffsets, + const size_t* panSizes, VSILFILE * fp ) +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + return poFileHandle->ReadMultiRange( nRanges, ppData, panOffsets, panSizes ); +} + +/************************************************************************/ +/* VSIFWriteL() */ +/************************************************************************/ + +/** + * \brief Write bytes to file. + * + * Writess nCount objects of nSize bytes to the indicated file at the + * current offset into the indicated buffer. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX fwrite() call. + * + * @param pBuffer the buffer from which the data should be written (at least + * nCount * nSize bytes in size. + * @param nSize size of objects to read in bytes. + * @param nCount number of objects to read. + * @param fp file handle opened with VSIFOpenL(). + * + * @return number of objects successfully written. + */ + +size_t VSIFWriteL( const void *pBuffer, size_t nSize, size_t nCount, VSILFILE *fp ) + +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + return poFileHandle->Write( pBuffer, nSize, nCount ); +} + +/************************************************************************/ +/* VSIFEofL() */ +/************************************************************************/ + +/** + * \brief Test for end of file. + * + * Returns TRUE (non-zero) if an end-of-file condition occured during the + * previous read operation. The end-of-file flag is cleared by a successful + * VSIFSeekL() call. + * + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX feof() call. + * + * @param fp file handle opened with VSIFOpenL(). + * + * @return TRUE if at EOF else FALSE. + */ + +int VSIFEofL( VSILFILE * fp ) + +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + return poFileHandle->Eof(); +} + +/************************************************************************/ +/* VSIFTruncateL() */ +/************************************************************************/ + +/** + * \brief Truncate/expand the file to the specified size + + * This method goes through the VSIFileHandler virtualization and may + * work on unusual filesystems such as in memory. + * + * Analog of the POSIX ftruncate() call. + * + * @param fp file handle opened with VSIFOpenL(). + * @param nNewSize new size in bytes. + * + * @return 0 on success + * @since GDAL 1.9.0 + */ + +int VSIFTruncateL( VSILFILE * fp, vsi_l_offset nNewSize ) + +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + return poFileHandle->Truncate(nNewSize); +} + +/************************************************************************/ +/* VSIFPrintfL() */ +/************************************************************************/ + +/** + * \brief Formatted write to file. + * + * Provides fprintf() style formatted output to a VSI*L file. This formats + * an internal buffer which is written using VSIFWriteL(). + * + * Analog of the POSIX fprintf() call. + * + * @param fp file handle opened with VSIFOpenL(). + * @param pszFormat the printf style format string. + * + * @return the number of bytes written or -1 on an error. + */ + +int VSIFPrintfL( VSILFILE *fp, const char *pszFormat, ... ) + +{ + va_list args; + CPLString osResult; + + va_start( args, pszFormat ); + osResult.vPrintf( pszFormat, args ); + va_end( args ); + + return VSIFWriteL( osResult.c_str(), 1, osResult.length(), fp ); +} + +/************************************************************************/ +/* VSIFPutcL() */ +/************************************************************************/ + +int VSIFPutcL( int nChar, VSILFILE * fp ) + +{ + unsigned char cChar = (unsigned char)nChar; + return VSIFWriteL(&cChar, 1, 1, fp); +} + +/************************************************************************/ +/* VSIIngestFile() */ +/************************************************************************/ + +/** + * \brief Ingest a file into memory. + * + * Read the whole content of a file into a memory buffer. + * + * Either fp or pszFilename can be NULL, but not both at the same time. + * + * If fp is passed non-NULL, it is the responsibility of the caller to + * close it. + * + * If non-NULL, the returned buffer is guaranteed to be NUL-terminated. + * + * @param fp file handle opened with VSIFOpenL(). + * @param pszFilename filename. + * @param ppabyRet pointer to the target buffer. *ppabyRet must be freed with + * VSIFree() + * @param pnSize pointer to variable to store the file size. May be NULL. + * @param nMaxSize maximum size of file allowed. If no limit, set to a negative + * value. + * + * @return TRUE in case of success. + * + * @since GDAL 1.11 + */ + +int VSIIngestFile( VSILFILE* fp, + const char* pszFilename, + GByte** ppabyRet, + vsi_l_offset* pnSize, + GIntBig nMaxSize) +{ + vsi_l_offset nDataLen = 0; + int bFreeFP = FALSE; + + if( fp == NULL && pszFilename == NULL ) + return FALSE; + if( ppabyRet == NULL ) + return FALSE; + + *ppabyRet = NULL; + if( pnSize != NULL ) + *pnSize = 0; + + if( NULL == fp ) + { + fp = VSIFOpenL( pszFilename, "rb" ); + if( NULL == fp ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Cannot open file '%s'", pszFilename ); + return FALSE; + } + bFreeFP = TRUE; + } + else + VSIFSeekL(fp, 0, SEEK_SET); + + if( pszFilename == NULL || + strcmp(pszFilename, "/vsistdin/") == 0 ) + { + vsi_l_offset nDataAlloc = 0; + VSIFSeekL( fp, 0, SEEK_SET ); + while(TRUE) + { + if( nDataLen + 8192 + 1 > nDataAlloc ) + { + nDataAlloc = (nDataAlloc * 4) / 3 + 8192 + 1; + if( nDataAlloc > (vsi_l_offset)(size_t)nDataAlloc ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Input file too large to be opened" ); + VSIFree( *ppabyRet ); + *ppabyRet = NULL; + if( bFreeFP ) + VSIFCloseL( fp ); + return FALSE; + } + GByte* pabyNew = (GByte*)VSIRealloc(*ppabyRet, (size_t)nDataAlloc); + if( pabyNew == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Cannot allocated " CPL_FRMT_GIB " bytes", + nDataAlloc ); + VSIFree( *ppabyRet ); + *ppabyRet = NULL; + if( bFreeFP ) + VSIFCloseL( fp ); + return FALSE; + } + *ppabyRet = pabyNew; + } + int nRead = (int)VSIFReadL( *ppabyRet + nDataLen, 1, 8192, fp ); + nDataLen += nRead; + + if ( nMaxSize >= 0 && nDataLen > (vsi_l_offset)nMaxSize ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Input file too large to be opened" ); + VSIFree( *ppabyRet ); + *ppabyRet = NULL; + if( pnSize != NULL ) + *pnSize = 0; + if( bFreeFP ) + VSIFCloseL( fp ); + return FALSE; + } + + if( pnSize != NULL ) + *pnSize += nRead; + (*ppabyRet)[nDataLen] = '\0'; + if( nRead == 0 ) + break; + } + } + else + { + VSIFSeekL( fp, 0, SEEK_END ); + nDataLen = VSIFTellL( fp ); + + // With "large" VSI I/O API we can read data chunks larger than VSIMalloc + // could allocate. Catch it here. + if ( nDataLen > (vsi_l_offset)(size_t)nDataLen || + (nMaxSize >= 0 && nDataLen > (vsi_l_offset)nMaxSize) ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Input file too large to be opened" ); + if( bFreeFP ) + VSIFCloseL( fp ); + return FALSE; + } + + VSIFSeekL( fp, 0, SEEK_SET ); + + *ppabyRet = (GByte*)VSIMalloc((size_t)(nDataLen + 1)); + if( NULL == *ppabyRet ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "Cannot allocated " CPL_FRMT_GIB " bytes", + nDataLen + 1 ); + if( bFreeFP ) + VSIFCloseL( fp ); + return FALSE; + } + + (*ppabyRet)[nDataLen] = '\0'; + if( ( nDataLen != VSIFReadL( *ppabyRet, 1, (size_t)nDataLen, fp ) ) ) + { + CPLError( CE_Failure, CPLE_FileIO, + "Cannot read " CPL_FRMT_GIB " bytes", + nDataLen ); + VSIFree( *ppabyRet ); + *ppabyRet = NULL; + if( bFreeFP ) + VSIFCloseL( fp ); + return FALSE; + } + if( pnSize != NULL ) + *pnSize = nDataLen; + } + if( bFreeFP ) + VSIFCloseL( fp ); + return TRUE; +} + +/************************************************************************/ +/* VSIFGetNativeFileDescriptorL() */ +/************************************************************************/ + +/** + * \brief Returns the "native" file descriptor for the virtual handle. + * + * This will only return a non-NULL value for "real" files handled by the + * operating system (to be opposed to GDAL virtual file systems). + * + * On POSIX systems, this will be a integer value ("fd") cast as a void*. + * On Windows systems, this will be the HANDLE. + * + * @param fp file handle opened with VSIFOpenL(). + * + * @return the native file descriptor, or NULL. + */ + +void *VSIFGetNativeFileDescriptorL( VSILFILE* fp ) +{ + VSIVirtualHandle *poFileHandle = (VSIVirtualHandle *) fp; + + return poFileHandle->GetNativeFileDescriptor(); +} + + +/************************************************************************/ +/* ==================================================================== */ +/* VSIFileManager() */ +/* ==================================================================== */ +/************************************************************************/ + +/* +** Notes on Multithreading: +** +** The VSIFileManager maintains a list of file type handlers (mem, large +** file, etc). It should be thread safe as long as all the handlers are +** instantiated before multiple threads begin to operate. +**/ + +/************************************************************************/ +/* VSIFileManager() */ +/************************************************************************/ + +VSIFileManager::VSIFileManager() + +{ + poDefaultHandler = NULL; +} + +/************************************************************************/ +/* ~VSIFileManager() */ +/************************************************************************/ + +VSIFileManager::~VSIFileManager() +{ + std::map::const_iterator iter; + + for( iter = oHandlers.begin(); + iter != oHandlers.end(); + ++iter ) + { + delete iter->second; + } + + delete poDefaultHandler; +} + + +/************************************************************************/ +/* Get() */ +/************************************************************************/ + +static VSIFileManager *poManager = NULL; +static CPLMutex* hVSIFileManagerMutex = NULL; + +VSIFileManager *VSIFileManager::Get() + +{ + static volatile int nConstructerPID = 0; + if( poManager != NULL ) + { + if( nConstructerPID != 0 ) + { + int nCurrentPID = (int)CPLGetPID(); + if( nConstructerPID != nCurrentPID ) + { + //printf("Thread %d: Waiting for VSIFileManager to be finished by other thread.\n", nCurrentPID); + { + CPLMutexHolder oHolder( &hVSIFileManagerMutex ); + } + //printf("Thread %d: End of wait for VSIFileManager construction to be finished\n", nCurrentPID); + CPLAssert(nConstructerPID == 0); + } + } + return poManager; + } + + CPLMutexHolder oHolder2( &hVSIFileManagerMutex ); + if( poManager == NULL ) + { + nConstructerPID = (int)CPLGetPID(); + //printf("Thread %d: VSIFileManager in construction\n", nConstructerPID); + poManager = new VSIFileManager; + VSIInstallLargeFileHandler(); + VSIInstallSubFileHandler(); + VSIInstallMemFileHandler(); +#ifdef HAVE_LIBZ + VSIInstallGZipFileHandler(); + VSIInstallZipFileHandler(); +#endif +#ifdef HAVE_CURL + VSIInstallCurlFileHandler(); + VSIInstallCurlStreamingFileHandler(); +#endif + VSIInstallStdinHandler(); + VSIInstallStdoutHandler(); + VSIInstallSparseFileHandler(); + VSIInstallTarFileHandler(); + //printf("Thread %d: VSIFileManager construction finished\n", nConstructerPID); + nConstructerPID = 0; + } + + return poManager; +} + +/************************************************************************/ +/* GetHandler() */ +/************************************************************************/ + +VSIFilesystemHandler *VSIFileManager::GetHandler( const char *pszPath ) + +{ + VSIFileManager *poThis = Get(); + std::map::const_iterator iter; + int nPathLen = strlen(pszPath); + + for( iter = poThis->oHandlers.begin(); + iter != poThis->oHandlers.end(); + ++iter ) + { + const char* pszIterKey = iter->first.c_str(); + int nIterKeyLen = iter->first.size(); + if( strncmp(pszPath,pszIterKey,nIterKeyLen) == 0 ) + return iter->second; + + /* "/vsimem\foo" should be handled as "/vsimem/foo" */ + if (nIterKeyLen && nPathLen > nIterKeyLen && + pszIterKey[nIterKeyLen-1] == '/' && + pszPath[nIterKeyLen-1] == '\\' && + strncmp(pszPath,pszIterKey,nIterKeyLen-1) == 0 ) + return iter->second; + + /* /vsimem should be treated as a match for /vsimem/ */ + if( nPathLen == nIterKeyLen - 1 + && strncmp(pszPath,pszIterKey,nIterKeyLen-1) == 0 ) + return iter->second; + } + + return poThis->poDefaultHandler; +} + +/************************************************************************/ +/* InstallHandler() */ +/************************************************************************/ + +void VSIFileManager::InstallHandler( const std::string& osPrefix, + VSIFilesystemHandler *poHandler ) + +{ + if( osPrefix == "" ) + Get()->poDefaultHandler = poHandler; + else + Get()->oHandlers[osPrefix] = poHandler; +} + +/************************************************************************/ +/* VSICleanupFileManager() */ +/************************************************************************/ + +void VSICleanupFileManager() + +{ + if( poManager ) + { + delete poManager; + poManager = NULL; + } + + if( hVSIFileManagerMutex != NULL ) + { + CPLDestroyMutex(hVSIFileManagerMutex); + hVSIFileManagerMutex = NULL; + } +} + +/************************************************************************/ +/* ReadMultiRange() */ +/************************************************************************/ + +int VSIVirtualHandle::ReadMultiRange( int nRanges, void ** ppData, + const vsi_l_offset* panOffsets, + const size_t* panSizes ) +{ + int nRet = 0; + vsi_l_offset nCurOffset = Tell(); + for(int i=0;i + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include +#include + +#define ENABLE_DEBUG 0 + +CPL_CVSID("$Id: cpl_vsil_abstract_archive.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* ~VSIArchiveEntryFileOffset() */ +/************************************************************************/ + +VSIArchiveEntryFileOffset::~VSIArchiveEntryFileOffset() +{ +} + +/************************************************************************/ +/* ~VSIArchiveReader() */ +/************************************************************************/ + +VSIArchiveReader::~VSIArchiveReader() +{ +} + +/************************************************************************/ +/* VSIArchiveFilesystemHandler() */ +/************************************************************************/ + +VSIArchiveFilesystemHandler::VSIArchiveFilesystemHandler() +{ + hMutex = NULL; +} + +/************************************************************************/ +/* ~VSIArchiveFilesystemHandler() */ +/************************************************************************/ + +VSIArchiveFilesystemHandler::~VSIArchiveFilesystemHandler() + +{ + std::map::const_iterator iter; + + for( iter = oFileList.begin(); iter != oFileList.end(); ++iter ) + { + VSIArchiveContent* content = iter->second; + int i; + for(i=0;inEntries;i++) + { + delete content->entries[i].file_pos; + CPLFree(content->entries[i].fileName); + } + CPLFree(content->entries); + delete content; + } + + if( hMutex != NULL ) + CPLDestroyMutex( hMutex ); + hMutex = NULL; +} + +/************************************************************************/ +/* GetContentOfArchive() */ +/************************************************************************/ + +const VSIArchiveContent* VSIArchiveFilesystemHandler::GetContentOfArchive + (const char* archiveFilename, VSIArchiveReader* poReader) +{ + CPLMutexHolder oHolder( &hMutex ); + + if (oFileList.find(archiveFilename) != oFileList.end() ) + { + return oFileList[archiveFilename]; + } + + int bMustClose = (poReader == NULL); + if (poReader == NULL) + { + poReader = CreateReader(archiveFilename); + if (!poReader) + return NULL; + } + + if (poReader->GotoFirstFile() == FALSE) + { + if (bMustClose) + delete(poReader); + return NULL; + } + + VSIArchiveContent* content = new VSIArchiveContent; + content->nEntries = 0; + content->entries = NULL; + oFileList[archiveFilename] = content; + + std::set oSet; + + do + { + CPLString osFileName = poReader->GetFileName(); + const char* fileName = osFileName.c_str(); + + /* Remove ./ pattern at the beginning of a filename */ + if (fileName[0] == '.' && fileName[1] == '/') + { + fileName += 2; + if (fileName[0] == '\0') + continue; + } + + char* pszStrippedFileName = CPLStrdup(fileName); + char* pszIter; + for(pszIter = pszStrippedFileName;*pszIter;pszIter++) + { + if (*pszIter == '\\') + *pszIter = '/'; + } + + int bIsDir = strlen(fileName) > 0 && + fileName[strlen(fileName)-1] == '/'; + if (bIsDir) + { + /* Remove trailing slash */ + pszStrippedFileName[strlen(fileName)-1] = 0; + } + + if (oSet.find(pszStrippedFileName) == oSet.end()) + { + oSet.insert(pszStrippedFileName); + + /* Add intermediate directory structure */ + for(pszIter = pszStrippedFileName;*pszIter;pszIter++) + { + if (*pszIter == '/') + { + char* pszStrippedFileName2 = CPLStrdup(pszStrippedFileName); + pszStrippedFileName2[pszIter - pszStrippedFileName] = 0; + if (oSet.find(pszStrippedFileName2) == oSet.end()) + { + oSet.insert(pszStrippedFileName2); + + content->entries = (VSIArchiveEntry*)CPLRealloc(content->entries, + sizeof(VSIArchiveEntry) * (content->nEntries + 1)); + content->entries[content->nEntries].fileName = pszStrippedFileName2; + content->entries[content->nEntries].nModifiedTime = poReader->GetModifiedTime(); + content->entries[content->nEntries].uncompressed_size = 0; + content->entries[content->nEntries].bIsDir = TRUE; + content->entries[content->nEntries].file_pos = NULL; + if (ENABLE_DEBUG) + CPLDebug("VSIArchive", "[%d] %s : " CPL_FRMT_GUIB " bytes", content->nEntries+1, + content->entries[content->nEntries].fileName, + content->entries[content->nEntries].uncompressed_size); + content->nEntries++; + } + else + { + CPLFree(pszStrippedFileName2); + } + } + } + + content->entries = (VSIArchiveEntry*)CPLRealloc(content->entries, + sizeof(VSIArchiveEntry) * (content->nEntries + 1)); + content->entries[content->nEntries].fileName = pszStrippedFileName; + content->entries[content->nEntries].nModifiedTime = poReader->GetModifiedTime(); + content->entries[content->nEntries].uncompressed_size = poReader->GetFileSize(); + content->entries[content->nEntries].bIsDir = bIsDir; + content->entries[content->nEntries].file_pos = poReader->GetFileOffset(); + if (ENABLE_DEBUG) + CPLDebug("VSIArchive", "[%d] %s : " CPL_FRMT_GUIB " bytes", content->nEntries+1, + content->entries[content->nEntries].fileName, + content->entries[content->nEntries].uncompressed_size); + content->nEntries++; + } + else + { + CPLFree(pszStrippedFileName); + } + } while(poReader->GotoNextFile()); + + if (bMustClose) + delete(poReader); + + return content; +} + +/************************************************************************/ +/* FindFileInArchive() */ +/************************************************************************/ + +int VSIArchiveFilesystemHandler::FindFileInArchive(const char* archiveFilename, + const char* fileInArchiveName, + const VSIArchiveEntry** archiveEntry) +{ + if (fileInArchiveName == NULL) + return FALSE; + + const VSIArchiveContent* content = GetContentOfArchive(archiveFilename); + if (content) + { + int i; + for(i=0;inEntries;i++) + { + if (strcmp(fileInArchiveName, content->entries[i].fileName) == 0) + { + if (archiveEntry) + *archiveEntry = &content->entries[i]; + return TRUE; + } + } + } + return FALSE; +} + +/************************************************************************/ +/* SplitFilename() */ +/************************************************************************/ + +char* VSIArchiveFilesystemHandler::SplitFilename(const char *pszFilename, + CPLString &osFileInArchive, + int bCheckMainFileExists) +{ + int i = 0; + + if (strcmp(pszFilename, GetPrefix()) == 0) + return NULL; + + /* Allow natural chaining of VSI drivers without requiring double slash */ + + CPLString osDoubleVsi(GetPrefix()); + osDoubleVsi += "/vsi"; + + if (strncmp(pszFilename, osDoubleVsi.c_str(), osDoubleVsi.size()) == 0) + pszFilename += strlen(GetPrefix()); + else + pszFilename += strlen(GetPrefix()) + 1; + + while(pszFilename[i]) + { + std::vector oExtensions = GetExtensions(); + std::vector::const_iterator iter; + int nToSkip = 0; + + for( iter = oExtensions.begin(); iter != oExtensions.end(); ++iter ) + { + const CPLString& osExtension = *iter; + if (EQUALN(pszFilename + i, osExtension.c_str(), strlen(osExtension.c_str()))) + { + nToSkip = strlen(osExtension.c_str()); + break; + } + } + + if (nToSkip != 0) + { + VSIStatBufL statBuf; + char* archiveFilename = CPLStrdup(pszFilename); + int bArchiveFileExists = FALSE; + + if (archiveFilename[i + nToSkip] == '/' || + archiveFilename[i + nToSkip] == '\\') + { + archiveFilename[i + nToSkip] = 0; + } + + if (!bCheckMainFileExists) + { + bArchiveFileExists = TRUE; + } + else + { + CPLMutexHolder oHolder( &hMutex ); + + if (oFileList.find(archiveFilename) != oFileList.end() ) + { + bArchiveFileExists = TRUE; + } + } + + if (!bArchiveFileExists) + { + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( archiveFilename ); + if (poFSHandler->Stat(archiveFilename, &statBuf, + VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 && + !VSI_ISDIR(statBuf.st_mode)) + { + bArchiveFileExists = TRUE; + } + } + + if (bArchiveFileExists) + { + if (pszFilename[i + nToSkip] == '/' || + pszFilename[i + nToSkip] == '\\') + { + char* pszArchiveInFileName = CPLStrdup(pszFilename + i + nToSkip + 1); + + /* Replace a/../b by b and foo/a/../b by foo/b */ + while(TRUE) + { + char* pszPrevDir = strstr(pszArchiveInFileName, "/../"); + if (pszPrevDir == NULL || pszPrevDir == pszArchiveInFileName) + break; + + char* pszPrevSlash = pszPrevDir - 1; + while(pszPrevSlash != pszArchiveInFileName && + *pszPrevSlash != '/') + pszPrevSlash --; + if (pszPrevSlash == pszArchiveInFileName) + memmove(pszArchiveInFileName, pszPrevDir + nToSkip, strlen(pszPrevDir + nToSkip) + 1); + else + memmove(pszPrevSlash + 1, pszPrevDir + nToSkip, strlen(pszPrevDir + nToSkip) + 1); + } + + osFileInArchive = pszArchiveInFileName; + CPLFree(pszArchiveInFileName); + } + else + osFileInArchive = ""; + + /* Remove trailing slash */ + if (osFileInArchive.size()) + { + char lastC = osFileInArchive[strlen(osFileInArchive) - 1]; + if (lastC == '\\' || lastC == '/') + osFileInArchive.resize(strlen(osFileInArchive) - 1); + } + + return archiveFilename; + } + CPLFree(archiveFilename); + } + i++; + } + return NULL; +} + +/************************************************************************/ +/* OpenArchiveFile() */ +/************************************************************************/ + +VSIArchiveReader* VSIArchiveFilesystemHandler::OpenArchiveFile(const char* archiveFilename, + const char* fileInArchiveName) +{ + VSIArchiveReader* poReader = CreateReader(archiveFilename); + + if (poReader == NULL) + { + return NULL; + } + + if (fileInArchiveName == NULL || strlen(fileInArchiveName) == 0) + { + if (poReader->GotoFirstFile() == FALSE) + { + delete(poReader); + return NULL; + } + + /* Skip optionnal leading subdir */ + CPLString osFileName = poReader->GetFileName(); + const char* fileName = osFileName.c_str(); + if (fileName[strlen(fileName)-1] == '/' || fileName[strlen(fileName)-1] == '\\') + { + if (poReader->GotoNextFile() == FALSE) + { + delete(poReader); + return NULL; + } + } + + if (poReader->GotoNextFile()) + { + CPLString msg; + msg.Printf("Support only 1 file in archive file %s when no explicit in-archive filename is specified", + archiveFilename); + const VSIArchiveContent* content = GetContentOfArchive(archiveFilename, poReader); + if (content) + { + int i; + msg += "\nYou could try one of the following :\n"; + for(i=0;inEntries;i++) + { + msg += CPLString().Printf(" %s/%s/%s\n", GetPrefix(), archiveFilename, content->entries[i].fileName); + } + } + + CPLError(CE_Failure, CPLE_NotSupported, "%s", msg.c_str()); + + delete(poReader); + return NULL; + } + } + else + { + const VSIArchiveEntry* archiveEntry = NULL; + if (FindFileInArchive(archiveFilename, fileInArchiveName, &archiveEntry) == FALSE || + archiveEntry->bIsDir) + { + delete(poReader); + return NULL; + } + if (!poReader->GotoFileOffset(archiveEntry->file_pos)) + { + delete poReader; + return NULL; + } + } + return poReader; +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSIArchiveFilesystemHandler::Stat( const char *pszFilename, + VSIStatBufL *pStatBuf, + CPL_UNUSED int nFlags ) +{ + int ret = -1; + CPLString osFileInArchive; + + memset(pStatBuf, 0, sizeof(VSIStatBufL)); + + char* archiveFilename = SplitFilename(pszFilename, osFileInArchive, TRUE); + if (archiveFilename == NULL) + return -1; + + if (strlen(osFileInArchive) != 0) + { + if (ENABLE_DEBUG) CPLDebug("VSIArchive", "Looking for %s %s\n", + archiveFilename, osFileInArchive.c_str()); + + const VSIArchiveEntry* archiveEntry = NULL; + if (FindFileInArchive(archiveFilename, osFileInArchive, &archiveEntry)) + { + /* Patching st_size with uncompressed file size */ + pStatBuf->st_size = archiveEntry->uncompressed_size; + pStatBuf->st_mtime = (time_t)archiveEntry->nModifiedTime; + if (archiveEntry->bIsDir) + pStatBuf->st_mode = S_IFDIR; + else + pStatBuf->st_mode = S_IFREG; + ret = 0; + } + } + else + { + VSIArchiveReader* poReader = CreateReader(archiveFilename); + CPLFree(archiveFilename); + archiveFilename = NULL; + + if (poReader != NULL && poReader->GotoFirstFile()) + { + /* Skip optionnal leading subdir */ + CPLString osFileName = poReader->GetFileName(); + const char* fileName = osFileName.c_str(); + if (fileName[strlen(fileName)-1] == '/' || fileName[strlen(fileName)-1] == '\\') + { + if (poReader->GotoNextFile() == FALSE) + { + delete(poReader); + return -1; + } + } + + if (poReader->GotoNextFile()) + { + /* Several files in archive --> treat as dir */ + pStatBuf->st_size = 0; + pStatBuf->st_mode = S_IFDIR; + } + else + { + /* Patching st_size with uncompressed file size */ + pStatBuf->st_size = poReader->GetFileSize(); + pStatBuf->st_mtime = (time_t)poReader->GetModifiedTime(); + pStatBuf->st_mode = S_IFREG; + } + + ret = 0; + } + + delete(poReader); + } + + CPLFree(archiveFilename); + return ret; +} + +/************************************************************************/ +/* Unlink() */ +/************************************************************************/ + +int VSIArchiveFilesystemHandler::Unlink( CPL_UNUSED const char *pszFilename ) +{ + return -1; +} + +/************************************************************************/ +/* Rename() */ +/************************************************************************/ + +int VSIArchiveFilesystemHandler::Rename( CPL_UNUSED const char *oldpath, + CPL_UNUSED const char *newpath ) +{ + return -1; +} + +/************************************************************************/ +/* Mkdir() */ +/************************************************************************/ + +int VSIArchiveFilesystemHandler::Mkdir( CPL_UNUSED const char *pszDirname, + CPL_UNUSED long nMode ) +{ + return -1; +} + +/************************************************************************/ +/* Rmdir() */ +/************************************************************************/ + +int VSIArchiveFilesystemHandler::Rmdir( CPL_UNUSED const char *pszDirname ) +{ + return -1; +} + +/************************************************************************/ +/* ReadDir() */ +/************************************************************************/ + +char** VSIArchiveFilesystemHandler::ReadDir( const char *pszDirname ) +{ + CPLString osInArchiveSubDir; + char* archiveFilename = SplitFilename(pszDirname, osInArchiveSubDir, TRUE); + if (archiveFilename == NULL) + return NULL; + int lenInArchiveSubDir = strlen(osInArchiveSubDir); + + char **papszDir = NULL; + + const VSIArchiveContent* content = GetContentOfArchive(archiveFilename); + if (!content) + { + CPLFree(archiveFilename); + return NULL; + } + + if (ENABLE_DEBUG) CPLDebug("VSIArchive", "Read dir %s", pszDirname); + int i; + for(i=0;inEntries;i++) + { + const char* fileName = content->entries[i].fileName; + /* Only list entries at the same level of inArchiveSubDir */ + if (lenInArchiveSubDir != 0 && + strncmp(fileName, osInArchiveSubDir, lenInArchiveSubDir) == 0 && + (fileName[lenInArchiveSubDir] == '/' || fileName[lenInArchiveSubDir] == '\\') && + fileName[lenInArchiveSubDir + 1] != 0) + { + const char* slash = strchr(fileName + lenInArchiveSubDir + 1, '/'); + if (slash == NULL) + slash = strchr(fileName + lenInArchiveSubDir + 1, '\\'); + if (slash == NULL || slash[1] == 0) + { + char* tmpFileName = CPLStrdup(fileName); + if (slash != NULL) + { + tmpFileName[strlen(tmpFileName)-1] = 0; + } + if (ENABLE_DEBUG) + CPLDebug("VSIArchive", "Add %s as in directory %s\n", + tmpFileName + lenInArchiveSubDir + 1, pszDirname); + papszDir = CSLAddString(papszDir, tmpFileName + lenInArchiveSubDir + 1); + CPLFree(tmpFileName); + } + } + else if (lenInArchiveSubDir == 0 && + strchr(fileName, '/') == NULL && strchr(fileName, '\\') == NULL) + { + /* Only list toplevel files and directories */ + if (ENABLE_DEBUG) CPLDebug("VSIArchive", "Add %s as in directory %s\n", fileName, pszDirname); + papszDir = CSLAddString(papszDir, fileName); + } + } + + CPLFree(archiveFilename); + return papszDir; +} diff --git a/bazaar/plugin/gdal/port/cpl_vsil_buffered_reader.cpp b/bazaar/plugin/gdal/port/cpl_vsil_buffered_reader.cpp new file mode 100644 index 000000000..6124c3862 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_buffered_reader.cpp @@ -0,0 +1,323 @@ +/****************************************************************************** + * $Id: cpl_vsil_buffered_reader.cpp 28493 2015-02-16 09:49:16Z rouault $ + * + * Project: VSI Virtual File System + * Purpose: Implementation of buffered reader IO functions. + * Author: Even Rouault, even.rouault at mines-paris.org + * + ****************************************************************************** + * Copyright (c) 2010-2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* The intent of this class is to be a wrapper around an underlying virtual */ +/* handle and add very basic caching of last read bytes, so that a backward */ +/* seek of a few bytes doesn't require a seek on the underlying virtual handle. */ +/* This enable us to improve dramatically the performance of CPLReadLine2L() on */ +/* a gzip file */ + +#include "cpl_vsi_virtual.h" + +#include "cpl_port.h" + +#define MAX_BUFFER_SIZE 65536 + +CPL_CVSID("$Id: cpl_vsil_buffered_reader.cpp 28493 2015-02-16 09:49:16Z rouault $"); + +class VSIBufferedReaderHandle : public VSIVirtualHandle +{ + VSIVirtualHandle* poBaseHandle; + GByte* pabyBuffer; + GUIntBig nBufferOffset; + int nBufferSize; + GUIntBig nCurOffset; + int bNeedBaseHandleSeek; + int bEOF; + vsi_l_offset nSheatFileSize; + + int SeekBaseTo(vsi_l_offset nTargetOffset); + + public: + + VSIBufferedReaderHandle(VSIVirtualHandle* poBaseHandle); + VSIBufferedReaderHandle(VSIVirtualHandle* poBaseHandle, + const GByte* pabyBeginningContent, + vsi_l_offset nSheatFileSizeIn); + ~VSIBufferedReaderHandle(); + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); +}; + +/************************************************************************/ +/* VSICreateBufferedReaderHandle() */ +/************************************************************************/ + +VSIVirtualHandle* VSICreateBufferedReaderHandle(VSIVirtualHandle* poBaseHandle) +{ + return new VSIBufferedReaderHandle(poBaseHandle); +} + +VSIVirtualHandle* VSICreateBufferedReaderHandle(VSIVirtualHandle* poBaseHandle, + const GByte* pabyBeginningContent, + vsi_l_offset nSheatFileSizeIn) +{ + return new VSIBufferedReaderHandle(poBaseHandle, + pabyBeginningContent, + nSheatFileSizeIn); +} + +/************************************************************************/ +/* VSIBufferedReaderHandle() */ +/************************************************************************/ + +VSIBufferedReaderHandle::VSIBufferedReaderHandle(VSIVirtualHandle* poBaseHandle) +{ + this->poBaseHandle = poBaseHandle; + pabyBuffer = (GByte*)CPLMalloc(MAX_BUFFER_SIZE); + nBufferOffset = 0; + nBufferSize = 0; + nCurOffset = 0; + bNeedBaseHandleSeek = FALSE; + bEOF = FALSE; + nSheatFileSize = 0; +} + +VSIBufferedReaderHandle::VSIBufferedReaderHandle(VSIVirtualHandle* poBaseHandle, + const GByte* pabyBeginningContent, + vsi_l_offset nSheatFileSizeIn) +{ + this->poBaseHandle = poBaseHandle; + nBufferOffset = 0; + nBufferSize = (int)poBaseHandle->Tell(); + pabyBuffer = (GByte*)CPLMalloc(MAX(MAX_BUFFER_SIZE,nBufferSize)); + memcpy(pabyBuffer, pabyBeginningContent, nBufferSize); + nCurOffset = 0; + bNeedBaseHandleSeek = TRUE; + bEOF = FALSE; + nSheatFileSize = nSheatFileSizeIn; +} + +/************************************************************************/ +/* ~VSIBufferedReaderHandle() */ +/************************************************************************/ + +VSIBufferedReaderHandle::~VSIBufferedReaderHandle() +{ + delete poBaseHandle; + CPLFree(pabyBuffer); +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIBufferedReaderHandle::Seek( vsi_l_offset nOffset, int nWhence ) +{ + //CPLDebug( "BUFFERED", "Seek(%d,%d)", (int)nOffset, (int)nWhence); + bEOF = FALSE; + if (nWhence == SEEK_CUR) + nCurOffset += nOffset; + else if (nWhence == SEEK_END) + { + if( nSheatFileSize ) + nCurOffset = nSheatFileSize; + else + { + poBaseHandle->Seek(nOffset, nWhence); + nCurOffset = poBaseHandle->Tell(); + bNeedBaseHandleSeek = TRUE; + } + } + else + nCurOffset = nOffset; + + return 0; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIBufferedReaderHandle::Tell() +{ + //CPLDebug( "BUFFERED", "Tell() = %d", (int)nCurOffset); + return nCurOffset; +} + +/************************************************************************/ +/* SeekBaseTo() */ +/************************************************************************/ + +int VSIBufferedReaderHandle::SeekBaseTo(vsi_l_offset nTargetOffset) +{ + if( poBaseHandle->Seek(nTargetOffset, SEEK_SET) == 0 ) + return TRUE; + + nCurOffset = poBaseHandle->Tell(); + if( nCurOffset > nTargetOffset ) + return FALSE; + char abyTemp[8192]; + while(TRUE) + { + int nToRead = (int) MIN(8192, nTargetOffset - nCurOffset); + int nRead = (int)poBaseHandle->Read(abyTemp, 1, nToRead ); + nCurOffset += nRead; + + if (nRead < nToRead) + { + bEOF = TRUE; + return FALSE; + } + if (nToRead < 8192) + break; + } + return TRUE; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIBufferedReaderHandle::Read( void *pBuffer, size_t nSize, size_t nMemb ) +{ + const size_t nTotalToRead = nSize * nMemb; + //CPLDebug( "BUFFERED", "Read(%d)", (int)nTotalToRead); + + if (nSize == 0) + return 0; + + if (nBufferSize != 0 && + nCurOffset >= nBufferOffset && nCurOffset <= nBufferOffset + nBufferSize) + { + /* We try to read from an offset located within the buffer */ + const int nReadInBuffer = (int) MIN(nTotalToRead, nBufferOffset + nBufferSize - nCurOffset); + memcpy(pBuffer, pabyBuffer + nCurOffset - nBufferOffset, nReadInBuffer); + const int nToReadInFile = nTotalToRead - nReadInBuffer; + if (nToReadInFile > 0) + { + /* The beginning of the the data to read is located in the buffer */ + /* but the end must be read from the file */ + if (bNeedBaseHandleSeek) + { + if( !SeekBaseTo(nBufferOffset + nBufferSize) ) + { + nCurOffset += nReadInBuffer; + return nReadInBuffer / nSize; + } + } + bNeedBaseHandleSeek = FALSE; + //CPLAssert(poBaseHandle->Tell() == nBufferOffset + nBufferSize); + + const int nReadInFile = poBaseHandle->Read((GByte*)pBuffer + nReadInBuffer, 1, nToReadInFile); + const int nRead = nReadInBuffer + nReadInFile; + + nBufferSize = MIN(nRead, MAX_BUFFER_SIZE); + nBufferOffset = nCurOffset + nRead - nBufferSize; + memcpy(pabyBuffer, (GByte*)pBuffer + nRead - nBufferSize, nBufferSize); + + nCurOffset += nRead; + //CPLAssert(poBaseHandle->Tell() == nBufferOffset + nBufferSize); + //CPLAssert(poBaseHandle->Tell() == nCurOffset); + + bEOF = poBaseHandle->Eof(); + + return nRead / nSize; + } + else + { + /* The data to read is completely located within the buffer */ + nCurOffset += nTotalToRead; + return nTotalToRead / nSize; + } + } + else + { + /* We try either to read before or after the buffer, so a seek is necessary */ + if( !SeekBaseTo(nCurOffset) ) + return 0; + bNeedBaseHandleSeek = FALSE; + const int nReadInFile = poBaseHandle->Read(pBuffer, 1, nTotalToRead); + nBufferSize = MIN(nReadInFile, MAX_BUFFER_SIZE); + nBufferOffset = nCurOffset + nReadInFile - nBufferSize; + memcpy(pabyBuffer, (GByte*)pBuffer + nReadInFile - nBufferSize, nBufferSize); + + nCurOffset += nReadInFile; + //CPLAssert(poBaseHandle->Tell() == nBufferOffset + nBufferSize); + //CPLAssert(poBaseHandle->Tell() == nCurOffset); + + bEOF = poBaseHandle->Eof(); + + return nReadInFile / nSize; + } + +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIBufferedReaderHandle::Write( CPL_UNUSED const void *pBuffer, + CPL_UNUSED size_t nSize, + CPL_UNUSED size_t nMemb ) +{ + CPLError(CE_Failure, CPLE_NotSupported, + "VSIFWriteL is not supported on buffer reader streams\n"); + return 0; +} + + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSIBufferedReaderHandle::Eof() +{ + return bEOF; +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSIBufferedReaderHandle::Flush() +{ + return 0; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIBufferedReaderHandle::Close() +{ + if (poBaseHandle) + { + poBaseHandle->Close(); + delete poBaseHandle; + poBaseHandle = NULL; + } + return 0; +} diff --git a/bazaar/plugin/gdal/port/cpl_vsil_cache.cpp b/bazaar/plugin/gdal/port/cpl_vsil_cache.cpp new file mode 100644 index 000000000..c050e0965 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_cache.cpp @@ -0,0 +1,517 @@ +/****************************************************************************** + * $Id$ + * + * Project: VSI Virtual File System + * Purpose: Implementation of caching IO layer. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * Copyright (c) 2011-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" + +CPL_CVSID("$Id$"); + +/************************************************************************/ +/* ==================================================================== */ +/* VSICacheChunk */ +/* ==================================================================== */ +/************************************************************************/ + +class VSICacheChunk +{ +public: + VSICacheChunk() + { + poLRUPrev = poLRUNext = NULL; + nDataFilled = 0; + bDirty = FALSE; + pabyData = NULL; + } + + virtual ~VSICacheChunk() + { + VSIFree( pabyData ); + } + + bool Allocate( size_t nChunkSize ) + { + CPLAssert( pabyData == NULL ); + pabyData = (GByte *)VSIMalloc( nChunkSize ); + return (pabyData != NULL); + } + + int bDirty; + vsi_l_offset iBlock; + + VSICacheChunk *poLRUPrev; + VSICacheChunk *poLRUNext; + + vsi_l_offset nDataFilled; + GByte *pabyData; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSICachedFile */ +/* ==================================================================== */ +/************************************************************************/ + +class VSICachedFile : public VSIVirtualHandle +{ + public: + VSICachedFile( VSIVirtualHandle *poBaseHandle, + size_t nChunkSize, + size_t nCacheSize ); + ~VSICachedFile() { Close(); } + + void FlushLRU(); + int LoadBlocks( vsi_l_offset nStartBlock, size_t nBlockCount, + void *pBuffer, size_t nBufferSize ); + void Demote( VSICacheChunk * ); + + VSIVirtualHandle *poBase; + + vsi_l_offset nOffset; + vsi_l_offset nFileSize; + + GUIntBig nCacheUsed; + GUIntBig nCacheMax; + + size_t nChunkSize; + + VSICacheChunk *poLRUStart; + VSICacheChunk *poLRUEnd; + + std::vector apoCache; + + int bEOF; + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); + virtual void *GetNativeFileDescriptor() { return poBase->GetNativeFileDescriptor(); } +}; + +/************************************************************************/ +/* VSICachedFile() */ +/************************************************************************/ + +VSICachedFile::VSICachedFile( VSIVirtualHandle *poBaseHandle, size_t nChunkSize, size_t nCacheSize ) + +{ + poBase = poBaseHandle; + this->nChunkSize = nChunkSize; + + nCacheUsed = 0; + if ( nCacheSize == 0 ) + nCacheMax = CPLScanUIntBig( + CPLGetConfigOption( "VSI_CACHE_SIZE", "25000000" ), 40 ); + else + nCacheMax = nCacheSize; + + poLRUStart = NULL; + poLRUEnd = NULL; + + poBase->Seek( 0, SEEK_END ); + nFileSize = poBase->Tell(); + + nOffset = 0; + bEOF = FALSE; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSICachedFile::Close() + +{ + size_t i; + for( i = 0; i < apoCache.size(); i++ ) + delete apoCache[i]; + + apoCache.resize( 0 ); + + poLRUStart = NULL; + poLRUEnd = NULL; + + nCacheUsed = 0; + + if( poBase ) + { + poBase->Close(); + delete poBase; + poBase = NULL; + } + + return 0; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSICachedFile::Seek( vsi_l_offset nReqOffset, int nWhence ) + +{ + bEOF = FALSE; + + if( nWhence == SEEK_SET ) + { + // use offset directly. + } + + else if( nWhence == SEEK_CUR ) + { + nReqOffset += nOffset; + } + + else if( nWhence == SEEK_END ) + { + nReqOffset += nFileSize; + } + + nOffset = nReqOffset; + + return 0; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSICachedFile::Tell() + +{ + return nOffset; +} + +/************************************************************************/ +/* FlushLRU() */ +/************************************************************************/ + +void VSICachedFile::FlushLRU() + +{ + CPLAssert( poLRUStart != NULL ); + + VSICacheChunk *poBlock = poLRUStart; + + CPLAssert( nCacheUsed >= poBlock->nDataFilled ); + + nCacheUsed -= poBlock->nDataFilled; + + poLRUStart = poBlock->poLRUNext; + if( poLRUEnd == poBlock ) + poLRUEnd = NULL; + + if( poBlock->poLRUNext != NULL ) + poBlock->poLRUNext->poLRUPrev = NULL; + + CPLAssert( !poBlock->bDirty ); + + apoCache[poBlock->iBlock] = NULL; + + delete poBlock; +} + +/************************************************************************/ +/* Demote() */ +/* */ +/* Demote the indicated block to the end of the LRU list. */ +/* Potentially integrate the link into the list if it is not */ +/* already there. */ +/************************************************************************/ + +void VSICachedFile::Demote( VSICacheChunk *poBlock ) + +{ + // already at end? + if( poLRUEnd == poBlock ) + return; + + if( poLRUStart == poBlock ) + poLRUStart = poBlock->poLRUNext; + + if( poBlock->poLRUPrev != NULL ) + poBlock->poLRUPrev->poLRUNext = poBlock->poLRUNext; + + if( poBlock->poLRUNext != NULL ) + poBlock->poLRUNext->poLRUPrev = poBlock->poLRUPrev; + + poBlock->poLRUNext = NULL; + poBlock->poLRUPrev = NULL; + + if( poLRUEnd != NULL ) + poLRUEnd->poLRUNext = poBlock; + poLRUEnd = poBlock; + + if( poLRUStart == NULL ) + poLRUStart = poBlock; +} + +/************************************************************************/ +/* LoadBlocks() */ +/* */ +/* Load the desired set of blocks. Use pBuffer as a temporary */ +/* buffer if it would be helpful. */ +/************************************************************************/ + +int VSICachedFile::LoadBlocks( vsi_l_offset nStartBlock, size_t nBlockCount, + void *pBuffer, size_t nBufferSize ) + +{ + if( nBlockCount == 0 ) + return 1; + + if( apoCache.size() < nStartBlock + nBlockCount ) + apoCache.resize( nStartBlock + nBlockCount ); + +/* -------------------------------------------------------------------- */ +/* When we want to load only one block, we can directly load it */ +/* into the target buffer with no concern about intermediaries. */ +/* -------------------------------------------------------------------- */ + if( nBlockCount == 1 ) + { + poBase->Seek( (vsi_l_offset)nStartBlock * nChunkSize, SEEK_SET ); + + VSICacheChunk *poBlock = new VSICacheChunk(); + if ( !poBlock || !poBlock->Allocate( nChunkSize ) ) + { + delete poBlock; + return 0; + } + + apoCache[nStartBlock] = poBlock; + + poBlock->iBlock = nStartBlock; + poBlock->nDataFilled = poBase->Read( poBlock->pabyData, 1, nChunkSize ); + nCacheUsed += poBlock->nDataFilled; + + // Merges into the LRU list. + Demote( poBlock ); + + return 1; + } + +/* -------------------------------------------------------------------- */ +/* If the buffer is quite large but not quite large enough to */ +/* hold all the blocks we will take the pain of splitting the */ +/* io request in two in order to avoid allocating a large */ +/* temporary buffer. */ +/* -------------------------------------------------------------------- */ + if( nBufferSize > nChunkSize * 20 + && nBufferSize < nBlockCount * nChunkSize ) + { + if( !LoadBlocks( nStartBlock, 2, pBuffer, nBufferSize ) ) + return 0; + + return LoadBlocks( nStartBlock+2, nBlockCount-2, pBuffer, nBufferSize ); + } + +/* -------------------------------------------------------------------- */ +/* Do we need to allocate our own buffer? */ +/* -------------------------------------------------------------------- */ + GByte *pabyWorkBuffer = (GByte *) pBuffer; + + if( nBufferSize < nChunkSize * nBlockCount ) + pabyWorkBuffer = (GByte *) CPLMalloc(nChunkSize * nBlockCount); + +/* -------------------------------------------------------------------- */ +/* Read the whole request into the working buffer. */ +/* -------------------------------------------------------------------- */ + if( poBase->Seek( (vsi_l_offset)nStartBlock * nChunkSize, SEEK_SET ) != 0 ) + return 0; + + size_t nDataRead = poBase->Read( pabyWorkBuffer, 1, nBlockCount*nChunkSize); + + if( nBlockCount * nChunkSize > nDataRead + nChunkSize - 1 ) + nBlockCount = (nDataRead + nChunkSize - 1) / nChunkSize; + + for( size_t i = 0; i < nBlockCount; i++ ) + { + VSICacheChunk *poBlock = new VSICacheChunk(); + if ( !poBlock || !poBlock->Allocate( nChunkSize ) ) + { + delete poBlock; + return 0; + } + + poBlock->iBlock = nStartBlock + i; + + CPLAssert( apoCache[i+nStartBlock] == NULL ); + + apoCache[i + nStartBlock] = poBlock; + + if( nDataRead >= (i+1) * nChunkSize ) + poBlock->nDataFilled = nChunkSize; + else + poBlock->nDataFilled = nDataRead - i*nChunkSize; + + memcpy( poBlock->pabyData, pabyWorkBuffer + i*nChunkSize, + (size_t) poBlock->nDataFilled ); + + nCacheUsed += poBlock->nDataFilled; + + // Merges into the LRU list. + Demote( poBlock ); + } + + if( pabyWorkBuffer != pBuffer ) + CPLFree( pabyWorkBuffer ); + + return 1; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSICachedFile::Read( void * pBuffer, size_t nSize, size_t nCount ) + +{ + if( nOffset >= nFileSize ) + { + bEOF = TRUE; + return 0; + } + +/* ==================================================================== */ +/* Make sure the cache is loaded for the whole request region. */ +/* ==================================================================== */ + vsi_l_offset nStartBlock = nOffset / nChunkSize; + vsi_l_offset nEndBlock = (nOffset + nSize * nCount - 1) / nChunkSize; + + for( vsi_l_offset iBlock = nStartBlock; iBlock <= nEndBlock; iBlock++ ) + { + if( apoCache.size() <= iBlock || apoCache[iBlock] == NULL ) + { + size_t nBlocksToLoad = 1; + while( iBlock + nBlocksToLoad <= nEndBlock + && (apoCache.size() <= iBlock+nBlocksToLoad + || apoCache[iBlock+nBlocksToLoad] == NULL) ) + nBlocksToLoad++; + + LoadBlocks( iBlock, nBlocksToLoad, pBuffer, nSize * nCount ); + } + } + +/* ==================================================================== */ +/* Copy data into the target buffer to the extent possible. */ +/* ==================================================================== */ + size_t nAmountCopied = 0; + + while( nAmountCopied < nSize * nCount ) + { + vsi_l_offset iBlock = (nOffset + nAmountCopied) / nChunkSize; + size_t nThisCopy; + VSICacheChunk *poBlock = apoCache[iBlock]; + if( poBlock == NULL ) + { + /* We can reach that point when the amount to read exceeds */ + /* the cache size */ + LoadBlocks( iBlock, 1, ((GByte *) pBuffer) + nAmountCopied, + MIN(nSize * nCount - nAmountCopied, nChunkSize) ); + poBlock = apoCache[iBlock]; + CPLAssert(poBlock != NULL); + } + + vsi_l_offset nStartOffset = (vsi_l_offset)iBlock * nChunkSize; + nThisCopy = (size_t) + ((nStartOffset + poBlock->nDataFilled) + - nAmountCopied - nOffset); + + if( nThisCopy > nSize * nCount - nAmountCopied ) + nThisCopy = nSize * nCount - nAmountCopied; + + if( nThisCopy == 0 ) + break; + + memcpy( ((GByte *) pBuffer) + nAmountCopied, + poBlock->pabyData + + (nOffset + nAmountCopied) - nStartOffset, + nThisCopy ); + + nAmountCopied += nThisCopy; + } + + nOffset += nAmountCopied; + +/* -------------------------------------------------------------------- */ +/* Ensure the cache is reduced to our limit. */ +/* -------------------------------------------------------------------- */ + while( nCacheUsed > nCacheMax ) + FlushLRU(); + + size_t nRet = nAmountCopied / nSize; + if (nRet != nCount) + bEOF = TRUE; + return nRet; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSICachedFile::Write( CPL_UNUSED const void * pBuffer, + CPL_UNUSED size_t nSize, + CPL_UNUSED size_t nCount ) +{ + return 0; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSICachedFile::Eof() + +{ + return bEOF; +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSICachedFile::Flush() + +{ + return 0; +} + +/************************************************************************/ +/* VSICreateCachedFile() */ +/************************************************************************/ + +VSIVirtualHandle * +VSICreateCachedFile( VSIVirtualHandle *poBaseHandle, size_t nChunkSize, size_t nCacheSize ) + +{ + return new VSICachedFile( poBaseHandle, nChunkSize, nCacheSize ); +} diff --git a/bazaar/plugin/gdal/port/cpl_vsil_curl.cpp b/bazaar/plugin/gdal/port/cpl_vsil_curl.cpp new file mode 100644 index 000000000..5b14bb137 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_curl.cpp @@ -0,0 +1,2714 @@ +/****************************************************************************** + * $Id: cpl_vsil_curl.cpp 28798 2015-03-27 19:37:50Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement VSI large file api for HTTP/FTP files + * Author: Even Rouault, even.rouault at mines-paris.org + * + ****************************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include "cpl_hash_set.h" +#include "cpl_time.h" +#include "cpl_vsil_curl_priv.h" + +CPL_CVSID("$Id: cpl_vsil_curl.cpp 28798 2015-03-27 19:37:50Z rouault $"); + +#ifndef HAVE_CURL + +void VSIInstallCurlFileHandler(void) +{ + /* not supported */ +} + +/************************************************************************/ +/* VSICurlInstallReadCbk() */ +/************************************************************************/ + +int VSICurlInstallReadCbk (CPL_UNUSED VSILFILE* fp, + CPL_UNUSED VSICurlReadCbkFunc pfnReadCbk, + CPL_UNUSED void* pfnUserData, + CPL_UNUSED int bStopOnInterrruptUntilUninstall) +{ + return FALSE; +} + + +/************************************************************************/ +/* VSICurlUninstallReadCbk() */ +/************************************************************************/ + +int VSICurlUninstallReadCbk(CPL_UNUSED VSILFILE* fp) +{ + return FALSE; +} + +#else + +#include + +void CPLHTTPSetOptions(CURL *http_handle, char** papszOptions); +void VSICurlSetOptions(CURL* hCurlHandle, const char* pszURL); + +#include + +#define ENABLE_DEBUG 1 + +#define N_MAX_REGIONS 1000 + +#define DOWNLOAD_CHUNCK_SIZE 16384 + +typedef enum +{ + EXIST_UNKNOWN = -1, + EXIST_NO, + EXIST_YES, +} ExistStatus; + +typedef struct +{ + ExistStatus eExists; + int bHastComputedFileSize; + vsi_l_offset fileSize; + int bIsDirectory; + time_t mTime; +} CachedFileProp; + +typedef struct +{ + int bGotFileList; + char** papszFileList; /* only file name without path */ +} CachedDirList; + +typedef struct +{ + unsigned long pszURLHash; + vsi_l_offset nFileOffsetStart; + size_t nSize; + char *pData; +} CachedRegion; + + +static const char* VSICurlGetCacheFileName() +{ + return "gdal_vsicurl_cache.bin"; +} + +/************************************************************************/ +/* VSICurlFindStringSensitiveExceptEscapeSequences() */ +/************************************************************************/ + +static int VSICurlFindStringSensitiveExceptEscapeSequences( char ** papszList, + const char * pszTarget ) + +{ + int i; + + if( papszList == NULL ) + return -1; + + for( i = 0; papszList[i] != NULL; i++ ) + { + const char* pszIter1 = papszList[i]; + const char* pszIter2 = pszTarget; + char ch1, ch2; + /* The comparison is case-sensitive, escape for escaped */ + /* sequences where letters of the hexadecimal sequence */ + /* can be uppercase or lowercase depending on the quoting algorithm */ + while(TRUE) + { + ch1 = *pszIter1; + ch2 = *pszIter2; + if (ch1 == '\0' || ch2 == '\0') + break; + if (ch1 == '%' && ch2 == '%' && + pszIter1[1] != '\0' && pszIter1[2] != '\0' && + pszIter2[1] != '\0' && pszIter2[2] != '\0') + { + if (!EQUALN(pszIter1+1, pszIter2+1, 2)) + break; + pszIter1 += 2; + pszIter2 += 2; + } + if (ch1 != ch2) + break; + pszIter1 ++; + pszIter2 ++; + } + if (ch1 == ch2 && ch1 == '\0') + return i; + } + + return -1; +} + +/************************************************************************/ +/* VSICurlIsFileInList() */ +/************************************************************************/ + +static int VSICurlIsFileInList( char ** papszList, const char * pszTarget ) +{ + int nRet = VSICurlFindStringSensitiveExceptEscapeSequences(papszList, pszTarget); + if (nRet >= 0) + return nRet; + + /* If we didn't find anything, try to URL-escape the target filename */ + char* pszEscaped = CPLEscapeString(pszTarget, -1, CPLES_URL); + if (strcmp(pszTarget, pszEscaped) != 0) + { + nRet = VSICurlFindStringSensitiveExceptEscapeSequences(papszList, pszEscaped); + } + CPLFree(pszEscaped); + return nRet; +} + +/************************************************************************/ +/* VSICurlFilesystemHandler */ +/************************************************************************/ + +typedef struct +{ + CPLString osURL; + CURL *hCurlHandle; +} CachedConnection; + + +class VSICurlFilesystemHandler : public VSIFilesystemHandler +{ + CPLMutex *hMutex; + + CachedRegion **papsRegions; + int nRegions; + + std::map cacheFileSize; + std::map cacheDirList; + + int bUseCacheDisk; + + /* Per-thread Curl connection cache */ + std::map mapConnections; + + char** GetFileList(const char *pszFilename, int* pbGotFileList); + + char** ParseHTMLFileList(const char* pszFilename, + char* pszData, + int* pbGotFileList); +public: + VSICurlFilesystemHandler(); + ~VSICurlFilesystemHandler(); + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); + virtual int Unlink( const char *pszFilename ); + virtual int Rename( const char *oldpath, const char *newpath ); + virtual int Mkdir( const char *pszDirname, long nMode ); + virtual int Rmdir( const char *pszDirname ); + virtual char **ReadDir( const char *pszDirname ); + virtual char **ReadDir( const char *pszDirname, int* pbGotFileList ); + + + const CachedRegion* GetRegion(const char* pszURL, + vsi_l_offset nFileOffsetStart); + + void AddRegion(const char* pszURL, + vsi_l_offset nFileOffsetStart, + size_t nSize, + const char *pData); + + CachedFileProp* GetCachedFileProp(const char* pszURL); + + void AddRegionToCacheDisk(CachedRegion* psRegion); + const CachedRegion* GetRegionFromCacheDisk(const char* pszURL, + vsi_l_offset nFileOffsetStart); + + CURL *GetCurlHandleFor(CPLString osURL); +}; + +/************************************************************************/ +/* VSICurlHandle */ +/************************************************************************/ + +class VSICurlHandle : public VSIVirtualHandle +{ + private: + VSICurlFilesystemHandler* poFS; + + char* pszURL; + + vsi_l_offset curOffset; + vsi_l_offset fileSize; + int bHastComputedFileSize; + ExistStatus eExists; + int bIsDirectory; + time_t mTime; + + vsi_l_offset lastDownloadedOffset; + int nBlocksToDownload; + int bEOF; + + int DownloadRegion(vsi_l_offset startOffset, int nBlocks); + + VSICurlReadCbkFunc pfnReadCbk; + void *pReadCbkUserData; + int bStopOnInterrruptUntilUninstall; + int bInterrupted; + + public: + + VSICurlHandle(VSICurlFilesystemHandler* poFS, const char* pszURL); + ~VSICurlHandle(); + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual int ReadMultiRange( int nRanges, void ** ppData, + const vsi_l_offset* panOffsets, const size_t* panSizes ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); + + int IsKnownFileSize() const { return bHastComputedFileSize; } + vsi_l_offset GetFileSize(); + int Exists(); + int IsDirectory() const { return bIsDirectory; } + time_t GetMTime() const { return mTime; } + + int InstallReadCbk(VSICurlReadCbkFunc pfnReadCbk, + void* pfnUserData, + int bStopOnInterrruptUntilUninstall); + int UninstallReadCbk(); +}; + +/************************************************************************/ +/* VSICurlHandle() */ +/************************************************************************/ + +VSICurlHandle::VSICurlHandle(VSICurlFilesystemHandler* poFS, const char* pszURL) +{ + this->poFS = poFS; + this->pszURL = CPLStrdup(pszURL); + + curOffset = 0; + + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + eExists = cachedFileProp->eExists; + fileSize = cachedFileProp->fileSize; + bHastComputedFileSize = cachedFileProp->bHastComputedFileSize; + bIsDirectory = cachedFileProp->bIsDirectory; + mTime = cachedFileProp->mTime; + + lastDownloadedOffset = -1; + nBlocksToDownload = 1; + bEOF = FALSE; + + pfnReadCbk = NULL; + pReadCbkUserData = NULL; + bStopOnInterrruptUntilUninstall = FALSE; + bInterrupted = FALSE; +} + +/************************************************************************/ +/* ~VSICurlHandle() */ +/************************************************************************/ + +VSICurlHandle::~VSICurlHandle() +{ + CPLFree(pszURL); +} + +/************************************************************************/ +/* InstallReadCbk() */ +/************************************************************************/ + +int VSICurlHandle::InstallReadCbk(VSICurlReadCbkFunc pfnReadCbkIn, + void* pfnUserDataIn, + int bStopOnInterrruptUntilUninstallIn) +{ + if (pfnReadCbk != NULL) + return FALSE; + + pfnReadCbk = pfnReadCbkIn; + pReadCbkUserData = pfnUserDataIn; + bStopOnInterrruptUntilUninstall = bStopOnInterrruptUntilUninstallIn; + bInterrupted = FALSE; + return TRUE; +} + +/************************************************************************/ +/* UninstallReadCbk() */ +/************************************************************************/ + +int VSICurlHandle::UninstallReadCbk() +{ + if (pfnReadCbk == NULL) + return FALSE; + + pfnReadCbk = NULL; + pReadCbkUserData = NULL; + bStopOnInterrruptUntilUninstall = FALSE; + bInterrupted = FALSE; + return TRUE; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSICurlHandle::Seek( vsi_l_offset nOffset, int nWhence ) +{ + if (nWhence == SEEK_SET) + { + curOffset = nOffset; + } + else if (nWhence == SEEK_CUR) + { + curOffset = curOffset + nOffset; + } + else + { + curOffset = GetFileSize() + nOffset; + } + bEOF = FALSE; + return 0; +} + +/************************************************************************/ +/* VSICurlSetOptions() */ +/************************************************************************/ + +void VSICurlSetOptions(CURL* hCurlHandle, const char* pszURL) +{ + curl_easy_setopt(hCurlHandle, CURLOPT_URL, pszURL); + + CPLHTTPSetOptions(hCurlHandle, NULL); + +/* 7.16 */ +#if LIBCURL_VERSION_NUM >= 0x071000 + long option = CURLFTPMETHOD_SINGLECWD; + curl_easy_setopt(hCurlHandle, CURLOPT_FTP_FILEMETHOD, option); +#endif + +/* 7.12.3 */ +#if LIBCURL_VERSION_NUM > 0x070C03 + /* ftp://ftp2.cits.rncan.gc.ca/pub/cantopo/250k_tif/ doesn't like EPSV command */ + curl_easy_setopt(hCurlHandle, CURLOPT_FTP_USE_EPSV, 0); +#endif + + curl_easy_setopt(hCurlHandle, CURLOPT_NOBODY, 0); + curl_easy_setopt(hCurlHandle, CURLOPT_HTTPGET, 1); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADER, 0); + +/* 7.16.4 */ +#if LIBCURL_VERSION_NUM <= 0x071004 + curl_easy_setopt(hCurlHandle, CURLOPT_FTPLISTONLY, 0); +#elif LIBCURL_VERSION_NUM > 0x071004 + curl_easy_setopt(hCurlHandle, CURLOPT_DIRLISTONLY, 0); +#endif + + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION, NULL); +} + + +typedef struct +{ + char* pBuffer; + size_t nSize; + int bIsHTTP; + int bIsInHeader; + int bMultiRange; + vsi_l_offset nStartOffset; + vsi_l_offset nEndOffset; + int nHTTPCode; + vsi_l_offset nContentLength; + int bFoundContentRange; + int bError; + int bDownloadHeaderOnly; + + VSILFILE *fp; + VSICurlReadCbkFunc pfnReadCbk; + void *pReadCbkUserData; + int bInterrupted; +} WriteFuncStruct; + +/************************************************************************/ +/* VSICURLInitWriteFuncStruct() */ +/************************************************************************/ + +static void VSICURLInitWriteFuncStruct(WriteFuncStruct *psStruct, + VSILFILE *fp, + VSICurlReadCbkFunc pfnReadCbk, + void *pReadCbkUserData) +{ + psStruct->pBuffer = NULL; + psStruct->nSize = 0; + psStruct->bIsHTTP = FALSE; + psStruct->bIsInHeader = TRUE; + psStruct->bMultiRange = FALSE; + psStruct->nStartOffset = 0; + psStruct->nEndOffset = 0; + psStruct->nHTTPCode = 0; + psStruct->nContentLength = 0; + psStruct->bFoundContentRange = FALSE; + psStruct->bError = FALSE; + psStruct->bDownloadHeaderOnly = FALSE; + + psStruct->fp = fp; + psStruct->pfnReadCbk = pfnReadCbk; + psStruct->pReadCbkUserData = pReadCbkUserData; + psStruct->bInterrupted = FALSE; +} + +/************************************************************************/ +/* VSICurlHandleWriteFunc() */ +/************************************************************************/ + +static int VSICurlHandleWriteFunc(void *buffer, size_t count, size_t nmemb, void *req) +{ + WriteFuncStruct* psStruct = (WriteFuncStruct*) req; + size_t nSize = count * nmemb; + + char* pNewBuffer = (char*) VSIRealloc(psStruct->pBuffer, + psStruct->nSize + nSize + 1); + if (pNewBuffer) + { + psStruct->pBuffer = pNewBuffer; + memcpy(psStruct->pBuffer + psStruct->nSize, buffer, nSize); + psStruct->pBuffer[psStruct->nSize + nSize] = '\0'; + if (psStruct->bIsHTTP && psStruct->bIsInHeader) + { + char* pszLine = psStruct->pBuffer + psStruct->nSize; + if (EQUALN(pszLine, "HTTP/1.0 ", 9) || + EQUALN(pszLine, "HTTP/1.1 ", 9)) + psStruct->nHTTPCode = atoi(pszLine + 9); + else if (EQUALN(pszLine, "Content-Length: ", 16)) + psStruct->nContentLength = CPLScanUIntBig(pszLine + 16, + strlen(pszLine + 16)); + else if (EQUALN(pszLine, "Content-Range: ", 15)) + psStruct->bFoundContentRange = TRUE; + + /*if (nSize > 2 && pszLine[nSize - 2] == '\r' && + pszLine[nSize - 1] == '\n') + { + pszLine[nSize - 2] = 0; + CPLDebug("VSICURL", "%s", pszLine); + pszLine[nSize - 2] = '\r'; + }*/ + + if (pszLine[0] == '\r' || pszLine[0] == '\n') + { + if (psStruct->bDownloadHeaderOnly) + { + /* If moved permanently/temporarily, go on. Otherwise stop now*/ + if (!(psStruct->nHTTPCode == 301 || psStruct->nHTTPCode == 302)) + return 0; + } + else + { + psStruct->bIsInHeader = FALSE; + + /* Detect servers that don't support range downloading */ + if (psStruct->nHTTPCode == 200 && + !psStruct->bMultiRange && + !psStruct->bFoundContentRange && + (psStruct->nStartOffset != 0 || psStruct->nContentLength > 10 * + (psStruct->nEndOffset - psStruct->nStartOffset + 1))) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Range downloading not supported by this server !"); + psStruct->bError = TRUE; + return 0; + } + } + } + } + else + { + if (psStruct->pfnReadCbk) + { + if ( ! psStruct->pfnReadCbk(psStruct->fp, buffer, nSize, + psStruct->pReadCbkUserData) ) + { + psStruct->bInterrupted = TRUE; + return 0; + } + } + } + psStruct->nSize += nSize; + return nmemb; + } + else + { + return 0; + } +} + + +/************************************************************************/ +/* GetFileSize() */ +/************************************************************************/ + +vsi_l_offset VSICurlHandle::GetFileSize() +{ + WriteFuncStruct sWriteFuncData; + WriteFuncStruct sWriteFuncHeaderData; + + if (bHastComputedFileSize) + return fileSize; + + bHastComputedFileSize = TRUE; + + /* Consider that only the files whose extension ends up with one that is */ + /* listed in CPL_VSIL_CURL_ALLOWED_EXTENSIONS exist on the server */ + /* This can speeds up dramatically open experience, in case the server */ + /* cannot return a file list */ + /* {noext} can be used as a special token to mean file with no extension */ + /* For example : */ + /* gdalinfo --config CPL_VSIL_CURL_ALLOWED_EXTENSIONS ".tif" /vsicurl/http://igskmncngs506.cr.usgs.gov/gmted/Global_tiles_GMTED/075darcsec/bln/W030/30N030W_20101117_gmted_bln075.tif */ + const char* pszAllowedExtensions = + CPLGetConfigOption("CPL_VSIL_CURL_ALLOWED_EXTENSIONS", NULL); + if (pszAllowedExtensions) + { + char** papszExtensions = CSLTokenizeString2( pszAllowedExtensions, ", ", 0 ); + int nURLLen = strlen(pszURL); + int bFound = FALSE; + for(int i=0;papszExtensions[i] != NULL;i++) + { + int nExtensionLen = strlen(papszExtensions[i]); + if( EQUAL(papszExtensions[i], "{noext}") ) + { + if( nURLLen > 4 && strchr(pszURL + nURLLen - 4, '.') == NULL ) + { + bFound = TRUE; + break; + } + } + else if (nURLLen > nExtensionLen && + EQUAL(pszURL + nURLLen - nExtensionLen, papszExtensions[i])) + { + bFound = TRUE; + break; + } + } + + if (!bFound) + { + eExists = EXIST_NO; + fileSize = 0; + + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + cachedFileProp->bHastComputedFileSize = TRUE; + cachedFileProp->fileSize = fileSize; + cachedFileProp->eExists = eExists; + + CSLDestroy(papszExtensions); + + return 0; + } + + CSLDestroy(papszExtensions); + } + +#if LIBCURL_VERSION_NUM < 0x070B00 + /* Curl 7.10.X doesn't manage to unset the CURLOPT_RANGE that would have been */ + /* previously set, so we have to reinit the connection handle */ + poFS->GetCurlHandleFor(""); +#endif + CURL* hCurlHandle = poFS->GetCurlHandleFor(pszURL); + + VSICurlSetOptions(hCurlHandle, pszURL); + + VSICURLInitWriteFuncStruct(&sWriteFuncHeaderData, NULL, NULL, NULL); + + /* HACK for mbtiles driver: proper fix would be to auto-detect servers that don't accept HEAD */ + /* http://a.tiles.mapbox.com/v3/ doesn't accept HEAD, so let's start a GET */ + /* and interrupt is as soon as the header is found */ + if (strstr(pszURL, ".tiles.mapbox.com/") != NULL + || !CSLTestBoolean(CPLGetConfigOption("CPL_VSIL_CURL_USE_HEAD", "YES"))) + { + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, &sWriteFuncHeaderData); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION, VSICurlHandleWriteFunc); + + sWriteFuncHeaderData.bIsHTTP = strncmp(pszURL, "http", 4) == 0; + sWriteFuncHeaderData.bDownloadHeaderOnly = TRUE; + } + else + { + curl_easy_setopt(hCurlHandle, CURLOPT_NOBODY, 1); + curl_easy_setopt(hCurlHandle, CURLOPT_HTTPGET, 0); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADER, 1); + } + + /* We need that otherwise OSGEO4W's libcurl issue a dummy range request */ + /* when doing a HEAD when recycling connections */ + curl_easy_setopt(hCurlHandle, CURLOPT_RANGE, NULL); + + /* Bug with older curl versions (<=7.16.4) and FTP. See http://curl.haxx.se/mail/lib-2007-08/0312.html */ + VSICURLInitWriteFuncStruct(&sWriteFuncData, NULL, NULL, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, VSICurlHandleWriteFunc); + + char szCurlErrBuf[CURL_ERROR_SIZE+1]; + szCurlErrBuf[0] = '\0'; + curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf ); + + double dfSize = 0; + curl_easy_perform(hCurlHandle); + + eExists = EXIST_UNKNOWN; + + if (strncmp(pszURL, "ftp", 3) == 0) + { + if (sWriteFuncData.pBuffer != NULL && + strncmp(sWriteFuncData.pBuffer, "Content-Length: ", strlen( "Content-Length: ")) == 0) + { + const char* pszBuffer = sWriteFuncData.pBuffer + strlen("Content-Length: "); + eExists = EXIST_YES; + fileSize = CPLScanUIntBig(pszBuffer, sWriteFuncData.nSize - strlen("Content-Length: ")); + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "GetFileSize(%s)=" CPL_FRMT_GUIB, + pszURL, fileSize); + } + } + + if (eExists != EXIST_YES) + { + CURLcode code = curl_easy_getinfo(hCurlHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dfSize ); + if (code == 0) + { + eExists = EXIST_YES; + if (dfSize < 0) + fileSize = 0; + else + fileSize = (GUIntBig)dfSize; + } + else + { + eExists = EXIST_NO; + fileSize = 0; + CPLError(CE_Failure, CPLE_AppDefined, "VSICurlHandle::GetFileSize failed"); + } + + long response_code = 0; + curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code); + if (response_code != 200) + { + eExists = EXIST_NO; + fileSize = 0; + } + + /* Try to guess if this is a directory. Generally if this is a directory, */ + /* curl will retry with an URL with slash added */ + char *pszEffectiveURL = NULL; + curl_easy_getinfo(hCurlHandle, CURLINFO_EFFECTIVE_URL, &pszEffectiveURL); + if (pszEffectiveURL != NULL && strncmp(pszURL, pszEffectiveURL, strlen(pszURL)) == 0 && + pszEffectiveURL[strlen(pszURL)] == '/') + { + eExists = EXIST_YES; + fileSize = 0; + bIsDirectory = TRUE; + } + + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "GetFileSize(%s)=" CPL_FRMT_GUIB " response_code=%d", + pszURL, fileSize, (int)response_code); + } + + CPLFree(sWriteFuncData.pBuffer); + CPLFree(sWriteFuncHeaderData.pBuffer); + + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + cachedFileProp->bHastComputedFileSize = TRUE; + cachedFileProp->fileSize = fileSize; + cachedFileProp->eExists = eExists; + cachedFileProp->bIsDirectory = bIsDirectory; + + return fileSize; +} + +/************************************************************************/ +/* Exists() */ +/************************************************************************/ + +int VSICurlHandle::Exists() +{ + if (eExists == EXIST_UNKNOWN) + GetFileSize(); + return eExists == EXIST_YES; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSICurlHandle::Tell() +{ + return curOffset; +} + +/************************************************************************/ +/* DownloadRegion() */ +/************************************************************************/ + +int VSICurlHandle::DownloadRegion(vsi_l_offset startOffset, int nBlocks) +{ + WriteFuncStruct sWriteFuncData; + WriteFuncStruct sWriteFuncHeaderData; + + if (bInterrupted && bStopOnInterrruptUntilUninstall) + return FALSE; + + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + if (cachedFileProp->eExists == EXIST_NO) + return FALSE; + + CURL* hCurlHandle = poFS->GetCurlHandleFor(pszURL); + VSICurlSetOptions(hCurlHandle, pszURL); + + VSICURLInitWriteFuncStruct(&sWriteFuncData, (VSILFILE*)this, pfnReadCbk, pReadCbkUserData); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, VSICurlHandleWriteFunc); + + VSICURLInitWriteFuncStruct(&sWriteFuncHeaderData, NULL, NULL, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, &sWriteFuncHeaderData); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION, VSICurlHandleWriteFunc); + sWriteFuncHeaderData.bIsHTTP = strncmp(pszURL, "http", 4) == 0; + sWriteFuncHeaderData.nStartOffset = startOffset; + sWriteFuncHeaderData.nEndOffset = startOffset + nBlocks * DOWNLOAD_CHUNCK_SIZE - 1; + /* Some servers don't like we try to read after end-of-file (#5786) */ + if( cachedFileProp->bHastComputedFileSize && + sWriteFuncHeaderData.nEndOffset >= cachedFileProp->fileSize ) + { + sWriteFuncHeaderData.nEndOffset = cachedFileProp->fileSize - 1; + } + + char rangeStr[512]; + sprintf(rangeStr, CPL_FRMT_GUIB "-" CPL_FRMT_GUIB, startOffset, + sWriteFuncHeaderData.nEndOffset); + + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Downloading %s (%s)...", rangeStr, pszURL); + + curl_easy_setopt(hCurlHandle, CURLOPT_RANGE, rangeStr); + + char szCurlErrBuf[CURL_ERROR_SIZE+1]; + szCurlErrBuf[0] = '\0'; + curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf ); + + curl_easy_perform(hCurlHandle); + + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION, NULL); + + if (sWriteFuncData.bInterrupted) + { + bInterrupted = TRUE; + + CPLFree(sWriteFuncData.pBuffer); + CPLFree(sWriteFuncHeaderData.pBuffer); + + return FALSE; + } + + long response_code = 0; + curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code); + + char *content_type = 0; + curl_easy_getinfo(hCurlHandle, CURLINFO_CONTENT_TYPE, &content_type); + + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Got reponse_code=%ld", response_code); + + if ((response_code != 200 && response_code != 206 && + response_code != 225 && response_code != 226 && response_code != 426) || sWriteFuncHeaderData.bError) + { + if (response_code >= 400 && szCurlErrBuf[0] != '\0') + { + if (strcmp(szCurlErrBuf, "Couldn't use REST") == 0) + CPLError(CE_Failure, CPLE_AppDefined, "%d: %s, %s", + (int)response_code, szCurlErrBuf, + "Range downloading not supported by this server !"); + else + CPLError(CE_Failure, CPLE_AppDefined, "%d: %s", (int)response_code, szCurlErrBuf); + } + if (!bHastComputedFileSize && startOffset == 0) + { + cachedFileProp->bHastComputedFileSize = bHastComputedFileSize = TRUE; + cachedFileProp->fileSize = fileSize = 0; + cachedFileProp->eExists = eExists = EXIST_NO; + } + CPLFree(sWriteFuncData.pBuffer); + CPLFree(sWriteFuncHeaderData.pBuffer); + return FALSE; + } + + if (!bHastComputedFileSize && sWriteFuncHeaderData.pBuffer) + { + /* Try to retrieve the filesize from the HTTP headers */ + /* if in the form : "Content-Range: bytes x-y/filesize" */ + char* pszContentRange = strstr(sWriteFuncHeaderData.pBuffer, "Content-Range: bytes "); + if (pszContentRange) + { + char* pszEOL = strchr(pszContentRange, '\n'); + if (pszEOL) + { + *pszEOL = 0; + pszEOL = strchr(pszContentRange, '\r'); + if (pszEOL) + *pszEOL = 0; + char* pszSlash = strchr(pszContentRange, '/'); + if (pszSlash) + { + pszSlash ++; + fileSize = CPLScanUIntBig(pszSlash, strlen(pszSlash)); + } + } + } + else if (strncmp(pszURL, "ftp", 3) == 0) + { + /* Parse 213 answer for FTP protocol */ + char* pszSize = strstr(sWriteFuncHeaderData.pBuffer, "213 "); + if (pszSize) + { + pszSize += 4; + char* pszEOL = strchr(pszSize, '\n'); + if (pszEOL) + { + *pszEOL = 0; + pszEOL = strchr(pszSize, '\r'); + if (pszEOL) + *pszEOL = 0; + + fileSize = CPLScanUIntBig(pszSize, strlen(pszSize)); + } + } + } + + if (fileSize != 0) + { + eExists = EXIST_YES; + + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "GetFileSize(%s)=" CPL_FRMT_GUIB " response_code=%d", + pszURL, fileSize, (int)response_code); + + bHastComputedFileSize = cachedFileProp->bHastComputedFileSize = TRUE; + cachedFileProp->fileSize = fileSize; + cachedFileProp->eExists = eExists; + } + } + + lastDownloadedOffset = startOffset + nBlocks * DOWNLOAD_CHUNCK_SIZE; + + char* pBuffer = sWriteFuncData.pBuffer; + int nSize = sWriteFuncData.nSize; + + if (nSize > nBlocks * DOWNLOAD_CHUNCK_SIZE) + { + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Got more data than expected : %d instead of %d", + nSize, nBlocks * DOWNLOAD_CHUNCK_SIZE); + } + + while(nSize > 0) + { + //if (ENABLE_DEBUG) + // CPLDebug("VSICURL", "Add region %d - %d", startOffset, MIN(DOWNLOAD_CHUNCK_SIZE, nSize)); + poFS->AddRegion(pszURL, startOffset, MIN(DOWNLOAD_CHUNCK_SIZE, nSize), pBuffer); + startOffset += DOWNLOAD_CHUNCK_SIZE; + pBuffer += DOWNLOAD_CHUNCK_SIZE; + nSize -= DOWNLOAD_CHUNCK_SIZE; + } + + CPLFree(sWriteFuncData.pBuffer); + CPLFree(sWriteFuncHeaderData.pBuffer); + + return TRUE; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSICurlHandle::Read( void *pBuffer, size_t nSize, size_t nMemb ) +{ + size_t nBufferRequestSize = nSize * nMemb; + if (nBufferRequestSize == 0) + return 0; + + //CPLDebug("VSICURL", "offset=%d, size=%d", (int)curOffset, (int)nBufferRequestSize); + + vsi_l_offset iterOffset = curOffset; + while (nBufferRequestSize) + { + const CachedRegion* psRegion = poFS->GetRegion(pszURL, iterOffset); + if (psRegion == NULL) + { + vsi_l_offset nOffsetToDownload = + (iterOffset / DOWNLOAD_CHUNCK_SIZE) * DOWNLOAD_CHUNCK_SIZE; + + if (nOffsetToDownload == lastDownloadedOffset) + { + /* In case of consecutive reads (of small size), we use a */ + /* heuristic that we will read the file sequentially, so */ + /* we double the requested size to decrease the number of */ + /* client/server roundtrips. */ + if (nBlocksToDownload < 100) + nBlocksToDownload *= 2; + } + else + { + /* Random reads. Cancel the above heuristics */ + nBlocksToDownload = 1; + } + + /* Ensure that we will request at least the number of blocks */ + /* to satisfy the remaining buffer size to read */ + vsi_l_offset nEndOffsetToDownload = + ((iterOffset + nBufferRequestSize) / DOWNLOAD_CHUNCK_SIZE) * DOWNLOAD_CHUNCK_SIZE; + int nMinBlocksToDownload = 1 + (int) + ((nEndOffsetToDownload - nOffsetToDownload) / DOWNLOAD_CHUNCK_SIZE); + if (nBlocksToDownload < nMinBlocksToDownload) + nBlocksToDownload = nMinBlocksToDownload; + + int i; + /* Avoid reading already cached data */ + for(i=1;iGetRegion(pszURL, nOffsetToDownload + i * DOWNLOAD_CHUNCK_SIZE) != NULL) + { + nBlocksToDownload = i; + break; + } + } + + if( nBlocksToDownload > N_MAX_REGIONS ) + nBlocksToDownload = N_MAX_REGIONS; + + if (DownloadRegion(nOffsetToDownload, nBlocksToDownload) == FALSE) + { + if (!bInterrupted) + bEOF = TRUE; + return 0; + } + psRegion = poFS->GetRegion(pszURL, iterOffset); + } + if (psRegion == NULL || psRegion->pData == NULL) + { + bEOF = TRUE; + return 0; + } + int nToCopy = (int) MIN(nBufferRequestSize, psRegion->nSize - (iterOffset - psRegion->nFileOffsetStart)); + memcpy(pBuffer, psRegion->pData + iterOffset - psRegion->nFileOffsetStart, + nToCopy); + pBuffer = (char*) pBuffer + nToCopy; + iterOffset += nToCopy; + nBufferRequestSize -= nToCopy; + if (psRegion->nSize != DOWNLOAD_CHUNCK_SIZE && nBufferRequestSize != 0) + { + break; + } + } + + size_t ret = (size_t) ((iterOffset - curOffset) / nSize); + if (ret != nMemb) + bEOF = TRUE; + + curOffset = iterOffset; + + return ret; +} + + +/************************************************************************/ +/* ReadMultiRange() */ +/************************************************************************/ + +int VSICurlHandle::ReadMultiRange( int nRanges, void ** ppData, + const vsi_l_offset* panOffsets, + const size_t* panSizes ) +{ + WriteFuncStruct sWriteFuncData; + WriteFuncStruct sWriteFuncHeaderData; + + if (bInterrupted && bStopOnInterrruptUntilUninstall) + return FALSE; + + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + if (cachedFileProp->eExists == EXIST_NO) + return -1; + + CPLString osRanges, osFirstRange, osLastRange; + int i; + int nMergedRanges = 0; + vsi_l_offset nTotalReqSize = 0; + for(i=0;i nMaxRanges) + { + int nHalf = nRanges / 2; + int nRet = ReadMultiRange(nHalf, ppData, panOffsets, panSizes); + if (nRet != 0) + return nRet; + return ReadMultiRange(nRanges - nHalf, ppData + nHalf, panOffsets + nHalf, panSizes + nHalf); + } + + CURL* hCurlHandle = poFS->GetCurlHandleFor(pszURL); + VSICurlSetOptions(hCurlHandle, pszURL); + + VSICURLInitWriteFuncStruct(&sWriteFuncData, (VSILFILE*)this, pfnReadCbk, pReadCbkUserData); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, VSICurlHandleWriteFunc); + + VSICURLInitWriteFuncStruct(&sWriteFuncHeaderData, NULL, NULL, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, &sWriteFuncHeaderData); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION, VSICurlHandleWriteFunc); + sWriteFuncHeaderData.bIsHTTP = strncmp(pszURL, "http", 4) == 0; + sWriteFuncHeaderData.bMultiRange = nMergedRanges > 1; + if (nMergedRanges == 1) + { + sWriteFuncHeaderData.nStartOffset = panOffsets[0]; + sWriteFuncHeaderData.nEndOffset = panOffsets[0] + nTotalReqSize-1; + } + + if (ENABLE_DEBUG) + { + if (nMergedRanges == 1) + CPLDebug("VSICURL", "Downloading %s (%s)...", osRanges.c_str(), pszURL); + else + CPLDebug("VSICURL", "Downloading %s, ..., %s (" CPL_FRMT_GUIB " bytes, %s)...", + osFirstRange.c_str(), osLastRange.c_str(), (GUIntBig)nTotalReqSize, pszURL); + } + + curl_easy_setopt(hCurlHandle, CURLOPT_RANGE, osRanges.c_str()); + + char szCurlErrBuf[CURL_ERROR_SIZE+1]; + szCurlErrBuf[0] = '\0'; + curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf ); + + curl_easy_perform(hCurlHandle); + + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION, NULL); + + if (sWriteFuncData.bInterrupted) + { + bInterrupted = TRUE; + + CPLFree(sWriteFuncData.pBuffer); + CPLFree(sWriteFuncHeaderData.pBuffer); + + return -1; + } + + long response_code = 0; + curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code); + + char *content_type = 0; + curl_easy_getinfo(hCurlHandle, CURLINFO_CONTENT_TYPE, &content_type); + + if ((response_code != 200 && response_code != 206 && + response_code != 225 && response_code != 226 && response_code != 426) || sWriteFuncHeaderData.bError) + { + if (response_code >= 400 && szCurlErrBuf[0] != '\0') + { + if (strcmp(szCurlErrBuf, "Couldn't use REST") == 0) + CPLError(CE_Failure, CPLE_AppDefined, "%d: %s, %s", + (int)response_code, szCurlErrBuf, + "Range downloading not supported by this server !"); + else + CPLError(CE_Failure, CPLE_AppDefined, "%d: %s", (int)response_code, szCurlErrBuf); + } + /* + if (!bHastComputedFileSize && startOffset == 0) + { + cachedFileProp->bHastComputedFileSize = bHastComputedFileSize = TRUE; + cachedFileProp->fileSize = fileSize = 0; + cachedFileProp->eExists = eExists = EXIST_NO; + } + */ + CPLFree(sWriteFuncData.pBuffer); + CPLFree(sWriteFuncHeaderData.pBuffer); + return -1; + } + + char* pBuffer = sWriteFuncData.pBuffer; + int nSize = sWriteFuncData.nSize; + + int nRet = -1; + char* pszBoundary; + CPLString osBoundary; + char *pszNext; + int iRange = 0; + int iPart = 0; + char* pszEOL; + +/* -------------------------------------------------------------------- */ +/* No multipart if a single range has been requested */ +/* -------------------------------------------------------------------- */ + + if (nMergedRanges == 1) + { + int nAccSize = 0; + if ((vsi_l_offset)nSize < nTotalReqSize) + goto end; + + for(i=0;i 1 && pszEOL[-1] == '\r') + { + bRestoreAntislashR = TRUE; + pszEOL[-1] = '\0'; + } + + if (EQUALN(pszNext, "Content-Range: bytes ", strlen("Content-Range: bytes "))) + { + bExpectedRange = TRUE; /* FIXME */ + } + + if (bRestoreAntislashR) + pszEOL[-1] = '\r'; + *pszEOL = '\n'; + + pszNext = pszEOL + 1; + } + + if (!bExpectedRange) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error while parsing multipart content (at line %d)", __LINE__); + goto end; + } + + if( *pszNext == '\r' ) + pszNext++; + if( *pszNext == '\n' ) + pszNext++; + +/* -------------------------------------------------------------------- */ +/* Work out the data block size. */ +/* -------------------------------------------------------------------- */ + size_t nBytesAvail = nSize - (pszNext - pBuffer); + + while(TRUE) + { + if (nBytesAvail < panSizes[iRange]) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error while parsing multipart content (at line %d)", __LINE__); + goto end; + } + + memcpy(ppData[iRange], pszNext, panSizes[iRange]); + pszNext += panSizes[iRange]; + nBytesAvail -= panSizes[iRange]; + if( iRange + 1 < nRanges && + panOffsets[iRange] + panSizes[iRange] == panOffsets[iRange + 1] ) + { + iRange++; + } + else + break; + } + + iPart ++; + iRange ++; + + while( nBytesAvail > 0 + && (*pszNext != '-' + || strncmp(pszNext,osBoundary,strlen(osBoundary)) != 0) ) + { + pszNext++; + nBytesAvail--; + } + + if( nBytesAvail == 0 ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error while parsing multipart content (at line %d)", __LINE__); + goto end; + } + + pszNext += strlen(osBoundary); + if( strncmp(pszNext,"--",2) == 0 ) + { + /* End of multipart */ + break; + } + + if( *pszNext == '\r' ) + pszNext++; + if( *pszNext == '\n' ) + pszNext++; + else + { + CPLError(CE_Failure, CPLE_AppDefined, + "Error while parsing multipart content (at line %d)", __LINE__); + goto end; + } + } + + if (iPart == nMergedRanges) + nRet = 0; + else + CPLError(CE_Failure, CPLE_AppDefined, + "Got only %d parts, where %d were expected", iPart, nMergedRanges); + +end: + CPLFree(sWriteFuncData.pBuffer); + CPLFree(sWriteFuncHeaderData.pBuffer); + + return nRet; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSICurlHandle::Write( CPL_UNUSED const void *pBuffer, + CPL_UNUSED size_t nSize, + CPL_UNUSED size_t nMemb ) +{ + return 0; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + + +int VSICurlHandle::Eof() +{ + return bEOF; +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSICurlHandle::Flush() +{ + return 0; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSICurlHandle::Close() +{ + return 0; +} + + + + +/************************************************************************/ +/* VSICurlFilesystemHandler() */ +/************************************************************************/ + +VSICurlFilesystemHandler::VSICurlFilesystemHandler() +{ + hMutex = NULL; + papsRegions = NULL; + nRegions = 0; + bUseCacheDisk = CSLTestBoolean(CPLGetConfigOption("CPL_VSIL_CURL_USE_CACHE", "NO")); +} + +/************************************************************************/ +/* ~VSICurlFilesystemHandler() */ +/************************************************************************/ + +VSICurlFilesystemHandler::~VSICurlFilesystemHandler() +{ + int i; + for(i=0;ipData); + CPLFree(papsRegions[i]); + } + CPLFree(papsRegions); + + std::map::const_iterator iterCacheFileSize; + + for( iterCacheFileSize = cacheFileSize.begin(); iterCacheFileSize != cacheFileSize.end(); iterCacheFileSize++ ) + { + CPLFree(iterCacheFileSize->second); + } + + std::map::const_iterator iterCacheDirList; + + for( iterCacheDirList = cacheDirList.begin(); iterCacheDirList != cacheDirList.end(); iterCacheDirList++ ) + { + CSLDestroy(iterCacheDirList->second->papszFileList); + CPLFree(iterCacheDirList->second); + } + + std::map::const_iterator iterConnections; + for( iterConnections = mapConnections.begin(); iterConnections != mapConnections.end(); iterConnections++ ) + { + curl_easy_cleanup(iterConnections->second->hCurlHandle); + delete iterConnections->second; + } + + if( hMutex != NULL ) + CPLDestroyMutex( hMutex ); + hMutex = NULL; +} + +/************************************************************************/ +/* GetCurlHandleFor() */ +/************************************************************************/ + +CURL* VSICurlFilesystemHandler::GetCurlHandleFor(CPLString osURL) +{ + CPLMutexHolder oHolder( &hMutex ); + + std::map::const_iterator iterConnections; + + iterConnections = mapConnections.find(CPLGetPID()); + if (iterConnections == mapConnections.end()) + { + CURL* hCurlHandle = curl_easy_init(); + CachedConnection* psCachedConnection = new CachedConnection; + psCachedConnection->osURL = osURL; + psCachedConnection->hCurlHandle = hCurlHandle; + mapConnections[CPLGetPID()] = psCachedConnection; + return hCurlHandle; + } + else + { + CachedConnection* psCachedConnection = iterConnections->second; + if (osURL == psCachedConnection->osURL) + return psCachedConnection->hCurlHandle; + + const char* pszURL = osURL.c_str(); + const char* pszEndOfServ = strchr(pszURL, '.'); + if (pszEndOfServ != NULL) + pszEndOfServ = strchr(pszEndOfServ, '/'); + if (pszEndOfServ == NULL) + pszURL = pszURL + strlen(pszURL); + int bReinitConnection = strncmp(psCachedConnection->osURL, + pszURL, pszEndOfServ-pszURL) != 0; + + if (bReinitConnection) + { + if (psCachedConnection->hCurlHandle) + curl_easy_cleanup(psCachedConnection->hCurlHandle); + psCachedConnection->hCurlHandle = curl_easy_init(); + } + psCachedConnection->osURL = osURL; + + return psCachedConnection->hCurlHandle; + } +} + + +/************************************************************************/ +/* GetRegionFromCacheDisk() */ +/************************************************************************/ + +const CachedRegion* +VSICurlFilesystemHandler::GetRegionFromCacheDisk(const char* pszURL, + vsi_l_offset nFileOffsetStart) +{ + nFileOffsetStart = (nFileOffsetStart / DOWNLOAD_CHUNCK_SIZE) * DOWNLOAD_CHUNCK_SIZE; + VSILFILE* fp = VSIFOpenL(VSICurlGetCacheFileName(), "rb"); + if (fp) + { + unsigned long pszURLHash = CPLHashSetHashStr(pszURL); + unsigned long pszURLHashCached; + vsi_l_offset nFileOffsetStartCached; + size_t nSizeCached; + while(TRUE) + { + if (VSIFReadL(&pszURLHashCached, 1, sizeof(unsigned long), fp) == 0) + break; + VSIFReadL(&nFileOffsetStartCached, 1, sizeof(vsi_l_offset), fp); + VSIFReadL(&nSizeCached, 1, sizeof(size_t), fp); + if (pszURLHash == pszURLHashCached && + nFileOffsetStart == nFileOffsetStartCached) + { + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Got data at offset " CPL_FRMT_GUIB " from disk" , nFileOffsetStart); + if (nSizeCached) + { + char* pBuffer = (char*) CPLMalloc(nSizeCached); + VSIFReadL(pBuffer, 1, nSizeCached, fp); + AddRegion(pszURL, nFileOffsetStart, nSizeCached, pBuffer); + CPLFree(pBuffer); + } + else + { + AddRegion(pszURL, nFileOffsetStart, 0, NULL); + } + VSIFCloseL(fp); + return GetRegion(pszURL, nFileOffsetStart); + } + else + { + VSIFSeekL(fp, nSizeCached, SEEK_CUR); + } + } + VSIFCloseL(fp); + } + return NULL; +} + + +/************************************************************************/ +/* AddRegionToCacheDisk() */ +/************************************************************************/ + +void VSICurlFilesystemHandler::AddRegionToCacheDisk(CachedRegion* psRegion) +{ + VSILFILE* fp = VSIFOpenL(VSICurlGetCacheFileName(), "r+b"); + if (fp) + { + unsigned long pszURLHashCached; + vsi_l_offset nFileOffsetStartCached; + size_t nSizeCached; + while(TRUE) + { + if (VSIFReadL(&pszURLHashCached, 1, sizeof(unsigned long), fp) == 0) + break; + VSIFReadL(&nFileOffsetStartCached, 1, sizeof(vsi_l_offset), fp); + VSIFReadL(&nSizeCached, 1, sizeof(size_t), fp); + if (psRegion->pszURLHash == pszURLHashCached && + psRegion->nFileOffsetStart == nFileOffsetStartCached) + { + CPLAssert(psRegion->nSize == nSizeCached); + VSIFCloseL(fp); + return; + } + else + { + VSIFSeekL(fp, nSizeCached, SEEK_CUR); + } + } + } + else + { + fp = VSIFOpenL(VSICurlGetCacheFileName(), "wb"); + } + if (fp) + { + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Write data at offset " CPL_FRMT_GUIB " to disk" , psRegion->nFileOffsetStart); + VSIFWriteL(&psRegion->pszURLHash, 1, sizeof(unsigned long), fp); + VSIFWriteL(&psRegion->nFileOffsetStart, 1, sizeof(vsi_l_offset), fp); + VSIFWriteL(&psRegion->nSize, 1, sizeof(size_t), fp); + if (psRegion->nSize) + VSIFWriteL(psRegion->pData, 1, psRegion->nSize, fp); + + VSIFCloseL(fp); + } + return; +} + + +/************************************************************************/ +/* GetRegion() */ +/************************************************************************/ + +const CachedRegion* VSICurlFilesystemHandler::GetRegion(const char* pszURL, + vsi_l_offset nFileOffsetStart) +{ + CPLMutexHolder oHolder( &hMutex ); + + unsigned long pszURLHash = CPLHashSetHashStr(pszURL); + + nFileOffsetStart = (nFileOffsetStart / DOWNLOAD_CHUNCK_SIZE) * DOWNLOAD_CHUNCK_SIZE; + int i; + for(i=0;ipszURLHash == pszURLHash && + nFileOffsetStart == psRegion->nFileOffsetStart) + { + memmove(papsRegions + 1, papsRegions, i * sizeof(CachedRegion*)); + papsRegions[0] = psRegion; + return psRegion; + } + } + if (bUseCacheDisk) + return GetRegionFromCacheDisk(pszURL, nFileOffsetStart); + return NULL; +} + +/************************************************************************/ +/* AddRegion() */ +/************************************************************************/ + +void VSICurlFilesystemHandler::AddRegion(const char* pszURL, + vsi_l_offset nFileOffsetStart, + size_t nSize, + const char *pData) +{ + CPLMutexHolder oHolder( &hMutex ); + + unsigned long pszURLHash = CPLHashSetHashStr(pszURL); + + CachedRegion* psRegion; + if (nRegions == N_MAX_REGIONS) + { + psRegion = papsRegions[N_MAX_REGIONS-1]; + memmove(papsRegions + 1, papsRegions, (N_MAX_REGIONS-1) * sizeof(CachedRegion*)); + papsRegions[0] = psRegion; + CPLFree(psRegion->pData); + } + else + { + papsRegions = (CachedRegion**) CPLRealloc(papsRegions, (nRegions + 1) * sizeof(CachedRegion*)); + if (nRegions) + memmove(papsRegions + 1, papsRegions, nRegions * sizeof(CachedRegion*)); + nRegions ++; + papsRegions[0] = psRegion = (CachedRegion*) CPLMalloc(sizeof(CachedRegion)); + } + + psRegion->pszURLHash = pszURLHash; + psRegion->nFileOffsetStart = nFileOffsetStart; + psRegion->nSize = nSize; + psRegion->pData = (nSize) ? (char*) CPLMalloc(nSize) : NULL; + if (nSize) + memcpy(psRegion->pData, pData, nSize); + + if (bUseCacheDisk) + AddRegionToCacheDisk(psRegion); +} + +/************************************************************************/ +/* GetCachedFileProp() */ +/************************************************************************/ + +CachedFileProp* VSICurlFilesystemHandler::GetCachedFileProp(const char* pszURL) +{ + CPLMutexHolder oHolder( &hMutex ); + + CachedFileProp* cachedFileProp = cacheFileSize[pszURL]; + if (cachedFileProp == NULL) + { + cachedFileProp = (CachedFileProp*) CPLMalloc(sizeof(CachedFileProp)); + cachedFileProp->eExists = EXIST_UNKNOWN; + cachedFileProp->bHastComputedFileSize = FALSE; + cachedFileProp->fileSize = 0; + cachedFileProp->bIsDirectory = FALSE; + cacheFileSize[pszURL] = cachedFileProp; + } + + return cachedFileProp; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle* VSICurlFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess) +{ + if (strchr(pszAccess, 'w') != NULL || + strchr(pszAccess, '+') != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Only read-only mode is supported for /vsicurl"); + return NULL; + } + + const char* pszOptionVal = + CPLGetConfigOption( "GDAL_DISABLE_READDIR_ON_OPEN", "NO" ); + int bSkipReadDir = EQUAL(pszOptionVal, "EMPTY_DIR") || + CSLTestBoolean(pszOptionVal); + + CPLString osFilename(pszFilename); + int bGotFileList = TRUE; + if (strchr(CPLGetFilename(osFilename), '.') != NULL && + strncmp(CPLGetExtension(osFilename), "zip", 3) != 0 && !bSkipReadDir) + { + char** papszFileList = ReadDir(CPLGetDirname(osFilename), &bGotFileList); + int bFound = (VSICurlIsFileInList(papszFileList, CPLGetFilename(osFilename)) != -1); + CSLDestroy(papszFileList); + if (bGotFileList && !bFound) + { + return NULL; + } + } + + VSICurlHandle* poHandle = new VSICurlHandle( this, osFilename + strlen("/vsicurl/")); + if (!bGotFileList) + { + /* If we didn't get a filelist, check that the file really exists */ + if (!poHandle->Exists()) + { + delete poHandle; + poHandle = NULL; + } + } + + if( CSLTestBoolean( CPLGetConfigOption( "VSI_CACHE", "FALSE" ) ) ) + return VSICreateCachedFile( poHandle ); + else + return poHandle; +} + +/************************************************************************/ +/* VSICurlParserFindEOL() */ +/* */ +/* Small helper function for VSICurlPaseHTMLFileList() to find */ +/* the end of a line in the directory listing. Either a
    */ +/* or newline. */ +/************************************************************************/ + +static char *VSICurlParserFindEOL( char *pszData ) + +{ + while( *pszData != '\0' && *pszData != '\n' && !EQUALN(pszData,"
    ",4) ) + pszData++; + + if( *pszData == '\0' ) + return NULL; + else + return pszData; +} + + +/************************************************************************/ +/* VSICurlParseHTMLDateTimeFileSize() */ +/************************************************************************/ + +static const char* const apszMonths[] = { "January", "February", "March", + "April", "May", "June", "July", + "August", "September", "October", + "November", "December" }; + +static int VSICurlParseHTMLDateTimeFileSize(const char* pszStr, + struct tm& brokendowntime, + GUIntBig& nFileSize, + GIntBig& mTime) +{ + int iMonth; + for(iMonth=0;iMonth<12;iMonth++) + { + char szMonth[32]; + szMonth[0] = '-'; + memcpy(szMonth + 1, apszMonths[iMonth], 3); + szMonth[4] = '-'; + szMonth[5] = '\0'; + const char* pszMonthFound = strstr(pszStr, szMonth); + if (pszMonthFound) + { + /* Format of Apache, like in http://download.osgeo.org/gdal/data/gtiff/ */ + /* "17-May-2010 12:26" */ + if (pszMonthFound - pszStr > 2 && strlen(pszMonthFound) > 15 && + pszMonthFound[-2 + 11] == ' ' && pszMonthFound[-2 + 14] == ':') + { + pszMonthFound -= 2; + int nDay = atoi(pszMonthFound); + int nYear = atoi(pszMonthFound + 7); + int nHour = atoi(pszMonthFound + 12); + int nMin = atoi(pszMonthFound + 15); + if (nDay >= 1 && nDay <= 31 && nYear >= 1900 && + nHour >= 0 && nHour <= 24 && nMin >= 0 && nMin < 60) + { + brokendowntime.tm_year = nYear - 1900; + brokendowntime.tm_mon = iMonth; + brokendowntime.tm_mday = nDay; + brokendowntime.tm_hour = nHour; + brokendowntime.tm_min = nMin; + mTime = CPLYMDHMSToUnixTime(&brokendowntime); + + return TRUE; + } + } + return FALSE; + } + + /* Microsoft IIS */ + szMonth[0] = ' '; + strcpy(szMonth + 1, apszMonths[iMonth]); + strcat(szMonth, " "); + pszMonthFound = strstr(pszStr, szMonth); + if (pszMonthFound) + { + int nLenMonth = strlen(apszMonths[iMonth]); + if (pszMonthFound - pszStr > 2 && + pszMonthFound[-1] != ',' && + pszMonthFound[-2] != ' ' && + (int)strlen(pszMonthFound-2) > 2 + 1 + nLenMonth + 1 + 4 + 1 + 5 + 1 + 4) + { + /* Format of http://ortho.linz.govt.nz/tifs/1994_95/ */ + /* " Friday, 21 April 2006 12:05 p.m. 48062343 m35a_fy_94_95.tif" */ + pszMonthFound -= 2; + int nDay = atoi(pszMonthFound); + int nCurOffset = 2 + 1 + nLenMonth + 1; + int nYear = atoi(pszMonthFound + nCurOffset); + nCurOffset += 4 + 1; + int nHour = atoi(pszMonthFound + nCurOffset); + if (nHour < 10) + nCurOffset += 1 + 1; + else + nCurOffset += 2 + 1; + int nMin = atoi(pszMonthFound + nCurOffset); + nCurOffset += 2 + 1; + if (strncmp(pszMonthFound + nCurOffset, "p.m.", 4) == 0) + nHour += 12; + else if (strncmp(pszMonthFound + nCurOffset, "a.m.", 4) != 0) + nHour = -1; + nCurOffset += 4; + + const char* pszFilesize = pszMonthFound + nCurOffset; + while(*pszFilesize == ' ') + pszFilesize ++; + if (*pszFilesize >= '1' && *pszFilesize <= '9') + nFileSize = CPLScanUIntBig(pszFilesize, strlen(pszFilesize)); + + if (nDay >= 1 && nDay <= 31 && nYear >= 1900 && + nHour >= 0 && nHour <= 24 && nMin >= 0 && nMin < 60) + { + brokendowntime.tm_year = nYear - 1900; + brokendowntime.tm_mon = iMonth; + brokendowntime.tm_mday = nDay; + brokendowntime.tm_hour = nHour; + brokendowntime.tm_min = nMin; + mTime = CPLYMDHMSToUnixTime(&brokendowntime); + + return TRUE; + } + nFileSize = 0; + } + else if (pszMonthFound - pszStr > 1 && + pszMonthFound[-1] == ',' && + (int)strlen(pszMonthFound) > 1 + nLenMonth + 1 + 2 + 1 + 1 + 4 + 1 + 5 + 1 + 2) + { + /* Format of http://publicfiles.dep.state.fl.us/dear/BWR_GIS/2007NWFLULC/ */ + /* " Sunday, June 20, 2010 6:46 PM 233170905 NWF2007LULCForSDE.zip" */ + pszMonthFound += 1; + int nCurOffset = nLenMonth + 1; + int nDay = atoi(pszMonthFound + nCurOffset); + nCurOffset += 2 + 1 + 1; + int nYear = atoi(pszMonthFound + nCurOffset); + nCurOffset += 4 + 1; + int nHour = atoi(pszMonthFound + nCurOffset); + nCurOffset += 2 + 1; + int nMin = atoi(pszMonthFound + nCurOffset); + nCurOffset += 2 + 1; + if (strncmp(pszMonthFound + nCurOffset, "PM", 2) == 0) + nHour += 12; + else if (strncmp(pszMonthFound + nCurOffset, "AM", 2) != 0) + nHour = -1; + nCurOffset += 2; + + const char* pszFilesize = pszMonthFound + nCurOffset; + while(*pszFilesize == ' ') + pszFilesize ++; + if (*pszFilesize >= '1' && *pszFilesize <= '9') + nFileSize = CPLScanUIntBig(pszFilesize, strlen(pszFilesize)); + + if (nDay >= 1 && nDay <= 31 && nYear >= 1900 && + nHour >= 0 && nHour <= 24 && nMin >= 0 && nMin < 60) + { + brokendowntime.tm_year = nYear - 1900; + brokendowntime.tm_mon = iMonth; + brokendowntime.tm_mday = nDay; + brokendowntime.tm_hour = nHour; + brokendowntime.tm_min = nMin; + mTime = CPLYMDHMSToUnixTime(&brokendowntime); + + return TRUE; + } + nFileSize = 0; + } + return FALSE; + } + } + + return FALSE; +} + +/************************************************************************/ +/* ParseHTMLFileList() */ +/* */ +/* Parse a file list document and return all the components. */ +/************************************************************************/ + +char** VSICurlFilesystemHandler::ParseHTMLFileList(const char* pszFilename, + char* pszData, + int* pbGotFileList) +{ + CPLStringList oFileList; + char* pszLine = pszData; + char* c; + int nCount = 0; + int bIsHTMLDirList = FALSE; + CPLString osExpectedString; + CPLString osExpectedString2; + CPLString osExpectedString3; + CPLString osExpectedString4; + CPLString osExpectedString_unescaped; + + *pbGotFileList = FALSE; + + const char* pszDir; + if (EQUALN(pszFilename, "/vsicurl/http://", strlen("/vsicurl/http://"))) + pszDir = strchr(pszFilename + strlen("/vsicurl/http://"), '/'); + else if (EQUALN(pszFilename, "/vsicurl/https://", strlen("/vsicurl/https://"))) + pszDir = strchr(pszFilename + strlen("/vsicurl/https://"), '/'); + else + pszDir = strchr(pszFilename + strlen("/vsicurl/ftp://"), '/'); + if (pszDir == NULL) + pszDir = ""; + /* Apache */ + osExpectedString = "Index of "; + osExpectedString += pszDir; + osExpectedString += ""; + /* shttpd */ + osExpectedString2 = "Index of "; + osExpectedString2 += pszDir; + osExpectedString2 += "/"; + /* FTP */ + osExpectedString3 = "FTP Listing of "; + osExpectedString3 += pszDir; + osExpectedString3 += "/"; + /* Apache 1.3.33 */ + osExpectedString4 = "Index of "; + osExpectedString4 += pszDir; + osExpectedString4 += ""; + + /* The listing of http://dds.cr.usgs.gov/srtm/SRTM_image_sample/picture%20examples/ */ + /* has "Index of /srtm/SRTM_image_sample/picture examples" so we must */ + /* try unescaped %20 also */ + /* Similar with http://datalib.usask.ca/gis/Data/Central_America_goodbutdoweown%3f/ */ + if (strchr(pszDir, '%')) + { + char* pszUnescapedDir = CPLUnescapeString(pszDir, NULL, CPLES_URL); + osExpectedString_unescaped = "Index of "; + osExpectedString_unescaped += pszUnescapedDir; + osExpectedString_unescaped += ""; + CPLFree(pszUnescapedDir); + } + + int nCountTable = 0; + + while( (c = VSICurlParserFindEOL( pszLine )) != NULL ) + { + *c = 0; + + /* To avoid false positive on pages such as http://www.ngs.noaa.gov/PC_PROD/USGG2009BETA */ + /* This is a heuristics, but normal HTML listing of files have not more than one table */ + if (strstr(pszLine, "")) + { + /* Detect something like : gdal - Revision 20739: /trunk/autotest/gcore/data */ + /* The annoying thing is that what is after ': ' is a subpart of what is after http://server/ */ + char* pszSubDir = strstr(pszLine, ": "); + if (pszSubDir == NULL) + /* or ortho.linz.govt.nz - /tifs/2005_06/ */ + pszSubDir = strstr(pszLine, "- "); + if (pszSubDir) + { + pszSubDir += 2; + char* pszTmp = strstr(pszSubDir, ""); + if (pszTmp) + { + if (pszTmp[-1] == '/') + pszTmp[-1] = 0; + else + *pszTmp = 0; + if (strstr(pszDir, pszSubDir)) + { + bIsHTMLDirList = TRUE; + *pbGotFileList = TRUE; + } + } + } + } + else if (bIsHTMLDirList && + (strstr(pszLine, "eExists = EXIST_YES; + cachedFileProp->bIsDirectory = bIsDirectory; + cachedFileProp->mTime = mTime; + cachedFileProp->bHastComputedFileSize = nFileSize > 0; + cachedFileProp->fileSize = nFileSize; + + oFileList.AddString( beginFilename ); + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "File[%d] = %s, is_dir = %d, size = " CPL_FRMT_GUIB ", time = %04d/%02d/%02d %02d:%02d:%02d", + nCount, beginFilename, bIsDirectory, nFileSize, + brokendowntime.tm_year + 1900, brokendowntime.tm_mon + 1, brokendowntime.tm_mday, + brokendowntime.tm_hour, brokendowntime.tm_min, brokendowntime.tm_sec); + nCount ++; + } + } + } + pszLine = c + 1; + } + + return oFileList.StealList(); +} + + +/************************************************************************/ +/* VSICurlGetToken() */ +/************************************************************************/ + +static char* VSICurlGetToken(char* pszCurPtr, char** ppszNextToken) +{ + if (pszCurPtr == NULL) + return NULL; + + while((*pszCurPtr) == ' ') + pszCurPtr ++; + if (*pszCurPtr == '\0') + return NULL; + + char* pszToken = pszCurPtr; + while((*pszCurPtr) != ' ' && (*pszCurPtr) != '\0') + pszCurPtr ++; + if (*pszCurPtr == '\0') + *ppszNextToken = NULL; + else + { + *pszCurPtr = '\0'; + pszCurPtr ++; + while((*pszCurPtr) == ' ') + pszCurPtr ++; + *ppszNextToken = pszCurPtr; + } + + return pszToken; +} + +/************************************************************************/ +/* VSICurlParseFullFTPLine() */ +/************************************************************************/ + +/* Parse lines like the following ones : +-rw-r--r-- 1 10003 100 430 Jul 04 2008 COPYING +lrwxrwxrwx 1 ftp ftp 28 Jun 14 14:13 MPlayer -> mirrors/mplayerhq.hu/MPlayer +-rw-r--r-- 1 ftp ftp 725614592 May 13 20:13 Fedora-15-x86_64-Live-KDE.iso +drwxr-xr-x 280 1003 1003 6656 Aug 26 04:17 gnu +*/ + +static int VSICurlParseFullFTPLine(char* pszLine, + char*& pszFilename, + int& bSizeValid, + GUIntBig& nSize, + int& bIsDirectory, + GIntBig& nUnixTime) +{ + char* pszNextToken = pszLine; + char* pszPermissions = VSICurlGetToken(pszNextToken, &pszNextToken); + if (pszPermissions == NULL || strlen(pszPermissions) != 10) + return FALSE; + bIsDirectory = (pszPermissions[0] == 'd'); + + int i; + for(i = 0; i < 3; i++) + { + if (VSICurlGetToken(pszNextToken, &pszNextToken) == NULL) + return FALSE; + } + + char* pszSize = VSICurlGetToken(pszNextToken, &pszNextToken); + if (pszSize == NULL) + return FALSE; + + if (pszPermissions[0] == '-') + { + /* Regular file */ + bSizeValid = TRUE; + nSize = CPLScanUIntBig(pszSize, strlen(pszSize)); + } + + struct tm brokendowntime; + memset(&brokendowntime, 0, sizeof(brokendowntime)); + int bBrokenDownTimeValid = TRUE; + + char* pszMonth = VSICurlGetToken(pszNextToken, &pszNextToken); + if (pszMonth == NULL || strlen(pszMonth) != 3) + return FALSE; + + for(i = 0; i < 12; i++) + { + if (EQUALN(pszMonth, apszMonths[i], 3)) + break; + } + if (i < 12) + brokendowntime.tm_mon = i; + else + bBrokenDownTimeValid = FALSE; + + char* pszDay = VSICurlGetToken(pszNextToken, &pszNextToken); + if (pszDay == NULL || (strlen(pszDay) != 1 && strlen(pszDay) != 2)) + return FALSE; + int nDay = atoi(pszDay); + if (nDay >= 1 && nDay <= 31) + brokendowntime.tm_mday = nDay; + else + bBrokenDownTimeValid = FALSE; + + char* pszHourOrYear = VSICurlGetToken(pszNextToken, &pszNextToken); + if (pszHourOrYear == NULL || (strlen(pszHourOrYear) != 4 && strlen(pszHourOrYear) != 5)) + return FALSE; + if (strlen(pszHourOrYear) == 4) + { + brokendowntime.tm_year = atoi(pszHourOrYear) - 1900; + } + else + { + time_t sTime; + time(&sTime); + struct tm currentBrokendowntime; + CPLUnixTimeToYMDHMS((GIntBig)sTime, ¤tBrokendowntime); + brokendowntime.tm_year = currentBrokendowntime.tm_year; + brokendowntime.tm_hour = atoi(pszHourOrYear); + brokendowntime.tm_min = atoi(pszHourOrYear + 3); + } + + if (bBrokenDownTimeValid) + nUnixTime = CPLYMDHMSToUnixTime(&brokendowntime); + else + nUnixTime = 0; + + if (pszNextToken == NULL) + return FALSE; + + pszFilename = pszNextToken; + + char* pszCurPtr = pszFilename; + while( *pszCurPtr != '\0') + { + /* In case of a link, stop before the pointed part of the link */ + if (pszPermissions[0] == 'l' && strncmp(pszCurPtr, " -> ", 4) == 0) + { + break; + } + pszCurPtr ++; + } + *pszCurPtr = '\0'; + + return TRUE; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char** VSICurlFilesystemHandler::GetFileList(const char *pszDirname, int* pbGotFileList) +{ + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "GetFileList(%s)" , pszDirname); + + *pbGotFileList = FALSE; + + /* HACK (optimization in fact) for MBTiles driver */ + if (strstr(pszDirname, ".tiles.mapbox.com") != NULL) + return NULL; + + if (strncmp(pszDirname, "/vsicurl/ftp", strlen("/vsicurl/ftp")) == 0) + { + WriteFuncStruct sWriteFuncData; + sWriteFuncData.pBuffer = NULL; + + CPLString osDirname(pszDirname + strlen("/vsicurl/")); + osDirname += '/'; + + char** papszFileList = NULL; + + for(int iTry=0;iTry<2;iTry++) + { + CURL* hCurlHandle = GetCurlHandleFor(osDirname); + VSICurlSetOptions(hCurlHandle, osDirname.c_str()); + + /* On the first pass, we want to try fetching all the possible */ + /* informations (filename, file/directory, size). If that */ + /* does not work, then try again with CURLOPT_DIRLISTONLY set */ + if (iTry == 1) + { + /* 7.16.4 */ + #if LIBCURL_VERSION_NUM <= 0x071004 + curl_easy_setopt(hCurlHandle, CURLOPT_FTPLISTONLY, 1); + #elif LIBCURL_VERSION_NUM > 0x071004 + curl_easy_setopt(hCurlHandle, CURLOPT_DIRLISTONLY, 1); + #endif + } + + VSICURLInitWriteFuncStruct(&sWriteFuncData, NULL, NULL, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, VSICurlHandleWriteFunc); + + char szCurlErrBuf[CURL_ERROR_SIZE+1]; + szCurlErrBuf[0] = '\0'; + curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf ); + + curl_easy_perform(hCurlHandle); + + if (sWriteFuncData.pBuffer == NULL) + return NULL; + + char* pszLine = sWriteFuncData.pBuffer; + char* c; + int nCount = 0; + + if (EQUALN(pszLine, "", 6)) + { + papszFileList = ParseHTMLFileList(pszDirname, + sWriteFuncData.pBuffer, + pbGotFileList); + break; + } + else if (iTry == 0) + { + CPLStringList oFileList; + *pbGotFileList = TRUE; + + while( (c = strchr(pszLine, '\n')) != NULL) + { + *c = 0; + if (c - pszLine > 0 && c[-1] == '\r') + c[-1] = 0; + + char* pszFilename = NULL; + int bSizeValid = FALSE; + GUIntBig nFileSize = 0; + int bIsDirectory = FALSE; + GIntBig mUnixTime = 0; + if (!VSICurlParseFullFTPLine(pszLine, pszFilename, + bSizeValid, nFileSize, + bIsDirectory, mUnixTime)) + break; + + if (strcmp(pszFilename, ".") != 0 && + strcmp(pszFilename, "..") != 0) + { + CPLString osCachedFilename = + CPLSPrintf("%s/%s", pszDirname + strlen("/vsicurl/"), pszFilename); + CachedFileProp* cachedFileProp = GetCachedFileProp(osCachedFilename); + cachedFileProp->eExists = EXIST_YES; + cachedFileProp->bHastComputedFileSize = bSizeValid; + cachedFileProp->fileSize = nFileSize; + cachedFileProp->bIsDirectory = bIsDirectory; + cachedFileProp->mTime = mUnixTime; + + oFileList.AddString(pszFilename); + if (ENABLE_DEBUG) + { + struct tm brokendowntime; + CPLUnixTimeToYMDHMS(mUnixTime, &brokendowntime); + CPLDebug("VSICURL", "File[%d] = %s, is_dir = %d, size = " CPL_FRMT_GUIB ", time = %04d/%02d/%02d %02d:%02d:%02d", + nCount, pszFilename, bIsDirectory, nFileSize, + brokendowntime.tm_year + 1900, brokendowntime.tm_mon + 1, brokendowntime.tm_mday, + brokendowntime.tm_hour, brokendowntime.tm_min, brokendowntime.tm_sec); + } + + nCount ++; + } + + pszLine = c + 1; + } + + if (c == NULL) + { + papszFileList = oFileList.StealList(); + break; + } + } + else + { + CPLStringList oFileList; + *pbGotFileList = TRUE; + + while( (c = strchr(pszLine, '\n')) != NULL) + { + *c = 0; + if (c - pszLine > 0 && c[-1] == '\r') + c[-1] = 0; + + if (strcmp(pszLine, ".") != 0 && + strcmp(pszLine, "..") != 0) + { + oFileList.AddString(pszLine); + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "File[%d] = %s", nCount, pszLine); + nCount ++; + } + + pszLine = c + 1; + } + + papszFileList = oFileList.StealList(); + } + + CPLFree(sWriteFuncData.pBuffer); + sWriteFuncData.pBuffer = NULL; + } + + CPLFree(sWriteFuncData.pBuffer); + + return papszFileList; + } + + /* Try to recognize HTML pages that list the content of a directory */ + /* Currently this supports what Apache and shttpd can return */ + else if (strncmp(pszDirname, "/vsicurl/http://", strlen("/vsicurl/http://")) == 0 || + strncmp(pszDirname, "/vsicurl/https://", strlen("/vsicurl/https://")) == 0) + { + WriteFuncStruct sWriteFuncData; + + CPLString osDirname(pszDirname + strlen("/vsicurl/")); + osDirname += '/'; + + #if LIBCURL_VERSION_NUM < 0x070B00 + /* Curl 7.10.X doesn't manage to unset the CURLOPT_RANGE that would have been */ + /* previously set, so we have to reinit the connection handle */ + GetCurlHandleFor(""); + #endif + + CURL* hCurlHandle = GetCurlHandleFor(osDirname); + VSICurlSetOptions(hCurlHandle, osDirname.c_str()); + + curl_easy_setopt(hCurlHandle, CURLOPT_RANGE, NULL); + + VSICURLInitWriteFuncStruct(&sWriteFuncData, NULL, NULL, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, &sWriteFuncData); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, VSICurlHandleWriteFunc); + + char szCurlErrBuf[CURL_ERROR_SIZE+1]; + szCurlErrBuf[0] = '\0'; + curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf ); + + curl_easy_perform(hCurlHandle); + + if (sWriteFuncData.pBuffer == NULL) + return NULL; + + char** papszFileList = ParseHTMLFileList(pszDirname, + sWriteFuncData.pBuffer, + pbGotFileList); + + CPLFree(sWriteFuncData.pBuffer); + return papszFileList; + } + + return NULL; +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSICurlFilesystemHandler::Stat( const char *pszFilename, VSIStatBufL *pStatBuf, + int nFlags ) +{ + CPLString osFilename(pszFilename); + + memset(pStatBuf, 0, sizeof(VSIStatBufL)); + + const char* pszOptionVal = + CPLGetConfigOption( "GDAL_DISABLE_READDIR_ON_OPEN", "NO" ); + int bSkipReadDir = EQUAL(pszOptionVal, "EMPTY_DIR") || + CSLTestBoolean(pszOptionVal); + + /* Does it look like a FTP directory ? */ + if (strncmp(osFilename, "/vsicurl/ftp", strlen("/vsicurl/ftp")) == 0 && + pszFilename[strlen(osFilename) - 1] == '/' && !bSkipReadDir) + { + char** papszFileList = ReadDir(osFilename); + if (papszFileList) + { + pStatBuf->st_mode = S_IFDIR; + pStatBuf->st_size = 0; + + CSLDestroy(papszFileList); + + return 0; + } + return -1; + } + else if (strchr(CPLGetFilename(osFilename), '.') != NULL && + !EQUALN(CPLGetExtension(osFilename), "zip", 3) && + strstr(osFilename, ".zip.") != NULL && + strstr(osFilename, ".ZIP.") != NULL && + !bSkipReadDir) + { + int bGotFileList; + char** papszFileList = ReadDir(CPLGetDirname(osFilename), &bGotFileList); + int bFound = (VSICurlIsFileInList(papszFileList, CPLGetFilename(osFilename)) != -1); + CSLDestroy(papszFileList); + if (bGotFileList && !bFound) + { + return -1; + } + } + + VSICurlHandle oHandle( this, osFilename + strlen("/vsicurl/")); + + if ( oHandle.IsKnownFileSize() || + ((nFlags & VSI_STAT_SIZE_FLAG) && !oHandle.IsDirectory() && + CSLTestBoolean(CPLGetConfigOption("CPL_VSIL_CURL_SLOW_GET_SIZE", "YES"))) ) + pStatBuf->st_size = oHandle.GetFileSize(); + + int nRet = (oHandle.Exists()) ? 0 : -1; + pStatBuf->st_mtime = oHandle.GetMTime(); + pStatBuf->st_mode = oHandle.IsDirectory() ? S_IFDIR : S_IFREG; + return nRet; +} + +/************************************************************************/ +/* Unlink() */ +/************************************************************************/ + +int VSICurlFilesystemHandler::Unlink( CPL_UNUSED const char *pszFilename ) +{ + return -1; +} + +/************************************************************************/ +/* Rename() */ +/************************************************************************/ + +int VSICurlFilesystemHandler::Rename( CPL_UNUSED const char *oldpath, + CPL_UNUSED const char *newpath ) +{ + return -1; +} + +/************************************************************************/ +/* Mkdir() */ +/************************************************************************/ + +int VSICurlFilesystemHandler::Mkdir( CPL_UNUSED const char *pszDirname, + CPL_UNUSED long nMode ) +{ + return -1; +} +/************************************************************************/ +/* Rmdir() */ +/************************************************************************/ + +int VSICurlFilesystemHandler::Rmdir( CPL_UNUSED const char *pszDirname ) +{ + return -1; +} + +/************************************************************************/ +/* ReadDir() */ +/************************************************************************/ + +char** VSICurlFilesystemHandler::ReadDir( const char *pszDirname, int* pbGotFileList ) +{ + CPLString osDirname(pszDirname); + while (osDirname[strlen(osDirname) - 1] == '/') + osDirname.erase(strlen(osDirname) - 1); + + const char* pszUpDir = strstr(osDirname, "/.."); + if (pszUpDir != NULL) + { + int pos = pszUpDir - osDirname.c_str() - 1; + while(pos >= 0 && osDirname[pos] != '/') + pos --; + if (pos >= 1) + { + osDirname = osDirname.substr(0, pos) + CPLString(pszUpDir + 3); + } + } + + CPLMutexHolder oHolder( &hMutex ); + + /* If we know the file exists and is not a directory, then don't try to list its content */ + CachedFileProp* cachedFileProp = GetCachedFileProp(osDirname.c_str() + strlen("/vsicurl/")); + if (cachedFileProp->eExists == EXIST_YES && !cachedFileProp->bIsDirectory) + { + if (pbGotFileList) + *pbGotFileList = TRUE; + return NULL; + } + + CachedDirList* psCachedDirList = cacheDirList[osDirname]; + if (psCachedDirList == NULL) + { + psCachedDirList = (CachedDirList*) CPLMalloc(sizeof(CachedDirList)); + psCachedDirList->papszFileList = GetFileList(osDirname, &psCachedDirList->bGotFileList); + cacheDirList[osDirname] = psCachedDirList; + } + + if (pbGotFileList) + *pbGotFileList = psCachedDirList->bGotFileList; + + return CSLDuplicate(psCachedDirList->papszFileList); +} + +/************************************************************************/ +/* ReadDir() */ +/************************************************************************/ + +char** VSICurlFilesystemHandler::ReadDir( const char *pszDirname ) +{ + return ReadDir(pszDirname, NULL); +} + +/************************************************************************/ +/* VSIInstallCurlFileHandler() */ +/************************************************************************/ + +/** + * \brief Install /vsicurl/ HTTP/FTP file system handler (requires libcurl) + * + * A special file handler is installed that allows reading on-the-fly of files + * available through HTTP/FTP web protocols, without downloading the entire file. + * + * Recognized filenames are of the form /vsicurl/http://path/to/remote/resource or + * /vsicurl/ftp://path/to/remote/resource where path/to/remote/resource is the + * URL of a remote resource. + * + * Partial downloads (requires the HTTP server to support random reading) are done + * with a 16 KB granularity by default. If the driver detects sequential reading + * it will progressively increase the chunk size up to 2 MB to improve download + * performance. + * + * The GDAL_HTTP_PROXY, GDAL_HTTP_PROXYUSERPWD and GDAL_PROXY_AUTH configuration options can be + * used to define a proxy server. The syntax to use is the one of Curl CURLOPT_PROXY, + * CURLOPT_PROXYUSERPWD and CURLOPT_PROXYAUTH options. + * + * Starting with GDAL 1.10, the file can be cached in RAM by setting the configuration option + * VSI_CACHE to TRUE. The cache size defaults to 25 MB, but can be modified by setting + * the configuration option VSI_CACHE_SIZE (in bytes). + * + * VSIStatL() will return the size in st_size member and file + * nature- file or directory - in st_mode member (the later only reliable with FTP + * resources for now). + * + * VSIReadDir() should be able to parse the HTML directory listing returned by the + * most popular web servers, such as Apache or Microsoft IIS. + * + * This special file handler can be combined with other virtual filesystems handlers, + * such as /vsizip. For example, /vsizip//vsicurl/path/to/remote/file.zip/path/inside/zip + * + * @since GDAL 1.8.0 + */ +void VSIInstallCurlFileHandler(void) +{ + VSIFileManager::InstallHandler( "/vsicurl/", new VSICurlFilesystemHandler ); +} + +/************************************************************************/ +/* VSICurlInstallReadCbk() */ +/************************************************************************/ + +int VSICurlInstallReadCbk (VSILFILE* fp, + VSICurlReadCbkFunc pfnReadCbk, + void* pfnUserData, + int bStopOnInterrruptUntilUninstall) +{ + return ((VSICurlHandle*)fp)->InstallReadCbk(pfnReadCbk, pfnUserData, + bStopOnInterrruptUntilUninstall); +} + + +/************************************************************************/ +/* VSICurlUninstallReadCbk() */ +/************************************************************************/ + +int VSICurlUninstallReadCbk(VSILFILE* fp) +{ + return ((VSICurlHandle*)fp)->UninstallReadCbk(); +} + +#endif /* HAVE_CURL */ diff --git a/bazaar/plugin/gdal/port/cpl_vsil_curl_priv.h b/bazaar/plugin/gdal/port/cpl_vsil_curl_priv.h new file mode 100644 index 000000000..86a7107bb --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_curl_priv.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * $Id: cpl_vsil_curl_priv.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Private API for VSICurl + * Author: Even Rouault, even.rouault at mines-paris.org + * + ****************************************************************************** + * Copyright (c) 2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_VSIL_CURL_PRIV_H_INCLUDED +#define CPL_VSIL_CURL_PRIV_H_INCLUDED + +#include "cpl_vsi_virtual.h" + +/* NOTE: this is private API for GDAL internal use. May change without notice. */ +/* Used by the MBTiles driver for now */ + +/* Return TRUE to go on downloading, FALSE to stop */ +typedef int (*VSICurlReadCbkFunc) (VSILFILE* fp, void *pabyBuffer, size_t nBufferSize, void* pfnUserData); + +/* fp must be a VSICurl file handle, otherwise bad things will happen ! */ +/* bStopOnInterrruptUntilUninstall must be set to TRUE if all downloads */ +/* must be cancelled after a first one has been stopped by the callback function. */ +/* In that case, downloads will restart after uninstalling the callback. */ +int VSICurlInstallReadCbk(VSILFILE* fp, VSICurlReadCbkFunc pfnReadCbk, void* pfnUserData, + int bStopOnInterrruptUntilUninstall); +int VSICurlUninstallReadCbk(VSILFILE* fp); + +#endif // CPL_VSIL_CURL_PRIV_H_INCLUDED diff --git a/bazaar/plugin/gdal/port/cpl_vsil_curl_streaming.cpp b/bazaar/plugin/gdal/port/cpl_vsil_curl_streaming.cpp new file mode 100644 index 000000000..5c91c7963 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_curl_streaming.cpp @@ -0,0 +1,1478 @@ +/****************************************************************************** + * $Id: cpl_vsil_curl_streaming.cpp 28459 2015-02-12 13:48:21Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement VSI large file api for HTTP/FTP files in streaming mode + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2012-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include "cpl_hash_set.h" +#include "cpl_time.h" + +CPL_CVSID("$Id: cpl_vsil_curl_streaming.cpp 28459 2015-02-12 13:48:21Z rouault $"); + +#if !defined(HAVE_CURL) || defined(CPL_MULTIPROC_STUB) + +void VSIInstallCurlStreamingFileHandler(void) +{ + /* not supported */ +} + +#else + +#include + +void VSICurlSetOptions(CURL* hCurlHandle, const char* pszURL); + +#include + +#define ENABLE_DEBUG 0 + +#define N_MAX_REGIONS 10 + +#define BKGND_BUFFER_SIZE (1024 * 1024) + + +/************************************************************************/ +/* RingBuffer */ +/************************************************************************/ + +class RingBuffer +{ + GByte* pabyBuffer; + size_t nCapacity; + size_t nOffset; + size_t nLength; + + public: + RingBuffer(size_t nCapacity = BKGND_BUFFER_SIZE); + ~RingBuffer(); + + size_t GetCapacity() const { return nCapacity; } + size_t GetSize() const { return nLength; } + + void Reset(); + void Write(void* pBuffer, size_t nSize); + void Read(void* pBuffer, size_t nSize); +}; + +RingBuffer::RingBuffer(size_t nCapacityIn) +{ + pabyBuffer = (GByte*)CPLMalloc(nCapacityIn); + nCapacity = nCapacityIn; + nOffset = 0; + nLength = 0; +} + +RingBuffer::~RingBuffer() +{ + CPLFree(pabyBuffer); +} + +void RingBuffer::Reset() +{ + nOffset = 0; + nLength = 0; +} + +void RingBuffer::Write(void* pBuffer, size_t nSize) +{ + CPLAssert(nLength + nSize <= nCapacity); + + size_t nEndOffset = (nOffset + nLength) % nCapacity; + size_t nSz = MIN(nSize, nCapacity - nEndOffset); + memcpy(pabyBuffer + nEndOffset, pBuffer, nSz); + if (nSz < nSize) + memcpy(pabyBuffer, (GByte*)pBuffer + nSz, nSize - nSz); + + nLength += nSize; +} + +void RingBuffer::Read(void* pBuffer, size_t nSize) +{ + CPLAssert(nSize <= nLength); + + if (pBuffer) + { + size_t nSz = MIN(nSize, nCapacity - nOffset); + memcpy(pBuffer, pabyBuffer + nOffset, nSz); + if (nSz < nSize) + memcpy((GByte*)pBuffer + nSz, pabyBuffer, nSize - nSz); + } + + nOffset = (nOffset + nSize) % nCapacity; + nLength -= nSize; +} + +/************************************************************************/ + +typedef enum +{ + EXIST_UNKNOWN = -1, + EXIST_NO, + EXIST_YES, +} ExistStatus; + +typedef struct +{ + ExistStatus eExists; + int bHastComputedFileSize; + vsi_l_offset fileSize; + int bIsDirectory; +#ifdef notdef + unsigned int nChecksumOfFirst1024Bytes; +#endif +} CachedFileProp; + +/************************************************************************/ +/* VSICurlStreamingFSHandler */ +/************************************************************************/ + +class VSICurlStreamingFSHandler : public VSIFilesystemHandler +{ + CPLMutex *hMutex; + + std::map cacheFileSize; + +public: + VSICurlStreamingFSHandler(); + ~VSICurlStreamingFSHandler(); + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); + + void AcquireMutex(); + void ReleaseMutex(); + + CachedFileProp* GetCachedFileProp(const char* pszURL); +}; + +/************************************************************************/ +/* VSICurlStreamingHandle */ +/************************************************************************/ + +class VSICurlStreamingHandle : public VSIVirtualHandle +{ + private: + VSICurlStreamingFSHandler* poFS; + + char* pszURL; + +#ifdef notdef + unsigned int nRecomputedChecksumOfFirst1024Bytes; +#endif + vsi_l_offset curOffset; + vsi_l_offset fileSize; + int bHastComputedFileSize; + ExistStatus eExists; + int bIsDirectory; + + int bCanTrustCandidateFileSize; + int bHasCandidateFileSize; + vsi_l_offset nCandidateFileSize; + + int bEOF; + + size_t nCachedSize; + GByte *pCachedData; + + CURL* hCurlHandle; + + volatile int bDownloadInProgress; + volatile int bDownloadStopped; + volatile int bAskDownloadEnd; + vsi_l_offset nRingBufferFileOffset; + CPLJoinableThread *hThread; + CPLMutex *hRingBufferMutex; + CPLCond *hCondProducer; + CPLCond *hCondConsumer; + RingBuffer oRingBuffer; + void StartDownload(); + void StopDownload(); + void PutRingBufferInCache(); + + GByte *pabyHeaderData; + size_t nHeaderSize; + vsi_l_offset nBodySize; + int nHTTPCode; + + void AcquireMutex(); + void ReleaseMutex(); + + void AddRegion( vsi_l_offset nFileOffsetStart, + size_t nSize, + GByte *pData ); + public: + + VSICurlStreamingHandle(VSICurlStreamingFSHandler* poFS, const char* pszURL); + ~VSICurlStreamingHandle(); + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); + + void DownloadInThread(); + int ReceivedBytes(GByte *buffer, size_t count, size_t nmemb); + int ReceivedBytesHeader(GByte *buffer, size_t count, size_t nmemb); + + int IsKnownFileSize() const { return bHastComputedFileSize; } + vsi_l_offset GetFileSize(); + int Exists(); + int IsDirectory() const { return bIsDirectory; } +}; + +/************************************************************************/ +/* VSICurlStreamingHandle() */ +/************************************************************************/ + +VSICurlStreamingHandle::VSICurlStreamingHandle(VSICurlStreamingFSHandler* poFS, const char* pszURL) +{ + this->poFS = poFS; + this->pszURL = CPLStrdup(pszURL); + +#ifdef notdef + nRecomputedChecksumOfFirst1024Bytes = 0; +#endif + curOffset = 0; + + poFS->AcquireMutex(); + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + eExists = cachedFileProp->eExists; + fileSize = cachedFileProp->fileSize; + bHastComputedFileSize = cachedFileProp->bHastComputedFileSize; + bIsDirectory = cachedFileProp->bIsDirectory; + poFS->ReleaseMutex(); + + bCanTrustCandidateFileSize = TRUE; + bHasCandidateFileSize = FALSE; + nCandidateFileSize = 0; + + nCachedSize = 0; + pCachedData = NULL; + + bEOF = FALSE; + + hCurlHandle = NULL; + + hThread = NULL; + hRingBufferMutex = CPLCreateMutex(); + ReleaseMutex(); + hCondProducer = CPLCreateCond(); + hCondConsumer = CPLCreateCond(); + + bDownloadInProgress = FALSE; + bDownloadStopped = FALSE; + bAskDownloadEnd = FALSE; + nRingBufferFileOffset = 0; + + pabyHeaderData = NULL; + nHeaderSize = 0; + nBodySize = 0; + nHTTPCode = 0; +} + +/************************************************************************/ +/* ~VSICurlStreamingHandle() */ +/************************************************************************/ + +VSICurlStreamingHandle::~VSICurlStreamingHandle() +{ + StopDownload(); + + CPLFree(pszURL); + if (hCurlHandle != NULL) + curl_easy_cleanup(hCurlHandle); + + CPLFree(pCachedData); + + CPLFree(pabyHeaderData); + + CPLDestroyMutex( hRingBufferMutex ); + CPLDestroyCond( hCondProducer ); + CPLDestroyCond( hCondConsumer ); +} + +/************************************************************************/ +/* AcquireMutex() */ +/************************************************************************/ + +void VSICurlStreamingHandle::AcquireMutex() +{ + CPLAcquireMutex(hRingBufferMutex, 1000.0); +} + +/************************************************************************/ +/* ReleaseMutex() */ +/************************************************************************/ + +void VSICurlStreamingHandle::ReleaseMutex() +{ + CPLReleaseMutex(hRingBufferMutex); +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSICurlStreamingHandle::Seek( vsi_l_offset nOffset, int nWhence ) +{ + if( curOffset >= BKGND_BUFFER_SIZE ) + { + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Invalidating cache and file size due to Seek() beyond caching zone"); + CPLFree(pCachedData); + pCachedData = NULL; + nCachedSize = 0; + AcquireMutex(); + bHastComputedFileSize = FALSE; + fileSize = 0; + ReleaseMutex(); + } + + if (nWhence == SEEK_SET) + { + curOffset = nOffset; + } + else if (nWhence == SEEK_CUR) + { + curOffset = curOffset + nOffset; + } + else + { + curOffset = GetFileSize() + nOffset; + } + bEOF = FALSE; + return 0; +} + +typedef struct +{ + char* pBuffer; + size_t nSize; + int bIsHTTP; + int bIsInHeader; + int nHTTPCode; + int bDownloadHeaderOnly; +} WriteFuncStruct; + +/************************************************************************/ +/* VSICURLStreamingInitWriteFuncStruct() */ +/************************************************************************/ + +static void VSICURLStreamingInitWriteFuncStruct(WriteFuncStruct *psStruct) +{ + psStruct->pBuffer = NULL; + psStruct->nSize = 0; + psStruct->bIsHTTP = FALSE; + psStruct->bIsInHeader = TRUE; + psStruct->nHTTPCode = 0; + psStruct->bDownloadHeaderOnly = FALSE; +} + +/************************************************************************/ +/* VSICurlStreamingHandleWriteFuncForHeader() */ +/************************************************************************/ + +static int VSICurlStreamingHandleWriteFuncForHeader(void *buffer, size_t count, size_t nmemb, void *req) +{ + WriteFuncStruct* psStruct = (WriteFuncStruct*) req; + size_t nSize = count * nmemb; + + char* pNewBuffer = (char*) VSIRealloc(psStruct->pBuffer, + psStruct->nSize + nSize + 1); + if (pNewBuffer) + { + psStruct->pBuffer = pNewBuffer; + memcpy(psStruct->pBuffer + psStruct->nSize, buffer, nSize); + psStruct->pBuffer[psStruct->nSize + nSize] = '\0'; + if (psStruct->bIsHTTP && psStruct->bIsInHeader) + { + char* pszLine = psStruct->pBuffer + psStruct->nSize; + if (EQUALN(pszLine, "HTTP/1.0 ", 9) || + EQUALN(pszLine, "HTTP/1.1 ", 9)) + psStruct->nHTTPCode = atoi(pszLine + 9); + + if (pszLine[0] == '\r' || pszLine[0] == '\n') + { + if (psStruct->bDownloadHeaderOnly) + { + /* If moved permanently/temporarily, go on. Otherwise stop now*/ + if (!(psStruct->nHTTPCode == 301 || psStruct->nHTTPCode == 302)) + return 0; + } + else + { + psStruct->bIsInHeader = FALSE; + } + } + } + psStruct->nSize += nSize; + return nmemb; + } + else + { + return 0; + } +} + + +/************************************************************************/ +/* GetFileSize() */ +/************************************************************************/ + +vsi_l_offset VSICurlStreamingHandle::GetFileSize() +{ + WriteFuncStruct sWriteFuncData; + WriteFuncStruct sWriteFuncHeaderData; + + AcquireMutex(); + if (bHastComputedFileSize) + { + vsi_l_offset nRet = fileSize; + ReleaseMutex(); + return nRet; + } + ReleaseMutex(); + +#if LIBCURL_VERSION_NUM < 0x070B00 + /* Curl 7.10.X doesn't manage to unset the CURLOPT_RANGE that would have been */ + /* previously set, so we have to reinit the connection handle */ + if (hCurlHandle) + { + curl_easy_cleanup(hCurlHandle); + hCurlHandle = curl_easy_init(); + } +#endif + + CURL* hLocalHandle = curl_easy_init(); + + VSICurlSetOptions(hLocalHandle, pszURL); + + VSICURLStreamingInitWriteFuncStruct(&sWriteFuncHeaderData); + + /* HACK for mbtiles driver: proper fix would be to auto-detect servers that don't accept HEAD */ + /* http://a.tiles.mapbox.com/v3/ doesn't accept HEAD, so let's start a GET */ + /* and interrupt is as soon as the header is found */ + if (strstr(pszURL, ".tiles.mapbox.com/") != NULL) + { + curl_easy_setopt(hLocalHandle, CURLOPT_HEADERDATA, &sWriteFuncHeaderData); + curl_easy_setopt(hLocalHandle, CURLOPT_HEADERFUNCTION, VSICurlStreamingHandleWriteFuncForHeader); + + sWriteFuncHeaderData.bIsHTTP = strncmp(pszURL, "http", 4) == 0; + sWriteFuncHeaderData.bDownloadHeaderOnly = TRUE; + } + else + { + curl_easy_setopt(hLocalHandle, CURLOPT_NOBODY, 1); + curl_easy_setopt(hLocalHandle, CURLOPT_HTTPGET, 0); + curl_easy_setopt(hLocalHandle, CURLOPT_HEADER, 1); + } + + /* We need that otherwise OSGEO4W's libcurl issue a dummy range request */ + /* when doing a HEAD when recycling connections */ + curl_easy_setopt(hLocalHandle, CURLOPT_RANGE, NULL); + + /* Bug with older curl versions (<=7.16.4) and FTP. See http://curl.haxx.se/mail/lib-2007-08/0312.html */ + VSICURLStreamingInitWriteFuncStruct(&sWriteFuncData); + curl_easy_setopt(hLocalHandle, CURLOPT_WRITEDATA, &sWriteFuncData); + curl_easy_setopt(hLocalHandle, CURLOPT_WRITEFUNCTION, VSICurlStreamingHandleWriteFuncForHeader); + + char szCurlErrBuf[CURL_ERROR_SIZE+1]; + szCurlErrBuf[0] = '\0'; + curl_easy_setopt(hLocalHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf ); + + double dfSize = 0; + curl_easy_perform(hLocalHandle); + + AcquireMutex(); + + eExists = EXIST_UNKNOWN; + bHastComputedFileSize = TRUE; + + if (strncmp(pszURL, "ftp", 3) == 0) + { + if (sWriteFuncData.pBuffer != NULL && + strncmp(sWriteFuncData.pBuffer, "Content-Length: ", strlen( "Content-Length: ")) == 0) + { + const char* pszBuffer = sWriteFuncData.pBuffer + strlen("Content-Length: "); + eExists = EXIST_YES; + fileSize = CPLScanUIntBig(pszBuffer, sWriteFuncData.nSize - strlen("Content-Length: ")); + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "GetFileSize(%s)=" CPL_FRMT_GUIB, + pszURL, fileSize); + } + } + + if (eExists != EXIST_YES) + { + CURLcode code = curl_easy_getinfo(hLocalHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dfSize ); + if (code == 0) + { + eExists = EXIST_YES; + if (dfSize < 0) + fileSize = 0; + else + fileSize = (GUIntBig)dfSize; + } + else + { + eExists = EXIST_NO; + fileSize = 0; + CPLError(CE_Failure, CPLE_AppDefined, + "VSICurlStreamingHandle::GetFileSize failed"); + } + + long response_code = 0; + curl_easy_getinfo(hLocalHandle, CURLINFO_HTTP_CODE, &response_code); + if (response_code != 200) + { + eExists = EXIST_NO; + fileSize = 0; + } + + /* Try to guess if this is a directory. Generally if this is a directory, */ + /* curl will retry with an URL with slash added */ + char *pszEffectiveURL = NULL; + curl_easy_getinfo(hLocalHandle, CURLINFO_EFFECTIVE_URL, &pszEffectiveURL); + if (pszEffectiveURL != NULL && + strncmp(pszURL, pszEffectiveURL, strlen(pszURL)) == 0 && + pszEffectiveURL[strlen(pszURL)] == '/') + { + eExists = EXIST_YES; + fileSize = 0; + bIsDirectory = TRUE; + } + + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "GetFileSize(%s)=" CPL_FRMT_GUIB " response_code=%d", + pszURL, fileSize, (int)response_code); + } + + CPLFree(sWriteFuncData.pBuffer); + CPLFree(sWriteFuncHeaderData.pBuffer); + + poFS->AcquireMutex(); + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + cachedFileProp->bHastComputedFileSize = TRUE; +#ifdef notdef + cachedFileProp->nChecksumOfFirst1024Bytes = nRecomputedChecksumOfFirst1024Bytes; +#endif + cachedFileProp->fileSize = fileSize; + cachedFileProp->eExists = eExists; + cachedFileProp->bIsDirectory = bIsDirectory; + poFS->ReleaseMutex(); + + vsi_l_offset nRet = fileSize; + ReleaseMutex(); + + if (hCurlHandle == NULL) + hCurlHandle = hLocalHandle; + else + curl_easy_cleanup(hLocalHandle); + + return nRet; +} + +/************************************************************************/ +/* Exists() */ +/************************************************************************/ + +int VSICurlStreamingHandle::Exists() +{ + if (eExists == EXIST_UNKNOWN) + { + /* Consider that only the files whose extension ends up with one that is */ + /* listed in CPL_VSIL_CURL_ALLOWED_EXTENSIONS exist on the server */ + /* This can speeds up dramatically open experience, in case the server */ + /* cannot return a file list */ + /* For example : */ + /* gdalinfo --config CPL_VSIL_CURL_ALLOWED_EXTENSIONS ".tif" /vsicurl_streaming/http://igskmncngs506.cr.usgs.gov/gmted/Global_tiles_GMTED/075darcsec/bln/W030/30N030W_20101117_gmted_bln075.tif */ + const char* pszAllowedExtensions = + CPLGetConfigOption("CPL_VSIL_CURL_ALLOWED_EXTENSIONS", NULL); + if (pszAllowedExtensions) + { + char** papszExtensions = CSLTokenizeString2( pszAllowedExtensions, ", ", 0 ); + int nURLLen = strlen(pszURL); + int bFound = FALSE; + for(int i=0;papszExtensions[i] != NULL;i++) + { + int nExtensionLen = strlen(papszExtensions[i]); + if (nURLLen > nExtensionLen && + EQUAL(pszURL + nURLLen - nExtensionLen, papszExtensions[i])) + { + bFound = TRUE; + break; + } + } + + if (!bFound) + { + eExists = EXIST_NO; + fileSize = 0; + + poFS->AcquireMutex(); + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + cachedFileProp->bHastComputedFileSize = TRUE; + cachedFileProp->fileSize = fileSize; + cachedFileProp->eExists = eExists; + poFS->ReleaseMutex(); + + CSLDestroy(papszExtensions); + + return 0; + } + + CSLDestroy(papszExtensions); + } + + char chFirstByte; + int bExists = (Read(&chFirstByte, 1, 1) == 1); + + AcquireMutex(); + poFS->AcquireMutex(); + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + cachedFileProp->eExists = eExists = bExists ? EXIST_YES : EXIST_NO; + poFS->ReleaseMutex(); + ReleaseMutex(); + + Seek(0, SEEK_SET); + } + + return eExists == EXIST_YES; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSICurlStreamingHandle::Tell() +{ + return curOffset; +} + +/************************************************************************/ +/* ReceivedBytes() */ +/************************************************************************/ + +int VSICurlStreamingHandle::ReceivedBytes(GByte *buffer, size_t count, size_t nmemb) +{ + size_t nSize = count * nmemb; + nBodySize += nSize; + + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Receiving %d bytes...", (int)nSize); + + if( bHasCandidateFileSize && bCanTrustCandidateFileSize && !bHastComputedFileSize ) + { + poFS->AcquireMutex(); + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + cachedFileProp->fileSize = fileSize = nCandidateFileSize; + cachedFileProp->bHastComputedFileSize = bHastComputedFileSize = TRUE; + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "File size = " CPL_FRMT_GUIB, fileSize); + poFS->ReleaseMutex(); + } + + AcquireMutex(); + if (eExists == EXIST_UNKNOWN) + { + poFS->AcquireMutex(); + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + cachedFileProp->eExists = eExists = EXIST_YES; + poFS->ReleaseMutex(); + } + else if (eExists == EXIST_NO) + { + ReleaseMutex(); + return 0; + } + + while(TRUE) + { + size_t nFree = oRingBuffer.GetCapacity() - oRingBuffer.GetSize(); + if (nSize <= nFree) + { + oRingBuffer.Write(buffer, nSize); + + /* Signal to the consumer that we have added bytes to the buffer */ + CPLCondSignal(hCondProducer); + + if (bAskDownloadEnd) + { + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Download interruption asked"); + + ReleaseMutex(); + return 0; + } + break; + } + else + { + oRingBuffer.Write(buffer, nFree); + buffer += nFree; + nSize -= nFree; + + /* Signal to the consumer that we have added bytes to the buffer */ + CPLCondSignal(hCondProducer); + + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Waiting for reader to consume some bytes..."); + + while(oRingBuffer.GetSize() == oRingBuffer.GetCapacity() && !bAskDownloadEnd) + { + CPLCondWait(hCondConsumer, hRingBufferMutex); + } + + if (bAskDownloadEnd) + { + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Download interruption asked"); + + ReleaseMutex(); + return 0; + } + } + } + + ReleaseMutex(); + + return nmemb; +} + +/************************************************************************/ +/* VSICurlStreamingHandleReceivedBytes() */ +/************************************************************************/ + +static int VSICurlStreamingHandleReceivedBytes(void *buffer, size_t count, size_t nmemb, void *req) +{ + return ((VSICurlStreamingHandle*)req)->ReceivedBytes((GByte*)buffer, count, nmemb); +} + + +/************************************************************************/ +/* VSICurlStreamingHandleReceivedBytesHeader() */ +/************************************************************************/ + +#define HEADER_SIZE 32768 + +int VSICurlStreamingHandle::ReceivedBytesHeader(GByte *buffer, size_t count, size_t nmemb) +{ + size_t nSize = count * nmemb; + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Receiving %d bytes for header...", (int)nSize); + + /* Reset buffer if we have followed link after a redirect */ + if (nSize >=9 && (nHTTPCode == 301 || nHTTPCode == 302) && + (EQUALN((const char*)buffer, "HTTP/1.0 ", 9) || + EQUALN((const char*)buffer, "HTTP/1.1 ", 9))) + { + nHeaderSize = 0; + nHTTPCode = 0; + } + + if (nHeaderSize < HEADER_SIZE) + { + size_t nSz = MIN(nSize, HEADER_SIZE - nHeaderSize); + memcpy(pabyHeaderData + nHeaderSize, buffer, nSz); + pabyHeaderData[nHeaderSize + nSz] = '\0'; + nHeaderSize += nSz; + + //CPLDebug("VSICURL", "Header : %s", pabyHeaderData); + + AcquireMutex(); + + if (eExists == EXIST_UNKNOWN && nHTTPCode == 0 && + strchr((const char*)pabyHeaderData, '\n') != NULL && + (EQUALN((const char*)pabyHeaderData, "HTTP/1.0 ", 9) || + EQUALN((const char*)pabyHeaderData, "HTTP/1.1 ", 9))) + { + nHTTPCode = atoi((const char*)pabyHeaderData + 9); + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "HTTP code = %d", nHTTPCode); + + /* If moved permanently/temporarily, go on */ + if( !(nHTTPCode == 301 || nHTTPCode == 302) ) + { + poFS->AcquireMutex(); + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + cachedFileProp->eExists = eExists = (nHTTPCode == 200) ? EXIST_YES : EXIST_NO; + poFS->ReleaseMutex(); + } + } + + if ( !(nHTTPCode == 301 || nHTTPCode == 302) && !bHastComputedFileSize) + { + /* Caution: when gzip compression is enabled, the content-length is the compressed */ + /* size, which we are not interested in, so we must not take it into account. */ + + const char* pszContentLength = strstr((const char*)pabyHeaderData, "Content-Length: "); + const char* pszEndOfLine = pszContentLength ? strchr(pszContentLength, '\n') : NULL; + if( bCanTrustCandidateFileSize && pszEndOfLine != NULL ) + { + const char* pszVal = pszContentLength + strlen("Content-Length: "); + bHasCandidateFileSize = TRUE; + nCandidateFileSize = CPLScanUIntBig(pszVal, pszEndOfLine - pszVal); + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Has found candidate file size = " CPL_FRMT_GUIB, nCandidateFileSize); + } + + const char* pszContentEncoding = strstr((const char*)pabyHeaderData, "Content-Encoding: "); + pszEndOfLine = pszContentEncoding ? strchr(pszContentEncoding, '\n') : NULL; + if( bHasCandidateFileSize && pszEndOfLine != NULL ) + { + const char* pszVal = pszContentEncoding + strlen("Content-Encoding: "); + if( strncmp(pszVal, "gzip", 4) == 0 ) + { + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "GZip compression enabled --> cannot trust candidate file size"); + bCanTrustCandidateFileSize = FALSE; + } + } + } + + ReleaseMutex(); + } + + return nmemb; +} + +/************************************************************************/ +/* VSICurlStreamingHandleReceivedBytesHeader() */ +/************************************************************************/ + +static int VSICurlStreamingHandleReceivedBytesHeader(void *buffer, size_t count, size_t nmemb, void *req) +{ + return ((VSICurlStreamingHandle*)req)->ReceivedBytesHeader((GByte*)buffer, count, nmemb); +} +/************************************************************************/ +/* DownloadInThread() */ +/************************************************************************/ + +void VSICurlStreamingHandle::DownloadInThread() +{ + VSICurlSetOptions(hCurlHandle, pszURL); + + static int bHasCheckVersion = FALSE; + static int bSupportGZip = FALSE; + if (!bHasCheckVersion) + { + bSupportGZip = strstr(curl_version(), "zlib/") != NULL; + bHasCheckVersion = TRUE; + } + if (bSupportGZip && CSLTestBoolean(CPLGetConfigOption("CPL_CURL_GZIP", "YES"))) + { + curl_easy_setopt(hCurlHandle, CURLOPT_ENCODING, "gzip"); + } + + if (pabyHeaderData == NULL) + pabyHeaderData = (GByte*) CPLMalloc(HEADER_SIZE + 1); + nHeaderSize = 0; + nBodySize = 0; + nHTTPCode = 0; + + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, this); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION, VSICurlStreamingHandleReceivedBytesHeader); + + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, this); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, VSICurlStreamingHandleReceivedBytes); + + char szCurlErrBuf[CURL_ERROR_SIZE+1]; + szCurlErrBuf[0] = '\0'; + curl_easy_setopt(hCurlHandle, CURLOPT_ERRORBUFFER, szCurlErrBuf ); + + CURLcode eRet = curl_easy_perform(hCurlHandle); + + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEDATA, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERDATA, NULL); + curl_easy_setopt(hCurlHandle, CURLOPT_HEADERFUNCTION, NULL); + + AcquireMutex(); + if (!bAskDownloadEnd && eRet == 0 && !bHastComputedFileSize) + { + poFS->AcquireMutex(); + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + cachedFileProp->fileSize = fileSize = nBodySize; + cachedFileProp->bHastComputedFileSize = bHastComputedFileSize = TRUE; + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "File size = " CPL_FRMT_GUIB, fileSize); + poFS->ReleaseMutex(); + } + + bDownloadInProgress = FALSE; + bDownloadStopped = TRUE; + + /* Signal to the consumer that the download has ended */ + CPLCondSignal(hCondProducer); + ReleaseMutex(); +} + +static void VSICurlDownloadInThread(void* pArg) +{ + ((VSICurlStreamingHandle*)pArg)->DownloadInThread(); +} + +/************************************************************************/ +/* StartDownload() */ +/************************************************************************/ + +void VSICurlStreamingHandle::StartDownload() +{ + if (bDownloadInProgress || bDownloadStopped) + return; + + //if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Start download for %s", pszURL); + + if (hCurlHandle == NULL) + hCurlHandle = curl_easy_init(); + oRingBuffer.Reset(); + bDownloadInProgress = TRUE; + nRingBufferFileOffset = 0; + hThread = CPLCreateJoinableThread(VSICurlDownloadInThread, this); +} + +/************************************************************************/ +/* StopDownload() */ +/************************************************************************/ + +void VSICurlStreamingHandle::StopDownload() +{ + if (hThread) + { + //if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Stop download for %s", pszURL); + + AcquireMutex(); + /* Signal to the producer that we ask for download interruption */ + bAskDownloadEnd = TRUE; + CPLCondSignal(hCondConsumer); + + /* Wait for the producer to have finished */ + while(bDownloadInProgress) + CPLCondWait(hCondProducer, hRingBufferMutex); + + bAskDownloadEnd = FALSE; + + ReleaseMutex(); + + CPLJoinThread(hThread); + hThread = NULL; + + curl_easy_cleanup(hCurlHandle); + hCurlHandle = NULL; + } + + oRingBuffer.Reset(); + bDownloadStopped = FALSE; +} + +/************************************************************************/ +/* PutRingBufferInCache() */ +/************************************************************************/ + +void VSICurlStreamingHandle::PutRingBufferInCache() +{ + if (nRingBufferFileOffset >= BKGND_BUFFER_SIZE) + return; + + AcquireMutex(); + + /* Cache any remaining bytes available in the ring buffer */ + size_t nBufSize = oRingBuffer.GetSize(); + if ( nBufSize > 0 ) + { + if (nRingBufferFileOffset + nBufSize > BKGND_BUFFER_SIZE) + nBufSize = (size_t) (BKGND_BUFFER_SIZE - nRingBufferFileOffset); + GByte* pabyTmp = (GByte*) CPLMalloc(nBufSize); + oRingBuffer.Read(pabyTmp, nBufSize); + + /* Signal to the producer that we have ingested some bytes */ + CPLCondSignal(hCondConsumer); + + AddRegion(nRingBufferFileOffset, nBufSize, pabyTmp); + nRingBufferFileOffset += nBufSize; + CPLFree(pabyTmp); + } + + ReleaseMutex(); +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSICurlStreamingHandle::Read( void *pBuffer, size_t nSize, size_t nMemb ) +{ + GByte* pabyBuffer = (GByte*)pBuffer; + size_t nBufferRequestSize = nSize * nMemb; + if (nBufferRequestSize == 0) + return 0; + size_t nRemaining = nBufferRequestSize; + + AcquireMutex(); + int bHastComputedFileSizeLocal = bHastComputedFileSize; + vsi_l_offset fileSizeLocal = fileSize; + ReleaseMutex(); + + if (bHastComputedFileSizeLocal && curOffset >= fileSizeLocal) + { + CPLDebug("VSICURL", "Read attempt beyond end of file"); + bEOF = TRUE; + } + if (bEOF) + return 0; + + if (curOffset < nRingBufferFileOffset) + PutRingBufferInCache(); + + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Read [" CPL_FRMT_GUIB ", " CPL_FRMT_GUIB "[ in %s", + curOffset, curOffset + nBufferRequestSize, pszURL); + +#ifdef notdef + if( pCachedData != NULL && nCachedSize >= 1024 && + nRecomputedChecksumOfFirst1024Bytes == 0 ) + { + for(size_t i = 0; i < 1024 / sizeof(int); i ++) + { + int nVal; + memcpy(&nVal, pCachedData + i * sizeof(int), sizeof(int)); + nRecomputedChecksumOfFirst1024Bytes += nVal; + } + + if( bHastComputedFileSizeLocal ) + { + poFS->AcquireMutex(); + CachedFileProp* cachedFileProp = poFS->GetCachedFileProp(pszURL); + if( cachedFileProp->nChecksumOfFirst1024Bytes == 0 ) + { + cachedFileProp->nChecksumOfFirst1024Bytes = nRecomputedChecksumOfFirst1024Bytes; + } + else if( nRecomputedChecksumOfFirst1024Bytes != cachedFileProp->nChecksumOfFirst1024Bytes ) + { + CPLDebug("VSICURL", "Invalidating previously cached file size. First bytes of file have changed!"); + AcquireMutex(); + bHastComputedFileSize = FALSE; + cachedFileProp->bHastComputedFileSize = FALSE; + cachedFileProp->nChecksumOfFirst1024Bytes = 0; + ReleaseMutex(); + } + poFS->ReleaseMutex(); + } + } +#endif + + /* Can we use the cache ? */ + if( pCachedData != NULL && curOffset < nCachedSize ) + { + size_t nSz = MIN(nRemaining, (size_t)(nCachedSize - curOffset)); + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Using cache for [%d, %d[ in %s", + (int)curOffset, (int)(curOffset + nSz), pszURL); + memcpy(pabyBuffer, pCachedData + curOffset, nSz); + pabyBuffer += nSz; + curOffset += nSz; + nRemaining -= nSz; + } + + /* Is the request partially covered by the cache and going beyond file size ? */ + if ( pCachedData != NULL && bHastComputedFileSizeLocal && + curOffset <= nCachedSize && + curOffset + nRemaining > fileSizeLocal && + fileSize == nCachedSize ) + { + size_t nSz = (size_t) (nCachedSize - curOffset); + if (ENABLE_DEBUG && nSz != 0) + CPLDebug("VSICURL", "Using cache for [%d, %d[ in %s", + (int)curOffset, (int)(curOffset + nSz), pszURL); + memcpy(pabyBuffer, pCachedData + curOffset, nSz); + pabyBuffer += nSz; + curOffset += nSz; + nRemaining -= nSz; + bEOF = TRUE; + } + + /* Has a Seek() being done since the last Read() ? */ + if (!bEOF && nRemaining > 0 && curOffset != nRingBufferFileOffset) + { + /* Backward seek : we need to restart the download from the start */ + if (curOffset < nRingBufferFileOffset) + StopDownload(); + + StartDownload(); + +#define SKIP_BUFFER_SIZE 32768 + GByte* pabyTmp = (GByte*)CPLMalloc(SKIP_BUFFER_SIZE); + + CPLAssert(curOffset >= nRingBufferFileOffset); + vsi_l_offset nBytesToSkip = curOffset - nRingBufferFileOffset; + while(nBytesToSkip > 0) + { + vsi_l_offset nBytesToRead = nBytesToSkip; + + AcquireMutex(); + if (nBytesToRead > oRingBuffer.GetSize()) + nBytesToRead = oRingBuffer.GetSize(); + if (nBytesToRead > SKIP_BUFFER_SIZE) + nBytesToRead = SKIP_BUFFER_SIZE; + oRingBuffer.Read(pabyTmp, (size_t)nBytesToRead); + + /* Signal to the producer that we have ingested some bytes */ + CPLCondSignal(hCondConsumer); + ReleaseMutex(); + + if (nBytesToRead) + AddRegion(nRingBufferFileOffset, (size_t)nBytesToRead, pabyTmp); + + nBytesToSkip -= nBytesToRead; + nRingBufferFileOffset += nBytesToRead; + + if (nBytesToRead == 0 && nBytesToSkip != 0) + { + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Waiting for writer to produce some bytes..."); + + AcquireMutex(); + while(oRingBuffer.GetSize() == 0 && bDownloadInProgress) + CPLCondWait(hCondProducer, hRingBufferMutex); + int bBufferEmpty = (oRingBuffer.GetSize() == 0); + ReleaseMutex(); + + if (bBufferEmpty && !bDownloadInProgress) + break; + } + } + + CPLFree(pabyTmp); + + if (nBytesToSkip != 0) + { + bEOF = TRUE; + return 0; + } + } + + if (!bEOF && nRemaining > 0) + { + StartDownload(); + CPLAssert(curOffset == nRingBufferFileOffset); + } + + /* Fill the destination buffer from the ring buffer */ + while(!bEOF && nRemaining > 0) + { + AcquireMutex(); + size_t nToRead = oRingBuffer.GetSize(); + if (nToRead > nRemaining) + nToRead = nRemaining; + oRingBuffer.Read(pabyBuffer, nToRead); + + /* Signal to the producer that we have ingested some bytes */ + CPLCondSignal(hCondConsumer); + ReleaseMutex(); + + if (nToRead) + AddRegion(curOffset, nToRead, pabyBuffer); + + nRemaining -= nToRead; + pabyBuffer += nToRead; + curOffset += nToRead; + nRingBufferFileOffset += nToRead; + + if (nToRead == 0 && nRemaining != 0) + { + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Waiting for writer to produce some bytes..."); + + AcquireMutex(); + while(oRingBuffer.GetSize() == 0 && bDownloadInProgress) + CPLCondWait(hCondProducer, hRingBufferMutex); + int bBufferEmpty = (oRingBuffer.GetSize() == 0); + ReleaseMutex(); + + if (bBufferEmpty && !bDownloadInProgress) + break; + } + } + + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Read(%d) = %d", + (int)nBufferRequestSize, (int)(nBufferRequestSize - nRemaining)); + size_t nRet = (nBufferRequestSize - nRemaining) / nSize; + if (nRet < nMemb) + bEOF = TRUE; + + return nRet; +} + +/************************************************************************/ +/* AddRegion() */ +/************************************************************************/ + +void VSICurlStreamingHandle::AddRegion( vsi_l_offset nFileOffsetStart, + size_t nSize, + GByte *pData ) +{ + if (nFileOffsetStart >= BKGND_BUFFER_SIZE) + return; + + if (pCachedData == NULL) + pCachedData = (GByte*) CPLMalloc(BKGND_BUFFER_SIZE); + + if (nFileOffsetStart <= nCachedSize && + nFileOffsetStart + nSize > nCachedSize) + { + size_t nSz = MIN(nSize, (size_t) (BKGND_BUFFER_SIZE - nFileOffsetStart)); + if (ENABLE_DEBUG) + CPLDebug("VSICURL", "Writing [%d, %d[ in cache for %s", + (int)nFileOffsetStart, (int)(nFileOffsetStart + nSz), pszURL); + memcpy(pCachedData + nFileOffsetStart, pData, nSz); + nCachedSize = (size_t) (nFileOffsetStart + nSz); + } +} +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSICurlStreamingHandle::Write( CPL_UNUSED const void *pBuffer, + CPL_UNUSED size_t nSize, + CPL_UNUSED size_t nMemb ) +{ + return 0; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + + +int VSICurlStreamingHandle::Eof() +{ + return bEOF; +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSICurlStreamingHandle::Flush() +{ + return 0; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSICurlStreamingHandle::Close() +{ + return 0; +} + + +/************************************************************************/ +/* VSICurlStreamingFSHandler() */ +/************************************************************************/ + +VSICurlStreamingFSHandler::VSICurlStreamingFSHandler() +{ + hMutex = CPLCreateMutex(); + CPLReleaseMutex(hMutex); +} + +/************************************************************************/ +/* ~VSICurlStreamingFSHandler() */ +/************************************************************************/ + +VSICurlStreamingFSHandler::~VSICurlStreamingFSHandler() +{ + std::map::const_iterator iterCacheFileSize; + + for( iterCacheFileSize = cacheFileSize.begin(); + iterCacheFileSize != cacheFileSize.end(); + iterCacheFileSize++ ) + { + CPLFree(iterCacheFileSize->second); + } + + CPLDestroyMutex( hMutex ); + hMutex = NULL; +} + +/************************************************************************/ +/* AcquireMutex() */ +/************************************************************************/ + +void VSICurlStreamingFSHandler::AcquireMutex() +{ + CPLAcquireMutex(hMutex, 1000.0); +} + +/************************************************************************/ +/* ReleaseMutex() */ +/************************************************************************/ + +void VSICurlStreamingFSHandler::ReleaseMutex() +{ + CPLReleaseMutex(hMutex); +} + +/************************************************************************/ +/* GetCachedFileProp() */ +/************************************************************************/ + +/* Should be called under the FS Lock */ + +CachedFileProp* VSICurlStreamingFSHandler::GetCachedFileProp(const char* pszURL) +{ + CachedFileProp* cachedFileProp = cacheFileSize[pszURL]; + if (cachedFileProp == NULL) + { + cachedFileProp = (CachedFileProp*) CPLMalloc(sizeof(CachedFileProp)); + cachedFileProp->eExists = EXIST_UNKNOWN; + cachedFileProp->bHastComputedFileSize = FALSE; + cachedFileProp->fileSize = 0; + cachedFileProp->bIsDirectory = FALSE; +#ifdef notdef + cachedFileProp->nChecksumOfFirst1024Bytes = 0; +#endif + cacheFileSize[pszURL] = cachedFileProp; + } + + return cachedFileProp; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle* VSICurlStreamingFSHandler::Open( const char *pszFilename, + const char *pszAccess ) +{ + if (strchr(pszAccess, 'w') != NULL || + strchr(pszAccess, '+') != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Only read-only mode is supported for /vsicurl_streaming"); + return NULL; + } + + VSICurlStreamingHandle* poHandle = new VSICurlStreamingHandle( + this, pszFilename + strlen("/vsicurl_streaming/")); + /* If we didn't get a filelist, check that the file really exists */ + if (!poHandle->Exists()) + { + delete poHandle; + poHandle = NULL; + } + + if( CSLTestBoolean( CPLGetConfigOption( "VSI_CACHE", "FALSE" ) ) ) + return VSICreateCachedFile( poHandle ); + else + return poHandle; +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSICurlStreamingFSHandler::Stat( const char *pszFilename, + VSIStatBufL *pStatBuf, + int nFlags ) +{ + CPLString osFilename(pszFilename); + + memset(pStatBuf, 0, sizeof(VSIStatBufL)); + + VSICurlStreamingHandle oHandle( this, osFilename + strlen("/vsicurl_streaming/")); + + if ( oHandle.IsKnownFileSize() || + ((nFlags & VSI_STAT_SIZE_FLAG) && !oHandle.IsDirectory() && + CSLTestBoolean(CPLGetConfigOption("CPL_VSIL_CURL_SLOW_GET_SIZE", "YES"))) ) + pStatBuf->st_size = oHandle.GetFileSize(); + + int nRet = (oHandle.Exists()) ? 0 : -1; + pStatBuf->st_mode = oHandle.IsDirectory() ? S_IFDIR : S_IFREG; + return nRet; +} + +/************************************************************************/ +/* VSIInstallCurlFileHandler() */ +/************************************************************************/ + +/** + * \brief Install /vsicurl_streaming/ HTTP/FTP file system handler (requires libcurl) + * + * A special file handler is installed that allows on-the-fly reading of files + * streamed through HTTP/FTP web protocols (typically dynamically generated files), + * without downloading the entire file. + * + * Although this file handler is able seek to random offsets in the file, this will not + * be efficient. If you need efficient random access and that the server supports range + * dowloading, you should use the /vsicurl/ file system handler instead. + * + * Recognized filenames are of the form /vsicurl_streaming/http://path/to/remote/resource or + * /vsicurl_streaming/ftp://path/to/remote/resource where path/to/remote/resource is the + * URL of a remote resource. + * + * The GDAL_HTTP_PROXY, GDAL_HTTP_PROXYUSERPWD and GDAL_PROXY_AUTH configuration options can be + * used to define a proxy server. The syntax to use is the one of Curl CURLOPT_PROXY, + * CURLOPT_PROXYUSERPWD and CURLOPT_PROXYAUTH options. + * + * The file can be cached in RAM by setting the configuration option + * VSI_CACHE to TRUE. The cache size defaults to 25 MB, but can be modified by setting + * the configuration option VSI_CACHE_SIZE (in bytes). + * + * VSIStatL() will return the size in st_size member and file + * nature- file or directory - in st_mode member (the later only reliable with FTP + * resources for now). + * + * @since GDAL 1.10 + */ +void VSIInstallCurlStreamingFileHandler(void) +{ + VSIFileManager::InstallHandler( "/vsicurl_streaming/", new VSICurlStreamingFSHandler ); +} + +#ifdef AS_PLUGIN +CPL_C_START +void CPL_DLL GDALRegisterMe(); +void GDALRegisterMe() +{ + VSIInstallCurlStreamingFileHandler(); +} +CPL_C_END +#endif + +#endif /* !defined(HAVE_CURL) || defined(CPL_MULTIPROC_STUB) */ diff --git a/bazaar/plugin/gdal/port/cpl_vsil_gzip.cpp b/bazaar/plugin/gdal/port/cpl_vsil_gzip.cpp new file mode 100644 index 000000000..91bfb6c4f --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_gzip.cpp @@ -0,0 +1,2587 @@ +/****************************************************************************** + * $Id: cpl_vsil_gzip.cpp 29033 2015-04-27 10:47:22Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement VSI large file api for gz/zip files (.gz and .zip). + * Author: Even Rouault, even.rouault at mines-paris.org + * + ****************************************************************************** + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +/* gzio.c -- IO on .gz files + Copyright (C) 1995-2005 Jean-loup Gailly. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + + +/* This file contains a refactoring of gzio.c from zlib project. + + It replaces classical calls operating on FILE* by calls to the VSI large file + API. It also adds the capability to seek at the end of the file, which is not + implemented in original gzSeek. It also implements a concept of in-memory "snapshots", + that are a way of improving efficiency while seeking GZip files. Snapshots are + ceated regularly when decompressing the data a snapshot of the gzip state. + Later we can seek directly in the compressed data to the closest snapshot in order to + reduce the amount of data to uncompress again. + + For .gz files, an effort is done to cache the size of the uncompressed data in + a .gz.properties file, so that we don't need to seek at the end of the file + each time a Stat() is done. + + For .zip and .gz, both reading and writing are supported, but just one mode at a time + (read-only or write-only) +*/ + + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include + +#include +#include "cpl_minizip_unzip.h" +#include "cpl_time.h" + +CPL_CVSID("$Id: cpl_vsil_gzip.cpp 29033 2015-04-27 10:47:22Z rouault $"); + +#define Z_BUFSIZE 65536 /* original size is 16384 */ +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +#define CPL_VSIL_GZ_RETURN(ret) \ + CPLError(CE_Failure, CPLE_AppDefined, "In file %s, at line %d, return %d", __FILE__, __LINE__, ret) + +#define ENABLE_DEBUG 0 + +/************************************************************************/ +/* ==================================================================== */ +/* VSIGZipHandle */ +/* ==================================================================== */ +/************************************************************************/ + +typedef struct +{ + vsi_l_offset uncompressed_pos; + z_stream stream; + uLong crc; + int transparent; + vsi_l_offset in; + vsi_l_offset out; +} GZipSnapshot; + +class VSIGZipHandle : public VSIVirtualHandle +{ + VSIVirtualHandle* poBaseHandle; + vsi_l_offset offset; + vsi_l_offset compressed_size; + vsi_l_offset uncompressed_size; + vsi_l_offset offsetEndCompressedData; + unsigned int expected_crc; + char *pszBaseFileName; /* optional */ + int bCanSaveInfo; + + /* Fields from gz_stream structure */ + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file (but not necessarily of the uncompressed stream ! "in" must be null too ) */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + int transparent; /* 1 if input file is not a .gz file */ + vsi_l_offset startOff; /* startOff of compressed data in file (header skipped) */ + vsi_l_offset in; /* bytes into deflate or inflate */ + vsi_l_offset out; /* bytes out of deflate or inflate */ + vsi_l_offset nLastReadOffset; + + GZipSnapshot* snapshots; + vsi_l_offset snapshot_byte_interval; /* number of compressed bytes at which we create a "snapshot" */ + + void check_header(); + int get_byte(); + int gzseek( vsi_l_offset nOffset, int nWhence ); + int gzrewind (); + uLong getLong (); + + public: + + VSIGZipHandle(VSIVirtualHandle* poBaseHandle, + const char* pszBaseFileName, + vsi_l_offset offset = 0, + vsi_l_offset compressed_size = 0, + vsi_l_offset uncompressed_size = 0, + unsigned int expected_crc = 0, + int transparent = 0); + ~VSIGZipHandle(); + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); + + VSIGZipHandle* Duplicate(); + void CloseBaseHandle(); + + vsi_l_offset GetLastReadOffset() { return nLastReadOffset; } + const char* GetBaseFileName() { return pszBaseFileName; } + + void SetUncompressedSize(vsi_l_offset nUncompressedSize) { uncompressed_size = nUncompressedSize; } + vsi_l_offset GetUncompressedSize() { return uncompressed_size; } + + void SaveInfo_unlocked(); +}; + + +class VSIGZipFilesystemHandler : public VSIFilesystemHandler +{ + CPLMutex* hMutex; + VSIGZipHandle* poHandleLastGZipFile; + +public: + VSIGZipFilesystemHandler(); + ~VSIGZipFilesystemHandler(); + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + VSIGZipHandle *OpenGZipReadOnly( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); + virtual int Unlink( const char *pszFilename ); + virtual int Rename( const char *oldpath, const char *newpath ); + virtual int Mkdir( const char *pszDirname, long nMode ); + virtual int Rmdir( const char *pszDirname ); + virtual char **ReadDir( const char *pszDirname ); + + void SaveInfo( VSIGZipHandle* poHandle ); + void SaveInfo_unlocked( VSIGZipHandle* poHandle ); +}; + + +/************************************************************************/ +/* Duplicate() */ +/************************************************************************/ + +VSIGZipHandle* VSIGZipHandle::Duplicate() +{ + CPLAssert (offset == 0); + CPLAssert (compressed_size != 0); + CPLAssert (pszBaseFileName != NULL); + + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( pszBaseFileName ); + + VSIVirtualHandle* poNewBaseHandle = + poFSHandler->Open( pszBaseFileName, "rb" ); + + if (poNewBaseHandle == NULL) + return NULL; + + VSIGZipHandle* poHandle = new VSIGZipHandle(poNewBaseHandle, + pszBaseFileName, + 0, + compressed_size, + uncompressed_size); + + poHandle->nLastReadOffset = nLastReadOffset; + + /* Most important : duplicate the snapshots ! */ + + unsigned int i; + for(i=0;isnapshots[i].uncompressed_pos = snapshots[i].uncompressed_pos; + inflateCopy( &poHandle->snapshots[i].stream, &snapshots[i].stream); + poHandle->snapshots[i].crc = snapshots[i].crc; + poHandle->snapshots[i].transparent = snapshots[i].transparent; + poHandle->snapshots[i].in = snapshots[i].in; + poHandle->snapshots[i].out = snapshots[i].out; + } + + return poHandle; +} + +/************************************************************************/ +/* CloseBaseHandle() */ +/************************************************************************/ + +void VSIGZipHandle::CloseBaseHandle() +{ + if (poBaseHandle) + VSIFCloseL((VSILFILE*)poBaseHandle); + poBaseHandle = NULL; +} + +/************************************************************************/ +/* VSIGZipHandle() */ +/************************************************************************/ + +VSIGZipHandle::VSIGZipHandle(VSIVirtualHandle* poBaseHandle, + const char* pszBaseFileName, + vsi_l_offset offset, + vsi_l_offset compressed_size, + vsi_l_offset uncompressed_size, + unsigned int expected_crc, + int transparent) +{ + this->poBaseHandle = poBaseHandle; + this->expected_crc = expected_crc; + this->pszBaseFileName = (pszBaseFileName) ? CPLStrdup(pszBaseFileName) : NULL; + bCanSaveInfo = TRUE; + this->offset = offset; + if (compressed_size || transparent) + { + this->compressed_size = compressed_size; + } + else + { + VSIFSeekL((VSILFILE*)poBaseHandle, 0, SEEK_END); + this->compressed_size = VSIFTellL((VSILFILE*)poBaseHandle) - offset; + compressed_size = this->compressed_size; + } + this->uncompressed_size = uncompressed_size; + offsetEndCompressedData = offset + compressed_size; + + VSIFSeekL((VSILFILE*)poBaseHandle, offset, SEEK_SET); + + nLastReadOffset = 0; + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + stream.next_in = inbuf = Z_NULL; + stream.next_out = outbuf = Z_NULL; + stream.avail_in = stream.avail_out = 0; + z_err = Z_OK; + z_eof = 0; + in = 0; + out = 0; + crc = crc32(0L, Z_NULL, 0); + this->transparent = transparent; + + stream.next_in = inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + int err = inflateInit2(&(stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || inbuf == Z_NULL) { + CPLError(CE_Failure, CPLE_NotSupported, "inflateInit2 init failed"); + } + stream.avail_out = Z_BUFSIZE; + + if (offset == 0) check_header(); /* skip the .gz header */ + startOff = VSIFTellL((VSILFILE*)poBaseHandle) - stream.avail_in; + + if (transparent == 0) + { + snapshot_byte_interval = MAX(Z_BUFSIZE, compressed_size / 100); + snapshots = (GZipSnapshot*)CPLCalloc(sizeof(GZipSnapshot), (size_t) (compressed_size / snapshot_byte_interval + 1)); + } + else + { + snapshots = NULL; + } +} + +/************************************************************************/ +/* SaveInfo_unlocked() */ +/************************************************************************/ + +void VSIGZipHandle::SaveInfo_unlocked() +{ + if (pszBaseFileName && bCanSaveInfo) + { + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( "/vsigzip/" ); + ((VSIGZipFilesystemHandler*)poFSHandler)->SaveInfo_unlocked(this); + bCanSaveInfo = FALSE; + } +} + +/************************************************************************/ +/* ~VSIGZipHandle() */ +/************************************************************************/ + +VSIGZipHandle::~VSIGZipHandle() +{ + + if (pszBaseFileName && bCanSaveInfo) + { + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( "/vsigzip/" ); + ((VSIGZipFilesystemHandler*)poFSHandler)->SaveInfo(this); + } + + if (stream.state != NULL) { + inflateEnd(&(stream)); + } + + TRYFREE(inbuf); + TRYFREE(outbuf); + + if (snapshots != NULL) + { + unsigned int i; + for(i=0;i> len, (VSILFILE*)poBaseHandle); + if (ENABLE_DEBUG) CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB, + VSIFTellL((VSILFILE*)poBaseHandle), offsetEndCompressedData); + if (VSIFTellL((VSILFILE*)poBaseHandle) > offsetEndCompressedData) + { + len = len + (uInt) (offsetEndCompressedData - VSIFTellL((VSILFILE*)poBaseHandle)); + VSIFSeekL((VSILFILE*)poBaseHandle, offsetEndCompressedData, SEEK_SET); + } + if (len == 0 /* && ferror(file)*/) + { + if (VSIFTellL((VSILFILE*)poBaseHandle) != offsetEndCompressedData) + z_err = Z_ERRNO; + } + stream.avail_in += len; + stream.next_in = inbuf; + if (stream.avail_in < 2) { + transparent = stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (stream.next_in[0] != gz_magic[0] || + stream.next_in[1] != gz_magic[1]) { + transparent = 1; + return; + } + stream.avail_in -= 2; + stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(); + flags = get_byte(); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(); + len += ((uInt)get_byte())<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte() != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte()) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte()) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(); + } + z_err = z_eof ? Z_DATA_ERROR : Z_OK; +} + +/************************************************************************/ +/* get_byte() */ +/************************************************************************/ + +int VSIGZipHandle::get_byte() +{ + if (z_eof) return EOF; + if (stream.avail_in == 0) { + errno = 0; + stream.avail_in = (uInt)VSIFReadL(inbuf, 1, Z_BUFSIZE, (VSILFILE*)poBaseHandle); + if (ENABLE_DEBUG) CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB, + VSIFTellL((VSILFILE*)poBaseHandle), offsetEndCompressedData); + if (VSIFTellL((VSILFILE*)poBaseHandle) > offsetEndCompressedData) + { + stream.avail_in = stream.avail_in + (uInt) (offsetEndCompressedData - VSIFTellL((VSILFILE*)poBaseHandle)); + VSIFSeekL((VSILFILE*)poBaseHandle, offsetEndCompressedData, SEEK_SET); + } + if (stream.avail_in == 0) { + z_eof = 1; + if (VSIFTellL((VSILFILE*)poBaseHandle) != offsetEndCompressedData) + z_err = Z_ERRNO; + /*if (ferror(file)) z_err = Z_ERRNO;*/ + return EOF; + } + stream.next_in = inbuf; + } + stream.avail_in--; + return *(stream.next_in)++; +} + +/************************************************************************/ +/* gzrewind() */ +/************************************************************************/ + +int VSIGZipHandle::gzrewind () +{ + z_err = Z_OK; + z_eof = 0; + stream.avail_in = 0; + stream.next_in = inbuf; + crc = crc32(0L, Z_NULL, 0); + if (!transparent) (void)inflateReset(&stream); + in = 0; + out = 0; + return VSIFSeekL((VSILFILE*)poBaseHandle, startOff, SEEK_SET); +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIGZipHandle::Seek( vsi_l_offset nOffset, int nWhence ) +{ + /* The semantics of gzseek are different from ::Seek */ + /* It returns the current offset, where as ::Seek should return 0 */ + /* if successful */ + int ret = gzseek(nOffset, nWhence); + return (ret >= 0) ? 0 : ret; +} + +/************************************************************************/ +/* gzseek() */ +/************************************************************************/ + +int VSIGZipHandle::gzseek( vsi_l_offset offset, int whence ) +{ + vsi_l_offset original_offset = offset; + int original_nWhence = whence; + + z_eof = 0; + if (ENABLE_DEBUG) CPLDebug("GZIP", "Seek(" CPL_FRMT_GUIB ",%d)", offset, whence); + + if (transparent) + { + stream.avail_in = 0; + stream.next_in = inbuf; + if (whence == SEEK_CUR) + { + if (out + offset > compressed_size) + { + CPL_VSIL_GZ_RETURN(-1); + return -1L; + } + + offset = startOff + out + offset; + } + else if (whence == SEEK_SET) + { + if (offset > compressed_size) + { + CPL_VSIL_GZ_RETURN(-1); + return -1L; + } + + offset = startOff + offset; + } + else if (whence == SEEK_END) + { + /* Commented test : because vsi_l_offset is unsigned (for the moment) */ + /* so no way to seek backward. See #1590 */ + if (offset > 0 /*|| -offset > compressed_size*/) + { + CPL_VSIL_GZ_RETURN(-1); + return -1L; + } + + offset = startOff + compressed_size - offset; + } + else + { + CPL_VSIL_GZ_RETURN(-1); + return -1L; + } + if (VSIFSeekL((VSILFILE*)poBaseHandle, offset, SEEK_SET) < 0) + { + CPL_VSIL_GZ_RETURN(-1); + return -1L; + } + + in = out = offset - startOff; + if (ENABLE_DEBUG) CPLDebug("GZIP", "return " CPL_FRMT_GUIB, in); + return (int) in; + } + + /* whence == SEEK_END is unsuppored in original gzseek. */ + if (whence == SEEK_END) + { + /* If we known the uncompressed size, we can fake a jump to */ + /* the end of the stream */ + if (offset == 0 && uncompressed_size != 0) + { + out = uncompressed_size; + return 1; + } + + /* We don't know the uncompressed size. This is unfortunate. Let's do the slow version... */ + static int firstWarning = 1; + if (compressed_size > 10 * 1024 * 1024 && firstWarning) + { + CPLError(CE_Warning, CPLE_AppDefined, + "VSIFSeekL(xxx, SEEK_END) may be really slow on GZip streams."); + firstWarning = 0; + } + + whence = SEEK_CUR; + offset = 1024 * 1024 * 1024; + offset *= 1024 * 1024; + } + + if (/*whence == SEEK_END ||*/ + z_err == Z_ERRNO || z_err == Z_DATA_ERROR) { + CPL_VSIL_GZ_RETURN(-1); + return -1L; + } + + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += out; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= out) { + offset -= out; + } else if (gzrewind() < 0) { + CPL_VSIL_GZ_RETURN(-1); + return -1L; + } + + unsigned int i; + for(i=0;i out+offset)) + { + if (out >= snapshots[i].out) + break; + + if (ENABLE_DEBUG) + CPLDebug("SNAPSHOT", "using snapshot %d : uncompressed_pos(snapshot)=" CPL_FRMT_GUIB + " in(snapshot)=" CPL_FRMT_GUIB + " out(snapshot)=" CPL_FRMT_GUIB + " out=" CPL_FRMT_GUIB + " offset=" CPL_FRMT_GUIB, + i, snapshots[i].uncompressed_pos, snapshots[i].in, snapshots[i].out, out, offset); + offset = out + offset - snapshots[i].out; + VSIFSeekL((VSILFILE*)poBaseHandle, snapshots[i].uncompressed_pos, SEEK_SET); + inflateEnd(&stream); + inflateCopy(&stream, &snapshots[i].stream); + crc = snapshots[i].crc; + transparent = snapshots[i].transparent; + in = snapshots[i].in; + out = snapshots[i].out; + break; + } + } + + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && outbuf == Z_NULL) { + outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (outbuf == Z_NULL) { + CPL_VSIL_GZ_RETURN(-1); + return -1L; + } + } + + if (original_nWhence == SEEK_END && z_err == Z_STREAM_END) + { + if (ENABLE_DEBUG) CPLDebug("GZIP", "gzseek return " CPL_FRMT_GUIB, out); + return (int) out; + } + + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + int read_size = Read(outbuf, 1, (uInt)size); + if (read_size == 0) { + //CPL_VSIL_GZ_RETURN(-1); + return -1L; + } + if (original_nWhence == SEEK_END) + { + if (size != read_size) + { + z_err = Z_STREAM_END; + break; + } + } + offset -= read_size; + } + if (ENABLE_DEBUG) CPLDebug("GZIP", "gzseek return " CPL_FRMT_GUIB, out); + + if (original_offset == 0 && original_nWhence == SEEK_END) + { + uncompressed_size = out; + + if (pszBaseFileName) + { + CPLString osCacheFilename (pszBaseFileName); + osCacheFilename += ".properties"; + + /* Write a .properties file to avoid seeking next time */ + VSILFILE* fpCacheLength = VSIFOpenL(osCacheFilename.c_str(), "wb"); + if (fpCacheLength) + { + char* pszFirstNonSpace; + char szBuffer[32]; + szBuffer[31] = 0; + + CPLPrintUIntBig(szBuffer, compressed_size, 31); + pszFirstNonSpace = szBuffer; + while (*pszFirstNonSpace == ' ') pszFirstNonSpace ++; + VSIFPrintfL(fpCacheLength, "compressed_size=%s\n", pszFirstNonSpace); + + CPLPrintUIntBig(szBuffer, uncompressed_size, 31); + pszFirstNonSpace = szBuffer; + while (*pszFirstNonSpace == ' ') pszFirstNonSpace ++; + VSIFPrintfL(fpCacheLength, "uncompressed_size=%s\n", pszFirstNonSpace); + + VSIFCloseL(fpCacheLength); + } + } + } + + return (int) out; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIGZipHandle::Tell() +{ + if (ENABLE_DEBUG) CPLDebug("GZIP", "Tell() = " CPL_FRMT_GUIB, out); + return out; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIGZipHandle::Read( void *buf, size_t nSize, size_t nMemb ) +{ + if (ENABLE_DEBUG) CPLDebug("GZIP", "Read(%p, %d, %d)", buf, (int)nSize, (int)nMemb); + + unsigned len = nSize * nMemb; + + Bytef *pStart = (Bytef*)buf; /* startOffing point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (z_err == Z_DATA_ERROR || z_err == Z_ERRNO) + { + z_eof = 1; /* to avoid infinite loop in reader code */ + in = 0; + CPL_VSIL_GZ_RETURN(0); + return 0; + } + if ((z_eof && in == 0) || z_err == Z_STREAM_END) + { + z_eof = 1; + in = 0; + if (ENABLE_DEBUG) CPLDebug("GZIP", "Read: Eof"); + return 0; /* EOF */ + } + + next_out = (Byte*)buf; + stream.next_out = (Bytef*)buf; + stream.avail_out = len; + + while (stream.avail_out != 0) { + + if (transparent) { + /* Copy first the lookahead bytes: */ + uInt nRead = 0; + uInt n = stream.avail_in; + if (n > stream.avail_out) n = stream.avail_out; + if (n > 0) { + memcpy (stream.next_out, stream.next_in, n); + next_out += n; + stream.next_out = next_out; + stream.next_in += n; + stream.avail_out -= n; + stream.avail_in -= n; + nRead += n; + } + if (stream.avail_out > 0) { + uInt nToRead = (uInt) MIN(compressed_size - (in + nRead), stream.avail_out); + uInt nReadFromFile = + (uInt)VSIFReadL(next_out, 1, nToRead, (VSILFILE*)poBaseHandle); + stream.avail_out -= nReadFromFile; + nRead += nReadFromFile; + } + in += nRead; + out += nRead; + if (nRead < len) z_eof = 1; + if (ENABLE_DEBUG) CPLDebug("GZIP", "Read return %d", (int)(nRead / nSize)); + return (int)nRead / nSize; + } + if (stream.avail_in == 0 && !z_eof) + { + vsi_l_offset uncompressed_pos = VSIFTellL((VSILFILE*)poBaseHandle); + GZipSnapshot* snapshot = &snapshots[(uncompressed_pos - startOff) / snapshot_byte_interval]; + if (snapshot->uncompressed_pos == 0) + { + snapshot->crc = crc32 (crc, pStart, (uInt) (stream.next_out - pStart)); + if (ENABLE_DEBUG) + CPLDebug("SNAPSHOT", + "creating snapshot %d : uncompressed_pos=" CPL_FRMT_GUIB + " in=" CPL_FRMT_GUIB + " out=" CPL_FRMT_GUIB + " crc=%X", + (int)((uncompressed_pos - startOff) / snapshot_byte_interval), + uncompressed_pos, in, out, (unsigned int)snapshot->crc); + snapshot->uncompressed_pos = uncompressed_pos; + inflateCopy(&snapshot->stream, &stream); + snapshot->transparent = transparent; + snapshot->in = in; + snapshot->out = out; + + if (out > nLastReadOffset) + nLastReadOffset = out; + } + + errno = 0; + stream.avail_in = (uInt)VSIFReadL(inbuf, 1, Z_BUFSIZE, (VSILFILE*)poBaseHandle); + if (ENABLE_DEBUG) + CPLDebug("GZIP", CPL_FRMT_GUIB " " CPL_FRMT_GUIB, + VSIFTellL((VSILFILE*)poBaseHandle), offsetEndCompressedData); + if (VSIFTellL((VSILFILE*)poBaseHandle) > offsetEndCompressedData) + { + if (ENABLE_DEBUG) CPLDebug("GZIP", "avail_in before = %d", stream.avail_in); + stream.avail_in = stream.avail_in + (uInt) (offsetEndCompressedData - VSIFTellL((VSILFILE*)poBaseHandle)); + VSIFSeekL((VSILFILE*)poBaseHandle, offsetEndCompressedData, SEEK_SET); + if (ENABLE_DEBUG) CPLDebug("GZIP", "avail_in after = %d", stream.avail_in); + } + if (stream.avail_in == 0) { + z_eof = 1; + if (VSIFTellL((VSILFILE*)poBaseHandle) != offsetEndCompressedData) + { + z_err = Z_ERRNO; + break; + } + /*if (ferror (file)) { + z_err = Z_ERRNO; + break; + }*/ + } + stream.next_in = inbuf; + } + in += stream.avail_in; + out += stream.avail_out; + z_err = inflate(& (stream), Z_NO_FLUSH); + in -= stream.avail_in; + out -= stream.avail_out; + + if (z_err == Z_STREAM_END && compressed_size != 2 ) { + /* Check CRC and original size */ + crc = crc32 (crc, pStart, (uInt) (stream.next_out - pStart)); + pStart = stream.next_out; + if (expected_crc) + { + if (ENABLE_DEBUG) CPLDebug("GZIP", "Computed CRC = %X. Expected CRC = %X", (unsigned int)crc, expected_crc); + } + if (expected_crc != 0 && expected_crc != crc) + { + CPLError(CE_Failure, CPLE_FileIO, "CRC error. Got %X instead of %X", (unsigned int)crc, expected_crc); + z_err = Z_DATA_ERROR; + } + else if (expected_crc == 0) + { + unsigned int read_crc = getLong(); + if (read_crc != crc) + { + CPLError(CE_Failure, CPLE_FileIO, "CRC error. Got %X instead of %X", (unsigned int)crc, read_crc); + z_err = Z_DATA_ERROR; + } + else + { + (void)getLong(); + /* The uncompressed length returned by above getlong() may be + * different from out in case of concatenated .gz files. + * Check for such files: + */ + check_header(); + if (z_err == Z_OK) { + inflateReset(& (stream)); + crc = crc32(0L, Z_NULL, 0); + } + } + } + } + if (z_err != Z_OK || z_eof) break; + } + crc = crc32 (crc, pStart, (uInt) (stream.next_out - pStart)); + + if (len == stream.avail_out && + (z_err == Z_DATA_ERROR || z_err == Z_ERRNO)) + { + z_eof = 1; + in = 0; + CPL_VSIL_GZ_RETURN(0); + return 0; + } + if (ENABLE_DEBUG) + CPLDebug("GZIP", "Read return %d (z_err=%d, z_eof=%d)", + (int)((len - stream.avail_out) / nSize), z_err, z_eof); + return (int)(len - stream.avail_out) / nSize; +} + +/************************************************************************/ +/* getLong() */ +/************************************************************************/ + +uLong VSIGZipHandle::getLong () +{ + uLong x = (uLong)get_byte(); + int c; + + x += ((uLong)get_byte())<<8; + x += ((uLong)get_byte())<<16; + c = get_byte(); + if (c == EOF) z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIGZipHandle::Write( CPL_UNUSED const void *pBuffer, + CPL_UNUSED size_t nSize, + CPL_UNUSED size_t nMemb ) +{ + CPLError(CE_Failure, CPLE_NotSupported, "VSIFWriteL is not supported on GZip streams"); + return 0; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + + +int VSIGZipHandle::Eof() +{ + if (ENABLE_DEBUG) CPLDebug("GZIP", "Eof()"); + return z_eof && in == 0; +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSIGZipHandle::Flush() +{ + return 0; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIGZipHandle::Close() +{ + return 0; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* VSIGZipWriteHandle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIGZipWriteHandle : public VSIVirtualHandle +{ + VSIVirtualHandle* poBaseHandle; + z_stream sStream; + Byte *pabyInBuf; + Byte *pabyOutBuf; + bool bCompressActive; + vsi_l_offset nCurOffset; + GUInt32 nCRC; + int bRegularZLib; + int bAutoCloseBaseHandle; + + public: + + VSIGZipWriteHandle(VSIVirtualHandle* poBaseHandle, int bRegularZLib, int bAutoCloseBaseHandleIn); + + ~VSIGZipWriteHandle(); + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); +}; + +/************************************************************************/ +/* VSIGZipWriteHandle() */ +/************************************************************************/ + +VSIGZipWriteHandle::VSIGZipWriteHandle( VSIVirtualHandle *poBaseHandle, + int bRegularZLibIn, + int bAutoCloseBaseHandleIn ) + +{ + nCurOffset = 0; + + this->poBaseHandle = poBaseHandle; + bRegularZLib = bRegularZLibIn; + bAutoCloseBaseHandle = bAutoCloseBaseHandleIn; + + nCRC = crc32(0L, Z_NULL, 0); + sStream.zalloc = (alloc_func)0; + sStream.zfree = (free_func)0; + sStream.opaque = (voidpf)0; + sStream.next_in = Z_NULL; + sStream.next_out = Z_NULL; + sStream.avail_in = sStream.avail_out = 0; + + pabyInBuf = (Byte *) CPLMalloc( Z_BUFSIZE ); + sStream.next_in = pabyInBuf; + + pabyOutBuf = (Byte *) CPLMalloc( Z_BUFSIZE ); + + if( deflateInit2( &sStream, Z_DEFAULT_COMPRESSION, + Z_DEFLATED, (bRegularZLib) ? MAX_WBITS : -MAX_WBITS, 8, + Z_DEFAULT_STRATEGY ) != Z_OK ) + bCompressActive = false; + else + { + if (!bRegularZLib) + { + char header[11]; + + /* Write a very simple .gz header: + */ + sprintf( header, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, + 0x03 ); + poBaseHandle->Write( header, 1, 10 ); + } + + bCompressActive = true; + } +} + +/************************************************************************/ +/* VSICreateGZipWritable() */ +/************************************************************************/ + +VSIVirtualHandle* VSICreateGZipWritable( VSIVirtualHandle* poBaseHandle, + int bRegularZLibIn, + int bAutoCloseBaseHandle ) +{ + return new VSIGZipWriteHandle( poBaseHandle, bRegularZLibIn, bAutoCloseBaseHandle ); +} + +/************************************************************************/ +/* ~VSIGZipWriteHandle() */ +/************************************************************************/ + +VSIGZipWriteHandle::~VSIGZipWriteHandle() + +{ + if( bCompressActive ) + Close(); + + CPLFree( pabyInBuf ); + CPLFree( pabyOutBuf ); +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIGZipWriteHandle::Close() + +{ + if( bCompressActive ) + { + sStream.next_out = pabyOutBuf; + sStream.avail_out = Z_BUFSIZE; + + deflate( &sStream, Z_FINISH ); + + size_t nOutBytes = Z_BUFSIZE - sStream.avail_out; + + if( poBaseHandle->Write( pabyOutBuf, 1, nOutBytes ) < nOutBytes ) + return EOF; + + deflateEnd( &sStream ); + + if( !bRegularZLib ) + { + GUInt32 anTrailer[2]; + + anTrailer[0] = CPL_LSBWORD32( nCRC ); + anTrailer[1] = CPL_LSBWORD32( (GUInt32) nCurOffset ); + + poBaseHandle->Write( anTrailer, 1, 8 ); + } + + if( bAutoCloseBaseHandle ) + { + poBaseHandle->Close(); + + delete poBaseHandle; + } + + bCompressActive = false; + } + + return 0; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIGZipWriteHandle::Read( CPL_UNUSED void *pBuffer, + CPL_UNUSED size_t nSize, + CPL_UNUSED size_t nMemb ) +{ + CPLError(CE_Failure, CPLE_NotSupported, "VSIFReadL is not supported on GZip write streams\n"); + return 0; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIGZipWriteHandle::Write( const void *pBuffer, + size_t nSize, size_t nMemb ) + +{ + int nBytesToWrite, nNextByte; + + nBytesToWrite = (int) (nSize * nMemb); + nNextByte = 0; + + nCRC = crc32(nCRC, (const Bytef *)pBuffer, nBytesToWrite); + + if( !bCompressActive ) + return 0; + + while( nNextByte < nBytesToWrite ) + { + sStream.next_out = pabyOutBuf; + sStream.avail_out = Z_BUFSIZE; + + if( sStream.avail_in > 0 ) + memmove( pabyInBuf, sStream.next_in, sStream.avail_in ); + + int nNewBytesToWrite = MIN((int) (Z_BUFSIZE-sStream.avail_in), + nBytesToWrite - nNextByte); + memcpy( pabyInBuf + sStream.avail_in, + ((Byte *) pBuffer) + nNextByte, + nNewBytesToWrite ); + + sStream.next_in = pabyInBuf; + sStream.avail_in += nNewBytesToWrite; + + deflate( &sStream, Z_NO_FLUSH ); + + size_t nOutBytes = Z_BUFSIZE - sStream.avail_out; + + if( nOutBytes > 0 ) + { + if( poBaseHandle->Write( pabyOutBuf, 1, nOutBytes ) < nOutBytes ) + return 0; + } + + nNextByte += nNewBytesToWrite; + nCurOffset += nNewBytesToWrite; + } + + return nMemb; +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSIGZipWriteHandle::Flush() + +{ + // we *could* do something for this but for now we choose not to. + + return 0; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSIGZipWriteHandle::Eof() + +{ + return 1; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIGZipWriteHandle::Seek( vsi_l_offset nOffset, int nWhence ) + +{ + if( nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR) ) + return 0; + else if( nWhence == SEEK_SET && nOffset == nCurOffset ) + return 0; + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "Seeking on writable compressed data streams not supported." ); + + return -1; + } +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIGZipWriteHandle::Tell() + +{ + return nCurOffset; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* VSIGZipFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + + +/************************************************************************/ +/* VSIGZipFilesystemHandler() */ +/************************************************************************/ + +VSIGZipFilesystemHandler::VSIGZipFilesystemHandler() +{ + hMutex = NULL; + + poHandleLastGZipFile = NULL; +} + +/************************************************************************/ +/* ~VSIGZipFilesystemHandler() */ +/************************************************************************/ + +VSIGZipFilesystemHandler::~VSIGZipFilesystemHandler() +{ + if (poHandleLastGZipFile) + delete poHandleLastGZipFile; + + if( hMutex != NULL ) + CPLDestroyMutex( hMutex ); + hMutex = NULL; +} + +/************************************************************************/ +/* SaveInfo() */ +/************************************************************************/ + +void VSIGZipFilesystemHandler::SaveInfo( VSIGZipHandle* poHandle ) +{ + CPLMutexHolder oHolder(&hMutex); + SaveInfo_unlocked(poHandle); +} + +void VSIGZipFilesystemHandler::SaveInfo_unlocked( VSIGZipHandle* poHandle ) +{ + CPLAssert(poHandle->GetBaseFileName() != NULL); + + if (poHandleLastGZipFile && + strcmp(poHandleLastGZipFile->GetBaseFileName(), poHandle->GetBaseFileName()) == 0) + { + if (poHandle->GetLastReadOffset() > poHandleLastGZipFile->GetLastReadOffset()) + { + VSIGZipHandle* poTmp = poHandleLastGZipFile; + poHandleLastGZipFile = NULL; + poTmp->SaveInfo_unlocked(); + delete poTmp; + poHandleLastGZipFile = poHandle->Duplicate(); + if( poHandleLastGZipFile ) + poHandleLastGZipFile->CloseBaseHandle(); + } + } + else + { + VSIGZipHandle* poTmp = poHandleLastGZipFile; + poHandleLastGZipFile = NULL; + if( poTmp ) + { + poTmp->SaveInfo_unlocked(); + delete poTmp; + } + poHandleLastGZipFile = poHandle->Duplicate(); + if( poHandleLastGZipFile ) + poHandleLastGZipFile->CloseBaseHandle(); + } +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle* VSIGZipFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess) +{ + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( pszFilename + strlen("/vsigzip/")); + +/* -------------------------------------------------------------------- */ +/* Is this an attempt to write a new file without update (w+) */ +/* access? If so, create a writable handle for the underlying */ +/* filename. */ +/* -------------------------------------------------------------------- */ + if (strchr(pszAccess, 'w') != NULL ) + { + if( strchr(pszAccess, '+') != NULL ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Write+update (w+) not supported for /vsigzip, only read-only or write-only."); + return NULL; + } + + VSIVirtualHandle* poVirtualHandle = + poFSHandler->Open( pszFilename + strlen("/vsigzip/"), "wb" ); + + if (poVirtualHandle == NULL) + return NULL; + + else + return new VSIGZipWriteHandle( poVirtualHandle, strchr(pszAccess, 'z') != NULL, TRUE ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise we are in the read access case. */ +/* -------------------------------------------------------------------- */ + + VSIGZipHandle* poGZIPHandle = OpenGZipReadOnly(pszFilename, pszAccess); + if (poGZIPHandle) + /* Wrap the VSIGZipHandle inside a buffered reader that will */ + /* improve dramatically performance when doing small backward */ + /* seeks */ + return VSICreateBufferedReaderHandle(poGZIPHandle); + else + return NULL; +} + +/************************************************************************/ +/* OpenGZipReadOnly() */ +/************************************************************************/ + +VSIGZipHandle* VSIGZipFilesystemHandler::OpenGZipReadOnly( const char *pszFilename, + const char *pszAccess) +{ + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( pszFilename + strlen("/vsigzip/")); + + CPLMutexHolder oHolder(&hMutex); + + if (poHandleLastGZipFile != NULL && + strcmp(pszFilename + strlen("/vsigzip/"), poHandleLastGZipFile->GetBaseFileName()) == 0 && + EQUAL(pszAccess, "rb")) + { + VSIGZipHandle* poHandle = poHandleLastGZipFile->Duplicate(); + if (poHandle) + return poHandle; + } + + unsigned char signature[2]; + + VSIVirtualHandle* poVirtualHandle = + poFSHandler->Open( pszFilename + strlen("/vsigzip/"), "rb" ); + + if (poVirtualHandle == NULL) + return NULL; + + if (VSIFReadL(signature, 1, 2, (VSILFILE*)poVirtualHandle) != 2 || + signature[0] != gz_magic[0] || signature[1] != gz_magic[1]) + { + delete poVirtualHandle; + return NULL; + } + + if (poHandleLastGZipFile) + { + poHandleLastGZipFile->SaveInfo_unlocked(); + delete poHandleLastGZipFile; + poHandleLastGZipFile = NULL; + } + + return new VSIGZipHandle(poVirtualHandle, pszFilename + strlen("/vsigzip/")); +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSIGZipFilesystemHandler::Stat( const char *pszFilename, + VSIStatBufL *pStatBuf, + int nFlags ) +{ + CPLMutexHolder oHolder(&hMutex); + + memset(pStatBuf, 0, sizeof(VSIStatBufL)); + + if (poHandleLastGZipFile != NULL && + strcmp(pszFilename+strlen("/vsigzip/"), poHandleLastGZipFile->GetBaseFileName()) == 0) + { + if (poHandleLastGZipFile->GetUncompressedSize() != 0) + { + pStatBuf->st_mode = S_IFREG; + pStatBuf->st_size = poHandleLastGZipFile->GetUncompressedSize(); + return 0; + } + } + + /* Begin by doing a stat on the real file */ + int ret = VSIStatExL(pszFilename+strlen("/vsigzip/"), pStatBuf, nFlags); + + if (ret == 0 && (nFlags & VSI_STAT_SIZE_FLAG)) + { + CPLString osCacheFilename(pszFilename+strlen("/vsigzip/")); + osCacheFilename += ".properties"; + + /* Can we save a bit of seeking by using a .properties file ? */ + VSILFILE* fpCacheLength = VSIFOpenL(osCacheFilename.c_str(), "rb"); + if (fpCacheLength) + { + const char* pszLine; + GUIntBig nCompressedSize = 0; + GUIntBig nUncompressedSize = 0; + while ((pszLine = CPLReadLineL(fpCacheLength)) != NULL) + { + if (EQUALN(pszLine, "compressed_size=", strlen("compressed_size="))) + { + const char* pszBuffer = pszLine + strlen("compressed_size="); + nCompressedSize = + CPLScanUIntBig(pszBuffer, strlen(pszBuffer)); + } + else if (EQUALN(pszLine, "uncompressed_size=", strlen("uncompressed_size="))) + { + const char* pszBuffer = pszLine + strlen("uncompressed_size="); + nUncompressedSize = + CPLScanUIntBig(pszBuffer, strlen(pszBuffer)); + } + } + + VSIFCloseL(fpCacheLength); + + if (nCompressedSize == (GUIntBig) pStatBuf->st_size) + { + /* Patch with the uncompressed size */ + pStatBuf->st_size = nUncompressedSize; + + VSIGZipHandle* poHandle = + VSIGZipFilesystemHandler::OpenGZipReadOnly(pszFilename, "rb"); + if (poHandle) + { + poHandle->SetUncompressedSize(nUncompressedSize); + SaveInfo_unlocked(poHandle); + delete poHandle; + } + + return ret; + } + } + + /* No, then seek at the end of the data (slow) */ + VSIGZipHandle* poHandle = + VSIGZipFilesystemHandler::OpenGZipReadOnly(pszFilename, "rb"); + if (poHandle) + { + GUIntBig uncompressed_size; + poHandle->Seek(0, SEEK_END); + uncompressed_size = (GUIntBig) poHandle->Tell(); + poHandle->Seek(0, SEEK_SET); + + /* Patch with the uncompressed size */ + pStatBuf->st_size = uncompressed_size; + + delete poHandle; + } + else + { + ret = -1; + } + } + + return ret; +} + +/************************************************************************/ +/* Unlink() */ +/************************************************************************/ + +int VSIGZipFilesystemHandler::Unlink( CPL_UNUSED const char *pszFilename ) +{ + return -1; +} + +/************************************************************************/ +/* Rename() */ +/************************************************************************/ + +int VSIGZipFilesystemHandler::Rename( CPL_UNUSED const char *oldpath, + CPL_UNUSED const char *newpath ) +{ + return -1; +} + +/************************************************************************/ +/* Mkdir() */ +/************************************************************************/ + +int VSIGZipFilesystemHandler::Mkdir( CPL_UNUSED const char *pszDirname, + CPL_UNUSED long nMode ) +{ + return -1; +} +/************************************************************************/ +/* Rmdir() */ +/************************************************************************/ + +int VSIGZipFilesystemHandler::Rmdir( CPL_UNUSED const char *pszDirname ) +{ + return -1; +} + +/************************************************************************/ +/* ReadDir() */ +/************************************************************************/ + +char** VSIGZipFilesystemHandler::ReadDir( CPL_UNUSED const char *pszDirname ) +{ + return NULL; +} + +/************************************************************************/ +/* VSIInstallGZipFileHandler() */ +/************************************************************************/ + + +/** + * \brief Install GZip file system handler. + * + * A special file handler is installed that allows reading on-the-fly and + * writing in GZip (.gz) files. + * + * All portions of the file system underneath the base + * path "/vsigzip/" will be handled by this driver. + * + * Additional documentation is to be found at http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip + * + * @since GDAL 1.6.0 + */ + +void VSIInstallGZipFileHandler(void) +{ + VSIFileManager::InstallHandler( "/vsigzip/", new VSIGZipFilesystemHandler ); +} + + +/************************************************************************/ +/* ==================================================================== */ +/* VSIZipEntryFileOffset */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIZipEntryFileOffset : public VSIArchiveEntryFileOffset +{ +public: + unz_file_pos file_pos; + + VSIZipEntryFileOffset(unz_file_pos file_pos) + { + this->file_pos.pos_in_zip_directory = file_pos.pos_in_zip_directory; + this->file_pos.num_of_file = file_pos.num_of_file; + } +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIZipReader */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIZipReader : public VSIArchiveReader +{ + private: + unzFile unzF; + unz_file_pos file_pos; + GUIntBig nNextFileSize; + CPLString osNextFileName; + GIntBig nModifiedTime; + + void SetInfo(); + + public: + VSIZipReader(const char* pszZipFileName); + virtual ~VSIZipReader(); + + int IsValid() { return unzF != NULL; } + + unzFile GetUnzFileHandle() { return unzF; } + + virtual int GotoFirstFile(); + virtual int GotoNextFile(); + virtual VSIArchiveEntryFileOffset* GetFileOffset() { return new VSIZipEntryFileOffset(file_pos); } + virtual GUIntBig GetFileSize() { return nNextFileSize; } + virtual CPLString GetFileName() { return osNextFileName; } + virtual GIntBig GetModifiedTime() { return nModifiedTime; } + virtual int GotoFileOffset(VSIArchiveEntryFileOffset* pOffset); +}; + + +/************************************************************************/ +/* VSIZipReader() */ +/************************************************************************/ + +VSIZipReader::VSIZipReader(const char* pszZipFileName) +{ + unzF = cpl_unzOpen(pszZipFileName); + nNextFileSize = 0; + nModifiedTime = 0; +} + +/************************************************************************/ +/* ~VSIZipReader() */ +/************************************************************************/ + +VSIZipReader::~VSIZipReader() +{ + if (unzF) + cpl_unzClose(unzF); +} + +/************************************************************************/ +/* SetInfo() */ +/************************************************************************/ + +void VSIZipReader::SetInfo() +{ + char fileName[8193]; + unz_file_info file_info; + cpl_unzGetCurrentFileInfo (unzF, &file_info, fileName, sizeof(fileName) - 1, NULL, 0, NULL, 0); + fileName[sizeof(fileName) - 1] = '\0'; + osNextFileName = fileName; + nNextFileSize = file_info.uncompressed_size; + struct tm brokendowntime; + brokendowntime.tm_sec = file_info.tmu_date.tm_sec; + brokendowntime.tm_min = file_info.tmu_date.tm_min; + brokendowntime.tm_hour = file_info.tmu_date.tm_hour; + brokendowntime.tm_mday = file_info.tmu_date.tm_mday; + brokendowntime.tm_mon = file_info.tmu_date.tm_mon; + brokendowntime.tm_year = file_info.tmu_date.tm_year - 1900; /* the minizip conventions differs from the Unix one */ + nModifiedTime = CPLYMDHMSToUnixTime(&brokendowntime); + + cpl_unzGetFilePos(unzF, &this->file_pos); +} + +/************************************************************************/ +/* GotoNextFile() */ +/************************************************************************/ + +int VSIZipReader::GotoNextFile() +{ + if (cpl_unzGoToNextFile(unzF) != UNZ_OK) + return FALSE; + + SetInfo(); + + return TRUE; +} + +/************************************************************************/ +/* GotoFirstFile() */ +/************************************************************************/ + +int VSIZipReader::GotoFirstFile() +{ + if (cpl_unzGoToFirstFile(unzF) != UNZ_OK) + return FALSE; + + SetInfo(); + + return TRUE; +} + +/************************************************************************/ +/* GotoFileOffset() */ +/************************************************************************/ + +int VSIZipReader::GotoFileOffset(VSIArchiveEntryFileOffset* pOffset) +{ + VSIZipEntryFileOffset* pZipEntryOffset = (VSIZipEntryFileOffset*)pOffset; + if( cpl_unzGoToFilePos(unzF, &(pZipEntryOffset->file_pos)) != UNZ_OK ) + { + CPLError(CE_Failure, CPLE_AppDefined, "GotoFileOffset failed"); + return FALSE; + } + + SetInfo(); + + return TRUE; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSIZipFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIZipWriteHandle; + +class VSIZipFilesystemHandler : public VSIArchiveFilesystemHandler +{ + std::map oMapZipWriteHandles; + VSIVirtualHandle *OpenForWrite_unlocked( const char *pszFilename, + const char *pszAccess ); + +public: + virtual ~VSIZipFilesystemHandler(); + + virtual const char* GetPrefix() { return "/vsizip"; } + virtual std::vector GetExtensions(); + virtual VSIArchiveReader* CreateReader(const char* pszZipFileName); + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + + virtual VSIVirtualHandle *OpenForWrite( const char *pszFilename, + const char *pszAccess ); + + virtual int Mkdir( const char *pszDirname, long nMode ); + virtual char **ReadDir( const char *pszDirname ); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); + + void RemoveFromMap(VSIZipWriteHandle* poHandle); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIZipWriteHandle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIZipWriteHandle : public VSIVirtualHandle +{ + VSIZipFilesystemHandler *poFS; + void *hZIP; + VSIZipWriteHandle *poChildInWriting; + VSIZipWriteHandle *poParent; + int bAutoDeleteParent; + vsi_l_offset nCurOffset; + + public: + + VSIZipWriteHandle(VSIZipFilesystemHandler* poFS, + void *hZIP, + VSIZipWriteHandle* poParent); + + ~VSIZipWriteHandle(); + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); + + void StartNewFile(VSIZipWriteHandle* poSubFile); + void StopCurrentFile(); + void* GetHandle() { return hZIP; } + VSIZipWriteHandle* GetChildInWriting() { return poChildInWriting; }; + void SetAutoDeleteParent() { bAutoDeleteParent = TRUE; } +}; + +/************************************************************************/ +/* ~VSIZipFilesystemHandler() */ +/************************************************************************/ + +VSIZipFilesystemHandler::~VSIZipFilesystemHandler() +{ + std::map::const_iterator iter; + + for( iter = oMapZipWriteHandles.begin(); iter != oMapZipWriteHandles.end(); ++iter ) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s has not been closed", + iter->first.c_str()); + } +} + +/************************************************************************/ +/* GetExtensions() */ +/************************************************************************/ + +std::vector VSIZipFilesystemHandler::GetExtensions() +{ + std::vector oList; + oList.push_back(".zip"); + oList.push_back(".kmz"); + oList.push_back(".dwf"); + oList.push_back(".ods"); + oList.push_back(".xlsx"); + + /* Add to zip FS handler extensions array additional extensions */ + /* listed in CPL_VSIL_ZIP_ALLOWED_EXTENSIONS config option. */ + /* The extensions divided by comma */ + const char* pszAllowedExtensions = + CPLGetConfigOption("CPL_VSIL_ZIP_ALLOWED_EXTENSIONS", NULL); + if (pszAllowedExtensions) + { + char** papszExtensions = CSLTokenizeString2(pszAllowedExtensions, ", ", 0); + for (int i = 0; papszExtensions[i] != NULL; i++) + { + oList.push_back(papszExtensions[i]); + } + CSLDestroy(papszExtensions); + } + + return oList; +} + +/************************************************************************/ +/* CreateReader() */ +/************************************************************************/ + +VSIArchiveReader* VSIZipFilesystemHandler::CreateReader(const char* pszZipFileName) +{ + VSIZipReader* poReader = new VSIZipReader(pszZipFileName); + + if (!poReader->IsValid()) + { + delete poReader; + return NULL; + } + + if (!poReader->GotoFirstFile()) + { + delete poReader; + return NULL; + } + + return poReader; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle* VSIZipFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess) +{ + char* zipFilename; + CPLString osZipInFileName; + + if (strchr(pszAccess, 'w') != NULL) + { + return OpenForWrite(pszFilename, pszAccess); + } + + if (strchr(pszAccess, '+') != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Random access not supported for /vsizip"); + return NULL; + } + + zipFilename = SplitFilename(pszFilename, osZipInFileName, TRUE); + if (zipFilename == NULL) + return NULL; + + { + CPLMutexHolder oHolder(&hMutex); + if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read a zip file being written"); + CPLFree(zipFilename); + return NULL; + } + } + + VSIArchiveReader* poReader = OpenArchiveFile(zipFilename, osZipInFileName); + if (poReader == NULL) + { + CPLFree(zipFilename); + return NULL; + } + + VSIFilesystemHandler *poFSHandler = + VSIFileManager::GetHandler( zipFilename); + + VSIVirtualHandle* poVirtualHandle = + poFSHandler->Open( zipFilename, "rb" ); + + CPLFree(zipFilename); + zipFilename = NULL; + + if (poVirtualHandle == NULL) + { + delete poReader; + return NULL; + } + + unzFile unzF = ((VSIZipReader*)poReader)->GetUnzFileHandle(); + + if( cpl_unzOpenCurrentFile(unzF) != UNZ_OK ) + { + CPLError(CE_Failure, CPLE_AppDefined, "cpl_unzOpenCurrentFile() failed"); + delete poReader; + return NULL; + } + + uLong64 pos = cpl_unzGetCurrentFileZStreamPos(unzF); + + unz_file_info file_info; + if( cpl_unzGetCurrentFileInfo (unzF, &file_info, NULL, 0, NULL, 0, NULL, 0) != UNZ_OK ) + { + CPLError(CE_Failure, CPLE_AppDefined, "cpl_unzGetCurrentFileInfo() failed"); + cpl_unzCloseCurrentFile(unzF); + delete poReader; + return NULL; + } + + cpl_unzCloseCurrentFile(unzF); + + delete poReader; + + VSIGZipHandle* poGZIPHandle = new VSIGZipHandle(poVirtualHandle, + NULL, + pos, + file_info.compressed_size, + file_info.uncompressed_size, + file_info.crc, + file_info.compression_method == 0); + /* Wrap the VSIGZipHandle inside a buffered reader that will */ + /* improve dramatically performance when doing small backward */ + /* seeks */ + return VSICreateBufferedReaderHandle(poGZIPHandle); +} + +/************************************************************************/ +/* Mkdir() */ +/************************************************************************/ + +int VSIZipFilesystemHandler::Mkdir( const char *pszDirname, CPL_UNUSED long nMode ) +{ + CPLString osDirname = pszDirname; + if (osDirname.size() != 0 && osDirname[osDirname.size() - 1] != '/') + osDirname += "/"; + VSIVirtualHandle* poZIPHandle = OpenForWrite(osDirname, "wb"); + if (poZIPHandle == NULL) + return -1; + delete poZIPHandle; + return 0; +} + +/************************************************************************/ +/* ReadDir() */ +/************************************************************************/ + +char **VSIZipFilesystemHandler::ReadDir( const char *pszDirname ) +{ + CPLString osInArchiveSubDir; + char* zipFilename = SplitFilename(pszDirname, osInArchiveSubDir, TRUE); + if (zipFilename == NULL) + return NULL; + + { + CPLMutexHolder oHolder(&hMutex); + + if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read a zip file being written"); + CPLFree(zipFilename); + return NULL; + } + } + CPLFree(zipFilename); + + return VSIArchiveFilesystemHandler::ReadDir(pszDirname); +} + + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSIZipFilesystemHandler::Stat( const char *pszFilename, + VSIStatBufL *pStatBuf, int nFlags ) +{ + CPLString osInArchiveSubDir; + + memset(pStatBuf, 0, sizeof(VSIStatBufL)); + + char* zipFilename = SplitFilename(pszFilename, osInArchiveSubDir, TRUE); + if (zipFilename == NULL) + return -1; + + { + CPLMutexHolder oHolder(&hMutex); + + if (oMapZipWriteHandles.find(zipFilename) != oMapZipWriteHandles.end() ) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot read a zip file being written"); + CPLFree(zipFilename); + return -1; + } + } + CPLFree(zipFilename); + + return VSIArchiveFilesystemHandler::Stat(pszFilename, pStatBuf, nFlags); +} + +/************************************************************************/ +/* RemoveFromMap() */ +/************************************************************************/ + +void VSIZipFilesystemHandler::RemoveFromMap(VSIZipWriteHandle* poHandle) +{ + CPLMutexHolder oHolder( &hMutex ); + std::map::iterator iter; + + for( iter = oMapZipWriteHandles.begin(); + iter != oMapZipWriteHandles.end(); ++iter ) + { + if (iter->second == poHandle) + { + oMapZipWriteHandles.erase(iter); + break; + } + } +} + +/************************************************************************/ +/* OpenForWrite() */ +/************************************************************************/ + +VSIVirtualHandle* VSIZipFilesystemHandler::OpenForWrite( const char *pszFilename, + const char *pszAccess) +{ + CPLMutexHolder oHolder( &hMutex ); + return OpenForWrite_unlocked(pszFilename, pszAccess); +} + + +VSIVirtualHandle* VSIZipFilesystemHandler::OpenForWrite_unlocked( const char *pszFilename, + const char *pszAccess) +{ + char* zipFilename; + CPLString osZipInFileName; + + zipFilename = SplitFilename(pszFilename, osZipInFileName, FALSE); + if (zipFilename == NULL) + return NULL; + CPLString osZipFilename = zipFilename; + CPLFree(zipFilename); + zipFilename = NULL; + + /* Invalidate cached file list */ + std::map::iterator iter = oFileList.find(osZipFilename); + if (iter != oFileList.end()) + { + VSIArchiveContent* content = iter->second; + int i; + for(i=0;inEntries;i++) + { + delete content->entries[i].file_pos; + CPLFree(content->entries[i].fileName); + } + CPLFree(content->entries); + delete content; + + oFileList.erase(iter); + } + + VSIZipWriteHandle* poZIPHandle; + + if (oMapZipWriteHandles.find(osZipFilename) != oMapZipWriteHandles.end() ) + { + if (strchr(pszAccess, '+') != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Random access not supported for writable file in /vsizip"); + return NULL; + } + + poZIPHandle = oMapZipWriteHandles[osZipFilename]; + + if (poZIPHandle->GetChildInWriting() != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot create %s while another file is being written in the .zip", + osZipInFileName.c_str()); + return NULL; + } + + poZIPHandle->StopCurrentFile(); + + /* Re-add path separator when creating directories */ + char chLastChar = pszFilename[strlen(pszFilename) - 1]; + if (chLastChar == '/' || chLastChar == '\\') + osZipInFileName += chLastChar; + + if (CPLCreateFileInZip(poZIPHandle->GetHandle(), + osZipInFileName, NULL) != CE_None) + return NULL; + + VSIZipWriteHandle* poChildHandle = + new VSIZipWriteHandle(this, NULL, poZIPHandle); + + poZIPHandle->StartNewFile(poChildHandle); + + return poChildHandle; + } + else + { + char** papszOptions = NULL; + if ((strchr(pszAccess, '+') && osZipInFileName.size() == 0) || + osZipInFileName.size() != 0) + { + VSIStatBufL sBuf; + if (VSIStatExL(osZipFilename, &sBuf, VSI_STAT_EXISTS_FLAG) == 0) + papszOptions = CSLAddNameValue(papszOptions, "APPEND", "TRUE"); + } + + void* hZIP = CPLCreateZip(osZipFilename, papszOptions); + CSLDestroy(papszOptions); + + if (hZIP == NULL) + return NULL; + + oMapZipWriteHandles[osZipFilename] = + new VSIZipWriteHandle(this, hZIP, NULL); + + if (osZipInFileName.size() != 0) + { + VSIZipWriteHandle* poRes = + (VSIZipWriteHandle*)OpenForWrite_unlocked(pszFilename, pszAccess); + if (poRes == NULL) + { + delete oMapZipWriteHandles[osZipFilename]; + return NULL; + } + + poRes->SetAutoDeleteParent(); + + return poRes; + } + + return oMapZipWriteHandles[osZipFilename]; + } +} + + +/************************************************************************/ +/* VSIZipWriteHandle() */ +/************************************************************************/ + +VSIZipWriteHandle::VSIZipWriteHandle(VSIZipFilesystemHandler* poFS, + void* hZIP, + VSIZipWriteHandle* poParent) +{ + this->poFS = poFS; + this->hZIP = hZIP; + this->poParent = poParent; + poChildInWriting = NULL; + bAutoDeleteParent = FALSE; + nCurOffset = 0; +} + +/************************************************************************/ +/* ~VSIZipWriteHandle() */ +/************************************************************************/ + +VSIZipWriteHandle::~VSIZipWriteHandle() +{ + Close(); +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIZipWriteHandle::Seek( vsi_l_offset nOffset, int nWhence ) +{ + if( nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR) ) + return 0; + if( nOffset == nCurOffset && nWhence == SEEK_SET ) + return 0; + + CPLError(CE_Failure, CPLE_NotSupported, + "VSIFSeekL() is not supported on writable Zip files"); + return -1; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIZipWriteHandle::Tell() +{ + return nCurOffset; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIZipWriteHandle::Read( CPL_UNUSED void *pBuffer, + CPL_UNUSED size_t nSize, + CPL_UNUSED size_t nMemb ) +{ + CPLError(CE_Failure, CPLE_NotSupported, + "VSIFReadL() is not supported on writable Zip files"); + return 0; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIZipWriteHandle::Write( const void *pBuffer, size_t nSize, size_t nMemb ) +{ + if (poParent == NULL) + { + CPLError(CE_Failure, CPLE_NotSupported, + "VSIFWriteL() is not supported on main Zip file or closed subfiles"); + return 0; + } + + if (CPLWriteFileInZip( poParent->hZIP, pBuffer, (int)(nSize * nMemb) ) != CE_None) + return 0; + + nCurOffset +=(int) (nSize * nMemb); + + return nMemb; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSIZipWriteHandle::Eof() +{ + CPLError(CE_Failure, CPLE_NotSupported, + "VSIFEofL() is not supported on writable Zip files"); + return FALSE; +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSIZipWriteHandle::Flush() +{ + /*CPLError(CE_Failure, CPLE_NotSupported, + "VSIFFlushL() is not supported on writable Zip files");*/ + return 0; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIZipWriteHandle::Close() +{ + if (poParent) + { + CPLCloseFileInZip(poParent->hZIP); + poParent->poChildInWriting = NULL; + if (bAutoDeleteParent) + delete poParent; + poParent = NULL; + } + if (poChildInWriting) + { + poChildInWriting->Close(); + poChildInWriting = NULL; + } + if (hZIP) + { + CPLCloseZip(hZIP); + hZIP = NULL; + + poFS->RemoveFromMap(this); + } + + return 0; +} + +/************************************************************************/ +/* StopCurrentFile() */ +/************************************************************************/ + +void VSIZipWriteHandle::StopCurrentFile() +{ + if (poChildInWriting) + poChildInWriting->Close(); + poChildInWriting = NULL; +} + +/************************************************************************/ +/* StartNewFile() */ +/************************************************************************/ + +void VSIZipWriteHandle::StartNewFile(VSIZipWriteHandle* poSubFile) +{ + poChildInWriting = poSubFile; +} + +/************************************************************************/ +/* VSIInstallZipFileHandler() */ +/************************************************************************/ + + +/** + * \brief Install ZIP file system handler. + * + * A special file handler is installed that allows reading on-the-fly in ZIP + * (.zip) archives. + * + * All portions of the file system underneath the base path "/vsizip/" will be + * handled by this driver. + * + * The syntax to open a file inside a zip file is /vsizip/path/to/the/file.zip/path/inside/the/zip/file + * were path/to/the/file.zip is relative or absolute and path/inside/the/zip/file + * is the relative path to the file inside the archive. + * + * If the path is absolute, it should begin with a / on a Unix-like OS (or C:\ on Windows), + * so the line looks like /vsizip//home/gdal/... + * For example gdalinfo /vsizip/myarchive.zip/subdir1/file1.tif + * + * Syntaxic sugar : if the .zip file contains only one file located at its root, + * just mentionning "/vsizip/path/to/the/file.zip" will work + * + * VSIStatL() will return the uncompressed size in st_size member and file + * nature- file or directory - in st_mode member. + * + * Directory listing is available through VSIReadDir(). + * + * Since GDAL 1.8.0, write capabilities are available. They allow creating + * a new zip file and adding new files to an already existing (or just created) + * zip file. Read and write operations cannot be interleaved : the new zip must + * be closed before being re-opened for read. + * + * Additional documentation is to be found at http://trac.osgeo.org/gdal/wiki/UserDocs/ReadInZip + * + * @since GDAL 1.6.0 + */ + +void VSIInstallZipFileHandler(void) +{ + VSIFileManager::InstallHandler( "/vsizip/", new VSIZipFilesystemHandler() ); +} + + +/************************************************************************/ +/* CPLZLibDeflate() */ +/************************************************************************/ + +/** + * \brief Compress a buffer with ZLib DEFLATE compression. + * + * @param ptr input buffer. + * @param nBytes size of input buffer in bytes. + * @param nLevel ZLib compression level (-1 for default). + * @param outptr output buffer, or NULL to let the function allocate it. + * @param nOutAvailableBytes size of output buffer if provided, or ignored. + * @param pnOutBytes pointer to a size_t, where to store the size of the + * output buffer. + * + * @return the output buffer (to be freed with VSIFree() if not provided) + * or NULL in case of error. + * + * @since GDAL 1.10.0 + */ + +void* CPLZLibDeflate( const void* ptr, + size_t nBytes, + CPL_UNUSED int nLevel, + void* outptr, + size_t nOutAvailableBytes, + size_t* pnOutBytes ) +{ + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + int ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION); + if (ret != Z_OK) + { + if( pnOutBytes != NULL ) + *pnOutBytes = 0; + return NULL; + } + + size_t nTmpSize; + void* pTmp; + if( outptr == NULL ) + { + nTmpSize = 8 + nBytes * 2; + pTmp = VSIMalloc(nTmpSize); + if( pTmp == NULL ) + { + deflateEnd(&strm); + if( pnOutBytes != NULL ) + *pnOutBytes = 0; + return NULL; + } + } + else + { + pTmp = outptr; + nTmpSize = nOutAvailableBytes; + } + + strm.avail_in = nBytes; + strm.next_in = (Bytef*) ptr; + strm.avail_out = nTmpSize; + strm.next_out = (Bytef*) pTmp; + ret = deflate(&strm, Z_FINISH); + if( ret != Z_STREAM_END ) + { + if( pTmp != outptr ) + VSIFree(pTmp); + if( pnOutBytes != NULL ) + *pnOutBytes = 0; + return NULL; + } + if( pnOutBytes != NULL ) + *pnOutBytes = nTmpSize - strm.avail_out; + deflateEnd(&strm); + return pTmp; +} + +/************************************************************************/ +/* CPLZLibInflate() */ +/************************************************************************/ + +/** + * \brief Uncompress a buffer compressed with ZLib DEFLATE compression. + * + * @param ptr input buffer. + * @param nBytes size of input buffer in bytes. + * @param outptr output buffer, or NULL to let the function allocate it. + * @param nOutAvailableBytes size of output buffer if provided, or ignored. + * @param pnOutBytes pointer to a size_t, where to store the size of the + * output buffer. + * + * @return the output buffer (to be freed with VSIFree() if not provided) + * or NULL in case of error. + * + * @since GDAL 1.10.0 + */ + +void* CPLZLibInflate( const void* ptr, size_t nBytes, + void* outptr, size_t nOutAvailableBytes, + size_t* pnOutBytes ) +{ + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = nBytes; + strm.next_in = (Bytef*) ptr; + int ret = inflateInit(&strm); + if (ret != Z_OK) + { + if( pnOutBytes != NULL ) + *pnOutBytes = 0; + return NULL; + } + + size_t nTmpSize; + char* pszTmp; + if( outptr == NULL ) + { + nTmpSize = 2 * nBytes; + pszTmp = (char*) VSIMalloc(nTmpSize + 1); + if( pszTmp == NULL ) + { + inflateEnd(&strm); + if( pnOutBytes != NULL ) + *pnOutBytes = 0; + return NULL; + } + } + else + { + pszTmp = (char*) outptr; + nTmpSize = nOutAvailableBytes; + } + + strm.avail_out = nTmpSize; + strm.next_out = (Bytef*) pszTmp; + + while(TRUE) + { + ret = inflate(&strm, Z_FINISH); + if( ret == Z_BUF_ERROR ) + { + if( outptr == pszTmp ) + { + inflateEnd(&strm); + if( pnOutBytes != NULL ) + *pnOutBytes = 0; + return NULL; + } + + size_t nAlreadyWritten = nTmpSize - strm.avail_out; + nTmpSize = nTmpSize * 2; + char* pszTmpNew = (char*) VSIRealloc(pszTmp, nTmpSize + 1); + if( pszTmpNew == NULL ) + { + VSIFree(pszTmp); + inflateEnd(&strm); + if( pnOutBytes != NULL ) + *pnOutBytes = 0; + return NULL; + } + pszTmp = pszTmpNew; + strm.avail_out = nTmpSize - nAlreadyWritten; + strm.next_out = (Bytef*) (pszTmp + nAlreadyWritten); + } + else + break; + } + + if (ret == Z_OK || ret == Z_STREAM_END) + { + size_t nOutBytes = nTmpSize - strm.avail_out; + /* Nul-terminate if possible */ + if( outptr != pszTmp || nOutBytes < nTmpSize ) + pszTmp[nOutBytes] = '\0'; + inflateEnd(&strm); + if( pnOutBytes != NULL ) + *pnOutBytes = nOutBytes; + return pszTmp; + } + else + { + if( outptr != pszTmp ) + VSIFree(pszTmp); + inflateEnd(&strm); + if( pnOutBytes != NULL ) + *pnOutBytes = 0; + return NULL; + } +} diff --git a/bazaar/plugin/gdal/port/cpl_vsil_simple.cpp b/bazaar/plugin/gdal/port/cpl_vsil_simple.cpp new file mode 100644 index 000000000..a224f27fb --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_simple.cpp @@ -0,0 +1,193 @@ +/****************************************************************************** + * $Id: cpl_vsil_simple.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Project: VSI Virtual File System + * Purpose: Alternatve simplified implementation VSI*L File API that just + * uses plain VSI API and/or posix calls. This module isn't + * normally built into GDAL. It is for simple packages like + * dgnlib. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2006, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi.h" + +CPL_CVSID("$Id: cpl_vsil_simple.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + +#ifdef WIN32 +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +#endif + +/************************************************************************/ +/* VSIMkdir() */ +/************************************************************************/ + +int VSIMkdir( const char *pszPathname, long mode ) + +{ +#ifdef WIN32 + return mkdir( pszPathname ); +#else + return mkdir( pszPathname, mode ); +#endif +} + +/************************************************************************/ +/* VSIUnlink() */ +/*************************a***********************************************/ + +int VSIUnlink( const char * pszFilename ) + +{ + return unlink( pszFilename ); +} + +/************************************************************************/ +/* VSIRename() */ +/************************************************************************/ + +int VSIRename( const char * oldpath, const char * newpath ) + +{ + return rename( oldpath, newpath ); +} + +/************************************************************************/ +/* VSIRmdir() */ +/************************************************************************/ + +int VSIRmdir( const char * pszDirname ) + +{ + return rmdir( pszDirname ); +} + +/************************************************************************/ +/* VSIStatL() */ +/************************************************************************/ + +int VSIStatL( const char * pszFilename, VSIStatBufL *psStatBuf ) + +{ + return( VSIStat( pszFilename, (VSIStatBuf *) psStatBuf ) ); +} + +/************************************************************************/ +/* VSIFOpenL() */ +/************************************************************************/ + +FILE *VSIFOpenL( const char * pszFilename, const char * pszAccess ) + +{ + return VSIFOpen( pszFilename, pszAccess ); +} + +/************************************************************************/ +/* VSIFCloseL() */ +/************************************************************************/ + +int VSIFCloseL( FILE * fp ) + +{ + return VSIFClose( fp ); +} + +/************************************************************************/ +/* VSIFSeekL() */ +/************************************************************************/ + +int VSIFSeekL( FILE * fp, vsi_l_offset nOffset, int nWhence ) + +{ + return VSIFSeek( fp, (int) nOffset, nWhence ); +} + +/************************************************************************/ +/* VSIFTellL() */ +/************************************************************************/ + +vsi_l_offset VSIFTellL( FILE * fp ) + +{ + return (vsi_l_offset) VSIFTell( fp ); +} + +/************************************************************************/ +/* VSIRewindL() */ +/************************************************************************/ + +void VSIRewindL( FILE * fp ) + +{ + VSIFSeekL( fp, 0, SEEK_SET ); +} + +/************************************************************************/ +/* VSIFFlushL() */ +/************************************************************************/ + +int VSIFFlushL( FILE * fp ) + +{ + VSIFFlush( fp ); + return 0; +} + +/************************************************************************/ +/* VSIFReadL() */ +/************************************************************************/ + +size_t VSIFReadL( void * pBuffer, size_t nSize, size_t nCount, FILE * fp ) + +{ + return VSIFRead( pBuffer, nSize, nCount, fp ); +} + +/************************************************************************/ +/* VSIFWriteL() */ +/************************************************************************/ + +size_t VSIFWriteL( void * pBuffer, size_t nSize, size_t nCount, FILE * fp ) + +{ + return VSIFWrite( pBuffer, nSize, nCount, fp ); +} + +/************************************************************************/ +/* VSIFEofL() */ +/************************************************************************/ + +int VSIFEofL( FILE * fp ) + +{ + return VSIFEof( fp ); +} diff --git a/bazaar/plugin/gdal/port/cpl_vsil_sparsefile.cpp b/bazaar/plugin/gdal/port/cpl_vsil_sparsefile.cpp new file mode 100644 index 000000000..19bfd8710 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_sparsefile.cpp @@ -0,0 +1,575 @@ +/****************************************************************************** + * $Id: cpl_vsil_sparsefile.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: VSI Virtual File System + * Purpose: Implementation of sparse file virtual io driver. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include "cpl_minixml.h" +#include + +#if defined(WIN32CE) +# include +#endif + +CPL_CVSID("$Id: cpl_vsil_sparsefile.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +class SFRegion { +public: + SFRegion() : fp(NULL), nDstOffset(0), nSrcOffset(0), nLength(0), + byValue(0), bTriedOpen(FALSE) {} + + CPLString osFilename; + VSILFILE *fp; + GUIntBig nDstOffset; + GUIntBig nSrcOffset; + GUIntBig nLength; + GByte byValue; + int bTriedOpen; +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSISparseFileHandle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSISparseFileFilesystemHandler; + +class VSISparseFileHandle : public VSIVirtualHandle +{ + VSISparseFileFilesystemHandler* poFS; + + public: + VSISparseFileHandle(VSISparseFileFilesystemHandler* poFS) : poFS(poFS), nOverallLength(0), nCurOffset(0) {} + + GUIntBig nOverallLength; + GUIntBig nCurOffset; + + std::vector aoRegions; + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Close(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSISparseFileFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSISparseFileFilesystemHandler : public VSIFilesystemHandler +{ + std::map oRecOpenCount; + +public: + VSISparseFileFilesystemHandler(); + virtual ~VSISparseFileFilesystemHandler(); + + int DecomposePath( const char *pszPath, + CPLString &osFilename, + vsi_l_offset &nSparseFileOffset, + vsi_l_offset &nSparseFileSize ); + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); + virtual int Unlink( const char *pszFilename ); + virtual int Mkdir( const char *pszDirname, long nMode ); + virtual int Rmdir( const char *pszDirname ); + virtual char **ReadDir( const char *pszDirname ); + + int GetRecCounter() { return oRecOpenCount[CPLGetPID()]; } + void IncRecCounter() { oRecOpenCount[CPLGetPID()] ++; } + void DecRecCounter() { oRecOpenCount[CPLGetPID()] --; } +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSISparseFileHandle */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSISparseFileHandle::Close() + +{ + unsigned int i; + + for( i = 0; i < aoRegions.size(); i++ ) + { + if( aoRegions[i].fp != NULL ) + VSIFCloseL( aoRegions[i].fp ); + } + + return 0; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSISparseFileHandle::Seek( vsi_l_offset nOffset, int nWhence ) + +{ + if( nWhence == SEEK_SET ) + nCurOffset = nOffset; + else if( nWhence == SEEK_CUR ) + { + nCurOffset += nOffset; + } + else if( nWhence == SEEK_END ) + { + nCurOffset = nOverallLength + nOffset; + } + else + { + errno = EINVAL; + return -1; + } + + return 0; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSISparseFileHandle::Tell() + +{ + return nCurOffset; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSISparseFileHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) + +{ +/* -------------------------------------------------------------------- */ +/* Find what region we are in, searching linearly from the */ +/* start. */ +/* -------------------------------------------------------------------- */ + unsigned int iRegion; + + for( iRegion = 0; iRegion < aoRegions.size(); iRegion++ ) + { + if( nCurOffset >= aoRegions[iRegion].nDstOffset + && nCurOffset < aoRegions[iRegion].nDstOffset + aoRegions[iRegion].nLength ) + break; + } + +/* -------------------------------------------------------------------- */ +/* Default to zeroing the buffer if no corresponding region was */ +/* found. */ +/* -------------------------------------------------------------------- */ + if( iRegion == aoRegions.size() ) + { + memset( pBuffer, 0, nSize * nCount ); + nCurOffset += nSize * nSize; + return nCount; + } + +/* -------------------------------------------------------------------- */ +/* If this request crosses region boundaries, split it into two */ +/* requests. */ +/* -------------------------------------------------------------------- */ + size_t nReturnCount = nCount; + GUIntBig nBytesRequested = nSize * nCount; + GUIntBig nBytesAvailable = + aoRegions[iRegion].nDstOffset + aoRegions[iRegion].nLength; + + if( nCurOffset + nBytesRequested > nBytesAvailable ) + { + size_t nExtraBytes = + (size_t) (nCurOffset + nBytesRequested - nBytesAvailable); + // Recurse to get the rest of the request. + + GUIntBig nCurOffsetSave = nCurOffset; + nCurOffset += nBytesRequested - nExtraBytes; + size_t nBytesRead = + this->Read( ((char *) pBuffer) + nBytesRequested - nExtraBytes, + 1, nExtraBytes ); + nCurOffset = nCurOffsetSave; + + if( nBytesRead < nExtraBytes ) + nReturnCount -= (nExtraBytes-nBytesRead) / nSize; + + nBytesRequested -= nExtraBytes; + } + +/* -------------------------------------------------------------------- */ +/* Handle a constant region. */ +/* -------------------------------------------------------------------- */ + if( aoRegions[iRegion].osFilename.size() == 0 ) + { + memset( pBuffer, aoRegions[iRegion].byValue, + (size_t) nBytesRequested ); + } + +/* -------------------------------------------------------------------- */ +/* Otherwise handle as a file. */ +/* -------------------------------------------------------------------- */ + else + { + if( aoRegions[iRegion].fp == NULL ) + { + if( !aoRegions[iRegion].bTriedOpen ) + { + aoRegions[iRegion].fp = + VSIFOpenL( aoRegions[iRegion].osFilename, "r" ); + if( aoRegions[iRegion].fp == NULL ) + { + CPLDebug( "/vsisparse/", "Failed to open '%s'.", + aoRegions[iRegion].osFilename.c_str() ); + } + aoRegions[iRegion].bTriedOpen = TRUE; + } + if( aoRegions[iRegion].fp == NULL ) + { + return 0; + } + } + + if( VSIFSeekL( aoRegions[iRegion].fp, + nCurOffset + - aoRegions[iRegion].nDstOffset + + aoRegions[iRegion].nSrcOffset, + SEEK_SET ) != 0 ) + return 0; + + poFS->IncRecCounter(); + size_t nBytesRead = VSIFReadL( pBuffer, 1, (size_t) nBytesRequested, + aoRegions[iRegion].fp ); + poFS->DecRecCounter(); + + if( nBytesAvailable < nBytesRequested ) + nReturnCount = nBytesRead / nSize; + } + + nCurOffset += nReturnCount * nSize; + + return nReturnCount; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSISparseFileHandle::Write( CPL_UNUSED const void * pBuffer, + CPL_UNUSED size_t nSize, + CPL_UNUSED size_t nCount ) +{ + errno = EBADF; + return 0; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSISparseFileHandle::Eof() + +{ + return nCurOffset >= nOverallLength; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSISparseFileFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VSISparseFileFilesystemHandler() */ +/************************************************************************/ + +VSISparseFileFilesystemHandler::VSISparseFileFilesystemHandler() + +{ +} + +/************************************************************************/ +/* ~VSISparseFileFilesystemHandler() */ +/************************************************************************/ + +VSISparseFileFilesystemHandler::~VSISparseFileFilesystemHandler() + +{ +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle * +VSISparseFileFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess ) + +{ + CPLAssert( EQUALN(pszFilename,"/vsisparse/", 11) ); + + if( !EQUAL(pszAccess,"r") && !EQUAL(pszAccess,"rb") ) + { + errno = EACCES; + return NULL; + } + + /* Arbitrary number */ + if( GetRecCounter() == 32 ) + return NULL; + + CPLString osSparseFilePath = pszFilename + 11; + +/* -------------------------------------------------------------------- */ +/* Does this file even exist? */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFOpenL( osSparseFilePath, "r" ); + if( fp == NULL ) + return NULL; + VSIFCloseL( fp ); + +/* -------------------------------------------------------------------- */ +/* Read the XML file. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psXMLRoot = CPLParseXMLFile( osSparseFilePath ); + + if( psXMLRoot == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Setup the file handle on this file. */ +/* -------------------------------------------------------------------- */ + VSISparseFileHandle *poHandle = new VSISparseFileHandle(this); + +/* -------------------------------------------------------------------- */ +/* Translate the desired fields out of the XML tree. */ +/* -------------------------------------------------------------------- */ + CPLXMLNode *psRegion; + + for( psRegion = psXMLRoot->psChild; + psRegion != NULL; + psRegion = psRegion->psNext ) + { + if( psRegion->eType != CXT_Element ) + continue; + + if( !EQUAL(psRegion->pszValue,"SubfileRegion") + && !EQUAL(psRegion->pszValue,"ConstantRegion") ) + continue; + + SFRegion oRegion; + + oRegion.osFilename = CPLGetXMLValue( psRegion, "Filename", "" ); + if( atoi(CPLGetXMLValue( psRegion, "Filename.relative", "0" )) != 0 ) + { + CPLString osSFPath = CPLGetPath(osSparseFilePath); + oRegion.osFilename = CPLFormFilename( osSFPath, + oRegion.osFilename, NULL ); + } + + oRegion.nDstOffset = + CPLScanUIntBig( CPLGetXMLValue(psRegion,"DestinationOffset","0" ), + 32 ); + + oRegion.nSrcOffset = + CPLScanUIntBig( CPLGetXMLValue(psRegion,"SourceOffset","0" ), 32); + + oRegion.nLength = + CPLScanUIntBig( CPLGetXMLValue(psRegion,"RegionLength","0" ), 32); + + oRegion.byValue = (GByte) atoi(CPLGetXMLValue(psRegion,"Value","0" )); + + poHandle->aoRegions.push_back( oRegion ); + } + +/* -------------------------------------------------------------------- */ +/* Get sparse file length, use maximum bound of regions if not */ +/* explicit in file. */ +/* -------------------------------------------------------------------- */ + poHandle->nOverallLength = + CPLScanUIntBig( CPLGetXMLValue(psXMLRoot,"Length","0" ), 32); + if( poHandle->nOverallLength == 0 ) + { + unsigned int i; + + for( i = 0; i < poHandle->aoRegions.size(); i++ ) + { + poHandle->nOverallLength = MAX(poHandle->nOverallLength, + poHandle->aoRegions[i].nDstOffset + + poHandle->aoRegions[i].nLength); + } + } + + CPLDestroyXMLNode( psXMLRoot ); + + return poHandle; +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSISparseFileFilesystemHandler::Stat( const char * pszFilename, + VSIStatBufL * psStatBuf, + int nFlags ) + +{ + VSIVirtualHandle *poFile = Open( pszFilename, "r" ); + + memset( psStatBuf, 0, sizeof(VSIStatBufL) ); + + if( poFile == NULL ) + return -1; + + poFile->Seek( 0, SEEK_END ); + size_t nLength = (size_t) poFile->Tell(); + delete poFile; + + int nResult = VSIStatExL( pszFilename + strlen("/vsisparse/"), + psStatBuf, nFlags ); + + psStatBuf->st_size = nLength; + + return nResult; +} + +/************************************************************************/ +/* Unlink() */ +/************************************************************************/ + +int VSISparseFileFilesystemHandler::Unlink( CPL_UNUSED const char * pszFilename ) +{ + errno = EACCES; + return -1; +} + +/************************************************************************/ +/* Mkdir() */ +/************************************************************************/ + +int VSISparseFileFilesystemHandler::Mkdir( CPL_UNUSED const char * pszPathname, + CPL_UNUSED long nMode ) +{ + errno = EACCES; + return -1; +} + +/************************************************************************/ +/* Rmdir() */ +/************************************************************************/ + +int VSISparseFileFilesystemHandler::Rmdir( CPL_UNUSED const char * pszPathname ) +{ + errno = EACCES; + return -1; +} + +/************************************************************************/ +/* ReadDir() */ +/************************************************************************/ + +char **VSISparseFileFilesystemHandler::ReadDir( CPL_UNUSED const char *pszPath ) +{ + errno = EACCES; + return NULL; +} + +/************************************************************************/ +/* VSIInstallSparseFileFilesystemHandler() */ +/************************************************************************/ + +/** + * Install /vsisparse/ virtual file handler. + * + * The sparse virtual file handler allows a virtual file to be composed + * from chunks of data in other files, potentially with large spaces in + * the virtual file set to a constant value. This can make it possible to + * test some sorts of operations on what seems to be a large file with + * image data set to a constant value. It is also helpful when wanting to + * add test files to the test suite that are too large, but for which most + * of the data can be ignored. It could, in theory, also be used to + * treat several files on different file systems as one large virtual file. + * + * The file referenced by /vsisparse/ should be an XML control file + * formatted something like: + * + * +\verbatim + + 87629264 + Stuff at start of file. + 251_head.dat + 0 + 0 + 2768 + + + RasterDMS node. + 251_rasterdms.dat + 87313104 + 0 + 160 + + + Stuff at end of file. + 251_tail.dat + 87611924 + 0 + 17340 + + + Default for the rest of the file. + 0 + 87629264 + 0 + + +\endverbatim + * + * Hopefully the values and semantics are fairly obvious. + * + * This driver is installed by default. + */ + +void VSIInstallSparseFileHandler() +{ + VSIFileManager::InstallHandler( "/vsisparse/", + new VSISparseFileFilesystemHandler ); +} + diff --git a/bazaar/plugin/gdal/port/cpl_vsil_stdin.cpp b/bazaar/plugin/gdal/port/cpl_vsil_stdin.cpp new file mode 100644 index 000000000..158607f9b --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_stdin.cpp @@ -0,0 +1,394 @@ +/********************************************************************** + * $Id: cpl_vsil_stdin.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement VSI large file api for stdin + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2010-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_port.h" +#include "cpl_error.h" +#include "cpl_vsi_virtual.h" + +#include +#ifdef WIN32 +#include +#include +#endif + +CPL_CVSID("$Id: cpl_vsil_stdin.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/* We buffer the first 1MB of standard input to enable drivers */ +/* to autodetect data. In the first MB, backward and forward seeking */ +/* is allowed, after only forward seeking will work */ +#define BUFFER_SIZE (1024 * 1024) + +static GByte* pabyBuffer; +static GUInt32 nBufferLen; +static GUIntBig nRealPos; + +/************************************************************************/ +/* VSIStdinInit() */ +/************************************************************************/ + +static void VSIStdinInit() +{ + if (pabyBuffer == NULL) + { +#ifdef WIN32 + setmode( fileno( stdin ), O_BINARY ); +#endif + pabyBuffer = (GByte*)CPLMalloc(BUFFER_SIZE); + } +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdinFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIStdinFilesystemHandler : public VSIFilesystemHandler +{ +public: + VSIStdinFilesystemHandler(); + virtual ~VSIStdinFilesystemHandler(); + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, + VSIStatBufL *pStatBuf, int nFlags ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdinHandle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIStdinHandle : public VSIVirtualHandle +{ + private: + GUIntBig nCurOff; + int ReadAndCache( void* pBuffer, int nToRead ); + + public: + VSIStdinHandle(); + virtual ~VSIStdinHandle(); + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Close(); +}; + +/************************************************************************/ +/* VSIStdinHandle() */ +/************************************************************************/ + +VSIStdinHandle::VSIStdinHandle() +{ + nCurOff = 0; +} + +/************************************************************************/ +/* ~VSIStdinHandle() */ +/************************************************************************/ + +VSIStdinHandle::~VSIStdinHandle() +{ +} + + +/************************************************************************/ +/* ReadAndCache() */ +/************************************************************************/ + +int VSIStdinHandle::ReadAndCache( void* pBuffer, int nToRead ) +{ + CPLAssert(nCurOff == nRealPos); + + int nRead = fread(pBuffer, 1, nToRead, stdin); + + if (nRealPos < BUFFER_SIZE) + { + int nToCopy = MIN(BUFFER_SIZE - (int)nRealPos, nRead); + memcpy(pabyBuffer + nRealPos, pBuffer, nToCopy); + nBufferLen += nToCopy; + } + + nCurOff += nRead; + nRealPos = nCurOff; + + return nRead; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIStdinHandle::Seek( vsi_l_offset nOffset, int nWhence ) + +{ + if (nWhence == SEEK_SET && nOffset == nCurOff) + return 0; + + VSIStdinInit(); + if (nBufferLen == 0) + nRealPos = nBufferLen = fread(pabyBuffer, 1, BUFFER_SIZE, stdin); + + if (nWhence == SEEK_END) + { + if (nOffset != 0) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Seek(xx != 0, SEEK_END) unsupported on /vsistdin"); + return -1; + } + + if (nBufferLen < BUFFER_SIZE) + { + nCurOff = nBufferLen; + return 0; + } + + CPLError(CE_Failure, CPLE_NotSupported, + "Seek(SEEK_END) unsupported on /vsistdin when stdin > 1 MB"); + return -1; + } + + if (nWhence == SEEK_CUR) + nOffset += nCurOff; + + if (nRealPos > nBufferLen && nOffset < nRealPos) + { + CPLError(CE_Failure, CPLE_NotSupported, + "backward Seek() unsupported on /vsistdin above first MB"); + return -1; + } + + if (nOffset < nBufferLen) + { + nCurOff = nOffset; + return 0; + } + + if (nOffset == nCurOff) + return 0; + + CPLDebug("VSI", "Forward seek from " CPL_FRMT_GUIB " to " CPL_FRMT_GUIB, + nCurOff, nOffset); + + char abyTemp[8192]; + nCurOff = nRealPos; + while(TRUE) + { + int nToRead = (int) MIN(8192, nOffset - nCurOff); + int nRead = ReadAndCache( abyTemp, nToRead ); + + if (nRead < nToRead) + return -1; + if (nToRead < 8192) + break; + } + + return 0; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIStdinHandle::Tell() +{ + return nCurOff; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIStdinHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) + +{ + VSIStdinInit(); + + if (nCurOff < nBufferLen) + { + if (nCurOff + nSize * nCount < nBufferLen) + { + memcpy(pBuffer, pabyBuffer + nCurOff, nSize * nCount); + nCurOff += nSize * nCount; + return nCount; + } + + int nAlreadyCached = (int)(nBufferLen - nCurOff); + memcpy(pBuffer, pabyBuffer + nCurOff, nAlreadyCached); + + nCurOff += nAlreadyCached; + + int nRead = ReadAndCache( (GByte*)pBuffer + nAlreadyCached, + (int)(nSize*nCount - nAlreadyCached) ); + + return ((nRead + nAlreadyCached) / nSize); + } + + int nRead = ReadAndCache( pBuffer, (int)(nSize * nCount) ); + return nRead / nSize; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIStdinHandle::Write( CPL_UNUSED const void * pBuffer, + CPL_UNUSED size_t nSize, + CPL_UNUSED size_t nCount ) +{ + CPLError(CE_Failure, CPLE_NotSupported, + "Write() unsupported on /vsistdin"); + return 0; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSIStdinHandle::Eof() + +{ + if (nCurOff < nBufferLen) + return FALSE; + return feof(stdin); +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIStdinHandle::Close() + +{ + return 0; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdinFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VSIStdinFilesystemHandler() */ +/************************************************************************/ + +VSIStdinFilesystemHandler::VSIStdinFilesystemHandler() +{ + pabyBuffer = NULL; + nBufferLen = 0; + nRealPos = 0; +} + +/************************************************************************/ +/* ~VSIStdinFilesystemHandler() */ +/************************************************************************/ + +VSIStdinFilesystemHandler::~VSIStdinFilesystemHandler() +{ + CPLFree(pabyBuffer); + pabyBuffer = NULL; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle * +VSIStdinFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess ) + +{ + if (strcmp(pszFilename, "/vsistdin/") != 0) + return NULL; + + if ( strchr(pszAccess, 'w') != NULL || + strchr(pszAccess, '+') != NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Write or update mode not supported on /vsistdin"); + return NULL; + } + + return new VSIStdinHandle; +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSIStdinFilesystemHandler::Stat( const char * pszFilename, + VSIStatBufL * pStatBuf, + int nFlags ) + +{ + memset( pStatBuf, 0, sizeof(VSIStatBufL) ); + + if (strcmp(pszFilename, "/vsistdin/") != 0) + return -1; + + if ((nFlags & VSI_STAT_SIZE_FLAG)) + { + VSIStdinInit(); + if (nBufferLen == 0) + nRealPos = nBufferLen = fread(pabyBuffer, 1, BUFFER_SIZE, stdin); + + pStatBuf->st_size = nBufferLen; + } + + pStatBuf->st_mode = S_IFREG; + return 0; +} + +/************************************************************************/ +/* VSIInstallStdinHandler() */ +/************************************************************************/ + +/** + * \brief Install /vsistdin/ file system handler + * + * A special file handler is installed that allows reading from the standard + * input steam. + * + * The file operations available are of course limited to Read() and + * forward Seek() (full seek in the first MB of a file). + * + * @since GDAL 1.8.0 + */ +void VSIInstallStdinHandler() + +{ + VSIFileManager::InstallHandler( "/vsistdin/", new VSIStdinFilesystemHandler ); +} diff --git a/bazaar/plugin/gdal/port/cpl_vsil_stdout.cpp b/bazaar/plugin/gdal/port/cpl_vsil_stdout.cpp new file mode 100644 index 000000000..faeb037de --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_stdout.cpp @@ -0,0 +1,419 @@ +/********************************************************************** + * $Id: cpl_vsil_stdout.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement VSI large file api for stdout + * Author: Even Rouault, + * + ********************************************************************** + * Copyright (c) 2010-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_port.h" +#include "cpl_error.h" +#include "cpl_vsi_virtual.h" + +#include +#ifdef WIN32 +#include +#include +#endif + +CPL_CVSID("$Id: cpl_vsil_stdout.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +static VSIWriteFunction pWriteFunction = fwrite; +static FILE* pWriteStream = stdout; + +/************************************************************************/ +/* VSIStdoutSetRedirection() */ +/************************************************************************/ + +/** Set an alternative write function and output file handle instead of + * fwrite() / stdout. + * + * @param pFct Function with same signature as fwrite() + * @param stream File handle on which to output. Passed to pFct. + * + * @since GDAL 2.0 + */ +void VSIStdoutSetRedirection( VSIWriteFunction pFct, FILE* stream ) +{ + pWriteFunction = pFct; + pWriteStream = stream; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdoutFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIStdoutFilesystemHandler : public VSIFilesystemHandler +{ +public: + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdoutHandle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIStdoutHandle : public VSIVirtualHandle +{ + vsi_l_offset nOffset; + + public: + VSIStdoutHandle() : nOffset(0) {} + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); +}; + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIStdoutHandle::Seek( vsi_l_offset nOffset, int nWhence ) + +{ + if( nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR) ) + return 0; + if( nWhence == SEEK_SET && nOffset == Tell() ) + return 0; + CPLError(CE_Failure, CPLE_NotSupported, "Seek() unsupported on /vsistdout"); + return -1; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIStdoutHandle::Tell() +{ + return nOffset; +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSIStdoutHandle::Flush() + +{ + if( pWriteStream == stdout ) + return fflush( stdout ); + else + return 0; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIStdoutHandle::Read( CPL_UNUSED void * pBuffer, + CPL_UNUSED size_t nSize, + CPL_UNUSED size_t nCount ) +{ + CPLError(CE_Failure, CPLE_NotSupported, "Read() unsupported on /vsistdout"); + return 0; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIStdoutHandle::Write( const void * pBuffer, size_t nSize, + size_t nCount ) + +{ + size_t nRet = pWriteFunction(pBuffer, nSize, nCount, pWriteStream); + nOffset += nSize * nRet; + return nRet; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSIStdoutHandle::Eof() + +{ + return 0; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIStdoutHandle::Close() + +{ + return 0; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdoutFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle * +VSIStdoutFilesystemHandler::Open( CPL_UNUSED const char *pszFilename, + const char *pszAccess ) +{ + if ( strchr(pszAccess, 'r') != NULL || + strchr(pszAccess, '+') != NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Read or update mode not supported on /vsistdout"); + return NULL; + } + +#ifdef WIN32 + if ( strchr(pszAccess, 'b') != NULL ) + setmode( fileno( stdout ), O_BINARY ); +#endif + + return new VSIStdoutHandle; +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSIStdoutFilesystemHandler::Stat( CPL_UNUSED const char * pszFilename, + VSIStatBufL * pStatBuf, + CPL_UNUSED int nFlags ) + +{ + memset( pStatBuf, 0, sizeof(VSIStatBufL) ); + + return -1; +} + + + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdoutRedirectFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIStdoutRedirectFilesystemHandler : public VSIFilesystemHandler +{ +public: + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdoutRedirectHandle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIStdoutRedirectHandle : public VSIVirtualHandle +{ + VSIVirtualHandle* poHandle; + public: + VSIStdoutRedirectHandle(VSIVirtualHandle* poHandle); + ~VSIStdoutRedirectHandle(); + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); +}; + +/************************************************************************/ +/* VSIStdoutRedirectHandle() */ +/************************************************************************/ + +VSIStdoutRedirectHandle::VSIStdoutRedirectHandle(VSIVirtualHandle* poHandle) +{ + this->poHandle = poHandle; +} + +/************************************************************************/ +/* ~VSIStdoutRedirectHandle() */ +/************************************************************************/ + +VSIStdoutRedirectHandle::~VSIStdoutRedirectHandle() +{ + delete poHandle; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIStdoutRedirectHandle::Seek( CPL_UNUSED vsi_l_offset nOffset, + CPL_UNUSED int nWhence ) +{ + CPLError(CE_Failure, CPLE_NotSupported, "Seek() unsupported on /vsistdout_redirect"); + return -1; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIStdoutRedirectHandle::Tell() +{ + return poHandle->Tell(); +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSIStdoutRedirectHandle::Flush() + +{ + return poHandle->Flush(); +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIStdoutRedirectHandle::Read( CPL_UNUSED void * pBuffer, + CPL_UNUSED size_t nSize, + CPL_UNUSED size_t nCount ) +{ + CPLError(CE_Failure, CPLE_NotSupported, "Read() unsupported on /vsistdout_redirect"); + return 0; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIStdoutRedirectHandle::Write( const void * pBuffer, size_t nSize, + size_t nCount ) + +{ + return poHandle->Write(pBuffer, nSize, nCount); +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSIStdoutRedirectHandle::Eof() + +{ + return poHandle->Eof(); +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIStdoutRedirectHandle::Close() + +{ + return poHandle->Close(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSIStdoutRedirectFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle * +VSIStdoutRedirectFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess ) + +{ + if ( strchr(pszAccess, 'r') != NULL || + strchr(pszAccess, '+') != NULL ) + { + CPLError(CE_Failure, CPLE_NotSupported, + "Read or update mode not supported on /vsistdout_redirect"); + return NULL; + } + + VSIVirtualHandle* poHandle = (VSIVirtualHandle* )VSIFOpenL( + pszFilename + strlen("/vsistdout_redirect/"), pszAccess); + if (poHandle == NULL) + return NULL; + + return new VSIStdoutRedirectHandle(poHandle); +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSIStdoutRedirectFilesystemHandler::Stat( CPL_UNUSED const char * pszFilename, + VSIStatBufL * pStatBuf, + CPL_UNUSED int nFlags ) +{ + memset( pStatBuf, 0, sizeof(VSIStatBufL) ); + + return -1; +} + +/************************************************************************/ +/* VSIInstallStdoutHandler() */ +/************************************************************************/ + +/** + * \brief Install /vsistdout/ file system handler + * + * A special file handler is installed that allows writing to the standard + * output stream. + * + * The file operations available are of course limited to Write(). + * + * @since GDAL 1.8.0 + */ + +void VSIInstallStdoutHandler() + +{ + VSIFileManager::InstallHandler( "/vsistdout/", new VSIStdoutFilesystemHandler ); + VSIFileManager::InstallHandler( "/vsistdout_redirect/", new VSIStdoutRedirectFilesystemHandler ); +} diff --git a/bazaar/plugin/gdal/port/cpl_vsil_subfile.cpp b/bazaar/plugin/gdal/port/cpl_vsil_subfile.cpp new file mode 100644 index 000000000..6aed7a55c --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_subfile.cpp @@ -0,0 +1,464 @@ +/****************************************************************************** + * $Id: cpl_vsil_subfile.cpp 27745 2014-09-27 16:38:57Z goatbar $ + * + * Project: VSI Virtual File System + * Purpose: Implementation of subfile virtual IO functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2009-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" +#include + +#if defined(WIN32CE) +# include +#endif + +CPL_CVSID("$Id: cpl_vsil_subfile.cpp 27745 2014-09-27 16:38:57Z goatbar $"); + +/************************************************************************/ +/* ==================================================================== */ +/* VSISubFileHandle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSISubFileHandle : public VSIVirtualHandle +{ + public: + VSILFILE *fp; + vsi_l_offset nSubregionOffset; + vsi_l_offset nSubregionSize; + int bAtEOF; + + VSISubFileHandle() : fp(NULL), nSubregionOffset(0), nSubregionSize(0), bAtEOF(FALSE) {} + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Close(); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSISubFileFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSISubFileFilesystemHandler : public VSIFilesystemHandler +{ +public: + VSISubFileFilesystemHandler(); + virtual ~VSISubFileFilesystemHandler(); + + int DecomposePath( const char *pszPath, + CPLString &osFilename, + vsi_l_offset &nSubFileOffset, + vsi_l_offset &nSubFileSize ); + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); + virtual int Unlink( const char *pszFilename ); + virtual int Mkdir( const char *pszDirname, long nMode ); + virtual int Rmdir( const char *pszDirname ); + virtual char **ReadDir( const char *pszDirname ); +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSISubFileHandle */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSISubFileHandle::Close() + +{ + VSIFCloseL( fp ); + fp = NULL; + + return 0; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSISubFileHandle::Seek( vsi_l_offset nOffset, int nWhence ) + +{ + bAtEOF = FALSE; + + if( nWhence == SEEK_SET ) + nOffset += nSubregionOffset; + else if( nWhence == SEEK_CUR ) + { + // handle normally. + } + else if( nWhence == SEEK_END ) + { + if( nSubregionSize != 0 ) + { + nOffset = nSubregionOffset + nSubregionSize; + nWhence = SEEK_SET; + } + } + else + { + errno = EINVAL; + return -1; + } + + return VSIFSeekL( fp, nOffset, nWhence ); +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSISubFileHandle::Tell() + +{ + return VSIFTellL( fp ) - nSubregionOffset; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSISubFileHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) + +{ + size_t nRet; + if (nSubregionSize == 0) + nRet = VSIFReadL( pBuffer, nSize, nCount, fp ); + else + { + if (nSize == 0) + return 0; + + vsi_l_offset nCurOffset = VSIFTellL(fp); + if (nCurOffset >= nSubregionOffset + nSubregionSize) + { + bAtEOF = TRUE; + return 0; + } + + size_t nByteToRead = nSize * nCount; + if (nCurOffset + nByteToRead > nSubregionOffset + nSubregionSize) + { + int nRead = (int)VSIFReadL( pBuffer, 1, (size_t)(nSubregionOffset + nSubregionSize - nCurOffset), fp); + nRet = nRead / nSize; + } + else + nRet = VSIFReadL( pBuffer, nSize, nCount, fp ); + } + if( nRet < nCount ) + bAtEOF = TRUE; + return nRet; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSISubFileHandle::Write( const void * pBuffer, size_t nSize, size_t nCount ) + +{ + bAtEOF = FALSE; + + if (nSubregionSize == 0) + return VSIFWriteL( pBuffer, nSize, nCount, fp ); + + if (nSize == 0) + return 0; + + vsi_l_offset nCurOffset = VSIFTellL(fp); + if (nCurOffset >= nSubregionOffset + nSubregionSize) + return 0; + + size_t nByteToWrite = nSize * nCount; + if (nCurOffset + nByteToWrite > nSubregionOffset + nSubregionSize) + { + int nWritten = (int)VSIFWriteL( pBuffer, 1, (size_t)(nSubregionOffset + nSubregionSize - nCurOffset), fp); + return nWritten / nSize; + } + else + return VSIFWriteL( pBuffer, nSize, nCount, fp ); +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSISubFileHandle::Eof() + +{ + return bAtEOF; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSISubFileFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VSISubFileFilesystemHandler() */ +/************************************************************************/ + +VSISubFileFilesystemHandler::VSISubFileFilesystemHandler() + +{ +} + +/************************************************************************/ +/* ~VSISubFileFilesystemHandler() */ +/************************************************************************/ + +VSISubFileFilesystemHandler::~VSISubFileFilesystemHandler() + +{ +} + +/************************************************************************/ +/* DecomposePath() */ +/* */ +/* Parse a path like /vsisubfile/1000_2000,data/abc.tif into an */ +/* offset (1000), a size (2000) and a path (data/abc.tif). */ +/************************************************************************/ + +int +VSISubFileFilesystemHandler::DecomposePath( const char *pszPath, + CPLString &osFilename, + vsi_l_offset &nSubFileOffset, + vsi_l_offset &nSubFileSize ) + +{ + int i; + + osFilename = ""; + nSubFileOffset = 0; + nSubFileSize = 0; + + if( strncmp(pszPath,"/vsisubfile/",12) != 0 ) + return FALSE; + + nSubFileOffset = CPLScanUIntBig(pszPath+12, strlen(pszPath + 12)); + for( i = 12; pszPath[i] != '\0'; i++ ) + { + if( pszPath[i] == '_' && nSubFileSize == 0 ) + { + /* -1 is sometimes passed to mean that we don't know the file size */ + /* for example when creating a JPEG2000 datastream in a NITF file */ + /* Transform it into 0 for correct behaviour of Read(), Write() and Eof() */ + if (pszPath[i + 1] == '-') + nSubFileSize = 0; + else + nSubFileSize = CPLScanUIntBig(pszPath + i + 1, strlen(pszPath + i + 1)); + } + else if( pszPath[i] == ',' ) + { + osFilename = pszPath + i + 1; + return TRUE; + } + else if( pszPath[i] == '/' ) + { + // missing comma! + return FALSE; + } + } + + return FALSE; +} + + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle * +VSISubFileFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess ) + +{ + CPLString osSubFilePath; + vsi_l_offset nOff, nSize; + + if( !DecomposePath( pszFilename, osSubFilePath, nOff, nSize ) ) + { + errno = ENOENT; + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* We can't open the containing file with "w" access, so if tht */ +/* is requested use "r+" instead to update in place. */ +/* -------------------------------------------------------------------- */ + if( pszAccess[0] == 'w' ) + pszAccess = "r+"; + +/* -------------------------------------------------------------------- */ +/* Open the underlying file. */ +/* -------------------------------------------------------------------- */ + VSILFILE *fp = VSIFOpenL( osSubFilePath, pszAccess ); + + if( fp == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Setup the file handle on this file. */ +/* -------------------------------------------------------------------- */ + VSISubFileHandle *poHandle = new VSISubFileHandle; + + poHandle->fp = fp; + poHandle->nSubregionOffset = nOff; + poHandle->nSubregionSize = nSize; + + VSIFSeekL( fp, nOff, SEEK_SET ); + + return poHandle; +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSISubFileFilesystemHandler::Stat( const char * pszFilename, + VSIStatBufL * psStatBuf, + int nFlags ) + +{ + CPLString osSubFilePath; + vsi_l_offset nOff, nSize; + + memset( psStatBuf, 0, sizeof(VSIStatBufL) ); + + if( !DecomposePath( pszFilename, osSubFilePath, nOff, nSize ) ) + { + errno = ENOENT; + return -1; + } + + int nResult = VSIStatExL( osSubFilePath, psStatBuf, nFlags ); + + if( nResult == 0 ) + { + if( nSize != 0 ) + psStatBuf->st_size = nSize; + else + psStatBuf->st_size -= nOff; + } + + return nResult; +} + +/************************************************************************/ +/* Unlink() */ +/************************************************************************/ + +int VSISubFileFilesystemHandler::Unlink( CPL_UNUSED const char * pszFilename ) +{ + errno = EACCES; + return -1; +} + +/************************************************************************/ +/* Mkdir() */ +/************************************************************************/ + +int VSISubFileFilesystemHandler::Mkdir( CPL_UNUSED const char * pszPathname, + CPL_UNUSED long nMode ) +{ + errno = EACCES; + return -1; +} + +/************************************************************************/ +/* Rmdir() */ +/************************************************************************/ + +int VSISubFileFilesystemHandler::Rmdir( CPL_UNUSED const char * pszPathname ) + +{ + errno = EACCES; + return -1; +} + +/************************************************************************/ +/* ReadDir() */ +/************************************************************************/ + +char **VSISubFileFilesystemHandler::ReadDir( CPL_UNUSED const char *pszPath ) +{ + errno = EACCES; + return NULL; +} + +/************************************************************************/ +/* VSIInstallSubFileFilesystemHandler() */ +/************************************************************************/ + +/** + * Install /vsisubfile/ virtual file handler. + * + * This virtual file system handler allows access to subregions of + * files, treating them as a file on their own to the virtual file + * system functions (VSIFOpenL(), etc). + * + * A special form of the filename is used to indicate a subportion + * of another file: + * + * /vsisubfile/[_], + * + * The size parameter is optional. Without it the remainder of the + * file from the start offset as treated as part of the subfile. Otherwise + * only bytes from are treated as part of the subfile. + * The portion may be a relative or absolute path using normal + * rules. The and values are in bytes. + * + * eg. + * /vsisubfile/1000_3000,/data/abc.ntf + * /vsisubfile/5000,../xyz/raw.dat + * + * Unlike the /vsimem/ or conventional file system handlers, there + * is no meaningful support for filesystem operations for creating new + * files, traversing directories, and deleting files within the /vsisubfile/ + * area. Only the VSIStatL(), VSIFOpenL() and operations based on the file + * handle returned by VSIFOpenL() operate properly. + */ + +void VSIInstallSubFileHandler() +{ + VSIFileManager::InstallHandler( "/vsisubfile/", + new VSISubFileFilesystemHandler ); +} + diff --git a/bazaar/plugin/gdal/port/cpl_vsil_tar.cpp b/bazaar/plugin/gdal/port/cpl_vsil_tar.cpp new file mode 100644 index 000000000..b03def9a6 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_tar.cpp @@ -0,0 +1,337 @@ +/****************************************************************************** + * $Id: cpl_vsil_tar.cpp 28588 2015-03-01 20:44:43Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement VSI large file api for tar files (.tar). + * Author: Even Rouault, even.rouault at mines-paris.org + * + ****************************************************************************** + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" + +CPL_CVSID("$Id: cpl_vsil_tar.cpp 28588 2015-03-01 20:44:43Z rouault $"); + + +/************************************************************************/ +/* ==================================================================== */ +/* VSITarEntryFileOffset */ +/* ==================================================================== */ +/************************************************************************/ + +class VSITarEntryFileOffset : public VSIArchiveEntryFileOffset +{ +public: + GUIntBig nOffset; + + VSITarEntryFileOffset(GUIntBig nOffset) + { + this->nOffset = nOffset; + } +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSITarReader */ +/* ==================================================================== */ +/************************************************************************/ + +class VSITarReader : public VSIArchiveReader +{ + private: + VSILFILE* fp; + GUIntBig nCurOffset; + GUIntBig nNextFileSize; + CPLString osNextFileName; + GIntBig nModifiedTime; + + public: + VSITarReader(const char* pszTarFileName); + virtual ~VSITarReader(); + + int IsValid() { return fp != NULL; } + + virtual int GotoFirstFile(); + virtual int GotoNextFile(); + virtual VSIArchiveEntryFileOffset* GetFileOffset() { return new VSITarEntryFileOffset(nCurOffset); } + virtual GUIntBig GetFileSize() { return nNextFileSize; } + virtual CPLString GetFileName() { return osNextFileName; } + virtual GIntBig GetModifiedTime() { return nModifiedTime; } + virtual int GotoFileOffset(VSIArchiveEntryFileOffset* pOffset); +}; + + +/************************************************************************/ +/* VSIIsTGZ() */ +/************************************************************************/ + +static int VSIIsTGZ(const char* pszFilename) +{ + return (!EQUALN(pszFilename, "/vsigzip/", 9) && + ((strlen(pszFilename) > 4 && + EQUALN(pszFilename + strlen(pszFilename) - 4, ".tgz", 4)) || + (strlen(pszFilename) > 7 && + EQUALN(pszFilename + strlen(pszFilename) - 7, ".tar.gz", 7)))); +} + +/************************************************************************/ +/* VSITarReader() */ +/************************************************************************/ + +VSITarReader::VSITarReader(const char* pszTarFileName) +{ + fp = VSIFOpenL(pszTarFileName, "rb"); + nNextFileSize = 0; + nCurOffset = 0; + nModifiedTime = 0; +} + +/************************************************************************/ +/* ~VSITarReader() */ +/************************************************************************/ + +VSITarReader::~VSITarReader() +{ + if (fp) + VSIFCloseL(fp); +} + +/************************************************************************/ +/* GotoNextFile() */ +/************************************************************************/ + +int VSITarReader::GotoNextFile() +{ + char abyHeader[512]; + if (VSIFReadL(abyHeader, 512, 1, fp) != 1) + return FALSE; + + if (abyHeader[99] != '\0' || + abyHeader[107] != '\0' || + abyHeader[115] != '\0' || + abyHeader[123] != '\0' || + (abyHeader[135] != '\0' && abyHeader[135] != ' ') || + (abyHeader[147] != '\0' && abyHeader[147] != ' ')) + { + return FALSE; + } + if( abyHeader[124] < '0' || abyHeader[124] > '7' ) + return FALSE; + + osNextFileName = abyHeader; + nNextFileSize = 0; + int i; + for(i=0;i<11;i++) + nNextFileSize = nNextFileSize * 8 + (abyHeader[124+i] - '0'); + + nModifiedTime = 0; + for(i=0;i<11;i++) + nModifiedTime = nModifiedTime * 8 + (abyHeader[136+i] - '0'); + + nCurOffset = VSIFTellL(fp); + + GUIntBig nBytesToSkip = ((nNextFileSize + 511) / 512) * 512; + if( nBytesToSkip > (~((GUIntBig)0)) - nCurOffset ) + { + CPLError(CE_Failure, CPLE_AppDefined, "Bad .tar structure"); + return FALSE; + } + + VSIFSeekL(fp, nBytesToSkip, SEEK_CUR); + + return TRUE; +} + +/************************************************************************/ +/* GotoFirstFile() */ +/************************************************************************/ + +int VSITarReader::GotoFirstFile() +{ + VSIFSeekL(fp, 0, SEEK_SET); + return GotoNextFile(); +} + +/************************************************************************/ +/* GotoFileOffset() */ +/************************************************************************/ + +int VSITarReader::GotoFileOffset(VSIArchiveEntryFileOffset* pOffset) +{ + VSITarEntryFileOffset* pTarEntryOffset = (VSITarEntryFileOffset*)pOffset; + VSIFSeekL(fp, pTarEntryOffset->nOffset - 512, SEEK_SET); + return GotoNextFile(); +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSITarFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSITarFilesystemHandler : public VSIArchiveFilesystemHandler +{ +public: + virtual const char* GetPrefix() { return "/vsitar"; } + virtual std::vector GetExtensions(); + virtual VSIArchiveReader* CreateReader(const char* pszTarFileName); + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); +}; + + +/************************************************************************/ +/* GetExtensions() */ +/************************************************************************/ + +std::vector VSITarFilesystemHandler::GetExtensions() +{ + std::vector oList; + oList.push_back(".tar.gz"); + oList.push_back(".tar"); + oList.push_back(".tgz"); + return oList; +} + +/************************************************************************/ +/* CreateReader() */ +/************************************************************************/ + +VSIArchiveReader* VSITarFilesystemHandler::CreateReader(const char* pszTarFileName) +{ + CPLString osTarInFileName; + + if (VSIIsTGZ(pszTarFileName)) + { + osTarInFileName = "/vsigzip/"; + osTarInFileName += pszTarFileName; + } + else + osTarInFileName = pszTarFileName; + + VSITarReader* poReader = new VSITarReader(osTarInFileName); + + if (!poReader->IsValid()) + { + delete poReader; + return NULL; + } + + if (!poReader->GotoFirstFile()) + { + delete poReader; + return NULL; + } + + return poReader; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle* VSITarFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess) +{ + char* tarFilename; + CPLString osTarInFileName; + + if (strchr(pszAccess, 'w') != NULL || + strchr(pszAccess, '+') != NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Only read-only mode is supported for /vsitar"); + return NULL; + } + + tarFilename = SplitFilename(pszFilename, osTarInFileName, TRUE); + if (tarFilename == NULL) + return NULL; + + VSIArchiveReader* poReader = OpenArchiveFile(tarFilename, osTarInFileName); + if (poReader == NULL) + { + CPLFree(tarFilename); + return NULL; + } + + CPLString osSubFileName("/vsisubfile/"); + VSITarEntryFileOffset* pOffset = (VSITarEntryFileOffset*) poReader->GetFileOffset(); + osSubFileName += CPLString().Printf(CPL_FRMT_GUIB, pOffset->nOffset); + osSubFileName += "_"; + osSubFileName += CPLString().Printf(CPL_FRMT_GUIB, poReader->GetFileSize()); + osSubFileName += ","; + delete pOffset; + + if (VSIIsTGZ(tarFilename)) + { + osSubFileName += "/vsigzip/"; + osSubFileName += tarFilename; + } + else + osSubFileName += tarFilename; + + delete(poReader); + + CPLFree(tarFilename); + tarFilename = NULL; + + return (VSIVirtualHandle* )VSIFOpenL(osSubFileName, "rb"); +} + +/************************************************************************/ +/* VSIInstallTarFileHandler() */ +/************************************************************************/ + +/** + * \brief Install /vsitar/ file system handler. + * + * A special file handler is installed that allows reading on-the-fly in TAR + * (regular .tar, or compressed .tar.gz/.tgz) archives. + * + * All portions of the file system underneath the base path "/vsitar/" will be + * handled by this driver. + * + * The syntax to open a file inside a zip file is /vsitar/path/to/the/file.tar/path/inside/the/tar/file + * were path/to/the/file.tar is relative or absolute and path/inside/the/tar/file + * is the relative path to the file inside the archive. + * + * If the path is absolute, it should begin with a / on a Unix-like OS (or C:\ on Windows), + * so the line looks like /vsitar//home/gdal/... + * For example gdalinfo /vsitar/myarchive.tar/subdir1/file1.tif + * + * Syntaxic sugar : if the tar archive contains only one file located at its root, + * just mentionning "/vsitar/path/to/the/file.tar" will work + * + * VSIStatL() will return the uncompressed size in st_size member and file + * nature- file or directory - in st_mode member. + * + * Directory listing is available through VSIReadDir(). + * + * @since GDAL 1.8.0 + */ + +void VSIInstallTarFileHandler(void) +{ + VSIFileManager::InstallHandler( "/vsitar/", new VSITarFilesystemHandler() ); +} diff --git a/bazaar/plugin/gdal/port/cpl_vsil_unix_stdio_64.cpp b/bazaar/plugin/gdal/port/cpl_vsil_unix_stdio_64.cpp new file mode 100644 index 000000000..932bc1624 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_unix_stdio_64.cpp @@ -0,0 +1,618 @@ +/********************************************************************** + * $Id: cpl_vsil_unix_stdio_64.cpp 29006 2015-04-25 11:03:10Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement VSI large file api for Unix platforms with fseek64() + * and ftell64() such as IRIX. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * Copyright (c) 2010-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + **************************************************************************** + * + * NB: Note that in wrappers we are always saving the error state (errno + * variable) to avoid side effects during debug prints or other possible + * standard function calls (error states will be overwritten after such + * a call). + * + ****************************************************************************/ + +//#define VSI_COUNT_BYTES_READ + +#include "cpl_port.h" + +#if !defined(WIN32) && !defined(WIN32CE) + +#include "cpl_vsi_virtual.h" +#include "cpl_string.h" +#include "cpl_multiproc.h" + +#include +#include +#include +#include +#include + +CPL_CVSID("$Id: cpl_vsil_unix_stdio_64.cpp 29006 2015-04-25 11:03:10Z rouault $"); + +#if defined(UNIX_STDIO_64) + +#ifndef VSI_FTELL64 +#define VSI_FTELL64 ftell64 +#endif +#ifndef VSI_FSEEK64 +#define VSI_FSEEK64 fseek64 +#endif +#ifndef VSI_FOPEN64 +#define VSI_FOPEN64 fopen64 +#endif +#ifndef VSI_STAT64 +#define VSI_STAT64 stat64 +#endif +#ifndef VSI_STAT64_T +#define VSI_STAT64_T stat64 +#endif +#ifndef VSI_FTRUNCATE64 +#define VSI_FTRUNCATE64 ftruncate64 +#endif + +#else /* not UNIX_STDIO_64 */ + +#ifndef VSI_FTELL64 +#define VSI_FTELL64 ftell +#endif +#ifndef VSI_FSEEK64 +#define VSI_FSEEK64 fseek +#endif +#ifndef VSI_FOPEN64 +#define VSI_FOPEN64 fopen +#endif +#ifndef VSI_STAT64 +#define VSI_STAT64 stat +#endif +#ifndef VSI_STAT64_T +#define VSI_STAT64_T stat +#endif +#ifndef VSI_FTRUNCATE64 +#define VSI_FTRUNCATE64 ftruncate +#endif + +#endif /* ndef UNIX_STDIO_64 */ + +/************************************************************************/ +/* ==================================================================== */ +/* VSIUnixStdioFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIUnixStdioFilesystemHandler : public VSIFilesystemHandler +{ +#ifdef VSI_COUNT_BYTES_READ + vsi_l_offset nTotalBytesRead; + CPLMutex *hMutex; +#endif + +public: + VSIUnixStdioFilesystemHandler(); +#ifdef VSI_COUNT_BYTES_READ + virtual ~VSIUnixStdioFilesystemHandler(); +#endif + + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); + virtual int Unlink( const char *pszFilename ); + virtual int Rename( const char *oldpath, const char *newpath ); + virtual int Mkdir( const char *pszDirname, long nMode ); + virtual int Rmdir( const char *pszDirname ); + virtual char **ReadDir( const char *pszDirname ); + +#ifdef VSI_COUNT_BYTES_READ + void AddToTotal(vsi_l_offset nBytes); +#endif +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIUnixStdioHandle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIUnixStdioHandle : public VSIVirtualHandle +{ + FILE *fp; + vsi_l_offset nOffset; + int bReadOnly; + int bLastOpWrite; + int bLastOpRead; + int bAtEOF; +#ifdef VSI_COUNT_BYTES_READ + vsi_l_offset nTotalBytesRead; + VSIUnixStdioFilesystemHandler *poFS; +#endif + public: + VSIUnixStdioHandle(VSIUnixStdioFilesystemHandler *poFSIn, + FILE* fpIn, int bReadOnlyIn); + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); + virtual int Truncate( vsi_l_offset nNewSize ); + virtual void *GetNativeFileDescriptor() { return (void*) (size_t) fileno(fp); } +}; + + +/************************************************************************/ +/* VSIUnixStdioHandle() */ +/************************************************************************/ + +VSIUnixStdioHandle::VSIUnixStdioHandle( +#ifndef VSI_COUNT_BYTES_READ +CPL_UNUSED +#endif + VSIUnixStdioFilesystemHandler *poFSIn, + FILE* fpIn, int bReadOnlyIn) : + fp(fpIn), nOffset(0), bReadOnly(bReadOnlyIn), bLastOpWrite(FALSE), bLastOpRead(FALSE), bAtEOF(FALSE) +#ifdef VSI_COUNT_BYTES_READ + , nTotalBytesRead(0), poFS(poFSIn) +#endif +{ +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIUnixStdioHandle::Close() + +{ + VSIDebug1( "VSIUnixStdioHandle::Close(%p)", fp ); + +#ifdef VSI_COUNT_BYTES_READ + poFS->AddToTotal(nTotalBytesRead); +#endif + + return fclose( fp ); +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIUnixStdioHandle::Seek( vsi_l_offset nOffset, int nWhence ) +{ + bAtEOF = FALSE; + + // seeks that do nothing are still surprisingly expensive with MSVCRT. + // try and short circuit if possible. + if( nWhence == SEEK_SET && nOffset == this->nOffset ) + return 0; + + // on a read-only file, we can avoid a lseek() system call to be issued + // if the next position to seek to is within the buffered page + if( bReadOnly && nWhence == SEEK_SET ) + { + GIntBig nDiff = (GIntBig)nOffset - (GIntBig)this->nOffset; + if( nDiff > 0 && nDiff < 4096 ) + { + GByte abyTemp[4096]; + int nRead = (int)fread(abyTemp, 1, (int)nDiff, fp); + if( nRead == (int)nDiff ) + { + this->nOffset = nOffset; + bLastOpWrite = FALSE; + bLastOpRead = FALSE; + return 0; + } + } + } + + const int nResult = VSI_FSEEK64( fp, nOffset, nWhence ); + int nError = errno; + +#ifdef VSI_DEBUG + + if( nWhence == SEEK_SET ) + { + VSIDebug3( "VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB ",SEEK_SET) = %d", + fp, nOffset, nResult ); + } + else if( nWhence == SEEK_END ) + { + VSIDebug3( "VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB ",SEEK_END) = %d", + fp, nOffset, nResult ); + } + else if( nWhence == SEEK_CUR ) + { + VSIDebug3( "VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB ",SEEK_CUR) = %d", + fp, nOffset, nResult ); + } + else + { + VSIDebug4( "VSIUnixStdioHandle::Seek(%p," CPL_FRMT_GUIB ",%d-Unknown) = %d", + fp, nOffset, nWhence, nResult ); + } + +#endif + + if( nResult != -1 ) + { + if( nWhence == SEEK_SET ) + { + this->nOffset = nOffset; + } + else if( nWhence == SEEK_END ) + { + this->nOffset = VSI_FTELL64( fp ); + } + else if( nWhence == SEEK_CUR ) + { + this->nOffset += nOffset; + } + } + + bLastOpWrite = FALSE; + bLastOpRead = FALSE; + + errno = nError; + return nResult; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIUnixStdioHandle::Tell() + +{ +#ifdef notdef + vsi_l_offset nOffset = VSI_FTELL64( fp ); + int nError = errno; + + VSIDebug2( "VSIUnixStdioHandle::Tell(%p) = %ld", fp, (long)nOffset ); + + errno = nError; + return nOffset; +#endif + return nOffset; +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSIUnixStdioHandle::Flush() + +{ + VSIDebug1( "VSIUnixStdioHandle::Flush(%p)", fp ); + + return fflush( fp ); +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIUnixStdioHandle::Read( void * pBuffer, size_t nSize, size_t nCount ) + +{ +/* -------------------------------------------------------------------- */ +/* If a fwrite() is followed by an fread(), the POSIX rules are */ +/* that some of the write may still be buffered and lost. We */ +/* are required to do a seek between to force flushing. So we */ +/* keep careful track of what happened last to know if we */ +/* skipped a flushing seek that we may need to do now. */ +/* -------------------------------------------------------------------- */ + if( bLastOpWrite ) + VSI_FSEEK64( fp, nOffset, SEEK_SET ); + +/* -------------------------------------------------------------------- */ +/* Perform the read. */ +/* -------------------------------------------------------------------- */ + size_t nResult = fread( pBuffer, nSize, nCount, fp ); + int nError = errno; + + VSIDebug4( "VSIUnixStdioHandle::Read(%p,%ld,%ld) = %ld", + fp, (long)nSize, (long)nCount, (long)nResult ); + + errno = nError; + +/* -------------------------------------------------------------------- */ +/* Update current offset. */ +/* -------------------------------------------------------------------- */ + +#ifdef VSI_COUNT_BYTES_READ + nTotalBytesRead += nSize * nResult; +#endif + + nOffset += nSize * nResult; + bLastOpWrite = FALSE; + bLastOpRead = TRUE; + + if (nResult != nCount) + { + errno = 0; + vsi_l_offset nNewOffset = VSI_FTELL64( fp ); + if( errno == 0 ) /* ftell() can fail if we are end of file with a pipe */ + nOffset = nNewOffset; + else + CPLDebug("VSI", "%s", VSIStrerror(errno)); + bAtEOF = feof(fp); + } + + return nResult; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIUnixStdioHandle::Write( const void * pBuffer, size_t nSize, + size_t nCount ) + +{ +/* -------------------------------------------------------------------- */ +/* If a fwrite() is followed by an fread(), the POSIX rules are */ +/* that some of the write may still be buffered and lost. We */ +/* are required to do a seek between to force flushing. So we */ +/* keep careful track of what happened last to know if we */ +/* skipped a flushing seek that we may need to do now. */ +/* -------------------------------------------------------------------- */ + if( bLastOpRead ) + VSI_FSEEK64( fp, nOffset, SEEK_SET ); + +/* -------------------------------------------------------------------- */ +/* Perform the write. */ +/* -------------------------------------------------------------------- */ + size_t nResult = fwrite( pBuffer, nSize, nCount, fp ); + int nError = errno; + + VSIDebug4( "VSIUnixStdioHandle::Write(%p,%ld,%ld) = %ld", + fp, (long)nSize, (long)nCount, (long)nResult ); + + errno = nError; + +/* -------------------------------------------------------------------- */ +/* Update current offset. */ +/* -------------------------------------------------------------------- */ + nOffset += nSize * nResult; + bLastOpWrite = TRUE; + bLastOpRead = FALSE; + + return nResult; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSIUnixStdioHandle::Eof() + +{ + if( bAtEOF ) + return 1; + else + return 0; +} + +/************************************************************************/ +/* Truncate() */ +/************************************************************************/ + +int VSIUnixStdioHandle::Truncate( vsi_l_offset nNewSize ) +{ + fflush(fp); + int nRet = VSI_FTRUNCATE64(fileno(fp), nNewSize); + return nRet; +} + + +/************************************************************************/ +/* ==================================================================== */ +/* VSIUnixStdioFilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* VSIUnixStdioFilesystemHandler() */ +/************************************************************************/ + +VSIUnixStdioFilesystemHandler::VSIUnixStdioFilesystemHandler() +#ifdef VSI_COUNT_BYTES_READ + : nTotalBytesRead(0), hMutex(NULL) +#endif +{ +} + +#ifdef VSI_COUNT_BYTES_READ +/************************************************************************/ +/* ~VSIUnixStdioFilesystemHandler() */ +/************************************************************************/ + +VSIUnixStdioFilesystemHandler::~VSIUnixStdioFilesystemHandler() +{ + CPLDebug( "VSI", + "~VSIUnixStdioFilesystemHandler() : nTotalBytesRead = " CPL_FRMT_GUIB, + nTotalBytesRead ); + + if( hMutex != NULL ) + CPLDestroyMutex( hMutex ); + hMutex = NULL; +} +#endif + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle * +VSIUnixStdioFilesystemHandler::Open( const char *pszFilename, + const char *pszAccess ) + +{ + FILE *fp = VSI_FOPEN64( pszFilename, pszAccess ); + int nError = errno; + + VSIDebug3( "VSIUnixStdioFilesystemHandler::Open(\"%s\",\"%s\") = %p", + pszFilename, pszAccess, fp ); + + if( fp == NULL ) + { + errno = nError; + return NULL; + } + + int bReadOnly = strcmp(pszAccess, "rb") == 0 || strcmp(pszAccess, "r") == 0; + VSIUnixStdioHandle *poHandle = new VSIUnixStdioHandle(this, fp, bReadOnly ); + + errno = nError; + +/* -------------------------------------------------------------------- */ +/* If VSI_CACHE is set we want to use a cached reader instead */ +/* of more direct io on the underlying file. */ +/* -------------------------------------------------------------------- */ + if( bReadOnly + && CSLTestBoolean( CPLGetConfigOption( "VSI_CACHE", "FALSE" ) ) ) + { + return VSICreateCachedFile( poHandle ); + } + else + { + return poHandle; + } +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSIUnixStdioFilesystemHandler::Stat( const char * pszFilename, + VSIStatBufL * pStatBuf, + CPL_UNUSED int nFlags) +{ + return( VSI_STAT64( pszFilename, pStatBuf ) ); +} + +/************************************************************************/ +/* Unlink() */ +/************************************************************************/ + +int VSIUnixStdioFilesystemHandler::Unlink( const char * pszFilename ) + +{ + return unlink( pszFilename ); +} + +/************************************************************************/ +/* Rename() */ +/************************************************************************/ + +int VSIUnixStdioFilesystemHandler::Rename( const char *oldpath, + const char *newpath ) + +{ + return rename( oldpath, newpath ); +} + +/************************************************************************/ +/* Mkdir() */ +/************************************************************************/ + +int VSIUnixStdioFilesystemHandler::Mkdir( const char * pszPathname, + long nMode ) + +{ + return mkdir( pszPathname, nMode ); +} + +/************************************************************************/ +/* Rmdir() */ +/************************************************************************/ + +int VSIUnixStdioFilesystemHandler::Rmdir( const char * pszPathname ) + +{ + return rmdir( pszPathname ); +} + +/************************************************************************/ +/* ReadDir() */ +/************************************************************************/ + +char **VSIUnixStdioFilesystemHandler::ReadDir( const char *pszPath ) + +{ + DIR *hDir; + struct dirent *psDirEntry; + CPLStringList oDir; + + if (strlen(pszPath) == 0) + pszPath = "."; + + if ( (hDir = opendir(pszPath)) != NULL ) + { + // we want to avoid returning NULL for an empty list. + oDir.Assign( (char**) CPLCalloc(2,sizeof(char*)) ); + + while( (psDirEntry = readdir(hDir)) != NULL ) + oDir.AddString( psDirEntry->d_name ); + + closedir( hDir ); + } + else + { + /* Should we generate an error??? + * For now we'll just return NULL (at the end of the function) + */ + } + + return oDir.StealList(); +} + +#ifdef VSI_COUNT_BYTES_READ +/************************************************************************/ +/* AddToTotal() */ +/************************************************************************/ + +void VSIUnixStdioFilesystemHandler::AddToTotal(vsi_l_offset nBytes) +{ + CPLMutexHolder oHolder(&hMutex); + nTotalBytesRead += nBytes; +} + +#endif + +/************************************************************************/ +/* VSIInstallLargeFileHandler() */ +/************************************************************************/ + +void VSIInstallLargeFileHandler() + +{ + VSIFileManager::InstallHandler( "", new VSIUnixStdioFilesystemHandler() ); +} + +#endif /* ndef WIN32 */ diff --git a/bazaar/plugin/gdal/port/cpl_vsil_win32.cpp b/bazaar/plugin/gdal/port/cpl_vsil_win32.cpp new file mode 100644 index 000000000..1b6b5a4b5 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsil_win32.cpp @@ -0,0 +1,693 @@ +/********************************************************************** + * $Id: cpl_vsil_win32.cpp 27491 2014-07-05 11:24:48Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement VSI large file api for Win32. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2008-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_vsi_virtual.h" + +CPL_CVSID("$Id: cpl_vsil_win32.cpp 27491 2014-07-05 11:24:48Z rouault $"); + +#if defined(WIN32) + +#include +#include "cpl_string.h" + + +#if !defined(WIN32CE) +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +# include "cpl_win32ce_api.h" +#endif + + +/************************************************************************/ +/* ==================================================================== */ +/* VSIWin32FilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIWin32FilesystemHandler : public VSIFilesystemHandler +{ +public: + virtual VSIVirtualHandle *Open( const char *pszFilename, + const char *pszAccess); + virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags ); + virtual int Unlink( const char *pszFilename ); + virtual int Rename( const char *oldpath, const char *newpath ); + virtual int Mkdir( const char *pszDirname, long nMode ); + virtual int Rmdir( const char *pszDirname ); + virtual char **ReadDir( const char *pszDirname ); + virtual int IsCaseSensitive( const char* pszFilename ) + { (void) pszFilename; return FALSE; } +}; + +/************************************************************************/ +/* ==================================================================== */ +/* VSIWin32Handle */ +/* ==================================================================== */ +/************************************************************************/ + +class VSIWin32Handle : public VSIVirtualHandle +{ + public: + HANDLE hFile; + int bEOF; + + virtual int Seek( vsi_l_offset nOffset, int nWhence ); + virtual vsi_l_offset Tell(); + virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb ); + virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ); + virtual int Eof(); + virtual int Flush(); + virtual int Close(); + virtual int Truncate( vsi_l_offset nNewSize ); + virtual void *GetNativeFileDescriptor() { return (void*) hFile; } +}; + +/************************************************************************/ +/* ErrnoFromGetLastError() */ +/* */ +/* Private function translating Windows API error codes to POSIX errno. */ +/* */ +/* TODO: If the function is going to be public CPL function, then */ +/* replace the switch with array of (Win32 code, errno code) tuples and */ +/* complement it with missing codes. */ +/************************************************************************/ + +static int ErrnoFromGetLastError() +{ + int err = 0; + DWORD dwError = GetLastError(); + + switch( dwError ) + { + case NO_ERROR: + err = 0; + break; + case ERROR_FILE_NOT_FOUND: /* Cannot find the file specified. */ + case ERROR_PATH_NOT_FOUND: /* Cannot find the path specified. */ + case ERROR_INVALID_DRIVE: /* Cannot find the drive specified. */ + case ERROR_NO_MORE_FILES: /* There are no more files. */ + case ERROR_BAD_PATHNAME: /* The specified path is invalid. */ + case ERROR_BAD_NETPATH: /* The network path was not found. */ + case ERROR_FILENAME_EXCED_RANGE:/* The filename or extension is too long. */ + err = ENOENT; + break; + case ERROR_TOO_MANY_OPEN_FILES: /* The system cannot open the file. */ + err = EMFILE; + break; + case ERROR_ACCESS_DENIED: /* Access denied. */ + case ERROR_CURRENT_DIRECTORY: /* The directory cannot be removed. */ + case ERROR_WRITE_PROTECT: /* The media is write protected. */ + case ERROR_LOCK_VIOLATION: /* Another process has locked a portion of the file. */ + case ERROR_WRONG_DISK: /* The wrong diskette is in the drive. */ + case ERROR_SHARING_BUFFER_EXCEEDED: /* Too many files opened for sharing. */ + case ERROR_DRIVE_LOCKED: /* The disk is in use or locked by another process. */ + case ERROR_LOCK_FAILED: /* Unable to lock a region of a file. */ + case ERROR_SEEK_ON_DEVICE: /* The file pointer cannot be set on the specified device or file. */ + err = EACCES; + break; + case ERROR_INVALID_HANDLE: /* The handle is invalid. */ + case ERROR_INVALID_TARGET_HANDLE: /* The target internal file identifier is incorrect. */ + case ERROR_DIRECT_ACCESS_HANDLE: /* Operation other than raw disk I/O not permitted. */ + err = EBADF; + break; + case ERROR_ARENA_TRASHED: /* The storage control blocks were destroyed. */ + case ERROR_NOT_ENOUGH_MEMORY: /* Not enough storage is available. */ + case ERROR_INVALID_BLOCK: /* The storage control block address is invalid. */ + case ERROR_NOT_ENOUGH_QUOTA: /* Not enough quota is available to process this command. */ + err = ENOMEM; + break; + case ERROR_BAD_ENVIRONMENT: /* The environment is incorrect. */ + err = E2BIG; + break; + case ERROR_INVALID_ACCESS: /* The access code is invalid. */ + case ERROR_INVALID_DATA: /* The data is invalid. */ + err = EINVAL; + break; + case ERROR_NOT_SAME_DEVICE: /* The system cannot move the file to a different disk drive. */ + err = EXDEV; + break; + case ERROR_DIR_NOT_EMPTY: /* The directory is not empty. */ + err = ENOTEMPTY; + break; + case ERROR_FILE_EXISTS: /* The file exists. */ + case ERROR_ALREADY_EXISTS: /* Cannot create a file when that file already exists. */ + err = EEXIST; + break; + case ERROR_DISK_FULL: /* There is not enough space on the disk. */ + err = ENOSPC; + break; + case ERROR_HANDLE_EOF: /* Reached the end of the file. */ + err = 0; /* There is no errno quivalent, in the errno.h */ + break; + default: + err = 0; + } + CPLAssert( 0 <= err ); + + return err; +} + +/************************************************************************/ +/* Close() */ +/************************************************************************/ + +int VSIWin32Handle::Close() + +{ + return CloseHandle( hFile ) ? 0 : -1; +} + +/************************************************************************/ +/* Seek() */ +/************************************************************************/ + +int VSIWin32Handle::Seek( vsi_l_offset nOffset, int nWhence ) + +{ + GUInt32 dwMoveMethod, dwMoveHigh; + GUInt32 nMoveLow; + LARGE_INTEGER li; + + bEOF = FALSE; + + switch(nWhence) + { + case SEEK_CUR: + dwMoveMethod = FILE_CURRENT; + break; + case SEEK_END: + dwMoveMethod = FILE_END; + break; + case SEEK_SET: + default: + dwMoveMethod = FILE_BEGIN; + break; + } + + li.QuadPart = nOffset; + nMoveLow = li.LowPart; + dwMoveHigh = li.HighPart; + + SetLastError( 0 ); + SetFilePointer(hFile, (LONG) nMoveLow, (PLONG)&dwMoveHigh, + dwMoveMethod); + + if( GetLastError() != NO_ERROR ) + { +#ifdef notdef + LPVOID lpMsgBuf = NULL; + + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, 0, NULL ); + + printf( "[ERROR %d]\n %s\n", GetLastError(), (char *) lpMsgBuf ); + printf( "nOffset=%u, nMoveLow=%u, dwMoveHigh=%u\n", + (GUInt32) nOffset, nMoveLow, dwMoveHigh ); +#endif + errno = ErrnoFromGetLastError(); + return -1; + } + else + return 0; +} + +/************************************************************************/ +/* Tell() */ +/************************************************************************/ + +vsi_l_offset VSIWin32Handle::Tell() + +{ + LARGE_INTEGER li; + + li.HighPart = 0; + li.LowPart = SetFilePointer( hFile, 0, (PLONG) &(li.HighPart), + FILE_CURRENT ); + + return (static_cast(li.QuadPart)); +} + +/************************************************************************/ +/* Flush() */ +/************************************************************************/ + +int VSIWin32Handle::Flush() + +{ + /* Nothing needed to offer the same guarantee as POSIX fflush() */ + /* FlushFileBuffers() would be closer to fsync() */ + /* See http://trac.osgeo.org/gdal/ticket/5556 */ + return 0; +} + +/************************************************************************/ +/* Read() */ +/************************************************************************/ + +size_t VSIWin32Handle::Read( void * pBuffer, size_t nSize, size_t nCount ) + +{ + DWORD dwSizeRead; + size_t nResult; + + if( !ReadFile( hFile, pBuffer, (DWORD)(nSize*nCount), &dwSizeRead, NULL ) ) + { + nResult = 0; + errno = ErrnoFromGetLastError(); + } + else if( nSize == 0 ) + nResult = 0; + else + nResult = dwSizeRead / nSize; + + if( nResult != nCount ) + bEOF = TRUE; + + return nResult; +} + +/************************************************************************/ +/* Write() */ +/************************************************************************/ + +size_t VSIWin32Handle::Write( const void *pBuffer, size_t nSize, size_t nCount) + +{ + DWORD dwSizeWritten; + size_t nResult; + + if( !WriteFile(hFile, (void *)pBuffer, + (DWORD)(nSize*nCount),&dwSizeWritten,NULL) ) + { + nResult = 0; + errno = ErrnoFromGetLastError(); + } + else if( nSize == 0) + nResult = 0; + else + nResult = dwSizeWritten / nSize; + + return nResult; +} + +/************************************************************************/ +/* Eof() */ +/************************************************************************/ + +int VSIWin32Handle::Eof() + +{ + return bEOF; +} + +/************************************************************************/ +/* Truncate() */ +/************************************************************************/ + +int VSIWin32Handle::Truncate( vsi_l_offset nNewSize ) +{ + vsi_l_offset nCur = Tell(); + Seek( nNewSize, SEEK_SET ); + BOOL bRes = SetEndOfFile( hFile ); + Seek( nCur, SEEK_SET ); + + if (bRes) + return 0; + else + return -1; +} + +/************************************************************************/ +/* ==================================================================== */ +/* VSIWin32FilesystemHandler */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +VSIVirtualHandle *VSIWin32FilesystemHandler::Open( const char *pszFilename, + const char *pszAccess ) + +{ + DWORD dwDesiredAccess, dwCreationDisposition, dwFlagsAndAttributes; + HANDLE hFile; + + if( strchr(pszAccess, '+') != NULL || + strchr(pszAccess, 'w') != 0 || + strchr(pszAccess, 'a') != 0 ) + dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; + else + dwDesiredAccess = GENERIC_READ; + + if( strstr(pszAccess, "w") != NULL ) + dwCreationDisposition = CREATE_ALWAYS; + else if( strstr(pszAccess, "a") != NULL ) + dwCreationDisposition = OPEN_ALWAYS; + else + dwCreationDisposition = OPEN_EXISTING; + + dwFlagsAndAttributes = (dwDesiredAccess == GENERIC_READ) ? + FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL; + +/* -------------------------------------------------------------------- */ +/* On Win32 consider treating the filename as utf-8 and */ +/* converting to wide characters to open. */ +/* -------------------------------------------------------------------- */ +#ifndef WIN32CE + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + wchar_t *pwszFilename = + CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + hFile = CreateFileW( pwszFilename, dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, dwCreationDisposition, dwFlagsAndAttributes, + NULL ); + CPLFree( pwszFilename ); + } + else + { + hFile = CreateFile( pszFilename, dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, dwCreationDisposition, dwFlagsAndAttributes, + NULL ); + } + +/* -------------------------------------------------------------------- */ +/* On WinCE we only support plain ascii filenames. */ +/* -------------------------------------------------------------------- */ +#else /* def WIN32CE */ + hFile = CE_CreateFileA( pszFilename, dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL ); +#endif + + if( hFile == INVALID_HANDLE_VALUE ) + { + errno = ErrnoFromGetLastError(); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Create a VSI file handle. */ +/* -------------------------------------------------------------------- */ + VSIWin32Handle *poHandle = new VSIWin32Handle; + + poHandle->hFile = hFile; + poHandle->bEOF = FALSE; + + if (strchr(pszAccess, 'a') != 0) + poHandle->Seek(0, SEEK_END); + +/* -------------------------------------------------------------------- */ +/* If VSI_CACHE is set we want to use a cached reader instead */ +/* of more direct io on the underlying file. */ +/* -------------------------------------------------------------------- */ + if( (EQUAL(pszAccess,"r") || EQUAL(pszAccess,"rb")) + && CSLTestBoolean( CPLGetConfigOption( "VSI_CACHE", "FALSE" ) ) ) + { + return VSICreateCachedFile( poHandle ); + } + else + { + return poHandle; + } +} + +/************************************************************************/ +/* Stat() */ +/************************************************************************/ + +int VSIWin32FilesystemHandler::Stat( const char * pszFilename, + VSIStatBufL * pStatBuf, + int nFlags ) + +{ + (void) nFlags; + +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + int nResult; + wchar_t *pwszFilename = + CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + nResult = _wstat64( pwszFilename, pStatBuf ); + CPLFree( pwszFilename ); + + return nResult; + } + else +#endif + { + return( VSI_STAT64( pszFilename, pStatBuf ) ); + } +} + +/************************************************************************/ + +/* Unlink() */ +/************************************************************************/ + +int VSIWin32FilesystemHandler::Unlink( const char * pszFilename ) + +{ +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + int nResult; + wchar_t *pwszFilename = + CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + nResult = _wunlink( pwszFilename ); + CPLFree( pwszFilename ); + return nResult; + } + else +#endif + { + return unlink( pszFilename ); + } +} + +/************************************************************************/ +/* Rename() */ +/************************************************************************/ + +int VSIWin32FilesystemHandler::Rename( const char *oldpath, + const char *newpath ) + +{ +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + int nResult; + wchar_t *pwszOldPath = + CPLRecodeToWChar( oldpath, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + wchar_t *pwszNewPath = + CPLRecodeToWChar( newpath, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + nResult = _wrename( pwszOldPath, pwszNewPath ); + CPLFree( pwszOldPath ); + CPLFree( pwszNewPath ); + return nResult; + } + else +#endif + { + return rename( oldpath, newpath ); + } +} + +/************************************************************************/ +/* Mkdir() */ +/************************************************************************/ + +int VSIWin32FilesystemHandler::Mkdir( const char * pszPathname, + long nMode ) + +{ + (void) nMode; +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + int nResult; + wchar_t *pwszFilename = + CPLRecodeToWChar( pszPathname, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + nResult = _wmkdir( pwszFilename ); + CPLFree( pwszFilename ); + return nResult; + } + else +#endif + { + return mkdir( pszPathname ); + } +} + +/************************************************************************/ +/* Rmdir() */ +/************************************************************************/ + +int VSIWin32FilesystemHandler::Rmdir( const char * pszPathname ) + +{ +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + int nResult; + wchar_t *pwszFilename = + CPLRecodeToWChar( pszPathname, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + nResult = _wrmdir( pwszFilename ); + CPLFree( pwszFilename ); + return nResult; + } + else +#endif + { + return rmdir( pszPathname ); + } +} + +/************************************************************************/ +/* ReadDir() */ +/************************************************************************/ + +char **VSIWin32FilesystemHandler::ReadDir( const char *pszPath ) + +{ +#if (defined(WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601 + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + struct _wfinddata_t c_file; + intptr_t hFile; + char *pszFileSpec; + CPLStringList oDir; + + if (strlen(pszPath) == 0) + pszPath = "."; + + pszFileSpec = CPLStrdup(CPLSPrintf("%s\\*.*", pszPath)); + wchar_t *pwszFileSpec = + CPLRecodeToWChar( pszFileSpec, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + if ( (hFile = _wfindfirst( pwszFileSpec, &c_file )) != -1L ) + { + do + { + oDir.AddStringDirectly( + CPLRecodeFromWChar(c_file.name,CPL_ENC_UCS2,CPL_ENC_UTF8)); + } while( _wfindnext( hFile, &c_file ) == 0 ); + + _findclose( hFile ); + } + else + { + /* Should we generate an error??? + * For now we'll just return NULL (at the end of the function) + */ + } + + CPLFree(pszFileSpec); + CPLFree(pwszFileSpec); + + return oDir.StealList(); + } + else +#endif + { + struct _finddata_t c_file; + intptr_t hFile; + char *pszFileSpec; + CPLStringList oDir; + + if (strlen(pszPath) == 0) + pszPath = "."; + + pszFileSpec = CPLStrdup(CPLSPrintf("%s\\*.*", pszPath)); + + if ( (hFile = _findfirst( pszFileSpec, &c_file )) != -1L ) + { + do + { + oDir.AddString(c_file.name); + } while( _findnext( hFile, &c_file ) == 0 ); + + _findclose( hFile ); + } + else + { + /* Should we generate an error??? + * For now we'll just return NULL (at the end of the function) + */ + } + + CPLFree(pszFileSpec); + + return oDir.StealList(); + } +} + +/************************************************************************/ +/* VSIInstallLargeFileHandler() */ +/************************************************************************/ + +void VSIInstallLargeFileHandler() + +{ + VSIFileManager::InstallHandler( "", new VSIWin32FilesystemHandler ); +} + +#endif /* def WIN32 */ + + diff --git a/bazaar/plugin/gdal/port/cpl_vsisimple.cpp b/bazaar/plugin/gdal/port/cpl_vsisimple.cpp new file mode 100644 index 000000000..e012c291b --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_vsisimple.cpp @@ -0,0 +1,1033 @@ +/****************************************************************************** + * $Id: cpl_vsisimple.cpp 28971 2015-04-21 20:40:44Z rouault $ + * + * Project: Common Portability Library + * Purpose: Simple implementation of POSIX VSI functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1998, Frank Warmerdam + * Copyright (c) 2008-2012, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + **************************************************************************** + * + * NB: Note that in wrappers we are always saving the error state (errno + * variable) to avoid side effects during debug prints or other possible + * standard function calls (error states will be overwritten after such + * a call). + * + ****************************************************************************/ + +#include "cpl_config.h" +#include "cpl_port.h" +#include "cpl_vsi.h" +#include "cpl_error.h" +#include "cpl_string.h" + +/* Uncomment to check consistent usage of VSIMalloc(), VSIRealloc(), */ +/* VSICalloc(), VSIFree(), VSIStrdup() */ +//#define DEBUG_VSIMALLOC + +/* Uncomment to compute memory usage statistics. */ +/* DEBUG_VSIMALLOC must also be defined */ +//#define DEBUG_VSIMALLOC_STATS + +/* Highly experimental, and likely buggy. Do not use, except for fixing it! */ +/* DEBUG_VSIMALLOC must also be defined */ +//#define DEBUG_VSIMALLOC_MPROTECT + +#ifdef DEBUG_VSIMALLOC_MPROTECT +#include +#endif + +/* Uncomment to print every memory allocation or deallocation. */ +/* DEBUG_VSIMALLOC must also be defined */ +//#define DEBUG_VSIMALLOC_VERBOSE + +CPL_CVSID("$Id: cpl_vsisimple.cpp 28971 2015-04-21 20:40:44Z rouault $"); + +/* for stat() */ + +/* Unix or Windows NT/2000/XP */ +#if !defined(WIN32) && !defined(WIN32CE) +# include +#elif !defined(WIN32CE) /* not Win32 platform */ +# include +# include +# include +#endif + +/* Windows CE or other platforms */ +#if defined(WIN32CE) +# include +# include +# include +# include +# include +# define time wceex_time +#else +# include +# include +#endif + +/************************************************************************/ +/* VSIFOpen() */ +/************************************************************************/ + +FILE *VSIFOpen( const char * pszFilename, const char * pszAccess ) + +{ + FILE *fp = NULL; + int nError; + +#if defined(WIN32) && !defined(WIN32CE) + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + wchar_t *pwszFilename = + CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + wchar_t *pwszAccess = + CPLRecodeToWChar( pszAccess, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + fp = _wfopen( pwszFilename, pwszAccess ); + + CPLFree( pwszFilename ); + CPLFree( pwszAccess ); + } + else +#endif + fp = fopen( (char *) pszFilename, (char *) pszAccess ); + + nError = errno; + VSIDebug3( "VSIFOpen(%s,%s) = %p", pszFilename, pszAccess, fp ); + errno = nError; + + return( fp ); +} + +/************************************************************************/ +/* VSIFClose() */ +/************************************************************************/ + +int VSIFClose( FILE * fp ) + +{ + VSIDebug1( "VSIClose(%p)", fp ); + + return( fclose(fp) ); +} + +/************************************************************************/ +/* VSIFSeek() */ +/************************************************************************/ + +int VSIFSeek( FILE * fp, long nOffset, int nWhence ) + +{ + int nResult = fseek( fp, nOffset, nWhence ); + int nError = errno; + +#ifdef VSI_DEBUG + if( nWhence == SEEK_SET ) + { + VSIDebug3( "VSIFSeek(%p,%ld,SEEK_SET) = %d", fp, nOffset, nResult ); + } + else if( nWhence == SEEK_END ) + { + VSIDebug3( "VSIFSeek(%p,%ld,SEEK_END) = %d", fp, nOffset, nResult ); + } + else if( nWhence == SEEK_CUR ) + { + VSIDebug3( "VSIFSeek(%p,%ld,SEEK_CUR) = %d", fp, nOffset, nResult ); + } + else + { + VSIDebug4( "VSIFSeek(%p,%ld,%d-Unknown) = %d", + fp, nOffset, nWhence, nResult ); + } +#endif + + errno = nError; + return nResult; +} + +/************************************************************************/ +/* VSIFTell() */ +/************************************************************************/ + +long VSIFTell( FILE * fp ) + +{ + long nOffset = ftell(fp); + int nError = errno; + + VSIDebug2( "VSIFTell(%p) = %ld", fp, nOffset ); + + errno = nError; + return nOffset; +} + +/************************************************************************/ +/* VSIRewind() */ +/************************************************************************/ + +void VSIRewind( FILE * fp ) + +{ + VSIDebug1("VSIRewind(%p)", fp ); + rewind( fp ); +} + +/************************************************************************/ +/* VSIFRead() */ +/************************************************************************/ + +size_t VSIFRead( void * pBuffer, size_t nSize, size_t nCount, FILE * fp ) + +{ + size_t nResult = fread( pBuffer, nSize, nCount, fp ); + int nError = errno; + + VSIDebug4( "VSIFRead(%p,%ld,%ld) = %ld", + fp, (long)nSize, (long)nCount, (long)nResult ); + + errno = nError; + return nResult; +} + +/************************************************************************/ +/* VSIFWrite() */ +/************************************************************************/ + +size_t VSIFWrite( const void *pBuffer, size_t nSize, size_t nCount, FILE * fp ) + +{ + size_t nResult = fwrite( pBuffer, nSize, nCount, fp ); + int nError = errno; + + VSIDebug4( "VSIFWrite(%p,%ld,%ld) = %ld", + fp, (long)nSize, (long)nCount, (long)nResult ); + + errno = nError; + return nResult; +} + +/************************************************************************/ +/* VSIFFlush() */ +/************************************************************************/ + +void VSIFFlush( FILE * fp ) + +{ + VSIDebug1( "VSIFFlush(%p)", fp ); + fflush( fp ); +} + +/************************************************************************/ +/* VSIFGets() */ +/************************************************************************/ + +char *VSIFGets( char *pszBuffer, int nBufferSize, FILE * fp ) + +{ + return( fgets( pszBuffer, nBufferSize, fp ) ); +} + +/************************************************************************/ +/* VSIFGetc() */ +/************************************************************************/ + +int VSIFGetc( FILE * fp ) + +{ + return( fgetc( fp ) ); +} + +/************************************************************************/ +/* VSIUngetc() */ +/************************************************************************/ + +int VSIUngetc( int c, FILE * fp ) + +{ + return( ungetc( c, fp ) ); +} + +/************************************************************************/ +/* VSIFPrintf() */ +/* */ +/* This is a little more complicated than just calling */ +/* fprintf() because of the variable arguments. Instead we */ +/* have to use vfprintf(). */ +/************************************************************************/ + +int VSIFPrintf( FILE * fp, const char * pszFormat, ... ) + +{ + va_list args; + int nReturn; + + va_start( args, pszFormat ); + nReturn = vfprintf( fp, pszFormat, args ); + va_end( args ); + + return( nReturn ); +} + +/************************************************************************/ +/* VSIFEof() */ +/************************************************************************/ + +int VSIFEof( FILE * fp ) + +{ + return( feof( fp ) ); +} + +/************************************************************************/ +/* VSIFPuts() */ +/************************************************************************/ + +int VSIFPuts( const char * pszString, FILE * fp ) + +{ + return fputs( pszString, fp ); +} + +/************************************************************************/ +/* VSIFPutc() */ +/************************************************************************/ + +int VSIFPutc( int nChar, FILE * fp ) + +{ + return( fputc( nChar, fp ) ); +} + + +#ifdef DEBUG_VSIMALLOC_STATS +#include "cpl_multiproc.h" + +static CPLMutex* hMemStatMutex = 0; +static size_t nCurrentTotalAllocs = 0; +static size_t nMaxTotalAllocs = 0; +static GUIntBig nVSIMallocs = 0; +static GUIntBig nVSICallocs = 0; +static GUIntBig nVSIReallocs = 0; +static GUIntBig nVSIFrees = 0; + +/*size_t GetMaxTotalAllocs() +{ + return nMaxTotalAllocs; +}*/ + +/************************************************************************/ +/* VSIShowMemStats() */ +/************************************************************************/ + +void VSIShowMemStats() +{ + char* pszShowMemStats = getenv("CPL_SHOW_MEM_STATS"); + if (pszShowMemStats == NULL || pszShowMemStats[0] == '\0' ) + return; + printf("Current VSI memory usage : " CPL_FRMT_GUIB " bytes\n", + (GUIntBig)nCurrentTotalAllocs); + printf("Maximum VSI memory usage : " CPL_FRMT_GUIB " bytes\n", + (GUIntBig)nMaxTotalAllocs); + printf("Number of calls to VSIMalloc() : " CPL_FRMT_GUIB "\n", + nVSIMallocs); + printf("Number of calls to VSICalloc() : " CPL_FRMT_GUIB "\n", + nVSICallocs); + printf("Number of calls to VSIRealloc() : " CPL_FRMT_GUIB "\n", + nVSIReallocs); + printf("Number of calls to VSIFree() : " CPL_FRMT_GUIB "\n", + nVSIFrees); + printf("VSIMalloc + VSICalloc - VSIFree : " CPL_FRMT_GUIB "\n", + nVSIMallocs + nVSICallocs - nVSIFrees); +} +#endif + +#ifdef DEBUG_VSIMALLOC +static GIntBig nMaxPeakAllocSize = -1; +static GIntBig nMaxCumulAllocSize = -1; +#endif + +/************************************************************************/ +/* VSICalloc() */ +/************************************************************************/ + +void *VSICalloc( size_t nCount, size_t nSize ) + +{ +#ifdef DEBUG_VSIMALLOC + size_t nMul = nCount * nSize; + if (nCount != 0 && nMul / nCount != nSize) + { + fprintf(stderr, "Overflow in VSICalloc(%d, %d)\n", + (int)nCount, (int)nSize); + return NULL; + } + if (nMaxPeakAllocSize < 0) + { + char* pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE"); + nMaxPeakAllocSize = (pszMaxPeakAllocSize) ? atoi(pszMaxPeakAllocSize) : 0; + char* pszMaxCumulAllocSize = getenv("CPL_MAX_CUMUL_ALLOC_SIZE"); + nMaxCumulAllocSize = (pszMaxCumulAllocSize) ? atoi(pszMaxCumulAllocSize) : 0; + } + if (nMaxPeakAllocSize > 0 && (GIntBig)nMul > nMaxPeakAllocSize) + return NULL; +#ifdef DEBUG_VSIMALLOC_STATS + if (nMaxCumulAllocSize > 0 && (GIntBig)nCurrentTotalAllocs + (GIntBig)nMul > nMaxCumulAllocSize) + return NULL; +#endif + +#ifdef DEBUG_VSIMALLOC_MPROTECT + char* ptr = NULL; + size_t nPageSize = getpagesize(); + posix_memalign((void**)&ptr, nPageSize, (3 * sizeof(void*) + nMul + nPageSize - 1) & ~(nPageSize - 1)); + if (ptr == NULL) + return NULL; + memset(ptr + 2 * sizeof(void*), 0, nMul); +#else + char* ptr = (char*) calloc(1, 3 * sizeof(void*) + nMul); + if (ptr == NULL) + return NULL; +#endif + + ptr[0] = 'V'; + ptr[1] = 'S'; + ptr[2] = 'I'; + ptr[3] = 'M'; + memcpy(ptr + sizeof(void*), &nMul, sizeof(void*)); + ptr[2 * sizeof(void*) + nMul + 0] = 'E'; + ptr[2 * sizeof(void*) + nMul + 1] = 'V'; + ptr[2 * sizeof(void*) + nMul + 2] = 'S'; + ptr[2 * sizeof(void*) + nMul + 3] = 'I'; +#if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE) + { + CPLMutexHolderD(&hMemStatMutex); +#ifdef DEBUG_VSIMALLOC_VERBOSE + fprintf(stderr, "Thread[%p] VSICalloc(%d,%d) = %p\n", + (void*)CPLGetPID(), (int)nCount, (int)nSize, ptr + 2 * sizeof(void*)); +#endif +#ifdef DEBUG_VSIMALLOC_STATS + nVSICallocs ++; + if (nMaxTotalAllocs == 0) + atexit(VSIShowMemStats); + nCurrentTotalAllocs += nMul; + if (nCurrentTotalAllocs > nMaxTotalAllocs) + nMaxTotalAllocs = nCurrentTotalAllocs; +#endif + } +#endif + return ptr + 2 * sizeof(void*); +#else + return( calloc( nCount, nSize ) ); +#endif +} + +/************************************************************************/ +/* VSIMalloc() */ +/************************************************************************/ + +void *VSIMalloc( size_t nSize ) + +{ +#ifdef DEBUG_VSIMALLOC + if (nMaxPeakAllocSize < 0) + { + char* pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE"); + nMaxPeakAllocSize = (pszMaxPeakAllocSize) ? atoi(pszMaxPeakAllocSize) : 0; + char* pszMaxCumulAllocSize = getenv("CPL_MAX_CUMUL_ALLOC_SIZE"); + nMaxCumulAllocSize = (pszMaxCumulAllocSize) ? atoi(pszMaxCumulAllocSize) : 0; + } + if (nMaxPeakAllocSize > 0 && (GIntBig)nSize > nMaxPeakAllocSize) + return NULL; +#ifdef DEBUG_VSIMALLOC_STATS + if (nMaxCumulAllocSize > 0 && (GIntBig)nCurrentTotalAllocs + (GIntBig)nSize > nMaxCumulAllocSize) + return NULL; +#endif + +#ifdef DEBUG_VSIMALLOC_MPROTECT + char* ptr = NULL; + size_t nPageSize = getpagesize(); + posix_memalign((void**)&ptr, nPageSize, (3 * sizeof(void*) + nSize + nPageSize - 1) & ~(nPageSize - 1)); +#else + char* ptr = (char*) malloc(3 * sizeof(void*) + nSize); +#endif + if (ptr == NULL) + return NULL; + ptr[0] = 'V'; + ptr[1] = 'S'; + ptr[2] = 'I'; + ptr[3] = 'M'; + memcpy(ptr + sizeof(void*), &nSize, sizeof(void*)); + ptr[2 * sizeof(void*) + nSize + 0] = 'E'; + ptr[2 * sizeof(void*) + nSize + 1] = 'V'; + ptr[2 * sizeof(void*) + nSize + 2] = 'S'; + ptr[2 * sizeof(void*) + nSize + 3] = 'I'; +#if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE) + { + CPLMutexHolderD(&hMemStatMutex); +#ifdef DEBUG_VSIMALLOC_VERBOSE + fprintf(stderr, "Thread[%p] VSIMalloc(%d) = %p\n", + (void*)CPLGetPID(), (int)nSize, ptr + 2 * sizeof(void*)); +#endif +#ifdef DEBUG_VSIMALLOC_STATS + nVSIMallocs ++; + if (nMaxTotalAllocs == 0) + atexit(VSIShowMemStats); + nCurrentTotalAllocs += nSize; + if (nCurrentTotalAllocs > nMaxTotalAllocs) + nMaxTotalAllocs = nCurrentTotalAllocs; +#endif + } +#endif + return ptr + 2 * sizeof(void*); +#else + return( malloc( nSize ) ); +#endif +} + +#ifdef DEBUG_VSIMALLOC +void VSICheckMarkerBegin(char* ptr) +{ + if (memcmp(ptr, "VSIM", 4) != 0) + { + CPLError(CE_Fatal, CPLE_AppDefined, + "Inconsistent use of VSI memory allocation primitives for %p : %c%c%c%c", + ptr, ptr[0], ptr[1], ptr[2], ptr[3]); + } +} + +void VSICheckMarkerEnd(char* ptr, size_t nEnd) +{ + if (memcmp(ptr + nEnd, "EVSI", 4) != 0) + { + CPLError(CE_Fatal, CPLE_AppDefined, + "Memory has been written after the end of %p", ptr); + } +} + +#endif + +/************************************************************************/ +/* VSIRealloc() */ +/************************************************************************/ + +void * VSIRealloc( void * pData, size_t nNewSize ) + +{ +#ifdef DEBUG_VSIMALLOC + if (pData == NULL) + return VSIMalloc(nNewSize); + + char* ptr = ((char*)pData) - 2 * sizeof(void*); + VSICheckMarkerBegin(ptr); + + size_t nOldSize; + memcpy(&nOldSize, ptr + sizeof(void*), sizeof(void*)); + VSICheckMarkerEnd(ptr, 2 * sizeof(void*) + nOldSize); + ptr[2 * sizeof(void*) + nOldSize + 0] = 'I'; + ptr[2 * sizeof(void*) + nOldSize + 1] = 'S'; + ptr[2 * sizeof(void*) + nOldSize + 2] = 'V'; + ptr[2 * sizeof(void*) + nOldSize + 3] = 'E'; + + if (nMaxPeakAllocSize < 0) + { + char* pszMaxPeakAllocSize = getenv("CPL_MAX_PEAK_ALLOC_SIZE"); + nMaxPeakAllocSize = (pszMaxPeakAllocSize) ? atoi(pszMaxPeakAllocSize) : 0; + } + if (nMaxPeakAllocSize > 0 && (GIntBig)nNewSize > nMaxPeakAllocSize) + return NULL; +#ifdef DEBUG_VSIMALLOC_STATS + if (nMaxCumulAllocSize > 0 && (GIntBig)nCurrentTotalAllocs + (GIntBig)nNewSize - (GIntBig)nOldSize > nMaxCumulAllocSize) + return NULL; +#endif + +#ifdef DEBUG_VSIMALLOC_MPROTECT + char* newptr = NULL; + size_t nPageSize = getpagesize(); + posix_memalign((void**)&newptr, nPageSize, (nNewSize + 3 * sizeof(void*) + nPageSize - 1) & ~(nPageSize - 1)); + if (newptr == NULL) + { + ptr[2 * sizeof(void*) + nOldSize + 0] = 'E'; + ptr[2 * sizeof(void*) + nOldSize + 1] = 'V'; + ptr[2 * sizeof(void*) + nOldSize + 2] = 'S'; + ptr[2 * sizeof(void*) + nOldSize + 3] = 'I'; + return NULL; + } + memcpy(newptr + 2 * sizeof(void*), pData, nOldSize); + ptr[0] = 'M'; + ptr[1] = 'I'; + ptr[2] = 'S'; + ptr[3] = 'V'; + free(ptr); + newptr[0] = 'V'; + newptr[1] = 'S'; + newptr[2] = 'I'; + newptr[3] = 'M'; +#else + void* newptr = realloc(ptr, nNewSize + 3 * sizeof(void*)); + if (newptr == NULL) + { + ptr[2 * sizeof(void*) + nOldSize + 0] = 'E'; + ptr[2 * sizeof(void*) + nOldSize + 1] = 'V'; + ptr[2 * sizeof(void*) + nOldSize + 2] = 'S'; + ptr[2 * sizeof(void*) + nOldSize + 3] = 'I'; + return NULL; + } +#endif + ptr = (char*) newptr; + memcpy(ptr + sizeof(void*), &nNewSize, sizeof(void*)); + ptr[2 * sizeof(void*) + nNewSize + 0] = 'E'; + ptr[2 * sizeof(void*) + nNewSize + 1] = 'V'; + ptr[2 * sizeof(void*) + nNewSize + 2] = 'S'; + ptr[2 * sizeof(void*) + nNewSize + 3] = 'I'; + +#if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE) + { + CPLMutexHolderD(&hMemStatMutex); +#ifdef DEBUG_VSIMALLOC_VERBOSE + fprintf(stderr, "Thread[%p] VSIRealloc(%p, %d) = %p\n", + (void*)CPLGetPID(), pData, (int)nNewSize, ptr + 2 * sizeof(void*)); +#endif +#ifdef DEBUG_VSIMALLOC_STATS + nVSIReallocs ++; + nCurrentTotalAllocs -= nOldSize; + nCurrentTotalAllocs += nNewSize; + if (nCurrentTotalAllocs > nMaxTotalAllocs) + nMaxTotalAllocs = nCurrentTotalAllocs; +#endif + } +#endif + return ptr + 2 * sizeof(void*); +#else + return( realloc( pData, nNewSize ) ); +#endif +} + +/************************************************************************/ +/* VSIFree() */ +/************************************************************************/ + +void VSIFree( void * pData ) + +{ +#ifdef DEBUG_VSIMALLOC + if (pData == NULL) + return; + + char* ptr = ((char*)pData) - 2 * sizeof(void*); + VSICheckMarkerBegin(ptr); + size_t nOldSize; + memcpy(&nOldSize, ptr + sizeof(void*), sizeof(void*)); + VSICheckMarkerEnd(ptr, 2 * sizeof(void*) + nOldSize); + ptr[0] = 'M'; + ptr[1] = 'I'; + ptr[2] = 'S'; + ptr[3] = 'V'; + ptr[2 * sizeof(void*) + nOldSize + 0] = 'I'; + ptr[2 * sizeof(void*) + nOldSize + 1] = 'S'; + ptr[2 * sizeof(void*) + nOldSize + 2] = 'V'; + ptr[2 * sizeof(void*) + nOldSize + 3] = 'E'; +#if defined(DEBUG_VSIMALLOC_STATS) || defined(DEBUG_VSIMALLOC_VERBOSE) + { + CPLMutexHolderD(&hMemStatMutex); +#ifdef DEBUG_VSIMALLOC_VERBOSE + fprintf(stderr, "Thread[%p] VSIFree(%p, (%d bytes))\n", + (void*)CPLGetPID(), pData, (int)nOldSize); +#endif +#ifdef DEBUG_VSIMALLOC_STATS + nVSIFrees ++; + nCurrentTotalAllocs -= nOldSize; +#endif + } +#endif + +#ifdef DEBUG_VSIMALLOC_MPROTECT + mprotect(ptr, nOldSize + 2 * sizeof(void*), PROT_NONE); +#else + free(ptr); +#endif + +#else + if( pData != NULL ) + free( pData ); +#endif +} + +/************************************************************************/ +/* VSIStrdup() */ +/************************************************************************/ + +char *VSIStrdup( const char * pszString ) + +{ + int nSize = strlen(pszString) + 1; + char* ptr = (char*) VSIMalloc(nSize); + if (ptr == NULL) + return NULL; + memcpy(ptr, pszString, nSize); + return ptr; +} + +/************************************************************************/ +/* VSICheckMul2() */ +/************************************************************************/ + +static size_t VSICheckMul2( size_t mul1, size_t mul2, int *pbOverflowFlag) +{ + size_t res = mul1 * mul2; + if (mul1 != 0) + { + if (res / mul1 == mul2) + { + if (pbOverflowFlag) + *pbOverflowFlag = FALSE; + return res; + } + else + { + if (pbOverflowFlag) + *pbOverflowFlag = TRUE; + CPLError(CE_Failure, CPLE_OutOfMemory, + "Multiplication overflow : %lu * %lu", + (unsigned long)mul1, (unsigned long)mul2); + } + } + else + { + if (pbOverflowFlag) + *pbOverflowFlag = FALSE; + } + return 0; +} + +/************************************************************************/ +/* VSICheckMul3() */ +/************************************************************************/ + +static size_t VSICheckMul3( size_t mul1, size_t mul2, size_t mul3, int *pbOverflowFlag) +{ + if (mul1 != 0) + { + size_t res = mul1 * mul2; + if (res / mul1 == mul2) + { + size_t res2 = res * mul3; + if (mul3 != 0) + { + if (res2 / mul3 == res) + { + if (pbOverflowFlag) + *pbOverflowFlag = FALSE; + return res2; + } + else + { + if (pbOverflowFlag) + *pbOverflowFlag = TRUE; + CPLError(CE_Failure, CPLE_OutOfMemory, + "Multiplication overflow : %lu * %lu * %lu", + (unsigned long)mul1, (unsigned long)mul2, (unsigned long)mul3); + } + } + else + { + if (pbOverflowFlag) + *pbOverflowFlag = FALSE; + } + } + else + { + if (pbOverflowFlag) + *pbOverflowFlag = TRUE; + CPLError(CE_Failure, CPLE_OutOfMemory, + "Multiplication overflow : %lu * %lu * %lu", + (unsigned long)mul1, (unsigned long)mul2, (unsigned long)mul3); + } + } + else + { + if (pbOverflowFlag) + *pbOverflowFlag = FALSE; + } + return 0; +} + + + +/** + VSIMalloc2 allocates (nSize1 * nSize2) bytes. + In case of overflow of the multiplication, or if memory allocation fails, a + NULL pointer is returned and a CE_Failure error is raised with CPLError(). + If nSize1 == 0 || nSize2 == 0, a NULL pointer will also be returned. + CPLFree() or VSIFree() can be used to free memory allocated by this function. +*/ +void CPL_DLL *VSIMalloc2( size_t nSize1, size_t nSize2 ) +{ + int bOverflowFlag = FALSE; + size_t nSizeToAllocate; + void* pReturn; + + nSizeToAllocate = VSICheckMul2( nSize1, nSize2, &bOverflowFlag ); + if (bOverflowFlag) + return NULL; + + if (nSizeToAllocate == 0) + return NULL; + + pReturn = VSIMalloc(nSizeToAllocate); + + if( pReturn == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "VSIMalloc2(): Out of memory allocating %lu bytes.\n", + (unsigned long)nSizeToAllocate ); + } + + return pReturn; +} + +/** + VSIMalloc3 allocates (nSize1 * nSize2 * nSize3) bytes. + In case of overflow of the multiplication, or if memory allocation fails, a + NULL pointer is returned and a CE_Failure error is raised with CPLError(). + If nSize1 == 0 || nSize2 == 0 || nSize3 == 0, a NULL pointer will also be returned. + CPLFree() or VSIFree() can be used to free memory allocated by this function. +*/ +void CPL_DLL *VSIMalloc3( size_t nSize1, size_t nSize2, size_t nSize3 ) +{ + int bOverflowFlag = FALSE; + + size_t nSizeToAllocate; + void* pReturn; + + nSizeToAllocate = VSICheckMul3( nSize1, nSize2, nSize3, &bOverflowFlag ); + if (bOverflowFlag) + return NULL; + + if (nSizeToAllocate == 0) + return NULL; + + pReturn = VSIMalloc(nSizeToAllocate); + + if( pReturn == NULL ) + { + CPLError( CE_Failure, CPLE_OutOfMemory, + "VSIMalloc3(): Out of memory allocating %lu bytes.\n", + (unsigned long)nSizeToAllocate ); + } + + return pReturn; +} + + +/************************************************************************/ +/* VSIStat() */ +/************************************************************************/ + +int VSIStat( const char * pszFilename, VSIStatBuf * pStatBuf ) + +{ +#if defined(WIN32) && !defined(WIN32CE) + if( CSLTestBoolean( + CPLGetConfigOption( "GDAL_FILENAME_IS_UTF8", "YES" ) ) ) + { + int nResult; + wchar_t *pwszFilename = + CPLRecodeToWChar( pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2 ); + + nResult = _wstat( pwszFilename, (struct _stat *) pStatBuf ); + + CPLFree( pwszFilename ); + + return nResult; + } + else +#endif + return( stat( pszFilename, pStatBuf ) ); +} + +/************************************************************************/ +/* VSITime() */ +/************************************************************************/ + +unsigned long VSITime( unsigned long * pnTimeToSet ) + +{ + time_t tTime; + + tTime = time( NULL ); + + if( pnTimeToSet != NULL ) + *pnTimeToSet = (unsigned long) tTime; + + return (unsigned long) tTime; +} + +/************************************************************************/ +/* VSICTime() */ +/************************************************************************/ + +const char *VSICTime( unsigned long nTime ) + +{ + time_t tTime = (time_t) nTime; + + return (const char *) ctime( &tTime ); +} + +/************************************************************************/ +/* VSIGMTime() */ +/************************************************************************/ + +struct tm *VSIGMTime( const time_t *pnTime, struct tm *poBrokenTime ) +{ + +#if HAVE_GMTIME_R + gmtime_r( pnTime, poBrokenTime ); +#else + struct tm *poTime; + poTime = gmtime( pnTime ); + memcpy( poBrokenTime, poTime, sizeof(tm) ); +#endif + + return poBrokenTime; +} + +/************************************************************************/ +/* VSILocalTime() */ +/************************************************************************/ + +struct tm *VSILocalTime( const time_t *pnTime, struct tm *poBrokenTime ) +{ + +#if HAVE_LOCALTIME_R + localtime_r( pnTime, poBrokenTime ); +#else + struct tm *poTime; + poTime = localtime( pnTime ); + memcpy( poBrokenTime, poTime, sizeof(tm) ); +#endif + + return poBrokenTime; +} + +/************************************************************************/ +/* VSIStrerror() */ +/************************************************************************/ + +char *VSIStrerror( int nErrno ) + +{ + return strerror( nErrno ); +} + + +/************************************************************************/ +/* CPLGetPhysicalRAM() */ +/************************************************************************/ + +#if HAVE_SC_PHYS_PAGES + +/** Return the total physical RAM in bytes. + * + * @return the total physical RAM in bytes (or 0 in case of failure). + * @since GDAL 2.0 + */ +GIntBig CPLGetPhysicalRAM(void) +{ + return ((GIntBig)sysconf(_SC_PHYS_PAGES)) * sysconf(_SC_PAGESIZE); +} + +#elif defined(__MACH__) && defined(__APPLE__) + +#include +#include + +GIntBig CPLGetPhysicalRAM(void) +{ + int mib[2]; + GIntBig nPhysMem = 0; + + mib[0] = CTL_HW; + mib[1] = HW_MEMSIZE; + size_t nLengthRes = sizeof(nPhysMem); + sysctl(mib, 2, &nPhysMem, &nLengthRes, NULL, 0); + + return nPhysMem; +} + +#elif defined(WIN32) + +/* GlobalMemoryStatusEx requires _WIN32_WINNT >= 0x0500 */ +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif +#include + +GIntBig CPLGetPhysicalRAM(void) +{ + MEMORYSTATUSEX statex; + statex.ullTotalPhys = 0; + statex.dwLength = sizeof (statex); + GlobalMemoryStatusEx (&statex); + return (GIntBig) statex.ullTotalPhys; +} + +#else + +GIntBig CPLGetPhysicalRAM(void) +{ + static int bOnce = FALSE; + if( !bOnce ) + { + bOnce = TRUE; + CPLDebug("PORT", "No implementation for CPLGetPhysicalRAM()"); + } + return 0; +} +#endif + +/************************************************************************/ +/* CPLGetUsablePhysicalRAM() */ +/************************************************************************/ + +/** Return the total physical RAM, usable by a process, in bytes. + * + * This is the same as CPLGetPhysicalRAM() except it will limit to 2 GB + * for 32 bit processes. + * + * Note: This memory may already be partly used by other processes. + * + * @return the total physical RAM, usable by a process, in bytes (or 0 in case of failure). + * @since GDAL 2.0 + */ +GIntBig CPLGetUsablePhysicalRAM(void) +{ + GIntBig nRAM = CPLGetPhysicalRAM(); +#if SIZEOF_VOIDP == 4 + if( nRAM > INT_MAX ) + nRAM = INT_MAX; +#endif + return nRAM; +} diff --git a/bazaar/plugin/gdal/port/cpl_win32ce_api.cpp b/bazaar/plugin/gdal/port/cpl_win32ce_api.cpp new file mode 100644 index 000000000..c174a26d9 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_win32ce_api.cpp @@ -0,0 +1,130 @@ +/****************************************************************************** + * $Id: cpl_win32ce_api.cpp 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Name: cpl_win32ce_api.cpp + * Project: CPL - Common Portability Library + * Purpose: ASCII wrappers around only Unicode Windows CE API. + * Author: Mateusz £oskot, mloskot@taxussi.com.pl + * + ****************************************************************************** + * Copyright (c) 2006, Mateusz £oskot + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "cpl_port.h" +#include "cpl_win32ce_api.h" + +#ifdef WIN32CE + +CPL_CVSID("$Id: cpl_win32ce_api.cpp 10645 2007-01-18 02:22:39Z warmerdam $"); + + +/* Assume UNICODE and _UNICODE are defined here and TCHAR is wide-char. */ + +#include +#include +#include + + +HMODULE CE_LoadLibraryA(LPCSTR lpLibFileName) +{ + HMODULE hLib = NULL; + + size_t nLen = 0; + LPTSTR pszWideStr = 0; + + /* Covert filename buffer to Unicode. */ + nLen = MultiByteToWideChar (CP_ACP, 0, lpLibFileName, -1, NULL, 0) ; + pszWideStr = (wchar_t*)malloc(sizeof(wchar_t) * nLen); + MultiByteToWideChar(CP_ACP, 0, lpLibFileName, -1, pszWideStr, nLen); + + hLib = LoadLibraryW(pszWideStr); + + /* Free me! */ + free(pszWideStr); + + return hLib; +} + +FARPROC CE_GetProcAddressA(HMODULE hModule, LPCSTR lpProcName) +{ + FARPROC proc = NULL; + + size_t nLen = 0; + LPTSTR pszWideStr = 0; + + /* Covert filename buffer to Unicode. */ + nLen = MultiByteToWideChar (CP_ACP, 0, lpProcName, -1, NULL, 0) ; + pszWideStr = (wchar_t*)malloc(sizeof(wchar_t) * nLen); + MultiByteToWideChar(CP_ACP, 0, lpProcName, -1, pszWideStr, nLen); + + proc = GetProcAddressW(hModule, pszWideStr); + + /* Free me! */ + free(pszWideStr); + + return proc; +} + + +DWORD CE_GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize) +{ + DWORD dwLen = 0; + TCHAR szWBuf[MAX_PATH]; /* wide-char buffer */ + + if (lpFilename == NULL) + { + return 0; /* Error */ + } + + /* Get module filename to wide-char buffer */ + dwLen = GetModuleFileName(hModule, szWBuf, nSize); + + /* Covert buffer from Unicode to ANSI string. */ + WideCharToMultiByte(CP_ACP, 0, szWBuf, -1, lpFilename, dwLen, NULL, NULL); + + return dwLen; +} + +HANDLE CE_CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, + DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile) +{ + HANDLE hFile = INVALID_HANDLE_VALUE; + + size_t nLen = 0; + wchar_t * pszWideStr = NULL; + + /* Covert filename buffer to Unicode. */ + nLen = MultiByteToWideChar (CP_ACP, 0, lpFileName, -1, NULL, 0) ; + pszWideStr = (wchar_t*)malloc(sizeof(wchar_t) * nLen); + MultiByteToWideChar(CP_ACP, 0, lpFileName, -1, pszWideStr, nLen); + + hFile = CreateFileW(pszWideStr, dwDesiredAccess, dwShareMode, lpSecurityAttributes, + dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); + + /* Free me! */ + free(pszWideStr); + + return hFile; +} + + +#endif /* #ifdef WIN32CE */ diff --git a/bazaar/plugin/gdal/port/cpl_win32ce_api.h b/bazaar/plugin/gdal/port/cpl_win32ce_api.h new file mode 100644 index 000000000..7e8758708 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_win32ce_api.h @@ -0,0 +1,105 @@ +/****************************************************************************** + * $Id: cpl_win32ce_api.h 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Name: cpl_win32ce_api.h + * Project: CPL - Common Portability Library + * Purpose: ASCII wrappers around only Unicode Windows CE API. + * Author: Mateusz £oskot, mloskot@taxussi.com.pl + * + ****************************************************************************** + * Copyright (c) 2006, Mateusz £oskot + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef _CPL_WINCEAPI_H_INCLUDED +#define _CPL_WINCEAPI_H_INCLUDED 1 + +#define WIN32CE +#if defined(WIN32CE) + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Windows CE API non-Unicode Wrappers + */ + +HMODULE CE_LoadLibraryA( + LPCSTR lpLibFileName + ); + +FARPROC CE_GetProcAddressA( + HMODULE hModule, + LPCSTR lpProcName + ); + + +DWORD CE_GetModuleFileNameA( + HMODULE hModule, + LPSTR lpFilename, + DWORD nSize + ); + +HANDLE CE_CreateFileA( + LPCSTR lpFileName, + DWORD dwDesiredAccess, + DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile + ); + + +/* Replace Windows CE API calls with our own non-Unicode equivalents. */ + + +/* XXX - mloskot - those defines are quite confusing ! */ +/* +#ifdef LoadLibrary +# undef LoadLibrary +# define LoadLibrary CE_LoadLibraryA +#endif + +#ifdef GetProcAddress +# undef GetProcAddress +# define GetProcAddress CE_GetProcAddressA +#endif + +#ifdef GetModuleFileName +# undef GetModuleFileName +# define GetModuleFileName CE_GetModuleFileNameA +#endif + +#ifdef CreateFile +# undef CreateFile +# define CreateFile CE_CreateFileA +#endif +*/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* #ifdef WIN32CE */ + +#endif /* #ifndef _CPL_WINCEAPI_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_wince.h b/bazaar/plugin/gdal/port/cpl_wince.h new file mode 100644 index 000000000..eddac4239 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_wince.h @@ -0,0 +1,44 @@ +/****************************************************************************** + * $Id: cpl_wince.h 10645 2007-01-18 02:22:39Z warmerdam $ + * + * Name: cpl_win_api.h + * Project: CPL - Common Portability Library + * Purpose: Windows CE specific declarations. + * Author: Mateusz £oskot, mloskot@taxussi.com.pl + * + ****************************************************************************** + * Copyright (c) 2006, Mateusz £oskot + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#ifndef _CPL_WINCE_H_INCLUDED +#define _CPL_WINCE_H_INCLUDED + +#include "cpl_config.h" + +#if defined(WIN32CE) + +#include "cpl_conv.h" +#include "windows.h" + + + +#endif /* #if defined(WIN32CE) */ + +#endif /* #ifndef _CPL_WINCE_H_INCLUDED */ diff --git a/bazaar/plugin/gdal/port/cpl_xml_validate.cpp b/bazaar/plugin/gdal/port/cpl_xml_validate.cpp new file mode 100644 index 000000000..6cba8a981 --- /dev/null +++ b/bazaar/plugin/gdal/port/cpl_xml_validate.cpp @@ -0,0 +1,1139 @@ +/****************************************************************************** + * $Id: cpl_xml_validate.cpp 27755 2014-09-28 16:51:08Z goatbar $ + * + * Project: CPL - Common Portability Library + * Purpose: Implement XML validation against XSD schema + * Author: Even Rouault, even.rouault at mines-paris.org + * + ****************************************************************************** + * Copyright (c) 2012-2014, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" + +CPL_CVSID("$Id: cpl_xml_validate.cpp 27755 2014-09-28 16:51:08Z goatbar $"); + +#ifdef HAVE_LIBXML2 +#include +#if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20622 +/* We need at least 2.6.20 for xmlSchemaValidateDoc */ +/* and xmlParseDoc to accept a const xmlChar* */ +/* We could workaround it, but likely not worth the effort for now. */ +/* Actually, we need at least 2.6.22, at runtime, to be */ +/* able to parse the OGC GML schemas */ +#define HAVE_RECENT_LIBXML2 + +/* libxml2 before 2.8.0 had a bug to parse the OGC GML schemas */ +/* We have a workaround for that for versions >= 2.6.20 and < 2.8.0 */ +#if defined(LIBXML_VERSION) && LIBXML_VERSION < 20800 +#define HAS_VALIDATION_BUG +#endif + +#else +#warning "Not recent enough libxml2 version" +#endif +#endif + +#ifdef HAVE_RECENT_LIBXML2 +#include +#include +#include +#include + +#include "cpl_string.h" +#include "cpl_hash_set.h" +#include "cpl_minixml.h" + +static xmlExternalEntityLoader pfnLibXMLOldExtranerEntityLoader = NULL; + +/************************************************************************/ +/* CPLFixPath() */ +/************************************************************************/ + +/* Replace \ by / to make libxml2 happy on Windows and */ +/* replace "a/b/../c" pattern by "a/c" */ +static void CPLFixPath(char* pszPath) +{ + for(int i=0;pszPath[i] != '\0';i++) + { + if (pszPath[i] == '\\') + pszPath[i] = '/'; + } + + while(TRUE) + { + char* pszSlashDotDot = strstr(pszPath, "/../"); + if (pszSlashDotDot == NULL || pszSlashDotDot == pszPath) + return; + char* pszSlashBefore = pszSlashDotDot-1; + while(pszSlashBefore > pszPath && *pszSlashBefore != '/') + pszSlashBefore --; + if (pszSlashBefore == pszPath) + return; + memmove(pszSlashBefore + 1, pszSlashDotDot + 4, + strlen(pszSlashDotDot + 4) + 1); + } +} + +#ifdef HAS_VALIDATION_BUG + +static int bHasLibXMLBug = -1; + +/************************************************************************/ +/* CPLHasLibXMLBugWarningCallback() */ +/************************************************************************/ + +static void CPLHasLibXMLBugWarningCallback (void * ctx, const char * msg, ...) +{ +} + +/************************************************************************/ +/* CPLHasLibXMLBug() */ +/************************************************************************/ + +static int CPLHasLibXMLBug() +{ + if (bHasLibXMLBug >= 0) + return bHasLibXMLBug; + + static const char szLibXMLBugTester[] = + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; + + xmlSchemaParserCtxtPtr pSchemaParserCtxt; + xmlSchemaPtr pSchema; + + pSchemaParserCtxt = xmlSchemaNewMemParserCtxt(szLibXMLBugTester, strlen(szLibXMLBugTester)); + + xmlSchemaSetParserErrors(pSchemaParserCtxt, + CPLHasLibXMLBugWarningCallback, + CPLHasLibXMLBugWarningCallback, + NULL); + + pSchema = xmlSchemaParse(pSchemaParserCtxt); + xmlSchemaFreeParserCtxt(pSchemaParserCtxt); + + bHasLibXMLBug = (pSchema == NULL); + + if (pSchema) + xmlSchemaFree(pSchema); + + if (bHasLibXMLBug) + { + CPLDebug("CPL", + "LibXML bug found (cf https://bugzilla.gnome.org/show_bug.cgi?id=630130). " + "Will try to workaround for GML schemas."); + } + + return bHasLibXMLBug; +} + +#endif + +/************************************************************************/ +/* CPLExtractSubSchema() */ +/************************************************************************/ + +static CPLXMLNode* CPLExtractSubSchema(CPLXMLNode* psSubXML, CPLXMLNode* psMainSchema) +{ + if (psSubXML->eType == CXT_Element && strcmp(psSubXML->pszValue, "?xml") == 0) + { + CPLXMLNode* psNext = psSubXML->psNext; + psSubXML->psNext = NULL; + CPLDestroyXMLNode(psSubXML); + psSubXML = psNext; + } + + if (psSubXML != NULL && psSubXML->eType == CXT_Comment) + { + CPLXMLNode* psNext = psSubXML->psNext; + psSubXML->psNext = NULL; + CPLDestroyXMLNode(psSubXML); + psSubXML = psNext; + } + + if (psSubXML != NULL && psSubXML->eType == CXT_Element && + (strcmp(psSubXML->pszValue, "schema") == 0 || + strcmp(psSubXML->pszValue, "xs:schema") == 0 || + strcmp(psSubXML->pszValue, "xsd:schema") == 0) && + psSubXML->psNext == NULL) + { + CPLXMLNode* psNext = psSubXML->psChild; + while(psNext != NULL && psNext->eType != CXT_Element && + psNext->psNext != NULL && psNext->psNext->eType != CXT_Element) + { + /* Add xmlns: from subschema to main schema if missing */ + if (psNext->eType == CXT_Attribute && + strncmp(psNext->pszValue, "xmlns:", 6) == 0 && + CPLGetXMLValue(psMainSchema, psNext->pszValue, NULL) == NULL) + { + CPLXMLNode* psAttr = CPLCreateXMLNode(NULL, CXT_Attribute, psNext->pszValue); + CPLCreateXMLNode(psAttr, CXT_Text, psNext->psChild->pszValue); + + psAttr->psNext = psMainSchema->psChild; + psMainSchema->psChild = psAttr; + } + psNext = psNext->psNext; + } + + if (psNext != NULL && psNext->eType != CXT_Element && + psNext->psNext != NULL && psNext->psNext->eType == CXT_Element) + { + CPLXMLNode* psNext2 = psNext->psNext; + psNext->psNext = NULL; + CPLDestroyXMLNode(psSubXML); + psSubXML = psNext2; + } + } + + return psSubXML; +} + +#ifdef HAS_VALIDATION_BUG +/************************************************************************/ +/* CPLWorkaroundLibXMLBug() */ +/************************************************************************/ + +/* Return TRUE if the current node must be destroyed */ +static int CPLWorkaroundLibXMLBug(CPLXMLNode* psIter) +{ + if (psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "element") == 0 && + strcmp(CPLGetXMLValue(psIter, "name", ""), "QuantityExtent") == 0 && + strcmp(CPLGetXMLValue(psIter, "type", ""), "gml:QuantityExtentType") == 0) + { + CPLXMLNode* psIter2 = psIter->psChild; + while(psIter2) + { + if (psIter2->eType == CXT_Attribute && strcmp(psIter2->pszValue, "type") == 0) + { + CPLFree(psIter2->psChild->pszValue); + if (strcmp(CPLGetXMLValue(psIter, "substitutionGroup", ""), "gml:AbstractValue") == 0) + psIter2->psChild->pszValue = CPLStrdup("gml:MeasureOrNilReasonListType"); /* GML 3.2.1 */ + else + psIter2->psChild->pszValue = CPLStrdup("gml:MeasureOrNullListType"); + } + psIter2 = psIter2->psNext; + } + } + + else if (psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "element") == 0 && + strcmp(CPLGetXMLValue(psIter, "name", ""), "CategoryExtent") == 0 && + strcmp(CPLGetXMLValue(psIter, "type", ""), "gml:CategoryExtentType") == 0) + { + CPLXMLNode* psIter2 = psIter->psChild; + while(psIter2) + { + if (psIter2->eType == CXT_Attribute && strcmp(psIter2->pszValue, "type") == 0) + { + CPLFree(psIter2->psChild->pszValue); + if (strcmp(CPLGetXMLValue(psIter, "substitutionGroup", ""), "gml:AbstractValue") == 0) + psIter2->psChild->pszValue = CPLStrdup("gml:CodeOrNilReasonListType"); /* GML 3.2.1 */ + else + psIter2->psChild->pszValue = CPLStrdup("gml:CodeOrNullListType"); + } + psIter2 = psIter2->psNext; + } + } + + else if (bHasLibXMLBug && psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "complexType") == 0 && + (strcmp(CPLGetXMLValue(psIter, "name", ""), "QuantityExtentType") == 0 || + strcmp(CPLGetXMLValue(psIter, "name", ""), "CategoryExtentType") == 0)) + { + /* Destroy this element */ + return TRUE; + } + + /* For GML 3.2.1 */ + else if (psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "complexType") == 0 && + strcmp(CPLGetXMLValue(psIter, "name", ""), "VectorType") == 0) + { + CPLXMLNode* psSimpleContent = CPLCreateXMLNode(NULL, CXT_Element, "simpleContent"); + CPLXMLNode* psExtension = CPLCreateXMLNode(psSimpleContent, CXT_Element, "extension"); + CPLXMLNode* psExtensionBase = CPLCreateXMLNode(psExtension, CXT_Attribute, "base"); + CPLCreateXMLNode(psExtensionBase, CXT_Text, "gml:doubleList"); + CPLXMLNode* psAttributeGroup = CPLCreateXMLNode(psExtension, CXT_Element, "attributeGroup"); + CPLXMLNode* psAttributeGroupRef = CPLCreateXMLNode(psAttributeGroup, CXT_Attribute, "ref"); + CPLCreateXMLNode(psAttributeGroupRef, CXT_Text, "gml:SRSReferenceGroup"); + + CPLXMLNode* psName = CPLCreateXMLNode(NULL, CXT_Attribute, "name"); + CPLCreateXMLNode(psName, CXT_Text, "VectorType"); + + CPLDestroyXMLNode(psIter->psChild); + psIter->psChild = psName; + psIter->psChild->psNext = psSimpleContent; + } + + else if (psIter->eType == CXT_Element && + strcmp(psIter->pszValue, "element") == 0 && + (strcmp(CPLGetXMLValue(psIter, "name", ""), "domainOfValidity") == 0 || + strcmp(CPLGetXMLValue(psIter, "name", ""), "coordinateOperationAccuracy") == 0 || + strcmp(CPLGetXMLValue(psIter, "name", ""), "formulaCitation") == 0)) + { + CPLXMLNode* psComplexType = CPLCreateXMLNode(NULL, CXT_Element, "complexType"); + CPLXMLNode* psSequence = CPLCreateXMLNode(psComplexType, CXT_Element, "sequence"); + CPLXMLNode* psSequenceMinOccurs = CPLCreateXMLNode(psSequence, CXT_Attribute, "minOccurs"); + CPLCreateXMLNode(psSequenceMinOccurs, CXT_Text, "0"); + CPLXMLNode* psAny = CPLCreateXMLNode(psSequence, CXT_Element, "any"); + CPLXMLNode* psAnyMinOccurs = CPLCreateXMLNode(psAny, CXT_Attribute, "minOccurs"); + CPLCreateXMLNode(psAnyMinOccurs, CXT_Text, "0"); + CPLXMLNode* psAnyProcessContents = CPLCreateXMLNode(psAny, CXT_Attribute, " processContents"); + CPLCreateXMLNode(psAnyProcessContents, CXT_Text, "lax"); + + CPLXMLNode* psName = CPLCreateXMLNode(NULL, CXT_Attribute, "name"); + CPLCreateXMLNode(psName, CXT_Text, CPLGetXMLValue(psIter, "name", "")); + + CPLDestroyXMLNode(psIter->psChild); + psIter->psChild = psName; + psIter->psChild->psNext = psComplexType; + } + + return FALSE; +} +#endif + +/************************************************************************/ +/* CPLLoadSchemaStrInternal() */ +/************************************************************************/ + +static +CPLXMLNode* CPLLoadSchemaStrInternal(CPLHashSet* hSetSchemas, + const char* pszFile) +{ + CPLXMLNode* psXML; + CPLXMLNode* psSchema; + CPLXMLNode* psPrev; + CPLXMLNode* psIter; + + if (CPLHashSetLookup(hSetSchemas, pszFile)) + return NULL; + + CPLHashSetInsert(hSetSchemas, CPLStrdup(pszFile)); + + CPLDebug("CPL", "Parsing %s", pszFile); + + psXML = CPLParseXMLFile(pszFile); + if (psXML == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot open %s", pszFile); + return NULL; + } + + psSchema = CPLGetXMLNode(psXML, "=schema"); + if (psSchema == NULL) + psSchema = CPLGetXMLNode(psXML, "=xs:schema"); + if (psSchema == NULL) + psSchema = CPLGetXMLNode(psXML, "=xsd:schema"); + if (psSchema == NULL) + { + CPLError(CE_Failure, CPLE_AppDefined, + "Cannot find schema node in %s", pszFile); + CPLDestroyXMLNode(psXML); + return NULL; + } + + psPrev = NULL; + psIter = psSchema->psChild; + while(psIter) + { + int bDestroyCurrentNode = FALSE; + +#ifdef HAS_VALIDATION_BUG + if (bHasLibXMLBug) + bDestroyCurrentNode = CPLWorkaroundLibXMLBug(psIter); +#endif + + /* Load the referenced schemas, and integrate them in the main schema */ + if (psIter->eType == CXT_Element && + (strcmp(psIter->pszValue, "include") == 0 || + strcmp(psIter->pszValue, "xs:include") == 0|| + strcmp(psIter->pszValue, "xsd:include") == 0) && + psIter->psChild != NULL && + psIter->psChild->eType == CXT_Attribute && + strcmp(psIter->psChild->pszValue, "schemaLocation") == 0) + { + const char* pszIncludeSchema = psIter->psChild->psChild->pszValue; + char* pszFullFilename = CPLStrdup( + CPLFormFilename(CPLGetPath(pszFile), pszIncludeSchema, NULL)); + + CPLFixPath(pszFullFilename); + + CPLXMLNode* psSubXML = NULL; + + /* If we haven't yet loaded that schema, do it now */ + if (!CPLHashSetLookup(hSetSchemas, pszFullFilename)) + { + psSubXML = CPLLoadSchemaStrInternal(hSetSchemas, pszFullFilename); + if (psSubXML == NULL) + { + CPLFree(pszFullFilename); + CPLDestroyXMLNode(psXML); + return NULL; + } + } + CPLFree(pszFullFilename); + pszFullFilename = NULL; + + if (psSubXML) + { + CPLXMLNode* psNext = psIter->psNext; + + psSubXML = CPLExtractSubSchema(psSubXML, psSchema); + if (psSubXML == NULL) + { + CPLDestroyXMLNode(psXML); + return NULL; + } + + /* Replace node by the subXML */ + CPLXMLNode* psIter2 = psSubXML; + while(psIter2->psNext) + psIter2 = psIter2->psNext; + psIter2->psNext = psNext; + + if (psPrev == NULL) + psSchema->psChild = psSubXML; + else + psPrev->psNext = psSubXML; + + psIter->psNext = NULL; + CPLDestroyXMLNode(psIter); + + psPrev = psIter2; + psIter = psNext; + continue; + } + else + { + /* We have already included that file, */ + /* so just remove the node */ + bDestroyCurrentNode = TRUE; + } + } + + /* Patch the schemaLocation of */ + else if (psIter->eType == CXT_Element && + (strcmp(psIter->pszValue, "import") == 0 || + strcmp(psIter->pszValue, "xs:import") == 0|| + strcmp(psIter->pszValue, "xsd:import") == 0)) + { + CPLXMLNode* psIter2 = psIter->psChild; + while(psIter2) + { + if (psIter2->eType == CXT_Attribute && + strcmp(psIter2->pszValue, "schemaLocation") == 0 && + psIter2->psChild != NULL && + strncmp(psIter2->psChild->pszValue, "http://", 7) != 0 && + strncmp(psIter2->psChild->pszValue, "ftp://", 6) != 0 && + /* If the top file is our warping file, don't alter the path of the import */ + strstr(pszFile, "/vsimem/CPLValidateXML_") == NULL ) + { + char* pszFullFilename = CPLStrdup(CPLFormFilename( + CPLGetPath(pszFile), psIter2->psChild->pszValue, NULL)); + CPLFixPath(pszFullFilename); + CPLFree(psIter2->psChild->pszValue); + psIter2->psChild->pszValue = pszFullFilename; + } + psIter2 = psIter2->psNext; + } + } + + if (bDestroyCurrentNode) + { + CPLXMLNode* psNext = psIter->psNext; + if (psPrev == NULL) + psSchema->psChild = psNext; + else + psPrev->psNext = psNext; + + psIter->psNext = NULL; + CPLDestroyXMLNode(psIter); + + psIter = psNext; + continue; + } + + psPrev = psIter; + psIter = psIter->psNext; + } + + return psXML; +} + +/************************************************************************/ +/* CPLMoveImportAtBeginning() */ +/************************************************************************/ + +static +void CPLMoveImportAtBeginning(CPLXMLNode* psXML) +{ + CPLXMLNode* psIter; + CPLXMLNode* psPrev; + CPLXMLNode* psSchema; + + psSchema = CPLGetXMLNode(psXML, "=schema"); + if (psSchema == NULL) + psSchema = CPLGetXMLNode(psXML, "=xs:schema"); + if (psSchema == NULL) + psSchema = CPLGetXMLNode(psXML, "=xsd:schema"); + if (psSchema == NULL) + return; + + psPrev = NULL; + psIter = psSchema->psChild; + while(psIter) + { + if (psPrev != NULL && psIter->eType == CXT_Element && + (strcmp(psIter->pszValue, "import") == 0 || + strcmp(psIter->pszValue, "xs:import") == 0 || + strcmp(psIter->pszValue, "xsd:import") == 0)) + { + /* Reorder at the beginning */ + CPLXMLNode* psNext = psIter->psNext; + + psPrev->psNext = psNext; + + CPLXMLNode* psFirstChild = psSchema->psChild; + psSchema->psChild = psIter; + psIter->psNext = psFirstChild; + + psIter = psNext; + continue; + } + + psPrev = psIter; + psIter = psIter->psNext; + } +} + +/************************************************************************/ +/* CPLLoadSchemaStr() */ +/************************************************************************/ + +static +char* CPLLoadSchemaStr(const char* pszXSDFilename) +{ + char* pszStr = NULL; + +#ifdef HAS_VALIDATION_BUG + CPLHasLibXMLBug(); +#endif + + CPLHashSet* hSetSchemas = + CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree); + CPLXMLNode* psSchema = + CPLLoadSchemaStrInternal(hSetSchemas, pszXSDFilename); + if (psSchema) + { + CPLMoveImportAtBeginning(psSchema); + pszStr = CPLSerializeXMLTree(psSchema); + CPLDestroyXMLNode(psSchema); + } + CPLHashSetDestroy(hSetSchemas); + return pszStr; +} + +/************************************************************************/ +/* CPLLibXMLInputStreamCPLFree() */ +/************************************************************************/ + +static void CPLLibXMLInputStreamCPLFree(xmlChar* pszBuffer) +{ + CPLFree(pszBuffer); +} + +/************************************************************************/ +/* CPLFindLocalXSD() */ +/************************************************************************/ + +static CPLString CPLFindLocalXSD(const char* pszXSDFilename) +{ + const char *pszSchemasOpenGIS; + CPLString osTmp; + + pszSchemasOpenGIS = CPLGetConfigOption("GDAL_OPENGIS_SCHEMAS", NULL); + if (pszSchemasOpenGIS != NULL) + { + int nLen = (int)strlen(pszSchemasOpenGIS); + if (nLen > 0 && pszSchemasOpenGIS[nLen-1] == '/') + { + osTmp = pszSchemasOpenGIS; + osTmp += pszXSDFilename; + } + else + { + osTmp = pszSchemasOpenGIS; + osTmp += "/"; + osTmp += pszXSDFilename; + } + } + else if ((pszSchemasOpenGIS = CPLFindFile( "gdal", "SCHEMAS_OPENGIS_NET" )) != NULL) + { + osTmp = pszSchemasOpenGIS; + osTmp += "/"; + osTmp += pszXSDFilename; + } + + VSIStatBufL sStatBuf; + if( VSIStatExL(osTmp, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0 ) + return osTmp; + return ""; +} + +/************************************************************************/ +/* CPLExternalEntityLoader() */ +/************************************************************************/ + +static const char szXML_XSD[] = "" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +""; + +/* Simplified (and truncated) version of http://www.w3.org/1999/xlink.xsd (sufficient for GML schemas) */ +static const char szXLINK_XSD[] = "" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +""; + +static +xmlParserInputPtr CPLExternalEntityLoader (const char * URL, + const char * ID, + xmlParserCtxtPtr context) +{ + //CPLDebug("CPL", "CPLExternalEntityLoader(%s)", URL); + CPLString osURL; + + /* Use libxml2 catalog mechanism to resolve the URL to something else */ + xmlChar* pszResolved = xmlCatalogResolveSystem((const xmlChar*)URL); + if (pszResolved == NULL) + pszResolved = xmlCatalogResolveURI((const xmlChar*)URL); + if (pszResolved) + { + CPLDebug("CPL", "Resolving %s in %s", URL, (const char*)pszResolved ); + osURL = (const char*)pszResolved; + URL = osURL.c_str(); + xmlFree(pszResolved); + pszResolved = NULL; + } + + if (strncmp(URL, "http://", 7) == 0) + { + /* Make sure to use http://schemas.opengis.net/ */ + /* when gml/2 or gml/3 is detected */ + const char* pszGML = strstr(URL, "gml/2"); + if (pszGML == NULL) + pszGML = strstr(URL, "gml/3"); + if (pszGML != NULL) + { + osURL = "http://schemas.opengis.net/"; + osURL += pszGML; + URL = osURL.c_str(); + } + else if (strcmp(URL, "http://www.w3.org/2001/xml.xsd") == 0) + { + CPLString osTmp = CPLFindLocalXSD("xml.xsd"); + if( osTmp.size() != 0 ) + { + osURL = osTmp; + URL = osURL.c_str(); + } + else + { + CPLDebug("CPL", "Resolving %s to local definition", "http://www.w3.org/2001/xml.xsd"); + return xmlNewStringInputStream(context, (const xmlChar*) szXML_XSD); + } + } + else if (strcmp(URL, "http://www.w3.org/1999/xlink.xsd") == 0) + { + CPLString osTmp = CPLFindLocalXSD("xlink.xsd"); + if( osTmp.size() != 0 ) + { + osURL = osTmp; + URL = osURL.c_str(); + } + else + { + CPLDebug("CPL", "Resolving %s to local definition", "http://www.w3.org/1999/xlink.xsd"); + return xmlNewStringInputStream(context, (const xmlChar*) szXLINK_XSD); + } + } + else if (strncmp(URL, "http://schemas.opengis.net/", + strlen("http://schemas.opengis.net/")) != 0) + { + CPLDebug("CPL", "Loading %s", URL); + return pfnLibXMLOldExtranerEntityLoader(URL, ID, context); + } + } + else if (strncmp(URL, "ftp://", 6) == 0) + { + return pfnLibXMLOldExtranerEntityLoader(URL, ID, context); + } + else if (strncmp(URL, "file://", 7) == 0) + { + /* Parse file:// URI so as to be able to open them with VSI*L API */ + if (strncmp(URL, "file://localhost/", 17) == 0) + URL += 16; + else + URL += 7; + if (URL[0] == '/' && URL[1] != '\0' && URL[2] == ':' && URL[3] == '/') /* Windows */ + URL ++; + else if (URL[0] == '/') /* Unix */ + ; + else + return pfnLibXMLOldExtranerEntityLoader(URL, ID, context); + } + + CPLString osModURL; + if (strncmp(URL, "/vsizip/vsicurl/http%3A//", + strlen("/vsizip/vsicurl/http%3A//")) == 0) + { + osModURL = "/vsizip/vsicurl/http://"; + osModURL += URL + strlen("/vsizip/vsicurl/http%3A//"); + } + else if (strncmp(URL, "/vsicurl/http%3A//", + strlen("/vsicurl/http%3A//")) == 0) + { + osModURL = "vsicurl/http://"; + osModURL += URL + strlen("/vsicurl/http%3A//"); + } + else if (strncmp(URL, "http://schemas.opengis.net/", + strlen("http://schemas.opengis.net/")) == 0) + { + const char *pszAfterOpenGIS = + URL + strlen("http://schemas.opengis.net/"); + + const char *pszSchemasOpenGIS; + + pszSchemasOpenGIS = CPLGetConfigOption("GDAL_OPENGIS_SCHEMAS", NULL); + if (pszSchemasOpenGIS != NULL) + { + int nLen = (int)strlen(pszSchemasOpenGIS); + if (nLen > 0 && pszSchemasOpenGIS[nLen-1] == '/') + { + osModURL = pszSchemasOpenGIS; + osModURL += pszAfterOpenGIS; + } + else + { + osModURL = pszSchemasOpenGIS; + osModURL += "/"; + osModURL += pszAfterOpenGIS; + } + } + else if ((pszSchemasOpenGIS = CPLFindFile( "gdal", "SCHEMAS_OPENGIS_NET" )) != NULL) + { + osModURL = pszSchemasOpenGIS; + osModURL += "/"; + osModURL += pszAfterOpenGIS; + } + else if ((pszSchemasOpenGIS = CPLFindFile( "gdal", "SCHEMAS_OPENGIS_NET.zip" )) != NULL) + { + osModURL = "/vsizip/"; + osModURL += pszSchemasOpenGIS; + osModURL += "/"; + osModURL += pszAfterOpenGIS; + } + else + { + osModURL = "/vsizip/vsicurl/http://schemas.opengis.net/SCHEMAS_OPENGIS_NET.zip/"; + osModURL += pszAfterOpenGIS; + } + } + else + { + osModURL = URL; + } + + xmlChar* pszBuffer = (xmlChar*)CPLLoadSchemaStr(osModURL); + if (pszBuffer == NULL) + return NULL; + + xmlParserInputPtr poInputStream = xmlNewStringInputStream(context, pszBuffer); + if (poInputStream != NULL) + poInputStream->free = CPLLibXMLInputStreamCPLFree; + return poInputStream; +} + +/************************************************************************/ +/* CPLLibXMLWarningErrorCallback() */ +/************************************************************************/ + +static void CPLLibXMLWarningErrorCallback (void * ctx, const char * msg, ...) +{ + va_list varg; + char * pszStr; + + va_start(varg, msg); + pszStr = (char *)va_arg( varg, char *); + + if (strstr(pszStr, "since this namespace was already imported") == NULL) + { + xmlErrorPtr pErrorPtr = xmlGetLastError(); + const char* pszFilename = (const char*)ctx; + char* pszStrDup = CPLStrdup(pszStr); + int nLen = (int)strlen(pszStrDup); + if (nLen > 0 && pszStrDup[nLen-1] == '\n') + pszStrDup[nLen-1] = '\0'; + if( pszFilename != NULL && pszFilename[0] != '<' ) + { + CPLError(CE_Failure, CPLE_AppDefined, "libXML: %s:%d: %s", + pszFilename, pErrorPtr ? pErrorPtr->line : 0, pszStrDup); + } + else + { + CPLError(CE_Failure, CPLE_AppDefined, "libXML: %d: %s", + pErrorPtr ? pErrorPtr->line : 0, pszStrDup); + } + CPLFree(pszStrDup); + } + + va_end(varg); +} + +/************************************************************************/ +/* CPLLoadContentFromFile() */ +/************************************************************************/ + +static +char* CPLLoadContentFromFile(const char* pszFilename) +{ + VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); + if (fp == NULL) + return NULL; + vsi_l_offset nSize; + VSIFSeekL(fp, 0, SEEK_END); + nSize = VSIFTellL(fp); + VSIFSeekL(fp, 0, SEEK_SET); + if ((vsi_l_offset)(int)nSize != nSize || + nSize > INT_MAX - 1 ) + { + VSIFCloseL(fp); + return NULL; + } + char* pszBuffer = (char*)VSIMalloc(nSize + 1); + if (pszBuffer == NULL) + { + VSIFCloseL(fp); + return NULL; + } + VSIFReadL(pszBuffer, 1, nSize, fp); + pszBuffer[nSize] = '\0'; + VSIFCloseL(fp); + return pszBuffer; +} + +/************************************************************************/ +/* CPLLoadXMLSchema() */ +/************************************************************************/ + +typedef void* CPLXMLSchemaPtr; + +/** + * \brief Load a XSD schema. + * + * The return value should be freed with CPLFreeXMLSchema(). + * + * @param pszXSDFilename XSD schema to load. + * @return a handle to the parsed XML schema, or NULL in case of failure. + * + * @since GDAL 1.10.0 + */ + +static +CPLXMLSchemaPtr CPLLoadXMLSchema(const char* pszXSDFilename) +{ + char* pszStr = CPLLoadSchemaStr(pszXSDFilename); + if (pszStr == NULL) + return NULL; + + xmlExternalEntityLoader pfnLibXMLOldExtranerEntityLoaderLocal = NULL; + pfnLibXMLOldExtranerEntityLoaderLocal = xmlGetExternalEntityLoader(); + pfnLibXMLOldExtranerEntityLoader = pfnLibXMLOldExtranerEntityLoaderLocal; + xmlSetExternalEntityLoader(CPLExternalEntityLoader); + + xmlSchemaParserCtxtPtr pSchemaParserCtxt = + xmlSchemaNewMemParserCtxt(pszStr, strlen(pszStr)); + + xmlSchemaSetParserErrors(pSchemaParserCtxt, + CPLLibXMLWarningErrorCallback, + CPLLibXMLWarningErrorCallback, + NULL); + + xmlSchemaPtr pSchema = xmlSchemaParse(pSchemaParserCtxt); + xmlSchemaFreeParserCtxt(pSchemaParserCtxt); + + xmlSetExternalEntityLoader(pfnLibXMLOldExtranerEntityLoaderLocal); + + CPLFree(pszStr); + + return (CPLXMLSchemaPtr) pSchema; +} + +/************************************************************************/ +/* CPLFreeXMLSchema() */ +/************************************************************************/ + +/** + * \brief Free a XSD schema. + * + * @param pSchema a handle to the parsed XML schema. + * + * @since GDAL 1.10.0 + */ + +static +void CPLFreeXMLSchema(CPLXMLSchemaPtr pSchema) +{ + if (pSchema) + xmlSchemaFree((xmlSchemaPtr)pSchema); +} + +/************************************************************************/ +/* CPLValidateXML() */ +/************************************************************************/ + +/** + * \brief Validate a XML file against a XML schema. + * + * @param pszXMLFilename the filename of the XML file to validate. + * @param pszXSDFilename the filename of the XSD schema. + * @param papszOptions unused for now. + * @return TRUE if the XML file validates against the XML schema. + * + * @since GDAL 1.10.0 + */ + +int CPLValidateXML(const char* pszXMLFilename, + const char* pszXSDFilename, + CPL_UNUSED char** papszOptions) +{ + char szHeader[2048]; + CPLString osTmpXSDFilename; + + if( pszXMLFilename[0] == '<' ) + { + strncpy(szHeader, pszXMLFilename, sizeof(szHeader)); + szHeader[sizeof(szHeader)-1] = '\0'; + } + else + { + VSILFILE* fpXML = VSIFOpenL(pszXMLFilename, "rb"); + if (fpXML == NULL) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "Cannot open %s", pszXMLFilename); + return FALSE; + } + int nRead = (int)VSIFReadL(szHeader, 1, sizeof(szHeader)-1, fpXML); + szHeader[nRead] = '\0'; + VSIFCloseL(fpXML); + } + + /* Workaround following bug : "element FeatureCollection: Schemas validity error : Element '{http://www.opengis.net/wfs}FeatureCollection': No matching global declaration available for the validation root" */ + /* We create a wrapping XSD that imports the WFS .xsd (and possibly the GML .xsd too) and the application schema */ + /* This is a known libxml2 limitation */ + if (strstr(szHeader, "\n"); + VSIFPrintfL(fpMEM, " \n", pszWFSSchemaNamespace, pszWFSSchemaLocation); + VSIFPrintfL(fpMEM, " \n", pszEscapedXSDFilename); + if (pszGMLSchemaLocation) + VSIFPrintfL(fpMEM, " \n", pszGMLSchemaLocation); + VSIFPrintfL(fpMEM, "\n"); + VSIFCloseL(fpMEM); + CPLFree(pszEscapedXSDFilename); + } + } + + CPLXMLSchemaPtr pSchema = CPLLoadXMLSchema(osTmpXSDFilename.size() ? osTmpXSDFilename.c_str() : pszXSDFilename); + if (osTmpXSDFilename.size()) + VSIUnlink(osTmpXSDFilename); + if (pSchema == NULL) + return FALSE; + + xmlSchemaValidCtxtPtr pSchemaValidCtxt; + + pSchemaValidCtxt = xmlSchemaNewValidCtxt((xmlSchemaPtr)pSchema); + + if (pSchemaValidCtxt == NULL) + { + CPLFreeXMLSchema(pSchema); + return FALSE; + } + + xmlSchemaSetValidErrors(pSchemaValidCtxt, + CPLLibXMLWarningErrorCallback, + CPLLibXMLWarningErrorCallback, + (void*) pszXMLFilename); + + int bValid = FALSE; + if( pszXMLFilename[0] == '<' ) + { + xmlDocPtr pDoc = xmlParseDoc((const xmlChar *)pszXMLFilename); + if (pDoc != NULL) + { + bValid = xmlSchemaValidateDoc(pSchemaValidCtxt, pDoc) == 0; + } + xmlFreeDoc(pDoc); + } + else if (strncmp(pszXMLFilename, "/vsi", 4) != 0) + { + bValid = + xmlSchemaValidateFile(pSchemaValidCtxt, pszXMLFilename, 0) == 0; + } + else + { + char* pszXML = CPLLoadContentFromFile(pszXMLFilename); + if (pszXML != NULL) + { + xmlDocPtr pDoc = xmlParseDoc((const xmlChar *)pszXML); + if (pDoc != NULL) + { + bValid = xmlSchemaValidateDoc(pSchemaValidCtxt, pDoc) == 0; + } + xmlFreeDoc(pDoc); + } + CPLFree(pszXML); + } + xmlSchemaFreeValidCtxt(pSchemaValidCtxt); + CPLFreeXMLSchema(pSchema); + + return bValid; +} + +#else // HAVE_RECENT_LIBXML2 + +/************************************************************************/ +/* CPLValidateXML() */ +/************************************************************************/ + +int CPLValidateXML(CPL_UNUSED const char* pszXMLFilename, + CPL_UNUSED const char* pszXSDFilename, + CPL_UNUSED char** papszOptions) +{ + CPLError(CE_Failure, CPLE_NotSupported, + "%s not implemented due to missing libxml2 support", + "CPLValidateXML()"); + return FALSE; +} + +#endif // HAVE_RECENT_LIBXML2 diff --git a/bazaar/plugin/gdal/port/cplgetsymbol.cpp b/bazaar/plugin/gdal/port/cplgetsymbol.cpp new file mode 100644 index 000000000..bb9c74975 --- /dev/null +++ b/bazaar/plugin/gdal/port/cplgetsymbol.cpp @@ -0,0 +1,250 @@ +/****************************************************************************** + * $Id: cplgetsymbol.cpp 27460 2014-06-18 12:36:06Z rouault $ + * + * Project: Common Portability Library + * Purpose: Fetch a function pointer from a shared library / DLL. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2009-2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_conv.h" + +CPL_CVSID("$Id: cplgetsymbol.cpp 27460 2014-06-18 12:36:06Z rouault $"); + + +/* ==================================================================== */ +/* Unix Implementation */ +/* ==================================================================== */ + +/* MinGW32 might define HAVE_DLFCN_H, so skip the unix implementation */ +#if defined(HAVE_DLFCN_H) && !defined(WIN32) + +#define GOT_GETSYMBOL + +#include + +/************************************************************************/ +/* CPLGetSymbol() */ +/************************************************************************/ + +/** + * Fetch a function pointer from a shared library / DLL. + * + * This function is meant to abstract access to shared libraries and + * DLLs and performs functions similar to dlopen()/dlsym() on Unix and + * LoadLibrary() / GetProcAddress() on Windows. + * + * If no support for loading entry points from a shared library is available + * this function will always return NULL. Rules on when this function + * issues a CPLError() or not are not currently well defined, and will have + * to be resolved in the future. + * + * Currently CPLGetSymbol() doesn't try to: + *
      + *
    • prevent the reference count on the library from going up + * for every request, or given any opportunity to unload + * the library. + *
    • Attempt to look for the library in non-standard + * locations. + *
    • Attempt to try variations on the symbol name, like + * pre-prending or post-pending an underscore. + *
    + * + * Some of these issues may be worked on in the future. + * + * @param pszLibrary the name of the shared library or DLL containing + * the function. May contain path to file. If not system supplies search + * paths will be used. + * @param pszSymbolName the name of the function to fetch a pointer to. + * @return A pointer to the function if found, or NULL if the function isn't + * found, or the shared library can't be loaded. + */ + +void *CPLGetSymbol( const char * pszLibrary, const char * pszSymbolName ) + +{ + void *pLibrary; + void *pSymbol; + + pLibrary = dlopen(pszLibrary, RTLD_LAZY); + if( pLibrary == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", dlerror() ); + return NULL; + } + + pSymbol = dlsym( pLibrary, pszSymbolName ); + +#if (defined(__APPLE__) && defined(__MACH__)) + /* On mach-o systems, C symbols have a leading underscore and depending + * on how dlcompat is configured it may or may not add the leading + * underscore. So if dlsym() fails add an underscore and try again. + */ + if( pSymbol == NULL ) + { + char withUnder[strlen(pszSymbolName) + 2]; + withUnder[0] = '_'; withUnder[1] = 0; + strcat(withUnder, pszSymbolName); + pSymbol = dlsym( pLibrary, withUnder ); + } +#endif + + if( pSymbol == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "%s", dlerror() ); + return NULL; + } + + return( pSymbol ); +} + +#endif /* def __unix__ && defined(HAVE_DLFCN_H) */ + +/* ==================================================================== */ +/* Windows Implementation */ +/* ==================================================================== */ +#if defined(WIN32) && !defined(WIN32CE) + +#define GOT_GETSYMBOL + +#include + +/************************************************************************/ +/* CPLGetSymbol() */ +/************************************************************************/ + +void *CPLGetSymbol( const char * pszLibrary, const char * pszSymbolName ) + +{ + void *pLibrary; + void *pSymbol; + UINT uOldErrorMode; + + /* Avoid error boxes to pop up (#5211, #5525) */ + uOldErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + + pLibrary = LoadLibrary(pszLibrary); + + if( pLibrary <= (void*)HINSTANCE_ERROR ) + { + LPVOID lpMsgBuf = NULL; + int nLastError = GetLastError(); + + /* Restore old error mode */ + SetErrorMode(uOldErrorMode); + + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, nLastError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, 0, NULL ); + + CPLError( CE_Failure, CPLE_AppDefined, + "Can't load requested DLL: %s\n%d: %s", + pszLibrary, nLastError, (const char *) lpMsgBuf ); + return NULL; + } + + /* Restore old error mode */ + SetErrorMode(uOldErrorMode); + + pSymbol = (void *) GetProcAddress( (HINSTANCE) pLibrary, pszSymbolName ); + + if( pSymbol == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find requested entry point: %s\n", pszSymbolName ); + return NULL; + } + + return( pSymbol ); +} + +#endif /* def _WIN32 */ + +/* ==================================================================== */ +/* Windows CE Implementation */ +/* ==================================================================== */ +#if defined(WIN32CE) + +#define GOT_GETSYMBOL + +#include "cpl_win32ce_api.h" + +/************************************************************************/ +/* CPLGetSymbol() */ +/************************************************************************/ + +void *CPLGetSymbol( const char * pszLibrary, const char * pszSymbolName ) + +{ + void *pLibrary; + void *pSymbol; + + pLibrary = CE_LoadLibraryA(pszLibrary); + if( pLibrary == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't load requested DLL: %s", pszLibrary ); + return NULL; + } + + pSymbol = (void *) CE_GetProcAddressA( (HINSTANCE) pLibrary, pszSymbolName ); + + if( pSymbol == NULL ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "Can't find requested entry point: %s\n", pszSymbolName ); + return NULL; + } + + return( pSymbol ); +} + +#endif /* def WIN32CE */ + +/* ==================================================================== */ +/* Dummy implementation. */ +/* ==================================================================== */ + +#ifndef GOT_GETSYMBOL + +/************************************************************************/ +/* CPLGetSymbol() */ +/* */ +/* Dummy implementation. */ +/************************************************************************/ + +void *CPLGetSymbol(const char *pszLibrary, const char *pszEntryPoint) + +{ + CPLDebug( "CPL", + "CPLGetSymbol(%s,%s) called. Failed as this is stub" + " implementation.", pszLibrary, pszEntryPoint ); + return NULL; +} +#endif diff --git a/bazaar/plugin/gdal/port/cplkeywordparser.cpp b/bazaar/plugin/gdal/port/cplkeywordparser.cpp new file mode 100644 index 000000000..6a934c80f --- /dev/null +++ b/bazaar/plugin/gdal/port/cplkeywordparser.cpp @@ -0,0 +1,368 @@ +/****************************************************************************** + * $Id: cplkeywordparser.cpp 29123 2015-05-03 11:05:46Z bishop $ + * + * Project: Common Portability Library + * Purpose: Implementation of CPLKeywordParser - a class for parsing + * the keyword format used for files like QuickBird .RPB files. + * This is a slight variation on the NASAKeywordParser used for + * the PDS/ISIS2/ISIS3 formats. + * Author: Frank Warmerdam + * Copyright (c) 2009-2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_string.h" +#include "cplkeywordparser.h" + +CPL_CVSID("$Id"); + +/************************************************************************/ +/* ==================================================================== */ +/* CPLKeywordParser */ +/* ==================================================================== */ +/************************************************************************/ + +/************************************************************************/ +/* CPLKeywordParser() */ +/************************************************************************/ + +CPLKeywordParser::CPLKeywordParser() + +{ + papszKeywordList = NULL; +} + +/************************************************************************/ +/* ~CPLKeywordParser() */ +/************************************************************************/ + +CPLKeywordParser::~CPLKeywordParser() + +{ + CSLDestroy( papszKeywordList ); + papszKeywordList = NULL; +} + +/************************************************************************/ +/* Ingest() */ +/************************************************************************/ + +int CPLKeywordParser::Ingest( VSILFILE *fp ) + +{ +/* -------------------------------------------------------------------- */ +/* Read in buffer till we find END all on it's own line. */ +/* -------------------------------------------------------------------- */ + for( ; TRUE; ) + { + const char *pszCheck; + char szChunk[513]; + + int nBytesRead = VSIFReadL( szChunk, 1, 512, fp ); + + szChunk[nBytesRead] = '\0'; + osHeaderText += szChunk; + + if( nBytesRead < 512 ) + break; + + if( osHeaderText.size() > 520 ) + pszCheck = osHeaderText.c_str() + (osHeaderText.size() - 520); + else + pszCheck = szChunk; + + if( strstr(pszCheck,"\r\nEND;\r\n") != NULL + || strstr(pszCheck,"\nEND;\n") != NULL ) + break; + } + + pszHeaderNext = osHeaderText.c_str(); + +/* -------------------------------------------------------------------- */ +/* Process name/value pairs, keeping track of a "path stack". */ +/* -------------------------------------------------------------------- */ + return ReadGroup( "" ); +} + +/************************************************************************/ +/* ReadGroup() */ +/************************************************************************/ + +int CPLKeywordParser::ReadGroup( const char *pszPathPrefix ) + +{ + CPLString osName, osValue; + + for( ; TRUE; ) + { + if( !ReadPair( osName, osValue ) ) + return FALSE; + + if( EQUAL(osName,"BEGIN_GROUP") || EQUAL(osName,"GROUP") ) + { + if( !ReadGroup( (CPLString(pszPathPrefix) + osValue + ".").c_str() ) ) + return FALSE; + } + else if( EQUALN(osName,"END",3) ) + { + return TRUE; + } + else + { + osName = pszPathPrefix + osName; + papszKeywordList = CSLSetNameValue( papszKeywordList, + osName, osValue ); + } + } +} + +/************************************************************************/ +/* ReadPair() */ +/* */ +/* Read a name/value pair from the input stream. Strip off */ +/* white space, ignore comments, split on '='. */ +/************************************************************************/ + +int CPLKeywordParser::ReadPair( CPLString &osName, CPLString &osValue ) + +{ + osName = ""; + osValue = ""; + + if( !ReadWord( osName ) ) + return FALSE; + + SkipWhite(); + + if( EQUAL(osName,"END") ) + return TRUE; + + if( *pszHeaderNext != '=' ) + { + // ISIS3 does not have anything after the end group/object keyword. + if( EQUAL(osName,"End_Group") || EQUAL(osName,"End_Object") ) + return TRUE; + else + return FALSE; + } + + pszHeaderNext++; + + SkipWhite(); + + osValue = ""; + + // Handle value lists like: Name = (Red, Red) + // or list of lists like : TLCList = ( (0, 0.000000), (8299, 4.811014) ); + if( *pszHeaderNext == '(' ) + { + CPLString osWord; + int nDepth = 0; + const char* pszLastPos = pszHeaderNext; + + while( ReadWord( osWord ) && pszLastPos != pszHeaderNext) + { + SkipWhite(); + pszLastPos = pszHeaderNext; + + osValue += osWord; + const char* pszIter = osWord.c_str(); + int bInQuote = FALSE; + while(*pszIter != '\0') + { + if (*pszIter == '"') + bInQuote = !bInQuote; + else if (!bInQuote) + { + if (*pszIter == '(') + nDepth ++; + else if (*pszIter == ')') + { + nDepth --; + if (nDepth == 0) + break; + } + } + pszIter ++; + } + if (*pszIter == ')' && nDepth == 0) + break; + } + } + + else // Handle more normal "single word" values. + { + if( !ReadWord( osValue ) ) + return FALSE; + + } + + SkipWhite(); + + // No units keyword? + if( *pszHeaderNext != '<' ) + return TRUE; + + // Append units keyword. For lines that like like this: + // MAP_RESOLUTION = 4.0 + + CPLString osWord; + + osValue += " "; + + while( ReadWord( osWord ) ) + { + SkipWhite(); + + osValue += osWord; + if( osWord[strlen(osWord)-1] == '>' ) + break; + } + + return TRUE; +} + +/************************************************************************/ +/* ReadWord() */ +/************************************************************************/ + +int CPLKeywordParser::ReadWord( CPLString &osWord ) + +{ + osWord = ""; + + SkipWhite(); + + if( *pszHeaderNext == '\0' ) + return FALSE; + + while( *pszHeaderNext != '\0' + && *pszHeaderNext != '=' + && *pszHeaderNext != ';' + && !isspace((unsigned char)*pszHeaderNext) ) + { + if( *pszHeaderNext == '"' ) + { + osWord += *(pszHeaderNext++); + while( *pszHeaderNext != '"' ) + { + if( *pszHeaderNext == '\0' ) + return FALSE; + + osWord += *(pszHeaderNext++); + } + osWord += *(pszHeaderNext++); + } + else if( *pszHeaderNext == '\'' ) + { + osWord += *(pszHeaderNext++); + while( *pszHeaderNext != '\'' ) + { + if( *pszHeaderNext == '\0' ) + return FALSE; + + osWord += *(pszHeaderNext++); + } + osWord += *(pszHeaderNext++); + } + else + { + osWord += *pszHeaderNext; + pszHeaderNext++; + } + } + + if( *pszHeaderNext == ';' ) + pszHeaderNext++; + + return TRUE; +} + +/************************************************************************/ +/* SkipWhite() */ +/************************************************************************/ + +void CPLKeywordParser::SkipWhite() + +{ + for( ; TRUE; ) + { + // Skip white space (newline, space, tab, etc ) + if( isspace( (unsigned char)*pszHeaderNext ) ) + { + pszHeaderNext++; + continue; + } + + // Skip C style comments + if( *pszHeaderNext == '/' && pszHeaderNext[1] == '*' ) + { + pszHeaderNext += 2; + + while( *pszHeaderNext != '\0' + && (*pszHeaderNext != '*' + || pszHeaderNext[1] != '/' ) ) + { + pszHeaderNext++; + } + + pszHeaderNext += 2; + continue; + } + + // Skip # style comments + if( *pszHeaderNext == '#' ) + { + pszHeaderNext += 1; + + // consume till end of line. + while( *pszHeaderNext != '\0' + && *pszHeaderNext != 10 + && *pszHeaderNext != 13 ) + { + pszHeaderNext++; + } + continue; + } + + // not white space, return. + return; + } +} + +/************************************************************************/ +/* GetKeyword() */ +/************************************************************************/ + +const char *CPLKeywordParser::GetKeyword( const char *pszPath, + const char *pszDefault ) + +{ + const char *pszResult; + + pszResult = CSLFetchNameValue( papszKeywordList, pszPath ); + if( pszResult == NULL ) + return pszDefault; + else + return pszResult; +} diff --git a/bazaar/plugin/gdal/port/cplkeywordparser.h b/bazaar/plugin/gdal/port/cplkeywordparser.h new file mode 100644 index 000000000..050807d31 --- /dev/null +++ b/bazaar/plugin/gdal/port/cplkeywordparser.h @@ -0,0 +1,66 @@ +/****************************************************************************** + * $Id: cplkeywordparser.h 20996 2010-10-28 18:38:15Z rouault $ + * + * Project: Common Portability Library + * Purpose: Implementation of CPLKeywordParser - a class for parsing + * the keyword format used for files like QuickBird .RPB files. + * This is a slight variation on the NASAKeywordParser used for + * the PDS/ISIS2/ISIS3 formats. + * Author: Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef CPL_KEYWORD_PARSER +#define CPL_KEYWORD_PARSER + +#include "cpl_string.h" + +/************************************************************************/ +/* ==================================================================== */ +/* CPLKeywordParser */ +/* ==================================================================== */ +/************************************************************************/ + +class CPLKeywordParser +{ + char **papszKeywordList; + + CPLString osHeaderText; + const char *pszHeaderNext; + + void SkipWhite(); + int ReadWord( CPLString &osWord ); + int ReadPair( CPLString &osName, CPLString &osValue ); + int ReadGroup( const char *pszPathPrefix ); + +public: + CPLKeywordParser(); + ~CPLKeywordParser(); + + int Ingest( VSILFILE *fp ); + + const char *GetKeyword( const char *pszPath, const char *pszDefault=NULL ); + char **GetAllKeywords() { return papszKeywordList; } +}; + +#endif /* def CPL_KEYWORD_PARSER */ diff --git a/bazaar/plugin/gdal/port/cplstring.cpp b/bazaar/plugin/gdal/port/cplstring.cpp new file mode 100644 index 000000000..564ead8b7 --- /dev/null +++ b/bazaar/plugin/gdal/port/cplstring.cpp @@ -0,0 +1,444 @@ +/****************************************************************************** + * $Id: cplstring.cpp 28204 2014-12-24 06:00:26Z goatbar $ + * + * Project: GDAL + * Purpose: CPLString implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2005, Frank Warmerdam + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: cplstring.cpp 28204 2014-12-24 06:00:26Z goatbar $"); + +/* + * The CPLString class is derived from std::string, so the vast majority + * of the implementation comes from that. This module is just the extensions + * we add. + */ + +/************************************************************************/ +/* Printf() */ +/************************************************************************/ + +CPLString &CPLString::Printf( const char *pszFormat, ... ) + +{ + va_list args; + + va_start( args, pszFormat ); + vPrintf( pszFormat, args ); + va_end( args ); + + return *this; +} + +/************************************************************************/ +/* vPrintf() */ +/************************************************************************/ + +CPLString &CPLString::vPrintf( const char *pszFormat, va_list args ) + +{ +/* -------------------------------------------------------------------- */ +/* This implementation for platforms without vsnprintf() will */ +/* just plain fail if the formatted contents are too large. */ +/* -------------------------------------------------------------------- */ + +#if !defined(HAVE_VSNPRINTF) + char *pszBuffer = (char *) CPLMalloc(30000); + if( CPLvsnprintf( pszBuffer, 30000, pszFormat, args) > 29998 ) + { + CPLError( CE_Fatal, CPLE_AppDefined, + "CPLString::vPrintf() ... buffer overrun." ); + } + *this = pszBuffer; + CPLFree( pszBuffer ); + +/* -------------------------------------------------------------------- */ +/* This should grow a big enough buffer to hold any formatted */ +/* result. */ +/* -------------------------------------------------------------------- */ +#else + char szModestBuffer[500]; + int nPR; + va_list wrk_args; + +#ifdef va_copy + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + + nPR = CPLvsnprintf( szModestBuffer, sizeof(szModestBuffer), pszFormat, + wrk_args ); + if( nPR == -1 || nPR >= (int) sizeof(szModestBuffer)-1 ) + { + int nWorkBufferSize = 2000; + char *pszWorkBuffer = (char *) CPLMalloc(nWorkBufferSize); + +#ifdef va_copy + va_end( wrk_args ); + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + while( (nPR=CPLvsnprintf( pszWorkBuffer, nWorkBufferSize, pszFormat,wrk_args)) + >= nWorkBufferSize-1 + || nPR == -1 ) + { + nWorkBufferSize *= 4; + pszWorkBuffer = (char *) CPLRealloc(pszWorkBuffer, + nWorkBufferSize ); +#ifdef va_copy + va_end( wrk_args ); + va_copy( wrk_args, args ); +#else + wrk_args = args; +#endif + } + *this = pszWorkBuffer; + CPLFree( pszWorkBuffer ); + } + else + { + *this = szModestBuffer; + } +#ifdef va_copy + va_end( wrk_args ); +#endif + +#endif /* !defined(HAVE_VSNPRINTF) */ + + return *this; +} + +/************************************************************************/ +/* FormatC() */ +/************************************************************************/ + +/** + * Format double in C locale. + * + * The passed value is formatted using the C locale (period as decimal + * seperator) and appended to the target CPLString. + * + * @param dfValue the value to format. + * @param pszFormat the sprintf() style format to use or omit for default. + * Note that this format string should only include one substitution argument + * and it must be for a double (%f or %g). + * + * @return a reference to the CPLString. + */ + +CPLString &CPLString::FormatC( double dfValue, const char *pszFormat ) + +{ + if( pszFormat == NULL ) + pszFormat = "%g"; + + char szWork[512]; // presumably long enough for any number? + + CPLsprintf( szWork, pszFormat, dfValue ); + CPLAssert( strlen(szWork) < sizeof(szWork) ); + + *this += szWork; + + return *this; +} + +/************************************************************************/ +/* Trim() */ +/************************************************************************/ + +/** + * Trim white space. + * + * Trims white space off the let and right of the string. White space + * is any of a space, a tab, a newline ('\n') or a carriage control ('\r'). + * + * @return a reference to the CPLString. + */ + +CPLString &CPLString::Trim() + +{ + size_t iLeft, iRight; + static const char szWhitespace[] = " \t\r\n"; + + iLeft = find_first_not_of( szWhitespace ); + iRight = find_last_not_of( szWhitespace ); + + if( iLeft == std::string::npos ) + { + erase(); + return *this; + } + + assign( substr( iLeft, iRight - iLeft + 1 ) ); + + return *this; +} + +/************************************************************************/ +/* Recode() */ +/************************************************************************/ + +CPLString &CPLString::Recode( const char *pszSrcEncoding, + const char *pszDstEncoding ) + +{ + if( pszSrcEncoding == NULL ) + pszSrcEncoding = CPL_ENC_UTF8; + if( pszDstEncoding == NULL ) + pszDstEncoding = CPL_ENC_UTF8; + + if( strcmp(pszSrcEncoding,pszDstEncoding) == 0 ) + return *this; + + char *pszRecoded = CPLRecode( c_str(), + pszSrcEncoding, + pszDstEncoding ); + + if( pszRecoded == NULL ) + return *this; + + assign( pszRecoded ); + CPLFree( pszRecoded ); + + return *this; +} + +/************************************************************************/ +/* ifind() */ +/************************************************************************/ + +/** + * Case insensitive find() alternative. + * + * @param str substring to find. + * @param pos offset in the string at which the search starts. + * @return the position of substring in the string or std::string::npos if not found. + * @since GDAL 1.9.0 + */ + +size_t CPLString::ifind( const std::string & str, size_t pos ) const + +{ + return ifind( str.c_str(), pos ); +} + +/** + * Case insensitive find() alternative. + * + * @param s substring to find. + * @param nPos offset in the string at which the search starts. + * @return the position of the substring in the string or std::string::npos if not found. + * @since GDAL 1.9.0 + */ + +size_t CPLString::ifind( const char *s, size_t nPos ) const + +{ + const char *pszHaystack = c_str(); + char chFirst = (char) ::tolower( s[0] ); + int nTargetLen = strlen(s); + + if( nPos > size() ) + nPos = size(); + + pszHaystack += nPos; + + while( *pszHaystack != '\0' ) + { + if( chFirst == ::tolower(*pszHaystack) ) + { + if( EQUALN(pszHaystack,s,nTargetLen) ) + return nPos; + } + + nPos++; + pszHaystack++; + } + + return std::string::npos; +} + +/************************************************************************/ +/* toupper() */ +/************************************************************************/ + +/** + * Convert to upper case in place. + */ + +CPLString &CPLString::toupper() + +{ + size_t i; + + for( i = 0; i < size(); i++ ) + (*this)[i] = (char) ::toupper( (*this)[i] ); + + return *this; +} + +/************************************************************************/ +/* tolower() */ +/************************************************************************/ + +/** + * Convert to lower case in place. + */ + +CPLString &CPLString::tolower() + +{ + size_t i; + + for( i = 0; i < size(); i++ ) + (*this)[i] = (char) ::tolower( (*this)[i] ); + + return *this; +} + +/************************************************************************/ +/* CPLURLGetValue() */ +/************************************************************************/ + +/** + * Return the value matching a key from a key=value pair in a URL. + * + * @param pszURL the URL. + * @param pszKey the key to find. + * @return the value of empty string if not found. + * @since GDAL 1.9.0 + */ +CPLString CPLURLGetValue(const char* pszURL, const char* pszKey) +{ + CPLString osKey(pszKey); + osKey += "="; + size_t nKeyPos = CPLString(pszURL).ifind(osKey); + if (nKeyPos != std::string::npos && nKeyPos > 0 && + (pszURL[nKeyPos-1] == '?' || pszURL[nKeyPos-1] == '&')) + { + CPLString osValue(pszURL + nKeyPos + strlen(osKey)); + const char* pszValue = osValue.c_str(); + const char* pszSep = strchr(pszValue, '&'); + if (pszSep) + { + osValue.resize(pszSep - pszValue); + } + return osValue; + } + return ""; +} + +/************************************************************************/ +/* CPLURLAddKVP() */ +/************************************************************************/ + +/** + * Return a new URL with a new key=value pair. + * + * @param pszURL the URL. + * @param pszKey the key to find. + * @param pszValue the value of the key (may be NULL to unset an existing KVP). + * @return the modified URL. + * @since GDAL 1.9.0 + */ +CPLString CPLURLAddKVP(const char* pszURL, const char* pszKey, + const char* pszValue) +{ + CPLString osURL(pszURL); + if (strchr(osURL, '?') == NULL) + osURL += "?"; + pszURL = osURL.c_str(); + + CPLString osKey(pszKey); + osKey += "="; + size_t nKeyPos = osURL.ifind(osKey); + if (nKeyPos != std::string::npos && nKeyPos > 0 && + (pszURL[nKeyPos-1] == '?' || pszURL[nKeyPos-1] == '&')) + { + CPLString osNewURL(osURL); + osNewURL.resize(nKeyPos); + if (pszValue) + { + osNewURL += osKey; + osNewURL += pszValue; + } + const char* pszNext = strchr(pszURL + nKeyPos, '&'); + if (pszNext) + { + if (osNewURL[osNewURL.size()-1] == '&' + || osNewURL[osNewURL.size()-1] == '?' ) + osNewURL += pszNext + 1; + else + osNewURL += pszNext; + } + return osNewURL; + } + else + { + if (pszValue) + { + if (osURL[osURL.size()-1] != '&' && osURL[osURL.size()-1] != '?') + osURL += '&'; + osURL += osKey; + osURL += pszValue; + } + return osURL; + } +} + +/************************************************************************/ +/* CPLOPrintf() */ +/************************************************************************/ + +CPLString CPLOPrintf( const char *pszFormat, ... ) + +{ + va_list args; + CPLString osTarget; + + va_start( args, pszFormat ); + osTarget.vPrintf( pszFormat, args ); + va_end( args ); + + return osTarget; +} + +/************************************************************************/ +/* CPLOvPrintf() */ +/************************************************************************/ + +CPLString CPLOvPrintf( const char *pszFormat, va_list args ) + +{ + CPLString osTarget; + osTarget.vPrintf( pszFormat, args); + return osTarget; +} diff --git a/bazaar/plugin/gdal/port/cplstringlist.cpp b/bazaar/plugin/gdal/port/cplstringlist.cpp new file mode 100644 index 000000000..5f13d7524 --- /dev/null +++ b/bazaar/plugin/gdal/port/cplstringlist.cpp @@ -0,0 +1,772 @@ +/****************************************************************************** + * $Id: cplstringlist.cpp 27496 2014-07-06 11:23:58Z rouault $ + * + * Project: GDAL + * Purpose: CPLStringList implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2011, Frank Warmerdam + * Copyright (c) 2011, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "cpl_string.h" +#include + +CPL_CVSID("$Id: cplstringlist.cpp 27496 2014-07-06 11:23:58Z rouault $"); + +/************************************************************************/ +/* CPLStringList() */ +/************************************************************************/ + +CPLStringList::CPLStringList() + +{ + Initialize(); +} + +/************************************************************************/ +/* CPLStringList() */ +/************************************************************************/ + +/** + * CPLStringList constructor. + * + * @param papszListIn the NULL terminated list of strings to consume. + * @param bTakeOwnership TRUE if the CPLStringList should take ownership + * of the list of strings which implies responsibility to free them. + */ + +CPLStringList::CPLStringList( char **papszListIn, int bTakeOwnership ) + +{ + Initialize(); + Assign( papszListIn, bTakeOwnership ); +} + +/************************************************************************/ +/* CPLStringList() */ +/************************************************************************/ + +//! Copy constructor +CPLStringList::CPLStringList( const CPLStringList &oOther ) + +{ + Initialize(); + Assign( oOther.papszList, FALSE ); + + // We don't want to just retain a reference to the others list + // as we don't want to make assumptions about it's lifetime that + // might surprise the client developer. + MakeOurOwnCopy(); + bIsSorted = oOther.bIsSorted; +} + +/************************************************************************/ +/* operator=() */ +/************************************************************************/ + +CPLStringList &CPLStringList::operator=(const CPLStringList& oOther) +{ + if (this != &oOther) + { + Assign( oOther.papszList, FALSE ); + + // We don't want to just retain a reference to the others list + // as we don't want to make assumptions about it's lifetime that + // might surprise the client developer. + MakeOurOwnCopy(); + bIsSorted = oOther.bIsSorted; + } + + return *this; +} + +/************************************************************************/ +/* Initialize() */ +/************************************************************************/ + +void CPLStringList::Initialize() + +{ + papszList = NULL; + nCount = 0; + nAllocation = 0; + bOwnList = FALSE; + bIsSorted = FALSE; +} + +/************************************************************************/ +/* ~CPLStringList() */ +/************************************************************************/ + +CPLStringList::~CPLStringList() + +{ + Clear(); +} + +/************************************************************************/ +/* Clear() */ +/************************************************************************/ + +/** + * Clear the string list. + */ +CPLStringList &CPLStringList::Clear() + +{ + if( bOwnList ) + { + CSLDestroy( papszList ); + papszList = NULL; + + bOwnList = FALSE; + nAllocation = 0; + nCount = 0; + } + + return *this; +} + +/************************************************************************/ +/* Assign() */ +/************************************************************************/ + +/** + * Assign a list of strings. + * + * + * @param papszListIn the NULL terminated list of strings to consume. + * @param bTakeOwnership TRUE if the CPLStringList should take ownership + * of the list of strings which implies responsibility to free them. + * + * @return a reference to the CPLStringList on which it was invoked. + */ + +CPLStringList &CPLStringList::Assign( char **papszListIn, int bTakeOwnership ) + +{ + Clear(); + + papszList = papszListIn; + bOwnList = bTakeOwnership; + + if( papszList == NULL || *papszList == NULL ) + nCount = 0; + else + nCount = -1; // unknown + + nAllocation = 0; + bIsSorted = FALSE; + + return *this; +} + +/************************************************************************/ +/* Count() */ +/************************************************************************/ + +/** + * @return count of strings in the list, zero if empty. + */ + +int CPLStringList::Count() const + +{ + if( nCount == -1 ) + { + if( papszList == NULL ) + { + nCount = nAllocation = 0; + } + else + { + nCount = CSLCount( papszList ); + nAllocation = MAX(nCount+1,nAllocation); + } + } + + return nCount; +} + +/************************************************************************/ +/* MakeOurOwnCopy() */ +/* */ +/* If we don't own the list, a copy is made which we own. */ +/* Necessary if we are going to modify the list. */ +/************************************************************************/ + +void CPLStringList::MakeOurOwnCopy() + +{ + if( bOwnList ) + return; + + if( papszList == NULL ) + return; + + Count(); + bOwnList = TRUE; + papszList = CSLDuplicate( papszList ); + nAllocation = nCount+1; +} + +/************************************************************************/ +/* EnsureAllocation() */ +/* */ +/* Ensure we have enough room allocated for at least the */ +/* requested number of strings (so nAllocation will be at least */ +/* one more than the target) */ +/************************************************************************/ + +void CPLStringList::EnsureAllocation( int nMaxList ) + +{ + if( !bOwnList ) + MakeOurOwnCopy(); + + if( nAllocation <= nMaxList ) + { + nAllocation = MAX(nAllocation*2 + 20,nMaxList+1); + if( papszList == NULL ) + { + papszList = (char **) CPLCalloc(nAllocation,sizeof(char*)); + bOwnList = TRUE; + nCount = 0; + } + else + papszList = (char **) CPLRealloc(papszList, nAllocation*sizeof(char*)); + } +} + +/************************************************************************/ +/* AddStringDirectly() */ +/************************************************************************/ + +/** + * Add a string to the list. + * + * This method is similar to AddString(), but ownership of the + * pszNewString is transferred to the CPLStringList class. + * + * @param pszNewString the string to add to the list. + */ + +CPLStringList &CPLStringList::AddStringDirectly( char *pszNewString ) + +{ + if( nCount == -1 ) + Count(); + + EnsureAllocation( nCount+1 ); + + papszList[nCount++] = pszNewString; + papszList[nCount] = NULL; + + bIsSorted = FALSE; + + return *this; +} + +/************************************************************************/ +/* AddString() */ +/************************************************************************/ + +/** + * Add a string to the list. + * + * A copy of the passed in string is made and inserted in the list. + * + * @param pszNewString the string to add to the list. + */ + +CPLStringList &CPLStringList::AddString( const char *pszNewString ) + +{ + return AddStringDirectly( CPLStrdup( pszNewString ) ); +} + +/************************************************************************/ +/* AddNameValue() */ +/************************************************************************/ + +/** + * A a name=value entry to the list. + * + * A key=value string is prepared and appended to the list. There is no + * check for other values for the same key in the list. + * + * @param pszKey the key name to add. + * @param pszValue the key value to add. + */ + +CPLStringList &CPLStringList::AddNameValue( const char *pszKey, + const char *pszValue ) + +{ + if (pszKey == NULL || pszValue==NULL) + return *this; + + MakeOurOwnCopy(); + +/* -------------------------------------------------------------------- */ +/* Format the line. */ +/* -------------------------------------------------------------------- */ + char *pszLine; + pszLine = (char *) CPLMalloc(strlen(pszKey)+strlen(pszValue)+2); + sprintf( pszLine, "%s=%s", pszKey, pszValue ); + +/* -------------------------------------------------------------------- */ +/* If we don't need to keep the sort order things are pretty */ +/* straight forward. */ +/* -------------------------------------------------------------------- */ + if( !IsSorted() ) + return AddStringDirectly( pszLine ); + +/* -------------------------------------------------------------------- */ +/* Find the proper insertion point. */ +/* -------------------------------------------------------------------- */ + CPLAssert( IsSorted() ); + int iKey = FindSortedInsertionPoint( pszLine ); + InsertStringDirectly( iKey, pszLine ); + bIsSorted = TRUE; // we have actually preserved sort order. + + return *this; +} + +/************************************************************************/ +/* SetNameValue() */ +/************************************************************************/ + +/** + * Set name=value entry in the list. + * + * Similar to AddNameValue(), except if there is already a value for + * the key in the list it is replaced instead of adding a new entry to + * the list. If pszValue is NULL any existing key entry is removed. + * + * @param pszKey the key name to add. + * @param pszValue the key value to add. + */ + +CPLStringList &CPLStringList::SetNameValue( const char *pszKey, + const char *pszValue ) + +{ + int iKey = FindName( pszKey ); + + if( iKey == -1 ) + return AddNameValue( pszKey, pszValue ); + + Count(); + MakeOurOwnCopy(); + + CPLFree( papszList[iKey] ); + if( pszValue == NULL ) // delete entry + { + + // shift everything down by one. + do + { + papszList[iKey] = papszList[iKey+1]; + } + while( papszList[iKey++] != NULL ); + + nCount--; + } + else + { + char *pszLine; + pszLine = (char *) CPLMalloc(strlen(pszKey)+strlen(pszValue)+2); + sprintf( pszLine, "%s=%s", pszKey, pszValue ); + + papszList[iKey] = pszLine; + } + + return *this; +} + +/************************************************************************/ +/* operator[] */ +/************************************************************************/ + +/** + * Fetch entry "i". + * + * Fetches the requested item in the list. Note that the returned string + * remains owned by the CPLStringList. If "i" is out of range NULL is + * returned. + * + * @param i the index of the list item to return. + * @return selected entry in the list. + */ +char *CPLStringList::operator[]( int i ) + +{ + if( nCount == -1 ) + Count(); + + if( i < 0 || i >= nCount ) + return NULL; + else + return papszList[i]; +} + +const char *CPLStringList::operator[]( int i ) const + +{ + if( nCount == -1 ) + Count(); + + if( i < 0 || i >= nCount ) + return NULL; + else + return papszList[i]; +} + +/************************************************************************/ +/* StealList() */ +/************************************************************************/ + +/** + * Seize ownership of underlying string array. + * + * This method is simmilar to List(), except that the returned list is + * now owned by the caller and the CPLStringList is emptied. + * + * @return the C style string list. + */ +char **CPLStringList::StealList() + +{ + char **papszRetList = papszList; + + bOwnList = FALSE; + papszList = NULL; + nCount = 0; + nAllocation = 0; + + return papszRetList; +} + + +static int CPLCompareKeyValueString(const char* pszKVa, const char* pszKVb) +{ + const char* pszItera = pszKVa; + const char* pszIterb = pszKVb; + while( TRUE ) + { + char cha = *pszItera; + char chb = *pszIterb; + if( cha == '=' || cha == '\0' ) + { + if( chb == '=' || chb == '\0' ) + return 0; + else + return -1; + } + if( chb == '=' || chb == '\0' ) + { + return 1; + } + if( cha >= 'a' && cha <= 'z' ) + cha -= ('a' - 'A'); + if( chb >= 'a' && chb <= 'z' ) + chb -= ('a' - 'A'); + if( cha < chb ) + return -1; + else if( cha > chb ) + return 1; + pszItera ++; + pszIterb ++; + } +} + +/************************************************************************/ +/* llCompareStr() */ +/* */ +/* Note this is case insensitive! This is because we normally */ +/* treat key value keywords as case insensitive. */ +/************************************************************************/ +static int llCompareStr(const void *a, const void *b) +{ + return CPLCompareKeyValueString((*(const char **)a),(*(const char **)b)); +} + +/************************************************************************/ +/* Sort() */ +/************************************************************************/ + +/** + * Sort the entries in the list and mark list sorted. + * + * Note that once put into "sorted" mode, the CPLStringList will attempt to + * keep things in sorted order through calls to AddString(), + * AddStringDirectly(), AddNameValue(), SetNameValue(). Complete list + * assignments (via Assign() and operator= will clear the sorting state. + * When in sorted order FindName(), FetchNameValue() and FetchNameValueDef() + * will do a binary search to find the key, substantially improve lookup + * performance in large lists. + */ + +CPLStringList &CPLStringList::Sort() + +{ + Count(); + MakeOurOwnCopy(); + + qsort( papszList, nCount, sizeof(char*), llCompareStr ); + bIsSorted = TRUE; + + return *this; +} + +/************************************************************************/ +/* FindName() */ +/************************************************************************/ + +/** + * Get index of given name/value keyword. + * + * Note that this search is for a line in the form name=value or name:value. + * Use FindString() or PartialFindString() for searches not based on name=value + * pairs. + * + * @param pszKey the name to search for. + * + * @return the string list index of this name, or -1 on failure. + */ + +int CPLStringList::FindName( const char *pszKey ) const + +{ + if( !IsSorted() ) + return CSLFindName( papszList, pszKey ); + + // If we are sorted, we can do an optimized binary search. + int iStart=0, iEnd=nCount-1; + int nKeyLen = strlen(pszKey); + + while( iStart <= iEnd ) + { + int iMiddle = (iEnd+iStart)/2; + const char *pszMiddle = papszList[iMiddle]; + + if (EQUALN(pszMiddle, pszKey, nKeyLen) + && (pszMiddle[nKeyLen] == '=' || pszMiddle[nKeyLen] == ':') ) + return iMiddle; + + if( CPLCompareKeyValueString(pszKey,pszMiddle) < 0 ) + iEnd = iMiddle-1; + else + iStart = iMiddle+1; + } + + return -1; +} + +/************************************************************************/ +/* FetchBoolean() */ +/************************************************************************/ +/** + * + * Check for boolean key value. + * + * In a CPLStringList of "Name=Value" pairs, look to see if there is a key + * with the given name, and if it can be interpreted as being TRUE. If + * the key appears without any "=Value" portion it will be considered true. + * If the value is NO, FALSE or 0 it will be considered FALSE otherwise + * if the key appears in the list it will be considered TRUE. If the key + * doesn't appear at all, the indicated default value will be returned. + * + * @param pszKey the key value to look for (case insensitive). + * @param bDefault the value to return if the key isn't found at all. + * + * @return TRUE or FALSE + */ + +int CPLStringList::FetchBoolean( const char *pszKey, int bDefault ) const + +{ + const char *pszValue = FetchNameValue( pszKey ); + + if( pszValue == NULL ) + return bDefault; + else + return CSLTestBoolean( pszValue ); +} + +/************************************************************************/ +/* FetchNameValue() */ +/************************************************************************/ + +/** + * Fetch value associated with this key name. + * + * If this list sorted, a fast binary search is done, otherwise a linear + * scan is done. Name lookup is case insensitive. + * + * @param pszName the key name to search for. + * + * @return the corresponding value or NULL if not found. The returned string + * should not be modified and points into internal object state that may + * change on future calls. + */ + +const char *CPLStringList::FetchNameValue( const char *pszName ) const + +{ + int iKey = FindName( pszName ); + + if( iKey == -1 ) + return NULL; + + CPLAssert( papszList[iKey][strlen(pszName)] == '=' + || papszList[iKey][strlen(pszName)] == ':' ); + + return papszList[iKey] + strlen(pszName)+1; +} + +/************************************************************************/ +/* FetchNameValueDef() */ +/************************************************************************/ + +/** + * Fetch value associated with this key name. + * + * If this list sorted, a fast binary search is done, otherwise a linear + * scan is done. Name lookup is case insensitive. + * + * @param pszName the key name to search for. + * @param pszDefault the default value returned if the named entry isn't found. + * + * @return the corresponding value or the passed default if not found. + */ + +const char *CPLStringList::FetchNameValueDef( const char *pszName, + const char *pszDefault ) const + +{ + const char *pszValue = FetchNameValue( pszName ); + if( pszValue == NULL ) + return pszDefault; + else + return pszValue; +} + +/************************************************************************/ +/* InsertString() */ +/************************************************************************/ + +/** + * \fn CPLStringList *CPLStringList::InsertString( int nInsertAtLineNo, + * const char *pszNewLine ); + * + * \brief Insert into the list at identified location. + * + * This method will insert a string into the list at the identified + * location. The insertion point must be within or at the end of the list. + * The following entries are pushed down to make space. + * + * @param nInsertAtLineNo the line to insert at, zero to insert at front. + * @param pszNewLine to the line to insert. This string will be copied. + */ + + +/************************************************************************/ +/* InsertStringDirectly() */ +/************************************************************************/ + +/** + * Insert into the list at identified location. + * + * This method will insert a string into the list at the identified + * location. The insertion point must be within or at the end of the list. + * The following entries are pushed down to make space. + * + * @param nInsertAtLineNo the line to insert at, zero to insert at front. + * @param pszNewLine to the line to insert, the ownership of this string + * will be taken over the by the object. It must have been allocated on the + * heap. + */ + +CPLStringList &CPLStringList::InsertStringDirectly( int nInsertAtLineNo, + char *pszNewLine ) + +{ + if( nCount == -1 ) + Count(); + + EnsureAllocation( nCount+1 ); + + if( nInsertAtLineNo < 0 || nInsertAtLineNo > nCount ) + { + CPLError( CE_Failure, CPLE_AppDefined, + "CPLStringList::InsertString() requested beyond list end." ); + return *this; + } + + bIsSorted = FALSE; + + for( int i = nCount; i > nInsertAtLineNo; i-- ) + papszList[i] = papszList[i-1]; + + papszList[nInsertAtLineNo] = pszNewLine; + papszList[++nCount] = NULL; + + return *this; +} + +/************************************************************************/ +/* FindSortedInsertionPoint() */ +/* */ +/* Find the location at which the indicated line should be */ +/* inserted in order to keep things in sorted order. */ +/************************************************************************/ + +int CPLStringList::FindSortedInsertionPoint( const char *pszLine ) + +{ + CPLAssert( IsSorted() ); + + int iStart=0, iEnd=nCount-1; + + while( iStart <= iEnd ) + { + int iMiddle = (iEnd+iStart)/2; + const char *pszMiddle = papszList[iMiddle]; + + if( CPLCompareKeyValueString(pszLine,pszMiddle) < 0 ) + iEnd = iMiddle-1; + else + iStart = iMiddle+1; + } + + iEnd++; + CPLAssert( iEnd >= 0 && iEnd <= nCount ); + CPLAssert( iEnd == 0 + || CPLCompareKeyValueString(pszLine,papszList[iEnd-1]) >= 0 ); + CPLAssert( iEnd == nCount + || CPLCompareKeyValueString(pszLine,papszList[iEnd]) <= 0 ); + + return iEnd; +} diff --git a/bazaar/plugin/gdal/port/gdal_csv.h b/bazaar/plugin/gdal/port/gdal_csv.h new file mode 100644 index 000000000..e4f430c8b --- /dev/null +++ b/bazaar/plugin/gdal/port/gdal_csv.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * $Id: gdal_csv.h 27044 2014-03-16 23:41:27Z rouault $ + * + * Project: Common Portability Library + * Purpose: Functions for reading and scaning CSV (comma separated, + * variable length text files holding tables) files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * Copyright (c) 2010, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef GDAL_CSV_H_INCLUDED +#define GDAL_CSV_H_INCLUDED + +#include "cpl_port.h" + +CPL_C_START +const char * GDALDefaultCSVFilename( const char *pszBasename ); +CPL_C_END + +#endif diff --git a/bazaar/plugin/gdal/port/makefile.vc b/bazaar/plugin/gdal/port/makefile.vc new file mode 100644 index 000000000..1de656320 --- /dev/null +++ b/bazaar/plugin/gdal/port/makefile.vc @@ -0,0 +1,89 @@ +# +# CPL (Common Portability Library) makefile for MSVC +# + +OBJ = cpl_conv.obj \ + cpl_error.obj \ + cpl_string.obj \ + cplstring.obj \ + cplstringlist.obj \ + cpl_strtod.obj \ + cpl_vsisimple.obj \ + cplgetsymbol.obj \ + cpl_path.obj \ + cpl_csv.obj \ + cpl_findfile.obj \ + cpl_vsil_win32.obj \ + cpl_minixml.obj \ + cpl_multiproc.obj \ + cpl_list.obj \ + cpl_getexecpath.obj \ + cpl_vsil.obj \ + cpl_vsi_mem.obj \ + cpl_http.obj \ + cpl_hash_set.obj \ + cplkeywordparser.obj \ + cpl_recode.obj \ + cpl_recode_iconv.obj \ + cpl_recode_stub.obj \ + cpl_quad_tree.obj \ + cpl_vsil_gzip.obj \ + cpl_minizip_ioapi.obj \ + cpl_minizip_unzip.obj \ + cpl_minizip_zip.obj \ + cpl_vsil_subfile.obj \ + cpl_atomic_ops.obj \ + cpl_time.obj \ + cpl_vsil_stdout.obj \ + cpl_vsil_sparsefile.obj \ + cpl_vsil_abstract_archive.obj \ + cpl_vsil_tar.obj \ + cpl_vsil_curl.obj \ + cpl_vsil_curl_streaming.obj \ + cpl_vsil_stdin.obj \ + cpl_vsil_buffered_reader.obj \ + cpl_vsil_cache.obj \ + cpl_base64.obj \ + cpl_xml_validate.obj \ + cpl_spawn.obj \ + cpl_google_oauth2.obj \ + cpl_progress.obj \ + cpl_virtualmem.obj \ + $(ODBC_OBJ) + +LIB = cpl.lib + +GDAL_ROOT = .. + +!INCLUDE ..\nmake.opt + +EXTRAFLAGS = -I..\frmts\zlib -DHAVE_LIBZ + +!IFDEF LIBICONV_INCLUDE +EXTRAFLAGS = $(EXTRAFLAGS) -DHAVE_ICONV $(LIBICONV_CFLAGS) $(LIBICONV_INCLUDE) +!ENDIF + +!IFDEF CURL_INC +EXTRAFLAGS = $(EXTRAFLAGS) -DHAVE_CURL $(CURL_CFLAGS) $(CURL_INC) +!ENDIF + +!IFDEF LIBXML2_INC +EXTRAFLAGS = $(EXTRAFLAGS) -DHAVE_LIBXML2 $(LIBXML2_INC) +!ENDIF + +!IFDEF ODBC_SUPPORTED +ODBC_OBJ = cpl_odbc.obj +!ENDIF + +default: cpl_config.h $(LIB) + +$(LIB): $(OBJ) + lib /out:cpl.lib *.obj + +clean: + -del *.obj *.lib + +cpl_config.h: cpl_config.h.vc + copy cpl_config.h.vc cpl_config.h + + diff --git a/bazaar/plugin/gdal/port/vsipreload.cpp b/bazaar/plugin/gdal/port/vsipreload.cpp new file mode 100644 index 000000000..90589ad02 --- /dev/null +++ b/bazaar/plugin/gdal/port/vsipreload.cpp @@ -0,0 +1,1682 @@ +/****************************************************************************** + * $Id: vsipreload.cpp 28464 2015-02-12 14:40:34Z rouault $ + * + * Project: CPL - Common Portability Library + * Purpose: Standalone shared library that can be LD_PRELOAD'ed as an overload of + * libc to enable VSI Virtual FILE API to be used with binaries using + * regular libc for I/O. + * Author: Even Rouault + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +// WARNING: Linux glibc ONLY +// Might work with some adaptations (mainly around 64bit symbols) on other Unix systems + +// Compile: +// g++ -Wall -fPIC port/vsipreload.cpp -shared -o vsipreload.so -Iport -L. -L.libs -lgdal + +// Run: +// LD_PRELOAD=./vsipreload.so .... +// e.g: +// LD_PRELOAD=./vsipreload.so gdalinfo /vsicurl/http://download.osgeo.org/gdal/data/ecw/spif83.ecw +// LD_PRELOAD=./vsipreload.so gdalinfo 'HDF4_EOS:EOS_GRID:"/vsicurl/http://download.osgeo.org/gdal/data/hdf4/MOD09Q1G_EVI.A2006233.h07v03.005.2008338190308.hdf":MODIS_NACP_EVI:MODIS_EVI' +// LD_PRELOAD=./vsipreload.so ogrinfo /vsicurl/http://svn.osgeo.org/gdal/trunk/autotest/ogr/data/testavc -ro +// even non GDAL binaries : +// LD_PRELOAD=./vsipreload.so h5dump -d /x /vsicurl/http://download.osgeo.org/gdal/data/netcdf/utm-big-chunks.nc +// LD_PRELOAD=./vsipreload.so sqlite3 /vsicurl/http://download.osgeo.org/gdal/data/sqlite3/polygon.db "select * from polygon limit 10" +// LD_PRELOAD=./vsipreload.so ls -al /vsicurl/http://download.osgeo.org/gdal/data/sqlite3 +// LD_PRELOAD=./vsipreload.so find /vsicurl/http://download.osgeo.org/gdal/data/sqlite3 + +#define _GNU_SOURCE 1 +#define _LARGEFILE64_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "cpl_vsi.h" +#include "cpl_multiproc.h" +#include "cpl_string.h" +#include "cpl_hash_set.h" + +CPL_CVSID("$Id: vsipreload.cpp 28464 2015-02-12 14:40:34Z rouault $"); + +static int DEBUG_VSIPRELOAD = 0; +static int DEBUG_VSIPRELOAD_ONLY_VSIL = 1; +#define DEBUG_OUTPUT_READ 0 + +#ifndef NO_FSTATAT +#define HAVE_FSTATAT +#endif + +#define DECLARE_SYMBOL(x, retType, args) \ + typedef retType (*fn ## x ## Type)args;\ + static fn ## x ## Type pfn ## x = NULL + +DECLARE_SYMBOL(fopen, FILE*, (const char *path, const char *mode)); +DECLARE_SYMBOL(fopen64, FILE*, (const char *path, const char *mode)); +DECLARE_SYMBOL(fread, size_t, (void *ptr, size_t size, size_t nmemb, FILE *stream)); +DECLARE_SYMBOL(fwrite, size_t, (const void *ptr, size_t size, size_t nmemb, FILE *stream)); +DECLARE_SYMBOL(fclose, int, (FILE *stream)); +DECLARE_SYMBOL(__xstat, int, (int ver, const char *path, struct stat *buf)); +DECLARE_SYMBOL(__lxstat, int, (int ver, const char *path, struct stat *buf)); +DECLARE_SYMBOL(__xstat64, int, (int ver, const char *path, struct stat64 *buf)); +DECLARE_SYMBOL(fseeko64, int, (FILE *stream, off64_t off, int whence)); +DECLARE_SYMBOL(fseek, int, (FILE *stream, off_t off, int whence)); +DECLARE_SYMBOL(ftello64, off64_t, (FILE *stream)); +DECLARE_SYMBOL(ftell, off_t, (FILE *stream)); +DECLARE_SYMBOL(feof, int, (FILE *stream)); +DECLARE_SYMBOL(fflush, int, (FILE *stream)); +DECLARE_SYMBOL(fgetpos, int, (FILE *stream, fpos_t *pos)); +DECLARE_SYMBOL(fsetpos, int, (FILE *stream, fpos_t *pos)); +DECLARE_SYMBOL(fileno, int, (FILE *stream)); +DECLARE_SYMBOL(ferror, int, (FILE *stream)); +DECLARE_SYMBOL(clearerr, void, (FILE *stream)); + +DECLARE_SYMBOL(fdopen, FILE*, (int fd, const char *mode)); +DECLARE_SYMBOL(freopen, FILE*, (const char *path, const char *mode, FILE *stream)); + +DECLARE_SYMBOL(open, int, (const char *path, int flags, mode_t mode)); +DECLARE_SYMBOL(open64, int, (const char *path, int flags, mode_t mode)); +//DECLARE_SYMBOL(creat, int, (const char *path, mode_t mode)); +DECLARE_SYMBOL(close, int, (int fd)); +DECLARE_SYMBOL(read, ssize_t, (int fd, void *buf, size_t count)); +DECLARE_SYMBOL(write, ssize_t, (int fd, const void *buf, size_t count)); +DECLARE_SYMBOL(fsync, int, (int fd)); +DECLARE_SYMBOL(fdatasync, int, (int fd)); +DECLARE_SYMBOL(__fxstat, int, (int ver, int fd, struct stat *__stat_buf)); +DECLARE_SYMBOL(__fxstat64, int, (int ver, int fd, struct stat64 *__stat_buf)); +#ifdef HAVE_FSTATAT +DECLARE_SYMBOL(__fxstatat, int, (int ver, int dirfd, const char *pathname, struct stat *buf, int flags)); +#endif + +DECLARE_SYMBOL(lseek, off_t, (int fd, off_t off, int whence)); +DECLARE_SYMBOL(lseek64, off64_t , (int fd, off64_t off, int whence)); + +DECLARE_SYMBOL(truncate, int, (const char *path, off_t length)); +DECLARE_SYMBOL(ftruncate, int, (int fd, off_t length)); + +DECLARE_SYMBOL(opendir, DIR* , (const char *name)); +DECLARE_SYMBOL(readdir, struct dirent*, (DIR *dirp)); +DECLARE_SYMBOL(readdir64, struct dirent64*, (DIR *dirp)); +DECLARE_SYMBOL(closedir, int, (DIR *dirp)); +DECLARE_SYMBOL(dirfd, int, (DIR *dirp)); +DECLARE_SYMBOL(fchdir, int, (int fd)); + +static CPLLock* hLock = NULL; + +typedef struct +{ + char* pszDirname; + char** papszDir; + int nIter; + struct dirent ent; + struct dirent64 ent64; + int fd; +} VSIDIR; + +std::set oSetFiles; +std::map oMapfdToVSI; +std::map oMapVSITofd; +std::map oMapVSIToString; +std::set oSetVSIDIR; +std::map oMapfdToVSIDIR; +std::map oMapDirFdToName; +std::string osCurDir; + +/************************************************************************/ +/* myinit() */ +/************************************************************************/ + +#define LOAD_SYMBOL(x) \ + pfn ## x = (fn ## x ## Type) dlsym(RTLD_NEXT, #x); \ + assert(pfn ## x) + +static void myinit(void) +{ + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + + if( pfnfopen64 != NULL ) return; + DEBUG_VSIPRELOAD = getenv("DEBUG_VSIPRELOAD") != NULL; + LOAD_SYMBOL(fopen); + LOAD_SYMBOL(fopen64); + LOAD_SYMBOL(fread); + LOAD_SYMBOL(fwrite); + LOAD_SYMBOL(fclose); + LOAD_SYMBOL(fseeko64); + LOAD_SYMBOL(fseek); + LOAD_SYMBOL(__xstat); + LOAD_SYMBOL(__lxstat); + LOAD_SYMBOL(__xstat64); + LOAD_SYMBOL(ftello64); + LOAD_SYMBOL(ftell); + LOAD_SYMBOL(feof); + LOAD_SYMBOL(fflush); + LOAD_SYMBOL(fgetpos); + LOAD_SYMBOL(fsetpos); + LOAD_SYMBOL(fileno); + LOAD_SYMBOL(ferror); + LOAD_SYMBOL(clearerr); + + LOAD_SYMBOL(fdopen); + LOAD_SYMBOL(freopen); + + LOAD_SYMBOL(open); + LOAD_SYMBOL(open64); + //LOAD_SYMBOL(creat); + LOAD_SYMBOL(close); + LOAD_SYMBOL(read); + LOAD_SYMBOL(write); + LOAD_SYMBOL(fsync); + LOAD_SYMBOL(fdatasync); + LOAD_SYMBOL(__fxstat); + LOAD_SYMBOL(__fxstat64); +#ifdef HAVE_FSTATAT + LOAD_SYMBOL(__fxstatat); +#endif + LOAD_SYMBOL(lseek); + LOAD_SYMBOL(lseek64); + + LOAD_SYMBOL(truncate); + LOAD_SYMBOL(ftruncate); + + LOAD_SYMBOL(opendir); + LOAD_SYMBOL(readdir); + LOAD_SYMBOL(readdir64); + LOAD_SYMBOL(closedir); + LOAD_SYMBOL(dirfd); + LOAD_SYMBOL(fchdir); +} + +/************************************************************************/ +/* getVSILFILE() */ +/************************************************************************/ + +static VSILFILE* getVSILFILE(FILE* stream) +{ + VSILFILE* ret; + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + std::set::iterator oIter = oSetFiles.find((VSILFILE*)stream); + if( oIter != oSetFiles.end() ) + ret = *oIter; + else + ret = NULL; + return ret; +} + +/************************************************************************/ +/* getVSILFILE() */ +/************************************************************************/ + +static VSILFILE* getVSILFILE(int fd) +{ + VSILFILE* ret; + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + std::map::iterator oIter = oMapfdToVSI.find(fd); + if( oIter != oMapfdToVSI.end() ) + ret = oIter->second; + else + ret = NULL; + return ret; +} + +/************************************************************************/ +/* VSIFSeekLHelper() */ +/************************************************************************/ + +static int VSIFSeekLHelper(VSILFILE* fpVSIL, off64_t off, int whence) +{ + if( off < 0 && whence == SEEK_CUR ) + { + return VSIFSeekL(fpVSIL, VSIFTellL(fpVSIL) + off, SEEK_SET); + } + else if( off < 0 && whence == SEEK_END ) + { + VSIFSeekL(fpVSIL, 0, SEEK_END); + return VSIFSeekL(fpVSIL, VSIFTellL(fpVSIL) + off, SEEK_SET); + } + else + return VSIFSeekL(fpVSIL, off, whence); +} + +/************************************************************************/ +/* VSIFopenHelper() */ +/************************************************************************/ + +static VSILFILE* VSIFfopenHelper(const char *path, const char *mode) +{ + VSILFILE* fpVSIL = VSIFOpenL(path, mode); + if( fpVSIL != NULL ) + { + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + oSetFiles.insert(fpVSIL); + oMapVSIToString[fpVSIL] = path; + } + return fpVSIL; +} + +/************************************************************************/ +/* getfdFromVSILFILE() */ +/************************************************************************/ + +static int getfdFromVSILFILE(VSILFILE* fpVSIL) +{ + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + + int fd; + std::map::iterator oIter = oMapVSITofd.find(fpVSIL); + if( oIter != oMapVSITofd.end() ) + fd = oIter->second; + else + { + fd = open("/dev/zero", O_RDONLY); + assert(fd >= 0); + oMapVSITofd[fpVSIL] = fd; + oMapfdToVSI[fd] = fpVSIL; + } + return fd; +} + +/************************************************************************/ +/* VSIFopenHelper() */ +/************************************************************************/ + +static int VSIFopenHelper(const char *path, int flags) +{ + const char* pszMode = "rb"; + if ((flags & 3) == O_RDONLY) + pszMode = "rb"; + else if ((flags & 3) == O_WRONLY) + { + if( flags & O_APPEND ) + pszMode = "ab"; + else + pszMode = "wb"; + } + else + { + if( flags & O_APPEND ) + pszMode = "ab+"; + else + pszMode = "rb+"; + } + VSILFILE* fpVSIL = VSIFfopenHelper(path, pszMode ); + int fd; + if( fpVSIL != NULL ) + { + if( flags & O_TRUNC ) + { + VSIFTruncateL(fpVSIL, 0); + VSIFSeekL(fpVSIL, 0, SEEK_SET); + } + fd = getfdFromVSILFILE(fpVSIL); + } + else + fd = -1; + return fd; +} + +/************************************************************************/ +/* GET_DEBUG_VSIPRELOAD_COND() */ +/************************************************************************/ + +static int GET_DEBUG_VSIPRELOAD_COND(const char* path) +{ + return (DEBUG_VSIPRELOAD && (!DEBUG_VSIPRELOAD_ONLY_VSIL || strncmp(path, "/vsi", 4) == 0) ); +} + +static int GET_DEBUG_VSIPRELOAD_COND(VSILFILE* fpVSIL) +{ + return (DEBUG_VSIPRELOAD && (!DEBUG_VSIPRELOAD_ONLY_VSIL || fpVSIL != NULL)); +} + +static int GET_DEBUG_VSIPRELOAD_COND(VSIDIR* dirP) +{ + return (DEBUG_VSIPRELOAD && (!DEBUG_VSIPRELOAD_ONLY_VSIL || oSetVSIDIR.find(dirP) != oSetVSIDIR.end())); +} + +/************************************************************************/ +/* copyVSIStatBufLToBuf() */ +/************************************************************************/ + +static void copyVSIStatBufLToBuf(VSIStatBufL* bufSrc, struct stat *buf) +{ + buf->st_dev = bufSrc->st_dev; + buf->st_ino = bufSrc->st_ino; + buf->st_mode = bufSrc->st_mode | S_IRUSR | S_IRGRP | S_IROTH; // S_IXUSR | S_IXGRP | S_IXOTH; + buf->st_nlink = 1; //bufSrc->st_nlink; + buf->st_uid = bufSrc->st_uid; + buf->st_gid = bufSrc->st_gid; + buf->st_rdev = bufSrc->st_rdev; + buf->st_size = bufSrc->st_size; + buf->st_blksize = bufSrc->st_blksize; + buf->st_blocks = bufSrc->st_blocks; + buf->st_atime = bufSrc->st_atime; + buf->st_mtime = bufSrc->st_mtime; + buf->st_ctime = bufSrc->st_ctime; +} + +/************************************************************************/ +/* copyVSIStatBufLToBuf64() */ +/************************************************************************/ + +static void copyVSIStatBufLToBuf64(VSIStatBufL *bufSrc, struct stat64 *buf) +{ + buf->st_dev = bufSrc->st_dev; + buf->st_ino = bufSrc->st_ino; + buf->st_mode = bufSrc->st_mode | S_IRUSR | S_IRGRP | S_IROTH; // S_IXUSR | S_IXGRP | S_IXOTH; + buf->st_nlink = 1; //bufSrc->st_nlink; + buf->st_uid = bufSrc->st_uid; + buf->st_gid = bufSrc->st_gid; + buf->st_rdev = bufSrc->st_rdev; + buf->st_size = bufSrc->st_size; + buf->st_blksize = bufSrc->st_blksize; + buf->st_blocks = bufSrc->st_blocks; + buf->st_atime = bufSrc->st_atime; + buf->st_mtime = bufSrc->st_mtime; + buf->st_ctime = bufSrc->st_ctime; +} + +/************************************************************************/ +/* fopen() */ +/************************************************************************/ + +FILE *fopen(const char *path, const char *mode) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(path); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fopen(%s, %s)\n", path, mode); + FILE* ret; + if( strncmp(path, "/vsi", 4) == 0 ) + ret = (FILE*) VSIFfopenHelper(path, mode); + else + ret = pfnfopen(path, mode); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fopen() = %p\n", ret); + return ret; +} + +/************************************************************************/ +/* fopen64() */ +/************************************************************************/ + +FILE *fopen64(const char *path, const char *mode) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(path); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fopen64(%s, %s)\n", path, mode); + FILE* ret; + if( strncmp(path, "/vsi", 4) == 0 ) + ret = (FILE*) VSIFfopenHelper(path, mode); + else + ret = pfnfopen64(path, mode); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fopen64() = %p\n", ret); + return ret; +} + +/************************************************************************/ +/* fread() */ +/************************************************************************/ + +size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fread(stream=%p,size=%d,nmemb=%d)\n", + stream, (int)size, (int)nmemb); + size_t ret; + if( fpVSIL ) + ret = VSIFReadL(ptr, size, nmemb, fpVSIL); + else + ret = pfnfread(ptr, size, nmemb, stream); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fread(stream=%p,size=%d,nmemb=%d) -> %d\n", + stream, (int)size, (int)nmemb, (int)ret); + return ret; +} + +/************************************************************************/ +/* fwrite() */ +/************************************************************************/ + +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fwrite(stream=%p,size=%d,nmemb=%d)\n", + stream, (int)size, (int)nmemb); + size_t ret; + if( fpVSIL != NULL ) + ret = VSIFWriteL(ptr, size, nmemb, fpVSIL); + else + ret = pfnfwrite(ptr, size, nmemb, stream); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fwrite(stream=%p,size=%d,nmemb=%d) -> %d\n", + stream, (int)size, (int)nmemb, (int)ret); + return ret; +} + +/************************************************************************/ +/* fclose() */ +/************************************************************************/ + +int fclose(FILE *stream) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fclose(stream=%p)\n", stream); + if( fpVSIL != NULL ) + { + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + + int ret = VSIFCloseL(fpVSIL); + oMapVSIToString.erase(fpVSIL); + oSetFiles.erase(fpVSIL); + + std::map::iterator oIter = oMapVSITofd.find(fpVSIL); + if( oIter != oMapVSITofd.end() ) + { + int fd = oIter->second; + pfnclose(fd); + oMapVSITofd.erase(oIter); + oMapfdToVSI.erase(fd); + } + + return ret; + } + else + return pfnfclose(stream); +} + +/************************************************************************/ +/* __xstat() */ +/************************************************************************/ + +int __xstat(int ver, const char *path, struct stat *buf) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(path); + if( DEBUG_VSIPRELOAD && (osCurDir.size() != 0 && path[0] != '/') ) + DEBUG_VSIPRELOAD_COND = 1; + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "__xstat(%s)\n", path); + if( (osCurDir.size() != 0 && path[0] != '/') || strncmp(path, "/vsi", 4) == 0 ) + { + VSIStatBufL sStatBufL; + int ret; + std::string newpath; + if( (osCurDir.size() != 0 && path[0] != '/') ) + { + newpath = CPLFormFilename(osCurDir.c_str(), path, NULL); + path = newpath.c_str(); + } + ret = VSIStatL(path, &sStatBufL); + sStatBufL.st_ino = (int)CPLHashSetHashStr(path); + if( ret == 0 ) + { + copyVSIStatBufLToBuf(&sStatBufL, buf); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, + "__xstat(%s) ret = 0, mode = %d, size=%d\n", + path, sStatBufL.st_mode, (int)sStatBufL.st_size); + } + return ret; + } + else + { + int ret = pfn__xstat(ver, path, buf); + if( ret == 0 ) + { + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, + "__xstat ret = 0, mode = %d\n", buf->st_mode); + } + return ret; + } +} + +/************************************************************************/ +/* __lxstat() */ +/************************************************************************/ + +int __lxstat(int ver, const char *path, struct stat *buf) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(path); + if( DEBUG_VSIPRELOAD && (osCurDir.size() != 0 && path[0] != '/') ) + DEBUG_VSIPRELOAD_COND = 1; + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "__lxstat(%s)\n", path); + if( (osCurDir.size() != 0 && path[0] != '/') || strncmp(path, "/vsi", 4) == 0 ) + { + VSIStatBufL sStatBufL; + int ret; + std::string newpath; + if( (osCurDir.size() != 0 && path[0] != '/') ) + { + newpath = CPLFormFilename(osCurDir.c_str(), path, NULL); + path = newpath.c_str(); + } + ret = VSIStatL(path, &sStatBufL); + sStatBufL.st_ino = (int)CPLHashSetHashStr(path); + if( ret == 0 ) + { + copyVSIStatBufLToBuf(&sStatBufL, buf); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, + "__lxstat(%s) ret = 0, mode = %d, size=%d\n", + path, sStatBufL.st_mode, (int)sStatBufL.st_size); + } + return ret; + } + else + { + int ret = pfn__lxstat(ver, path, buf); + if( ret == 0 ) + { + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, + "__lxstat ret = 0, mode = %d\n", buf->st_mode); + } + return ret; + } +} + +/************************************************************************/ +/* __xstat64() */ +/************************************************************************/ + +int __xstat64(int ver, const char *path, struct stat64 *buf) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(path); + if( DEBUG_VSIPRELOAD && (osCurDir.size() != 0 && path[0] != '/') ) + DEBUG_VSIPRELOAD_COND = 1; + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "__xstat64(%s)\n", path); + if( (osCurDir.size() != 0 && path[0] != '/') || strncmp(path, "/vsi", 4) == 0 ) + { + VSIStatBufL sStatBufL; + int ret; + std::string newpath; + if( (osCurDir.size() != 0 && path[0] != '/') ) + { + newpath = CPLFormFilename(osCurDir.c_str(), path, NULL); + path = newpath.c_str(); + } + ret = VSIStatL(path, &sStatBufL); + sStatBufL.st_ino = (int)CPLHashSetHashStr(path); + if( ret == 0 ) + { + copyVSIStatBufLToBuf64(&sStatBufL, buf); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, + "__xstat64(%s) ret = 0, mode = %d, size = %d\n", + path, buf->st_mode, (int)buf->st_size); + } + return ret; + } + else + return pfn__xstat64(ver, path, buf); +} + +/************************************************************************/ +/* fseeko64() */ +/************************************************************************/ + +int fseeko64 (FILE *stream, off64_t off, int whence) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fseeko64(stream=%p, off=%d, whence=%d)\n", + stream, (int)off, whence); + if( fpVSIL != NULL ) + return VSIFSeekLHelper(fpVSIL, off, whence); + else + return pfnfseeko64(stream, off, whence); +} + +/************************************************************************/ +/* fseeko() */ +/************************************************************************/ + +int fseeko (FILE *stream, off_t off, int whence) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fseeko(stream=%p, off=%d, whence=%d)\n", + stream, (int)off, whence); + if( fpVSIL != NULL ) + return VSIFSeekLHelper(fpVSIL, off, whence); + else + return pfnfseeko64(stream, off, whence); +} + +/************************************************************************/ +/* fseek() */ +/************************************************************************/ + +int fseek (FILE *stream, off_t off, int whence) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fseek(stream=%p, off=%d, whence=%d)\n", + stream, (int)off, whence); + if( fpVSIL != NULL ) + return VSIFSeekLHelper(fpVSIL, off, whence); + else + return pfnfseek(stream, off, whence); +} + +/************************************************************************/ +/* ftello64() */ +/************************************************************************/ + +off64_t ftello64(FILE *stream) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "ftello64(stream=%p)\n", stream); + if( fpVSIL != NULL ) + return VSIFTellL(fpVSIL); + else + return pfnftello64(stream); +} + +/************************************************************************/ +/* ftello() */ +/************************************************************************/ + +off_t ftello(FILE *stream) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "ftello(stream=%p)\n", stream); + if( fpVSIL != NULL ) + return VSIFTellL(fpVSIL); + else + return pfnftello64(stream); +} + +/************************************************************************/ +/* ftell() */ +/************************************************************************/ + +off_t ftell(FILE *stream) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "ftell(stream=%p)\n", stream); + if( fpVSIL != NULL ) + return VSIFTellL(fpVSIL); + else + return pfnftell(stream); +} + +/************************************************************************/ +/* feof() */ +/************************************************************************/ + +int feof(FILE *stream) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "feof(stream=%p)\n", stream); + if( fpVSIL != NULL ) + return VSIFEofL(fpVSIL); + else + return pfnfeof(stream); +} + +/************************************************************************/ +/* rewind() */ +/************************************************************************/ + +void rewind(FILE *stream) +{ + fseek(stream, 0, SEEK_SET); +} + +/************************************************************************/ +/* fflush() */ +/************************************************************************/ + +int fflush(FILE *stream) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fflush(stream=%p)\n", stream); + if( fpVSIL != NULL ) + return 0; + else + return pfnfflush(stream); +} + +/************************************************************************/ +/* fgetpos() */ +/************************************************************************/ + +int fgetpos(FILE *stream, fpos_t *pos) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fgetpos(stream=%p)\n", stream); + if( fpVSIL != NULL ) + { + fprintf(stderr, "fgetpos() unimplemented for VSILFILE\n"); + return -1; // FIXME + } + else + return pfnfgetpos(stream, pos); +} + +/************************************************************************/ +/* fsetpos() */ +/************************************************************************/ + +int fsetpos(FILE *stream, fpos_t *pos) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fsetpos(stream=%p)\n", stream); + if( fpVSIL != NULL ) + { + fprintf(stderr, "fsetpos() unimplemented for VSILFILE\n"); + return -1; // FIXME + } + else + return pfnfsetpos(stream, pos); +} + +/************************************************************************/ +/* fileno() */ +/************************************************************************/ + +int fileno(FILE *stream) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fileno(stream=%p)\n", stream); + int fd; + if( fpVSIL != NULL ) + fd = getfdFromVSILFILE(fpVSIL); + else + fd = pfnfileno(stream); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fileno(stream=%p) = %d\n", stream, fd); + return fd; +} + +/************************************************************************/ +/* ferror() */ +/************************************************************************/ + +int ferror(FILE *stream) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "ferror(stream=%p)\n", stream); + if( fpVSIL != NULL ) + { + fprintf(stderr, "ferror() unimplemented for VSILFILE\n"); + return 0; // FIXME ? + } + else + return pfnferror(stream); +} + +/************************************************************************/ +/* clearerr() */ +/************************************************************************/ + +void clearerr(FILE *stream) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "clearerr(stream=%p)\n", stream); + if( fpVSIL != NULL ) + { + fprintf(stderr, "clearerr() unimplemented for VSILFILE\n"); + } + else + pfnclearerr(stream); +} + +/************************************************************************/ +/* fdopen() */ +/************************************************************************/ + +FILE * fdopen(int fd, const char *mode) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(fd); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fdopen(fd=%d)\n", fd); + if( fpVSIL != NULL ) + { + fprintf(stderr, "fdopen() unimplemented for VSILFILE\n"); + return NULL; // FIXME ? + } + else + return pfnfdopen(fd, mode); +} + +/************************************************************************/ +/* freopen() */ +/************************************************************************/ + +FILE *freopen(const char *path, const char *mode, FILE *stream) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(stream); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "freopen(path=%s,mode=%s,stream=%p)\n", path, mode, stream); + if( fpVSIL != NULL ) + { + fprintf(stderr, "freopen() unimplemented for VSILFILE\n"); + return NULL; // FIXME ? + } + else + return pfnfreopen(path, mode, stream); +} + +/************************************************************************/ +/* open() */ +/************************************************************************/ + +int open(const char *path, int flags, ...) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(path); + if( DEBUG_VSIPRELOAD && osCurDir.size() != 0 && path[0] != '/' ) + DEBUG_VSIPRELOAD_COND = 1; + if (DEBUG_VSIPRELOAD_COND) + { + if( osCurDir.size() != 0 && path[0] != '/' ) + fprintf(stderr, "open(%s)\n", CPLFormFilename(osCurDir.c_str(), path, NULL)); + else + fprintf(stderr, "open(%s)\n", path); + } + + va_list args; + va_start(args, flags); + mode_t mode = va_arg(args, mode_t); + int fd; + if( osCurDir.size() != 0 && path[0] != '/' && (flags & 3) == O_RDONLY && (flags & O_DIRECTORY) != 0 ) + { + VSIStatBufL sStatBufL; + char* newname = (char*)CPLFormFilename(osCurDir.c_str(), path, NULL); + if( strchr(osCurDir.c_str(), '/') != NULL && strcmp(path, "..") == 0 ) + { + char* lastslash = strrchr(newname, '/'); + if( lastslash != NULL ) + { + *lastslash = 0; + lastslash = strrchr(newname, '/'); + if( lastslash != NULL ) + *lastslash = 0; + } + } + if( VSIStatL(newname, &sStatBufL) == 0 && + S_ISDIR(sStatBufL.st_mode) ) + { + fd = open("/dev/zero", O_RDONLY); + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX) + oMapDirFdToName[fd] = newname; + } + else + fd = -1; + } + else if( strncmp(path, "/vsi", 4) == 0 ) + fd = VSIFopenHelper(path, flags); + else + fd = pfnopen(path, flags, mode); + va_end(args); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "open(%s) = %d\n", path, fd); + return fd; +} + +/************************************************************************/ +/* open64() */ +/************************************************************************/ + +int open64(const char *path, int flags, ...) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(path); + if( DEBUG_VSIPRELOAD && osCurDir.size() != 0 && path[0] != '/' ) + DEBUG_VSIPRELOAD_COND = 1; + if (DEBUG_VSIPRELOAD_COND) + { + if( osCurDir.size() != 0 && path[0] != '/' ) + fprintf(stderr, "open64(%s)\n", CPLFormFilename(osCurDir.c_str(), path, NULL)); + else + fprintf(stderr, "open64(%s)\n", path); + } + + va_list args; + va_start(args, flags); + mode_t mode = va_arg(args, mode_t); + int fd; + if( osCurDir.size() != 0 && path[0] != '/' && (flags & 3) == O_RDONLY && (flags & O_DIRECTORY) != 0 ) + { + VSIStatBufL sStatBufL; + char* newname = (char*)CPLFormFilename(osCurDir.c_str(), path, NULL); + if( strchr(osCurDir.c_str(), '/') != NULL && strcmp(path, "..") == 0 ) + { + char* lastslash = strrchr(newname, '/'); + if( lastslash != NULL ) + { + *lastslash = 0; + lastslash = strrchr(newname, '/'); + if( lastslash != NULL ) + *lastslash = 0; + } + } + if( VSIStatL(newname, &sStatBufL) == 0 && + S_ISDIR(sStatBufL.st_mode) ) + { + fd = open("/dev/zero", O_RDONLY); + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX) + oMapDirFdToName[fd] = newname; + } + else + fd = -1; + } + else if( strncmp(path, "/vsi", 4) == 0 ) + fd = VSIFopenHelper(path, flags); + else + fd = pfnopen64(path, flags, mode); + va_end(args); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "open64(%s) = %d\n", path, fd); + return fd; +} + +/************************************************************************/ +/* creat() */ +/************************************************************************/ + +int creat(const char *path, mode_t mode) +{ + return open64(path, O_CREAT|O_WRONLY|O_TRUNC, mode); +} + +/************************************************************************/ +/* close() */ +/************************************************************************/ + +int close(int fd) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(fd); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + { + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + assert( oMapfdToVSIDIR.find(fd) == oMapfdToVSIDIR.end() ); + + if( oMapDirFdToName.find(fd) != oMapDirFdToName.end()) + { + oMapDirFdToName.erase(fd); + if( DEBUG_VSIPRELOAD ) + DEBUG_VSIPRELOAD_COND = 1; + } + } + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "close(fd=%d)\n", fd); + if( fpVSIL != NULL ) + { + VSIFCloseL(fpVSIL); + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + oSetFiles.erase(fpVSIL); + pfnclose(oMapVSITofd[fpVSIL]); + oMapVSITofd.erase(fpVSIL); + oMapfdToVSI.erase(fd); + oMapVSIToString.erase(fpVSIL); + return 0; + } + else + return pfnclose(fd); +} + +/************************************************************************/ +/* read() */ +/************************************************************************/ + +ssize_t read(int fd, void *buf, size_t count) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(fd); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "read(fd=%d, count=%d)\n", fd, (int)count); + ssize_t ret; + if( fpVSIL != NULL ) + ret = VSIFReadL(buf, 1, count, fpVSIL); + else + ret = pfnread(fd, buf, count); + if (DEBUG_VSIPRELOAD_COND && DEBUG_OUTPUT_READ && ret < 40) + { + fprintf(stderr, "read() : "); + for(int i=0;i= 'A' && ((unsigned char*)buf)[i] <= 'Z' ) + fprintf(stderr, "%c ", ((unsigned char*)buf)[i]); + else + fprintf(stderr, "\\%02X ", ((unsigned char*)buf)[i]); + } + fprintf(stderr, "\n"); + } + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "read() -> %d\n", (int)ret); + return ret; +} + +/************************************************************************/ +/* write() */ +/************************************************************************/ + +ssize_t write(int fd, const void *buf, size_t count) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(fd); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "write(fd=%d, count=%d)\n", fd, (int)count); + if( fpVSIL != NULL ) + return VSIFWriteL(buf, 1, count, fpVSIL); + else + return pfnwrite(fd, buf, count); +} + +/************************************************************************/ +/* fsync() */ +/************************************************************************/ + +int fsync(int fd) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(fd); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fsync(fd=%d)\n", fd); + if( fpVSIL != NULL ) + return 0; + else + return pfnfsync(fd); +} + +/************************************************************************/ +/* fdatasync() */ +/************************************************************************/ + +int fdatasync(int fd) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(fd); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fdatasync(fd=%d)\n", fd); + if( fpVSIL != NULL ) + return 0; + else + return pfnfdatasync(fd); +} + +/************************************************************************/ +/* __fxstat() */ +/************************************************************************/ + +int __fxstat (int ver, int fd, struct stat *buf) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(fd); + std::string name; + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + { + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX) + if( oMapDirFdToName.find(fd) != oMapDirFdToName.end()) + { + name = oMapDirFdToName[fd]; + if( DEBUG_VSIPRELOAD ) + DEBUG_VSIPRELOAD_COND = 1; + } + } + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "__fxstat(fd=%d)\n", fd); + if( name.size() ) + { + VSIStatBufL sStatBufL; + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "__fxstat(%s)\n", name.c_str()); + int ret = VSIStatL(name.c_str(), &sStatBufL); + sStatBufL.st_ino = (int)CPLHashSetHashStr(name.c_str()); + if( ret == 0 ) + { + copyVSIStatBufLToBuf(&sStatBufL, buf); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, + "__fxstat ret = 0, mode = %d, size = %d\n", + sStatBufL.st_mode, (int)sStatBufL.st_size); + } + return ret; + } + else if( fpVSIL != NULL ) + { + VSIStatBufL sStatBufL; + { + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + name = oMapVSIToString[fpVSIL]; + } + int ret = VSIStatL(name.c_str(), &sStatBufL); + sStatBufL.st_ino = (int)CPLHashSetHashStr(name.c_str()); + if( ret == 0 ) + { + copyVSIStatBufLToBuf(&sStatBufL, buf); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, + "__fxstat ret = 0, mode = %d, size = %d\n", + sStatBufL.st_mode, (int)sStatBufL.st_size); + } + return ret; + } + else + return pfn__fxstat(ver, fd, buf); +} + +/************************************************************************/ +/* __fxstat64() */ +/************************************************************************/ + +int __fxstat64 (int ver, int fd, struct stat64 *buf) +{ + myinit(); + VSILFILE* fpVSIL = getVSILFILE(fd); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "__fxstat64(fd=%d)\n", fd); + if( fpVSIL != NULL ) + { + VSIStatBufL sStatBufL; + std::string name; + { + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + name = oMapVSIToString[fpVSIL]; + } + int ret = VSIStatL(name.c_str(), &sStatBufL); + sStatBufL.st_ino = (int)CPLHashSetHashStr(name.c_str()); + if( ret == 0 ) + { + copyVSIStatBufLToBuf64(&sStatBufL, buf); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, + "__fxstat64 ret = 0, mode = %d, size = %d\n", + buf->st_mode, (int)buf->st_size); + } + return ret; + } + else + return pfn__fxstat64(ver, fd, buf); +} + +/************************************************************************/ +/* __fxstatat() */ +/************************************************************************/ + +#ifdef HAVE_FSTATAT +int __fxstatat (int ver, int dirfd, const char *pathname, struct stat *buf, + int flags) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(pathname); + if( DEBUG_VSIPRELOAD && osCurDir.size() != 0 ) + DEBUG_VSIPRELOAD_COND = 1; + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "__fxstatat(dirfd=%d,pathname=%s,flags=%d)\n", dirfd, pathname, flags); + + if( osCurDir.size() != 0 || strncmp(pathname, "/vsi", 4) == 0 ) + { + VSIStatBufL sStatBufL; + int ret; + if( osCurDir.size() && dirfd == AT_FDCWD && pathname[0] != '/' ) + pathname = CPLFormFilename(osCurDir.c_str(), pathname, NULL); + ret = VSIStatL(pathname, &sStatBufL); + sStatBufL.st_ino = (int)CPLHashSetHashStr(pathname); + if( ret == 0 ) + { + copyVSIStatBufLToBuf(&sStatBufL, buf); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, + "__fxstatat(%s) ret = 0, mode = %d, size = %d\n", + pathname, buf->st_mode, (int)buf->st_size); + } + return ret; + } + else + return pfn__fxstatat(ver, dirfd, pathname, buf, flags); +} +#endif + +/************************************************************************/ +/* lseek() */ +/************************************************************************/ + +off_t lseek(int fd, off_t off, int whence) +{ + myinit(); + off_t ret; + VSILFILE* fpVSIL = getVSILFILE(fd); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, + "lseek(fd=%d, off=%d, whence=%d)\n", fd, (int)off, whence); + if( fpVSIL != NULL ) + { + VSIFSeekLHelper(fpVSIL, off, whence); + ret = VSIFTellL(fpVSIL); + } + else + ret = pfnlseek(fd, off, whence); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "lseek() -> ret = %d\n", (int)ret); + return ret; +} + +/************************************************************************/ +/* lseek64() */ +/************************************************************************/ + +off64_t lseek64(int fd, off64_t off, int whence) +{ + myinit(); + off_t ret; + VSILFILE* fpVSIL = getVSILFILE(fd); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, + "lseek64(fd=%d, off=%d, whence=%d)\n", fd, (int)off, whence); + if( fpVSIL != NULL ) + { + VSIFSeekLHelper(fpVSIL, off, whence); + ret = VSIFTellL(fpVSIL); + } + else + ret = pfnlseek64(fd, off, whence); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, + "lseek64() -> ret = %d\n", (int)ret); + return ret; +} + +/************************************************************************/ +/* truncate() */ +/************************************************************************/ + +int truncate(const char *path, off_t length) +{ + myinit(); + int ret; + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(path); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "truncate(%s)\n", path); + + if( strncmp(path, "/vsi", 4) == 0 ) + { + VSILFILE* fpVSIL = VSIFOpenL(path, "wb+"); + if( fpVSIL ) + { + ret = VSIFTruncateL(fpVSIL, length); + VSIFCloseL(fpVSIL); + } + else + ret = -1; + } + else + ret = pfntruncate(path, length); + return ret; +} + +/************************************************************************/ +/* ftruncate() */ +/************************************************************************/ + +int ftruncate(int fd, off_t length) +{ + myinit(); + int ret; + VSILFILE* fpVSIL = getVSILFILE(fd); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(fpVSIL); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "ftruncate(fd=%d)\n", fd); + if( fpVSIL != NULL ) + { + ret = VSIFTruncateL(fpVSIL, length); + } + else + ret = pfnftruncate(fd, length); + return ret; +} + +/************************************************************************/ +/* opendir() */ +/************************************************************************/ + +DIR *opendir(const char *name) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(name); + if( DEBUG_VSIPRELOAD && osCurDir.size() != 0 ) + DEBUG_VSIPRELOAD_COND = 1; + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "opendir(%s)\n", name); + + DIR * ret; + if( osCurDir.size() != 0 || strncmp(name, "/vsi", 4) == 0 ) + { + char** papszDir; + if( osCurDir.size() != 0 && name[0] != '/' ) + name = CPLFormFilename(osCurDir.c_str(), name, NULL); + papszDir = VSIReadDir(name); + if( papszDir == NULL ) + { + VSIStatBufL sStatBufL; + if( VSIStatL(name, &sStatBufL) == 0 && S_ISDIR(sStatBufL.st_mode) ) + { + papszDir = (char**) CPLMalloc(sizeof(char*)); + papszDir[0] = NULL; + } + } + if( papszDir == NULL ) + ret = NULL; + else + { + VSIDIR* mydir = (VSIDIR*)malloc(sizeof(VSIDIR)); + mydir->pszDirname = CPLStrdup(name); + mydir->papszDir = papszDir; + mydir->nIter = 0; + mydir->fd = -1; + ret = (DIR*)mydir; + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + oSetVSIDIR.insert(mydir); + } + } + else + ret = pfnopendir(name); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "opendir(%s) -> %p\n", name, ret); + return ret; +} + +/************************************************************************/ +/* filldir() */ +/************************************************************************/ + +static int filldir(VSIDIR* mydir) +{ + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(mydir); + char* pszName = mydir->papszDir[mydir->nIter++]; + if( pszName == NULL ) + return FALSE; + mydir->ent.d_ino = 0; + mydir->ent.d_off = 0; + mydir->ent.d_reclen = sizeof(mydir->ent); + VSIStatBufL sStatBufL; + VSIStatL(CPLFormFilename(mydir->pszDirname, pszName, NULL), &sStatBufL); + if( DEBUG_VSIPRELOAD_COND && S_ISDIR(sStatBufL.st_mode) ) + fprintf(stderr, "%s is dir\n", pszName); + mydir->ent.d_type = S_ISDIR(sStatBufL.st_mode) ? DT_DIR : + S_ISREG(sStatBufL.st_mode) ? DT_REG : + S_ISLNK(sStatBufL.st_mode) ? DT_LNK : + DT_UNKNOWN; + strncpy(mydir->ent.d_name, pszName, 256); + mydir->ent.d_name[255] = '\0'; + + mydir->ent64.d_ino = 0; + mydir->ent64.d_off = 0; + mydir->ent64.d_reclen = sizeof(mydir->ent64); + mydir->ent64.d_type = mydir->ent.d_type; + strcpy(mydir->ent64.d_name, mydir->ent.d_name); + + return TRUE; +} + +/************************************************************************/ +/* readdir() */ +/************************************************************************/ + +struct dirent *readdir(DIR *dirp) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND((VSIDIR*)dirp); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "readdir(%p)\n", dirp); + if( oSetVSIDIR.find((VSIDIR*)dirp) != oSetVSIDIR.end() ) + { + VSIDIR* mydir = (VSIDIR*)dirp; + if( !filldir(mydir) ) + return FALSE; + + return &(mydir->ent); + } + else + return pfnreaddir(dirp); +} + +/************************************************************************/ +/* readdir64() */ +/************************************************************************/ + +struct dirent64 *readdir64(DIR *dirp) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND((VSIDIR*)dirp); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "readdir64(%p)\n", dirp); + if( oSetVSIDIR.find((VSIDIR*)dirp) != oSetVSIDIR.end() ) + { + VSIDIR* mydir = (VSIDIR*)dirp; + if( !filldir(mydir) ) + return FALSE; + + return &(mydir->ent64); + } + else + return pfnreaddir64(dirp); +} + +/************************************************************************/ +/* closedir() */ +/************************************************************************/ + +int closedir(DIR *dirp) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND((VSIDIR*)dirp); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "closedir(%p)\n", dirp); + if( oSetVSIDIR.find((VSIDIR*)dirp) != oSetVSIDIR.end() ) + { + VSIDIR* mydir = (VSIDIR*)dirp; + CPLFree(mydir->pszDirname); + CSLDestroy(mydir->papszDir); + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + if( mydir->fd >= 0 ) + { + oMapfdToVSIDIR.erase(mydir->fd); + close(mydir->fd); + } + oSetVSIDIR.erase(mydir); + free(mydir); + return 0; + } + else + return pfnclosedir(dirp); +} + +/************************************************************************/ +/* dirfd() */ +/************************************************************************/ + +int dirfd(DIR *dirp) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND((VSIDIR*)dirp); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "dirfd(%p)\n", dirp); + int ret; + if( oSetVSIDIR.find((VSIDIR*)dirp) != oSetVSIDIR.end() ) + { + VSIDIR* mydir = (VSIDIR*)dirp; + if( mydir->fd < 0 ) + { + mydir->fd = open("/dev/zero", O_RDONLY); + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + oMapfdToVSIDIR[mydir->fd] = mydir; + } + ret = mydir->fd; + } + else + ret = pfndirfd(dirp); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "dirfd(%p) -> %d\n", dirp, ret); + return ret; +} + +/************************************************************************/ +/* fchdir() */ +/************************************************************************/ + +int fchdir(int fd) +{ + VSIDIR* mydir = NULL; + { + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX); + if( oMapfdToVSIDIR.find(fd) != oMapfdToVSIDIR.end() ) + mydir = oMapfdToVSIDIR[fd]; + } + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(mydir); + std::string name; + { + CPLLockHolderD(&hLock, LOCK_RECURSIVE_MUTEX) + if( oMapDirFdToName.find(fd) != oMapDirFdToName.end()) + { + name = oMapDirFdToName[fd]; + if( DEBUG_VSIPRELOAD ) + DEBUG_VSIPRELOAD_COND = 1; + } + } + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fchdir(%d)\n", fd); + if( name.size() ) + { + osCurDir = name; + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fchdir(%d) -> %s\n", fd, osCurDir.c_str()); + return 0; + } + else if( mydir != NULL ) + { + osCurDir = mydir->pszDirname; + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fchdir(%d) -> %s\n", fd, osCurDir.c_str()); + return 0; + } + else + { + osCurDir = ""; + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "fchdir(%d) -> %s\n", fd, osCurDir.c_str()); + return pfnfchdir(fd); + } +} + +/************************************************************************/ +/* acl_extended_file() */ +/************************************************************************/ + +// #include +extern "C" int acl_extended_file(const char *name); +DECLARE_SYMBOL(acl_extended_file, int, (const char *name)); + +int acl_extended_file(const char *path) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(path); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "acl_extended_file(%s)\n", path); + int ret; + if( strncmp(path, "/vsi", 4) == 0 ) + ret = -1; + else + { + if( pfnacl_extended_file == NULL ) + pfnacl_extended_file = (fnacl_extended_fileType) dlsym(RTLD_NEXT, "acl_extended_file"); + if( pfnacl_extended_file == NULL ) + ret = -1; + else + ret = pfnacl_extended_file(path); + } + return ret; +} + +/************************************************************************/ +/* getfilecon() */ +/************************************************************************/ + +// #include +extern "C" int getfilecon(const char *name, void* con); +DECLARE_SYMBOL(getfilecon, int, (const char *name, void* con)); + +int getfilecon(const char *path, /*security_context_t **/ void* con) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(path); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "getfilecon(%s)\n", path); + int ret; + if( strncmp(path, "/vsi", 4) == 0 ) + { + errno = ENOTSUP; + ret = -1; + } + else + { + if( pfngetfilecon == NULL ) + pfngetfilecon = (fngetfileconType) dlsym(RTLD_NEXT, "getfilecon"); + if( pfngetfilecon == NULL ) + ret = -1; + else + ret = pfngetfilecon(path, con); + } + return ret; +} + +/************************************************************************/ +/* lgetfilecon() */ +/************************************************************************/ + +// #include +extern "C" int lgetfilecon(const char *name, void* con); +DECLARE_SYMBOL(lgetfilecon, int, (const char *name, void* con)); + +int lgetfilecon(const char *path, /*security_context_t **/ void* con) +{ + myinit(); + int DEBUG_VSIPRELOAD_COND = GET_DEBUG_VSIPRELOAD_COND(path); + if (DEBUG_VSIPRELOAD_COND) fprintf(stderr, "lgetfilecon(%s)\n", path); + int ret; + if( strncmp(path, "/vsi", 4) == 0 ) + { + errno = ENOTSUP; + ret = -1; + } + else + { + if( pfnlgetfilecon == NULL ) + pfnlgetfilecon = (fnlgetfileconType) dlsym(RTLD_NEXT, "lgetfilecon"); + if( pfnlgetfilecon == NULL ) + ret = -1; + else + ret = pfnlgetfilecon(path, con); + } + return ret; +} diff --git a/bazaar/plugin/gdal/port/xmlreformat.cpp b/bazaar/plugin/gdal/port/xmlreformat.cpp new file mode 100644 index 000000000..277f43d00 --- /dev/null +++ b/bazaar/plugin/gdal/port/xmlreformat.cpp @@ -0,0 +1,81 @@ +/********************************************************************** + * $Id: xmlreformat.cpp 24835 2012-08-23 20:11:13Z warmerdam $ + * + * Project: CPL - Common Portability Library + * Purpose: XML Reformatting - mostly for testing minixml implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ********************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + **********************************************************************/ + +#include "cpl_minixml.h" +#include "cpl_conv.h" + +int main( int argc, char **argv ) + +{ + CPLXMLNode *poTree; + static char szXML[20000000]; + FILE *fp; + int nLen; + + if( argc == 1 ) + fp = stdin; + else if( argv[1][0] == '-' ) + { + printf( "Usage: xmlreformat [filename]\n" ); + exit( 0 ); + } + else + { + fp = fopen( argv[1], "rt" ); + if( fp == NULL ) + { + printf( "Failed to open file %s.\n", argv[1] ); + exit( 1 ); + } + } + + nLen = fread( szXML, 1, sizeof(szXML), fp ); + if( nLen >= (int) sizeof(szXML)-2 ) { + fprintf( stderr, + "xmlreformat fixed sized buffer (%d bytes) exceeded.\n", + (int) sizeof(szXML) ); + exit(1); + } + + if( fp != stdin ) + fclose( fp ); + + szXML[nLen] = '\0'; + + poTree = CPLParseXMLString( szXML ); + if( poTree != NULL ) + { + char *pszRawXML = CPLSerializeXMLTree( poTree ); + printf( "%s", pszRawXML ); + CPLFree( pszRawXML ); + CPLDestroyXMLNode( poTree ); + } + + return 0; +}